From 900b075e3aa03890f1538d97558920fc2bb9ae59 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 May 2001 11:40:29 +0000 Subject: [PATCH 001/807] initial revision of synergy. currently semi-supports X windows on unix, but client screens don't simulate events other than mouse move. also not supporting clipboard at all yet and the main app is just a temporary framework to test with. must clean up protocol and communication. --- BasicTypes.h | 33 ++ CClient.cpp | 287 ++++++++++++++++ CClient.h | 28 ++ CEvent.h | 58 ++++ CEventQueue.cpp | 80 +++++ CEventQueue.h | 36 ++ CMessageSocket.cpp | 153 +++++++++ CMessageSocket.h | 33 ++ CProtocol.h | 20 ++ CScreenProxy.cpp | 234 +++++++++++++ CScreenProxy.h | 43 +++ CServer.cpp | 811 ++++++++++++++++++++++++++++++++++++++++++++ CServer.h | 103 ++++++ CSocket.cpp | 58 ++++ CSocket.h | 41 +++ CSocketFactory.cpp | 31 ++ CSocketFactory.h | 29 ++ CString.h | 40 +++ CTrace.cpp | 16 + CTrace.h | 19 ++ CUnixEventQueue.cpp | 141 ++++++++ CUnixEventQueue.h | 54 +++ CUnixTCPSocket.cpp | 262 ++++++++++++++ CUnixTCPSocket.h | 47 +++ CUnixXScreen.cpp | 34 ++ CUnixXScreen.h | 16 + CXScreen.cpp | 571 +++++++++++++++++++++++++++++++ CXScreen.h | 81 +++++ IClient.h | 17 + IClipboard.h | 0 IEventQueue.h | 45 +++ IJob.h | 14 + IScreen.h | 132 +++++++ IServer.h | 29 ++ ISocket.h | 45 +++ KeyTypes.h | 18 + Make-linux | 103 ++++++ Makefile | 38 +++ MouseTypes.h | 13 + TMethodJob.h | 23 ++ XBase.cpp | 40 +++ XBase.h | 31 ++ XSocket.h | 33 ++ main.cpp | 114 +++++++ tools/depconv | 46 +++ 45 files changed, 4100 insertions(+) create mode 100644 BasicTypes.h create mode 100644 CClient.cpp create mode 100644 CClient.h create mode 100644 CEvent.h create mode 100644 CEventQueue.cpp create mode 100644 CEventQueue.h create mode 100644 CMessageSocket.cpp create mode 100644 CMessageSocket.h create mode 100644 CProtocol.h create mode 100644 CScreenProxy.cpp create mode 100644 CScreenProxy.h create mode 100644 CServer.cpp create mode 100644 CServer.h create mode 100644 CSocket.cpp create mode 100644 CSocket.h create mode 100644 CSocketFactory.cpp create mode 100644 CSocketFactory.h create mode 100644 CString.h create mode 100644 CTrace.cpp create mode 100644 CTrace.h create mode 100644 CUnixEventQueue.cpp create mode 100644 CUnixEventQueue.h create mode 100644 CUnixTCPSocket.cpp create mode 100644 CUnixTCPSocket.h create mode 100644 CUnixXScreen.cpp create mode 100644 CUnixXScreen.h create mode 100644 CXScreen.cpp create mode 100644 CXScreen.h create mode 100644 IClient.h create mode 100644 IClipboard.h create mode 100644 IEventQueue.h create mode 100644 IJob.h create mode 100644 IScreen.h create mode 100644 IServer.h create mode 100644 ISocket.h create mode 100644 KeyTypes.h create mode 100644 Make-linux create mode 100644 Makefile create mode 100644 MouseTypes.h create mode 100644 TMethodJob.h create mode 100644 XBase.cpp create mode 100644 XBase.h create mode 100644 XSocket.h create mode 100644 main.cpp create mode 100755 tools/depconv diff --git a/BasicTypes.h b/BasicTypes.h new file mode 100644 index 00000000..8dfce8c2 --- /dev/null +++ b/BasicTypes.h @@ -0,0 +1,33 @@ +#ifndef BASICTYPES_H +#define BASICTYPES_H + +#if defined(__linux__) + +#define CONFIG_PLATFORM_LINUX +#define CONFIG_TYPES_X11 + +#include + +typedef int8_t SInt8; +typedef int16_t SInt16; +typedef int32_t SInt32; +typedef int64_t SInt64; + +typedef uint8_t UInt8; +typedef uint16_t UInt16; +typedef uint32_t UInt32; +typedef uint64_t UInt64; + +#else + +#error unsupported platform + +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#endif + + diff --git a/CClient.cpp b/CClient.cpp new file mode 100644 index 00000000..65030c99 --- /dev/null +++ b/CClient.cpp @@ -0,0 +1,287 @@ +#include "CClient.h" +#include "CString.h" +#include "TMethodJob.h" +#include "IScreen.h" +#include "ISocket.h" +#include "CMessageSocket.h" +#include "CSocketFactory.h" +#include "IEventQueue.h" +#include "CEvent.h" +#include "CTrace.h" +#include + +// +// CClient +// + +CClient::CClient(IScreen* screen) : m_screen(screen), + m_socket(NULL) +{ + assert(m_screen != NULL); + assert(!m_screen->getName().empty()); +} + +CClient::~CClient() +{ + assert(m_socket == NULL); +} + +void CClient::run(const CString& hostname) +{ + assert(m_socket == NULL); + + try { + // create socket and + m_socket = CSOCKETFACTORY->create(); + m_socket->setWriteJob(new TMethodJob(this, + &CClient::onConnect)); + TRACE(("connecting to %s...", hostname.c_str())); + m_socket->connect(hostname, 40001); // CProtocol::kDefaultPort + + bool m_done = false; // FIXME + + IEventQueue* queue = CEQ; + while (!m_done) { + // wait for connection, network messages, and events + queue->wait(-1.0); + + // handle events + while (!queue->isEmpty()) { + // get the next event + CEvent event; + queue->pop(&event); + + // handle it + switch (event.m_any.m_type) { + case CEventBase::kScreenSize: { + sendScreenSize(); + break; + } + + case CEventBase::kNull: + case CEventBase::kKeyDown: + case CEventBase::kKeyRepeat: + case CEventBase::kKeyUp: + case CEventBase::kMouseDown: + case CEventBase::kMouseUp: + case CEventBase::kMouseMove: + case CEventBase::kMouseWheel: + // FIXME -- other cases + break; + } + } + } + + delete m_socket; + m_socket = NULL; + } + + catch (...) { + delete m_socket; + m_socket = NULL; + throw; + } +} + +void CClient::onConnect() +{ + TRACE(("connected")); + + // say hello + const CString name(m_screen->getName()); + char buf[512]; + memcpy(buf, "SYNERGY\000\001", 9); + buf[9] = static_cast(name.length()); + memcpy(buf + 10, name.c_str(), name.length()); + m_socket->write(buf, 10 + name.length()); + + // handle messages + m_socket->setWriteJob(NULL); + m_socket = new CMessageSocket(m_socket); + m_socket->setReadJob(new TMethodJob(this, &CClient::onRead)); +} + +void CClient::onRead() +{ + char buf[512]; + SInt32 n = m_socket->read(buf, sizeof(buf)); + if (n == -1) { + // disconnect + TRACE(("hangup")); + } + else if (n > 0) { + TRACE(("msg: 0x%02x length %d", buf[0], n)); + switch (buf[0]) { + case '\002': + TRACE((" open")); + + // open the screen + m_screen->open(buf[1] != 0); + + // send initial size + sendScreenSize(); + break; + + case '\003': + TRACE((" close")); + m_screen->close(); + break; + + case '\004': { + const SInt32 x = static_cast( + (static_cast(buf[1]) << 24) + + (static_cast(buf[2]) << 16) + + (static_cast(buf[3]) << 8) + + (static_cast(buf[4]) )); + const SInt32 y = static_cast( + (static_cast(buf[5]) << 24) + + (static_cast(buf[6]) << 16) + + (static_cast(buf[7]) << 8) + + (static_cast(buf[8]) )); + TRACE((" enter: %d,%d", x, y)); + m_screen->enterScreen(x, y); + break; + } + + case '\005': + TRACE((" leave")); + m_screen->leaveScreen(); + break; + + case '\007': { + const KeyID k = static_cast( + (static_cast(buf[1]) << 24) + + (static_cast(buf[2]) << 16) + + (static_cast(buf[3]) << 8) + + (static_cast(buf[4]) )); + TRACE((" key down: %d", k)); + m_screen->onKeyDown(k); + break; + } + + case '\010': { + const KeyID k = static_cast( + (static_cast(buf[1]) << 24) + + (static_cast(buf[2]) << 16) + + (static_cast(buf[3]) << 8) + + (static_cast(buf[4]) )); + const SInt32 n = static_cast( + (static_cast(buf[5]) << 24) + + (static_cast(buf[6]) << 16) + + (static_cast(buf[7]) << 8) + + (static_cast(buf[8]) )); + TRACE((" key repeat: %d x%d", k, n)); + m_screen->onKeyRepeat(k, n); + break; + } + + case '\011': { + const KeyID k = static_cast( + (static_cast(buf[1]) << 24) + + (static_cast(buf[2]) << 16) + + (static_cast(buf[3]) << 8) + + (static_cast(buf[4]) )); + TRACE((" key up: %d", k)); + m_screen->onKeyUp(k); + break; + } + + case '\012': { + const KeyToggleMask m = static_cast( + (static_cast(buf[1]) << 8) + + (static_cast(buf[2]) )); + TRACE((" key toggle: 0x%04x", m)); + m_screen->onKeyToggle(m); + break; + } + + case '\013': { + const ButtonID b = static_cast( + static_cast(buf[1])); + TRACE((" mouse down: %d", b)); + m_screen->onMouseDown(b); + break; + } + + case '\014': { + const ButtonID b = static_cast( + static_cast(buf[1])); + TRACE((" mouse up: %d", b)); + m_screen->onMouseUp(b); + break; + } + + case '\015': { + const SInt32 x = static_cast( + (static_cast(buf[1]) << 24) + + (static_cast(buf[2]) << 16) + + (static_cast(buf[3]) << 8) + + (static_cast(buf[4]) )); + const SInt32 y = static_cast( + (static_cast(buf[5]) << 24) + + (static_cast(buf[6]) << 16) + + (static_cast(buf[7]) << 8) + + (static_cast(buf[8]) )); + TRACE((" mouse move: %d,%d", x, y)); + m_screen->onMouseMove(x, y); + break; + } + + case '\016': { + const SInt32 n = static_cast( + (static_cast(buf[1]) << 24) + + (static_cast(buf[2]) << 16) + + (static_cast(buf[3]) << 8) + + (static_cast(buf[4]) )); + TRACE((" mouse wheel: %d", n)); + m_screen->onMouseWheel(n); + break; + } + + case '\017': { + TRACE((" screen saver: %s", buf[1] ? "on" : "off")); + m_screen->onScreenSaver(buf[1] != 0); + break; + } + + case '\020': { + const SInt32 x = static_cast( + (static_cast(buf[1]) << 24) + + (static_cast(buf[2]) << 16) + + (static_cast(buf[3]) << 8) + + (static_cast(buf[4]) )); + const SInt32 y = static_cast( + (static_cast(buf[5]) << 24) + + (static_cast(buf[6]) << 16) + + (static_cast(buf[7]) << 8) + + (static_cast(buf[8]) )); + TRACE((" warp: %d,%d", x, y)); + m_screen->warpCursor(x, y); + break; + } + + default: + TRACE((" unknown message")); + } + } +} + +void CClient::sendScreenSize() +{ + // get the size + SInt32 w, h; + m_screen->getSize(&w, &h); + + // send it + char buf[9]; + memcpy(buf, "\201", 1); + buf[1] = static_cast((w >> 24) & 0xff); + buf[2] = static_cast((w >> 16) & 0xff); + buf[3] = static_cast((w >> 8) & 0xff); + buf[4] = static_cast(w & 0xff); + buf[5] = static_cast((h >> 24) & 0xff); + buf[6] = static_cast((h >> 16) & 0xff); + buf[7] = static_cast((h >> 8) & 0xff); + buf[8] = static_cast(h & 0xff); + m_socket->write(buf, sizeof(buf)); +} diff --git a/CClient.h b/CClient.h new file mode 100644 index 00000000..c99cb364 --- /dev/null +++ b/CClient.h @@ -0,0 +1,28 @@ +#ifndef CCLIENT_H +#define CCLIENT_H + +#include "IClient.h" + +class IScreen; +class ISocket; + +class CClient : public IClient { + public: + CClient(IScreen* screen); + virtual ~CClient(); + + // IClient overrides + virtual void run(const CString& hostname); + + private: + void onConnect(); + void onRead(); + + void sendScreenSize(); + + private: + IScreen* m_screen; + ISocket* m_socket; +}; + +#endif diff --git a/CEvent.h b/CEvent.h new file mode 100644 index 00000000..452aa4e6 --- /dev/null +++ b/CEvent.h @@ -0,0 +1,58 @@ +#ifndef CEVENT_H +#define CEVENT_H + +#include "BasicTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" + +class ISocket; + +class CEventBase { + public: + enum EType { + kNull, + kKeyDown, + kKeyRepeat, + kKeyUp, + kMouseDown, + kMouseUp, + kMouseMove, + kMouseWheel, + kScreenSize + }; + + EType m_type; +}; + +class CEventKey : public CEventBase { + public: + KeyID m_key; + SInt32 m_count; +}; + +class CEventMouse : public CEventBase { + public: + ButtonID m_button; + SInt32 m_x; // or wheel delta + SInt32 m_y; +}; + +class CEventSize : public CEventBase { + public: + SInt32 m_w; + SInt32 m_h; +}; + +class CEvent { + public: + union { + public: + CEventBase m_any; + CEventKey m_key; + CEventMouse m_mouse; + CEventSize m_size; + }; +}; + +#endif + diff --git a/CEventQueue.cpp b/CEventQueue.cpp new file mode 100644 index 00000000..0cef9742 --- /dev/null +++ b/CEventQueue.cpp @@ -0,0 +1,80 @@ +#include "CEventQueue.h" + +// +// IEventQueue +// + +IEventQueue* IEventQueue::s_instance = NULL; + +IEventQueue::IEventQueue() +{ + assert(s_instance == NULL); + s_instance = this; +} + +IEventQueue::~IEventQueue() +{ + s_instance = NULL; +} + +IEventQueue* IEventQueue::getInstance() +{ + return s_instance; +} + + +// +// CEventQueue +// + +CEventQueue::CEventQueue() +{ + // do nothing +} + +CEventQueue::~CEventQueue() +{ + // do nothing +} + +void CEventQueue::pop(CEvent* event) +{ + assert(event != NULL); + + // wait for an event + while (isEmpty()) + wait(-1.0); + + // lock the queue, extract an event, then unlock + lock(); + *event = m_queue.front(); + m_queue.pop_front(); + unlock(); +} + +void CEventQueue::push(const CEvent* event) +{ + // put the event at the end of the queue and signal that the queue + // is not empty + lock(); + m_queue.push_back(*event); + signalNotEmpty(); + unlock(); +} + +bool CEventQueue::isEmpty() +{ + lock(); + bool e = m_queue.empty(); + unlock(); + + // if queue is empty then poll to see if more stuff is ready to go + // on the queue and check again if the queue is empty. + if (e) { + wait(0.0); + lock(); + e = m_queue.empty(); + unlock(); + } + return e; +} diff --git a/CEventQueue.h b/CEventQueue.h new file mode 100644 index 00000000..de6a853d --- /dev/null +++ b/CEventQueue.h @@ -0,0 +1,36 @@ +#ifndef CEVENTQUEUE_H +#define CEVENTQUEUE_H + +#include "IEventQueue.h" +#include "CEvent.h" +#include + +class CEventQueue : public IEventQueue { + public: + CEventQueue(); + virtual ~CEventQueue(); + + // IEventQueue overrides + virtual void wait(double timeout) = 0; + virtual void pop(CEvent*); + virtual void push(const CEvent*); + virtual bool isEmpty(); + + protected: + // signal the queue not-empty condition. this should cause wait() + // to stop waiting. + virtual void signalNotEmpty() = 0; + + // lock the queue mutex + virtual void lock() = 0; + + // unlock the queue mutex + virtual void unlock() = 0; + + private: + typedef std::list List; + + List m_queue; +}; + +#endif diff --git a/CMessageSocket.cpp b/CMessageSocket.cpp new file mode 100644 index 00000000..c89c0db1 --- /dev/null +++ b/CMessageSocket.cpp @@ -0,0 +1,153 @@ +#include "CMessageSocket.h" +#include "TMethodJob.h" +#include "CTrace.h" +#include +#include + +// +// CMessageSocket +// + +CMessageSocket::CMessageSocket(ISocket* socket) : + m_socket(socket), + m_buffer(NULL), + m_size(0), + m_capacity(0), + m_msgSize(0) +{ + m_socket->setReadJob(new TMethodJob(this, + &CMessageSocket::readJobCB)); +} + +CMessageSocket::~CMessageSocket() +{ + delete m_socket; + delete[] m_buffer; +} + +void CMessageSocket::setWriteJob(IJob* adoptedJob) +{ + CSocket::setWriteJob(adoptedJob); + if (adoptedJob != NULL) + m_socket->setWriteJob(new TMethodJob(this, + &CMessageSocket::writeJobCB)); + else + m_socket->setWriteJob(NULL); +} + +void CMessageSocket::connect(const CString&, UInt16) +{ + assert(0 && "connect() illegal on CMessageSocket"); +} + +void CMessageSocket::listen(const CString&, UInt16) +{ + assert(0 && "listen() illegal on CMessageSocket"); +} + +ISocket* CMessageSocket::accept() +{ + assert(0 && "accept() illegal on CMessageSocket"); + return NULL; +} + +SInt32 CMessageSocket::read(void* buffer, SInt32 n) +{ + // if we don't have an entire message yet then read more data + if (m_size == 0 || m_size < m_msgSize) { + doRead(); + } + + // if we don't have a whole message yet then return 0 + if (m_size < m_msgSize) + return 0; + + // how many bytes should we return? + if (m_msgSize - 2 < n) + n = m_msgSize - 2; + + // copy data + // FIXME -- should have method for retrieving size of next message + ::memcpy(buffer, m_buffer + 2, n); + + // discard returned message + ::memmove(m_buffer, m_buffer + m_msgSize, m_size - m_msgSize); + m_size -= m_msgSize; + m_msgSize = 0; + + // get next message size + if (m_size >= 2) { + m_msgSize = static_cast( + (static_cast(m_buffer[1]) << 8) + + (static_cast(m_buffer[2]) )); + TRACE((" next message size: %d", m_msgSize)); + } + + return n; +} + +void CMessageSocket::write(const void* buffer, SInt32 n) +{ + // FIXME -- no fixed size buffers + char tmp[512]; + assert(n < (SInt32)sizeof(tmp) - 2); + ::memcpy(tmp + 2, buffer, n); + n += 2; + tmp[0] = static_cast((n >> 8) & 0xff); + tmp[1] = static_cast(n & 0xff); + m_socket->write(tmp, n); +} + +SInt32 CMessageSocket::doRead() +{ + // if read buffer is full then grow it + if (m_size == m_capacity) { + // compute new capacity and allocate space + SInt32 newCapacity = (m_capacity < 256) ? 256 : 2 * m_capacity; + UInt8* newBuffer = new UInt8[newCapacity]; + + // cut over + ::memcpy(newBuffer, m_buffer, m_size); + delete[] m_buffer; + m_buffer = newBuffer; + m_capacity = newCapacity; + } + + // read as much data as possible + const SInt32 numRead = m_socket->read(m_buffer + m_size, + m_capacity - m_size); + TRACE(("socket %p read %d bytes", this, numRead)); + + // hangup is a special case. if buffer isn't empty then we'll + // discard the partial message. + if (numRead == -1) + return numRead; + + // get next message size + if (m_size < 2 && m_size + numRead >= 2) { + m_msgSize = static_cast( + (static_cast(m_buffer[0]) << 8) + + (static_cast(m_buffer[1]) )); + TRACE((" next message size: %d", m_msgSize)); + } + + m_size += numRead; + return numRead; +} + +void CMessageSocket::readJobCB() +{ + if (doRead() == -1) { + // remote side hungup. don't check for readability anymore. + m_socket->setReadJob(NULL); + } + else if (m_size > 0 && m_size >= m_msgSize) { + TRACE((" message ready")); + runReadJob(); + } +} + +void CMessageSocket::writeJobCB() +{ + runWriteJob(); +} diff --git a/CMessageSocket.h b/CMessageSocket.h new file mode 100644 index 00000000..b199c3ec --- /dev/null +++ b/CMessageSocket.h @@ -0,0 +1,33 @@ +#ifndef CMESSAGESOCKET_H +#define CMESSAGESOCKET_H + +#include "CSocket.h" + +class CMessageSocket : public CSocket { + public: + CMessageSocket(ISocket* adoptedSocket); + virtual ~CMessageSocket(); + + // ISocket overrides + // connect(), listen(), and accept() may not be called. + virtual void setWriteJob(IJob* adoptedJob); + virtual void connect(const CString& hostname, UInt16 port); + virtual void listen(const CString& hostname, UInt16 port); + virtual ISocket* accept(); + virtual SInt32 read(void* buffer, SInt32 numBytes); + virtual void write(const void* buffer, SInt32 numBytes); + + private: + SInt32 doRead(); + virtual void readJobCB(); + virtual void writeJobCB(); + + private: + ISocket* m_socket; + UInt8* m_buffer; + SInt32 m_size; + SInt32 m_capacity; + SInt32 m_msgSize; +}; + +#endif diff --git a/CProtocol.h b/CProtocol.h new file mode 100644 index 00000000..83659fd4 --- /dev/null +++ b/CProtocol.h @@ -0,0 +1,20 @@ +#ifndef CPROTOCOL_H +#define CPROTOCOL_H + +#include "BasicTypes.h" + +class CProtocol { + public: + CProtocol(); + virtual ~CProtocol(); + + // manipulators + + + // accessors + + void ReadMessage(ISocket*, CMessage&) const; + void WriteMessage(ISocket*, const CMessage&) const; +}; + +#endif diff --git a/CScreenProxy.cpp b/CScreenProxy.cpp new file mode 100644 index 00000000..d287107d --- /dev/null +++ b/CScreenProxy.cpp @@ -0,0 +1,234 @@ +#include "CScreenProxy.h" +#include "ISocket.h" +#include "CMessageSocket.h" +#include "TMethodJob.h" +#include "CTrace.h" +// +// CScreenProxy +// + +CScreenProxy::CScreenProxy(const CString& name, ISocket* socket) : + m_name(name), + m_socket(socket), + m_w(0), m_h(0) +{ + assert(!m_name.empty()); + assert(m_socket != NULL); + + m_socket = new CMessageSocket(m_socket); + m_socket->setReadJob(new TMethodJob(this, + &CScreenProxy::onRead)); +} + +CScreenProxy::~CScreenProxy() +{ + delete m_socket; +} + +void CScreenProxy::open(bool isPrimary) +{ + char buf[2]; + memcpy(buf, "\002", 1); + buf[1] = static_cast(isPrimary ? 1 : 0); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::close() +{ + char buf[1]; + memcpy(buf, "\003", 1); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::enterScreen(SInt32 x, SInt32 y) +{ + char buf[9]; + memcpy(buf, "\004", 1); + buf[1] = static_cast((x >> 24) & 0xff); + buf[2] = static_cast((x >> 16) & 0xff); + buf[3] = static_cast((x >> 8) & 0xff); + buf[4] = static_cast(x & 0xff); + buf[5] = static_cast((y >> 24) & 0xff); + buf[6] = static_cast((y >> 16) & 0xff); + buf[7] = static_cast((y >> 8) & 0xff); + buf[8] = static_cast(y & 0xff); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::leaveScreen() +{ + char buf[1]; + memcpy(buf, "\005", 1); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::warpCursor(SInt32 x, SInt32 y) +{ + char buf[9]; + memcpy(buf, "\020", 1); + buf[1] = static_cast((x >> 24) & 0xff); + buf[2] = static_cast((x >> 16) & 0xff); + buf[3] = static_cast((x >> 8) & 0xff); + buf[4] = static_cast(x & 0xff); + buf[5] = static_cast((y >> 24) & 0xff); + buf[6] = static_cast((y >> 16) & 0xff); + buf[7] = static_cast((y >> 8) & 0xff); + buf[8] = static_cast(y & 0xff); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::setClipboard(const IClipboard*) +{ + // FIXME +} + +void CScreenProxy::onKeyDown(KeyID k) +{ + char buf[5]; + memcpy(buf, "\007", 1); + buf[1] = static_cast((k >> 24) & 0xff); + buf[2] = static_cast((k >> 16) & 0xff); + buf[3] = static_cast((k >> 8) & 0xff); + buf[4] = static_cast(k & 0xff); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::onKeyRepeat(KeyID k, SInt32 n) +{ + char buf[9]; + memcpy(buf, "\010", 1); + buf[1] = static_cast((k >> 24) & 0xff); + buf[2] = static_cast((k >> 16) & 0xff); + buf[3] = static_cast((k >> 8) & 0xff); + buf[4] = static_cast(k & 0xff); + buf[5] = static_cast((n >> 24) & 0xff); + buf[6] = static_cast((n >> 16) & 0xff); + buf[7] = static_cast((n >> 8) & 0xff); + buf[8] = static_cast(n & 0xff); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::onKeyUp(KeyID k) +{ + char buf[5]; + memcpy(buf, "\011", 1); + buf[1] = static_cast((k >> 24) & 0xff); + buf[2] = static_cast((k >> 16) & 0xff); + buf[3] = static_cast((k >> 8) & 0xff); + buf[4] = static_cast(k & 0xff); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::onKeyToggle(KeyToggleMask m) +{ + char buf[3]; + memcpy(buf, "\012", 1); + buf[1] = static_cast((m >> 8) & 0xff); + buf[2] = static_cast(m & 0xff); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::onMouseDown(ButtonID b) +{ + char buf[2]; + memcpy(buf, "\013", 1); + buf[1] = static_cast(b & 0xff); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::onMouseUp(ButtonID b) +{ + char buf[2]; + memcpy(buf, "\014", 1); + buf[1] = static_cast(b & 0xff); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::onMouseMove(SInt32 x, SInt32 y) +{ + char buf[9]; + memcpy(buf, "\015", 1); + buf[1] = static_cast((x >> 24) & 0xff); + buf[2] = static_cast((x >> 16) & 0xff); + buf[3] = static_cast((x >> 8) & 0xff); + buf[4] = static_cast(x & 0xff); + buf[5] = static_cast((y >> 24) & 0xff); + buf[6] = static_cast((y >> 16) & 0xff); + buf[7] = static_cast((y >> 8) & 0xff); + buf[8] = static_cast(y & 0xff); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::onMouseWheel(SInt32 n) +{ + char buf[5]; + memcpy(buf, "\016", 1); + buf[1] = static_cast((n >> 24) & 0xff); + buf[2] = static_cast((n >> 16) & 0xff); + buf[3] = static_cast((n >> 8) & 0xff); + buf[4] = static_cast(n & 0xff); + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::onScreenSaver(bool show) +{ + char buf[2]; + memcpy(buf, "\017", 1); + buf[1] = show ? 1 : 0; + m_socket->write(buf, sizeof(buf)); +} + +void CScreenProxy::onClipboardChanged() +{ + // FIXME +} + +CString CScreenProxy::getName() const +{ + return m_name; +} + +void CScreenProxy::getSize(SInt32* w, SInt32* h) const +{ + assert(w != NULL); + assert(h != NULL); + + *w = m_w; + *h = m_h; +} + +void CScreenProxy::getClipboard(IClipboard*) const +{ + // FIXME +} + +void CScreenProxy::onRead() +{ + char buf[512]; + SInt32 n = m_socket->read(buf, sizeof(buf)); + if (n == -1) { + // FIXME -- disconnect + TRACE(("hangup")); + } + else if (n > 0) { + switch (buf[0]) { + case '\201': + m_w = static_cast( + (static_cast(buf[1]) << 24) + + (static_cast(buf[2]) << 16) + + (static_cast(buf[3]) << 8) + + (static_cast(buf[4]) )); + m_h = static_cast( + (static_cast(buf[5]) << 24) + + (static_cast(buf[6]) << 16) + + (static_cast(buf[7]) << 8) + + (static_cast(buf[8]) )); + TRACE(("new size: %dx%d", m_w, m_h)); + break; + + default: + TRACE(("unknown message: 0x%02x, %d bytes", buf[0], n)); + break; + } + } +} diff --git a/CScreenProxy.h b/CScreenProxy.h new file mode 100644 index 00000000..39786d82 --- /dev/null +++ b/CScreenProxy.h @@ -0,0 +1,43 @@ +#ifndef CSCREENPROXY_H +#define CSCREENPROXY_H + +#include "IScreen.h" + +class ISocket; + +class CScreenProxy : public IScreen { + public: + CScreenProxy(const CString& name, ISocket*); + virtual ~CScreenProxy(); + + // IScreen overrides + virtual void open(bool); + virtual void close(); + virtual void enterScreen(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void leaveScreen(); + virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void setClipboard(const IClipboard*); + virtual void onScreenSaver(bool show); + virtual void onKeyDown(KeyID); + virtual void onKeyRepeat(KeyID, SInt32 count); + virtual void onKeyUp(KeyID); + virtual void onKeyToggle(KeyToggleMask); + virtual void onMouseDown(ButtonID); + virtual void onMouseUp(ButtonID); + virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void onMouseWheel(SInt32 delta); + virtual void onClipboardChanged(); + virtual CString getName() const; + virtual void getSize(SInt32* width, SInt32* height) const; + virtual void getClipboard(IClipboard*) const; + + private: + void onRead(); + + private: + CString m_name; + ISocket* m_socket; + SInt32 m_w, m_h; +}; + +#endif diff --git a/CServer.cpp b/CServer.cpp new file mode 100644 index 00000000..fb28810f --- /dev/null +++ b/CServer.cpp @@ -0,0 +1,811 @@ +#include "CServer.h" +#include "CEvent.h" +#include "IEventQueue.h" +#include "IScreen.h" +#include "CScreenProxy.h" +#include "ISocket.h" +#include "CSocketFactory.h" +#include "CMessageSocket.h" +#include "TMethodJob.h" +#include "CTrace.h" +#include +#include +#include + +#if !defined(NDEBUG) +static const char* s_dirName[] = { "left", "right", "top", "bottom" }; +#endif + +// +// CServerSocketJob +// + +class CServerSocketJob : public IJob { + public: + typedef void (CServer::*ServerMethod)(ISocket*); + + CServerSocketJob(CServer*, ServerMethod, ISocket*); + virtual ~CServerSocketJob(); + + // IJob overrides + virtual void run(); + + private: + CServer* m_server; + ServerMethod m_method; + ISocket* m_socket; +}; + +CServerSocketJob::CServerSocketJob(CServer* server, + ServerMethod method, ISocket* socket) : + m_server(server), + m_method(method), + m_socket(socket) +{ + // do nothing +} + +CServerSocketJob::~CServerSocketJob() +{ + // do nothing +} + +void CServerSocketJob::run() +{ + (m_server->*m_method)(m_socket); +} + + +// +// CServer +// + +class XServerScreenExists { // FIXME + public: + XServerScreenExists(const CString&) { } +}; + +// the width/height of the zone on the edge of the local screen that +// will provoke a switch to a neighboring screen. this generally +// shouldn't be changed because it effectively reduces the size of +// the local screen's screen. +// FIXME -- should get this from the local screen itself. it may +// need a slightly larger zone (to avoid virtual screens) or it may +// be able to generate off-screen coordinates to provoke the switch +// in which case the size can be zero. +const SInt32 CServer::s_zoneSize = 1; + +CServer::CServer() : m_running(false), m_done(false), + m_localScreen(NULL), + m_activeScreen(NULL), + m_listenHost(), + // FIXME -- define kDefaultPort + m_listenPort(40001/*CProtocol::kDefaultPort*/), + m_listenSocket(NULL) +{ + // FIXME +} + +CServer::~CServer() +{ + assert(m_listenSocket == NULL); + + // FIXME +} + +void CServer::setListenPort( + const CString& hostname, UInt16 port) +{ + m_listenHost = hostname; + m_listenPort = port; +} + +void CServer::addLocalScreen(IScreen* screen) +{ + assert(screen != NULL); + assert(m_running == false); + assert(m_localScreen == NULL); + + addScreen(screen->getName(), screen); + m_localScreen = screen; + m_activeScreen = screen; + + // open the screen as primary + screen->open(true); +} + +void CServer::addRemoteScreen(const CString& name) +{ + addScreen(name, NULL); +} + +void CServer::addScreen(const CString& name, IScreen* screen) +{ + assert(!name.empty()); + + // cannot add a screen multiple times + if (m_map.count(name) != 0) + throw XServerScreenExists(name); + + // add entry for screen in the map + ScreenCell& cell = m_map[name]; + + // set the cell's screen + cell.m_screen = screen; +} + +void CServer::removeScreen(const CString& name) +{ + // screen must in map + assert(!name.empty()); + assert(m_map.count(name) == 1); + + // look up cell + ScreenCell& cell = m_map[name]; + + // if this is the local screen then there must not be any other + // screens and we must not be running. + assert(cell.m_screen != m_localScreen || (m_map.size() == 1 && !m_running)); + + // if this is the active screen then warp to the local screen, or + // set no active screen if this is the local screen. + if (cell.m_screen == m_localScreen) { + m_activeScreen = NULL; + m_localScreen = NULL; + } + else if (cell.m_screen == m_activeScreen) { + setActiveScreen(m_localScreen); + } + + // close the screen + if (cell.m_screen) + cell.m_screen->close(); + + // fix up map + if (!cell.m_neighbor[kLeft].empty()) { + assert(m_map.count(cell.m_neighbor[kLeft]) == 1); + m_map[cell.m_neighbor[kLeft]].m_neighbor[kRight] = + cell.m_neighbor[kRight]; + } + if (!cell.m_neighbor[kRight].empty()) { + assert(m_map.count(cell.m_neighbor[kRight]) == 1); + m_map[cell.m_neighbor[kRight]].m_neighbor[kLeft] = + cell.m_neighbor[kLeft]; + } + if (!cell.m_neighbor[kTop].empty()) { + assert(m_map.count(cell.m_neighbor[kTop]) == 1); + m_map[cell.m_neighbor[kTop]].m_neighbor[kBottom] = + cell.m_neighbor[kBottom]; + } + if (!cell.m_neighbor[kBottom].empty()) { + assert(m_map.count(cell.m_neighbor[kBottom]) == 1); + m_map[cell.m_neighbor[kBottom]].m_neighbor[kTop] = + cell.m_neighbor[kTop]; + } +} + +void CServer::connectEdge( + const CString& src, EDirection srcSide, + const CString& dst) +{ + // check input + assert(!src.empty()); + assert(!dst.empty()); + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // both screens must exist in map + assert(m_map.count(src) == 1); + assert(m_map.count(dst) == 1); + + // look up map entry + ScreenCell& cell = m_map[src]; + + // set edge + cell.m_neighbor[srcSide] = dst; + + TRACE(("connect %s:%s to %s", src.c_str(), + s_dirName[srcSide], + cell.m_neighbor[srcSide].c_str())); +} + +void CServer::disconnectEdge( + const CString& src, EDirection srcSide) +{ + // check input + assert(!src.empty()); + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + assert(m_map.count(src) == 1); + + TRACE(("disconnect %s:%s from %s", src.c_str(), + s_dirName[srcSide], + m_map[src].m_neighbor[srcSide].c_str())); + + // look up map entry + ScreenCell& cell = m_map[src]; + + // set edge + cell.m_neighbor[srcSide] = CString(); +} + +void CServer::run() +{ + assert(m_running == false); + assert(m_activeScreen != NULL); + assert(m_activeScreen == m_localScreen); + + // prepare socket to listen for remote screens + // FIXME -- need m_socketFactory (creates sockets of desired type) +// m_listenSocket = m_socketFactory->createSocket(); +m_listenSocket = CSOCKETFACTORY->create(); + m_listenSocket->setReadJob(new TMethodJob(this, + &CServer::newConnectionCB)); + // FIXME -- keep retrying until this works (in case of FIN_WAIT). + // also, must clean up m_listenSocket if this method throws anywhere. + m_listenSocket->listen(m_listenHost, m_listenPort); + + // now running + m_running = true; + + // event loop + IEventQueue* queue = CEQ; + while (!m_done) { + // wait for new connections, network messages, and user events + queue->wait(-1.0); + + // handle events + while (!queue->isEmpty()) { + // get the next event + CEvent event; + queue->pop(&event); + + // handle it + switch (event.m_any.m_type) { + case CEventBase::kNull: + // do nothing + break; + + case CEventBase::kKeyDown: + case CEventBase::kKeyRepeat: + case CEventBase::kKeyUp: + if (!onCommandKey(&event.m_key)) + relayEvent(&event); + break; + + case CEventBase::kMouseDown: + case CEventBase::kMouseUp: + case CEventBase::kMouseWheel: + relayEvent(&event); + break; + + case CEventBase::kMouseMove: + if (m_localScreen == m_activeScreen) + onLocalMouseMove(event.m_mouse.m_x, event.m_mouse.m_y); + else + onRemoteMouseMove(event.m_mouse.m_x, event.m_mouse.m_y); + break; + + case CEventBase::kScreenSize: + // FIXME + break; + } + } + } + + // reset + m_running = false; + m_done = false; + + // tell screens to shutdown + // FIXME + + // close our socket + delete m_listenSocket; + m_listenSocket = NULL; +} + +void CServer::onClipboardChanged(IScreen*) +{ + // FIXME -- should take screen name not screen pointer + // FIXME +} + +void CServer::setActiveScreen(IScreen* screen) +{ + // FIXME -- should take screen name not screen pointer + assert(screen != NULL); + assert(m_map.count(screen->getName()) == 1); + + // ignore if no change + if (m_activeScreen == screen) + return; + + // get center of screen + SInt32 w, h; + screen->getSize(&w, &h); + w >>= 1; + h >>= 1; + + // switch + switchScreen(screen, w, h); +} + +IScreen* CServer::getActiveScreen() const +{ + return m_activeScreen; +} + +void CServer::relayEvent(const CEvent* event) +{ + assert(event != NULL); + assert(m_activeScreen != NULL); + + // ignore attempts to relay to the local screen + if (m_activeScreen == m_localScreen) + return; + + // relay the event + switch (event->m_any.m_type) { + case CEventBase::kNull: + // do nothing + break; + + case CEventBase::kKeyDown: + m_activeScreen->onKeyDown(event->m_key.m_key); + break; + + case CEventBase::kKeyRepeat: + m_activeScreen->onKeyRepeat(event->m_key.m_key, event->m_key.m_count); + break; + + case CEventBase::kKeyUp: + m_activeScreen->onKeyUp(event->m_key.m_key); + break; + + case CEventBase::kMouseDown: + m_activeScreen->onMouseDown(event->m_mouse.m_button); + break; + + case CEventBase::kMouseUp: + m_activeScreen->onMouseUp(event->m_mouse.m_button); + break; + + case CEventBase::kMouseWheel: + m_activeScreen->onMouseWheel(event->m_mouse.m_x); + break; + + case CEventBase::kMouseMove: + assert(0 && "kMouseMove relayed"); + break; + + default: + assert(0 && "invalid event relayed"); + break; + } +} + +bool CServer::onCommandKey(const CEventKey* /*keyEvent*/) +{ + // FIXME -- strip out command keys (e.g. lock to screen, warp, etc.) + return false; +} + +void CServer::onLocalMouseMove(SInt32 x, SInt32 y) +{ + assert(m_activeScreen == m_localScreen); + + // ignore if locked to screen + if (isLockedToScreen()) + return; + + // get local screen's size + SInt32 w, h; + m_activeScreen->getSize(&w, &h); + + // see if we should change screens + EDirection dir; + if (x < s_zoneSize) { + x -= s_zoneSize; + dir = kLeft; + } + else if (x >= w - s_zoneSize) { + x += s_zoneSize; + dir = kRight; + } + else if (y < s_zoneSize) { + y -= s_zoneSize; + dir = kTop; + } + else if (y >= h - s_zoneSize) { + y += s_zoneSize; + dir = kBottom; + } + else { + // still on local screen + return; + } + TRACE(("leave %s on %s", m_activeScreen->getName().c_str(), s_dirName[dir])); + + // get new screen. if no screen in that direction then ignore move. + IScreen* newScreen = getNeighbor(m_activeScreen, dir, x, y); + if (newScreen == NULL) + return; + + // remap position to account for resolution differences between screens + mapPosition(m_activeScreen, dir, newScreen, x, y); + + // switch screen + switchScreen(newScreen, x, y); +} + +void CServer::onRemoteMouseMove(SInt32 dx, SInt32 dy) +{ + assert(m_activeScreen != NULL); + assert(m_activeScreen != m_localScreen); + + // put mouse back in center of local screen's grab area +// XXX m_localScreen->warpToCenter(); + + // save old position + const SInt32 xOld = m_x; + const SInt32 yOld = m_y; + + // accumulate mouse position + m_x += dx; + m_y += dy; + + // get active screen's size + SInt32 w, h; + m_activeScreen->getSize(&w, &h); + + // switch screens if mouse is outside screen and not locked to screen + IScreen* newScreen = NULL; + if (!isLockedToScreen()) { + // find direction of neighbor + EDirection dir; + if (m_x < 0) + dir = kLeft; + else if (m_x > w - 1) + dir = kRight; + else if (m_y < 0) + dir = kTop; + else if (m_y > h - 1) + dir = kBottom; + else + newScreen = m_activeScreen; + + // get neighbor if we should switch + if (newScreen == NULL) { + TRACE(("leave %s on %s", m_activeScreen->getName().c_str(), + s_dirName[dir])); + + SInt32 x = m_x, y = m_y; + newScreen = getNeighbor(m_activeScreen, dir, x, y); + + // remap position to account for resolution differences + if (newScreen != NULL) { + m_x = x; + m_y = y; + mapPosition(m_activeScreen, dir, newScreen, m_x, m_y); + } + else { + if (m_x < 0) + m_x = 0; + else if (m_x > w - 1) + m_x = w - 1; + if (m_y < 0) + m_y = 0; + else if (m_y > h - 1) + m_y = h - 1; + } + } + } + + // clamp mouse position if locked to screen + else { + TRACE(("clamp to %s", m_activeScreen->getName().c_str())); + + if (m_x < 0) + m_x = 0; + else if (m_x > w - 1) + m_x = w - 1; + if (m_y < 0) + m_y = 0; + else if (m_y > h - 1) + m_y = h - 1; + } + + // if on same screen then warp cursor + if (newScreen == NULL || newScreen == m_activeScreen) { + // ignore if clamped mouse didn't move + if (m_x != xOld || m_y != yOld) { + TRACE(("move on %s to %d,%d", + m_activeScreen->getName().c_str(), m_x, m_y)); + m_activeScreen->onMouseMove(m_x, m_y); + } + } + + // otherwise switch the screen + else { + switchScreen(newScreen, m_x, m_y); + } +} + +bool CServer::isLockedToScreen() const +{ + // FIXME + return false; +} + +void CServer::mapPosition( + const IScreen* src, EDirection srcSide, + const IScreen* dst, SInt32& x, SInt32& y) const +{ + assert(src != NULL); + assert(dst != NULL); + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // get sizes + SInt32 wSrc, hSrc, wDst, hDst; + src->getSize(&wSrc, &hSrc); + dst->getSize(&wDst, &hDst); + + // remap + switch (srcSide) { + case kLeft: + case kRight: + assert(y >= 0 && y < hSrc); + y = static_cast(0.5 + y * + static_cast(hDst - 1) / (hSrc - 1)); + break; + + case kTop: + case kBottom: + assert(x >= 0 && x < wSrc); + x = static_cast(0.5 + x * + static_cast(wSrc - 1) / (wSrc - 1)); + break; + } +} + +IScreen* CServer::getNeighbor( + const IScreen* src, EDirection dir) const +{ + // check input + assert(src != NULL); + assert(dir >= kFirstDirection && dir <= kLastDirection); + assert(m_map.count(src->getName()) == 1); + + // look up source cell + ScreenMap::const_iterator index = m_map.find(src->getName()); + do { + // look up name of neighbor + const ScreenCell& cell = index->second; + const CString dstName(cell.m_neighbor[dir]); + + // if nothing in that direction then return NULL + if (dstName.empty()) + return NULL; + + // look up neighbor cell + assert(m_map.count(dstName) == 1); + index = m_map.find(dstName); + + // if no screen pointer then can't go to that neighbor so keep + // searching in the same direction. +#ifndef NDEBUG + if (index->second.m_screen == NULL) + TRACE(("skipping over unconnected screen %s", dstName.c_str())); +#endif + } while (index->second.m_screen == NULL); + + return index->second.m_screen; +} + +IScreen* CServer::getNeighbor( + const IScreen* src, EDirection srcSide, + SInt32& x, SInt32& y) const +{ + // given a position relative to src and which side of the screen we + // left, find the screen we should move onto and where. if the + // position is sufficiently far from src then we may cross multiple + // screens. + + // check input + assert(src != NULL); + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // get the first neighbor + IScreen* dst = getNeighbor(src, srcSide); + IScreen* lastGoodScreen = dst; + + // get the original screen's size (needed for kRight and kBottom) + SInt32 w, h; + src->getSize(&w, &h); + + // find destination screen, adjusting x or y (but not both) + switch (srcSide) { + case kLeft: + while (dst) { + lastGoodScreen = dst; + lastGoodScreen->getSize(&w, &h); + x += w; + if (x >= 0) + break; + TRACE(("skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + break; + + case kRight: + while (dst) { + lastGoodScreen = dst; + x -= w; + lastGoodScreen->getSize(&w, &h); + if (x < w) + break; + TRACE(("skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + break; + + case kTop: + while (dst) { + lastGoodScreen = dst; + lastGoodScreen->getSize(&w, &h); + y += h; + if (y >= 0) + break; + TRACE(("skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + break; + + case kBottom: + while (dst) { + lastGoodScreen = dst; + y -= h; + lastGoodScreen->getSize(&w, &h); + if (y < h) + break; + TRACE(("skipping over screen %s", dst->getName().c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + break; + } + + // if entering local screen then be sure to move in far enough to + // avoid the switching zone. if entering a side that doesn't have + // a neighbor (i.e. an asymmetrical side) then we don't need to + // move inwards because that side can't provoke a switch. + if (lastGoodScreen == m_localScreen) { + ScreenMap::const_iterator index = m_map.find(m_localScreen->getName()); + const ScreenCell& cell = index->second; + switch (srcSide) { + case kLeft: + if (!cell.m_neighbor[kRight].empty() && x > w - 1 - s_zoneSize) + x = w - 1 - s_zoneSize; + break; + + case kRight: + if (!cell.m_neighbor[kLeft].empty() && x < s_zoneSize) + x = s_zoneSize; + break; + + case kTop: + if (!cell.m_neighbor[kBottom].empty() && y > h - 1 - s_zoneSize) + y = h - 1 - s_zoneSize; + break; + + case kBottom: + if (!cell.m_neighbor[kTop].empty() && y < s_zoneSize) + y = s_zoneSize; + break; + } + } + + return lastGoodScreen; +} + +void CServer::switchScreen( + IScreen* screen, SInt32 x, SInt32 y) +{ + assert(screen != NULL); + assert(m_running == true); + assert(m_activeScreen != NULL); +#ifndef NDEBUG + { + SInt32 w, h; + screen->getSize(&w, &h); + assert(x >= 0 && y >= 0 && x < w && y < h); + } +#endif + + TRACE(("switch %s to %s at %d,%d", m_activeScreen->getName().c_str(), + screen->getName().c_str(), x, y)); + + // wrapping means leaving the active screen and entering it again. + // since that's a waste of time we skip that and just warp the + // mouse. + if (m_activeScreen != screen) { + // leave active screen + m_activeScreen->leaveScreen(); + + // cut over + m_activeScreen = screen; + + // enter new screen + m_activeScreen->enterScreen(x, y); + } + else { + m_activeScreen->warpCursor(x, y); + } + + // record new position + m_x = x; + m_y = y; +} + +void CServer::newConnectionCB() +{ + ISocket* socket = m_listenSocket->accept(); + TRACE(("accepted socket %p", socket)); + socket->setReadJob(new CServerSocketJob(this, &CServer::loginCB, socket)); + m_logins.insert(socket); +} + +void CServer::loginCB(ISocket* socket) +{ + // FIXME -- no fixed size buffers + UInt8 buffer[512]; + SInt32 n = socket->read(buffer, sizeof(buffer)); + if (n == -1) { + TRACE(("socket %p disconnected", socket)); + goto fail; + } + TRACE(("read %d bytes from socket %p", n, socket)); + if (n <= 10) { + TRACE(("socket %p: bogus %d byte message; hanging up", socket, n)); + goto fail; + } + if (n > 10) { + if (::memcmp(buffer, "SYNERGY\000\001", 9) != 0) { + TRACE(("socket %p: bad login", socket)); + goto fail; + } + + const SInt32 nameLen = static_cast(buffer[9]); + if (nameLen < 1 || nameLen > 64) { + TRACE(("socket %p: bad login name length %d", socket, nameLen)); + goto fail; + } + + for (SInt32 i = 0; i < nameLen; ++i) + if (!isalnum(buffer[10 + i])) { + TRACE(("socket %p: bad login name", socket)); + goto fail; + } + + CString name(reinterpret_cast(buffer + 10), nameLen); + const ScreenMap::iterator index = m_map.find(name); + if (index == m_map.end()) { + TRACE(("socket %p: unknown screen %s", socket, name.c_str())); + goto fail; + } + if (index->second.m_screen != NULL) { + TRACE(("socket %p: screen %s already connected", + socket, name.c_str())); + goto fail; + } + + TRACE(("socket %p: login %s", socket, name.c_str())); + CScreenProxy* screen = new CScreenProxy(name, socket); + m_logins.erase(socket); + index->second.m_screen = screen; + index->second.m_screen->open(false); + } + return; + +fail: + m_logins.erase(socket); + delete socket; +} diff --git a/CServer.h b/CServer.h new file mode 100644 index 00000000..b95fa7ef --- /dev/null +++ b/CServer.h @@ -0,0 +1,103 @@ +#ifndef CSERVER_H +#define CSERVER_H + +#include "IServer.h" +#include "BasicTypes.h" +#include "CString.h" +#include +#include + +class CEvent; +class CEventKey; +class IScreen; +class ISocket; + +class CServer : public IServer { + public: + enum EDirection { kLeft, kRight, kTop, kBottom, + kFirstDirection = kLeft, kLastDirection = kBottom }; + + CServer(); + virtual ~CServer(); + + // manipulators + + // set the server's interface and port to listen for remote screens + void setListenPort(const CString& hostname, UInt16 port); + + // add local screen + void addLocalScreen(IScreen*); + + // add a remote screen + void addRemoteScreen(const CString& name); + + // remove a local or remote screen. neighbors on opposite sides + // of this screen are made neighbors of each other. + void removeScreen(const CString& name); + + // connect/disconnect screen edges + void connectEdge(const CString& src, EDirection srcSide, + const CString& dst); + void disconnectEdge(const CString& src, EDirection srcSide); + + // accessors + + + // IServer overrides + virtual void run(); + virtual void onClipboardChanged(IScreen*); + virtual void setActiveScreen(IScreen*); + virtual IScreen* getActiveScreen() const; + + protected: + virtual void relayEvent(const CEvent* event); + virtual bool onCommandKey(const CEventKey* keyEvent); + virtual void onLocalMouseMove(SInt32 x, SInt32 y); + virtual void onRemoteMouseMove(SInt32 dx, SInt32 dy); + virtual bool isLockedToScreen() const; + virtual void mapPosition(const IScreen* src, EDirection srcSide, + const IScreen* dst, SInt32& x, SInt32& y) const; + IScreen* getNeighbor(const IScreen* src, EDirection) const; + IScreen* getNeighbor(const IScreen* src, EDirection srcSide, + SInt32& x, SInt32& y) const; + void switchScreen(IScreen* screen, SInt32 x, SInt32 y); + + private: + void addScreen(const CString&, IScreen*); + void newConnectionCB(); + void loginCB(ISocket*); + + struct ScreenCell { + public: + ScreenCell() : m_screen(NULL) { } + public: + IScreen* m_screen; + CString m_neighbor[kLastDirection - kFirstDirection + 1]; + }; + + private: + typedef std::map ScreenMap; + typedef std::set SocketSet; + + // main loop stuff + bool m_running; + bool m_done; + + // screen tracking + IScreen* m_localScreen; + IScreen* m_activeScreen; + SInt32 m_x, m_y; + ScreenMap m_map; + + // listen socket stuff + CString m_listenHost; + UInt16 m_listenPort; + ISocket* m_listenSocket; + + // login sockets + SocketSet m_logins; + + static const SInt32 s_zoneSize; +}; + +#endif diff --git a/CSocket.cpp b/CSocket.cpp new file mode 100644 index 00000000..311d87d0 --- /dev/null +++ b/CSocket.cpp @@ -0,0 +1,58 @@ +#include "CSocket.h" +#include "IJob.h" + +// +// CSocket +// + +CSocket::CSocket() : m_readJob(NULL), m_writeJob(NULL) +{ + // do nothing +} + +CSocket::~CSocket() +{ + delete m_readJob; + delete m_writeJob; +} + +void CSocket::setReadJob(IJob* adoptedJob) +{ + delete m_readJob; + m_readJob = adoptedJob; + onJobChanged(); +} + +void CSocket::setWriteJob(IJob* adoptedJob) +{ + delete m_writeJob; + m_writeJob = adoptedJob; + onJobChanged(); +} + +void CSocket::onJobChanged() +{ + // do nothing +} + +void CSocket::runReadJob() +{ + if (m_readJob) + m_readJob->run(); +} + +void CSocket::runWriteJob() +{ + if (m_writeJob) + m_writeJob->run(); +} + +bool CSocket::hasReadJob() const +{ + return (m_readJob != NULL); +} + +bool CSocket::hasWriteJob() const +{ + return (m_writeJob != NULL); +} diff --git a/CSocket.h b/CSocket.h new file mode 100644 index 00000000..aa4cf9f8 --- /dev/null +++ b/CSocket.h @@ -0,0 +1,41 @@ +#ifndef CSOCKET_H +#define CSOCKET_H + +#include "ISocket.h" + +class IJob; + +class CSocket : public ISocket { + public: + CSocket(); + virtual ~CSocket(); + + // ISocket overrides + virtual void setReadJob(IJob* adoptedJob); + virtual void setWriteJob(IJob* adoptedJob); + virtual void connect(const CString& hostname, UInt16 port) = 0; + virtual void listen(const CString& hostname, UInt16 port) = 0; + virtual ISocket* accept() = 0; + virtual SInt32 read(void* buffer, SInt32 numBytes) = 0; + virtual void write(const void* buffer, SInt32 numBytes) = 0; + + protected: + // called when the read or write job is changed. default does nothing. + virtual void onJobChanged(); + + // subclasses should call these at the appropriate time. different + // platforms will arrange to detect these situations differently. + // does nothing if the respective job is NULL. + void runReadJob(); + void runWriteJob(); + + // return true iff the socket has a read job or a write job + bool hasReadJob() const; + bool hasWriteJob() const; + + private: + IJob* m_readJob; + IJob* m_writeJob; +}; + +#endif diff --git a/CSocketFactory.cpp b/CSocketFactory.cpp new file mode 100644 index 00000000..0995b897 --- /dev/null +++ b/CSocketFactory.cpp @@ -0,0 +1,31 @@ +#include "CSocketFactory.h" +#include "BasicTypes.h" +#include + +// +// CSocketFactory +// + +CSocketFactory* CSocketFactory::s_instance = NULL; + +CSocketFactory::CSocketFactory() +{ + // do nothing +} + +CSocketFactory::~CSocketFactory() +{ + // do nothing +} + +void CSocketFactory::setInstance(CSocketFactory* factory) +{ + delete s_instance; + s_instance = factory; +} + +CSocketFactory* CSocketFactory::getInstance() +{ + assert(s_instance != NULL); + return s_instance; +} diff --git a/CSocketFactory.h b/CSocketFactory.h new file mode 100644 index 00000000..7a73d391 --- /dev/null +++ b/CSocketFactory.h @@ -0,0 +1,29 @@ +#ifndef CSOCKETFACTORY_H +#define CSOCKETFACTORY_H + +#define CSOCKETFACTORY CSocketFactory::getInstance() + +class ISocket; + +class CSocketFactory { + public: + CSocketFactory(); + virtual ~CSocketFactory(); + + // manipulators + + static void setInstance(CSocketFactory*); + + // accessors + + // create a socket + virtual ISocket* create() const = 0; + + // get the global instance + static CSocketFactory* getInstance(); + + private: + static CSocketFactory* s_instance; +}; + +#endif diff --git a/CString.h b/CString.h new file mode 100644 index 00000000..9b5667ac --- /dev/null +++ b/CString.h @@ -0,0 +1,40 @@ +#ifndef CSTRING_H +#define CSTRING_H + +#include + +#ifndef CSTRING_DEF_CTOR +#define CSTRING_ALLOC1 +#define CSTRING_ALLOC2 +#define CSTRING_DEF_CTOR CString() : _Myt() { } +#endif + +// use to get appropriate type for string constants. it depends on +// the internal representation type of CString. +#define _CS(_x) _x + +class CString : public std::string { + public: + typedef char _E; + typedef _E CharT; + typedef std::allocator<_E> _A; + typedef std::string _Myt; + typedef const_iterator _It; + + // same constructors as base class + CSTRING_DEF_CTOR + CString(const _Myt& _X) : _Myt(_X) { } + CString(const _Myt& _X, size_type _P, size_type _M CSTRING_ALLOC1) : + _Myt(_X, _P, _M CSTRING_ALLOC2) { } + CString(const _E *_S, size_type _N CSTRING_ALLOC1) : + _Myt(_S, _N CSTRING_ALLOC2) { } + CString(const _E *_S CSTRING_ALLOC1) : + _Myt(_S CSTRING_ALLOC2) { } + CString(size_type _N, _E _C CSTRING_ALLOC1) : + _Myt(_N, _C CSTRING_ALLOC2) { } + CString(_It _F, _It _L CSTRING_ALLOC1) : + _Myt(_F, _L CSTRING_ALLOC2) { } +}; + +#endif + diff --git a/CTrace.cpp b/CTrace.cpp new file mode 100644 index 00000000..e782616a --- /dev/null +++ b/CTrace.cpp @@ -0,0 +1,16 @@ +#include "CTrace.h" +#include +#include + +// +// CTrace +// + +void CTrace::print(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} diff --git a/CTrace.h b/CTrace.h new file mode 100644 index 00000000..34787ac9 --- /dev/null +++ b/CTrace.h @@ -0,0 +1,19 @@ +#ifndef CTRACE_H +#define CTRACE_H + +class CTrace { + public: + static void print(const char* fmt, ...); +}; + +#if defined(NDEBUG) + +#define TRACE(_X) + +#else // NDEBUG + +#define TRACE(_X) CTrace::print ## _X + +#endif // NDEBUG + +#endif diff --git a/CUnixEventQueue.cpp b/CUnixEventQueue.cpp new file mode 100644 index 00000000..db685dca --- /dev/null +++ b/CUnixEventQueue.cpp @@ -0,0 +1,141 @@ +#include "CUnixEventQueue.h" +#include "IJob.h" +#include +#include +#include + +// +// CUnixEventQueue +// + +CUnixEventQueue::CUnixEventQueue() +{ + // do nothing +} + +CUnixEventQueue::~CUnixEventQueue() +{ + // clean up lists + clearList(m_readList); + clearList(m_writeList); +} + +void CUnixEventQueue::addFileDesc(int fd, + IJob* readJob, IJob* writeJob) +{ + assert(fd != -1); + assert(m_readList.count(fd) == 0 && m_writeList.count(fd) == 0); + assert(readJob != writeJob || readJob == NULL); + + if (readJob) + m_readList[fd] = readJob; + if (writeJob) + m_writeList[fd] = writeJob; +} + +void CUnixEventQueue::removeFileDesc(int fd) +{ + assert(fd != -1); + + // remove from lists + eraseList(m_readList, fd); + eraseList(m_writeList, fd); +} + +void CUnixEventQueue::wait(double timeout) +{ + // prepare sets + fd_set fdRead, fdWrite; + const int maxRead = prepList(m_readList, &fdRead); + const int maxWrite = prepList(m_writeList, &fdWrite); + + // compute the larger of maxRead and maxWrite + const int fdMax = (maxRead > maxWrite) ? maxRead : maxWrite; + if (fdMax == -1) + return; + + // prepare timeout + struct timeval* pTimeout = NULL; + struct timeval sTimeout; + if (timeout >= 0.0) { + sTimeout.tv_sec = static_cast(timeout); + sTimeout.tv_usec = static_cast(1000000.0 * + (timeout - sTimeout.tv_sec)); + pTimeout = &sTimeout; + } + + // wait + const int n = ::select(fdMax + 1, &fdRead, &fdWrite, NULL, pTimeout); + + // return on error or if nothing to do + if (n <= 0) + return; + + // invoke jobs + // note -- calling removeFileDesc() from a job is likely to crash the + // program because we expect all jobs with active file descriptors to + // persist for the duration of these loops. + int fd; + for (fd = 0; fd <= maxRead; ++fd) + if (FD_ISSET(fd, &fdRead)) { + assert(m_readList.count(fd) > 0); + assert(m_readList[fd] != NULL); + m_readList[fd]->run(); + } + for (fd = 0; fd <= maxWrite; ++fd) + if (FD_ISSET(fd, &fdWrite)) { + assert(m_writeList.count(fd) > 0); + assert(m_writeList[fd] != NULL); + m_writeList[fd]->run(); + } +} + +void CUnixEventQueue::lock() +{ + // do nothing +} + +void CUnixEventQueue::unlock() +{ + // do nothing +} + +void CUnixEventQueue::signalNotEmpty() +{ + // do nothing +} + +void CUnixEventQueue::eraseList(List& list, int fd) const +{ + List::iterator index = list.find(fd); + if (index != list.end()) { + delete index->second; + list.erase(index); + } +} + +void CUnixEventQueue::clearList(List& list) const +{ + for (List::const_iterator index = list.begin(); + index != list.end(); ++index) + delete index->second; + list.clear(); +} + +int CUnixEventQueue::prepList( + const List& list, void* vfdSet) const +{ + fd_set* fdSet = reinterpret_cast(vfdSet); + FD_ZERO(fdSet); + + int fdMax = -1; + for (List::const_iterator index = list.begin(); + index != list.end(); ++index) { + const int fd = index->first; + FD_SET(fd, fdSet); + if (fd > fdMax) + fdMax = fd; + } + + return fdMax; +} diff --git a/CUnixEventQueue.h b/CUnixEventQueue.h new file mode 100644 index 00000000..217efbf4 --- /dev/null +++ b/CUnixEventQueue.h @@ -0,0 +1,54 @@ +#ifndef CUNIXEVENTQUEUE_H +#define CUNIXEVENTQUEUE_H + +#include "CEventQueue.h" +#include + +#undef CEQ +#define CEQ ((CUnixEventQueue*)CEventQueue::getInstance()) + +class IJob; + +class CUnixEventQueue : public CEventQueue { + public: + CUnixEventQueue(); + virtual ~CUnixEventQueue(); + + // manipulators + + // add a file descriptor to wait on. if adoptedReadJob is not NULL + // then it'll be called when the file descriptor is readable. if + // adoptedWriteJob is not NULL then it will be called then the file + // descriptor is writable. at least one job must not be NULL and + // the jobs may not be the same. ownership of the jobs is assumed. + // the file descriptor must not have already been added or, if it + // was, it must have been removed. + void addFileDesc(int fd, + IJob* adoptedReadJob, IJob* adoptedWriteJob); + + // remove a file descriptor from the list being waited on. the + // associated jobs are destroyed. the file descriptor must have + // been added and not since removed. + void removeFileDesc(int fd); + + // IEventQueue overrides + virtual void wait(double timeout); + + protected: + // CEventQueue overrides + virtual void lock(); + virtual void unlock(); + virtual void signalNotEmpty(); + + private: + typedef std::map List; + void eraseList(List&, int fd) const; + void clearList(List&) const; + int prepList(const List&, void* fdSet) const; + + private: + List m_readList; + List m_writeList; +}; + +#endif diff --git a/CUnixTCPSocket.cpp b/CUnixTCPSocket.cpp new file mode 100644 index 00000000..ebec593b --- /dev/null +++ b/CUnixTCPSocket.cpp @@ -0,0 +1,262 @@ +#include "CUnixTCPSocket.h" +#include "CUnixEventQueue.h" +#include "CString.h" +#include "TMethodJob.h" +#include "XSocket.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern int h_errno; + +CUnixTCPSocket::CUnixTCPSocket() : m_fd(-1), + m_state(kNone), + m_addedJobs(false) +{ + // create socket + m_fd = ::socket(PF_INET, SOCK_STREAM, 0); + if (m_fd == -1) + throw XSocketCreate(::strerror(errno)); + + // make it non-blocking + int mode = ::fcntl(m_fd, F_GETFL, 0); + if (mode == -1 || ::fcntl(m_fd, F_SETFL, mode | O_NONBLOCK) == -1) { + ::close(m_fd); + throw XSocketCreate(::strerror(errno)); + } +} + +CUnixTCPSocket::CUnixTCPSocket(int fd) : m_fd(fd), + m_state(kConnected), + m_addedJobs(false) +{ + assert(m_fd != -1); +} + +CUnixTCPSocket::~CUnixTCPSocket() +{ + assert(m_fd != -1); + + // unhook events + if (m_addedJobs) + CEQ->removeFileDesc(m_fd); + + // drain socket + if (m_state == kConnected) + ::shutdown(m_fd, 0); + + // close socket + ::close(m_fd); +} + +void CUnixTCPSocket::onJobChanged() +{ + // remove old jobs + if (m_addedJobs) { + CEQ->removeFileDesc(m_fd); + m_addedJobs = false; + } + + // which jobs should we install? + bool doRead = false; + bool doWrite = false; + switch (m_state) { + case kNone: + return; + + case kConnecting: + doWrite = true; + break; + + case kConnected: + doRead = hasReadJob(); + doWrite = hasWriteJob(); + break; + + case kListening: + doRead = true; + break; + } + + // make jobs + IJob* readJob = doRead ? new TMethodJob(this, + &CUnixTCPSocket::readCB) : NULL; + IJob* writeJob = doWrite ? new TMethodJob(this, + &CUnixTCPSocket::writeCB) : NULL; + + // install jobs + CEQ->addFileDesc(m_fd, readJob, writeJob); + m_addedJobs = true; +} + +void CUnixTCPSocket::readCB() +{ + runReadJob(); +} + +void CUnixTCPSocket::writeCB() +{ + if (m_state == kConnecting) { + // now connected. start watching for reads. + m_state = kConnected; + onJobChanged(); + } + runWriteJob(); +} + +void CUnixTCPSocket::connect( + const CString& hostname, UInt16 port) +{ + assert(m_fd != -1); + assert(m_state == kNone); + + // hostname to address + struct hostent* hent = ::gethostbyname(hostname.c_str()); + if (hent == NULL) + throw XSocketName(::hstrerror(h_errno)); + + // construct address + struct sockaddr_in addr; + assert(hent->h_addrtype == AF_INET); + assert(hent->h_length == sizeof(addr.sin_addr)); + addr.sin_family = hent->h_addrtype; + addr.sin_port = htons(port); + ::memcpy(&addr.sin_addr, hent->h_addr_list[0], hent->h_length); + + // start connecting + if (::connect(m_fd, reinterpret_cast(&addr), + sizeof(addr)) == -1) { + if (errno != EINPROGRESS) + throw XSocketConnect(::strerror(errno)); + m_state = kConnecting; + } + else { + m_state = kConnected; + runWriteJob(); + } + onJobChanged(); +} + +void CUnixTCPSocket::listen( + const CString& hostname, UInt16 port) +{ + assert(m_fd != -1); + assert(m_state == kNone); + assert(port != 0); + + // construct address + struct sockaddr_in addr; + if (!hostname.empty()) { + // hostname to address + struct hostent* hent = ::gethostbyname(hostname.c_str()); + if (hent == NULL) + throw XSocketName(::hstrerror(h_errno)); + + // fill in address + assert(hent->h_addrtype == AF_INET); + assert(hent->h_length == sizeof(addr.sin_addr)); + ::memcpy(&addr.sin_addr, hent->h_addr_list[0], hent->h_length); + } + else { + // all addresses + addr.sin_addr.s_addr = htonl(INADDR_ANY); + } + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + + // bind to address + if (::bind(m_fd, reinterpret_cast(&addr), + sizeof(addr)) == -1) + throw XSocketListen(::strerror(errno)); + + // start listening + if (::listen(m_fd, 3) == -1) + throw XSocketListen(::strerror(errno)); + m_state = kListening; + onJobChanged(); +} + +ISocket* CUnixTCPSocket::accept() +{ + assert(m_fd != -1); + assert(m_state == kListening); + + for (;;) { + // wait for connection + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(m_fd, &fdset); + ::select(m_fd + 1, &fdset, NULL, NULL, NULL); + + // accept connection + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + int fd = ::accept(m_fd, &addr, &addrlen); + if (fd == -1) + if (errno == EAGAIN) + continue; + else + throw XSocketAccept(::strerror(errno)); + + // return new socket object + return new CUnixTCPSocket(fd); + } +} + +SInt32 CUnixTCPSocket::read(void* buffer, SInt32 numBytes) +{ + assert(m_fd != -1); + assert(m_state == kConnected); + + const ssize_t n = ::read(m_fd, buffer, numBytes); + if (n == -1) { + // check for no data to read + if (errno == EAGAIN || errno == EINTR) + return 0; + + // error + return -1; + } + + // check for socket closed + if (n == 0) + return -1; + + // return num bytes read + return n; +} + +void CUnixTCPSocket::write( + const void* buffer, SInt32 numBytes) +{ + const char* ptr = static_cast(buffer); + + while (numBytes > 0) { + // write more data + const ssize_t n = ::write(m_fd, ptr, numBytes); + + // check for errors + if (n == -1) { + // wait if can't write data then try again + if (errno == EAGAIN || errno == EINTR) { + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(m_fd, &fdset); + ::select(m_fd + 1, NULL, &fdset, NULL, NULL); + continue; + } + + // error + throw XSocketWrite(::strerror(errno)); + } + + // account for written data + ptr += n; + numBytes -= n; + } +} diff --git a/CUnixTCPSocket.h b/CUnixTCPSocket.h new file mode 100644 index 00000000..ac8d0a34 --- /dev/null +++ b/CUnixTCPSocket.h @@ -0,0 +1,47 @@ +#ifndef CUNIXTCPSOCKET_H +#define CUNIXTCPSOCKET_H + +#include "CSocket.h" +#include "CSocketFactory.h" + +class CUnixTCPSocket : public CSocket { + public: + CUnixTCPSocket(); + virtual ~CUnixTCPSocket(); + + // ISocket overrides + virtual void connect(const CString& hostname, UInt16 port); + virtual void listen(const CString& hostname, UInt16 port); + virtual ISocket* accept(); + virtual SInt32 read(void* buffer, SInt32 numBytes); + virtual void write(const void* buffer, SInt32 numBytes); + + protected: + // CSocket overrides + virtual void onJobChanged(); + + private: + CUnixTCPSocket(int); + + // callbacks for read/write events + void readCB(); + void writeCB(); + + private: + enum EState { kNone, kConnecting, kConnected, kListening }; + int m_fd; + EState m_state; + bool m_addedJobs; +}; + +class CUnixTCPSocketFactory : public CSocketFactory { + public: + CUnixTCPSocketFactory() { } + virtual ~CUnixTCPSocketFactory() { } + + // CSocketFactory overrides + virtual ISocket* create() const + { return new CUnixTCPSocket; } +}; + +#endif diff --git a/CUnixXScreen.cpp b/CUnixXScreen.cpp new file mode 100644 index 00000000..752a774d --- /dev/null +++ b/CUnixXScreen.cpp @@ -0,0 +1,34 @@ +#include "CUnixXScreen.h" +#include "CUnixEventQueue.h" +#include "TMethodJob.h" +#include + +// +// CUnixXScreen +// + +CUnixXScreen::CUnixXScreen(const CString& name) : + CXScreen(name) +{ + // do nothing +} + +CUnixXScreen::~CUnixXScreen() +{ + // do nothing +} + +void CUnixXScreen::onOpen(bool) +{ + // register our X event handler + CEQ->addFileDesc(ConnectionNumber(getDisplay()), + new TMethodJob(this, + &CUnixXScreen::onEvents), NULL); + +} + +void CUnixXScreen::onClose() +{ + // unregister the X event handler + CEQ->removeFileDesc(ConnectionNumber(getDisplay())); +} diff --git a/CUnixXScreen.h b/CUnixXScreen.h new file mode 100644 index 00000000..459eed8c --- /dev/null +++ b/CUnixXScreen.h @@ -0,0 +1,16 @@ +#ifndef CUNIXXSCREEN_H +#define CUNIXXSCREEN_H + +#include "CXScreen.h" + +class CUnixXScreen : public CXScreen { + public: + CUnixXScreen(const CString& name); + virtual ~CUnixXScreen(); + + protected: + virtual void onOpen(bool isPrimary); + virtual void onClose(); +}; + +#endif diff --git a/CXScreen.cpp b/CXScreen.cpp new file mode 100644 index 00000000..bd683eb4 --- /dev/null +++ b/CXScreen.cpp @@ -0,0 +1,571 @@ +#include "CXScreen.h" +#include "CEvent.h" +#include "CEventQueue.h" +#include +#include +#include +#include + +// +// CXScreen +// +class XClientOpen { }; // FIXME + +CXScreen::CXScreen(const CString& name) : + m_name(name), + m_display(NULL), + m_primary(false), + m_w(0), m_h(0), + m_window(None), + m_active(false) +{ + // do nothing +} + +CXScreen::~CXScreen() +{ + assert(m_display == NULL); +} + +void CXScreen::open(bool isPrimary) +{ + assert(m_display == NULL); + + m_primary = isPrimary; + + bool opened = false; + try { + // open the display + m_display = ::XOpenDisplay(NULL); // FIXME -- allow non-default + if (m_display == NULL) + throw XClientOpen(); + + // hook up event handling + onOpen(m_primary); + opened = true; + + // get default screen + m_screen = DefaultScreen(m_display); + Screen* screen = ScreenOfDisplay(m_display, m_screen); + + // get screen size + m_w = WidthOfScreen(screen); + m_h = HeightOfScreen(screen); + + // type specific operations + if (m_primary) + openPrimary(); + else + openSecondary(); + } + catch (...) { + if (opened) + onClose(); + + if (m_display != NULL) { + ::XCloseDisplay(m_display); + m_display = NULL; + } + + throw; + } +} + +void CXScreen::close() +{ + assert(m_display != NULL); + + // type specific operations + if (m_primary) + closePrimary(); + else + closeSecondary(); + + // unhook event handling + onClose(); + + // close the display + ::XCloseDisplay(m_display); + m_display = NULL; +} + +void CXScreen::enterScreen(SInt32 x, SInt32 y) +{ + assert(m_display != NULL); + + if (m_primary) + enterScreenPrimary(x, y); + else + enterScreenSecondary(x, y); +} + +void CXScreen::leaveScreen() +{ + assert(m_display != NULL); + + if (m_primary) + leaveScreenPrimary(); + else + leaveScreenSecondary(); +} + +void CXScreen::warpCursor(SInt32 x, SInt32 y) +{ + assert(m_display != NULL); + + // warp the mouse + Window root = RootWindow(m_display, m_screen); + ::XWarpPointer(m_display, None, root, 0, 0, 0, 0, x, y); + ::XSync(m_display, False); + + // discard mouse events since we just added one we don't want + XEvent xevent; + while (::XCheckWindowEvent(m_display, m_window, + PointerMotionMask, &xevent)) + ; // do nothing +} + +void CXScreen::setClipboard( + const IClipboard* clipboard) +{ + assert(m_display != NULL); + + if (m_primary) + setClipboardPrimary(clipboard); + else + setClipboardSecondary(clipboard); +} + +void CXScreen::onScreenSaver(bool show) +{ + assert(m_display != NULL); + + if (m_primary) + onScreenSaverPrimary(show); + else + onScreenSaverSecondary(show); +} + +void CXScreen::onKeyDown(KeyID) +{ + assert(m_display != NULL); + assert(m_primary == false); + + // FIXME +} + +void CXScreen::onKeyRepeat(KeyID, SInt32) +{ + assert(m_display != NULL); + assert(m_primary == false); + + // FIXME +} + +void CXScreen::onKeyUp(KeyID) +{ + assert(m_display != NULL); + assert(m_primary == false); + + // FIXME +} + +void CXScreen::onKeyToggle(KeyToggleMask) +{ + assert(m_display != NULL); + assert(m_primary == false); + + // FIXME +} + +void CXScreen::onMouseDown(ButtonID) +{ + assert(m_display != NULL); + assert(m_primary == false); + + // FIXME +} + +void CXScreen::onMouseUp(ButtonID) +{ + assert(m_display != NULL); + assert(m_primary == false); + + // FIXME +} + +void CXScreen::onMouseMove(SInt32 x, SInt32 y) +{ + assert(m_display != NULL); + assert(m_primary == false); + + XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); +} + +void CXScreen::onMouseWheel(SInt32) +{ + assert(m_display != NULL); + assert(m_primary == false); + + // FIXME +} + +void CXScreen::onClipboardChanged() +{ + assert(m_display != NULL); + assert(m_primary == false); + + // FIXME +} + +CString CXScreen::getName() const +{ + return m_name; +} + +void CXScreen::getSize( + SInt32* width, SInt32* height) const +{ + assert(m_display != NULL); + assert(width != NULL && height != NULL); + + *width = m_w; + *height = m_h; +} + +void CXScreen::getClipboard( + IClipboard* /*clipboard*/) const +{ + assert(m_display != NULL); + + // FIXME +} + +void CXScreen::openPrimary() +{ + // get the root window + Window root = RootWindow(m_display, m_screen); + + // create the grab window. this window is used to capture user + // input when the user is focussed on another client. don't let + // the window manager mess with it. + XSetWindowAttributes attr; + attr.event_mask = PointerMotionMask |// PointerMotionHintMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = None; + m_window = ::XCreateWindow(m_display, root, 0, 0, m_w, m_h, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + + // start watching for events on other windows + selectEvents(root); +} + +void CXScreen::closePrimary() +{ + assert(m_window != None); + + // destroy window + ::XDestroyWindow(m_display, m_window); + m_window = None; +} + +void CXScreen::enterScreenPrimary(SInt32 x, SInt32 y) +{ + assert(m_window != None); + assert(m_active == true); + + // warp to requested location + ::XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, x, y); + + // unmap the grab window. this also ungrabs the mouse and keyboard. + ::XUnmapWindow(m_display, m_window); + + // remove all input events for grab window + XEvent event; + while (::XCheckWindowEvent(m_display, m_window, + PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask, + &event)) + ; // do nothing + + // not active anymore + m_active = false; +} + +void CXScreen::leaveScreenPrimary() +{ + assert(m_window != None); + assert(m_active == false); + + // raise and show the input window + ::XMapRaised(m_display, m_window); + + // grab the mouse and keyboard. keep trying until we get them. + // if we can't grab one after grabbing the other then ungrab + // and wait before retrying. + int result; + do { + // mouse first + do { + result = ::XGrabPointer(m_display, m_window, True, 0, + GrabModeAsync, GrabModeAsync, + m_window, None, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) + ::sleep(1); + } while (result != GrabSuccess); + + // now the keyboard + result = ::XGrabKeyboard(m_display, m_window, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + ::XUngrabPointer(m_display, CurrentTime); + ::sleep(1); + } + } while (result != GrabSuccess); + + // move the mouse to the center of grab window + warpCursor(m_w >> 1, m_h >> 1); + + // local client now active + m_active = true; +} + +void CXScreen::setClipboardPrimary( + const IClipboard* /*clipboard*/) +{ + // FIXME +} + +void CXScreen::onScreenSaverPrimary(bool /*show*/) +{ + // FIXME +} + +void CXScreen::openSecondary() +{ + // verify the availability of the XTest extension + int majorOpcode, firstEvent, firstError; + if (!::XQueryExtension(m_display, XTestExtensionName, + &majorOpcode, &firstEvent, &firstError)) + throw XClientOpen(); + + // become impervious to server grabs + XTestGrabControl(m_display, True); +} + +void CXScreen::closeSecondary() +{ + // no longer impervious to server grabs + XTestGrabControl(m_display, False); +} + +void CXScreen::enterScreenSecondary( + SInt32 x, SInt32 y) +{ + // FIXME +} + +void CXScreen::leaveScreenSecondary() +{ + // FIXME +} + +void CXScreen::setClipboardSecondary( + const IClipboard* /*clipboard*/) +{ + // FIXME +} + +void CXScreen::onScreenSaverSecondary(bool /*show*/) +{ + // FIXME +} + +Display* CXScreen::getDisplay() const +{ + return m_display; +} + +void CXScreen::onEvents() +{ + if (m_primary) + onPrimaryEvents(); + else + onSecondaryEvents(); +} + +void CXScreen::selectEvents(Window w) const +{ + // we want to track the mouse everywhere on the display. to achieve + // that we select PointerMotionMask on every window. we also select + // SubstructureNotifyMask in order to get CreateNotify events so we + // select events on new windows too. + + // we don't want to adjust our grab window + if (w == m_window) + return; + + // select events of interest + ::XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask); + + // recurse on child windows + Window rw, pw, *cw; + unsigned int nc; + if (::XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) + selectEvents(cw[i]); + ::XFree(cw); + } +} + +KeyID CXScreen::mapKey(unsigned int keycode) const +{ + return keycode; +} + +ButtonID CXScreen::mapButton(unsigned int button) const +{ + switch (button) { + case 1: + return kButtonLeft; + + case 2: + return kButtonMiddle; + + case 3: + return kButtonRight; + } + + return kButtonNone; +} + +void CXScreen::onPrimaryEvents() +{ + while (XPending(m_display) > 0) { + XEvent xevent; + XNextEvent(m_display, &xevent); + + switch (xevent.type) { + case KeyPress: { + const KeyID key = mapKey(xevent.xkey.keycode); + if (key != kKeyNone) { + CEvent event; + event.m_key.m_type = CEventBase::kKeyDown; + event.m_key.m_key = key; + event.m_key.m_count = 0; + CEQ->push(&event); + } + break; + } + + // FIXME -- simulate key repeat. X sends press/release for + // repeat. must detect auto repeat and use kKeyRepeat. + case KeyRelease: { + const KeyID key = mapKey(xevent.xkey.keycode); + if (key != kKeyNone) { + CEvent event; + event.m_key.m_type = CEventBase::kKeyUp; + event.m_key.m_key = key; + event.m_key.m_count = 0; + CEQ->push(&event); + } + break; + } + + case ButtonPress: { + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + CEvent event; + event.m_mouse.m_type = CEventBase::kMouseDown; + event.m_mouse.m_button = button; + event.m_mouse.m_x = 0; + event.m_mouse.m_y = 0; + CEQ->push(&event); + } + break; + } + + case ButtonRelease: { + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + CEvent event; + event.m_mouse.m_type = CEventBase::kMouseUp; + event.m_mouse.m_button = button; + event.m_mouse.m_x = 0; + event.m_mouse.m_y = 0; + CEQ->push(&event); + } + break; + } + + case MotionNotify: { + CEvent event; + event.m_mouse.m_type = CEventBase::kMouseMove; + event.m_mouse.m_button = kButtonNone; + if (!m_active) { + event.m_mouse.m_x = xevent.xmotion.x_root; + event.m_mouse.m_y = xevent.xmotion.y_root; + } + else { + // FIXME -- slurp up all remaining motion events? + // probably not since key strokes may go to wrong place. + + // get mouse deltas + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int mask; + if (!::XQueryPointer(m_display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &mask)) + break; + event.m_mouse.m_x = xRoot - (m_w >> 1); + event.m_mouse.m_y = yRoot - (m_h >> 1); + + // warp mouse back to center + warpCursor(m_w >> 1, m_h >> 1); + } + CEQ->push(&event); + break; + } + + case CreateNotify: + // select events on new window + if (m_primary) + selectEvents(xevent.xcreatewindow.window); + break; + +/* + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; +*/ + } + } +} + +void CXScreen::onSecondaryEvents() +{ + while (XPending(m_display) > 0) { + XEvent xevent; + XNextEvent(m_display, &xevent); + // FIXME + } +} diff --git a/CXScreen.h b/CXScreen.h new file mode 100644 index 00000000..c2fb9b83 --- /dev/null +++ b/CXScreen.h @@ -0,0 +1,81 @@ +#ifndef CXSCREEN_H +#define CXSCREEN_H + +#include "IScreen.h" +#include + +class CXScreen : public IScreen { + public: + CXScreen(const CString& name); + virtual ~CXScreen(); + + // IScreen overrides + virtual void open(bool isPrimary); + virtual void close(); + virtual void enterScreen(SInt32 x, SInt32 y); + virtual void leaveScreen(); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void setClipboard(const IClipboard*); + virtual void onScreenSaver(bool); + virtual void onKeyDown(KeyID); + virtual void onKeyRepeat(KeyID, SInt32); + virtual void onKeyUp(KeyID); + virtual void onKeyToggle(KeyToggleMask); + virtual void onMouseDown(ButtonID); + virtual void onMouseUp(ButtonID); + virtual void onMouseMove(SInt32, SInt32); + virtual void onMouseWheel(SInt32); + virtual void onClipboardChanged(); + virtual CString getName() const; + virtual void getSize(SInt32* width, SInt32* height) const; + virtual void getClipboard(IClipboard*) const; + + protected: + // primary screen implementations + virtual void openPrimary(); + virtual void closePrimary(); + virtual void enterScreenPrimary(SInt32 x, SInt32 y); + virtual void leaveScreenPrimary(); + virtual void setClipboardPrimary(const IClipboard*); + virtual void onScreenSaverPrimary(bool); + + // secondary screen implementations + virtual void openSecondary(); + virtual void closeSecondary(); + virtual void enterScreenSecondary(SInt32 x, SInt32 y); + virtual void leaveScreenSecondary(); + virtual void setClipboardSecondary(const IClipboard*); + virtual void onScreenSaverSecondary(bool); + + // get the display + Display* getDisplay() const; + + // process X events from the display + void onEvents(); + + // called by open() and close(). override to hook up and unhook the + // display connection to the event queue. call onEvents() when events + // are available. + virtual void onOpen(bool isPrimary) = 0; + virtual void onClose() = 0; + + private: + void selectEvents(Window) const; + KeyID mapKey(unsigned int keycode) const; + ButtonID mapButton(unsigned int button) const; + void onPrimaryEvents(); + void onSecondaryEvents(); + + private: + CString m_name; + Display* m_display; + int m_screen; + bool m_primary; + SInt32 m_w, m_h; + + // stuff for primary screens + Window m_window; + bool m_active; +}; + +#endif diff --git a/IClient.h b/IClient.h new file mode 100644 index 00000000..02dd7129 --- /dev/null +++ b/IClient.h @@ -0,0 +1,17 @@ +#ifndef ICLIENT_H +#define ICLIENT_H + +class CString; + +class IClient { + public: + IClient() { } + virtual ~IClient() { } + + // manipulators + + // connect to server and begin processing events + virtual void run(const CString& hostname) = 0; +}; + +#endif diff --git a/IClipboard.h b/IClipboard.h new file mode 100644 index 00000000..e69de29b diff --git a/IEventQueue.h b/IEventQueue.h new file mode 100644 index 00000000..36609b0c --- /dev/null +++ b/IEventQueue.h @@ -0,0 +1,45 @@ +#ifndef IEVENTQUEUE_H +#define IEVENTQUEUE_H + +#define CEQ (IEventQueue::getInstance()) + +class CEvent; + +class IEventQueue { + public: + IEventQueue(); + virtual ~IEventQueue(); + + // note -- all of the methods in an IEventQueue subclass for a + // platform must be thread safe if it will be used by multiple + // threads simultaneously on that platform. + + // manipulators + + // wait up to timeout seconds for the queue to become not empty. + // as a side effect this can do the insertion of events. if + // timeout < 0.0 then wait indefinitely. it's possible for + // wait() to return prematurely so always call isEmpty() to + // see if there are any events. + virtual void wait(double timeout) = 0; + + // reads and removes the next event on the queue. waits indefinitely + // for an event if the queue is empty. + virtual void pop(CEvent*) = 0; + + // push an event onto the queue + virtual void push(const CEvent*) = 0; + + // returns true if the queue is empty and wait() would block + virtual bool isEmpty() = 0; + + // accessors + + // get the singleton event queue + static IEventQueue* getInstance(); + + private: + static IEventQueue* s_instance; +}; + +#endif diff --git a/IJob.h b/IJob.h new file mode 100644 index 00000000..381e29ad --- /dev/null +++ b/IJob.h @@ -0,0 +1,14 @@ +#ifndef IJOB_H +#define IJOB_H + +class IJob { + public: + IJob() { } + virtual ~IJob() { } + + // manipulators + + virtual void run() = 0; +}; + +#endif diff --git a/IScreen.h b/IScreen.h new file mode 100644 index 00000000..a8427584 --- /dev/null +++ b/IScreen.h @@ -0,0 +1,132 @@ +#ifndef ISCREEN_H +#define ISCREEN_H + +/* + * IScreen -- interface for display screens + * + * a screen encapsulates input and output devices, typically a mouse + * and keyboard for input and a graphical display for output. one + * screen is designated as the primary screen. only input from the + * primary screen's input devices is used. other screens are secondary + * screens and they simulate input from their input devices but ignore + * any actual input. a screen can be either a primary or a secondary + * but not both at the same time. most methods behave differently + * depending on the screen type. + */ + +#include "BasicTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CString.h" + +class IClipboard; + +class IScreen { + public: + IScreen() { } + virtual ~IScreen() { } + + // manipulators + + // open/close screen. these are where the client should do + // initialization and cleanup of the system's screen. if isPrimary + // is true then this screen will be used (exclusively) as the + // primary screen, otherwise it will be used (exclusively) as a + // secondary screen. + // + // primary: + // open(): open the screen and begin reporting input events to + // the event queue. input events should be reported no matter + // where on the screen they occur but the screen should not + // interfere with the normal dispatching of events. the screen + // should detect when the screen saver is activated. if it can't + // do that it should disable the screen saver and start it itself + // after the appropriate duration of no input. + // + // secondary: + // open(): open the screen, hide the cursor and disable the + // screen saver. then wait for an enterScreen() or close(), + // reporting the following events: FIXME. + virtual void open(bool isPrimary) = 0; + virtual void close() = 0; + + // enter/leave screen + // + // primary: + // enterScreen(): the user has navigated back to the primary + // screen. warp the cursor to the given coordinates, unhide the + // cursor and ungrab the input devices. the screen must also + // detect and report (enqueue) input events. for the primary + // screen, enterScreen() is only called after a leaveScreen(). + // leaveScreen(): the user has navigated off the primary screen. + // hide the cursor and grab exclusive access to the input devices. + // input events must be reported. + // + // secondary: + // enterScreen(): the user has navigated to this secondary + // screen. warp the cursor to the given coordinates and show it. + // prepare to simulate input events. + // leaveScreen(): the user has navigated off this secondary + // screen. clean up input event simulation. hide the cursor. + virtual void enterScreen(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + virtual void leaveScreen() = 0; + + // warp the cursor to the given position + virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + + // + // clipboard operations + // + + // set the screen's clipboard contents. this is usually called + // soon after an enterScreen(). + virtual void setClipboard(const IClipboard*) = 0; + + // + // screen saver operations + // + + // show or hide the screen saver + virtual void onScreenSaver(bool show) = 0; + + // + // input simulation + // + // these methods must simulate the appropriate input event. + // these methods may only called on secondary screens. + // + + // keyboard input + // onKeyToggle() sets the keyboard toggle key states (e.g. num lock). + virtual void onKeyDown(KeyID) = 0; + virtual void onKeyRepeat(KeyID, SInt32 count) = 0; + virtual void onKeyUp(KeyID) = 0; + virtual void onKeyToggle(KeyToggleMask) = 0; + + // mouse input + virtual void onMouseDown(ButtonID) = 0; + virtual void onMouseUp(ButtonID) = 0; + virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + virtual void onMouseWheel(SInt32 delta) = 0; + + // clipboard input + // FIXME -- do we need this? + virtual void onClipboardChanged() = 0; + + // accessors + + // get the screen's name. all screens must have a name unique on + // the server they connect to. the hostname is usually an + // appropriate name. + virtual CString getName() const = 0; + + // get the size of the screen + virtual void getSize(SInt32* width, SInt32* height) const = 0; + + // clipboard operations + + // get the screen's clipboard contents + virtual void getClipboard(IClipboard*) const = 0; +}; + +#endif diff --git a/IServer.h b/IServer.h new file mode 100644 index 00000000..2a041d64 --- /dev/null +++ b/IServer.h @@ -0,0 +1,29 @@ +#ifndef ISERVER_H +#define ISERVER_H + +class IScreen; + +class IServer { + public: + IServer() { } + virtual ~IServer() { } + + // manipulators + + // run the server until terminated + virtual void run() = 0; + + // clipboard operations + virtual void onClipboardChanged(IScreen*) = 0; + + // enter the given screen, leaving the previous screen. the cursor + // should be warped to the center of the screen. + virtual void setActiveScreen(IScreen*) = 0; + + // accessors + + // get the screen that was last entered + virtual IScreen* getActiveScreen() const = 0; +}; + +#endif diff --git a/ISocket.h b/ISocket.h new file mode 100644 index 00000000..1a46f5d9 --- /dev/null +++ b/ISocket.h @@ -0,0 +1,45 @@ +#ifndef ISOCKET_H +#define ISOCKET_H + +#include "BasicTypes.h" + +class IJob; +class CString; + +class ISocket { + public: + // d'tor closes the socket + ISocket() { } + virtual ~ISocket() { } + + // manipulators + + // set the job to invoke when the socket is readable or writable. + // a socket that has connected after a call to connect() becomes + // writable. a socket that is ready to accept a connection after + // a call to listen() becomes readable. the socket returned by + // accept() does not have any jobs assigned to it. + virtual void setReadJob(IJob* adoptedJob) = 0; + virtual void setWriteJob(IJob* adoptedJob) = 0; + + // open/close. connect() begins connecting to the given host but + // doesn't wait for the connection to complete. listen() begins + // listening on the given interface and port; if hostname is + // empty then listen on all interfaces. accept() waits for a + // connection on the listening interface and returns a new + // socket for the connection. + virtual void connect(const CString& hostname, UInt16 port) = 0; + virtual void listen(const CString& hostname, UInt16 port) = 0; + virtual ISocket* accept() = 0; + + // read data from socket. returns without waiting if not enough + // data is available. returns the number of bytes actually read, + // which is zero if there were no bytes to read and -1 if the + // remote end of the socket has disconnected. + virtual SInt32 read(void* buffer, SInt32 numBytes) = 0; + + // write data to socket. waits until all data has been written. + virtual void write(const void* buffer, SInt32 numBytes) = 0; +}; + +#endif diff --git a/KeyTypes.h b/KeyTypes.h new file mode 100644 index 00000000..c220a9fc --- /dev/null +++ b/KeyTypes.h @@ -0,0 +1,18 @@ +#ifndef KEYTYPES_H +#define KEYTYPES_H + +// type to hold a key identifier +typedef UInt32 KeyID; + +// type to hold bitmask of keys that have toggle states +typedef UInt16 KeyToggleMask; + +// toggle key bitmasks +static const UInt32 KeyToggleShiftLock = 0x0001; +static const UInt32 KeyToggleNumLock = 0x0002; +static const UInt32 KeyToggleScrollLock = 0x0004; + +// key codes +static const KeyID kKeyNone = 0; + +#endif diff --git a/Make-linux b/Make-linux new file mode 100644 index 00000000..e78e2da4 --- /dev/null +++ b/Make-linux @@ -0,0 +1,103 @@ +# +# empty define, used to terminate lists +# +NULL = + +# +# build tools +# +CXX = /usr/bin/g++ +LD = /usr/bin/ld +MKDIR = /bin/mkdir +RM = /bin/rm -f +RMR = /bin/rm -rf + +# +# compiler options +# +CXXFLAGS = $(LCXXFLAGS) $(GCXXFLAGS) $(CXXOPTIMIZER) $(MKDEPOPT) +LCXXFLAGS = $(LCXXDEFS) $(LCXXINCS) $(LCXXOPTS) +GCXXFLAGS = $(GCXXDEFS) $(GCXXINCS) $(GCXXOPTS) + +GCXXDEFS = -D_POSIX_SOURCE +GCXXINCS = -I$(DEPTH)/include -I/usr/include -I/usr/X11R6/include +GCXXOPTS = -Wall -W -fexceptions -fno-rtti +CXXOPTIMIZER = -g +#CXXOPTIMIZER = -O2 -DNDEBUG + +# +# linker options +# +LDFLAGS = $(LDOPTS) $(LDLIBS) +LDOPTS = $(LLDOPTS) $(GLDOPTS) +LDLIBS = $(LLDLIBS) $(GLDLIBS) + +GLDLIBS = -L/usr/X11R6/lib -lX11 -lXext -lXtst +GLDOPTS = + +# +# dependency generation stuff +# +MKDEP = $(DEPTH)/tools/depconv +MKDEPOPT = -MD +MKDEPPRE = +MKDEPPOST = $(MKDEP) -f $(MKDEPFILE) $*.d; $(RM) $*.d +MKDEPFILE = Makedepend + +# +# Convenience file list macros: +# +SOURCES = $(CXXFILES) +OBJECTS = $(CXXFILES:.cpp=.o) + +# +# stuff to clean +# +DIRT = $(_FORCE) $(LDIRT) $(GDIRT) +GDIRT = *.[eoud] a.out core ar.tmp.* $(MKDEPFILE) + +# +# always unsatisfied target +# +_FORCE = $(COMMONPREF)_force + +# +# +# common rules +# +# + +# +# default target. makefiles must define a target named `targets'. +# +default: targets + +# +# always unsatisfied target +# +$(_FORCE): + +# +# cleaners +# +COMMONTARGETS = clean clobber +$(COMMONPREF)clean: $(_FORCE) + $(RM) $(DIRT) + +$(COMMONPREF)clobber: clean $(_FORCE) + $(RM) $(TARGETS) + +# +# implicit target rules +# +.SUFFIXES: .cpp .o + +.cpp.o: + $(MKDEPPRE) + $(CXX) $(CXXFLAGS) -c $< + $(MKDEPPOST) + +# +# load dependencies +# +sinclude $(MKDEPFILE) diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..f8384949 --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +DEPTH=. +include Make-linux + +# +# target files +# +TARGETS = main + +# +# source files +# +CXXFILES = \ + XBase.cpp \ + CTrace.cpp \ + CEventQueue.cpp \ + CSocket.cpp \ + CMessageSocket.cpp \ + CSocketFactory.cpp \ + CServer.cpp \ + CClient.cpp \ + CScreenProxy.cpp \ + CXScreen.cpp \ + CUnixXScreen.cpp \ + CUnixTCPSocket.cpp \ + CUnixEventQueue.cpp \ + main.cpp \ + $(NULL) + +# +# libraries we depend on +# +DEPLIBS = \ + $(NULL) + +targets: $(TARGETS) + +main: $(OBJECTS) $(DEPLIBS) + $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) diff --git a/MouseTypes.h b/MouseTypes.h new file mode 100644 index 00000000..ac908486 --- /dev/null +++ b/MouseTypes.h @@ -0,0 +1,13 @@ +#ifndef MOUSETYPES_H +#define MOUSETYPES_H + +// type to hold mouse button identifier +typedef UInt8 ButtonID; + +// mouse button identifiers +static const ButtonID kButtonNone = 0; +static const ButtonID kButtonLeft = 1; +static const ButtonID kButtonRight = 2; +static const ButtonID kButtonMiddle = 3; + +#endif diff --git a/TMethodJob.h b/TMethodJob.h new file mode 100644 index 00000000..341e3491 --- /dev/null +++ b/TMethodJob.h @@ -0,0 +1,23 @@ +#ifndef TMETHODJOB_H +#define TMETHODJOB_H + +#include "IJob.h" + +template +class TMethodJob : public IJob { + public: + typedef void (T::*Method)(); + + TMethodJob(T* object, Method method) : + m_object(object), m_method(method) { } + virtual ~TMethodJob() { } + + // IJob overrides + virtual void run() { (m_object->*m_method)(); } + + private: + T* m_object; + Method m_method; +}; + +#endif diff --git a/XBase.cpp b/XBase.cpp new file mode 100644 index 00000000..df1bd475 --- /dev/null +++ b/XBase.cpp @@ -0,0 +1,40 @@ +#include "XBase.h" + +// win32 wants a const char* argument to std::exception c'tor +#if CONFIG_PLATFORM_WIN32 +#define STDEXCEPTARG "" +#endif + +// default to no argument +#ifndef STDEXCEPTARG +#define STDEXCEPTARG +#endif + +// +// XBase +// + +XBase::XBase() : exception(STDEXCEPTARG) +{ + // do nothing +} + +XBase::~XBase() +{ + // do nothing +} + +const char* XBase::what() const +{ + return getType(); +} + +const char* XBase::getType() const +{ + return "XBase.h"; +} + +CString XBase::format(const CString& fmt) const +{ + return fmt; +} diff --git a/XBase.h b/XBase.h new file mode 100644 index 00000000..f2ed5162 --- /dev/null +++ b/XBase.h @@ -0,0 +1,31 @@ +#ifndef XBASE_H +#define XBASE_H + +#include "CString.h" +#include + +class XBase : public std::exception { + public: + XBase(); + virtual ~XBase(); + + // accessors + + // return the name of the exception type + virtual const char* getType() const; + + // format and return formatString by replacing positional + // arguments (%1, %2, etc.). default returns formatString + // unchanged. subclasses should document what positional + // arguments they replace. + virtual CString format(const CString& formatString) const; + + // std::exception overrides + virtual const char* what() const; +}; + +#define XNAME(_n) \ + public: \ + virtual const char* getType() const { return #_n; } + +#endif diff --git a/XSocket.h b/XSocket.h new file mode 100644 index 00000000..e8f775f2 --- /dev/null +++ b/XSocket.h @@ -0,0 +1,33 @@ +#ifndef XSOCKET_H +#define XSOCKET_H + +#include "XBase.h" + +class XSocket : public XBase { + public: + // accessors + + const char* getMessage() const { return m_msg; } + + protected: + XSocket(const char* msg) : m_msg(msg) { } + + private: + const char* m_msg; +}; + +#define XSOCKETDEF(_n) \ +class _n : public XSocket { \ + public: \ + _n(const char* msg) : XSocket(msg) { } \ + XNAME(_n) \ +}; + +XSOCKETDEF(XSocketCreate) +XSOCKETDEF(XSocketName) +XSOCKETDEF(XSocketConnect) +XSOCKETDEF(XSocketListen) +XSOCKETDEF(XSocketAccept) +XSOCKETDEF(XSocketWrite) + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 00000000..b879e1a1 --- /dev/null +++ b/main.cpp @@ -0,0 +1,114 @@ +#include +#include +#include + +#include "CServer.h" +#include "CClient.h" +#include "CUnixTCPSocket.h" +#include "CUnixEventQueue.h" +#include "CUnixXScreen.h" + +/* +static void selectMotion(Display* dpy, Window w) +{ + // select events + XSelectInput(dpy, w, PointerMotionMask | SubstructureNotifyMask); + + // recurse on child windows + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(dpy, w, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) + selectMotion(dpy, cw[i]); + XFree(cw); + } +} + +static void trackMouse(Display* dpy) +{ + // note -- this doesn't track the mouse when it's grabbed. that's + // okay for synergy because we don't want to cross screens then. + selectMotion(dpy, DefaultRootWindow(dpy)); + while (true) { + XEvent event; + XNextEvent(dpy, &event); + switch (event.type) { + case MotionNotify: + fprintf(stderr, "mouse: %d,%d\n", event.xmotion.x_root, event.xmotion.y_root); + break; + + case CreateNotify: + selectMotion(dpy, event.xcreatewindow.window); + break; + } + } +} + +static void checkLEDs(Display* dpy) +{ + XKeyboardState values; + XGetKeyboardControl(dpy, &values); + + fprintf(stderr, "led (%08x): ", (unsigned int)values.led_mask); + for (int i = 0; i < 32; ++i) + fprintf(stderr, "%c", (values.led_mask & (1 << i)) ? 'O' : '.'); + fprintf(stderr, "\n"); + + XKeyboardControl ctrl; + for (int i = 0; i < 32; i += 2) { + ctrl.led = i + 1; + ctrl.led_mode = LedModeOff; + XChangeKeyboardControl(dpy, KBLed | KBLedMode, &ctrl); + XSync(dpy, False); + } +} +*/ + +int main(int argc, char** argv) +{ +/* + printf("Hello world\n"); + + Display* dpy = XOpenDisplay(NULL); + + checkLEDs(dpy); + trackMouse(dpy); + + XCloseDisplay(dpy); +*/ + + // install socket factory + CSocketFactory::setInstance(new CUnixTCPSocketFactory); + + // create event queue + CUnixEventQueue eventQueue; + + if (argc <= 1) { + // create server + CServer server; + + // create clients + CUnixXScreen localScreen("audrey2"); + + // register clients + server.addLocalScreen(&localScreen); + server.addRemoteScreen("remote1"); + + // hook up edges + server.connectEdge("audrey2", CServer::kLeft, "remote1"); + server.connectEdge("audrey2", CServer::kTop, "audrey2"); + server.connectEdge("audrey2", CServer::kBottom, "audrey2"); + server.connectEdge("remote1", CServer::kLeft, "audrey2"); + + // do it + server.run(); + } + else { + // create client + CUnixXScreen screen("remote1"); + CClient client(&screen); + client.run(argv[1]); + } + + return 0; +} diff --git a/tools/depconv b/tools/depconv new file mode 100755 index 00000000..7bccd466 --- /dev/null +++ b/tools/depconv @@ -0,0 +1,46 @@ +#!/bin/sh +# +# add dependency info from files on command line to $depfile + +depfile="Makedepends" + +dependencies="" +targets="^$" + +# tmp directory +if test -z "$TMP"; then TMP=/tmp; fi + +while [ -n "$*" ]; do + case "$1" in + -f) + depfile=$2 + shift + shift + ;; + -*) + echo "usage: $0 [-f ] ..." + exit 1 + ;; + *) + break + esac +done + +# collect all dependencies +while [ -n "$*" ]; do + line=`cat $1 | sed -e 's/\\\\//g' | sed -e 's/ \/[^ ]*//g'` + target=`echo $line | sed -e 's/^\([^:]*\):.*/\1/'` + targets="$targets|^$target:" + dependencies="$dependencies$line\n" + shift +done + +# add new dependencies to $depfile +if [ -n "$targets" ]; then + if [ -r $depfile ]; then + (egrep -v $targets $depfile; echo -e -n $dependencies) > $TMP/dep$$ + if [ $? -eq 0 ]; then mv $TMP/dep$$ $depfile; fi + else + echo -e -n $dependencies > $depfile + fi +fi From 2155c00c4444cc7a1b48e18b5df313dc42e47d00 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 May 2001 12:07:32 +0000 Subject: [PATCH 002/807] fixed bug in read() that miscalculated the message length. --- CMessageSocket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMessageSocket.cpp b/CMessageSocket.cpp index c89c0db1..97b0d516 100644 --- a/CMessageSocket.cpp +++ b/CMessageSocket.cpp @@ -78,8 +78,8 @@ SInt32 CMessageSocket::read(void* buffer, SInt32 n) // get next message size if (m_size >= 2) { m_msgSize = static_cast( - (static_cast(m_buffer[1]) << 8) + - (static_cast(m_buffer[2]) )); + (static_cast(m_buffer[0]) << 8) + + (static_cast(m_buffer[1]) )); TRACE((" next message size: %d", m_msgSize)); } From d0594ea9f32fc3175e0b868dbdb014ba4514ab7c Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 May 2001 12:21:11 +0000 Subject: [PATCH 003/807] fixes to avoid update delays. --- CUnixTCPSocket.cpp | 8 ++++++++ CXScreen.cpp | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CUnixTCPSocket.cpp b/CUnixTCPSocket.cpp index ebec593b..99cd1ecd 100644 --- a/CUnixTCPSocket.cpp +++ b/CUnixTCPSocket.cpp @@ -7,6 +7,7 @@ #include #include #include +#include // FIXME -- for disabling nagle algorithm #include #include #include @@ -29,6 +30,13 @@ CUnixTCPSocket::CUnixTCPSocket() : m_fd(-1), ::close(m_fd); throw XSocketCreate(::strerror(errno)); } + + // turn off Nagle algorithm. we send lots of really short messages. + struct protoent* p = getprotobyname("tcp"); + if (p) { + int on = 1; + setsockopt(m_fd, p->p_proto, TCP_NODELAY, &on, sizeof(on)); + } } CUnixTCPSocket::CUnixTCPSocket(int fd) : m_fd(fd), diff --git a/CXScreen.cpp b/CXScreen.cpp index bd683eb4..1f738fc2 100644 --- a/CXScreen.cpp +++ b/CXScreen.cpp @@ -199,7 +199,8 @@ void CXScreen::onMouseMove(SInt32 x, SInt32 y) assert(m_display != NULL); assert(m_primary == false); - XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); + ::XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); + ::XSync(m_display, False); } void CXScreen::onMouseWheel(SInt32) From fe79ac593cfc7b6cbda38db74a8e5f5be3d6cbd7 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 May 2001 12:43:16 +0000 Subject: [PATCH 004/807] more fixes to reduce latency. nagle agorithm doesn't seem to stay off on a socket on linux because a connection clearly doesn't send data as often as possible. will have to implement a UDP socket to reduce overhead and avoid these delays. wanted to do that anyway. --- CUnixTCPSocket.cpp | 20 ++++++++++++++------ CUnixTCPSocket.h | 3 +++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CUnixTCPSocket.cpp b/CUnixTCPSocket.cpp index 99cd1ecd..ed5d2202 100644 --- a/CUnixTCPSocket.cpp +++ b/CUnixTCPSocket.cpp @@ -31,12 +31,8 @@ CUnixTCPSocket::CUnixTCPSocket() : m_fd(-1), throw XSocketCreate(::strerror(errno)); } - // turn off Nagle algorithm. we send lots of really short messages. - struct protoent* p = getprotobyname("tcp"); - if (p) { - int on = 1; - setsockopt(m_fd, p->p_proto, TCP_NODELAY, &on, sizeof(on)); - } + // always send immediately + setNoDelay(); } CUnixTCPSocket::CUnixTCPSocket(int fd) : m_fd(fd), @@ -44,6 +40,7 @@ CUnixTCPSocket::CUnixTCPSocket(int fd) : m_fd(fd), m_addedJobs(false) { assert(m_fd != -1); + setNoDelay(); } CUnixTCPSocket::~CUnixTCPSocket() @@ -268,3 +265,14 @@ void CUnixTCPSocket::write( numBytes -= n; } } + +void CUnixTCPSocket::setNoDelay() +{ + // turn off Nagle algorithm. we send lots of really short messages + // so we'll accept the (much) larger overhead to reduce latency. + struct protoent* p = getprotobyname("tcp"); + if (p) { + int on = 1; + setsockopt(m_fd, p->p_proto, TCP_NODELAY, &on, sizeof(on)); + } +} diff --git a/CUnixTCPSocket.h b/CUnixTCPSocket.h index ac8d0a34..0a04c5c4 100644 --- a/CUnixTCPSocket.h +++ b/CUnixTCPSocket.h @@ -23,6 +23,9 @@ class CUnixTCPSocket : public CSocket { private: CUnixTCPSocket(int); + // disable Nagle algorithm + void setNoDelay(); + // callbacks for read/write events void readCB(); void writeCB(); From 8beea84a8d253c8554c8bbddcec0c71cc10bbf38 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 14 May 2001 21:14:25 +0000 Subject: [PATCH 005/807] added other mouse and key event handling to CXScreen. key repeat isn't implemented and modifier masks are ignored. modifier masks are new; they indicate the modifier key (shift, ctrl, etc) state at the time of the key event. --- CClient.cpp | 38 ++++++++------ CEvent.h | 1 + CScreenProxy.cpp | 42 ++++++++------- CScreenProxy.h | 7 ++- CServer.cpp | 7 +-- CXScreen.cpp | 132 +++++++++++++++++++++++++++++++++-------------- CXScreen.h | 15 +++--- IScreen.h | 10 ++-- KeyTypes.h | 18 ++++--- 9 files changed, 170 insertions(+), 100 deletions(-) diff --git a/CClient.cpp b/CClient.cpp index 65030c99..83739e8c 100644 --- a/CClient.cpp +++ b/CClient.cpp @@ -154,8 +154,13 @@ void CClient::onRead() (static_cast(buf[2]) << 16) + (static_cast(buf[3]) << 8) + (static_cast(buf[4]) )); - TRACE((" key down: %d", k)); - m_screen->onKeyDown(k); + const KeyModifierMask m = static_cast( + (static_cast(buf[5]) << 24) + + (static_cast(buf[6]) << 16) + + (static_cast(buf[7]) << 8) + + (static_cast(buf[8]) )); + TRACE((" key down: %d 0x%08x", k, m)); + m_screen->onKeyDown(k, m); break; } @@ -165,13 +170,18 @@ void CClient::onRead() (static_cast(buf[2]) << 16) + (static_cast(buf[3]) << 8) + (static_cast(buf[4]) )); - const SInt32 n = static_cast( + const KeyModifierMask m = static_cast( (static_cast(buf[5]) << 24) + (static_cast(buf[6]) << 16) + (static_cast(buf[7]) << 8) + (static_cast(buf[8]) )); - TRACE((" key repeat: %d x%d", k, n)); - m_screen->onKeyRepeat(k, n); + const SInt32 n = static_cast( + (static_cast(buf[9]) << 24) + + (static_cast(buf[10]) << 16) + + (static_cast(buf[11]) << 8) + + (static_cast(buf[12]) )); + TRACE((" key repeat: %d 0x%08x x%d", k, m, n)); + m_screen->onKeyRepeat(k, m, n); break; } @@ -181,17 +191,13 @@ void CClient::onRead() (static_cast(buf[2]) << 16) + (static_cast(buf[3]) << 8) + (static_cast(buf[4]) )); - TRACE((" key up: %d", k)); - m_screen->onKeyUp(k); - break; - } - - case '\012': { - const KeyToggleMask m = static_cast( - (static_cast(buf[1]) << 8) + - (static_cast(buf[2]) )); - TRACE((" key toggle: 0x%04x", m)); - m_screen->onKeyToggle(m); + const KeyModifierMask m = static_cast( + (static_cast(buf[5]) << 24) + + (static_cast(buf[6]) << 16) + + (static_cast(buf[7]) << 8) + + (static_cast(buf[8]) )); + TRACE((" key up: %d 0x%08x", k, m)); + m_screen->onKeyUp(k, m); break; } diff --git a/CEvent.h b/CEvent.h index 452aa4e6..348218a2 100644 --- a/CEvent.h +++ b/CEvent.h @@ -27,6 +27,7 @@ class CEventBase { class CEventKey : public CEventBase { public: KeyID m_key; + KeyModifierMask m_mask; SInt32 m_count; }; diff --git a/CScreenProxy.cpp b/CScreenProxy.cpp index d287107d..62cf9258 100644 --- a/CScreenProxy.cpp +++ b/CScreenProxy.cpp @@ -82,49 +82,53 @@ void CScreenProxy::setClipboard(const IClipboard*) // FIXME } -void CScreenProxy::onKeyDown(KeyID k) +void CScreenProxy::onKeyDown(KeyID k, KeyModifierMask m) { - char buf[5]; + char buf[9]; memcpy(buf, "\007", 1); buf[1] = static_cast((k >> 24) & 0xff); buf[2] = static_cast((k >> 16) & 0xff); buf[3] = static_cast((k >> 8) & 0xff); buf[4] = static_cast(k & 0xff); + buf[5] = static_cast((m >> 24) & 0xff); + buf[6] = static_cast((m >> 16) & 0xff); + buf[7] = static_cast((m >> 8) & 0xff); + buf[8] = static_cast(m & 0xff); m_socket->write(buf, sizeof(buf)); } -void CScreenProxy::onKeyRepeat(KeyID k, SInt32 n) +void CScreenProxy::onKeyRepeat( + KeyID k, KeyModifierMask m, SInt32 n) { - char buf[9]; + char buf[13]; memcpy(buf, "\010", 1); buf[1] = static_cast((k >> 24) & 0xff); buf[2] = static_cast((k >> 16) & 0xff); buf[3] = static_cast((k >> 8) & 0xff); buf[4] = static_cast(k & 0xff); - buf[5] = static_cast((n >> 24) & 0xff); - buf[6] = static_cast((n >> 16) & 0xff); - buf[7] = static_cast((n >> 8) & 0xff); - buf[8] = static_cast(n & 0xff); + buf[5] = static_cast((m >> 24) & 0xff); + buf[6] = static_cast((m >> 16) & 0xff); + buf[7] = static_cast((m >> 8) & 0xff); + buf[8] = static_cast(m & 0xff); + buf[9] = static_cast((n >> 24) & 0xff); + buf[10] = static_cast((n >> 16) & 0xff); + buf[11] = static_cast((n >> 8) & 0xff); + buf[12] = static_cast(n & 0xff); m_socket->write(buf, sizeof(buf)); } -void CScreenProxy::onKeyUp(KeyID k) +void CScreenProxy::onKeyUp(KeyID k, KeyModifierMask m) { - char buf[5]; + char buf[9]; memcpy(buf, "\011", 1); buf[1] = static_cast((k >> 24) & 0xff); buf[2] = static_cast((k >> 16) & 0xff); buf[3] = static_cast((k >> 8) & 0xff); buf[4] = static_cast(k & 0xff); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::onKeyToggle(KeyToggleMask m) -{ - char buf[3]; - memcpy(buf, "\012", 1); - buf[1] = static_cast((m >> 8) & 0xff); - buf[2] = static_cast(m & 0xff); + buf[5] = static_cast((m >> 24) & 0xff); + buf[6] = static_cast((m >> 16) & 0xff); + buf[7] = static_cast((m >> 8) & 0xff); + buf[8] = static_cast(m & 0xff); m_socket->write(buf, sizeof(buf)); } diff --git a/CScreenProxy.h b/CScreenProxy.h index 39786d82..bbcb7a1d 100644 --- a/CScreenProxy.h +++ b/CScreenProxy.h @@ -18,10 +18,9 @@ class CScreenProxy : public IScreen { virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(const IClipboard*); virtual void onScreenSaver(bool show); - virtual void onKeyDown(KeyID); - virtual void onKeyRepeat(KeyID, SInt32 count); - virtual void onKeyUp(KeyID); - virtual void onKeyToggle(KeyToggleMask); + virtual void onKeyDown(KeyID, KeyModifierMask); + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void onKeyUp(KeyID, KeyModifierMask); virtual void onMouseDown(ButtonID); virtual void onMouseUp(ButtonID); virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute); diff --git a/CServer.cpp b/CServer.cpp index fb28810f..80bf026c 100644 --- a/CServer.cpp +++ b/CServer.cpp @@ -350,15 +350,16 @@ void CServer::relayEvent(const CEvent* event) break; case CEventBase::kKeyDown: - m_activeScreen->onKeyDown(event->m_key.m_key); + m_activeScreen->onKeyDown(event->m_key.m_key, event->m_key.m_mask); break; case CEventBase::kKeyRepeat: - m_activeScreen->onKeyRepeat(event->m_key.m_key, event->m_key.m_count); + m_activeScreen->onKeyRepeat(event->m_key.m_key, + event->m_key.m_mask, event->m_key.m_count); break; case CEventBase::kKeyUp: - m_activeScreen->onKeyUp(event->m_key.m_key); + m_activeScreen->onKeyUp(event->m_key.m_key, event->m_key.m_mask); break; case CEventBase::kMouseDown: diff --git a/CXScreen.cpp b/CXScreen.cpp index 1f738fc2..049641c6 100644 --- a/CXScreen.cpp +++ b/CXScreen.cpp @@ -146,7 +146,17 @@ void CXScreen::onScreenSaver(bool show) onScreenSaverSecondary(show); } -void CXScreen::onKeyDown(KeyID) +void CXScreen::onKeyDown(KeyID key, KeyModifierMask) +{ + assert(m_display != NULL); + assert(m_primary == false); + + // FIXME -- use mask + ::XTestFakeKeyEvent(m_display, mapKeyToX(key), True, CurrentTime); + ::XSync(m_display, False); +} + +void CXScreen::onKeyRepeat(KeyID, KeyModifierMask, SInt32) { assert(m_display != NULL); assert(m_primary == false); @@ -154,44 +164,32 @@ void CXScreen::onKeyDown(KeyID) // FIXME } -void CXScreen::onKeyRepeat(KeyID, SInt32) +void CXScreen::onKeyUp(KeyID key, KeyModifierMask) { assert(m_display != NULL); assert(m_primary == false); - // FIXME + // FIXME -- use mask + ::XTestFakeKeyEvent(m_display, mapKeyToX(key), False, CurrentTime); + ::XSync(m_display, False); } -void CXScreen::onKeyUp(KeyID) +void CXScreen::onMouseDown(ButtonID button) { assert(m_display != NULL); assert(m_primary == false); - // FIXME + ::XTestFakeButtonEvent(m_display, mapButtonToX(button), True, CurrentTime); + ::XSync(m_display, False); } -void CXScreen::onKeyToggle(KeyToggleMask) +void CXScreen::onMouseUp(ButtonID button) { assert(m_display != NULL); assert(m_primary == false); - // FIXME -} - -void CXScreen::onMouseDown(ButtonID) -{ - assert(m_display != NULL); - assert(m_primary == false); - - // FIXME -} - -void CXScreen::onMouseUp(ButtonID) -{ - assert(m_display != NULL); - assert(m_primary == false); - - // FIXME + ::XTestFakeButtonEvent(m_display, mapButtonToX(button), False, CurrentTime); + ::XSync(m_display, False); } void CXScreen::onMouseMove(SInt32 x, SInt32 y) @@ -430,25 +428,77 @@ void CXScreen::selectEvents(Window w) const } } -KeyID CXScreen::mapKey(unsigned int keycode) const +KeyModifierMask CXScreen::mapModifierFromX(unsigned int state) const { - return keycode; + // FIXME -- should be configurable + KeyModifierMask mask = 0; + if (state & 1) + mask |= KeyModifierShift; + if (state & 2) + mask |= KeyModifierCapsLock; + if (state & 4) + mask |= KeyModifierControl; + if (state & 8) + mask |= KeyModifierAlt; + if (state & 16) + mask |= KeyModifierNumLock; + if (state & 32) + mask |= KeyModifierMeta; + if (state & 128) + mask |= KeyModifierScrollLock; + return mask; } -ButtonID CXScreen::mapButton(unsigned int button) const +unsigned int CXScreen::mapModifierToX(KeyModifierMask mask) const { - switch (button) { - case 1: - return kButtonLeft; + // FIXME -- should be configurable + unsigned int state = 0; + if (mask & KeyModifierShift) + state |= 1; + if (mask & KeyModifierControl) + state |= 4; + if (mask & KeyModifierAlt) + state |= 8; + if (mask & KeyModifierMeta) + state |= 32; + if (mask & KeyModifierCapsLock) + state |= 2; + if (mask & KeyModifierNumLock) + state |= 16; + if (mask & KeyModifierScrollLock) + state |= 128; + return state; +} - case 2: - return kButtonMiddle; +KeyID CXScreen::mapKeyFromX( + KeyCode keycode, KeyModifierMask mask) const +{ + int index; + if (mask & KeyModifierShift) + index = 1; + else + index = 0; + return static_cast(::XKeycodeToKeysym(m_display, keycode, index)); +} - case 3: - return kButtonRight; - } +KeyCode CXScreen::mapKeyToX(KeyID keyID) const +{ + return ::XKeysymToKeycode(m_display, static_cast(keyID)); +} - return kButtonNone; +ButtonID CXScreen::mapButtonFromX(unsigned int button) const +{ + // FIXME -- should use button mapping? + if (button >= 1 && button <= 3) + return static_cast(button); + else + return kButtonNone; +} + +unsigned int CXScreen::mapButtonToX(ButtonID buttonID) const +{ + // FIXME -- should use button mapping? + return static_cast(buttonID); } void CXScreen::onPrimaryEvents() @@ -459,11 +509,13 @@ void CXScreen::onPrimaryEvents() switch (xevent.type) { case KeyPress: { - const KeyID key = mapKey(xevent.xkey.keycode); + const KeyModifierMask mask = mapModifierFromX(xevent.xkey.state); + const KeyID key = mapKeyFromX(xevent.xkey.keycode, mask); if (key != kKeyNone) { CEvent event; event.m_key.m_type = CEventBase::kKeyDown; event.m_key.m_key = key; + event.m_key.m_mask = mask; event.m_key.m_count = 0; CEQ->push(&event); } @@ -473,11 +525,13 @@ void CXScreen::onPrimaryEvents() // FIXME -- simulate key repeat. X sends press/release for // repeat. must detect auto repeat and use kKeyRepeat. case KeyRelease: { - const KeyID key = mapKey(xevent.xkey.keycode); + const KeyModifierMask mask = mapModifierFromX(xevent.xkey.state); + const KeyID key = mapKeyFromX(xevent.xkey.keycode, mask); if (key != kKeyNone) { CEvent event; event.m_key.m_type = CEventBase::kKeyUp; event.m_key.m_key = key; + event.m_key.m_mask = mask; event.m_key.m_count = 0; CEQ->push(&event); } @@ -485,7 +539,7 @@ void CXScreen::onPrimaryEvents() } case ButtonPress: { - const ButtonID button = mapButton(xevent.xbutton.button); + const ButtonID button = mapButtonFromX(xevent.xbutton.button); if (button != kButtonNone) { CEvent event; event.m_mouse.m_type = CEventBase::kMouseDown; @@ -498,7 +552,7 @@ void CXScreen::onPrimaryEvents() } case ButtonRelease: { - const ButtonID button = mapButton(xevent.xbutton.button); + const ButtonID button = mapButtonFromX(xevent.xbutton.button); if (button != kButtonNone) { CEvent event; event.m_mouse.m_type = CEventBase::kMouseUp; diff --git a/CXScreen.h b/CXScreen.h index c2fb9b83..96a4ab28 100644 --- a/CXScreen.h +++ b/CXScreen.h @@ -17,10 +17,9 @@ class CXScreen : public IScreen { virtual void warpCursor(SInt32 x, SInt32 y); virtual void setClipboard(const IClipboard*); virtual void onScreenSaver(bool); - virtual void onKeyDown(KeyID); - virtual void onKeyRepeat(KeyID, SInt32); - virtual void onKeyUp(KeyID); - virtual void onKeyToggle(KeyToggleMask); + virtual void onKeyDown(KeyID, KeyModifierMask); + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32); + virtual void onKeyUp(KeyID, KeyModifierMask); virtual void onMouseDown(ButtonID); virtual void onMouseUp(ButtonID); virtual void onMouseMove(SInt32, SInt32); @@ -61,8 +60,12 @@ class CXScreen : public IScreen { private: void selectEvents(Window) const; - KeyID mapKey(unsigned int keycode) const; - ButtonID mapButton(unsigned int button) const; + KeyModifierMask mapModifierFromX(unsigned int) const; + unsigned int mapModifierToX(KeyModifierMask) const; + KeyID mapKeyFromX(KeyCode, KeyModifierMask) const; + KeyCode mapKeyToX(KeyID) const; + ButtonID mapButtonFromX(unsigned int button) const; + unsigned int mapButtonToX(ButtonID) const; void onPrimaryEvents(); void onSecondaryEvents(); diff --git a/IScreen.h b/IScreen.h index a8427584..fd2a8346 100644 --- a/IScreen.h +++ b/IScreen.h @@ -93,15 +93,13 @@ class IScreen { // input simulation // // these methods must simulate the appropriate input event. - // these methods may only called on secondary screens. + // these methods are only called on secondary screens. // // keyboard input - // onKeyToggle() sets the keyboard toggle key states (e.g. num lock). - virtual void onKeyDown(KeyID) = 0; - virtual void onKeyRepeat(KeyID, SInt32 count) = 0; - virtual void onKeyUp(KeyID) = 0; - virtual void onKeyToggle(KeyToggleMask) = 0; + virtual void onKeyDown(KeyID, KeyModifierMask) = 0; + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void onKeyUp(KeyID, KeyModifierMask) = 0; // mouse input virtual void onMouseDown(ButtonID) = 0; diff --git a/KeyTypes.h b/KeyTypes.h index c220a9fc..c3789fa1 100644 --- a/KeyTypes.h +++ b/KeyTypes.h @@ -4,15 +4,19 @@ // type to hold a key identifier typedef UInt32 KeyID; -// type to hold bitmask of keys that have toggle states -typedef UInt16 KeyToggleMask; - -// toggle key bitmasks -static const UInt32 KeyToggleShiftLock = 0x0001; -static const UInt32 KeyToggleNumLock = 0x0002; -static const UInt32 KeyToggleScrollLock = 0x0004; +// type to hold bitmask of key modifiers (i.e. shift keys) +typedef UInt32 KeyModifierMask; // key codes static const KeyID kKeyNone = 0; +// modifier key bitmasks +static const KeyModifierMask KeyModifierShift = 0x0001; +static const KeyModifierMask KeyModifierControl = 0x0002; +static const KeyModifierMask KeyModifierAlt = 0x0004; +static const KeyModifierMask KeyModifierMeta = 0x0008; +static const KeyModifierMask KeyModifierCapsLock = 0x1000; +static const KeyModifierMask KeyModifierNumLock = 0x2000; +static const KeyModifierMask KeyModifierScrollLock = 0x4000; + #endif From 27ead1f713c87542efb9e8861c64056826a1268f Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 14 May 2001 21:14:49 +0000 Subject: [PATCH 006/807] flipped order of buttons to match default X setup. --- MouseTypes.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MouseTypes.h b/MouseTypes.h index ac908486..3dea13d9 100644 --- a/MouseTypes.h +++ b/MouseTypes.h @@ -7,7 +7,7 @@ typedef UInt8 ButtonID; // mouse button identifiers static const ButtonID kButtonNone = 0; static const ButtonID kButtonLeft = 1; -static const ButtonID kButtonRight = 2; -static const ButtonID kButtonMiddle = 3; +static const ButtonID kButtonMiddle = 2; +static const ButtonID kButtonRight = 3; #endif From ff81f708e2aa0411756e460ec242c52084aaddae Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 6 Oct 2001 14:13:28 +0000 Subject: [PATCH 007/807] Started over. --- BasicTypes.h | 33 -- CClient.cpp | 293 --------- CClient.h | 28 - CEvent.h | 59 -- CEventQueue.cpp | 80 --- CEventQueue.h | 36 -- CMessageSocket.cpp | 153 ----- CMessageSocket.h | 33 -- CProtocol.h | 20 - CScreenProxy.cpp | 238 -------- CScreenProxy.h | 42 -- CServer.cpp | 812 ------------------------- CServer.h | 103 ---- CSocket.cpp | 58 -- CSocket.h | 41 -- CSocketFactory.cpp | 31 - CSocketFactory.h | 29 - CString.h | 40 -- CTrace.cpp | 16 - CTrace.h | 19 - CUnixEventQueue.cpp | 141 ----- CUnixEventQueue.h | 54 -- CUnixTCPSocket.cpp | 278 --------- CUnixTCPSocket.h | 50 -- CUnixXScreen.cpp | 34 -- CUnixXScreen.h | 16 - CXScreen.cpp | 626 -------------------- CXScreen.h | 84 --- IClient.h | 17 - IClipboard.h | 0 IEventQueue.h | 45 -- IScreen.h | 130 ---- IServer.h | 29 - ISocket.h | 45 -- Make-solaris | 67 +++ Makecommon | 93 +++ TMethodJob.h | 23 - XBase.cpp | 40 -- XBase.h | 31 - XSocket.h | 33 -- base/BasicTypes.h | 46 ++ base/CFunctionJob.cpp | 19 + base/CFunctionJob.h | 18 + base/CStopwatch.cpp | 180 ++++++ base/CStopwatch.h | 57 ++ base/CString.h | 41 ++ base/IInterface.h | 11 + IJob.h => base/IJob.h | 9 +- base/Makefile | 24 + base/TMethodJob.h | 39 ++ base/XBase.cpp | 72 +++ base/XBase.h | 44 ++ base/common.h | 32 + io/CBufferedInputStream.cpp | 107 ++++ io/CBufferedInputStream.h | 51 ++ io/CBufferedOutputStream.cpp | 79 +++ io/CBufferedOutputStream.h | 46 ++ io/CInputStreamFilter.cpp | 25 + io/CInputStreamFilter.h | 28 + io/COutputStreamFilter.cpp | 25 + io/COutputStreamFilter.h | 28 + io/CStreamBuffer.cpp | 107 ++++ io/CStreamBuffer.h | 38 ++ io/IInputStream.h | 29 + io/IOutputStream.h | 24 + io/Makefile | 29 + io/XIO.cpp | 46 ++ io/XIO.h | 33 ++ main.cpp | 114 ---- mt/CCondVar.cpp | 296 ++++++++++ mt/CCondVar.h | 144 +++++ mt/CLock.cpp | 22 + mt/CLock.h | 24 + mt/CMutex.cpp | 123 ++++ mt/CMutex.h | 35 ++ mt/CThread.cpp | 131 +++++ mt/CThread.h | 130 ++++ mt/CThreadRep.cpp | 518 ++++++++++++++++ mt/CThreadRep.h | 122 ++++ mt/CTimerThread.cpp | 30 + mt/CTimerThread.h | 27 + mt/Makefile | 28 + mt/XThread.h | 31 + net/CNetworkAddress.cpp | 65 ++ net/CNetworkAddress.h | 27 + net/CSocketInputStream.cpp | 93 +++ net/CSocketInputStream.h | 42 ++ net/CSocketOutputStream.cpp | 78 +++ net/CSocketOutputStream.h | 43 ++ net/CSocketStreamBuffer.cpp | 106 ++++ net/CSocketStreamBuffer.h | 38 ++ net/CTCPListenSocket.cpp | 72 +++ net/CTCPListenSocket.h | 24 + net/CTCPSocket.cpp | 226 +++++++ net/CTCPSocket.h | 49 ++ net/IListenSocket.h | 27 + net/ISocket.h | 35 ++ net/Makefile | 28 + net/XSocket.cpp | 84 +++ net/XSocket.h | 58 ++ notes | 85 +++ synergy/CClient.cpp | 265 +++++++++ synergy/CClient.h | 46 ++ synergy/CInputPacketStream.cpp | 109 ++++ synergy/CInputPacketStream.h | 33 ++ synergy/COutputPacketStream.cpp | 56 ++ synergy/COutputPacketStream.h | 21 + synergy/CProtocolUtil.cpp | 336 +++++++++++ synergy/CProtocolUtil.h | 53 ++ synergy/CScreenMap.cpp | 89 +++ synergy/CScreenMap.h | 46 ++ synergy/CServer.cpp | 849 +++++++++++++++++++++++++++ synergy/CServer.h | 158 +++++ synergy/CServerProtocol.cpp | 72 +++ synergy/CServerProtocol.h | 58 ++ synergy/CServerProtocol1_0.cpp | 157 +++++ synergy/CServerProtocol1_0.h | 37 ++ synergy/CTCPSocketFactory.cpp | 27 + synergy/CTCPSocketFactory.h | 20 + synergy/CXWindowsPrimaryScreen.cpp | 363 ++++++++++++ synergy/CXWindowsPrimaryScreen.h | 43 ++ synergy/IPrimaryScreen.h | 73 +++ synergy/ISecondaryScreen.h | 71 +++ synergy/IServerProtocol.h | 47 ++ synergy/ISocketFactory.h | 21 + KeyTypes.h => synergy/KeyTypes.h | 2 + synergy/Makefile | 44 ++ MouseTypes.h => synergy/MouseTypes.h | 2 + synergy/ProtocolTypes.h | 38 ++ synergy/XSynergy.cpp | 37 ++ synergy/XSynergy.h | 34 ++ test.cpp | 145 +++++ 132 files changed, 7634 insertions(+), 3960 deletions(-) delete mode 100644 BasicTypes.h delete mode 100644 CClient.cpp delete mode 100644 CClient.h delete mode 100644 CEvent.h delete mode 100644 CEventQueue.cpp delete mode 100644 CEventQueue.h delete mode 100644 CMessageSocket.cpp delete mode 100644 CMessageSocket.h delete mode 100644 CProtocol.h delete mode 100644 CScreenProxy.cpp delete mode 100644 CScreenProxy.h delete mode 100644 CServer.cpp delete mode 100644 CServer.h delete mode 100644 CSocket.cpp delete mode 100644 CSocket.h delete mode 100644 CSocketFactory.cpp delete mode 100644 CSocketFactory.h delete mode 100644 CString.h delete mode 100644 CTrace.cpp delete mode 100644 CTrace.h delete mode 100644 CUnixEventQueue.cpp delete mode 100644 CUnixEventQueue.h delete mode 100644 CUnixTCPSocket.cpp delete mode 100644 CUnixTCPSocket.h delete mode 100644 CUnixXScreen.cpp delete mode 100644 CUnixXScreen.h delete mode 100644 CXScreen.cpp delete mode 100644 CXScreen.h delete mode 100644 IClient.h delete mode 100644 IClipboard.h delete mode 100644 IEventQueue.h delete mode 100644 IScreen.h delete mode 100644 IServer.h delete mode 100644 ISocket.h create mode 100644 Make-solaris create mode 100644 Makecommon delete mode 100644 TMethodJob.h delete mode 100644 XBase.cpp delete mode 100644 XBase.h delete mode 100644 XSocket.h create mode 100644 base/BasicTypes.h create mode 100644 base/CFunctionJob.cpp create mode 100644 base/CFunctionJob.h create mode 100644 base/CStopwatch.cpp create mode 100644 base/CStopwatch.h create mode 100644 base/CString.h create mode 100644 base/IInterface.h rename IJob.h => base/IJob.h (55%) create mode 100644 base/Makefile create mode 100644 base/TMethodJob.h create mode 100644 base/XBase.cpp create mode 100644 base/XBase.h create mode 100644 base/common.h create mode 100644 io/CBufferedInputStream.cpp create mode 100644 io/CBufferedInputStream.h create mode 100644 io/CBufferedOutputStream.cpp create mode 100644 io/CBufferedOutputStream.h create mode 100644 io/CInputStreamFilter.cpp create mode 100644 io/CInputStreamFilter.h create mode 100644 io/COutputStreamFilter.cpp create mode 100644 io/COutputStreamFilter.h create mode 100644 io/CStreamBuffer.cpp create mode 100644 io/CStreamBuffer.h create mode 100644 io/IInputStream.h create mode 100644 io/IOutputStream.h create mode 100644 io/Makefile create mode 100644 io/XIO.cpp create mode 100644 io/XIO.h delete mode 100644 main.cpp create mode 100644 mt/CCondVar.cpp create mode 100644 mt/CCondVar.h create mode 100644 mt/CLock.cpp create mode 100644 mt/CLock.h create mode 100644 mt/CMutex.cpp create mode 100644 mt/CMutex.h create mode 100644 mt/CThread.cpp create mode 100644 mt/CThread.h create mode 100644 mt/CThreadRep.cpp create mode 100644 mt/CThreadRep.h create mode 100644 mt/CTimerThread.cpp create mode 100644 mt/CTimerThread.h create mode 100644 mt/Makefile create mode 100644 mt/XThread.h create mode 100644 net/CNetworkAddress.cpp create mode 100644 net/CNetworkAddress.h create mode 100644 net/CSocketInputStream.cpp create mode 100644 net/CSocketInputStream.h create mode 100644 net/CSocketOutputStream.cpp create mode 100644 net/CSocketOutputStream.h create mode 100644 net/CSocketStreamBuffer.cpp create mode 100644 net/CSocketStreamBuffer.h create mode 100644 net/CTCPListenSocket.cpp create mode 100644 net/CTCPListenSocket.h create mode 100644 net/CTCPSocket.cpp create mode 100644 net/CTCPSocket.h create mode 100644 net/IListenSocket.h create mode 100644 net/ISocket.h create mode 100644 net/Makefile create mode 100644 net/XSocket.cpp create mode 100644 net/XSocket.h create mode 100644 notes create mode 100644 synergy/CClient.cpp create mode 100644 synergy/CClient.h create mode 100644 synergy/CInputPacketStream.cpp create mode 100644 synergy/CInputPacketStream.h create mode 100644 synergy/COutputPacketStream.cpp create mode 100644 synergy/COutputPacketStream.h create mode 100644 synergy/CProtocolUtil.cpp create mode 100644 synergy/CProtocolUtil.h create mode 100644 synergy/CScreenMap.cpp create mode 100644 synergy/CScreenMap.h create mode 100644 synergy/CServer.cpp create mode 100644 synergy/CServer.h create mode 100644 synergy/CServerProtocol.cpp create mode 100644 synergy/CServerProtocol.h create mode 100644 synergy/CServerProtocol1_0.cpp create mode 100644 synergy/CServerProtocol1_0.h create mode 100644 synergy/CTCPSocketFactory.cpp create mode 100644 synergy/CTCPSocketFactory.h create mode 100644 synergy/CXWindowsPrimaryScreen.cpp create mode 100644 synergy/CXWindowsPrimaryScreen.h create mode 100644 synergy/IPrimaryScreen.h create mode 100644 synergy/ISecondaryScreen.h create mode 100644 synergy/IServerProtocol.h create mode 100644 synergy/ISocketFactory.h rename KeyTypes.h => synergy/KeyTypes.h (96%) create mode 100644 synergy/Makefile rename MouseTypes.h => synergy/MouseTypes.h (92%) create mode 100644 synergy/ProtocolTypes.h create mode 100644 synergy/XSynergy.cpp create mode 100644 synergy/XSynergy.h create mode 100644 test.cpp diff --git a/BasicTypes.h b/BasicTypes.h deleted file mode 100644 index 8dfce8c2..00000000 --- a/BasicTypes.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef BASICTYPES_H -#define BASICTYPES_H - -#if defined(__linux__) - -#define CONFIG_PLATFORM_LINUX -#define CONFIG_TYPES_X11 - -#include - -typedef int8_t SInt8; -typedef int16_t SInt16; -typedef int32_t SInt32; -typedef int64_t SInt64; - -typedef uint8_t UInt8; -typedef uint16_t UInt16; -typedef uint32_t UInt32; -typedef uint64_t UInt64; - -#else - -#error unsupported platform - -#endif - -#ifndef NULL -#define NULL 0 -#endif - -#endif - - diff --git a/CClient.cpp b/CClient.cpp deleted file mode 100644 index 83739e8c..00000000 --- a/CClient.cpp +++ /dev/null @@ -1,293 +0,0 @@ -#include "CClient.h" -#include "CString.h" -#include "TMethodJob.h" -#include "IScreen.h" -#include "ISocket.h" -#include "CMessageSocket.h" -#include "CSocketFactory.h" -#include "IEventQueue.h" -#include "CEvent.h" -#include "CTrace.h" -#include - -// -// CClient -// - -CClient::CClient(IScreen* screen) : m_screen(screen), - m_socket(NULL) -{ - assert(m_screen != NULL); - assert(!m_screen->getName().empty()); -} - -CClient::~CClient() -{ - assert(m_socket == NULL); -} - -void CClient::run(const CString& hostname) -{ - assert(m_socket == NULL); - - try { - // create socket and - m_socket = CSOCKETFACTORY->create(); - m_socket->setWriteJob(new TMethodJob(this, - &CClient::onConnect)); - TRACE(("connecting to %s...", hostname.c_str())); - m_socket->connect(hostname, 40001); // CProtocol::kDefaultPort - - bool m_done = false; // FIXME - - IEventQueue* queue = CEQ; - while (!m_done) { - // wait for connection, network messages, and events - queue->wait(-1.0); - - // handle events - while (!queue->isEmpty()) { - // get the next event - CEvent event; - queue->pop(&event); - - // handle it - switch (event.m_any.m_type) { - case CEventBase::kScreenSize: { - sendScreenSize(); - break; - } - - case CEventBase::kNull: - case CEventBase::kKeyDown: - case CEventBase::kKeyRepeat: - case CEventBase::kKeyUp: - case CEventBase::kMouseDown: - case CEventBase::kMouseUp: - case CEventBase::kMouseMove: - case CEventBase::kMouseWheel: - // FIXME -- other cases - break; - } - } - } - - delete m_socket; - m_socket = NULL; - } - - catch (...) { - delete m_socket; - m_socket = NULL; - throw; - } -} - -void CClient::onConnect() -{ - TRACE(("connected")); - - // say hello - const CString name(m_screen->getName()); - char buf[512]; - memcpy(buf, "SYNERGY\000\001", 9); - buf[9] = static_cast(name.length()); - memcpy(buf + 10, name.c_str(), name.length()); - m_socket->write(buf, 10 + name.length()); - - // handle messages - m_socket->setWriteJob(NULL); - m_socket = new CMessageSocket(m_socket); - m_socket->setReadJob(new TMethodJob(this, &CClient::onRead)); -} - -void CClient::onRead() -{ - char buf[512]; - SInt32 n = m_socket->read(buf, sizeof(buf)); - if (n == -1) { - // disconnect - TRACE(("hangup")); - } - else if (n > 0) { - TRACE(("msg: 0x%02x length %d", buf[0], n)); - switch (buf[0]) { - case '\002': - TRACE((" open")); - - // open the screen - m_screen->open(buf[1] != 0); - - // send initial size - sendScreenSize(); - break; - - case '\003': - TRACE((" close")); - m_screen->close(); - break; - - case '\004': { - const SInt32 x = static_cast( - (static_cast(buf[1]) << 24) + - (static_cast(buf[2]) << 16) + - (static_cast(buf[3]) << 8) + - (static_cast(buf[4]) )); - const SInt32 y = static_cast( - (static_cast(buf[5]) << 24) + - (static_cast(buf[6]) << 16) + - (static_cast(buf[7]) << 8) + - (static_cast(buf[8]) )); - TRACE((" enter: %d,%d", x, y)); - m_screen->enterScreen(x, y); - break; - } - - case '\005': - TRACE((" leave")); - m_screen->leaveScreen(); - break; - - case '\007': { - const KeyID k = static_cast( - (static_cast(buf[1]) << 24) + - (static_cast(buf[2]) << 16) + - (static_cast(buf[3]) << 8) + - (static_cast(buf[4]) )); - const KeyModifierMask m = static_cast( - (static_cast(buf[5]) << 24) + - (static_cast(buf[6]) << 16) + - (static_cast(buf[7]) << 8) + - (static_cast(buf[8]) )); - TRACE((" key down: %d 0x%08x", k, m)); - m_screen->onKeyDown(k, m); - break; - } - - case '\010': { - const KeyID k = static_cast( - (static_cast(buf[1]) << 24) + - (static_cast(buf[2]) << 16) + - (static_cast(buf[3]) << 8) + - (static_cast(buf[4]) )); - const KeyModifierMask m = static_cast( - (static_cast(buf[5]) << 24) + - (static_cast(buf[6]) << 16) + - (static_cast(buf[7]) << 8) + - (static_cast(buf[8]) )); - const SInt32 n = static_cast( - (static_cast(buf[9]) << 24) + - (static_cast(buf[10]) << 16) + - (static_cast(buf[11]) << 8) + - (static_cast(buf[12]) )); - TRACE((" key repeat: %d 0x%08x x%d", k, m, n)); - m_screen->onKeyRepeat(k, m, n); - break; - } - - case '\011': { - const KeyID k = static_cast( - (static_cast(buf[1]) << 24) + - (static_cast(buf[2]) << 16) + - (static_cast(buf[3]) << 8) + - (static_cast(buf[4]) )); - const KeyModifierMask m = static_cast( - (static_cast(buf[5]) << 24) + - (static_cast(buf[6]) << 16) + - (static_cast(buf[7]) << 8) + - (static_cast(buf[8]) )); - TRACE((" key up: %d 0x%08x", k, m)); - m_screen->onKeyUp(k, m); - break; - } - - case '\013': { - const ButtonID b = static_cast( - static_cast(buf[1])); - TRACE((" mouse down: %d", b)); - m_screen->onMouseDown(b); - break; - } - - case '\014': { - const ButtonID b = static_cast( - static_cast(buf[1])); - TRACE((" mouse up: %d", b)); - m_screen->onMouseUp(b); - break; - } - - case '\015': { - const SInt32 x = static_cast( - (static_cast(buf[1]) << 24) + - (static_cast(buf[2]) << 16) + - (static_cast(buf[3]) << 8) + - (static_cast(buf[4]) )); - const SInt32 y = static_cast( - (static_cast(buf[5]) << 24) + - (static_cast(buf[6]) << 16) + - (static_cast(buf[7]) << 8) + - (static_cast(buf[8]) )); - TRACE((" mouse move: %d,%d", x, y)); - m_screen->onMouseMove(x, y); - break; - } - - case '\016': { - const SInt32 n = static_cast( - (static_cast(buf[1]) << 24) + - (static_cast(buf[2]) << 16) + - (static_cast(buf[3]) << 8) + - (static_cast(buf[4]) )); - TRACE((" mouse wheel: %d", n)); - m_screen->onMouseWheel(n); - break; - } - - case '\017': { - TRACE((" screen saver: %s", buf[1] ? "on" : "off")); - m_screen->onScreenSaver(buf[1] != 0); - break; - } - - case '\020': { - const SInt32 x = static_cast( - (static_cast(buf[1]) << 24) + - (static_cast(buf[2]) << 16) + - (static_cast(buf[3]) << 8) + - (static_cast(buf[4]) )); - const SInt32 y = static_cast( - (static_cast(buf[5]) << 24) + - (static_cast(buf[6]) << 16) + - (static_cast(buf[7]) << 8) + - (static_cast(buf[8]) )); - TRACE((" warp: %d,%d", x, y)); - m_screen->warpCursor(x, y); - break; - } - - default: - TRACE((" unknown message")); - } - } -} - -void CClient::sendScreenSize() -{ - // get the size - SInt32 w, h; - m_screen->getSize(&w, &h); - - // send it - char buf[9]; - memcpy(buf, "\201", 1); - buf[1] = static_cast((w >> 24) & 0xff); - buf[2] = static_cast((w >> 16) & 0xff); - buf[3] = static_cast((w >> 8) & 0xff); - buf[4] = static_cast(w & 0xff); - buf[5] = static_cast((h >> 24) & 0xff); - buf[6] = static_cast((h >> 16) & 0xff); - buf[7] = static_cast((h >> 8) & 0xff); - buf[8] = static_cast(h & 0xff); - m_socket->write(buf, sizeof(buf)); -} diff --git a/CClient.h b/CClient.h deleted file mode 100644 index c99cb364..00000000 --- a/CClient.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef CCLIENT_H -#define CCLIENT_H - -#include "IClient.h" - -class IScreen; -class ISocket; - -class CClient : public IClient { - public: - CClient(IScreen* screen); - virtual ~CClient(); - - // IClient overrides - virtual void run(const CString& hostname); - - private: - void onConnect(); - void onRead(); - - void sendScreenSize(); - - private: - IScreen* m_screen; - ISocket* m_socket; -}; - -#endif diff --git a/CEvent.h b/CEvent.h deleted file mode 100644 index 348218a2..00000000 --- a/CEvent.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef CEVENT_H -#define CEVENT_H - -#include "BasicTypes.h" -#include "KeyTypes.h" -#include "MouseTypes.h" - -class ISocket; - -class CEventBase { - public: - enum EType { - kNull, - kKeyDown, - kKeyRepeat, - kKeyUp, - kMouseDown, - kMouseUp, - kMouseMove, - kMouseWheel, - kScreenSize - }; - - EType m_type; -}; - -class CEventKey : public CEventBase { - public: - KeyID m_key; - KeyModifierMask m_mask; - SInt32 m_count; -}; - -class CEventMouse : public CEventBase { - public: - ButtonID m_button; - SInt32 m_x; // or wheel delta - SInt32 m_y; -}; - -class CEventSize : public CEventBase { - public: - SInt32 m_w; - SInt32 m_h; -}; - -class CEvent { - public: - union { - public: - CEventBase m_any; - CEventKey m_key; - CEventMouse m_mouse; - CEventSize m_size; - }; -}; - -#endif - diff --git a/CEventQueue.cpp b/CEventQueue.cpp deleted file mode 100644 index 0cef9742..00000000 --- a/CEventQueue.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "CEventQueue.h" - -// -// IEventQueue -// - -IEventQueue* IEventQueue::s_instance = NULL; - -IEventQueue::IEventQueue() -{ - assert(s_instance == NULL); - s_instance = this; -} - -IEventQueue::~IEventQueue() -{ - s_instance = NULL; -} - -IEventQueue* IEventQueue::getInstance() -{ - return s_instance; -} - - -// -// CEventQueue -// - -CEventQueue::CEventQueue() -{ - // do nothing -} - -CEventQueue::~CEventQueue() -{ - // do nothing -} - -void CEventQueue::pop(CEvent* event) -{ - assert(event != NULL); - - // wait for an event - while (isEmpty()) - wait(-1.0); - - // lock the queue, extract an event, then unlock - lock(); - *event = m_queue.front(); - m_queue.pop_front(); - unlock(); -} - -void CEventQueue::push(const CEvent* event) -{ - // put the event at the end of the queue and signal that the queue - // is not empty - lock(); - m_queue.push_back(*event); - signalNotEmpty(); - unlock(); -} - -bool CEventQueue::isEmpty() -{ - lock(); - bool e = m_queue.empty(); - unlock(); - - // if queue is empty then poll to see if more stuff is ready to go - // on the queue and check again if the queue is empty. - if (e) { - wait(0.0); - lock(); - e = m_queue.empty(); - unlock(); - } - return e; -} diff --git a/CEventQueue.h b/CEventQueue.h deleted file mode 100644 index de6a853d..00000000 --- a/CEventQueue.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef CEVENTQUEUE_H -#define CEVENTQUEUE_H - -#include "IEventQueue.h" -#include "CEvent.h" -#include - -class CEventQueue : public IEventQueue { - public: - CEventQueue(); - virtual ~CEventQueue(); - - // IEventQueue overrides - virtual void wait(double timeout) = 0; - virtual void pop(CEvent*); - virtual void push(const CEvent*); - virtual bool isEmpty(); - - protected: - // signal the queue not-empty condition. this should cause wait() - // to stop waiting. - virtual void signalNotEmpty() = 0; - - // lock the queue mutex - virtual void lock() = 0; - - // unlock the queue mutex - virtual void unlock() = 0; - - private: - typedef std::list List; - - List m_queue; -}; - -#endif diff --git a/CMessageSocket.cpp b/CMessageSocket.cpp deleted file mode 100644 index 97b0d516..00000000 --- a/CMessageSocket.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "CMessageSocket.h" -#include "TMethodJob.h" -#include "CTrace.h" -#include -#include - -// -// CMessageSocket -// - -CMessageSocket::CMessageSocket(ISocket* socket) : - m_socket(socket), - m_buffer(NULL), - m_size(0), - m_capacity(0), - m_msgSize(0) -{ - m_socket->setReadJob(new TMethodJob(this, - &CMessageSocket::readJobCB)); -} - -CMessageSocket::~CMessageSocket() -{ - delete m_socket; - delete[] m_buffer; -} - -void CMessageSocket::setWriteJob(IJob* adoptedJob) -{ - CSocket::setWriteJob(adoptedJob); - if (adoptedJob != NULL) - m_socket->setWriteJob(new TMethodJob(this, - &CMessageSocket::writeJobCB)); - else - m_socket->setWriteJob(NULL); -} - -void CMessageSocket::connect(const CString&, UInt16) -{ - assert(0 && "connect() illegal on CMessageSocket"); -} - -void CMessageSocket::listen(const CString&, UInt16) -{ - assert(0 && "listen() illegal on CMessageSocket"); -} - -ISocket* CMessageSocket::accept() -{ - assert(0 && "accept() illegal on CMessageSocket"); - return NULL; -} - -SInt32 CMessageSocket::read(void* buffer, SInt32 n) -{ - // if we don't have an entire message yet then read more data - if (m_size == 0 || m_size < m_msgSize) { - doRead(); - } - - // if we don't have a whole message yet then return 0 - if (m_size < m_msgSize) - return 0; - - // how many bytes should we return? - if (m_msgSize - 2 < n) - n = m_msgSize - 2; - - // copy data - // FIXME -- should have method for retrieving size of next message - ::memcpy(buffer, m_buffer + 2, n); - - // discard returned message - ::memmove(m_buffer, m_buffer + m_msgSize, m_size - m_msgSize); - m_size -= m_msgSize; - m_msgSize = 0; - - // get next message size - if (m_size >= 2) { - m_msgSize = static_cast( - (static_cast(m_buffer[0]) << 8) + - (static_cast(m_buffer[1]) )); - TRACE((" next message size: %d", m_msgSize)); - } - - return n; -} - -void CMessageSocket::write(const void* buffer, SInt32 n) -{ - // FIXME -- no fixed size buffers - char tmp[512]; - assert(n < (SInt32)sizeof(tmp) - 2); - ::memcpy(tmp + 2, buffer, n); - n += 2; - tmp[0] = static_cast((n >> 8) & 0xff); - tmp[1] = static_cast(n & 0xff); - m_socket->write(tmp, n); -} - -SInt32 CMessageSocket::doRead() -{ - // if read buffer is full then grow it - if (m_size == m_capacity) { - // compute new capacity and allocate space - SInt32 newCapacity = (m_capacity < 256) ? 256 : 2 * m_capacity; - UInt8* newBuffer = new UInt8[newCapacity]; - - // cut over - ::memcpy(newBuffer, m_buffer, m_size); - delete[] m_buffer; - m_buffer = newBuffer; - m_capacity = newCapacity; - } - - // read as much data as possible - const SInt32 numRead = m_socket->read(m_buffer + m_size, - m_capacity - m_size); - TRACE(("socket %p read %d bytes", this, numRead)); - - // hangup is a special case. if buffer isn't empty then we'll - // discard the partial message. - if (numRead == -1) - return numRead; - - // get next message size - if (m_size < 2 && m_size + numRead >= 2) { - m_msgSize = static_cast( - (static_cast(m_buffer[0]) << 8) + - (static_cast(m_buffer[1]) )); - TRACE((" next message size: %d", m_msgSize)); - } - - m_size += numRead; - return numRead; -} - -void CMessageSocket::readJobCB() -{ - if (doRead() == -1) { - // remote side hungup. don't check for readability anymore. - m_socket->setReadJob(NULL); - } - else if (m_size > 0 && m_size >= m_msgSize) { - TRACE((" message ready")); - runReadJob(); - } -} - -void CMessageSocket::writeJobCB() -{ - runWriteJob(); -} diff --git a/CMessageSocket.h b/CMessageSocket.h deleted file mode 100644 index b199c3ec..00000000 --- a/CMessageSocket.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef CMESSAGESOCKET_H -#define CMESSAGESOCKET_H - -#include "CSocket.h" - -class CMessageSocket : public CSocket { - public: - CMessageSocket(ISocket* adoptedSocket); - virtual ~CMessageSocket(); - - // ISocket overrides - // connect(), listen(), and accept() may not be called. - virtual void setWriteJob(IJob* adoptedJob); - virtual void connect(const CString& hostname, UInt16 port); - virtual void listen(const CString& hostname, UInt16 port); - virtual ISocket* accept(); - virtual SInt32 read(void* buffer, SInt32 numBytes); - virtual void write(const void* buffer, SInt32 numBytes); - - private: - SInt32 doRead(); - virtual void readJobCB(); - virtual void writeJobCB(); - - private: - ISocket* m_socket; - UInt8* m_buffer; - SInt32 m_size; - SInt32 m_capacity; - SInt32 m_msgSize; -}; - -#endif diff --git a/CProtocol.h b/CProtocol.h deleted file mode 100644 index 83659fd4..00000000 --- a/CProtocol.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef CPROTOCOL_H -#define CPROTOCOL_H - -#include "BasicTypes.h" - -class CProtocol { - public: - CProtocol(); - virtual ~CProtocol(); - - // manipulators - - - // accessors - - void ReadMessage(ISocket*, CMessage&) const; - void WriteMessage(ISocket*, const CMessage&) const; -}; - -#endif diff --git a/CScreenProxy.cpp b/CScreenProxy.cpp deleted file mode 100644 index 62cf9258..00000000 --- a/CScreenProxy.cpp +++ /dev/null @@ -1,238 +0,0 @@ -#include "CScreenProxy.h" -#include "ISocket.h" -#include "CMessageSocket.h" -#include "TMethodJob.h" -#include "CTrace.h" -// -// CScreenProxy -// - -CScreenProxy::CScreenProxy(const CString& name, ISocket* socket) : - m_name(name), - m_socket(socket), - m_w(0), m_h(0) -{ - assert(!m_name.empty()); - assert(m_socket != NULL); - - m_socket = new CMessageSocket(m_socket); - m_socket->setReadJob(new TMethodJob(this, - &CScreenProxy::onRead)); -} - -CScreenProxy::~CScreenProxy() -{ - delete m_socket; -} - -void CScreenProxy::open(bool isPrimary) -{ - char buf[2]; - memcpy(buf, "\002", 1); - buf[1] = static_cast(isPrimary ? 1 : 0); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::close() -{ - char buf[1]; - memcpy(buf, "\003", 1); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::enterScreen(SInt32 x, SInt32 y) -{ - char buf[9]; - memcpy(buf, "\004", 1); - buf[1] = static_cast((x >> 24) & 0xff); - buf[2] = static_cast((x >> 16) & 0xff); - buf[3] = static_cast((x >> 8) & 0xff); - buf[4] = static_cast(x & 0xff); - buf[5] = static_cast((y >> 24) & 0xff); - buf[6] = static_cast((y >> 16) & 0xff); - buf[7] = static_cast((y >> 8) & 0xff); - buf[8] = static_cast(y & 0xff); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::leaveScreen() -{ - char buf[1]; - memcpy(buf, "\005", 1); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::warpCursor(SInt32 x, SInt32 y) -{ - char buf[9]; - memcpy(buf, "\020", 1); - buf[1] = static_cast((x >> 24) & 0xff); - buf[2] = static_cast((x >> 16) & 0xff); - buf[3] = static_cast((x >> 8) & 0xff); - buf[4] = static_cast(x & 0xff); - buf[5] = static_cast((y >> 24) & 0xff); - buf[6] = static_cast((y >> 16) & 0xff); - buf[7] = static_cast((y >> 8) & 0xff); - buf[8] = static_cast(y & 0xff); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::setClipboard(const IClipboard*) -{ - // FIXME -} - -void CScreenProxy::onKeyDown(KeyID k, KeyModifierMask m) -{ - char buf[9]; - memcpy(buf, "\007", 1); - buf[1] = static_cast((k >> 24) & 0xff); - buf[2] = static_cast((k >> 16) & 0xff); - buf[3] = static_cast((k >> 8) & 0xff); - buf[4] = static_cast(k & 0xff); - buf[5] = static_cast((m >> 24) & 0xff); - buf[6] = static_cast((m >> 16) & 0xff); - buf[7] = static_cast((m >> 8) & 0xff); - buf[8] = static_cast(m & 0xff); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::onKeyRepeat( - KeyID k, KeyModifierMask m, SInt32 n) -{ - char buf[13]; - memcpy(buf, "\010", 1); - buf[1] = static_cast((k >> 24) & 0xff); - buf[2] = static_cast((k >> 16) & 0xff); - buf[3] = static_cast((k >> 8) & 0xff); - buf[4] = static_cast(k & 0xff); - buf[5] = static_cast((m >> 24) & 0xff); - buf[6] = static_cast((m >> 16) & 0xff); - buf[7] = static_cast((m >> 8) & 0xff); - buf[8] = static_cast(m & 0xff); - buf[9] = static_cast((n >> 24) & 0xff); - buf[10] = static_cast((n >> 16) & 0xff); - buf[11] = static_cast((n >> 8) & 0xff); - buf[12] = static_cast(n & 0xff); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::onKeyUp(KeyID k, KeyModifierMask m) -{ - char buf[9]; - memcpy(buf, "\011", 1); - buf[1] = static_cast((k >> 24) & 0xff); - buf[2] = static_cast((k >> 16) & 0xff); - buf[3] = static_cast((k >> 8) & 0xff); - buf[4] = static_cast(k & 0xff); - buf[5] = static_cast((m >> 24) & 0xff); - buf[6] = static_cast((m >> 16) & 0xff); - buf[7] = static_cast((m >> 8) & 0xff); - buf[8] = static_cast(m & 0xff); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::onMouseDown(ButtonID b) -{ - char buf[2]; - memcpy(buf, "\013", 1); - buf[1] = static_cast(b & 0xff); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::onMouseUp(ButtonID b) -{ - char buf[2]; - memcpy(buf, "\014", 1); - buf[1] = static_cast(b & 0xff); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::onMouseMove(SInt32 x, SInt32 y) -{ - char buf[9]; - memcpy(buf, "\015", 1); - buf[1] = static_cast((x >> 24) & 0xff); - buf[2] = static_cast((x >> 16) & 0xff); - buf[3] = static_cast((x >> 8) & 0xff); - buf[4] = static_cast(x & 0xff); - buf[5] = static_cast((y >> 24) & 0xff); - buf[6] = static_cast((y >> 16) & 0xff); - buf[7] = static_cast((y >> 8) & 0xff); - buf[8] = static_cast(y & 0xff); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::onMouseWheel(SInt32 n) -{ - char buf[5]; - memcpy(buf, "\016", 1); - buf[1] = static_cast((n >> 24) & 0xff); - buf[2] = static_cast((n >> 16) & 0xff); - buf[3] = static_cast((n >> 8) & 0xff); - buf[4] = static_cast(n & 0xff); - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::onScreenSaver(bool show) -{ - char buf[2]; - memcpy(buf, "\017", 1); - buf[1] = show ? 1 : 0; - m_socket->write(buf, sizeof(buf)); -} - -void CScreenProxy::onClipboardChanged() -{ - // FIXME -} - -CString CScreenProxy::getName() const -{ - return m_name; -} - -void CScreenProxy::getSize(SInt32* w, SInt32* h) const -{ - assert(w != NULL); - assert(h != NULL); - - *w = m_w; - *h = m_h; -} - -void CScreenProxy::getClipboard(IClipboard*) const -{ - // FIXME -} - -void CScreenProxy::onRead() -{ - char buf[512]; - SInt32 n = m_socket->read(buf, sizeof(buf)); - if (n == -1) { - // FIXME -- disconnect - TRACE(("hangup")); - } - else if (n > 0) { - switch (buf[0]) { - case '\201': - m_w = static_cast( - (static_cast(buf[1]) << 24) + - (static_cast(buf[2]) << 16) + - (static_cast(buf[3]) << 8) + - (static_cast(buf[4]) )); - m_h = static_cast( - (static_cast(buf[5]) << 24) + - (static_cast(buf[6]) << 16) + - (static_cast(buf[7]) << 8) + - (static_cast(buf[8]) )); - TRACE(("new size: %dx%d", m_w, m_h)); - break; - - default: - TRACE(("unknown message: 0x%02x, %d bytes", buf[0], n)); - break; - } - } -} diff --git a/CScreenProxy.h b/CScreenProxy.h deleted file mode 100644 index bbcb7a1d..00000000 --- a/CScreenProxy.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef CSCREENPROXY_H -#define CSCREENPROXY_H - -#include "IScreen.h" - -class ISocket; - -class CScreenProxy : public IScreen { - public: - CScreenProxy(const CString& name, ISocket*); - virtual ~CScreenProxy(); - - // IScreen overrides - virtual void open(bool); - virtual void close(); - virtual void enterScreen(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void leaveScreen(); - virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void setClipboard(const IClipboard*); - virtual void onScreenSaver(bool show); - virtual void onKeyDown(KeyID, KeyModifierMask); - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void onKeyUp(KeyID, KeyModifierMask); - virtual void onMouseDown(ButtonID); - virtual void onMouseUp(ButtonID); - virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void onMouseWheel(SInt32 delta); - virtual void onClipboardChanged(); - virtual CString getName() const; - virtual void getSize(SInt32* width, SInt32* height) const; - virtual void getClipboard(IClipboard*) const; - - private: - void onRead(); - - private: - CString m_name; - ISocket* m_socket; - SInt32 m_w, m_h; -}; - -#endif diff --git a/CServer.cpp b/CServer.cpp deleted file mode 100644 index 80bf026c..00000000 --- a/CServer.cpp +++ /dev/null @@ -1,812 +0,0 @@ -#include "CServer.h" -#include "CEvent.h" -#include "IEventQueue.h" -#include "IScreen.h" -#include "CScreenProxy.h" -#include "ISocket.h" -#include "CSocketFactory.h" -#include "CMessageSocket.h" -#include "TMethodJob.h" -#include "CTrace.h" -#include -#include -#include - -#if !defined(NDEBUG) -static const char* s_dirName[] = { "left", "right", "top", "bottom" }; -#endif - -// -// CServerSocketJob -// - -class CServerSocketJob : public IJob { - public: - typedef void (CServer::*ServerMethod)(ISocket*); - - CServerSocketJob(CServer*, ServerMethod, ISocket*); - virtual ~CServerSocketJob(); - - // IJob overrides - virtual void run(); - - private: - CServer* m_server; - ServerMethod m_method; - ISocket* m_socket; -}; - -CServerSocketJob::CServerSocketJob(CServer* server, - ServerMethod method, ISocket* socket) : - m_server(server), - m_method(method), - m_socket(socket) -{ - // do nothing -} - -CServerSocketJob::~CServerSocketJob() -{ - // do nothing -} - -void CServerSocketJob::run() -{ - (m_server->*m_method)(m_socket); -} - - -// -// CServer -// - -class XServerScreenExists { // FIXME - public: - XServerScreenExists(const CString&) { } -}; - -// the width/height of the zone on the edge of the local screen that -// will provoke a switch to a neighboring screen. this generally -// shouldn't be changed because it effectively reduces the size of -// the local screen's screen. -// FIXME -- should get this from the local screen itself. it may -// need a slightly larger zone (to avoid virtual screens) or it may -// be able to generate off-screen coordinates to provoke the switch -// in which case the size can be zero. -const SInt32 CServer::s_zoneSize = 1; - -CServer::CServer() : m_running(false), m_done(false), - m_localScreen(NULL), - m_activeScreen(NULL), - m_listenHost(), - // FIXME -- define kDefaultPort - m_listenPort(40001/*CProtocol::kDefaultPort*/), - m_listenSocket(NULL) -{ - // FIXME -} - -CServer::~CServer() -{ - assert(m_listenSocket == NULL); - - // FIXME -} - -void CServer::setListenPort( - const CString& hostname, UInt16 port) -{ - m_listenHost = hostname; - m_listenPort = port; -} - -void CServer::addLocalScreen(IScreen* screen) -{ - assert(screen != NULL); - assert(m_running == false); - assert(m_localScreen == NULL); - - addScreen(screen->getName(), screen); - m_localScreen = screen; - m_activeScreen = screen; - - // open the screen as primary - screen->open(true); -} - -void CServer::addRemoteScreen(const CString& name) -{ - addScreen(name, NULL); -} - -void CServer::addScreen(const CString& name, IScreen* screen) -{ - assert(!name.empty()); - - // cannot add a screen multiple times - if (m_map.count(name) != 0) - throw XServerScreenExists(name); - - // add entry for screen in the map - ScreenCell& cell = m_map[name]; - - // set the cell's screen - cell.m_screen = screen; -} - -void CServer::removeScreen(const CString& name) -{ - // screen must in map - assert(!name.empty()); - assert(m_map.count(name) == 1); - - // look up cell - ScreenCell& cell = m_map[name]; - - // if this is the local screen then there must not be any other - // screens and we must not be running. - assert(cell.m_screen != m_localScreen || (m_map.size() == 1 && !m_running)); - - // if this is the active screen then warp to the local screen, or - // set no active screen if this is the local screen. - if (cell.m_screen == m_localScreen) { - m_activeScreen = NULL; - m_localScreen = NULL; - } - else if (cell.m_screen == m_activeScreen) { - setActiveScreen(m_localScreen); - } - - // close the screen - if (cell.m_screen) - cell.m_screen->close(); - - // fix up map - if (!cell.m_neighbor[kLeft].empty()) { - assert(m_map.count(cell.m_neighbor[kLeft]) == 1); - m_map[cell.m_neighbor[kLeft]].m_neighbor[kRight] = - cell.m_neighbor[kRight]; - } - if (!cell.m_neighbor[kRight].empty()) { - assert(m_map.count(cell.m_neighbor[kRight]) == 1); - m_map[cell.m_neighbor[kRight]].m_neighbor[kLeft] = - cell.m_neighbor[kLeft]; - } - if (!cell.m_neighbor[kTop].empty()) { - assert(m_map.count(cell.m_neighbor[kTop]) == 1); - m_map[cell.m_neighbor[kTop]].m_neighbor[kBottom] = - cell.m_neighbor[kBottom]; - } - if (!cell.m_neighbor[kBottom].empty()) { - assert(m_map.count(cell.m_neighbor[kBottom]) == 1); - m_map[cell.m_neighbor[kBottom]].m_neighbor[kTop] = - cell.m_neighbor[kTop]; - } -} - -void CServer::connectEdge( - const CString& src, EDirection srcSide, - const CString& dst) -{ - // check input - assert(!src.empty()); - assert(!dst.empty()); - assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); - - // both screens must exist in map - assert(m_map.count(src) == 1); - assert(m_map.count(dst) == 1); - - // look up map entry - ScreenCell& cell = m_map[src]; - - // set edge - cell.m_neighbor[srcSide] = dst; - - TRACE(("connect %s:%s to %s", src.c_str(), - s_dirName[srcSide], - cell.m_neighbor[srcSide].c_str())); -} - -void CServer::disconnectEdge( - const CString& src, EDirection srcSide) -{ - // check input - assert(!src.empty()); - assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); - assert(m_map.count(src) == 1); - - TRACE(("disconnect %s:%s from %s", src.c_str(), - s_dirName[srcSide], - m_map[src].m_neighbor[srcSide].c_str())); - - // look up map entry - ScreenCell& cell = m_map[src]; - - // set edge - cell.m_neighbor[srcSide] = CString(); -} - -void CServer::run() -{ - assert(m_running == false); - assert(m_activeScreen != NULL); - assert(m_activeScreen == m_localScreen); - - // prepare socket to listen for remote screens - // FIXME -- need m_socketFactory (creates sockets of desired type) -// m_listenSocket = m_socketFactory->createSocket(); -m_listenSocket = CSOCKETFACTORY->create(); - m_listenSocket->setReadJob(new TMethodJob(this, - &CServer::newConnectionCB)); - // FIXME -- keep retrying until this works (in case of FIN_WAIT). - // also, must clean up m_listenSocket if this method throws anywhere. - m_listenSocket->listen(m_listenHost, m_listenPort); - - // now running - m_running = true; - - // event loop - IEventQueue* queue = CEQ; - while (!m_done) { - // wait for new connections, network messages, and user events - queue->wait(-1.0); - - // handle events - while (!queue->isEmpty()) { - // get the next event - CEvent event; - queue->pop(&event); - - // handle it - switch (event.m_any.m_type) { - case CEventBase::kNull: - // do nothing - break; - - case CEventBase::kKeyDown: - case CEventBase::kKeyRepeat: - case CEventBase::kKeyUp: - if (!onCommandKey(&event.m_key)) - relayEvent(&event); - break; - - case CEventBase::kMouseDown: - case CEventBase::kMouseUp: - case CEventBase::kMouseWheel: - relayEvent(&event); - break; - - case CEventBase::kMouseMove: - if (m_localScreen == m_activeScreen) - onLocalMouseMove(event.m_mouse.m_x, event.m_mouse.m_y); - else - onRemoteMouseMove(event.m_mouse.m_x, event.m_mouse.m_y); - break; - - case CEventBase::kScreenSize: - // FIXME - break; - } - } - } - - // reset - m_running = false; - m_done = false; - - // tell screens to shutdown - // FIXME - - // close our socket - delete m_listenSocket; - m_listenSocket = NULL; -} - -void CServer::onClipboardChanged(IScreen*) -{ - // FIXME -- should take screen name not screen pointer - // FIXME -} - -void CServer::setActiveScreen(IScreen* screen) -{ - // FIXME -- should take screen name not screen pointer - assert(screen != NULL); - assert(m_map.count(screen->getName()) == 1); - - // ignore if no change - if (m_activeScreen == screen) - return; - - // get center of screen - SInt32 w, h; - screen->getSize(&w, &h); - w >>= 1; - h >>= 1; - - // switch - switchScreen(screen, w, h); -} - -IScreen* CServer::getActiveScreen() const -{ - return m_activeScreen; -} - -void CServer::relayEvent(const CEvent* event) -{ - assert(event != NULL); - assert(m_activeScreen != NULL); - - // ignore attempts to relay to the local screen - if (m_activeScreen == m_localScreen) - return; - - // relay the event - switch (event->m_any.m_type) { - case CEventBase::kNull: - // do nothing - break; - - case CEventBase::kKeyDown: - m_activeScreen->onKeyDown(event->m_key.m_key, event->m_key.m_mask); - break; - - case CEventBase::kKeyRepeat: - m_activeScreen->onKeyRepeat(event->m_key.m_key, - event->m_key.m_mask, event->m_key.m_count); - break; - - case CEventBase::kKeyUp: - m_activeScreen->onKeyUp(event->m_key.m_key, event->m_key.m_mask); - break; - - case CEventBase::kMouseDown: - m_activeScreen->onMouseDown(event->m_mouse.m_button); - break; - - case CEventBase::kMouseUp: - m_activeScreen->onMouseUp(event->m_mouse.m_button); - break; - - case CEventBase::kMouseWheel: - m_activeScreen->onMouseWheel(event->m_mouse.m_x); - break; - - case CEventBase::kMouseMove: - assert(0 && "kMouseMove relayed"); - break; - - default: - assert(0 && "invalid event relayed"); - break; - } -} - -bool CServer::onCommandKey(const CEventKey* /*keyEvent*/) -{ - // FIXME -- strip out command keys (e.g. lock to screen, warp, etc.) - return false; -} - -void CServer::onLocalMouseMove(SInt32 x, SInt32 y) -{ - assert(m_activeScreen == m_localScreen); - - // ignore if locked to screen - if (isLockedToScreen()) - return; - - // get local screen's size - SInt32 w, h; - m_activeScreen->getSize(&w, &h); - - // see if we should change screens - EDirection dir; - if (x < s_zoneSize) { - x -= s_zoneSize; - dir = kLeft; - } - else if (x >= w - s_zoneSize) { - x += s_zoneSize; - dir = kRight; - } - else if (y < s_zoneSize) { - y -= s_zoneSize; - dir = kTop; - } - else if (y >= h - s_zoneSize) { - y += s_zoneSize; - dir = kBottom; - } - else { - // still on local screen - return; - } - TRACE(("leave %s on %s", m_activeScreen->getName().c_str(), s_dirName[dir])); - - // get new screen. if no screen in that direction then ignore move. - IScreen* newScreen = getNeighbor(m_activeScreen, dir, x, y); - if (newScreen == NULL) - return; - - // remap position to account for resolution differences between screens - mapPosition(m_activeScreen, dir, newScreen, x, y); - - // switch screen - switchScreen(newScreen, x, y); -} - -void CServer::onRemoteMouseMove(SInt32 dx, SInt32 dy) -{ - assert(m_activeScreen != NULL); - assert(m_activeScreen != m_localScreen); - - // put mouse back in center of local screen's grab area -// XXX m_localScreen->warpToCenter(); - - // save old position - const SInt32 xOld = m_x; - const SInt32 yOld = m_y; - - // accumulate mouse position - m_x += dx; - m_y += dy; - - // get active screen's size - SInt32 w, h; - m_activeScreen->getSize(&w, &h); - - // switch screens if mouse is outside screen and not locked to screen - IScreen* newScreen = NULL; - if (!isLockedToScreen()) { - // find direction of neighbor - EDirection dir; - if (m_x < 0) - dir = kLeft; - else if (m_x > w - 1) - dir = kRight; - else if (m_y < 0) - dir = kTop; - else if (m_y > h - 1) - dir = kBottom; - else - newScreen = m_activeScreen; - - // get neighbor if we should switch - if (newScreen == NULL) { - TRACE(("leave %s on %s", m_activeScreen->getName().c_str(), - s_dirName[dir])); - - SInt32 x = m_x, y = m_y; - newScreen = getNeighbor(m_activeScreen, dir, x, y); - - // remap position to account for resolution differences - if (newScreen != NULL) { - m_x = x; - m_y = y; - mapPosition(m_activeScreen, dir, newScreen, m_x, m_y); - } - else { - if (m_x < 0) - m_x = 0; - else if (m_x > w - 1) - m_x = w - 1; - if (m_y < 0) - m_y = 0; - else if (m_y > h - 1) - m_y = h - 1; - } - } - } - - // clamp mouse position if locked to screen - else { - TRACE(("clamp to %s", m_activeScreen->getName().c_str())); - - if (m_x < 0) - m_x = 0; - else if (m_x > w - 1) - m_x = w - 1; - if (m_y < 0) - m_y = 0; - else if (m_y > h - 1) - m_y = h - 1; - } - - // if on same screen then warp cursor - if (newScreen == NULL || newScreen == m_activeScreen) { - // ignore if clamped mouse didn't move - if (m_x != xOld || m_y != yOld) { - TRACE(("move on %s to %d,%d", - m_activeScreen->getName().c_str(), m_x, m_y)); - m_activeScreen->onMouseMove(m_x, m_y); - } - } - - // otherwise switch the screen - else { - switchScreen(newScreen, m_x, m_y); - } -} - -bool CServer::isLockedToScreen() const -{ - // FIXME - return false; -} - -void CServer::mapPosition( - const IScreen* src, EDirection srcSide, - const IScreen* dst, SInt32& x, SInt32& y) const -{ - assert(src != NULL); - assert(dst != NULL); - assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); - - // get sizes - SInt32 wSrc, hSrc, wDst, hDst; - src->getSize(&wSrc, &hSrc); - dst->getSize(&wDst, &hDst); - - // remap - switch (srcSide) { - case kLeft: - case kRight: - assert(y >= 0 && y < hSrc); - y = static_cast(0.5 + y * - static_cast(hDst - 1) / (hSrc - 1)); - break; - - case kTop: - case kBottom: - assert(x >= 0 && x < wSrc); - x = static_cast(0.5 + x * - static_cast(wSrc - 1) / (wSrc - 1)); - break; - } -} - -IScreen* CServer::getNeighbor( - const IScreen* src, EDirection dir) const -{ - // check input - assert(src != NULL); - assert(dir >= kFirstDirection && dir <= kLastDirection); - assert(m_map.count(src->getName()) == 1); - - // look up source cell - ScreenMap::const_iterator index = m_map.find(src->getName()); - do { - // look up name of neighbor - const ScreenCell& cell = index->second; - const CString dstName(cell.m_neighbor[dir]); - - // if nothing in that direction then return NULL - if (dstName.empty()) - return NULL; - - // look up neighbor cell - assert(m_map.count(dstName) == 1); - index = m_map.find(dstName); - - // if no screen pointer then can't go to that neighbor so keep - // searching in the same direction. -#ifndef NDEBUG - if (index->second.m_screen == NULL) - TRACE(("skipping over unconnected screen %s", dstName.c_str())); -#endif - } while (index->second.m_screen == NULL); - - return index->second.m_screen; -} - -IScreen* CServer::getNeighbor( - const IScreen* src, EDirection srcSide, - SInt32& x, SInt32& y) const -{ - // given a position relative to src and which side of the screen we - // left, find the screen we should move onto and where. if the - // position is sufficiently far from src then we may cross multiple - // screens. - - // check input - assert(src != NULL); - assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); - - // get the first neighbor - IScreen* dst = getNeighbor(src, srcSide); - IScreen* lastGoodScreen = dst; - - // get the original screen's size (needed for kRight and kBottom) - SInt32 w, h; - src->getSize(&w, &h); - - // find destination screen, adjusting x or y (but not both) - switch (srcSide) { - case kLeft: - while (dst) { - lastGoodScreen = dst; - lastGoodScreen->getSize(&w, &h); - x += w; - if (x >= 0) - break; - TRACE(("skipping over screen %s", dst->getName().c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); - } - break; - - case kRight: - while (dst) { - lastGoodScreen = dst; - x -= w; - lastGoodScreen->getSize(&w, &h); - if (x < w) - break; - TRACE(("skipping over screen %s", dst->getName().c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); - } - break; - - case kTop: - while (dst) { - lastGoodScreen = dst; - lastGoodScreen->getSize(&w, &h); - y += h; - if (y >= 0) - break; - TRACE(("skipping over screen %s", dst->getName().c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); - } - break; - - case kBottom: - while (dst) { - lastGoodScreen = dst; - y -= h; - lastGoodScreen->getSize(&w, &h); - if (y < h) - break; - TRACE(("skipping over screen %s", dst->getName().c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); - } - break; - } - - // if entering local screen then be sure to move in far enough to - // avoid the switching zone. if entering a side that doesn't have - // a neighbor (i.e. an asymmetrical side) then we don't need to - // move inwards because that side can't provoke a switch. - if (lastGoodScreen == m_localScreen) { - ScreenMap::const_iterator index = m_map.find(m_localScreen->getName()); - const ScreenCell& cell = index->second; - switch (srcSide) { - case kLeft: - if (!cell.m_neighbor[kRight].empty() && x > w - 1 - s_zoneSize) - x = w - 1 - s_zoneSize; - break; - - case kRight: - if (!cell.m_neighbor[kLeft].empty() && x < s_zoneSize) - x = s_zoneSize; - break; - - case kTop: - if (!cell.m_neighbor[kBottom].empty() && y > h - 1 - s_zoneSize) - y = h - 1 - s_zoneSize; - break; - - case kBottom: - if (!cell.m_neighbor[kTop].empty() && y < s_zoneSize) - y = s_zoneSize; - break; - } - } - - return lastGoodScreen; -} - -void CServer::switchScreen( - IScreen* screen, SInt32 x, SInt32 y) -{ - assert(screen != NULL); - assert(m_running == true); - assert(m_activeScreen != NULL); -#ifndef NDEBUG - { - SInt32 w, h; - screen->getSize(&w, &h); - assert(x >= 0 && y >= 0 && x < w && y < h); - } -#endif - - TRACE(("switch %s to %s at %d,%d", m_activeScreen->getName().c_str(), - screen->getName().c_str(), x, y)); - - // wrapping means leaving the active screen and entering it again. - // since that's a waste of time we skip that and just warp the - // mouse. - if (m_activeScreen != screen) { - // leave active screen - m_activeScreen->leaveScreen(); - - // cut over - m_activeScreen = screen; - - // enter new screen - m_activeScreen->enterScreen(x, y); - } - else { - m_activeScreen->warpCursor(x, y); - } - - // record new position - m_x = x; - m_y = y; -} - -void CServer::newConnectionCB() -{ - ISocket* socket = m_listenSocket->accept(); - TRACE(("accepted socket %p", socket)); - socket->setReadJob(new CServerSocketJob(this, &CServer::loginCB, socket)); - m_logins.insert(socket); -} - -void CServer::loginCB(ISocket* socket) -{ - // FIXME -- no fixed size buffers - UInt8 buffer[512]; - SInt32 n = socket->read(buffer, sizeof(buffer)); - if (n == -1) { - TRACE(("socket %p disconnected", socket)); - goto fail; - } - TRACE(("read %d bytes from socket %p", n, socket)); - if (n <= 10) { - TRACE(("socket %p: bogus %d byte message; hanging up", socket, n)); - goto fail; - } - if (n > 10) { - if (::memcmp(buffer, "SYNERGY\000\001", 9) != 0) { - TRACE(("socket %p: bad login", socket)); - goto fail; - } - - const SInt32 nameLen = static_cast(buffer[9]); - if (nameLen < 1 || nameLen > 64) { - TRACE(("socket %p: bad login name length %d", socket, nameLen)); - goto fail; - } - - for (SInt32 i = 0; i < nameLen; ++i) - if (!isalnum(buffer[10 + i])) { - TRACE(("socket %p: bad login name", socket)); - goto fail; - } - - CString name(reinterpret_cast(buffer + 10), nameLen); - const ScreenMap::iterator index = m_map.find(name); - if (index == m_map.end()) { - TRACE(("socket %p: unknown screen %s", socket, name.c_str())); - goto fail; - } - if (index->second.m_screen != NULL) { - TRACE(("socket %p: screen %s already connected", - socket, name.c_str())); - goto fail; - } - - TRACE(("socket %p: login %s", socket, name.c_str())); - CScreenProxy* screen = new CScreenProxy(name, socket); - m_logins.erase(socket); - index->second.m_screen = screen; - index->second.m_screen->open(false); - } - return; - -fail: - m_logins.erase(socket); - delete socket; -} diff --git a/CServer.h b/CServer.h deleted file mode 100644 index b95fa7ef..00000000 --- a/CServer.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef CSERVER_H -#define CSERVER_H - -#include "IServer.h" -#include "BasicTypes.h" -#include "CString.h" -#include -#include - -class CEvent; -class CEventKey; -class IScreen; -class ISocket; - -class CServer : public IServer { - public: - enum EDirection { kLeft, kRight, kTop, kBottom, - kFirstDirection = kLeft, kLastDirection = kBottom }; - - CServer(); - virtual ~CServer(); - - // manipulators - - // set the server's interface and port to listen for remote screens - void setListenPort(const CString& hostname, UInt16 port); - - // add local screen - void addLocalScreen(IScreen*); - - // add a remote screen - void addRemoteScreen(const CString& name); - - // remove a local or remote screen. neighbors on opposite sides - // of this screen are made neighbors of each other. - void removeScreen(const CString& name); - - // connect/disconnect screen edges - void connectEdge(const CString& src, EDirection srcSide, - const CString& dst); - void disconnectEdge(const CString& src, EDirection srcSide); - - // accessors - - - // IServer overrides - virtual void run(); - virtual void onClipboardChanged(IScreen*); - virtual void setActiveScreen(IScreen*); - virtual IScreen* getActiveScreen() const; - - protected: - virtual void relayEvent(const CEvent* event); - virtual bool onCommandKey(const CEventKey* keyEvent); - virtual void onLocalMouseMove(SInt32 x, SInt32 y); - virtual void onRemoteMouseMove(SInt32 dx, SInt32 dy); - virtual bool isLockedToScreen() const; - virtual void mapPosition(const IScreen* src, EDirection srcSide, - const IScreen* dst, SInt32& x, SInt32& y) const; - IScreen* getNeighbor(const IScreen* src, EDirection) const; - IScreen* getNeighbor(const IScreen* src, EDirection srcSide, - SInt32& x, SInt32& y) const; - void switchScreen(IScreen* screen, SInt32 x, SInt32 y); - - private: - void addScreen(const CString&, IScreen*); - void newConnectionCB(); - void loginCB(ISocket*); - - struct ScreenCell { - public: - ScreenCell() : m_screen(NULL) { } - public: - IScreen* m_screen; - CString m_neighbor[kLastDirection - kFirstDirection + 1]; - }; - - private: - typedef std::map ScreenMap; - typedef std::set SocketSet; - - // main loop stuff - bool m_running; - bool m_done; - - // screen tracking - IScreen* m_localScreen; - IScreen* m_activeScreen; - SInt32 m_x, m_y; - ScreenMap m_map; - - // listen socket stuff - CString m_listenHost; - UInt16 m_listenPort; - ISocket* m_listenSocket; - - // login sockets - SocketSet m_logins; - - static const SInt32 s_zoneSize; -}; - -#endif diff --git a/CSocket.cpp b/CSocket.cpp deleted file mode 100644 index 311d87d0..00000000 --- a/CSocket.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "CSocket.h" -#include "IJob.h" - -// -// CSocket -// - -CSocket::CSocket() : m_readJob(NULL), m_writeJob(NULL) -{ - // do nothing -} - -CSocket::~CSocket() -{ - delete m_readJob; - delete m_writeJob; -} - -void CSocket::setReadJob(IJob* adoptedJob) -{ - delete m_readJob; - m_readJob = adoptedJob; - onJobChanged(); -} - -void CSocket::setWriteJob(IJob* adoptedJob) -{ - delete m_writeJob; - m_writeJob = adoptedJob; - onJobChanged(); -} - -void CSocket::onJobChanged() -{ - // do nothing -} - -void CSocket::runReadJob() -{ - if (m_readJob) - m_readJob->run(); -} - -void CSocket::runWriteJob() -{ - if (m_writeJob) - m_writeJob->run(); -} - -bool CSocket::hasReadJob() const -{ - return (m_readJob != NULL); -} - -bool CSocket::hasWriteJob() const -{ - return (m_writeJob != NULL); -} diff --git a/CSocket.h b/CSocket.h deleted file mode 100644 index aa4cf9f8..00000000 --- a/CSocket.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CSOCKET_H -#define CSOCKET_H - -#include "ISocket.h" - -class IJob; - -class CSocket : public ISocket { - public: - CSocket(); - virtual ~CSocket(); - - // ISocket overrides - virtual void setReadJob(IJob* adoptedJob); - virtual void setWriteJob(IJob* adoptedJob); - virtual void connect(const CString& hostname, UInt16 port) = 0; - virtual void listen(const CString& hostname, UInt16 port) = 0; - virtual ISocket* accept() = 0; - virtual SInt32 read(void* buffer, SInt32 numBytes) = 0; - virtual void write(const void* buffer, SInt32 numBytes) = 0; - - protected: - // called when the read or write job is changed. default does nothing. - virtual void onJobChanged(); - - // subclasses should call these at the appropriate time. different - // platforms will arrange to detect these situations differently. - // does nothing if the respective job is NULL. - void runReadJob(); - void runWriteJob(); - - // return true iff the socket has a read job or a write job - bool hasReadJob() const; - bool hasWriteJob() const; - - private: - IJob* m_readJob; - IJob* m_writeJob; -}; - -#endif diff --git a/CSocketFactory.cpp b/CSocketFactory.cpp deleted file mode 100644 index 0995b897..00000000 --- a/CSocketFactory.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "CSocketFactory.h" -#include "BasicTypes.h" -#include - -// -// CSocketFactory -// - -CSocketFactory* CSocketFactory::s_instance = NULL; - -CSocketFactory::CSocketFactory() -{ - // do nothing -} - -CSocketFactory::~CSocketFactory() -{ - // do nothing -} - -void CSocketFactory::setInstance(CSocketFactory* factory) -{ - delete s_instance; - s_instance = factory; -} - -CSocketFactory* CSocketFactory::getInstance() -{ - assert(s_instance != NULL); - return s_instance; -} diff --git a/CSocketFactory.h b/CSocketFactory.h deleted file mode 100644 index 7a73d391..00000000 --- a/CSocketFactory.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef CSOCKETFACTORY_H -#define CSOCKETFACTORY_H - -#define CSOCKETFACTORY CSocketFactory::getInstance() - -class ISocket; - -class CSocketFactory { - public: - CSocketFactory(); - virtual ~CSocketFactory(); - - // manipulators - - static void setInstance(CSocketFactory*); - - // accessors - - // create a socket - virtual ISocket* create() const = 0; - - // get the global instance - static CSocketFactory* getInstance(); - - private: - static CSocketFactory* s_instance; -}; - -#endif diff --git a/CString.h b/CString.h deleted file mode 100644 index 9b5667ac..00000000 --- a/CString.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef CSTRING_H -#define CSTRING_H - -#include - -#ifndef CSTRING_DEF_CTOR -#define CSTRING_ALLOC1 -#define CSTRING_ALLOC2 -#define CSTRING_DEF_CTOR CString() : _Myt() { } -#endif - -// use to get appropriate type for string constants. it depends on -// the internal representation type of CString. -#define _CS(_x) _x - -class CString : public std::string { - public: - typedef char _E; - typedef _E CharT; - typedef std::allocator<_E> _A; - typedef std::string _Myt; - typedef const_iterator _It; - - // same constructors as base class - CSTRING_DEF_CTOR - CString(const _Myt& _X) : _Myt(_X) { } - CString(const _Myt& _X, size_type _P, size_type _M CSTRING_ALLOC1) : - _Myt(_X, _P, _M CSTRING_ALLOC2) { } - CString(const _E *_S, size_type _N CSTRING_ALLOC1) : - _Myt(_S, _N CSTRING_ALLOC2) { } - CString(const _E *_S CSTRING_ALLOC1) : - _Myt(_S CSTRING_ALLOC2) { } - CString(size_type _N, _E _C CSTRING_ALLOC1) : - _Myt(_N, _C CSTRING_ALLOC2) { } - CString(_It _F, _It _L CSTRING_ALLOC1) : - _Myt(_F, _L CSTRING_ALLOC2) { } -}; - -#endif - diff --git a/CTrace.cpp b/CTrace.cpp deleted file mode 100644 index e782616a..00000000 --- a/CTrace.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "CTrace.h" -#include -#include - -// -// CTrace -// - -void CTrace::print(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - fprintf(stderr, "\n"); -} diff --git a/CTrace.h b/CTrace.h deleted file mode 100644 index 34787ac9..00000000 --- a/CTrace.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef CTRACE_H -#define CTRACE_H - -class CTrace { - public: - static void print(const char* fmt, ...); -}; - -#if defined(NDEBUG) - -#define TRACE(_X) - -#else // NDEBUG - -#define TRACE(_X) CTrace::print ## _X - -#endif // NDEBUG - -#endif diff --git a/CUnixEventQueue.cpp b/CUnixEventQueue.cpp deleted file mode 100644 index db685dca..00000000 --- a/CUnixEventQueue.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include "CUnixEventQueue.h" -#include "IJob.h" -#include -#include -#include - -// -// CUnixEventQueue -// - -CUnixEventQueue::CUnixEventQueue() -{ - // do nothing -} - -CUnixEventQueue::~CUnixEventQueue() -{ - // clean up lists - clearList(m_readList); - clearList(m_writeList); -} - -void CUnixEventQueue::addFileDesc(int fd, - IJob* readJob, IJob* writeJob) -{ - assert(fd != -1); - assert(m_readList.count(fd) == 0 && m_writeList.count(fd) == 0); - assert(readJob != writeJob || readJob == NULL); - - if (readJob) - m_readList[fd] = readJob; - if (writeJob) - m_writeList[fd] = writeJob; -} - -void CUnixEventQueue::removeFileDesc(int fd) -{ - assert(fd != -1); - - // remove from lists - eraseList(m_readList, fd); - eraseList(m_writeList, fd); -} - -void CUnixEventQueue::wait(double timeout) -{ - // prepare sets - fd_set fdRead, fdWrite; - const int maxRead = prepList(m_readList, &fdRead); - const int maxWrite = prepList(m_writeList, &fdWrite); - - // compute the larger of maxRead and maxWrite - const int fdMax = (maxRead > maxWrite) ? maxRead : maxWrite; - if (fdMax == -1) - return; - - // prepare timeout - struct timeval* pTimeout = NULL; - struct timeval sTimeout; - if (timeout >= 0.0) { - sTimeout.tv_sec = static_cast(timeout); - sTimeout.tv_usec = static_cast(1000000.0 * - (timeout - sTimeout.tv_sec)); - pTimeout = &sTimeout; - } - - // wait - const int n = ::select(fdMax + 1, &fdRead, &fdWrite, NULL, pTimeout); - - // return on error or if nothing to do - if (n <= 0) - return; - - // invoke jobs - // note -- calling removeFileDesc() from a job is likely to crash the - // program because we expect all jobs with active file descriptors to - // persist for the duration of these loops. - int fd; - for (fd = 0; fd <= maxRead; ++fd) - if (FD_ISSET(fd, &fdRead)) { - assert(m_readList.count(fd) > 0); - assert(m_readList[fd] != NULL); - m_readList[fd]->run(); - } - for (fd = 0; fd <= maxWrite; ++fd) - if (FD_ISSET(fd, &fdWrite)) { - assert(m_writeList.count(fd) > 0); - assert(m_writeList[fd] != NULL); - m_writeList[fd]->run(); - } -} - -void CUnixEventQueue::lock() -{ - // do nothing -} - -void CUnixEventQueue::unlock() -{ - // do nothing -} - -void CUnixEventQueue::signalNotEmpty() -{ - // do nothing -} - -void CUnixEventQueue::eraseList(List& list, int fd) const -{ - List::iterator index = list.find(fd); - if (index != list.end()) { - delete index->second; - list.erase(index); - } -} - -void CUnixEventQueue::clearList(List& list) const -{ - for (List::const_iterator index = list.begin(); - index != list.end(); ++index) - delete index->second; - list.clear(); -} - -int CUnixEventQueue::prepList( - const List& list, void* vfdSet) const -{ - fd_set* fdSet = reinterpret_cast(vfdSet); - FD_ZERO(fdSet); - - int fdMax = -1; - for (List::const_iterator index = list.begin(); - index != list.end(); ++index) { - const int fd = index->first; - FD_SET(fd, fdSet); - if (fd > fdMax) - fdMax = fd; - } - - return fdMax; -} diff --git a/CUnixEventQueue.h b/CUnixEventQueue.h deleted file mode 100644 index 217efbf4..00000000 --- a/CUnixEventQueue.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef CUNIXEVENTQUEUE_H -#define CUNIXEVENTQUEUE_H - -#include "CEventQueue.h" -#include - -#undef CEQ -#define CEQ ((CUnixEventQueue*)CEventQueue::getInstance()) - -class IJob; - -class CUnixEventQueue : public CEventQueue { - public: - CUnixEventQueue(); - virtual ~CUnixEventQueue(); - - // manipulators - - // add a file descriptor to wait on. if adoptedReadJob is not NULL - // then it'll be called when the file descriptor is readable. if - // adoptedWriteJob is not NULL then it will be called then the file - // descriptor is writable. at least one job must not be NULL and - // the jobs may not be the same. ownership of the jobs is assumed. - // the file descriptor must not have already been added or, if it - // was, it must have been removed. - void addFileDesc(int fd, - IJob* adoptedReadJob, IJob* adoptedWriteJob); - - // remove a file descriptor from the list being waited on. the - // associated jobs are destroyed. the file descriptor must have - // been added and not since removed. - void removeFileDesc(int fd); - - // IEventQueue overrides - virtual void wait(double timeout); - - protected: - // CEventQueue overrides - virtual void lock(); - virtual void unlock(); - virtual void signalNotEmpty(); - - private: - typedef std::map List; - void eraseList(List&, int fd) const; - void clearList(List&) const; - int prepList(const List&, void* fdSet) const; - - private: - List m_readList; - List m_writeList; -}; - -#endif diff --git a/CUnixTCPSocket.cpp b/CUnixTCPSocket.cpp deleted file mode 100644 index ed5d2202..00000000 --- a/CUnixTCPSocket.cpp +++ /dev/null @@ -1,278 +0,0 @@ -#include "CUnixTCPSocket.h" -#include "CUnixEventQueue.h" -#include "CString.h" -#include "TMethodJob.h" -#include "XSocket.h" -#include -#include -#include -#include -#include // FIXME -- for disabling nagle algorithm -#include -#include -#include -#include -#include -extern int h_errno; - -CUnixTCPSocket::CUnixTCPSocket() : m_fd(-1), - m_state(kNone), - m_addedJobs(false) -{ - // create socket - m_fd = ::socket(PF_INET, SOCK_STREAM, 0); - if (m_fd == -1) - throw XSocketCreate(::strerror(errno)); - - // make it non-blocking - int mode = ::fcntl(m_fd, F_GETFL, 0); - if (mode == -1 || ::fcntl(m_fd, F_SETFL, mode | O_NONBLOCK) == -1) { - ::close(m_fd); - throw XSocketCreate(::strerror(errno)); - } - - // always send immediately - setNoDelay(); -} - -CUnixTCPSocket::CUnixTCPSocket(int fd) : m_fd(fd), - m_state(kConnected), - m_addedJobs(false) -{ - assert(m_fd != -1); - setNoDelay(); -} - -CUnixTCPSocket::~CUnixTCPSocket() -{ - assert(m_fd != -1); - - // unhook events - if (m_addedJobs) - CEQ->removeFileDesc(m_fd); - - // drain socket - if (m_state == kConnected) - ::shutdown(m_fd, 0); - - // close socket - ::close(m_fd); -} - -void CUnixTCPSocket::onJobChanged() -{ - // remove old jobs - if (m_addedJobs) { - CEQ->removeFileDesc(m_fd); - m_addedJobs = false; - } - - // which jobs should we install? - bool doRead = false; - bool doWrite = false; - switch (m_state) { - case kNone: - return; - - case kConnecting: - doWrite = true; - break; - - case kConnected: - doRead = hasReadJob(); - doWrite = hasWriteJob(); - break; - - case kListening: - doRead = true; - break; - } - - // make jobs - IJob* readJob = doRead ? new TMethodJob(this, - &CUnixTCPSocket::readCB) : NULL; - IJob* writeJob = doWrite ? new TMethodJob(this, - &CUnixTCPSocket::writeCB) : NULL; - - // install jobs - CEQ->addFileDesc(m_fd, readJob, writeJob); - m_addedJobs = true; -} - -void CUnixTCPSocket::readCB() -{ - runReadJob(); -} - -void CUnixTCPSocket::writeCB() -{ - if (m_state == kConnecting) { - // now connected. start watching for reads. - m_state = kConnected; - onJobChanged(); - } - runWriteJob(); -} - -void CUnixTCPSocket::connect( - const CString& hostname, UInt16 port) -{ - assert(m_fd != -1); - assert(m_state == kNone); - - // hostname to address - struct hostent* hent = ::gethostbyname(hostname.c_str()); - if (hent == NULL) - throw XSocketName(::hstrerror(h_errno)); - - // construct address - struct sockaddr_in addr; - assert(hent->h_addrtype == AF_INET); - assert(hent->h_length == sizeof(addr.sin_addr)); - addr.sin_family = hent->h_addrtype; - addr.sin_port = htons(port); - ::memcpy(&addr.sin_addr, hent->h_addr_list[0], hent->h_length); - - // start connecting - if (::connect(m_fd, reinterpret_cast(&addr), - sizeof(addr)) == -1) { - if (errno != EINPROGRESS) - throw XSocketConnect(::strerror(errno)); - m_state = kConnecting; - } - else { - m_state = kConnected; - runWriteJob(); - } - onJobChanged(); -} - -void CUnixTCPSocket::listen( - const CString& hostname, UInt16 port) -{ - assert(m_fd != -1); - assert(m_state == kNone); - assert(port != 0); - - // construct address - struct sockaddr_in addr; - if (!hostname.empty()) { - // hostname to address - struct hostent* hent = ::gethostbyname(hostname.c_str()); - if (hent == NULL) - throw XSocketName(::hstrerror(h_errno)); - - // fill in address - assert(hent->h_addrtype == AF_INET); - assert(hent->h_length == sizeof(addr.sin_addr)); - ::memcpy(&addr.sin_addr, hent->h_addr_list[0], hent->h_length); - } - else { - // all addresses - addr.sin_addr.s_addr = htonl(INADDR_ANY); - } - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - - // bind to address - if (::bind(m_fd, reinterpret_cast(&addr), - sizeof(addr)) == -1) - throw XSocketListen(::strerror(errno)); - - // start listening - if (::listen(m_fd, 3) == -1) - throw XSocketListen(::strerror(errno)); - m_state = kListening; - onJobChanged(); -} - -ISocket* CUnixTCPSocket::accept() -{ - assert(m_fd != -1); - assert(m_state == kListening); - - for (;;) { - // wait for connection - fd_set fdset; - FD_ZERO(&fdset); - FD_SET(m_fd, &fdset); - ::select(m_fd + 1, &fdset, NULL, NULL, NULL); - - // accept connection - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); - int fd = ::accept(m_fd, &addr, &addrlen); - if (fd == -1) - if (errno == EAGAIN) - continue; - else - throw XSocketAccept(::strerror(errno)); - - // return new socket object - return new CUnixTCPSocket(fd); - } -} - -SInt32 CUnixTCPSocket::read(void* buffer, SInt32 numBytes) -{ - assert(m_fd != -1); - assert(m_state == kConnected); - - const ssize_t n = ::read(m_fd, buffer, numBytes); - if (n == -1) { - // check for no data to read - if (errno == EAGAIN || errno == EINTR) - return 0; - - // error - return -1; - } - - // check for socket closed - if (n == 0) - return -1; - - // return num bytes read - return n; -} - -void CUnixTCPSocket::write( - const void* buffer, SInt32 numBytes) -{ - const char* ptr = static_cast(buffer); - - while (numBytes > 0) { - // write more data - const ssize_t n = ::write(m_fd, ptr, numBytes); - - // check for errors - if (n == -1) { - // wait if can't write data then try again - if (errno == EAGAIN || errno == EINTR) { - fd_set fdset; - FD_ZERO(&fdset); - FD_SET(m_fd, &fdset); - ::select(m_fd + 1, NULL, &fdset, NULL, NULL); - continue; - } - - // error - throw XSocketWrite(::strerror(errno)); - } - - // account for written data - ptr += n; - numBytes -= n; - } -} - -void CUnixTCPSocket::setNoDelay() -{ - // turn off Nagle algorithm. we send lots of really short messages - // so we'll accept the (much) larger overhead to reduce latency. - struct protoent* p = getprotobyname("tcp"); - if (p) { - int on = 1; - setsockopt(m_fd, p->p_proto, TCP_NODELAY, &on, sizeof(on)); - } -} diff --git a/CUnixTCPSocket.h b/CUnixTCPSocket.h deleted file mode 100644 index 0a04c5c4..00000000 --- a/CUnixTCPSocket.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef CUNIXTCPSOCKET_H -#define CUNIXTCPSOCKET_H - -#include "CSocket.h" -#include "CSocketFactory.h" - -class CUnixTCPSocket : public CSocket { - public: - CUnixTCPSocket(); - virtual ~CUnixTCPSocket(); - - // ISocket overrides - virtual void connect(const CString& hostname, UInt16 port); - virtual void listen(const CString& hostname, UInt16 port); - virtual ISocket* accept(); - virtual SInt32 read(void* buffer, SInt32 numBytes); - virtual void write(const void* buffer, SInt32 numBytes); - - protected: - // CSocket overrides - virtual void onJobChanged(); - - private: - CUnixTCPSocket(int); - - // disable Nagle algorithm - void setNoDelay(); - - // callbacks for read/write events - void readCB(); - void writeCB(); - - private: - enum EState { kNone, kConnecting, kConnected, kListening }; - int m_fd; - EState m_state; - bool m_addedJobs; -}; - -class CUnixTCPSocketFactory : public CSocketFactory { - public: - CUnixTCPSocketFactory() { } - virtual ~CUnixTCPSocketFactory() { } - - // CSocketFactory overrides - virtual ISocket* create() const - { return new CUnixTCPSocket; } -}; - -#endif diff --git a/CUnixXScreen.cpp b/CUnixXScreen.cpp deleted file mode 100644 index 752a774d..00000000 --- a/CUnixXScreen.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "CUnixXScreen.h" -#include "CUnixEventQueue.h" -#include "TMethodJob.h" -#include - -// -// CUnixXScreen -// - -CUnixXScreen::CUnixXScreen(const CString& name) : - CXScreen(name) -{ - // do nothing -} - -CUnixXScreen::~CUnixXScreen() -{ - // do nothing -} - -void CUnixXScreen::onOpen(bool) -{ - // register our X event handler - CEQ->addFileDesc(ConnectionNumber(getDisplay()), - new TMethodJob(this, - &CUnixXScreen::onEvents), NULL); - -} - -void CUnixXScreen::onClose() -{ - // unregister the X event handler - CEQ->removeFileDesc(ConnectionNumber(getDisplay())); -} diff --git a/CUnixXScreen.h b/CUnixXScreen.h deleted file mode 100644 index 459eed8c..00000000 --- a/CUnixXScreen.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef CUNIXXSCREEN_H -#define CUNIXXSCREEN_H - -#include "CXScreen.h" - -class CUnixXScreen : public CXScreen { - public: - CUnixXScreen(const CString& name); - virtual ~CUnixXScreen(); - - protected: - virtual void onOpen(bool isPrimary); - virtual void onClose(); -}; - -#endif diff --git a/CXScreen.cpp b/CXScreen.cpp deleted file mode 100644 index 049641c6..00000000 --- a/CXScreen.cpp +++ /dev/null @@ -1,626 +0,0 @@ -#include "CXScreen.h" -#include "CEvent.h" -#include "CEventQueue.h" -#include -#include -#include -#include - -// -// CXScreen -// -class XClientOpen { }; // FIXME - -CXScreen::CXScreen(const CString& name) : - m_name(name), - m_display(NULL), - m_primary(false), - m_w(0), m_h(0), - m_window(None), - m_active(false) -{ - // do nothing -} - -CXScreen::~CXScreen() -{ - assert(m_display == NULL); -} - -void CXScreen::open(bool isPrimary) -{ - assert(m_display == NULL); - - m_primary = isPrimary; - - bool opened = false; - try { - // open the display - m_display = ::XOpenDisplay(NULL); // FIXME -- allow non-default - if (m_display == NULL) - throw XClientOpen(); - - // hook up event handling - onOpen(m_primary); - opened = true; - - // get default screen - m_screen = DefaultScreen(m_display); - Screen* screen = ScreenOfDisplay(m_display, m_screen); - - // get screen size - m_w = WidthOfScreen(screen); - m_h = HeightOfScreen(screen); - - // type specific operations - if (m_primary) - openPrimary(); - else - openSecondary(); - } - catch (...) { - if (opened) - onClose(); - - if (m_display != NULL) { - ::XCloseDisplay(m_display); - m_display = NULL; - } - - throw; - } -} - -void CXScreen::close() -{ - assert(m_display != NULL); - - // type specific operations - if (m_primary) - closePrimary(); - else - closeSecondary(); - - // unhook event handling - onClose(); - - // close the display - ::XCloseDisplay(m_display); - m_display = NULL; -} - -void CXScreen::enterScreen(SInt32 x, SInt32 y) -{ - assert(m_display != NULL); - - if (m_primary) - enterScreenPrimary(x, y); - else - enterScreenSecondary(x, y); -} - -void CXScreen::leaveScreen() -{ - assert(m_display != NULL); - - if (m_primary) - leaveScreenPrimary(); - else - leaveScreenSecondary(); -} - -void CXScreen::warpCursor(SInt32 x, SInt32 y) -{ - assert(m_display != NULL); - - // warp the mouse - Window root = RootWindow(m_display, m_screen); - ::XWarpPointer(m_display, None, root, 0, 0, 0, 0, x, y); - ::XSync(m_display, False); - - // discard mouse events since we just added one we don't want - XEvent xevent; - while (::XCheckWindowEvent(m_display, m_window, - PointerMotionMask, &xevent)) - ; // do nothing -} - -void CXScreen::setClipboard( - const IClipboard* clipboard) -{ - assert(m_display != NULL); - - if (m_primary) - setClipboardPrimary(clipboard); - else - setClipboardSecondary(clipboard); -} - -void CXScreen::onScreenSaver(bool show) -{ - assert(m_display != NULL); - - if (m_primary) - onScreenSaverPrimary(show); - else - onScreenSaverSecondary(show); -} - -void CXScreen::onKeyDown(KeyID key, KeyModifierMask) -{ - assert(m_display != NULL); - assert(m_primary == false); - - // FIXME -- use mask - ::XTestFakeKeyEvent(m_display, mapKeyToX(key), True, CurrentTime); - ::XSync(m_display, False); -} - -void CXScreen::onKeyRepeat(KeyID, KeyModifierMask, SInt32) -{ - assert(m_display != NULL); - assert(m_primary == false); - - // FIXME -} - -void CXScreen::onKeyUp(KeyID key, KeyModifierMask) -{ - assert(m_display != NULL); - assert(m_primary == false); - - // FIXME -- use mask - ::XTestFakeKeyEvent(m_display, mapKeyToX(key), False, CurrentTime); - ::XSync(m_display, False); -} - -void CXScreen::onMouseDown(ButtonID button) -{ - assert(m_display != NULL); - assert(m_primary == false); - - ::XTestFakeButtonEvent(m_display, mapButtonToX(button), True, CurrentTime); - ::XSync(m_display, False); -} - -void CXScreen::onMouseUp(ButtonID button) -{ - assert(m_display != NULL); - assert(m_primary == false); - - ::XTestFakeButtonEvent(m_display, mapButtonToX(button), False, CurrentTime); - ::XSync(m_display, False); -} - -void CXScreen::onMouseMove(SInt32 x, SInt32 y) -{ - assert(m_display != NULL); - assert(m_primary == false); - - ::XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); - ::XSync(m_display, False); -} - -void CXScreen::onMouseWheel(SInt32) -{ - assert(m_display != NULL); - assert(m_primary == false); - - // FIXME -} - -void CXScreen::onClipboardChanged() -{ - assert(m_display != NULL); - assert(m_primary == false); - - // FIXME -} - -CString CXScreen::getName() const -{ - return m_name; -} - -void CXScreen::getSize( - SInt32* width, SInt32* height) const -{ - assert(m_display != NULL); - assert(width != NULL && height != NULL); - - *width = m_w; - *height = m_h; -} - -void CXScreen::getClipboard( - IClipboard* /*clipboard*/) const -{ - assert(m_display != NULL); - - // FIXME -} - -void CXScreen::openPrimary() -{ - // get the root window - Window root = RootWindow(m_display, m_screen); - - // create the grab window. this window is used to capture user - // input when the user is focussed on another client. don't let - // the window manager mess with it. - XSetWindowAttributes attr; - attr.event_mask = PointerMotionMask |// PointerMotionHintMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = None; - m_window = ::XCreateWindow(m_display, root, 0, 0, m_w, m_h, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - - // start watching for events on other windows - selectEvents(root); -} - -void CXScreen::closePrimary() -{ - assert(m_window != None); - - // destroy window - ::XDestroyWindow(m_display, m_window); - m_window = None; -} - -void CXScreen::enterScreenPrimary(SInt32 x, SInt32 y) -{ - assert(m_window != None); - assert(m_active == true); - - // warp to requested location - ::XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, x, y); - - // unmap the grab window. this also ungrabs the mouse and keyboard. - ::XUnmapWindow(m_display, m_window); - - // remove all input events for grab window - XEvent event; - while (::XCheckWindowEvent(m_display, m_window, - PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask, - &event)) - ; // do nothing - - // not active anymore - m_active = false; -} - -void CXScreen::leaveScreenPrimary() -{ - assert(m_window != None); - assert(m_active == false); - - // raise and show the input window - ::XMapRaised(m_display, m_window); - - // grab the mouse and keyboard. keep trying until we get them. - // if we can't grab one after grabbing the other then ungrab - // and wait before retrying. - int result; - do { - // mouse first - do { - result = ::XGrabPointer(m_display, m_window, True, 0, - GrabModeAsync, GrabModeAsync, - m_window, None, CurrentTime); - assert(result != GrabNotViewable); - if (result != GrabSuccess) - ::sleep(1); - } while (result != GrabSuccess); - - // now the keyboard - result = ::XGrabKeyboard(m_display, m_window, True, - GrabModeAsync, GrabModeAsync, CurrentTime); - assert(result != GrabNotViewable); - if (result != GrabSuccess) { - ::XUngrabPointer(m_display, CurrentTime); - ::sleep(1); - } - } while (result != GrabSuccess); - - // move the mouse to the center of grab window - warpCursor(m_w >> 1, m_h >> 1); - - // local client now active - m_active = true; -} - -void CXScreen::setClipboardPrimary( - const IClipboard* /*clipboard*/) -{ - // FIXME -} - -void CXScreen::onScreenSaverPrimary(bool /*show*/) -{ - // FIXME -} - -void CXScreen::openSecondary() -{ - // verify the availability of the XTest extension - int majorOpcode, firstEvent, firstError; - if (!::XQueryExtension(m_display, XTestExtensionName, - &majorOpcode, &firstEvent, &firstError)) - throw XClientOpen(); - - // become impervious to server grabs - XTestGrabControl(m_display, True); -} - -void CXScreen::closeSecondary() -{ - // no longer impervious to server grabs - XTestGrabControl(m_display, False); -} - -void CXScreen::enterScreenSecondary( - SInt32 x, SInt32 y) -{ - // FIXME -} - -void CXScreen::leaveScreenSecondary() -{ - // FIXME -} - -void CXScreen::setClipboardSecondary( - const IClipboard* /*clipboard*/) -{ - // FIXME -} - -void CXScreen::onScreenSaverSecondary(bool /*show*/) -{ - // FIXME -} - -Display* CXScreen::getDisplay() const -{ - return m_display; -} - -void CXScreen::onEvents() -{ - if (m_primary) - onPrimaryEvents(); - else - onSecondaryEvents(); -} - -void CXScreen::selectEvents(Window w) const -{ - // we want to track the mouse everywhere on the display. to achieve - // that we select PointerMotionMask on every window. we also select - // SubstructureNotifyMask in order to get CreateNotify events so we - // select events on new windows too. - - // we don't want to adjust our grab window - if (w == m_window) - return; - - // select events of interest - ::XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask); - - // recurse on child windows - Window rw, pw, *cw; - unsigned int nc; - if (::XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) { - for (unsigned int i = 0; i < nc; ++i) - selectEvents(cw[i]); - ::XFree(cw); - } -} - -KeyModifierMask CXScreen::mapModifierFromX(unsigned int state) const -{ - // FIXME -- should be configurable - KeyModifierMask mask = 0; - if (state & 1) - mask |= KeyModifierShift; - if (state & 2) - mask |= KeyModifierCapsLock; - if (state & 4) - mask |= KeyModifierControl; - if (state & 8) - mask |= KeyModifierAlt; - if (state & 16) - mask |= KeyModifierNumLock; - if (state & 32) - mask |= KeyModifierMeta; - if (state & 128) - mask |= KeyModifierScrollLock; - return mask; -} - -unsigned int CXScreen::mapModifierToX(KeyModifierMask mask) const -{ - // FIXME -- should be configurable - unsigned int state = 0; - if (mask & KeyModifierShift) - state |= 1; - if (mask & KeyModifierControl) - state |= 4; - if (mask & KeyModifierAlt) - state |= 8; - if (mask & KeyModifierMeta) - state |= 32; - if (mask & KeyModifierCapsLock) - state |= 2; - if (mask & KeyModifierNumLock) - state |= 16; - if (mask & KeyModifierScrollLock) - state |= 128; - return state; -} - -KeyID CXScreen::mapKeyFromX( - KeyCode keycode, KeyModifierMask mask) const -{ - int index; - if (mask & KeyModifierShift) - index = 1; - else - index = 0; - return static_cast(::XKeycodeToKeysym(m_display, keycode, index)); -} - -KeyCode CXScreen::mapKeyToX(KeyID keyID) const -{ - return ::XKeysymToKeycode(m_display, static_cast(keyID)); -} - -ButtonID CXScreen::mapButtonFromX(unsigned int button) const -{ - // FIXME -- should use button mapping? - if (button >= 1 && button <= 3) - return static_cast(button); - else - return kButtonNone; -} - -unsigned int CXScreen::mapButtonToX(ButtonID buttonID) const -{ - // FIXME -- should use button mapping? - return static_cast(buttonID); -} - -void CXScreen::onPrimaryEvents() -{ - while (XPending(m_display) > 0) { - XEvent xevent; - XNextEvent(m_display, &xevent); - - switch (xevent.type) { - case KeyPress: { - const KeyModifierMask mask = mapModifierFromX(xevent.xkey.state); - const KeyID key = mapKeyFromX(xevent.xkey.keycode, mask); - if (key != kKeyNone) { - CEvent event; - event.m_key.m_type = CEventBase::kKeyDown; - event.m_key.m_key = key; - event.m_key.m_mask = mask; - event.m_key.m_count = 0; - CEQ->push(&event); - } - break; - } - - // FIXME -- simulate key repeat. X sends press/release for - // repeat. must detect auto repeat and use kKeyRepeat. - case KeyRelease: { - const KeyModifierMask mask = mapModifierFromX(xevent.xkey.state); - const KeyID key = mapKeyFromX(xevent.xkey.keycode, mask); - if (key != kKeyNone) { - CEvent event; - event.m_key.m_type = CEventBase::kKeyUp; - event.m_key.m_key = key; - event.m_key.m_mask = mask; - event.m_key.m_count = 0; - CEQ->push(&event); - } - break; - } - - case ButtonPress: { - const ButtonID button = mapButtonFromX(xevent.xbutton.button); - if (button != kButtonNone) { - CEvent event; - event.m_mouse.m_type = CEventBase::kMouseDown; - event.m_mouse.m_button = button; - event.m_mouse.m_x = 0; - event.m_mouse.m_y = 0; - CEQ->push(&event); - } - break; - } - - case ButtonRelease: { - const ButtonID button = mapButtonFromX(xevent.xbutton.button); - if (button != kButtonNone) { - CEvent event; - event.m_mouse.m_type = CEventBase::kMouseUp; - event.m_mouse.m_button = button; - event.m_mouse.m_x = 0; - event.m_mouse.m_y = 0; - CEQ->push(&event); - } - break; - } - - case MotionNotify: { - CEvent event; - event.m_mouse.m_type = CEventBase::kMouseMove; - event.m_mouse.m_button = kButtonNone; - if (!m_active) { - event.m_mouse.m_x = xevent.xmotion.x_root; - event.m_mouse.m_y = xevent.xmotion.y_root; - } - else { - // FIXME -- slurp up all remaining motion events? - // probably not since key strokes may go to wrong place. - - // get mouse deltas - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int mask; - if (!::XQueryPointer(m_display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &mask)) - break; - event.m_mouse.m_x = xRoot - (m_w >> 1); - event.m_mouse.m_y = yRoot - (m_h >> 1); - - // warp mouse back to center - warpCursor(m_w >> 1, m_h >> 1); - } - CEQ->push(&event); - break; - } - - case CreateNotify: - // select events on new window - if (m_primary) - selectEvents(xevent.xcreatewindow.window); - break; - -/* - case SelectionClear: - target->XXX(xevent.xselectionclear.); - break; - - case SelectionNotify: - target->XXX(xevent.xselection.); - break; - - case SelectionRequest: - target->XXX(xevent.xselectionrequest.); - break; -*/ - } - } -} - -void CXScreen::onSecondaryEvents() -{ - while (XPending(m_display) > 0) { - XEvent xevent; - XNextEvent(m_display, &xevent); - // FIXME - } -} diff --git a/CXScreen.h b/CXScreen.h deleted file mode 100644 index 96a4ab28..00000000 --- a/CXScreen.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef CXSCREEN_H -#define CXSCREEN_H - -#include "IScreen.h" -#include - -class CXScreen : public IScreen { - public: - CXScreen(const CString& name); - virtual ~CXScreen(); - - // IScreen overrides - virtual void open(bool isPrimary); - virtual void close(); - virtual void enterScreen(SInt32 x, SInt32 y); - virtual void leaveScreen(); - virtual void warpCursor(SInt32 x, SInt32 y); - virtual void setClipboard(const IClipboard*); - virtual void onScreenSaver(bool); - virtual void onKeyDown(KeyID, KeyModifierMask); - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32); - virtual void onKeyUp(KeyID, KeyModifierMask); - virtual void onMouseDown(ButtonID); - virtual void onMouseUp(ButtonID); - virtual void onMouseMove(SInt32, SInt32); - virtual void onMouseWheel(SInt32); - virtual void onClipboardChanged(); - virtual CString getName() const; - virtual void getSize(SInt32* width, SInt32* height) const; - virtual void getClipboard(IClipboard*) const; - - protected: - // primary screen implementations - virtual void openPrimary(); - virtual void closePrimary(); - virtual void enterScreenPrimary(SInt32 x, SInt32 y); - virtual void leaveScreenPrimary(); - virtual void setClipboardPrimary(const IClipboard*); - virtual void onScreenSaverPrimary(bool); - - // secondary screen implementations - virtual void openSecondary(); - virtual void closeSecondary(); - virtual void enterScreenSecondary(SInt32 x, SInt32 y); - virtual void leaveScreenSecondary(); - virtual void setClipboardSecondary(const IClipboard*); - virtual void onScreenSaverSecondary(bool); - - // get the display - Display* getDisplay() const; - - // process X events from the display - void onEvents(); - - // called by open() and close(). override to hook up and unhook the - // display connection to the event queue. call onEvents() when events - // are available. - virtual void onOpen(bool isPrimary) = 0; - virtual void onClose() = 0; - - private: - void selectEvents(Window) const; - KeyModifierMask mapModifierFromX(unsigned int) const; - unsigned int mapModifierToX(KeyModifierMask) const; - KeyID mapKeyFromX(KeyCode, KeyModifierMask) const; - KeyCode mapKeyToX(KeyID) const; - ButtonID mapButtonFromX(unsigned int button) const; - unsigned int mapButtonToX(ButtonID) const; - void onPrimaryEvents(); - void onSecondaryEvents(); - - private: - CString m_name; - Display* m_display; - int m_screen; - bool m_primary; - SInt32 m_w, m_h; - - // stuff for primary screens - Window m_window; - bool m_active; -}; - -#endif diff --git a/IClient.h b/IClient.h deleted file mode 100644 index 02dd7129..00000000 --- a/IClient.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef ICLIENT_H -#define ICLIENT_H - -class CString; - -class IClient { - public: - IClient() { } - virtual ~IClient() { } - - // manipulators - - // connect to server and begin processing events - virtual void run(const CString& hostname) = 0; -}; - -#endif diff --git a/IClipboard.h b/IClipboard.h deleted file mode 100644 index e69de29b..00000000 diff --git a/IEventQueue.h b/IEventQueue.h deleted file mode 100644 index 36609b0c..00000000 --- a/IEventQueue.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef IEVENTQUEUE_H -#define IEVENTQUEUE_H - -#define CEQ (IEventQueue::getInstance()) - -class CEvent; - -class IEventQueue { - public: - IEventQueue(); - virtual ~IEventQueue(); - - // note -- all of the methods in an IEventQueue subclass for a - // platform must be thread safe if it will be used by multiple - // threads simultaneously on that platform. - - // manipulators - - // wait up to timeout seconds for the queue to become not empty. - // as a side effect this can do the insertion of events. if - // timeout < 0.0 then wait indefinitely. it's possible for - // wait() to return prematurely so always call isEmpty() to - // see if there are any events. - virtual void wait(double timeout) = 0; - - // reads and removes the next event on the queue. waits indefinitely - // for an event if the queue is empty. - virtual void pop(CEvent*) = 0; - - // push an event onto the queue - virtual void push(const CEvent*) = 0; - - // returns true if the queue is empty and wait() would block - virtual bool isEmpty() = 0; - - // accessors - - // get the singleton event queue - static IEventQueue* getInstance(); - - private: - static IEventQueue* s_instance; -}; - -#endif diff --git a/IScreen.h b/IScreen.h deleted file mode 100644 index fd2a8346..00000000 --- a/IScreen.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef ISCREEN_H -#define ISCREEN_H - -/* - * IScreen -- interface for display screens - * - * a screen encapsulates input and output devices, typically a mouse - * and keyboard for input and a graphical display for output. one - * screen is designated as the primary screen. only input from the - * primary screen's input devices is used. other screens are secondary - * screens and they simulate input from their input devices but ignore - * any actual input. a screen can be either a primary or a secondary - * but not both at the same time. most methods behave differently - * depending on the screen type. - */ - -#include "BasicTypes.h" -#include "KeyTypes.h" -#include "MouseTypes.h" -#include "CString.h" - -class IClipboard; - -class IScreen { - public: - IScreen() { } - virtual ~IScreen() { } - - // manipulators - - // open/close screen. these are where the client should do - // initialization and cleanup of the system's screen. if isPrimary - // is true then this screen will be used (exclusively) as the - // primary screen, otherwise it will be used (exclusively) as a - // secondary screen. - // - // primary: - // open(): open the screen and begin reporting input events to - // the event queue. input events should be reported no matter - // where on the screen they occur but the screen should not - // interfere with the normal dispatching of events. the screen - // should detect when the screen saver is activated. if it can't - // do that it should disable the screen saver and start it itself - // after the appropriate duration of no input. - // - // secondary: - // open(): open the screen, hide the cursor and disable the - // screen saver. then wait for an enterScreen() or close(), - // reporting the following events: FIXME. - virtual void open(bool isPrimary) = 0; - virtual void close() = 0; - - // enter/leave screen - // - // primary: - // enterScreen(): the user has navigated back to the primary - // screen. warp the cursor to the given coordinates, unhide the - // cursor and ungrab the input devices. the screen must also - // detect and report (enqueue) input events. for the primary - // screen, enterScreen() is only called after a leaveScreen(). - // leaveScreen(): the user has navigated off the primary screen. - // hide the cursor and grab exclusive access to the input devices. - // input events must be reported. - // - // secondary: - // enterScreen(): the user has navigated to this secondary - // screen. warp the cursor to the given coordinates and show it. - // prepare to simulate input events. - // leaveScreen(): the user has navigated off this secondary - // screen. clean up input event simulation. hide the cursor. - virtual void enterScreen(SInt32 xAbsolute, SInt32 yAbsolute) = 0; - virtual void leaveScreen() = 0; - - // warp the cursor to the given position - virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; - - // - // clipboard operations - // - - // set the screen's clipboard contents. this is usually called - // soon after an enterScreen(). - virtual void setClipboard(const IClipboard*) = 0; - - // - // screen saver operations - // - - // show or hide the screen saver - virtual void onScreenSaver(bool show) = 0; - - // - // input simulation - // - // these methods must simulate the appropriate input event. - // these methods are only called on secondary screens. - // - - // keyboard input - virtual void onKeyDown(KeyID, KeyModifierMask) = 0; - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void onKeyUp(KeyID, KeyModifierMask) = 0; - - // mouse input - virtual void onMouseDown(ButtonID) = 0; - virtual void onMouseUp(ButtonID) = 0; - virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; - virtual void onMouseWheel(SInt32 delta) = 0; - - // clipboard input - // FIXME -- do we need this? - virtual void onClipboardChanged() = 0; - - // accessors - - // get the screen's name. all screens must have a name unique on - // the server they connect to. the hostname is usually an - // appropriate name. - virtual CString getName() const = 0; - - // get the size of the screen - virtual void getSize(SInt32* width, SInt32* height) const = 0; - - // clipboard operations - - // get the screen's clipboard contents - virtual void getClipboard(IClipboard*) const = 0; -}; - -#endif diff --git a/IServer.h b/IServer.h deleted file mode 100644 index 2a041d64..00000000 --- a/IServer.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef ISERVER_H -#define ISERVER_H - -class IScreen; - -class IServer { - public: - IServer() { } - virtual ~IServer() { } - - // manipulators - - // run the server until terminated - virtual void run() = 0; - - // clipboard operations - virtual void onClipboardChanged(IScreen*) = 0; - - // enter the given screen, leaving the previous screen. the cursor - // should be warped to the center of the screen. - virtual void setActiveScreen(IScreen*) = 0; - - // accessors - - // get the screen that was last entered - virtual IScreen* getActiveScreen() const = 0; -}; - -#endif diff --git a/ISocket.h b/ISocket.h deleted file mode 100644 index 1a46f5d9..00000000 --- a/ISocket.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef ISOCKET_H -#define ISOCKET_H - -#include "BasicTypes.h" - -class IJob; -class CString; - -class ISocket { - public: - // d'tor closes the socket - ISocket() { } - virtual ~ISocket() { } - - // manipulators - - // set the job to invoke when the socket is readable or writable. - // a socket that has connected after a call to connect() becomes - // writable. a socket that is ready to accept a connection after - // a call to listen() becomes readable. the socket returned by - // accept() does not have any jobs assigned to it. - virtual void setReadJob(IJob* adoptedJob) = 0; - virtual void setWriteJob(IJob* adoptedJob) = 0; - - // open/close. connect() begins connecting to the given host but - // doesn't wait for the connection to complete. listen() begins - // listening on the given interface and port; if hostname is - // empty then listen on all interfaces. accept() waits for a - // connection on the listening interface and returns a new - // socket for the connection. - virtual void connect(const CString& hostname, UInt16 port) = 0; - virtual void listen(const CString& hostname, UInt16 port) = 0; - virtual ISocket* accept() = 0; - - // read data from socket. returns without waiting if not enough - // data is available. returns the number of bytes actually read, - // which is zero if there were no bytes to read and -1 if the - // remote end of the socket has disconnected. - virtual SInt32 read(void* buffer, SInt32 numBytes) = 0; - - // write data to socket. waits until all data has been written. - virtual void write(const void* buffer, SInt32 numBytes) = 0; -}; - -#endif diff --git a/Make-solaris b/Make-solaris new file mode 100644 index 00000000..4a78355d --- /dev/null +++ b/Make-solaris @@ -0,0 +1,67 @@ +# +# build tools +# +AR = /usr/ccs/bin/ar cru +CD = cd +CXX = g++ +ECHO = echo +LD = /usr/css/bin/ld +MKDIR = /bin/mkdir +RM = /bin/rm -f +RMR = /bin/rm -rf + +# +# compiler options +# +GCXXDEFS = +GCXXINCS = -I$(DEPTH)/include -I/usr/X11R6/include +GCXXOPTS = -Wall -W -fexceptions -fno-rtti +CXXOPTIMIZER = -g +#CXXOPTIMIZER = -O2 -DNDEBUG + +# +# linker options +# +#GLDLIBS = -L$(LIBDIR) -L/usr/X11R6/lib -lX11 -lXext -lXtst +GLDLIBS = -L$(LIBDIR) -lsocket -lnsl -lposix4 +GLDOPTS = -z muldefs + +# +# library stuff +# +LIBTARGET = $(LIBDIR)/lib$(TARGET).a + +# +# dependency generation stuff +# +MKDEP = $(DEPTH)/tools/depconv +MKDEPOPT = -MD +MKDEPPRE = +MKDEPPOST = $(MKDEP) -f $(MKDEPFILE) $*.d; $(RM) $*.d +MKDEPFILE = Makedepend + +# +# stuff to clean +# +DIRT = $(_FORCE) $(LDIRT) $(GDIRT) +GDIRT = *.[eoud] a.out core ar.tmp.* $(MKDEPFILE) + +# +# Rule macros for nonterminal makefiles that iterate over subdirectories, +# making the current target. Set SUBDIRS to the relevant list of kids. +# +# Set NOSUBMESG to any value to suppress a warning that subdirectories +# are not present. +# +SUBDIR_MAKERULE= \ + if test ! -d $$d; then \ + if test "$(NOSUBMESG)" = "" ; then \ + ${ECHO} "SKIPPING $$d: No such directory."; \ + fi \ + else \ + ${ECHO} "${CD} $$d; $(MAKE) $${RULE:=$@}"; \ + (${CD} $$d; ${MAKE} $${RULE:=$@}); \ + fi + +SUBDIRS_MAKERULE= \ + @for d in $(SUBDIRS); do $(SUBDIR_MAKERULE); done diff --git a/Makecommon b/Makecommon new file mode 100644 index 00000000..ad8c1558 --- /dev/null +++ b/Makecommon @@ -0,0 +1,93 @@ +# +# +# common definitions +# +# + +# +# empty define, used to terminate lists +# +NULL = + +# +# target directories +# +LIBDIR = $(DEPTH)/lib +TARGETDIR = $(DEPTH)/bin + +# +# compiler options +# +CXXFLAGS = $(LCXXFLAGS) $(GCXXFLAGS) $(CXXOPTIMIZER) $(MKDEPOPT) +LCXXFLAGS = $(LCXXDEFS) $(LCXXINCS) $(LCXXOPTS) +GCXXFLAGS = $(GCXXDEFS) $(GCXXINCS) $(GCXXOPTS) + +# +# linker options +# +LDFLAGS = $(LDOPTS) $(LDLIBS) +LDOPTS = $(LLDOPTS) $(GLDOPTS) +LDLIBS = $(LLDLIBS) $(GLDLIBS) + +# +# ar options +# +ARF = $(AR) + +# +# Convenience file list macros: +# +SOURCES = $(CXXFILES) +OBJECTS = $(CXXFILES:.cpp=.o) + +# +# always unsatisfied target +# +_FORCE = $(COMMONPREF)_force + +# +# +# common rules +# +# + +# +# default target. makefiles must define a target named `targets'. +# +$(COMMONPREF)default: targets + +# +# always unsatisfied target +# +$(_FORCE): + +# +# cleaners +# +COMMONTARGETS = clean clobber +$(COMMONPREF)clean: $(_FORCE) + $(RM) $(DIRT) + +$(COMMONPREF)clobber: clean $(_FORCE) + $(RM) $(TARGETS) + +# +# implicit target rules +# +.SUFFIXES: .cpp .o + +.cpp.o: + $(MKDEPPRE) + $(CXX) $(CXXFLAGS) -c $< + $(MKDEPPOST) + +# +# platform stuff +# +include $(DEPTH)/Make-linux +#include $(DEPTH)/Make-solaris + +# +# load dependencies +# +sinclude $(MKDEPFILE) diff --git a/TMethodJob.h b/TMethodJob.h deleted file mode 100644 index 341e3491..00000000 --- a/TMethodJob.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TMETHODJOB_H -#define TMETHODJOB_H - -#include "IJob.h" - -template -class TMethodJob : public IJob { - public: - typedef void (T::*Method)(); - - TMethodJob(T* object, Method method) : - m_object(object), m_method(method) { } - virtual ~TMethodJob() { } - - // IJob overrides - virtual void run() { (m_object->*m_method)(); } - - private: - T* m_object; - Method m_method; -}; - -#endif diff --git a/XBase.cpp b/XBase.cpp deleted file mode 100644 index df1bd475..00000000 --- a/XBase.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "XBase.h" - -// win32 wants a const char* argument to std::exception c'tor -#if CONFIG_PLATFORM_WIN32 -#define STDEXCEPTARG "" -#endif - -// default to no argument -#ifndef STDEXCEPTARG -#define STDEXCEPTARG -#endif - -// -// XBase -// - -XBase::XBase() : exception(STDEXCEPTARG) -{ - // do nothing -} - -XBase::~XBase() -{ - // do nothing -} - -const char* XBase::what() const -{ - return getType(); -} - -const char* XBase::getType() const -{ - return "XBase.h"; -} - -CString XBase::format(const CString& fmt) const -{ - return fmt; -} diff --git a/XBase.h b/XBase.h deleted file mode 100644 index f2ed5162..00000000 --- a/XBase.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef XBASE_H -#define XBASE_H - -#include "CString.h" -#include - -class XBase : public std::exception { - public: - XBase(); - virtual ~XBase(); - - // accessors - - // return the name of the exception type - virtual const char* getType() const; - - // format and return formatString by replacing positional - // arguments (%1, %2, etc.). default returns formatString - // unchanged. subclasses should document what positional - // arguments they replace. - virtual CString format(const CString& formatString) const; - - // std::exception overrides - virtual const char* what() const; -}; - -#define XNAME(_n) \ - public: \ - virtual const char* getType() const { return #_n; } - -#endif diff --git a/XSocket.h b/XSocket.h deleted file mode 100644 index e8f775f2..00000000 --- a/XSocket.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef XSOCKET_H -#define XSOCKET_H - -#include "XBase.h" - -class XSocket : public XBase { - public: - // accessors - - const char* getMessage() const { return m_msg; } - - protected: - XSocket(const char* msg) : m_msg(msg) { } - - private: - const char* m_msg; -}; - -#define XSOCKETDEF(_n) \ -class _n : public XSocket { \ - public: \ - _n(const char* msg) : XSocket(msg) { } \ - XNAME(_n) \ -}; - -XSOCKETDEF(XSocketCreate) -XSOCKETDEF(XSocketName) -XSOCKETDEF(XSocketConnect) -XSOCKETDEF(XSocketListen) -XSOCKETDEF(XSocketAccept) -XSOCKETDEF(XSocketWrite) - -#endif diff --git a/base/BasicTypes.h b/base/BasicTypes.h new file mode 100644 index 00000000..1a88d3a0 --- /dev/null +++ b/base/BasicTypes.h @@ -0,0 +1,46 @@ +#ifndef BASICTYPES_H +#define BASICTYPES_H + +#include "common.h" + +#if defined(CONFIG_PLATFORM_LINUX) + +#include + +typedef int8_t SInt8; +typedef int16_t SInt16; +typedef int32_t SInt32; +typedef int64_t SInt64; + +typedef uint8_t UInt8; +typedef uint16_t UInt16; +typedef uint32_t UInt32; +typedef uint64_t UInt64; + +#endif // CONFIG_PLATFORM_LINUX + +#if defined(CONFIG_PLATFORM_SOLARIS) + +#include + +typedef int8_t SInt8; +typedef int16_t SInt16; +typedef int32_t SInt32; +typedef int64_t SInt64; + +typedef uint8_t UInt8; +typedef uint16_t UInt16; +typedef uint32_t UInt32; +typedef uint64_t UInt64; + +#endif // CONFIG_PLATFORM_SOLARIS + +#if defined(CONFIG_PLATFORM_WIN32) + +// FIXME + +#endif // CONFIG_PLATFORM_WIN32 + +#endif + + diff --git a/base/CFunctionJob.cpp b/base/CFunctionJob.cpp new file mode 100644 index 00000000..50a55100 --- /dev/null +++ b/base/CFunctionJob.cpp @@ -0,0 +1,19 @@ +#include "CFunctionJob.h" + +// +// CFunctionJob +// + +CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) : + m_func(func), + m_arg(arg) +{ + // do nothing +} + +void CFunctionJob::run() +{ + if (m_func != NULL) { + m_func(m_arg); + } +} diff --git a/base/CFunctionJob.h b/base/CFunctionJob.h new file mode 100644 index 00000000..346d28e5 --- /dev/null +++ b/base/CFunctionJob.h @@ -0,0 +1,18 @@ +#ifndef CFUNCTIONJOB_H +#define CFUNCTIONJOB_H + +#include "IJob.h" + +class CFunctionJob : public IJob { + public: + CFunctionJob(void (*func)(void*), void* arg = NULL); + + // IJob overrides + virtual void run(); + + private: + void (*m_func)(void*); + void* m_arg; +}; + +#endif diff --git a/base/CStopwatch.cpp b/base/CStopwatch.cpp new file mode 100644 index 00000000..f7adac6b --- /dev/null +++ b/base/CStopwatch.cpp @@ -0,0 +1,180 @@ +#include "CStopwatch.h" + +// +// CStopwatch +// + +CStopwatch::CStopwatch(bool triggered) : + m_mark(0.0), + m_triggered(triggered), + m_stopped(triggered) +{ + if (!triggered) + m_mark = getClock(); +} + +CStopwatch::~CStopwatch() +{ + // do nothing +} + +double CStopwatch::reset() +{ + if (m_stopped) { + const double dt = m_mark; + m_mark = 0.0; + return dt; + } + else { + const double t = getClock(); + const double dt = t - m_mark; + m_mark = t; + return dt; + } +} + +void CStopwatch::stop() +{ + if (m_stopped) + return; + + // save the elapsed time + m_mark = getClock() - m_mark; + m_stopped = true; +} + +void CStopwatch::start() +{ + m_triggered = false; + if (!m_stopped) + return; + + // set the mark such that it reports the time elapsed at stop() + m_mark = getClock() - m_mark; + m_stopped = false; +} + +void CStopwatch::setTrigger() +{ + stop(); + m_triggered = true; +} + +double CStopwatch::getTime() +{ + if (m_triggered) { + const double dt = m_mark; + start(); + return dt; + } + if (m_stopped) + return m_mark; + return getClock() - m_mark; +} + +CStopwatch::operator double() +{ + return getTime(); +} + +bool CStopwatch::isStopped() const +{ + return m_stopped; +} + +double CStopwatch::getTime() const +{ + if (m_stopped) + return m_mark; + return getClock() - m_mark; +} + +CStopwatch::operator double() const +{ + return getTime(); +} + +#if defined(CONFIG_PLATFORM_UNIX) + +#include + +double CStopwatch::getClock() const +{ + struct timeval t; + gettimeofday(&t, NULL); + return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; +} + +#endif // CONFIG_PLATFORM_UNIX + +#if defined(CONFIG_PLATFORM_WIN32) + +// avoid getting a lot a crap from mmsystem.h that we don't need +#define MMNODRV // Installable driver support +#define MMNOSOUND // Sound support +#define MMNOWAVE // Waveform support +#define MMNOMIDI // MIDI support +#define MMNOAUX // Auxiliary audio support +#define MMNOMIXER // Mixer support +#define MMNOJOY // Joystick support +#define MMNOMCI // MCI support +#define MMNOMMIO // Multimedia file I/O support +#define MMNOMMSYSTEM // General MMSYSTEM functions + +#include +#include + +typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void); + +static double s_freq = 0.0; +static HINSTANCE s_mmInstance = NULL; +static PTimeGetTime s_tgt = NULL; + +// +// initialize local variables +// + +class CStopwatchInit { + public: + CStopwatchInit(); + ~CStopwatchInit(); +}; +static CStopwatchInit s_init; + +CStopwatchInit::CStopwatchInit() +{ + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) { + s_freq = 1.0 / static_cast(freq.QuadPart); + } + else { + // load winmm.dll and get timeGetTime + s_mmInstance = LoadLibrary("winmm"); + if (s_mmInstance) + s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime"); + } +} + +CStopwatchInit::~CStopwatchInit() +{ + if (s_mmInstance) + FreeLibrary(reinterpret_cast(s_mmInstance)); +} + +double CStopwatch::getClock() const +{ + // get time. we try three ways, in order of descending precision + if (s_freq != 0.0) { + LARGE_INTEGER c; + QueryPerformanceCounter(&c); + return s_freq * static_cast(c.QuadPart); + } + else if (s_tgt) { + return 0.001 * static_cast(s_tgt()); + } + else { + return 0.001 * static_cast(GetTickCount()); + } +} + +#endif // CONFIG_PLATFORM_WIN32 diff --git a/base/CStopwatch.h b/base/CStopwatch.h new file mode 100644 index 00000000..da0c1ab5 --- /dev/null +++ b/base/CStopwatch.h @@ -0,0 +1,57 @@ +#ifndef CSTOPWATCH_H +#define CSTOPWATCH_H + +#include "common.h" + +class CStopwatch { + public: + // the default constructor does an implicit reset() or setTrigger(). + // if triggered == false then the clock starts ticking. + CStopwatch(bool triggered = false); + ~CStopwatch(); + + // manipulators + + // set the start time to the current time, returning the time since + // the last reset. this does not remove the trigger if it's set nor + // does it start a stopped clock. if the clock is stopped then + // subsequent reset()'s will return 0. + double reset(); + + // stop and start the stopwatch. while stopped, no time elapses. + // stop() does not remove the trigger but start() does, even if + // the clock was already started. + void stop(); + void start(); + + // setTrigger() stops the clock like stop() except there's an + // implicit start() the next time (non-const) getTime() is called. + // this is useful when you want the clock to start the first time + // you check it. + void setTrigger(); + + // return the time since the last reset() (or call reset() and + // return zero if the trigger is set). + double getTime(); + operator double(); + + // accessors + + // returns true if the watch is stopped + bool isStopped() const; + + // return the time since the last reset(). these cannot trigger + // the clock to start so if the trigger is set it's as if it wasn't. + double getTime() const; + operator double() const; + + private: + double getClock() const; + + private: + double m_mark; + bool m_triggered; + bool m_stopped; +}; + +#endif diff --git a/base/CString.h b/base/CString.h new file mode 100644 index 00000000..88041939 --- /dev/null +++ b/base/CString.h @@ -0,0 +1,41 @@ +#ifndef CSTRING_H +#define CSTRING_H + +#include "common.h" +#include + +#ifndef CSTRING_DEF_CTOR +#define CSTRING_ALLOC1 +#define CSTRING_ALLOC2 +#define CSTRING_DEF_CTOR CString() : _Myt() { } +#endif + +// use to get appropriate type for string constants. it depends on +// the internal representation type of CString. +#define _CS(_x) _x + +class CString : public std::string { + public: + typedef char _e; + typedef _e CharT; + typedef std::allocator<_e> _a; + typedef std::string _Myt; + typedef const_iterator _It; + + // same constructors as base class + CSTRING_DEF_CTOR + CString(const _Myt& _x) : _Myt(_x) { } + CString(const _Myt& _x, size_type _p, size_type _m CSTRING_ALLOC1) : + _Myt(_x, _p, _m CSTRING_ALLOC2) { } + CString(const _e *_s, size_type _n CSTRING_ALLOC1) : + _Myt(_s, _n CSTRING_ALLOC2) { } + CString(const _e *_s CSTRING_ALLOC1) : + _Myt(_s CSTRING_ALLOC2) { } + CString(size_type _n, _e _c CSTRING_ALLOC1) : + _Myt(_n, _c CSTRING_ALLOC2) { } + CString(_It _f, _It _l CSTRING_ALLOC1) : + _Myt(_f, _l CSTRING_ALLOC2) { } +}; + +#endif + diff --git a/base/IInterface.h b/base/IInterface.h new file mode 100644 index 00000000..bb3676bc --- /dev/null +++ b/base/IInterface.h @@ -0,0 +1,11 @@ +#ifndef IINTERFACE_H +#define IINTERFACE_H + +#include "common.h" + +class IInterface { + public: + virtual ~IInterface() { } +}; + +#endif diff --git a/IJob.h b/base/IJob.h similarity index 55% rename from IJob.h rename to base/IJob.h index 381e29ad..3efb1b51 100644 --- a/IJob.h +++ b/base/IJob.h @@ -1,13 +1,10 @@ #ifndef IJOB_H #define IJOB_H -class IJob { +#include "IInterface.h" + +class IJob : public IInterface { public: - IJob() { } - virtual ~IJob() { } - - // manipulators - virtual void run() = 0; }; diff --git a/base/Makefile b/base/Makefile new file mode 100644 index 00000000..42128cec --- /dev/null +++ b/base/Makefile @@ -0,0 +1,24 @@ +DEPTH=.. +include $(DEPTH)/Makecommon + +# +# target file +# +TARGET = base + +# +# source files +# +LCXXINCS = \ + $(NULL) +CXXFILES = \ + XBase.cpp \ + CFunctionJob.cpp \ + CStopwatch.cpp \ + $(NULL) + +targets: $(LIBTARGET) + +$(LIBTARGET): $(OBJECTS) + if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi + $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/base/TMethodJob.h b/base/TMethodJob.h new file mode 100644 index 00000000..8e889b3d --- /dev/null +++ b/base/TMethodJob.h @@ -0,0 +1,39 @@ +#ifndef CMETHODJOB_H +#define CMETHODJOB_H + +#include "IJob.h" + +template +class TMethodJob : public IJob { + public: + TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL); + + // IJob overrides + virtual void run(); + + private: + T* m_object; + void (T::*m_method)(void*); + void* m_arg; +}; + +template +inline +TMethodJob::TMethodJob(T* object, void (T::*method)(void*), void* arg) : + m_object(object), + m_method(method), + m_arg(arg) +{ + // do nothing +} + +template +inline +void TMethodJob::run() +{ + if (m_object != NULL) { + (m_object->*m_method)(m_arg); + } +} + +#endif diff --git a/base/XBase.cpp b/base/XBase.cpp new file mode 100644 index 00000000..6558d5e3 --- /dev/null +++ b/base/XBase.cpp @@ -0,0 +1,72 @@ +#include "XBase.h" +#include + +// win32 wants a const char* argument to std::exception c'tor +#if CONFIG_PLATFORM_WIN32 +#define STDEXCEPTARG "" +#endif + +// default to no argument +#ifndef STDEXCEPTARG +#define STDEXCEPTARG +#endif + +// +// XBase +// + +XBase::XBase() : exception(STDEXCEPTARG), m_what() +{ + // do nothing +} + +XBase::XBase(const CString& msg) : exception(STDEXCEPTARG), m_what(msg) +{ + // do nothing +} + +XBase::~XBase() +{ + // do nothing +} + +const char* XBase::what() const +{ + if (m_what.empty()) { + m_what = getWhat(); + } + return m_what.c_str(); +} + +CString XBase::format(const char* /*id*/, + const char* fmt, ...) const throw() +{ + // FIXME -- use id to lookup formating string + // FIXME -- format string with arguments + return fmt; +} + + +// +// MXErrno +// + +MXErrno::MXErrno() : m_errno(errno) +{ + // do nothing +} + +MXErrno::MXErrno(int err) : m_errno(err) +{ + // do nothing +} + +int MXErrno::getErrno() const +{ + return m_errno; +} + +const char* MXErrno::getErrstr() const +{ + return strerror(m_errno); +} diff --git a/base/XBase.h b/base/XBase.h new file mode 100644 index 00000000..196f2122 --- /dev/null +++ b/base/XBase.h @@ -0,0 +1,44 @@ +#ifndef XBASE_H +#define XBASE_H + +#include "CString.h" +#include + +class XBase : public std::exception { + public: + XBase(); + XBase(const CString& msg); + virtual ~XBase(); + + // std::exception overrides + virtual const char* what() const; + + protected: + // returns a human readable string describing the exception + virtual CString getWhat() const throw() = 0; + + // look up a message and format it + virtual CString format(const char* id, + const char* defaultFormat, ...) const throw(); + + private: + mutable CString m_what; +}; + +class MXErrno { + public: + MXErrno(); + MXErrno(int); + + // manipulators + + // accessors + + int getErrno() const; + const char* getErrstr() const; + + private: + int m_errno; +}; + +#endif diff --git a/base/common.h b/base/common.h new file mode 100644 index 00000000..ca1224ca --- /dev/null +++ b/base/common.h @@ -0,0 +1,32 @@ +#ifndef COMMON_H +#define COMMON_H + +#if defined(__linux__) + +#define CONFIG_PLATFORM_LINUX +#define CONFIG_PLATFORM_UNIX +#define CONFIG_TYPES_X11 +#define CONFIG_PTHREADS + +#elif defined(__sun__) + +#define CONFIG_PLATFORM_SOLARIS +#define CONFIG_PLATFORM_UNIX +#define CONFIG_TYPES_X11 +#define CONFIG_PTHREADS + +#elif defined(_WINDOWS) && defined(WIN32) + +#define CONFIG_PLATFORM_WIN32 + +#else + +#error unsupported platform + +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#endif diff --git a/io/CBufferedInputStream.cpp b/io/CBufferedInputStream.cpp new file mode 100644 index 00000000..d0978dfb --- /dev/null +++ b/io/CBufferedInputStream.cpp @@ -0,0 +1,107 @@ +#include "CBufferedInputStream.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "IJob.h" +#include "XIO.h" +#include +#include + +// +// CBufferedInputStream +// + +CBufferedInputStream::CBufferedInputStream(CMutex* mutex, IJob* closeCB) : + m_mutex(mutex), + m_empty(mutex, true), + m_closeCB(closeCB), + m_closed(false), + m_hungup(false) +{ + assert(m_mutex != NULL); +} + +CBufferedInputStream::~CBufferedInputStream() +{ + delete m_closeCB; +} + +void CBufferedInputStream::write( + const void* data, UInt32 n) throw() +{ + if (!m_hungup && n > 0) { + m_buffer.write(data, n); + m_empty = (m_buffer.getSize() == 0); + m_empty.broadcast(); + } +} + +void CBufferedInputStream::hangup() throw() +{ + m_hungup = true; + m_empty.broadcast(); +} + +UInt32 CBufferedInputStream::readNoLock( + void* dst, UInt32 n) throw(XIO) +{ + if (m_closed) { + throw XIOClosed(); + } + + // wait for data (or hangup) + while (!m_hungup && m_empty == true) { + m_empty.wait(); + } + + // read data + const UInt32 count = m_buffer.getSize(); + if (n > count) { + n = count; + } + if (n > 0) { + if (dst != NULL) { + ::memcpy(dst, m_buffer.peek(n), n); + } + m_buffer.pop(n); + } + + // update empty state + if (m_buffer.getSize() == 0) { + m_empty = true; + m_empty.broadcast(); + } + return n; +} + +UInt32 CBufferedInputStream::getSizeNoLock() const throw() +{ + return m_buffer.getSize(); +} + +void CBufferedInputStream::close() throw(XIO) +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_closed = true; + if (m_closeCB) { + m_closeCB->run(); + } +} + +UInt32 CBufferedInputStream::read( + void* dst, UInt32 n) throw(XIO) +{ + CLock lock(m_mutex); + return readNoLock(dst, n); +} + +UInt32 CBufferedInputStream::getSize() const throw() +{ + CLock lock(m_mutex); + return getSizeNoLock(); +} + diff --git a/io/CBufferedInputStream.h b/io/CBufferedInputStream.h new file mode 100644 index 00000000..8a4dd7be --- /dev/null +++ b/io/CBufferedInputStream.h @@ -0,0 +1,51 @@ +#ifndef CBUFFEREDINPUTSTREAM_H +#define CBUFFEREDINPUTSTREAM_H + +#include "CStreamBuffer.h" +#include "CCondVar.h" +#include "IInputStream.h" + +class CMutex; +class IJob; + +class CBufferedInputStream : public IInputStream { + public: + CBufferedInputStream(CMutex*, IJob* adoptedCloseCB); + ~CBufferedInputStream(); + + // the caller is expected to lock the mutex before calling + // methods unless otherwise noted. + + // manipulators + + // write() appends n bytes to the buffer + void write(const void*, UInt32 n) throw(); + + // causes read() to always return immediately. if there is no + // more data then it returns 0. further writes are discarded. + void hangup() throw(); + + // same as read() but caller must lock the mutex + UInt32 readNoLock(void*, UInt32 count) throw(XIO); + + // accessors + + // same as getSize() but caller must lock the mutex + UInt32 getSizeNoLock() const throw(); + + // IInputStream overrides + // these all lock the mutex for their duration + virtual void close() throw(XIO); + virtual UInt32 read(void*, UInt32 count) throw(XIO); + virtual UInt32 getSize() const throw(); + + private: + CMutex* m_mutex; + CCondVar m_empty; + IJob* m_closeCB; + CStreamBuffer m_buffer; + bool m_closed; + bool m_hungup; +}; + +#endif diff --git a/io/CBufferedOutputStream.cpp b/io/CBufferedOutputStream.cpp new file mode 100644 index 00000000..bf3d9165 --- /dev/null +++ b/io/CBufferedOutputStream.cpp @@ -0,0 +1,79 @@ +#include "CBufferedOutputStream.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "IJob.h" +#include "XIO.h" +#include + +// +// CBufferedOutputStream +// + +CBufferedOutputStream::CBufferedOutputStream(CMutex* mutex, IJob* closeCB) : + m_mutex(mutex), + m_closeCB(closeCB), + m_closed(false) +{ + assert(m_mutex != NULL); +} + +CBufferedOutputStream::~CBufferedOutputStream() +{ + delete m_closeCB; +} + +const void* CBufferedOutputStream::peek(UInt32 n) throw() +{ + return m_buffer.peek(n); +} + +void CBufferedOutputStream::pop(UInt32 n) throw() +{ + m_buffer.pop(n); +} + +UInt32 CBufferedOutputStream::getSize() const throw() +{ + return m_buffer.getSize(); +} + +void CBufferedOutputStream::close() throw(XIO) +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_closed = true; + if (m_closeCB) { + m_closeCB->run(); + } +} + +UInt32 CBufferedOutputStream::write( + const void* data, UInt32 n) throw(XIO) +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_buffer.write(data, n); + return n; +} + +void CBufferedOutputStream::flush() throw(XIO) +{ + // wait until all data is written + while (getSizeWithLock() > 0) { + CThread::sleep(0.05); + } +} + +UInt32 CBufferedOutputStream::getSizeWithLock() const throw() +{ + CLock lock(m_mutex); + return m_buffer.getSize(); +} + diff --git a/io/CBufferedOutputStream.h b/io/CBufferedOutputStream.h new file mode 100644 index 00000000..ab43aecb --- /dev/null +++ b/io/CBufferedOutputStream.h @@ -0,0 +1,46 @@ +#ifndef CBUFFEREDOUTPUTSTREAM_H +#define CBUFFEREDOUTPUTSTREAM_H + +#include "CStreamBuffer.h" +#include "IOutputStream.h" + +class CMutex; +class IJob; + +class CBufferedOutputStream : public IOutputStream { + public: + CBufferedOutputStream(CMutex*, IJob* adoptedCloseCB); + ~CBufferedOutputStream(); + + // the caller is expected to lock the mutex before calling + // methods unless otherwise noted. + + // manipulators + + // peek() returns a buffer of n bytes (which must be <= getSize()). + // pop() discards the next n bytes. + const void* peek(UInt32 n) throw(); + void pop(UInt32 n) throw(); + + // accessors + + // return the number of bytes in the buffer + UInt32 getSize() const throw(); + + // IOutputStream overrides + // these all lock the mutex for their duration + virtual void close() throw(XIO); + virtual UInt32 write(const void*, UInt32 count) throw(XIO); + virtual void flush() throw(XIO); + + private: + UInt32 getSizeWithLock() const throw(); + + private: + CMutex* m_mutex; + IJob* m_closeCB; + CStreamBuffer m_buffer; + bool m_closed; +}; + +#endif diff --git a/io/CInputStreamFilter.cpp b/io/CInputStreamFilter.cpp new file mode 100644 index 00000000..486a6298 --- /dev/null +++ b/io/CInputStreamFilter.cpp @@ -0,0 +1,25 @@ +#include "CInputStreamFilter.h" +#include + +// +// CInputStreamFilter +// + +CInputStreamFilter::CInputStreamFilter(IInputStream* stream, bool adopted) : + m_stream(stream), + m_adopted(adopted) +{ + assert(m_stream != NULL); +} + +CInputStreamFilter::~CInputStreamFilter() +{ + if (m_adopted) { + delete m_stream; + } +} + +IInputStream* CInputStreamFilter::getStream() const throw() +{ + return m_stream; +} diff --git a/io/CInputStreamFilter.h b/io/CInputStreamFilter.h new file mode 100644 index 00000000..51f40b35 --- /dev/null +++ b/io/CInputStreamFilter.h @@ -0,0 +1,28 @@ +#ifndef CINPUTSTREAMFILTER_H +#define CINPUTSTREAMFILTER_H + +#include "IInputStream.h" + +class CInputStreamFilter : public IInputStream { + public: + CInputStreamFilter(IInputStream*, bool adoptStream = true); + ~CInputStreamFilter(); + + // manipulators + + // accessors + + // IInputStream overrides + virtual void close() throw(XIO) = 0; + virtual UInt32 read(void*, UInt32 maxCount) throw(XIO) = 0; + virtual UInt32 getSize() const throw() = 0; + + protected: + IInputStream* getStream() const throw(); + + private: + IInputStream* m_stream; + bool m_adopted; +}; + +#endif diff --git a/io/COutputStreamFilter.cpp b/io/COutputStreamFilter.cpp new file mode 100644 index 00000000..3f62075c --- /dev/null +++ b/io/COutputStreamFilter.cpp @@ -0,0 +1,25 @@ +#include "COutputStreamFilter.h" +#include + +// +// COutputStreamFilter +// + +COutputStreamFilter::COutputStreamFilter(IOutputStream* stream, bool adopted) : + m_stream(stream), + m_adopted(adopted) +{ + assert(m_stream != NULL); +} + +COutputStreamFilter::~COutputStreamFilter() +{ + if (m_adopted) { + delete m_stream; + } +} + +IOutputStream* COutputStreamFilter::getStream() const throw() +{ + return m_stream; +} diff --git a/io/COutputStreamFilter.h b/io/COutputStreamFilter.h new file mode 100644 index 00000000..11499d80 --- /dev/null +++ b/io/COutputStreamFilter.h @@ -0,0 +1,28 @@ +#ifndef COUTPUTSTREAMFILTER_H +#define COUTPUTSTREAMFILTER_H + +#include "IOutputStream.h" + +class COutputStreamFilter : public IOutputStream { + public: + COutputStreamFilter(IOutputStream*, bool adoptStream = true); + ~COutputStreamFilter(); + + // manipulators + + // accessors + + // IOutputStream overrides + virtual void close() throw(XIO) = 0; + virtual UInt32 write(const void*, UInt32 count) throw(XIO) = 0; + virtual void flush() throw(XIO) = 0; + + protected: + IOutputStream* getStream() const throw(); + + private: + IOutputStream* m_stream; + bool m_adopted; +}; + +#endif diff --git a/io/CStreamBuffer.cpp b/io/CStreamBuffer.cpp new file mode 100644 index 00000000..d2ce3140 --- /dev/null +++ b/io/CStreamBuffer.cpp @@ -0,0 +1,107 @@ +#include "CStreamBuffer.h" +#include + +// +// CStreamBuffer +// + +const UInt32 CStreamBuffer::kChunkSize = 4096; + +CStreamBuffer::CStreamBuffer() : m_size(0) +{ + // do nothing +} + +CStreamBuffer::~CStreamBuffer() +{ + // do nothing +} + +const void* CStreamBuffer::peek(UInt32 n) throw() +{ + assert(n <= m_size); + + // reserve space in first chunk + ChunkList::iterator head = m_chunks.begin(); + head->reserve(n); + + // consolidate chunks into the first chunk until it has n bytes + ChunkList::iterator scan = head; + ++scan; + while (head->size() < n && scan != m_chunks.end()) { + head->insert(head->end(), scan->begin(), scan->end()); + scan = m_chunks.erase(scan); + } + + return reinterpret_cast(head->begin()); +} + +void CStreamBuffer::pop(UInt32 n) throw() +{ + m_size -= n; + + // discard chunks until more than n bytes would've been discarded + ChunkList::iterator scan = m_chunks.begin(); + while (scan->size() <= n && scan != m_chunks.end()) { + n -= scan->size(); + scan = m_chunks.erase(scan); + } + + // if there's anything left over then remove it from the head chunk. + // if there's no head chunk then we're already empty. + if (scan == m_chunks.end()) { + m_size = 0; + } + else if (n > 0) { + scan->erase(scan->begin(), scan->begin() + n); + } +} + +void CStreamBuffer::write( + const void* vdata, UInt32 n) throw() +{ + assert(vdata != NULL); + + if (n == 0) { + return; + } + m_size += n; + + // cast data to bytes + const UInt8* data = reinterpret_cast(vdata); + + // point to last chunk if it has space, otherwise append an empty chunk + ChunkList::iterator scan = m_chunks.end(); + if (scan != m_chunks.begin()) { + --scan; + if (scan->size() >= kChunkSize) + ++scan; + } + if (scan == m_chunks.end()) { + scan = m_chunks.insert(scan); + } + + // append data in chunks + while (n > 0) { + // choose number of bytes for next chunk + UInt32 count = kChunkSize - scan->size(); + if (count > n) + count = n; + + // transfer data + scan->insert(scan->end(), data, data + count); + n -= count; + data += count; + + // append another empty chunk if we're not done yet + if (n > 0) { + scan = m_chunks.insert(scan); + } + } +} + +UInt32 CStreamBuffer::getSize() const throw() +{ + return m_size; +} + diff --git a/io/CStreamBuffer.h b/io/CStreamBuffer.h new file mode 100644 index 00000000..cb5bb864 --- /dev/null +++ b/io/CStreamBuffer.h @@ -0,0 +1,38 @@ +#ifndef CSTREAMBUFFER_H +#define CSTREAMBUFFER_H + +#include "BasicTypes.h" +#include +#include + +class CStreamBuffer { + public: + CStreamBuffer(); + ~CStreamBuffer(); + + // manipulators + + // peek() returns a buffer of n bytes (which must be <= getSize()). + // pop() discards the next n bytes. + const void* peek(UInt32 n) throw(); + void pop(UInt32 n) throw(); + + // write() appends n bytes to the buffer + void write(const void*, UInt32 n) throw(); + + // accessors + + // return the number of bytes in the buffer + UInt32 getSize() const throw(); + + private: + static const UInt32 kChunkSize; + + typedef std::vector Chunk; + typedef std::list ChunkList; + + ChunkList m_chunks; + UInt32 m_size; +}; + +#endif diff --git a/io/IInputStream.h b/io/IInputStream.h new file mode 100644 index 00000000..e4f93394 --- /dev/null +++ b/io/IInputStream.h @@ -0,0 +1,29 @@ +#ifndef IINPUTSTREAM_H +#define IINPUTSTREAM_H + +#include "IInterface.h" +#include "BasicTypes.h" +#include "XIO.h" + +class IInputStream : public IInterface { + public: + // manipulators + + // close the stream + virtual void close() throw(XIO) = 0; + + // read up to maxCount bytes into buffer, return number read. + // blocks if no data is currently available. if buffer is NULL + // then the data is discarded. + virtual UInt32 read(void* buffer, UInt32 maxCount) throw(XIO) = 0; + + // accessors + + // get a conservative estimate of the available bytes to read + // (i.e. a number not greater than the actual number of bytes). + // some streams may not be able to determine this and will always + // return zero. + virtual UInt32 getSize() const throw() = 0; +}; + +#endif diff --git a/io/IOutputStream.h b/io/IOutputStream.h new file mode 100644 index 00000000..f0b46244 --- /dev/null +++ b/io/IOutputStream.h @@ -0,0 +1,24 @@ +#ifndef IOUTPUTSTREAM_H +#define IOUTPUTSTREAM_H + +#include "IInterface.h" +#include "BasicTypes.h" +#include "XIO.h" + +class IOutputStream : public IInterface { + public: + // manipulators + + // close the stream + virtual void close() throw(XIO) = 0; + + // write count bytes to stream + virtual UInt32 write(const void*, UInt32 count) throw(XIO) = 0; + + // flush the stream + virtual void flush() throw(XIO) = 0; + + // accessors +}; + +#endif diff --git a/io/Makefile b/io/Makefile new file mode 100644 index 00000000..49496cd9 --- /dev/null +++ b/io/Makefile @@ -0,0 +1,29 @@ +DEPTH=.. +include $(DEPTH)/Makecommon + +# +# target file +# +TARGET = io + +# +# source files +# +LCXXINCS = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + $(NULL) +CXXFILES = \ + XIO.cpp \ + CInputStreamFilter.cpp \ + COutputStreamFilter.cpp \ + CStreamBuffer.cpp \ + CBufferedInputStream.cpp \ + CBufferedOutputStream.cpp \ + $(NULL) + +targets: $(LIBTARGET) + +$(LIBTARGET): $(OBJECTS) $(DEPLIBS) + if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi + $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/io/XIO.cpp b/io/XIO.cpp new file mode 100644 index 00000000..c51641d4 --- /dev/null +++ b/io/XIO.cpp @@ -0,0 +1,46 @@ +#include "XIO.h" + +// +// XIOErrno +// + +XIOErrno::XIOErrno() : MXErrno() +{ + // do nothing +} + +XIOErrno::XIOErrno(int err) : MXErrno(err) +{ + // do nothing +} + + +// +// XIOClose +// + +CString XIOClose::getWhat() const throw() +{ + return format("XIOClose", "close: %1", XIOErrno::getErrstr()); +} + + +// +// XIOClosed +// + +CString XIOClosed::getWhat() const throw() +{ + return format("XIOClosed", "already closed"); +} + + +// +// XIOEndOfStream +// + +CString XIOEndOfStream::getWhat() const throw() +{ + return format("XIOEndOfStream", "reached end of stream"); +} + diff --git a/io/XIO.h b/io/XIO.h new file mode 100644 index 00000000..b468e764 --- /dev/null +++ b/io/XIO.h @@ -0,0 +1,33 @@ +#ifndef XIO_H +#define XIO_H + +#include "XBase.h" +#include "BasicTypes.h" + +class XIO : public XBase { }; + +class XIOErrno : public XIO, public MXErrno { + public: + XIOErrno(); + XIOErrno(int); +}; + +class XIOClose: public XIOErrno { + protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +class XIOClosed : public XIO { + protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +class XIOEndOfStream : public XIO { + protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +#endif diff --git a/main.cpp b/main.cpp deleted file mode 100644 index b879e1a1..00000000 --- a/main.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include -#include - -#include "CServer.h" -#include "CClient.h" -#include "CUnixTCPSocket.h" -#include "CUnixEventQueue.h" -#include "CUnixXScreen.h" - -/* -static void selectMotion(Display* dpy, Window w) -{ - // select events - XSelectInput(dpy, w, PointerMotionMask | SubstructureNotifyMask); - - // recurse on child windows - Window rw, pw, *cw; - unsigned int nc; - if (XQueryTree(dpy, w, &rw, &pw, &cw, &nc)) { - for (unsigned int i = 0; i < nc; ++i) - selectMotion(dpy, cw[i]); - XFree(cw); - } -} - -static void trackMouse(Display* dpy) -{ - // note -- this doesn't track the mouse when it's grabbed. that's - // okay for synergy because we don't want to cross screens then. - selectMotion(dpy, DefaultRootWindow(dpy)); - while (true) { - XEvent event; - XNextEvent(dpy, &event); - switch (event.type) { - case MotionNotify: - fprintf(stderr, "mouse: %d,%d\n", event.xmotion.x_root, event.xmotion.y_root); - break; - - case CreateNotify: - selectMotion(dpy, event.xcreatewindow.window); - break; - } - } -} - -static void checkLEDs(Display* dpy) -{ - XKeyboardState values; - XGetKeyboardControl(dpy, &values); - - fprintf(stderr, "led (%08x): ", (unsigned int)values.led_mask); - for (int i = 0; i < 32; ++i) - fprintf(stderr, "%c", (values.led_mask & (1 << i)) ? 'O' : '.'); - fprintf(stderr, "\n"); - - XKeyboardControl ctrl; - for (int i = 0; i < 32; i += 2) { - ctrl.led = i + 1; - ctrl.led_mode = LedModeOff; - XChangeKeyboardControl(dpy, KBLed | KBLedMode, &ctrl); - XSync(dpy, False); - } -} -*/ - -int main(int argc, char** argv) -{ -/* - printf("Hello world\n"); - - Display* dpy = XOpenDisplay(NULL); - - checkLEDs(dpy); - trackMouse(dpy); - - XCloseDisplay(dpy); -*/ - - // install socket factory - CSocketFactory::setInstance(new CUnixTCPSocketFactory); - - // create event queue - CUnixEventQueue eventQueue; - - if (argc <= 1) { - // create server - CServer server; - - // create clients - CUnixXScreen localScreen("audrey2"); - - // register clients - server.addLocalScreen(&localScreen); - server.addRemoteScreen("remote1"); - - // hook up edges - server.connectEdge("audrey2", CServer::kLeft, "remote1"); - server.connectEdge("audrey2", CServer::kTop, "audrey2"); - server.connectEdge("audrey2", CServer::kBottom, "audrey2"); - server.connectEdge("remote1", CServer::kLeft, "audrey2"); - - // do it - server.run(); - } - else { - // create client - CUnixXScreen screen("remote1"); - CClient client(&screen); - client.run(argv[1]); - } - - return 0; -} diff --git a/mt/CCondVar.cpp b/mt/CCondVar.cpp new file mode 100644 index 00000000..e2d441ae --- /dev/null +++ b/mt/CCondVar.cpp @@ -0,0 +1,296 @@ +#include "CCondVar.h" +#include "CStopwatch.h" +#include + +// +// CCondVarBase +// + +CCondVarBase::CCondVarBase(CMutex* mutex) : + m_mutex(mutex) +#if defined(CONFIG_PLATFORM_WIN32) + , m_waitCountMutex() +#endif +{ + assert(m_mutex != NULL); + init(); +} + +CCondVarBase::~CCondVarBase() +{ + fini(); +} + +void CCondVarBase::lock() const throw() +{ + m_mutex->lock(); +} + +void CCondVarBase::unlock() const throw() +{ + m_mutex->unlock(); +} + +bool CCondVarBase::wait(double timeout) const +{ + CStopwatch timer(true); + return wait(timer, timeout); +} + +CMutex* CCondVarBase::getMutex() const throw() +{ + return m_mutex; +} + +#if defined(CONFIG_PTHREADS) + +#include "CThread.h" +#include +#include +#include + +void CCondVarBase::init() +{ + pthread_cond_t* cond = new pthread_cond_t; + int status = pthread_cond_init(cond, NULL); + assert(status == 0); + m_cond = reinterpret_cast(cond); +} + +void CCondVarBase::fini() +{ + pthread_cond_t* cond = reinterpret_cast(m_cond); + int status = pthread_cond_destroy(cond); + assert(status == 0); + delete cond; +} + +void CCondVarBase::signal() throw() +{ + pthread_cond_t* cond = reinterpret_cast(m_cond); + int status = pthread_cond_signal(cond); + assert(status == 0); +} + +void CCondVarBase::broadcast() throw() +{ + pthread_cond_t* cond = reinterpret_cast(m_cond); + int status = pthread_cond_broadcast(cond); + assert(status == 0); +} + +bool CCondVarBase::wait( + CStopwatch& timer, double timeout) const +{ + // check timeout against timer + if (timeout >= 0.0) { + timeout -= timer.getTime(); + if (timeout < 0.0) + return false; + } + + // get condition variable and mutex + pthread_cond_t* cond = reinterpret_cast(m_cond); + pthread_mutex_t* mutex = reinterpret_cast(m_mutex->m_mutex); + + // get final time + struct timeval now; + gettimeofday(&now, NULL); + struct timespec finalTime; + finalTime.tv_sec = now.tv_sec; + finalTime.tv_nsec = now.tv_usec * 1000; + if (timeout >= 0.0) { + const long timeout_sec = (long)timeout; + const long timeout_nsec = (long)(1000000000.0 * (timeout - timeout_sec)); + finalTime.tv_sec += timeout_sec; + finalTime.tv_nsec += timeout_nsec; + if (finalTime.tv_nsec >= 1000000000) { + finalTime.tv_nsec -= 1000000000; + finalTime.tv_sec += 1; + } + } + + // repeat until we reach the final time + int status; + for (;;) { + // compute the next timeout + gettimeofday(&now, NULL); + struct timespec endTime; + endTime.tv_sec = now.tv_sec; + endTime.tv_nsec = now.tv_usec * 1000 + 50000000; + if (endTime.tv_nsec >= 1000000000) { + endTime.tv_nsec -= 1000000000; + endTime.tv_sec += 1; + } + + // see if we should cancel this thread + CThread::testCancel(); + + // done if past final timeout + if (timeout >= 0.0) { + if (endTime.tv_sec > finalTime.tv_sec || + (endTime.tv_sec == finalTime.tv_sec && + endTime.tv_nsec >= finalTime.tv_nsec)) { + status = ETIMEDOUT; + break; + } + } + + // wait + status = pthread_cond_timedwait(cond, mutex, &endTime); + + // check for cancel again + CThread::testCancel(); + + // check wait status + if (status != ETIMEDOUT) + break; + } + + switch (status) { + case 0: + // success + return true; + + case ETIMEDOUT: + return false; + + default: + assert(0 && "condition variable wait error"); + return false; + } +} + +#endif // CONFIG_PTHREADS + +#if defined(CONFIG_PLATFORM_WIN32) + +#include "CLock.h" +#include "CThreadRep.h" +#include + +// +// note -- implementation taken from +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html +// titled "Strategies for Implementing POSIX Condition Variables +// on Win32." it also provides an implementation that doesn't +// suffer from the incorrectness problem described in our +// corresponding header but it is slower, still unfair, and +// can cause busy waiting. +// + +void CCondVarBase::init() +{ + // prepare events + HANDLE* events = new HANDLE[2]; + events[kSignal] = CreateEvent(NULL, FALSE, FALSE, NULL); + events[kBroadcast] = CreateEvent(NULL, TRUE, FALSE, NULL); + + // prepare members + m_cond = reinterpret_cast(events); + m_waitCount = 0; +} + +void CCondVarBase::fini() +{ + HANDLE* events = reinterpret_cast(m_cond); + CloseHandle(events[kSignal]); + CloseHandle(events[kBroadcast]); + delete[] events; +} + +void CCondVarBase::signal() throw() +{ + // is anybody waiting? + bool hasWaiter; + { + CLock lock(&m_waitCountMutex); + hasWaiter = (m_waitCount > 0); + } + + // wake one thread if anybody is waiting + if (hasWaiter) + SetEvent(reinterpret_cast(m_cond)[kSignal]); +} + +void CCondVarBase::broadcast() throw() +{ + // is anybody waiting? + bool hasWaiter; + { + CLock lock(&m_waitCountMutex); + hasWaiter = (m_waitCount > 0); + } + + // wake all threads if anybody is waiting + if (hasWaiter) + SetEvent(reinterpret_cast(m_cond)[kBroadcast]); +} + +bool CCondVarBase::wait( + CStopwatch& timer, double timeout) const +{ + // check timeout against timer + if (timeout >= 0.0) { + timeout -= timer.getTime(); + if (timeout < 0.0) + return false; + } + + // prepare to wait + CRefCountedPtr currentRep(CThreadRep::getCurrentThreadRep()); + const DWORD winTimeout = (timeout < 0.0) ? INFINITE : + static_cast(1000.0 * timeout); + HANDLE* events = reinterpret_cast(m_cond); + HANDLE handles[3]; + handles[0] = events[kSignal]; + handles[1] = events[kBroadcast]; + handles[2] = currentRep->getCancelEvent(); + const DWORD n = currentRep->isCancellable() ? 3 : 2; + + // update waiter count + { + CLock lock(&m_waitCountMutex); + ++m_waitCount; + } + + // release mutex. this should be atomic with the wait so that it's + // impossible for another thread to signal us between the unlock and + // the wait, which would lead to a lost signal on broadcasts. + // however, we're using a manual reset event for broadcasts which + // stays set until we reset it, so we don't lose the broadcast. + m_mutex->unlock(); + + // wait for a signal or broadcast + DWORD result = ::WaitForMultipleObjects(n, handles, FALSE, winTimeout); + + // cancel takes priority + if (n == 3 && result != WAIT_OBJECT_0 + 2 && + ::WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) + result = WAIT_OBJECT_0 + 2; + + // update the waiter count and check if we're the last waiter + bool last; + { + CLock lock(&m_waitCountMutex); + --m_waitCount; + last = (result == WAIT_OBJECT_0 + 1 && m_waitCount == 0); + } + + // reset the broadcast event if we're the last waiter + if (last) + ResetEvent(events[kBroadcast]); + + // reacquire the mutex + m_mutex->lock(); + + // cancel thread if necessary + if (result == WAIT_OBJECT_0 + 2) + currentRep->testCancel(); + + // return success or failure + return (result == WAIT_OBJECT_0 + 0 || + result == WAIT_OBJECT_0 + 1); +} + +#endif // CONFIG_PLATFORM_WIN32 diff --git a/mt/CCondVar.h b/mt/CCondVar.h new file mode 100644 index 00000000..a42e25ab --- /dev/null +++ b/mt/CCondVar.h @@ -0,0 +1,144 @@ +#ifndef CCONDVAR_H +#define CCONDVAR_H + +#include "CMutex.h" +#include "BasicTypes.h" + +class CStopwatch; + +class CCondVarBase { + public: + // mutex must be supplied. all condition variables have an + // associated mutex. the copy c'tor uses the same mutex as the + // argument and is otherwise like the default c'tor. + CCondVarBase(CMutex* mutex); + ~CCondVarBase(); + + // manipulators + + // lock/unlock the mutex. see CMutex. + void lock() const throw(); + void unlock() const throw(); + + // signal the condition. Signal() wakes one waiting thread. + // Broadcast() wakes all waiting threads. + void signal() throw(); + void broadcast() throw(); + + // accessors + + // wait on the condition. if timeout < 0 then wait until signalled, + // otherwise up to timeout seconds or until signalled, whichever + // comes first. since clients normally wait on condition variables + // in a loop, clients can provide a CStopwatch that acts as the + // timeout clock. using it, clients don't have to recalculate the + // timeout on each iteration. passing a stopwatch with a negative + // timeout is pointless but permitted. + // + // returns true if the object was signalled during the wait, false + // otherwise. + // + // (cancellation point) + bool wait(double timeout = -1.0) const; + bool wait(CStopwatch&, double timeout) const; + + // get the mutex passed to the c'tor + CMutex* getMutex() const throw(); + + private: + void init(); + void fini(); + + // not implemented + CCondVarBase(const CCondVarBase&); + CCondVarBase& operator=(const CCondVarBase&); + + private: + CMutex* m_mutex; + void* m_cond; + +#if defined(CONFIG_PLATFORM_WIN32) + enum { kSignal, kBroadcast }; + mutable UInt32 m_waitCount; + CMutex m_waitCountMutex; +#endif +}; + +template +class CCondVar : public CCondVarBase { + public: + CCondVar(CMutex* mutex, const T&); + CCondVar(const CCondVar&); + ~CCondVar(); + + // manipulators + + // assigns the value of the variable + CCondVar& operator=(const CCondVar&); + + // assign the value + CCondVar& operator=(const T&); + + // accessors + + // get the const value. this object should be locked before + // calling this method. + operator const T&() const throw(); + + private: + T m_data; +}; + +template +inline +CCondVar::CCondVar(CMutex* mutex, const T& data) : + CCondVarBase(mutex), m_data(data) +{ + // do nothing +} + +template +inline +CCondVar::CCondVar(const CCondVar& cv) : + CCondVarBase(cv.getMutex()), + m_data(cv.m_data) +{ + // do nothing +} + +template +inline +CCondVar::~CCondVar() +{ + // do nothing +} + +template +inline +CCondVar& CCondVar::operator=(const CCondVar& cv) +{ + m_data = cv.m_data; + return *this; +} + +template +inline +CCondVar& CCondVar::operator=(const T& data) +{ + m_data = data; + return *this; +} + +template +inline +CCondVar::operator const T&() const throw() +{ + return m_data; +} + + +// force instantiation of these common types +template class CCondVar; +template class CCondVar; + +#endif diff --git a/mt/CLock.cpp b/mt/CLock.cpp new file mode 100644 index 00000000..86aa721e --- /dev/null +++ b/mt/CLock.cpp @@ -0,0 +1,22 @@ +#include "CLock.h" +#include "CMutex.h" +#include "CCondVar.h" + +// +// CLock +// + +CLock::CLock(const CMutex* mutex) throw() : m_mutex(mutex) +{ + m_mutex->lock(); +} + +CLock::CLock(const CCondVarBase* cv) throw() : m_mutex(cv->getMutex()) +{ + m_mutex->lock(); +} + +CLock::~CLock() throw() +{ + m_mutex->unlock(); +} diff --git a/mt/CLock.h b/mt/CLock.h new file mode 100644 index 00000000..9bd81942 --- /dev/null +++ b/mt/CLock.h @@ -0,0 +1,24 @@ +#ifndef CLOCK_H +#define CLOCK_H + +#include "common.h" + +class CMutex; +class CCondVarBase; + +class CLock { + public: + CLock(const CMutex* mutex) throw(); + CLock(const CCondVarBase* cv) throw(); + ~CLock() throw(); + + private: + // not implemented + CLock(const CLock&); + CLock& operator=(const CLock&); + + private: + const CMutex* m_mutex; +}; + +#endif diff --git a/mt/CMutex.cpp b/mt/CMutex.cpp new file mode 100644 index 00000000..854d52e5 --- /dev/null +++ b/mt/CMutex.cpp @@ -0,0 +1,123 @@ +#include "CMutex.h" +#include + +// +// CMutex +// + +CMutex::CMutex() +{ + init(); +} + +CMutex::CMutex(const CMutex&) +{ + init(); +} + +CMutex::~CMutex() +{ + fini(); +} + +CMutex& CMutex::operator=(const CMutex&) +{ + return *this; +} + +#if defined(CONFIG_PTHREADS) + +#include +#include + +void CMutex::init() +{ + pthread_mutex_t* mutex = new pthread_mutex_t; + int status = pthread_mutex_init(mutex, NULL); + assert(status == 0); +// status = pthread_mutexattr_settype(mutex, PTHREAD_MUTEX_RECURSIVE); +// assert(status == 0); + m_mutex = reinterpret_cast(mutex); +} + +void CMutex::fini() +{ + pthread_mutex_t* mutex = reinterpret_cast(m_mutex); + int status = pthread_mutex_destroy(mutex); + assert(status == 0); + delete mutex; +} + +void CMutex::lock() const throw() +{ + pthread_mutex_t* mutex = reinterpret_cast(m_mutex); + int status = pthread_mutex_lock(mutex); + + switch (status) { + case 0: + // success + return; + + case EDEADLK: + assert(0 && "lock already owned"); + break; + + case EAGAIN: + assert(0 && "too many recursive locks"); + break; + + default: + assert(0 && "unexpected error"); + } +} + +void CMutex::unlock() const throw() +{ + pthread_mutex_t* mutex = reinterpret_cast(m_mutex); + int status = pthread_mutex_unlock(mutex); + + switch (status) { + case 0: + // success + return; + + case EPERM: + assert(0 && "thread doesn't own a lock"); + break; + + default: + assert(0 && "unexpected error"); + } +} + +#endif // CONFIG_PTHREADS + +#if defined(CONFIG_PLATFORM_WIN32) + +#include + +void CMutex::init() +{ + CRITICAL_SECTION* mutex = new CRITICAL_SECTION; + ::InitializeCriticalSection(mutex); + m_mutex = reinterpret_cast(mutex); +} + +void CMutex::fini() +{ + CRITICAL_SECTION* mutex = reinterpret_cast(m_mutex); + ::DeleteCriticalSection(mutex); + delete mutex; +} + +void CMutex::lock() const throw() +{ + ::EnterCriticalSection(reinterpret_cast(m_mutex)); +} + +void CMutex::unlock() const throw() +{ + ::LeaveCriticalSection(reinterpret_cast(m_mutex)); +} + +#endif // CONFIG_PLATFORM_WIN32 diff --git a/mt/CMutex.h b/mt/CMutex.h new file mode 100644 index 00000000..f9423827 --- /dev/null +++ b/mt/CMutex.h @@ -0,0 +1,35 @@ +#ifndef CMUTEX_H +#define CMUTEX_H + +#include "common.h" + +// recursive mutex class +class CMutex { + public: + // copy c'tor is equivalent to default c'tor. it's here to + // allow copying of objects that have mutexes. + CMutex(); + CMutex(const CMutex&); + ~CMutex(); + + // manipulators + + // this has no effect. it's only here to allow assignment of + // objects that have mutexes. + CMutex& operator=(const CMutex&); + + // accessors + + void lock() const throw(); + void unlock() const throw(); + + private: + void init(); + void fini(); + + private: + friend class CCondVarBase; + void* m_mutex; +}; + +#endif diff --git a/mt/CThread.cpp b/mt/CThread.cpp new file mode 100644 index 00000000..17fd85c1 --- /dev/null +++ b/mt/CThread.cpp @@ -0,0 +1,131 @@ +#include "CThread.h" +#include "CThreadRep.h" +#include "XThread.h" +#include "CLock.h" +#include "CStopwatch.h" + +// +// CThreadPtr +// + +class CThreadPtr { + public: + CThreadPtr(CThreadRep* rep) : m_rep(rep) { } + ~CThreadPtr() { m_rep->unref(); } + + CThreadRep* operator->() const { return m_rep; } + + private: + // not implemented + CThreadPtr(const CThreadPtr&); + CThreadPtr& operator=(const CThreadPtr&); + + private: + CThreadRep* m_rep; +}; + +// +// CThread +// + +CThread::CThread(IJob* job, void* userData) +{ + m_rep = new CThreadRep(job, userData); +} + +CThread::CThread(const CThread& thread) : m_rep(thread.m_rep) +{ + m_rep->ref(); +} + +CThread::CThread(CThreadRep* rep) : m_rep(rep) +{ + // do nothing. rep should have already been Ref()'d. +} + +CThread::~CThread() +{ + m_rep->unref(); +} + +CThread& CThread::operator=(const CThread& thread) +{ + if (thread.m_rep != m_rep) { + m_rep->unref(); + m_rep = thread.m_rep; + m_rep->ref(); + } + return *this; +} + +void CThread::sleep(double timeout) +{ + CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); + if (timeout >= 0.0) { + currentRep->testCancel(); + currentRep->sleep(timeout); + } + currentRep->testCancel(); +} + +void CThread::exit(void* result) +{ + throw XThreadExit(result); +} + +bool CThread::enableCancel(bool enable) +{ + CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); + return currentRep->enableCancel(enable); +} + +void CThread::cancel() +{ + m_rep->cancel(); +} + +void CThread::setPriority(int n) +{ + m_rep->setPriority(n); +} + +CThread CThread::getCurrentThread() +{ + return CThread(CThreadRep::getCurrentThreadRep()); +} + +bool CThread::wait(double timeout) const +{ + CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); + return currentRep->wait(m_rep, timeout); +} + +void CThread::testCancel() +{ + CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); + currentRep->testCancel(); +} + +void* CThread::getResult() const +{ + if (wait()) + return m_rep->getResult(); + else + return NULL; +} + +void* CThread::getUserData() +{ + CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); + return currentRep->getUserData(); +} + +bool CThread::operator==(const CThread& thread) const +{ + return (m_rep == thread.m_rep); +} + +bool CThread::operator!=(const CThread& thread) const +{ + return (m_rep != thread.m_rep); +} diff --git a/mt/CThread.h b/mt/CThread.h new file mode 100644 index 00000000..b5cf6d49 --- /dev/null +++ b/mt/CThread.h @@ -0,0 +1,130 @@ +#ifndef CTHREAD_H +#define CTHREAD_H + +#include "common.h" + +class IJob; +class CThreadRep; + +// note -- do not derive from this class +class CThread { + public: + // create and start a new thread executing the job. + // the user data can be retrieved with getUserData(). + CThread(IJob* adopted, void* userData = 0); + + // make a new thread object that refers to an existing thread. + // this does *not* start a new thread. + CThread(const CThread&); + + // release thread. this does not terminate the thread. a thread + // will keep running until the job completes or calls exit(). + ~CThread(); + + // manipulators + + // assign thread. this has no effect on the threads. it simply + // makes this thread object refer to another thread. it does *not* + // start a new thread. + CThread& operator=(const CThread&); + + // the calling thread sleeps for the given number of seconds. if + // timeout <= 0.0 then the call returns immediately. if timeout + // == 0.0 then the calling thread yields the CPU. + // (cancellation point) + static void sleep(double timeout); + + // terminate the calling thread. this function does not return but + // the stack is unwound and automatic objects are destroyed, as if + // exit() threw an exception (which is, in fact, what it does). the + // argument is saved as the result returned by getResult(). if you + // have a catch(...) block then you should add the following before + // it to avoid catching the exit: catch(CThreadExit&) { throw; } + static void exit(void*); + + // enable/disable cancellation. default is enabled. this is not + // a cancellation point so if you enabled cancellation and want to + // allow immediate cancellation you need to call testCancel(). + // return value is the previous state. + static bool enableCancel(bool); + + // cancel the thread. cancel() never waits for the thread to + // terminate; it just posts the cancel and returns. a thread will + // terminate when it enters a cancellation point with cancellation + // enabled. if cancellation is disabled then the cancel is + // remembered but not acted on until the first call to a + // cancellation point after cancellation is enabled. + // + // a cancellation point is a function that can act on cancellation. + // a cancellation point does not return if there's a cancel pending. + // instead, it unwinds the stack and destroys automatic objects, as + // if cancel() threw an exception (which is, in fact, what it does). + // threads must take care to clean up and release any resources they + // may have, especially mutexes. they can catch (XThreadCancel) to + // do that then rethrow the exception or they can let it happen + // automatically by doing clean up in the d'tors of automatic + // objects. clients are strongly encouraged to do the latter. + // during cancellation, further cancel() calls are ignored (i.e. + // a thread cannot be interrupted by a cancel during cancellation). + // + // clients that catch (XThreadCancel) must always rethrow the + // exception. clients that catch(...) must either rethrow the + // exception or include a catch (XThreadCancel) handler that + // rethrows. + void cancel(); + + // change the priority of the thread. normal priority is 0, 1 is + // the next lower, etc. -1 is the next higher, etc. but boosting + // the priority may not be available. + void setPriority(int n); + + // accessors + + // return a thread object representing the calling thread + static CThread getCurrentThread(); + + // get the user data passed to the constructor for the current + // thread. + static void* getUserData(); + + // testCancel() does nothing but is a cancellation point. call + // this to make a function itself a cancellation point. + // (cancellation point) + static void testCancel(); + + // waits for the thread to terminate (by exit() or cancel() or + // by returning from the thread job). returns immediately if + // the thread has already terminated. returns immediately with + // false if called by a thread on itself. returns false on + // timeout (or error) and true on success. + // (cancellation point) + bool wait(double timeout = -1.0) const; + + // get the exit result. does an implicit wait(). returns NULL + // immediately if called by a thread on itself. returns NULL for + // threads that were cancelled. + // (cancellation point) + void* getResult() const; + + // compare threads for (in)equality + bool operator==(const CThread&) const; + bool operator!=(const CThread&) const; + + private: + CThread(CThreadRep*); + + private: + CThreadRep* m_rep; +}; + +// disables cancellation in the c'tor and enables it in the d'tor. +class CThreadMaskCancel { + public: + CThreadMaskCancel() : m_old(CThread::enableCancel(false)) { } + ~CThreadMaskCancel() { CThread::enableCancel(m_old); } + + private: + bool m_old; +}; + +#endif diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp new file mode 100644 index 00000000..296b50da --- /dev/null +++ b/mt/CThreadRep.cpp @@ -0,0 +1,518 @@ +#include "CThreadRep.h" +#include "CThread.h" +#include "XThread.h" +#include "CLock.h" +#include "IJob.h" +#include + +#if defined(CONFIG_PTHREADS) +#include +#endif + +// FIXME -- temporary exception type +class XThreadUnavailable { }; + +// +// CThreadRep +// + +CMutex CThreadRep::s_mutex; +CThreadRep* CThreadRep::s_head = NULL; + +CThreadRep::CThreadRep() : m_prev(NULL), + m_next(NULL), + m_refCount(1), + m_job(NULL), + m_userData(NULL) +{ + // note -- s_mutex must be locked on entry + + // initialize stuff + init(); +#if defined(CONFIG_PTHREADS) + // get main thread id + m_thread = pthread_self(); + + // install SIGALRM handler + struct sigaction act; + act.sa_handler = &threadCancel; +# if defined(SA_INTERRUPT) + act.sa_flags = SA_INTERRUPT; +# else + act.sa_flags = 0; +# endif + sigemptyset(&act.sa_mask); + sigaction(SIGALRM, &act, NULL); +#elif defined(CONFIG_PLATFORM_WIN32) + // get main thread id + m_thread = NULL; + m_id = GetCurrentThreadId(); +#endif + + // insert ourself into linked list + if (s_head != NULL) { + s_head->m_prev = this; + m_next = s_head; + } + s_head = this; +} + +CThreadRep::CThreadRep(IJob* job, void* userData) : + m_prev(NULL), + m_next(NULL), + m_refCount(2), // 1 for us, 1 for thread + m_job(job), + m_userData(userData) +{ + assert(m_job != NULL); + + // create a thread rep for the main thread if the current thread + // is unknown. note that this might cause multiple "main" threads + // if threads are created external to this library. + getCurrentThreadRep()->unref(); + + // initialize + init(); + + // hold mutex while we create the thread + CLock lock(&s_mutex); + + // start the thread. throw if it doesn't start. +#if defined(CONFIG_PTHREADS) + int status = pthread_create(&m_thread, NULL, threadFunc, (void*)this); + if (status != 0) + throw XThreadUnavailable(); + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); +#elif defined(CONFIG_PLATFORM_WIN32) + unsigned int id; + m_thread = reinterpret_cast(_beginthreadex(NULL, 0, + threadFunc, (void*)this, 0, &id)); + m_id = static_cast(id); + if (m_thread == 0) + throw XThreadUnavailable(); +#endif + + // insert ourself into linked list + if (s_head != NULL) { + s_head->m_prev = this; + m_next = s_head; + } + s_head = this; + + // returning releases the locks, allowing the child thread to run +} + +CThreadRep::~CThreadRep() +{ + // note -- s_mutex must be locked on entry + + // remove ourself from linked list + if (m_prev != NULL) { + m_prev->m_next = m_next; + } + if (m_next != NULL) { + m_next->m_prev = m_prev; + } + if (s_head == this) { + s_head = m_next; + } + + // clean up + fini(); +} + +void CThreadRep::ref() +{ + CLock lock(&s_mutex); + ++m_refCount; +} + +void CThreadRep::unref() +{ + CLock lock(&s_mutex); + if (--m_refCount == 0) { + delete this; + } +} + +bool CThreadRep::enableCancel(bool enable) +{ + CLock lock(&s_mutex); + const bool old = m_cancellable; + m_cancellable = enable; + return old; +} + +bool CThreadRep::isCancellable() const +{ + CLock lock(&s_mutex); + return (m_cancellable && !m_cancelling); +} + +void* CThreadRep::getResult() const +{ + // no lock necessary since thread isn't running + return m_result; +} + +void* CThreadRep::getUserData() const +{ + // no lock necessary because the value never changes + return m_userData; +} + +CThreadRep* CThreadRep::getCurrentThreadRep() +{ +#if defined(CONFIG_PTHREADS) + const pthread_t thread = pthread_self(); +#elif defined(CONFIG_PLATFORM_WIN32) + const DWORD id = GetCurrentThreadId(); +#endif + + // lock list while we search + CLock lock(&s_mutex); + + // search + CThreadRep* scan = s_head; + while (scan != NULL) { +#if defined(CONFIG_PTHREADS) + if (scan->m_thread == thread) { + break; + } +#elif defined(CONFIG_PLATFORM_WIN32) + if (scan->m_id == id) { + break; + } +#endif + scan = scan->m_next; + } + + // create and use main thread rep if thread not found + if (scan == NULL) { + scan = new CThreadRep(); + } + + // ref for caller + ++scan->m_refCount; + + return scan; +} + +void CThreadRep::doThreadFunc() +{ + // default priority is slightly below normal + setPriority(1); + + // wait for parent to initialize this object + { CLock lock(&s_mutex); } + + void* result = NULL; + try { + // go + m_job->run(); + } + + catch (XThreadCancel&) { + // client called cancel() + } + + catch (XThreadExit& e) { + // client called exit() + result = e.m_result; + } + + // note -- don't catch (...) to avoid masking bugs + + // done with job + delete m_job; + + // store exit result (no lock necessary because the result will + // not be accessed until m_exit is set) + m_result = result; +} + +#if defined(CONFIG_PTHREADS) + +#include "CStopwatch.h" +#include + +void CThreadRep::init() +{ + m_result = NULL; + m_cancellable = true; + m_cancelling = false; + m_cancel = false; + m_exit = false; +} + +void CThreadRep::fini() +{ + // main thread has NULL job + if (m_job != NULL) { + pthread_detach(m_thread); + } +} + +void CThreadRep::sleep(double timeout) +{ + if (timeout < 0.0) + return; + struct timespec t; + t.tv_sec = (long)timeout; + t.tv_nsec = (long)(1000000000.0 * (timeout - (double)t.tv_sec)); + nanosleep(&t, NULL); +} + +void CThreadRep::cancel() +{ + CLock lock(&s_mutex); + if (m_cancellable && !m_cancelling) { + m_cancel = true; + + // break out of system calls + pthread_kill(m_thread, SIGALRM); + } +} + +void CThreadRep::testCancel() +{ + { + // prevent further cancellation + CLock lock(&s_mutex); + if (!m_cancel || !m_cancellable || m_cancelling) + return; + + // update state for cancel + m_cancel = false; + m_cancelling = true; + } + + // start cancel + throw XThreadCancel(); +} + +bool CThreadRep::wait(CThreadRep* target, double timeout) +{ + if (target == this) + return false; + + testCancel(); + if (target->isExited()) + return true; + + if (timeout > 0.0) { + CStopwatch timer; + do { + sleep(0.05); + testCancel(); + if (target->isExited()) + return true; + } while (timer.getTime() <= timeout); + } + + return false; +} + +void CThreadRep::setPriority(int) +{ + // FIXME +} + +bool CThreadRep::isExited() const +{ + CLock lock(&s_mutex); + return m_exit; +} + +void* CThreadRep::threadFunc(void* arg) +{ + CThreadRep* rep = (CThreadRep*)arg; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + // run thread + rep->doThreadFunc(); + + // unref thread + rep->unref(); + + // mark as terminated + CLock lock(&s_mutex); + rep->m_exit = true; + + // terminate the thread + return NULL; +} + +void CThreadRep::threadCancel(int) +{ + // do nothing +} + +#elif defined(CONFIG_PLATFORM_WIN32) + +#include + +void CThreadRep::init() +{ + m_result = NULL; + m_cancellable = true; + m_cancelling = false; + m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); + m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +void CThreadRep::fini() +{ + // destroy the events + CloseHandle(m_cancel); + CloseHandle(m_exit); + + // close the handle (main thread has a NULL handle) + if (m_thread != NULL) { + CloseHandle(m_thread); + } +} + +void CThreadRep::sleep(double timeout) +{ + if (isCancellable()) + WaitForSingleObject(m_cancel, (DWORD)(1000.0 * timeout)); + else + ::Sleep((DWORD)(1000.0 * timeout)); +} + +void CThreadRep::cancel() +{ + SetEvent(m_cancel); +} + +void CThreadRep::testCancel() +{ + // poll cancel event. return if not set. + const DWORD result = ::WaitForSingleObject(getCancelEvent(), 0); + if (result != WAIT_OBJECT_0) + return; + + { + // ignore if disabled or already cancelling + CLock lock(&s_mutex); + if (!m_cancellable || m_cancelling) + return; + + // update state for cancel + m_cancelling = true; + ResetEvent(m_cancel); + } + + // start cancel + throw XThreadCancel(); +} + +bool CThreadRep::wait(CThreadRep* target, double timeout) +{ + // get the current thread. if it's the same as the target thread + // then the thread is waiting on itself. + CRefCountedPtr currentRep(CThreadRep::getCurrentThreadRep()); + if (target == this) + return false; + + // is cancellation enabled? + const DWORD n = (isCancellable() ? 2 : 1); + + // convert timeout + DWORD t; + if (timeout < 0.0) + t = INFINITE; + else + t = (DWORD)(1000.0 * timeout); + + // wait for this thread to be cancelled or for the target thread to + // terminate. + HANDLE handles[2]; + handles[0] = target->getExitEvent(); + handles[1] = m_cancel; + DWORD result = ::WaitForMultipleObjects(n, handles, FALSE, t); + + // cancel takes priority + if (n == 2 && result != WAIT_OBJECT_0 + 1 && + ::WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) + result = WAIT_OBJECT_0 + 1; + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 0: + // target thread terminated + return true; + + case WAIT_OBJECT_0 + 1: + // this thread was cancelled. does not return. + testCancel(); + + default: + // error + return false; + } +} + +void CThreadRep::setPriority(int n) +{ + if (n < 0) { + switch (-n) { + case 1: n = THREAD_PRIORITY_ABOVE_NORMAL; break; + default: n = THREAD_PRIORITY_HIGHEST; break; + } + } + else { + switch (n) { + case 0: n = THREAD_PRIORITY_NORMAL; break; + case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; + case 2: n = THREAD_PRIORITY_LOWEST; break; + default: n = THREAD_PRIORITY_IDLE; break; + } + } + SetThreadPriority(m_thread, n); +} + +HANDLE CThreadRep::getExitEvent() const +{ + // no lock necessary because the value never changes + return m_exit; +} + +HANDLE CThreadRep::getCancelEvent() const +{ + // no lock necessary because the value never changes + return m_cancel; +} + +unsigned int __stdcall CThreadRep::threadFunc(void* arg) +{ + CThreadRep* rep = (CThreadRep*)arg; + + // initialize OLE + const HRESULT hr = ::OleInitialize(NULL); + + // run thread + rep->doThreadFunc(); + + // close OLE + if (!FAILED(hr)) { + OleUninitialize(); + } + + // signal termination + SetEvent(rep->m_exit); + + // unref thread + rep->unref(); + + // terminate the thread + return 0; +} + +#endif diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h new file mode 100644 index 00000000..89bb6131 --- /dev/null +++ b/mt/CThreadRep.h @@ -0,0 +1,122 @@ +#ifndef CTHREADREP_H +#define CTHREADREP_H + +#include "CMutex.h" +#include "BasicTypes.h" + +#if defined(CONFIG_PTHREADS) +#include +#elif defined(CONFIG_PLATFORM_WIN32) +#include +#endif + +class IJob; + +class CThreadRep { + public: + CThreadRep(IJob*, void* userData); + + // manipulators + + // change ref count + void ref(); + void unref(); + + // the calling thread sleeps for t seconds. if t == 0.0 then + // the thread yields the CPU. + void sleep(double timeout); + + // cancel the thread + void cancel(); + + // set cancellation state + bool enableCancel(bool enable); + + // permanently disable further cancellation and start cancel cleanup + // if cancel has been called and cancellation hasn't been started yet. + void testCancel(); + + // wait for thread to exit or for current thread to cancel + bool wait(CThreadRep*, double timeout); + + // set the priority + void setPriority(int n); + + // accessors + + // get the exit result for this thread. thread must be terminated. + void* getResult() const; + + // get the user data passed to the constructor + void* getUserData() const; + + // get the current cancellable state + bool isCancellable() const; + +#if defined(CONFIG_PTHREADS) + bool isExited() const; +#elif defined(CONFIG_PLATFORM_WIN32) + HANDLE getExitEvent() const; + HANDLE getCancelEvent() const; +#endif + + // return the thread rep for the calling thread. the returned + // rep has been ref()'d. + static CThreadRep* getCurrentThreadRep(); + + protected: + virtual ~CThreadRep(); + + private: + // internal constructor + CThreadRep(); + + // initialization/cleanup + void init(); + void fini(); + + // thread rep lookup + static CThreadRep* find(); + + // thread functions +#if defined(CONFIG_PTHREADS) + static void* threadFunc(void* arg); + static void threadCancel(int); +#elif defined(CONFIG_PLATFORM_WIN32) + static unsigned int __stdcall threadFunc(void* arg); +#endif + void doThreadFunc(); + + // not implemented + CThreadRep(const CThreadRep&); + CThreadRep& operator=(const CThreadRep&); + + private: + static CMutex s_mutex; + static CThreadRep* s_head; + + CThreadRep* m_prev; + CThreadRep* m_next; + + SInt32 m_refCount; + IJob* m_job; + void* m_userData; + void* m_result; + bool m_cancellable; + bool m_cancelling; + +#if defined(CONFIG_PTHREADS) + pthread_t m_thread; + bool m_exit; + bool m_cancel; +#endif + +#if defined(CONFIG_PLATFORM_WIN32) + HANDLE m_thread; + DWORD m_id; + HANDLE m_exit; + HANDLE m_cancel; +#endif +}; + +#endif diff --git a/mt/CTimerThread.cpp b/mt/CTimerThread.cpp new file mode 100644 index 00000000..afe2925c --- /dev/null +++ b/mt/CTimerThread.cpp @@ -0,0 +1,30 @@ +#include "CTimerThread.h" +#include "CThread.h" +#include "TMethodJob.h" +#include + +// +// CTimerThread +// + +CTimerThread::CTimerThread(double timeout) : m_timeout(timeout) +{ + assert(m_timeout > 0.0); + m_callingThread = new CThread(CThread::getCurrentThread()); + m_timingThread = new CThread(new TMethodJob( + this, &CTimerThread::timer)); +} + +CTimerThread::~CTimerThread() +{ + m_timingThread->cancel(); + delete m_timingThread; + delete m_callingThread; +} + +void CTimerThread::timer(void*) +{ + CThread::sleep(m_timeout); + m_callingThread->cancel(); +} + diff --git a/mt/CTimerThread.h b/mt/CTimerThread.h new file mode 100644 index 00000000..48ca64cb --- /dev/null +++ b/mt/CTimerThread.h @@ -0,0 +1,27 @@ +#ifndef CTIMERTHREAD_H +#define CTIMERTHREAD_H + +#include "common.h" + +class CThread; + +class CTimerThread { + public: + CTimerThread(double timeout); + ~CTimerThread(); + + private: + void timer(void*); + + // not implemented + CTimerThread(const CTimerThread&); + CTimerThread& operator=(const CTimerThread&); + + private: + double m_timeout; + CThread* m_callingThread; + CThread* m_timingThread; +}; + +#endif + diff --git a/mt/Makefile b/mt/Makefile new file mode 100644 index 00000000..1a7270c8 --- /dev/null +++ b/mt/Makefile @@ -0,0 +1,28 @@ +DEPTH=.. +include $(DEPTH)/Makecommon + +# +# target file +# +TARGET = mt + +# +# source files +# +LCXXINCS = \ + -I$(DEPTH)/base \ + $(NULL) +CXXFILES = \ + CLock.cpp \ + CMutex.cpp \ + CCondVar.cpp \ + CThread.cpp \ + CThreadRep.cpp \ + CTimerThread.cpp \ + $(NULL) + +targets: $(LIBTARGET) + +$(LIBTARGET): $(OBJECTS) + if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi + $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/mt/XThread.h b/mt/XThread.h new file mode 100644 index 00000000..3fd7441f --- /dev/null +++ b/mt/XThread.h @@ -0,0 +1,31 @@ +#ifndef XTHREAD_H +#define XTHREAD_H + +#include "common.h" + +// generic thread exception +class XThread { }; + +// thrown by CThread::Exit() to exit a thread. clients of CThread +// must not throw this type but must rethrow it if caught (by +// XThreadExit, XThread, or ...). +class XThreadExit : public XThread { + public: + XThreadExit(void* result) : m_result(result) { } + ~XThreadExit() { } + + public: + void* m_result; +}; + +// thrown to cancel a thread. clients must not throw this type, but +// must rethrow it if caught (by XThreadCancel, XThread, or ...). +class XThreadCancel : public XThread { }; + +// convenience macro to rethrow an XThread exception but ignore other +// exceptions. put this in your catch (...) handler after necessary +// cleanup but before leaving or returning from the handler. +#define RETHROW_XTHREAD \ + try { throw; } catch (XThread&) { throw; } catch (...) { } + +#endif diff --git a/net/CNetworkAddress.cpp b/net/CNetworkAddress.cpp new file mode 100644 index 00000000..024965ce --- /dev/null +++ b/net/CNetworkAddress.cpp @@ -0,0 +1,65 @@ +#include "CNetworkAddress.h" +#include +#include +#include +#include + +// +// CNetworkAddress +// + +CNetworkAddress::CNetworkAddress(UInt16 port) throw(XSocketAddress) +{ + if (port == 0) + throw XSocketAddress(XSocketAddress::kBadPort, CString(), port); + + struct sockaddr_in* inetAddress = reinterpret_cast(&m_address); + inetAddress->sin_family = AF_INET; + inetAddress->sin_port = htons(port); + inetAddress->sin_addr.s_addr = INADDR_ANY; + ::memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); +} + +CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) + throw(XSocketAddress) +{ + if (port == 0) + throw XSocketAddress(XSocketAddress::kBadPort, hostname, port); + + struct hostent* hent = gethostbyname(hostname.c_str()); + if (hent == NULL) { + switch (h_errno) { + case HOST_NOT_FOUND: + throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); + + case NO_DATA: + throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); + + case NO_RECOVERY: + case TRY_AGAIN: + default: + throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); + } + } + + struct sockaddr_in* inetAddress = reinterpret_cast(&m_address); + inetAddress->sin_family = hent->h_addrtype; + inetAddress->sin_port = htons(port); + ::memcpy(&inetAddress->sin_addr, hent->h_addr_list[0], hent->h_length); + ::memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); +} + +CNetworkAddress::~CNetworkAddress() +{ + // do nothing +} + +const struct sockaddr* CNetworkAddress::getAddress() const throw() +{ + return &m_address; +} + +int CNetworkAddress::getAddressLength() const throw() +{ + return sizeof(m_address); +} diff --git a/net/CNetworkAddress.h b/net/CNetworkAddress.h new file mode 100644 index 00000000..f9394b32 --- /dev/null +++ b/net/CNetworkAddress.h @@ -0,0 +1,27 @@ +#ifndef CNETWORKADDRESS_H +#define CNETWORKADDRESS_H + +#include "BasicTypes.h" +#include "XSocket.h" +#include + +class CString; + +class CNetworkAddress { + public: + CNetworkAddress(UInt16 port) throw(XSocketAddress); + CNetworkAddress(const CString& hostname, UInt16 port) throw(XSocketAddress); + ~CNetworkAddress(); + + // manipulators + + // accessors + + const struct sockaddr* getAddress() const throw(); + int getAddressLength() const throw(); + + private: + struct sockaddr m_address; +}; + +#endif diff --git a/net/CSocketInputStream.cpp b/net/CSocketInputStream.cpp new file mode 100644 index 00000000..d6f9d7ae --- /dev/null +++ b/net/CSocketInputStream.cpp @@ -0,0 +1,93 @@ +#include "CSocketInputStream.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "IJob.h" +#include "XIO.h" +#include +#include + +// +// CSocketInputStream +// + +CSocketInputStream::CSocketInputStream(CMutex* mutex, IJob* closeCB) : + m_mutex(mutex), + m_empty(mutex, true), + m_closeCB(closeCB), + m_closed(false), + m_hungup(false) +{ + assert(m_mutex != NULL); +} + +CSocketInputStream::~CSocketInputStream() +{ + delete m_closeCB; +} + +void CSocketInputStream::write( + const void* data, UInt32 n) throw() +{ + if (!m_hungup && n > 0) { + m_buffer.write(data, n); + m_empty = (m_buffer.getSize() == 0); + m_empty.broadcast(); + } +} + +void CSocketInputStream::hangup() throw() +{ + m_hungup = true; + m_empty.broadcast(); +} + +void CSocketInputStream::close() throw(XIO) +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_closed = true; + if (m_closeCB) { + m_closeCB->run(); + } +} + +UInt32 CSocketInputStream::read( + void* dst, UInt32 n) throw(XIO) +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + // wait for data (or hangup) + while (!m_hungup && m_empty == true) { + m_empty.wait(); + } + + // read data + const UInt32 count = m_buffer.getSize(); + if (n > count) { + n = count; + } + if (n > 0) { + ::memcpy(dst, m_buffer.peek(n), n); + m_buffer.pop(n); + } + + // update empty state + if (m_buffer.getSize() == 0) { + m_empty = true; + m_empty.broadcast(); + } + return n; +} + +UInt32 CSocketInputStream::getSize() const throw() +{ + CLock lock(m_mutex); + return m_buffer.getSize(); +} diff --git a/net/CSocketInputStream.h b/net/CSocketInputStream.h new file mode 100644 index 00000000..46cb1c5b --- /dev/null +++ b/net/CSocketInputStream.h @@ -0,0 +1,42 @@ +#ifndef CSOCKETINPUTSTREAM_H +#define CSOCKETINPUTSTREAM_H + +#include "CSocketStreamBuffer.h" +#include "CCondVar.h" +#include "IInputStream.h" + +class CMutex; +class IJob; + +class CSocketInputStream : public IInputStream { + public: + CSocketInputStream(CMutex*, IJob* adoptedCloseCB); + ~CSocketInputStream(); + + // manipulators + + // write() appends n bytes to the buffer + void write(const void*, UInt32 n) throw(); + + // causes read() to always return immediately. if there is no + // more data then it returns 0. further writes are discarded. + void hangup() throw(); + + // accessors + + // IInputStream overrides + // these all lock the mutex for their duration + virtual void close() throw(XIO); + virtual UInt32 read(void*, UInt32 count) throw(XIO); + virtual UInt32 getSize() const throw(); + + private: + CMutex* m_mutex; + CCondVar m_empty; + IJob* m_closeCB; + CSocketStreamBuffer m_buffer; + bool m_closed; + bool m_hungup; +}; + +#endif diff --git a/net/CSocketOutputStream.cpp b/net/CSocketOutputStream.cpp new file mode 100644 index 00000000..a1d36c18 --- /dev/null +++ b/net/CSocketOutputStream.cpp @@ -0,0 +1,78 @@ +#include "CSocketOutputStream.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "IJob.h" +#include "XIO.h" +#include + +// +// CSocketOutputStream +// + +CSocketOutputStream::CSocketOutputStream(CMutex* mutex, IJob* closeCB) : + m_mutex(mutex), + m_closeCB(closeCB), + m_closed(false) +{ + assert(m_mutex != NULL); +} + +CSocketOutputStream::~CSocketOutputStream() +{ + delete m_closeCB; +} + +const void* CSocketOutputStream::peek(UInt32 n) throw() +{ + return m_buffer.peek(n); +} + +void CSocketOutputStream::pop(UInt32 n) throw() +{ + m_buffer.pop(n); +} + +UInt32 CSocketOutputStream::getSize() const throw() +{ + return m_buffer.getSize(); +} + +void CSocketOutputStream::close() throw(XIO) +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_closed = true; + if (m_closeCB) { + m_closeCB->run(); + } +} + +UInt32 CSocketOutputStream::write( + const void* data, UInt32 n) throw(XIO) +{ + CLock lock(m_mutex); + if (m_closed) { + throw XIOClosed(); + } + + m_buffer.write(data, n); + return n; +} + +void CSocketOutputStream::flush() throw(XIO) +{ + // wait until all data is written + while (getSizeWithLock() > 0) { + CThread::sleep(0.05); + } +} + +UInt32 CSocketOutputStream::getSizeWithLock() const throw() +{ + CLock lock(m_mutex); + return m_buffer.getSize(); +} diff --git a/net/CSocketOutputStream.h b/net/CSocketOutputStream.h new file mode 100644 index 00000000..d55443c7 --- /dev/null +++ b/net/CSocketOutputStream.h @@ -0,0 +1,43 @@ +#ifndef CSOCKETOUTPUTSTREAM_H +#define CSOCKETOUTPUTSTREAM_H + +#include "CSocketStreamBuffer.h" +#include "IOutputStream.h" + +class CMutex; +class IJob; + +class CSocketOutputStream : public IOutputStream { + public: + CSocketOutputStream(CMutex*, IJob* adoptedCloseCB); + ~CSocketOutputStream(); + + // manipulators + + // peek() returns a buffer of n bytes (which must be <= getSize()). + // pop() discards the next n bytes. + const void* peek(UInt32 n) throw(); + void pop(UInt32 n) throw(); + + // accessors + + // return the number of bytes in the buffer + UInt32 getSize() const throw(); + + // IOutputStream overrides + // these all lock the mutex for their duration + virtual void close() throw(XIO); + virtual UInt32 write(const void*, UInt32 count) throw(XIO); + virtual void flush() throw(XIO); + + private: + UInt32 getSizeWithLock() const throw(); + + private: + CMutex* m_mutex; + IJob* m_closeCB; + CSocketStreamBuffer m_buffer; + bool m_closed; +}; + +#endif diff --git a/net/CSocketStreamBuffer.cpp b/net/CSocketStreamBuffer.cpp new file mode 100644 index 00000000..0a2848ce --- /dev/null +++ b/net/CSocketStreamBuffer.cpp @@ -0,0 +1,106 @@ +#include "CSocketStreamBuffer.h" +#include + +// +// CSocketStreamBuffer +// + +const UInt32 CSocketStreamBuffer::kChunkSize = 4096; + +CSocketStreamBuffer::CSocketStreamBuffer() : m_size(0) +{ + // do nothing +} + +CSocketStreamBuffer::~CSocketStreamBuffer() +{ + // do nothing +} + +const void* CSocketStreamBuffer::peek(UInt32 n) throw() +{ + assert(n <= m_size); + + // reserve space in first chunk + ChunkList::iterator head = m_chunks.begin(); + head->reserve(n); + + // consolidate chunks into the first chunk until it has n bytes + ChunkList::iterator scan = head; + ++scan; + while (head->size() < n && scan != m_chunks.end()) { + head->insert(head->end(), scan->begin(), scan->end()); + scan = m_chunks.erase(scan); + } + + return reinterpret_cast(head->begin()); +} + +void CSocketStreamBuffer::pop(UInt32 n) throw() +{ + m_size -= n; + + // discard chunks until more than n bytes would've been discarded + ChunkList::iterator scan = m_chunks.begin(); + while (scan->size() <= n && scan != m_chunks.end()) { + n -= scan->size(); + scan = m_chunks.erase(scan); + } + + // if there's anything left over then remove it from the head chunk. + // if there's no head chunk then we're already empty. + if (scan == m_chunks.end()) { + m_size = 0; + } + else if (n > 0) { + scan->erase(scan->begin(), scan->begin() + n); + } +} + +void CSocketStreamBuffer::write( + const void* vdata, UInt32 n) throw() +{ + assert(vdata != NULL); + + if (n == 0) { + return; + } + m_size += n; + + // cast data to bytes + const UInt8* data = reinterpret_cast(vdata); + + // point to last chunk if it has space, otherwise append an empty chunk + ChunkList::iterator scan = m_chunks.end(); + if (scan != m_chunks.begin()) { + --scan; + if (scan->size() >= kChunkSize) + ++scan; + } + if (scan == m_chunks.end()) { + scan = m_chunks.insert(scan); + } + + // append data in chunks + while (n > 0) { + // choose number of bytes for next chunk + UInt32 count = kChunkSize - scan->size(); + if (count > n) + count = n; + + // transfer data + scan->insert(scan->end(), data, data + count); + n -= count; + data += count; + + // append another empty chunk if we're not done yet + if (n > 0) { + scan = m_chunks.insert(scan); + } + } +} + +UInt32 CSocketStreamBuffer::getSize() const throw() +{ + return m_size; +} diff --git a/net/CSocketStreamBuffer.h b/net/CSocketStreamBuffer.h new file mode 100644 index 00000000..ea6234b4 --- /dev/null +++ b/net/CSocketStreamBuffer.h @@ -0,0 +1,38 @@ +#ifndef CSOCKETSTREAMBUFFER_H +#define CSOCKETSTREAMBUFFER_H + +#include "BasicTypes.h" +#include +#include + +class CSocketStreamBuffer { + public: + CSocketStreamBuffer(); + ~CSocketStreamBuffer(); + + // manipulators + + // peek() returns a buffer of n bytes (which must be <= getSize()). + // pop() discards the next n bytes. + const void* peek(UInt32 n) throw(); + void pop(UInt32 n) throw(); + + // write() appends n bytes to the buffer + void write(const void*, UInt32 n) throw(); + + // accessors + + // return the number of bytes in the buffer + UInt32 getSize() const throw(); + + private: + static const UInt32 kChunkSize; + + typedef std::vector Chunk; + typedef std::list ChunkList; + + ChunkList m_chunks; + UInt32 m_size; +}; + +#endif diff --git a/net/CTCPListenSocket.cpp b/net/CTCPListenSocket.cpp new file mode 100644 index 00000000..959e8c52 --- /dev/null +++ b/net/CTCPListenSocket.cpp @@ -0,0 +1,72 @@ +#include "CTCPListenSocket.h" +#include "CTCPSocket.h" +#include "CNetworkAddress.h" +#include "CThread.h" +#include +#include +#include +#include +#include + +// +// CTCPListenSocket +// + +CTCPListenSocket::CTCPListenSocket() +{ + m_fd = socket(PF_INET, SOCK_STREAM, 0); + if (m_fd == -1) { + throw XSocketCreate(); + } +} + +CTCPListenSocket::~CTCPListenSocket() +{ + try { + close(); + } + catch (...) { + // ignore + } +} + +void CTCPListenSocket::bind( + const CNetworkAddress& addr) throw(XSocket) +{ + if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { + if (errno == EADDRINUSE) { + throw XSocketAddressInUse(); + } + throw XSocketBind(); + } + if (listen(m_fd, 3) == -1) { + throw XSocketBind(); + } +} + +ISocket* CTCPListenSocket::accept() throw(XSocket) +{ + for (;;) { + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + CThread::testCancel(); + int fd = ::accept(m_fd, &addr, &addrlen); + if (fd == -1) { + CThread::testCancel(); + } + else { + return new CTCPSocket(fd); + } + } +} + +void CTCPListenSocket::close() throw(XIO) +{ + if (m_fd == -1) { + throw XIOClosed(); + } + if (::close(m_fd) == -1) { + throw XIOClose(); + } + m_fd = -1; +} diff --git a/net/CTCPListenSocket.h b/net/CTCPListenSocket.h new file mode 100644 index 00000000..8807fba8 --- /dev/null +++ b/net/CTCPListenSocket.h @@ -0,0 +1,24 @@ +#ifndef CTCPLISTENSOCKET_H +#define CTCPLISTENSOCKET_H + +#include "IListenSocket.h" + +class CTCPListenSocket : public IListenSocket { + public: + CTCPListenSocket(); + ~CTCPListenSocket(); + + // manipulators + + // accessors + + // IListenSocket overrides + virtual void bind(const CNetworkAddress&) throw(XSocket); + virtual ISocket* accept() throw(XSocket); + virtual void close() throw(XIO); + + private: + int m_fd; +}; + +#endif diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp new file mode 100644 index 00000000..86a1b0bd --- /dev/null +++ b/net/CTCPSocket.cpp @@ -0,0 +1,226 @@ +#include "CTCPSocket.h" +#include "CBufferedInputStream.h" +#include "CBufferedOutputStream.h" +#include "CNetworkAddress.h" +#include "CLock.h" +#include "CMutex.h" +#include "CCondVar.h" +#include "CThread.h" +#include "TMethodJob.h" +#include "CStopwatch.h" +#include +#include +#include +#include +#include +#include +#include + +// +// CTCPSocket +// + +CTCPSocket::CTCPSocket() throw(XSocket) +{ + m_fd = socket(PF_INET, SOCK_STREAM, 0); + if (m_fd == -1) { + throw XSocketCreate(); + } + init(); +} + +CTCPSocket::CTCPSocket(int fd) throw() : + m_fd(fd) +{ + assert(m_fd != -1); + + init(); + + // socket starts in connected state + m_connected = kReadWrite; + + // start handling socket + m_thread = new CThread(new TMethodJob( + this, &CTCPSocket::service)); +} + +CTCPSocket::~CTCPSocket() +{ + try { + close(); + } + catch (...) { + // ignore failures + } + + // clean up + delete m_mutex; + delete m_input; + delete m_output; +} + +void CTCPSocket::bind(const CNetworkAddress& addr) + throw(XSocket) +{ + if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { + if (errno == EADDRINUSE) { + throw XSocketAddressInUse(); + } + throw XSocketBind(); + } +} + +void CTCPSocket::connect(const CNetworkAddress& addr) + throw(XSocket) +{ + CThread::testCancel(); + if (::connect(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { + CThread::testCancel(); + throw XSocketConnect(); + } + + // start servicing the socket + m_connected = kReadWrite; + m_thread = new CThread(new TMethodJob( + this, &CTCPSocket::service)); +} + +void CTCPSocket::close() throw(XIO) +{ + // shutdown I/O thread before close + if (m_thread != NULL) { + // flush if output buffer not empty and output buffer not closed + bool doFlush; + { + CLock lock(m_mutex); + doFlush = ((m_connected & kWrite) != 0); + } + if (doFlush) { + m_output->flush(); + } + + m_thread->cancel(); + m_thread->wait(); + delete m_thread; + m_thread = NULL; + } + + CLock lock(m_mutex); + if (m_fd != -1) { + if (::close(m_fd) == -1) { + throw XIOClose(); + } + m_fd = -1; + } +} + +IInputStream* CTCPSocket::getInputStream() throw() +{ + return m_input; +} + +IOutputStream* CTCPSocket::getOutputStream() throw() +{ + return m_output; +} + +void CTCPSocket::init() throw(XIO) +{ + m_mutex = new CMutex; + m_thread = NULL; + m_connected = kClosed; + m_input = new CBufferedInputStream(m_mutex, + new TMethodJob( + this, &CTCPSocket::closeInput)); + m_output = new CBufferedOutputStream(m_mutex, + new TMethodJob( + this, &CTCPSocket::closeOutput)); +} + +void CTCPSocket::service(void*) throw(XThread) +{ + assert(m_fd != -1); + + // now service the connection + struct pollfd pfds[1]; + pfds[0].fd = m_fd; + for (;;) { + { + // choose events to poll for + CLock lock(m_mutex); + pfds[0].events = 0; + if ((m_connected & kRead) != 0) { + // still open for reading + pfds[0].events |= POLLIN; + } + if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) { + // data queued for writing + pfds[0].events |= POLLOUT; + } + } + + // check for status + CThread::testCancel(); + const int status = poll(pfds, 1, 50); + CThread::testCancel(); + + // transfer data and handle errors + if (status == 1) { + if ((pfds[0].revents & (POLLERR | POLLNVAL)) != 0) { + // stream is no good anymore so bail + m_input->hangup(); + return; + } + + // read some data + if (pfds[0].revents & POLLIN) { + UInt8 buffer[4096]; + ssize_t n = read(m_fd, buffer, sizeof(buffer)); + if (n > 0) { + CLock lock(m_mutex); + m_input->write(buffer, n); + } + else if (n == 0) { + // stream hungup + m_input->hangup(); + return; + } + } + + // write some data + if (pfds[0].revents & POLLOUT) { + CLock lock(m_mutex); + + // get amount of data to write + UInt32 n = m_output->getSize(); + if (n > 4096) { + // limit write size + n = 4096; + } + + // write data + const void* buffer = m_output->peek(n); + n = write(m_fd, buffer, n); + + // discard written data + if (n > 0) { + m_output->pop(n); + } + } + } + } +} + +void CTCPSocket::closeInput(void*) throw() +{ + // note -- m_mutex should already be locked + shutdown(m_fd, 0); + m_connected &= ~kRead; +} + +void CTCPSocket::closeOutput(void*) throw() +{ + // note -- m_mutex should already be locked + shutdown(m_fd, 1); + m_connected &= ~kWrite; +} diff --git a/net/CTCPSocket.h b/net/CTCPSocket.h new file mode 100644 index 00000000..8218bbad --- /dev/null +++ b/net/CTCPSocket.h @@ -0,0 +1,49 @@ +#ifndef CTCPSOCKET_H +#define CTCPSOCKET_H + +#include "ISocket.h" +#include "XThread.h" + +class CMutex; +template +class CCondVar; +class CThread; +class CBufferedInputStream; +class CBufferedOutputStream; + +class CTCPSocket : public ISocket { + public: + CTCPSocket() throw(XSocket); + CTCPSocket(int fd) throw(); + ~CTCPSocket(); + + // manipulators + + // accessors + + // ISocket overrides + virtual void bind(const CNetworkAddress&) throw(XSocket); + virtual void connect(const CNetworkAddress&) throw(XSocket); + virtual void close() throw(XIO); + virtual IInputStream* getInputStream() throw(); + virtual IOutputStream* getOutputStream() throw(); + + private: + void init() throw(XIO); + void service(void*) throw(XThread); + void closeInput(void*) throw(); + void closeOutput(void*) throw(); + + private: + enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 }; + + int m_fd; + CBufferedInputStream* m_input; + CBufferedOutputStream* m_output; + + CMutex* m_mutex; + CThread* m_thread; + UInt32 m_connected; +}; + +#endif diff --git a/net/IListenSocket.h b/net/IListenSocket.h new file mode 100644 index 00000000..eb8d7903 --- /dev/null +++ b/net/IListenSocket.h @@ -0,0 +1,27 @@ +#ifndef ILISTENSOCKET_H +#define ILISTENSOCKET_H + +#include "IInterface.h" +#include "XIO.h" +#include "XSocket.h" + +class CNetworkAddress; +class ISocket; + +class IListenSocket : public IInterface { + public: + // manipulators + + // bind the socket to a particular address + virtual void bind(const CNetworkAddress&) throw(XSocket) = 0; + + // wait for a connection + virtual ISocket* accept() throw(XSocket) = 0; + + // close the socket + virtual void close() throw(XIO) = 0; + + // accessors +}; + +#endif diff --git a/net/ISocket.h b/net/ISocket.h new file mode 100644 index 00000000..98d1fd45 --- /dev/null +++ b/net/ISocket.h @@ -0,0 +1,35 @@ +#ifndef ISOCKET_H +#define ISOCKET_H + +#include "IInterface.h" +#include "BasicTypes.h" +#include "XSocket.h" +#include "XIO.h" + +class CNetworkAddress; +class IInputStream; +class IOutputStream; + +class ISocket : public IInterface { + public: + // manipulators + + // bind the socket to a particular address + virtual void bind(const CNetworkAddress&) throw(XSocket) = 0; + + // connect the socket + virtual void connect(const CNetworkAddress&) throw(XSocket) = 0; + + // close the socket. this will flush the output stream if it + // hasn't been closed yet. + virtual void close() throw(XIO) = 0; + + // get the input and output streams for the socket. closing + // these streams closes the appropriate half of the socket. + virtual IInputStream* getInputStream() throw() = 0; + virtual IOutputStream* getOutputStream() throw() = 0; + + // accessors +}; + +#endif diff --git a/net/Makefile b/net/Makefile new file mode 100644 index 00000000..5b9534f1 --- /dev/null +++ b/net/Makefile @@ -0,0 +1,28 @@ +DEPTH=.. +include $(DEPTH)/Makecommon + +# +# target file +# +TARGET = net + +# +# source files +# +LCXXINCS = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + $(NULL) +CXXFILES = \ + XSocket.cpp \ + CNetworkAddress.cpp \ + CTCPSocket.cpp \ + CTCPListenSocket.cpp \ + $(NULL) + +targets: $(LIBTARGET) + +$(LIBTARGET): $(OBJECTS) + if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi + $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/net/XSocket.cpp b/net/XSocket.cpp new file mode 100644 index 00000000..d5478908 --- /dev/null +++ b/net/XSocket.cpp @@ -0,0 +1,84 @@ +#include "XSocket.h" + +// +// XSocketAddress +// + +XSocketAddress::XSocketAddress(Error error, + const CString& hostname, SInt16 port) throw() : + m_error(error), + m_hostname(hostname), + m_port(port) +{ + // do nothing +} + +XSocketAddress::Error XSocketAddress::getError() const throw() +{ + return m_error; +} + +CString XSocketAddress::getHostname() const throw() +{ + return m_hostname; +} + +SInt16 XSocketAddress::getPort() const throw() +{ + return m_port; +} + +CString XSocketAddress::getWhat() const throw() +{ + return "no address"; +/* + return format("XSocketAddress", "no address: %1:%2", + m_hostname.t_str(), + CString::sprintf("%d", m_port).t_str()); +*/ +} + + +// +// XSocketErrno +// + +XSocketErrno::XSocketErrno() : MXErrno() +{ + // do nothing +} + +XSocketErrno::XSocketErrno(int err) : MXErrno(err) +{ + // do nothing +} + + +// +// XSocketBind +// + +CString XSocketBind::getWhat() const throw() +{ + return format("XSocketBind", "cannot bind address"); +} + + +// +// XSocketConnect +// + +CString XSocketConnect::getWhat() const throw() +{ + return format("XSocketConnect", "cannot connect socket"); +} + + +// +// XSocketCreate +// + +CString XSocketCreate::getWhat() const throw() +{ + return format("XSocketCreate", "cannot create socket"); +} diff --git a/net/XSocket.h b/net/XSocket.h new file mode 100644 index 00000000..578009fa --- /dev/null +++ b/net/XSocket.h @@ -0,0 +1,58 @@ +#ifndef XSOCKET_H +#define XSOCKET_H + +#include "CString.h" +#include "XBase.h" +#include "BasicTypes.h" + +class XSocket : public XBase { }; + +class XSocketAddress : public XSocket { + public: + enum Error { kUnknown, kNotFound, kNoAddress, kBadPort }; + + XSocketAddress(Error, const CString& hostname, SInt16 port) throw(); + + // accessors + + virtual Error getError() const throw(); + virtual CString getHostname() const throw(); + virtual SInt16 getPort() const throw(); + + protected: + // XBase overrides + virtual CString getWhat() const throw(); + + private: + Error m_error; + CString m_hostname; + SInt16 m_port; +}; + +class XSocketErrno : public XSocket, public MXErrno { + public: + XSocketErrno(); + XSocketErrno(int); +}; + +class XSocketBind : public XSocketErrno { + protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +class XSocketAddressInUse : public XSocketBind { }; + +class XSocketConnect : public XSocketErrno { + protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +class XSocketCreate : public XSocketErrno { + protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +#endif diff --git a/notes b/notes new file mode 100644 index 00000000..0effd884 --- /dev/null +++ b/notes @@ -0,0 +1,85 @@ +throw specs: + must add XThread if method can be cancelled + must add out-of-memory exception if method allocs and returns an object + should catch and handle out-of-mem if object wasn't going to be returned + +CServer + * must have connection thread in screen info map + allows disconnect if screen map changes and screen is removed + * put mutex locks wherever necessary (like accessing m_active) + +CClient + * need thread to handle screen + * need methods for screen event handler to call as appropriate + +server client +------ ------ +[accept] <-- connect +challenge --> [encrypt] +[verify] <-- response (encrypted challenge, client name) +hangup if invalid +query info --> + <-- info (size) + +... +enter (x,y) --> +clipboard data --> optional +mouse/key events --> optional +query clipboard --> optional + <-- clipboard data (cont.) +leave --> + +... +grab clipboard --> + +... (on clipboard ownership stolen) + <-- clipboard lost + +... (on screen resize) + <-- info (size) + +... (on screen saver, primary screen) +saver (on/off) --> + +... +quit --> + <-- close + +--- +primary screen + open + close + enter + leave + warp + clipboard (get/set) + screen saver (show/hide) + queue events with server (including screen saver activation) + +secondary screen + open + close + enter + leave + warp + synth mouse + synth key + clipboard (get/set) + screen saver (show/hide) + queue events with client (clipboard lost/changed, size change) + +--- +client: + open + close + wait: server messages, clipboard taken, screen resize, quit + +server: + accept + asynchronously accept new clients + config + asynchronously accept and handle config message (via HTTP) + primary + asynchronously handle primary screen events + comm + send/recv messages to/from clients diff --git a/synergy/CClient.cpp b/synergy/CClient.cpp new file mode 100644 index 00000000..bd4e011b --- /dev/null +++ b/synergy/CClient.cpp @@ -0,0 +1,265 @@ +#include "CClient.h" +#include "CInputPacketStream.h" +#include "COutputPacketStream.h" +#include "CProtocolUtil.h" +#include "CTimerThread.h" +#include + +// +// CClient +// + +CClient::CClient(const CString& clientName) : + m_name(clientName) +{ +} + +CClient::~CClient() +{ +} + +#include "CTCPSocket.h" +void CClient::run(const CNetworkAddress& serverAddress) +{ + std::auto_ptr socket; + std::auto_ptr input; + std::auto_ptr output; + try { + // allow connect this much time to succeed + CTimerThread timer(30.0); // FIXME -- timeout in member + + // create socket and attempt to connect to server + socket.reset(new CTCPSocket()); // FIXME -- use factory + socket->connect(serverAddress); + + // get the input and output streams + IInputStream* srcInput = socket->getInputStream(); + IOutputStream* srcOutput = socket->getOutputStream(); + + // attach the encryption layer +/* FIXME -- implement ISecurityFactory + if (m_securityFactory != NULL) { + input.reset(m_securityFactory->createInputFilter(srcInput, false)); + output.reset(m_securityFactory->createOutputFilter(srcOutput, false)); + srcInput = input.get(); + srcOutput = output.get(); + } +*/ + + // attach the packetizing filters + input.reset(new CInputPacketStream(srcInput, true)); + output.reset(new COutputPacketStream(srcOutput, true)); + + // wait for hello from server + SInt32 major, minor; + CProtocolUtil::readf(input.get(), "Synergy%2i%2i", &major, &minor); + + // check versions + if (major < kMajorVersion || + (major == kMajorVersion && minor < kMinorVersion)) { + throw XIncompatibleClient(major, minor); + } + + // say hello back + CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s", + kMajorVersion, kMinorVersion, + m_name.size(), m_name.data()); + + // record streams in a more useful place + m_input = input.get(); + m_output = output.get(); + } + catch (XIncompatibleClient& e) { + fprintf(stderr, "incompatible server version (%d.%d)\n", + e.getMajor(), e.getMinor()); + return; + } + catch (XThread&) { + fprintf(stderr, "connection timed out\n"); + throw; + } + catch (XBase& e) { + fprintf(stderr, "connection failed: %s\n", e.what()); + return; + } + + // connect to screen + // FIXME -- make object that closes and destroys screen in + // it's d'tor. screen must not be handling event queue by + // the time the streams are destroyed. + + // handle messages from server + try { + for (;;) { + // wait for reply + UInt8 code[4]; + UInt32 n = input->read(code, 4); + + // verify we got an entire code + if (n == 0) { + // server hungup + break; + } + if (n != 4) { + // client sent an incomplete message + fprintf(stderr, "incomplete message from server\n"); + break; + } + + // parse message + if (memcmp(code, kMsgDMouseMove, 4) == 0) { + onMouseMove(input.get()); + } + else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { + onMouseWheel(input.get()); + } + else if (memcmp(code, kMsgDKeyDown, 4) == 0) { + onKeyDown(input.get()); + } + else if (memcmp(code, kMsgDKeyUp, 4) == 0) { + onKeyUp(input.get()); + } + else if (memcmp(code, kMsgDMouseDown, 4) == 0) { + onMouseDown(input.get()); + } + else if (memcmp(code, kMsgDMouseUp, 4) == 0) { + onMouseUp(input.get()); + } + else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { + onKeyRepeat(input.get()); + } + else if (memcmp(code, kMsgCEnter, 4) == 0) { + onEnter(input.get()); + } + else if (memcmp(code, kMsgCLeave, 4) == 0) { + onLeave(input.get()); + } + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + onGrabClipboard(input.get()); + } + else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { + onScreenSaver(input.get()); + } + else if (memcmp(code, kMsgQInfo, 4) == 0) { + onQueryInfo(input.get()); + } + else if (memcmp(code, kMsgQClipboard, 4) == 0) { + onQueryClipboard(input.get()); + } + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + onSetClipboard(input.get()); + } + else if (memcmp(code, kMsgCClose, 4) == 0) { + // server wants us to hangup + break; + } + else { + // unknown message + fprintf(stderr, "unknown message from server\n"); + break; + } + } + + // done with socket + m_socket->close(); + } + catch (XBase& e) { + fprintf(stderr, "error: %s\n", e.what()); + return; + } +} + +void CClient::onEnter() +{ + SInt32 x, y; + CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y); + m_screen->enter(x, y); +} + +void CClient::onLeave() +{ + m_screen->leave(); +} + +void CClient::onGrabClipboard() +{ + // FIXME +} + +void CClient::onScreenSaver() +{ + SInt32 on; + CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on); + // FIXME +} + +void CClient::onQueryInfo() +{ + SInt32 w, h; + m_screen->getSize(w, h); + SInt32 zoneSize = m_screen->getJumpZoneSize(); + CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); +} + +void CClient::onQueryClipboard() +{ + // FIXME +} + +void CClient::onSetClipboard() +{ + // FIXME +} + +void CClient::onKeyDown() +{ + SInt32 id, mask; + CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); + m_screen->onKeyDown(reinterpret_cast(id), + reinterpret_cast(mask)); +} + +void CClient::onKeyRepeat() +{ + SInt32 id, mask, count; + CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); + m_screen->onKeyRepeat(reinterpret_cast(id), + reinterpret_cast(mask), + count); +} + +void CClient::onKeyUp() +{ + SInt32 id, mask; + CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); + m_screen->onKeyUp(reinterpret_cast(id), + reinterpret_cast(mask)); +} + +void CClient::onMouseDown() +{ + SInt32 id; + CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); + m_screen->onMouseDown(reinterpret_cast(id)); +} + +void CClient::onMouseUp() +{ + SInt32 id; + CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); + m_screen->onMouseUp(reinterpret_cast(id)); +} + +void CClient::onMouseMove() +{ + SInt32 x, y; + CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); + m_screen->onMouseMove(x, y); +} + +void CClient::onMouseWheel() +{ + SInt32 delta; + CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); + m_screen->onMouseWheel(delta); +} diff --git a/synergy/CClient.h b/synergy/CClient.h new file mode 100644 index 00000000..7ad0b7f7 --- /dev/null +++ b/synergy/CClient.h @@ -0,0 +1,46 @@ +#ifndef CCLIENT_H +#define CCLIENT_H + +#include "CString.h" +#include "BasicTypes.h" + +class CNetworkAddress; +class IInputStream; +class IOutputStream; + +class CClient { + public: + CClient(const CString& clientName); + ~CClient(); + + // manipulators + + void run(const CNetworkAddress& serverAddress); + + // accessors + + + private: + // message handlers + void onEnter(); + void onLeave(); + void onGrabClipboard(); + void onScreenSaver(); + void onQueryInfo(); + void onQueryClipboard(); + void onSetClipboard(); + void onKeyDown(); + void onKeyRepeat(); + void onKeyUp(); + void onMouseDown(); + void onMouseUp(); + void onMouseMove(); + void onMouseWheel(); + + private: + CString m_name; + IInputStream* m_output; + IOutputStream* m_output; +}; + +#endif diff --git a/synergy/CInputPacketStream.cpp b/synergy/CInputPacketStream.cpp new file mode 100644 index 00000000..c7642e45 --- /dev/null +++ b/synergy/CInputPacketStream.cpp @@ -0,0 +1,109 @@ +#include "CInputPacketStream.h" +#include "CLock.h" +#include + +// +// CInputPacketStream +// + +CInputPacketStream::CInputPacketStream(IInputStream* stream, bool adopt) : + CInputStreamFilter(stream, adopt), + m_mutex(), + m_size(0), + m_buffer(&m_mutex, NULL) +{ + // do nothing +} + +CInputPacketStream::~CInputPacketStream() +{ + // do nothing +} + +void CInputPacketStream::close() throw(XIO) +{ + getStream()->close(); +} + +UInt32 CInputPacketStream::read( + void* buffer, UInt32 n) throw(XIO) +{ + CLock lock(&m_mutex); + + // wait for entire message to be read. return immediately if + // stream hungup. + if (getSizeNoLock() == 0) { + return 0; + } + + // limit number of bytes to read to the number of bytes left in the + // current message. + if (n > m_size) { + n = m_size; + } + + // now read from our buffer + n = m_buffer.readNoLock(buffer, n); + assert(n <= m_size); + m_size -= n; + + return n; +} + +UInt32 CInputPacketStream::getSize() const throw() +{ + CLock lock(&m_mutex); + return getSizeNoLock(); +} + +UInt32 CInputPacketStream::getSizeNoLock() const throw() +{ + while (!hasFullMessage()) { + // read more data + char buffer[4096]; + UInt32 n = getStream()->read(buffer, sizeof(buffer)); + + // return if stream hungup + if (n == 0) { + m_buffer.hangup(); + return 0; + } + + // append to our buffer + m_buffer.write(buffer, n); + } + + return m_size; +} + +bool CInputPacketStream::hasFullMessage() const throw() +{ + // get payload length if we don't have it yet + if (m_size == 0) { + // check if length field has been read yet + if (m_buffer.getSizeNoLock() < 4) { + // not enough data for payload length + return false; + } + + // save payload length + UInt8 buffer[4]; + UInt32 n = m_buffer.readNoLock(buffer, sizeof(buffer)); + assert(n == 4); + m_size = ((UInt32)buffer[0] << 24) | + ((UInt32)buffer[1] << 16) | + ((UInt32)buffer[2] << 8) | + (UInt32)buffer[3]; + + // if payload length is zero then discard null message + if (m_size == 0) { + return false; + } + } + assert(m_size > 0); + + // we have the full message when we have at least m_size bytes in + // the buffer + return (m_buffer.getSizeNoLock() >= m_size); +} + diff --git a/synergy/CInputPacketStream.h b/synergy/CInputPacketStream.h new file mode 100644 index 00000000..5bd31772 --- /dev/null +++ b/synergy/CInputPacketStream.h @@ -0,0 +1,33 @@ +#ifndef CINPUTPACKETSTREAM_H +#define CINPUTPACKETSTREAM_H + +#include "CInputStreamFilter.h" +#include "CBufferedInputStream.h" +#include "CMutex.h" + +class CInputPacketStream : public CInputStreamFilter { + public: + CInputPacketStream(IInputStream*, bool adoptStream = true); + ~CInputPacketStream(); + + // manipulators + + // accessors + + // IInputStream overrides + virtual void close() throw(XIO); + virtual UInt32 read(void*, UInt32 maxCount) throw(XIO); + virtual UInt32 getSize() const throw(); + + private: + UInt32 getSizeNoLock() const throw(); + bool hasFullMessage() const throw(); + + private: + CMutex m_mutex; + mutable UInt32 m_size; + mutable CBufferedInputStream m_buffer; +}; + +#endif + diff --git a/synergy/COutputPacketStream.cpp b/synergy/COutputPacketStream.cpp new file mode 100644 index 00000000..cffd050a --- /dev/null +++ b/synergy/COutputPacketStream.cpp @@ -0,0 +1,56 @@ +#include "COutputPacketStream.h" + +// +// COuputPacketStream +// + +COutputPacketStream::COutputPacketStream(IOutputStream* stream, bool adopt) : + COutputStreamFilter(stream, adopt) +{ + // do nothing +} + +COutputPacketStream::~COutputPacketStream() +{ + // do nothing +} + +void COutputPacketStream::close() throw(XIO) +{ + getStream()->close(); +} + +UInt32 COutputPacketStream::write( + const void* buffer, UInt32 count) throw(XIO) +{ + // write the length of the payload + UInt8 length[4]; + length[0] = (UInt8)((count >> 24) & 0xff); + length[1] = (UInt8)((count >> 16) & 0xff); + length[2] = (UInt8)((count >> 8) & 0xff); + length[3] = (UInt8)( count & 0xff); + UInt32 count2 = sizeof(length); + const UInt8* cbuffer = length; + while (count2 > 0) { + UInt32 n = getStream()->write(cbuffer, count2); + cbuffer += n; + count2 -= n; + } + + // write the payload + count2 = count; + cbuffer = reinterpret_cast(buffer); + while (count2 > 0) { + UInt32 n = getStream()->write(cbuffer, count2); + cbuffer += n; + count2 -= n; + } + + return count; +} + +void COutputPacketStream::flush() throw(XIO) +{ + getStream()->flush(); +} + diff --git a/synergy/COutputPacketStream.h b/synergy/COutputPacketStream.h new file mode 100644 index 00000000..20293386 --- /dev/null +++ b/synergy/COutputPacketStream.h @@ -0,0 +1,21 @@ +#ifndef COUTPUTPACKETSTREAM_H +#define COUTPUTPACKETSTREAM_H + +#include "COutputStreamFilter.h" + +class COutputPacketStream : public COutputStreamFilter { + public: + COutputPacketStream(IOutputStream*, bool adoptStream = true); + ~COutputPacketStream(); + + // manipulators + + // accessors + + // IOutputStream overrides + virtual void close() throw(XIO); + virtual UInt32 write(const void*, UInt32 count) throw(XIO); + virtual void flush() throw(XIO); +}; + +#endif diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp new file mode 100644 index 00000000..7ebed7b2 --- /dev/null +++ b/synergy/CProtocolUtil.cpp @@ -0,0 +1,336 @@ +#include "CProtocolUtil.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include +#include +#include + +// +// CProtocolUtil +// + +void CProtocolUtil::writef(IOutputStream* stream, + const char* fmt, ...) throw(XIO) +{ + assert(stream != NULL); + assert(fmt != NULL); + + va_list args; + + // determine total size to write + va_start(args, fmt); + UInt32 count = getLength(fmt, args); + va_end(args); + + // done if nothing to write + if (count == 0) { + return; + } + + // fill buffer + UInt8* buffer = new UInt8[count]; + va_start(args, fmt); + writef(buffer, fmt, args); + va_end(args); + + // write buffer + UInt8* scan = buffer; + while (count > 0) { + const UInt32 n = stream->write(scan, n); + count -= n; + scan += n; + } + + delete[] buffer; +} + +void CProtocolUtil::readf(IInputStream* stream, + const char* fmt, ...) throw(XIO) +{ + assert(stream != NULL); + assert(fmt != NULL); + + va_list args; + + // begin scanning + while (*fmt) { + if (*fmt == '%') { + // format specifier. determine argument size. + ++fmt; + UInt32 len = eatLength(&fmt); + switch (*fmt) { + case 'i': { + // check for valid length + assert(len == 1 || len == 2 || len == 4); + + // read the data + UInt8 buffer[4]; + read(stream, buffer, len); + + // convert it + UInt32* v = va_arg(args, UInt32*); + switch (len) { + case 1: + // 1 byte integer + *v = static_cast(buffer[0]); + break; + + case 2: + // 2 byte integer + *v = (static_cast(buffer[0]) << 8) | + static_cast(buffer[1]); + break; + + case 4: + // 4 byte integer + *v = (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); + break; + } + break; + } + + case 's': { + assert(len == 0); + + // read the string length + UInt8 buffer[128]; + read(stream, buffer, 4); + UInt32 len = (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); + + // use a fixed size buffer if its big enough + const bool useFixed = (len <= sizeof(buffer)); + + // allocate a buffer to read the data + UInt8* sBuffer; + if (useFixed) { + sBuffer = buffer; + } + else { + sBuffer = new UInt8[len]; + } + + // read the data + try { + read(stream, sBuffer, len); + } + catch (...) { + if (!useFixed) { + delete[] sBuffer; + } + throw; + } + + // save the data + CString* dst = va_arg(args, CString*); + dst->assign((const char*)sBuffer, len); + + // release the buffer + if (!useFixed) { + delete[] sBuffer; + } + break; + } + + case '%': + assert(len == 0); + break; + + default: + assert(0 && "invalid format specifier"); + } + + // next format character + ++fmt; + } + else { + // read next character + char buffer[1]; + read(stream, buffer, 1); + + // verify match + if (buffer[0] != *fmt) { + throw XIOReadMismatch(); + } + + // next format character + ++fmt; + } + } +} + +UInt32 CProtocolUtil::getLength( + const char* fmt, va_list args) throw() +{ + UInt32 n = 0; + while (*fmt) { + if (*fmt == '%') { + // format specifier. determine argument size. + ++fmt; + UInt32 len = eatLength(&fmt); + switch (*fmt) { + case 'i': + assert(len == 1 || len == 2 || len == 4); + (void)va_arg(args, UInt32); + break; + + case 's': + assert(len == 0); + len = va_arg(args, UInt32) + 4; + (void)va_arg(args, UInt8*); + break; + + case '%': + assert(len == 0); + len = 1; + break; + + default: + assert(0 && "invalid format specifier"); + } + + // accumulate size + n += len; + ++fmt; + } + else { + // regular character + ++n; + ++fmt; + } + } + return n; +} + +void CProtocolUtil::writef(void* buffer, + const char* fmt, va_list args) throw(XIO) +{ + UInt8* dst = reinterpret_cast(buffer); + + while (*fmt) { + if (*fmt == '%') { + // format specifier. determine argument size. + ++fmt; + UInt32 len = eatLength(&fmt); + switch (*fmt) { + case 'i': { + const UInt32 v = va_arg(args, UInt32); + switch (len) { + case 1: + // 1 byte integer + *dst++ = static_cast(v & 0xff); + break; + + case 2: + // 2 byte integer + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + break; + + case 4: + // 4 byte integer + *dst++ = static_cast((v >> 24) & 0xff); + *dst++ = static_cast((v >> 16) & 0xff); + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + break; + + default: + assert(0 && "invalid integer format length"); + return; + } + break; + } + + case 's': { + assert(len == 0); + const UInt32 len = va_arg(args, UInt32); + const UInt8* src = va_arg(args, UInt8*); + *dst++ = static_cast((len >> 24) & 0xff); + *dst++ = static_cast((len >> 16) & 0xff); + *dst++ = static_cast((len >> 8) & 0xff); + *dst++ = static_cast( len & 0xff); + memcpy(dst, src, len); + dst += len; + break; + } + + case '%': + assert(len == 0); + *dst++ = '%'; + break; + + default: + assert(0 && "invalid format specifier"); + } + + // next format character + ++fmt; + } + else { + // copy regular character + *dst++ = *fmt++; + } + } +} + +UInt32 CProtocolUtil::eatLength(const char** pfmt) throw() +{ + const char* fmt = *pfmt; + UInt32 n = 0; + for (;;) { + UInt32 d; + switch (*fmt) { + case '0': d = 0; break; + case '1': d = 1; break; + case '2': d = 2; break; + case '3': d = 3; break; + case '4': d = 4; break; + case '5': d = 5; break; + case '6': d = 6; break; + case '7': d = 7; break; + case '8': d = 8; break; + case '9': d = 9; break; + default: *pfmt = fmt; return n; + } + n = 10 * n + d; + ++fmt; + } +} + +void CProtocolUtil::read(IInputStream* stream, + void* vbuffer, UInt32 count) throw(XIO) +{ + assert(stream != NULL); + assert(vbuffer != NULL); + + UInt8* buffer = reinterpret_cast(vbuffer); + while (count > 0) { + // read more + UInt32 n = stream->read(buffer, count); + + // bail if stream has hungup + if (n == 0) { + throw XIOEndOfStream(); + } + + // prepare for next read + buffer += n; + count -= n; + } +} + + +// +// XIOReadMismatch +// + +CString XIOReadMismatch::getWhat() const throw() +{ + return "CProtocolUtil::readf() mismatch"; +} diff --git a/synergy/CProtocolUtil.h b/synergy/CProtocolUtil.h new file mode 100644 index 00000000..346accda --- /dev/null +++ b/synergy/CProtocolUtil.h @@ -0,0 +1,53 @@ +#ifndef CPROTOCOLUTIL_H +#define CPROTOCOLUTIL_H + +#include "BasicTypes.h" +#include "XIO.h" +#include + +class IInputStream; +class IOutputStream; + +class CProtocolUtil { + public: + // write formatted binary data to a stream. fmt consists of + // regular characters and format specifiers. format specifiers + // begin with %. all characters not part of a format specifier + // are regular and are transmitted unchanged. + // + // format specifiers are: + // %% -- writes % + // %1i -- converts integer argument to 1 byte integer + // %2i -- converts integer argument to 2 byte integer in NBO + // %4i -- converts integer argument to 4 byte integer in NBO + // %s -- converts integer N and const UInt8* to stream of N bytes + static void writef(IOutputStream*, + const char* fmt, ...) throw(XIO); + + // read formatted binary data from a buffer. this performs the + // reverse operation of writef(). + // + // format specifiers are: + // %% -- read (and discard) a % + // %1i -- reads a 1 byte integer; argument is a SInt32* or UInt32* + // %2i -- reads an NBO 2 byte integer; arg is SInt32* or UInt32* + // %4i -- reads an NBO 4 byte integer; arg is SInt32* or UInt32* + // %s -- reads bytes; argument must be a CString*, *not* a char* + static void readf(IInputStream*, + const char* fmt, ...) throw(XIO); + + private: + static UInt32 getLength(const char* fmt, va_list) throw(); + static void writef(void*, const char* fmt, va_list) throw(XIO); + static UInt32 eatLength(const char** fmt) throw(); + static void read(IInputStream*, void*, UInt32) throw(XIO); +}; + +class XIOReadMismatch : public XIO { + public: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +#endif + diff --git a/synergy/CScreenMap.cpp b/synergy/CScreenMap.cpp new file mode 100644 index 00000000..7e59ea1d --- /dev/null +++ b/synergy/CScreenMap.cpp @@ -0,0 +1,89 @@ +#include "CScreenMap.h" +#include + +// +// CScreenMap +// + +CScreenMap::CScreenMap() +{ + // do nothing +} + +CScreenMap::~CScreenMap() +{ + // do nothing +} + +void CScreenMap::addScreen(const CString& name) +{ + if (m_map.count(name) != 0) { + assert(0 && "name already in map"); // FIXME -- throw instead + } + m_map.insert(std::make_pair(name, CCell())); +} + +void CScreenMap::removeScreen(const CString& name) +{ + CCellMap::iterator index = m_map.find(name); + if (index == m_map.end()) { + assert(0 && "name not in map"); // FIXME -- throw instead + } + + // remove from map + m_map.erase(index); + + // disconnect + for (index = m_map.begin(); index != m_map.end(); ++index) { + CCell& cell = index->second; + for (SInt32 i = 0; kLastDirection - kFirstDirection; ++i) + if (cell.m_neighbor[i] == name) { + cell.m_neighbor[i].erase(); + } + } +} + +void CScreenMap::removeAllScreens() +{ + m_map.clear(); +} + +void CScreenMap::connect(const CString& srcName, + EDirection srcSide, + const CString& dstName) +{ + // find source cell + CCellMap::iterator index = m_map.find(srcName); + if (index == m_map.end()) { + assert(0 && "name not in map"); // FIXME -- throw instead + } + + // connect side (overriding any previous connection) + index->second.m_neighbor[srcSide - kFirstDirection] = dstName; +} + +void CScreenMap::disconnect(const CString& srcName, + EDirection srcSide) +{ + // find source cell + CCellMap::iterator index = m_map.find(srcName); + if (index == m_map.end()) { + assert(0 && "name not in map"); // FIXME -- throw instead + } + + // disconnect side + index->second.m_neighbor[srcSide - kFirstDirection].erase(); +} + +CString CScreenMap::getNeighbor(const CString& srcName, + EDirection srcSide) const throw() +{ + // find source cell + CCellMap::const_iterator index = m_map.find(srcName); + if (index == m_map.end()) { + assert(0 && "name not in map"); // FIXME -- throw instead + } + + // return connection + return index->second.m_neighbor[srcSide - kFirstDirection]; +} diff --git a/synergy/CScreenMap.h b/synergy/CScreenMap.h new file mode 100644 index 00000000..234ae05b --- /dev/null +++ b/synergy/CScreenMap.h @@ -0,0 +1,46 @@ +#ifndef CSCREENMAP_H +#define CSCREENMAP_H + +#include "BasicTypes.h" +#include "CString.h" +#include + +class CScreenMap { + public: + enum EDirection { kLeft, kRight, kTop, kBottom, + kFirstDirection = kLeft, kLastDirection = kBottom }; + + CScreenMap(); + virtual ~CScreenMap(); + + // manipulators + + // add/remove screens + void addScreen(const CString& name); + void removeScreen(const CString& name); + void removeAllScreens(); + + // connect edges + void connect(const CString& srcName, + EDirection srcSide, + const CString& dstName); + void disconnect(const CString& srcName, + EDirection srcSide); + + // accessors + + // get the neighbor in the given direction. returns the empty string + // if there is no neighbor in that direction. + CString getNeighbor(const CString&, EDirection) const throw(); + + private: + class CCell { + public: + CString m_neighbor[kLastDirection - kFirstDirection + 1]; + }; + typedef std::map CCellMap; + + CCellMap m_map; +}; + +#endif diff --git a/synergy/CServer.cpp b/synergy/CServer.cpp new file mode 100644 index 00000000..28699806 --- /dev/null +++ b/synergy/CServer.cpp @@ -0,0 +1,849 @@ +#include "CServer.h" +#include "CInputPacketStream.h" +#include "COutputPacketStream.h" +#include "CServerProtocol.h" +#include "CProtocolUtil.h" +#include "IPrimaryScreen.h" +#include "ISocketFactory.h" +#include "ProtocolTypes.h" +#include "XSynergy.h" +#include "CNetworkAddress.h" +#include "ISocket.h" +#include "IListenSocket.h" +#include "XSocket.h" +#include "CLock.h" +#include "CThread.h" +#include "CTimerThread.h" +#include "CStopwatch.h" +#include "TMethodJob.h" +#include +#include +#include + +// +// CServer +// + +CServer::CServer() : m_done(&m_mutex, false) +{ + m_socketFactory = NULL; + m_securityFactory = NULL; + m_bindTimeout = 5.0 * 60.0; +} + +CServer::~CServer() +{ +} + +void CServer::run() +{ + try { + // connect to primary screen + openPrimaryScreen(); + + // start listening for new clients + CThread(new TMethodJob(this, &CServer::acceptClients)); + + // start listening for configuration connections + // FIXME + + // wait until done + CLock lock(&m_mutex); + while (m_done == false) { + m_done.wait(); + } + + // clean up + closePrimaryScreen(); + cleanupThreads(); + } + catch (XBase& e) { + fprintf(stderr, "server error: %s\n", e.what()); + + // clean up + closePrimaryScreen(); + cleanupThreads(); + } + catch (...) { + // clean up + closePrimaryScreen(); + cleanupThreads(); + throw; + } +} + +void CServer::setScreenMap(const CScreenMap& screenMap) +{ + CLock lock(&m_mutex); + // FIXME -- must disconnect screens no longer listed + // (that may include warping back to server's screen) + // FIXME -- server screen must be in new map or map is rejected + m_screenMap = screenMap; +} + +void CServer::getScreenMap(CScreenMap* screenMap) const +{ + assert(screenMap != NULL); + + CLock lock(&m_mutex); + *screenMap = m_screenMap; +} + +void CServer::setInfo(const CString& client, + SInt32 w, SInt32 h, SInt32 zoneSize) throw() +{ + CLock lock(&m_mutex); + + // client must be connected + CScreenList::iterator index = m_screens.find(client); + if (index == m_screens.end()) { + throw XBadClient(); + } + + // update client info + CScreenInfo* info = index->second; + if (info == m_active) { + // FIXME -- ensure mouse is still on screen. warp it if necessary. + } + info->m_width = w; + info->m_height = h; + info->m_zoneSize = zoneSize; +} + +bool CServer::onCommandKey(KeyID /*id*/, + KeyModifierMask /*mask*/, bool /*down*/) +{ + return false; +} + +void CServer::onKeyDown(KeyID id, KeyModifierMask mask) +{ + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, true)) { + return; + } + + // relay + if (m_active->m_protocol != NULL) { + m_active->m_protocol->sendKeyDown(id, mask); + } +} + +void CServer::onKeyUp(KeyID id, KeyModifierMask mask) +{ + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, false)) { + return; + } + + // relay + if (m_active->m_protocol != NULL) { + m_active->m_protocol->sendKeyUp(id, mask); + } +} + +void CServer::onKeyRepeat(KeyID id, KeyModifierMask mask) +{ + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, false)) { + onCommandKey(id, mask, true); + return; + } + + // relay + if (m_active->m_protocol != NULL) { + m_active->m_protocol->sendKeyRepeat(id, mask); + } +} + +void CServer::onMouseDown(ButtonID id) +{ + assert(m_active != NULL); + + // relay + if (m_active->m_protocol != NULL) { + m_active->m_protocol->sendMouseDown(id); + } +} + +void CServer::onMouseUp(ButtonID id) +{ + assert(m_active != NULL); + + // relay + if (m_active->m_protocol != NULL) { + m_active->m_protocol->sendMouseUp(id); + } +} + +void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) +{ + // mouse move on primary (server's) screen + assert(m_active != NULL); + assert(m_active->m_protocol == NULL); + + // ignore if mouse is locked to screen + if (isLockedToScreen()) { + return; + } + + // see if we should change screens + CScreenMap::EDirection dir; + if (x < m_active->m_zoneSize) { + x -= m_active->m_zoneSize; + dir = CScreenMap::kLeft; + } + else if (x >= m_active->m_width - m_active->m_zoneSize) { + x += m_active->m_zoneSize; + dir = CScreenMap::kRight; + } + else if (y < m_active->m_zoneSize) { + y -= m_active->m_zoneSize; + dir = CScreenMap::kTop; + } + else if (y >= m_active->m_height - m_active->m_zoneSize) { + y += m_active->m_zoneSize; + dir = CScreenMap::kBottom; + } + else { + // still on local screen + return; + } + + // get jump destination + CScreenInfo* newScreen = getNeighbor(m_active, dir, x, y); + + // if no screen in jump direction then ignore the move + if (newScreen == NULL) { + return; + } + + // remap position to account for resolution differences + mapPosition(m_active, dir, newScreen, x, y); + + // switch screen + switchScreen(newScreen, x, y); +} + +void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) +{ + // mouse move on secondary (client's) screen + assert(m_active != NULL); + assert(m_active->m_protocol != NULL); + + // save old position + const SInt32 xOld = m_x; + const SInt32 yOld = m_y; + + // accumulate motion + m_x += dx; + m_y += dy; + + // switch screens if the mouse is outside the screen and not + // locked to the screen + CScreenInfo* newScreen = NULL; + if (!isLockedToScreen()) { + // find direction of neighbor + CScreenMap::EDirection dir; + if (m_x < 0) + dir = CScreenMap::kLeft; + else if (m_x > m_active->m_width - 1) + dir = CScreenMap::kRight; + else if (m_y < 0) + dir = CScreenMap::kTop; + else if (m_y > m_active->m_height - 1) + dir = CScreenMap::kBottom; + else + newScreen = m_active; + + // get neighbor if we should switch + if (newScreen == NULL) { +// TRACE(("leave %s on %s", m_activeScreen->getName().c_str(), +// s_dirName[dir])); + + SInt32 x = m_x, y = m_y; + newScreen = getNeighbor(m_active, dir, x, y); + + // remap position to account for resolution differences + if (newScreen != NULL) { + mapPosition(m_active, dir, newScreen, x, y); + m_x = x; + m_y = y; + } + else { + if (m_x < 0) + m_x = 0; + else if (m_x > m_active->m_width - 1) + m_x = m_active->m_width - 1; + if (m_y < 0) + m_y = 0; + else if (m_y > m_active->m_height - 1) + m_y = m_active->m_height - 1; + } + } + } + else { + // clamp to edge when locked +// TRACE(("clamp to %s", m_activeScreen->getName().c_str())); + if (m_x < 0) + m_x = 0; + else if (m_x > m_active->m_width - 1) + m_x = m_active->m_width - 1; + if (m_y < 0) + m_y = 0; + else if (m_y > m_active->m_height - 1) + m_y = m_active->m_height - 1; + } + + // warp cursor if on same screen + if (newScreen == NULL || newScreen == m_active) { + // do nothing if mouse didn't move + if (m_x != xOld || m_y != yOld) { +// TRACE(("move on %s to %d,%d", +// m_activeScreen->getName().c_str(), m_x, m_y)); + m_active->m_protocol->sendMouseMove(m_x, m_y); + } + } + + // otherwise screen screens + else { + switchScreen(newScreen, m_x, m_y); + } +} + +void CServer::onMouseWheel(SInt32 delta) +{ + assert(m_active != NULL); + + // relay + if (m_active->m_protocol != NULL) { + m_active->m_protocol->sendMouseWheel(delta); + } +} + +bool CServer::isLockedToScreen() const +{ + // FIXME + return false; +} + +void CServer::switchScreen(CScreenInfo* dst, + SInt32 x, SInt32 y) +{ + assert(dst != NULL); + assert(x >= 0 && y >= 0 && x < dst->m_width && y < dst->m_height); + assert(m_active != NULL); + +// TRACE(("switch %s to %s at %d,%d", m_active->m_name.c_str(), +// dst->m_name.c_str(), x, y)); + + // wrapping means leaving the active screen and entering it again. + // since that's a waste of time we skip that and just warp the + // mouse. + if (m_active != dst) { + // leave active screen + if (m_active->m_protocol == NULL) { + m_primary->leave(); + } + else { + m_active->m_protocol->sendLeave(); + } + + // cut over + m_active = dst; + + // enter new screen + if (m_active->m_protocol == NULL) { + m_primary->enter(x, y); + } + else { + m_active->m_protocol->sendEnter(x, y); + } + } + else { + if (m_active->m_protocol == NULL) { + m_primary->warpCursor(x, y); + } + else { + m_active->m_protocol->sendMouseMove(x, y); + } + } + + // record new position + m_x = x; + m_y = y; +} + +CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, + CScreenMap::EDirection dir) const +{ + assert(src != NULL); + + CString srcName = src->m_name; + assert(!srcName.empty()); + for (;;) { + // look up name of neighbor + const CString dstName(m_screenMap.getNeighbor(srcName, dir)); + + // if nothing in that direction then return NULL + if (dstName.empty()) + return NULL; + + // look up neighbor cell. if the screen is connected then + // we can stop. otherwise we skip over an unconnected + // screen. + CScreenList::const_iterator index = m_screens.find(dstName); + if (index != m_screens.end()) { + return index->second; + } + + srcName = dstName; + } +} + +CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, + CScreenMap::EDirection srcSide, + SInt32& x, SInt32& y) const +{ + assert(src != NULL); + + // get the first neighbor + CScreenInfo* dst = getNeighbor(src, srcSide); + CScreenInfo* lastGoodScreen = dst; + + // get the source screen's size (needed for kRight and kBottom) + SInt32 w = src->m_width, h = src->m_height; + + // find destination screen, adjusting x or y (but not both) + switch (srcSide) { + case CScreenMap::kLeft: + while (dst != NULL) { + lastGoodScreen = dst; + w = lastGoodScreen->m_width; + h = lastGoodScreen->m_height; + x += w; + if (x >= 0) { + break; + } +// TRACE(("skipping over screen %s", dst->m_name.c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + break; + + case CScreenMap::kRight: + while (dst != NULL) { + lastGoodScreen = dst; + x -= w; + w = lastGoodScreen->m_width; + h = lastGoodScreen->m_height; + if (x < w) { + break; + } +// TRACE(("skipping over screen %s", dst->m_name.c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + break; + + case CScreenMap::kTop: + while (dst != NULL) { + lastGoodScreen = dst; + w = lastGoodScreen->m_width; + h = lastGoodScreen->m_height; + y += h; + if (y >= 0) { + break; + } +// TRACE(("skipping over screen %s", dst->m_name.c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + break; + + case CScreenMap::kBottom: + while (dst != NULL) { + lastGoodScreen = dst; + y -= h; + w = lastGoodScreen->m_width; + h = lastGoodScreen->m_height; + if (y < h) { + break; + } +// TRACE(("skipping over screen %s", dst->m_name.c_str())); + dst = getNeighbor(lastGoodScreen, srcSide); + } + break; + } + + // if entering primary screen then be sure to move in far enough + // to avoid the jump zone. if entering a side that doesn't have + // a neighbor (i.e. an asymmetrical side) then we don't need to + // move inwards because that side can't provoke a jump. + assert(lastGoodScreen != NULL); + if (lastGoodScreen->m_protocol == NULL) { + const CString dstName(lastGoodScreen->m_name); + switch (srcSide) { + case CScreenMap::kLeft: + if (!m_screenMap.getNeighbor(dstName, CScreenMap::kRight).empty() && + x > w - 1 - lastGoodScreen->m_zoneSize) + x = w - 1 - lastGoodScreen->m_zoneSize; + break; + + case CScreenMap::kRight: + if (!m_screenMap.getNeighbor(dstName, CScreenMap::kLeft).empty() && + x < lastGoodScreen->m_zoneSize) + x = lastGoodScreen->m_zoneSize; + break; + + case CScreenMap::kTop: + if (!m_screenMap.getNeighbor(dstName, CScreenMap::kBottom).empty() && + y > h - 1 - lastGoodScreen->m_zoneSize) + y = h - 1 - lastGoodScreen->m_zoneSize; + break; + + case CScreenMap::kBottom: + if (!m_screenMap.getNeighbor(dstName, CScreenMap::kTop).empty() && + y < lastGoodScreen->m_zoneSize) + y = lastGoodScreen->m_zoneSize; + break; + } + } + + return lastGoodScreen; +} + +void CServer::mapPosition(CScreenInfo* src, + CScreenMap::EDirection srcSide, + CScreenInfo* dst, + SInt32& x, SInt32& y) const +{ + assert(src != NULL); + assert(dst != NULL); + assert(srcSide >= CScreenMap::kFirstDirection && + srcSide <= CScreenMap::kLastDirection); + + switch (srcSide) { + case CScreenMap::kLeft: + case CScreenMap::kRight: + assert(y >= 0 && y < src->m_height); + y = static_cast(0.5 + y * + static_cast(dst->m_height - 1) / + (src->m_height - 1)); + break; + + case CScreenMap::kTop: + case CScreenMap::kBottom: + assert(x >= 0 && x < src->m_width); + x = static_cast(0.5 + x * + static_cast(dst->m_width - 1) / + (src->m_width - 1)); + break; + } +} + +void CServer::acceptClients(void*) +{ + // add this thread to the list of threads to cancel. remove from + // list in d'tor. + CCleanupNote cleanupNote(this); + + std::auto_ptr listen; + try { + // create socket listener + listen.reset(m_socketFactory->createListen()); + + // bind to the desired port. keep retrying if we can't bind + // the address immediately. + CStopwatch timer; + CNetworkAddress addr(50001 /* FIXME -- m_port */); + for (;;) { + try { + listen->bind(addr); + break; + } + catch (XSocketAddressInUse&) { + // give up if we've waited too long + if (timer.getTime() >= m_bindTimeout) { + throw; + } + + // wait a bit before retrying + CThread::sleep(5.0); + } + } + + // accept connections and begin processing them + for (;;) { + // accept connection + CThread::testCancel(); + ISocket* socket = listen->accept(); + CThread::testCancel(); + + // start handshake thread + CThread(new TMethodJob( + this, &CServer::handshakeClient, socket)); + } + } + catch (XBase& e) { + fprintf(stderr, "cannot listen for clients: %s\n", e.what()); + quit(); + } +} + +void CServer::handshakeClient(void* vsocket) +{ + // get the socket pointer from the argument + assert(vsocket != NULL); + std::auto_ptr socket(reinterpret_cast(vsocket)); + + // add this thread to the list of threads to cancel. remove from + // list in d'tor. + CCleanupNote cleanupNote(this); + + CString name(""); + try { + // get the input and output streams + IInputStream* srcInput = socket->getInputStream(); + IOutputStream* srcOutput = socket->getOutputStream(); + std::auto_ptr input; + std::auto_ptr output; + + // attach the encryption layer + if (m_securityFactory != NULL) { +/* FIXME -- implement ISecurityFactory + input.reset(m_securityFactory->createInputFilter(srcInput, false)); + output.reset(m_securityFactory->createOutputFilter(srcOutput, false)); + srcInput = input.get(); + srcOutput = output.get(); +*/ + } + + // attach the packetizing filters + input.reset(new CInputPacketStream(srcInput, true)); + output.reset(new COutputPacketStream(srcOutput, true)); + + std::auto_ptr protocol; + std::auto_ptr connectedNote; + { + // give the client a limited time to complete the handshake + CTimerThread timer(30.0); + + // limit the maximum length of the hello + static const UInt32 maxHelloLen = 1024; + + // say hello + CProtocolUtil::writef(output.get(), "Synergy%2i%2i", + kMajorVersion, kMinorVersion); + output->flush(); + + // wait for the reply + UInt32 n = input->getSize(); + if (n > maxHelloLen) { + throw XBadClient(); + } + + // get and parse the reply to hello + SInt32 major, minor; + try { + CProtocolUtil::readf(input.get(), "Synergy%2i%2i%s", + &major, &minor, &name); + } + catch (XIO&) { + throw XBadClient(); + } + if (major < 0 || minor < 0) { + throw XBadClient(); + } + + // create a protocol interpreter for the version + protocol.reset(CServerProtocol::create(major, minor, + this, name, input.get(), output.get())); + + // client is now pending + connectedNote.reset(new CConnectionNote(this, + name, protocol.get())); + + // ask and wait for the client's info + protocol->queryInfo(); + } + + // handle messages from client. returns when the client + // disconnects. + protocol->run(); + } + catch (XIncompatibleClient& e) { + // client is incompatible + fprintf(stderr, "client is incompatible (%s, %d.%d)\n", + name.c_str(), e.getMajor(), e.getMinor()); + // FIXME -- could print network address if socket had suitable method + } + catch (XBadClient&) { + // client not behaving + fprintf(stderr, "protocol error from client %s\n", name.c_str()); + // FIXME -- could print network address if socket had suitable method + } + catch (XBase& e) { + // misc error + fprintf(stderr, "error communicating with client %s: %s\n", + name.c_str(), e.what()); + // FIXME -- could print network address if socket had suitable method + } +} + +void CServer::quit() throw() +{ + CLock lock(&m_mutex); + m_done = true; + m_done.broadcast(); +} + +// FIXME -- use factory to create screen +#include "CXWindowsPrimaryScreen.h" +void CServer::openPrimaryScreen() +{ + assert(m_primary == NULL); + + // open screen + m_primary = new CXWindowsPrimaryScreen; + m_primary->open(this); + + // add connection + m_active = addConnection(CString("primary"/* FIXME */), NULL); +} + +void CServer::closePrimaryScreen() throw() +{ + assert(m_primary != NULL); + + // remove connection + removeConnection(CString("primary"/* FIXME */)); + + // close the primary screen + try { + m_primary->close(); + } + catch (...) { + // ignore + } + + // clean up + delete m_primary; + m_primary = NULL; +} + +void CServer::addCleanupThread(const CThread& thread) +{ + CLock lock(&m_mutex); + m_cleanupList.insert(m_cleanupList.begin(), new CThread(thread)); +} + +void CServer::removeCleanupThread(const CThread& thread) +{ + CLock lock(&m_mutex); + for (CThreadList::iterator index = m_cleanupList.begin(); + index != m_cleanupList.end(); ++index) { + if (**index == thread) { + m_cleanupList.erase(index); + delete *index; + return; + } + } +} + +void CServer::cleanupThreads() throw() +{ + m_mutex.lock(); + while (m_cleanupList.begin() != m_cleanupList.end()) { + // get the next thread and cancel it + CThread* thread = m_cleanupList.front(); + thread->cancel(); + + // wait for thread to finish with cleanup list unlocked. the + // thread will remove itself from the cleanup list. + m_mutex.unlock(); + thread->wait(); + m_mutex.lock(); + } + + // FIXME -- delete remaining threads from list + m_mutex.unlock(); +} + +CServer::CScreenInfo* CServer::addConnection( + const CString& name, IServerProtocol* protocol) +{ + CLock lock(&m_mutex); + assert(m_screens.count(name) == 0); + CScreenInfo* newScreen = new CScreenInfo(name, protocol); + m_screens.insert(std::make_pair(name, newScreen)); + return newScreen; +} + +void CServer::removeConnection(const CString& name) +{ + CLock lock(&m_mutex); + CScreenList::iterator index = m_screens.find(name); + assert(index == m_screens.end()); + delete index->second; + m_screens.erase(index); +} + + +// +// CServer::CCleanupNote +// + +CServer::CCleanupNote::CCleanupNote(CServer* server) : m_server(server) +{ + assert(m_server != NULL); + m_server->addCleanupThread(CThread::getCurrentThread()); +} + +CServer::CCleanupNote::~CCleanupNote() +{ + m_server->removeCleanupThread(CThread::getCurrentThread()); +} + + +// +// CServer::CConnectionNote +// + +CServer::CConnectionNote::CConnectionNote(CServer* server, + const CString& name, + IServerProtocol* protocol) : + m_server(server), + m_name(name) +{ + assert(m_server != NULL); + m_server->addConnection(m_name, protocol); +} + +CServer::CConnectionNote::~CConnectionNote() +{ + m_server->removeConnection(m_name); +} + + +// +// CServer::CScreenInfo +// + +CServer::CScreenInfo::CScreenInfo(const CString& name, + IServerProtocol* protocol) : + m_name(name), + m_protocol(protocol), + m_width(0), m_height(0), + m_zoneSize(0) +{ + // do nothing +} + +CServer::CScreenInfo::~CScreenInfo() +{ + // do nothing +} diff --git a/synergy/CServer.h b/synergy/CServer.h new file mode 100644 index 00000000..e7fc814e --- /dev/null +++ b/synergy/CServer.h @@ -0,0 +1,158 @@ +#ifndef CSERVER_H +#define CSERVER_H + +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CScreenMap.h" +#include "CCondVar.h" +#include "CMutex.h" +#include "CString.h" +#include "XBase.h" +#include +#include + +class CThread; +class IServerProtocol; +class ISocketFactory; +class ISecurityFactory; +class IPrimaryScreen; + +class CServer { + public: + CServer(); + ~CServer(); + + // manipulators + + void run(); + + // update screen map + void setScreenMap(const CScreenMap&); + + // handle events on server's screen + void onKeyDown(KeyID, KeyModifierMask); + void onKeyUp(KeyID, KeyModifierMask); + void onKeyRepeat(KeyID, KeyModifierMask); + void onMouseDown(ButtonID); + void onMouseUp(ButtonID); + void onMouseMovePrimary(SInt32 x, SInt32 y); + void onMouseMoveSecondary(SInt32 dx, SInt32 dy); + void onMouseWheel(SInt32 delta); + + // handle messages from clients + void setInfo(const CString& clientName, + SInt32 w, SInt32 h, SInt32 zoneSize) throw(); + + // accessors + + bool isLockedToScreen() const; + + // get the current screen map + void getScreenMap(CScreenMap*) const; + + protected: + bool onCommandKey(KeyID, KeyModifierMask, bool down); + void quit() throw(); + + private: + class CCleanupNote { + public: + CCleanupNote(CServer*); + ~CCleanupNote(); + + private: + CServer* m_server; + }; + + class CConnectionNote { + public: + CConnectionNote(CServer*, const CString&, IServerProtocol*); + ~CConnectionNote(); + + private: + bool m_pending; + CServer* m_server; + CString m_name; + }; + + class CScreenInfo { + public: + CScreenInfo(const CString& name, IServerProtocol*); + ~CScreenInfo(); + + public: + CString m_name; + IServerProtocol* m_protocol; + SInt32 m_width, m_height; + SInt32 m_zoneSize; + }; + + // change the active screen + void switchScreen(CScreenInfo*, SInt32 x, SInt32 y); + + // lookup neighboring screen + CScreenInfo* getNeighbor(CScreenInfo*, CScreenMap::EDirection) const; + + // lookup neighboring screen. given a position relative to the + // source screen, find the screen we should move onto and where. + // if the position is sufficiently far from the source then we + // cross multiple screens. + CScreenInfo* getNeighbor(CScreenInfo*, + CScreenMap::EDirection, + SInt32& x, SInt32& y) const; + + // adjust coordinates to account for resolution differences. the + // position is converted to a resolution independent form then + // converted back to screen coordinates on the destination screen. + void mapPosition(CScreenInfo* src, + CScreenMap::EDirection srcSide, + CScreenInfo* dst, + SInt32& x, SInt32& y) const; + + // open/close the primary screen + void openPrimaryScreen(); + void closePrimaryScreen() throw(); + + // cancel running threads + void cleanupThreads() throw(); + + // thread method to accept incoming client connections + void acceptClients(void*); + + // thread method to do startup handshake with client + void handshakeClient(void*); + + // thread cleanup list maintenance + friend class CCleanupNote; + void addCleanupThread(const CThread& thread); + void removeCleanupThread(const CThread& thread); + + // connection list maintenance + friend class CConnectionNote; + CScreenInfo* addConnection(const CString& name, IServerProtocol*); + void removeConnection(const CString& name); + + private: + typedef std::list CThreadList; + typedef std::map CScreenList; + + CMutex m_mutex; + CCondVar m_done; + + double m_bindTimeout; + + ISocketFactory* m_socketFactory; + ISecurityFactory* m_securityFactory; + + CThreadList m_cleanupList; + + IPrimaryScreen* m_primary; + CScreenList m_screens; + CScreenInfo* m_active; + + SInt32 m_x, m_y; + + CScreenMap m_screenMap; +}; + +#endif diff --git a/synergy/CServerProtocol.cpp b/synergy/CServerProtocol.cpp new file mode 100644 index 00000000..eb52afd8 --- /dev/null +++ b/synergy/CServerProtocol.cpp @@ -0,0 +1,72 @@ +#include "CServerProtocol.h" +#include "CServerProtocol1_0.h" +#include "ProtocolTypes.h" +#include "IOutputStream.h" +#include +#include + +// +// CServerProtocol +// + +CServerProtocol::CServerProtocol(CServer* server, const CString& client, + IInputStream* input, IOutputStream* output) : + m_server(server), + m_client(client), + m_input(input), + m_output(output) +{ + assert(m_server != NULL); + assert(m_input != NULL); + assert(m_output != NULL); +} + +CServerProtocol::~CServerProtocol() +{ + // do nothing +} + +CServer* CServerProtocol::getServer() const throw() +{ + return m_server; +} + +CString CServerProtocol::getClient() const throw() +{ + return m_client; +} + +IInputStream* CServerProtocol::getInputStream() const throw() +{ + return m_input; +} + +IOutputStream* CServerProtocol::getOutputStream() const throw() +{ + return m_output; +} + +IServerProtocol* CServerProtocol::create(SInt32 major, SInt32 minor, + CServer* server, const CString& client, + IInputStream* input, IOutputStream* output) +{ + // disallow connection from test versions to release versions + if (major == 0 && kMajorVersion != 0) { + output->write(kMsgEIncompatible, sizeof(kMsgEIncompatible) - 1); + output->flush(); + throw XIncompatibleClient(major, minor); + } + + // hangup (with error) if version isn't supported + if (major > kMajorVersion || + (major == kMajorVersion && minor > kMinorVersion)) { + output->write(kMsgEIncompatible, sizeof(kMsgEIncompatible) - 1); + output->flush(); + throw XIncompatibleClient(major, minor); + } + + // create highest version protocol object not higher than the + // given version. + return new CServerProtocol1_0(server, client, input, output); +} + diff --git a/synergy/CServerProtocol.h b/synergy/CServerProtocol.h new file mode 100644 index 00000000..485ee2c3 --- /dev/null +++ b/synergy/CServerProtocol.h @@ -0,0 +1,58 @@ +#ifndef CSERVERPROTOCOL_H +#define CSERVERPROTOCOL_H + +#include "CString.h" +#include "IServerProtocol.h" + +class CServer; +class IInputStream; +class IOutputStream; + +class CServerProtocol : public IServerProtocol { + public: + CServerProtocol(CServer*, const CString& clientName, + IInputStream*, IOutputStream*); + ~CServerProtocol(); + + // manipulators + + // accessors + + virtual CServer* getServer() const throw(); + virtual CString getClient() const throw(); + virtual IInputStream* getInputStream() const throw(); + virtual IOutputStream* getOutputStream() const throw(); + + static IServerProtocol* create(SInt32 major, SInt32 minor, + CServer*, const CString& clientName, + IInputStream*, IOutputStream*); + + // IServerProtocol overrides + virtual void run() throw(XIO,XBadClient) = 0; + virtual void queryInfo() throw(XIO,XBadClient) = 0; + virtual void sendClose() throw(XIO) = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) throw(XIO) = 0; + virtual void sendLeave() throw(XIO) = 0; + virtual void sendGrabClipboard() throw(XIO) = 0; + virtual void sendQueryClipboard() throw(XIO) = 0; + virtual void sendScreenSaver(bool on) throw(XIO) = 0; + virtual void sendKeyDown(KeyID, KeyModifierMask) throw(XIO) = 0; + virtual void sendKeyRepeat(KeyID, KeyModifierMask) throw(XIO) = 0; + virtual void sendKeyUp(KeyID, KeyModifierMask) throw(XIO) = 0; + virtual void sendMouseDown(ButtonID) throw(XIO) = 0; + virtual void sendMouseUp(ButtonID) throw(XIO) = 0; + virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) throw(XIO) = 0; + virtual void sendMouseWheel(SInt32 delta) throw(XIO) = 0; + + protected: + //IServerProtocol overrides + virtual void recvInfo() throw(XIO,XBadClient) = 0; + + private: + CServer* m_server; + CString m_client; + IInputStream* m_input; + IOutputStream* m_output; +}; + +#endif diff --git a/synergy/CServerProtocol1_0.cpp b/synergy/CServerProtocol1_0.cpp new file mode 100644 index 00000000..eb6ab9cc --- /dev/null +++ b/synergy/CServerProtocol1_0.cpp @@ -0,0 +1,157 @@ +#include "CServerProtocol1_0.h" +#include "CServer.h" +#include "CProtocolUtil.h" +#include "ProtocolTypes.h" +#include "IInputStream.h" +#include + +// +// CServerProtocol1_0 +// + +CServerProtocol1_0::CServerProtocol1_0(CServer* server, const CString& client, + IInputStream* input, IOutputStream* output) : + CServerProtocol(server, client, input, output) +{ + // do nothing +} + +CServerProtocol1_0::~CServerProtocol1_0() +{ + // do nothing +} + +void CServerProtocol1_0::run() throw(XIO,XBadClient) +{ + // handle messages until the client hangs up + for (;;) { + // wait for a message + UInt8 code[4]; + UInt32 n = getInputStream()->read(code, 4); + + // verify we got an entire code + if (n == 0) { + // client hungup + return; + } + if (n != 4) { + // client sent an incomplete message + throw XBadClient(); + } + + // parse message + if (memcmp(code, kMsgDInfo, 4) == 0) { + recvInfo(); + } + // FIXME -- more message here + else { + // unknown message + throw XBadClient(); + } + } +} + +void CServerProtocol1_0::queryInfo() throw(XIO,XBadClient) +{ + // send request + CProtocolUtil::writef(getOutputStream(), kMsgQInfo); + + // wait for and verify reply + UInt8 code[4]; + UInt32 n = getInputStream()->read(code, 4); + if (n != 4 && memcmp(code, kMsgDInfo, 4) != 0) { + throw XBadClient(); + } + + // handle reply + recvInfo(); +} + +void CServerProtocol1_0::sendClose() throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgCClose); +} + +void CServerProtocol1_0::sendEnter( + SInt32 xAbs, SInt32 yAbs) throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs); +} + +void CServerProtocol1_0::sendLeave() throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgCLeave); +} + +void CServerProtocol1_0::sendGrabClipboard() throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard); +} + +void CServerProtocol1_0::sendQueryClipboard() throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgQClipboard); +} + +void CServerProtocol1_0::sendScreenSaver(bool on) throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); +} + +void CServerProtocol1_0::sendKeyDown( + KeyID key, KeyModifierMask mask) throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); +} + +void CServerProtocol1_0::sendKeyRepeat( + KeyID key, KeyModifierMask mask) throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask); +} + +void CServerProtocol1_0::sendKeyUp( + KeyID key, KeyModifierMask mask) throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); +} + +void CServerProtocol1_0::sendMouseDown( + ButtonID button) throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); +} + +void CServerProtocol1_0::sendMouseUp( + ButtonID button) throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); +} + +void CServerProtocol1_0::sendMouseMove( + SInt32 xAbs, SInt32 yAbs) throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); +} + +void CServerProtocol1_0::sendMouseWheel( + SInt32 delta) throw(XIO) +{ + CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); +} + +void CServerProtocol1_0::recvInfo() throw(XIO,XBadClient) +{ + // parse the message + SInt32 w, h, zoneInfo; + CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, &w, &h, &zoneInfo); + + // validate + if (w == 0 || h == 0) { + throw XBadClient(); + } + + // tell server of change + getServer()->setInfo(getClient(), w, h, zoneInfo); +} + diff --git a/synergy/CServerProtocol1_0.h b/synergy/CServerProtocol1_0.h new file mode 100644 index 00000000..eec2d156 --- /dev/null +++ b/synergy/CServerProtocol1_0.h @@ -0,0 +1,37 @@ +#ifndef CSERVERPROTOCOL1_0_H +#define CSERVERPROTOCOL1_0_H + +#include "CServerProtocol.h" + +class CServerProtocol1_0 : public CServerProtocol { + public: + CServerProtocol1_0(CServer*, const CString&, IInputStream*, IOutputStream*); + ~CServerProtocol1_0(); + + // manipulators + + // accessors + + // IServerProtocol overrides + virtual void run() throw(XIO,XBadClient); + virtual void queryInfo() throw(XIO,XBadClient); + virtual void sendClose() throw(XIO); + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) throw(XIO); + virtual void sendLeave() throw(XIO); + virtual void sendGrabClipboard() throw(XIO); + virtual void sendQueryClipboard() throw(XIO); + virtual void sendScreenSaver(bool on) throw(XIO); + virtual void sendKeyDown(KeyID, KeyModifierMask) throw(XIO); + virtual void sendKeyRepeat(KeyID, KeyModifierMask) throw(XIO); + virtual void sendKeyUp(KeyID, KeyModifierMask) throw(XIO); + virtual void sendMouseDown(ButtonID) throw(XIO); + virtual void sendMouseUp(ButtonID) throw(XIO); + virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) throw(XIO); + virtual void sendMouseWheel(SInt32 delta) throw(XIO); + + protected: + // IServerProtocol overrides + virtual void recvInfo() throw(XIO,XBadClient); +}; + +#endif diff --git a/synergy/CTCPSocketFactory.cpp b/synergy/CTCPSocketFactory.cpp new file mode 100644 index 00000000..cecbb348 --- /dev/null +++ b/synergy/CTCPSocketFactory.cpp @@ -0,0 +1,27 @@ +#include "CTCPSocketFactory.h" +#include "CTCPSocket.h" +#include "CTCPListenSocket.h" + +// +// CTCPSocketFactory +// + +CTCPSocketFactory::CTCPSocketFactory() +{ + // do nothing +} + +CTCPSocketFactory::~CTCPSocketFactory() +{ + // do nothing +} + +ISocket* CTCPSocketFactory::create() const throw(XSocket) +{ + return new CTCPSocket; +} + +IListenSocket* CTCPSocketFactory::createListen() const throw(XSocket) +{ + return new CTCPListenSocket; +} diff --git a/synergy/CTCPSocketFactory.h b/synergy/CTCPSocketFactory.h new file mode 100644 index 00000000..db38ee6e --- /dev/null +++ b/synergy/CTCPSocketFactory.h @@ -0,0 +1,20 @@ +#ifndef CTCPSOCKETFACTORY_H +#define CTCPSOCKETFACTORY_H + +#include "ISocketFactory.h" + +class CTCPSocketFactory : public ISocketFactory { + public: + CTCPSocketFactory(); + virtual ~CTCPSocketFactory(); + + // manipulators + + // accessors + + // ISocketFactory overrides + virtual ISocket* create() const throw(XSocket); + virtual IListenSocket* createListen() const throw(XSocket); +}; + +#endif diff --git a/synergy/CXWindowsPrimaryScreen.cpp b/synergy/CXWindowsPrimaryScreen.cpp new file mode 100644 index 00000000..1c24b332 --- /dev/null +++ b/synergy/CXWindowsPrimaryScreen.cpp @@ -0,0 +1,363 @@ +#include "CXWindowsPrimaryScreen.h" +#include "CServer.h" +#include "CThread.h" +#include "TMethodJob.h" +#include +#include +#include + +// +// CXWindowsPrimaryScreen +// + +CXWindowsPrimaryScreen::CXWindowsPrimaryScreen() : + m_server(NULL), + m_display(NULL), + m_w(0), m_h(0), + m_window(None), + m_active(false) +{ + // do nothing +} + +CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() +{ + assert(m_display == NULL); +} + +void CXWindowsPrimaryScreen::open(CServer* server) +{ + assert(m_server == NULL); + assert(server != NULL); + + // set the server + m_server = server; + + // open the display + m_display = ::XOpenDisplay(NULL); // FIXME -- allow non-default + if (m_display == NULL) + throw int(5); // FIXME -- make exception for this + + // get default screen + m_screen = DefaultScreen(m_display); + Screen* screen = ScreenOfDisplay(m_display, m_screen); + + // get screen size + m_w = WidthOfScreen(screen); + m_h = HeightOfScreen(screen); + + // get the root window + Window root = RootWindow(m_display, m_screen); + + // create the grab window. this window is used to capture user + // input when the user is focussed on another client. don't let + // the window manager mess with it. + XSetWindowAttributes attr; + attr.event_mask = PointerMotionMask |// PointerMotionHintMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = None; + m_window = ::XCreateWindow(m_display, root, 0, 0, m_w, m_h, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + + // start watching for events on other windows + selectEvents(root); + + // start processing events + m_eventThread = new CThread(new TMethodJob( + this, &CXWindowsPrimaryScreen::eventThread)); +} + +void CXWindowsPrimaryScreen::close() +{ + assert(m_server != NULL); + assert(m_window != None); + assert(m_eventThread != NULL); + + // stop event thread + m_eventThread->cancel(); + m_eventThread->wait(); + delete m_eventThread; + m_eventThread = NULL; + + // destroy window + ::XDestroyWindow(m_display, m_window); + m_window = None; + + // close the display + ::XCloseDisplay(m_display); + m_display = NULL; +} + +void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) +{ + assert(m_display != NULL); + assert(m_window != None); + assert(m_active == true); + + // warp to requested location + ::XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, x, y); + + // unmap the grab window. this also ungrabs the mouse and keyboard. + ::XUnmapWindow(m_display, m_window); + + // remove all input events for grab window + XEvent event; + while (::XCheckWindowEvent(m_display, m_window, + PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask, + &event)) { + // do nothing + } + + // not active anymore + m_active = false; +} + +void CXWindowsPrimaryScreen::leave() +{ + assert(m_display != NULL); + assert(m_window != None); + assert(m_active == false); + + // raise and show the input window + ::XMapRaised(m_display, m_window); + + // grab the mouse and keyboard. keep trying until we get them. + // if we can't grab one after grabbing the other then ungrab + // and wait before retrying. + int result; + do { + // mouse first + do { + result = ::XGrabPointer(m_display, m_window, True, 0, + GrabModeAsync, GrabModeAsync, + m_window, None, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) + CThread::sleep(0.25); + } while (result != GrabSuccess); + + // now the keyboard + result = ::XGrabKeyboard(m_display, m_window, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + ::XUngrabPointer(m_display, CurrentTime); + CThread::sleep(0.25); + } + } while (result != GrabSuccess); + + // move the mouse to the center of grab window + warpCursor(m_w >> 1, m_h >> 1); + + // local client now active + m_active = true; +} + +void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + + // warp the mouse + Window root = RootWindow(m_display, m_screen); + ::XWarpPointer(m_display, None, root, 0, 0, 0, 0, x, y); + ::XSync(m_display, False); + + // discard mouse events since we just added one we don't want + XEvent xevent; + while (::XCheckWindowEvent(m_display, m_window, + PointerMotionMask, &xevent)) { + // do nothing + } +} + +void CXWindowsPrimaryScreen::getSize( + SInt32* width, SInt32* height) const +{ + assert(m_display != NULL); + assert(width != NULL && height != NULL); + + *width = m_w; + *height = m_h; +} + +SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const +{ + assert(m_display != NULL); + + return 1; +} + +void CXWindowsPrimaryScreen::selectEvents(Window w) const +{ + // we want to track the mouse everywhere on the display. to achieve + // that we select PointerMotionMask on every window. we also select + // SubstructureNotifyMask in order to get CreateNotify events so we + // select events on new windows too. + + // we don't want to adjust our grab window + if (w == m_window) + return; + + // select events of interest + ::XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask); + + // recurse on child windows + Window rw, pw, *cw; + unsigned int nc; + if (::XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) + selectEvents(cw[i]); + ::XFree(cw); + } +} + +void CXWindowsPrimaryScreen::eventThread(void*) +{ + for (;;) { + // wait for and then get the next event + while (XPending(m_display) == 0) { + CThread::sleep(0.05); + } + XEvent xevent; + XNextEvent(m_display, &xevent); + + // handle event + switch (xevent.type) { + case CreateNotify: + // select events on new window + selectEvents(xevent.xcreatewindow.window); + break; + + case KeyPress: { + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(xevent.xkey.keycode, mask); + if (key != kKeyNone) { + m_server->onKeyDown(key, mask); + } + break; + } + + // FIXME -- simulate key repeat. X sends press/release for + // repeat. must detect auto repeat and use kKeyRepeat. + case KeyRelease: { + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(xevent.xkey.keycode, mask); + if (key != kKeyNone) { + m_server->onKeyUp(key, mask); + } + break; + } + + case ButtonPress: { + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_server->onMouseDown(button); + } + break; + } + + case ButtonRelease: { + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_server->onMouseUp(button); + } + break; + } + + case MotionNotify: { + SInt32 x, y; + if (!m_active) { + x = xevent.xmotion.x_root; + y = xevent.xmotion.y_root; + m_server->onMouseMovePrimary(x, y); + } + else { + // FIXME -- slurp up all remaining motion events? + // probably not since key strokes may go to wrong place. + + // get mouse deltas + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int mask; + if (!::XQueryPointer(m_display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &mask)) + break; + x = xRoot - (m_w >> 1); + y = yRoot - (m_h >> 1); + + // warp mouse back to center + warpCursor(m_w >> 1, m_h >> 1); + + m_server->onMouseMoveSecondary(x, y); + } + break; + } + +/* + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; +*/ + } + } +} + +KeyModifierMask CXWindowsPrimaryScreen::mapModifier( + unsigned int state) const +{ + // FIXME -- should be configurable + KeyModifierMask mask = 0; + if (state & 1) + mask |= KeyModifierShift; + if (state & 2) + mask |= KeyModifierCapsLock; + if (state & 4) + mask |= KeyModifierControl; + if (state & 8) + mask |= KeyModifierAlt; + if (state & 16) + mask |= KeyModifierNumLock; + if (state & 32) + mask |= KeyModifierMeta; + if (state & 128) + mask |= KeyModifierScrollLock; + return mask; +} + +KeyID CXWindowsPrimaryScreen::mapKey( + KeyCode keycode, KeyModifierMask mask) const +{ + int index; + if (mask & KeyModifierShift) + index = 1; + else + index = 0; + return static_cast(::XKeycodeToKeysym(m_display, keycode, index)); +} + +ButtonID CXWindowsPrimaryScreen::mapButton( + unsigned int button) const +{ + // FIXME -- should use button mapping? + if (button >= 1 && button <= 3) + return static_cast(button); + else + return kButtonNone; +} diff --git a/synergy/CXWindowsPrimaryScreen.h b/synergy/CXWindowsPrimaryScreen.h new file mode 100644 index 00000000..14134cf4 --- /dev/null +++ b/synergy/CXWindowsPrimaryScreen.h @@ -0,0 +1,43 @@ +#ifndef CXWINDOWSPRIMARYSCREEN_H +#define CXWINDOWSPRIMARYSCREEN_H + +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "IPrimaryScreen.h" +#include + +class CThread; + +class CXWindowsPrimaryScreen : public IPrimaryScreen { + public: + CXWindowsPrimaryScreen(); + virtual ~CXWindowsPrimaryScreen(); + + // IPrimaryScreen overrides + virtual void open(CServer*); + virtual void close(); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void leave(); + virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void getSize(SInt32* width, SInt32* height) const; + virtual SInt32 getJumpZoneSize() const; + + private: + void selectEvents(Window) const; + + void eventThread(void*); + KeyModifierMask mapModifier(unsigned int state) const; + KeyID mapKey(KeyCode, KeyModifierMask) const; + ButtonID mapButton(unsigned int button) const; + + private: + CServer* m_server; + CThread* m_eventThread; + Display* m_display; + int m_screen; + SInt32 m_w, m_h; + Window m_window; + bool m_active; +}; + +#endif diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h new file mode 100644 index 00000000..86f98498 --- /dev/null +++ b/synergy/IPrimaryScreen.h @@ -0,0 +1,73 @@ +#ifndef IPRIMARYSCREEN_H +#define IPRIMARYSCREEN_H + +#include "IInterface.h" +#include "BasicTypes.h" + +class CServer; +//class IClipboard; + +class IPrimaryScreen : public IInterface { + public: + // manipulators + + // initialize the screen and start reporting events to the server. + // events should be reported no matter where on the screen they + // occur but do not interfere with normal event dispatch. the + // screen saver engaging should be reported as an event. if that + // can't be detected then this object should disable the system's + // screen saver timer and should start the screen saver after + // idling for an appropriate time. + virtual void open(CServer*) = 0; + + // close the screen. should restore the screen saver timer if it + // was disabled. + virtual void close() = 0; + + // called when the user navigates back to the primary screen. + // warp the cursor to the given coordinates, unhide it, and + // ungrab the input devices. every call to method has a matching + // call to leave() which preceeds it. + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + + // called when the user navigates off the primary screen. hide + // the cursor and grab exclusive access to the input devices. + virtual void leave() = 0; + + // warp the cursor to the given position + virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + +/* + // set the screen's clipboard contents. this is usually called + // soon after an enter(). + virtual void setClipboard(const IClipboard*) = 0; + + // show or hide the screen saver + virtual void onScreenSaver(bool show) = 0; + + // clipboard input + virtual void onClipboardChanged() = 0; +*/ + + // accessors + +/* + // get the screen's name. all screens must have a name unique on + // the server they connect to. the hostname is usually an + // appropriate name. + virtual CString getName() const = 0; +*/ + + // get the size of the screen + virtual void getSize(SInt32* width, SInt32* height) const = 0; + + // get the size of jump zone + virtual SInt32 getJumpZoneSize() const = 0; + +/* + // get the screen's clipboard contents + virtual void getClipboard(IClipboard*) const = 0; +*/ +}; + +#endif diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h new file mode 100644 index 00000000..1deb45f9 --- /dev/null +++ b/synergy/ISecondaryScreen.h @@ -0,0 +1,71 @@ +#ifndef ISECONDARYSCREEN_H +#define ISECONDARYSCREEN_H + +#include "IInterface.h" +#include "BasicTypes.h" + +class CClient; +//class IClipboard; + +class ISecondaryScreen : public IInterface { + public: + // manipulators + + // initialize the screen, hide the cursor, and disable the screen + // saver. start reporting certain events to the client (clipboard + // stolen and screen size changed). + virtual void open(CClient*) = 0; + + // close the screen. should restore the screen saver. + virtual void close() = 0; + + // called when the user navigates to the secondary screen. warp + // the cursor to the given coordinates and unhide it. prepare to + // simulate input events. + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + + // called when the user navigates off the secondary screen. clean + // up input event simulation and hide the cursor. + virtual void leave() = 0; + + // warp the cursor to the given position + virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + + // keyboard input simulation + virtual void onKeyDown(KeyID, KeyModifierMask) = 0; + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void onKeyUp(KeyID, KeyModifierMask) = 0; + + // mouse input simulation + virtual void onMouseDown(ButtonID) = 0; + virtual void onMouseUp(ButtonID) = 0; + virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + virtual void onMouseWheel(SInt32 delta) = 0; + +/* + // set the screen's clipboard contents. this is usually called + // soon after an enter(). + virtual void setClipboard(const IClipboard*) = 0; + + // show or hide the screen saver + virtual void onScreenSaver(bool show) = 0; + + // clipboard input + virtual void onClipboardChanged() = 0; +*/ + + // accessors + + // get the size of the screen + virtual void getSize(SInt32* width, SInt32* height) const = 0; + + // get the size of jump zone + virtual SInt32 getJumpZoneSize() const = 0; + +/* + // get the screen's clipboard contents + virtual void getClipboard(IClipboard*) const = 0; +*/ +}; + +#endif diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h new file mode 100644 index 00000000..cf5ece9f --- /dev/null +++ b/synergy/IServerProtocol.h @@ -0,0 +1,47 @@ +#ifndef ISERVERPROTOCOL_H +#define ISERVERPROTOCOL_H + +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "IInterface.h" +#include "XSynergy.h" +#include "XIO.h" + +class IServerProtocol : public IInterface { + public: + // manipulators + + // process messages from the client and insert the appropriate + // events into the server's event queue. return when the client + // disconnects. + virtual void run() throw(XIO,XBadClient) = 0; + + // send client info query and process reply + virtual void queryInfo() throw(XIO,XBadClient) = 0; + + // send various messages to client + virtual void sendClose() throw(XIO) = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) throw(XIO) = 0; + virtual void sendLeave() throw(XIO) = 0; + virtual void sendGrabClipboard() throw(XIO) = 0; + virtual void sendQueryClipboard() throw(XIO) = 0; + virtual void sendScreenSaver(bool on) throw(XIO) = 0; + virtual void sendKeyDown(KeyID, KeyModifierMask) throw(XIO) = 0; + virtual void sendKeyRepeat(KeyID, KeyModifierMask) throw(XIO) = 0; + virtual void sendKeyUp(KeyID, KeyModifierMask) throw(XIO) = 0; + virtual void sendMouseDown(ButtonID) throw(XIO) = 0; + virtual void sendMouseUp(ButtonID) throw(XIO) = 0; + virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) throw(XIO) = 0; + virtual void sendMouseWheel(SInt32 delta) throw(XIO) = 0; + + // accessors + + protected: + // manipulators + + virtual void recvInfo() throw(XIO,XBadClient) = 0; + + // accessors +}; + +#endif diff --git a/synergy/ISocketFactory.h b/synergy/ISocketFactory.h new file mode 100644 index 00000000..e919432d --- /dev/null +++ b/synergy/ISocketFactory.h @@ -0,0 +1,21 @@ +#ifndef ISOCKETFACTORY_H +#define ISOCKETFACTORY_H + +#include "IInterface.h" +#include "XSocket.h" + +class ISocket; +class IListenSocket; + +class ISocketFactory : public IInterface { + public: + // manipulators + + // accessors + + // create sockets + virtual ISocket* create() const throw(XSocket) = 0; + virtual IListenSocket* createListen() const throw(XSocket) = 0; +}; + +#endif diff --git a/KeyTypes.h b/synergy/KeyTypes.h similarity index 96% rename from KeyTypes.h rename to synergy/KeyTypes.h index c3789fa1..ec070e3f 100644 --- a/KeyTypes.h +++ b/synergy/KeyTypes.h @@ -1,6 +1,8 @@ #ifndef KEYTYPES_H #define KEYTYPES_H +#include "BasicTypes.h" + // type to hold a key identifier typedef UInt32 KeyID; diff --git a/synergy/Makefile b/synergy/Makefile new file mode 100644 index 00000000..c16c90e7 --- /dev/null +++ b/synergy/Makefile @@ -0,0 +1,44 @@ +DEPTH=.. +include $(DEPTH)/Makecommon + +# +# source files +# +LCXXINCS = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + -I$(DEPTH)/net \ + $(NULL) +CXXFILES = \ + CInputPacketStream.cpp \ + COutputPacketStream.cpp \ + CTCPSocketFactory.cpp \ + CProtocolUtil.cpp \ + CServerProtocol.cpp \ + CServerProtocol1_0.cpp \ + CScreenMap.cpp \ + CServer.cpp \ + CXWindowsPrimaryScreen.cpp \ + XSynergy.cpp \ + $(NULL) + +# +# libraries we depend on +# +DEPLIBS = \ + $(LIBDIR)/libnet.a \ + $(LIBDIR)/libio.a \ + $(LIBDIR)/libmt.a \ + $(LIBDIR)/libbase.a \ + $(NULL) +LLDLIBS = \ + $(DEPLIBS) \ + -lpthread \ + $(NULL) + +targets: server + +server: $(OBJECTS) $(DEPLIBS) + $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) + diff --git a/MouseTypes.h b/synergy/MouseTypes.h similarity index 92% rename from MouseTypes.h rename to synergy/MouseTypes.h index 3dea13d9..ad5f0c7a 100644 --- a/MouseTypes.h +++ b/synergy/MouseTypes.h @@ -1,6 +1,8 @@ #ifndef MOUSETYPES_H #define MOUSETYPES_H +#include "BasicTypes.h" + // type to hold mouse button identifier typedef UInt8 ButtonID; diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h new file mode 100644 index 00000000..c41935c0 --- /dev/null +++ b/synergy/ProtocolTypes.h @@ -0,0 +1,38 @@ +#ifndef PROTOCOLTYPES_H +#define PROTOCOLTYPES_H + +#include "BasicTypes.h" + +// version number +static const SInt32 kMajorVersion = 0; +static const SInt32 kMinorVersion = 1; + +// message codes (trailing NUL is not part of code). codes are +// grouped into: +// commands -- request an action, no reply expected +// queries -- request info +// data -- send info +// errors -- notify of error +static const char kMsgCClose[] = "CBYE"; // server +static const char kMsgCEnter[] = "CINN%2i%2i"; // server +static const char kMsgCLeave[] = "COUT"; // server +static const char kMsgCClipboard[] = "CCLP"; // server +static const char kMsgCScreenSaver[] = "CSEC%1i"; // server + +static const char kMsgDKeyDown[] = "DKDN%2i%2i"; // server +static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i"; // server +static const char kMsgDKeyUp[] = "DKUP%2i%2i"; // server +static const char kMsgDMouseDown[] = "DMDN%1i"; // server +static const char kMsgDMouseUp[] = "DMUP%1i"; // server +static const char kMsgDMouseMove[] = "DMMV%2i%2i"; // server +static const char kMsgDMouseWheel[] = "DMWM%2i"; // server +static const char kMsgDClipboard[] = "DCLP%s"; // server +static const char kMsgDInfo[] = "DINF%2i%2i%2i"; // client + +static const char kMsgQClipboard[] = "QCLP"; // server +static const char kMsgQInfo[] = "QINF"; // server + +static const char kMsgEIncompatible[] = "EICV"; + +#endif + diff --git a/synergy/XSynergy.cpp b/synergy/XSynergy.cpp new file mode 100644 index 00000000..bda330a3 --- /dev/null +++ b/synergy/XSynergy.cpp @@ -0,0 +1,37 @@ +#include "XSynergy.h" + +// +// XBadClient +// + +CString XBadClient::getWhat() const throw() +{ + return "XBadClient"; +} + +// +// +// + +XIncompatibleClient::XIncompatibleClient(int major, int minor) : + m_major(major), + m_minor(minor) +{ + // do nothing +} + +int XIncompatibleClient::getMajor() const throw() +{ + return m_major; +} + +int XIncompatibleClient::getMinor() const throw() +{ + return m_minor; +} + +CString XIncompatibleClient::getWhat() const throw() +{ + return "XIncompatibleClient"; +} + diff --git a/synergy/XSynergy.h b/synergy/XSynergy.h new file mode 100644 index 00000000..da54af49 --- /dev/null +++ b/synergy/XSynergy.h @@ -0,0 +1,34 @@ +#ifndef XSYNERGY_H +#define XSYNERGY_H + +#include "XBase.h" + +class XSynergy : public XBase { }; + +// client is misbehaving +class XBadClient : public XSynergy { + protected: + virtual CString getWhat() const throw(); +}; + +// client has incompatible version +class XIncompatibleClient : public XSynergy { + public: + XIncompatibleClient(int major, int minor); + + // manipulators + + // accessors + + int getMajor() const throw(); + int getMinor() const throw(); + + protected: + virtual CString getWhat() const throw(); + + private: + int m_major; + int m_minor; +}; + +#endif diff --git a/test.cpp b/test.cpp new file mode 100644 index 00000000..cd62450f --- /dev/null +++ b/test.cpp @@ -0,0 +1,145 @@ +#include "CTCPSocket.h" +#include "CTCPListenSocket.h" +#include "CNetworkAddress.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CThread.h" +#include "CFunctionJob.h" +#include + +// +// test +// + +SInt16 port = 50000; + +static void thread1(void*) +{ + try { + fprintf(stdout, "client started\n"); + CThread::sleep(1.0); + CNetworkAddress addr("127.0.0.1", port); + + fprintf(stdout, "client connecting\n"); + CTCPSocket* socket = new CTCPSocket; + socket->connect(addr); + + fprintf(stdout, "client connected (%p). waiting.\n", socket); + CThread::sleep(2.0); + + fprintf(stdout, "client sending message\n"); + static const char msg[] = "message from client\n"; + socket->getOutputStream()->write(msg, sizeof(msg) - 1); + socket->getOutputStream()->flush(); + + fprintf(stdout, "client waiting for reply\n"); + UInt8 buffer[4096]; + UInt32 n; + do { + n = socket->getInputStream()->read(buffer, sizeof(buffer) - 1); + buffer[n] = 0; + fprintf(stdout, "%s", buffer); + } while (n == 0 && memchr(buffer, '\n', n) == NULL); + + fprintf(stdout, "client closing\n"); + socket->close(); + delete socket; + fprintf(stdout, "client terminating\n"); + } + catch (XBase& e) { + fprintf(stderr, "exception: %s\n", e.what()); + } +} + +static void thread2(void*) +{ + try { + fprintf(stdout, "server started\n"); + CNetworkAddress addr("127.0.0.1", port); + CTCPListenSocket listenSocket; + listenSocket.bind(addr); + + fprintf(stdout, "server accepting\n"); + ISocket* socket = listenSocket.accept(); + fprintf(stdout, "server accepted %p\n", socket); + + UInt8 buffer[4096]; + UInt32 n; + do { + n = socket->getInputStream()->read(buffer, sizeof(buffer) - 1); + buffer[n] = 0; + fprintf(stdout, "%s", buffer); + } while (n == 0 && memchr(buffer, '\n', n) == NULL); + + fprintf(stdout, "server replying\n"); + static const char reply[] = "data received\n"; + socket->getOutputStream()->write(reply, sizeof(reply) - 1); + + fprintf(stdout, "server closing\n"); + socket->close(); + delete socket; + fprintf(stdout, "server terminating\n"); + } + catch (XBase& e) { + fprintf(stderr, "exception: %s\n", e.what()); + } +} + +static void thread3(void*) +{ + try { + fprintf(stdout, "### looking up address\n"); + CNetworkAddress addr("www.google.com", 80); + + fprintf(stdout, "### connecting\n"); + CTCPSocket* socket = new CTCPSocket; + socket->connect(addr); + + fprintf(stdout, "### sending message\n"); + static const char msg[] = "GET / HTTP/1.0\nAccept: */*\n\n"; + socket->getOutputStream()->write(msg, sizeof(msg) - 1); + socket->getOutputStream()->flush(); + socket->getOutputStream()->close(); + + fprintf(stdout, "### waiting for reply\n"); + UInt8 buffer[4096]; + UInt32 n; + do { + n = socket->getInputStream()->read(buffer, sizeof(buffer) - 1); + buffer[n] = 0; + fprintf(stdout, "%s", buffer); + } while (n != 0); + + fprintf(stdout, "### closing\n"); + socket->close(); + delete socket; + fprintf(stdout, "### terminating\n"); + } + catch (XBase& e) { + fprintf(stderr, "### exception: %s\n", e.what()); + } +} + +int main(int argc, char** argv) +{ +/* + if (argc > 1) { + port = (SInt16)atoi(argv[1]); + } + + fprintf(stdout, "starting threads\n"); + CThread t1(new CFunctionJob(thread1)); + CThread t2(new CFunctionJob(thread2)); + + fprintf(stdout, "waiting for threads\n"); + t1.wait(); + t2.wait(); + fprintf(stdout, "threads finished\n"); +*/ + int tick = 0; + CThread t3(new CFunctionJob(thread3)); + while (!t3.wait(0.1)) { + fprintf(stdout, "$$$ %d\n", ++tick); + } + return 0; +} From 23f1b1aba15d36c519d2183b98c02add7a599050 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 6 Oct 2001 14:18:01 +0000 Subject: [PATCH 008/807] updated old files to new implementation --- Make-linux | 91 ++++++++++++++++-------------------------------------- Makefile | 69 ++++++++++++++++++++++++++++------------- 2 files changed, 74 insertions(+), 86 deletions(-) diff --git a/Make-linux b/Make-linux index e78e2da4..c64314b5 100644 --- a/Make-linux +++ b/Make-linux @@ -1,12 +1,10 @@ -# -# empty define, used to terminate lists -# -NULL = - # # build tools # +AR = /usr/bin/ar cru +CD = cd CXX = /usr/bin/g++ +ECHO = echo LD = /usr/bin/ld MKDIR = /bin/mkdir RM = /bin/rm -f @@ -15,12 +13,8 @@ RMR = /bin/rm -rf # # compiler options # -CXXFLAGS = $(LCXXFLAGS) $(GCXXFLAGS) $(CXXOPTIMIZER) $(MKDEPOPT) -LCXXFLAGS = $(LCXXDEFS) $(LCXXINCS) $(LCXXOPTS) -GCXXFLAGS = $(GCXXDEFS) $(GCXXINCS) $(GCXXOPTS) - -GCXXDEFS = -D_POSIX_SOURCE -GCXXINCS = -I$(DEPTH)/include -I/usr/include -I/usr/X11R6/include +GCXXDEFS = -D_POSIX_C_SOURCE=199309 +GCXXINCS = -I$(DEPTH)/include -I/usr/X11R6/include GCXXOPTS = -Wall -W -fexceptions -fno-rtti CXXOPTIMIZER = -g #CXXOPTIMIZER = -O2 -DNDEBUG @@ -28,12 +22,13 @@ CXXOPTIMIZER = -g # # linker options # -LDFLAGS = $(LDOPTS) $(LDLIBS) -LDOPTS = $(LLDOPTS) $(GLDOPTS) -LDLIBS = $(LLDLIBS) $(GLDLIBS) - GLDLIBS = -L/usr/X11R6/lib -lX11 -lXext -lXtst -GLDOPTS = +GLDOPTS = -L$(LIBDIR) + +# +# library stuff +# +LIBTARGET = $(LIBDIR)/lib$(TARGET).a # # dependency generation stuff @@ -44,12 +39,6 @@ MKDEPPRE = MKDEPPOST = $(MKDEP) -f $(MKDEPFILE) $*.d; $(RM) $*.d MKDEPFILE = Makedepend -# -# Convenience file list macros: -# -SOURCES = $(CXXFILES) -OBJECTS = $(CXXFILES:.cpp=.o) - # # stuff to clean # @@ -57,47 +46,21 @@ DIRT = $(_FORCE) $(LDIRT) $(GDIRT) GDIRT = *.[eoud] a.out core ar.tmp.* $(MKDEPFILE) # -# always unsatisfied target +# Rule macros for nonterminal makefiles that iterate over subdirectories, +# making the current target. Set SUBDIRS to the relevant list of kids. # -_FORCE = $(COMMONPREF)_force +# Set NOSUBMESG to any value to suppress a warning that subdirectories +# are not present. +# +SUBDIR_MAKERULE= \ + if test ! -d $$d; then \ + if test "$(NOSUBMESG)" = "" ; then \ + ${ECHO} "SKIPPING $$d: No such directory."; \ + fi \ + else \ + ${ECHO} "${CD} $$d; $(MAKE) $${RULE:=$@}"; \ + (${CD} $$d; ${MAKE} $${RULE:=$@}); \ + fi -# -# -# common rules -# -# - -# -# default target. makefiles must define a target named `targets'. -# -default: targets - -# -# always unsatisfied target -# -$(_FORCE): - -# -# cleaners -# -COMMONTARGETS = clean clobber -$(COMMONPREF)clean: $(_FORCE) - $(RM) $(DIRT) - -$(COMMONPREF)clobber: clean $(_FORCE) - $(RM) $(TARGETS) - -# -# implicit target rules -# -.SUFFIXES: .cpp .o - -.cpp.o: - $(MKDEPPRE) - $(CXX) $(CXXFLAGS) -c $< - $(MKDEPPOST) - -# -# load dependencies -# -sinclude $(MKDEPFILE) +SUBDIRS_MAKERULE= \ + @for d in $(SUBDIRS); do $(SUBDIR_MAKERULE); done diff --git a/Makefile b/Makefile index f8384949..d3ee18df 100644 --- a/Makefile +++ b/Makefile @@ -1,38 +1,63 @@ DEPTH=. -include Make-linux +COMMONPREF = root +include Makecommon # -# target files +# subdirectories +# +SUBDIRS = \ + base \ + mt \ + io \ + net \ + $(NULL) +# +# targets +# + +default: $(COMMONPREF)_force + $(SUBDIRS_MAKERULE) + +all targets: default + +clean: + $(RMR) $(LIBDIR) + $(SUBDIRS_MAKERULE) + +clobber: + $(RMR) $(LIBDIR) + $(SUBDIRS_MAKERULE) + +# +# +# test +# # -TARGETS = main # # source files # -CXXFILES = \ - XBase.cpp \ - CTrace.cpp \ - CEventQueue.cpp \ - CSocket.cpp \ - CMessageSocket.cpp \ - CSocketFactory.cpp \ - CServer.cpp \ - CClient.cpp \ - CScreenProxy.cpp \ - CXScreen.cpp \ - CUnixXScreen.cpp \ - CUnixTCPSocket.cpp \ - CUnixEventQueue.cpp \ - main.cpp \ +LCXXINCS = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + -I$(DEPTH)/net \ $(NULL) +CXXFILES = test.cpp # # libraries we depend on # -DEPLIBS = \ +DEPLIBS = \ + $(LIBDIR)/libnet.a \ + $(LIBDIR)/libio.a \ + $(LIBDIR)/libmt.a \ + $(LIBDIR)/libbase.a \ + $(NULL) +LLDLIBS = \ + $(DEPLIBS) \ + -lpthread \ $(NULL) -targets: $(TARGETS) - -main: $(OBJECTS) $(DEPLIBS) +test: $(OBJECTS) $(DEPLIBS) $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) From 23f948d55afa5856a0db3bf88bdb5e4bb1cfae4b Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 8 Oct 2001 19:24:46 +0000 Subject: [PATCH 009/807] checkpoint. first cut of client and server apps. not tested yet but they compile and *should* work as is. --- Makefile | 3 + notes | 1 - synergy/CClient.cpp | 106 ++++++++---- synergy/CClient.h | 14 +- synergy/CServer.cpp | 11 +- synergy/CXWindowsPrimaryScreen.cpp | 1 - synergy/CXWindowsSecondaryScreen.cpp | 231 +++++++++++++++++++++++++++ synergy/CXWindowsSecondaryScreen.h | 44 +++++ synergy/ISecondaryScreen.h | 2 + synergy/Makefile | 11 +- synergy/client.cpp | 21 +++ synergy/server.cpp | 29 ++++ 12 files changed, 434 insertions(+), 40 deletions(-) create mode 100644 synergy/CXWindowsSecondaryScreen.cpp create mode 100644 synergy/CXWindowsSecondaryScreen.h create mode 100644 synergy/client.cpp create mode 100644 synergy/server.cpp diff --git a/Makefile b/Makefile index d3ee18df..8c3dfeb5 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,7 @@ LCXXINCS = \ -I$(DEPTH)/mt \ -I$(DEPTH)/io \ -I$(DEPTH)/net \ + -I$(DEPTH)/synergy \ $(NULL) CXXFILES = test.cpp @@ -49,6 +50,7 @@ CXXFILES = test.cpp # libraries we depend on # DEPLIBS = \ + $(LIBDIR)/libsynergy.a \ $(LIBDIR)/libnet.a \ $(LIBDIR)/libio.a \ $(LIBDIR)/libmt.a \ @@ -61,3 +63,4 @@ LLDLIBS = \ test: $(OBJECTS) $(DEPLIBS) $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) + diff --git a/notes b/notes index 0effd884..b0845048 100644 --- a/notes +++ b/notes @@ -9,7 +9,6 @@ CServer * put mutex locks wherever necessary (like accessing m_active) CClient - * need thread to handle screen * need methods for screen event handler to call as appropriate server client diff --git a/synergy/CClient.cpp b/synergy/CClient.cpp index bd4e011b..1a20bc1e 100644 --- a/synergy/CClient.cpp +++ b/synergy/CClient.cpp @@ -2,15 +2,22 @@ #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" +#include "ISecondaryScreen.h" +#include "ProtocolTypes.h" #include "CTimerThread.h" +#include "XSynergy.h" #include +#include // // CClient // CClient::CClient(const CString& clientName) : - m_name(clientName) + m_name(clientName), + m_input(NULL), + m_output(NULL), + m_screen(NULL) { } @@ -19,6 +26,7 @@ CClient::~CClient() } #include "CTCPSocket.h" +#include "CXWindowsSecondaryScreen.h" void CClient::run(const CNetworkAddress& serverAddress) { std::auto_ptr socket; @@ -84,12 +92,18 @@ void CClient::run(const CNetworkAddress& serverAddress) } // connect to screen - // FIXME -- make object that closes and destroys screen in - // it's d'tor. screen must not be handling event queue by - // the time the streams are destroyed. - - // handle messages from server + std::auto_ptr screenCleaner; try { + m_screen = new CXWindowsSecondaryScreen; + screenCleaner.reset(new CScreenCleaner(this, m_screen)); + } + catch (XBase& e) { + fprintf(stderr, "cannot open screen: %s\n", e.what()); + return; + } + + try { + // handle messages from server for (;;) { // wait for reply UInt8 code[4]; @@ -108,46 +122,46 @@ void CClient::run(const CNetworkAddress& serverAddress) // parse message if (memcmp(code, kMsgDMouseMove, 4) == 0) { - onMouseMove(input.get()); + onMouseMove(); } else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { - onMouseWheel(input.get()); + onMouseWheel(); } else if (memcmp(code, kMsgDKeyDown, 4) == 0) { - onKeyDown(input.get()); + onKeyDown(); } else if (memcmp(code, kMsgDKeyUp, 4) == 0) { - onKeyUp(input.get()); + onKeyUp(); } else if (memcmp(code, kMsgDMouseDown, 4) == 0) { - onMouseDown(input.get()); + onMouseDown(); } else if (memcmp(code, kMsgDMouseUp, 4) == 0) { - onMouseUp(input.get()); + onMouseUp(); } else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { - onKeyRepeat(input.get()); + onKeyRepeat(); } else if (memcmp(code, kMsgCEnter, 4) == 0) { - onEnter(input.get()); + onEnter(); } else if (memcmp(code, kMsgCLeave, 4) == 0) { - onLeave(input.get()); + onLeave(); } else if (memcmp(code, kMsgCClipboard, 4) == 0) { - onGrabClipboard(input.get()); + onGrabClipboard(); } else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { - onScreenSaver(input.get()); + onScreenSaver(); } else if (memcmp(code, kMsgQInfo, 4) == 0) { - onQueryInfo(input.get()); + onQueryInfo(); } else if (memcmp(code, kMsgQClipboard, 4) == 0) { - onQueryClipboard(input.get()); + onQueryClipboard(); } else if (memcmp(code, kMsgDClipboard, 4) == 0) { - onSetClipboard(input.get()); + onSetClipboard(); } else if (memcmp(code, kMsgCClose, 4) == 0) { // server wants us to hangup @@ -159,14 +173,17 @@ void CClient::run(const CNetworkAddress& serverAddress) break; } } - - // done with socket - m_socket->close(); } catch (XBase& e) { fprintf(stderr, "error: %s\n", e.what()); return; } + + // done with screen + screenCleaner.reset(); + + // done with socket + socket->close(); } void CClient::onEnter() @@ -196,7 +213,7 @@ void CClient::onScreenSaver() void CClient::onQueryInfo() { SInt32 w, h; - m_screen->getSize(w, h); + m_screen->getSize(&w, &h); SInt32 zoneSize = m_screen->getJumpZoneSize(); CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); } @@ -215,16 +232,16 @@ void CClient::onKeyDown() { SInt32 id, mask; CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); - m_screen->onKeyDown(reinterpret_cast(id), - reinterpret_cast(mask)); + m_screen->onKeyDown(static_cast(id), + static_cast(mask)); } void CClient::onKeyRepeat() { SInt32 id, mask, count; CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); - m_screen->onKeyRepeat(reinterpret_cast(id), - reinterpret_cast(mask), + m_screen->onKeyRepeat(static_cast(id), + static_cast(mask), count); } @@ -232,22 +249,22 @@ void CClient::onKeyUp() { SInt32 id, mask; CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); - m_screen->onKeyUp(reinterpret_cast(id), - reinterpret_cast(mask)); + m_screen->onKeyUp(static_cast(id), + static_cast(mask)); } void CClient::onMouseDown() { SInt32 id; CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); - m_screen->onMouseDown(reinterpret_cast(id)); + m_screen->onMouseDown(static_cast(id)); } void CClient::onMouseUp() { SInt32 id; CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); - m_screen->onMouseUp(reinterpret_cast(id)); + m_screen->onMouseUp(static_cast(id)); } void CClient::onMouseMove() @@ -263,3 +280,28 @@ void CClient::onMouseWheel() CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); m_screen->onMouseWheel(delta); } + + +// +// CClient::CScreenCleaner +// + +CClient::CScreenCleaner::CScreenCleaner(CClient* client, + ISecondaryScreen* screen) : + m_screen(screen) +{ + assert(m_screen != NULL); + try { + m_screen->open(client); + } + catch (...) { + delete m_screen; + throw; + } +} + +CClient::CScreenCleaner::~CScreenCleaner() +{ + m_screen->close(); + delete m_screen; +} diff --git a/synergy/CClient.h b/synergy/CClient.h index 7ad0b7f7..825e973f 100644 --- a/synergy/CClient.h +++ b/synergy/CClient.h @@ -7,6 +7,7 @@ class CNetworkAddress; class IInputStream; class IOutputStream; +class ISecondaryScreen; class CClient { public: @@ -37,10 +38,21 @@ class CClient { void onMouseMove(); void onMouseWheel(); + private: + class CScreenCleaner { + public: + CScreenCleaner(CClient*, ISecondaryScreen*); + ~CScreenCleaner(); + + private: + ISecondaryScreen* m_screen; + }; + private: CString m_name; - IInputStream* m_output; + IInputStream* m_input; IOutputStream* m_output; + ISecondaryScreen* m_screen; }; #endif diff --git a/synergy/CServer.cpp b/synergy/CServer.cpp index 28699806..94b86968 100644 --- a/synergy/CServer.cpp +++ b/synergy/CServer.cpp @@ -545,6 +545,7 @@ void CServer::mapPosition(CScreenInfo* src, } } +#include "CTCPListenSocket.h" void CServer::acceptClients(void*) { // add this thread to the list of threads to cancel. remove from @@ -554,7 +555,8 @@ void CServer::acceptClients(void*) std::auto_ptr listen; try { // create socket listener - listen.reset(m_socketFactory->createListen()); +// listen.reset(m_socketFactory->createListen()); + listen.reset(new CTCPListenSocket); // FIXME // bind to the desired port. keep retrying if we can't bind // the address immediately. @@ -712,7 +714,12 @@ void CServer::openPrimaryScreen() m_primary->open(this); // add connection - m_active = addConnection(CString("primary"/* FIXME */), NULL); + m_active = addConnection(CString("primary"/* FIXME */), NULL); + + // update info + m_primary->getSize(&m_active->m_width, &m_active->m_height); + m_active->m_zoneSize = m_primary->getJumpZoneSize(); + // FIXME -- need way for primary screen to call us back } void CServer::closePrimaryScreen() throw() diff --git a/synergy/CXWindowsPrimaryScreen.cpp b/synergy/CXWindowsPrimaryScreen.cpp index 1c24b332..9fbc5647 100644 --- a/synergy/CXWindowsPrimaryScreen.cpp +++ b/synergy/CXWindowsPrimaryScreen.cpp @@ -4,7 +4,6 @@ #include "TMethodJob.h" #include #include -#include // // CXWindowsPrimaryScreen diff --git a/synergy/CXWindowsSecondaryScreen.cpp b/synergy/CXWindowsSecondaryScreen.cpp new file mode 100644 index 00000000..d7f1cc4e --- /dev/null +++ b/synergy/CXWindowsSecondaryScreen.cpp @@ -0,0 +1,231 @@ +#include "CXWindowsSecondaryScreen.h" +#include "CClient.h" +#include "CThread.h" +#include "TMethodJob.h" +#include +#include +#include + +// +// CXWindowsSecondaryScreen +// + +CXWindowsSecondaryScreen::CXWindowsSecondaryScreen() : + m_client(NULL), + m_display(NULL), + m_window(None), + m_w(0), m_h(0) +{ + // do nothing +} + +CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() +{ + assert(m_display == NULL); +} + +void CXWindowsSecondaryScreen::open(CClient* client) +{ + assert(m_client == NULL); + assert(client != NULL); + + // set the client + m_client = client; + + // open the display + m_display = ::XOpenDisplay(NULL); // FIXME -- allow non-default + if (m_display == NULL) + throw int(5); // FIXME -- make exception for this + + // get default screen + m_screen = DefaultScreen(m_display); + Screen* screen = ScreenOfDisplay(m_display, m_screen); + + // get screen size + m_w = WidthOfScreen(screen); + m_h = HeightOfScreen(screen); + + // verify the availability of the XTest extension + int majorOpcode, firstEvent, firstError; + if (!::XQueryExtension(m_display, XTestExtensionName, + &majorOpcode, &firstEvent, &firstError)) + throw int(6); // FIXME -- make exception for this + + // become impervious to server grabs + ::XTestGrabControl(m_display, True); + + // start processing events + m_eventThread = new CThread(new TMethodJob( + this, &CXWindowsSecondaryScreen::eventThread)); +} + +void CXWindowsSecondaryScreen::close() +{ + assert(m_client != NULL); + assert(m_eventThread != NULL); + + // stop event thread + m_eventThread->cancel(); + m_eventThread->wait(); + delete m_eventThread; + m_eventThread = NULL; + + // no longer impervious to server grabs + ::XTestGrabControl(m_display, False); + + // close the display + ::XCloseDisplay(m_display); + m_display = NULL; +} + +void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) +{ + assert(m_display != NULL); + + // warp to requested location + warpCursor(x, y); + + // show cursor + // FIXME +} + +void CXWindowsSecondaryScreen::leave() +{ + assert(m_display != NULL); + + // hide cursor + // FIXME +} + +void CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + assert(m_display != NULL); + + ::XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); + ::XSync(m_display, False); +} + +void CXWindowsSecondaryScreen::onKeyDown( + KeyID key, KeyModifierMask mask) +{ + assert(m_display != NULL); + + ::XTestFakeKeyEvent(m_display, mapKey(key, mask), True, CurrentTime); + ::XSync(m_display, False); +} + +void CXWindowsSecondaryScreen::onKeyRepeat( + KeyID, KeyModifierMask, SInt32) +{ + assert(m_display != NULL); + + // FIXME +} + +void CXWindowsSecondaryScreen::onKeyUp( + KeyID key, KeyModifierMask mask) +{ + assert(m_display != NULL); + + ::XTestFakeKeyEvent(m_display, mapKey(key, mask), False, CurrentTime); + ::XSync(m_display, False); +} + +void CXWindowsSecondaryScreen::onMouseDown(ButtonID button) +{ + assert(m_display != NULL); + + ::XTestFakeButtonEvent(m_display, mapButton(button), True, CurrentTime); + ::XSync(m_display, False); +} + +void CXWindowsSecondaryScreen::onMouseUp(ButtonID button) +{ + assert(m_display != NULL); + + ::XTestFakeButtonEvent(m_display, mapButton(button), False, CurrentTime); + ::XSync(m_display, False); +} + +void CXWindowsSecondaryScreen::onMouseMove( + SInt32 x, SInt32 y) +{ + assert(m_display != NULL); + + ::XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); + ::XSync(m_display, False); +} + +void CXWindowsSecondaryScreen::onMouseWheel(SInt32) +{ + assert(m_display != NULL); + + // FIXME +} + +void CXWindowsSecondaryScreen::getSize( + SInt32* width, SInt32* height) const +{ + assert(m_display != NULL); + assert(width != NULL && height != NULL); + + *width = m_w; + *height = m_h; +} + +SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const +{ + assert(m_display != NULL); + + return 0; +} + +void CXWindowsSecondaryScreen::eventThread(void*) +{ + for (;;) { + // wait for and then get the next event + while (XPending(m_display) == 0) { + CThread::sleep(0.05); + } + XEvent xevent; + XNextEvent(m_display, &xevent); + + // handle event + switch (xevent.type) { + case LeaveNotify: + // mouse moved out of window somehow. hide the window. +// ::XUnmapWindow(m_display, m_window); + break; + +/* + // FIXME -- handle screen resolution changes + + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; +*/ + } + } +} + +KeyCode CXWindowsSecondaryScreen::mapKey( + KeyID id, KeyModifierMask /*mask*/) const +{ + // FIXME -- use mask + return ::XKeysymToKeycode(m_display, static_cast(id)); +} + +unsigned int CXWindowsSecondaryScreen::mapButton( + ButtonID id) const +{ + // FIXME -- should use button mapping? + return static_cast(id); +} diff --git a/synergy/CXWindowsSecondaryScreen.h b/synergy/CXWindowsSecondaryScreen.h new file mode 100644 index 00000000..06cf0abe --- /dev/null +++ b/synergy/CXWindowsSecondaryScreen.h @@ -0,0 +1,44 @@ +#ifndef CXWINDOWSSECONDARYSCREEN_H +#define CXWINDOWSSECONDARYSCREEN_H + +#include "ISecondaryScreen.h" +#include + +class CThread; + +class CXWindowsSecondaryScreen : public ISecondaryScreen { + public: + CXWindowsSecondaryScreen(); + virtual ~CXWindowsSecondaryScreen(); + + // ISecondaryScreen overrides + virtual void open(CClient*); + virtual void close(); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void leave(); + virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void onKeyDown(KeyID, KeyModifierMask); + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void onKeyUp(KeyID, KeyModifierMask); + virtual void onMouseDown(ButtonID); + virtual void onMouseUp(ButtonID); + virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void onMouseWheel(SInt32 delta); + virtual void getSize(SInt32* width, SInt32* height) const; + virtual SInt32 getJumpZoneSize() const; + + private: + void eventThread(void*); + KeyCode mapKey(KeyID, KeyModifierMask) const; + unsigned int mapButton(ButtonID button) const; + + private: + CClient* m_client; + CThread* m_eventThread; + Display* m_display; + int m_screen; + Window m_window; + SInt32 m_w, m_h; +}; + +#endif diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 1deb45f9..8d24a64c 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -3,6 +3,8 @@ #include "IInterface.h" #include "BasicTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" class CClient; //class IClipboard; diff --git a/synergy/Makefile b/synergy/Makefile index c16c90e7..5305a5c5 100644 --- a/synergy/Makefile +++ b/synergy/Makefile @@ -15,11 +15,13 @@ CXXFILES = \ COutputPacketStream.cpp \ CTCPSocketFactory.cpp \ CProtocolUtil.cpp \ + CClient.cpp \ CServerProtocol.cpp \ CServerProtocol1_0.cpp \ CScreenMap.cpp \ CServer.cpp \ CXWindowsPrimaryScreen.cpp \ + CXWindowsSecondaryScreen.cpp \ XSynergy.cpp \ $(NULL) @@ -37,8 +39,11 @@ LLDLIBS = \ -lpthread \ $(NULL) -targets: server +targets: server client -server: $(OBJECTS) $(DEPLIBS) - $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) +server: server.o $(OBJECTS) $(DEPLIBS) + $(CXX) $(CXXFLAGS) -o $@ server.o $(OBJECTS) $(LDFLAGS) + +client: client.o $(OBJECTS) $(DEPLIBS) + $(CXX) $(CXXFLAGS) -o $@ client.o $(OBJECTS) $(LDFLAGS) diff --git a/synergy/client.cpp b/synergy/client.cpp new file mode 100644 index 00000000..b1c3c16b --- /dev/null +++ b/synergy/client.cpp @@ -0,0 +1,21 @@ +#include "CClient.h" +#include "CNetworkAddress.h" +#include + +int main(int argc, char** argv) +{ + if (argc != 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + + try { + CClient* client = new CClient("ingrid"); + client->run(CNetworkAddress(argv[1], 50001)); + } + catch (XBase& e) { + fprintf(stderr, "failed: %s\n", e.what()); + return 1; + } + return 0; +} diff --git a/synergy/server.cpp b/synergy/server.cpp new file mode 100644 index 00000000..29c8466b --- /dev/null +++ b/synergy/server.cpp @@ -0,0 +1,29 @@ +#include "CServer.h" +#include "CScreenMap.h" +#include + +int main(int argc, char** argv) +{ + if (argc != 1) { + fprintf(stderr, "usage: %s\n", argv[0]); + return 1; + } + + CScreenMap screenMap; + screenMap.addScreen("primary"); + screenMap.addScreen("ingrid"); + screenMap.connect("primary", CScreenMap::kRight, "ingrid"); + screenMap.connect("ingrid", CScreenMap::kLeft, "primary"); + + try { + CServer* server = new CServer(); + server->setScreenMap(screenMap); + server->run(); + } + catch (XBase& e) { + fprintf(stderr, "failed: %s\n", e.what()); + return 1; + } + + return 0; +} From 1ed2b43e13b7a014861b584cb55fff0b0f96fdcd Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 14 Oct 2001 14:37:41 +0000 Subject: [PATCH 010/807] added logging facility and added a bunch of log messages. --- Make-linux | 2 +- base/Makefile | 1 + synergy/CClient.cpp | 31 +++++++---- synergy/CScreenMap.cpp | 6 +++ synergy/CScreenMap.h | 3 ++ synergy/CServer.cpp | 85 +++++++++++++++++++++++------- synergy/CServerProtocol1_0.cpp | 18 +++++++ synergy/CXWindowsPrimaryScreen.cpp | 21 +++++++- 8 files changed, 136 insertions(+), 31 deletions(-) diff --git a/Make-linux b/Make-linux index c64314b5..e3e656d6 100644 --- a/Make-linux +++ b/Make-linux @@ -13,7 +13,7 @@ RMR = /bin/rm -rf # # compiler options # -GCXXDEFS = -D_POSIX_C_SOURCE=199309 +GCXXDEFS = -D_XOPEN_SOURCE=500 GCXXINCS = -I$(DEPTH)/include -I/usr/X11R6/include GCXXOPTS = -Wall -W -fexceptions -fno-rtti CXXOPTIMIZER = -g diff --git a/base/Makefile b/base/Makefile index 42128cec..8e262bd4 100644 --- a/base/Makefile +++ b/base/Makefile @@ -13,6 +13,7 @@ LCXXINCS = \ $(NULL) CXXFILES = \ XBase.cpp \ + CLog.cpp \ CFunctionJob.cpp \ CStopwatch.cpp \ $(NULL) diff --git a/synergy/CClient.cpp b/synergy/CClient.cpp index 1a20bc1e..6fb1d215 100644 --- a/synergy/CClient.cpp +++ b/synergy/CClient.cpp @@ -6,7 +6,7 @@ #include "ProtocolTypes.h" #include "CTimerThread.h" #include "XSynergy.h" -#include +#include "CLog.h" #include // @@ -29,6 +29,8 @@ CClient::~CClient() #include "CXWindowsSecondaryScreen.h" void CClient::run(const CNetworkAddress& serverAddress) { + log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); + std::auto_ptr socket; std::auto_ptr input; std::auto_ptr output; @@ -37,8 +39,10 @@ void CClient::run(const CNetworkAddress& serverAddress) CTimerThread timer(30.0); // FIXME -- timeout in member // create socket and attempt to connect to server + log((CLOG_DEBUG "connecting to server")); socket.reset(new CTCPSocket()); // FIXME -- use factory socket->connect(serverAddress); + log((CLOG_INFO "connected to server")); // get the input and output streams IInputStream* srcInput = socket->getInputStream(); @@ -59,16 +63,19 @@ void CClient::run(const CNetworkAddress& serverAddress) output.reset(new COutputPacketStream(srcOutput, true)); // wait for hello from server + log((CLOG_DEBUG "wait for hello")); SInt32 major, minor; CProtocolUtil::readf(input.get(), "Synergy%2i%2i", &major, &minor); // check versions + log((CLOG_DEBUG "got hello version %d.%d", major, minor)); if (major < kMajorVersion || (major == kMajorVersion && minor < kMinorVersion)) { throw XIncompatibleClient(major, minor); } // say hello back + log((CLOG_DEBUG "say hello version %d.%d", kMajorVersion, kMinorVersion)); CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s", kMajorVersion, kMinorVersion, m_name.size(), m_name.data()); @@ -78,27 +85,27 @@ void CClient::run(const CNetworkAddress& serverAddress) m_output = output.get(); } catch (XIncompatibleClient& e) { - fprintf(stderr, "incompatible server version (%d.%d)\n", - e.getMajor(), e.getMinor()); + log((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); return; } catch (XThread&) { - fprintf(stderr, "connection timed out\n"); + log((CLOG_ERR "connection timed out")); throw; } catch (XBase& e) { - fprintf(stderr, "connection failed: %s\n", e.what()); + log((CLOG_ERR "connection failed: %s", e.what())); return; } // connect to screen std::auto_ptr screenCleaner; try { + log((CLOG_DEBUG "creating secondary screen")); m_screen = new CXWindowsSecondaryScreen; screenCleaner.reset(new CScreenCleaner(this, m_screen)); } catch (XBase& e) { - fprintf(stderr, "cannot open screen: %s\n", e.what()); + log((CLOG_ERR "cannot open screen: %s", e.what())); return; } @@ -106,21 +113,24 @@ void CClient::run(const CNetworkAddress& serverAddress) // handle messages from server for (;;) { // wait for reply + log((CLOG_DEBUG "waiting for message")); UInt8 code[4]; UInt32 n = input->read(code, 4); // verify we got an entire code if (n == 0) { + log((CLOG_NOTE "server disconnected")); // server hungup break; } if (n != 4) { // client sent an incomplete message - fprintf(stderr, "incomplete message from server\n"); + log((CLOG_ERR "incomplete message from server")); break; } // parse message + log((CLOG_DEBUG "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); if (memcmp(code, kMsgDMouseMove, 4) == 0) { onMouseMove(); } @@ -169,20 +179,22 @@ void CClient::run(const CNetworkAddress& serverAddress) } else { // unknown message - fprintf(stderr, "unknown message from server\n"); + log((CLOG_ERR "unknown message from server")); break; } } } catch (XBase& e) { - fprintf(stderr, "error: %s\n", e.what()); + log((CLOG_ERR "error: %s", e.what())); return; } // done with screen + log((CLOG_DEBUG "destroying secondary screen")); screenCleaner.reset(); // done with socket + log((CLOG_DEBUG "disconnecting from server")); socket->close(); } @@ -215,6 +227,7 @@ void CClient::onQueryInfo() SInt32 w, h; m_screen->getSize(&w, &h); SInt32 zoneSize = m_screen->getJumpZoneSize(); + log((CLOG_DEBUG "sending info size=%d,%d zone=%d", w, h, zoneSize)); CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); } diff --git a/synergy/CScreenMap.cpp b/synergy/CScreenMap.cpp index 7e59ea1d..eb2b4d7f 100644 --- a/synergy/CScreenMap.cpp +++ b/synergy/CScreenMap.cpp @@ -87,3 +87,9 @@ CString CScreenMap::getNeighbor(const CString& srcName, // return connection return index->second.m_neighbor[srcSide - kFirstDirection]; } + +const char* CScreenMap::dirName(EDirection dir) +{ + static const char* s_name[] = { "left", "right", "top", "bottom" }; + return s_name[dir - kFirstDirection]; +} diff --git a/synergy/CScreenMap.h b/synergy/CScreenMap.h index 234ae05b..816b8f93 100644 --- a/synergy/CScreenMap.h +++ b/synergy/CScreenMap.h @@ -33,6 +33,9 @@ class CScreenMap { // if there is no neighbor in that direction. CString getNeighbor(const CString&, EDirection) const throw(); + // get the name of a direction (for debugging) + static const char* dirName(EDirection); + private: class CCell { public: diff --git a/synergy/CServer.cpp b/synergy/CServer.cpp index 94b86968..5a579c30 100644 --- a/synergy/CServer.cpp +++ b/synergy/CServer.cpp @@ -16,7 +16,7 @@ #include "CTimerThread.h" #include "CStopwatch.h" #include "TMethodJob.h" -#include +#include "CLog.h" #include #include @@ -38,6 +38,8 @@ CServer::~CServer() void CServer::run() { try { + log((CLOG_NOTE "starting server")); + // connect to primary screen openPrimaryScreen(); @@ -48,23 +50,27 @@ void CServer::run() // FIXME // wait until done + log((CLOG_DEBUG "waiting for quit")); CLock lock(&m_mutex); while (m_done == false) { m_done.wait(); } // clean up + log((CLOG_DEBUG "stopping server")); closePrimaryScreen(); cleanupThreads(); } catch (XBase& e) { - fprintf(stderr, "server error: %s\n", e.what()); + log((CLOG_ERR "server error: %s\n", e.what())); // clean up closePrimaryScreen(); cleanupThreads(); } catch (...) { + log((CLOG_DEBUG "server shutdown")); + // clean up closePrimaryScreen(); cleanupThreads(); @@ -108,6 +114,7 @@ void CServer::setInfo(const CString& client, info->m_width = w; info->m_height = h; info->m_zoneSize = zoneSize; + log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d", client.c_str(), w, h, zoneSize)); } bool CServer::onCommandKey(KeyID /*id*/, @@ -118,6 +125,7 @@ bool CServer::onCommandKey(KeyID /*id*/, void CServer::onKeyDown(KeyID id, KeyModifierMask mask) { + log((CLOG_DEBUG "onKeyDown id=%d mask=0x%04x", id, mask)); assert(m_active != NULL); // handle command keys @@ -133,6 +141,7 @@ void CServer::onKeyDown(KeyID id, KeyModifierMask mask) void CServer::onKeyUp(KeyID id, KeyModifierMask mask) { + log((CLOG_DEBUG "onKeyUp id=%d mask=0x%04x", id, mask)); assert(m_active != NULL); // handle command keys @@ -148,6 +157,7 @@ void CServer::onKeyUp(KeyID id, KeyModifierMask mask) void CServer::onKeyRepeat(KeyID id, KeyModifierMask mask) { + log((CLOG_DEBUG "onKeyRepeat id=%d mask=0x%04x", id, mask)); assert(m_active != NULL); // handle command keys @@ -164,6 +174,7 @@ void CServer::onKeyRepeat(KeyID id, KeyModifierMask mask) void CServer::onMouseDown(ButtonID id) { + log((CLOG_DEBUG "onMouseDown id=%d", id)); assert(m_active != NULL); // relay @@ -174,6 +185,7 @@ void CServer::onMouseDown(ButtonID id) void CServer::onMouseUp(ButtonID id) { + log((CLOG_DEBUG "onMouseUp id=%d", id)); assert(m_active != NULL); // relay @@ -184,6 +196,8 @@ void CServer::onMouseUp(ButtonID id) void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) { + log((CLOG_DEBUG "onMouseMovePrimary %d,%d", x, y)); + // mouse move on primary (server's) screen assert(m_active != NULL); assert(m_active->m_protocol == NULL); @@ -198,18 +212,22 @@ void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) if (x < m_active->m_zoneSize) { x -= m_active->m_zoneSize; dir = CScreenMap::kLeft; + log((CLOG_DEBUG "switch to left")); } else if (x >= m_active->m_width - m_active->m_zoneSize) { x += m_active->m_zoneSize; dir = CScreenMap::kRight; + log((CLOG_DEBUG "switch to right")); } else if (y < m_active->m_zoneSize) { y -= m_active->m_zoneSize; dir = CScreenMap::kTop; + log((CLOG_DEBUG "switch to top")); } else if (y >= m_active->m_height - m_active->m_zoneSize) { y += m_active->m_zoneSize; dir = CScreenMap::kBottom; + log((CLOG_DEBUG "switch to bottom")); } else { // still on local screen @@ -233,6 +251,8 @@ void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) { + log((CLOG_DEBUG "onMouseMoveSecondary %+d,%+d", dx, dy)); + // mouse move on secondary (client's) screen assert(m_active != NULL); assert(m_active->m_protocol != NULL); @@ -264,8 +284,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) // get neighbor if we should switch if (newScreen == NULL) { -// TRACE(("leave %s on %s", m_activeScreen->getName().c_str(), -// s_dirName[dir])); + log((CLOG_DEBUG "leave \"%s\" on %s", m_active->m_name.c_str(), CScreenMap::dirName(dir))); SInt32 x = m_x, y = m_y; newScreen = getNeighbor(m_active, dir, x, y); @@ -290,7 +309,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) } else { // clamp to edge when locked -// TRACE(("clamp to %s", m_activeScreen->getName().c_str())); + log((CLOG_DEBUG "clamp to \"%s\"", m_active->m_name.c_str())); if (m_x < 0) m_x = 0; else if (m_x > m_active->m_width - 1) @@ -305,8 +324,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) if (newScreen == NULL || newScreen == m_active) { // do nothing if mouse didn't move if (m_x != xOld || m_y != yOld) { -// TRACE(("move on %s to %d,%d", -// m_activeScreen->getName().c_str(), m_x, m_y)); + log((CLOG_DEBUG "move on %s to %d,%d", m_active->m_name.c_str(), m_x, m_y)); m_active->m_protocol->sendMouseMove(m_x, m_y); } } @@ -319,6 +337,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) void CServer::onMouseWheel(SInt32 delta) { + log((CLOG_DEBUG "onMouseWheel %+d", delta)); assert(m_active != NULL); // relay @@ -340,8 +359,7 @@ void CServer::switchScreen(CScreenInfo* dst, assert(x >= 0 && y >= 0 && x < dst->m_width && y < dst->m_height); assert(m_active != NULL); -// TRACE(("switch %s to %s at %d,%d", m_active->m_name.c_str(), -// dst->m_name.c_str(), x, y)); + log((CLOG_NOTE "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y)); // wrapping means leaving the active screen and entering it again. // since that's a waste of time we skip that and just warp the @@ -387,22 +405,27 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, CString srcName = src->m_name; assert(!srcName.empty()); + log((CLOG_DEBUG "find neighbor on %s of \"%s\"", CScreenMap::dirName(dir), srcName.c_str())); for (;;) { // look up name of neighbor const CString dstName(m_screenMap.getNeighbor(srcName, dir)); // if nothing in that direction then return NULL - if (dstName.empty()) + if (dstName.empty()) { + log((CLOG_DEBUG "no neighbor on %s of \"%s\"", CScreenMap::dirName(dir), srcName.c_str())); return NULL; + } // look up neighbor cell. if the screen is connected then // we can stop. otherwise we skip over an unconnected // screen. CScreenList::const_iterator index = m_screens.find(dstName); if (index != m_screens.end()) { + log((CLOG_DEBUG "\"%s\" is on %s of \"%s\"", dstName.c_str(), CScreenMap::dirName(dir), srcName.c_str())); return index->second; } + log((CLOG_DEBUG "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CScreenMap::dirName(dir), srcName.c_str())); srcName = dstName; } } @@ -431,7 +454,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, if (x >= 0) { break; } -// TRACE(("skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } break; @@ -445,7 +468,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, if (x < w) { break; } -// TRACE(("skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } break; @@ -459,7 +482,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, if (y >= 0) { break; } -// TRACE(("skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } break; @@ -473,7 +496,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, if (y < h) { break; } -// TRACE(("skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } break; @@ -548,6 +571,8 @@ void CServer::mapPosition(CScreenInfo* src, #include "CTCPListenSocket.h" void CServer::acceptClients(void*) { + log((CLOG_DEBUG "starting to wait for clients")); + // add this thread to the list of threads to cancel. remove from // list in d'tor. CCleanupNote cleanupNote(this); @@ -564,25 +589,30 @@ void CServer::acceptClients(void*) CNetworkAddress addr(50001 /* FIXME -- m_port */); for (;;) { try { + log((CLOG_DEBUG "binding listen socket")); listen->bind(addr); break; } catch (XSocketAddressInUse&) { // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { + log((CLOG_DEBUG "waited too long to bind, giving up")); throw; } // wait a bit before retrying + log((CLOG_DEBUG "bind failed; waiting to retry")); CThread::sleep(5.0); } } // accept connections and begin processing them + log((CLOG_DEBUG "waiting for client connections")); for (;;) { // accept connection CThread::testCancel(); ISocket* socket = listen->accept(); + log((CLOG_NOTE "accepted client connection")); CThread::testCancel(); // start handshake thread @@ -591,13 +621,15 @@ void CServer::acceptClients(void*) } } catch (XBase& e) { - fprintf(stderr, "cannot listen for clients: %s\n", e.what()); + log((CLOG_ERR "cannot listen for clients: %s", e.what())); quit(); } } void CServer::handshakeClient(void* vsocket) { + log((CLOG_DEBUG "negotiating with new client")); + // get the socket pointer from the argument assert(vsocket != NULL); std::auto_ptr socket(reinterpret_cast(vsocket)); @@ -638,11 +670,13 @@ void CServer::handshakeClient(void* vsocket) static const UInt32 maxHelloLen = 1024; // say hello + log((CLOG_DEBUG "saying hello")); CProtocolUtil::writef(output.get(), "Synergy%2i%2i", kMajorVersion, kMinorVersion); output->flush(); // wait for the reply + log((CLOG_DEBUG "waiting for hello reply")); UInt32 n = input->getSize(); if (n > maxHelloLen) { throw XBadClient(); @@ -651,6 +685,7 @@ void CServer::handshakeClient(void* vsocket) // get and parse the reply to hello SInt32 major, minor; try { + log((CLOG_DEBUG "parsing hello reply")); CProtocolUtil::readf(input.get(), "Synergy%2i%2i%s", &major, &minor, &name); } @@ -662,6 +697,7 @@ void CServer::handshakeClient(void* vsocket) } // create a protocol interpreter for the version + log((CLOG_DEBUG "creating interpreter for client %s version %d.%d", name.c_str(), major, minor)); protocol.reset(CServerProtocol::create(major, minor, this, name, input.get(), output.get())); @@ -670,28 +706,28 @@ void CServer::handshakeClient(void* vsocket) name, protocol.get())); // ask and wait for the client's info + log((CLOG_DEBUG "waiting for info for client %s", name.c_str())); protocol->queryInfo(); } // handle messages from client. returns when the client // disconnects. + log((CLOG_NOTE "client %s is connected", name.c_str())); protocol->run(); } catch (XIncompatibleClient& e) { // client is incompatible - fprintf(stderr, "client is incompatible (%s, %d.%d)\n", - name.c_str(), e.getMajor(), e.getMinor()); + log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); // FIXME -- could print network address if socket had suitable method } catch (XBadClient&) { // client not behaving - fprintf(stderr, "protocol error from client %s\n", name.c_str()); + log((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); // FIXME -- could print network address if socket had suitable method } catch (XBase& e) { // misc error - fprintf(stderr, "error communicating with client %s: %s\n", - name.c_str(), e.what()); + log((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); // FIXME -- could print network address if socket had suitable method } } @@ -710,7 +746,9 @@ void CServer::openPrimaryScreen() assert(m_primary == NULL); // open screen + log((CLOG_DEBUG "creating primary screen")); m_primary = new CXWindowsPrimaryScreen; + log((CLOG_DEBUG "opening primary screen")); m_primary->open(this); // add connection @@ -719,6 +757,7 @@ void CServer::openPrimaryScreen() // update info m_primary->getSize(&m_active->m_width, &m_active->m_height); m_active->m_zoneSize = m_primary->getJumpZoneSize(); + log((CLOG_NOTE "server size=%dx%d zone=%d", m_active->m_width, m_active->m_height, m_active->m_zoneSize)); // FIXME -- need way for primary screen to call us back } @@ -731,6 +770,7 @@ void CServer::closePrimaryScreen() throw() // close the primary screen try { + log((CLOG_DEBUG "closing primary screen")); m_primary->close(); } catch (...) { @@ -738,6 +778,7 @@ void CServer::closePrimaryScreen() throw() } // clean up + log((CLOG_DEBUG "destroying primary screen")); delete m_primary; m_primary = NULL; } @@ -763,6 +804,7 @@ void CServer::removeCleanupThread(const CThread& thread) void CServer::cleanupThreads() throw() { + log((CLOG_DEBUG "cleaning up threads")); m_mutex.lock(); while (m_cleanupList.begin() != m_cleanupList.end()) { // get the next thread and cancel it @@ -778,11 +820,13 @@ void CServer::cleanupThreads() throw() // FIXME -- delete remaining threads from list m_mutex.unlock(); + log((CLOG_DEBUG "cleaned up threads")); } CServer::CScreenInfo* CServer::addConnection( const CString& name, IServerProtocol* protocol) { + log((CLOG_DEBUG "adding connection \"%s\"", name.c_str())); CLock lock(&m_mutex); assert(m_screens.count(name) == 0); CScreenInfo* newScreen = new CScreenInfo(name, protocol); @@ -792,6 +836,7 @@ CServer::CScreenInfo* CServer::addConnection( void CServer::removeConnection(const CString& name) { + log((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); CLock lock(&m_mutex); CScreenList::iterator index = m_screens.find(name); assert(index == m_screens.end()); diff --git a/synergy/CServerProtocol1_0.cpp b/synergy/CServerProtocol1_0.cpp index eb6ab9cc..2efe6aa1 100644 --- a/synergy/CServerProtocol1_0.cpp +++ b/synergy/CServerProtocol1_0.cpp @@ -3,6 +3,7 @@ #include "CProtocolUtil.h" #include "ProtocolTypes.h" #include "IInputStream.h" +#include "CLog.h" #include // @@ -40,6 +41,7 @@ void CServerProtocol1_0::run() throw(XIO,XBadClient) } // parse message + log((CLOG_DEBUG "msg from \"%s\": %c%c%c%c", getClient().c_str(), code[0], code[1], code[2], code[3])); if (memcmp(code, kMsgDInfo, 4) == 0) { recvInfo(); } @@ -53,6 +55,8 @@ void CServerProtocol1_0::run() throw(XIO,XBadClient) void CServerProtocol1_0::queryInfo() throw(XIO,XBadClient) { + log((CLOG_INFO "querying client \"%s\" info", getClient().c_str())); + // send request CProtocolUtil::writef(getOutputStream(), kMsgQInfo); @@ -69,74 +73,87 @@ void CServerProtocol1_0::queryInfo() throw(XIO,XBadClient) void CServerProtocol1_0::sendClose() throw(XIO) { + log((CLOG_INFO "send close to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClose); } void CServerProtocol1_0::sendEnter( SInt32 xAbs, SInt32 yAbs) throw(XIO) { + log((CLOG_INFO "send enter to \"%s\", %d,%d", getClient().c_str(), xAbs, yAbs)); CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs); } void CServerProtocol1_0::sendLeave() throw(XIO) { + log((CLOG_INFO "send leave to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCLeave); } void CServerProtocol1_0::sendGrabClipboard() throw(XIO) { + log((CLOG_INFO "send grab clipboard to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard); } void CServerProtocol1_0::sendQueryClipboard() throw(XIO) { + log((CLOG_INFO "query clipboard to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgQClipboard); } void CServerProtocol1_0::sendScreenSaver(bool on) throw(XIO) { + log((CLOG_INFO "send screen saver to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } void CServerProtocol1_0::sendKeyDown( KeyID key, KeyModifierMask mask) throw(XIO) { + log((CLOG_INFO "send key down to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); } void CServerProtocol1_0::sendKeyRepeat( KeyID key, KeyModifierMask mask) throw(XIO) { + log((CLOG_INFO "send key repeat to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask); } void CServerProtocol1_0::sendKeyUp( KeyID key, KeyModifierMask mask) throw(XIO) { + log((CLOG_INFO "send key up to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); } void CServerProtocol1_0::sendMouseDown( ButtonID button) throw(XIO) { + log((CLOG_INFO "send mouse down to \"%s\" id=%d", getClient().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); } void CServerProtocol1_0::sendMouseUp( ButtonID button) throw(XIO) { + log((CLOG_INFO "send mouse up to \"%s\" id=%d", getClient().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); } void CServerProtocol1_0::sendMouseMove( SInt32 xAbs, SInt32 yAbs) throw(XIO) { + log((CLOG_INFO "send mouse move to \"%s\" %d,%d", getClient().c_str(), xAbs, yAbs)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); } void CServerProtocol1_0::sendMouseWheel( SInt32 delta) throw(XIO) { + log((CLOG_INFO "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); } @@ -145,6 +162,7 @@ void CServerProtocol1_0::recvInfo() throw(XIO,XBadClient) // parse the message SInt32 w, h, zoneInfo; CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, &w, &h, &zoneInfo); + log((CLOG_INFO "received client \"%s\" info size=%dx%d, zone=%d", getClient().c_str(), w, h, zoneInfo)); // validate if (w == 0 || h == 0) { diff --git a/synergy/CXWindowsPrimaryScreen.cpp b/synergy/CXWindowsPrimaryScreen.cpp index 9fbc5647..cafbbcb8 100644 --- a/synergy/CXWindowsPrimaryScreen.cpp +++ b/synergy/CXWindowsPrimaryScreen.cpp @@ -2,6 +2,7 @@ #include "CServer.h" #include "CThread.h" #include "TMethodJob.h" +#include "CLog.h" #include #include @@ -33,6 +34,7 @@ void CXWindowsPrimaryScreen::open(CServer* server) m_server = server; // open the display + log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); m_display = ::XOpenDisplay(NULL); // FIXME -- allow non-default if (m_display == NULL) throw int(5); // FIXME -- make exception for this @@ -44,6 +46,7 @@ void CXWindowsPrimaryScreen::open(CServer* server) // get screen size m_w = WidthOfScreen(screen); m_h = HeightOfScreen(screen); + log((CLOG_INFO "primary display size: %dx%d", m_w, m_h)); // get the root window Window root = RootWindow(m_display, m_screen); @@ -80,10 +83,12 @@ void CXWindowsPrimaryScreen::close() assert(m_eventThread != NULL); // stop event thread + log((CLOG_DEBUG "stopping event thread")); m_eventThread->cancel(); m_eventThread->wait(); delete m_eventThread; m_eventThread = NULL; + log((CLOG_DEBUG "stopped event thread")); // destroy window ::XDestroyWindow(m_display, m_window); @@ -92,10 +97,12 @@ void CXWindowsPrimaryScreen::close() // close the display ::XCloseDisplay(m_display); m_display = NULL; + log((CLOG_DEBUG "closed display")); } void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) { + log((CLOG_INFO "entering primary at %d,%d", x, y)); assert(m_display != NULL); assert(m_window != None); assert(m_active == true); @@ -123,6 +130,7 @@ void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) void CXWindowsPrimaryScreen::leave() { + log((CLOG_INFO "leaving primary")); assert(m_display != NULL); assert(m_window != None); assert(m_active == false); @@ -141,9 +149,12 @@ void CXWindowsPrimaryScreen::leave() GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime); assert(result != GrabNotViewable); - if (result != GrabSuccess) + if (result != GrabSuccess) { + log((CLOG_DEBUG "waiting to grab pointer")); CThread::sleep(0.25); + } } while (result != GrabSuccess); + log((CLOG_DEBUG "grabbed pointer")); // now the keyboard result = ::XGrabKeyboard(m_display, m_window, True, @@ -151,9 +162,11 @@ void CXWindowsPrimaryScreen::leave() assert(result != GrabNotViewable); if (result != GrabSuccess) { ::XUngrabPointer(m_display, CurrentTime); + log((CLOG_DEBUG "ungrabbed pointer, waiting to grab keyboard")); CThread::sleep(0.25); } } while (result != GrabSuccess); + log((CLOG_DEBUG "grabbed keyboard")); // move the mouse to the center of grab window warpCursor(m_w >> 1, m_h >> 1); @@ -169,6 +182,7 @@ void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) Window root = RootWindow(m_display, m_screen); ::XWarpPointer(m_display, None, root, 0, 0, 0, 0, x, y); ::XSync(m_display, False); + log((CLOG_DEBUG "warped to %d,%d", x, y)); // discard mouse events since we just added one we don't want XEvent xevent; @@ -237,6 +251,7 @@ void CXWindowsPrimaryScreen::eventThread(void*) break; case KeyPress: { + log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(xevent.xkey.keycode, mask); if (key != kKeyNone) { @@ -248,6 +263,7 @@ void CXWindowsPrimaryScreen::eventThread(void*) // FIXME -- simulate key repeat. X sends press/release for // repeat. must detect auto repeat and use kKeyRepeat. case KeyRelease: { + log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(xevent.xkey.keycode, mask); if (key != kKeyNone) { @@ -257,6 +273,7 @@ void CXWindowsPrimaryScreen::eventThread(void*) } case ButtonPress: { + log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { m_server->onMouseDown(button); @@ -265,6 +282,7 @@ void CXWindowsPrimaryScreen::eventThread(void*) } case ButtonRelease: { + log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { m_server->onMouseUp(button); @@ -273,6 +291,7 @@ void CXWindowsPrimaryScreen::eventThread(void*) } case MotionNotify: { + log((CLOG_DEBUG "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); SInt32 x, y; if (!m_active) { x = xevent.xmotion.x_root; From 22b99b6ca449596fa2d69f55fb74aa6d5910ce01 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 14 Oct 2001 14:38:45 +0000 Subject: [PATCH 011/807] forgot to add the logger files. --- base/CLog.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ base/CLog.h | 35 ++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 base/CLog.cpp create mode 100644 base/CLog.h diff --git a/base/CLog.cpp b/base/CLog.cpp new file mode 100644 index 00000000..92a32744 --- /dev/null +++ b/base/CLog.cpp @@ -0,0 +1,110 @@ +#include "CLog.h" +#include +#include +#include + +// +// CLog +// + +void CLog::print(const char* fmt, ...) +{ + // check if fmt begins with a priority argument + int priority = 4; + if (fmt[0] == '%' && fmt[1] == 'z') { + priority = fmt[2] - '\060'; + fmt += 3; + } + + // print to buffer + char stack[1024]; + va_list args; + va_start(args, fmt); + char* buffer = vsprint(0, stack, + sizeof(stack) / sizeof(stack[0]), fmt, args); + va_end(args); + + // output buffer + output(priority, buffer); + + // clean up + if (buffer != stack) + delete[] buffer; +} + +void CLog::printt(const char* file, int line, + const char* fmt, ...) +{ + // check if fmt begins with a priority argument + int priority = 4; + if (fmt[0] == '%' && fmt[1] == 'z') { + priority = fmt[2] - '\060'; + fmt += 3; + } + + // compute prefix padding length + char stack[1024]; + sprintf(stack, "%d", line); + int pad = strlen(file) + 1 + strlen(stack) + 1 + 1; + + // print to buffer + va_list args; + va_start(args, fmt); + char* buffer = vsprint(pad, stack, + sizeof(stack) / sizeof(stack[0]), fmt, args); + va_end(args); + + // print the prefix to the buffer + sprintf(buffer, "%s,%d:", file, line); + buffer[pad - 1] = ' '; + + // output buffer + output(priority, buffer); + + // clean up + if (buffer != stack) + delete[] buffer; +} + +void CLog::output(int priority, const char* msg) +{ + static const char* s_priority[] = { + "FATAL", + "ERROR", + "WARNING", + "NOTE", + "INFO", + "DEBUG", + }; + + assert(priority >= 0 && priority < (int)(sizeof(s_priority) / + sizeof(s_priority[0]))); + assert(msg != 0); + + fprintf(stderr, "%s: %s\n", s_priority[priority], msg); +} + +char* CLog::vsprint(int pad, char* buffer, int len, + const char* fmt, va_list args) +{ + assert(len > 0); + + // try writing to input buffer + int n; + if (len >= pad) { + n = vsnprintf(buffer + pad, len - pad, fmt, args); + if (n != -1 && n <= len - pad) + return buffer; + } + + // start allocating buffers until we write the whole string + buffer = 0; + do { + delete[] buffer; + len *= 2; + buffer = new char[len + pad]; + n = vsnprintf(buffer + pad, len - pad, fmt, args); + } while (n == -1 || n > len - pad); + + return buffer; +} diff --git a/base/CLog.h b/base/CLog.h new file mode 100644 index 00000000..0d44c3e7 --- /dev/null +++ b/base/CLog.h @@ -0,0 +1,35 @@ +#ifndef CLOG_H +#define CLOG_H + +#include + +class CLog { + public: + static void print(const char*, ...); + static void printt(const char* file, int line, const char*, ...); + + private: + static void output(int priority, const char* msg); + static char* vsprint(int pad, char*, int len, const char*, va_list); + static int nprint(const char*, va_list); +}; + +#if defined(NOLOGGING) +#define log(_a1) +#define CLOG_TRACE +#elif defined(NDEBUG) +#define log(_a1) CLog::print _a1 +#define CLOG_TRACE +#else +#define log(_a1) CLog::printt _a1 +#define CLOG_TRACE __FILE__, __LINE__, +#endif + +#define CLOG_CRIT CLOG_TRACE "%z\060" +#define CLOG_ERR CLOG_TRACE "%z\061" +#define CLOG_WARN CLOG_TRACE "%z\062" +#define CLOG_NOTE CLOG_TRACE "%z\063" +#define CLOG_INFO CLOG_TRACE "%z\064" +#define CLOG_DEBUG CLOG_TRACE "%z\065" + +#endif From c6ed114410ecb9943feb226960c4c60fca780680 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 14 Oct 2001 14:56:06 +0000 Subject: [PATCH 012/807] stupid bug fixes. writef() used the wrong variable as the number of bytes to write. readf() forgot to prepare the va_list. --- synergy/CProtocolUtil.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index 7ebed7b2..d35b2867 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -1,6 +1,7 @@ #include "CProtocolUtil.h" #include "IInputStream.h" #include "IOutputStream.h" +#include "CLog.h" #include #include #include @@ -14,6 +15,7 @@ void CProtocolUtil::writef(IOutputStream* stream, { assert(stream != NULL); assert(fmt != NULL); + log((CLOG_DEBUG "writef(%s)", fmt)); va_list args; @@ -36,7 +38,8 @@ void CProtocolUtil::writef(IOutputStream* stream, // write buffer UInt8* scan = buffer; while (count > 0) { - const UInt32 n = stream->write(scan, n); + const UInt32 n = stream->write(scan, count); + log((CLOG_DEBUG "wrote %d of %d bytes", n, count)); count -= n; scan += n; } @@ -51,6 +54,7 @@ void CProtocolUtil::readf(IInputStream* stream, assert(fmt != NULL); va_list args; + va_start(args, fmt); // begin scanning while (*fmt) { @@ -162,6 +166,8 @@ void CProtocolUtil::readf(IInputStream* stream, ++fmt; } } + + va_end(args); } UInt32 CProtocolUtil::getLength( From 6aba3a6f57b9d7a2e05aea1489d64ce0ab0b78fa Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 14 Oct 2001 16:58:01 +0000 Subject: [PATCH 013/807] removed exception specifications. thread exceptions weren't being listed and they'd have to be added to every one. just doesn't seem worth the trouble. --- io/CBufferedInputStream.cpp | 14 ++++++------ io/CBufferedInputStream.h | 14 ++++++------ io/CBufferedOutputStream.cpp | 14 ++++++------ io/CBufferedOutputStream.h | 14 ++++++------ io/CInputStreamFilter.cpp | 2 +- io/CInputStreamFilter.h | 8 +++---- io/COutputStreamFilter.cpp | 2 +- io/COutputStreamFilter.h | 8 +++---- io/CStreamBuffer.cpp | 8 +++---- io/CStreamBuffer.h | 8 +++---- io/IInputStream.h | 6 ++--- io/IOutputStream.h | 6 ++--- mt/CCondVar.cpp | 16 ++++++------- mt/CCondVar.h | 14 ++++++------ mt/CLock.cpp | 6 ++--- mt/CLock.h | 6 ++--- mt/CMutex.cpp | 8 +++---- mt/CMutex.h | 4 ++-- net/CNetworkAddress.cpp | 7 +++--- net/CNetworkAddress.h | 8 +++---- net/CSocketInputStream.cpp | 10 ++++----- net/CSocketInputStream.h | 10 ++++----- net/CSocketOutputStream.cpp | 14 ++++++------ net/CSocketOutputStream.h | 14 ++++++------ net/CSocketStreamBuffer.cpp | 8 +++---- net/CSocketStreamBuffer.h | 8 +++---- net/CTCPListenSocket.cpp | 6 ++--- net/CTCPListenSocket.h | 6 ++--- net/CTCPSocket.cpp | 20 ++++++++--------- net/CTCPSocket.h | 22 +++++++++--------- net/IListenSocket.h | 6 ++--- net/ISocket.h | 10 ++++----- synergy/CInputPacketStream.cpp | 10 ++++----- synergy/CInputPacketStream.h | 10 ++++----- synergy/COutputPacketStream.cpp | 6 ++--- synergy/COutputPacketStream.h | 6 ++--- synergy/CProtocolUtil.cpp | 12 +++++----- synergy/CProtocolUtil.h | 12 +++++----- synergy/CScreenMap.cpp | 2 +- synergy/CScreenMap.h | 2 +- synergy/CServer.cpp | 10 ++++----- synergy/CServer.h | 8 +++---- synergy/CServerProtocol.cpp | 8 +++---- synergy/CServerProtocol.h | 40 ++++++++++++++++----------------- synergy/CServerProtocol1_0.cpp | 32 +++++++++++++------------- synergy/CServerProtocol1_0.h | 32 +++++++++++++------------- synergy/CTCPSocketFactory.cpp | 4 ++-- synergy/CTCPSocketFactory.h | 4 ++-- synergy/IServerProtocol.h | 32 +++++++++++++------------- synergy/ISocketFactory.h | 4 ++-- 50 files changed, 269 insertions(+), 272 deletions(-) diff --git a/io/CBufferedInputStream.cpp b/io/CBufferedInputStream.cpp index d0978dfb..98c8fbe9 100644 --- a/io/CBufferedInputStream.cpp +++ b/io/CBufferedInputStream.cpp @@ -27,7 +27,7 @@ CBufferedInputStream::~CBufferedInputStream() } void CBufferedInputStream::write( - const void* data, UInt32 n) throw() + const void* data, UInt32 n) { if (!m_hungup && n > 0) { m_buffer.write(data, n); @@ -36,14 +36,14 @@ void CBufferedInputStream::write( } } -void CBufferedInputStream::hangup() throw() +void CBufferedInputStream::hangup() { m_hungup = true; m_empty.broadcast(); } UInt32 CBufferedInputStream::readNoLock( - void* dst, UInt32 n) throw(XIO) + void* dst, UInt32 n) { if (m_closed) { throw XIOClosed(); @@ -74,12 +74,12 @@ UInt32 CBufferedInputStream::readNoLock( return n; } -UInt32 CBufferedInputStream::getSizeNoLock() const throw() +UInt32 CBufferedInputStream::getSizeNoLock() const { return m_buffer.getSize(); } -void CBufferedInputStream::close() throw(XIO) +void CBufferedInputStream::close() { CLock lock(m_mutex); if (m_closed) { @@ -93,13 +93,13 @@ void CBufferedInputStream::close() throw(XIO) } UInt32 CBufferedInputStream::read( - void* dst, UInt32 n) throw(XIO) + void* dst, UInt32 n) { CLock lock(m_mutex); return readNoLock(dst, n); } -UInt32 CBufferedInputStream::getSize() const throw() +UInt32 CBufferedInputStream::getSize() const { CLock lock(m_mutex); return getSizeNoLock(); diff --git a/io/CBufferedInputStream.h b/io/CBufferedInputStream.h index 8a4dd7be..28fbdd27 100644 --- a/io/CBufferedInputStream.h +++ b/io/CBufferedInputStream.h @@ -19,25 +19,25 @@ class CBufferedInputStream : public IInputStream { // manipulators // write() appends n bytes to the buffer - void write(const void*, UInt32 n) throw(); + void write(const void*, UInt32 n); // causes read() to always return immediately. if there is no // more data then it returns 0. further writes are discarded. - void hangup() throw(); + void hangup(); // same as read() but caller must lock the mutex - UInt32 readNoLock(void*, UInt32 count) throw(XIO); + UInt32 readNoLock(void*, UInt32 count); // accessors // same as getSize() but caller must lock the mutex - UInt32 getSizeNoLock() const throw(); + UInt32 getSizeNoLock() const; // IInputStream overrides // these all lock the mutex for their duration - virtual void close() throw(XIO); - virtual UInt32 read(void*, UInt32 count) throw(XIO); - virtual UInt32 getSize() const throw(); + virtual void close(); + virtual UInt32 read(void*, UInt32 count); + virtual UInt32 getSize() const; private: CMutex* m_mutex; diff --git a/io/CBufferedOutputStream.cpp b/io/CBufferedOutputStream.cpp index bf3d9165..bf1603c6 100644 --- a/io/CBufferedOutputStream.cpp +++ b/io/CBufferedOutputStream.cpp @@ -23,22 +23,22 @@ CBufferedOutputStream::~CBufferedOutputStream() delete m_closeCB; } -const void* CBufferedOutputStream::peek(UInt32 n) throw() +const void* CBufferedOutputStream::peek(UInt32 n) { return m_buffer.peek(n); } -void CBufferedOutputStream::pop(UInt32 n) throw() +void CBufferedOutputStream::pop(UInt32 n) { m_buffer.pop(n); } -UInt32 CBufferedOutputStream::getSize() const throw() +UInt32 CBufferedOutputStream::getSize() const { return m_buffer.getSize(); } -void CBufferedOutputStream::close() throw(XIO) +void CBufferedOutputStream::close() { CLock lock(m_mutex); if (m_closed) { @@ -52,7 +52,7 @@ void CBufferedOutputStream::close() throw(XIO) } UInt32 CBufferedOutputStream::write( - const void* data, UInt32 n) throw(XIO) + const void* data, UInt32 n) { CLock lock(m_mutex); if (m_closed) { @@ -63,7 +63,7 @@ UInt32 CBufferedOutputStream::write( return n; } -void CBufferedOutputStream::flush() throw(XIO) +void CBufferedOutputStream::flush() { // wait until all data is written while (getSizeWithLock() > 0) { @@ -71,7 +71,7 @@ void CBufferedOutputStream::flush() throw(XIO) } } -UInt32 CBufferedOutputStream::getSizeWithLock() const throw() +UInt32 CBufferedOutputStream::getSizeWithLock() const { CLock lock(m_mutex); return m_buffer.getSize(); diff --git a/io/CBufferedOutputStream.h b/io/CBufferedOutputStream.h index ab43aecb..a88e2357 100644 --- a/io/CBufferedOutputStream.h +++ b/io/CBufferedOutputStream.h @@ -19,22 +19,22 @@ class CBufferedOutputStream : public IOutputStream { // peek() returns a buffer of n bytes (which must be <= getSize()). // pop() discards the next n bytes. - const void* peek(UInt32 n) throw(); - void pop(UInt32 n) throw(); + const void* peek(UInt32 n); + void pop(UInt32 n); // accessors // return the number of bytes in the buffer - UInt32 getSize() const throw(); + UInt32 getSize() const; // IOutputStream overrides // these all lock the mutex for their duration - virtual void close() throw(XIO); - virtual UInt32 write(const void*, UInt32 count) throw(XIO); - virtual void flush() throw(XIO); + virtual void close(); + virtual UInt32 write(const void*, UInt32 count); + virtual void flush(); private: - UInt32 getSizeWithLock() const throw(); + UInt32 getSizeWithLock() const; private: CMutex* m_mutex; diff --git a/io/CInputStreamFilter.cpp b/io/CInputStreamFilter.cpp index 486a6298..254c2313 100644 --- a/io/CInputStreamFilter.cpp +++ b/io/CInputStreamFilter.cpp @@ -19,7 +19,7 @@ CInputStreamFilter::~CInputStreamFilter() } } -IInputStream* CInputStreamFilter::getStream() const throw() +IInputStream* CInputStreamFilter::getStream() const { return m_stream; } diff --git a/io/CInputStreamFilter.h b/io/CInputStreamFilter.h index 51f40b35..0e4dd41b 100644 --- a/io/CInputStreamFilter.h +++ b/io/CInputStreamFilter.h @@ -13,12 +13,12 @@ class CInputStreamFilter : public IInputStream { // accessors // IInputStream overrides - virtual void close() throw(XIO) = 0; - virtual UInt32 read(void*, UInt32 maxCount) throw(XIO) = 0; - virtual UInt32 getSize() const throw() = 0; + virtual void close() = 0; + virtual UInt32 read(void*, UInt32 maxCount) = 0; + virtual UInt32 getSize() const = 0; protected: - IInputStream* getStream() const throw(); + IInputStream* getStream() const; private: IInputStream* m_stream; diff --git a/io/COutputStreamFilter.cpp b/io/COutputStreamFilter.cpp index 3f62075c..8adc2862 100644 --- a/io/COutputStreamFilter.cpp +++ b/io/COutputStreamFilter.cpp @@ -19,7 +19,7 @@ COutputStreamFilter::~COutputStreamFilter() } } -IOutputStream* COutputStreamFilter::getStream() const throw() +IOutputStream* COutputStreamFilter::getStream() const { return m_stream; } diff --git a/io/COutputStreamFilter.h b/io/COutputStreamFilter.h index 11499d80..c6b02a46 100644 --- a/io/COutputStreamFilter.h +++ b/io/COutputStreamFilter.h @@ -13,12 +13,12 @@ class COutputStreamFilter : public IOutputStream { // accessors // IOutputStream overrides - virtual void close() throw(XIO) = 0; - virtual UInt32 write(const void*, UInt32 count) throw(XIO) = 0; - virtual void flush() throw(XIO) = 0; + virtual void close() = 0; + virtual UInt32 write(const void*, UInt32 count) = 0; + virtual void flush() = 0; protected: - IOutputStream* getStream() const throw(); + IOutputStream* getStream() const; private: IOutputStream* m_stream; diff --git a/io/CStreamBuffer.cpp b/io/CStreamBuffer.cpp index d2ce3140..e9d1b5a9 100644 --- a/io/CStreamBuffer.cpp +++ b/io/CStreamBuffer.cpp @@ -17,7 +17,7 @@ CStreamBuffer::~CStreamBuffer() // do nothing } -const void* CStreamBuffer::peek(UInt32 n) throw() +const void* CStreamBuffer::peek(UInt32 n) { assert(n <= m_size); @@ -36,7 +36,7 @@ const void* CStreamBuffer::peek(UInt32 n) throw() return reinterpret_cast(head->begin()); } -void CStreamBuffer::pop(UInt32 n) throw() +void CStreamBuffer::pop(UInt32 n) { m_size -= n; @@ -58,7 +58,7 @@ void CStreamBuffer::pop(UInt32 n) throw() } void CStreamBuffer::write( - const void* vdata, UInt32 n) throw() + const void* vdata, UInt32 n) { assert(vdata != NULL); @@ -100,7 +100,7 @@ void CStreamBuffer::write( } } -UInt32 CStreamBuffer::getSize() const throw() +UInt32 CStreamBuffer::getSize() const { return m_size; } diff --git a/io/CStreamBuffer.h b/io/CStreamBuffer.h index cb5bb864..7333de90 100644 --- a/io/CStreamBuffer.h +++ b/io/CStreamBuffer.h @@ -14,16 +14,16 @@ class CStreamBuffer { // peek() returns a buffer of n bytes (which must be <= getSize()). // pop() discards the next n bytes. - const void* peek(UInt32 n) throw(); - void pop(UInt32 n) throw(); + const void* peek(UInt32 n); + void pop(UInt32 n); // write() appends n bytes to the buffer - void write(const void*, UInt32 n) throw(); + void write(const void*, UInt32 n); // accessors // return the number of bytes in the buffer - UInt32 getSize() const throw(); + UInt32 getSize() const; private: static const UInt32 kChunkSize; diff --git a/io/IInputStream.h b/io/IInputStream.h index e4f93394..3650dfd4 100644 --- a/io/IInputStream.h +++ b/io/IInputStream.h @@ -10,12 +10,12 @@ class IInputStream : public IInterface { // manipulators // close the stream - virtual void close() throw(XIO) = 0; + virtual void close() = 0; // read up to maxCount bytes into buffer, return number read. // blocks if no data is currently available. if buffer is NULL // then the data is discarded. - virtual UInt32 read(void* buffer, UInt32 maxCount) throw(XIO) = 0; + virtual UInt32 read(void* buffer, UInt32 maxCount) = 0; // accessors @@ -23,7 +23,7 @@ class IInputStream : public IInterface { // (i.e. a number not greater than the actual number of bytes). // some streams may not be able to determine this and will always // return zero. - virtual UInt32 getSize() const throw() = 0; + virtual UInt32 getSize() const = 0; }; #endif diff --git a/io/IOutputStream.h b/io/IOutputStream.h index f0b46244..ca1a196c 100644 --- a/io/IOutputStream.h +++ b/io/IOutputStream.h @@ -10,13 +10,13 @@ class IOutputStream : public IInterface { // manipulators // close the stream - virtual void close() throw(XIO) = 0; + virtual void close() = 0; // write count bytes to stream - virtual UInt32 write(const void*, UInt32 count) throw(XIO) = 0; + virtual UInt32 write(const void*, UInt32 count) = 0; // flush the stream - virtual void flush() throw(XIO) = 0; + virtual void flush() = 0; // accessors }; diff --git a/mt/CCondVar.cpp b/mt/CCondVar.cpp index e2d441ae..ca0d8526 100644 --- a/mt/CCondVar.cpp +++ b/mt/CCondVar.cpp @@ -21,12 +21,12 @@ CCondVarBase::~CCondVarBase() fini(); } -void CCondVarBase::lock() const throw() +void CCondVarBase::lock() const { m_mutex->lock(); } -void CCondVarBase::unlock() const throw() +void CCondVarBase::unlock() const { m_mutex->unlock(); } @@ -37,7 +37,7 @@ bool CCondVarBase::wait(double timeout) const return wait(timer, timeout); } -CMutex* CCondVarBase::getMutex() const throw() +CMutex* CCondVarBase::getMutex() const { return m_mutex; } @@ -65,14 +65,14 @@ void CCondVarBase::fini() delete cond; } -void CCondVarBase::signal() throw() +void CCondVarBase::signal() { pthread_cond_t* cond = reinterpret_cast(m_cond); int status = pthread_cond_signal(cond); assert(status == 0); } -void CCondVarBase::broadcast() throw() +void CCondVarBase::broadcast() { pthread_cond_t* cond = reinterpret_cast(m_cond); int status = pthread_cond_broadcast(cond); @@ -143,7 +143,7 @@ bool CCondVarBase::wait( CThread::testCancel(); // check wait status - if (status != ETIMEDOUT) + if (status != ETIMEDOUT && status != EINTR) break; } @@ -199,7 +199,7 @@ void CCondVarBase::fini() delete[] events; } -void CCondVarBase::signal() throw() +void CCondVarBase::signal() { // is anybody waiting? bool hasWaiter; @@ -213,7 +213,7 @@ void CCondVarBase::signal() throw() SetEvent(reinterpret_cast(m_cond)[kSignal]); } -void CCondVarBase::broadcast() throw() +void CCondVarBase::broadcast() { // is anybody waiting? bool hasWaiter; diff --git a/mt/CCondVar.h b/mt/CCondVar.h index a42e25ab..083962ab 100644 --- a/mt/CCondVar.h +++ b/mt/CCondVar.h @@ -17,13 +17,13 @@ class CCondVarBase { // manipulators // lock/unlock the mutex. see CMutex. - void lock() const throw(); - void unlock() const throw(); + void lock() const; + void unlock() const; // signal the condition. Signal() wakes one waiting thread. // Broadcast() wakes all waiting threads. - void signal() throw(); - void broadcast() throw(); + void signal(); + void broadcast(); // accessors @@ -43,7 +43,7 @@ class CCondVarBase { bool wait(CStopwatch&, double timeout) const; // get the mutex passed to the c'tor - CMutex* getMutex() const throw(); + CMutex* getMutex() const; private: void init(); @@ -83,7 +83,7 @@ class CCondVar : public CCondVarBase { // get the const value. this object should be locked before // calling this method. - operator const T&() const throw(); + operator const T&() const; private: T m_data; @@ -131,7 +131,7 @@ CCondVar& CCondVar::operator=(const T& data) template inline -CCondVar::operator const T&() const throw() +CCondVar::operator const T&() const { return m_data; } diff --git a/mt/CLock.cpp b/mt/CLock.cpp index 86aa721e..41ceae9c 100644 --- a/mt/CLock.cpp +++ b/mt/CLock.cpp @@ -6,17 +6,17 @@ // CLock // -CLock::CLock(const CMutex* mutex) throw() : m_mutex(mutex) +CLock::CLock(const CMutex* mutex) : m_mutex(mutex) { m_mutex->lock(); } -CLock::CLock(const CCondVarBase* cv) throw() : m_mutex(cv->getMutex()) +CLock::CLock(const CCondVarBase* cv) : m_mutex(cv->getMutex()) { m_mutex->lock(); } -CLock::~CLock() throw() +CLock::~CLock() { m_mutex->unlock(); } diff --git a/mt/CLock.h b/mt/CLock.h index 9bd81942..9ab933ce 100644 --- a/mt/CLock.h +++ b/mt/CLock.h @@ -8,9 +8,9 @@ class CCondVarBase; class CLock { public: - CLock(const CMutex* mutex) throw(); - CLock(const CCondVarBase* cv) throw(); - ~CLock() throw(); + CLock(const CMutex* mutex); + CLock(const CCondVarBase* cv); + ~CLock(); private: // not implemented diff --git a/mt/CMutex.cpp b/mt/CMutex.cpp index 854d52e5..3b203d50 100644 --- a/mt/CMutex.cpp +++ b/mt/CMutex.cpp @@ -48,7 +48,7 @@ void CMutex::fini() delete mutex; } -void CMutex::lock() const throw() +void CMutex::lock() const { pthread_mutex_t* mutex = reinterpret_cast(m_mutex); int status = pthread_mutex_lock(mutex); @@ -71,7 +71,7 @@ void CMutex::lock() const throw() } } -void CMutex::unlock() const throw() +void CMutex::unlock() const { pthread_mutex_t* mutex = reinterpret_cast(m_mutex); int status = pthread_mutex_unlock(mutex); @@ -110,12 +110,12 @@ void CMutex::fini() delete mutex; } -void CMutex::lock() const throw() +void CMutex::lock() const { ::EnterCriticalSection(reinterpret_cast(m_mutex)); } -void CMutex::unlock() const throw() +void CMutex::unlock() const { ::LeaveCriticalSection(reinterpret_cast(m_mutex)); } diff --git a/mt/CMutex.h b/mt/CMutex.h index f9423827..77612366 100644 --- a/mt/CMutex.h +++ b/mt/CMutex.h @@ -20,8 +20,8 @@ class CMutex { // accessors - void lock() const throw(); - void unlock() const throw(); + void lock() const; + void unlock() const; private: void init(); diff --git a/net/CNetworkAddress.cpp b/net/CNetworkAddress.cpp index 024965ce..6d780b2f 100644 --- a/net/CNetworkAddress.cpp +++ b/net/CNetworkAddress.cpp @@ -8,7 +8,7 @@ // CNetworkAddress // -CNetworkAddress::CNetworkAddress(UInt16 port) throw(XSocketAddress) +CNetworkAddress::CNetworkAddress(UInt16 port) { if (port == 0) throw XSocketAddress(XSocketAddress::kBadPort, CString(), port); @@ -21,7 +21,6 @@ CNetworkAddress::CNetworkAddress(UInt16 port) throw(XSocketAddress) } CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) - throw(XSocketAddress) { if (port == 0) throw XSocketAddress(XSocketAddress::kBadPort, hostname, port); @@ -54,12 +53,12 @@ CNetworkAddress::~CNetworkAddress() // do nothing } -const struct sockaddr* CNetworkAddress::getAddress() const throw() +const struct sockaddr* CNetworkAddress::getAddress() const { return &m_address; } -int CNetworkAddress::getAddressLength() const throw() +int CNetworkAddress::getAddressLength() const { return sizeof(m_address); } diff --git a/net/CNetworkAddress.h b/net/CNetworkAddress.h index f9394b32..95e377ce 100644 --- a/net/CNetworkAddress.h +++ b/net/CNetworkAddress.h @@ -9,16 +9,16 @@ class CString; class CNetworkAddress { public: - CNetworkAddress(UInt16 port) throw(XSocketAddress); - CNetworkAddress(const CString& hostname, UInt16 port) throw(XSocketAddress); + CNetworkAddress(UInt16 port); + CNetworkAddress(const CString& hostname, UInt16 port); ~CNetworkAddress(); // manipulators // accessors - const struct sockaddr* getAddress() const throw(); - int getAddressLength() const throw(); + const struct sockaddr* getAddress() const; + int getAddressLength() const; private: struct sockaddr m_address; diff --git a/net/CSocketInputStream.cpp b/net/CSocketInputStream.cpp index d6f9d7ae..e17e0229 100644 --- a/net/CSocketInputStream.cpp +++ b/net/CSocketInputStream.cpp @@ -27,7 +27,7 @@ CSocketInputStream::~CSocketInputStream() } void CSocketInputStream::write( - const void* data, UInt32 n) throw() + const void* data, UInt32 n) { if (!m_hungup && n > 0) { m_buffer.write(data, n); @@ -36,13 +36,13 @@ void CSocketInputStream::write( } } -void CSocketInputStream::hangup() throw() +void CSocketInputStream::hangup() { m_hungup = true; m_empty.broadcast(); } -void CSocketInputStream::close() throw(XIO) +void CSocketInputStream::close() { CLock lock(m_mutex); if (m_closed) { @@ -56,7 +56,7 @@ void CSocketInputStream::close() throw(XIO) } UInt32 CSocketInputStream::read( - void* dst, UInt32 n) throw(XIO) + void* dst, UInt32 n) { CLock lock(m_mutex); if (m_closed) { @@ -86,7 +86,7 @@ UInt32 CSocketInputStream::read( return n; } -UInt32 CSocketInputStream::getSize() const throw() +UInt32 CSocketInputStream::getSize() const { CLock lock(m_mutex); return m_buffer.getSize(); diff --git a/net/CSocketInputStream.h b/net/CSocketInputStream.h index 46cb1c5b..38096538 100644 --- a/net/CSocketInputStream.h +++ b/net/CSocketInputStream.h @@ -16,19 +16,19 @@ class CSocketInputStream : public IInputStream { // manipulators // write() appends n bytes to the buffer - void write(const void*, UInt32 n) throw(); + void write(const void*, UInt32 n); // causes read() to always return immediately. if there is no // more data then it returns 0. further writes are discarded. - void hangup() throw(); + void hangup(); // accessors // IInputStream overrides // these all lock the mutex for their duration - virtual void close() throw(XIO); - virtual UInt32 read(void*, UInt32 count) throw(XIO); - virtual UInt32 getSize() const throw(); + virtual void close(); + virtual UInt32 read(void*, UInt32 count); + virtual UInt32 getSize() const; private: CMutex* m_mutex; diff --git a/net/CSocketOutputStream.cpp b/net/CSocketOutputStream.cpp index a1d36c18..fe9998be 100644 --- a/net/CSocketOutputStream.cpp +++ b/net/CSocketOutputStream.cpp @@ -23,22 +23,22 @@ CSocketOutputStream::~CSocketOutputStream() delete m_closeCB; } -const void* CSocketOutputStream::peek(UInt32 n) throw() +const void* CSocketOutputStream::peek(UInt32 n) { return m_buffer.peek(n); } -void CSocketOutputStream::pop(UInt32 n) throw() +void CSocketOutputStream::pop(UInt32 n) { m_buffer.pop(n); } -UInt32 CSocketOutputStream::getSize() const throw() +UInt32 CSocketOutputStream::getSize() const { return m_buffer.getSize(); } -void CSocketOutputStream::close() throw(XIO) +void CSocketOutputStream::close() { CLock lock(m_mutex); if (m_closed) { @@ -52,7 +52,7 @@ void CSocketOutputStream::close() throw(XIO) } UInt32 CSocketOutputStream::write( - const void* data, UInt32 n) throw(XIO) + const void* data, UInt32 n) { CLock lock(m_mutex); if (m_closed) { @@ -63,7 +63,7 @@ UInt32 CSocketOutputStream::write( return n; } -void CSocketOutputStream::flush() throw(XIO) +void CSocketOutputStream::flush() { // wait until all data is written while (getSizeWithLock() > 0) { @@ -71,7 +71,7 @@ void CSocketOutputStream::flush() throw(XIO) } } -UInt32 CSocketOutputStream::getSizeWithLock() const throw() +UInt32 CSocketOutputStream::getSizeWithLock() const { CLock lock(m_mutex); return m_buffer.getSize(); diff --git a/net/CSocketOutputStream.h b/net/CSocketOutputStream.h index d55443c7..c959c140 100644 --- a/net/CSocketOutputStream.h +++ b/net/CSocketOutputStream.h @@ -16,22 +16,22 @@ class CSocketOutputStream : public IOutputStream { // peek() returns a buffer of n bytes (which must be <= getSize()). // pop() discards the next n bytes. - const void* peek(UInt32 n) throw(); - void pop(UInt32 n) throw(); + const void* peek(UInt32 n); + void pop(UInt32 n); // accessors // return the number of bytes in the buffer - UInt32 getSize() const throw(); + UInt32 getSize() const; // IOutputStream overrides // these all lock the mutex for their duration - virtual void close() throw(XIO); - virtual UInt32 write(const void*, UInt32 count) throw(XIO); - virtual void flush() throw(XIO); + virtual void close(); + virtual UInt32 write(const void*, UInt32 count); + virtual void flush(); private: - UInt32 getSizeWithLock() const throw(); + UInt32 getSizeWithLock() const; private: CMutex* m_mutex; diff --git a/net/CSocketStreamBuffer.cpp b/net/CSocketStreamBuffer.cpp index 0a2848ce..5ee20c28 100644 --- a/net/CSocketStreamBuffer.cpp +++ b/net/CSocketStreamBuffer.cpp @@ -17,7 +17,7 @@ CSocketStreamBuffer::~CSocketStreamBuffer() // do nothing } -const void* CSocketStreamBuffer::peek(UInt32 n) throw() +const void* CSocketStreamBuffer::peek(UInt32 n) { assert(n <= m_size); @@ -36,7 +36,7 @@ const void* CSocketStreamBuffer::peek(UInt32 n) throw() return reinterpret_cast(head->begin()); } -void CSocketStreamBuffer::pop(UInt32 n) throw() +void CSocketStreamBuffer::pop(UInt32 n) { m_size -= n; @@ -58,7 +58,7 @@ void CSocketStreamBuffer::pop(UInt32 n) throw() } void CSocketStreamBuffer::write( - const void* vdata, UInt32 n) throw() + const void* vdata, UInt32 n) { assert(vdata != NULL); @@ -100,7 +100,7 @@ void CSocketStreamBuffer::write( } } -UInt32 CSocketStreamBuffer::getSize() const throw() +UInt32 CSocketStreamBuffer::getSize() const { return m_size; } diff --git a/net/CSocketStreamBuffer.h b/net/CSocketStreamBuffer.h index ea6234b4..545fd25f 100644 --- a/net/CSocketStreamBuffer.h +++ b/net/CSocketStreamBuffer.h @@ -14,16 +14,16 @@ class CSocketStreamBuffer { // peek() returns a buffer of n bytes (which must be <= getSize()). // pop() discards the next n bytes. - const void* peek(UInt32 n) throw(); - void pop(UInt32 n) throw(); + const void* peek(UInt32 n); + void pop(UInt32 n); // write() appends n bytes to the buffer - void write(const void*, UInt32 n) throw(); + void write(const void*, UInt32 n); // accessors // return the number of bytes in the buffer - UInt32 getSize() const throw(); + UInt32 getSize() const; private: static const UInt32 kChunkSize; diff --git a/net/CTCPListenSocket.cpp b/net/CTCPListenSocket.cpp index 959e8c52..d7fdaa93 100644 --- a/net/CTCPListenSocket.cpp +++ b/net/CTCPListenSocket.cpp @@ -31,7 +31,7 @@ CTCPListenSocket::~CTCPListenSocket() } void CTCPListenSocket::bind( - const CNetworkAddress& addr) throw(XSocket) + const CNetworkAddress& addr) { if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { if (errno == EADDRINUSE) { @@ -44,7 +44,7 @@ void CTCPListenSocket::bind( } } -ISocket* CTCPListenSocket::accept() throw(XSocket) +ISocket* CTCPListenSocket::accept() { for (;;) { struct sockaddr addr; @@ -60,7 +60,7 @@ ISocket* CTCPListenSocket::accept() throw(XSocket) } } -void CTCPListenSocket::close() throw(XIO) +void CTCPListenSocket::close() { if (m_fd == -1) { throw XIOClosed(); diff --git a/net/CTCPListenSocket.h b/net/CTCPListenSocket.h index 8807fba8..30310b52 100644 --- a/net/CTCPListenSocket.h +++ b/net/CTCPListenSocket.h @@ -13,9 +13,9 @@ class CTCPListenSocket : public IListenSocket { // accessors // IListenSocket overrides - virtual void bind(const CNetworkAddress&) throw(XSocket); - virtual ISocket* accept() throw(XSocket); - virtual void close() throw(XIO); + virtual void bind(const CNetworkAddress&); + virtual ISocket* accept(); + virtual void close(); private: int m_fd; diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index 86a1b0bd..d38ee738 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -20,7 +20,7 @@ // CTCPSocket // -CTCPSocket::CTCPSocket() throw(XSocket) +CTCPSocket::CTCPSocket() { m_fd = socket(PF_INET, SOCK_STREAM, 0); if (m_fd == -1) { @@ -29,7 +29,7 @@ CTCPSocket::CTCPSocket() throw(XSocket) init(); } -CTCPSocket::CTCPSocket(int fd) throw() : +CTCPSocket::CTCPSocket(int fd) : m_fd(fd) { assert(m_fd != -1); @@ -60,7 +60,6 @@ CTCPSocket::~CTCPSocket() } void CTCPSocket::bind(const CNetworkAddress& addr) - throw(XSocket) { if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { if (errno == EADDRINUSE) { @@ -71,7 +70,6 @@ void CTCPSocket::bind(const CNetworkAddress& addr) } void CTCPSocket::connect(const CNetworkAddress& addr) - throw(XSocket) { CThread::testCancel(); if (::connect(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { @@ -85,7 +83,7 @@ void CTCPSocket::connect(const CNetworkAddress& addr) this, &CTCPSocket::service)); } -void CTCPSocket::close() throw(XIO) +void CTCPSocket::close() { // shutdown I/O thread before close if (m_thread != NULL) { @@ -114,17 +112,17 @@ void CTCPSocket::close() throw(XIO) } } -IInputStream* CTCPSocket::getInputStream() throw() +IInputStream* CTCPSocket::getInputStream() { return m_input; } -IOutputStream* CTCPSocket::getOutputStream() throw() +IOutputStream* CTCPSocket::getOutputStream() { return m_output; } -void CTCPSocket::init() throw(XIO) +void CTCPSocket::init() { m_mutex = new CMutex; m_thread = NULL; @@ -137,7 +135,7 @@ void CTCPSocket::init() throw(XIO) this, &CTCPSocket::closeOutput)); } -void CTCPSocket::service(void*) throw(XThread) +void CTCPSocket::service(void*) { assert(m_fd != -1); @@ -211,14 +209,14 @@ void CTCPSocket::service(void*) throw(XThread) } } -void CTCPSocket::closeInput(void*) throw() +void CTCPSocket::closeInput(void*) { // note -- m_mutex should already be locked shutdown(m_fd, 0); m_connected &= ~kRead; } -void CTCPSocket::closeOutput(void*) throw() +void CTCPSocket::closeOutput(void*) { // note -- m_mutex should already be locked shutdown(m_fd, 1); diff --git a/net/CTCPSocket.h b/net/CTCPSocket.h index 8218bbad..208774ff 100644 --- a/net/CTCPSocket.h +++ b/net/CTCPSocket.h @@ -13,8 +13,8 @@ class CBufferedOutputStream; class CTCPSocket : public ISocket { public: - CTCPSocket() throw(XSocket); - CTCPSocket(int fd) throw(); + CTCPSocket(); + CTCPSocket(int fd); ~CTCPSocket(); // manipulators @@ -22,17 +22,17 @@ class CTCPSocket : public ISocket { // accessors // ISocket overrides - virtual void bind(const CNetworkAddress&) throw(XSocket); - virtual void connect(const CNetworkAddress&) throw(XSocket); - virtual void close() throw(XIO); - virtual IInputStream* getInputStream() throw(); - virtual IOutputStream* getOutputStream() throw(); + virtual void bind(const CNetworkAddress&); + virtual void connect(const CNetworkAddress&); + virtual void close(); + virtual IInputStream* getInputStream(); + virtual IOutputStream* getOutputStream(); private: - void init() throw(XIO); - void service(void*) throw(XThread); - void closeInput(void*) throw(); - void closeOutput(void*) throw(); + void init(); + void service(void*); + void closeInput(void*); + void closeOutput(void*); private: enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 }; diff --git a/net/IListenSocket.h b/net/IListenSocket.h index eb8d7903..f411412e 100644 --- a/net/IListenSocket.h +++ b/net/IListenSocket.h @@ -13,13 +13,13 @@ class IListenSocket : public IInterface { // manipulators // bind the socket to a particular address - virtual void bind(const CNetworkAddress&) throw(XSocket) = 0; + virtual void bind(const CNetworkAddress&) = 0; // wait for a connection - virtual ISocket* accept() throw(XSocket) = 0; + virtual ISocket* accept() = 0; // close the socket - virtual void close() throw(XIO) = 0; + virtual void close() = 0; // accessors }; diff --git a/net/ISocket.h b/net/ISocket.h index 98d1fd45..2c86e561 100644 --- a/net/ISocket.h +++ b/net/ISocket.h @@ -15,19 +15,19 @@ class ISocket : public IInterface { // manipulators // bind the socket to a particular address - virtual void bind(const CNetworkAddress&) throw(XSocket) = 0; + virtual void bind(const CNetworkAddress&) = 0; // connect the socket - virtual void connect(const CNetworkAddress&) throw(XSocket) = 0; + virtual void connect(const CNetworkAddress&) = 0; // close the socket. this will flush the output stream if it // hasn't been closed yet. - virtual void close() throw(XIO) = 0; + virtual void close() = 0; // get the input and output streams for the socket. closing // these streams closes the appropriate half of the socket. - virtual IInputStream* getInputStream() throw() = 0; - virtual IOutputStream* getOutputStream() throw() = 0; + virtual IInputStream* getInputStream() = 0; + virtual IOutputStream* getOutputStream() = 0; // accessors }; diff --git a/synergy/CInputPacketStream.cpp b/synergy/CInputPacketStream.cpp index c7642e45..607ac4f5 100644 --- a/synergy/CInputPacketStream.cpp +++ b/synergy/CInputPacketStream.cpp @@ -20,13 +20,13 @@ CInputPacketStream::~CInputPacketStream() // do nothing } -void CInputPacketStream::close() throw(XIO) +void CInputPacketStream::close() { getStream()->close(); } UInt32 CInputPacketStream::read( - void* buffer, UInt32 n) throw(XIO) + void* buffer, UInt32 n) { CLock lock(&m_mutex); @@ -50,13 +50,13 @@ UInt32 CInputPacketStream::read( return n; } -UInt32 CInputPacketStream::getSize() const throw() +UInt32 CInputPacketStream::getSize() const { CLock lock(&m_mutex); return getSizeNoLock(); } -UInt32 CInputPacketStream::getSizeNoLock() const throw() +UInt32 CInputPacketStream::getSizeNoLock() const { while (!hasFullMessage()) { // read more data @@ -76,7 +76,7 @@ UInt32 CInputPacketStream::getSizeNoLock() const throw() return m_size; } -bool CInputPacketStream::hasFullMessage() const throw() +bool CInputPacketStream::hasFullMessage() const { // get payload length if we don't have it yet if (m_size == 0) { diff --git a/synergy/CInputPacketStream.h b/synergy/CInputPacketStream.h index 5bd31772..50be260c 100644 --- a/synergy/CInputPacketStream.h +++ b/synergy/CInputPacketStream.h @@ -15,13 +15,13 @@ class CInputPacketStream : public CInputStreamFilter { // accessors // IInputStream overrides - virtual void close() throw(XIO); - virtual UInt32 read(void*, UInt32 maxCount) throw(XIO); - virtual UInt32 getSize() const throw(); + virtual void close(); + virtual UInt32 read(void*, UInt32 maxCount); + virtual UInt32 getSize() const; private: - UInt32 getSizeNoLock() const throw(); - bool hasFullMessage() const throw(); + UInt32 getSizeNoLock() const; + bool hasFullMessage() const; private: CMutex m_mutex; diff --git a/synergy/COutputPacketStream.cpp b/synergy/COutputPacketStream.cpp index cffd050a..1895b782 100644 --- a/synergy/COutputPacketStream.cpp +++ b/synergy/COutputPacketStream.cpp @@ -15,13 +15,13 @@ COutputPacketStream::~COutputPacketStream() // do nothing } -void COutputPacketStream::close() throw(XIO) +void COutputPacketStream::close() { getStream()->close(); } UInt32 COutputPacketStream::write( - const void* buffer, UInt32 count) throw(XIO) + const void* buffer, UInt32 count) { // write the length of the payload UInt8 length[4]; @@ -49,7 +49,7 @@ UInt32 COutputPacketStream::write( return count; } -void COutputPacketStream::flush() throw(XIO) +void COutputPacketStream::flush() { getStream()->flush(); } diff --git a/synergy/COutputPacketStream.h b/synergy/COutputPacketStream.h index 20293386..a41d63e7 100644 --- a/synergy/COutputPacketStream.h +++ b/synergy/COutputPacketStream.h @@ -13,9 +13,9 @@ class COutputPacketStream : public COutputStreamFilter { // accessors // IOutputStream overrides - virtual void close() throw(XIO); - virtual UInt32 write(const void*, UInt32 count) throw(XIO); - virtual void flush() throw(XIO); + virtual void close(); + virtual UInt32 write(const void*, UInt32 count); + virtual void flush(); }; #endif diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index d35b2867..ec0cc2cc 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -11,7 +11,7 @@ // void CProtocolUtil::writef(IOutputStream* stream, - const char* fmt, ...) throw(XIO) + const char* fmt, ...) { assert(stream != NULL); assert(fmt != NULL); @@ -48,7 +48,7 @@ void CProtocolUtil::writef(IOutputStream* stream, } void CProtocolUtil::readf(IInputStream* stream, - const char* fmt, ...) throw(XIO) + const char* fmt, ...) { assert(stream != NULL); assert(fmt != NULL); @@ -171,7 +171,7 @@ void CProtocolUtil::readf(IInputStream* stream, } UInt32 CProtocolUtil::getLength( - const char* fmt, va_list args) throw() + const char* fmt, va_list args) { UInt32 n = 0; while (*fmt) { @@ -214,7 +214,7 @@ UInt32 CProtocolUtil::getLength( } void CProtocolUtil::writef(void* buffer, - const char* fmt, va_list args) throw(XIO) + const char* fmt, va_list args) { UInt8* dst = reinterpret_cast(buffer); @@ -285,7 +285,7 @@ void CProtocolUtil::writef(void* buffer, } } -UInt32 CProtocolUtil::eatLength(const char** pfmt) throw() +UInt32 CProtocolUtil::eatLength(const char** pfmt) { const char* fmt = *pfmt; UInt32 n = 0; @@ -310,7 +310,7 @@ UInt32 CProtocolUtil::eatLength(const char** pfmt) throw() } void CProtocolUtil::read(IInputStream* stream, - void* vbuffer, UInt32 count) throw(XIO) + void* vbuffer, UInt32 count) { assert(stream != NULL); assert(vbuffer != NULL); diff --git a/synergy/CProtocolUtil.h b/synergy/CProtocolUtil.h index 346accda..70bd5851 100644 --- a/synergy/CProtocolUtil.h +++ b/synergy/CProtocolUtil.h @@ -22,7 +22,7 @@ class CProtocolUtil { // %4i -- converts integer argument to 4 byte integer in NBO // %s -- converts integer N and const UInt8* to stream of N bytes static void writef(IOutputStream*, - const char* fmt, ...) throw(XIO); + const char* fmt, ...); // read formatted binary data from a buffer. this performs the // reverse operation of writef(). @@ -34,13 +34,13 @@ class CProtocolUtil { // %4i -- reads an NBO 4 byte integer; arg is SInt32* or UInt32* // %s -- reads bytes; argument must be a CString*, *not* a char* static void readf(IInputStream*, - const char* fmt, ...) throw(XIO); + const char* fmt, ...); private: - static UInt32 getLength(const char* fmt, va_list) throw(); - static void writef(void*, const char* fmt, va_list) throw(XIO); - static UInt32 eatLength(const char** fmt) throw(); - static void read(IInputStream*, void*, UInt32) throw(XIO); + static UInt32 getLength(const char* fmt, va_list); + static void writef(void*, const char* fmt, va_list); + static UInt32 eatLength(const char** fmt); + static void read(IInputStream*, void*, UInt32); }; class XIOReadMismatch : public XIO { diff --git a/synergy/CScreenMap.cpp b/synergy/CScreenMap.cpp index eb2b4d7f..39d7d7f2 100644 --- a/synergy/CScreenMap.cpp +++ b/synergy/CScreenMap.cpp @@ -76,7 +76,7 @@ void CScreenMap::disconnect(const CString& srcName, } CString CScreenMap::getNeighbor(const CString& srcName, - EDirection srcSide) const throw() + EDirection srcSide) const { // find source cell CCellMap::const_iterator index = m_map.find(srcName); diff --git a/synergy/CScreenMap.h b/synergy/CScreenMap.h index 816b8f93..80c571f6 100644 --- a/synergy/CScreenMap.h +++ b/synergy/CScreenMap.h @@ -31,7 +31,7 @@ class CScreenMap { // get the neighbor in the given direction. returns the empty string // if there is no neighbor in that direction. - CString getNeighbor(const CString&, EDirection) const throw(); + CString getNeighbor(const CString&, EDirection) const; // get the name of a direction (for debugging) static const char* dirName(EDirection); diff --git a/synergy/CServer.cpp b/synergy/CServer.cpp index 5a579c30..31f6e65e 100644 --- a/synergy/CServer.cpp +++ b/synergy/CServer.cpp @@ -96,7 +96,7 @@ void CServer::getScreenMap(CScreenMap* screenMap) const } void CServer::setInfo(const CString& client, - SInt32 w, SInt32 h, SInt32 zoneSize) throw() + SInt32 w, SInt32 h, SInt32 zoneSize) { CLock lock(&m_mutex); @@ -732,7 +732,7 @@ void CServer::handshakeClient(void* vsocket) } } -void CServer::quit() throw() +void CServer::quit() { CLock lock(&m_mutex); m_done = true; @@ -761,7 +761,7 @@ void CServer::openPrimaryScreen() // FIXME -- need way for primary screen to call us back } -void CServer::closePrimaryScreen() throw() +void CServer::closePrimaryScreen() { assert(m_primary != NULL); @@ -802,7 +802,7 @@ void CServer::removeCleanupThread(const CThread& thread) } } -void CServer::cleanupThreads() throw() +void CServer::cleanupThreads() { log((CLOG_DEBUG "cleaning up threads")); m_mutex.lock(); @@ -839,7 +839,7 @@ void CServer::removeConnection(const CString& name) log((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); CLock lock(&m_mutex); CScreenList::iterator index = m_screens.find(name); - assert(index == m_screens.end()); + assert(index != m_screens.end()); delete index->second; m_screens.erase(index); } diff --git a/synergy/CServer.h b/synergy/CServer.h index e7fc814e..0274b2fe 100644 --- a/synergy/CServer.h +++ b/synergy/CServer.h @@ -41,7 +41,7 @@ class CServer { // handle messages from clients void setInfo(const CString& clientName, - SInt32 w, SInt32 h, SInt32 zoneSize) throw(); + SInt32 w, SInt32 h, SInt32 zoneSize); // accessors @@ -52,7 +52,7 @@ class CServer { protected: bool onCommandKey(KeyID, KeyModifierMask, bool down); - void quit() throw(); + void quit(); private: class CCleanupNote { @@ -111,10 +111,10 @@ class CServer { // open/close the primary screen void openPrimaryScreen(); - void closePrimaryScreen() throw(); + void closePrimaryScreen(); // cancel running threads - void cleanupThreads() throw(); + void cleanupThreads(); // thread method to accept incoming client connections void acceptClients(void*); diff --git a/synergy/CServerProtocol.cpp b/synergy/CServerProtocol.cpp index eb52afd8..f4d05697 100644 --- a/synergy/CServerProtocol.cpp +++ b/synergy/CServerProtocol.cpp @@ -26,22 +26,22 @@ CServerProtocol::~CServerProtocol() // do nothing } -CServer* CServerProtocol::getServer() const throw() +CServer* CServerProtocol::getServer() const { return m_server; } -CString CServerProtocol::getClient() const throw() +CString CServerProtocol::getClient() const { return m_client; } -IInputStream* CServerProtocol::getInputStream() const throw() +IInputStream* CServerProtocol::getInputStream() const { return m_input; } -IOutputStream* CServerProtocol::getOutputStream() const throw() +IOutputStream* CServerProtocol::getOutputStream() const { return m_output; } diff --git a/synergy/CServerProtocol.h b/synergy/CServerProtocol.h index 485ee2c3..5fb552a7 100644 --- a/synergy/CServerProtocol.h +++ b/synergy/CServerProtocol.h @@ -18,35 +18,35 @@ class CServerProtocol : public IServerProtocol { // accessors - virtual CServer* getServer() const throw(); - virtual CString getClient() const throw(); - virtual IInputStream* getInputStream() const throw(); - virtual IOutputStream* getOutputStream() const throw(); + virtual CServer* getServer() const; + virtual CString getClient() const; + virtual IInputStream* getInputStream() const; + virtual IOutputStream* getOutputStream() const; static IServerProtocol* create(SInt32 major, SInt32 minor, CServer*, const CString& clientName, IInputStream*, IOutputStream*); // IServerProtocol overrides - virtual void run() throw(XIO,XBadClient) = 0; - virtual void queryInfo() throw(XIO,XBadClient) = 0; - virtual void sendClose() throw(XIO) = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) throw(XIO) = 0; - virtual void sendLeave() throw(XIO) = 0; - virtual void sendGrabClipboard() throw(XIO) = 0; - virtual void sendQueryClipboard() throw(XIO) = 0; - virtual void sendScreenSaver(bool on) throw(XIO) = 0; - virtual void sendKeyDown(KeyID, KeyModifierMask) throw(XIO) = 0; - virtual void sendKeyRepeat(KeyID, KeyModifierMask) throw(XIO) = 0; - virtual void sendKeyUp(KeyID, KeyModifierMask) throw(XIO) = 0; - virtual void sendMouseDown(ButtonID) throw(XIO) = 0; - virtual void sendMouseUp(ButtonID) throw(XIO) = 0; - virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) throw(XIO) = 0; - virtual void sendMouseWheel(SInt32 delta) throw(XIO) = 0; + virtual void run() = 0; + virtual void queryInfo() = 0; + virtual void sendClose() = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void sendLeave() = 0; + virtual void sendGrabClipboard() = 0; + virtual void sendQueryClipboard() = 0; + virtual void sendScreenSaver(bool on) = 0; + virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; + virtual void sendKeyRepeat(KeyID, KeyModifierMask) = 0; + virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; + virtual void sendMouseDown(ButtonID) = 0; + virtual void sendMouseUp(ButtonID) = 0; + virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void sendMouseWheel(SInt32 delta) = 0; protected: //IServerProtocol overrides - virtual void recvInfo() throw(XIO,XBadClient) = 0; + virtual void recvInfo() = 0; private: CServer* m_server; diff --git a/synergy/CServerProtocol1_0.cpp b/synergy/CServerProtocol1_0.cpp index 2efe6aa1..746e0c1a 100644 --- a/synergy/CServerProtocol1_0.cpp +++ b/synergy/CServerProtocol1_0.cpp @@ -22,7 +22,7 @@ CServerProtocol1_0::~CServerProtocol1_0() // do nothing } -void CServerProtocol1_0::run() throw(XIO,XBadClient) +void CServerProtocol1_0::run() { // handle messages until the client hangs up for (;;) { @@ -53,7 +53,7 @@ void CServerProtocol1_0::run() throw(XIO,XBadClient) } } -void CServerProtocol1_0::queryInfo() throw(XIO,XBadClient) +void CServerProtocol1_0::queryInfo() { log((CLOG_INFO "querying client \"%s\" info", getClient().c_str())); @@ -71,93 +71,93 @@ void CServerProtocol1_0::queryInfo() throw(XIO,XBadClient) recvInfo(); } -void CServerProtocol1_0::sendClose() throw(XIO) +void CServerProtocol1_0::sendClose() { log((CLOG_INFO "send close to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClose); } void CServerProtocol1_0::sendEnter( - SInt32 xAbs, SInt32 yAbs) throw(XIO) + SInt32 xAbs, SInt32 yAbs) { log((CLOG_INFO "send enter to \"%s\", %d,%d", getClient().c_str(), xAbs, yAbs)); CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs); } -void CServerProtocol1_0::sendLeave() throw(XIO) +void CServerProtocol1_0::sendLeave() { log((CLOG_INFO "send leave to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCLeave); } -void CServerProtocol1_0::sendGrabClipboard() throw(XIO) +void CServerProtocol1_0::sendGrabClipboard() { log((CLOG_INFO "send grab clipboard to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard); } -void CServerProtocol1_0::sendQueryClipboard() throw(XIO) +void CServerProtocol1_0::sendQueryClipboard() { log((CLOG_INFO "query clipboard to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgQClipboard); } -void CServerProtocol1_0::sendScreenSaver(bool on) throw(XIO) +void CServerProtocol1_0::sendScreenSaver(bool on) { log((CLOG_INFO "send screen saver to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } void CServerProtocol1_0::sendKeyDown( - KeyID key, KeyModifierMask mask) throw(XIO) + KeyID key, KeyModifierMask mask) { log((CLOG_INFO "send key down to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); } void CServerProtocol1_0::sendKeyRepeat( - KeyID key, KeyModifierMask mask) throw(XIO) + KeyID key, KeyModifierMask mask) { log((CLOG_INFO "send key repeat to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask); } void CServerProtocol1_0::sendKeyUp( - KeyID key, KeyModifierMask mask) throw(XIO) + KeyID key, KeyModifierMask mask) { log((CLOG_INFO "send key up to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); } void CServerProtocol1_0::sendMouseDown( - ButtonID button) throw(XIO) + ButtonID button) { log((CLOG_INFO "send mouse down to \"%s\" id=%d", getClient().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); } void CServerProtocol1_0::sendMouseUp( - ButtonID button) throw(XIO) + ButtonID button) { log((CLOG_INFO "send mouse up to \"%s\" id=%d", getClient().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); } void CServerProtocol1_0::sendMouseMove( - SInt32 xAbs, SInt32 yAbs) throw(XIO) + SInt32 xAbs, SInt32 yAbs) { log((CLOG_INFO "send mouse move to \"%s\" %d,%d", getClient().c_str(), xAbs, yAbs)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); } void CServerProtocol1_0::sendMouseWheel( - SInt32 delta) throw(XIO) + SInt32 delta) { log((CLOG_INFO "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); } -void CServerProtocol1_0::recvInfo() throw(XIO,XBadClient) +void CServerProtocol1_0::recvInfo() { // parse the message SInt32 w, h, zoneInfo; diff --git a/synergy/CServerProtocol1_0.h b/synergy/CServerProtocol1_0.h index eec2d156..00cc58cb 100644 --- a/synergy/CServerProtocol1_0.h +++ b/synergy/CServerProtocol1_0.h @@ -13,25 +13,25 @@ class CServerProtocol1_0 : public CServerProtocol { // accessors // IServerProtocol overrides - virtual void run() throw(XIO,XBadClient); - virtual void queryInfo() throw(XIO,XBadClient); - virtual void sendClose() throw(XIO); - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) throw(XIO); - virtual void sendLeave() throw(XIO); - virtual void sendGrabClipboard() throw(XIO); - virtual void sendQueryClipboard() throw(XIO); - virtual void sendScreenSaver(bool on) throw(XIO); - virtual void sendKeyDown(KeyID, KeyModifierMask) throw(XIO); - virtual void sendKeyRepeat(KeyID, KeyModifierMask) throw(XIO); - virtual void sendKeyUp(KeyID, KeyModifierMask) throw(XIO); - virtual void sendMouseDown(ButtonID) throw(XIO); - virtual void sendMouseUp(ButtonID) throw(XIO); - virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) throw(XIO); - virtual void sendMouseWheel(SInt32 delta) throw(XIO); + virtual void run(); + virtual void queryInfo(); + virtual void sendClose(); + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs); + virtual void sendLeave(); + virtual void sendGrabClipboard(); + virtual void sendQueryClipboard(); + virtual void sendScreenSaver(bool on); + virtual void sendKeyDown(KeyID, KeyModifierMask); + virtual void sendKeyRepeat(KeyID, KeyModifierMask); + virtual void sendKeyUp(KeyID, KeyModifierMask); + virtual void sendMouseDown(ButtonID); + virtual void sendMouseUp(ButtonID); + virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void sendMouseWheel(SInt32 delta); protected: // IServerProtocol overrides - virtual void recvInfo() throw(XIO,XBadClient); + virtual void recvInfo(); }; #endif diff --git a/synergy/CTCPSocketFactory.cpp b/synergy/CTCPSocketFactory.cpp index cecbb348..7b10ff8d 100644 --- a/synergy/CTCPSocketFactory.cpp +++ b/synergy/CTCPSocketFactory.cpp @@ -16,12 +16,12 @@ CTCPSocketFactory::~CTCPSocketFactory() // do nothing } -ISocket* CTCPSocketFactory::create() const throw(XSocket) +ISocket* CTCPSocketFactory::create() const { return new CTCPSocket; } -IListenSocket* CTCPSocketFactory::createListen() const throw(XSocket) +IListenSocket* CTCPSocketFactory::createListen() const { return new CTCPListenSocket; } diff --git a/synergy/CTCPSocketFactory.h b/synergy/CTCPSocketFactory.h index db38ee6e..d5d689e7 100644 --- a/synergy/CTCPSocketFactory.h +++ b/synergy/CTCPSocketFactory.h @@ -13,8 +13,8 @@ class CTCPSocketFactory : public ISocketFactory { // accessors // ISocketFactory overrides - virtual ISocket* create() const throw(XSocket); - virtual IListenSocket* createListen() const throw(XSocket); + virtual ISocket* create() const; + virtual IListenSocket* createListen() const; }; #endif diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index cf5ece9f..360c9d7e 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -14,32 +14,32 @@ class IServerProtocol : public IInterface { // process messages from the client and insert the appropriate // events into the server's event queue. return when the client // disconnects. - virtual void run() throw(XIO,XBadClient) = 0; + virtual void run() = 0; // send client info query and process reply - virtual void queryInfo() throw(XIO,XBadClient) = 0; + virtual void queryInfo() = 0; // send various messages to client - virtual void sendClose() throw(XIO) = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) throw(XIO) = 0; - virtual void sendLeave() throw(XIO) = 0; - virtual void sendGrabClipboard() throw(XIO) = 0; - virtual void sendQueryClipboard() throw(XIO) = 0; - virtual void sendScreenSaver(bool on) throw(XIO) = 0; - virtual void sendKeyDown(KeyID, KeyModifierMask) throw(XIO) = 0; - virtual void sendKeyRepeat(KeyID, KeyModifierMask) throw(XIO) = 0; - virtual void sendKeyUp(KeyID, KeyModifierMask) throw(XIO) = 0; - virtual void sendMouseDown(ButtonID) throw(XIO) = 0; - virtual void sendMouseUp(ButtonID) throw(XIO) = 0; - virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) throw(XIO) = 0; - virtual void sendMouseWheel(SInt32 delta) throw(XIO) = 0; + virtual void sendClose() = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void sendLeave() = 0; + virtual void sendGrabClipboard() = 0; + virtual void sendQueryClipboard() = 0; + virtual void sendScreenSaver(bool on) = 0; + virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; + virtual void sendKeyRepeat(KeyID, KeyModifierMask) = 0; + virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; + virtual void sendMouseDown(ButtonID) = 0; + virtual void sendMouseUp(ButtonID) = 0; + virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void sendMouseWheel(SInt32 delta) = 0; // accessors protected: // manipulators - virtual void recvInfo() throw(XIO,XBadClient) = 0; + virtual void recvInfo() = 0; // accessors }; diff --git a/synergy/ISocketFactory.h b/synergy/ISocketFactory.h index e919432d..2bb8ce2f 100644 --- a/synergy/ISocketFactory.h +++ b/synergy/ISocketFactory.h @@ -14,8 +14,8 @@ class ISocketFactory : public IInterface { // accessors // create sockets - virtual ISocket* create() const throw(XSocket) = 0; - virtual IListenSocket* createListen() const throw(XSocket) = 0; + virtual ISocket* create() const = 0; + virtual IListenSocket* createListen() const = 0; }; #endif From b3291bc2b524d96f3bde9eddae74754298929d07 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 14 Oct 2001 18:29:43 +0000 Subject: [PATCH 014/807] fixed timeout bug in CThreadRep::wait() (negative timeout wouldn't wait forever). also fixed early return from sleep due to signal. now forcing client to initialize CThread to ensure global mutex gets initialized before threads are used. --- base/CLog.h | 3 +++ mt/CMutex.cpp | 8 ++++++ mt/CThread.cpp | 5 ++++ mt/CThread.h | 5 ++++ mt/CThreadRep.cpp | 47 ++++++++++++++++++++++------------ mt/CThreadRep.h | 7 +++-- mt/CTimerThread.cpp | 3 +++ synergy/CClient.cpp | 15 +++++++++-- synergy/CClient.h | 3 +++ synergy/CProtocolUtil.cpp | 5 ++++ synergy/CServerProtocol1_0.cpp | 6 +++++ synergy/client.cpp | 3 +++ synergy/server.cpp | 3 +++ 13 files changed, 92 insertions(+), 21 deletions(-) diff --git a/base/CLog.h b/base/CLog.h index 0d44c3e7..3116142b 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -16,12 +16,15 @@ class CLog { #if defined(NOLOGGING) #define log(_a1) +#define logc(_a1, _a2) #define CLOG_TRACE #elif defined(NDEBUG) #define log(_a1) CLog::print _a1 +#define logc(_a1, _a2) if (_a1) CLog::print _a2 #define CLOG_TRACE #else #define log(_a1) CLog::printt _a1 +#define logc(_a1, _a2) if (_a1) CLog::printt _a2 #define CLOG_TRACE __FILE__, __LINE__, #endif diff --git a/mt/CMutex.cpp b/mt/CMutex.cpp index 3b203d50..311df19a 100644 --- a/mt/CMutex.cpp +++ b/mt/CMutex.cpp @@ -1,4 +1,5 @@ #include "CMutex.h" +#include "CLog.h" #include // @@ -40,10 +41,15 @@ void CMutex::init() m_mutex = reinterpret_cast(mutex); } +#include +#include +#include +#include void CMutex::fini() { pthread_mutex_t* mutex = reinterpret_cast(m_mutex); int status = pthread_mutex_destroy(mutex); + logc(status != 0, (CLOG_ERR "pthread_mutex_destroy status %d", status)); assert(status == 0); delete mutex; } @@ -67,6 +73,7 @@ void CMutex::lock() const break; default: + log((CLOG_ERR "pthread_mutex_lock status %d", status)); assert(0 && "unexpected error"); } } @@ -86,6 +93,7 @@ void CMutex::unlock() const break; default: + log((CLOG_ERR "pthread_mutex_unlock status %d", status)); assert(0 && "unexpected error"); } } diff --git a/mt/CThread.cpp b/mt/CThread.cpp index 17fd85c1..0b2a098a 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -58,6 +58,11 @@ CThread& CThread::operator=(const CThread& thread) return *this; } +void CThread::init() +{ + CThreadRep::initThreads(); +} + void CThread::sleep(double timeout) { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); diff --git a/mt/CThread.h b/mt/CThread.h index b5cf6d49..f505ad00 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -28,6 +28,11 @@ class CThread { // start a new thread. CThread& operator=(const CThread&); + // initialize the thread library. this must be called before + // any other thread methods or creating a thread object. it + // is harmless to call init() multiple times. + static void init(); + // the calling thread sleeps for the given number of seconds. if // timeout <= 0.0 then the call returns immediately. if timeout // == 0.0 then the calling thread yields the CPU. diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 296b50da..87847f29 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -1,7 +1,8 @@ #include "CThreadRep.h" #include "CThread.h" -#include "XThread.h" +#include "CMutex.h" #include "CLock.h" +#include "XThread.h" #include "IJob.h" #include @@ -16,7 +17,7 @@ class XThreadUnavailable { }; // CThreadRep // -CMutex CThreadRep::s_mutex; +CMutex* CThreadRep::s_mutex = NULL; CThreadRep* CThreadRep::s_head = NULL; CThreadRep::CThreadRep() : m_prev(NULL), @@ -26,6 +27,7 @@ CThreadRep::CThreadRep() : m_prev(NULL), m_userData(NULL) { // note -- s_mutex must be locked on entry + assert(s_mutex != NULL); // initialize stuff init(); @@ -65,6 +67,7 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : m_userData(userData) { assert(m_job != NULL); + assert(s_mutex != NULL); // create a thread rep for the main thread if the current thread // is unknown. note that this might cause multiple "main" threads @@ -75,7 +78,7 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : init(); // hold mutex while we create the thread - CLock lock(&s_mutex); + CLock lock(s_mutex); // start the thread. throw if it doesn't start. #if defined(CONFIG_PTHREADS) @@ -124,15 +127,22 @@ CThreadRep::~CThreadRep() fini(); } +void CThreadRep::initThreads() +{ + if (s_mutex == NULL) { + s_mutex = new CMutex; + } +} + void CThreadRep::ref() { - CLock lock(&s_mutex); + CLock lock(s_mutex); ++m_refCount; } void CThreadRep::unref() { - CLock lock(&s_mutex); + CLock lock(s_mutex); if (--m_refCount == 0) { delete this; } @@ -140,7 +150,7 @@ void CThreadRep::unref() bool CThreadRep::enableCancel(bool enable) { - CLock lock(&s_mutex); + CLock lock(s_mutex); const bool old = m_cancellable; m_cancellable = enable; return old; @@ -148,7 +158,7 @@ bool CThreadRep::enableCancel(bool enable) bool CThreadRep::isCancellable() const { - CLock lock(&s_mutex); + CLock lock(s_mutex); return (m_cancellable && !m_cancelling); } @@ -166,6 +176,8 @@ void* CThreadRep::getUserData() const CThreadRep* CThreadRep::getCurrentThreadRep() { + assert(s_mutex != NULL); + #if defined(CONFIG_PTHREADS) const pthread_t thread = pthread_self(); #elif defined(CONFIG_PLATFORM_WIN32) @@ -173,7 +185,7 @@ CThreadRep* CThreadRep::getCurrentThreadRep() #endif // lock list while we search - CLock lock(&s_mutex); + CLock lock(s_mutex); // search CThreadRep* scan = s_head; @@ -207,7 +219,7 @@ void CThreadRep::doThreadFunc() setPriority(1); // wait for parent to initialize this object - { CLock lock(&s_mutex); } + { CLock lock(s_mutex); } void* result = NULL; try { @@ -263,12 +275,13 @@ void CThreadRep::sleep(double timeout) struct timespec t; t.tv_sec = (long)timeout; t.tv_nsec = (long)(1000000000.0 * (timeout - (double)t.tv_sec)); - nanosleep(&t, NULL); + while (nanosleep(&t, &t) < 0) + testCancel(); } void CThreadRep::cancel() { - CLock lock(&s_mutex); + CLock lock(s_mutex); if (m_cancellable && !m_cancelling) { m_cancel = true; @@ -281,7 +294,7 @@ void CThreadRep::testCancel() { { // prevent further cancellation - CLock lock(&s_mutex); + CLock lock(s_mutex); if (!m_cancel || !m_cancellable || m_cancelling) return; @@ -303,14 +316,14 @@ bool CThreadRep::wait(CThreadRep* target, double timeout) if (target->isExited()) return true; - if (timeout > 0.0) { + if (timeout != 0.0) { CStopwatch timer; do { sleep(0.05); testCancel(); if (target->isExited()) return true; - } while (timer.getTime() <= timeout); + } while (timeout < 0.0 || timer.getTime() <= timeout); } return false; @@ -323,7 +336,7 @@ void CThreadRep::setPriority(int) bool CThreadRep::isExited() const { - CLock lock(&s_mutex); + CLock lock(s_mutex); return m_exit; } @@ -341,7 +354,7 @@ void* CThreadRep::threadFunc(void* arg) rep->unref(); // mark as terminated - CLock lock(&s_mutex); + CLock lock(s_mutex); rep->m_exit = true; // terminate the thread @@ -400,7 +413,7 @@ void CThreadRep::testCancel() { // ignore if disabled or already cancelling - CLock lock(&s_mutex); + CLock lock(s_mutex); if (!m_cancellable || m_cancelling) return; diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h index 89bb6131..3cfce4b9 100644 --- a/mt/CThreadRep.h +++ b/mt/CThreadRep.h @@ -1,7 +1,6 @@ #ifndef CTHREADREP_H #define CTHREADREP_H -#include "CMutex.h" #include "BasicTypes.h" #if defined(CONFIG_PTHREADS) @@ -10,6 +9,7 @@ #include #endif +class CMutex; class IJob; class CThreadRep { @@ -18,6 +18,9 @@ class CThreadRep { // manipulators + // initialize the thread library + static void initThreads(); + // change ref count void ref(); void unref(); @@ -92,7 +95,7 @@ class CThreadRep { CThreadRep& operator=(const CThreadRep&); private: - static CMutex s_mutex; + static CMutex* s_mutex; static CThreadRep* s_head; CThreadRep* m_prev; diff --git a/mt/CTimerThread.cpp b/mt/CTimerThread.cpp index afe2925c..d1d7d48f 100644 --- a/mt/CTimerThread.cpp +++ b/mt/CTimerThread.cpp @@ -1,6 +1,7 @@ #include "CTimerThread.h" #include "CThread.h" #include "TMethodJob.h" +#include "CLog.h" #include // @@ -24,7 +25,9 @@ CTimerThread::~CTimerThread() void CTimerThread::timer(void*) { + log((CLOG_DEBUG "timeout in %f seconds", m_timeout)); CThread::sleep(m_timeout); + log((CLOG_DEBUG "timeout")); m_callingThread->cancel(); } diff --git a/synergy/CClient.cpp b/synergy/CClient.cpp index 6fb1d215..8cd8fb61 100644 --- a/synergy/CClient.cpp +++ b/synergy/CClient.cpp @@ -4,8 +4,10 @@ #include "CProtocolUtil.h" #include "ISecondaryScreen.h" #include "ProtocolTypes.h" +#include "CThread.h" #include "CTimerThread.h" #include "XSynergy.h" +#include "TMethodJob.h" #include "CLog.h" #include @@ -19,15 +21,24 @@ CClient::CClient(const CString& clientName) : m_output(NULL), m_screen(NULL) { + // do nothing } CClient::~CClient() { + // do nothing +} + +void CClient::run(const CNetworkAddress& serverAddress) +{ + m_serverAddress = &serverAddress; + CThread thread(new TMethodJob(this, &CClient::runSession)); + thread.wait(); } #include "CTCPSocket.h" #include "CXWindowsSecondaryScreen.h" -void CClient::run(const CNetworkAddress& serverAddress) +void CClient::runSession(void*) { log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); @@ -41,7 +52,7 @@ void CClient::run(const CNetworkAddress& serverAddress) // create socket and attempt to connect to server log((CLOG_DEBUG "connecting to server")); socket.reset(new CTCPSocket()); // FIXME -- use factory - socket->connect(serverAddress); + socket->connect(*m_serverAddress); log((CLOG_INFO "connected to server")); // get the input and output streams diff --git a/synergy/CClient.h b/synergy/CClient.h index 825e973f..ddcd3a09 100644 --- a/synergy/CClient.h +++ b/synergy/CClient.h @@ -22,6 +22,8 @@ class CClient { private: + void runSession(void*); + // message handlers void onEnter(); void onLeave(); @@ -53,6 +55,7 @@ class CClient { IInputStream* m_input; IOutputStream* m_output; ISecondaryScreen* m_screen; + const CNetworkAddress* m_serverAddress; }; #endif diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index ec0cc2cc..e22d24c4 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -52,6 +52,7 @@ void CProtocolUtil::readf(IInputStream* stream, { assert(stream != NULL); assert(fmt != NULL); + log((CLOG_DEBUG "readf(%s)", fmt)); va_list args; va_start(args, fmt); @@ -93,6 +94,7 @@ void CProtocolUtil::readf(IInputStream* stream, static_cast(buffer[3]); break; } + log((CLOG_DEBUG "readf: read %d byte integer: %d (0x%x)", len, *v, *v)); break; } @@ -129,6 +131,7 @@ void CProtocolUtil::readf(IInputStream* stream, } throw; } + log((CLOG_DEBUG "readf: read %d byte string: %.*s", len, len, sBuffer)); // save the data CString* dst = va_arg(args, CString*); @@ -159,6 +162,7 @@ void CProtocolUtil::readf(IInputStream* stream, // verify match if (buffer[0] != *fmt) { + log((CLOG_DEBUG "readf: format mismatch: %c vs %c", *fmt, buffer[0])); throw XIOReadMismatch(); } @@ -322,6 +326,7 @@ void CProtocolUtil::read(IInputStream* stream, // bail if stream has hungup if (n == 0) { + log((CLOG_DEBUG "unexpected disconnect in readf(), %d bytes left", count)); throw XIOEndOfStream(); } diff --git a/synergy/CServerProtocol1_0.cpp b/synergy/CServerProtocol1_0.cpp index 746e0c1a..de28aa81 100644 --- a/synergy/CServerProtocol1_0.cpp +++ b/synergy/CServerProtocol1_0.cpp @@ -32,10 +32,14 @@ void CServerProtocol1_0::run() // verify we got an entire code if (n == 0) { + log((CLOG_NOTE "client \"%s\" disconnected", getClient().c_str())); + // client hungup return; } if (n != 4) { + log((CLOG_ERR "incomplete message from \"%s\": %d bytes", getClient().c_str(), n)); + // client sent an incomplete message throw XBadClient(); } @@ -47,6 +51,8 @@ void CServerProtocol1_0::run() } // FIXME -- more message here else { + log((CLOG_ERR "unknown message from client \"%s\"", getClient().c_str())); + // unknown message throw XBadClient(); } diff --git a/synergy/client.cpp b/synergy/client.cpp index b1c3c16b..4ada77d9 100644 --- a/synergy/client.cpp +++ b/synergy/client.cpp @@ -1,9 +1,12 @@ #include "CClient.h" #include "CNetworkAddress.h" +#include "CThread.h" #include int main(int argc, char** argv) { + CThread::init(); + if (argc != 2) { fprintf(stderr, "usage: %s \n", argv[0]); return 1; diff --git a/synergy/server.cpp b/synergy/server.cpp index 29c8466b..ef1b578f 100644 --- a/synergy/server.cpp +++ b/synergy/server.cpp @@ -1,9 +1,12 @@ #include "CServer.h" #include "CScreenMap.h" +#include "CThread.h" #include int main(int argc, char** argv) { + CThread::init(); + if (argc != 1) { fprintf(stderr, "usage: %s\n", argv[0]); return 1; From 9435639545705f96a73a2f5c45cabc511dd0ba64 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 14 Oct 2001 19:16:54 +0000 Subject: [PATCH 015/807] some debugging code. --- mt/CThread.cpp | 3 +++ mt/CThreadRep.cpp | 14 ++++++++++++-- mt/CTimerThread.cpp | 3 +++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mt/CThread.cpp b/mt/CThread.cpp index 0b2a098a..6e40060b 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -3,6 +3,7 @@ #include "XThread.h" #include "CLock.h" #include "CStopwatch.h" +#include "CLog.h" // // CThreadPtr @@ -75,6 +76,8 @@ void CThread::sleep(double timeout) void CThread::exit(void* result) { + CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); + log((CLOG_DEBUG "throw exit on thread %p", currentRep.operator->())); throw XThreadExit(result); } diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 87847f29..cdcccd99 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -3,6 +3,7 @@ #include "CMutex.h" #include "CLock.h" #include "XThread.h" +#include "CLog.h" #include "IJob.h" #include @@ -229,14 +230,19 @@ void CThreadRep::doThreadFunc() catch (XThreadCancel&) { // client called cancel() + log((CLOG_DEBUG "caught cancel on thread %p", this)); } catch (XThreadExit& e) { // client called exit() result = e.m_result; + log((CLOG_DEBUG "caught exit on thread %p", this)); + } + catch (...) { + log((CLOG_DEBUG "caught exit on thread %p", this)); + // note -- don't catch (...) to avoid masking bugs + throw; } - - // note -- don't catch (...) to avoid masking bugs // done with job delete m_job; @@ -286,6 +292,7 @@ void CThreadRep::cancel() m_cancel = true; // break out of system calls + log((CLOG_DEBUG "cancel thread %p", this)); pthread_kill(m_thread, SIGALRM); } } @@ -304,6 +311,7 @@ void CThreadRep::testCancel() } // start cancel + log((CLOG_DEBUG "throw cancel on thread %p", this)); throw XThreadCancel(); } @@ -401,6 +409,7 @@ void CThreadRep::sleep(double timeout) void CThreadRep::cancel() { + log((CLOG_DEBUG "cancel thread %p", this)); SetEvent(m_cancel); } @@ -423,6 +432,7 @@ void CThreadRep::testCancel() } // start cancel + log((CLOG_DEBUG "throw cancel on thread %p", this)); throw XThreadCancel(); } diff --git a/mt/CTimerThread.cpp b/mt/CTimerThread.cpp index d1d7d48f..3d149c45 100644 --- a/mt/CTimerThread.cpp +++ b/mt/CTimerThread.cpp @@ -18,7 +18,10 @@ CTimerThread::CTimerThread(double timeout) : m_timeout(timeout) CTimerThread::~CTimerThread() { + log((CLOG_DEBUG "cancelling timeout")); m_timingThread->cancel(); + m_timingThread->wait(); + log((CLOG_DEBUG "cancelled timeout")); delete m_timingThread; delete m_callingThread; } From 3cfbc0f68c7a3eaf60b8445a0591055f1af0f548 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 20 Oct 2001 20:43:31 +0000 Subject: [PATCH 016/807] threading fixes. had sigmask set in wrong place. was setting m_exit flag potentially after the object had been destroyed. most importantly, RTTI must be enabled on PPC to avoid SIGILL. --- Make-linux | 2 +- mt/CThreadRep.cpp | 69 +++++++++++++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/Make-linux b/Make-linux index e3e656d6..f5ce7843 100644 --- a/Make-linux +++ b/Make-linux @@ -15,7 +15,7 @@ RMR = /bin/rm -rf # GCXXDEFS = -D_XOPEN_SOURCE=500 GCXXINCS = -I$(DEPTH)/include -I/usr/X11R6/include -GCXXOPTS = -Wall -W -fexceptions -fno-rtti +GCXXOPTS = -Wall -W -fexceptions CXXOPTIMIZER = -g #CXXOPTIMIZER = -O2 -DNDEBUG diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index cdcccd99..cb6824fa 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -9,6 +9,7 @@ #if defined(CONFIG_PTHREADS) #include +#define SIGWAKEUP SIGUSR1 #endif // FIXME -- temporary exception type @@ -35,17 +36,6 @@ CThreadRep::CThreadRep() : m_prev(NULL), #if defined(CONFIG_PTHREADS) // get main thread id m_thread = pthread_self(); - - // install SIGALRM handler - struct sigaction act; - act.sa_handler = &threadCancel; -# if defined(SA_INTERRUPT) - act.sa_flags = SA_INTERRUPT; -# else - act.sa_flags = 0; -# endif - sigemptyset(&act.sa_mask); - sigaction(SIGALRM, &act, NULL); #elif defined(CONFIG_PLATFORM_WIN32) // get main thread id m_thread = NULL; @@ -86,10 +76,6 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : int status = pthread_create(&m_thread, NULL, threadFunc, (void*)this); if (status != 0) throw XThreadUnavailable(); - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGALRM); - pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); #elif defined(CONFIG_PLATFORM_WIN32) unsigned int id; m_thread = reinterpret_cast(_beginthreadex(NULL, 0, @@ -132,6 +118,25 @@ void CThreadRep::initThreads() { if (s_mutex == NULL) { s_mutex = new CMutex; + +#if defined(CONFIG_PTHREADS) + // install SIGWAKEUP handler + struct sigaction act; + act.sa_handler = &threadCancel; +# if defined(SA_INTERRUPT) + act.sa_flags = SA_INTERRUPT; +# else + act.sa_flags = 0; +# endif + sigemptyset(&act.sa_mask); + sigaction(SIGWAKEUP, &act, NULL); +#endif + + // set signal mask + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGWAKEUP); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); } } @@ -228,7 +233,7 @@ void CThreadRep::doThreadFunc() m_job->run(); } - catch (XThreadCancel&) { + catch (XThreadCancel& e) { // client called cancel() log((CLOG_DEBUG "caught cancel on thread %p", this)); } @@ -239,8 +244,9 @@ void CThreadRep::doThreadFunc() log((CLOG_DEBUG "caught exit on thread %p", this)); } catch (...) { - log((CLOG_DEBUG "caught exit on thread %p", this)); + log((CLOG_DEBUG "exception on thread %p", this)); // note -- don't catch (...) to avoid masking bugs + delete m_job; throw; } @@ -290,11 +296,14 @@ void CThreadRep::cancel() CLock lock(s_mutex); if (m_cancellable && !m_cancelling) { m_cancel = true; - - // break out of system calls - log((CLOG_DEBUG "cancel thread %p", this)); - pthread_kill(m_thread, SIGALRM); } + else { + return; + } + + // break out of system calls + log((CLOG_DEBUG "cancel thread %p", this)); + pthread_kill(m_thread, SIGWAKEUP); } void CThreadRep::testCancel() @@ -355,16 +364,24 @@ void* CThreadRep::threadFunc(void* arg) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + // set signal mask + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGWAKEUP); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + // run thread rep->doThreadFunc(); + { + // mark as terminated + CLock lock(s_mutex); + rep->m_exit = true; + } + // unref thread rep->unref(); - // mark as terminated - CLock lock(s_mutex); - rep->m_exit = true; - // terminate the thread return NULL; } @@ -518,7 +535,7 @@ unsigned int __stdcall CThreadRep::threadFunc(void* arg) CThreadRep* rep = (CThreadRep*)arg; // initialize OLE - const HRESULT hr = ::OleInitialize(NULL); + const HRESULT hr = ::OleInitialize(NULL); // run thread rep->doThreadFunc(); From 8d1cf844c7707c69c4a8f399ea0ce0743b2059ae Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 21 Oct 2001 00:21:02 +0000 Subject: [PATCH 017/807] fixed bugs in handling streams. --- io/CBufferedInputStream.cpp | 3 +++ io/CBufferedOutputStream.cpp | 1 + mt/CThreadRep.cpp | 32 ++++++++++++++++++++++++++++++-- net/CTCPSocket.cpp | 30 +++++++++++++++++++++++------- net/CTCPSocket.h | 3 ++- synergy/CServer.cpp | 19 +++++++++++++++---- 6 files changed, 74 insertions(+), 14 deletions(-) diff --git a/io/CBufferedInputStream.cpp b/io/CBufferedInputStream.cpp index 98c8fbe9..dbcb63f7 100644 --- a/io/CBufferedInputStream.cpp +++ b/io/CBufferedInputStream.cpp @@ -87,6 +87,9 @@ void CBufferedInputStream::close() } m_closed = true; + m_hungup = true; + m_buffer.pop(m_buffer.getSize()); + m_empty.broadcast(); if (m_closeCB) { m_closeCB->run(); } diff --git a/io/CBufferedOutputStream.cpp b/io/CBufferedOutputStream.cpp index bf1603c6..387ce406 100644 --- a/io/CBufferedOutputStream.cpp +++ b/io/CBufferedOutputStream.cpp @@ -46,6 +46,7 @@ void CBufferedOutputStream::close() } m_closed = true; + m_buffer.pop(m_buffer.getSize()); if (m_closeCB) { m_closeCB->run(); } diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index cb6824fa..41380225 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -15,6 +15,18 @@ // FIXME -- temporary exception type class XThreadUnavailable { }; +#ifndef NDEBUG +#include +#include +#include +#include +static void threadDebug(int) +{ + if (fork() == 0) abort(); + else { wait(0); exit(1); } +} +#endif + // // CThreadRep // @@ -122,21 +134,31 @@ void CThreadRep::initThreads() #if defined(CONFIG_PTHREADS) // install SIGWAKEUP handler struct sigaction act; - act.sa_handler = &threadCancel; + sigemptyset(&act.sa_mask); # if defined(SA_INTERRUPT) act.sa_flags = SA_INTERRUPT; # else act.sa_flags = 0; # endif - sigemptyset(&act.sa_mask); + act.sa_handler = &threadCancel; sigaction(SIGWAKEUP, &act, NULL); +# ifndef NDEBUG + act.sa_handler = &threadDebug; + sigaction(SIGSEGV, &act, NULL); +# endif #endif // set signal mask sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGWAKEUP); +#ifndef NDEBUG + sigaddset(&sigset, SIGSEGV); +#endif pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + sigemptyset(&sigset); + sigaddset(&sigset, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); } } @@ -368,7 +390,13 @@ void* CThreadRep::threadFunc(void* arg) sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGWAKEUP); +#ifndef NDEBUG + sigaddset(&sigset, SIGSEGV); +#endif pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + sigemptyset(&sigset); + sigaddset(&sigset, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); // run thread rep->doThreadFunc(); diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index d38ee738..39defc3d 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -29,8 +29,7 @@ CTCPSocket::CTCPSocket() init(); } -CTCPSocket::CTCPSocket(int fd) : - m_fd(fd) +CTCPSocket::CTCPSocket(int fd) : m_fd(fd) { assert(m_fd != -1); @@ -41,7 +40,7 @@ CTCPSocket::CTCPSocket(int fd) : // start handling socket m_thread = new CThread(new TMethodJob( - this, &CTCPSocket::service)); + this, &CTCPSocket::ioThread)); } CTCPSocket::~CTCPSocket() @@ -54,9 +53,9 @@ CTCPSocket::~CTCPSocket() } // clean up - delete m_mutex; delete m_input; delete m_output; + delete m_mutex; } void CTCPSocket::bind(const CNetworkAddress& addr) @@ -80,7 +79,7 @@ void CTCPSocket::connect(const CNetworkAddress& addr) // start servicing the socket m_connected = kReadWrite; m_thread = new CThread(new TMethodJob( - this, &CTCPSocket::service)); + this, &CTCPSocket::ioThread)); } void CTCPSocket::close() @@ -135,7 +134,21 @@ void CTCPSocket::init() this, &CTCPSocket::closeOutput)); } -void CTCPSocket::service(void*) +void CTCPSocket::ioThread(void*) +{ + try { + ioService(); + m_input->close(); + m_output->close(); + } + catch (...) { + m_input->close(); + m_output->close(); + throw; + } +} + +void CTCPSocket::ioService() { assert(m_fd != -1); @@ -198,12 +211,15 @@ void CTCPSocket::service(void*) // write data const void* buffer = m_output->peek(n); - n = write(m_fd, buffer, n); + n = (UInt32)write(m_fd, buffer, n); // discard written data if (n > 0) { m_output->pop(n); } + else if (n == (UInt32)-1 && errno == EPIPE) { + return; + } } } } diff --git a/net/CTCPSocket.h b/net/CTCPSocket.h index 208774ff..c7b9ca73 100644 --- a/net/CTCPSocket.h +++ b/net/CTCPSocket.h @@ -30,7 +30,8 @@ class CTCPSocket : public ISocket { private: void init(); - void service(void*); + void ioThread(void*); + void ioService(); void closeInput(void*); void closeOutput(void*); diff --git a/synergy/CServer.cpp b/synergy/CServer.cpp index 31f6e65e..aeafedba 100644 --- a/synergy/CServer.cpp +++ b/synergy/CServer.cpp @@ -20,6 +20,15 @@ #include #include +/* XXX +#include +#include +#include +#include +if (fork() == 0) abort(); +else { wait(0); exit(1); } +*/ + // // CServer // @@ -647,18 +656,20 @@ void CServer::handshakeClient(void* vsocket) std::auto_ptr output; // attach the encryption layer + bool own = false; if (m_securityFactory != NULL) { /* FIXME -- implement ISecurityFactory - input.reset(m_securityFactory->createInputFilter(srcInput, false)); - output.reset(m_securityFactory->createOutputFilter(srcOutput, false)); + input.reset(m_securityFactory->createInputFilter(srcInput, own)); + output.reset(m_securityFactory->createOutputFilter(srcOutput, own)); srcInput = input.get(); srcOutput = output.get(); + own = true; */ } // attach the packetizing filters - input.reset(new CInputPacketStream(srcInput, true)); - output.reset(new COutputPacketStream(srcOutput, true)); + input.reset(new CInputPacketStream(srcInput, own)); + output.reset(new COutputPacketStream(srcOutput, own)); std::auto_ptr protocol; std::auto_ptr connectedNote; From cfdf6bd73ac71314b348fc8c74b0d7854f0c0763 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 21 Oct 2001 00:21:21 +0000 Subject: [PATCH 018/807] fixed handling of stream ownership. --- synergy/CClient.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/synergy/CClient.cpp b/synergy/CClient.cpp index 8cd8fb61..51298a2c 100644 --- a/synergy/CClient.cpp +++ b/synergy/CClient.cpp @@ -60,18 +60,20 @@ void CClient::runSession(void*) IOutputStream* srcOutput = socket->getOutputStream(); // attach the encryption layer + bool own = false; /* FIXME -- implement ISecurityFactory if (m_securityFactory != NULL) { - input.reset(m_securityFactory->createInputFilter(srcInput, false)); - output.reset(m_securityFactory->createOutputFilter(srcOutput, false)); + input.reset(m_securityFactory->createInputFilter(srcInput, own)); + output.reset(m_securityFactory->createOutputFilter(srcOutput, own)); srcInput = input.get(); srcOutput = output.get(); + own = true; } */ // attach the packetizing filters - input.reset(new CInputPacketStream(srcInput, true)); - output.reset(new COutputPacketStream(srcOutput, true)); + input.reset(new CInputPacketStream(srcInput, own)); + output.reset(new COutputPacketStream(srcOutput, own)); // wait for hello from server log((CLOG_DEBUG "wait for hello")); From b7533aea2a973a19b0508f049d60b523a222e976 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Oct 2001 21:13:08 +0000 Subject: [PATCH 019/807] fixed blown assert trying to find neighbor when there was none. --- synergy/CServer.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/synergy/CServer.cpp b/synergy/CServer.cpp index aeafedba..595f6fde 100644 --- a/synergy/CServer.cpp +++ b/synergy/CServer.cpp @@ -305,6 +305,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) m_y = y; } else { + log((CLOG_DEBUG "no neighbor; clamping")); if (m_x < 0) m_x = 0; else if (m_x > m_active->m_width - 1) @@ -446,8 +447,8 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, assert(src != NULL); // get the first neighbor + CScreenInfo* lastGoodScreen = src; CScreenInfo* dst = getNeighbor(src, srcSide); - CScreenInfo* lastGoodScreen = dst; // get the source screen's size (needed for kRight and kBottom) SInt32 w = src->m_width, h = src->m_height; @@ -510,12 +511,16 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, } break; } + assert(lastGoodScreen != NULL); + + // no neighbor if best neighbor is the source itself + if (lastGoodScreen == src) + return NULL; // if entering primary screen then be sure to move in far enough // to avoid the jump zone. if entering a side that doesn't have // a neighbor (i.e. an asymmetrical side) then we don't need to // move inwards because that side can't provoke a jump. - assert(lastGoodScreen != NULL); if (lastGoodScreen->m_protocol == NULL) { const CString dstName(lastGoodScreen->m_name); switch (srcSide) { From c9f0b694de97913867c4e9d61a47aa5883fe913d Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Oct 2001 21:23:29 +0000 Subject: [PATCH 020/807] can now filter logging by level. --- base/CLog.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index 92a32744..cb98cbbd 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -1,5 +1,6 @@ #include "CLog.h" #include +#include #include #include @@ -7,6 +8,8 @@ // CLog // +static int g_maxPriority = -1; + void CLog::print(const char* fmt, ...) { // check if fmt begins with a priority argument @@ -76,12 +79,25 @@ void CLog::output(int priority, const char* msg) "INFO", "DEBUG", }; - - assert(priority >= 0 && priority < (int)(sizeof(s_priority) / - sizeof(s_priority[0]))); + static const int s_numPriority = (int)(sizeof(s_priority) / + sizeof(s_priority[0])); + assert(priority >= 0 && priority < s_numPriority); assert(msg != 0); - fprintf(stderr, "%s: %s\n", s_priority[priority], msg); + if (g_maxPriority == -1) { + g_maxPriority = s_numPriority - 1; + const char* priEnv = getenv("SYN_LOG_PRI"); + if (priEnv != NULL) { + for (int i = 0; i < s_numPriority; ++i) + if (strcmp(priEnv, s_priority[i]) == 0) { + g_maxPriority = i; + break; + } + } + } + + if (priority <= g_maxPriority) + fprintf(stderr, "%s: %s\n", s_priority[priority], msg); } char* CLog::vsprint(int pad, char* buffer, int len, From 20c7aca1db01ad357934a28835a10f5c7c627969 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Oct 2001 22:41:46 +0000 Subject: [PATCH 021/807] added cursor hiding. --- synergy/CXWindowsPrimaryScreen.cpp | 46 +++++++++++++--- synergy/CXWindowsPrimaryScreen.h | 2 + synergy/CXWindowsSecondaryScreen.cpp | 81 ++++++++++++++++++++++++++-- synergy/CXWindowsSecondaryScreen.h | 2 + 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/synergy/CXWindowsPrimaryScreen.cpp b/synergy/CXWindowsPrimaryScreen.cpp index cafbbcb8..d464b0f7 100644 --- a/synergy/CXWindowsPrimaryScreen.cpp +++ b/synergy/CXWindowsPrimaryScreen.cpp @@ -49,7 +49,7 @@ void CXWindowsPrimaryScreen::open(CServer* server) log((CLOG_INFO "primary display size: %dx%d", m_w, m_h)); // get the root window - Window root = RootWindow(m_display, m_screen); + m_root = RootWindow(m_display, m_screen); // create the grab window. this window is used to capture user // input when the user is focussed on another client. don't let @@ -61,15 +61,15 @@ void CXWindowsPrimaryScreen::open(CServer* server) KeymapStateMask; attr.do_not_propagate_mask = 0; attr.override_redirect = True; - attr.cursor = None; - m_window = ::XCreateWindow(m_display, root, 0, 0, m_w, m_h, 0, 0, + attr.cursor = createBlankCursor(); + m_window = ::XCreateWindow(m_display, m_root, 0, 0, m_w, m_h, 0, 0, InputOnly, CopyFromParent, CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, &attr); // start watching for events on other windows - selectEvents(root); + selectEvents(m_root); // start processing events m_eventThread = new CThread(new TMethodJob( @@ -179,8 +179,7 @@ void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { // warp the mouse - Window root = RootWindow(m_display, m_screen); - ::XWarpPointer(m_display, None, root, 0, 0, 0, 0, x, y); + ::XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); ::XSync(m_display, False); log((CLOG_DEBUG "warped to %d,%d", x, y)); @@ -233,6 +232,41 @@ void CXWindowsPrimaryScreen::selectEvents(Window w) const } } +Cursor CXWindowsPrimaryScreen::createBlankCursor() +{ + // this seems just a bit more complicated than really necessary + + // get the closet cursor size to 1x1 + unsigned int w, h; + ::XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); + + // make bitmap data for cursor of closet size. since the cursor + // is blank we can use the same bitmap for shape and mask: all + // zeros. + const int size = ((w + 7) >> 3) * h; + char* data = new char[size]; + memset(data, 0, size); + + // make bitmap + Pixmap bitmap = ::XCreateBitmapFromData(m_display, m_root, data, w, h); + + // need an arbitrary color for the cursor + XColor color; + color.pixel = 0; + color.red = color.green = color.blue = 0; + color.flags = DoRed | DoGreen | DoBlue; + + // make cursor from bitmap + Cursor cursor = ::XCreatePixmapCursor(m_display, bitmap, bitmap, + &color, &color, 0, 0); + + // don't need bitmap or the data anymore + delete[] data; + ::XFreePixmap(m_display, bitmap); + + return cursor; +} + void CXWindowsPrimaryScreen::eventThread(void*) { for (;;) { diff --git a/synergy/CXWindowsPrimaryScreen.h b/synergy/CXWindowsPrimaryScreen.h index 14134cf4..8ab278bf 100644 --- a/synergy/CXWindowsPrimaryScreen.h +++ b/synergy/CXWindowsPrimaryScreen.h @@ -24,6 +24,7 @@ class CXWindowsPrimaryScreen : public IPrimaryScreen { private: void selectEvents(Window) const; + Cursor createBlankCursor(); void eventThread(void*); KeyModifierMask mapModifier(unsigned int state) const; @@ -35,6 +36,7 @@ class CXWindowsPrimaryScreen : public IPrimaryScreen { CThread* m_eventThread; Display* m_display; int m_screen; + Window m_root; SInt32 m_w, m_h; Window m_window; bool m_active; diff --git a/synergy/CXWindowsSecondaryScreen.cpp b/synergy/CXWindowsSecondaryScreen.cpp index d7f1cc4e..b2091400 100644 --- a/synergy/CXWindowsSecondaryScreen.cpp +++ b/synergy/CXWindowsSecondaryScreen.cpp @@ -2,6 +2,7 @@ #include "CClient.h" #include "CThread.h" #include "TMethodJob.h" +#include "CLog.h" #include #include #include @@ -33,6 +34,7 @@ void CXWindowsSecondaryScreen::open(CClient* client) m_client = client; // open the display + log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); m_display = ::XOpenDisplay(NULL); // FIXME -- allow non-default if (m_display == NULL) throw int(5); // FIXME -- make exception for this @@ -44,6 +46,7 @@ void CXWindowsSecondaryScreen::open(CClient* client) // get screen size m_w = WidthOfScreen(screen); m_h = HeightOfScreen(screen); + log((CLOG_INFO "secondary display size: %dx%d", m_w, m_h)); // verify the availability of the XTest extension int majorOpcode, firstEvent, firstError; @@ -51,9 +54,30 @@ void CXWindowsSecondaryScreen::open(CClient* client) &majorOpcode, &firstEvent, &firstError)) throw int(6); // FIXME -- make exception for this + // get the root window + m_root = RootWindow(m_display, m_screen); + + // create the cursor hiding window. this window is used to hide the + // cursor when it's not on the screen. the window is hidden as soon + // as the cursor enters the screen or the display's real cursor is + // moved. + XSetWindowAttributes attr; + attr.event_mask = LeaveWindowMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = createBlankCursor(); + m_window = ::XCreateWindow(m_display, m_root, 0, 0, 1, 1, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + // become impervious to server grabs ::XTestGrabControl(m_display, True); + // hide the cursor + leave(); + // start processing events m_eventThread = new CThread(new TMethodJob( this, &CXWindowsSecondaryScreen::eventThread)); @@ -73,6 +97,10 @@ void CXWindowsSecondaryScreen::close() // no longer impervious to server grabs ::XTestGrabControl(m_display, False); + // destroy window + ::XDestroyWindow(m_display, m_window); + m_window = None; + // close the display ::XCloseDisplay(m_display); m_display = NULL; @@ -81,20 +109,25 @@ void CXWindowsSecondaryScreen::close() void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) { assert(m_display != NULL); + assert(m_window != None); // warp to requested location warpCursor(x, y); // show cursor - // FIXME + ::XUnmapWindow(m_display, m_window); } void CXWindowsSecondaryScreen::leave() { assert(m_display != NULL); + assert(m_window != None); - // hide cursor - // FIXME + // raise and show the hider window + ::XMapRaised(m_display, m_window); + + // hide cursor by moving it into the hider window + ::XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, 0, 0); } void CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) @@ -180,8 +213,46 @@ SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const return 0; } +Cursor CXWindowsSecondaryScreen::createBlankCursor() +{ + // this seems just a bit more complicated than really necessary + + // get the closet cursor size to 1x1 + unsigned int w, h; + ::XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); + + // make bitmap data for cursor of closet size. since the cursor + // is blank we can use the same bitmap for shape and mask: all + // zeros. + const int size = ((w + 7) >> 3) * h; + char* data = new char[size]; + memset(data, 0, size); + + // make bitmap + Pixmap bitmap = ::XCreateBitmapFromData(m_display, m_root, data, w, h); + + // need an arbitrary color for the cursor + XColor color; + color.pixel = 0; + color.red = color.green = color.blue = 0; + color.flags = DoRed | DoGreen | DoBlue; + + // make cursor from bitmap + Cursor cursor = ::XCreatePixmapCursor(m_display, bitmap, bitmap, + &color, &color, 0, 0); + + // don't need bitmap or the data anymore + delete[] data; + ::XFreePixmap(m_display, bitmap); + + return cursor; +} + void CXWindowsSecondaryScreen::eventThread(void*) { + assert(m_display != NULL); + assert(m_window != None); + for (;;) { // wait for and then get the next event while (XPending(m_display) == 0) { @@ -193,8 +264,8 @@ void CXWindowsSecondaryScreen::eventThread(void*) // handle event switch (xevent.type) { case LeaveNotify: - // mouse moved out of window somehow. hide the window. -// ::XUnmapWindow(m_display, m_window); + // mouse moved out of hider window somehow. hide the window. + ::XUnmapWindow(m_display, m_window); break; /* diff --git a/synergy/CXWindowsSecondaryScreen.h b/synergy/CXWindowsSecondaryScreen.h index 06cf0abe..e9688016 100644 --- a/synergy/CXWindowsSecondaryScreen.h +++ b/synergy/CXWindowsSecondaryScreen.h @@ -28,6 +28,7 @@ class CXWindowsSecondaryScreen : public ISecondaryScreen { virtual SInt32 getJumpZoneSize() const; private: + Cursor createBlankCursor(); void eventThread(void*); KeyCode mapKey(KeyID, KeyModifierMask) const; unsigned int mapButton(ButtonID button) const; @@ -37,6 +38,7 @@ class CXWindowsSecondaryScreen : public ISecondaryScreen { CThread* m_eventThread; Display* m_display; int m_screen; + Window m_root; Window m_window; SInt32 m_w, m_h; }; From fdb888102be44d95270f015eaa28a297d99da517 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Oct 2001 22:45:59 +0000 Subject: [PATCH 022/807] more notes. --- notes | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/notes b/notes index b0845048..8a470980 100644 --- a/notes +++ b/notes @@ -7,10 +7,15 @@ CServer * must have connection thread in screen info map allows disconnect if screen map changes and screen is removed * put mutex locks wherever necessary (like accessing m_active) + * must handle disconnect of screen by warping cursor off of it CClient * need methods for screen event handler to call as appropriate +threads and X: + X doesn't handle threading very well. need to ensure that all X + calls are protected by a mutex. + server client ------ ------ [accept] <-- connect From 066910fab868fabc52ba707d79a382cc4be0c406 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 24 Oct 2001 22:33:24 +0000 Subject: [PATCH 023/807] made calls to X thread safe. --- synergy/CXWindowsPrimaryScreen.cpp | 43 +++++++++++++++++++++------- synergy/CXWindowsPrimaryScreen.h | 5 ++++ synergy/CXWindowsSecondaryScreen.cpp | 36 +++++++++++++++++++++-- synergy/CXWindowsSecondaryScreen.h | 5 ++++ 4 files changed, 76 insertions(+), 13 deletions(-) diff --git a/synergy/CXWindowsPrimaryScreen.cpp b/synergy/CXWindowsPrimaryScreen.cpp index d464b0f7..0ae0b168 100644 --- a/synergy/CXWindowsPrimaryScreen.cpp +++ b/synergy/CXWindowsPrimaryScreen.cpp @@ -1,6 +1,7 @@ #include "CXWindowsPrimaryScreen.h" #include "CServer.h" #include "CThread.h" +#include "CLock.h" #include "TMethodJob.h" #include "CLog.h" #include @@ -107,6 +108,8 @@ void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) assert(m_window != None); assert(m_active == true); + CLock lock(&m_mutex); + // warp to requested location ::XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, x, y); @@ -135,6 +138,8 @@ void CXWindowsPrimaryScreen::leave() assert(m_window != None); assert(m_active == false); + CLock lock(&m_mutex); + // raise and show the input window ::XMapRaised(m_display, m_window); @@ -169,7 +174,7 @@ void CXWindowsPrimaryScreen::leave() log((CLOG_DEBUG "grabbed keyboard")); // move the mouse to the center of grab window - warpCursor(m_w >> 1, m_h >> 1); + warpCursorNoLock(m_w >> 1, m_h >> 1); // local client now active m_active = true; @@ -177,7 +182,13 @@ void CXWindowsPrimaryScreen::leave() void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { + CLock lock(&m_mutex); + warpCursorNoLock(x, y); +} +void CXWindowsPrimaryScreen::warpCursorNoLock( + SInt32 x, SInt32 y) +{ // warp the mouse ::XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); ::XSync(m_display, False); @@ -271,18 +282,24 @@ void CXWindowsPrimaryScreen::eventThread(void*) { for (;;) { // wait for and then get the next event + m_mutex.lock(); while (XPending(m_display) == 0) { + m_mutex.unlock(); CThread::sleep(0.05); + m_mutex.lock(); } XEvent xevent; XNextEvent(m_display, &xevent); + m_mutex.unlock(); // handle event switch (xevent.type) { - case CreateNotify: + case CreateNotify: { // select events on new window + CLock lock(&m_mutex); selectEvents(xevent.xcreatewindow.window); break; + } case KeyPress: { log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); @@ -337,17 +354,21 @@ void CXWindowsPrimaryScreen::eventThread(void*) // probably not since key strokes may go to wrong place. // get mouse deltas - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int mask; - if (!::XQueryPointer(m_display, m_window, &root, &window, + { + CLock lock(&m_mutex); + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int mask; + if (!::XQueryPointer(m_display, m_window, &root, &window, &xRoot, &yRoot, &xWindow, &yWindow, &mask)) - break; - x = xRoot - (m_w >> 1); - y = yRoot - (m_h >> 1); + break; - // warp mouse back to center - warpCursor(m_w >> 1, m_h >> 1); + x = xRoot - (m_w >> 1); + y = yRoot - (m_h >> 1); + + // warp mouse back to center + warpCursorNoLock(m_w >> 1, m_h >> 1); + } m_server->onMouseMoveSecondary(x, y); } diff --git a/synergy/CXWindowsPrimaryScreen.h b/synergy/CXWindowsPrimaryScreen.h index 8ab278bf..0daa3342 100644 --- a/synergy/CXWindowsPrimaryScreen.h +++ b/synergy/CXWindowsPrimaryScreen.h @@ -1,6 +1,7 @@ #ifndef CXWINDOWSPRIMARYSCREEN_H #define CXWINDOWSPRIMARYSCREEN_H +#include "CMutex.h" #include "KeyTypes.h" #include "MouseTypes.h" #include "IPrimaryScreen.h" @@ -25,6 +26,7 @@ class CXWindowsPrimaryScreen : public IPrimaryScreen { private: void selectEvents(Window) const; Cursor createBlankCursor(); + void warpCursorNoLock(SInt32 xAbsolute, SInt32 yAbsolute); void eventThread(void*); KeyModifierMask mapModifier(unsigned int state) const; @@ -40,6 +42,9 @@ class CXWindowsPrimaryScreen : public IPrimaryScreen { SInt32 m_w, m_h; Window m_window; bool m_active; + + // X is not thread safe + CMutex m_mutex; }; #endif diff --git a/synergy/CXWindowsSecondaryScreen.cpp b/synergy/CXWindowsSecondaryScreen.cpp index b2091400..a1a0d0aa 100644 --- a/synergy/CXWindowsSecondaryScreen.cpp +++ b/synergy/CXWindowsSecondaryScreen.cpp @@ -1,6 +1,7 @@ #include "CXWindowsSecondaryScreen.h" #include "CClient.h" #include "CThread.h" +#include "CLock.h" #include "TMethodJob.h" #include "CLog.h" #include @@ -111,8 +112,10 @@ void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) assert(m_display != NULL); assert(m_window != None); + CLock lock(&m_mutex); + // warp to requested location - warpCursor(x, y); + warpCursorNoLock(x, y); // show cursor ::XUnmapWindow(m_display, m_window); @@ -123,6 +126,8 @@ void CXWindowsSecondaryScreen::leave() assert(m_display != NULL); assert(m_window != None); + CLock lock(&m_mutex); + // raise and show the hider window ::XMapRaised(m_display, m_window); @@ -131,6 +136,13 @@ void CXWindowsSecondaryScreen::leave() } void CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + CLock lock(&m_mutex); + warpCursorNoLock(x, y); +} + +void CXWindowsSecondaryScreen::warpCursorNoLock( + SInt32 x, SInt32 y) { assert(m_display != NULL); @@ -143,6 +155,8 @@ void CXWindowsSecondaryScreen::onKeyDown( { assert(m_display != NULL); + CLock lock(&m_mutex); + ::XTestFakeKeyEvent(m_display, mapKey(key, mask), True, CurrentTime); ::XSync(m_display, False); } @@ -152,6 +166,8 @@ void CXWindowsSecondaryScreen::onKeyRepeat( { assert(m_display != NULL); + CLock lock(&m_mutex); + // FIXME } @@ -160,6 +176,8 @@ void CXWindowsSecondaryScreen::onKeyUp( { assert(m_display != NULL); + CLock lock(&m_mutex); + ::XTestFakeKeyEvent(m_display, mapKey(key, mask), False, CurrentTime); ::XSync(m_display, False); } @@ -168,6 +186,8 @@ void CXWindowsSecondaryScreen::onMouseDown(ButtonID button) { assert(m_display != NULL); + CLock lock(&m_mutex); + ::XTestFakeButtonEvent(m_display, mapButton(button), True, CurrentTime); ::XSync(m_display, False); } @@ -176,6 +196,8 @@ void CXWindowsSecondaryScreen::onMouseUp(ButtonID button) { assert(m_display != NULL); + CLock lock(&m_mutex); + ::XTestFakeButtonEvent(m_display, mapButton(button), False, CurrentTime); ::XSync(m_display, False); } @@ -185,6 +207,8 @@ void CXWindowsSecondaryScreen::onMouseMove( { assert(m_display != NULL); + CLock lock(&m_mutex); + ::XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); ::XSync(m_display, False); } @@ -193,6 +217,8 @@ void CXWindowsSecondaryScreen::onMouseWheel(SInt32) { assert(m_display != NULL); + CLock lock(&m_mutex); + // FIXME } @@ -255,18 +281,24 @@ void CXWindowsSecondaryScreen::eventThread(void*) for (;;) { // wait for and then get the next event + m_mutex.lock(); while (XPending(m_display) == 0) { + m_mutex.unlock(); CThread::sleep(0.05); + m_mutex.lock(); } XEvent xevent; XNextEvent(m_display, &xevent); + m_mutex.unlock(); // handle event switch (xevent.type) { - case LeaveNotify: + case LeaveNotify: { // mouse moved out of hider window somehow. hide the window. + CLock lock(&m_mutex); ::XUnmapWindow(m_display, m_window); break; + } /* // FIXME -- handle screen resolution changes diff --git a/synergy/CXWindowsSecondaryScreen.h b/synergy/CXWindowsSecondaryScreen.h index e9688016..18e79416 100644 --- a/synergy/CXWindowsSecondaryScreen.h +++ b/synergy/CXWindowsSecondaryScreen.h @@ -1,6 +1,7 @@ #ifndef CXWINDOWSSECONDARYSCREEN_H #define CXWINDOWSSECONDARYSCREEN_H +#include "CMutex.h" #include "ISecondaryScreen.h" #include @@ -29,6 +30,7 @@ class CXWindowsSecondaryScreen : public ISecondaryScreen { private: Cursor createBlankCursor(); + void warpCursorNoLock(SInt32 xAbsolute, SInt32 yAbsolute); void eventThread(void*); KeyCode mapKey(KeyID, KeyModifierMask) const; unsigned int mapButton(ButtonID button) const; @@ -41,6 +43,9 @@ class CXWindowsSecondaryScreen : public ISecondaryScreen { Window m_root; Window m_window; SInt32 m_w, m_h; + + // X is not thread safe + CMutex m_mutex; }; #endif From 5b1826a6387cd310d787dc4c1f5de79534fb5b8b Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 24 Oct 2001 23:29:29 +0000 Subject: [PATCH 024/807] now handling disconnect of secondary screen that has the cursor by jumping back to the primary screen (without trying to notify the now disconnected secondary screen). also fixed blown assert in mapPosition(). --- synergy/CServer.cpp | 42 +++++++++++++++++++++++++++++++++++++----- synergy/CServer.h | 1 + 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/synergy/CServer.cpp b/synergy/CServer.cpp index 595f6fde..65488d9a 100644 --- a/synergy/CServer.cpp +++ b/synergy/CServer.cpp @@ -33,7 +33,10 @@ else { wait(0); exit(1); } // CServer // -CServer::CServer() : m_done(&m_mutex, false) +CServer::CServer() : m_done(&m_mutex, false), + m_primary(NULL), + m_active(NULL), + m_primaryInfo(NULL) { m_socketFactory = NULL; m_securityFactory = NULL; @@ -566,16 +569,24 @@ void CServer::mapPosition(CScreenInfo* src, switch (srcSide) { case CScreenMap::kLeft: case CScreenMap::kRight: - assert(y >= 0 && y < src->m_height); - y = static_cast(0.5 + y * + if (y < 0) + y = 0; + else if (y >= src->m_height) + y = src->m_height - 1; + else + y = static_cast(0.5 + y * static_cast(dst->m_height - 1) / (src->m_height - 1)); break; case CScreenMap::kTop: case CScreenMap::kBottom: - assert(x >= 0 && x < src->m_width); - x = static_cast(0.5 + x * + if (x < 0) + x = 0; + else if (x >= src->m_width) + x = src->m_width - 1; + else + x = static_cast(0.5 + x * static_cast(dst->m_width - 1) / (src->m_width - 1)); break; @@ -769,6 +780,7 @@ void CServer::openPrimaryScreen() // add connection m_active = addConnection(CString("primary"/* FIXME */), NULL); + m_primaryInfo = m_active; // update info m_primary->getSize(&m_active->m_width, &m_active->m_height); @@ -854,8 +866,28 @@ void CServer::removeConnection(const CString& name) { log((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); CLock lock(&m_mutex); + + // find screen info CScreenList::iterator index = m_screens.find(name); assert(index != m_screens.end()); + + // if this is active screen then we have to jump off of it + if (m_active == index->second) { + // record new position (center of primary screen) + m_x = m_primaryInfo->m_width >> 1; + m_y = m_primaryInfo->m_height >> 1; + + // don't notify active screen since it probably already disconnected + log((CLOG_NOTE "jump from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y)); + + // cut over + m_active = m_primaryInfo; + + // enter new screen + m_primary->enter(m_x, m_y); + } + + // done with screen info delete index->second; m_screens.erase(index); } diff --git a/synergy/CServer.h b/synergy/CServer.h index 0274b2fe..83fa337f 100644 --- a/synergy/CServer.h +++ b/synergy/CServer.h @@ -149,6 +149,7 @@ class CServer { IPrimaryScreen* m_primary; CScreenList m_screens; CScreenInfo* m_active; + CScreenInfo* m_primaryInfo; SInt32 m_x, m_y; From c5df2edd14a25938c5026313d814a374e67d57e1 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Oct 2001 21:40:29 +0000 Subject: [PATCH 025/807] changed some method names and removed warpCursor() from secondary screen interface. --- synergy/CClient.cpp | 14 ++++++------ synergy/CXWindowsSecondaryScreen.cpp | 33 ++++++++-------------------- synergy/CXWindowsSecondaryScreen.h | 16 ++++++-------- synergy/ISecondaryScreen.h | 21 ++++++++---------- 4 files changed, 32 insertions(+), 52 deletions(-) diff --git a/synergy/CClient.cpp b/synergy/CClient.cpp index 51298a2c..bb4579e7 100644 --- a/synergy/CClient.cpp +++ b/synergy/CClient.cpp @@ -258,7 +258,7 @@ void CClient::onKeyDown() { SInt32 id, mask; CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); - m_screen->onKeyDown(static_cast(id), + m_screen->keyDown(static_cast(id), static_cast(mask)); } @@ -266,7 +266,7 @@ void CClient::onKeyRepeat() { SInt32 id, mask, count; CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); - m_screen->onKeyRepeat(static_cast(id), + m_screen->keyRepeat(static_cast(id), static_cast(mask), count); } @@ -275,7 +275,7 @@ void CClient::onKeyUp() { SInt32 id, mask; CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); - m_screen->onKeyUp(static_cast(id), + m_screen->keyUp(static_cast(id), static_cast(mask)); } @@ -283,28 +283,28 @@ void CClient::onMouseDown() { SInt32 id; CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); - m_screen->onMouseDown(static_cast(id)); + m_screen->mouseDown(static_cast(id)); } void CClient::onMouseUp() { SInt32 id; CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); - m_screen->onMouseUp(static_cast(id)); + m_screen->mouseUp(static_cast(id)); } void CClient::onMouseMove() { SInt32 x, y; CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); - m_screen->onMouseMove(x, y); + m_screen->mouseMove(x, y); } void CClient::onMouseWheel() { SInt32 delta; CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); - m_screen->onMouseWheel(delta); + m_screen->mouseWheel(delta); } diff --git a/synergy/CXWindowsSecondaryScreen.cpp b/synergy/CXWindowsSecondaryScreen.cpp index a1a0d0aa..2a347b30 100644 --- a/synergy/CXWindowsSecondaryScreen.cpp +++ b/synergy/CXWindowsSecondaryScreen.cpp @@ -115,7 +115,8 @@ void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) CLock lock(&m_mutex); // warp to requested location - warpCursorNoLock(x, y); + ::XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); + ::XSync(m_display, False); // show cursor ::XUnmapWindow(m_display, m_window); @@ -135,22 +136,7 @@ void CXWindowsSecondaryScreen::leave() ::XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, 0, 0); } -void CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) -{ - CLock lock(&m_mutex); - warpCursorNoLock(x, y); -} - -void CXWindowsSecondaryScreen::warpCursorNoLock( - SInt32 x, SInt32 y) -{ - assert(m_display != NULL); - - ::XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); - ::XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::onKeyDown( +void CXWindowsSecondaryScreen::keyDown( KeyID key, KeyModifierMask mask) { assert(m_display != NULL); @@ -161,7 +147,7 @@ void CXWindowsSecondaryScreen::onKeyDown( ::XSync(m_display, False); } -void CXWindowsSecondaryScreen::onKeyRepeat( +void CXWindowsSecondaryScreen::keyRepeat( KeyID, KeyModifierMask, SInt32) { assert(m_display != NULL); @@ -171,7 +157,7 @@ void CXWindowsSecondaryScreen::onKeyRepeat( // FIXME } -void CXWindowsSecondaryScreen::onKeyUp( +void CXWindowsSecondaryScreen::keyUp( KeyID key, KeyModifierMask mask) { assert(m_display != NULL); @@ -182,7 +168,7 @@ void CXWindowsSecondaryScreen::onKeyUp( ::XSync(m_display, False); } -void CXWindowsSecondaryScreen::onMouseDown(ButtonID button) +void CXWindowsSecondaryScreen::mouseDown(ButtonID button) { assert(m_display != NULL); @@ -192,7 +178,7 @@ void CXWindowsSecondaryScreen::onMouseDown(ButtonID button) ::XSync(m_display, False); } -void CXWindowsSecondaryScreen::onMouseUp(ButtonID button) +void CXWindowsSecondaryScreen::mouseUp(ButtonID button) { assert(m_display != NULL); @@ -202,8 +188,7 @@ void CXWindowsSecondaryScreen::onMouseUp(ButtonID button) ::XSync(m_display, False); } -void CXWindowsSecondaryScreen::onMouseMove( - SInt32 x, SInt32 y) +void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) { assert(m_display != NULL); @@ -213,7 +198,7 @@ void CXWindowsSecondaryScreen::onMouseMove( ::XSync(m_display, False); } -void CXWindowsSecondaryScreen::onMouseWheel(SInt32) +void CXWindowsSecondaryScreen::mouseWheel(SInt32) { assert(m_display != NULL); diff --git a/synergy/CXWindowsSecondaryScreen.h b/synergy/CXWindowsSecondaryScreen.h index 18e79416..d37efc05 100644 --- a/synergy/CXWindowsSecondaryScreen.h +++ b/synergy/CXWindowsSecondaryScreen.h @@ -17,20 +17,18 @@ class CXWindowsSecondaryScreen : public ISecondaryScreen { virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); virtual void leave(); - virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void onKeyDown(KeyID, KeyModifierMask); - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void onKeyUp(KeyID, KeyModifierMask); - virtual void onMouseDown(ButtonID); - virtual void onMouseUp(ButtonID); - virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void onMouseWheel(SInt32 delta); + virtual void keyDown(KeyID, KeyModifierMask); + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void keyUp(KeyID, KeyModifierMask); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void mouseWheel(SInt32 delta); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; private: Cursor createBlankCursor(); - void warpCursorNoLock(SInt32 xAbsolute, SInt32 yAbsolute); void eventThread(void*); KeyCode mapKey(KeyID, KeyModifierMask) const; unsigned int mapButton(ButtonID button) const; diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 8d24a64c..29db0066 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -30,19 +30,16 @@ class ISecondaryScreen : public IInterface { // up input event simulation and hide the cursor. virtual void leave() = 0; - // warp the cursor to the given position - virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; - // keyboard input simulation - virtual void onKeyDown(KeyID, KeyModifierMask) = 0; - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void onKeyUp(KeyID, KeyModifierMask) = 0; + virtual void keyDown(KeyID, KeyModifierMask) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void keyUp(KeyID, KeyModifierMask) = 0; // mouse input simulation - virtual void onMouseDown(ButtonID) = 0; - virtual void onMouseUp(ButtonID) = 0; - virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; - virtual void onMouseWheel(SInt32 delta) = 0; + virtual void mouseDown(ButtonID) = 0; + virtual void mouseUp(ButtonID) = 0; + virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + virtual void mouseWheel(SInt32 delta) = 0; /* // set the screen's clipboard contents. this is usually called @@ -50,10 +47,10 @@ class ISecondaryScreen : public IInterface { virtual void setClipboard(const IClipboard*) = 0; // show or hide the screen saver - virtual void onScreenSaver(bool show) = 0; + virtual void screenSaver(bool show) = 0; // clipboard input - virtual void onClipboardChanged() = 0; + virtual void clipboardChanged() = 0; */ // accessors From dbdf20e804071ee8d7bf09372b3d8923d5e06d3d Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Oct 2001 22:09:27 +0000 Subject: [PATCH 026/807] changed hider window to move underneath mouse when leaving the screen. this makes it so if the mouse is moved locally, it'll reappear where it was last seen. --- synergy/CXWindowsSecondaryScreen.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/synergy/CXWindowsSecondaryScreen.cpp b/synergy/CXWindowsSecondaryScreen.cpp index 2a347b30..c225616f 100644 --- a/synergy/CXWindowsSecondaryScreen.cpp +++ b/synergy/CXWindowsSecondaryScreen.cpp @@ -129,6 +129,14 @@ void CXWindowsSecondaryScreen::leave() CLock lock(&m_mutex); + // move hider window under the mouse + int x, y, dummy; + unsigned int dummyMask; + Window dummyWindow; + ::XQueryPointer(m_display, m_root, &dummyWindow, &dummyWindow, + &x, &y, &dummy, &dummy, &dummyMask); + ::XMoveWindow(m_display, m_window, x, y); + // raise and show the hider window ::XMapRaised(m_display, m_window); From ef3149cead7aee0f8100e296f0dbeab68279c49a Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Oct 2001 22:17:17 +0000 Subject: [PATCH 027/807] removed unnecessary global scoping operators. --- io/CBufferedInputStream.cpp | 2 +- mt/CCondVar.cpp | 4 +- mt/CMutex.cpp | 8 ++-- mt/CThreadRep.cpp | 10 ++--- net/CNetworkAddress.cpp | 6 +-- net/CSocketInputStream.cpp | 2 +- synergy/CXWindowsPrimaryScreen.cpp | 46 ++++++++++---------- synergy/CXWindowsSecondaryScreen.cpp | 63 ++++++++++++++-------------- 8 files changed, 71 insertions(+), 70 deletions(-) diff --git a/io/CBufferedInputStream.cpp b/io/CBufferedInputStream.cpp index dbcb63f7..740f7dc6 100644 --- a/io/CBufferedInputStream.cpp +++ b/io/CBufferedInputStream.cpp @@ -61,7 +61,7 @@ UInt32 CBufferedInputStream::readNoLock( } if (n > 0) { if (dst != NULL) { - ::memcpy(dst, m_buffer.peek(n), n); + memcpy(dst, m_buffer.peek(n), n); } m_buffer.pop(n); } diff --git a/mt/CCondVar.cpp b/mt/CCondVar.cpp index ca0d8526..f6072058 100644 --- a/mt/CCondVar.cpp +++ b/mt/CCondVar.cpp @@ -262,11 +262,11 @@ bool CCondVarBase::wait( m_mutex->unlock(); // wait for a signal or broadcast - DWORD result = ::WaitForMultipleObjects(n, handles, FALSE, winTimeout); + DWORD result = WaitForMultipleObjects(n, handles, FALSE, winTimeout); // cancel takes priority if (n == 3 && result != WAIT_OBJECT_0 + 2 && - ::WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) + WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) result = WAIT_OBJECT_0 + 2; // update the waiter count and check if we're the last waiter diff --git a/mt/CMutex.cpp b/mt/CMutex.cpp index 311df19a..b9ceb5d5 100644 --- a/mt/CMutex.cpp +++ b/mt/CMutex.cpp @@ -107,25 +107,25 @@ void CMutex::unlock() const void CMutex::init() { CRITICAL_SECTION* mutex = new CRITICAL_SECTION; - ::InitializeCriticalSection(mutex); + InitializeCriticalSection(mutex); m_mutex = reinterpret_cast(mutex); } void CMutex::fini() { CRITICAL_SECTION* mutex = reinterpret_cast(m_mutex); - ::DeleteCriticalSection(mutex); + DeleteCriticalSection(mutex); delete mutex; } void CMutex::lock() const { - ::EnterCriticalSection(reinterpret_cast(m_mutex)); + EnterCriticalSection(reinterpret_cast(m_mutex)); } void CMutex::unlock() const { - ::LeaveCriticalSection(reinterpret_cast(m_mutex)); + LeaveCriticalSection(reinterpret_cast(m_mutex)); } #endif // CONFIG_PLATFORM_WIN32 diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 41380225..dcba9e9f 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -449,7 +449,7 @@ void CThreadRep::sleep(double timeout) if (isCancellable()) WaitForSingleObject(m_cancel, (DWORD)(1000.0 * timeout)); else - ::Sleep((DWORD)(1000.0 * timeout)); + Sleep((DWORD)(1000.0 * timeout)); } void CThreadRep::cancel() @@ -461,7 +461,7 @@ void CThreadRep::cancel() void CThreadRep::testCancel() { // poll cancel event. return if not set. - const DWORD result = ::WaitForSingleObject(getCancelEvent(), 0); + const DWORD result = WaitForSingleObject(getCancelEvent(), 0); if (result != WAIT_OBJECT_0) return; @@ -504,11 +504,11 @@ bool CThreadRep::wait(CThreadRep* target, double timeout) HANDLE handles[2]; handles[0] = target->getExitEvent(); handles[1] = m_cancel; - DWORD result = ::WaitForMultipleObjects(n, handles, FALSE, t); + DWORD result = WaitForMultipleObjects(n, handles, FALSE, t); // cancel takes priority if (n == 2 && result != WAIT_OBJECT_0 + 1 && - ::WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) + WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) result = WAIT_OBJECT_0 + 1; // handle result @@ -563,7 +563,7 @@ unsigned int __stdcall CThreadRep::threadFunc(void* arg) CThreadRep* rep = (CThreadRep*)arg; // initialize OLE - const HRESULT hr = ::OleInitialize(NULL); + const HRESULT hr = OleInitialize(NULL); // run thread rep->doThreadFunc(); diff --git a/net/CNetworkAddress.cpp b/net/CNetworkAddress.cpp index 6d780b2f..84c7f3ec 100644 --- a/net/CNetworkAddress.cpp +++ b/net/CNetworkAddress.cpp @@ -17,7 +17,7 @@ CNetworkAddress::CNetworkAddress(UInt16 port) inetAddress->sin_family = AF_INET; inetAddress->sin_port = htons(port); inetAddress->sin_addr.s_addr = INADDR_ANY; - ::memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); + memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) @@ -44,8 +44,8 @@ CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) struct sockaddr_in* inetAddress = reinterpret_cast(&m_address); inetAddress->sin_family = hent->h_addrtype; inetAddress->sin_port = htons(port); - ::memcpy(&inetAddress->sin_addr, hent->h_addr_list[0], hent->h_length); - ::memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); + memcpy(&inetAddress->sin_addr, hent->h_addr_list[0], hent->h_length); + memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } CNetworkAddress::~CNetworkAddress() diff --git a/net/CSocketInputStream.cpp b/net/CSocketInputStream.cpp index e17e0229..4bbac9a6 100644 --- a/net/CSocketInputStream.cpp +++ b/net/CSocketInputStream.cpp @@ -74,7 +74,7 @@ UInt32 CSocketInputStream::read( n = count; } if (n > 0) { - ::memcpy(dst, m_buffer.peek(n), n); + memcpy(dst, m_buffer.peek(n), n); m_buffer.pop(n); } diff --git a/synergy/CXWindowsPrimaryScreen.cpp b/synergy/CXWindowsPrimaryScreen.cpp index 0ae0b168..0521210a 100644 --- a/synergy/CXWindowsPrimaryScreen.cpp +++ b/synergy/CXWindowsPrimaryScreen.cpp @@ -36,7 +36,7 @@ void CXWindowsPrimaryScreen::open(CServer* server) // open the display log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); - m_display = ::XOpenDisplay(NULL); // FIXME -- allow non-default + m_display = XOpenDisplay(NULL); // FIXME -- allow non-default if (m_display == NULL) throw int(5); // FIXME -- make exception for this @@ -63,7 +63,7 @@ void CXWindowsPrimaryScreen::open(CServer* server) attr.do_not_propagate_mask = 0; attr.override_redirect = True; attr.cursor = createBlankCursor(); - m_window = ::XCreateWindow(m_display, m_root, 0, 0, m_w, m_h, 0, 0, + m_window = XCreateWindow(m_display, m_root, 0, 0, m_w, m_h, 0, 0, InputOnly, CopyFromParent, CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, @@ -92,11 +92,11 @@ void CXWindowsPrimaryScreen::close() log((CLOG_DEBUG "stopped event thread")); // destroy window - ::XDestroyWindow(m_display, m_window); + XDestroyWindow(m_display, m_window); m_window = None; // close the display - ::XCloseDisplay(m_display); + XCloseDisplay(m_display); m_display = NULL; log((CLOG_DEBUG "closed display")); } @@ -111,14 +111,14 @@ void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) CLock lock(&m_mutex); // warp to requested location - ::XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, x, y); + XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, x, y); // unmap the grab window. this also ungrabs the mouse and keyboard. - ::XUnmapWindow(m_display, m_window); + XUnmapWindow(m_display, m_window); // remove all input events for grab window XEvent event; - while (::XCheckWindowEvent(m_display, m_window, + while (XCheckWindowEvent(m_display, m_window, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | @@ -141,7 +141,7 @@ void CXWindowsPrimaryScreen::leave() CLock lock(&m_mutex); // raise and show the input window - ::XMapRaised(m_display, m_window); + XMapRaised(m_display, m_window); // grab the mouse and keyboard. keep trying until we get them. // if we can't grab one after grabbing the other then ungrab @@ -150,7 +150,7 @@ void CXWindowsPrimaryScreen::leave() do { // mouse first do { - result = ::XGrabPointer(m_display, m_window, True, 0, + result = XGrabPointer(m_display, m_window, True, 0, GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime); assert(result != GrabNotViewable); @@ -162,11 +162,11 @@ void CXWindowsPrimaryScreen::leave() log((CLOG_DEBUG "grabbed pointer")); // now the keyboard - result = ::XGrabKeyboard(m_display, m_window, True, + result = XGrabKeyboard(m_display, m_window, True, GrabModeAsync, GrabModeAsync, CurrentTime); assert(result != GrabNotViewable); if (result != GrabSuccess) { - ::XUngrabPointer(m_display, CurrentTime); + XUngrabPointer(m_display, CurrentTime); log((CLOG_DEBUG "ungrabbed pointer, waiting to grab keyboard")); CThread::sleep(0.25); } @@ -190,13 +190,13 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( SInt32 x, SInt32 y) { // warp the mouse - ::XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); - ::XSync(m_display, False); + XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); + XSync(m_display, False); log((CLOG_DEBUG "warped to %d,%d", x, y)); // discard mouse events since we just added one we don't want XEvent xevent; - while (::XCheckWindowEvent(m_display, m_window, + while (XCheckWindowEvent(m_display, m_window, PointerMotionMask, &xevent)) { // do nothing } @@ -231,15 +231,15 @@ void CXWindowsPrimaryScreen::selectEvents(Window w) const return; // select events of interest - ::XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask); + XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask); // recurse on child windows Window rw, pw, *cw; unsigned int nc; - if (::XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) { + if (XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) { for (unsigned int i = 0; i < nc; ++i) selectEvents(cw[i]); - ::XFree(cw); + XFree(cw); } } @@ -249,7 +249,7 @@ Cursor CXWindowsPrimaryScreen::createBlankCursor() // get the closet cursor size to 1x1 unsigned int w, h; - ::XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); + XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); // make bitmap data for cursor of closet size. since the cursor // is blank we can use the same bitmap for shape and mask: all @@ -259,7 +259,7 @@ Cursor CXWindowsPrimaryScreen::createBlankCursor() memset(data, 0, size); // make bitmap - Pixmap bitmap = ::XCreateBitmapFromData(m_display, m_root, data, w, h); + Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h); // need an arbitrary color for the cursor XColor color; @@ -268,12 +268,12 @@ Cursor CXWindowsPrimaryScreen::createBlankCursor() color.flags = DoRed | DoGreen | DoBlue; // make cursor from bitmap - Cursor cursor = ::XCreatePixmapCursor(m_display, bitmap, bitmap, + Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, &color, &color, 0, 0); // don't need bitmap or the data anymore delete[] data; - ::XFreePixmap(m_display, bitmap); + XFreePixmap(m_display, bitmap); return cursor; } @@ -359,7 +359,7 @@ void CXWindowsPrimaryScreen::eventThread(void*) Window root, window; int xRoot, yRoot, xWindow, yWindow; unsigned int mask; - if (!::XQueryPointer(m_display, m_window, &root, &window, + if (!XQueryPointer(m_display, m_window, &root, &window, &xRoot, &yRoot, &xWindow, &yWindow, &mask)) break; @@ -422,7 +422,7 @@ KeyID CXWindowsPrimaryScreen::mapKey( index = 1; else index = 0; - return static_cast(::XKeycodeToKeysym(m_display, keycode, index)); + return static_cast(XKeycodeToKeysym(m_display, keycode, index)); } ButtonID CXWindowsPrimaryScreen::mapButton( diff --git a/synergy/CXWindowsSecondaryScreen.cpp b/synergy/CXWindowsSecondaryScreen.cpp index c225616f..62934590 100644 --- a/synergy/CXWindowsSecondaryScreen.cpp +++ b/synergy/CXWindowsSecondaryScreen.cpp @@ -36,7 +36,7 @@ void CXWindowsSecondaryScreen::open(CClient* client) // open the display log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); - m_display = ::XOpenDisplay(NULL); // FIXME -- allow non-default + m_display = XOpenDisplay(NULL); // FIXME -- allow non-default if (m_display == NULL) throw int(5); // FIXME -- make exception for this @@ -51,7 +51,7 @@ void CXWindowsSecondaryScreen::open(CClient* client) // verify the availability of the XTest extension int majorOpcode, firstEvent, firstError; - if (!::XQueryExtension(m_display, XTestExtensionName, + if (!XQueryExtension(m_display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) throw int(6); // FIXME -- make exception for this @@ -67,14 +67,14 @@ void CXWindowsSecondaryScreen::open(CClient* client) attr.do_not_propagate_mask = 0; attr.override_redirect = True; attr.cursor = createBlankCursor(); - m_window = ::XCreateWindow(m_display, m_root, 0, 0, 1, 1, 0, 0, + m_window = XCreateWindow(m_display, m_root, 0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent, CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, &attr); // become impervious to server grabs - ::XTestGrabControl(m_display, True); + XTestGrabControl(m_display, True); // hide the cursor leave(); @@ -96,14 +96,14 @@ void CXWindowsSecondaryScreen::close() m_eventThread = NULL; // no longer impervious to server grabs - ::XTestGrabControl(m_display, False); + XTestGrabControl(m_display, False); // destroy window - ::XDestroyWindow(m_display, m_window); + XDestroyWindow(m_display, m_window); m_window = None; // close the display - ::XCloseDisplay(m_display); + XCloseDisplay(m_display); m_display = NULL; } @@ -115,11 +115,11 @@ void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) CLock lock(&m_mutex); // warp to requested location - ::XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); - ::XSync(m_display, False); + XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); + XSync(m_display, False); // show cursor - ::XUnmapWindow(m_display, m_window); + XUnmapWindow(m_display, m_window); } void CXWindowsSecondaryScreen::leave() @@ -129,19 +129,20 @@ void CXWindowsSecondaryScreen::leave() CLock lock(&m_mutex); - // move hider window under the mouse + // move hider window under the mouse (rather than moving the mouse + // somewhere else on the screen) int x, y, dummy; unsigned int dummyMask; Window dummyWindow; - ::XQueryPointer(m_display, m_root, &dummyWindow, &dummyWindow, + XQueryPointer(m_display, m_root, &dummyWindow, &dummyWindow, &x, &y, &dummy, &dummy, &dummyMask); - ::XMoveWindow(m_display, m_window, x, y); + XMoveWindow(m_display, m_window, x, y); // raise and show the hider window - ::XMapRaised(m_display, m_window); + XMapRaised(m_display, m_window); // hide cursor by moving it into the hider window - ::XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, 0, 0); + XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, 0, 0); } void CXWindowsSecondaryScreen::keyDown( @@ -151,8 +152,8 @@ void CXWindowsSecondaryScreen::keyDown( CLock lock(&m_mutex); - ::XTestFakeKeyEvent(m_display, mapKey(key, mask), True, CurrentTime); - ::XSync(m_display, False); + XTestFakeKeyEvent(m_display, mapKey(key, mask), True, CurrentTime); + XSync(m_display, False); } void CXWindowsSecondaryScreen::keyRepeat( @@ -172,8 +173,8 @@ void CXWindowsSecondaryScreen::keyUp( CLock lock(&m_mutex); - ::XTestFakeKeyEvent(m_display, mapKey(key, mask), False, CurrentTime); - ::XSync(m_display, False); + XTestFakeKeyEvent(m_display, mapKey(key, mask), False, CurrentTime); + XSync(m_display, False); } void CXWindowsSecondaryScreen::mouseDown(ButtonID button) @@ -182,8 +183,8 @@ void CXWindowsSecondaryScreen::mouseDown(ButtonID button) CLock lock(&m_mutex); - ::XTestFakeButtonEvent(m_display, mapButton(button), True, CurrentTime); - ::XSync(m_display, False); + XTestFakeButtonEvent(m_display, mapButton(button), True, CurrentTime); + XSync(m_display, False); } void CXWindowsSecondaryScreen::mouseUp(ButtonID button) @@ -192,8 +193,8 @@ void CXWindowsSecondaryScreen::mouseUp(ButtonID button) CLock lock(&m_mutex); - ::XTestFakeButtonEvent(m_display, mapButton(button), False, CurrentTime); - ::XSync(m_display, False); + XTestFakeButtonEvent(m_display, mapButton(button), False, CurrentTime); + XSync(m_display, False); } void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) @@ -202,8 +203,8 @@ void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) CLock lock(&m_mutex); - ::XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); - ::XSync(m_display, False); + XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); + XSync(m_display, False); } void CXWindowsSecondaryScreen::mouseWheel(SInt32) @@ -238,7 +239,7 @@ Cursor CXWindowsSecondaryScreen::createBlankCursor() // get the closet cursor size to 1x1 unsigned int w, h; - ::XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); + XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); // make bitmap data for cursor of closet size. since the cursor // is blank we can use the same bitmap for shape and mask: all @@ -248,7 +249,7 @@ Cursor CXWindowsSecondaryScreen::createBlankCursor() memset(data, 0, size); // make bitmap - Pixmap bitmap = ::XCreateBitmapFromData(m_display, m_root, data, w, h); + Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h); // need an arbitrary color for the cursor XColor color; @@ -257,12 +258,12 @@ Cursor CXWindowsSecondaryScreen::createBlankCursor() color.flags = DoRed | DoGreen | DoBlue; // make cursor from bitmap - Cursor cursor = ::XCreatePixmapCursor(m_display, bitmap, bitmap, + Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, &color, &color, 0, 0); // don't need bitmap or the data anymore delete[] data; - ::XFreePixmap(m_display, bitmap); + XFreePixmap(m_display, bitmap); return cursor; } @@ -289,7 +290,7 @@ void CXWindowsSecondaryScreen::eventThread(void*) case LeaveNotify: { // mouse moved out of hider window somehow. hide the window. CLock lock(&m_mutex); - ::XUnmapWindow(m_display, m_window); + XUnmapWindow(m_display, m_window); break; } @@ -316,7 +317,7 @@ KeyCode CXWindowsSecondaryScreen::mapKey( KeyID id, KeyModifierMask /*mask*/) const { // FIXME -- use mask - return ::XKeysymToKeycode(m_display, static_cast(id)); + return XKeysymToKeycode(m_display, static_cast(id)); } unsigned int CXWindowsSecondaryScreen::mapButton( From 836d9d2d33503d4493662b4ae9a387dbdbac6779 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 10 Nov 2001 22:28:30 +0000 Subject: [PATCH 028/807] added main app directory to build. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 8c3dfeb5..06e4943d 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ SUBDIRS = \ mt \ io \ net \ + synergy \ $(NULL) # # targets From 05928f28f81885f1d0400d8eeb242d1300464e85 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 10 Nov 2001 22:28:37 +0000 Subject: [PATCH 029/807] updated notes. --- notes | 9 --------- 1 file changed, 9 deletions(-) diff --git a/notes b/notes index 8a470980..b6340818 100644 --- a/notes +++ b/notes @@ -1,8 +1,3 @@ -throw specs: - must add XThread if method can be cancelled - must add out-of-memory exception if method allocs and returns an object - should catch and handle out-of-mem if object wasn't going to be returned - CServer * must have connection thread in screen info map allows disconnect if screen map changes and screen is removed @@ -12,10 +7,6 @@ CServer CClient * need methods for screen event handler to call as appropriate -threads and X: - X doesn't handle threading very well. need to ensure that all X - calls are protected by a mutex. - server client ------ ------ [accept] <-- connect From cf4e1fd9caf9ff7bdf81abb912bc06233aee2b3c Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Nov 2001 21:15:30 +0000 Subject: [PATCH 030/807] factored common X windows screen stuff into a common base class. --- synergy/CXWindowsPrimaryScreen.cpp | 244 +++++++----------- synergy/CXWindowsPrimaryScreen.h | 32 +-- synergy/CXWindowsScreen.cpp | 172 +++++++++++++ synergy/CXWindowsScreen.h | 73 ++++++ synergy/CXWindowsSecondaryScreen.cpp | 357 +++++++++++---------------- synergy/CXWindowsSecondaryScreen.h | 24 +- synergy/Makefile | 1 + 7 files changed, 502 insertions(+), 401 deletions(-) create mode 100644 synergy/CXWindowsScreen.cpp create mode 100644 synergy/CXWindowsScreen.h diff --git a/synergy/CXWindowsPrimaryScreen.cpp b/synergy/CXWindowsPrimaryScreen.cpp index 0521210a..39595354 100644 --- a/synergy/CXWindowsPrimaryScreen.cpp +++ b/synergy/CXWindowsPrimaryScreen.cpp @@ -1,8 +1,6 @@ #include "CXWindowsPrimaryScreen.h" #include "CServer.h" #include "CThread.h" -#include "CLock.h" -#include "TMethodJob.h" #include "CLog.h" #include #include @@ -13,17 +11,15 @@ CXWindowsPrimaryScreen::CXWindowsPrimaryScreen() : m_server(NULL), - m_display(NULL), - m_w(0), m_h(0), - m_window(None), - m_active(false) + m_active(false), + m_window(None) { // do nothing } CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() { - assert(m_display == NULL); + assert(m_window == None); } void CXWindowsPrimaryScreen::open(CServer* server) @@ -35,90 +31,37 @@ void CXWindowsPrimaryScreen::open(CServer* server) m_server = server; // open the display - log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); - m_display = XOpenDisplay(NULL); // FIXME -- allow non-default - if (m_display == NULL) - throw int(5); // FIXME -- make exception for this - - // get default screen - m_screen = DefaultScreen(m_display); - Screen* screen = ScreenOfDisplay(m_display, m_screen); - - // get screen size - m_w = WidthOfScreen(screen); - m_h = HeightOfScreen(screen); - log((CLOG_INFO "primary display size: %dx%d", m_w, m_h)); - - // get the root window - m_root = RootWindow(m_display, m_screen); - - // create the grab window. this window is used to capture user - // input when the user is focussed on another client. don't let - // the window manager mess with it. - XSetWindowAttributes attr; - attr.event_mask = PointerMotionMask |// PointerMotionHintMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = createBlankCursor(); - m_window = XCreateWindow(m_display, m_root, 0, 0, m_w, m_h, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - - // start watching for events on other windows - selectEvents(m_root); - - // start processing events - m_eventThread = new CThread(new TMethodJob( - this, &CXWindowsPrimaryScreen::eventThread)); + openDisplay(); } void CXWindowsPrimaryScreen::close() { assert(m_server != NULL); - assert(m_window != None); - assert(m_eventThread != NULL); - - // stop event thread - log((CLOG_DEBUG "stopping event thread")); - m_eventThread->cancel(); - m_eventThread->wait(); - delete m_eventThread; - m_eventThread = NULL; - log((CLOG_DEBUG "stopped event thread")); - - // destroy window - XDestroyWindow(m_display, m_window); - m_window = None; // close the display - XCloseDisplay(m_display); - m_display = NULL; - log((CLOG_DEBUG "closed display")); + closeDisplay(); + + // done with server + m_server = NULL; } void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) { log((CLOG_INFO "entering primary at %d,%d", x, y)); - assert(m_display != NULL); - assert(m_window != None); - assert(m_active == true); + assert(m_active == true); + assert(m_window != None); - CLock lock(&m_mutex); + CDisplayLock display(this); // warp to requested location - XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, x, y); + XWarpPointer(display, None, m_window, 0, 0, 0, 0, x, y); // unmap the grab window. this also ungrabs the mouse and keyboard. - XUnmapWindow(m_display, m_window); + XUnmapWindow(display, m_window); // remove all input events for grab window XEvent event; - while (XCheckWindowEvent(m_display, m_window, + while (XCheckWindowEvent(display, m_window, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | @@ -134,14 +77,13 @@ void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) void CXWindowsPrimaryScreen::leave() { log((CLOG_INFO "leaving primary")); - assert(m_display != NULL); - assert(m_window != None); - assert(m_active == false); + assert(m_active == false); + assert(m_window != None); - CLock lock(&m_mutex); + CDisplayLock display(this); // raise and show the input window - XMapRaised(m_display, m_window); + XMapRaised(display, m_window); // grab the mouse and keyboard. keep trying until we get them. // if we can't grab one after grabbing the other then ungrab @@ -150,7 +92,7 @@ void CXWindowsPrimaryScreen::leave() do { // mouse first do { - result = XGrabPointer(m_display, m_window, True, 0, + result = XGrabPointer(display, m_window, True, 0, GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime); assert(result != GrabNotViewable); @@ -162,11 +104,12 @@ void CXWindowsPrimaryScreen::leave() log((CLOG_DEBUG "grabbed pointer")); // now the keyboard - result = XGrabKeyboard(m_display, m_window, True, + result = XGrabKeyboard(display, m_window, True, GrabModeAsync, GrabModeAsync, CurrentTime); assert(result != GrabNotViewable); if (result != GrabSuccess) { - XUngrabPointer(m_display, CurrentTime); + // back off to avoid grab deadlock + XUngrabPointer(display, CurrentTime); log((CLOG_DEBUG "ungrabbed pointer, waiting to grab keyboard")); CThread::sleep(0.25); } @@ -174,7 +117,9 @@ void CXWindowsPrimaryScreen::leave() log((CLOG_DEBUG "grabbed keyboard")); // move the mouse to the center of grab window - warpCursorNoLock(m_w >> 1, m_h >> 1); + SInt32 w, h; + getScreenSize(&w, &h); + warpCursorNoLock(display, w >> 1, h >> 1); // local client now active m_active = true; @@ -182,21 +127,24 @@ void CXWindowsPrimaryScreen::leave() void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { - CLock lock(&m_mutex); - warpCursorNoLock(x, y); + CDisplayLock display(this); + warpCursorNoLock(display, x, y); } void CXWindowsPrimaryScreen::warpCursorNoLock( - SInt32 x, SInt32 y) + Display* display, SInt32 x, SInt32 y) { + assert(display != NULL); + assert(m_window != None); + // warp the mouse - XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); - XSync(m_display, False); + XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); + XSync(display, False); log((CLOG_DEBUG "warped to %d,%d", x, y)); // discard mouse events since we just added one we don't want XEvent xevent; - while (XCheckWindowEvent(m_display, m_window, + while (XCheckWindowEvent(display, m_window, PointerMotionMask, &xevent)) { // do nothing } @@ -205,21 +153,57 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( void CXWindowsPrimaryScreen::getSize( SInt32* width, SInt32* height) const { - assert(m_display != NULL); - assert(width != NULL && height != NULL); - - *width = m_w; - *height = m_h; + getScreenSize(width, height); } SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const { - assert(m_display != NULL); - return 1; } -void CXWindowsPrimaryScreen::selectEvents(Window w) const +void CXWindowsPrimaryScreen::onOpenDisplay() +{ + assert(m_window == None); + + CDisplayLock display(this); + + // get size of screen + SInt32 w, h; + getScreenSize(&w, &h); + + // create the grab window. this window is used to capture user + // input when the user is focussed on another client. don't let + // the window manager mess with it. + XSetWindowAttributes attr; + attr.event_mask = PointerMotionMask |// PointerMotionHintMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = createBlankCursor(); + m_window = XCreateWindow(display, getRoot(), 0, 0, w, h, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + + // start watching for events on other windows + selectEvents(display, getRoot()); +} + +void CXWindowsPrimaryScreen::onCloseDisplay() +{ + assert(m_window != None); + + // destroy window + CDisplayLock display(this); + XDestroyWindow(display, m_window); + m_window = None; +} + +void CXWindowsPrimaryScreen::selectEvents( + Display* display, Window w) const { // we want to track the mouse everywhere on the display. to achieve // that we select PointerMotionMask on every window. we also select @@ -231,73 +215,31 @@ void CXWindowsPrimaryScreen::selectEvents(Window w) const return; // select events of interest - XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask); + XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask); // recurse on child windows Window rw, pw, *cw; unsigned int nc; - if (XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) { + if (XQueryTree(display, w, &rw, &pw, &cw, &nc)) { for (unsigned int i = 0; i < nc; ++i) - selectEvents(cw[i]); + selectEvents(display, cw[i]); XFree(cw); } } -Cursor CXWindowsPrimaryScreen::createBlankCursor() -{ - // this seems just a bit more complicated than really necessary - - // get the closet cursor size to 1x1 - unsigned int w, h; - XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); - - // make bitmap data for cursor of closet size. since the cursor - // is blank we can use the same bitmap for shape and mask: all - // zeros. - const int size = ((w + 7) >> 3) * h; - char* data = new char[size]; - memset(data, 0, size); - - // make bitmap - Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h); - - // need an arbitrary color for the cursor - XColor color; - color.pixel = 0; - color.red = color.green = color.blue = 0; - color.flags = DoRed | DoGreen | DoBlue; - - // make cursor from bitmap - Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, - &color, &color, 0, 0); - - // don't need bitmap or the data anymore - delete[] data; - XFreePixmap(m_display, bitmap); - - return cursor; -} - void CXWindowsPrimaryScreen::eventThread(void*) { for (;;) { - // wait for and then get the next event - m_mutex.lock(); - while (XPending(m_display) == 0) { - m_mutex.unlock(); - CThread::sleep(0.05); - m_mutex.lock(); - } + // wait for and get the next event XEvent xevent; - XNextEvent(m_display, &xevent); - m_mutex.unlock(); + getEvent(&xevent); // handle event switch (xevent.type) { case CreateNotify: { // select events on new window - CLock lock(&m_mutex); - selectEvents(xevent.xcreatewindow.window); + CDisplayLock display(this); + selectEvents(display, xevent.xcreatewindow.window); break; } @@ -355,19 +297,22 @@ void CXWindowsPrimaryScreen::eventThread(void*) // get mouse deltas { - CLock lock(&m_mutex); + CDisplayLock display(this); Window root, window; int xRoot, yRoot, xWindow, yWindow; unsigned int mask; - if (!XQueryPointer(m_display, m_window, &root, &window, + if (!XQueryPointer(display, m_window, &root, &window, &xRoot, &yRoot, &xWindow, &yWindow, &mask)) break; - x = xRoot - (m_w >> 1); - y = yRoot - (m_h >> 1); + // compute position of center of window + SInt32 w, h; + getScreenSize(&w, &h); + x = xRoot - (w >> 1); + y = yRoot - (h >> 1); // warp mouse back to center - warpCursorNoLock(m_w >> 1, m_h >> 1); + warpCursorNoLock(display, w >> 1, h >> 1); } m_server->onMouseMoveSecondary(x, y); @@ -422,7 +367,8 @@ KeyID CXWindowsPrimaryScreen::mapKey( index = 1; else index = 0; - return static_cast(XKeycodeToKeysym(m_display, keycode, index)); + CDisplayLock display(this); + return static_cast(XKeycodeToKeysym(display, keycode, index)); } ButtonID CXWindowsPrimaryScreen::mapButton( diff --git a/synergy/CXWindowsPrimaryScreen.h b/synergy/CXWindowsPrimaryScreen.h index 0daa3342..fd8883d8 100644 --- a/synergy/CXWindowsPrimaryScreen.h +++ b/synergy/CXWindowsPrimaryScreen.h @@ -1,15 +1,12 @@ #ifndef CXWINDOWSPRIMARYSCREEN_H #define CXWINDOWSPRIMARYSCREEN_H -#include "CMutex.h" #include "KeyTypes.h" #include "MouseTypes.h" +#include "CXWindowsScreen.h" #include "IPrimaryScreen.h" -#include -class CThread; - -class CXWindowsPrimaryScreen : public IPrimaryScreen { +class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { public: CXWindowsPrimaryScreen(); virtual ~CXWindowsPrimaryScreen(); @@ -23,28 +20,25 @@ class CXWindowsPrimaryScreen : public IPrimaryScreen { virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; - private: - void selectEvents(Window) const; - Cursor createBlankCursor(); - void warpCursorNoLock(SInt32 xAbsolute, SInt32 yAbsolute); + protected: + // CXWindowsScreen overrides + virtual void onOpenDisplay(); + virtual void onCloseDisplay(); + virtual void eventThread(void*); + + private: + void selectEvents(Display*, Window) const; + void warpCursorNoLock(Display*, + SInt32 xAbsolute, SInt32 yAbsolute); - void eventThread(void*); KeyModifierMask mapModifier(unsigned int state) const; KeyID mapKey(KeyCode, KeyModifierMask) const; ButtonID mapButton(unsigned int button) const; private: CServer* m_server; - CThread* m_eventThread; - Display* m_display; - int m_screen; - Window m_root; - SInt32 m_w, m_h; - Window m_window; bool m_active; - - // X is not thread safe - CMutex m_mutex; + Window m_window; }; #endif diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp new file mode 100644 index 00000000..1ec8413f --- /dev/null +++ b/synergy/CXWindowsScreen.cpp @@ -0,0 +1,172 @@ +#include "CXWindowsScreen.h" +#include "CThread.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CLog.h" +#include +#include +#include + +// +// CXWindowsScreen +// + +CXWindowsScreen::CXWindowsScreen() : + m_display(NULL), + m_root(None), + m_w(0), m_h(0) +{ + // do nothing +} + +CXWindowsScreen::~CXWindowsScreen() +{ + assert(m_display == NULL); +} + +void CXWindowsScreen::openDisplay() +{ + assert(m_display == NULL); + + // open the display + log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); + m_display = XOpenDisplay(NULL); // FIXME -- allow non-default + if (m_display == NULL) + throw int(5); // FIXME -- make exception for this + + // get default screen + m_screen = DefaultScreen(m_display); + Screen* screen = ScreenOfDisplay(m_display, m_screen); + + // get screen size + m_w = WidthOfScreen(screen); + m_h = HeightOfScreen(screen); + log((CLOG_INFO "display size: %dx%d", m_w, m_h)); + + // get the root window + m_root = RootWindow(m_display, m_screen); + + // let subclass prep display + onOpenDisplay(); + + // start processing events + m_eventThread = new CThread(new TMethodJob( + this, &CXWindowsScreen::eventThread)); +} + +void CXWindowsScreen::closeDisplay() +{ + assert(m_display != NULL); + assert(m_eventThread != NULL); + + // stop event thread + log((CLOG_DEBUG "stopping event thread")); + m_eventThread->cancel(); + m_eventThread->wait(); + delete m_eventThread; + m_eventThread = NULL; + log((CLOG_DEBUG "stopped event thread")); + + // let subclass close down display + onCloseDisplay(); + + // close the display + XCloseDisplay(m_display); + m_display = NULL; + log((CLOG_DEBUG "closed display")); +} + +int CXWindowsScreen::getScreen() const +{ + assert(m_display != NULL); + return m_screen; +} + +Window CXWindowsScreen::getRoot() const +{ + assert(m_display != NULL); + return m_root; +} + +void CXWindowsScreen::getScreenSize( + SInt32* w, SInt32* h) const +{ + assert(m_display != NULL); + assert(w != NULL && h != NULL); + + *w = m_w; + *h = m_h; +} + +Cursor CXWindowsScreen::createBlankCursor() const +{ + // this seems just a bit more complicated than really necessary + + // get the closet cursor size to 1x1 + unsigned int w, h; + XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); + + // make bitmap data for cursor of closet size. since the cursor + // is blank we can use the same bitmap for shape and mask: all + // zeros. + const int size = ((w + 7) >> 3) * h; + char* data = new char[size]; + memset(data, 0, size); + + // make bitmap + Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h); + + // need an arbitrary color for the cursor + XColor color; + color.pixel = 0; + color.red = color.green = color.blue = 0; + color.flags = DoRed | DoGreen | DoBlue; + + // make cursor from bitmap + Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, + &color, &color, 0, 0); + + // don't need bitmap or the data anymore + delete[] data; + XFreePixmap(m_display, bitmap); + + return cursor; +} + +void CXWindowsScreen::getEvent(XEvent* xevent) const +{ + // wait for an event in a cancellable way and don't lock the + // display while we're waiting. + m_mutex.lock(); + while (XPending(m_display) == 0) { + m_mutex.unlock(); + CThread::sleep(0.05); + m_mutex.lock(); + } + XNextEvent(m_display, xevent); + m_mutex.unlock(); +} + + +// +// CXWindowsScreen::CDisplayLock +// + +CXWindowsScreen::CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : + m_mutex(&screen->m_mutex), + m_display(screen->m_display) +{ + assert(m_display != NULL); + + m_mutex->lock(); +} + +CXWindowsScreen::CDisplayLock::~CDisplayLock() +{ + m_mutex->unlock(); +} + +CXWindowsScreen::CDisplayLock::operator Display*() const +{ + return m_display; +} diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h new file mode 100644 index 00000000..6ad39859 --- /dev/null +++ b/synergy/CXWindowsScreen.h @@ -0,0 +1,73 @@ +#ifndef CXWINDOWSSCREEN_H +#define CXWINDOWSSCREEN_H + +#include "CMutex.h" +#include "BasicTypes.h" +#include + +class CThread; + +class CXWindowsScreen { + public: + CXWindowsScreen(); + virtual ~CXWindowsScreen(); + + protected: + class CDisplayLock { + public: + CDisplayLock(const CXWindowsScreen*); + ~CDisplayLock(); + + operator Display*() const; + + private: + const CMutex* m_mutex; + Display* m_display; + }; + friend class CDisplayLock; + + // open the X display. calls onOpenDisplay() after opening the display, + // getting the screen, its size, and root window. then it starts the + // event thread. + void openDisplay(); + + // destroy the window and close the display. calls onCloseDisplay() + // after the event thread has been shut down but before the display + // is closed. + void closeDisplay(); + + // get the opened screen, its size, its root window. to get the + // display create a CDisplayLock object passing this. while the + // object exists no other threads may access the display. do not + // save the Display* beyond the lifetime of the CDisplayLock. + int getScreen() const; + void getScreenSize(SInt32* w, SInt32* h) const; + Window getRoot() const; + + // create a cursor that is transparent everywhere + Cursor createBlankCursor() const; + + // wait for and get the next X event. cancellable. + void getEvent(XEvent*) const; + + // called by openDisplay() to allow subclasses to prepare the display + virtual void onOpenDisplay() = 0; + + // called by closeDisplay() to + virtual void onCloseDisplay() = 0; + + // override to process X events + virtual void eventThread(void*) = 0; + + private: + CThread* m_eventThread; + Display* m_display; + int m_screen; + Window m_root; + SInt32 m_w, m_h; + + // X is not thread safe + CMutex m_mutex; +}; + +#endif diff --git a/synergy/CXWindowsSecondaryScreen.cpp b/synergy/CXWindowsSecondaryScreen.cpp index 62934590..853855a6 100644 --- a/synergy/CXWindowsSecondaryScreen.cpp +++ b/synergy/CXWindowsSecondaryScreen.cpp @@ -1,8 +1,6 @@ #include "CXWindowsSecondaryScreen.h" #include "CClient.h" #include "CThread.h" -#include "CLock.h" -#include "TMethodJob.h" #include "CLog.h" #include #include @@ -14,16 +12,14 @@ CXWindowsSecondaryScreen::CXWindowsSecondaryScreen() : m_client(NULL), - m_display(NULL), - m_window(None), - m_w(0), m_h(0) + m_window(None) { // do nothing } CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() { - assert(m_display == NULL); + assert(m_window == None); } void CXWindowsSecondaryScreen::open(CClient* client) @@ -35,28 +31,113 @@ void CXWindowsSecondaryScreen::open(CClient* client) m_client = client; // open the display - log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); - m_display = XOpenDisplay(NULL); // FIXME -- allow non-default - if (m_display == NULL) - throw int(5); // FIXME -- make exception for this - - // get default screen - m_screen = DefaultScreen(m_display); - Screen* screen = ScreenOfDisplay(m_display, m_screen); - - // get screen size - m_w = WidthOfScreen(screen); - m_h = HeightOfScreen(screen); - log((CLOG_INFO "secondary display size: %dx%d", m_w, m_h)); + openDisplay(); // verify the availability of the XTest extension + CDisplayLock display(this); int majorOpcode, firstEvent, firstError; - if (!XQueryExtension(m_display, XTestExtensionName, + if (!XQueryExtension(display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) throw int(6); // FIXME -- make exception for this +} - // get the root window - m_root = RootWindow(m_display, m_screen); +void CXWindowsSecondaryScreen::close() +{ + assert(m_client != NULL); + + // close the display + closeDisplay(); + + // done with client + m_client = NULL; +} + +void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) +{ + assert(m_window != None); + + CDisplayLock display(this); + + // warp to requested location + XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime); + XSync(display, False); + + // show cursor + XUnmapWindow(display, m_window); +} + +void CXWindowsSecondaryScreen::leave() +{ + CDisplayLock display(this); + leaveNoLock(display); +} + +void CXWindowsSecondaryScreen::keyDown( + KeyID key, KeyModifierMask mask) +{ + CDisplayLock display(this); + XTestFakeKeyEvent(display, mapKey(key, mask), True, CurrentTime); + XSync(display, False); +} + +void CXWindowsSecondaryScreen::keyRepeat( + KeyID, KeyModifierMask, SInt32) +{ + CDisplayLock display(this); + // FIXME +} + +void CXWindowsSecondaryScreen::keyUp( + KeyID key, KeyModifierMask mask) +{ + CDisplayLock display(this); + XTestFakeKeyEvent(display, mapKey(key, mask), False, CurrentTime); + XSync(display, False); +} + +void CXWindowsSecondaryScreen::mouseDown(ButtonID button) +{ + CDisplayLock display(this); + XTestFakeButtonEvent(display, mapButton(button), True, CurrentTime); + XSync(display, False); +} + +void CXWindowsSecondaryScreen::mouseUp(ButtonID button) +{ + CDisplayLock display(this); + XTestFakeButtonEvent(display, mapButton(button), False, CurrentTime); + XSync(display, False); +} + +void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +{ + CDisplayLock display(this); + XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime); + XSync(display, False); +} + +void CXWindowsSecondaryScreen::mouseWheel(SInt32) +{ + CDisplayLock display(this); + // FIXME +} + +void CXWindowsSecondaryScreen::getSize( + SInt32* width, SInt32* height) const +{ + getScreenSize(width, height); +} + +SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + +void CXWindowsSecondaryScreen::onOpenDisplay() +{ + assert(m_window == None); + + CDisplayLock display(this); // create the cursor hiding window. this window is used to hide the // cursor when it's not on the screen. the window is hidden as soon @@ -67,230 +148,48 @@ void CXWindowsSecondaryScreen::open(CClient* client) attr.do_not_propagate_mask = 0; attr.override_redirect = True; attr.cursor = createBlankCursor(); - m_window = XCreateWindow(m_display, m_root, 0, 0, 1, 1, 0, 0, + m_window = XCreateWindow(display, getRoot(), 0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent, CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, &attr); // become impervious to server grabs - XTestGrabControl(m_display, True); + XTestGrabControl(display, True); // hide the cursor - leave(); - - // start processing events - m_eventThread = new CThread(new TMethodJob( - this, &CXWindowsSecondaryScreen::eventThread)); + leaveNoLock(display); } -void CXWindowsSecondaryScreen::close() +void CXWindowsSecondaryScreen::onCloseDisplay() { - assert(m_client != NULL); - assert(m_eventThread != NULL); - - // stop event thread - m_eventThread->cancel(); - m_eventThread->wait(); - delete m_eventThread; - m_eventThread = NULL; + assert(m_window != None); // no longer impervious to server grabs - XTestGrabControl(m_display, False); + CDisplayLock display(this); + XTestGrabControl(display, False); // destroy window - XDestroyWindow(m_display, m_window); + XDestroyWindow(display, m_window); m_window = None; - - // close the display - XCloseDisplay(m_display); - m_display = NULL; -} - -void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) -{ - assert(m_display != NULL); - assert(m_window != None); - - CLock lock(&m_mutex); - - // warp to requested location - XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); - XSync(m_display, False); - - // show cursor - XUnmapWindow(m_display, m_window); -} - -void CXWindowsSecondaryScreen::leave() -{ - assert(m_display != NULL); - assert(m_window != None); - - CLock lock(&m_mutex); - - // move hider window under the mouse (rather than moving the mouse - // somewhere else on the screen) - int x, y, dummy; - unsigned int dummyMask; - Window dummyWindow; - XQueryPointer(m_display, m_root, &dummyWindow, &dummyWindow, - &x, &y, &dummy, &dummy, &dummyMask); - XMoveWindow(m_display, m_window, x, y); - - // raise and show the hider window - XMapRaised(m_display, m_window); - - // hide cursor by moving it into the hider window - XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, 0, 0); -} - -void CXWindowsSecondaryScreen::keyDown( - KeyID key, KeyModifierMask mask) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - XTestFakeKeyEvent(m_display, mapKey(key, mask), True, CurrentTime); - XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::keyRepeat( - KeyID, KeyModifierMask, SInt32) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - // FIXME -} - -void CXWindowsSecondaryScreen::keyUp( - KeyID key, KeyModifierMask mask) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - XTestFakeKeyEvent(m_display, mapKey(key, mask), False, CurrentTime); - XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::mouseDown(ButtonID button) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - XTestFakeButtonEvent(m_display, mapButton(button), True, CurrentTime); - XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::mouseUp(ButtonID button) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - XTestFakeButtonEvent(m_display, mapButton(button), False, CurrentTime); - XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); - XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::mouseWheel(SInt32) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - // FIXME -} - -void CXWindowsSecondaryScreen::getSize( - SInt32* width, SInt32* height) const -{ - assert(m_display != NULL); - assert(width != NULL && height != NULL); - - *width = m_w; - *height = m_h; -} - -SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const -{ - assert(m_display != NULL); - - return 0; -} - -Cursor CXWindowsSecondaryScreen::createBlankCursor() -{ - // this seems just a bit more complicated than really necessary - - // get the closet cursor size to 1x1 - unsigned int w, h; - XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); - - // make bitmap data for cursor of closet size. since the cursor - // is blank we can use the same bitmap for shape and mask: all - // zeros. - const int size = ((w + 7) >> 3) * h; - char* data = new char[size]; - memset(data, 0, size); - - // make bitmap - Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h); - - // need an arbitrary color for the cursor - XColor color; - color.pixel = 0; - color.red = color.green = color.blue = 0; - color.flags = DoRed | DoGreen | DoBlue; - - // make cursor from bitmap - Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, - &color, &color, 0, 0); - - // don't need bitmap or the data anymore - delete[] data; - XFreePixmap(m_display, bitmap); - - return cursor; } void CXWindowsSecondaryScreen::eventThread(void*) { - assert(m_display != NULL); assert(m_window != None); for (;;) { - // wait for and then get the next event - m_mutex.lock(); - while (XPending(m_display) == 0) { - m_mutex.unlock(); - CThread::sleep(0.05); - m_mutex.lock(); - } + // wait for and get the next event XEvent xevent; - XNextEvent(m_display, &xevent); - m_mutex.unlock(); + getEvent(&xevent); // handle event switch (xevent.type) { case LeaveNotify: { // mouse moved out of hider window somehow. hide the window. - CLock lock(&m_mutex); - XUnmapWindow(m_display, m_window); + assert(m_window != None); + CDisplayLock display(this); + XUnmapWindow(display, m_window); break; } @@ -313,11 +212,33 @@ void CXWindowsSecondaryScreen::eventThread(void*) } } +void CXWindowsSecondaryScreen::leaveNoLock(Display* display) +{ + assert(display != NULL); + assert(m_window != None); + + // move hider window under the mouse (rather than moving the mouse + // somewhere else on the screen) + int x, y, dummy; + unsigned int dummyMask; + Window dummyWindow; + XQueryPointer(display, getRoot(), &dummyWindow, &dummyWindow, + &x, &y, &dummy, &dummy, &dummyMask); + XMoveWindow(display, m_window, x, y); + + // raise and show the hider window + XMapRaised(display, m_window); + + // hide cursor by moving it into the hider window + XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0); +} + KeyCode CXWindowsSecondaryScreen::mapKey( KeyID id, KeyModifierMask /*mask*/) const { + CDisplayLock display(this); // FIXME -- use mask - return XKeysymToKeycode(m_display, static_cast(id)); + return XKeysymToKeycode(display, static_cast(id)); } unsigned int CXWindowsSecondaryScreen::mapButton( diff --git a/synergy/CXWindowsSecondaryScreen.h b/synergy/CXWindowsSecondaryScreen.h index d37efc05..ef180537 100644 --- a/synergy/CXWindowsSecondaryScreen.h +++ b/synergy/CXWindowsSecondaryScreen.h @@ -1,13 +1,10 @@ #ifndef CXWINDOWSSECONDARYSCREEN_H #define CXWINDOWSSECONDARYSCREEN_H -#include "CMutex.h" +#include "CXWindowsScreen.h" #include "ISecondaryScreen.h" -#include -class CThread; - -class CXWindowsSecondaryScreen : public ISecondaryScreen { +class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen { public: CXWindowsSecondaryScreen(); virtual ~CXWindowsSecondaryScreen(); @@ -27,23 +24,20 @@ class CXWindowsSecondaryScreen : public ISecondaryScreen { virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; + protected: + // CXWindowsScreen overrides + virtual void onOpenDisplay(); + virtual void onCloseDisplay(); + virtual void eventThread(void*); + private: - Cursor createBlankCursor(); - void eventThread(void*); + void leaveNoLock(Display*); KeyCode mapKey(KeyID, KeyModifierMask) const; unsigned int mapButton(ButtonID button) const; private: CClient* m_client; - CThread* m_eventThread; - Display* m_display; - int m_screen; - Window m_root; Window m_window; - SInt32 m_w, m_h; - - // X is not thread safe - CMutex m_mutex; }; #endif diff --git a/synergy/Makefile b/synergy/Makefile index 5305a5c5..689c29c0 100644 --- a/synergy/Makefile +++ b/synergy/Makefile @@ -20,6 +20,7 @@ CXXFILES = \ CServerProtocol1_0.cpp \ CScreenMap.cpp \ CServer.cpp \ + CXWindowsScreen.cpp \ CXWindowsPrimaryScreen.cpp \ CXWindowsSecondaryScreen.cpp \ XSynergy.cpp \ From 303734a438db930905d0c99580e849eee661d0e8 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Nov 2001 21:27:36 +0000 Subject: [PATCH 031/807] fixed clamping when mapping to a different screen when beyond bottom or right of source screen. --- synergy/CServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synergy/CServer.cpp b/synergy/CServer.cpp index 65488d9a..98f8dbe8 100644 --- a/synergy/CServer.cpp +++ b/synergy/CServer.cpp @@ -572,7 +572,7 @@ void CServer::mapPosition(CScreenInfo* src, if (y < 0) y = 0; else if (y >= src->m_height) - y = src->m_height - 1; + y = dst->m_height - 1; else y = static_cast(0.5 + y * static_cast(dst->m_height - 1) / @@ -584,7 +584,7 @@ void CServer::mapPosition(CScreenInfo* src, if (x < 0) x = 0; else if (x >= src->m_width) - x = src->m_width - 1; + x = dst->m_width - 1; else x = static_cast(0.5 + x * static_cast(dst->m_width - 1) / From 6a223b7da5e82e56bbd88301fdfccf85c23e0644 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 13 Nov 2001 23:34:12 +0000 Subject: [PATCH 032/807] added preliminary support for getting the X selection. --- synergy/CServer.cpp | 5 + synergy/CXWindowsClipboard.cpp | 41 ++++ synergy/CXWindowsClipboard.h | 20 ++ synergy/CXWindowsPrimaryScreen.cpp | 26 ++- synergy/CXWindowsPrimaryScreen.h | 2 + synergy/CXWindowsScreen.cpp | 325 +++++++++++++++++++++++++++++ synergy/CXWindowsScreen.h | 34 +++ synergy/IClipboard.h | 44 ++++ synergy/IPrimaryScreen.h | 6 +- synergy/Makefile | 1 + 10 files changed, 499 insertions(+), 5 deletions(-) create mode 100644 synergy/CXWindowsClipboard.cpp create mode 100644 synergy/CXWindowsClipboard.h create mode 100644 synergy/IClipboard.h diff --git a/synergy/CServer.cpp b/synergy/CServer.cpp index 98f8dbe8..c60d515c 100644 --- a/synergy/CServer.cpp +++ b/synergy/CServer.cpp @@ -365,6 +365,7 @@ bool CServer::isLockedToScreen() const return false; } +#include "CXWindowsClipboard.h" // FIXME void CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y) { @@ -381,6 +382,10 @@ void CServer::switchScreen(CScreenInfo* dst, // leave active screen if (m_active->m_protocol == NULL) { m_primary->leave(); + + // FIXME -- testing + CXWindowsClipboard clipboard; + m_primary->getClipboard(&clipboard); } else { m_active->m_protocol->sendLeave(); diff --git a/synergy/CXWindowsClipboard.cpp b/synergy/CXWindowsClipboard.cpp new file mode 100644 index 00000000..6d72dc8d --- /dev/null +++ b/synergy/CXWindowsClipboard.cpp @@ -0,0 +1,41 @@ +#include "CXWindowsClipboard.h" +#include "CString.h" +#include "CLog.h" + +// +// CXWindowsClipboard +// + +CXWindowsClipboard::CXWindowsClipboard() +{ +} + +CXWindowsClipboard::~CXWindowsClipboard() +{ +} + +void CXWindowsClipboard::open() +{ + log((CLOG_INFO "open clipboard")); +} + +void CXWindowsClipboard::close() +{ + log((CLOG_INFO "close clipboard")); +} + +void CXWindowsClipboard::add( + EFormat format, const CString& data) +{ + log((CLOG_INFO "add clipboard format: %d\n%s", format, data.c_str())); +} + +bool CXWindowsClipboard::has(EFormat /*format*/) const +{ + return false; +} + +CString CXWindowsClipboard::get(EFormat /*format*/) const +{ + return CString(); +} diff --git a/synergy/CXWindowsClipboard.h b/synergy/CXWindowsClipboard.h new file mode 100644 index 00000000..a67aeed8 --- /dev/null +++ b/synergy/CXWindowsClipboard.h @@ -0,0 +1,20 @@ +#ifndef CXWINDOWSCLIPBOARD_H +#define CXWINDOWSCLIPBOARD_H + +#include "IClipboard.h" + +class CXWindowsClipboard : public IClipboard { + public: + CXWindowsClipboard(); + virtual ~CXWindowsClipboard(); + + // IClipboard overrides + virtual void open(); + virtual void close(); + virtual void add(EFormat, const CString& data); + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; +}; + +#endif + diff --git a/synergy/CXWindowsPrimaryScreen.cpp b/synergy/CXWindowsPrimaryScreen.cpp index 39595354..6e208869 100644 --- a/synergy/CXWindowsPrimaryScreen.cpp +++ b/synergy/CXWindowsPrimaryScreen.cpp @@ -150,6 +150,21 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( } } +#include // FIXME +void CXWindowsPrimaryScreen::setClipboard( + const IClipboard* /*clipboard*/) +{ + // FIXME -- put this in superclass? + // FIXME -- don't use CurrentTime + CDisplayLock display(this); + XSetSelectionOwner(display, XA_PRIMARY, m_window, CurrentTime); + if (XGetSelectionOwner(display, XA_PRIMARY) == m_window) { + // we got the selection + log((CLOG_DEBUG "grabbed clipboard")); + } + // FIXME -- need to copy or adopt the clipboard to serve future requests +} + void CXWindowsPrimaryScreen::getSize( SInt32* width, SInt32* height) const { @@ -161,6 +176,14 @@ SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const return 1; } +void CXWindowsPrimaryScreen::getClipboard( + IClipboard* clipboard) const +{ + // FIXME -- put this in superclass? + // FIXME -- don't use CurrentTime + getDisplayClipboard(clipboard, m_window, CurrentTime); +} + void CXWindowsPrimaryScreen::onOpenDisplay() { assert(m_window == None); @@ -215,7 +238,8 @@ void CXWindowsPrimaryScreen::selectEvents( return; // select events of interest - XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask); + XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask | + PropertyChangeMask); // recurse on child windows Window rw, pw, *cw; diff --git a/synergy/CXWindowsPrimaryScreen.h b/synergy/CXWindowsPrimaryScreen.h index fd8883d8..3e056154 100644 --- a/synergy/CXWindowsPrimaryScreen.h +++ b/synergy/CXWindowsPrimaryScreen.h @@ -17,8 +17,10 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); virtual void leave(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void setClipboard(const IClipboard*); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; + virtual void getClipboard(IClipboard*) const; protected: // CXWindowsScreen overrides diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 1ec8413f..3559de52 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -3,9 +3,12 @@ #include "CLock.h" #include "TMethodJob.h" #include "CLog.h" +#include "CString.h" #include #include #include +#include +#include // // CXWindowsScreen @@ -46,6 +49,13 @@ void CXWindowsScreen::openDisplay() // get the root window m_root = RootWindow(m_display, m_screen); + // get some atoms + m_atomTargets = XInternAtom(m_display, "TARGETS", False); + m_atomData = XInternAtom(m_display, "DESTINATION", False); + m_atomINCR = XInternAtom(m_display, "INCR", False); + m_atomText = XInternAtom(m_display, "TEXT", False); + m_atomCompoundText = XInternAtom(m_display, "COMPOUND_TEXT", False); + // let subclass prep display onOpenDisplay(); @@ -147,6 +157,321 @@ void CXWindowsScreen::getEvent(XEvent* xevent) const m_mutex.unlock(); } +void CXWindowsScreen::getDisplayClipboard( + IClipboard* clipboard, + Window requestor, Time timestamp) const +{ + assert(clipboard != NULL); + assert(requestor != None); + + // clear the clipboard object + clipboard->open(); + + // block others from using the display while we get the clipboard. + // in particular, this prevents the event thread from stealing the + // selection notify event we're expecting. + CLock lock(&m_mutex); + + // use PRIMARY selection as the "clipboard" + Atom selection = XA_PRIMARY; + + // ask the selection for all the formats it has. some owners return + // the TARGETS atom and some the ATOM atom when TARGETS is requested. + Atom format; + CString targets; + if (getDisplayClipboard(selection, m_atomTargets, + requestor, timestamp, &format, &targets) && + (format == m_atomTargets || format == XA_ATOM)) { + // get each target (that we can interpret). some owners return + // some targets multiple times in the list so don't try to get + // those multiple times. + const Atom* targetAtoms = reinterpret_cast(targets.data()); + const SInt32 numTargets = targets.size() / sizeof(Atom); + std::set clipboardFormats; + std::set targets; + log((CLOG_DEBUG "selection has %d targets", numTargets)); + for (SInt32 i = 0; i < numTargets; ++i) { + Atom format = targetAtoms[i]; + log((CLOG_DEBUG " source target %d", format)); + + // skip already handled targets + if (targets.count(format) > 0) { + log((CLOG_DEBUG " skipping handled target %d", format)); + continue; + } + + // mark this target as done + targets.insert(format); + + // determine the expected clipboard format + IClipboard::EFormat expectedFormat = getFormat(format); + + // if we can use the format and we haven't already retrieved + // it then get it + if (expectedFormat == IClipboard::kNum) { + log((CLOG_DEBUG " no format for target", format)); + continue; + } + if (clipboardFormats.count(expectedFormat) > 0) { + log((CLOG_DEBUG " skipping handled format %d", expectedFormat)); + continue; + } + + CString data; + if (!getDisplayClipboard(selection, format, + requestor, timestamp, &format, &data)) { + log((CLOG_DEBUG " no data for target", format)); + continue; + } + + // use the actual format, not the expected + IClipboard::EFormat actualFormat = getFormat(format); + if (actualFormat == IClipboard::kNum) { + log((CLOG_DEBUG " no format for target", format)); + continue; + } + if (clipboardFormats.count(actualFormat) > 0) { + log((CLOG_DEBUG " skipping handled format %d", actualFormat)); + continue; + } + + // add to clipboard and note we've done it + clipboard->add(actualFormat, data); + clipboardFormats.insert(actualFormat); + } + } + else { + // non-ICCCM conforming selection owner. try TEXT format. + // FIXME + log((CLOG_DEBUG "selection doesn't support TARGETS, format is %d", format)); + } + + // done with clipboard + clipboard->close(); +} + +bool CXWindowsScreen::getDisplayClipboard( + Atom selection, Atom type, + Window requestor, Time timestamp, + Atom* outputType, CString* outputData) const +{ + assert(outputType != NULL); + assert(outputData != NULL); + + // delete data property + XDeleteProperty(m_display, requestor, m_atomData); + + // request data conversion + XConvertSelection(m_display, selection, type, + m_atomData, requestor, timestamp); + + // wait for the selection notify event. can't just mask out other + // events because X stupidly doesn't provide a mask for selection + // events, so we use a predicate to find our event. + XEvent xevent; + while (XCheckIfEvent(m_display, &xevent, + &CXWindowsScreen::findSelectionNotify, + (XPointer)&requestor) != True) { + // wait a bit + CThread::sleep(0.05); + } + assert(xevent.type == SelectionNotify); + assert(xevent.xselection.requestor == requestor); + + // make sure the transfer worked + Atom property = xevent.xselection.property; + if (property == None) { + // cannot convert + *outputType = type; + log((CLOG_DEBUG "selection conversion failed for %d", type)); + return false; + } + + // get the data and discard the property + SInt32 datumSize; + CString data; + bool okay = getData(requestor, property, outputType, &datumSize, &data); + XDeleteProperty(m_display, requestor, property); + + // fail if we couldn't get the data + if (!okay) { + log((CLOG_DEBUG "can't get data for selection format %d", type)); + return false; + } + + // handle INCR type specially. it means we'll be receiving the data + // piecemeal so we just loop until we've collected all the data. + if (*outputType == m_atomINCR) { + log((CLOG_DEBUG "selection data for format %d is incremental", type)); + // the data is a lower bound on the amount of data to be + // transferred. use it as a hint to size our buffer. + UInt32 size; + switch (datumSize) { + case 8: + size = *(reinterpret_cast(data.data())); + break; + + case 16: + size = *(reinterpret_cast(data.data())); + break; + + case 32: + size = *(reinterpret_cast(data.data())); + break; + + default: + assert(0 && "invalid datum size"); + } + + // empty the buffer and reserve the lower bound + data.erase(); + data.reserve(size); + + // look for property notify events with the following + PropertyNotifyInfo filter; + filter.m_window = requestor; + filter.m_property = property; + + // now enter the INCR loop + bool error = false; + *outputType = (Atom)0; + for (;;) { + // wait for more data + while (XCheckIfEvent(m_display, &xevent, + &CXWindowsScreen::findPropertyNotify, + (XPointer)&filter) != True) { + // wait a bit + CThread::sleep(0.05); + } + assert(xevent.type == PropertyNotify); + assert(xevent.xproperty.window == requestor); + assert(xevent.xproperty.atom == property); + + // get the additional data then delete the property to + // ask the clipboard owner for the next chunk. + Atom newType; + CString newData; + okay = getData(requestor, property, &newType, NULL, &newData); + XDeleteProperty(m_display, requestor, property); + + // transfer has failed if we can't get the data + if (!okay) + error = true; + + // a zero length property means we got the last chunk + if (newData.size() == 0) + break; + + // if this is the first chunk then save the type. otherwise + // note that the new type is the same as the first chunk's + // type. if they're not the the clipboard owner is busted + // but we have to continue the transfer because there's no + // way to cancel it. + if (*outputType == (Atom)0) + *outputType = newType; + else if (*outputType != newType) + error = true; + + // append the data + data += newData; + } + + // if there was an error we could say the transferred failed + // but we'll be liberal in what we accept. + if (error) { + log((CLOG_WARN "ICCCM violation by clipboard owner")); +// return false; + } + } + + *outputData = data; + return true; +} + +bool CXWindowsScreen::getData( + Window window, Atom property, + Atom* type, SInt32* datumSize, + CString* data) const +{ + assert(type != NULL); + assert(data != NULL); + + // clear out any existing data + data->erase(); + + // read the property + long offset = 0; + long length = 8192 / 4; + for (;;) { + // get more data + int actualDatumSize; + unsigned long numItems, bytesLeft; + unsigned char* rawData; + const int result = XGetWindowProperty(m_display, window, property, + offset, length, False, AnyPropertyType, + type, &actualDatumSize, + &numItems, &bytesLeft, + &rawData); + if (result != Success) { + // failed + return false; + } + + // save datum size + if (datumSize != NULL) + *datumSize = (SInt32)actualDatumSize; + const SInt32 bytesPerDatum = (SInt32)actualDatumSize / 8; + + // advance read pointer. since we can only read at offsets that + // are multiples of 4 byte we take care to write multiples of 4 + // bytes to data, except when we've retrieved the last chunk. + SInt32 quadCount = (numItems * bytesPerDatum) / 4; + offset += quadCount; + + // append data + if (bytesLeft == 0) + data->append((char*)rawData, bytesPerDatum * numItems); + else + data->append((char*)rawData, 4 * quadCount); + + // done with returned data + XFree(rawData); + + // done if no data is left + if (bytesLeft == 0) + return true; + } +} + +IClipboard::EFormat CXWindowsScreen::getFormat(Atom src) const +{ + // FIXME -- handle more formats (especially mime-type-like formats + // and various character encodings like unicode). + if (src == XA_STRING || + src == m_atomText || + src == m_atomCompoundText) + return IClipboard::kText; + return IClipboard::kNum; +} + +Bool CXWindowsScreen::findSelectionNotify( + Display*, XEvent* xevent, XPointer arg) +{ + Window requestor = *((Window*)arg); + return (xevent->type == SelectionNotify && + xevent->xselection.requestor == requestor) ? True : False; +} + +Bool CXWindowsScreen::findPropertyNotify( + Display*, XEvent* xevent, XPointer arg) +{ + PropertyNotifyInfo* filter = (PropertyNotifyInfo*)arg; + return (xevent->type == PropertyNotify && + xevent->xproperty.window == filter->m_window && + xevent->xproperty.atom == filter->m_property && + xevent->xproperty.state == PropertyNewValue) ? True : False; +} + // // CXWindowsScreen::CDisplayLock diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 6ad39859..66cac76e 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -2,9 +2,11 @@ #define CXWINDOWSSCREEN_H #include "CMutex.h" +#include "IClipboard.h" #include "BasicTypes.h" #include +class CString; class CThread; class CXWindowsScreen { @@ -50,6 +52,12 @@ class CXWindowsScreen { // wait for and get the next X event. cancellable. void getEvent(XEvent*) const; + // copy the clipboard contents to clipboard. requestor must be a + // valid window; it will be used to receive the transfer. timestamp + // should be the timestamp of the provoking event and not CurrentTime. + void getDisplayClipboard(IClipboard* clipboard, + Window requestor, Time timestamp) const; + // called by openDisplay() to allow subclasses to prepare the display virtual void onOpenDisplay() = 0; @@ -59,6 +67,25 @@ class CXWindowsScreen { // override to process X events virtual void eventThread(void*) = 0; + private: + struct PropertyNotifyInfo { + public: + Window m_window; + Atom m_property; + }; + + bool getDisplayClipboard(Atom selection, Atom type, + Window requestor, Time timestamp, + Atom* outputType, CString* data) const; + bool getData(Window, Atom property, + Atom* type, SInt32* datumSize, + CString* data) const; + IClipboard::EFormat getFormat(Atom) const; + static Bool findSelectionNotify(Display*, + XEvent* xevent, XPointer arg); + static Bool findPropertyNotify(Display*, + XEvent* xevent, XPointer arg); + private: CThread* m_eventThread; Display* m_display; @@ -66,6 +93,13 @@ class CXWindowsScreen { Window m_root; SInt32 m_w, m_h; + // atoms we'll need + Atom m_atomTargets; + Atom m_atomData; + Atom m_atomINCR; + Atom m_atomText; + Atom m_atomCompoundText; + // X is not thread safe CMutex m_mutex; }; diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h new file mode 100644 index 00000000..e42b2706 --- /dev/null +++ b/synergy/IClipboard.h @@ -0,0 +1,44 @@ +#ifndef ICLIPBOARD_H +#define ICLIPBOARD_H + +#include "IInterface.h" +#include "BasicTypes.h" + +class CString; + +class IClipboard : public IInterface { + public: + enum EFormat { kText, kNum }; + + // manipulators + + // grab ownership of and clear the clipboard of all data. + // only add() may be called between an open() and its + // corresponding close(). + virtual void open() = 0; + + // close the clipboard. close() must match a preceding open(). + // this signals that the clipboard has been filled with all the + // necessary data. it does not mean the clipboard ownership + // should be released. + virtual void close() = 0; + + // add data in the given format to the clipboard. data is + // passed as a string but the contents are generally not + // interpreted. may only be called between an open() and + // a close(). + virtual void add(EFormat, const CString& data) = 0; + + // accessors + + // returns true iff the clipboard contains data in the given + // format. + virtual bool has(EFormat) const = 0; + + // returns data in the given format. rturns the empty string + // if there is no data in that format. + virtual CString get(EFormat) const = 0; +}; + +#endif + diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 86f98498..284728b4 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -5,7 +5,7 @@ #include "BasicTypes.h" class CServer; -//class IClipboard; +class IClipboard; class IPrimaryScreen : public IInterface { public: @@ -37,11 +37,11 @@ class IPrimaryScreen : public IInterface { // warp the cursor to the given position virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; -/* // set the screen's clipboard contents. this is usually called // soon after an enter(). virtual void setClipboard(const IClipboard*) = 0; +/* // show or hide the screen saver virtual void onScreenSaver(bool show) = 0; @@ -64,10 +64,8 @@ class IPrimaryScreen : public IInterface { // get the size of jump zone virtual SInt32 getJumpZoneSize() const = 0; -/* // get the screen's clipboard contents virtual void getClipboard(IClipboard*) const = 0; -*/ }; #endif diff --git a/synergy/Makefile b/synergy/Makefile index 689c29c0..15d806f4 100644 --- a/synergy/Makefile +++ b/synergy/Makefile @@ -23,6 +23,7 @@ CXXFILES = \ CXWindowsScreen.cpp \ CXWindowsPrimaryScreen.cpp \ CXWindowsSecondaryScreen.cpp \ + CXWindowsClipboard.cpp \ XSynergy.cpp \ $(NULL) From 51505783aabddce5bdcaa848aa5d2ec6460e5727 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 18 Nov 2001 23:14:28 +0000 Subject: [PATCH 033/807] moved client and server files into their own respective directories. --- Makefile | 3 ++ {synergy => client}/CClient.cpp | 0 {synergy => client}/CClient.h | 0 .../CXWindowsSecondaryScreen.cpp | 0 .../CXWindowsSecondaryScreen.h | 0 client/Makefile | 44 +++++++++++++++++ {synergy => client}/client.cpp | 0 {synergy => server}/CScreenMap.cpp | 0 {synergy => server}/CScreenMap.h | 0 {synergy => server}/CServer.cpp | 0 {synergy => server}/CServer.h | 0 {synergy => server}/CServerProtocol.cpp | 0 {synergy => server}/CServerProtocol.h | 0 {synergy => server}/CServerProtocol1_0.cpp | 0 {synergy => server}/CServerProtocol1_0.h | 0 .../CXWindowsPrimaryScreen.cpp | 0 {synergy => server}/CXWindowsPrimaryScreen.h | 0 server/Makefile | 47 +++++++++++++++++++ {synergy => server}/server.cpp | 0 synergy/Makefile | 40 +++++----------- 20 files changed, 105 insertions(+), 29 deletions(-) rename {synergy => client}/CClient.cpp (100%) rename {synergy => client}/CClient.h (100%) rename {synergy => client}/CXWindowsSecondaryScreen.cpp (100%) rename {synergy => client}/CXWindowsSecondaryScreen.h (100%) create mode 100644 client/Makefile rename {synergy => client}/client.cpp (100%) rename {synergy => server}/CScreenMap.cpp (100%) rename {synergy => server}/CScreenMap.h (100%) rename {synergy => server}/CServer.cpp (100%) rename {synergy => server}/CServer.h (100%) rename {synergy => server}/CServerProtocol.cpp (100%) rename {synergy => server}/CServerProtocol.h (100%) rename {synergy => server}/CServerProtocol1_0.cpp (100%) rename {synergy => server}/CServerProtocol1_0.h (100%) rename {synergy => server}/CXWindowsPrimaryScreen.cpp (100%) rename {synergy => server}/CXWindowsPrimaryScreen.h (100%) create mode 100644 server/Makefile rename {synergy => server}/server.cpp (100%) diff --git a/Makefile b/Makefile index 06e4943d..c89c39e7 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,10 @@ SUBDIRS = \ io \ net \ synergy \ + client \ + server \ $(NULL) + # # targets # diff --git a/synergy/CClient.cpp b/client/CClient.cpp similarity index 100% rename from synergy/CClient.cpp rename to client/CClient.cpp diff --git a/synergy/CClient.h b/client/CClient.h similarity index 100% rename from synergy/CClient.h rename to client/CClient.h diff --git a/synergy/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp similarity index 100% rename from synergy/CXWindowsSecondaryScreen.cpp rename to client/CXWindowsSecondaryScreen.cpp diff --git a/synergy/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h similarity index 100% rename from synergy/CXWindowsSecondaryScreen.h rename to client/CXWindowsSecondaryScreen.h diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 00000000..6c0b49cd --- /dev/null +++ b/client/Makefile @@ -0,0 +1,44 @@ +DEPTH=.. +include $(DEPTH)/Makecommon + +# +# target file +# +TARGET = client + +# +# source files +# +LCXXINCS = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + -I$(DEPTH)/net \ + -I$(DEPTH)/synergy \ + $(NULL) +CXXFILES = \ + CXWindowsSecondaryScreen.cpp \ + CClient.cpp \ + client.cpp \ + $(NULL) + +# +# libraries we depend on +# +DEPLIBS = \ + $(LIBDIR)/libsynergy.a \ + $(LIBDIR)/libnet.a \ + $(LIBDIR)/libio.a \ + $(LIBDIR)/libmt.a \ + $(LIBDIR)/libbase.a \ + $(NULL) +LLDLIBS = \ + $(DEPLIBS) \ + -lpthread \ + $(NULL) + +targets: $(TARGET) + +$(TARGET): $(OBJECTS) $(DEPLIBS) + $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) + diff --git a/synergy/client.cpp b/client/client.cpp similarity index 100% rename from synergy/client.cpp rename to client/client.cpp diff --git a/synergy/CScreenMap.cpp b/server/CScreenMap.cpp similarity index 100% rename from synergy/CScreenMap.cpp rename to server/CScreenMap.cpp diff --git a/synergy/CScreenMap.h b/server/CScreenMap.h similarity index 100% rename from synergy/CScreenMap.h rename to server/CScreenMap.h diff --git a/synergy/CServer.cpp b/server/CServer.cpp similarity index 100% rename from synergy/CServer.cpp rename to server/CServer.cpp diff --git a/synergy/CServer.h b/server/CServer.h similarity index 100% rename from synergy/CServer.h rename to server/CServer.h diff --git a/synergy/CServerProtocol.cpp b/server/CServerProtocol.cpp similarity index 100% rename from synergy/CServerProtocol.cpp rename to server/CServerProtocol.cpp diff --git a/synergy/CServerProtocol.h b/server/CServerProtocol.h similarity index 100% rename from synergy/CServerProtocol.h rename to server/CServerProtocol.h diff --git a/synergy/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp similarity index 100% rename from synergy/CServerProtocol1_0.cpp rename to server/CServerProtocol1_0.cpp diff --git a/synergy/CServerProtocol1_0.h b/server/CServerProtocol1_0.h similarity index 100% rename from synergy/CServerProtocol1_0.h rename to server/CServerProtocol1_0.h diff --git a/synergy/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp similarity index 100% rename from synergy/CXWindowsPrimaryScreen.cpp rename to server/CXWindowsPrimaryScreen.cpp diff --git a/synergy/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h similarity index 100% rename from synergy/CXWindowsPrimaryScreen.h rename to server/CXWindowsPrimaryScreen.h diff --git a/server/Makefile b/server/Makefile new file mode 100644 index 00000000..2ff1150f --- /dev/null +++ b/server/Makefile @@ -0,0 +1,47 @@ +DEPTH=.. +include $(DEPTH)/Makecommon + +# +# target file +# +TARGET = server + +# +# source files +# +LCXXINCS = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + -I$(DEPTH)/net \ + -I$(DEPTH)/synergy \ + $(NULL) +CXXFILES = \ + CScreenMap.cpp \ + CServerProtocol.cpp \ + CServerProtocol1_0.cpp \ + CXWindowsPrimaryScreen.cpp \ + CServer.cpp \ + server.cpp \ + $(NULL) + +# +# libraries we depend on +# +DEPLIBS = \ + $(LIBDIR)/libsynergy.a \ + $(LIBDIR)/libnet.a \ + $(LIBDIR)/libio.a \ + $(LIBDIR)/libmt.a \ + $(LIBDIR)/libbase.a \ + $(NULL) +LLDLIBS = \ + $(DEPLIBS) \ + -lpthread \ + $(NULL) + +targets: $(TARGET) + +$(TARGET): $(OBJECTS) $(DEPLIBS) + $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) + diff --git a/synergy/server.cpp b/server/server.cpp similarity index 100% rename from synergy/server.cpp rename to server/server.cpp diff --git a/synergy/Makefile b/synergy/Makefile index 15d806f4..bb557bf8 100644 --- a/synergy/Makefile +++ b/synergy/Makefile @@ -1,6 +1,11 @@ DEPTH=.. include $(DEPTH)/Makecommon +# +# target file +# +TARGET = synergy + # # source files # @@ -13,39 +18,16 @@ LCXXINCS = \ CXXFILES = \ CInputPacketStream.cpp \ COutputPacketStream.cpp \ - CTCPSocketFactory.cpp \ CProtocolUtil.cpp \ - CClient.cpp \ - CServerProtocol.cpp \ - CServerProtocol1_0.cpp \ - CScreenMap.cpp \ - CServer.cpp \ - CXWindowsScreen.cpp \ - CXWindowsPrimaryScreen.cpp \ - CXWindowsSecondaryScreen.cpp \ + CTCPSocketFactory.cpp \ CXWindowsClipboard.cpp \ + CXWindowsScreen.cpp \ XSynergy.cpp \ $(NULL) -# -# libraries we depend on -# -DEPLIBS = \ - $(LIBDIR)/libnet.a \ - $(LIBDIR)/libio.a \ - $(LIBDIR)/libmt.a \ - $(LIBDIR)/libbase.a \ - $(NULL) -LLDLIBS = \ - $(DEPLIBS) \ - -lpthread \ - $(NULL) +targets: $(LIBTARGET) -targets: server client - -server: server.o $(OBJECTS) $(DEPLIBS) - $(CXX) $(CXXFLAGS) -o $@ server.o $(OBJECTS) $(LDFLAGS) - -client: client.o $(OBJECTS) $(DEPLIBS) - $(CXX) $(CXXFLAGS) -o $@ client.o $(OBJECTS) $(LDFLAGS) +$(LIBTARGET): $(OBJECTS) + if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi + $(ARF) $(LIBTARGET) $(OBJECTS) From 3f6146b15f5f4f2943bac060ee0cfca08ef6acc4 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 19 Nov 2001 00:33:36 +0000 Subject: [PATCH 034/807] checkpoint. merging win32 code. server on X is currently broken and client probably is. --- Make-linux | 2 +- all.dsp | 63 ++ base/BasicTypes.h | 24 +- base/CLog.cpp | 94 ++- base/CLog.h | 8 +- base/XBase.cpp | 2 +- base/base.dsp | 150 +++++ base/common.h | 6 +- client/CClient.cpp | 150 +++-- client/CClient.h | 14 +- client/CMSWindowsSecondaryScreen.cpp | 839 +++++++++++++++++++++++++++ client/CMSWindowsSecondaryScreen.h | 43 ++ client/CXWindowsSecondaryScreen.cpp | 83 +-- client/CXWindowsSecondaryScreen.h | 3 +- client/Makefile | 6 +- client/client.cpp | 62 +- client/client.dsp | 127 ++++ client/client.rc | 97 ++++ client/resource.h | 17 + io/io.dsp | 150 +++++ mt/CCondVar.cpp | 2 +- mt/CThread.cpp | 20 - mt/CThreadRep.cpp | 21 +- mt/CThreadRep.h | 20 + mt/mt.dsp | 146 +++++ net/CNetwork.cpp | 339 +++++++++++ net/CNetwork.h | 143 +++++ net/CNetworkAddress.cpp | 24 +- net/CNetworkAddress.h | 8 +- net/CTCPListenSocket.cpp | 41 +- net/CTCPListenSocket.h | 3 +- net/CTCPSocket.cpp | 55 +- net/CTCPSocket.h | 5 +- net/Makefile | 3 +- net/XNetwork.cpp | 72 +++ net/XNetwork.h | 52 ++ net/net.dsp | 174 ++++++ notes | 20 + server/CMSWindowsPrimaryScreen.cpp | 509 ++++++++++++++++ server/CMSWindowsPrimaryScreen.h | 56 ++ server/CScreenMap.h | 2 + server/CServer.cpp | 94 ++- server/CServer.h | 15 +- server/CSynergyHook.cpp | 242 ++++++++ server/CSynergyHook.h | 37 ++ server/CXWindowsPrimaryScreen.cpp | 229 ++++---- server/CXWindowsPrimaryScreen.h | 3 +- server/Makefile | 6 +- server/makehook.dsp | 63 ++ server/resource.h | 17 + server/server.cpp | 67 ++- server/server.dsp | 155 +++++ server/server.rc | 97 ++++ server/synrgyhk.dsp | 111 ++++ synergy.dsw | 215 +++++++ synergy/CMSWindowsClipboard.cpp | 41 ++ synergy/CMSWindowsClipboard.h | 19 + synergy/CMSWindowsScreen.cpp | 248 ++++++++ synergy/CMSWindowsScreen.h | 73 +++ synergy/CXWindowsClipboard.h | 1 - synergy/CXWindowsScreen.cpp | 37 +- synergy/CXWindowsScreen.h | 11 +- synergy/IClipboard.h | 1 - synergy/IPrimaryScreen.h | 14 +- synergy/ISecondaryScreen.h | 9 + synergy/synergy.dsp | 186 ++++++ 66 files changed, 5222 insertions(+), 424 deletions(-) create mode 100644 all.dsp create mode 100644 base/base.dsp create mode 100644 client/CMSWindowsSecondaryScreen.cpp create mode 100644 client/CMSWindowsSecondaryScreen.h create mode 100644 client/client.dsp create mode 100644 client/client.rc create mode 100644 client/resource.h create mode 100644 io/io.dsp create mode 100644 mt/mt.dsp create mode 100644 net/CNetwork.cpp create mode 100644 net/CNetwork.h create mode 100644 net/XNetwork.cpp create mode 100644 net/XNetwork.h create mode 100644 net/net.dsp create mode 100644 server/CMSWindowsPrimaryScreen.cpp create mode 100644 server/CMSWindowsPrimaryScreen.h create mode 100644 server/CSynergyHook.cpp create mode 100644 server/CSynergyHook.h create mode 100644 server/makehook.dsp create mode 100644 server/resource.h create mode 100644 server/server.dsp create mode 100644 server/server.rc create mode 100644 server/synrgyhk.dsp create mode 100644 synergy.dsw create mode 100644 synergy/CMSWindowsClipboard.cpp create mode 100644 synergy/CMSWindowsClipboard.h create mode 100644 synergy/CMSWindowsScreen.cpp create mode 100644 synergy/CMSWindowsScreen.h create mode 100644 synergy/synergy.dsp diff --git a/Make-linux b/Make-linux index f5ce7843..033ca228 100644 --- a/Make-linux +++ b/Make-linux @@ -13,7 +13,7 @@ RMR = /bin/rm -rf # # compiler options # -GCXXDEFS = -D_XOPEN_SOURCE=500 +GCXXDEFS = -D_XOPEN_SOURCE=600 GCXXINCS = -I$(DEPTH)/include -I/usr/X11R6/include GCXXOPTS = -Wall -W -fexceptions CXXOPTIMIZER = -g diff --git a/all.dsp b/all.dsp new file mode 100644 index 00000000..902d872b --- /dev/null +++ b/all.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="all" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=all - Win32 Debug +!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 "all.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 "all.mak" CFG="all - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "all - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "all - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +MTL=midl.exe + +!IF "$(CFG)" == "all - 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" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "all - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "all___Win32_Debug" +# PROP BASE Intermediate_Dir "all___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "all - Win32 Release" +# Name "all - Win32 Debug" +# End Target +# End Project diff --git a/base/BasicTypes.h b/base/BasicTypes.h index 1a88d3a0..b21eba8f 100644 --- a/base/BasicTypes.h +++ b/base/BasicTypes.h @@ -37,7 +37,29 @@ typedef uint64_t UInt64; #if defined(CONFIG_PLATFORM_WIN32) -// FIXME +// use VC++ extensions if available +#if defined(_MSC_VER) +typedef signed __int8 SInt8; +typedef signed __int16 SInt16; +typedef signed __int32 SInt32; +typedef signed __int64 SInt64; + +typedef unsigned __int8 UInt8; +typedef unsigned __int16 UInt16; +typedef unsigned __int32 UInt32; +typedef unsigned __int64 UInt64; +#else +typedef signed char SInt8; +typedef short SInt16; +typedef int SInt32; +typedef long long SInt64; + +typedef unsigned char UInt8; +typedef unsigned short UInt16; +typedef unsigned int UInt32; +typedef unsigned long long UInt64; + +#endif #endif // CONFIG_PLATFORM_WIN32 diff --git a/base/CLog.cpp b/base/CLog.cpp index cb98cbbd..f8fd3479 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -1,14 +1,37 @@ #include "CLog.h" +#include "BasicTypes.h" #include #include #include #include +#if defined(CONFIG_PLATFORM_WIN32) +#include +#define vsnprintf _vsnprintf +#endif + +static int g_maxPriority = -1; +static const char* g_priority[] = { + "FATAL", + "ERROR", + "WARNING", + "NOTE", + "INFO", + "DEBUG", + }; +static const int g_numPriority = (int)(sizeof(g_priority) / + sizeof(g_priority[0])); +static const int g_maxPriorityLength = 7; // length of longest string +static const int g_prioritySuffixLength = 2; +static const int g_priorityPad = g_maxPriorityLength + + g_prioritySuffixLength; +static const int g_newlineLength = 2; + // // CLog // -static int g_maxPriority = -1; +CLog::Outputter CLog::s_outputter = NULL; void CLog::print(const char* fmt, ...) { @@ -19,11 +42,14 @@ void CLog::print(const char* fmt, ...) fmt += 3; } + // compute prefix padding length + int pad = g_priorityPad; + // print to buffer char stack[1024]; va_list args; va_start(args, fmt); - char* buffer = vsprint(0, stack, + char* buffer = vsprint(pad, stack, sizeof(stack) / sizeof(stack[0]), fmt, args); va_end(args); @@ -48,17 +74,19 @@ void CLog::printt(const char* file, int line, // compute prefix padding length char stack[1024]; sprintf(stack, "%d", line); - int pad = strlen(file) + 1 + strlen(stack) + 1 + 1; + int pad = strlen(file) + 1 /* comma */ + + strlen(stack) + 1 /* colon */ + 1 /* space */ + + g_priorityPad; - // print to buffer + // print to buffer, leaving space for a newline at the end va_list args; va_start(args, fmt); char* buffer = vsprint(pad, stack, sizeof(stack) / sizeof(stack[0]), fmt, args); va_end(args); - // print the prefix to the buffer - sprintf(buffer, "%s,%d:", file, line); + // print the prefix to the buffer. leave space for priority label. + sprintf(buffer + g_priorityPad, "%s,%d:", file, line); buffer[pad - 1] = ' '; // output buffer @@ -69,35 +97,51 @@ void CLog::printt(const char* file, int line, delete[] buffer; } -void CLog::output(int priority, const char* msg) +void CLog::setOutputter(Outputter outputter) { - static const char* s_priority[] = { - "FATAL", - "ERROR", - "WARNING", - "NOTE", - "INFO", - "DEBUG", - }; - static const int s_numPriority = (int)(sizeof(s_priority) / - sizeof(s_priority[0])); - assert(priority >= 0 && priority < s_numPriority); + s_outputter = outputter; +} + +void CLog::output(int priority, char* msg) +{ + assert(priority >= 0 && priority < g_numPriority); assert(msg != 0); if (g_maxPriority == -1) { - g_maxPriority = s_numPriority - 1; + g_maxPriority = g_numPriority - 1; const char* priEnv = getenv("SYN_LOG_PRI"); if (priEnv != NULL) { - for (int i = 0; i < s_numPriority; ++i) - if (strcmp(priEnv, s_priority[i]) == 0) { + for (int i = 0; i < g_numPriority; ++i) + if (strcmp(priEnv, g_priority[i]) == 0) { g_maxPriority = i; break; } } } - if (priority <= g_maxPriority) - fprintf(stderr, "%s: %s\n", s_priority[priority], msg); + if (priority <= g_maxPriority) { + // insert priority label + int n = strlen(g_priority[priority]); + sprintf(msg + g_maxPriorityLength - n, "%s:", g_priority[priority]); + msg[g_maxPriorityLength + 1] = ' '; + + // put a newline at the end +#if defined(CONFIG_PLATFORM_WIN32) + strcat(msg + g_priorityPad, "\r\n"); +#else + strcat(msg + g_priorityPad, "\n"); +#endif + + // print it + if (s_outputter) + s_outputter(msg + g_maxPriorityLength - n); + else +#if defined(CONFIG_PLATFORM_WIN32) + OutputDebugString(msg + g_maxPriorityLength - n); +#else + fprintf(stderr, "%s", msg + g_maxPriorityLength - n); +#endif + } } char* CLog::vsprint(int pad, char* buffer, int len, @@ -109,7 +153,7 @@ char* CLog::vsprint(int pad, char* buffer, int len, int n; if (len >= pad) { n = vsnprintf(buffer + pad, len - pad, fmt, args); - if (n != -1 && n <= len - pad) + if (n != -1 && n <= len - pad + g_newlineLength) return buffer; } @@ -120,7 +164,7 @@ char* CLog::vsprint(int pad, char* buffer, int len, len *= 2; buffer = new char[len + pad]; n = vsnprintf(buffer + pad, len - pad, fmt, args); - } while (n == -1 || n > len - pad); + } while (n == -1 || n > len - pad + g_newlineLength); return buffer; } diff --git a/base/CLog.h b/base/CLog.h index 3116142b..908072e9 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -5,13 +5,19 @@ class CLog { public: + typedef void (*Outputter)(const char*); + static void print(const char*, ...); static void printt(const char* file, int line, const char*, ...); + static void setOutputter(Outputter); private: - static void output(int priority, const char* msg); + static void output(int priority, char* msg); static char* vsprint(int pad, char*, int len, const char*, va_list); static int nprint(const char*, va_list); + + private: + static Outputter s_outputter; }; #if defined(NOLOGGING) diff --git a/base/XBase.cpp b/base/XBase.cpp index 6558d5e3..bc2db608 100644 --- a/base/XBase.cpp +++ b/base/XBase.cpp @@ -2,7 +2,7 @@ #include // win32 wants a const char* argument to std::exception c'tor -#if CONFIG_PLATFORM_WIN32 +#if defined(CONFIG_PLATFORM_WIN32) #define STDEXCEPTARG "" #endif diff --git a/base/base.dsp b/base/base.dsp new file mode 100644 index 00000000..c73c0e44 --- /dev/null +++ b/base/base.dsp @@ -0,0 +1,150 @@ +# Microsoft Developer Studio Project File - Name="base" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=base - Win32 Debug +!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 "base.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 "base.mak" CFG="base - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "base - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "base - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "base - 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" +# 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 /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "base - 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" +# 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 /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "base - Win32 Release" +# Name "base - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CFunctionJob.cpp +# End Source File +# Begin Source File + +SOURCE=.\CLog.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStopwatch.cpp +# End Source File +# Begin Source File + +SOURCE=.\XBase.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\BasicTypes.h +# End Source File +# Begin Source File + +SOURCE=.\CFunctionJob.h +# End Source File +# Begin Source File + +SOURCE=.\CLog.h +# End Source File +# Begin Source File + +SOURCE=.\common.h +# End Source File +# Begin Source File + +SOURCE=.\CStopwatch.h +# End Source File +# Begin Source File + +SOURCE=.\CString.h +# End Source File +# Begin Source File + +SOURCE=.\IInterface.h +# End Source File +# Begin Source File + +SOURCE=.\IJob.h +# End Source File +# Begin Source File + +SOURCE=.\TMethodJob.h +# End Source File +# Begin Source File + +SOURCE=.\XBase.h +# End Source File +# End Group +# End Target +# End Project diff --git a/base/common.h b/base/common.h index ca1224ca..e95b14ea 100644 --- a/base/common.h +++ b/base/common.h @@ -15,10 +15,14 @@ #define CONFIG_TYPES_X11 #define CONFIG_PTHREADS -#elif defined(_WINDOWS) && defined(WIN32) +#elif defined(_WIN32) #define CONFIG_PLATFORM_WIN32 +#if (_MSC_VER >= 1200) +#pragma warning(disable: 4786) // identifier truncated in debug info +#endif + #else #error unsupported platform diff --git a/client/CClient.cpp b/client/CClient.cpp index bb4579e7..33447115 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -9,8 +9,17 @@ #include "XSynergy.h" #include "TMethodJob.h" #include "CLog.h" +#include #include +// hack to work around operator=() bug in STL in g++ prior to v3 +#if defined(__GNUC__) && (__GNUC__ < 3) +#define assign(_dst, _src, _type) _dst.reset(_src) +#else +#define assign(_dst, _src, _type) _dst = std::auto_ptr<_type >(_src) +#endif + + // // CClient // @@ -31,13 +40,50 @@ CClient::~CClient() void CClient::run(const CNetworkAddress& serverAddress) { - m_serverAddress = &serverAddress; - CThread thread(new TMethodJob(this, &CClient::runSession)); - thread.wait(); + CThread* thread; + try { + log((CLOG_NOTE "starting client")); + + // connect to secondary screen + openSecondaryScreen(); + + // start server interactions + m_serverAddress = &serverAddress; + thread = new CThread(new TMethodJob(this, &CClient::runSession)); + + // handle events + log((CLOG_DEBUG "starting event handling")); + m_screen->run(); + + // clean up + log((CLOG_DEBUG "stopping client")); + thread->cancel(); + thread->wait(); + delete thread; + closeSecondaryScreen(); + } + catch (XBase& e) { + log((CLOG_ERR "client error: %s\n", e.what())); + + // clean up + thread->cancel(); + thread->wait(); + delete thread; + closeSecondaryScreen(); + } + catch (...) { + log((CLOG_DEBUG "unknown client error")); + + // clean up + thread->cancel(); + thread->wait(); + delete thread; + closeSecondaryScreen(); + throw; + } } -#include "CTCPSocket.h" -#include "CXWindowsSecondaryScreen.h" +#include "CTCPSocket.h" // FIXME void CClient::runSession(void*) { log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); @@ -51,7 +97,7 @@ void CClient::runSession(void*) // create socket and attempt to connect to server log((CLOG_DEBUG "connecting to server")); - socket.reset(new CTCPSocket()); // FIXME -- use factory + assign(socket, new CTCPSocket(), ISocket); // FIXME -- use factory socket->connect(*m_serverAddress); log((CLOG_INFO "connected to server")); @@ -72,8 +118,8 @@ void CClient::runSession(void*) */ // attach the packetizing filters - input.reset(new CInputPacketStream(srcInput, own)); - output.reset(new COutputPacketStream(srcOutput, own)); + assign(input, new CInputPacketStream(srcInput, own), IInputStream); + assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); // wait for hello from server log((CLOG_DEBUG "wait for hello")); @@ -99,26 +145,17 @@ void CClient::runSession(void*) } catch (XIncompatibleClient& e) { log((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); + m_screen->stop(); return; } catch (XThread&) { log((CLOG_ERR "connection timed out")); + m_screen->stop(); throw; } catch (XBase& e) { log((CLOG_ERR "connection failed: %s", e.what())); - return; - } - - // connect to screen - std::auto_ptr screenCleaner; - try { - log((CLOG_DEBUG "creating secondary screen")); - m_screen = new CXWindowsSecondaryScreen; - screenCleaner.reset(new CScreenCleaner(this, m_screen)); - } - catch (XBase& e) { - log((CLOG_ERR "cannot open screen: %s", e.what())); + m_screen->stop(); return; } @@ -199,16 +236,56 @@ void CClient::runSession(void*) } catch (XBase& e) { log((CLOG_ERR "error: %s", e.what())); + m_screen->stop(); return; } - // done with screen - log((CLOG_DEBUG "destroying secondary screen")); - screenCleaner.reset(); - // done with socket log((CLOG_DEBUG "disconnecting from server")); socket->close(); + + // exit event loop + m_screen->stop(); +} + +// FIXME -- use factory to create screen +#if defined(CONFIG_PLATFORM_WIN32) +#include "CMSWindowsSecondaryScreen.h" +#elif defined(CONFIG_PLATFORM_UNIX) +#include "CXWindowsSecondaryScreen.h" +#endif +void CClient::openSecondaryScreen() +{ + assert(m_screen == NULL); + + // open screen + log((CLOG_DEBUG "creating secondary screen")); +#if defined(CONFIG_PLATFORM_WIN32) + m_screen = new CMSWindowsSecondaryScreen; +#elif defined(CONFIG_PLATFORM_UNIX) + m_screen = new CXWindowsSecondaryScreen; +#endif + log((CLOG_DEBUG "opening secondary screen")); + m_screen->open(this); +} + +void CClient::closeSecondaryScreen() +{ + assert(m_screen != NULL); + + // close the secondary screen + try { + log((CLOG_DEBUG "closing secondary screen")); + m_screen->close(); + } + catch (...) { + // ignore + } + + // clean up + log((CLOG_DEBUG "destroying secondary screen")); + delete m_screen; + m_screen = NULL; } void CClient::onEnter() @@ -306,28 +383,3 @@ void CClient::onMouseWheel() CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); m_screen->mouseWheel(delta); } - - -// -// CClient::CScreenCleaner -// - -CClient::CScreenCleaner::CScreenCleaner(CClient* client, - ISecondaryScreen* screen) : - m_screen(screen) -{ - assert(m_screen != NULL); - try { - m_screen->open(client); - } - catch (...) { - delete m_screen; - throw; - } -} - -CClient::CScreenCleaner::~CScreenCleaner() -{ - m_screen->close(); - delete m_screen; -} diff --git a/client/CClient.h b/client/CClient.h index ddcd3a09..c332ae7c 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -24,6 +24,10 @@ class CClient { private: void runSession(void*); + // open/close the primary screen + void openSecondaryScreen(); + void closeSecondaryScreen(); + // message handlers void onEnter(); void onLeave(); @@ -40,16 +44,6 @@ class CClient { void onMouseMove(); void onMouseWheel(); - private: - class CScreenCleaner { - public: - CScreenCleaner(CClient*, ISecondaryScreen*); - ~CScreenCleaner(); - - private: - ISecondaryScreen* m_screen; - }; - private: CString m_name; IInputStream* m_input; diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp new file mode 100644 index 00000000..439b5876 --- /dev/null +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -0,0 +1,839 @@ +#include "CMSWindowsSecondaryScreen.h" +#include "CClient.h" +#include "CThread.h" +#include "CLog.h" +#include + +// +// CMSWindowsSecondaryScreen +// + +CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : + m_client(NULL), + m_window(NULL) +{ + // do nothing +} + +CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() +{ + assert(m_window == NULL); +} + +static HWND s_debug = NULL; +static HWND s_debugLog = NULL; +static DWORD s_thread = 0; +static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: + return TRUE; + + case WM_CLOSE: + PostQuitMessage(0); + return TRUE; + } + return FALSE; +} +static void debugOutput(const char* msg) +{ + if (s_thread != 0) { + const DWORD threadID = ::GetCurrentThreadId(); + if (threadID != s_thread) { + GetDesktopWindow(); + AttachThreadInput(threadID, s_thread, TRUE); + } + } + DWORD len = SendMessage(s_debugLog, WM_GETTEXTLENGTH, 0, 0); + if (len > 20000) { + SendMessage(s_debugLog, EM_SETSEL, -1, 0); + SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)msg); + } + else { + SendMessage(s_debugLog, EM_SETSEL, -1, len); + SendMessage(s_debugLog, EM_REPLACESEL, FALSE, (LPARAM)(LPCTSTR)msg); + } + SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0); +} + +void CMSWindowsSecondaryScreen::run() +{ +CLog::setOutputter(&debugOutput); + doRun(); +CLog::setOutputter(NULL); +} + +void CMSWindowsSecondaryScreen::stop() +{ + doStop(); +} + +void CMSWindowsSecondaryScreen::open(CClient* client) +{ + assert(m_client == NULL); + assert(client != NULL); + + // set the client + m_client = client; + + // open the display + openDisplay(); +} + +void CMSWindowsSecondaryScreen::close() +{ + assert(m_client != NULL); + + // close the display + closeDisplay(); + + // done with client + m_client = NULL; +} + +void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) +{ + assert(m_window != NULL); + + // warp to requested location + SInt32 w, h; + getScreenSize(&w, &h); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65535.99 * x) / (w - 1)), + (DWORD)((65535.99 * y) / (h - 1)), + 0, 0); + + // show cursor + ShowWindow(m_window, SW_HIDE); +} + +void CMSWindowsSecondaryScreen::leave() +{ + assert(m_window != NULL); + + // move hider window under the mouse (rather than moving the mouse + // somewhere else on the screen) + POINT point; + GetCursorPos(&point); + MoveWindow(m_window, point.x, point.y, 1, 1, FALSE); + + // raise and show the hider window. take activation. + ShowWindow(m_window, SW_SHOWNORMAL); + + // hide cursor by moving it into the hider window + SetCursorPos(point.x, point.y); +} + +void CMSWindowsSecondaryScreen::keyDown( + KeyID key, KeyModifierMask mask) +{ + const UINT vkey = mapKey(key, mask); + if (vkey != 0) { + const UINT code = MapVirtualKey(vkey, 0); + keybd_event(vkey, code, 0, 0); + } +} + +void CMSWindowsSecondaryScreen::keyRepeat( + KeyID key, KeyModifierMask mask, SInt32 count) +{ + const UINT vkey = mapKey(key, mask); + if (vkey != 0) { + const UINT code = MapVirtualKey(vkey, 0); + for (SInt32 i = 0; i < count; ++i) { + keybd_event(vkey, code, KEYEVENTF_KEYUP, 0); + keybd_event(vkey, code, 0, 0); + } + } +} + +void CMSWindowsSecondaryScreen::keyUp( + KeyID key, KeyModifierMask mask) +{ + const UINT vkey = mapKey(key, mask); + if (vkey != 0) { + const UINT code = MapVirtualKey(vkey, 0); + keybd_event(vkey, code, KEYEVENTF_KEYUP, 0); + } +} + +void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) +{ + // map button id to button flag + DWORD flags; + switch (button) { + case kButtonLeft: + flags = MOUSEEVENTF_LEFTDOWN; + break; + + case kButtonMiddle: + flags = MOUSEEVENTF_MIDDLEDOWN; + break; + + case kButtonRight: + flags = MOUSEEVENTF_RIGHTDOWN; + break; + + default: + return; + } + + // send event + mouse_event(flags, 0, 0, 0, 0); +} + +void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) +{ + // map button id to button flag + DWORD flags; + switch (button) { + case kButtonLeft: + flags = MOUSEEVENTF_LEFTUP; + break; + + case kButtonMiddle: + flags = MOUSEEVENTF_MIDDLEUP; + break; + + case kButtonRight: + flags = MOUSEEVENTF_RIGHTUP; + break; + + default: + return; + } + + // send event + mouse_event(flags, 0, 0, 0, 0); +} + +void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +{ + SInt32 w, h; + getScreenSize(&w, &h); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (SInt32)(65535.99 * x / (w - 1)), + (SInt32)(65535.99 * y / (h - 1)), + 0, 0); +} + +void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) +{ + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); +} + +void CMSWindowsSecondaryScreen::getSize( + SInt32* width, SInt32* height) const +{ + getScreenSize(width, height); +} + +SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + +#include "resource.h" // FIXME + +void CMSWindowsSecondaryScreen::onOpenDisplay() +{ + assert(m_window == NULL); + +// create debug dialog +s_thread = GetCurrentThreadId();; +s_debug = CreateDialog(getInstance(), MAKEINTRESOURCE(IDD_SYNERGY), NULL, &debugProc); +s_debugLog = ::GetDlgItem(s_debug, IDC_LOG); +CLog::setOutputter(&debugOutput); +ShowWindow(s_debug, SW_SHOWNORMAL); + + // create the cursor hiding window. this window is used to hide the + // cursor when it's not on the screen. the window is hidden as soon + // as the cursor enters the screen or the display's real cursor is + // moved. + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, + (LPCTSTR)getClass(), "Synergy", + WS_POPUP | WS_DISABLED, + 0, 0, 1, 1, NULL, NULL, + getInstance(), + NULL); + + // hide the cursor + leave(); +} + +void CMSWindowsSecondaryScreen::onCloseDisplay() +{ + assert(m_window != NULL); + + // destroy window + DestroyWindow(m_window); + m_window = NULL; + +CLog::setOutputter(NULL); +DestroyWindow(s_debug); +s_debug = NULL; +s_thread = 0; +} + +bool CMSWindowsSecondaryScreen::onEvent(MSG* msg) +{ +if (IsDialogMessage(s_debug, msg)) { + return true; +} + + // handle event + switch (msg->message) { + // FIXME -- handle display changes + case WM_PAINT: + ValidateRect(m_window, NULL); + return true; + + case WM_MOUSEMOVE: + // mouse was moved. hide the hider window. + ShowWindow(m_window, SW_HIDE); + break; + + case WM_ACTIVATEAPP: + if (msg->wParam == FALSE) { + // some other app activated. hide the hider window. + ShowWindow(m_window, SW_HIDE); + } + break; + +/* + // FIXME -- handle screen resolution changes + + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; +*/ + } + + return false; +} + +static const UINT g_latin1[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_latin2[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_latin3[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_latin4[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_latin5[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_latin6[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_latin7[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_latin8[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_latin9[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_terminal[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const UINT g_function[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +// FIXME -- will this work? +// 0x100 + = shift +// 0x200 + = ctrl +// 0x400 + = alt +/* XK_KP_Space to XK_KP_Equal */ +static const UINT g_miscellany[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ VK_BACK, VK_TAB, /*0x100 +*/ VK_RETURN, VK_CLEAR, 0, VK_RETURN, 0, 0, + /* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0, + /* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, + /* 0x54 */ VK_DOWN, VK_PRIOR, VK_NEXT, VK_END, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT, 0, 0, 0, VK_APPS, + /* 0x68 */ 0, 0, VK_HELP, VK_CANCEL, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, VK_MODECHANGE, VK_NUMLOCK, + /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, + /* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, + /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, + /* 0xac */ VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE, + /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, + /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, + /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, + /* 0xc0 */ VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, + /* 0xc8 */ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, + /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, + /* 0xe4 */ VK_RCONTROL, VK_CAPITAL, 0, VK_LWIN, + /* 0xe8 */ VK_RWIN, VK_LMENU, VK_RMENU, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE +}; +static const UINT* g_katakana = NULL; +static const UINT* g_arabic = NULL; +static const UINT* g_cyrillic = NULL; +static const UINT* g_greek = NULL; +static const UINT* g_technical = NULL; +static const UINT* g_special = NULL; +static const UINT* g_publishing = NULL; +static const UINT* g_apl = NULL; +static const UINT* g_hebrew = NULL; +static const UINT* g_thai = NULL; +static const UINT* g_korean = NULL; +static const UINT* g_armenian = NULL; +static const UINT* g_georgian = NULL; +static const UINT* g_azeri = NULL; +static const UINT* g_vietnamese = NULL; +static const UINT* g_currency = NULL; +static const UINT* g_mapTable[] = +{ + /* 0x00 */ g_latin1, g_latin2, g_latin3, g_latin4, + /* 0x04 */ g_katakana, g_arabic, g_cyrillic, g_greek, + /* 0x08 */ g_technical, g_special, g_publishing, g_apl, + /* 0x0c */ g_hebrew, g_thai, g_korean, NULL, + /* 0x10 */ NULL, NULL, g_latin8, g_latin9, + /* 0x14 */ g_armenian, g_georgian, g_azeri, NULL, + /* 0x18 */ NULL, NULL, NULL, NULL, NULL, NULL, g_vietnamese, NULL, + /* 0x20 */ g_currency, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x28 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x30 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x38 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x40 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x48 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x50 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x58 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x60 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x68 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x70 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x78 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x80 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x88 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x90 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0x98 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xa0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xa8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xb0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xb8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xc0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xc8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xd0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xd8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xe0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xe8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xf0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + /* 0xf8 */ NULL, NULL, NULL, NULL, + /* 0xfc */ NULL, g_terminal, g_function, g_miscellany +}; + +UINT CMSWindowsSecondaryScreen::mapKey( + KeyID id, KeyModifierMask /*mask*/) const +{ + const UInt32 mapID = ((id >> 8) & 0xff); + const UInt32 code = (id & 0xff); + + // lookup the key table + const UINT* map = g_mapTable[mapID]; + if (map == NULL) { + return 0; + } + + if (mapID == 0) { + SHORT scan = VkKeyScan(code); + if (scan != 0xffff) { + // FIXME -- must ensure shift state is correct. that means + // tracking the shift state from the moment we enter until + // the moment we leave (and probably disallowing leave if + // any shift keys are down). if current shift state is + // correct then do nothing extra, otherwise must surround + // injected key event with injected shift key events to + // get shift key in correct state then back to the previous + // state. + return (UINT)LOBYTE(scan); + } + } + + // lookup the key in the table + return map[code]; +} diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h new file mode 100644 index 00000000..4362d400 --- /dev/null +++ b/client/CMSWindowsSecondaryScreen.h @@ -0,0 +1,43 @@ +#ifndef CMSWINDOWSSECONDARYSCREEN_H +#define CMSWINDOWSSECONDARYSCREEN_H + +#include "CMSWindowsScreen.h" +#include "ISecondaryScreen.h" + +class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen { + public: + CMSWindowsSecondaryScreen(); + virtual ~CMSWindowsSecondaryScreen(); + + // ISecondaryScreen overrides + virtual void run(); + virtual void stop(); + virtual void open(CClient*); + virtual void close(); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void leave(); + virtual void keyDown(KeyID, KeyModifierMask); + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void keyUp(KeyID, KeyModifierMask); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void mouseWheel(SInt32 delta); + virtual void getSize(SInt32* width, SInt32* height) const; + virtual SInt32 getJumpZoneSize() const; + + protected: + // CMSWindowsScreen overrides + virtual bool onEvent(MSG*); + virtual void onOpenDisplay(); + virtual void onCloseDisplay(); + + private: + UINT mapKey(KeyID, KeyModifierMask) const; + + private: + CClient* m_client; + HWND m_window; +}; + +#endif diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 853855a6..d4b99bd9 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -22,6 +22,51 @@ CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() assert(m_window == None); } +void CXWindowsSecondaryScreen::run() +{ + assert(m_window != None); + + for (;;) { + // wait for and get the next event + XEvent xevent; + if (!getEvent(&xevent)) { + break; + } + + // handle event + switch (xevent.type) { + case LeaveNotify: { + // mouse moved out of hider window somehow. hide the window. + assert(m_window != None); + CDisplayLock display(this); + XUnmapWindow(display, m_window); + break; + } + +/* + // FIXME -- handle screen resolution changes + + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; +*/ + } + } +} + +void CXWindowsSecondaryScreen::stop() +{ + doStop(); +} + void CXWindowsSecondaryScreen::open(CClient* client) { assert(m_client == NULL); @@ -174,44 +219,6 @@ void CXWindowsSecondaryScreen::onCloseDisplay() m_window = None; } -void CXWindowsSecondaryScreen::eventThread(void*) -{ - assert(m_window != None); - - for (;;) { - // wait for and get the next event - XEvent xevent; - getEvent(&xevent); - - // handle event - switch (xevent.type) { - case LeaveNotify: { - // mouse moved out of hider window somehow. hide the window. - assert(m_window != None); - CDisplayLock display(this); - XUnmapWindow(display, m_window); - break; - } - -/* - // FIXME -- handle screen resolution changes - - case SelectionClear: - target->XXX(xevent.xselectionclear.); - break; - - case SelectionNotify: - target->XXX(xevent.xselection.); - break; - - case SelectionRequest: - target->XXX(xevent.xselectionrequest.); - break; -*/ - } - } -} - void CXWindowsSecondaryScreen::leaveNoLock(Display* display) { assert(display != NULL); diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index ef180537..2ee6989b 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -10,6 +10,8 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen virtual ~CXWindowsSecondaryScreen(); // ISecondaryScreen overrides + virtual void run(); + virtual void stop(); virtual void open(CClient*); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); @@ -28,7 +30,6 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen // CXWindowsScreen overrides virtual void onOpenDisplay(); virtual void onCloseDisplay(); - virtual void eventThread(void*); private: void leaveNoLock(Display*); diff --git a/client/Makefile b/client/Makefile index 6c0b49cd..e7a6573a 100644 --- a/client/Makefile +++ b/client/Makefile @@ -4,7 +4,7 @@ include $(DEPTH)/Makecommon # # target file # -TARGET = client +TARGETS = client # # source files @@ -37,8 +37,8 @@ LLDLIBS = \ -lpthread \ $(NULL) -targets: $(TARGET) +targets: $(TARGETS) -$(TARGET): $(OBJECTS) $(DEPLIBS) +$(TARGETS): $(OBJECTS) $(DEPLIBS) $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) diff --git a/client/client.cpp b/client/client.cpp index 4ada77d9..abca2c8f 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -1,24 +1,76 @@ #include "CClient.h" +#include "CString.h" +#include "CNetwork.h" #include "CNetworkAddress.h" #include "CThread.h" + +void realMain(const CString& name, + const CString& hostname, + SInt32 port) +{ + CThread::init(); + CNetwork::init(); + + CClient* client = NULL; + try { + CNetworkAddress addr(hostname, port); + client = new CClient(name); + client->run(addr); + delete client; + CNetwork::cleanup(); + } + catch (...) { + delete client; + CNetwork::cleanup(); + throw; + } +} + +#if defined(CONFIG_PLATFORM_WIN32) + +#include "CMSWindowsScreen.h" + +int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +{ + CMSWindowsScreen::init(instance); + + if (__argc != 2) { + CString msg = "hostname required. exiting."; + MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); + return 1; + } + + try { + realMain("ingrid", __argv[1], 50001); + return 0; + } + catch (XBase& e) { + CString msg = "failed: "; + msg += e.what(); + MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); + return 1; + } +} + +#else + #include int main(int argc, char** argv) { - CThread::init(); - if (argc != 2) { fprintf(stderr, "usage: %s \n", argv[0]); return 1; } try { - CClient* client = new CClient("ingrid"); - client->run(CNetworkAddress(argv[1], 50001)); + realMain("ingrid", argv[1], 50001); + return 0; } catch (XBase& e) { fprintf(stderr, "failed: %s\n", e.what()); return 1; } - return 0; } + +#endif diff --git a/client/client.dsp b/client/client.dsp new file mode 100644 index 00000000..905b486a --- /dev/null +++ b/client/client.dsp @@ -0,0 +1,127 @@ +# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=client - Win32 Debug +!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 "client.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 "client.mak" CFG="client - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "client - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "client - 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" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "client - 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" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "client - Win32 Release" +# Name "client - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\client.cpp +# End Source File +# Begin Source File + +SOURCE=.\client.rc +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClient.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/client/client.rc b/client/client.rc new file mode 100644 index 00000000..60c3a920 --- /dev/null +++ b/client/client.rc @@ -0,0 +1,97 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_SYNERGY DIALOG DISCARDABLE 0, 0, 329, 158 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Synergy" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_LOG,7,7,315,144,ES_MULTILINE | ES_AUTOHSCROLL | + ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_SYNERGY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 322 + TOPMARGIN, 7 + BOTTOMMARGIN, 151 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/client/resource.h b/client/resource.h new file mode 100644 index 00000000..993c9edc --- /dev/null +++ b/client/resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by client.rc +// +#define IDD_SYNERGY 101 +#define IDC_LOG 1000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/io/io.dsp b/io/io.dsp new file mode 100644 index 00000000..b06989a9 --- /dev/null +++ b/io/io.dsp @@ -0,0 +1,150 @@ +# Microsoft Developer Studio Project File - Name="io" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=io - Win32 Debug +!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 "io.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 "io.mak" CFG="io - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "io - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "io - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "io - 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" +# 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 /O2 /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "io - 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" +# 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 "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "io - Win32 Release" +# Name "io - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CBufferedInputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBufferedOutputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInputStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStreamBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=.\XIO.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CBufferedInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CBufferedOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CInputStreamFilter.h +# End Source File +# Begin Source File + +SOURCE=.\COutputStreamFilter.h +# End Source File +# Begin Source File + +SOURCE=.\CStreamBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\IInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\IOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\XIO.h +# End Source File +# End Group +# End Target +# End Project diff --git a/mt/CCondVar.cpp b/mt/CCondVar.cpp index f6072058..7e39e7f4 100644 --- a/mt/CCondVar.cpp +++ b/mt/CCondVar.cpp @@ -238,7 +238,7 @@ bool CCondVarBase::wait( } // prepare to wait - CRefCountedPtr currentRep(CThreadRep::getCurrentThreadRep()); + CThreadPtr currentRep = CThreadRep::getCurrentThreadRep(); const DWORD winTimeout = (timeout < 0.0) ? INFINITE : static_cast(1000.0 * timeout); HANDLE* events = reinterpret_cast(m_cond); diff --git a/mt/CThread.cpp b/mt/CThread.cpp index 6e40060b..c9a07fa4 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -5,26 +5,6 @@ #include "CStopwatch.h" #include "CLog.h" -// -// CThreadPtr -// - -class CThreadPtr { - public: - CThreadPtr(CThreadRep* rep) : m_rep(rep) { } - ~CThreadPtr() { m_rep->unref(); } - - CThreadRep* operator->() const { return m_rep; } - - private: - // not implemented - CThreadPtr(const CThreadPtr&); - CThreadPtr& operator=(const CThreadPtr&); - - private: - CThreadRep* m_rep; -}; - // // CThread // diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index dcba9e9f..313b33b4 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -12,10 +12,17 @@ #define SIGWAKEUP SIGUSR1 #endif +#if defined(CONFIG_PLATFORM_WIN32) +# if !defined(_MT) +# error multithreading compile option is required +# endif +#include +#endif + // FIXME -- temporary exception type class XThreadUnavailable { }; -#ifndef NDEBUG +#if defined(CONFIG_PLATFORM_UNIX) && !defined(NDEBUG) #include #include #include @@ -146,19 +153,19 @@ void CThreadRep::initThreads() act.sa_handler = &threadDebug; sigaction(SIGSEGV, &act, NULL); # endif -#endif // set signal mask sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGWAKEUP); -#ifndef NDEBUG +# ifndef NDEBUG sigaddset(&sigset, SIGSEGV); -#endif +# endif pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); sigemptyset(&sigset); sigaddset(&sigset, SIGPIPE); pthread_sigmask(SIG_BLOCK, &sigset, NULL); +#endif } } @@ -255,7 +262,7 @@ void CThreadRep::doThreadFunc() m_job->run(); } - catch (XThreadCancel& e) { + catch (XThreadCancel&) { // client called cancel() log((CLOG_DEBUG "caught cancel on thread %p", this)); } @@ -421,8 +428,6 @@ void CThreadRep::threadCancel(int) #elif defined(CONFIG_PLATFORM_WIN32) -#include - void CThreadRep::init() { m_result = NULL; @@ -485,7 +490,7 @@ bool CThreadRep::wait(CThreadRep* target, double timeout) { // get the current thread. if it's the same as the target thread // then the thread is waiting on itself. - CRefCountedPtr currentRep(CThreadRep::getCurrentThreadRep()); + CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); if (target == this) return false; diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h index 3cfce4b9..3a14ba09 100644 --- a/mt/CThreadRep.h +++ b/mt/CThreadRep.h @@ -122,4 +122,24 @@ class CThreadRep { #endif }; +// +// CThreadPtr -- auto unref'ing pointer to thread rep +// + +class CThreadPtr { + public: + CThreadPtr(CThreadRep* rep) : m_rep(rep) { } + ~CThreadPtr() { m_rep->unref(); } + + CThreadRep* operator->() const { return m_rep; } + + private: + // not implemented + CThreadPtr(const CThreadPtr&); + CThreadPtr& operator=(const CThreadPtr&); + + private: + CThreadRep* m_rep; +}; + #endif diff --git a/mt/mt.dsp b/mt/mt.dsp new file mode 100644 index 00000000..8067b7d7 --- /dev/null +++ b/mt/mt.dsp @@ -0,0 +1,146 @@ +# Microsoft Developer Studio Project File - Name="mt" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=mt - Win32 Debug +!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 "mt.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 "mt.mak" CFG="mt - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mt - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "mt - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mt - 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" +# 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 /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "mt - 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" +# 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 "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "mt - Win32 Release" +# Name "mt - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CCondVar.cpp +# End Source File +# Begin Source File + +SOURCE=.\CLock.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMutex.cpp +# End Source File +# Begin Source File + +SOURCE=.\CThread.cpp +# End Source File +# Begin Source File + +SOURCE=.\CThreadRep.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CCondVar.h +# End Source File +# Begin Source File + +SOURCE=.\CLock.h +# End Source File +# Begin Source File + +SOURCE=.\CMutex.h +# End Source File +# Begin Source File + +SOURCE=.\CThread.h +# End Source File +# Begin Source File + +SOURCE=.\CThreadRep.h +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.h +# End Source File +# Begin Source File + +SOURCE=.\XThread.h +# End Source File +# End Group +# End Target +# End Project diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp new file mode 100644 index 00000000..4eac18f5 --- /dev/null +++ b/net/CNetwork.cpp @@ -0,0 +1,339 @@ +#include "CNetwork.h" +#include "XNetwork.h" +#include "CLog.h" +#include + +// +// CNetwork +// + +CNetwork::Socket (PASCAL FAR *CNetwork::accept)(CNetwork::Socket s, CNetwork::Address FAR *addr, CNetwork::AddressLength FAR *addrlen); +int (PASCAL FAR *CNetwork::bind)(CNetwork::Socket s, const CNetwork::Address FAR *addr, CNetwork::AddressLength namelen); +int (PASCAL FAR *CNetwork::close)(CNetwork::Socket s); +int (PASCAL FAR *CNetwork::connect)(CNetwork::Socket s, const CNetwork::Address FAR *name, CNetwork::AddressLength namelen); +int (PASCAL FAR *CNetwork::ioctl)(CNetwork::Socket s, int cmd, ...); +int (PASCAL FAR *CNetwork::getpeername)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); +int (PASCAL FAR *CNetwork::getsockname)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); +int (PASCAL FAR *CNetwork::getsockopt)(CNetwork::Socket s, int level, int optname, void FAR * optval, CNetwork::AddressLength FAR *optlen); +UInt32 (PASCAL FAR *CNetwork::swaphtonl)(UInt32 hostlong); +UInt16 (PASCAL FAR *CNetwork::swaphtons)(UInt16 hostshort); +unsigned long (PASCAL FAR *CNetwork::inet_addr)(const char FAR * cp); +char FAR * (PASCAL FAR *CNetwork::inet_ntoa)(struct in_addr in); +int (PASCAL FAR *CNetwork::listen)(CNetwork::Socket s, int backlog); +UInt32 (PASCAL FAR *CNetwork::swapntohl)(UInt32 netlong); +UInt16 (PASCAL FAR *CNetwork::swapntohs)(UInt16 netshort); +ssize_t (PASCAL FAR *CNetwork::read)(CNetwork::Socket s, void FAR * buf, size_t len); +ssize_t (PASCAL FAR *CNetwork::recv)(CNetwork::Socket s, void FAR * buf, size_t len, int flags); +ssize_t (PASCAL FAR *CNetwork::recvfrom)(CNetwork::Socket s, void FAR * buf, size_t len, int flags, CNetwork::Address FAR *from, CNetwork::AddressLength FAR * fromlen); +int (PASCAL FAR *CNetwork::poll)(CNetwork::PollEntry fds[], int nfds, int timeout); +ssize_t (PASCAL FAR *CNetwork::send)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags); +ssize_t (PASCAL FAR *CNetwork::sendto)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags, const CNetwork::Address FAR *to, CNetwork::AddressLength tolen); +int (PASCAL FAR *CNetwork::setsockopt)(CNetwork::Socket s, int level, int optname, const void FAR * optval, CNetwork::AddressLength optlen); +int (PASCAL FAR *CNetwork::shutdown)(CNetwork::Socket s, int how); +CNetwork::Socket (PASCAL FAR *CNetwork::socket)(int af, int type, int protocol); +ssize_t (PASCAL FAR *CNetwork::write)(CNetwork::Socket s, const void FAR * buf, size_t len); +struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyaddr)(const char FAR * addr, int len, int type); +struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyname)(const char FAR * name); +int (PASCAL FAR *CNetwork::gethostname)(char FAR * name, int namelen); +struct servent FAR * (PASCAL FAR *CNetwork::getservbyport)(int port, const char FAR * proto); +struct servent FAR * (PASCAL FAR *CNetwork::getservbyname)(const char FAR * name, const char FAR * proto); +struct protoent FAR * (PASCAL FAR *CNetwork::getprotobynumber)(int proto); +struct protoent FAR * (PASCAL FAR *CNetwork::getprotobyname)(const char FAR * name); +int (PASCAL FAR *CNetwork::getsockerror)(void); +int (PASCAL FAR *CNetwork::gethosterror)(void); + +#if defined(CONFIG_PLATFORM_WIN32) + +int (PASCAL FAR *CNetwork::WSACleanup)(void); +int (PASCAL FAR *CNetwork::__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); +const int CNetwork::Error = SOCKET_ERROR; +const CNetwork::Socket CNetwork::Null = INVALID_SOCKET; + +#undef FD_ISSET +#define FD_ISSET(fd, set) CNetwork::__WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set)) + +static HMODULE s_networkModule = NULL; + +static FARPROC netGetProcAddress(HMODULE module, LPCSTR name) +{ + FARPROC func = ::GetProcAddress(module, name); + if (!func) + throw XNetworkFunctionUnavailable(name); + return func; +} + +void CNetwork::init() +{ + assert(WSACleanup == NULL); + assert(s_networkModule == NULL); + + // try winsock 2 + HMODULE module = (HMODULE)::LoadLibrary("ws2_32.dll"); + if (module == NULL) { + log((CLOG_DEBUG "ws2_32.dll not found")); + } + else { + try { + init2(module); + return; + } + catch (XNetwork& e) { + log((CLOG_DEBUG "ws2_32.dll error: %s", e.what())); + } + } + + // try winsock 1 + module = (HMODULE)::LoadLibrary("wsock32.dll"); + if (module == NULL) { + log((CLOG_DEBUG "wsock32.dll not found")); + } + else { + try { + init2(module); + return; + } + catch (XNetwork& e) { + log((CLOG_DEBUG "wsock32.dll error: %s", e.what())); + } + } + + // no networking + throw XNetworkUnavailable(); +} + +void CNetwork::cleanup() +{ + if (s_networkModule != NULL) { + WSACleanup(); + ::FreeLibrary(s_networkModule); + + WSACleanup = NULL; + s_networkModule = NULL; + } +} + +#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name) + +void CNetwork::init2(HMODULE module) +{ + assert(module != NULL); + + // get startup function address + int (PASCAL FAR *startup)(WORD, LPWSADATA); + setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA)); + + // startup network library + WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); + WSADATA data; + int err = startup(version, &data); + if (data.wVersion != version) + throw XNetworkVersion(LOBYTE(data.wVersion), HIBYTE(data.wVersion)); + if (err != 0) + throw XNetworkFailed(); + + // get function addresses + setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); + setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); + setfunc(close, closesocket, int (PASCAL FAR *)(Socket s)); + setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); + setfunc(ioctl, ioctlsocket, int (PASCAL FAR *)(Socket s, int cmd, ...)); + setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); + setfunc(swaphtonl, htonl, UInt32 (PASCAL FAR *)(UInt32 hostlong)); + setfunc(swaphtons, htons, UInt16 (PASCAL FAR *)(UInt16 hostshort)); + setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); + setfunc(swapntohl, ntohl, UInt32 (PASCAL FAR *)(UInt32 netlong)); + setfunc(swapntohs, ntohs, UInt16 (PASCAL FAR *)(UInt16 netshort)); + setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); + setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); + setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); + setfunc(sendto, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); + setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); + setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); + setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(gethostbyaddr, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(gethostname, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); + setfunc(getservbyport, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); + setfunc(getservbyname, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); + setfunc(getprotobynumber, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); + setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(getsockerror, WSAGetLastError, int (PASCAL FAR *)(void)); + setfunc(gethosterror, WSAGetLastError, int (PASCAL FAR *)(void)); + setfunc(WSACleanup, WSACleanup, int (PASCAL FAR *)(void)); + setfunc(__WSAFDIsSet, __WSAFDIsSet, int (PASCAL FAR *)(CNetwork::Socket, fd_set FAR *)); + setfunc(select, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); + poll = poll2; + read = read2; + write = write2; + + s_networkModule = module; +} + +int PASCAL FAR CNetwork::poll2(PollEntry fd[], int nfds, int timeout) +{ + int i; + + // prepare sets for select + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < nfds; ++i) { + if (fd[i].events & kPOLLIN) { + FD_SET(fd[i].fd, &readSet); + readSetP = &readSet; + } + if (fd[i].events & kPOLLOUT) { + FD_SET(fd[i].fd, &writeSet); + writeSetP = &writeSet; + } + if (true) { + FD_SET(fd[i].fd, &errSet); + errSetP = &errSet; + } + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = timeout / 1000; + timeout2.tv_usec = 1000 * (timeout % 1000); + } + + // do the select. note that winsock ignores the first argument. + int n = select(0, readSetP, writeSetP, errSetP, timeout2P); + + // handle results + if (n == Error) + return Error; + if (n == 0) + return 0; + for (i = 0; i < nfds; ++i) { + fd[i].revents = 0; + if (FD_ISSET(fd[i].fd, &readSet)) + fd[i].revents |= kPOLLIN; + if (FD_ISSET(fd[i].fd, &writeSet)) + fd[i].revents |= kPOLLOUT; + if (FD_ISSET(fd[i].fd, &errSet)) + fd[i].revents |= kPOLLERR; + } + return n; +} + +ssize_t PASCAL FAR CNetwork::read2(Socket s, void FAR * buf, size_t len) +{ + return recv(s, buf, len, 0); +} + +ssize_t PASCAL FAR CNetwork::write2(Socket s, + const void FAR * buf, size_t len) +{ + return send(s, buf, len, 0); +} + +#endif + +#if defined(CONFIG_PLATFORM_UNIX) + +#include +#include +#include + +// FIXME -- use reentrant versions of non-reentrant functions + +#define setfunc(var, name, type) var = (type)::name + +static UInt32 myhtonl(UInt32 v) +{ + return htonl(v); +} + +static UInt16 myhtons(UInt16 v) +{ + return htons(v); +} + +static UInt32 myntohl(UInt32 v) +{ + return ntohl(v); +} + +static UInt16 myntohs(UInt16 v) +{ + return ntohs(v); +} + +static int myerrno() +{ + return errno; +} + +static int myherrno() +{ + return h_errno; +} + +static int mygethostname(char* name, int namelen) +{ + return gethostname(name, namelen); +} + +const int CNetwork::Error = -1; +const CNetwork::Socket CNetwork::Null = -1; + +void CNetwork::init() +{ + setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); + setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); + setfunc(close, close, int (PASCAL FAR *)(Socket s)); + setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); + setfunc(ioctl, ioctl, int (PASCAL FAR *)(Socket s, int cmd, ...)); + setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); + setfunc(swaphtonl, myhtonl, UInt32 (PASCAL FAR *)(UInt32 hostlong)); + setfunc(swaphtons, myhtons, UInt16 (PASCAL FAR *)(UInt16 hostshort)); + setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); + setfunc(swapntohl, myntohl, UInt32 (PASCAL FAR *)(UInt32 netlong)); + setfunc(swapntohs, myntohs, UInt16 (PASCAL FAR *)(UInt16 netshort)); + setfunc(poll, poll, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); + setfunc(read, read, ssize_t (PASCAL FAR *)(CNetwork::Socket s, void FAR * buf, size_t len)); + setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); + setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); + setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); + setfunc(sendto, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); + setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); + setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); + setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(write, write, ssize_t (PASCAL FAR *)(CNetwork::Socket s, const void FAR * buf, size_t len)); + setfunc(gethostbyaddr, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(gethostname, mygethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); + setfunc(getservbyport, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); + setfunc(getservbyname, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); + setfunc(getprotobynumber, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); + setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(getsockerror, myerrno, int (PASCAL FAR *)(void)); + setfunc(gethosterror, myherrno, int (PASCAL FAR *)(void)); +} + +void CNetwork::cleanup() +{ + // do nothing +} + +#endif diff --git a/net/CNetwork.h b/net/CNetwork.h new file mode 100644 index 00000000..fdbb2ae2 --- /dev/null +++ b/net/CNetwork.h @@ -0,0 +1,143 @@ +#ifndef CNETWORK_H +#define CNETWORK_H + +#include "BasicTypes.h" + +#if defined(CONFIG_PLATFORM_WIN32) +// declare no functions in winsock2 +# define INCL_WINSOCK_API_PROTOTYPES 0 +# define INCL_WINSOCK_API_TYPEDEFS 0 +# include +typedef int ssize_t; +#else +# define FAR +# define PASCAL +#endif + +#if defined(CONFIG_PLATFORM_UNIX) +# include +# include +# include +# include +# include +# include +#endif + +// FIXME -- must handle htonl and ilk when defined as macros + +class CNetwork { + public: +#if defined(CONFIG_PLATFORM_WIN32) + typedef SOCKET Socket; + typedef struct sockaddr Address; + typedef int AddressLength; + struct PollEntry { + Socket fd; + short events; + short revents; + }; + enum { + kPOLLIN = 1, + kPOLLOUT = 2, + kPOLLERR = 4, + kPOLLNVAL = 8 + }; +#elif defined(CONFIG_PLATFORM_UNIX) + typedef int Socket; + typedef struct sockaddr Address; + typedef socklen_t AddressLength; + typedef struct pollfd PollEntry; + enum { + kPOLLIN = POLLIN, + kPOLLOUT = POLLOUT, + kPOLLERR = POLLERR, + kPOLLNVAL = POLLNVAL + }; +#endif + + // manipulators + + static void init(); + static void cleanup(); + + // constants + + static const int Error; + static const Socket Null; + + // getsockerror() constants + enum { +#if defined(CONFIG_PLATFORM_WIN32) + kEADDRINUSE = WSAEADDRINUSE, +#elif defined(CONFIG_PLATFORM_UNIX) + kEADDRINUSE = EADDRINUSE, +#endif + kNone = 0 + }; + + // gethosterror() constants + enum { +#if defined(CONFIG_PLATFORM_WIN32) + kHOST_NOT_FOUND = WSAHOST_NOT_FOUND, + kNO_DATA = WSANO_DATA, + kNO_RECOVERY = WSANO_RECOVERY, + kTRY_AGAIN = WSATRY_AGAIN, +#elif defined(CONFIG_PLATFORM_UNIX) + kHOST_NOT_FOUND = HOST_NOT_FOUND, + kNO_DATA = NO_DATA, + kNO_RECOVERY = NO_RECOVERY, + kTRY_AGAIN = TRY_AGAIN, +#endif + kHNone = 0 + }; + + // socket interface + + static Socket (PASCAL FAR *accept)(Socket s, Address FAR *addr, AddressLength FAR *addrlen); + static int (PASCAL FAR *bind)(Socket s, const Address FAR *addr, AddressLength namelen); + static int (PASCAL FAR *close)(Socket s); + static int (PASCAL FAR *connect)(Socket s, const Address FAR *name, AddressLength namelen); + static int (PASCAL FAR *ioctl)(Socket s, int cmd, ...); + static int (PASCAL FAR *getpeername)(Socket s, Address FAR *name, AddressLength FAR * namelen); + static int (PASCAL FAR *getsockname)(Socket s, Address FAR *name, AddressLength FAR * namelen); + static int (PASCAL FAR *getsockopt)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen); + static UInt32 (PASCAL FAR *swaphtonl)(UInt32 hostlong); + static UInt16 (PASCAL FAR *swaphtons)(UInt16 hostshort); + static unsigned long (PASCAL FAR *inet_addr)(const char FAR * cp); + static char FAR * (PASCAL FAR *inet_ntoa)(struct in_addr in); + static int (PASCAL FAR *listen)(Socket s, int backlog); + static UInt32 (PASCAL FAR *swapntohl)(UInt32 netlong); + static UInt16 (PASCAL FAR *swapntohs)(UInt16 netshort); + static ssize_t (PASCAL FAR *read)(Socket s, void FAR * buf, size_t len); + static ssize_t (PASCAL FAR *recv)(Socket s, void FAR * buf, size_t len, int flags); + static ssize_t (PASCAL FAR *recvfrom)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen); + static int (PASCAL FAR *poll)(PollEntry[], int nfds, int timeout); + static ssize_t (PASCAL FAR *send)(Socket s, const void FAR * buf, size_t len, int flags); + static ssize_t (PASCAL FAR *sendto)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen); + static int (PASCAL FAR *setsockopt)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen); + static int (PASCAL FAR *shutdown)(Socket s, int how); + static Socket (PASCAL FAR *socket)(int af, int type, int protocol); + static ssize_t (PASCAL FAR *write)(Socket s, const void FAR * buf, size_t len); + static struct hostent FAR * (PASCAL FAR *gethostbyaddr)(const char FAR * addr, int len, int type); + static struct hostent FAR * (PASCAL FAR *gethostbyname)(const char FAR * name); + static int (PASCAL FAR *gethostname)(char FAR * name, int namelen); + static struct servent FAR * (PASCAL FAR *getservbyport)(int port, const char FAR * proto); + static struct servent FAR * (PASCAL FAR *getservbyname)(const char FAR * name, const char FAR * proto); + static struct protoent FAR * (PASCAL FAR *getprotobynumber)(int proto); + static struct protoent FAR * (PASCAL FAR *getprotobyname)(const char FAR * name); + static int (PASCAL FAR *getsockerror)(void); + static int (PASCAL FAR *gethosterror)(void); + +#if defined(CONFIG_PLATFORM_WIN32) + private: + static void init2(HMODULE); + static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout); + static ssize_t PASCAL FAR read2(Socket s, void FAR * buf, size_t len); + static ssize_t PASCAL FAR write2(Socket s, const void FAR * buf, size_t len); + static int (PASCAL FAR *WSACleanup)(void); + static int (PASCAL FAR *__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); + static int (PASCAL FAR *select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); +#endif +}; + +#endif diff --git a/net/CNetworkAddress.cpp b/net/CNetworkAddress.cpp index 84c7f3ec..a07d5ac7 100644 --- a/net/CNetworkAddress.cpp +++ b/net/CNetworkAddress.cpp @@ -1,8 +1,4 @@ #include "CNetworkAddress.h" -#include -#include -#include -#include // // CNetworkAddress @@ -15,7 +11,7 @@ CNetworkAddress::CNetworkAddress(UInt16 port) struct sockaddr_in* inetAddress = reinterpret_cast(&m_address); inetAddress->sin_family = AF_INET; - inetAddress->sin_port = htons(port); + inetAddress->sin_port = CNetwork::swaphtons(port); inetAddress->sin_addr.s_addr = INADDR_ANY; memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } @@ -25,17 +21,17 @@ CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) if (port == 0) throw XSocketAddress(XSocketAddress::kBadPort, hostname, port); - struct hostent* hent = gethostbyname(hostname.c_str()); + struct hostent* hent = CNetwork::gethostbyname(hostname.c_str()); if (hent == NULL) { - switch (h_errno) { - case HOST_NOT_FOUND: + switch (CNetwork::gethosterror()) { + case CNetwork::kHOST_NOT_FOUND: throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); - case NO_DATA: + case CNetwork::kNO_DATA: throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); - case NO_RECOVERY: - case TRY_AGAIN: + case CNetwork::kNO_RECOVERY: + case CNetwork::kTRY_AGAIN: default: throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); } @@ -43,7 +39,7 @@ CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) struct sockaddr_in* inetAddress = reinterpret_cast(&m_address); inetAddress->sin_family = hent->h_addrtype; - inetAddress->sin_port = htons(port); + inetAddress->sin_port = CNetwork::swaphtons(port); memcpy(&inetAddress->sin_addr, hent->h_addr_list[0], hent->h_length); memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } @@ -53,12 +49,12 @@ CNetworkAddress::~CNetworkAddress() // do nothing } -const struct sockaddr* CNetworkAddress::getAddress() const +const CNetwork::Address* CNetworkAddress::getAddress() const { return &m_address; } -int CNetworkAddress::getAddressLength() const +CNetwork::AddressLength CNetworkAddress::getAddressLength() const { return sizeof(m_address); } diff --git a/net/CNetworkAddress.h b/net/CNetworkAddress.h index 95e377ce..18d69812 100644 --- a/net/CNetworkAddress.h +++ b/net/CNetworkAddress.h @@ -2,8 +2,8 @@ #define CNETWORKADDRESS_H #include "BasicTypes.h" +#include "CNetwork.h" #include "XSocket.h" -#include class CString; @@ -17,11 +17,11 @@ class CNetworkAddress { // accessors - const struct sockaddr* getAddress() const; - int getAddressLength() const; + const CNetwork::Address* getAddress() const; + CNetwork::AddressLength getAddressLength() const; private: - struct sockaddr m_address; + CNetwork::Address m_address; }; #endif diff --git a/net/CTCPListenSocket.cpp b/net/CTCPListenSocket.cpp index d7fdaa93..cb95baf7 100644 --- a/net/CTCPListenSocket.cpp +++ b/net/CTCPListenSocket.cpp @@ -2,11 +2,6 @@ #include "CTCPSocket.h" #include "CNetworkAddress.h" #include "CThread.h" -#include -#include -#include -#include -#include // // CTCPListenSocket @@ -14,8 +9,8 @@ CTCPListenSocket::CTCPListenSocket() { - m_fd = socket(PF_INET, SOCK_STREAM, 0); - if (m_fd == -1) { + m_fd = CNetwork::socket(PF_INET, SOCK_STREAM, 0); + if (m_fd == CNetwork::Null) { throw XSocketCreate(); } } @@ -33,40 +28,44 @@ CTCPListenSocket::~CTCPListenSocket() void CTCPListenSocket::bind( const CNetworkAddress& addr) { - if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { - if (errno == EADDRINUSE) { + if (CNetwork::bind(m_fd, addr.getAddress(), + addr.getAddressLength()) == CNetwork::Error) { + if (CNetwork::getsockerror() == CNetwork::kEADDRINUSE) { throw XSocketAddressInUse(); } throw XSocketBind(); } - if (listen(m_fd, 3) == -1) { + if (CNetwork::listen(m_fd, 3) == CNetwork::Error) { throw XSocketBind(); } } ISocket* CTCPListenSocket::accept() { + CNetwork::PollEntry pfds[1]; + pfds[0].fd = m_fd; + pfds[0].events = CNetwork::kPOLLIN; for (;;) { - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); CThread::testCancel(); - int fd = ::accept(m_fd, &addr, &addrlen); - if (fd == -1) { - CThread::testCancel(); - } - else { - return new CTCPSocket(fd); + const int status = CNetwork::poll(pfds, 1, 50); + if (status > 0 && (pfds[0].revents & CNetwork::kPOLLIN) != 0) { + CNetwork::Address addr; + CNetwork::AddressLength addrlen = sizeof(addr); + int fd = CNetwork::accept(m_fd, &addr, &addrlen); + if (fd != CNetwork::Null) { + return new CTCPSocket(fd); + } } } } void CTCPListenSocket::close() { - if (m_fd == -1) { + if (m_fd == CNetwork::Null) { throw XIOClosed(); } - if (::close(m_fd) == -1) { + if (CNetwork::close(m_fd) == CNetwork::Error) { throw XIOClose(); } - m_fd = -1; + m_fd = CNetwork::Null; } diff --git a/net/CTCPListenSocket.h b/net/CTCPListenSocket.h index 30310b52..20031d17 100644 --- a/net/CTCPListenSocket.h +++ b/net/CTCPListenSocket.h @@ -2,6 +2,7 @@ #define CTCPLISTENSOCKET_H #include "IListenSocket.h" +#include "CNetwork.h" class CTCPListenSocket : public IListenSocket { public: @@ -18,7 +19,7 @@ class CTCPListenSocket : public IListenSocket { virtual void close(); private: - int m_fd; + CNetwork::Socket m_fd; }; #endif diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index 39defc3d..ce32c846 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -8,12 +8,6 @@ #include "CThread.h" #include "TMethodJob.h" #include "CStopwatch.h" -#include -#include -#include -#include -#include -#include #include // @@ -22,16 +16,16 @@ CTCPSocket::CTCPSocket() { - m_fd = socket(PF_INET, SOCK_STREAM, 0); - if (m_fd == -1) { + m_fd = CNetwork::socket(PF_INET, SOCK_STREAM, 0); + if (m_fd == CNetwork::Null) { throw XSocketCreate(); } init(); } -CTCPSocket::CTCPSocket(int fd) : m_fd(fd) +CTCPSocket::CTCPSocket(CNetwork::Socket fd) : m_fd(fd) { - assert(m_fd != -1); + assert(m_fd != CNetwork::Null); init(); @@ -60,8 +54,9 @@ CTCPSocket::~CTCPSocket() void CTCPSocket::bind(const CNetworkAddress& addr) { - if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { - if (errno == EADDRINUSE) { + if (CNetwork::bind(m_fd, addr.getAddress(), + addr.getAddressLength()) == CNetwork::Error) { + if (errno == CNetwork::kEADDRINUSE) { throw XSocketAddressInUse(); } throw XSocketBind(); @@ -71,7 +66,8 @@ void CTCPSocket::bind(const CNetworkAddress& addr) void CTCPSocket::connect(const CNetworkAddress& addr) { CThread::testCancel(); - if (::connect(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) { + if (CNetwork::connect(m_fd, addr.getAddress(), + addr.getAddressLength()) == CNetwork::Error) { CThread::testCancel(); throw XSocketConnect(); } @@ -103,11 +99,11 @@ void CTCPSocket::close() } CLock lock(m_mutex); - if (m_fd != -1) { - if (::close(m_fd) == -1) { + if (m_fd != CNetwork::Null) { + if (CNetwork::close(m_fd) == CNetwork::Error) { throw XIOClose(); } - m_fd = -1; + m_fd = CNetwork::Null; } } @@ -150,10 +146,10 @@ void CTCPSocket::ioThread(void*) void CTCPSocket::ioService() { - assert(m_fd != -1); + assert(m_fd != CNetwork::Null); // now service the connection - struct pollfd pfds[1]; + CNetwork::PollEntry pfds[1]; pfds[0].fd = m_fd; for (;;) { { @@ -162,31 +158,32 @@ void CTCPSocket::ioService() pfds[0].events = 0; if ((m_connected & kRead) != 0) { // still open for reading - pfds[0].events |= POLLIN; + pfds[0].events |= CNetwork::kPOLLIN; } if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) { // data queued for writing - pfds[0].events |= POLLOUT; + pfds[0].events |= CNetwork::kPOLLOUT; } } // check for status CThread::testCancel(); - const int status = poll(pfds, 1, 50); + const int status = CNetwork::poll(pfds, 1, 50); CThread::testCancel(); // transfer data and handle errors if (status == 1) { - if ((pfds[0].revents & (POLLERR | POLLNVAL)) != 0) { + if ((pfds[0].revents & (CNetwork::kPOLLERR | + CNetwork::kPOLLNVAL)) != 0) { // stream is no good anymore so bail m_input->hangup(); return; } // read some data - if (pfds[0].revents & POLLIN) { + if (pfds[0].revents & CNetwork::kPOLLIN) { UInt8 buffer[4096]; - ssize_t n = read(m_fd, buffer, sizeof(buffer)); + ssize_t n = CNetwork::read(m_fd, buffer, sizeof(buffer)); if (n > 0) { CLock lock(m_mutex); m_input->write(buffer, n); @@ -199,7 +196,7 @@ void CTCPSocket::ioService() } // write some data - if (pfds[0].revents & POLLOUT) { + if (pfds[0].revents & CNetwork::kPOLLOUT) { CLock lock(m_mutex); // get amount of data to write @@ -211,13 +208,13 @@ void CTCPSocket::ioService() // write data const void* buffer = m_output->peek(n); - n = (UInt32)write(m_fd, buffer, n); + n = (UInt32)CNetwork::write(m_fd, buffer, n); // discard written data if (n > 0) { m_output->pop(n); } - else if (n == (UInt32)-1 && errno == EPIPE) { + else if (n == (UInt32)-1 && CNetwork::getsockerror() == EPIPE) { return; } } @@ -228,13 +225,13 @@ void CTCPSocket::ioService() void CTCPSocket::closeInput(void*) { // note -- m_mutex should already be locked - shutdown(m_fd, 0); + CNetwork::shutdown(m_fd, 0); m_connected &= ~kRead; } void CTCPSocket::closeOutput(void*) { // note -- m_mutex should already be locked - shutdown(m_fd, 1); + CNetwork::shutdown(m_fd, 1); m_connected &= ~kWrite; } diff --git a/net/CTCPSocket.h b/net/CTCPSocket.h index c7b9ca73..56da71b4 100644 --- a/net/CTCPSocket.h +++ b/net/CTCPSocket.h @@ -2,6 +2,7 @@ #define CTCPSOCKET_H #include "ISocket.h" +#include "CNetwork.h" #include "XThread.h" class CMutex; @@ -14,7 +15,7 @@ class CBufferedOutputStream; class CTCPSocket : public ISocket { public: CTCPSocket(); - CTCPSocket(int fd); + CTCPSocket(CNetwork::Socket); ~CTCPSocket(); // manipulators @@ -38,7 +39,7 @@ class CTCPSocket : public ISocket { private: enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 }; - int m_fd; + CNetwork::Socket m_fd; CBufferedInputStream* m_input; CBufferedOutputStream* m_output; diff --git a/net/Makefile b/net/Makefile index 5b9534f1..38110334 100644 --- a/net/Makefile +++ b/net/Makefile @@ -15,10 +15,11 @@ LCXXINCS = \ -I$(DEPTH)/io \ $(NULL) CXXFILES = \ - XSocket.cpp \ + CNetwork.cpp \ CNetworkAddress.cpp \ CTCPSocket.cpp \ CTCPListenSocket.cpp \ + XSocket.cpp \ $(NULL) targets: $(LIBTARGET) diff --git a/net/XNetwork.cpp b/net/XNetwork.cpp new file mode 100644 index 00000000..a745fc6a --- /dev/null +++ b/net/XNetwork.cpp @@ -0,0 +1,72 @@ +#include "XNetwork.h" + +// +// XNetworkUnavailable +// + +CString XNetworkUnavailable::getWhat() const throw() +{ + return format("XNetworkUnavailable", "network library is not available"); +} + + +// +// XNetworkFailed +// + +CString XNetworkFailed::getWhat() const throw() +{ + return format("XNetworkFailed", "cannot initialize network library"); +} + + +// +// XNetworkVersion +// + +XNetworkVersion::XNetworkVersion(int major, int minor) throw() : + m_major(major), + m_minor(minor) +{ + // do nothing +} + +int XNetworkVersion::getMajor() const throw() +{ + return m_major; +} + +int XNetworkVersion::getMinor() const throw() +{ + return m_minor; +} + +CString XNetworkVersion::getWhat() const throw() +{ + return format("XNetworkVersion", + "unsupported network version %d.%d", + m_major, m_minor); +} + + +// +// XNetworkFunctionUnavailable +// + +XNetworkFunctionUnavailable::XNetworkFunctionUnavailable( + const char* name) throw() +{ + try { + m_name = name; + } + catch (...) { + // ignore + } +} + +CString XNetworkFunctionUnavailable::getWhat() const throw() +{ + return format("XNetworkFunctionUnavailable", + "missing network function %s", + m_name.c_str()); +} diff --git a/net/XNetwork.h b/net/XNetwork.h new file mode 100644 index 00000000..21e91574 --- /dev/null +++ b/net/XNetwork.h @@ -0,0 +1,52 @@ +#ifndef XNETWORK_H +#define XNETWORK_H + +#include "CString.h" +#include "XBase.h" +#include "BasicTypes.h" + +class XNetwork : public XBase { }; + +class XNetworkUnavailable : public XNetwork { + protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +class XNetworkFailed : public XNetwork { + protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +class XNetworkVersion : public XNetwork { + public: + XNetworkVersion(int major, int minor) throw(); + + // accessors + + int getMajor() const throw(); + int getMinor() const throw(); + + protected: + // XBase overrides + virtual CString getWhat() const throw(); + + private: + int m_major; + int m_minor; +}; + +class XNetworkFunctionUnavailable : public XNetwork { + public: + XNetworkFunctionUnavailable(const char* name) throw(); + + protected: + // XBase overrides + virtual CString getWhat() const throw(); + + private: + CString m_name; +}; + +#endif diff --git a/net/net.dsp b/net/net.dsp new file mode 100644 index 00000000..58d0ee13 --- /dev/null +++ b/net/net.dsp @@ -0,0 +1,174 @@ +# Microsoft Developer Studio Project File - Name="net" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=net - Win32 Debug +!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 "net.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 "net.mak" CFG="net - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "net - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "net - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "net - 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" +# 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 /O2 /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "net - 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" +# 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 "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "net - Win32 Release" +# Name "net - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CNetwork.cpp +# End Source File +# Begin Source File + +SOURCE=.\CNetworkAddress.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSocketInputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSocketOutputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSocketStreamBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPListenSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\XNetwork.cpp +# End Source File +# Begin Source File + +SOURCE=.\XSocket.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\CNetworkAddress.h +# End Source File +# Begin Source File + +SOURCE=.\CSocketInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CSocketOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CSocketStreamBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocket.h +# End Source File +# Begin Source File + +SOURCE=.\IListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\ISocket.h +# End Source File +# Begin Source File + +SOURCE=.\XNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\XSocket.h +# End Source File +# End Group +# End Target +# End Project diff --git a/notes b/notes index b6340818..14edcc1b 100644 --- a/notes +++ b/notes @@ -78,3 +78,23 @@ server: asynchronously handle primary screen events comm send/recv messages to/from clients + +--- +win32: + double click support + need to support window stations + login screen on NT is a separate window station + handle display changes + +--- +win32 key translation (client) + get character for key (with appropriate shift) + keypad enter -> VK_??? + sys req -> VK_??? + compose -> VK_??? + +X11 key translation (server) + handle compose key? + * don't send compose, don't send dead keys, send composed key? + * send all keys, let client do compose + diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp new file mode 100644 index 00000000..1580de71 --- /dev/null +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -0,0 +1,509 @@ +#include "CMSWindowsPrimaryScreen.h" +#include "CServer.h" +#include "CSynergyHook.h" +#include "CThread.h" +#include "CLog.h" +#include + +// +// CMSWindowsPrimaryScreen +// + +CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : + m_server(NULL), + m_active(false), + m_window(NULL), + m_hookLibrary(NULL), + m_mark(0), + m_markReceived(0) +{ + // do nothing +} + +CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() +{ + assert(m_window == NULL); + assert(m_hookLibrary == NULL); +} + +static HWND s_debug = NULL; +static HWND s_debugLog = NULL; +static DWORD s_thread = 0; +static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: + return TRUE; + + case WM_CLOSE: + PostQuitMessage(0); + return TRUE; + } + return FALSE; +} +static void debugOutput(const char* msg) +{ + if (s_thread != 0) { + const DWORD threadID = ::GetCurrentThreadId(); + if (threadID != s_thread) { + GetDesktopWindow(); + AttachThreadInput(threadID, s_thread, TRUE); + } + } + DWORD len = SendMessage(s_debugLog, WM_GETTEXTLENGTH, 0, 0); + if (len > 20000) { + SendMessage(s_debugLog, EM_SETSEL, -1, 0); + SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)msg); + } + else { + SendMessage(s_debugLog, EM_SETSEL, -1, len); + SendMessage(s_debugLog, EM_REPLACESEL, FALSE, (LPARAM)(LPCTSTR)msg); + } + SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0); +} + +void CMSWindowsPrimaryScreen::run() +{ +CLog::setOutputter(&debugOutput); + doRun(); +CLog::setOutputter(NULL); +} + +void CMSWindowsPrimaryScreen::stop() +{ + doStop(); +} + +void CMSWindowsPrimaryScreen::open(CServer* server) +{ + assert(m_server == NULL); + assert(server != NULL); + + // set the server + m_server = server; + + // open the display + openDisplay(); + + // enter the screen + doEnter(); +} + +void CMSWindowsPrimaryScreen::close() +{ + assert(m_server != NULL); + + // close the display + closeDisplay(); + + // done with server + m_server = NULL; +} + +void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) +{ + log((CLOG_INFO "entering primary at %d,%d", x, y)); + assert(m_active == true); + + // do non-warp enter stuff + doEnter(); + + // warp to requested location + warpCursor(x, y); +} + +void CMSWindowsPrimaryScreen::doEnter() +{ + // set the zones that should cause a jump + SInt32 w, h; + getScreenSize(&w, &h); + SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( + m_hookLibrary, "setZone"); + setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize()); + + // all messages prior to now are invalid + nextMark(); + + // not active anymore + m_active = false; +} + +void CMSWindowsPrimaryScreen::leave() +{ + log((CLOG_INFO "leaving primary")); + assert(m_active == false); + + // all messages prior to now are invalid + nextMark(); + + // relay all mouse and keyboard events + SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress( + m_hookLibrary, "setRelay"); + setRelay(); + + // warp the mouse to the center of the screen + SInt32 w, h; + getScreenSize(&w, &h); + warpCursor(w >> 1, h >> 1); + + // warp is also invalid + nextMark(); + + // local client now active + m_active = true; +} + +void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + SInt32 w, h; + getScreenSize(&w, &h); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65535.99 * x) / (w - 1)), + (DWORD)((65535.99 * y) / (h - 1)), + 0, 0); +} + +void CMSWindowsPrimaryScreen::setClipboard( + const IClipboard* /*clipboard*/) +{ + assert(m_window != NULL); + + // FIXME -- should we retry until we get it? + if (!OpenClipboard(m_window)) + return; + if (EmptyClipboard()) { + log((CLOG_DEBUG "grabbed clipboard")); + // FIXME -- set the clipboard data + } + CloseClipboard(); +} + +void CMSWindowsPrimaryScreen::getSize( + SInt32* width, SInt32* height) const +{ + getScreenSize(width, height); +} + +SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + +void CMSWindowsPrimaryScreen::getClipboard( + IClipboard* clipboard) const +{ + // FIXME -- put this in superclass? + // FIXME -- don't use CurrentTime +// getDisplayClipboard(clipboard, m_window, CurrentTime); +} + +#include "resource.h" // FIXME + +void CMSWindowsPrimaryScreen::onOpenDisplay() +{ + assert(m_window == NULL); + assert(m_server != NULL); + +// create debug dialog +s_thread = GetCurrentThreadId();; +s_debug = CreateDialog(getInstance(), MAKEINTRESOURCE(IDD_SYNERGY), NULL, &debugProc); +s_debugLog = ::GetDlgItem(s_debug, IDC_LOG); +CLog::setOutputter(&debugOutput); +ShowWindow(s_debug, SW_SHOWNORMAL); + + // create the window + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, + (LPCTSTR)getClass(), "Synergy", + WS_POPUP, + 0, 0, 1, 1, NULL, NULL, + getInstance(), + NULL); + + // load the hook library + bool hooked = false; + m_hookLibrary = LoadLibrary("synrgyhk"); + if (m_hookLibrary != NULL) { + // install input hooks + InstallFunc install = (InstallFunc)GetProcAddress( + m_hookLibrary, "install"); + if (install != NULL) { + hooked = (install(m_window) != 0); + } + } + if (!hooked) { + DestroyWindow(m_window); + m_window = NULL; + // FIXME -- throw + } + + // initialize marks + m_mark = 0; + m_markReceived = 0; + nextMark(); +} + +void CMSWindowsPrimaryScreen::onCloseDisplay() +{ + assert(m_window != NULL); + + // uninstall input hooks + UninstallFunc uninstall = (UninstallFunc)GetProcAddress( + m_hookLibrary, "uninstall"); + if (uninstall != NULL) { + uninstall(); + } + + // done with hook library + FreeLibrary(m_hookLibrary); + m_hookLibrary = NULL; + + // destroy window + DestroyWindow(m_window); + m_window = NULL; + +CLog::setOutputter(NULL); +DestroyWindow(s_debug); +s_debug = NULL; +s_thread = 0; +} + +bool CMSWindowsPrimaryScreen::onEvent(MSG* msg) +{ +if (IsDialogMessage(s_debug, msg)) { + return true; +} + + // handle event + switch (msg->message) { + // FIXME -- handle display changes + case WM_PAINT: + ValidateRect(m_window, NULL); + return true; + + case SYNERGY_MSG_MARK: + m_markReceived = msg->wParam; + return true; + + case SYNERGY_MSG_KEY: + // ignore if not at current mark + if (m_mark == m_markReceived) { + // FIXME -- vk code; key data + } + return true; + + case SYNERGY_MSG_MOUSE_BUTTON: + // ignore if not at current mark + if (m_mark == m_markReceived) { + const ButtonID button = mapButton(msg->wParam); + switch (msg->wParam) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + log((CLOG_DEBUG "event: button press button=%d", button)); + if (button != kButtonNone) { + m_server->onMouseDown(button); + } + break; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + log((CLOG_DEBUG "event: button release button=%d", button)); + if (button != kButtonNone) { + m_server->onMouseUp(button); + } + break; + } + } + return true; + + case SYNERGY_MSG_MOUSE_MOVE: + // ignore if not at current mark + if (m_mark == m_markReceived) { + SInt32 x = (SInt32)msg->wParam; + SInt32 y = (SInt32)msg->lParam; + if (!m_active) { + log((CLOG_DEBUG "event: inactive move %d,%d", x, y)); + m_server->onMouseMovePrimary(x, y); + } + else { + log((CLOG_DEBUG "event: active move %d,%d", x, y)); + + // get screen size + SInt32 w, h; + getScreenSize(&w, &h); + + // get center pixel + w >>= 1; + h >>= 1; + + // ignore and discard message if motion is to center of + // screen. those are caused by us warping the mouse. + if (x != w || y != h) { + // get mouse deltas + x -= w; + y -= h; + + // warp mouse back to center + warpCursor(w, h); + + // send motion + m_server->onMouseMoveSecondary(x, y); + } + } + } + return true; + } + + return false; +/* + case WM_MOUSEMOVE: { + if (!m_active) { + // mouse entered a jump zone window + POINT p; + p.x = (short)LOWORD(msg.lParam); + p.y = (short)HIWORD(msg.lParam); + ClientToScreen(msg.hwnd, &p); + log((CLOG_DEBUG "event: WM_MOUSEMOVE %d,%d", p.x, p.y)); + m_server->onMouseMovePrimary((SInt32)p.x, (SInt32)p.y); + } + break; + } + + case KeyPress: { + log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(xevent.xkey.keycode, mask); + if (key != kKeyNULL) { + m_server->onKeyDown(key, mask); + } + break; + } + + // FIXME -- simulate key repeat. X sends press/release for + // repeat. must detect auto repeat and use kKeyRepeat. + case KeyRelease: { + log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(xevent.xkey.keycode, mask); + if (key != kKeyNULL) { + m_server->onKeyUp(key, mask); + } + break; + } + + case ButtonPress: { + log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNULL) { + m_server->onMouseDown(button); + } + break; + } + + case ButtonRelease: { + log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNULL) { + m_server->onMouseUp(button); + } + break; + } + + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +*/ +} + +void CMSWindowsPrimaryScreen::nextMark() +{ + assert(m_window != NULL); + + PostMessage(m_window, SYNERGY_MSG_MARK, ++m_mark, 0); +} + +#if 0 +bool CMSWindowsPrimaryScreen::keyboardHook( + int /*code*/, WPARAM wParam, LPARAM lParam) +{ + if (m_active) { + // handle keyboard events + const KeyID key = mapKey(wParam, lParam); + if (key != kKeyNone) { + const KeyModifierMask modifiers = mapModifier(wParam, lParam); + if ((lParam & KF_UP) == 0) { + log((CLOG_DEBUG "event: key press key=%d", key)); + m_server->onKeyDown(key, modifiers); + } + else { + log((CLOG_DEBUG "event: key release key=%d", key)); + m_server->onKeyUp(key, modifiers); + } + return true; + } + } + return false; +} +#endif + +KeyModifierMask CMSWindowsPrimaryScreen::mapModifier( + WPARAM keycode, LPARAM info) const +{ + // FIXME -- should be configurable + KeyModifierMask mask = 0; + if (GetKeyState(VK_SHIFT) < 0) + mask |= KeyModifierShift; + if ((GetKeyState(VK_CAPITAL) & 1) != 0) + mask |= KeyModifierCapsLock; + if (GetKeyState(VK_CONTROL) < 0) + mask |= KeyModifierControl; + if (GetKeyState(VK_MENU) < 0) + mask |= KeyModifierAlt; + if ((GetKeyState(VK_NUMLOCK) & 1) != 0) + mask |= KeyModifierNumLock; + if (GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0) + mask |= KeyModifierMeta; + if ((GetKeyState(VK_SCROLL) & 1) != 0) + mask |= KeyModifierScrollLock; + return mask; +} + +KeyID CMSWindowsPrimaryScreen::mapKey( + WPARAM keycode, LPARAM info) const +{ + // FIXME -- must convert to X keysyms + return keycode; +} + +ButtonID CMSWindowsPrimaryScreen::mapButton( + WPARAM button) const +{ + switch (button) { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + return kButtonLeft; + + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + return kButtonMiddle; + + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + return kButtonRight; + + default: + return kButtonNone; + } +} diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h new file mode 100644 index 00000000..d34b44bf --- /dev/null +++ b/server/CMSWindowsPrimaryScreen.h @@ -0,0 +1,56 @@ +#ifndef CMSWINDOWSPRIMARYSCREEN_H +#define CMSWINDOWSPRIMARYSCREEN_H + +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CMSWindowsScreen.h" +#include "IPrimaryScreen.h" + +class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { + public: + typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM); + + CMSWindowsPrimaryScreen(); + virtual ~CMSWindowsPrimaryScreen(); + + // IPrimaryScreen overrides + virtual void run(); + virtual void stop(); + virtual void open(CServer*); + virtual void close(); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void leave(); + virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void setClipboard(const IClipboard*); + virtual void getSize(SInt32* width, SInt32* height) const; + virtual SInt32 getJumpZoneSize() const; + virtual void getClipboard(IClipboard*) const; + + protected: + // CMSWindowsScreen overrides + virtual bool onEvent(MSG*); + virtual void onOpenDisplay(); + virtual void onCloseDisplay(); + + private: + void doEnter(); + + void nextMark(); + +// bool keyboardHook(int, WPARAM, LPARAM); +// bool mouseHook(int, WPARAM, LPARAM); + + KeyModifierMask mapModifier(WPARAM keycode, LPARAM info) const; + KeyID mapKey(WPARAM keycode, LPARAM info) const; + ButtonID mapButton(WPARAM button) const; + + private: + CServer* m_server; + bool m_active; + HWND m_window; + HINSTANCE m_hookLibrary; + UInt32 m_mark; + UInt32 m_markReceived; +}; + +#endif diff --git a/server/CScreenMap.h b/server/CScreenMap.h index 80c571f6..e86a61d8 100644 --- a/server/CScreenMap.h +++ b/server/CScreenMap.h @@ -9,6 +9,8 @@ class CScreenMap { public: enum EDirection { kLeft, kRight, kTop, kBottom, kFirstDirection = kLeft, kLastDirection = kBottom }; + enum EDirectionMask { kLeftMask = 1, kRightMask = 2, + kTopMask = 4, kBottomMask = 8 }; CScreenMap(); virtual ~CScreenMap(); diff --git a/server/CServer.cpp b/server/CServer.cpp index c60d515c..42df94f8 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -20,6 +20,14 @@ #include #include +// hack to work around operator=() bug in STL in g++ prior to v3 +#if defined(__GNUC__) && (__GNUC__ < 3) +#define assign(_dst, _src, _type) _dst.reset(_src) +#else +#define assign(_dst, _src, _type) _dst = std::auto_ptr<_type >(_src) +#endif + + /* XXX #include #include @@ -33,8 +41,7 @@ else { wait(0); exit(1); } // CServer // -CServer::CServer() : m_done(&m_mutex, false), - m_primary(NULL), +CServer::CServer() : m_primary(NULL), m_active(NULL), m_primaryInfo(NULL) { @@ -61,31 +68,28 @@ void CServer::run() // start listening for configuration connections // FIXME - // wait until done - log((CLOG_DEBUG "waiting for quit")); - CLock lock(&m_mutex); - while (m_done == false) { - m_done.wait(); - } + // handle events + log((CLOG_DEBUG "starting event handling")); + m_primary->run(); // clean up log((CLOG_DEBUG "stopping server")); - closePrimaryScreen(); cleanupThreads(); + closePrimaryScreen(); } catch (XBase& e) { log((CLOG_ERR "server error: %s\n", e.what())); // clean up - closePrimaryScreen(); cleanupThreads(); + closePrimaryScreen(); } catch (...) { - log((CLOG_DEBUG "server shutdown")); + log((CLOG_DEBUG "unknown server error")); // clean up - closePrimaryScreen(); cleanupThreads(); + closePrimaryScreen(); throw; } } @@ -107,6 +111,21 @@ void CServer::getScreenMap(CScreenMap* screenMap) const *screenMap = m_screenMap; } +UInt32 CServer::getActivePrimarySides() const +{ + UInt32 sides = 0; + CLock lock(&m_mutex); + if (!m_screenMap.getNeighbor("primary", CScreenMap::kLeft).empty()) + sides |= CScreenMap::kLeftMask; + if (!m_screenMap.getNeighbor("primary", CScreenMap::kRight).empty()) + sides |= CScreenMap::kRightMask; + if (!m_screenMap.getNeighbor("primary", CScreenMap::kTop).empty()) + sides |= CScreenMap::kTopMask; + if (!m_screenMap.getNeighbor("primary", CScreenMap::kBottom).empty()) + sides |= CScreenMap::kBottomMask; + return sides; +} + void CServer::setInfo(const CString& client, SInt32 w, SInt32 h, SInt32 zoneSize) { @@ -206,7 +225,7 @@ void CServer::onMouseUp(ButtonID id) } } -void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) +bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) { log((CLOG_DEBUG "onMouseMovePrimary %d,%d", x, y)); @@ -216,7 +235,7 @@ void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) // ignore if mouse is locked to screen if (isLockedToScreen()) { - return; + return false; } // see if we should change screens @@ -243,7 +262,7 @@ void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) } else { // still on local screen - return; + return false; } // get jump destination @@ -251,7 +270,7 @@ void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) // if no screen in jump direction then ignore the move if (newScreen == NULL) { - return; + return false; } // remap position to account for resolution differences @@ -259,6 +278,7 @@ void CServer::onMouseMovePrimary(SInt32 x, SInt32 y) // switch screen switchScreen(newScreen, x, y); + return true; } void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) @@ -365,7 +385,11 @@ bool CServer::isLockedToScreen() const return false; } +#if defined(CONFIG_PLATFORM_WIN32) +#include "CMSWindowsClipboard.h" // FIXME +#elif defined(CONFIG_PLATFORM_UNIX) #include "CXWindowsClipboard.h" // FIXME +#endif void CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y) { @@ -384,7 +408,11 @@ void CServer::switchScreen(CScreenInfo* dst, m_primary->leave(); // FIXME -- testing +#if defined(CONFIG_PLATFORM_WIN32) + CMSWindowsClipboard clipboard; +#elif defined(CONFIG_PLATFORM_UNIX) CXWindowsClipboard clipboard; +#endif m_primary->getClipboard(&clipboard); } else { @@ -610,8 +638,8 @@ void CServer::acceptClients(void*) std::auto_ptr listen; try { // create socket listener -// listen.reset(m_socketFactory->createListen()); - listen.reset(new CTCPListenSocket); // FIXME +// listen = std::auto_ptr(m_socketFactory->createListen()); + assign(listen, new CTCPListenSocket, IListenSocket); // FIXME // bind to the desired port. keep retrying if we can't bind // the address immediately. @@ -689,8 +717,8 @@ void CServer::handshakeClient(void* vsocket) } // attach the packetizing filters - input.reset(new CInputPacketStream(srcInput, own)); - output.reset(new COutputPacketStream(srcOutput, own)); + assign(input, new CInputPacketStream(srcInput, own), IInputStream); + assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); std::auto_ptr protocol; std::auto_ptr connectedNote; @@ -730,12 +758,13 @@ void CServer::handshakeClient(void* vsocket) // create a protocol interpreter for the version log((CLOG_DEBUG "creating interpreter for client %s version %d.%d", name.c_str(), major, minor)); - protocol.reset(CServerProtocol::create(major, minor, - this, name, input.get(), output.get())); + assign(protocol, CServerProtocol::create(major, minor, + this, name, input.get(), output.get()), + IServerProtocol); // client is now pending - connectedNote.reset(new CConnectionNote(this, - name, protocol.get())); + assign(connectedNote, new CConnectionNote(this, + name, protocol.get()), CConnectionNote); // ask and wait for the client's info log((CLOG_DEBUG "waiting for info for client %s", name.c_str())); @@ -766,20 +795,26 @@ void CServer::handshakeClient(void* vsocket) void CServer::quit() { - CLock lock(&m_mutex); - m_done = true; - m_done.broadcast(); + m_primary->stop(); } // FIXME -- use factory to create screen +#if defined(CONFIG_PLATFORM_WIN32) +#include "CMSWindowsPrimaryScreen.h" +#elif defined(CONFIG_PLATFORM_UNIX) #include "CXWindowsPrimaryScreen.h" +#endif void CServer::openPrimaryScreen() { assert(m_primary == NULL); // open screen log((CLOG_DEBUG "creating primary screen")); +#if defined(CONFIG_PLATFORM_WIN32) + m_primary = new CMSWindowsPrimaryScreen; +#elif defined(CONFIG_PLATFORM_UNIX) m_primary = new CXWindowsPrimaryScreen; +#endif log((CLOG_DEBUG "opening primary screen")); m_primary->open(this); @@ -828,8 +863,9 @@ void CServer::removeCleanupThread(const CThread& thread) for (CThreadList::iterator index = m_cleanupList.begin(); index != m_cleanupList.end(); ++index) { if (**index == thread) { + CThread* thread = *index; m_cleanupList.erase(index); - delete *index; + delete thread; return; } } @@ -877,7 +913,7 @@ void CServer::removeConnection(const CString& name) assert(index != m_screens.end()); // if this is active screen then we have to jump off of it - if (m_active == index->second) { + if (m_active == index->second && m_active != m_primaryInfo) { // record new position (center of primary screen) m_x = m_primaryInfo->m_width >> 1; m_y = m_primaryInfo->m_height >> 1; diff --git a/server/CServer.h b/server/CServer.h index 83fa337f..18391aad 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -4,7 +4,6 @@ #include "KeyTypes.h" #include "MouseTypes.h" #include "CScreenMap.h" -#include "CCondVar.h" #include "CMutex.h" #include "CString.h" #include "XBase.h" @@ -24,18 +23,23 @@ class CServer { // manipulators + // start the server. does not return until quit() is called. void run(); + // tell server to exit gracefully + void quit(); + // update screen map void setScreenMap(const CScreenMap&); - // handle events on server's screen + // handle events on server's screen. onMouseMovePrimary() returns + // true iff the mouse enters a jump zone and jumps. void onKeyDown(KeyID, KeyModifierMask); void onKeyUp(KeyID, KeyModifierMask); void onKeyRepeat(KeyID, KeyModifierMask); void onMouseDown(ButtonID); void onMouseUp(ButtonID); - void onMouseMovePrimary(SInt32 x, SInt32 y); + bool onMouseMovePrimary(SInt32 x, SInt32 y); void onMouseMoveSecondary(SInt32 dx, SInt32 dy); void onMouseWheel(SInt32 delta); @@ -50,9 +54,11 @@ class CServer { // get the current screen map void getScreenMap(CScreenMap*) const; + // get the sides of the primary screen that have neighbors + UInt32 getActivePrimarySides() const; + protected: bool onCommandKey(KeyID, KeyModifierMask, bool down); - void quit(); private: class CCleanupNote { @@ -137,7 +143,6 @@ class CServer { typedef std::map CScreenList; CMutex m_mutex; - CCondVar m_done; double m_bindTimeout; diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp new file mode 100644 index 00000000..2cdf876d --- /dev/null +++ b/server/CSynergyHook.cpp @@ -0,0 +1,242 @@ +#include "CSynergyHook.h" +#include "CScreenMap.h" +#include + +// +// globals +// + +#pragma comment(linker, "-section:shared,rws") +#pragma data_seg("shared") +// all data in this shared section *must* be initialized + +static HINSTANCE g_hinstance = NULL; +static DWORD g_process = NULL; +static HWND g_hwnd = NULL; +static HHOOK g_keyboard = NULL; +static HHOOK g_mouse = NULL; +static bool g_relay = false; +static SInt32 g_zoneSize = 0; +static UInt32 g_zoneSides = 0; +static SInt32 g_wScreen = 0; +static SInt32 g_hScreen = 0; +static HCURSOR g_cursor = NULL; +static DWORD g_cursorThread = 0; + +#pragma data_seg() + +// +// internal functions +// + +static void hideCursor(DWORD thread) +{ + // we should be running the context of the window who's cursor + // we want to hide so we shouldn't have to attach thread input. + g_cursor = GetCursor(); + g_cursorThread = thread; + SetCursor(NULL); +} + +static void restoreCursor() +{ + // restore the show cursor in the window we hid it last + if (g_cursor != NULL && g_cursorThread != 0) { + DWORD myThread = GetCurrentThreadId(); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, TRUE); + SetCursor(g_cursor); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, FALSE); + } + g_cursor = NULL; + g_cursorThread = 0; +} + +static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // FIXME + } + + return CallNextHookEx(g_keyboard, code, wParam, lParam); +} + +static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + if (g_relay) { + switch (wParam) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_BUTTON, wParam, 0); + return 1; + + case WM_MOUSEMOVE: { + const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + + // we want the cursor to be hidden at all times so we + // hide the cursor on whatever window has it. but then + // we have to show the cursor whenever we leave that + // window (or at some later time before we stop relaying). + // so check the window with the cursor. if it's not the + // same window that had it before then show the cursor + // in the last window and hide it in this window. + DWORD thread = GetWindowThreadProcessId(info->hwnd, NULL); + if (thread != g_cursorThread) { + restoreCursor(); + hideCursor(thread); + } + + // relay the motion + PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y); + return 1; + } + } + } + else { + // check for mouse inside jump zone + bool inside = false; + const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + if (!inside && (g_zoneSides & CScreenMap::kLeftMask) != 0) { + inside = (x < g_zoneSize); + } + if (!inside && (g_zoneSides & CScreenMap::kRightMask) != 0) { + inside = (x >= g_wScreen - g_zoneSize); + } + if (!inside && (g_zoneSides & CScreenMap::kTopMask) != 0) { + inside = (y < g_zoneSize); + } + if (!inside && (g_zoneSides & CScreenMap::kBottomMask) != 0) { + inside = (y >= g_hScreen - g_zoneSize); + } + + // if inside then eat event and notify our window + if (inside) { + restoreCursor(); + PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y); + return 1; + } + } + } + + return CallNextHookEx(g_mouse, code, wParam, lParam); +} + + +// +// external functions +// + +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) +{ + if (reason == DLL_PROCESS_ATTACH) { + if (g_hinstance == NULL) { + g_hinstance = instance; + g_process = GetCurrentProcessId(); + } + } + else if (reason == DLL_PROCESS_DETACH) { + if (g_process == GetCurrentProcessId()) { + if (g_keyboard != NULL || g_mouse != NULL) { + uninstall(); + } + g_process = NULL; + } + } + return TRUE; +} + +extern "C" { + +int install(HWND hwnd) +{ + assert(g_hinstance != NULL); + assert(g_keyboard == NULL); + assert(g_mouse == NULL); + + // save window + g_hwnd = hwnd; + + // set defaults + g_relay = false; + g_zoneSize = 0; + g_zoneSides = 0; + g_wScreen = 0; + g_hScreen = 0; + g_cursor = NULL; + g_cursorThread = 0; + + // install keyboard hook + g_keyboard = SetWindowsHookEx(WH_KEYBOARD, + &keyboardHook, + g_hinstance, + 0); + if (g_keyboard == NULL) { + g_hwnd = NULL; + return 0; + } + + // install mouse hook + g_mouse = SetWindowsHookEx(WH_MOUSE, + &mouseHook, + g_hinstance, + 0); + if (g_mouse == NULL) { + // uninstall keyboard hook before failing + UnhookWindowsHookEx(g_keyboard); + g_keyboard = NULL; + g_hwnd = NULL; + return 0; + } + + return 1; +} + +int uninstall(void) +{ + assert(g_keyboard != NULL); + assert(g_mouse != NULL); + + // uninstall hooks + UnhookWindowsHookEx(g_keyboard); + UnhookWindowsHookEx(g_mouse); + g_keyboard = NULL; + g_mouse = NULL; + g_hwnd = NULL; + + // show the cursor + restoreCursor(); + + return 1; +} + +void setZone(UInt32 sides, + SInt32 w, SInt32 h, SInt32 jumpZoneSize) +{ + g_zoneSize = jumpZoneSize; + g_zoneSides = sides; + g_wScreen = w; + g_hScreen = h; + g_relay = false; + restoreCursor(); +} + +void setRelay(void) +{ + g_relay = true; + g_zoneSize = 0; + g_zoneSides = 0; + g_wScreen = 0; + g_hScreen = 0; +} + +} diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h new file mode 100644 index 00000000..b3b30c24 --- /dev/null +++ b/server/CSynergyHook.h @@ -0,0 +1,37 @@ +#ifndef CSYNERGYHOOK_H +#define CSYNERGYHOOK_H + +#include "BasicTypes.h" +#include + +#if defined(CONFIG_PLATFORM_WIN32) +#if defined(SYNRGYHK_EXPORTS) +#define CSYNERGYHOOK_API __declspec(dllexport) +#else +#define CSYNERGYHOOK_API __declspec(dllimport) +#endif +#else +#define CSYNERGYHOOK_API +#endif + +#define SYNERGY_MSG_MARK WM_APP + 0x0001 // mark id; +#define SYNERGY_MSG_KEY WM_APP + 0x0002 // vk code; key data +#define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0003 // button msg; +#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0004 // x; y + +typedef int (*InstallFunc)(HWND); +typedef int (*UninstallFunc)(void); +typedef void (*SetZoneFunc)(UInt32, SInt32, SInt32, SInt32); +typedef void (*SetRelayFunc)(void); + +extern "C" { + +CSYNERGYHOOK_API int install(HWND); +CSYNERGYHOOK_API int uninstall(void); +CSYNERGYHOOK_API void setZone(UInt32 sides, + SInt32 w, SInt32 h, SInt32 jumpZoneSize); +CSYNERGYHOOK_API void setRelay(void); + +} + +#endif diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 6e208869..a36b07ec 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -19,7 +19,124 @@ CXWindowsPrimaryScreen::CXWindowsPrimaryScreen() : CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() { - assert(m_window == None); + assert(m_window == None); +} + +void CXWindowsPrimaryScreen::run() +{ + for (;;) { + // wait for and get the next event + XEvent xevent; + if (!getEvent(&xevent)) { + break; + } + + // handle event + switch (xevent.type) { + case CreateNotify: { + // select events on new window + CDisplayLock display(this); + selectEvents(display, xevent.xcreatewindow.window); + break; + } + + case KeyPress: { + log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(xevent.xkey.keycode, mask); + if (key != kKeyNone) { + m_server->onKeyDown(key, mask); + } + break; + } + + // FIXME -- simulate key repeat. X sends press/release for + // repeat. must detect auto repeat and use kKeyRepeat. + case KeyRelease: { + log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(xevent.xkey.keycode, mask); + if (key != kKeyNone) { + m_server->onKeyUp(key, mask); + } + break; + } + + case ButtonPress: { + log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_server->onMouseDown(button); + } + break; + } + + case ButtonRelease: { + log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_server->onMouseUp(button); + } + break; + } + + case MotionNotify: { + log((CLOG_DEBUG "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); + SInt32 x, y; + if (!m_active) { + x = xevent.xmotion.x_root; + y = xevent.xmotion.y_root; + m_server->onMouseMovePrimary(x, y); + } + else { + // FIXME -- slurp up all remaining motion events? + // probably not since key strokes may go to wrong place. + + // get mouse deltas + { + CDisplayLock display(this); + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int mask; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &mask)) + break; + + // compute position of center of window + SInt32 w, h; + getScreenSize(&w, &h); + x = xRoot - (w >> 1); + y = yRoot - (h >> 1); + + // warp mouse back to center + warpCursorNoLock(display, w >> 1, h >> 1); + } + + m_server->onMouseMoveSecondary(x, y); + } + break; + } + +/* + case SelectionClear: + target->XXX(xevent.xselectionclear.); + break; + + case SelectionNotify: + target->XXX(xevent.xselection.); + break; + + case SelectionRequest: + target->XXX(xevent.xselectionrequest.); + break; +*/ + } + } +} + +void CXWindowsPrimaryScreen::stop() +{ + doStop(); } void CXWindowsPrimaryScreen::open(CServer* server) @@ -251,116 +368,6 @@ void CXWindowsPrimaryScreen::selectEvents( } } -void CXWindowsPrimaryScreen::eventThread(void*) -{ - for (;;) { - // wait for and get the next event - XEvent xevent; - getEvent(&xevent); - - // handle event - switch (xevent.type) { - case CreateNotify: { - // select events on new window - CDisplayLock display(this); - selectEvents(display, xevent.xcreatewindow.window); - break; - } - - case KeyPress: { - log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(xevent.xkey.keycode, mask); - if (key != kKeyNone) { - m_server->onKeyDown(key, mask); - } - break; - } - - // FIXME -- simulate key repeat. X sends press/release for - // repeat. must detect auto repeat and use kKeyRepeat. - case KeyRelease: { - log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(xevent.xkey.keycode, mask); - if (key != kKeyNone) { - m_server->onKeyUp(key, mask); - } - break; - } - - case ButtonPress: { - log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNone) { - m_server->onMouseDown(button); - } - break; - } - - case ButtonRelease: { - log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNone) { - m_server->onMouseUp(button); - } - break; - } - - case MotionNotify: { - log((CLOG_DEBUG "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); - SInt32 x, y; - if (!m_active) { - x = xevent.xmotion.x_root; - y = xevent.xmotion.y_root; - m_server->onMouseMovePrimary(x, y); - } - else { - // FIXME -- slurp up all remaining motion events? - // probably not since key strokes may go to wrong place. - - // get mouse deltas - { - CDisplayLock display(this); - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int mask; - if (!XQueryPointer(display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &mask)) - break; - - // compute position of center of window - SInt32 w, h; - getScreenSize(&w, &h); - x = xRoot - (w >> 1); - y = yRoot - (h >> 1); - - // warp mouse back to center - warpCursorNoLock(display, w >> 1, h >> 1); - } - - m_server->onMouseMoveSecondary(x, y); - } - break; - } - -/* - case SelectionClear: - target->XXX(xevent.xselectionclear.); - break; - - case SelectionNotify: - target->XXX(xevent.xselection.); - break; - - case SelectionRequest: - target->XXX(xevent.xselectionrequest.); - break; -*/ - } - } -} - KeyModifierMask CXWindowsPrimaryScreen::mapModifier( unsigned int state) const { diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 3e056154..66b84fb5 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -12,6 +12,8 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { virtual ~CXWindowsPrimaryScreen(); // IPrimaryScreen overrides + virtual void run(); + virtual void stop(); virtual void open(CServer*); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); @@ -26,7 +28,6 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { // CXWindowsScreen overrides virtual void onOpenDisplay(); virtual void onCloseDisplay(); - virtual void eventThread(void*); private: void selectEvents(Display*, Window) const; diff --git a/server/Makefile b/server/Makefile index 2ff1150f..b8dd68a3 100644 --- a/server/Makefile +++ b/server/Makefile @@ -4,7 +4,7 @@ include $(DEPTH)/Makecommon # # target file # -TARGET = server +TARGETS = server # # source files @@ -40,8 +40,8 @@ LLDLIBS = \ -lpthread \ $(NULL) -targets: $(TARGET) +targets: $(TARGETS) -$(TARGET): $(OBJECTS) $(DEPLIBS) +$(TARGETS): $(OBJECTS) $(DEPLIBS) $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) diff --git a/server/makehook.dsp b/server/makehook.dsp new file mode 100644 index 00000000..507157ef --- /dev/null +++ b/server/makehook.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="makehook" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=makehook - Win32 Debug +!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 "makehook.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 "makehook.mak" CFG="makehook - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "makehook - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "makehook - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "makehook - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "makehook___Win32_Release" +# PROP BASE Intermediate_Dir "makehook___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "makehook - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "makehook___Win32_Debug" +# PROP BASE Intermediate_Dir "makehook___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "makehook - Win32 Release" +# Name "makehook - Win32 Debug" +# End Target +# End Project diff --git a/server/resource.h b/server/resource.h new file mode 100644 index 00000000..2b751aa3 --- /dev/null +++ b/server/resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by server.rc +// +#define IDD_SYNERGY 101 +#define IDC_LOG 1000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/server/server.cpp b/server/server.cpp index ef1b578f..809c53d6 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -1,16 +1,12 @@ #include "CServer.h" #include "CScreenMap.h" #include "CThread.h" -#include +#include "CNetwork.h" -int main(int argc, char** argv) +void realMain() { CThread::init(); - - if (argc != 1) { - fprintf(stderr, "usage: %s\n", argv[0]); - return 1; - } + CNetwork::init(); CScreenMap screenMap; screenMap.addScreen("primary"); @@ -18,15 +14,66 @@ int main(int argc, char** argv) screenMap.connect("primary", CScreenMap::kRight, "ingrid"); screenMap.connect("ingrid", CScreenMap::kLeft, "primary"); + CServer* server = NULL; try { - CServer* server = new CServer(); + server = new CServer(); server->setScreenMap(screenMap); server->run(); + delete server; + CNetwork::cleanup(); + } + catch (...) { + delete server; + CNetwork::cleanup(); + throw; + } +} + +#if defined(CONFIG_PLATFORM_WIN32) + +#include "CMSWindowsScreen.h" + +int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +{ + CMSWindowsScreen::init(instance); + + if (__argc != 1) { + CString msg = "no arguments allowed. exiting."; + MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); + return 1; + } + + try { + realMain(); + return 0; + } + catch (XBase& e) { + CString msg = "failed: "; + msg += e.what(); + MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); + return 1; + } +} + +#else + +#include + +int main(int argc, char** argv) +{ + if (argc != 1) { + fprintf(stderr, "usage: %s\n", argv[0]); + return 1; + } + + try { + realMain(); + return 0; } catch (XBase& e) { fprintf(stderr, "failed: %s\n", e.what()); return 1; } - - return 0; } + +#endif diff --git a/server/server.dsp b/server/server.dsp new file mode 100644 index 00000000..b65f6dec --- /dev/null +++ b/server/server.dsp @@ -0,0 +1,155 @@ +# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=server - Win32 Debug +!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 "server.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 "server.mak" CFG="server - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "server - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "server - 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" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "server - 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" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "server - Win32 Release" +# Name "server - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CScreenMap.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServer.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerProtocol.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerProtocol1_0.cpp +# End Source File +# Begin Source File + +SOURCE=.\server.cpp +# End Source File +# Begin Source File + +SOURCE=.\server.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CScreenMap.h +# End Source File +# Begin Source File + +SOURCE=.\CServer.h +# End Source File +# Begin Source File + +SOURCE=.\CServerProtocol.h +# End Source File +# Begin Source File + +SOURCE=.\CServerProtocol1_0.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/server/server.rc b/server/server.rc new file mode 100644 index 00000000..60c3a920 --- /dev/null +++ b/server/server.rc @@ -0,0 +1,97 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_SYNERGY DIALOG DISCARDABLE 0, 0, 329, 158 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Synergy" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_LOG,7,7,315,144,ES_MULTILINE | ES_AUTOHSCROLL | + ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_SYNERGY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 322 + TOPMARGIN, 7 + BOTTOMMARGIN, 151 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/server/synrgyhk.dsp b/server/synrgyhk.dsp new file mode 100644 index 00000000..979b6172 --- /dev/null +++ b/server/synrgyhk.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="synrgyhk" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=synrgyhk - Win32 Debug +!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 "synrgyhk.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 "synrgyhk.mak" CFG="synrgyhk - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synrgyhk - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "synrgyhk - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synrgyhk - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "synrgyhk___Win32_Release" +# PROP BASE Intermediate_Dir "synrgyhk___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 + +!ELSEIF "$(CFG)" == "synrgyhk - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "synrgyhk___Win32_Debug" +# PROP BASE Intermediate_Dir "synrgyhk___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "synrgyhk - Win32 Release" +# Name "synrgyhk - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CSynergyHook.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CSynergyHook.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/synergy.dsw b/synergy.dsw new file mode 100644 index 00000000..e441f5fc --- /dev/null +++ b/synergy.dsw @@ -0,0 +1,215 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "all"=.\all.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + . + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name client + End Project Dependency + Begin Project Dependency + Project_Dep_Name server + End Project Dependency +}}} + +############################################################################### + +Project: "base"=.\base\base.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\base + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "client"=.\client\client.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\client + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name synergy + End Project Dependency +}}} + +############################################################################### + +Project: "io"=.\io\io.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\io + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "makehook"=.\server\makehook.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name synrgyhk + End Project Dependency +}}} + +############################################################################### + +Project: "mt"=.\mt\mt.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\mt + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "net"=.\net\net.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\net + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "server"=.\server\server.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\server + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name synergy + End Project Dependency + Begin Project Dependency + Project_Dep_Name makehook + End Project Dependency +}}} + +############################################################################### + +Project: "synergy"=.\synergy\synergy.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\synergy + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "synrgyhk"=.\server\synrgyhk.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + millpond + .\server + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/synergy/CMSWindowsClipboard.cpp b/synergy/CMSWindowsClipboard.cpp new file mode 100644 index 00000000..bdef4024 --- /dev/null +++ b/synergy/CMSWindowsClipboard.cpp @@ -0,0 +1,41 @@ +#include "CMSWindowsClipboard.h" +#include "CString.h" +#include "CLog.h" + +// +// CMSWindowsClipboard +// + +CMSWindowsClipboard::CMSWindowsClipboard() +{ +} + +CMSWindowsClipboard::~CMSWindowsClipboard() +{ +} + +void CMSWindowsClipboard::open() +{ + log((CLOG_INFO "open clipboard")); +} + +void CMSWindowsClipboard::close() +{ + log((CLOG_INFO "close clipboard")); +} + +void CMSWindowsClipboard::add( + EFormat format, const CString& data) +{ + log((CLOG_INFO "add clipboard format: %d\n%s", format, data.c_str())); +} + +bool CMSWindowsClipboard::has(EFormat /*format*/) const +{ + return false; +} + +CString CMSWindowsClipboard::get(EFormat /*format*/) const +{ + return CString(); +} diff --git a/synergy/CMSWindowsClipboard.h b/synergy/CMSWindowsClipboard.h new file mode 100644 index 00000000..c13b11d3 --- /dev/null +++ b/synergy/CMSWindowsClipboard.h @@ -0,0 +1,19 @@ +#ifndef CMSWINDOWSCLIPBOARD_H +#define CMSWINDOWSCLIPBOARD_H + +#include "IClipboard.h" + +class CMSWindowsClipboard : public IClipboard { + public: + CMSWindowsClipboard(); + virtual ~CMSWindowsClipboard(); + + // IClipboard overrides + virtual void open(); + virtual void close(); + virtual void add(EFormat, const CString& data); + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; +}; + +#endif diff --git a/synergy/CMSWindowsScreen.cpp b/synergy/CMSWindowsScreen.cpp new file mode 100644 index 00000000..63640388 --- /dev/null +++ b/synergy/CMSWindowsScreen.cpp @@ -0,0 +1,248 @@ +#include "CMSWindowsScreen.h" +#include "CThread.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CLog.h" +#include "CString.h" +#include +#include + +// +// CMSWindowsScreen +// + +HINSTANCE CMSWindowsScreen::s_instance = NULL; + +CMSWindowsScreen::CMSWindowsScreen() : + m_class(0), + m_cursor(NULL), + m_w(0), m_h(0), + m_thread(0) +{ + // do nothing +} + +CMSWindowsScreen::~CMSWindowsScreen() +{ + assert(m_class == 0); +} + +void CMSWindowsScreen::init(HINSTANCE instance) +{ + s_instance = instance; +} + +void CMSWindowsScreen::doRun() +{ + m_thread = GetCurrentThreadId(); + for (;;) { + // wait for and get the next event + MSG msg; + getEvent(&msg); + + // handle quit message + if (msg.message == WM_QUIT) { + break; + } + + // dispatch message + if (!onEvent(&msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +void CMSWindowsScreen::doStop() +{ + PostThreadMessage(m_thread, WM_QUIT, 0, 0); +} + +void CMSWindowsScreen::openDisplay() +{ + assert(s_instance != NULL); + assert(m_class == 0); + + // create a transparent cursor + int cw = GetSystemMetrics(SM_CXCURSOR); + int ch = GetSystemMetrics(SM_CYCURSOR); + UInt8* cursorBits = new UInt8[ch * ((cw + 31) >> 5)]; + memset(cursorBits, 0, ch * ((cw + 31) >> 5)); + m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorBits, cursorBits); + delete[] cursorBits; + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = s_instance; + classInfo.hIcon = NULL; + classInfo.hCursor = m_cursor; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "Synergy"; + classInfo.hIconSm = NULL; + m_class = RegisterClassEx(&classInfo); + + // get screen size + // FIXME -- should handle multiple screens + m_w = GetSystemMetrics(SM_CXSCREEN); + m_h = GetSystemMetrics(SM_CYSCREEN); + log((CLOG_INFO "display size: %dx%d", m_w, m_h)); + + // let subclass prep display + onOpenDisplay(); +} + +void CMSWindowsScreen::closeDisplay() +{ + assert(s_instance != NULL); + assert(m_class != 0); + + // let subclass close down display + onCloseDisplay(); + + // unregister the window class + UnregisterClass((LPCTSTR)m_class, s_instance); + m_class = 0; + + // delete resources + DestroyCursor(m_cursor); + m_cursor = NULL; + + log((CLOG_DEBUG "closed display")); +} + +HINSTANCE CMSWindowsScreen::getInstance() +{ + return s_instance; +} + +ATOM CMSWindowsScreen::getClass() const +{ + return m_class; +} + +void CMSWindowsScreen::getScreenSize( + SInt32* w, SInt32* h) const +{ + assert(m_class != 0); + assert(w != NULL && h != NULL); + + *w = m_w; + *h = m_h; +} + +void CMSWindowsScreen::getEvent(MSG* msg) const +{ + // wait for an event in a cancellable way + while (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0) { + CThread::sleep(0.05); + } + GetMessage(msg, NULL, 0, 0); +} + +void CMSWindowsScreen::getDisplayClipboard( + IClipboard* clipboard, + HWND requestor) const +{ +/* FIXME + assert(clipboard != NULL); + assert(requestor != None); + + // clear the clipboard object + clipboard->open(); + + // block others from using the display while we get the clipboard. + // in particular, this prevents the event thread from stealing the + // selection notify event we're expecting. + CLock lock(&m_mutex); + + // use PRIMARY selection as the "clipboard" + Atom selection = XA_PRIMARY; + + // ask the selection for all the formats it has. some owners return + // the TARGETS atom and some the ATOM atom when TARGETS is requested. + Atom format; + CString targets; + if (getDisplayClipboard(selection, m_atomTargets, + requestor, timestamp, &format, &targets) && + (format == m_atomTargets || format == XA_ATOM)) { + // get each target (that we can interpret). some owners return + // some targets multiple times in the list so don't try to get + // those multiple times. + const Atom* targetAtoms = reinterpret_cast(targets.data()); + const SInt32 numTargets = targets.size() / sizeof(Atom); + std::set clipboardFormats; + std::set targets; + log((CLOG_DEBUG "selection has %d targets", numTargets)); + for (SInt32 i = 0; i < numTargets; ++i) { + Atom format = targetAtoms[i]; + log((CLOG_DEBUG " source target %d", format)); + + // skip already handled targets + if (targets.count(format) > 0) { + log((CLOG_DEBUG " skipping handled target %d", format)); + continue; + } + + // mark this target as done + targets.insert(format); + + // determine the expected clipboard format + IClipboard::EFormat expectedFormat = getFormat(format); + + // if we can use the format and we haven't already retrieved + // it then get it + if (expectedFormat == IClipboard::kNum) { + log((CLOG_DEBUG " no format for target", format)); + continue; + } + if (clipboardFormats.count(expectedFormat) > 0) { + log((CLOG_DEBUG " skipping handled format %d", expectedFormat)); + continue; + } + + CString data; + if (!getDisplayClipboard(selection, format, + requestor, timestamp, &format, &data)) { + log((CLOG_DEBUG " no data for target", format)); + continue; + } + + // use the actual format, not the expected + IClipboard::EFormat actualFormat = getFormat(format); + if (actualFormat == IClipboard::kNum) { + log((CLOG_DEBUG " no format for target", format)); + continue; + } + if (clipboardFormats.count(actualFormat) > 0) { + log((CLOG_DEBUG " skipping handled format %d", actualFormat)); + continue; + } + + // add to clipboard and note we've done it + clipboard->add(actualFormat, data); + clipboardFormats.insert(actualFormat); + } + } + else { + // non-ICCCM conforming selection owner. try TEXT format. + // FIXME + log((CLOG_DEBUG "selection doesn't support TARGETS, format is %d", format)); + } + + // done with clipboard + clipboard->close(); +*/ +} + +LRESULT CALLBACK CMSWindowsScreen::wndProc( + HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hwnd, msg, wParam, lParam); +} diff --git a/synergy/CMSWindowsScreen.h b/synergy/CMSWindowsScreen.h new file mode 100644 index 00000000..4c51be00 --- /dev/null +++ b/synergy/CMSWindowsScreen.h @@ -0,0 +1,73 @@ +#ifndef CMSWINDOWSSCREEN_H +#define CMSWINDOWSSCREEN_H + +#include "CMutex.h" +#include "IClipboard.h" +#include "BasicTypes.h" +#include + +class CString; +class CThread; + +class CMSWindowsScreen { + public: + CMSWindowsScreen(); + virtual ~CMSWindowsScreen(); + + // manipulators + + static void init(HINSTANCE); + + protected: + // runs an event loop and returns when WM_QUIT is received + void doRun(); + + // sends WM_QUIT to force doRun() to return + void doStop(); + + // open the X display. calls onOpenDisplay() after opening the display, + // getting the screen, its size, and root window. then it starts the + // event thread. + void openDisplay(); + + // destroy the window and close the display. calls onCloseDisplay() + // after the event thread has been shut down but before the display + // is closed. + void closeDisplay(); + + // get the application instance handle and the registered window + // class atom + static HINSTANCE getInstance(); + ATOM getClass() const; + + // get the size of the screen + void getScreenSize(SInt32* w, SInt32* h) const; + + // wait for and get the next message. cancellable. + void getEvent(MSG*) const; + + // copy the clipboard contents to clipboard + void getDisplayClipboard(IClipboard* clipboard, HWND) const; + + // called by doRun() to handle an event + virtual bool onEvent(MSG*) = 0; + + // called by openDisplay() to allow subclasses to prepare the display + virtual void onOpenDisplay() = 0; + + // called by closeDisplay() to + virtual void onCloseDisplay() = 0; + + private: + static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); + + private: + static HINSTANCE s_instance; + ATOM m_class; + HICON m_icon; + HCURSOR m_cursor; + SInt32 m_w, m_h; + DWORD m_thread; +}; + +#endif diff --git a/synergy/CXWindowsClipboard.h b/synergy/CXWindowsClipboard.h index a67aeed8..334af77c 100644 --- a/synergy/CXWindowsClipboard.h +++ b/synergy/CXWindowsClipboard.h @@ -17,4 +17,3 @@ class CXWindowsClipboard : public IClipboard { }; #endif - diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 3559de52..dafbb648 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -17,7 +17,8 @@ CXWindowsScreen::CXWindowsScreen() : m_display(NULL), m_root(None), - m_w(0), m_h(0) + m_w(0), m_h(0), + m_stop(false) { // do nothing } @@ -58,24 +59,11 @@ void CXWindowsScreen::openDisplay() // let subclass prep display onOpenDisplay(); - - // start processing events - m_eventThread = new CThread(new TMethodJob( - this, &CXWindowsScreen::eventThread)); } void CXWindowsScreen::closeDisplay() { assert(m_display != NULL); - assert(m_eventThread != NULL); - - // stop event thread - log((CLOG_DEBUG "stopping event thread")); - m_eventThread->cancel(); - m_eventThread->wait(); - delete m_eventThread; - m_eventThread = NULL; - log((CLOG_DEBUG "stopped event thread")); // let subclass close down display onCloseDisplay(); @@ -143,18 +131,31 @@ Cursor CXWindowsScreen::createBlankCursor() const return cursor; } -void CXWindowsScreen::getEvent(XEvent* xevent) const +bool CXWindowsScreen::getEvent(XEvent* xevent) const { // wait for an event in a cancellable way and don't lock the // display while we're waiting. m_mutex.lock(); - while (XPending(m_display) == 0) { + while (!m_stop && XPending(m_display) == 0) { m_mutex.unlock(); CThread::sleep(0.05); m_mutex.lock(); } - XNextEvent(m_display, xevent); - m_mutex.unlock(); + if (m_stop) { + m_mutex.unlock(); + return true; + } + else { + XNextEvent(m_display, xevent); + m_mutex.unlock(); + return false; + } +} + +void CXWindowsScreen::doStop() +{ + CLock lock(&m_mutex); + m_stop = true; } void CXWindowsScreen::getDisplayClipboard( diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 66cac76e..98088925 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -7,7 +7,6 @@ #include class CString; -class CThread; class CXWindowsScreen { public: @@ -50,7 +49,10 @@ class CXWindowsScreen { Cursor createBlankCursor() const; // wait for and get the next X event. cancellable. - void getEvent(XEvent*) const; + bool getEvent(XEvent*) const; + + // cause getEvent() to return false immediately and forever after + void doStop(); // copy the clipboard contents to clipboard. requestor must be a // valid window; it will be used to receive the transfer. timestamp @@ -64,9 +66,6 @@ class CXWindowsScreen { // called by closeDisplay() to virtual void onCloseDisplay() = 0; - // override to process X events - virtual void eventThread(void*) = 0; - private: struct PropertyNotifyInfo { public: @@ -87,11 +86,11 @@ class CXWindowsScreen { XEvent* xevent, XPointer arg); private: - CThread* m_eventThread; Display* m_display; int m_screen; Window m_root; SInt32 m_w, m_h; + bool m_stop; // atoms we'll need Atom m_atomTargets; diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index e42b2706..9eaae81e 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -41,4 +41,3 @@ class IClipboard : public IInterface { }; #endif - diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 284728b4..17341b63 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -11,6 +11,15 @@ class IPrimaryScreen : public IInterface { public: // manipulators + // enter the screen's message loop. this returns when it detects + // the application should terminate or when stop() is called. + // the screen must be open()'d before run() and must not be + // close()'d until run() returns. + virtual void run() = 0; + + // cause run() to return + virtual void stop() = 0; + // initialize the screen and start reporting events to the server. // events should be reported no matter where on the screen they // occur but do not interfere with normal event dispatch. the @@ -26,8 +35,9 @@ class IPrimaryScreen : public IInterface { // called when the user navigates back to the primary screen. // warp the cursor to the given coordinates, unhide it, and - // ungrab the input devices. every call to method has a matching - // call to leave() which preceeds it. + // ungrab the input devices. every call to enter has a matching + // call to leave() which preceeds it, however the screen can + // assume an implicit call to enter() in the call to open(). virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute) = 0; // called when the user navigates off the primary screen. hide diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 29db0066..88893da6 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -13,6 +13,15 @@ class ISecondaryScreen : public IInterface { public: // manipulators + // enter the screen's message loop. this returns when it detects + // the application should terminate or when stop() is called. + // the screen must be open()'d before run() and must not be + // close()'d until run() returns. + virtual void run() = 0; + + // cause run() to return + virtual void stop() = 0; + // initialize the screen, hide the cursor, and disable the screen // saver. start reporting certain events to the client (clipboard // stolen and screen size changed). diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp new file mode 100644 index 00000000..9bff2e3d --- /dev/null +++ b/synergy/synergy.dsp @@ -0,0 +1,186 @@ +# Microsoft Developer Studio Project File - Name="synergy" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=synergy - Win32 Debug +!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 "synergy.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 "synergy.mak" CFG="synergy - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synergy - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "synergy - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synergy - 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" +# 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 /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "synergy - 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" +# 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 "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "synergy - Win32 Release" +# Name "synergy - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CInputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.cpp +# End Source File +# Begin Source File + +SOURCE=.\XSynergy.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CInputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.h +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\IClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\ISecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IServerProtocol.h +# End Source File +# Begin Source File + +SOURCE=.\ISocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\KeyTypes.h +# End Source File +# Begin Source File + +SOURCE=.\MouseTypes.h +# End Source File +# Begin Source File + +SOURCE=.\ProtocolTypes.h +# End Source File +# Begin Source File + +SOURCE=.\XSynergy.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project From f15c9df85b1cd75a1138cc1daf1cf6a91bf32854 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 25 Nov 2001 18:32:41 +0000 Subject: [PATCH 035/807] added platform independent clipboard transfer stuff clipboard owner support (MS windows done, X windows partial) added key transfer on ms windows mutex fixes in CClient (had race conditions) faster debug output in ms windows changed temporary screen name to "secondary" network fixes on ms windows (poll returned wrong result) fixed transparent cursor on ms windows --- client/CClient.cpp | 106 ++++- client/CClient.h | 5 + client/CMSWindowsSecondaryScreen.cpp | 176 +++++--- client/CMSWindowsSecondaryScreen.h | 8 +- client/CXWindowsSecondaryScreen.cpp | 61 ++- client/CXWindowsSecondaryScreen.h | 3 + client/client.cpp | 4 +- client/client.dsp | 4 +- net/CNetwork.cpp | 4 + notes | 11 +- server/CMSWindowsPrimaryScreen.cpp | 589 +++++++++++++++++++++------ server/CMSWindowsPrimaryScreen.h | 14 +- server/CServer.cpp | 135 +++++- server/CServer.h | 13 +- server/CServerProtocol.h | 7 +- server/CServerProtocol1_0.cpp | 35 +- server/CServerProtocol1_0.h | 7 +- server/CSynergyHook.cpp | 5 +- server/CSynergyHook.h | 8 +- server/CXWindowsPrimaryScreen.cpp | 56 ++- server/CXWindowsPrimaryScreen.h | 1 + server/makehook.dsp | 4 +- server/server.cpp | 6 +- server/server.dsp | 4 +- server/synrgyhk.dsp | 8 +- synergy.dsw | 4 + synergy/CClipboard.cpp | 137 +++++++ synergy/CClipboard.h | 49 +++ synergy/CMSWindowsClipboard.cpp | 168 +++++++- synergy/CMSWindowsClipboard.h | 13 +- synergy/CMSWindowsScreen.cpp | 25 +- synergy/CMSWindowsScreen.h | 9 +- synergy/CProtocolUtil.cpp | 21 + synergy/CProtocolUtil.h | 3 +- synergy/CXWindowsScreen.cpp | 134 +++++- synergy/CXWindowsScreen.h | 27 +- synergy/IClipboard.h | 8 +- synergy/IPrimaryScreen.h | 6 +- synergy/ISecondaryScreen.h | 12 +- synergy/IServerProtocol.h | 9 +- synergy/Makefile | 1 + synergy/ProtocolTypes.h | 6 +- synergy/synergy.dsp | 8 + 43 files changed, 1590 insertions(+), 324 deletions(-) create mode 100644 synergy/CClipboard.cpp create mode 100644 synergy/CClipboard.h diff --git a/client/CClient.cpp b/client/CClient.cpp index 33447115..5721ac5c 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -2,8 +2,10 @@ #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" +#include "CClipboard.h" #include "ISecondaryScreen.h" #include "ProtocolTypes.h" +#include "CLock.h" #include "CThread.h" #include "CTimerThread.h" #include "XSynergy.h" @@ -63,7 +65,7 @@ void CClient::run(const CNetworkAddress& serverAddress) closeSecondaryScreen(); } catch (XBase& e) { - log((CLOG_ERR "client error: %s\n", e.what())); + log((CLOG_ERR "client error: %s", e.what())); // clean up thread->cancel(); @@ -83,6 +85,17 @@ void CClient::run(const CNetworkAddress& serverAddress) } } +void CClient::onClipboardChanged() +{ + log((CLOG_DEBUG "sending clipboard changed")); + CLock lock(&m_mutex); + if (m_output != NULL) { + // m_output can be NULL if the screen calls this method + // before we've gotten around to connecting to the server. + CProtocolUtil::writef(m_output, kMsgCClipboard); + } +} + #include "CTCPSocket.h" // FIXME void CClient::runSession(void*) { @@ -136,10 +149,10 @@ void CClient::runSession(void*) // say hello back log((CLOG_DEBUG "say hello version %d.%d", kMajorVersion, kMinorVersion)); CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s", - kMajorVersion, kMinorVersion, - m_name.size(), m_name.data()); + kMajorVersion, kMinorVersion, &m_name); // record streams in a more useful place + CLock lock(&m_mutex); m_input = input.get(); m_output = output.get(); } @@ -291,7 +304,10 @@ void CClient::closeSecondaryScreen() void CClient::onEnter() { SInt32 x, y; - CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y); + } m_screen->enter(x, y); } @@ -302,13 +318,16 @@ void CClient::onLeave() void CClient::onGrabClipboard() { - // FIXME + m_screen->grabClipboard(); } void CClient::onScreenSaver() { SInt32 on; - CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on); + } // FIXME } @@ -317,24 +336,63 @@ void CClient::onQueryInfo() SInt32 w, h; m_screen->getSize(&w, &h); SInt32 zoneSize = m_screen->getJumpZoneSize(); + log((CLOG_DEBUG "sending info size=%d,%d zone=%d", w, h, zoneSize)); + CLock lock(&m_mutex); CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); } void CClient::onQueryClipboard() { - // FIXME + // parse message + UInt32 seqNum; + CClipboard clipboard; + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgQClipboard + 4, &seqNum); + } + log((CLOG_DEBUG "received query clipboard seqnum=%d", seqNum)); + + // get screen's clipboard data + m_screen->getClipboard(&clipboard); + + // marshall the data + CString data = clipboard.marshall(); + + // send it + log((CLOG_DEBUG "sending clipboard seqnum=%d, size=%d", seqNum, data.size())); + { + CLock lock(&m_mutex); + CProtocolUtil::writef(m_output, kMsgDClipboard, seqNum, &data); + } } void CClient::onSetClipboard() { - // FIXME + CString data; + { + // parse message + UInt32 seqNum; + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDClipboard + 4, &seqNum, &data); + } + log((CLOG_DEBUG "received clipboard size=%d", data.size())); + + // unmarshall + CClipboard clipboard; + clipboard.unmarshall(data); + + // set screen's clipboard + m_screen->setClipboard(&clipboard); } void CClient::onKeyDown() { SInt32 id, mask; - CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); + } m_screen->keyDown(static_cast(id), static_cast(mask)); } @@ -342,7 +400,10 @@ void CClient::onKeyDown() void CClient::onKeyRepeat() { SInt32 id, mask, count; - CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); + } m_screen->keyRepeat(static_cast(id), static_cast(mask), count); @@ -351,7 +412,10 @@ void CClient::onKeyRepeat() void CClient::onKeyUp() { SInt32 id, mask; - CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); + } m_screen->keyUp(static_cast(id), static_cast(mask)); } @@ -359,27 +423,39 @@ void CClient::onKeyUp() void CClient::onMouseDown() { SInt32 id; - CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); + } m_screen->mouseDown(static_cast(id)); } void CClient::onMouseUp() { SInt32 id; - CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); + } m_screen->mouseUp(static_cast(id)); } void CClient::onMouseMove() { SInt32 x, y; - CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); + } m_screen->mouseMove(x, y); } void CClient::onMouseWheel() { SInt32 delta; - CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); + } m_screen->mouseWheel(delta); } diff --git a/client/CClient.h b/client/CClient.h index c332ae7c..b214dfc3 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -1,6 +1,7 @@ #ifndef CCLIENT_H #define CCLIENT_H +#include "CMutex.h" #include "CString.h" #include "BasicTypes.h" @@ -18,6 +19,9 @@ class CClient { void run(const CNetworkAddress& serverAddress); + // handle events on client's screen + void onClipboardChanged(); + // accessors @@ -45,6 +49,7 @@ class CClient { void onMouseWheel(); private: + CMutex m_mutex; CString m_name; IInputStream* m_input; IOutputStream* m_output; diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 439b5876..94507e4e 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -1,5 +1,7 @@ #include "CMSWindowsSecondaryScreen.h" +#include "CMSWindowsClipboard.h" #include "CClient.h" +#include "CClipboard.h" #include "CThread.h" #include "CLog.h" #include @@ -10,7 +12,8 @@ CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : m_client(NULL), - m_window(NULL) + m_window(NULL), + m_nextClipboardWindow(NULL) { // do nothing } @@ -20,6 +23,8 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() assert(m_window == NULL); } +static CString s_log; +static CString s_logMore; static HWND s_debug = NULL; static HWND s_debugLog = NULL; static DWORD s_thread = 0; @@ -32,39 +37,40 @@ static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lPar case WM_CLOSE: PostQuitMessage(0); return TRUE; + + case WM_APP: + if (!s_logMore.empty()) { + if (s_log.size() > 20000) + s_log = s_logMore; + else + s_log += s_logMore; + s_logMore = ""; + SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)s_log.c_str()); + SendMessage(s_debugLog, EM_SETSEL, s_log.size(), s_log.size()); + SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0); + } + return TRUE; } return FALSE; } static void debugOutput(const char* msg) { - if (s_thread != 0) { - const DWORD threadID = ::GetCurrentThreadId(); - if (threadID != s_thread) { - GetDesktopWindow(); - AttachThreadInput(threadID, s_thread, TRUE); - } - } - DWORD len = SendMessage(s_debugLog, WM_GETTEXTLENGTH, 0, 0); - if (len > 20000) { - SendMessage(s_debugLog, EM_SETSEL, -1, 0); - SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)msg); - } - else { - SendMessage(s_debugLog, EM_SETSEL, -1, len); - SendMessage(s_debugLog, EM_REPLACESEL, FALSE, (LPARAM)(LPCTSTR)msg); - } - SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0); + s_logMore += msg; + PostMessage(s_debug, WM_APP, 0, 0); } void CMSWindowsSecondaryScreen::run() { CLog::setOutputter(&debugOutput); + log((CLOG_INFO "entering event loop")); doRun(); + log((CLOG_INFO "exiting event loop")); CLog::setOutputter(NULL); } void CMSWindowsSecondaryScreen::stop() { + log((CLOG_INFO "requesting event loop stop")); doStop(); } @@ -73,6 +79,8 @@ void CMSWindowsSecondaryScreen::open(CClient* client) assert(m_client == NULL); assert(client != NULL); + log((CLOG_INFO "opening screen")); + // set the client m_client = client; @@ -84,6 +92,8 @@ void CMSWindowsSecondaryScreen::close() { assert(m_client != NULL); + log((CLOG_INFO "closing screen")); + // close the display closeDisplay(); @@ -95,6 +105,8 @@ void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) { assert(m_window != NULL); + log((CLOG_INFO "entering screen at %d,%d", x, y)); + // warp to requested location SInt32 w, h; getScreenSize(&w, &h); @@ -104,6 +116,7 @@ void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) 0, 0); // show cursor + log((CLOG_INFO "show cursor")); ShowWindow(m_window, SW_HIDE); } @@ -111,6 +124,8 @@ void CMSWindowsSecondaryScreen::leave() { assert(m_window != NULL); + log((CLOG_INFO "leaving screen")); + // move hider window under the mouse (rather than moving the mouse // somewhere else on the screen) POINT point; @@ -118,10 +133,27 @@ void CMSWindowsSecondaryScreen::leave() MoveWindow(m_window, point.x, point.y, 1, 1, FALSE); // raise and show the hider window. take activation. + log((CLOG_INFO "hide cursor")); ShowWindow(m_window, SW_SHOWNORMAL); - // hide cursor by moving it into the hider window - SetCursorPos(point.x, point.y); + // if we think we own the clipboard but we don't then somebody + // grabbed the clipboard on this screen without us knowing. + // tell the server that this screen grabbed the clipboard. + // + // this works around bugs in the clipboard viewer chain. + // sometimes NT will simply never send WM_DRAWCLIPBOARD + // messages for no apparent reason and rebooting fixes the + // problem. since we don't want a broken clipboard until the + // next reboot we do this double check. clipboard ownership + // won't be reflected on other screens until we leave but at + // least the clipboard itself will work. + HWND clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != clipboardOwner) { + m_clipboardOwner = clipboardOwner; + if (m_clipboardOwner != m_window) { + m_client->onClipboardChanged(); + } + } } void CMSWindowsSecondaryScreen::keyDown( @@ -222,6 +254,25 @@ void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); } +void CMSWindowsSecondaryScreen::setClipboard( + const IClipboard* src) +{ + assert(m_window != NULL); + + CMSWindowsClipboard dst(m_window); + CClipboard::copy(&dst, src); +} + +void CMSWindowsSecondaryScreen::grabClipboard() +{ + assert(m_window != NULL); + + CMSWindowsClipboard clipboard(m_window); + if (clipboard.open()) { + clipboard.close(); + } +} + void CMSWindowsSecondaryScreen::getSize( SInt32* width, SInt32* height) const { @@ -233,6 +284,15 @@ SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const return 0; } +void CMSWindowsSecondaryScreen::getClipboard( + IClipboard* dst) const +{ + assert(m_window != NULL); + + CMSWindowsClipboard src(m_window); + CClipboard::copy(dst, &src); +} + #include "resource.h" // FIXME void CMSWindowsSecondaryScreen::onOpenDisplay() @@ -246,6 +306,10 @@ s_debugLog = ::GetDlgItem(s_debug, IDC_LOG); CLog::setOutputter(&debugOutput); ShowWindow(s_debug, SW_SHOWNORMAL); + // initialize clipboard owner to current owner. we don't want + // to take ownership of the clipboard just by starting up. + m_clipboardOwner = GetClipboardOwner(); + // create the cursor hiding window. this window is used to hide the // cursor when it's not on the screen. the window is hidden as soon // as the cursor enters the screen or the display's real cursor is @@ -253,19 +317,26 @@ ShowWindow(s_debug, SW_SHOWNORMAL); m_window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, (LPCTSTR)getClass(), "Synergy", - WS_POPUP | WS_DISABLED, + WS_POPUP, 0, 0, 1, 1, NULL, NULL, getInstance(), NULL); // hide the cursor leave(); + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); } void CMSWindowsSecondaryScreen::onCloseDisplay() { assert(m_window != NULL); + // remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + // destroy window DestroyWindow(m_window); m_window = NULL; @@ -276,49 +347,56 @@ s_debug = NULL; s_thread = 0; } -bool CMSWindowsSecondaryScreen::onEvent(MSG* msg) +bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg) { if (IsDialogMessage(s_debug, msg)) { return true; } + return false; +} - // handle event - switch (msg->message) { + +LRESULT CMSWindowsSecondaryScreen::onEvent( + HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + switch (msg) { // FIXME -- handle display changes case WM_PAINT: - ValidateRect(m_window, NULL); - return true; - - case WM_MOUSEMOVE: - // mouse was moved. hide the hider window. - ShowWindow(m_window, SW_HIDE); - break; + ValidateRect(hwnd, NULL); + return 0; case WM_ACTIVATEAPP: - if (msg->wParam == FALSE) { + if (wParam == FALSE) { // some other app activated. hide the hider window. + log((CLOG_INFO "show cursor")); ShowWindow(m_window, SW_HIDE); } break; -/* - // FIXME -- handle screen resolution changes + case WM_DRAWCLIPBOARD: + log((CLOG_DEBUG "clipboard was taken")); - case SelectionClear: - target->XXX(xevent.xselectionclear.); - break; + // first pass it on + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); - case SelectionNotify: - target->XXX(xevent.xselection.); - break; + // now notify client that somebody changed the clipboard (unless + // we're now the owner, in which case it's because we took + // ownership). + m_clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != m_window) { + m_client->onClipboardChanged(); + } + return 0; - case SelectionRequest: - target->XXX(xevent.xselectionrequest.); - break; -*/ + case WM_CHANGECBCHAIN: + if (m_nextClipboardWindow == (HWND)wParam) + m_nextClipboardWindow = (HWND)lParam; + else + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + return 0; } - - return false; + return DefWindowProc(hwnd, msg, wParam, lParam); } static const UINT g_latin1[] = @@ -745,9 +823,11 @@ static const UINT g_miscellany[] = /* 0xc8 */ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, +/* FIXME -- want to use LSHIFT, LCONTROL, and LMENU but those don't seem + * to affect the shift state for VkKeyScan. */ + /* 0xe0 */ 0, VK_SHIFT, VK_RSHIFT, VK_CONTROL, /* 0xe4 */ VK_RCONTROL, VK_CAPITAL, 0, VK_LWIN, - /* 0xe8 */ VK_RWIN, VK_LMENU, VK_RMENU, 0, 0, 0, 0, 0, + /* 0xe8 */ VK_RWIN, VK_MENU, VK_RMENU, 0, 0, 0, 0, 0, /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE }; diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 4362d400..12763e60 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -23,12 +23,16 @@ class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScre virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); virtual void mouseWheel(SInt32 delta); + virtual void setClipboard(const IClipboard*); + virtual void grabClipboard(); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; + virtual void getClipboard(IClipboard*) const; protected: // CMSWindowsScreen overrides - virtual bool onEvent(MSG*); + virtual bool onPreTranslate(MSG*); + virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); virtual void onOpenDisplay(); virtual void onCloseDisplay(); @@ -38,6 +42,8 @@ class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScre private: CClient* m_client; HWND m_window; + HWND m_nextClipboardWindow; + HWND m_clipboardOwner; }; #endif diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index d4b99bd9..beb143df 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -19,7 +19,7 @@ CXWindowsSecondaryScreen::CXWindowsSecondaryScreen() : CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() { - assert(m_window == None); + assert(m_window == None); } void CXWindowsSecondaryScreen::run() @@ -43,21 +43,46 @@ void CXWindowsSecondaryScreen::run() break; } -/* - // FIXME -- handle screen resolution changes - case SelectionClear: - target->XXX(xevent.xselectionclear.); + // we just lost the selection. that means someone else + // grabbed the selection so this screen is now the + // selection owner. report that to the server. + m_client->onClipboardChanged(); break; case SelectionNotify: - target->XXX(xevent.xselection.); + // notification of selection transferred. we shouldn't + // get this here because we handle them in the selection + // retrieval methods. we'll just delete the property + // with the data (satisfying the usual ICCCM protocol). + if (xevent.xselection.property != None) { + CDisplayLock display(this); + XDeleteProperty(display, m_window, xevent.xselection.property); + } break; case SelectionRequest: - target->XXX(xevent.xselectionrequest.); + // somebody is asking for clipboard data + if (xevent.xselectionrequest.owner == m_window) { + addClipboardRequest(m_window, + xevent.xselectionrequest.requestor, + xevent.xselectionrequest.selection, + xevent.xselectionrequest.target, + xevent.xselectionrequest.property, + xevent.xselectionrequest.time); + } + break; + + case PropertyNotify: + // clipboard transfers involve property changes so forward + // the event to the superclass. we only care about the + // deletion of properties. + if (xevent.xproperty.state == PropertyDelete) { + processClipboardRequest(xevent.xproperty.window, + xevent.xproperty.atom, + xevent.xproperty.time); + } break; -*/ } } } @@ -167,6 +192,19 @@ void CXWindowsSecondaryScreen::mouseWheel(SInt32) // FIXME } +void CXWindowsSecondaryScreen::setClipboard( + const IClipboard* clipboard) +{ + // FIXME -- don't use CurrentTime + setDisplayClipboard(clipboard, m_window, CurrentTime); +} + +void CXWindowsSecondaryScreen::grabClipboard() +{ + // FIXME -- don't use CurrentTime + setDisplayClipboard(NULL, m_window, CurrentTime); +} + void CXWindowsSecondaryScreen::getSize( SInt32* width, SInt32* height) const { @@ -178,6 +216,13 @@ SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const return 0; } +void CXWindowsSecondaryScreen::getClipboard( + IClipboard* clipboard) const +{ + // FIXME -- don't use CurrentTime + getDisplayClipboard(clipboard, m_window, CurrentTime); +} + void CXWindowsSecondaryScreen::onOpenDisplay() { assert(m_window == None); diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 2ee6989b..11586f26 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -23,8 +23,11 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); virtual void mouseWheel(SInt32 delta); + virtual void setClipboard(const IClipboard*); + virtual void grabClipboard(); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; + virtual void getClipboard(IClipboard*) const; protected: // CXWindowsScreen overrides diff --git a/client/client.cpp b/client/client.cpp index abca2c8f..6cbf991c 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -41,7 +41,7 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) } try { - realMain("ingrid", __argv[1], 50001); + realMain("secondary", __argv[1], 50001); return 0; } catch (XBase& e) { @@ -64,7 +64,7 @@ int main(int argc, char** argv) } try { - realMain("ingrid", argv[1], 50001); + realMain("secondary", argv[1], 50001); return 0; } catch (XBase& e) { diff --git a/client/client.dsp b/client/client.dsp index 905b486a..b7ffa272 100644 --- a/client/client.dsp +++ b/client/client.dsp @@ -38,7 +38,7 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" +# PROP Output_Dir "../Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c @@ -64,7 +64,7 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" +# PROP Output_Dir "../Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp index 4eac18f5..7093ce3e 100644 --- a/net/CNetwork.cpp +++ b/net/CNetwork.cpp @@ -44,6 +44,7 @@ int (PASCAL FAR *CNetwork::gethosterror)(void); #if defined(CONFIG_PLATFORM_WIN32) +int (PASCAL FAR *CNetwork::select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); int (PASCAL FAR *CNetwork::WSACleanup)(void); int (PASCAL FAR *CNetwork::__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); const int CNetwork::Error = SOCKET_ERROR; @@ -220,6 +221,7 @@ int PASCAL FAR CNetwork::poll2(PollEntry fd[], int nfds, int timeout) return Error; if (n == 0) return 0; + n = 0; for (i = 0; i < nfds; ++i) { fd[i].revents = 0; if (FD_ISSET(fd[i].fd, &readSet)) @@ -228,6 +230,8 @@ int PASCAL FAR CNetwork::poll2(PollEntry fd[], int nfds, int timeout) fd[i].revents |= kPOLLOUT; if (FD_ISSET(fd[i].fd, &errSet)) fd[i].revents |= kPOLLERR; + if (fd[i].revents != 0) + ++n; } return n; } diff --git a/notes b/notes index 14edcc1b..f4933c81 100644 --- a/notes +++ b/notes @@ -87,11 +87,12 @@ win32: handle display changes --- -win32 key translation (client) - get character for key (with appropriate shift) - keypad enter -> VK_??? - sys req -> VK_??? - compose -> VK_??? +not sending VK_?WIN and VK_APPS. possibly hotkeys being stolen. + +VkKeyScan() doesn't get proper shift state unless we map shift +(etc?) to VK_SHIFT not VK_LSHIFT or VK_RSHIFT. + +not handling international characters X11 key translation (server) handle compose key? diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 1580de71..d2775577 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -1,6 +1,8 @@ #include "CMSWindowsPrimaryScreen.h" +#include "CMSWindowsClipboard.h" #include "CServer.h" #include "CSynergyHook.h" +#include "XSynergy.h" #include "CThread.h" #include "CLog.h" #include @@ -13,6 +15,8 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : m_server(NULL), m_active(false), m_window(NULL), + m_nextClipboardWindow(NULL), + m_clipboardOwner(NULL), m_hookLibrary(NULL), m_mark(0), m_markReceived(0) @@ -26,6 +30,8 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() assert(m_hookLibrary == NULL); } +static CString s_log; +static CString s_logMore; static HWND s_debug = NULL; static HWND s_debugLog = NULL; static DWORD s_thread = 0; @@ -38,28 +44,26 @@ static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lPar case WM_CLOSE: PostQuitMessage(0); return TRUE; + + case WM_APP: + if (!s_logMore.empty()) { + if (s_log.size() > 20000) + s_log = s_logMore; + else + s_log += s_logMore; + s_logMore = ""; + SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)s_log.c_str()); + SendMessage(s_debugLog, EM_SETSEL, s_log.size(), s_log.size()); + SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0); + } + return TRUE; } return FALSE; } static void debugOutput(const char* msg) { - if (s_thread != 0) { - const DWORD threadID = ::GetCurrentThreadId(); - if (threadID != s_thread) { - GetDesktopWindow(); - AttachThreadInput(threadID, s_thread, TRUE); - } - } - DWORD len = SendMessage(s_debugLog, WM_GETTEXTLENGTH, 0, 0); - if (len > 20000) { - SendMessage(s_debugLog, EM_SETSEL, -1, 0); - SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)msg); - } - else { - SendMessage(s_debugLog, EM_SETSEL, -1, len); - SendMessage(s_debugLog, EM_REPLACESEL, FALSE, (LPARAM)(LPCTSTR)msg); - } - SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0); + s_logMore += msg; + PostMessage(s_debug, WM_APP, 0, 0); } void CMSWindowsPrimaryScreen::run() @@ -114,6 +118,13 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) void CMSWindowsPrimaryScreen::doEnter() { + // release the capture + ReleaseCapture(); + + // hide our window and restore the foreground window + SetForegroundWindow(m_lastActive); + ShowWindow(m_window, SW_HIDE); + // set the zones that should cause a jump SInt32 w, h; getScreenSize(&w, &h); @@ -136,6 +147,16 @@ void CMSWindowsPrimaryScreen::leave() // all messages prior to now are invalid nextMark(); + // remember the active window before we leave + m_lastActive = GetForegroundWindow(); + + // show our window and put it in the foreground + ShowWindow(m_window, SW_SHOW); + SetForegroundWindow(m_window); + + // capture the cursor so we don't lose keyboard input + SetCapture(m_window); + // relay all mouse and keyboard events SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress( m_hookLibrary, "setRelay"); @@ -151,6 +172,30 @@ void CMSWindowsPrimaryScreen::leave() // local client now active m_active = true; + + // if we think we own the clipboard but we don't then somebody + // grabbed the clipboard on this screen without us knowing. + // tell the server that this screen grabbed the clipboard. + // + // this works around bugs in the clipboard viewer chain. + // sometimes NT will simply never send WM_DRAWCLIPBOARD + // messages for no apparent reason and rebooting fixes the + // problem. since we don't want a broken clipboard until the + // next reboot we do this double check. clipboard ownership + // won't be reflected on other screens until we leave but at + // least the clipboard itself will work. + HWND clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != clipboardOwner) { + try { + m_clipboardOwner = clipboardOwner; + if (m_clipboardOwner != m_window) { + m_server->grabClipboard(); + } + } + catch (XBadClient&) { + // ignore + } + } } void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) @@ -164,18 +209,22 @@ void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) } void CMSWindowsPrimaryScreen::setClipboard( - const IClipboard* /*clipboard*/) + const IClipboard* src) { assert(m_window != NULL); - // FIXME -- should we retry until we get it? - if (!OpenClipboard(m_window)) - return; - if (EmptyClipboard()) { - log((CLOG_DEBUG "grabbed clipboard")); - // FIXME -- set the clipboard data + CMSWindowsClipboard dst(m_window); + CClipboard::copy(&dst, src); +} + +void CMSWindowsPrimaryScreen::grabClipboard() +{ + assert(m_window != NULL); + + CMSWindowsClipboard clipboard(m_window); + if (clipboard.open()) { + clipboard.close(); } - CloseClipboard(); } void CMSWindowsPrimaryScreen::getSize( @@ -190,11 +239,12 @@ SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const } void CMSWindowsPrimaryScreen::getClipboard( - IClipboard* clipboard) const + IClipboard* dst) const { - // FIXME -- put this in superclass? - // FIXME -- don't use CurrentTime -// getDisplayClipboard(clipboard, m_window, CurrentTime); + assert(m_window != NULL); + + CMSWindowsClipboard src(m_window); + CClipboard::copy(dst, &src); } #include "resource.h" // FIXME @@ -211,6 +261,10 @@ s_debugLog = ::GetDlgItem(s_debug, IDC_LOG); CLog::setOutputter(&debugOutput); ShowWindow(s_debug, SW_SHOWNORMAL); + // initialize clipboard owner to current owner. we don't want + // to take ownership of the clipboard just by starting up. + m_clipboardOwner = GetClipboardOwner(); + // create the window m_window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, @@ -219,6 +273,10 @@ ShowWindow(s_debug, SW_SHOWNORMAL); 0, 0, 1, 1, NULL, NULL, getInstance(), NULL); + assert(m_window != NULL); + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); // load the hook library bool hooked = false; @@ -232,6 +290,8 @@ ShowWindow(s_debug, SW_SHOWNORMAL); } } if (!hooked) { + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; DestroyWindow(m_window); m_window = NULL; // FIXME -- throw @@ -258,6 +318,10 @@ void CMSWindowsPrimaryScreen::onCloseDisplay() FreeLibrary(m_hookLibrary); m_hookLibrary = NULL; + // remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + // destroy window DestroyWindow(m_window); m_window = NULL; @@ -268,7 +332,7 @@ s_debug = NULL; s_thread = 0; } -bool CMSWindowsPrimaryScreen::onEvent(MSG* msg) +bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) { if (IsDialogMessage(s_debug, msg)) { return true; @@ -276,11 +340,6 @@ if (IsDialogMessage(s_debug, msg)) { // handle event switch (msg->message) { - // FIXME -- handle display changes - case WM_PAINT: - ValidateRect(m_window, NULL); - return true; - case SYNERGY_MSG_MARK: m_markReceived = msg->wParam; return true; @@ -288,7 +347,29 @@ if (IsDialogMessage(s_debug, msg)) { case SYNERGY_MSG_KEY: // ignore if not at current mark if (m_mark == m_markReceived) { - // FIXME -- vk code; key data + KeyModifierMask mask; + const KeyID key = mapKey(msg->wParam, msg->lParam, &mask); +log((CLOG_DEBUG "event: key event vk=%d info=0x%08x", msg->wParam, msg->lParam)); + if (key != kKeyNone) { + if ((msg->lParam & 0x80000000) == 0) { + // key press + const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); + if (repeat >= 2) { + log((CLOG_DEBUG "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); + m_server->onKeyRepeat(key, mask, repeat); + } + else { + log((CLOG_DEBUG "event: key press key=%d mask=0x%04x", key, mask)); + m_server->onKeyDown(key, mask); + } + } + else { + // key release + log((CLOG_DEBUG "event: key release key=%d mask=0x%04x", key, mask)); + m_server->onKeyUp(key, mask); + } + } + } return true; @@ -357,74 +438,46 @@ if (IsDialogMessage(s_debug, msg)) { } return false; -/* - case WM_MOUSEMOVE: { - if (!m_active) { - // mouse entered a jump zone window - POINT p; - p.x = (short)LOWORD(msg.lParam); - p.y = (short)HIWORD(msg.lParam); - ClientToScreen(msg.hwnd, &p); - log((CLOG_DEBUG "event: WM_MOUSEMOVE %d,%d", p.x, p.y)); - m_server->onMouseMovePrimary((SInt32)p.x, (SInt32)p.y); +} + +LRESULT CMSWindowsPrimaryScreen::onEvent( + HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + // FIXME -- handle display changes + case WM_PAINT: + ValidateRect(hwnd, NULL); + return 0; + + case WM_DRAWCLIPBOARD: + log((CLOG_DEBUG "clipboard was taken")); + + // first pass it on + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + + // now notify server that somebody changed the clipboard. + // skip that if we're the new owner. + try { + m_clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != m_window) { + m_server->grabClipboard(); + } } - break; - } - - case KeyPress: { - log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(xevent.xkey.keycode, mask); - if (key != kKeyNULL) { - m_server->onKeyDown(key, mask); + catch (XBadClient&) { + // ignore. this can happen if we receive this event + // before we've fully started up. } - break; - } + return 0; - // FIXME -- simulate key repeat. X sends press/release for - // repeat. must detect auto repeat and use kKeyRepeat. - case KeyRelease: { - log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(xevent.xkey.keycode, mask); - if (key != kKeyNULL) { - m_server->onKeyUp(key, mask); - } - break; - } - - case ButtonPress: { - log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNULL) { - m_server->onMouseDown(button); - } - break; - } - - case ButtonRelease: { - log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNULL) { - m_server->onMouseUp(button); - } - break; - } - - case SelectionClear: - target->XXX(xevent.xselectionclear.); - break; - - case SelectionNotify: - target->XXX(xevent.xselection.); - break; - - case SelectionRequest: - target->XXX(xevent.xselectionrequest.); - break; + case WM_CHANGECBCHAIN: + if (m_nextClipboardWindow == (HWND)wParam) + m_nextClipboardWindow = (HWND)lParam; + else + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); -*/ } void CMSWindowsPrimaryScreen::nextMark() @@ -434,34 +487,274 @@ void CMSWindowsPrimaryScreen::nextMark() PostMessage(m_window, SYNERGY_MSG_MARK, ++m_mark, 0); } -#if 0 -bool CMSWindowsPrimaryScreen::keyboardHook( - int /*code*/, WPARAM wParam, LPARAM lParam) +static const KeyID g_virtualKey[] = { - if (m_active) { - // handle keyboard events - const KeyID key = mapKey(wParam, lParam); - if (key != kKeyNone) { - const KeyModifierMask modifiers = mapModifier(wParam, lParam); - if ((lParam & KF_UP) == 0) { - log((CLOG_DEBUG "event: key press key=%d", key)); - m_server->onKeyDown(key, modifiers); - } - else { - log((CLOG_DEBUG "event: key release key=%d", key)); - m_server->onKeyUp(key, modifiers); - } - return true; - } - } - return false; -} -#endif + /* 0x00 */ kKeyNone, // reserved + /* 0x01 */ kKeyNone, // VK_LBUTTON + /* 0x02 */ kKeyNone, // VK_RBUTTON + /* 0x03 */ 0xff6b, // VK_CANCEL XK_Break + /* 0x04 */ kKeyNone, // VK_MBUTTON + /* 0x05 */ kKeyNone, // undefined + /* 0x06 */ kKeyNone, // undefined + /* 0x07 */ kKeyNone, // undefined + /* 0x08 */ 0xff08, // VK_BACK XK_Backspace + /* 0x09 */ 0xff09, // VK_TAB VK_Tab + /* 0x0a */ kKeyNone, // undefined + /* 0x0b */ kKeyNone, // undefined + /* 0x0c */ 0xff0b, // VK_CLEAR XK_Clear + /* 0x0d */ 0xff0d, // VK_RETURN XK_Return + /* 0x0e */ kKeyNone, // undefined + /* 0x0f */ kKeyNone, // undefined + /* 0x10 */ 0xffe1, // VK_SHIFT XK_Shift_L + /* 0x11 */ 0xffe3, // VK_CONTROL XK_Control_L + /* 0x12 */ 0xffe9, // VK_MENU XK_Alt_L + /* 0x13 */ 0xff13, // VK_PAUSE XK_Pause + /* 0x14 */ 0xffe5, // VK_CAPITAL XK_Caps_Lock + /* 0x15 */ kKeyNone, // VK_KANA + /* 0x16 */ kKeyNone, // VK_HANGUL + /* 0x17 */ kKeyNone, // VK_JUNJA + /* 0x18 */ kKeyNone, // VK_FINAL + /* 0x19 */ kKeyNone, // VK_KANJI + /* 0x1a */ kKeyNone, // undefined + /* 0x1b */ 0xff1b, // VK_ESCAPE XK_Escape + /* 0x1c */ kKeyNone, // VK_CONVERT + /* 0x1d */ kKeyNone, // VK_NONCONVERT + /* 0x1e */ kKeyNone, // VK_ACCEPT + /* 0x1f */ kKeyNone, // VK_MODECHANGE + /* 0x20 */ 0xff20, // VK_SPACE XK_space + /* 0x21 */ 0xff55, // VK_PRIOR XK_Prior + /* 0x22 */ 0xff56, // VK_NEXT XK_Next + /* 0x23 */ 0xff57, // VK_END XK_End + /* 0x24 */ 0xff50, // VK_HOME XK_Home + /* 0x25 */ 0xff51, // VK_LEFT XK_Left + /* 0x26 */ 0xff52, // VK_UP XK_Up + /* 0x27 */ 0xff53, // VK_RIGHT XK_Right + /* 0x28 */ 0xff54, // VK_DOWN XK_Down + /* 0x29 */ 0xff60, // VK_SELECT XK_Select + /* 0x2a */ kKeyNone, // VK_PRINT + /* 0x2b */ 0xff62, // VK_EXECUTE XK_Execute + /* 0x2c */ 0xff61, // VK_SNAPSHOT XK_Print + /* 0x2d */ 0xff63, // VK_INSERT XK_Insert + /* 0x2e */ 0xffff, // VK_DELETE XK_Delete + /* 0x2f */ 0xff6a, // VK_HELP XK_Help + /* 0x30 */ 0x0030, // VK_0 XK_0 + /* 0x31 */ 0x0031, // VK_1 XK_1 + /* 0x32 */ 0x0032, // VK_2 XK_2 + /* 0x33 */ 0x0033, // VK_3 XK_3 + /* 0x34 */ 0x0034, // VK_4 XK_4 + /* 0x35 */ 0x0035, // VK_5 XK_5 + /* 0x36 */ 0x0036, // VK_6 XK_6 + /* 0x37 */ 0x0037, // VK_7 XK_7 + /* 0x38 */ 0x0038, // VK_8 XK_8 + /* 0x39 */ 0x0039, // VK_9 XK_9 + /* 0x3a */ kKeyNone, // undefined + /* 0x3b */ kKeyNone, // undefined + /* 0x3c */ kKeyNone, // undefined + /* 0x3d */ kKeyNone, // undefined + /* 0x3e */ kKeyNone, // undefined + /* 0x3f */ kKeyNone, // undefined + /* 0x40 */ kKeyNone, // undefined + /* 0x41 */ 0x0041, // VK_A XK_A + /* 0x42 */ 0x0042, // VK_B XK_B + /* 0x43 */ 0x0043, // VK_C XK_C + /* 0x44 */ 0x0044, // VK_D XK_D + /* 0x45 */ 0x0045, // VK_E XK_E + /* 0x46 */ 0x0046, // VK_F XK_F + /* 0x47 */ 0x0047, // VK_G XK_G + /* 0x48 */ 0x0048, // VK_H XK_H + /* 0x49 */ 0x0049, // VK_I XK_I + /* 0x4a */ 0x004a, // VK_J XK_J + /* 0x4b */ 0x004b, // VK_K XK_K + /* 0x4c */ 0x004c, // VK_L XK_L + /* 0x4d */ 0x004d, // VK_M XK_M + /* 0x4e */ 0x004e, // VK_N XK_N + /* 0x4f */ 0x004f, // VK_O XK_O + /* 0x50 */ 0x0050, // VK_P XK_P + /* 0x51 */ 0x0051, // VK_Q XK_Q + /* 0x52 */ 0x0052, // VK_R XK_R + /* 0x53 */ 0x0053, // VK_S XK_S + /* 0x54 */ 0x0054, // VK_T XK_T + /* 0x55 */ 0x0055, // VK_U XK_U + /* 0x56 */ 0x0056, // VK_V XK_V + /* 0x57 */ 0x0057, // VK_W XK_W + /* 0x58 */ 0x0058, // VK_X XK_X + /* 0x59 */ 0x0059, // VK_Y XK_Y + /* 0x5a */ 0x005a, // VK_Z XK_Z + /* 0x5b */ 0xffe7, // VK_LWIN XK_Meta_L + /* 0x5c */ 0xffe8, // VK_RWIN XK_Meta_R + /* 0x5d */ 0xff67, // VK_APPS XK_Menu + /* 0x5e */ kKeyNone, // undefined + /* 0x5f */ kKeyNone, // undefined + /* 0x60 */ 0xffb0, // VK_NUMPAD0 XK_KP_0 + /* 0x61 */ 0xffb1, // VK_NUMPAD1 XK_KP_1 + /* 0x62 */ 0xffb2, // VK_NUMPAD2 XK_KP_2 + /* 0x63 */ 0xffb3, // VK_NUMPAD3 XK_KP_3 + /* 0x64 */ 0xffb4, // VK_NUMPAD4 XK_KP_4 + /* 0x65 */ 0xffb5, // VK_NUMPAD5 XK_KP_5 + /* 0x66 */ 0xffb6, // VK_NUMPAD6 XK_KP_6 + /* 0x67 */ 0xffb7, // VK_NUMPAD7 XK_KP_7 + /* 0x68 */ 0xffb8, // VK_NUMPAD8 XK_KP_8 + /* 0x69 */ 0xffb9, // VK_NUMPAD9 XK_KP_9 + /* 0x6a */ 0xffaa, // VK_MULTIPLY XK_KP_Multiply + /* 0x6b */ 0xffab, // VK_ADD XK_KP_Add + /* 0x6c */ 0xffac, // VK_SEPARATOR XK_KP_Separator + /* 0x6d */ 0xffad, // VK_SUBTRACT XK_KP_Subtract + /* 0x6e */ 0xffae, // VK_DECIMAL XK_KP_Decimal + /* 0x6f */ 0xffaf, // VK_DIVIDE XK_KP_Divide + /* 0x70 */ 0xffbe, // VK_F1 XK_F1 + /* 0x71 */ 0xffbf, // VK_F2 XK_F2 + /* 0x72 */ 0xffc0, // VK_F3 XK_F3 + /* 0x73 */ 0xffc1, // VK_F4 XK_F4 + /* 0x74 */ 0xffc2, // VK_F5 XK_F5 + /* 0x75 */ 0xffc3, // VK_F6 XK_F6 + /* 0x76 */ 0xffc4, // VK_F7 XK_F7 + /* 0x77 */ 0xffc5, // VK_F8 XK_F8 + /* 0x78 */ 0xffc6, // VK_F9 XK_F9 + /* 0x79 */ 0xffc7, // VK_F10 XK_F10 + /* 0x7a */ 0xffc8, // VK_F11 XK_F11 + /* 0x7b */ 0xffc9, // VK_F12 XK_F12 + /* 0x7c */ 0xffca, // VK_F13 XK_F13 + /* 0x7d */ 0xffcb, // VK_F14 XK_F14 + /* 0x7e */ 0xffcc, // VK_F15 XK_F15 + /* 0x7f */ 0xffcd, // VK_F16 XK_F16 + /* 0x80 */ 0xffce, // VK_F17 XK_F17 + /* 0x81 */ 0xffcf, // VK_F18 XK_F18 + /* 0x82 */ 0xffd0, // VK_F19 XK_F19 + /* 0x83 */ 0xffd1, // VK_F20 XK_F20 + /* 0x84 */ 0xffd2, // VK_F21 XK_F21 + /* 0x85 */ 0xffd3, // VK_F22 XK_F22 + /* 0x86 */ 0xffd4, // VK_F23 XK_F23 + /* 0x87 */ 0xffd5, // VK_F24 XK_F24 + /* 0x88 */ kKeyNone, // unassigned + /* 0x89 */ kKeyNone, // unassigned + /* 0x8a */ kKeyNone, // unassigned + /* 0x8b */ kKeyNone, // unassigned + /* 0x8c */ kKeyNone, // unassigned + /* 0x8d */ kKeyNone, // unassigned + /* 0x8e */ kKeyNone, // unassigned + /* 0x8f */ kKeyNone, // unassigned + /* 0x90 */ 0xff7f, // VK_NUMLOCK XK_Num_Lock + /* 0x91 */ 0xff14, // VK_SCROLL XK_Scroll_Lock + /* 0x92 */ kKeyNone, // unassigned + /* 0x93 */ kKeyNone, // unassigned + /* 0x94 */ kKeyNone, // unassigned + /* 0x95 */ kKeyNone, // unassigned + /* 0x96 */ kKeyNone, // unassigned + /* 0x97 */ kKeyNone, // unassigned + /* 0x98 */ kKeyNone, // unassigned + /* 0x99 */ kKeyNone, // unassigned + /* 0x9a */ kKeyNone, // unassigned + /* 0x9b */ kKeyNone, // unassigned + /* 0x9c */ kKeyNone, // unassigned + /* 0x9d */ kKeyNone, // unassigned + /* 0x9e */ kKeyNone, // unassigned + /* 0x9f */ kKeyNone, // unassigned + /* 0xa0 */ kKeyNone, // unassigned + /* 0xa1 */ kKeyNone, // unassigned + /* 0xa2 */ kKeyNone, // unassigned + /* 0xa3 */ kKeyNone, // unassigned + /* 0xa4 */ kKeyNone, // unassigned + /* 0xa5 */ kKeyNone, // unassigned + /* 0xa6 */ kKeyNone, // unassigned + /* 0xa7 */ kKeyNone, // unassigned + /* 0xa8 */ kKeyNone, // unassigned + /* 0xa9 */ kKeyNone, // unassigned + /* 0xaa */ kKeyNone, // unassigned + /* 0xab */ kKeyNone, // unassigned + /* 0xac */ kKeyNone, // unassigned + /* 0xad */ kKeyNone, // unassigned + /* 0xae */ kKeyNone, // unassigned + /* 0xaf */ kKeyNone, // unassigned + /* 0xb0 */ kKeyNone, // unassigned + /* 0xb1 */ kKeyNone, // unassigned + /* 0xb2 */ kKeyNone, // unassigned + /* 0xb3 */ kKeyNone, // unassigned + /* 0xb4 */ kKeyNone, // unassigned + /* 0xb5 */ kKeyNone, // unassigned + /* 0xb6 */ kKeyNone, // unassigned + /* 0xb7 */ kKeyNone, // unassigned + /* 0xb8 */ kKeyNone, // unassigned + /* 0xb9 */ kKeyNone, // unassigned + /* 0xba */ kKeyNone, // OEM specific + /* 0xbb */ kKeyNone, // OEM specific + /* 0xbc */ kKeyNone, // OEM specific + /* 0xbd */ kKeyNone, // OEM specific + /* 0xbe */ kKeyNone, // OEM specific + /* 0xbf */ kKeyNone, // OEM specific + /* 0xc0 */ kKeyNone, // OEM specific + /* 0xc1 */ kKeyNone, // unassigned + /* 0xc2 */ kKeyNone, // unassigned + /* 0xc3 */ kKeyNone, // unassigned + /* 0xc4 */ kKeyNone, // unassigned + /* 0xc5 */ kKeyNone, // unassigned + /* 0xc6 */ kKeyNone, // unassigned + /* 0xc7 */ kKeyNone, // unassigned + /* 0xc8 */ kKeyNone, // unassigned + /* 0xc9 */ kKeyNone, // unassigned + /* 0xca */ kKeyNone, // unassigned + /* 0xcb */ kKeyNone, // unassigned + /* 0xcc */ kKeyNone, // unassigned + /* 0xcd */ kKeyNone, // unassigned + /* 0xce */ kKeyNone, // unassigned + /* 0xcf */ kKeyNone, // unassigned + /* 0xd0 */ kKeyNone, // unassigned + /* 0xd1 */ kKeyNone, // unassigned + /* 0xd2 */ kKeyNone, // unassigned + /* 0xd3 */ kKeyNone, // unassigned + /* 0xd4 */ kKeyNone, // unassigned + /* 0xd5 */ kKeyNone, // unassigned + /* 0xd6 */ kKeyNone, // unassigned + /* 0xd7 */ kKeyNone, // unassigned + /* 0xd8 */ kKeyNone, // unassigned + /* 0xd9 */ kKeyNone, // unassigned + /* 0xda */ kKeyNone, // unassigned + /* 0xdb */ kKeyNone, // OEM specific + /* 0xdc */ kKeyNone, // OEM specific + /* 0xdd */ kKeyNone, // OEM specific + /* 0xde */ kKeyNone, // OEM specific + /* 0xdf */ kKeyNone, // OEM specific + /* 0xe0 */ kKeyNone, // OEM specific + /* 0xe1 */ kKeyNone, // OEM specific + /* 0xe2 */ kKeyNone, // OEM specific + /* 0xe3 */ kKeyNone, // OEM specific + /* 0xe4 */ kKeyNone, // OEM specific + /* 0xe5 */ kKeyNone, // unassigned + /* 0xe6 */ kKeyNone, // OEM specific + /* 0xe7 */ kKeyNone, // unassigned + /* 0xe8 */ kKeyNone, // unassigned + /* 0xe9 */ kKeyNone, // OEM specific + /* 0xea */ kKeyNone, // OEM specific + /* 0xeb */ kKeyNone, // OEM specific + /* 0xec */ kKeyNone, // OEM specific + /* 0xed */ kKeyNone, // OEM specific + /* 0xee */ kKeyNone, // OEM specific + /* 0xef */ kKeyNone, // OEM specific + /* 0xf0 */ kKeyNone, // OEM specific + /* 0xf1 */ kKeyNone, // OEM specific + /* 0xf2 */ kKeyNone, // OEM specific + /* 0xf3 */ kKeyNone, // OEM specific + /* 0xf4 */ kKeyNone, // OEM specific + /* 0xf5 */ kKeyNone, // OEM specific + /* 0xf6 */ kKeyNone, // VK_ATTN + /* 0xf7 */ kKeyNone, // VK_CRSEL + /* 0xf8 */ kKeyNone, // VK_EXSEL + /* 0xf9 */ kKeyNone, // VK_EREOF + /* 0xfa */ kKeyNone, // VK_PLAY + /* 0xfb */ kKeyNone, // VK_ZOOM + /* 0xfc */ kKeyNone, // reserved + /* 0xfd */ kKeyNone, // VK_PA1 + /* 0xfe */ kKeyNone, // VK_OEM_CLEAR + /* 0xff */ kKeyNone // reserved +}; -KeyModifierMask CMSWindowsPrimaryScreen::mapModifier( - WPARAM keycode, LPARAM info) const +KeyID CMSWindowsPrimaryScreen::mapKey( + WPARAM vkCode, LPARAM info, + KeyModifierMask* maskOut) const { - // FIXME -- should be configurable + assert(maskOut != NULL); + + // map modifier key + // FIXME -- should be configurable? KeyModifierMask mask = 0; if (GetKeyState(VK_SHIFT) < 0) mask |= KeyModifierShift; @@ -477,14 +770,48 @@ KeyModifierMask CMSWindowsPrimaryScreen::mapModifier( mask |= KeyModifierMeta; if ((GetKeyState(VK_SCROLL) & 1) != 0) mask |= KeyModifierScrollLock; - return mask; -} + *maskOut = mask; -KeyID CMSWindowsPrimaryScreen::mapKey( - WPARAM keycode, LPARAM info) const -{ - // FIXME -- must convert to X keysyms - return keycode; + KeyID id = g_virtualKey[vkCode]; + if (id != kKeyNone) { + return id; + } + + BYTE state[256]; + GetKeyboardState(state); + WORD ascii; + int result = ToAscii(vkCode, MapVirtualKey(0, vkCode), state, &ascii, 0); + if (result > 0) { + // FIXME -- handle dead keys + return (KeyID)(ascii & 0x00ff); + } + + return kKeyNone; + +/* + UINT character = MapVirtualKey(2, vkCode); + if (character != 0) { + if ((character & ~0xff) != 0) { + // dead key (i.e. a key to compose with the next) + // FIXME + return kKeyNone; + } + else { + // map character + KeyID id = g_virtualKey[character & 0xff]; + + // uppercase to lowercase conversion + if ((mask & KeyModifierShift) == 0 && id >= 'A' && id <= 'Z') + id += 0x20; + + return id; + } + } + else { + // non-ascii key + return g_virtualKey[vkCode]; + } +*/ } ButtonID CMSWindowsPrimaryScreen::mapButton( diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index d34b44bf..fd96d5d5 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -22,13 +22,15 @@ class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { virtual void leave(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(const IClipboard*); + virtual void grabClipboard(); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(IClipboard*) const; protected: // CMSWindowsScreen overrides - virtual bool onEvent(MSG*); + virtual bool onPreTranslate(MSG*); + virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); virtual void onOpenDisplay(); virtual void onCloseDisplay(); @@ -37,17 +39,17 @@ class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { void nextMark(); -// bool keyboardHook(int, WPARAM, LPARAM); -// bool mouseHook(int, WPARAM, LPARAM); - - KeyModifierMask mapModifier(WPARAM keycode, LPARAM info) const; - KeyID mapKey(WPARAM keycode, LPARAM info) const; + KeyID mapKey(WPARAM keycode, LPARAM info, + KeyModifierMask* maskOut) const; ButtonID mapButton(WPARAM button) const; private: CServer* m_server; bool m_active; HWND m_window; + HWND m_nextClipboardWindow; + HWND m_clipboardOwner; + HWND m_lastActive; HINSTANCE m_hookLibrary; UInt32 m_mark; UInt32 m_markReceived; diff --git a/server/CServer.cpp b/server/CServer.cpp index 42df94f8..10158e26 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -43,7 +43,9 @@ else { wait(0); exit(1); } CServer::CServer() : m_primary(NULL), m_active(NULL), - m_primaryInfo(NULL) + m_primaryInfo(NULL), + m_clipboardSeqNum(0), + m_clipboardReady(false) { m_socketFactory = NULL; m_securityFactory = NULL; @@ -78,7 +80,7 @@ void CServer::run() closePrimaryScreen(); } catch (XBase& e) { - log((CLOG_ERR "server error: %s\n", e.what())); + log((CLOG_ERR "server error: %s", e.what())); // clean up cleanupThreads(); @@ -148,6 +150,93 @@ void CServer::setInfo(const CString& client, log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d", client.c_str(), w, h, zoneSize)); } +void CServer::grabClipboard() +{ + grabClipboard(m_primaryInfo->m_name); +} + +void CServer::grabClipboard(const CString& client) +{ + CLock lock(&m_mutex); + + // client must be connected + CScreenList::iterator index = m_screens.find(client); + if (index == m_screens.end()) { + throw XBadClient(); + } + + log((CLOG_NOTE "client \"%s\" grabbed clipboard from \"%s\"", client.c_str(), m_clipboardOwner.c_str())); + + // save the clipboard owner + m_clipboardOwner = client; + + // mark client as having the clipboard data + index->second->m_gotClipboard = true; + + // tell all other clients to take ownership of clipboard and mark + // them as not having the data yet. + for (index = m_screens.begin(); index != m_screens.end(); ++index) { + if (index->first != client) { + CScreenInfo* info = index->second; + info->m_gotClipboard = false; + if (info->m_protocol == NULL) { + m_primary->grabClipboard(); + } + else { + info->m_protocol->sendGrabClipboard(); + } + } + } + + // increment the clipboard sequence number so we can identify the + // clipboard query's response. + ++m_clipboardSeqNum; + + // begin getting the clipboard data + if (m_active->m_protocol == NULL) { + // get clipboard immediately from primary screen + m_primary->getClipboard(&m_clipboard); + m_clipboardData = m_clipboard.marshall(); + m_clipboardReady = true; + } + else { + // clear out the clipboard since existing data is now out of date. + if (m_clipboard.open()) { + m_clipboard.close(); + } + m_clipboardReady = false; + + // send request but don't wait for reply + m_active->m_protocol->sendQueryClipboard(m_clipboardSeqNum); + } +} + +void CServer::setClipboard( + UInt32 seqNum, const CString& data) +{ + // update the clipboard if the sequence number matches + CLock lock(&m_mutex); + if (seqNum == m_clipboardSeqNum) { + // unmarshall into our clipboard buffer + m_clipboardData = data; + m_clipboard.unmarshall(m_clipboardData); + m_clipboardReady = true; + + // if the active client doesn't have the clipboard data + // (and it won't unless the client is the one sending us + // the data) then send the data now. + if (!m_active->m_gotClipboard) { + if (m_active->m_protocol == NULL) { + m_primary->setClipboard(&m_clipboard); + } + else { + m_active->m_protocol->sendClipboard(m_clipboardData); + } + m_active->m_gotClipboard = true; + } + } +} + bool CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) { @@ -186,9 +275,10 @@ void CServer::onKeyUp(KeyID id, KeyModifierMask mask) } } -void CServer::onKeyRepeat(KeyID id, KeyModifierMask mask) +void CServer::onKeyRepeat( + KeyID id, KeyModifierMask mask, SInt32 count) { - log((CLOG_DEBUG "onKeyRepeat id=%d mask=0x%04x", id, mask)); + log((CLOG_DEBUG "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); assert(m_active != NULL); // handle command keys @@ -199,7 +289,7 @@ void CServer::onKeyRepeat(KeyID id, KeyModifierMask mask) // relay if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendKeyRepeat(id, mask); + m_active->m_protocol->sendKeyRepeat(id, mask, count); } } @@ -385,11 +475,6 @@ bool CServer::isLockedToScreen() const return false; } -#if defined(CONFIG_PLATFORM_WIN32) -#include "CMSWindowsClipboard.h" // FIXME -#elif defined(CONFIG_PLATFORM_UNIX) -#include "CXWindowsClipboard.h" // FIXME -#endif void CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y) { @@ -398,6 +483,7 @@ void CServer::switchScreen(CScreenInfo* dst, assert(m_active != NULL); log((CLOG_NOTE "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y)); + // FIXME -- we're not locked here but we probably should be // wrapping means leaving the active screen and entering it again. // since that's a waste of time we skip that and just warp the @@ -406,14 +492,6 @@ void CServer::switchScreen(CScreenInfo* dst, // leave active screen if (m_active->m_protocol == NULL) { m_primary->leave(); - - // FIXME -- testing -#if defined(CONFIG_PLATFORM_WIN32) - CMSWindowsClipboard clipboard; -#elif defined(CONFIG_PLATFORM_UNIX) - CXWindowsClipboard clipboard; -#endif - m_primary->getClipboard(&clipboard); } else { m_active->m_protocol->sendLeave(); @@ -429,6 +507,17 @@ void CServer::switchScreen(CScreenInfo* dst, else { m_active->m_protocol->sendEnter(x, y); } + + // send the clipboard data if we haven't done so yet + if (m_clipboardReady && !m_active->m_gotClipboard) { + if (m_active->m_protocol == NULL) { + m_primary->setClipboard(&m_clipboard); + } + else { + m_active->m_protocol->sendClipboard(m_clipboardData); + } + m_active->m_gotClipboard = true; + } } else { if (m_active->m_protocol == NULL) { @@ -827,6 +916,13 @@ void CServer::openPrimaryScreen() m_active->m_zoneSize = m_primary->getJumpZoneSize(); log((CLOG_NOTE "server size=%dx%d zone=%d", m_active->m_width, m_active->m_height, m_active->m_zoneSize)); // FIXME -- need way for primary screen to call us back + + // set the clipboard owner to the primary screen and then get the + // current clipboard data. + m_primary->getClipboard(&m_clipboard); + m_clipboardData = m_clipboard.marshall(); + m_clipboardReady = true; + m_clipboardOwner = m_active->m_name; } void CServer::closePrimaryScreen() @@ -979,7 +1075,8 @@ CServer::CScreenInfo::CScreenInfo(const CString& name, m_name(name), m_protocol(protocol), m_width(0), m_height(0), - m_zoneSize(0) + m_zoneSize(0), + m_gotClipboard(false) { // do nothing } diff --git a/server/CServer.h b/server/CServer.h index 18391aad..3ca443d2 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -4,6 +4,7 @@ #include "KeyTypes.h" #include "MouseTypes.h" #include "CScreenMap.h" +#include "CClipboard.h" #include "CMutex.h" #include "CString.h" #include "XBase.h" @@ -36,16 +37,19 @@ class CServer { // true iff the mouse enters a jump zone and jumps. void onKeyDown(KeyID, KeyModifierMask); void onKeyUp(KeyID, KeyModifierMask); - void onKeyRepeat(KeyID, KeyModifierMask); + void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); void onMouseDown(ButtonID); void onMouseUp(ButtonID); bool onMouseMovePrimary(SInt32 x, SInt32 y); void onMouseMoveSecondary(SInt32 dx, SInt32 dy); void onMouseWheel(SInt32 delta); + void grabClipboard(); // handle messages from clients void setInfo(const CString& clientName, SInt32 w, SInt32 h, SInt32 zoneSize); + void grabClipboard(const CString& clientName); + void setClipboard(UInt32 seqNum, const CString& data); // accessors @@ -91,6 +95,7 @@ class CServer { IServerProtocol* m_protocol; SInt32 m_width, m_height; SInt32 m_zoneSize; + bool m_gotClipboard; }; // change the active screen @@ -159,6 +164,12 @@ class CServer { SInt32 m_x, m_y; CScreenMap m_screenMap; + + CClipboard m_clipboard; + CString m_clipboardData; + CString m_clipboardOwner; + UInt32 m_clipboardSeqNum; + bool m_clipboardReady; }; #endif diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index 5fb552a7..d5a69a4d 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -33,11 +33,12 @@ class CServerProtocol : public IServerProtocol { virtual void sendClose() = 0; virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; virtual void sendLeave() = 0; + virtual void sendClipboard(const CString&) = 0; virtual void sendGrabClipboard() = 0; - virtual void sendQueryClipboard() = 0; + virtual void sendQueryClipboard(UInt32 seqNum) = 0; virtual void sendScreenSaver(bool on) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; - virtual void sendKeyRepeat(KeyID, KeyModifierMask) = 0; + virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; virtual void sendMouseDown(ButtonID) = 0; virtual void sendMouseUp(ButtonID) = 0; @@ -47,6 +48,8 @@ class CServerProtocol : public IServerProtocol { protected: //IServerProtocol overrides virtual void recvInfo() = 0; + virtual void recvClipboard() = 0; + virtual void recvGrabClipboard() = 0; private: CServer* m_server; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index de28aa81..a2e02811 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -1,5 +1,6 @@ #include "CServerProtocol1_0.h" #include "CServer.h" +#include "CClipboard.h" #include "CProtocolUtil.h" #include "ProtocolTypes.h" #include "IInputStream.h" @@ -49,6 +50,12 @@ void CServerProtocol1_0::run() if (memcmp(code, kMsgDInfo, 4) == 0) { recvInfo(); } + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + recvGrabClipboard(); + } + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + recvClipboard(); + } // FIXME -- more message here else { log((CLOG_ERR "unknown message from client \"%s\"", getClient().c_str())); @@ -96,16 +103,22 @@ void CServerProtocol1_0::sendLeave() CProtocolUtil::writef(getOutputStream(), kMsgCLeave); } +void CServerProtocol1_0::sendClipboard(const CString& data) +{ + log((CLOG_INFO "send clipboard to \"%s\" size=%d", getClient().c_str(), data.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, 0, &data); +} + void CServerProtocol1_0::sendGrabClipboard() { log((CLOG_INFO "send grab clipboard to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard); } -void CServerProtocol1_0::sendQueryClipboard() +void CServerProtocol1_0::sendQueryClipboard(UInt32 seqNum) { log((CLOG_INFO "query clipboard to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgQClipboard); + CProtocolUtil::writef(getOutputStream(), kMsgQClipboard, seqNum); } void CServerProtocol1_0::sendScreenSaver(bool on) @@ -122,10 +135,10 @@ void CServerProtocol1_0::sendKeyDown( } void CServerProtocol1_0::sendKeyRepeat( - KeyID key, KeyModifierMask mask) + KeyID key, KeyModifierMask mask, SInt32 count) { log((CLOG_INFO "send key repeat to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); } void CServerProtocol1_0::sendKeyUp( @@ -179,3 +192,17 @@ void CServerProtocol1_0::recvInfo() getServer()->setInfo(getClient(), w, h, zoneInfo); } +void CServerProtocol1_0::recvClipboard() +{ + UInt32 seqNum; + CString data; + CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &seqNum, &data); + log((CLOG_INFO "received client \"%s\" clipboard seqnum=%d, size=%d", getClient().c_str(), seqNum, data.size())); + getServer()->setClipboard(seqNum, data); +} + +void CServerProtocol1_0::recvGrabClipboard() +{ + log((CLOG_INFO "received client \"%s\" grabbed clipboard", getClient().c_str())); + getServer()->grabClipboard(getClient()); +} diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index 00cc58cb..97745298 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -18,11 +18,12 @@ class CServerProtocol1_0 : public CServerProtocol { virtual void sendClose(); virtual void sendEnter(SInt32 xAbs, SInt32 yAbs); virtual void sendLeave(); + virtual void sendClipboard(const CString&); virtual void sendGrabClipboard(); - virtual void sendQueryClipboard(); + virtual void sendQueryClipboard(UInt32 seqNum); virtual void sendScreenSaver(bool on); virtual void sendKeyDown(KeyID, KeyModifierMask); - virtual void sendKeyRepeat(KeyID, KeyModifierMask); + virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count); virtual void sendKeyUp(KeyID, KeyModifierMask); virtual void sendMouseDown(ButtonID); virtual void sendMouseUp(ButtonID); @@ -32,6 +33,8 @@ class CServerProtocol1_0 : public CServerProtocol { protected: // IServerProtocol overrides virtual void recvInfo(); + virtual void recvClipboard(); + virtual void recvGrabClipboard(); }; #endif diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 2cdf876d..92eb5803 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -56,7 +56,10 @@ static void restoreCursor() static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { - // FIXME + if (g_relay) { + PostMessage(g_hwnd, SYNERGY_MSG_KEY, wParam, lParam); + return 1; + } } return CallNextHookEx(g_keyboard, code, wParam, lParam); diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index b3b30c24..5cdf9c12 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -14,10 +14,10 @@ #define CSYNERGYHOOK_API #endif -#define SYNERGY_MSG_MARK WM_APP + 0x0001 // mark id; -#define SYNERGY_MSG_KEY WM_APP + 0x0002 // vk code; key data -#define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0003 // button msg; -#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0004 // x; y +#define SYNERGY_MSG_MARK WM_APP + 0x0011 // mark id; +#define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data +#define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; +#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y typedef int (*InstallFunc)(HWND); typedef int (*UninstallFunc)(void); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index a36b07ec..db5cac08 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -117,19 +117,46 @@ void CXWindowsPrimaryScreen::run() break; } -/* case SelectionClear: - target->XXX(xevent.xselectionclear.); + // we just lost the selection. that means someone else + // grabbed the selection so this screen is now the + // selection owner. report that to the server. + m_server->grabClipboard(); break; case SelectionNotify: - target->XXX(xevent.xselection.); + // notification of selection transferred. we shouldn't + // get this here because we handle them in the selection + // retrieval methods. we'll just delete the property + // with the data (satisfying the usual ICCCM protocol). + if (xevent.xselection.property != None) { + CDisplayLock display(this); + XDeleteProperty(display, m_window, xevent.xselection.property); + } break; case SelectionRequest: - target->XXX(xevent.xselectionrequest.); + // somebody is asking for clipboard data + if (xevent.xselectionrequest.owner == m_window) { + addClipboardRequest(m_window, + xevent.xselectionrequest.requestor, + xevent.xselectionrequest.selection, + xevent.xselectionrequest.target, + xevent.xselectionrequest.property, + xevent.xselectionrequest.time); + } + break; + + case PropertyNotify: + // clipboard transfers involve property changes so forward + // the event to the superclass. we only care about the + // deletion of properties. + if (xevent.xproperty.state == PropertyDelete) { + processClipboardRequest(xevent.xproperty.window, + xevent.xproperty.atom, + xevent.xproperty.time); + } break; -*/ } } } @@ -267,19 +294,17 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( } } -#include // FIXME void CXWindowsPrimaryScreen::setClipboard( - const IClipboard* /*clipboard*/) + const IClipboard* clipboard) { - // FIXME -- put this in superclass? // FIXME -- don't use CurrentTime - CDisplayLock display(this); - XSetSelectionOwner(display, XA_PRIMARY, m_window, CurrentTime); - if (XGetSelectionOwner(display, XA_PRIMARY) == m_window) { - // we got the selection - log((CLOG_DEBUG "grabbed clipboard")); - } - // FIXME -- need to copy or adopt the clipboard to serve future requests + setDisplayClipboard(clipboard, m_window, CurrentTime); +} + +void CXWindowsPrimaryScreen::grabClipboard() +{ + // FIXME -- don't use CurrentTime + setDisplayClipboard(NULL, m_window, CurrentTime); } void CXWindowsPrimaryScreen::getSize( @@ -296,7 +321,6 @@ SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const void CXWindowsPrimaryScreen::getClipboard( IClipboard* clipboard) const { - // FIXME -- put this in superclass? // FIXME -- don't use CurrentTime getDisplayClipboard(clipboard, m_window, CurrentTime); } diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 66b84fb5..74f7456d 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -20,6 +20,7 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { virtual void leave(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(const IClipboard*); + virtual void grabClipboard(); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(IClipboard*) const; diff --git a/server/makehook.dsp b/server/makehook.dsp index 507157ef..7a9aac98 100644 --- a/server/makehook.dsp +++ b/server/makehook.dsp @@ -23,8 +23,8 @@ CFG=makehook - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." MTL=midl.exe !IF "$(CFG)" == "makehook - Win32 Release" diff --git a/server/server.cpp b/server/server.cpp index 809c53d6..b01f75c3 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -10,9 +10,9 @@ void realMain() CScreenMap screenMap; screenMap.addScreen("primary"); - screenMap.addScreen("ingrid"); - screenMap.connect("primary", CScreenMap::kRight, "ingrid"); - screenMap.connect("ingrid", CScreenMap::kLeft, "primary"); + screenMap.addScreen("secondary"); + screenMap.connect("primary", CScreenMap::kRight, "secondary"); + screenMap.connect("secondary", CScreenMap::kLeft, "primary"); CServer* server = NULL; try { diff --git a/server/server.dsp b/server/server.dsp index b65f6dec..cdfb0802 100644 --- a/server/server.dsp +++ b/server/server.dsp @@ -38,7 +38,7 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" +# PROP Output_Dir "../Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c @@ -64,7 +64,7 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" +# PROP Output_Dir "../Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c diff --git a/server/synrgyhk.dsp b/server/synrgyhk.dsp index 979b6172..d3c6c9b5 100644 --- a/server/synrgyhk.dsp +++ b/server/synrgyhk.dsp @@ -38,8 +38,8 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "../Release" +# PROP Intermediate_Dir "ReleaseHook" # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c # ADD CPP /nologo /MT /W3 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c @@ -64,8 +64,8 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "../Debug" +# PROP Intermediate_Dir "DebugHook" # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c diff --git a/synergy.dsw b/synergy.dsw index e441f5fc..4e67cce8 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -92,6 +92,10 @@ Project: "makehook"=.\server\makehook.dsp - Package Owner=<4> Package=<5> {{{ + begin source code control + millpond + .\server + end source code control }}} Package=<4> diff --git a/synergy/CClipboard.cpp b/synergy/CClipboard.cpp new file mode 100644 index 00000000..1e572af1 --- /dev/null +++ b/synergy/CClipboard.cpp @@ -0,0 +1,137 @@ +#include "CClipboard.h" + +// +// CClipboard +// + +CClipboard::CClipboard() +{ + // do nothing +} + +CClipboard::~CClipboard() +{ + // do nothing +} + +bool CClipboard::open() +{ + // clear all data + for (SInt32 index = 0; index < kNumFormats; ++index) { + m_data[index] = ""; + m_added[index] = false; + } + return true; +} + +void CClipboard::close() +{ + // do nothing +} + +void CClipboard::add(EFormat format, const CString& data) +{ + m_data[format] = data; + m_added[format] = true; +} + +bool CClipboard::has(EFormat format) const +{ + return m_added[format]; +} + +CString CClipboard::get(EFormat format) const +{ + return m_data[format]; +} + +void CClipboard::copy(IClipboard* dst, const IClipboard* src) +{ + if (dst->open()) { + for (SInt32 format = 0; format != IClipboard::kNumFormats; ++format) { + IClipboard::EFormat eFormat = (IClipboard::EFormat)format; + if (src->has(eFormat)) { + dst->add(eFormat, src->get(eFormat)); + } + } + dst->close(); + } +} + +void CClipboard::unmarshall(const CString& data) +{ + const char* index = data.data(); + + // clear existing data + open(); + + // read the number of formats + const UInt32 numFormats = readUInt32(index); + index += 4; + + // read each format + for (UInt32 format = 0; format < numFormats; ++format) { + // get the format id + UInt32 format = readUInt32(index); + index += 4; + + // get the size of the format data + UInt32 size = readUInt32(index); + index += 4; + + // save the data + m_added[format] = true; + m_data[format] = CString(index, size); + index += size; + } + + // done + close(); +} + +CString CClipboard::marshall() const +{ + CString data; + + // compute size of marshalled data + UInt32 size = 4; + UInt32 numFormats = 0; + UInt32 format; + for (format = 0; format != IClipboard::kNumFormats; ++format) { + if (m_added[format]) { + ++numFormats; + size += 4 + 4 + m_data[format].size(); + } + } + + // allocate space + data.reserve(size); + + // marshall the data + writeUInt32(&data, numFormats); + for (format = 0; format != IClipboard::kNumFormats; ++format) { + if (m_added[format]) { + writeUInt32(&data, format); + writeUInt32(&data, m_data[format].size()); + data += m_data[format]; + } + } + + return data; +} + +UInt32 CClipboard::readUInt32(const char* buf) const +{ + return (static_cast(buf[0]) << 24) | + (static_cast(buf[1]) << 16) | + (static_cast(buf[2]) << 8) | + static_cast(buf[3]); +} + +void CClipboard::writeUInt32(CString* buf, UInt32 v) const +{ + *buf += static_cast((v >> 24) & 0xff); + *buf += static_cast((v >> 16) & 0xff); + *buf += static_cast((v >> 8) & 0xff); + *buf += static_cast( v & 0xff); +} diff --git a/synergy/CClipboard.h b/synergy/CClipboard.h new file mode 100644 index 00000000..e5a63ccc --- /dev/null +++ b/synergy/CClipboard.h @@ -0,0 +1,49 @@ +#ifndef CCLIPBOARD_H +#define CCLIPBOARD_H + +// +// CClipboard -- stores clipboard data in a memory buffer +// + +#include "IClipboard.h" +#include "CString.h" + +class CClipboard : public IClipboard { + public: + CClipboard(); + virtual ~CClipboard(); + + // manipulators + + // unmarshall clipboard data + void unmarshall(const CString& data); + + // accessors + + // marshall clipboard data + CString marshall() const; + + // IClipboard overrides + virtual bool open(); + virtual void close(); + virtual void add(EFormat, const CString& data); + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; + + // accessors + + // transfer all the data in one clipboard to another. the + // clipboards can be of any concrete clipboard type (and + // they don't have to be the same type). + static void copy(IClipboard* dst, const IClipboard* src); + + private: + UInt32 readUInt32(const char*) const; + void writeUInt32(CString*, UInt32) const; + + private: + bool m_added[kNumFormats]; + CString m_data[kNumFormats]; +}; + +#endif diff --git a/synergy/CMSWindowsClipboard.cpp b/synergy/CMSWindowsClipboard.cpp index bdef4024..ecf8ae65 100644 --- a/synergy/CMSWindowsClipboard.cpp +++ b/synergy/CMSWindowsClipboard.cpp @@ -6,36 +6,190 @@ // CMSWindowsClipboard // -CMSWindowsClipboard::CMSWindowsClipboard() +CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : m_window(window) { + // do nothing } CMSWindowsClipboard::~CMSWindowsClipboard() { + // do nothing } -void CMSWindowsClipboard::open() +bool CMSWindowsClipboard::open() { log((CLOG_INFO "open clipboard")); + + if (!OpenClipboard(m_window)) { + log((CLOG_WARN "failed to open clipboard")); + return false; + } + if (EmptyClipboard()) { + log((CLOG_DEBUG "grabbed clipboard")); + } + else { + log((CLOG_WARN "failed to grab clipboard")); + } + return true; } void CMSWindowsClipboard::close() { log((CLOG_INFO "close clipboard")); + CloseClipboard(); } void CMSWindowsClipboard::add( EFormat format, const CString& data) { - log((CLOG_INFO "add clipboard format: %d\n%s", format, data.c_str())); + log((CLOG_INFO "add %d bytes to clipboard format: %d", data.size(), format)); + + if (!OpenClipboard(m_window)) { + log((CLOG_WARN "failed to open clipboard")); + return; + } + + // convert data to win32 required form + const UINT win32Format = convertFormatToWin32(format); + HANDLE win32Data; + switch (win32Format) { + case CF_TEXT: + win32Data = convertTextToWin32(data); + break; + + default: + win32Data = NULL; + break; + } + + // put the data on the clipboard + if (win32Data != NULL) { + SetClipboardData(win32Format, win32Data); + } + + // done with clipboard + CloseClipboard(); } -bool CMSWindowsClipboard::has(EFormat /*format*/) const +bool CMSWindowsClipboard::has(EFormat format) const { - return false; + const UINT win32Format = convertFormatToWin32(format); + return (win32Format != 0 && IsClipboardFormatAvailable(win32Format) != 0); } -CString CMSWindowsClipboard::get(EFormat /*format*/) const +CString CMSWindowsClipboard::get(EFormat format) const { - return CString(); + // get the win32 format. return empty data if unknown format. + const UINT win32Format = convertFormatToWin32(format); + if (win32Format == 0) + return CString(); + + if (!OpenClipboard(m_window)) { + log((CLOG_WARN "failed to open clipboard")); + return CString(); + } + + // get a handle to the clipboard data and convert it + HANDLE win32Data = GetClipboardData(win32Format); + CString data; + if (win32Data != NULL) { + // convert the data + switch (win32Format) { + case CF_TEXT: + data = convertTextFromWin32(win32Data); + } + } + + // close the clipboard + CloseClipboard(); + + return data; +} + +UINT CMSWindowsClipboard::convertFormatToWin32( + EFormat format) const +{ + switch (format) { + case kText: + return CF_TEXT; + + default: + return 0; + } +} + +HANDLE CMSWindowsClipboard::convertTextToWin32( + const CString& data) const +{ + // compute size of converted text + UInt32 dstSize = 0; + const UInt32 srcSize = data.size(); + const char* src = data.c_str(); + for (UInt32 index = 0; index < srcSize; ++index) { + if (src[index] == '\n') { + // add \r + ++dstSize; + } + ++dstSize; + } + + // allocate + HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dstSize); + if (gData != NULL) { + // get a pointer to the allocated memory + char* dst = (char*)GlobalLock(gData); + if (dst != NULL) { + // convert text. we change LF to CRLF. + dstSize = 0; + for (UInt32 index = 0; index < srcSize; ++index) { + if (src[index] == '\n') { + // add \r + dst[dstSize++] = '\r'; + } + dst[dstSize++] = src[index]; + } + + // done converting + GlobalUnlock(gData); + } + } + return gData; +} + +CString CMSWindowsClipboard::convertTextFromWin32( + HANDLE handle) const +{ + // get source data and it's size + const char* src = (const char*)GlobalLock(handle); + UInt32 srcSize = (SInt32)GlobalSize(handle); + if (src == NULL || srcSize == 0) + return CString(); + + // compute size of converted text + UInt32 dstSize = 0; + UInt32 index; + for (index = 0; index < srcSize; ++index) { + if (src[index] == '\r') { + // skip \r + if (index + 1 < srcSize && src[index + 1] == '\n') + ++index; + } + ++dstSize; + } + + // allocate + CString data; + data.reserve(dstSize); + + // convert text. we change CRLF to LF. + for (index = 0; index < srcSize; ++index) { + if (src[index] == '\r') { + // skip \r + if (index + 1 < srcSize && src[index + 1] == '\n') + ++index; + } + data += src[index]; + } + + return data; } diff --git a/synergy/CMSWindowsClipboard.h b/synergy/CMSWindowsClipboard.h index c13b11d3..afc8f52c 100644 --- a/synergy/CMSWindowsClipboard.h +++ b/synergy/CMSWindowsClipboard.h @@ -2,18 +2,27 @@ #define CMSWINDOWSCLIPBOARD_H #include "IClipboard.h" +#include class CMSWindowsClipboard : public IClipboard { public: - CMSWindowsClipboard(); + CMSWindowsClipboard(HWND window); virtual ~CMSWindowsClipboard(); // IClipboard overrides - virtual void open(); + virtual bool open(); virtual void close(); virtual void add(EFormat, const CString& data); virtual bool has(EFormat) const; virtual CString get(EFormat) const; + + private: + UINT convertFormatToWin32(EFormat) const; + HANDLE convertTextToWin32(const CString& data) const; + CString convertTextFromWin32(HANDLE) const; + + private: + HWND m_window; }; #endif diff --git a/synergy/CMSWindowsScreen.cpp b/synergy/CMSWindowsScreen.cpp index 63640388..e0fbbdd6 100644 --- a/synergy/CMSWindowsScreen.cpp +++ b/synergy/CMSWindowsScreen.cpp @@ -12,6 +12,7 @@ // HINSTANCE CMSWindowsScreen::s_instance = NULL; +CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; CMSWindowsScreen::CMSWindowsScreen() : m_class(0), @@ -19,12 +20,14 @@ CMSWindowsScreen::CMSWindowsScreen() : m_w(0), m_h(0), m_thread(0) { - // do nothing + assert(s_screen == NULL); + s_screen = this; } CMSWindowsScreen::~CMSWindowsScreen() { assert(m_class == 0); + s_screen = NULL; } void CMSWindowsScreen::init(HINSTANCE instance) @@ -46,7 +49,7 @@ void CMSWindowsScreen::doRun() } // dispatch message - if (!onEvent(&msg)) { + if (!onPreTranslate(&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } @@ -66,10 +69,13 @@ void CMSWindowsScreen::openDisplay() // create a transparent cursor int cw = GetSystemMetrics(SM_CXCURSOR); int ch = GetSystemMetrics(SM_CYCURSOR); - UInt8* cursorBits = new UInt8[ch * ((cw + 31) >> 5)]; - memset(cursorBits, 0, ch * ((cw + 31) >> 5)); - m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorBits, cursorBits); - delete[] cursorBits; + UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; + UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; + memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); + memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); + m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); + delete[] cursorXOR; + delete[] cursorAND; // register a window class WNDCLASSEX classInfo; @@ -197,7 +203,7 @@ void CMSWindowsScreen::getDisplayClipboard( // if we can use the format and we haven't already retrieved // it then get it - if (expectedFormat == IClipboard::kNum) { + if (expectedFormat == IClipboard::kNumFormats) { log((CLOG_DEBUG " no format for target", format)); continue; } @@ -215,7 +221,7 @@ void CMSWindowsScreen::getDisplayClipboard( // use the actual format, not the expected IClipboard::EFormat actualFormat = getFormat(format); - if (actualFormat == IClipboard::kNum) { + if (actualFormat == IClipboard::kNumFormats) { log((CLOG_DEBUG " no format for target", format)); continue; } @@ -244,5 +250,6 @@ LRESULT CALLBACK CMSWindowsScreen::wndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - return DefWindowProc(hwnd, msg, wParam, lParam); + assert(s_screen != NULL); + return s_screen->onEvent(hwnd, msg, wParam, lParam); } diff --git a/synergy/CMSWindowsScreen.h b/synergy/CMSWindowsScreen.h index 4c51be00..2cfa9964 100644 --- a/synergy/CMSWindowsScreen.h +++ b/synergy/CMSWindowsScreen.h @@ -49,8 +49,12 @@ class CMSWindowsScreen { // copy the clipboard contents to clipboard void getDisplayClipboard(IClipboard* clipboard, HWND) const; - // called by doRun() to handle an event - virtual bool onEvent(MSG*) = 0; + // called by doRun() to handle an event. return true to skip + // event translation and dispatch. + virtual bool onPreTranslate(MSG*) = 0; + + // called by window proc. subclass must call DefWindowProc() if necessary + virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM) = 0; // called by openDisplay() to allow subclasses to prepare the display virtual void onOpenDisplay() = 0; @@ -68,6 +72,7 @@ class CMSWindowsScreen { HCURSOR m_cursor; SInt32 m_w, m_h; DWORD m_thread; + static CMSWindowsScreen* s_screen; }; #endif diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index e22d24c4..c15cfb2a 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -190,6 +190,12 @@ UInt32 CProtocolUtil::getLength( break; case 's': + assert(len == 0); + len = (va_arg(args, CString*))->size() + 4; + (void)va_arg(args, UInt8*); + break; + + case 'S': assert(len == 0); len = va_arg(args, UInt32) + 4; (void)va_arg(args, UInt8*); @@ -258,6 +264,21 @@ void CProtocolUtil::writef(void* buffer, } case 's': { + assert(len == 0); + const CString* src = va_arg(args, CString*); + const UInt32 len = (src != NULL) ? src->size() : 0; + *dst++ = static_cast((len >> 24) & 0xff); + *dst++ = static_cast((len >> 16) & 0xff); + *dst++ = static_cast((len >> 8) & 0xff); + *dst++ = static_cast( len & 0xff); + if (len != 0) { + memcpy(dst, src->data(), len); + dst += len; + } + break; + } + + case 'S': { assert(len == 0); const UInt32 len = va_arg(args, UInt32); const UInt8* src = va_arg(args, UInt8*); diff --git a/synergy/CProtocolUtil.h b/synergy/CProtocolUtil.h index 70bd5851..d85d26f1 100644 --- a/synergy/CProtocolUtil.h +++ b/synergy/CProtocolUtil.h @@ -20,7 +20,8 @@ class CProtocolUtil { // %1i -- converts integer argument to 1 byte integer // %2i -- converts integer argument to 2 byte integer in NBO // %4i -- converts integer argument to 4 byte integer in NBO - // %s -- converts integer N and const UInt8* to stream of N bytes + // %s -- converts CString* to stream of bytes + // %S -- converts integer N and const UInt8* to stream of N bytes static void writef(IOutputStream*, const char* fmt, ...); diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index dafbb648..a1a6aa74 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -52,6 +52,7 @@ void CXWindowsScreen::openDisplay() // get some atoms m_atomTargets = XInternAtom(m_display, "TARGETS", False); + m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False); m_atomData = XInternAtom(m_display, "DESTINATION", False); m_atomINCR = XInternAtom(m_display, "INCR", False); m_atomText = XInternAtom(m_display, "TEXT", False); @@ -143,12 +144,12 @@ bool CXWindowsScreen::getEvent(XEvent* xevent) const } if (m_stop) { m_mutex.unlock(); - return true; + return false; } else { XNextEvent(m_display, xevent); m_mutex.unlock(); - return false; + return true; } } @@ -158,6 +159,34 @@ void CXWindowsScreen::doStop() m_stop = true; } +bool CXWindowsScreen::setDisplayClipboard( + const IClipboard* clipboard, + Window requestor, Time timestamp) +{ + CLock lock(&m_mutex); + + XSetSelectionOwner(m_display, XA_PRIMARY, requestor, timestamp); + if (XGetSelectionOwner(m_display, XA_PRIMARY) == requestor) { + // we got the selection + log((CLOG_DEBUG "grabbed clipboard")); + + if (clipboard != NULL) { + // save clipboard to serve requests + CClipboard::copy(&m_clipboard, clipboard); + } + else { + // clear clipboard + if (m_clipboard.open()) { + m_clipboard.close(); + } + } + + return true; + } + + return false; +} + void CXWindowsScreen::getDisplayClipboard( IClipboard* clipboard, Window requestor, Time timestamp) const @@ -166,7 +195,8 @@ void CXWindowsScreen::getDisplayClipboard( assert(requestor != None); // clear the clipboard object - clipboard->open(); + if (!clipboard->open()) + return; // block others from using the display while we get the clipboard. // in particular, this prevents the event thread from stealing the @@ -209,7 +239,7 @@ void CXWindowsScreen::getDisplayClipboard( // if we can use the format and we haven't already retrieved // it then get it - if (expectedFormat == IClipboard::kNum) { + if (expectedFormat == IClipboard::kNumFormats) { log((CLOG_DEBUG " no format for target", format)); continue; } @@ -227,7 +257,7 @@ void CXWindowsScreen::getDisplayClipboard( // use the actual format, not the expected IClipboard::EFormat actualFormat = getFormat(format); - if (actualFormat == IClipboard::kNum) { + if (actualFormat == IClipboard::kNumFormats) { log((CLOG_DEBUG " no format for target", format)); continue; } @@ -452,7 +482,7 @@ IClipboard::EFormat CXWindowsScreen::getFormat(Atom src) const src == m_atomText || src == m_atomCompoundText) return IClipboard::kText; - return IClipboard::kNum; + return IClipboard::kNumFormats; } Bool CXWindowsScreen::findSelectionNotify( @@ -473,6 +503,98 @@ Bool CXWindowsScreen::findPropertyNotify( xevent->xproperty.state == PropertyNewValue) ? True : False; } +void CXWindowsScreen::addClipboardRequest( + Window /*owner*/, Window requestor, + Atom selection, Atom target, + Atom property, Time time) +{ + // mutex the display + CLock lock(&m_mutex); + + // check the target + IClipboard::EFormat format = IClipboard::kNumFormats; + if (selection == XA_PRIMARY) { + if (target == m_atomTargets) { + // send the list of available targets + sendClipboardTargetsNotify(requestor, property, time); + return; + } + else if (target == m_atomMultiple) { + // add a multiple request + if (property != None) { + addClipboardMultipleRequest(requestor, property, time); + return; + } + } + else { + format = getFormat(target); + } + } + + // if we can't handle the format then send a failure notification + if (format == IClipboard::kNumFormats) { + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = m_display; + event.xselection.requestor = requestor; + event.xselection.selection = selection; + event.xselection.target = target; + event.xselection.property = None; + event.xselection.time = time; + XSendEvent(m_display, requestor, False, 0, &event); + return; + } + + // we could process short requests without adding to the request + // queue but we'll keep things simple by doing all requests the + // same way. + // FIXME +} + +void CXWindowsScreen::sendClipboardTargetsNotify( + Window requestor, + Atom property, Time time) +{ + // construct response + // FIXME + + // set property + // FIXME + + // send notification + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = m_display; + event.xselection.requestor = requestor; + event.xselection.selection = XA_PRIMARY; + event.xselection.target = m_atomTargets; + event.xselection.property = property; + event.xselection.time = time; + XSendEvent(m_display, requestor, False, 0, &event); +} + +void CXWindowsScreen::processClipboardRequest( + Window requestor, + Atom property, Time time) +{ + // FIXME +} + +void CXWindowsScreen::addClipboardRequest( + Window requestor, + Atom property, Time time, + IClipboard::EFormat format) +{ + // FIXME +} + +void CXWindowsScreen::addClipboardMultipleRequest( + Window requestor, + Atom property, Time time) +{ + // FIXME +} + // // CXWindowsScreen::CDisplayLock diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 98088925..d7e983b9 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -1,8 +1,8 @@ #ifndef CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H +#include "CClipboard.h" #include "CMutex.h" -#include "IClipboard.h" #include "BasicTypes.h" #include @@ -54,12 +54,25 @@ class CXWindowsScreen { // cause getEvent() to return false immediately and forever after void doStop(); + bool setDisplayClipboard(const IClipboard* clipboard, + Window requestor, Time timestamp); + // copy the clipboard contents to clipboard. requestor must be a // valid window; it will be used to receive the transfer. timestamp // should be the timestamp of the provoking event and not CurrentTime. void getDisplayClipboard(IClipboard* clipboard, Window requestor, Time timestamp) const; + // add a selection request to the request list + void addClipboardRequest( + Window owner, Window requestor, + Atom selection, Atom target, + Atom property, Time time); + + // continue processing a selection request + void processClipboardRequest(Window window, + Atom property, Time time); + // called by openDisplay() to allow subclasses to prepare the display virtual void onOpenDisplay() = 0; @@ -85,6 +98,14 @@ class CXWindowsScreen { static Bool findPropertyNotify(Display*, XEvent* xevent, XPointer arg); + void sendClipboardTargetsNotify(Window requestor, + Atom property, Time time); + void addClipboardRequest(Window requestor, + Atom property, Time time, + IClipboard::EFormat); + void addClipboardMultipleRequest(Window requestor, + Atom property, Time time); + private: Display* m_display; int m_screen; @@ -94,11 +115,15 @@ class CXWindowsScreen { // atoms we'll need Atom m_atomTargets; + Atom m_atomMultiple; Atom m_atomData; Atom m_atomINCR; Atom m_atomText; Atom m_atomCompoundText; + // the contents of our selection + CClipboard m_clipboard; + // X is not thread safe CMutex m_mutex; }; diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index 9eaae81e..08551e37 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -8,14 +8,16 @@ class CString; class IClipboard : public IInterface { public: - enum EFormat { kText, kNum }; + enum EFormat { kText, kNumFormats }; // manipulators // grab ownership of and clear the clipboard of all data. // only add() may be called between an open() and its - // corresponding close(). - virtual void open() = 0; + // corresponding close(). if open() returns false then + // the clipboard could not be opened or grabbed; do not + // call close() in that case. + virtual bool open() = 0; // close the clipboard. close() must match a preceding open(). // this signals that the clipboard has been filled with all the diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 17341b63..92572e33 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -54,11 +54,11 @@ class IPrimaryScreen : public IInterface { /* // show or hide the screen saver virtual void onScreenSaver(bool show) = 0; - - // clipboard input - virtual void onClipboardChanged() = 0; */ + // synergy should own the clipboard + virtual void grabClipboard() = 0; + // accessors /* diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 88893da6..2e886fd5 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -7,7 +7,7 @@ #include "MouseTypes.h" class CClient; -//class IClipboard; +class IClipboard; class ISecondaryScreen : public IInterface { public: @@ -50,18 +50,18 @@ class ISecondaryScreen : public IInterface { virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; virtual void mouseWheel(SInt32 delta) = 0; -/* // set the screen's clipboard contents. this is usually called // soon after an enter(). virtual void setClipboard(const IClipboard*) = 0; +/* // show or hide the screen saver virtual void screenSaver(bool show) = 0; - - // clipboard input - virtual void clipboardChanged() = 0; */ + // take ownership of clipboard + virtual void grabClipboard() = 0; + // accessors // get the size of the screen @@ -70,10 +70,8 @@ class ISecondaryScreen : public IInterface { // get the size of jump zone virtual SInt32 getJumpZoneSize() const = 0; -/* // get the screen's clipboard contents virtual void getClipboard(IClipboard*) const = 0; -*/ }; #endif diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index 360c9d7e..ecd396be 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -7,6 +7,8 @@ #include "XSynergy.h" #include "XIO.h" +class IClipboard; + class IServerProtocol : public IInterface { public: // manipulators @@ -23,11 +25,12 @@ class IServerProtocol : public IInterface { virtual void sendClose() = 0; virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; virtual void sendLeave() = 0; + virtual void sendClipboard(const CString&) = 0; virtual void sendGrabClipboard() = 0; - virtual void sendQueryClipboard() = 0; + virtual void sendQueryClipboard(UInt32 seqNum) = 0; virtual void sendScreenSaver(bool on) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; - virtual void sendKeyRepeat(KeyID, KeyModifierMask) = 0; + virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; virtual void sendMouseDown(ButtonID) = 0; virtual void sendMouseUp(ButtonID) = 0; @@ -40,6 +43,8 @@ class IServerProtocol : public IInterface { // manipulators virtual void recvInfo() = 0; + virtual void recvClipboard() = 0; + virtual void recvGrabClipboard() = 0; // accessors }; diff --git a/synergy/Makefile b/synergy/Makefile index bb557bf8..d1cef559 100644 --- a/synergy/Makefile +++ b/synergy/Makefile @@ -19,6 +19,7 @@ CXXFILES = \ CInputPacketStream.cpp \ COutputPacketStream.cpp \ CProtocolUtil.cpp \ + CClipboard.cpp \ CTCPSocketFactory.cpp \ CXWindowsClipboard.cpp \ CXWindowsScreen.cpp \ diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index c41935c0..c806ae3a 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -16,7 +16,7 @@ static const SInt32 kMinorVersion = 1; static const char kMsgCClose[] = "CBYE"; // server static const char kMsgCEnter[] = "CINN%2i%2i"; // server static const char kMsgCLeave[] = "COUT"; // server -static const char kMsgCClipboard[] = "CCLP"; // server +static const char kMsgCClipboard[] = "CCLP"; // server/client static const char kMsgCScreenSaver[] = "CSEC%1i"; // server static const char kMsgDKeyDown[] = "DKDN%2i%2i"; // server @@ -26,10 +26,10 @@ static const char kMsgDMouseDown[] = "DMDN%1i"; // server static const char kMsgDMouseUp[] = "DMUP%1i"; // server static const char kMsgDMouseMove[] = "DMMV%2i%2i"; // server static const char kMsgDMouseWheel[] = "DMWM%2i"; // server -static const char kMsgDClipboard[] = "DCLP%s"; // server +static const char kMsgDClipboard[] = "DCLP%4i%s"; // server/client static const char kMsgDInfo[] = "DINF%2i%2i%2i"; // client -static const char kMsgQClipboard[] = "QCLP"; // server +static const char kMsgQClipboard[] = "QCLP%4i"; // server static const char kMsgQInfo[] = "QINF"; // server static const char kMsgEIncompatible[] = "EICV"; diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp index 9bff2e3d..6b95ec97 100644 --- a/synergy/synergy.dsp +++ b/synergy/synergy.dsp @@ -87,6 +87,10 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CClipboard.cpp +# End Source File +# Begin Source File + SOURCE=.\CInputPacketStream.cpp # End Source File # Begin Source File @@ -119,6 +123,10 @@ SOURCE=.\XSynergy.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CClipboard.h +# End Source File +# Begin Source File + SOURCE=.\CInputPacketStream.h # End Source File # Begin Source File From f395bc6e129bc764afe9793a6d9252011a5ca4cd Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 25 Nov 2001 18:42:13 +0000 Subject: [PATCH 036/807] executables are now built into a common area on unix (and they already were on win32). --- Make-linux | 5 +++++ Make-solaris | 5 +++++ Makecommon | 2 +- client/Makefile | 7 ++++--- server/Makefile | 7 ++++--- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Make-linux b/Make-linux index 033ca228..3d500d25 100644 --- a/Make-linux +++ b/Make-linux @@ -25,6 +25,11 @@ CXXOPTIMIZER = -g GLDLIBS = -L/usr/X11R6/lib -lX11 -lXext -lXtst GLDOPTS = -L$(LIBDIR) +# +# binary stuff +# +BINTARGET = $(BINDIR)/$(TARGET) + # # library stuff # diff --git a/Make-solaris b/Make-solaris index 4a78355d..c045f2f5 100644 --- a/Make-solaris +++ b/Make-solaris @@ -26,6 +26,11 @@ CXXOPTIMIZER = -g GLDLIBS = -L$(LIBDIR) -lsocket -lnsl -lposix4 GLDOPTS = -z muldefs +# +# binary stuff +# +BINTARGET = $(BINDIR)/$(TARGET) + # # library stuff # diff --git a/Makecommon b/Makecommon index ad8c1558..a8b27ef1 100644 --- a/Makecommon +++ b/Makecommon @@ -13,7 +13,7 @@ NULL = # target directories # LIBDIR = $(DEPTH)/lib -TARGETDIR = $(DEPTH)/bin +BINDIR = $(DEPTH)/bin # # compiler options diff --git a/client/Makefile b/client/Makefile index e7a6573a..9f0e3579 100644 --- a/client/Makefile +++ b/client/Makefile @@ -4,7 +4,7 @@ include $(DEPTH)/Makecommon # # target file # -TARGETS = client +TARGET = client # # source files @@ -37,8 +37,9 @@ LLDLIBS = \ -lpthread \ $(NULL) -targets: $(TARGETS) +targets: $(BINTARGET) -$(TARGETS): $(OBJECTS) $(DEPLIBS) +$(BINTARGET): $(OBJECTS) $(DEPLIBS) + if test ! -d $(BINDIR); then $(MKDIR) $(BINDIR); fi $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) diff --git a/server/Makefile b/server/Makefile index b8dd68a3..301adb38 100644 --- a/server/Makefile +++ b/server/Makefile @@ -4,7 +4,7 @@ include $(DEPTH)/Makecommon # # target file # -TARGETS = server +TARGET = server # # source files @@ -40,8 +40,9 @@ LLDLIBS = \ -lpthread \ $(NULL) -targets: $(TARGETS) +targets: $(BINTARGET) -$(TARGETS): $(OBJECTS) $(DEPLIBS) +$(BINTARGET): $(OBJECTS) $(DEPLIBS) + if test ! -d $(BINDIR); then $(MKDIR) $(BINDIR); fi $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) From c19022e965c86ad7795653a391e8364ac409f02d Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 25 Nov 2001 18:44:13 +0000 Subject: [PATCH 037/807] fixed function signature. --- synergy/CXWindowsClipboard.cpp | 3 ++- synergy/CXWindowsClipboard.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/synergy/CXWindowsClipboard.cpp b/synergy/CXWindowsClipboard.cpp index 6d72dc8d..a8b3823a 100644 --- a/synergy/CXWindowsClipboard.cpp +++ b/synergy/CXWindowsClipboard.cpp @@ -14,9 +14,10 @@ CXWindowsClipboard::~CXWindowsClipboard() { } -void CXWindowsClipboard::open() +bool CXWindowsClipboard::open() { log((CLOG_INFO "open clipboard")); + return true; } void CXWindowsClipboard::close() diff --git a/synergy/CXWindowsClipboard.h b/synergy/CXWindowsClipboard.h index 334af77c..b025536d 100644 --- a/synergy/CXWindowsClipboard.h +++ b/synergy/CXWindowsClipboard.h @@ -9,7 +9,7 @@ class CXWindowsClipboard : public IClipboard { virtual ~CXWindowsClipboard(); // IClipboard overrides - virtual void open(); + virtual bool open(); virtual void close(); virtual void add(EFormat, const CString& data); virtual bool has(EFormat) const; From ff9c3ba7afa547c87a0776892900b88a96b994b6 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 25 Nov 2001 22:20:41 +0000 Subject: [PATCH 038/807] checkpoint. implementing clipboard owner in x windows. --- client/CXWindowsSecondaryScreen.cpp | 5 +- server/CXWindowsPrimaryScreen.cpp | 5 +- synergy/CXWindowsScreen.cpp | 221 ++++++++++++++++++++++------ synergy/CXWindowsScreen.h | 46 +++++- 4 files changed, 222 insertions(+), 55 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index beb143df..5e10f84c 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -47,7 +47,10 @@ void CXWindowsSecondaryScreen::run() // we just lost the selection. that means someone else // grabbed the selection so this screen is now the // selection owner. report that to the server. - m_client->onClipboardChanged(); + if (lostClipboard(xevent.xselectionclear.selection, + xevent.xselectionclear.time)) { + m_client->onClipboardChanged(); + } break; case SelectionNotify: diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index db5cac08..45f80328 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -121,7 +121,10 @@ void CXWindowsPrimaryScreen::run() // we just lost the selection. that means someone else // grabbed the selection so this screen is now the // selection owner. report that to the server. - m_server->grabClipboard(); + if (lostClipboard(xevent.xselectionclear.selection, + xevent.xselectionclear.time)) { + m_server->grabClipboard(); + } break; case SelectionNotify: diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index a1a6aa74..c776c2fe 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -14,6 +14,9 @@ // CXWindowsScreen // +static const Atom kClipboardSelection = XA_PRIMARY; +static const UInt32 kMaxRequestSize = 4096; + CXWindowsScreen::CXWindowsScreen() : m_display(NULL), m_root(None), @@ -53,6 +56,10 @@ void CXWindowsScreen::openDisplay() // get some atoms m_atomTargets = XInternAtom(m_display, "TARGETS", False); m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False); + m_atomTimestamp = XInternAtom(m_display, "TIMESTAMP", False); + m_atomAtom = XInternAtom(m_display, "ATOM", False); + m_atomAtomPair = XInternAtom(m_display, "ATOM_PAIR", False); + m_atomInteger = XInternAtom(m_display, "INTEGER", False); m_atomData = XInternAtom(m_display, "DESTINATION", False); m_atomINCR = XInternAtom(m_display, "INCR", False); m_atomText = XInternAtom(m_display, "TEXT", False); @@ -159,16 +166,29 @@ void CXWindowsScreen::doStop() m_stop = true; } +void CXWindowsScreen::lostClipboard( + Atom selection, Time timestamp) +{ + if (selection == kClipboardSelection) { + // note the time + CLock lock(&m_mutex); + m_lostClipboard = timestamp; + return true; + } + return false; +} + bool CXWindowsScreen::setDisplayClipboard( const IClipboard* clipboard, Window requestor, Time timestamp) { CLock lock(&m_mutex); - XSetSelectionOwner(m_display, XA_PRIMARY, requestor, timestamp); - if (XGetSelectionOwner(m_display, XA_PRIMARY) == requestor) { + XSetSelectionOwner(m_display, kClipboardSelection, requestor, timestamp); + if (XGetSelectionOwner(m_display, kClipboardSelection) == requestor) { // we got the selection log((CLOG_DEBUG "grabbed clipboard")); + m_gotClipboard = timestamp; if (clipboard != NULL) { // save clipboard to serve requests @@ -202,9 +222,7 @@ void CXWindowsScreen::getDisplayClipboard( // in particular, this prevents the event thread from stealing the // selection notify event we're expecting. CLock lock(&m_mutex); - - // use PRIMARY selection as the "clipboard" - Atom selection = XA_PRIMARY; + Atom selection = kClipboardSelection; // ask the selection for all the formats it has. some owners return // the TARGETS atom and some the ATOM atom when TARGETS is requested. @@ -359,7 +377,7 @@ bool CXWindowsScreen::getDisplayClipboard( data.reserve(size); // look for property notify events with the following - PropertyNotifyInfo filter; + CPropertyNotifyInfo filter; filter.m_window = requestor; filter.m_property = property; @@ -488,7 +506,7 @@ IClipboard::EFormat CXWindowsScreen::getFormat(Atom src) const Bool CXWindowsScreen::findSelectionNotify( Display*, XEvent* xevent, XPointer arg) { - Window requestor = *((Window*)arg); + Window requestor = *static_cast(arg); return (xevent->type == SelectionNotify && xevent->xselection.requestor == requestor) ? True : False; } @@ -496,7 +514,7 @@ Bool CXWindowsScreen::findSelectionNotify( Bool CXWindowsScreen::findPropertyNotify( Display*, XEvent* xevent, XPointer arg) { - PropertyNotifyInfo* filter = (PropertyNotifyInfo*)arg; + CPropertyNotifyInfo* filter = static_cast(arg); return (xevent->type == PropertyNotify && xevent->xproperty.window == filter->m_window && xevent->xproperty.atom == filter->m_property && @@ -511,63 +529,139 @@ void CXWindowsScreen::addClipboardRequest( // mutex the display CLock lock(&m_mutex); - // check the target - IClipboard::EFormat format = IClipboard::kNumFormats; - if (selection == XA_PRIMARY) { - if (target == m_atomTargets) { - // send the list of available targets - sendClipboardTargetsNotify(requestor, property, time); - return; - } - else if (target == m_atomMultiple) { - // add a multiple request - if (property != None) { - addClipboardMultipleRequest(requestor, property, time); + // we can only own kClipboardSelection + if (selection != kClipboardSelection) { + return; + } + + // a request for multiple targets is special + if (target == m_atomMultiple) { + // add a multiple request + if (property != None) { + if (addClipboardMultipleRequest(requestor, property, time)) { return; } } - else { - format = getFormat(target); - } + + // bad request or couldn't satisfy request + sendNotify(requestor, target, None, time); + return; } - // if we can't handle the format then send a failure notification + // handle remaining request formats + doAddClipboardRequest(requestor, target, property, time); +} + +void CXWindowsScreen::doAddClipboardRequest( + Window requestor, Atom target, + Atom property, Time time) +{ + // check the target. if we can't handle the format then send + // a failure notification. + IClipboard::EFormat format = getFormat(target); if (format == IClipboard::kNumFormats) { - XEvent event; - event.xselection.type = SelectionNotify; - event.xselection.display = m_display; - event.xselection.requestor = requestor; - event.xselection.selection = selection; - event.xselection.target = target; - event.xselection.property = None; - event.xselection.time = time; - XSendEvent(m_display, requestor, False, 0, &event); + sendNotify(requestor, target, None, time); return; } // we could process short requests without adding to the request // queue but we'll keep things simple by doing all requests the // same way. - // FIXME + addClipboardRequest(requestor, property, time, target); } -void CXWindowsScreen::sendClipboardTargetsNotify( +bool CXWindowsScreen::sendClipboardData( + Window requestor, Atom target, + Atom property, Time time) +{ + // FIXME -- caller must send notify event + if (target == m_atomTargets) { + return sendClipboardTargets(requestor, property, time); + } + else if (target == m_atomTimestamp) { + // FIXME -- handle Alloc errors (by returning false) + XChangeProperty(m_display, requestor, property, + m_atomInteger, sizeof(m_gotClipboard), + PropModeReplace, + reinterpret_cast(&m_gotClipboard), + 1); + return true; + } + else if (getFormat(target) != IClipboard::kNumFormats) { + // convert clipboard to appropriate format + CString data; + + if (data.size() > kMaxRequestSize) { + // FIXME -- handle Alloc errors (by returning false) + const UInt32 zero = 0; + XChangeProperty(m_display, requestor, property, + m_atomINCR, sizeof(zero), + PropModeReplace, + reinterpret_cast(&zero), + 1); + // FIXME -- add to request list + } + else { + // FIXME -- handle Alloc errors (by returning false) + XChangeProperty(m_display, requestor, property, + /* FIXME -- use appropriate type */, + /* FIXME -- use appropriate size */, + PropModeReplace, + data.data(), data.size()); + } + return true; + } + return false; +} + +void CXWindowsScreen::sendClipboardData(CRequestList* list) +{ + // send more data from the request at the head of the queue +} + +bool CXWindowsScreen::sendClipboardTargets( Window requestor, Atom property, Time time) { + // count the number of targets, plus TARGETS and MULTIPLE + SInt32 numTargets = 2; + if (m_clipboard.has(IClipboard::kText)) { + numTargets += 1; + } + // construct response - // FIXME + Atom* response = new Atom[numTargets]; + SInt32 count = 0; + response[count++] = m_atomTargets; + response[count++] = m_atomMultiple; + if (m_clipboard.has(IClipboard::kText)) { + response[count++] = m_atomText; + } - // set property - // FIXME + // send response (we assume we can transfer the entire list at once) + // FIXME -- handle Alloc errors (by returning false) + XChangeProperty(m_display, requestor, property, + m_atomAtom, sizeof(Atom), + PropModeReplace, + reinterpret_cast(response), + count); - // send notification + // done with our response + delete[] response; + + return true; +} + +void CXWindowsScreen::sendNotify( + Window requestor, Atom target, + Atom property, Time time) +{ XEvent event; event.xselection.type = SelectionNotify; event.xselection.display = m_display; event.xselection.requestor = requestor; - event.xselection.selection = XA_PRIMARY; - event.xselection.target = m_atomTargets; + event.xselection.selection = kClipboardSelection; + event.xselection.target = target; event.xselection.property = property; event.xselection.time = time; XSendEvent(m_display, requestor, False, 0, &event); @@ -581,18 +675,51 @@ void CXWindowsScreen::processClipboardRequest( } void CXWindowsScreen::addClipboardRequest( - Window requestor, - Atom property, Time time, - IClipboard::EFormat format) + Window requestor, Atom target, + Atom property, Time time); { - // FIXME + // FIXME -- add to queue. send first part. } -void CXWindowsScreen::addClipboardMultipleRequest( +bool CXWindowsScreen::addClipboardMultipleRequest( Window requestor, Atom property, Time time) { - // FIXME + // get the list of requested formats + Atom type; + SInt32 size; + CString data; + getData(requestor, property, &type, &size, &data); + if (type != m_atomAtomPair) { + // unexpected data type + return false; + } + + // check each format, replacing ones we can't do with None. set + // the property for each to the requested data (for small requests) + // or INCR (for large requests). + bool updated = false; + const Atom none = None; + const Atom* request = static_cast(data.data()); + UInt32 numRequests = data.size() / (2 * sizeof(Atom)); + for (UInt32 index = 0; index < numRequests; ++index) { + // get request info + const Atom target = request[2 * index + 0]; + const Atom property = request[2 * index + 1]; + + // handle target + if (property != None && + !sendClipboardData(requestor, target, property, time)) { + // couldn't handle target. change property to None. + data.replace((2 * index + 1) * sizeof(Atom), sizeof(Atom), + static_cast(&none), sizeof(none)); + updated = true; + } + } + + // FIXME -- update property if updated is true + // FIXME -- send notify. if no formats were allowed then send failure + // but return true. } diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index d7e983b9..b3403b57 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -5,6 +5,8 @@ #include "CMutex.h" #include "BasicTypes.h" #include +#include +#include class CString; @@ -54,6 +56,12 @@ class CXWindowsScreen { // cause getEvent() to return false immediately and forever after void doStop(); + // call when we lose the clipboard ownership (i.e. when we receive + // a SelectionClear event). returns true iff we've actually lost + // a selection we care about. + bool lostClipboard(Atom selection, Time timestamp); + + // set the contents of the clipboard (i.e. primary selection) bool setDisplayClipboard(const IClipboard* clipboard, Window requestor, Time timestamp); @@ -80,11 +88,22 @@ class CXWindowsScreen { virtual void onCloseDisplay() = 0; private: - struct PropertyNotifyInfo { + struct CPropertyNotifyInfo { public: Window m_window; Atom m_property; }; + struct CClipboardRequest { + public: + CString m_data; + UInt32 m_sent; + Window m_requestor; + Atom m_property; + Atom m_type; + int m_format; + }; + typedef std::list CRequestList; + typedef std::map CRequestMap; bool getDisplayClipboard(Atom selection, Atom type, Window requestor, Time timestamp, @@ -98,12 +117,16 @@ class CXWindowsScreen { static Bool findPropertyNotify(Display*, XEvent* xevent, XPointer arg); - void sendClipboardTargetsNotify(Window requestor, + void sendClipboardData(CRequestList*); + void sendClipboardData(Window requestor, Atom target, Atom property, Time time); - void addClipboardRequest(Window requestor, - Atom property, Time time, - IClipboard::EFormat); - void addClipboardMultipleRequest(Window requestor, + void sendClipboardTargets(Window requestor, + Atom property, Time time); + void sendNotify(Window requestor, Atom target, + Atom property, Time time); + void addClipboardRequest(Window requestor, Atom target, + Atom property, Time time); + bool addClipboardMultipleRequest(Window requestor, Atom property, Time time); private: @@ -116,6 +139,10 @@ class CXWindowsScreen { // atoms we'll need Atom m_atomTargets; Atom m_atomMultiple; + Atom m_atomTimestamp; + Atom m_atomAtom; + Atom m_atomAtomPair; + Atom m_atomInteger; Atom m_atomData; Atom m_atomINCR; Atom m_atomText; @@ -124,6 +151,13 @@ class CXWindowsScreen { // the contents of our selection CClipboard m_clipboard; + // when we got the selection and when we lost it + Time m_gotClipboard; + Time m_lostClipboard; + + // the request queues + CRequestMap m_requests; + // X is not thread safe CMutex m_mutex; }; From 9d83cfdbe21e55068741341d7f4a2bce02cad757 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 26 Nov 2001 22:09:53 +0000 Subject: [PATCH 039/807] checkpoint. testing clipboard transfer on X windows. --- synergy/CXWindowsScreen.cpp | 296 ++++++++++++++++++++++++------------ synergy/CXWindowsScreen.h | 16 +- 2 files changed, 204 insertions(+), 108 deletions(-) diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index c776c2fe..10098df6 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -62,6 +62,7 @@ void CXWindowsScreen::openDisplay() m_atomInteger = XInternAtom(m_display, "INTEGER", False); m_atomData = XInternAtom(m_display, "DESTINATION", False); m_atomINCR = XInternAtom(m_display, "INCR", False); + m_atomString = XInternAtom(m_display, "STRING", False); m_atomText = XInternAtom(m_display, "TEXT", False); m_atomCompoundText = XInternAtom(m_display, "COMPOUND_TEXT", False); @@ -76,6 +77,18 @@ void CXWindowsScreen::closeDisplay() // let subclass close down display onCloseDisplay(); + // clear out the clipboard request lists + for (CRequestMap::iterator index = m_requests.begin(); + index != m_requests.end(); ++index) { + CRequestList* list = index->second; + for (CRequestList::iterator index2 = list->begin(); + index2 != list->end(); ++index2) { + delete *index2; + } + delete list; + } + m_requests.clear(); + // close the display XCloseDisplay(m_display); m_display = NULL; @@ -166,7 +179,7 @@ void CXWindowsScreen::doStop() m_stop = true; } -void CXWindowsScreen::lostClipboard( +bool CXWindowsScreen::lostClipboard( Atom selection, Time timestamp) { if (selection == kClipboardSelection) { @@ -506,7 +519,7 @@ IClipboard::EFormat CXWindowsScreen::getFormat(Atom src) const Bool CXWindowsScreen::findSelectionNotify( Display*, XEvent* xevent, XPointer arg) { - Window requestor = *static_cast(arg); + Window requestor = *reinterpret_cast(arg); return (xevent->type == SelectionNotify && xevent->xselection.requestor == requestor) ? True : False; } @@ -514,7 +527,7 @@ Bool CXWindowsScreen::findSelectionNotify( Bool CXWindowsScreen::findPropertyNotify( Display*, XEvent* xevent, XPointer arg) { - CPropertyNotifyInfo* filter = static_cast(arg); + CPropertyNotifyInfo* filter = reinterpret_cast(arg); return (xevent->type == PropertyNotify && xevent->xproperty.window == filter->m_window && xevent->xproperty.atom == filter->m_property && @@ -526,102 +539,227 @@ void CXWindowsScreen::addClipboardRequest( Atom selection, Atom target, Atom property, Time time) { - // mutex the display - CLock lock(&m_mutex); - // we can only own kClipboardSelection if (selection != kClipboardSelection) { return; } + // mutex the display + CLock lock(&m_mutex); + bool success = false; + // a request for multiple targets is special if (target == m_atomMultiple) { // add a multiple request if (property != None) { - if (addClipboardMultipleRequest(requestor, property, time)) { - return; - } + success = sendClipboardMultiple(requestor, property, time); } - - // bad request or couldn't satisfy request - sendNotify(requestor, target, None, time); - return; + } + else { + // handle remaining request formats + success = sendClipboardData(requestor, target, property, time); } - // handle remaining request formats - doAddClipboardRequest(requestor, target, property, time); + // send success or failure + sendNotify(requestor, target, success ? property : None, time); } -void CXWindowsScreen::doAddClipboardRequest( - Window requestor, Atom target, - Atom property, Time time) +void CXWindowsScreen::processClipboardRequest( + Window requestor, + Atom property, Time /*time*/) { - // check the target. if we can't handle the format then send - // a failure notification. - IClipboard::EFormat format = getFormat(target); - if (format == IClipboard::kNumFormats) { - sendNotify(requestor, target, None, time); + CLock lock(&m_mutex); + + // find the request list + CRequestMap::iterator index = m_requests.find(requestor); + if (index == m_requests.end()) { + log((CLOG_WARN "received property event on unexpected window")); return; } + CRequestList* list = index->second; - // we could process short requests without adding to the request - // queue but we'll keep things simple by doing all requests the - // same way. - addClipboardRequest(requestor, property, time, target); + // find the property in the list + CRequestList::iterator index2; + for (index2 = list->begin(); index2 != list->end(); ++index2) { + if ((*index2)->m_property == property) { + break; + } + } + if (index2 == list->end()) { + log((CLOG_WARN "received property event on unexpected property")); + return; + } + CClipboardRequest* request = *index2; + + // compute amount of data to send + assert(request->m_sent <= request->m_data.size()); + UInt32 count = request->m_data.size() - request->m_sent; + if (count > kMaxRequestSize) { + // limit maximum chunk size + count = kMaxRequestSize; + + // make it a multiple of the size + count &= ~((request->m_size >> 3) - 1); + } + + // send more data + // FIXME -- handle Alloc errors (by returning false) + XChangeProperty(m_display, request->m_requestor, request->m_property, + request->m_type, request->m_size, + PropModeReplace, + reinterpret_cast( + request->m_data.data() + request->m_sent), + count / (request->m_size >> 3)); + + // account for sent data + request->m_sent += count; + + // if we sent zero bytes then we're done sending this data. remove + // it from the list and, if the list is empty, the list from the + // map. + list->erase(index2); + delete request; + if (list->empty()) { + m_requests.erase(index); + delete list; + } } bool CXWindowsScreen::sendClipboardData( Window requestor, Atom target, Atom property, Time time) { - // FIXME -- caller must send notify event if (target == m_atomTargets) { return sendClipboardTargets(requestor, property, time); } else if (target == m_atomTimestamp) { - // FIXME -- handle Alloc errors (by returning false) - XChangeProperty(m_display, requestor, property, - m_atomInteger, sizeof(m_gotClipboard), - PropModeReplace, - reinterpret_cast(&m_gotClipboard), - 1); - return true; + return sendClipboardTimestamp(requestor, property, time); } - else if (getFormat(target) != IClipboard::kNumFormats) { - // convert clipboard to appropriate format + else { + // compute the type and size for the requested target and + // convert the data from the clipboard. + Atom type = None; + int size = 0; CString data; + if (target == m_atomText || target == m_atomString) { + if (m_clipboard.has(IClipboard::kText)) { + type = m_atomString; + size = 8; + data = m_clipboard.get(IClipboard::kText); + } + } + + // fail if we don't recognize or can't handle the target + if (type == None || size == 0) { + return false; + } if (data.size() > kMaxRequestSize) { // FIXME -- handle Alloc errors (by returning false) + // set property to INCR const UInt32 zero = 0; XChangeProperty(m_display, requestor, property, m_atomINCR, sizeof(zero), PropModeReplace, - reinterpret_cast(&zero), + reinterpret_cast(&zero), 1); - // FIXME -- add to request list + + // get the appropriate list, creating it if necessary + CRequestList* list = m_requests[requestor]; + if (list == NULL) { + list = new CRequestList; + m_requests[requestor] = list; + } + + // create request object + CClipboardRequest* request = new CClipboardRequest; + request->m_data = data; + request->m_sent = 0; + request->m_requestor = requestor; + request->m_property = property; + request->m_type = type; + request->m_size = size; + + // add request to request list + list->push_back(request); } else { // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, - /* FIXME -- use appropriate type */, - /* FIXME -- use appropriate size */, + type, size, PropModeReplace, - data.data(), data.size()); + reinterpret_cast(data.data()), + data.size() / (size >> 3)); } return true; } return false; } -void CXWindowsScreen::sendClipboardData(CRequestList* list) +bool CXWindowsScreen::sendClipboardMultiple( + Window requestor, + Atom property, Time time) { - // send more data from the request at the head of the queue + // get the list of requested formats + Atom type; + SInt32 size; + CString data; + getData(requestor, property, &type, &size, &data); + if (type != m_atomAtomPair) { + // unexpected data type + return false; + } + + // check each format, replacing ones we can't do with None. set + // the property for each to the requested data (for small requests) + // or INCR (for large requests). + bool success = false; + bool updated = false; + UInt32 numRequests = data.size() / (2 * sizeof(Atom)); + for (UInt32 index = 0; index < numRequests; ++index) { + // get request info + const Atom* request = reinterpret_cast(data.data()); + const Atom target = request[2 * index + 0]; + const Atom property = request[2 * index + 1]; + + // handle target + if (property != None) { + if (!sendClipboardData(requestor, target, property, time)) { + // couldn't handle target. change property to None. + const Atom none = None; + data.replace((2 * index + 1) * sizeof(Atom), sizeof(Atom), + reinterpret_cast(&none), + sizeof(none)); + updated = true; + } + else { + success = true; + } + } + } + + // update property if we changed it + if (updated) { + // FIXME -- handle Alloc errors (by returning false) + XChangeProperty(m_display, requestor, property, + m_atomAtomPair, sizeof(Atom), + PropModeReplace, + reinterpret_cast(data.data()), + data.length()); + } + + // send notify if any format was successful + if (success) { + sendNotify(requestor, m_atomMultiple, success ? property : None, time); + return true; + } + + return false; } bool CXWindowsScreen::sendClipboardTargets( Window requestor, - Atom property, Time time) + Atom property, Time /*time*/) { // count the number of targets, plus TARGETS and MULTIPLE SInt32 numTargets = 2; @@ -652,6 +790,19 @@ bool CXWindowsScreen::sendClipboardTargets( return true; } +bool CXWindowsScreen::sendClipboardTimestamp( + Window requestor, + Atom property, Time /*time*/) +{ + // FIXME -- handle Alloc errors (by returning false) + XChangeProperty(m_display, requestor, property, + m_atomInteger, sizeof(m_gotClipboard), + PropModeReplace, + reinterpret_cast(&m_gotClipboard), + 1); + return true; +} + void CXWindowsScreen::sendNotify( Window requestor, Atom target, Atom property, Time time) @@ -667,61 +818,6 @@ void CXWindowsScreen::sendNotify( XSendEvent(m_display, requestor, False, 0, &event); } -void CXWindowsScreen::processClipboardRequest( - Window requestor, - Atom property, Time time) -{ - // FIXME -} - -void CXWindowsScreen::addClipboardRequest( - Window requestor, Atom target, - Atom property, Time time); -{ - // FIXME -- add to queue. send first part. -} - -bool CXWindowsScreen::addClipboardMultipleRequest( - Window requestor, - Atom property, Time time) -{ - // get the list of requested formats - Atom type; - SInt32 size; - CString data; - getData(requestor, property, &type, &size, &data); - if (type != m_atomAtomPair) { - // unexpected data type - return false; - } - - // check each format, replacing ones we can't do with None. set - // the property for each to the requested data (for small requests) - // or INCR (for large requests). - bool updated = false; - const Atom none = None; - const Atom* request = static_cast(data.data()); - UInt32 numRequests = data.size() / (2 * sizeof(Atom)); - for (UInt32 index = 0; index < numRequests; ++index) { - // get request info - const Atom target = request[2 * index + 0]; - const Atom property = request[2 * index + 1]; - - // handle target - if (property != None && - !sendClipboardData(requestor, target, property, time)) { - // couldn't handle target. change property to None. - data.replace((2 * index + 1) * sizeof(Atom), sizeof(Atom), - static_cast(&none), sizeof(none)); - updated = true; - } - } - - // FIXME -- update property if updated is true - // FIXME -- send notify. if no formats were allowed then send failure - // but return true. -} - // // CXWindowsScreen::CDisplayLock diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index b3403b57..4de7f27c 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -100,7 +100,7 @@ class CXWindowsScreen { Window m_requestor; Atom m_property; Atom m_type; - int m_format; + int m_size; }; typedef std::list CRequestList; typedef std::map CRequestMap; @@ -117,17 +117,16 @@ class CXWindowsScreen { static Bool findPropertyNotify(Display*, XEvent* xevent, XPointer arg); - void sendClipboardData(CRequestList*); - void sendClipboardData(Window requestor, Atom target, + bool sendClipboardData(Window requestor, Atom target, Atom property, Time time); - void sendClipboardTargets(Window requestor, + bool sendClipboardMultiple(Window requestor, + Atom property, Time time); + bool sendClipboardTargets(Window requestor, + Atom property, Time time); + bool sendClipboardTimestamp(Window requestor, Atom property, Time time); void sendNotify(Window requestor, Atom target, Atom property, Time time); - void addClipboardRequest(Window requestor, Atom target, - Atom property, Time time); - bool addClipboardMultipleRequest(Window requestor, - Atom property, Time time); private: Display* m_display; @@ -145,6 +144,7 @@ class CXWindowsScreen { Atom m_atomInteger; Atom m_atomData; Atom m_atomINCR; + Atom m_atomString; Atom m_atomText; Atom m_atomCompoundText; From f63d0e4adab5120aeece4f43eb10ee9fee871cc5 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 26 Nov 2001 22:36:51 +0000 Subject: [PATCH 040/807] checkpoint. improvements to clipboard transfer on X windows. not detecting a change to clipboard when synergy window isn't the owner (since there's no event for this; we'll have to check when we leave the screen i guess). large transfers don't seem to work. --- synergy/CXWindowsScreen.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 10098df6..ebc933f4 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -186,6 +186,7 @@ bool CXWindowsScreen::lostClipboard( // note the time CLock lock(&m_mutex); m_lostClipboard = timestamp; + log((CLOG_INFO "lost clipboard ownership at %d", timestamp)); return true; } return false; @@ -200,7 +201,7 @@ bool CXWindowsScreen::setDisplayClipboard( XSetSelectionOwner(m_display, kClipboardSelection, requestor, timestamp); if (XGetSelectionOwner(m_display, kClipboardSelection) == requestor) { // we got the selection - log((CLOG_DEBUG "grabbed clipboard")); + log((CLOG_INFO "grabbed clipboard at %d", timestamp)); m_gotClipboard = timestamp; if (clipboard != NULL) { @@ -251,7 +252,7 @@ void CXWindowsScreen::getDisplayClipboard( const SInt32 numTargets = targets.size() / sizeof(Atom); std::set clipboardFormats; std::set targets; - log((CLOG_DEBUG "selection has %d targets", numTargets)); + log((CLOG_INFO "getting selection with %d targets", numTargets)); for (SInt32 i = 0; i < numTargets; ++i) { Atom format = targetAtoms[i]; log((CLOG_DEBUG " source target %d", format)); @@ -300,6 +301,7 @@ void CXWindowsScreen::getDisplayClipboard( // add to clipboard and note we've done it clipboard->add(actualFormat, data); clipboardFormats.insert(actualFormat); + log((CLOG_INFO " added format %d for target %d", actualFormat, format)); } } else { @@ -573,7 +575,6 @@ void CXWindowsScreen::processClipboardRequest( // find the request list CRequestMap::iterator index = m_requests.find(requestor); if (index == m_requests.end()) { - log((CLOG_WARN "received property event on unexpected window")); return; } CRequestList* list = index->second; @@ -655,11 +656,13 @@ bool CXWindowsScreen::sendClipboardData( } if (data.size() > kMaxRequestSize) { + log((CLOG_DEBUG "handling clipboard request for %d as INCR", target)); + // FIXME -- handle Alloc errors (by returning false) // set property to INCR const UInt32 zero = 0; XChangeProperty(m_display, requestor, property, - m_atomINCR, sizeof(zero), + m_atomINCR, 8 * sizeof(zero), PropModeReplace, reinterpret_cast(&zero), 1); @@ -684,6 +687,8 @@ bool CXWindowsScreen::sendClipboardData( list->push_back(request); } else { + log((CLOG_DEBUG "handling clipboard request for %d", target)); + // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, type, size, @@ -700,6 +705,8 @@ bool CXWindowsScreen::sendClipboardMultiple( Window requestor, Atom property, Time time) { + log((CLOG_DEBUG "handling clipboard request for MULTIPLE")); + // get the list of requested formats Atom type; SInt32 size; @@ -742,7 +749,7 @@ bool CXWindowsScreen::sendClipboardMultiple( if (updated) { // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, - m_atomAtomPair, sizeof(Atom), + m_atomAtomPair, 8 * sizeof(Atom), PropModeReplace, reinterpret_cast(data.data()), data.length()); @@ -761,10 +768,12 @@ bool CXWindowsScreen::sendClipboardTargets( Window requestor, Atom property, Time /*time*/) { + log((CLOG_DEBUG "handling request for TARGETS")); + // count the number of targets, plus TARGETS and MULTIPLE SInt32 numTargets = 2; if (m_clipboard.has(IClipboard::kText)) { - numTargets += 1; + numTargets += 2; } // construct response @@ -774,12 +783,13 @@ bool CXWindowsScreen::sendClipboardTargets( response[count++] = m_atomMultiple; if (m_clipboard.has(IClipboard::kText)) { response[count++] = m_atomText; + response[count++] = m_atomString; } // send response (we assume we can transfer the entire list at once) // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, - m_atomAtom, sizeof(Atom), + m_atomAtom, 8 * sizeof(Atom), PropModeReplace, reinterpret_cast(response), count); @@ -794,9 +804,11 @@ bool CXWindowsScreen::sendClipboardTimestamp( Window requestor, Atom property, Time /*time*/) { + log((CLOG_DEBUG "handling clipboard request for TIMESTAMP")); + // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, - m_atomInteger, sizeof(m_gotClipboard), + m_atomInteger, 8 * sizeof(m_gotClipboard), PropModeReplace, reinterpret_cast(&m_gotClipboard), 1); From 7d9fa4151401faada60ed85372081f8abd1af6a3 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Apr 2002 10:43:53 +0000 Subject: [PATCH 041/807] added handling for DestroyNotify of clipboard requestors. --- client/CXWindowsSecondaryScreen.cpp | 6 +++ server/CXWindowsPrimaryScreen.cpp | 6 +++ synergy/CXWindowsScreen.cpp | 67 ++++++++++++++++++++++------- synergy/CXWindowsScreen.h | 3 ++ 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 5e10f84c..553564e1 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -86,6 +86,12 @@ void CXWindowsSecondaryScreen::run() xevent.xproperty.time); } break; + + case DestroyNotify: + // looks like one of the windows that requested a clipboard + // transfer has gone bye-bye. + destroyClipboardRequest(xevent.xdestroywindow.window); + break; } } } diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 45f80328..2401e9f4 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -160,6 +160,12 @@ void CXWindowsPrimaryScreen::run() xevent.xproperty.time); } break; + + case DestroyNotify: + // looks like one of the windows that requested a clipboard + // transfer has gone bye-bye. + destroyClipboardRequest(xevent.xdestroywindow.window); + break; } } } diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index ebc933f4..e3cfee8f 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -578,6 +578,7 @@ void CXWindowsScreen::processClipboardRequest( return; } CRequestList* list = index->second; + assert(list != NULL); // find the property in the list CRequestList::iterator index2; @@ -591,6 +592,7 @@ void CXWindowsScreen::processClipboardRequest( return; } CClipboardRequest* request = *index2; + assert(request != NULL); // compute amount of data to send assert(request->m_sent <= request->m_data.size()); @@ -617,15 +619,45 @@ void CXWindowsScreen::processClipboardRequest( // if we sent zero bytes then we're done sending this data. remove // it from the list and, if the list is empty, the list from the - // map. - list->erase(index2); - delete request; - if (list->empty()) { - m_requests.erase(index); - delete list; + // map. also stop watching the requestor for events. + if (count == 0) { + list->erase(index2); + delete request; + if (list->empty()) { + m_requests.erase(index); + delete list; + } + XSelectInput(m_display, requestor, NoEventMask); } } +void CXWindowsScreen::destroyClipboardRequest( + Window requestor) +{ + CLock lock(&m_mutex); + + // find the request list + CRequestMap::iterator index = m_requests.find(requestor); + if (index == m_requests.end()) { + return; + } + CRequestList* list = index->second; + assert(list != NULL); + + // destroy every request in the list + for (CRequestList::iterator index2 = list->begin(); + index2 != list->end(); ++index2) { + delete *index2; + } + + // remove and destroy the list + m_requests.erase(index); + delete list; + + // note -- we don't stop watching the window for events because + // we're called in response to the window being destroyed. +} + bool CXWindowsScreen::sendClipboardData( Window requestor, Atom target, Atom property, Time time) @@ -658,15 +690,6 @@ bool CXWindowsScreen::sendClipboardData( if (data.size() > kMaxRequestSize) { log((CLOG_DEBUG "handling clipboard request for %d as INCR", target)); - // FIXME -- handle Alloc errors (by returning false) - // set property to INCR - const UInt32 zero = 0; - XChangeProperty(m_display, requestor, property, - m_atomINCR, 8 * sizeof(zero), - PropModeReplace, - reinterpret_cast(&zero), - 1); - // get the appropriate list, creating it if necessary CRequestList* list = m_requests[requestor]; if (list == NULL) { @@ -685,6 +708,20 @@ bool CXWindowsScreen::sendClipboardData( // add request to request list list->push_back(request); + + // start watching requestor for property changes and + // destruction + XSelectInput(m_display, requestor, StructureNotifyMask | + PropertyChangeMask); + + // FIXME -- handle Alloc errors (by returning false) + // set property to INCR + const UInt32 zero = 0; + XChangeProperty(m_display, requestor, property, + m_atomINCR, 8 * sizeof(zero), + PropModeReplace, + reinterpret_cast(&zero), + 1); } else { log((CLOG_DEBUG "handling clipboard request for %d", target)); diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 4de7f27c..0166a473 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -81,6 +81,9 @@ class CXWindowsScreen { void processClipboardRequest(Window window, Atom property, Time time); + // terminate a selection request + void destroyClipboardRequest(Window window); + // called by openDisplay() to allow subclasses to prepare the display virtual void onOpenDisplay() = 0; From b01fc4bd35614cdd16a89f154a9ad3f808ddee8a Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Apr 2002 10:44:01 +0000 Subject: [PATCH 042/807] Added notes on keyboard handling. --- notes | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/notes b/notes index f4933c81..83320ca2 100644 --- a/notes +++ b/notes @@ -99,3 +99,24 @@ X11 key translation (server) * don't send compose, don't send dead keys, send composed key? * send all keys, let client do compose +--- +translate keys to keysyms + shift state is embedded in keysym + alt/ctrl/meta are stored separately, also caps/num/scroll lock + shift state also stored separately + +X to keysym: + keycode + shift -> keysym via XLookupString() +X from keysym: + keysym -> XKeysymToKeycode() or XGetKeyboardMapping() and search for index + must synth shift key events to get correct index for key + +Win32 to keysym: + VK code + shift -> keysym via table lookup +Win32 from keysym: + VK code and shift via table lookup + must synth shift key events to get correct character for key + +don't allow leaving screen when any key is down + that includes shift keys + caps lock, etc must not be down but can be on From 419eadfaf9c4d901a130ebb4bc4bd016af24913e Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 26 Apr 2002 17:38:01 +0000 Subject: [PATCH 043/807] changed processing of key events in X. secondary screen now activates/deactivates modifiers as necessary to get a keycode interpreted as the expected keysym. still some work and testing to do on this. --- client/CXWindowsSecondaryScreen.cpp | 469 +++++++++++++++++++++++++++- client/CXWindowsSecondaryScreen.h | 53 +++- server/CXWindowsPrimaryScreen.cpp | 40 ++- server/CXWindowsPrimaryScreen.h | 2 +- synergy/KeyTypes.h | 2 +- 5 files changed, 536 insertions(+), 30 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 553564e1..5e60a4d7 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -4,6 +4,9 @@ #include "CLog.h" #include #include +#include +#define XK_MISCELLANY +#include #include // @@ -35,6 +38,17 @@ void CXWindowsSecondaryScreen::run() // handle event switch (xevent.type) { + case MappingNotify: { + // keyboard mapping changed + CDisplayLock display(this); + XRefreshKeyboardMapping(&xevent.xmapping); + updateKeys(display); + updateKeycodeMap(display); + updateModifierMap(display); + updateModifiers(display); + break; + } + case LeaveNotify: { // mouse moved out of hider window somehow. hide the window. assert(m_window != None); @@ -118,6 +132,12 @@ void CXWindowsSecondaryScreen::open(CClient* client) if (!XQueryExtension(display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) throw int(6); // FIXME -- make exception for this + + // update key state + updateKeys(display); + updateKeycodeMap(display); + updateModifierMap(display); + updateModifiers(display); } void CXWindowsSecondaryScreen::close() @@ -143,6 +163,10 @@ void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) // show cursor XUnmapWindow(display, m_window); + + // update our keyboard state to reflect the local state + updateKeys(display); + updateModifiers(display); } void CXWindowsSecondaryScreen::leave() @@ -154,8 +178,25 @@ void CXWindowsSecondaryScreen::leave() void CXWindowsSecondaryScreen::keyDown( KeyID key, KeyModifierMask mask) { + Keystrokes keys; + KeyCode keycode; + CDisplayLock display(this); - XTestFakeKeyEvent(display, mapKey(key, mask), True, CurrentTime); + + // get the sequence of keys to simulate key press and the final + // modifier state. + m_mask = mapKey(keys, keycode, key, mask, True); + if (keys.empty()) + return; + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) + XTestFakeKeyEvent(display, k->first, k->second, CurrentTime); + + // note that key is now down + m_keys[keycode] = true; + + // update XSync(display, False); } @@ -169,8 +210,25 @@ void CXWindowsSecondaryScreen::keyRepeat( void CXWindowsSecondaryScreen::keyUp( KeyID key, KeyModifierMask mask) { + Keystrokes keys; + KeyCode keycode; + CDisplayLock display(this); - XTestFakeKeyEvent(display, mapKey(key, mask), False, CurrentTime); + + // get the sequence of keys to simulate key release and the final + // modifier state. + m_mask = mapKey(keys, keycode, key, mask, False); + if (keys.empty()) + return; + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) + XTestFakeKeyEvent(display, k->first, k->second, CurrentTime); + + // note that key is now up + m_keys[keycode] = false; + + // update XSync(display, False); } @@ -294,17 +352,408 @@ void CXWindowsSecondaryScreen::leaveNoLock(Display* display) XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0); } -KeyCode CXWindowsSecondaryScreen::mapKey( - KeyID id, KeyModifierMask /*mask*/) const -{ - CDisplayLock display(this); - // FIXME -- use mask - return XKeysymToKeycode(display, static_cast(id)); -} - unsigned int CXWindowsSecondaryScreen::mapButton( ButtonID id) const { // FIXME -- should use button mapping? return static_cast(id); } + +KeyModifierMask CXWindowsSecondaryScreen::mapKey( + Keystrokes& keys, + KeyCode& keycode, + KeyID id, KeyModifierMask mask, + Bool press) const +{ + // note -- must have display locked on entry + + // the system translates key events into characters depending + // on the modifier key state at the time of the event. to + // generate the right keysym we need to set the modifier key + // states appropriately. + // + // the mask passed by the caller is the desired mask. however, + // there may not be a keycode mapping to generate the desired + // keysym with that mask. we override the bits in the mask + // that cannot be accomodated. + + // lookup the a keycode for this key id. also return the + // key modifier mask required. + unsigned int outMask; + if (!findKeyCode(keycode, outMask, id, maskToX(mask))) { + // we cannot generate the desired keysym because no key + // maps to that keysym. just return the current mask. + return m_mask; + } + + // if we cannot match the modifier mask then don't return any + // keys and just return the current mask. + if ((outMask & m_modifierMask) != outMask) { + return m_mask; + } + + // note if the key is a modifier + ModifierMap::const_iterator index = m_keycodeToModifier.find(keycode); + const bool isModifier = (index != m_keycodeToModifier.end()); + + // add the key events required to get to the modifier state + // necessary to generate an event yielding id. also save the + // key events required to restore the state. if the key is + // a modifier key then skip this because modifiers should not + // modify modifiers. + Keystrokes undo; + if (outMask != m_mask && !isModifier) { + for (unsigned int i = 0; i < 8; ++i) { + unsigned int bit = (1 << i); + if ((outMask & bit) != (m_mask & bit)) { + // get list of keycodes for the modifier. there must + // be at least one. + const KeyCode* modifierKeys = + &m_modifierToKeycode[i * m_keysPerModifier]; + assert(modifierKeys[0] != 0); + + if ((outMask & bit) != 0) { + // modifier is not active but should be. if the + // modifier is a toggle then toggle it on with a + // press/release, otherwise activate it with a + // press. use the first keycode for the modifier. + const KeyCode modifierKey = modifierKeys[0]; + keys.push_back(std::make_pair(modifierKey, True)); + if ((bit & m_toggleModifierMask) != 0) { + keys.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, True)); + } + else { + undo.push_back(std::make_pair(modifierKey, False)); + } + } + + else { + // modifier is active but should not be. if the + // modifier is a toggle then toggle it off with a + // press/release, otherwise deactivate it with a + // release. we must check each keycode for the + // modifier if not a toggle. + if (bit & m_toggleModifierMask) { + const KeyCode modifierKey = modifierKeys[0]; + keys.push_back(std::make_pair(modifierKey, True)); + keys.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, True)); + } + else { + for (unsigned int j = 0; j < m_keysPerModifier; ++j) { + const KeyCode key = modifierKeys[j]; + if (m_keys[key]) { + keys.push_back(std::make_pair(key, False)); + undo.push_back(std::make_pair(key, True)); + } + } + } + } + } + } + } + + // add the key event + keys.push_back(std::make_pair(keycode, press)); + + // add key events to restore the modifier state. apply events in + // the reverse order that they're stored in undo. + while (!undo.empty()) { + keys.push_back(undo.back()); + undo.pop_back(); + } + + // if the key is a modifier key then compute the modifier map after + // this key is pressed. + mask = m_mask; + if (isModifier) { + // get modifier + const unsigned int modifierBit = (1 << index->second); + + // toggle keys modify the state on press if toggling on and on + // release if toggling off. other keys set the bit on press + // and clear the bit on release. + if ((modifierBit & m_toggleModifierMask) != 0) { + if (((mask & modifierBit) == 0) == press) + mask ^= modifierBit; + } + else if (press) { + mask |= modifierBit; + } + else { + // can't reset bit until all keys that set it are released. + // scan those keys to see if any (except keycode) are pressed. + bool down = false; + const KeyCode* modifierKeys = &m_modifierToKeycode[ + index->second * m_keysPerModifier]; + for (unsigned int j = 0; !down && j < m_keysPerModifier; ++j) { + if (m_keys[modifierKeys[j]]) + down = true; + } + if (!down) + mask &= ~modifierBit; + } + } + + return mask; +} + +bool CXWindowsSecondaryScreen::findKeyCode( + KeyCode& keycode, + unsigned int& maskOut, + KeyID id, + unsigned int maskIn) const +{ + // find a keycode to generate id. XKeysymToKeycode() almost does + // what we need but won't tell us which index to use with the + // keycode. return false if there's no keycode to generate id. + KeyCodeMap::const_iterator index = m_keycodeMap.find(id); + if (index == m_keycodeMap.end()) + return false; + + // save the keycode + keycode = index->second.keycode; + + // compute output mask. that's the set of modifiers that need to + // be enabled when the keycode event is encountered in order to + // generate the id keysym and match maskIn. it's possible that + // maskIn wants, say, a shift key to be down but that would make + // it impossible to generate the keysym. in that case we must + // override maskIn. + // + // this is complicated by caps/shift-lock and num-lock. for + // example, if id is a keypad keysym and maskIn indicates that + // shift is not active but keyMask indicates that shift is + // required then we can either activate shift and then send + // the keycode or we can activate num-lock and then send the + // keycode. the latter is better because applications may + // treat, say, shift+Home differently than Home. + maskOut = (maskIn & ~index->second.keyMaskMask); + if (IsKeypadKey(id) || IsPrivateKeypadKey(id)) { + // compare shift state of maskIn and keyMask + const bool agree = ((maskIn & ShiftMask) == + (index->second.keyMask & ShiftMask)); + + // get num-lock state + const bool numLockActive = ((m_mask & m_numLockMask) != 0); + + // if num-lock is active and the shift states agree or if + // num-lock is not active and the shift states do not agree + // then we should toggle num-lock. + if (numLockActive == agree) { + maskOut |= (maskIn & ShiftMask) | + (index->second.keyMask & ~ShiftMask); + if (numLockActive) + maskOut &= ~m_numLockMask; + else + maskOut |= m_numLockMask; + } + else { + maskOut |= index->second.keyMask; + } + } + else { + // compare shift state of maskIn and keyMask + const bool agree = ((maskIn & ShiftMask) == + (index->second.keyMask & ShiftMask)); + + // get caps-lock state + const bool capsLockActive = ((m_mask & m_capsLockMask) != 0); + + // if caps-lock is active and the shift states agree or if + // caps-lock is not active and the shift states do not agree + // then we should toggle caps-lock. + if (capsLockActive == agree) { + maskOut |= (maskIn & ShiftMask) | + (index->second.keyMask & ~ShiftMask); + if (capsLockActive) + maskOut &= ~m_capsLockMask; + else + maskOut |= m_capsLockMask; + } + else { + maskOut |= index->second.keyMask; + } + } + + return true; +} + +unsigned int CXWindowsSecondaryScreen::maskToX( + KeyModifierMask inMask) const +{ + // FIXME -- should be configurable. not using Mod3Mask. + unsigned int outMask = 0; + if (inMask & KeyModifierShift) + outMask |= ShiftMask; + if (inMask & KeyModifierControl) + outMask |= ControlMask; + if (inMask & KeyModifierAlt) + outMask |= Mod1Mask; + if (inMask & KeyModifierMeta) + outMask |= Mod4Mask; + if (inMask & KeyModifierCapsLock) + outMask |= LockMask; + if (inMask & KeyModifierNumLock) + outMask |= Mod2Mask; + if (inMask & KeyModifierScrollLock) + outMask |= Mod5Mask; + return outMask; +} + +void CXWindowsSecondaryScreen::updateKeys(Display* display) +{ + // ask server which keys are pressed + char keys[32]; + XQueryKeymap(display, keys); + + // transfer to our state + for (unsigned int i = 0, j = 0; i < 32; j += 8, ++i) { + m_keys[j + 0] = ((keys[i] & 0x01) != 0); + m_keys[j + 1] = ((keys[i] & 0x02) != 0); + m_keys[j + 2] = ((keys[i] & 0x04) != 0); + m_keys[j + 3] = ((keys[i] & 0x08) != 0); + m_keys[j + 4] = ((keys[i] & 0x10) != 0); + m_keys[j + 5] = ((keys[i] & 0x20) != 0); + m_keys[j + 6] = ((keys[i] & 0x40) != 0); + m_keys[j + 7] = ((keys[i] & 0x80) != 0); + } +} + +void CXWindowsSecondaryScreen::updateModifiers( + Display*) +{ + // update active modifier mask + m_mask = 0; + for (unsigned int i = 0; i < 8; ++i) { + const unsigned int bit = (1 << i); + if ((bit & m_toggleModifierMask) == 0) { + for (unsigned int j = 0; j < m_keysPerModifier; ++j) { + if (m_keys[m_modifierToKeycode[i * m_keysPerModifier + j]]) + m_mask |= bit; + } + } + else { + // FIXME -- not sure how to check current lock states + } + } +} + +void CXWindowsSecondaryScreen::updateKeycodeMap( + Display* display) +{ + // get the number of keycodes + int minKeycode, maxKeycode; + XDisplayKeycodes(display, &minKeycode, &maxKeycode); + const int numKeycodes = maxKeycode - minKeycode + 1; + + // get the keyboard mapping for all keys + int keysymsPerKeycode; + KeySym* keysyms = XGetKeyboardMapping(display, + minKeycode, numKeycodes, + &keysymsPerKeycode); + + // restrict keysyms per keycode to 2 because, frankly, i have no + // idea how/what modifiers are used to access keysyms beyond the + // first 2. + int numKeysyms = 2; // keysymsPerKeycode + + // initialize + KeyCodeMask entry; + m_keycodeMap.clear(); + + // insert keys + for (int i = 0; i < numKeycodes; ++i) { + // how many keysyms for this keycode? + int n; + for (n = 0; n < numKeysyms; ++n) { + if (keysyms[i * keysymsPerKeycode + n] == NoSymbol) { + break; + } + } + + // move to next keycode if there are no keysyms + if (n == 0) { + continue; + } + + // set the mask of modifiers that this keycode uses + entry.keyMaskMask = (n == 1) ? 0 : (ShiftMask | LockMask); + + // add entries for this keycode + entry.keycode = static_cast(minKeycode + i); + for (int j = 0; j < numKeysyms; ++j) { + entry.keyMask = (j == 0) ? 0 : ShiftMask; + m_keycodeMap.insert(std::make_pair(keysyms[i * + keysymsPerKeycode + j], entry)); + } + } + + // clean up + XFree(keysyms); +} + +void CXWindowsSecondaryScreen::updateModifierMap( + Display* display) +{ + // get modifier map from server + XModifierKeymap* keymap = XGetModifierMapping(display); + + // initialize + m_modifierMask = 0; + m_toggleModifierMask = 0; + m_numLockMask = 0; + m_capsLockMask = 0; + m_keysPerModifier = keymap->max_keypermod; + m_modifierToKeycode.clear(); + m_modifierToKeycode.resize(8 * m_keysPerModifier); + + // set keycodes and masks + for (unsigned int i = 0; i < 8; ++i) { + const unsigned int bit = (1 << i); + for (unsigned int j = 0; j < m_keysPerModifier; ++j) { + KeyCode keycode = keymap->modifiermap[i * m_keysPerModifier + j]; + + // save in modifier to keycode + m_modifierToKeycode[i * m_keysPerModifier + j] = keycode; + + // save in keycode to modifier + m_keycodeToModifier.insert(std::make_pair(keycode, i)); + + // modifier is enabled if keycode isn't 0 + if (keycode != 0) + m_modifierMask |= bit; + + // modifier is a toggle if the keysym is a toggle modifier + const KeySym keysym = XKeycodeToKeysym(display, keycode, 0); + if (isToggleKeysym(keysym)) { + m_toggleModifierMask |= bit; + + // note num/caps-lock + if (keysym == XK_Num_Lock) + m_numLockMask |= bit; + if (keysym == XK_Caps_Lock) + m_capsLockMask |= bit; + } + } + } + + XFreeModifiermap(keymap); +} + +bool CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) +{ + switch (key) { + case XK_Caps_Lock: + case XK_Shift_Lock: + case XK_Num_Lock: + case XK_Scroll_Lock: + return true; + + default: + return false; + } +} diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 11586f26..5597dfe7 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -3,6 +3,7 @@ #include "CXWindowsScreen.h" #include "ISecondaryScreen.h" +#include class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen { public: @@ -35,13 +36,63 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen virtual void onCloseDisplay(); private: + struct KeyCodeMask { + public: + KeyCode keycode; + unsigned int keyMask; + unsigned int keyMaskMask; + }; + typedef std::pair Keystroke; + typedef std::vector Keystrokes; + typedef std::vector KeyCodes; + typedef std::map KeyCodeMap; + typedef std::map ModifierMap; + void leaveNoLock(Display*); - KeyCode mapKey(KeyID, KeyModifierMask) const; unsigned int mapButton(ButtonID button) const; + unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, + KeyModifierMask, Bool press) const; + bool findKeyCode(KeyCode&, unsigned int&, + KeyID id, unsigned int) const; + unsigned int maskToX(KeyModifierMask) const; + + void updateKeys(Display* display); + void updateKeycodeMap(Display* display); + void updateModifiers(Display* display); + void updateModifierMap(Display* display); + static bool isToggleKeysym(KeySym); + private: CClient* m_client; Window m_window; + + // set entries indicate keys that are pressed. indexed by keycode. + bool m_keys[256]; + + // current active modifiers (X key masks) + unsigned int m_mask; + + // maps key IDs to X keycodes and the X modifier key mask needed + // to generate the right keysym + KeyCodeMap m_keycodeMap; + + // the modifiers that have keys bound to them + unsigned int m_modifierMask; + + // set bits indicate modifiers that toggle (e.g. caps-lock) + unsigned int m_toggleModifierMask; + + // masks that indicate which modifier bits are num-lock and caps-lock + unsigned int m_numLockMask; + unsigned int m_capsLockMask; + + // map X modifier key indices to the key codes bound to them + unsigned int m_keysPerModifier; + KeyCodes m_modifierToKeycode; + + // maps keycodes to modifier indices + ModifierMap m_keycodeToModifier; }; #endif diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 2401e9f4..cd8d736a 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -4,6 +4,7 @@ #include "CLog.h" #include #include +#include // // CXWindowsPrimaryScreen @@ -40,10 +41,17 @@ void CXWindowsPrimaryScreen::run() break; } + case MappingNotify: { + // keyboard mapping changed + CDisplayLock display(this); + XRefreshKeyboardMapping(&xevent.xmapping); + break; + } + case KeyPress: { log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(xevent.xkey.keycode, mask); + const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { m_server->onKeyDown(key, mask); } @@ -55,7 +63,7 @@ void CXWindowsPrimaryScreen::run() case KeyRelease: { log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(xevent.xkey.keycode, mask); + const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { m_server->onKeyUp(key, mask); } @@ -406,33 +414,31 @@ KeyModifierMask CXWindowsPrimaryScreen::mapModifier( { // FIXME -- should be configurable KeyModifierMask mask = 0; - if (state & 1) + if (state & ShiftMask) mask |= KeyModifierShift; - if (state & 2) + if (state & LockMask) mask |= KeyModifierCapsLock; - if (state & 4) + if (state & ControlMask) mask |= KeyModifierControl; - if (state & 8) + if (state & Mod1Mask) mask |= KeyModifierAlt; - if (state & 16) + if (state & Mod2Mask) mask |= KeyModifierNumLock; - if (state & 32) + if (state & Mod4Mask) mask |= KeyModifierMeta; - if (state & 128) + if (state & Mod5Mask) mask |= KeyModifierScrollLock; return mask; } -KeyID CXWindowsPrimaryScreen::mapKey( - KeyCode keycode, KeyModifierMask mask) const +KeyID CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const { - int index; - if (mask & KeyModifierShift) - index = 1; - else - index = 0; + KeySym keysym; + char dummy[1]; + CDisplayLock display(this); - return static_cast(XKeycodeToKeysym(display, keycode, index)); + XLookupString(event, dummy, 0, &keysym, NULL); + return static_cast(keysym); } ButtonID CXWindowsPrimaryScreen::mapButton( diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 74f7456d..a0a1b9c8 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -36,7 +36,7 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { SInt32 xAbsolute, SInt32 yAbsolute); KeyModifierMask mapModifier(unsigned int state) const; - KeyID mapKey(KeyCode, KeyModifierMask) const; + KeyID mapKey(XKeyEvent*) const; ButtonID mapButton(unsigned int button) const; private: diff --git a/synergy/KeyTypes.h b/synergy/KeyTypes.h index ec070e3f..f9ca8241 100644 --- a/synergy/KeyTypes.h +++ b/synergy/KeyTypes.h @@ -6,7 +6,7 @@ // type to hold a key identifier typedef UInt32 KeyID; -// type to hold bitmask of key modifiers (i.e. shift keys) +// type to hold bitmask of key modifiers (e.g. shift keys) typedef UInt32 KeyModifierMask; // key codes From 242109d430bce63c325679c213d5c5f4f4219a6a Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 26 Apr 2002 20:12:55 +0000 Subject: [PATCH 044/807] Added hack to handle "half-duplex" caps-lock key on powerbook. That key only reports press when pressed and released when caps-lock is activated and only reports release when pressed and released when caps-lock is deactivated. I don't know of a way to detect this behavior so it may have to be configured by the user. The code assumes normal behavior; will have to add code to set the flag (perhaps from a user configuration). --- server/CXWindowsPrimaryScreen.cpp | 11 +++++++++++ server/CXWindowsPrimaryScreen.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index cd8d736a..2f1efaa9 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -5,6 +5,8 @@ #include #include #include +#define XK_MISCELLANY +#include // // CXWindowsPrimaryScreen @@ -54,6 +56,8 @@ void CXWindowsPrimaryScreen::run() const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { m_server->onKeyDown(key, mask); + if (key == XK_Caps_Lock && m_capsLockHalfDuplex) + m_server->onKeyUp(key, mask | KeyModifierCapsLock); } break; } @@ -65,6 +69,8 @@ void CXWindowsPrimaryScreen::run() const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { + if (key == XK_Caps_Lock && m_capsLockHalfDuplex) + m_server->onKeyDown(key, mask); m_server->onKeyUp(key, mask); } break; @@ -193,6 +199,11 @@ void CXWindowsPrimaryScreen::open(CServer* server) // open the display openDisplay(); + + // check for peculiarities + // FIXME -- may have to get these from some database + m_capsLockHalfDuplex = false; +// m_capsLockHalfDuplex = true; } void CXWindowsPrimaryScreen::close() diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index a0a1b9c8..562f9048 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -43,6 +43,8 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { CServer* m_server; bool m_active; Window m_window; + + bool m_capsLockHalfDuplex; }; #endif From 79b4d333fa42b4c4772a530e5537ff85b62d065d Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 26 Apr 2002 20:14:46 +0000 Subject: [PATCH 045/807] Fixed caps-lock and num-lock behavior. It seems to work okay now but did notice one problem: when powerbook is primary and num-lock is on the keypad works fine until shift is pressed (and released); after that the keypad only works while the shift key is down. --- client/CXWindowsSecondaryScreen.cpp | 60 +++++------------------------ 1 file changed, 9 insertions(+), 51 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 5e60a4d7..8c081d8b 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -522,61 +522,19 @@ bool CXWindowsSecondaryScreen::findKeyCode( // generate the id keysym and match maskIn. it's possible that // maskIn wants, say, a shift key to be down but that would make // it impossible to generate the keysym. in that case we must - // override maskIn. - // - // this is complicated by caps/shift-lock and num-lock. for - // example, if id is a keypad keysym and maskIn indicates that - // shift is not active but keyMask indicates that shift is - // required then we can either activate shift and then send - // the keycode or we can activate num-lock and then send the - // keycode. the latter is better because applications may - // treat, say, shift+Home differently than Home. + // override maskIn. this is complicated by caps/shift-lock and + // num-lock. maskOut = (maskIn & ~index->second.keyMaskMask); if (IsKeypadKey(id) || IsPrivateKeypadKey(id)) { - // compare shift state of maskIn and keyMask - const bool agree = ((maskIn & ShiftMask) == - (index->second.keyMask & ShiftMask)); - - // get num-lock state - const bool numLockActive = ((m_mask & m_numLockMask) != 0); - - // if num-lock is active and the shift states agree or if - // num-lock is not active and the shift states do not agree - // then we should toggle num-lock. - if (numLockActive == agree) { - maskOut |= (maskIn & ShiftMask) | - (index->second.keyMask & ~ShiftMask); - if (numLockActive) - maskOut &= ~m_numLockMask; - else - maskOut |= m_numLockMask; - } - else { - maskOut |= index->second.keyMask; - } + maskOut |= index->second.keyMask; + maskOut &= ~m_numLockMask; } else { - // compare shift state of maskIn and keyMask - const bool agree = ((maskIn & ShiftMask) == - (index->second.keyMask & ShiftMask)); - - // get caps-lock state - const bool capsLockActive = ((m_mask & m_capsLockMask) != 0); - - // if caps-lock is active and the shift states agree or if - // caps-lock is not active and the shift states do not agree - // then we should toggle caps-lock. - if (capsLockActive == agree) { - maskOut |= (maskIn & ShiftMask) | - (index->second.keyMask & ~ShiftMask); - if (capsLockActive) - maskOut &= ~m_capsLockMask; - else - maskOut |= m_capsLockMask; - } - else { - maskOut |= index->second.keyMask; - } + unsigned int maskShift = (index->second.keyMask & ShiftMask); + if (index->second.keyMaskMask != 0 && (m_mask & m_capsLockMask) != 0) + maskShift ^= ShiftMask; + maskOut |= maskShift | (m_mask & m_capsLockMask); + maskOut |= (index->second.keyMask & ~(ShiftMask | LockMask)); } return true; From 80cdde6a3df96998be4e712de9c4699eaa8a09e4 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 26 Apr 2002 20:15:59 +0000 Subject: [PATCH 046/807] updated --- notes | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/notes b/notes index 83320ca2..b3386e0e 100644 --- a/notes +++ b/notes @@ -105,12 +105,6 @@ translate keys to keysyms alt/ctrl/meta are stored separately, also caps/num/scroll lock shift state also stored separately -X to keysym: - keycode + shift -> keysym via XLookupString() -X from keysym: - keysym -> XKeysymToKeycode() or XGetKeyboardMapping() and search for index - must synth shift key events to get correct index for key - Win32 to keysym: VK code + shift -> keysym via table lookup Win32 from keysym: @@ -120,3 +114,16 @@ Win32 from keysym: don't allow leaving screen when any key is down that includes shift keys caps lock, etc must not be down but can be on + +may need to handle "half-duplex" caps-lock on secondary screen + +--- +clipboard stuff: + PRIMARY -- the current selection, normally pastes with middle-mouse + CLIPBOARD -- set by cut/copy menu items, pasted via paste menu item +synergy must track and transfer both. however, only X clients need +the PRIMARY sent to them. + +--- +remove space pairs + not a good thing for formatting From 428166fe8526c7f2e4d0d05b00fead9365d9f82c Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 27 Apr 2002 14:19:19 +0000 Subject: [PATCH 047/807] set TARGETS macro to BIN and LIB targets. --- Makecommon | 1 + 1 file changed, 1 insertion(+) diff --git a/Makecommon b/Makecommon index a8b27ef1..a126b2da 100644 --- a/Makecommon +++ b/Makecommon @@ -39,6 +39,7 @@ ARF = $(AR) # SOURCES = $(CXXFILES) OBJECTS = $(CXXFILES:.cpp=.o) +TARGETS = $(BINTARGET) $(LIBTARGET) # # always unsatisfied target From f9170eb1397f3a4c06e56b5ba8e0098009bdc624 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 27 Apr 2002 14:19:53 +0000 Subject: [PATCH 048/807] Added support for multiple clipboards. This is mainly to support both PRIMARY and CLIPBOARD selections on X windows. --- client/CClient.cpp | 31 ++-- client/CClient.h | 3 +- client/CXWindowsSecondaryScreen.cpp | 15 +- client/CXWindowsSecondaryScreen.h | 6 +- server/CServer.cpp | 111 +++++++----- server/CServer.h | 27 ++- server/CServerProtocol.h | 6 +- server/CServerProtocol1_0.cpp | 33 ++-- server/CServerProtocol1_0.h | 6 +- server/CXWindowsPrimaryScreen.cpp | 19 ++- server/CXWindowsPrimaryScreen.h | 6 +- synergy/CXWindowsScreen.cpp | 256 +++++++++++++++++----------- synergy/CXWindowsScreen.h | 43 ++++- synergy/ClipboardTypes.h | 21 +++ synergy/IPrimaryScreen.h | 7 +- synergy/ISecondaryScreen.h | 7 +- synergy/IServerProtocol.h | 7 +- synergy/ProtocolTypes.h | 115 ++++++++++--- 18 files changed, 468 insertions(+), 251 deletions(-) create mode 100644 synergy/ClipboardTypes.h diff --git a/client/CClient.cpp b/client/CClient.cpp index 5721ac5c..811ac7ee 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -85,14 +85,14 @@ void CClient::run(const CNetworkAddress& serverAddress) } } -void CClient::onClipboardChanged() +void CClient::onClipboardChanged(ClipboardID id) { - log((CLOG_DEBUG "sending clipboard changed")); + log((CLOG_DEBUG "sending clipboard %d changed", id)); CLock lock(&m_mutex); if (m_output != NULL) { // m_output can be NULL if the screen calls this method // before we've gotten around to connecting to the server. - CProtocolUtil::writef(m_output, kMsgCClipboard); + CProtocolUtil::writef(m_output, kMsgCClipboard, id); } } @@ -318,7 +318,12 @@ void CClient::onLeave() void CClient::onGrabClipboard() { - m_screen->grabClipboard(); + ClipboardID id; + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id); + } + m_screen->grabClipboard(id); } void CClient::onScreenSaver() @@ -345,45 +350,47 @@ void CClient::onQueryInfo() void CClient::onQueryClipboard() { // parse message + ClipboardID id; UInt32 seqNum; CClipboard clipboard; { CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgQClipboard + 4, &seqNum); + CProtocolUtil::readf(m_input, kMsgQClipboard + 4, &id, &seqNum); } - log((CLOG_DEBUG "received query clipboard seqnum=%d", seqNum)); + log((CLOG_DEBUG "received query clipboard %d seqnum=%d", id, seqNum)); // get screen's clipboard data - m_screen->getClipboard(&clipboard); + m_screen->getClipboard(id, &clipboard); // marshall the data CString data = clipboard.marshall(); // send it - log((CLOG_DEBUG "sending clipboard seqnum=%d, size=%d", seqNum, data.size())); + log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, seqNum, data.size())); { CLock lock(&m_mutex); - CProtocolUtil::writef(m_output, kMsgDClipboard, seqNum, &data); + CProtocolUtil::writef(m_output, kMsgDClipboard, id, seqNum, &data); } } void CClient::onSetClipboard() { + ClipboardID id; CString data; { // parse message UInt32 seqNum; CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDClipboard + 4, &seqNum, &data); + CProtocolUtil::readf(m_input, kMsgDClipboard + 4, &id, &seqNum, &data); } - log((CLOG_DEBUG "received clipboard size=%d", data.size())); + log((CLOG_DEBUG "received clipboard %d size=%d", id, data.size())); // unmarshall CClipboard clipboard; clipboard.unmarshall(data); // set screen's clipboard - m_screen->setClipboard(&clipboard); + m_screen->setClipboard(id, &clipboard); } void CClient::onKeyDown() diff --git a/client/CClient.h b/client/CClient.h index b214dfc3..7776ad3d 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -4,6 +4,7 @@ #include "CMutex.h" #include "CString.h" #include "BasicTypes.h" +#include "ClipboardTypes.h" class CNetworkAddress; class IInputStream; @@ -20,7 +21,7 @@ class CClient { void run(const CNetworkAddress& serverAddress); // handle events on client's screen - void onClipboardChanged(); + void onClipboardChanged(ClipboardID); // accessors diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 8c081d8b..f5fe3461 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -63,7 +63,8 @@ void CXWindowsSecondaryScreen::run() // selection owner. report that to the server. if (lostClipboard(xevent.xselectionclear.selection, xevent.xselectionclear.time)) { - m_client->onClipboardChanged(); + m_client->onClipboardChanged(getClipboardID( + xevent.xselectionclear.selection)); } break; @@ -260,16 +261,16 @@ void CXWindowsSecondaryScreen::mouseWheel(SInt32) } void CXWindowsSecondaryScreen::setClipboard( - const IClipboard* clipboard) + ClipboardID id, const IClipboard* clipboard) { // FIXME -- don't use CurrentTime - setDisplayClipboard(clipboard, m_window, CurrentTime); + setDisplayClipboard(id, clipboard, m_window, CurrentTime); } -void CXWindowsSecondaryScreen::grabClipboard() +void CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) { // FIXME -- don't use CurrentTime - setDisplayClipboard(NULL, m_window, CurrentTime); + setDisplayClipboard(id, NULL, m_window, CurrentTime); } void CXWindowsSecondaryScreen::getSize( @@ -284,10 +285,10 @@ SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const } void CXWindowsSecondaryScreen::getClipboard( - IClipboard* clipboard) const + ClipboardID id, IClipboard* clipboard) const { // FIXME -- don't use CurrentTime - getDisplayClipboard(clipboard, m_window, CurrentTime); + getDisplayClipboard(id, clipboard, m_window, CurrentTime); } void CXWindowsSecondaryScreen::onOpenDisplay() diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 5597dfe7..5f4eda9f 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -24,11 +24,11 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); virtual void mouseWheel(SInt32 delta); - virtual void setClipboard(const IClipboard*); - virtual void grabClipboard(); + virtual void setClipboard(ClipboardID, const IClipboard*); + virtual void grabClipboard(ClipboardID); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; - virtual void getClipboard(IClipboard*) const; + virtual void getClipboard(ClipboardID, IClipboard*) const; protected: // CXWindowsScreen overrides diff --git a/server/CServer.cpp b/server/CServer.cpp index 10158e26..84617aa4 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -43,9 +43,7 @@ else { wait(0); exit(1); } CServer::CServer() : m_primary(NULL), m_active(NULL), - m_primaryInfo(NULL), - m_clipboardSeqNum(0), - m_clipboardReady(false) + m_primaryInfo(NULL) { m_socketFactory = NULL; m_securityFactory = NULL; @@ -150,14 +148,16 @@ void CServer::setInfo(const CString& client, log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d", client.c_str(), w, h, zoneSize)); } -void CServer::grabClipboard() +void CServer::grabClipboard(ClipboardID id) { - grabClipboard(m_primaryInfo->m_name); + grabClipboard(id, m_primaryInfo->m_name); } -void CServer::grabClipboard(const CString& client) +void CServer::grabClipboard( + ClipboardID id, const CString& client) { CLock lock(&m_mutex); + ClipboardInfo& clipboard = m_clipboards[id]; // client must be connected CScreenList::iterator index = m_screens.find(client); @@ -165,74 +165,77 @@ void CServer::grabClipboard(const CString& client) throw XBadClient(); } - log((CLOG_NOTE "client \"%s\" grabbed clipboard from \"%s\"", client.c_str(), m_clipboardOwner.c_str())); + log((CLOG_NOTE "client \"%s\" grabbed clipboard %d from \"%s\"", client.c_str(), id, clipboard.m_clipboardOwner.c_str())); // save the clipboard owner - m_clipboardOwner = client; + clipboard.m_clipboardOwner = client; // mark client as having the clipboard data - index->second->m_gotClipboard = true; + index->second->m_gotClipboard[id] = true; // tell all other clients to take ownership of clipboard and mark // them as not having the data yet. for (index = m_screens.begin(); index != m_screens.end(); ++index) { if (index->first != client) { CScreenInfo* info = index->second; - info->m_gotClipboard = false; + info->m_gotClipboard[id] = false; if (info->m_protocol == NULL) { - m_primary->grabClipboard(); + m_primary->grabClipboard(id); } else { - info->m_protocol->sendGrabClipboard(); + info->m_protocol->sendGrabClipboard(id); } } } // increment the clipboard sequence number so we can identify the // clipboard query's response. - ++m_clipboardSeqNum; + ++clipboard.m_clipboardSeqNum; // begin getting the clipboard data if (m_active->m_protocol == NULL) { // get clipboard immediately from primary screen - m_primary->getClipboard(&m_clipboard); - m_clipboardData = m_clipboard.marshall(); - m_clipboardReady = true; + m_primary->getClipboard(id, &clipboard.m_clipboard); + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + clipboard.m_clipboardReady = true; } else { // clear out the clipboard since existing data is now out of date. - if (m_clipboard.open()) { - m_clipboard.close(); + if (clipboard.m_clipboard.open()) { + clipboard.m_clipboard.close(); } - m_clipboardReady = false; + clipboard.m_clipboardReady = false; // send request but don't wait for reply - m_active->m_protocol->sendQueryClipboard(m_clipboardSeqNum); + m_active->m_protocol->sendQueryClipboard(id, + clipboard.m_clipboardSeqNum); } } -void CServer::setClipboard( +void CServer::setClipboard(ClipboardID id, UInt32 seqNum, const CString& data) { // update the clipboard if the sequence number matches CLock lock(&m_mutex); - if (seqNum == m_clipboardSeqNum) { + ClipboardInfo& clipboard = m_clipboards[id]; + if (seqNum == clipboard.m_clipboardSeqNum) { // unmarshall into our clipboard buffer - m_clipboardData = data; - m_clipboard.unmarshall(m_clipboardData); - m_clipboardReady = true; + clipboard.m_clipboardData = data; + clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData); + clipboard.m_clipboardReady = true; // if the active client doesn't have the clipboard data // (and it won't unless the client is the one sending us // the data) then send the data now. - if (!m_active->m_gotClipboard) { + if (!m_active->m_gotClipboard[id]) { if (m_active->m_protocol == NULL) { - m_primary->setClipboard(&m_clipboard); + m_primary->setClipboard(id, &clipboard.m_clipboard); } else { - m_active->m_protocol->sendClipboard(m_clipboardData); + m_active->m_protocol->sendClipboard(id, + clipboard.m_clipboardData); } - m_active->m_gotClipboard = true; + m_active->m_gotClipboard[id] = true; } } } @@ -509,14 +512,18 @@ void CServer::switchScreen(CScreenInfo* dst, } // send the clipboard data if we haven't done so yet - if (m_clipboardReady && !m_active->m_gotClipboard) { - if (m_active->m_protocol == NULL) { - m_primary->setClipboard(&m_clipboard); + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + ClipboardInfo& clipboard = m_clipboards[id]; + if (clipboard.m_clipboardReady && !m_active->m_gotClipboard[id]) { + if (m_active->m_protocol == NULL) { + m_primary->setClipboard(id, &clipboard.m_clipboard); + } + else { + m_active->m_protocol->sendClipboard(id, + clipboard.m_clipboardData); + } + m_active->m_gotClipboard[id] = true; } - else { - m_active->m_protocol->sendClipboard(m_clipboardData); - } - m_active->m_gotClipboard = true; } } else { @@ -919,10 +926,13 @@ void CServer::openPrimaryScreen() // set the clipboard owner to the primary screen and then get the // current clipboard data. - m_primary->getClipboard(&m_clipboard); - m_clipboardData = m_clipboard.marshall(); - m_clipboardReady = true; - m_clipboardOwner = m_active->m_name; + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + ClipboardInfo& clipboard = m_clipboards[id]; + m_primary->getClipboard(id, &clipboard.m_clipboard); + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + clipboard.m_clipboardReady = true; + clipboard.m_clipboardOwner = m_active->m_name; + } } void CServer::closePrimaryScreen() @@ -1075,13 +1085,28 @@ CServer::CScreenInfo::CScreenInfo(const CString& name, m_name(name), m_protocol(protocol), m_width(0), m_height(0), - m_zoneSize(0), - m_gotClipboard(false) + m_zoneSize(0) { - // do nothing + for (ClipboardID id = 0; id < kClipboardEnd; ++id) + m_gotClipboard[id] = false; } CServer::CScreenInfo::~CScreenInfo() { // do nothing } + + +// +// CServer::ClipboardInfo +// + +CServer::ClipboardInfo::ClipboardInfo() : + m_clipboard(), + m_clipboardData(), + m_clipboardOwner(), + m_clipboardSeqNum(0), + m_clipboardReady(false) +{ + // do nothing +} diff --git a/server/CServer.h b/server/CServer.h index 3ca443d2..d4bf8553 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -1,6 +1,7 @@ #ifndef CSERVER_H #define CSERVER_H +#include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" #include "CScreenMap.h" @@ -43,13 +44,14 @@ class CServer { bool onMouseMovePrimary(SInt32 x, SInt32 y); void onMouseMoveSecondary(SInt32 dx, SInt32 dy); void onMouseWheel(SInt32 delta); - void grabClipboard(); + void grabClipboard(ClipboardID); // handle messages from clients void setInfo(const CString& clientName, SInt32 w, SInt32 h, SInt32 zoneSize); - void grabClipboard(const CString& clientName); - void setClipboard(UInt32 seqNum, const CString& data); + void grabClipboard(ClipboardID, const CString& clientName); + void setClipboard(ClipboardID, + UInt32 seqNum, const CString& data); // accessors @@ -95,7 +97,7 @@ class CServer { IServerProtocol* m_protocol; SInt32 m_width, m_height; SInt32 m_zoneSize; - bool m_gotClipboard; + bool m_gotClipboard[kClipboardEnd]; }; // change the active screen @@ -146,6 +148,17 @@ class CServer { private: typedef std::list CThreadList; typedef std::map CScreenList; + class ClipboardInfo { + public: + ClipboardInfo(); + + public: + CClipboard m_clipboard; + CString m_clipboardData; + CString m_clipboardOwner; + UInt32 m_clipboardSeqNum; + bool m_clipboardReady; + }; CMutex m_mutex; @@ -165,11 +178,7 @@ class CServer { CScreenMap m_screenMap; - CClipboard m_clipboard; - CString m_clipboardData; - CString m_clipboardOwner; - UInt32 m_clipboardSeqNum; - bool m_clipboardReady; + ClipboardInfo m_clipboards[kClipboardEnd]; }; #endif diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index d5a69a4d..1dda0969 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -33,9 +33,9 @@ class CServerProtocol : public IServerProtocol { virtual void sendClose() = 0; virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; virtual void sendLeave() = 0; - virtual void sendClipboard(const CString&) = 0; - virtual void sendGrabClipboard() = 0; - virtual void sendQueryClipboard(UInt32 seqNum) = 0; + virtual void sendClipboard(ClipboardID, const CString&) = 0; + virtual void sendGrabClipboard(ClipboardID) = 0; + virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum) = 0; virtual void sendScreenSaver(bool on) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index a2e02811..5a97c3e5 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -103,22 +103,24 @@ void CServerProtocol1_0::sendLeave() CProtocolUtil::writef(getOutputStream(), kMsgCLeave); } -void CServerProtocol1_0::sendClipboard(const CString& data) +void CServerProtocol1_0::sendClipboard( + ClipboardID id, const CString& data) { - log((CLOG_INFO "send clipboard to \"%s\" size=%d", getClient().c_str(), data.size())); - CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, 0, &data); + log((CLOG_INFO "send clipboard %d to \"%s\" size=%d", id, getClient().c_str(), data.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); } -void CServerProtocol1_0::sendGrabClipboard() +void CServerProtocol1_0::sendGrabClipboard(ClipboardID id) { - log((CLOG_INFO "send grab clipboard to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCClipboard); + log((CLOG_INFO "send grab clipboard %d to \"%s\"", id, getClient().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id); } -void CServerProtocol1_0::sendQueryClipboard(UInt32 seqNum) +void CServerProtocol1_0::sendQueryClipboard( + ClipboardID id, UInt32 seqNum) { - log((CLOG_INFO "query clipboard to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgQClipboard, seqNum); + log((CLOG_INFO "query clipboard %d to \"%s\"", id, getClient().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgQClipboard, id, seqNum); } void CServerProtocol1_0::sendScreenSaver(bool on) @@ -194,15 +196,18 @@ void CServerProtocol1_0::recvInfo() void CServerProtocol1_0::recvClipboard() { + ClipboardID id; UInt32 seqNum; CString data; - CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &seqNum, &data); - log((CLOG_INFO "received client \"%s\" clipboard seqnum=%d, size=%d", getClient().c_str(), seqNum, data.size())); - getServer()->setClipboard(seqNum, data); + CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); + log((CLOG_INFO "received client \"%s\" clipboard %d seqnum=%d, size=%d", getClient().c_str(), id, seqNum, data.size())); + getServer()->setClipboard(id, seqNum, data); } void CServerProtocol1_0::recvGrabClipboard() { - log((CLOG_INFO "received client \"%s\" grabbed clipboard", getClient().c_str())); - getServer()->grabClipboard(getClient()); + ClipboardID id; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id); + log((CLOG_INFO "received client \"%s\" grabbed clipboard %d", getClient().c_str(), id)); + getServer()->grabClipboard(id, getClient()); } diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index 97745298..63c64f73 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -18,9 +18,9 @@ class CServerProtocol1_0 : public CServerProtocol { virtual void sendClose(); virtual void sendEnter(SInt32 xAbs, SInt32 yAbs); virtual void sendLeave(); - virtual void sendClipboard(const CString&); - virtual void sendGrabClipboard(); - virtual void sendQueryClipboard(UInt32 seqNum); + virtual void sendClipboard(ClipboardID, const CString&); + virtual void sendGrabClipboard(ClipboardID); + virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum); virtual void sendScreenSaver(bool on); virtual void sendKeyDown(KeyID, KeyModifierMask); virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 2f1efaa9..f1315116 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -137,7 +137,8 @@ void CXWindowsPrimaryScreen::run() // selection owner. report that to the server. if (lostClipboard(xevent.xselectionclear.selection, xevent.xselectionclear.time)) { - m_server->grabClipboard(); + m_server->grabClipboard(getClipboardID( + xevent.xselectionclear.selection)); } break; @@ -270,7 +271,7 @@ void CXWindowsPrimaryScreen::leave() assert(result != GrabNotViewable); if (result != GrabSuccess) { log((CLOG_DEBUG "waiting to grab pointer")); - CThread::sleep(0.25); + CThread::sleep(0.1); } } while (result != GrabSuccess); log((CLOG_DEBUG "grabbed pointer")); @@ -283,7 +284,7 @@ void CXWindowsPrimaryScreen::leave() // back off to avoid grab deadlock XUngrabPointer(display, CurrentTime); log((CLOG_DEBUG "ungrabbed pointer, waiting to grab keyboard")); - CThread::sleep(0.25); + CThread::sleep(0.1); } } while (result != GrabSuccess); log((CLOG_DEBUG "grabbed keyboard")); @@ -323,16 +324,16 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( } void CXWindowsPrimaryScreen::setClipboard( - const IClipboard* clipboard) + ClipboardID id, const IClipboard* clipboard) { // FIXME -- don't use CurrentTime - setDisplayClipboard(clipboard, m_window, CurrentTime); + setDisplayClipboard(id, clipboard, m_window, CurrentTime); } -void CXWindowsPrimaryScreen::grabClipboard() +void CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) { // FIXME -- don't use CurrentTime - setDisplayClipboard(NULL, m_window, CurrentTime); + setDisplayClipboard(id, NULL, m_window, CurrentTime); } void CXWindowsPrimaryScreen::getSize( @@ -347,10 +348,10 @@ SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const } void CXWindowsPrimaryScreen::getClipboard( - IClipboard* clipboard) const + ClipboardID id, IClipboard* clipboard) const { // FIXME -- don't use CurrentTime - getDisplayClipboard(clipboard, m_window, CurrentTime); + getDisplayClipboard(id, clipboard, m_window, CurrentTime); } void CXWindowsPrimaryScreen::onOpenDisplay() diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 562f9048..1cdc1563 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -19,11 +19,11 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); virtual void leave(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void setClipboard(const IClipboard*); - virtual void grabClipboard(); + virtual void setClipboard(ClipboardID, const IClipboard*); + virtual void grabClipboard(ClipboardID); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; - virtual void getClipboard(IClipboard*) const; + virtual void getClipboard(ClipboardID, IClipboard*) const; protected: // CXWindowsScreen overrides diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index e3cfee8f..4aa74a41 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -14,7 +14,6 @@ // CXWindowsScreen // -static const Atom kClipboardSelection = XA_PRIMARY; static const UInt32 kMaxRequestSize = 4096; CXWindowsScreen::CXWindowsScreen() : @@ -66,6 +65,11 @@ void CXWindowsScreen::openDisplay() m_atomText = XInternAtom(m_display, "TEXT", False); m_atomCompoundText = XInternAtom(m_display, "COMPOUND_TEXT", False); + // clipboard atoms + m_atomClipboard[kClipboardClipboard] = + XInternAtom(m_display, "CLIPBOARD", False); + m_atomClipboard[kClipboardSelection] = XA_PRIMARY; + // let subclass prep display onOpenDisplay(); } @@ -78,16 +82,19 @@ void CXWindowsScreen::closeDisplay() onCloseDisplay(); // clear out the clipboard request lists - for (CRequestMap::iterator index = m_requests.begin(); - index != m_requests.end(); ++index) { - CRequestList* list = index->second; - for (CRequestList::iterator index2 = list->begin(); + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + ClipboardInfo& clipboard = m_clipboards[id]; + for (CRequestMap::iterator index = clipboard.m_requests.begin(); + index != clipboard.m_requests.end(); ++index) { + CRequestList* list = index->second; + for (CRequestList::iterator index2 = list->begin(); index2 != list->end(); ++index2) { - delete *index2; + delete *index2; + } + delete list; } - delete list; + clipboard.m_requests.clear(); } - m_requests.clear(); // close the display XCloseDisplay(m_display); @@ -179,39 +186,50 @@ void CXWindowsScreen::doStop() m_stop = true; } +ClipboardID CXWindowsScreen::getClipboardID(Atom selection) +{ + for (ClipboardID id = 0; id < kClipboardEnd; ++id) + if (selection == m_atomClipboard[id]) + return id; + return kClipboardEnd; +} + bool CXWindowsScreen::lostClipboard( Atom selection, Time timestamp) { - if (selection == kClipboardSelection) { - // note the time - CLock lock(&m_mutex); - m_lostClipboard = timestamp; - log((CLOG_INFO "lost clipboard ownership at %d", timestamp)); - return true; + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (selection == m_atomClipboard[id]) { + // note the time + CLock lock(&m_mutex); + m_clipboards[id].m_lostClipboard = timestamp; + log((CLOG_INFO "lost clipboard %d ownership at %d", id, timestamp)); + return true; + } } return false; } bool CXWindowsScreen::setDisplayClipboard( + ClipboardID id, const IClipboard* clipboard, Window requestor, Time timestamp) { CLock lock(&m_mutex); - XSetSelectionOwner(m_display, kClipboardSelection, requestor, timestamp); - if (XGetSelectionOwner(m_display, kClipboardSelection) == requestor) { + XSetSelectionOwner(m_display, m_atomClipboard[id], requestor, timestamp); + if (XGetSelectionOwner(m_display, m_atomClipboard[id]) == requestor) { // we got the selection log((CLOG_INFO "grabbed clipboard at %d", timestamp)); - m_gotClipboard = timestamp; + m_clipboards[id].m_gotClipboard = timestamp; if (clipboard != NULL) { // save clipboard to serve requests - CClipboard::copy(&m_clipboard, clipboard); + CClipboard::copy(&m_clipboards[id].m_clipboard, clipboard); } else { // clear clipboard - if (m_clipboard.open()) { - m_clipboard.close(); + if (m_clipboards[id].m_clipboard.open()) { + m_clipboards[id].m_clipboard.close(); } } @@ -222,6 +240,7 @@ bool CXWindowsScreen::setDisplayClipboard( } void CXWindowsScreen::getDisplayClipboard( + ClipboardID id, IClipboard* clipboard, Window requestor, Time timestamp) const { @@ -236,7 +255,7 @@ void CXWindowsScreen::getDisplayClipboard( // in particular, this prevents the event thread from stealing the // selection notify event we're expecting. CLock lock(&m_mutex); - Atom selection = kClipboardSelection; + Atom selection = m_atomClipboard[id]; // ask the selection for all the formats it has. some owners return // the TARGETS atom and some the ATOM atom when TARGETS is requested. @@ -541,10 +560,13 @@ void CXWindowsScreen::addClipboardRequest( Atom selection, Atom target, Atom property, Time time) { - // we can only own kClipboardSelection - if (selection != kClipboardSelection) { + // see if it's a selection we know about + ClipboardID id; + for (id = 0; id < kClipboardEnd; ++id) + if (selection == m_atomClipboard[id]) + break; + if (id == kClipboardEnd) return; - } // mutex the display CLock lock(&m_mutex); @@ -554,16 +576,16 @@ void CXWindowsScreen::addClipboardRequest( if (target == m_atomMultiple) { // add a multiple request if (property != None) { - success = sendClipboardMultiple(requestor, property, time); + success = sendClipboardMultiple(id, requestor, property, time); } } else { // handle remaining request formats - success = sendClipboardData(requestor, target, property, time); + success = sendClipboardData(id, requestor, target, property, time); } // send success or failure - sendNotify(requestor, target, success ? property : None, time); + sendNotify(id, requestor, target, success ? property : None, time); } void CXWindowsScreen::processClipboardRequest( @@ -572,62 +594,71 @@ void CXWindowsScreen::processClipboardRequest( { CLock lock(&m_mutex); - // find the request list - CRequestMap::iterator index = m_requests.find(requestor); - if (index == m_requests.end()) { - return; - } - CRequestList* list = index->second; - assert(list != NULL); + // check every clipboard + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + ClipboardInfo& clipboard = m_clipboards[id]; - // find the property in the list - CRequestList::iterator index2; - for (index2 = list->begin(); index2 != list->end(); ++index2) { - if ((*index2)->m_property == property) { - break; + // find the request list + CRequestMap::iterator index = clipboard.m_requests.find(requestor); + if (index == clipboard.m_requests.end()) { + // this clipboard isn't servicing this requestor window + continue; } - } - if (index2 == list->end()) { - log((CLOG_WARN "received property event on unexpected property")); - return; - } - CClipboardRequest* request = *index2; - assert(request != NULL); + CRequestList* list = index->second; + assert(list != NULL); - // compute amount of data to send - assert(request->m_sent <= request->m_data.size()); - UInt32 count = request->m_data.size() - request->m_sent; - if (count > kMaxRequestSize) { - // limit maximum chunk size - count = kMaxRequestSize; + // find the property in the list + CRequestList::iterator index2; + for (index2 = list->begin(); index2 != list->end(); ++index2) { + if ((*index2)->m_property == property) { + break; + } + } + if (index2 == list->end()) { + // this clipboard isn't waiting on this property + continue; + } + CClipboardRequest* request = *index2; + assert(request != NULL); - // make it a multiple of the size - count &= ~((request->m_size >> 3) - 1); - } + // compute amount of data to send + assert(request->m_sent <= request->m_data.size()); + UInt32 count = request->m_data.size() - request->m_sent; + if (count > kMaxRequestSize) { + // limit maximum chunk size + count = kMaxRequestSize; - // send more data - // FIXME -- handle Alloc errors (by returning false) - XChangeProperty(m_display, request->m_requestor, request->m_property, + // make it a multiple of the size + count &= ~((request->m_size >> 3) - 1); + } + + // send more data + // FIXME -- handle Alloc errors (by returning false) + XChangeProperty(m_display, request->m_requestor, request->m_property, request->m_type, request->m_size, PropModeReplace, reinterpret_cast( request->m_data.data() + request->m_sent), count / (request->m_size >> 3)); - // account for sent data - request->m_sent += count; + // account for sent data + request->m_sent += count; - // if we sent zero bytes then we're done sending this data. remove - // it from the list and, if the list is empty, the list from the - // map. also stop watching the requestor for events. - if (count == 0) { - list->erase(index2); - delete request; - if (list->empty()) { - m_requests.erase(index); - delete list; + // if we sent zero bytes then we're done sending this data. remove + // it from the list and, if the list is empty, the list from the + // map. also stop watching the requestor for events. + if (count == 0) { + list->erase(index2); + delete request; + if (list->empty()) { + clipboard.m_requests.erase(index); + delete list; + } + XSelectInput(m_display, requestor, NoEventMask); } - XSelectInput(m_display, requestor, NoEventMask); + + // request has been serviced + break; } } @@ -636,37 +667,43 @@ void CXWindowsScreen::destroyClipboardRequest( { CLock lock(&m_mutex); - // find the request list - CRequestMap::iterator index = m_requests.find(requestor); - if (index == m_requests.end()) { - return; - } - CRequestList* list = index->second; - assert(list != NULL); + // check every clipboard + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + ClipboardInfo& clipboard = m_clipboards[id]; - // destroy every request in the list - for (CRequestList::iterator index2 = list->begin(); + // find the request list + CRequestMap::iterator index = clipboard.m_requests.find(requestor); + if (index == clipboard.m_requests.end()) { + continue; + } + CRequestList* list = index->second; + assert(list != NULL); + + // destroy every request in the list + for (CRequestList::iterator index2 = list->begin(); index2 != list->end(); ++index2) { - delete *index2; - } + delete *index2; + } - // remove and destroy the list - m_requests.erase(index); - delete list; + // remove and destroy the list + clipboard.m_requests.erase(index); + delete list; + } // note -- we don't stop watching the window for events because // we're called in response to the window being destroyed. } bool CXWindowsScreen::sendClipboardData( + ClipboardID id, Window requestor, Atom target, Atom property, Time time) { if (target == m_atomTargets) { - return sendClipboardTargets(requestor, property, time); + return sendClipboardTargets(id, requestor, property, time); } else if (target == m_atomTimestamp) { - return sendClipboardTimestamp(requestor, property, time); + return sendClipboardTimestamp(id, requestor, property, time); } else { // compute the type and size for the requested target and @@ -675,10 +712,10 @@ bool CXWindowsScreen::sendClipboardData( int size = 0; CString data; if (target == m_atomText || target == m_atomString) { - if (m_clipboard.has(IClipboard::kText)) { + if (m_clipboards[id].m_clipboard.has(IClipboard::kText)) { type = m_atomString; size = 8; - data = m_clipboard.get(IClipboard::kText); + data = m_clipboards[id].m_clipboard.get(IClipboard::kText); } } @@ -691,10 +728,10 @@ bool CXWindowsScreen::sendClipboardData( log((CLOG_DEBUG "handling clipboard request for %d as INCR", target)); // get the appropriate list, creating it if necessary - CRequestList* list = m_requests[requestor]; + CRequestList* list = m_clipboards[id].m_requests[requestor]; if (list == NULL) { list = new CRequestList; - m_requests[requestor] = list; + m_clipboards[id].m_requests[requestor] = list; } // create request object @@ -718,7 +755,8 @@ bool CXWindowsScreen::sendClipboardData( // set property to INCR const UInt32 zero = 0; XChangeProperty(m_display, requestor, property, - m_atomINCR, 8 * sizeof(zero), + m_atomINCR, + 8 * sizeof(zero), PropModeReplace, reinterpret_cast(&zero), 1); @@ -730,7 +768,8 @@ bool CXWindowsScreen::sendClipboardData( XChangeProperty(m_display, requestor, property, type, size, PropModeReplace, - reinterpret_cast(data.data()), + reinterpret_cast( + data.data()), data.size() / (size >> 3)); } return true; @@ -739,6 +778,7 @@ bool CXWindowsScreen::sendClipboardData( } bool CXWindowsScreen::sendClipboardMultiple( + ClipboardID id, Window requestor, Atom property, Time time) { @@ -768,7 +808,7 @@ bool CXWindowsScreen::sendClipboardMultiple( // handle target if (property != None) { - if (!sendClipboardData(requestor, target, property, time)) { + if (!sendClipboardData(id, requestor, target, property, time)) { // couldn't handle target. change property to None. const Atom none = None; data.replace((2 * index + 1) * sizeof(Atom), sizeof(Atom), @@ -786,15 +826,18 @@ bool CXWindowsScreen::sendClipboardMultiple( if (updated) { // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, - m_atomAtomPair, 8 * sizeof(Atom), + m_atomAtomPair, + 8 * sizeof(Atom), PropModeReplace, - reinterpret_cast(data.data()), + reinterpret_cast( + data.data()), data.length()); } // send notify if any format was successful if (success) { - sendNotify(requestor, m_atomMultiple, success ? property : None, time); + sendNotify(id, requestor, m_atomMultiple, + success ? property : None, time); return true; } @@ -802,6 +845,7 @@ bool CXWindowsScreen::sendClipboardMultiple( } bool CXWindowsScreen::sendClipboardTargets( + ClipboardID id, Window requestor, Atom property, Time /*time*/) { @@ -809,7 +853,7 @@ bool CXWindowsScreen::sendClipboardTargets( // count the number of targets, plus TARGETS and MULTIPLE SInt32 numTargets = 2; - if (m_clipboard.has(IClipboard::kText)) { + if (m_clipboards[id].m_clipboard.has(IClipboard::kText)) { numTargets += 2; } @@ -818,7 +862,7 @@ bool CXWindowsScreen::sendClipboardTargets( SInt32 count = 0; response[count++] = m_atomTargets; response[count++] = m_atomMultiple; - if (m_clipboard.has(IClipboard::kText)) { + if (m_clipboards[id].m_clipboard.has(IClipboard::kText)) { response[count++] = m_atomText; response[count++] = m_atomString; } @@ -826,7 +870,8 @@ bool CXWindowsScreen::sendClipboardTargets( // send response (we assume we can transfer the entire list at once) // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, - m_atomAtom, 8 * sizeof(Atom), + m_atomAtom, + 8 * sizeof(Atom), PropModeReplace, reinterpret_cast(response), count); @@ -838,6 +883,7 @@ bool CXWindowsScreen::sendClipboardTargets( } bool CXWindowsScreen::sendClipboardTimestamp( + ClipboardID id, Window requestor, Atom property, Time /*time*/) { @@ -845,22 +891,24 @@ bool CXWindowsScreen::sendClipboardTimestamp( // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, - m_atomInteger, 8 * sizeof(m_gotClipboard), + m_atomInteger, + 8 * sizeof(m_clipboards[id].m_gotClipboard), PropModeReplace, - reinterpret_cast(&m_gotClipboard), + reinterpret_cast( + &m_clipboards[id].m_gotClipboard), 1); return true; } void CXWindowsScreen::sendNotify( - Window requestor, Atom target, - Atom property, Time time) + ClipboardID id, Window requestor, + Atom target, Atom property, Time time) { XEvent event; event.xselection.type = SelectionNotify; event.xselection.display = m_display; event.xselection.requestor = requestor; - event.xselection.selection = kClipboardSelection; + event.xselection.selection = m_atomClipboard[id]; event.xselection.target = target; event.xselection.property = property; event.xselection.time = time; diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 0166a473..2e61e5f4 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -4,6 +4,7 @@ #include "CClipboard.h" #include "CMutex.h" #include "BasicTypes.h" +#include "ClipboardTypes.h" #include #include #include @@ -56,19 +57,25 @@ class CXWindowsScreen { // cause getEvent() to return false immediately and forever after void doStop(); + // determine the clipboard from the X selection. returns + // kClipboardEnd if no such clipboard. + ClipboardID getClipboardID(Atom selection); + // call when we lose the clipboard ownership (i.e. when we receive // a SelectionClear event). returns true iff we've actually lost // a selection we care about. bool lostClipboard(Atom selection, Time timestamp); // set the contents of the clipboard (i.e. primary selection) - bool setDisplayClipboard(const IClipboard* clipboard, + bool setDisplayClipboard(ClipboardID, + const IClipboard* clipboard, Window requestor, Time timestamp); // copy the clipboard contents to clipboard. requestor must be a // valid window; it will be used to receive the transfer. timestamp // should be the timestamp of the provoking event and not CurrentTime. - void getDisplayClipboard(IClipboard* clipboard, + void getDisplayClipboard(ClipboardID, + IClipboard* clipboard, Window requestor, Time timestamp) const; // add a selection request to the request list @@ -120,18 +127,31 @@ class CXWindowsScreen { static Bool findPropertyNotify(Display*, XEvent* xevent, XPointer arg); - bool sendClipboardData(Window requestor, Atom target, + bool sendClipboardData(ClipboardID, Window requestor, + Atom target, Atom property, Time time); + bool sendClipboardMultiple(ClipboardID, Window requestor, Atom property, Time time); - bool sendClipboardMultiple(Window requestor, + bool sendClipboardTargets(ClipboardID, Window requestor, Atom property, Time time); - bool sendClipboardTargets(Window requestor, - Atom property, Time time); - bool sendClipboardTimestamp(Window requestor, - Atom property, Time time); - void sendNotify(Window requestor, Atom target, + bool sendClipboardTimestamp(ClipboardID, Window requestor, Atom property, Time time); + void sendNotify(ClipboardID, Window requestor, + Atom target, Atom property, Time time); private: + class ClipboardInfo { + public: + // the contents of the clipboard + CClipboard m_clipboard; + + // when we got the clipboard and when we lost it + Time m_gotClipboard; + Time m_lostClipboard; + + // the request queues + CRequestMap m_requests; + }; + Display* m_display; int m_screen; Window m_root; @@ -150,7 +170,11 @@ class CXWindowsScreen { Atom m_atomString; Atom m_atomText; Atom m_atomCompoundText; + Atom m_atomClipboard[kClipboardEnd]; + // clipboard info + ClipboardInfo m_clipboards[kClipboardEnd]; +/* // the contents of our selection CClipboard m_clipboard; @@ -160,6 +184,7 @@ class CXWindowsScreen { // the request queues CRequestMap m_requests; +*/ // X is not thread safe CMutex m_mutex; diff --git a/synergy/ClipboardTypes.h b/synergy/ClipboardTypes.h new file mode 100644 index 00000000..63c44c91 --- /dev/null +++ b/synergy/ClipboardTypes.h @@ -0,0 +1,21 @@ +#ifndef CLIPBOARDTYPES_H +#define CLIPBOARDTYPES_H + +#include "BasicTypes.h" + +// type to hold a clipboard identifier +typedef UInt8 ClipboardID; + +// clipboard identifiers. kClipboardClipboard is what is normally +// considered the clipboard (e.g. the cut/copy/paste menu items +// affect it). kClipboardSelection is the selection on those +// platforms that can treat the selection as a clipboard (e.g. X +// windows). clipboard identifiers must be sequential starting +// at zero. +static const ClipboardID kClipboardClipboard = 0; +static const ClipboardID kClipboardSelection = 1; + +// the number of clipboards (i.e. one greater than the last clipboard id) +static const ClipboardID kClipboardEnd = 2; + +#endif diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 92572e33..a398f226 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -3,6 +3,7 @@ #include "IInterface.h" #include "BasicTypes.h" +#include "ClipboardTypes.h" class CServer; class IClipboard; @@ -49,7 +50,7 @@ class IPrimaryScreen : public IInterface { // set the screen's clipboard contents. this is usually called // soon after an enter(). - virtual void setClipboard(const IClipboard*) = 0; + virtual void setClipboard(ClipboardID, const IClipboard*) = 0; /* // show or hide the screen saver @@ -57,7 +58,7 @@ class IPrimaryScreen : public IInterface { */ // synergy should own the clipboard - virtual void grabClipboard() = 0; + virtual void grabClipboard(ClipboardID) = 0; // accessors @@ -75,7 +76,7 @@ class IPrimaryScreen : public IInterface { virtual SInt32 getJumpZoneSize() const = 0; // get the screen's clipboard contents - virtual void getClipboard(IClipboard*) const = 0; + virtual void getClipboard(ClipboardID, IClipboard*) const = 0; }; #endif diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 2e886fd5..9599d25c 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -3,6 +3,7 @@ #include "IInterface.h" #include "BasicTypes.h" +#include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" @@ -52,7 +53,7 @@ class ISecondaryScreen : public IInterface { // set the screen's clipboard contents. this is usually called // soon after an enter(). - virtual void setClipboard(const IClipboard*) = 0; + virtual void setClipboard(ClipboardID, const IClipboard*) = 0; /* // show or hide the screen saver @@ -60,7 +61,7 @@ class ISecondaryScreen : public IInterface { */ // take ownership of clipboard - virtual void grabClipboard() = 0; + virtual void grabClipboard(ClipboardID) = 0; // accessors @@ -71,7 +72,7 @@ class ISecondaryScreen : public IInterface { virtual SInt32 getJumpZoneSize() const = 0; // get the screen's clipboard contents - virtual void getClipboard(IClipboard*) const = 0; + virtual void getClipboard(ClipboardID, IClipboard*) const = 0; }; #endif diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index ecd396be..49a38898 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -1,6 +1,7 @@ #ifndef ISERVERPROTOCOL_H #define ISERVERPROTOCOL_H +#include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" #include "IInterface.h" @@ -25,9 +26,9 @@ class IServerProtocol : public IInterface { virtual void sendClose() = 0; virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; virtual void sendLeave() = 0; - virtual void sendClipboard(const CString&) = 0; - virtual void sendGrabClipboard() = 0; - virtual void sendQueryClipboard(UInt32 seqNum) = 0; + virtual void sendClipboard(ClipboardID, const CString&) = 0; + virtual void sendGrabClipboard(ClipboardID) = 0; + virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum) = 0; virtual void sendScreenSaver(bool on) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index c806ae3a..0e70502c 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -7,30 +7,101 @@ static const SInt32 kMajorVersion = 0; static const SInt32 kMinorVersion = 1; -// message codes (trailing NUL is not part of code). codes are -// grouped into: -// commands -- request an action, no reply expected -// queries -- request info -// data -- send info -// errors -- notify of error -static const char kMsgCClose[] = "CBYE"; // server -static const char kMsgCEnter[] = "CINN%2i%2i"; // server -static const char kMsgCLeave[] = "COUT"; // server -static const char kMsgCClipboard[] = "CCLP"; // server/client -static const char kMsgCScreenSaver[] = "CSEC%1i"; // server +// +// message codes (trailing NUL is not part of code). in comments, $n +// refers to the n'th argument (counting from one). message codes are +// always 4 bytes optionally followed by message specific parameters. +// -static const char kMsgDKeyDown[] = "DKDN%2i%2i"; // server -static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i"; // server -static const char kMsgDKeyUp[] = "DKUP%2i%2i"; // server -static const char kMsgDMouseDown[] = "DMDN%1i"; // server -static const char kMsgDMouseUp[] = "DMUP%1i"; // server -static const char kMsgDMouseMove[] = "DMMV%2i%2i"; // server -static const char kMsgDMouseWheel[] = "DMWM%2i"; // server -static const char kMsgDClipboard[] = "DCLP%4i%s"; // server/client -static const char kMsgDInfo[] = "DINF%2i%2i%2i"; // client +// +// command codes +// -static const char kMsgQClipboard[] = "QCLP%4i"; // server -static const char kMsgQInfo[] = "QINF"; // server +// close connection; primary -> secondary +static const char kMsgCClose[] = "CBYE"; + +// enter screen: primary -> secondary +// entering screen at screen position $1 = x, $2 = y. x,y are +// absolute screen coordinates. +static const char kMsgCEnter[] = "CINN%2i%2i"; + +// leave screen: primary -> secondary +// leaving screen +static const char kMsgCLeave[] = "COUT"; + +// grab clipboard: primary <-> secondary +// sent by screen when some other app on that screen grabs a +// clipboard. $1 = the clipboard identifier. +static const char kMsgCClipboard[] = "CCLP%1i"; + +// screensaver change: primary -> secondary +// screensaver on primary has started ($1 == 1) or closed ($1 == 0) +static const char kMsgCScreenSaver[] = "CSEC%1i"; + + +// +// data codes +// + +// key pressed: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask +static const char kMsgDKeyDown[] = "DKDN%2i%2i"; + +// key auto-repeat: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats +static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i"; + +// key released: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask +static const char kMsgDKeyUp[] = "DKUP%2i%2i"; + +// mouse button pressed: primary -> secondary +// $1 = ButtonID +static const char kMsgDMouseDown[] = "DMDN%1i"; + +// mouse button released: primary -> secondary +// $1 = ButtonID +static const char kMsgDMouseUp[] = "DMUP%1i"; + +// mouse moved: primary -> secondary +// $1 = x, $2 = y. x,y are absolute screen coordinates. +static const char kMsgDMouseMove[] = "DMMV%2i%2i"; + +// mouse button pressed: primary -> secondary +// $1 = delta +static const char kMsgDMouseWheel[] = "DMWM%2i"; + +// clipboard data: primary <-> secondary +// $2 = sequence number, $3 = clipboard data. the sequence number +// is 0 when sent by the primary. the secondary sends this message +// in response to a kMsgQClipboard and uses the sequence number from +// that message. $1 = clipboard identifier. +static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; + +// client data: seconary -> primary +// $1 = seconary screen width in pixels, $2 = screen height, $3 = +// size of warp zone. +static const char kMsgDInfo[] = "DINF%2i%2i%2i"; + + +// +// query codes +// + +// query clipboard: primary -> secondary +// $2 = sequence number. the sequence number is an arbitrary value +// used by primary to identify the kMsgDClipboard response to a +// query. $1 = clipboard identifier. +static const char kMsgQClipboard[] = "QCLP%1i%4i"; + +// query screen info: primary -> secondary +// client should reply with a kMsgDInfo. +static const char kMsgQInfo[] = "QINF"; + + +// +// error codes +// static const char kMsgEIncompatible[] = "EICV"; From 9ac0473d5a485aa52e73c52c88d842a8c823fa08 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 27 Apr 2002 18:06:40 +0000 Subject: [PATCH 049/807] checkpoint. changed CProtocolUtil::readf() to store 1 and 2 byte integers into pointers to 1 and 2 byte integers. was always assuming pointers to 4 byte integers. --- client/CClient.cpp | 18 +++++++++--------- client/CXWindowsSecondaryScreen.cpp | 26 ++++++++++++++++---------- server/CServer.cpp | 2 +- server/CServerProtocol1_0.cpp | 2 +- server/CXWindowsPrimaryScreen.cpp | 2 +- synergy/CProtocolUtil.cpp | 22 +++++++++++++--------- synergy/CXWindowsScreen.cpp | 1 + synergy/ProtocolTypes.h | 2 +- 8 files changed, 43 insertions(+), 32 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 811ac7ee..5af2031a 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -303,7 +303,7 @@ void CClient::closeSecondaryScreen() void CClient::onEnter() { - SInt32 x, y; + SInt16 x, y; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y); @@ -328,7 +328,7 @@ void CClient::onGrabClipboard() void CClient::onScreenSaver() { - SInt32 on; + SInt8 on; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on); @@ -395,7 +395,7 @@ void CClient::onSetClipboard() void CClient::onKeyDown() { - SInt32 id, mask; + SInt16 id, mask; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); @@ -406,7 +406,7 @@ void CClient::onKeyDown() void CClient::onKeyRepeat() { - SInt32 id, mask, count; + SInt16 id, mask, count; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); @@ -418,7 +418,7 @@ void CClient::onKeyRepeat() void CClient::onKeyUp() { - SInt32 id, mask; + SInt16 id, mask; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); @@ -429,7 +429,7 @@ void CClient::onKeyUp() void CClient::onMouseDown() { - SInt32 id; + SInt8 id; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); @@ -439,7 +439,7 @@ void CClient::onMouseDown() void CClient::onMouseUp() { - SInt32 id; + SInt8 id; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); @@ -449,7 +449,7 @@ void CClient::onMouseUp() void CClient::onMouseMove() { - SInt32 x, y; + SInt16 x, y; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); @@ -459,7 +459,7 @@ void CClient::onMouseMove() void CClient::onMouseWheel() { - SInt32 delta; + SInt16 delta; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index f5fe3461..8e088c8e 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -127,18 +127,24 @@ void CXWindowsSecondaryScreen::open(CClient* client) // open the display openDisplay(); - // verify the availability of the XTest extension - CDisplayLock display(this); - int majorOpcode, firstEvent, firstError; - if (!XQueryExtension(display, XTestExtensionName, + { + // verify the availability of the XTest extension + CDisplayLock display(this); + int majorOpcode, firstEvent, firstError; + if (!XQueryExtension(display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) - throw int(6); // FIXME -- make exception for this + throw int(6); // FIXME -- make exception for this - // update key state - updateKeys(display); - updateKeycodeMap(display); - updateModifierMap(display); - updateModifiers(display); + // update key state + updateKeys(display); + updateKeycodeMap(display); + updateModifierMap(display); + updateModifiers(display); + } + + // assume primary has all clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) + grabClipboard(id); } void CXWindowsSecondaryScreen::close() diff --git a/server/CServer.cpp b/server/CServer.cpp index 84617aa4..f5080edc 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -320,7 +320,7 @@ void CServer::onMouseUp(ButtonID id) bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) { - log((CLOG_DEBUG "onMouseMovePrimary %d,%d", x, y)); +// log((CLOG_DEBUG "onMouseMovePrimary %d,%d", x, y)); // mouse move on primary (server's) screen assert(m_active != NULL); diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 5a97c3e5..c405063b 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -181,7 +181,7 @@ void CServerProtocol1_0::sendMouseWheel( void CServerProtocol1_0::recvInfo() { // parse the message - SInt32 w, h, zoneInfo; + SInt16 w, h, zoneInfo; CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, &w, &h, &zoneInfo); log((CLOG_INFO "received client \"%s\" info size=%dx%d, zone=%d", getClient().c_str(), w, h, zoneInfo)); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index f1315116..3ee1d65f 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -95,7 +95,7 @@ void CXWindowsPrimaryScreen::run() } case MotionNotify: { - log((CLOG_DEBUG "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); +// log((CLOG_DEBUG "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); SInt32 x, y; if (!m_active) { x = xevent.xmotion.x_root; diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index c15cfb2a..5aca0e09 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -73,28 +73,32 @@ void CProtocolUtil::readf(IInputStream* stream, read(stream, buffer, len); // convert it - UInt32* v = va_arg(args, UInt32*); + void* v = va_arg(args, void*); switch (len) { case 1: // 1 byte integer - *v = static_cast(buffer[0]); + *reinterpret_cast(v) = buffer[0]; + log((CLOG_DEBUG "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; case 2: // 2 byte integer - *v = (static_cast(buffer[0]) << 8) | - static_cast(buffer[1]); + *reinterpret_cast(v) = + (static_cast(buffer[0]) << 8) | + static_cast(buffer[1]); + log((CLOG_DEBUG "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; case 4: // 4 byte integer - *v = (static_cast(buffer[0]) << 24) | - (static_cast(buffer[1]) << 16) | - (static_cast(buffer[2]) << 8) | - static_cast(buffer[3]); + *reinterpret_cast(v) = + (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); + log((CLOG_DEBUG "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; } - log((CLOG_DEBUG "readf: read %d byte integer: %d (0x%x)", len, *v, *v)); break; } diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 4aa74a41..ee9f1492 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -352,6 +352,7 @@ bool CXWindowsScreen::getDisplayClipboard( // events because X stupidly doesn't provide a mask for selection // events, so we use a predicate to find our event. XEvent xevent; +// FIXME -- must limit the time we wait for bad clients while (XCheckIfEvent(m_display, &xevent, &CXWindowsScreen::findSelectionNotify, (XPointer)&requestor) != True) { diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index 0e70502c..ca54ff04 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -78,7 +78,7 @@ static const char kMsgDMouseWheel[] = "DMWM%2i"; // that message. $1 = clipboard identifier. static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; -// client data: seconary -> primary +// client data: secondary -> primary // $1 = seconary screen width in pixels, $2 = screen height, $3 = // size of warp zone. static const char kMsgDInfo[] = "DINF%2i%2i%2i"; From d1ca5295d13a658f1d23c85b2a0515cc8cc47f16 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 27 Apr 2002 18:49:03 +0000 Subject: [PATCH 050/807] Added more debug levels and moved some annoying debug messages to those levels. Default log level is now DEBUG for debug builds and INFO for release builds. --- base/CLog.cpp | 8 ++- base/CLog.h | 2 + client/CClient.cpp | 26 +++++----- mt/CThread.cpp | 2 +- mt/CThreadRep.cpp | 14 ++--- mt/CTimerThread.cpp | 8 +-- net/CNetwork.cpp | 8 +-- server/CServer.cpp | 86 +++++++++++++++---------------- server/CServerProtocol1_0.cpp | 38 +++++++------- server/CXWindowsPrimaryScreen.cpp | 20 +++---- synergy/CProtocolUtil.cpp | 18 +++---- synergy/CXWindowsScreen.cpp | 28 +++++----- 12 files changed, 133 insertions(+), 125 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index f8fd3479..8d11d44c 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -18,6 +18,8 @@ static const char* g_priority[] = { "NOTE", "INFO", "DEBUG", + "DEBUG1", + "DEBUG2" }; static const int g_numPriority = (int)(sizeof(g_priority) / sizeof(g_priority[0])); @@ -108,7 +110,11 @@ void CLog::output(int priority, char* msg) assert(msg != 0); if (g_maxPriority == -1) { - g_maxPriority = g_numPriority - 1; +#if defined(NDEBUG) + g_maxPriority = 4; +#else + g_maxPriority = 5; +#endif const char* priEnv = getenv("SYN_LOG_PRI"); if (priEnv != NULL) { for (int i = 0; i < g_numPriority; ++i) diff --git a/base/CLog.h b/base/CLog.h index 908072e9..2775df16 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -40,5 +40,7 @@ class CLog { #define CLOG_NOTE CLOG_TRACE "%z\063" #define CLOG_INFO CLOG_TRACE "%z\064" #define CLOG_DEBUG CLOG_TRACE "%z\065" +#define CLOG_DEBUG1 CLOG_TRACE "%z\066" +#define CLOG_DEBUG2 CLOG_TRACE "%z\067" #endif diff --git a/client/CClient.cpp b/client/CClient.cpp index 5af2031a..69738f02 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -58,7 +58,7 @@ void CClient::run(const CNetworkAddress& serverAddress) m_screen->run(); // clean up - log((CLOG_DEBUG "stopping client")); + log((CLOG_NOTE "stopping client")); thread->cancel(); thread->wait(); delete thread; @@ -109,7 +109,7 @@ void CClient::runSession(void*) CTimerThread timer(30.0); // FIXME -- timeout in member // create socket and attempt to connect to server - log((CLOG_DEBUG "connecting to server")); + log((CLOG_DEBUG1 "connecting to server")); assign(socket, new CTCPSocket(), ISocket); // FIXME -- use factory socket->connect(*m_serverAddress); log((CLOG_INFO "connected to server")); @@ -135,19 +135,19 @@ void CClient::runSession(void*) assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); // wait for hello from server - log((CLOG_DEBUG "wait for hello")); - SInt32 major, minor; + log((CLOG_DEBUG1 "wait for hello")); + SInt16 major, minor; CProtocolUtil::readf(input.get(), "Synergy%2i%2i", &major, &minor); // check versions - log((CLOG_DEBUG "got hello version %d.%d", major, minor)); + log((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); if (major < kMajorVersion || (major == kMajorVersion && minor < kMinorVersion)) { throw XIncompatibleClient(major, minor); } // say hello back - log((CLOG_DEBUG "say hello version %d.%d", kMajorVersion, kMinorVersion)); + log((CLOG_DEBUG1 "say hello version %d.%d", kMajorVersion, kMinorVersion)); CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s", kMajorVersion, kMinorVersion, &m_name); @@ -176,7 +176,7 @@ void CClient::runSession(void*) // handle messages from server for (;;) { // wait for reply - log((CLOG_DEBUG "waiting for message")); + log((CLOG_DEBUG1 "waiting for message")); UInt8 code[4]; UInt32 n = input->read(code, 4); @@ -193,7 +193,7 @@ void CClient::runSession(void*) } // parse message - log((CLOG_DEBUG "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + log((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); if (memcmp(code, kMsgDMouseMove, 4) == 0) { onMouseMove(); } @@ -272,13 +272,13 @@ void CClient::openSecondaryScreen() assert(m_screen == NULL); // open screen - log((CLOG_DEBUG "creating secondary screen")); + log((CLOG_DEBUG1 "creating secondary screen")); #if defined(CONFIG_PLATFORM_WIN32) m_screen = new CMSWindowsSecondaryScreen; #elif defined(CONFIG_PLATFORM_UNIX) m_screen = new CXWindowsSecondaryScreen; #endif - log((CLOG_DEBUG "opening secondary screen")); + log((CLOG_DEBUG1 "opening secondary screen")); m_screen->open(this); } @@ -288,7 +288,7 @@ void CClient::closeSecondaryScreen() // close the secondary screen try { - log((CLOG_DEBUG "closing secondary screen")); + log((CLOG_DEBUG1 "closing secondary screen")); m_screen->close(); } catch (...) { @@ -296,7 +296,7 @@ void CClient::closeSecondaryScreen() } // clean up - log((CLOG_DEBUG "destroying secondary screen")); + log((CLOG_DEBUG1 "destroying secondary screen")); delete m_screen; m_screen = NULL; } @@ -342,7 +342,7 @@ void CClient::onQueryInfo() m_screen->getSize(&w, &h); SInt32 zoneSize = m_screen->getJumpZoneSize(); - log((CLOG_DEBUG "sending info size=%d,%d zone=%d", w, h, zoneSize)); + log((CLOG_DEBUG1 "sending info size=%d,%d zone=%d", w, h, zoneSize)); CLock lock(&m_mutex); CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); } diff --git a/mt/CThread.cpp b/mt/CThread.cpp index c9a07fa4..9df33fb9 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -57,7 +57,7 @@ void CThread::sleep(double timeout) void CThread::exit(void* result) { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - log((CLOG_DEBUG "throw exit on thread %p", currentRep.operator->())); + log((CLOG_DEBUG1 "throw exit on thread %p", currentRep.operator->())); throw XThreadExit(result); } diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 313b33b4..e40df753 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -264,16 +264,16 @@ void CThreadRep::doThreadFunc() catch (XThreadCancel&) { // client called cancel() - log((CLOG_DEBUG "caught cancel on thread %p", this)); + log((CLOG_DEBUG1 "caught cancel on thread %p", this)); } catch (XThreadExit& e) { // client called exit() result = e.m_result; - log((CLOG_DEBUG "caught exit on thread %p", this)); + log((CLOG_DEBUG1 "caught exit on thread %p", this)); } catch (...) { - log((CLOG_DEBUG "exception on thread %p", this)); + log((CLOG_DEBUG1 "exception on thread %p", this)); // note -- don't catch (...) to avoid masking bugs delete m_job; throw; @@ -331,7 +331,7 @@ void CThreadRep::cancel() } // break out of system calls - log((CLOG_DEBUG "cancel thread %p", this)); + log((CLOG_DEBUG1 "cancel thread %p", this)); pthread_kill(m_thread, SIGWAKEUP); } @@ -349,7 +349,7 @@ void CThreadRep::testCancel() } // start cancel - log((CLOG_DEBUG "throw cancel on thread %p", this)); + log((CLOG_DEBUG1 "throw cancel on thread %p", this)); throw XThreadCancel(); } @@ -459,7 +459,7 @@ void CThreadRep::sleep(double timeout) void CThreadRep::cancel() { - log((CLOG_DEBUG "cancel thread %p", this)); + log((CLOG_DEBUG1 "cancel thread %p", this)); SetEvent(m_cancel); } @@ -482,7 +482,7 @@ void CThreadRep::testCancel() } // start cancel - log((CLOG_DEBUG "throw cancel on thread %p", this)); + log((CLOG_DEBUG1 "throw cancel on thread %p", this)); throw XThreadCancel(); } diff --git a/mt/CTimerThread.cpp b/mt/CTimerThread.cpp index 3d149c45..023cb06b 100644 --- a/mt/CTimerThread.cpp +++ b/mt/CTimerThread.cpp @@ -18,19 +18,19 @@ CTimerThread::CTimerThread(double timeout) : m_timeout(timeout) CTimerThread::~CTimerThread() { - log((CLOG_DEBUG "cancelling timeout")); + log((CLOG_DEBUG1 "cancelling timeout")); m_timingThread->cancel(); m_timingThread->wait(); - log((CLOG_DEBUG "cancelled timeout")); + log((CLOG_DEBUG1 "cancelled timeout")); delete m_timingThread; delete m_callingThread; } void CTimerThread::timer(void*) { - log((CLOG_DEBUG "timeout in %f seconds", m_timeout)); + log((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); CThread::sleep(m_timeout); - log((CLOG_DEBUG "timeout")); + log((CLOG_DEBUG1 "timeout")); m_callingThread->cancel(); } diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp index 7093ce3e..6bbf7511 100644 --- a/net/CNetwork.cpp +++ b/net/CNetwork.cpp @@ -71,7 +71,7 @@ void CNetwork::init() // try winsock 2 HMODULE module = (HMODULE)::LoadLibrary("ws2_32.dll"); if (module == NULL) { - log((CLOG_DEBUG "ws2_32.dll not found")); + log((CLOG_NOTE "ws2_32.dll not found")); } else { try { @@ -79,14 +79,14 @@ void CNetwork::init() return; } catch (XNetwork& e) { - log((CLOG_DEBUG "ws2_32.dll error: %s", e.what())); + log((CLOG_NOTE "ws2_32.dll error: %s", e.what())); } } // try winsock 1 module = (HMODULE)::LoadLibrary("wsock32.dll"); if (module == NULL) { - log((CLOG_DEBUG "wsock32.dll not found")); + log((CLOG_NOTE "wsock32.dll not found")); } else { try { @@ -94,7 +94,7 @@ void CNetwork::init() return; } catch (XNetwork& e) { - log((CLOG_DEBUG "wsock32.dll error: %s", e.what())); + log((CLOG_NOTE "wsock32.dll error: %s", e.what())); } } diff --git a/server/CServer.cpp b/server/CServer.cpp index f5080edc..94c2806b 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -73,7 +73,7 @@ void CServer::run() m_primary->run(); // clean up - log((CLOG_DEBUG "stopping server")); + log((CLOG_NOTE "stopping server")); cleanupThreads(); closePrimaryScreen(); } @@ -248,7 +248,7 @@ bool CServer::onCommandKey(KeyID /*id*/, void CServer::onKeyDown(KeyID id, KeyModifierMask mask) { - log((CLOG_DEBUG "onKeyDown id=%d mask=0x%04x", id, mask)); + log((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x", id, mask)); assert(m_active != NULL); // handle command keys @@ -264,7 +264,7 @@ void CServer::onKeyDown(KeyID id, KeyModifierMask mask) void CServer::onKeyUp(KeyID id, KeyModifierMask mask) { - log((CLOG_DEBUG "onKeyUp id=%d mask=0x%04x", id, mask)); + log((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x", id, mask)); assert(m_active != NULL); // handle command keys @@ -281,7 +281,7 @@ void CServer::onKeyUp(KeyID id, KeyModifierMask mask) void CServer::onKeyRepeat( KeyID id, KeyModifierMask mask, SInt32 count) { - log((CLOG_DEBUG "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); + log((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); assert(m_active != NULL); // handle command keys @@ -298,7 +298,7 @@ void CServer::onKeyRepeat( void CServer::onMouseDown(ButtonID id) { - log((CLOG_DEBUG "onMouseDown id=%d", id)); + log((CLOG_DEBUG1 "onMouseDown id=%d", id)); assert(m_active != NULL); // relay @@ -309,7 +309,7 @@ void CServer::onMouseDown(ButtonID id) void CServer::onMouseUp(ButtonID id) { - log((CLOG_DEBUG "onMouseUp id=%d", id)); + log((CLOG_DEBUG1 "onMouseUp id=%d", id)); assert(m_active != NULL); // relay @@ -320,7 +320,7 @@ void CServer::onMouseUp(ButtonID id) bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) { -// log((CLOG_DEBUG "onMouseMovePrimary %d,%d", x, y)); + log((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); // mouse move on primary (server's) screen assert(m_active != NULL); @@ -336,22 +336,22 @@ bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) if (x < m_active->m_zoneSize) { x -= m_active->m_zoneSize; dir = CScreenMap::kLeft; - log((CLOG_DEBUG "switch to left")); + log((CLOG_DEBUG1 "switch to left")); } else if (x >= m_active->m_width - m_active->m_zoneSize) { x += m_active->m_zoneSize; dir = CScreenMap::kRight; - log((CLOG_DEBUG "switch to right")); + log((CLOG_DEBUG1 "switch to right")); } else if (y < m_active->m_zoneSize) { y -= m_active->m_zoneSize; dir = CScreenMap::kTop; - log((CLOG_DEBUG "switch to top")); + log((CLOG_DEBUG1 "switch to top")); } else if (y >= m_active->m_height - m_active->m_zoneSize) { y += m_active->m_zoneSize; dir = CScreenMap::kBottom; - log((CLOG_DEBUG "switch to bottom")); + log((CLOG_DEBUG1 "switch to bottom")); } else { // still on local screen @@ -376,7 +376,7 @@ bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) { - log((CLOG_DEBUG "onMouseMoveSecondary %+d,%+d", dx, dy)); + log((CLOG_DEBUG1 "onMouseMoveSecondary %+d,%+d", dx, dy)); // mouse move on secondary (client's) screen assert(m_active != NULL); @@ -409,7 +409,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) // get neighbor if we should switch if (newScreen == NULL) { - log((CLOG_DEBUG "leave \"%s\" on %s", m_active->m_name.c_str(), CScreenMap::dirName(dir))); + log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->m_name.c_str(), CScreenMap::dirName(dir))); SInt32 x = m_x, y = m_y; newScreen = getNeighbor(m_active, dir, x, y); @@ -421,7 +421,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) m_y = y; } else { - log((CLOG_DEBUG "no neighbor; clamping")); + log((CLOG_DEBUG1 "no neighbor; clamping")); if (m_x < 0) m_x = 0; else if (m_x > m_active->m_width - 1) @@ -435,7 +435,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) } else { // clamp to edge when locked - log((CLOG_DEBUG "clamp to \"%s\"", m_active->m_name.c_str())); + log((CLOG_DEBUG1 "clamp to \"%s\"", m_active->m_name.c_str())); if (m_x < 0) m_x = 0; else if (m_x > m_active->m_width - 1) @@ -450,7 +450,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) if (newScreen == NULL || newScreen == m_active) { // do nothing if mouse didn't move if (m_x != xOld || m_y != yOld) { - log((CLOG_DEBUG "move on %s to %d,%d", m_active->m_name.c_str(), m_x, m_y)); + log((CLOG_DEBUG1 "move on %s to %d,%d", m_active->m_name.c_str(), m_x, m_y)); m_active->m_protocol->sendMouseMove(m_x, m_y); } } @@ -463,7 +463,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) void CServer::onMouseWheel(SInt32 delta) { - log((CLOG_DEBUG "onMouseWheel %+d", delta)); + log((CLOG_DEBUG1 "onMouseWheel %+d", delta)); assert(m_active != NULL); // relay @@ -547,14 +547,14 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, CString srcName = src->m_name; assert(!srcName.empty()); - log((CLOG_DEBUG "find neighbor on %s of \"%s\"", CScreenMap::dirName(dir), srcName.c_str())); + log((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CScreenMap::dirName(dir), srcName.c_str())); for (;;) { // look up name of neighbor const CString dstName(m_screenMap.getNeighbor(srcName, dir)); // if nothing in that direction then return NULL if (dstName.empty()) { - log((CLOG_DEBUG "no neighbor on %s of \"%s\"", CScreenMap::dirName(dir), srcName.c_str())); + log((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CScreenMap::dirName(dir), srcName.c_str())); return NULL; } @@ -563,11 +563,11 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, // screen. CScreenList::const_iterator index = m_screens.find(dstName); if (index != m_screens.end()) { - log((CLOG_DEBUG "\"%s\" is on %s of \"%s\"", dstName.c_str(), CScreenMap::dirName(dir), srcName.c_str())); + log((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CScreenMap::dirName(dir), srcName.c_str())); return index->second; } - log((CLOG_DEBUG "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CScreenMap::dirName(dir), srcName.c_str())); + log((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CScreenMap::dirName(dir), srcName.c_str())); srcName = dstName; } } @@ -596,7 +596,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, if (x >= 0) { break; } - log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } break; @@ -610,7 +610,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, if (x < w) { break; } - log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } break; @@ -624,7 +624,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, if (y >= 0) { break; } - log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } break; @@ -638,7 +638,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, if (y < h) { break; } - log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } break; @@ -725,7 +725,7 @@ void CServer::mapPosition(CScreenInfo* src, #include "CTCPListenSocket.h" void CServer::acceptClients(void*) { - log((CLOG_DEBUG "starting to wait for clients")); + log((CLOG_DEBUG1 "starting to wait for clients")); // add this thread to the list of threads to cancel. remove from // list in d'tor. @@ -743,25 +743,25 @@ void CServer::acceptClients(void*) CNetworkAddress addr(50001 /* FIXME -- m_port */); for (;;) { try { - log((CLOG_DEBUG "binding listen socket")); + log((CLOG_DEBUG1 "binding listen socket")); listen->bind(addr); break; } catch (XSocketAddressInUse&) { // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { - log((CLOG_DEBUG "waited too long to bind, giving up")); + log((CLOG_DEBUG1 "waited too long to bind, giving up")); throw; } // wait a bit before retrying - log((CLOG_DEBUG "bind failed; waiting to retry")); + log((CLOG_DEBUG1 "bind failed; waiting to retry")); CThread::sleep(5.0); } } // accept connections and begin processing them - log((CLOG_DEBUG "waiting for client connections")); + log((CLOG_DEBUG1 "waiting for client connections")); for (;;) { // accept connection CThread::testCancel(); @@ -782,7 +782,7 @@ void CServer::acceptClients(void*) void CServer::handshakeClient(void* vsocket) { - log((CLOG_DEBUG "negotiating with new client")); + log((CLOG_DEBUG1 "negotiating with new client")); // get the socket pointer from the argument assert(vsocket != NULL); @@ -826,22 +826,22 @@ void CServer::handshakeClient(void* vsocket) static const UInt32 maxHelloLen = 1024; // say hello - log((CLOG_DEBUG "saying hello")); + log((CLOG_DEBUG1 "saying hello")); CProtocolUtil::writef(output.get(), "Synergy%2i%2i", kMajorVersion, kMinorVersion); output->flush(); // wait for the reply - log((CLOG_DEBUG "waiting for hello reply")); + log((CLOG_DEBUG1 "waiting for hello reply")); UInt32 n = input->getSize(); if (n > maxHelloLen) { throw XBadClient(); } // get and parse the reply to hello - SInt32 major, minor; + SInt16 major, minor; try { - log((CLOG_DEBUG "parsing hello reply")); + log((CLOG_DEBUG1 "parsing hello reply")); CProtocolUtil::readf(input.get(), "Synergy%2i%2i%s", &major, &minor, &name); } @@ -853,7 +853,7 @@ void CServer::handshakeClient(void* vsocket) } // create a protocol interpreter for the version - log((CLOG_DEBUG "creating interpreter for client %s version %d.%d", name.c_str(), major, minor)); + log((CLOG_DEBUG1 "creating interpreter for client %s version %d.%d", name.c_str(), major, minor)); assign(protocol, CServerProtocol::create(major, minor, this, name, input.get(), output.get()), IServerProtocol); @@ -863,7 +863,7 @@ void CServer::handshakeClient(void* vsocket) name, protocol.get()), CConnectionNote); // ask and wait for the client's info - log((CLOG_DEBUG "waiting for info for client %s", name.c_str())); + log((CLOG_DEBUG1 "waiting for info for client %s", name.c_str())); protocol->queryInfo(); } @@ -905,13 +905,13 @@ void CServer::openPrimaryScreen() assert(m_primary == NULL); // open screen - log((CLOG_DEBUG "creating primary screen")); + log((CLOG_DEBUG1 "creating primary screen")); #if defined(CONFIG_PLATFORM_WIN32) m_primary = new CMSWindowsPrimaryScreen; #elif defined(CONFIG_PLATFORM_UNIX) m_primary = new CXWindowsPrimaryScreen; #endif - log((CLOG_DEBUG "opening primary screen")); + log((CLOG_DEBUG1 "opening primary screen")); m_primary->open(this); // add connection @@ -944,7 +944,7 @@ void CServer::closePrimaryScreen() // close the primary screen try { - log((CLOG_DEBUG "closing primary screen")); + log((CLOG_DEBUG1 "closing primary screen")); m_primary->close(); } catch (...) { @@ -952,7 +952,7 @@ void CServer::closePrimaryScreen() } // clean up - log((CLOG_DEBUG "destroying primary screen")); + log((CLOG_DEBUG1 "destroying primary screen")); delete m_primary; m_primary = NULL; } @@ -979,7 +979,7 @@ void CServer::removeCleanupThread(const CThread& thread) void CServer::cleanupThreads() { - log((CLOG_DEBUG "cleaning up threads")); + log((CLOG_DEBUG1 "cleaning up threads")); m_mutex.lock(); while (m_cleanupList.begin() != m_cleanupList.end()) { // get the next thread and cancel it @@ -995,7 +995,7 @@ void CServer::cleanupThreads() // FIXME -- delete remaining threads from list m_mutex.unlock(); - log((CLOG_DEBUG "cleaned up threads")); + log((CLOG_DEBUG1 "cleaned up threads")); } CServer::CScreenInfo* CServer::addConnection( diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index c405063b..02f27a71 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -46,7 +46,7 @@ void CServerProtocol1_0::run() } // parse message - log((CLOG_DEBUG "msg from \"%s\": %c%c%c%c", getClient().c_str(), code[0], code[1], code[2], code[3])); + log((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getClient().c_str(), code[0], code[1], code[2], code[3])); if (memcmp(code, kMsgDInfo, 4) == 0) { recvInfo(); } @@ -68,7 +68,7 @@ void CServerProtocol1_0::run() void CServerProtocol1_0::queryInfo() { - log((CLOG_INFO "querying client \"%s\" info", getClient().c_str())); + log((CLOG_DEBUG1 "querying client \"%s\" info", getClient().c_str())); // send request CProtocolUtil::writef(getOutputStream(), kMsgQInfo); @@ -86,95 +86,95 @@ void CServerProtocol1_0::queryInfo() void CServerProtocol1_0::sendClose() { - log((CLOG_INFO "send close to \"%s\"", getClient().c_str())); + log((CLOG_DEBUG1 "send close to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClose); } void CServerProtocol1_0::sendEnter( SInt32 xAbs, SInt32 yAbs) { - log((CLOG_INFO "send enter to \"%s\", %d,%d", getClient().c_str(), xAbs, yAbs)); + log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d", getClient().c_str(), xAbs, yAbs)); CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs); } void CServerProtocol1_0::sendLeave() { - log((CLOG_INFO "send leave to \"%s\"", getClient().c_str())); + log((CLOG_DEBUG1 "send leave to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCLeave); } void CServerProtocol1_0::sendClipboard( ClipboardID id, const CString& data) { - log((CLOG_INFO "send clipboard %d to \"%s\" size=%d", id, getClient().c_str(), data.size())); + log((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getClient().c_str(), data.size())); CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); } void CServerProtocol1_0::sendGrabClipboard(ClipboardID id) { - log((CLOG_INFO "send grab clipboard %d to \"%s\"", id, getClient().c_str())); + log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id); } void CServerProtocol1_0::sendQueryClipboard( ClipboardID id, UInt32 seqNum) { - log((CLOG_INFO "query clipboard %d to \"%s\"", id, getClient().c_str())); + log((CLOG_DEBUG "query clipboard %d to \"%s\"", id, getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgQClipboard, id, seqNum); } void CServerProtocol1_0::sendScreenSaver(bool on) { - log((CLOG_INFO "send screen saver to \"%s\"", getClient().c_str())); + log((CLOG_DEBUG1 "send screen saver to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } void CServerProtocol1_0::sendKeyDown( KeyID key, KeyModifierMask mask) { - log((CLOG_INFO "send key down to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); + log((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); } void CServerProtocol1_0::sendKeyRepeat( KeyID key, KeyModifierMask mask, SInt32 count) { - log((CLOG_INFO "send key repeat to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); + log((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); } void CServerProtocol1_0::sendKeyUp( KeyID key, KeyModifierMask mask) { - log((CLOG_INFO "send key up to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); + log((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); } void CServerProtocol1_0::sendMouseDown( ButtonID button) { - log((CLOG_INFO "send mouse down to \"%s\" id=%d", getClient().c_str(), button)); + log((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getClient().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); } void CServerProtocol1_0::sendMouseUp( ButtonID button) { - log((CLOG_INFO "send mouse up to \"%s\" id=%d", getClient().c_str(), button)); + log((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getClient().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); } void CServerProtocol1_0::sendMouseMove( SInt32 xAbs, SInt32 yAbs) { - log((CLOG_INFO "send mouse move to \"%s\" %d,%d", getClient().c_str(), xAbs, yAbs)); + log((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getClient().c_str(), xAbs, yAbs)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); } void CServerProtocol1_0::sendMouseWheel( SInt32 delta) { - log((CLOG_INFO "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta)); + log((CLOG_DEBUG1 "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); } @@ -183,7 +183,7 @@ void CServerProtocol1_0::recvInfo() // parse the message SInt16 w, h, zoneInfo; CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, &w, &h, &zoneInfo); - log((CLOG_INFO "received client \"%s\" info size=%dx%d, zone=%d", getClient().c_str(), w, h, zoneInfo)); + log((CLOG_DEBUG "received client \"%s\" info size=%dx%d, zone=%d", getClient().c_str(), w, h, zoneInfo)); // validate if (w == 0 || h == 0) { @@ -200,7 +200,7 @@ void CServerProtocol1_0::recvClipboard() UInt32 seqNum; CString data; CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); - log((CLOG_INFO "received client \"%s\" clipboard %d seqnum=%d, size=%d", getClient().c_str(), id, seqNum, data.size())); + log((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getClient().c_str(), id, seqNum, data.size())); getServer()->setClipboard(id, seqNum, data); } @@ -208,6 +208,6 @@ void CServerProtocol1_0::recvGrabClipboard() { ClipboardID id; CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id); - log((CLOG_INFO "received client \"%s\" grabbed clipboard %d", getClient().c_str(), id)); + log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d", getClient().c_str(), id)); getServer()->grabClipboard(id, getClient()); } diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 3ee1d65f..3935dc66 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -51,7 +51,7 @@ void CXWindowsPrimaryScreen::run() } case KeyPress: { - log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { @@ -65,7 +65,7 @@ void CXWindowsPrimaryScreen::run() // FIXME -- simulate key repeat. X sends press/release for // repeat. must detect auto repeat and use kKeyRepeat. case KeyRelease: { - log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { @@ -77,7 +77,7 @@ void CXWindowsPrimaryScreen::run() } case ButtonPress: { - log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button)); + log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { m_server->onMouseDown(button); @@ -86,7 +86,7 @@ void CXWindowsPrimaryScreen::run() } case ButtonRelease: { - log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button)); + log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { m_server->onMouseUp(button); @@ -95,7 +95,7 @@ void CXWindowsPrimaryScreen::run() } case MotionNotify: { -// log((CLOG_DEBUG "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); + log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); SInt32 x, y; if (!m_active) { x = xevent.xmotion.x_root; @@ -270,11 +270,11 @@ void CXWindowsPrimaryScreen::leave() m_window, None, CurrentTime); assert(result != GrabNotViewable); if (result != GrabSuccess) { - log((CLOG_DEBUG "waiting to grab pointer")); + log((CLOG_DEBUG2 "waiting to grab pointer")); CThread::sleep(0.1); } } while (result != GrabSuccess); - log((CLOG_DEBUG "grabbed pointer")); + log((CLOG_DEBUG2 "grabbed pointer")); // now the keyboard result = XGrabKeyboard(display, m_window, True, @@ -283,11 +283,11 @@ void CXWindowsPrimaryScreen::leave() if (result != GrabSuccess) { // back off to avoid grab deadlock XUngrabPointer(display, CurrentTime); - log((CLOG_DEBUG "ungrabbed pointer, waiting to grab keyboard")); + log((CLOG_DEBUG2 "ungrabbed pointer, waiting to grab keyboard")); CThread::sleep(0.1); } } while (result != GrabSuccess); - log((CLOG_DEBUG "grabbed keyboard")); + log((CLOG_DEBUG1 "grabbed pointer and keyboard")); // move the mouse to the center of grab window SInt32 w, h; @@ -313,7 +313,7 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( // warp the mouse XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); XSync(display, False); - log((CLOG_DEBUG "warped to %d,%d", x, y)); + log((CLOG_DEBUG1 "warped to %d,%d", x, y)); // discard mouse events since we just added one we don't want XEvent xevent; diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index 5aca0e09..aa0c41b4 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -15,7 +15,7 @@ void CProtocolUtil::writef(IOutputStream* stream, { assert(stream != NULL); assert(fmt != NULL); - log((CLOG_DEBUG "writef(%s)", fmt)); + log((CLOG_DEBUG2 "writef(%s)", fmt)); va_list args; @@ -39,7 +39,7 @@ void CProtocolUtil::writef(IOutputStream* stream, UInt8* scan = buffer; while (count > 0) { const UInt32 n = stream->write(scan, count); - log((CLOG_DEBUG "wrote %d of %d bytes", n, count)); + log((CLOG_DEBUG2 "wrote %d of %d bytes", n, count)); count -= n; scan += n; } @@ -52,7 +52,7 @@ void CProtocolUtil::readf(IInputStream* stream, { assert(stream != NULL); assert(fmt != NULL); - log((CLOG_DEBUG "readf(%s)", fmt)); + log((CLOG_DEBUG2 "readf(%s)", fmt)); va_list args; va_start(args, fmt); @@ -78,7 +78,7 @@ void CProtocolUtil::readf(IInputStream* stream, case 1: // 1 byte integer *reinterpret_cast(v) = buffer[0]; - log((CLOG_DEBUG "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + log((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; case 2: @@ -86,7 +86,7 @@ void CProtocolUtil::readf(IInputStream* stream, *reinterpret_cast(v) = (static_cast(buffer[0]) << 8) | static_cast(buffer[1]); - log((CLOG_DEBUG "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + log((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; case 4: @@ -96,7 +96,7 @@ void CProtocolUtil::readf(IInputStream* stream, (static_cast(buffer[1]) << 16) | (static_cast(buffer[2]) << 8) | static_cast(buffer[3]); - log((CLOG_DEBUG "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + log((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; } break; @@ -135,7 +135,7 @@ void CProtocolUtil::readf(IInputStream* stream, } throw; } - log((CLOG_DEBUG "readf: read %d byte string: %.*s", len, len, sBuffer)); + log((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer)); // save the data CString* dst = va_arg(args, CString*); @@ -166,7 +166,7 @@ void CProtocolUtil::readf(IInputStream* stream, // verify match if (buffer[0] != *fmt) { - log((CLOG_DEBUG "readf: format mismatch: %c vs %c", *fmt, buffer[0])); + log((CLOG_DEBUG2 "readf: format mismatch: %c vs %c", *fmt, buffer[0])); throw XIOReadMismatch(); } @@ -351,7 +351,7 @@ void CProtocolUtil::read(IInputStream* stream, // bail if stream has hungup if (n == 0) { - log((CLOG_DEBUG "unexpected disconnect in readf(), %d bytes left", count)); + log((CLOG_DEBUG2 "unexpected disconnect in readf(), %d bytes left", count)); throw XIOEndOfStream(); } diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index ee9f1492..fe1ae05e 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -274,11 +274,11 @@ void CXWindowsScreen::getDisplayClipboard( log((CLOG_INFO "getting selection with %d targets", numTargets)); for (SInt32 i = 0; i < numTargets; ++i) { Atom format = targetAtoms[i]; - log((CLOG_DEBUG " source target %d", format)); + log((CLOG_DEBUG1 " source target %d", format)); // skip already handled targets if (targets.count(format) > 0) { - log((CLOG_DEBUG " skipping handled target %d", format)); + log((CLOG_DEBUG1 " skipping handled target %d", format)); continue; } @@ -291,29 +291,29 @@ void CXWindowsScreen::getDisplayClipboard( // if we can use the format and we haven't already retrieved // it then get it if (expectedFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG " no format for target", format)); + log((CLOG_DEBUG1 " no format for target", format)); continue; } if (clipboardFormats.count(expectedFormat) > 0) { - log((CLOG_DEBUG " skipping handled format %d", expectedFormat)); + log((CLOG_DEBUG1 " skipping handled format %d", expectedFormat)); continue; } CString data; if (!getDisplayClipboard(selection, format, requestor, timestamp, &format, &data)) { - log((CLOG_DEBUG " no data for target", format)); + log((CLOG_DEBUG1 " no data for target", format)); continue; } // use the actual format, not the expected IClipboard::EFormat actualFormat = getFormat(format); if (actualFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG " no format for target", format)); + log((CLOG_DEBUG1 " no format for target", format)); continue; } if (clipboardFormats.count(actualFormat) > 0) { - log((CLOG_DEBUG " skipping handled format %d", actualFormat)); + log((CLOG_DEBUG1 " skipping handled format %d", actualFormat)); continue; } @@ -326,7 +326,7 @@ void CXWindowsScreen::getDisplayClipboard( else { // non-ICCCM conforming selection owner. try TEXT format. // FIXME - log((CLOG_DEBUG "selection doesn't support TARGETS, format is %d", format)); + log((CLOG_DEBUG1 "selection doesn't support TARGETS, format is %d", format)); } // done with clipboard @@ -386,7 +386,7 @@ bool CXWindowsScreen::getDisplayClipboard( // handle INCR type specially. it means we'll be receiving the data // piecemeal so we just loop until we've collected all the data. if (*outputType == m_atomINCR) { - log((CLOG_DEBUG "selection data for format %d is incremental", type)); + log((CLOG_DEBUG1 "selection data for format %d is incremental", type)); // the data is a lower bound on the amount of data to be // transferred. use it as a hint to size our buffer. UInt32 size; @@ -726,7 +726,7 @@ bool CXWindowsScreen::sendClipboardData( } if (data.size() > kMaxRequestSize) { - log((CLOG_DEBUG "handling clipboard request for %d as INCR", target)); + log((CLOG_DEBUG1 "handling clipboard request for %d as INCR", target)); // get the appropriate list, creating it if necessary CRequestList* list = m_clipboards[id].m_requests[requestor]; @@ -763,7 +763,7 @@ bool CXWindowsScreen::sendClipboardData( 1); } else { - log((CLOG_DEBUG "handling clipboard request for %d", target)); + log((CLOG_DEBUG1 "handling clipboard request for %d", target)); // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, @@ -783,7 +783,7 @@ bool CXWindowsScreen::sendClipboardMultiple( Window requestor, Atom property, Time time) { - log((CLOG_DEBUG "handling clipboard request for MULTIPLE")); + log((CLOG_DEBUG1 "handling clipboard request for MULTIPLE")); // get the list of requested formats Atom type; @@ -850,7 +850,7 @@ bool CXWindowsScreen::sendClipboardTargets( Window requestor, Atom property, Time /*time*/) { - log((CLOG_DEBUG "handling request for TARGETS")); + log((CLOG_DEBUG1 "handling request for TARGETS")); // count the number of targets, plus TARGETS and MULTIPLE SInt32 numTargets = 2; @@ -888,7 +888,7 @@ bool CXWindowsScreen::sendClipboardTimestamp( Window requestor, Atom property, Time /*time*/) { - log((CLOG_DEBUG "handling clipboard request for TIMESTAMP")); + log((CLOG_DEBUG1 "handling clipboard request for TIMESTAMP")); // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, From 3be014f8f522319d3e2b93419159a0a8cd127c0d Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Apr 2002 00:46:15 +0000 Subject: [PATCH 051/807] Clipboard improvements. Still not working right. Nedit doesn't work at all but at least now there's a timeout to prevent synergy from hanging waiting on a reply. --- client/CXWindowsSecondaryScreen.cpp | 23 +++- server/CXWindowsPrimaryScreen.cpp | 25 +++- synergy/CXWindowsScreen.cpp | 206 ++++++++++++++++++++-------- synergy/CXWindowsScreen.h | 26 ++-- 4 files changed, 196 insertions(+), 84 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 8e088c8e..79ff7efd 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -89,6 +89,20 @@ void CXWindowsSecondaryScreen::run() xevent.xselectionrequest.property, xevent.xselectionrequest.time); } + else { + // unknown window. return failure. + CDisplayLock display(this); + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = display; + event.xselection.requestor = xevent.xselectionrequest.requestor; + event.xselection.selection = xevent.xselectionrequest.selection; + event.xselection.target = xevent.xselectionrequest.target; + event.xselection.property = None; + event.xselection.time = xevent.xselectionrequest.time; + XSendEvent(display, xevent.xselectionrequest.requestor, + False, 0, &event); + } break; case PropertyNotify: @@ -269,14 +283,12 @@ void CXWindowsSecondaryScreen::mouseWheel(SInt32) void CXWindowsSecondaryScreen::setClipboard( ClipboardID id, const IClipboard* clipboard) { - // FIXME -- don't use CurrentTime - setDisplayClipboard(id, clipboard, m_window, CurrentTime); + setDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); } void CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) { - // FIXME -- don't use CurrentTime - setDisplayClipboard(id, NULL, m_window, CurrentTime); + setDisplayClipboard(id, NULL, m_window, getCurrentTime(m_window)); } void CXWindowsSecondaryScreen::getSize( @@ -293,8 +305,7 @@ SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const void CXWindowsSecondaryScreen::getClipboard( ClipboardID id, IClipboard* clipboard) const { - // FIXME -- don't use CurrentTime - getDisplayClipboard(id, clipboard, m_window, CurrentTime); + getDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); } void CXWindowsSecondaryScreen::onOpenDisplay() diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 3935dc66..73a1de48 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -163,6 +163,20 @@ void CXWindowsPrimaryScreen::run() xevent.xselectionrequest.property, xevent.xselectionrequest.time); } + else { + // unknown window. return failure. + CDisplayLock display(this); + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = display; + event.xselection.requestor = xevent.xselectionrequest.requestor; + event.xselection.selection = xevent.xselectionrequest.selection; + event.xselection.target = xevent.xselectionrequest.target; + event.xselection.property = None; + event.xselection.time = xevent.xselectionrequest.time; + XSendEvent(display, xevent.xselectionrequest.requestor, + False, 0, &event); + } break; case PropertyNotify: @@ -326,14 +340,12 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( void CXWindowsPrimaryScreen::setClipboard( ClipboardID id, const IClipboard* clipboard) { - // FIXME -- don't use CurrentTime - setDisplayClipboard(id, clipboard, m_window, CurrentTime); + setDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); } void CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) { - // FIXME -- don't use CurrentTime - setDisplayClipboard(id, NULL, m_window, CurrentTime); + setDisplayClipboard(id, NULL, m_window, getCurrentTime(m_window)); } void CXWindowsPrimaryScreen::getSize( @@ -350,8 +362,7 @@ SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const void CXWindowsPrimaryScreen::getClipboard( ClipboardID id, IClipboard* clipboard) const { - // FIXME -- don't use CurrentTime - getDisplayClipboard(id, clipboard, m_window, CurrentTime); + getDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); } void CXWindowsPrimaryScreen::onOpenDisplay() @@ -371,7 +382,7 @@ void CXWindowsPrimaryScreen::onOpenDisplay() attr.event_mask = PointerMotionMask |// PointerMotionHintMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | - KeymapStateMask; + KeymapStateMask | PropertyChangeMask; attr.do_not_propagate_mask = 0; attr.override_redirect = True; attr.cursor = createBlankCursor(); diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index fe1ae05e..1c1ad88d 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -4,6 +4,7 @@ #include "TMethodJob.h" #include "CLog.h" #include "CString.h" +#include "CStopwatch.h" #include #include #include @@ -64,6 +65,7 @@ void CXWindowsScreen::openDisplay() m_atomString = XInternAtom(m_display, "STRING", False); m_atomText = XInternAtom(m_display, "TEXT", False); m_atomCompoundText = XInternAtom(m_display, "COMPOUND_TEXT", False); + m_atomSynergyTime = XInternAtom(m_display, "SYNERGY_TIME", False); // clipboard atoms m_atomClipboard[kClipboardClipboard] = @@ -83,7 +85,7 @@ void CXWindowsScreen::closeDisplay() // clear out the clipboard request lists for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - ClipboardInfo& clipboard = m_clipboards[id]; + CClipboardInfo& clipboard = m_clipboards[id]; for (CRequestMap::iterator index = clipboard.m_requests.begin(); index != clipboard.m_requests.end(); ++index) { CRequestList* list = index->second; @@ -221,6 +223,7 @@ bool CXWindowsScreen::setDisplayClipboard( // we got the selection log((CLOG_INFO "grabbed clipboard at %d", timestamp)); m_clipboards[id].m_gotClipboard = timestamp; + m_clipboards[id].m_lostClipboard = CurrentTime; if (clipboard != NULL) { // save clipboard to serve requests @@ -271,7 +274,7 @@ void CXWindowsScreen::getDisplayClipboard( const SInt32 numTargets = targets.size() / sizeof(Atom); std::set clipboardFormats; std::set targets; - log((CLOG_INFO "getting selection with %d targets", numTargets)); + log((CLOG_INFO "getting selection %d with %d targets", id, numTargets)); for (SInt32 i = 0; i < numTargets; ++i) { Atom format = targetAtoms[i]; log((CLOG_DEBUG1 " source target %d", format)); @@ -341,6 +344,8 @@ bool CXWindowsScreen::getDisplayClipboard( assert(outputType != NULL); assert(outputData != NULL); + // FIXME -- this doesn't work to retrieve Motif selections. + // delete data property XDeleteProperty(m_display, requestor, m_atomData); @@ -350,12 +355,18 @@ bool CXWindowsScreen::getDisplayClipboard( // wait for the selection notify event. can't just mask out other // events because X stupidly doesn't provide a mask for selection - // events, so we use a predicate to find our event. + // events, so we use a predicate to find our event. we also set + // a time limit for a response so we're not screwed by a bad + // clipboard owner. + CStopwatch timer(true); XEvent xevent; -// FIXME -- must limit the time we wait for bad clients while (XCheckIfEvent(m_display, &xevent, &CXWindowsScreen::findSelectionNotify, (XPointer)&requestor) != True) { + // return false if we've timed-out + if (timer.getTime() >= 1.0) + return false; + // wait a bit CThread::sleep(0.05); } @@ -557,36 +568,41 @@ Bool CXWindowsScreen::findPropertyNotify( } void CXWindowsScreen::addClipboardRequest( - Window /*owner*/, Window requestor, + Window owner, Window requestor, Atom selection, Atom target, Atom property, Time time) { + bool success = false; + // see if it's a selection we know about ClipboardID id; for (id = 0; id < kClipboardEnd; ++id) if (selection == m_atomClipboard[id]) break; - if (id == kClipboardEnd) - return; // mutex the display CLock lock(&m_mutex); - bool success = false; // a request for multiple targets is special - if (target == m_atomMultiple) { - // add a multiple request - if (property != None) { - success = sendClipboardMultiple(id, requestor, property, time); + if (id != kClipboardEnd) { + // check time we own the selection against the requested time + if (!wasOwnedAtTime(id, owner, time)) { + // fail for time we didn't own selection + } + else if (target == m_atomMultiple) { + // add a multiple request + if (property != None) { + success = sendClipboardMultiple(id, requestor, property, time); + } + } + else { + // handle remaining request formats + success = sendClipboardData(id, requestor, target, property, time); } - } - else { - // handle remaining request formats - success = sendClipboardData(id, requestor, target, property, time); } // send success or failure - sendNotify(id, requestor, target, success ? property : None, time); + sendNotify(requestor, selection, target, success ? property : None, time); } void CXWindowsScreen::processClipboardRequest( @@ -597,7 +613,7 @@ void CXWindowsScreen::processClipboardRequest( // check every clipboard for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - ClipboardInfo& clipboard = m_clipboards[id]; + CClipboardInfo& clipboard = m_clipboards[id]; // find the request list CRequestMap::iterator index = clipboard.m_requests.find(requestor); @@ -670,7 +686,7 @@ void CXWindowsScreen::destroyClipboardRequest( // check every clipboard for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - ClipboardInfo& clipboard = m_clipboards[id]; + CClipboardInfo& clipboard = m_clipboards[id]; // find the request list CRequestMap::iterator index = clipboard.m_requests.find(requestor); @@ -789,60 +805,57 @@ bool CXWindowsScreen::sendClipboardMultiple( Atom type; SInt32 size; CString data; - getData(requestor, property, &type, &size, &data); - if (type != m_atomAtomPair) { - // unexpected data type - return false; + if (!getData(requestor, property, &type, &size, &data)) { + type = 0; } - // check each format, replacing ones we can't do with None. set - // the property for each to the requested data (for small requests) - // or INCR (for large requests). + // we only handle atom pair type bool success = false; - bool updated = false; - UInt32 numRequests = data.size() / (2 * sizeof(Atom)); - for (UInt32 index = 0; index < numRequests; ++index) { - // get request info - const Atom* request = reinterpret_cast(data.data()); - const Atom target = request[2 * index + 0]; - const Atom property = request[2 * index + 1]; + if (type == m_atomAtomPair) { + // check each format, replacing ones we can't do with None. set + // the property for each to the requested data (for small requests) + // or INCR (for large requests). + bool updated = false; + UInt32 numRequests = data.size() / (2 * sizeof(Atom)); + for (UInt32 index = 0; index < numRequests; ++index) { + // get request info + const Atom* request = reinterpret_cast(data.data()); + const Atom target = request[2 * index + 0]; + const Atom property = request[2 * index + 1]; - // handle target - if (property != None) { - if (!sendClipboardData(id, requestor, target, property, time)) { - // couldn't handle target. change property to None. - const Atom none = None; - data.replace((2 * index + 1) * sizeof(Atom), sizeof(Atom), + // handle target + if (property != None) { + if (!sendClipboardData(id, requestor, target, property, time)) { + // couldn't handle target. change property to None. + const Atom none = None; + data.replace((2 * index + 1) * sizeof(Atom), sizeof(Atom), reinterpret_cast(&none), sizeof(none)); - updated = true; - } - else { - success = true; + updated = true; + } + else { + success = true; + } } } - } - // update property if we changed it - if (updated) { - // FIXME -- handle Alloc errors (by returning false) - XChangeProperty(m_display, requestor, property, + // update property if we changed it + if (updated) { + // FIXME -- handle Alloc errors (by returning false) + XChangeProperty(m_display, requestor, property, m_atomAtomPair, 8 * sizeof(Atom), PropModeReplace, reinterpret_cast( data.data()), data.length()); + } } - // send notify if any format was successful - if (success) { - sendNotify(id, requestor, m_atomMultiple, + // send notify + sendNotify(requestor, m_atomClipboard[id], m_atomMultiple, success ? property : None, time); - return true; - } - - return false; + return success; } bool CXWindowsScreen::sendClipboardTargets( @@ -902,20 +915,99 @@ bool CXWindowsScreen::sendClipboardTimestamp( } void CXWindowsScreen::sendNotify( - ClipboardID id, Window requestor, + Window requestor, Atom selection, Atom target, Atom property, Time time) { XEvent event; event.xselection.type = SelectionNotify; event.xselection.display = m_display; event.xselection.requestor = requestor; - event.xselection.selection = m_atomClipboard[id]; + event.xselection.selection = selection; event.xselection.target = target; event.xselection.property = property; event.xselection.time = time; XSendEvent(m_display, requestor, False, 0, &event); } +bool CXWindowsScreen::wasOwnedAtTime( + ClipboardID id, Window window, Time time) const +{ + const CClipboardInfo& clipboard = m_clipboards[id]; + + // not owned if we've never owned the selection + if (clipboard.m_gotClipboard == CurrentTime) + return false; + + // if time is CurrentTime then return true if we still own the + // selection and false if we do not. else if we still own the + // selection then get the current time, otherwise use + // m_lostClipboard as the end time. + Time lost = clipboard.m_lostClipboard; + if (lost == CurrentTime) + if (time == CurrentTime) + return true; + else + lost = getCurrentTimeNoLock(window); + else + if (time == CurrentTime) + return false; + + // compare time to range + Time duration = clipboard.m_lostClipboard - clipboard.m_gotClipboard; + Time when = time - clipboard.m_gotClipboard; + return (/*when >= 0 &&*/ when < duration); +} + +Time CXWindowsScreen::getCurrentTime(Window window) const +{ + CLock lock(&m_mutex); + return getCurrentTimeNoLock(window); +} + +Time CXWindowsScreen::getCurrentTimeNoLock( + Window window) const +{ + // do a zero-length append to get the current time + unsigned char dummy; + XChangeProperty(m_display, window, m_atomSynergyTime, + m_atomInteger, 8, + PropModeAppend, + &dummy, 0); + + // look for property notify events with the following + CPropertyNotifyInfo filter; + filter.m_window = window; + filter.m_property = m_atomSynergyTime; + + // wait for reply + XEvent xevent; + while (XCheckIfEvent(m_display, &xevent, + &CXWindowsScreen::findPropertyNotify, + (XPointer)&filter) != True) { + // wait a bit + CThread::sleep(0.05); + } + assert(xevent.type == PropertyNotify); + assert(xevent.xproperty.window == window); + assert(xevent.xproperty.atom == m_atomSynergyTime); + + return xevent.xproperty.time; +} + + +// +// CXWindowsScreen::CClipboardInfo +// + +CXWindowsScreen::CClipboardInfo::CClipboardInfo() : + m_clipboard(), + m_gotClipboard(CurrentTime), + m_lostClipboard(CurrentTime), + m_requests() +{ + // do nothing +} + // // CXWindowsScreen::CDisplayLock diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 2e61e5f4..00c7a703 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -91,6 +91,9 @@ class CXWindowsScreen { // terminate a selection request void destroyClipboardRequest(Window window); + // get the current server time + Time getCurrentTime(Window) const; + // called by openDisplay() to allow subclasses to prepare the display virtual void onOpenDisplay() = 0; @@ -135,11 +138,16 @@ class CXWindowsScreen { Atom property, Time time); bool sendClipboardTimestamp(ClipboardID, Window requestor, Atom property, Time time); - void sendNotify(ClipboardID, Window requestor, + void sendNotify(Window requestor, Atom selection, Atom target, Atom property, Time time); + bool wasOwnedAtTime(ClipboardID, Window, Time) const; + Time getCurrentTimeNoLock(Window) const; private: - class ClipboardInfo { + class CClipboardInfo { + public: + CClipboardInfo(); + public: // the contents of the clipboard CClipboard m_clipboard; @@ -171,20 +179,10 @@ class CXWindowsScreen { Atom m_atomText; Atom m_atomCompoundText; Atom m_atomClipboard[kClipboardEnd]; + Atom m_atomSynergyTime; // clipboard info - ClipboardInfo m_clipboards[kClipboardEnd]; -/* - // the contents of our selection - CClipboard m_clipboard; - - // when we got the selection and when we lost it - Time m_gotClipboard; - Time m_lostClipboard; - - // the request queues - CRequestMap m_requests; -*/ + CClipboardInfo m_clipboards[kClipboardEnd]; // X is not thread safe CMutex m_mutex; From c5f6b34d85c50430ddf4987d30c99353f6759a7b Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 29 Apr 2002 13:31:44 +0000 Subject: [PATCH 052/807] checkpoint. changed protocol to better handle clipboards. now sending a sequence number with enter messages. screens use that sequence number in clipboard grab and data messages. the server uses the sequence number to order messages across clients. also changed secondary screens to send clipboard updates on leaving (or when grab occurs when not active) instead of on a query from the server. primary effectively does the same. the query message has been removed. --- client/CClient.cpp | 114 ++++++++++++++------ client/CClient.h | 6 +- server/CServer.cpp | 189 +++++++++++++++++++++++----------- server/CServer.h | 21 +++- server/CServerProtocol.h | 3 +- server/CServerProtocol1_0.cpp | 22 ++-- server/CServerProtocol1_0.h | 3 +- synergy/CClipboard.cpp | 32 +++++- synergy/CClipboard.h | 10 +- synergy/CXWindowsScreen.cpp | 24 ++--- synergy/CXWindowsScreen.h | 10 +- synergy/IClipboard.h | 16 ++- synergy/IPrimaryScreen.h | 4 +- synergy/IServerProtocol.h | 3 +- synergy/ProtocolTypes.h | 31 +++--- 15 files changed, 325 insertions(+), 163 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 69738f02..b5f00de0 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -30,7 +30,9 @@ CClient::CClient(const CString& clientName) : m_name(clientName), m_input(NULL), m_output(NULL), - m_screen(NULL) + m_screen(NULL), + m_active(false), + m_seqNum(0) { // do nothing } @@ -92,7 +94,29 @@ void CClient::onClipboardChanged(ClipboardID id) if (m_output != NULL) { // m_output can be NULL if the screen calls this method // before we've gotten around to connecting to the server. - CProtocolUtil::writef(m_output, kMsgCClipboard, id); + CProtocolUtil::writef(m_output, kMsgCClipboard, id, m_seqNum); + } + + // we now own the clipboard and it has not been sent to the server + m_ownClipboard[id] = true; + m_timeClipboard[id] = 0; + + // if we're not the active screen then send the clipboard now, + // otherwise we'll until we leave. + if (!m_active) { + // get clipboard + CClipboard clipboard; + m_screen->getClipboard(id, &clipboard); + + // save new time + m_timeClipboard[id] = clipboard.getTime(); + + // marshall the data + CString data = clipboard.marshall(); + + // send data + log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); + CProtocolUtil::writef(m_output, kMsgDClipboard, id, m_seqNum, &data); } } @@ -230,9 +254,6 @@ void CClient::runSession(void*) else if (memcmp(code, kMsgQInfo, 4) == 0) { onQueryInfo(); } - else if (memcmp(code, kMsgQClipboard, 4) == 0) { - onQueryClipboard(); - } else if (memcmp(code, kMsgDClipboard, 4) == 0) { onSetClipboard(); } @@ -271,6 +292,18 @@ void CClient::openSecondaryScreen() { assert(m_screen == NULL); + // not active + m_active = false; + + // reset last sequence number + m_seqNum = 0; + + // reset clipboard state + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_ownClipboard[id] = false; + m_timeClipboard[id] = 0; + } + // open screen log((CLOG_DEBUG1 "creating secondary screen")); #if defined(CONFIG_PLATFORM_WIN32) @@ -306,22 +339,61 @@ void CClient::onEnter() SInt16 x, y; { CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y); + CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum); + m_active = true; } m_screen->enter(x, y); } void CClient::onLeave() { + // tell screen we're leaving m_screen->leave(); + + // no longer the active screen + CLock lock(&m_mutex); + m_active = false; + + // send clipboards that we own and that have changed + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_ownClipboard[id]) { + // get clipboard data. set the clipboard time to the last + // clipboard time before getting the data from the screen + // as the screen may detect an unchanged clipboard and + // avoid copying the data. + CClipboard clipboard; + if (clipboard.open(m_timeClipboard[id])) + clipboard.close(); + m_screen->getClipboard(id, &clipboard); + + // check time + if (m_timeClipboard[id] == 0 || + clipboard.getTime() != m_timeClipboard[id]) { + // save new time + m_timeClipboard[id] = clipboard.getTime(); + + // marshall the data + CString data = clipboard.marshall(); + + // send data + log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); + CProtocolUtil::writef(m_output, + kMsgDClipboard, id, m_seqNum, &data); + } + } + } } void CClient::onGrabClipboard() { ClipboardID id; + UInt32 seqNum; { CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id); + CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id, &seqNum); + + // we no longer own the clipboard + m_ownClipboard[id] = false; } m_screen->grabClipboard(id); } @@ -347,32 +419,6 @@ void CClient::onQueryInfo() CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); } -void CClient::onQueryClipboard() -{ - // parse message - ClipboardID id; - UInt32 seqNum; - CClipboard clipboard; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgQClipboard + 4, &id, &seqNum); - } - log((CLOG_DEBUG "received query clipboard %d seqnum=%d", id, seqNum)); - - // get screen's clipboard data - m_screen->getClipboard(id, &clipboard); - - // marshall the data - CString data = clipboard.marshall(); - - // send it - log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, seqNum, data.size())); - { - CLock lock(&m_mutex); - CProtocolUtil::writef(m_output, kMsgDClipboard, id, seqNum, &data); - } -} - void CClient::onSetClipboard() { ClipboardID id; @@ -387,7 +433,7 @@ void CClient::onSetClipboard() // unmarshall CClipboard clipboard; - clipboard.unmarshall(data); + clipboard.unmarshall(data, 0); // set screen's clipboard m_screen->setClipboard(id, &clipboard); diff --git a/client/CClient.h b/client/CClient.h index 7776ad3d..f2cf0628 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -5,6 +5,7 @@ #include "CString.h" #include "BasicTypes.h" #include "ClipboardTypes.h" +#include "IClipboard.h" class CNetworkAddress; class IInputStream; @@ -39,7 +40,6 @@ class CClient { void onGrabClipboard(); void onScreenSaver(); void onQueryInfo(); - void onQueryClipboard(); void onSetClipboard(); void onKeyDown(); void onKeyRepeat(); @@ -56,6 +56,10 @@ class CClient { IOutputStream* m_output; ISecondaryScreen* m_screen; const CNetworkAddress* m_serverAddress; + bool m_active; + UInt32 m_seqNum; + bool m_ownClipboard[kClipboardEnd]; + IClipboard::Time m_timeClipboard[kClipboardEnd]; }; #endif diff --git a/server/CServer.cpp b/server/CServer.cpp index 94c2806b..bd24f766 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -43,7 +43,8 @@ else { wait(0); exit(1); } CServer::CServer() : m_primary(NULL), m_active(NULL), - m_primaryInfo(NULL) + m_primaryInfo(NULL), + m_seqNum(0) { m_socketFactory = NULL; m_securityFactory = NULL; @@ -94,6 +95,11 @@ void CServer::run() } } +void CServer::quit() +{ + m_primary->stop(); +} + void CServer::setScreenMap(const CScreenMap& screenMap) { CLock lock(&m_mutex); @@ -150,35 +156,43 @@ void CServer::setInfo(const CString& client, void CServer::grabClipboard(ClipboardID id) { - grabClipboard(id, m_primaryInfo->m_name); + grabClipboard(id, 0, m_primaryInfo->m_name); } void CServer::grabClipboard( - ClipboardID id, const CString& client) + ClipboardID id, UInt32 seqNum, + const CString& client) { CLock lock(&m_mutex); - ClipboardInfo& clipboard = m_clipboards[id]; + CClipboardInfo& clipboard = m_clipboards[id]; - // client must be connected + // screen must be connected CScreenList::iterator index = m_screens.find(client); if (index == m_screens.end()) { throw XBadClient(); } + // ignore grab if sequence number is old. always allow primary + // screen to grab. + if (client != m_primaryInfo->m_name && + seqNum < clipboard.m_clipboardSeqNum) { + log((CLOG_INFO "ignored client \"%s\" grab of clipboard %d", client.c_str(), id)); + return; + } + + // mark screen as owning clipboard log((CLOG_NOTE "client \"%s\" grabbed clipboard %d from \"%s\"", client.c_str(), id, clipboard.m_clipboardOwner.c_str())); + clipboard.m_clipboardOwner = client; + clipboard.m_clipboardSeqNum = seqNum; - // save the clipboard owner - clipboard.m_clipboardOwner = client; - - // mark client as having the clipboard data + // no screens have the new clipboard except the sender + clearGotClipboard(id); index->second->m_gotClipboard[id] = true; - // tell all other clients to take ownership of clipboard and mark - // them as not having the data yet. + // tell all other screens to take ownership of clipboard for (index = m_screens.begin(); index != m_screens.end(); ++index) { if (index->first != client) { CScreenInfo* info = index->second; - info->m_gotClipboard[id] = false; if (info->m_protocol == NULL) { m_primary->grabClipboard(id); } @@ -186,13 +200,10 @@ void CServer::grabClipboard( info->m_protocol->sendGrabClipboard(id); } } - } + } - // increment the clipboard sequence number so we can identify the - // clipboard query's response. - ++clipboard.m_clipboardSeqNum; - - // begin getting the clipboard data + // get the clipboard data if primary has it, otherwise mark the + // clipboard data as unknown. if (m_active->m_protocol == NULL) { // get clipboard immediately from primary screen m_primary->getClipboard(id, &clipboard.m_clipboard); @@ -201,43 +212,42 @@ void CServer::grabClipboard( } else { // clear out the clipboard since existing data is now out of date. - if (clipboard.m_clipboard.open()) { + if (clipboard.m_clipboard.open(0)) { clipboard.m_clipboard.close(); } clipboard.m_clipboardReady = false; - - // send request but don't wait for reply - m_active->m_protocol->sendQueryClipboard(id, - clipboard.m_clipboardSeqNum); } } void CServer::setClipboard(ClipboardID id, UInt32 seqNum, const CString& data) { - // update the clipboard if the sequence number matches CLock lock(&m_mutex); - ClipboardInfo& clipboard = m_clipboards[id]; - if (seqNum == clipboard.m_clipboardSeqNum) { - // unmarshall into our clipboard buffer - clipboard.m_clipboardData = data; - clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData); - clipboard.m_clipboardReady = true; + CClipboardInfo& clipboard = m_clipboards[id]; - // if the active client doesn't have the clipboard data - // (and it won't unless the client is the one sending us - // the data) then send the data now. - if (!m_active->m_gotClipboard[id]) { - if (m_active->m_protocol == NULL) { - m_primary->setClipboard(id, &clipboard.m_clipboard); - } - else { - m_active->m_protocol->sendClipboard(id, - clipboard.m_clipboardData); - } - m_active->m_gotClipboard[id] = true; - } + // ignore update if sequence number is old + if (seqNum < clipboard.m_clipboardSeqNum) { + log((CLOG_INFO "ignored client \"%s\" update of clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + return; } + + // unmarshall into our clipboard buffer + log((CLOG_NOTE "client \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + clipboard.m_clipboardReady = true; + clipboard.m_clipboardData = data; + clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); + + // all screens have an out-of-date clipboard except the sender + clearGotClipboard(id); + CScreenList::const_iterator index = + m_screens.find(clipboard.m_clipboardOwner); + if (index != m_screens.end()) { + index->second->m_gotClipboard[id] = true; + } + + // send the new clipboard to the active screen (will do nothing if + // the active screen is the one sending the new clipboard) + sendClipboard(id); } bool CServer::onCommandKey(KeyID /*id*/, @@ -492,8 +502,19 @@ void CServer::switchScreen(CScreenInfo* dst, // since that's a waste of time we skip that and just warp the // mouse. if (m_active != dst) { + // note if we're leaving the primary screen + const bool leavingPrimary = (m_active->m_protocol == NULL); + + // if leaving the primary screen then update the clipboards + // that it owns + if (leavingPrimary) { + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + updatePrimaryClipboard(id); + } + } + // leave active screen - if (m_active->m_protocol == NULL) { + if (leavingPrimary) { m_primary->leave(); } else { @@ -503,27 +524,20 @@ void CServer::switchScreen(CScreenInfo* dst, // cut over m_active = dst; + // increment enter sequence number + ++m_seqNum; + // enter new screen if (m_active->m_protocol == NULL) { m_primary->enter(x, y); } else { - m_active->m_protocol->sendEnter(x, y); + m_active->m_protocol->sendEnter(x, y, m_seqNum); } - // send the clipboard data if we haven't done so yet + // send the clipboard data to new active screen for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - ClipboardInfo& clipboard = m_clipboards[id]; - if (clipboard.m_clipboardReady && !m_active->m_gotClipboard[id]) { - if (m_active->m_protocol == NULL) { - m_primary->setClipboard(id, &clipboard.m_clipboard); - } - else { - m_active->m_protocol->sendClipboard(id, - clipboard.m_clipboardData); - } - m_active->m_gotClipboard[id] = true; - } + sendClipboard(id); } } else { @@ -889,9 +903,57 @@ void CServer::handshakeClient(void* vsocket) } } -void CServer::quit() +void CServer::clearGotClipboard(ClipboardID id) { - m_primary->stop(); + for (CScreenList::const_iterator index = m_screens.begin(); + index != m_screens.end(); ++index) { + index->second->m_gotClipboard[id] = false; + } +} + +void CServer::sendClipboard(ClipboardID id) +{ + // do nothing if clipboard was already sent + if (!m_active->m_gotClipboard[id]) { + CClipboardInfo& clipboard = m_clipboards[id]; + if (clipboard.m_clipboardReady) { + // send it + if (m_active->m_protocol == NULL) { + m_primary->setClipboard(id, &clipboard.m_clipboard); + } + else { + m_active->m_protocol->sendClipboard(id, + clipboard.m_clipboardData); + } + + // clipboard has been sent + m_active->m_gotClipboard[id] = true; + } + } +} + +void CServer::updatePrimaryClipboard(ClipboardID id) +{ + CClipboardInfo& clipboard = m_clipboards[id]; + + // if leaving primary and the primary owns the clipboard + // then update it. + if (clipboard.m_clipboardOwner == m_primaryInfo->m_name) { + assert(clipboard.m_clipboardReady == true); + + // save clipboard time + IClipboard::Time time = clipboard.m_clipboard.getTime(); + + // update + m_primary->getClipboard(id, &clipboard.m_clipboard); + + // if clipboard changed then other screens have an + // out-of-date clipboard. + if (time != clipboard.m_clipboard.getTime()) { + clearGotClipboard(id); + m_primaryInfo->m_gotClipboard[id] = true; + } + } } // FIXME -- use factory to create screen @@ -904,6 +966,9 @@ void CServer::openPrimaryScreen() { assert(m_primary == NULL); + // reset sequence number + m_seqNum = 0; + // open screen log((CLOG_DEBUG1 "creating primary screen")); #if defined(CONFIG_PLATFORM_WIN32) @@ -927,7 +992,7 @@ void CServer::openPrimaryScreen() // set the clipboard owner to the primary screen and then get the // current clipboard data. for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - ClipboardInfo& clipboard = m_clipboards[id]; + CClipboardInfo& clipboard = m_clipboards[id]; m_primary->getClipboard(id, &clipboard.m_clipboard); clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); clipboard.m_clipboardReady = true; @@ -1098,10 +1163,10 @@ CServer::CScreenInfo::~CScreenInfo() // -// CServer::ClipboardInfo +// CServer::CClipboardInfo // -CServer::ClipboardInfo::ClipboardInfo() : +CServer::CClipboardInfo::CClipboardInfo() : m_clipboard(), m_clipboardData(), m_clipboardOwner(), diff --git a/server/CServer.h b/server/CServer.h index d4bf8553..1f0ab721 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -49,7 +49,8 @@ class CServer { // handle messages from clients void setInfo(const CString& clientName, SInt32 w, SInt32 h, SInt32 zoneSize); - void grabClipboard(ClipboardID, const CString& clientName); + void grabClipboard(ClipboardID, + UInt32 seqNum, const CString& clientName); void setClipboard(ClipboardID, UInt32 seqNum, const CString& data); @@ -126,6 +127,15 @@ class CServer { void openPrimaryScreen(); void closePrimaryScreen(); + // clear gotClipboard flags in all screens + void clearGotClipboard(ClipboardID); + + // send clipboard to the active screen if it doesn't already have it + void sendClipboard(ClipboardID); + + // update the clipboard if owned by the primary screen + void updatePrimaryClipboard(ClipboardID); + // cancel running threads void cleanupThreads(); @@ -148,9 +158,9 @@ class CServer { private: typedef std::list CThreadList; typedef std::map CScreenList; - class ClipboardInfo { + class CClipboardInfo { public: - ClipboardInfo(); + CClipboardInfo(); public: CClipboard m_clipboard; @@ -174,11 +184,14 @@ class CServer { CScreenInfo* m_active; CScreenInfo* m_primaryInfo; + // the sequence number of enter messages + UInt32 m_seqNum; + SInt32 m_x, m_y; CScreenMap m_screenMap; - ClipboardInfo m_clipboards[kClipboardEnd]; + CClipboardInfo m_clipboards[kClipboardEnd]; }; #endif diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index 1dda0969..e605049f 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -31,11 +31,10 @@ class CServerProtocol : public IServerProtocol { virtual void run() = 0; virtual void queryInfo() = 0; virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) = 0; virtual void sendLeave() = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; - virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum) = 0; virtual void sendScreenSaver(bool on) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 02f27a71..8cfcff0a 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -91,10 +91,10 @@ void CServerProtocol1_0::sendClose() } void CServerProtocol1_0::sendEnter( - SInt32 xAbs, SInt32 yAbs) + SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) { - log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d", getClient().c_str(), xAbs, yAbs)); - CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs); + log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d", getClient().c_str(), xAbs, yAbs, seqNum)); + CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs, seqNum); } void CServerProtocol1_0::sendLeave() @@ -113,14 +113,7 @@ void CServerProtocol1_0::sendClipboard( void CServerProtocol1_0::sendGrabClipboard(ClipboardID id) { log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id); -} - -void CServerProtocol1_0::sendQueryClipboard( - ClipboardID id, UInt32 seqNum) -{ - log((CLOG_DEBUG "query clipboard %d to \"%s\"", id, getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgQClipboard, id, seqNum); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); } void CServerProtocol1_0::sendScreenSaver(bool on) @@ -207,7 +200,8 @@ void CServerProtocol1_0::recvClipboard() void CServerProtocol1_0::recvGrabClipboard() { ClipboardID id; - CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id); - log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d", getClient().c_str(), id)); - getServer()->grabClipboard(id, getClient()); + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getClient().c_str(), id, seqNum)); + getServer()->grabClipboard(id, seqNum, getClient()); } diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index 63c64f73..e2cacd8d 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -16,11 +16,10 @@ class CServerProtocol1_0 : public CServerProtocol { virtual void run(); virtual void queryInfo(); virtual void sendClose(); - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs); + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum); virtual void sendLeave(); virtual void sendClipboard(ClipboardID, const CString&); virtual void sendGrabClipboard(ClipboardID); - virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum); virtual void sendScreenSaver(bool on); virtual void sendKeyDown(KeyID, KeyModifierMask); virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count); diff --git a/synergy/CClipboard.cpp b/synergy/CClipboard.cpp index 1e572af1..e5021aab 100644 --- a/synergy/CClipboard.cpp +++ b/synergy/CClipboard.cpp @@ -6,7 +6,8 @@ CClipboard::CClipboard() { - // do nothing + open(0); + close(); } CClipboard::~CClipboard() @@ -14,13 +15,17 @@ CClipboard::~CClipboard() // do nothing } -bool CClipboard::open() +bool CClipboard::open(Time time) { // clear all data for (SInt32 index = 0; index < kNumFormats; ++index) { m_data[index] = ""; m_added[index] = false; } + + // save time + m_time = time; + return true; } @@ -35,6 +40,11 @@ void CClipboard::add(EFormat format, const CString& data) m_added[format] = true; } +CClipboard::Time CClipboard::getTime() const +{ + return m_time; +} + bool CClipboard::has(EFormat format) const { return m_added[format]; @@ -47,7 +57,19 @@ CString CClipboard::get(EFormat format) const void CClipboard::copy(IClipboard* dst, const IClipboard* src) { - if (dst->open()) { + assert(dst != NULL); + assert(src != NULL); + + copy(dst, src, src->getTime()); +} + +void CClipboard::copy(IClipboard* dst, + const IClipboard* src, Time time) +{ + assert(dst != NULL); + assert(src != NULL); + + if (dst->open(time)) { for (SInt32 format = 0; format != IClipboard::kNumFormats; ++format) { IClipboard::EFormat eFormat = (IClipboard::EFormat)format; if (src->has(eFormat)) { @@ -58,12 +80,12 @@ void CClipboard::copy(IClipboard* dst, const IClipboard* src) } } -void CClipboard::unmarshall(const CString& data) +void CClipboard::unmarshall(const CString& data, Time time) { const char* index = data.data(); // clear existing data - open(); + open(time); // read the number of formats const UInt32 numFormats = readUInt32(index); diff --git a/synergy/CClipboard.h b/synergy/CClipboard.h index e5a63ccc..8fcf5cc0 100644 --- a/synergy/CClipboard.h +++ b/synergy/CClipboard.h @@ -16,7 +16,7 @@ class CClipboard : public IClipboard { // manipulators // unmarshall clipboard data - void unmarshall(const CString& data); + void unmarshall(const CString& data, Time); // accessors @@ -24,9 +24,10 @@ class CClipboard : public IClipboard { CString marshall() const; // IClipboard overrides - virtual bool open(); + virtual bool open(Time); virtual void close(); virtual void add(EFormat, const CString& data); + virtual Time getTime() const; virtual bool has(EFormat) const; virtual CString get(EFormat) const; @@ -34,14 +35,17 @@ class CClipboard : public IClipboard { // transfer all the data in one clipboard to another. the // clipboards can be of any concrete clipboard type (and - // they don't have to be the same type). + // they don't have to be the same type). this also sets + // the timestamp to time, if provided, or the time in src. static void copy(IClipboard* dst, const IClipboard* src); + static void copy(IClipboard* dst, const IClipboard* src, Time); private: UInt32 readUInt32(const char*) const; void writeUInt32(CString*, UInt32) const; private: + Time m_time; bool m_added[kNumFormats]; CString m_data[kNumFormats]; }; diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 1c1ad88d..51109584 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -222,16 +222,15 @@ bool CXWindowsScreen::setDisplayClipboard( if (XGetSelectionOwner(m_display, m_atomClipboard[id]) == requestor) { // we got the selection log((CLOG_INFO "grabbed clipboard at %d", timestamp)); - m_clipboards[id].m_gotClipboard = timestamp; m_clipboards[id].m_lostClipboard = CurrentTime; - if (clipboard != NULL) { // save clipboard to serve requests - CClipboard::copy(&m_clipboards[id].m_clipboard, clipboard); + CClipboard::copy(&m_clipboards[id].m_clipboard, + clipboard, timestamp); } else { // clear clipboard - if (m_clipboards[id].m_clipboard.open()) { + if (m_clipboards[id].m_clipboard.open(timestamp)) { m_clipboards[id].m_clipboard.close(); } } @@ -250,8 +249,10 @@ void CXWindowsScreen::getDisplayClipboard( assert(clipboard != NULL); assert(requestor != None); + // FIXME -- don't update clipboard object if clipboard hasn't changed + // clear the clipboard object - if (!clipboard->open()) + if (!clipboard->open(timestamp)) return; // block others from using the display while we get the clipboard. @@ -904,12 +905,12 @@ bool CXWindowsScreen::sendClipboardTimestamp( log((CLOG_DEBUG1 "handling clipboard request for TIMESTAMP")); // FIXME -- handle Alloc errors (by returning false) + Time time = m_clipboards[id].m_clipboard.getTime(); XChangeProperty(m_display, requestor, property, m_atomInteger, - 8 * sizeof(m_clipboards[id].m_gotClipboard), + 32, PropModeReplace, - reinterpret_cast( - &m_clipboards[id].m_gotClipboard), + reinterpret_cast(time), 1); return true; } @@ -935,7 +936,7 @@ bool CXWindowsScreen::wasOwnedAtTime( const CClipboardInfo& clipboard = m_clipboards[id]; // not owned if we've never owned the selection - if (clipboard.m_gotClipboard == CurrentTime) + if (clipboard.m_clipboard.getTime() == CurrentTime) return false; // if time is CurrentTime then return true if we still own the @@ -953,8 +954,8 @@ bool CXWindowsScreen::wasOwnedAtTime( return false; // compare time to range - Time duration = clipboard.m_lostClipboard - clipboard.m_gotClipboard; - Time when = time - clipboard.m_gotClipboard; + Time duration = clipboard.m_lostClipboard - clipboard.m_clipboard.getTime(); + Time when = time - clipboard.m_clipboard.getTime(); return (/*when >= 0 &&*/ when < duration); } @@ -1001,7 +1002,6 @@ Time CXWindowsScreen::getCurrentTimeNoLock( CXWindowsScreen::CClipboardInfo::CClipboardInfo() : m_clipboard(), - m_gotClipboard(CurrentTime), m_lostClipboard(CurrentTime), m_requests() { diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 00c7a703..ec8cfb7e 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -1,10 +1,10 @@ #ifndef CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H -#include "CClipboard.h" -#include "CMutex.h" #include "BasicTypes.h" #include "ClipboardTypes.h" +#include "CClipboard.h" +#include "CMutex.h" #include #include #include @@ -74,6 +74,7 @@ class CXWindowsScreen { // copy the clipboard contents to clipboard. requestor must be a // valid window; it will be used to receive the transfer. timestamp // should be the timestamp of the provoking event and not CurrentTime. + // if force is false then only update clipboard void getDisplayClipboard(ClipboardID, IClipboard* clipboard, Window requestor, Time timestamp) const; @@ -149,11 +150,10 @@ class CXWindowsScreen { CClipboardInfo(); public: - // the contents of the clipboard + // the contents of the clipboard and the time we got it CClipboard m_clipboard; - // when we got the clipboard and when we lost it - Time m_gotClipboard; + // when we lost the clipboard Time m_lostClipboard; // the request queues diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index 08551e37..a75ec70a 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -8,6 +8,13 @@ class CString; class IClipboard : public IInterface { public: + // timestamp type. timestamps are in milliseconds from some + // arbitrary starting time. timestamps will wrap around to 0 + // after about 49 3/4 days. + typedef UInt32 Time; + + // known clipboard formats. kNumFormats must be last and + // formats must be sequential starting from zero. enum EFormat { kText, kNumFormats }; // manipulators @@ -16,8 +23,10 @@ class IClipboard : public IInterface { // only add() may be called between an open() and its // corresponding close(). if open() returns false then // the clipboard could not be opened or grabbed; do not - // call close() in that case. - virtual bool open() = 0; + // call close() in that case. iff open() returns true it + // should have saved the timestamp. the timestamp should + // be zero before the first successful open. + virtual bool open(Time) = 0; // close the clipboard. close() must match a preceding open(). // this signals that the clipboard has been filled with all the @@ -33,6 +42,9 @@ class IClipboard : public IInterface { // accessors + // returns the timestamp passed to the last successful open(). + virtual Time getTime() const = 0; + // returns true iff the clipboard contains data in the given // format. virtual bool has(EFormat) const = 0; diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index a398f226..91a698bd 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -75,7 +75,9 @@ class IPrimaryScreen : public IInterface { // get the size of jump zone virtual SInt32 getJumpZoneSize() const = 0; - // get the screen's clipboard contents + // get the screen's clipboard contents. the implementation can + // and should avoid setting the clipboard object if the screen's + // clipboard hasn't changed. virtual void getClipboard(ClipboardID, IClipboard*) const = 0; }; diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index 49a38898..84ed10ef 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -24,11 +24,10 @@ class IServerProtocol : public IInterface { // send various messages to client virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) = 0; virtual void sendLeave() = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; - virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum) = 0; virtual void sendScreenSaver(bool on) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index ca54ff04..f9b5edf7 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -22,17 +22,26 @@ static const char kMsgCClose[] = "CBYE"; // enter screen: primary -> secondary // entering screen at screen position $1 = x, $2 = y. x,y are -// absolute screen coordinates. -static const char kMsgCEnter[] = "CINN%2i%2i"; +// absolute screen coordinates. $3 = sequence number, which is +// used to order messages between screens. the secondary screen +// must return this number with some messages. +static const char kMsgCEnter[] = "CINN%2i%2i%4i"; // leave screen: primary -> secondary -// leaving screen +// leaving screen. the secondary screen should send clipboard +// data in response to this message for those clipboards that +// it has grabbed (i.e. has sent a kMsgCClipboard for and has +// not received a kMsgCClipboard for with a greater sequence +// number) and that were grabbed or have changed since the +// last leave. static const char kMsgCLeave[] = "COUT"; // grab clipboard: primary <-> secondary // sent by screen when some other app on that screen grabs a -// clipboard. $1 = the clipboard identifier. -static const char kMsgCClipboard[] = "CCLP%1i"; +// clipboard. $1 = the clipboard identifier, $2 = sequence number. +// secondary screens must use the sequence number passed in the +// most recent kMsgCEnter. the primary always sends 0. +static const char kMsgCClipboard[] = "CCLP%1i%4i"; // screensaver change: primary -> secondary // screensaver on primary has started ($1 == 1) or closed ($1 == 0) @@ -73,9 +82,9 @@ static const char kMsgDMouseWheel[] = "DMWM%2i"; // clipboard data: primary <-> secondary // $2 = sequence number, $3 = clipboard data. the sequence number -// is 0 when sent by the primary. the secondary sends this message -// in response to a kMsgQClipboard and uses the sequence number from -// that message. $1 = clipboard identifier. +// is 0 when sent by the primary. secondary screens should use the +// sequence number from the most recent kMsgCEnter. $1 = clipboard +// identifier. static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; // client data: secondary -> primary @@ -88,12 +97,6 @@ static const char kMsgDInfo[] = "DINF%2i%2i%2i"; // query codes // -// query clipboard: primary -> secondary -// $2 = sequence number. the sequence number is an arbitrary value -// used by primary to identify the kMsgDClipboard response to a -// query. $1 = clipboard identifier. -static const char kMsgQClipboard[] = "QCLP%1i%4i"; - // query screen info: primary -> secondary // client should reply with a kMsgDInfo. static const char kMsgQInfo[] = "QINF"; From 6299eea7b676f3922db2003949557e8e8e5e4d7e Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 29 Apr 2002 13:49:56 +0000 Subject: [PATCH 053/807] Added missing event mask. --- client/CXWindowsSecondaryScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 79ff7efd..84995990 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -319,7 +319,7 @@ void CXWindowsSecondaryScreen::onOpenDisplay() // as the cursor enters the screen or the display's real cursor is // moved. XSetWindowAttributes attr; - attr.event_mask = LeaveWindowMask; + attr.event_mask = LeaveWindowMask | PropertyChangeMask; attr.do_not_propagate_mask = 0; attr.override_redirect = True; attr.cursor = createBlankCursor(); From 6a9a180688348154fdfec562dbd026e9e47256c6 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 29 Apr 2002 14:08:48 +0000 Subject: [PATCH 054/807] Made event selection a little more robust. Also fixed failure to marshall clipboard data when updating primary clipboards. --- client/CXWindowsSecondaryScreen.cpp | 10 +++++++++- client/CXWindowsSecondaryScreen.h | 1 + server/CServer.cpp | 2 ++ server/CXWindowsPrimaryScreen.cpp | 14 ++++++++++++-- server/CXWindowsPrimaryScreen.h | 1 + synergy/CXWindowsScreen.cpp | 19 +++++++++++++++---- synergy/CXWindowsScreen.h | 3 +++ 7 files changed, 43 insertions(+), 7 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 84995990..d4d1c912 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -319,7 +319,7 @@ void CXWindowsSecondaryScreen::onOpenDisplay() // as the cursor enters the screen or the display's real cursor is // moved. XSetWindowAttributes attr; - attr.event_mask = LeaveWindowMask | PropertyChangeMask; + attr.event_mask = LeaveWindowMask; attr.do_not_propagate_mask = 0; attr.override_redirect = True; attr.cursor = createBlankCursor(); @@ -349,6 +349,14 @@ void CXWindowsSecondaryScreen::onCloseDisplay() m_window = None; } +long CXWindowsSecondaryScreen::getEventMask(Window w) const +{ + if (w == m_window) + return LeaveWindowMask; + else + return NoEventMask; +} + void CXWindowsSecondaryScreen::leaveNoLock(Display* display) { assert(display != NULL); diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 5f4eda9f..29809de6 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -34,6 +34,7 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen // CXWindowsScreen overrides virtual void onOpenDisplay(); virtual void onCloseDisplay(); + virtual long getEventMask(Window) const; private: struct KeyCodeMask { diff --git a/server/CServer.cpp b/server/CServer.cpp index bd24f766..a2e593fa 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -950,6 +950,8 @@ void CServer::updatePrimaryClipboard(ClipboardID id) // if clipboard changed then other screens have an // out-of-date clipboard. if (time != clipboard.m_clipboard.getTime()) { + log((CLOG_DEBUG "clipboard %d changed", id)); + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); clearGotClipboard(id); m_primaryInfo->m_gotClipboard[id] = true; } diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 73a1de48..0ce318cc 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -406,6 +406,17 @@ void CXWindowsPrimaryScreen::onCloseDisplay() m_window = None; } +long CXWindowsPrimaryScreen::getEventMask(Window w) const +{ + if (w == m_window) + return PointerMotionMask |// PointerMotionHintMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask; + else + return PointerMotionMask | SubstructureNotifyMask; +} + void CXWindowsPrimaryScreen::selectEvents( Display* display, Window w) const { @@ -419,8 +430,7 @@ void CXWindowsPrimaryScreen::selectEvents( return; // select events of interest - XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask | - PropertyChangeMask); + XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask); // recurse on child windows Window rw, pw, *cw; diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 1cdc1563..f6ab7a63 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -29,6 +29,7 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { // CXWindowsScreen overrides virtual void onOpenDisplay(); virtual void onCloseDisplay(); + virtual long getEventMask(Window) const; private: void selectEvents(Display*, Window) const; diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 51109584..4c55a5fd 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -672,7 +672,7 @@ void CXWindowsScreen::processClipboardRequest( clipboard.m_requests.erase(index); delete list; } - XSelectInput(m_display, requestor, NoEventMask); + XSelectInput(m_display, requestor, getEventMask(requestor)); } // request has been serviced @@ -765,9 +765,12 @@ bool CXWindowsScreen::sendClipboardData( list->push_back(request); // start watching requestor for property changes and - // destruction - XSelectInput(m_display, requestor, StructureNotifyMask | - PropertyChangeMask); + // destruction, in addition to other events required by + // the subclass. + XSelectInput(m_display, requestor, + getEventMask(requestor) | + StructureNotifyMask | + PropertyChangeMask); // FIXME -- handle Alloc errors (by returning false) // set property to INCR @@ -968,6 +971,11 @@ Time CXWindowsScreen::getCurrentTime(Window window) const Time CXWindowsScreen::getCurrentTimeNoLock( Window window) const { + // select property events on window + // note -- this will break clipboard transfer if used on a requestor + // window, so don't do that. + XSelectInput(m_display, window, getEventMask(window) | PropertyChangeMask); + // do a zero-length append to get the current time unsigned char dummy; XChangeProperty(m_display, window, m_atomSynergyTime, @@ -992,6 +1000,9 @@ Time CXWindowsScreen::getCurrentTimeNoLock( assert(xevent.xproperty.window == window); assert(xevent.xproperty.atom == m_atomSynergyTime); + // restore event mask + XSelectInput(m_display, window, getEventMask(window)); + return xevent.xproperty.time; } diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index ec8cfb7e..21e8d639 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -101,6 +101,9 @@ class CXWindowsScreen { // called by closeDisplay() to virtual void onCloseDisplay() = 0; + // get the X event mask required by the subclass for the given window + virtual long getEventMask(Window) const = 0; + private: struct CPropertyNotifyInfo { public: From 32b085e42e3554e5b09bd559082a7f4094f157c3 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 29 Apr 2002 14:12:48 +0000 Subject: [PATCH 055/807] Shortened timeout on waiting for clipboard response. --- synergy/CXWindowsScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 4c55a5fd..c82cb36c 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -365,7 +365,7 @@ bool CXWindowsScreen::getDisplayClipboard( &CXWindowsScreen::findSelectionNotify, (XPointer)&requestor) != True) { // return false if we've timed-out - if (timer.getTime() >= 1.0) + if (timer.getTime() >= 0.2) return false; // wait a bit From 3ca72b35f3727692618a67ecee319f101a89b3d0 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 29 Apr 2002 14:25:24 +0000 Subject: [PATCH 056/807] Added some validation of protocol message parameters. --- client/CClient.cpp | 11 +++++++++++ server/CServer.cpp | 5 +++++ server/CServerProtocol1_0.cpp | 18 +++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index b5f00de0..f41dc791 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -391,6 +391,12 @@ void CClient::onGrabClipboard() { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id, &seqNum); + log((CLOG_DEBUG "received clipboard %d grab", id)); + + // validate + if (id >= kClipboardEnd) { + return; + } // we no longer own the clipboard m_ownClipboard[id] = false; @@ -431,6 +437,11 @@ void CClient::onSetClipboard() } log((CLOG_DEBUG "received clipboard %d size=%d", id, data.size())); + // validate + if (id >= kClipboardEnd) { + return; + } + // unmarshall CClipboard clipboard; clipboard.unmarshall(data, 0); diff --git a/server/CServer.cpp b/server/CServer.cpp index a2e593fa..99fcca5f 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -135,6 +135,11 @@ UInt32 CServer::getActivePrimarySides() const void CServer::setInfo(const CString& client, SInt32 w, SInt32 h, SInt32 zoneSize) { + assert(!client.empty()); + assert(w > 0); + assert(h > 0); + assert(zoneSize >= 0); + CLock lock(&m_mutex); // client must be connected diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 8cfcff0a..c69b1144 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -179,7 +179,7 @@ void CServerProtocol1_0::recvInfo() log((CLOG_DEBUG "received client \"%s\" info size=%dx%d, zone=%d", getClient().c_str(), w, h, zoneInfo)); // validate - if (w == 0 || h == 0) { + if (w <= 0 || h <= 0 || zoneInfo < 0) { throw XBadClient(); } @@ -189,19 +189,35 @@ void CServerProtocol1_0::recvInfo() void CServerProtocol1_0::recvClipboard() { + // parse message ClipboardID id; UInt32 seqNum; CString data; CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); log((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getClient().c_str(), id, seqNum, data.size())); + + // validate + if (id >= kClipboardEnd) { + throw XBadClient(); + } + + // send update getServer()->setClipboard(id, seqNum, data); } void CServerProtocol1_0::recvGrabClipboard() { + // parse message ClipboardID id; UInt32 seqNum; CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getClient().c_str(), id, seqNum)); + + // validate + if (id >= kClipboardEnd) { + throw XBadClient(); + } + + // send update getServer()->grabClipboard(id, seqNum, getClient()); } From ea6b347b18a44135f2090a67a5b394f23685dc06 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 29 Apr 2002 14:40:01 +0000 Subject: [PATCH 057/807] Indentation changes. --- base/CFunctionJob.h | 4 +- base/CLog.h | 6 +- base/CStopwatch.cpp | 2 +- base/CStopwatch.h | 72 ++++++++++++------------ base/CString.h | 2 +- base/IInterface.h | 2 +- base/IJob.h | 2 +- base/TMethodJob.h | 4 +- base/XBase.h | 10 ++-- client/CClient.h | 6 +- client/CMSWindowsSecondaryScreen.cpp | 32 +++++------ client/CMSWindowsSecondaryScreen.h | 8 +-- client/CXWindowsSecondaryScreen.cpp | 18 +++--- client/CXWindowsSecondaryScreen.h | 8 +-- io/CBufferedInputStream.h | 4 +- io/CBufferedOutputStream.h | 6 +- io/CInputStreamFilter.h | 6 +- io/COutputStreamFilter.h | 6 +- io/CStreamBuffer.h | 4 +- io/IInputStream.h | 2 +- io/IOutputStream.h | 2 +- io/XIO.h | 8 +-- mt/CCondVar.cpp | 18 +++--- mt/CCondVar.h | 10 ++-- mt/CLock.h | 6 +- mt/CMutex.cpp | 14 ++--- mt/CMutex.h | 12 ++-- mt/CThread.h | 84 ++++++++++++++-------------- mt/CThreadRep.cpp | 18 +++--- mt/CThreadRep.h | 40 ++++++------- mt/CTimerThread.h | 6 +- mt/XThread.h | 10 ++-- net/CNetwork.h | 4 +- net/CNetworkAddress.cpp | 10 ++-- net/CNetworkAddress.h | 4 +- net/CSocketInputStream.h | 4 +- net/CSocketOutputStream.h | 6 +- net/CSocketStreamBuffer.h | 4 +- net/CTCPListenSocket.h | 4 +- net/CTCPSocket.h | 6 +- net/IListenSocket.h | 2 +- net/ISocket.h | 2 +- net/XNetwork.h | 16 +++--- net/XSocket.h | 14 ++--- server/CMSWindowsPrimaryScreen.cpp | 48 ++++++++-------- server/CMSWindowsPrimaryScreen.h | 8 +-- server/CScreenMap.h | 6 +- server/CServer.cpp | 24 ++++---- server/CServer.h | 20 +++---- server/CServerProtocol.h | 6 +- server/CServerProtocol1_0.h | 4 +- server/CSynergyHook.cpp | 16 +++--- server/CXWindowsPrimaryScreen.cpp | 42 +++++++------- server/CXWindowsPrimaryScreen.h | 8 +-- synergy/CClipboard.h | 6 +- synergy/CInputPacketStream.h | 6 +- synergy/CMSWindowsClipboard.cpp | 10 ++-- synergy/CMSWindowsClipboard.h | 6 +- synergy/CMSWindowsScreen.h | 8 +-- synergy/COutputPacketStream.h | 2 +- synergy/CProtocolUtil.cpp | 74 ++++++++++++------------ synergy/CProtocolUtil.h | 6 +- synergy/CTCPSocketFactory.h | 2 +- synergy/CXWindowsClipboard.h | 2 +- synergy/CXWindowsScreen.cpp | 8 +-- synergy/CXWindowsScreen.h | 16 +++--- synergy/IClipboard.h | 2 +- synergy/IPrimaryScreen.h | 2 +- synergy/ISecondaryScreen.h | 2 +- synergy/IServerProtocol.h | 4 +- synergy/ISocketFactory.h | 2 +- synergy/XSynergy.h | 8 +-- 72 files changed, 428 insertions(+), 428 deletions(-) diff --git a/base/CFunctionJob.h b/base/CFunctionJob.h index 346d28e5..f3b5272d 100644 --- a/base/CFunctionJob.h +++ b/base/CFunctionJob.h @@ -4,13 +4,13 @@ #include "IJob.h" class CFunctionJob : public IJob { - public: +public: CFunctionJob(void (*func)(void*), void* arg = NULL); // IJob overrides virtual void run(); - private: +private: void (*m_func)(void*); void* m_arg; }; diff --git a/base/CLog.h b/base/CLog.h index 2775df16..ae8fdc10 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -4,19 +4,19 @@ #include class CLog { - public: +public: typedef void (*Outputter)(const char*); static void print(const char*, ...); static void printt(const char* file, int line, const char*, ...); static void setOutputter(Outputter); - private: +private: static void output(int priority, char* msg); static char* vsprint(int pad, char*, int len, const char*, va_list); static int nprint(const char*, va_list); - private: +private: static Outputter s_outputter; }; diff --git a/base/CStopwatch.cpp b/base/CStopwatch.cpp index f7adac6b..6a346a7f 100644 --- a/base/CStopwatch.cpp +++ b/base/CStopwatch.cpp @@ -135,7 +135,7 @@ static PTimeGetTime s_tgt = NULL; // class CStopwatchInit { - public: +public: CStopwatchInit(); ~CStopwatchInit(); }; diff --git a/base/CStopwatch.h b/base/CStopwatch.h index da0c1ab5..791729f2 100644 --- a/base/CStopwatch.h +++ b/base/CStopwatch.h @@ -4,54 +4,54 @@ #include "common.h" class CStopwatch { - public: - // the default constructor does an implicit reset() or setTrigger(). - // if triggered == false then the clock starts ticking. - CStopwatch(bool triggered = false); - ~CStopwatch(); +public: + // the default constructor does an implicit reset() or setTrigger(). + // if triggered == false then the clock starts ticking. + CStopwatch(bool triggered = false); + ~CStopwatch(); - // manipulators + // manipulators - // set the start time to the current time, returning the time since - // the last reset. this does not remove the trigger if it's set nor - // does it start a stopped clock. if the clock is stopped then - // subsequent reset()'s will return 0. - double reset(); + // set the start time to the current time, returning the time since + // the last reset. this does not remove the trigger if it's set nor + // does it start a stopped clock. if the clock is stopped then + // subsequent reset()'s will return 0. + double reset(); - // stop and start the stopwatch. while stopped, no time elapses. - // stop() does not remove the trigger but start() does, even if - // the clock was already started. - void stop(); - void start(); + // stop and start the stopwatch. while stopped, no time elapses. + // stop() does not remove the trigger but start() does, even if + // the clock was already started. + void stop(); + void start(); - // setTrigger() stops the clock like stop() except there's an - // implicit start() the next time (non-const) getTime() is called. - // this is useful when you want the clock to start the first time - // you check it. - void setTrigger(); + // setTrigger() stops the clock like stop() except there's an + // implicit start() the next time (non-const) getTime() is called. + // this is useful when you want the clock to start the first time + // you check it. + void setTrigger(); - // return the time since the last reset() (or call reset() and - // return zero if the trigger is set). - double getTime(); + // return the time since the last reset() (or call reset() and + // return zero if the trigger is set). + double getTime(); operator double(); - // accessors + // accessors - // returns true if the watch is stopped - bool isStopped() const; + // returns true if the watch is stopped + bool isStopped() const; - // return the time since the last reset(). these cannot trigger - // the clock to start so if the trigger is set it's as if it wasn't. - double getTime() const; + // return the time since the last reset(). these cannot trigger + // the clock to start so if the trigger is set it's as if it wasn't. + double getTime() const; operator double() const; - private: - double getClock() const; +private: + double getClock() const; - private: - double m_mark; - bool m_triggered; - bool m_stopped; +private: + double m_mark; + bool m_triggered; + bool m_stopped; }; #endif diff --git a/base/CString.h b/base/CString.h index 88041939..547152e3 100644 --- a/base/CString.h +++ b/base/CString.h @@ -15,7 +15,7 @@ #define _CS(_x) _x class CString : public std::string { - public: +public: typedef char _e; typedef _e CharT; typedef std::allocator<_e> _a; diff --git a/base/IInterface.h b/base/IInterface.h index bb3676bc..52b4bde4 100644 --- a/base/IInterface.h +++ b/base/IInterface.h @@ -4,7 +4,7 @@ #include "common.h" class IInterface { - public: +public: virtual ~IInterface() { } }; diff --git a/base/IJob.h b/base/IJob.h index 3efb1b51..f13a359c 100644 --- a/base/IJob.h +++ b/base/IJob.h @@ -4,7 +4,7 @@ #include "IInterface.h" class IJob : public IInterface { - public: +public: virtual void run() = 0; }; diff --git a/base/TMethodJob.h b/base/TMethodJob.h index 8e889b3d..f9514bf2 100644 --- a/base/TMethodJob.h +++ b/base/TMethodJob.h @@ -5,13 +5,13 @@ template class TMethodJob : public IJob { - public: +public: TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL); // IJob overrides virtual void run(); - private: +private: T* m_object; void (T::*m_method)(void*); void* m_arg; diff --git a/base/XBase.h b/base/XBase.h index 196f2122..8ab63b5f 100644 --- a/base/XBase.h +++ b/base/XBase.h @@ -5,7 +5,7 @@ #include class XBase : public std::exception { - public: +public: XBase(); XBase(const CString& msg); virtual ~XBase(); @@ -13,7 +13,7 @@ class XBase : public std::exception { // std::exception overrides virtual const char* what() const; - protected: +protected: // returns a human readable string describing the exception virtual CString getWhat() const throw() = 0; @@ -21,12 +21,12 @@ class XBase : public std::exception { virtual CString format(const char* id, const char* defaultFormat, ...) const throw(); - private: +private: mutable CString m_what; }; class MXErrno { - public: +public: MXErrno(); MXErrno(int); @@ -37,7 +37,7 @@ class MXErrno { int getErrno() const; const char* getErrstr() const; - private: +private: int m_errno; }; diff --git a/client/CClient.h b/client/CClient.h index f2cf0628..ac471ecd 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -13,7 +13,7 @@ class IOutputStream; class ISecondaryScreen; class CClient { - public: +public: CClient(const CString& clientName); ~CClient(); @@ -27,7 +27,7 @@ class CClient { // accessors - private: +private: void runSession(void*); // open/close the primary screen @@ -49,7 +49,7 @@ class CClient { void onMouseMove(); void onMouseWheel(); - private: +private: CMutex m_mutex; CString m_name; IInputStream* m_input; diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 94507e4e..74ce2874 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -31,14 +31,14 @@ static DWORD s_thread = 0; static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { - case WM_INITDIALOG: + case WM_INITDIALOG: return TRUE; - case WM_CLOSE: + case WM_CLOSE: PostQuitMessage(0); return TRUE; - case WM_APP: + case WM_APP: if (!s_logMore.empty()) { if (s_log.size() > 20000) s_log = s_logMore; @@ -194,19 +194,19 @@ void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) // map button id to button flag DWORD flags; switch (button) { - case kButtonLeft: + case kButtonLeft: flags = MOUSEEVENTF_LEFTDOWN; break; - case kButtonMiddle: + case kButtonMiddle: flags = MOUSEEVENTF_MIDDLEDOWN; break; - case kButtonRight: + case kButtonRight: flags = MOUSEEVENTF_RIGHTDOWN; break; - default: + default: return; } @@ -219,19 +219,19 @@ void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) // map button id to button flag DWORD flags; switch (button) { - case kButtonLeft: + case kButtonLeft: flags = MOUSEEVENTF_LEFTUP; break; - case kButtonMiddle: + case kButtonMiddle: flags = MOUSEEVENTF_MIDDLEUP; break; - case kButtonRight: + case kButtonRight: flags = MOUSEEVENTF_RIGHTUP; break; - default: + default: return; } @@ -361,12 +361,12 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( WPARAM wParam, LPARAM lParam) { switch (msg) { - // FIXME -- handle display changes - case WM_PAINT: + // FIXME -- handle display changes + case WM_PAINT: ValidateRect(hwnd, NULL); return 0; - case WM_ACTIVATEAPP: + case WM_ACTIVATEAPP: if (wParam == FALSE) { // some other app activated. hide the hider window. log((CLOG_INFO "show cursor")); @@ -374,7 +374,7 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( } break; - case WM_DRAWCLIPBOARD: + case WM_DRAWCLIPBOARD: log((CLOG_DEBUG "clipboard was taken")); // first pass it on @@ -389,7 +389,7 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( } return 0; - case WM_CHANGECBCHAIN: + case WM_CHANGECBCHAIN: if (m_nextClipboardWindow == (HWND)wParam) m_nextClipboardWindow = (HWND)lParam; else diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 12763e60..196817af 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -5,7 +5,7 @@ #include "ISecondaryScreen.h" class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen { - public: +public: CMSWindowsSecondaryScreen(); virtual ~CMSWindowsSecondaryScreen(); @@ -29,17 +29,17 @@ class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScre virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(IClipboard*) const; - protected: +protected: // CMSWindowsScreen overrides virtual bool onPreTranslate(MSG*); virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); virtual void onOpenDisplay(); virtual void onCloseDisplay(); - private: +private: UINT mapKey(KeyID, KeyModifierMask) const; - private: +private: CClient* m_client; HWND m_window; HWND m_nextClipboardWindow; diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index d4d1c912..cc935285 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -38,7 +38,7 @@ void CXWindowsSecondaryScreen::run() // handle event switch (xevent.type) { - case MappingNotify: { + case MappingNotify: { // keyboard mapping changed CDisplayLock display(this); XRefreshKeyboardMapping(&xevent.xmapping); @@ -47,17 +47,17 @@ void CXWindowsSecondaryScreen::run() updateModifierMap(display); updateModifiers(display); break; - } + } - case LeaveNotify: { + case LeaveNotify: { // mouse moved out of hider window somehow. hide the window. assert(m_window != None); CDisplayLock display(this); XUnmapWindow(display, m_window); break; - } + } - case SelectionClear: + case SelectionClear: // we just lost the selection. that means someone else // grabbed the selection so this screen is now the // selection owner. report that to the server. @@ -68,7 +68,7 @@ void CXWindowsSecondaryScreen::run() } break; - case SelectionNotify: + case SelectionNotify: // notification of selection transferred. we shouldn't // get this here because we handle them in the selection // retrieval methods. we'll just delete the property @@ -79,7 +79,7 @@ void CXWindowsSecondaryScreen::run() } break; - case SelectionRequest: + case SelectionRequest: // somebody is asking for clipboard data if (xevent.xselectionrequest.owner == m_window) { addClipboardRequest(m_window, @@ -105,7 +105,7 @@ void CXWindowsSecondaryScreen::run() } break; - case PropertyNotify: + case PropertyNotify: // clipboard transfers involve property changes so forward // the event to the superclass. we only care about the // deletion of properties. @@ -116,7 +116,7 @@ void CXWindowsSecondaryScreen::run() } break; - case DestroyNotify: + case DestroyNotify: // looks like one of the windows that requested a clipboard // transfer has gone bye-bye. destroyClipboardRequest(xevent.xdestroywindow.window); diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 29809de6..4e0f8c2e 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -6,7 +6,7 @@ #include class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen { - public: +public: CXWindowsSecondaryScreen(); virtual ~CXWindowsSecondaryScreen(); @@ -30,13 +30,13 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; - protected: +protected: // CXWindowsScreen overrides virtual void onOpenDisplay(); virtual void onCloseDisplay(); virtual long getEventMask(Window) const; - private: +private: struct KeyCodeMask { public: KeyCode keycode; @@ -64,7 +64,7 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen void updateModifierMap(Display* display); static bool isToggleKeysym(KeySym); - private: +private: CClient* m_client; Window m_window; diff --git a/io/CBufferedInputStream.h b/io/CBufferedInputStream.h index 28fbdd27..d1abfea2 100644 --- a/io/CBufferedInputStream.h +++ b/io/CBufferedInputStream.h @@ -9,7 +9,7 @@ class CMutex; class IJob; class CBufferedInputStream : public IInputStream { - public: +public: CBufferedInputStream(CMutex*, IJob* adoptedCloseCB); ~CBufferedInputStream(); @@ -39,7 +39,7 @@ class CBufferedInputStream : public IInputStream { virtual UInt32 read(void*, UInt32 count); virtual UInt32 getSize() const; - private: +private: CMutex* m_mutex; CCondVar m_empty; IJob* m_closeCB; diff --git a/io/CBufferedOutputStream.h b/io/CBufferedOutputStream.h index a88e2357..1afbc118 100644 --- a/io/CBufferedOutputStream.h +++ b/io/CBufferedOutputStream.h @@ -8,7 +8,7 @@ class CMutex; class IJob; class CBufferedOutputStream : public IOutputStream { - public: +public: CBufferedOutputStream(CMutex*, IJob* adoptedCloseCB); ~CBufferedOutputStream(); @@ -33,10 +33,10 @@ class CBufferedOutputStream : public IOutputStream { virtual UInt32 write(const void*, UInt32 count); virtual void flush(); - private: +private: UInt32 getSizeWithLock() const; - private: +private: CMutex* m_mutex; IJob* m_closeCB; CStreamBuffer m_buffer; diff --git a/io/CInputStreamFilter.h b/io/CInputStreamFilter.h index 0e4dd41b..85487226 100644 --- a/io/CInputStreamFilter.h +++ b/io/CInputStreamFilter.h @@ -4,7 +4,7 @@ #include "IInputStream.h" class CInputStreamFilter : public IInputStream { - public: +public: CInputStreamFilter(IInputStream*, bool adoptStream = true); ~CInputStreamFilter(); @@ -17,10 +17,10 @@ class CInputStreamFilter : public IInputStream { virtual UInt32 read(void*, UInt32 maxCount) = 0; virtual UInt32 getSize() const = 0; - protected: +protected: IInputStream* getStream() const; - private: +private: IInputStream* m_stream; bool m_adopted; }; diff --git a/io/COutputStreamFilter.h b/io/COutputStreamFilter.h index c6b02a46..781fa2d4 100644 --- a/io/COutputStreamFilter.h +++ b/io/COutputStreamFilter.h @@ -4,7 +4,7 @@ #include "IOutputStream.h" class COutputStreamFilter : public IOutputStream { - public: +public: COutputStreamFilter(IOutputStream*, bool adoptStream = true); ~COutputStreamFilter(); @@ -17,10 +17,10 @@ class COutputStreamFilter : public IOutputStream { virtual UInt32 write(const void*, UInt32 count) = 0; virtual void flush() = 0; - protected: +protected: IOutputStream* getStream() const; - private: +private: IOutputStream* m_stream; bool m_adopted; }; diff --git a/io/CStreamBuffer.h b/io/CStreamBuffer.h index 7333de90..2338b8da 100644 --- a/io/CStreamBuffer.h +++ b/io/CStreamBuffer.h @@ -6,7 +6,7 @@ #include class CStreamBuffer { - public: +public: CStreamBuffer(); ~CStreamBuffer(); @@ -25,7 +25,7 @@ class CStreamBuffer { // return the number of bytes in the buffer UInt32 getSize() const; - private: +private: static const UInt32 kChunkSize; typedef std::vector Chunk; diff --git a/io/IInputStream.h b/io/IInputStream.h index 3650dfd4..700e11a0 100644 --- a/io/IInputStream.h +++ b/io/IInputStream.h @@ -6,7 +6,7 @@ #include "XIO.h" class IInputStream : public IInterface { - public: +public: // manipulators // close the stream diff --git a/io/IOutputStream.h b/io/IOutputStream.h index ca1a196c..688f5af7 100644 --- a/io/IOutputStream.h +++ b/io/IOutputStream.h @@ -6,7 +6,7 @@ #include "XIO.h" class IOutputStream : public IInterface { - public: +public: // manipulators // close the stream diff --git a/io/XIO.h b/io/XIO.h index b468e764..743c0aaa 100644 --- a/io/XIO.h +++ b/io/XIO.h @@ -7,25 +7,25 @@ class XIO : public XBase { }; class XIOErrno : public XIO, public MXErrno { - public: +public: XIOErrno(); XIOErrno(int); }; class XIOClose: public XIOErrno { - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); }; class XIOClosed : public XIO { - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); }; class XIOEndOfStream : public XIO { - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); }; diff --git a/mt/CCondVar.cpp b/mt/CCondVar.cpp index 7e39e7f4..9aeb7d8f 100644 --- a/mt/CCondVar.cpp +++ b/mt/CCondVar.cpp @@ -148,14 +148,14 @@ bool CCondVarBase::wait( } switch (status) { - case 0: + case 0: // success return true; - case ETIMEDOUT: + case ETIMEDOUT: return false; - default: + default: assert(0 && "condition variable wait error"); return false; } @@ -183,8 +183,8 @@ void CCondVarBase::init() { // prepare events HANDLE* events = new HANDLE[2]; - events[kSignal] = CreateEvent(NULL, FALSE, FALSE, NULL); - events[kBroadcast] = CreateEvent(NULL, TRUE, FALSE, NULL); + events[kSignal] = CreateEvent(NULL, FALSE, FALSE, NULL); + events[kBroadcast] = CreateEvent(NULL, TRUE, FALSE, NULL); // prepare members m_cond = reinterpret_cast(events); @@ -194,8 +194,8 @@ void CCondVarBase::init() void CCondVarBase::fini() { HANDLE* events = reinterpret_cast(m_cond); - CloseHandle(events[kSignal]); - CloseHandle(events[kBroadcast]); + CloseHandle(events[kSignal]); + CloseHandle(events[kBroadcast]); delete[] events; } @@ -210,7 +210,7 @@ void CCondVarBase::signal() // wake one thread if anybody is waiting if (hasWaiter) - SetEvent(reinterpret_cast(m_cond)[kSignal]); + SetEvent(reinterpret_cast(m_cond)[kSignal]); } void CCondVarBase::broadcast() @@ -224,7 +224,7 @@ void CCondVarBase::broadcast() // wake all threads if anybody is waiting if (hasWaiter) - SetEvent(reinterpret_cast(m_cond)[kBroadcast]); + SetEvent(reinterpret_cast(m_cond)[kBroadcast]); } bool CCondVarBase::wait( diff --git a/mt/CCondVar.h b/mt/CCondVar.h index 083962ab..b0663db8 100644 --- a/mt/CCondVar.h +++ b/mt/CCondVar.h @@ -7,7 +7,7 @@ class CStopwatch; class CCondVarBase { - public: +public: // mutex must be supplied. all condition variables have an // associated mutex. the copy c'tor uses the same mutex as the // argument and is otherwise like the default c'tor. @@ -45,7 +45,7 @@ class CCondVarBase { // get the mutex passed to the c'tor CMutex* getMutex() const; - private: +private: void init(); void fini(); @@ -53,7 +53,7 @@ class CCondVarBase { CCondVarBase(const CCondVarBase&); CCondVarBase& operator=(const CCondVarBase&); - private: +private: CMutex* m_mutex; void* m_cond; @@ -66,7 +66,7 @@ class CCondVarBase { template class CCondVar : public CCondVarBase { - public: +public: CCondVar(CMutex* mutex, const T&); CCondVar(const CCondVar&); ~CCondVar(); @@ -85,7 +85,7 @@ class CCondVar : public CCondVarBase { // calling this method. operator const T&() const; - private: +private: T m_data; }; diff --git a/mt/CLock.h b/mt/CLock.h index 9ab933ce..d554733f 100644 --- a/mt/CLock.h +++ b/mt/CLock.h @@ -7,17 +7,17 @@ class CMutex; class CCondVarBase; class CLock { - public: +public: CLock(const CMutex* mutex); CLock(const CCondVarBase* cv); ~CLock(); - private: +private: // not implemented CLock(const CLock&); CLock& operator=(const CLock&); - private: +private: const CMutex* m_mutex; }; diff --git a/mt/CMutex.cpp b/mt/CMutex.cpp index b9ceb5d5..2d092b8d 100644 --- a/mt/CMutex.cpp +++ b/mt/CMutex.cpp @@ -60,19 +60,19 @@ void CMutex::lock() const int status = pthread_mutex_lock(mutex); switch (status) { - case 0: + case 0: // success return; - case EDEADLK: + case EDEADLK: assert(0 && "lock already owned"); break; - case EAGAIN: + case EAGAIN: assert(0 && "too many recursive locks"); break; - default: + default: log((CLOG_ERR "pthread_mutex_lock status %d", status)); assert(0 && "unexpected error"); } @@ -84,15 +84,15 @@ void CMutex::unlock() const int status = pthread_mutex_unlock(mutex); switch (status) { - case 0: + case 0: // success return; - case EPERM: + case EPERM: assert(0 && "thread doesn't own a lock"); break; - default: + default: log((CLOG_ERR "pthread_mutex_unlock status %d", status)); assert(0 && "unexpected error"); } diff --git a/mt/CMutex.h b/mt/CMutex.h index 77612366..cf43ea70 100644 --- a/mt/CMutex.h +++ b/mt/CMutex.h @@ -5,7 +5,7 @@ // recursive mutex class class CMutex { - public: +public: // copy c'tor is equivalent to default c'tor. it's here to // allow copying of objects that have mutexes. CMutex(); @@ -14,22 +14,22 @@ class CMutex { // manipulators - // this has no effect. it's only here to allow assignment of + // this has no effect. it's only here to allow assignment of // objects that have mutexes. - CMutex& operator=(const CMutex&); + CMutex& operator=(const CMutex&); // accessors void lock() const; void unlock() const; - private: +private: void init(); void fini(); - private: +private: friend class CCondVarBase; - void* m_mutex; + void* m_mutex; }; #endif diff --git a/mt/CThread.h b/mt/CThread.h index f505ad00..982a1912 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -8,44 +8,44 @@ class CThreadRep; // note -- do not derive from this class class CThread { - public: - // create and start a new thread executing the job. +public: + // create and start a new thread executing the job. // the user data can be retrieved with getUserData(). - CThread(IJob* adopted, void* userData = 0); + CThread(IJob* adopted, void* userData = 0); - // make a new thread object that refers to an existing thread. + // make a new thread object that refers to an existing thread. // this does *not* start a new thread. - CThread(const CThread&); + CThread(const CThread&); - // release thread. this does not terminate the thread. a thread - // will keep running until the job completes or calls exit(). - ~CThread(); + // release thread. this does not terminate the thread. a thread + // will keep running until the job completes or calls exit(). + ~CThread(); - // manipulators + // manipulators - // assign thread. this has no effect on the threads. it simply - // makes this thread object refer to another thread. it does *not* + // assign thread. this has no effect on the threads. it simply + // makes this thread object refer to another thread. it does *not* // start a new thread. - CThread& operator=(const CThread&); + CThread& operator=(const CThread&); // initialize the thread library. this must be called before // any other thread methods or creating a thread object. it // is harmless to call init() multiple times. static void init(); - // the calling thread sleeps for the given number of seconds. if - // timeout <= 0.0 then the call returns immediately. if timeout + // the calling thread sleeps for the given number of seconds. if + // timeout <= 0.0 then the call returns immediately. if timeout // == 0.0 then the calling thread yields the CPU. // (cancellation point) - static void sleep(double timeout); + static void sleep(double timeout); - // terminate the calling thread. this function does not return but - // the stack is unwound and automatic objects are destroyed, as if - // exit() threw an exception (which is, in fact, what it does). the - // argument is saved as the result returned by getResult(). if you - // have a catch(...) block then you should add the following before - // it to avoid catching the exit: catch(CThreadExit&) { throw; } - static void exit(void*); + // terminate the calling thread. this function does not return but + // the stack is unwound and automatic objects are destroyed, as if + // exit() threw an exception (which is, in fact, what it does). the + // argument is saved as the result returned by getResult(). if you + // have a catch(...) block then you should add the following before + // it to avoid catching the exit: catch(CThreadExit&) { throw; } + static void exit(void*); // enable/disable cancellation. default is enabled. this is not // a cancellation point so if you enabled cancellation and want to @@ -76,26 +76,26 @@ class CThread { // exception. clients that catch(...) must either rethrow the // exception or include a catch (XThreadCancel) handler that // rethrows. - void cancel(); + void cancel(); - // change the priority of the thread. normal priority is 0, 1 is + // change the priority of the thread. normal priority is 0, 1 is // the next lower, etc. -1 is the next higher, etc. but boosting // the priority may not be available. - void setPriority(int n); + void setPriority(int n); - // accessors + // accessors - // return a thread object representing the calling thread - static CThread getCurrentThread(); + // return a thread object representing the calling thread + static CThread getCurrentThread(); - // get the user data passed to the constructor for the current + // get the user data passed to the constructor for the current // thread. - static void* getUserData(); + static void* getUserData(); - // testCancel() does nothing but is a cancellation point. call + // testCancel() does nothing but is a cancellation point. call // this to make a function itself a cancellation point. // (cancellation point) - static void testCancel(); + static void testCancel(); // waits for the thread to terminate (by exit() or cancel() or // by returning from the thread job). returns immediately if @@ -105,30 +105,30 @@ class CThread { // (cancellation point) bool wait(double timeout = -1.0) const; - // get the exit result. does an implicit wait(). returns NULL + // get the exit result. does an implicit wait(). returns NULL // immediately if called by a thread on itself. returns NULL for // threads that were cancelled. // (cancellation point) void* getResult() const; - // compare threads for (in)equality - bool operator==(const CThread&) const; - bool operator!=(const CThread&) const; + // compare threads for (in)equality + bool operator==(const CThread&) const; + bool operator!=(const CThread&) const; - private: - CThread(CThreadRep*); +private: + CThread(CThreadRep*); - private: - CThreadRep* m_rep; +private: + CThreadRep* m_rep; }; // disables cancellation in the c'tor and enables it in the d'tor. class CThreadMaskCancel { - public: +public: CThreadMaskCancel() : m_old(CThread::enableCancel(false)) { } ~CThreadMaskCancel() { CThread::enableCancel(m_old); } - private: +private: bool m_old; }; diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index e40df753..4b2de42a 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -518,15 +518,15 @@ bool CThreadRep::wait(CThreadRep* target, double timeout) // handle result switch (result) { - case WAIT_OBJECT_0 + 0: + case WAIT_OBJECT_0 + 0: // target thread terminated return true; - case WAIT_OBJECT_0 + 1: + case WAIT_OBJECT_0 + 1: // this thread was cancelled. does not return. testCancel(); - default: + default: // error return false; } @@ -536,16 +536,16 @@ void CThreadRep::setPriority(int n) { if (n < 0) { switch (-n) { - case 1: n = THREAD_PRIORITY_ABOVE_NORMAL; break; - default: n = THREAD_PRIORITY_HIGHEST; break; + case 1: n = THREAD_PRIORITY_ABOVE_NORMAL; break; + default: n = THREAD_PRIORITY_HIGHEST; break; } } else { switch (n) { - case 0: n = THREAD_PRIORITY_NORMAL; break; - case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; - case 2: n = THREAD_PRIORITY_LOWEST; break; - default: n = THREAD_PRIORITY_IDLE; break; + case 0: n = THREAD_PRIORITY_NORMAL; break; + case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; + case 2: n = THREAD_PRIORITY_LOWEST; break; + default: n = THREAD_PRIORITY_IDLE; break; } } SetThreadPriority(m_thread, n); diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h index 3a14ba09..c0c7b9a0 100644 --- a/mt/CThreadRep.h +++ b/mt/CThreadRep.h @@ -13,7 +13,7 @@ class CMutex; class IJob; class CThreadRep { - public: +public: CThreadRep(IJob*, void* userData); // manipulators @@ -25,9 +25,9 @@ class CThreadRep { void ref(); void unref(); - // the calling thread sleeps for t seconds. if t == 0.0 then - // the thread yields the CPU. - void sleep(double timeout); + // the calling thread sleeps for t seconds. if t == 0.0 then + // the thread yields the CPU. + void sleep(double timeout); // cancel the thread void cancel(); @@ -42,16 +42,16 @@ class CThreadRep { // wait for thread to exit or for current thread to cancel bool wait(CThreadRep*, double timeout); - // set the priority - void setPriority(int n); + // set the priority + void setPriority(int n); - // accessors + // accessors - // get the exit result for this thread. thread must be terminated. - void* getResult() const; + // get the exit result for this thread. thread must be terminated. + void* getResult() const; - // get the user data passed to the constructor - void* getUserData() const; + // get the user data passed to the constructor + void* getUserData() const; // get the current cancellable state bool isCancellable() const; @@ -63,14 +63,14 @@ class CThreadRep { HANDLE getCancelEvent() const; #endif - // return the thread rep for the calling thread. the returned - // rep has been ref()'d. - static CThreadRep* getCurrentThreadRep(); + // return the thread rep for the calling thread. the returned + // rep has been ref()'d. + static CThreadRep* getCurrentThreadRep(); - protected: +protected: virtual ~CThreadRep(); - private: +private: // internal constructor CThreadRep(); @@ -94,7 +94,7 @@ class CThreadRep { CThreadRep(const CThreadRep&); CThreadRep& operator=(const CThreadRep&); - private: +private: static CMutex* s_mutex; static CThreadRep* s_head; @@ -127,18 +127,18 @@ class CThreadRep { // class CThreadPtr { - public: +public: CThreadPtr(CThreadRep* rep) : m_rep(rep) { } ~CThreadPtr() { m_rep->unref(); } CThreadRep* operator->() const { return m_rep; } - private: +private: // not implemented CThreadPtr(const CThreadPtr&); CThreadPtr& operator=(const CThreadPtr&); - private: +private: CThreadRep* m_rep; }; diff --git a/mt/CTimerThread.h b/mt/CTimerThread.h index 48ca64cb..817642d3 100644 --- a/mt/CTimerThread.h +++ b/mt/CTimerThread.h @@ -6,18 +6,18 @@ class CThread; class CTimerThread { - public: +public: CTimerThread(double timeout); ~CTimerThread(); - private: +private: void timer(void*); // not implemented CTimerThread(const CTimerThread&); CTimerThread& operator=(const CTimerThread&); - private: +private: double m_timeout; CThread* m_callingThread; CThread* m_timingThread; diff --git a/mt/XThread.h b/mt/XThread.h index 3fd7441f..bc56a44c 100644 --- a/mt/XThread.h +++ b/mt/XThread.h @@ -10,12 +10,12 @@ class XThread { }; // must not throw this type but must rethrow it if caught (by // XThreadExit, XThread, or ...). class XThreadExit : public XThread { - public: - XThreadExit(void* result) : m_result(result) { } - ~XThreadExit() { } +public: + XThreadExit(void* result) : m_result(result) { } + ~XThreadExit() { } - public: - void* m_result; +public: + void* m_result; }; // thrown to cancel a thread. clients must not throw this type, but diff --git a/net/CNetwork.h b/net/CNetwork.h index fdbb2ae2..fcc65e40 100644 --- a/net/CNetwork.h +++ b/net/CNetwork.h @@ -26,7 +26,7 @@ typedef int ssize_t; // FIXME -- must handle htonl and ilk when defined as macros class CNetwork { - public: +public: #if defined(CONFIG_PLATFORM_WIN32) typedef SOCKET Socket; typedef struct sockaddr Address; @@ -129,7 +129,7 @@ class CNetwork { static int (PASCAL FAR *gethosterror)(void); #if defined(CONFIG_PLATFORM_WIN32) - private: +private: static void init2(HMODULE); static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout); static ssize_t PASCAL FAR read2(Socket s, void FAR * buf, size_t len); diff --git a/net/CNetworkAddress.cpp b/net/CNetworkAddress.cpp index a07d5ac7..1d832475 100644 --- a/net/CNetworkAddress.cpp +++ b/net/CNetworkAddress.cpp @@ -24,15 +24,15 @@ CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) struct hostent* hent = CNetwork::gethostbyname(hostname.c_str()); if (hent == NULL) { switch (CNetwork::gethosterror()) { - case CNetwork::kHOST_NOT_FOUND: + case CNetwork::kHOST_NOT_FOUND: throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); - case CNetwork::kNO_DATA: + case CNetwork::kNO_DATA: throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); - case CNetwork::kNO_RECOVERY: - case CNetwork::kTRY_AGAIN: - default: + case CNetwork::kNO_RECOVERY: + case CNetwork::kTRY_AGAIN: + default: throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); } } diff --git a/net/CNetworkAddress.h b/net/CNetworkAddress.h index 18d69812..6b1729d5 100644 --- a/net/CNetworkAddress.h +++ b/net/CNetworkAddress.h @@ -8,7 +8,7 @@ class CString; class CNetworkAddress { - public: +public: CNetworkAddress(UInt16 port); CNetworkAddress(const CString& hostname, UInt16 port); ~CNetworkAddress(); @@ -20,7 +20,7 @@ class CNetworkAddress { const CNetwork::Address* getAddress() const; CNetwork::AddressLength getAddressLength() const; - private: +private: CNetwork::Address m_address; }; diff --git a/net/CSocketInputStream.h b/net/CSocketInputStream.h index 38096538..3e686df3 100644 --- a/net/CSocketInputStream.h +++ b/net/CSocketInputStream.h @@ -9,7 +9,7 @@ class CMutex; class IJob; class CSocketInputStream : public IInputStream { - public: +public: CSocketInputStream(CMutex*, IJob* adoptedCloseCB); ~CSocketInputStream(); @@ -30,7 +30,7 @@ class CSocketInputStream : public IInputStream { virtual UInt32 read(void*, UInt32 count); virtual UInt32 getSize() const; - private: +private: CMutex* m_mutex; CCondVar m_empty; IJob* m_closeCB; diff --git a/net/CSocketOutputStream.h b/net/CSocketOutputStream.h index c959c140..4f65b5c1 100644 --- a/net/CSocketOutputStream.h +++ b/net/CSocketOutputStream.h @@ -8,7 +8,7 @@ class CMutex; class IJob; class CSocketOutputStream : public IOutputStream { - public: +public: CSocketOutputStream(CMutex*, IJob* adoptedCloseCB); ~CSocketOutputStream(); @@ -30,10 +30,10 @@ class CSocketOutputStream : public IOutputStream { virtual UInt32 write(const void*, UInt32 count); virtual void flush(); - private: +private: UInt32 getSizeWithLock() const; - private: +private: CMutex* m_mutex; IJob* m_closeCB; CSocketStreamBuffer m_buffer; diff --git a/net/CSocketStreamBuffer.h b/net/CSocketStreamBuffer.h index 545fd25f..cf6f2344 100644 --- a/net/CSocketStreamBuffer.h +++ b/net/CSocketStreamBuffer.h @@ -6,7 +6,7 @@ #include class CSocketStreamBuffer { - public: +public: CSocketStreamBuffer(); ~CSocketStreamBuffer(); @@ -25,7 +25,7 @@ class CSocketStreamBuffer { // return the number of bytes in the buffer UInt32 getSize() const; - private: +private: static const UInt32 kChunkSize; typedef std::vector Chunk; diff --git a/net/CTCPListenSocket.h b/net/CTCPListenSocket.h index 20031d17..3120d00b 100644 --- a/net/CTCPListenSocket.h +++ b/net/CTCPListenSocket.h @@ -5,7 +5,7 @@ #include "CNetwork.h" class CTCPListenSocket : public IListenSocket { - public: +public: CTCPListenSocket(); ~CTCPListenSocket(); @@ -18,7 +18,7 @@ class CTCPListenSocket : public IListenSocket { virtual ISocket* accept(); virtual void close(); - private: +private: CNetwork::Socket m_fd; }; diff --git a/net/CTCPSocket.h b/net/CTCPSocket.h index 56da71b4..70fe9770 100644 --- a/net/CTCPSocket.h +++ b/net/CTCPSocket.h @@ -13,7 +13,7 @@ class CBufferedInputStream; class CBufferedOutputStream; class CTCPSocket : public ISocket { - public: +public: CTCPSocket(); CTCPSocket(CNetwork::Socket); ~CTCPSocket(); @@ -29,14 +29,14 @@ class CTCPSocket : public ISocket { virtual IInputStream* getInputStream(); virtual IOutputStream* getOutputStream(); - private: +private: void init(); void ioThread(void*); void ioService(); void closeInput(void*); void closeOutput(void*); - private: +private: enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 }; CNetwork::Socket m_fd; diff --git a/net/IListenSocket.h b/net/IListenSocket.h index f411412e..f62f2239 100644 --- a/net/IListenSocket.h +++ b/net/IListenSocket.h @@ -9,7 +9,7 @@ class CNetworkAddress; class ISocket; class IListenSocket : public IInterface { - public: +public: // manipulators // bind the socket to a particular address diff --git a/net/ISocket.h b/net/ISocket.h index 2c86e561..0a28ccc7 100644 --- a/net/ISocket.h +++ b/net/ISocket.h @@ -11,7 +11,7 @@ class IInputStream; class IOutputStream; class ISocket : public IInterface { - public: +public: // manipulators // bind the socket to a particular address diff --git a/net/XNetwork.h b/net/XNetwork.h index 21e91574..a971d423 100644 --- a/net/XNetwork.h +++ b/net/XNetwork.h @@ -8,19 +8,19 @@ class XNetwork : public XBase { }; class XNetworkUnavailable : public XNetwork { - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); }; class XNetworkFailed : public XNetwork { - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); }; class XNetworkVersion : public XNetwork { - public: +public: XNetworkVersion(int major, int minor) throw(); // accessors @@ -28,24 +28,24 @@ class XNetworkVersion : public XNetwork { int getMajor() const throw(); int getMinor() const throw(); - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); - private: +private: int m_major; int m_minor; }; class XNetworkFunctionUnavailable : public XNetwork { - public: +public: XNetworkFunctionUnavailable(const char* name) throw(); - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); - private: +private: CString m_name; }; diff --git a/net/XSocket.h b/net/XSocket.h index 578009fa..7a5cc6f5 100644 --- a/net/XSocket.h +++ b/net/XSocket.h @@ -8,7 +8,7 @@ class XSocket : public XBase { }; class XSocketAddress : public XSocket { - public: +public: enum Error { kUnknown, kNotFound, kNoAddress, kBadPort }; XSocketAddress(Error, const CString& hostname, SInt16 port) throw(); @@ -19,24 +19,24 @@ class XSocketAddress : public XSocket { virtual CString getHostname() const throw(); virtual SInt16 getPort() const throw(); - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); - private: +private: Error m_error; CString m_hostname; SInt16 m_port; }; class XSocketErrno : public XSocket, public MXErrno { - public: +public: XSocketErrno(); XSocketErrno(int); }; class XSocketBind : public XSocketErrno { - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); }; @@ -44,13 +44,13 @@ class XSocketBind : public XSocketErrno { class XSocketAddressInUse : public XSocketBind { }; class XSocketConnect : public XSocketErrno { - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); }; class XSocketCreate : public XSocketErrno { - protected: +protected: // XBase overrides virtual CString getWhat() const throw(); }; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index d2775577..f7ef900e 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -38,14 +38,14 @@ static DWORD s_thread = 0; static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { - case WM_INITDIALOG: + case WM_INITDIALOG: return TRUE; - case WM_CLOSE: + case WM_CLOSE: PostQuitMessage(0); return TRUE; - case WM_APP: + case WM_APP: if (!s_logMore.empty()) { if (s_log.size() > 20000) s_log = s_logMore; @@ -340,11 +340,11 @@ if (IsDialogMessage(s_debug, msg)) { // handle event switch (msg->message) { - case SYNERGY_MSG_MARK: + case SYNERGY_MSG_MARK: m_markReceived = msg->wParam; return true; - case SYNERGY_MSG_KEY: + case SYNERGY_MSG_KEY: // ignore if not at current mark if (m_mark == m_markReceived) { KeyModifierMask mask; @@ -373,23 +373,23 @@ log((CLOG_DEBUG "event: key event vk=%d info=0x%08x", msg->wParam, msg->lParam)) } return true; - case SYNERGY_MSG_MOUSE_BUTTON: + case SYNERGY_MSG_MOUSE_BUTTON: // ignore if not at current mark if (m_mark == m_markReceived) { const ButtonID button = mapButton(msg->wParam); switch (msg->wParam) { - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: log((CLOG_DEBUG "event: button press button=%d", button)); if (button != kButtonNone) { m_server->onMouseDown(button); } break; - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: log((CLOG_DEBUG "event: button release button=%d", button)); if (button != kButtonNone) { m_server->onMouseUp(button); @@ -399,7 +399,7 @@ log((CLOG_DEBUG "event: key event vk=%d info=0x%08x", msg->wParam, msg->lParam)) } return true; - case SYNERGY_MSG_MOUSE_MOVE: + case SYNERGY_MSG_MOUSE_MOVE: // ignore if not at current mark if (m_mark == m_markReceived) { SInt32 x = (SInt32)msg->wParam; @@ -445,12 +445,12 @@ LRESULT CMSWindowsPrimaryScreen::onEvent( WPARAM wParam, LPARAM lParam) { switch (msg) { - // FIXME -- handle display changes - case WM_PAINT: + // FIXME -- handle display changes + case WM_PAINT: ValidateRect(hwnd, NULL); return 0; - case WM_DRAWCLIPBOARD: + case WM_DRAWCLIPBOARD: log((CLOG_DEBUG "clipboard was taken")); // first pass it on @@ -470,7 +470,7 @@ LRESULT CMSWindowsPrimaryScreen::onEvent( } return 0; - case WM_CHANGECBCHAIN: + case WM_CHANGECBCHAIN: if (m_nextClipboardWindow == (HWND)wParam) m_nextClipboardWindow = (HWND)lParam; else @@ -818,19 +818,19 @@ ButtonID CMSWindowsPrimaryScreen::mapButton( WPARAM button) const { switch (button) { - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: return kButtonLeft; - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: return kButtonMiddle; - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: return kButtonRight; - default: + default: return kButtonNone; } } diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index fd96d5d5..bfa1eb56 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -7,7 +7,7 @@ #include "IPrimaryScreen.h" class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { - public: +public: typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM); CMSWindowsPrimaryScreen(); @@ -27,14 +27,14 @@ class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(IClipboard*) const; - protected: +protected: // CMSWindowsScreen overrides virtual bool onPreTranslate(MSG*); virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); virtual void onOpenDisplay(); virtual void onCloseDisplay(); - private: +private: void doEnter(); void nextMark(); @@ -43,7 +43,7 @@ class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { KeyModifierMask* maskOut) const; ButtonID mapButton(WPARAM button) const; - private: +private: CServer* m_server; bool m_active; HWND m_window; diff --git a/server/CScreenMap.h b/server/CScreenMap.h index e86a61d8..f677b679 100644 --- a/server/CScreenMap.h +++ b/server/CScreenMap.h @@ -6,7 +6,7 @@ #include class CScreenMap { - public: +public: enum EDirection { kLeft, kRight, kTop, kBottom, kFirstDirection = kLeft, kLastDirection = kBottom }; enum EDirectionMask { kLeftMask = 1, kRightMask = 2, @@ -38,9 +38,9 @@ class CScreenMap { // get the name of a direction (for debugging) static const char* dirName(EDirection); - private: +private: class CCell { - public: + public: CString m_neighbor[kLastDirection - kFirstDirection + 1]; }; typedef std::map CCellMap; diff --git a/server/CServer.cpp b/server/CServer.cpp index 99fcca5f..4e82f043 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -606,7 +606,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, // find destination screen, adjusting x or y (but not both) switch (srcSide) { - case CScreenMap::kLeft: + case CScreenMap::kLeft: while (dst != NULL) { lastGoodScreen = dst; w = lastGoodScreen->m_width; @@ -620,7 +620,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, } break; - case CScreenMap::kRight: + case CScreenMap::kRight: while (dst != NULL) { lastGoodScreen = dst; x -= w; @@ -634,7 +634,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, } break; - case CScreenMap::kTop: + case CScreenMap::kTop: while (dst != NULL) { lastGoodScreen = dst; w = lastGoodScreen->m_width; @@ -648,7 +648,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, } break; - case CScreenMap::kBottom: + case CScreenMap::kBottom: while (dst != NULL) { lastGoodScreen = dst; y -= h; @@ -675,25 +675,25 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, if (lastGoodScreen->m_protocol == NULL) { const CString dstName(lastGoodScreen->m_name); switch (srcSide) { - case CScreenMap::kLeft: + case CScreenMap::kLeft: if (!m_screenMap.getNeighbor(dstName, CScreenMap::kRight).empty() && x > w - 1 - lastGoodScreen->m_zoneSize) x = w - 1 - lastGoodScreen->m_zoneSize; break; - case CScreenMap::kRight: + case CScreenMap::kRight: if (!m_screenMap.getNeighbor(dstName, CScreenMap::kLeft).empty() && x < lastGoodScreen->m_zoneSize) x = lastGoodScreen->m_zoneSize; break; - case CScreenMap::kTop: + case CScreenMap::kTop: if (!m_screenMap.getNeighbor(dstName, CScreenMap::kBottom).empty() && y > h - 1 - lastGoodScreen->m_zoneSize) y = h - 1 - lastGoodScreen->m_zoneSize; break; - case CScreenMap::kBottom: + case CScreenMap::kBottom: if (!m_screenMap.getNeighbor(dstName, CScreenMap::kTop).empty() && y < lastGoodScreen->m_zoneSize) y = lastGoodScreen->m_zoneSize; @@ -715,8 +715,8 @@ void CServer::mapPosition(CScreenInfo* src, srcSide <= CScreenMap::kLastDirection); switch (srcSide) { - case CScreenMap::kLeft: - case CScreenMap::kRight: + case CScreenMap::kLeft: + case CScreenMap::kRight: if (y < 0) y = 0; else if (y >= src->m_height) @@ -727,8 +727,8 @@ void CServer::mapPosition(CScreenInfo* src, (src->m_height - 1)); break; - case CScreenMap::kTop: - case CScreenMap::kBottom: + case CScreenMap::kTop: + case CScreenMap::kBottom: if (x < 0) x = 0; else if (x >= src->m_width) diff --git a/server/CServer.h b/server/CServer.h index 1f0ab721..321017ce 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -19,7 +19,7 @@ class ISecurityFactory; class IPrimaryScreen; class CServer { - public: +public: CServer(); ~CServer(); @@ -64,36 +64,36 @@ class CServer { // get the sides of the primary screen that have neighbors UInt32 getActivePrimarySides() const; - protected: +protected: bool onCommandKey(KeyID, KeyModifierMask, bool down); - private: +private: class CCleanupNote { - public: + public: CCleanupNote(CServer*); ~CCleanupNote(); - private: + private: CServer* m_server; }; class CConnectionNote { - public: + public: CConnectionNote(CServer*, const CString&, IServerProtocol*); ~CConnectionNote(); - private: + private: bool m_pending; CServer* m_server; CString m_name; }; class CScreenInfo { - public: + public: CScreenInfo(const CString& name, IServerProtocol*); ~CScreenInfo(); - public: + public: CString m_name; IServerProtocol* m_protocol; SInt32 m_width, m_height; @@ -155,7 +155,7 @@ class CServer { CScreenInfo* addConnection(const CString& name, IServerProtocol*); void removeConnection(const CString& name); - private: +private: typedef std::list CThreadList; typedef std::map CScreenList; class CClipboardInfo { diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index e605049f..d2cb2d80 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -9,7 +9,7 @@ class IInputStream; class IOutputStream; class CServerProtocol : public IServerProtocol { - public: +public: CServerProtocol(CServer*, const CString& clientName, IInputStream*, IOutputStream*); ~CServerProtocol(); @@ -44,13 +44,13 @@ class CServerProtocol : public IServerProtocol { virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) = 0; virtual void sendMouseWheel(SInt32 delta) = 0; - protected: +protected: //IServerProtocol overrides virtual void recvInfo() = 0; virtual void recvClipboard() = 0; virtual void recvGrabClipboard() = 0; - private: +private: CServer* m_server; CString m_client; IInputStream* m_input; diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index e2cacd8d..f992bec8 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -4,7 +4,7 @@ #include "CServerProtocol.h" class CServerProtocol1_0 : public CServerProtocol { - public: +public: CServerProtocol1_0(CServer*, const CString&, IInputStream*, IOutputStream*); ~CServerProtocol1_0(); @@ -29,7 +29,7 @@ class CServerProtocol1_0 : public CServerProtocol { virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs); virtual void sendMouseWheel(SInt32 delta); - protected: +protected: // IServerProtocol overrides virtual void recvInfo(); virtual void recvClipboard(); diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 92eb5803..5609f44a 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -70,16 +70,16 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) if (code >= 0) { if (g_relay) { switch (wParam) { - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_BUTTON, wParam, 0); return 1; - case WM_MOUSEMOVE: { + case WM_MOUSEMOVE: { const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; SInt32 x = (SInt32)info->pt.x; SInt32 y = (SInt32)info->pt.y; @@ -100,7 +100,7 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) // relay the motion PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y); return 1; - } + } } } else { diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 0ce318cc..bafcbd02 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -36,21 +36,21 @@ void CXWindowsPrimaryScreen::run() // handle event switch (xevent.type) { - case CreateNotify: { + case CreateNotify: { // select events on new window CDisplayLock display(this); selectEvents(display, xevent.xcreatewindow.window); break; - } + } - case MappingNotify: { + case MappingNotify: { // keyboard mapping changed CDisplayLock display(this); XRefreshKeyboardMapping(&xevent.xmapping); break; - } + } - case KeyPress: { + case KeyPress: { log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); @@ -60,11 +60,11 @@ void CXWindowsPrimaryScreen::run() m_server->onKeyUp(key, mask | KeyModifierCapsLock); } break; - } + } - // FIXME -- simulate key repeat. X sends press/release for - // repeat. must detect auto repeat and use kKeyRepeat. - case KeyRelease: { + // FIXME -- simulate key repeat. X sends press/release for + // repeat. must detect auto repeat and use kKeyRepeat. + case KeyRelease: { log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); @@ -74,27 +74,27 @@ void CXWindowsPrimaryScreen::run() m_server->onKeyUp(key, mask); } break; - } + } - case ButtonPress: { + case ButtonPress: { log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { m_server->onMouseDown(button); } break; - } + } - case ButtonRelease: { + case ButtonRelease: { log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { m_server->onMouseUp(button); } break; - } + } - case MotionNotify: { + case MotionNotify: { log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); SInt32 x, y; if (!m_active) { @@ -129,9 +129,9 @@ void CXWindowsPrimaryScreen::run() m_server->onMouseMoveSecondary(x, y); } break; - } + } - case SelectionClear: + case SelectionClear: // we just lost the selection. that means someone else // grabbed the selection so this screen is now the // selection owner. report that to the server. @@ -142,7 +142,7 @@ void CXWindowsPrimaryScreen::run() } break; - case SelectionNotify: + case SelectionNotify: // notification of selection transferred. we shouldn't // get this here because we handle them in the selection // retrieval methods. we'll just delete the property @@ -153,7 +153,7 @@ void CXWindowsPrimaryScreen::run() } break; - case SelectionRequest: + case SelectionRequest: // somebody is asking for clipboard data if (xevent.xselectionrequest.owner == m_window) { addClipboardRequest(m_window, @@ -179,7 +179,7 @@ void CXWindowsPrimaryScreen::run() } break; - case PropertyNotify: + case PropertyNotify: // clipboard transfers involve property changes so forward // the event to the superclass. we only care about the // deletion of properties. @@ -190,7 +190,7 @@ void CXWindowsPrimaryScreen::run() } break; - case DestroyNotify: + case DestroyNotify: // looks like one of the windows that requested a clipboard // transfer has gone bye-bye. destroyClipboardRequest(xevent.xdestroywindow.window); diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index f6ab7a63..ba1b4584 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -7,7 +7,7 @@ #include "IPrimaryScreen.h" class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { - public: +public: CXWindowsPrimaryScreen(); virtual ~CXWindowsPrimaryScreen(); @@ -25,13 +25,13 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; - protected: +protected: // CXWindowsScreen overrides virtual void onOpenDisplay(); virtual void onCloseDisplay(); virtual long getEventMask(Window) const; - private: +private: void selectEvents(Display*, Window) const; void warpCursorNoLock(Display*, SInt32 xAbsolute, SInt32 yAbsolute); @@ -40,7 +40,7 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { KeyID mapKey(XKeyEvent*) const; ButtonID mapButton(unsigned int button) const; - private: +private: CServer* m_server; bool m_active; Window m_window; diff --git a/synergy/CClipboard.h b/synergy/CClipboard.h index 8fcf5cc0..734631f6 100644 --- a/synergy/CClipboard.h +++ b/synergy/CClipboard.h @@ -9,7 +9,7 @@ #include "CString.h" class CClipboard : public IClipboard { - public: +public: CClipboard(); virtual ~CClipboard(); @@ -40,11 +40,11 @@ class CClipboard : public IClipboard { static void copy(IClipboard* dst, const IClipboard* src); static void copy(IClipboard* dst, const IClipboard* src, Time); - private: +private: UInt32 readUInt32(const char*) const; void writeUInt32(CString*, UInt32) const; - private: +private: Time m_time; bool m_added[kNumFormats]; CString m_data[kNumFormats]; diff --git a/synergy/CInputPacketStream.h b/synergy/CInputPacketStream.h index 50be260c..11d41703 100644 --- a/synergy/CInputPacketStream.h +++ b/synergy/CInputPacketStream.h @@ -6,7 +6,7 @@ #include "CMutex.h" class CInputPacketStream : public CInputStreamFilter { - public: +public: CInputPacketStream(IInputStream*, bool adoptStream = true); ~CInputPacketStream(); @@ -19,11 +19,11 @@ class CInputPacketStream : public CInputStreamFilter { virtual UInt32 read(void*, UInt32 maxCount); virtual UInt32 getSize() const; - private: +private: UInt32 getSizeNoLock() const; bool hasFullMessage() const; - private: +private: CMutex m_mutex; mutable UInt32 m_size; mutable CBufferedInputStream m_buffer; diff --git a/synergy/CMSWindowsClipboard.cpp b/synergy/CMSWindowsClipboard.cpp index ecf8ae65..4b0049cf 100644 --- a/synergy/CMSWindowsClipboard.cpp +++ b/synergy/CMSWindowsClipboard.cpp @@ -53,11 +53,11 @@ void CMSWindowsClipboard::add( const UINT win32Format = convertFormatToWin32(format); HANDLE win32Data; switch (win32Format) { - case CF_TEXT: + case CF_TEXT: win32Data = convertTextToWin32(data); break; - default: + default: win32Data = NULL; break; } @@ -95,7 +95,7 @@ CString CMSWindowsClipboard::get(EFormat format) const if (win32Data != NULL) { // convert the data switch (win32Format) { - case CF_TEXT: + case CF_TEXT: data = convertTextFromWin32(win32Data); } } @@ -110,10 +110,10 @@ UINT CMSWindowsClipboard::convertFormatToWin32( EFormat format) const { switch (format) { - case kText: + case kText: return CF_TEXT; - default: + default: return 0; } } diff --git a/synergy/CMSWindowsClipboard.h b/synergy/CMSWindowsClipboard.h index afc8f52c..8fae59c0 100644 --- a/synergy/CMSWindowsClipboard.h +++ b/synergy/CMSWindowsClipboard.h @@ -5,7 +5,7 @@ #include class CMSWindowsClipboard : public IClipboard { - public: +public: CMSWindowsClipboard(HWND window); virtual ~CMSWindowsClipboard(); @@ -16,12 +16,12 @@ class CMSWindowsClipboard : public IClipboard { virtual bool has(EFormat) const; virtual CString get(EFormat) const; - private: +private: UINT convertFormatToWin32(EFormat) const; HANDLE convertTextToWin32(const CString& data) const; CString convertTextFromWin32(HANDLE) const; - private: +private: HWND m_window; }; diff --git a/synergy/CMSWindowsScreen.h b/synergy/CMSWindowsScreen.h index 2cfa9964..9e1590a8 100644 --- a/synergy/CMSWindowsScreen.h +++ b/synergy/CMSWindowsScreen.h @@ -10,7 +10,7 @@ class CString; class CThread; class CMSWindowsScreen { - public: +public: CMSWindowsScreen(); virtual ~CMSWindowsScreen(); @@ -18,7 +18,7 @@ class CMSWindowsScreen { static void init(HINSTANCE); - protected: +protected: // runs an event loop and returns when WM_QUIT is received void doRun(); @@ -62,10 +62,10 @@ class CMSWindowsScreen { // called by closeDisplay() to virtual void onCloseDisplay() = 0; - private: +private: static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); - private: +private: static HINSTANCE s_instance; ATOM m_class; HICON m_icon; diff --git a/synergy/COutputPacketStream.h b/synergy/COutputPacketStream.h index a41d63e7..07ee40a5 100644 --- a/synergy/COutputPacketStream.h +++ b/synergy/COutputPacketStream.h @@ -4,7 +4,7 @@ #include "COutputStreamFilter.h" class COutputPacketStream : public COutputStreamFilter { - public: +public: COutputPacketStream(IOutputStream*, bool adoptStream = true); ~COutputPacketStream(); diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index aa0c41b4..5c736efa 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -64,7 +64,7 @@ void CProtocolUtil::readf(IInputStream* stream, ++fmt; UInt32 len = eatLength(&fmt); switch (*fmt) { - case 'i': { + case 'i': { // check for valid length assert(len == 1 || len == 2 || len == 4); @@ -75,13 +75,13 @@ void CProtocolUtil::readf(IInputStream* stream, // convert it void* v = va_arg(args, void*); switch (len) { - case 1: + case 1: // 1 byte integer *reinterpret_cast(v) = buffer[0]; log((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; - case 2: + case 2: // 2 byte integer *reinterpret_cast(v) = (static_cast(buffer[0]) << 8) | @@ -89,7 +89,7 @@ void CProtocolUtil::readf(IInputStream* stream, log((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; - case 4: + case 4: // 4 byte integer *reinterpret_cast(v) = (static_cast(buffer[0]) << 24) | @@ -100,9 +100,9 @@ void CProtocolUtil::readf(IInputStream* stream, break; } break; - } + } - case 's': { + case 's': { assert(len == 0); // read the string length @@ -146,13 +146,13 @@ void CProtocolUtil::readf(IInputStream* stream, delete[] sBuffer; } break; - } + } - case '%': + case '%': assert(len == 0); break; - default: + default: assert(0 && "invalid format specifier"); } @@ -188,29 +188,29 @@ UInt32 CProtocolUtil::getLength( ++fmt; UInt32 len = eatLength(&fmt); switch (*fmt) { - case 'i': + case 'i': assert(len == 1 || len == 2 || len == 4); (void)va_arg(args, UInt32); break; - case 's': + case 's': assert(len == 0); len = (va_arg(args, CString*))->size() + 4; (void)va_arg(args, UInt8*); break; - case 'S': + case 'S': assert(len == 0); len = va_arg(args, UInt32) + 4; (void)va_arg(args, UInt8*); break; - case '%': + case '%': assert(len == 0); len = 1; break; - default: + default: assert(0 && "invalid format specifier"); } @@ -238,21 +238,21 @@ void CProtocolUtil::writef(void* buffer, ++fmt; UInt32 len = eatLength(&fmt); switch (*fmt) { - case 'i': { + case 'i': { const UInt32 v = va_arg(args, UInt32); switch (len) { - case 1: + case 1: // 1 byte integer *dst++ = static_cast(v & 0xff); break; - case 2: + case 2: // 2 byte integer *dst++ = static_cast((v >> 8) & 0xff); *dst++ = static_cast( v & 0xff); break; - case 4: + case 4: // 4 byte integer *dst++ = static_cast((v >> 24) & 0xff); *dst++ = static_cast((v >> 16) & 0xff); @@ -260,14 +260,14 @@ void CProtocolUtil::writef(void* buffer, *dst++ = static_cast( v & 0xff); break; - default: + default: assert(0 && "invalid integer format length"); return; } break; - } + } - case 's': { + case 's': { assert(len == 0); const CString* src = va_arg(args, CString*); const UInt32 len = (src != NULL) ? src->size() : 0; @@ -280,9 +280,9 @@ void CProtocolUtil::writef(void* buffer, dst += len; } break; - } + } - case 'S': { + case 'S': { assert(len == 0); const UInt32 len = va_arg(args, UInt32); const UInt8* src = va_arg(args, UInt8*); @@ -293,14 +293,14 @@ void CProtocolUtil::writef(void* buffer, memcpy(dst, src, len); dst += len; break; - } + } - case '%': + case '%': assert(len == 0); *dst++ = '%'; break; - default: + default: assert(0 && "invalid format specifier"); } @@ -321,17 +321,17 @@ UInt32 CProtocolUtil::eatLength(const char** pfmt) for (;;) { UInt32 d; switch (*fmt) { - case '0': d = 0; break; - case '1': d = 1; break; - case '2': d = 2; break; - case '3': d = 3; break; - case '4': d = 4; break; - case '5': d = 5; break; - case '6': d = 6; break; - case '7': d = 7; break; - case '8': d = 8; break; - case '9': d = 9; break; - default: *pfmt = fmt; return n; + case '0': d = 0; break; + case '1': d = 1; break; + case '2': d = 2; break; + case '3': d = 3; break; + case '4': d = 4; break; + case '5': d = 5; break; + case '6': d = 6; break; + case '7': d = 7; break; + case '8': d = 8; break; + case '9': d = 9; break; + default: *pfmt = fmt; return n; } n = 10 * n + d; ++fmt; diff --git a/synergy/CProtocolUtil.h b/synergy/CProtocolUtil.h index d85d26f1..63a4376b 100644 --- a/synergy/CProtocolUtil.h +++ b/synergy/CProtocolUtil.h @@ -9,7 +9,7 @@ class IInputStream; class IOutputStream; class CProtocolUtil { - public: +public: // write formatted binary data to a stream. fmt consists of // regular characters and format specifiers. format specifiers // begin with %. all characters not part of a format specifier @@ -37,7 +37,7 @@ class CProtocolUtil { static void readf(IInputStream*, const char* fmt, ...); - private: +private: static UInt32 getLength(const char* fmt, va_list); static void writef(void*, const char* fmt, va_list); static UInt32 eatLength(const char** fmt); @@ -45,7 +45,7 @@ class CProtocolUtil { }; class XIOReadMismatch : public XIO { - public: +public: // XBase overrides virtual CString getWhat() const throw(); }; diff --git a/synergy/CTCPSocketFactory.h b/synergy/CTCPSocketFactory.h index d5d689e7..a320f9ae 100644 --- a/synergy/CTCPSocketFactory.h +++ b/synergy/CTCPSocketFactory.h @@ -4,7 +4,7 @@ #include "ISocketFactory.h" class CTCPSocketFactory : public ISocketFactory { - public: +public: CTCPSocketFactory(); virtual ~CTCPSocketFactory(); diff --git a/synergy/CXWindowsClipboard.h b/synergy/CXWindowsClipboard.h index b025536d..7bdfb08c 100644 --- a/synergy/CXWindowsClipboard.h +++ b/synergy/CXWindowsClipboard.h @@ -4,7 +4,7 @@ #include "IClipboard.h" class CXWindowsClipboard : public IClipboard { - public: +public: CXWindowsClipboard(); virtual ~CXWindowsClipboard(); diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index c82cb36c..b3dd2bf2 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -403,19 +403,19 @@ bool CXWindowsScreen::getDisplayClipboard( // transferred. use it as a hint to size our buffer. UInt32 size; switch (datumSize) { - case 8: + case 8: size = *(reinterpret_cast(data.data())); break; - case 16: + case 16: size = *(reinterpret_cast(data.data())); break; - case 32: + case 32: size = *(reinterpret_cast(data.data())); break; - default: + default: assert(0 && "invalid datum size"); } diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 21e8d639..66df2968 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -12,19 +12,19 @@ class CString; class CXWindowsScreen { - public: +public: CXWindowsScreen(); virtual ~CXWindowsScreen(); - protected: +protected: class CDisplayLock { - public: + public: CDisplayLock(const CXWindowsScreen*); ~CDisplayLock(); operator Display*() const; - private: + private: const CMutex* m_mutex; Display* m_display; }; @@ -104,14 +104,14 @@ class CXWindowsScreen { // get the X event mask required by the subclass for the given window virtual long getEventMask(Window) const = 0; - private: +private: struct CPropertyNotifyInfo { - public: + public: Window m_window; Atom m_property; }; struct CClipboardRequest { - public: + public: CString m_data; UInt32 m_sent; Window m_requestor; @@ -147,7 +147,7 @@ class CXWindowsScreen { bool wasOwnedAtTime(ClipboardID, Window, Time) const; Time getCurrentTimeNoLock(Window) const; - private: +private: class CClipboardInfo { public: CClipboardInfo(); diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index a75ec70a..32856c1b 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -7,7 +7,7 @@ class CString; class IClipboard : public IInterface { - public: +public: // timestamp type. timestamps are in milliseconds from some // arbitrary starting time. timestamps will wrap around to 0 // after about 49 3/4 days. diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 91a698bd..198c917a 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -9,7 +9,7 @@ class CServer; class IClipboard; class IPrimaryScreen : public IInterface { - public: +public: // manipulators // enter the screen's message loop. this returns when it detects diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 9599d25c..11752c2b 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -11,7 +11,7 @@ class CClient; class IClipboard; class ISecondaryScreen : public IInterface { - public: +public: // manipulators // enter the screen's message loop. this returns when it detects diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index 84ed10ef..1bf4a9df 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -11,7 +11,7 @@ class IClipboard; class IServerProtocol : public IInterface { - public: +public: // manipulators // process messages from the client and insert the appropriate @@ -39,7 +39,7 @@ class IServerProtocol : public IInterface { // accessors - protected: +protected: // manipulators virtual void recvInfo() = 0; diff --git a/synergy/ISocketFactory.h b/synergy/ISocketFactory.h index 2bb8ce2f..287e4302 100644 --- a/synergy/ISocketFactory.h +++ b/synergy/ISocketFactory.h @@ -8,7 +8,7 @@ class ISocket; class IListenSocket; class ISocketFactory : public IInterface { - public: +public: // manipulators // accessors diff --git a/synergy/XSynergy.h b/synergy/XSynergy.h index da54af49..e88b1c56 100644 --- a/synergy/XSynergy.h +++ b/synergy/XSynergy.h @@ -7,13 +7,13 @@ class XSynergy : public XBase { }; // client is misbehaving class XBadClient : public XSynergy { - protected: +protected: virtual CString getWhat() const throw(); }; // client has incompatible version class XIncompatibleClient : public XSynergy { - public: +public: XIncompatibleClient(int major, int minor); // manipulators @@ -23,10 +23,10 @@ class XIncompatibleClient : public XSynergy { int getMajor() const throw(); int getMinor() const throw(); - protected: +protected: virtual CString getWhat() const throw(); - private: +private: int m_major; int m_minor; }; From dc195706215c4e176256a7a0d4110489978567a0 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Apr 2002 16:23:03 +0000 Subject: [PATCH 058/807] Fixes to get win32 client and server up to date. --- client/CClient.cpp | 29 +- client/CMSWindowsSecondaryScreen.cpp | 421 ++++++++++++++++++++++----- client/CMSWindowsSecondaryScreen.h | 24 +- server/CMSWindowsPrimaryScreen.cpp | 411 +++++++++++++++++++------- server/CMSWindowsPrimaryScreen.h | 11 +- server/CServerProtocol1_0.cpp | 6 +- server/server.rc | 8 +- synergy/CClipboard.cpp | 12 +- synergy/CMSWindowsClipboard.cpp | 24 +- synergy/CMSWindowsClipboard.h | 4 +- synergy/synergy.dsp | 4 + 11 files changed, 746 insertions(+), 208 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index f41dc791..2e278cb7 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -116,7 +116,10 @@ void CClient::onClipboardChanged(ClipboardID id) // send data log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); - CProtocolUtil::writef(m_output, kMsgDClipboard, id, m_seqNum, &data); + if (m_output != NULL) { +// FIXME -- will we send the clipboard when we connect? + CProtocolUtil::writef(m_output, kMsgDClipboard, id, m_seqNum, &data); + } } } @@ -200,7 +203,7 @@ void CClient::runSession(void*) // handle messages from server for (;;) { // wait for reply - log((CLOG_DEBUG1 "waiting for message")); + log((CLOG_DEBUG2 "waiting for message")); UInt8 code[4]; UInt32 n = input->read(code, 4); @@ -259,6 +262,7 @@ void CClient::runSession(void*) } else if (memcmp(code, kMsgCClose, 4) == 0) { // server wants us to hangup + log((CLOG_DEBUG1 "recv close")); break; } else { @@ -342,11 +346,14 @@ void CClient::onEnter() CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum); m_active = true; } + log((CLOG_DEBUG1 "recv enter, %d,%d %d", x, y, m_seqNum)); m_screen->enter(x, y); } void CClient::onLeave() { + log((CLOG_DEBUG1 "recv leave")); + // tell screen we're leaving m_screen->leave(); @@ -391,7 +398,7 @@ void CClient::onGrabClipboard() { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id, &seqNum); - log((CLOG_DEBUG "received clipboard %d grab", id)); + log((CLOG_DEBUG "recv grab clipboard %d", id)); // validate if (id >= kClipboardEnd) { @@ -411,6 +418,7 @@ void CClient::onScreenSaver() CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on); } + log((CLOG_DEBUG1 "recv screen saver on=%d", on)); // FIXME } @@ -435,7 +443,7 @@ void CClient::onSetClipboard() CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDClipboard + 4, &id, &seqNum, &data); } - log((CLOG_DEBUG "received clipboard %d size=%d", id, data.size())); + log((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); // validate if (id >= kClipboardEnd) { @@ -452,22 +460,24 @@ void CClient::onSetClipboard() void CClient::onKeyDown() { - SInt16 id, mask; + UInt16 id, mask; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); } + log((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask)); m_screen->keyDown(static_cast(id), static_cast(mask)); } void CClient::onKeyRepeat() { - SInt16 id, mask, count; + UInt16 id, mask, count; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); } + log((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count)); m_screen->keyRepeat(static_cast(id), static_cast(mask), count); @@ -475,11 +485,12 @@ void CClient::onKeyRepeat() void CClient::onKeyUp() { - SInt16 id, mask; + UInt16 id, mask; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); } + log((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask)); m_screen->keyUp(static_cast(id), static_cast(mask)); } @@ -491,6 +502,7 @@ void CClient::onMouseDown() CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); } + log((CLOG_DEBUG1 "recv mouse down id=%d", id)); m_screen->mouseDown(static_cast(id)); } @@ -501,6 +513,7 @@ void CClient::onMouseUp() CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); } + log((CLOG_DEBUG1 "recv mouse up id=%d", id)); m_screen->mouseUp(static_cast(id)); } @@ -511,6 +524,7 @@ void CClient::onMouseMove() CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); } + log((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); m_screen->mouseMove(x, y); } @@ -521,5 +535,6 @@ void CClient::onMouseWheel() CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); } + log((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); m_screen->mouseWheel(delta); } diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 74ce2874..e1dc6ab4 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -86,6 +86,14 @@ void CMSWindowsSecondaryScreen::open(CClient* client) // open the display openDisplay(); + + // update key state + updateKeys(); + updateModifiers(); + + // assume primary has all clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) + grabClipboard(id); } void CMSWindowsSecondaryScreen::close() @@ -118,6 +126,10 @@ void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) // show cursor log((CLOG_INFO "show cursor")); ShowWindow(m_window, SW_HIDE); + + // update our keyboard state to reflect the local state + updateKeys(); + updateModifiers(); } void CMSWindowsSecondaryScreen::leave() @@ -151,7 +163,8 @@ void CMSWindowsSecondaryScreen::leave() if (m_clipboardOwner != clipboardOwner) { m_clipboardOwner = clipboardOwner; if (m_clipboardOwner != m_window) { - m_client->onClipboardChanged(); + m_client->onClipboardChanged(kClipboardClipboard); + m_client->onClipboardChanged(kClipboardSelection); } } } @@ -159,22 +172,45 @@ void CMSWindowsSecondaryScreen::leave() void CMSWindowsSecondaryScreen::keyDown( KeyID key, KeyModifierMask mask) { - const UINT vkey = mapKey(key, mask); - if (vkey != 0) { - const UINT code = MapVirtualKey(vkey, 0); - keybd_event(vkey, code, 0, 0); + Keystrokes keys; + UINT virtualKey; + + // get the sequence of keys to simulate key press and the final + // modifier state. + m_mask = mapKey(keys, virtualKey, key, mask, true); + if (keys.empty()) + return; + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) { + const UINT code = MapVirtualKey(k->first, 0); + keybd_event(k->first, code, k->second ? 0 : KEYEVENTF_KEYUP, 0); } + + // note that key is now down + m_keys[virtualKey] |= 0x80; } void CMSWindowsSecondaryScreen::keyRepeat( KeyID key, KeyModifierMask mask, SInt32 count) { - const UINT vkey = mapKey(key, mask); - if (vkey != 0) { - const UINT code = MapVirtualKey(vkey, 0); - for (SInt32 i = 0; i < count; ++i) { - keybd_event(vkey, code, KEYEVENTF_KEYUP, 0); - keybd_event(vkey, code, 0, 0); + Keystrokes keys; + UINT virtualKey; + + // get the sequence of keys to simulate key release and the final + // modifier state. + m_mask = mapKey(keys, virtualKey, key, mask, true); + if (keys.empty()) + return; + + // generate key events +// YYY -- need to know which code in Keystrokes should be repeated; +// then repeat only that key count times + for (SInt32 i = 0; i < count; ++i) { + for (Keystrokes::const_iterator k = keys.begin(); + k != keys.end(); ++k) { + const UINT code = MapVirtualKey(k->first, 0); + keybd_event(k->first, code, k->second ? 0 : KEYEVENTF_KEYUP, 0); } } } @@ -182,61 +218,43 @@ void CMSWindowsSecondaryScreen::keyRepeat( void CMSWindowsSecondaryScreen::keyUp( KeyID key, KeyModifierMask mask) { - const UINT vkey = mapKey(key, mask); - if (vkey != 0) { - const UINT code = MapVirtualKey(vkey, 0); - keybd_event(vkey, code, KEYEVENTF_KEYUP, 0); + Keystrokes keys; + UINT virtualKey; + + // get the sequence of keys to simulate key release and the final + // modifier state. + m_mask = mapKey(keys, virtualKey, key, mask, false); + if (keys.empty()) + return; + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) { + const UINT code = MapVirtualKey(k->first, 0); + keybd_event(k->first, code, k->second ? 0 : KEYEVENTF_KEYUP, 0); } + + // note that key is now up + m_keys[virtualKey] &= ~0x80; } void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) { // map button id to button flag - DWORD flags; - switch (button) { - case kButtonLeft: - flags = MOUSEEVENTF_LEFTDOWN; - break; - - case kButtonMiddle: - flags = MOUSEEVENTF_MIDDLEDOWN; - break; - - case kButtonRight: - flags = MOUSEEVENTF_RIGHTDOWN; - break; - - default: - return; - } + DWORD flags = mapButton(button); // send event - mouse_event(flags, 0, 0, 0, 0); + if (flags != 0) + mouse_event(flags, 0, 0, 0, 0); } void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) { // map button id to button flag - DWORD flags; - switch (button) { - case kButtonLeft: - flags = MOUSEEVENTF_LEFTUP; - break; - - case kButtonMiddle: - flags = MOUSEEVENTF_MIDDLEUP; - break; - - case kButtonRight: - flags = MOUSEEVENTF_RIGHTUP; - break; - - default: - return; - } + DWORD flags = mapButton(button); // send event - mouse_event(flags, 0, 0, 0, 0); + if (flags != 0) + mouse_event(flags, 0, 0, 0, 0); } void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) @@ -255,7 +273,7 @@ void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) } void CMSWindowsSecondaryScreen::setClipboard( - const IClipboard* src) + ClipboardID id, const IClipboard* src) { assert(m_window != NULL); @@ -263,12 +281,12 @@ void CMSWindowsSecondaryScreen::setClipboard( CClipboard::copy(&dst, src); } -void CMSWindowsSecondaryScreen::grabClipboard() +void CMSWindowsSecondaryScreen::grabClipboard(ClipboardID id) { assert(m_window != NULL); CMSWindowsClipboard clipboard(m_window); - if (clipboard.open()) { + if (clipboard.open(0)) { clipboard.close(); } } @@ -285,7 +303,7 @@ SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const } void CMSWindowsSecondaryScreen::getClipboard( - IClipboard* dst) const + ClipboardID id, IClipboard* dst) const { assert(m_window != NULL); @@ -385,7 +403,8 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( // ownership). m_clipboardOwner = GetClipboardOwner(); if (m_clipboardOwner != m_window) { - m_client->onClipboardChanged(); + m_client->onClipboardChanged(kClipboardClipboard); + m_client->onClipboardChanged(kClipboardSelection); } return 0; @@ -887,33 +906,281 @@ static const UINT* g_mapTable[] = /* 0xfc */ NULL, g_terminal, g_function, g_miscellany }; -UINT CMSWindowsSecondaryScreen::mapKey( - KeyID id, KeyModifierMask /*mask*/) const +DWORD CMSWindowsSecondaryScreen::mapButton( + ButtonID button) const { - const UInt32 mapID = ((id >> 8) & 0xff); - const UInt32 code = (id & 0xff); + // map button id to button flag + switch (button) { + case kButtonLeft: + return MOUSEEVENTF_LEFTDOWN; - // lookup the key table - const UINT* map = g_mapTable[mapID]; - if (map == NULL) { + case kButtonMiddle: + return MOUSEEVENTF_MIDDLEDOWN; + + case kButtonRight: + return MOUSEEVENTF_RIGHTDOWN; + + default: return 0; } +} - if (mapID == 0) { - SHORT scan = VkKeyScan(code); - if (scan != 0xffff) { - // FIXME -- must ensure shift state is correct. that means - // tracking the shift state from the moment we enter until - // the moment we leave (and probably disallowing leave if - // any shift keys are down). if current shift state is - // correct then do nothing extra, otherwise must surround - // injected key event with injected shift key events to - // get shift key in correct state then back to the previous - // state. - return (UINT)LOBYTE(scan); +KeyModifierMask CMSWindowsSecondaryScreen::mapKey( + Keystrokes& keys, + UINT& virtualKey, + KeyID id, KeyModifierMask mask, + bool press) const +{ + // lookup the key table + const UInt32 mapID = ((id >> 8) & 0xff); + const UINT* map = g_mapTable[mapID]; + if (map == NULL) { + // unknown key + return m_mask; + } + + // look up virtual key for id. default output mask carries over + // the current toggle modifier states. + const UInt32 code = (id & 0xff); + virtualKey = map[code]; + KeyModifierMask outMask = (m_mask & + (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)); + + // if not in map then ask system to convert ascii character + if (virtualKey == 0) { + if (mapID != 0) { + // not ascii + return m_mask; + } + + // translate. return no keys if unknown key. + SHORT vk = VkKeyScan(code); + if (vk == 0xffff) { + return m_mask; + } + + // convert system modifier mask to our mask + if (HIBYTE(vk) & 1) + outMask |= KeyModifierShift; + if (HIBYTE(vk) & 2) + outMask |= KeyModifierControl; + if (HIBYTE(vk) & 4) + outMask |= KeyModifierAlt; + + // if caps-lock is on and so is shift then turn off caps-lock + if (outMask & (KeyModifierShift | KeyModifierCapsLock) == + (KeyModifierShift | KeyModifierCapsLock)) + outMask &= ~KeyModifierCapsLock; + + // get virtual key + virtualKey = LOBYTE(vk); + } + + // if in map then figure out correct modifier state + else { + // check numeric keypad. note that while KeyID distinguishes + // between the keypad movement keys (e.g. Home, left arrow), + // the virtual keys do not. however, the virtual keys do + // distinguish between keypad numbers and operators (e.g. + // add, multiply) and their main keyboard counterparts. + // therefore, we can ignore the num-lock state for movement + // virtual keys but not for numeric keys. + if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) { + // set required shift state based on current numlock state + if ((outMask & KeyModifierNumLock) == 0) + outMask |= KeyModifierShift; + } + + // FIXME -- should check for LeftTab KeySym + } + + // a list of modifier key info + class CModifierInfo { + public: + KeyModifierMask mask; + UINT virtualKey; + UINT virtualKey2; + bool isToggle; + }; + static const CModifierInfo s_modifier[] = { + { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, + { KeyModifierControl, VK_LCONTROL, VK_RCONTROL, false }, + { KeyModifierAlt, VK_LMENU, VK_RMENU, false }, + { KeyModifierMeta, VK_LWIN, VK_RWIN, false }, + { KeyModifierCapsLock, VK_CAPITAL, 0, true }, + { KeyModifierNumLock, VK_NUMLOCK, 0, true }, + { KeyModifierScrollLock, VK_SCROLL, 0, true } + }; + static const unsigned int s_numModifiers = + sizeof(s_modifier) / sizeof(s_modifier[0]); + + // note if the key is a modifier + unsigned int modifierIndex; + switch (virtualKey) { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + modifierIndex = 0; + break; + + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + modifierIndex = 1; + break; + + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + modifierIndex = 2; + break; + + case VK_LWIN: + case VK_RWIN: + modifierIndex = 3; + break; + + case VK_CAPITAL: + modifierIndex = 4; + break; + + case VK_NUMLOCK: + modifierIndex = 5; + break; + + case VK_SCROLL: + modifierIndex = 6; + break; + + default: + modifierIndex = s_numModifiers; + break; + } + const bool isModifier = (modifierIndex != s_numModifiers); + + // add the key events required to get to the modifier state + // necessary to generate an event yielding id. also save the + // key events required to restore the state. if the key is + // a modifier key then skip this because modifiers should not + // modify modifiers. + Keystrokes undo; + if (outMask != m_mask && !isModifier) { + for (unsigned int i = 0; i < s_numModifiers; ++i) { + KeyModifierMask bit = s_modifier[i].mask; + if ((outMask & bit) != (m_mask & bit)) { + if ((outMask & bit) != 0) { + // modifier is not active but should be. if the + // modifier is a toggle then toggle it on with a + // press/release, otherwise activate it with a + // press. + const UINT modifierKey = s_modifier[i].virtualKey; + keys.push_back(std::make_pair(modifierKey, true)); + if (s_modifier[i].isToggle) { + keys.push_back(std::make_pair(modifierKey, false)); + undo.push_back(std::make_pair(modifierKey, false)); + undo.push_back(std::make_pair(modifierKey, true)); + } + else { + undo.push_back(std::make_pair(modifierKey, false)); + } + } + + else { + // modifier is active but should not be. if the + // modifier is a toggle then toggle it off with a + // press/release, otherwise deactivate it with a + // release. we must check each keycode for the + // modifier if not a toggle. + if (s_modifier[i].isToggle) { + const UINT modifierKey = s_modifier[i].virtualKey; + keys.push_back(std::make_pair(modifierKey, true)); + keys.push_back(std::make_pair(modifierKey, false)); + undo.push_back(std::make_pair(modifierKey, false)); + undo.push_back(std::make_pair(modifierKey, true)); + } + else { + UINT key = s_modifier[i].virtualKey; + if ((m_keys[key] & 0x80) != 0) { + keys.push_back(std::make_pair(key, false)); + undo.push_back(std::make_pair(key, true)); + } + key = s_modifier[i].virtualKey2; + if ((m_keys[key] & 0x80) != 0) { + keys.push_back(std::make_pair(key, false)); + undo.push_back(std::make_pair(key, true)); + } + } + } + } } } - // lookup the key in the table - return map[code]; + // add the key event + keys.push_back(std::make_pair(virtualKey, press)); + + // add key events to restore the modifier state. apply events in + // the reverse order that they're stored in undo. + while (!undo.empty()) { + keys.push_back(undo.back()); + undo.pop_back(); + } + + // if the key is a modifier key then compute the modifier mask after + // this key is pressed. + mask = m_mask; + if (isModifier) { + // toggle keys modify the state on press if toggling on and on + // release if toggling off. other keys set the bit on press + // and clear the bit on release. + // FIXME -- verify if that's true on win32 + if (s_modifier[modifierIndex].isToggle) { + if (((mask & s_modifier[modifierIndex].mask) == 0) == press) + mask ^= s_modifier[modifierIndex].mask; + } + else if (press) { + mask |= s_modifier[modifierIndex].mask; + } + else { + // can't reset bit until all keys that set it are released. + // scan those keys to see if any are pressed. + bool down = false; + if ((m_keys[s_modifier[modifierIndex].virtualKey] & 0x80) != 0) { + down = true; + } + if ((m_keys[s_modifier[modifierIndex].virtualKey2] & 0x80) != 0) { + down = true; + } + if (!down) + mask &= ~s_modifier[modifierIndex].mask; + } + } + + return mask; +} + +void CMSWindowsSecondaryScreen::updateKeys() +{ + GetKeyboardState(m_keys); +} + +void CMSWindowsSecondaryScreen::updateModifiers() +{ + // update active modifier mask + m_mask = 0; + if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) + m_mask |= KeyModifierShift; + if ((m_keys[VK_LCONTROL] & 0x80) != 0 || (m_keys[VK_RCONTROL] & 0x80) != 0) + m_mask |= KeyModifierControl; + if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) + m_mask |= KeyModifierAlt; + if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) + m_mask |= KeyModifierMeta; + if ((m_keys[VK_CAPITAL] & 0x01) != 0) + m_mask |= KeyModifierCapsLock; + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + m_mask |= KeyModifierNumLock; + if ((m_keys[VK_SCROLL] & 0x01) != 0) + m_mask |= KeyModifierScrollLock; } diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 196817af..dbd9b5a8 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -3,6 +3,8 @@ #include "CMSWindowsScreen.h" #include "ISecondaryScreen.h" +#include +#include class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen { public: @@ -23,11 +25,11 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); virtual void mouseWheel(SInt32 delta); - virtual void setClipboard(const IClipboard*); - virtual void grabClipboard(); + virtual void setClipboard(ClipboardID, const IClipboard*); + virtual void grabClipboard(ClipboardID); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; - virtual void getClipboard(IClipboard*) const; + virtual void getClipboard(ClipboardID, IClipboard*) const; protected: // CMSWindowsScreen overrides @@ -37,13 +39,27 @@ protected: virtual void onCloseDisplay(); private: - UINT mapKey(KeyID, KeyModifierMask) const; + typedef std::pair Keystroke; + typedef std::vector Keystrokes; + + DWORD mapButton(ButtonID button) const; + KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, + KeyModifierMask, bool press) const; + + void updateKeys(); + void updateModifiers(); private: CClient* m_client; HWND m_window; HWND m_nextClipboardWindow; HWND m_clipboardOwner; + + // virtual key states + BYTE m_keys[256]; + + // current active modifiers + KeyModifierMask m_mask; }; #endif diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index f7ef900e..9c4f3169 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -6,6 +6,7 @@ #include "CThread.h" #include "CLog.h" #include +#include // // CMSWindowsPrimaryScreen @@ -86,6 +87,9 @@ void CMSWindowsPrimaryScreen::open(CServer* server) // set the server m_server = server; + // get keyboard state + updateKeys(); + // open the display openDisplay(); @@ -189,7 +193,8 @@ void CMSWindowsPrimaryScreen::leave() try { m_clipboardOwner = clipboardOwner; if (m_clipboardOwner != m_window) { - m_server->grabClipboard(); + m_server->grabClipboard(kClipboardClipboard); + m_server->grabClipboard(kClipboardSelection); } } catch (XBadClient&) { @@ -209,7 +214,7 @@ void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) } void CMSWindowsPrimaryScreen::setClipboard( - const IClipboard* src) + ClipboardID id, const IClipboard* src) { assert(m_window != NULL); @@ -217,12 +222,12 @@ void CMSWindowsPrimaryScreen::setClipboard( CClipboard::copy(&dst, src); } -void CMSWindowsPrimaryScreen::grabClipboard() +void CMSWindowsPrimaryScreen::grabClipboard(ClipboardID id) { assert(m_window != NULL); CMSWindowsClipboard clipboard(m_window); - if (clipboard.open()) { + if (clipboard.open(0)) { clipboard.close(); } } @@ -239,7 +244,7 @@ SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const } void CMSWindowsPrimaryScreen::getClipboard( - IClipboard* dst) const + ClipboardID id, IClipboard* dst) const { assert(m_window != NULL); @@ -349,24 +354,29 @@ if (IsDialogMessage(s_debug, msg)) { if (m_mark == m_markReceived) { KeyModifierMask mask; const KeyID key = mapKey(msg->wParam, msg->lParam, &mask); -log((CLOG_DEBUG "event: key event vk=%d info=0x%08x", msg->wParam, msg->lParam)); if (key != kKeyNone) { if ((msg->lParam & 0x80000000) == 0) { // key press const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); if (repeat >= 2) { - log((CLOG_DEBUG "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); + log((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); m_server->onKeyRepeat(key, mask, repeat); } else { - log((CLOG_DEBUG "event: key press key=%d mask=0x%04x", key, mask)); + log((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x", key, mask)); m_server->onKeyDown(key, mask); } + + // update key state + updateKey(msg->wParam, true); } else { // key release - log((CLOG_DEBUG "event: key release key=%d mask=0x%04x", key, mask)); + log((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask)); m_server->onKeyUp(key, mask); + + // update key state + updateKey(msg->wParam, false); } } @@ -381,7 +391,7 @@ log((CLOG_DEBUG "event: key event vk=%d info=0x%08x", msg->wParam, msg->lParam)) case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: - log((CLOG_DEBUG "event: button press button=%d", button)); + log((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { m_server->onMouseDown(button); } @@ -390,7 +400,7 @@ log((CLOG_DEBUG "event: key event vk=%d info=0x%08x", msg->wParam, msg->lParam)) case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: - log((CLOG_DEBUG "event: button release button=%d", button)); + log((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { m_server->onMouseUp(button); } @@ -405,11 +415,11 @@ log((CLOG_DEBUG "event: key event vk=%d info=0x%08x", msg->wParam, msg->lParam)) SInt32 x = (SInt32)msg->wParam; SInt32 y = (SInt32)msg->lParam; if (!m_active) { - log((CLOG_DEBUG "event: inactive move %d,%d", x, y)); + log((CLOG_DEBUG2 "event: inactive move %d,%d", x, y)); m_server->onMouseMovePrimary(x, y); } else { - log((CLOG_DEBUG "event: active move %d,%d", x, y)); + log((CLOG_DEBUG2 "event: active move %d,%d", x, y)); // get screen size SInt32 w, h; @@ -461,7 +471,8 @@ LRESULT CMSWindowsPrimaryScreen::onEvent( try { m_clipboardOwner = GetClipboardOwner(); if (m_clipboardOwner != m_window) { - m_server->grabClipboard(); + m_server->grabClipboard(kClipboardClipboard); + m_server->grabClipboard(kClipboardSelection); } } catch (XBadClient&) { @@ -521,7 +532,7 @@ static const KeyID g_virtualKey[] = /* 0x1d */ kKeyNone, // VK_NONCONVERT /* 0x1e */ kKeyNone, // VK_ACCEPT /* 0x1f */ kKeyNone, // VK_MODECHANGE - /* 0x20 */ 0xff20, // VK_SPACE XK_space + /* 0x20 */ 0x0020, // VK_SPACE XK_space /* 0x21 */ 0xff55, // VK_PRIOR XK_Prior /* 0x22 */ 0xff56, // VK_NEXT XK_Next /* 0x23 */ 0xff57, // VK_END XK_End @@ -537,16 +548,16 @@ static const KeyID g_virtualKey[] = /* 0x2d */ 0xff63, // VK_INSERT XK_Insert /* 0x2e */ 0xffff, // VK_DELETE XK_Delete /* 0x2f */ 0xff6a, // VK_HELP XK_Help - /* 0x30 */ 0x0030, // VK_0 XK_0 - /* 0x31 */ 0x0031, // VK_1 XK_1 - /* 0x32 */ 0x0032, // VK_2 XK_2 - /* 0x33 */ 0x0033, // VK_3 XK_3 - /* 0x34 */ 0x0034, // VK_4 XK_4 - /* 0x35 */ 0x0035, // VK_5 XK_5 - /* 0x36 */ 0x0036, // VK_6 XK_6 - /* 0x37 */ 0x0037, // VK_7 XK_7 - /* 0x38 */ 0x0038, // VK_8 XK_8 - /* 0x39 */ 0x0039, // VK_9 XK_9 + /* 0x30 */ kKeyNone, // VK_0 XK_0 + /* 0x31 */ kKeyNone, // VK_1 XK_1 + /* 0x32 */ kKeyNone, // VK_2 XK_2 + /* 0x33 */ kKeyNone, // VK_3 XK_3 + /* 0x34 */ kKeyNone, // VK_4 XK_4 + /* 0x35 */ kKeyNone, // VK_5 XK_5 + /* 0x36 */ kKeyNone, // VK_6 XK_6 + /* 0x37 */ kKeyNone, // VK_7 XK_7 + /* 0x38 */ kKeyNone, // VK_8 XK_8 + /* 0x39 */ kKeyNone, // VK_9 XK_9 /* 0x3a */ kKeyNone, // undefined /* 0x3b */ kKeyNone, // undefined /* 0x3c */ kKeyNone, // undefined @@ -554,32 +565,32 @@ static const KeyID g_virtualKey[] = /* 0x3e */ kKeyNone, // undefined /* 0x3f */ kKeyNone, // undefined /* 0x40 */ kKeyNone, // undefined - /* 0x41 */ 0x0041, // VK_A XK_A - /* 0x42 */ 0x0042, // VK_B XK_B - /* 0x43 */ 0x0043, // VK_C XK_C - /* 0x44 */ 0x0044, // VK_D XK_D - /* 0x45 */ 0x0045, // VK_E XK_E - /* 0x46 */ 0x0046, // VK_F XK_F - /* 0x47 */ 0x0047, // VK_G XK_G - /* 0x48 */ 0x0048, // VK_H XK_H - /* 0x49 */ 0x0049, // VK_I XK_I - /* 0x4a */ 0x004a, // VK_J XK_J - /* 0x4b */ 0x004b, // VK_K XK_K - /* 0x4c */ 0x004c, // VK_L XK_L - /* 0x4d */ 0x004d, // VK_M XK_M - /* 0x4e */ 0x004e, // VK_N XK_N - /* 0x4f */ 0x004f, // VK_O XK_O - /* 0x50 */ 0x0050, // VK_P XK_P - /* 0x51 */ 0x0051, // VK_Q XK_Q - /* 0x52 */ 0x0052, // VK_R XK_R - /* 0x53 */ 0x0053, // VK_S XK_S - /* 0x54 */ 0x0054, // VK_T XK_T - /* 0x55 */ 0x0055, // VK_U XK_U - /* 0x56 */ 0x0056, // VK_V XK_V - /* 0x57 */ 0x0057, // VK_W XK_W - /* 0x58 */ 0x0058, // VK_X XK_X - /* 0x59 */ 0x0059, // VK_Y XK_Y - /* 0x5a */ 0x005a, // VK_Z XK_Z + /* 0x41 */ kKeyNone, // VK_A XK_A + /* 0x42 */ kKeyNone, // VK_B XK_B + /* 0x43 */ kKeyNone, // VK_C XK_C + /* 0x44 */ kKeyNone, // VK_D XK_D + /* 0x45 */ kKeyNone, // VK_E XK_E + /* 0x46 */ kKeyNone, // VK_F XK_F + /* 0x47 */ kKeyNone, // VK_G XK_G + /* 0x48 */ kKeyNone, // VK_H XK_H + /* 0x49 */ kKeyNone, // VK_I XK_I + /* 0x4a */ kKeyNone, // VK_J XK_J + /* 0x4b */ kKeyNone, // VK_K XK_K + /* 0x4c */ kKeyNone, // VK_L XK_L + /* 0x4d */ kKeyNone, // VK_M XK_M + /* 0x4e */ kKeyNone, // VK_N XK_N + /* 0x4f */ kKeyNone, // VK_O XK_O + /* 0x50 */ kKeyNone, // VK_P XK_P + /* 0x51 */ kKeyNone, // VK_Q XK_Q + /* 0x52 */ kKeyNone, // VK_R XK_R + /* 0x53 */ kKeyNone, // VK_S XK_S + /* 0x54 */ kKeyNone, // VK_T XK_T + /* 0x55 */ kKeyNone, // VK_U XK_U + /* 0x56 */ kKeyNone, // VK_V XK_V + /* 0x57 */ kKeyNone, // VK_W XK_W + /* 0x58 */ kKeyNone, // VK_X XK_X + /* 0x59 */ kKeyNone, // VK_Y XK_Y + /* 0x5a */ kKeyNone, // VK_Z XK_Z /* 0x5b */ 0xffe7, // VK_LWIN XK_Meta_L /* 0x5c */ 0xffe8, // VK_RWIN XK_Meta_R /* 0x5d */ 0xff67, // VK_APPS XK_Menu @@ -649,12 +660,12 @@ static const KeyID g_virtualKey[] = /* 0x9d */ kKeyNone, // unassigned /* 0x9e */ kKeyNone, // unassigned /* 0x9f */ kKeyNone, // unassigned - /* 0xa0 */ kKeyNone, // unassigned - /* 0xa1 */ kKeyNone, // unassigned - /* 0xa2 */ kKeyNone, // unassigned - /* 0xa3 */ kKeyNone, // unassigned - /* 0xa4 */ kKeyNone, // unassigned - /* 0xa5 */ kKeyNone, // unassigned + /* 0xa0 */ 0xffe1, // VK_LSHIFT XK_Shift_L + /* 0xa1 */ 0xffe2, // VK_RSHIFT XK_Shift_R + /* 0xa2 */ 0xffe3, // VK_LCONTROL XK_Control_L + /* 0xa3 */ 0xffe4, // VK_RCONTROL XK_Control_R + /* 0xa4 */ 0xffe9, // VK_LMENU XK_Alt_L + /* 0xa5 */ 0xffea, // VK_RMENU XK_Alt_R /* 0xa6 */ kKeyNone, // unassigned /* 0xa7 */ kKeyNone, // unassigned /* 0xa8 */ kKeyNone, // unassigned @@ -749,69 +760,157 @@ static const KeyID g_virtualKey[] = KeyID CMSWindowsPrimaryScreen::mapKey( WPARAM vkCode, LPARAM info, - KeyModifierMask* maskOut) const + KeyModifierMask* maskOut) { + // note: known microsoft bugs + // Q72583 -- MapVirtualKey() maps keypad keys incorrectly + // 95,98: num pad vk code -> invalid scan code + // 95,98,NT4: num pad scan code -> bad vk code except + // SEPARATOR, MULTIPLY, SUBTRACT, ADD + + static const KeyID XK_Multi_key = 0xff20; + assert(maskOut != NULL); // map modifier key - // FIXME -- should be configurable? KeyModifierMask mask = 0; - if (GetKeyState(VK_SHIFT) < 0) + if (((m_keys[VK_LSHIFT] | + m_keys[VK_RSHIFT] | + m_keys[VK_SHIFT]) & 0x80) != 0) mask |= KeyModifierShift; - if ((GetKeyState(VK_CAPITAL) & 1) != 0) - mask |= KeyModifierCapsLock; - if (GetKeyState(VK_CONTROL) < 0) + if (((m_keys[VK_LCONTROL] | + m_keys[VK_RCONTROL] | + m_keys[VK_CONTROL]) & 0x80) != 0) mask |= KeyModifierControl; - if (GetKeyState(VK_MENU) < 0) + if (((m_keys[VK_LMENU] | + m_keys[VK_RMENU] | + m_keys[VK_MENU]) & 0x80) != 0) mask |= KeyModifierAlt; - if ((GetKeyState(VK_NUMLOCK) & 1) != 0) - mask |= KeyModifierNumLock; - if (GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0) + if (((m_keys[VK_LWIN] | + m_keys[VK_RWIN]) & 0x80) != 0) mask |= KeyModifierMeta; - if ((GetKeyState(VK_SCROLL) & 1) != 0) + if ((m_keys[VK_CAPITAL] & 0x01) != 0) + mask |= KeyModifierCapsLock; + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + mask |= KeyModifierNumLock; + if ((m_keys[VK_SCROLL] & 0x01) != 0) mask |= KeyModifierScrollLock; *maskOut = mask; + // get the scan code + UINT scanCode = static_cast((info & 0xff0000) >> 16); + + // convert virtual key to one that distinguishes between left and + // right for keys that have left/right versions. known scan codes + // that don't have left/right versions are passed through unchanged. + // unknown scan codes return 0. + UINT vkCode2 = MapVirtualKey(scanCode, 3); + + // work around bug Q72583 (bad num pad conversion in MapVirtualKey()) + if (vkCode >= VK_NUMPAD0 && vkCode <= VK_DIVIDE) + vkCode2 = vkCode; + + // MapVirtualKey() appears to map VK_LWIN, VK_RWIN, VK_APPS to + // some other meaningless virtual key. work around that bug. + else if (vkCode >= VK_LWIN && vkCode <= VK_APPS) + vkCode2 = vkCode; + + // sadly, win32 will not distinguish between the left and right + // control and alt keys using the above function. however, we + // can check for those: if bit 24 of info is set then the key + // is a "extended" key, such as the right control and right alt + // keys. + if ((info & 0x1000000) != 0) { + switch (vkCode2) { + case VK_LCONTROL: + vkCode2 = VK_RCONTROL; + break; + + case VK_LMENU: + vkCode2 = VK_RMENU; + break; + } + } + + // use left/right distinguishing virtual key + vkCode = vkCode2; + log((CLOG_DEBUG1 "key vk=%d scan=%d", vkCode, scanCode)); + + // handle some keys via table lookup KeyID id = g_virtualKey[vkCode]; if (id != kKeyNone) { return id; } - BYTE state[256]; - GetKeyboardState(state); + // check for dead keys + if (MapVirtualKey(vkCode, 2) >= 0x8000) { + return XK_Multi_key; + } + + // ToAscii() maps ctrl+letter to the corresponding control code + // and ctrl+backspace to delete. if we've got a control code or + // delete then do ToAscii() again but without the control state. + // ToAscii() interprets the control modifier state which we don't + // want. so save the control state then clear it. + BYTE lControl = m_keys[VK_LCONTROL]; + BYTE rControl = m_keys[VK_RCONTROL]; + BYTE control = m_keys[VK_CONTROL]; + m_keys[VK_LCONTROL] = 0; + m_keys[VK_RCONTROL] = 0; + m_keys[VK_CONTROL] = 0; + + // convert to ascii WORD ascii; - int result = ToAscii(vkCode, MapVirtualKey(0, vkCode), state, &ascii, 0); - if (result > 0) { - // FIXME -- handle dead keys - return (KeyID)(ascii & 0x00ff); + int result = ToAscii(vkCode, scanCode, m_keys, &ascii, 0); + + // restore control state + m_keys[VK_LCONTROL] = lControl; + m_keys[VK_RCONTROL] = rControl; + m_keys[VK_CONTROL] = control; + + // if result is less than zero then it was a dead key. that key + // is remembered by the keyboard which we don't want. remove it + // by calling ToAscii() again with arbitrary arguments. + if (result < 0) { + ToAscii(vkCode, scanCode, m_keys, &ascii, 0); + return XK_Multi_key; } + // if result is 1 then the key was succesfully converted + else if (result == 1) { + return static_cast(ascii & 0x00ff); + } + + // if result is 2 then a previous dead key could not be composed. + // put the old dead key back. + else if (result == 2) { + // get the scan code of the dead key and the shift state + // required to generate it. + vkCode = VkKeyScan(ascii & 0x00ff); + + // set shift state required to generate key + BYTE keys[256]; + memset(keys, 0, sizeof(keys)); + if (vkCode & 0x0100) + keys[VK_SHIFT] = 0x80; + if (vkCode & 0x0100) + keys[VK_CONTROL] = 0x80; + if (vkCode & 0x0100) + keys[VK_MENU] = 0x80; + + // strip shift state off of virtual key code + vkCode &= 0x00ff; + + // get the scan code for the key + scanCode = MapVirtualKey(vkCode, 0); + + // put it back + ToAscii(vkCode, scanCode, keys, &ascii, 0); + return XK_Multi_key; + } + + // cannot convert key return kKeyNone; - -/* - UINT character = MapVirtualKey(2, vkCode); - if (character != 0) { - if ((character & ~0xff) != 0) { - // dead key (i.e. a key to compose with the next) - // FIXME - return kKeyNone; - } - else { - // map character - KeyID id = g_virtualKey[character & 0xff]; - - // uppercase to lowercase conversion - if ((mask & KeyModifierShift) == 0 && id >= 'A' && id <= 'Z') - id += 0x20; - - return id; - } - } - else { - // non-ascii key - return g_virtualKey[vkCode]; - } -*/ } ButtonID CMSWindowsPrimaryScreen::mapButton( @@ -834,3 +933,117 @@ ButtonID CMSWindowsPrimaryScreen::mapButton( return kButtonNone; } } + +void CMSWindowsPrimaryScreen::updateKeys() +{ + // clear key state + memset(m_keys, 0, sizeof(m_keys)); + + // we only care about the modifier key states + m_keys[VK_LSHIFT] = GetKeyState(VK_LSHIFT); + m_keys[VK_RSHIFT] = GetKeyState(VK_RSHIFT); + m_keys[VK_SHIFT] = GetKeyState(VK_SHIFT); + m_keys[VK_LCONTROL] = GetKeyState(VK_LCONTROL); + m_keys[VK_RCONTROL] = GetKeyState(VK_RCONTROL); + m_keys[VK_CONTROL] = GetKeyState(VK_CONTROL); + m_keys[VK_LMENU] = GetKeyState(VK_LMENU); + m_keys[VK_RMENU] = GetKeyState(VK_RMENU); + m_keys[VK_MENU] = GetKeyState(VK_MENU); + m_keys[VK_LWIN] = GetKeyState(VK_LWIN); + m_keys[VK_RWIN] = GetKeyState(VK_RWIN); + m_keys[VK_APPS] = GetKeyState(VK_APPS); + m_keys[VK_CAPITAL] = GetKeyState(VK_CAPITAL); + m_keys[VK_NUMLOCK] = GetKeyState(VK_NUMLOCK); + m_keys[VK_SCROLL] = GetKeyState(VK_SCROLL); +} + +void CMSWindowsPrimaryScreen::updateKey( + UINT vkCode, bool press) +{ + if (press) { + switch (vkCode) { + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + m_keys[vkCode] |= 0x80; + m_keys[VK_SHIFT] |= 0x80; + break; + + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + m_keys[vkCode] |= 0x80; + m_keys[VK_CONTROL] |= 0x80; + break; + + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + m_keys[vkCode] |= 0x80; + m_keys[VK_MENU] |= 0x80; + break; + + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + m_keys[vkCode] |= 0x80; + break; + + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // toggle keys + m_keys[vkCode] |= 0x80; + if ((m_keys[vkCode] & 0x01) == 0) { + m_keys[vkCode] |= 0x01; + } + break; + } + } + else { + switch (vkCode) { + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + m_keys[vkCode] &= ~0x80; + if (((m_keys[VK_LSHIFT] | m_keys[VK_RSHIFT]) & 0x80) == 0) { + m_keys[VK_SHIFT] &= ~0x80; + } + break; + + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + m_keys[vkCode] &= ~0x80; + if (((m_keys[VK_LCONTROL] | m_keys[VK_RCONTROL]) & 0x80) == 0) { + m_keys[VK_CONTROL] &= ~0x80; + } + break; + + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + m_keys[vkCode] &= ~0x80; + if (((m_keys[VK_LMENU] | m_keys[VK_RMENU]) & 0x80) == 0) { + m_keys[VK_MENU] &= ~0x80; + } + break; + + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + m_keys[vkCode] &= ~0x80; + break; + + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // toggle keys + m_keys[vkCode] &= ~0x80; + if ((m_keys[vkCode] & 0x01) != 0) { + m_keys[vkCode] &= ~0x01; + } + break; + } + } +} diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index bfa1eb56..9e19a4b5 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -21,11 +21,11 @@ public: virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); virtual void leave(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void setClipboard(const IClipboard*); - virtual void grabClipboard(); + virtual void setClipboard(ClipboardID, const IClipboard*); + virtual void grabClipboard(ClipboardID); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; - virtual void getClipboard(IClipboard*) const; + virtual void getClipboard(ClipboardID, IClipboard*) const; protected: // CMSWindowsScreen overrides @@ -40,8 +40,10 @@ private: void nextMark(); KeyID mapKey(WPARAM keycode, LPARAM info, - KeyModifierMask* maskOut) const; + KeyModifierMask* maskOut); ButtonID mapButton(WPARAM button) const; + void updateKeys(); + void updateKey(UINT vkCode, bool press); private: CServer* m_server; @@ -53,6 +55,7 @@ private: HINSTANCE m_hookLibrary; UInt32 m_mark; UInt32 m_markReceived; + BYTE m_keys[256]; }; #endif diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index c69b1144..33159fc2 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -118,7 +118,7 @@ void CServerProtocol1_0::sendGrabClipboard(ClipboardID id) void CServerProtocol1_0::sendScreenSaver(bool on) { - log((CLOG_DEBUG1 "send screen saver to \"%s\"", getClient().c_str())); + log((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getClient().c_str(), on ? 1 : 0)); CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } @@ -132,7 +132,7 @@ void CServerProtocol1_0::sendKeyDown( void CServerProtocol1_0::sendKeyRepeat( KeyID key, KeyModifierMask mask, SInt32 count) { - log((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); + log((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getClient().c_str(), key, mask, count)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); } @@ -167,7 +167,7 @@ void CServerProtocol1_0::sendMouseMove( void CServerProtocol1_0::sendMouseWheel( SInt32 delta) { - log((CLOG_DEBUG1 "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta)); + log((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); } diff --git a/server/server.rc b/server/server.rc index 60c3a920..731c32ce 100644 --- a/server/server.rc +++ b/server/server.rc @@ -52,12 +52,12 @@ END // Dialog // -IDD_SYNERGY DIALOG DISCARDABLE 0, 0, 329, 158 +IDD_SYNERGY DIALOG DISCARDABLE 0, 0, 531, 159 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Synergy" FONT 8, "MS Sans Serif" BEGIN - EDITTEXT IDC_LOG,7,7,315,144,ES_MULTILINE | ES_AUTOHSCROLL | + EDITTEXT IDC_LOG,7,7,517,145,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL END @@ -73,9 +73,9 @@ BEGIN IDD_SYNERGY, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 322 + RIGHTMARGIN, 524 TOPMARGIN, 7 - BOTTOMMARGIN, 151 + BOTTOMMARGIN, 152 END END #endif // APSTUDIO_INVOKED diff --git a/synergy/CClipboard.cpp b/synergy/CClipboard.cpp index e5021aab..bf5d94ed 100644 --- a/synergy/CClipboard.cpp +++ b/synergy/CClipboard.cpp @@ -1,4 +1,5 @@ #include "CClipboard.h" +#include // // CClipboard @@ -92,7 +93,7 @@ void CClipboard::unmarshall(const CString& data, Time time) index += 4; // read each format - for (UInt32 format = 0; format < numFormats; ++format) { + for (UInt32 i = 0; i < numFormats; ++i) { // get the format id UInt32 format = readUInt32(index); index += 4; @@ -144,10 +145,11 @@ CString CClipboard::marshall() const UInt32 CClipboard::readUInt32(const char* buf) const { - return (static_cast(buf[0]) << 24) | - (static_cast(buf[1]) << 16) | - (static_cast(buf[2]) << 8) | - static_cast(buf[3]); + const unsigned char* ubuf = reinterpret_cast(buf); + return (static_cast(ubuf[0]) << 24) | + (static_cast(ubuf[1]) << 16) | + (static_cast(ubuf[2]) << 8) | + static_cast(ubuf[3]); } void CClipboard::writeUInt32(CString* buf, UInt32 v) const diff --git a/synergy/CMSWindowsClipboard.cpp b/synergy/CMSWindowsClipboard.cpp index 4b0049cf..f1b2060b 100644 --- a/synergy/CMSWindowsClipboard.cpp +++ b/synergy/CMSWindowsClipboard.cpp @@ -6,7 +6,9 @@ // CMSWindowsClipboard // -CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : m_window(window) +CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : + m_window(window), + m_time(0) { // do nothing } @@ -16,7 +18,7 @@ CMSWindowsClipboard::~CMSWindowsClipboard() // do nothing } -bool CMSWindowsClipboard::open() +bool CMSWindowsClipboard::open(Time time) { log((CLOG_INFO "open clipboard")); @@ -29,7 +31,12 @@ bool CMSWindowsClipboard::open() } else { log((CLOG_WARN "failed to grab clipboard")); + CloseClipboard(); + return false; } + + m_time = time; + return true; } @@ -71,6 +78,11 @@ void CMSWindowsClipboard::add( CloseClipboard(); } +IClipboard::Time CMSWindowsClipboard::getTime() const +{ + return m_time; +} + bool CMSWindowsClipboard::has(EFormat format) const { const UINT win32Format = convertFormatToWin32(format); @@ -122,7 +134,7 @@ HANDLE CMSWindowsClipboard::convertTextToWin32( const CString& data) const { // compute size of converted text - UInt32 dstSize = 0; + UInt32 dstSize = 1; const UInt32 srcSize = data.size(); const char* src = data.c_str(); for (UInt32 index = 0; index < srcSize; ++index) { @@ -148,6 +160,7 @@ HANDLE CMSWindowsClipboard::convertTextToWin32( } dst[dstSize++] = src[index]; } + dst[dstSize] = '\0'; // done converting GlobalUnlock(gData); @@ -162,9 +175,12 @@ CString CMSWindowsClipboard::convertTextFromWin32( // get source data and it's size const char* src = (const char*)GlobalLock(handle); UInt32 srcSize = (SInt32)GlobalSize(handle); - if (src == NULL || srcSize == 0) + if (src == NULL || srcSize <= 1) return CString(); + // ignore trailing NUL + --srcSize; + // compute size of converted text UInt32 dstSize = 0; UInt32 index; diff --git a/synergy/CMSWindowsClipboard.h b/synergy/CMSWindowsClipboard.h index 8fae59c0..0010af57 100644 --- a/synergy/CMSWindowsClipboard.h +++ b/synergy/CMSWindowsClipboard.h @@ -10,9 +10,10 @@ public: virtual ~CMSWindowsClipboard(); // IClipboard overrides - virtual bool open(); + virtual bool open(Time); virtual void close(); virtual void add(EFormat, const CString& data); + virtual Time getTime() const; virtual bool has(EFormat) const; virtual CString get(EFormat) const; @@ -23,6 +24,7 @@ private: private: HWND m_window; + Time m_time; }; #endif diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp index 6b95ec97..91f325ee 100644 --- a/synergy/synergy.dsp +++ b/synergy/synergy.dsp @@ -131,6 +131,10 @@ SOURCE=.\CInputPacketStream.h # End Source File # Begin Source File +SOURCE=.\ClipboardTypes.h +# End Source File +# Begin Source File + SOURCE=.\CMSWindowsClipboard.h # End Source File # Begin Source File From c2a69d6c6446c1c1122a5f8b2f8e2df8029f28a1 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Apr 2002 16:23:30 +0000 Subject: [PATCH 059/807] Changed name for auto-generated dependency files from Makedepend to .depend. --- Make-linux | 2 +- Make-solaris | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Make-linux b/Make-linux index 3d500d25..98572f46 100644 --- a/Make-linux +++ b/Make-linux @@ -42,7 +42,7 @@ MKDEP = $(DEPTH)/tools/depconv MKDEPOPT = -MD MKDEPPRE = MKDEPPOST = $(MKDEP) -f $(MKDEPFILE) $*.d; $(RM) $*.d -MKDEPFILE = Makedepend +MKDEPFILE = .depend # # stuff to clean diff --git a/Make-solaris b/Make-solaris index c045f2f5..8730b63e 100644 --- a/Make-solaris +++ b/Make-solaris @@ -43,7 +43,7 @@ MKDEP = $(DEPTH)/tools/depconv MKDEPOPT = -MD MKDEPPRE = MKDEPPOST = $(MKDEP) -f $(MKDEPFILE) $*.d; $(RM) $*.d -MKDEPFILE = Makedepend +MKDEPFILE = .depend # # stuff to clean From 56877bcc7dcb1feb134b158d573f15eb535759b6 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Apr 2002 16:25:29 +0000 Subject: [PATCH 060/807] Added logging and handling of "half-duplex" caps-lock key. --- client/CXWindowsSecondaryScreen.cpp | 23 ++++++++++++++++++++++- client/CXWindowsSecondaryScreen.h | 4 ++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index cc935285..d1ea46ab 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -156,6 +156,11 @@ void CXWindowsSecondaryScreen::open(CClient* client) updateModifiers(display); } + // check for peculiarities + // FIXME -- may have to get these from some database + m_capsLockHalfDuplex = false; +// m_capsLockHalfDuplex = true; + // assume primary has all clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) grabClipboard(id); @@ -403,18 +408,28 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // keysym with that mask. we override the bits in the mask // that cannot be accomodated. + // note if the key is the caps lock and it's "half-duplex" + const bool isHalfDuplex = (id == XK_Caps_Lock && m_capsLockHalfDuplex); + + // ignore releases for half-duplex keys + if (isHalfDuplex && !press) { + return m_mask; + } + // lookup the a keycode for this key id. also return the // key modifier mask required. unsigned int outMask; if (!findKeyCode(keycode, outMask, id, maskToX(mask))) { // we cannot generate the desired keysym because no key // maps to that keysym. just return the current mask. + log((CLOG_DEBUG2 "no keycode for keysym %d modifiers %04x", id, mask)); return m_mask; } // if we cannot match the modifier mask then don't return any // keys and just return the current mask. if ((outMask & m_modifierMask) != outMask) { + log((CLOG_DEBUG2 "cannot match modifiers %04x", outMask)); return m_mask; } @@ -482,6 +497,11 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( } } + // note if the press of a half-duplex key should be treated as a release + if (isHalfDuplex && (m_mask & (1 << index->second)) != 0) { + press = false; + } + // add the key event keys.push_back(std::make_pair(keycode, press)); @@ -501,7 +521,8 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // toggle keys modify the state on press if toggling on and on // release if toggling off. other keys set the bit on press - // and clear the bit on release. + // and clear the bit on release. if half-duplex then toggle + // each time we get here. if ((modifierBit & m_toggleModifierMask) != 0) { if (((mask & modifierBit) == 0) == press) mask ^= modifierBit; diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 4e0f8c2e..66fe2c55 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -68,6 +68,10 @@ private: CClient* m_client; Window m_window; + // note if caps lock key toggles on up/down (false) or on + // transition (true) + bool m_capsLockHalfDuplex; + // set entries indicate keys that are pressed. indexed by keycode. bool m_keys[256]; From b279c80608c997907fed015c0b17e96f53fdb699 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Apr 2002 17:48:11 +0000 Subject: [PATCH 061/807] checkpoint. now sending toggle modifier state when entering a screen. this allows the secondary screen to set it's modifier state to match the primary screen's state. this is not strictly necessary since each keystroke should adjust the modifier state as needed to get the right result. --- client/CClient.cpp | 7 ++- client/CMSWindowsSecondaryScreen.cpp | 29 +++++++++- client/CMSWindowsSecondaryScreen.h | 4 +- client/CXWindowsSecondaryScreen.cpp | 84 +++++++++++++++++++++++----- client/CXWindowsSecondaryScreen.h | 7 ++- server/CMSWindowsPrimaryScreen.cpp | 12 ++++ server/CMSWindowsPrimaryScreen.h | 1 + server/CServer.cpp | 3 +- server/CServerProtocol.h | 3 +- server/CServerProtocol1_0.cpp | 8 ++- server/CServerProtocol1_0.h | 3 +- server/CXWindowsPrimaryScreen.cpp | 67 ++++++++++++++++++++++ server/CXWindowsPrimaryScreen.h | 10 ++++ synergy/IPrimaryScreen.h | 5 ++ synergy/ISecondaryScreen.h | 3 +- synergy/IServerProtocol.h | 3 +- synergy/ProtocolTypes.h | 7 ++- 17 files changed, 224 insertions(+), 32 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 2e278cb7..d1ef18a6 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -341,13 +341,14 @@ void CClient::closeSecondaryScreen() void CClient::onEnter() { SInt16 x, y; + UInt16 mask; { CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum); + CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum, &mask); m_active = true; } - log((CLOG_DEBUG1 "recv enter, %d,%d %d", x, y, m_seqNum)); - m_screen->enter(x, y); + log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, m_seqNum, mask)); + m_screen->enter(x, y, static_cast(mask)); } void CClient::onLeave() diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index e1dc6ab4..6d2fd886 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -109,11 +109,12 @@ void CMSWindowsSecondaryScreen::close() m_client = NULL; } -void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) +void CMSWindowsSecondaryScreen::enter( + SInt32 x, SInt32 y, KeyModifierMask mask) { assert(m_window != NULL); - log((CLOG_INFO "entering screen at %d,%d", x, y)); + log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); // warp to requested location SInt32 w, h; @@ -130,6 +131,17 @@ void CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) // update our keyboard state to reflect the local state updateKeys(); updateModifiers(); + + // toggle modifiers that don't match the desired state + if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { + toggleKey(VK_CAPITAL, KeyModifierCapsLock); + } + if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { + toggleKey(VK_NUMLOCK, KeyModifierNumLock); + } + if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { + toggleKey(VK_SCROLL, KeyModifierScrollLock); + } } void CMSWindowsSecondaryScreen::leave() @@ -1184,3 +1196,16 @@ void CMSWindowsSecondaryScreen::updateModifiers() if ((m_keys[VK_SCROLL] & 0x01) != 0) m_mask |= KeyModifierScrollLock; } + +void CMSWindowsSecondaryScreen::toggleKey( + UINT virtualKey, KeyModifierMask mask) +{ + // send key events to simulate a press and release + const UINT code = MapVirtualKey(virtualKey, 0); + keybd_event(virtualKey, code, 0, 0); + keybd_event(virtualKey, code, KEYEVENTF_KEYUP, 0); + + // toggle shadow state + m_mask ^= mask; + m_keys[virtualKey] ^= 0x01; +} diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index dbd9b5a8..56235c65 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -16,7 +16,8 @@ public: virtual void stop(); virtual void open(CClient*); virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, + KeyModifierMask mask); virtual void leave(); virtual void keyDown(KeyID, KeyModifierMask); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -48,6 +49,7 @@ private: void updateKeys(); void updateModifiers(); + void toggleKey(UINT virtualKey, KeyModifierMask mask); private: CClient* m_client; diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index d1ea46ab..8ec64d2d 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -177,7 +177,8 @@ void CXWindowsSecondaryScreen::close() m_client = NULL; } -void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) +void CXWindowsSecondaryScreen::enter( + SInt32 x, SInt32 y, KeyModifierMask mask) { assert(m_window != None); @@ -193,6 +194,19 @@ void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) // update our keyboard state to reflect the local state updateKeys(display); updateModifiers(display); + + // toggle modifiers that don't match the desired state + unsigned int xMask = maskToX(mask); + if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) { + toggleKey(display, XK_Caps_Lock, m_capsLockMask); + } + if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) { + toggleKey(display, XK_Num_Lock, m_numLockMask); + } + if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { + toggleKey(display, XK_Scroll_Lock, m_scrollLockMask); + } + XSync(display, False); } void CXWindowsSecondaryScreen::leave() @@ -461,9 +475,14 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( const KeyCode modifierKey = modifierKeys[0]; keys.push_back(std::make_pair(modifierKey, True)); if ((bit & m_toggleModifierMask) != 0) { - keys.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, True)); + if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { + keys.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, True)); + } + else { + undo.push_back(std::make_pair(modifierKey, False)); + } } else { undo.push_back(std::make_pair(modifierKey, False)); @@ -476,12 +495,18 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // press/release, otherwise deactivate it with a // release. we must check each keycode for the // modifier if not a toggle. - if (bit & m_toggleModifierMask) { + if ((bit & m_toggleModifierMask) != 0) { const KeyCode modifierKey = modifierKeys[0]; - keys.push_back(std::make_pair(modifierKey, True)); - keys.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, True)); + if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { + keys.push_back(std::make_pair(modifierKey, True)); + keys.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, True)); + } + else { + keys.push_back(std::make_pair(modifierKey, False)); + undo.push_back(std::make_pair(modifierKey, True)); + } } else { for (unsigned int j = 0; j < m_keysPerModifier; ++j) { @@ -601,11 +626,11 @@ unsigned int CXWindowsSecondaryScreen::maskToX( if (inMask & KeyModifierMeta) outMask |= Mod4Mask; if (inMask & KeyModifierCapsLock) - outMask |= LockMask; + outMask |= m_capsLockMask; if (inMask & KeyModifierNumLock) - outMask |= Mod2Mask; + outMask |= m_numLockMask; if (inMask & KeyModifierScrollLock) - outMask |= Mod5Mask; + outMask |= m_scrollLockMask; return outMask; } @@ -712,6 +737,7 @@ void CXWindowsSecondaryScreen::updateModifierMap( m_toggleModifierMask = 0; m_numLockMask = 0; m_capsLockMask = 0; + m_scrollLockMask = 0; m_keysPerModifier = keymap->max_keypermod; m_modifierToKeycode.clear(); m_modifierToKeycode.resize(8 * m_keysPerModifier); @@ -738,10 +764,15 @@ void CXWindowsSecondaryScreen::updateModifierMap( m_toggleModifierMask |= bit; // note num/caps-lock - if (keysym == XK_Num_Lock) + if (keysym == XK_Num_Lock) { m_numLockMask |= bit; - if (keysym == XK_Caps_Lock) + } + else if (keysym == XK_Caps_Lock) { m_capsLockMask |= bit; + } + else if (keysym == XK_Scroll_Lock) { + m_scrollLockMask |= bit; + } } } } @@ -749,6 +780,31 @@ void CXWindowsSecondaryScreen::updateModifierMap( XFreeModifiermap(keymap); } +void CXWindowsSecondaryScreen::toggleKey( + Display* display, + KeySym keysym, unsigned int mask) +{ + // lookup the keycode + KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym); + if (index == m_keycodeMap.end()) + return; + KeyCode keycode = index->second.keycode; + + // toggle the key + if (keysym == XK_Caps_Lock && m_capsLockHalfDuplex) { + // "half-duplex" toggle + XTestFakeKeyEvent(display, keycode, (m_mask & mask) == 0, CurrentTime); + } + else { + // normal toggle + XTestFakeKeyEvent(display, keycode, True, CurrentTime); + XTestFakeKeyEvent(display, keycode, False, CurrentTime); + } + + // toggle shadow state + m_mask ^= mask; +} + bool CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) { switch (key) { diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 66fe2c55..15388442 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -15,7 +15,8 @@ public: virtual void stop(); virtual void open(CClient*); virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, + KeyModifierMask mask); virtual void leave(); virtual void keyDown(KeyID, KeyModifierMask); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -62,6 +63,7 @@ private: void updateKeycodeMap(Display* display); void updateModifiers(Display* display); void updateModifierMap(Display* display); + void toggleKey(Display*, KeySym, unsigned int mask); static bool isToggleKeysym(KeySym); private: @@ -88,9 +90,10 @@ private: // set bits indicate modifiers that toggle (e.g. caps-lock) unsigned int m_toggleModifierMask; - // masks that indicate which modifier bits are num-lock and caps-lock + // masks that indicate which modifier bits are for toggle keys unsigned int m_numLockMask; unsigned int m_capsLockMask; + unsigned int m_scrollLockMask; // map X modifier key indices to the key codes bound to them unsigned int m_keysPerModifier; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 9c4f3169..51196271 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -252,6 +252,18 @@ void CMSWindowsPrimaryScreen::getClipboard( CClipboard::copy(dst, &src); } +KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const +{ + KeyModifierMask mask; + if ((m_keys[VK_CAPITAL] & 0x01) != 0) + mask |= KeyModifierCapsLock; + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + mask |= KeyModifierNumLock; + if ((m_keys[VK_SCROLL] & 0x01) != 0) + mask |= KeyModifierScrollLock; + return mask; +} + #include "resource.h" // FIXME void CMSWindowsPrimaryScreen::onOpenDisplay() diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 9e19a4b5..f24d51a2 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -26,6 +26,7 @@ public: virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; + virtual KeyModifierMask getToggleMask() const; protected: // CMSWindowsScreen overrides diff --git a/server/CServer.cpp b/server/CServer.cpp index 4e82f043..aaea5f20 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -537,7 +537,8 @@ void CServer::switchScreen(CScreenInfo* dst, m_primary->enter(x, y); } else { - m_active->m_protocol->sendEnter(x, y, m_seqNum); + m_active->m_protocol->sendEnter(x, y, m_seqNum, + m_primary->getToggleMask()); } // send the clipboard data to new active screen diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index d2cb2d80..c64fa6cf 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -31,7 +31,8 @@ public: virtual void run() = 0; virtual void queryInfo() = 0; virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask) = 0; virtual void sendLeave() = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 33159fc2..7e9a9802 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -91,10 +91,12 @@ void CServerProtocol1_0::sendClose() } void CServerProtocol1_0::sendEnter( - SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) + SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask) { - log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d", getClient().c_str(), xAbs, yAbs, seqNum)); - CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs, seqNum); + log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getClient().c_str(), xAbs, yAbs, seqNum, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgCEnter, + xAbs, yAbs, seqNum, mask); } void CServerProtocol1_0::sendLeave() diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index f992bec8..e7fbcc58 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -16,7 +16,8 @@ public: virtual void run(); virtual void queryInfo(); virtual void sendClose(); - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum); + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask); virtual void sendLeave(); virtual void sendClipboard(ClipboardID, const CString&); virtual void sendGrabClipboard(ClipboardID); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index bafcbd02..bff3596b 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -47,6 +47,7 @@ void CXWindowsPrimaryScreen::run() // keyboard mapping changed CDisplayLock display(this); XRefreshKeyboardMapping(&xevent.xmapping); + updateModifierMap(display); break; } @@ -219,6 +220,12 @@ void CXWindowsPrimaryScreen::open(CServer* server) // FIXME -- may have to get these from some database m_capsLockHalfDuplex = false; // m_capsLockHalfDuplex = true; + + // update key state + { + CDisplayLock display(this); + updateModifierMap(display); + } } void CXWindowsPrimaryScreen::close() @@ -365,6 +372,31 @@ void CXWindowsPrimaryScreen::getClipboard( getDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); } +KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const +{ + CDisplayLock display(this); + + // query the pointer to get the keyboard state + // FIXME -- is there a better way to do this? + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) + return 0; + + // convert to KeyModifierMask + KeyModifierMask mask; + if (state & m_numLockMask) + mask |= KeyModifierNumLock; + if (state & m_capsLockMask) + mask |= KeyModifierCapsLock; + if (state & m_scrollLockMask) + mask |= KeyModifierScrollLock; + + return mask; +} + void CXWindowsPrimaryScreen::onOpenDisplay() { assert(m_window == None); @@ -483,3 +515,38 @@ ButtonID CXWindowsPrimaryScreen::mapButton( else return kButtonNone; } + +void CXWindowsPrimaryScreen::updateModifierMap( + Display* display) +{ + // get modifier map from server + XModifierKeymap* keymap = XGetModifierMapping(display); + + // initialize + m_numLockMask = 0; + m_capsLockMask = 0; + m_scrollLockMask = 0; + + // set keycodes and masks + for (unsigned int i = 0; i < 8; ++i) { + const unsigned int bit = (1 << i); + for (int j = 0; j < keymap->max_keypermod; ++j) { + KeyCode keycode = keymap->modifiermap[i * + keymap->max_keypermod + j]; + + // note toggle modifier bits + const KeySym keysym = XKeycodeToKeysym(display, keycode, 0); + if (keysym == XK_Num_Lock) { + m_numLockMask |= bit; + } + else if (keysym == XK_Caps_Lock) { + m_capsLockMask |= bit; + } + else if (keysym == XK_Scroll_Lock) { + m_scrollLockMask |= bit; + } + } + } + + XFreeModifiermap(keymap); +} diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index ba1b4584..5df90544 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -24,6 +24,7 @@ public: virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; + virtual KeyModifierMask getToggleMask() const; protected: // CXWindowsScreen overrides @@ -40,12 +41,21 @@ private: KeyID mapKey(XKeyEvent*) const; ButtonID mapButton(unsigned int button) const; + void updateModifierMap(Display* display); + private: CServer* m_server; bool m_active; Window m_window; + // note if caps lock key toggles on up/down (false) or on + // transition (true) bool m_capsLockHalfDuplex; + + // masks that indicate which modifier bits are for toggle keys + unsigned int m_numLockMask; + unsigned int m_capsLockMask; + unsigned int m_scrollLockMask; }; #endif diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 198c917a..64c44bec 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -79,6 +79,11 @@ public: // and should avoid setting the clipboard object if the screen's // clipboard hasn't changed. virtual void getClipboard(ClipboardID, IClipboard*) const = 0; + + // get the primary screen's current toggle modifier key state. + // the returned mask should have the corresponding bit set for + // each toggle key that is active. + virtual KeyModifierMask getToggleMask() const = 0; }; #endif diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 11752c2b..c7f1ba3a 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -34,7 +34,8 @@ public: // called when the user navigates to the secondary screen. warp // the cursor to the given coordinates and unhide it. prepare to // simulate input events. - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, + KeyModifierMask mask) = 0; // called when the user navigates off the secondary screen. clean // up input event simulation and hide the cursor. diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index 1bf4a9df..c8d40f2a 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -24,7 +24,8 @@ public: // send various messages to client virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask) = 0; virtual void sendLeave() = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index f9b5edf7..13588df9 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -24,8 +24,11 @@ static const char kMsgCClose[] = "CBYE"; // entering screen at screen position $1 = x, $2 = y. x,y are // absolute screen coordinates. $3 = sequence number, which is // used to order messages between screens. the secondary screen -// must return this number with some messages. -static const char kMsgCEnter[] = "CINN%2i%2i%4i"; +// must return this number with some messages. $4 = modifier key +// mask. this will have bits set for each toggle modifier key +// that is activated on entry to the screen. the secondary screen +// should adjust its toggle modifiers to reflect that state. +static const char kMsgCEnter[] = "CINN%2i%2i%4i%2i"; // leave screen: primary -> secondary // leaving screen. the secondary screen should send clipboard From f536e1cece0417e70946ac7f95fba37c21094d34 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Apr 2002 18:30:05 +0000 Subject: [PATCH 062/807] added fallback for missing numpad movement keys (if there's no mapping for those keys then the non-keypad versions are tried). --- client/CXWindowsSecondaryScreen.cpp | 57 ++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 8ec64d2d..8bde1c78 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -583,8 +583,61 @@ bool CXWindowsSecondaryScreen::findKeyCode( // what we need but won't tell us which index to use with the // keycode. return false if there's no keycode to generate id. KeyCodeMap::const_iterator index = m_keycodeMap.find(id); - if (index == m_keycodeMap.end()) - return false; + if (index == m_keycodeMap.end()) { + // try backup keysym for certain keys (particularly the numpad + // keys since most laptops don't have a separate numpad and the + // numpad overlaying the main keyboard may not have movement + // key bindings). + switch (id) { + case XK_KP_Home: + id = XK_Home; + break; + + case XK_KP_Left: + id = XK_Left; + break; + + case XK_KP_Up: + id = XK_Up; + break; + + case XK_KP_Right: + id = XK_Right; + break; + + case XK_KP_Down: + id = XK_Down; + break; + + case XK_KP_Prior: + id = XK_Prior; + break; + + case XK_KP_Next: + id = XK_Next; + break; + + case XK_KP_End: + id = XK_End; + break; + + case XK_KP_Insert: + id = XK_Insert; + break; + + case XK_KP_Delete: + id = XK_Delete; + break; + + default: + return false; + } + + index = m_keycodeMap.find(id); + if (index == m_keycodeMap.end()) { + return false; + } + } // save the keycode keycode = index->second.keycode; From c8737de4ade0ee7fed24cbc1058b7b3ee9f8a9c6 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 1 May 2002 14:35:55 +0000 Subject: [PATCH 063/807] removed obsolete files. --- net/CSocketInputStream.cpp | 93 ------------------------------- net/CSocketInputStream.h | 42 -------------- net/CSocketOutputStream.cpp | 78 -------------------------- net/CSocketOutputStream.h | 43 --------------- net/CSocketStreamBuffer.cpp | 106 ------------------------------------ net/CSocketStreamBuffer.h | 38 ------------- net/net.dsp | 24 -------- 7 files changed, 424 deletions(-) delete mode 100644 net/CSocketInputStream.cpp delete mode 100644 net/CSocketInputStream.h delete mode 100644 net/CSocketOutputStream.cpp delete mode 100644 net/CSocketOutputStream.h delete mode 100644 net/CSocketStreamBuffer.cpp delete mode 100644 net/CSocketStreamBuffer.h diff --git a/net/CSocketInputStream.cpp b/net/CSocketInputStream.cpp deleted file mode 100644 index 4bbac9a6..00000000 --- a/net/CSocketInputStream.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "CSocketInputStream.h" -#include "CLock.h" -#include "CMutex.h" -#include "CThread.h" -#include "IJob.h" -#include "XIO.h" -#include -#include - -// -// CSocketInputStream -// - -CSocketInputStream::CSocketInputStream(CMutex* mutex, IJob* closeCB) : - m_mutex(mutex), - m_empty(mutex, true), - m_closeCB(closeCB), - m_closed(false), - m_hungup(false) -{ - assert(m_mutex != NULL); -} - -CSocketInputStream::~CSocketInputStream() -{ - delete m_closeCB; -} - -void CSocketInputStream::write( - const void* data, UInt32 n) -{ - if (!m_hungup && n > 0) { - m_buffer.write(data, n); - m_empty = (m_buffer.getSize() == 0); - m_empty.broadcast(); - } -} - -void CSocketInputStream::hangup() -{ - m_hungup = true; - m_empty.broadcast(); -} - -void CSocketInputStream::close() -{ - CLock lock(m_mutex); - if (m_closed) { - throw XIOClosed(); - } - - m_closed = true; - if (m_closeCB) { - m_closeCB->run(); - } -} - -UInt32 CSocketInputStream::read( - void* dst, UInt32 n) -{ - CLock lock(m_mutex); - if (m_closed) { - throw XIOClosed(); - } - - // wait for data (or hangup) - while (!m_hungup && m_empty == true) { - m_empty.wait(); - } - - // read data - const UInt32 count = m_buffer.getSize(); - if (n > count) { - n = count; - } - if (n > 0) { - memcpy(dst, m_buffer.peek(n), n); - m_buffer.pop(n); - } - - // update empty state - if (m_buffer.getSize() == 0) { - m_empty = true; - m_empty.broadcast(); - } - return n; -} - -UInt32 CSocketInputStream::getSize() const -{ - CLock lock(m_mutex); - return m_buffer.getSize(); -} diff --git a/net/CSocketInputStream.h b/net/CSocketInputStream.h deleted file mode 100644 index 3e686df3..00000000 --- a/net/CSocketInputStream.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef CSOCKETINPUTSTREAM_H -#define CSOCKETINPUTSTREAM_H - -#include "CSocketStreamBuffer.h" -#include "CCondVar.h" -#include "IInputStream.h" - -class CMutex; -class IJob; - -class CSocketInputStream : public IInputStream { -public: - CSocketInputStream(CMutex*, IJob* adoptedCloseCB); - ~CSocketInputStream(); - - // manipulators - - // write() appends n bytes to the buffer - void write(const void*, UInt32 n); - - // causes read() to always return immediately. if there is no - // more data then it returns 0. further writes are discarded. - void hangup(); - - // accessors - - // IInputStream overrides - // these all lock the mutex for their duration - virtual void close(); - virtual UInt32 read(void*, UInt32 count); - virtual UInt32 getSize() const; - -private: - CMutex* m_mutex; - CCondVar m_empty; - IJob* m_closeCB; - CSocketStreamBuffer m_buffer; - bool m_closed; - bool m_hungup; -}; - -#endif diff --git a/net/CSocketOutputStream.cpp b/net/CSocketOutputStream.cpp deleted file mode 100644 index fe9998be..00000000 --- a/net/CSocketOutputStream.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "CSocketOutputStream.h" -#include "CLock.h" -#include "CMutex.h" -#include "CThread.h" -#include "IJob.h" -#include "XIO.h" -#include - -// -// CSocketOutputStream -// - -CSocketOutputStream::CSocketOutputStream(CMutex* mutex, IJob* closeCB) : - m_mutex(mutex), - m_closeCB(closeCB), - m_closed(false) -{ - assert(m_mutex != NULL); -} - -CSocketOutputStream::~CSocketOutputStream() -{ - delete m_closeCB; -} - -const void* CSocketOutputStream::peek(UInt32 n) -{ - return m_buffer.peek(n); -} - -void CSocketOutputStream::pop(UInt32 n) -{ - m_buffer.pop(n); -} - -UInt32 CSocketOutputStream::getSize() const -{ - return m_buffer.getSize(); -} - -void CSocketOutputStream::close() -{ - CLock lock(m_mutex); - if (m_closed) { - throw XIOClosed(); - } - - m_closed = true; - if (m_closeCB) { - m_closeCB->run(); - } -} - -UInt32 CSocketOutputStream::write( - const void* data, UInt32 n) -{ - CLock lock(m_mutex); - if (m_closed) { - throw XIOClosed(); - } - - m_buffer.write(data, n); - return n; -} - -void CSocketOutputStream::flush() -{ - // wait until all data is written - while (getSizeWithLock() > 0) { - CThread::sleep(0.05); - } -} - -UInt32 CSocketOutputStream::getSizeWithLock() const -{ - CLock lock(m_mutex); - return m_buffer.getSize(); -} diff --git a/net/CSocketOutputStream.h b/net/CSocketOutputStream.h deleted file mode 100644 index 4f65b5c1..00000000 --- a/net/CSocketOutputStream.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef CSOCKETOUTPUTSTREAM_H -#define CSOCKETOUTPUTSTREAM_H - -#include "CSocketStreamBuffer.h" -#include "IOutputStream.h" - -class CMutex; -class IJob; - -class CSocketOutputStream : public IOutputStream { -public: - CSocketOutputStream(CMutex*, IJob* adoptedCloseCB); - ~CSocketOutputStream(); - - // manipulators - - // peek() returns a buffer of n bytes (which must be <= getSize()). - // pop() discards the next n bytes. - const void* peek(UInt32 n); - void pop(UInt32 n); - - // accessors - - // return the number of bytes in the buffer - UInt32 getSize() const; - - // IOutputStream overrides - // these all lock the mutex for their duration - virtual void close(); - virtual UInt32 write(const void*, UInt32 count); - virtual void flush(); - -private: - UInt32 getSizeWithLock() const; - -private: - CMutex* m_mutex; - IJob* m_closeCB; - CSocketStreamBuffer m_buffer; - bool m_closed; -}; - -#endif diff --git a/net/CSocketStreamBuffer.cpp b/net/CSocketStreamBuffer.cpp deleted file mode 100644 index 5ee20c28..00000000 --- a/net/CSocketStreamBuffer.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "CSocketStreamBuffer.h" -#include - -// -// CSocketStreamBuffer -// - -const UInt32 CSocketStreamBuffer::kChunkSize = 4096; - -CSocketStreamBuffer::CSocketStreamBuffer() : m_size(0) -{ - // do nothing -} - -CSocketStreamBuffer::~CSocketStreamBuffer() -{ - // do nothing -} - -const void* CSocketStreamBuffer::peek(UInt32 n) -{ - assert(n <= m_size); - - // reserve space in first chunk - ChunkList::iterator head = m_chunks.begin(); - head->reserve(n); - - // consolidate chunks into the first chunk until it has n bytes - ChunkList::iterator scan = head; - ++scan; - while (head->size() < n && scan != m_chunks.end()) { - head->insert(head->end(), scan->begin(), scan->end()); - scan = m_chunks.erase(scan); - } - - return reinterpret_cast(head->begin()); -} - -void CSocketStreamBuffer::pop(UInt32 n) -{ - m_size -= n; - - // discard chunks until more than n bytes would've been discarded - ChunkList::iterator scan = m_chunks.begin(); - while (scan->size() <= n && scan != m_chunks.end()) { - n -= scan->size(); - scan = m_chunks.erase(scan); - } - - // if there's anything left over then remove it from the head chunk. - // if there's no head chunk then we're already empty. - if (scan == m_chunks.end()) { - m_size = 0; - } - else if (n > 0) { - scan->erase(scan->begin(), scan->begin() + n); - } -} - -void CSocketStreamBuffer::write( - const void* vdata, UInt32 n) -{ - assert(vdata != NULL); - - if (n == 0) { - return; - } - m_size += n; - - // cast data to bytes - const UInt8* data = reinterpret_cast(vdata); - - // point to last chunk if it has space, otherwise append an empty chunk - ChunkList::iterator scan = m_chunks.end(); - if (scan != m_chunks.begin()) { - --scan; - if (scan->size() >= kChunkSize) - ++scan; - } - if (scan == m_chunks.end()) { - scan = m_chunks.insert(scan); - } - - // append data in chunks - while (n > 0) { - // choose number of bytes for next chunk - UInt32 count = kChunkSize - scan->size(); - if (count > n) - count = n; - - // transfer data - scan->insert(scan->end(), data, data + count); - n -= count; - data += count; - - // append another empty chunk if we're not done yet - if (n > 0) { - scan = m_chunks.insert(scan); - } - } -} - -UInt32 CSocketStreamBuffer::getSize() const -{ - return m_size; -} diff --git a/net/CSocketStreamBuffer.h b/net/CSocketStreamBuffer.h deleted file mode 100644 index cf6f2344..00000000 --- a/net/CSocketStreamBuffer.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef CSOCKETSTREAMBUFFER_H -#define CSOCKETSTREAMBUFFER_H - -#include "BasicTypes.h" -#include -#include - -class CSocketStreamBuffer { -public: - CSocketStreamBuffer(); - ~CSocketStreamBuffer(); - - // manipulators - - // peek() returns a buffer of n bytes (which must be <= getSize()). - // pop() discards the next n bytes. - const void* peek(UInt32 n); - void pop(UInt32 n); - - // write() appends n bytes to the buffer - void write(const void*, UInt32 n); - - // accessors - - // return the number of bytes in the buffer - UInt32 getSize() const; - -private: - static const UInt32 kChunkSize; - - typedef std::vector Chunk; - typedef std::list ChunkList; - - ChunkList m_chunks; - UInt32 m_size; -}; - -#endif diff --git a/net/net.dsp b/net/net.dsp index 58d0ee13..54d52353 100644 --- a/net/net.dsp +++ b/net/net.dsp @@ -95,18 +95,6 @@ SOURCE=.\CNetworkAddress.cpp # End Source File # Begin Source File -SOURCE=.\CSocketInputStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\CSocketOutputStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\CSocketStreamBuffer.cpp -# End Source File -# Begin Source File - SOURCE=.\CTCPListenSocket.cpp # End Source File # Begin Source File @@ -135,18 +123,6 @@ SOURCE=.\CNetworkAddress.h # End Source File # Begin Source File -SOURCE=.\CSocketInputStream.h -# End Source File -# Begin Source File - -SOURCE=.\CSocketOutputStream.h -# End Source File -# Begin Source File - -SOURCE=.\CSocketStreamBuffer.h -# End Source File -# Begin Source File - SOURCE=.\CTCPListenSocket.h # End Source File # Begin Source File From 59c5e4bf13f7f7418d91df4be18236ee884d2991 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 1 May 2002 14:36:52 +0000 Subject: [PATCH 064/807] Fixed uninitialized variable when computing toggle mask. Also reduced priority of some mouse motion log messages. --- server/CServer.cpp | 4 ++-- server/CXWindowsPrimaryScreen.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/CServer.cpp b/server/CServer.cpp index aaea5f20..2718f36b 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -391,7 +391,7 @@ bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) { - log((CLOG_DEBUG1 "onMouseMoveSecondary %+d,%+d", dx, dy)); + log((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); // mouse move on secondary (client's) screen assert(m_active != NULL); @@ -465,7 +465,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) if (newScreen == NULL || newScreen == m_active) { // do nothing if mouse didn't move if (m_x != xOld || m_y != yOld) { - log((CLOG_DEBUG1 "move on %s to %d,%d", m_active->m_name.c_str(), m_x, m_y)); + log((CLOG_DEBUG2 "move on %s to %d,%d", m_active->m_name.c_str(), m_x, m_y)); m_active->m_protocol->sendMouseMove(m_x, m_y); } } diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index bff3596b..35b7014e 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -334,7 +334,7 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( // warp the mouse XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); XSync(display, False); - log((CLOG_DEBUG1 "warped to %d,%d", x, y)); + log((CLOG_DEBUG2 "warped to %d,%d", x, y)); // discard mouse events since we just added one we don't want XEvent xevent; @@ -386,7 +386,7 @@ KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const return 0; // convert to KeyModifierMask - KeyModifierMask mask; + KeyModifierMask mask = 0; if (state & m_numLockMask) mask |= KeyModifierNumLock; if (state & m_capsLockMask) From f2e83e607dfedcc1bfba6f498d1e0068e3cb966f Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 1 May 2002 15:31:47 +0000 Subject: [PATCH 065/807] checkpoint. turned off nagle and send buffering. also added test to skip clipboard conversion if a previous conversion from that owner failed. --- Make-linux | 2 +- net/CNetwork.h | 8 ++++++++ net/CTCPSocket.cpp | 10 ++++++++++ synergy/CXWindowsScreen.cpp | 30 +++++++++++++++++++++++++++++- synergy/CXWindowsScreen.h | 6 ++++++ 5 files changed, 54 insertions(+), 2 deletions(-) diff --git a/Make-linux b/Make-linux index 98572f46..8300b12d 100644 --- a/Make-linux +++ b/Make-linux @@ -13,7 +13,7 @@ RMR = /bin/rm -rf # # compiler options # -GCXXDEFS = -D_XOPEN_SOURCE=600 +GCXXDEFS = -D_XOPEN_SOURCE=600 -D_SVID_SOURCE GCXXINCS = -I$(DEPTH)/include -I/usr/X11R6/include GCXXOPTS = -Wall -W -fexceptions CXXOPTIMIZER = -g diff --git a/net/CNetwork.h b/net/CNetwork.h index fcc65e40..8d6e9842 100644 --- a/net/CNetwork.h +++ b/net/CNetwork.h @@ -9,6 +9,9 @@ # define INCL_WINSOCK_API_TYPEDEFS 0 # include typedef int ssize_t; +# if !defined(SOL_TCP) +# define SOL_TCP IPPROTO_TCP +# endif #else # define FAR # define PASCAL @@ -21,6 +24,9 @@ typedef int ssize_t; # include # include # include +# if !defined(TCP_NODELAY) || !defined(SOL_TCP) +# include +# endif #endif // FIXME -- must handle htonl and ilk when defined as macros @@ -31,6 +37,7 @@ public: typedef SOCKET Socket; typedef struct sockaddr Address; typedef int AddressLength; + typedef BOOL TCPNoDelayType; struct PollEntry { Socket fd; short events; @@ -47,6 +54,7 @@ public: typedef struct sockaddr Address; typedef socklen_t AddressLength; typedef struct pollfd PollEntry; + typedef int TCPNoDelayType; enum { kPOLLIN = POLLIN, kPOLLOUT = POLLOUT, diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index ce32c846..f5f4f3a5 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -128,6 +128,16 @@ void CTCPSocket::init() m_output = new CBufferedOutputStream(m_mutex, new TMethodJob( this, &CTCPSocket::closeOutput)); + + // turn off Nagle algorithm. we send lots of very short messages + // that should be sent without (much) delay. for example, the + // mouse motion messages are much less useful if they're delayed. + CNetwork::TCPNoDelayType flag = 1; + CNetwork::setsockopt(m_fd, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); + + // don't buffer sends, we merge messages ourself + int data = 0; + CNetwork::setsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &data, sizeof(data)); } void CTCPSocket::ioThread(void*) diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index b3dd2bf2..58fb5d0a 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -261,6 +261,23 @@ void CXWindowsScreen::getDisplayClipboard( CLock lock(&m_mutex); Atom selection = m_atomClipboard[id]; + // if we're trying to request the clipboard from the same + // unresponsive owner then immediately give up. this is lame + // because we can't be sure the owner won't become responsive; + // we can only ask the X server for the current owner, not the + // owner at time timestamp, allowing a race condition; and we + // don't detect if the owner window is destroyed in order to + // reset the unresponsive flag. + Window owner = XGetSelectionOwner(m_display, selection); + if (m_clipboards[id].m_unresponsive) { + if (owner != None && owner == m_clipboards[id].m_owner) { + clipboard->close(); + return; + } + } + CClipboardInfo& clipboardInfo = + const_cast(m_clipboards[id]); + // ask the selection for all the formats it has. some owners return // the TARGETS atom and some the ATOM atom when TARGETS is requested. Atom format; @@ -268,6 +285,10 @@ void CXWindowsScreen::getDisplayClipboard( if (getDisplayClipboard(selection, m_atomTargets, requestor, timestamp, &format, &targets) && (format == m_atomTargets || format == XA_ATOM)) { + // save owner info + clipboardInfo.m_owner = owner; + clipboardInfo.m_unresponsive = false; + // get each target (that we can interpret). some owners return // some targets multiple times in the list so don't try to get // those multiple times. @@ -330,6 +351,11 @@ void CXWindowsScreen::getDisplayClipboard( else { // non-ICCCM conforming selection owner. try TEXT format. // FIXME + + // save owner info + clipboardInfo.m_owner = owner; + clipboardInfo.m_unresponsive = true; + log((CLOG_DEBUG1 "selection doesn't support TARGETS, format is %d", format)); } @@ -1014,7 +1040,9 @@ Time CXWindowsScreen::getCurrentTimeNoLock( CXWindowsScreen::CClipboardInfo::CClipboardInfo() : m_clipboard(), m_lostClipboard(CurrentTime), - m_requests() + m_requests(), + m_owner(None), + m_unresponsive(false) { // do nothing } diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 66df2968..0f81414a 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -161,6 +161,12 @@ private: // the request queues CRequestMap m_requests; + + // owner of clipboard when we last asked for it + Window m_owner; + + // true iff the previous request to m_owner got no reply + bool m_unresponsive; }; Display* m_display; From f052d126c0e7d5639160b765d007c5e52b204365 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 1 May 2002 16:17:57 +0000 Subject: [PATCH 066/807] Added more checks to avoid sending unchanged clipboard data. Still takes too long to query the clipboard owner for info (maybe 1/10th second) but not sure why or if that can be improved. --- server/CServer.cpp | 18 +++++++++++++++--- synergy/CXWindowsScreen.cpp | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/server/CServer.cpp b/server/CServer.cpp index 2718f36b..cde6191b 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -956,9 +956,21 @@ void CServer::updatePrimaryClipboard(ClipboardID id) // if clipboard changed then other screens have an // out-of-date clipboard. if (time != clipboard.m_clipboard.getTime()) { - log((CLOG_DEBUG "clipboard %d changed", id)); - clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); - clearGotClipboard(id); + log((CLOG_DEBUG "clipboard %d time changed (%08x to %08x)", id, time, clipboard.m_clipboard.getTime())); + + // marshall data + CString newData = clipboard.m_clipboard.marshall(); + + // compare old and new data. if identical then the clipboard + // hasn't really changed. + if (newData != clipboard.m_clipboardData) { + log((CLOG_DEBUG "clipboard %d changed", id)); + clipboard.m_clipboardData = newData; + clearGotClipboard(id); + } + else { + log((CLOG_DEBUG "clipboard %d unchanged", id)); + } m_primaryInfo->m_gotClipboard[id] = true; } } diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 58fb5d0a..0aee857f 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -249,12 +249,6 @@ void CXWindowsScreen::getDisplayClipboard( assert(clipboard != NULL); assert(requestor != None); - // FIXME -- don't update clipboard object if clipboard hasn't changed - - // clear the clipboard object - if (!clipboard->open(timestamp)) - return; - // block others from using the display while we get the clipboard. // in particular, this prevents the event thread from stealing the // selection notify event we're expecting. @@ -271,16 +265,43 @@ void CXWindowsScreen::getDisplayClipboard( Window owner = XGetSelectionOwner(m_display, selection); if (m_clipboards[id].m_unresponsive) { if (owner != None && owner == m_clipboards[id].m_owner) { - clipboard->close(); + log((CLOG_DEBUG1 "skip unresponsive clipboard owner")); + // clear the clipboard and return + if (!clipboard->open(timestamp)) { + clipboard->close(); + } return; } } CClipboardInfo& clipboardInfo = const_cast(m_clipboards[id]); + // don't update clipboard object if clipboard hasn't changed. ask + // the selection for the tiemstamp when it acquired the selection. + Atom format; + CString data; + if (getDisplayClipboard(selection, m_atomTimestamp, + requestor, timestamp, &format, &data) && + format == m_atomInteger) { + // get the owner's time + Time time = *reinterpret_cast(data.data()); + log((CLOG_DEBUG "got clipboard timestamp %08x", time)); + + // if unchanged then clipboard hasn't changed + if (time == clipboard->getTime()) + return; + + // use clipboard owner's time as timestamp + timestamp = time; + } + + // clear the clipboard object + if (!clipboard->open(timestamp)) { + return; + } + // ask the selection for all the formats it has. some owners return // the TARGETS atom and some the ATOM atom when TARGETS is requested. - Atom format; CString targets; if (getDisplayClipboard(selection, m_atomTargets, requestor, timestamp, &format, &targets) && From eb2a202834613474eab0da003257b62053059d1c Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 1 May 2002 16:30:20 +0000 Subject: [PATCH 067/807] Was trying to avoid sending clipboard if timestamp wasn't changed but clipboard owners may not update that timestamp when the selection is changed. Disabled the timestamp check. --- synergy/CXWindowsScreen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 0aee857f..377a3da7 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -279,6 +279,7 @@ void CXWindowsScreen::getDisplayClipboard( // don't update clipboard object if clipboard hasn't changed. ask // the selection for the tiemstamp when it acquired the selection. Atom format; +/* XXX -- timestamp not always updated when clipboard is changed CString data; if (getDisplayClipboard(selection, m_atomTimestamp, requestor, timestamp, &format, &data) && @@ -294,6 +295,7 @@ void CXWindowsScreen::getDisplayClipboard( // use clipboard owner's time as timestamp timestamp = time; } +*/ // clear the clipboard object if (!clipboard->open(timestamp)) { From 76269a48c1eb5c3ade01f87e06d1595b409f4a5f Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 2 May 2002 11:33:34 +0000 Subject: [PATCH 068/807] checkpoint debugging of stream buffer. --- io/CStreamBuffer.cpp | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/io/CStreamBuffer.cpp b/io/CStreamBuffer.cpp index e9d1b5a9..2f78e6dc 100644 --- a/io/CStreamBuffer.cpp +++ b/io/CStreamBuffer.cpp @@ -38,64 +38,84 @@ const void* CStreamBuffer::peek(UInt32 n) void CStreamBuffer::pop(UInt32 n) { + // discard all chunks if n is greater than or equal to m_size + if (n >= m_size) { + m_size = 0; + m_chunks.clear(); + return; + } + + // update size m_size -= n; // discard chunks until more than n bytes would've been discarded ChunkList::iterator scan = m_chunks.begin(); - while (scan->size() <= n && scan != m_chunks.end()) { - n -= scan->size(); + assert(scan != m_chunks.end()); + while (scan->size() <= n) { + n -= scan->size(); scan = m_chunks.erase(scan); + assert(scan != m_chunks.end()); } + assert(n > 0); - // if there's anything left over then remove it from the head chunk. - // if there's no head chunk then we're already empty. - if (scan == m_chunks.end()) { - m_size = 0; - } - else if (n > 0) { - scan->erase(scan->begin(), scan->begin() + n); - } + // remove left over bytes from the head chunk + scan->erase(scan->begin(), scan->begin() + n); } +#include "CLog.h" void CStreamBuffer::write( const void* vdata, UInt32 n) { assert(vdata != NULL); + // ignore if no data, otherwise update size if (n == 0) { return; } +log((CLOG_DEBUG "### %p buffering %d bytes onto %d bytes", this, n, m_size)); m_size += n; // cast data to bytes const UInt8* data = reinterpret_cast(vdata); +if (n > 1000) +log((CLOG_DEBUG "### %p src: %u %u %u %u %u %u %u %u", this, +data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7])); // point to last chunk if it has space, otherwise append an empty chunk ChunkList::iterator scan = m_chunks.end(); if (scan != m_chunks.begin()) { +log((CLOG_DEBUG "### %p at least one chunk", this)); --scan; if (scan->size() >= kChunkSize) +{ +log((CLOG_DEBUG "### %p last chunk full", this)); ++scan; +} } if (scan == m_chunks.end()) { - scan = m_chunks.insert(scan); +log((CLOG_DEBUG "### %p append chunk", this)); + scan = m_chunks.insert(scan, Chunk()); } // append data in chunks while (n > 0) { // choose number of bytes for next chunk + assert(scan->size() <= kChunkSize); UInt32 count = kChunkSize - scan->size(); if (count > n) count = n; // transfer data +log((CLOG_DEBUG "### %p append %d bytes onto last chunk", this, count)); scan->insert(scan->end(), data, data + count); n -= count; data += count; // append another empty chunk if we're not done yet if (n > 0) { - scan = m_chunks.insert(scan); + ++scan; + scan = m_chunks.insert(scan, Chunk()); +log((CLOG_DEBUG "### %p append chunk2", this)); } } } From 5132161e30d44295ddfd66803a4f8134fa718610 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 2 May 2002 11:43:52 +0000 Subject: [PATCH 069/807] Fixed bug in stream buffer that could cause data to be inserted out of order. Also removed unnecessary limit on writes to the TCP socket. --- io/CStreamBuffer.cpp | 18 +++--------------- net/CTCPSocket.cpp | 4 ---- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/io/CStreamBuffer.cpp b/io/CStreamBuffer.cpp index 2f78e6dc..d468847d 100644 --- a/io/CStreamBuffer.cpp +++ b/io/CStreamBuffer.cpp @@ -56,13 +56,13 @@ void CStreamBuffer::pop(UInt32 n) scan = m_chunks.erase(scan); assert(scan != m_chunks.end()); } - assert(n > 0); // remove left over bytes from the head chunk - scan->erase(scan->begin(), scan->begin() + n); + if (n > 0) { + scan->erase(scan->begin(), scan->begin() + n); + } } -#include "CLog.h" void CStreamBuffer::write( const void* vdata, UInt32 n) { @@ -72,28 +72,19 @@ void CStreamBuffer::write( if (n == 0) { return; } -log((CLOG_DEBUG "### %p buffering %d bytes onto %d bytes", this, n, m_size)); m_size += n; // cast data to bytes const UInt8* data = reinterpret_cast(vdata); -if (n > 1000) -log((CLOG_DEBUG "### %p src: %u %u %u %u %u %u %u %u", this, -data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7])); // point to last chunk if it has space, otherwise append an empty chunk ChunkList::iterator scan = m_chunks.end(); if (scan != m_chunks.begin()) { -log((CLOG_DEBUG "### %p at least one chunk", this)); --scan; if (scan->size() >= kChunkSize) -{ -log((CLOG_DEBUG "### %p last chunk full", this)); ++scan; -} } if (scan == m_chunks.end()) { -log((CLOG_DEBUG "### %p append chunk", this)); scan = m_chunks.insert(scan, Chunk()); } @@ -106,7 +97,6 @@ log((CLOG_DEBUG "### %p append chunk", this)); count = n; // transfer data -log((CLOG_DEBUG "### %p append %d bytes onto last chunk", this, count)); scan->insert(scan->end(), data, data + count); n -= count; data += count; @@ -115,7 +105,6 @@ log((CLOG_DEBUG "### %p append %d bytes onto last chunk", this, count)); if (n > 0) { ++scan; scan = m_chunks.insert(scan, Chunk()); -log((CLOG_DEBUG "### %p append chunk2", this)); } } } @@ -124,4 +113,3 @@ UInt32 CStreamBuffer::getSize() const { return m_size; } - diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index f5f4f3a5..dc631862 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -211,10 +211,6 @@ void CTCPSocket::ioService() // get amount of data to write UInt32 n = m_output->getSize(); - if (n > 4096) { - // limit write size - n = 4096; - } // write data const void* buffer = m_output->peek(n); From 570d85c842789e4e6e092667f0651806269fc558 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 2 May 2002 11:44:21 +0000 Subject: [PATCH 070/807] Indentation change. --- synergy/COutputPacketStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synergy/COutputPacketStream.cpp b/synergy/COutputPacketStream.cpp index 1895b782..e9ffc68f 100644 --- a/synergy/COutputPacketStream.cpp +++ b/synergy/COutputPacketStream.cpp @@ -29,7 +29,7 @@ UInt32 COutputPacketStream::write( length[1] = (UInt8)((count >> 16) & 0xff); length[2] = (UInt8)((count >> 8) & 0xff); length[3] = (UInt8)( count & 0xff); - UInt32 count2 = sizeof(length); + UInt32 count2 = sizeof(length); const UInt8* cbuffer = length; while (count2 > 0) { UInt32 n = getStream()->write(cbuffer, count2); From 5641a875c189f6d3344eeb4de83536e946edcd7a Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 3 May 2002 11:26:44 +0000 Subject: [PATCH 071/807] checkpoint. made changes to support key autorepeats on X. --- client/CXWindowsSecondaryScreen.cpp | 189 ++++++++++++++++++++-------- client/CXWindowsSecondaryScreen.h | 19 ++- server/CXWindowsPrimaryScreen.cpp | 46 ++++++- server/CXWindowsPrimaryScreen.h | 9 ++ 4 files changed, 197 insertions(+), 66 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 8bde1c78..eda25015 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -221,30 +221,33 @@ void CXWindowsSecondaryScreen::keyDown( Keystrokes keys; KeyCode keycode; - CDisplayLock display(this); - // get the sequence of keys to simulate key press and the final // modifier state. - m_mask = mapKey(keys, keycode, key, mask, True); + m_mask = mapKey(keys, keycode, key, mask, kPress); if (keys.empty()) return; // generate key events - for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) - XTestFakeKeyEvent(display, k->first, k->second, CurrentTime); + doKeystrokes(keys, 1); // note that key is now down m_keys[keycode] = true; - - // update - XSync(display, False); } void CXWindowsSecondaryScreen::keyRepeat( - KeyID, KeyModifierMask, SInt32) + KeyID key, KeyModifierMask mask, SInt32 count) { - CDisplayLock display(this); - // FIXME + Keystrokes keys; + KeyCode keycode; + + // get the sequence of keys to simulate key repeat and the final + // modifier state. + m_mask = mapKey(keys, keycode, key, mask, kRepeat); + if (keys.empty()) + return; + + // generate key events + doKeystrokes(keys, count); } void CXWindowsSecondaryScreen::keyUp( @@ -253,23 +256,17 @@ void CXWindowsSecondaryScreen::keyUp( Keystrokes keys; KeyCode keycode; - CDisplayLock display(this); - // get the sequence of keys to simulate key release and the final // modifier state. - m_mask = mapKey(keys, keycode, key, mask, False); + m_mask = mapKey(keys, keycode, key, mask, kRelease); if (keys.empty()) return; // generate key events - for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) - XTestFakeKeyEvent(display, k->first, k->second, CurrentTime); + doKeystrokes(keys, 1); // note that key is now up m_keys[keycode] = false; - - // update - XSync(display, False); } void CXWindowsSecondaryScreen::mouseDown(ButtonID button) @@ -408,7 +405,7 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( Keystrokes& keys, KeyCode& keycode, KeyID id, KeyModifierMask mask, - Bool press) const + EKeyAction action) const { // note -- must have display locked on entry @@ -425,8 +422,8 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // note if the key is the caps lock and it's "half-duplex" const bool isHalfDuplex = (id == XK_Caps_Lock && m_capsLockHalfDuplex); - // ignore releases for half-duplex keys - if (isHalfDuplex && !press) { + // ignore releases and repeats for half-duplex keys + if (isHalfDuplex && action != kPress) { return m_mask; } @@ -457,6 +454,7 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // a modifier key then skip this because modifiers should not // modify modifiers. Keystrokes undo; + Keystroke keystroke; if (outMask != m_mask && !isModifier) { for (unsigned int i = 0; i < 8; ++i) { unsigned int bit = (1 << i); @@ -472,20 +470,26 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a // press. use the first keycode for the modifier. - const KeyCode modifierKey = modifierKeys[0]; - keys.push_back(std::make_pair(modifierKey, True)); + keystroke.m_keycode = modifierKeys[0]; + keystroke.m_press = True; + keystroke.m_repeat = False; + keys.push_back(keystroke); if ((bit & m_toggleModifierMask) != 0) { if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { - keys.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, True)); + keystroke.m_press = False; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); } else { - undo.push_back(std::make_pair(modifierKey, False)); + keystroke.m_press = False; + undo.push_back(keystroke); } } else { - undo.push_back(std::make_pair(modifierKey, False)); + keystroke.m_press = False; + undo.push_back(keystroke); } } @@ -496,24 +500,34 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // release. we must check each keycode for the // modifier if not a toggle. if ((bit & m_toggleModifierMask) != 0) { - const KeyCode modifierKey = modifierKeys[0]; + keystroke.m_keycode = modifierKeys[0]; + keystroke.m_repeat = False; if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { - keys.push_back(std::make_pair(modifierKey, True)); - keys.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, True)); + keystroke.m_press = True; + keys.push_back(keystroke); + keystroke.m_press = False; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); } else { - keys.push_back(std::make_pair(modifierKey, False)); - undo.push_back(std::make_pair(modifierKey, True)); + keystroke.m_press = False; + keys.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); } } else { for (unsigned int j = 0; j < m_keysPerModifier; ++j) { const KeyCode key = modifierKeys[j]; if (m_keys[key]) { - keys.push_back(std::make_pair(key, False)); - undo.push_back(std::make_pair(key, True)); + keystroke.m_keycode = key; + keystroke.m_press = False; + keystroke.m_repeat = False; + keys.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); } } } @@ -524,11 +538,32 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // note if the press of a half-duplex key should be treated as a release if (isHalfDuplex && (m_mask & (1 << index->second)) != 0) { - press = false; + action = kRelease; } // add the key event - keys.push_back(std::make_pair(keycode, press)); + keystroke.m_keycode = keycode; + switch (action) { + case kPress: + keystroke.m_press = True; + keystroke.m_repeat = False; + keys.push_back(keystroke); + break; + + case kRelease: + keystroke.m_press = False; + keystroke.m_repeat = False; + keys.push_back(keystroke); + break; + + case kRepeat: + keystroke.m_press = False; + keystroke.m_repeat = True; + keys.push_back(keystroke); + keystroke.m_press = True; + keys.push_back(keystroke); + break; + } // add key events to restore the modifier state. apply events in // the reverse order that they're stored in undo. @@ -538,9 +573,9 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( } // if the key is a modifier key then compute the modifier map after - // this key is pressed. + // this key is pressed or released. if repeating then ignore. mask = m_mask; - if (isModifier) { + if (isModifier && action != kRepeat) { // get modifier const unsigned int modifierBit = (1 << index->second); @@ -549,10 +584,10 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // and clear the bit on release. if half-duplex then toggle // each time we get here. if ((modifierBit & m_toggleModifierMask) != 0) { - if (((mask & modifierBit) == 0) == press) + if (((mask & modifierBit) == 0) == (action == kPress)) mask ^= modifierBit; } - else if (press) { + else if (action == kPress) { mask |= modifierBit; } else { @@ -640,7 +675,7 @@ bool CXWindowsSecondaryScreen::findKeyCode( } // save the keycode - keycode = index->second.keycode; + keycode = index->second.m_keycode; // compute output mask. that's the set of modifiers that need to // be enabled when the keycode event is encountered in order to @@ -649,22 +684,68 @@ bool CXWindowsSecondaryScreen::findKeyCode( // it impossible to generate the keysym. in that case we must // override maskIn. this is complicated by caps/shift-lock and // num-lock. - maskOut = (maskIn & ~index->second.keyMaskMask); + maskOut = (maskIn & ~index->second.m_keyMaskMask); if (IsKeypadKey(id) || IsPrivateKeypadKey(id)) { - maskOut |= index->second.keyMask; + maskOut |= index->second.m_keyMask; maskOut &= ~m_numLockMask; } else { - unsigned int maskShift = (index->second.keyMask & ShiftMask); - if (index->second.keyMaskMask != 0 && (m_mask & m_capsLockMask) != 0) + unsigned int maskShift = (index->second.m_keyMask & ShiftMask); + if (index->second.m_keyMaskMask != 0 && (m_mask & m_capsLockMask) != 0) maskShift ^= ShiftMask; maskOut |= maskShift | (m_mask & m_capsLockMask); - maskOut |= (index->second.keyMask & ~(ShiftMask | LockMask)); + maskOut |= (index->second.m_keyMask & ~(ShiftMask | LockMask)); } return true; } +void CXWindowsSecondaryScreen::doKeystrokes( + const Keystrokes& keys, SInt32 count) +{ + // do nothing if no keys or no repeats + if (count < 1 || keys.empty()) + return; + + // lock display + CDisplayLock display(this); + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { + if (k->m_repeat) { + // repeat from here up to but not including the next key + // with m_repeat == false count times. + Keystrokes::const_iterator start = k; + for (; count > 0; --count) { + // we generally want repeating keys to use the exact + // same event time for each release/press pair so we + // don't want to use CurrentTime which can't ensure + // that. + Time time = getCurrentTime(m_window); + + // send repeating events + for (k = start; k != keys.end() && k->m_repeat; ++k) { + XTestFakeKeyEvent(display, + k->m_keycode, k->m_press, time); + } + } + + // note -- k is now on the first non-repeat key after the + // repeat keys, exactly where we'd like to continue from. + } + else { + // send event + XTestFakeKeyEvent(display, k->m_keycode, k->m_press, CurrentTime); + + // next key + ++k; + } + } + + // update + XSync(display, False); +} + unsigned int CXWindowsSecondaryScreen::maskToX( KeyModifierMask inMask) const { @@ -764,12 +845,12 @@ void CXWindowsSecondaryScreen::updateKeycodeMap( } // set the mask of modifiers that this keycode uses - entry.keyMaskMask = (n == 1) ? 0 : (ShiftMask | LockMask); + entry.m_keyMaskMask = (n == 1) ? 0 : (ShiftMask | LockMask); // add entries for this keycode - entry.keycode = static_cast(minKeycode + i); + entry.m_keycode = static_cast(minKeycode + i); for (int j = 0; j < numKeysyms; ++j) { - entry.keyMask = (j == 0) ? 0 : ShiftMask; + entry.m_keyMask = (j == 0) ? 0 : ShiftMask; m_keycodeMap.insert(std::make_pair(keysyms[i * keysymsPerKeycode + j], entry)); } @@ -841,7 +922,7 @@ void CXWindowsSecondaryScreen::toggleKey( KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym); if (index == m_keycodeMap.end()) return; - KeyCode keycode = index->second.keycode; + KeyCode keycode = index->second.m_keycode; // toggle the key if (keysym == XK_Caps_Lock && m_capsLockHalfDuplex) { diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 15388442..2a79266b 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -38,13 +38,19 @@ protected: virtual long getEventMask(Window) const; private: - struct KeyCodeMask { + enum EKeyAction { kPress, kRelease, kRepeat }; + class KeyCodeMask { public: - KeyCode keycode; - unsigned int keyMask; - unsigned int keyMaskMask; + KeyCode m_keycode; + unsigned int m_keyMask; + unsigned int m_keyMaskMask; + }; + class Keystroke { + public: + KeyCode m_keycode; + Bool m_press; + bool m_repeat; }; - typedef std::pair Keystroke; typedef std::vector Keystrokes; typedef std::vector KeyCodes; typedef std::map KeyCodeMap; @@ -54,9 +60,10 @@ private: unsigned int mapButton(ButtonID button) const; unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, - KeyModifierMask, Bool press) const; + KeyModifierMask, EKeyAction) const; bool findKeyCode(KeyCode&, unsigned int&, KeyID id, unsigned int) const; + void doKeystrokes(const Keystrokes&, SInt32 count); unsigned int maskToX(KeyModifierMask) const; void updateKeys(Display* display); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 35b7014e..aacb32b6 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -63,16 +63,40 @@ void CXWindowsPrimaryScreen::run() break; } - // FIXME -- simulate key repeat. X sends press/release for - // repeat. must detect auto repeat and use kKeyRepeat. case KeyRelease: { - log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { - if (key == XK_Caps_Lock && m_capsLockHalfDuplex) - m_server->onKeyDown(key, mask); - m_server->onKeyUp(key, mask); + // check if this is a key repeat by getting the next + // KeyPress event that has the same key and time as + // this release event, if any. first prepare the + // filter info. + CKeyEventInfo filter; + filter.m_event = KeyPress; + filter.m_window = xevent.xkey.window; + filter.m_time = xevent.xkey.time; + filter.m_keycode = xevent.xkey.keycode; + + // now check for event + XEvent xevent2; + CDisplayLock display(this); + if (XCheckIfEvent(display, &xevent2, + &CXWindowsPrimaryScreen::findKeyEvent, + (XPointer)&filter) != True) { + // no press event follows so it's a plain release + log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + if (key == XK_Caps_Lock && m_capsLockHalfDuplex) + m_server->onKeyDown(key, mask); + m_server->onKeyUp(key, mask); + } + else { + // found a press event following so it's a repeat. + // we could attempt to count the already queued + // repeats but we'll just send a repeat of 1. + // note that we discard the press event. + log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + m_server->onKeyRepeat(key, mask, 1); + } } break; } @@ -550,3 +574,13 @@ void CXWindowsPrimaryScreen::updateModifierMap( XFreeModifiermap(keymap); } + +Bool CXWindowsPrimaryScreen::findKeyEvent( + Display*, XEvent* xevent, XPointer arg) +{ + CKeyEventInfo* filter = reinterpret_cast(arg); + return (xevent->type == filter->m_event && + xevent->xkey.window == filter->m_window && + xevent->xkey.time == filter->m_time && + xevent->xkey.keycode == filter->m_keycode) ? True : False; +} diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 5df90544..8c521bed 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -43,6 +43,15 @@ private: void updateModifierMap(Display* display); + class CKeyEventInfo { + public: + int m_event; + Window m_window; + Time m_time; + KeyCode m_keycode; + }; + static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); + private: CServer* m_server; bool m_active; From ab62dec0f703c9d448ccd5190361fce8e36d8f15 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 3 May 2002 11:49:30 +0000 Subject: [PATCH 072/807] removed attempt to make release/press of a repeating key use the same server time. was getting what appears to be deadlock but not sure why. --- client/CXWindowsSecondaryScreen.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index eda25015..c0cdffc1 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -717,16 +717,10 @@ void CXWindowsSecondaryScreen::doKeystrokes( // with m_repeat == false count times. Keystrokes::const_iterator start = k; for (; count > 0; --count) { - // we generally want repeating keys to use the exact - // same event time for each release/press pair so we - // don't want to use CurrentTime which can't ensure - // that. - Time time = getCurrentTime(m_window); - // send repeating events for (k = start; k != keys.end() && k->m_repeat; ++k) { XTestFakeKeyEvent(display, - k->m_keycode, k->m_press, time); + k->m_keycode, k->m_press, CurrentTime); } } From afa14b67c204f3757e8d9a29dc863e865d3d1181 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 3 May 2002 12:14:55 +0000 Subject: [PATCH 073/807] fixed handling of ISO_Left_Tab when that is not mapped to a keycode by mapping it to tab with shift pressed. --- client/CXWindowsSecondaryScreen.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index c0cdffc1..668f4635 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -6,6 +6,7 @@ #include #include #define XK_MISCELLANY +#define XK_XKB_KEYS #include #include @@ -664,6 +665,10 @@ bool CXWindowsSecondaryScreen::findKeyCode( id = XK_Delete; break; + case XK_ISO_Left_Tab: + id = XK_Tab; + break; + default: return false; } From b19fdd86cff846bae61b94f8c7a5f21910c4c452 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 3 May 2002 12:23:48 +0000 Subject: [PATCH 074/807] fixed handling of shift+tab on a system that can map ISO_Left_Tab. now tries to map ISO_Left_Tab without shift first then falls back to Tab (note that if ISO_Left_Tab can be mapped but requires a modifier then the modifier will be added). also changed attempt to map ISO_Left_Tab as a backup to Tab to request the shift modifier whether or not the primary screen requested it. --- client/CXWindowsSecondaryScreen.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 668f4635..54b915b2 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -615,6 +615,15 @@ bool CXWindowsSecondaryScreen::findKeyCode( KeyID id, unsigned int maskIn) const { + // if XK_Tab is requested with shift active then try XK_ISO_Left_Tab + // instead. if that doesn't work, we'll fall back to XK_Tab with + // shift active. this is to handle primary screens that don't map + // XK_ISO_Left_Tab sending events to secondary screens that do. + if (id == XK_Tab && (maskIn & ShiftMask) != 0) { + id = XK_ISO_Left_Tab; + maskIn &= ~ShiftMask; + } + // find a keycode to generate id. XKeysymToKeycode() almost does // what we need but won't tell us which index to use with the // keycode. return false if there's no keycode to generate id. @@ -666,7 +675,8 @@ bool CXWindowsSecondaryScreen::findKeyCode( break; case XK_ISO_Left_Tab: - id = XK_Tab; + id = XK_Tab; + maskIn |= ShiftMask; break; default: From f3c70dc30002b8356cf7299c83fc45330935746c Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 4 May 2002 11:23:11 +0000 Subject: [PATCH 075/807] fixed handling of shift + caps-lock. those two modifiers should cancel out if the keysym is subject to case conversion, but not otherwise. also added logging of key lookup code. --- client/CXWindowsSecondaryScreen.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 54b915b2..3c16cbe9 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -434,14 +434,15 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( if (!findKeyCode(keycode, outMask, id, maskToX(mask))) { // we cannot generate the desired keysym because no key // maps to that keysym. just return the current mask. - log((CLOG_DEBUG2 "no keycode for keysym %d modifiers %04x", id, mask)); + log((CLOG_DEBUG2 "no keycode for keysym %d modifiers 0x%04x", id, mask)); return m_mask; } + log((CLOG_DEBUG2 "keysym %d -> keycode %d modifiers 0x%04x", id, keycode, outMask)); // if we cannot match the modifier mask then don't return any // keys and just return the current mask. if ((outMask & m_modifierMask) != outMask) { - log((CLOG_DEBUG2 "cannot match modifiers %04x", outMask)); + log((CLOG_DEBUG2 "cannot match modifiers to mask 0x%04x", m_modifierMask)); return m_mask; } @@ -471,11 +472,13 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a // press. use the first keycode for the modifier. + log((CLOG_DEBUG2 "modifier 0x%04x is not active", bit)); keystroke.m_keycode = modifierKeys[0]; keystroke.m_press = True; keystroke.m_repeat = False; keys.push_back(keystroke); if ((bit & m_toggleModifierMask) != 0) { + log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { keystroke.m_press = False; keys.push_back(keystroke); @@ -500,7 +503,9 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // press/release, otherwise deactivate it with a // release. we must check each keycode for the // modifier if not a toggle. + log((CLOG_DEBUG2 "modifier 0x%04x is active", bit)); if ((bit & m_toggleModifierMask) != 0) { + log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); keystroke.m_keycode = modifierKeys[0]; keystroke.m_repeat = False; if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { @@ -700,16 +705,31 @@ bool CXWindowsSecondaryScreen::findKeyCode( // override maskIn. this is complicated by caps/shift-lock and // num-lock. maskOut = (maskIn & ~index->second.m_keyMaskMask); + log((CLOG_DEBUG2 "maskIn(0x%04x) & ~maskMask(0x%04x) -> 0x%04x", maskIn, index->second.m_keyMaskMask, maskOut)); if (IsKeypadKey(id) || IsPrivateKeypadKey(id)) { maskOut |= index->second.m_keyMask; maskOut &= ~m_numLockMask; + log((CLOG_DEBUG2 "keypad key: | mask(0x%04x) & ~numLockMask(0x%04x) -> 0x%04x", index->second.m_keyMask, m_numLockMask, maskOut)); } else { unsigned int maskShift = (index->second.m_keyMask & ShiftMask); - if (index->second.m_keyMaskMask != 0 && (m_mask & m_capsLockMask) != 0) - maskShift ^= ShiftMask; + log((CLOG_DEBUG2 "maskShift = 0x%04x", maskShift)); + if (index->second.m_keyMaskMask != 0 && + (m_mask & m_capsLockMask) != 0) { + // shift and capsLock cancel out for keysyms subject to + // case conversion but not for keys with shifted + // characters that are not case conversions. see if + // case conversion is necessary. + KeySym lKey, uKey; + XConvertCase(id, &lKey, &uKey); + if (lKey != uKey) { + maskShift ^= ShiftMask; + log((CLOG_DEBUG2 "maskMask != 0 && capsLock on -> toggle maskShift 0x%04x", maskShift)); + } + } maskOut |= maskShift | (m_mask & m_capsLockMask); maskOut |= (index->second.m_keyMask & ~(ShiftMask | LockMask)); + log((CLOG_DEBUG2 "| maskShift(0x%04x) | old caps lock(0x%04x) | other (0x%04x) -> 0x%04x", maskShift, (m_mask & m_capsLockMask), (index->second.m_keyMask & ~(ShiftMask | LockMask)), maskOut)); } return true; From 4d11079095ad7467b62eead092743bc7ddbec601 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 4 May 2002 18:08:22 +0000 Subject: [PATCH 076/807] Fixes for win32 key handling. --- client/CMSWindowsSecondaryScreen.cpp | 241 +++++++++++++++++++-------- client/CMSWindowsSecondaryScreen.h | 13 +- net/CTCPSocket.cpp | 2 + server/CMSWindowsPrimaryScreen.cpp | 2 +- 4 files changed, 187 insertions(+), 71 deletions(-) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 6d2fd886..973bb004 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -5,6 +5,7 @@ #include "CThread.h" #include "CLog.h" #include +#include // // CMSWindowsSecondaryScreen @@ -40,7 +41,7 @@ static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lPar case WM_APP: if (!s_logMore.empty()) { - if (s_log.size() > 20000) + if (s_log.size() > 40000) s_log = s_logMore; else s_log += s_logMore; @@ -189,15 +190,12 @@ void CMSWindowsSecondaryScreen::keyDown( // get the sequence of keys to simulate key press and the final // modifier state. - m_mask = mapKey(keys, virtualKey, key, mask, true); + m_mask = mapKey(keys, virtualKey, key, mask, kPress); if (keys.empty()) return; // generate key events - for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) { - const UINT code = MapVirtualKey(k->first, 0); - keybd_event(k->first, code, k->second ? 0 : KEYEVENTF_KEYUP, 0); - } + doKeystrokes(keys, 1); // note that key is now down m_keys[virtualKey] |= 0x80; @@ -209,22 +207,14 @@ void CMSWindowsSecondaryScreen::keyRepeat( Keystrokes keys; UINT virtualKey; - // get the sequence of keys to simulate key release and the final + // get the sequence of keys to simulate key repeat and the final // modifier state. - m_mask = mapKey(keys, virtualKey, key, mask, true); + m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); if (keys.empty()) return; // generate key events -// YYY -- need to know which code in Keystrokes should be repeated; -// then repeat only that key count times - for (SInt32 i = 0; i < count; ++i) { - for (Keystrokes::const_iterator k = keys.begin(); - k != keys.end(); ++k) { - const UINT code = MapVirtualKey(k->first, 0); - keybd_event(k->first, code, k->second ? 0 : KEYEVENTF_KEYUP, 0); - } - } + doKeystrokes(keys, count); } void CMSWindowsSecondaryScreen::keyUp( @@ -235,15 +225,12 @@ void CMSWindowsSecondaryScreen::keyUp( // get the sequence of keys to simulate key release and the final // modifier state. - m_mask = mapKey(keys, virtualKey, key, mask, false); + m_mask = mapKey(keys, virtualKey, key, mask, kRelease); if (keys.empty()) return; // generate key events - for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ++k) { - const UINT code = MapVirtualKey(k->first, 0); - keybd_event(k->first, code, k->second ? 0 : KEYEVENTF_KEYUP, 0); - } + doKeystrokes(keys, 1); // note that key is now up m_keys[virtualKey] &= ~0x80; @@ -252,7 +239,7 @@ void CMSWindowsSecondaryScreen::keyUp( void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) { // map button id to button flag - DWORD flags = mapButton(button); + DWORD flags = mapButton(button, true); // send event if (flags != 0) @@ -262,7 +249,7 @@ void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) { // map button id to button flag - DWORD flags = mapButton(button); + DWORD flags = mapButton(button, false); // send event if (flags != 0) @@ -786,7 +773,7 @@ static const UINT g_function[] = /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ VK_TAB, 0, 0, 0, 0, 0, 0, 0, /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -815,10 +802,6 @@ static const UINT g_function[] = /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 }; -// FIXME -- will this work? -// 0x100 + = shift -// 0x200 + = ctrl -// 0x400 + = alt /* XK_KP_Space to XK_KP_Equal */ static const UINT g_miscellany[] = { @@ -919,18 +902,18 @@ static const UINT* g_mapTable[] = }; DWORD CMSWindowsSecondaryScreen::mapButton( - ButtonID button) const + ButtonID button, bool press) const { // map button id to button flag switch (button) { case kButtonLeft: - return MOUSEEVENTF_LEFTDOWN; + return press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; case kButtonMiddle: - return MOUSEEVENTF_MIDDLEDOWN; + return press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; case kButtonRight: - return MOUSEEVENTF_RIGHTDOWN; + return press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; default: return 0; @@ -941,7 +924,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( Keystrokes& keys, UINT& virtualKey, KeyID id, KeyModifierMask mask, - bool press) const + EKeyAction action) const { // lookup the key table const UInt32 mapID = ((id >> 8) & 0xff); @@ -959,17 +942,20 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( (KeyModifierCapsLock | KeyModifierNumLock | KeyModifierScrollLock)); + log((CLOG_DEBUG2 "key id %d -> virtual key %d", id, virtualKey)); // if not in map then ask system to convert ascii character if (virtualKey == 0) { if (mapID != 0) { // not ascii + log((CLOG_DEBUG2 "not ascii")); return m_mask; } // translate. return no keys if unknown key. SHORT vk = VkKeyScan(code); if (vk == 0xffff) { + log((CLOG_DEBUG2 "no virtual key for character %d", code)); return m_mask; } @@ -980,11 +966,30 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( outMask |= KeyModifierControl; if (HIBYTE(vk) & 4) outMask |= KeyModifierAlt; + log((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%04x", code, LOBYTE(vk), outMask)); - // if caps-lock is on and so is shift then turn off caps-lock - if (outMask & (KeyModifierShift | KeyModifierCapsLock) == - (KeyModifierShift | KeyModifierCapsLock)) - outMask &= ~KeyModifierCapsLock; + // handle combination of caps-lock and shift. if caps-lock is + // off locally then use shift as necessary. if caps-lock is on + // locally then shift reverses its meaning (for keys that are + // subject to case conversion). + if ((m_mask & KeyModifierCapsLock) != 0) { + // caps-lock is on. note if shift is required. + log((CLOG_DEBUG2 "caps-lock is on")); + const bool needShift = ((outMask & KeyModifierShift) != 0); + + // if needShift is true then see if the key is subject to + // case conversion. if it is then caps-lock and shift + // cancel out. if not then caps-lock is ignored and shift + // is required. + if (needShift) { + // FIXME -- there should be some system call to test + // if a key is subject to case conversion. + if (tolower(code) != toupper(code)) { + log((CLOG_DEBUG2 "turn off shift")); + outMask &= ~KeyModifierShift; + } + } + } // get virtual key virtualKey = LOBYTE(vk); @@ -1001,11 +1006,22 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // virtual keys but not for numeric keys. if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) { // set required shift state based on current numlock state - if ((outMask & KeyModifierNumLock) == 0) - outMask |= KeyModifierShift; + if ((outMask & KeyModifierNumLock) == 0) { + if ((m_mask & KeyModifierNumLock) == 0) { + log((CLOG_DEBUG2 "turn on num lock for keypad key")); + outMask |= KeyModifierNumLock; + } + else { + log((CLOG_DEBUG2 "turn on shift for keypad key")); + outMask |= KeyModifierShift; + } + } } - // FIXME -- should check for LeftTab KeySym + // check for ISO_Left_Tab + else if (id == 0xfe20) { + outMask |= KeyModifierShift; + } } // a list of modifier key info @@ -1078,6 +1094,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // a modifier key then skip this because modifiers should not // modify modifiers. Keystrokes undo; + Keystroke keystroke; if (outMask != m_mask && !isModifier) { for (unsigned int i = 0; i < s_numModifiers; ++i) { KeyModifierMask bit = s_modifier[i].mask; @@ -1087,15 +1104,20 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a // press. - const UINT modifierKey = s_modifier[i].virtualKey; - keys.push_back(std::make_pair(modifierKey, true)); + keystroke.m_virtualKey = s_modifier[i].virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); if (s_modifier[i].isToggle) { - keys.push_back(std::make_pair(modifierKey, false)); - undo.push_back(std::make_pair(modifierKey, false)); - undo.push_back(std::make_pair(modifierKey, true)); + keystroke.m_press = false; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); } else { - undo.push_back(std::make_pair(modifierKey, false)); + keystroke.m_press = false; + undo.push_back(keystroke); } } @@ -1106,22 +1128,34 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // release. we must check each keycode for the // modifier if not a toggle. if (s_modifier[i].isToggle) { - const UINT modifierKey = s_modifier[i].virtualKey; - keys.push_back(std::make_pair(modifierKey, true)); - keys.push_back(std::make_pair(modifierKey, false)); - undo.push_back(std::make_pair(modifierKey, false)); - undo.push_back(std::make_pair(modifierKey, true)); + keystroke.m_virtualKey = s_modifier[i].virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); } else { UINT key = s_modifier[i].virtualKey; if ((m_keys[key] & 0x80) != 0) { - keys.push_back(std::make_pair(key, false)); - undo.push_back(std::make_pair(key, true)); + keystroke.m_virtualKey = key; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); } key = s_modifier[i].virtualKey2; - if ((m_keys[key] & 0x80) != 0) { - keys.push_back(std::make_pair(key, false)); - undo.push_back(std::make_pair(key, true)); + if (key != 0 && (m_keys[key] & 0x80) != 0) { + keystroke.m_virtualKey = key; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); } } } @@ -1130,7 +1164,26 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( } // add the key event - keys.push_back(std::make_pair(virtualKey, press)); + keystroke.m_virtualKey = virtualKey; + switch (action) { + case kPress: + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRelease: + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + break; + + case kRepeat: + keystroke.m_press = true; + keystroke.m_repeat = true; + keys.push_back(keystroke); + break; + } // add key events to restore the modifier state. apply events in // the reverse order that they're stored in undo. @@ -1142,16 +1195,15 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // if the key is a modifier key then compute the modifier mask after // this key is pressed. mask = m_mask; - if (isModifier) { - // toggle keys modify the state on press if toggling on and on - // release if toggling off. other keys set the bit on press - // and clear the bit on release. - // FIXME -- verify if that's true on win32 + if (isModifier && action != kRepeat) { + // toggle keys modify the state on release. other keys set + // the bit on press and clear the bit on release. if (s_modifier[modifierIndex].isToggle) { - if (((mask & s_modifier[modifierIndex].mask) == 0) == press) + if (action == kRelease) { mask ^= s_modifier[modifierIndex].mask; + } } - else if (press) { + else if (action == kPress) { mask |= s_modifier[modifierIndex].mask; } else { @@ -1161,7 +1213,8 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( if ((m_keys[s_modifier[modifierIndex].virtualKey] & 0x80) != 0) { down = true; } - if ((m_keys[s_modifier[modifierIndex].virtualKey2] & 0x80) != 0) { + if (s_modifier[modifierIndex].virtualKey2 != 0 && + (m_keys[s_modifier[modifierIndex].virtualKey2] & 0x80) != 0) { down = true; } if (!down) @@ -1169,12 +1222,65 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( } } + log((CLOG_DEBUG2 "previous modifiers 0x%04x, final modifiers 0x%04x", m_mask, mask)); return mask; } +void CMSWindowsSecondaryScreen::doKeystrokes( + const Keystrokes& keys, SInt32 count) +{ + // do nothing if no keys or no repeats + if (count < 1 || keys.empty()) + return; + + // generate key events + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { + if (k->m_repeat) { + // repeat from here up to but not including the next key + // with m_repeat == false count times. + Keystrokes::const_iterator start = k; + for (; count > 0; --count) { + // send repeating events + for (k = start; k != keys.end() && k->m_repeat; ++k) { + const UINT code = MapVirtualKey(k->m_virtualKey, 0); + keybd_event(k->m_virtualKey, code, + k->m_press ? 0 : KEYEVENTF_KEYUP, 0); + } + } + + // note -- k is now on the first non-repeat key after the + // repeat keys, exactly where we'd like to continue from. + } + else { + // send event + const UINT code = MapVirtualKey(k->m_virtualKey, 0); + keybd_event(k->m_virtualKey, code, + k->m_press ? 0 : KEYEVENTF_KEYUP, 0); + + // next key + ++k; + } + } +} + void CMSWindowsSecondaryScreen::updateKeys() { - GetKeyboardState(m_keys); + // GetKeyboardKeys() doesn't seem to do the expected thing + memset(m_keys, 0, sizeof(m_keys)); + m_keys[VK_LSHIFT] = GetKeyState(VK_LSHIFT); + m_keys[VK_RSHIFT] = GetKeyState(VK_RSHIFT); + m_keys[VK_SHIFT] = GetKeyState(VK_SHIFT); + m_keys[VK_LCONTROL] = GetKeyState(VK_LCONTROL); + m_keys[VK_RCONTROL] = GetKeyState(VK_RCONTROL); + m_keys[VK_CONTROL] = GetKeyState(VK_CONTROL); + m_keys[VK_LMENU] = GetKeyState(VK_LMENU); + m_keys[VK_RMENU] = GetKeyState(VK_RMENU); + m_keys[VK_MENU] = GetKeyState(VK_MENU); + m_keys[VK_LWIN] = GetKeyState(VK_LWIN); + m_keys[VK_RWIN] = GetKeyState(VK_RWIN); + m_keys[VK_CAPITAL] = GetKeyState(VK_CAPITAL); + m_keys[VK_NUMLOCK] = GetKeyState(VK_NUMLOCK); + m_keys[VK_SCROLL] = GetKeyState(VK_SCROLL); } void CMSWindowsSecondaryScreen::updateModifiers() @@ -1195,6 +1301,7 @@ void CMSWindowsSecondaryScreen::updateModifiers() m_mask |= KeyModifierNumLock; if ((m_keys[VK_SCROLL] & 0x01) != 0) m_mask |= KeyModifierScrollLock; + log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); } void CMSWindowsSecondaryScreen::toggleKey( diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 56235c65..d05f44ab 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -40,12 +40,19 @@ protected: virtual void onCloseDisplay(); private: - typedef std::pair Keystroke; + enum EKeyAction { kPress, kRelease, kRepeat }; + class Keystroke { + public: + UINT m_virtualKey; + bool m_press; + bool m_repeat; + }; typedef std::vector Keystrokes; - DWORD mapButton(ButtonID button) const; + DWORD mapButton(ButtonID button, bool press) const; KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, - KeyModifierMask, bool press) const; + KeyModifierMask, EKeyAction) const; + void doKeystrokes(const Keystrokes&, SInt32 count); void updateKeys(); void updateModifiers(); diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index dc631862..be55c3e7 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -135,9 +135,11 @@ void CTCPSocket::init() CNetwork::TCPNoDelayType flag = 1; CNetwork::setsockopt(m_fd, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); +#if !defined(CONFIG_PLATFORM_WIN32) // don't buffer sends, we merge messages ourself int data = 0; CNetwork::setsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &data, sizeof(data)); +#endif } void CTCPSocket::ioThread(void*) diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 51196271..8ffb036a 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -252,7 +252,7 @@ void CMSWindowsPrimaryScreen::getClipboard( CClipboard::copy(dst, &src); } -KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const +KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { KeyModifierMask mask; if ((m_keys[VK_CAPITAL] & 0x01) != 0) From 8de269636901674abcaa13ecd14e4a42b67b4645 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 4 May 2002 18:09:02 +0000 Subject: [PATCH 077/807] checkpoint. changed when toggle keys toggle (now always on release). must see if this works. --- client/CXWindowsSecondaryScreen.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 3c16cbe9..bfac2f44 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -585,13 +585,23 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // get modifier const unsigned int modifierBit = (1 << index->second); +/* FIXME -- old comment // toggle keys modify the state on press if toggling on and on // release if toggling off. other keys set the bit on press // and clear the bit on release. if half-duplex then toggle // each time we get here. +*/ + // toggle keys modify the state on release. other keys set the + // bit on press and clear the bit on release. if half-duplex + // then toggle each time we get here. if ((modifierBit & m_toggleModifierMask) != 0) { + if (action == kRelease) { + mask ^= modifierBit; + } +/* FIXME -- supposed to toggle on on press and off on release but does it? if (((mask & modifierBit) == 0) == (action == kPress)) mask ^= modifierBit; +*/ } else if (action == kPress) { mask |= modifierBit; From cd3e49a9a9e9e0062a3250a6383b6bfb23789a7a Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 4 May 2002 18:31:54 +0000 Subject: [PATCH 078/807] checkpoint. fixing up handling of half-duplex num-lock. --- client/CXWindowsSecondaryScreen.cpp | 28 +++++++++++++++++----------- client/CXWindowsSecondaryScreen.h | 3 ++- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index bfac2f44..47948f72 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -159,7 +159,9 @@ void CXWindowsSecondaryScreen::open(CClient* client) // check for peculiarities // FIXME -- may have to get these from some database + m_numLockHalfDuplex = false; m_capsLockHalfDuplex = false; +// m_numLockHalfDuplex = true; // m_capsLockHalfDuplex = true; // assume primary has all clipboards @@ -421,7 +423,8 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // that cannot be accomodated. // note if the key is the caps lock and it's "half-duplex" - const bool isHalfDuplex = (id == XK_Caps_Lock && m_capsLockHalfDuplex); + const bool isHalfDuplex = ((id == XK_Caps_Lock && m_capsLockHalfDuplex) || + (id == XK_Num_Lock && m_numLockHalfDuplex)); // ignore releases and repeats for half-duplex keys if (isHalfDuplex && action != kPress) { @@ -479,15 +482,16 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( keys.push_back(keystroke); if ((bit & m_toggleModifierMask) != 0) { log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); - if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { + if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || + (bit == m_numLockMask && m_numLockHalfDuplex)) { keystroke.m_press = False; - keys.push_back(keystroke); - undo.push_back(keystroke); - keystroke.m_press = True; undo.push_back(keystroke); } else { keystroke.m_press = False; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = True; undo.push_back(keystroke); } } @@ -508,18 +512,19 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); keystroke.m_keycode = modifierKeys[0]; keystroke.m_repeat = False; - if (bit != m_capsLockMask || !m_capsLockHalfDuplex) { - keystroke.m_press = True; - keys.push_back(keystroke); + if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || + (bit == m_numLockMask && m_numLockHalfDuplex)) { keystroke.m_press = False; keys.push_back(keystroke); - undo.push_back(keystroke); keystroke.m_press = True; undo.push_back(keystroke); } else { + keystroke.m_press = True; + keys.push_back(keystroke); keystroke.m_press = False; keys.push_back(keystroke); + undo.push_back(keystroke); keystroke.m_press = True; undo.push_back(keystroke); } @@ -595,7 +600,7 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // bit on press and clear the bit on release. if half-duplex // then toggle each time we get here. if ((modifierBit & m_toggleModifierMask) != 0) { - if (action == kRelease) { + if (isHalfDuplex || action == kRelease) { mask ^= modifierBit; } /* FIXME -- supposed to toggle on on press and off on release but does it? @@ -964,7 +969,8 @@ void CXWindowsSecondaryScreen::toggleKey( KeyCode keycode = index->second.m_keycode; // toggle the key - if (keysym == XK_Caps_Lock && m_capsLockHalfDuplex) { + if ((keysym == XK_Caps_Lock && m_capsLockHalfDuplex) || + (keysym == XK_Num_Lock && m_numLockHalfDuplex)) { // "half-duplex" toggle XTestFakeKeyEvent(display, keycode, (m_mask & mask) == 0, CurrentTime); } diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 2a79266b..6aa1cb8b 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -77,8 +77,9 @@ private: CClient* m_client; Window m_window; - // note if caps lock key toggles on up/down (false) or on + // note toggle keys that toggles on up/down (false) or on // transition (true) + bool m_numLockHalfDuplex; bool m_capsLockHalfDuplex; // set entries indicate keys that are pressed. indexed by keycode. From 2dad59a027e5e3e8d394a0d91630841e914a7fba Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 4 May 2002 18:33:48 +0000 Subject: [PATCH 079/807] checkpoint. added half duplex for num lock. --- server/CXWindowsPrimaryScreen.cpp | 14 ++++++++++++-- server/CXWindowsPrimaryScreen.h | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index aacb32b6..bae8ca5c 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -57,8 +57,12 @@ void CXWindowsPrimaryScreen::run() const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { m_server->onKeyDown(key, mask); - if (key == XK_Caps_Lock && m_capsLockHalfDuplex) + if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { m_server->onKeyUp(key, mask | KeyModifierCapsLock); + } + else if (key == XK_Num_Lock && m_numLockHalfDuplex) { + m_server->onKeyUp(key, mask | KeyModifierNumLock); + } } break; } @@ -85,8 +89,12 @@ void CXWindowsPrimaryScreen::run() (XPointer)&filter) != True) { // no press event follows so it's a plain release log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - if (key == XK_Caps_Lock && m_capsLockHalfDuplex) + if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { m_server->onKeyDown(key, mask); + } + else if (key == XK_Num_Lock && m_numLockHalfDuplex) { + m_server->onKeyDown(key, mask); + } m_server->onKeyUp(key, mask); } else { @@ -242,7 +250,9 @@ void CXWindowsPrimaryScreen::open(CServer* server) // check for peculiarities // FIXME -- may have to get these from some database + m_numLockHalfDuplex = false; m_capsLockHalfDuplex = false; +// m_numLockHalfDuplex = true; // m_capsLockHalfDuplex = true; // update key state diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 8c521bed..381b013a 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -57,8 +57,9 @@ private: bool m_active; Window m_window; - // note if caps lock key toggles on up/down (false) or on + // note toggle keys that toggle on up/down (false) or on // transition (true) + bool m_numLockHalfDuplex; bool m_capsLockHalfDuplex; // masks that indicate which modifier bits are for toggle keys From 2d7e3e8a614903aa215fcdd67919d501624e35f5 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 4 May 2002 19:43:20 +0000 Subject: [PATCH 080/807] fixed caps-lock handling. --- client/CXWindowsSecondaryScreen.cpp | 39 +++++++++++++++-------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 47948f72..278f4bcc 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -590,12 +590,6 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // get modifier const unsigned int modifierBit = (1 << index->second); -/* FIXME -- old comment - // toggle keys modify the state on press if toggling on and on - // release if toggling off. other keys set the bit on press - // and clear the bit on release. if half-duplex then toggle - // each time we get here. -*/ // toggle keys modify the state on release. other keys set the // bit on press and clear the bit on release. if half-duplex // then toggle each time we get here. @@ -603,10 +597,6 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( if (isHalfDuplex || action == kRelease) { mask ^= modifierBit; } -/* FIXME -- supposed to toggle on on press and off on release but does it? - if (((mask & modifierBit) == 0) == (action == kPress)) - mask ^= modifierBit; -*/ } else if (action == kPress) { mask |= modifierBit; @@ -722,15 +712,21 @@ bool CXWindowsSecondaryScreen::findKeyCode( maskOut = (maskIn & ~index->second.m_keyMaskMask); log((CLOG_DEBUG2 "maskIn(0x%04x) & ~maskMask(0x%04x) -> 0x%04x", maskIn, index->second.m_keyMaskMask, maskOut)); if (IsKeypadKey(id) || IsPrivateKeypadKey(id)) { - maskOut |= index->second.m_keyMask; - maskOut &= ~m_numLockMask; - log((CLOG_DEBUG2 "keypad key: | mask(0x%04x) & ~numLockMask(0x%04x) -> 0x%04x", index->second.m_keyMask, m_numLockMask, maskOut)); + if ((m_mask & m_numLockMask) != 0) { + maskOut &= ~index->second.m_keyMask; + maskOut |= m_numLockMask; + log((CLOG_DEBUG2 "keypad key: & ~mask(0x%04x) | numLockMask(0x%04x) -> 0x%04x", index->second.m_keyMask, m_numLockMask, maskOut)); + } + else { + maskOut |= index->second.m_keyMask; + maskOut &= ~m_numLockMask; + log((CLOG_DEBUG2 "keypad key: | mask(0x%04x) & ~numLockMask(0x%04x) -> 0x%04x", index->second.m_keyMask, m_numLockMask, maskOut)); + } } else { unsigned int maskShift = (index->second.m_keyMask & ShiftMask); log((CLOG_DEBUG2 "maskShift = 0x%04x", maskShift)); - if (index->second.m_keyMaskMask != 0 && - (m_mask & m_capsLockMask) != 0) { + if (maskShift != 0 && (m_mask & m_capsLockMask) != 0) { // shift and capsLock cancel out for keysyms subject to // case conversion but not for keys with shifted // characters that are not case conversions. see if @@ -738,13 +734,18 @@ bool CXWindowsSecondaryScreen::findKeyCode( KeySym lKey, uKey; XConvertCase(id, &lKey, &uKey); if (lKey != uKey) { - maskShift ^= ShiftMask; - log((CLOG_DEBUG2 "maskMask != 0 && capsLock on -> toggle maskShift 0x%04x", maskShift)); + log((CLOG_DEBUG2 "case convertable, shift && capsLock -> caps lock")); + maskShift = m_capsLockMask; + } + else { + log((CLOG_DEBUG2 "case unconvertable, shift && capsLock -> shift, caps lock")); + maskShift |= m_capsLockMask; } } - maskOut |= maskShift | (m_mask & m_capsLockMask); + log((CLOG_DEBUG2 "maskShift = 0x%04x", maskShift)); + maskOut |= maskShift; maskOut |= (index->second.m_keyMask & ~(ShiftMask | LockMask)); - log((CLOG_DEBUG2 "| maskShift(0x%04x) | old caps lock(0x%04x) | other (0x%04x) -> 0x%04x", maskShift, (m_mask & m_capsLockMask), (index->second.m_keyMask & ~(ShiftMask | LockMask)), maskOut)); + log((CLOG_DEBUG2 "| maskShift(0x%04x) | other (0x%04x) -> 0x%04x", maskShift, (index->second.m_keyMask & ~(ShiftMask | LockMask)), maskOut)); } return true; From 7ebaab42e091b2cd790af4ead57be16ca8c98bdf Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 5 May 2002 19:38:09 +0000 Subject: [PATCH 081/807] fixes for win32 keyboard. --- client/CMSWindowsSecondaryScreen.cpp | 142 ++++++++++++++++++++------- server/CMSWindowsPrimaryScreen.cpp | 7 +- 2 files changed, 106 insertions(+), 43 deletions(-) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 973bb004..e593c56f 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -199,6 +199,22 @@ void CMSWindowsSecondaryScreen::keyDown( // note that key is now down m_keys[virtualKey] |= 0x80; + switch (virtualKey) { + case VK_LSHIFT: + case VK_RSHIFT: + m_keys[VK_SHIFT] |= 0x80; + break; + + case VK_LCONTROL: + case VK_RCONTROL: + m_keys[VK_CONTROL] |= 0x80; + break; + + case VK_LMENU: + case VK_RMENU: + m_keys[VK_MENU] |= 0x80; + break; + } } void CMSWindowsSecondaryScreen::keyRepeat( @@ -234,6 +250,43 @@ void CMSWindowsSecondaryScreen::keyUp( // note that key is now up m_keys[virtualKey] &= ~0x80; + switch (virtualKey) { + case VK_LSHIFT: + if ((m_keys[VK_RSHIFT] & 0x80) == 0) { + m_keys[VK_SHIFT] &= ~0x80; + } + break; + + case VK_RSHIFT: + if ((m_keys[VK_LSHIFT] & 0x80) == 0) { + m_keys[VK_SHIFT] &= ~0x80; + } + break; + + case VK_LCONTROL: + if ((m_keys[VK_RCONTROL] & 0x80) == 0) { + m_keys[VK_CONTROL] &= ~0x80; + } + break; + + case VK_RCONTROL: + if ((m_keys[VK_LCONTROL] & 0x80) == 0) { + m_keys[VK_CONTROL] &= ~0x80; + } + break; + + case VK_LMENU: + if ((m_keys[VK_RMENU] & 0x80) == 0) { + m_keys[VK_MENU] &= ~0x80; + } + break; + + case VK_RMENU: + if ((m_keys[VK_LMENU] & 0x80) == 0) { + m_keys[VK_MENU] &= ~0x80; + } + break; + } } void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) @@ -837,11 +890,9 @@ static const UINT g_miscellany[] = /* 0xc8 */ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, -/* FIXME -- want to use LSHIFT, LCONTROL, and LMENU but those don't seem - * to affect the shift state for VkKeyScan. */ - /* 0xe0 */ 0, VK_SHIFT, VK_RSHIFT, VK_CONTROL, + /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, /* 0xe4 */ VK_RCONTROL, VK_CAPITAL, 0, VK_LWIN, - /* 0xe8 */ VK_RWIN, VK_MENU, VK_RMENU, 0, 0, 0, 0, 0, + /* 0xe8 */ VK_RWIN, VK_LMENU, VK_RMENU, 0, 0, 0, 0, 0, /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE }; @@ -935,13 +986,19 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( } // look up virtual key for id. default output mask carries over - // the current toggle modifier states. + // the current toggle modifier states and includes desired shift, + // control, alt, and meta states. const UInt32 code = (id & 0xff); virtualKey = map[code]; KeyModifierMask outMask = (m_mask & (KeyModifierCapsLock | KeyModifierNumLock | KeyModifierScrollLock)); + outMask |= (mask & + (KeyModifierShift | + KeyModifierControl | + KeyModifierAlt | + KeyModifierMeta)); log((CLOG_DEBUG2 "key id %d -> virtual key %d", id, virtualKey)); // if not in map then ask system to convert ascii character @@ -959,6 +1016,12 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( return m_mask; } + // use whatever shift state VkKeyScan says + // FIXME -- also for control and alt, but it's more difficult + // to determine if control and alt must be off or if it just + // doesn't matter. + outMask &= ~KeyModifierShift; + // convert system modifier mask to our mask if (HIBYTE(vk) & 1) outMask |= KeyModifierShift; @@ -1027,19 +1090,19 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // a list of modifier key info class CModifierInfo { public: - KeyModifierMask mask; - UINT virtualKey; - UINT virtualKey2; - bool isToggle; + KeyModifierMask m_mask; + UINT m_virtualKey; + UINT m_virtualKey2; + bool m_isToggle; }; static const CModifierInfo s_modifier[] = { - { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, - { KeyModifierControl, VK_LCONTROL, VK_RCONTROL, false }, - { KeyModifierAlt, VK_LMENU, VK_RMENU, false }, - { KeyModifierMeta, VK_LWIN, VK_RWIN, false }, - { KeyModifierCapsLock, VK_CAPITAL, 0, true }, - { KeyModifierNumLock, VK_NUMLOCK, 0, true }, - { KeyModifierScrollLock, VK_SCROLL, 0, true } + { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, + { KeyModifierControl, VK_LCONTROL, VK_RCONTROL,false }, + { KeyModifierAlt, VK_LMENU, VK_RMENU, false }, + { KeyModifierMeta, VK_LWIN, VK_RWIN, false }, + { KeyModifierCapsLock, VK_CAPITAL, 0, true }, + { KeyModifierNumLock, VK_NUMLOCK, 0, true }, + { KeyModifierScrollLock, VK_SCROLL, 0, true } }; static const unsigned int s_numModifiers = sizeof(s_modifier) / sizeof(s_modifier[0]); @@ -1097,18 +1160,18 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( Keystroke keystroke; if (outMask != m_mask && !isModifier) { for (unsigned int i = 0; i < s_numModifiers; ++i) { - KeyModifierMask bit = s_modifier[i].mask; + KeyModifierMask bit = s_modifier[i].m_mask; if ((outMask & bit) != (m_mask & bit)) { if ((outMask & bit) != 0) { // modifier is not active but should be. if the // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a // press. - keystroke.m_virtualKey = s_modifier[i].virtualKey; + keystroke.m_virtualKey = s_modifier[i].m_virtualKey; keystroke.m_press = true; keystroke.m_repeat = false; keys.push_back(keystroke); - if (s_modifier[i].isToggle) { + if (s_modifier[i].m_isToggle) { keystroke.m_press = false; keys.push_back(keystroke); undo.push_back(keystroke); @@ -1127,8 +1190,8 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // press/release, otherwise deactivate it with a // release. we must check each keycode for the // modifier if not a toggle. - if (s_modifier[i].isToggle) { - keystroke.m_virtualKey = s_modifier[i].virtualKey; + if (s_modifier[i].m_isToggle) { + keystroke.m_virtualKey = s_modifier[i].m_virtualKey; keystroke.m_press = true; keystroke.m_repeat = false; keys.push_back(keystroke); @@ -1139,7 +1202,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( undo.push_back(keystroke); } else { - UINT key = s_modifier[i].virtualKey; + UINT key = s_modifier[i].m_virtualKey; if ((m_keys[key] & 0x80) != 0) { keystroke.m_virtualKey = key; keystroke.m_press = false; @@ -1148,7 +1211,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( keystroke.m_press = true; undo.push_back(keystroke); } - key = s_modifier[i].virtualKey2; + key = s_modifier[i].m_virtualKey2; if (key != 0 && (m_keys[key] & 0x80) != 0) { keystroke.m_virtualKey = key; keystroke.m_press = false; @@ -1164,23 +1227,25 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( } // add the key event - keystroke.m_virtualKey = virtualKey; switch (action) { case kPress: - keystroke.m_press = true; - keystroke.m_repeat = false; + keystroke.m_virtualKey = virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; keys.push_back(keystroke); break; case kRelease: - keystroke.m_press = false; - keystroke.m_repeat = false; + keystroke.m_virtualKey = virtualKey; + keystroke.m_press = false; + keystroke.m_repeat = false; keys.push_back(keystroke); break; case kRepeat: - keystroke.m_press = true; - keystroke.m_repeat = true; + keystroke.m_virtualKey = virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = true; keys.push_back(keystroke); break; } @@ -1198,27 +1263,30 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( if (isModifier && action != kRepeat) { // toggle keys modify the state on release. other keys set // the bit on press and clear the bit on release. - if (s_modifier[modifierIndex].isToggle) { + const CModifierInfo& modifier = s_modifier[modifierIndex]; + if (modifier.m_isToggle) { if (action == kRelease) { - mask ^= s_modifier[modifierIndex].mask; + mask ^= modifier.m_mask; } } else if (action == kPress) { - mask |= s_modifier[modifierIndex].mask; + mask |= modifier.m_mask; } else { // can't reset bit until all keys that set it are released. // scan those keys to see if any are pressed. bool down = false; - if ((m_keys[s_modifier[modifierIndex].virtualKey] & 0x80) != 0) { + if (virtualKey != modifier.m_virtualKey && + (m_keys[modifier.m_virtualKey] & 0x80) != 0) { down = true; } - if (s_modifier[modifierIndex].virtualKey2 != 0 && - (m_keys[s_modifier[modifierIndex].virtualKey2] & 0x80) != 0) { + if (modifier.m_virtualKey2 != 0 && + virtualKey != modifier.m_virtualKey2 && + (m_keys[modifier.m_virtualKey2] & 0x80) != 0) { down = true; } if (!down) - mask &= ~s_modifier[modifierIndex].mask; + mask &= ~modifier.m_mask; } } diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 8ffb036a..f5f67872 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -1006,9 +1006,6 @@ void CMSWindowsPrimaryScreen::updateKey( case VK_SCROLL: // toggle keys m_keys[vkCode] |= 0x80; - if ((m_keys[vkCode] & 0x01) == 0) { - m_keys[vkCode] |= 0x01; - } break; } } @@ -1052,9 +1049,7 @@ void CMSWindowsPrimaryScreen::updateKey( case VK_SCROLL: // toggle keys m_keys[vkCode] &= ~0x80; - if ((m_keys[vkCode] & 0x01) != 0) { - m_keys[vkCode] &= ~0x01; - } + m_keys[vkCode] ^= 0x01; break; } } From f8f1fb40411bb048aec0b2671ffa33491a95dcc4 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 5 May 2002 19:52:03 +0000 Subject: [PATCH 082/807] replaced True/False with true/false when assigning to m_repeat. also should now work if the first element of a modifier keymapping is 0. that won't normally be the case but xmodmap was doing weird things on grace. if the first element is 0 it'll try the second element. if that's also zero then that modifier will be ignored. --- client/CXWindowsSecondaryScreen.cpp | 63 ++++++++++++++++------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 278f4bcc..f83d6814 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -468,17 +468,20 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // be at least one. const KeyCode* modifierKeys = &m_modifierToKeycode[i * m_keysPerModifier]; + KeyCode modifierKey = modifierKeys[0]; + if (modifierKey == 0) + modifierKey = modifierKeys[1]; assert(modifierKeys[0] != 0); - if ((outMask & bit) != 0) { + if (modifierKey != 0 && (outMask & bit) != 0) { // modifier is not active but should be. if the // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a // press. use the first keycode for the modifier. log((CLOG_DEBUG2 "modifier 0x%04x is not active", bit)); - keystroke.m_keycode = modifierKeys[0]; + keystroke.m_keycode = modifierKey; keystroke.m_press = True; - keystroke.m_repeat = False; + keystroke.m_repeat = false; keys.push_back(keystroke); if ((bit & m_toggleModifierMask) != 0) { log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); @@ -501,7 +504,7 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( } } - else { + else if ((outMask & bit) == 0) { // modifier is active but should not be. if the // modifier is a toggle then toggle it off with a // press/release, otherwise deactivate it with a @@ -509,33 +512,37 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( // modifier if not a toggle. log((CLOG_DEBUG2 "modifier 0x%04x is active", bit)); if ((bit & m_toggleModifierMask) != 0) { - log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); - keystroke.m_keycode = modifierKeys[0]; - keystroke.m_repeat = False; - if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || - (bit == m_numLockMask && m_numLockHalfDuplex)) { - keystroke.m_press = False; - keys.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); - } - else { - keystroke.m_press = True; - keys.push_back(keystroke); - keystroke.m_press = False; - keys.push_back(keystroke); - undo.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); + if (modifierKey != 0) { + log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); + keystroke.m_keycode = modifierKey; + keystroke.m_repeat = false; + if ((bit == m_capsLockMask && + m_capsLockHalfDuplex) || + (bit == m_numLockMask && + m_numLockHalfDuplex)) { + keystroke.m_press = False; + keys.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); + } + else { + keystroke.m_press = True; + keys.push_back(keystroke); + keystroke.m_press = False; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); + } } } else { for (unsigned int j = 0; j < m_keysPerModifier; ++j) { const KeyCode key = modifierKeys[j]; - if (m_keys[key]) { + if (key != 0 && m_keys[key]) { keystroke.m_keycode = key; keystroke.m_press = False; - keystroke.m_repeat = False; + keystroke.m_repeat = false; keys.push_back(keystroke); keystroke.m_press = True; undo.push_back(keystroke); @@ -557,19 +564,19 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( switch (action) { case kPress: keystroke.m_press = True; - keystroke.m_repeat = False; + keystroke.m_repeat = false; keys.push_back(keystroke); break; case kRelease: keystroke.m_press = False; - keystroke.m_repeat = False; + keystroke.m_repeat = false; keys.push_back(keystroke); break; case kRepeat: keystroke.m_press = False; - keystroke.m_repeat = True; + keystroke.m_repeat = true; keys.push_back(keystroke); keystroke.m_press = True; keys.push_back(keystroke); @@ -608,7 +615,7 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( const KeyCode* modifierKeys = &m_modifierToKeycode[ index->second * m_keysPerModifier]; for (unsigned int j = 0; !down && j < m_keysPerModifier; ++j) { - if (m_keys[modifierKeys[j]]) + if (modifierKeys[j] != 0 && m_keys[modifierKeys[j]]) down = true; } if (!down) From 4968a5bb7cc60fcd28f660ff6e5f65f87fb47fce Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 5 May 2002 23:37:12 +0000 Subject: [PATCH 083/807] removed setting send buffer to zero size. it just reduced performance. --- net/CTCPSocket.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index be55c3e7..189a5e3e 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -134,12 +134,6 @@ void CTCPSocket::init() // mouse motion messages are much less useful if they're delayed. CNetwork::TCPNoDelayType flag = 1; CNetwork::setsockopt(m_fd, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); - -#if !defined(CONFIG_PLATFORM_WIN32) - // don't buffer sends, we merge messages ourself - int data = 0; - CNetwork::setsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &data, sizeof(data)); -#endif } void CTCPSocket::ioThread(void*) From 0cbd6b07a28cb9bbe124aed308c0d0333919e197 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 16:42:48 +0000 Subject: [PATCH 084/807] fixed NULL dereference. --- client/CClient.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index d1ef18a6..3f0bdd40 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -44,7 +44,7 @@ CClient::~CClient() void CClient::run(const CNetworkAddress& serverAddress) { - CThread* thread; + CThread* thread = NULL; try { log((CLOG_NOTE "starting client")); @@ -70,18 +70,22 @@ void CClient::run(const CNetworkAddress& serverAddress) log((CLOG_ERR "client error: %s", e.what())); // clean up - thread->cancel(); - thread->wait(); - delete thread; + if (thread != NULL) { + thread->cancel(); + thread->wait(); + delete thread; + } closeSecondaryScreen(); } catch (...) { log((CLOG_DEBUG "unknown client error")); // clean up - thread->cancel(); - thread->wait(); - delete thread; + if (thread != NULL) { + thread->cancel(); + thread->wait(); + delete thread; + } closeSecondaryScreen(); throw; } From aa125b07c5bb633a05e824c8320a2bc29802f1e8 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 16:43:14 +0000 Subject: [PATCH 085/807] changed set of disabled win32 warnings. --- base/common.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/base/common.h b/base/common.h index e95b14ea..2f6d2037 100644 --- a/base/common.h +++ b/base/common.h @@ -20,8 +20,20 @@ #define CONFIG_PLATFORM_WIN32 #if (_MSC_VER >= 1200) +// turn off bonehead warnings #pragma warning(disable: 4786) // identifier truncated in debug info -#endif + +// +// ignore warnings inside microsoft's standard C++ library +// +// bonehead bugs/warnings +#pragma warning(disable: 4097) // typedef-name used as synonym +#pragma warning(disable: 4511) // copy constructor can't be generated +#pragma warning(disable: 4512) // assignment operator can't be generated + +// we'd really rather have these enabled to check our code +#pragma warning(disable: 4100) // unreferenced formal parameter +#endif // (_MSC_VER >= 1200) #else From cda243ac766171b4e6b412debe6f812518619cd8 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 16:51:59 +0000 Subject: [PATCH 086/807] fixed parameter type for socket port. --- client/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/client.cpp b/client/client.cpp index 6cbf991c..8271c6e3 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -6,7 +6,7 @@ void realMain(const CString& name, const CString& hostname, - SInt32 port) + UInt16 port) { CThread::init(); CNetwork::init(); From 024f76c9098727f1b95a5e46da7d88d9be8e9ecf Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 16:55:05 +0000 Subject: [PATCH 087/807] changed un-inlined code to avoid bogus VC++ level 4 warnings. added support for more win32 thread priorities. --- mt/CThread.cpp | 15 +++++++++++++++ mt/CThread.h | 4 ++-- mt/CThreadRep.cpp | 14 +++++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/mt/CThread.cpp b/mt/CThread.cpp index 9df33fb9..f67573fb 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -117,3 +117,18 @@ bool CThread::operator!=(const CThread& thread) const { return (m_rep != thread.m_rep); } + + +// +// CThreadMaskCancel +// + +CThreadMaskCancel::CThreadMaskCancel() : m_old(CThread::enableCancel(false)) +{ + // do nothing +} + +CThreadMaskCancel::~CThreadMaskCancel() +{ + CThread::enableCancel(m_old); +} diff --git a/mt/CThread.h b/mt/CThread.h index 982a1912..b6ebe1e3 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -125,8 +125,8 @@ private: // disables cancellation in the c'tor and enables it in the d'tor. class CThreadMaskCancel { public: - CThreadMaskCancel() : m_old(CThread::enableCancel(false)) { } - ~CThreadMaskCancel() { CThread::enableCancel(m_old); } + CThreadMaskCancel(); + ~CThreadMaskCancel(); private: bool m_old; diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 4b2de42a..8b48f6b8 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -534,10 +534,21 @@ bool CThreadRep::wait(CThreadRep* target, double timeout) void CThreadRep::setPriority(int n) { + DWORD pClass = NORMAL_PRIORITY_CLASS; if (n < 0) { switch (-n) { case 1: n = THREAD_PRIORITY_ABOVE_NORMAL; break; - default: n = THREAD_PRIORITY_HIGHEST; break; + case 2: n = THREAD_PRIORITY_HIGHEST; break; + default: + pClass = HIGH_PRIORITY_CLASS; + switch (-n - 3) { + case 0: n = THREAD_PRIORITY_LOWEST; break; + case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; + case 2: n = THREAD_PRIORITY_NORMAL; break; + case 3: n = THREAD_PRIORITY_ABOVE_NORMAL; break; + default: n = THREAD_PRIORITY_HIGHEST; break; + } + break; } } else { @@ -548,6 +559,7 @@ void CThreadRep::setPriority(int n) default: n = THREAD_PRIORITY_IDLE; break; } } + SetPriorityClass(m_thread, pClass); SetThreadPriority(m_thread, n); } From 5c760183ce4a9d385b67f706d13a2eef0a229138 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 16:55:19 +0000 Subject: [PATCH 088/807] removed blank line. --- mt/CTimerThread.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/mt/CTimerThread.cpp b/mt/CTimerThread.cpp index 023cb06b..0a3e8542 100644 --- a/mt/CTimerThread.cpp +++ b/mt/CTimerThread.cpp @@ -33,4 +33,3 @@ void CTimerThread::timer(void*) log((CLOG_DEBUG1 "timeout")); m_callingThread->cancel(); } - From d893cc16a889aff553ba1573d4e01ac85365c81f Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 16:56:06 +0000 Subject: [PATCH 089/807] fixed type of socket handle (from int to CNetwork::Socket). --- net/CTCPListenSocket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/CTCPListenSocket.cpp b/net/CTCPListenSocket.cpp index cb95baf7..9dab306a 100644 --- a/net/CTCPListenSocket.cpp +++ b/net/CTCPListenSocket.cpp @@ -51,7 +51,7 @@ ISocket* CTCPListenSocket::accept() if (status > 0 && (pfds[0].revents & CNetwork::kPOLLIN) != 0) { CNetwork::Address addr; CNetwork::AddressLength addrlen = sizeof(addr); - int fd = CNetwork::accept(m_fd, &addr, &addrlen); + CNetwork::Socket fd = CNetwork::accept(m_fd, &addr, &addrlen); if (fd != CNetwork::Null) { return new CTCPSocket(fd); } From a5ae8011e294f7b9f92da70630457bef6dfd4ed1 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 17:01:17 +0000 Subject: [PATCH 090/807] win32 changes. replaced log dialog hack with a windows console window. now attaching thread input queues as necessary. shifted code around so toggling toggle keys is immediately reflected by secondary screen's keyboard. now setting extended key flag for keys that need it. fixed handling of shift + caps-lock. added handling of keys that should distinguish between left and right but don't. fixed get/set of active window on leave/enter of primary screen. replaced 1x1 primary window with a full screen window to work around a problem with losing key events. changed calculation of mouse move deltas. --- base/CLog.cpp | 72 +++++- base/CLog.h | 4 + client/CMSWindowsSecondaryScreen.cpp | 323 +++++++++++++++------------ client/CMSWindowsSecondaryScreen.h | 6 + server/CMSWindowsPrimaryScreen.cpp | 238 +++++++++----------- server/CMSWindowsPrimaryScreen.h | 5 +- 6 files changed, 371 insertions(+), 277 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index 8d11d44c..de22098e 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -1,5 +1,4 @@ #include "CLog.h" -#include "BasicTypes.h" #include #include #include @@ -139,14 +138,15 @@ void CLog::output(int priority, char* msg) #endif // print it - if (s_outputter) + if (s_outputter) { s_outputter(msg + g_maxPriorityLength - n); - else + } + else { #if defined(CONFIG_PLATFORM_WIN32) - OutputDebugString(msg + g_maxPriorityLength - n); -#else - fprintf(stderr, "%s", msg + g_maxPriorityLength - n); + openConsole(); #endif + fprintf(stderr, "%s", msg + g_maxPriorityLength - n); + } } } @@ -174,3 +174,63 @@ char* CLog::vsprint(int pad, char* buffer, int len, return buffer; } + +#if defined(CONFIG_PLATFORM_WIN32) + +static DWORD s_thread = 0; + +static BOOL WINAPI CLogSignalHandler(DWORD) +{ + // terminate cleanly and skip remaining handlers + PostThreadMessage(s_thread, WM_QUIT, 0, 0); + return TRUE; +} + +void CLog::openConsole() +{ + static bool s_hasConsole = false; + + // ignore if already created + if (s_hasConsole) + return; + + // remember the current thread. when we get a ctrl+break or the + // console is closed we'll post WM_QUIT to this thread to shutdown + // cleanly. + // note -- win95/98/me are broken and will not receive a signal + // when the console is closed nor during logoff or shutdown, + // see microsoft articles Q130717 and Q134284. we could work + // around this in a painful way using hooks and hidden windows + // (as apache does) but it's not worth it. the app will still + // quit, just not cleanly. users in-the-know can use ctrl+c. + s_thread = GetCurrentThreadId(); + + // open a console + if (!AllocConsole()) + return; + + // get the handle for error output + HANDLE herr = GetStdHandle(STD_ERROR_HANDLE); + + // prep console. windows 95 and its ilk have braindead + // consoles that can't even resize independently of the + // buffer size. use a 25 line buffer for those systems. + OSVERSIONINFO osInfo; + COORD size = { 80, 1000 }; + osInfo.dwOSVersionInfoSize = sizeof(osInfo); + if (GetVersionEx(&osInfo) && + osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + size.Y = 25; + SetConsoleScreenBufferSize(herr, size); + SetConsoleTextAttribute(herr, + FOREGROUND_RED | + FOREGROUND_GREEN | + FOREGROUND_BLUE); + SetConsoleCtrlHandler(CLogSignalHandler, TRUE); + + // reopen stderr to point at console + freopen("con", "w", stderr); + s_hasConsole = true; +} + +#endif diff --git a/base/CLog.h b/base/CLog.h index ae8fdc10..66cfa799 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -1,6 +1,7 @@ #ifndef CLOG_H #define CLOG_H +#include "BasicTypes.h" #include class CLog { @@ -15,6 +16,9 @@ private: static void output(int priority, char* msg); static char* vsprint(int pad, char*, int len, const char*, va_list); static int nprint(const char*, va_list); +#if defined(CONFIG_PLATFORM_WIN32) + static void openConsole(); +#endif private: static Outputter s_outputter; diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index e593c56f..cdca03c4 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -24,49 +24,18 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() assert(m_window == NULL); } -static CString s_log; -static CString s_logMore; -static HWND s_debug = NULL; -static HWND s_debugLog = NULL; -static DWORD s_thread = 0; -static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - return TRUE; - - case WM_CLOSE: - PostQuitMessage(0); - return TRUE; - - case WM_APP: - if (!s_logMore.empty()) { - if (s_log.size() > 40000) - s_log = s_logMore; - else - s_log += s_logMore; - s_logMore = ""; - SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)s_log.c_str()); - SendMessage(s_debugLog, EM_SETSEL, s_log.size(), s_log.size()); - SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0); - } - return TRUE; - } - return FALSE; -} -static void debugOutput(const char* msg) -{ - s_logMore += msg; - PostMessage(s_debug, WM_APP, 0, 0); -} - void CMSWindowsSecondaryScreen::run() { -CLog::setOutputter(&debugOutput); + // change our priority + CThread::getCurrentThread().setPriority(-7); + + // save thread id + m_threadID = GetCurrentThreadId(); + + // run event loop log((CLOG_INFO "entering event loop")); doRun(); log((CLOG_INFO "exiting event loop")); -CLog::setOutputter(NULL); } void CMSWindowsSecondaryScreen::stop() @@ -117,6 +86,24 @@ void CMSWindowsSecondaryScreen::enter( log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); + // attach thread input queues + AttachThreadInput(GetCurrentThreadId(), m_threadID, TRUE); + + // update our keyboard state to reflect the local state + updateKeys(); + updateModifiers(); + + // toggle modifiers that don't match the desired state + if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { + toggleKey(VK_CAPITAL, KeyModifierCapsLock); + } + if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { + toggleKey(VK_NUMLOCK | 0x100, KeyModifierNumLock); + } + if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { + toggleKey(VK_SCROLL, KeyModifierScrollLock); + } + // warp to requested location SInt32 w, h; getScreenSize(&w, &h); @@ -128,21 +115,6 @@ void CMSWindowsSecondaryScreen::enter( // show cursor log((CLOG_INFO "show cursor")); ShowWindow(m_window, SW_HIDE); - - // update our keyboard state to reflect the local state - updateKeys(); - updateModifiers(); - - // toggle modifiers that don't match the desired state - if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { - toggleKey(VK_CAPITAL, KeyModifierCapsLock); - } - if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { - toggleKey(VK_NUMLOCK, KeyModifierNumLock); - } - if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { - toggleKey(VK_SCROLL, KeyModifierScrollLock); - } } void CMSWindowsSecondaryScreen::leave() @@ -363,19 +335,10 @@ void CMSWindowsSecondaryScreen::getClipboard( CClipboard::copy(dst, &src); } -#include "resource.h" // FIXME - void CMSWindowsSecondaryScreen::onOpenDisplay() { assert(m_window == NULL); -// create debug dialog -s_thread = GetCurrentThreadId();; -s_debug = CreateDialog(getInstance(), MAKEINTRESOURCE(IDD_SYNERGY), NULL, &debugProc); -s_debugLog = ::GetDlgItem(s_debug, IDC_LOG); -CLog::setOutputter(&debugOutput); -ShowWindow(s_debug, SW_SHOWNORMAL); - // initialize clipboard owner to current owner. we don't want // to take ownership of the clipboard just by starting up. m_clipboardOwner = GetClipboardOwner(); @@ -410,22 +373,13 @@ void CMSWindowsSecondaryScreen::onCloseDisplay() // destroy window DestroyWindow(m_window); m_window = NULL; - -CLog::setOutputter(NULL); -DestroyWindow(s_debug); -s_debug = NULL; -s_thread = 0; } bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg) { -if (IsDialogMessage(s_debug, msg)) { - return true; -} return false; } - LRESULT CMSWindowsSecondaryScreen::onEvent( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -467,9 +421,14 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( SendMessage(m_nextClipboardWindow, msg, wParam, lParam); return 0; } + return DefWindowProc(hwnd, msg, wParam, lParam); } +// these tables map KeyID (a X windows KeySym) to virtual key codes. +// if the key is an extended key then the entry is the virtual key +// code | 0x100. keys that map to normal characters have a 0 entry +// and the conversion is done elsewhere. static const UINT g_latin1[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -859,7 +818,7 @@ static const UINT g_function[] = static const UINT g_miscellany[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ VK_BACK, VK_TAB, /*0x100 +*/ VK_RETURN, VK_CLEAR, 0, VK_RETURN, 0, 0, + /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, /* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0, /* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -868,21 +827,22 @@ static const UINT g_miscellany[] = /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, - /* 0x54 */ VK_DOWN, VK_PRIOR, VK_NEXT, VK_END, + /* 0x50 */ VK_HOME|0x100, VK_LEFT|0x100, VK_UP|0x100, VK_RIGHT|0x100, + /* 0x54 */ VK_DOWN|0x100, VK_PRIOR|0x100, VK_NEXT|0x100, VK_END|0x100, /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT, 0, 0, 0, VK_APPS, - /* 0x68 */ 0, 0, VK_HELP, VK_CANCEL, 0, 0, 0, 0, + /* 0x60 */ VK_SELECT|0x100, VK_SNAPSHOT|0x100, VK_EXECUTE|0x100, VK_INSERT|0x100, + /* 0x64 */ 0, 0, 0, VK_APPS|0x100, + /* 0x68 */ 0, 0, VK_HELP|0x100, VK_CANCEL|0x100, 0, 0, 0, 0, /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, VK_MODECHANGE, VK_NUMLOCK, + /* 0x78 */ 0, 0, 0, 0, 0, 0, VK_MODECHANGE|0x100, VK_NUMLOCK|0x100, /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN, 0, 0, + /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN|0x100, 0, 0, /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, /* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, - /* 0xac */ VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE, + /* 0xac */ VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100, /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, @@ -891,10 +851,10 @@ static const UINT g_miscellany[] = /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, - /* 0xe4 */ VK_RCONTROL, VK_CAPITAL, 0, VK_LWIN, - /* 0xe8 */ VK_RWIN, VK_LMENU, VK_RMENU, 0, 0, 0, 0, 0, + /* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, VK_LWIN|0x100, + /* 0xe8 */ VK_RWIN|0x100, VK_LMENU, VK_RMENU|0x100, 0, 0, 0, 0, 0, /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100 }; static const UINT* g_katakana = NULL; static const UINT* g_arabic = NULL; @@ -1001,6 +961,10 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( KeyModifierMeta)); log((CLOG_DEBUG2 "key id %d -> virtual key %d", id, virtualKey)); + // extract extended key flag + const bool isExtended = ((virtualKey & 0x100) != 0); + virtualKey &= ~0x100; + // if not in map then ask system to convert ascii character if (virtualKey == 0) { if (mapID != 0) { @@ -1010,7 +974,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( } // translate. return no keys if unknown key. - SHORT vk = VkKeyScan(code); + SHORT vk = VkKeyScan(static_cast(code)); if (vk == 0xffff) { log((CLOG_DEBUG2 "no virtual key for character %d", code)); return m_mask; @@ -1033,24 +997,12 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // handle combination of caps-lock and shift. if caps-lock is // off locally then use shift as necessary. if caps-lock is on - // locally then shift reverses its meaning (for keys that are - // subject to case conversion). - if ((m_mask & KeyModifierCapsLock) != 0) { - // caps-lock is on. note if shift is required. - log((CLOG_DEBUG2 "caps-lock is on")); - const bool needShift = ((outMask & KeyModifierShift) != 0); - - // if needShift is true then see if the key is subject to - // case conversion. if it is then caps-lock and shift - // cancel out. if not then caps-lock is ignored and shift - // is required. - if (needShift) { - // FIXME -- there should be some system call to test - // if a key is subject to case conversion. - if (tolower(code) != toupper(code)) { - log((CLOG_DEBUG2 "turn off shift")); - outMask &= ~KeyModifierShift; - } + // locally then it reverses the meaning of shift for keys that + // are subject to case conversion. + if ((outMask & KeyModifierCapsLock) != 0) { + if (tolower(code) != toupper(code)) { + log((CLOG_DEBUG2 "flip shift")); + outMask ^= KeyModifierShift; } } @@ -1096,13 +1048,13 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( bool m_isToggle; }; static const CModifierInfo s_modifier[] = { - { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, - { KeyModifierControl, VK_LCONTROL, VK_RCONTROL,false }, - { KeyModifierAlt, VK_LMENU, VK_RMENU, false }, - { KeyModifierMeta, VK_LWIN, VK_RWIN, false }, - { KeyModifierCapsLock, VK_CAPITAL, 0, true }, - { KeyModifierNumLock, VK_NUMLOCK, 0, true }, - { KeyModifierScrollLock, VK_SCROLL, 0, true } + { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, + { KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false }, + { KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false }, + { KeyModifierMeta, VK_LWIN | 0x100, VK_RWIN | 0x100, false }, + { KeyModifierCapsLock, VK_CAPITAL, 0, true }, + { KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true }, + { KeyModifierScrollLock,VK_SCROLL, 0, true } }; static const unsigned int s_numModifiers = sizeof(s_modifier) / sizeof(s_modifier[0]); @@ -1203,7 +1155,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( } else { UINT key = s_modifier[i].m_virtualKey; - if ((m_keys[key] & 0x80) != 0) { + if ((m_keys[key & 0xff] & 0x80) != 0) { keystroke.m_virtualKey = key; keystroke.m_press = false; keystroke.m_repeat = false; @@ -1212,7 +1164,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( undo.push_back(keystroke); } key = s_modifier[i].m_virtualKey2; - if (key != 0 && (m_keys[key] & 0x80) != 0) { + if (key != 0 && (m_keys[key & 0xff] & 0x80) != 0) { keystroke.m_virtualKey = key; keystroke.m_press = false; keystroke.m_repeat = false; @@ -1227,23 +1179,23 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( } // add the key event + keystroke.m_virtualKey = virtualKey; + if (isExtended) + keystroke.m_virtualKey |= 0x100; switch (action) { case kPress: - keystroke.m_virtualKey = virtualKey; keystroke.m_press = true; keystroke.m_repeat = false; keys.push_back(keystroke); break; case kRelease: - keystroke.m_virtualKey = virtualKey; keystroke.m_press = false; keystroke.m_repeat = false; keys.push_back(keystroke); break; case kRepeat: - keystroke.m_virtualKey = virtualKey; keystroke.m_press = true; keystroke.m_repeat = true; keys.push_back(keystroke); @@ -1276,13 +1228,13 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // can't reset bit until all keys that set it are released. // scan those keys to see if any are pressed. bool down = false; - if (virtualKey != modifier.m_virtualKey && - (m_keys[modifier.m_virtualKey] & 0x80) != 0) { + if (virtualKey != (modifier.m_virtualKey & 0xff) && + (m_keys[modifier.m_virtualKey & 0xff] & 0x80) != 0) { down = true; } if (modifier.m_virtualKey2 != 0 && - virtualKey != modifier.m_virtualKey2 && - (m_keys[modifier.m_virtualKey2] & 0x80) != 0) { + virtualKey != (modifier.m_virtualKey2 & 0xff) && + (m_keys[modifier.m_virtualKey2 & 0xff] & 0x80) != 0) { down = true; } if (!down) @@ -1310,9 +1262,7 @@ void CMSWindowsSecondaryScreen::doKeystrokes( for (; count > 0; --count) { // send repeating events for (k = start; k != keys.end() && k->m_repeat; ++k) { - const UINT code = MapVirtualKey(k->m_virtualKey, 0); - keybd_event(k->m_virtualKey, code, - k->m_press ? 0 : KEYEVENTF_KEYUP, 0); + sendKeyEvent(k->m_virtualKey, k->m_press); } } @@ -1321,9 +1271,7 @@ void CMSWindowsSecondaryScreen::doKeystrokes( } else { // send event - const UINT code = MapVirtualKey(k->m_virtualKey, 0); - keybd_event(k->m_virtualKey, code, - k->m_press ? 0 : KEYEVENTF_KEYUP, 0); + sendKeyEvent(k->m_virtualKey, k->m_press); // next key ++k; @@ -1333,22 +1281,25 @@ void CMSWindowsSecondaryScreen::doKeystrokes( void CMSWindowsSecondaryScreen::updateKeys() { - // GetKeyboardKeys() doesn't seem to do the expected thing + // clear key state memset(m_keys, 0, sizeof(m_keys)); - m_keys[VK_LSHIFT] = GetKeyState(VK_LSHIFT); - m_keys[VK_RSHIFT] = GetKeyState(VK_RSHIFT); - m_keys[VK_SHIFT] = GetKeyState(VK_SHIFT); - m_keys[VK_LCONTROL] = GetKeyState(VK_LCONTROL); - m_keys[VK_RCONTROL] = GetKeyState(VK_RCONTROL); - m_keys[VK_CONTROL] = GetKeyState(VK_CONTROL); - m_keys[VK_LMENU] = GetKeyState(VK_LMENU); - m_keys[VK_RMENU] = GetKeyState(VK_RMENU); - m_keys[VK_MENU] = GetKeyState(VK_MENU); - m_keys[VK_LWIN] = GetKeyState(VK_LWIN); - m_keys[VK_RWIN] = GetKeyState(VK_RWIN); - m_keys[VK_CAPITAL] = GetKeyState(VK_CAPITAL); - m_keys[VK_NUMLOCK] = GetKeyState(VK_NUMLOCK); - m_keys[VK_SCROLL] = GetKeyState(VK_SCROLL); + + // we only care about the modifier key states + m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); + m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); + m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); + m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL)); + m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL)); + m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL)); + m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU)); + m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU)); + m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU)); + m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN)); + m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN)); + m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS)); + m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); + m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); + m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); } void CMSWindowsSecondaryScreen::updateModifiers() @@ -1376,11 +1327,97 @@ void CMSWindowsSecondaryScreen::toggleKey( UINT virtualKey, KeyModifierMask mask) { // send key events to simulate a press and release - const UINT code = MapVirtualKey(virtualKey, 0); - keybd_event(virtualKey, code, 0, 0); - keybd_event(virtualKey, code, KEYEVENTF_KEYUP, 0); + sendKeyEvent(virtualKey, true); + sendKeyEvent(virtualKey, false); // toggle shadow state - m_mask ^= mask; - m_keys[virtualKey] ^= 0x01; + m_mask ^= mask; + m_keys[virtualKey & 0xff] ^= 0x01; +} + +UINT CMSWindowsSecondaryScreen::virtualKeyToScanCode( + UINT& virtualKey) +{ + // try mapping given virtual key + UINT code = MapVirtualKey(virtualKey & 0xff, 0); + if (code != 0) + return code; + + // no dice. if the virtual key distinguishes between left/right + // then try the one that doesn't distinguish sides. windows (or + // keyboard drivers) are inconsistent in their treatment of these + // virtual keys. the following behaviors have been observed: + // + // win2k (gateway desktop): + // MapVirtualKey(vk, 0): + // VK_SHIFT == VK_LSHIFT != VK_RSHIFT + // VK_CONTROL == VK_LCONTROL == VK_RCONTROL + // VK_MENU == VK_LMENU == VK_RMENU + // MapVirtualKey(sc, 3): + // VK_LSHIFT and VK_RSHIFT mapped independently + // VK_LCONTROL is mapped but not VK_RCONTROL + // VK_LMENU is mapped but not VK_RMENU + // + // win me (sony vaio laptop): + // MapVirtualKey(vk, 0): + // VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped + // VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped + // VK_MENU mapped; VK_LMENU, VK_RMENU not mapped + // MapVirtualKey(sc, 3): + // all scan codes unmapped (function apparently unimplemented) + switch (virtualKey & 0xff) { + case VK_LSHIFT: + case VK_RSHIFT: + virtualKey = VK_SHIFT; + return MapVirtualKey(VK_SHIFT, 0); + + case VK_LCONTROL: + case VK_RCONTROL: + virtualKey = VK_CONTROL; + return MapVirtualKey(VK_CONTROL, 0); + + case VK_LMENU: + case VK_RMENU: + virtualKey = VK_MENU; + return MapVirtualKey(VK_MENU, 0); + + default: + return 0; + } +} + +bool CMSWindowsSecondaryScreen::isExtendedKey( + UINT virtualKey) +{ + // see if we've already encoded the extended flag + if ((virtualKey & 0x100) != 0) + return true; + + // check known virtual keys + switch (virtualKey & 0xff) { + case VK_NUMLOCK: + case VK_RCONTROL: + case VK_RMENU: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + return true; + + default: + return false; + } +} + +void CMSWindowsSecondaryScreen::sendKeyEvent( + UINT virtualKey, bool press) +{ + DWORD flags = 0; + if (isExtendedKey(virtualKey)) + flags |= KEYEVENTF_EXTENDEDKEY; + if (!press) + flags |= KEYEVENTF_KEYUP; + const UINT code = virtualKeyToScanCode(virtualKey); + keybd_event(static_cast(virtualKey & 0xff), + static_cast(code), flags, 0); + log((CLOG_DEBUG2 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : ""))); } diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index d05f44ab..8149ea92 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -57,6 +57,9 @@ private: void updateKeys(); void updateModifiers(); void toggleKey(UINT virtualKey, KeyModifierMask mask); + UINT virtualKeyToScanCode(UINT& virtualKey); + bool isExtendedKey(UINT virtualKey); + void sendKeyEvent(UINT virtualKey, bool press); private: CClient* m_client; @@ -64,6 +67,9 @@ private: HWND m_nextClipboardWindow; HWND m_clipboardOwner; + // thread id of the event loop thread + DWORD m_threadID; + // virtual key states BYTE m_keys[256]; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index f5f67872..5d3a36b6 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -31,47 +31,15 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() assert(m_hookLibrary == NULL); } -static CString s_log; -static CString s_logMore; -static HWND s_debug = NULL; -static HWND s_debugLog = NULL; -static DWORD s_thread = 0; -static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - return TRUE; - - case WM_CLOSE: - PostQuitMessage(0); - return TRUE; - - case WM_APP: - if (!s_logMore.empty()) { - if (s_log.size() > 20000) - s_log = s_logMore; - else - s_log += s_logMore; - s_logMore = ""; - SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)s_log.c_str()); - SendMessage(s_debugLog, EM_SETSEL, s_log.size(), s_log.size()); - SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0); - } - return TRUE; - } - return FALSE; -} -static void debugOutput(const char* msg) -{ - s_logMore += msg; - PostMessage(s_debug, WM_APP, 0, 0); -} - void CMSWindowsPrimaryScreen::run() { -CLog::setOutputter(&debugOutput); + // change our priority + CThread::getCurrentThread().setPriority(-3); + + // run event loop + log((CLOG_INFO "entering event loop")); doRun(); -CLog::setOutputter(NULL); + log((CLOG_INFO "exiting event loop")); } void CMSWindowsPrimaryScreen::stop() @@ -87,12 +55,12 @@ void CMSWindowsPrimaryScreen::open(CServer* server) // set the server m_server = server; - // get keyboard state - updateKeys(); - // open the display openDisplay(); + // get keyboard state + updateKeys(); + // enter the screen doEnter(); } @@ -113,7 +81,6 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) log((CLOG_INFO "entering primary at %d,%d", x, y)); assert(m_active == true); - // do non-warp enter stuff doEnter(); // warp to requested location @@ -122,12 +89,8 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) void CMSWindowsPrimaryScreen::doEnter() { - // release the capture - ReleaseCapture(); - - // hide our window and restore the foreground window - SetForegroundWindow(m_lastActive); - ShowWindow(m_window, SW_HIDE); + // not active anymore + m_active = false; // set the zones that should cause a jump SInt32 w, h; @@ -136,11 +99,23 @@ void CMSWindowsPrimaryScreen::doEnter() m_hookLibrary, "setZone"); setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize()); + // restore the active window and hide our window. we can only set + // the active window for another thread if we first attach our input + // to that thread. + ReleaseCapture(); + if (m_lastActiveWindow != NULL) { + DWORD myThread = GetCurrentThreadId(); + if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { + // FIXME -- shouldn't raise window if X-Mouse is enabled + // but i have no idea how to do that or check if enabled. + SetActiveWindow(m_lastActiveWindow); + AttachThreadInput(myThread, m_lastActiveThread, FALSE); + } + } + ShowWindow(m_window, SW_HIDE); + // all messages prior to now are invalid nextMark(); - - // not active anymore - m_active = false; } void CMSWindowsPrimaryScreen::leave() @@ -148,30 +123,50 @@ void CMSWindowsPrimaryScreen::leave() log((CLOG_INFO "leaving primary")); assert(m_active == false); + // do non-warp enter stuff + // get state of keys as we leave + updateKeys(); + // all messages prior to now are invalid nextMark(); - // remember the active window before we leave - m_lastActive = GetForegroundWindow(); + // remember the active window before we leave. GetActiveWindow() + // will only return the active window for the thread's queue (i.e. + // our app) but we need the globally active window. get that by + // attaching input to the foreground window's thread then calling + // GetActiveWindow() and then detaching our input. + m_lastActiveWindow = NULL; + m_lastForegroundWindow = GetForegroundWindow(); + m_lastActiveThread = GetWindowThreadProcessId( + m_lastForegroundWindow, NULL); + if (m_lastActiveThread != 0) { + DWORD myThread = GetCurrentThreadId(); + if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { + m_lastActiveWindow = GetActiveWindow(); + AttachThreadInput(myThread, m_lastActiveThread, FALSE); + } + } - // show our window and put it in the foreground + // show our window ShowWindow(m_window, SW_SHOW); - SetForegroundWindow(m_window); - - // capture the cursor so we don't lose keyboard input - SetCapture(m_window); // relay all mouse and keyboard events SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress( m_hookLibrary, "setRelay"); setRelay(); - // warp the mouse to the center of the screen - SInt32 w, h; - getScreenSize(&w, &h); - warpCursor(w >> 1, h >> 1); + // get keyboard input and capture mouse + SetActiveWindow(m_window); + SetFocus(m_window); + SetCapture(m_window); - // warp is also invalid + // warp the mouse to the center of the screen + getScreenSize(&m_xCenter, &m_yCenter); + m_xCenter >>= 1; + m_yCenter >>= 1; + warpCursor(m_xCenter, m_yCenter); + + // discard messages until after the warp nextMark(); // local client now active @@ -205,12 +200,8 @@ void CMSWindowsPrimaryScreen::leave() void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { - SInt32 w, h; - getScreenSize(&w, &h); - mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65535.99 * x) / (w - 1)), - (DWORD)((65535.99 * y) / (h - 1)), - 0, 0); + // set the cursor position without generating an event + SetCursorPos(x, y); } void CMSWindowsPrimaryScreen::setClipboard( @@ -254,7 +245,7 @@ void CMSWindowsPrimaryScreen::getClipboard( KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { - KeyModifierMask mask; + KeyModifierMask mask = 0; if ((m_keys[VK_CAPITAL] & 0x01) != 0) mask |= KeyModifierCapsLock; if ((m_keys[VK_NUMLOCK] & 0x01) != 0) @@ -264,30 +255,30 @@ KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const return mask; } -#include "resource.h" // FIXME - void CMSWindowsPrimaryScreen::onOpenDisplay() { assert(m_window == NULL); assert(m_server != NULL); -// create debug dialog -s_thread = GetCurrentThreadId();; -s_debug = CreateDialog(getInstance(), MAKEINTRESOURCE(IDD_SYNERGY), NULL, &debugProc); -s_debugLog = ::GetDlgItem(s_debug, IDC_LOG); -CLog::setOutputter(&debugOutput); -ShowWindow(s_debug, SW_SHOWNORMAL); - // initialize clipboard owner to current owner. we don't want // to take ownership of the clipboard just by starting up. m_clipboardOwner = GetClipboardOwner(); + // get screen size + // note -- we use a fullscreen window to grab input. it should + // be possible to use a 1x1 window but i've run into problems + // with losing keyboard input (focus?) in that case. + // unfortunately, hiding the full screen window causes all other + // windows to redraw. + SInt32 w, h; + getScreenSize(&w, &h); + // create the window m_window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, (LPCTSTR)getClass(), "Synergy", WS_POPUP, - 0, 0, 1, 1, NULL, NULL, + 0, 0, w, h, NULL, NULL, getInstance(), NULL); assert(m_window != NULL); @@ -307,6 +298,7 @@ ShowWindow(s_debug, SW_SHOWNORMAL); } } if (!hooked) { + log((CLOG_ERR "failed to install hooks")); ChangeClipboardChain(m_window, m_nextClipboardWindow); m_nextClipboardWindow = NULL; DestroyWindow(m_window); @@ -342,19 +334,10 @@ void CMSWindowsPrimaryScreen::onCloseDisplay() // destroy window DestroyWindow(m_window); m_window = NULL; - -CLog::setOutputter(NULL); -DestroyWindow(s_debug); -s_debug = NULL; -s_thread = 0; } bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) { -if (IsDialogMessage(s_debug, msg)) { - return true; -} - // handle event switch (msg->message) { case SYNERGY_MSG_MARK: @@ -391,7 +374,9 @@ if (IsDialogMessage(s_debug, msg)) { updateKey(msg->wParam, false); } } - + else { + log((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", msg->wParam, msg->lParam)); + } } return true; @@ -431,29 +416,16 @@ if (IsDialogMessage(s_debug, msg)) { m_server->onMouseMovePrimary(x, y); } else { + // get mouse deltas + x -= m_xCenter; + y -= m_yCenter; log((CLOG_DEBUG2 "event: active move %d,%d", x, y)); - // get screen size - SInt32 w, h; - getScreenSize(&w, &h); + // warp mouse back to center + warpCursor(m_xCenter, m_yCenter); - // get center pixel - w >>= 1; - h >>= 1; - - // ignore and discard message if motion is to center of - // screen. those are caused by us warping the mouse. - if (x != w || y != h) { - // get mouse deltas - x -= w; - y -= h; - - // warp mouse back to center - warpCursor(w, h); - - // send motion - m_server->onMouseMoveSecondary(x, y); - } + // send motion + m_server->onMouseMoveSecondary(x, y); } } return true; @@ -467,7 +439,7 @@ LRESULT CMSWindowsPrimaryScreen::onEvent( WPARAM wParam, LPARAM lParam) { switch (msg) { - // FIXME -- handle display changes + // FIXME -- handle display changes (and resize full-screen window) case WM_PAINT: ValidateRect(hwnd, NULL); return 0; @@ -500,6 +472,7 @@ LRESULT CMSWindowsPrimaryScreen::onEvent( SendMessage(m_nextClipboardWindow, msg, wParam, lParam); return 0; } + return DefWindowProc(hwnd, msg, wParam, lParam); } @@ -808,6 +781,7 @@ KeyID CMSWindowsPrimaryScreen::mapKey( if ((m_keys[VK_SCROLL] & 0x01) != 0) mask |= KeyModifierScrollLock; *maskOut = mask; + log((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask)); // get the scan code UINT scanCode = static_cast((info & 0xff0000) >> 16); @@ -827,6 +801,10 @@ KeyID CMSWindowsPrimaryScreen::mapKey( else if (vkCode >= VK_LWIN && vkCode <= VK_APPS) vkCode2 = vkCode; + // if MapVirtualKey failed then use original virtual key + else if (vkCode2 == 0) + vkCode2 = vkCode; + // sadly, win32 will not distinguish between the left and right // control and alt keys using the above function. however, we // can check for those: if bit 24 of info is set then the key @@ -834,10 +812,12 @@ KeyID CMSWindowsPrimaryScreen::mapKey( // keys. if ((info & 0x1000000) != 0) { switch (vkCode2) { + case VK_CONTROL: case VK_LCONTROL: vkCode2 = VK_RCONTROL; break; + case VK_MENU: case VK_LMENU: vkCode2 = VK_RMENU; break; @@ -898,7 +878,7 @@ KeyID CMSWindowsPrimaryScreen::mapKey( else if (result == 2) { // get the scan code of the dead key and the shift state // required to generate it. - vkCode = VkKeyScan(ascii & 0x00ff); + vkCode = VkKeyScan(static_cast(ascii & 0x00ff)); // set shift state required to generate key BYTE keys[256]; @@ -948,25 +928,29 @@ ButtonID CMSWindowsPrimaryScreen::mapButton( void CMSWindowsPrimaryScreen::updateKeys() { + // not using GetKeyboardState() because that doesn't seem to give + // up-to-date results. i don't know why that is or why GetKeyState() + // should give different results. + // clear key state memset(m_keys, 0, sizeof(m_keys)); // we only care about the modifier key states - m_keys[VK_LSHIFT] = GetKeyState(VK_LSHIFT); - m_keys[VK_RSHIFT] = GetKeyState(VK_RSHIFT); - m_keys[VK_SHIFT] = GetKeyState(VK_SHIFT); - m_keys[VK_LCONTROL] = GetKeyState(VK_LCONTROL); - m_keys[VK_RCONTROL] = GetKeyState(VK_RCONTROL); - m_keys[VK_CONTROL] = GetKeyState(VK_CONTROL); - m_keys[VK_LMENU] = GetKeyState(VK_LMENU); - m_keys[VK_RMENU] = GetKeyState(VK_RMENU); - m_keys[VK_MENU] = GetKeyState(VK_MENU); - m_keys[VK_LWIN] = GetKeyState(VK_LWIN); - m_keys[VK_RWIN] = GetKeyState(VK_RWIN); - m_keys[VK_APPS] = GetKeyState(VK_APPS); - m_keys[VK_CAPITAL] = GetKeyState(VK_CAPITAL); - m_keys[VK_NUMLOCK] = GetKeyState(VK_NUMLOCK); - m_keys[VK_SCROLL] = GetKeyState(VK_SCROLL); + m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); + m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); + m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); + m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL)); + m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL)); + m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL)); + m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU)); + m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU)); + m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU)); + m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN)); + m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN)); + m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS)); + m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); + m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); + m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); } void CMSWindowsPrimaryScreen::updateKey( diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index f24d51a2..35bb7167 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -52,11 +52,14 @@ private: HWND m_window; HWND m_nextClipboardWindow; HWND m_clipboardOwner; - HWND m_lastActive; + HWND m_lastForegroundWindow; + HWND m_lastActiveWindow; + DWORD m_lastActiveThread; HINSTANCE m_hookLibrary; UInt32 m_mark; UInt32 m_markReceived; BYTE m_keys[256]; + SInt32 m_xCenter, m_yCenter; }; #endif From 685bcfa05fe5f5bb323d4974b1672d84ba9ac029 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 17:02:58 +0000 Subject: [PATCH 091/807] fixed incorrect for-loop over directions conditional. --- server/CScreenMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CScreenMap.cpp b/server/CScreenMap.cpp index 39d7d7f2..750427f6 100644 --- a/server/CScreenMap.cpp +++ b/server/CScreenMap.cpp @@ -36,7 +36,7 @@ void CScreenMap::removeScreen(const CString& name) // disconnect for (index = m_map.begin(); index != m_map.end(); ++index) { CCell& cell = index->second; - for (SInt32 i = 0; kLastDirection - kFirstDirection; ++i) + for (SInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) if (cell.m_neighbor[i] == name) { cell.m_neighbor[i].erase(); } From 9e161163b006561cd5f24c88269daadb1e06c913 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 17:05:26 +0000 Subject: [PATCH 092/807] now letting some key events filter through. this allows the keyboard lights to track toggle changes. however, it also seems to let through keyboard events that shouldn't get through. --- server/CSynergyHook.cpp | 65 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 5609f44a..7c7dc293 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -15,6 +15,7 @@ static DWORD g_process = NULL; static HWND g_hwnd = NULL; static HHOOK g_keyboard = NULL; static HHOOK g_mouse = NULL; +static HHOOK g_cbt = NULL; static bool g_relay = false; static SInt32 g_zoneSize = 0; static UInt32 g_zoneSides = 0; @@ -57,8 +58,28 @@ static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { if (g_relay) { - PostMessage(g_hwnd, SYNERGY_MSG_KEY, wParam, lParam); - return 1; + if (code == HC_ACTION) { + // forward message to our window + PostMessage(g_hwnd, SYNERGY_MSG_KEY, wParam, lParam); + + // if the active window isn't our window then make it + // active. + const bool wrongFocus = (GetActiveWindow() != g_hwnd); + if (wrongFocus) { + SetForegroundWindow(g_hwnd); + } + + // let non-system keyboard messages through to our window. + // this allows DefWindowProc() to do normal processing. + // for most keys that means do nothing. for toggle keys + // it means updating the thread's toggle state. discard + // system messages (i.e. keys pressed when alt is down) to + // prevent unexpected or undesired processing. also + // discard messages if not destined for our window + if (wrongFocus || (lParam & 0x20000000lu) != 0) { + return 1; + } + } } } @@ -134,6 +155,25 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_mouse, code, wParam, lParam); } +static LRESULT CALLBACK cbtHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + if (g_relay) { + switch (code) { + case HCBT_ACTIVATE: + case HCBT_SETFOCUS: + // discard unless activating our window + if (reinterpret_cast(wParam) != g_hwnd) { + return 1; + } + break; + } + } + } + + return CallNextHookEx(g_cbt, code, wParam, lParam); +} + // // external functions @@ -149,7 +189,7 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) } else if (reason == DLL_PROCESS_DETACH) { if (g_process == GetCurrentProcessId()) { - if (g_keyboard != NULL || g_mouse != NULL) { + if (g_keyboard != NULL || g_mouse != NULL || g_cbt != NULL) { uninstall(); } g_process = NULL; @@ -165,6 +205,7 @@ int install(HWND hwnd) assert(g_hinstance != NULL); assert(g_keyboard == NULL); assert(g_mouse == NULL); + assert(g_cbt == NULL); // save window g_hwnd = hwnd; @@ -201,6 +242,21 @@ int install(HWND hwnd) return 0; } + // install CBT hook + g_cbt = SetWindowsHookEx(WH_CBT, + &cbtHook, + g_hinstance, + 0); + if (g_cbt == NULL) { + // uninstall keyboard and mouse hooks before failing + UnhookWindowsHookEx(g_keyboard); + UnhookWindowsHookEx(g_mouse); + g_keyboard = NULL; + g_mouse = NULL; + g_hwnd = NULL; + return 0; + } + return 1; } @@ -208,12 +264,15 @@ int uninstall(void) { assert(g_keyboard != NULL); assert(g_mouse != NULL); + assert(g_cbt != NULL); // uninstall hooks UnhookWindowsHookEx(g_keyboard); UnhookWindowsHookEx(g_mouse); + UnhookWindowsHookEx(g_cbt); g_keyboard = NULL; g_mouse = NULL; + g_cbt = NULL; g_hwnd = NULL; // show the cursor From 8df02380e56bd0fecfe6e70eb7eb407b00d486ee Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 17:08:37 +0000 Subject: [PATCH 093/807] removed unnecessary call in screen class, added logging calls in clipboard class, and added another cast in protocol util to avoid warning on win32. --- synergy/CMSWindowsClipboard.cpp | 6 +- synergy/CMSWindowsScreen.cpp | 98 +-------------------------------- synergy/CMSWindowsScreen.h | 3 - synergy/CProtocolUtil.cpp | 3 +- 4 files changed, 8 insertions(+), 102 deletions(-) diff --git a/synergy/CMSWindowsClipboard.cpp b/synergy/CMSWindowsClipboard.cpp index f1b2060b..006ebc75 100644 --- a/synergy/CMSWindowsClipboard.cpp +++ b/synergy/CMSWindowsClipboard.cpp @@ -20,7 +20,7 @@ CMSWindowsClipboard::~CMSWindowsClipboard() bool CMSWindowsClipboard::open(Time time) { - log((CLOG_INFO "open clipboard")); + log((CLOG_DEBUG "open clipboard")); if (!OpenClipboard(m_window)) { log((CLOG_WARN "failed to open clipboard")); @@ -42,14 +42,14 @@ bool CMSWindowsClipboard::open(Time time) void CMSWindowsClipboard::close() { - log((CLOG_INFO "close clipboard")); + log((CLOG_DEBUG "close clipboard")); CloseClipboard(); } void CMSWindowsClipboard::add( EFormat format, const CString& data) { - log((CLOG_INFO "add %d bytes to clipboard format: %d", data.size(), format)); + log((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); if (!OpenClipboard(m_window)) { log((CLOG_WARN "failed to open clipboard")); diff --git a/synergy/CMSWindowsScreen.cpp b/synergy/CMSWindowsScreen.cpp index e0fbbdd6..49860496 100644 --- a/synergy/CMSWindowsScreen.cpp +++ b/synergy/CMSWindowsScreen.cpp @@ -37,7 +37,10 @@ void CMSWindowsScreen::init(HINSTANCE instance) void CMSWindowsScreen::doRun() { + // save thread id for posting quit message m_thread = GetCurrentThreadId(); + + // event loop for (;;) { // wait for and get the next event MSG msg; @@ -151,101 +154,6 @@ void CMSWindowsScreen::getEvent(MSG* msg) const GetMessage(msg, NULL, 0, 0); } -void CMSWindowsScreen::getDisplayClipboard( - IClipboard* clipboard, - HWND requestor) const -{ -/* FIXME - assert(clipboard != NULL); - assert(requestor != None); - - // clear the clipboard object - clipboard->open(); - - // block others from using the display while we get the clipboard. - // in particular, this prevents the event thread from stealing the - // selection notify event we're expecting. - CLock lock(&m_mutex); - - // use PRIMARY selection as the "clipboard" - Atom selection = XA_PRIMARY; - - // ask the selection for all the formats it has. some owners return - // the TARGETS atom and some the ATOM atom when TARGETS is requested. - Atom format; - CString targets; - if (getDisplayClipboard(selection, m_atomTargets, - requestor, timestamp, &format, &targets) && - (format == m_atomTargets || format == XA_ATOM)) { - // get each target (that we can interpret). some owners return - // some targets multiple times in the list so don't try to get - // those multiple times. - const Atom* targetAtoms = reinterpret_cast(targets.data()); - const SInt32 numTargets = targets.size() / sizeof(Atom); - std::set clipboardFormats; - std::set targets; - log((CLOG_DEBUG "selection has %d targets", numTargets)); - for (SInt32 i = 0; i < numTargets; ++i) { - Atom format = targetAtoms[i]; - log((CLOG_DEBUG " source target %d", format)); - - // skip already handled targets - if (targets.count(format) > 0) { - log((CLOG_DEBUG " skipping handled target %d", format)); - continue; - } - - // mark this target as done - targets.insert(format); - - // determine the expected clipboard format - IClipboard::EFormat expectedFormat = getFormat(format); - - // if we can use the format and we haven't already retrieved - // it then get it - if (expectedFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG " no format for target", format)); - continue; - } - if (clipboardFormats.count(expectedFormat) > 0) { - log((CLOG_DEBUG " skipping handled format %d", expectedFormat)); - continue; - } - - CString data; - if (!getDisplayClipboard(selection, format, - requestor, timestamp, &format, &data)) { - log((CLOG_DEBUG " no data for target", format)); - continue; - } - - // use the actual format, not the expected - IClipboard::EFormat actualFormat = getFormat(format); - if (actualFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG " no format for target", format)); - continue; - } - if (clipboardFormats.count(actualFormat) > 0) { - log((CLOG_DEBUG " skipping handled format %d", actualFormat)); - continue; - } - - // add to clipboard and note we've done it - clipboard->add(actualFormat, data); - clipboardFormats.insert(actualFormat); - } - } - else { - // non-ICCCM conforming selection owner. try TEXT format. - // FIXME - log((CLOG_DEBUG "selection doesn't support TARGETS, format is %d", format)); - } - - // done with clipboard - clipboard->close(); -*/ -} - LRESULT CALLBACK CMSWindowsScreen::wndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) diff --git a/synergy/CMSWindowsScreen.h b/synergy/CMSWindowsScreen.h index 9e1590a8..a916beb3 100644 --- a/synergy/CMSWindowsScreen.h +++ b/synergy/CMSWindowsScreen.h @@ -46,9 +46,6 @@ protected: // wait for and get the next message. cancellable. void getEvent(MSG*) const; - // copy the clipboard contents to clipboard - void getDisplayClipboard(IClipboard* clipboard, HWND) const; - // called by doRun() to handle an event. return true to skip // event translation and dispatch. virtual bool onPreTranslate(MSG*) = 0; diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index 5c736efa..fb0543a7 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -84,8 +84,9 @@ void CProtocolUtil::readf(IInputStream* stream, case 2: // 2 byte integer *reinterpret_cast(v) = + static_cast( (static_cast(buffer[0]) << 8) | - static_cast(buffer[1]); + static_cast(buffer[1])); log((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; From 6697bd900e2890ee0eeeb3714dd43790e8c94d20 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 May 2002 17:09:08 +0000 Subject: [PATCH 094/807] checkpoint. --- notes | 253 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 155 insertions(+), 98 deletions(-) diff --git a/notes b/notes index b3386e0e..7c24a23a 100644 --- a/notes +++ b/notes @@ -2,10 +2,6 @@ CServer * must have connection thread in screen info map allows disconnect if screen map changes and screen is removed * put mutex locks wherever necessary (like accessing m_active) - * must handle disconnect of screen by warping cursor off of it - -CClient - * need methods for screen event handler to call as appropriate server client ------ ------ @@ -16,114 +12,175 @@ hangup if invalid query info --> <-- info (size) -... -enter (x,y) --> -clipboard data --> optional -mouse/key events --> optional -query clipboard --> optional - <-- clipboard data (cont.) -leave --> - -... -grab clipboard --> - -... (on clipboard ownership stolen) - <-- clipboard lost - -... (on screen resize) - <-- info (size) - -... (on screen saver, primary screen) -saver (on/off) --> - -... -quit --> - <-- close - ---- -primary screen - open - close - enter - leave - warp - clipboard (get/set) - screen saver (show/hide) - queue events with server (including screen saver activation) - -secondary screen - open - close - enter - leave - warp - synth mouse - synth key - clipboard (get/set) - screen saver (show/hide) - queue events with client (clipboard lost/changed, size change) - ---- -client: - open - close - wait: server messages, clipboard taken, screen resize, quit - -server: - accept - asynchronously accept new clients - config - asynchronously accept and handle config message (via HTTP) - primary - asynchronously handle primary screen events - comm - send/recv messages to/from clients - --- win32: - double click support need to support window stations login screen on NT is a separate window station handle display changes --- -not sending VK_?WIN and VK_APPS. possibly hotkeys being stolen. - -VkKeyScan() doesn't get proper shift state unless we map shift -(etc?) to VK_SHIFT not VK_LSHIFT or VK_RSHIFT. - not handling international characters -X11 key translation (server) - handle compose key? - * don't send compose, don't send dead keys, send composed key? - * send all keys, let client do compose - --- -translate keys to keysyms - shift state is embedded in keysym - alt/ctrl/meta are stored separately, also caps/num/scroll lock - shift state also stored separately - -Win32 to keysym: - VK code + shift -> keysym via table lookup -Win32 from keysym: - VK code and shift via table lookup - must synth shift key events to get correct character for key - don't allow leaving screen when any key is down - that includes shift keys + that includes shift keys and mouse buttons caps lock, etc must not be down but can be on -may need to handle "half-duplex" caps-lock on secondary screen +not distinguishing between caps lock and shift lock + In Caps Lock mode, the alphabetic keys send their uppercase (shifted) char- + acter when pressed alone. For Caps Lock mode, pressing and releasing the + lock key turns on the mode and pressing and releasing the lock key again + turns off the mode. In Shift Lock mode, all keys on the main keypad send + their shifted character when pressed alone. For Shift Lock mode, pressing + and releasing the Lock key turns on the mode, and pressing and releasing + either the Lock or the Shift key turns off the mode. --- -clipboard stuff: - PRIMARY -- the current selection, normally pastes with middle-mouse - CLIPBOARD -- set by cut/copy menu items, pasted via paste menu item -synergy must track and transfer both. however, only X clients need -the PRIMARY sent to them. +problems with CLIPBOARD and KDE and NEdit + KDE mail reader (others?) and NEdit never reply to selection request + perhaps a general Motif problem? + +currently sending all clipboards to all clients + some clients may not need all clipboards + add filtering mechanism + +must find app that correctly sets the CLIPBOARD selection for testing + synergy must note changes to one or both selections + seems to be having trouble with noting CLIPBOARD after PRIMARY changes + +investigate klipper + KDE's clipboard manager? + it crashed once when server did (see below) --- -remove space pairs - not a good thing for formatting +got crash when opening netscape window after trying copy in KDE mail + BadWindow in ChangeWindowAttributes + also got it when trying to transfer big clipboard from secondary + or just after transferring and perhaps trying to set locally + +--- +sometimes not sending correct clipboard + some kind of race condition i think + can select text then switch and get empty clipboard + switching back to owner screen then out gets correct clipboard + +--- +screen resize should report new mouse position + client could change it to keep mouse on screen + server will need to know new position + may need to ignore mouse events sent after resize but before server + knows about the change. + not handling screen resizing yet + +--- +not handling screen saver at all yet + +--- +slow leaving primary screen on X + possibly due to getting primary clipboards + possibly something else + could try asynchronously getting primary clipboard + race condition on secondary wrt pasting + +--- +win32 hook DLL sometimes not unloaded when server is killed? + will get assertion on restart as if DLL was still loaded. + can tell because all DLL copies share data segment. + should prevent multiple copies from loading (or initializing) + replace asserts with (multithread locked) check of variables + return 0 if busy + server must handle failure + try to figure out why it's not unloading + +--- +test: + X primary + ctrl+alt+keypad_add, ctrl+alt_keypad_minus -> eaten by primary X server + ctrl+alt+backspace probably eaten by primary X server if so configured + ctrl+alt+delete probably eaten locally + probably underneath primary X server too + Win32 desktop -> X laptop + eaten global hot keys: + ctrl+alt+delete -> task manager (or logon or reboot) + alt+[shift+]tab -> cycle through tasks + alt+[shift+]esc -> cycle windows + ctrl+esc -> start menu + windows+R -> run dialog + windows+M -> minimize all + windows+shift+M -> minimize all + windows+F1 -> help + windows+E -> explorer + windows+F -> find files + windows+ctrl+F -> find computer + windows+tab -> cycle through taskbar buttons + windows+break -> system properties + not eaten: + alt+space -> system menu + shift+F10 -> context menu + app key -> context menu + misc: + accessibility shortcuts: not eaten but dialogs appear anyway + X laptop -> Win32 desktop + check: + shift+tab -> okay + alt+[shift+]tab -> okay + alt+[shift+]esc -> okay + ctrl+esc -> start menu -> okay + ctrl+esc, esc, shift+F10 -> toolbar context menu -> okay + F10 -> activate menu in explorer -> okay + shift+F10 -> context menu -> okay + alt+space -> context menu -> okay + alt+hyphen -> MDI window menu -> okay + alt+letter -> open corresponding menu -> okay + alt+F6 -> switch between windows in single program -> okay + shift+insert CD-ROM -> bypass auto-run + check accessibility shortcuts -> does not work + check accessibility features + sticky keys -> no + filter keys -> no + toggle keys -> no + mouse keys -> no + high contrast -> no + X desktop -> win32 + shift, ctrl, alt not working + check: + shift+tab + windows+R -> run dialog + windows+M -> minimize all + windows+shift+M -> minimize all + windows+F1 -> help + windows+E -> explorer + windows+F -> find files + windows+ctrl+F -> find computer + windows+tab -> cycle through taskbar buttons + windows+break -> system properties + app key -> context menu + Win32 -> Win32 + check accessibility shortcuts work on secondary + check accessibility features work on secondary + +--- +server asserts if two clients with same name try to connect + should simply refuse second client +also not reaping disconnects very fast + investigate slowness + +--- +replace all structs with classes + +--- +Accessibility Shortcuts + + Key Result + ------------------------------------------------------------------- + Tap SHIFT 5 times Toggles StickyKeys on and off. + + Press down and hold the right Toggles FilterKeys on and off. + SHIFT key for 8 seconds + + Press down and hold the NUM LOCK Toggles ToggleKeys on and off. + key for 5 seconds + + Left ALT+left SHIFT+NUM LOCK Toggles MouseKeys on and off. + + Left ALT+left SHIFT+PRINT SCREEN Toggles High Contrast on and off. From 6611ea871e1099e77db0015f1773563a7833d088 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 23 May 2002 14:04:35 +0000 Subject: [PATCH 095/807] changed structs to classes. there should be no more structs now. --- net/CNetwork.h | 3 ++- synergy/CXWindowsScreen.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/net/CNetwork.h b/net/CNetwork.h index 8d6e9842..696973da 100644 --- a/net/CNetwork.h +++ b/net/CNetwork.h @@ -38,7 +38,8 @@ public: typedef struct sockaddr Address; typedef int AddressLength; typedef BOOL TCPNoDelayType; - struct PollEntry { + class PollEntry { + public: Socket fd; short events; short revents; diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 0f81414a..b02fd264 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -105,12 +105,12 @@ protected: virtual long getEventMask(Window) const = 0; private: - struct CPropertyNotifyInfo { + class CPropertyNotifyInfo { public: Window m_window; Atom m_property; }; - struct CClipboardRequest { + class CClipboardRequest { public: CString m_data; UInt32 m_sent; From 995771eec1b40fe4033776583a848d4c87cfcf33 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 23 May 2002 14:04:43 +0000 Subject: [PATCH 096/807] checkpoint --- notes | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/notes b/notes index 7c24a23a..d18b39d9 100644 --- a/notes +++ b/notes @@ -107,7 +107,7 @@ test: ctrl+esc -> start menu windows+R -> run dialog windows+M -> minimize all - windows+shift+M -> minimize all + windows+shift+M -> undo minimize all windows+F1 -> help windows+E -> explorer windows+F -> find files @@ -142,7 +142,6 @@ test: mouse keys -> no high contrast -> no X desktop -> win32 - shift, ctrl, alt not working check: shift+tab windows+R -> run dialog @@ -156,9 +155,41 @@ test: windows+break -> system properties app key -> context menu Win32 -> Win32 + alt+F6 -> eaten (isabel to audrey, but not audrey to isabel) check accessibility shortcuts work on secondary check accessibility features work on secondary +--- +audrey->isabel + num-lock light on isabel doesn't respond to generated events. + seems to be linked to something lower-level. however, keytest + does report num-lock keyboard state is being toggled. + + other toggle lights don't always respond immediately + usually go on/off when active window changes though + +--- +avoid fullscreen transparent window + using CBT hook to discard activation/focus messages + but it's not being called when we lose keyboard input + also, we sometimes don't get the keyboard input to start with + +--- +keyboard hook problems + now passing keyboard events through in most cases + this fixes problem on isabel where toggle lights don't toggle + and num-lock behaved incorrectly (generated wrong keys). + seems the DefWindowProc() needed to process the keys + unfortunately, keys sometimes leak into wrong app. seems related + to times when we don't get the keyboard input. key events are + not delivered to app on primary until cursor returns to primary. + no idea how that's possible. + current have some code to check active window and reset it in + keyboard hook. that code doesn't work. + seem to be having problems with windows key now, too. looks like + a down is sent but not the corresponding up so secondary system + thinks the key is always down. + --- server asserts if two clients with same name try to connect should simply refuse second client @@ -166,7 +197,38 @@ also not reaping disconnects very fast investigate slowness --- -replace all structs with classes +blown assert: + client crashed and primary jumped to primary + left over queued motion events then processed + caused call to CServer::onMouseMoveSecondary + m_protocol is NULL because we're on primary screen + must flush queued events when jumping to primary + check for other things to do when jumping to primary + +--- +not detecting disconnect when remote side crashes + is there some socket flag to help with this? + should catch signal when closing console window and close nicely + +--- +sending same clipboard repeatedly from win32 server + can we detect no change? + +--- +key events sent to console on win me (95/98?) are very slow + 1/4 to 1/2 second delay before key up is processed + key up log message is also delayed + console causing a system-wide delay? + +--- +adjust thread priorities on win32 + maybe remove changes altogether + currently isabel starts single-stepping mouse when dragging window + sometimes it goes normal speed but it's mostly very slow + even a very high priority doesn't seem to help + haven't tried real-time priority + a very high priority on client fixes delay when typeing into console + is it the output console causing the slowness? --- Accessibility Shortcuts @@ -184,3 +246,29 @@ Accessibility Shortcuts Left ALT+left SHIFT+NUM LOCK Toggles MouseKeys on and off. Left ALT+left SHIFT+PRINT SCREEN Toggles High Contrast on and off. + +--- +disable ctrl+alt+del info: + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0) + fools system into thinking screen saver is running + that disables ctrl+alt+del, alt+tab, ctrl+esc + dunno if keystrokes are queued + may be limited to win 95/98/me + win nt sp 3 and above: + low-level keyboard hook is called before ctrl+esc, alt+tab, alt+esc + use that to capture and disable + seems that low-level hook is notified of ctrl+alt+del but can't stop it + win nt sp 2 and below + ctrl+esc can be disabled by replacing task manager (dunno how) + RegisterHotKey() can be used to catch alt+tab and alt+esc + only while app is running, of course + win nt + keyboard filter driver can capture keys (see win NT DDK) + http://216.239.51.100/search?q=cache:q-f03UHhFMMC:www.thescarms.com/VBasic/StopReBoot.asp+alt%2Btab+disable&hl=en + some info on changing keyboard scan code mapping + can be used to disable keys but looks like reboot is required + +--- +Q179905 + suggestion for bypassing hook code when called by debugger. + may prevent system from getting locked up when debugging. From 13eee1423279266f88f8315a683674e7fb7b6185 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 23 May 2002 14:56:03 +0000 Subject: [PATCH 097/807] server no longer asserts when a client connects with a name that's already in use by another client. also added reporting of errors from the server to clients so clients can report meaningful messages to users. --- client/CClient.cpp | 30 +++++++++ client/CClient.h | 3 + server/CServer.cpp | 120 ++++++++++++++++++++-------------- server/CServerProtocol.cpp | 9 +-- server/CServerProtocol1_0.cpp | 2 +- synergy/ProtocolTypes.h | 11 +++- synergy/XSynergy.cpp | 22 ++++++- synergy/XSynergy.h | 19 ++++++ 8 files changed, 159 insertions(+), 57 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 3f0bdd40..c3d44cfd 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -269,6 +269,18 @@ void CClient::runSession(void*) log((CLOG_DEBUG1 "recv close")); break; } + else if (memcmp(code, kMsgEIncompatible, 4) == 0) { + onErrorIncompatible(); + break; + } + else if (memcmp(code, kMsgEBusy, 4) == 0) { + onErrorBusy(); + break; + } + else if (memcmp(code, kMsgEBad, 4) == 0) { + onErrorBad(); + break; + } else { // unknown message log((CLOG_ERR "unknown message from server")); @@ -543,3 +555,21 @@ void CClient::onMouseWheel() log((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); m_screen->mouseWheel(delta); } + +void CClient::onErrorIncompatible() +{ + SInt32 major, minor; + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgEIncompatible + 4, &major, &minor); + log((CLOG_ERR "server has incompatible version %d.%d", major, minor)); +} + +void CClient::onErrorBusy() +{ + log((CLOG_ERR "server already has a connected client with name \"%s\"", m_name.c_str())); +} + +void CClient::onErrorBad() +{ + log((CLOG_ERR "server disconnected due to a protocol error")); +} diff --git a/client/CClient.h b/client/CClient.h index ac471ecd..498a9d5b 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -48,6 +48,9 @@ private: void onMouseUp(); void onMouseMove(); void onMouseWheel(); + void onErrorIncompatible(); + void onErrorBusy(); + void onErrorBad(); private: CMutex m_mutex; diff --git a/server/CServer.cpp b/server/CServer.cpp index cde6191b..958ac72c 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -838,69 +838,81 @@ void CServer::handshakeClient(void* vsocket) std::auto_ptr protocol; std::auto_ptr connectedNote; - { - // give the client a limited time to complete the handshake - CTimerThread timer(30.0); + try { + { + // give the client a limited time to complete the handshake + CTimerThread timer(30.0); - // limit the maximum length of the hello - static const UInt32 maxHelloLen = 1024; + // limit the maximum length of the hello + static const UInt32 maxHelloLen = 1024; - // say hello - log((CLOG_DEBUG1 "saying hello")); - CProtocolUtil::writef(output.get(), "Synergy%2i%2i", + // say hello + log((CLOG_DEBUG1 "saying hello")); + CProtocolUtil::writef(output.get(), "Synergy%2i%2i", kMajorVersion, kMinorVersion); - output->flush(); + output->flush(); - // wait for the reply - log((CLOG_DEBUG1 "waiting for hello reply")); - UInt32 n = input->getSize(); - if (n > maxHelloLen) { - throw XBadClient(); - } + // wait for the reply + log((CLOG_DEBUG1 "waiting for hello reply")); + UInt32 n = input->getSize(); + if (n > maxHelloLen) { + throw XBadClient(); + } - // get and parse the reply to hello - SInt16 major, minor; - try { - log((CLOG_DEBUG1 "parsing hello reply")); - CProtocolUtil::readf(input.get(), "Synergy%2i%2i%s", + // get and parse the reply to hello + SInt16 major, minor; + try { + log((CLOG_DEBUG1 "parsing hello reply")); + CProtocolUtil::readf(input.get(), "Synergy%2i%2i%s", &major, &minor, &name); - } - catch (XIO&) { - throw XBadClient(); - } - if (major < 0 || minor < 0) { - throw XBadClient(); - } + } + catch (XIO&) { + throw XBadClient(); + } - // create a protocol interpreter for the version - log((CLOG_DEBUG1 "creating interpreter for client %s version %d.%d", name.c_str(), major, minor)); - assign(protocol, CServerProtocol::create(major, minor, + // create a protocol interpreter for the version + log((CLOG_DEBUG1 "creating interpreter for client \"%s\" version %d.%d", name.c_str(), major, minor)); + assign(protocol, CServerProtocol::create(major, minor, this, name, input.get(), output.get()), IServerProtocol); - // client is now pending - assign(connectedNote, new CConnectionNote(this, + // client is now pending + assign(connectedNote, new CConnectionNote(this, name, protocol.get()), CConnectionNote); - // ask and wait for the client's info - log((CLOG_DEBUG1 "waiting for info for client %s", name.c_str())); - protocol->queryInfo(); + // ask and wait for the client's info + log((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str())); + protocol->queryInfo(); + + // now connected; client no longer subject to timeout. + } + + // handle messages from client. returns when the client + // disconnects. + log((CLOG_NOTE "client \"%s\" has connected", name.c_str())); + protocol->run(); + } + catch (XDuplicateClient& e) { + // client has duplicate name + log((CLOG_WARN "a client with name \"%s\" is already connected)", e.getName().c_str())); + CProtocolUtil::writef(output.get(), kMsgEBusy); + } + catch (XIncompatibleClient& e) { + // client is incompatible + // FIXME -- could print network address if socket had suitable method + log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); + CProtocolUtil::writef(output.get(), kMsgEIncompatible, + kMajorVersion, kMinorVersion); + } + catch (XBadClient&) { + // client not behaving + // FIXME -- could print network address if socket had suitable method + log((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); + CProtocolUtil::writef(output.get(), kMsgEBad); } - // handle messages from client. returns when the client - // disconnects. - log((CLOG_NOTE "client %s is connected", name.c_str())); - protocol->run(); - } - catch (XIncompatibleClient& e) { - // client is incompatible - log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); - // FIXME -- could print network address if socket had suitable method - } - catch (XBadClient&) { - // client not behaving - log((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); - // FIXME -- could print network address if socket had suitable method + // flush any pending output + output.get()->flush(); } catch (XBase& e) { // misc error @@ -1087,10 +1099,18 @@ CServer::CScreenInfo* CServer::addConnection( const CString& name, IServerProtocol* protocol) { log((CLOG_DEBUG "adding connection \"%s\"", name.c_str())); + CLock lock(&m_mutex); - assert(m_screens.count(name) == 0); + + // can only have one screen with a given name at any given time + if (m_screens.count(name) != 0) { + throw XDuplicateClient(name); + } + + // save screen info CScreenInfo* newScreen = new CScreenInfo(name, protocol); m_screens.insert(std::make_pair(name, newScreen)); + return newScreen; } diff --git a/server/CServerProtocol.cpp b/server/CServerProtocol.cpp index f4d05697..c74bf0a3 100644 --- a/server/CServerProtocol.cpp +++ b/server/CServerProtocol.cpp @@ -50,18 +50,19 @@ IServerProtocol* CServerProtocol::create(SInt32 major, SInt32 minor, CServer* server, const CString& client, IInputStream* input, IOutputStream* output) { + // disallow invalid version numbers + if (major < 0 || minor < 0) { + throw XIncompatibleClient(major, minor); + } + // disallow connection from test versions to release versions if (major == 0 && kMajorVersion != 0) { - output->write(kMsgEIncompatible, sizeof(kMsgEIncompatible) - 1); - output->flush(); throw XIncompatibleClient(major, minor); } // hangup (with error) if version isn't supported if (major > kMajorVersion || (major == kMajorVersion && minor > kMinorVersion)) { - output->write(kMsgEIncompatible, sizeof(kMsgEIncompatible) - 1); - output->flush(); throw XIncompatibleClient(major, minor); } diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 7e9a9802..e9ed0519 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -58,7 +58,7 @@ void CServerProtocol1_0::run() } // FIXME -- more message here else { - log((CLOG_ERR "unknown message from client \"%s\"", getClient().c_str())); + log((CLOG_ERR "invalid message from client \"%s\"", getClient().c_str())); // unknown message throw XBadClient(); diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index 13588df9..66b45c93 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -109,7 +109,16 @@ static const char kMsgQInfo[] = "QINF"; // error codes // -static const char kMsgEIncompatible[] = "EICV"; +// incompatible versions: primary -> secondary +// $1 = major version of primary, $2 = minor version of primary. +static const char kMsgEIncompatible[] = "EICV%2i%2i"; + +// name provided when connecting is already in use: primary -> secondary +static const char kMsgEBusy[] = "EBSY"; + +// protocol violation: primary -> secondary +// primary should disconnect after sending this message. +static const char kMsgEBad[] = "EBAD"; #endif diff --git a/synergy/XSynergy.cpp b/synergy/XSynergy.cpp index bda330a3..d07c727c 100644 --- a/synergy/XSynergy.cpp +++ b/synergy/XSynergy.cpp @@ -9,8 +9,9 @@ CString XBadClient::getWhat() const throw() return "XBadClient"; } + // -// +// XIncompatibleClient // XIncompatibleClient::XIncompatibleClient(int major, int minor) : @@ -35,3 +36,22 @@ CString XIncompatibleClient::getWhat() const throw() return "XIncompatibleClient"; } + +// +// XDuplicateClient +// + +XDuplicateClient::XDuplicateClient(const CString& name) : m_name(name) +{ + // do nothing +} + +const CString& XDuplicateClient::getName() const throw() +{ + return m_name; +} + +CString XDuplicateClient::getWhat() const throw() +{ + return "XDuplicateClient"; +} diff --git a/synergy/XSynergy.h b/synergy/XSynergy.h index e88b1c56..f3dde1f6 100644 --- a/synergy/XSynergy.h +++ b/synergy/XSynergy.h @@ -31,4 +31,23 @@ private: int m_minor; }; +// client has duplicate name (i.e. client with name is already connected) +class XDuplicateClient : public XSynergy { +public: + XDuplicateClient(const CString& name); + + // manipulators + + // accessors + + virtual const CString& + getName() const throw(); + +protected: + virtual CString getWhat() const throw(); + +private: + CString m_name; +}; + #endif From 9ce7a2757a0224370b2cc140a5253a061c427fd4 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 23 May 2002 15:00:13 +0000 Subject: [PATCH 098/807] fixed log message. --- server/CServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CServer.cpp b/server/CServer.cpp index 958ac72c..16cb7576 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -894,7 +894,7 @@ void CServer::handshakeClient(void* vsocket) } catch (XDuplicateClient& e) { // client has duplicate name - log((CLOG_WARN "a client with name \"%s\" is already connected)", e.getName().c_str())); + log((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); CProtocolUtil::writef(output.get(), kMsgEBusy); } catch (XIncompatibleClient& e) { From 4542bb9e40c099979230c707691693e23ad5f6c8 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 23 May 2002 15:00:39 +0000 Subject: [PATCH 099/807] added a third screen to hard coded map for testing purposes. --- server/server.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/server.cpp b/server/server.cpp index b01f75c3..da5ccf73 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -11,8 +11,11 @@ void realMain() CScreenMap screenMap; screenMap.addScreen("primary"); screenMap.addScreen("secondary"); + screenMap.addScreen("secondary2"); screenMap.connect("primary", CScreenMap::kRight, "secondary"); screenMap.connect("secondary", CScreenMap::kLeft, "primary"); + screenMap.connect("secondary", CScreenMap::kRight, "secondary2"); + screenMap.connect("secondary2", CScreenMap::kLeft, "secondary"); CServer* server = NULL; try { From 24f5b66cf390bd07db69d829a11c8a8b78b76d92 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 23 May 2002 15:50:38 +0000 Subject: [PATCH 100/807] added support for mouse wheel on X. --- client/CXWindowsSecondaryScreen.cpp | 17 +++++++++++++++-- server/CXWindowsPrimaryScreen.cpp | 8 ++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index f83d6814..010c4475 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -293,10 +293,23 @@ void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) XSync(display, False); } -void CXWindowsSecondaryScreen::mouseWheel(SInt32) +void CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) { + // choose button depending on rotation direction + const unsigned int button = (delta >= 0) ? 4 : 5; + + // now use absolute value of delta + if (delta < 0) { + delta = -delta; + } + + // send as many clicks as necessary CDisplayLock display(this); - // FIXME + for (; delta >= 120; delta -= 120) { + XTestFakeButtonEvent(display, button, True, CurrentTime); + XTestFakeButtonEvent(display, button, False, CurrentTime); + } + XSync(display, False); } void CXWindowsSecondaryScreen::setClipboard( diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index bae8ca5c..ebaabeba 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -124,6 +124,14 @@ void CXWindowsPrimaryScreen::run() if (button != kButtonNone) { m_server->onMouseUp(button); } + else if (xevent.xbutton.button == 4) { + // wheel forward (away from user) + m_server->onMouseWheel(120); + } + else if (xevent.xbutton.button == 5) { + // wheel backward (toward user) + m_server->onMouseWheel(-120); + } break; } From e277e6e74a8812cef550d68da615cfa187ab60ec Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 23 May 2002 18:35:08 +0000 Subject: [PATCH 101/807] added support for mouse wheel on win32. --- server/CMSWindowsPrimaryScreen.cpp | 8 ++ server/CSynergyHook.cpp | 188 ++++++++++++++++++++++++----- server/CSynergyHook.h | 1 + 3 files changed, 170 insertions(+), 27 deletions(-) diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 5d3a36b6..43136b07 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -406,6 +406,14 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) } return true; + case SYNERGY_MSG_MOUSE_WHEEL: + // ignore if not at current mark + if (m_mark == m_markReceived) { + log((CLOG_ERR "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); + m_server->onMouseWheel(msg->wParam); + } + return true; + case SYNERGY_MSG_MOUSE_MOVE: // ignore if not at current mark if (m_mark == m_markReceived) { diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 7c7dc293..ef3b052a 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -1,6 +1,29 @@ #include "CSynergyHook.h" #include "CScreenMap.h" #include +#include + +// +// extra mouse wheel stuff +// + +enum EWheelSupport { + kWheelNone, + kWheelOld, + kWheelWin2000, + kWheelModern +}; + +// declare extended mouse hook struct. useable on win2k +typedef struct tagMOUSEHOOKSTRUCTWin2000 { + MOUSEHOOKSTRUCT mhs; + DWORD mouseData; +} MOUSEHOOKSTRUCTWin2000; + +#if !defined(SM_MOUSEWHEELPRESENT) +#define SM_MOUSEWHEELPRESENT 75 +#endif + // // globals @@ -12,10 +35,13 @@ static HINSTANCE g_hinstance = NULL; static DWORD g_process = NULL; +static EWheelSupport g_wheelSupport = kWheelNone; +static UINT g_wmMouseWheel = 0; static HWND g_hwnd = NULL; static HHOOK g_keyboard = NULL; static HHOOK g_mouse = NULL; static HHOOK g_cbt = NULL; +static HHOOK g_getMessage = NULL; static bool g_relay = false; static SInt32 g_zoneSize = 0; static UInt32 g_zoneSides = 0; @@ -24,6 +50,8 @@ static SInt32 g_hScreen = 0; static HCURSOR g_cursor = NULL; static DWORD g_cursorThread = 0; +static int foo = 0; + #pragma data_seg() // @@ -58,27 +86,29 @@ static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { if (g_relay) { - if (code == HC_ACTION) { - // forward message to our window - PostMessage(g_hwnd, SYNERGY_MSG_KEY, wParam, lParam); + // forward message to our window + PostMessage(g_hwnd, SYNERGY_MSG_KEY, wParam, lParam); - // if the active window isn't our window then make it - // active. - const bool wrongFocus = (GetActiveWindow() != g_hwnd); - if (wrongFocus) { - SetForegroundWindow(g_hwnd); - } +/* XXX -- this doesn't seem to work/help with lost key events + // if the active window isn't our window then make it + // active. + const bool wrongFocus = (GetActiveWindow() != g_hwnd); + if (wrongFocus) { + SetForegroundWindow(g_hwnd); + } +*/ - // let non-system keyboard messages through to our window. - // this allows DefWindowProc() to do normal processing. - // for most keys that means do nothing. for toggle keys - // it means updating the thread's toggle state. discard - // system messages (i.e. keys pressed when alt is down) to - // prevent unexpected or undesired processing. also - // discard messages if not destined for our window - if (wrongFocus || (lParam & 0x20000000lu) != 0) { - return 1; - } + // let certain keys pass through + switch (wParam) { + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // pass event + break; + + default: + // discard event + return 1; } } } @@ -100,6 +130,33 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_BUTTON, wParam, 0); return 1; + case WM_MOUSEWHEEL: { + // win2k and other systems supporting WM_MOUSEWHEEL in + // the mouse hook are gratuitously different (and poorly + // documented). + switch (g_wheelSupport) { + case kWheelModern: { + const MOUSEHOOKSTRUCT* info = + (const MOUSEHOOKSTRUCT*)lParam; + PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL, + static_cast(LOWORD(info->dwExtraInfo)), 0); + break; + } + + case kWheelWin2000: { + const MOUSEHOOKSTRUCTWin2000* info = + (const MOUSEHOOKSTRUCTWin2000*)lParam; + PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL, + static_cast(HIWORD(info->mouseData)), 0); + break; + } + + default: + break; + } + return 1; + } + case WM_MOUSEMOVE: { const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; SInt32 x = (SInt32)info->pt.x; @@ -174,6 +231,66 @@ static LRESULT CALLBACK cbtHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_cbt, code, wParam, lParam); } +static LRESULT CALLBACK getMessageHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + if (g_relay) { + MSG* msg = reinterpret_cast(lParam); + if (msg->message == g_wmMouseWheel) { + // post message to our window + PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL, msg->wParam, 0); + + // zero out the delta in the message so it's (hopefully) + // ignored + msg->wParam = 0; + } + } + } + + return CallNextHookEx(g_getMessage, code, wParam, lParam); +} + +static EWheelSupport GetWheelSupport() +{ + // get operating system + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + if (!GetVersionEx(&info)) { + return kWheelNone; + } + + // see if modern wheel is present + if (GetSystemMetrics(SM_MOUSEWHEELPRESENT)) { + // note if running on win2k + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT && + info.dwMajorVersion == 5 && + info.dwMinorVersion == 0) { + return kWheelWin2000; + } + return kWheelModern; + } + + // not modern. see if we've got old-style support. + UINT wheelSupportMsg = RegisterWindowMessage(MSH_WHEELSUPPORT); + HWND wheelSupportWindow = FindWindow(MSH_WHEELMODULE_CLASS, + MSH_WHEELMODULE_TITLE); + if (wheelSupportWindow != NULL && wheelSupportMsg != 0) { + if (SendMessage(wheelSupportWindow, wheelSupportMsg, 0, 0) != 0) { + g_wmMouseWheel = RegisterWindowMessage(MSH_MOUSEWHEEL); + if (g_wmMouseWheel != 0) { + return kWheelOld; + } + } + } + + // assume modern. we don't do anything special in this case + // except respond to WM_MOUSEWHEEL messages. GetSystemMetrics() + // can apparently return FALSE even if a mouse wheel is present + // though i'm not sure exactly when it does that (but WinME does + // for my logitech usb trackball). + return kWheelModern; +} + // // external functions @@ -202,10 +319,11 @@ extern "C" { int install(HWND hwnd) { - assert(g_hinstance != NULL); - assert(g_keyboard == NULL); - assert(g_mouse == NULL); - assert(g_cbt == NULL); + assert(g_hinstance != NULL); + assert(g_keyboard == NULL); + assert(g_mouse == NULL); + assert(g_cbt == NULL); + assert(g_wheelSupport != kWheelOld || g_getMessage == NULL); // save window g_hwnd = hwnd; @@ -219,6 +337,9 @@ int install(HWND hwnd) g_cursor = NULL; g_cursorThread = 0; + // check for mouse wheel support + g_wheelSupport = GetWheelSupport(); + // install keyboard hook g_keyboard = SetWindowsHookEx(WH_KEYBOARD, &keyboardHook, @@ -257,6 +378,15 @@ int install(HWND hwnd) return 0; } + // install GetMessage hook + if (g_wheelSupport == kWheelOld) { + g_getMessage = SetWindowsHookEx(WH_GETMESSAGE, + &getMessageHook, + g_hinstance, + 0); + // ignore failure; we just won't get mouse wheel messages + } + return 1; } @@ -270,10 +400,14 @@ int uninstall(void) UnhookWindowsHookEx(g_keyboard); UnhookWindowsHookEx(g_mouse); UnhookWindowsHookEx(g_cbt); - g_keyboard = NULL; - g_mouse = NULL; - g_cbt = NULL; - g_hwnd = NULL; + if (g_getMessage != NULL) { + UnhookWindowsHookEx(g_getMessage); + } + g_keyboard = NULL; + g_mouse = NULL; + g_cbt = NULL; + g_getMessage = NULL; + g_hwnd = NULL; // show the cursor restoreCursor(); diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index 5cdf9c12..ec99afa2 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -18,6 +18,7 @@ #define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data #define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; #define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y +#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0015 // delta; typedef int (*InstallFunc)(HWND); typedef int (*UninstallFunc)(void); From a0b25b9d4a6f87947de9194942e18a97ccbfacd8 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 23 May 2002 18:35:15 +0000 Subject: [PATCH 102/807] checkpoint --- notes | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/notes b/notes index d18b39d9..6d70163d 100644 --- a/notes +++ b/notes @@ -12,11 +12,23 @@ hangup if invalid query info --> <-- info (size) +--- +need config file and parser + remove hard coded debugging stuff (in server.cpp) + --- win32: need to support window stations login screen on NT is a separate window station handle display changes + provide taskbar icon + +win32 dll: + should make this as small as possible + don't use standard libraries + use custom _DllMainCRTStartup(); just call DllMain() from it. + use /MERGE linker switch to merge sections + should use mutex on all public functions --- not handling international characters @@ -191,10 +203,9 @@ keyboard hook problems thinks the key is always down. --- -server asserts if two clients with same name try to connect - should simply refuse second client -also not reaping disconnects very fast - investigate slowness +try to determine keyboard quirks automatically + in particular, set the half-duplex flags + maybe KBD extension will help determine this --- blown assert: @@ -205,11 +216,6 @@ blown assert: must flush queued events when jumping to primary check for other things to do when jumping to primary ---- -not detecting disconnect when remote side crashes - is there some socket flag to help with this? - should catch signal when closing console window and close nicely - --- sending same clipboard repeatedly from win32 server can we detect no change? From 34c587e86477b0848498b2e2e2c0eeb31b1e21e2 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 24 May 2002 14:37:12 +0000 Subject: [PATCH 103/807] added support for locking to a screen when the sroll lock is toggled on or when any key or button is pressed. fully implemented on X but stubbed out for now on win32. --- server/CMSWindowsPrimaryScreen.cpp | 6 +++++ server/CMSWindowsPrimaryScreen.h | 1 + server/CServer.cpp | 12 +++++++++- server/CXWindowsPrimaryScreen.cpp | 35 +++++++++++++++++++++++++++++- server/CXWindowsPrimaryScreen.h | 1 + synergy/IPrimaryScreen.h | 5 +++++ 6 files changed, 58 insertions(+), 2 deletions(-) diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 43136b07..546536d0 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -255,6 +255,12 @@ KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const return mask; } +bool CMSWindowsPrimaryScreen::isLockedToScreen() const +{ + // FIXME + return false; +} + void CMSWindowsPrimaryScreen::onOpenDisplay() { assert(m_window == NULL); diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 35bb7167..9d44c912 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -27,6 +27,7 @@ public: virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; virtual KeyModifierMask getToggleMask() const; + virtual bool isLockedToScreen() const; protected: // CMSWindowsScreen overrides diff --git a/server/CServer.cpp b/server/CServer.cpp index 16cb7576..09739198 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -489,7 +489,17 @@ void CServer::onMouseWheel(SInt32 delta) bool CServer::isLockedToScreen() const { - // FIXME + // locked if primary says we're locked + if (m_primary->isLockedToScreen()) { + return true; + } + + // locked if scroll-lock is toggled on + if ((m_primary->getToggleMask() & KeyModifierScrollLock) != 0) { + return true; + } + + // not locked return false; } diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index ebaabeba..26e374c0 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -424,8 +424,9 @@ KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const int xRoot, yRoot, xWindow, yWindow; unsigned int state; if (!XQueryPointer(display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &state)) + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { return 0; + } // convert to KeyModifierMask KeyModifierMask mask = 0; @@ -439,6 +440,38 @@ KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const return mask; } +bool CXWindowsPrimaryScreen::isLockedToScreen() const +{ + CDisplayLock display(this); + + // query the pointer to get the button state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + if ((state & (Button1Mask | Button2Mask | Button3Mask | + Button4Mask | Button5Mask)) != 0) { + return true; + } + } + + // get logical keyboard state + char keyMap[32]; + memset(keyMap, 0, sizeof(keyMap)); + XQueryKeymap(display, keyMap); + + // locked if any key is down + for (unsigned int i = 0; i < sizeof(keyMap); ++i) { + if (keyMap[i] != 0) { + return true; + } + } + + // not locked + return false; +} + void CXWindowsPrimaryScreen::onOpenDisplay() { assert(m_window == None); diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 381b013a..03e5bc98 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -25,6 +25,7 @@ public: virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; virtual KeyModifierMask getToggleMask() const; + virtual bool isLockedToScreen() const; protected: // CXWindowsScreen overrides diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 64c44bec..02535ac9 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -84,6 +84,11 @@ public: // the returned mask should have the corresponding bit set for // each toggle key that is active. virtual KeyModifierMask getToggleMask() const = 0; + + // return true if any key or button is being pressed or if there's + // any other reason that the user should not be allowed to switch + // screens. + virtual bool isLockedToScreen() const = 0; }; #endif From 5fc87baa0f9debe1bd846f21e45ee4a4d8f587d8 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 24 May 2002 17:54:28 +0000 Subject: [PATCH 104/807] added screen locking support to win32. added support for resolution changing (only semi-supported on X because that has no means for resizing screen anyway). also fixed some clipboard problems on win32. --- client/CClient.cpp | 55 +++++++++++++++++---- client/CClient.h | 5 ++ client/CMSWindowsSecondaryScreen.cpp | 23 +++++++++ client/CMSWindowsSecondaryScreen.h | 1 + client/CXWindowsSecondaryScreen.cpp | 13 +++++ client/CXWindowsSecondaryScreen.h | 1 + server/CMSWindowsPrimaryScreen.cpp | 74 ++++++++++++++++++++++++---- server/CServer.cpp | 45 ++++++++++++----- server/CServer.h | 10 +++- server/CServerProtocol.h | 1 + server/CServerProtocol1_0.cpp | 18 +++++-- server/CServerProtocol1_0.h | 1 + server/CXWindowsPrimaryScreen.cpp | 5 ++ synergy/CMSWindowsScreen.cpp | 7 +++ synergy/CMSWindowsScreen.h | 3 ++ synergy/IPrimaryScreen.h | 6 ++- synergy/ISecondaryScreen.h | 3 ++ synergy/IServerProtocol.h | 1 + synergy/ProtocolTypes.h | 21 ++++++-- 19 files changed, 253 insertions(+), 40 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index c3d44cfd..3f8ec103 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -32,7 +32,8 @@ CClient::CClient(const CString& clientName) : m_output(NULL), m_screen(NULL), m_active(false), - m_seqNum(0) + m_seqNum(0), + m_ignoreMove(false) { // do nothing } @@ -106,7 +107,7 @@ void CClient::onClipboardChanged(ClipboardID id) m_timeClipboard[id] = 0; // if we're not the active screen then send the clipboard now, - // otherwise we'll until we leave. + // otherwise we'll wait until we leave. if (!m_active) { // get clipboard CClipboard clipboard; @@ -127,6 +128,19 @@ void CClient::onClipboardChanged(ClipboardID id) } } +void CClient::onResolutionChanged() +{ + log((CLOG_DEBUG "resolution changed")); + + CLock lock(&m_mutex); + + // start ignoring mouse movement until we get an acknowledgment + m_ignoreMove = true; + + // send notification of resolution change + onQueryInfoNoLock(); +} + #include "CTCPSocket.h" // FIXME void CClient::runSession(void*) { @@ -261,6 +275,9 @@ void CClient::runSession(void*) else if (memcmp(code, kMsgQInfo, 4) == 0) { onQueryInfo(); } + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + onInfoAcknowledgment(); + } else if (memcmp(code, kMsgDClipboard, 4) == 0) { onSetClipboard(); } @@ -399,10 +416,13 @@ void CClient::onLeave() // marshall the data CString data = clipboard.marshall(); - // send data - log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); - CProtocolUtil::writef(m_output, + // save and send data if different + if (data != m_dataClipboard[id]) { + log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); + m_dataClipboard[id] = data; + CProtocolUtil::writef(m_output, kMsgDClipboard, id, m_seqNum, &data); + } } } } @@ -441,13 +461,26 @@ void CClient::onScreenSaver() void CClient::onQueryInfo() { - SInt32 w, h; + CLock lock(&m_mutex); + onQueryInfoNoLock(); +} + +void CClient::onQueryInfoNoLock() +{ + SInt32 x, y, w, h; + m_screen->getMousePos(&x, &y); m_screen->getSize(&w, &h); SInt32 zoneSize = m_screen->getJumpZoneSize(); - log((CLOG_DEBUG1 "sending info size=%d,%d zone=%d", w, h, zoneSize)); + log((CLOG_DEBUG1 "sending info size=%d,%d zone=%d pos=%d,%d", w, h, zoneSize, x, y)); + CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize, x, y); +} + +void CClient::onInfoAcknowledgment() +{ + log((CLOG_DEBUG1 "recv info acknowledgment")); CLock lock(&m_mutex); - CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); + m_ignoreMove = false; } void CClient::onSetClipboard() @@ -536,13 +569,17 @@ void CClient::onMouseUp() void CClient::onMouseMove() { + bool ignore; SInt16 x, y; { CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); + ignore = m_ignoreMove; } log((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); - m_screen->mouseMove(x, y); + if (!ignore) { + m_screen->mouseMove(x, y); + } } void CClient::onMouseWheel() diff --git a/client/CClient.h b/client/CClient.h index 498a9d5b..a6c0cea1 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -23,6 +23,7 @@ public: // handle events on client's screen void onClipboardChanged(ClipboardID); + void onResolutionChanged(); // accessors @@ -40,6 +41,8 @@ private: void onGrabClipboard(); void onScreenSaver(); void onQueryInfo(); + void onQueryInfoNoLock(); + void onInfoAcknowledgment(); void onSetClipboard(); void onKeyDown(); void onKeyRepeat(); @@ -61,8 +64,10 @@ private: const CNetworkAddress* m_serverAddress; bool m_active; UInt32 m_seqNum; + bool m_ignoreMove; bool m_ownClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; + CString m_dataClipboard[kClipboardEnd]; }; #endif diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index cdca03c4..26a97fa5 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -315,6 +315,23 @@ void CMSWindowsSecondaryScreen::grabClipboard(ClipboardID id) } } +void CMSWindowsSecondaryScreen::getMousePos( + SInt32* x, SInt32* y) const +{ + assert(x != NULL); + assert(y != NULL); + + POINT pos; + if (GetCursorPos(&pos)) { + *x = pos.x; + *y = pos.y; + } + else { + *x = 0; + *y = 0; + } +} + void CMSWindowsSecondaryScreen::getSize( SInt32* width, SInt32* height) const { @@ -420,6 +437,12 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( else SendMessage(m_nextClipboardWindow, msg, wParam, lParam); return 0; + + case WM_DISPLAYCHANGE: + // screen resolution has changed + updateScreenSize(); + m_client->onResolutionChanged(); + return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 8149ea92..3ca03338 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -28,6 +28,7 @@ public: virtual void mouseWheel(SInt32 delta); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); + virtual void getMousePos(SInt32* x, SInt32* y) const; virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 010c4475..1a58a7bd 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -323,6 +323,19 @@ void CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) setDisplayClipboard(id, NULL, m_window, getCurrentTime(m_window)); } +void CXWindowsSecondaryScreen::getMousePos( + SInt32* x, SInt32* y) const +{ + CDisplayLock display(this); + int xTmp, yTmp, dummy; + unsigned int dummyMask; + Window dummyWindow; + XQueryPointer(display, getRoot(), &dummyWindow, &dummyWindow, + &xTmp, &yTmp, &dummy, &dummy, &dummyMask); + *x = xTmp; + *y = yTmp; +} + void CXWindowsSecondaryScreen::getSize( SInt32* width, SInt32* height) const { diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 6aa1cb8b..44f63182 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -27,6 +27,7 @@ public: virtual void mouseWheel(SInt32 delta); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); + virtual void getMousePos(SInt32* x, SInt32* y) const; virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 546536d0..fa6d7e5c 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -61,6 +61,11 @@ void CMSWindowsPrimaryScreen::open(CServer* server) // get keyboard state updateKeys(); + // send screen info + SInt32 w, h; + getScreenSize(&w, &h); + m_server->setInfo(w, h, getJumpZoneSize(), 0, 0); + // enter the screen doEnter(); } @@ -246,18 +251,35 @@ void CMSWindowsPrimaryScreen::getClipboard( KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { KeyModifierMask mask = 0; - if ((m_keys[VK_CAPITAL] & 0x01) != 0) + if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) mask |= KeyModifierCapsLock; - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) mask |= KeyModifierNumLock; - if ((m_keys[VK_SCROLL] & 0x01) != 0) + if ((GetKeyState(VK_SCROLL) & 0x01) != 0) mask |= KeyModifierScrollLock; return mask; } bool CMSWindowsPrimaryScreen::isLockedToScreen() const { - // FIXME + // check buttons + if (GetAsyncKeyState(VK_LBUTTON) < 0 || + GetAsyncKeyState(VK_MBUTTON) < 0 || + GetAsyncKeyState(VK_RBUTTON) < 0) { + return true; + } + + // check keys + BYTE keys[256]; + if (GetKeyboardState(keys)) { + for (unsigned int i = 0; i < sizeof(keys); ++i) { + if ((keys[i] & 0x80) != 0) { + return true; + } + } + } + + // not locked return false; } @@ -433,13 +455,17 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) // get mouse deltas x -= m_xCenter; y -= m_yCenter; - log((CLOG_DEBUG2 "event: active move %d,%d", x, y)); - // warp mouse back to center - warpCursor(m_xCenter, m_yCenter); + // ignore if the mouse didn't move + if (x != 0 && y != 0) { + log((CLOG_DEBUG2 "event: active move %d,%d", x, y)); - // send motion - m_server->onMouseMoveSecondary(x, y); + // warp mouse back to center + warpCursor(m_xCenter, m_yCenter); + + // send motion + m_server->onMouseMoveSecondary(x, y); + } } } return true; @@ -485,6 +511,36 @@ LRESULT CMSWindowsPrimaryScreen::onEvent( else SendMessage(m_nextClipboardWindow, msg, wParam, lParam); return 0; + + case WM_DISPLAYCHANGE: { + // screen resolution has changed + SInt32 w, h; + updateScreenSize(); + getScreenSize(&w, &h); + + // recompute center pixel of screen + m_xCenter = w >> 1; + m_yCenter = h >> 1; + + // warp mouse to center if active + if (m_active) { + warpCursor(m_xCenter, m_yCenter); + } + + // tell hook about resize if not active + else { + SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( + m_hookLibrary, "setZone"); + setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize()); + } + + // send new screen info + POINT pos; + GetCursorPos(&pos); + m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y); + + return 0; + } } return DefWindowProc(hwnd, msg, wParam, lParam); diff --git a/server/CServer.cpp b/server/CServer.cpp index 09739198..6c8001a3 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -132,8 +132,16 @@ UInt32 CServer::getActivePrimarySides() const return sides; } +void CServer::setInfo( + SInt32 w, SInt32 h, SInt32 zoneSize, + SInt32 x, SInt32 y) +{ + setInfo(m_primaryInfo->m_name, w, h, zoneSize, x, y); +} + void CServer::setInfo(const CString& client, - SInt32 w, SInt32 h, SInt32 zoneSize) + SInt32 w, SInt32 h, SInt32 zoneSize, + SInt32 x, SInt32 y) { assert(!client.empty()); assert(w > 0); @@ -151,12 +159,29 @@ void CServer::setInfo(const CString& client, // update client info CScreenInfo* info = index->second; if (info == m_active) { - // FIXME -- ensure mouse is still on screen. warp it if necessary. + // update the remote mouse coordinates + m_x = x; + m_y = y; } info->m_width = w; info->m_height = h; info->m_zoneSize = zoneSize; - log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d", client.c_str(), w, h, zoneSize)); + log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d pos=%d,%d", client.c_str(), w, h, zoneSize, x, y)); + + // send acknowledgement (if client isn't the primary) + if (info->m_protocol != NULL) { + info->m_protocol->sendInfoAcknowledgment(); + } + + // handle resolution change to primary screen + else { + if (info == m_active) { + onMouseMovePrimary(x, y); + } + else { + onMouseMoveSecondary(0, 0); + } + } } void CServer::grabClipboard(ClipboardID id) @@ -1011,6 +1036,10 @@ void CServer::openPrimaryScreen() // reset sequence number m_seqNum = 0; + // add connection + m_active = addConnection(CString("primary"/* FIXME */), NULL); + m_primaryInfo = m_active; + // open screen log((CLOG_DEBUG1 "creating primary screen")); #if defined(CONFIG_PLATFORM_WIN32) @@ -1021,16 +1050,6 @@ void CServer::openPrimaryScreen() log((CLOG_DEBUG1 "opening primary screen")); m_primary->open(this); - // add connection - m_active = addConnection(CString("primary"/* FIXME */), NULL); - m_primaryInfo = m_active; - - // update info - m_primary->getSize(&m_active->m_width, &m_active->m_height); - m_active->m_zoneSize = m_primary->getJumpZoneSize(); - log((CLOG_NOTE "server size=%dx%d zone=%d", m_active->m_width, m_active->m_height, m_active->m_zoneSize)); - // FIXME -- need way for primary screen to call us back - // set the clipboard owner to the primary screen and then get the // current clipboard data. for (ClipboardID id = 0; id < kClipboardEnd; ++id) { diff --git a/server/CServer.h b/server/CServer.h index 321017ce..5cdadfe5 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -46,9 +46,16 @@ public: void onMouseWheel(SInt32 delta); void grabClipboard(ClipboardID); + // handle updates from primary + void setInfo(SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse); + // handle messages from clients void setInfo(const CString& clientName, - SInt32 w, SInt32 h, SInt32 zoneSize); + SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse); void grabClipboard(ClipboardID, UInt32 seqNum, const CString& clientName); void setClipboard(ClipboardID, @@ -187,6 +194,7 @@ private: // the sequence number of enter messages UInt32 m_seqNum; + // current mouse position (in absolute secondary screen coordinates) SInt32 m_x, m_y; CScreenMap m_screenMap; diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index c64fa6cf..47ae856d 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -37,6 +37,7 @@ public: virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; virtual void sendScreenSaver(bool on) = 0; + virtual void sendInfoAcknowledgment() = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index e9ed0519..1d4240f0 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -124,6 +124,12 @@ void CServerProtocol1_0::sendScreenSaver(bool on) CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } +void CServerProtocol1_0::sendInfoAcknowledgment() +{ + log((CLOG_DEBUG1 "send info ack to \"%s\"", getClient().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCInfoAck); +} + void CServerProtocol1_0::sendKeyDown( KeyID key, KeyModifierMask mask) { @@ -176,17 +182,21 @@ void CServerProtocol1_0::sendMouseWheel( void CServerProtocol1_0::recvInfo() { // parse the message - SInt16 w, h, zoneInfo; - CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, &w, &h, &zoneInfo); - log((CLOG_DEBUG "received client \"%s\" info size=%dx%d, zone=%d", getClient().c_str(), w, h, zoneInfo)); + SInt16 x, y, w, h, zoneInfo; + CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, + &w, &h, &zoneInfo, &x, &y); + log((CLOG_DEBUG "received client \"%s\" info size=%dx%d, zone=%d, pos=%d,%d", getClient().c_str(), w, h, zoneInfo, x, y)); // validate if (w <= 0 || h <= 0 || zoneInfo < 0) { throw XBadClient(); } + if (x < 0 || y < 0 || x >= w || y >= h) { + throw XBadClient(); + } // tell server of change - getServer()->setInfo(getClient(), w, h, zoneInfo); + getServer()->setInfo(getClient(), w, h, zoneInfo, x, y); } void CServerProtocol1_0::recvClipboard() diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index e7fbcc58..4114643e 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -22,6 +22,7 @@ public: virtual void sendClipboard(ClipboardID, const CString&); virtual void sendGrabClipboard(ClipboardID); virtual void sendScreenSaver(bool on); + virtual void sendInfoAcknowledgment(); virtual void sendKeyDown(KeyID, KeyModifierMask); virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count); virtual void sendKeyUp(KeyID, KeyModifierMask); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 26e374c0..db112338 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -268,6 +268,11 @@ void CXWindowsPrimaryScreen::open(CServer* server) CDisplayLock display(this); updateModifierMap(display); } + + // send screen info + SInt32 w, h; + getScreenSize(&w, &h); + m_server->setInfo(w, h, getJumpZoneSize(), 0, 0); } void CXWindowsPrimaryScreen::close() diff --git a/synergy/CMSWindowsScreen.cpp b/synergy/CMSWindowsScreen.cpp index 49860496..ff2c2e39 100644 --- a/synergy/CMSWindowsScreen.cpp +++ b/synergy/CMSWindowsScreen.cpp @@ -135,6 +135,13 @@ ATOM CMSWindowsScreen::getClass() const return m_class; } +void CMSWindowsScreen::updateScreenSize() +{ + m_w = GetSystemMetrics(SM_CXSCREEN); + m_h = GetSystemMetrics(SM_CYSCREEN); + log((CLOG_INFO "display resize: %dx%d", m_w, m_h)); +} + void CMSWindowsScreen::getScreenSize( SInt32* w, SInt32* h) const { diff --git a/synergy/CMSWindowsScreen.h b/synergy/CMSWindowsScreen.h index a916beb3..510ed3c6 100644 --- a/synergy/CMSWindowsScreen.h +++ b/synergy/CMSWindowsScreen.h @@ -40,6 +40,9 @@ protected: static HINSTANCE getInstance(); ATOM getClass() const; + // update screen size cache + void updateScreenSize(); + // get the size of the screen void getScreenSize(SInt32* w, SInt32* h) const; diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 02535ac9..4390addc 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -28,7 +28,11 @@ public: // can't be detected then this object should disable the system's // screen saver timer and should start the screen saver after // idling for an appropriate time. - virtual void open(CServer*) = 0; + // + // open() must call server->setInfo() to notify the server of the + // primary screen's resolution and jump zone size. the mouse + // position is ignored and may be 0,0. + virtual void open(CServer* server) = 0; // close the screen. should restore the screen saver timer if it // was disabled. diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index c7f1ba3a..c5bd736e 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -66,6 +66,9 @@ public: // accessors + // get the position of the mouse on the screen + virtual void getMousePos(SInt32* x, SInt32* y) const = 0; + // get the size of the screen virtual void getSize(SInt32* width, SInt32* height) const = 0; diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index c8d40f2a..ecddeeda 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -30,6 +30,7 @@ public: virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; virtual void sendScreenSaver(bool on) = 0; + virtual void sendInfoAcknowledgment() = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index 66b45c93..c8194961 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -50,6 +50,12 @@ static const char kMsgCClipboard[] = "CCLP%1i%4i"; // screensaver on primary has started ($1 == 1) or closed ($1 == 0) static const char kMsgCScreenSaver[] = "CSEC%1i"; +// resolution change acknowledgment: primary -> secondary +// sent by primary in response to a secondary screen's kMsgDInfo. +// this is sent for every kMsgDInfo, whether or not the primary +// had sent a kMsgQInfo. +static const char kMsgCInfoAck[] = "CIAK"; + // // data codes @@ -80,7 +86,8 @@ static const char kMsgDMouseUp[] = "DMUP%1i"; static const char kMsgDMouseMove[] = "DMMV%2i%2i"; // mouse button pressed: primary -> secondary -// $1 = delta +// $1 = delta. the delta should be +120 for one tick forward (away +// from the user) and -120 for one tick backward (toward the user). static const char kMsgDMouseWheel[] = "DMWM%2i"; // clipboard data: primary <-> secondary @@ -92,8 +99,16 @@ static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; // client data: secondary -> primary // $1 = seconary screen width in pixels, $2 = screen height, $3 = -// size of warp zone. -static const char kMsgDInfo[] = "DINF%2i%2i%2i"; +// size of warp zone. $4 and $5 are the x,y position of the mouse +// on the secondary screen. +// +// the secondary screen must send this message in response to the +// kMsgQInfo message. it must also send this message when the +// screen's resolution changes. in this case, the secondary screen +// should ignore any kMsgDMouseMove messages until it receives a +// kMsgCInfoAck in order to prevent attempts to move the mouse off +// the new screen area. +static const char kMsgDInfo[] = "DINF%2i%2i%2i%2i%2i"; // From 097882b71c08f5e5d1fba8312b8b385ccecea40f Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 24 May 2002 17:54:34 +0000 Subject: [PATCH 105/807] checkpoint --- notes | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/notes b/notes index 6d70163d..f497c867 100644 --- a/notes +++ b/notes @@ -12,6 +12,9 @@ hangup if invalid query info --> <-- info (size) +--- +use automake + --- need config file and parser remove hard coded debugging stuff (in server.cpp) @@ -20,7 +23,6 @@ need config file and parser win32: need to support window stations login screen on NT is a separate window station - handle display changes provide taskbar icon win32 dll: @@ -34,10 +36,9 @@ win32 dll: not handling international characters --- -don't allow leaving screen when any key is down - that includes shift keys and mouse buttons - caps lock, etc must not be down but can be on +not handling screen saver at all yet +--- not distinguishing between caps lock and shift lock In Caps Lock mode, the alphabetic keys send their uppercase (shifted) char- acter when pressed alone. For Caps Lock mode, pressing and releasing the @@ -77,15 +78,10 @@ sometimes not sending correct clipboard switching back to owner screen then out gets correct clipboard --- -screen resize should report new mouse position - client could change it to keep mouse on screen - server will need to know new position - may need to ignore mouse events sent after resize but before server - knows about the change. - not handling screen resizing yet - ---- -not handling screen saver at all yet +win32 not sending correct clipboard + got 43 bytes sent for a 3 byte selection + remaining bytes were 0 + problem persisted; also got 43 bytes for a 2 byte selection --- slow leaving primary screen on X @@ -181,13 +177,13 @@ audrey->isabel usually go on/off when active window changes though --- -avoid fullscreen transparent window +avoid fullscreen transparent window on win32 using CBT hook to discard activation/focus messages but it's not being called when we lose keyboard input also, we sometimes don't get the keyboard input to start with --- -keyboard hook problems +keyboard hook problems (win32) now passing keyboard events through in most cases this fixes problem on isabel where toggle lights don't toggle and num-lock behaved incorrectly (generated wrong keys). @@ -216,10 +212,6 @@ blown assert: must flush queued events when jumping to primary check for other things to do when jumping to primary ---- -sending same clipboard repeatedly from win32 server - can we detect no change? - --- key events sent to console on win me (95/98?) are very slow 1/4 to 1/2 second delay before key up is processed From 854d2c7fbf5a4be23cd4dc8bad95cd2ea442b448 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 May 2002 16:22:59 +0000 Subject: [PATCH 106/807] checkpoint. changed clipboard model. the clipboard can only be accessed now between open()/close(). ownership of the clipboard is asserted via the empty() method. this parallels the win32 model (but the win32 code hasn't been updated yet). refactored X11 clipboard code. moved the bulk of it into CXWindowsClipboard and moved some comment event handling into CXWindowsScreen. changed how requests are processed into a hopefully easier to understand model. added support for getting clipboard from and sending clipboard to motif (or at least lesstif) clients. sending to lesstif required a hack to work around an apparent bug in lesstif. --- client/CXWindowsSecondaryScreen.cpp | 87 +- client/CXWindowsSecondaryScreen.h | 4 + server/CXWindowsPrimaryScreen.cpp | 87 +- server/CXWindowsPrimaryScreen.h | 3 + synergy/CClipboard.cpp | 72 +- synergy/CClipboard.h | 15 +- synergy/CXWindowsClipboard.cpp | 1275 ++++++++++++++++++++++++++- synergy/CXWindowsClipboard.h | 250 +++++- synergy/CXWindowsScreen.cpp | 1006 +++------------------ synergy/CXWindowsScreen.h | 141 +-- synergy/IClipboard.h | 41 +- synergy/Makefile | 1 + 12 files changed, 1800 insertions(+), 1182 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 1a58a7bd..ec4f5d21 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -1,4 +1,6 @@ #include "CXWindowsSecondaryScreen.h" +#include "CXWindowsClipboard.h" +#include "CXWindowsUtil.h" #include "CClient.h" #include "CThread.h" #include "CLog.h" @@ -57,71 +59,6 @@ void CXWindowsSecondaryScreen::run() XUnmapWindow(display, m_window); break; } - - case SelectionClear: - // we just lost the selection. that means someone else - // grabbed the selection so this screen is now the - // selection owner. report that to the server. - if (lostClipboard(xevent.xselectionclear.selection, - xevent.xselectionclear.time)) { - m_client->onClipboardChanged(getClipboardID( - xevent.xselectionclear.selection)); - } - break; - - case SelectionNotify: - // notification of selection transferred. we shouldn't - // get this here because we handle them in the selection - // retrieval methods. we'll just delete the property - // with the data (satisfying the usual ICCCM protocol). - if (xevent.xselection.property != None) { - CDisplayLock display(this); - XDeleteProperty(display, m_window, xevent.xselection.property); - } - break; - - case SelectionRequest: - // somebody is asking for clipboard data - if (xevent.xselectionrequest.owner == m_window) { - addClipboardRequest(m_window, - xevent.xselectionrequest.requestor, - xevent.xselectionrequest.selection, - xevent.xselectionrequest.target, - xevent.xselectionrequest.property, - xevent.xselectionrequest.time); - } - else { - // unknown window. return failure. - CDisplayLock display(this); - XEvent event; - event.xselection.type = SelectionNotify; - event.xselection.display = display; - event.xselection.requestor = xevent.xselectionrequest.requestor; - event.xselection.selection = xevent.xselectionrequest.selection; - event.xselection.target = xevent.xselectionrequest.target; - event.xselection.property = None; - event.xselection.time = xevent.xselectionrequest.time; - XSendEvent(display, xevent.xselectionrequest.requestor, - False, 0, &event); - } - break; - - case PropertyNotify: - // clipboard transfers involve property changes so forward - // the event to the superclass. we only care about the - // deletion of properties. - if (xevent.xproperty.state == PropertyDelete) { - processClipboardRequest(xevent.xproperty.window, - xevent.xproperty.atom, - xevent.xproperty.time); - } - break; - - case DestroyNotify: - // looks like one of the windows that requested a clipboard - // transfer has gone bye-bye. - destroyClipboardRequest(xevent.xdestroywindow.window); - break; } } } @@ -315,12 +252,12 @@ void CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) void CXWindowsSecondaryScreen::setClipboard( ClipboardID id, const IClipboard* clipboard) { - setDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); + setDisplayClipboard(id, clipboard); } void CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) { - setDisplayClipboard(id, NULL, m_window, getCurrentTime(m_window)); + setDisplayClipboard(id, NULL); } void CXWindowsSecondaryScreen::getMousePos( @@ -350,7 +287,7 @@ SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const void CXWindowsSecondaryScreen::getClipboard( ClipboardID id, IClipboard* clipboard) const { - getDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); + getDisplayClipboard(id, clipboard); } void CXWindowsSecondaryScreen::onOpenDisplay() @@ -381,6 +318,13 @@ void CXWindowsSecondaryScreen::onOpenDisplay() leaveNoLock(display); } +CXWindowsClipboard* CXWindowsSecondaryScreen::createClipboard( + ClipboardID id) +{ + CDisplayLock display(this); + return new CXWindowsClipboard(display, m_window, id); +} + void CXWindowsSecondaryScreen::onCloseDisplay() { assert(m_window != None); @@ -394,6 +338,13 @@ void CXWindowsSecondaryScreen::onCloseDisplay() m_window = None; } +void CXWindowsSecondaryScreen::onLostClipboard( + ClipboardID id) +{ + // tell client that the clipboard was grabbed locally + m_client->onClipboardChanged(id); +} + long CXWindowsSecondaryScreen::getEventMask(Window w) const { if (w == m_window) diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 44f63182..52bbde1b 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -3,6 +3,7 @@ #include "CXWindowsScreen.h" #include "ISecondaryScreen.h" +#include #include class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen { @@ -35,7 +36,10 @@ public: protected: // CXWindowsScreen overrides virtual void onOpenDisplay(); + virtual CXWindowsClipboard* + createClipboard(ClipboardID); virtual void onCloseDisplay(); + virtual void onLostClipboard(ClipboardID); virtual long getEventMask(Window) const; private: diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index db112338..149b1835 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -1,4 +1,6 @@ #include "CXWindowsPrimaryScreen.h" +#include "CXWindowsClipboard.h" +#include "CXWindowsUtil.h" #include "CServer.h" #include "CThread.h" #include "CLog.h" @@ -171,71 +173,6 @@ void CXWindowsPrimaryScreen::run() } break; } - - case SelectionClear: - // we just lost the selection. that means someone else - // grabbed the selection so this screen is now the - // selection owner. report that to the server. - if (lostClipboard(xevent.xselectionclear.selection, - xevent.xselectionclear.time)) { - m_server->grabClipboard(getClipboardID( - xevent.xselectionclear.selection)); - } - break; - - case SelectionNotify: - // notification of selection transferred. we shouldn't - // get this here because we handle them in the selection - // retrieval methods. we'll just delete the property - // with the data (satisfying the usual ICCCM protocol). - if (xevent.xselection.property != None) { - CDisplayLock display(this); - XDeleteProperty(display, m_window, xevent.xselection.property); - } - break; - - case SelectionRequest: - // somebody is asking for clipboard data - if (xevent.xselectionrequest.owner == m_window) { - addClipboardRequest(m_window, - xevent.xselectionrequest.requestor, - xevent.xselectionrequest.selection, - xevent.xselectionrequest.target, - xevent.xselectionrequest.property, - xevent.xselectionrequest.time); - } - else { - // unknown window. return failure. - CDisplayLock display(this); - XEvent event; - event.xselection.type = SelectionNotify; - event.xselection.display = display; - event.xselection.requestor = xevent.xselectionrequest.requestor; - event.xselection.selection = xevent.xselectionrequest.selection; - event.xselection.target = xevent.xselectionrequest.target; - event.xselection.property = None; - event.xselection.time = xevent.xselectionrequest.time; - XSendEvent(display, xevent.xselectionrequest.requestor, - False, 0, &event); - } - break; - - case PropertyNotify: - // clipboard transfers involve property changes so forward - // the event to the superclass. we only care about the - // deletion of properties. - if (xevent.xproperty.state == PropertyDelete) { - processClipboardRequest(xevent.xproperty.window, - xevent.xproperty.atom, - xevent.xproperty.time); - } - break; - - case DestroyNotify: - // looks like one of the windows that requested a clipboard - // transfer has gone bye-bye. - destroyClipboardRequest(xevent.xdestroywindow.window); - break; } } } @@ -394,12 +331,12 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( void CXWindowsPrimaryScreen::setClipboard( ClipboardID id, const IClipboard* clipboard) { - setDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); + setDisplayClipboard(id, clipboard); } void CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) { - setDisplayClipboard(id, NULL, m_window, getCurrentTime(m_window)); + setDisplayClipboard(id, NULL); } void CXWindowsPrimaryScreen::getSize( @@ -416,7 +353,7 @@ SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const void CXWindowsPrimaryScreen::getClipboard( ClipboardID id, IClipboard* clipboard) const { - getDisplayClipboard(id, clipboard, m_window, getCurrentTime(m_window)); + getDisplayClipboard(id, clipboard); } KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const @@ -508,6 +445,13 @@ void CXWindowsPrimaryScreen::onOpenDisplay() selectEvents(display, getRoot()); } +CXWindowsClipboard* CXWindowsPrimaryScreen::createClipboard( + ClipboardID id) +{ + CDisplayLock display(this); + return new CXWindowsClipboard(display, m_window, id); +} + void CXWindowsPrimaryScreen::onCloseDisplay() { assert(m_window != None); @@ -518,6 +462,13 @@ void CXWindowsPrimaryScreen::onCloseDisplay() m_window = None; } +void CXWindowsPrimaryScreen::onLostClipboard( + ClipboardID id) +{ + // tell server that the clipboard was grabbed locally + m_server->grabClipboard(id); +} + long CXWindowsPrimaryScreen::getEventMask(Window w) const { if (w == m_window) diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 03e5bc98..b3fc98b6 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -30,7 +30,10 @@ public: protected: // CXWindowsScreen overrides virtual void onOpenDisplay(); + virtual CXWindowsClipboard* + createClipboard(ClipboardID); virtual void onCloseDisplay(); + virtual void onLostClipboard(ClipboardID); virtual long getEventMask(Window) const; private: diff --git a/synergy/CClipboard.cpp b/synergy/CClipboard.cpp index bf5d94ed..f60c5a76 100644 --- a/synergy/CClipboard.cpp +++ b/synergy/CClipboard.cpp @@ -5,9 +5,10 @@ // CClipboard // -CClipboard::CClipboard() +CClipboard::CClipboard() : m_open(false), m_owner(false) { open(0); + empty(); close(); } @@ -16,8 +17,10 @@ CClipboard::~CClipboard() // do nothing } -bool CClipboard::open(Time time) +bool CClipboard::empty() { + assert(m_open); + // clear all data for (SInt32 index = 0; index < kNumFormats; ++index) { m_data[index] = ""; @@ -25,60 +28,90 @@ bool CClipboard::open(Time time) } // save time + m_timeOwned = m_time; + + // we're the owner now + m_owner = true; + + return true; +} + +void CClipboard::add(EFormat format, const CString& data) +{ + assert(m_open); + assert(m_owner); + + m_data[format] = data; + m_added[format] = true; +} + +bool CClipboard::open(Time time) const +{ + assert(!m_open); + + m_open = true; m_time = time; return true; } -void CClipboard::close() +void CClipboard::close() const { - // do nothing -} + assert(m_open); -void CClipboard::add(EFormat format, const CString& data) -{ - m_data[format] = data; - m_added[format] = true; + m_open = false; } CClipboard::Time CClipboard::getTime() const { - return m_time; + return m_timeOwned; } bool CClipboard::has(EFormat format) const { + assert(m_open); return m_added[format]; } CString CClipboard::get(EFormat format) const { + assert(m_open); return m_data[format]; } -void CClipboard::copy(IClipboard* dst, const IClipboard* src) +bool CClipboard::copy(IClipboard* dst, const IClipboard* src) { assert(dst != NULL); assert(src != NULL); - copy(dst, src, src->getTime()); + return copy(dst, src, src->getTime()); } -void CClipboard::copy(IClipboard* dst, +bool CClipboard::copy(IClipboard* dst, const IClipboard* src, Time time) { assert(dst != NULL); assert(src != NULL); - if (dst->open(time)) { - for (SInt32 format = 0; format != IClipboard::kNumFormats; ++format) { - IClipboard::EFormat eFormat = (IClipboard::EFormat)format; - if (src->has(eFormat)) { - dst->add(eFormat, src->get(eFormat)); + bool success = false; + if (src->open(time)) { + if (dst->open(time)) { + if (dst->empty()) { + for (SInt32 format = 0; + format != IClipboard::kNumFormats; ++format) { + IClipboard::EFormat eFormat = (IClipboard::EFormat)format; + if (src->has(eFormat)) { + dst->add(eFormat, src->get(eFormat)); + } + } + success = true; } + dst->close(); } - dst->close(); + src->close(); } + + return success; } void CClipboard::unmarshall(const CString& data, Time time) @@ -87,6 +120,7 @@ void CClipboard::unmarshall(const CString& data, Time time) // clear existing data open(time); + empty(); // read the number of formats const UInt32 numFormats = readUInt32(index); diff --git a/synergy/CClipboard.h b/synergy/CClipboard.h index 734631f6..80fdec04 100644 --- a/synergy/CClipboard.h +++ b/synergy/CClipboard.h @@ -24,9 +24,10 @@ public: CString marshall() const; // IClipboard overrides - virtual bool open(Time); - virtual void close(); + virtual bool empty(); virtual void add(EFormat, const CString& data); + virtual bool open(Time) const; + virtual void close() const; virtual Time getTime() const; virtual bool has(EFormat) const; virtual CString get(EFormat) const; @@ -37,15 +38,19 @@ public: // clipboards can be of any concrete clipboard type (and // they don't have to be the same type). this also sets // the timestamp to time, if provided, or the time in src. - static void copy(IClipboard* dst, const IClipboard* src); - static void copy(IClipboard* dst, const IClipboard* src, Time); + // returns true iff the copy succeeded. + static bool copy(IClipboard* dst, const IClipboard* src); + static bool copy(IClipboard* dst, const IClipboard* src, Time); private: UInt32 readUInt32(const char*) const; void writeUInt32(CString*, UInt32) const; private: - Time m_time; + mutable bool m_open; + mutable Time m_time; + bool m_owner; + Time m_timeOwned; bool m_added[kNumFormats]; CString m_data[kNumFormats]; }; diff --git a/synergy/CXWindowsClipboard.cpp b/synergy/CXWindowsClipboard.cpp index a8b3823a..30ade009 100644 --- a/synergy/CXWindowsClipboard.cpp +++ b/synergy/CXWindowsClipboard.cpp @@ -1,42 +1,1297 @@ #include "CXWindowsClipboard.h" -#include "CString.h" +#include "CXWindowsUtil.h" #include "CLog.h" +#include "CStopwatch.h" +#include "CThread.h" +#include +#include // // CXWindowsClipboard // -CXWindowsClipboard::CXWindowsClipboard() +CXWindowsClipboard::CXWindowsClipboard(Display* display, + Window window, ClipboardID id) : + m_display(display), + m_window(window), + m_id(id), + m_open(false), + m_time(0), + m_owner(false), + m_timeOwned(0), + m_timeLost(0) { + // get some atoms + m_atomTargets = XInternAtom(m_display, "TARGETS", False); + m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False); + m_atomTimestamp = XInternAtom(m_display, "TIMESTAMP", False); + m_atomAtom = XInternAtom(m_display, "ATOM", False); + m_atomAtomPair = XInternAtom(m_display, "ATOM_PAIR", False); + m_atomInteger = XInternAtom(m_display, "INTEGER", False); + m_atomData = XInternAtom(m_display, "CLIP_TEMPORARY", False); + m_atomINCR = XInternAtom(m_display, "INCR", False); + m_atomString = XInternAtom(m_display, "STRING", False); + m_atomText = XInternAtom(m_display, "TEXT", False); + m_atomCompoundText = XInternAtom(m_display, "COMPOUND_TEXT", False); + m_atomMotifClipLock = XInternAtom(m_display, "_MOTIF_CLIP_LOCK", False); + m_atomMotifClipHeader = XInternAtom(m_display, "_MOTIF_CLIP_HEADER", False); + m_atomMotifClipAccess = XInternAtom(m_display, + "_MOTIF_CLIP_LOCK_ACCESS_VALID", False); + m_atomGDKSelection = XInternAtom(m_display, "GDK_SELECTION", False); + + // set selection atom based on clipboard id + switch (id) { + case kClipboardClipboard: + m_selection = XInternAtom(m_display, "CLIPBOARD", False); + break; + + case kClipboardSelection: + default: + m_selection = XA_PRIMARY; + break; + } + + // we have no data + clearCache(); } CXWindowsClipboard::~CXWindowsClipboard() { + clearReplies(); } -bool CXWindowsClipboard::open() +void CXWindowsClipboard::lost(Time time) { - log((CLOG_INFO "open clipboard")); + log((CLOG_DEBUG "lost clipboard %d ownership at %d", m_id, time)); + if (m_owner) { + m_owner = false; + m_timeLost = time; + clearCache(); + } +} + +void CXWindowsClipboard::addRequest( + Window owner, + Window requestor, Atom target, + ::Time time, Atom property) +{ + // must be for our window and we must have owned the selection + // at the given time. + bool success = false; + if (owner == m_window) { + log((CLOG_DEBUG "request for clipboard %d, target %d by 0x%08x (property=%d)", m_selection, target, requestor, property)); + if (wasOwnedAtTime(time)) { + if (target == m_atomMultiple) { + // add a multiple request. property may not be None + // according to ICCCM. + if (property != None) { + success = insertMultipleReply(requestor, time, property); + } + } + else { + addSimpleRequest(requestor, target, time, property); + + // addSimpleRequest() will have already handled failure + success = true; + } + } + else { + log((CLOG_DEBUG "failed, not owned at time %d", time)); + } + } + + if (!success) { + // send failure + log((CLOG_DEBUG "failed")); + insertReply(new CReply(requestor, target, time)); + } + + // send notifications that are pending + pushReplies(); +} + +bool CXWindowsClipboard::addSimpleRequest( + Window requestor, Atom target, + ::Time time, Atom property) +{ + // obsolete requestors may supply a None property. in + // that case we use the target as the property to store + // the conversion. + if (property == None) { + property = target; + } + + // handle targets + CString data; + Atom type = None; + int format = 0; + if (target == m_atomTargets) { + type = getTargetsData(data, &format); + } + else if (target == m_atomTimestamp) { + type = getTimestampData(data, &format); + } + else if (target == m_atomString || + target == m_atomText) { + type = getStringData(data, &format); + } + + if (type != None) { + // success + log((CLOG_DEBUG "success")); + insertReply(new CReply(requestor, target, time, + property, data, type, format)); + return true; + } + else { + // failure + log((CLOG_DEBUG "failed")); + insertReply(new CReply(requestor, target, time)); + return false; + } +} + +bool CXWindowsClipboard::processRequest( + Window requestor, + ::Time /*time*/, Atom property) +{ + CReplyMap::iterator index = m_replies.find(requestor); + if (index == m_replies.end()) { + // unknown requestor window + return false; + } + log((CLOG_DEBUG1 "received property %d delete from 0x08%x", property, requestor)); + + // find the property in the known requests. it should be the + // first property but we'll check 'em all if we have to. + CReplyList& replies = index->second; + for (CReplyList::iterator index2 = replies.begin(); + index2 != replies.end(); ++index2) { + CReply* reply = *index2; + if (reply->m_replied && reply->m_property == property) { + // if reply is complete then remove it and start the + // next one. + pushReplies(index, replies, index2); + return true; + } + } + + return false; +} + +bool CXWindowsClipboard::destroyRequest( + Window requestor) +{ + CReplyMap::iterator index = m_replies.find(requestor); + if (index == m_replies.end()) { + // unknown requestor window + return false; + } + + // destroy all replies for this window + clearReplies(index->second); + + // note -- we don't stop watching the window for events because + // we're called in response to the window being destroyed. + return true; } -void CXWindowsClipboard::close() +Window CXWindowsClipboard::getWindow() const { - log((CLOG_INFO "close clipboard")); + return m_window; +} + +Atom CXWindowsClipboard::getSelection() const +{ + return m_selection; +} + +bool CXWindowsClipboard::empty() +{ + assert(m_open); + + log((CLOG_DEBUG "empty clipboard %d", m_id)); + + // assert ownership of clipboard + XSetSelectionOwner(m_display, m_selection, m_window, m_time); + if (XGetSelectionOwner(m_display, m_selection) != m_window) { + log((CLOG_DEBUG "failed to grab clipboard %d", m_id)); + return false; + } + + // clear all data. since we own the data now, the cache is up + // to date. + clearCache(); + m_cached = true; + + // FIXME -- actually delete motif clipboard items? + // FIXME -- do anything to motif clipboard properties? + + // save time + m_timeOwned = m_time; + m_timeLost = 0; + + // we're the owner now + m_owner = true; + log((CLOG_DEBUG "grabbed clipboard %d", m_id)); + + return true; } void CXWindowsClipboard::add( EFormat format, const CString& data) { - log((CLOG_INFO "add clipboard format: %d\n%s", format, data.c_str())); + assert(m_open); + assert(m_owner); + + log((CLOG_DEBUG "add %d bytes to clipboard %d format: %d", data.size(), m_id, format)); + + m_data[format] = data; + m_added[format] = true; + + // FIXME -- set motif clipboard item? } -bool CXWindowsClipboard::has(EFormat /*format*/) const +bool CXWindowsClipboard::open(Time time) const { + assert(!m_open); + + log((CLOG_DEBUG "open clipboard %d", m_id)); + + // assume not motif + m_motif = false; + + // lock clipboard + if (m_id == kClipboardClipboard) { + if (!motifLockClipboard()) { + return false; + } + + // check if motif owns the selection. unlock motif clipboard + // if it does not. + m_motif = motifOwnsClipboard(); + log((CLOG_DEBUG1 "motif does %sown clipboard", m_motif ? "" : "not ")); + if (!m_motif) { + motifUnlockClipboard(); + } + } + + // now open + m_open = true; + m_time = time; + + // get the time the clipboard ownership was taken by the current + // owner. + if (m_motif) { + m_timeOwned = motifGetTime(); + } + else { + m_timeOwned = icccmGetTime(); + } + + // if we can't get the time then use the time passed to us + if (m_timeOwned == 0) { + m_timeOwned = m_time; + } + + // if the cache is dirty then flush it + if (m_timeOwned != m_cacheTime) { + clearCache(); + } + + return true; +} + +void CXWindowsClipboard::close() const +{ + assert(m_open); + + log((CLOG_DEBUG "close clipboard %d", m_id)); + + // unlock clipboard + if (m_motif) { + motifUnlockClipboard(); + } + + m_motif = false; + m_open = false; +} + +IClipboard::Time CXWindowsClipboard::getTime() const +{ + return m_timeOwned; +} + +bool CXWindowsClipboard::has(EFormat format) const +{ + assert(m_open); + + fillCache(); + return m_added[format]; +} + +CString CXWindowsClipboard::get(EFormat format) const +{ + assert(m_open); + + fillCache(); + return m_data[format]; +} + +IClipboard::EFormat CXWindowsClipboard::getFormat(Atom src) const +{ + // FIXME -- handle more formats (especially mime-type-like formats + // and various character encodings like unicode). + if (src == m_atomString || + src == m_atomText /*|| + src == m_atomCompoundText*/) + return IClipboard::kText; + return IClipboard::kNumFormats; +} + +void CXWindowsClipboard::clearCache() const +{ + const_cast(this)->doClearCache(); +} + +void CXWindowsClipboard::doClearCache() +{ + m_cached = false; + for (SInt32 index = 0; index < kNumFormats; ++index) { + m_data[index] = ""; + m_added[index] = false; + } +} + +void CXWindowsClipboard::fillCache() const +{ + // get the selection data if not already cached + if (!m_cached) { + const_cast(this)->doFillCache(); + } +} + +void CXWindowsClipboard::doFillCache() +{ + if (m_motif) { + motifFillCache(); + } + else { + icccmFillCache(); + } + m_cached = true; + m_cacheTime = m_timeOwned; +} + +void CXWindowsClipboard::icccmFillCache() +{ + log((CLOG_DEBUG "ICCCM fill clipboard %d", m_id)); + + // see if we can get the list of available formats from the selection. + // if not then use a default list of formats. + const Atom atomTargets = m_atomTargets; + Atom target; + CString data; + if (!icccmGetSelection(atomTargets, &target, &data)) { + log((CLOG_DEBUG1 "selection doesn't support TARGETS")); + data = ""; + + target = XA_STRING; + data.append(reinterpret_cast(&target), sizeof(target)); + } + + // try getting each format + const Atom* targets = reinterpret_cast(data.data()); + const UInt32 numTargets = data.size() / sizeof(Atom); + for (UInt32 i = 0; i < numTargets; ++i) { + // determine the expected clipboard format + Atom target = targets[i]; + IClipboard::EFormat expectedFormat = getFormat(target); + if (expectedFormat == IClipboard::kNumFormats) { + log((CLOG_DEBUG1 " no format for target %d", target)); + continue; + } + log((CLOG_DEBUG1 " source target %d -> %d", target, expectedFormat)); + + // skip already handled targets + if (m_added[expectedFormat]) { + log((CLOG_DEBUG1 " skipping handled format %d", expectedFormat)); + continue; + } + + Atom actualTarget; + CString targetData; + if (!icccmGetSelection(target, &actualTarget, &targetData)) { + log((CLOG_DEBUG1 " no data for target", target)); + continue; + } + logc(actualTarget != target, (CLOG_DEBUG1 " actual target is %d", actualTarget)); + + // use the actual format, not the expected + IClipboard::EFormat actualFormat = getFormat(actualTarget); + if (actualFormat == IClipboard::kNumFormats) { + log((CLOG_DEBUG1 " no format for target %d", actualTarget)); + continue; + } + if (m_added[actualFormat]) { + log((CLOG_DEBUG1 " skipping handled format %d", actualFormat)); + continue; + } + + // add to clipboard and note we've done it + m_data[actualFormat] = targetData; + m_added[actualFormat] = true; + log((CLOG_DEBUG " added format %d for target %d", actualFormat, target)); + } +} + +bool CXWindowsClipboard::icccmGetSelection( + Atom target, + Atom* actualTarget, + CString* data) const +{ + assert(actualTarget != NULL); + assert(data != NULL); + + // request data conversion + CICCCMGetClipboard getter(m_window, m_time, m_atomData); + if (!getter.readClipboard(m_display, m_selection, + target, actualTarget, data)) { + log((CLOG_DEBUG1 "can't get data for selection target %d", target)); + logc(getter.m_error, (CLOG_WARN "ICCCM violation by clipboard owner")); + return false; + } + else if (*actualTarget == None) { + log((CLOG_DEBUG1 "selection conversion failed for target %d", target)); + return false; + } + return true; +} + +IClipboard::Time CXWindowsClipboard::icccmGetTime() const +{ + Atom actualTarget; + CString data; + if (icccmGetSelection(m_atomTimestamp, &actualTarget, &data) && + actualTarget == m_atomTimestamp) { + Time time = *reinterpret_cast(data.data()); + log((CLOG_DEBUG1 "got ICCCM time %d", time)); + return time; + } + else { + // no timestamp + log((CLOG_DEBUG1 "can't get ICCCM time")); + return 0; + } +} + +bool CXWindowsClipboard::motifLockClipboard() const +{ + // fail if anybody owns the lock (even us, so this is non-recursive) + Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); + if (lockOwner != None) { + return false; + } + + // try to grab the lock + // FIXME -- is this right? there's a race condition here -- + // A grabs successfully, B grabs successfully, A thinks it + // still has the grab until it gets a SelectionClear. + Time time = CXWindowsUtil::getCurrentTime(m_display, m_window); + XSetSelectionOwner(m_display, m_atomMotifClipLock, m_window, time); + lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); + if (lockOwner != m_window) { + return false; + } + + log((CLOG_DEBUG1 "locked motif clipboard")); + return true; +} + +void CXWindowsClipboard::motifUnlockClipboard() const +{ + log((CLOG_DEBUG1 "unlocked motif clipboard")); + + // fail if we don't own the lock + Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); + if (lockOwner != m_window) { + return; + } + + // release lock + Time time = CXWindowsUtil::getCurrentTime(m_display, m_window); + XSetSelectionOwner(m_display, m_atomMotifClipLock, None, time); +} + +bool CXWindowsClipboard::motifOwnsClipboard() const +{ + // get the current selection owner + // FIXME -- this can't be right. even if the window is destroyed + // Motif will still have a valid clipboard. how can we tell if + // some other client owns CLIPBOARD? + Window owner = XGetSelectionOwner(m_display, m_selection); + if (owner == None) { + return false; + } + + // get the Motif clipboard header property from the root window + Atom target; + SInt32 format; + CString data; + Window root = RootWindow(m_display, DefaultScreen(m_display)); + if (!CXWindowsUtil::getWindowProperty(m_display, root, + m_atomMotifClipHeader, + &data, &target, &format, False)) { + return false; + } + if (target != m_atomMotifClipHeader) { + return false; + } + + // check the owner window against the current clipboard owner + const CMotifClipHeader* header = + reinterpret_cast(data.data()); + if (data.size() >= sizeof(CMotifClipHeader) && + header->m_id == kMotifClipHeader) { + if (header->m_selectionOwner == owner) { + return true; + } + } + return false; } -CString CXWindowsClipboard::get(EFormat /*format*/) const +void CXWindowsClipboard::motifFillCache() { - return CString(); + log((CLOG_DEBUG "Motif fill clipboard %d", m_id)); + + // get the Motif clipboard header property from the root window + Atom target; + SInt32 format; + CString data; + Window root = RootWindow(m_display, DefaultScreen(m_display)); + if (!CXWindowsUtil::getWindowProperty(m_display, root, + m_atomMotifClipHeader, + &data, &target, &format, False)) { + return; + } + if (target != m_atomMotifClipHeader) { + return; + } + + // check that the header is okay + const CMotifClipHeader* header = + reinterpret_cast(data.data()); + if (data.size() < sizeof(CMotifClipHeader) || + header->m_id != kMotifClipHeader || + header->m_numItems < 1) { + return; + } + + // get the Motif item property from the root window + char name[18 + 20]; + sprintf(name, "_MOTIF_CLIP_ITEM_%d", header->m_item); + Atom atomItem = XInternAtom(m_display, name, False); + data = ""; + if (!CXWindowsUtil::getWindowProperty(m_display, root, + atomItem, &data, + &target, &format, False)) { + return; + } + if (target != atomItem) { + return; + } + + // check that the item is okay + const CMotifClipItem* item = + reinterpret_cast(data.data()); + if (data.size() < sizeof(CMotifClipItem) || + item->m_id != kMotifClipItem || + item->m_numFormats < 1) { + return; + } + + // convert each available format + for (SInt32 i = 0; i < item->m_numFormats; ++i) { + // get Motif format property from the root window + sprintf(name, "_MOTIF_CLIP_ITEM_%d", item->m_formats[i]); + Atom atomFormat = XInternAtom(m_display, name, False); + CString data; + if (!CXWindowsUtil::getWindowProperty(m_display, root, + atomFormat, &data, + &target, &format, False)) { + continue; + } + if (target != atomFormat) { + continue; + } + + // check that the format is okay + const CMotifClipFormat* motifFormat = + reinterpret_cast(data.data()); + if (data.size() < sizeof(CMotifClipFormat) || + motifFormat->m_id != kMotifClipFormat || + motifFormat->m_length < 0 || + motifFormat->m_type == None) { + continue; + } + + // determine the expected clipboard format + Atom target = motifFormat->m_type; + IClipboard::EFormat expectedFormat = getFormat(target); + if (expectedFormat == IClipboard::kNumFormats) { + log((CLOG_DEBUG1 " no format for target %d", target)); + continue; + } + log((CLOG_DEBUG1 " source target %d -> %d", target, expectedFormat)); + + // skip already handled targets + if (m_added[expectedFormat]) { + log((CLOG_DEBUG1 " skipping handled format %d", expectedFormat)); + continue; + } + + // get the data (finally) + SInt32 length = motifFormat->m_length; + sprintf(name, "_MOTIF_CLIP_ITEM_%d", motifFormat->m_data); + Atom atomData = XInternAtom(m_display, name, False); + data = ""; + if (!CXWindowsUtil::getWindowProperty(m_display, root, + atomData, &data, + &target, &format, False)) { + continue; + } + if (target != atomData) { + continue; + } + + // truncate data to length specified in the format + data.erase(length); + + // add to clipboard and note we've done it + m_data[expectedFormat] = data; + m_added[expectedFormat] = true; + log((CLOG_DEBUG " added format %d for target %d", expectedFormat, motifFormat->m_type)); + } +} + +IClipboard::Time CXWindowsClipboard::motifGetTime() const +{ + // FIXME -- does Motif report this? + return 0; +} + +bool CXWindowsClipboard::insertMultipleReply( + Window requestor, ::Time time, Atom property) +{ + // get the requested targets + Atom target; + SInt32 format; + CString data; + if (!CXWindowsUtil::getWindowProperty(m_display, requestor, + property, &data, &target, &format, False)) { + // can't get the requested targets + return false; + } + + // fail if the requested targets isn't of the correct form + if (format != 32 || + target != m_atomAtomPair) { + return false; + } + + // data is a list of atom pairs: target, property + const Atom* targets = reinterpret_cast(data.data()); + const UInt32 numTargets = data.size() / sizeof(Atom); + + // add replies for each target + bool changed = false; + for (UInt32 i = 0; i < numTargets; i += 2) { + const Atom target = targets[i + 0]; + const Atom property = targets[i + 1]; + if (!addSimpleRequest(requestor, target, time, property)) { + // note that we can't perform the requested conversion + static const Atom none = None; + data.replace(i * sizeof(Atom), sizeof(Atom), + reinterpret_cast(&none), + sizeof(Atom)); + changed = true; + } + } + + // update the targets property if we changed it + if (changed) { + CXWindowsUtil::setWindowProperty(m_display, requestor, + property, data.data(), data.size(), + target, format); + } + + // add reply for MULTIPLE request + insertReply(new CReply(requestor, m_atomMultiple, + time, property, CString(), None, 32)); + + return true; +} + +void CXWindowsClipboard::insertReply(CReply* reply) +{ + assert(reply != NULL); + + // note -- we must respond to requests in order if requestor,target,time + // are the same, otherwise we can use whatever order we like with one + // exception: each reply in a MULTIPLE reply must be handled in order + // as well. those replies will almost certainly not share targets so + // we can't simply use requestor,target,time as map index. + // + // instead we'll use just the requestor. that's more restrictive than + // necessary but we're guaranteed to do things in the right order. + // note that we could also include the time in the map index and still + // ensure the right order. but since that'll just make it harder to + // find the right reply when handling property notify events we stick + // to just the requestor. + + const bool newWindow = (m_replies.count(reply->m_requestor) == 0); + m_replies[reply->m_requestor].push_back(reply); + + // adjust requestor's event mask if we haven't done so already. we + // want events in case the window is destroyed or any of its + // properties change. + if (newWindow) { + XWindowAttributes attr; + XGetWindowAttributes(m_display, reply->m_requestor, &attr); + XSelectInput(m_display, reply->m_requestor, attr.your_event_mask | + StructureNotifyMask | PropertyChangeMask); + m_eventMasks[reply->m_requestor] = attr.your_event_mask; + } +} + +void CXWindowsClipboard::pushReplies() +{ + // send the first reply for each window if that reply hasn't + // been sent yet. + for (CReplyMap::iterator index = m_replies.begin(); + index != m_replies.end(); ++index) { + assert(!index->second.empty()); + if (!index->second.front()->m_replied) { + pushReplies(index, index->second, index->second.begin()); + } + } +} + +void CXWindowsClipboard::pushReplies( + CReplyMap::iterator mapIndex, + CReplyList& replies, + CReplyList::iterator index) +{ + CReply* reply = *index; + while (sendReply(reply)) { + // reply is complete. discard it and send the next reply, + // if any. + index = replies.erase(index); + delete reply; + if (index == replies.end()) { + break; + } + reply = *index; + } + + // if there are no more replies in the list then remove the list + // and stop watching the requestor for events. + if (replies.empty()) { + Window requestor = mapIndex->first; + XSelectInput(m_display, requestor, m_eventMasks[requestor]); + m_replies.erase(mapIndex); + m_eventMasks.erase(requestor); + } +} + +bool CXWindowsClipboard::sendReply(CReply* reply) +{ + assert(reply != NULL); + + // bail out immediately if reply is done + if (reply->m_done) { + log((CLOG_DEBUG1 "clipboard: finished reply to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + return true; + } + + // start in failed state if property is None + bool failed = (reply->m_property == None); + if (!failed) { + log((CLOG_DEBUG1 "clipboard: setting property on 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + + // send using INCR if already sending incrementally or if reply + // is too large, otherwise just send it. + const UInt32 maxRequestSize = 4 * XMaxRequestSize(m_display); + const bool useINCR = (reply->m_data.size() > maxRequestSize); + + // send INCR reply if incremental and we haven't replied yet + if (useINCR && !reply->m_replied) { + UInt32 size = reply->m_data.size(); + if (!CXWindowsUtil::setWindowProperty(m_display, + reply->m_requestor, reply->m_property, + &size, 4, m_atomINCR, 32)) { + failed = true; + } + } + + // send more INCR reply or entire non-incremental reply + else { + // how much more data should we send? + UInt32 size = reply->m_data.size() - reply->m_ptr; + if (size > maxRequestSize) + size = maxRequestSize; + + // send it + if (!CXWindowsUtil::setWindowProperty(m_display, + reply->m_requestor, reply->m_property, + reply->m_data.data() + reply->m_ptr, + size, + reply->m_type, reply->m_format)) { + failed = true; + } + else { + reply->m_ptr += size; + + // we've finished the reply if we just sent the zero + // size incremental chunk or if we're not incremental. + reply->m_done = (size == 0 || !useINCR); + } + } + } + + // if we've failed then delete the property and say we're done. + // if we haven't replied yet then we can send a failure notify, + // otherwise we've failed in the middle of an incremental + // transfer; i don't know how to cancel that so i'll just send + // the final zero-length property. + // FIXME -- how do you gracefully cancel an incremental transfer? + if (failed) { + log((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + reply->m_done = true; + if (reply->m_property != None) { + XDeleteProperty(m_display, reply->m_requestor, reply->m_property); + } + + if (!reply->m_replied) { + sendNotify(reply->m_requestor, m_selection, + reply->m_target, None, + reply->m_time); + + // don't wait for any reply (because we're not expecting one) + return true; + } + else { + static const char dummy = 0; + CXWindowsUtil::setWindowProperty(m_display, + reply->m_requestor, reply->m_property, + &dummy, + 0, + reply->m_type, reply->m_format); + + // wait for delete notify + return false; + } + } + + // send notification if we haven't yet + if (!reply->m_replied) { + log((CLOG_DEBUG1 "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + reply->m_replied = true; + + // HACK -- work around apparent bug in lesstif, which doesn't + // wait around for the SelectionNotify then gets confused when + // it sees it the next time it requests the selection. if it + // looks like a lesstif requestor window then don't send the + // SelectionNotify. it looks like a lesstif requestor if: + // it has a _MOTIF_CLIP_LOCK_ACCESS_VALID property + // it does not have a GDK_SELECTION property + CString dummy; + if (!CXWindowsUtil::getWindowProperty(m_display, + reply->m_requestor, + m_atomMotifClipAccess, + &dummy, NULL, NULL, False) || + CXWindowsUtil::getWindowProperty(m_display, + reply->m_requestor, + m_atomGDKSelection, + &dummy, NULL, NULL, False)) { + sendNotify(reply->m_requestor, m_selection, + reply->m_target, reply->m_property, + reply->m_time); + } + } + + // wait for delete notify + return false; +} + +void CXWindowsClipboard::clearReplies() +{ + for (CReplyMap::iterator index = m_replies.begin(); + index != m_replies.end(); ++index) { + clearReplies(index->second); + } + m_replies.clear(); + m_eventMasks.clear(); +} + +void CXWindowsClipboard::clearReplies(CReplyList& replies) +{ + for (CReplyList::iterator index = replies.begin(); + index != replies.end(); ++index) { + delete *index; + } + replies.clear(); +} + +void CXWindowsClipboard::sendNotify( + Window requestor, Atom selection, + Atom target, Atom property, Time time) +{ + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = m_display; + event.xselection.requestor = requestor; + event.xselection.selection = selection; + event.xselection.target = target; + event.xselection.property = property; + event.xselection.time = time; + XSendEvent(m_display, requestor, False, 0, &event); +} + +bool CXWindowsClipboard::wasOwnedAtTime( + ::Time time) const +{ + // not owned if we've never owned the selection + if (m_timeOwned == 0) + return false; + + // if time is CurrentTime then return true if we still own the + // selection and false if we do not. else if we still own the + // selection then get the current time, otherwise use + // m_timeLost as the end time. + Time lost = m_timeLost; + if (m_timeLost == 0) + if (time == CurrentTime) + return true; + else + lost = CXWindowsUtil::getCurrentTime(m_display, m_window); + else + if (time == CurrentTime) + return false; + + // compare time to range + Time duration = lost - m_timeOwned; + Time when = time - m_timeOwned; + return (/*when >= 0 &&*/ when < duration); +} + +Atom CXWindowsClipboard::getTargetsData( + CString& data, int* format) const +{ + assert(format != NULL); + + // construct response + Atom atom; + atom = m_atomTargets; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + atom = m_atomMultiple; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + atom = m_atomTimestamp; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + if (m_added[kText]) { + atom = m_atomString; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + atom = m_atomText; + data.append(reinterpret_cast(&atom), sizeof(Atom)); + } + + *format = 32; + return m_atomTargets; +} + +Atom CXWindowsClipboard::getTimestampData( + CString& data, int* format) const +{ + assert(format != NULL); + + assert(sizeof(m_timeOwned) == 4); + data.append(reinterpret_cast(&m_timeOwned), 4); + *format = 32; + return m_atomTimestamp; +} + +Atom CXWindowsClipboard::getStringData( + CString& data, int* format) const +{ + assert(format != NULL); + + if (m_added[kText]) { + data = m_data[kText]; + *format = 8; + return m_atomString; + } + else { + return None; + } +} + + +// +// CXWindowsClipboard::CICCCMGetClipboard +// + +CXWindowsClipboard::CICCCMGetClipboard::CICCCMGetClipboard( + Window requestor, Time time, Atom property) : + m_requestor(requestor), + m_time(time), + m_property(property), + m_incr(false), + m_failed(false), + m_done(false), + m_reading(false), + m_data(NULL), + m_actualTarget(NULL), + m_error(false) +{ + // do nothing +} + +CXWindowsClipboard::CICCCMGetClipboard::~CICCCMGetClipboard() +{ + // do nothing +} + +bool CXWindowsClipboard::CICCCMGetClipboard::readClipboard( + Display* display, + Atom selection, Atom target, + Atom* actualTarget, CString* data) +{ + assert(actualTarget != NULL); + assert(data != NULL); + + log((CLOG_DEBUG1 "request selection=%d, target=%d, window=%x", selection, target, m_requestor)); + + // save output pointers + m_actualTarget = actualTarget; + m_data = data; + + // assume failure + *m_actualTarget = None; + *m_data = ""; + + // delete target property + XDeleteProperty(display, m_requestor, m_property); + + // select window for property changes + XWindowAttributes attr; + XGetWindowAttributes(display, m_requestor, &attr); + XSelectInput(display, m_requestor, + attr.your_event_mask | PropertyChangeMask); + + // request data conversion + XConvertSelection(display, selection, target, + m_property, m_requestor, m_time); + + // process selection events. use a timeout so we don't get + // screwed by a bad selection owner. + CStopwatch timer(true); + XEvent xevent; + while (!m_done && !m_failed) { + // return false if we've timed-out + if (timer.getTime() >= 0.2) { + log((CLOG_DEBUG1 "request timed out")); + XSelectInput(display, m_requestor, attr.your_event_mask); + return false; + } + + // process events + if (!XCheckIfEvent(display, &xevent, + &CXWindowsClipboard::CICCCMGetClipboard::eventPredicate, + reinterpret_cast(this))) { + // wait a bit to avoid spinning + CThread::sleep(0.05); + } + } + + // restore mask + XSelectInput(display, m_requestor, attr.your_event_mask); + + // return success or failure + log((CLOG_DEBUG1 "request %s", m_failed ? "failed" : "succeeded")); + return !m_failed; +} + +bool CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate( + Display* display, + XEvent* xevent) +{ + // process event + switch (xevent->type) { + case DestroyNotify: + if (xevent->xdestroywindow.window == m_requestor) { + m_failed = true; + return true; + } + + // not interested + return false; + + case SelectionNotify: + if (xevent->xselection.requestor == m_requestor) { + // done if we can't convert + if (xevent->xselection.property == None) { + m_done = true; + return true; + } + + // proceed if conversion successful + else if (xevent->xselection.property == m_property) { + m_reading = true; + break; + } + } + + // otherwise not interested + return false; + + case PropertyNotify: + // proceed if conversion successful and we're receiving more data + if (xevent->xproperty.window == m_requestor && + xevent->xproperty.atom == m_property && + xevent->xproperty.state == PropertyNewValue) { + if (!m_reading) { + return false; + } + break; + } + + // otherwise not interested + return false; + + default: + // not interested + return false; + } + + // get the data from the property + Atom target; + const CString::size_type oldSize = m_data->size(); + if (!CXWindowsUtil::getWindowProperty(display, m_requestor, + m_property, m_data, &target, NULL, True)) { + // unable to read property + m_failed = true; + return True; + } + + // note if incremental. if we're already incremental then the + // selection owner is busted. if the INCR property has no size + // then the selection owner is busted. + if (target == XInternAtom(display, "INCR", False)) { +log((CLOG_INFO " INCR")); // FIXME + if (m_incr) { +log((CLOG_INFO " INCR repeat")); // FIXME + m_failed = true; + m_error = true; + } + else if (m_data->size() == oldSize) { +log((CLOG_INFO " INCR zero size")); // FIXME + m_failed = true; + m_error = true; + } + else { +log((CLOG_INFO " INCR start")); // FIXME + m_incr = true; + + // discard INCR data + *m_data = ""; + } + } + + // handle incremental chunks + else if (m_incr) { + // if first incremental chunk then save target + if (oldSize == 0) { + log((CLOG_DEBUG1 " INCR first chunk, target %d", target)); + *m_actualTarget = target; + } + + // secondary chunks must have the same target + else { +log((CLOG_INFO " INCR secondary chunk")); // FIXME + if (target != *m_actualTarget) { + log((CLOG_WARN " INCR target mismatch")); + m_failed = true; + m_error = true; + } + } + + // note if this is the final chunk + if (m_data->size() == oldSize) { + log((CLOG_DEBUG1 " INCR final chunk: %d bytes total", m_data->size())); + m_done = true; + } + } + + // not incremental; save the target. + else { + log((CLOG_DEBUG1 " target %d", target)); + *m_actualTarget = target; + m_done = true; + } + + // say we're not interested in this event if the conversion is + // incremental. that'll cause this method to be called again + // when there's more data. we finally finish the incremental + // copy when we read a 0 byte property. + logc(!m_incr, (CLOG_DEBUG1 " got data, %d bytes", m_data->size())); + return !m_incr; +} + +Bool CXWindowsClipboard::CICCCMGetClipboard::eventPredicate( + Display* display, + XEvent* xevent, + XPointer arg) +{ + CICCCMGetClipboard* self = reinterpret_cast(arg); + return self->doEventPredicate(display, xevent) ? True : False; +} + + +// +// CXWindowsClipboard::CReply +// + +CXWindowsClipboard::CReply::CReply(Window requestor, + Atom target, ::Time time) : + m_requestor(requestor), + m_target(target), + m_time(time), + m_property(None), + m_replied(false), + m_done(false), + m_data(), + m_type(None), + m_format(32), + m_ptr(0) +{ + // do nothing +} + +CXWindowsClipboard::CReply::CReply(Window requestor, + Atom target, ::Time time, Atom property, + const CString& data, Atom type, int format) : + m_requestor(requestor), + m_target(target), + m_time(time), + m_property(property), + m_replied(false), + m_done(false), + m_data(data), + m_type(type), + m_format(format), + m_ptr(0) +{ + // do nothing } diff --git a/synergy/CXWindowsClipboard.h b/synergy/CXWindowsClipboard.h index 7bdfb08c..bb0fde8e 100644 --- a/synergy/CXWindowsClipboard.h +++ b/synergy/CXWindowsClipboard.h @@ -2,18 +2,262 @@ #define CXWINDOWSCLIPBOARD_H #include "IClipboard.h" +#include "ClipboardTypes.h" +#include "CString.h" +#include +#include +#include class CXWindowsClipboard : public IClipboard { public: - CXWindowsClipboard(); + CXWindowsClipboard(Display*, Window, ClipboardID); virtual ~CXWindowsClipboard(); + // tell clipboard it lost ownership + void lost(Time); + + // add a selection request to the request list. if the given + // owner window isn't this clipboard's window then this simply + // sends a failure event to the requestor. + void addRequest(Window owner, + Window requestor, Atom target, + ::Time time, Atom property); + + // continue processing a selection request. returns true if the + // request was handled, false if the request was unknown. + bool processRequest(Window requestor, + ::Time time, Atom property); + + // terminate a selection request. returns true iff the request + // was known and handled. + bool destroyRequest(Window requestor); + + // get the clipboard's window + Window getWindow() const; + + // get the clipboard selection atom + Atom getSelection() const; + // IClipboard overrides - virtual bool open(); - virtual void close(); + virtual bool empty(); virtual void add(EFormat, const CString& data); + virtual bool open(Time) const; + virtual void close() const; + virtual Time getTime() const; virtual bool has(EFormat) const; virtual CString get(EFormat) const; + +private: + // convert target atom to clipboard format + EFormat getFormat(Atom target) const; + + // add a non-MULTIPLE request. does not verify that the selection + // was owned at the given time. returns true if the conversion + // could be performed, false otherwise. in either case, the + // reply is inserted. + bool addSimpleRequest( + Window requestor, Atom target, + ::Time time, Atom property); + + // clear the cache, resetting the cached flag and the added flag for + // each format. + void clearCache() const; + void doClearCache(); + + // cache all formats of the selection + void fillCache() const; + void doFillCache(); + + // ICCCM interoperability methods + void icccmFillCache(); + bool icccmGetSelection(Atom target, + Atom* actualTarget, + CString* data) const; + Time icccmGetTime() const; + + // motif interoperability methods + bool motifLockClipboard() const; + void motifUnlockClipboard() const; + bool motifOwnsClipboard() const; + Time motifGetTime() const; + void motifFillCache(); + // FIXME + + // + // helper classes + // + + // read an ICCCM conforming selection + class CICCCMGetClipboard { + public: + CICCCMGetClipboard(Window requestor, Time time, Atom property); + ~CICCCMGetClipboard(); + + // convert the given selection to the given type. returns + // true iff the conversion was successful or the conversion + // cannot be performed (in which case *actualTarget == None). + bool readClipboard(Display* display, + Atom selection, Atom target, + Atom* actualTarget, CString* data); + + private: + bool doEventPredicate(Display* display, + XEvent* event); + static Bool eventPredicate(Display* display, + XEvent* event, + XPointer arg); + + private: + Window m_requestor; + Time m_time; + Atom m_property; + bool m_incr; + bool m_failed; + bool m_done; + + // true iff we've received the selection notify + bool m_reading; + + // the converted selection data + CString* m_data; + + // the actual type of the data. if this is None then the + // selection owner cannot convert to the requested type. + Atom* m_actualTarget; + + public: + // true iff the selection owner didn't follow ICCCM conventions + bool m_error; + }; + + // Motif structure IDs + enum { kMotifClipFormat = 1, kMotifClipItem, kMotifClipHeader }; + + // _MOTIF_CLIP_HEADER structure + class CMotifClipHeader { + public: + SInt32 m_id; // kMotifClipHeader + SInt32 m_pad1[3]; + SInt32 m_item; + SInt32 m_pad2[4]; + SInt32 m_numItems; + SInt32 m_pad3[3]; + Window m_selectionOwner; + SInt32 m_pad4[2]; + SInt32 m_items[1]; // m_numItems items + }; + + // Motif clip item structure + class CMotifClipItem { + public: + SInt32 m_id; // kMotifClipItem + SInt32 m_pad1[6]; + SInt32 m_numFormats; + SInt32 m_pad2[7]; + SInt32 m_formats[1]; // m_numFormats formats + }; + + // Motif clip format structure + class CMotifClipFormat { + public: + SInt32 m_id; // kMotifClipFormat + SInt32 m_pad1[6]; + SInt32 m_length; + SInt32 m_data; + Atom m_type; + SInt32 m_pad2[6]; + }; + + // stores data needed to respond to a selection request + class CReply { + public: + CReply(Window, Atom target, ::Time); + CReply(Window, Atom target, ::Time, Atom property, + const CString& data, Atom type, int format); + + public: + // information about the request + Window m_requestor; + Atom m_target; + ::Time m_time; + Atom m_property; + + // true iff we've sent the notification for this reply + bool m_replied; + + // true iff the reply has sent its last message + bool m_done; + + // the data to send and its type and format + CString m_data; + Atom m_type; + int m_format; + + // index of next byte in m_data to send + UInt32 m_ptr; + }; + typedef std::list CReplyList; + typedef std::map CReplyMap; + typedef std::map CReplyEventMask; + + // reply methods + bool insertMultipleReply(Window, ::Time, Atom); + void insertReply(CReply*); + void pushReplies(); + void pushReplies(CReplyMap::iterator, + CReplyList&, CReplyList::iterator); + bool sendReply(CReply*); + void clearReplies(); + void clearReplies(CReplyList&); + void sendNotify(Window requestor, Atom selection, + Atom target, Atom property, Time time); + bool wasOwnedAtTime(::Time) const; + + // data conversion methods + Atom getTargetsData(CString&, int* format) const; + Atom getTimestampData(CString&, int* format) const; + Atom getStringData(CString&, int* format) const; + +private: + Display* m_display; + Window m_window; + ClipboardID m_id; + Atom m_selection; + mutable bool m_open; + mutable Time m_time; + bool m_owner; + mutable Time m_timeOwned; + Time m_timeLost; + + // true iff open and clipboard owned by a motif app + mutable bool m_motif; + + // the added/cached clipboard data + bool m_cached; + Time m_cacheTime; + bool m_added[kNumFormats]; + CString m_data[kNumFormats]; + + // conversion request replies + CReplyMap m_replies; + CReplyEventMask m_eventMasks; + + // atoms we'll need + Atom m_atomTargets; + Atom m_atomMultiple; + Atom m_atomTimestamp; + Atom m_atomAtom; + Atom m_atomAtomPair; + Atom m_atomInteger; + Atom m_atomData; + Atom m_atomINCR; + Atom m_atomString; + Atom m_atomText; + Atom m_atomCompoundText; + Atom m_atomMotifClipLock; + Atom m_atomMotifClipHeader; + Atom m_atomMotifClipAccess; + Atom m_atomGDKSelection; }; #endif diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 377a3da7..33d716b1 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -1,22 +1,18 @@ #include "CXWindowsScreen.h" -#include "CThread.h" +#include "CXWindowsClipboard.h" +#include "CXWindowsUtil.h" +#include "CClipboard.h" #include "CLock.h" -#include "TMethodJob.h" #include "CLog.h" #include "CString.h" -#include "CStopwatch.h" +#include "CThread.h" #include #include -#include -#include -#include // // CXWindowsScreen // -static const UInt32 kMaxRequestSize = 4096; - CXWindowsScreen::CXWindowsScreen() : m_display(NULL), m_root(None), @@ -53,27 +49,13 @@ void CXWindowsScreen::openDisplay() // get the root window m_root = RootWindow(m_display, m_screen); - // get some atoms - m_atomTargets = XInternAtom(m_display, "TARGETS", False); - m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False); - m_atomTimestamp = XInternAtom(m_display, "TIMESTAMP", False); - m_atomAtom = XInternAtom(m_display, "ATOM", False); - m_atomAtomPair = XInternAtom(m_display, "ATOM_PAIR", False); - m_atomInteger = XInternAtom(m_display, "INTEGER", False); - m_atomData = XInternAtom(m_display, "DESTINATION", False); - m_atomINCR = XInternAtom(m_display, "INCR", False); - m_atomString = XInternAtom(m_display, "STRING", False); - m_atomText = XInternAtom(m_display, "TEXT", False); - m_atomCompoundText = XInternAtom(m_display, "COMPOUND_TEXT", False); - m_atomSynergyTime = XInternAtom(m_display, "SYNERGY_TIME", False); - - // clipboard atoms - m_atomClipboard[kClipboardClipboard] = - XInternAtom(m_display, "CLIPBOARD", False); - m_atomClipboard[kClipboardSelection] = XA_PRIMARY; - // let subclass prep display onOpenDisplay(); + + // initialize clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = createClipboard(id); + } } void CXWindowsScreen::closeDisplay() @@ -83,19 +65,9 @@ void CXWindowsScreen::closeDisplay() // let subclass close down display onCloseDisplay(); - // clear out the clipboard request lists + // destroy clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - CClipboardInfo& clipboard = m_clipboards[id]; - for (CRequestMap::iterator index = clipboard.m_requests.begin(); - index != clipboard.m_requests.end(); ++index) { - CRequestList* list = index->second; - for (CRequestList::iterator index2 = list->begin(); - index2 != list->end(); ++index2) { - delete *index2; - } - delete list; - } - clipboard.m_requests.clear(); + delete m_clipboard[id]; } // close the display @@ -166,19 +138,27 @@ bool CXWindowsScreen::getEvent(XEvent* xevent) const // wait for an event in a cancellable way and don't lock the // display while we're waiting. m_mutex.lock(); - while (!m_stop && XPending(m_display) == 0) { - m_mutex.unlock(); - CThread::sleep(0.05); - m_mutex.lock(); - } - if (m_stop) { - m_mutex.unlock(); - return false; - } - else { - XNextEvent(m_display, xevent); - m_mutex.unlock(); - return true; + for (;;) { + while (!m_stop && XPending(m_display) == 0) { + m_mutex.unlock(); + CThread::sleep(0.05); + m_mutex.lock(); + } + if (m_stop) { + m_mutex.unlock(); + return false; + } + else { + // get the event + XNextEvent(m_display, xevent); + + // process the event. return the event if unhandled. + m_mutex.unlock(); + if (!const_cast(this)->processEvent(xevent)) { + return true; + } + m_mutex.lock(); + } } } @@ -188,544 +168,147 @@ void CXWindowsScreen::doStop() m_stop = true; } -ClipboardID CXWindowsScreen::getClipboardID(Atom selection) +ClipboardID CXWindowsScreen::getClipboardID(Atom selection) const { - for (ClipboardID id = 0; id < kClipboardEnd; ++id) - if (selection == m_atomClipboard[id]) + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_clipboard[id] != NULL && + m_clipboard[id]->getSelection() == selection) { return id; + } + } return kClipboardEnd; } -bool CXWindowsScreen::lostClipboard( - Atom selection, Time timestamp) +bool CXWindowsScreen::processEvent(XEvent* xevent) { - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - if (selection == m_atomClipboard[id]) { - // note the time - CLock lock(&m_mutex); - m_clipboards[id].m_lostClipboard = timestamp; - log((CLOG_INFO "lost clipboard %d ownership at %d", id, timestamp)); + switch (xevent->type) { + case SelectionClear: { + // we just lost the selection. that means someone else + // grabbed the selection so this screen is now the + // selection owner. report that to the subclass. + ClipboardID id = getClipboardID(xevent->xselectionclear.selection); + if (id != kClipboardEnd) { + log((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); + m_clipboard[id]->lost(xevent->xselectionclear.time); + onLostClipboard(id); return true; } + break; } - return false; -} -bool CXWindowsScreen::setDisplayClipboard( - ClipboardID id, - const IClipboard* clipboard, - Window requestor, Time timestamp) -{ - CLock lock(&m_mutex); - - XSetSelectionOwner(m_display, m_atomClipboard[id], requestor, timestamp); - if (XGetSelectionOwner(m_display, m_atomClipboard[id]) == requestor) { - // we got the selection - log((CLOG_INFO "grabbed clipboard at %d", timestamp)); - m_clipboards[id].m_lostClipboard = CurrentTime; - if (clipboard != NULL) { - // save clipboard to serve requests - CClipboard::copy(&m_clipboards[id].m_clipboard, - clipboard, timestamp); - } - else { - // clear clipboard - if (m_clipboards[id].m_clipboard.open(timestamp)) { - m_clipboards[id].m_clipboard.close(); - } + case SelectionNotify: + // notification of selection transferred. we shouldn't + // get this here because we handle them in the selection + // retrieval methods. we'll just delete the property + // with the data (satisfying the usual ICCCM protocol). + if (xevent->xselection.property != None) { + CLock lock(&m_mutex); + XDeleteProperty(m_display, + xevent->xselection.requestor, + xevent->xselection.property); } + return true; + case SelectionRequest: { + // somebody is asking for clipboard data + ClipboardID id = getClipboardID(xevent->xselectionrequest.selection); + if (id != kClipboardEnd) { + CLock lock(&m_mutex); + m_clipboard[id]->addRequest( + xevent->xselectionrequest.owner, + xevent->xselectionrequest.requestor, + xevent->xselectionrequest.target, + xevent->xselectionrequest.time, + xevent->xselectionrequest.property); + return true; + } + break; + } + + case PropertyNotify: + // property delete may be part of a selection conversion + if (xevent->xproperty.state == PropertyDelete) { + processClipboardRequest(xevent->xproperty.window, + xevent->xproperty.time, + xevent->xproperty.atom); + return true; + } + break; + + case DestroyNotify: + // looks like one of the windows that requested a clipboard + // transfer has gone bye-bye. + destroyClipboardRequest(xevent->xdestroywindow.window); return true; } return false; } -void CXWindowsScreen::getDisplayClipboard( +bool CXWindowsScreen::setDisplayClipboard( ClipboardID id, - IClipboard* clipboard, - Window requestor, Time timestamp) const + const IClipboard* clipboard) { - assert(clipboard != NULL); - assert(requestor != None); - - // block others from using the display while we get the clipboard. - // in particular, this prevents the event thread from stealing the - // selection notify event we're expecting. CLock lock(&m_mutex); - Atom selection = m_atomClipboard[id]; - // if we're trying to request the clipboard from the same - // unresponsive owner then immediately give up. this is lame - // because we can't be sure the owner won't become responsive; - // we can only ask the X server for the current owner, not the - // owner at time timestamp, allowing a race condition; and we - // don't detect if the owner window is destroyed in order to - // reset the unresponsive flag. - Window owner = XGetSelectionOwner(m_display, selection); - if (m_clipboards[id].m_unresponsive) { - if (owner != None && owner == m_clipboards[id].m_owner) { - log((CLOG_DEBUG1 "skip unresponsive clipboard owner")); - // clear the clipboard and return - if (!clipboard->open(timestamp)) { - clipboard->close(); - } - return; - } - } - CClipboardInfo& clipboardInfo = - const_cast(m_clipboards[id]); - - // don't update clipboard object if clipboard hasn't changed. ask - // the selection for the tiemstamp when it acquired the selection. - Atom format; -/* XXX -- timestamp not always updated when clipboard is changed - CString data; - if (getDisplayClipboard(selection, m_atomTimestamp, - requestor, timestamp, &format, &data) && - format == m_atomInteger) { - // get the owner's time - Time time = *reinterpret_cast(data.data()); - log((CLOG_DEBUG "got clipboard timestamp %08x", time)); - - // if unchanged then clipboard hasn't changed - if (time == clipboard->getTime()) - return; - - // use clipboard owner's time as timestamp - timestamp = time; - } -*/ - - // clear the clipboard object - if (!clipboard->open(timestamp)) { - return; + // fail if we don't have the requested clipboard + if (m_clipboard[id] == NULL) { + return false; } - // ask the selection for all the formats it has. some owners return - // the TARGETS atom and some the ATOM atom when TARGETS is requested. - CString targets; - if (getDisplayClipboard(selection, m_atomTargets, - requestor, timestamp, &format, &targets) && - (format == m_atomTargets || format == XA_ATOM)) { - // save owner info - clipboardInfo.m_owner = owner; - clipboardInfo.m_unresponsive = false; + // get the actual time. ICCCM does not allow CurrentTime. + Time timestamp = CXWindowsUtil::getCurrentTime( + m_display, m_clipboard[id]->getWindow()); - // get each target (that we can interpret). some owners return - // some targets multiple times in the list so don't try to get - // those multiple times. - const Atom* targetAtoms = reinterpret_cast(targets.data()); - const SInt32 numTargets = targets.size() / sizeof(Atom); - std::set clipboardFormats; - std::set targets; - log((CLOG_INFO "getting selection %d with %d targets", id, numTargets)); - for (SInt32 i = 0; i < numTargets; ++i) { - Atom format = targetAtoms[i]; - log((CLOG_DEBUG1 " source target %d", format)); - - // skip already handled targets - if (targets.count(format) > 0) { - log((CLOG_DEBUG1 " skipping handled target %d", format)); - continue; - } - - // mark this target as done - targets.insert(format); - - // determine the expected clipboard format - IClipboard::EFormat expectedFormat = getFormat(format); - - // if we can use the format and we haven't already retrieved - // it then get it - if (expectedFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG1 " no format for target", format)); - continue; - } - if (clipboardFormats.count(expectedFormat) > 0) { - log((CLOG_DEBUG1 " skipping handled format %d", expectedFormat)); - continue; - } - - CString data; - if (!getDisplayClipboard(selection, format, - requestor, timestamp, &format, &data)) { - log((CLOG_DEBUG1 " no data for target", format)); - continue; - } - - // use the actual format, not the expected - IClipboard::EFormat actualFormat = getFormat(format); - if (actualFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG1 " no format for target", format)); - continue; - } - if (clipboardFormats.count(actualFormat) > 0) { - log((CLOG_DEBUG1 " skipping handled format %d", actualFormat)); - continue; - } - - // add to clipboard and note we've done it - clipboard->add(actualFormat, data); - clipboardFormats.insert(actualFormat); - log((CLOG_INFO " added format %d for target %d", actualFormat, format)); - } + if (clipboard != NULL) { + // save clipboard data + return CClipboard::copy(m_clipboard[id], clipboard, timestamp); } else { - // non-ICCCM conforming selection owner. try TEXT format. - // FIXME - - // save owner info - clipboardInfo.m_owner = owner; - clipboardInfo.m_unresponsive = true; - - log((CLOG_DEBUG1 "selection doesn't support TARGETS, format is %d", format)); + // assert clipboard ownership + if (!m_clipboard[id]->open(timestamp)) { + return false; + } + m_clipboard[id]->empty(); + m_clipboard[id]->close(); + return true; } - - // done with clipboard - clipboard->close(); } bool CXWindowsScreen::getDisplayClipboard( - Atom selection, Atom type, - Window requestor, Time timestamp, - Atom* outputType, CString* outputData) const + ClipboardID id, + IClipboard* clipboard) const { - assert(outputType != NULL); - assert(outputData != NULL); + assert(clipboard != NULL); - // FIXME -- this doesn't work to retrieve Motif selections. - - // delete data property - XDeleteProperty(m_display, requestor, m_atomData); - - // request data conversion - XConvertSelection(m_display, selection, type, - m_atomData, requestor, timestamp); - - // wait for the selection notify event. can't just mask out other - // events because X stupidly doesn't provide a mask for selection - // events, so we use a predicate to find our event. we also set - // a time limit for a response so we're not screwed by a bad - // clipboard owner. - CStopwatch timer(true); - XEvent xevent; - while (XCheckIfEvent(m_display, &xevent, - &CXWindowsScreen::findSelectionNotify, - (XPointer)&requestor) != True) { - // return false if we've timed-out - if (timer.getTime() >= 0.2) - return false; - - // wait a bit - CThread::sleep(0.05); - } - assert(xevent.type == SelectionNotify); - assert(xevent.xselection.requestor == requestor); - - // make sure the transfer worked - Atom property = xevent.xselection.property; - if (property == None) { - // cannot convert - *outputType = type; - log((CLOG_DEBUG "selection conversion failed for %d", type)); - return false; - } - - // get the data and discard the property - SInt32 datumSize; - CString data; - bool okay = getData(requestor, property, outputType, &datumSize, &data); - XDeleteProperty(m_display, requestor, property); - - // fail if we couldn't get the data - if (!okay) { - log((CLOG_DEBUG "can't get data for selection format %d", type)); - return false; - } - - // handle INCR type specially. it means we'll be receiving the data - // piecemeal so we just loop until we've collected all the data. - if (*outputType == m_atomINCR) { - log((CLOG_DEBUG1 "selection data for format %d is incremental", type)); - // the data is a lower bound on the amount of data to be - // transferred. use it as a hint to size our buffer. - UInt32 size; - switch (datumSize) { - case 8: - size = *(reinterpret_cast(data.data())); - break; - - case 16: - size = *(reinterpret_cast(data.data())); - break; - - case 32: - size = *(reinterpret_cast(data.data())); - break; - - default: - assert(0 && "invalid datum size"); - } - - // empty the buffer and reserve the lower bound - data.erase(); - data.reserve(size); - - // look for property notify events with the following - CPropertyNotifyInfo filter; - filter.m_window = requestor; - filter.m_property = property; - - // now enter the INCR loop - bool error = false; - *outputType = (Atom)0; - for (;;) { - // wait for more data - while (XCheckIfEvent(m_display, &xevent, - &CXWindowsScreen::findPropertyNotify, - (XPointer)&filter) != True) { - // wait a bit - CThread::sleep(0.05); - } - assert(xevent.type == PropertyNotify); - assert(xevent.xproperty.window == requestor); - assert(xevent.xproperty.atom == property); - - // get the additional data then delete the property to - // ask the clipboard owner for the next chunk. - Atom newType; - CString newData; - okay = getData(requestor, property, &newType, NULL, &newData); - XDeleteProperty(m_display, requestor, property); - - // transfer has failed if we can't get the data - if (!okay) - error = true; - - // a zero length property means we got the last chunk - if (newData.size() == 0) - break; - - // if this is the first chunk then save the type. otherwise - // note that the new type is the same as the first chunk's - // type. if they're not the the clipboard owner is busted - // but we have to continue the transfer because there's no - // way to cancel it. - if (*outputType == (Atom)0) - *outputType = newType; - else if (*outputType != newType) - error = true; - - // append the data - data += newData; - } - - // if there was an error we could say the transferred failed - // but we'll be liberal in what we accept. - if (error) { - log((CLOG_WARN "ICCCM violation by clipboard owner")); -// return false; - } - } - - *outputData = data; - return true; -} - -bool CXWindowsScreen::getData( - Window window, Atom property, - Atom* type, SInt32* datumSize, - CString* data) const -{ - assert(type != NULL); - assert(data != NULL); - - // clear out any existing data - data->erase(); - - // read the property - long offset = 0; - long length = 8192 / 4; - for (;;) { - // get more data - int actualDatumSize; - unsigned long numItems, bytesLeft; - unsigned char* rawData; - const int result = XGetWindowProperty(m_display, window, property, - offset, length, False, AnyPropertyType, - type, &actualDatumSize, - &numItems, &bytesLeft, - &rawData); - if (result != Success) { - // failed - return false; - } - - // save datum size - if (datumSize != NULL) - *datumSize = (SInt32)actualDatumSize; - const SInt32 bytesPerDatum = (SInt32)actualDatumSize / 8; - - // advance read pointer. since we can only read at offsets that - // are multiples of 4 byte we take care to write multiples of 4 - // bytes to data, except when we've retrieved the last chunk. - SInt32 quadCount = (numItems * bytesPerDatum) / 4; - offset += quadCount; - - // append data - if (bytesLeft == 0) - data->append((char*)rawData, bytesPerDatum * numItems); - else - data->append((char*)rawData, 4 * quadCount); - - // done with returned data - XFree(rawData); - - // done if no data is left - if (bytesLeft == 0) - return true; - } -} - -IClipboard::EFormat CXWindowsScreen::getFormat(Atom src) const -{ - // FIXME -- handle more formats (especially mime-type-like formats - // and various character encodings like unicode). - if (src == XA_STRING || - src == m_atomText || - src == m_atomCompoundText) - return IClipboard::kText; - return IClipboard::kNumFormats; -} - -Bool CXWindowsScreen::findSelectionNotify( - Display*, XEvent* xevent, XPointer arg) -{ - Window requestor = *reinterpret_cast(arg); - return (xevent->type == SelectionNotify && - xevent->xselection.requestor == requestor) ? True : False; -} - -Bool CXWindowsScreen::findPropertyNotify( - Display*, XEvent* xevent, XPointer arg) -{ - CPropertyNotifyInfo* filter = reinterpret_cast(arg); - return (xevent->type == PropertyNotify && - xevent->xproperty.window == filter->m_window && - xevent->xproperty.atom == filter->m_property && - xevent->xproperty.state == PropertyNewValue) ? True : False; -} - -void CXWindowsScreen::addClipboardRequest( - Window owner, Window requestor, - Atom selection, Atom target, - Atom property, Time time) -{ - bool success = false; - - // see if it's a selection we know about - ClipboardID id; - for (id = 0; id < kClipboardEnd; ++id) - if (selection == m_atomClipboard[id]) - break; - - // mutex the display + // block others from using the display while we get the clipboard CLock lock(&m_mutex); - // a request for multiple targets is special - if (id != kClipboardEnd) { - // check time we own the selection against the requested time - if (!wasOwnedAtTime(id, owner, time)) { - // fail for time we didn't own selection - } - else if (target == m_atomMultiple) { - // add a multiple request - if (property != None) { - success = sendClipboardMultiple(id, requestor, property, time); - } - } - else { - // handle remaining request formats - success = sendClipboardData(id, requestor, target, property, time); - } + // fail if we don't have the requested clipboard + if (m_clipboard[id] == NULL) { + return false; } - // send success or failure - sendNotify(requestor, selection, target, success ? property : None, time); + // get the actual time. ICCCM does not allow CurrentTime. + Time timestamp = CXWindowsUtil::getCurrentTime( + m_display, m_clipboard[id]->getWindow()); + + // copy the clipboard + return CClipboard::copy(clipboard, m_clipboard[id], timestamp); } void CXWindowsScreen::processClipboardRequest( Window requestor, - Atom property, Time /*time*/) + Time time, Atom property) { CLock lock(&m_mutex); - // check every clipboard + // check every clipboard until one returns success for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - CClipboardInfo& clipboard = m_clipboards[id]; - - // find the request list - CRequestMap::iterator index = clipboard.m_requests.find(requestor); - if (index == clipboard.m_requests.end()) { - // this clipboard isn't servicing this requestor window - continue; + if (m_clipboard[id] != NULL && + m_clipboard[id]->processRequest(requestor, time, property)) { + break; } - CRequestList* list = index->second; - assert(list != NULL); - - // find the property in the list - CRequestList::iterator index2; - for (index2 = list->begin(); index2 != list->end(); ++index2) { - if ((*index2)->m_property == property) { - break; - } - } - if (index2 == list->end()) { - // this clipboard isn't waiting on this property - continue; - } - CClipboardRequest* request = *index2; - assert(request != NULL); - - // compute amount of data to send - assert(request->m_sent <= request->m_data.size()); - UInt32 count = request->m_data.size() - request->m_sent; - if (count > kMaxRequestSize) { - // limit maximum chunk size - count = kMaxRequestSize; - - // make it a multiple of the size - count &= ~((request->m_size >> 3) - 1); - } - - // send more data - // FIXME -- handle Alloc errors (by returning false) - XChangeProperty(m_display, request->m_requestor, request->m_property, - request->m_type, request->m_size, - PropModeReplace, - reinterpret_cast( - request->m_data.data() + request->m_sent), - count / (request->m_size >> 3)); - - // account for sent data - request->m_sent += count; - - // if we sent zero bytes then we're done sending this data. remove - // it from the list and, if the list is empty, the list from the - // map. also stop watching the requestor for events. - if (count == 0) { - list->erase(index2); - delete request; - if (list->empty()) { - clipboard.m_requests.erase(index); - delete list; - } - XSelectInput(m_display, requestor, getEventMask(requestor)); - } - - // request has been serviced - break; } } @@ -734,340 +317,13 @@ void CXWindowsScreen::destroyClipboardRequest( { CLock lock(&m_mutex); - // check every clipboard + // check every clipboard until one returns success for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - CClipboardInfo& clipboard = m_clipboards[id]; - - // find the request list - CRequestMap::iterator index = clipboard.m_requests.find(requestor); - if (index == clipboard.m_requests.end()) { - continue; - } - CRequestList* list = index->second; - assert(list != NULL); - - // destroy every request in the list - for (CRequestList::iterator index2 = list->begin(); - index2 != list->end(); ++index2) { - delete *index2; - } - - // remove and destroy the list - clipboard.m_requests.erase(index); - delete list; - } - - // note -- we don't stop watching the window for events because - // we're called in response to the window being destroyed. -} - -bool CXWindowsScreen::sendClipboardData( - ClipboardID id, - Window requestor, Atom target, - Atom property, Time time) -{ - if (target == m_atomTargets) { - return sendClipboardTargets(id, requestor, property, time); - } - else if (target == m_atomTimestamp) { - return sendClipboardTimestamp(id, requestor, property, time); - } - else { - // compute the type and size for the requested target and - // convert the data from the clipboard. - Atom type = None; - int size = 0; - CString data; - if (target == m_atomText || target == m_atomString) { - if (m_clipboards[id].m_clipboard.has(IClipboard::kText)) { - type = m_atomString; - size = 8; - data = m_clipboards[id].m_clipboard.get(IClipboard::kText); - } - } - - // fail if we don't recognize or can't handle the target - if (type == None || size == 0) { - return false; - } - - if (data.size() > kMaxRequestSize) { - log((CLOG_DEBUG1 "handling clipboard request for %d as INCR", target)); - - // get the appropriate list, creating it if necessary - CRequestList* list = m_clipboards[id].m_requests[requestor]; - if (list == NULL) { - list = new CRequestList; - m_clipboards[id].m_requests[requestor] = list; - } - - // create request object - CClipboardRequest* request = new CClipboardRequest; - request->m_data = data; - request->m_sent = 0; - request->m_requestor = requestor; - request->m_property = property; - request->m_type = type; - request->m_size = size; - - // add request to request list - list->push_back(request); - - // start watching requestor for property changes and - // destruction, in addition to other events required by - // the subclass. - XSelectInput(m_display, requestor, - getEventMask(requestor) | - StructureNotifyMask | - PropertyChangeMask); - - // FIXME -- handle Alloc errors (by returning false) - // set property to INCR - const UInt32 zero = 0; - XChangeProperty(m_display, requestor, property, - m_atomINCR, - 8 * sizeof(zero), - PropModeReplace, - reinterpret_cast(&zero), - 1); - } - else { - log((CLOG_DEBUG1 "handling clipboard request for %d", target)); - - // FIXME -- handle Alloc errors (by returning false) - XChangeProperty(m_display, requestor, property, - type, size, - PropModeReplace, - reinterpret_cast( - data.data()), - data.size() / (size >> 3)); - } - return true; - } - return false; -} - -bool CXWindowsScreen::sendClipboardMultiple( - ClipboardID id, - Window requestor, - Atom property, Time time) -{ - log((CLOG_DEBUG1 "handling clipboard request for MULTIPLE")); - - // get the list of requested formats - Atom type; - SInt32 size; - CString data; - if (!getData(requestor, property, &type, &size, &data)) { - type = 0; - } - - // we only handle atom pair type - bool success = false; - if (type == m_atomAtomPair) { - // check each format, replacing ones we can't do with None. set - // the property for each to the requested data (for small requests) - // or INCR (for large requests). - bool updated = false; - UInt32 numRequests = data.size() / (2 * sizeof(Atom)); - for (UInt32 index = 0; index < numRequests; ++index) { - // get request info - const Atom* request = reinterpret_cast(data.data()); - const Atom target = request[2 * index + 0]; - const Atom property = request[2 * index + 1]; - - // handle target - if (property != None) { - if (!sendClipboardData(id, requestor, target, property, time)) { - // couldn't handle target. change property to None. - const Atom none = None; - data.replace((2 * index + 1) * sizeof(Atom), sizeof(Atom), - reinterpret_cast(&none), - sizeof(none)); - updated = true; - } - else { - success = true; - } - } - } - - // update property if we changed it - if (updated) { - // FIXME -- handle Alloc errors (by returning false) - XChangeProperty(m_display, requestor, property, - m_atomAtomPair, - 8 * sizeof(Atom), - PropModeReplace, - reinterpret_cast( - data.data()), - data.length()); + if (m_clipboard[id] != NULL && + m_clipboard[id]->destroyRequest(requestor)) { + break; } } - - // send notify - sendNotify(requestor, m_atomClipboard[id], m_atomMultiple, - success ? property : None, time); - return success; -} - -bool CXWindowsScreen::sendClipboardTargets( - ClipboardID id, - Window requestor, - Atom property, Time /*time*/) -{ - log((CLOG_DEBUG1 "handling request for TARGETS")); - - // count the number of targets, plus TARGETS and MULTIPLE - SInt32 numTargets = 2; - if (m_clipboards[id].m_clipboard.has(IClipboard::kText)) { - numTargets += 2; - } - - // construct response - Atom* response = new Atom[numTargets]; - SInt32 count = 0; - response[count++] = m_atomTargets; - response[count++] = m_atomMultiple; - if (m_clipboards[id].m_clipboard.has(IClipboard::kText)) { - response[count++] = m_atomText; - response[count++] = m_atomString; - } - - // send response (we assume we can transfer the entire list at once) - // FIXME -- handle Alloc errors (by returning false) - XChangeProperty(m_display, requestor, property, - m_atomAtom, - 8 * sizeof(Atom), - PropModeReplace, - reinterpret_cast(response), - count); - - // done with our response - delete[] response; - - return true; -} - -bool CXWindowsScreen::sendClipboardTimestamp( - ClipboardID id, - Window requestor, - Atom property, Time /*time*/) -{ - log((CLOG_DEBUG1 "handling clipboard request for TIMESTAMP")); - - // FIXME -- handle Alloc errors (by returning false) - Time time = m_clipboards[id].m_clipboard.getTime(); - XChangeProperty(m_display, requestor, property, - m_atomInteger, - 32, - PropModeReplace, - reinterpret_cast(time), - 1); - return true; -} - -void CXWindowsScreen::sendNotify( - Window requestor, Atom selection, - Atom target, Atom property, Time time) -{ - XEvent event; - event.xselection.type = SelectionNotify; - event.xselection.display = m_display; - event.xselection.requestor = requestor; - event.xselection.selection = selection; - event.xselection.target = target; - event.xselection.property = property; - event.xselection.time = time; - XSendEvent(m_display, requestor, False, 0, &event); -} - -bool CXWindowsScreen::wasOwnedAtTime( - ClipboardID id, Window window, Time time) const -{ - const CClipboardInfo& clipboard = m_clipboards[id]; - - // not owned if we've never owned the selection - if (clipboard.m_clipboard.getTime() == CurrentTime) - return false; - - // if time is CurrentTime then return true if we still own the - // selection and false if we do not. else if we still own the - // selection then get the current time, otherwise use - // m_lostClipboard as the end time. - Time lost = clipboard.m_lostClipboard; - if (lost == CurrentTime) - if (time == CurrentTime) - return true; - else - lost = getCurrentTimeNoLock(window); - else - if (time == CurrentTime) - return false; - - // compare time to range - Time duration = clipboard.m_lostClipboard - clipboard.m_clipboard.getTime(); - Time when = time - clipboard.m_clipboard.getTime(); - return (/*when >= 0 &&*/ when < duration); -} - -Time CXWindowsScreen::getCurrentTime(Window window) const -{ - CLock lock(&m_mutex); - return getCurrentTimeNoLock(window); -} - -Time CXWindowsScreen::getCurrentTimeNoLock( - Window window) const -{ - // select property events on window - // note -- this will break clipboard transfer if used on a requestor - // window, so don't do that. - XSelectInput(m_display, window, getEventMask(window) | PropertyChangeMask); - - // do a zero-length append to get the current time - unsigned char dummy; - XChangeProperty(m_display, window, m_atomSynergyTime, - m_atomInteger, 8, - PropModeAppend, - &dummy, 0); - - // look for property notify events with the following - CPropertyNotifyInfo filter; - filter.m_window = window; - filter.m_property = m_atomSynergyTime; - - // wait for reply - XEvent xevent; - while (XCheckIfEvent(m_display, &xevent, - &CXWindowsScreen::findPropertyNotify, - (XPointer)&filter) != True) { - // wait a bit - CThread::sleep(0.05); - } - assert(xevent.type == PropertyNotify); - assert(xevent.xproperty.window == window); - assert(xevent.xproperty.atom == m_atomSynergyTime); - - // restore event mask - XSelectInput(m_display, window, getEventMask(window)); - - return xevent.xproperty.time; -} - - -// -// CXWindowsScreen::CClipboardInfo -// - -CXWindowsScreen::CClipboardInfo::CClipboardInfo() : - m_clipboard(), - m_lostClipboard(CurrentTime), - m_requests(), - m_owner(None), - m_unresponsive(false) -{ - // do nothing } diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index b02fd264..c97be519 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -3,13 +3,11 @@ #include "BasicTypes.h" #include "ClipboardTypes.h" -#include "CClipboard.h" #include "CMutex.h" #include -#include -#include -class CString; +class IClipboard; +class CXWindowsClipboard; class CXWindowsScreen { public: @@ -57,141 +55,54 @@ protected: // cause getEvent() to return false immediately and forever after void doStop(); - // determine the clipboard from the X selection. returns - // kClipboardEnd if no such clipboard. - ClipboardID getClipboardID(Atom selection); - - // call when we lose the clipboard ownership (i.e. when we receive - // a SelectionClear event). returns true iff we've actually lost - // a selection we care about. - bool lostClipboard(Atom selection, Time timestamp); - // set the contents of the clipboard (i.e. primary selection) bool setDisplayClipboard(ClipboardID, - const IClipboard* clipboard, - Window requestor, Time timestamp); + const IClipboard* clipboard); - // copy the clipboard contents to clipboard. requestor must be a - // valid window; it will be used to receive the transfer. timestamp - // should be the timestamp of the provoking event and not CurrentTime. - // if force is false then only update clipboard - void getDisplayClipboard(ClipboardID, - IClipboard* clipboard, - Window requestor, Time timestamp) const; - - // add a selection request to the request list - void addClipboardRequest( - Window owner, Window requestor, - Atom selection, Atom target, - Atom property, Time time); - - // continue processing a selection request - void processClipboardRequest(Window window, - Atom property, Time time); - - // terminate a selection request - void destroyClipboardRequest(Window window); - - // get the current server time - Time getCurrentTime(Window) const; + // copy the clipboard contents to clipboard + bool getDisplayClipboard(ClipboardID, + IClipboard* clipboard) const; // called by openDisplay() to allow subclasses to prepare the display virtual void onOpenDisplay() = 0; + // called by openDisplay() after onOpenDisplay() to create each clipboard + virtual CXWindowsClipboard* + createClipboard(ClipboardID) = 0; + // called by closeDisplay() to virtual void onCloseDisplay() = 0; + // called when a clipboard is lost + virtual void onLostClipboard(ClipboardID) = 0; + // get the X event mask required by the subclass for the given window virtual long getEventMask(Window) const = 0; private: - class CPropertyNotifyInfo { - public: - Window m_window; - Atom m_property; - }; - class CClipboardRequest { - public: - CString m_data; - UInt32 m_sent; - Window m_requestor; - Atom m_property; - Atom m_type; - int m_size; - }; - typedef std::list CRequestList; - typedef std::map CRequestMap; + // internal event processing + bool processEvent(XEvent*); - bool getDisplayClipboard(Atom selection, Atom type, - Window requestor, Time timestamp, - Atom* outputType, CString* data) const; - bool getData(Window, Atom property, - Atom* type, SInt32* datumSize, - CString* data) const; - IClipboard::EFormat getFormat(Atom) const; - static Bool findSelectionNotify(Display*, - XEvent* xevent, XPointer arg); - static Bool findPropertyNotify(Display*, - XEvent* xevent, XPointer arg); + // determine the clipboard from the X selection. returns + // kClipboardEnd if no such clipboard. + ClipboardID getClipboardID(Atom selection) const; - bool sendClipboardData(ClipboardID, Window requestor, - Atom target, Atom property, Time time); - bool sendClipboardMultiple(ClipboardID, Window requestor, - Atom property, Time time); - bool sendClipboardTargets(ClipboardID, Window requestor, - Atom property, Time time); - bool sendClipboardTimestamp(ClipboardID, Window requestor, - Atom property, Time time); - void sendNotify(Window requestor, Atom selection, - Atom target, Atom property, Time time); - bool wasOwnedAtTime(ClipboardID, Window, Time) const; - Time getCurrentTimeNoLock(Window) const; + // continue processing a selection request + void processClipboardRequest(Window window, + Time time, Atom property); + + // terminate a selection request + void destroyClipboardRequest(Window window); private: - class CClipboardInfo { - public: - CClipboardInfo(); - - public: - // the contents of the clipboard and the time we got it - CClipboard m_clipboard; - - // when we lost the clipboard - Time m_lostClipboard; - - // the request queues - CRequestMap m_requests; - - // owner of clipboard when we last asked for it - Window m_owner; - - // true iff the previous request to m_owner got no reply - bool m_unresponsive; - }; - Display* m_display; int m_screen; Window m_root; SInt32 m_w, m_h; bool m_stop; - // atoms we'll need - Atom m_atomTargets; - Atom m_atomMultiple; - Atom m_atomTimestamp; - Atom m_atomAtom; - Atom m_atomAtomPair; - Atom m_atomInteger; - Atom m_atomData; - Atom m_atomINCR; - Atom m_atomString; - Atom m_atomText; - Atom m_atomCompoundText; - Atom m_atomClipboard[kClipboardEnd]; - Atom m_atomSynergyTime; - - // clipboard info - CClipboardInfo m_clipboards[kClipboardEnd]; + // clipboards + CXWindowsClipboard* m_clipboard[kClipboardEnd]; // X is not thread safe CMutex m_mutex; diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index 32856c1b..43632f02 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -19,38 +19,41 @@ public: // manipulators - // grab ownership of and clear the clipboard of all data. - // only add() may be called between an open() and its - // corresponding close(). if open() returns false then - // the clipboard could not be opened or grabbed; do not - // call close() in that case. iff open() returns true it - // should have saved the timestamp. the timestamp should - // be zero before the first successful open. - virtual bool open(Time) = 0; + // take ownership of the clipboard and clear all data from it. + // must be called between an open() and close(). if returns + // false then the clipboard ownership could not be taken; the + // clipboard should not be emptied in this case. + virtual bool empty() = 0; + + // add data in the given format to the clipboard. data is + // passed as a string but the contents are generally not + // interpreted. may only be called after a successful empty(). + virtual void add(EFormat, const CString& data) = 0; + + // accessors + + // open the clipboard. return true iff the clipboard could + // be opened. if open() returns true then it must be followed + // by a close() at some later time; if it returns false then + // close() must not be called. + virtual bool open(Time) const = 0; // close the clipboard. close() must match a preceding open(). // this signals that the clipboard has been filled with all the // necessary data. it does not mean the clipboard ownership // should be released. - virtual void close() = 0; - - // add data in the given format to the clipboard. data is - // passed as a string but the contents are generally not - // interpreted. may only be called between an open() and - // a close(). - virtual void add(EFormat, const CString& data) = 0; - - // accessors + virtual void close() const = 0; // returns the timestamp passed to the last successful open(). virtual Time getTime() const = 0; // returns true iff the clipboard contains data in the given - // format. + // format. must be called between an open() and close(). virtual bool has(EFormat) const = 0; // returns data in the given format. rturns the empty string - // if there is no data in that format. + // if there is no data in that format. must be called between + // an open() and close(). virtual CString get(EFormat) const = 0; }; diff --git a/synergy/Makefile b/synergy/Makefile index d1cef559..3d499dcd 100644 --- a/synergy/Makefile +++ b/synergy/Makefile @@ -23,6 +23,7 @@ CXXFILES = \ CTCPSocketFactory.cpp \ CXWindowsClipboard.cpp \ CXWindowsScreen.cpp \ + CXWindowsUtil.cpp \ XSynergy.cpp \ $(NULL) From 56d7271bc18462983280a9b892725e8d1849d62b Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 May 2002 16:51:07 +0000 Subject: [PATCH 107/807] added missing files from previous submit. --- synergy/CXWindowsUtil.cpp | 169 ++++++++++++++++++++++++++++++++++++++ synergy/CXWindowsUtil.h | 31 +++++++ 2 files changed, 200 insertions(+) create mode 100644 synergy/CXWindowsUtil.cpp create mode 100644 synergy/CXWindowsUtil.h diff --git a/synergy/CXWindowsUtil.cpp b/synergy/CXWindowsUtil.cpp new file mode 100644 index 00000000..86b4677a --- /dev/null +++ b/synergy/CXWindowsUtil.cpp @@ -0,0 +1,169 @@ +#include "CXWindowsUtil.h" +#include "CLog.h" +#include "CThread.h" +#include + +bool CXWindowsUtil::getWindowProperty( + Display* display, + Window window, Atom property, + CString* data, Atom* type, + int* format, bool deleteProperty) +{ + assert(display != NULL); + assert(data != NULL); + + Atom actualType; + int actualDatumSize; + + // read the property + const long length = XMaxRequestSize(display); + long offset = 0; + unsigned long bytesLeft = 1; + while (bytesLeft != 0) { + // get more data + unsigned long numItems; + unsigned char* rawData; + const int result = XGetWindowProperty(display, window, property, + offset, length, False, AnyPropertyType, + &actualType, &actualDatumSize, + &numItems, &bytesLeft, &rawData); + if (result != Success || actualType == None || actualDatumSize == 0) { + // failed + return false; + } + + // compute bytes read and advance offset + unsigned long numBytes; + switch (actualDatumSize) { + case 8: + default: + numBytes = numItems; + offset += numItems / 4; + break; + + case 16: + numBytes = 2 * numItems; + offset += numItems / 2; + break; + + case 32: + numBytes = 4 * numItems; + offset += numItems; + break; + } + + // append data + data->append((char*)rawData, numBytes); + + // done with returned data + XFree(rawData); + } + + // delete the property if requested + if (deleteProperty) { + XDeleteProperty(display, window, property); + } + + // save property info + if (type != NULL) { + *type = actualType; + } + if (format != NULL) { + *format = static_cast(actualDatumSize); + } + + log((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, data->size())); + return true; +} + +bool CXWindowsUtil::setWindowProperty( + Display* display, + Window window, Atom property, + const void* vdata, UInt32 size, + Atom type, SInt32 format) +{ +// FIXME -- must catch Alloc errors (using XSetErrorHandler()) and +// report failure to caller. + + const UInt32 length = 4 * XMaxRequestSize(display); + const unsigned char* data = reinterpret_cast(vdata); + const UInt32 datumSize = static_cast(format / 8); + + // how much data to send in first chunk? + UInt32 chunkSize = size; + if (chunkSize > length) + chunkSize = length; + + // send first chunk + XChangeProperty(display, window, property, + type, format, PropModeReplace, + data, chunkSize / datumSize); + + // append remaining chunks + data += chunkSize; + size -= chunkSize; + while (size > 0) { + chunkSize = size; + if (chunkSize > length) + chunkSize = length; + XChangeProperty(display, window, property, + type, format, PropModeAppend, + data, chunkSize / datumSize); + data += chunkSize; + size -= chunkSize; + } + + return true; +} + +Time CXWindowsUtil::getCurrentTime( + Display* display, Window window) +{ + // select property events on window + XWindowAttributes attr; + XGetWindowAttributes(display, window, &attr); + XSelectInput(display, window, attr.your_event_mask | PropertyChangeMask); + + // make a property name to receive dummy change + Atom atom = XInternAtom(display, "TIMESTAMP", False); + + // do a zero-length append to get the current time + unsigned char dummy; + XChangeProperty(display, window, atom, + XA_INTEGER, 8, + PropModeAppend, + &dummy, 0); + + // look for property notify events with the following + CPropertyNotifyPredicateInfo filter; + filter.m_window = window; + filter.m_property = atom; + + // wait for reply + XEvent xevent; + while (XCheckIfEvent(display, &xevent, + &CXWindowsUtil::propertyNotifyPredicate, + (XPointer)&filter) != True) { + // wait a bit + CThread::sleep(0.05); + } + assert(xevent.type == PropertyNotify); + assert(xevent.xproperty.window == window); + assert(xevent.xproperty.atom == atom); + + // restore event mask + XSelectInput(display, window, attr.your_event_mask); + + return xevent.xproperty.time; +} + +Bool CXWindowsUtil::propertyNotifyPredicate( + Display*, XEvent* xevent, XPointer arg) +{ + CPropertyNotifyPredicateInfo* filter = + reinterpret_cast(arg); + return (xevent->type == PropertyNotify && + xevent->xproperty.window == filter->m_window && + xevent->xproperty.atom == filter->m_property && + xevent->xproperty.state == PropertyNewValue) ? True : False; +} diff --git a/synergy/CXWindowsUtil.h b/synergy/CXWindowsUtil.h new file mode 100644 index 00000000..07bd2a97 --- /dev/null +++ b/synergy/CXWindowsUtil.h @@ -0,0 +1,31 @@ +#ifndef CXWINDOWSUTIL_H +#define CXWINDOWSUTIL_H + +#include "BasicTypes.h" +#include "CString.h" +#include + +class CXWindowsUtil { +public: + static bool getWindowProperty(Display*, + Window window, Atom property, + CString* data, Atom* type, + SInt32* format, bool deleteProperty); + static bool setWindowProperty(Display*, + Window window, Atom property, + const void* data, UInt32 size, + Atom type, SInt32 format); + static Time getCurrentTime(Display*, Window); + +private: + class CPropertyNotifyPredicateInfo { + public: + Window m_window; + Atom m_property; + }; + + static Bool propertyNotifyPredicate(Display*, + XEvent* xevent, XPointer arg); +}; + +#endif From f8eb775278736b21971f6a25f67d786e78709bb2 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 May 2002 17:05:34 +0000 Subject: [PATCH 108/807] changed lesstif hack to only apply to the CLIPBOARD selection. apprently the PRIMARY selection must follow the ICCCM protocol correctly. --- synergy/CXWindowsClipboard.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/synergy/CXWindowsClipboard.cpp b/synergy/CXWindowsClipboard.cpp index 30ade009..0ec423b7 100644 --- a/synergy/CXWindowsClipboard.cpp +++ b/synergy/CXWindowsClipboard.cpp @@ -908,7 +908,8 @@ bool CXWindowsClipboard::sendReply(CReply* reply) // it has a _MOTIF_CLIP_LOCK_ACCESS_VALID property // it does not have a GDK_SELECTION property CString dummy; - if (!CXWindowsUtil::getWindowProperty(m_display, + if (m_id != kClipboardClipboard || + !CXWindowsUtil::getWindowProperty(m_display, reply->m_requestor, m_atomMotifClipAccess, &dummy, NULL, NULL, False) || From 7cf20d9ad063b50af6dd5f5e4c6885167bd62e67 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 May 2002 18:28:06 +0000 Subject: [PATCH 109/807] removed getEventMask() and fixed some comments. also now using toggle key states in updateModifiers(). --- client/CXWindowsSecondaryScreen.cpp | 27 +++++++++++++++------------ client/CXWindowsSecondaryScreen.h | 1 - 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index ec4f5d21..2adb1a81 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -310,6 +310,7 @@ void CXWindowsSecondaryScreen::onOpenDisplay() CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, &attr); + log((CLOG_DEBUG "window is 0x%08x", m_window)); // become impervious to server grabs XTestGrabControl(display, True); @@ -345,14 +346,6 @@ void CXWindowsSecondaryScreen::onLostClipboard( m_client->onClipboardChanged(id); } -long CXWindowsSecondaryScreen::getEventMask(Window w) const -{ - if (w == m_window) - return LeaveWindowMask; - else - return NoEventMask; -} - void CXWindowsSecondaryScreen::leaveNoLock(Display* display) { assert(display != NULL); @@ -778,7 +771,7 @@ void CXWindowsSecondaryScreen::doKeystrokes( unsigned int CXWindowsSecondaryScreen::maskToX( KeyModifierMask inMask) const { - // FIXME -- should be configurable. not using Mod3Mask. + // FIXME -- should be configurable. also not using Mod3Mask. unsigned int outMask = 0; if (inMask & KeyModifierShift) outMask |= ShiftMask; @@ -817,8 +810,17 @@ void CXWindowsSecondaryScreen::updateKeys(Display* display) } void CXWindowsSecondaryScreen::updateModifiers( - Display*) + Display* display) { + // query the pointer to get the keyboard state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + state = 0; + } + // update active modifier mask m_mask = 0; for (unsigned int i = 0; i < 8; ++i) { @@ -829,8 +831,9 @@ void CXWindowsSecondaryScreen::updateModifiers( m_mask |= bit; } } - else { - // FIXME -- not sure how to check current lock states + else if ((bit & state) != 0) { + // toggle is on + m_mask |= bit; } } } diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 52bbde1b..a89e344b 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -40,7 +40,6 @@ protected: createClipboard(ClipboardID); virtual void onCloseDisplay(); virtual void onLostClipboard(ClipboardID); - virtual long getEventMask(Window) const; private: enum EKeyAction { kPress, kRelease, kRepeat }; From 189537b8b9892f404871823f3040e7280c137653 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 May 2002 18:30:13 +0000 Subject: [PATCH 110/807] removed getEventMask() from primary screen. added a class to CXWindowsUtil that installs/uninstalls an X error hander. using that in primary screen, clipboard, and util to ensure that certain errors don't kill the app. --- server/CXWindowsPrimaryScreen.cpp | 20 ++++---- server/CXWindowsPrimaryScreen.h | 2 +- synergy/CXWindowsClipboard.cpp | 19 +++++++- synergy/CXWindowsScreen.h | 3 -- synergy/CXWindowsUtil.cpp | 79 +++++++++++++++++++++++++++++-- synergy/CXWindowsUtil.h | 30 ++++++++++++ 6 files changed, 133 insertions(+), 20 deletions(-) diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 149b1835..2ba6cfee 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -440,6 +440,7 @@ void CXWindowsPrimaryScreen::onOpenDisplay() CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, &attr); + log((CLOG_DEBUG "window is 0x%08x", m_window)); // start watching for events on other windows selectEvents(display, getRoot()); @@ -469,18 +470,17 @@ void CXWindowsPrimaryScreen::onLostClipboard( m_server->grabClipboard(id); } -long CXWindowsPrimaryScreen::getEventMask(Window w) const +void CXWindowsPrimaryScreen::selectEvents( + Display* display, Window w) const { - if (w == m_window) - return PointerMotionMask |// PointerMotionHintMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask; - else - return PointerMotionMask | SubstructureNotifyMask; + // ignore errors while we adjust event masks + CXWindowsUtil::CErrorLock lock; + + // adjust event masks + doSelectEvents(display, w); } -void CXWindowsPrimaryScreen::selectEvents( +void CXWindowsPrimaryScreen::doSelectEvents( Display* display, Window w) const { // we want to track the mouse everywhere on the display. to achieve @@ -500,7 +500,7 @@ void CXWindowsPrimaryScreen::selectEvents( unsigned int nc; if (XQueryTree(display, w, &rw, &pw, &cw, &nc)) { for (unsigned int i = 0; i < nc; ++i) - selectEvents(display, cw[i]); + doSelectEvents(display, cw[i]); XFree(cw); } } diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index b3fc98b6..e57f00b4 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -34,10 +34,10 @@ protected: createClipboard(ClipboardID); virtual void onCloseDisplay(); virtual void onLostClipboard(ClipboardID); - virtual long getEventMask(Window) const; private: void selectEvents(Display*, Window) const; + void doSelectEvents(Display*, Window) const; void warpCursorNoLock(Display*, SInt32 xAbsolute, SInt32 yAbsolute); diff --git a/synergy/CXWindowsClipboard.cpp b/synergy/CXWindowsClipboard.cpp index 0ec423b7..09b713e7 100644 --- a/synergy/CXWindowsClipboard.cpp +++ b/synergy/CXWindowsClipboard.cpp @@ -190,6 +190,7 @@ bool CXWindowsClipboard::destroyRequest( // destroy all replies for this window clearReplies(index->second); + m_replies.erase(index); // note -- we don't stop watching the window for events because // we're called in response to the window being destroyed. @@ -758,11 +759,24 @@ void CXWindowsClipboard::insertReply(CReply* reply) // want events in case the window is destroyed or any of its // properties change. if (newWindow) { + // note errors while we adjust event masks + bool error = false; + CXWindowsUtil::CErrorLock lock(&error); + + // get and save the current event mask XWindowAttributes attr; XGetWindowAttributes(m_display, reply->m_requestor, &attr); + m_eventMasks[reply->m_requestor] = attr.your_event_mask; + + // add the events we want XSelectInput(m_display, reply->m_requestor, attr.your_event_mask | StructureNotifyMask | PropertyChangeMask); - m_eventMasks[reply->m_requestor] = attr.your_event_mask; + + // if we failed then the window has already been destroyed + if (error) { + m_replies.erase(reply->m_requestor); + delete reply; + } } } @@ -799,6 +813,7 @@ void CXWindowsClipboard::pushReplies( // if there are no more replies in the list then remove the list // and stop watching the requestor for events. if (replies.empty()) { + CXWindowsUtil::CErrorLock lock; Window requestor = mapIndex->first; XSelectInput(m_display, requestor, m_eventMasks[requestor]); m_replies.erase(mapIndex); @@ -871,6 +886,7 @@ bool CXWindowsClipboard::sendReply(CReply* reply) log((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); reply->m_done = true; if (reply->m_property != None) { + CXWindowsUtil::CErrorLock lock; XDeleteProperty(m_display, reply->m_requestor, reply->m_property); } @@ -958,6 +974,7 @@ void CXWindowsClipboard::sendNotify( event.xselection.target = target; event.xselection.property = property; event.xselection.time = time; + CXWindowsUtil::CErrorLock lock; XSendEvent(m_display, requestor, False, 0, &event); } diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index c97be519..b2a675ed 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -76,9 +76,6 @@ protected: // called when a clipboard is lost virtual void onLostClipboard(ClipboardID) = 0; - // get the X event mask required by the subclass for the given window - virtual long getEventMask(Window) const = 0; - private: // internal event processing bool processEvent(XEvent*); diff --git a/synergy/CXWindowsUtil.cpp b/synergy/CXWindowsUtil.cpp index 86b4677a..ffc6a9f5 100644 --- a/synergy/CXWindowsUtil.cpp +++ b/synergy/CXWindowsUtil.cpp @@ -1,8 +1,13 @@ #include "CXWindowsUtil.h" #include "CLog.h" #include "CThread.h" +#include #include +// +// CXWindowsUtil +// + bool CXWindowsUtil::getWindowProperty( Display* display, Window window, Atom property, @@ -15,6 +20,9 @@ bool CXWindowsUtil::getWindowProperty( Atom actualType; int actualDatumSize; + // ignore errors. XGetWindowProperty() will report failure. + CXWindowsUtil::CErrorLock lock; + // read the property const long length = XMaxRequestSize(display); long offset = 0; @@ -82,13 +90,14 @@ bool CXWindowsUtil::setWindowProperty( const void* vdata, UInt32 size, Atom type, SInt32 format) { -// FIXME -- must catch Alloc errors (using XSetErrorHandler()) and -// report failure to caller. - const UInt32 length = 4 * XMaxRequestSize(display); const unsigned char* data = reinterpret_cast(vdata); const UInt32 datumSize = static_cast(format / 8); + // save errors + bool error = false; + CXWindowsUtil::CErrorLock lock(&error); + // how much data to send in first chunk? UInt32 chunkSize = size; if (chunkSize > length) @@ -102,7 +111,7 @@ bool CXWindowsUtil::setWindowProperty( // append remaining chunks data += chunkSize; size -= chunkSize; - while (size > 0) { + while (!error && size > 0) { chunkSize = size; if (chunkSize > length) chunkSize = length; @@ -113,7 +122,7 @@ bool CXWindowsUtil::setWindowProperty( size -= chunkSize; } - return true; + return !error; } Time CXWindowsUtil::getCurrentTime( @@ -167,3 +176,63 @@ Bool CXWindowsUtil::propertyNotifyPredicate( xevent->xproperty.atom == filter->m_property && xevent->xproperty.state == PropertyNewValue) ? True : False; } + + +// +// CXWindowsUtil::CErrorLock +// + +CXWindowsUtil::CErrorLock* CXWindowsUtil::CErrorLock::s_top = NULL; + +CXWindowsUtil::CErrorLock::CErrorLock() +{ + install(&CXWindowsUtil::CErrorLock::ignoreHandler, NULL); +} + +CXWindowsUtil::CErrorLock::CErrorLock(bool* flag) +{ + install(&CXWindowsUtil::CErrorLock::saveHandler, flag); +} + +CXWindowsUtil::CErrorLock::CErrorLock(ErrorHandler handler, void* data) +{ + install(handler, data); +} + +CXWindowsUtil::CErrorLock::~CErrorLock() +{ + XSetErrorHandler(m_oldXHandler); + s_top = m_next; +} + +void CXWindowsUtil::CErrorLock::install( + ErrorHandler handler, void* data) +{ + m_handler = handler; + m_userData = data; + m_oldXHandler = XSetErrorHandler( + &CXWindowsUtil::CErrorLock::internalHandler); + m_next = s_top; + s_top = this; +} + +int CXWindowsUtil::CErrorLock::internalHandler( + Display* display, XErrorEvent* event) +{ + if (s_top != NULL && s_top->m_handler != NULL) { + s_top->m_handler(display, event, s_top->m_userData); + } + return 0; +} + +void CXWindowsUtil::CErrorLock::ignoreHandler( + Display*, XErrorEvent*, void*) +{ + // do nothing +} + +void CXWindowsUtil::CErrorLock::saveHandler( + Display*, XErrorEvent*, void* flag) +{ + *reinterpret_cast(flag) = true; +} diff --git a/synergy/CXWindowsUtil.h b/synergy/CXWindowsUtil.h index 07bd2a97..82414dd5 100644 --- a/synergy/CXWindowsUtil.h +++ b/synergy/CXWindowsUtil.h @@ -17,6 +17,36 @@ public: Atom type, SInt32 format); static Time getCurrentTime(Display*, Window); + // class to set an X error handler in the c'tor and restore the + // previous error handler in the d'tor. a lock should only + // be installed while the display is locked by the thread. + // + // CErrorLock() ignores errors + // CErrorLock(bool* flag) sets *flag to true if any error occurs + class CErrorLock { + public: + typedef void (*ErrorHandler)(Display*, XErrorEvent*, void* userData); + CErrorLock(); + CErrorLock(bool* errorFlag); + CErrorLock(ErrorHandler, void* userData); + ~CErrorLock(); + + private: + void install(ErrorHandler, void*); + static int internalHandler(Display*, XErrorEvent*); + static void ignoreHandler(Display*, XErrorEvent*, void*); + static void saveHandler(Display*, XErrorEvent*, void*); + + private: + typedef int (*XErrorHandler)(Display*, XErrorEvent*); + + ErrorHandler m_handler; + void* m_userData; + XErrorHandler m_oldXHandler; + CErrorLock* m_next; + static CErrorLock* s_top; + }; + private: class CPropertyNotifyPredicateInfo { public: From f5a6d2738a45f46e4d7d64c2d755bedbacd19995 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 May 2002 18:35:14 +0000 Subject: [PATCH 111/807] checkpoint --- notes | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/notes b/notes index f497c867..69004cb2 100644 --- a/notes +++ b/notes @@ -65,6 +65,15 @@ investigate klipper KDE's clipboard manager? it crashed once when server did (see below) +--- +blown assertion + between client being added to screen list and sending screen size: + attempt to switch to that screen blows assert + x,y isn't inside screen because screen has zero size + maybe have a "ready" flag for each client + only count ready clients when finding neighbor + or don't add client to screen list until fully ready + --- got crash when opening netscape window after trying copy in KDE mail BadWindow in ChangeWindowAttributes @@ -270,3 +279,28 @@ disable ctrl+alt+del info: Q179905 suggestion for bypassing hook code when called by debugger. may prevent system from getting locked up when debugging. + +--- +config file format: + +section screens + : + # args in any order + # not sure if any args +end + +section links + : + # args in any order + [left=] + [right=] + [up=] + [down=] +end + +--- +will have to redo clipboard stuff on win32 + +need timeout on CReply objects + should flush replies that are too old + assumption is that requestor is broken From 8cb0302665951f4ca370f57c6606ecc746f9d3b8 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 May 2002 18:55:51 +0000 Subject: [PATCH 112/807] updated win32 clipboard to match new model. --- synergy/CMSWindowsClipboard.cpp | 56 +++++++++++++-------------------- synergy/CMSWindowsClipboard.h | 7 +++-- 2 files changed, 26 insertions(+), 37 deletions(-) diff --git a/synergy/CMSWindowsClipboard.cpp b/synergy/CMSWindowsClipboard.cpp index 006ebc75..8503574c 100644 --- a/synergy/CMSWindowsClipboard.cpp +++ b/synergy/CMSWindowsClipboard.cpp @@ -18,44 +18,23 @@ CMSWindowsClipboard::~CMSWindowsClipboard() // do nothing } -bool CMSWindowsClipboard::open(Time time) +bool CMSWindowsClipboard::empty() { - log((CLOG_DEBUG "open clipboard")); + log((CLOG_DEBUG "empty clipboard")); - if (!OpenClipboard(m_window)) { - log((CLOG_WARN "failed to open clipboard")); + if (!EmptyClipboard()) { + log((CLOG_DEBUG "failed to grab clipboard")); return false; } - if (EmptyClipboard()) { - log((CLOG_DEBUG "grabbed clipboard")); - } - else { - log((CLOG_WARN "failed to grab clipboard")); - CloseClipboard(); - return false; - } - - m_time = time; return true; } -void CMSWindowsClipboard::close() -{ - log((CLOG_DEBUG "close clipboard")); - CloseClipboard(); -} - void CMSWindowsClipboard::add( EFormat format, const CString& data) { log((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); - if (!OpenClipboard(m_window)) { - log((CLOG_WARN "failed to open clipboard")); - return; - } - // convert data to win32 required form const UINT win32Format = convertFormatToWin32(format); HANDLE win32Data; @@ -73,8 +52,25 @@ void CMSWindowsClipboard::add( if (win32Data != NULL) { SetClipboardData(win32Format, win32Data); } +} - // done with clipboard +bool CMSWindowsClipboard::open(Time time) const +{ + log((CLOG_DEBUG "open clipboard")); + + if (!OpenClipboard(m_window)) { + log((CLOG_WARN "failed to open clipboard")); + return false; + } + + m_time = time; + + return true; +} + +void CMSWindowsClipboard::close() const +{ + log((CLOG_DEBUG "close clipboard")); CloseClipboard(); } @@ -96,11 +92,6 @@ CString CMSWindowsClipboard::get(EFormat format) const if (win32Format == 0) return CString(); - if (!OpenClipboard(m_window)) { - log((CLOG_WARN "failed to open clipboard")); - return CString(); - } - // get a handle to the clipboard data and convert it HANDLE win32Data = GetClipboardData(win32Format); CString data; @@ -112,9 +103,6 @@ CString CMSWindowsClipboard::get(EFormat format) const } } - // close the clipboard - CloseClipboard(); - return data; } diff --git a/synergy/CMSWindowsClipboard.h b/synergy/CMSWindowsClipboard.h index 0010af57..7d7d0225 100644 --- a/synergy/CMSWindowsClipboard.h +++ b/synergy/CMSWindowsClipboard.h @@ -10,9 +10,10 @@ public: virtual ~CMSWindowsClipboard(); // IClipboard overrides - virtual bool open(Time); - virtual void close(); + virtual bool empty(); virtual void add(EFormat, const CString& data); + virtual bool open(Time) const; + virtual void close() const; virtual Time getTime() const; virtual bool has(EFormat) const; virtual CString get(EFormat) const; @@ -24,7 +25,7 @@ private: private: HWND m_window; - Time m_time; + mutable Time m_time; }; #endif From 2cc63e31aaa43073d4d81fcb5e271b7710cc2a42 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 30 May 2002 16:11:59 +0000 Subject: [PATCH 113/807] fixed bug in closing down a socket. --- net/CTCPSocket.cpp | 30 ++++++++++++++++++++++++++---- net/CTCPSocket.h | 1 + 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index 189a5e3e..989a9668 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -140,13 +140,27 @@ void CTCPSocket::ioThread(void*) { try { ioService(); + ioCleanup(); + } + catch (...) { + ioCleanup(); + throw; + } +} + +void CTCPSocket::ioCleanup() +{ + try { m_input->close(); + } + catch (...) { + // ignore + } + try { m_output->close(); } catch (...) { - m_input->close(); - m_output->close(); - throw; + // ignore } } @@ -162,6 +176,9 @@ void CTCPSocket::ioService() // choose events to poll for CLock lock(m_mutex); pfds[0].events = 0; + if (m_connected == 0) { + return; + } if ((m_connected & kRead) != 0) { // still open for reading pfds[0].events |= CNetwork::kPOLLIN; @@ -174,6 +191,11 @@ void CTCPSocket::ioService() // check for status CThread::testCancel(); + if (pfds[0].events == 0) { + CThread::sleep(0.05); + CThread::testCancel(); + continue; + } const int status = CNetwork::poll(pfds, 1, 50); CThread::testCancel(); @@ -197,7 +219,7 @@ void CTCPSocket::ioService() else if (n == 0) { // stream hungup m_input->hangup(); - return; + m_connected &= ~kRead; } } diff --git a/net/CTCPSocket.h b/net/CTCPSocket.h index 70fe9770..3e7913b2 100644 --- a/net/CTCPSocket.h +++ b/net/CTCPSocket.h @@ -32,6 +32,7 @@ public: private: void init(); void ioThread(void*); + void ioCleanup(); void ioService(); void closeInput(void*); void closeOutput(void*); From 70f5f9491d5eaf40192ea8562b1aa495941df4e1 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 30 May 2002 16:13:16 +0000 Subject: [PATCH 114/807] added basic support for an embedded HTTP server. server currently supports editing the screen map but changing the map won't behave correctly if there are connected screens. --- Makefile | 1 + base/common.h | 3 + http/CHTTPProtocol.cpp | 611 ++++++++++++++++++++++++++++++ http/CHTTPProtocol.h | 88 +++++ http/Makefile | 26 ++ http/XHTTP.cpp | 119 ++++++ http/XHTTP.h | 44 +++ server/CHTTPServer.cpp | 823 +++++++++++++++++++++++++++++++++++++++++ server/CHTTPServer.h | 101 +++++ server/CScreenMap.cpp | 12 + server/CScreenMap.h | 46 ++- server/CServer.cpp | 106 +++++- server/CServer.h | 13 + server/Makefile | 3 + 14 files changed, 1986 insertions(+), 10 deletions(-) create mode 100644 http/CHTTPProtocol.cpp create mode 100644 http/CHTTPProtocol.h create mode 100644 http/Makefile create mode 100644 http/XHTTP.cpp create mode 100644 http/XHTTP.h create mode 100644 server/CHTTPServer.cpp create mode 100644 server/CHTTPServer.h diff --git a/Makefile b/Makefile index c89c39e7..70d1a8bf 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ SUBDIRS = \ base \ mt \ io \ + http \ net \ synergy \ client \ diff --git a/base/common.h b/base/common.h index 2f6d2037..e4ecb865 100644 --- a/base/common.h +++ b/base/common.h @@ -20,6 +20,9 @@ #define CONFIG_PLATFORM_WIN32 #if (_MSC_VER >= 1200) +// work around for statement scoping bug +#define for if (false) { } else for + // turn off bonehead warnings #pragma warning(disable: 4786) // identifier truncated in debug info diff --git a/http/CHTTPProtocol.cpp b/http/CHTTPProtocol.cpp new file mode 100644 index 00000000..46d5bc7a --- /dev/null +++ b/http/CHTTPProtocol.cpp @@ -0,0 +1,611 @@ +#include "CHTTPProtocol.h" +#include "CLog.h" +#include "XHTTP.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include +#include +#include +#include +#include + +// +// CHTTPUtil::CaselessCmp +// + +inline +bool CHTTPUtil::CaselessCmp::cmpEqual( + const CString::value_type& a, + const CString::value_type& b) +{ + // FIXME -- use std::tolower + return tolower(a) == tolower(b); +} + +inline +bool CHTTPUtil::CaselessCmp::cmpLess( + const CString::value_type& a, + const CString::value_type& b) +{ + // FIXME -- use std::tolower + return tolower(a) < tolower(b); +} + +bool CHTTPUtil::CaselessCmp::less( + const CString& a, + const CString& b) +{ + return std::lexicographical_compare( + a.begin(), a.end(), + b.begin(), b.end(), + &CHTTPUtil::CaselessCmp::cmpLess); +} + +bool CHTTPUtil::CaselessCmp::equal( + const CString& a, + const CString& b) +{ + return !(less(a, b) || less(b, a)); +} + +bool CHTTPUtil::CaselessCmp::operator()( + const CString& a, + const CString& b) const +{ + return less(a, b); +} + + +// +// CHTTPProtocol +// + +CHTTPRequest* CHTTPProtocol::readRequest(IInputStream* stream) +{ + CString scratch; + + // parse request line by line + CHTTPRequest* request = new CHTTPRequest; + try { + CString line; + + // read request line. accept and discard leading empty lines. + do { + line = readLine(stream, scratch); + } while (line.empty()); + + // parse request line: + { + std::istringstream s(line); + s.exceptions(std::ios::goodbit); + CString version; + s >> request->m_method >> request->m_uri >> version; + if (!s || request->m_uri.empty() || version.find("HTTP/") != 0) { + log((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); + throw XHTTP(400); + } + + // parse version + char dot; + s.str(version); + s.ignore(5); + s >> request->m_majorVersion; + s.get(dot); + s >> request->m_minorVersion; + if (!s || dot != '.') { + log((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); + throw XHTTP(400); + } + } + if (!isValidToken(request->m_method)) { + log((CLOG_DEBUG1 "invalid HTTP method: %s", line.c_str())); + throw XHTTP(400); + } + if (request->m_majorVersion < 1 || request->m_minorVersion < 0) { + log((CLOG_DEBUG1 "invalid HTTP version: %s", line.c_str())); + throw XHTTP(400); + } + + // parse headers + readHeaders(stream, request, false, scratch); + + // HTTP/1.1 requests must have a Host header + if (request->m_majorVersion > 1 || + (request->m_majorVersion == 1 && request->m_minorVersion >= 1)) { + if (request->m_headerIndexByName.count("Host") == 0) { + log((CLOG_DEBUG1 "Host header missing")); + throw XHTTP(400); + } + } + + // some methods may not have a body. ensure that the headers + // that indicate the body length do not exist for those methods + // and do exist for others. + if ((request->m_headerIndexByName.count("Transfer-Encoding") == 0 && + request->m_headerIndexByName.count("Content-Length") == 0) != + (request->m_method == "GET" || + request->m_method == "HEAD")) { + log((CLOG_DEBUG1 "HTTP method (%s)/body mismatch", request->m_method.c_str())); + throw XHTTP(400); + } + + // prepare to read the body. the length of the body is + // determined using, in order: + // 1. Transfer-Encoding indicates a "chunked" transfer + // 2. Content-Length is present + // Content-Length is ignored for "chunked" transfers. + CHTTPRequest::CHeaderMap::iterator index = request-> + m_headerIndexByName.find("Transfer-Encoding"); + if (index != request->m_headerIndexByName.end()) { + // we only understand "chunked" encodings + if (!CHTTPUtil::CaselessCmp::equal( + request->m_headers[index->second], "chunked")) { + log((CLOG_DEBUG1 "unsupported Transfer-Encoding %s", request->m_headers[index->second].c_str())); + throw XHTTP(501); + } + + // chunked encoding + UInt32 oldSize; + do { + oldSize = request->m_body.size(); + request->m_body += readChunk(stream, scratch); + } while (request->m_body.size() != oldSize); + + // read footer + readHeaders(stream, request, true, scratch); + + // remove "chunked" from Transfer-Encoding and set the + // Content-Length. + // FIXME + // FIXME -- note that just deleting Transfer-Encoding will + // mess up indices in m_headerIndexByName, and replacing + // it with Content-Length could lead to two of those. + } + else if ((index = request->m_headerIndexByName. + find("Content-Length")) != + request->m_headerIndexByName.end()) { + // FIXME -- check for overly-long requests + + // parse content-length + UInt32 length; + { + std::istringstream s(request->m_headers[index->second]); + s.exceptions(std::ios::goodbit); + s >> length; + if (!s) { + log((CLOG_DEBUG1 "cannot parse Content-Length", request->m_headers[index->second].c_str())); + throw XHTTP(400); + } + } + + // use content length + request->m_body = readBlock(stream, length, scratch); + if (request->m_body.size() != length) { + // length must match size of body + log((CLOG_DEBUG1 "Content-Length/actual length mismatch (%d vs %d)", length, request->m_body.size())); + throw XHTTP(400); + } + } + } + catch (...) { + delete request; + throw; + } + + return request; +} + +void CHTTPProtocol::reply( + IOutputStream* stream, + CHTTPReply& reply) +{ + // suppress body for certain replies + bool hasBody = true; + if ((reply.m_status / 100) == 1 || + reply.m_status == 204 || + reply.m_status == 304) { + hasBody = false; + } + + // adjust headers + for (CHTTPReply::CHeaderList::iterator + index = reply.m_headers.begin(); + index != reply.m_headers.end(); ) { + const CString& header = index->first; + + // remove certain headers + if (CHTTPUtil::CaselessCmp::equal(header, "Content-Length") || + CHTTPUtil::CaselessCmp::equal(header, "Date") || + CHTTPUtil::CaselessCmp::equal(header, "Transfer-Encoding")) { + // FIXME -- Transfer-Encoding should be left as-is if + // not "chunked" and if the version is 1.1 or up. + index = reply.m_headers.erase(index); + } + + // keep as-is + else { + ++index; + } + } + + // write reply header + ostringstream s; + s << "HTTP/" << reply.m_majorVersion << "." << + reply.m_minorVersion << " " << + reply.m_status << " " << + reply.m_reason << "\r\n"; + + // get date + // FIXME -- should use C++ locale stuff but VC++ time_put is broken. + // FIXME -- double check that VC++ is broken + // FIXME -- should mutex gmtime() since the return value may not + // be thread safe + char date[30]; + { + const char* oldLocale = setlocale(LC_TIME, "C"); + time_t t = time(NULL); + struct tm* tm = gmtime(&t); + strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", tm); + setlocale(LC_TIME, oldLocale); + } + + // write headers + s << "Date: " << date << "\r\n"; + for (CHTTPReply::CHeaderList::const_iterator + index = reply.m_headers.begin(); + index != reply.m_headers.end(); ++index) { + s << index->first << ": " << index->second << "\r\n"; + } + if (hasBody) { + s << "Content-Length: " << reply.m_body.size() << "\r\n"; + } + s << "Connection: close\r\n"; + + // write end of headers + s << "\r\n"; + + // write to stream + stream->write(s.str().data(), s.str().size()); + + // write body. replies to HEAD method never have a body (though + // they do have the Content-Length header). + if (hasBody && reply.m_method != "HEAD") { + stream->write(reply.m_body.data(), reply.m_body.size()); + } +} + +bool CHTTPProtocol::parseFormData( + const CHTTPRequest& request, + CFormParts& parts) +{ + static const char formData[] = "multipart/form-data"; + static const char boundary[] = "boundary="; + static const char disposition[] = "Content-Disposition:"; + static const char nameAttr[] = "name="; + static const char quote[] = "\""; + + // find the Content-Type header + CHTTPRequest::CHeaderMap::const_iterator contentTypeIndex = + request.m_headerIndexByName.find("Content-Type"); + if (contentTypeIndex == request.m_headerIndexByName.end()) { + // missing required Content-Type header + return false; + } + const CString contentType = request.m_headers[contentTypeIndex->second]; + + // parse type + CString::const_iterator index = std::search( + contentType.begin(), contentType.end(), + formData, formData + sizeof(formData) - 1, + CHTTPUtil::CaselessCmp::cmpEqual); + if (index == contentType.end()) { + // not form-data + return false; + } + index += sizeof(formData) - 1; + index = std::search(index, contentType.end(), + boundary, boundary + sizeof(boundary) - 1, + CHTTPUtil::CaselessCmp::cmpEqual); + if (index == contentType.end()) { + // no boundary + return false; + } + CString delimiter = contentType.c_str() + + (index - contentType.begin()) + + sizeof(boundary) - 1; + + // find first delimiter + const CString& body = request.m_body; + CString::size_type partIndex = body.find(delimiter); + if (partIndex == CString::npos) { + return false; + } + + // skip over it + partIndex += delimiter.size(); + + // prepend CRLF-- to delimiter + delimiter = "\r\n--" + delimiter; + + // parse parts until there are no more + for (;;) { + // is it the last part? + if (body.size() >= partIndex + 2 && + body[partIndex ] == '-' && + body[partIndex + 1] == '-') { + // found last part. success if there's no trailing data. + // FIXME -- check for trailing data (other than a single CRLF) + return true; + } + + // find the end of this part + CString::size_type nextPart = body.find(delimiter, partIndex); + if (nextPart == CString::npos) { + // no terminator + return false; + } + + // find end of headers + CString::size_type endOfHeaders = body.find("\r\n\r\n", partIndex); + if (endOfHeaders == CString::npos || endOfHeaders > nextPart) { + // bad part + return false; + } + endOfHeaders += 2; + + // now find Content-Disposition + index = std::search(body.begin() + partIndex, + body.begin() + endOfHeaders, + disposition, + disposition + sizeof(disposition) - 1, + CHTTPUtil::CaselessCmp::cmpEqual); + if (index == contentType.begin() + endOfHeaders) { + // bad part + return false; + } + + // find the name in the Content-Disposition + CString::size_type endOfHeader = body.find("\r\n", + index - body.begin()); + if (endOfHeader >= endOfHeaders) { + // bad part + return false; + } + index = std::search(index, body.begin() + endOfHeader, + nameAttr, nameAttr + sizeof(nameAttr) - 1, + CHTTPUtil::CaselessCmp::cmpEqual); + if (index == body.begin() + endOfHeader) { + // no name + return false; + } + + // extract the name + CString name; + index += sizeof(nameAttr) - 1; + if (*index == quote[0]) { + // quoted name + ++index; + CString::size_type namePos = index - body.begin(); + index = std::search(index, body.begin() + endOfHeader, + quote, quote + 1, + CHTTPUtil::CaselessCmp::cmpEqual); + if (index == body.begin() + endOfHeader) { + // missing close quote + return false; + } + name = body.substr(namePos, index - body.begin() - namePos); + } + else { + // unquoted name + name = body.substr(index - body.begin(), + body.find_first_of(" \t\r\n")); + } + + // save part. add 2 to endOfHeaders to skip CRLF. + parts.insert(std::make_pair(name, body.substr(endOfHeaders + 2, + nextPart - (endOfHeaders + 2)))); + + // move to next part + partIndex = nextPart + delimiter.size(); + } + + // should've found the last delimiter inside the loop but we did not + return false; +} + +CString CHTTPProtocol::readLine( + IInputStream* stream, + CString& tmpBuffer) +{ + // read up to and including a CRLF from stream, using whatever + // is in tmpBuffer as if it were at the head of the stream. + + for (;;) { + // scan tmpBuffer for CRLF + CString::size_type newline = tmpBuffer.find("\r\n"); + if (newline != CString::npos) { + // copy line without the CRLF + CString line = tmpBuffer.substr(0, newline); + + // discard line and CRLF from tmpBuffer + tmpBuffer.erase(0, newline + 2); + return line; + } + + // read more from stream + char buffer[4096]; + UInt32 n = stream->read(buffer, sizeof(buffer)); + if (n == 0) { + // stream is empty. return what's leftover. + CString line = tmpBuffer; + tmpBuffer.erase(); + return line; + } + + // append stream data + tmpBuffer.append(buffer, n); + } +} + +CString CHTTPProtocol::readBlock( + IInputStream* stream, + UInt32 numBytes, + CString& tmpBuffer) +{ + CString data; + + // read numBytes from stream, using whatever is in tmpBuffer as + // if it were at the head of the stream. + if (tmpBuffer.size() > 0) { + // ignore stream if there's enough data in tmpBuffer + if (tmpBuffer.size() >= numBytes) { + data = tmpBuffer.substr(0, numBytes); + tmpBuffer.erase(0, numBytes); + return data; + } + + // move everything out of tmpBuffer into data + data = tmpBuffer; + tmpBuffer.erase(); + } + + // account for bytes read so far + assert(data.size() < numBytes); + numBytes -= data.size(); + + // read until we have all the requested data + while (numBytes > 0) { + // read max(4096, bytes_left) bytes into buffer + char buffer[4096]; + UInt32 n = sizeof(buffer); + if (n > numBytes) { + n = numBytes; + } + n = stream->read(buffer, n); + + // if stream is empty then return what we've got so far + if (n == 0) { + break; + } + + // append stream data + data.append(buffer, n); + numBytes -= n; + } + + return data; +} + +CString CHTTPProtocol::readChunk( + IInputStream* stream, + CString& tmpBuffer) +{ + CString line; + + // get chunk header + line = readLine(stream, tmpBuffer); + + // parse chunk size + UInt32 size; + { + std::istringstream s(line); + s.exceptions(std::ios::goodbit); + s >> std::hex >> size; + if (!s) { + log((CLOG_DEBUG1 "cannot parse chunk size", line.c_str())); + throw XHTTP(400); + } + } + if (size == 0) { + return CString(); + } + + // read size bytes + // FIXME -- check for overly-long requests + CString data = readBlock(stream, size, tmpBuffer); + if (data.size() != size) { + log((CLOG_DEBUG1 "expected/actual chunk size mismatch", size, data.size())); + throw XHTTP(400); + } + + // read an discard CRLF + line = readLine(stream, tmpBuffer); + if (!line.empty()) { + log((CLOG_DEBUG1 "missing CRLF after chunk")); + throw XHTTP(400); + } + + return data; +} + +void CHTTPProtocol::readHeaders( + IInputStream* stream, + CHTTPRequest* request, + bool isFooter, + CString& tmpBuffer) +{ + // parse headers. done with headers when we get a blank line. + CString line = readLine(stream, tmpBuffer); + while (!line.empty()) { + // if line starts with space or tab then append it to the + // previous header. if there is no previous header then + // throw. + if (line[0] == ' ' || line[0] == '\t') { + if (request->m_headers.size() == 0) { + log((CLOG_DEBUG1 "first header is a continuation")); + throw XHTTP(400); + } + request->m_headers.back() += ","; + request->m_headers.back() == line; + } + + // line should have the form: :[] + else { + // parse + CString name, value; + std::istringstream s(line); + s.exceptions(std::ios::goodbit); + std::getline(s, name, ':'); + if (!s || !isValidToken(name)) { + log((CLOG_DEBUG1 "invalid header: %s", line.c_str())); + throw XHTTP(400); + } + std::getline(s, value); + + // check validity of name + if (isFooter) { + // FIXME -- only certain names are allowed in footers + } + + // check if we've seen this header before + CHTTPRequest::CHeaderMap::iterator index = + request->m_headerIndexByName.find(name); + if (index == request->m_headerIndexByName.end()) { + // it's a new header + request->m_headerIndexByName.insert(std::make_pair(name, + request->m_headers.size())); + request->m_headers.push_back(value); + } + else { + // it's an existing header. append value to previous + // header, separated by a comma. + request->m_headers[index->second] += ','; + request->m_headers[index->second] += value; + } + } + + // next header + line = readLine(stream, tmpBuffer); + + // FIXME -- should check for overly-long requests + } +} + +bool CHTTPProtocol::isValidToken(const CString& token) +{ + return (token.find("()<>@,;:\\\"/[]?={} " + "\0\1\2\3\4\5\6\7" + "\10\11\12\13\14\15\16\17" + "\20\21\22\23\24\25\26\27" + "\30\31\32\33\34\35\36\37\177") == CString::npos); +} diff --git a/http/CHTTPProtocol.h b/http/CHTTPProtocol.h new file mode 100644 index 00000000..216c8e45 --- /dev/null +++ b/http/CHTTPProtocol.h @@ -0,0 +1,88 @@ +#ifndef CHTTPPROTOCOL_H +#define CHTTPPROTOCOL_H + +#include "BasicTypes.h" +#include "CString.h" +#include +#include + +class IInputStream; +class IOutputStream; + +class CHTTPUtil { +public: + class CaselessCmp { + public: + bool operator()(const CString&, const CString&) const; + static bool less(const CString&, const CString&); + static bool equal(const CString&, const CString&); + static bool cmpLess(const CString::value_type&, + const CString::value_type&); + static bool cmpEqual(const CString::value_type&, + const CString::value_type&); + }; +}; + +class CHTTPRequest { +public: + typedef std::map CHeaderMap; + typedef std::vector CHeaderList; + + CString m_method; + CString m_uri; + SInt32 m_majorVersion; + SInt32 m_minorVersion; + + CHeaderList m_headers; + CHeaderMap m_headerIndexByName; + + CString m_body; + // FIXME -- need parts-of-body for POST messages +}; + +class CHTTPReply { +public: + typedef std::vector > CHeaderList; + + SInt32 m_majorVersion; + SInt32 m_minorVersion; + SInt32 m_status; + CString m_reason; + CString m_method; + + CHeaderList m_headers; + + CString m_body; +}; + +class CHTTPProtocol { +public: + // read and parse an HTTP request. result is returned in a + // CHTTPRequest which the client must delete. throws an + // XHTTP if there was a parse error. throws an XIO + // exception if there was a read error. + static CHTTPRequest* readRequest(IInputStream*); + + // send an HTTP reply on the stream + static void reply(IOutputStream*, CHTTPReply&); + + // parse a multipart/form-data body into its parts. returns true + // iff the entire body was correctly parsed. + // FIXME -- name/value pairs insufficient to save part headers + typedef std::map CFormParts; + static bool parseFormData(const CHTTPRequest&, + CFormParts& parts); + +private: + static CString readLine(IInputStream*, CString& tmpBuffer); + static CString readBlock(IInputStream*, + UInt32 numBytes, CString& tmpBuffer); + static CString readChunk(IInputStream*, CString& tmpBuffer); + static void readHeaders(IInputStream*, + CHTTPRequest*, bool isFooter, + CString& tmpBuffer); + + static bool isValidToken(const CString&); +}; + +#endif diff --git a/http/Makefile b/http/Makefile new file mode 100644 index 00000000..dd89e329 --- /dev/null +++ b/http/Makefile @@ -0,0 +1,26 @@ +DEPTH=.. +include $(DEPTH)/Makecommon + +# +# target file +# +TARGET = http + +# +# source files +# +LCXXINCS = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + $(NULL) +CXXFILES = \ + XHTTP.cpp \ + CHTTPProtocol.cpp \ + $(NULL) + +targets: $(LIBTARGET) + +$(LIBTARGET): $(OBJECTS) $(DEPLIBS) + if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi + $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/http/XHTTP.cpp b/http/XHTTP.cpp new file mode 100644 index 00000000..dfae8a97 --- /dev/null +++ b/http/XHTTP.cpp @@ -0,0 +1,119 @@ +#include "XHTTP.h" +#include "CHTTPProtocol.h" +#include + +// +// XHTTP +// + +XHTTP::XHTTP(SInt32 statusCode) : + XBase(), + m_status(statusCode), + m_reason(getReason(statusCode)) +{ + // do nothing +} + +XHTTP::XHTTP(SInt32 statusCode, const CString& reasonPhrase) : + XBase(), + m_status(statusCode), + m_reason(reasonPhrase) +{ + // do nothing +} + +XHTTP::~XHTTP() +{ + // do nothing +} + +SInt32 XHTTP::getStatus() const +{ + return m_status; +} + +CString XHTTP::getReason() const +{ + return m_reason; +} + +void XHTTP::addHeaders(CHTTPReply&) const +{ + // do nothing +} + +CString XHTTP::getWhat() const throw() +{ + try { + char code[20]; + sprintf(code, "%d ", m_status); + CString msg(code); + if (!m_reason.empty()) { + msg += m_reason; + } + else { + msg += getReason(m_status); + } + return msg; + } + catch (...) { + return CString(); + } +} + +const char* XHTTP::getReason(SInt32 status) +{ + switch (status) { + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Moved Temporarily"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Time-out"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Request Entity Too Large"; + case 414: return "Request-URI Too Large"; + case 415: return "Unsupported Media Type"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Time-out"; + case 505: return "HTTP Version not supported"; + default: return ""; + } +} + + +// +// XHTTPAllow +// + +XHTTPAllow::XHTTPAllow(const CString& allowed) : + XHTTP(405), + m_allowed(allowed) +{ + // do nothing +} + +XHTTPAllow::~XHTTPAllow() +{ + // do nothing +} + +void XHTTPAllow::addHeaders(CHTTPReply& reply) const +{ + reply.m_headers.push_back(std::make_pair(CString("Allow"), m_allowed)); +} diff --git a/http/XHTTP.h b/http/XHTTP.h new file mode 100644 index 00000000..754a345f --- /dev/null +++ b/http/XHTTP.h @@ -0,0 +1,44 @@ +#ifndef XHTTP_H +#define XHTTP_H + +#include "BasicTypes.h" +#include "CString.h" +#include "XBase.h" + +class CHTTPReply; + +class XHTTP : public XBase { +public: + XHTTP(SInt32 statusCode); + XHTTP(SInt32 statusCode, const CString& reasonPhrase); + ~XHTTP(); + + SInt32 getStatus() const; + CString getReason() const; + + virtual void addHeaders(CHTTPReply&) const; + +protected: + virtual CString getWhat() const throw(); + +private: + static const char* getReason(SInt32 status); + +private: + SInt32 m_status; + CString m_reason; +}; + +class XHTTPAllow : public XHTTP { +public: + XHTTPAllow(const CString& allowedMethods); + ~XHTTPAllow(); + + // XHTTP overrides + virtual void addHeaders(CHTTPReply&) const; + +private: + CString m_allowed; +}; + +#endif diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp new file mode 100644 index 00000000..be8f1e37 --- /dev/null +++ b/server/CHTTPServer.cpp @@ -0,0 +1,823 @@ +#include "CHTTPServer.h" +#include "CHTTPProtocol.h" +#include "XHTTP.h" +#include "CServer.h" +#include "CScreenMap.h" +#include "CLog.h" +#include "XThread.h" +#include "ISocket.h" +#include +#include + +// +// CHTTPServer +// + +CHTTPServer::CHTTPServer(CServer* server) : m_server(server) +{ + // do nothing +} + +CHTTPServer::~CHTTPServer() +{ + // do nothing +} + +void CHTTPServer::processRequest(ISocket* socket) +{ + assert(socket != NULL); + + CHTTPRequest* request = NULL; + try { + // parse request + request = CHTTPProtocol::readRequest(socket->getInputStream()); + if (request == NULL) { + throw XHTTP(400); + } + + // if absolute uri then strip off scheme and host + if (request->m_uri[0] != '/') { + CString::size_type n = request->m_uri.find('/'); + if (n == CString::npos) { + throw XHTTP(404); + } + request->m_uri = request->m_uri.substr(n); + } + + // process + CHTTPReply reply; + doProcessRequest(*request, reply); + + // send reply + CHTTPProtocol::reply(socket->getOutputStream(), reply); + log((CLOG_INFO "HTTP reply %d for %s %s", reply.m_status, request->m_method.c_str(), request->m_uri.c_str())); + + // clean up + delete request; + } + catch (XHTTP& e) { + log((CLOG_WARN "returning HTTP error %d %s for %s", e.getStatus(), e.getReason().c_str(), (request != NULL) ? request->m_uri.c_str() : "")); + + // clean up + delete request; + + // return error + CHTTPReply reply; + reply.m_majorVersion = 1; + reply.m_minorVersion = 0; + reply.m_status = e.getStatus(); + reply.m_reason = e.getReason(); + reply.m_method = "GET"; +// FIXME -- use a nicer error page + reply.m_headers.push_back(std::make_pair(CString("Content-Type"), + CString("text/plain"))); + reply.m_body = e.getReason(); + e.addHeaders(reply); + CHTTPProtocol::reply(socket->getOutputStream(), reply); + } + catch (...) { + // ignore other exceptions + RETHROW_XTHREAD + } +} + +void CHTTPServer::doProcessRequest( + CHTTPRequest& request, + CHTTPReply& reply) +{ + reply.m_majorVersion = request.m_majorVersion; + reply.m_minorVersion = request.m_minorVersion; + reply.m_status = 200; + reply.m_reason = "OK"; + reply.m_method = request.m_method; + reply.m_headers.push_back(std::make_pair(CString("Content-Type"), + CString("text/html"))); + + // switch based on uri + if (request.m_uri == "/editmap") { + if (request.m_method == "GET" || request.m_method == "HEAD") { + doProcessGetEditMap(request, reply); + } + else if (request.m_method == "POST") { + doProcessPostEditMap(request, reply); + } + else { + throw XHTTPAllow("GET, HEAD, POST"); + } + } + else { + // unknown page + throw XHTTP(404); + } +} + +void CHTTPServer::doProcessGetEditMap( + CHTTPRequest& /*request*/, + CHTTPReply& reply) +{ + static const char* s_editMapProlog1 = + "\r\n" + "\r\n" + " Synergy -- Edit Screens\r\n" + "\r\n" + "\r\n" + "
\r\n" + " \r\n"; + static const char* s_editMapEpilog = + " \r\n" + " \r\n" + "
\r\n" + "
\r\n" + "\r\n" + "\r\n"; + static const char* s_editMapTableProlog = + "
" + " \r\n"; + static const char* s_editMapTableEpilog = + "
" + "
\r\n"; + static const char* s_editMapRowProlog = + "\r\n"; + static const char* s_editMapRowEpilog = + "\r\n"; + static const char* s_editMapScreenDummy = + ""; + static const char* s_editMapScreenPrimary = + ""; + static const char* s_editMapScreenEnd = + "\r\n"; + + ostringstream s; + + // convert screen map into a temporary screen map + CScreenArray screens; + { + CScreenMap currentMap; + m_server->getScreenMap(¤tMap); + screens.convertFrom(currentMap); + // FIXME -- note to user if currentMap couldn't be exactly represented + } + + // insert blank columns and rows around array (to allow the user + // to insert new screens) + screens.insertColumn(0); + screens.insertColumn(screens.getWidth()); + screens.insertRow(0); + screens.insertRow(screens.getHeight()); + + // get array size + const SInt32 w = screens.getWidth(); + const SInt32 h = screens.getHeight(); + + // construct reply + reply.m_body += s_editMapProlog1; + s << w << "x" << h; + reply.m_body += s.str(); + reply.m_body += s_editMapProlog2; + + // add screen map for editing + const CString primaryName = m_server->getPrimaryScreenName(); + reply.m_body += s_editMapTableProlog; + for (SInt32 y = 0; y < h; ++y) { + reply.m_body += s_editMapRowProlog; + for (SInt32 x = 0; x < w; ++x) { + s.str(""); + if (!screens.isAllowed(x, y) && screens.get(x, y) != primaryName) { + s << s_editMapScreenDummy; + } + else { + if (!screens.isSet(x, y)) { + s << s_editMapScreenDead; + } + else if (screens.get(x, y) == primaryName) { + s << s_editMapScreenPrimary; + } + else { + s << s_editMapScreenLive; + } + s << screens.get(x, y) << + s_editMapScreenLiveDead1 << + "n" << x << "x" << y << + s_editMapScreenLiveDead2; + } + s << s_editMapScreenEnd; + reply.m_body += s.str(); + } + reply.m_body += s_editMapRowEpilog; + } + reply.m_body += s_editMapTableEpilog; + + reply.m_body += s_editMapEpilog; +} + +void CHTTPServer::doProcessPostEditMap( + CHTTPRequest& request, + CHTTPReply& reply) +{ + typedef std::vector ScreenArray; + typedef std::set ScreenSet; + + // parse the result + CHTTPProtocol::CFormParts parts; + if (!CHTTPProtocol::parseFormData(request, parts)) { + log((CLOG_WARN "editmap: cannot parse form data")); + throw XHTTP(400); + } + + try { + ostringstream s; + + // convert post data into a temporary screen map. also check + // that no screen name is invalid or used more than once. + SInt32 w, h; + CHTTPProtocol::CFormParts::iterator index = parts.find("size"); + if (index == parts.end() || + !parseXY(index->second, w, h) || + w <= 0 || h <= 0) { + log((CLOG_WARN "editmap: cannot parse size or size is invalid")); + throw XHTTP(400); + } + ScreenSet screenNames; + CScreenArray screens; + screens.resize(w, h); + for (SInt32 y = 0; y < h; ++y) { + for (SInt32 x = 0; x < w; ++x) { + // find part + s.str(""); + s << "n" << x << "x" << y; + index = parts.find(s.str()); + if (index == parts.end()) { + // FIXME -- screen is missing. error? + continue; + } + + // skip blank names + const CString& name = index->second; + if (name.empty()) { + continue; + } + + // check name. name must be legal and must not have + // already been seen. + if (screenNames.count(name)) { + // FIXME -- better error message + log((CLOG_WARN "editmap: duplicate name %s", name.c_str())); + throw XHTTP(400); + } + // FIXME -- check that name is legal + + // save name. if we've already seen the name then + // report an error. + screens.set(x, y, name); + screenNames.insert(name); + } + } + + // if new map is invalid then return error. map is invalid if: + // there are no screens, or + // the screens are not 4-connected. + if (screenNames.empty()) { + // no screens + // FIXME -- need better no screens + log((CLOG_WARN "editmap: no screens")); + throw XHTTP(400); + } + if (!screens.isValid()) { + // FIXME -- need better unconnected screens error + log((CLOG_WARN "editmap: unconnected screens")); + throw XHTTP(400); + } + + // convert temporary screen map into a regular map + CScreenMap newMap; + screens.convertTo(newMap); + + // set new screen map on server + m_server->setScreenMap(newMap); + + // now reply with current map + doProcessGetEditMap(request, reply); + } + catch (XHTTP& e) { + // FIXME -- construct a more meaningful error? + throw; + } +} + +bool CHTTPServer::parseXY( + const CString& xy, SInt32& x, SInt32& y) +{ + istringstream s(xy); + char delimiter; + s >> x; + s.get(delimiter); + s >> y; + return (!!s && delimiter == 'x'); +} + +/* +#include // FIXME +// FIXME + cout << "method: " << request.m_method << endl; + cout << "uri: " << request.m_uri << endl; + cout << "version: " << request.m_majorVersion << "." << + request.m_majorVersion << endl; + + cout << "headers:" << endl; + for (CHTTPRequest::CHeaderMap::const_iterator + index = request.m_headerIndexByName.begin(); + index != request.m_headerIndexByName.end(); + ++index) { + assert(index->second < request.m_headers.size()); + cout << " " << index->first << ": " << + request.m_headers[index->second] << endl; + } + cout << endl; + + cout << request.m_body << endl; + +// FIXME + reply.m_majorVersion = request.m_majorVersion; + reply.m_minorVersion = request.m_minorVersion; + reply.m_status = 200; + reply.m_reason = "OK"; + reply.m_method = request.m_method; + reply.m_headers.push_back(std::make_pair(CString("Content-Type"), + CString("text/html"))); +if (request.m_uri != "/bar") { + reply.m_body = +"\r\n" +" \r\n" +" test\r\n" +" \r\n" +" \r\n" +"

test

\r\n" +"
\r\n" +" \r\n" +" \r\n" +"
\r\n" +" \r\n" +"\r\n" +; +} +else { + reply.m_body = +"\r\n" +" \r\n" +" test reply\r\n" +" \r\n" +" \r\n" +"

test reply

\r\n" +" \r\n" +"\r\n" +; +} + // FIXME +*/ + +CHTTPServer::CScreenArray::CScreenArray() : m_w(0), m_h(0) +{ + // do nothing +} + +CHTTPServer::CScreenArray::~CScreenArray() +{ + // do nothing +} + +void CHTTPServer::CScreenArray::resize(SInt32 w, SInt32 h) +{ + m_screens.clear(); + m_screens.resize(w * h); + m_w = w; + m_h = h; +} + +void CHTTPServer::CScreenArray::insertRow(SInt32 i) +{ + assert(i >= 0 && i <= m_h); + + CNames newScreens; + newScreens.resize(m_w * (m_h + 1)); + + for (SInt32 y = 0; y < i; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + y * m_w] = m_screens[x + y * m_w]; + } + } + for (SInt32 y = i; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + (y + 1) * m_w] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + ++m_h; +} + +void CHTTPServer::CScreenArray::insertColumn(SInt32 i) +{ + assert(i >= 0 && i <= m_w); + + CNames newScreens; + newScreens.resize((m_w + 1) * m_h); + + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < i; ++x) { + newScreens[x + y * (m_w + 1)] = m_screens[x + y * m_w]; + } + for (SInt32 x = i; x < m_w; ++x) { + newScreens[(x + 1) + y * (m_w + 1)] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + ++m_w; +} + +void CHTTPServer::CScreenArray::eraseRow(SInt32 i) +{ + assert(i >= 0 && i < m_h); + + CNames newScreens; + newScreens.resize(m_w * (m_h - 1)); + + for (SInt32 y = 0; y < i; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + y * m_w] = m_screens[x + y * m_w]; + } + } + for (SInt32 y = i + 1; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + (y - 1) * m_w] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + --m_h; +} + +void CHTTPServer::CScreenArray::eraseColumn(SInt32 i) +{ + assert(i >= 0 && i < m_w); + + CNames newScreens; + newScreens.resize((m_w - 1) * m_h); + + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + newScreens[x + y * (m_w - 1)] = m_screens[x + y * m_w]; + } + for (SInt32 x = i + 1; x < m_w; ++x) { + newScreens[(x - 1) + y * (m_w - 1)] = m_screens[x + y * m_w]; + } + } + + m_screens.swap(newScreens); + --m_w; +} + +void CHTTPServer::CScreenArray::rotateRows(SInt32 i) +{ + // nothing to do if no rows + if (m_h == 0) { + return; + } + + // convert to canonical form + if (i < 0) { + i = m_h - ((-i) % m_h); + } + else { + i %= m_h; + } + if (i == 0 || i == m_h) { + return; + } + + while (i > 0) { + // rotate one row + for (SInt32 x = 0; x < m_w; ++x) { + CString tmp = m_screens[x]; + for (SInt32 y = 1; y < m_h; ++y) { + m_screens[x + (y - 1) * m_w] = m_screens[x + y * m_w]; + } + m_screens[x + (m_h - 1) * m_w] = tmp; + } + } +} + +void CHTTPServer::CScreenArray::rotateColumns(SInt32 i) +{ + // nothing to do if no columns + if (m_h == 0) { + return; + } + + // convert to canonical form + if (i < 0) { + i = m_w - ((-i) % m_w); + } + else { + i %= m_w; + } + if (i == 0 || i == m_w) { + return; + } + + while (i > 0) { + // rotate one column + for (SInt32 y = 0; y < m_h; ++y) { + CString tmp = m_screens[0 + y * m_w]; + for (SInt32 x = 1; x < m_w; ++x) { + m_screens[x - 1 + y * m_w] = m_screens[x + y * m_w]; + } + m_screens[m_w - 1 + y * m_w] = tmp; + } + } +} + +void CHTTPServer::CScreenArray::remove(SInt32 x, SInt32 y) +{ + set(x, y, CString()); +} + +void CHTTPServer::CScreenArray::set( + SInt32 x, SInt32 y, const CString& name) +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + m_screens[x + y * m_w] = name; +} + +bool CHTTPServer::CScreenArray::isAllowed( + SInt32 x, SInt32 y) const +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + if (x > 0 && !m_screens[(x - 1) + y * m_w].empty()) { + return true; + } + if (x < m_w - 1 && !m_screens[(x + 1) + y * m_w].empty()) { + return true; + } + if (y > 0 && !m_screens[x + (y - 1) * m_w].empty()) { + return true; + } + if (y < m_h - 1 && !m_screens[x + (y + 1) * m_w].empty()) { + return true; + } + return false; +} + +bool CHTTPServer::CScreenArray::isSet( + SInt32 x, SInt32 y) const +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + return !m_screens[x + y * m_w].empty(); +} + +CString CHTTPServer::CScreenArray::get( + SInt32 x, SInt32 y) const +{ + assert(x >= 0 && x < m_w); + assert(y >= 0 && y < m_h); + + return m_screens[x + y * m_w]; +} + +bool CHTTPServer::CScreenArray::find( + const CString& name, + SInt32& xOut, SInt32& yOut) const +{ + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (m_screens[x + y * m_w] == name) { + xOut = x; + yOut = y; + return true; + } + } + } + return false; +} + +bool CHTTPServer::CScreenArray::isValid() const +{ + SInt32 count = 0, isolated = 0; + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (isSet(x, y)) { + ++count; + if (!isAllowed(x, y)) { + ++isolated; + } + } + } + } + return (count <= 1 || isolated == 0); +} + +bool CHTTPServer::CScreenArray::convertFrom( + const CScreenMap& screenMap) +{ + typedef std::set ScreenSet; + + // insert the first screen + CScreenMap::const_iterator index = screenMap.begin(); + if (index == screenMap.end()) { + // no screens + resize(0, 0); + return true; + } + CString name = *index; + resize(1, 1); + set(0, 0, name); + + // flood fill state + CNames screenStack; + ScreenSet doneSet; + + // put all but the first screen on the stack + // note -- if all screens are 4-connected then we can skip this + while (++index != screenMap.end()) { + screenStack.push_back(*index); + } + + // put the first screen on the stack last so we process it first + screenStack.push_back(name); + + // perform a flood fill using the stack as the seeds + while (!screenStack.empty()) { + // get next screen from stack + CString name = screenStack.back(); + screenStack.pop_back(); + + // skip screen if we've seen it before + if (doneSet.count(name) > 0) { + continue; + } + + // add this screen to doneSet so we don't process it again + doneSet.insert(name); + + // find the screen. if it's not found then not all of the + // screens are 4-connected. discard disconnected screens. + SInt32 x, y; + if (!find(name, x, y)) { + continue; + } + + // insert the screen's neighbors + // FIXME -- handle edge wrapping + CString neighbor; + neighbor = screenMap.getNeighbor(name, CScreenMap::kLeft); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert left neighbor, adding a column if necessary + if (x == 0 || get(x - 1, y) != neighbor) { + ++x; + insertColumn(x - 1); + set(x - 1, y, neighbor); + } + screenStack.push_back(neighbor); + } + neighbor = screenMap.getNeighbor(name, CScreenMap::kRight); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert right neighbor, adding a column if necessary + if (x == m_w - 1 || get(x + 1, y) != neighbor) { + insertColumn(x + 1); + set(x + 1, y, neighbor); + } + screenStack.push_back(neighbor); + } + neighbor = screenMap.getNeighbor(name, CScreenMap::kTop); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert top neighbor, adding a row if necessary + if (y == 0 || get(x, y - 1) != neighbor) { + ++y; + insertRow(y - 1); + set(x, y - 1, neighbor); + } + screenStack.push_back(neighbor); + } + neighbor = screenMap.getNeighbor(name, CScreenMap::kBottom); + if (!neighbor.empty() && doneSet.count(neighbor) == 0) { + // insert bottom neighbor, adding a row if necessary + if (y == m_h - 1 || get(x, y + 1) != neighbor) { + insertRow(y + 1); + set(x, y + 1, neighbor); + } + screenStack.push_back(neighbor); + } + } + + // check symmetry + // FIXME -- handle edge wrapping + for (index = screenMap.begin(); index != screenMap.end(); ++index) { + const CString& name = *index; + SInt32 x, y; + if (!find(name, x, y)) { + return false; + } + + CString neighbor; + neighbor = screenMap.getNeighbor(name, CScreenMap::kLeft); + if ((x == 0 && !neighbor.empty()) || + (x > 0 && get(x - 1, y) != neighbor)) { + return false; + } + + neighbor = screenMap.getNeighbor(name, CScreenMap::kRight); + if ((x == m_w - 1 && !neighbor.empty()) || + (x < m_w - 1 && get(x + 1, y) != neighbor)) { + return false; + } + + neighbor = screenMap.getNeighbor(name, CScreenMap::kTop); + if ((y == 0 && !neighbor.empty()) || + (y > 0 && get(x, y - 1) != neighbor)) { + return false; + } + + neighbor = screenMap.getNeighbor(name, CScreenMap::kBottom); + if ((y == m_h - 1 && !neighbor.empty()) || + (y < m_h - 1 && get(x, y + 1) != neighbor)) { + return false; + } + } + + return true; +} + +void CHTTPServer::CScreenArray::convertTo( + CScreenMap& screenMap) const +{ + // add screens and find smallest box containing all screens + SInt32 x0 = m_w, x1 = 0, y0 = m_h, y1 = 0; + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (isSet(x, y)) { + screenMap.addScreen(get(x, y)); + if (x < x0) { + x0 = x; + } + if (x > x1) { + x1 = x; + } + if (y < y0) { + y0 = y; + } + if (y > y1) { + y1 = y; + } + } + + } + } + + // make connections between screens + // FIXME -- add support for wrapping + // FIXME -- mark topmost and leftmost screens + for (SInt32 y = 0; y < m_h; ++y) { + for (SInt32 x = 0; x < m_w; ++x) { + if (!isSet(x, y)) { + continue; + } + if (x > x0 && isSet(x - 1, y)) { + screenMap.connect(get(x, y), + CScreenMap::kLeft, + get(x - 1, y)); + } + if (x < x1 && isSet(x + 1, y)) { + screenMap.connect(get(x, y), + CScreenMap::kRight, + get(x + 1, y)); + } + if (y > y0 && isSet(x, y - 1)) { + screenMap.connect(get(x, y), + CScreenMap::kTop, + get(x, y - 1)); + } + if (y < y1 && isSet(x, y + 1)) { + screenMap.connect(get(x, y), + CScreenMap::kBottom, + get(x, y + 1)); + } + } + } +} diff --git a/server/CHTTPServer.h b/server/CHTTPServer.h new file mode 100644 index 00000000..0fdd9795 --- /dev/null +++ b/server/CHTTPServer.h @@ -0,0 +1,101 @@ +#ifndef CHTTPSERVER_H +#define CHTTPSERVER_H + +#include "BasicTypes.h" +#include "CString.h" +#include + +class CServer; +class CScreenMap; +class CHTTPRequest; +class CHTTPReply; +class ISocket; + +class CHTTPServer { +public: + CHTTPServer(CServer*); + virtual ~CHTTPServer(); + + // manipulators + + // synchronously process an HTTP request on the given socket + void processRequest(ISocket*); + + // accessors + +protected: + virtual void doProcessRequest(CHTTPRequest&, CHTTPReply&); + + virtual void doProcessGetEditMap(CHTTPRequest&, CHTTPReply&); + virtual void doProcessPostEditMap(CHTTPRequest&, CHTTPReply&); + + static bool parseXY(const CString&, SInt32& x, SInt32& y); + + class CScreenArray { + public: + CScreenArray(); + ~CScreenArray(); + + // resize the array. this also clears all the elements. + void resize(SInt32 w, SInt32 h); + + // insert/remove a row/column. all elements in a new row/column + // are unset. + void insertRow(SInt32 insertedBeforeRow); + void insertColumn(SInt32 insertedBeforeColumn); + void eraseRow(SInt32 row); + void eraseColumn(SInt32 column); + + // rotate rows or columns + void rotateRows(SInt32 rowsDown); + void rotateColumns(SInt32 columnsDown); + + // remove/set a screen name. setting an empty name is the + // same as removing a name. names are not checked for + // validity. + void remove(SInt32 x, SInt32 y); + void set(SInt32 x, SInt32 y, const CString&); + + // convert a CScreenMap to a CScreenArray. returns true iff + // all connections are symmetric and therefore exactly + // representable by a CScreenArray. + bool convertFrom(const CScreenMap&); + + // accessors + + // get the array size + SInt32 getWidth() const { return m_w; } + SInt32 getHeight() const { return m_h; } + + // returns true iff the cell has a 4-connected neighbor + bool isAllowed(SInt32 x, SInt32 y) const; + + // returns true iff the cell has a (non-empty) name + bool isSet(SInt32 x, SInt32 y) const; + + // get a screen name + CString get(SInt32 x, SInt32 y) const; + + // find a screen by name. returns true iff found. + bool find(const CString&, SInt32& x, SInt32& y) const; + + // return true iff the overall array is valid. that means + // just zero or one screen or all screens are 4-connected + // to other screens. + bool isValid() const; + + // convert this to a CScreenMap + void convertTo(CScreenMap&) const; + + private: + typedef std::vector CNames; + + SInt32 m_w, m_h; + CNames m_screens; + }; + +private: + CServer* m_server; +}; + +#endif diff --git a/server/CScreenMap.cpp b/server/CScreenMap.cpp index 750427f6..5f5de405 100644 --- a/server/CScreenMap.cpp +++ b/server/CScreenMap.cpp @@ -75,6 +75,18 @@ void CScreenMap::disconnect(const CString& srcName, index->second.m_neighbor[srcSide - kFirstDirection].erase(); } +CScreenMap::const_iterator + CScreenMap::begin() const +{ + return const_iterator(m_map.begin()); +} + +CScreenMap::const_iterator + CScreenMap::end() const +{ + return const_iterator(m_map.end()); +} + CString CScreenMap::getNeighbor(const CString& srcName, EDirection srcSide) const { diff --git a/server/CScreenMap.h b/server/CScreenMap.h index f677b679..b4e242e8 100644 --- a/server/CScreenMap.h +++ b/server/CScreenMap.h @@ -11,6 +11,42 @@ public: kFirstDirection = kLeft, kLastDirection = kBottom }; enum EDirectionMask { kLeftMask = 1, kRightMask = 2, kTopMask = 4, kBottomMask = 8 }; +private: + class CCell { + public: + CString m_neighbor[kLastDirection - kFirstDirection + 1]; + }; + typedef std::map CCellMap; + +public: + typedef CCellMap::const_iterator internal_const_iterator; + class const_iterator : public std::iterator< + std::bidirectional_iterator_tag, + CString, ptrdiff_t, CString*, CString&> { + public: + explicit const_iterator() : m_i() { } + explicit const_iterator(const internal_const_iterator& i) : m_i(i) { } + + const_iterator& operator=(const const_iterator& i) { + m_i = i.m_i; + return *this; + } + CString operator*() { return m_i->first; } + const CString* operator->() { return &(m_i->first); } + const_iterator& operator++() { ++m_i; return *this; } + const_iterator operator++(int) { return const_iterator(m_i++); } + const_iterator& operator--() { --m_i; return *this; } + const_iterator operator--(int) { return const_iterator(m_i--); } + bool operator==(const const_iterator& i) const { + return (m_i == i.m_i); + } + bool operator!=(const const_iterator& i) const { + return (m_i != i.m_i); + } + + private: + CScreenMap::internal_const_iterator m_i; + }; CScreenMap(); virtual ~CScreenMap(); @@ -31,6 +67,10 @@ public: // accessors + // iterators over screen names + const_iterator begin() const; + const_iterator end() const; + // get the neighbor in the given direction. returns the empty string // if there is no neighbor in that direction. CString getNeighbor(const CString&, EDirection) const; @@ -39,12 +79,6 @@ public: static const char* dirName(EDirection); private: - class CCell { - public: - CString m_neighbor[kLastDirection - kFirstDirection + 1]; - }; - typedef std::map CCellMap; - CCellMap m_map; }; diff --git a/server/CServer.cpp b/server/CServer.cpp index 6c8001a3..fdb1088b 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -1,4 +1,5 @@ #include "CServer.h" +#include "CHTTPServer.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CServerProtocol.h" @@ -15,6 +16,7 @@ #include "CThread.h" #include "CTimerThread.h" #include "CStopwatch.h" +#include "CFunctionJob.h" #include "TMethodJob.h" #include "CLog.h" #include @@ -44,7 +46,8 @@ else { wait(0); exit(1); } CServer::CServer() : m_primary(NULL), m_active(NULL), m_primaryInfo(NULL), - m_seqNum(0) + m_seqNum(0), + m_httpServer(NULL) { m_socketFactory = NULL; m_securityFactory = NULL; @@ -63,18 +66,21 @@ void CServer::run() // connect to primary screen openPrimaryScreen(); + // start listening for HTTP requests + m_httpServer = new CHTTPServer(this); + CThread(new TMethodJob(this, &CServer::acceptHTTPClients)); + // start listening for new clients CThread(new TMethodJob(this, &CServer::acceptClients)); - // start listening for configuration connections - // FIXME - // handle events log((CLOG_DEBUG "starting event handling")); m_primary->run(); // clean up log((CLOG_NOTE "stopping server")); + delete m_httpServer; + m_httpServer = NULL; cleanupThreads(); closePrimaryScreen(); } @@ -82,6 +88,8 @@ void CServer::run() log((CLOG_ERR "server error: %s", e.what())); // clean up + delete m_httpServer; + m_httpServer = NULL; cleanupThreads(); closePrimaryScreen(); } @@ -89,6 +97,8 @@ void CServer::run() log((CLOG_DEBUG "unknown server error")); // clean up + delete m_httpServer; + m_httpServer = NULL; cleanupThreads(); closePrimaryScreen(); throw; @@ -109,6 +119,12 @@ void CServer::setScreenMap(const CScreenMap& screenMap) m_screenMap = screenMap; } +CString CServer::getPrimaryScreenName() const +{ + CLock lock(&m_mutex); + return (m_primaryInfo == NULL) ? "" : m_primaryInfo->m_name; +} + void CServer::getScreenMap(CScreenMap* screenMap) const { assert(screenMap != NULL); @@ -956,6 +972,88 @@ void CServer::handshakeClient(void* vsocket) } } +void CServer::acceptHTTPClients(void*) +{ + log((CLOG_DEBUG1 "starting to wait for HTTP clients")); + + // add this thread to the list of threads to cancel. remove from + // list in d'tor. + CCleanupNote cleanupNote(this); + + std::auto_ptr listen; + try { + // create socket listener +// listen = std::auto_ptr(m_socketFactory->createListen()); + assign(listen, new CTCPListenSocket, IListenSocket); // FIXME + + // bind to the desired port. keep retrying if we can't bind + // the address immediately. + CStopwatch timer; + CNetworkAddress addr(50002 /* FIXME -- m_httpPort */); + for (;;) { + try { + log((CLOG_DEBUG1 "binding listen socket")); + listen->bind(addr); + break; + } + catch (XSocketAddressInUse&) { + // give up if we've waited too long + if (timer.getTime() >= m_bindTimeout) { + log((CLOG_DEBUG1 "waited too long to bind HTTP, giving up")); + throw; + } + + // wait a bit before retrying + log((CLOG_DEBUG1 "bind HTTP failed; waiting to retry")); + CThread::sleep(5.0); + } + } + + // accept connections and begin processing them + log((CLOG_DEBUG1 "waiting for HTTP connections")); + for (;;) { + // accept connection + CThread::testCancel(); + ISocket* socket = listen->accept(); + log((CLOG_NOTE "accepted HTTP connection")); + CThread::testCancel(); + + // handle HTTP request + CThread(new TMethodJob( + this, &CServer::processHTTPRequest, socket)); + } + } + catch (XBase& e) { + log((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); + quit(); + } +} + +void CServer::processHTTPRequest(void* vsocket) +{ + // add this thread to the list of threads to cancel. remove from + // list in d'tor. + CCleanupNote cleanupNote(this); + + ISocket* socket = reinterpret_cast(vsocket); + try { + // process the request and force delivery + m_httpServer->processRequest(socket); + socket->getOutputStream()->flush(); + + // wait a moment to give the client a chance to hangup first + CThread::sleep(3.0); + + // clean up + socket->close(); + delete socket; + } + catch (...) { + delete socket; + throw; + } +} + void CServer::clearGotClipboard(ClipboardID id) { for (CScreenList::const_iterator index = m_screens.begin(); diff --git a/server/CServer.h b/server/CServer.h index 5cdadfe5..2baf2a82 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -17,6 +17,7 @@ class IServerProtocol; class ISocketFactory; class ISecurityFactory; class IPrimaryScreen; +class CHTTPServer; class CServer { public: @@ -68,6 +69,9 @@ public: // get the current screen map void getScreenMap(CScreenMap*) const; + // get the primary screen's name + CString getPrimaryScreenName() const; + // get the sides of the primary screen that have neighbors UInt32 getActivePrimarySides() const; @@ -152,6 +156,12 @@ private: // thread method to do startup handshake with client void handshakeClient(void*); + // thread method to accept incoming HTTP connections + void acceptHTTPClients(void*); + + // thread method to process HTTP requests + void processHTTPRequest(void*); + // thread cleanup list maintenance friend class CCleanupNote; void addCleanupThread(const CThread& thread); @@ -200,6 +210,9 @@ private: CScreenMap m_screenMap; CClipboardInfo m_clipboards[kClipboardEnd]; + + // server for processing HTTP requests + CHTTPServer* m_httpServer; }; #endif diff --git a/server/Makefile b/server/Makefile index 301adb38..9cc1e628 100644 --- a/server/Makefile +++ b/server/Makefile @@ -13,6 +13,7 @@ LCXXINCS = \ -I$(DEPTH)/base \ -I$(DEPTH)/mt \ -I$(DEPTH)/io \ + -I$(DEPTH)/http \ -I$(DEPTH)/net \ -I$(DEPTH)/synergy \ $(NULL) @@ -22,6 +23,7 @@ CXXFILES = \ CServerProtocol1_0.cpp \ CXWindowsPrimaryScreen.cpp \ CServer.cpp \ + CHTTPServer.cpp \ server.cpp \ $(NULL) @@ -31,6 +33,7 @@ CXXFILES = \ DEPLIBS = \ $(LIBDIR)/libsynergy.a \ $(LIBDIR)/libnet.a \ + $(LIBDIR)/libhttp.a \ $(LIBDIR)/libio.a \ $(LIBDIR)/libmt.a \ $(LIBDIR)/libbase.a \ From 536eb52337f5cd72eae833bc5005e3f12756a71d Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 31 May 2002 14:25:26 +0000 Subject: [PATCH 115/807] added methods to CLog for getting the outputter, getting and setting the priority filter, and added code for thread safety. added code to apps to enable thread safety in CLog. --- base/CLog.cpp | 82 ++++++++++++++++++++++++++++++++++++----------- base/CLog.h | 35 ++++++++++++++++++++ client/client.cpp | 42 ++++++++++++++++++++++++ server/server.cpp | 44 ++++++++++++++++++++++++- 4 files changed, 184 insertions(+), 19 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index de22098e..7ee437fa 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -9,7 +9,6 @@ #define vsnprintf _vsnprintf #endif -static int g_maxPriority = -1; static const char* g_priority[] = { "FATAL", "ERROR", @@ -33,6 +32,8 @@ static const int g_newlineLength = 2; // CLog::Outputter CLog::s_outputter = NULL; +CLog::Lock CLog::s_lock = &CLog::dummyLock; +int CLog::s_maxPriority = -1; void CLog::print(const char* fmt, ...) { @@ -100,31 +101,75 @@ void CLog::printt(const char* file, int line, void CLog::setOutputter(Outputter outputter) { + CHoldLock lock(s_lock); s_outputter = outputter; } +CLog::Outputter CLog::getOutputter() +{ + CHoldLock lock(s_lock); + return s_outputter; +} + +void CLog::setLock(Lock newLock) +{ + CHoldLock lock(s_lock); + s_lock = (newLock == NULL) ? dummyLock : newLock; +} + +CLog::Lock CLog::getLock() +{ + CHoldLock lock(s_lock); + return (s_lock == dummyLock) ? NULL : s_lock; +} + +void CLog::setFilter(int maxPriority) +{ + CHoldLock lock(s_lock); + s_maxPriority = maxPriority; +} + +int CLog::getFilter() +{ + CHoldLock lock(s_lock); + return getMaxPriority(); +} + +void CLog::dummyLock(bool) +{ + // do nothing +} + +int CLog::getMaxPriority() +{ + CHoldLock lock(s_lock); + + if (s_maxPriority == -1) { +#if defined(NDEBUG) + s_maxPriority = 4; +#else + s_maxPriority = 5; +#endif + const char* priEnv = getenv("SYN_LOG_PRI"); + if (priEnv != NULL) { + for (int i = 0; i < g_numPriority; ++i) { + if (strcmp(priEnv, g_priority[i]) == 0) { + s_maxPriority = i; + break; + } + } + } + } + + return s_maxPriority; +} + void CLog::output(int priority, char* msg) { assert(priority >= 0 && priority < g_numPriority); assert(msg != 0); - if (g_maxPriority == -1) { -#if defined(NDEBUG) - g_maxPriority = 4; -#else - g_maxPriority = 5; -#endif - const char* priEnv = getenv("SYN_LOG_PRI"); - if (priEnv != NULL) { - for (int i = 0; i < g_numPriority; ++i) - if (strcmp(priEnv, g_priority[i]) == 0) { - g_maxPriority = i; - break; - } - } - } - - if (priority <= g_maxPriority) { + if (priority <= getMaxPriority()) { // insert priority label int n = strlen(g_priority[priority]); sprintf(msg + g_maxPriorityLength - n, "%s:", g_priority[priority]); @@ -138,6 +183,7 @@ void CLog::output(int priority, char* msg) #endif // print it + CHoldLock lock(s_lock); if (s_outputter) { s_outputter(msg + g_maxPriorityLength - n); } diff --git a/base/CLog.h b/base/CLog.h index 66cfa799..0a65a787 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -7,12 +7,45 @@ class CLog { public: typedef void (*Outputter)(const char*); + typedef void (*Lock)(bool lock); + // static void print(const char*, ...); static void printt(const char* file, int line, const char*, ...); + + // get/set the function used to write the log. a NULL outputter + // means to use the default which is fprintf(stderr, ...). note + // that the outputter should not call CLog methods but, if it + // does, the current lock function must permit recursive locks. static void setOutputter(Outputter); + static Outputter getOutputter(); + + // get/set the lock/unlock function. use setLock(NULL) to remove + // the locking function. note that the lock function is used when + // retrieving the lock function. there is no default lock function. + static void setLock(Lock); + static Lock getLock(); + + // get/set the minimum priority filter. any message below this + // priority is discarded. the default priority is 4 (INFO) + // (unless built without NDEBUG in which case it's 5 (DEBUG)). + // the default can be overridden by setting the SYN_LOG_PRI env + // var to "CRIT", "ERR", etc. + static void setFilter(int); + static int getFilter(); private: + class CHoldLock { + public: + CHoldLock(CLog::Lock lock) : m_lock(lock) { m_lock(true); } + ~CHoldLock() { m_lock(false); } + + private: + CLog::Lock m_lock; + }; + + static void dummyLock(bool); + static int getMaxPriority(); static void output(int priority, char* msg); static char* vsprint(int pad, char*, int len, const char*, va_list); static int nprint(const char*, va_list); @@ -22,6 +55,8 @@ private: private: static Outputter s_outputter; + static Lock s_lock; + static int s_maxPriority; }; #if defined(NOLOGGING) diff --git a/client/client.cpp b/client/client.cpp index 8271c6e3..479dbe06 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -1,14 +1,47 @@ #include "CClient.h" #include "CString.h" +#include "CLog.h" +#include "CMutex.h" #include "CNetwork.h" #include "CNetworkAddress.h" #include "CThread.h" +// +// logging thread safety +// + +static CMutex* s_logMutex = NULL; + +static void logLock(bool lock) +{ + assert(s_logMutex != NULL); + + if (lock) { + s_logMutex->lock(); + } + else { + s_logMutex->unlock(); + } +} + + +// +// main +// + void realMain(const CString& name, const CString& hostname, UInt16 port) { + // initialize threading library CThread::init(); + + // make logging thread safe + CMutex logMutex; + s_logMutex = &logMutex; + CLog::setLock(&logLock); + + // initialize network library CNetwork::init(); CClient* client = NULL; @@ -18,14 +51,23 @@ void realMain(const CString& name, client->run(addr); delete client; CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; } catch (...) { delete client; CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; throw; } } + +// +// platform dependent entry points +// + #if defined(CONFIG_PLATFORM_WIN32) #include "CMSWindowsScreen.h" diff --git a/server/server.cpp b/server/server.cpp index da5ccf73..114250ba 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -1,11 +1,44 @@ #include "CServer.h" #include "CScreenMap.h" -#include "CThread.h" +#include "CLog.h" +#include "CMutex.h" #include "CNetwork.h" +#include "CThread.h" + +// +// logging thread safety +// + +static CMutex* s_logMutex = NULL; + +static void logLock(bool lock) +{ + assert(s_logMutex != NULL); + + if (lock) { + s_logMutex->lock(); + } + else { + s_logMutex->unlock(); + } +} + + +// +// main +// void realMain() { + // initialize threading library CThread::init(); + + // make logging thread safe + CMutex logMutex; + s_logMutex = &logMutex; + CLog::setLock(&logLock); + + // initialize network library CNetwork::init(); CScreenMap screenMap; @@ -24,14 +57,23 @@ void realMain() server->run(); delete server; CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; } catch (...) { delete server; CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; throw; } } + +// +// platform dependent entry points +// + #if defined(CONFIG_PLATFORM_WIN32) #include "CMSWindowsScreen.h" From ed96354bad57ec4d339e7bfc1794838001d92bdd Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 31 May 2002 14:34:16 +0000 Subject: [PATCH 116/807] checkpoint. renamed CScreenMap.h to CConfig.h. will be changing CScreenMap to CConfig everywhere. --- server/{CScreenMap.h => CConfig.h} | 0 server/CHTTPServer.cpp | 2 +- server/CScreenMap.cpp | 2 +- server/CServer.h | 2 +- server/CSynergyHook.cpp | 2 +- server/server.cpp | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename server/{CScreenMap.h => CConfig.h} (100%) diff --git a/server/CScreenMap.h b/server/CConfig.h similarity index 100% rename from server/CScreenMap.h rename to server/CConfig.h diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index be8f1e37..d4c2360e 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -2,7 +2,7 @@ #include "CHTTPProtocol.h" #include "XHTTP.h" #include "CServer.h" -#include "CScreenMap.h" +#include "CConfig.h" #include "CLog.h" #include "XThread.h" #include "ISocket.h" diff --git a/server/CScreenMap.cpp b/server/CScreenMap.cpp index 5f5de405..e66a4445 100644 --- a/server/CScreenMap.cpp +++ b/server/CScreenMap.cpp @@ -1,4 +1,4 @@ -#include "CScreenMap.h" +#include "CConfig.h" #include // diff --git a/server/CServer.h b/server/CServer.h index 2baf2a82..91055b0e 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -4,7 +4,7 @@ #include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" -#include "CScreenMap.h" +#include "CConfig.h" #include "CClipboard.h" #include "CMutex.h" #include "CString.h" diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index ef3b052a..e08d687f 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -1,5 +1,5 @@ #include "CSynergyHook.h" -#include "CScreenMap.h" +#include "CConfig.h" #include #include diff --git a/server/server.cpp b/server/server.cpp index 114250ba..d5e5b322 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -1,5 +1,5 @@ #include "CServer.h" -#include "CScreenMap.h" +#include "CConfig.h" #include "CLog.h" #include "CMutex.h" #include "CNetwork.h" From 67b149d3a421683e6a605f85d3aaea2ec10d483a Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 31 May 2002 14:43:23 +0000 Subject: [PATCH 117/807] checkpoint. changed CScreenMap to CConfig. must still change CScreenMap.cpp to CConfig.cpp. --- server/CConfig.h | 12 ++--- server/CHTTPServer.cpp | 60 ++++++++++++------------ server/CHTTPServer.h | 10 ++-- server/CScreenMap.cpp | 26 +++++------ server/CServer.cpp | 100 ++++++++++++++++++++-------------------- server/CServer.h | 12 ++--- server/CSynergyHook.cpp | 8 ++-- server/server.cpp | 18 ++++---- 8 files changed, 122 insertions(+), 124 deletions(-) diff --git a/server/CConfig.h b/server/CConfig.h index b4e242e8..33cb1745 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -1,11 +1,11 @@ -#ifndef CSCREENMAP_H -#define CSCREENMAP_H +#ifndef CCONFIG_H +#define CCONFIG_H #include "BasicTypes.h" #include "CString.h" #include -class CScreenMap { +class CConfig { public: enum EDirection { kLeft, kRight, kTop, kBottom, kFirstDirection = kLeft, kLastDirection = kBottom }; @@ -45,11 +45,11 @@ public: } private: - CScreenMap::internal_const_iterator m_i; + CConfig::internal_const_iterator m_i; }; - CScreenMap(); - virtual ~CScreenMap(); + CConfig(); + virtual ~CConfig(); // manipulators diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index d4c2360e..7344fb29 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -163,10 +163,10 @@ void CHTTPServer::doProcessGetEditMap( // convert screen map into a temporary screen map CScreenArray screens; { - CScreenMap currentMap; - m_server->getScreenMap(¤tMap); - screens.convertFrom(currentMap); - // FIXME -- note to user if currentMap couldn't be exactly represented + CConfig config; + m_server->getConfig(&config); + screens.convertFrom(config); + // FIXME -- note to user if config couldn't be exactly represented } // insert blank columns and rows around array (to allow the user @@ -300,11 +300,11 @@ void CHTTPServer::doProcessPostEditMap( } // convert temporary screen map into a regular map - CScreenMap newMap; - screens.convertTo(newMap); + CConfig config; + screens.convertTo(config); // set new screen map on server - m_server->setScreenMap(newMap); + m_server->setConfig(config); // now reply with current map doProcessGetEditMap(request, reply); @@ -634,13 +634,13 @@ bool CHTTPServer::CScreenArray::isValid() const } bool CHTTPServer::CScreenArray::convertFrom( - const CScreenMap& screenMap) + const CConfig& config) { typedef std::set ScreenSet; // insert the first screen - CScreenMap::const_iterator index = screenMap.begin(); - if (index == screenMap.end()) { + CConfig::const_iterator index = config.begin(); + if (index == config.end()) { // no screens resize(0, 0); return true; @@ -655,7 +655,7 @@ bool CHTTPServer::CScreenArray::convertFrom( // put all but the first screen on the stack // note -- if all screens are 4-connected then we can skip this - while (++index != screenMap.end()) { + while (++index != config.end()) { screenStack.push_back(*index); } @@ -686,7 +686,7 @@ bool CHTTPServer::CScreenArray::convertFrom( // insert the screen's neighbors // FIXME -- handle edge wrapping CString neighbor; - neighbor = screenMap.getNeighbor(name, CScreenMap::kLeft); + neighbor = config.getNeighbor(name, CConfig::kLeft); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert left neighbor, adding a column if necessary if (x == 0 || get(x - 1, y) != neighbor) { @@ -696,7 +696,7 @@ bool CHTTPServer::CScreenArray::convertFrom( } screenStack.push_back(neighbor); } - neighbor = screenMap.getNeighbor(name, CScreenMap::kRight); + neighbor = config.getNeighbor(name, CConfig::kRight); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert right neighbor, adding a column if necessary if (x == m_w - 1 || get(x + 1, y) != neighbor) { @@ -705,7 +705,7 @@ bool CHTTPServer::CScreenArray::convertFrom( } screenStack.push_back(neighbor); } - neighbor = screenMap.getNeighbor(name, CScreenMap::kTop); + neighbor = config.getNeighbor(name, CConfig::kTop); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert top neighbor, adding a row if necessary if (y == 0 || get(x, y - 1) != neighbor) { @@ -715,7 +715,7 @@ bool CHTTPServer::CScreenArray::convertFrom( } screenStack.push_back(neighbor); } - neighbor = screenMap.getNeighbor(name, CScreenMap::kBottom); + neighbor = config.getNeighbor(name, CConfig::kBottom); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert bottom neighbor, adding a row if necessary if (y == m_h - 1 || get(x, y + 1) != neighbor) { @@ -728,7 +728,7 @@ bool CHTTPServer::CScreenArray::convertFrom( // check symmetry // FIXME -- handle edge wrapping - for (index = screenMap.begin(); index != screenMap.end(); ++index) { + for (index = config.begin(); index != config.end(); ++index) { const CString& name = *index; SInt32 x, y; if (!find(name, x, y)) { @@ -736,25 +736,25 @@ bool CHTTPServer::CScreenArray::convertFrom( } CString neighbor; - neighbor = screenMap.getNeighbor(name, CScreenMap::kLeft); + neighbor = config.getNeighbor(name, CConfig::kLeft); if ((x == 0 && !neighbor.empty()) || (x > 0 && get(x - 1, y) != neighbor)) { return false; } - neighbor = screenMap.getNeighbor(name, CScreenMap::kRight); + neighbor = config.getNeighbor(name, CConfig::kRight); if ((x == m_w - 1 && !neighbor.empty()) || (x < m_w - 1 && get(x + 1, y) != neighbor)) { return false; } - neighbor = screenMap.getNeighbor(name, CScreenMap::kTop); + neighbor = config.getNeighbor(name, CConfig::kTop); if ((y == 0 && !neighbor.empty()) || (y > 0 && get(x, y - 1) != neighbor)) { return false; } - neighbor = screenMap.getNeighbor(name, CScreenMap::kBottom); + neighbor = config.getNeighbor(name, CConfig::kBottom); if ((y == m_h - 1 && !neighbor.empty()) || (y < m_h - 1 && get(x, y + 1) != neighbor)) { return false; @@ -765,14 +765,14 @@ bool CHTTPServer::CScreenArray::convertFrom( } void CHTTPServer::CScreenArray::convertTo( - CScreenMap& screenMap) const + CConfig& config) const { // add screens and find smallest box containing all screens SInt32 x0 = m_w, x1 = 0, y0 = m_h, y1 = 0; for (SInt32 y = 0; y < m_h; ++y) { for (SInt32 x = 0; x < m_w; ++x) { if (isSet(x, y)) { - screenMap.addScreen(get(x, y)); + config.addScreen(get(x, y)); if (x < x0) { x0 = x; } @@ -799,23 +799,23 @@ void CHTTPServer::CScreenArray::convertTo( continue; } if (x > x0 && isSet(x - 1, y)) { - screenMap.connect(get(x, y), - CScreenMap::kLeft, + config.connect(get(x, y), + CConfig::kLeft, get(x - 1, y)); } if (x < x1 && isSet(x + 1, y)) { - screenMap.connect(get(x, y), - CScreenMap::kRight, + config.connect(get(x, y), + CConfig::kRight, get(x + 1, y)); } if (y > y0 && isSet(x, y - 1)) { - screenMap.connect(get(x, y), - CScreenMap::kTop, + config.connect(get(x, y), + CConfig::kTop, get(x, y - 1)); } if (y < y1 && isSet(x, y + 1)) { - screenMap.connect(get(x, y), - CScreenMap::kBottom, + config.connect(get(x, y), + CConfig::kBottom, get(x, y + 1)); } } diff --git a/server/CHTTPServer.h b/server/CHTTPServer.h index 0fdd9795..60369c31 100644 --- a/server/CHTTPServer.h +++ b/server/CHTTPServer.h @@ -6,7 +6,7 @@ #include class CServer; -class CScreenMap; +class CConfig; class CHTTPRequest; class CHTTPReply; class ISocket; @@ -56,10 +56,10 @@ protected: void remove(SInt32 x, SInt32 y); void set(SInt32 x, SInt32 y, const CString&); - // convert a CScreenMap to a CScreenArray. returns true iff + // convert a CConfig to a CScreenArray. returns true iff // all connections are symmetric and therefore exactly // representable by a CScreenArray. - bool convertFrom(const CScreenMap&); + bool convertFrom(const CConfig&); // accessors @@ -84,8 +84,8 @@ protected: // to other screens. bool isValid() const; - // convert this to a CScreenMap - void convertTo(CScreenMap&) const; + // convert this to a CConfig + void convertTo(CConfig&) const; private: typedef std::vector CNames; diff --git a/server/CScreenMap.cpp b/server/CScreenMap.cpp index e66a4445..7c919277 100644 --- a/server/CScreenMap.cpp +++ b/server/CScreenMap.cpp @@ -2,20 +2,20 @@ #include // -// CScreenMap +// CConfig // -CScreenMap::CScreenMap() +CConfig::CConfig() { // do nothing } -CScreenMap::~CScreenMap() +CConfig::~CConfig() { // do nothing } -void CScreenMap::addScreen(const CString& name) +void CConfig::addScreen(const CString& name) { if (m_map.count(name) != 0) { assert(0 && "name already in map"); // FIXME -- throw instead @@ -23,7 +23,7 @@ void CScreenMap::addScreen(const CString& name) m_map.insert(std::make_pair(name, CCell())); } -void CScreenMap::removeScreen(const CString& name) +void CConfig::removeScreen(const CString& name) { CCellMap::iterator index = m_map.find(name); if (index == m_map.end()) { @@ -43,12 +43,12 @@ void CScreenMap::removeScreen(const CString& name) } } -void CScreenMap::removeAllScreens() +void CConfig::removeAllScreens() { m_map.clear(); } -void CScreenMap::connect(const CString& srcName, +void CConfig::connect(const CString& srcName, EDirection srcSide, const CString& dstName) { @@ -62,7 +62,7 @@ void CScreenMap::connect(const CString& srcName, index->second.m_neighbor[srcSide - kFirstDirection] = dstName; } -void CScreenMap::disconnect(const CString& srcName, +void CConfig::disconnect(const CString& srcName, EDirection srcSide) { // find source cell @@ -75,19 +75,17 @@ void CScreenMap::disconnect(const CString& srcName, index->second.m_neighbor[srcSide - kFirstDirection].erase(); } -CScreenMap::const_iterator - CScreenMap::begin() const +CConfig::const_iterator CConfig::begin() const { return const_iterator(m_map.begin()); } -CScreenMap::const_iterator - CScreenMap::end() const +CConfig::const_iterator CConfig::end() const { return const_iterator(m_map.end()); } -CString CScreenMap::getNeighbor(const CString& srcName, +CString CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const { // find source cell @@ -100,7 +98,7 @@ CString CScreenMap::getNeighbor(const CString& srcName, return index->second.m_neighbor[srcSide - kFirstDirection]; } -const char* CScreenMap::dirName(EDirection dir) +const char* CConfig::dirName(EDirection dir) { static const char* s_name[] = { "left", "right", "top", "bottom" }; return s_name[dir - kFirstDirection]; diff --git a/server/CServer.cpp b/server/CServer.cpp index fdb1088b..0a0a3845 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -110,13 +110,13 @@ void CServer::quit() m_primary->stop(); } -void CServer::setScreenMap(const CScreenMap& screenMap) +void CServer::setConfig(const CConfig& config) { CLock lock(&m_mutex); // FIXME -- must disconnect screens no longer listed // (that may include warping back to server's screen) // FIXME -- server screen must be in new map or map is rejected - m_screenMap = screenMap; + m_config = config; } CString CServer::getPrimaryScreenName() const @@ -125,26 +125,26 @@ CString CServer::getPrimaryScreenName() const return (m_primaryInfo == NULL) ? "" : m_primaryInfo->m_name; } -void CServer::getScreenMap(CScreenMap* screenMap) const +void CServer::getConfig(CConfig* config) const { - assert(screenMap != NULL); + assert(config != NULL); CLock lock(&m_mutex); - *screenMap = m_screenMap; + *config = m_config; } UInt32 CServer::getActivePrimarySides() const { UInt32 sides = 0; CLock lock(&m_mutex); - if (!m_screenMap.getNeighbor("primary", CScreenMap::kLeft).empty()) - sides |= CScreenMap::kLeftMask; - if (!m_screenMap.getNeighbor("primary", CScreenMap::kRight).empty()) - sides |= CScreenMap::kRightMask; - if (!m_screenMap.getNeighbor("primary", CScreenMap::kTop).empty()) - sides |= CScreenMap::kTopMask; - if (!m_screenMap.getNeighbor("primary", CScreenMap::kBottom).empty()) - sides |= CScreenMap::kBottomMask; + if (!m_config.getNeighbor("primary", CConfig::kLeft).empty()) + sides |= CConfig::kLeftMask; + if (!m_config.getNeighbor("primary", CConfig::kRight).empty()) + sides |= CConfig::kRightMask; + if (!m_config.getNeighbor("primary", CConfig::kTop).empty()) + sides |= CConfig::kTopMask; + if (!m_config.getNeighbor("primary", CConfig::kBottom).empty()) + sides |= CConfig::kBottomMask; return sides; } @@ -388,25 +388,25 @@ bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) } // see if we should change screens - CScreenMap::EDirection dir; + CConfig::EDirection dir; if (x < m_active->m_zoneSize) { x -= m_active->m_zoneSize; - dir = CScreenMap::kLeft; + dir = CConfig::kLeft; log((CLOG_DEBUG1 "switch to left")); } else if (x >= m_active->m_width - m_active->m_zoneSize) { x += m_active->m_zoneSize; - dir = CScreenMap::kRight; + dir = CConfig::kRight; log((CLOG_DEBUG1 "switch to right")); } else if (y < m_active->m_zoneSize) { y -= m_active->m_zoneSize; - dir = CScreenMap::kTop; + dir = CConfig::kTop; log((CLOG_DEBUG1 "switch to top")); } else if (y >= m_active->m_height - m_active->m_zoneSize) { y += m_active->m_zoneSize; - dir = CScreenMap::kBottom; + dir = CConfig::kBottom; log((CLOG_DEBUG1 "switch to bottom")); } else { @@ -451,21 +451,21 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) CScreenInfo* newScreen = NULL; if (!isLockedToScreen()) { // find direction of neighbor - CScreenMap::EDirection dir; + CConfig::EDirection dir; if (m_x < 0) - dir = CScreenMap::kLeft; + dir = CConfig::kLeft; else if (m_x > m_active->m_width - 1) - dir = CScreenMap::kRight; + dir = CConfig::kRight; else if (m_y < 0) - dir = CScreenMap::kTop; + dir = CConfig::kTop; else if (m_y > m_active->m_height - 1) - dir = CScreenMap::kBottom; + dir = CConfig::kBottom; else newScreen = m_active; // get neighbor if we should switch if (newScreen == NULL) { - log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->m_name.c_str(), CScreenMap::dirName(dir))); + log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->m_name.c_str(), CConfig::dirName(dir))); SInt32 x = m_x, y = m_y; newScreen = getNeighbor(m_active, dir, x, y); @@ -612,20 +612,20 @@ void CServer::switchScreen(CScreenInfo* dst, } CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, - CScreenMap::EDirection dir) const + CConfig::EDirection dir) const { assert(src != NULL); CString srcName = src->m_name; assert(!srcName.empty()); - log((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CScreenMap::dirName(dir), srcName.c_str())); + log((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); for (;;) { // look up name of neighbor - const CString dstName(m_screenMap.getNeighbor(srcName, dir)); + const CString dstName(m_config.getNeighbor(srcName, dir)); // if nothing in that direction then return NULL if (dstName.empty()) { - log((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CScreenMap::dirName(dir), srcName.c_str())); + log((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); return NULL; } @@ -634,17 +634,17 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, // screen. CScreenList::const_iterator index = m_screens.find(dstName); if (index != m_screens.end()) { - log((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CScreenMap::dirName(dir), srcName.c_str())); + log((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); return index->second; } - log((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CScreenMap::dirName(dir), srcName.c_str())); + log((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); srcName = dstName; } } CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, - CScreenMap::EDirection srcSide, + CConfig::EDirection srcSide, SInt32& x, SInt32& y) const { assert(src != NULL); @@ -658,7 +658,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, // find destination screen, adjusting x or y (but not both) switch (srcSide) { - case CScreenMap::kLeft: + case CConfig::kLeft: while (dst != NULL) { lastGoodScreen = dst; w = lastGoodScreen->m_width; @@ -672,7 +672,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, } break; - case CScreenMap::kRight: + case CConfig::kRight: while (dst != NULL) { lastGoodScreen = dst; x -= w; @@ -686,7 +686,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, } break; - case CScreenMap::kTop: + case CConfig::kTop: while (dst != NULL) { lastGoodScreen = dst; w = lastGoodScreen->m_width; @@ -700,7 +700,7 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, } break; - case CScreenMap::kBottom: + case CConfig::kBottom: while (dst != NULL) { lastGoodScreen = dst; y -= h; @@ -727,26 +727,26 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, if (lastGoodScreen->m_protocol == NULL) { const CString dstName(lastGoodScreen->m_name); switch (srcSide) { - case CScreenMap::kLeft: - if (!m_screenMap.getNeighbor(dstName, CScreenMap::kRight).empty() && + case CConfig::kLeft: + if (!m_config.getNeighbor(dstName, CConfig::kRight).empty() && x > w - 1 - lastGoodScreen->m_zoneSize) x = w - 1 - lastGoodScreen->m_zoneSize; break; - case CScreenMap::kRight: - if (!m_screenMap.getNeighbor(dstName, CScreenMap::kLeft).empty() && + case CConfig::kRight: + if (!m_config.getNeighbor(dstName, CConfig::kLeft).empty() && x < lastGoodScreen->m_zoneSize) x = lastGoodScreen->m_zoneSize; break; - case CScreenMap::kTop: - if (!m_screenMap.getNeighbor(dstName, CScreenMap::kBottom).empty() && + case CConfig::kTop: + if (!m_config.getNeighbor(dstName, CConfig::kBottom).empty() && y > h - 1 - lastGoodScreen->m_zoneSize) y = h - 1 - lastGoodScreen->m_zoneSize; break; - case CScreenMap::kBottom: - if (!m_screenMap.getNeighbor(dstName, CScreenMap::kTop).empty() && + case CConfig::kBottom: + if (!m_config.getNeighbor(dstName, CConfig::kTop).empty() && y < lastGoodScreen->m_zoneSize) y = lastGoodScreen->m_zoneSize; break; @@ -757,18 +757,18 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, } void CServer::mapPosition(CScreenInfo* src, - CScreenMap::EDirection srcSide, + CConfig::EDirection srcSide, CScreenInfo* dst, SInt32& x, SInt32& y) const { assert(src != NULL); assert(dst != NULL); - assert(srcSide >= CScreenMap::kFirstDirection && - srcSide <= CScreenMap::kLastDirection); + assert(srcSide >= CConfig::kFirstDirection && + srcSide <= CConfig::kLastDirection); switch (srcSide) { - case CScreenMap::kLeft: - case CScreenMap::kRight: + case CConfig::kLeft: + case CConfig::kRight: if (y < 0) y = 0; else if (y >= src->m_height) @@ -779,8 +779,8 @@ void CServer::mapPosition(CScreenInfo* src, (src->m_height - 1)); break; - case CScreenMap::kTop: - case CScreenMap::kBottom: + case CConfig::kTop: + case CConfig::kBottom: if (x < 0) x = 0; else if (x >= src->m_width) diff --git a/server/CServer.h b/server/CServer.h index 91055b0e..4a47d821 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -33,7 +33,7 @@ public: void quit(); // update screen map - void setScreenMap(const CScreenMap&); + void setConfig(const CConfig&); // handle events on server's screen. onMouseMovePrimary() returns // true iff the mouse enters a jump zone and jumps. @@ -67,7 +67,7 @@ public: bool isLockedToScreen() const; // get the current screen map - void getScreenMap(CScreenMap*) const; + void getConfig(CConfig*) const; // get the primary screen's name CString getPrimaryScreenName() const; @@ -116,21 +116,21 @@ private: void switchScreen(CScreenInfo*, SInt32 x, SInt32 y); // lookup neighboring screen - CScreenInfo* getNeighbor(CScreenInfo*, CScreenMap::EDirection) const; + CScreenInfo* getNeighbor(CScreenInfo*, CConfig::EDirection) const; // lookup neighboring screen. given a position relative to the // source screen, find the screen we should move onto and where. // if the position is sufficiently far from the source then we // cross multiple screens. CScreenInfo* getNeighbor(CScreenInfo*, - CScreenMap::EDirection, + CConfig::EDirection, SInt32& x, SInt32& y) const; // adjust coordinates to account for resolution differences. the // position is converted to a resolution independent form then // converted back to screen coordinates on the destination screen. void mapPosition(CScreenInfo* src, - CScreenMap::EDirection srcSide, + CConfig::EDirection srcSide, CScreenInfo* dst, SInt32& x, SInt32& y) const; @@ -207,7 +207,7 @@ private: // current mouse position (in absolute secondary screen coordinates) SInt32 m_x, m_y; - CScreenMap m_screenMap; + CConfig m_config; CClipboardInfo m_clipboards[kClipboardEnd]; diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index e08d687f..3c1df9eb 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -187,16 +187,16 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; SInt32 x = (SInt32)info->pt.x; SInt32 y = (SInt32)info->pt.y; - if (!inside && (g_zoneSides & CScreenMap::kLeftMask) != 0) { + if (!inside && (g_zoneSides & CConfig::kLeftMask) != 0) { inside = (x < g_zoneSize); } - if (!inside && (g_zoneSides & CScreenMap::kRightMask) != 0) { + if (!inside && (g_zoneSides & CConfig::kRightMask) != 0) { inside = (x >= g_wScreen - g_zoneSize); } - if (!inside && (g_zoneSides & CScreenMap::kTopMask) != 0) { + if (!inside && (g_zoneSides & CConfig::kTopMask) != 0) { inside = (y < g_zoneSize); } - if (!inside && (g_zoneSides & CScreenMap::kBottomMask) != 0) { + if (!inside && (g_zoneSides & CConfig::kBottomMask) != 0) { inside = (y >= g_hScreen - g_zoneSize); } diff --git a/server/server.cpp b/server/server.cpp index d5e5b322..8a209e74 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -41,19 +41,19 @@ void realMain() // initialize network library CNetwork::init(); - CScreenMap screenMap; - screenMap.addScreen("primary"); - screenMap.addScreen("secondary"); - screenMap.addScreen("secondary2"); - screenMap.connect("primary", CScreenMap::kRight, "secondary"); - screenMap.connect("secondary", CScreenMap::kLeft, "primary"); - screenMap.connect("secondary", CScreenMap::kRight, "secondary2"); - screenMap.connect("secondary2", CScreenMap::kLeft, "secondary"); + CConfig config; + config.addScreen("primary"); + config.addScreen("secondary"); + config.addScreen("secondary2"); + config.connect("primary", CConfig::kRight, "secondary"); + config.connect("secondary", CConfig::kLeft, "primary"); + config.connect("secondary", CConfig::kRight, "secondary2"); + config.connect("secondary2", CConfig::kLeft, "secondary"); CServer* server = NULL; try { server = new CServer(); - server->setScreenMap(screenMap); + server->setConfig(config); server->run(); delete server; CNetwork::cleanup(); From 0eccd52226604d056c0a621cb14143f345f19939 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 31 May 2002 14:44:54 +0000 Subject: [PATCH 118/807] finished renaming CScreenMap to CConfig. --- server/{CScreenMap.cpp => CConfig.cpp} | 0 server/Makefile | 2 +- server/server.dsp | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename server/{CScreenMap.cpp => CConfig.cpp} (100%) diff --git a/server/CScreenMap.cpp b/server/CConfig.cpp similarity index 100% rename from server/CScreenMap.cpp rename to server/CConfig.cpp diff --git a/server/Makefile b/server/Makefile index 9cc1e628..717a9a1a 100644 --- a/server/Makefile +++ b/server/Makefile @@ -18,7 +18,7 @@ LCXXINCS = \ -I$(DEPTH)/synergy \ $(NULL) CXXFILES = \ - CScreenMap.cpp \ + CConfig.cpp \ CServerProtocol.cpp \ CServerProtocol1_0.cpp \ CXWindowsPrimaryScreen.cpp \ diff --git a/server/server.dsp b/server/server.dsp index cdfb0802..db189bee 100644 --- a/server/server.dsp +++ b/server/server.dsp @@ -96,7 +96,7 @@ SOURCE=.\CMSWindowsPrimaryScreen.cpp # End Source File # Begin Source File -SOURCE=.\CScreenMap.cpp +SOURCE=.\CConfig.cpp # End Source File # Begin Source File @@ -128,7 +128,7 @@ SOURCE=.\CMSWindowsPrimaryScreen.h # End Source File # Begin Source File -SOURCE=.\CScreenMap.h +SOURCE=.\CConfig.h # End Source File # Begin Source File From 942e57fc8dedf056a0f490f7dd250e4024e4fdb7 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 31 May 2002 17:32:26 +0000 Subject: [PATCH 119/807] added I/O for configuration files and changed the server to use an external file for its configuration (was hard coding a config for testing). --- server/CConfig.cpp | 308 +++++++++++++++++++++++++++++++++++++++++++++ server/CConfig.h | 35 ++++++ server/server.cpp | 30 +++-- 3 files changed, 364 insertions(+), 9 deletions(-) diff --git a/server/CConfig.cpp b/server/CConfig.cpp index 7c919277..a09e4c7f 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -1,5 +1,12 @@ #include "CConfig.h" #include +// FIXME -- fix this with automake and config.h +#if !defined(CONFIG_PLATFORM_LINUX) +#include +#include +#else +#include +#endif // // CConfig @@ -75,6 +82,50 @@ void CConfig::disconnect(const CString& srcName, index->second.m_neighbor[srcSide - kFirstDirection].erase(); } +bool CConfig::isValidScreenName(const CString& name) const +{ + // name is valid if matches validname + // name ::= [A-Za-z0-9] | [A-Za-z0-9][-A-Za-z0-9]*[A-Za-z0-9] + // domain ::= . name + // validname ::= name domain* + + // check each dot separated part + CString::size_type b = 0; + for (;;) { + // find end of part + CString::size_type e = name.find('.', b); + if (e == CString::npos) { + e = name.size(); + } + + // part may not be empty + if (e - b < 1) { + return false; + } + + // check first and last characters + if (!isalnum(name[b]) || !isalnum(name[e - 1])) { + return false; + } + + // check interior characters + for (CString::size_type i = b; i < e; ++i) { + if (!isalnum(name[i]) && name[i] != '-') { + return false; + } + } + + // next part + if (e == name.size()) { + // no more parts + break; + } + b = e + 1; + } + + return true; +} + CConfig::const_iterator CConfig::begin() const { return const_iterator(m_map.begin()); @@ -85,6 +136,11 @@ CConfig::const_iterator CConfig::end() const return const_iterator(m_map.end()); } +bool CConfig::isScreen(const CString& name) +{ + return (m_map.count(name) > 0); +} + CString CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const { @@ -103,3 +159,255 @@ const char* CConfig::dirName(EDirection dir) static const char* s_name[] = { "left", "right", "top", "bottom" }; return s_name[dir - kFirstDirection]; } + +bool CConfig::readLine(istream& s, CString& line) +{ + s >> std::ws; + while (getline(s, line)) { + // strip comments and then trailing whitespace + CString::size_type i = line.rfind('#'); + if (i != CString::npos) { + line.erase(i); + } + i = line.find_last_not_of(" \t"); + if (i != CString::npos) { + line.erase(i + 1); + } + + // return non empty line + if (!line.empty()) { + return true; + } + s >> std::ws; + } + return false; +} + +void CConfig::readSection(istream& s) +{ + static const char s_section[] = "section:"; + static const char s_screens[] = "screens"; + static const char s_links[] = "links"; + + CString line; + if (!readLine(s, line)) { + // no more sections + return; + } + + // should be a section header + if (line.find(s_section) != 0) { + throw XConfigRead("found data outside section"); + } + + // get section name + CString::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1); + if (i == CString::npos) { + throw XConfigRead("section name is missing"); + } + CString name = line.substr(i); + i = name.find_first_of(" \t"); + if (i != CString::npos) { + throw XConfigRead("unexpected data after section name"); + } + + // read section + if (name == s_screens) { + readSectionScreens(s); + } + else if (name == s_links) { + readSectionLinks(s); + } + else { + throw XConfigRead("unknown section name"); + } +} + +void CConfig::readSectionScreens(istream& s) +{ + CString line; + CString name; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + name = line.substr(0, line.size() - 1); + + // verify validity of screen name + if (!isValidScreenName(name)) { + throw XConfigRead("invalid screen name"); + } + + // add the screen to the configuration + addScreen(name); + } + else if (name.empty()) { + throw XConfigRead("argument before first screen"); + } + else { + throw XConfigRead("unknown argument"); + } + } + throw XConfigRead("unexpected end of screens section"); +} + +void CConfig::readSectionLinks(istream& s) +{ + CString line; + CString screen; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify we known about the screen + if (!isScreen(screen)) { + throw XConfigRead("unknown screen name"); + } + } + else if (screen.empty()) { + throw XConfigRead("argument before first screen"); + } + else { + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead("missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead("missing = in argument"); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead("missing = in argument"); + } + i = line.find_first_not_of(" \t", i + 1); + CString value; + if (i != CString::npos) { + value = line.substr(i); + } + + // handle argument + if (name == "left") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kLeft, value); + } + else if (name == "right") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kRight, value); + } + else if (name == "up") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kTop, value); + } + else if (name == "down") { + if (!isScreen(value)) { + throw XConfigRead("unknown screen"); + } + connect(screen, kBottom, value); + } + else { + // unknown argument + throw XConfigRead("unknown argument"); + } + } + } + throw XConfigRead("unexpected end of links section"); +} + + +// +// CConfig I/O +// + +istream& operator>>(istream& s, CConfig& config) +{ + // FIXME -- should track line and column to improve error reporting + + CConfig tmp; + while (s) { + tmp.readSection(s); + } + config = tmp; + return s; +} + +ostream& operator<<(ostream& s, const CConfig& config) +{ + // screens section + s << "section: screens" << endl; + for (CConfig::const_iterator screen = config.begin(); + screen != config.end(); ++screen) { + s << "\t" << screen->c_str() << ":" << endl; + } + s << "end" << endl; + + // links section + CString neighbor; + s << "section: links" << endl; + for (CConfig::const_iterator screen = config.begin(); + screen != config.end(); ++screen) { + s << "\t" << screen->c_str() << ":" << endl; + + neighbor = config.getNeighbor(*screen, CConfig::kLeft); + if (!neighbor.empty()) { + s << "\t\tleft=" << neighbor.c_str() << endl; + } + + neighbor = config.getNeighbor(*screen, CConfig::kRight); + if (!neighbor.empty()) { + s << "\t\tright=" << neighbor.c_str() << endl; + } + + neighbor = config.getNeighbor(*screen, CConfig::kTop); + if (!neighbor.empty()) { + s << "\t\tup=" << neighbor.c_str() << endl; + } + + neighbor = config.getNeighbor(*screen, CConfig::kBottom); + if (!neighbor.empty()) { + s << "\t\tdown=" << neighbor.c_str() << endl; + } + } + s << "end" << endl; + + return s; +} + + +// +// CConfig I/O exceptions +// + +XConfigRead::XConfigRead(const CString& error) : m_error(error) +{ + // do nothing +} + +XConfigRead::~XConfigRead() +{ + // do nothing +} + +CString XConfigRead::getWhat() const throw() +{ + return m_error; +} diff --git a/server/CConfig.h b/server/CConfig.h index 33cb1745..d1d04500 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -3,6 +3,8 @@ #include "BasicTypes.h" #include "CString.h" +#include "XBase.h" +#include #include class CConfig { @@ -53,6 +55,9 @@ public: // manipulators + // note that case is preserved in screen names but has no effect + // FIXME -- make that true + // add/remove screens void addScreen(const CString& name); void removeScreen(const CString& name); @@ -67,19 +72,49 @@ public: // accessors + // returns true iff the given name is a valid screen name. + bool isValidScreenName(const CString&) const; + // iterators over screen names const_iterator begin() const; const_iterator end() const; + // returns true iff name names a screen + bool isScreen(const CString& name); + // get the neighbor in the given direction. returns the empty string // if there is no neighbor in that direction. CString getNeighbor(const CString&, EDirection) const; + // read/write a configuration. operator>> will throw XConfigRead + // on error. + friend istream& operator>>(istream&, CConfig&); + friend ostream& operator<<(ostream&, const CConfig&); + // get the name of a direction (for debugging) static const char* dirName(EDirection); +private: + static bool readLine(istream&, CString&); + void readSection(istream&); + void readSectionScreens(istream&); + void readSectionLinks(istream&); + private: CCellMap m_map; }; +class XConfigRead : public XBase { +public: + XConfigRead(const CString&); + ~XConfigRead(); + +protected: + // XBase overrides + virtual CString getWhat() const throw(); + +private: + CString m_error; +}; + #endif diff --git a/server/server.cpp b/server/server.cpp index 8a209e74..e2b1ffdb 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -4,6 +4,14 @@ #include "CMutex.h" #include "CNetwork.h" #include "CThread.h" +#include + +// +// config file stuff +// + +static const char* s_configFileName = "synergy.conf"; + // // logging thread safety @@ -41,17 +49,19 @@ void realMain() // initialize network library CNetwork::init(); - CConfig config; - config.addScreen("primary"); - config.addScreen("secondary"); - config.addScreen("secondary2"); - config.connect("primary", CConfig::kRight, "secondary"); - config.connect("secondary", CConfig::kLeft, "primary"); - config.connect("secondary", CConfig::kRight, "secondary2"); - config.connect("secondary2", CConfig::kLeft, "secondary"); - CServer* server = NULL; try { + CConfig config; + { + log((CLOG_DEBUG "opening configuration")); + ifstream configStream(s_configFileName); + if (!configStream) { + throw XConfigRead("cannot open configuration"); + } + configStream >> config; + log((CLOG_DEBUG "configuration read successfully")); + } + server = new CServer(); server->setConfig(config); server->run(); @@ -93,6 +103,7 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) return 0; } catch (XBase& e) { + log((CLOG_CRIT "failed: %s", e.what())); CString msg = "failed: "; msg += e.what(); MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); @@ -116,6 +127,7 @@ int main(int argc, char** argv) return 0; } catch (XBase& e) { + log((CLOG_CRIT "failed: %s", e.what())); fprintf(stderr, "failed: %s\n", e.what()); return 1; } From 71c374b6cd97d8180498c0722c9410dc159b912c Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 31 May 2002 18:08:08 +0000 Subject: [PATCH 120/807] made isScreen() a const method. --- server/CConfig.cpp | 2 +- server/CConfig.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/CConfig.cpp b/server/CConfig.cpp index a09e4c7f..f1615622 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -136,7 +136,7 @@ CConfig::const_iterator CConfig::end() const return const_iterator(m_map.end()); } -bool CConfig::isScreen(const CString& name) +bool CConfig::isScreen(const CString& name) const { return (m_map.count(name) > 0); } diff --git a/server/CConfig.h b/server/CConfig.h index d1d04500..88f58a76 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -80,7 +80,7 @@ public: const_iterator end() const; // returns true iff name names a screen - bool isScreen(const CString& name); + bool isScreen(const CString& name) const; // get the neighbor in the given direction. returns the empty string // if there is no neighbor in that direction. From 1e8a5d7fa9b0922d106d934d4b8532976463ef2f Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 31 May 2002 18:09:43 +0000 Subject: [PATCH 121/807] fixed setConfig() to disconnect secondary screens that aren't in the new configuration. --- server/CServer.cpp | 67 +++++++++++++++++++++++++++++------ server/CServer.h | 7 ++-- server/CServerProtocol1_0.cpp | 4 +++ 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/server/CServer.cpp b/server/CServer.cpp index 0a0a3845..e7cf497f 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -110,13 +110,49 @@ void CServer::quit() m_primary->stop(); } -void CServer::setConfig(const CConfig& config) +bool CServer::setConfig(const CConfig& config) { - CLock lock(&m_mutex); - // FIXME -- must disconnect screens no longer listed - // (that may include warping back to server's screen) - // FIXME -- server screen must be in new map or map is rejected - m_config = config; + typedef std::vector CThreads; + CThreads threads; + { + CLock lock(&m_mutex); + + // refuse configuration if it doesn't include the primary screen + if (m_primaryInfo != NULL && + !config.isScreen(m_primaryInfo->m_name)) { + return false; + } + + // get the set of screens that are connected but are being + // dropped from the configuration. don't add the primary + // screen. + for (CScreenList::const_iterator index = m_screens.begin(); + index != m_screens.end(); ++index) { + if (!config.isScreen(index->first) && + index->second != m_primaryInfo) { + threads.push_back(index->second->m_thread); + } + } + + // cancel the old secondary screen threads + for (CThreads::iterator index = threads.begin(); + index != threads.end(); ++index) { + index->cancel(); + } + + // cut over + m_config = config; + } + + // wait for old secondary screen threads to disconnect. must + // not hold lock while we do this so those threads can finish + // any calls to this object. + for (CThreads::iterator index = threads.begin(); + index != threads.end(); ++index) { + index->wait(); + } + + return true; } CString CServer::getPrimaryScreenName() const @@ -137,14 +173,22 @@ UInt32 CServer::getActivePrimarySides() const { UInt32 sides = 0; CLock lock(&m_mutex); - if (!m_config.getNeighbor("primary", CConfig::kLeft).empty()) + if (!m_config.getNeighbor(getPrimaryScreenName(), + CConfig::kLeft).empty()) { sides |= CConfig::kLeftMask; - if (!m_config.getNeighbor("primary", CConfig::kRight).empty()) + } + if (!m_config.getNeighbor(getPrimaryScreenName(), + CConfig::kRight).empty()) { sides |= CConfig::kRightMask; - if (!m_config.getNeighbor("primary", CConfig::kTop).empty()) + } + if (!m_config.getNeighbor(getPrimaryScreenName(), + CConfig::kTop).empty()) { sides |= CConfig::kTopMask; - if (!m_config.getNeighbor("primary", CConfig::kBottom).empty()) + } + if (!m_config.getNeighbor(getPrimaryScreenName(), + CConfig::kBottom).empty()) { sides |= CConfig::kBottomMask; + } return sides; } @@ -1234,6 +1278,8 @@ CServer::CScreenInfo* CServer::addConnection( throw XDuplicateClient(name); } + // FIXME -- throw if the name is not in our map + // save screen info CScreenInfo* newScreen = new CScreenInfo(name, protocol); m_screens.insert(std::make_pair(name, newScreen)); @@ -1314,6 +1360,7 @@ CServer::CConnectionNote::~CConnectionNote() CServer::CScreenInfo::CScreenInfo(const CString& name, IServerProtocol* protocol) : + m_thread(CThread::getCurrentThread()), m_name(name), m_protocol(protocol), m_width(0), m_height(0), diff --git a/server/CServer.h b/server/CServer.h index 4a47d821..b0844704 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -8,6 +8,7 @@ #include "CClipboard.h" #include "CMutex.h" #include "CString.h" +#include "CThread.h" #include "XBase.h" #include #include @@ -32,8 +33,9 @@ public: // tell server to exit gracefully void quit(); - // update screen map - void setConfig(const CConfig&); + // update screen map. returns true iff the new configuration was + // accepted. + bool setConfig(const CConfig&); // handle events on server's screen. onMouseMovePrimary() returns // true iff the mouse enters a jump zone and jumps. @@ -105,6 +107,7 @@ private: ~CScreenInfo(); public: + CThread m_thread; CString m_name; IServerProtocol* m_protocol; SInt32 m_width, m_height; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 1d4240f0..8c58fd83 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -5,6 +5,7 @@ #include "ProtocolTypes.h" #include "IInputStream.h" #include "CLog.h" +#include "CThread.h" #include // @@ -27,9 +28,12 @@ void CServerProtocol1_0::run() { // handle messages until the client hangs up for (;;) { + CThread::testCancel(); + // wait for a message UInt8 code[4]; UInt32 n = getInputStream()->read(code, 4); + CThread::testCancel(); // verify we got an entire code if (n == 0) { From 793c968f003ff1b11b8c6d35e83dc2357e19348b Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 31 May 2002 18:18:29 +0000 Subject: [PATCH 122/807] server now rejects clients that are not in the configuration. added a protocol message to indicate this. --- client/CClient.cpp | 9 +++++++++ client/CClient.h | 1 + server/CServer.cpp | 10 +++++++++- synergy/ProtocolTypes.h | 5 +++++ synergy/XSynergy.cpp | 20 ++++++++++++++++++++ synergy/XSynergy.h | 19 +++++++++++++++++++ 6 files changed, 63 insertions(+), 1 deletion(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 3f8ec103..4447d295 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -294,6 +294,10 @@ void CClient::runSession(void*) onErrorBusy(); break; } + else if (memcmp(code, kMsgEUnknown, 4) == 0) { + onErrorUnknown(); + break; + } else if (memcmp(code, kMsgEBad, 4) == 0) { onErrorBad(); break; @@ -606,6 +610,11 @@ void CClient::onErrorBusy() log((CLOG_ERR "server already has a connected client with name \"%s\"", m_name.c_str())); } +void CClient::onErrorUnknown() +{ + log((CLOG_ERR "server refused client with name \"%s\"", m_name.c_str())); +} + void CClient::onErrorBad() { log((CLOG_ERR "server disconnected due to a protocol error")); diff --git a/client/CClient.h b/client/CClient.h index a6c0cea1..9920a45c 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -53,6 +53,7 @@ private: void onMouseWheel(); void onErrorIncompatible(); void onErrorBusy(); + void onErrorUnknown(); void onErrorBad(); private: diff --git a/server/CServer.cpp b/server/CServer.cpp index e7cf497f..d7c71da6 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -992,6 +992,11 @@ void CServer::handshakeClient(void* vsocket) log((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); CProtocolUtil::writef(output.get(), kMsgEBusy); } + catch (XUnknownClient& e) { + // client has unknown name + log((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); + CProtocolUtil::writef(output.get(), kMsgEUnknown); + } catch (XIncompatibleClient& e) { // client is incompatible // FIXME -- could print network address if socket had suitable method @@ -1278,7 +1283,10 @@ CServer::CScreenInfo* CServer::addConnection( throw XDuplicateClient(name); } - // FIXME -- throw if the name is not in our map + // name must be in our configuration + if (!m_config.isScreen(name)) { + throw XUnknownClient(name); + } // save screen info CScreenInfo* newScreen = new CScreenInfo(name, protocol); diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index c8194961..e06093fa 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -131,6 +131,11 @@ static const char kMsgEIncompatible[] = "EICV%2i%2i"; // name provided when connecting is already in use: primary -> secondary static const char kMsgEBusy[] = "EBSY"; +// unknown client: primary -> secondary +// name provided when connecting is not in primary's screen +// configuration map. +static const char kMsgEUnknown[] = "EUNK"; + // protocol violation: primary -> secondary // primary should disconnect after sending this message. static const char kMsgEBad[] = "EBAD"; diff --git a/synergy/XSynergy.cpp b/synergy/XSynergy.cpp index d07c727c..94159f9a 100644 --- a/synergy/XSynergy.cpp +++ b/synergy/XSynergy.cpp @@ -55,3 +55,23 @@ CString XDuplicateClient::getWhat() const throw() { return "XDuplicateClient"; } + + +// +// XUnknownClient +// + +XUnknownClient::XUnknownClient(const CString& name) : m_name(name) +{ + // do nothing +} + +const CString& XUnknownClient::getName() const throw() +{ + return m_name; +} + +CString XUnknownClient::getWhat() const throw() +{ + return "XUnknownClient"; +} diff --git a/synergy/XSynergy.h b/synergy/XSynergy.h index f3dde1f6..dda1d106 100644 --- a/synergy/XSynergy.h +++ b/synergy/XSynergy.h @@ -50,4 +50,23 @@ private: CString m_name; }; +// client has unknown name (i.e. name is not in server's screen map) +class XUnknownClient : public XSynergy { +public: + XUnknownClient(const CString& name); + + // manipulators + + // accessors + + virtual const CString& + getName() const throw(); + +protected: + virtual CString getWhat() const throw(); + +private: + CString m_name; +}; + #endif From 3a80df28dd5352d3125a5531693ea231c9e85d4d Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 31 May 2002 18:35:53 +0000 Subject: [PATCH 123/807] changed iterator to use iterator_traits directly instead of std::iterator to support the old STL on grace. --- server/CConfig.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/server/CConfig.h b/server/CConfig.h index 88f58a76..3e9fcd16 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -7,6 +7,18 @@ #include #include +class CConfig; + +namespace std { +struct iterator_traits { + typedef CString value_type; + typedef ptrdiff_t difference_type; + typedef bidirectional_iterator_tag iterator_category; + typedef CString* pointer; + typedef CString& reference; +}; +}; + class CConfig { public: enum EDirection { kLeft, kRight, kTop, kBottom, @@ -22,9 +34,7 @@ private: public: typedef CCellMap::const_iterator internal_const_iterator; - class const_iterator : public std::iterator< - std::bidirectional_iterator_tag, - CString, ptrdiff_t, CString*, CString&> { + class const_iterator : std::iterator_traits { public: explicit const_iterator() : m_i() { } explicit const_iterator(const internal_const_iterator& i) : m_i(i) { } From 1ac62a9533da7fb318392b3dd6d98c253c226b24 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 Jun 2002 10:52:02 +0000 Subject: [PATCH 124/807] added mutex to all public methods that didn't already have it. fixed two blown assertions. first, if user tried to switch to a client that had connected but hadn't yet sent the first info message it would assert on the zero size screen. second, if the primary screen was handling a mouse motion on behalf of a secondary screen when that secondary screen disconnected then an assert would blow because the primary screen would call onMouseMoveSecondary() but m_protocol on the active screen is NULL because disconnecting the active secondary screen caused the mouse to jump to the primary screen. --- server/CServer.cpp | 119 ++++++++++++++++++++++++++-------- server/CServer.h | 21 ++++++ server/CServerProtocol1_0.cpp | 4 ++ 3 files changed, 116 insertions(+), 28 deletions(-) diff --git a/server/CServer.cpp b/server/CServer.cpp index d7c71da6..c2c172a4 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -125,15 +125,24 @@ bool CServer::setConfig(const CConfig& config) // get the set of screens that are connected but are being // dropped from the configuration. don't add the primary - // screen. + // screen. also tell the secondary screen to disconnect. for (CScreenList::const_iterator index = m_screens.begin(); index != m_screens.end(); ++index) { if (!config.isScreen(index->first) && index->second != m_primaryInfo) { + assert(index->second->m_protocol != NULL); + index->second->m_protocol->sendClose(); threads.push_back(index->second->m_thread); } } + // wait a moment to allow each secondary screen to close + // its connection before we close it (to avoid having our + // socket enter TIME_WAIT). + if (threads.size() > 0) { + CThread::sleep(1.0); + } + // cancel the old secondary screen threads for (CThreads::iterator index = threads.begin(); index != threads.end(); ++index) { @@ -196,28 +205,39 @@ void CServer::setInfo( SInt32 w, SInt32 h, SInt32 zoneSize, SInt32 x, SInt32 y) { - setInfo(m_primaryInfo->m_name, w, h, zoneSize, x, y); + CLock lock(&m_mutex); + assert(m_primaryInfo != NULL); + setInfoNoLock(m_primaryInfo->m_name, w, h, zoneSize, x, y); } void CServer::setInfo(const CString& client, SInt32 w, SInt32 h, SInt32 zoneSize, SInt32 x, SInt32 y) { - assert(!client.empty()); + CLock lock(&m_mutex); + setInfoNoLock(client, w, h, zoneSize, x, y); +} + +void CServer::setInfoNoLock(const CString& screen, + SInt32 w, SInt32 h, SInt32 zoneSize, + SInt32 x, SInt32 y) +{ + assert(!screen.empty()); assert(w > 0); assert(h > 0); assert(zoneSize >= 0); - CLock lock(&m_mutex); - - // client must be connected - CScreenList::iterator index = m_screens.find(client); + // screen must be connected + CScreenList::iterator index = m_screens.find(screen); if (index == m_screens.end()) { throw XBadClient(); } - // update client info + // screen is now ready (i.e. available to user) CScreenInfo* info = index->second; + info->m_ready = true; + + // update screen info if (info == m_active) { // update the remote mouse coordinates m_x = x; @@ -226,9 +246,9 @@ void CServer::setInfo(const CString& client, info->m_width = w; info->m_height = h; info->m_zoneSize = zoneSize; - log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d pos=%d,%d", client.c_str(), w, h, zoneSize, x, y)); + log((CLOG_NOTE "screen \"%s\" size=%dx%d zone=%d pos=%d,%d", screen.c_str(), w, h, zoneSize, x, y)); - // send acknowledgement (if client isn't the primary) + // send acknowledgement (if screen isn't the primary) if (info->m_protocol != NULL) { info->m_protocol->sendInfoAcknowledgment(); } @@ -236,17 +256,19 @@ void CServer::setInfo(const CString& client, // handle resolution change to primary screen else { if (info == m_active) { - onMouseMovePrimary(x, y); + onMouseMovePrimaryNoLock(x, y); } else { - onMouseMoveSecondary(0, 0); + onMouseMoveSecondaryNoLock(0, 0); } } } void CServer::grabClipboard(ClipboardID id) { - grabClipboard(id, 0, m_primaryInfo->m_name); + CLock lock(&m_mutex); + assert(m_primaryInfo != NULL); + grabClipboardNoLock(id, 0, m_primaryInfo->m_name); } void CServer::grabClipboard( @@ -254,25 +276,33 @@ void CServer::grabClipboard( const CString& client) { CLock lock(&m_mutex); + grabClipboardNoLock(id, seqNum, client); +} + +void CServer::grabClipboardNoLock( + ClipboardID id, UInt32 seqNum, + const CString& screen) +{ + // note -- must be locked on entry CClipboardInfo& clipboard = m_clipboards[id]; // screen must be connected - CScreenList::iterator index = m_screens.find(client); + CScreenList::iterator index = m_screens.find(screen); if (index == m_screens.end()) { throw XBadClient(); } // ignore grab if sequence number is old. always allow primary // screen to grab. - if (client != m_primaryInfo->m_name && + if (screen != m_primaryInfo->m_name && seqNum < clipboard.m_clipboardSeqNum) { - log((CLOG_INFO "ignored client \"%s\" grab of clipboard %d", client.c_str(), id)); + log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", screen.c_str(), id)); return; } // mark screen as owning clipboard - log((CLOG_NOTE "client \"%s\" grabbed clipboard %d from \"%s\"", client.c_str(), id, clipboard.m_clipboardOwner.c_str())); - clipboard.m_clipboardOwner = client; + log((CLOG_NOTE "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str())); + clipboard.m_clipboardOwner = screen; clipboard.m_clipboardSeqNum = seqNum; // no screens have the new clipboard except the sender @@ -281,7 +311,7 @@ void CServer::grabClipboard( // tell all other screens to take ownership of clipboard for (index = m_screens.begin(); index != m_screens.end(); ++index) { - if (index->first != client) { + if (index->first != screen) { CScreenInfo* info = index->second; if (info->m_protocol == NULL) { m_primary->grabClipboard(id); @@ -317,12 +347,12 @@ void CServer::setClipboard(ClipboardID id, // ignore update if sequence number is old if (seqNum < clipboard.m_clipboardSeqNum) { - log((CLOG_INFO "ignored client \"%s\" update of clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + log((CLOG_INFO "ignored screen \"%s\" update of clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); return; } // unmarshall into our clipboard buffer - log((CLOG_NOTE "client \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + log((CLOG_NOTE "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); clipboard.m_clipboardReady = true; clipboard.m_clipboardData = data; clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); @@ -349,6 +379,7 @@ bool CServer::onCommandKey(KeyID /*id*/, void CServer::onKeyDown(KeyID id, KeyModifierMask mask) { log((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x", id, mask)); + CLock lock(&m_mutex); assert(m_active != NULL); // handle command keys @@ -365,6 +396,7 @@ void CServer::onKeyDown(KeyID id, KeyModifierMask mask) void CServer::onKeyUp(KeyID id, KeyModifierMask mask) { log((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x", id, mask)); + CLock lock(&m_mutex); assert(m_active != NULL); // handle command keys @@ -382,6 +414,7 @@ void CServer::onKeyRepeat( KeyID id, KeyModifierMask mask, SInt32 count) { log((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); + CLock lock(&m_mutex); assert(m_active != NULL); // handle command keys @@ -399,6 +432,7 @@ void CServer::onKeyRepeat( void CServer::onMouseDown(ButtonID id) { log((CLOG_DEBUG1 "onMouseDown id=%d", id)); + CLock lock(&m_mutex); assert(m_active != NULL); // relay @@ -410,6 +444,7 @@ void CServer::onMouseDown(ButtonID id) void CServer::onMouseUp(ButtonID id) { log((CLOG_DEBUG1 "onMouseUp id=%d", id)); + CLock lock(&m_mutex); assert(m_active != NULL); // relay @@ -421,13 +456,18 @@ void CServer::onMouseUp(ButtonID id) bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) { log((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); + CLock lock(&m_mutex); + return onMouseMovePrimaryNoLock(x, y); +} +bool CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) +{ // mouse move on primary (server's) screen assert(m_active != NULL); assert(m_active->m_protocol == NULL); // ignore if mouse is locked to screen - if (isLockedToScreen()) { + if (isLockedToScreenNoLock()) { return false; } @@ -477,10 +517,25 @@ bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) { log((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); + CLock lock(&m_mutex); + onMouseMoveSecondaryNoLock(dx, dy); +} +void CServer::onMouseMoveSecondaryNoLock( + SInt32 dx, SInt32 dy) +{ // mouse move on secondary (client's) screen assert(m_active != NULL); - assert(m_active->m_protocol != NULL); + if (m_active->m_protocol == NULL) { + // we're actually on the primary screen. this can happen + // when the primary screen begins processing a mouse move + // for a secondary screen, then the active (secondary) + // screen disconnects causing us to jump to the primary + // screen, and finally the primary screen finishes + // processing the mouse move, still thinking it's for + // a secondary screen. we just ignore the motion. + return; + } // save old position const SInt32 xOld = m_x; @@ -493,7 +548,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) // switch screens if the mouse is outside the screen and not // locked to the screen CScreenInfo* newScreen = NULL; - if (!isLockedToScreen()) { + if (!isLockedToScreenNoLock()) { // find direction of neighbor CConfig::EDirection dir; if (m_x < 0) @@ -564,6 +619,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) void CServer::onMouseWheel(SInt32 delta) { log((CLOG_DEBUG1 "onMouseWheel %+d", delta)); + CLock lock(&m_mutex); assert(m_active != NULL); // relay @@ -573,6 +629,12 @@ void CServer::onMouseWheel(SInt32 delta) } bool CServer::isLockedToScreen() const +{ + CLock lock(&m_mutex); + return isLockedToScreenNoLock(); +} + +bool CServer::isLockedToScreenNoLock() const { // locked if primary says we're locked if (m_primary->isLockedToScreen()) { @@ -673,11 +735,11 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, return NULL; } - // look up neighbor cell. if the screen is connected then - // we can stop. otherwise we skip over an unconnected - // screen. + // look up neighbor cell. if the screen is connected and + // ready then we can stop. otherwise we skip over an + // unconnected screen. CScreenList::const_iterator index = m_screens.find(dstName); - if (index != m_screens.end()) { + if (index != m_screens.end() && index->second->m_ready) { log((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); return index->second; } @@ -1371,6 +1433,7 @@ CServer::CScreenInfo::CScreenInfo(const CString& name, m_thread(CThread::getCurrentThread()), m_name(name), m_protocol(protocol), + m_ready(false), m_width(0), m_height(0), m_zoneSize(0) { diff --git a/server/CServer.h b/server/CServer.h index b0844704..f11ba980 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -66,6 +66,7 @@ public: // accessors + // returns true if the mouse should be locked to the current screen bool isLockedToScreen() const; // get the current screen map @@ -107,14 +108,34 @@ private: ~CScreenInfo(); public: + // the thread handling this screen's connection. used when + // forcing a screen to disconnect. CThread m_thread; CString m_name; IServerProtocol* m_protocol; + bool m_ready; SInt32 m_width, m_height; SInt32 m_zoneSize; bool m_gotClipboard[kClipboardEnd]; }; + // handle mouse motion + bool onMouseMovePrimaryNoLock(SInt32 x, SInt32 y); + void onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy); + + // update screen info + void setInfoNoLock(const CString& screenName, + SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse); + + // grab the clipboard + void grabClipboardNoLock(ClipboardID, + UInt32 seqNum, const CString& clientName); + + // returns true iff mouse should be locked to the current screen + bool isLockedToScreenNoLock() const; + // change the active screen void switchScreen(CScreenInfo*, SInt32 x, SInt32 y); diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 8c58fd83..5fe9ffd4 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -4,6 +4,7 @@ #include "CProtocolUtil.h" #include "ProtocolTypes.h" #include "IInputStream.h" +#include "IOutputStream.h" #include "CLog.h" #include "CThread.h" #include @@ -92,6 +93,9 @@ void CServerProtocol1_0::sendClose() { log((CLOG_DEBUG1 "send close to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClose); + + // force the close to be sent before we return + getOutputStream()->flush(); } void CServerProtocol1_0::sendEnter( From d2135af0d97170b91e6cc774eeaa778af3607855 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 Jun 2002 19:26:11 +0000 Subject: [PATCH 125/807] fixes, mainly for windows. first, had to add a notification from CServer to the primary screen when the configuration changes so it can make necessary adjustments (the win32 primary screen must tell the hook dll about the new jump zones). changed includes of some std c++ library files to go through our own include files. these wrap the include with stuff to keep vc++ quiet when compiling at warning level 4, which is what it does now. it also works around missing and on g++2.96. added missing std:: where necessary. g++ doesn't really support namespaces so it lets references without the namespace slip through. added workaround or fix. not sure if istringstream::str(string) should reset eofbit. it does on g++ but does not on vc++. added clear() after str() so it works either way. added low-level keyboard hook to win32. if available (it's only available on NT SP3 and up) it allows us to catch and handle alt+tab, alt+esc, ctrl+esc, and windows key hot keys. i think that leaves only ctrl+alt+del and accessibility functions uncaught on those systems. --- base/CLog.cpp | 11 +- base/CLog.h | 4 +- base/CString.h | 11 ++ base/XBase.h | 2 + base/base.dsp | 44 +++++++- base/common.h | 11 +- base/stdfstream.h | 4 + base/stdistream.h | 28 +++++ base/stdlist.h | 3 + base/stdmap.h | 3 + base/stdostream.h | 7 ++ base/stdpost.h | 3 + base/stdpre.h | 11 ++ base/stdset.h | 3 + base/stdsstream.h | 4 + base/stdvector.h | 3 + client/CMSWindowsSecondaryScreen.h | 4 +- client/CXWindowsSecondaryScreen.h | 4 +- client/client.cpp | 1 + client/client.dsp | 4 +- http/CHTTPProtocol.cpp | 6 +- http/CHTTPProtocol.h | 4 +- http/XHTTP.cpp | 13 ++- http/http.dsp | 110 ++++++++++++++++++++ io/CStreamBuffer.h | 4 +- io/io.dsp | 4 +- mt/mt.dsp | 4 +- net/net.dsp | 4 +- server/CConfig.cpp | 43 ++++---- server/CConfig.h | 16 +-- server/CHTTPServer.cpp | 74 ++----------- server/CHTTPServer.h | 2 +- server/CMSWindowsPrimaryScreen.cpp | 53 +++++++++- server/CMSWindowsPrimaryScreen.h | 2 + server/CServer.cpp | 5 + server/CServer.h | 4 +- server/CSynergyHook.cpp | 162 ++++++++++++++++++++++++++++- server/CXWindowsPrimaryScreen.cpp | 5 + server/CXWindowsPrimaryScreen.h | 1 + server/server.cpp | 5 +- server/server.dsp | 20 ++-- server/synrgyhk.dsp | 5 +- synergy.dsw | 55 +++------- synergy/CXWindowsClipboard.h | 4 +- synergy/IPrimaryScreen.h | 5 + synergy/synergy.dsp | 4 +- 46 files changed, 576 insertions(+), 203 deletions(-) create mode 100644 base/stdfstream.h create mode 100644 base/stdistream.h create mode 100644 base/stdlist.h create mode 100644 base/stdmap.h create mode 100644 base/stdostream.h create mode 100644 base/stdpost.h create mode 100644 base/stdpre.h create mode 100644 base/stdset.h create mode 100644 base/stdsstream.h create mode 100644 base/stdvector.h create mode 100755 http/http.dsp diff --git a/base/CLog.cpp b/base/CLog.cpp index 7ee437fa..145a2a2c 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -21,6 +21,11 @@ static const char* g_priority[] = { }; static const int g_numPriority = (int)(sizeof(g_priority) / sizeof(g_priority[0])); +#if defined(NDEBUG) +static const int g_defaultMaxPriority = 4; +#else +static const int g_defaultMaxPriority = 5; +#endif static const int g_maxPriorityLength = 7; // length of longest string static const int g_prioritySuffixLength = 2; static const int g_priorityPad = g_maxPriorityLength + @@ -145,11 +150,7 @@ int CLog::getMaxPriority() CHoldLock lock(s_lock); if (s_maxPriority == -1) { -#if defined(NDEBUG) - s_maxPriority = 4; -#else - s_maxPriority = 5; -#endif + s_maxPriority = g_defaultMaxPriority; const char* priEnv = getenv("SYN_LOG_PRI"); if (priEnv != NULL) { for (int i = 0; i < g_numPriority; ++i) { diff --git a/base/CLog.h b/base/CLog.h index 0a65a787..63d97174 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -37,11 +37,11 @@ public: private: class CHoldLock { public: - CHoldLock(CLog::Lock lock) : m_lock(lock) { m_lock(true); } + CHoldLock(Lock lock) : m_lock(lock) { m_lock(true); } ~CHoldLock() { m_lock(false); } private: - CLog::Lock m_lock; + Lock m_lock; }; static void dummyLock(bool); diff --git a/base/CString.h b/base/CString.h index 547152e3..dc0a15f4 100644 --- a/base/CString.h +++ b/base/CString.h @@ -2,7 +2,14 @@ #define CSTRING_H #include "common.h" +#include "stdpre.h" #include +#include "stdpost.h" + +#if defined(_MSC_VER) +#pragma warning(push, 4) +#pragma warning(disable: 4097) // typedef-name used as synonym +#endif #ifndef CSTRING_DEF_CTOR #define CSTRING_ALLOC1 @@ -37,5 +44,9 @@ public: _Myt(_f, _l CSTRING_ALLOC2) { } }; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + #endif diff --git a/base/XBase.h b/base/XBase.h index 8ab63b5f..c654de6e 100644 --- a/base/XBase.h +++ b/base/XBase.h @@ -2,7 +2,9 @@ #define XBASE_H #include "CString.h" +#include "stdpre.h" #include +#include "stdpost.h" class XBase : public std::exception { public: diff --git a/base/base.dsp b/base/base.dsp index c73c0e44..fa7569aa 100644 --- a/base/base.dsp +++ b/base/base.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -139,6 +139,46 @@ SOURCE=.\IJob.h # End Source File # Begin Source File +SOURCE=.\stdfstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdistream.h +# End Source File +# Begin Source File + +SOURCE=.\stdlist.h +# End Source File +# Begin Source File + +SOURCE=.\stdmap.h +# End Source File +# Begin Source File + +SOURCE=.\stdostream.h +# End Source File +# Begin Source File + +SOURCE=.\stdpost.h +# End Source File +# Begin Source File + +SOURCE=.\stdpre.h +# End Source File +# Begin Source File + +SOURCE=.\stdset.h +# End Source File +# Begin Source File + +SOURCE=.\stdsstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdvector.h +# End Source File +# Begin Source File + SOURCE=.\TMethodJob.h # End Source File # Begin Source File diff --git a/base/common.h b/base/common.h index e4ecb865..08f02331 100644 --- a/base/common.h +++ b/base/common.h @@ -25,17 +25,8 @@ // turn off bonehead warnings #pragma warning(disable: 4786) // identifier truncated in debug info +#pragma warning(disable: 4514) // unreferenced inline function removed -// -// ignore warnings inside microsoft's standard C++ library -// -// bonehead bugs/warnings -#pragma warning(disable: 4097) // typedef-name used as synonym -#pragma warning(disable: 4511) // copy constructor can't be generated -#pragma warning(disable: 4512) // assignment operator can't be generated - -// we'd really rather have these enabled to check our code -#pragma warning(disable: 4100) // unreferenced formal parameter #endif // (_MSC_VER >= 1200) #else diff --git a/base/stdfstream.h b/base/stdfstream.h new file mode 100644 index 00000000..1b9db065 --- /dev/null +++ b/base/stdfstream.h @@ -0,0 +1,4 @@ +#include "stdpre.h" +#include +#include "stdpost.h" +#include "stdistream.h" diff --git a/base/stdistream.h b/base/stdistream.h new file mode 100644 index 00000000..1cbbdcbd --- /dev/null +++ b/base/stdistream.h @@ -0,0 +1,28 @@ +#include "stdpre.h" +#if !defined(CONFIG_PLATFORM_LINUX) +#include +#else +#include +#endif +#include "stdpost.h" + +#if defined(CONFIG_PLATFORM_WIN32) && defined(_MSC_VER) +inline +std::istream& operator>>(std::istream& s, SInt8& i) +{ return s >> (signed char&)i; } +inline +std::istream& operator>>(std::istream& s, SInt16& i) +{ return s >> (short&)i; } +inline +std::istream& operator>>(std::istream& s, SInt32& i) +{ return s >> (int&)i; } +inline +std::istream& operator>>(std::istream& s, UInt8& i) +{ return s >> (unsigned char&)i; } +inline +std::istream& operator>>(std::istream& s, UInt16& i) +{ return s >> (unsigned short&)i; } +inline +std::istream& operator>>(std::istream& s, UInt32& i) +{ return s >> (unsigned int&)i; } +#endif diff --git a/base/stdlist.h b/base/stdlist.h new file mode 100644 index 00000000..98503b7b --- /dev/null +++ b/base/stdlist.h @@ -0,0 +1,3 @@ +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/base/stdmap.h b/base/stdmap.h new file mode 100644 index 00000000..a77f22b6 --- /dev/null +++ b/base/stdmap.h @@ -0,0 +1,3 @@ +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/base/stdostream.h b/base/stdostream.h new file mode 100644 index 00000000..11a1b361 --- /dev/null +++ b/base/stdostream.h @@ -0,0 +1,7 @@ +#include "stdpre.h" +#if !defined(CONFIG_PLATFORM_LINUX) +#include +#else +#include +#endif +#include "stdpost.h" diff --git a/base/stdpost.h b/base/stdpost.h new file mode 100644 index 00000000..658cd5d5 --- /dev/null +++ b/base/stdpost.h @@ -0,0 +1,3 @@ +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/base/stdpre.h b/base/stdpre.h new file mode 100644 index 00000000..4461b475 --- /dev/null +++ b/base/stdpre.h @@ -0,0 +1,11 @@ +#if defined(_MSC_VER) +#pragma warning(disable: 4786) // identifier truncated +#pragma warning(disable: 4514) // unreferenced inline +#pragma warning(disable: 4710) // not inlined +#pragma warning(disable: 4663) // C++ change, template specialization +#pragma warning(push, 3) +#pragma warning(disable: 4018) // signed/unsigned mismatch +#pragma warning(disable: 4284) +#pragma warning(disable: 4146) // unary minus on unsigned value +#pragma warning(disable: 4127) // conditional expression is constant +#endif diff --git a/base/stdset.h b/base/stdset.h new file mode 100644 index 00000000..fc818573 --- /dev/null +++ b/base/stdset.h @@ -0,0 +1,3 @@ +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/base/stdsstream.h b/base/stdsstream.h new file mode 100644 index 00000000..b486dc62 --- /dev/null +++ b/base/stdsstream.h @@ -0,0 +1,4 @@ +#include "stdpre.h" +#include +#include "stdpost.h" +#include "stdistream.h" diff --git a/base/stdvector.h b/base/stdvector.h new file mode 100644 index 00000000..8c7cd443 --- /dev/null +++ b/base/stdvector.h @@ -0,0 +1,3 @@ +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 3ca03338..258853b5 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -3,8 +3,8 @@ #include "CMSWindowsScreen.h" #include "ISecondaryScreen.h" -#include -#include +#include "stdmap.h" +#include "stdvector.h" class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen { public: diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index a89e344b..2617096c 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -3,8 +3,8 @@ #include "CXWindowsScreen.h" #include "ISecondaryScreen.h" -#include -#include +#include "stdmap.h" +#include "stdvector.h" class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen { public: diff --git a/client/client.cpp b/client/client.cpp index 479dbe06..1364d83d 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -5,6 +5,7 @@ #include "CNetwork.h" #include "CNetworkAddress.h" #include "CThread.h" +#include // // logging thread safety diff --git a/client/client.dsp b/client/client.dsp index b7ffa272..0d516a91 100644 --- a/client/client.dsp +++ b/client/client.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -68,7 +68,7 @@ LINK32=link.exe # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/http/CHTTPProtocol.cpp b/http/CHTTPProtocol.cpp index 46d5bc7a..4c52ca7d 100644 --- a/http/CHTTPProtocol.cpp +++ b/http/CHTTPProtocol.cpp @@ -3,11 +3,12 @@ #include "XHTTP.h" #include "IInputStream.h" #include "IOutputStream.h" +#include "stdsstream.h" +#include #include #include #include #include -#include // // CHTTPUtil::CaselessCmp @@ -88,6 +89,7 @@ CHTTPRequest* CHTTPProtocol::readRequest(IInputStream* stream) // parse version char dot; s.str(version); + s.clear(); s.ignore(5); s >> request->m_majorVersion; s.get(dot); @@ -229,7 +231,7 @@ void CHTTPProtocol::reply( } // write reply header - ostringstream s; + std::ostringstream s; s << "HTTP/" << reply.m_majorVersion << "." << reply.m_minorVersion << " " << reply.m_status << " " << diff --git a/http/CHTTPProtocol.h b/http/CHTTPProtocol.h index 216c8e45..357c3175 100644 --- a/http/CHTTPProtocol.h +++ b/http/CHTTPProtocol.h @@ -3,8 +3,8 @@ #include "BasicTypes.h" #include "CString.h" -#include -#include +#include "stdmap.h" +#include "stdvector.h" class IInputStream; class IOutputStream; diff --git a/http/XHTTP.cpp b/http/XHTTP.cpp index dfae8a97..f069f1da 100644 --- a/http/XHTTP.cpp +++ b/http/XHTTP.cpp @@ -1,6 +1,6 @@ #include "XHTTP.h" #include "CHTTPProtocol.h" -#include +#include "stdsstream.h" // // XHTTP @@ -45,16 +45,15 @@ void XHTTP::addHeaders(CHTTPReply&) const CString XHTTP::getWhat() const throw() { try { - char code[20]; - sprintf(code, "%d ", m_status); - CString msg(code); + std::ostringstream s; + s << m_status << " "; if (!m_reason.empty()) { - msg += m_reason; + s << m_reason.c_str(); } else { - msg += getReason(m_status); + s << getReason(m_status); } - return msg; + return s.str(); } catch (...) { return CString(); diff --git a/http/http.dsp b/http/http.dsp new file mode 100755 index 00000000..75173bef --- /dev/null +++ b/http/http.dsp @@ -0,0 +1,110 @@ +# Microsoft Developer Studio Project File - Name="http" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=http - Win32 Debug +!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 "http.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 "http.mak" CFG="http - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "http - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "http - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "millpond" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "http - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "http - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "http - Win32 Release" +# Name "http - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CHTTPProtocol.cpp +# End Source File +# Begin Source File + +SOURCE=.\XHTTP.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CHTTPProtocol.h +# End Source File +# Begin Source File + +SOURCE=.\XHTTP.h +# End Source File +# End Group +# End Target +# End Project diff --git a/io/CStreamBuffer.h b/io/CStreamBuffer.h index 2338b8da..97b32296 100644 --- a/io/CStreamBuffer.h +++ b/io/CStreamBuffer.h @@ -2,8 +2,8 @@ #define CSTREAMBUFFER_H #include "BasicTypes.h" -#include -#include +#include "stdlist.h" +#include "stdvector.h" class CStreamBuffer { public: diff --git a/io/io.dsp b/io/io.dsp index b06989a9..58df7ddc 100644 --- a/io/io.dsp +++ b/io/io.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /O2 /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/mt/mt.dsp b/mt/mt.dsp index 8067b7d7..0c5dab8d 100644 --- a/mt/mt.dsp +++ b/mt/mt.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/net/net.dsp b/net/net.dsp index 54d52353..1b5ff922 100644 --- a/net/net.dsp +++ b/net/net.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /O2 /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/server/CConfig.cpp b/server/CConfig.cpp index f1615622..c458c2b1 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -1,12 +1,7 @@ #include "CConfig.h" +#include "stdistream.h" +#include "stdostream.h" #include -// FIXME -- fix this with automake and config.h -#if !defined(CONFIG_PLATFORM_LINUX) -#include -#include -#else -#include -#endif // // CConfig @@ -160,10 +155,10 @@ const char* CConfig::dirName(EDirection dir) return s_name[dir - kFirstDirection]; } -bool CConfig::readLine(istream& s, CString& line) +bool CConfig::readLine(std::istream& s, CString& line) { s >> std::ws; - while (getline(s, line)) { + while (std::getline(s, line)) { // strip comments and then trailing whitespace CString::size_type i = line.rfind('#'); if (i != CString::npos) { @@ -183,7 +178,7 @@ bool CConfig::readLine(istream& s, CString& line) return false; } -void CConfig::readSection(istream& s) +void CConfig::readSection(std::istream& s) { static const char s_section[] = "section:"; static const char s_screens[] = "screens"; @@ -223,7 +218,7 @@ void CConfig::readSection(istream& s) } } -void CConfig::readSectionScreens(istream& s) +void CConfig::readSectionScreens(std::istream& s) { CString line; CString name; @@ -256,7 +251,7 @@ void CConfig::readSectionScreens(istream& s) throw XConfigRead("unexpected end of screens section"); } -void CConfig::readSectionLinks(istream& s) +void CConfig::readSectionLinks(std::istream& s) { CString line; CString screen; @@ -338,7 +333,7 @@ void CConfig::readSectionLinks(istream& s) // CConfig I/O // -istream& operator>>(istream& s, CConfig& config) +std::istream& operator>>(std::istream& s, CConfig& config) { // FIXME -- should track line and column to improve error reporting @@ -350,44 +345,44 @@ istream& operator>>(istream& s, CConfig& config) return s; } -ostream& operator<<(ostream& s, const CConfig& config) +std::ostream& operator<<(std::ostream& s, const CConfig& config) { // screens section - s << "section: screens" << endl; + s << "section: screens" << std::endl; for (CConfig::const_iterator screen = config.begin(); screen != config.end(); ++screen) { - s << "\t" << screen->c_str() << ":" << endl; + s << "\t" << screen->c_str() << ":" << std::endl; } - s << "end" << endl; + s << "end" << std::endl; // links section CString neighbor; - s << "section: links" << endl; + s << "section: links" << std::endl; for (CConfig::const_iterator screen = config.begin(); screen != config.end(); ++screen) { - s << "\t" << screen->c_str() << ":" << endl; + s << "\t" << screen->c_str() << ":" << std::endl; neighbor = config.getNeighbor(*screen, CConfig::kLeft); if (!neighbor.empty()) { - s << "\t\tleft=" << neighbor.c_str() << endl; + s << "\t\tleft=" << neighbor.c_str() << std::endl; } neighbor = config.getNeighbor(*screen, CConfig::kRight); if (!neighbor.empty()) { - s << "\t\tright=" << neighbor.c_str() << endl; + s << "\t\tright=" << neighbor.c_str() << std::endl; } neighbor = config.getNeighbor(*screen, CConfig::kTop); if (!neighbor.empty()) { - s << "\t\tup=" << neighbor.c_str() << endl; + s << "\t\tup=" << neighbor.c_str() << std::endl; } neighbor = config.getNeighbor(*screen, CConfig::kBottom); if (!neighbor.empty()) { - s << "\t\tdown=" << neighbor.c_str() << endl; + s << "\t\tdown=" << neighbor.c_str() << std::endl; } } - s << "end" << endl; + s << "end" << std::endl; return s; } diff --git a/server/CConfig.h b/server/CConfig.h index 3e9fcd16..cbb5f9ae 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -5,7 +5,7 @@ #include "CString.h" #include "XBase.h" #include -#include +#include "stdmap.h" class CConfig; @@ -57,7 +57,7 @@ public: } private: - CConfig::internal_const_iterator m_i; + internal_const_iterator m_i; }; CConfig(); @@ -98,17 +98,17 @@ public: // read/write a configuration. operator>> will throw XConfigRead // on error. - friend istream& operator>>(istream&, CConfig&); - friend ostream& operator<<(ostream&, const CConfig&); + friend std::istream& operator>>(std::istream&, CConfig&); + friend std::ostream& operator<<(std::ostream&, const CConfig&); // get the name of a direction (for debugging) static const char* dirName(EDirection); private: - static bool readLine(istream&, CString&); - void readSection(istream&); - void readSectionScreens(istream&); - void readSectionLinks(istream&); + static bool readLine(std::istream&, CString&); + void readSection(std::istream&); + void readSectionScreens(std::istream&); + void readSectionLinks(std::istream&); private: CCellMap m_map; diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index 7344fb29..05378d8e 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -6,8 +6,9 @@ #include "CLog.h" #include "XThread.h" #include "ISocket.h" -#include -#include +#include "stdset.h" +#include "stdsstream.h" +#include // // CHTTPServer @@ -158,7 +159,7 @@ void CHTTPServer::doProcessGetEditMap( static const char* s_editMapScreenEnd = "\r\n"; - ostringstream s; + std::ostringstream s; // convert screen map into a temporary screen map CScreenArray screens; @@ -236,7 +237,7 @@ void CHTTPServer::doProcessPostEditMap( } try { - ostringstream s; + std::ostringstream s; // convert post data into a temporary screen map. also check // that no screen name is invalid or used more than once. @@ -309,7 +310,7 @@ void CHTTPServer::doProcessPostEditMap( // now reply with current map doProcessGetEditMap(request, reply); } - catch (XHTTP& e) { + catch (XHTTP&) { // FIXME -- construct a more meaningful error? throw; } @@ -318,7 +319,7 @@ void CHTTPServer::doProcessPostEditMap( bool CHTTPServer::parseXY( const CString& xy, SInt32& x, SInt32& y) { - istringstream s(xy); + std::istringstream s(xy); char delimiter; s >> x; s.get(delimiter); @@ -326,65 +327,10 @@ bool CHTTPServer::parseXY( return (!!s && delimiter == 'x'); } -/* -#include // FIXME -// FIXME - cout << "method: " << request.m_method << endl; - cout << "uri: " << request.m_uri << endl; - cout << "version: " << request.m_majorVersion << "." << - request.m_majorVersion << endl; - cout << "headers:" << endl; - for (CHTTPRequest::CHeaderMap::const_iterator - index = request.m_headerIndexByName.begin(); - index != request.m_headerIndexByName.end(); - ++index) { - assert(index->second < request.m_headers.size()); - cout << " " << index->first << ": " << - request.m_headers[index->second] << endl; - } - cout << endl; - - cout << request.m_body << endl; - -// FIXME - reply.m_majorVersion = request.m_majorVersion; - reply.m_minorVersion = request.m_minorVersion; - reply.m_status = 200; - reply.m_reason = "OK"; - reply.m_method = request.m_method; - reply.m_headers.push_back(std::make_pair(CString("Content-Type"), - CString("text/html"))); -if (request.m_uri != "/bar") { - reply.m_body = -"\r\n" -" \r\n" -" test\r\n" -" \r\n" -" \r\n" -"

test

\r\n" -"
\r\n" -" \r\n" -" \r\n" -"
\r\n" -" \r\n" -"\r\n" -; -} -else { - reply.m_body = -"\r\n" -" \r\n" -" test reply\r\n" -" \r\n" -" \r\n" -"

test reply

\r\n" -" \r\n" -"\r\n" -; -} - // FIXME -*/ +// +// CHTTPServer::CScreenArray +// CHTTPServer::CScreenArray::CScreenArray() : m_w(0), m_h(0) { diff --git a/server/CHTTPServer.h b/server/CHTTPServer.h index 60369c31..50a5708b 100644 --- a/server/CHTTPServer.h +++ b/server/CHTTPServer.h @@ -3,7 +3,7 @@ #include "BasicTypes.h" #include "CString.h" -#include +#include "stdvector.h" class CServer; class CConfig; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index fa6d7e5c..6e084fdd 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -22,7 +22,13 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : m_mark(0), m_markReceived(0) { - // do nothing + // detect operating system + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + log((CLOG_WARN "cannot determine OS: %d", GetLastError())); + } + m_is95Family = (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); } CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() @@ -97,7 +103,19 @@ void CMSWindowsPrimaryScreen::doEnter() // not active anymore m_active = false; - // set the zones that should cause a jump + // release keyboard/mouse and set the zones that should cause a jump +/* FIXME +if (UnregisterHotKey(m_window, 0x0001) != 0) { +log((CLOG_INFO "released hot key")); +} +else { +log((CLOG_INFO "failed to release hot key: %d", GetLastError())); +} +*/ + if (m_is95Family) { + DWORD dummy = 0; + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0); + } SInt32 w, h; getScreenSize(&w, &h); SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( @@ -159,6 +177,19 @@ void CMSWindowsPrimaryScreen::leave() SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress( m_hookLibrary, "setRelay"); setRelay(); + if (m_is95Family) { + // disable ctrl+alt+del, alt+tab, ctrl+esc + DWORD dummy = 0; + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); + } +/* FIXME +if (RegisterHotKey(m_window, 0x0001, MOD_ALT, VK_TAB) != 0) { +log((CLOG_INFO "got hot key")); +} +else { +log((CLOG_INFO "failed to get hot key: %d", GetLastError())); +} +*/ // get keyboard input and capture mouse SetActiveWindow(m_window); @@ -203,6 +234,17 @@ void CMSWindowsPrimaryScreen::leave() } } +void CMSWindowsPrimaryScreen::onConfigure() +{ + if (!m_active) { + SInt32 w, h; + getScreenSize(&w, &h); + SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( + m_hookLibrary, "setZone"); + setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize()); + } +} + void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { // set the cursor position without generating an event @@ -479,7 +521,12 @@ LRESULT CMSWindowsPrimaryScreen::onEvent( WPARAM wParam, LPARAM lParam) { switch (msg) { - // FIXME -- handle display changes (and resize full-screen window) +/* +case WM_HOTKEY: +log((CLOG_INFO "hot key: %d, %d, %s %s %s", wParam, HIWORD(lParam), (LOWORD(lParam) & MOD_ALT) ? "ALT" : "", (LOWORD(lParam) & MOD_CONTROL) ? "CTRL" : "", (LOWORD(lParam) & MOD_SHIFT) ? "SHIFT" : "", (LOWORD(lParam) & MOD_WIN) ? "WIN" : "")); +return 0; +*/ + case WM_PAINT: ValidateRect(hwnd, NULL); return 0; diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 9d44c912..a3f3afe0 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -20,6 +20,7 @@ public: virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); virtual void leave(); + virtual void onConfigure(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); @@ -49,6 +50,7 @@ private: private: CServer* m_server; + bool m_is95Family; bool m_active; HWND m_window; HWND m_nextClipboardWindow; diff --git a/server/CServer.cpp b/server/CServer.cpp index c2c172a4..0de5c196 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -151,6 +151,11 @@ bool CServer::setConfig(const CConfig& config) // cut over m_config = config; + + // tell primary screen about reconfiguration + if (m_primary != NULL) { + m_primary->onConfigure(); + } } // wait for old secondary screen threads to disconnect. must diff --git a/server/CServer.h b/server/CServer.h index f11ba980..9f7b32b3 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -10,8 +10,8 @@ #include "CString.h" #include "CThread.h" #include "XBase.h" -#include -#include +#include "stdlist.h" +#include "stdmap.h" class CThread; class IServerProtocol; diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 3c1df9eb..21bf518d 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -42,6 +42,10 @@ static HHOOK g_keyboard = NULL; static HHOOK g_mouse = NULL; static HHOOK g_cbt = NULL; static HHOOK g_getMessage = NULL; +static HANDLE g_keyHookThread = NULL; +static DWORD g_keyHookThreadID = 0; +static HANDLE g_keyHookEvent = NULL; +static HHOOK g_keyboardLL = NULL; static bool g_relay = false; static SInt32 g_zoneSize = 0; static UInt32 g_zoneSides = 0; @@ -103,7 +107,9 @@ static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) case VK_CAPITAL: case VK_NUMLOCK: case VK_SCROLL: - // pass event + // pass event on. we want to let these through to + // the window proc because otherwise the keyboard + // lights may not stay synchronized. break; default: @@ -250,6 +256,119 @@ static LRESULT CALLBACK getMessageHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_getMessage, code, wParam, lParam); } +#if (_WIN32_WINNT >= 0x0400) + +// +// low-level keyboard hook -- this allows us to capture and handle +// alt+tab, alt+esc, ctrl+esc, and windows key hot keys. on the down +// side, key repeats are not compressed for us. +// + +static LRESULT CALLBACK keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + if (g_relay) { + KBDLLHOOKSTRUCT* info = reinterpret_cast(lParam); + + // let certain keys pass through + switch (info->vkCode) { + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // pass event on. we want to let these through to + // the window proc because otherwise the keyboard + // lights may not stay synchronized. + break; + + default: + // construct lParam for WM_KEYDOWN, etc. + DWORD lParam = 1; // repeat code + lParam |= (info->scanCode << 16); // scan code + if (info->flags & LLKHF_EXTENDED) { + lParam |= (1lu << 24); // extended key + } + if (info->flags & LLKHF_ALTDOWN) { + lParam |= (1lu << 29); // context code + } + if (info->flags & LLKHF_UP) { + lParam |= (1lu << 31); // transition + } + // FIXME -- bit 30 should be set if key was already down + + // forward message to our window + PostMessage(g_hwnd, SYNERGY_MSG_KEY, info->vkCode, lParam); + + // discard event + return 1; + } + } + } + + return CallNextHookEx(g_keyboardLL, code, wParam, lParam); +} + +static DWORD WINAPI getKeyboardLLProc(void*) +{ + // thread proc for low-level keyboard hook. this does nothing but + // install the hook, process events, and uninstall the hook. + + // force this thread to have a message queue + MSG msg; + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + // install low-level keyboard hook + g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, + &keyboardLLHook, + g_hinstance, + 0); + if (g_keyboardLL == NULL) { + // indicate failure and exit + g_keyHookThreadID = 0; + SetEvent(g_keyHookEvent); + return 1; + } + + // ready + SetEvent(g_keyHookEvent); + + // message loop + bool done = false; + while (!done) { + switch (GetMessage(&msg, NULL, 0, 0)) { + case -1: + break; + + case 0: + done = true; + break; + + default: + TranslateMessage(&msg); + DispatchMessage(&msg); + break; + } + } + + // uninstall hook + UnhookWindowsHookEx(g_keyboardLL); + g_keyboardLL = NULL; + + return 0; +} + +#else // (_WIN32_WINNT < 0x0400) + +#error foo + +static DWORD WINAPI getKeyboardLLProc(void*) +{ + g_keyHookThreadID = 0; + SetEvent(g_keyHookEvent); + return 1; +} + +#endif + static EWheelSupport GetWheelSupport() { // get operating system @@ -286,8 +405,8 @@ static EWheelSupport GetWheelSupport() // assume modern. we don't do anything special in this case // except respond to WM_MOUSEWHEEL messages. GetSystemMetrics() // can apparently return FALSE even if a mouse wheel is present - // though i'm not sure exactly when it does that (but WinME does - // for my logitech usb trackball). + // though i'm not sure exactly when it does that (WinME returns + // FALSE for my logitech USB trackball). return kWheelModern; } @@ -387,6 +506,34 @@ int install(HWND hwnd) // ignore failure; we just won't get mouse wheel messages } + // install low-level keyboard hook, if possible. since this hook + // is called in the context of the installing thread and that + // thread must have a message loop but we don't want the caller's + // message loop to do the work, we'll fire up a separate thread + // just for the hook. note that low-level keyboard hooks are only + // available on windows NT SP3 and above. + g_keyHookEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (g_keyHookEvent != NULL) { + g_keyHookThread = CreateThread(NULL, 0, &getKeyboardLLProc, 0, + CREATE_SUSPENDED, &g_keyHookThreadID); + if (g_keyHookThread != NULL) { + // start the thread and wait for it to initialize + ResumeThread(g_keyHookThread); + WaitForSingleObject(g_keyHookEvent, INFINITE); + ResetEvent(g_keyHookEvent); + + // the thread clears g_keyHookThreadID if it failed + if (g_keyHookThreadID == 0) { + CloseHandle(g_keyHookThread); + g_keyHookThread = NULL; + } + } + if (g_keyHookThread == NULL) { + CloseHandle(g_keyHookEvent); + g_keyHookEvent = NULL; + } + } + return 1; } @@ -397,6 +544,15 @@ int uninstall(void) assert(g_cbt != NULL); // uninstall hooks + if (g_keyHookThread != NULL) { + PostThreadMessage(g_keyHookThreadID, WM_QUIT, 0, 0); + WaitForSingleObject(g_keyHookThread, INFINITE); + CloseHandle(g_keyHookEvent); + CloseHandle(g_keyHookThread); + g_keyHookEvent = NULL; + g_keyHookThread = NULL; + g_keyHookThreadID = 0; + } UnhookWindowsHookEx(g_keyboard); UnhookWindowsHookEx(g_mouse); UnhookWindowsHookEx(g_cbt); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 2ba6cfee..9d5a828b 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -303,6 +303,11 @@ void CXWindowsPrimaryScreen::leave() m_active = true; } +void CXWindowsPrimaryScreen::onConfigure() +{ + // do nothing +} + void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { CDisplayLock display(this); diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index e57f00b4..05c53a30 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -18,6 +18,7 @@ public: virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); virtual void leave(); + virtual void onConfigure(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); diff --git a/server/server.cpp b/server/server.cpp index e2b1ffdb..56a15d0c 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -4,7 +4,8 @@ #include "CMutex.h" #include "CNetwork.h" #include "CThread.h" -#include +#include "stdfstream.h" +#include // // config file stuff @@ -54,7 +55,7 @@ void realMain() CConfig config; { log((CLOG_DEBUG "opening configuration")); - ifstream configStream(s_configFileName); + std::ifstream configStream(s_configFileName); if (!configStream) { throw XConfigRead("cannot open configuration"); } diff --git a/server/server.dsp b/server/server.dsp index db189bee..9f201f65 100644 --- a/server/server.dsp +++ b/server/server.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\http" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -68,7 +68,7 @@ LINK32=link.exe # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\http" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -92,11 +92,15 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=.\CMSWindowsPrimaryScreen.cpp +SOURCE=.\CConfig.cpp # End Source File # Begin Source File -SOURCE=.\CConfig.cpp +SOURCE=.\CHTTPServer.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.cpp # End Source File # Begin Source File @@ -124,11 +128,15 @@ SOURCE=.\server.rc # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\CMSWindowsPrimaryScreen.h +SOURCE=.\CConfig.h # End Source File # Begin Source File -SOURCE=.\CConfig.h +SOURCE=.\CHTTPServer.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.h # End Source File # Begin Source File diff --git a/server/synrgyhk.dsp b/server/synrgyhk.dsp index d3c6c9b5..784e00a8 100644 --- a/server/synrgyhk.dsp +++ b/server/synrgyhk.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Intermediate_Dir "ReleaseHook" # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -68,7 +68,7 @@ LINK32=link.exe # PROP Intermediate_Dir "DebugHook" # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -93,6 +93,7 @@ LINK32=link.exe # Begin Source File SOURCE=.\CSynergyHook.cpp +# ADD CPP /D _WIN32_WINNT=0x0400 # End Source File # End Group # Begin Group "Header Files" diff --git a/synergy.dsw b/synergy.dsw index 4e67cce8..1d70ed66 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -7,10 +7,6 @@ Project: "all"=.\all.dsp - Package Owner=<4> Package=<5> {{{ - begin source code control - millpond - . - end source code control }}} Package=<4> @@ -29,10 +25,6 @@ Project: "base"=.\base\base.dsp - Package Owner=<4> Package=<5> {{{ - begin source code control - millpond - .\base - end source code control }}} Package=<4> @@ -45,10 +37,6 @@ Project: "client"=.\client\client.dsp - Package Owner=<4> Package=<5> {{{ - begin source code control - millpond - .\client - end source code control }}} Package=<4> @@ -72,14 +60,22 @@ Package=<4> ############################################################################### +Project: "http"=.\HTTP\http.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "io"=.\io\io.dsp - Package Owner=<4> Package=<5> {{{ - begin source code control - millpond - .\io - end source code control }}} Package=<4> @@ -92,10 +88,6 @@ Project: "makehook"=.\server\makehook.dsp - Package Owner=<4> Package=<5> {{{ - begin source code control - millpond - .\server - end source code control }}} Package=<4> @@ -111,10 +103,6 @@ Project: "mt"=.\mt\mt.dsp - Package Owner=<4> Package=<5> {{{ - begin source code control - millpond - .\mt - end source code control }}} Package=<4> @@ -127,10 +115,6 @@ Project: "net"=.\net\net.dsp - Package Owner=<4> Package=<5> {{{ - begin source code control - millpond - .\net - end source code control }}} Package=<4> @@ -143,10 +127,6 @@ Project: "server"=.\server\server.dsp - Package Owner=<4> Package=<5> {{{ - begin source code control - millpond - .\server - end source code control }}} Package=<4> @@ -169,6 +149,9 @@ Package=<4> Begin Project Dependency Project_Dep_Name makehook End Project Dependency + Begin Project Dependency + Project_Dep_Name http + End Project Dependency }}} ############################################################################### @@ -177,10 +160,6 @@ Project: "synergy"=.\synergy\synergy.dsp - Package Owner=<4> Package=<5> {{{ - begin source code control - millpond - .\synergy - end source code control }}} Package=<4> @@ -193,10 +172,6 @@ Project: "synrgyhk"=.\server\synrgyhk.dsp - Package Owner=<4> Package=<5> {{{ - begin source code control - millpond - .\server - end source code control }}} Package=<4> diff --git a/synergy/CXWindowsClipboard.h b/synergy/CXWindowsClipboard.h index bb0fde8e..3cc4b3b9 100644 --- a/synergy/CXWindowsClipboard.h +++ b/synergy/CXWindowsClipboard.h @@ -4,9 +4,9 @@ #include "IClipboard.h" #include "ClipboardTypes.h" #include "CString.h" +#include "stdmap.h" +#include "stdlist.h" #include -#include -#include class CXWindowsClipboard : public IClipboard { public: diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 4390addc..464d85af 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -49,6 +49,11 @@ public: // the cursor and grab exclusive access to the input devices. virtual void leave() = 0; + // called when the configuration has changed. subclasses may need + // to adjust things (like the jump zones) after the configuration + // changes. + virtual void onConfigure() = 0; + // warp the cursor to the given position virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp index 91f325ee..a14a9587 100644 --- a/synergy/synergy.dsp +++ b/synergy/synergy.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" From fa4d24216fc2f7a61600438b15be3f2bd64c22ba Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 Jun 2002 11:49:46 +0000 Subject: [PATCH 126/807] now limiting number of simultaneous HTTP requests being handled at once. this is to prevent denial of service. --- mt/CCondVar.h | 3 +-- server/CServer.cpp | 29 ++++++++++++++++++++++++++++- server/CServer.h | 5 ++++- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/mt/CCondVar.h b/mt/CCondVar.h index b0663db8..59b2d90c 100644 --- a/mt/CCondVar.h +++ b/mt/CCondVar.h @@ -9,8 +9,7 @@ class CStopwatch; class CCondVarBase { public: // mutex must be supplied. all condition variables have an - // associated mutex. the copy c'tor uses the same mutex as the - // argument and is otherwise like the default c'tor. + // associated mutex. CCondVarBase(CMutex* mutex); ~CCondVarBase(); diff --git a/server/CServer.cpp b/server/CServer.cpp index 0de5c196..cc53826f 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -43,11 +43,15 @@ else { wait(0); exit(1); } // CServer // +const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; + CServer::CServer() : m_primary(NULL), m_active(NULL), m_primaryInfo(NULL), m_seqNum(0), - m_httpServer(NULL) + m_httpServer(NULL), + m_httpAvailable(&m_mutex, + s_httpMaxSimultaneousRequests) { m_socketFactory = NULL; m_securityFactory = NULL; @@ -1128,6 +1132,16 @@ void CServer::acceptHTTPClients(void*) // accept connections and begin processing them log((CLOG_DEBUG1 "waiting for HTTP connections")); for (;;) { + // limit the number of HTTP requests being handled at once + { + CLock lock(&m_httpAvailable); + while (m_httpAvailable == 0) { + m_httpAvailable.wait(); + } + assert(m_httpAvailable > 0); + m_httpAvailable = m_httpAvailable - 1; + } + // accept connection CThread::testCancel(); ISocket* socket = listen->accept(); @@ -1141,6 +1155,7 @@ void CServer::acceptHTTPClients(void*) } catch (XBase& e) { log((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); + // FIXME -- quit? quit(); } } @@ -1163,9 +1178,21 @@ void CServer::processHTTPRequest(void* vsocket) // clean up socket->close(); delete socket; + + // increment available HTTP handlers + { + CLock lock(&m_httpAvailable); + m_httpAvailable = m_httpAvailable + 1; + m_httpAvailable.signal(); + } } catch (...) { delete socket; + { + CLock lock(&m_httpAvailable); + m_httpAvailable = m_httpAvailable + 1; + m_httpAvailable.signal(); + } throw; } } diff --git a/server/CServer.h b/server/CServer.h index 9f7b32b3..d05bc867 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -6,6 +6,7 @@ #include "MouseTypes.h" #include "CConfig.h" #include "CClipboard.h" +#include "CCondVar.h" #include "CMutex.h" #include "CString.h" #include "CThread.h" @@ -235,8 +236,10 @@ private: CClipboardInfo m_clipboards[kClipboardEnd]; - // server for processing HTTP requests + // HTTP request processing stuff CHTTPServer* m_httpServer; + CCondVar m_httpAvailable; + static const SInt32 s_httpMaxSimultaneousRequests; }; #endif From 1da9be88c999a587a3eaba06d78eac9730974a88 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 Jun 2002 13:34:35 +0000 Subject: [PATCH 127/807] added a maximum request size to CHTTPProtocol so we can bail on clients that cause us to use too much memory. also put methods in CHTTPRequest to get/set headers and changed the data structure used to store them. fixed a couple of other miscellaneous bugs in CHTTPProtocol.cpp. --- http/CHTTPProtocol.cpp | 186 +++++++++++++++++++++++++++++------------ http/CHTTPProtocol.h | 62 +++++++++++--- server/CHTTPServer.cpp | 8 +- server/CHTTPServer.h | 1 + 4 files changed, 190 insertions(+), 67 deletions(-) diff --git a/http/CHTTPProtocol.cpp b/http/CHTTPProtocol.cpp index 4c52ca7d..0d00fb87 100644 --- a/http/CHTTPProtocol.cpp +++ b/http/CHTTPProtocol.cpp @@ -56,15 +56,86 @@ bool CHTTPUtil::CaselessCmp::operator()( return less(a, b); } +// +// CHTTPRequest +// + +CHTTPRequest::CHTTPRequest() +{ + // do nothing +} + +CHTTPRequest::~CHTTPRequest() +{ + // do nothing +} + +void CHTTPRequest::insertHeader( + const CString& name, const CString& value) +{ + CHeaderMap::iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + index->second->second = value; + } + else { + CHeaderList::iterator pos = m_headers.insert( + m_headers.end(), std::make_pair(name, value)); + m_headerByName.insert(std::make_pair(name, pos)); + } +} + +void CHTTPRequest::appendHeader( + const CString& name, const CString& value) +{ + CHeaderMap::iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + index->second->second += ","; + index->second->second += value; + } + else { + CHeaderList::iterator pos = m_headers.insert( + m_headers.end(), std::make_pair(name, value)); + m_headerByName.insert(std::make_pair(name, pos)); + } +} + +void CHTTPRequest::eraseHeader(const CString& name) +{ + CHeaderMap::iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + m_headers.erase(index->second); + } +} + +bool CHTTPRequest::isHeader(const CString& name) const +{ + return (m_headerByName.find(name) != m_headerByName.end()); +} + +CString CHTTPRequest::getHeader(const CString& name) const +{ + CHeaderMap::const_iterator index = m_headerByName.find(name); + if (index != m_headerByName.end()) { + return index->second->second; + } + else { + return CString(); + } +} + // // CHTTPProtocol // -CHTTPRequest* CHTTPProtocol::readRequest(IInputStream* stream) +CHTTPRequest* CHTTPProtocol::readRequest( + IInputStream* stream, UInt32 maxSize) { CString scratch; + // note if we should limit the request size + const bool checkSize = (maxSize > 0); + // parse request line by line CHTTPRequest* request = new CHTTPRequest; try { @@ -73,6 +144,12 @@ CHTTPRequest* CHTTPProtocol::readRequest(IInputStream* stream) // read request line. accept and discard leading empty lines. do { line = readLine(stream, scratch); + if (checkSize) { + if (line.size() + 2 > maxSize) { + throw XHTTP(413); + } + maxSize -= line.size() + 2; + } } while (line.empty()); // parse request line: @@ -109,12 +186,13 @@ CHTTPRequest* CHTTPProtocol::readRequest(IInputStream* stream) } // parse headers - readHeaders(stream, request, false, scratch); + readHeaders(stream, request, false, scratch, + checkSize ? &maxSize : NULL); // HTTP/1.1 requests must have a Host header if (request->m_majorVersion > 1 || (request->m_majorVersion == 1 && request->m_minorVersion >= 1)) { - if (request->m_headerIndexByName.count("Host") == 0) { + if (request->isHeader("Host") == 0) { log((CLOG_DEBUG1 "Host header missing")); throw XHTTP(400); } @@ -123,8 +201,8 @@ CHTTPRequest* CHTTPProtocol::readRequest(IInputStream* stream) // some methods may not have a body. ensure that the headers // that indicate the body length do not exist for those methods // and do exist for others. - if ((request->m_headerIndexByName.count("Transfer-Encoding") == 0 && - request->m_headerIndexByName.count("Content-Length") == 0) != + if ((request->isHeader("Transfer-Encoding") || + request->isHeader("Content-Length")) == (request->m_method == "GET" || request->m_method == "HEAD")) { log((CLOG_DEBUG1 "HTTP method (%s)/body mismatch", request->m_method.c_str())); @@ -136,13 +214,11 @@ CHTTPRequest* CHTTPProtocol::readRequest(IInputStream* stream) // 1. Transfer-Encoding indicates a "chunked" transfer // 2. Content-Length is present // Content-Length is ignored for "chunked" transfers. - CHTTPRequest::CHeaderMap::iterator index = request-> - m_headerIndexByName.find("Transfer-Encoding"); - if (index != request->m_headerIndexByName.end()) { + CString header; + if (!(header = request->getHeader("Transfer-Encoding")).empty()) { // we only understand "chunked" encodings - if (!CHTTPUtil::CaselessCmp::equal( - request->m_headers[index->second], "chunked")) { - log((CLOG_DEBUG1 "unsupported Transfer-Encoding %s", request->m_headers[index->second].c_str())); + if (!CHTTPUtil::CaselessCmp::equal(header, "chunked")) { + log((CLOG_DEBUG1 "unsupported Transfer-Encoding %s", header.c_str())); throw XHTTP(501); } @@ -150,36 +226,39 @@ CHTTPRequest* CHTTPProtocol::readRequest(IInputStream* stream) UInt32 oldSize; do { oldSize = request->m_body.size(); - request->m_body += readChunk(stream, scratch); + request->m_body += readChunk(stream, scratch, + checkSize ? &maxSize : NULL); } while (request->m_body.size() != oldSize); // read footer - readHeaders(stream, request, true, scratch); + readHeaders(stream, request, true, scratch, + checkSize ? &maxSize : NULL); // remove "chunked" from Transfer-Encoding and set the // Content-Length. - // FIXME - // FIXME -- note that just deleting Transfer-Encoding will - // mess up indices in m_headerIndexByName, and replacing - // it with Content-Length could lead to two of those. + std::ostringstream s; + s << std::dec << request->m_body.size(); + request->eraseHeader("Transfer-Encoding"); + request->insertHeader("Content-Length", s.str()); } - else if ((index = request->m_headerIndexByName. - find("Content-Length")) != - request->m_headerIndexByName.end()) { - // FIXME -- check for overly-long requests - + else if (!(header = request->getHeader("Content-Length")).empty()) { // parse content-length UInt32 length; { - std::istringstream s(request->m_headers[index->second]); + std::istringstream s(header); s.exceptions(std::ios::goodbit); s >> length; if (!s) { - log((CLOG_DEBUG1 "cannot parse Content-Length", request->m_headers[index->second].c_str())); + log((CLOG_DEBUG1 "cannot parse Content-Length", header.c_str())); throw XHTTP(400); } } + // check against expected size + if (checkSize && length > maxSize) { + throw XHTTP(413); + } + // use content length request->m_body = readBlock(stream, length, scratch); if (request->m_body.size() != length) { @@ -287,13 +366,11 @@ bool CHTTPProtocol::parseFormData( static const char quote[] = "\""; // find the Content-Type header - CHTTPRequest::CHeaderMap::const_iterator contentTypeIndex = - request.m_headerIndexByName.find("Content-Type"); - if (contentTypeIndex == request.m_headerIndexByName.end()) { + const CString contentType = request.getHeader("Content-Type"); + if (contentType.empty()) { // missing required Content-Type header return false; } - const CString contentType = request.m_headers[contentTypeIndex->second]; // parse type CString::const_iterator index = std::search( @@ -335,8 +412,7 @@ bool CHTTPProtocol::parseFormData( if (body.size() >= partIndex + 2 && body[partIndex ] == '-' && body[partIndex + 1] == '-') { - // found last part. success if there's no trailing data. - // FIXME -- check for trailing data (other than a single CRLF) + // found last part. ignore trailing data, if any. return true; } @@ -500,7 +576,8 @@ CString CHTTPProtocol::readBlock( CString CHTTPProtocol::readChunk( IInputStream* stream, - CString& tmpBuffer) + CString& tmpBuffer, + UInt32* maxSize) { CString line; @@ -522,8 +599,15 @@ CString CHTTPProtocol::readChunk( return CString(); } + // check size + if (maxSize != NULL) { + if (line.size() + 2 + size + 2 > *maxSize) { + throw XHTTP(413); + } + maxSize -= line.size() + 2 + size + 2; + } + // read size bytes - // FIXME -- check for overly-long requests CString data = readBlock(stream, size, tmpBuffer); if (data.size() != size) { log((CLOG_DEBUG1 "expected/actual chunk size mismatch", size, data.size())); @@ -544,27 +628,36 @@ void CHTTPProtocol::readHeaders( IInputStream* stream, CHTTPRequest* request, bool isFooter, - CString& tmpBuffer) + CString& tmpBuffer, + UInt32* maxSize) { // parse headers. done with headers when we get a blank line. + CString name; CString line = readLine(stream, tmpBuffer); while (!line.empty()) { + // check size + if (maxSize != NULL) { + if (line.size() + 2 > *maxSize) { + throw XHTTP(413); + } + *maxSize -= line.size() + 2; + } + // if line starts with space or tab then append it to the // previous header. if there is no previous header then // throw. if (line[0] == ' ' || line[0] == '\t') { - if (request->m_headers.size() == 0) { + if (name.empty()) { log((CLOG_DEBUG1 "first header is a continuation")); throw XHTTP(400); } - request->m_headers.back() += ","; - request->m_headers.back() == line; + request->appendHeader(name, line); } // line should have the form: :[] else { // parse - CString name, value; + CString value; std::istringstream s(line); s.exceptions(std::ios::goodbit); std::getline(s, name, ':'); @@ -577,29 +670,14 @@ void CHTTPProtocol::readHeaders( // check validity of name if (isFooter) { // FIXME -- only certain names are allowed in footers + // but which ones? } - // check if we've seen this header before - CHTTPRequest::CHeaderMap::iterator index = - request->m_headerIndexByName.find(name); - if (index == request->m_headerIndexByName.end()) { - // it's a new header - request->m_headerIndexByName.insert(std::make_pair(name, - request->m_headers.size())); - request->m_headers.push_back(value); - } - else { - // it's an existing header. append value to previous - // header, separated by a comma. - request->m_headers[index->second] += ','; - request->m_headers[index->second] += value; - } + request->appendHeader(name, value); } // next header line = readLine(stream, tmpBuffer); - - // FIXME -- should check for overly-long requests } } diff --git a/http/CHTTPProtocol.h b/http/CHTTPProtocol.h index 357c3175..316d1fd4 100644 --- a/http/CHTTPProtocol.h +++ b/http/CHTTPProtocol.h @@ -3,6 +3,7 @@ #include "BasicTypes.h" #include "CString.h" +#include "stdlist.h" #include "stdmap.h" #include "stdvector.h" @@ -25,19 +26,52 @@ public: class CHTTPRequest { public: - typedef std::map CHeaderMap; - typedef std::vector CHeaderList; + typedef std::list > CHeaderList; + typedef std::map CHeaderMap; + typedef CHeaderList::const_iterator const_iterator; + CHTTPRequest(); + ~CHTTPRequest(); + + // manipulators + + // add a header by name. replaces existing header, if any. + // headers are sent in the order they're inserted. replacing + // a header does not change its original position in the order. + void insertHeader(const CString& name, const CString& value); + + // append a header. equivalent to insertHeader() if the header + // doesn't exist, otherwise it appends a comma and the value to + // the existing header. + void appendHeader(const CString& name, const CString& value); + + // remove a header by name. does nothing if no such header. + void eraseHeader(const CString& name); + + // accessors + + // returns true iff the header exists + bool isHeader(const CString& name) const; + + // get a header by name. returns the empty string if no such header. + CString getHeader(const CString& name) const; + + // get iterator over all headers in the order they were added + const_iterator begin() const { return m_headers.begin(); } + const_iterator end() const { return m_headers.end(); } + +public: + // note -- these members are public for convenience CString m_method; CString m_uri; SInt32 m_majorVersion; SInt32 m_minorVersion; - - CHeaderList m_headers; - CHeaderMap m_headerIndexByName; - CString m_body; - // FIXME -- need parts-of-body for POST messages + +private: + CHeaderList m_headers; + CHeaderMap m_headerByName; }; class CHTTPReply { @@ -59,9 +93,11 @@ class CHTTPProtocol { public: // read and parse an HTTP request. result is returned in a // CHTTPRequest which the client must delete. throws an - // XHTTP if there was a parse error. throws an XIO - // exception if there was a read error. - static CHTTPRequest* readRequest(IInputStream*); + // XHTTP if there was a parse error. throws an XIO exception + // if there was a read error. if maxSize is greater than + // zero and the request is larger than maxSize bytes then + // throws XHTTP(413). + static CHTTPRequest* readRequest(IInputStream*, UInt32 maxSize = 0); // send an HTTP reply on the stream static void reply(IOutputStream*, CHTTPReply&); @@ -77,10 +113,12 @@ private: static CString readLine(IInputStream*, CString& tmpBuffer); static CString readBlock(IInputStream*, UInt32 numBytes, CString& tmpBuffer); - static CString readChunk(IInputStream*, CString& tmpBuffer); + static CString readChunk(IInputStream*, CString& tmpBuffer, + UInt32* maxSize); static void readHeaders(IInputStream*, CHTTPRequest*, bool isFooter, - CString& tmpBuffer); + CString& tmpBuffer, + UInt32* maxSize); static bool isValidToken(const CString&); }; diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index 05378d8e..d85b0978 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -14,6 +14,11 @@ // CHTTPServer // +// maximum size of an HTTP request. this should be large enough to +// handle any reasonable request but small enough to prevent a +// malicious client from causing us to use too much memory. +const UInt32 CHTTPServer::s_maxRequestSize = 32768; + CHTTPServer::CHTTPServer(CServer* server) : m_server(server) { // do nothing @@ -31,7 +36,8 @@ void CHTTPServer::processRequest(ISocket* socket) CHTTPRequest* request = NULL; try { // parse request - request = CHTTPProtocol::readRequest(socket->getInputStream()); + request = CHTTPProtocol::readRequest( + socket->getInputStream(), s_maxRequestSize); if (request == NULL) { throw XHTTP(400); } diff --git a/server/CHTTPServer.h b/server/CHTTPServer.h index 50a5708b..7c0cf311 100644 --- a/server/CHTTPServer.h +++ b/server/CHTTPServer.h @@ -96,6 +96,7 @@ protected: private: CServer* m_server; + static const UInt32 s_maxRequestSize; }; #endif From 1d3807cb0ecccbbcc76ca2e5dedb3a5429e32e02 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 Jun 2002 18:49:35 +0000 Subject: [PATCH 128/807] added SIGINT and SIGTERM handling to unix client and server. either signal causes the main thread to be cancelled. added necessary code to make main thread cancellation clean up nicely. --- client/CClient.cpp | 18 ++++++++++++-- client/client.cpp | 19 ++++++++++++--- mt/CThread.h | 2 +- mt/CThreadRep.cpp | 61 ++++++++++++++++++++++++++++++++++++---------- mt/CThreadRep.h | 3 +++ server/CServer.cpp | 18 +++++++++++--- server/server.cpp | 19 ++++++++++++--- 7 files changed, 115 insertions(+), 25 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 4447d295..de4e7de0 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -6,11 +6,12 @@ #include "ISecondaryScreen.h" #include "ProtocolTypes.h" #include "CLock.h" +#include "CLog.h" #include "CThread.h" #include "CTimerThread.h" -#include "XSynergy.h" #include "TMethodJob.h" -#include "CLog.h" +#include "XSynergy.h" +#include "XThread.h" #include #include @@ -71,6 +72,7 @@ void CClient::run(const CNetworkAddress& serverAddress) log((CLOG_ERR "client error: %s", e.what())); // clean up + log((CLOG_NOTE "stopping client")); if (thread != NULL) { thread->cancel(); thread->wait(); @@ -78,10 +80,22 @@ void CClient::run(const CNetworkAddress& serverAddress) } closeSecondaryScreen(); } + catch (XThread&) { + // clean up + log((CLOG_NOTE "stopping client")); + if (thread != NULL) { + thread->cancel(); + thread->wait(); + delete thread; + } + closeSecondaryScreen(); + throw; + } catch (...) { log((CLOG_DEBUG "unknown client error")); // clean up + log((CLOG_NOTE "stopping client")); if (thread != NULL) { thread->cancel(); thread->wait(); diff --git a/client/client.cpp b/client/client.cpp index 1364d83d..4876c527 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -5,6 +5,7 @@ #include "CNetwork.h" #include "CNetworkAddress.h" #include "CThread.h" +#include "XThread.h" #include // @@ -42,20 +43,24 @@ void realMain(const CString& name, s_logMutex = &logMutex; CLog::setLock(&logLock); - // initialize network library - CNetwork::init(); - CClient* client = NULL; try { + // initialize network library + CNetwork::init(); + + // run client CNetworkAddress addr(hostname, port); client = new CClient(name); client->run(addr); + + // clean up delete client; CNetwork::cleanup(); CLog::setLock(NULL); s_logMutex = NULL; } catch (...) { + // clean up delete client; CNetwork::cleanup(); CLog::setLock(NULL); @@ -93,6 +98,10 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); return 1; } + catch (XThread&) { + // terminated + return 1; + } } #else @@ -114,6 +123,10 @@ int main(int argc, char** argv) fprintf(stderr, "failed: %s\n", e.what()); return 1; } + catch (XThread&) { + // terminated + return 1; + } } #endif diff --git a/mt/CThread.h b/mt/CThread.h index b6ebe1e3..ae49838c 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -1,7 +1,7 @@ #ifndef CTHREAD_H #define CTHREAD_H -#include "common.h" +#include "BasicTypes.h" class IJob; class CThreadRep; diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 8b48f6b8..4a0f99f4 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -40,6 +40,9 @@ static void threadDebug(int) CMutex* CThreadRep::s_mutex = NULL; CThreadRep* CThreadRep::s_head = NULL; +#if defined(CONFIG_PTHREADS) +pthread_t CThreadRep::s_signalThread; +#endif CThreadRep::CThreadRep() : m_prev(NULL), m_next(NULL), @@ -92,7 +95,14 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : // start the thread. throw if it doesn't start. #if defined(CONFIG_PTHREADS) + // mask some signals in all threads except the main thread + sigset_t sigset, oldsigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); int status = pthread_create(&m_thread, NULL, threadFunc, (void*)this); + pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); if (status != 0) throw XThreadUnavailable(); #elif defined(CONFIG_PLATFORM_WIN32) @@ -164,7 +174,22 @@ void CThreadRep::initThreads() pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); sigemptyset(&sigset); sigaddset(&sigset, SIGPIPE); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); pthread_sigmask(SIG_BLOCK, &sigset, NULL); + + // fire up the INT and TERM signal handler thread + int status = pthread_create(&s_signalThread, NULL, + &CThreadRep::threadSignalHandler, + getCurrentThreadRep()); + if (status != 0) { + // can't create thread to wait for signal so don't block + // the signals. + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + } #endif } } @@ -338,8 +363,9 @@ void CThreadRep::cancel() void CThreadRep::testCancel() { { - // prevent further cancellation CLock lock(s_mutex); + + // done if not cancelled, not cancellable, or already cancelling if (!m_cancel || !m_cancellable || m_cancelling) return; @@ -393,18 +419,6 @@ void* CThreadRep::threadFunc(void* arg) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); - // set signal mask - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGWAKEUP); -#ifndef NDEBUG - sigaddset(&sigset, SIGSEGV); -#endif - pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); - sigemptyset(&sigset); - sigaddset(&sigset, SIGPIPE); - pthread_sigmask(SIG_BLOCK, &sigset, NULL); - // run thread rep->doThreadFunc(); @@ -426,6 +440,27 @@ void CThreadRep::threadCancel(int) // do nothing } +void* CThreadRep::threadSignalHandler(void* vrep) +{ + CThreadRep* mainThreadRep = reinterpret_cast(vrep); + + // add signal to mask + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + + // we exit the loop via thread cancellation in sigwait() + for (;;) { + // wait + int signal; + sigwait(&sigset, &signal); + + // if we get here then the signal was raised. cancel the thread. + mainThreadRep->cancel(); + } +} + #elif defined(CONFIG_PLATFORM_WIN32) void CThreadRep::init() diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h index c0c7b9a0..7189752b 100644 --- a/mt/CThreadRep.h +++ b/mt/CThreadRep.h @@ -85,6 +85,7 @@ private: #if defined(CONFIG_PTHREADS) static void* threadFunc(void* arg); static void threadCancel(int); + static void* threadSignalHandler(void*); #elif defined(CONFIG_PLATFORM_WIN32) static unsigned int __stdcall threadFunc(void* arg); #endif @@ -107,11 +108,13 @@ private: void* m_result; bool m_cancellable; bool m_cancelling; + UInt32 m_signals; #if defined(CONFIG_PTHREADS) pthread_t m_thread; bool m_exit; bool m_cancel; + static pthread_t s_signalThread; #endif #if defined(CONFIG_PLATFORM_WIN32) diff --git a/server/CServer.cpp b/server/CServer.cpp index cc53826f..9b6686d9 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -7,18 +7,19 @@ #include "IPrimaryScreen.h" #include "ISocketFactory.h" #include "ProtocolTypes.h" -#include "XSynergy.h" #include "CNetworkAddress.h" #include "ISocket.h" #include "IListenSocket.h" -#include "XSocket.h" #include "CLock.h" +#include "CLog.h" #include "CThread.h" #include "CTimerThread.h" #include "CStopwatch.h" #include "CFunctionJob.h" #include "TMethodJob.h" -#include "CLog.h" +#include "XSocket.h" +#include "XSynergy.h" +#include "XThread.h" #include #include @@ -92,15 +93,26 @@ void CServer::run() log((CLOG_ERR "server error: %s", e.what())); // clean up + log((CLOG_NOTE "stopping server")); delete m_httpServer; m_httpServer = NULL; cleanupThreads(); closePrimaryScreen(); } + catch (XThread&) { + // clean up + log((CLOG_NOTE "stopping server")); + delete m_httpServer; + m_httpServer = NULL; + cleanupThreads(); + closePrimaryScreen(); + throw; + } catch (...) { log((CLOG_DEBUG "unknown server error")); // clean up + log((CLOG_NOTE "stopping server")); delete m_httpServer; m_httpServer = NULL; cleanupThreads(); diff --git a/server/server.cpp b/server/server.cpp index 56a15d0c..3cc4a081 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -4,6 +4,7 @@ #include "CMutex.h" #include "CNetwork.h" #include "CThread.h" +#include "XThread.h" #include "stdfstream.h" #include @@ -47,11 +48,12 @@ void realMain() s_logMutex = &logMutex; CLog::setLock(&logLock); - // initialize network library - CNetwork::init(); - CServer* server = NULL; try { + // initialize network library + CNetwork::init(); + + // load configuration CConfig config; { log((CLOG_DEBUG "opening configuration")); @@ -63,9 +65,12 @@ void realMain() log((CLOG_DEBUG "configuration read successfully")); } + // run server server = new CServer(); server->setConfig(config); server->run(); + + // clean up delete server; CNetwork::cleanup(); CLog::setLock(NULL); @@ -110,6 +115,10 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); return 1; } + catch (XThread&) { + // terminated + return 1; + } } #else @@ -132,6 +141,10 @@ int main(int argc, char** argv) fprintf(stderr, "failed: %s\n", e.what()); return 1; } + catch (XThread&) { + // terminated + return 1; + } } #endif From 869617a34a56779d4448ee648135a3b2b80289ed Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 Jun 2002 19:04:24 +0000 Subject: [PATCH 129/807] now ignores key if there's no key mapped for a required modifier. was asserting (on the wrong expression). --- client/CXWindowsSecondaryScreen.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 2adb1a81..68672a7e 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -98,8 +98,8 @@ void CXWindowsSecondaryScreen::open(CClient* client) // FIXME -- may have to get these from some database m_numLockHalfDuplex = false; m_capsLockHalfDuplex = false; -// m_numLockHalfDuplex = true; -// m_capsLockHalfDuplex = true; + m_numLockHalfDuplex = true; + m_capsLockHalfDuplex = true; // assume primary has all clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) @@ -434,14 +434,19 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( for (unsigned int i = 0; i < 8; ++i) { unsigned int bit = (1 << i); if ((outMask & bit) != (m_mask & bit)) { - // get list of keycodes for the modifier. there must - // be at least one. + // get list of keycodes for the modifier. if there isn't + // one then there's no key mapped to this modifier. + // we can't generate the desired key so bail. const KeyCode* modifierKeys = &m_modifierToKeycode[i * m_keysPerModifier]; KeyCode modifierKey = modifierKeys[0]; - if (modifierKey == 0) + if (modifierKey == 0) { modifierKey = modifierKeys[1]; - assert(modifierKeys[0] != 0); + } + if (modifierKey == 0) { + log((CLOG_DEBUG1 "no key mapped to modifier 0x%04x", bit)); + return m_mask; + } if (modifierKey != 0 && (outMask & bit) != 0) { // modifier is not active but should be. if the From a541ebc5571fec6ca53d6efc26bce1d7a3b4f6f3 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 Jun 2002 21:03:38 +0000 Subject: [PATCH 130/807] removed poll/sleep code to improve performance. --- synergy/CXWindowsClipboard.cpp | 60 ++++++++++++++++++++++++---------- synergy/CXWindowsClipboard.h | 4 +++ synergy/CXWindowsUtil.cpp | 8 ++--- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/synergy/CXWindowsClipboard.cpp b/synergy/CXWindowsClipboard.cpp index 09b713e7..8ab22899 100644 --- a/synergy/CXWindowsClipboard.cpp +++ b/synergy/CXWindowsClipboard.cpp @@ -1,8 +1,8 @@ #include "CXWindowsClipboard.h" #include "CXWindowsUtil.h" #include "CLog.h" -#include "CStopwatch.h" #include "CThread.h" +#include "TMethodJob.h" #include #include @@ -1099,6 +1099,9 @@ bool CXWindowsClipboard::CICCCMGetClipboard::readClipboard( *m_actualTarget = None; *m_data = ""; + // get timeout atom + m_timeout = XInternAtom(display, "SYNERGY_TIMEOUT", False); + // delete target property XDeleteProperty(display, m_requestor, m_property); @@ -1112,26 +1115,21 @@ bool CXWindowsClipboard::CICCCMGetClipboard::readClipboard( XConvertSelection(display, selection, target, m_property, m_requestor, m_time); - // process selection events. use a timeout so we don't get - // screwed by a bad selection owner. - CStopwatch timer(true); + // process selection events. have a separate thread send us an + // event after a timeout so we don't get locked up by badly + // behaved selection owners. + CThread timer(new TMethodJob( + this, + &CXWindowsClipboard::CICCCMGetClipboard::timeout, + display)); XEvent xevent; while (!m_done && !m_failed) { - // return false if we've timed-out - if (timer.getTime() >= 0.2) { - log((CLOG_DEBUG1 "request timed out")); - XSelectInput(display, m_requestor, attr.your_event_mask); - return false; - } - // process events - if (!XCheckIfEvent(display, &xevent, + XIfEvent(display, &xevent, &CXWindowsClipboard::CICCCMGetClipboard::eventPredicate, - reinterpret_cast(this))) { - // wait a bit to avoid spinning - CThread::sleep(0.05); - } + reinterpret_cast(this)); } + timer.cancel(); // restore mask XSelectInput(display, m_requestor, attr.your_event_mask); @@ -1188,6 +1186,16 @@ bool CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate( // otherwise not interested return false; + case ClientMessage: + // done if this is the timeout message + if (xevent->xclient.window == m_requestor && + xevent->xclient.message_type == m_timeout) { + return true; + } + + // otherwise not interested + return false; + default: // not interested return false; @@ -1200,7 +1208,7 @@ bool CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate( m_property, m_data, &target, NULL, True)) { // unable to read property m_failed = true; - return True; + return true; } // note if incremental. if we're already incremental then the @@ -1276,6 +1284,24 @@ Bool CXWindowsClipboard::CICCCMGetClipboard::eventPredicate( return self->doEventPredicate(display, xevent) ? True : False; } +void CXWindowsClipboard::CICCCMGetClipboard::timeout( + void* vdisplay) +{ + // wait + CThread::sleep(0.2); // FIXME -- is this too short? + + // send wake up + Display* display = reinterpret_cast(vdisplay); + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.display = display; + event.xclient.window = m_requestor; + event.xclient.message_type = m_timeout; + event.xclient.format = 8; + CXWindowsUtil::CErrorLock lock; + XSendEvent(display, m_requestor, False, 0, &event); +} + // // CXWindowsClipboard::CReply diff --git a/synergy/CXWindowsClipboard.h b/synergy/CXWindowsClipboard.h index 3cc4b3b9..9c04c9bc 100644 --- a/synergy/CXWindowsClipboard.h +++ b/synergy/CXWindowsClipboard.h @@ -106,6 +106,7 @@ private: static Bool eventPredicate(Display* display, XEvent* event, XPointer arg); + void timeout(void*); private: Window m_requestor; @@ -125,6 +126,9 @@ private: // selection owner cannot convert to the requested type. Atom* m_actualTarget; + // property used in event to wake up event loop + Atom m_timeout; + public: // true iff the selection owner didn't follow ICCCM conventions bool m_error; diff --git a/synergy/CXWindowsUtil.cpp b/synergy/CXWindowsUtil.cpp index ffc6a9f5..64b46a44 100644 --- a/synergy/CXWindowsUtil.cpp +++ b/synergy/CXWindowsUtil.cpp @@ -150,12 +150,8 @@ Time CXWindowsUtil::getCurrentTime( // wait for reply XEvent xevent; - while (XCheckIfEvent(display, &xevent, - &CXWindowsUtil::propertyNotifyPredicate, - (XPointer)&filter) != True) { - // wait a bit - CThread::sleep(0.05); - } + XIfEvent(display, &xevent, &CXWindowsUtil::propertyNotifyPredicate, + (XPointer)&filter); assert(xevent.type == PropertyNotify); assert(xevent.xproperty.window == window); assert(xevent.xproperty.atom == atom); From 1e988b3839ffbb6893d2f0caeb7b1ff77c59986e Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 Jun 2002 21:35:20 +0000 Subject: [PATCH 131/807] make sleep shorter in poll/sleep getEvent() loops. --- synergy/CMSWindowsScreen.cpp | 2 +- synergy/CXWindowsScreen.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/synergy/CMSWindowsScreen.cpp b/synergy/CMSWindowsScreen.cpp index ff2c2e39..7cd34a53 100644 --- a/synergy/CMSWindowsScreen.cpp +++ b/synergy/CMSWindowsScreen.cpp @@ -156,7 +156,7 @@ void CMSWindowsScreen::getEvent(MSG* msg) const { // wait for an event in a cancellable way while (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0) { - CThread::sleep(0.05); + CThread::sleep(0.01); } GetMessage(msg, NULL, 0, 0); } diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 33d716b1..5b73e9fc 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -141,7 +141,7 @@ bool CXWindowsScreen::getEvent(XEvent* xevent) const for (;;) { while (!m_stop && XPending(m_display) == 0) { m_mutex.unlock(); - CThread::sleep(0.05); + CThread::sleep(0.01); m_mutex.lock(); } if (m_stop) { From ad15393732bdb7ca31774ae3edb58b6b4c0f40e7 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 Jun 2002 22:57:50 +0000 Subject: [PATCH 132/807] changed buffered output stream to wait() when flush()ing instead of polling/sleeping. changed CTCPSocket to not use thread cancellation but to instead use m_connected to exit the thread. also shortened poll timeout. --- io/CBufferedOutputStream.cpp | 16 ++++++------- io/CBufferedOutputStream.h | 5 ++-- net/CTCPSocket.cpp | 45 +++++++++++++++++++----------------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/io/CBufferedOutputStream.cpp b/io/CBufferedOutputStream.cpp index 387ce406..3acd168b 100644 --- a/io/CBufferedOutputStream.cpp +++ b/io/CBufferedOutputStream.cpp @@ -13,6 +13,7 @@ CBufferedOutputStream::CBufferedOutputStream(CMutex* mutex, IJob* closeCB) : m_mutex(mutex), m_closeCB(closeCB), + m_empty(mutex, true), m_closed(false) { assert(m_mutex != NULL); @@ -31,6 +32,9 @@ const void* CBufferedOutputStream::peek(UInt32 n) void CBufferedOutputStream::pop(UInt32 n) { m_buffer.pop(n); + if (m_buffer.getSize() == 0) { + m_empty.broadcast(); + } } UInt32 CBufferedOutputStream::getSize() const @@ -67,14 +71,8 @@ UInt32 CBufferedOutputStream::write( void CBufferedOutputStream::flush() { // wait until all data is written - while (getSizeWithLock() > 0) { - CThread::sleep(0.05); + CLock lock(m_mutex); + while (m_buffer.getSize() > 0) { + m_empty.wait(); } } - -UInt32 CBufferedOutputStream::getSizeWithLock() const -{ - CLock lock(m_mutex); - return m_buffer.getSize(); -} - diff --git a/io/CBufferedOutputStream.h b/io/CBufferedOutputStream.h index 1afbc118..c609336e 100644 --- a/io/CBufferedOutputStream.h +++ b/io/CBufferedOutputStream.h @@ -3,6 +3,7 @@ #include "CStreamBuffer.h" #include "IOutputStream.h" +#include "CCondVar.h" class CMutex; class IJob; @@ -33,12 +34,10 @@ public: virtual UInt32 write(const void*, UInt32 count); virtual void flush(); -private: - UInt32 getSizeWithLock() const; - private: CMutex* m_mutex; IJob* m_closeCB; + CCondVar m_empty; CStreamBuffer m_buffer; bool m_closed; }; diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index 989a9668..3429c9f8 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -80,25 +80,35 @@ void CTCPSocket::connect(const CNetworkAddress& addr) void CTCPSocket::close() { - // shutdown I/O thread before close - if (m_thread != NULL) { - // flush if output buffer not empty and output buffer not closed - bool doFlush; - { - CLock lock(m_mutex); - doFlush = ((m_connected & kWrite) != 0); - } - if (doFlush) { - m_output->flush(); - } + // see if buffers should be flushed + bool doFlush = false; + { + CLock lock(m_mutex); + doFlush = (m_thread != NULL && (m_connected & kWrite) != 0); + } - m_thread->cancel(); + // flush buffers + if (doFlush) { + m_output->flush(); + } + + // cause ioThread to exit + { + CLock lock(m_mutex); + if (m_fd != CNetwork::Null) { + CNetwork::shutdown(m_fd, 2); + m_connected = kClosed; + } + } + + // wait for thread + if (m_thread != NULL) { m_thread->wait(); delete m_thread; m_thread = NULL; } - CLock lock(m_mutex); + // close socket if (m_fd != CNetwork::Null) { if (CNetwork::close(m_fd) == CNetwork::Error) { throw XIOClose(); @@ -190,14 +200,7 @@ void CTCPSocket::ioService() } // check for status - CThread::testCancel(); - if (pfds[0].events == 0) { - CThread::sleep(0.05); - CThread::testCancel(); - continue; - } - const int status = CNetwork::poll(pfds, 1, 50); - CThread::testCancel(); + const int status = CNetwork::poll(pfds, 1, 10); // transfer data and handle errors if (status == 1) { From ddbb465540fdf86136331262b4c644397b6174d7 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 Jun 2002 23:07:57 +0000 Subject: [PATCH 133/807] shortened poll() timeout. --- net/CTCPListenSocket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/CTCPListenSocket.cpp b/net/CTCPListenSocket.cpp index 9dab306a..28e254c9 100644 --- a/net/CTCPListenSocket.cpp +++ b/net/CTCPListenSocket.cpp @@ -47,7 +47,7 @@ ISocket* CTCPListenSocket::accept() pfds[0].events = CNetwork::kPOLLIN; for (;;) { CThread::testCancel(); - const int status = CNetwork::poll(pfds, 1, 50); + const int status = CNetwork::poll(pfds, 1, 10); if (status > 0 && (pfds[0].revents & CNetwork::kPOLLIN) != 0) { CNetwork::Address addr; CNetwork::AddressLength addrlen = sizeof(addr); From 1cbdaee31b8af80c334670bff11cc6a88f0f800d Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 3 Jun 2002 13:45:30 +0000 Subject: [PATCH 134/807] added better handling of X server disconnecting unexpectedly. the apps still exit but they do it in a mostly controlled manner. in particular, the server threads except the one processing primary screen events will terminate gracefully. this will be important should the server ever allow HTTP clients to rewrite the configuration file. note that X makes it effectively impossible to continue once the X server disconnects. even if it didn't it would be difficult for synergy to recover. users will have to add synergy to the X display manager's startup script if they expect the server to be restarted. alternatively, we could add code to fork synergy at startup; the child would do the normal work while the parent would simply wait for the child to exit and restart it. --- client/CClient.cpp | 20 ++++- client/CXWindowsSecondaryScreen.cpp | 19 ++--- client/CXWindowsSecondaryScreen.h | 4 +- server/CServer.cpp | 121 +++++++++++++++++++++------- server/CServer.h | 8 +- server/CXWindowsPrimaryScreen.cpp | 26 +++--- server/CXWindowsPrimaryScreen.h | 5 +- synergy/CXWindowsScreen.cpp | 52 +++++++++--- synergy/CXWindowsScreen.h | 24 ++++-- synergy/Makefile | 1 + synergy/XScreen.cpp | 10 +++ synergy/XScreen.h | 14 ++++ synergy/synergy.dsp | 8 ++ 13 files changed, 242 insertions(+), 70 deletions(-) create mode 100644 synergy/XScreen.cpp create mode 100644 synergy/XScreen.h diff --git a/client/CClient.cpp b/client/CClient.cpp index de4e7de0..a5178e43 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -10,6 +10,7 @@ #include "CThread.h" #include "CTimerThread.h" #include "TMethodJob.h" +#include "XScreen.h" #include "XSynergy.h" #include "XThread.h" #include @@ -51,7 +52,16 @@ void CClient::run(const CNetworkAddress& serverAddress) log((CLOG_NOTE "starting client")); // connect to secondary screen - openSecondaryScreen(); + while (m_screen == NULL) { + try { + openSecondaryScreen(); + } + catch (XScreenOpenFailure&) { + // can't open screen yet. wait a few seconds to retry. + log((CLOG_INFO "failed to open screen. waiting to retry.")); + CThread::sleep(3.0); + } + } // start server interactions m_serverAddress = &serverAddress; @@ -88,7 +98,9 @@ void CClient::run(const CNetworkAddress& serverAddress) thread->wait(); delete thread; } - closeSecondaryScreen(); + if (m_screen != NULL) { + closeSecondaryScreen(); + } throw; } catch (...) { @@ -101,7 +113,9 @@ void CClient::run(const CNetworkAddress& serverAddress) thread->wait(); delete thread; } - closeSecondaryScreen(); + if (m_screen != NULL) { + closeSecondaryScreen(); + } throw; } } diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 68672a7e..e3e3aa97 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -290,12 +290,11 @@ void CXWindowsSecondaryScreen::getClipboard( getDisplayClipboard(id, clipboard); } -void CXWindowsSecondaryScreen::onOpenDisplay() +void CXWindowsSecondaryScreen::onOpenDisplay( + Display* display) { assert(m_window == None); - CDisplayLock display(this); - // create the cursor hiding window. this window is used to hide the // cursor when it's not on the screen. the window is hidden as soon // as the cursor enters the screen or the display's real cursor is @@ -326,16 +325,18 @@ CXWindowsClipboard* CXWindowsSecondaryScreen::createClipboard( return new CXWindowsClipboard(display, m_window, id); } -void CXWindowsSecondaryScreen::onCloseDisplay() +void CXWindowsSecondaryScreen::onCloseDisplay( + Display* display) { assert(m_window != None); - // no longer impervious to server grabs - CDisplayLock display(this); - XTestGrabControl(display, False); + if (display != NULL) { + // no longer impervious to server grabs + XTestGrabControl(display, False); - // destroy window - XDestroyWindow(display, m_window); + // destroy window + XDestroyWindow(display, m_window); + } m_window = None; } diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 2617096c..d4a00a48 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -35,10 +35,10 @@ public: protected: // CXWindowsScreen overrides - virtual void onOpenDisplay(); + virtual void onOpenDisplay(Display*); virtual CXWindowsClipboard* createClipboard(ClipboardID); - virtual void onCloseDisplay(); + virtual void onCloseDisplay(Display*); virtual void onLostClipboard(ClipboardID); private: diff --git a/server/CServer.cpp b/server/CServer.cpp index 9b6686d9..02ed0bd1 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -17,6 +17,7 @@ #include "CStopwatch.h" #include "CFunctionJob.h" #include "TMethodJob.h" +#include "XScreen.h" #include "XSocket.h" #include "XSynergy.h" #include "XThread.h" @@ -46,7 +47,8 @@ else { wait(0); exit(1); } const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; -CServer::CServer() : m_primary(NULL), +CServer::CServer() : m_cleanupSize(&m_mutex, 0), + m_primary(NULL), m_active(NULL), m_primaryInfo(NULL), m_seqNum(0), @@ -69,7 +71,16 @@ void CServer::run() log((CLOG_NOTE "starting server")); // connect to primary screen - openPrimaryScreen(); + while (m_primary == NULL) { + try { + openPrimaryScreen(); + } + catch (XScreenOpenFailure&) { + // can't open screen yet. wait a few seconds to retry. + log((CLOG_INFO "failed to open screen. waiting to retry.")); + CThread::sleep(3.0); + } + } // start listening for HTTP requests m_httpServer = new CHTTPServer(this); @@ -84,9 +95,9 @@ void CServer::run() // clean up log((CLOG_NOTE "stopping server")); + cleanupThreads(); delete m_httpServer; m_httpServer = NULL; - cleanupThreads(); closePrimaryScreen(); } catch (XBase& e) { @@ -94,18 +105,20 @@ void CServer::run() // clean up log((CLOG_NOTE "stopping server")); + cleanupThreads(); delete m_httpServer; m_httpServer = NULL; - cleanupThreads(); closePrimaryScreen(); } catch (XThread&) { // clean up log((CLOG_NOTE "stopping server")); + cleanupThreads(); delete m_httpServer; m_httpServer = NULL; - cleanupThreads(); - closePrimaryScreen(); + if (m_primary != NULL) { + closePrimaryScreen(); + } throw; } catch (...) { @@ -113,10 +126,12 @@ void CServer::run() // clean up log((CLOG_NOTE "stopping server")); + cleanupThreads(); delete m_httpServer; m_httpServer = NULL; - cleanupThreads(); - closePrimaryScreen(); + if (m_primary != NULL) { + closePrimaryScreen(); + } throw; } } @@ -126,6 +141,19 @@ void CServer::quit() m_primary->stop(); } +void CServer::shutdown() +{ + // stop all running threads but don't wait too long since some + // threads may be unable to proceed until this thread returns. + cleanupThreads(3.0); + + // done with the HTTP server + delete m_httpServer; + m_httpServer = NULL; + + // note -- we do not attempt to close down the primary screen +} + bool CServer::setConfig(const CConfig& config) { typedef std::vector CThreads; @@ -1289,19 +1317,29 @@ void CServer::openPrimaryScreen() // reset sequence number m_seqNum = 0; - // add connection - m_active = addConnection(CString("primary"/* FIXME */), NULL); - m_primaryInfo = m_active; + try { + // add connection + m_active = addConnection(CString("primary"/* FIXME */), NULL); + m_primaryInfo = m_active; - // open screen - log((CLOG_DEBUG1 "creating primary screen")); + // open screen + log((CLOG_DEBUG1 "creating primary screen")); #if defined(CONFIG_PLATFORM_WIN32) - m_primary = new CMSWindowsPrimaryScreen; + m_primary = new CMSWindowsPrimaryScreen; #elif defined(CONFIG_PLATFORM_UNIX) - m_primary = new CXWindowsPrimaryScreen; + m_primary = new CXWindowsPrimaryScreen; #endif - log((CLOG_DEBUG1 "opening primary screen")); - m_primary->open(this); + log((CLOG_DEBUG1 "opening primary screen")); + m_primary->open(this); + } + catch (...) { + delete m_primary; + removeConnection(CString("primary"/* FIXME */)); + m_primary = NULL; + m_primaryInfo = NULL; + m_active = NULL; + throw; + } // set the clipboard owner to the primary screen and then get the // current clipboard data. @@ -1340,6 +1378,7 @@ void CServer::addCleanupThread(const CThread& thread) { CLock lock(&m_mutex); m_cleanupList.insert(m_cleanupList.begin(), new CThread(thread)); + m_cleanupSize = m_cleanupSize + 1; } void CServer::removeCleanupThread(const CThread& thread) @@ -1350,30 +1389,52 @@ void CServer::removeCleanupThread(const CThread& thread) if (**index == thread) { CThread* thread = *index; m_cleanupList.erase(index); + m_cleanupSize = m_cleanupSize - 1; + if (m_cleanupSize == 0) { + m_cleanupSize.broadcast(); + } delete thread; return; } } } -void CServer::cleanupThreads() +void CServer::cleanupThreads(double timeout) { log((CLOG_DEBUG1 "cleaning up threads")); - m_mutex.lock(); - while (m_cleanupList.begin() != m_cleanupList.end()) { - // get the next thread and cancel it - CThread* thread = m_cleanupList.front(); - thread->cancel(); - // wait for thread to finish with cleanup list unlocked. the - // thread will remove itself from the cleanup list. - m_mutex.unlock(); - thread->wait(); - m_mutex.lock(); + // first cancel every thread except the current one (with mutex + // locked so the cleanup list won't change). + CLock lock(&m_mutex); + CThread current(CThread::getCurrentThread()); + SInt32 minCount = 0; + for (CThreadList::iterator index = m_cleanupList.begin(); + index != m_cleanupList.end(); ++index) { + CThread* thread = *index; + if (thread != ¤t) { + thread->cancel(); + } + else { + minCount = 1; + } } - // FIXME -- delete remaining threads from list - m_mutex.unlock(); + // now wait for the threads (with mutex unlocked as each thread + // will remove itself from the list) + CStopwatch timer(true); + while (m_cleanupSize > minCount) { + m_cleanupSize.wait(timer, timeout); + } + + // delete remaining threads + for (CThreadList::iterator index = m_cleanupList.begin(); + index != m_cleanupList.end(); ++index) { + CThread* thread = *index; + delete thread; + } + m_cleanupList.clear(); + m_cleanupSize = 0; + log((CLOG_DEBUG1 "cleaned up threads")); } diff --git a/server/CServer.h b/server/CServer.h index d05bc867..37b1eb8f 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -34,6 +34,11 @@ public: // tell server to exit gracefully void quit(); + // tell the server to shutdown. this is called in an emergency + // when we need to tell the server that we cannot continue. the + // server will attempt to clean up. + void shutdown(); + // update screen map. returns true iff the new configuration was // accepted. bool setConfig(const CConfig&); @@ -173,7 +178,7 @@ private: void updatePrimaryClipboard(ClipboardID); // cancel running threads - void cleanupThreads(); + void cleanupThreads(double timeout = -1.0); // thread method to accept incoming client connections void acceptClients(void*); @@ -220,6 +225,7 @@ private: ISecurityFactory* m_securityFactory; CThreadList m_cleanupList; + CCondVar m_cleanupSize; IPrimaryScreen* m_primary; CScreenList m_screens; diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 9d5a828b..9fe5d801 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -179,6 +179,7 @@ void CXWindowsPrimaryScreen::run() void CXWindowsPrimaryScreen::stop() { + CDisplayLock display(this); doStop(); } @@ -187,12 +188,12 @@ void CXWindowsPrimaryScreen::open(CServer* server) assert(m_server == NULL); assert(server != NULL); - // set the server - m_server = server; - // open the display openDisplay(); + // set the server + m_server = server; + // check for peculiarities // FIXME -- may have to get these from some database m_numLockHalfDuplex = false; @@ -419,12 +420,10 @@ bool CXWindowsPrimaryScreen::isLockedToScreen() const return false; } -void CXWindowsPrimaryScreen::onOpenDisplay() +void CXWindowsPrimaryScreen::onOpenDisplay(Display* display) { assert(m_window == None); - CDisplayLock display(this); - // get size of screen SInt32 w, h; getScreenSize(&w, &h); @@ -458,16 +457,25 @@ CXWindowsClipboard* CXWindowsPrimaryScreen::createClipboard( return new CXWindowsClipboard(display, m_window, id); } -void CXWindowsPrimaryScreen::onCloseDisplay() +void CXWindowsPrimaryScreen::onCloseDisplay(Display* display) { assert(m_window != None); // destroy window - CDisplayLock display(this); - XDestroyWindow(display, m_window); + if (display != NULL) { + XDestroyWindow(display, m_window); + } m_window = None; } +void CXWindowsPrimaryScreen::onUnexpectedClose() +{ + // tell server to shutdown + if (m_server != NULL) { + m_server->shutdown(); + } +} + void CXWindowsPrimaryScreen::onLostClipboard( ClipboardID id) { diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 05c53a30..0590deaa 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -30,10 +30,11 @@ public: protected: // CXWindowsScreen overrides - virtual void onOpenDisplay(); + virtual void onOpenDisplay(Display*); virtual CXWindowsClipboard* createClipboard(ClipboardID); - virtual void onCloseDisplay(); + virtual void onCloseDisplay(Display*); + virtual void onUnexpectedClose(); virtual void onLostClipboard(ClipboardID); private: diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 5b73e9fc..25602a4a 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -6,6 +6,8 @@ #include "CLog.h" #include "CString.h" #include "CThread.h" +#include "XScreen.h" +#include #include #include @@ -13,29 +15,38 @@ // CXWindowsScreen // +CXWindowsScreen* CXWindowsScreen::s_screen = NULL; + CXWindowsScreen::CXWindowsScreen() : m_display(NULL), m_root(None), m_w(0), m_h(0), m_stop(false) { - // do nothing + assert(s_screen == NULL); + s_screen = this; } CXWindowsScreen::~CXWindowsScreen() { + assert(s_screen != NULL); assert(m_display == NULL); + + s_screen = NULL; } void CXWindowsScreen::openDisplay() { assert(m_display == NULL); + // set the X I/O error handler so we catch the display disconnecting + XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); + // open the display log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); m_display = XOpenDisplay(NULL); // FIXME -- allow non-default if (m_display == NULL) - throw int(5); // FIXME -- make exception for this + throw XScreenOpenFailure(); // get default screen m_screen = DefaultScreen(m_display); @@ -50,7 +61,7 @@ void CXWindowsScreen::openDisplay() m_root = RootWindow(m_display, m_screen); // let subclass prep display - onOpenDisplay(); + onOpenDisplay(m_display); // initialize clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) { @@ -60,10 +71,10 @@ void CXWindowsScreen::openDisplay() void CXWindowsScreen::closeDisplay() { - assert(m_display != NULL); + CLock lock(&m_mutex); // let subclass close down display - onCloseDisplay(); + onCloseDisplay(m_display); // destroy clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) { @@ -71,9 +82,12 @@ void CXWindowsScreen::closeDisplay() } // close the display - XCloseDisplay(m_display); - m_display = NULL; - log((CLOG_DEBUG "closed display")); + if (m_display != NULL) { + XCloseDisplay(m_display); + m_display = NULL; + log((CLOG_DEBUG "closed display")); + } + XSetIOErrorHandler(NULL); } int CXWindowsScreen::getScreen() const @@ -164,7 +178,7 @@ bool CXWindowsScreen::getEvent(XEvent* xevent) const void CXWindowsScreen::doStop() { - CLock lock(&m_mutex); + // caller must have locked display m_stop = true; } @@ -179,6 +193,11 @@ ClipboardID CXWindowsScreen::getClipboardID(Atom selection) const return kClipboardEnd; } +void CXWindowsScreen::onUnexpectedClose() +{ + // do nothing +} + bool CXWindowsScreen::processEvent(XEvent* xevent) { switch (xevent->type) { @@ -326,6 +345,21 @@ void CXWindowsScreen::destroyClipboardRequest( } } +int CXWindowsScreen::ioErrorHandler(Display*) +{ + // the display has disconnected, probably because X is shutting + // down. X forces us to exit at this point. that's arguably + // a flaw in X but, realistically, it's difficult to gracefully + // handle not having a Display* anymore. we'll simply log the + // error, notify the subclass (which must not use the display + // so we set it to NULL), and exit. + log((CLOG_WARN "X display has unexpectedly disconnected")); + s_screen->m_display = NULL; + s_screen->onUnexpectedClose(); + log((CLOG_CRIT "quiting due to X display disconnection")); + exit(1); +} + // // CXWindowsScreen::CDisplayLock diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index b2a675ed..a100d22a 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -52,7 +52,8 @@ protected: // wait for and get the next X event. cancellable. bool getEvent(XEvent*) const; - // cause getEvent() to return false immediately and forever after + // cause getEvent() to return false immediately and forever after. + // the caller must have locked the display. void doStop(); // set the contents of the clipboard (i.e. primary selection) @@ -63,15 +64,21 @@ protected: bool getDisplayClipboard(ClipboardID, IClipboard* clipboard) const; - // called by openDisplay() to allow subclasses to prepare the display - virtual void onOpenDisplay() = 0; + // called by openDisplay() to allow subclasses to prepare the display. + // the display is locked and passed to the subclass. + virtual void onOpenDisplay(Display*) = 0; // called by openDisplay() after onOpenDisplay() to create each clipboard virtual CXWindowsClipboard* createClipboard(ClipboardID) = 0; - // called by closeDisplay() to - virtual void onCloseDisplay() = 0; + // called by closeDisplay() to allow subclasses to clean up the display. + // the display is locked and passed to the subclass. note that the + // display may be NULL if the display has unexpectedly disconnected. + virtual void onCloseDisplay(Display*) = 0; + + // called if the display is unexpectedly closing. default does nothing. + virtual void onUnexpectedClose(); // called when a clipboard is lost virtual void onLostClipboard(ClipboardID) = 0; @@ -91,6 +98,9 @@ private: // terminate a selection request void destroyClipboardRequest(Window window); + // X I/O error handler + static int ioErrorHandler(Display*); + private: Display* m_display; int m_screen; @@ -103,6 +113,10 @@ private: // X is not thread safe CMutex m_mutex; + + // pointer to (singleton) screen. this is only needed by + // ioErrorHandler(). + static CXWindowsScreen* s_screen; }; #endif diff --git a/synergy/Makefile b/synergy/Makefile index 3d499dcd..c09c122d 100644 --- a/synergy/Makefile +++ b/synergy/Makefile @@ -24,6 +24,7 @@ CXXFILES = \ CXWindowsClipboard.cpp \ CXWindowsScreen.cpp \ CXWindowsUtil.cpp \ + XScreen.cpp \ XSynergy.cpp \ $(NULL) diff --git a/synergy/XScreen.cpp b/synergy/XScreen.cpp new file mode 100644 index 00000000..4466525f --- /dev/null +++ b/synergy/XScreen.cpp @@ -0,0 +1,10 @@ +#include "XScreen.h" + +// +// XScreenOpenFailure +// + +CString XScreenOpenFailure::getWhat() const throw() +{ + return "XScreenOpenFailure"; +} diff --git a/synergy/XScreen.h b/synergy/XScreen.h new file mode 100644 index 00000000..23ea45e5 --- /dev/null +++ b/synergy/XScreen.h @@ -0,0 +1,14 @@ +#ifndef XSCREEN_H +#define XSCREEN_H + +#include "XBase.h" + +class XScreen : public XBase { }; + +// screen cannot be opened +class XScreenOpenFailure : public XScreen { +protected: + virtual CString getWhat() const throw(); +}; + +#endif diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp index a14a9587..cf76d74a 100644 --- a/synergy/synergy.dsp +++ b/synergy/synergy.dsp @@ -115,6 +115,10 @@ SOURCE=.\CTCPSocketFactory.cpp # End Source File # Begin Source File +SOURCE=.\XScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\XSynergy.cpp # End Source File # End Group @@ -187,6 +191,10 @@ SOURCE=.\ProtocolTypes.h # End Source File # Begin Source File +SOURCE=.\XScreen.h +# End Source File +# Begin Source File + SOURCE=.\XSynergy.h # End Source File # End Group From 014b781fb0a4b1858240b346a7910539952e7f9f Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 3 Jun 2002 16:34:22 +0000 Subject: [PATCH 135/807] moved case insensitive comparison utility functions into CString from CHTTPProtocol. --- base/CString.cpp | 47 ++++++++++++++++++++++++++++++ base/CString.h | 14 +++++++++ base/Makefile | 1 + base/base.dsp | 4 +++ http/CHTTPProtocol.cpp | 65 ++++++------------------------------------ http/CHTTPProtocol.h | 16 +---------- 6 files changed, 76 insertions(+), 71 deletions(-) create mode 100644 base/CString.cpp diff --git a/base/CString.cpp b/base/CString.cpp new file mode 100644 index 00000000..e174cad0 --- /dev/null +++ b/base/CString.cpp @@ -0,0 +1,47 @@ +#include "CString.h" +#include +#include + +// +// CStringUtil::CaselessCmp +// + +bool CStringUtil::CaselessCmp::cmpEqual( + const CString::value_type& a, + const CString::value_type& b) +{ + // FIXME -- use std::tolower + return tolower(a) == tolower(b); +} + +bool CStringUtil::CaselessCmp::cmpLess( + const CString::value_type& a, + const CString::value_type& b) +{ + // FIXME -- use std::tolower + return tolower(a) < tolower(b); +} + +bool CStringUtil::CaselessCmp::less( + const CString& a, + const CString& b) +{ + return std::lexicographical_compare( + a.begin(), a.end(), + b.begin(), b.end(), + &CStringUtil::CaselessCmp::cmpLess); +} + +bool CStringUtil::CaselessCmp::equal( + const CString& a, + const CString& b) +{ + return !(less(a, b) || less(b, a)); +} + +bool CStringUtil::CaselessCmp::operator()( + const CString& a, + const CString& b) const +{ + return less(a, b); +} diff --git a/base/CString.h b/base/CString.h index dc0a15f4..10664e30 100644 --- a/base/CString.h +++ b/base/CString.h @@ -44,6 +44,20 @@ public: _Myt(_f, _l CSTRING_ALLOC2) { } }; +class CStringUtil { +public: + class CaselessCmp { + public: + bool operator()(const CString&, const CString&) const; + static bool less(const CString&, const CString&); + static bool equal(const CString&, const CString&); + static bool cmpLess(const CString::value_type&, + const CString::value_type&); + static bool cmpEqual(const CString::value_type&, + const CString::value_type&); + }; +}; + #if defined(_MSC_VER) #pragma warning(pop) #endif diff --git a/base/Makefile b/base/Makefile index 8e262bd4..dcb324f4 100644 --- a/base/Makefile +++ b/base/Makefile @@ -16,6 +16,7 @@ CXXFILES = \ CLog.cpp \ CFunctionJob.cpp \ CStopwatch.cpp \ + CString.cpp \ $(NULL) targets: $(LIBTARGET) diff --git a/base/base.dsp b/base/base.dsp index fa7569aa..ef498104 100644 --- a/base/base.dsp +++ b/base/base.dsp @@ -99,6 +99,10 @@ SOURCE=.\CStopwatch.cpp # End Source File # Begin Source File +SOURCE=.\CString.cpp +# End Source File +# Begin Source File + SOURCE=.\XBase.cpp # End Source File # End Group diff --git a/http/CHTTPProtocol.cpp b/http/CHTTPProtocol.cpp index 0d00fb87..d1bf09e3 100644 --- a/http/CHTTPProtocol.cpp +++ b/http/CHTTPProtocol.cpp @@ -5,57 +5,10 @@ #include "IOutputStream.h" #include "stdsstream.h" #include -#include #include #include #include -// -// CHTTPUtil::CaselessCmp -// - -inline -bool CHTTPUtil::CaselessCmp::cmpEqual( - const CString::value_type& a, - const CString::value_type& b) -{ - // FIXME -- use std::tolower - return tolower(a) == tolower(b); -} - -inline -bool CHTTPUtil::CaselessCmp::cmpLess( - const CString::value_type& a, - const CString::value_type& b) -{ - // FIXME -- use std::tolower - return tolower(a) < tolower(b); -} - -bool CHTTPUtil::CaselessCmp::less( - const CString& a, - const CString& b) -{ - return std::lexicographical_compare( - a.begin(), a.end(), - b.begin(), b.end(), - &CHTTPUtil::CaselessCmp::cmpLess); -} - -bool CHTTPUtil::CaselessCmp::equal( - const CString& a, - const CString& b) -{ - return !(less(a, b) || less(b, a)); -} - -bool CHTTPUtil::CaselessCmp::operator()( - const CString& a, - const CString& b) const -{ - return less(a, b); -} - // // CHTTPRequest // @@ -217,7 +170,7 @@ CHTTPRequest* CHTTPProtocol::readRequest( CString header; if (!(header = request->getHeader("Transfer-Encoding")).empty()) { // we only understand "chunked" encodings - if (!CHTTPUtil::CaselessCmp::equal(header, "chunked")) { + if (!CStringUtil::CaselessCmp::equal(header, "chunked")) { log((CLOG_DEBUG1 "unsupported Transfer-Encoding %s", header.c_str())); throw XHTTP(501); } @@ -295,9 +248,9 @@ void CHTTPProtocol::reply( const CString& header = index->first; // remove certain headers - if (CHTTPUtil::CaselessCmp::equal(header, "Content-Length") || - CHTTPUtil::CaselessCmp::equal(header, "Date") || - CHTTPUtil::CaselessCmp::equal(header, "Transfer-Encoding")) { + if (CStringUtil::CaselessCmp::equal(header, "Content-Length") || + CStringUtil::CaselessCmp::equal(header, "Date") || + CStringUtil::CaselessCmp::equal(header, "Transfer-Encoding")) { // FIXME -- Transfer-Encoding should be left as-is if // not "chunked" and if the version is 1.1 or up. index = reply.m_headers.erase(index); @@ -376,7 +329,7 @@ bool CHTTPProtocol::parseFormData( CString::const_iterator index = std::search( contentType.begin(), contentType.end(), formData, formData + sizeof(formData) - 1, - CHTTPUtil::CaselessCmp::cmpEqual); + CStringUtil::CaselessCmp::cmpEqual); if (index == contentType.end()) { // not form-data return false; @@ -384,7 +337,7 @@ bool CHTTPProtocol::parseFormData( index += sizeof(formData) - 1; index = std::search(index, contentType.end(), boundary, boundary + sizeof(boundary) - 1, - CHTTPUtil::CaselessCmp::cmpEqual); + CStringUtil::CaselessCmp::cmpEqual); if (index == contentType.end()) { // no boundary return false; @@ -436,7 +389,7 @@ bool CHTTPProtocol::parseFormData( body.begin() + endOfHeaders, disposition, disposition + sizeof(disposition) - 1, - CHTTPUtil::CaselessCmp::cmpEqual); + CStringUtil::CaselessCmp::cmpEqual); if (index == contentType.begin() + endOfHeaders) { // bad part return false; @@ -451,7 +404,7 @@ bool CHTTPProtocol::parseFormData( } index = std::search(index, body.begin() + endOfHeader, nameAttr, nameAttr + sizeof(nameAttr) - 1, - CHTTPUtil::CaselessCmp::cmpEqual); + CStringUtil::CaselessCmp::cmpEqual); if (index == body.begin() + endOfHeader) { // no name return false; @@ -466,7 +419,7 @@ bool CHTTPProtocol::parseFormData( CString::size_type namePos = index - body.begin(); index = std::search(index, body.begin() + endOfHeader, quote, quote + 1, - CHTTPUtil::CaselessCmp::cmpEqual); + CStringUtil::CaselessCmp::cmpEqual); if (index == body.begin() + endOfHeader) { // missing close quote return false; diff --git a/http/CHTTPProtocol.h b/http/CHTTPProtocol.h index 316d1fd4..19818b95 100644 --- a/http/CHTTPProtocol.h +++ b/http/CHTTPProtocol.h @@ -10,25 +10,11 @@ class IInputStream; class IOutputStream; -class CHTTPUtil { -public: - class CaselessCmp { - public: - bool operator()(const CString&, const CString&) const; - static bool less(const CString&, const CString&); - static bool equal(const CString&, const CString&); - static bool cmpLess(const CString::value_type&, - const CString::value_type&); - static bool cmpEqual(const CString::value_type&, - const CString::value_type&); - }; -}; - class CHTTPRequest { public: typedef std::list > CHeaderList; typedef std::map CHeaderMap; + CStringUtil::CaselessCmp> CHeaderMap; typedef CHeaderList::const_iterator const_iterator; CHTTPRequest(); From 10f4e9455763973a37acedc2cccae144e5874a48 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 3 Jun 2002 16:36:45 +0000 Subject: [PATCH 136/807] added a method to set the filter given a priority string (instead of a number). fixed a comment related to what those priority strings are. added a CLOG_PRINT priority which is never filtered and suppresses the trace info and the priority level message. it's intended as a way to output a message through the logger without getting extra output. --- base/CLog.cpp | 41 ++++++++++++++++++++++++++++------------- base/CLog.h | 6 +++++- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index 145a2a2c..a9098c8e 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -96,6 +96,11 @@ void CLog::printt(const char* file, int line, sprintf(buffer + g_priorityPad, "%s,%d:", file, line); buffer[pad - 1] = ' '; + // discard file and line if priority < 0 + if (priority < 0) { + buffer += pad - g_priorityPad; + } + // output buffer output(priority, buffer); @@ -128,6 +133,20 @@ CLog::Lock CLog::getLock() return (s_lock == dummyLock) ? NULL : s_lock; } +bool CLog::setFilter(const char* maxPriority) +{ + if (maxPriority != NULL) { + for (int i = 0; i < g_numPriority; ++i) { + if (strcmp(maxPriority, g_priority[i]) == 0) { + setFilter(i); + return true; + } + } + return false; + } + return true; +} + void CLog::setFilter(int maxPriority) { CHoldLock lock(s_lock); @@ -151,15 +170,7 @@ int CLog::getMaxPriority() if (s_maxPriority == -1) { s_maxPriority = g_defaultMaxPriority; - const char* priEnv = getenv("SYN_LOG_PRI"); - if (priEnv != NULL) { - for (int i = 0; i < g_numPriority; ++i) { - if (strcmp(priEnv, g_priority[i]) == 0) { - s_maxPriority = i; - break; - } - } - } + setFilter(getenv("SYN_LOG_PRI")); } return s_maxPriority; @@ -167,14 +178,18 @@ int CLog::getMaxPriority() void CLog::output(int priority, char* msg) { - assert(priority >= 0 && priority < g_numPriority); + assert(priority >= -1 && priority < g_numPriority); assert(msg != 0); if (priority <= getMaxPriority()) { // insert priority label - int n = strlen(g_priority[priority]); - sprintf(msg + g_maxPriorityLength - n, "%s:", g_priority[priority]); - msg[g_maxPriorityLength + 1] = ' '; + int n = -g_prioritySuffixLength; + if (priority >= 0) { + n = strlen(g_priority[priority]); + sprintf(msg + g_maxPriorityLength - n, + "%s:", g_priority[priority]); + msg[g_maxPriorityLength + 1] = ' '; + } // put a newline at the end #if defined(CONFIG_PLATFORM_WIN32) diff --git a/base/CLog.h b/base/CLog.h index 63d97174..6a1189bd 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -30,7 +30,10 @@ public: // priority is discarded. the default priority is 4 (INFO) // (unless built without NDEBUG in which case it's 5 (DEBUG)). // the default can be overridden by setting the SYN_LOG_PRI env - // var to "CRIT", "ERR", etc. + // var to "FATAL", "ERROR", etc. setFilter(const char*) returns + // true if the priority name was recognized; if name == NULL + // then it simply returns true. + static bool setFilter(const char* name); static void setFilter(int); static int getFilter(); @@ -73,6 +76,7 @@ private: #define CLOG_TRACE __FILE__, __LINE__, #endif +#define CLOG_PRINT CLOG_TRACE "%z\057" #define CLOG_CRIT CLOG_TRACE "%z\060" #define CLOG_ERR CLOG_TRACE "%z\061" #define CLOG_WARN CLOG_TRACE "%z\062" From beda89fd53b5955064f7499848e1d712eaad5f46 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 3 Jun 2002 18:53:18 +0000 Subject: [PATCH 137/807] changes to add command line arguments. also added automatic restarting and daemonizing on unix. daemon sends log messages to syslog. unix now reads config file from file named on command line; if no command line arg then uses effective user's config file and if that's not there it finally tries /etc/synergy.conf. if there are no screens configured then one is added for the primary screen. broke some startup stuff on win32. also now timing out if X primary screen can't grab the mouse and keyboard. the server will just give up trying to switch screens. the grabs will fail is some other app has a grab and won't release it. note that kdm grabs the keyboard for the duration that the login window is displayed, effectively disabling synergy. --- base/CLog.cpp | 2 +- base/CLog.h | 13 +- server/CConfig.h | 6 +- server/CMSWindowsPrimaryScreen.cpp | 4 +- server/CMSWindowsPrimaryScreen.h | 2 +- server/CServer.cpp | 34 ++- server/CXWindowsPrimaryScreen.cpp | 51 ++-- server/CXWindowsPrimaryScreen.h | 2 +- server/server.cpp | 453 +++++++++++++++++++++++++++-- synergy/CXWindowsScreen.cpp | 2 +- synergy/IPrimaryScreen.h | 3 +- 11 files changed, 503 insertions(+), 69 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index a9098c8e..fb5f5efb 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -201,7 +201,7 @@ void CLog::output(int priority, char* msg) // print it CHoldLock lock(s_lock); if (s_outputter) { - s_outputter(msg + g_maxPriorityLength - n); + s_outputter(priority, msg + g_maxPriorityLength - n); } else { #if defined(CONFIG_PLATFORM_WIN32) diff --git a/base/CLog.h b/base/CLog.h index 6a1189bd..0e53e7c1 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -6,7 +6,18 @@ class CLog { public: - typedef void (*Outputter)(const char*); + enum { + kFATAL, + kERROR, + kWARNING, + kNOTE, + kINFO, + kDEBUG, + kDEBUG1, + kDEBUG2 + }; + + typedef void (*Outputter)(int priority, const char*); typedef void (*Lock)(bool lock); // diff --git a/server/CConfig.h b/server/CConfig.h index cbb5f9ae..22016182 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -30,7 +30,7 @@ private: public: CString m_neighbor[kLastDirection - kFirstDirection + 1]; }; - typedef std::map CCellMap; + typedef std::map CCellMap; public: typedef CCellMap::const_iterator internal_const_iterator; @@ -65,8 +65,8 @@ public: // manipulators - // note that case is preserved in screen names but has no effect - // FIXME -- make that true + // note that case is preserved in screen names but is ignored when + // comparing names. // add/remove screens void addScreen(const CString& name); diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 6e084fdd..ae4ea098 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -141,7 +141,7 @@ log((CLOG_INFO "failed to release hot key: %d", GetLastError())); nextMark(); } -void CMSWindowsPrimaryScreen::leave() +bool CMSWindowsPrimaryScreen::leave() { log((CLOG_INFO "leaving primary")); assert(m_active == false); @@ -232,6 +232,8 @@ log((CLOG_INFO "failed to get hot key: %d", GetLastError())); // ignore } } + + return true; } void CMSWindowsPrimaryScreen::onConfigure() diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index a3f3afe0..fa54388c 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -19,7 +19,7 @@ public: virtual void open(CServer*); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void leave(); + virtual bool leave(); virtual void onConfigure(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(ClipboardID, const IClipboard*); diff --git a/server/CServer.cpp b/server/CServer.cpp index 02ed0bd1..ad91dc1a 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -63,6 +63,7 @@ CServer::CServer() : m_cleanupSize(&m_mutex, 0), CServer::~CServer() { + // do nothing } void CServer::run() @@ -108,7 +109,9 @@ void CServer::run() cleanupThreads(); delete m_httpServer; m_httpServer = NULL; - closePrimaryScreen(); + if (m_primary != NULL) { + closePrimaryScreen(); + } } catch (XThread&) { // clean up @@ -709,6 +712,10 @@ void CServer::switchScreen(CScreenInfo* dst, log((CLOG_NOTE "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y)); // FIXME -- we're not locked here but we probably should be + // record new position + m_x = x; + m_y = y; + // wrapping means leaving the active screen and entering it again. // since that's a waste of time we skip that and just warp the // mouse. @@ -716,18 +723,19 @@ void CServer::switchScreen(CScreenInfo* dst, // note if we're leaving the primary screen const bool leavingPrimary = (m_active->m_protocol == NULL); - // if leaving the primary screen then update the clipboards - // that it owns + // leave active screen if (leavingPrimary) { + if (!m_primary->leave()) { + // cannot leave primary screen + log((CLOG_WARN "can't leave primary screen")); + return; + } + + // update the clipboards that the primary screen owns for (ClipboardID id = 0; id < kClipboardEnd; ++id) { updatePrimaryClipboard(id); } } - - // leave active screen - if (leavingPrimary) { - m_primary->leave(); - } else { m_active->m_protocol->sendLeave(); } @@ -760,10 +768,6 @@ void CServer::switchScreen(CScreenInfo* dst, m_active->m_protocol->sendMouseMove(x, y); } } - - // record new position - m_x = x; - m_y = y; } CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, @@ -1333,8 +1337,10 @@ void CServer::openPrimaryScreen() m_primary->open(this); } catch (...) { - delete m_primary; - removeConnection(CString("primary"/* FIXME */)); + if (m_primary != NULL) { + removeConnection(CString("primary"/* FIXME */)); + delete m_primary; + } m_primary = NULL; m_primaryInfo = NULL; m_active = NULL; diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 9fe5d801..9012e089 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -2,6 +2,7 @@ #include "CXWindowsClipboard.h" #include "CXWindowsUtil.h" #include "CServer.h" +#include "CStopwatch.h" #include "CThread.h" #include "CLog.h" #include @@ -253,7 +254,7 @@ void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) m_active = false; } -void CXWindowsPrimaryScreen::leave() +bool CXWindowsPrimaryScreen::leave() { log((CLOG_INFO "leaving primary")); assert(m_active == false); @@ -266,31 +267,43 @@ void CXWindowsPrimaryScreen::leave() // grab the mouse and keyboard. keep trying until we get them. // if we can't grab one after grabbing the other then ungrab - // and wait before retrying. + // and wait before retrying. give up after s_timeout seconds. + static const double s_timeout = 1.0; int result; + CStopwatch timer; do { - // mouse first + // keyboard first do { - result = XGrabPointer(display, m_window, True, 0, - GrabModeAsync, GrabModeAsync, - m_window, None, CurrentTime); - assert(result != GrabNotViewable); - if (result != GrabSuccess) { - log((CLOG_DEBUG2 "waiting to grab pointer")); - CThread::sleep(0.1); - } - } while (result != GrabSuccess); - log((CLOG_DEBUG2 "grabbed pointer")); - - // now the keyboard result = XGrabKeyboard(display, m_window, True, GrabModeAsync, GrabModeAsync, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + log((CLOG_DEBUG2 "waiting to grab keyboard")); + CThread::sleep(0.05); + if (timer.getTime() >= s_timeout) { + log((CLOG_DEBUG2 "grab keyboard timed out")); + XUnmapWindow(display, m_window); + return false; + } + } + } while (result != GrabSuccess); + log((CLOG_DEBUG2 "grabbed keyboard")); + + // now the mouse + result = XGrabPointer(display, m_window, True, 0, + GrabModeAsync, GrabModeAsync, + m_window, None, CurrentTime); assert(result != GrabNotViewable); if (result != GrabSuccess) { // back off to avoid grab deadlock - XUngrabPointer(display, CurrentTime); - log((CLOG_DEBUG2 "ungrabbed pointer, waiting to grab keyboard")); - CThread::sleep(0.1); + XUngrabKeyboard(display, CurrentTime); + log((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); + CThread::sleep(0.05); + if (timer.getTime() >= s_timeout) { + log((CLOG_DEBUG2 "grab pointer timed out")); + XUnmapWindow(display, m_window); + return false; + } } } while (result != GrabSuccess); log((CLOG_DEBUG1 "grabbed pointer and keyboard")); @@ -302,6 +315,8 @@ void CXWindowsPrimaryScreen::leave() // local client now active m_active = true; + + return true; } void CXWindowsPrimaryScreen::onConfigure() diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 0590deaa..aa30c327 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -17,7 +17,7 @@ public: virtual void open(CServer*); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void leave(); + virtual bool leave(); virtual void onConfigure(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(ClipboardID, const IClipboard*); diff --git a/server/server.cpp b/server/server.cpp index 3cc4a081..7ea320b2 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -5,14 +5,36 @@ #include "CNetwork.h" #include "CThread.h" #include "XThread.h" +#include "ProtocolTypes.h" #include "stdfstream.h" #include +static const char* s_copyright = "Copyright (C) 2002 Chris Schoeneman"; +static const SInt32 s_majorVersion = 0; +static const SInt32 s_minorVersion = 5; +static const char s_releaseVersion = ' '; + +// configuration file name +#if defined(CONFIG_PLATFORM_WIN32) +#define CONFIG_NAME "synergy.sgc" +#define CONFIG_USER_DIR "%HOME%/" +#define CONFIG_SYS_DIR "" +#elif defined(CONFIG_PLATFORM_UNIX) +#define CONFIG_NAME "synergy.conf" +#define CONFIG_USER_DIR "~/" +#define CONFIG_SYS_DIR "/etc/" +#endif + // -// config file stuff +// program arguments // -static const char* s_configFileName = "synergy.conf"; +static const char* pname = NULL; +static bool s_restartable = true; +static bool s_daemon = true; +static const char* s_configFile = NULL; +static const char* s_logFilter = NULL; +static CConfig s_config; // @@ -53,21 +75,15 @@ void realMain() // initialize network library CNetwork::init(); - // load configuration - CConfig config; - { - log((CLOG_DEBUG "opening configuration")); - std::ifstream configStream(s_configFileName); - if (!configStream) { - throw XConfigRead("cannot open configuration"); - } - configStream >> config; - log((CLOG_DEBUG "configuration read successfully")); + // if configuration has no screens then add this system + // as the default + if (s_config.begin() == s_config.end()) { + s_config.addScreen("primary"); } // run server server = new CServer(); - server->setConfig(config); + server->setConfig(s_config); server->run(); // clean up @@ -86,6 +102,202 @@ void realMain() } +// +// command line parsing +// + +static void bye() +{ + log((CLOG_PRINT "Try `%s --help' for more information.", pname)); + exit(1); +} + +static void version() +{ + log((CLOG_PRINT +"%s %d.%d%c protocol version %d.%d\n" +"%s", + pname, + s_majorVersion, + s_minorVersion, + s_releaseVersion, + kMajorVersion, + kMinorVersion, + s_copyright)); +} + +static void help() +{ + log((CLOG_PRINT +"Usage: %s" +" [--config ]" +" [--debug ]" +" [--daemon|--no-daemon]" +" [--restart|--no-restart]\n" +"Start the synergy mouse/keyboard sharing server.\n" +"\n" +" -c, --config use the named configuration file instead\n" +" where ~ represents the user's home directory.\n" +" -d, --debug filter out log messages with priorty below level.\n" +" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" +" DEBUG, DEBUG1, DEBUG2.\n" +" -f, --no-daemon run the server in the foreground.\n" +" --daemon run the server as a daemon.\n" +" -1, --no-restart do not try to restart the server if it fails for\n" +" some reason.\n" +" --restart restart the server automatically if it fails.\n" +" -h, --help display this help and exit.\n" +" --version display version information and exit.\n" +"\n" +"By default, the server is a restartable daemon. If no configuration file\n" +"pathname is provided then the first of the following to load sets the\n" +"configuration:\n" +" " CONFIG_USER_DIR CONFIG_NAME "\n" +" " CONFIG_SYS_DIR CONFIG_NAME "\n" +"If no configuration file can be loaded then the configuration uses its\n" +"defaults with just the server screen.\n" +"\n" +"Where log messages go depends on the platform and whether or not the\n" +"server is running as a daemon.", + pname)); + +} + +static bool loadConfig(const char* pathname, bool require) +{ + assert(pathname != NULL); + + try { + // load configuration + log((CLOG_DEBUG "opening configuration \"%s\"", pathname)); + std::ifstream configStream(pathname); + if (!configStream) { + throw XConfigRead("cannot open configuration"); + } + configStream >> s_config; + log((CLOG_DEBUG "configuration read successfully")); + return true; + } + catch (XConfigRead& e) { + if (require) { + log((CLOG_PRINT "%s: cannot read configuration '%s'", + pname, pathname)); + exit(1); + } + else { + log((CLOG_DEBUG "cannot read configuration \"%s\"", pathname)); + } + } + return false; +} + +static bool isArg(int argi, + int argc, char** argv, + const char* name1, + const char* name2, + int minRequiredParameters = 0) +{ + if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || + (name2 != NULL && strcmp(argv[argi], name2) == 0)) { + // match. check args left. + if (argi + minRequiredParameters >= argc) { + log((CLOG_PRINT "%s: missing arguments for `%s'", + pname, argv[argi])); + bye(); + } + return true; + } + + // no match + return false; +} + +static void parse(int argc, char** argv) +{ + assert(pname != NULL); + assert(argv != NULL); + assert(argc >= 1); + + // parse options + int i; + for (i = 1; i < argc; ++i) { + if (isArg(i, argc, argv, "-d", "--debug", 1)) { + // change logging level + s_logFilter = argv[++i]; + } + + else if (isArg(i, argc, argv, "-c", "--config", 1)) { + // save configuration file path + s_configFile = argv[++i]; + } + + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { + // not a daemon + s_daemon = false; + } + + else if (isArg(i, argc, argv, NULL, "--daemon")) { + // daemonize + s_daemon = true; + } + + else if (isArg(i, argc, argv, "-1", "--no-restart")) { + // don't try to restart + s_restartable = false; + } + + else if (isArg(i, argc, argv, NULL, "--restart")) { + // try to restart + s_restartable = true; + } + + else if (isArg(i, argc, argv, "-h", "--help")) { + help(); + exit(1); + } + + else if (isArg(i, argc, argv, NULL, "--version")) { + version(); + exit(1); + } + + else if (isArg(i, argc, argv, "--", NULL)) { + // remaining arguments are not options + ++i; + break; + } + + else if (argv[i][0] == '-') { + log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); + bye(); + } + + else { + // this and remaining arguments are not options + break; + } + } + + // no non-option arguments are allowed + if (i != argc) { + log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); + bye(); + } + + // set log filter + if (!CLog::setFilter(s_logFilter)) { + log((CLOG_PRINT "%s: unrecognized log level `%s'", pname, s_logFilter)); + bye(); + } + + // load the config file, if any + if (s_configFile != NULL) { + // require the user specified file to load correctly + loadConfig(s_configFile, true); + } +} + + // // platform dependent entry points // @@ -93,11 +305,39 @@ void realMain() #if defined(CONFIG_PLATFORM_WIN32) #include "CMSWindowsScreen.h" +#include int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { CMSWindowsScreen::init(instance); + // get program name + pname = strrchr(argv[0], '/'); + if (pname == NULL) { + pname = argv[0]; + } + else { + ++pname; + } + const char* pname2 = strrchr(argv[0], '\\'); + if (pname2 != NULL && pname2 > pname) { + pname = pname2 + 1; + } + +// FIXME -- direct CLog to MessageBox + + parse(__argc, __argv); + +// FIXME -- undirect CLog from MessageBox +// FIXME -- if daemon then use win32 event log (however that's done), +// otherwise do what? want to use console window for debugging but +// not otherwise. + + // load the configuration file if we haven't already + if (s_configFile == NULL) { + } + +// FIXME if (__argc != 1) { CString msg = "no arguments allowed. exiting."; MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); @@ -121,30 +361,189 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) } } -#else +#elif defined(CONFIG_PLATFORM_UNIX) #include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* s_configFileDefault = CONFIG_SYS_DIR CONFIG_NAME; + +static void daemonize() +{ + // fork so shell thinks we're done and so we're not a process + // group leader + switch (fork()) { + case -1: + // failed + log((CLOG_PRINT "failed to daemonize")); + exit(1); + + case 0: + // child + break; + + default: + // parent exits + exit(0); + } + + // become leader of a new session + setsid(); + + // chdir to root so we don't keep mounted filesystems points busy + chdir("/"); + + // mask off permissions for any but owner + umask(077); + + // close open files. we only expect stdin, stdout, stderr to be open. + close(0); + close(1); + close(2); + + // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use + // of standard I/O safely goes in the bit bucket. + open("/dev/null", O_RDWR); + dup(0); + dup(0); +} + +static void syslogOutputter(int priority, const char* msg) +{ + // convert priority + switch (priority) { + case CLog::kFATAL: + case CLog::kERROR: + priority = LOG_ERR; + break; + + case CLog::kWARNING: + priority = LOG_WARNING; + break; + + case CLog::kNOTE: + priority = LOG_NOTICE; + break; + + case CLog::kINFO: + priority = LOG_INFO; + break; + + default: + priority = LOG_DEBUG; + break; + } + + // log it + syslog(priority, "%s", msg); +} int main(int argc, char** argv) { - if (argc != 1) { - fprintf(stderr, "usage: %s\n", argv[0]); - return 1; + // get program name + pname = strrchr(argv[0], '/'); + if (pname == NULL) { + pname = argv[0]; + } + else { + ++pname; } - try { - realMain(); - return 0; + // parse command line + parse(argc, argv); + + // load the configuration file if we haven't already + if (s_configFile == NULL) { + // get the user's home directory. use the effective user id + // so a user can't get a setuid root program to load his file. + bool loaded = false; + struct passwd* pwent = getpwuid(geteuid()); + if (pwent != NULL && pwent->pw_dir != NULL) { + // construct path if it isn't too long + if (strlen(pwent->pw_dir) + strlen(CONFIG_NAME) + 2 <= PATH_MAX) { + char path[PATH_MAX]; + strcpy(path, pwent->pw_dir); + strcat(path, "/"); + strcat(path, CONFIG_NAME); + + // now try loading the user's configuration + loaded = loadConfig(path, false); + } + } + if (!loaded) { + // try the system-wide config file + loadConfig(s_configFileDefault, false); + } } - catch (XBase& e) { - log((CLOG_CRIT "failed: %s", e.what())); - fprintf(stderr, "failed: %s\n", e.what()); - return 1; + + // daemonize if requested + if (s_daemon) { + daemonize(); + + // send log to syslog + openlog("synergy", 0, LOG_DAEMON); + CLog::setOutputter(&syslogOutputter); } - catch (XThread&) { - // terminated - return 1; + + // run the server. if running as a daemon then run it in a child + // process and restart it as necessary. we have to do this in case + // the X server restarts because our process cannot recover from + // that. + for (;;) { + // don't fork if not restartable + switch (s_restartable ? fork() : 0) { + default: { + // parent process. wait for child to exit. + int status; + if (wait(&status) == -1) { + // wait failed. this is unexpected so bail. + log((CLOG_CRIT "wait() failed")); + return 16; + } + + // what happened? if the child exited normally with a + // status less than 16 then the child was deliberately + // terminated so we also terminate. otherwise, we + // loop. + if (WIFEXITED(status) && WEXITSTATUS(status) < 16) { + return 0; + } + break; + } + + case -1: + // fork() failed. log the error and proceed as a child + log((CLOG_WARN "fork() failed; cannot automatically restart on error")); + // fall through + + case 0: + // child process + try { + realMain(); + return 0; + } + catch (XBase& e) { + log((CLOG_CRIT "failed: %s", e.what())); + fprintf(stderr, "failed: %s\n", e.what()); + return 16; + } + catch (XThread&) { + // terminated + return 1; + } + } } } +#else + +#error no main() for platform + #endif diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 25602a4a..d2817c95 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -357,7 +357,7 @@ int CXWindowsScreen::ioErrorHandler(Display*) s_screen->m_display = NULL; s_screen->onUnexpectedClose(); log((CLOG_CRIT "quiting due to X display disconnection")); - exit(1); + exit(17); } diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 464d85af..295bf37a 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -47,7 +47,8 @@ public: // called when the user navigates off the primary screen. hide // the cursor and grab exclusive access to the input devices. - virtual void leave() = 0; + // return true iff successful. + virtual bool leave() = 0; // called when the configuration has changed. subclasses may need // to adjust things (like the jump zones) after the configuration From 3b872bb727d48f27db8654c2b9cf0fda784ccaaf Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 4 Jun 2002 11:02:33 +0000 Subject: [PATCH 138/807] fixed timeout when getting selection -- forgot to set flag to terminate event loop. --- synergy/CXWindowsClipboard.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/synergy/CXWindowsClipboard.cpp b/synergy/CXWindowsClipboard.cpp index 8ab22899..9a225e39 100644 --- a/synergy/CXWindowsClipboard.cpp +++ b/synergy/CXWindowsClipboard.cpp @@ -1190,6 +1190,7 @@ bool CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate( // done if this is the timeout message if (xevent->xclient.window == m_requestor && xevent->xclient.message_type == m_timeout) { + m_failed = true; return true; } From e409c83ef92759b893c310e476a63d2da655c7c9 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 4 Jun 2002 11:03:34 +0000 Subject: [PATCH 139/807] fixed delete bug in printt -- when skipping file and line the deleted pointer was wrong. --- base/CLog.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index fb5f5efb..2b519a6e 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -97,12 +97,13 @@ void CLog::printt(const char* file, int line, buffer[pad - 1] = ' '; // discard file and line if priority < 0 + char* message = buffer; if (priority < 0) { - buffer += pad - g_priorityPad; + message += pad - g_priorityPad; } // output buffer - output(priority, buffer); + output(priority, message); // clean up if (buffer != stack) From c3649df304b6ae2eb8b3b5377d138e04fd1bde7a Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 4 Jun 2002 11:06:26 +0000 Subject: [PATCH 140/807] added command line parsing, restartability, and daemonizing to client. broke win32 stuff though. also moved version and copyright constants into a new file and renamed protocol version constants. --- client/CClient.cpp | 9 +- client/client.cpp | 357 +++++++++++++++++++++++++++++++++++-- server/CServer.cpp | 5 +- server/CServerProtocol.cpp | 6 +- server/server.cpp | 18 +- synergy/ProtocolTypes.h | 4 +- synergy/Version.h | 13 ++ 7 files changed, 371 insertions(+), 41 deletions(-) create mode 100644 synergy/Version.h diff --git a/client/CClient.cpp b/client/CClient.cpp index a5178e43..09e78f6c 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -214,15 +214,16 @@ void CClient::runSession(void*) // check versions log((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); - if (major < kMajorVersion || - (major == kMajorVersion && minor < kMinorVersion)) { + if (major < kProtocolMajorVersion || + (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) { throw XIncompatibleClient(major, minor); } // say hello back - log((CLOG_DEBUG1 "say hello version %d.%d", kMajorVersion, kMinorVersion)); + log((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s", - kMajorVersion, kMinorVersion, &m_name); + kProtocolMajorVersion, + kProtocolMinorVersion, &m_name); // record streams in a more useful place CLock lock(&m_mutex); diff --git a/client/client.cpp b/client/client.cpp index 4876c527..4c0e3ef8 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -6,8 +6,21 @@ #include "CNetworkAddress.h" #include "CThread.h" #include "XThread.h" +#include "ProtocolTypes.h" +#include "Version.h" #include +// +// program arguments +// + +static const char* pname = NULL; +static bool s_restartable = true; +static bool s_daemon = true; +static const char* s_logFilter = NULL; +static const char* s_serverName = NULL; + + // // logging thread safety // @@ -70,6 +83,160 @@ void realMain(const CString& name, } +// +// command line parsing +// + +static void bye() +{ + log((CLOG_PRINT "Try `%s --help' for more information.", pname)); + exit(1); +} + +static void version() +{ + log((CLOG_PRINT +"%s %d.%d.%d, protocol version %d.%d\n" +"%s", + pname, + kMajorVersion, + kMinorVersion, + kReleaseVersion, + kProtocolMajorVersion, + kProtocolMinorVersion, + kCopyright)); +} + +static void help() +{ + log((CLOG_PRINT +"Usage: %s" +" [--debug ]" +" [--daemon|--no-daemon]" +" [--restart|--no-restart]" +" \n" +"Start the synergy mouse/keyboard sharing server.\n" +"\n" +" -d, --debug filter out log messages with priorty below level.\n" +" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" +" DEBUG, DEBUG1, DEBUG2.\n" +" -f, --no-daemon run the client in the foreground.\n" +" --daemon run the client as a daemon.\n" +" -1, --no-restart do not try to restart the client if it fails for\n" +" some reason.\n" +" --restart restart the client automatically if it fails.\n" +" -h, --help display this help and exit.\n" +" --version display version information and exit.\n" +"\n" +"By default, the client is a restartable daemon.\n" +"\n" +"Where log messages go depends on the platform and whether or not the\n" +"client is running as a daemon.", + pname)); + +} + +static bool isArg(int argi, + int argc, char** argv, + const char* name1, + const char* name2, + int minRequiredParameters = 0) +{ + if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || + (name2 != NULL && strcmp(argv[argi], name2) == 0)) { + // match. check args left. + if (argi + minRequiredParameters >= argc) { + log((CLOG_PRINT "%s: missing arguments for `%s'", + pname, argv[argi])); + bye(); + } + return true; + } + + // no match + return false; +} + +static void parse(int argc, char** argv) +{ + assert(pname != NULL); + assert(argv != NULL); + assert(argc >= 1); + + // parse options + int i; + for (i = 1; i < argc; ++i) { + if (isArg(i, argc, argv, "-d", "--debug", 1)) { + // change logging level + s_logFilter = argv[++i]; + } + + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { + // not a daemon + s_daemon = false; + } + + else if (isArg(i, argc, argv, NULL, "--daemon")) { + // daemonize + s_daemon = true; + } + + else if (isArg(i, argc, argv, "-1", "--no-restart")) { + // don't try to restart + s_restartable = false; + } + + else if (isArg(i, argc, argv, NULL, "--restart")) { + // try to restart + s_restartable = true; + } + + else if (isArg(i, argc, argv, "-h", "--help")) { + help(); + exit(1); + } + + else if (isArg(i, argc, argv, NULL, "--version")) { + version(); + exit(1); + } + + else if (isArg(i, argc, argv, "--", NULL)) { + // remaining arguments are not options + ++i; + break; + } + + else if (argv[i][0] == '-') { + log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); + bye(); + } + + else { + // this and remaining arguments are not options + break; + } + } + + // exactly one non-option argument: server-address + if (i == argc) { + log((CLOG_PRINT "%s: a server address or name is required", pname)); + bye(); + } + if (i + 1 != argc) { + log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); + bye(); + } + s_serverName = argv[i]; + + // set log filter + if (!CLog::setFilter(s_logFilter)) { + log((CLOG_PRINT "%s: unrecognized log level `%s'", pname, s_logFilter)); + bye(); + } +} + + // // platform dependent entry points // @@ -77,22 +244,41 @@ void realMain(const CString& name, #if defined(CONFIG_PLATFORM_WIN32) #include "CMSWindowsScreen.h" +#include int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { CMSWindowsScreen::init(instance); - if (__argc != 2) { - CString msg = "hostname required. exiting."; - MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); - return 1; + // get program name + pname = strrchr(argv[0], '/'); + if (pname == NULL) { + pname = argv[0]; + } + else { + ++pname; + } + const char* pname2 = strrchr(argv[0], '\\'); + if (pname2 != NULL && pname2 > pname) { + pname = pname2 + 1; } +// FIXME -- direct CLog to MessageBox + + parse(__argc, __argv); + +// FIXME -- undirect CLog from MessageBox +// FIXME -- if daemon then use win32 event log (however that's done), +// otherwise do what? want to use console window for debugging but +// not otherwise. + +// FIXME try { - realMain("secondary", __argv[1], 50001); + realMain("secondary", s_serverName, 50001); return 0; } catch (XBase& e) { + log((CLOG_CRIT "failed: %s", e.what())); CString msg = "failed: "; msg += e.what(); MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); @@ -104,29 +290,162 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) } } -#else +#elif defined(CONFIG_PLATFORM_UNIX) #include +#include +#include +#include +#include +#include +#include +#include +#include + +static void daemonize() +{ + // fork so shell thinks we're done and so we're not a process + // group leader + switch (fork()) { + case -1: + // failed + log((CLOG_PRINT "failed to daemonize")); + exit(1); + + case 0: + // child + break; + + default: + // parent exits + exit(0); + } + + // become leader of a new session + setsid(); + + // chdir to root so we don't keep mounted filesystems points busy + chdir("/"); + + // mask off permissions for any but owner + umask(077); + + // close open files. we only expect stdin, stdout, stderr to be open. + close(0); + close(1); + close(2); + + // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use + // of standard I/O safely goes in the bit bucket. + open("/dev/null", O_RDWR); + dup(0); + dup(0); +} + +static void syslogOutputter(int priority, const char* msg) +{ + // convert priority + switch (priority) { + case CLog::kFATAL: + case CLog::kERROR: + priority = LOG_ERR; + break; + + case CLog::kWARNING: + priority = LOG_WARNING; + break; + + case CLog::kNOTE: + priority = LOG_NOTICE; + break; + + case CLog::kINFO: + priority = LOG_INFO; + break; + + default: + priority = LOG_DEBUG; + break; + } + + // log it + syslog(priority, "%s", msg); +} int main(int argc, char** argv) { - if (argc != 2) { - fprintf(stderr, "usage: %s \n", argv[0]); - return 1; + // get program name + pname = strrchr(argv[0], '/'); + if (pname == NULL) { + pname = argv[0]; + } + else { + ++pname; } - try { - realMain("secondary", argv[1], 50001); - return 0; + // parse command line + parse(argc, argv); + + // daemonize if requested + if (s_daemon) { + daemonize(); + + // send log to syslog + openlog("synergy", 0, LOG_DAEMON); + CLog::setOutputter(&syslogOutputter); } - catch (XBase& e) { - fprintf(stderr, "failed: %s\n", e.what()); - return 1; - } - catch (XThread&) { - // terminated - return 1; + + // run the server. if running as a daemon then run it in a child + // process and restart it as necessary. we have to do this in case + // the X server restarts because our process cannot recover from + // that. + for (;;) { + // don't fork if not restartable + switch (s_restartable ? fork() : 0) { + default: { + // parent process. wait for child to exit. + int status; + if (wait(&status) == -1) { + // wait failed. this is unexpected so bail. + log((CLOG_CRIT "wait() failed")); + return 16; + } + + // what happened? if the child exited normally with a + // status less than 16 then the child was deliberately + // terminated so we also terminate. otherwise, we + // loop. + if (WIFEXITED(status) && WEXITSTATUS(status) < 16) { + return 0; + } + break; + } + + case -1: + // fork() failed. log the error and proceed as a child + log((CLOG_WARN "fork() failed; cannot automatically restart on error")); + // fall through + + case 0: + // child process + try { + realMain("secondary", s_serverName, 50001); + return 0; + } + catch (XBase& e) { + log((CLOG_CRIT "failed: %s", e.what())); + return 16; + } + catch (XThread&) { + // terminated + return 1; + } + } } } +#else + +#error no main() for platform + #endif diff --git a/server/CServer.cpp b/server/CServer.cpp index ad91dc1a..8e0fa1d8 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -1059,7 +1059,8 @@ void CServer::handshakeClient(void* vsocket) // say hello log((CLOG_DEBUG1 "saying hello")); CProtocolUtil::writef(output.get(), "Synergy%2i%2i", - kMajorVersion, kMinorVersion); + kProtocolMajorVersion, + kProtocolMinorVersion); output->flush(); // wait for the reply @@ -1117,7 +1118,7 @@ void CServer::handshakeClient(void* vsocket) // FIXME -- could print network address if socket had suitable method log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); CProtocolUtil::writef(output.get(), kMsgEIncompatible, - kMajorVersion, kMinorVersion); + kProtocolMajorVersion, kProtocolMinorVersion); } catch (XBadClient&) { // client not behaving diff --git a/server/CServerProtocol.cpp b/server/CServerProtocol.cpp index c74bf0a3..73b7ecc5 100644 --- a/server/CServerProtocol.cpp +++ b/server/CServerProtocol.cpp @@ -56,13 +56,13 @@ IServerProtocol* CServerProtocol::create(SInt32 major, SInt32 minor, } // disallow connection from test versions to release versions - if (major == 0 && kMajorVersion != 0) { + if (major == 0 && kProtocolMajorVersion != 0) { throw XIncompatibleClient(major, minor); } // hangup (with error) if version isn't supported - if (major > kMajorVersion || - (major == kMajorVersion && minor > kMinorVersion)) { + if (major > kProtocolMajorVersion || + (major == kProtocolMajorVersion && minor > kProtocolMinorVersion)) { throw XIncompatibleClient(major, minor); } diff --git a/server/server.cpp b/server/server.cpp index 7ea320b2..671b1b7c 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -6,14 +6,10 @@ #include "CThread.h" #include "XThread.h" #include "ProtocolTypes.h" +#include "Version.h" #include "stdfstream.h" #include -static const char* s_copyright = "Copyright (C) 2002 Chris Schoeneman"; -static const SInt32 s_majorVersion = 0; -static const SInt32 s_minorVersion = 5; -static const char s_releaseVersion = ' '; - // configuration file name #if defined(CONFIG_PLATFORM_WIN32) #define CONFIG_NAME "synergy.sgc" @@ -115,15 +111,15 @@ static void bye() static void version() { log((CLOG_PRINT -"%s %d.%d%c protocol version %d.%d\n" +"%s %d.%d.%d, protocol version %d.%d\n" "%s", pname, - s_majorVersion, - s_minorVersion, - s_releaseVersion, kMajorVersion, kMinorVersion, - s_copyright)); + kReleaseVersion, + kProtocolMajorVersion, + kProtocolMinorVersion, + kCopyright)); } static void help() @@ -335,6 +331,7 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // load the configuration file if we haven't already if (s_configFile == NULL) { +// FIXME } // FIXME @@ -531,7 +528,6 @@ int main(int argc, char** argv) } catch (XBase& e) { log((CLOG_CRIT "failed: %s", e.what())); - fprintf(stderr, "failed: %s\n", e.what()); return 16; } catch (XThread&) { diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index e06093fa..32cd4e0b 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -4,8 +4,8 @@ #include "BasicTypes.h" // version number -static const SInt32 kMajorVersion = 0; -static const SInt32 kMinorVersion = 1; +static const SInt16 kProtocolMajorVersion = 0; +static const SInt16 kProtocolMinorVersion = 1; // // message codes (trailing NUL is not part of code). in comments, $n diff --git a/synergy/Version.h b/synergy/Version.h new file mode 100644 index 00000000..24367162 --- /dev/null +++ b/synergy/Version.h @@ -0,0 +1,13 @@ +#ifndef VERSION_H +#define VERSION_H + +#include "BasicTypes.h" + +static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; + +// build version +static const SInt16 kMajorVersion = 0; +static const SInt16 kMinorVersion = 5; +static const SInt16 kReleaseVersion = 0; + +#endif From 5709d8ddef48471866fcacfd91d974ff259ea1e4 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 4 Jun 2002 12:26:23 +0000 Subject: [PATCH 141/807] refactored some common platform dependent stuff into a new library: platform. also removed test.cpp. --- Makefile | 38 +---- client/Makefile | 4 +- client/client.cpp | 111 ++---------- client/client.dsp | 4 +- {synergy => platform}/CMSWindowsClipboard.cpp | 0 {synergy => platform}/CMSWindowsClipboard.h | 0 {synergy => platform}/CMSWindowsScreen.cpp | 0 {synergy => platform}/CMSWindowsScreen.h | 0 platform/CPlatform.cpp | 11 ++ platform/CPlatform.h | 18 ++ platform/CUnixPlatform.cpp | 160 ++++++++++++++++++ platform/CUnixPlatform.h | 29 ++++ platform/CWin32Platform.cpp | 90 ++++++++++ platform/CWin32Platform.h | 26 +++ {synergy => platform}/CXWindowsClipboard.cpp | 0 {synergy => platform}/CXWindowsClipboard.h | 0 {synergy => platform}/CXWindowsScreen.cpp | 0 {synergy => platform}/CXWindowsScreen.h | 0 {synergy => platform}/CXWindowsUtil.cpp | 0 {synergy => platform}/CXWindowsUtil.h | 0 platform/IPlatform.h | 45 +++++ platform/Makefile | 28 +++ server/Makefile | 4 +- server/server.cpp | 142 +++------------- server/server.dsp | 4 +- synergy.dsw | 22 ++- synergy/Makefile | 3 - synergy/synergy.dsp | 16 -- test.cpp | 145 ---------------- 29 files changed, 476 insertions(+), 424 deletions(-) rename {synergy => platform}/CMSWindowsClipboard.cpp (100%) rename {synergy => platform}/CMSWindowsClipboard.h (100%) rename {synergy => platform}/CMSWindowsScreen.cpp (100%) rename {synergy => platform}/CMSWindowsScreen.h (100%) create mode 100644 platform/CPlatform.cpp create mode 100644 platform/CPlatform.h create mode 100644 platform/CUnixPlatform.cpp create mode 100644 platform/CUnixPlatform.h create mode 100644 platform/CWin32Platform.cpp create mode 100644 platform/CWin32Platform.h rename {synergy => platform}/CXWindowsClipboard.cpp (100%) rename {synergy => platform}/CXWindowsClipboard.h (100%) rename {synergy => platform}/CXWindowsScreen.cpp (100%) rename {synergy => platform}/CXWindowsScreen.h (100%) rename {synergy => platform}/CXWindowsUtil.cpp (100%) rename {synergy => platform}/CXWindowsUtil.h (100%) create mode 100644 platform/IPlatform.h create mode 100644 platform/Makefile delete mode 100644 test.cpp diff --git a/Makefile b/Makefile index 70d1a8bf..29e61219 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ SUBDIRS = \ http \ net \ synergy \ + platform \ client \ server \ $(NULL) @@ -32,40 +33,3 @@ clean: clobber: $(RMR) $(LIBDIR) $(SUBDIRS_MAKERULE) - -# -# -# test -# -# - -# -# source files -# -LCXXINCS = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ - -I$(DEPTH)/net \ - -I$(DEPTH)/synergy \ - $(NULL) -CXXFILES = test.cpp - -# -# libraries we depend on -# -DEPLIBS = \ - $(LIBDIR)/libsynergy.a \ - $(LIBDIR)/libnet.a \ - $(LIBDIR)/libio.a \ - $(LIBDIR)/libmt.a \ - $(LIBDIR)/libbase.a \ - $(NULL) -LLDLIBS = \ - $(DEPLIBS) \ - -lpthread \ - $(NULL) - -test: $(OBJECTS) $(DEPLIBS) - $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) - diff --git a/client/Makefile b/client/Makefile index 9f0e3579..0c3d7cba 100644 --- a/client/Makefile +++ b/client/Makefile @@ -4,7 +4,7 @@ include $(DEPTH)/Makecommon # # target file # -TARGET = client +TARGET = synergy # # source files @@ -15,6 +15,7 @@ LCXXINCS = \ -I$(DEPTH)/io \ -I$(DEPTH)/net \ -I$(DEPTH)/synergy \ + -I$(DEPTH)/platform \ $(NULL) CXXFILES = \ CXWindowsSecondaryScreen.cpp \ @@ -26,6 +27,7 @@ CXXFILES = \ # libraries we depend on # DEPLIBS = \ + $(LIBDIR)/libplatform.a \ $(LIBDIR)/libsynergy.a \ $(LIBDIR)/libnet.a \ $(LIBDIR)/libio.a \ diff --git a/client/client.cpp b/client/client.cpp index 4c0e3ef8..da943c18 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -4,6 +4,7 @@ #include "CMutex.h" #include "CNetwork.h" #include "CNetworkAddress.h" +#include "CPlatform.h" #include "CThread.h" #include "XThread.h" #include "ProtocolTypes.h" @@ -248,20 +249,13 @@ static void parse(int argc, char** argv) int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { + CPlatform platform; + + // save instance CMSWindowsScreen::init(instance); // get program name - pname = strrchr(argv[0], '/'); - if (pname == NULL) { - pname = argv[0]; - } - else { - ++pname; - } - const char* pname2 = strrchr(argv[0], '\\'); - if (pname2 != NULL && pname2 > pname) { - pname = pname2 + 1; - } + pname = platform.getBasename(argv[0]); // FIXME -- direct CLog to MessageBox @@ -292,107 +286,26 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) #elif defined(CONFIG_PLATFORM_UNIX) -#include -#include #include -#include -#include -#include -#include #include #include -static void daemonize() -{ - // fork so shell thinks we're done and so we're not a process - // group leader - switch (fork()) { - case -1: - // failed - log((CLOG_PRINT "failed to daemonize")); - exit(1); - - case 0: - // child - break; - - default: - // parent exits - exit(0); - } - - // become leader of a new session - setsid(); - - // chdir to root so we don't keep mounted filesystems points busy - chdir("/"); - - // mask off permissions for any but owner - umask(077); - - // close open files. we only expect stdin, stdout, stderr to be open. - close(0); - close(1); - close(2); - - // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use - // of standard I/O safely goes in the bit bucket. - open("/dev/null", O_RDWR); - dup(0); - dup(0); -} - -static void syslogOutputter(int priority, const char* msg) -{ - // convert priority - switch (priority) { - case CLog::kFATAL: - case CLog::kERROR: - priority = LOG_ERR; - break; - - case CLog::kWARNING: - priority = LOG_WARNING; - break; - - case CLog::kNOTE: - priority = LOG_NOTICE; - break; - - case CLog::kINFO: - priority = LOG_INFO; - break; - - default: - priority = LOG_DEBUG; - break; - } - - // log it - syslog(priority, "%s", msg); -} - int main(int argc, char** argv) { + CPlatform platform; + // get program name - pname = strrchr(argv[0], '/'); - if (pname == NULL) { - pname = argv[0]; - } - else { - ++pname; - } + pname = platform.getBasename(argv[0]); // parse command line parse(argc, argv); // daemonize if requested if (s_daemon) { - daemonize(); - - // send log to syslog - openlog("synergy", 0, LOG_DAEMON); - CLog::setOutputter(&syslogOutputter); + if (!platform.daemonize("synergy")) { + log((CLOG_CRIT "failed to daemonize")); + return 16; + } } // run the server. if running as a daemon then run it in a child diff --git a/client/client.dsp b/client/client.dsp index 0d516a91..682006b2 100644 --- a/client/client.dsp +++ b/client/client.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -68,7 +68,7 @@ LINK32=link.exe # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/synergy/CMSWindowsClipboard.cpp b/platform/CMSWindowsClipboard.cpp similarity index 100% rename from synergy/CMSWindowsClipboard.cpp rename to platform/CMSWindowsClipboard.cpp diff --git a/synergy/CMSWindowsClipboard.h b/platform/CMSWindowsClipboard.h similarity index 100% rename from synergy/CMSWindowsClipboard.h rename to platform/CMSWindowsClipboard.h diff --git a/synergy/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp similarity index 100% rename from synergy/CMSWindowsScreen.cpp rename to platform/CMSWindowsScreen.cpp diff --git a/synergy/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h similarity index 100% rename from synergy/CMSWindowsScreen.h rename to platform/CMSWindowsScreen.h diff --git a/platform/CPlatform.cpp b/platform/CPlatform.cpp new file mode 100644 index 00000000..79d7a047 --- /dev/null +++ b/platform/CPlatform.cpp @@ -0,0 +1,11 @@ +#include "common.h" + +#if defined(CONFIG_PLATFORM_WIN32) + +#include "CWin32Platform.cpp" + +#elif defined(CONFIG_PLATFORM_UNIX) + +#include "CUnixPlatform.cpp" + +#endif diff --git a/platform/CPlatform.h b/platform/CPlatform.h new file mode 100644 index 00000000..76d2dea6 --- /dev/null +++ b/platform/CPlatform.h @@ -0,0 +1,18 @@ +#ifndef CPLATFORM_H +#define CPLATFORM_H + +#include "common.h" + +#if defined(CONFIG_PLATFORM_WIN32) + +#include "CWin32Platform.h" +typedef CWin32Platform CPlatform; + +#elif defined(CONFIG_PLATFORM_UNIX) + +#include "CUnixPlatform.h" +typedef CUnixPlatform CPlatform; + +#endif + +#endif diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp new file mode 100644 index 00000000..ff519a92 --- /dev/null +++ b/platform/CUnixPlatform.cpp @@ -0,0 +1,160 @@ +#include "CUnixPlatform.h" +#include "CLog.h" +#include +#include +#include +#include +#include +#include +#include + +// +// CUnixPlatform +// + +CUnixPlatform::CUnixPlatform() +{ + // do nothing +} + +CUnixPlatform::~CUnixPlatform() +{ + // do nothing +} + +bool CUnixPlatform::installDaemon(/* FIXME */) +{ + // daemons don't require special installation + return true; +} + +bool CUnixPlatform::uninstallDaemon(/* FIXME */) +{ + // daemons don't require special installation + return true; +} + +bool CUnixPlatform::daemonize(const char* name) +{ + // fork so shell thinks we're done and so we're not a process + // group leader + switch (fork()) { + case -1: + // failed + return false; + + case 0: + // child + break; + + default: + // parent exits + exit(0); + } + + // become leader of a new session + setsid(); + + // chdir to root so we don't keep mounted filesystems points busy + chdir("/"); + + // mask off permissions for any but owner + umask(077); + + // close open files. we only expect stdin, stdout, stderr to be open. + close(0); + close(1); + close(2); + + // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use + // of standard I/O safely goes in the bit bucket. + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDWR); + dup(1); + + // hook up logger + setDaemonLogger(name); + + return true; +} + +const char* CUnixPlatform::getBasename(const char* pathname) const +{ + if (pathname == NULL) { + return NULL; + } + + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + return basename + 1; + } + else { + return pathname; + } +} + +CString CUnixPlatform::getUserDirectory() const +{ + // FIXME -- use geteuid? shouldn't run this setuid anyway. + struct passwd* pwent = getpwuid(getuid()); + if (pwent != NULL && pwent->pw_dir != NULL) { + return pwent->pw_dir; + } + else { + return CString(); + } +} + +CString CUnixPlatform::getSystemDirectory() const +{ + return "/etc"; +} + +CString CUnixPlatform::addPathComponent( + const CString& prefix, + const CString& suffix) const +{ + CString path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + path += '/'; + path += suffix; + return path; +} + +void CUnixPlatform::setDaemonLogger(const char* name) +{ + openlog(name, 0, LOG_DAEMON); + CLog::setOutputter(&CUnixPlatform::deamonLogger); +} + +void CUnixPlatform::deamonLogger( + int priority, const char* msg) +{ + // convert priority + switch (priority) { + case CLog::kFATAL: + case CLog::kERROR: + priority = LOG_ERR; + break; + + case CLog::kWARNING: + priority = LOG_WARNING; + break; + + case CLog::kNOTE: + priority = LOG_NOTICE; + break; + + case CLog::kINFO: + priority = LOG_INFO; + break; + + default: + priority = LOG_DEBUG; + break; + } + + // log it + syslog(priority, "%s", msg); +} diff --git a/platform/CUnixPlatform.h b/platform/CUnixPlatform.h new file mode 100644 index 00000000..c449e3ed --- /dev/null +++ b/platform/CUnixPlatform.h @@ -0,0 +1,29 @@ +#ifndef CUNIXPLATFORM_H +#define CUNIXPLATFORM_H + +#include "IPlatform.h" + +class CUnixPlatform : public IPlatform { +public: + CUnixPlatform(); + virtual ~CUnixPlatform(); + + // IPlatform overrides + virtual bool installDaemon(/* FIXME */); + virtual bool uninstallDaemon(/* FIXME */); + virtual bool daemonize(const char* name); + virtual const char* getBasename(const char* pathname) const; + virtual CString getUserDirectory() const; + virtual CString getSystemDirectory() const; + virtual CString addPathComponent( + const CString& prefix, + const CString& suffix) const; + +protected: + virtual void setDaemonLogger(const char* name); + +private: + static void deamonLogger(int, const char*); +}; + +#endif diff --git a/platform/CWin32Platform.cpp b/platform/CWin32Platform.cpp new file mode 100644 index 00000000..b0ee15d5 --- /dev/null +++ b/platform/CWin32Platform.cpp @@ -0,0 +1,90 @@ +#include "CWin32Platform.h" +#include "CLog.h" +#include +#include + +// +// CWin32Platform +// + +CWin32Platform::CWin32Platform() +{ + // do nothing +} + +CWin32Platform::~CWin32Platform() +{ + // do nothing +} + +bool CWin32Platform::installDaemon(/* FIXME */) +{ + // FIXME + return false; +} + +bool CWin32Platform::uninstallDaemon(/* FIXME */) +{ + // FIXME + return false; +} + +bool CWin32Platform::daemonize(const char* name) +{ + // FIXME + return false; +} + +const char* CWin32Platform::getBasename(const char* pathname) const +{ + if (pathname == NULL) { + return NULL; + } + + // check for last / + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + ++basename; + } + else { + basename = pathname; + } + + // check for last backslash + const char* basename2 = strrchr(pathname, '\\'); + if (basename2 != NULL && basename2 > basename) { + basename = basename2 + 1; + } + + return basename; +} + +CString CWin32Platform::getUserDirectory() const +{ + // FIXME + return CString(); +} + +CString CWin32Platform::getSystemDirectory() const +{ + // FIXME + return ""; +} + +CString CWin32Platform::addPathComponent( + const CString& prefix, + const CString& suffix) const +{ + CString path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + path += '\\'; + path += suffix; + return path; +} + +void CWin32Platform::serviceLogger( + int priority, const char* msg) +{ + // FIXME +} diff --git a/platform/CWin32Platform.h b/platform/CWin32Platform.h new file mode 100644 index 00000000..3a680be0 --- /dev/null +++ b/platform/CWin32Platform.h @@ -0,0 +1,26 @@ +#ifndef CWIN32PLATFORM_H +#define CWIN32PLATFORM_H + +#include "IPlatform.h" + +class CWin32Platform : public IPlatform { +public: + CWin32Platform(); + virtual ~CWin32Platform(); + + // IPlatform overrides + virtual bool installDaemon(/* FIXME */); + virtual bool uninstallDaemon(/* FIXME */); + virtual bool daemonize(const char* name); + virtual const char* getBasename(const char* pathname) const; + virtual CString getUserDirectory() const; + virtual CString getSystemDirectory() const; + virtual CString addPathComponent( + const CString& prefix, + const CString& suffix) const; + +private: + static void serviceLogger(int, const char*); +}; + +#endif diff --git a/synergy/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp similarity index 100% rename from synergy/CXWindowsClipboard.cpp rename to platform/CXWindowsClipboard.cpp diff --git a/synergy/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h similarity index 100% rename from synergy/CXWindowsClipboard.h rename to platform/CXWindowsClipboard.h diff --git a/synergy/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp similarity index 100% rename from synergy/CXWindowsScreen.cpp rename to platform/CXWindowsScreen.cpp diff --git a/synergy/CXWindowsScreen.h b/platform/CXWindowsScreen.h similarity index 100% rename from synergy/CXWindowsScreen.h rename to platform/CXWindowsScreen.h diff --git a/synergy/CXWindowsUtil.cpp b/platform/CXWindowsUtil.cpp similarity index 100% rename from synergy/CXWindowsUtil.cpp rename to platform/CXWindowsUtil.cpp diff --git a/synergy/CXWindowsUtil.h b/platform/CXWindowsUtil.h similarity index 100% rename from synergy/CXWindowsUtil.h rename to platform/CXWindowsUtil.h diff --git a/platform/IPlatform.h b/platform/IPlatform.h new file mode 100644 index 00000000..73769abd --- /dev/null +++ b/platform/IPlatform.h @@ -0,0 +1,45 @@ +#ifndef IPLATFORM_H +#define IPLATFORM_H + +#include "BasicTypes.h" +#include "CString.h" +#include "IInterface.h" + +class IPlatform : public IInterface { +public: + // manipulators + + // install/uninstall a daemon. + // FIXME -- throw on error? will get better error messages that way. + virtual bool installDaemon(/* FIXME */) = 0; + virtual bool uninstallDaemon(/* FIXME */) = 0; + + // daemonize. this should have the side effect of sending log + // messages to a system message logger since messages can no + // longer go to the console. returns true iff successful. + // the name is the name of the daemon. +// FIXME -- win32 services will require a more complex interface + virtual bool daemonize(const char* name) = 0; + + // accessors + + // find the basename in the given pathname + virtual const char* getBasename(const char* pathname) const = 0; + + // get the user's home directory. returns the empty string if + // this cannot be determined. + virtual CString getUserDirectory() const = 0; + + // get the system configuration file directory + virtual CString getSystemDirectory() const = 0; + + // concatenate pathname components with a directory separator + // between them. this should not check if the resulting path + // is longer than allowed by the system. we'll rely on the + // system calls to tell us that. + virtual CString addPathComponent( + const CString& prefix, + const CString& suffix) const = 0; +}; + +#endif diff --git a/platform/Makefile b/platform/Makefile new file mode 100644 index 00000000..26186b72 --- /dev/null +++ b/platform/Makefile @@ -0,0 +1,28 @@ +DEPTH=.. +include $(DEPTH)/Makecommon + +# +# target file +# +TARGET = platform + +# +# source files +# +LCXXINCS = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/synergy \ + $(NULL) +CXXFILES = \ + CPlatform.cpp \ + CXWindowsClipboard.cpp \ + CXWindowsScreen.cpp \ + CXWindowsUtil.cpp \ + $(NULL) + +targets: $(LIBTARGET) + +$(LIBTARGET): $(OBJECTS) $(DEPLIBS) + if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi + $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/server/Makefile b/server/Makefile index 717a9a1a..eca0dcd2 100644 --- a/server/Makefile +++ b/server/Makefile @@ -4,7 +4,7 @@ include $(DEPTH)/Makecommon # # target file # -TARGET = server +TARGET = synergyd # # source files @@ -16,6 +16,7 @@ LCXXINCS = \ -I$(DEPTH)/http \ -I$(DEPTH)/net \ -I$(DEPTH)/synergy \ + -I$(DEPTH)/platform \ $(NULL) CXXFILES = \ CConfig.cpp \ @@ -31,6 +32,7 @@ CXXFILES = \ # libraries we depend on # DEPLIBS = \ + $(LIBDIR)/libplatform.a \ $(LIBDIR)/libsynergy.a \ $(LIBDIR)/libnet.a \ $(LIBDIR)/libhttp.a \ diff --git a/server/server.cpp b/server/server.cpp index 671b1b7c..feaa4f5b 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -3,6 +3,7 @@ #include "CLog.h" #include "CMutex.h" #include "CNetwork.h" +#include "CPlatform.h" #include "CThread.h" #include "XThread.h" #include "ProtocolTypes.h" @@ -305,20 +306,13 @@ static void parse(int argc, char** argv) int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { + CPlatform platform; + + // save instance CMSWindowsScreen::init(instance); // get program name - pname = strrchr(argv[0], '/'); - if (pname == NULL) { - pname = argv[0]; - } - else { - ++pname; - } - const char* pname2 = strrchr(argv[0], '\\'); - if (pname2 != NULL && pname2 > pname) { - pname = pname2 + 1; - } + pname = platform.getBasename(argv[0]); // FIXME -- direct CLog to MessageBox @@ -360,98 +354,16 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) #elif defined(CONFIG_PLATFORM_UNIX) -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const char* s_configFileDefault = CONFIG_SYS_DIR CONFIG_NAME; - -static void daemonize() -{ - // fork so shell thinks we're done and so we're not a process - // group leader - switch (fork()) { - case -1: - // failed - log((CLOG_PRINT "failed to daemonize")); - exit(1); - - case 0: - // child - break; - - default: - // parent exits - exit(0); - } - - // become leader of a new session - setsid(); - - // chdir to root so we don't keep mounted filesystems points busy - chdir("/"); - - // mask off permissions for any but owner - umask(077); - - // close open files. we only expect stdin, stdout, stderr to be open. - close(0); - close(1); - close(2); - - // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use - // of standard I/O safely goes in the bit bucket. - open("/dev/null", O_RDWR); - dup(0); - dup(0); -} - -static void syslogOutputter(int priority, const char* msg) -{ - // convert priority - switch (priority) { - case CLog::kFATAL: - case CLog::kERROR: - priority = LOG_ERR; - break; - - case CLog::kWARNING: - priority = LOG_WARNING; - break; - - case CLog::kNOTE: - priority = LOG_NOTICE; - break; - - case CLog::kINFO: - priority = LOG_INFO; - break; - - default: - priority = LOG_DEBUG; - break; - } - - // log it - syslog(priority, "%s", msg); -} +#include // fork() +#include // wait() +#include // wait() int main(int argc, char** argv) { + CPlatform platform; + // get program name - pname = strrchr(argv[0], '/'); - if (pname == NULL) { - pname = argv[0]; - } - else { - ++pname; - } + pname = platform.getBasename(argv[0]); // parse command line parse(argc, argv); @@ -461,32 +373,30 @@ int main(int argc, char** argv) // get the user's home directory. use the effective user id // so a user can't get a setuid root program to load his file. bool loaded = false; - struct passwd* pwent = getpwuid(geteuid()); - if (pwent != NULL && pwent->pw_dir != NULL) { - // construct path if it isn't too long - if (strlen(pwent->pw_dir) + strlen(CONFIG_NAME) + 2 <= PATH_MAX) { - char path[PATH_MAX]; - strcpy(path, pwent->pw_dir); - strcat(path, "/"); - strcat(path, CONFIG_NAME); + CString path = platform.getUserDirectory(); + if (!path.empty()) { + // complete path + path = platform.addPathComponent(path, CONFIG_NAME); - // now try loading the user's configuration - loaded = loadConfig(path, false); - } + // now try loading the user's configuration + loaded = loadConfig(path.c_str(), false); } if (!loaded) { // try the system-wide config file - loadConfig(s_configFileDefault, false); + path = platform.getSystemDirectory(); + if (!path.empty()) { + path = platform.addPathComponent(path, CONFIG_NAME); + loadConfig(path.c_str(), false); + } } } // daemonize if requested if (s_daemon) { - daemonize(); - - // send log to syslog - openlog("synergy", 0, LOG_DAEMON); - CLog::setOutputter(&syslogOutputter); + if (!platform.daemonize("synergyd")) { + log((CLOG_CRIT "failed to daemonize")); + return 16; + } } // run the server. if running as a daemon then run it in a child diff --git a/server/server.dsp b/server/server.dsp index 9f201f65..46ac171a 100644 --- a/server/server.dsp +++ b/server/server.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\http" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -68,7 +68,7 @@ LINK32=link.exe # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\http" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/synergy.dsw b/synergy.dsw index 1d70ed66..13b664f3 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -56,6 +56,9 @@ Package=<4> Begin Project Dependency Project_Dep_Name synergy End Project Dependency + Begin Project Dependency + Project_Dep_Name platform + End Project Dependency }}} ############################################################################### @@ -123,6 +126,18 @@ Package=<4> ############################################################################### +Project: "platform"=.\platform\platform.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "server"=.\server\server.dsp - Package Owner=<4> Package=<5> @@ -135,6 +150,9 @@ Package=<4> Project_Dep_Name base End Project Dependency Begin Project Dependency + Project_Dep_Name http + End Project Dependency + Begin Project Dependency Project_Dep_Name io End Project Dependency Begin Project Dependency @@ -147,10 +165,10 @@ Package=<4> Project_Dep_Name synergy End Project Dependency Begin Project Dependency - Project_Dep_Name makehook + Project_Dep_Name platform End Project Dependency Begin Project Dependency - Project_Dep_Name http + Project_Dep_Name makehook End Project Dependency }}} diff --git a/synergy/Makefile b/synergy/Makefile index c09c122d..6c55ab69 100644 --- a/synergy/Makefile +++ b/synergy/Makefile @@ -21,9 +21,6 @@ CXXFILES = \ CProtocolUtil.cpp \ CClipboard.cpp \ CTCPSocketFactory.cpp \ - CXWindowsClipboard.cpp \ - CXWindowsScreen.cpp \ - CXWindowsUtil.cpp \ XScreen.cpp \ XSynergy.cpp \ $(NULL) diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp index cf76d74a..a6417067 100644 --- a/synergy/synergy.dsp +++ b/synergy/synergy.dsp @@ -95,14 +95,6 @@ SOURCE=.\CInputPacketStream.cpp # End Source File # Begin Source File -SOURCE=.\CMSWindowsClipboard.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreen.cpp -# End Source File -# Begin Source File - SOURCE=.\COutputPacketStream.cpp # End Source File # Begin Source File @@ -139,14 +131,6 @@ SOURCE=.\ClipboardTypes.h # End Source File # Begin Source File -SOURCE=.\CMSWindowsClipboard.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreen.h -# End Source File -# Begin Source File - SOURCE=.\COutputPacketStream.h # End Source File # Begin Source File diff --git a/test.cpp b/test.cpp deleted file mode 100644 index cd62450f..00000000 --- a/test.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "CTCPSocket.h" -#include "CTCPListenSocket.h" -#include "CNetworkAddress.h" -#include "IInputStream.h" -#include "IOutputStream.h" -#include "CThread.h" -#include "CFunctionJob.h" -#include - -// -// test -// - -SInt16 port = 50000; - -static void thread1(void*) -{ - try { - fprintf(stdout, "client started\n"); - CThread::sleep(1.0); - CNetworkAddress addr("127.0.0.1", port); - - fprintf(stdout, "client connecting\n"); - CTCPSocket* socket = new CTCPSocket; - socket->connect(addr); - - fprintf(stdout, "client connected (%p). waiting.\n", socket); - CThread::sleep(2.0); - - fprintf(stdout, "client sending message\n"); - static const char msg[] = "message from client\n"; - socket->getOutputStream()->write(msg, sizeof(msg) - 1); - socket->getOutputStream()->flush(); - - fprintf(stdout, "client waiting for reply\n"); - UInt8 buffer[4096]; - UInt32 n; - do { - n = socket->getInputStream()->read(buffer, sizeof(buffer) - 1); - buffer[n] = 0; - fprintf(stdout, "%s", buffer); - } while (n == 0 && memchr(buffer, '\n', n) == NULL); - - fprintf(stdout, "client closing\n"); - socket->close(); - delete socket; - fprintf(stdout, "client terminating\n"); - } - catch (XBase& e) { - fprintf(stderr, "exception: %s\n", e.what()); - } -} - -static void thread2(void*) -{ - try { - fprintf(stdout, "server started\n"); - CNetworkAddress addr("127.0.0.1", port); - CTCPListenSocket listenSocket; - listenSocket.bind(addr); - - fprintf(stdout, "server accepting\n"); - ISocket* socket = listenSocket.accept(); - fprintf(stdout, "server accepted %p\n", socket); - - UInt8 buffer[4096]; - UInt32 n; - do { - n = socket->getInputStream()->read(buffer, sizeof(buffer) - 1); - buffer[n] = 0; - fprintf(stdout, "%s", buffer); - } while (n == 0 && memchr(buffer, '\n', n) == NULL); - - fprintf(stdout, "server replying\n"); - static const char reply[] = "data received\n"; - socket->getOutputStream()->write(reply, sizeof(reply) - 1); - - fprintf(stdout, "server closing\n"); - socket->close(); - delete socket; - fprintf(stdout, "server terminating\n"); - } - catch (XBase& e) { - fprintf(stderr, "exception: %s\n", e.what()); - } -} - -static void thread3(void*) -{ - try { - fprintf(stdout, "### looking up address\n"); - CNetworkAddress addr("www.google.com", 80); - - fprintf(stdout, "### connecting\n"); - CTCPSocket* socket = new CTCPSocket; - socket->connect(addr); - - fprintf(stdout, "### sending message\n"); - static const char msg[] = "GET / HTTP/1.0\nAccept: */*\n\n"; - socket->getOutputStream()->write(msg, sizeof(msg) - 1); - socket->getOutputStream()->flush(); - socket->getOutputStream()->close(); - - fprintf(stdout, "### waiting for reply\n"); - UInt8 buffer[4096]; - UInt32 n; - do { - n = socket->getInputStream()->read(buffer, sizeof(buffer) - 1); - buffer[n] = 0; - fprintf(stdout, "%s", buffer); - } while (n != 0); - - fprintf(stdout, "### closing\n"); - socket->close(); - delete socket; - fprintf(stdout, "### terminating\n"); - } - catch (XBase& e) { - fprintf(stderr, "### exception: %s\n", e.what()); - } -} - -int main(int argc, char** argv) -{ -/* - if (argc > 1) { - port = (SInt16)atoi(argv[1]); - } - - fprintf(stdout, "starting threads\n"); - CThread t1(new CFunctionJob(thread1)); - CThread t2(new CFunctionJob(thread2)); - - fprintf(stdout, "waiting for threads\n"); - t1.wait(); - t2.wait(); - fprintf(stdout, "threads finished\n"); -*/ - int tick = 0; - CThread t3(new CFunctionJob(thread3)); - while (!t3.wait(0.1)) { - fprintf(stdout, "$$$ %d\n", ++tick); - } - return 0; -} From 4b28ffc5b265ad80a6ceae1d283c8b721144a3c4 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 8 Jun 2002 21:48:00 +0000 Subject: [PATCH 142/807] win32 changes. changed names of binaries. added support for running as (and installing/installing) a service. added support for multiple desktops (NT only, 95 doesn't support multiple desktops). --- all.dsp | 4 +- base/CLog.cpp | 6 +- base/CLog.h | 6 +- base/base.dsp | 4 +- base/common.h | 3 + base/stdpre.h | 1 + client/CClient.cpp | 5 + client/CClient.h | 4 + client/CMSWindowsSecondaryScreen.cpp | 384 ++++++++++--- client/CMSWindowsSecondaryScreen.h | 41 +- client/client.cpp | 494 ++++++++++++----- client/client.dsp | 10 +- http/http.dsp | 4 +- io/io.dsp | 4 +- mt/CThreadRep.cpp | 8 - mt/mt.dsp | 4 +- net/net.dsp | 4 +- platform/CMSWindowsScreen.cpp | 32 ++ platform/CMSWindowsScreen.h | 23 +- platform/CUnixPlatform.cpp | 61 ++- platform/CUnixPlatform.h | 12 +- platform/CWin32Platform.cpp | 786 ++++++++++++++++++++++++++- platform/CWin32Platform.h | 77 ++- platform/IPlatform.h | 39 +- platform/platform.dsp | 139 +++++ server/CMSWindowsPrimaryScreen.cpp | 679 +++++++++++++++-------- server/CMSWindowsPrimaryScreen.h | 63 ++- server/CServer.cpp | 28 +- server/CSynergyHook.cpp | 179 +++--- server/CSynergyHook.h | 8 +- server/makehook.dsp | 4 +- server/server.cpp | 668 +++++++++++++++-------- server/server.dsp | 10 +- server/synrgyhk.dsp | 4 +- synergy/IPrimaryScreen.h | 1 + synergy/synergy.dsp | 4 +- 36 files changed, 2948 insertions(+), 855 deletions(-) create mode 100644 platform/platform.dsp diff --git a/all.dsp b/all.dsp index 902d872b..0c7635eb 100644 --- a/all.dsp +++ b/all.dsp @@ -23,8 +23,8 @@ CFG=all - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" MTL=midl.exe !IF "$(CFG)" == "all - Win32 Release" diff --git a/base/CLog.cpp b/base/CLog.cpp index 2b519a6e..70c2955e 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -201,10 +201,8 @@ void CLog::output(int priority, char* msg) // print it CHoldLock lock(s_lock); - if (s_outputter) { - s_outputter(priority, msg + g_maxPriorityLength - n); - } - else { + if (s_outputter == NULL || + !s_outputter(priority, msg + g_maxPriorityLength - n)) { #if defined(CONFIG_PLATFORM_WIN32) openConsole(); #endif diff --git a/base/CLog.h b/base/CLog.h index 0e53e7c1..22a34357 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -17,7 +17,11 @@ public: kDEBUG2 }; - typedef void (*Outputter)(int priority, const char*); + // type of outputter function. return false if CLog should use + // the default outputter, true otherwise. + typedef bool (*Outputter)(int priority, const char*); + + // type of lock/unlock function typedef void (*Lock)(bool lock); // diff --git a/base/base.dsp b/base/base.dsp index ef498104..b3495e9e 100644 --- a/base/base.dsp +++ b/base/base.dsp @@ -23,8 +23,8 @@ CFG=base - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe diff --git a/base/common.h b/base/common.h index 08f02331..c29d7543 100644 --- a/base/common.h +++ b/base/common.h @@ -27,6 +27,9 @@ #pragma warning(disable: 4786) // identifier truncated in debug info #pragma warning(disable: 4514) // unreferenced inline function removed +// this one's a little too aggressive +#pragma warning(disable: 4127) // conditional expression is constant + #endif // (_MSC_VER >= 1200) #else diff --git a/base/stdpre.h b/base/stdpre.h index 4461b475..c51292a4 100644 --- a/base/stdpre.h +++ b/base/stdpre.h @@ -8,4 +8,5 @@ #pragma warning(disable: 4284) #pragma warning(disable: 4146) // unary minus on unsigned value #pragma warning(disable: 4127) // conditional expression is constant +#pragma warning(disable: 4701) // variable possibly used uninitialized #endif diff --git a/client/CClient.cpp b/client/CClient.cpp index 09e78f6c..61ed3f54 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -120,6 +120,11 @@ void CClient::run(const CNetworkAddress& serverAddress) } } +void CClient::quit() +{ + m_screen->stop(); +} + void CClient::onClipboardChanged(ClipboardID id) { log((CLOG_DEBUG "sending clipboard %d changed", id)); diff --git a/client/CClient.h b/client/CClient.h index 9920a45c..30bcee50 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -19,8 +19,12 @@ public: // manipulators + // start the client. does not return until quit() is called. void run(const CNetworkAddress& serverAddress); + // tell client to exit gracefully + void quit(); + // handle events on client's screen void onClipboardChanged(ClipboardID); void onResolutionChanged(); diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 26a97fa5..4cc19a4f 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -1,9 +1,12 @@ #include "CMSWindowsSecondaryScreen.h" #include "CMSWindowsClipboard.h" #include "CClient.h" +#include "CPlatform.h" #include "CClipboard.h" -#include "CThread.h" +#include "CLock.h" #include "CLog.h" +#include "CThread.h" +#include "XScreen.h" #include #include @@ -13,10 +16,18 @@ CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : m_client(NULL), + m_threadID(0), + m_desk(NULL), + m_deskName(), m_window(NULL), + m_active(false), m_nextClipboardWindow(NULL) { - // do nothing + m_is95Family = CPlatform::isWindows95Family(); + + // make sure this thread has a message queue + MSG dummy; + PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); } CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() @@ -26,21 +37,32 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() void CMSWindowsSecondaryScreen::run() { + // must call run() from same thread as open() + assert(m_threadID == GetCurrentThreadId()); + // change our priority CThread::getCurrentThread().setPriority(-7); - // save thread id - m_threadID = GetCurrentThreadId(); + // poll input desktop to see if it changes (onPreTranslate() + // handles WM_TIMER) + UINT timer = 0; + if (!m_is95Family) { + SetTimer(NULL, 0, 200, NULL); + } // run event loop log((CLOG_INFO "entering event loop")); doRun(); log((CLOG_INFO "exiting event loop")); + + // remove timer + if (!m_is95Family) { + KillTimer(NULL, timer); + } } void CMSWindowsSecondaryScreen::stop() { - log((CLOG_INFO "requesting event loop stop")); doStop(); } @@ -49,8 +71,6 @@ void CMSWindowsSecondaryScreen::open(CClient* client) assert(m_client == NULL); assert(client != NULL); - log((CLOG_INFO "opening screen")); - // set the client m_client = client; @@ -64,14 +84,16 @@ void CMSWindowsSecondaryScreen::open(CClient* client) // assume primary has all clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) grabClipboard(id); + + // hide the cursor + m_active = true; + leave(); } void CMSWindowsSecondaryScreen::close() { assert(m_client != NULL); - log((CLOG_INFO "closing screen")); - // close the display closeDisplay(); @@ -82,12 +104,16 @@ void CMSWindowsSecondaryScreen::close() void CMSWindowsSecondaryScreen::enter( SInt32 x, SInt32 y, KeyModifierMask mask) { + CLock lock(&m_mutex); assert(m_window != NULL); + assert(m_active == false); log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); - // attach thread input queues - AttachThreadInput(GetCurrentThreadId(), m_threadID, TRUE); + syncDesktop(); + + // now active + m_active = true; // update our keyboard state to reflect the local state updateKeys(); @@ -104,34 +130,25 @@ void CMSWindowsSecondaryScreen::enter( toggleKey(VK_SCROLL, KeyModifierScrollLock); } - // warp to requested location - SInt32 w, h; - getScreenSize(&w, &h); - mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65535.99 * x) / (w - 1)), - (DWORD)((65535.99 * y) / (h - 1)), - 0, 0); - - // show cursor - log((CLOG_INFO "show cursor")); - ShowWindow(m_window, SW_HIDE); + // hide mouse + onEnter(x, y); } void CMSWindowsSecondaryScreen::leave() { + CLock lock(&m_mutex); assert(m_window != NULL); + assert(m_active == true); log((CLOG_INFO "leaving screen")); - // move hider window under the mouse (rather than moving the mouse - // somewhere else on the screen) - POINT point; - GetCursorPos(&point); - MoveWindow(m_window, point.x, point.y, 1, 1, FALSE); + syncDesktop(); - // raise and show the hider window. take activation. - log((CLOG_INFO "hide cursor")); - ShowWindow(m_window, SW_SHOWNORMAL); + // hide mouse + onLeave(); + + // not active anymore + m_active = false; // if we think we own the clipboard but we don't then somebody // grabbed the clipboard on this screen without us knowing. @@ -160,6 +177,10 @@ void CMSWindowsSecondaryScreen::keyDown( Keystrokes keys; UINT virtualKey; + CLock lock(&m_mutex); + assert(m_window != NULL); + syncDesktop(); + // get the sequence of keys to simulate key press and the final // modifier state. m_mask = mapKey(keys, virtualKey, key, mask, kPress); @@ -195,6 +216,10 @@ void CMSWindowsSecondaryScreen::keyRepeat( Keystrokes keys; UINT virtualKey; + CLock lock(&m_mutex); + assert(m_window != NULL); + syncDesktop(); + // get the sequence of keys to simulate key repeat and the final // modifier state. m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); @@ -211,6 +236,10 @@ void CMSWindowsSecondaryScreen::keyUp( Keystrokes keys; UINT virtualKey; + CLock lock(&m_mutex); + assert(m_window != NULL); + syncDesktop(); + // get the sequence of keys to simulate key release and the final // modifier state. m_mask = mapKey(keys, virtualKey, key, mask, kRelease); @@ -263,6 +292,10 @@ void CMSWindowsSecondaryScreen::keyUp( void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) { + CLock lock(&m_mutex); + assert(m_window != NULL); + syncDesktop(); + // map button id to button flag DWORD flags = mapButton(button, true); @@ -273,6 +306,10 @@ void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) { + CLock lock(&m_mutex); + assert(m_window != NULL); + syncDesktop(); + // map button id to button flag DWORD flags = mapButton(button, false); @@ -281,8 +318,13 @@ void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) mouse_event(flags, 0, 0, 0, 0); } -void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +void CMSWindowsSecondaryScreen::mouseMove( + SInt32 x, SInt32 y) { + CLock lock(&m_mutex); + assert(m_window != NULL); + syncDesktop(); + SInt32 w, h; getScreenSize(&w, &h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, @@ -293,20 +335,27 @@ void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) { + CLock lock(&m_mutex); + assert(m_window != NULL); + syncDesktop(); + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); } void CMSWindowsSecondaryScreen::setClipboard( - ClipboardID id, const IClipboard* src) + ClipboardID /*id*/, const IClipboard* src) { + CLock lock(&m_mutex); assert(m_window != NULL); CMSWindowsClipboard dst(m_window); CClipboard::copy(&dst, src); } -void CMSWindowsSecondaryScreen::grabClipboard(ClipboardID id) +void CMSWindowsSecondaryScreen::grabClipboard( + ClipboardID /*id*/) { + CLock lock(&m_mutex); assert(m_window != NULL); CMSWindowsClipboard clipboard(m_window); @@ -321,6 +370,10 @@ void CMSWindowsSecondaryScreen::getMousePos( assert(x != NULL); assert(y != NULL); + CLock lock(&m_mutex); + assert(m_window != NULL); + syncDesktop(); + POINT pos; if (GetCursorPos(&pos)) { *x = pos.x; @@ -344,8 +397,9 @@ SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const } void CMSWindowsSecondaryScreen::getClipboard( - ClipboardID id, IClipboard* dst) const + ClipboardID /*id*/, IClipboard* dst) const { + CLock lock(&m_mutex); assert(m_window != NULL); CMSWindowsClipboard src(m_window); @@ -356,44 +410,59 @@ void CMSWindowsSecondaryScreen::onOpenDisplay() { assert(m_window == NULL); - // initialize clipboard owner to current owner. we don't want - // to take ownership of the clipboard just by starting up. - m_clipboardOwner = GetClipboardOwner(); + // save thread id. we'll need to pass this to the hook library. + m_threadID = GetCurrentThreadId(); - // create the cursor hiding window. this window is used to hide the - // cursor when it's not on the screen. the window is hidden as soon - // as the cursor enters the screen or the display's real cursor is - // moved. - m_window = CreateWindowEx(WS_EX_TOPMOST | - WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - (LPCTSTR)getClass(), "Synergy", - WS_POPUP, - 0, 0, 1, 1, NULL, NULL, - getInstance(), - NULL); - - // hide the cursor - leave(); - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); + // get the input desktop and switch to it + if (m_is95Family) { + if (!openDesktop()) { + throw XScreenOpenFailure(); + } + } + else { + if (!switchDesktop(openInputDesktop())) { + throw XScreenOpenFailure(); + } + } } void CMSWindowsSecondaryScreen::onCloseDisplay() { - assert(m_window != NULL); + // disconnect from desktop + if (m_is95Family) { + closeDesktop(); + } + else { + switchDesktop(NULL); + } - // remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; + // clear thread id + m_threadID = 0; - // destroy window - DestroyWindow(m_window); - m_window = NULL; + assert(m_window == NULL); + assert(m_desk == NULL); } bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg) { + // handle event + switch (msg->message) { + case WM_TIMER: + // if current desktop is not the input desktop then switch to it + if (!m_is95Family) { + HDESK desk = openInputDesktop(); + if (desk != NULL) { + if (isCurrentDesktop(desk)) { + CloseDesktop(desk); + } + else { + switchDesktop(desk); + } + } + } + return true; + } + return false; } @@ -402,7 +471,21 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( WPARAM wParam, LPARAM lParam) { switch (msg) { - // FIXME -- handle display changes + case WM_QUERYENDSESSION: + if (m_is95Family) { + return TRUE; + } + break; + + case WM_ENDSESSION: + if (m_is95Family) { + if (wParam == TRUE && lParam == 0) { + stop(); + } + return 0; + } + break; + case WM_PAINT: ValidateRect(hwnd, NULL); return 0; @@ -410,7 +493,6 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( case WM_ACTIVATEAPP: if (wParam == FALSE) { // some other app activated. hide the hider window. - log((CLOG_INFO "show cursor")); ShowWindow(m_window, SW_HIDE); } break; @@ -423,9 +505,11 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( // now notify client that somebody changed the clipboard (unless // we're now the owner, in which case it's because we took - // ownership). + // ownership, or now it's owned by nobody, which will happen if + // we owned it and switched desktops because we destroy our + // window to do that). m_clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != m_window) { + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { m_client->onClipboardChanged(kClipboardClipboard); m_client->onClipboardChanged(kClipboardSelection); } @@ -448,6 +532,176 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( return DefWindowProc(hwnd, msg, wParam, lParam); } +void CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y) +{ + // warp to requested location + SInt32 w, h; + getScreenSize(&w, &h); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65535.99 * x) / (w - 1)), + (DWORD)((65535.99 * y) / (h - 1)), + 0, 0); + + // show cursor + ShowWindow(m_window, SW_HIDE); +} + +void CMSWindowsSecondaryScreen::onLeave() +{ + // move hider window under the mouse (rather than moving the mouse + // somewhere else on the screen) + POINT point; + GetCursorPos(&point); + MoveWindow(m_window, point.x, point.y, 1, 1, FALSE); + + // raise and show the hider window. take activation. + ShowWindow(m_window, SW_SHOWNORMAL); +} + +bool CMSWindowsSecondaryScreen::openDesktop() +{ + CLock lock(&m_mutex); + + // initialize clipboard owner to current owner. we don't want + // to take ownership of the clipboard just by starting up. + m_clipboardOwner = GetClipboardOwner(); + + // create the cursor hiding window. this window is used to hide the + // cursor when it's not on the screen. the window is hidden as soon + // as the cursor enters the screen or the display's real cursor is + // moved. + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, + (LPCTSTR)getClass(), "Synergy", + WS_POPUP, + 0, 0, 1, 1, NULL, NULL, + getInstance(), + NULL); + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + + return true; +} + +void CMSWindowsSecondaryScreen::closeDesktop() +{ + CLock lock(&m_mutex); + + if (m_window != NULL) { + // remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + // destroy window + DestroyWindow(m_window); + m_window = NULL; + } +} + +bool CMSWindowsSecondaryScreen::switchDesktop(HDESK desk) +{ + CLock lock(&m_mutex); + + bool ownClipboard = false; + if (m_window != NULL) { + // note if we own the clipboard + ownClipboard = (m_clipboardOwner == m_window); + + // remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + // destroy window + DestroyWindow(m_window); + m_window = NULL; + } + + // done with desktop + if (m_desk != NULL) { + CloseDesktop(m_desk); + m_desk = NULL; + m_deskName = ""; + } + + // if no new desktop then we're done + if (desk == NULL) { + log((CLOG_INFO "disconnecting desktop")); + return true; + } + + // set the desktop. can only do this when there are no windows + // and hooks on the current desktop owned by this thread. + if (SetThreadDesktop(desk) == 0) { + log((CLOG_ERR "failed to set desktop: %d", GetLastError())); + CloseDesktop(desk); + return false; + } + + // initialize clipboard owner to current owner. we don't want + // to take ownership of the clipboard just by starting up. + m_clipboardOwner = GetClipboardOwner(); + + // create the cursor hiding window. this window is used to hide the + // cursor when it's not on the screen. the window is hidden as soon + // as the cursor enters the screen or the display's real cursor is + // moved. + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, + (LPCTSTR)getClass(), "Synergy", + WS_POPUP, + 0, 0, 1, 1, NULL, NULL, + getInstance(), + NULL); + if (m_window == NULL) { + log((CLOG_ERR "failed to create window: %d", GetLastError())); + CloseDesktop(desk); + return false; + } + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + + // if we owned the desktop then set the clipboard owner + if (ownClipboard) { + m_clipboardOwner = GetClipboardOwner(); + } + + // save new desktop + m_desk = desk; + m_deskName = getDesktopName(m_desk); + log((CLOG_INFO "switched to desktop %s", m_deskName.c_str())); + + // get desktop up to date + if (!m_active) { + onLeave(); + } + + return true; +} + +void CMSWindowsSecondaryScreen::syncDesktop() const +{ + // note -- mutex must be locked on entry + + DWORD threadID = GetCurrentThreadId(); + if (!m_is95Family) { + if (GetThreadDesktop(threadID) != m_desk) { + // FIXME -- this doesn't work. if we set a desktop then + // sending events doesn't work. + if (SetThreadDesktop(m_desk) == 0) { + log((CLOG_ERR "failed to set desktop: %d", GetLastError())); + } + } + } + AttachThreadInput(threadID, m_threadID, TRUE); +} + +CString CMSWindowsSecondaryScreen::getCurrentDesktopName() const +{ + return m_deskName; +} + // these tables map KeyID (a X windows KeySym) to virtual key codes. // if the key is an extended key then the entry is the virtual key // code | 0x100. keys that map to normal characters have a 0 entry diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 258853b5..d8cce84b 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -3,7 +3,8 @@ #include "CMSWindowsScreen.h" #include "ISecondaryScreen.h" -#include "stdmap.h" +#include "CMutex.h" +#include "CString.h" #include "stdvector.h" class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen { @@ -39,6 +40,7 @@ protected: virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); virtual void onOpenDisplay(); virtual void onCloseDisplay(); + virtual CString getCurrentDesktopName() const; private: enum EKeyAction { kPress, kRelease, kRepeat }; @@ -49,7 +51,21 @@ private: bool m_repeat; }; typedef std::vector Keystrokes; - + + void onEnter(SInt32 x, SInt32 y); + void onLeave(); + + // open/close desktop (for windows 95/98/me) + bool openDesktop(); + void closeDesktop(); + + // make desk the thread desktop (for windows NT/2000/XP) + bool switchDesktop(HDESK desk); + + // get calling thread to use the input desktop + void syncDesktop() const; + + // key and button queries and operations DWORD mapButton(ButtonID button, bool press) const; KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, KeyModifierMask, EKeyAction) const; @@ -63,14 +79,29 @@ private: void sendKeyEvent(UINT virtualKey, bool press); private: + CMutex m_mutex; CClient* m_client; + + // true if windows 95/98/me + bool m_is95Family; + + // the main loop's thread id + DWORD m_threadID; + + // the current desk and it's name + HDESK m_desk; + CString m_deskName; + + // our window (for getting clipboard changes) HWND m_window; + + // m_active is true if this screen has been entered + bool m_active; + + // clipboard stuff HWND m_nextClipboardWindow; HWND m_clipboardOwner; - // thread id of the event loop thread - DWORD m_threadID; - // virtual key states BYTE m_keys[256]; diff --git a/client/client.cpp b/client/client.cpp index da943c18..6297ad38 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -1,6 +1,8 @@ #include "CClient.h" #include "CString.h" #include "CLog.h" +#include "CCondVar.h" +#include "CLock.h" #include "CMutex.h" #include "CNetwork.h" #include "CNetworkAddress.h" @@ -11,6 +13,15 @@ #include "Version.h" #include +// platform dependent name of a daemon +#if defined(CONFIG_PLATFORM_WIN32) +#define DAEMON "service" +#define DAEMON_NAME "Synergy Client" +#elif defined(CONFIG_PLATFORM_UNIX) +#define DAEMON "daemon" +#define DAEMON_NAME "synergy" +#endif + // // program arguments // @@ -18,6 +29,8 @@ static const char* pname = NULL; static bool s_restartable = true; static bool s_daemon = true; +static bool s_install = false; +static bool s_uninstall = false; static const char* s_logFilter = NULL; static const char* s_serverName = NULL; @@ -42,44 +55,91 @@ static void logLock(bool lock) // -// main +// platform independent main // -void realMain(const CString& name, - const CString& hostname, - UInt16 port) +static CClient* s_client = NULL; + +static int realMain(CMutex* mutex) { - // initialize threading library - CThread::init(); + static const UInt16 port = 50001; // FIXME - // make logging thread safe - CMutex logMutex; - s_logMutex = &logMutex; - CLog::setLock(&logLock); - - CClient* client = NULL; try { - // initialize network library - CNetwork::init(); + // initialize threading library + CThread::init(); - // run client - CNetworkAddress addr(hostname, port); - client = new CClient(name); - client->run(addr); + // make logging thread safe + CMutex logMutex; + s_logMutex = &logMutex; + CLog::setLock(&logLock); - // clean up - delete client; - CNetwork::cleanup(); - CLog::setLock(NULL); - s_logMutex = NULL; + bool locked = true; + try { + // initialize network library + CNetwork::init(); + + // create client + CNetworkAddress addr(s_serverName, port); + s_client = new CClient("secondary"); // FIXME + + // run client + if (mutex != NULL) { + mutex->unlock(); + } + locked = false; + s_client->run(addr); + locked = true; + if (mutex != NULL) { + mutex->lock(); + } + + // clean up + delete s_client; + s_client = NULL; + CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; + } + catch (...) { + // clean up + if (!locked && mutex != NULL) { + mutex->lock(); + } + delete s_client; + s_client = NULL; + CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; + throw; + } } - catch (...) { - // clean up - delete client; - CNetwork::cleanup(); - CLog::setLock(NULL); - s_logMutex = NULL; - throw; + catch (XBase& e) { + log((CLOG_CRIT "failed: %s", e.what())); + return 16; + } + catch (XThread&) { + // terminated + return 1; + } + + return 0; +} + +static int restartMain() +{ + return realMain(NULL); +} + +// invoke realMain and wait for it. if s_restartable then keep +// restarting realMain until it returns a terminate code. +static int restartableMain() +{ + if (s_restartable) { + CPlatform platform; + return platform.restart(restartMain, 16); + } + else { + return realMain(NULL); } } @@ -88,11 +148,9 @@ void realMain(const CString& name, // command line parsing // -static void bye() -{ - log((CLOG_PRINT "Try `%s --help' for more information.", pname)); - exit(1); -} +#define BYE "\nTry `%s --help' for more information." + +static void (*bye)(int) = &exit; static void version() { @@ -113,32 +171,37 @@ static void help() log((CLOG_PRINT "Usage: %s" " [--debug ]" -" [--daemon|--no-daemon]" +" [--"DAEMON"|--no-"DAEMON"]" " [--restart|--no-restart]" +" [--install]" " \n" +"or\n" +" --uninstall\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" -" -f, --no-daemon run the client in the foreground.\n" -" --daemon run the client as a daemon.\n" +" -f, --no-"DAEMON" run the client in the foreground.\n" +"* --"DAEMON" run the client as a "DAEMON".\n" " -1, --no-restart do not try to restart the client if it fails for\n" " some reason.\n" -" --restart restart the client automatically if it fails.\n" +"* --restart restart the client automatically if it fails.\n" +" --install install server as a "DAEMON".\n" +" --uninstall uninstall server "DAEMON".\n" " -h, --help display this help and exit.\n" " --version display version information and exit.\n" "\n" -"By default, the client is a restartable daemon.\n" +"* marks defaults.\n" "\n" "Where log messages go depends on the platform and whether or not the\n" -"client is running as a daemon.", +"client is running as a "DAEMON".", pname)); } static bool isArg(int argi, - int argc, char** argv, + int argc, const char** argv, const char* name1, const char* name2, int minRequiredParameters = 0) @@ -147,9 +210,9 @@ static bool isArg(int argi, (name2 != NULL && strcmp(argv[argi], name2) == 0)) { // match. check args left. if (argi + minRequiredParameters >= argc) { - log((CLOG_PRINT "%s: missing arguments for `%s'", - pname, argv[argi])); - bye(); + log((CLOG_PRINT "%s: missing arguments for `%s'" BYE, + pname, argv[argi], pname)); + bye(2); } return true; } @@ -158,7 +221,7 @@ static bool isArg(int argi, return false; } -static void parse(int argc, char** argv) +static void parse(int argc, const char** argv) { assert(pname != NULL); assert(argv != NULL); @@ -172,12 +235,12 @@ static void parse(int argc, char** argv) s_logFilter = argv[++i]; } - else if (isArg(i, argc, argv, "-f", "--no-daemon")) { + else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) { // not a daemon s_daemon = false; } - else if (isArg(i, argc, argv, NULL, "--daemon")) { + else if (isArg(i, argc, argv, NULL, "--"DAEMON)) { // daemonize s_daemon = true; } @@ -194,12 +257,42 @@ static void parse(int argc, char** argv) else if (isArg(i, argc, argv, "-h", "--help")) { help(); - exit(1); + bye(0); } else if (isArg(i, argc, argv, NULL, "--version")) { version(); - exit(1); + bye(0); + } + + else if (isArg(i, argc, argv, NULL, "--install")) { +#if !defined(CONFIG_PLATFORM_WIN32) + log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE, + pname, argv[i], pname)); + bye(2); +#endif + s_install = true; + if (s_uninstall) { + log((CLOG_PRINT "%s: `--install' and `--uninstall'" + " are mutually exclusive" BYE, + pname, argv[i], pname)); + bye(2); + } + } + + else if (isArg(i, argc, argv, NULL, "--uninstall")) { +#if !defined(CONFIG_PLATFORM_WIN32) + log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE, + pname, argv[i], pname)); + bye(2); +#endif + s_uninstall = true; + if (s_install) { + log((CLOG_PRINT "%s: `--install' and `--uninstall'" + " are mutually exclusive" BYE, + pname, argv[i], pname)); + bye(2); + } } else if (isArg(i, argc, argv, "--", NULL)) { @@ -209,8 +302,9 @@ static void parse(int argc, char** argv) } else if (argv[i][0] == '-') { - log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); - bye(); + log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + pname, argv[i], pname)); + bye(2); } else { @@ -219,21 +313,51 @@ static void parse(int argc, char** argv) } } - // exactly one non-option argument: server-address - if (i == argc) { - log((CLOG_PRINT "%s: a server address or name is required", pname)); - bye(); + // exactly one non-option argument (server-address) unless using + // --uninstall. + if (s_uninstall) { + if (i != argc) { + log((CLOG_PRINT "%s: unrecognized option `%s' to `%s'" BYE, + pname, argv[i], pname, + s_install ? "--install" : "--uninstall")); + bye(2); + } } - if (i + 1 != argc) { - log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); - bye(); + else { + if (i == argc) { + log((CLOG_PRINT "%s: a server address or name is required" BYE, + pname, pname)); + bye(1); + } + if (i + 1 != argc) { + log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + pname, argv[i], pname)); + bye(2); + } + s_serverName = argv[i]; + } + + // increase default filter level for daemon. the user must + // explicitly request another level for a daemon. + if (s_daemon && s_logFilter == NULL) { +#if defined(CONFIG_PLATFORM_WIN32) + if (CPlatform::isWindows95Family()) { + // windows 95 has no place for logging so avoid showing + // the log console window. + s_logFilter = "FATAL"; + } + else +#endif + { + s_logFilter = "NOTE"; + } } - s_serverName = argv[i]; // set log filter if (!CLog::setFilter(s_logFilter)) { - log((CLOG_PRINT "%s: unrecognized log level `%s'", pname, s_logFilter)); - bye(); + log((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, + pname, s_logFilter, pname)); + bye(2); } } @@ -245,7 +369,71 @@ static void parse(int argc, char** argv) #if defined(CONFIG_PLATFORM_WIN32) #include "CMSWindowsScreen.h" -#include + +static bool logMessageBox(int priority, const char* msg) +{ + if (priority <= CLog::kFATAL) { + MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); + return true; + } + else { + return false; + } +} + +static void byeThrow(int x) +{ + throw CWin32Platform::CDaemonFailed(x); +} + +static void daemonStop(void) +{ + s_client->quit(); +} + +static int daemonStartup(IPlatform* iplatform, + int argc, const char** argv) +{ + // get platform pointer + CWin32Platform* platform = static_cast(iplatform); + + // catch errors that would normally exit + bye = &byeThrow; + + // parse command line + s_install = false; + s_uninstall = false; + parse(argc, argv); + if (s_install || s_uninstall) { + // not allowed to install/uninstall from service + throw CWin32Platform::CDaemonFailed(1); + } + + // run as a service + return platform->runDaemon(realMain, daemonStop); +} + +static int daemonStartup95(IPlatform*, int, const char**) +{ + return realMain(NULL); +} + +static bool logDiscard(int, const char*) +{ + return true; +} + +static bool s_die = false; + +static void checkParse(int e) +{ + // anything over 1 means invalid args. 1 means missing args. + // 0 means graceful exit. we plan to exit for anything but + // 1 (missing args); the service control manager may supply + // the missing arguments so we don't exit in that case. + s_die = (e != 1); + throw s_die; +} int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { @@ -255,40 +443,115 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) CMSWindowsScreen::init(instance); // get program name - pname = platform.getBasename(argv[0]); + pname = platform.getBasename(__argv[0]); -// FIXME -- direct CLog to MessageBox - - parse(__argc, __argv); - -// FIXME -- undirect CLog from MessageBox -// FIXME -- if daemon then use win32 event log (however that's done), -// otherwise do what? want to use console window for debugging but -// not otherwise. - -// FIXME + // parse command line without reporting errors but recording if + // the app would've exited. this is too avoid showing a dialog + // box if we're being started as a service because we shouldn't + // take too long to startup in that case. this mostly works but + // will choke if the service control manager passes --install + // or --uninstall (but that's unlikely). + CLog::setOutputter(&logDiscard); + bye = &checkParse; try { - realMain("secondary", s_serverName, 50001); + parse(__argc, const_cast(__argv)); + } + catch (...) { + // ignore + } + + // if we're not starting as an NT service then reparse the command + // line normally. + if (s_die || !s_daemon || s_install || s_uninstall || + CWin32Platform::isWindows95Family()) { + // send PRINT and FATAL output to a message box + CLog::setOutputter(&logMessageBox); + + // exit on bye + bye = &exit; + + // reparse + parse(__argc, const_cast(__argv)); + } + + // if starting as a daemon then we ignore the startup command line + // here. we'll parse the command line passed in when the service + // control manager calls us back. + else { + // do nothing + } + + // install/uninstall + if (s_install) { + // get the full path to this program + TCHAR path[MAX_PATH]; + if (GetModuleFileName(NULL, path, + sizeof(path) / sizeof(path[0])) == 0) { + log((CLOG_CRIT "cannot determine absolute path to program")); + return 16; + } + + // construct the command line to start the service with + CString commandLine = "--"DAEMON; + if (s_restartable) { + commandLine += " --restart"; + } + else { + commandLine += " --no-restart"; + } + if (s_logFilter != NULL) { + commandLine += " --debug "; + commandLine += s_logFilter; + } + commandLine += " "; + commandLine += s_serverName; + + // install + if (!platform.installDaemon(DAEMON_NAME, + "Shares this system's mouse and keyboard with others.", + path, commandLine.c_str())) { + log((CLOG_CRIT "failed to install service")); + return 16; + } + log((CLOG_PRINT "installed successfully")); return 0; } - catch (XBase& e) { - log((CLOG_CRIT "failed: %s", e.what())); - CString msg = "failed: "; - msg += e.what(); - MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); - return 1; + else if (s_uninstall) { + if (!platform.uninstallDaemon(DAEMON_NAME)) { + log((CLOG_CRIT "failed to uninstall service")); + return 16; + } + log((CLOG_PRINT "uninstalled successfully")); + return 0; } - catch (XThread&) { - // terminated - return 1; + + // daemonize if requested + int result; + if (s_daemon) { + if (CWin32Platform::isWindows95Family()) { + result = platform.daemonize(DAEMON_NAME, &daemonStartup95); + } + else { + result = platform.daemonize(DAEMON_NAME, &daemonStartup); + } + if (result == -1) { + log((CLOG_CRIT "failed to start as a service")); + return 16; + } } + else { + result = restartableMain(); + } + + return result; } #elif defined(CONFIG_PLATFORM_UNIX) -#include -#include -#include +static int daemonStartup(IPlatform*, int, const char**) +{ + return restartableMain(); +} int main(int argc, char** argv) { @@ -298,63 +561,22 @@ int main(int argc, char** argv) pname = platform.getBasename(argv[0]); // parse command line - parse(argc, argv); + parse(argc, const_cast(argv)); // daemonize if requested + int result; if (s_daemon) { - if (!platform.daemonize("synergy")) { + result = platform.daemonize(DAEMON_NAME, &daemonStartup); + if (result == -1) { log((CLOG_CRIT "failed to daemonize")); return 16; } } - - // run the server. if running as a daemon then run it in a child - // process and restart it as necessary. we have to do this in case - // the X server restarts because our process cannot recover from - // that. - for (;;) { - // don't fork if not restartable - switch (s_restartable ? fork() : 0) { - default: { - // parent process. wait for child to exit. - int status; - if (wait(&status) == -1) { - // wait failed. this is unexpected so bail. - log((CLOG_CRIT "wait() failed")); - return 16; - } - - // what happened? if the child exited normally with a - // status less than 16 then the child was deliberately - // terminated so we also terminate. otherwise, we - // loop. - if (WIFEXITED(status) && WEXITSTATUS(status) < 16) { - return 0; - } - break; - } - - case -1: - // fork() failed. log the error and proceed as a child - log((CLOG_WARN "fork() failed; cannot automatically restart on error")); - // fall through - - case 0: - // child process - try { - realMain("secondary", s_serverName, 50001); - return 0; - } - catch (XBase& e) { - log((CLOG_CRIT "failed: %s", e.what())); - return 16; - } - catch (XThread&) { - // terminated - return 1; - } - } + else { + result = restartableMain(); } + + return result; } #else diff --git a/client/client.dsp b/client/client.dsp index 682006b2..e5755909 100644 --- a/client/client.dsp +++ b/client/client.dsp @@ -23,8 +23,8 @@ CFG=client - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe @@ -40,6 +40,7 @@ RSC=rc.exe # PROP Use_Debug_Libraries 0 # PROP Output_Dir "../Release" # PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c @@ -53,7 +54,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../Release/synergy.exe" !ELSEIF "$(CFG)" == "client - Win32 Debug" @@ -66,6 +67,7 @@ LINK32=link.exe # PROP Use_Debug_Libraries 1 # PROP Output_Dir "../Debug" # PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c @@ -79,7 +81,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../Debug/synergy.exe" /pdbtype:sept !ENDIF diff --git a/http/http.dsp b/http/http.dsp index 75173bef..2330ca65 100755 --- a/http/http.dsp +++ b/http/http.dsp @@ -23,8 +23,8 @@ CFG=http - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe diff --git a/io/io.dsp b/io/io.dsp index 58df7ddc..b53b3236 100644 --- a/io/io.dsp +++ b/io/io.dsp @@ -23,8 +23,8 @@ CFG=io - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 4a0f99f4..6e54080f 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -614,17 +614,9 @@ unsigned int __stdcall CThreadRep::threadFunc(void* arg) { CThreadRep* rep = (CThreadRep*)arg; - // initialize OLE - const HRESULT hr = OleInitialize(NULL); - // run thread rep->doThreadFunc(); - // close OLE - if (!FAILED(hr)) { - OleUninitialize(); - } - // signal termination SetEvent(rep->m_exit); diff --git a/mt/mt.dsp b/mt/mt.dsp index 0c5dab8d..baa08de9 100644 --- a/mt/mt.dsp +++ b/mt/mt.dsp @@ -23,8 +23,8 @@ CFG=mt - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe diff --git a/net/net.dsp b/net/net.dsp index 1b5ff922..2e11719b 100644 --- a/net/net.dsp +++ b/net/net.dsp @@ -23,8 +23,8 @@ CFG=net - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index 7cd34a53..83e11704 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -152,6 +152,38 @@ void CMSWindowsScreen::getScreenSize( *h = m_h; } +HDESK CMSWindowsScreen::openInputDesktop() const +{ + return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, + DESKTOP_CREATEWINDOW | + DESKTOP_HOOKCONTROL | + GENERIC_WRITE); +} + +CString CMSWindowsScreen::getDesktopName( + HDESK desk) const +{ + if (desk == NULL) { + return CString(); + } + else { + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + TCHAR* name = new TCHAR[size / sizeof(TCHAR) + 1]; + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + CString result(name); + delete[] name; + return result; + } +} + +bool CMSWindowsScreen::isCurrentDesktop( + HDESK desk) const +{ + return CStringUtil::CaselessCmp::equal(getDesktopName(desk), + getCurrentDesktopName()); +} + void CMSWindowsScreen::getEvent(MSG* msg) const { // wait for an event in a cancellable way diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index 510ed3c6..90c00b95 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -18,6 +18,11 @@ public: static void init(HINSTANCE); + // accessors + + // get the application instance handle + static HINSTANCE getInstance(); + protected: // runs an event loop and returns when WM_QUIT is received void doRun(); @@ -35,9 +40,7 @@ protected: // is closed. void closeDisplay(); - // get the application instance handle and the registered window - // class atom - static HINSTANCE getInstance(); + // get the registered window class atom ATOM getClass() const; // update screen size cache @@ -46,6 +49,17 @@ protected: // get the size of the screen void getScreenSize(SInt32* w, SInt32* h) const; + // get the input desktop. caller must CloseDesktop() the result. + // do not call under windows 95/98/me. + HDESK openInputDesktop() const; + + // get the desktop's name. do not call under windows 95/98/me. + CString getDesktopName(HDESK) const; + + // returns true iff desk is the current desk. do not call under + // windows 95/98/me. + bool isCurrentDesktop(HDESK desk) const; + // wait for and get the next message. cancellable. void getEvent(MSG*) const; @@ -62,6 +76,9 @@ protected: // called by closeDisplay() to virtual void onCloseDisplay() = 0; + // called by isCurrentDesktop() to get the current desktop name + virtual CString getCurrentDesktopName() const = 0; + private: static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index ff519a92..63fb77d2 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -5,9 +5,11 @@ #include #include #include +#include #include #include + // // CUnixPlatform // @@ -22,26 +24,31 @@ CUnixPlatform::~CUnixPlatform() // do nothing } -bool CUnixPlatform::installDaemon(/* FIXME */) +bool CUnixPlatform::installDaemon( + const char*, + const char*, + const char*, + const char*) { // daemons don't require special installation return true; } -bool CUnixPlatform::uninstallDaemon(/* FIXME */) +bool CUnixPlatform::uninstallDaemon(const char*) { // daemons don't require special installation return true; } -bool CUnixPlatform::daemonize(const char* name) +int CUnixPlatform::daemonize( + const char* name, DaemonFunc func) { // fork so shell thinks we're done and so we're not a process // group leader switch (fork()) { case -1: // failed - return false; + return -1; case 0: // child @@ -75,7 +82,44 @@ bool CUnixPlatform::daemonize(const char* name) // hook up logger setDaemonLogger(name); - return true; + // invoke function + return func(this, 1, &name); +} + +int CUnixPlatform::restart( + RestartFunc func, int minErrorCode) +{ + for (;;) { + switch (fork()) { + default: { + // parent process. wait for child to exit. + int status; + if (wait(&status) == -1) { + // wait failed. this is unexpected so bail. + log((CLOG_CRIT "wait() failed")); + return minErrorCode; + } + + // what happened? if the child exited normally with a + // status less than 16 then the child was deliberately + // terminated so we also terminate. otherwise, we + // loop. + if (WIFEXITED(status) && WEXITSTATUS(status) < minErrorCode) { + return WEXITSTATUS(status); + } + break; + } + + case -1: + // fork() failed. log the error and proceed as a child + log((CLOG_WARN "fork() failed; cannot automatically restart on error")); + // fall through + + case 0: + // child process + return func(); + } + } } const char* CUnixPlatform::getBasename(const char* pathname) const @@ -117,7 +161,9 @@ CString CUnixPlatform::addPathComponent( CString path; path.reserve(prefix.size() + 1 + suffix.size()); path += prefix; - path += '/'; + if (path.size() == 0 || path[path.size() - 1] != '/') { + path += '/'; + } path += suffix; return path; } @@ -128,7 +174,7 @@ void CUnixPlatform::setDaemonLogger(const char* name) CLog::setOutputter(&CUnixPlatform::deamonLogger); } -void CUnixPlatform::deamonLogger( +bool CUnixPlatform::deamonLogger( int priority, const char* msg) { // convert priority @@ -157,4 +203,5 @@ void CUnixPlatform::deamonLogger( // log it syslog(priority, "%s", msg); + return true; } diff --git a/platform/CUnixPlatform.h b/platform/CUnixPlatform.h index c449e3ed..94bc4a05 100644 --- a/platform/CUnixPlatform.h +++ b/platform/CUnixPlatform.h @@ -9,9 +9,13 @@ public: virtual ~CUnixPlatform(); // IPlatform overrides - virtual bool installDaemon(/* FIXME */); - virtual bool uninstallDaemon(/* FIXME */); - virtual bool daemonize(const char* name); + virtual bool installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine); + virtual bool uninstallDaemon(const char* name); + virtual int daemonize(const char* name, DaemonFunc); + virtual int restart(RestartFunc, int minErrorCode); virtual const char* getBasename(const char* pathname) const; virtual CString getUserDirectory() const; virtual CString getSystemDirectory() const; @@ -23,7 +27,7 @@ protected: virtual void setDaemonLogger(const char* name); private: - static void deamonLogger(int, const char*); + static bool deamonLogger(int, const char*); }; #endif diff --git a/platform/CWin32Platform.cpp b/platform/CWin32Platform.cpp index b0ee15d5..9e71a141 100644 --- a/platform/CWin32Platform.cpp +++ b/platform/CWin32Platform.cpp @@ -1,12 +1,20 @@ #include "CWin32Platform.h" +#include "CLock.h" +#include "CThread.h" #include "CLog.h" +#include "stdvector.h" #include -#include +#include +#include +#include // // CWin32Platform // +HANDLE CWin32Platform::s_eventLog = NULL; +CWin32Platform* CWin32Platform::s_daemonPlatform = NULL; + CWin32Platform::CWin32Platform() { // do nothing @@ -17,22 +25,282 @@ CWin32Platform::~CWin32Platform() // do nothing } -bool CWin32Platform::installDaemon(/* FIXME */) +bool CWin32Platform::isWindows95Family() { - // FIXME - return false; + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + log((CLOG_WARN "cannot determine OS: %d", GetLastError())); + return true; + } + return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); } -bool CWin32Platform::uninstallDaemon(/* FIXME */) +void CWin32Platform::setStatus( + SERVICE_STATUS_HANDLE handle, + DWORD state) { - // FIXME - return false; + setStatus(handle, state, 0, 0); } -bool CWin32Platform::daemonize(const char* name) +void CWin32Platform::setStatus( + SERVICE_STATUS_HANDLE handle, + DWORD state, DWORD step, DWORD waitHint) { - // FIXME - return false; + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS; + status.dwCurrentState = state; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = step; + status.dwWaitHint = waitHint; + SetServiceStatus(handle, &status); +} + +void CWin32Platform::setStatusError( + SERVICE_STATUS_HANDLE handle, + DWORD error) +{ + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS; + status.dwCurrentState = SERVICE_STOPPED; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + status.dwServiceSpecificExitCode = error; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + SetServiceStatus(handle, &status); +} + +bool CWin32Platform::installDaemon( + const char* name, + const char* description, + const char* pathname, + const char* commandLine) +{ + // windows 95 family services + if (isWindows95Family()) { + // open registry + HKEY key = open95ServicesKey(); + if (key == NULL) { + log((CLOG_ERR "cannot open RunServices registry key", GetLastError())); + return false; + } + + // construct entry + CString value; + value += "\""; + value += pathname; + value += "\" "; + value += commandLine; + + // install entry + setValue(key, name, value); + + // clean up + closeKey(key); + + return true; + } + + // windows NT family services + else { + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + log((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); + return false; + } + + // create the servie + SC_HANDLE service = CreateService(mgr, + name, + name, + 0, + SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + pathname, + NULL, + NULL, + NULL, + NULL, + NULL); + + // done with service and manager + if (service != NULL) { + CloseServiceHandle(service); + CloseServiceHandle(mgr); + } + else { + log((CLOG_ERR "CreateService failed with %d", GetLastError())); + CloseServiceHandle(mgr); + return false; + } + + // open the registry key for this service + HKEY key = openNTServicesKey(); + key = openKey(key, name); + if (key == NULL) { + // can't open key + uninstallDaemon(name); + return false; + } + + // set the description + setValue(key, "Description", description); + + // set command line + key = openKey(key, "Parameters"); + if (key == NULL) { + // can't open key + uninstallDaemon(name); + return false; + } + setValue(key, "CommandLine", commandLine); + + // done with registry + closeKey(key); + + return true; + } +} + +bool CWin32Platform::uninstallDaemon(const char* name) +{ + // windows 95 family services + if (isWindows95Family()) { + // open registry + HKEY key = open95ServicesKey(); + if (key == NULL) { + log((CLOG_ERR "cannot open RunServices registry key", GetLastError())); + return false; + } + + // remove entry + deleteValue(key, name); + + // clean up + closeKey(key); + + return true; + } + + // windows NT family services + else { + // remove parameters for this service + HKEY key = openNTServicesKey(); + key = openKey(key, name); + if (key != NULL) { + deleteKey(key, "Parameters"); + closeKey(key); + } + + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + log((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); + return false; + } + + // open the service. oddly, you must open a service to delete it. + bool success; + SC_HANDLE service = OpenService(mgr, name, DELETE); + if (service == NULL) { + log((CLOG_ERR "OpenService failed with %d", GetLastError())); + success = false; + } + + else { + success = (DeleteService(service) != 0); + CloseServiceHandle(service); + } + + // close the manager + CloseServiceHandle(mgr); + + return success; + } +} + +int CWin32Platform::daemonize( + const char* name, + DaemonFunc func) +{ + assert(name != NULL); + assert(func != NULL); + + // windows 95 family services + if (isWindows95Family()) { + typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD); + + // mark this process as a service so it's not killed when the + // user logs off. + HINSTANCE kernel = LoadLibrary("kernel32.dll"); + if (kernel == NULL) { + log((CLOG_ERR "LoadLibrary failed with %d", GetLastError())); + return -1; + } + RegisterServiceProcessT RegisterServiceProcess = + reinterpret_cast( + GetProcAddress(kernel, + _T("RegisterServiceProcess"))); + if (RegisterServiceProcess == NULL) { + log((CLOG_ERR "can't lookup RegisterServiceProcess: %d", GetLastError())); + FreeLibrary(kernel); + return -1; + } + if (RegisterServiceProcess(NULL, 1) == 0) { + log((CLOG_ERR "RegisterServiceProcess failed with %d", GetLastError())); + FreeLibrary(kernel); + return -1; + } + FreeLibrary(kernel); + + // now simply call the daemon function + return func(this, 1, &name); + } + + // windows NT family services + else { + // save daemon function + m_daemonFunc = func; + + // construct the service entry + SERVICE_TABLE_ENTRY entry[2]; + entry[0].lpServiceName = const_cast(name); + entry[0].lpServiceProc = &CWin32Platform::serviceMainEntry; + entry[1].lpServiceName = NULL; + entry[1].lpServiceProc = NULL; + + // hook us up to the service control manager. this won't return + // (if successful) until the processes have terminated. + s_daemonPlatform = this; + if (StartServiceCtrlDispatcher(entry)) { + s_daemonPlatform = NULL; + return m_daemonResult; + } + log((CLOG_ERR "StartServiceCtrlDispatcher failed with %d", GetLastError())); + s_daemonPlatform = NULL; + return -1; + } +} + +int CWin32Platform::restart( + RestartFunc func, int /*minErrorCode*/) +{ + // FIXME -- start in separate process or thread. note that this + // isn't too critical as win32 doesn't force us to terminate for + // any reason so we should never have to restart. + return func(); } const char* CWin32Platform::getBasename(const char* pathname) const @@ -61,14 +329,58 @@ const char* CWin32Platform::getBasename(const char* pathname) const CString CWin32Platform::getUserDirectory() const { - // FIXME - return CString(); + // try %HOMEPATH% + TCHAR dir[MAX_PATH]; + DWORD size = sizeof(dir) / sizeof(TCHAR); + DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size); + if (result != 0 && result <= size) { + // sanity check -- if dir doesn't appear to start with a + // drive letter and isn't a UNC name then don't use it + // FIXME -- allow UNC names + if (dir[0] != '\0' && (dir[1] == ':' || + ((dir[0] == '\\' || dir[0] == '/') && + (dir[1] == '\\' || dir[1] == '/')))) { + return dir; + } + } + + // get the location of the personal files. that's as close to + // a home directory as we're likely to find. + ITEMIDLIST* idl; + if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) { + TCHAR* path = NULL; + if (SHGetPathFromIDList(idl, dir)) { + DWORD attr = GetFileAttributes(dir); + if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + path = dir; + } + + IMalloc* shalloc; + if (SUCCEEDED(SHGetMalloc(&shalloc))) { + shalloc->Free(idl); + shalloc->Release(); + } + + if (path != NULL) { + return path; + } + } + + // use root of C drive as a default + return "C:"; } CString CWin32Platform::getSystemDirectory() const { - // FIXME - return ""; + // get windows directory + char dir[MAX_PATH]; + if (GetWindowsDirectory(dir, sizeof(dir)) != 0) { + return dir; + } + else { + // can't get it. use C:\ as a default. + return "C:"; + } } CString CWin32Platform::addPathComponent( @@ -78,13 +390,453 @@ CString CWin32Platform::addPathComponent( CString path; path.reserve(prefix.size() + 1 + suffix.size()); path += prefix; - path += '\\'; + if (path.size() == 0 || + (path[path.size() - 1] != '\\' && + path[path.size() - 1] != '/')) { + path += '\\'; + } path += suffix; return path; } -void CWin32Platform::serviceLogger( +HKEY CWin32Platform::openKey( + HKEY key, const char* keyName) +{ + // open next key + HKEY newKey; + LONG result = RegOpenKeyEx(key, keyName, 0, + KEY_WRITE | KEY_QUERY_VALUE, &newKey); + if (result != ERROR_SUCCESS) { + DWORD disp; + result = RegCreateKeyEx(key, keyName, 0, _T(""), + 0, KEY_WRITE | KEY_QUERY_VALUE, + NULL, &newKey, &disp); + } + if (result != ERROR_SUCCESS) { + RegCloseKey(key); + return NULL; + } + + // switch to new key + RegCloseKey(key); + return newKey; +} + +HKEY CWin32Platform::openKey( + HKEY key, const char** keyNames) +{ + for (UInt32 i = 0; key != NULL && keyNames[i] != NULL; ++i) { + // open next key + key = openKey(key, keyNames[i]); + } + return key; +} + +void CWin32Platform::closeKey(HKEY key) +{ + assert(key != NULL); + RegCloseKey(key); +} + +void CWin32Platform::deleteKey(HKEY key, const char* name) +{ + assert(key != NULL); + assert(name != NULL); + RegDeleteKey(key, name); +} + +void CWin32Platform::deleteValue(HKEY key, const char* name) +{ + assert(key != NULL); + assert(name != NULL); + RegDeleteValue(key, name); +} + +void CWin32Platform::setValue(HKEY key, + const char* name, const CString& value) +{ + assert(key != NULL); + assert(name != NULL); + RegSetValueEx(key, name, 0, REG_SZ, + reinterpret_cast(value.c_str()), + value.size() + 1); +} + +CString CWin32Platform::readValueString(HKEY key, + const char* name) +{ + // get the size of the string + DWORD type; + DWORD size = 0; + LONG result = RegQueryValueEx(key, name, 0, &type, NULL, &size); + if (result != ERROR_SUCCESS || type != REG_SZ) { + return CString(); + } + + // allocate space + char* buffer = new char[size]; + + // read it + result = RegQueryValueEx(key, name, 0, &type, + reinterpret_cast(buffer), &size); + if (result != ERROR_SUCCESS || type != REG_SZ) { + delete[] buffer; + return CString(); + } + + // clean up and return value + CString value(buffer); + delete[] buffer; + return value; +} + +HKEY CWin32Platform::openNTServicesKey() +{ + static const char* s_keyNames[] = { + _T("SYSTEM"), + _T("CurrentControlSet"), + _T("Services"), + NULL + }; + + return openKey(HKEY_LOCAL_MACHINE, s_keyNames); +} + +HKEY CWin32Platform::open95ServicesKey() +{ + static const char* s_keyNames[] = { + _T("Software"), + _T("Microsoft"), + _T("Windows"), + _T("CurrentVersion"), + _T("RunServices"), + NULL + }; + + return openKey(HKEY_LOCAL_MACHINE, s_keyNames); +} + +int CWin32Platform::runDaemon(RunFunc run, StopFunc stop) +{ + // should only be called from DaemonFunc + assert(m_serviceMutex != NULL); + + CLock lock(m_serviceMutex); + try { + int result; + m_stop = stop; + m_serviceHandlerWaiting = false; + m_serviceRunning = false; + for (;;) { + // mark server as running + setStatus(m_statusHandle, SERVICE_RUNNING); + + // run callback + m_serviceRunning = true; + result = run(m_serviceMutex); + m_serviceRunning = false; + + // notify handler that the server stopped. if handler + // isn't waiting then we stopped unexpectedly and we + // quit. + if (m_serviceHandlerWaiting) { + m_serviceHandlerWaiting = false; + m_serviceState->broadcast(); + } + else { + break; + } + + // wait until we're told what to do next + while (*m_serviceState != SERVICE_RUNNING && + *m_serviceState != SERVICE_STOPPED) { + m_serviceState->wait(); + } + + // exit loop if we've been told to stop + if (*m_serviceState == SERVICE_STOPPED) { + break; + } + } + + // prevent daemonHandler from changing state + *m_serviceState = SERVICE_STOPPED; + + // tell service control that the service is stopped. + // FIXME -- hopefully this will ensure that our handler won't + // be called again but i can't find documentation that + // verifies that. if it does it'll crash on the mutex that + // we're about to destroy. + setStatus(m_statusHandle, *m_serviceState); + + // clean up + m_stop = NULL; + + return result; + } + catch (...) { + // FIXME -- report error + + // prevent serviceHandler from changing state + *m_serviceState = SERVICE_STOPPED; + + // set status + setStatusError(m_statusHandle, 0); + + // wake up serviceHandler if it's waiting then wait for it + if (m_serviceHandlerWaiting) { + m_serviceHandlerWaiting = false; + m_serviceState->broadcast(); + m_serviceState->wait(); + // serviceHandler has exited by now + } + + throw; + } +} + +void CWin32Platform::serviceMain( + DWORD argc, LPTSTR* argvIn) +{ + typedef std::vector ArgList; + typedef std::vector Arguments; + const char** argv = const_cast(argvIn); + + // open event log and direct log messages to it + if (s_eventLog == NULL) { + s_eventLog = RegisterEventSource(NULL, argv[0]); + if (s_eventLog != NULL) { + CLog::setOutputter(&CWin32Platform::serviceLogger); + } + } + + // create synchronization objects + CThread::init(); + m_serviceMutex = new CMutex; + m_serviceState = new CCondVar(m_serviceMutex, SERVICE_RUNNING); + + // register our service handler functiom + m_statusHandle = RegisterServiceCtrlHandler(argv[0], + &CWin32Platform::serviceHandlerEntry); + if (m_statusHandle == NULL) { + // cannot start as service + m_daemonResult = -1; + delete m_serviceState; + delete m_serviceMutex; + return; + } + + // tell service control manager that we're starting + setStatus(m_statusHandle, SERVICE_START_PENDING, 0, 1000); + + // if no arguments supplied then try getting them from the registry. + // the first argument doesn't count because it's the service name. + Arguments args; + ArgList myArgv; + if (argc <= 1) { + // read command line + CString commandLine; + HKEY key = openNTServicesKey(); + key = openKey(key, argv[0]); + key = openKey(key, "Parameters"); + if (key != NULL) { + commandLine = readValueString(key, "CommandLine"); + } + + // if the command line isn't empty then parse and use it + if (!commandLine.empty()) { + // parse, honoring double quoted substrings + CString::size_type i = commandLine.find_first_not_of(" \t"); + while (i != CString::npos && i != commandLine.size()) { + // find end of string + CString::size_type e; + if (commandLine[i] == '\"') { + // quoted. find closing quote. + ++i; + e = commandLine.find("\"", i); + + // whitespace must follow closing quote + if (e == CString::npos || + (e + 1 != commandLine.size() && + commandLine[e + 1] != ' ' && + commandLine[e + 1] != '\t')) { + args.clear(); + break; + } + + // extract + args.push_back(commandLine.substr(i, e - i)); + i = e + 1; + } + else { + // unquoted. find next whitespace. + e = commandLine.find_first_of(" \t", i); + if (e == CString::npos) { + e = commandLine.size(); + } + + // extract + args.push_back(commandLine.substr(i, e - i)); + i = e + 1; + } + + // next argument + i = commandLine.find_first_not_of(" \t", i); + } + + // service name goes first + myArgv.push_back(argv[0]); + + // get pointers + for (UInt32 i = 0; i < args.size(); ++i) { + myArgv.push_back(args[i].c_str()); + } + + // adjust argc/argv + argc = myArgv.size(); + argv = &myArgv[0]; + } + } + + try { + // invoke daemon function + m_daemonResult = m_daemonFunc(this, static_cast(argc), argv); + } + catch (CDaemonFailed& e) { + setStatusError(m_statusHandle, e.m_result); + m_daemonResult = -1; + } + catch (...) { + setStatusError(m_statusHandle, 1); + m_daemonResult = -1; + } + + // clean up + delete m_serviceState; + delete m_serviceMutex; + + // FIXME -- close event log? +} + +void WINAPI CWin32Platform::serviceMainEntry( + DWORD argc, LPTSTR* argv) +{ + s_daemonPlatform->serviceMain(argc, argv); +} + +void CWin32Platform::serviceHandler(DWORD ctrl) +{ + assert(m_serviceMutex != NULL); + assert(m_serviceState != NULL); + + CLock lock(m_serviceMutex); + + // ignore request if service is already stopped + if (*m_serviceState == SERVICE_STOPPED) { + setStatus(m_statusHandle, *m_serviceState); + return; + } + + switch (ctrl) { + case SERVICE_CONTROL_PAUSE: + // update state + *m_serviceState = SERVICE_PAUSE_PENDING; + setStatus(m_statusHandle, *m_serviceState, 0, 1000); + + // stop run callback if running and wait for it to finish + if (m_serviceRunning) { + m_serviceHandlerWaiting = true; + m_stop(); + m_serviceState->wait(); + } + + // update state if service hasn't stopped while we were waiting + if (*m_serviceState != SERVICE_STOPPED) { + *m_serviceState = SERVICE_PAUSED; + } + m_serviceState->broadcast(); + break; + + case SERVICE_CONTROL_CONTINUE: + // required status update + setStatus(m_statusHandle, *m_serviceState); + + // update state but let main loop send RUNNING notification + *m_serviceState = SERVICE_RUNNING; + m_serviceState->broadcast(); + return; + + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + // update state + *m_serviceState = SERVICE_STOP_PENDING; + setStatus(m_statusHandle, *m_serviceState, 0, 1000); + + // stop run callback if running and wait for it to finish + if (m_serviceRunning) { + m_serviceHandlerWaiting = true; + m_stop(); + m_serviceState->wait(); + } + + // update state + *m_serviceState = SERVICE_STOPPED; + m_serviceState->broadcast(); + break; + + default: + log((CLOG_WARN "unknown service command: %d", ctrl)); + // fall through + + case SERVICE_CONTROL_INTERROGATE: + break; + } + + // send update + setStatus(m_statusHandle, *m_serviceState); +} + +void WINAPI CWin32Platform::serviceHandlerEntry(DWORD ctrl) +{ + s_daemonPlatform->serviceHandler(ctrl); +} + +bool CWin32Platform::serviceLogger( int priority, const char* msg) { - // FIXME + if (s_eventLog == NULL) { + return false; + } + + // convert priority + WORD type; + switch (priority) { + case CLog::kFATAL: + case CLog::kERROR: + type = EVENTLOG_ERROR_TYPE; + break; + + case CLog::kWARNING: + type = EVENTLOG_WARNING_TYPE; + break; + + default: + type = EVENTLOG_INFORMATION_TYPE; + break; + } + + // log it + // FIXME -- win32 wants to use a message table to look up event + // strings. log messages aren't organized that way so we'll + // just dump our string into the raw data section of the event + // so users can at least see the message. note that we use our + // priority as the event category. + ReportEvent(s_eventLog, type, static_cast(priority), + 0, // event ID + NULL, + 0, + strlen(msg + 1), // raw data size + NULL, + const_cast(msg));// raw data + return true; } diff --git a/platform/CWin32Platform.h b/platform/CWin32Platform.h index 3a680be0..33f657b9 100644 --- a/platform/CWin32Platform.h +++ b/platform/CWin32Platform.h @@ -2,16 +2,52 @@ #define CWIN32PLATFORM_H #include "IPlatform.h" +#include "CCondVar.h" +#include "CMutex.h" +#include class CWin32Platform : public IPlatform { public: + typedef int (*RunFunc)(CMutex*); + typedef void (*StopFunc)(void); + CWin32Platform(); virtual ~CWin32Platform(); + // returns true iff the platform is win95/98/me + static bool isWindows95Family(); + + // utility for calling SetServiceStatus() + static void setStatus(SERVICE_STATUS_HANDLE, DWORD state); + static void setStatus(SERVICE_STATUS_HANDLE, + DWORD state, DWORD step, DWORD waitHint); + static void setStatusError(SERVICE_STATUS_HANDLE, DWORD error); + + // run a service. the RunFunc should unlock the passed in mutex + // (which will be locked on entry) when not initializing or + // shutting down (i.e. when running its loop). StopFunc should + // cause the RunFunc() to return. returns what RunFunc returns. + // RunFunc should throw CDaemonFailed if the service fails. + int runDaemon(RunFunc, StopFunc); + + // thrown by RunFunc on service failure. result is the error + // code reported by the service. + class CDaemonFailed { + public: + CDaemonFailed(int result) : m_result(result) { } + + public: + int m_result; + }; + // IPlatform overrides - virtual bool installDaemon(/* FIXME */); - virtual bool uninstallDaemon(/* FIXME */); - virtual bool daemonize(const char* name); + virtual bool installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine); + virtual bool uninstallDaemon(const char* name); + virtual int daemonize(const char* name, DaemonFunc); + virtual int restart(RestartFunc, int minErrorCode); virtual const char* getBasename(const char* pathname) const; virtual CString getUserDirectory() const; virtual CString getSystemDirectory() const; @@ -20,7 +56,40 @@ public: const CString& suffix) const; private: - static void serviceLogger(int, const char*); + static HKEY openKey(HKEY parent, const char*); + static HKEY openKey(HKEY parent, const char**); + static void closeKey(HKEY); + static void deleteKey(HKEY, const char* name); + static void deleteValue(HKEY, const char* name); + static void setValue(HKEY, const char* name, + const CString& value); + static CString readValueString(HKEY, const char* name); + static HKEY openNTServicesKey(); + static HKEY open95ServicesKey(); + + void serviceMain(DWORD, LPTSTR*); + static void WINAPI serviceMainEntry(DWORD, LPTSTR*); + + void serviceHandler(DWORD ctrl); + static void WINAPI serviceHandlerEntry(DWORD ctrl); + + static bool serviceLogger(int, const char*); + +private: + DaemonFunc m_daemonFunc; + int m_daemonResult; + + SERVICE_STATUS_HANDLE m_statusHandle; + + CMutex* m_serviceMutex; + CCondVar* m_serviceState; + bool m_serviceHandlerWaiting; + bool m_serviceRunning; + StopFunc m_stop; + + static HANDLE s_eventLog; + + static CWin32Platform* s_daemonPlatform; }; #endif diff --git a/platform/IPlatform.h b/platform/IPlatform.h index 73769abd..7012304e 100644 --- a/platform/IPlatform.h +++ b/platform/IPlatform.h @@ -7,19 +7,48 @@ class IPlatform : public IInterface { public: + typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv); + typedef int (*RestartFunc)(); + // manipulators - // install/uninstall a daemon. + // install/uninstall a daemon. commandLine should *not* + // include the name of program as the first argument. // FIXME -- throw on error? will get better error messages that way. - virtual bool installDaemon(/* FIXME */) = 0; - virtual bool uninstallDaemon(/* FIXME */) = 0; + virtual bool installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine) = 0; + virtual bool uninstallDaemon(const char* name) = 0; // daemonize. this should have the side effect of sending log // messages to a system message logger since messages can no // longer go to the console. returns true iff successful. // the name is the name of the daemon. -// FIXME -- win32 services will require a more complex interface - virtual bool daemonize(const char* name) = 0; + + // daemonize. this should have the side effect of sending log + // messages to a system message logger since messages can no + // longer go to the console. name is the name of the daemon. + // once daemonized, func is invoked and daemonize returns when + // and what func does. daemonize() returns -1 on error. + // + // exactly what happens when daemonizing depends on the platform. + // unix: + // detaches from terminal. func gets one argument, the name + // passed to daemonize(). + // win32: + // becomes a service. argument 0 is the name of the service + // and the rest are the arguments passed to StartService(). + // func is only called when the service is actually started. + // func must behave like a proper ServiceMain() function; in + // particular, it must call RegisterServiceCtrlHandler() and + // SetServiceStatus(). + virtual int daemonize(const char* name, DaemonFunc func) = 0; + + // continually restart the given function in a separate process + // or thread until it exits normally with a code less than the + // given code then return the code. + virtual int restart(RestartFunc, int minErrorCode) = 0; // accessors diff --git a/platform/platform.dsp b/platform/platform.dsp new file mode 100644 index 00000000..1a7efbca --- /dev/null +++ b/platform/platform.dsp @@ -0,0 +1,139 @@ +# Microsoft Developer Studio Project File - Name="platform" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=platform - Win32 Debug +!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 "platform.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 "platform.mak" CFG="platform - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "platform - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "platform - Win32 Debug" (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)" == "platform - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "platform - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "platform - Win32 Release" +# Name "platform - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPlatform.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CPlatform.h +# End Source File +# Begin Source File + +SOURCE=.\CWin32Platform.h +# End Source File +# Begin Source File + +SOURCE=.\IPlatform.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# Begin Group "Included Files" + +# PROP Default_Filter "inc" +# Begin Source File + +SOURCE=.\CWin32Platform.cpp +# PROP Exclude_From_Build 1 +# End Source File +# End Group +# End Target +# End Project diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index ae4ea098..cc83079a 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -1,10 +1,11 @@ #include "CMSWindowsPrimaryScreen.h" #include "CMSWindowsClipboard.h" #include "CServer.h" -#include "CSynergyHook.h" +#include "CPlatform.h" +#include "XScreen.h" #include "XSynergy.h" -#include "CThread.h" #include "CLog.h" +#include "CThread.h" #include #include @@ -14,38 +15,76 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : m_server(NULL), - m_active(false), + m_threadID(0), + m_desk(NULL), + m_deskName(), m_window(NULL), - m_nextClipboardWindow(NULL), - m_clipboardOwner(NULL), - m_hookLibrary(NULL), + m_active(false), m_mark(0), - m_markReceived(0) + m_markReceived(0), + m_nextClipboardWindow(NULL), + m_clipboardOwner(NULL) { - // detect operating system - OSVERSIONINFO version; - version.dwOSVersionInfoSize = sizeof(version); - if (GetVersionEx(&version) == 0) { - log((CLOG_WARN "cannot determine OS: %d", GetLastError())); + // load the hook library + m_hookLibrary = LoadLibrary("synrgyhk"); + if (m_hookLibrary == NULL) { + log((CLOG_ERR "failed to load hook library")); + throw XScreenOpenFailure(); } - m_is95Family = (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); + m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone"); + m_setRelay = (SetRelayFunc)GetProcAddress(m_hookLibrary, "setRelay"); + m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install"); + m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall"); + if (m_setZone == NULL || + m_setRelay == NULL || + m_install == NULL || + m_uninstall == NULL) { + log((CLOG_ERR "invalid hook library")); + FreeLibrary(m_hookLibrary); + throw XScreenOpenFailure(); + } + + // detect operating system + m_is95Family = CPlatform::isWindows95Family(); + + // make sure this thread has a message queue + MSG dummy; + PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); } CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() { + assert(m_hookLibrary != NULL); assert(m_window == NULL); - assert(m_hookLibrary == NULL); + + // done with hook library + FreeLibrary(m_hookLibrary); } void CMSWindowsPrimaryScreen::run() { + // must call run() from same thread as open() + assert(m_threadID == GetCurrentThreadId()); + // change our priority CThread::getCurrentThread().setPriority(-3); + // poll input desktop to see if it changes (preTranslateMessage() + // handles WM_TIMER) + UINT timer = 0; + if (!m_is95Family) { + SetTimer(NULL, 0, 200, NULL); + } + // run event loop log((CLOG_INFO "entering event loop")); doRun(); log((CLOG_INFO "exiting event loop")); + + // remove timer + if (!m_is95Family) { + KillTimer(NULL, timer); + } } void CMSWindowsPrimaryScreen::stop() @@ -64,16 +103,25 @@ void CMSWindowsPrimaryScreen::open(CServer* server) // open the display openDisplay(); - // get keyboard state - updateKeys(); + // initialize marks + m_mark = 0; + m_markReceived = 0; + nextMark(); // send screen info SInt32 w, h; getScreenSize(&w, &h); m_server->setInfo(w, h, getJumpZoneSize(), 0, 0); + // compute center pixel of screen + m_xCenter = w >> 1; + m_yCenter = h >> 1; + + // get keyboard state + updateKeys(); + // enter the screen - doEnter(); + enterNoWarp(); } void CMSWindowsPrimaryScreen::close() @@ -92,115 +140,48 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) log((CLOG_INFO "entering primary at %d,%d", x, y)); assert(m_active == true); - doEnter(); + // enter the screen + enterNoWarp(); // warp to requested location warpCursor(x, y); } -void CMSWindowsPrimaryScreen::doEnter() -{ - // not active anymore - m_active = false; - - // release keyboard/mouse and set the zones that should cause a jump -/* FIXME -if (UnregisterHotKey(m_window, 0x0001) != 0) { -log((CLOG_INFO "released hot key")); -} -else { -log((CLOG_INFO "failed to release hot key: %d", GetLastError())); -} -*/ - if (m_is95Family) { - DWORD dummy = 0; - SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0); - } - SInt32 w, h; - getScreenSize(&w, &h); - SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( - m_hookLibrary, "setZone"); - setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize()); - - // restore the active window and hide our window. we can only set - // the active window for another thread if we first attach our input - // to that thread. - ReleaseCapture(); - if (m_lastActiveWindow != NULL) { - DWORD myThread = GetCurrentThreadId(); - if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { - // FIXME -- shouldn't raise window if X-Mouse is enabled - // but i have no idea how to do that or check if enabled. - SetActiveWindow(m_lastActiveWindow); - AttachThreadInput(myThread, m_lastActiveThread, FALSE); - } - } - ShowWindow(m_window, SW_HIDE); - - // all messages prior to now are invalid - nextMark(); -} - bool CMSWindowsPrimaryScreen::leave() { log((CLOG_INFO "leaving primary")); assert(m_active == false); - // do non-warp enter stuff - // get state of keys as we leave - updateKeys(); - // all messages prior to now are invalid nextMark(); - // remember the active window before we leave. GetActiveWindow() - // will only return the active window for the thread's queue (i.e. - // our app) but we need the globally active window. get that by - // attaching input to the foreground window's thread then calling - // GetActiveWindow() and then detaching our input. - m_lastActiveWindow = NULL; - m_lastForegroundWindow = GetForegroundWindow(); - m_lastActiveThread = GetWindowThreadProcessId( - m_lastForegroundWindow, NULL); - if (m_lastActiveThread != 0) { - DWORD myThread = GetCurrentThreadId(); - if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { - m_lastActiveWindow = GetActiveWindow(); - AttachThreadInput(myThread, m_lastActiveThread, FALSE); - } + // save active window, show ours, and grab mouse/keyboard + if (!onLeave()) { + return false; } - // show our window - ShowWindow(m_window, SW_SHOW); - // relay all mouse and keyboard events - SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress( - m_hookLibrary, "setRelay"); - setRelay(); + m_setRelay(); + + // get state of keys as we leave + updateKeys(); + + // warp mouse to center of screen + warpCursor(m_xCenter, m_yCenter); + + // ignore this many mouse motion events (not including the already + // queued events). on (at least) the win2k login desktop, one + // motion event is reported using a position from before the above + // warpCursor(). i don't know why it does that and other desktops + // don't have the same problem. anyway, simply ignoring that event + // works around it. + m_mouseMoveIgnore = 1; + + // disable ctrl+alt+del, alt+tab, etc if (m_is95Family) { - // disable ctrl+alt+del, alt+tab, ctrl+esc DWORD dummy = 0; SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); } -/* FIXME -if (RegisterHotKey(m_window, 0x0001, MOD_ALT, VK_TAB) != 0) { -log((CLOG_INFO "got hot key")); -} -else { -log((CLOG_INFO "failed to get hot key: %d", GetLastError())); -} -*/ - - // get keyboard input and capture mouse - SetActiveWindow(m_window); - SetFocus(m_window); - SetCapture(m_window); - - // warp the mouse to the center of the screen - getScreenSize(&m_xCenter, &m_yCenter); - m_xCenter >>= 1; - m_yCenter >>= 1; - warpCursor(m_xCenter, m_yCenter); // discard messages until after the warp nextMark(); @@ -238,12 +219,11 @@ log((CLOG_INFO "failed to get hot key: %d", GetLastError())); void CMSWindowsPrimaryScreen::onConfigure() { - if (!m_active) { + if ((m_is95Family || m_desk != NULL) && !m_active) { SInt32 w, h; getScreenSize(&w, &h); - SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( - m_hookLibrary, "setZone"); - setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize()); + m_setZone(m_server->getActivePrimarySides(), + w, h, getJumpZoneSize()); } } @@ -254,7 +234,7 @@ void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) } void CMSWindowsPrimaryScreen::setClipboard( - ClipboardID id, const IClipboard* src) + ClipboardID /*id*/, const IClipboard* src) { assert(m_window != NULL); @@ -262,7 +242,8 @@ void CMSWindowsPrimaryScreen::setClipboard( CClipboard::copy(&dst, src); } -void CMSWindowsPrimaryScreen::grabClipboard(ClipboardID id) +void CMSWindowsPrimaryScreen::grabClipboard( + ClipboardID /*id*/) { assert(m_window != NULL); @@ -284,7 +265,7 @@ SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const } void CMSWindowsPrimaryScreen::getClipboard( - ClipboardID id, IClipboard* dst) const + ClipboardID /*id*/, IClipboard* dst) const { assert(m_window != NULL); @@ -332,80 +313,37 @@ void CMSWindowsPrimaryScreen::onOpenDisplay() assert(m_window == NULL); assert(m_server != NULL); - // initialize clipboard owner to current owner. we don't want - // to take ownership of the clipboard just by starting up. - m_clipboardOwner = GetClipboardOwner(); + // save thread id. we'll need to pass this to the hook library. + m_threadID = GetCurrentThreadId(); - // get screen size - // note -- we use a fullscreen window to grab input. it should - // be possible to use a 1x1 window but i've run into problems - // with losing keyboard input (focus?) in that case. - // unfortunately, hiding the full screen window causes all other - // windows to redraw. - SInt32 w, h; - getScreenSize(&w, &h); - - // create the window - m_window = CreateWindowEx(WS_EX_TOPMOST | - WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - (LPCTSTR)getClass(), "Synergy", - WS_POPUP, - 0, 0, w, h, NULL, NULL, - getInstance(), - NULL); - assert(m_window != NULL); - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - // load the hook library - bool hooked = false; - m_hookLibrary = LoadLibrary("synrgyhk"); - if (m_hookLibrary != NULL) { - // install input hooks - InstallFunc install = (InstallFunc)GetProcAddress( - m_hookLibrary, "install"); - if (install != NULL) { - hooked = (install(m_window) != 0); + // get the input desktop and switch to it + if (m_is95Family) { + if (!openDesktop()) { + throw XScreenOpenFailure(); } } - if (!hooked) { - log((CLOG_ERR "failed to install hooks")); - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - DestroyWindow(m_window); - m_window = NULL; - // FIXME -- throw + else { + if (!switchDesktop(openInputDesktop())) { + throw XScreenOpenFailure(); + } } - - // initialize marks - m_mark = 0; - m_markReceived = 0; - nextMark(); } void CMSWindowsPrimaryScreen::onCloseDisplay() { - assert(m_window != NULL); - - // uninstall input hooks - UninstallFunc uninstall = (UninstallFunc)GetProcAddress( - m_hookLibrary, "uninstall"); - if (uninstall != NULL) { - uninstall(); + // disconnect from desktop + if (m_is95Family) { + closeDesktop(); + } + else { + switchDesktop(NULL); } - // done with hook library - FreeLibrary(m_hookLibrary); - m_hookLibrary = NULL; + // clear thread id + m_threadID = 0; - // remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // destroy window - DestroyWindow(m_window); - m_window = NULL; + assert(m_window == NULL); + assert(m_desk == NULL); } bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) @@ -417,8 +355,8 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) return true; case SYNERGY_MSG_KEY: - // ignore if not at current mark - if (m_mark == m_markReceived) { + // ignore message if posted prior to last mark change + if (m_markReceived == m_mark) { KeyModifierMask mask; const KeyID key = mapKey(msg->wParam, msg->lParam, &mask); if (key != kKeyNone) { @@ -453,8 +391,8 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) return true; case SYNERGY_MSG_MOUSE_BUTTON: - // ignore if not at current mark - if (m_mark == m_markReceived) { + // ignore message if posted prior to last mark change + if (m_markReceived == m_mark) { const ButtonID button = mapButton(msg->wParam); switch (msg->wParam) { case WM_LBUTTONDOWN: @@ -479,20 +417,19 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) return true; case SYNERGY_MSG_MOUSE_WHEEL: - // ignore if not at current mark - if (m_mark == m_markReceived) { + // ignore message if posted prior to last mark change + if (m_markReceived == m_mark) { log((CLOG_ERR "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); m_server->onMouseWheel(msg->wParam); } return true; case SYNERGY_MSG_MOUSE_MOVE: - // ignore if not at current mark - if (m_mark == m_markReceived) { - SInt32 x = (SInt32)msg->wParam; - SInt32 y = (SInt32)msg->lParam; + // ignore message if posted prior to last mark change + if (m_markReceived == m_mark) { + SInt32 x = static_cast(msg->wParam); + SInt32 y = static_cast(msg->lParam); if (!m_active) { - log((CLOG_DEBUG2 "event: inactive move %d,%d", x, y)); m_server->onMouseMovePrimary(x, y); } else { @@ -500,15 +437,35 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) x -= m_xCenter; y -= m_yCenter; - // ignore if the mouse didn't move - if (x != 0 && y != 0) { - log((CLOG_DEBUG2 "event: active move %d,%d", x, y)); + // ignore if the mouse didn't move or we're ignoring + // motion. + if (m_mouseMoveIgnore == 0) { + if (x != 0 && y != 0) { + // warp mouse back to center + warpCursor(m_xCenter, m_yCenter); - // warp mouse back to center - warpCursor(m_xCenter, m_yCenter); + // send motion + m_server->onMouseMoveSecondary(x, y); + } + } + else { + // ignored one more motion event + --m_mouseMoveIgnore; + } + } + } + return true; - // send motion - m_server->onMouseMoveSecondary(x, y); + case WM_TIMER: + // if current desktop is not the input desktop then switch to it + if (!m_is95Family) { + HDESK desk = openInputDesktop(); + if (desk != NULL) { + if (isCurrentDesktop(desk)) { + CloseDesktop(desk); + } + else { + switchDesktop(desk); } } } @@ -523,11 +480,20 @@ LRESULT CMSWindowsPrimaryScreen::onEvent( WPARAM wParam, LPARAM lParam) { switch (msg) { -/* -case WM_HOTKEY: -log((CLOG_INFO "hot key: %d, %d, %s %s %s", wParam, HIWORD(lParam), (LOWORD(lParam) & MOD_ALT) ? "ALT" : "", (LOWORD(lParam) & MOD_CONTROL) ? "CTRL" : "", (LOWORD(lParam) & MOD_SHIFT) ? "SHIFT" : "", (LOWORD(lParam) & MOD_WIN) ? "WIN" : "")); -return 0; -*/ + case WM_QUERYENDSESSION: + if (m_is95Family) { + return TRUE; + } + break; + + case WM_ENDSESSION: + if (m_is95Family) { + if (wParam == TRUE && lParam == 0) { + stop(); + } + return 0; + } + break; case WM_PAINT: ValidateRect(hwnd, NULL); @@ -537,7 +503,9 @@ return 0; log((CLOG_DEBUG "clipboard was taken")); // first pass it on - SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + if (m_nextClipboardWindow != NULL) { + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + } // now notify server that somebody changed the clipboard. // skip that if we're the new owner. @@ -555,51 +523,302 @@ return 0; return 0; case WM_CHANGECBCHAIN: - if (m_nextClipboardWindow == (HWND)wParam) + if (m_nextClipboardWindow == (HWND)wParam) { m_nextClipboardWindow = (HWND)lParam; - else + } + else if (m_nextClipboardWindow != NULL) { SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + } return 0; - case WM_DISPLAYCHANGE: { - // screen resolution has changed - SInt32 w, h; - updateScreenSize(); - getScreenSize(&w, &h); + case WM_DISPLAYCHANGE: + { + // screen resolution may have changed + SInt32 wOld, hOld; + getScreenSize(&wOld, &hOld); + SInt32 w, h; + updateScreenSize(); + getScreenSize(&w, &h); - // recompute center pixel of screen - m_xCenter = w >> 1; - m_yCenter = h >> 1; + // do nothing if resolution hasn't changed + if (w != wOld || h != hOld) { + // recompute center pixel of screen + m_xCenter = w >> 1; + m_yCenter = h >> 1; - // warp mouse to center if active - if (m_active) { - warpCursor(m_xCenter, m_yCenter); + // warp mouse to center if active + if (m_active) { + warpCursor(m_xCenter, m_yCenter); + } + + // tell hook about resize if not active + else { + onConfigure(); + } + + // send new screen info + POINT pos; + GetCursorPos(&pos); + m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y); + } + + return 0; } - - // tell hook about resize if not active - else { - SetZoneFunc setZone = (SetZoneFunc)GetProcAddress( - m_hookLibrary, "setZone"); - setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize()); - } - - // send new screen info - POINT pos; - GetCursorPos(&pos); - m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y); - - return 0; - } } return DefWindowProc(hwnd, msg, wParam, lParam); } +void CMSWindowsPrimaryScreen::enterNoWarp() +{ + // not active anymore + m_active = false; + + // reset motion ignore count + m_mouseMoveIgnore = 0; + + // enable ctrl+alt+del, alt+tab, etc + if (m_is95Family) { + DWORD dummy = 0; + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0); + } + + // install jump zones + onConfigure(); + + // restore active window and hide our window + onEnter(); + + // all messages prior to now are invalid + nextMark(); +} + +void CMSWindowsPrimaryScreen::onEnter() +{ + // restore the active window and hide our window. we can only set + // the active window for another thread if we first attach our input + // to that thread. + ReleaseCapture(); + if (m_lastActiveWindow != NULL) { + DWORD myThread = GetCurrentThreadId(); + if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { + // FIXME -- shouldn't raise window if X-Mouse is enabled + // but i have no idea how to do that or check if enabled. + SetActiveWindow(m_lastActiveWindow); + AttachThreadInput(myThread, m_lastActiveThread, FALSE); + } + } + ShowWindow(m_window, SW_HIDE); +} + +bool CMSWindowsPrimaryScreen::onLeave() +{ + // remember the active window before we leave. GetActiveWindow() + // will only return the active window for the thread's queue (i.e. + // our app) but we need the globally active window. get that by + // attaching input to the foreground window's thread then calling + // GetActiveWindow() and then detaching our input. + m_lastActiveWindow = NULL; + m_lastForegroundWindow = GetForegroundWindow(); + m_lastActiveThread = GetWindowThreadProcessId( + m_lastForegroundWindow, NULL); + if (m_lastActiveThread != 0) { + DWORD myThread = GetCurrentThreadId(); + if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { + m_lastActiveWindow = GetActiveWindow(); + AttachThreadInput(myThread, m_lastActiveThread, FALSE); + } + } + + // show our window + ShowWindow(m_window, SW_SHOW); + + // get keyboard input and capture mouse + SetActiveWindow(m_window); + SetFocus(m_window); + SetCapture(m_window); + + return true; +} + void CMSWindowsPrimaryScreen::nextMark() { - assert(m_window != NULL); + // next mark + ++m_mark; - PostMessage(m_window, SYNERGY_MSG_MARK, ++m_mark, 0); + // mark point in message queue where the mark was changed + PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); +} + +bool CMSWindowsPrimaryScreen::openDesktop() +{ + // install hooks + m_install(m_threadID); + + // note -- we use a fullscreen window to grab input. it should + // be possible to use a 1x1 window but i've run into problems + // with losing keyboard input (focus?) in that case. + // unfortunately, hiding the full screen window (when entering + // the scren causes all other windows to redraw). + SInt32 w, h; + getScreenSize(&w, &h); + + // create the window + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | + WS_EX_TOOLWINDOW, + (LPCTSTR)getClass(), + "Synergy", + WS_POPUP, + 0, 0, w, h, NULL, NULL, + getInstance(), + NULL); + if (m_window == NULL) { + log((CLOG_ERR "failed to create window: %d", GetLastError())); + m_uninstall(); + return false; + } + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + + return true; +} + +void CMSWindowsPrimaryScreen::closeDesktop() +{ + // destroy old window + if (m_window != NULL) { + // restore active window and hide ours + if (m_active) { + onEnter(); + } + + // first remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + // we no longer own the clipboard + if (m_clipboardOwner == m_window) { + m_clipboardOwner = NULL; + } + + // now destroy window + DestroyWindow(m_window); + m_window = NULL; + } + + // unhook + m_uninstall(); +} + +bool CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) +{ + // did we own the clipboard? + bool ownClipboard = (m_clipboardOwner == m_window); + + // destroy old window + if (m_window != NULL) { + // restore active window and hide ours + if (m_active) { + onEnter(); + } + + // first remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + // we no longer own the clipboard + if (ownClipboard) { + m_clipboardOwner = NULL; + } + + // now destroy window + DestroyWindow(m_window); + m_window = NULL; + } + + // unhook + if (m_desk != NULL) { + m_uninstall(); + CloseDesktop(m_desk); + m_desk = NULL; + m_deskName = ""; + } + + // if no new desktop then we're done + if (desk == NULL) { + log((CLOG_INFO "disconnecting desktop")); + return true; + } + + // set the desktop. can only do this when there are no windows + // and hooks on the current desktop owned by this thread. + if (SetThreadDesktop(desk) == 0) { + log((CLOG_ERR "failed to set desktop: %d", GetLastError())); + CloseDesktop(desk); + return false; + } + + // install hooks + m_install(m_threadID); + + // note -- we use a fullscreen window to grab input. it should + // be possible to use a 1x1 window but i've run into problems + // with losing keyboard input (focus?) in that case. + // unfortunately, hiding the full screen window (when entering + // the scren causes all other windows to redraw). + SInt32 w, h; + getScreenSize(&w, &h); + + // create the window + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | + WS_EX_TOOLWINDOW, + (LPCTSTR)getClass(), + "Synergy", + WS_POPUP, + 0, 0, w, h, NULL, NULL, + getInstance(), + NULL); + if (m_window == NULL) { + log((CLOG_ERR "failed to create window: %d", GetLastError())); + m_uninstall(); + CloseDesktop(desk); + return false; + } + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + + // reassert clipboard ownership + if (ownClipboard) { + // FIXME + } + + // save new desktop + m_desk = desk; + m_deskName = getDesktopName(m_desk); + log((CLOG_INFO "switched to desktop %s", m_deskName.c_str())); + + // get active window and show ours + if (m_active) { + onLeave(); + } + else { + // set jump zones + onConfigure(); + + // all messages prior to now are invalid + nextMark(); + } + + return true; +} + +CString CMSWindowsPrimaryScreen::getCurrentDesktopName() const +{ + return m_deskName; } static const KeyID g_virtualKey[] = diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index fa54388c..a568171a 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -1,10 +1,11 @@ #ifndef CMSWINDOWSPRIMARYSCREEN_H #define CMSWINDOWSPRIMARYSCREEN_H -#include "KeyTypes.h" -#include "MouseTypes.h" #include "CMSWindowsScreen.h" #include "IPrimaryScreen.h" +#include "MouseTypes.h" +#include "CString.h" +#include "CSynergyHook.h" class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { public: @@ -36,12 +37,24 @@ protected: virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); virtual void onOpenDisplay(); virtual void onCloseDisplay(); + virtual CString getCurrentDesktopName() const; private: - void doEnter(); + void enterNoWarp(); + void onEnter(); + bool onLeave(); + // discard posted messages void nextMark(); + // open/close desktop (for windows 95/98/me) + bool openDesktop(); + void closeDesktop(); + + // make desk the thread desktop (for windows NT/2000/XP) + bool switchDesktop(HDESK desk); + + // key and button queries KeyID mapKey(WPARAM keycode, LPARAM info, KeyModifierMask* maskOut); ButtonID mapButton(WPARAM button) const; @@ -50,19 +63,51 @@ private: private: CServer* m_server; + + // true if windows 95/98/me bool m_is95Family; - bool m_active; + + // the main loop's thread id + DWORD m_threadID; + + // the current desk and it's name + HDESK m_desk; + CString m_deskName; + + // our window (for getting clipboard changes) HWND m_window; + + // m_active is true the hooks are relaying events + bool m_active; + + // used to discard queued messages that are no longer needed + UInt32 m_mark; + UInt32 m_markReceived; + + // clipboard stuff HWND m_nextClipboardWindow; HWND m_clipboardOwner; + + // map of key state + BYTE m_keys[256]; + + // position of center pixel of screen + SInt32 m_xCenter, m_yCenter; + + // used to ignore mouse motion + SInt32 m_mouseMoveIgnore; + + // hook library stuff + HINSTANCE m_hookLibrary; + InstallFunc m_install; + UninstallFunc m_uninstall; + SetZoneFunc m_setZone; + SetRelayFunc m_setRelay; + + // stuff for restoring active window HWND m_lastForegroundWindow; HWND m_lastActiveWindow; DWORD m_lastActiveThread; - HINSTANCE m_hookLibrary; - UInt32 m_mark; - UInt32 m_markReceived; - BYTE m_keys[256]; - SInt32 m_xCenter, m_yCenter; }; #endif diff --git a/server/CServer.cpp b/server/CServer.cpp index 8e0fa1d8..578d4dfa 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -298,7 +298,7 @@ void CServer::setInfoNoLock(const CString& screen, info->m_width = w; info->m_height = h; info->m_zoneSize = zoneSize; - log((CLOG_NOTE "screen \"%s\" size=%dx%d zone=%d pos=%d,%d", screen.c_str(), w, h, zoneSize, x, y)); + log((CLOG_INFO "screen \"%s\" size=%dx%d zone=%d pos=%d,%d", screen.c_str(), w, h, zoneSize, x, y)); // send acknowledgement (if screen isn't the primary) if (info->m_protocol != NULL) { @@ -353,7 +353,7 @@ void CServer::grabClipboardNoLock( } // mark screen as owning clipboard - log((CLOG_NOTE "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str())); + log((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str())); clipboard.m_clipboardOwner = screen; clipboard.m_clipboardSeqNum = seqNum; @@ -404,7 +404,7 @@ void CServer::setClipboard(ClipboardID id, } // unmarshall into our clipboard buffer - log((CLOG_NOTE "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + log((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); clipboard.m_clipboardReady = true; clipboard.m_clipboardData = data; clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); @@ -603,17 +603,25 @@ void CServer::onMouseMoveSecondaryNoLock( if (!isLockedToScreenNoLock()) { // find direction of neighbor CConfig::EDirection dir; - if (m_x < 0) + if (m_x < 0) { dir = CConfig::kLeft; - else if (m_x > m_active->m_width - 1) + } + else if (m_x > m_active->m_width - 1) { dir = CConfig::kRight; - else if (m_y < 0) + } + else if (m_y < 0) { dir = CConfig::kTop; - else if (m_y > m_active->m_height - 1) + } + else if (m_y > m_active->m_height - 1) { dir = CConfig::kBottom; - else + } + else { newScreen = m_active; + // keep compiler quiet about unset variable + dir = CConfig::kLeft; + } + // get neighbor if we should switch if (newScreen == NULL) { log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->m_name.c_str(), CConfig::dirName(dir))); @@ -709,7 +717,7 @@ void CServer::switchScreen(CScreenInfo* dst, assert(x >= 0 && y >= 0 && x < dst->m_width && y < dst->m_height); assert(m_active != NULL); - log((CLOG_NOTE "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y)); + log((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y)); // FIXME -- we're not locked here but we probably should be // record new position @@ -1485,7 +1493,7 @@ void CServer::removeConnection(const CString& name) m_y = m_primaryInfo->m_height >> 1; // don't notify active screen since it probably already disconnected - log((CLOG_NOTE "jump from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y)); + log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y)); // cut over m_active = m_primaryInfo; diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 21bf518d..de47ab5f 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -33,28 +33,26 @@ typedef struct tagMOUSEHOOKSTRUCTWin2000 { #pragma data_seg("shared") // all data in this shared section *must* be initialized -static HINSTANCE g_hinstance = NULL; -static DWORD g_process = NULL; -static EWheelSupport g_wheelSupport = kWheelNone; -static UINT g_wmMouseWheel = 0; -static HWND g_hwnd = NULL; -static HHOOK g_keyboard = NULL; -static HHOOK g_mouse = NULL; -static HHOOK g_cbt = NULL; -static HHOOK g_getMessage = NULL; -static HANDLE g_keyHookThread = NULL; +static HINSTANCE g_hinstance = NULL; +static DWORD g_process = NULL; +static EWheelSupport g_wheelSupport = kWheelNone; +static UINT g_wmMouseWheel = 0; +static DWORD g_threadID = 0; +static HHOOK g_keyboard = NULL; +static HHOOK g_mouse = NULL; +static HHOOK g_cbt = NULL; +static HHOOK g_getMessage = NULL; +static HANDLE g_keyHookThread = NULL; static DWORD g_keyHookThreadID = 0; -static HANDLE g_keyHookEvent = NULL; -static HHOOK g_keyboardLL = NULL; -static bool g_relay = false; -static SInt32 g_zoneSize = 0; -static UInt32 g_zoneSides = 0; -static SInt32 g_wScreen = 0; -static SInt32 g_hScreen = 0; -static HCURSOR g_cursor = NULL; -static DWORD g_cursorThread = 0; - -static int foo = 0; +static HANDLE g_keyHookEvent = NULL; +static HHOOK g_keyboardLL = NULL; +static bool g_relay = false; +static SInt32 g_zoneSize = 0; +static UInt32 g_zoneSides = 0; +static SInt32 g_wScreen = 0; +static SInt32 g_hScreen = 0; +static HCURSOR g_cursor = NULL; +static DWORD g_cursorThread = 0; #pragma data_seg() @@ -91,16 +89,7 @@ static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) if (code >= 0) { if (g_relay) { // forward message to our window - PostMessage(g_hwnd, SYNERGY_MSG_KEY, wParam, lParam); - -/* XXX -- this doesn't seem to work/help with lost key events - // if the active window isn't our window then make it - // active. - const bool wrongFocus = (GetActiveWindow() != g_hwnd); - if (wrongFocus) { - SetForegroundWindow(g_hwnd); - } -*/ + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, wParam, lParam); // let certain keys pass through switch (wParam) { @@ -133,59 +122,65 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: - PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_BUTTON, wParam, 0); + PostThreadMessage(g_threadID, + SYNERGY_MSG_MOUSE_BUTTON, wParam, 0); return 1; - case WM_MOUSEWHEEL: { - // win2k and other systems supporting WM_MOUSEWHEEL in - // the mouse hook are gratuitously different (and poorly - // documented). - switch (g_wheelSupport) { - case kWheelModern: { + case WM_MOUSEWHEEL: + { + // win2k and other systems supporting WM_MOUSEWHEEL in + // the mouse hook are gratuitously different (and poorly + // documented). + switch (g_wheelSupport) { + case kWheelModern: { + const MOUSEHOOKSTRUCT* info = + (const MOUSEHOOKSTRUCT*)lParam; + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, + static_cast( + LOWORD(info->dwExtraInfo)), 0); + break; + } + + case kWheelWin2000: { + const MOUSEHOOKSTRUCTWin2000* info = + (const MOUSEHOOKSTRUCTWin2000*)lParam; + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, + static_cast( + HIWORD(info->mouseData)), 0); + break; + } + + default: + break; + } + } + return 1; + + case WM_MOUSEMOVE: + { const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; - PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL, - static_cast(LOWORD(info->dwExtraInfo)), 0); - break; - } + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; - case kWheelWin2000: { - const MOUSEHOOKSTRUCTWin2000* info = - (const MOUSEHOOKSTRUCTWin2000*)lParam; - PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL, - static_cast(HIWORD(info->mouseData)), 0); - break; - } + // we want the cursor to be hidden at all times so we + // hide the cursor on whatever window has it. but then + // we have to show the cursor whenever we leave that + // window (or at some later time before we stop relaying). + // so check the window with the cursor. if it's not the + // same window that had it before then show the cursor + // in the last window and hide it in this window. + DWORD thread = GetWindowThreadProcessId(info->hwnd, NULL); + if (thread != g_cursorThread) { + restoreCursor(); + hideCursor(thread); + } - default: - break; + // relay the motion + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); } return 1; } - - case WM_MOUSEMOVE: { - const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; - SInt32 x = (SInt32)info->pt.x; - SInt32 y = (SInt32)info->pt.y; - - // we want the cursor to be hidden at all times so we - // hide the cursor on whatever window has it. but then - // we have to show the cursor whenever we leave that - // window (or at some later time before we stop relaying). - // so check the window with the cursor. if it's not the - // same window that had it before then show the cursor - // in the last window and hide it in this window. - DWORD thread = GetWindowThreadProcessId(info->hwnd, NULL); - if (thread != g_cursorThread) { - restoreCursor(); - hideCursor(thread); - } - - // relay the motion - PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y); - return 1; - } - } } else { // check for mouse inside jump zone @@ -209,7 +204,7 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) // if inside then eat event and notify our window if (inside) { restoreCursor(); - PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_MOVE, x, y); + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); return 1; } } @@ -222,15 +217,7 @@ static LRESULT CALLBACK cbtHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { if (g_relay) { - switch (code) { - case HCBT_ACTIVATE: - case HCBT_SETFOCUS: - // discard unless activating our window - if (reinterpret_cast(wParam) != g_hwnd) { - return 1; - } - break; - } + // do nothing for now. may add something later. } } @@ -244,7 +231,8 @@ static LRESULT CALLBACK getMessageHook(int code, WPARAM wParam, LPARAM lParam) MSG* msg = reinterpret_cast(lParam); if (msg->message == g_wmMouseWheel) { // post message to our window - PostMessage(g_hwnd, SYNERGY_MSG_MOUSE_WHEEL, msg->wParam, 0); + PostThreadMessage(g_threadID, + SYNERGY_MSG_MOUSE_WHEEL, msg->wParam, 0); // zero out the delta in the message so it's (hopefully) // ignored @@ -296,7 +284,8 @@ static LRESULT CALLBACK keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) // FIXME -- bit 30 should be set if key was already down // forward message to our window - PostMessage(g_hwnd, SYNERGY_MSG_KEY, info->vkCode, lParam); + PostThreadMessage(g_threadID, + SYNERGY_MSG_KEY, info->vkCode, lParam); // discard event return 1; @@ -436,16 +425,18 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) extern "C" { -int install(HWND hwnd) +int install(DWORD threadID) { + assert(g_threadID == 0); assert(g_hinstance != NULL); assert(g_keyboard == NULL); assert(g_mouse == NULL); assert(g_cbt == NULL); assert(g_wheelSupport != kWheelOld || g_getMessage == NULL); - // save window - g_hwnd = hwnd; + // save thread id. we'll post messages to this thread's + // message queue. + g_threadID = threadID; // set defaults g_relay = false; @@ -465,7 +456,7 @@ int install(HWND hwnd) g_hinstance, 0); if (g_keyboard == NULL) { - g_hwnd = NULL; + g_threadID = NULL; return 0; } @@ -478,7 +469,7 @@ int install(HWND hwnd) // uninstall keyboard hook before failing UnhookWindowsHookEx(g_keyboard); g_keyboard = NULL; - g_hwnd = NULL; + g_threadID = NULL; return 0; } @@ -493,7 +484,7 @@ int install(HWND hwnd) UnhookWindowsHookEx(g_mouse); g_keyboard = NULL; g_mouse = NULL; - g_hwnd = NULL; + g_threadID = NULL; return 0; } @@ -563,7 +554,7 @@ int uninstall(void) g_mouse = NULL; g_cbt = NULL; g_getMessage = NULL; - g_hwnd = NULL; + g_threadID = 0; // show the cursor restoreCursor(); diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index ec99afa2..7d6fa53b 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -20,14 +20,14 @@ #define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y #define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0015 // delta; -typedef int (*InstallFunc)(HWND); +extern "C" { + +typedef int (*InstallFunc)(DWORD targetQueueThreadID); typedef int (*UninstallFunc)(void); typedef void (*SetZoneFunc)(UInt32, SInt32, SInt32, SInt32); typedef void (*SetRelayFunc)(void); -extern "C" { - -CSYNERGYHOOK_API int install(HWND); +CSYNERGYHOOK_API int install(DWORD); CSYNERGYHOOK_API int uninstall(void); CSYNERGYHOOK_API void setZone(UInt32 sides, SInt32 w, SInt32 h, SInt32 jumpZoneSize); diff --git a/server/makehook.dsp b/server/makehook.dsp index 7a9aac98..507157ef 100644 --- a/server/makehook.dsp +++ b/server/makehook.dsp @@ -23,8 +23,8 @@ CFG=makehook - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" MTL=midl.exe !IF "$(CFG)" == "makehook - Win32 Release" diff --git a/server/server.cpp b/server/server.cpp index feaa4f5b..e33c3383 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -1,6 +1,7 @@ #include "CServer.h" #include "CConfig.h" #include "CLog.h" +#include "CLock.h" #include "CMutex.h" #include "CNetwork.h" #include "CPlatform.h" @@ -11,15 +12,20 @@ #include "stdfstream.h" #include +// platform dependent name of a daemon +#if defined(CONFIG_PLATFORM_WIN32) +#define DAEMON "service" +#define DAEMON_NAME "Synergy Server" +#elif defined(CONFIG_PLATFORM_UNIX) +#define DAEMON "daemon" +#define DAEMON_NAME "synergyd" +#endif + // configuration file name #if defined(CONFIG_PLATFORM_WIN32) #define CONFIG_NAME "synergy.sgc" -#define CONFIG_USER_DIR "%HOME%/" -#define CONFIG_SYS_DIR "" #elif defined(CONFIG_PLATFORM_UNIX) #define CONFIG_NAME "synergy.conf" -#define CONFIG_USER_DIR "~/" -#define CONFIG_SYS_DIR "/etc/" #endif // @@ -29,6 +35,8 @@ static const char* pname = NULL; static bool s_restartable = true; static bool s_daemon = true; +static bool s_install = false; +static bool s_uninstall = false; static const char* s_configFile = NULL; static const char* s_logFilter = NULL; static CConfig s_config; @@ -54,47 +62,97 @@ static void logLock(bool lock) // -// main +// platform independent main // -void realMain() +static CServer* s_server = NULL; + +static int realMain(CMutex* mutex) { - // initialize threading library - CThread::init(); + // s_serverLock should have mutex locked on entry - // make logging thread safe - CMutex logMutex; - s_logMutex = &logMutex; - CLog::setLock(&logLock); - - CServer* server = NULL; try { - // initialize network library - CNetwork::init(); + // initialize threading library + CThread::init(); - // if configuration has no screens then add this system - // as the default - if (s_config.begin() == s_config.end()) { - s_config.addScreen("primary"); + // make logging thread safe + CMutex logMutex; + s_logMutex = &logMutex; + CLog::setLock(&logLock); + + bool locked = true; + try { + // initialize network library + CNetwork::init(); + + // if configuration has no screens then add this system + // as the default + if (s_config.begin() == s_config.end()) { + s_config.addScreen("primary"); + } + + // create server + s_server = new CServer(); + + // run server (unlocked) + if (mutex != NULL) { + mutex->unlock(); + } + locked = false; + s_server->setConfig(s_config); + s_server->run(); + locked = true; + if (mutex != NULL) { + mutex->lock(); + } + + // clean up + delete s_server; + s_server = NULL; + CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; + } + catch (...) { + // clean up + if (!locked && mutex != NULL) { + mutex->lock(); + } + delete s_server; + s_server = NULL; + CNetwork::cleanup(); + CLog::setLock(NULL); + s_logMutex = NULL; + throw; } - - // run server - server = new CServer(); - server->setConfig(s_config); - server->run(); - - // clean up - delete server; - CNetwork::cleanup(); - CLog::setLock(NULL); - s_logMutex = NULL; } - catch (...) { - delete server; - CNetwork::cleanup(); - CLog::setLock(NULL); - s_logMutex = NULL; - throw; + catch (XBase& e) { + log((CLOG_CRIT "failed: %s", e.what())); + return 16; + } + catch (XThread&) { + // terminated + return 1; + } + + return 0; +} + +static int restartMain() +{ + return realMain(NULL); +} + +// invoke realMain and wait for it. if s_restartable then keep +// restarting realMain until it returns a terminate code. +static int restartableMain() +{ + if (s_restartable) { + CPlatform platform; + return platform.restart(restartMain, 16); + } + else { + return realMain(NULL); } } @@ -103,11 +161,9 @@ void realMain() // command line parsing // -static void bye() -{ - log((CLOG_PRINT "Try `%s --help' for more information.", pname)); - exit(1); -} +#define BYE "\nTry `%s --help' for more information." + +static void (*bye)(int) = &exit; static void version() { @@ -125,12 +181,18 @@ static void version() static void help() { + CPlatform platform; + log((CLOG_PRINT "Usage: %s" " [--config ]" " [--debug ]" -" [--daemon|--no-daemon]" +" [--"DAEMON"|--no-"DAEMON"]" " [--restart|--no-restart]\n" +"or\n" +" --install\n" +" --uninstall\n" +"\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" " -c, --config use the named configuration file instead\n" @@ -138,58 +200,38 @@ static void help() " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" -" -f, --no-daemon run the server in the foreground.\n" -" --daemon run the server as a daemon.\n" +" -f, --no-"DAEMON" run the server in the foreground.\n" +"* --"DAEMON" run the server as a "DAEMON".\n" " -1, --no-restart do not try to restart the server if it fails for\n" " some reason.\n" -" --restart restart the server automatically if it fails.\n" +"* --restart restart the server automatically if it fails.\n" +" --install install server as a "DAEMON".\n" +" --uninstall uninstall server "DAEMON".\n" " -h, --help display this help and exit.\n" " --version display version information and exit.\n" "\n" -"By default, the server is a restartable daemon. If no configuration file\n" -"pathname is provided then the first of the following to load sets the\n" -"configuration:\n" -" " CONFIG_USER_DIR CONFIG_NAME "\n" -" " CONFIG_SYS_DIR CONFIG_NAME "\n" +"* marks defaults.\n" +"\n" +"If no configuration file pathname is provided then the first of the\n" +"following to load sets the configuration:\n" +" %s\n" +" %s\n" "If no configuration file can be loaded then the configuration uses its\n" "defaults with just the server screen.\n" "\n" "Where log messages go depends on the platform and whether or not the\n" -"server is running as a daemon.", - pname)); - -} - -static bool loadConfig(const char* pathname, bool require) -{ - assert(pathname != NULL); - - try { - // load configuration - log((CLOG_DEBUG "opening configuration \"%s\"", pathname)); - std::ifstream configStream(pathname); - if (!configStream) { - throw XConfigRead("cannot open configuration"); - } - configStream >> s_config; - log((CLOG_DEBUG "configuration read successfully")); - return true; - } - catch (XConfigRead& e) { - if (require) { - log((CLOG_PRINT "%s: cannot read configuration '%s'", - pname, pathname)); - exit(1); - } - else { - log((CLOG_DEBUG "cannot read configuration \"%s\"", pathname)); - } - } - return false; +"server is running as a "DAEMON".", + pname, + platform.addPathComponent( + platform.getUserDirectory(), + CONFIG_NAME).c_str(), + platform.addPathComponent( + platform.getSystemDirectory(), + CONFIG_NAME).c_str())); } static bool isArg(int argi, - int argc, char** argv, + int argc, const char** argv, const char* name1, const char* name2, int minRequiredParameters = 0) @@ -198,9 +240,9 @@ static bool isArg(int argi, (name2 != NULL && strcmp(argv[argi], name2) == 0)) { // match. check args left. if (argi + minRequiredParameters >= argc) { - log((CLOG_PRINT "%s: missing arguments for `%s'", - pname, argv[argi])); - bye(); + log((CLOG_PRINT "%s: missing arguments for `%s'" BYE, + pname, argv[argi], pname)); + bye(2); } return true; } @@ -209,7 +251,7 @@ static bool isArg(int argi, return false; } -static void parse(int argc, char** argv) +static void parse(int argc, const char** argv) { assert(pname != NULL); assert(argv != NULL); @@ -228,12 +270,12 @@ static void parse(int argc, char** argv) s_configFile = argv[++i]; } - else if (isArg(i, argc, argv, "-f", "--no-daemon")) { + else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) { // not a daemon s_daemon = false; } - else if (isArg(i, argc, argv, NULL, "--daemon")) { + else if (isArg(i, argc, argv, NULL, "--"DAEMON)) { // daemonize s_daemon = true; } @@ -250,12 +292,42 @@ static void parse(int argc, char** argv) else if (isArg(i, argc, argv, "-h", "--help")) { help(); - exit(1); + bye(0); } else if (isArg(i, argc, argv, NULL, "--version")) { version(); - exit(1); + bye(0); + } + + else if (isArg(i, argc, argv, NULL, "--install")) { +#if !defined(CONFIG_PLATFORM_WIN32) + log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE, + pname, argv[i], pname)); + bye(2); +#endif + s_install = true; + if (s_uninstall) { + log((CLOG_PRINT "%s: `--install' and `--uninstall'" + " are mutually exclusive" BYE, + pname, argv[i], pname)); + bye(2); + } + } + + else if (isArg(i, argc, argv, NULL, "--uninstall")) { +#if !defined(CONFIG_PLATFORM_WIN32) + log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE, + pname, argv[i], pname)); + bye(2); +#endif + s_uninstall = true; + if (s_install) { + log((CLOG_PRINT "%s: `--install' and `--uninstall'" + " are mutually exclusive" BYE, + pname, argv[i], pname)); + bye(2); + } } else if (isArg(i, argc, argv, "--", NULL)) { @@ -265,8 +337,9 @@ static void parse(int argc, char** argv) } else if (argv[i][0] == '-') { - log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); - bye(); + log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + pname, argv[i], pname)); + bye(2); } else { @@ -277,101 +350,76 @@ static void parse(int argc, char** argv) // no non-option arguments are allowed if (i != argc) { - log((CLOG_PRINT "%s: unrecognized option `%s'", pname, argv[i])); - bye(); + log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + pname, argv[i], pname)); + bye(2); + } + + // increase default filter level for daemon. the user must + // explicitly request another level for a daemon. + if (s_daemon && s_logFilter == NULL) { +#if defined(CONFIG_PLATFORM_WIN32) + if (CPlatform::isWindows95Family()) { + // windows 95 has no place for logging so avoid showing + // the log console window. + s_logFilter = "FATAL"; + } + else +#endif + { + s_logFilter = "NOTE"; + } } // set log filter if (!CLog::setFilter(s_logFilter)) { - log((CLOG_PRINT "%s: unrecognized log level `%s'", pname, s_logFilter)); - bye(); + log((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, + pname, s_logFilter, pname)); + bye(2); } +} - // load the config file, if any +static bool loadConfig(const char* pathname, bool require) +{ + assert(pathname != NULL); + + try { + // load configuration + log((CLOG_DEBUG "opening configuration \"%s\"", pathname)); + std::ifstream configStream(pathname); + if (!configStream) { + throw XConfigRead("cannot open configuration"); + } + configStream >> s_config; + log((CLOG_DEBUG "configuration read successfully")); + return true; + } + catch (XConfigRead&) { + if (require) { + log((CLOG_PRINT "%s: cannot read configuration '%s'", + pname, pathname)); + bye(3); + } + else { + log((CLOG_DEBUG "cannot read configuration \"%s\"", pathname)); + } + } + return false; +} + +static void loadConfig() +{ + // load the config file, if specified if (s_configFile != NULL) { // require the user specified file to load correctly loadConfig(s_configFile, true); } -} - -// -// platform dependent entry points -// - -#if defined(CONFIG_PLATFORM_WIN32) - -#include "CMSWindowsScreen.h" -#include - -int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) -{ - CPlatform platform; - - // save instance - CMSWindowsScreen::init(instance); - - // get program name - pname = platform.getBasename(argv[0]); - -// FIXME -- direct CLog to MessageBox - - parse(__argc, __argv); - -// FIXME -- undirect CLog from MessageBox -// FIXME -- if daemon then use win32 event log (however that's done), -// otherwise do what? want to use console window for debugging but -// not otherwise. - - // load the configuration file if we haven't already - if (s_configFile == NULL) { -// FIXME - } - -// FIXME - if (__argc != 1) { - CString msg = "no arguments allowed. exiting."; - MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); - return 1; - } - - try { - realMain(); - return 0; - } - catch (XBase& e) { - log((CLOG_CRIT "failed: %s", e.what())); - CString msg = "failed: "; - msg += e.what(); - MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR); - return 1; - } - catch (XThread&) { - // terminated - return 1; - } -} - -#elif defined(CONFIG_PLATFORM_UNIX) - -#include // fork() -#include // wait() -#include // wait() - -int main(int argc, char** argv) -{ - CPlatform platform; - - // get program name - pname = platform.getBasename(argv[0]); - - // parse command line - parse(argc, argv); - - // load the configuration file if we haven't already - if (s_configFile == NULL) { + // load the default configuration if no explicit file given + else { // get the user's home directory. use the effective user id // so a user can't get a setuid root program to load his file. + CPlatform platform; bool loaded = false; CString path = platform.getUserDirectory(); if (!path.empty()) { @@ -390,62 +438,236 @@ int main(int argc, char** argv) } } } +} + +// +// platform dependent entry points +// + +#if defined(CONFIG_PLATFORM_WIN32) + +#include "CMSWindowsScreen.h" + +static bool logMessageBox(int priority, const char* msg) +{ + if (priority <= CLog::kFATAL) { + MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); + return true; + } + else { + return false; + } +} + +static void byeThrow(int x) +{ + throw CWin32Platform::CDaemonFailed(x); +} + +static void daemonStop(void) +{ + s_server->quit(); +} + +static int daemonStartup(IPlatform* iplatform, + int argc, const char** argv) +{ + // get platform pointer + CWin32Platform* platform = static_cast(iplatform); + + // catch errors that would normally exit + bye = &byeThrow; + + // parse command line + s_install = false; + s_uninstall = false; + parse(argc, argv); + if (s_install || s_uninstall) { + // not allowed to install/uninstall from service + throw CWin32Platform::CDaemonFailed(1); + } + + // load configuration + loadConfig(); + + // run as a service + return platform->runDaemon(realMain, daemonStop); +} + +static int daemonStartup95(IPlatform*, int, const char**) +{ + return realMain(NULL); +} + +static bool logDiscard(int, const char*) +{ + return true; +} + +static bool s_die = false; + +static void checkParse(int e) +{ + // anything over 1 means invalid args. 1 means missing args. + // 0 means graceful exit. we plan to exit for anything but + // 1 (missing args); the service control manager may supply + // the missing arguments so we don't exit in that case. + s_die = (e != 1); + throw s_die; +} + +int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +{ + CPlatform platform; + + // save instance + CMSWindowsScreen::init(instance); + + // get program name + pname = platform.getBasename(__argv[0]); + + // parse command line without reporting errors but recording if + // the app would've exited. this is too avoid showing a dialog + // box if we're being started as a service because we shouldn't + // take to long to startup as a service. this mostly works but + // will choke if the service control manager passes --install + // or --uninstall (but that's unlikely). + CLog::setOutputter(&logDiscard); + bye = &checkParse; + try { + parse(__argc, const_cast(__argv)); + } + catch (...) { + // ignore + } + + // if we're not starting as an NT service then reparse the command + // line normally. + if (s_die || !s_daemon || s_install || s_uninstall || + CWin32Platform::isWindows95Family()) { + // send PRINT and FATAL output to a message box + CLog::setOutputter(&logMessageBox); + + // exit on bye + bye = &exit; + + // reparse + parse(__argc, const_cast(__argv)); + } + + // if starting as a daemon then we ignore the startup command line + // here. we'll parse the command line passed in when the service + // control manager calls us back. + else { + // do nothing + } + + // install/uninstall + if (s_install) { + // get the full path to this program + TCHAR path[MAX_PATH]; + if (GetModuleFileName(NULL, path, + sizeof(path) / sizeof(path[0])) == 0) { + log((CLOG_CRIT "cannot determine absolute path to program")); + return 16; + } + + // construct the command line to start the service with + CString commandLine; + commandLine += "--"DAEMON; + if (s_restartable) { + commandLine += " --restart"; + } + else { + commandLine += " --no-restart"; + } + if (s_logFilter != NULL) { + commandLine += " --debug "; + commandLine += s_logFilter; + } + if (s_configFile != NULL) { + commandLine += " --config \""; + commandLine += s_configFile; + commandLine += "\""; + } + + // install + if (!platform.installDaemon(DAEMON_NAME, + "Shares this system's mouse and keyboard with others.", + path, commandLine.c_str())) { + log((CLOG_CRIT "failed to install service")); + return 16; + } + log((CLOG_PRINT "installed successfully")); + return 0; + } + else if (s_uninstall) { + if (!platform.uninstallDaemon(DAEMON_NAME)) { + log((CLOG_CRIT "failed to uninstall service")); + return 16; + } + log((CLOG_PRINT "uninstalled successfully")); + return 0; + } + + // load configuration + loadConfig(); // daemonize if requested + int result; if (s_daemon) { - if (!platform.daemonize("synergyd")) { + if (CWin32Platform::isWindows95Family()) { + result = platform.daemonize(DAEMON_NAME, &daemonStartup95); + } + else { + result = platform.daemonize(DAEMON_NAME, &daemonStartup); + } + if (result == -1) { + log((CLOG_CRIT "failed to start as a service")); + return 16; + } + } + else { + result = restartableMain(); + } + + return result; +} + +#elif defined(CONFIG_PLATFORM_UNIX) + +static int daemonStartup(IPlatform*, int, const char**) +{ + return restartableMain(); +} + +int main(int argc, char** argv) +{ + CPlatform platform; + + // get program name + pname = platform.getBasename(argv[0]); + + // parse command line + parse(argc, const_cast(argv)); + + // load configuration + loadConfig(); + + // daemonize if requested + int result; + if (s_daemon) { + result = platform.daemonize(DAEMON_NAME, &daemonStartup); + if (result == -1) { log((CLOG_CRIT "failed to daemonize")); return 16; } } - - // run the server. if running as a daemon then run it in a child - // process and restart it as necessary. we have to do this in case - // the X server restarts because our process cannot recover from - // that. - for (;;) { - // don't fork if not restartable - switch (s_restartable ? fork() : 0) { - default: { - // parent process. wait for child to exit. - int status; - if (wait(&status) == -1) { - // wait failed. this is unexpected so bail. - log((CLOG_CRIT "wait() failed")); - return 16; - } - - // what happened? if the child exited normally with a - // status less than 16 then the child was deliberately - // terminated so we also terminate. otherwise, we - // loop. - if (WIFEXITED(status) && WEXITSTATUS(status) < 16) { - return 0; - } - break; - } - - case -1: - // fork() failed. log the error and proceed as a child - log((CLOG_WARN "fork() failed; cannot automatically restart on error")); - // fall through - - case 0: - // child process - try { - realMain(); - return 0; - } - catch (XBase& e) { - log((CLOG_CRIT "failed: %s", e.what())); - return 16; - } - catch (XThread&) { - // terminated - return 1; - } - } + else { + result = restartableMain(); } + + return result; } #else diff --git a/server/server.dsp b/server/server.dsp index 46ac171a..b50dd9f7 100644 --- a/server/server.dsp +++ b/server/server.dsp @@ -23,8 +23,8 @@ CFG=server - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe @@ -40,6 +40,7 @@ RSC=rc.exe # PROP Use_Debug_Libraries 0 # PROP Output_Dir "../Release" # PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c @@ -53,7 +54,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../Release/synergyd.exe" !ELSEIF "$(CFG)" == "server - Win32 Debug" @@ -66,6 +67,7 @@ LINK32=link.exe # PROP Use_Debug_Libraries 1 # PROP Output_Dir "../Debug" # PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c @@ -79,7 +81,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../Debug/synergyd.exe" /pdbtype:sept !ENDIF diff --git a/server/synrgyhk.dsp b/server/synrgyhk.dsp index 784e00a8..fe82c7cd 100644 --- a/server/synrgyhk.dsp +++ b/server/synrgyhk.dsp @@ -23,8 +23,8 @@ CFG=synrgyhk - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 295bf37a..fbe00b46 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -3,6 +3,7 @@ #include "IInterface.h" #include "BasicTypes.h" +#include "KeyTypes.h" #include "ClipboardTypes.h" class CServer; diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp index a6417067..e4dae53d 100644 --- a/synergy/synergy.dsp +++ b/synergy/synergy.dsp @@ -23,8 +23,8 @@ CFG=synergy - Win32 Debug # Begin Project # PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "millpond" -# PROP Scc_LocalPath "." +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe From 562e3aebb52c9863f4576347139ce11923ed61d8 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 8 Jun 2002 21:48:16 +0000 Subject: [PATCH 143/807] checkpoint. --- notes | 206 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 134 insertions(+), 72 deletions(-) diff --git a/notes b/notes index 69004cb2..7ebbcc48 100644 --- a/notes +++ b/notes @@ -1,8 +1,3 @@ -CServer - * must have connection thread in screen info map - allows disconnect if screen map changes and screen is removed - * put mutex locks wherever necessary (like accessing m_active) - server client ------ ------ [accept] <-- connect @@ -16,13 +11,22 @@ query info --> use automake --- -need config file and parser - remove hard coded debugging stuff (in server.cpp) +HTTP stuff + no way to save config using HTTP + should have a button or a page to force save of config + or just save config every time it's changed + should use authentication (at least basic) + +--- +use hostname instead of "primary", "secondary" + +--- +add aliases in config file + any alias of a screen name counts as that screen name + handy for mapping FQDN <-> hostname --- win32: - need to support window stations - login screen on NT is a separate window station provide taskbar icon win32 dll: @@ -30,7 +34,91 @@ win32 dll: don't use standard libraries use custom _DllMainCRTStartup(); just call DllMain() from it. use /MERGE linker switch to merge sections - should use mutex on all public functions + +clean up c:\winnt\synergy.sgc + +desktop switcher program failure when running synergy as service + returns access denied from CreateDesktop() + don't know why + +log window may prevent changing desktop + should start console in a thread just for that purpose + +client will need to be able to retry connecting to server + in case server isn't ready yet, client can camp + +--- +bug with half-duplex keys + win32 server sent num-lock to grace + grace converts to half-duplex so only synthesizes key down + now grace starts server and is locked to screen + grace should ignore half duplex keys when seeing if locked to screen + +--- +CClient and CServer: + not checking in stop() that we're actually running + must mutex m_primary/m_screen + they could be NULL if in the middle of shutting down + +--- +win32 screen saver + win95 etc: + keybd_event can terminate screen saver but not mouse_event + keybd_event resets screen saver start timer but not mouse_event + to kill screen saver (if it's known to be running): + PostMessage (GetActiveWindow(), WM_CLOSE, 0, 0L); + to find if screen saver is running: + FindWindow ("WindowsScreenSaverClass", NULL); + win nt 4: + mouse_event resets screen saver start timer but not keybd_event + neither can stop screen saver because it runs in a separate desktop + to kill screen saver: + BOOL CALLBACK KillScreenSaverFunc(HWND hwnd, LPARAM lParam) + { + if(IsWindowVisible(hwnd)) + PostMessage(hwnd, WM_CLOSE, 0, 0); + return TRUE; + } + + HDESK hdesk; + + hdesk = OpenDesktop(TEXT("Screen-saver"), + 0, + FALSE, + DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS); + if (hdesk) + { + EnumDesktopWindows(hdesk, KillScreenSaverFunc, 0); + CloseDesktop(hdesk); + } + +--- +unix: + restarting fails to connect until a user logs in: + Xlib: no protocol specified + this is caused by login manager + all clients are rejected, except those started by manager itself + workaround is to have synergy started by login manager + should then use --no-restart, though that's not necessary + affects client and server + cannot switch screens on login screen: + xdm,kdm grab keyboard for duration of login screen + synergy cannot switch unless it can grab keyboard + gdm doesn't appear to grab keyboard or mouse for duration + affects server, only + + get auto-restart into platform code + provide a `int(*)(void)' ptr to an auto-restart function + function just forks(), calls ptr, waits, repeats + may need something else on win32 or maybe just skip it + +--- +bug in updating screens + saw a goofy set of screens after update + i think the config was similar to: + A B + C + D E --- not handling international characters @@ -49,42 +137,16 @@ not distinguishing between caps lock and shift lock either the Lock or the Shift key turns off the mode. --- -problems with CLIPBOARD and KDE and NEdit - KDE mail reader (others?) and NEdit never reply to selection request - perhaps a general Motif problem? - currently sending all clipboards to all clients some clients may not need all clipboards add filtering mechanism -must find app that correctly sets the CLIPBOARD selection for testing - synergy must note changes to one or both selections - seems to be having trouble with noting CLIPBOARD after PRIMARY changes - -investigate klipper - KDE's clipboard manager? - it crashed once when server did (see below) - ---- -blown assertion - between client being added to screen list and sending screen size: - attempt to switch to that screen blows assert - x,y isn't inside screen because screen has zero size - maybe have a "ready" flag for each client - only count ready clients when finding neighbor - or don't add client to screen list until fully ready - ---- -got crash when opening netscape window after trying copy in KDE mail - BadWindow in ChangeWindowAttributes - also got it when trying to transfer big clipboard from secondary - or just after transferring and perhaps trying to set locally - --- sometimes not sending correct clipboard some kind of race condition i think can select text then switch and get empty clipboard switching back to owner screen then out gets correct clipboard + dunno if this is still a problem --- win32 not sending correct clipboard @@ -93,11 +155,17 @@ win32 not sending correct clipboard problem persisted; also got 43 bytes for a 2 byte selection --- -slow leaving primary screen on X - possibly due to getting primary clipboards - possibly something else - could try asynchronously getting primary clipboard - race condition on secondary wrt pasting +need timeout on CXWindowsClipboard::CReply objects + should flush replies that are too old + assumption is that requestor is broken + +--- +inconsistencies + exceptions + using getWhat() in some places to return a code, elsewhere returns message + references/pointers + should use pointers for any in/out parameter + but using references in some places --- win32 hook DLL sometimes not unloaded when server is killed? @@ -212,15 +280,6 @@ try to determine keyboard quirks automatically in particular, set the half-duplex flags maybe KBD extension will help determine this ---- -blown assert: - client crashed and primary jumped to primary - left over queued motion events then processed - caused call to CServer::onMouseMoveSecondary - m_protocol is NULL because we're on primary screen - must flush queued events when jumping to primary - check for other things to do when jumping to primary - --- key events sent to console on win me (95/98?) are very slow 1/4 to 1/2 second delay before key up is processed @@ -237,6 +296,14 @@ adjust thread priorities on win32 a very high priority on client fixes delay when typeing into console is it the output console causing the slowness? +--- +must send up key events when leaving screen for keys still down + some key combos get eaten and the key ups aren't sent + e.g. ctrl+alt+del on win2k to grace + mouse is forced back to to win2k + ctrl and alt up not sent to grace + grace thinks ctrl and alt are down 'til user physically press/releases them + --- Accessibility Shortcuts @@ -281,26 +348,21 @@ Q179905 may prevent system from getting locked up when debugging. --- -config file format: - -section screens - : - # args in any order - # not sure if any args -end - -section links - : - # args in any order - [left=] - [right=] - [up=] - [down=] -end +how is CTCPSocket::connect() able to cancel? + looks like it can't + make it asynchronous and use poll() to wait for connection + use testCancel() to make it cancellable --- -will have to redo clipboard stuff on win32 - -need timeout on CReply objects - should flush replies that are too old - assumption is that requestor is broken +win32 rel notes: + HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows\NoInteractiveServices + can prevent an interactive service from running interactively. + how can we tell if we're not running interactively? + even if not interactive we can use MessageBox() to alert user + use MB_SERVICE_NOTIFICATION flag + must use a separate thread in initialization if called from handler + must close dialog if CTRL_LOGOFF_EVENT is received + see SetConsoleCtrlHandler() and CLog (which uses it) + maybe possible to get around NoInteractiveServices + modify DACLs on windows station/desktops to impersonate logged on user + see "Interacting with the User by a Win32 Service" From 8b2a282eb5b4e274746a89be79a7606354163181 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 8 Jun 2002 23:24:40 +0000 Subject: [PATCH 144/807] added aliases to configuration. an alias is another name for a screen. it's expected that the server will want to accept a given client under several names (e.g. the hostname and the FQDN). --- server/CConfig.cpp | 209 +++++++++++++++++++++++++++++++++++++++++---- server/CConfig.h | 44 ++++++++-- server/CServer.cpp | 77 +++++++++-------- server/server.cpp | 9 +- 4 files changed, 274 insertions(+), 65 deletions(-) diff --git a/server/CConfig.cpp b/server/CConfig.cpp index c458c2b1..84f8789c 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -17,19 +17,29 @@ CConfig::~CConfig() // do nothing } -void CConfig::addScreen(const CString& name) +bool CConfig::addScreen(const CString& name) { - if (m_map.count(name) != 0) { - assert(0 && "name already in map"); // FIXME -- throw instead + // alias name must not exist + if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) { + return false; } + + // add cell m_map.insert(std::make_pair(name, CCell())); + + // add name + m_nameToCanonicalName.insert(std::make_pair(name, name)); + + return true; } void CConfig::removeScreen(const CString& name) { - CCellMap::iterator index = m_map.find(name); + // get canonical name and find cell + CString canonical = getCanonicalName(name); + CCellMap::iterator index = m_map.find(canonical); if (index == m_map.end()) { - assert(0 && "name not in map"); // FIXME -- throw instead + return; } // remove from map @@ -39,42 +49,111 @@ void CConfig::removeScreen(const CString& name) for (index = m_map.begin(); index != m_map.end(); ++index) { CCell& cell = index->second; for (SInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) - if (cell.m_neighbor[i] == name) { + if (getCanonicalName(cell.m_neighbor[i]) == canonical) { cell.m_neighbor[i].erase(); } } + + // remove aliases (and canonical name) + for (CNameMap::iterator index = m_nameToCanonicalName.begin(); + index != m_nameToCanonicalName.end(); ) { + if (index->second == canonical) { + m_nameToCanonicalName.erase(index++); + } + else { + ++index; + } + } } void CConfig::removeAllScreens() { m_map.clear(); + m_nameToCanonicalName.clear(); } -void CConfig::connect(const CString& srcName, +bool CConfig::addAlias(const CString& canonical, + const CString& alias) +{ + // alias name must not exist + if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) { + return false; + } + + // canonical name must be known + if (m_nameToCanonicalName.find(canonical) == m_nameToCanonicalName.end()) { + return false; + } + + // insert alias + m_nameToCanonicalName.insert(std::make_pair(alias, canonical)); + + return true; +} + +bool CConfig::removeAlias(const CString& alias) +{ + // must not be a canonical name + if (m_map.find(alias) != m_map.end()) { + return false; + } + + // find alias + CNameMap::iterator index = m_nameToCanonicalName.find(alias); + if (index == m_nameToCanonicalName.end()) { + return false; + } + + // remove alias + m_nameToCanonicalName.erase(index); + + return true; +} + +void CConfig::removeAllAliases() +{ + // remove all names + m_nameToCanonicalName.clear(); + + // put the canonical names back in + for (CCellMap::iterator index = m_map.begin(); + index != m_map.end(); ++index) { + m_nameToCanonicalName.insert( + std::make_pair(index->first, index->first)); + } +} + +bool CConfig::connect(const CString& srcName, EDirection srcSide, const CString& dstName) { // find source cell - CCellMap::iterator index = m_map.find(srcName); + CCellMap::iterator index = m_map.find(getCanonicalName(srcName)); if (index == m_map.end()) { - assert(0 && "name not in map"); // FIXME -- throw instead + return false; } - // connect side (overriding any previous connection) + // connect side (overriding any previous connection). we + // canonicalize in getNeighbor() instead of here because the + // destination name doesn't have to exist yet. index->second.m_neighbor[srcSide - kFirstDirection] = dstName; + + return true; } -void CConfig::disconnect(const CString& srcName, +bool CConfig::disconnect(const CString& srcName, EDirection srcSide) { // find source cell CCellMap::iterator index = m_map.find(srcName); if (index == m_map.end()) { - assert(0 && "name not in map"); // FIXME -- throw instead + return false; } // disconnect side index->second.m_neighbor[srcSide - kFirstDirection].erase(); + + return true; } bool CConfig::isValidScreenName(const CString& name) const @@ -133,20 +212,37 @@ CConfig::const_iterator CConfig::end() const bool CConfig::isScreen(const CString& name) const { - return (m_map.count(name) > 0); + return (m_nameToCanonicalName.count(name) > 0); +} + +bool CConfig::isCanonicalName(const CString& name) const +{ + return CStringUtil::CaselessCmp::equal(getCanonicalName(name), name); +} + +CString CConfig::getCanonicalName(const CString& name) const +{ + CNameMap::const_iterator index = m_nameToCanonicalName.find(name); + if (index == m_nameToCanonicalName.end()) { + return CString(); + } + else { + return index->second; + } } CString CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const { // find source cell - CCellMap::const_iterator index = m_map.find(srcName); + CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); if (index == m_map.end()) { - assert(0 && "name not in map"); // FIXME -- throw instead + return CString(); } // return connection - return index->second.m_neighbor[srcSide - kFirstDirection]; + return getCanonicalName(index->second.m_neighbor[ + srcSide - kFirstDirection]); } const char* CConfig::dirName(EDirection dir) @@ -183,6 +279,7 @@ void CConfig::readSection(std::istream& s) static const char s_section[] = "section:"; static const char s_screens[] = "screens"; static const char s_links[] = "links"; + static const char s_aliases[] = "aliases"; CString line; if (!readLine(s, line)) { @@ -213,6 +310,9 @@ void CConfig::readSection(std::istream& s) else if (name == s_links) { readSectionLinks(s); } + else if (name == s_aliases) { + readSectionAliases(s); + } else { throw XConfigRead("unknown section name"); } @@ -239,7 +339,9 @@ void CConfig::readSectionScreens(std::istream& s) } // add the screen to the configuration - addScreen(name); + if (!addScreen(name)) { + throw XConfigRead("duplicate screen name"); + } } else if (name.empty()) { throw XConfigRead("argument before first screen"); @@ -266,10 +368,13 @@ void CConfig::readSectionLinks(std::istream& s) // strip : screen = line.substr(0, line.size() - 1); - // verify we known about the screen + // verify we know about the screen if (!isScreen(screen)) { throw XConfigRead("unknown screen name"); } + if (!isCanonicalName(screen)) { + throw XConfigRead("cannot use screen name alias here"); + } } else if (screen.empty()) { throw XConfigRead("argument before first screen"); @@ -328,6 +433,47 @@ void CConfig::readSectionLinks(std::istream& s) throw XConfigRead("unexpected end of links section"); } +void CConfig::readSectionAliases(std::istream& s) +{ + CString line; + CString screen; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify we know about the screen + if (!isScreen(screen)) { + throw XConfigRead("unknown screen name"); + } + if (!isCanonicalName(screen)) { + throw XConfigRead("cannot use screen name alias here"); + } + } + else if (screen.empty()) { + throw XConfigRead("argument before first screen"); + } + else { + // verify validity of screen name + if (!isValidScreenName(line)) { + throw XConfigRead("invalid screen alias"); + } + + // add alias + if (!addAlias(screen, line)) { + throw XConfigRead("alias is duplicate screen name"); + } + } + } + throw XConfigRead("unexpected end of aliases section"); +} + // // CConfig I/O @@ -384,6 +530,33 @@ std::ostream& operator<<(std::ostream& s, const CConfig& config) } s << "end" << std::endl; + // aliases section (if there are any) + if (config.m_map.size() != config.m_nameToCanonicalName.size()) { + // map canonical to alias + CConfig::CNameMap aliases; + for (CConfig::CNameMap::const_iterator + index = config.m_nameToCanonicalName.begin(); + index != config.m_nameToCanonicalName.end(); + ++index) { + if (index->first != index->second) { + aliases.insert(std::make_pair(index->second, index->first)); + } + } + + // dump it + CString screen; + s << "section: aliases" << std::endl; + for (CConfig::CNameMap::const_iterator index = aliases.begin(); + index != aliases.end(); ++index) { + if (index->first != screen) { + screen = index->first; + s << "\t" << screen.c_str() << std::endl; + } + s << "\t\t" << index->second.c_str() << std::endl; + } + s << "end" << std::endl; + } + return s; } diff --git a/server/CConfig.h b/server/CConfig.h index 22016182..f97c7a0a 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -6,6 +6,7 @@ #include "XBase.h" #include #include "stdmap.h" +#include "stdset.h" class CConfig; @@ -66,18 +67,33 @@ public: // manipulators // note that case is preserved in screen names but is ignored when - // comparing names. + // comparing names. screen names and their aliases share a + // namespace and must be unique. - // add/remove screens - void addScreen(const CString& name); + // add/remove screens. addScreen() returns false if the name + // already exists. the remove methods automatically remove + // aliases for the named screen and disconnect any connections + // to the removed screen(s). + bool addScreen(const CString& name); void removeScreen(const CString& name); void removeAllScreens(); - // connect edges - void connect(const CString& srcName, + // add/remove alias for a screen name. an alias can be used + // any place the canonical screen name can (except addScreen). + // addAlias() returns false if the alias name already exists + // or the canonical name is unknown. removeAlias() fails if + // the alias is unknown or a canonical name. + bool addAlias(const CString& canonical, + const CString& alias); + bool removeAlias(const CString& alias); + void removeAllAliases(); + + // connect/disconnect edges. both return false if srcName is + // unknown. + bool connect(const CString& srcName, EDirection srcSide, const CString& dstName); - void disconnect(const CString& srcName, + bool disconnect(const CString& srcName, EDirection srcSide); // accessors @@ -85,15 +101,23 @@ public: // returns true iff the given name is a valid screen name. bool isValidScreenName(const CString&) const; - // iterators over screen names + // iterators over (canonical) screen names const_iterator begin() const; const_iterator end() const; // returns true iff name names a screen bool isScreen(const CString& name) const; + // returns true iff name is the canonical name of a screen + bool isCanonicalName(const CString& name) const; + + // returns the canonical name of a screen or the empty string if + // the name is unknown. returns the canonical name if one is given. + CString getCanonicalName(const CString& name) const; + // get the neighbor in the given direction. returns the empty string - // if there is no neighbor in that direction. + // if there is no neighbor in that direction. returns the canonical + // screen name. CString getNeighbor(const CString&, EDirection) const; // read/write a configuration. operator>> will throw XConfigRead @@ -109,9 +133,13 @@ private: void readSection(std::istream&); void readSectionScreens(std::istream&); void readSectionLinks(std::istream&); + void readSectionAliases(std::istream&); private: + typedef std::map CNameMap; + CCellMap m_map; + CNameMap m_nameToCanonicalName; }; class XConfigRead : public XBase { diff --git a/server/CServer.cpp b/server/CServer.cpp index 578d4dfa..85e65179 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -171,38 +171,31 @@ bool CServer::setConfig(const CConfig& config) } // get the set of screens that are connected but are being - // dropped from the configuration. don't add the primary - // screen. also tell the secondary screen to disconnect. + // dropped from the configuration (or who's canonical name + // is changing). don't add the primary screen. also tell + // the secondary screen to disconnect. for (CScreenList::const_iterator index = m_screens.begin(); index != m_screens.end(); ++index) { - if (!config.isScreen(index->first) && - index->second != m_primaryInfo) { + if (index->second != m_primaryInfo && + !config.isCanonicalName(index->first)) { assert(index->second->m_protocol != NULL); index->second->m_protocol->sendClose(); threads.push_back(index->second->m_thread); } } + } - // wait a moment to allow each secondary screen to close - // its connection before we close it (to avoid having our - // socket enter TIME_WAIT). - if (threads.size() > 0) { - CThread::sleep(1.0); - } + // wait a moment to allow each secondary screen to close + // its connection before we close it (to avoid having our + // socket enter TIME_WAIT). + if (threads.size() > 0) { + CThread::sleep(1.0); + } - // cancel the old secondary screen threads - for (CThreads::iterator index = threads.begin(); + // cancel the old secondary screen threads + for (CThreads::iterator index = threads.begin(); index != threads.end(); ++index) { - index->cancel(); - } - - // cut over - m_config = config; - - // tell primary screen about reconfiguration - if (m_primary != NULL) { - m_primary->onConfigure(); - } + index->cancel(); } // wait for old secondary screen threads to disconnect. must @@ -213,6 +206,15 @@ bool CServer::setConfig(const CConfig& config) index->wait(); } + // cut over + CLock lock(&m_mutex); + m_config = config; + + // tell primary screen about reconfiguration + if (m_primary != NULL) { + m_primary->onConfigure(); + } + return true; } @@ -280,7 +282,8 @@ void CServer::setInfoNoLock(const CString& screen, assert(zoneSize >= 0); // screen must be connected - CScreenList::iterator index = m_screens.find(screen); + CString screenName = m_config.getCanonicalName(screen); + CScreenList::iterator index = m_screens.find(screenName); if (index == m_screens.end()) { throw XBadClient(); } @@ -339,14 +342,15 @@ void CServer::grabClipboardNoLock( CClipboardInfo& clipboard = m_clipboards[id]; // screen must be connected - CScreenList::iterator index = m_screens.find(screen); + CString screenName = m_config.getCanonicalName(screen); + CScreenList::iterator index = m_screens.find(screenName); if (index == m_screens.end()) { throw XBadClient(); } // ignore grab if sequence number is old. always allow primary // screen to grab. - if (screen != m_primaryInfo->m_name && + if (screenName != m_primaryInfo->m_name && seqNum < clipboard.m_clipboardSeqNum) { log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", screen.c_str(), id)); return; @@ -354,7 +358,7 @@ void CServer::grabClipboardNoLock( // mark screen as owning clipboard log((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str())); - clipboard.m_clipboardOwner = screen; + clipboard.m_clipboardOwner = screenName; clipboard.m_clipboardSeqNum = seqNum; // no screens have the new clipboard except the sender @@ -363,7 +367,7 @@ void CServer::grabClipboardNoLock( // tell all other screens to take ownership of clipboard for (index = m_screens.begin(); index != m_screens.end(); ++index) { - if (index->first != screen) { + if (index->first != screenName) { CScreenInfo* info = index->second; if (info->m_protocol == NULL) { m_primary->grabClipboard(id); @@ -1460,19 +1464,21 @@ CServer::CScreenInfo* CServer::addConnection( CLock lock(&m_mutex); - // can only have one screen with a given name at any given time - if (m_screens.count(name) != 0) { - throw XDuplicateClient(name); - } - // name must be in our configuration if (!m_config.isScreen(name)) { throw XUnknownClient(name); } + CString screenName = m_config.getCanonicalName(name); + + // can only have one screen with a given name at any given time + if (m_screens.count(screenName) != 0) { + throw XDuplicateClient(name); + } // save screen info - CScreenInfo* newScreen = new CScreenInfo(name, protocol); - m_screens.insert(std::make_pair(name, newScreen)); + CScreenInfo* newScreen = new CScreenInfo(screenName, protocol); + m_screens.insert(std::make_pair(screenName, newScreen)); + log((CLOG_DEBUG "added connection \"%s\" (\"%s\")", name.c_str(), screenName.c_str())); return newScreen; } @@ -1483,7 +1489,8 @@ void CServer::removeConnection(const CString& name) CLock lock(&m_mutex); // find screen info - CScreenList::iterator index = m_screens.find(name); + CString screenName = m_config.getCanonicalName(name); + CScreenList::iterator index = m_screens.find(screenName); assert(index != m_screens.end()); // if this is active screen then we have to jump off of it diff --git a/server/server.cpp b/server/server.cpp index e33c3383..2a54cb03 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -394,14 +394,15 @@ static bool loadConfig(const char* pathname, bool require) log((CLOG_DEBUG "configuration read successfully")); return true; } - catch (XConfigRead&) { + catch (XConfigRead& e) { if (require) { - log((CLOG_PRINT "%s: cannot read configuration '%s'", - pname, pathname)); + log((CLOG_PRINT "%s: cannot read configuration '%s': %s", + pname, pathname, e.what())); bye(3); } else { - log((CLOG_DEBUG "cannot read configuration \"%s\"", pathname)); + log((CLOG_DEBUG "cannot read configuration \"%s\": %s", + pathname, e.what())); } } return false; From 555aa19eb21e4ffa70196e86377b5f05e9a5f156 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 9 Jun 2002 16:53:25 +0000 Subject: [PATCH 145/807] added command line and configuration file arguments to choose the address and port to listen on or connect to. changed the default port and put it in ProtocolTypes.h. the HTTP port is now no longer opened unless the --http argument is supplied or the config file includes it. --- client/client.cpp | 35 +++++++++---- net/CNetwork.cpp | 52 +++++++++++++------ net/CNetwork.h | 12 +++-- net/CNetworkAddress.cpp | 110 ++++++++++++++++++++++++++++++++++++---- net/CNetworkAddress.h | 27 ++++++++-- net/XSocket.cpp | 4 +- net/XSocket.h | 6 +-- server/CConfig.cpp | 94 +++++++++++++++++++++++++++++++++- server/CConfig.h | 13 +++++ server/CHTTPServer.cpp | 3 ++ server/CServer.cpp | 18 +++---- server/CServer.h | 1 + server/server.cpp | 60 ++++++++++++++++++++-- synergy/ProtocolTypes.h | 3 ++ 14 files changed, 376 insertions(+), 62 deletions(-) diff --git a/client/client.cpp b/client/client.cpp index 6297ad38..b34010a0 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -32,7 +32,7 @@ static bool s_daemon = true; static bool s_install = false; static bool s_uninstall = false; static const char* s_logFilter = NULL; -static const char* s_serverName = NULL; +static CNetworkAddress s_serverAddress; // @@ -62,8 +62,6 @@ static CClient* s_client = NULL; static int realMain(CMutex* mutex) { - static const UInt16 port = 50001; // FIXME - try { // initialize threading library CThread::init(); @@ -75,11 +73,7 @@ static int realMain(CMutex* mutex) bool locked = true; try { - // initialize network library - CNetwork::init(); - // create client - CNetworkAddress addr(s_serverName, port); s_client = new CClient("secondary"); // FIXME // run client @@ -87,7 +81,7 @@ static int realMain(CMutex* mutex) mutex->unlock(); } locked = false; - s_client->run(addr); + s_client->run(s_serverAddress); locked = true; if (mutex != NULL) { mutex->lock(); @@ -194,9 +188,13 @@ static void help() "\n" "* marks defaults.\n" "\n" +"The server address is of the form: [][:]. The hostname\n" +"must be the address or hostname of the server. The port overrides the\n" +"default port, %d.\n" +"\n" "Where log messages go depends on the platform and whether or not the\n" "client is running as a "DAEMON".", - pname)); + pname, kDefaultPort)); } @@ -334,7 +332,16 @@ static void parse(int argc, const char** argv) pname, argv[i], pname)); bye(2); } - s_serverName = argv[i]; + + // save server address + try { + s_serverAddress = CNetworkAddress(argv[i], kDefaultPort); + } + catch (XSocketAddress&) { + log((CLOG_PRINT "%s: invalid server address" BYE, + pname, pname)); + bye(2); + } } // increase default filter level for daemon. the user must @@ -445,6 +452,9 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // get program name pname = platform.getBasename(__argv[0]); + // initialize network library + CNetwork::init(); + // parse command line without reporting errors but recording if // the app would've exited. this is too avoid showing a dialog // box if we're being started as a service because we shouldn't @@ -504,7 +514,7 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) commandLine += s_logFilter; } commandLine += " "; - commandLine += s_serverName; + commandLine += s_serverAddress.getHostname().c_str(); // install if (!platform.installDaemon(DAEMON_NAME, @@ -560,6 +570,9 @@ int main(int argc, char** argv) // get program name pname = platform.getBasename(argv[0]); + // initialize network library + CNetwork::init(); + // parse command line parse(argc, const_cast(argv)); diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp index 6bbf7511..30f68913 100644 --- a/net/CNetwork.cpp +++ b/net/CNetwork.cpp @@ -15,13 +15,9 @@ int (PASCAL FAR *CNetwork::ioctl)(CNetwork::Socket s, int cmd, ...); int (PASCAL FAR *CNetwork::getpeername)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); int (PASCAL FAR *CNetwork::getsockname)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); int (PASCAL FAR *CNetwork::getsockopt)(CNetwork::Socket s, int level, int optname, void FAR * optval, CNetwork::AddressLength FAR *optlen); -UInt32 (PASCAL FAR *CNetwork::swaphtonl)(UInt32 hostlong); -UInt16 (PASCAL FAR *CNetwork::swaphtons)(UInt16 hostshort); unsigned long (PASCAL FAR *CNetwork::inet_addr)(const char FAR * cp); char FAR * (PASCAL FAR *CNetwork::inet_ntoa)(struct in_addr in); int (PASCAL FAR *CNetwork::listen)(CNetwork::Socket s, int backlog); -UInt32 (PASCAL FAR *CNetwork::swapntohl)(UInt32 netlong); -UInt16 (PASCAL FAR *CNetwork::swapntohs)(UInt16 netshort); ssize_t (PASCAL FAR *CNetwork::read)(CNetwork::Socket s, void FAR * buf, size_t len); ssize_t (PASCAL FAR *CNetwork::recv)(CNetwork::Socket s, void FAR * buf, size_t len, int flags); ssize_t (PASCAL FAR *CNetwork::recvfrom)(CNetwork::Socket s, void FAR * buf, size_t len, int flags, CNetwork::Address FAR *from, CNetwork::AddressLength FAR * fromlen); @@ -113,6 +109,38 @@ void CNetwork::cleanup() } } +UInt32 CNetwork::swaphtonl(UInt32 v) +{ + static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; + if (s_endian.b[0] == 0x34) + return ((v & 0xff000000lu) >> 24) | + ((v & 0x00ff0000lu) >> 8) | + ((v & 0x0000ff00lu) << 8) | + ((v & 0x000000fflu) << 24); + else + return v; +} + +UInt16 CNetwork::swaphtons(UInt16 v) +{ + static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; + if (s_endian.b[0] == 0x34) + return ((v & 0xff00u) >> 8) | + ((v & 0x00ffu) << 8); + else + return v; +} + +UInt32 CNetwork::swapntohl(UInt32 v) +{ + return swaphtonl(v); +} + +UInt16 CNetwork::swapntohs(UInt16 v) +{ + return swaphtons(v); +} + #define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name) void CNetwork::init2(HMODULE module) @@ -141,13 +169,9 @@ void CNetwork::init2(HMODULE module) setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); - setfunc(swaphtonl, htonl, UInt32 (PASCAL FAR *)(UInt32 hostlong)); - setfunc(swaphtons, htons, UInt16 (PASCAL FAR *)(UInt16 hostshort)); setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); - setfunc(swapntohl, ntohl, UInt32 (PASCAL FAR *)(UInt32 netlong)); - setfunc(swapntohs, ntohs, UInt16 (PASCAL FAR *)(UInt16 netshort)); setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); @@ -259,22 +283,22 @@ ssize_t PASCAL FAR CNetwork::write2(Socket s, #define setfunc(var, name, type) var = (type)::name -static UInt32 myhtonl(UInt32 v) +UInt32 CNetwork::swaphtonl(UInt32 v) { return htonl(v); } -static UInt16 myhtons(UInt16 v) +UInt16 CNetwork::swaphtons(UInt16 v) { return htons(v); } -static UInt32 myntohl(UInt32 v) +UInt32 CNetwork::swapntohl(UInt32 v) { return ntohl(v); } -static UInt16 myntohs(UInt16 v) +UInt16 CNetwork::swapntohs(UInt16 v) { return ntohs(v); } @@ -307,13 +331,9 @@ void CNetwork::init() setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); - setfunc(swaphtonl, myhtonl, UInt32 (PASCAL FAR *)(UInt32 hostlong)); - setfunc(swaphtons, myhtons, UInt16 (PASCAL FAR *)(UInt16 hostshort)); setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); - setfunc(swapntohl, myntohl, UInt32 (PASCAL FAR *)(UInt32 netlong)); - setfunc(swapntohs, myntohs, UInt16 (PASCAL FAR *)(UInt16 netshort)); setfunc(poll, poll, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); setfunc(read, read, ssize_t (PASCAL FAR *)(CNetwork::Socket s, void FAR * buf, size_t len)); setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); diff --git a/net/CNetwork.h b/net/CNetwork.h index 696973da..1fa01afa 100644 --- a/net/CNetwork.h +++ b/net/CNetwork.h @@ -69,6 +69,12 @@ public: static void init(); static void cleanup(); + // byte swapping functions + static UInt32 swaphtonl(UInt32 hostlong); + static UInt16 swaphtons(UInt16 hostshort); + static UInt32 swapntohl(UInt32 netlong); + static UInt16 swapntohs(UInt16 netshort); + // constants static const int Error; @@ -100,7 +106,7 @@ public: kHNone = 0 }; - // socket interface + // socket interface (only available after init()) static Socket (PASCAL FAR *accept)(Socket s, Address FAR *addr, AddressLength FAR *addrlen); static int (PASCAL FAR *bind)(Socket s, const Address FAR *addr, AddressLength namelen); @@ -110,13 +116,9 @@ public: static int (PASCAL FAR *getpeername)(Socket s, Address FAR *name, AddressLength FAR * namelen); static int (PASCAL FAR *getsockname)(Socket s, Address FAR *name, AddressLength FAR * namelen); static int (PASCAL FAR *getsockopt)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen); - static UInt32 (PASCAL FAR *swaphtonl)(UInt32 hostlong); - static UInt16 (PASCAL FAR *swaphtons)(UInt16 hostshort); static unsigned long (PASCAL FAR *inet_addr)(const char FAR * cp); static char FAR * (PASCAL FAR *inet_ntoa)(struct in_addr in); static int (PASCAL FAR *listen)(Socket s, int backlog); - static UInt32 (PASCAL FAR *swapntohl)(UInt32 netlong); - static UInt16 (PASCAL FAR *swapntohs)(UInt16 netshort); static ssize_t (PASCAL FAR *read)(Socket s, void FAR * buf, size_t len); static ssize_t (PASCAL FAR *recv)(Socket s, void FAR * buf, size_t len, int flags); static ssize_t (PASCAL FAR *recvfrom)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen); diff --git a/net/CNetworkAddress.cpp b/net/CNetworkAddress.cpp index 1d832475..0e07605e 100644 --- a/net/CNetworkAddress.cpp +++ b/net/CNetworkAddress.cpp @@ -1,26 +1,102 @@ #include "CNetworkAddress.h" +#include "CString.h" +#include // // CNetworkAddress // -CNetworkAddress::CNetworkAddress(UInt16 port) +CNetworkAddress::CNetworkAddress() : m_port(0) { - if (port == 0) - throw XSocketAddress(XSocketAddress::kBadPort, CString(), port); + // note -- make no calls to CNetwork socket interface here; + // we're often called prior to CNetwork::init(). - struct sockaddr_in* inetAddress = reinterpret_cast(&m_address); + struct sockaddr_in* inetAddress = reinterpret_cast< + struct sockaddr_in*>(&m_address); inetAddress->sin_family = AF_INET; - inetAddress->sin_port = CNetwork::swaphtons(port); + inetAddress->sin_port = CNetwork::swaphtons(m_port); inetAddress->sin_addr.s_addr = INADDR_ANY; memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } -CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) +CNetworkAddress::CNetworkAddress(UInt16 port) : m_port(port) { - if (port == 0) - throw XSocketAddress(XSocketAddress::kBadPort, hostname, port); + if (port == 0) { + throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, m_port); + } + struct sockaddr_in* inetAddress = reinterpret_cast< + struct sockaddr_in*>(&m_address); + inetAddress->sin_family = AF_INET; + inetAddress->sin_port = CNetwork::swaphtons(m_port); + inetAddress->sin_addr.s_addr = INADDR_ANY; + memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); +} + +CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : + m_hostname(hostname_), + m_port(port) +{ + CString hostname(m_hostname); + + if (port == 0) { + throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, m_port); + } + + // check for port suffix + CString::size_type i = hostname.rfind(':'); + if (i != CString::npos && i + 1 < hostname.size()) { + // found a colon. see if it looks like an IPv6 address. + bool colonNotation = false; + bool dotNotation = false; + bool doubleColon = false; + for (CString::size_type j = 0; j < i; ++j) { + if (hostname[j] == ':') { + colonNotation = true; + dotNotation = false; + if (hostname[j + 1] == ':') { + doubleColon = true; + } + } + else if (hostname[j] == '.' && colonNotation) { + dotNotation = true; + } + } + + // port suffix is ambiguous with IPv6 notation if there's + // a double colon and the end of the address is not in dot + // notation. in that case we assume it's not a port suffix. + // the user can replace the double colon with zeros to + // disambiguate. + if ((!doubleColon || dotNotation) || !colonNotation) { + char* end; + long suffixPort = strtol(hostname.c_str() + i + 1, &end, 10); + if (end == hostname.c_str() + i + 1 || *end != '\0' || + suffixPort <= 0 || suffixPort > 65535) { + // bogus port + throw XSocketAddress(XSocketAddress::kBadPort, + m_hostname, m_port); + } + else { + // good port + port = static_cast(suffixPort); + hostname.erase(i); + } + } + } + + // if hostname is empty then use wildcard address + if (hostname.empty()) { + struct sockaddr_in* inetAddress = reinterpret_cast< + struct sockaddr_in*>(&m_address); + inetAddress->sin_family = AF_INET; + inetAddress->sin_port = CNetwork::swaphtons(port); + inetAddress->sin_addr.s_addr = INADDR_ANY; + memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); + return; + } + + // look up name struct hostent* hent = CNetwork::gethostbyname(hostname.c_str()); if (hent == NULL) { switch (CNetwork::gethosterror()) { @@ -37,7 +113,8 @@ CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port) } } - struct sockaddr_in* inetAddress = reinterpret_cast(&m_address); + struct sockaddr_in* inetAddress = reinterpret_cast< + struct sockaddr_in*>(&m_address); inetAddress->sin_family = hent->h_addrtype; inetAddress->sin_port = CNetwork::swaphtons(port); memcpy(&inetAddress->sin_addr, hent->h_addr_list[0], hent->h_length); @@ -49,6 +126,11 @@ CNetworkAddress::~CNetworkAddress() // do nothing } +bool CNetworkAddress::isValid() const +{ + return (m_port != 0); +} + const CNetwork::Address* CNetworkAddress::getAddress() const { return &m_address; @@ -58,3 +140,13 @@ CNetwork::AddressLength CNetworkAddress::getAddressLength() const { return sizeof(m_address); } + +CString CNetworkAddress::getHostname() const +{ + return m_hostname; +} + +UInt16 CNetworkAddress::getPort() const +{ + return m_port; +} diff --git a/net/CNetworkAddress.h b/net/CNetworkAddress.h index 6b1729d5..08750e29 100644 --- a/net/CNetworkAddress.h +++ b/net/CNetworkAddress.h @@ -1,27 +1,48 @@ #ifndef CNETWORKADDRESS_H #define CNETWORKADDRESS_H -#include "BasicTypes.h" #include "CNetwork.h" #include "XSocket.h" - -class CString; +#include "CString.h" +#include "BasicTypes.h" class CNetworkAddress { public: + // invalid address + CNetworkAddress(); + + // wildcard address and given port. port must not be zero. CNetworkAddress(UInt16 port); + + // given address and port. if hostname can be parsed as numerical + // address then that's how it's used, otherwise the hostname is + // looked up. if lookup fails then it throws XSocketAddress. if + // hostname ends in ":[0-9]+" then that suffix is extracted and + // used as the port, overridding the port parameter. neither + // port may be zero. CNetworkAddress(const CString& hostname, UInt16 port); + ~CNetworkAddress(); // manipulators // accessors + // returns true if this is not the invalid address + bool isValid() const; + + // get the address const CNetwork::Address* getAddress() const; CNetwork::AddressLength getAddressLength() const; + // get the hostname and port (as provided in the c'tor) + CString getHostname() const; + UInt16 getPort() const; + private: CNetwork::Address m_address; + CString m_hostname; + UInt16 m_port; }; #endif diff --git a/net/XSocket.cpp b/net/XSocket.cpp index d5478908..983e9eae 100644 --- a/net/XSocket.cpp +++ b/net/XSocket.cpp @@ -5,7 +5,7 @@ // XSocketAddress::XSocketAddress(Error error, - const CString& hostname, SInt16 port) throw() : + const CString& hostname, UInt16 port) throw() : m_error(error), m_hostname(hostname), m_port(port) @@ -23,7 +23,7 @@ CString XSocketAddress::getHostname() const throw() return m_hostname; } -SInt16 XSocketAddress::getPort() const throw() +UInt16 XSocketAddress::getPort() const throw() { return m_port; } diff --git a/net/XSocket.h b/net/XSocket.h index 7a5cc6f5..1a83c99d 100644 --- a/net/XSocket.h +++ b/net/XSocket.h @@ -11,13 +11,13 @@ class XSocketAddress : public XSocket { public: enum Error { kUnknown, kNotFound, kNoAddress, kBadPort }; - XSocketAddress(Error, const CString& hostname, SInt16 port) throw(); + XSocketAddress(Error, const CString& hostname, UInt16 port) throw(); // accessors virtual Error getError() const throw(); virtual CString getHostname() const throw(); - virtual SInt16 getPort() const throw(); + virtual UInt16 getPort() const throw(); protected: // XBase overrides @@ -26,7 +26,7 @@ protected: private: Error m_error; CString m_hostname; - SInt16 m_port; + UInt16 m_port; }; class XSocketErrno : public XSocket, public MXErrno { diff --git a/server/CConfig.cpp b/server/CConfig.cpp index 84f8789c..4d82366d 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -1,4 +1,5 @@ #include "CConfig.h" +#include "ProtocolTypes.h" #include "stdistream.h" #include "stdostream.h" #include @@ -156,6 +157,16 @@ bool CConfig::disconnect(const CString& srcName, return true; } +void CConfig::setSynergyAddress(const CNetworkAddress& addr) +{ + m_synergyAddress = addr; +} + +void CConfig::setHTTPAddress(const CNetworkAddress& addr) +{ + m_httpAddress = addr; +} + bool CConfig::isValidScreenName(const CString& name) const { // name is valid if matches validname @@ -245,6 +256,16 @@ CString CConfig::getNeighbor(const CString& srcName, srcSide - kFirstDirection]); } +const CNetworkAddress& CConfig::getSynergyAddress() const +{ + return m_synergyAddress; +} + +const CNetworkAddress& CConfig::getHTTPAddress() const +{ + return m_httpAddress; +} + const char* CConfig::dirName(EDirection dir) { static const char* s_name[] = { "left", "right", "top", "bottom" }; @@ -277,6 +298,7 @@ bool CConfig::readLine(std::istream& s, CString& line) void CConfig::readSection(std::istream& s) { static const char s_section[] = "section:"; + static const char s_network[] = "network"; static const char s_screens[] = "screens"; static const char s_links[] = "links"; static const char s_aliases[] = "aliases"; @@ -304,7 +326,10 @@ void CConfig::readSection(std::istream& s) } // read section - if (name == s_screens) { + if (name == s_network) { + readSectionNetwork(s); + } + else if (name == s_screens) { readSectionScreens(s); } else if (name == s_links) { @@ -318,6 +343,61 @@ void CConfig::readSection(std::istream& s) } } +void CConfig::readSectionNetwork(std::istream& s) +{ + CString line; + CString name; + while (readLine(s, line)) { + // check for end of section + if (line == "end") { + return; + } + + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead("missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead("missing = in argument"); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead("missing = in argument"); + } + i = line.find_first_not_of(" \t", i + 1); + CString value; + if (i != CString::npos) { + value = line.substr(i); + } + if (value.empty()) { + throw XConfigRead("missing value after ="); + } + + if (name == "address") { + try { + m_synergyAddress = CNetworkAddress(value, kDefaultPort); + } + catch (XSocketAddress&) { + throw XConfigRead("invalid address argument"); + } + } + else if (name == "http") { + try { + m_httpAddress = CNetworkAddress(value, kDefaultPort + 1); + } + catch (XSocketAddress&) { + throw XConfigRead("invalid http argument"); + } + } + else { + throw XConfigRead("unknown argument"); + } + } + throw XConfigRead("unexpected end of screens section"); +} + void CConfig::readSectionScreens(std::istream& s) { CString line; @@ -493,6 +573,18 @@ std::istream& operator>>(std::istream& s, CConfig& config) std::ostream& operator<<(std::ostream& s, const CConfig& config) { + // network section + s << "section: network" << std::endl; + if (config.m_synergyAddress.isValid()) { + s << "\taddress=" << config.m_synergyAddress.getHostname().c_str() << + std::endl; + } + if (config.m_httpAddress.isValid()) { + s << "\thttp=" << config.m_httpAddress.getHostname().c_str() << + std::endl; + } + s << "end" << std::endl; + // screens section s << "section: screens" << std::endl; for (CConfig::const_iterator screen = config.begin(); diff --git a/server/CConfig.h b/server/CConfig.h index f97c7a0a..73f4a911 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -3,6 +3,7 @@ #include "BasicTypes.h" #include "CString.h" +#include "CNetworkAddress.h" #include "XBase.h" #include #include "stdmap.h" @@ -96,6 +97,11 @@ public: bool disconnect(const CString& srcName, EDirection srcSide); + // set the synergy and http listen addresses. there are no + // default addresses. + void setSynergyAddress(const CNetworkAddress&); + void setHTTPAddress(const CNetworkAddress&); + // accessors // returns true iff the given name is a valid screen name. @@ -120,6 +126,10 @@ public: // screen name. CString getNeighbor(const CString&, EDirection) const; + // get the listen addresses + const CNetworkAddress& getSynergyAddress() const; + const CNetworkAddress& getHTTPAddress() const; + // read/write a configuration. operator>> will throw XConfigRead // on error. friend std::istream& operator>>(std::istream&, CConfig&); @@ -131,6 +141,7 @@ public: private: static bool readLine(std::istream&, CString&); void readSection(std::istream&); + void readSectionNetwork(std::istream&); void readSectionScreens(std::istream&); void readSectionLinks(std::istream&); void readSectionAliases(std::istream&); @@ -140,6 +151,8 @@ private: CCellMap m_map; CNameMap m_nameToCanonicalName; + CNetworkAddress m_synergyAddress; + CNetworkAddress m_httpAddress; }; class XConfigRead : public XBase { diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index d85b0978..95cb6d43 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -308,6 +308,7 @@ void CHTTPServer::doProcessPostEditMap( // convert temporary screen map into a regular map CConfig config; + m_server->getConfig(&config); screens.convertTo(config); // set new screen map on server @@ -719,6 +720,8 @@ bool CHTTPServer::CScreenArray::convertFrom( void CHTTPServer::CScreenArray::convertTo( CConfig& config) const { + config.removeAllScreens(); + // add screens and find smallest box containing all screens SInt32 x0 = m_w, x1 = 0, y0 = m_h, y1 = 0; for (SInt32 y = 0; y < m_h; ++y) { diff --git a/server/CServer.cpp b/server/CServer.cpp index 85e65179..475bf5ef 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -83,13 +83,15 @@ void CServer::run() } } - // start listening for HTTP requests - m_httpServer = new CHTTPServer(this); - CThread(new TMethodJob(this, &CServer::acceptHTTPClients)); - // start listening for new clients CThread(new TMethodJob(this, &CServer::acceptClients)); + // start listening for HTTP requests + if (m_config.getHTTPAddress().isValid()) { + m_httpServer = new CHTTPServer(this); + CThread(new TMethodJob(this, &CServer::acceptHTTPClients)); + } + // handle events log((CLOG_DEBUG "starting event handling")); m_primary->run(); @@ -982,11 +984,10 @@ void CServer::acceptClients(void*) // bind to the desired port. keep retrying if we can't bind // the address immediately. CStopwatch timer; - CNetworkAddress addr(50001 /* FIXME -- m_port */); for (;;) { try { log((CLOG_DEBUG1 "binding listen socket")); - listen->bind(addr); + listen->bind(m_config.getSynergyAddress()); break; } catch (XSocketAddressInUse&) { @@ -1166,11 +1167,10 @@ void CServer::acceptHTTPClients(void*) // bind to the desired port. keep retrying if we can't bind // the address immediately. CStopwatch timer; - CNetworkAddress addr(50002 /* FIXME -- m_httpPort */); for (;;) { try { - log((CLOG_DEBUG1 "binding listen socket")); - listen->bind(addr); + log((CLOG_DEBUG1 "binding HTTP listen socket")); + listen->bind(m_config.getHTTPAddress()); break; } catch (XSocketAddressInUse&) { diff --git a/server/CServer.h b/server/CServer.h index 37b1eb8f..ab4c92a2 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -6,6 +6,7 @@ #include "MouseTypes.h" #include "CConfig.h" #include "CClipboard.h" +#include "CNetworkAddress.h" #include "CCondVar.h" #include "CMutex.h" #include "CString.h" diff --git a/server/server.cpp b/server/server.cpp index 2a54cb03..c0c553d5 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -39,6 +39,8 @@ static bool s_install = false; static bool s_uninstall = false; static const char* s_configFile = NULL; static const char* s_logFilter = NULL; +static CNetworkAddress s_synergyAddress; +static CNetworkAddress s_httpAddress; static CConfig s_config; @@ -67,6 +69,7 @@ static void logLock(bool lock) static CServer* s_server = NULL; +#include static int realMain(CMutex* mutex) { // s_serverLock should have mutex locked on entry @@ -82,15 +85,27 @@ static int realMain(CMutex* mutex) bool locked = true; try { - // initialize network library - CNetwork::init(); - // if configuration has no screens then add this system // as the default if (s_config.begin() == s_config.end()) { s_config.addScreen("primary"); } + // set the contact address, if provided, in the config. + // otherwise, if the config doesn't have an address, use + // the default. + if (s_synergyAddress.isValid()) { + s_config.setSynergyAddress(s_synergyAddress); + } + else if (!s_config.getSynergyAddress().isValid()) { + s_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); + } + + // set HTTP address is provided + if (s_httpAddress.isValid()) { + s_config.setHTTPAddress(s_httpAddress); + } + // create server s_server = new CServer(); @@ -195,6 +210,7 @@ static void help() "\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" +" -a, --address
listen for clients on the given address.\n" " -c, --config use the named configuration file instead\n" " where ~ represents the user's home directory.\n" " -d, --debug filter out log messages with priorty below level.\n" @@ -212,6 +228,11 @@ static void help() "\n" "* marks defaults.\n" "\n" +"The argument for --address is of the form: [][:]. The\n" +"hostname must be the address or hostname of an interface on the system.\n" +"The default is to listen on all interfaces. The port overrides the\n" +"default port, %d.\n" +"\n" "If no configuration file pathname is provided then the first of the\n" "following to load sets the configuration:\n" " %s\n" @@ -222,6 +243,7 @@ static void help() "Where log messages go depends on the platform and whether or not the\n" "server is running as a "DAEMON".", pname, + kDefaultPort, platform.addPathComponent( platform.getUserDirectory(), CONFIG_NAME).c_str(), @@ -265,6 +287,32 @@ static void parse(int argc, const char** argv) s_logFilter = argv[++i]; } + else if (isArg(i, argc, argv, "-a", "--address", 1)) { + // save listen address + try { + s_synergyAddress = CNetworkAddress(argv[i + 1], kDefaultPort); + } + catch (XSocketAddress&) { + log((CLOG_PRINT "%s: invalid address for `%s'" BYE, + pname, argv[i], pname)); + bye(2); + } + ++i; + } + + else if (isArg(i, argc, argv, NULL, "--http", 1)) { + // save listen address + try { + s_httpAddress = CNetworkAddress(argv[i + 1], kDefaultPort + 1); + } + catch (XSocketAddress&) { + log((CLOG_PRINT "%s: invalid address for `%s'" BYE, + pname, argv[i], pname)); + bye(2); + } + ++i; + } + else if (isArg(i, argc, argv, "-c", "--config", 1)) { // save configuration file path s_configFile = argv[++i]; @@ -527,6 +575,9 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // get program name pname = platform.getBasename(__argv[0]); + // initialize network library + CNetwork::init(); + // parse command line without reporting errors but recording if // the app would've exited. this is too avoid showing a dialog // box if we're being started as a service because we shouldn't @@ -649,6 +700,9 @@ int main(int argc, char** argv) // get program name pname = platform.getBasename(argv[0]); + // initialize network library + CNetwork::init(); + // parse command line parse(argc, const_cast(argv)); diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index 32cd4e0b..b60fc831 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -7,6 +7,9 @@ static const SInt16 kProtocolMajorVersion = 0; static const SInt16 kProtocolMinorVersion = 1; +// contact port number +static const UInt16 kDefaultPort = 24800; + // // message codes (trailing NUL is not part of code). in comments, $n // refers to the n'th argument (counting from one). message codes are From a9910041b32bcd3b1a4e0cb0e06576af2f227a69 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 9 Jun 2002 16:53:57 +0000 Subject: [PATCH 146/807] now exits instead of restarting if child dies due to an unexpected signal. --- platform/CUnixPlatform.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index 63fb77d2..99349ea7 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -102,11 +102,25 @@ int CUnixPlatform::restart( // what happened? if the child exited normally with a // status less than 16 then the child was deliberately - // terminated so we also terminate. otherwise, we - // loop. + // terminated so we also terminate. if (WIFEXITED(status) && WEXITSTATUS(status) < minErrorCode) { return WEXITSTATUS(status); } + + // did child die horribly? + if (WIFSIGNALED(status)) { + switch (WTERMSIG(status)) { + case SIGHUP: + case SIGINT: + case SIGQUIT: + case SIGTERM: + break; + + default: + // uh oh. bail out. + return 16; + } + } break; } From 255e46017e48f12ee9b0adc8acf2dcff8214ad8b Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 9 Jun 2002 17:21:33 +0000 Subject: [PATCH 147/807] fixed problem with setConfig(). if the new config didn't include a screen that was already connected under an alias then that screen wouldn't be disconnected and removed from the screen list until the screen voluntarily disconnected. at that time removeConnection() would assert because the screen name would not be found. now using the canonical name in the protocol object as well as CServer. this allows setConfig() to always detect removed screens and disconnect them. --- server/CServer.cpp | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/server/CServer.cpp b/server/CServer.cpp index 475bf5ef..31edb4b0 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -284,8 +284,7 @@ void CServer::setInfoNoLock(const CString& screen, assert(zoneSize >= 0); // screen must be connected - CString screenName = m_config.getCanonicalName(screen); - CScreenList::iterator index = m_screens.find(screenName); + CScreenList::iterator index = m_screens.find(screen); if (index == m_screens.end()) { throw XBadClient(); } @@ -344,15 +343,14 @@ void CServer::grabClipboardNoLock( CClipboardInfo& clipboard = m_clipboards[id]; // screen must be connected - CString screenName = m_config.getCanonicalName(screen); - CScreenList::iterator index = m_screens.find(screenName); + CScreenList::iterator index = m_screens.find(screen); if (index == m_screens.end()) { throw XBadClient(); } // ignore grab if sequence number is old. always allow primary // screen to grab. - if (screenName != m_primaryInfo->m_name && + if (screen != m_primaryInfo->m_name && seqNum < clipboard.m_clipboardSeqNum) { log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", screen.c_str(), id)); return; @@ -360,7 +358,7 @@ void CServer::grabClipboardNoLock( // mark screen as owning clipboard log((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str())); - clipboard.m_clipboardOwner = screenName; + clipboard.m_clipboardOwner = screen; clipboard.m_clipboardSeqNum = seqNum; // no screens have the new clipboard except the sender @@ -369,7 +367,7 @@ void CServer::grabClipboardNoLock( // tell all other screens to take ownership of clipboard for (index = m_screens.begin(); index != m_screens.end(); ++index) { - if (index->first != screenName) { + if (index->first != screen) { CScreenInfo* info = index->second; if (info->m_protocol == NULL) { m_primary->grabClipboard(id); @@ -1094,6 +1092,11 @@ void CServer::handshakeClient(void* vsocket) throw XBadClient(); } + // convert name to canonical form (if any) + if (m_config.isScreen(name)) { + name = m_config.getCanonicalName(name); + } + // create a protocol interpreter for the version log((CLOG_DEBUG1 "creating interpreter for client \"%s\" version %d.%d", name.c_str(), major, minor)); assign(protocol, CServerProtocol::create(major, minor, @@ -1334,10 +1337,11 @@ void CServer::openPrimaryScreen() // reset sequence number m_seqNum = 0; + CString primary = m_config.getCanonicalName("primary"); // FIXME try { // add connection - m_active = addConnection(CString("primary"/* FIXME */), NULL); - m_primaryInfo = m_active; + m_active = addConnection(primary, NULL); + m_primaryInfo = m_active; // open screen log((CLOG_DEBUG1 "creating primary screen")); @@ -1351,7 +1355,7 @@ void CServer::openPrimaryScreen() } catch (...) { if (m_primary != NULL) { - removeConnection(CString("primary"/* FIXME */)); + removeConnection(primary); delete m_primary; } m_primary = NULL; @@ -1376,7 +1380,8 @@ void CServer::closePrimaryScreen() assert(m_primary != NULL); // remove connection - removeConnection(CString("primary"/* FIXME */)); + CString primary = m_config.getCanonicalName("primary"); // FIXME + removeConnection(primary); // close the primary screen try { @@ -1468,17 +1473,16 @@ CServer::CScreenInfo* CServer::addConnection( if (!m_config.isScreen(name)) { throw XUnknownClient(name); } - CString screenName = m_config.getCanonicalName(name); // can only have one screen with a given name at any given time - if (m_screens.count(screenName) != 0) { + if (m_screens.count(name) != 0) { throw XDuplicateClient(name); } // save screen info - CScreenInfo* newScreen = new CScreenInfo(screenName, protocol); - m_screens.insert(std::make_pair(screenName, newScreen)); - log((CLOG_DEBUG "added connection \"%s\" (\"%s\")", name.c_str(), screenName.c_str())); + CScreenInfo* newScreen = new CScreenInfo(name, protocol); + m_screens.insert(std::make_pair(name, newScreen)); + log((CLOG_DEBUG "added connection \"%s\"", name.c_str())); return newScreen; } @@ -1489,8 +1493,7 @@ void CServer::removeConnection(const CString& name) CLock lock(&m_mutex); // find screen info - CString screenName = m_config.getCanonicalName(name); - CScreenList::iterator index = m_screens.find(screenName); + CScreenList::iterator index = m_screens.find(name); assert(index != m_screens.end()); // if this is active screen then we have to jump off of it From c35718053005603bf63aeb6492a95d9f6abfffab Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 9 Jun 2002 17:35:28 +0000 Subject: [PATCH 148/807] added FIXME comment. --- mt/CThreadRep.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 6e54080f..aa641b50 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -179,6 +179,9 @@ void CThreadRep::initThreads() pthread_sigmask(SIG_BLOCK, &sigset, NULL); // fire up the INT and TERM signal handler thread + // FIXME -- i've seen this thread hanging around after the app + // asserted. should figure out how it stays alive and prevent + // it from happening. int status = pthread_create(&s_signalThread, NULL, &CThreadRep::threadSignalHandler, getCurrentThreadRep()); From 7ca4804667f598487cb65d5eddd00569b2f5ce64 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 9 Jun 2002 17:59:32 +0000 Subject: [PATCH 149/807] added command line option to choose the screen name. also now using the hostname as the default name. this is on both client and server. --- client/client.cpp | 19 +++++++++++++++++-- server/CServer.cpp | 20 +++++++++++++++----- server/CServer.h | 4 +++- server/server.cpp | 25 ++++++++++++++++++++----- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/client/client.cpp b/client/client.cpp index b34010a0..f7a12d5a 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -32,6 +32,7 @@ static bool s_daemon = true; static bool s_install = false; static bool s_uninstall = false; static const char* s_logFilter = NULL; +static CString s_name; static CNetworkAddress s_serverAddress; @@ -74,7 +75,7 @@ static int realMain(CMutex* mutex) bool locked = true; try { // create client - s_client = new CClient("secondary"); // FIXME + s_client = new CClient(s_name); // run client if (mutex != NULL) { @@ -164,8 +165,9 @@ static void help() { log((CLOG_PRINT "Usage: %s" -" [--debug ]" " [--"DAEMON"|--no-"DAEMON"]" +" [--debug ]" +" [--name ]" " [--restart|--no-restart]" " [--install]" " \n" @@ -178,6 +180,8 @@ static void help() " DEBUG, DEBUG1, DEBUG2.\n" " -f, --no-"DAEMON" run the client in the foreground.\n" "* --"DAEMON" run the client as a "DAEMON".\n" +" -n, --name use screen-name instead the hostname to identify\n" +" ourself to the server.\n" " -1, --no-restart do not try to restart the client if it fails for\n" " some reason.\n" "* --restart restart the client automatically if it fails.\n" @@ -225,6 +229,12 @@ static void parse(int argc, const char** argv) assert(argv != NULL); assert(argc >= 1); + // set defaults + char hostname[256]; + if (CNetwork::gethostname(hostname, sizeof(hostname)) != CNetwork::Error) { + s_name = hostname; + } + // parse options int i; for (i = 1; i < argc; ++i) { @@ -233,6 +243,11 @@ static void parse(int argc, const char** argv) s_logFilter = argv[++i]; } + else if (isArg(i, argc, argv, "-n", "--name", 1)) { + // save screen name + s_name = argv[++i]; + } + else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) { // not a daemon s_daemon = false; diff --git a/server/CServer.cpp b/server/CServer.cpp index 31edb4b0..bd09ce74 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -47,7 +47,9 @@ else { wait(0); exit(1); } const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; -CServer::CServer() : m_cleanupSize(&m_mutex, 0), +CServer::CServer(const CString& serverName) : + m_name(serverName), + m_cleanupSize(&m_mutex, 0), m_primary(NULL), m_active(NULL), m_primaryInfo(NULL), @@ -81,6 +83,12 @@ void CServer::run() log((CLOG_INFO "failed to open screen. waiting to retry.")); CThread::sleep(3.0); } + catch (XUnknownClient& e) { + // can't open screen yet. wait a few seconds to retry. + log((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); + log((CLOG_NOTE "stopping server")); + return; + } } // start listening for new clients @@ -222,8 +230,7 @@ bool CServer::setConfig(const CConfig& config) CString CServer::getPrimaryScreenName() const { - CLock lock(&m_mutex); - return (m_primaryInfo == NULL) ? "" : m_primaryInfo->m_name; + return m_name; } void CServer::getConfig(CConfig* config) const @@ -1337,7 +1344,10 @@ void CServer::openPrimaryScreen() // reset sequence number m_seqNum = 0; - CString primary = m_config.getCanonicalName("primary"); // FIXME + CString primary = m_config.getCanonicalName(m_name); + if (primary.empty()) { + throw XUnknownClient(m_name); + } try { // add connection m_active = addConnection(primary, NULL); @@ -1380,7 +1390,7 @@ void CServer::closePrimaryScreen() assert(m_primary != NULL); // remove connection - CString primary = m_config.getCanonicalName("primary"); // FIXME + CString primary = m_config.getCanonicalName(m_name); removeConnection(primary); // close the primary screen diff --git a/server/CServer.h b/server/CServer.h index ab4c92a2..568d2c1d 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -24,7 +24,7 @@ class CHTTPServer; class CServer { public: - CServer(); + CServer(const CString& serverName); ~CServer(); // manipulators @@ -220,6 +220,8 @@ private: CMutex m_mutex; + CString m_name; + double m_bindTimeout; ISocketFactory* m_socketFactory; diff --git a/server/server.cpp b/server/server.cpp index c0c553d5..38030244 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -39,6 +39,7 @@ static bool s_install = false; static bool s_uninstall = false; static const char* s_configFile = NULL; static const char* s_logFilter = NULL; +static CString s_name; static CNetworkAddress s_synergyAddress; static CNetworkAddress s_httpAddress; static CConfig s_config; @@ -69,7 +70,6 @@ static void logLock(bool lock) static CServer* s_server = NULL; -#include static int realMain(CMutex* mutex) { // s_serverLock should have mutex locked on entry @@ -88,7 +88,7 @@ static int realMain(CMutex* mutex) // if configuration has no screens then add this system // as the default if (s_config.begin() == s_config.end()) { - s_config.addScreen("primary"); + s_config.addScreen(s_name); } // set the contact address, if provided, in the config. @@ -107,7 +107,7 @@ static int realMain(CMutex* mutex) } // create server - s_server = new CServer(); + s_server = new CServer(s_name); // run server (unlocked) if (mutex != NULL) { @@ -200,12 +200,14 @@ static void help() log((CLOG_PRINT "Usage: %s" +" [--address
]" " [--config ]" -" [--debug ]" " [--"DAEMON"|--no-"DAEMON"]" +" [--debug ]" +" [--name ]" " [--restart|--no-restart]\n" +" [--install]\n" "or\n" -" --install\n" " --uninstall\n" "\n" "Start the synergy mouse/keyboard sharing server.\n" @@ -218,6 +220,8 @@ static void help() " DEBUG, DEBUG1, DEBUG2.\n" " -f, --no-"DAEMON" run the server in the foreground.\n" "* --"DAEMON" run the server as a "DAEMON".\n" +" -n, --name use screen-name instead the hostname to identify\n" +" this screen in the configuration.\n" " -1, --no-restart do not try to restart the server if it fails for\n" " some reason.\n" "* --restart restart the server automatically if it fails.\n" @@ -279,6 +283,12 @@ static void parse(int argc, const char** argv) assert(argv != NULL); assert(argc >= 1); + // set defaults + char hostname[256]; + if (CNetwork::gethostname(hostname, sizeof(hostname)) != CNetwork::Error) { + s_name = hostname; + } + // parse options int i; for (i = 1; i < argc; ++i) { @@ -313,6 +323,11 @@ static void parse(int argc, const char** argv) ++i; } + else if (isArg(i, argc, argv, "-n", "--name", 1)) { + // save screen name + s_name = argv[++i]; + } + else if (isArg(i, argc, argv, "-c", "--config", 1)) { // save configuration file path s_configFile = argv[++i]; From e94be94a50f414355e81b12787067417939ca591 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 9 Jun 2002 18:00:03 +0000 Subject: [PATCH 150/807] checkpoint. --- notes | 55 ++++++++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/notes b/notes index 7ebbcc48..75a2c492 100644 --- a/notes +++ b/notes @@ -10,20 +10,29 @@ query info --> --- use automake +order includes according to source directory +fix function definitions + return type on first line + function name in 1st column on 2nd line + parameters at 1st tab stop starting 3rd line, one per line +fix parameter indentation + in header file: 7th tab stop (not 8th) + in source file: 1st tab stop (not 8th) + --- HTTP stuff no way to save config using HTTP should have a button or a page to force save of config or just save config every time it's changed should use authentication (at least basic) + provide way to kill/restart server via HTTP + provide way to query why locked to screen? + handy for debugging at least --- -use hostname instead of "primary", "secondary" - ---- -add aliases in config file - any alias of a screen name counts as that screen name - handy for mapping FQDN <-> hostname +CThreadRep::threadSignalHandler not shutdown on assert + thread stays around and holds our resources (like sockets) + should ensure that it terminates correctly --- win32: @@ -41,9 +50,6 @@ desktop switcher program failure when running synergy as service returns access denied from CreateDesktop() don't know why -log window may prevent changing desktop - should start console in a thread just for that purpose - client will need to be able to retry connecting to server in case server isn't ready yet, client can camp @@ -54,6 +60,14 @@ bug with half-duplex keys now grace starts server and is locked to screen grace should ignore half duplex keys when seeing if locked to screen +--- +must send up key events when leaving screen for keys still down + some key combos get eaten and the key ups aren't sent + e.g. ctrl+alt+del on win2k to grace + mouse is forced back to to win2k + ctrl and alt up not sent to grace + grace thinks ctrl and alt are down 'til user physically press/releases them + --- CClient and CServer: not checking in stop() that we're actually running @@ -106,11 +120,7 @@ unix: synergy cannot switch unless it can grab keyboard gdm doesn't appear to grab keyboard or mouse for duration affects server, only - - get auto-restart into platform code - provide a `int(*)(void)' ptr to an auto-restart function - function just forks(), calls ptr, waits, repeats - may need something else on win32 or maybe just skip it + check if DISPLAY env var is unset. use ":0.0" as default. --- bug in updating screens @@ -244,15 +254,6 @@ test: check accessibility shortcuts work on secondary check accessibility features work on secondary ---- -audrey->isabel - num-lock light on isabel doesn't respond to generated events. - seems to be linked to something lower-level. however, keytest - does report num-lock keyboard state is being toggled. - - other toggle lights don't always respond immediately - usually go on/off when active window changes though - --- avoid fullscreen transparent window on win32 using CBT hook to discard activation/focus messages @@ -296,14 +297,6 @@ adjust thread priorities on win32 a very high priority on client fixes delay when typeing into console is it the output console causing the slowness? ---- -must send up key events when leaving screen for keys still down - some key combos get eaten and the key ups aren't sent - e.g. ctrl+alt+del on win2k to grace - mouse is forced back to to win2k - ctrl and alt up not sent to grace - grace thinks ctrl and alt are down 'til user physically press/releases them - --- Accessibility Shortcuts From db8165db09c00da1abed9907cef691c60a2d78de Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 9 Jun 2002 18:03:32 +0000 Subject: [PATCH 151/807] now using ":0.0" as the display if DISPLAY isn't set. --- platform/CXWindowsScreen.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index d2817c95..adbc5193 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -42,9 +42,15 @@ void CXWindowsScreen::openDisplay() // set the X I/O error handler so we catch the display disconnecting XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); + // get the DISPLAY + const char* display = getenv("DISPLAY"); + if (display == NULL) { + display = ":0.0"; + } + // open the display - log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); - m_display = XOpenDisplay(NULL); // FIXME -- allow non-default + log((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); + m_display = XOpenDisplay(display); if (m_display == NULL) throw XScreenOpenFailure(); From 30a6a8b83750345fdea7ef7b3407ac1018e91f0c Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 9 Jun 2002 22:20:01 +0000 Subject: [PATCH 152/807] CTimerThread now allows zero and negative timeouts. a negative timeout never times out and CTimerThread is a no-op. --- mt/CTimerThread.cpp | 25 ++++++++++++++++--------- mt/CTimerThread.h | 3 +++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/mt/CTimerThread.cpp b/mt/CTimerThread.cpp index 0a3e8542..786ed8df 100644 --- a/mt/CTimerThread.cpp +++ b/mt/CTimerThread.cpp @@ -10,20 +10,27 @@ CTimerThread::CTimerThread(double timeout) : m_timeout(timeout) { - assert(m_timeout > 0.0); - m_callingThread = new CThread(CThread::getCurrentThread()); - m_timingThread = new CThread(new TMethodJob( + if (m_timeout >= 0.0) { + m_callingThread = new CThread(CThread::getCurrentThread()); + m_timingThread = new CThread(new TMethodJob( this, &CTimerThread::timer)); + } + else { + m_callingThread = NULL; + m_timingThread = NULL; + } } CTimerThread::~CTimerThread() { - log((CLOG_DEBUG1 "cancelling timeout")); - m_timingThread->cancel(); - m_timingThread->wait(); - log((CLOG_DEBUG1 "cancelled timeout")); - delete m_timingThread; - delete m_callingThread; + if (m_timingThread != NULL) { + log((CLOG_DEBUG1 "cancelling timeout")); + m_timingThread->cancel(); + m_timingThread->wait(); + log((CLOG_DEBUG1 "cancelled timeout")); + delete m_timingThread; + delete m_callingThread; + } } void CTimerThread::timer(void*) diff --git a/mt/CTimerThread.h b/mt/CTimerThread.h index 817642d3..118fde1d 100644 --- a/mt/CTimerThread.h +++ b/mt/CTimerThread.h @@ -7,6 +7,9 @@ class CThread; class CTimerThread { public: + // cancels the calling thread after timeout seconds unless destroyed + // before then. if timeout is less than zero then it never times + // out and is a no-op. CTimerThread(double timeout); ~CTimerThread(); From 28afcddf62ae01cc69cdc84888712d5ea724ee1c Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 9 Jun 2002 22:20:28 +0000 Subject: [PATCH 153/807] added support for camping, i.e. repeatly trying to connect to the server until we succeed. --- client/CClient.cpp | 57 ++++++++++++++++++++++++++++++++++++---------- client/CClient.h | 12 +++++++++- client/client.cpp | 25 ++++++++++++++++++-- 3 files changed, 79 insertions(+), 15 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 61ed3f54..af8855f9 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -33,6 +33,7 @@ CClient::CClient(const CString& clientName) : m_input(NULL), m_output(NULL), m_screen(NULL), + m_camp(false), m_active(false), m_seqNum(0), m_ignoreMove(false) @@ -45,7 +46,12 @@ CClient::~CClient() // do nothing } -void CClient::run(const CNetworkAddress& serverAddress) +void CClient::camp(bool on) +{ + m_camp = on; +} + +bool CClient::run(const CNetworkAddress& serverAddress) { CThread* thread = NULL; try { @@ -74,9 +80,10 @@ void CClient::run(const CNetworkAddress& serverAddress) // clean up log((CLOG_NOTE "stopping client")); thread->cancel(); - thread->wait(); + void* result = thread->getResult(); delete thread; closeSecondaryScreen(); + return (result != NULL); } catch (XBase& e) { log((CLOG_ERR "client error: %s", e.what())); @@ -89,6 +96,7 @@ void CClient::run(const CNetworkAddress& serverAddress) delete thread; } closeSecondaryScreen(); + return true; } catch (XThread&) { // clean up @@ -183,14 +191,29 @@ void CClient::runSession(void*) std::auto_ptr input; std::auto_ptr output; try { - // allow connect this much time to succeed - CTimerThread timer(30.0); // FIXME -- timeout in member + for (;;) { + try { + // allow connect this much time to succeed + // FIXME -- timeout in member + CTimerThread timer(m_camp ? -1.0 : 30.0); - // create socket and attempt to connect to server - log((CLOG_DEBUG1 "connecting to server")); - assign(socket, new CTCPSocket(), ISocket); // FIXME -- use factory - socket->connect(*m_serverAddress); - log((CLOG_INFO "connected to server")); + // create socket and attempt to connect to server + log((CLOG_DEBUG1 "connecting to server")); + assign(socket, new CTCPSocket(), ISocket); // FIXME -- use factory + socket->connect(*m_serverAddress); + log((CLOG_INFO "connected to server")); + break; + } + catch (XSocketConnect&) { + // failed to connect. if not camping then rethrow. + if (!m_camp) { + throw; + } + + // we're camping. wait a bit before retrying + CThread::sleep(5.0); + } + } // get the input and output streams IInputStream* srcInput = socket->getInputStream(); @@ -208,6 +231,9 @@ void CClient::runSession(void*) } */ + // give handshake some time + CTimerThread timer(30.0); + // attach the packetizing filters assign(input, new CInputPacketStream(srcInput, own), IInputStream); assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); @@ -238,7 +264,7 @@ void CClient::runSession(void*) catch (XIncompatibleClient& e) { log((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); m_screen->stop(); - return; + CThread::exit(NULL); } catch (XThread&) { log((CLOG_ERR "connection timed out")); @@ -248,7 +274,12 @@ void CClient::runSession(void*) catch (XBase& e) { log((CLOG_ERR "connection failed: %s", e.what())); m_screen->stop(); - return; + CThread::exit(NULL); + } + catch (...) { + log((CLOG_ERR "connection failed: ")); + m_screen->stop(); + CThread::exit(NULL); } try { @@ -346,7 +377,7 @@ void CClient::runSession(void*) catch (XBase& e) { log((CLOG_ERR "error: %s", e.what())); m_screen->stop(); - return; + CThread::exit(reinterpret_cast(1)); } // done with socket @@ -355,6 +386,8 @@ void CClient::runSession(void*) // exit event loop m_screen->stop(); + + CThread::exit(reinterpret_cast(1)); } // FIXME -- use factory to create screen diff --git a/client/CClient.h b/client/CClient.h index 30bcee50..992d51c9 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -19,8 +19,17 @@ public: // manipulators + // turn camping on or off. when camping the client will keep + // trying to connect to the server until it succeeds. this + // is useful if the client may start before the server. do + // not call this while in run(). + void camp(bool on); + // start the client. does not return until quit() is called. - void run(const CNetworkAddress& serverAddress); + // returns true if the client ever connected to the server + // successfully. may also throw exceptions after successfully + // connecting. + bool run(const CNetworkAddress& serverAddress); // tell client to exit gracefully void quit(); @@ -67,6 +76,7 @@ private: IOutputStream* m_output; ISecondaryScreen* m_screen; const CNetworkAddress* m_serverAddress; + bool m_camp; bool m_active; UInt32 m_seqNum; bool m_ignoreMove; diff --git a/client/client.cpp b/client/client.cpp index f7a12d5a..6f9ed543 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -29,6 +29,7 @@ static const char* pname = NULL; static bool s_restartable = true; static bool s_daemon = true; +static bool s_camp = true; static bool s_install = false; static bool s_uninstall = false; static const char* s_logFilter = NULL; @@ -76,13 +77,14 @@ static int realMain(CMutex* mutex) try { // create client s_client = new CClient(s_name); + s_client->camp(s_camp); // run client if (mutex != NULL) { mutex->unlock(); } locked = false; - s_client->run(s_serverAddress); + bool success = s_client->run(s_serverAddress); locked = true; if (mutex != NULL) { mutex->lock(); @@ -94,6 +96,8 @@ static int realMain(CMutex* mutex) CNetwork::cleanup(); CLog::setLock(NULL); s_logMutex = NULL; + + return success ? 16 : 0; } catch (...) { // clean up @@ -166,6 +170,7 @@ static void help() log((CLOG_PRINT "Usage: %s" " [--"DAEMON"|--no-"DAEMON"]" +" [--camp|--no-camp]" " [--debug ]" " [--name ]" " [--restart|--no-restart]" @@ -175,6 +180,9 @@ static void help() " --uninstall\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" +"* --camp keep attempting to connect to the server until\n" +" successful.\n" +" --no-camp do not camp.\n" " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" @@ -184,7 +192,10 @@ static void help() " ourself to the server.\n" " -1, --no-restart do not try to restart the client if it fails for\n" " some reason.\n" -"* --restart restart the client automatically if it fails.\n" +"* --restart restart the client automatically if it fails for\n" +" some unexpected reason, including the server\n" +" disconnecting but not including failing to\n" +" connect to the server." " --install install server as a "DAEMON".\n" " --uninstall uninstall server "DAEMON".\n" " -h, --help display this help and exit.\n" @@ -248,6 +259,16 @@ static void parse(int argc, const char** argv) s_name = argv[++i]; } + else if (isArg(i, argc, argv, NULL, "--camp")) { + // enable camping + s_camp = true; + } + + else if (isArg(i, argc, argv, NULL, "--no-camp")) { + // disable camping + s_camp = false; + } + else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) { // not a daemon s_daemon = false; From 0a2f8547fc7620ada30a63ad84106984aa877288 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 9 Jun 2002 23:08:18 +0000 Subject: [PATCH 154/807] no longer camps if the server sends an error message. --- client/CClient.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index af8855f9..3d0e4d89 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -282,6 +282,7 @@ void CClient::runSession(void*) CThread::exit(NULL); } + bool fail = false; try { // handle messages from server for (;;) { @@ -353,18 +354,22 @@ void CClient::runSession(void*) } else if (memcmp(code, kMsgEIncompatible, 4) == 0) { onErrorIncompatible(); + fail = true; break; } else if (memcmp(code, kMsgEBusy, 4) == 0) { onErrorBusy(); + fail = true; break; } else if (memcmp(code, kMsgEUnknown, 4) == 0) { onErrorUnknown(); + fail = true; break; } else if (memcmp(code, kMsgEBad, 4) == 0) { onErrorBad(); + fail = true; break; } else { @@ -387,7 +392,7 @@ void CClient::runSession(void*) // exit event loop m_screen->stop(); - CThread::exit(reinterpret_cast(1)); + CThread::exit(fail ? NULL : reinterpret_cast(1)); } // FIXME -- use factory to create screen From 2e931a4fd9c938c1510b9e60f9f385e80759364f Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 10 Jun 2002 09:49:03 +0000 Subject: [PATCH 155/807] changed "permitted" to "supported" in error messages. --- client/client.cpp | 4 ++-- server/server.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/client.cpp b/client/client.cpp index 6f9ed543..c2797ae1 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -301,7 +301,7 @@ static void parse(int argc, const char** argv) else if (isArg(i, argc, argv, NULL, "--install")) { #if !defined(CONFIG_PLATFORM_WIN32) - log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE, + log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE, pname, argv[i], pname)); bye(2); #endif @@ -316,7 +316,7 @@ static void parse(int argc, const char** argv) else if (isArg(i, argc, argv, NULL, "--uninstall")) { #if !defined(CONFIG_PLATFORM_WIN32) - log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE, + log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE, pname, argv[i], pname)); bye(2); #endif diff --git a/server/server.cpp b/server/server.cpp index 38030244..0b671e0a 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -365,7 +365,7 @@ static void parse(int argc, const char** argv) else if (isArg(i, argc, argv, NULL, "--install")) { #if !defined(CONFIG_PLATFORM_WIN32) - log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE, + log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE, pname, argv[i], pname)); bye(2); #endif @@ -380,7 +380,7 @@ static void parse(int argc, const char** argv) else if (isArg(i, argc, argv, NULL, "--uninstall")) { #if !defined(CONFIG_PLATFORM_WIN32) - log((CLOG_PRINT "%s: `%s' not permitted on this platform" BYE, + log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE, pname, argv[i], pname)); bye(2); #endif From 2ea3d66112108982688a79709fd52eda4b0b5502 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 10 Jun 2002 09:49:21 +0000 Subject: [PATCH 156/807] fixed stripping of comments from configuration streams. --- server/CConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CConfig.cpp b/server/CConfig.cpp index 4d82366d..167db361 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -277,7 +277,7 @@ bool CConfig::readLine(std::istream& s, CString& line) s >> std::ws; while (std::getline(s, line)) { // strip comments and then trailing whitespace - CString::size_type i = line.rfind('#'); + CString::size_type i = line.find('#'); if (i != CString::npos) { line.erase(i); } From 172fc7e5b36a47120feb0a65eed9282ae2581e46 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 10 Jun 2002 10:08:36 +0000 Subject: [PATCH 157/807] now allowing a screen to be its own neighbor to allow wrapping. also no longer warping mouse to 0,0 when setting server screen info. that was causing the mouse to jump if the server screen had itself as its left or top neighbor (directly or indirectly) once a screen could be its own neighbor. --- server/CMSWindowsPrimaryScreen.cpp | 4 +++- server/CServer.cpp | 2 ++ server/CXWindowsPrimaryScreen.cpp | 22 ++++++++++++++++++---- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index cc83079a..b85c6909 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -111,7 +111,9 @@ void CMSWindowsPrimaryScreen::open(CServer* server) // send screen info SInt32 w, h; getScreenSize(&w, &h); - m_server->setInfo(w, h, getJumpZoneSize(), 0, 0); + POINT pos; + GetCursorPos(&pos); + m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y); // compute center pixel of screen m_xCenter = w >> 1; diff --git a/server/CServer.cpp b/server/CServer.cpp index bd09ce74..dfaa86da 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -894,9 +894,11 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, } assert(lastGoodScreen != NULL); +/* allow screen to be it's own neighbor to allow wrapping // no neighbor if best neighbor is the source itself if (lastGoodScreen == src) return NULL; +*/ // if entering primary screen then be sure to move in far enough // to avoid the jump zone. if entering a side that doesn't have diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 9012e089..52297109 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -202,16 +202,30 @@ void CXWindowsPrimaryScreen::open(CServer* server) // m_numLockHalfDuplex = true; // m_capsLockHalfDuplex = true; - // update key state + // get screen size + SInt32 w, h; + getScreenSize(&w, &h); + + int x, y; { CDisplayLock display(this); + + // update key state updateModifierMap(display); + + // get mouse position + Window root, window; + int xWindow, yWindow; + unsigned int mask; + if (!XQueryPointer(display, m_window, &root, &window, + &x, &y, &xWindow, &yWindow, &mask)) { + x = w >> 1; + y = h >> 1; + } } // send screen info - SInt32 w, h; - getScreenSize(&w, &h); - m_server->setInfo(w, h, getJumpZoneSize(), 0, 0); + m_server->setInfo(w, h, getJumpZoneSize(), x, y); } void CXWindowsPrimaryScreen::close() From afc8d6a9e7c841545aa61bc5fd93d497da91af63 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 10 Jun 2002 11:00:55 +0000 Subject: [PATCH 158/807] added example files and a README. --- README | 426 +++++++++++++++++++++++++++++++++++ examples/synergy.conf | 37 +++ examples/synergy.linux.init | 43 ++++ examples/synergyd.linux.init | 43 ++++ 4 files changed, 549 insertions(+) create mode 100644 README create mode 100644 examples/synergy.conf create mode 100755 examples/synergy.linux.init create mode 100755 examples/synergyd.linux.init diff --git a/README b/README new file mode 100644 index 00000000..606d1e7f --- /dev/null +++ b/README @@ -0,0 +1,426 @@ +synergy +------- +synergy: [noun] a mutually advantageous conjunction of distinct elements + +synergy lets you easily share a single mouse and keyboard between +multiple computers through software. it also merges each system's +clipboards into one, allowing cut-and-paste between systems. +redirecting the mouse and keyboard is as simple as moving the +mouse off the edge of your screen. + + +system requirements +------------------- +all systems: + keyboard + mouse + TCP/IP networking + +Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family): + ??? MB RAM + +Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family): + ??? MB RAM + +Linux, Unix: + ??? MB RAM + X Windows, revision 4 or up with the XTEST extension + use `xdpyinfo | grep XTEST' to check + + +manifest +-------- + linux windows + ----- ------- + synergy synergy.exe the synergy client + synergyd synergyd.exe the synergy server + synergy.conf synergy.sgc sample configuration file + README README this file + + +running synergy +--------------- +synergy is simple to configure. the server uses a configuration file +and command line options while the client uses only command line +options. it's recommended that both the client and server be run in +the foreground until the configuration is verified to work. + +step 1: create a configuration file + edit the sample configuration file. there are two sections you + must fill in and a third optional section. you should delete + the existing lines inside the sections. + + in the "screens" section, add a line for each computer you'll + be using (server and clients). put the hostname of the computer + followed by a colon (with no space in between). the computers + can be listed in any order. + + in the "links" section you define how screens are connected. + each screen is listed as in the "screens" section except + following each screen is a list of links to other screens in + the form " = " where is "left", + "right", "up", or "down" and is a screen listed in + the "screens" section. + + as an example, if we have "left=foo" under the "bar" screen + then screen "foo" is on the left of screen "bar". the user + will be able to move the mouse off the left edge of "foo" and + will appear on the opposite (right) edge of "bar". note that + it's entirely possible to have one-way (asymmetric) links and + screens with only links into them. the latter should be + avoided since there's no way to move the mouse off those + screens. + + in the "aliases" section you can list other names for each + screen. this is especially useful for dealing with fully + qualified domain names versus simple hostnames. + +step 2: start the server + the server is the system with the mouse and keyboard to be + shared. each platform has its own tradeoffs when running as + the server. see the release notes below for more information. + + run the synergy server on the server system using the following + command line: + + synergyd -f --config + + replacing with the path to the configuration + file. you can use `synergyd --help' for a list of command line + options. + +step 3: start the clients + on each client system start the synergy client using the + following command line: + + synergy -f --debug INFO --no-camp + + replacing with the hostname or address of the + server system. + + the client should quickly report `connected to server'. if it + does not but doesn't print an error and exit immeditately then + it's trying to connect to the server but cannot. it will time + out in 30 seconds and exit (use ctrl+c to exit earlier). you + should check that the server is running and try again. + + otherwise, if the client doesn't connect it should print an + error describing the problem. here are typical problems and + possible solutions: + + failed to open screen: + check permission to open the X display; + check that the DISPLAY environment variable is set. + already connected: + check that synergy isn't already running. + refused client: + add client to the server's configuration file. + connection failed: + check server-hostname; + the server cannot open the desired port, stop the + program using that port (24800) and restart the + server. + +step 4: verify the configuration + once the clients are connected, use the mouse to check that + the screens are properly linked. moving the mouse off the + edge of a screen with a link should cause it to appear on + the opposite edge of the linked-to screen. + + +using synergy +------------- +using synergy is very easy. once clients have connected to the +server all you do to redirect keyboard and mouse input to a screen +(i.e. switch screens) is move the mouse cursor off the edge of the +screen you're on. which edges go to which screens depends on the +configuration. + +clients can be connected and disconnected at any time. until a +client is connected, switching to it works as if you switched to +it then moved all the way across it in the same direction and +switched to the next screen. this repeats until you reach a +connected screen. if there is no connected screen in that +direction then the mouse will not leave the starting screen. + +disconnecting a client while the mouse is on it causes the mouse +to instantly jump to the center of the server screen. note that +there's a bug in this situation where keys may be left in the +logical pressed state; see the "known bugs" section. + +the clipboard is automatically transferred between screens. if +you copy on one screen you just switch to another screen and paste. +note that X Windows has two major clipboards: the primary +selection and the clipboard. synergy supports both. however, +Microsoft Windows only supports the clipboard. the Windows +clipboard is transferred to both the X primary selection and the +clipboard. whichever X clipboard was changed last becomes the +Windows clipboard. end-of-line sequences (LF on linux and unix, +CRLF on Windows) are automatically converted as necessary. + + +installing as a daemon/service +------------------------------ +synergy can run in the foreground or as a daemon/service. it's +recommended that you run it in the foreground until you've sorted +out your configuration. + +in the text below, except where noted, synergy refers to the +client and/or the server. + +windows: + to install synergy just run one of the following: + + synergy --install [other command line options] + synergyd --install [other command line options] + + the client/server is installed as a service and the command + line is saved and used when starting the service. note that + when installing the client you must provide the server + hostname argument. to change the arguments you must first + uninstall then reinstall. + + to uninstall use: + + synergy --uninstall + synergyd --uninstall + +linux, unix: + before starting synergy as a daemon you should understand that + synergy requires an X server that it can connect to. synergy + can start before the X server does and will repeatly attempt to + connect to the X server until it succeeds. the DISPLAY env var + should be set appropriately before starting synergy. note that + it probably will not be set when running startup scripts so you + have to set it yourself (probably to ":0"). + + if the X server goes down then all the clients connected to it + must terminate. synergy has to deal with this because some + display managers will automatically restart the X server when + a user logs out. synergy can automatically restart itself and + it does so by default. the `--no-restart' option turns this + feature off. + + some display managers also prevent any X client other than + those they themselves start from connecting to the X server + for security reasons. for these display managers, synergy + should be part of the manager's screen initialization script + and synergy should be started with the --no-restart option + since the display manager will do the restarting. otherwise, + synergy should not use the --no-restart option and it can be + started at boot time (sometime after the network is started) + or when the X server is started. + + finally, some display managers (xdm and kdm, but not gdm) + grab the keyboard and do not release it until the user logs + in, also for security reasons. this prevents a synergy server + from sharing the mouse and keyboard until the user logs in. + + to install synergy as a daemon, you'll need to add the + appropriate lines and/or files to start synergy at boot time + or as part of a display manager screen initialization script. + do not use the `-f' or `--no-daemon' options. for the server + use the `--config' option to specify the path to the + configuration file or just put the configuration in + /etc/synergy.conf. + +linux: + if starting synergy from xdm's or kdm's screen initialization + script you'll want add a line to start synergy in + /etc/X11/xdm/Xsetup_0, probably near the bottom. you'll also + want to put the configuration file in /etc/synergy.conf and + the synergy executable in /usr/sbin. to uninstall, remove the + added line and files. + + if starting the synergy client using init.d then: + # /bin/cp synergy /usr/sbin/synergy + # /bin/cp synergy.linux.init /etc/init.d/synergy + # /sbin/chkconfig --add synergy + + if starting the synergy server using init.d then: + # /bin/cp synergyd /usr/sbin/synergyd + # /bin/cp synergyd.linux.init /etc/init.d/synergyd + # /bin/cp synergy.conf /etc/synergy.conf + # /sbin/chkconfig --add synergyd + of course, the configuration should be edited your for systems. + + to manually start or stop the client + # /etc/init.d/synergy start + # /etc/init.d/synergy stop + + to manually start or stop the server + # /etc/init.d/synergyd start + # /etc/init.d/synergyd stop + + to uninstall the client: + # /etc/init.d/synergy stop + # /sbin/chkconfig --del synergy + # /bin/rm /etc/init.d/synergy + # /bin/rm /usr/sbin/synergy + + to uninstall the server: + # /etc/init.d/synergyd stop + # /sbin/chkconfig --del synergyd + # /bin/rm /etc/synergy.conf + # /bin/rm /etc/init.d/synergyd + # /bin/rm /usr/sbin/synergyd + + +common command line options +--------------------------- + -d, --debug use debugging level + --daemon run as a daemon (linux,unix) + --no-daemon run in the foreground (linux,unix) + --service run as a service (windows) + --no-service run in the foreground (windows) + -f run in the foreground + -n, --name use instead of the hostname + --restart automatically restart on unexpected failures + -1, --no-restart do not restart on unexpected failure + -h, --help print help and exit + --version print version information and exit + --install install as a service (windows) + --uninstall uninstall service (windows) + +debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, +INFO, DEBUG, DEBUG1, and DEBUG2. only messages at or above the given +level are logged. messages are logged to a terminal window when +running in the foreground. unix logs messages to syslog when running +as a daemon. the Windows NT family logs messages to the event log +when running as a service. the Windows 95 family shows FATAL log +messages in a message box and others in a terminal window when running +as a service. + +the `--name' option lets the client or server use a name other than +its hostname for its screen. this name is used when checking the +configuration. + + +server command line options +--------------------------- + -a, --address
listen for connections on the given address + -c, --config read configuration from + +an address has one of the following forms: + + : + : + is a hostname or address of a network interface on the +server system. is a port number from 1 to 65535. + + +client command line options +--------------------------- + --camp retry connection to server until successful + --no-camp try connection to server only once +
address of server + +see the "server command line options" for a description of
. + + +release notes +------------- +synergy does not yet fully capture all possible input or have full +control over the mouse and keyboard on all platforms. each platform +has its own limitations and these limitations may influence your +choice for the server. + +the following lists enumerate the limitations of each platform. a +key (combination) that cannot be captured is not detected by synergy. +a key (combination) that cannot be blocked will be passed through to +the server system even when the mouse is on a client system. if a +key cannot be captured then it also cannot be blocked. + +windows 95 family, windows NT prior to service pack 3: + * cannot capture: + * ctrl+alt+del + * ctrl+esc + * alt+[shift+]tab + * alt+[shift+]esc + * windows+E + * windows+[ctrl+]F + * windows+[shift+]M + * windows+R + * windows+F1 + * windows+tab + * windows+break + * accessibility shortcuts (e.g. press shift 5 times for sticky keys) + * the individual keys are captured but the dialogs still appear + * cannot synthesize: + * accessibility shortcuts + +windows NT family (except NT prior to SP3): + * cannot block: + * ctrl+alt+del + * accessibility shortcuts (e.g. press shift 5 times for sticky keys) + * the individual keys are captured but the dialogs still appear + * cannot synthesize: + * accessibility shortcuts + +linux, unix: + * cannot capture: + * ctrl+alt+del + * ctrl+alt+backspace (only if used by the X server) + * ctrl+alt+keypad_plus (only if used by the X server) + * ctrl+alt+keypad_minus (only if used by the X server) + * keyboard/mouse grabs prevent switching screens for their duration + * some display managers grab the keyboard until login + +currently, the windows NT family (except NT prior to SP3) makes the +best server. + + +known bugs +---------- +all: + * screen savers are not handled + * non-ASCII characters are not supported + * keys may be left in the logical pressed state on a client if it + terminates while the mouse is on that client. physically pressing + and releasing the key fixes the problem. + * plain text is the only supported clipboard format + +windows: + * screen flashes when entering the screen + * synergy may interfere with desktop switcher programs, however + synergy understands and handles multiple desktops. + * there should be a control panel + * there should be a taskbar icon + * hook DLL sometimes not properly shutdown. this is usually only + a problem when forcibly killing the synergy server then trying + to restart it. logging off and on should fix it. + +windows 95 family: + * typing into a console window can be slow + +windows NT family: + * the event viewer reports a message lookup error for synergy logs. + however, the full synergy message is in the extra data portion of + the event dialog. + * does not gracefully handle NoInteractiveServices being enabled + +linux: + * one thread may linger if the application crashes. + use kill or killall to terminate this process (threads are + processes under linux) before restarting. + * some keyboards have toggle keys that toggle on on key press and + toggle off on the key release after the next key press. synergy + doesn't handle these properly. + * shift-lock (as opposed to caps-lock) is not supported + + +tips and tricks +--------------- +* a screen can be its own neighbor. that allows a screen to "wrap". + for example, if a configuration linked the left and right sides of + a screen to itself then moving off the left of the screen would put + the mouse at the right of the screen and vice versa. + +* you cannot switch screens when a key or mouse button is pressed. + +* you cannot switch screens when the scroll lock it toggled on. use + this to prevent unintentional switching. + +* turn off mouse driven virtual desktop switching on X windows. it + will interfere with synergy. use keyboard shortcuts instead. + diff --git a/examples/synergy.conf b/examples/synergy.conf new file mode 100644 index 00000000..2586dfaf --- /dev/null +++ b/examples/synergy.conf @@ -0,0 +1,37 @@ +# sample synergy configuration file +# +# comments begin with the # character and continue to the end of +# line. comments may appear anywhere the syntax permits. + +section: screens + # three hosts named: moe, larry, and curly + moe: + larry: + curly: +end + +section: links + # larry is to the right of moe and curly is above moe + moe: + right = larry + up = curly + + # moe is to the left of larry and curly is above larry. + # note that curly is above both moe and larry and moe + # and larry have a symmetric connection (they're in + # opposite directions of each other). + larry: + left = moe + up = curly + + # larry is below curly. if you move up from moe and then + # down, you'll end up on larry. + curly: + down = larry +end + +section: aliases + # curly is also known as shemp + curly: + shemp +end diff --git a/examples/synergy.linux.init b/examples/synergy.linux.init new file mode 100755 index 00000000..4901f6e0 --- /dev/null +++ b/examples/synergy.linux.init @@ -0,0 +1,43 @@ +#!/bin/sh +# +# Startup script for synergy client +# +# chkconfig: 5 98 02 +# description: Starts/stops synergy client when X server is started/stopped +# +# processname: synergy + +# startup command line arguments +ARGS=192.168.1.3 + +# Source function library. +. /etc/rc.d/init.d/functions + +case "$1" in + start) + echo -n "Starting synergy client: " + daemon synergy $ARGS + RETVAL=$? + echo + ;; + stop) + echo -n "Stopping synergy client: " + killproc synergy + RETVAL=$? + echo + ;; + status) + status synergy + RETVAL=$? + ;; + restart) + $0 stop + $0 start + RETVAL=$? + ;; + *) + echo "Usage: synergy {start|stop|status|restart}" + exit 1 +esac + +exit $RETVAL diff --git a/examples/synergyd.linux.init b/examples/synergyd.linux.init new file mode 100755 index 00000000..9f4e3626 --- /dev/null +++ b/examples/synergyd.linux.init @@ -0,0 +1,43 @@ +#!/bin/sh +# +# Startup script for synergy server +# +# chkconfig: 5 98 02 +# description: Starts/stops synergy server when X server is started/stopped +# +# processname: synergyd + +# startup command line arguments +ARGS= + +# Source function library. +. /etc/rc.d/init.d/functions + +case "$1" in + start) + echo -n "Starting synergy server: " + daemon synergyd $ARGS + RETVAL=$? + echo + ;; + stop) + echo -n "Stopping synergy server: " + killproc synergyd + RETVAL=$? + echo + ;; + status) + status synergyd + RETVAL=$? + ;; + restart) + $0 stop + $0 start + RETVAL=$? + ;; + *) + echo "Usage: synergyd {start|stop|status|restart}" + exit 1 +esac + +exit $RETVAL From cf0805c93a883fcc5c946b568c503c924f7fa1f8 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 10 Jun 2002 11:08:02 +0000 Subject: [PATCH 159/807] updates. --- README | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/README b/README index 606d1e7f..3ef993c4 100644 --- a/README +++ b/README @@ -30,12 +30,14 @@ Linux, Unix: manifest -------- - linux windows - ----- ------- - synergy synergy.exe the synergy client - synergyd synergyd.exe the synergy server - synergy.conf synergy.sgc sample configuration file - README README this file + linux windows + ----- ------- + README README this file + synergy synergy.exe the synergy client + synergyd synergyd.exe the synergy server + synergy.conf synergy.sgc sample configuration file + synergy.linux.init startup script for client + synergyd.linux.init startup script for server running synergy @@ -180,6 +182,15 @@ windows: hostname argument. to change the arguments you must first uninstall then reinstall. + you must also install the configuration file along with the + server. it's recommended that you put it in the windows + directory (e.g. C:\WINNT) and call it "synergy.sgc". the + server will automatically find this file. however, you can + also use the --config command line option and specify an + absolute path to the file. remember that this file must be + accessible when the system starts up, before network shares + are mapped. + to uninstall use: synergy --uninstall From 500990b1537f78660fc595b13369bcaa675b7787 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 10 Jun 2002 11:09:02 +0000 Subject: [PATCH 160/807] fixes. --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 3ef993c4..476f5a5a 100644 --- a/README +++ b/README @@ -35,7 +35,7 @@ manifest README README this file synergy synergy.exe the synergy client synergyd synergyd.exe the synergy server - synergy.conf synergy.sgc sample configuration file + synergy.conf synergy.conf sample configuration file synergy.linux.init startup script for client synergyd.linux.init startup script for server From 68940e58f305f63e14ae546e96bae4e441c8c93f Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 10 Jun 2002 16:49:46 +0000 Subject: [PATCH 161/807] win32 changes. now including windows.h with WIN32_LEAN_AND_MEAN to avoid including some stuff we don't want (like winsock). --- base/CLog.cpp | 82 ++++++++++++++++++++++++++-------- base/CStopwatch.cpp | 63 +++++++++++++++++--------- mt/CCondVar.cpp | 1 + mt/CMutex.cpp | 1 + mt/CThreadRep.h | 1 + net/CNetwork.cpp | 4 +- net/CNetwork.h | 2 - platform/CMSWindowsClipboard.h | 1 + platform/CMSWindowsScreen.h | 1 + platform/CWin32Platform.h | 1 + server/CSynergyHook.h | 1 + server/synrgyhk.dsp | 4 +- 12 files changed, 117 insertions(+), 45 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index 70c2955e..d36ae24d 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -5,10 +5,12 @@ #include #if defined(CONFIG_PLATFORM_WIN32) +#define WIN32_LEAN_AND_MEAN #include #define vsnprintf _vsnprintf #endif +// names of priorities static const char* g_priority[] = { "FATAL", "ERROR", @@ -19,17 +21,29 @@ static const char* g_priority[] = { "DEBUG1", "DEBUG2" }; + +// number of priorities static const int g_numPriority = (int)(sizeof(g_priority) / sizeof(g_priority[0])); + +// the default priority #if defined(NDEBUG) static const int g_defaultMaxPriority = 4; #else static const int g_defaultMaxPriority = 5; #endif -static const int g_maxPriorityLength = 7; // length of longest string + +// length of longest string in g_priority +static const int g_maxPriorityLength = 7; + +// length of suffix string (": ") static const int g_prioritySuffixLength = 2; + +// amount of padded required to fill in the priority prefix static const int g_priorityPad = g_maxPriorityLength + g_prioritySuffixLength; + +// minimum length of a newline sequence static const int g_newlineLength = 2; // @@ -40,7 +54,10 @@ CLog::Outputter CLog::s_outputter = NULL; CLog::Lock CLog::s_lock = &CLog::dummyLock; int CLog::s_maxPriority = -1; -void CLog::print(const char* fmt, ...) +void +CLog::print( + const char* fmt, + ...) { // check if fmt begins with a priority argument int priority = 4; @@ -68,8 +85,12 @@ void CLog::print(const char* fmt, ...) delete[] buffer; } -void CLog::printt(const char* file, int line, - const char* fmt, ...) +void +CLog::printt( + const char* file, + int line, + const char* fmt, + ...) { // check if fmt begins with a priority argument int priority = 4; @@ -110,31 +131,39 @@ void CLog::printt(const char* file, int line, delete[] buffer; } -void CLog::setOutputter(Outputter outputter) +void +CLog::setOutputter( + Outputter outputter) { CHoldLock lock(s_lock); s_outputter = outputter; } -CLog::Outputter CLog::getOutputter() +CLog::Outputter +CLog::getOutputter() { CHoldLock lock(s_lock); return s_outputter; } -void CLog::setLock(Lock newLock) +void +CLog::setLock( + Lock newLock) { CHoldLock lock(s_lock); s_lock = (newLock == NULL) ? dummyLock : newLock; } -CLog::Lock CLog::getLock() +CLog::Lock +CLog::getLock() { CHoldLock lock(s_lock); return (s_lock == dummyLock) ? NULL : s_lock; } -bool CLog::setFilter(const char* maxPriority) +bool +CLog::setFilter( + const char* maxPriority) { if (maxPriority != NULL) { for (int i = 0; i < g_numPriority; ++i) { @@ -148,24 +177,30 @@ bool CLog::setFilter(const char* maxPriority) return true; } -void CLog::setFilter(int maxPriority) +void +CLog::setFilter( + int maxPriority) { CHoldLock lock(s_lock); s_maxPriority = maxPriority; } -int CLog::getFilter() +int +CLog::getFilter() { CHoldLock lock(s_lock); return getMaxPriority(); } -void CLog::dummyLock(bool) +void +CLog::dummyLock( + bool) { // do nothing } -int CLog::getMaxPriority() +int +CLog::getMaxPriority() { CHoldLock lock(s_lock); @@ -177,7 +212,10 @@ int CLog::getMaxPriority() return s_maxPriority; } -void CLog::output(int priority, char* msg) +void +CLog::output( + int priority, + char* msg) { assert(priority >= -1 && priority < g_numPriority); assert(msg != 0); @@ -211,8 +249,13 @@ void CLog::output(int priority, char* msg) } } -char* CLog::vsprint(int pad, char* buffer, int len, - const char* fmt, va_list args) +char* +CLog::vsprint( + int pad, + char* buffer, + int len, + const char* fmt, + va_list args) { assert(len > 0); @@ -240,14 +283,17 @@ char* CLog::vsprint(int pad, char* buffer, int len, static DWORD s_thread = 0; -static BOOL WINAPI CLogSignalHandler(DWORD) +static +BOOL WINAPI +CLogSignalHandler(DWORD) { // terminate cleanly and skip remaining handlers PostThreadMessage(s_thread, WM_QUIT, 0, 0); return TRUE; } -void CLog::openConsole() +void +CLog::openConsole() { static bool s_hasConsole = false; diff --git a/base/CStopwatch.cpp b/base/CStopwatch.cpp index 6a346a7f..5cd61bc7 100644 --- a/base/CStopwatch.cpp +++ b/base/CStopwatch.cpp @@ -5,12 +5,13 @@ // CStopwatch::CStopwatch(bool triggered) : - m_mark(0.0), - m_triggered(triggered), - m_stopped(triggered) + m_mark(0.0), + m_triggered(triggered), + m_stopped(triggered) { - if (!triggered) + if (!triggered) { m_mark = getClock(); + } } CStopwatch::~CStopwatch() @@ -18,7 +19,8 @@ CStopwatch::~CStopwatch() // do nothing } -double CStopwatch::reset() +double +CStopwatch::reset() { if (m_stopped) { const double dt = m_mark; @@ -33,43 +35,52 @@ double CStopwatch::reset() } } -void CStopwatch::stop() +void +CStopwatch::stop() { - if (m_stopped) + if (m_stopped) { return; + } // save the elapsed time m_mark = getClock() - m_mark; m_stopped = true; } -void CStopwatch::start() +void +CStopwatch::start() { m_triggered = false; - if (!m_stopped) + if (!m_stopped) { return; + } // set the mark such that it reports the time elapsed at stop() m_mark = getClock() - m_mark; m_stopped = false; } -void CStopwatch::setTrigger() +void +CStopwatch::setTrigger() { stop(); m_triggered = true; } -double CStopwatch::getTime() +double +CStopwatch::getTime() { if (m_triggered) { const double dt = m_mark; start(); return dt; } - if (m_stopped) + else if (m_stopped) { return m_mark; - return getClock() - m_mark; + } + else { + return getClock() - m_mark; + } } CStopwatch::operator double() @@ -77,16 +88,21 @@ CStopwatch::operator double() return getTime(); } -bool CStopwatch::isStopped() const +bool +CStopwatch::isStopped() const { return m_stopped; } -double CStopwatch::getTime() const +double +CStopwatch::getTime() const { - if (m_stopped) + if (m_stopped) { return m_mark; - return getClock() - m_mark; + } + else { + return getClock() - m_mark; + } } CStopwatch::operator double() const @@ -98,7 +114,8 @@ CStopwatch::operator double() const #include -double CStopwatch::getClock() const +double +CStopwatch::getClock() const { struct timeval t; gettimeofday(&t, NULL); @@ -121,6 +138,7 @@ double CStopwatch::getClock() const #define MMNOMMIO // Multimedia file I/O support #define MMNOMMSYSTEM // General MMSYSTEM functions +#define WIN32_LEAN_AND_MEAN #include #include @@ -150,18 +168,21 @@ CStopwatchInit::CStopwatchInit() else { // load winmm.dll and get timeGetTime s_mmInstance = LoadLibrary("winmm"); - if (s_mmInstance) + if (s_mmInstance) { s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime"); + } } } CStopwatchInit::~CStopwatchInit() { - if (s_mmInstance) + if (s_mmInstance) { FreeLibrary(reinterpret_cast(s_mmInstance)); + } } -double CStopwatch::getClock() const +double +CStopwatch::getClock() const { // get time. we try three ways, in order of descending precision if (s_freq != 0.0) { diff --git a/mt/CCondVar.cpp b/mt/CCondVar.cpp index 9aeb7d8f..104332c6 100644 --- a/mt/CCondVar.cpp +++ b/mt/CCondVar.cpp @@ -167,6 +167,7 @@ bool CCondVarBase::wait( #include "CLock.h" #include "CThreadRep.h" +#define WIN32_LEAN_AND_MEAN #include // diff --git a/mt/CMutex.cpp b/mt/CMutex.cpp index 2d092b8d..0318908a 100644 --- a/mt/CMutex.cpp +++ b/mt/CMutex.cpp @@ -102,6 +102,7 @@ void CMutex::unlock() const #if defined(CONFIG_PLATFORM_WIN32) +#define WIN32_LEAN_AND_MEAN #include void CMutex::init() diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h index 7189752b..b407ae88 100644 --- a/mt/CThreadRep.h +++ b/mt/CThreadRep.h @@ -6,6 +6,7 @@ #if defined(CONFIG_PTHREADS) #include #elif defined(CONFIG_PLATFORM_WIN32) +#define WIN32_LEAN_AND_MEAN #include #endif diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp index 30f68913..4037d9d9 100644 --- a/net/CNetwork.cpp +++ b/net/CNetwork.cpp @@ -125,8 +125,8 @@ UInt16 CNetwork::swaphtons(UInt16 v) { static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; if (s_endian.b[0] == 0x34) - return ((v & 0xff00u) >> 8) | - ((v & 0x00ffu) << 8); + return static_cast( ((v & 0xff00u) >> 8) | + ((v & 0x00ffu) << 8)); else return v; } diff --git a/net/CNetwork.h b/net/CNetwork.h index 1fa01afa..c66d8164 100644 --- a/net/CNetwork.h +++ b/net/CNetwork.h @@ -29,8 +29,6 @@ typedef int ssize_t; # endif #endif -// FIXME -- must handle htonl and ilk when defined as macros - class CNetwork { public: #if defined(CONFIG_PLATFORM_WIN32) diff --git a/platform/CMSWindowsClipboard.h b/platform/CMSWindowsClipboard.h index 7d7d0225..6179ee30 100644 --- a/platform/CMSWindowsClipboard.h +++ b/platform/CMSWindowsClipboard.h @@ -2,6 +2,7 @@ #define CMSWINDOWSCLIPBOARD_H #include "IClipboard.h" +#define WIN32_LEAN_AND_MEAN #include class CMSWindowsClipboard : public IClipboard { diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index 90c00b95..0e54205b 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -4,6 +4,7 @@ #include "CMutex.h" #include "IClipboard.h" #include "BasicTypes.h" +#define WIN32_LEAN_AND_MEAN #include class CString; diff --git a/platform/CWin32Platform.h b/platform/CWin32Platform.h index 33f657b9..4300c050 100644 --- a/platform/CWin32Platform.h +++ b/platform/CWin32Platform.h @@ -4,6 +4,7 @@ #include "IPlatform.h" #include "CCondVar.h" #include "CMutex.h" +#define WIN32_LEAN_AND_MEAN #include class CWin32Platform : public IPlatform { diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index 7d6fa53b..4aefcafc 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -2,6 +2,7 @@ #define CSYNERGYHOOK_H #include "BasicTypes.h" +#define WIN32_LEAN_AND_MEAN #include #if defined(CONFIG_PLATFORM_WIN32) diff --git a/server/synrgyhk.dsp b/server/synrgyhk.dsp index fe82c7cd..1f04aabb 100644 --- a/server/synrgyhk.dsp +++ b/server/synrgyhk.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Intermediate_Dir "ReleaseHook" # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -68,7 +68,7 @@ LINK32=link.exe # PROP Intermediate_Dir "DebugHook" # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 From 62519b19fe460af5f188674893e561879515cd14 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 10 Jun 2002 22:06:45 +0000 Subject: [PATCH 162/807] indentation and other formatting changes. also cleaned up #includes. --- base/BasicTypes.h | 1 - base/CFunctionJob.cpp | 7 +- base/CLog.cpp | 7 +- base/CLog.h | 4 +- base/CString.cpp | 41 +-- base/CString.h | 43 +-- base/TMethodJob.h | 9 +- base/XBase.cpp | 31 ++- base/XBase.h | 2 +- base/common.h | 2 + base/stdistream.h | 3 + base/stdostream.h | 2 + client/CClient.cpp | 117 +++++--- client/CClient.h | 5 +- client/CMSWindowsSecondaryScreen.cpp | 287 ++++++++++++------- client/CMSWindowsSecondaryScreen.h | 7 +- client/CXWindowsSecondaryScreen.cpp | 268 +++++++++++------- client/CXWindowsSecondaryScreen.h | 9 +- client/client.cpp | 120 +++++--- http/CHTTPProtocol.cpp | 93 ++++--- http/CHTTPProtocol.h | 16 +- http/XHTTP.cpp | 47 ++-- http/XHTTP.h | 1 - io/CBufferedInputStream.cpp | 48 ++-- io/CBufferedInputStream.h | 2 +- io/CBufferedOutputStream.cpp | 38 ++- io/CBufferedOutputStream.h | 2 +- io/CInputStreamFilter.cpp | 12 +- io/COutputStreamFilter.cpp | 12 +- io/CStreamBuffer.cpp | 24 +- io/IInputStream.h | 1 - io/IOutputStream.h | 1 - io/XIO.cpp | 16 +- io/XIO.h | 1 - mt/CCondVar.cpp | 76 +++-- mt/CCondVar.h | 22 +- mt/CLock.cpp | 8 +- mt/CLock.h | 2 - mt/CMutex.cpp | 35 ++- mt/CMutex.h | 2 - mt/CThread.cpp | 63 +++-- mt/CThread.h | 2 - mt/CThreadRep.cpp | 173 ++++++++---- mt/CTimerThread.cpp | 5 +- mt/CTimerThread.h | 2 - mt/XThread.h | 2 - net/CNetwork.cpp | 127 ++++++--- net/CNetworkAddress.cpp | 35 ++- net/CNetworkAddress.h | 1 - net/CTCPListenSocket.cpp | 13 +- net/CTCPSocket.cpp | 45 ++- net/CTCPSocket.h | 1 - net/IListenSocket.h | 2 - net/ISocket.h | 3 - net/Makefile | 1 + net/XNetwork.cpp | 28 +- net/XNetwork.h | 3 +- net/XSocket.cpp | 40 ++- net/XSocket.h | 2 +- platform/CMSWindowsClipboard.cpp | 62 +++-- platform/CMSWindowsScreen.cpp | 68 +++-- platform/CMSWindowsScreen.h | 3 +- platform/CUnixPlatform.cpp | 110 ++++---- platform/CUnixPlatform.h | 10 +- platform/CWin32Platform.cpp | 150 ++++++---- platform/CWin32Platform.h | 14 +- platform/CXWindowsClipboard.cpp | 343 ++++++++++++++--------- platform/CXWindowsClipboard.h | 31 +-- platform/CXWindowsScreen.cpp | 143 ++++++---- platform/CXWindowsScreen.h | 7 +- platform/CXWindowsUtil.cpp | 83 ++++-- platform/CXWindowsUtil.h | 16 +- platform/IPlatform.h | 13 +- server/CConfig.cpp | 128 ++++++--- server/CConfig.h | 12 +- server/CHTTPServer.cpp | 131 ++++++--- server/CHTTPServer.h | 2 +- server/CMSWindowsPrimaryScreen.cpp | 194 ++++++++----- server/CMSWindowsPrimaryScreen.h | 8 +- server/CServer.cpp | 344 +++++++++++++++-------- server/CServer.h | 38 ++- server/CServerProtocol.cpp | 43 +-- server/CServerProtocol.h | 9 +- server/CServerProtocol1_0.cpp | 101 ++++--- server/CServerProtocol1_0.h | 2 +- server/CSynergyHook.cpp | 86 ++++-- server/CSynergyHook.h | 2 +- server/CXWindowsPrimaryScreen.cpp | 401 +++++++++++++++------------ server/CXWindowsPrimaryScreen.h | 5 +- server/server.cpp | 128 ++++++--- synergy/CClipboard.cpp | 60 ++-- synergy/CClipboard.h | 19 +- synergy/CInputPacketStream.cpp | 32 ++- synergy/COutputPacketStream.cpp | 18 +- synergy/CProtocolUtil.cpp | 46 ++- synergy/CProtocolUtil.h | 6 +- synergy/CTCPSocketFactory.cpp | 6 +- synergy/IClipboard.h | 3 +- synergy/IPrimaryScreen.h | 1 - synergy/ISecondaryScreen.h | 3 +- synergy/IServerProtocol.h | 7 +- synergy/ISocketFactory.h | 1 - synergy/XScreen.cpp | 3 +- synergy/XSynergy.cpp | 40 ++- 104 files changed, 3089 insertions(+), 1815 deletions(-) diff --git a/base/BasicTypes.h b/base/BasicTypes.h index b21eba8f..f71d15b4 100644 --- a/base/BasicTypes.h +++ b/base/BasicTypes.h @@ -58,7 +58,6 @@ typedef unsigned char UInt8; typedef unsigned short UInt16; typedef unsigned int UInt32; typedef unsigned long long UInt64; - #endif #endif // CONFIG_PLATFORM_WIN32 diff --git a/base/CFunctionJob.cpp b/base/CFunctionJob.cpp index 50a55100..7558c911 100644 --- a/base/CFunctionJob.cpp +++ b/base/CFunctionJob.cpp @@ -5,13 +5,14 @@ // CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) : - m_func(func), - m_arg(arg) + m_func(func), + m_arg(arg) { // do nothing } -void CFunctionJob::run() +void +CFunctionJob::run() { if (m_func != NULL) { m_func(m_arg); diff --git a/base/CLog.cpp b/base/CLog.cpp index d36ae24d..a35ca588 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -1,8 +1,7 @@ #include "CLog.h" -#include -#include -#include -#include +#include +#include +#include #if defined(CONFIG_PLATFORM_WIN32) #define WIN32_LEAN_AND_MEAN diff --git a/base/CLog.h b/base/CLog.h index 22a34357..0a790929 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -2,7 +2,7 @@ #define CLOG_H #include "BasicTypes.h" -#include +#include class CLog { public: @@ -24,7 +24,7 @@ public: // type of lock/unlock function typedef void (*Lock)(bool lock); - // + // print a log message static void print(const char*, ...); static void printt(const char* file, int line, const char*, ...); diff --git a/base/CString.cpp b/base/CString.cpp index e174cad0..2ef952ee 100644 --- a/base/CString.cpp +++ b/base/CString.cpp @@ -1,30 +1,33 @@ #include "CString.h" -#include +#include #include // // CStringUtil::CaselessCmp // -bool CStringUtil::CaselessCmp::cmpEqual( - const CString::value_type& a, - const CString::value_type& b) +bool +CStringUtil::CaselessCmp::cmpEqual( + const CString::value_type& a, + const CString::value_type& b) { - // FIXME -- use std::tolower + // FIXME -- use std::tolower but not in all versions of libstdc++ return tolower(a) == tolower(b); } -bool CStringUtil::CaselessCmp::cmpLess( - const CString::value_type& a, - const CString::value_type& b) +bool +CStringUtil::CaselessCmp::cmpLess( + const CString::value_type& a, + const CString::value_type& b) { - // FIXME -- use std::tolower + // FIXME -- use std::tolower but not in all versions of libstdc++ return tolower(a) < tolower(b); } -bool CStringUtil::CaselessCmp::less( - const CString& a, - const CString& b) +bool +CStringUtil::CaselessCmp::less( + const CString& a, + const CString& b) { return std::lexicographical_compare( a.begin(), a.end(), @@ -32,16 +35,18 @@ bool CStringUtil::CaselessCmp::less( &CStringUtil::CaselessCmp::cmpLess); } -bool CStringUtil::CaselessCmp::equal( - const CString& a, - const CString& b) +bool +CStringUtil::CaselessCmp::equal( + const CString& a, + const CString& b) { return !(less(a, b) || less(b, a)); } -bool CStringUtil::CaselessCmp::operator()( - const CString& a, - const CString& b) const +bool +CStringUtil::CaselessCmp::operator()( + const CString& a, + const CString& b) const { return less(a, b); } diff --git a/base/CString.h b/base/CString.h index 10664e30..78fd9ad2 100644 --- a/base/CString.h +++ b/base/CString.h @@ -1,48 +1,15 @@ #ifndef CSTRING_H #define CSTRING_H -#include "common.h" #include "stdpre.h" #include #include "stdpost.h" -#if defined(_MSC_VER) -#pragma warning(push, 4) -#pragma warning(disable: 4097) // typedef-name used as synonym -#endif - -#ifndef CSTRING_DEF_CTOR -#define CSTRING_ALLOC1 -#define CSTRING_ALLOC2 -#define CSTRING_DEF_CTOR CString() : _Myt() { } -#endif - // use to get appropriate type for string constants. it depends on // the internal representation type of CString. #define _CS(_x) _x -class CString : public std::string { -public: - typedef char _e; - typedef _e CharT; - typedef std::allocator<_e> _a; - typedef std::string _Myt; - typedef const_iterator _It; - - // same constructors as base class - CSTRING_DEF_CTOR - CString(const _Myt& _x) : _Myt(_x) { } - CString(const _Myt& _x, size_type _p, size_type _m CSTRING_ALLOC1) : - _Myt(_x, _p, _m CSTRING_ALLOC2) { } - CString(const _e *_s, size_type _n CSTRING_ALLOC1) : - _Myt(_s, _n CSTRING_ALLOC2) { } - CString(const _e *_s CSTRING_ALLOC1) : - _Myt(_s CSTRING_ALLOC2) { } - CString(size_type _n, _e _c CSTRING_ALLOC1) : - _Myt(_n, _c CSTRING_ALLOC2) { } - CString(_It _f, _It _l CSTRING_ALLOC1) : - _Myt(_f, _l CSTRING_ALLOC2) { } -}; +typedef std::string CString; class CStringUtil { public: @@ -52,15 +19,11 @@ public: static bool less(const CString&, const CString&); static bool equal(const CString&, const CString&); static bool cmpLess(const CString::value_type&, - const CString::value_type&); + const CString::value_type&); static bool cmpEqual(const CString::value_type&, - const CString::value_type&); + const CString::value_type&); }; }; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - #endif diff --git a/base/TMethodJob.h b/base/TMethodJob.h index f9514bf2..59a22208 100644 --- a/base/TMethodJob.h +++ b/base/TMethodJob.h @@ -20,16 +20,17 @@ private: template inline TMethodJob::TMethodJob(T* object, void (T::*method)(void*), void* arg) : - m_object(object), - m_method(method), - m_arg(arg) + m_object(object), + m_method(method), + m_arg(arg) { // do nothing } template inline -void TMethodJob::run() +void +TMethodJob::run() { if (m_object != NULL) { (m_object->*m_method)(m_arg); diff --git a/base/XBase.cpp b/base/XBase.cpp index bc2db608..c23a77ee 100644 --- a/base/XBase.cpp +++ b/base/XBase.cpp @@ -1,5 +1,5 @@ #include "XBase.h" -#include +#include // win32 wants a const char* argument to std::exception c'tor #if defined(CONFIG_PLATFORM_WIN32) @@ -15,12 +15,16 @@ // XBase // -XBase::XBase() : exception(STDEXCEPTARG), m_what() +XBase::XBase() : + exception(STDEXCEPTARG), + m_what() { // do nothing } -XBase::XBase(const CString& msg) : exception(STDEXCEPTARG), m_what(msg) +XBase::XBase(const CString& msg) : + exception(STDEXCEPTARG), + m_what(msg) { // do nothing } @@ -30,7 +34,8 @@ XBase::~XBase() // do nothing } -const char* XBase::what() const +const char* +XBase::what() const { if (m_what.empty()) { m_what = getWhat(); @@ -38,8 +43,10 @@ const char* XBase::what() const return m_what.c_str(); } -CString XBase::format(const char* /*id*/, - const char* fmt, ...) const throw() +CString +XBase::format( + const char* /*id*/, + const char* fmt, ...) const throw() { // FIXME -- use id to lookup formating string // FIXME -- format string with arguments @@ -51,22 +58,26 @@ CString XBase::format(const char* /*id*/, // MXErrno // -MXErrno::MXErrno() : m_errno(errno) +MXErrno::MXErrno() : + m_errno(errno) { // do nothing } -MXErrno::MXErrno(int err) : m_errno(err) +MXErrno::MXErrno(int err) : + m_errno(err) { // do nothing } -int MXErrno::getErrno() const +int +MXErrno::getErrno() const { return m_errno; } -const char* MXErrno::getErrstr() const +const char* +MXErrno::getErrstr() const { return strerror(m_errno); } diff --git a/base/XBase.h b/base/XBase.h index c654de6e..1e4c208c 100644 --- a/base/XBase.h +++ b/base/XBase.h @@ -21,7 +21,7 @@ protected: // look up a message and format it virtual CString format(const char* id, - const char* defaultFormat, ...) const throw(); + const char* defaultFormat, ...) const throw(); private: mutable CString m_what; diff --git a/base/common.h b/base/common.h index c29d7543..e31a1b73 100644 --- a/base/common.h +++ b/base/common.h @@ -42,4 +42,6 @@ #define NULL 0 #endif +#include + #endif diff --git a/base/stdistream.h b/base/stdistream.h index 1cbbdcbd..440a33fe 100644 --- a/base/stdistream.h +++ b/base/stdistream.h @@ -2,11 +2,14 @@ #if !defined(CONFIG_PLATFORM_LINUX) #include #else +// some versions of libstdc++ don't have +// FIXME -- only include iostream for versions that don't have istream #include #endif #include "stdpost.h" #if defined(CONFIG_PLATFORM_WIN32) && defined(_MSC_VER) +// istream has no overloads for __int* types inline std::istream& operator>>(std::istream& s, SInt8& i) { return s >> (signed char&)i; } diff --git a/base/stdostream.h b/base/stdostream.h index 11a1b361..fd434281 100644 --- a/base/stdostream.h +++ b/base/stdostream.h @@ -2,6 +2,8 @@ #if !defined(CONFIG_PLATFORM_LINUX) #include #else +// some versions of libstdc++ don't have +// FIXME -- only include iostream for versions that don't have ostream #include #endif #include "stdpost.h" diff --git a/client/CClient.cpp b/client/CClient.cpp index 3d0e4d89..394efdf0 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -1,19 +1,19 @@ #include "CClient.h" +#include "CClipboard.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" -#include "CClipboard.h" #include "ISecondaryScreen.h" #include "ProtocolTypes.h" -#include "CLock.h" -#include "CLog.h" -#include "CThread.h" -#include "CTimerThread.h" -#include "TMethodJob.h" #include "XScreen.h" #include "XSynergy.h" +#include "XSocket.h" +#include "CLock.h" +#include "CThread.h" +#include "CTimerThread.h" #include "XThread.h" -#include +#include "CLog.h" +#include "TMethodJob.h" #include // hack to work around operator=() bug in STL in g++ prior to v3 @@ -28,15 +28,16 @@ // CClient // -CClient::CClient(const CString& clientName) : - m_name(clientName), - m_input(NULL), - m_output(NULL), - m_screen(NULL), - m_camp(false), - m_active(false), - m_seqNum(0), - m_ignoreMove(false) +CClient::CClient( + const CString& clientName) : + m_name(clientName), + m_input(NULL), + m_output(NULL), + m_screen(NULL), + m_camp(false), + m_active(false), + m_seqNum(0), + m_ignoreMove(false) { // do nothing } @@ -46,12 +47,16 @@ CClient::~CClient() // do nothing } -void CClient::camp(bool on) +void +CClient::camp( + bool on) { m_camp = on; } -bool CClient::run(const CNetworkAddress& serverAddress) +bool +CClient::run( + const CNetworkAddress& serverAddress) { CThread* thread = NULL; try { @@ -128,12 +133,15 @@ bool CClient::run(const CNetworkAddress& serverAddress) } } -void CClient::quit() +void +CClient::quit() { m_screen->stop(); } -void CClient::onClipboardChanged(ClipboardID id) +void +CClient::onClipboardChanged( + ClipboardID id) { log((CLOG_DEBUG "sending clipboard %d changed", id)); CLock lock(&m_mutex); @@ -169,7 +177,8 @@ void CClient::onClipboardChanged(ClipboardID id) } } -void CClient::onResolutionChanged() +void +CClient::onResolutionChanged() { log((CLOG_DEBUG "resolution changed")); @@ -183,7 +192,8 @@ void CClient::onResolutionChanged() } #include "CTCPSocket.h" // FIXME -void CClient::runSession(void*) +void +CClient::runSession(void*) { log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); @@ -401,7 +411,8 @@ void CClient::runSession(void*) #elif defined(CONFIG_PLATFORM_UNIX) #include "CXWindowsSecondaryScreen.h" #endif -void CClient::openSecondaryScreen() +void +CClient::openSecondaryScreen() { assert(m_screen == NULL); @@ -428,7 +439,8 @@ void CClient::openSecondaryScreen() m_screen->open(this); } -void CClient::closeSecondaryScreen() +void +CClient::closeSecondaryScreen() { assert(m_screen != NULL); @@ -447,7 +459,8 @@ void CClient::closeSecondaryScreen() m_screen = NULL; } -void CClient::onEnter() +void +CClient::onEnter() { SInt16 x, y; UInt16 mask; @@ -460,7 +473,8 @@ void CClient::onEnter() m_screen->enter(x, y, static_cast(mask)); } -void CClient::onLeave() +void +CClient::onLeave() { log((CLOG_DEBUG1 "recv leave")); @@ -504,7 +518,8 @@ void CClient::onLeave() } } -void CClient::onGrabClipboard() +void +CClient::onGrabClipboard() { ClipboardID id; UInt32 seqNum; @@ -524,7 +539,8 @@ void CClient::onGrabClipboard() m_screen->grabClipboard(id); } -void CClient::onScreenSaver() +void +CClient::onScreenSaver() { SInt8 on; { @@ -535,13 +551,15 @@ void CClient::onScreenSaver() // FIXME } -void CClient::onQueryInfo() +void +CClient::onQueryInfo() { CLock lock(&m_mutex); onQueryInfoNoLock(); } -void CClient::onQueryInfoNoLock() +void +CClient::onQueryInfoNoLock() { SInt32 x, y, w, h; m_screen->getMousePos(&x, &y); @@ -552,14 +570,16 @@ void CClient::onQueryInfoNoLock() CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize, x, y); } -void CClient::onInfoAcknowledgment() +void +CClient::onInfoAcknowledgment() { log((CLOG_DEBUG1 "recv info acknowledgment")); CLock lock(&m_mutex); m_ignoreMove = false; } -void CClient::onSetClipboard() +void +CClient::onSetClipboard() { ClipboardID id; CString data; @@ -584,7 +604,8 @@ void CClient::onSetClipboard() m_screen->setClipboard(id, &clipboard); } -void CClient::onKeyDown() +void +CClient::onKeyDown() { UInt16 id, mask; { @@ -596,7 +617,8 @@ void CClient::onKeyDown() static_cast(mask)); } -void CClient::onKeyRepeat() +void +CClient::onKeyRepeat() { UInt16 id, mask, count; { @@ -609,7 +631,8 @@ void CClient::onKeyRepeat() count); } -void CClient::onKeyUp() +void +CClient::onKeyUp() { UInt16 id, mask; { @@ -621,7 +644,8 @@ void CClient::onKeyUp() static_cast(mask)); } -void CClient::onMouseDown() +void +CClient::onMouseDown() { SInt8 id; { @@ -632,7 +656,8 @@ void CClient::onMouseDown() m_screen->mouseDown(static_cast(id)); } -void CClient::onMouseUp() +void +CClient::onMouseUp() { SInt8 id; { @@ -643,7 +668,8 @@ void CClient::onMouseUp() m_screen->mouseUp(static_cast(id)); } -void CClient::onMouseMove() +void +CClient::onMouseMove() { bool ignore; SInt16 x, y; @@ -658,7 +684,8 @@ void CClient::onMouseMove() } } -void CClient::onMouseWheel() +void +CClient::onMouseWheel() { SInt16 delta; { @@ -669,7 +696,8 @@ void CClient::onMouseWheel() m_screen->mouseWheel(delta); } -void CClient::onErrorIncompatible() +void +CClient::onErrorIncompatible() { SInt32 major, minor; CLock lock(&m_mutex); @@ -677,17 +705,20 @@ void CClient::onErrorIncompatible() log((CLOG_ERR "server has incompatible version %d.%d", major, minor)); } -void CClient::onErrorBusy() +void +CClient::onErrorBusy() { log((CLOG_ERR "server already has a connected client with name \"%s\"", m_name.c_str())); } -void CClient::onErrorUnknown() +void +CClient::onErrorUnknown() { log((CLOG_ERR "server refused client with name \"%s\"", m_name.c_str())); } -void CClient::onErrorBad() +void +CClient::onErrorBad() { log((CLOG_ERR "server disconnected due to a protocol error")); } diff --git a/client/CClient.h b/client/CClient.h index 992d51c9..d2dacc07 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -1,11 +1,10 @@ #ifndef CCLIENT_H #define CCLIENT_H -#include "CMutex.h" -#include "CString.h" -#include "BasicTypes.h" #include "ClipboardTypes.h" #include "IClipboard.h" +#include "CMutex.h" +#include "CString.h" class CNetworkAddress; class IInputStream; diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 4cc19a4f..895ae78f 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -1,27 +1,26 @@ #include "CMSWindowsSecondaryScreen.h" -#include "CMSWindowsClipboard.h" #include "CClient.h" -#include "CPlatform.h" #include "CClipboard.h" -#include "CLock.h" -#include "CLog.h" -#include "CThread.h" +#include "CMSWindowsClipboard.h" +#include "CPlatform.h" #include "XScreen.h" -#include -#include +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" +#include // // CMSWindowsSecondaryScreen // CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : - m_client(NULL), - m_threadID(0), - m_desk(NULL), - m_deskName(), - m_window(NULL), - m_active(false), - m_nextClipboardWindow(NULL) + m_client(NULL), + m_threadID(0), + m_desk(NULL), + m_deskName(), + m_window(NULL), + m_active(false), + m_nextClipboardWindow(NULL) { m_is95Family = CPlatform::isWindows95Family(); @@ -35,7 +34,8 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() assert(m_window == NULL); } -void CMSWindowsSecondaryScreen::run() +void +CMSWindowsSecondaryScreen::run() { // must call run() from same thread as open() assert(m_threadID == GetCurrentThreadId()); @@ -61,12 +61,15 @@ void CMSWindowsSecondaryScreen::run() } } -void CMSWindowsSecondaryScreen::stop() +void +CMSWindowsSecondaryScreen::stop() { doStop(); } -void CMSWindowsSecondaryScreen::open(CClient* client) +void +CMSWindowsSecondaryScreen::open( + CClient* client) { assert(m_client == NULL); assert(client != NULL); @@ -82,15 +85,17 @@ void CMSWindowsSecondaryScreen::open(CClient* client) updateModifiers(); // assume primary has all clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { grabClipboard(id); + } // hide the cursor m_active = true; leave(); } -void CMSWindowsSecondaryScreen::close() +void +CMSWindowsSecondaryScreen::close() { assert(m_client != NULL); @@ -101,8 +106,11 @@ void CMSWindowsSecondaryScreen::close() m_client = NULL; } -void CMSWindowsSecondaryScreen::enter( - SInt32 x, SInt32 y, KeyModifierMask mask) +void +CMSWindowsSecondaryScreen::enter( + SInt32 x, + SInt32 y, + KeyModifierMask mask) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -134,7 +142,8 @@ void CMSWindowsSecondaryScreen::enter( onEnter(x, y); } -void CMSWindowsSecondaryScreen::leave() +void +CMSWindowsSecondaryScreen::leave() { CLock lock(&m_mutex); assert(m_window != NULL); @@ -171,8 +180,10 @@ void CMSWindowsSecondaryScreen::leave() } } -void CMSWindowsSecondaryScreen::keyDown( - KeyID key, KeyModifierMask mask) +void +CMSWindowsSecondaryScreen::keyDown( + KeyID key, + KeyModifierMask mask) { Keystrokes keys; UINT virtualKey; @@ -184,8 +195,9 @@ void CMSWindowsSecondaryScreen::keyDown( // get the sequence of keys to simulate key press and the final // modifier state. m_mask = mapKey(keys, virtualKey, key, mask, kPress); - if (keys.empty()) + if (keys.empty()) { return; + } // generate key events doKeystrokes(keys, 1); @@ -210,8 +222,11 @@ void CMSWindowsSecondaryScreen::keyDown( } } -void CMSWindowsSecondaryScreen::keyRepeat( - KeyID key, KeyModifierMask mask, SInt32 count) +void +CMSWindowsSecondaryScreen::keyRepeat( + KeyID key, + KeyModifierMask mask, + SInt32 count) { Keystrokes keys; UINT virtualKey; @@ -223,15 +238,18 @@ void CMSWindowsSecondaryScreen::keyRepeat( // get the sequence of keys to simulate key repeat and the final // modifier state. m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); - if (keys.empty()) + if (keys.empty()) { return; + } // generate key events doKeystrokes(keys, count); } -void CMSWindowsSecondaryScreen::keyUp( - KeyID key, KeyModifierMask mask) +void +CMSWindowsSecondaryScreen::keyUp( + KeyID key, + KeyModifierMask mask) { Keystrokes keys; UINT virtualKey; @@ -243,8 +261,9 @@ void CMSWindowsSecondaryScreen::keyUp( // get the sequence of keys to simulate key release and the final // modifier state. m_mask = mapKey(keys, virtualKey, key, mask, kRelease); - if (keys.empty()) + if (keys.empty()) { return; + } // generate key events doKeystrokes(keys, 1); @@ -290,7 +309,9 @@ void CMSWindowsSecondaryScreen::keyUp( } } -void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) +void +CMSWindowsSecondaryScreen::mouseDown( + ButtonID button) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -300,11 +321,14 @@ void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) DWORD flags = mapButton(button, true); // send event - if (flags != 0) + if (flags != 0) { mouse_event(flags, 0, 0, 0, 0); + } } -void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) +void +CMSWindowsSecondaryScreen::mouseUp( + ButtonID button) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -314,12 +338,15 @@ void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) DWORD flags = mapButton(button, false); // send event - if (flags != 0) + if (flags != 0) { mouse_event(flags, 0, 0, 0, 0); + } } -void CMSWindowsSecondaryScreen::mouseMove( - SInt32 x, SInt32 y) +void +CMSWindowsSecondaryScreen::mouseMove( + SInt32 x, + SInt32 y) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -333,7 +360,9 @@ void CMSWindowsSecondaryScreen::mouseMove( 0, 0); } -void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) +void +CMSWindowsSecondaryScreen::mouseWheel( + SInt32 delta) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -342,8 +371,10 @@ void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); } -void CMSWindowsSecondaryScreen::setClipboard( - ClipboardID /*id*/, const IClipboard* src) +void +CMSWindowsSecondaryScreen::setClipboard( + ClipboardID /*id*/, + const IClipboard* src) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -352,8 +383,9 @@ void CMSWindowsSecondaryScreen::setClipboard( CClipboard::copy(&dst, src); } -void CMSWindowsSecondaryScreen::grabClipboard( - ClipboardID /*id*/) +void +CMSWindowsSecondaryScreen::grabClipboard( + ClipboardID /*id*/) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -364,8 +396,10 @@ void CMSWindowsSecondaryScreen::grabClipboard( } } -void CMSWindowsSecondaryScreen::getMousePos( - SInt32* x, SInt32* y) const +void +CMSWindowsSecondaryScreen::getMousePos( + SInt32* x, + SInt32* y) const { assert(x != NULL); assert(y != NULL); @@ -385,19 +419,24 @@ void CMSWindowsSecondaryScreen::getMousePos( } } -void CMSWindowsSecondaryScreen::getSize( - SInt32* width, SInt32* height) const +void +CMSWindowsSecondaryScreen::getSize( + SInt32* width, + SInt32* height) const { getScreenSize(width, height); } -SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const +SInt32 +CMSWindowsSecondaryScreen::getJumpZoneSize() const { return 0; } -void CMSWindowsSecondaryScreen::getClipboard( - ClipboardID /*id*/, IClipboard* dst) const +void +CMSWindowsSecondaryScreen::getClipboard( + ClipboardID /*id*/, + IClipboard* dst) const { CLock lock(&m_mutex); assert(m_window != NULL); @@ -406,7 +445,8 @@ void CMSWindowsSecondaryScreen::getClipboard( CClipboard::copy(dst, &src); } -void CMSWindowsSecondaryScreen::onOpenDisplay() +void +CMSWindowsSecondaryScreen::onOpenDisplay() { assert(m_window == NULL); @@ -426,7 +466,8 @@ void CMSWindowsSecondaryScreen::onOpenDisplay() } } -void CMSWindowsSecondaryScreen::onCloseDisplay() +void +CMSWindowsSecondaryScreen::onCloseDisplay() { // disconnect from desktop if (m_is95Family) { @@ -443,7 +484,9 @@ void CMSWindowsSecondaryScreen::onCloseDisplay() assert(m_desk == NULL); } -bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg) +bool +CMSWindowsSecondaryScreen::onPreTranslate( + MSG* msg) { // handle event switch (msg->message) { @@ -466,9 +509,12 @@ bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg) return false; } -LRESULT CMSWindowsSecondaryScreen::onEvent( - HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) +LRESULT +CMSWindowsSecondaryScreen::onEvent( + HWND hwnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) { switch (msg) { case WM_QUERYENDSESSION: @@ -516,10 +562,12 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( return 0; case WM_CHANGECBCHAIN: - if (m_nextClipboardWindow == (HWND)wParam) + if (m_nextClipboardWindow == (HWND)wParam) { m_nextClipboardWindow = (HWND)lParam; - else + } + else { SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + } return 0; case WM_DISPLAYCHANGE: @@ -532,7 +580,10 @@ LRESULT CMSWindowsSecondaryScreen::onEvent( return DefWindowProc(hwnd, msg, wParam, lParam); } -void CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y) +void +CMSWindowsSecondaryScreen::onEnter( + SInt32 x, + SInt32 y) { // warp to requested location SInt32 w, h; @@ -546,7 +597,8 @@ void CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y) ShowWindow(m_window, SW_HIDE); } -void CMSWindowsSecondaryScreen::onLeave() +void +CMSWindowsSecondaryScreen::onLeave() { // move hider window under the mouse (rather than moving the mouse // somewhere else on the screen) @@ -558,7 +610,8 @@ void CMSWindowsSecondaryScreen::onLeave() ShowWindow(m_window, SW_SHOWNORMAL); } -bool CMSWindowsSecondaryScreen::openDesktop() +bool +CMSWindowsSecondaryScreen::openDesktop() { CLock lock(&m_mutex); @@ -584,7 +637,8 @@ bool CMSWindowsSecondaryScreen::openDesktop() return true; } -void CMSWindowsSecondaryScreen::closeDesktop() +void +CMSWindowsSecondaryScreen::closeDesktop() { CLock lock(&m_mutex); @@ -599,7 +653,9 @@ void CMSWindowsSecondaryScreen::closeDesktop() } } -bool CMSWindowsSecondaryScreen::switchDesktop(HDESK desk) +bool +CMSWindowsSecondaryScreen::switchDesktop( + HDESK desk) { CLock lock(&m_mutex); @@ -680,7 +736,8 @@ bool CMSWindowsSecondaryScreen::switchDesktop(HDESK desk) return true; } -void CMSWindowsSecondaryScreen::syncDesktop() const +void +CMSWindowsSecondaryScreen::syncDesktop() const { // note -- mutex must be locked on entry @@ -697,7 +754,8 @@ void CMSWindowsSecondaryScreen::syncDesktop() const AttachThreadInput(threadID, m_threadID, TRUE); } -CString CMSWindowsSecondaryScreen::getCurrentDesktopName() const +CString +CMSWindowsSecondaryScreen::getCurrentDesktopName() const { return m_deskName; } @@ -1189,8 +1247,10 @@ static const UINT* g_mapTable[] = /* 0xfc */ NULL, g_terminal, g_function, g_miscellany }; -DWORD CMSWindowsSecondaryScreen::mapButton( - ButtonID button, bool press) const +DWORD +CMSWindowsSecondaryScreen::mapButton( + ButtonID button, + bool press) const { // map button id to button flag switch (button) { @@ -1208,11 +1268,13 @@ DWORD CMSWindowsSecondaryScreen::mapButton( } } -KeyModifierMask CMSWindowsSecondaryScreen::mapKey( - Keystrokes& keys, - UINT& virtualKey, - KeyID id, KeyModifierMask mask, - EKeyAction action) const +KeyModifierMask +CMSWindowsSecondaryScreen::mapKey( + Keystrokes& keys, + UINT& virtualKey, + KeyID id, + KeyModifierMask mask, + EKeyAction action) const { // lookup the key table const UInt32 mapID = ((id >> 8) & 0xff); @@ -1264,12 +1326,15 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( outMask &= ~KeyModifierShift; // convert system modifier mask to our mask - if (HIBYTE(vk) & 1) + if (HIBYTE(vk) & 1) { outMask |= KeyModifierShift; - if (HIBYTE(vk) & 2) + } + if (HIBYTE(vk) & 2) { outMask |= KeyModifierControl; - if (HIBYTE(vk) & 4) + } + if (HIBYTE(vk) & 4) { outMask |= KeyModifierAlt; + } log((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%04x", code, LOBYTE(vk), outMask)); // handle combination of caps-lock and shift. if caps-lock is @@ -1457,8 +1522,9 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( // add the key event keystroke.m_virtualKey = virtualKey; - if (isExtended) + if (isExtended) { keystroke.m_virtualKey |= 0x100; + } switch (action) { case kPress: keystroke.m_press = true; @@ -1523,12 +1589,15 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey( return mask; } -void CMSWindowsSecondaryScreen::doKeystrokes( - const Keystrokes& keys, SInt32 count) +void +CMSWindowsSecondaryScreen::doKeystrokes( + const Keystrokes& keys, + SInt32 count) { // do nothing if no keys or no repeats - if (count < 1 || keys.empty()) + if (count < 1 || keys.empty()) { return; + } // generate key events for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { @@ -1556,7 +1625,8 @@ void CMSWindowsSecondaryScreen::doKeystrokes( } } -void CMSWindowsSecondaryScreen::updateKeys() +void +CMSWindowsSecondaryScreen::updateKeys() { // clear key state memset(m_keys, 0, sizeof(m_keys)); @@ -1579,29 +1649,40 @@ void CMSWindowsSecondaryScreen::updateKeys() m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); } -void CMSWindowsSecondaryScreen::updateModifiers() +void +CMSWindowsSecondaryScreen::updateModifiers() { // update active modifier mask m_mask = 0; - if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) + if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) { m_mask |= KeyModifierShift; - if ((m_keys[VK_LCONTROL] & 0x80) != 0 || (m_keys[VK_RCONTROL] & 0x80) != 0) + } + if ((m_keys[VK_LCONTROL] & 0x80) != 0 || + (m_keys[VK_RCONTROL] & 0x80) != 0) { m_mask |= KeyModifierControl; - if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) + } + if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) { m_mask |= KeyModifierAlt; - if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) + } + if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) { m_mask |= KeyModifierMeta; - if ((m_keys[VK_CAPITAL] & 0x01) != 0) + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { m_mask |= KeyModifierCapsLock; - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { m_mask |= KeyModifierNumLock; - if ((m_keys[VK_SCROLL] & 0x01) != 0) + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { m_mask |= KeyModifierScrollLock; + } log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); } -void CMSWindowsSecondaryScreen::toggleKey( - UINT virtualKey, KeyModifierMask mask) +void +CMSWindowsSecondaryScreen::toggleKey( + UINT virtualKey, + KeyModifierMask mask) { // send key events to simulate a press and release sendKeyEvent(virtualKey, true); @@ -1612,13 +1693,15 @@ void CMSWindowsSecondaryScreen::toggleKey( m_keys[virtualKey & 0xff] ^= 0x01; } -UINT CMSWindowsSecondaryScreen::virtualKeyToScanCode( - UINT& virtualKey) +UINT +CMSWindowsSecondaryScreen::virtualKeyToScanCode( + UINT& virtualKey) { // try mapping given virtual key UINT code = MapVirtualKey(virtualKey & 0xff, 0); - if (code != 0) + if (code != 0) { return code; + } // no dice. if the virtual key distinguishes between left/right // then try the one that doesn't distinguish sides. windows (or @@ -1663,12 +1746,14 @@ UINT CMSWindowsSecondaryScreen::virtualKeyToScanCode( } } -bool CMSWindowsSecondaryScreen::isExtendedKey( - UINT virtualKey) +bool +CMSWindowsSecondaryScreen::isExtendedKey( + UINT virtualKey) { // see if we've already encoded the extended flag - if ((virtualKey & 0x100) != 0) + if ((virtualKey & 0x100) != 0) { return true; + } // check known virtual keys switch (virtualKey & 0xff) { @@ -1685,14 +1770,18 @@ bool CMSWindowsSecondaryScreen::isExtendedKey( } } -void CMSWindowsSecondaryScreen::sendKeyEvent( - UINT virtualKey, bool press) +void +CMSWindowsSecondaryScreen::sendKeyEvent( + UINT virtualKey, + bool press) { DWORD flags = 0; - if (isExtendedKey(virtualKey)) + if (isExtendedKey(virtualKey)) { flags |= KEYEVENTF_EXTENDEDKEY; - if (!press) + } + if (!press) { flags |= KEYEVENTF_KEYUP; + } const UINT code = virtualKeyToScanCode(virtualKey); keybd_event(static_cast(virtualKey & 0xff), static_cast(code), flags, 0); diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index d8cce84b..176203f2 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -7,7 +7,8 @@ #include "CString.h" #include "stdvector.h" -class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen { +class CMSWindowsSecondaryScreen : public CMSWindowsScreen, + public ISecondaryScreen { public: CMSWindowsSecondaryScreen(); virtual ~CMSWindowsSecondaryScreen(); @@ -18,7 +19,7 @@ public: virtual void open(CClient*); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, - KeyModifierMask mask); + KeyModifierMask mask); virtual void leave(); virtual void keyDown(KeyID, KeyModifierMask); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -68,7 +69,7 @@ private: // key and button queries and operations DWORD mapButton(ButtonID button, bool press) const; KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, - KeyModifierMask, EKeyAction) const; + KeyModifierMask, EKeyAction) const; void doKeystrokes(const Keystrokes&, SInt32 count); void updateKeys(); diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index e3e3aa97..46a6b3f1 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -1,10 +1,9 @@ #include "CXWindowsSecondaryScreen.h" +#include "CClient.h" #include "CXWindowsClipboard.h" #include "CXWindowsUtil.h" -#include "CClient.h" #include "CThread.h" #include "CLog.h" -#include #include #include #define XK_MISCELLANY @@ -17,8 +16,8 @@ // CXWindowsSecondaryScreen::CXWindowsSecondaryScreen() : - m_client(NULL), - m_window(None) + m_client(NULL), + m_window(None) { // do nothing } @@ -28,7 +27,8 @@ CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() assert(m_window == None); } -void CXWindowsSecondaryScreen::run() +void +CXWindowsSecondaryScreen::run() { assert(m_window != None); @@ -41,34 +41,39 @@ void CXWindowsSecondaryScreen::run() // handle event switch (xevent.type) { - case MappingNotify: { - // keyboard mapping changed - CDisplayLock display(this); - XRefreshKeyboardMapping(&xevent.xmapping); - updateKeys(display); - updateKeycodeMap(display); - updateModifierMap(display); - updateModifiers(display); + case MappingNotify: + { + // keyboard mapping changed + CDisplayLock display(this); + XRefreshKeyboardMapping(&xevent.xmapping); + updateKeys(display); + updateKeycodeMap(display); + updateModifierMap(display); + updateModifiers(display); + } break; - } - case LeaveNotify: { - // mouse moved out of hider window somehow. hide the window. - assert(m_window != None); - CDisplayLock display(this); - XUnmapWindow(display, m_window); + case LeaveNotify: + { + // mouse moved out of hider window somehow. hide the window. + assert(m_window != None); + CDisplayLock display(this); + XUnmapWindow(display, m_window); + } break; } - } } } -void CXWindowsSecondaryScreen::stop() +void +CXWindowsSecondaryScreen::stop() { doStop(); } -void CXWindowsSecondaryScreen::open(CClient* client) +void +CXWindowsSecondaryScreen::open( + CClient* client) { assert(m_client == NULL); assert(client != NULL); @@ -84,8 +89,9 @@ void CXWindowsSecondaryScreen::open(CClient* client) CDisplayLock display(this); int majorOpcode, firstEvent, firstError; if (!XQueryExtension(display, XTestExtensionName, - &majorOpcode, &firstEvent, &firstError)) + &majorOpcode, &firstEvent, &firstError)) { throw int(6); // FIXME -- make exception for this + } // update key state updateKeys(display); @@ -102,11 +108,13 @@ void CXWindowsSecondaryScreen::open(CClient* client) m_capsLockHalfDuplex = true; // assume primary has all clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { grabClipboard(id); + } } -void CXWindowsSecondaryScreen::close() +void +CXWindowsSecondaryScreen::close() { assert(m_client != NULL); @@ -117,8 +125,11 @@ void CXWindowsSecondaryScreen::close() m_client = NULL; } -void CXWindowsSecondaryScreen::enter( - SInt32 x, SInt32 y, KeyModifierMask mask) +void +CXWindowsSecondaryScreen::enter( + SInt32 x, + SInt32 y, + KeyModifierMask mask) { assert(m_window != None); @@ -149,14 +160,17 @@ void CXWindowsSecondaryScreen::enter( XSync(display, False); } -void CXWindowsSecondaryScreen::leave() +void +CXWindowsSecondaryScreen::leave() { CDisplayLock display(this); leaveNoLock(display); } -void CXWindowsSecondaryScreen::keyDown( - KeyID key, KeyModifierMask mask) +void +CXWindowsSecondaryScreen::keyDown( + KeyID key, + KeyModifierMask mask) { Keystrokes keys; KeyCode keycode; @@ -164,8 +178,9 @@ void CXWindowsSecondaryScreen::keyDown( // get the sequence of keys to simulate key press and the final // modifier state. m_mask = mapKey(keys, keycode, key, mask, kPress); - if (keys.empty()) + if (keys.empty()) { return; + } // generate key events doKeystrokes(keys, 1); @@ -174,8 +189,11 @@ void CXWindowsSecondaryScreen::keyDown( m_keys[keycode] = true; } -void CXWindowsSecondaryScreen::keyRepeat( - KeyID key, KeyModifierMask mask, SInt32 count) +void +CXWindowsSecondaryScreen::keyRepeat( + KeyID key, + KeyModifierMask mask, + SInt32 count) { Keystrokes keys; KeyCode keycode; @@ -183,15 +201,18 @@ void CXWindowsSecondaryScreen::keyRepeat( // get the sequence of keys to simulate key repeat and the final // modifier state. m_mask = mapKey(keys, keycode, key, mask, kRepeat); - if (keys.empty()) + if (keys.empty()) { return; + } // generate key events doKeystrokes(keys, count); } -void CXWindowsSecondaryScreen::keyUp( - KeyID key, KeyModifierMask mask) +void +CXWindowsSecondaryScreen::keyUp( + KeyID key, + KeyModifierMask mask) { Keystrokes keys; KeyCode keycode; @@ -199,8 +220,9 @@ void CXWindowsSecondaryScreen::keyUp( // get the sequence of keys to simulate key release and the final // modifier state. m_mask = mapKey(keys, keycode, key, mask, kRelease); - if (keys.empty()) + if (keys.empty()) { return; + } // generate key events doKeystrokes(keys, 1); @@ -209,28 +231,37 @@ void CXWindowsSecondaryScreen::keyUp( m_keys[keycode] = false; } -void CXWindowsSecondaryScreen::mouseDown(ButtonID button) +void +CXWindowsSecondaryScreen::mouseDown( + ButtonID button) { CDisplayLock display(this); XTestFakeButtonEvent(display, mapButton(button), True, CurrentTime); XSync(display, False); } -void CXWindowsSecondaryScreen::mouseUp(ButtonID button) +void +CXWindowsSecondaryScreen::mouseUp( + ButtonID button) { CDisplayLock display(this); XTestFakeButtonEvent(display, mapButton(button), False, CurrentTime); XSync(display, False); } -void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +void +CXWindowsSecondaryScreen::mouseMove( + SInt32 x, + SInt32 y) { CDisplayLock display(this); XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime); XSync(display, False); } -void CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) +void +CXWindowsSecondaryScreen::mouseWheel( + SInt32 delta) { // choose button depending on rotation direction const unsigned int button = (delta >= 0) ? 4 : 5; @@ -249,19 +280,25 @@ void CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) XSync(display, False); } -void CXWindowsSecondaryScreen::setClipboard( - ClipboardID id, const IClipboard* clipboard) +void +CXWindowsSecondaryScreen::setClipboard( + ClipboardID id, + const IClipboard* clipboard) { setDisplayClipboard(id, clipboard); } -void CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) +void +CXWindowsSecondaryScreen::grabClipboard( + ClipboardID id) { setDisplayClipboard(id, NULL); } -void CXWindowsSecondaryScreen::getMousePos( - SInt32* x, SInt32* y) const +void +CXWindowsSecondaryScreen::getMousePos( + SInt32* x, + SInt32* y) const { CDisplayLock display(this); int xTmp, yTmp, dummy; @@ -273,25 +310,31 @@ void CXWindowsSecondaryScreen::getMousePos( *y = yTmp; } -void CXWindowsSecondaryScreen::getSize( - SInt32* width, SInt32* height) const +void +CXWindowsSecondaryScreen::getSize( + SInt32* width, + SInt32* height) const { getScreenSize(width, height); } -SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const +SInt32 +CXWindowsSecondaryScreen::getJumpZoneSize() const { return 0; } -void CXWindowsSecondaryScreen::getClipboard( - ClipboardID id, IClipboard* clipboard) const +void +CXWindowsSecondaryScreen::getClipboard( + ClipboardID id, + IClipboard* clipboard) const { getDisplayClipboard(id, clipboard); } -void CXWindowsSecondaryScreen::onOpenDisplay( - Display* display) +void +CXWindowsSecondaryScreen::onOpenDisplay( + Display* display) { assert(m_window == None); @@ -318,15 +361,17 @@ void CXWindowsSecondaryScreen::onOpenDisplay( leaveNoLock(display); } -CXWindowsClipboard* CXWindowsSecondaryScreen::createClipboard( - ClipboardID id) +CXWindowsClipboard* +CXWindowsSecondaryScreen::createClipboard( + ClipboardID id) { CDisplayLock display(this); return new CXWindowsClipboard(display, m_window, id); } -void CXWindowsSecondaryScreen::onCloseDisplay( - Display* display) +void +CXWindowsSecondaryScreen::onCloseDisplay( + Display* display) { assert(m_window != None); @@ -340,14 +385,17 @@ void CXWindowsSecondaryScreen::onCloseDisplay( m_window = None; } -void CXWindowsSecondaryScreen::onLostClipboard( - ClipboardID id) +void +CXWindowsSecondaryScreen::onLostClipboard( + ClipboardID id) { // tell client that the clipboard was grabbed locally m_client->onClipboardChanged(id); } -void CXWindowsSecondaryScreen::leaveNoLock(Display* display) +void +CXWindowsSecondaryScreen::leaveNoLock( + Display* display) { assert(display != NULL); assert(m_window != None); @@ -368,18 +416,21 @@ void CXWindowsSecondaryScreen::leaveNoLock(Display* display) XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0); } -unsigned int CXWindowsSecondaryScreen::mapButton( - ButtonID id) const +unsigned int +CXWindowsSecondaryScreen::mapButton( + ButtonID id) const { // FIXME -- should use button mapping? return static_cast(id); } -KeyModifierMask CXWindowsSecondaryScreen::mapKey( - Keystrokes& keys, - KeyCode& keycode, - KeyID id, KeyModifierMask mask, - EKeyAction action) const +KeyModifierMask +CXWindowsSecondaryScreen::mapKey( + Keystrokes& keys, + KeyCode& keycode, + KeyID id, + KeyModifierMask mask, + EKeyAction action) const { // note -- must have display locked on entry @@ -602,11 +653,12 @@ KeyModifierMask CXWindowsSecondaryScreen::mapKey( return mask; } -bool CXWindowsSecondaryScreen::findKeyCode( - KeyCode& keycode, - unsigned int& maskOut, - KeyID id, - unsigned int maskIn) const +bool +CXWindowsSecondaryScreen::findKeyCode( + KeyCode& keycode, + unsigned int& maskOut, + KeyID id, + unsigned int maskIn) const { // if XK_Tab is requested with shift active then try XK_ISO_Left_Tab // instead. if that doesn't work, we'll fall back to XK_Tab with @@ -734,12 +786,15 @@ bool CXWindowsSecondaryScreen::findKeyCode( return true; } -void CXWindowsSecondaryScreen::doKeystrokes( - const Keystrokes& keys, SInt32 count) +void +CXWindowsSecondaryScreen::doKeystrokes( + const Keystrokes& keys, + SInt32 count) { // do nothing if no keys or no repeats - if (count < 1 || keys.empty()) + if (count < 1 || keys.empty()) { return; + } // lock display CDisplayLock display(this); @@ -774,29 +829,39 @@ void CXWindowsSecondaryScreen::doKeystrokes( XSync(display, False); } -unsigned int CXWindowsSecondaryScreen::maskToX( - KeyModifierMask inMask) const +unsigned int +CXWindowsSecondaryScreen::maskToX( + KeyModifierMask inMask) const { // FIXME -- should be configurable. also not using Mod3Mask. unsigned int outMask = 0; - if (inMask & KeyModifierShift) + if (inMask & KeyModifierShift) { outMask |= ShiftMask; - if (inMask & KeyModifierControl) + } + if (inMask & KeyModifierControl) { outMask |= ControlMask; - if (inMask & KeyModifierAlt) + } + if (inMask & KeyModifierAlt) { outMask |= Mod1Mask; - if (inMask & KeyModifierMeta) + } + if (inMask & KeyModifierMeta) { outMask |= Mod4Mask; - if (inMask & KeyModifierCapsLock) + } + if (inMask & KeyModifierCapsLock) { outMask |= m_capsLockMask; - if (inMask & KeyModifierNumLock) + } + if (inMask & KeyModifierNumLock) { outMask |= m_numLockMask; - if (inMask & KeyModifierScrollLock) + } + if (inMask & KeyModifierScrollLock) { outMask |= m_scrollLockMask; + } return outMask; } -void CXWindowsSecondaryScreen::updateKeys(Display* display) +void +CXWindowsSecondaryScreen::updateKeys( + Display* display) { // ask server which keys are pressed char keys[32]; @@ -815,8 +880,9 @@ void CXWindowsSecondaryScreen::updateKeys(Display* display) } } -void CXWindowsSecondaryScreen::updateModifiers( - Display* display) +void +CXWindowsSecondaryScreen::updateModifiers( + Display* display) { // query the pointer to get the keyboard state Window root, window; @@ -844,8 +910,9 @@ void CXWindowsSecondaryScreen::updateModifiers( } } -void CXWindowsSecondaryScreen::updateKeycodeMap( - Display* display) +void +CXWindowsSecondaryScreen::updateKeycodeMap( + Display* display) { // get the number of keycodes int minKeycode, maxKeycode; @@ -898,8 +965,9 @@ void CXWindowsSecondaryScreen::updateKeycodeMap( XFree(keysyms); } -void CXWindowsSecondaryScreen::updateModifierMap( - Display* display) +void +CXWindowsSecondaryScreen::updateModifierMap( + Display* display) { // get modifier map from server XModifierKeymap* keymap = XGetModifierMapping(display); @@ -927,8 +995,9 @@ void CXWindowsSecondaryScreen::updateModifierMap( m_keycodeToModifier.insert(std::make_pair(keycode, i)); // modifier is enabled if keycode isn't 0 - if (keycode != 0) + if (keycode != 0) { m_modifierMask |= bit; + } // modifier is a toggle if the keysym is a toggle modifier const KeySym keysym = XKeycodeToKeysym(display, keycode, 0); @@ -952,14 +1021,17 @@ void CXWindowsSecondaryScreen::updateModifierMap( XFreeModifiermap(keymap); } -void CXWindowsSecondaryScreen::toggleKey( - Display* display, - KeySym keysym, unsigned int mask) +void +CXWindowsSecondaryScreen::toggleKey( + Display* display, + KeySym keysym, + unsigned int mask) { // lookup the keycode KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym); - if (index == m_keycodeMap.end()) + if (index == m_keycodeMap.end()) { return; + } KeyCode keycode = index->second.m_keycode; // toggle the key @@ -978,7 +1050,9 @@ void CXWindowsSecondaryScreen::toggleKey( m_mask ^= mask; } -bool CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) +bool +CXWindowsSecondaryScreen::isToggleKeysym( + KeySym key) { switch (key) { case XK_Caps_Lock: diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index d4a00a48..77ebf158 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -6,7 +6,8 @@ #include "stdmap.h" #include "stdvector.h" -class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen { +class CXWindowsSecondaryScreen : public CXWindowsScreen, + public ISecondaryScreen { public: CXWindowsSecondaryScreen(); virtual ~CXWindowsSecondaryScreen(); @@ -17,7 +18,7 @@ public: virtual void open(CClient*); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, - KeyModifierMask mask); + KeyModifierMask mask); virtual void leave(); virtual void keyDown(KeyID, KeyModifierMask); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -64,9 +65,9 @@ private: unsigned int mapButton(ButtonID button) const; unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, - KeyModifierMask, EKeyAction) const; + KeyModifierMask, EKeyAction) const; bool findKeyCode(KeyCode&, unsigned int&, - KeyID id, unsigned int) const; + KeyID id, unsigned int) const; void doKeystrokes(const Keystrokes&, SInt32 count); unsigned int maskToX(KeyModifierMask) const; diff --git a/client/client.cpp b/client/client.cpp index c2797ae1..b7c7214c 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -1,17 +1,18 @@ #include "CClient.h" -#include "CString.h" -#include "CLog.h" +#include "CPlatform.h" +#include "ProtocolTypes.h" +#include "Version.h" +#include "CNetwork.h" +#include "CNetworkAddress.h" +#include "XSocket.h" #include "CCondVar.h" #include "CLock.h" #include "CMutex.h" -#include "CNetwork.h" -#include "CNetworkAddress.h" -#include "CPlatform.h" #include "CThread.h" #include "XThread.h" -#include "ProtocolTypes.h" -#include "Version.h" -#include +#include "CLog.h" +#include "CString.h" +#include // platform dependent name of a daemon #if defined(CONFIG_PLATFORM_WIN32) @@ -43,7 +44,10 @@ static CNetworkAddress s_serverAddress; static CMutex* s_logMutex = NULL; -static void logLock(bool lock) +static +void +logLock( + bool lock) { assert(s_logMutex != NULL); @@ -62,7 +66,10 @@ static void logLock(bool lock) static CClient* s_client = NULL; -static int realMain(CMutex* mutex) +static +int +realMain( + CMutex* mutex) { try { // initialize threading library @@ -124,14 +131,18 @@ static int realMain(CMutex* mutex) return 0; } -static int restartMain() +static +int +restartMain() { return realMain(NULL); } // invoke realMain and wait for it. if s_restartable then keep // restarting realMain until it returns a terminate code. -static int restartableMain() +static +int +restartableMain() { if (s_restartable) { CPlatform platform; @@ -151,7 +162,9 @@ static int restartableMain() static void (*bye)(int) = &exit; -static void version() +static +void +version() { log((CLOG_PRINT "%s %d.%d.%d, protocol version %d.%d\n" @@ -165,7 +178,9 @@ static void version() kCopyright)); } -static void help() +static +void +help() { log((CLOG_PRINT "Usage: %s" @@ -213,11 +228,14 @@ static void help() } -static bool isArg(int argi, - int argc, const char** argv, - const char* name1, - const char* name2, - int minRequiredParameters = 0) +static +bool +isArg(int argi, + int argc, + const char** argv, + const char* name1, + const char* name2, + int minRequiredParameters = 0) { if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || (name2 != NULL && strcmp(argv[argi], name2) == 0)) { @@ -234,7 +252,11 @@ static bool isArg(int argi, return false; } -static void parse(int argc, const char** argv) +static +void +parse( + int argc, + const char** argv) { assert(pname != NULL); assert(argv != NULL); @@ -413,7 +435,11 @@ static void parse(int argc, const char** argv) #include "CMSWindowsScreen.h" -static bool logMessageBox(int priority, const char* msg) +static +bool +logMessageBox( + int priority, + const char* msg) { if (priority <= CLog::kFATAL) { MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); @@ -424,18 +450,26 @@ static bool logMessageBox(int priority, const char* msg) } } -static void byeThrow(int x) +static +void +byeThrow(int x) { throw CWin32Platform::CDaemonFailed(x); } -static void daemonStop(void) +static +void +daemonStop(void) { s_client->quit(); } -static int daemonStartup(IPlatform* iplatform, - int argc, const char** argv) +static +int +daemonStartup( + IPlatform* iplatform, + int argc, + const char** argv) { // get platform pointer CWin32Platform* platform = static_cast(iplatform); @@ -456,19 +490,30 @@ static int daemonStartup(IPlatform* iplatform, return platform->runDaemon(realMain, daemonStop); } -static int daemonStartup95(IPlatform*, int, const char**) +static +int +daemonStartup95( + IPlatform*, + int, + const char**) { return realMain(NULL); } -static bool logDiscard(int, const char*) +static +bool +logDiscard( + int, + const char*) { return true; } static bool s_die = false; -static void checkParse(int e) +static +void +checkParse(int e) { // anything over 1 means invalid args. 1 means missing args. // 0 means graceful exit. we plan to exit for anything but @@ -478,7 +523,12 @@ static void checkParse(int e) throw s_die; } -int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +int WINAPI +WinMain( + HINSTANCE instance, + HINSTANCE, + LPSTR, + int) { CPlatform platform; @@ -594,12 +644,20 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) #elif defined(CONFIG_PLATFORM_UNIX) -static int daemonStartup(IPlatform*, int, const char**) +static +int +daemonStartup( + IPlatform*, + int, + const char**) { return restartableMain(); } -int main(int argc, char** argv) +int +main( + int argc, + char** argv) { CPlatform platform; diff --git a/http/CHTTPProtocol.cpp b/http/CHTTPProtocol.cpp index d1bf09e3..3a668f84 100644 --- a/http/CHTTPProtocol.cpp +++ b/http/CHTTPProtocol.cpp @@ -1,12 +1,11 @@ #include "CHTTPProtocol.h" -#include "CLog.h" #include "XHTTP.h" #include "IInputStream.h" #include "IOutputStream.h" +#include "CLog.h" #include "stdsstream.h" -#include -#include -#include +#include +#include #include // @@ -23,8 +22,10 @@ CHTTPRequest::~CHTTPRequest() // do nothing } -void CHTTPRequest::insertHeader( - const CString& name, const CString& value) +void +CHTTPRequest::insertHeader( + const CString& name, + const CString& value) { CHeaderMap::iterator index = m_headerByName.find(name); if (index != m_headerByName.end()) { @@ -37,8 +38,10 @@ void CHTTPRequest::insertHeader( } } -void CHTTPRequest::appendHeader( - const CString& name, const CString& value) +void +CHTTPRequest::appendHeader( + const CString& name, + const CString& value) { CHeaderMap::iterator index = m_headerByName.find(name); if (index != m_headerByName.end()) { @@ -52,7 +55,9 @@ void CHTTPRequest::appendHeader( } } -void CHTTPRequest::eraseHeader(const CString& name) +void +CHTTPRequest::eraseHeader( + const CString& name) { CHeaderMap::iterator index = m_headerByName.find(name); if (index != m_headerByName.end()) { @@ -60,12 +65,16 @@ void CHTTPRequest::eraseHeader(const CString& name) } } -bool CHTTPRequest::isHeader(const CString& name) const +bool +CHTTPRequest::isHeader( + const CString& name) const { return (m_headerByName.find(name) != m_headerByName.end()); } -CString CHTTPRequest::getHeader(const CString& name) const +CString +CHTTPRequest::getHeader( + const CString& name) const { CHeaderMap::const_iterator index = m_headerByName.find(name); if (index != m_headerByName.end()) { @@ -81,8 +90,10 @@ CString CHTTPRequest::getHeader(const CString& name) const // CHTTPProtocol // -CHTTPRequest* CHTTPProtocol::readRequest( - IInputStream* stream, UInt32 maxSize) +CHTTPRequest* +CHTTPProtocol::readRequest( + IInputStream* stream, + UInt32 maxSize) { CString scratch; @@ -229,9 +240,10 @@ CHTTPRequest* CHTTPProtocol::readRequest( return request; } -void CHTTPProtocol::reply( - IOutputStream* stream, - CHTTPReply& reply) +void +CHTTPProtocol::reply( + IOutputStream* stream, + CHTTPReply& reply) { // suppress body for certain replies bool hasBody = true; @@ -308,9 +320,10 @@ void CHTTPProtocol::reply( } } -bool CHTTPProtocol::parseFormData( - const CHTTPRequest& request, - CFormParts& parts) +bool +CHTTPProtocol::parseFormData( + const CHTTPRequest& request, + CFormParts& parts) { static const char formData[] = "multipart/form-data"; static const char boundary[] = "boundary="; @@ -444,9 +457,10 @@ bool CHTTPProtocol::parseFormData( return false; } -CString CHTTPProtocol::readLine( - IInputStream* stream, - CString& tmpBuffer) +CString +CHTTPProtocol::readLine( + IInputStream* stream, + CString& tmpBuffer) { // read up to and including a CRLF from stream, using whatever // is in tmpBuffer as if it were at the head of the stream. @@ -478,10 +492,11 @@ CString CHTTPProtocol::readLine( } } -CString CHTTPProtocol::readBlock( - IInputStream* stream, - UInt32 numBytes, - CString& tmpBuffer) +CString +CHTTPProtocol::readBlock( + IInputStream* stream, + UInt32 numBytes, + CString& tmpBuffer) { CString data; @@ -527,10 +542,11 @@ CString CHTTPProtocol::readBlock( return data; } -CString CHTTPProtocol::readChunk( - IInputStream* stream, - CString& tmpBuffer, - UInt32* maxSize) +CString +CHTTPProtocol::readChunk( + IInputStream* stream, + CString& tmpBuffer, + UInt32* maxSize) { CString line; @@ -577,12 +593,13 @@ CString CHTTPProtocol::readChunk( return data; } -void CHTTPProtocol::readHeaders( - IInputStream* stream, - CHTTPRequest* request, - bool isFooter, - CString& tmpBuffer, - UInt32* maxSize) +void +CHTTPProtocol::readHeaders( + IInputStream* stream, + CHTTPRequest* request, + bool isFooter, + CString& tmpBuffer, + UInt32* maxSize) { // parse headers. done with headers when we get a blank line. CString name; @@ -634,7 +651,9 @@ void CHTTPProtocol::readHeaders( } } -bool CHTTPProtocol::isValidToken(const CString& token) +bool +CHTTPProtocol::isValidToken( + const CString& token) { return (token.find("()<>@,;:\\\"/[]?={} " "\0\1\2\3\4\5\6\7" diff --git a/http/CHTTPProtocol.h b/http/CHTTPProtocol.h index 19818b95..19979ed1 100644 --- a/http/CHTTPProtocol.h +++ b/http/CHTTPProtocol.h @@ -1,8 +1,8 @@ #ifndef CHTTPPROTOCOL_H #define CHTTPPROTOCOL_H -#include "BasicTypes.h" #include "CString.h" +#include "BasicTypes.h" #include "stdlist.h" #include "stdmap.h" #include "stdvector.h" @@ -14,7 +14,7 @@ class CHTTPRequest { public: typedef std::list > CHeaderList; typedef std::map CHeaderMap; + CStringUtil::CaselessCmp> CHeaderMap; typedef CHeaderList::const_iterator const_iterator; CHTTPRequest(); @@ -93,18 +93,18 @@ public: // FIXME -- name/value pairs insufficient to save part headers typedef std::map CFormParts; static bool parseFormData(const CHTTPRequest&, - CFormParts& parts); + CFormParts& parts); private: static CString readLine(IInputStream*, CString& tmpBuffer); static CString readBlock(IInputStream*, - UInt32 numBytes, CString& tmpBuffer); + UInt32 numBytes, CString& tmpBuffer); static CString readChunk(IInputStream*, CString& tmpBuffer, - UInt32* maxSize); + UInt32* maxSize); static void readHeaders(IInputStream*, - CHTTPRequest*, bool isFooter, - CString& tmpBuffer, - UInt32* maxSize); + CHTTPRequest*, bool isFooter, + CString& tmpBuffer, + UInt32* maxSize); static bool isValidToken(const CString&); }; diff --git a/http/XHTTP.cpp b/http/XHTTP.cpp index f069f1da..6232bc03 100644 --- a/http/XHTTP.cpp +++ b/http/XHTTP.cpp @@ -6,18 +6,21 @@ // XHTTP // -XHTTP::XHTTP(SInt32 statusCode) : - XBase(), - m_status(statusCode), - m_reason(getReason(statusCode)) +XHTTP::XHTTP( + SInt32 statusCode) : + XBase(), + m_status(statusCode), + m_reason(getReason(statusCode)) { // do nothing } -XHTTP::XHTTP(SInt32 statusCode, const CString& reasonPhrase) : - XBase(), - m_status(statusCode), - m_reason(reasonPhrase) +XHTTP::XHTTP( + SInt32 statusCode, + const CString& reasonPhrase) : + XBase(), + m_status(statusCode), + m_reason(reasonPhrase) { // do nothing } @@ -27,22 +30,27 @@ XHTTP::~XHTTP() // do nothing } -SInt32 XHTTP::getStatus() const +SInt32 +XHTTP::getStatus() const { return m_status; } -CString XHTTP::getReason() const +CString +XHTTP::getReason() const { return m_reason; } -void XHTTP::addHeaders(CHTTPReply&) const +void +XHTTP::addHeaders( + CHTTPReply&) const { // do nothing } -CString XHTTP::getWhat() const throw() +CString +XHTTP::getWhat() const throw() { try { std::ostringstream s; @@ -60,7 +68,9 @@ CString XHTTP::getWhat() const throw() } } -const char* XHTTP::getReason(SInt32 status) +const char* +XHTTP::getReason( + SInt32 status) { switch (status) { case 300: return "Multiple Choices"; @@ -100,9 +110,10 @@ const char* XHTTP::getReason(SInt32 status) // XHTTPAllow // -XHTTPAllow::XHTTPAllow(const CString& allowed) : - XHTTP(405), - m_allowed(allowed) +XHTTPAllow::XHTTPAllow( + const CString& allowed) : + XHTTP(405), + m_allowed(allowed) { // do nothing } @@ -112,7 +123,9 @@ XHTTPAllow::~XHTTPAllow() // do nothing } -void XHTTPAllow::addHeaders(CHTTPReply& reply) const +void +XHTTPAllow::addHeaders( + CHTTPReply& reply) const { reply.m_headers.push_back(std::make_pair(CString("Allow"), m_allowed)); } diff --git a/http/XHTTP.h b/http/XHTTP.h index 754a345f..e098a713 100644 --- a/http/XHTTP.h +++ b/http/XHTTP.h @@ -2,7 +2,6 @@ #define XHTTP_H #include "BasicTypes.h" -#include "CString.h" #include "XBase.h" class CHTTPReply; diff --git a/io/CBufferedInputStream.cpp b/io/CBufferedInputStream.cpp index 740f7dc6..5078b39d 100644 --- a/io/CBufferedInputStream.cpp +++ b/io/CBufferedInputStream.cpp @@ -4,19 +4,20 @@ #include "CThread.h" #include "IJob.h" #include "XIO.h" -#include -#include +#include // // CBufferedInputStream // -CBufferedInputStream::CBufferedInputStream(CMutex* mutex, IJob* closeCB) : - m_mutex(mutex), - m_empty(mutex, true), - m_closeCB(closeCB), - m_closed(false), - m_hungup(false) +CBufferedInputStream::CBufferedInputStream( + CMutex* mutex, + IJob* closeCB) : + m_mutex(mutex), + m_empty(mutex, true), + m_closeCB(closeCB), + m_closed(false), + m_hungup(false) { assert(m_mutex != NULL); } @@ -26,8 +27,10 @@ CBufferedInputStream::~CBufferedInputStream() delete m_closeCB; } -void CBufferedInputStream::write( - const void* data, UInt32 n) +void +CBufferedInputStream::write( + const void* data, + UInt32 n) { if (!m_hungup && n > 0) { m_buffer.write(data, n); @@ -36,14 +39,17 @@ void CBufferedInputStream::write( } } -void CBufferedInputStream::hangup() +void +CBufferedInputStream::hangup() { m_hungup = true; m_empty.broadcast(); } -UInt32 CBufferedInputStream::readNoLock( - void* dst, UInt32 n) +UInt32 +CBufferedInputStream::readNoLock( + void* dst, + UInt32 n) { if (m_closed) { throw XIOClosed(); @@ -74,12 +80,14 @@ UInt32 CBufferedInputStream::readNoLock( return n; } -UInt32 CBufferedInputStream::getSizeNoLock() const +UInt32 +CBufferedInputStream::getSizeNoLock() const { return m_buffer.getSize(); } -void CBufferedInputStream::close() +void +CBufferedInputStream::close() { CLock lock(m_mutex); if (m_closed) { @@ -95,16 +103,18 @@ void CBufferedInputStream::close() } } -UInt32 CBufferedInputStream::read( - void* dst, UInt32 n) +UInt32 +CBufferedInputStream::read( + void* dst, + UInt32 n) { CLock lock(m_mutex); return readNoLock(dst, n); } -UInt32 CBufferedInputStream::getSize() const +UInt32 +CBufferedInputStream::getSize() const { CLock lock(m_mutex); return getSizeNoLock(); } - diff --git a/io/CBufferedInputStream.h b/io/CBufferedInputStream.h index d1abfea2..f47658f3 100644 --- a/io/CBufferedInputStream.h +++ b/io/CBufferedInputStream.h @@ -1,9 +1,9 @@ #ifndef CBUFFEREDINPUTSTREAM_H #define CBUFFEREDINPUTSTREAM_H +#include "IInputStream.h" #include "CStreamBuffer.h" #include "CCondVar.h" -#include "IInputStream.h" class CMutex; class IJob; diff --git a/io/CBufferedOutputStream.cpp b/io/CBufferedOutputStream.cpp index 3acd168b..386c2b60 100644 --- a/io/CBufferedOutputStream.cpp +++ b/io/CBufferedOutputStream.cpp @@ -1,20 +1,21 @@ #include "CBufferedOutputStream.h" +#include "XIO.h" #include "CLock.h" #include "CMutex.h" #include "CThread.h" #include "IJob.h" -#include "XIO.h" -#include // // CBufferedOutputStream // -CBufferedOutputStream::CBufferedOutputStream(CMutex* mutex, IJob* closeCB) : - m_mutex(mutex), - m_closeCB(closeCB), - m_empty(mutex, true), - m_closed(false) +CBufferedOutputStream::CBufferedOutputStream( + CMutex* mutex, + IJob* closeCB) : + m_mutex(mutex), + m_closeCB(closeCB), + m_empty(mutex, true), + m_closed(false) { assert(m_mutex != NULL); } @@ -24,12 +25,16 @@ CBufferedOutputStream::~CBufferedOutputStream() delete m_closeCB; } -const void* CBufferedOutputStream::peek(UInt32 n) +const void* +CBufferedOutputStream::peek( + UInt32 n) { return m_buffer.peek(n); } -void CBufferedOutputStream::pop(UInt32 n) +void +CBufferedOutputStream::pop( + UInt32 n) { m_buffer.pop(n); if (m_buffer.getSize() == 0) { @@ -37,12 +42,14 @@ void CBufferedOutputStream::pop(UInt32 n) } } -UInt32 CBufferedOutputStream::getSize() const +UInt32 +CBufferedOutputStream::getSize() const { return m_buffer.getSize(); } -void CBufferedOutputStream::close() +void +CBufferedOutputStream::close() { CLock lock(m_mutex); if (m_closed) { @@ -56,8 +63,10 @@ void CBufferedOutputStream::close() } } -UInt32 CBufferedOutputStream::write( - const void* data, UInt32 n) +UInt32 +CBufferedOutputStream::write( + const void* data, + UInt32 n) { CLock lock(m_mutex); if (m_closed) { @@ -68,7 +77,8 @@ UInt32 CBufferedOutputStream::write( return n; } -void CBufferedOutputStream::flush() +void +CBufferedOutputStream::flush() { // wait until all data is written CLock lock(m_mutex); diff --git a/io/CBufferedOutputStream.h b/io/CBufferedOutputStream.h index c609336e..6f0ba6a1 100644 --- a/io/CBufferedOutputStream.h +++ b/io/CBufferedOutputStream.h @@ -1,8 +1,8 @@ #ifndef CBUFFEREDOUTPUTSTREAM_H #define CBUFFEREDOUTPUTSTREAM_H -#include "CStreamBuffer.h" #include "IOutputStream.h" +#include "CStreamBuffer.h" #include "CCondVar.h" class CMutex; diff --git a/io/CInputStreamFilter.cpp b/io/CInputStreamFilter.cpp index 254c2313..276c1a34 100644 --- a/io/CInputStreamFilter.cpp +++ b/io/CInputStreamFilter.cpp @@ -1,13 +1,14 @@ #include "CInputStreamFilter.h" -#include // // CInputStreamFilter // -CInputStreamFilter::CInputStreamFilter(IInputStream* stream, bool adopted) : - m_stream(stream), - m_adopted(adopted) +CInputStreamFilter::CInputStreamFilter( + IInputStream* stream, + bool adopted) : + m_stream(stream), + m_adopted(adopted) { assert(m_stream != NULL); } @@ -19,7 +20,8 @@ CInputStreamFilter::~CInputStreamFilter() } } -IInputStream* CInputStreamFilter::getStream() const +IInputStream* +CInputStreamFilter::getStream() const { return m_stream; } diff --git a/io/COutputStreamFilter.cpp b/io/COutputStreamFilter.cpp index 8adc2862..197d626a 100644 --- a/io/COutputStreamFilter.cpp +++ b/io/COutputStreamFilter.cpp @@ -1,13 +1,14 @@ #include "COutputStreamFilter.h" -#include // // COutputStreamFilter // -COutputStreamFilter::COutputStreamFilter(IOutputStream* stream, bool adopted) : - m_stream(stream), - m_adopted(adopted) +COutputStreamFilter::COutputStreamFilter( + IOutputStream* stream, + bool adopted) : + m_stream(stream), + m_adopted(adopted) { assert(m_stream != NULL); } @@ -19,7 +20,8 @@ COutputStreamFilter::~COutputStreamFilter() } } -IOutputStream* COutputStreamFilter::getStream() const +IOutputStream* +COutputStreamFilter::getStream() const { return m_stream; } diff --git a/io/CStreamBuffer.cpp b/io/CStreamBuffer.cpp index d468847d..fee8546b 100644 --- a/io/CStreamBuffer.cpp +++ b/io/CStreamBuffer.cpp @@ -1,5 +1,4 @@ #include "CStreamBuffer.h" -#include // // CStreamBuffer @@ -7,7 +6,8 @@ const UInt32 CStreamBuffer::kChunkSize = 4096; -CStreamBuffer::CStreamBuffer() : m_size(0) +CStreamBuffer::CStreamBuffer() : + m_size(0) { // do nothing } @@ -17,7 +17,9 @@ CStreamBuffer::~CStreamBuffer() // do nothing } -const void* CStreamBuffer::peek(UInt32 n) +const void* +CStreamBuffer::peek( + UInt32 n) { assert(n <= m_size); @@ -36,7 +38,9 @@ const void* CStreamBuffer::peek(UInt32 n) return reinterpret_cast(head->begin()); } -void CStreamBuffer::pop(UInt32 n) +void +CStreamBuffer::pop( + UInt32 n) { // discard all chunks if n is greater than or equal to m_size if (n >= m_size) { @@ -63,8 +67,10 @@ void CStreamBuffer::pop(UInt32 n) } } -void CStreamBuffer::write( - const void* vdata, UInt32 n) +void +CStreamBuffer::write( + const void* vdata, + UInt32 n) { assert(vdata != NULL); @@ -81,8 +87,9 @@ void CStreamBuffer::write( ChunkList::iterator scan = m_chunks.end(); if (scan != m_chunks.begin()) { --scan; - if (scan->size() >= kChunkSize) + if (scan->size() >= kChunkSize) { ++scan; + } } if (scan == m_chunks.end()) { scan = m_chunks.insert(scan, Chunk()); @@ -109,7 +116,8 @@ void CStreamBuffer::write( } } -UInt32 CStreamBuffer::getSize() const +UInt32 +CStreamBuffer::getSize() const { return m_size; } diff --git a/io/IInputStream.h b/io/IInputStream.h index 700e11a0..f025ed40 100644 --- a/io/IInputStream.h +++ b/io/IInputStream.h @@ -3,7 +3,6 @@ #include "IInterface.h" #include "BasicTypes.h" -#include "XIO.h" class IInputStream : public IInterface { public: diff --git a/io/IOutputStream.h b/io/IOutputStream.h index 688f5af7..301ce482 100644 --- a/io/IOutputStream.h +++ b/io/IOutputStream.h @@ -3,7 +3,6 @@ #include "IInterface.h" #include "BasicTypes.h" -#include "XIO.h" class IOutputStream : public IInterface { public: diff --git a/io/XIO.cpp b/io/XIO.cpp index c51641d4..67508865 100644 --- a/io/XIO.cpp +++ b/io/XIO.cpp @@ -4,12 +4,14 @@ // XIOErrno // -XIOErrno::XIOErrno() : MXErrno() +XIOErrno::XIOErrno() : + MXErrno() { // do nothing } -XIOErrno::XIOErrno(int err) : MXErrno(err) +XIOErrno::XIOErrno(int err) : + MXErrno(err) { // do nothing } @@ -19,7 +21,8 @@ XIOErrno::XIOErrno(int err) : MXErrno(err) // XIOClose // -CString XIOClose::getWhat() const throw() +CString +XIOClose::getWhat() const throw() { return format("XIOClose", "close: %1", XIOErrno::getErrstr()); } @@ -29,7 +32,8 @@ CString XIOClose::getWhat() const throw() // XIOClosed // -CString XIOClosed::getWhat() const throw() +CString +XIOClosed::getWhat() const throw() { return format("XIOClosed", "already closed"); } @@ -39,8 +43,8 @@ CString XIOClosed::getWhat() const throw() // XIOEndOfStream // -CString XIOEndOfStream::getWhat() const throw() +CString +XIOEndOfStream::getWhat() const throw() { return format("XIOEndOfStream", "reached end of stream"); } - diff --git a/io/XIO.h b/io/XIO.h index 743c0aaa..4c4e4315 100644 --- a/io/XIO.h +++ b/io/XIO.h @@ -2,7 +2,6 @@ #define XIO_H #include "XBase.h" -#include "BasicTypes.h" class XIO : public XBase { }; diff --git a/mt/CCondVar.cpp b/mt/CCondVar.cpp index 104332c6..53d70fc8 100644 --- a/mt/CCondVar.cpp +++ b/mt/CCondVar.cpp @@ -1,15 +1,14 @@ #include "CCondVar.h" #include "CStopwatch.h" -#include // // CCondVarBase // CCondVarBase::CCondVarBase(CMutex* mutex) : - m_mutex(mutex) + m_mutex(mutex) #if defined(CONFIG_PLATFORM_WIN32) - , m_waitCountMutex() + , m_waitCountMutex() #endif { assert(m_mutex != NULL); @@ -21,23 +20,27 @@ CCondVarBase::~CCondVarBase() fini(); } -void CCondVarBase::lock() const +void +CCondVarBase::lock() const { m_mutex->lock(); } -void CCondVarBase::unlock() const +void +CCondVarBase::unlock() const { m_mutex->unlock(); } -bool CCondVarBase::wait(double timeout) const +bool +CCondVarBase::wait(double timeout) const { CStopwatch timer(true); return wait(timer, timeout); } -CMutex* CCondVarBase::getMutex() const +CMutex* +CCondVarBase::getMutex() const { return m_mutex; } @@ -47,9 +50,10 @@ CMutex* CCondVarBase::getMutex() const #include "CThread.h" #include #include -#include +#include -void CCondVarBase::init() +void +CCondVarBase::init() { pthread_cond_t* cond = new pthread_cond_t; int status = pthread_cond_init(cond, NULL); @@ -57,7 +61,8 @@ void CCondVarBase::init() m_cond = reinterpret_cast(cond); } -void CCondVarBase::fini() +void +CCondVarBase::fini() { pthread_cond_t* cond = reinterpret_cast(m_cond); int status = pthread_cond_destroy(cond); @@ -65,22 +70,26 @@ void CCondVarBase::fini() delete cond; } -void CCondVarBase::signal() +void +CCondVarBase::signal() { pthread_cond_t* cond = reinterpret_cast(m_cond); int status = pthread_cond_signal(cond); assert(status == 0); } -void CCondVarBase::broadcast() +void +CCondVarBase::broadcast() { pthread_cond_t* cond = reinterpret_cast(m_cond); int status = pthread_cond_broadcast(cond); assert(status == 0); } -bool CCondVarBase::wait( - CStopwatch& timer, double timeout) const +bool +CCondVarBase::wait( + CStopwatch& timer, + double timeout) const { // check timeout against timer if (timeout >= 0.0) { @@ -143,8 +152,9 @@ bool CCondVarBase::wait( CThread::testCancel(); // check wait status - if (status != ETIMEDOUT && status != EINTR) + if (status != ETIMEDOUT && status != EINTR) { break; + } } switch (status) { @@ -180,7 +190,8 @@ bool CCondVarBase::wait( // can cause busy waiting. // -void CCondVarBase::init() +void +CCondVarBase::init() { // prepare events HANDLE* events = new HANDLE[2]; @@ -192,7 +203,8 @@ void CCondVarBase::init() m_waitCount = 0; } -void CCondVarBase::fini() +void +CCondVarBase::fini() { HANDLE* events = reinterpret_cast(m_cond); CloseHandle(events[kSignal]); @@ -200,7 +212,8 @@ void CCondVarBase::fini() delete[] events; } -void CCondVarBase::signal() +void +CCondVarBase::signal() { // is anybody waiting? bool hasWaiter; @@ -210,11 +223,13 @@ void CCondVarBase::signal() } // wake one thread if anybody is waiting - if (hasWaiter) + if (hasWaiter) { SetEvent(reinterpret_cast(m_cond)[kSignal]); + } } -void CCondVarBase::broadcast() +void +CCondVarBase::broadcast() { // is anybody waiting? bool hasWaiter; @@ -224,18 +239,22 @@ void CCondVarBase::broadcast() } // wake all threads if anybody is waiting - if (hasWaiter) + if (hasWaiter) { SetEvent(reinterpret_cast(m_cond)[kBroadcast]); + } } -bool CCondVarBase::wait( - CStopwatch& timer, double timeout) const +bool +CCondVarBase::wait( + CStopwatch& timer, + double timeout) const { // check timeout against timer if (timeout >= 0.0) { timeout -= timer.getTime(); - if (timeout < 0.0) + if (timeout < 0.0) { return false; + } } // prepare to wait @@ -267,8 +286,9 @@ bool CCondVarBase::wait( // cancel takes priority if (n == 3 && result != WAIT_OBJECT_0 + 2 && - WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) + WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) { result = WAIT_OBJECT_0 + 2; + } // update the waiter count and check if we're the last waiter bool last; @@ -279,15 +299,17 @@ bool CCondVarBase::wait( } // reset the broadcast event if we're the last waiter - if (last) + if (last) { ResetEvent(events[kBroadcast]); + } // reacquire the mutex m_mutex->lock(); // cancel thread if necessary - if (result == WAIT_OBJECT_0 + 2) + if (result == WAIT_OBJECT_0 + 2) { currentRep->testCancel(); + } // return success or failure return (result == WAIT_OBJECT_0 + 0 || diff --git a/mt/CCondVar.h b/mt/CCondVar.h index 59b2d90c..cf0f824b 100644 --- a/mt/CCondVar.h +++ b/mt/CCondVar.h @@ -90,17 +90,21 @@ private: template inline -CCondVar::CCondVar(CMutex* mutex, const T& data) : - CCondVarBase(mutex), m_data(data) +CCondVar::CCondVar( + CMutex* mutex, + const T& data) : + CCondVarBase(mutex), + m_data(data) { // do nothing } template inline -CCondVar::CCondVar(const CCondVar& cv) : - CCondVarBase(cv.getMutex()), - m_data(cv.m_data) +CCondVar::CCondVar( + const CCondVar& cv) : + CCondVarBase(cv.getMutex()), + m_data(cv.m_data) { // do nothing } @@ -114,7 +118,9 @@ CCondVar::~CCondVar() template inline -CCondVar& CCondVar::operator=(const CCondVar& cv) +CCondVar& +CCondVar::operator=( + const CCondVar& cv) { m_data = cv.m_data; return *this; @@ -122,7 +128,9 @@ CCondVar& CCondVar::operator=(const CCondVar& cv) template inline -CCondVar& CCondVar::operator=(const T& data) +CCondVar& +CCondVar::operator=( + const T& data) { m_data = data; return *this; diff --git a/mt/CLock.cpp b/mt/CLock.cpp index 41ceae9c..d9f6f7d2 100644 --- a/mt/CLock.cpp +++ b/mt/CLock.cpp @@ -1,17 +1,19 @@ #include "CLock.h" -#include "CMutex.h" #include "CCondVar.h" +#include "CMutex.h" // // CLock // -CLock::CLock(const CMutex* mutex) : m_mutex(mutex) +CLock::CLock(const CMutex* mutex) : + m_mutex(mutex) { m_mutex->lock(); } -CLock::CLock(const CCondVarBase* cv) : m_mutex(cv->getMutex()) +CLock::CLock(const CCondVarBase* cv) : + m_mutex(cv->getMutex()) { m_mutex->lock(); } diff --git a/mt/CLock.h b/mt/CLock.h index d554733f..2044855c 100644 --- a/mt/CLock.h +++ b/mt/CLock.h @@ -1,8 +1,6 @@ #ifndef CLOCK_H #define CLOCK_H -#include "common.h" - class CMutex; class CCondVarBase; diff --git a/mt/CMutex.cpp b/mt/CMutex.cpp index 0318908a..547dfb18 100644 --- a/mt/CMutex.cpp +++ b/mt/CMutex.cpp @@ -1,6 +1,5 @@ #include "CMutex.h" #include "CLog.h" -#include // // CMutex @@ -21,7 +20,9 @@ CMutex::~CMutex() fini(); } -CMutex& CMutex::operator=(const CMutex&) +CMutex& +CMutex::operator=( + const CMutex&) { return *this; } @@ -29,9 +30,10 @@ CMutex& CMutex::operator=(const CMutex&) #if defined(CONFIG_PTHREADS) #include -#include +#include -void CMutex::init() +void +CMutex::init() { pthread_mutex_t* mutex = new pthread_mutex_t; int status = pthread_mutex_init(mutex, NULL); @@ -41,11 +43,8 @@ void CMutex::init() m_mutex = reinterpret_cast(mutex); } -#include -#include -#include -#include -void CMutex::fini() +void +CMutex::fini() { pthread_mutex_t* mutex = reinterpret_cast(m_mutex); int status = pthread_mutex_destroy(mutex); @@ -54,7 +53,8 @@ void CMutex::fini() delete mutex; } -void CMutex::lock() const +void +CMutex::lock() const { pthread_mutex_t* mutex = reinterpret_cast(m_mutex); int status = pthread_mutex_lock(mutex); @@ -78,7 +78,8 @@ void CMutex::lock() const } } -void CMutex::unlock() const +void +CMutex::unlock() const { pthread_mutex_t* mutex = reinterpret_cast(m_mutex); int status = pthread_mutex_unlock(mutex); @@ -105,26 +106,30 @@ void CMutex::unlock() const #define WIN32_LEAN_AND_MEAN #include -void CMutex::init() +void +CMutex::init() { CRITICAL_SECTION* mutex = new CRITICAL_SECTION; InitializeCriticalSection(mutex); m_mutex = reinterpret_cast(mutex); } -void CMutex::fini() +void +CMutex::fini() { CRITICAL_SECTION* mutex = reinterpret_cast(m_mutex); DeleteCriticalSection(mutex); delete mutex; } -void CMutex::lock() const +void +CMutex::lock() const { EnterCriticalSection(reinterpret_cast(m_mutex)); } -void CMutex::unlock() const +void +CMutex::unlock() const { LeaveCriticalSection(reinterpret_cast(m_mutex)); } diff --git a/mt/CMutex.h b/mt/CMutex.h index cf43ea70..2f6b9483 100644 --- a/mt/CMutex.h +++ b/mt/CMutex.h @@ -1,8 +1,6 @@ #ifndef CMUTEX_H #define CMUTEX_H -#include "common.h" - // recursive mutex class class CMutex { public: diff --git a/mt/CThread.cpp b/mt/CThread.cpp index f67573fb..924509f4 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -1,9 +1,9 @@ #include "CThread.h" +#include "CLock.h" #include "CThreadRep.h" #include "XThread.h" -#include "CLock.h" -#include "CStopwatch.h" #include "CLog.h" +#include "CStopwatch.h" // // CThread @@ -14,12 +14,14 @@ CThread::CThread(IJob* job, void* userData) m_rep = new CThreadRep(job, userData); } -CThread::CThread(const CThread& thread) : m_rep(thread.m_rep) +CThread::CThread(const CThread& thread) : + m_rep(thread.m_rep) { m_rep->ref(); } -CThread::CThread(CThreadRep* rep) : m_rep(rep) +CThread::CThread(CThreadRep* rep) : + m_rep(rep) { // do nothing. rep should have already been Ref()'d. } @@ -29,7 +31,9 @@ CThread::~CThread() m_rep->unref(); } -CThread& CThread::operator=(const CThread& thread) +CThread& +CThread::operator=( + const CThread& thread) { if (thread.m_rep != m_rep) { m_rep->unref(); @@ -39,12 +43,15 @@ CThread& CThread::operator=(const CThread& thread) return *this; } -void CThread::init() +void +CThread::init() { CThreadRep::initThreads(); } -void CThread::sleep(double timeout) +void +CThread::sleep( + double timeout) { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); if (timeout >= 0.0) { @@ -54,47 +61,59 @@ void CThread::sleep(double timeout) currentRep->testCancel(); } -void CThread::exit(void* result) +void +CThread::exit( + void* result) { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); log((CLOG_DEBUG1 "throw exit on thread %p", currentRep.operator->())); throw XThreadExit(result); } -bool CThread::enableCancel(bool enable) +bool +CThread::enableCancel( + bool enable) { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); return currentRep->enableCancel(enable); } -void CThread::cancel() +void +CThread::cancel() { m_rep->cancel(); } -void CThread::setPriority(int n) +void +CThread::setPriority( + int n) { m_rep->setPriority(n); } -CThread CThread::getCurrentThread() +CThread +CThread::getCurrentThread() { return CThread(CThreadRep::getCurrentThreadRep()); } -bool CThread::wait(double timeout) const +bool +CThread::wait( + double timeout) const { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); return currentRep->wait(m_rep, timeout); } -void CThread::testCancel() +void +CThread::testCancel() { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); currentRep->testCancel(); } -void* CThread::getResult() const +void* +CThread::getResult() const { if (wait()) return m_rep->getResult(); @@ -102,18 +121,23 @@ void* CThread::getResult() const return NULL; } -void* CThread::getUserData() +void* +CThread::getUserData() { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); return currentRep->getUserData(); } -bool CThread::operator==(const CThread& thread) const +bool +CThread::operator==( + const CThread& thread) const { return (m_rep == thread.m_rep); } -bool CThread::operator!=(const CThread& thread) const +bool +CThread::operator!=( + const CThread& thread) const { return (m_rep != thread.m_rep); } @@ -123,7 +147,8 @@ bool CThread::operator!=(const CThread& thread) const // CThreadMaskCancel // -CThreadMaskCancel::CThreadMaskCancel() : m_old(CThread::enableCancel(false)) +CThreadMaskCancel::CThreadMaskCancel() : + m_old(CThread::enableCancel(false)) { // do nothing } diff --git a/mt/CThread.h b/mt/CThread.h index ae49838c..03dc3fd3 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -1,8 +1,6 @@ #ifndef CTHREAD_H #define CTHREAD_H -#include "BasicTypes.h" - class IJob; class CThreadRep; diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index aa641b50..06af394a 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -1,11 +1,10 @@ #include "CThreadRep.h" -#include "CThread.h" -#include "CMutex.h" #include "CLock.h" +#include "CMutex.h" +#include "CThread.h" #include "XThread.h" #include "CLog.h" #include "IJob.h" -#include #if defined(CONFIG_PTHREADS) #include @@ -23,7 +22,7 @@ class XThreadUnavailable { }; #if defined(CONFIG_PLATFORM_UNIX) && !defined(NDEBUG) -#include +#include #include #include #include @@ -44,11 +43,12 @@ CThreadRep* CThreadRep::s_head = NULL; pthread_t CThreadRep::s_signalThread; #endif -CThreadRep::CThreadRep() : m_prev(NULL), - m_next(NULL), - m_refCount(1), - m_job(NULL), - m_userData(NULL) +CThreadRep::CThreadRep() : + m_prev(NULL), + m_next(NULL), + m_refCount(1), + m_job(NULL), + m_userData(NULL) { // note -- s_mutex must be locked on entry assert(s_mutex != NULL); @@ -73,11 +73,11 @@ CThreadRep::CThreadRep() : m_prev(NULL), } CThreadRep::CThreadRep(IJob* job, void* userData) : - m_prev(NULL), - m_next(NULL), - m_refCount(2), // 1 for us, 1 for thread - m_job(job), - m_userData(userData) + m_prev(NULL), + m_next(NULL), + m_refCount(2), // 1 for us, 1 for thread + m_job(job), + m_userData(userData) { assert(m_job != NULL); assert(s_mutex != NULL); @@ -103,15 +103,17 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); int status = pthread_create(&m_thread, NULL, threadFunc, (void*)this); pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); - if (status != 0) + if (status != 0) { throw XThreadUnavailable(); + } #elif defined(CONFIG_PLATFORM_WIN32) unsigned int id; m_thread = reinterpret_cast(_beginthreadex(NULL, 0, threadFunc, (void*)this, 0, &id)); m_id = static_cast(id); - if (m_thread == 0) + if (m_thread == 0) { throw XThreadUnavailable(); + } #endif // insert ourself into linked list @@ -143,7 +145,8 @@ CThreadRep::~CThreadRep() fini(); } -void CThreadRep::initThreads() +void +CThreadRep::initThreads() { if (s_mutex == NULL) { s_mutex = new CMutex; @@ -197,13 +200,15 @@ void CThreadRep::initThreads() } } -void CThreadRep::ref() +void +CThreadRep::ref() { CLock lock(s_mutex); ++m_refCount; } -void CThreadRep::unref() +void +CThreadRep::unref() { CLock lock(s_mutex); if (--m_refCount == 0) { @@ -211,7 +216,8 @@ void CThreadRep::unref() } } -bool CThreadRep::enableCancel(bool enable) +bool +CThreadRep::enableCancel(bool enable) { CLock lock(s_mutex); const bool old = m_cancellable; @@ -219,25 +225,29 @@ bool CThreadRep::enableCancel(bool enable) return old; } -bool CThreadRep::isCancellable() const +bool +CThreadRep::isCancellable() const { CLock lock(s_mutex); return (m_cancellable && !m_cancelling); } -void* CThreadRep::getResult() const +void* +CThreadRep::getResult() const { // no lock necessary since thread isn't running return m_result; } -void* CThreadRep::getUserData() const +void* +CThreadRep::getUserData() const { // no lock necessary because the value never changes return m_userData; } -CThreadRep* CThreadRep::getCurrentThreadRep() +CThreadRep* +CThreadRep::getCurrentThreadRep() { assert(s_mutex != NULL); @@ -276,7 +286,8 @@ CThreadRep* CThreadRep::getCurrentThreadRep() return scan; } -void CThreadRep::doThreadFunc() +void +CThreadRep::doThreadFunc() { // default priority is slightly below normal setPriority(1); @@ -318,9 +329,10 @@ void CThreadRep::doThreadFunc() #if defined(CONFIG_PTHREADS) #include "CStopwatch.h" -#include +#include -void CThreadRep::init() +void +CThreadRep::init() { m_result = NULL; m_cancellable = true; @@ -329,7 +341,8 @@ void CThreadRep::init() m_exit = false; } -void CThreadRep::fini() +void +CThreadRep::fini() { // main thread has NULL job if (m_job != NULL) { @@ -337,10 +350,13 @@ void CThreadRep::fini() } } -void CThreadRep::sleep(double timeout) +void +CThreadRep::sleep( + double timeout) { - if (timeout < 0.0) + if (timeout < 0.0) { return; + } struct timespec t; t.tv_sec = (long)timeout; t.tv_nsec = (long)(1000000000.0 * (timeout - (double)t.tv_sec)); @@ -348,7 +364,8 @@ void CThreadRep::sleep(double timeout) testCancel(); } -void CThreadRep::cancel() +void +CThreadRep::cancel() { CLock lock(s_mutex); if (m_cancellable && !m_cancelling) { @@ -363,14 +380,16 @@ void CThreadRep::cancel() pthread_kill(m_thread, SIGWAKEUP); } -void CThreadRep::testCancel() +void +CThreadRep::testCancel() { { CLock lock(s_mutex); // done if not cancelled, not cancellable, or already cancelling - if (!m_cancel || !m_cancellable || m_cancelling) + if (!m_cancel || !m_cancellable || m_cancelling) { return; + } // update state for cancel m_cancel = false; @@ -382,40 +401,50 @@ void CThreadRep::testCancel() throw XThreadCancel(); } -bool CThreadRep::wait(CThreadRep* target, double timeout) +bool +CThreadRep::wait( + CThreadRep* target, + double timeout) { - if (target == this) + if (target == this) { return false; + } testCancel(); - if (target->isExited()) + if (target->isExited()) { return true; + } if (timeout != 0.0) { CStopwatch timer; do { sleep(0.05); testCancel(); - if (target->isExited()) + if (target->isExited()) { return true; + } } while (timeout < 0.0 || timer.getTime() <= timeout); } return false; } -void CThreadRep::setPriority(int) +void +CThreadRep::setPriority( + int) { // FIXME } -bool CThreadRep::isExited() const +bool +CThreadRep::isExited() const { CLock lock(s_mutex); return m_exit; } -void* CThreadRep::threadFunc(void* arg) +void* +CThreadRep::threadFunc(void* arg) { CThreadRep* rep = (CThreadRep*)arg; @@ -438,12 +467,16 @@ void* CThreadRep::threadFunc(void* arg) return NULL; } -void CThreadRep::threadCancel(int) +void +CThreadRep::threadCancel( + int) { // do nothing } -void* CThreadRep::threadSignalHandler(void* vrep) +void* +CThreadRep::threadSignalHandler( + void* vrep) { CThreadRep* mainThreadRep = reinterpret_cast(vrep); @@ -466,7 +499,8 @@ void* CThreadRep::threadSignalHandler(void* vrep) #elif defined(CONFIG_PLATFORM_WIN32) -void CThreadRep::init() +void +CThreadRep::init() { m_result = NULL; m_cancellable = true; @@ -475,7 +509,8 @@ void CThreadRep::init() m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); } -void CThreadRep::fini() +void +CThreadRep::fini() { // destroy the events CloseHandle(m_cancel); @@ -487,26 +522,33 @@ void CThreadRep::fini() } } -void CThreadRep::sleep(double timeout) +void +CThreadRep::sleep( + double timeout) { - if (isCancellable()) + if (isCancellable()) { WaitForSingleObject(m_cancel, (DWORD)(1000.0 * timeout)); - else + } + else { Sleep((DWORD)(1000.0 * timeout)); + } } -void CThreadRep::cancel() +void +CThreadRep::cancel() { log((CLOG_DEBUG1 "cancel thread %p", this)); SetEvent(m_cancel); } -void CThreadRep::testCancel() +void +CThreadRep::testCancel() { // poll cancel event. return if not set. const DWORD result = WaitForSingleObject(getCancelEvent(), 0); - if (result != WAIT_OBJECT_0) + if (result != WAIT_OBJECT_0) { return; + } { // ignore if disabled or already cancelling @@ -524,23 +566,29 @@ void CThreadRep::testCancel() throw XThreadCancel(); } -bool CThreadRep::wait(CThreadRep* target, double timeout) +bool +CThreadRep::wait( + CThreadRep* target, + double timeout) { // get the current thread. if it's the same as the target thread // then the thread is waiting on itself. CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - if (target == this) + if (target == this) { return false; + } // is cancellation enabled? const DWORD n = (isCancellable() ? 2 : 1); // convert timeout DWORD t; - if (timeout < 0.0) + if (timeout < 0.0) { t = INFINITE; - else + } + else { t = (DWORD)(1000.0 * timeout); + } // wait for this thread to be cancelled or for the target thread to // terminate. @@ -551,8 +599,9 @@ bool CThreadRep::wait(CThreadRep* target, double timeout) // cancel takes priority if (n == 2 && result != WAIT_OBJECT_0 + 1 && - WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) + WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) { result = WAIT_OBJECT_0 + 1; + } // handle result switch (result) { @@ -570,7 +619,9 @@ bool CThreadRep::wait(CThreadRep* target, double timeout) } } -void CThreadRep::setPriority(int n) +void +CThreadRep::setPriority( + int n) { DWORD pClass = NORMAL_PRIORITY_CLASS; if (n < 0) { @@ -601,19 +652,23 @@ void CThreadRep::setPriority(int n) SetThreadPriority(m_thread, n); } -HANDLE CThreadRep::getExitEvent() const +HANDLE +CThreadRep::getExitEvent() const { // no lock necessary because the value never changes return m_exit; } -HANDLE CThreadRep::getCancelEvent() const +HANDLE +CThreadRep::getCancelEvent() const { // no lock necessary because the value never changes return m_cancel; } -unsigned int __stdcall CThreadRep::threadFunc(void* arg) +unsigned int __stdcall +CThreadRep::threadFunc( + void* arg) { CThreadRep* rep = (CThreadRep*)arg; diff --git a/mt/CTimerThread.cpp b/mt/CTimerThread.cpp index 786ed8df..b47cc429 100644 --- a/mt/CTimerThread.cpp +++ b/mt/CTimerThread.cpp @@ -2,7 +2,6 @@ #include "CThread.h" #include "TMethodJob.h" #include "CLog.h" -#include // // CTimerThread @@ -33,7 +32,9 @@ CTimerThread::~CTimerThread() } } -void CTimerThread::timer(void*) +void +CTimerThread::timer( + void*) { log((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); CThread::sleep(m_timeout); diff --git a/mt/CTimerThread.h b/mt/CTimerThread.h index 118fde1d..5605fadc 100644 --- a/mt/CTimerThread.h +++ b/mt/CTimerThread.h @@ -1,8 +1,6 @@ #ifndef CTIMERTHREAD_H #define CTIMERTHREAD_H -#include "common.h" - class CThread; class CTimerThread { diff --git a/mt/XThread.h b/mt/XThread.h index bc56a44c..18c22f09 100644 --- a/mt/XThread.h +++ b/mt/XThread.h @@ -1,8 +1,6 @@ #ifndef XTHREAD_H #define XTHREAD_H -#include "common.h" - // generic thread exception class XThread { }; diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp index 4037d9d9..23cc9701 100644 --- a/net/CNetwork.cpp +++ b/net/CNetwork.cpp @@ -1,7 +1,6 @@ #include "CNetwork.h" #include "XNetwork.h" #include "CLog.h" -#include // // CNetwork @@ -51,15 +50,21 @@ const CNetwork::Socket CNetwork::Null = INVALID_SOCKET; static HMODULE s_networkModule = NULL; -static FARPROC netGetProcAddress(HMODULE module, LPCSTR name) +static +FARPROC +netGetProcAddress( + HMODULE module, + LPCSTR name) { FARPROC func = ::GetProcAddress(module, name); - if (!func) + if (!func) { throw XNetworkFunctionUnavailable(name); + } return func; } -void CNetwork::init() +void +CNetwork::init() { assert(WSACleanup == NULL); assert(s_networkModule == NULL); @@ -98,7 +103,8 @@ void CNetwork::init() throw XNetworkUnavailable(); } -void CNetwork::cleanup() +void +CNetwork::cleanup() { if (s_networkModule != NULL) { WSACleanup(); @@ -109,41 +115,55 @@ void CNetwork::cleanup() } } -UInt32 CNetwork::swaphtonl(UInt32 v) +UInt32 +CNetwork::swaphtonl( + UInt32 v) { static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; - if (s_endian.b[0] == 0x34) + if (s_endian.b[0] == 0x34) { return ((v & 0xff000000lu) >> 24) | ((v & 0x00ff0000lu) >> 8) | ((v & 0x0000ff00lu) << 8) | ((v & 0x000000fflu) << 24); - else + } + else { return v; + } } -UInt16 CNetwork::swaphtons(UInt16 v) +UInt16 +CNetwork::swaphtons( + UInt16 v) { static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; - if (s_endian.b[0] == 0x34) + if (s_endian.b[0] == 0x34) { return static_cast( ((v & 0xff00u) >> 8) | ((v & 0x00ffu) << 8)); - else + } + else { return v; + } } -UInt32 CNetwork::swapntohl(UInt32 v) +UInt32 +CNetwork::swapntohl( + UInt32 v) { return swaphtonl(v); } -UInt16 CNetwork::swapntohs(UInt16 v) +UInt16 +CNetwork::swapntohs( + UInt16 v) { return swaphtons(v); } #define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name) -void CNetwork::init2(HMODULE module) +void +CNetwork::init2( + HMODULE module) { assert(module != NULL); @@ -155,10 +175,12 @@ void CNetwork::init2(HMODULE module) WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); WSADATA data; int err = startup(version, &data); - if (data.wVersion != version) + if (data.wVersion != version) { throw XNetworkVersion(LOBYTE(data.wVersion), HIBYTE(data.wVersion)); - if (err != 0) + } + if (err != 0) { throw XNetworkFailed(); + } // get function addresses setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); @@ -198,7 +220,11 @@ void CNetwork::init2(HMODULE module) s_networkModule = module; } -int PASCAL FAR CNetwork::poll2(PollEntry fd[], int nfds, int timeout) +int PASCAL FAR +CNetwork::poll2( + PollEntry fd[], + int nfds, + int timeout) { int i; @@ -241,32 +267,45 @@ int PASCAL FAR CNetwork::poll2(PollEntry fd[], int nfds, int timeout) int n = select(0, readSetP, writeSetP, errSetP, timeout2P); // handle results - if (n == Error) + if (n == Error) { return Error; - if (n == 0) + } + if (n == 0) { return 0; + } n = 0; for (i = 0; i < nfds; ++i) { fd[i].revents = 0; - if (FD_ISSET(fd[i].fd, &readSet)) + if (FD_ISSET(fd[i].fd, &readSet)) { fd[i].revents |= kPOLLIN; - if (FD_ISSET(fd[i].fd, &writeSet)) + } + if (FD_ISSET(fd[i].fd, &writeSet)) { fd[i].revents |= kPOLLOUT; - if (FD_ISSET(fd[i].fd, &errSet)) + } + if (FD_ISSET(fd[i].fd, &errSet)) { fd[i].revents |= kPOLLERR; - if (fd[i].revents != 0) + } + if (fd[i].revents != 0) { ++n; + } } return n; } -ssize_t PASCAL FAR CNetwork::read2(Socket s, void FAR * buf, size_t len) +ssize_t PASCAL FAR +CNetwork::read2( + Socket s, + void FAR* buf, + size_t len) { return recv(s, buf, len, 0); } -ssize_t PASCAL FAR CNetwork::write2(Socket s, - const void FAR * buf, size_t len) +ssize_t PASCAL FAR +CNetwork::write2( + Socket s, + const void FAR* buf, + size_t len) { return send(s, buf, len, 0); } @@ -283,37 +322,53 @@ ssize_t PASCAL FAR CNetwork::write2(Socket s, #define setfunc(var, name, type) var = (type)::name -UInt32 CNetwork::swaphtonl(UInt32 v) +UInt32 +CNetwork::swaphtonl( + UInt32 v) { return htonl(v); } -UInt16 CNetwork::swaphtons(UInt16 v) +UInt16 +CNetwork::swaphtons( + UInt16 v) { return htons(v); } -UInt32 CNetwork::swapntohl(UInt32 v) +UInt32 +CNetwork::swapntohl( + UInt32 v) { return ntohl(v); } -UInt16 CNetwork::swapntohs(UInt16 v) +UInt16 +CNetwork::swapntohs( + UInt16 v) { return ntohs(v); } -static int myerrno() +static +int +myerrno() { return errno; } -static int myherrno() +static +int +myherrno() { return h_errno; } -static int mygethostname(char* name, int namelen) +static +int +mygethostname( + char* name, + int namelen) { return gethostname(name, namelen); } @@ -321,7 +376,8 @@ static int mygethostname(char* name, int namelen) const int CNetwork::Error = -1; const CNetwork::Socket CNetwork::Null = -1; -void CNetwork::init() +void +CNetwork::init() { setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); @@ -355,7 +411,8 @@ void CNetwork::init() setfunc(gethosterror, myherrno, int (PASCAL FAR *)(void)); } -void CNetwork::cleanup() +void +CNetwork::cleanup() { // do nothing } diff --git a/net/CNetworkAddress.cpp b/net/CNetworkAddress.cpp index 0e07605e..28f20537 100644 --- a/net/CNetworkAddress.cpp +++ b/net/CNetworkAddress.cpp @@ -1,12 +1,14 @@ #include "CNetworkAddress.h" -#include "CString.h" -#include +#include "XSocket.h" +#include +#include // // CNetworkAddress // -CNetworkAddress::CNetworkAddress() : m_port(0) +CNetworkAddress::CNetworkAddress() : + m_port(0) { // note -- make no calls to CNetwork socket interface here; // we're often called prior to CNetwork::init(). @@ -19,7 +21,9 @@ CNetworkAddress::CNetworkAddress() : m_port(0) memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } -CNetworkAddress::CNetworkAddress(UInt16 port) : m_port(port) +CNetworkAddress::CNetworkAddress( + UInt16 port) : + m_port(port) { if (port == 0) { throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, m_port); @@ -33,9 +37,11 @@ CNetworkAddress::CNetworkAddress(UInt16 port) : m_port(port) memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } -CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : - m_hostname(hostname_), - m_port(port) +CNetworkAddress::CNetworkAddress( + const CString& hostname_, + UInt16 port) : + m_hostname(hostname_), + m_port(port) { CString hostname(m_hostname); @@ -126,27 +132,32 @@ CNetworkAddress::~CNetworkAddress() // do nothing } -bool CNetworkAddress::isValid() const +bool +CNetworkAddress::isValid() const { return (m_port != 0); } -const CNetwork::Address* CNetworkAddress::getAddress() const +const CNetwork::Address* +CNetworkAddress::getAddress() const { return &m_address; } -CNetwork::AddressLength CNetworkAddress::getAddressLength() const +CNetwork::AddressLength +CNetworkAddress::getAddressLength() const { return sizeof(m_address); } -CString CNetworkAddress::getHostname() const +CString +CNetworkAddress::getHostname() const { return m_hostname; } -UInt16 CNetworkAddress::getPort() const +UInt16 +CNetworkAddress::getPort() const { return m_port; } diff --git a/net/CNetworkAddress.h b/net/CNetworkAddress.h index 08750e29..1fd60fd4 100644 --- a/net/CNetworkAddress.h +++ b/net/CNetworkAddress.h @@ -2,7 +2,6 @@ #define CNETWORKADDRESS_H #include "CNetwork.h" -#include "XSocket.h" #include "CString.h" #include "BasicTypes.h" diff --git a/net/CTCPListenSocket.cpp b/net/CTCPListenSocket.cpp index 28e254c9..f7f2266e 100644 --- a/net/CTCPListenSocket.cpp +++ b/net/CTCPListenSocket.cpp @@ -1,6 +1,8 @@ #include "CTCPListenSocket.h" #include "CTCPSocket.h" #include "CNetworkAddress.h" +#include "XIO.h" +#include "XSocket.h" #include "CThread.h" // @@ -25,8 +27,9 @@ CTCPListenSocket::~CTCPListenSocket() } } -void CTCPListenSocket::bind( - const CNetworkAddress& addr) +void +CTCPListenSocket::bind( + const CNetworkAddress& addr) { if (CNetwork::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == CNetwork::Error) { @@ -40,7 +43,8 @@ void CTCPListenSocket::bind( } } -ISocket* CTCPListenSocket::accept() +ISocket* +CTCPListenSocket::accept() { CNetwork::PollEntry pfds[1]; pfds[0].fd = m_fd; @@ -59,7 +63,8 @@ ISocket* CTCPListenSocket::accept() } } -void CTCPListenSocket::close() +void +CTCPListenSocket::close() { if (m_fd == CNetwork::Null) { throw XIOClosed(); diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index 3429c9f8..fdc640f3 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -2,13 +2,14 @@ #include "CBufferedInputStream.h" #include "CBufferedOutputStream.h" #include "CNetworkAddress.h" +#include "XIO.h" +#include "XSocket.h" +#include "CCondVar.h" #include "CLock.h" #include "CMutex.h" -#include "CCondVar.h" #include "CThread.h" -#include "TMethodJob.h" #include "CStopwatch.h" -#include +#include "TMethodJob.h" // // CTCPSocket @@ -23,7 +24,8 @@ CTCPSocket::CTCPSocket() init(); } -CTCPSocket::CTCPSocket(CNetwork::Socket fd) : m_fd(fd) +CTCPSocket::CTCPSocket(CNetwork::Socket fd) : + m_fd(fd) { assert(m_fd != CNetwork::Null); @@ -52,7 +54,9 @@ CTCPSocket::~CTCPSocket() delete m_mutex; } -void CTCPSocket::bind(const CNetworkAddress& addr) +void +CTCPSocket::bind( + const CNetworkAddress& addr) { if (CNetwork::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == CNetwork::Error) { @@ -63,7 +67,9 @@ void CTCPSocket::bind(const CNetworkAddress& addr) } } -void CTCPSocket::connect(const CNetworkAddress& addr) +void +CTCPSocket::connect( + const CNetworkAddress& addr) { CThread::testCancel(); if (CNetwork::connect(m_fd, addr.getAddress(), @@ -78,7 +84,8 @@ void CTCPSocket::connect(const CNetworkAddress& addr) this, &CTCPSocket::ioThread)); } -void CTCPSocket::close() +void +CTCPSocket::close() { // see if buffers should be flushed bool doFlush = false; @@ -117,17 +124,20 @@ void CTCPSocket::close() } } -IInputStream* CTCPSocket::getInputStream() +IInputStream* +CTCPSocket::getInputStream() { return m_input; } -IOutputStream* CTCPSocket::getOutputStream() +IOutputStream* +CTCPSocket::getOutputStream() { return m_output; } -void CTCPSocket::init() +void +CTCPSocket::init() { m_mutex = new CMutex; m_thread = NULL; @@ -146,7 +156,8 @@ void CTCPSocket::init() CNetwork::setsockopt(m_fd, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); } -void CTCPSocket::ioThread(void*) +void +CTCPSocket::ioThread(void*) { try { ioService(); @@ -158,7 +169,8 @@ void CTCPSocket::ioThread(void*) } } -void CTCPSocket::ioCleanup() +void +CTCPSocket::ioCleanup() { try { m_input->close(); @@ -174,7 +186,8 @@ void CTCPSocket::ioCleanup() } } -void CTCPSocket::ioService() +void +CTCPSocket::ioService() { assert(m_fd != CNetwork::Null); @@ -249,14 +262,16 @@ void CTCPSocket::ioService() } } -void CTCPSocket::closeInput(void*) +void +CTCPSocket::closeInput(void*) { // note -- m_mutex should already be locked CNetwork::shutdown(m_fd, 0); m_connected &= ~kRead; } -void CTCPSocket::closeOutput(void*) +void +CTCPSocket::closeOutput(void*) { // note -- m_mutex should already be locked CNetwork::shutdown(m_fd, 1); diff --git a/net/CTCPSocket.h b/net/CTCPSocket.h index 3e7913b2..980e0dec 100644 --- a/net/CTCPSocket.h +++ b/net/CTCPSocket.h @@ -3,7 +3,6 @@ #include "ISocket.h" #include "CNetwork.h" -#include "XThread.h" class CMutex; template diff --git a/net/IListenSocket.h b/net/IListenSocket.h index f62f2239..dd454976 100644 --- a/net/IListenSocket.h +++ b/net/IListenSocket.h @@ -2,8 +2,6 @@ #define ILISTENSOCKET_H #include "IInterface.h" -#include "XIO.h" -#include "XSocket.h" class CNetworkAddress; class ISocket; diff --git a/net/ISocket.h b/net/ISocket.h index 0a28ccc7..6c853b59 100644 --- a/net/ISocket.h +++ b/net/ISocket.h @@ -2,9 +2,6 @@ #define ISOCKET_H #include "IInterface.h" -#include "BasicTypes.h" -#include "XSocket.h" -#include "XIO.h" class CNetworkAddress; class IInputStream; diff --git a/net/Makefile b/net/Makefile index 38110334..741ba8bf 100644 --- a/net/Makefile +++ b/net/Makefile @@ -19,6 +19,7 @@ CXXFILES = \ CNetworkAddress.cpp \ CTCPSocket.cpp \ CTCPListenSocket.cpp \ + XNetwork.cpp \ XSocket.cpp \ $(NULL) diff --git a/net/XNetwork.cpp b/net/XNetwork.cpp index a745fc6a..26108535 100644 --- a/net/XNetwork.cpp +++ b/net/XNetwork.cpp @@ -4,7 +4,8 @@ // XNetworkUnavailable // -CString XNetworkUnavailable::getWhat() const throw() +CString +XNetworkUnavailable::getWhat() const throw() { return format("XNetworkUnavailable", "network library is not available"); } @@ -14,7 +15,8 @@ CString XNetworkUnavailable::getWhat() const throw() // XNetworkFailed // -CString XNetworkFailed::getWhat() const throw() +CString +XNetworkFailed::getWhat() const throw() { return format("XNetworkFailed", "cannot initialize network library"); } @@ -24,24 +26,29 @@ CString XNetworkFailed::getWhat() const throw() // XNetworkVersion // -XNetworkVersion::XNetworkVersion(int major, int minor) throw() : - m_major(major), - m_minor(minor) +XNetworkVersion::XNetworkVersion( + int major, + int minor) throw() : + m_major(major), + m_minor(minor) { // do nothing } -int XNetworkVersion::getMajor() const throw() +int +XNetworkVersion::getMajor() const throw() { return m_major; } -int XNetworkVersion::getMinor() const throw() +int +XNetworkVersion::getMinor() const throw() { return m_minor; } -CString XNetworkVersion::getWhat() const throw() +CString +XNetworkVersion::getWhat() const throw() { return format("XNetworkVersion", "unsupported network version %d.%d", @@ -54,7 +61,7 @@ CString XNetworkVersion::getWhat() const throw() // XNetworkFunctionUnavailable::XNetworkFunctionUnavailable( - const char* name) throw() + const char* name) throw() { try { m_name = name; @@ -64,7 +71,8 @@ XNetworkFunctionUnavailable::XNetworkFunctionUnavailable( } } -CString XNetworkFunctionUnavailable::getWhat() const throw() +CString +XNetworkFunctionUnavailable::getWhat() const throw() { return format("XNetworkFunctionUnavailable", "missing network function %s", diff --git a/net/XNetwork.h b/net/XNetwork.h index a971d423..ac0e5a24 100644 --- a/net/XNetwork.h +++ b/net/XNetwork.h @@ -1,9 +1,8 @@ #ifndef XNETWORK_H #define XNETWORK_H -#include "CString.h" #include "XBase.h" -#include "BasicTypes.h" +#include "CString.h" class XNetwork : public XBase { }; diff --git a/net/XSocket.cpp b/net/XSocket.cpp index 983e9eae..73731115 100644 --- a/net/XSocket.cpp +++ b/net/XSocket.cpp @@ -4,31 +4,37 @@ // XSocketAddress // -XSocketAddress::XSocketAddress(Error error, - const CString& hostname, UInt16 port) throw() : - m_error(error), - m_hostname(hostname), - m_port(port) +XSocketAddress::XSocketAddress( + Error error, + const CString& hostname, + UInt16 port) throw() : + m_error(error), + m_hostname(hostname), + m_port(port) { // do nothing } -XSocketAddress::Error XSocketAddress::getError() const throw() +XSocketAddress::Error +XSocketAddress::getError() const throw() { return m_error; } -CString XSocketAddress::getHostname() const throw() +CString +XSocketAddress::getHostname() const throw() { return m_hostname; } -UInt16 XSocketAddress::getPort() const throw() +UInt16 +XSocketAddress::getPort() const throw() { return m_port; } -CString XSocketAddress::getWhat() const throw() +CString +XSocketAddress::getWhat() const throw() { return "no address"; /* @@ -43,12 +49,15 @@ CString XSocketAddress::getWhat() const throw() // XSocketErrno // -XSocketErrno::XSocketErrno() : MXErrno() +XSocketErrno::XSocketErrno() : + MXErrno() { // do nothing } -XSocketErrno::XSocketErrno(int err) : MXErrno(err) +XSocketErrno::XSocketErrno( + int err) : + MXErrno(err) { // do nothing } @@ -58,7 +67,8 @@ XSocketErrno::XSocketErrno(int err) : MXErrno(err) // XSocketBind // -CString XSocketBind::getWhat() const throw() +CString +XSocketBind::getWhat() const throw() { return format("XSocketBind", "cannot bind address"); } @@ -68,7 +78,8 @@ CString XSocketBind::getWhat() const throw() // XSocketConnect // -CString XSocketConnect::getWhat() const throw() +CString +XSocketConnect::getWhat() const throw() { return format("XSocketConnect", "cannot connect socket"); } @@ -78,7 +89,8 @@ CString XSocketConnect::getWhat() const throw() // XSocketCreate // -CString XSocketCreate::getWhat() const throw() +CString +XSocketCreate::getWhat() const throw() { return format("XSocketCreate", "cannot create socket"); } diff --git a/net/XSocket.h b/net/XSocket.h index 1a83c99d..4b0fbf7c 100644 --- a/net/XSocket.h +++ b/net/XSocket.h @@ -1,8 +1,8 @@ #ifndef XSOCKET_H #define XSOCKET_H -#include "CString.h" #include "XBase.h" +#include "CString.h" #include "BasicTypes.h" class XSocket : public XBase { }; diff --git a/platform/CMSWindowsClipboard.cpp b/platform/CMSWindowsClipboard.cpp index 8503574c..87b2b298 100644 --- a/platform/CMSWindowsClipboard.cpp +++ b/platform/CMSWindowsClipboard.cpp @@ -1,14 +1,14 @@ #include "CMSWindowsClipboard.h" -#include "CString.h" #include "CLog.h" // // CMSWindowsClipboard // -CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : - m_window(window), - m_time(0) +CMSWindowsClipboard::CMSWindowsClipboard( + HWND window) : + m_window(window), + m_time(0) { // do nothing } @@ -18,7 +18,8 @@ CMSWindowsClipboard::~CMSWindowsClipboard() // do nothing } -bool CMSWindowsClipboard::empty() +bool +CMSWindowsClipboard::empty() { log((CLOG_DEBUG "empty clipboard")); @@ -30,8 +31,10 @@ bool CMSWindowsClipboard::empty() return true; } -void CMSWindowsClipboard::add( - EFormat format, const CString& data) +void +CMSWindowsClipboard::add( + EFormat format, + const CString& data) { log((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); @@ -54,7 +57,9 @@ void CMSWindowsClipboard::add( } } -bool CMSWindowsClipboard::open(Time time) const +bool +CMSWindowsClipboard::open( + Time time) const { log((CLOG_DEBUG "open clipboard")); @@ -68,29 +73,36 @@ bool CMSWindowsClipboard::open(Time time) const return true; } -void CMSWindowsClipboard::close() const +void +CMSWindowsClipboard::close() const { log((CLOG_DEBUG "close clipboard")); CloseClipboard(); } -IClipboard::Time CMSWindowsClipboard::getTime() const +IClipboard::Time +CMSWindowsClipboard::getTime() const { return m_time; } -bool CMSWindowsClipboard::has(EFormat format) const +bool +CMSWindowsClipboard::has( + EFormat format) const { const UINT win32Format = convertFormatToWin32(format); return (win32Format != 0 && IsClipboardFormatAvailable(win32Format) != 0); } -CString CMSWindowsClipboard::get(EFormat format) const +CString +CMSWindowsClipboard::get( + EFormat format) const { // get the win32 format. return empty data if unknown format. const UINT win32Format = convertFormatToWin32(format); - if (win32Format == 0) + if (win32Format == 0) { return CString(); + } // get a handle to the clipboard data and convert it HANDLE win32Data = GetClipboardData(win32Format); @@ -106,8 +118,9 @@ CString CMSWindowsClipboard::get(EFormat format) const return data; } -UINT CMSWindowsClipboard::convertFormatToWin32( - EFormat format) const +UINT +CMSWindowsClipboard::convertFormatToWin32( + EFormat format) const { switch (format) { case kText: @@ -118,8 +131,9 @@ UINT CMSWindowsClipboard::convertFormatToWin32( } } -HANDLE CMSWindowsClipboard::convertTextToWin32( - const CString& data) const +HANDLE +CMSWindowsClipboard::convertTextToWin32( + const CString& data) const { // compute size of converted text UInt32 dstSize = 1; @@ -157,14 +171,16 @@ HANDLE CMSWindowsClipboard::convertTextToWin32( return gData; } -CString CMSWindowsClipboard::convertTextFromWin32( - HANDLE handle) const +CString +CMSWindowsClipboard::convertTextFromWin32( + HANDLE handle) const { // get source data and it's size const char* src = (const char*)GlobalLock(handle); UInt32 srcSize = (SInt32)GlobalSize(handle); - if (src == NULL || srcSize <= 1) + if (src == NULL || srcSize <= 1) { return CString(); + } // ignore trailing NUL --srcSize; @@ -175,8 +191,9 @@ CString CMSWindowsClipboard::convertTextFromWin32( for (index = 0; index < srcSize; ++index) { if (src[index] == '\r') { // skip \r - if (index + 1 < srcSize && src[index + 1] == '\n') + if (index + 1 < srcSize && src[index + 1] == '\n') { ++index; + } } ++dstSize; } @@ -189,8 +206,9 @@ CString CMSWindowsClipboard::convertTextFromWin32( for (index = 0; index < srcSize; ++index) { if (src[index] == '\r') { // skip \r - if (index + 1 < srcSize && src[index + 1] == '\n') + if (index + 1 < srcSize && src[index + 1] == '\n') { ++index; + } } data += src[index]; } diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index 83e11704..f093c770 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -4,8 +4,7 @@ #include "TMethodJob.h" #include "CLog.h" #include "CString.h" -#include -#include +#include // // CMSWindowsScreen @@ -15,10 +14,10 @@ HINSTANCE CMSWindowsScreen::s_instance = NULL; CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; CMSWindowsScreen::CMSWindowsScreen() : - m_class(0), - m_cursor(NULL), - m_w(0), m_h(0), - m_thread(0) + m_class(0), + m_cursor(NULL), + m_w(0), m_h(0), + m_thread(0) { assert(s_screen == NULL); s_screen = this; @@ -30,12 +29,15 @@ CMSWindowsScreen::~CMSWindowsScreen() s_screen = NULL; } -void CMSWindowsScreen::init(HINSTANCE instance) +void +CMSWindowsScreen::init( + HINSTANCE instance) { s_instance = instance; } -void CMSWindowsScreen::doRun() +void +CMSWindowsScreen::doRun() { // save thread id for posting quit message m_thread = GetCurrentThreadId(); @@ -59,12 +61,14 @@ void CMSWindowsScreen::doRun() } } -void CMSWindowsScreen::doStop() +void +CMSWindowsScreen::doStop() { PostThreadMessage(m_thread, WM_QUIT, 0, 0); } -void CMSWindowsScreen::openDisplay() +void +CMSWindowsScreen::openDisplay() { assert(s_instance != NULL); assert(m_class == 0); @@ -106,7 +110,8 @@ void CMSWindowsScreen::openDisplay() onOpenDisplay(); } -void CMSWindowsScreen::closeDisplay() +void +CMSWindowsScreen::closeDisplay() { assert(s_instance != NULL); assert(m_class != 0); @@ -125,25 +130,30 @@ void CMSWindowsScreen::closeDisplay() log((CLOG_DEBUG "closed display")); } -HINSTANCE CMSWindowsScreen::getInstance() +HINSTANCE +CMSWindowsScreen::getInstance() { return s_instance; } -ATOM CMSWindowsScreen::getClass() const +ATOM +CMSWindowsScreen::getClass() const { return m_class; } -void CMSWindowsScreen::updateScreenSize() +void +CMSWindowsScreen::updateScreenSize() { m_w = GetSystemMetrics(SM_CXSCREEN); m_h = GetSystemMetrics(SM_CYSCREEN); log((CLOG_INFO "display resize: %dx%d", m_w, m_h)); } -void CMSWindowsScreen::getScreenSize( - SInt32* w, SInt32* h) const +void +CMSWindowsScreen::getScreenSize( + SInt32* w, + SInt32* h) const { assert(m_class != 0); assert(w != NULL && h != NULL); @@ -152,7 +162,8 @@ void CMSWindowsScreen::getScreenSize( *h = m_h; } -HDESK CMSWindowsScreen::openInputDesktop() const +HDESK +CMSWindowsScreen::openInputDesktop() const { return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, DESKTOP_CREATEWINDOW | @@ -160,8 +171,9 @@ HDESK CMSWindowsScreen::openInputDesktop() const GENERIC_WRITE); } -CString CMSWindowsScreen::getDesktopName( - HDESK desk) const +CString +CMSWindowsScreen::getDesktopName( + HDESK desk) const { if (desk == NULL) { return CString(); @@ -177,14 +189,17 @@ CString CMSWindowsScreen::getDesktopName( } } -bool CMSWindowsScreen::isCurrentDesktop( - HDESK desk) const +bool +CMSWindowsScreen::isCurrentDesktop( + HDESK desk) const { return CStringUtil::CaselessCmp::equal(getDesktopName(desk), getCurrentDesktopName()); } -void CMSWindowsScreen::getEvent(MSG* msg) const +void +CMSWindowsScreen::getEvent( + MSG* msg) const { // wait for an event in a cancellable way while (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0) { @@ -193,9 +208,12 @@ void CMSWindowsScreen::getEvent(MSG* msg) const GetMessage(msg, NULL, 0, 0); } -LRESULT CALLBACK CMSWindowsScreen::wndProc( - HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) +LRESULT CALLBACK +CMSWindowsScreen::wndProc( + HWND hwnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) { assert(s_screen != NULL); return s_screen->onEvent(hwnd, msg, wParam, lParam); diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index 0e54205b..f3db8a43 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -1,9 +1,8 @@ #ifndef CMSWINDOWSSCREEN_H #define CMSWINDOWSSCREEN_H -#include "CMutex.h" #include "IClipboard.h" -#include "BasicTypes.h" +#include "CMutex.h" #define WIN32_LEAN_AND_MEAN #include diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index 99349ea7..ea14d40f 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -1,6 +1,6 @@ #include "CUnixPlatform.h" #include "CLog.h" -#include +#include #include #include #include @@ -9,7 +9,6 @@ #include #include - // // CUnixPlatform // @@ -24,24 +23,29 @@ CUnixPlatform::~CUnixPlatform() // do nothing } -bool CUnixPlatform::installDaemon( - const char*, - const char*, - const char*, - const char*) +bool +CUnixPlatform::installDaemon( + const char*, + const char*, + const char*, + const char*) { // daemons don't require special installation return true; } -bool CUnixPlatform::uninstallDaemon(const char*) +bool +CUnixPlatform::uninstallDaemon( + const char*) { // daemons don't require special installation return true; } -int CUnixPlatform::daemonize( - const char* name, DaemonFunc func) +int +CUnixPlatform::daemonize( + const char* name, + DaemonFunc func) { // fork so shell thinks we're done and so we're not a process // group leader @@ -86,43 +90,46 @@ int CUnixPlatform::daemonize( return func(this, 1, &name); } -int CUnixPlatform::restart( - RestartFunc func, int minErrorCode) +int +CUnixPlatform::restart( + RestartFunc func, + int minErrorCode) { for (;;) { switch (fork()) { - default: { - // parent process. wait for child to exit. - int status; - if (wait(&status) == -1) { - // wait failed. this is unexpected so bail. - log((CLOG_CRIT "wait() failed")); - return minErrorCode; - } + default: + { + // parent process. wait for child to exit. + int status; + if (wait(&status) == -1) { + // wait failed. this is unexpected so bail. + log((CLOG_CRIT "wait() failed")); + return minErrorCode; + } - // what happened? if the child exited normally with a - // status less than 16 then the child was deliberately - // terminated so we also terminate. - if (WIFEXITED(status) && WEXITSTATUS(status) < minErrorCode) { - return WEXITSTATUS(status); - } + // what happened? if the child exited normally with a + // status less than 16 then the child was deliberately + // terminated so we also terminate. + if (WIFEXITED(status) && WEXITSTATUS(status) < minErrorCode) { + return WEXITSTATUS(status); + } - // did child die horribly? - if (WIFSIGNALED(status)) { - switch (WTERMSIG(status)) { - case SIGHUP: - case SIGINT: - case SIGQUIT: - case SIGTERM: - break; + // did child die horribly? + if (WIFSIGNALED(status)) { + switch (WTERMSIG(status)) { + case SIGHUP: + case SIGINT: + case SIGQUIT: + case SIGTERM: + break; - default: - // uh oh. bail out. - return 16; + default: + // uh oh. bail out. + return 16; + } } } break; - } case -1: // fork() failed. log the error and proceed as a child @@ -136,7 +143,9 @@ int CUnixPlatform::restart( } } -const char* CUnixPlatform::getBasename(const char* pathname) const +const char* +CUnixPlatform::getBasename( + const char* pathname) const { if (pathname == NULL) { return NULL; @@ -151,7 +160,8 @@ const char* CUnixPlatform::getBasename(const char* pathname) const } } -CString CUnixPlatform::getUserDirectory() const +CString +CUnixPlatform::getUserDirectory() const { // FIXME -- use geteuid? shouldn't run this setuid anyway. struct passwd* pwent = getpwuid(getuid()); @@ -163,14 +173,16 @@ CString CUnixPlatform::getUserDirectory() const } } -CString CUnixPlatform::getSystemDirectory() const +CString +CUnixPlatform::getSystemDirectory() const { return "/etc"; } -CString CUnixPlatform::addPathComponent( - const CString& prefix, - const CString& suffix) const +CString +CUnixPlatform::addPathComponent( + const CString& prefix, + const CString& suffix) const { CString path; path.reserve(prefix.size() + 1 + suffix.size()); @@ -182,14 +194,18 @@ CString CUnixPlatform::addPathComponent( return path; } -void CUnixPlatform::setDaemonLogger(const char* name) +void +CUnixPlatform::setDaemonLogger( + const char* name) { openlog(name, 0, LOG_DAEMON); CLog::setOutputter(&CUnixPlatform::deamonLogger); } -bool CUnixPlatform::deamonLogger( - int priority, const char* msg) +bool +CUnixPlatform::deamonLogger( + int priority, + const char* msg) { // convert priority switch (priority) { diff --git a/platform/CUnixPlatform.h b/platform/CUnixPlatform.h index 94bc4a05..d5a92ec3 100644 --- a/platform/CUnixPlatform.h +++ b/platform/CUnixPlatform.h @@ -10,9 +10,9 @@ public: // IPlatform overrides virtual bool installDaemon(const char* name, - const char* description, - const char* pathname, - const char* commandLine); + const char* description, + const char* pathname, + const char* commandLine); virtual bool uninstallDaemon(const char* name); virtual int daemonize(const char* name, DaemonFunc); virtual int restart(RestartFunc, int minErrorCode); @@ -20,8 +20,8 @@ public: virtual CString getUserDirectory() const; virtual CString getSystemDirectory() const; virtual CString addPathComponent( - const CString& prefix, - const CString& suffix) const; + const CString& prefix, + const CString& suffix) const; protected: virtual void setDaemonLogger(const char* name); diff --git a/platform/CWin32Platform.cpp b/platform/CWin32Platform.cpp index 9e71a141..75dc0c37 100644 --- a/platform/CWin32Platform.cpp +++ b/platform/CWin32Platform.cpp @@ -3,10 +3,9 @@ #include "CThread.h" #include "CLog.h" #include "stdvector.h" -#include +#include #include #include -#include // // CWin32Platform @@ -25,7 +24,8 @@ CWin32Platform::~CWin32Platform() // do nothing } -bool CWin32Platform::isWindows95Family() +bool +CWin32Platform::isWindows95Family() { OSVERSIONINFO version; version.dwOSVersionInfoSize = sizeof(version); @@ -36,16 +36,20 @@ bool CWin32Platform::isWindows95Family() return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); } -void CWin32Platform::setStatus( - SERVICE_STATUS_HANDLE handle, - DWORD state) +void +CWin32Platform::setStatus( + SERVICE_STATUS_HANDLE handle, + DWORD state) { setStatus(handle, state, 0, 0); } -void CWin32Platform::setStatus( - SERVICE_STATUS_HANDLE handle, - DWORD state, DWORD step, DWORD waitHint) +void +CWin32Platform::setStatus( + SERVICE_STATUS_HANDLE handle, + DWORD state, + DWORD step, + DWORD waitHint) { SERVICE_STATUS status; status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | @@ -61,9 +65,10 @@ void CWin32Platform::setStatus( SetServiceStatus(handle, &status); } -void CWin32Platform::setStatusError( - SERVICE_STATUS_HANDLE handle, - DWORD error) +void +CWin32Platform::setStatusError( + SERVICE_STATUS_HANDLE handle, + DWORD error) { SERVICE_STATUS status; status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | @@ -79,11 +84,12 @@ void CWin32Platform::setStatusError( SetServiceStatus(handle, &status); } -bool CWin32Platform::installDaemon( - const char* name, - const char* description, - const char* pathname, - const char* commandLine) +bool +CWin32Platform::installDaemon( + const char* name, + const char* description, + const char* pathname, + const char* commandLine) { // windows 95 family services if (isWindows95Family()) { @@ -174,7 +180,9 @@ bool CWin32Platform::installDaemon( } } -bool CWin32Platform::uninstallDaemon(const char* name) +bool +CWin32Platform::uninstallDaemon( + const char* name) { // windows 95 family services if (isWindows95Family()) { @@ -231,9 +239,10 @@ bool CWin32Platform::uninstallDaemon(const char* name) } } -int CWin32Platform::daemonize( - const char* name, - DaemonFunc func) +int +CWin32Platform::daemonize( + const char* name, + DaemonFunc func) { assert(name != NULL); assert(func != NULL); @@ -294,8 +303,10 @@ int CWin32Platform::daemonize( } } -int CWin32Platform::restart( - RestartFunc func, int /*minErrorCode*/) +int +CWin32Platform::restart( + RestartFunc func, + int /*minErrorCode*/) { // FIXME -- start in separate process or thread. note that this // isn't too critical as win32 doesn't force us to terminate for @@ -303,7 +314,9 @@ int CWin32Platform::restart( return func(); } -const char* CWin32Platform::getBasename(const char* pathname) const +const char* +CWin32Platform::getBasename( + const char* pathname) const { if (pathname == NULL) { return NULL; @@ -327,7 +340,8 @@ const char* CWin32Platform::getBasename(const char* pathname) const return basename; } -CString CWin32Platform::getUserDirectory() const +CString +CWin32Platform::getUserDirectory() const { // try %HOMEPATH% TCHAR dir[MAX_PATH]; @@ -370,7 +384,8 @@ CString CWin32Platform::getUserDirectory() const return "C:"; } -CString CWin32Platform::getSystemDirectory() const +CString +CWin32Platform::getSystemDirectory() const { // get windows directory char dir[MAX_PATH]; @@ -383,9 +398,10 @@ CString CWin32Platform::getSystemDirectory() const } } -CString CWin32Platform::addPathComponent( - const CString& prefix, - const CString& suffix) const +CString +CWin32Platform::addPathComponent( + const CString& prefix, + const CString& suffix) const { CString path; path.reserve(prefix.size() + 1 + suffix.size()); @@ -399,8 +415,10 @@ CString CWin32Platform::addPathComponent( return path; } -HKEY CWin32Platform::openKey( - HKEY key, const char* keyName) +HKEY +CWin32Platform::openKey( + HKEY key, + const char* keyName) { // open next key HKEY newKey; @@ -422,8 +440,10 @@ HKEY CWin32Platform::openKey( return newKey; } -HKEY CWin32Platform::openKey( - HKEY key, const char** keyNames) +HKEY +CWin32Platform::openKey( + HKEY key, + const char** keyNames) { for (UInt32 i = 0; key != NULL && keyNames[i] != NULL; ++i) { // open next key @@ -432,28 +452,39 @@ HKEY CWin32Platform::openKey( return key; } -void CWin32Platform::closeKey(HKEY key) +void +CWin32Platform::closeKey( + HKEY key) { assert(key != NULL); RegCloseKey(key); } -void CWin32Platform::deleteKey(HKEY key, const char* name) +void +CWin32Platform::deleteKey( + HKEY key, + const char* name) { assert(key != NULL); assert(name != NULL); RegDeleteKey(key, name); } -void CWin32Platform::deleteValue(HKEY key, const char* name) +void +CWin32Platform::deleteValue( + HKEY key, + const char* name) { assert(key != NULL); assert(name != NULL); RegDeleteValue(key, name); } -void CWin32Platform::setValue(HKEY key, - const char* name, const CString& value) +void +CWin32Platform::setValue( + HKEY key, + const char* name, + const CString& value) { assert(key != NULL); assert(name != NULL); @@ -462,8 +493,10 @@ void CWin32Platform::setValue(HKEY key, value.size() + 1); } -CString CWin32Platform::readValueString(HKEY key, - const char* name) +CString +CWin32Platform::readValueString( + HKEY key, + const char* name) { // get the size of the string DWORD type; @@ -490,7 +523,8 @@ CString CWin32Platform::readValueString(HKEY key, return value; } -HKEY CWin32Platform::openNTServicesKey() +HKEY +CWin32Platform::openNTServicesKey() { static const char* s_keyNames[] = { _T("SYSTEM"), @@ -502,7 +536,8 @@ HKEY CWin32Platform::openNTServicesKey() return openKey(HKEY_LOCAL_MACHINE, s_keyNames); } -HKEY CWin32Platform::open95ServicesKey() +HKEY +CWin32Platform::open95ServicesKey() { static const char* s_keyNames[] = { _T("Software"), @@ -516,7 +551,10 @@ HKEY CWin32Platform::open95ServicesKey() return openKey(HKEY_LOCAL_MACHINE, s_keyNames); } -int CWin32Platform::runDaemon(RunFunc run, StopFunc stop) +int +CWin32Platform::runDaemon( + RunFunc run, + StopFunc stop) { // should only be called from DaemonFunc assert(m_serviceMutex != NULL); @@ -595,8 +633,10 @@ int CWin32Platform::runDaemon(RunFunc run, StopFunc stop) } } -void CWin32Platform::serviceMain( - DWORD argc, LPTSTR* argvIn) +void +CWin32Platform::serviceMain( + DWORD argc, + LPTSTR* argvIn) { typedef std::vector ArgList; typedef std::vector Arguments; @@ -718,13 +758,17 @@ void CWin32Platform::serviceMain( // FIXME -- close event log? } -void WINAPI CWin32Platform::serviceMainEntry( - DWORD argc, LPTSTR* argv) +void WINAPI +CWin32Platform::serviceMainEntry( + DWORD argc, + LPTSTR* argv) { s_daemonPlatform->serviceMain(argc, argv); } -void CWin32Platform::serviceHandler(DWORD ctrl) +void +CWin32Platform::serviceHandler( + DWORD ctrl) { assert(m_serviceMutex != NULL); assert(m_serviceState != NULL); @@ -796,13 +840,17 @@ void CWin32Platform::serviceHandler(DWORD ctrl) setStatus(m_statusHandle, *m_serviceState); } -void WINAPI CWin32Platform::serviceHandlerEntry(DWORD ctrl) +void WINAPI +CWin32Platform::serviceHandlerEntry( + DWORD ctrl) { s_daemonPlatform->serviceHandler(ctrl); } -bool CWin32Platform::serviceLogger( - int priority, const char* msg) +bool +CWin32Platform::serviceLogger( + int priority, + const char* msg) { if (s_eventLog == NULL) { return false; diff --git a/platform/CWin32Platform.h b/platform/CWin32Platform.h index 4300c050..40dfbe29 100644 --- a/platform/CWin32Platform.h +++ b/platform/CWin32Platform.h @@ -21,7 +21,7 @@ public: // utility for calling SetServiceStatus() static void setStatus(SERVICE_STATUS_HANDLE, DWORD state); static void setStatus(SERVICE_STATUS_HANDLE, - DWORD state, DWORD step, DWORD waitHint); + DWORD state, DWORD step, DWORD waitHint); static void setStatusError(SERVICE_STATUS_HANDLE, DWORD error); // run a service. the RunFunc should unlock the passed in mutex @@ -43,9 +43,9 @@ public: // IPlatform overrides virtual bool installDaemon(const char* name, - const char* description, - const char* pathname, - const char* commandLine); + const char* description, + const char* pathname, + const char* commandLine); virtual bool uninstallDaemon(const char* name); virtual int daemonize(const char* name, DaemonFunc); virtual int restart(RestartFunc, int minErrorCode); @@ -53,8 +53,8 @@ public: virtual CString getUserDirectory() const; virtual CString getSystemDirectory() const; virtual CString addPathComponent( - const CString& prefix, - const CString& suffix) const; + const CString& prefix, + const CString& suffix) const; private: static HKEY openKey(HKEY parent, const char*); @@ -63,7 +63,7 @@ private: static void deleteKey(HKEY, const char* name); static void deleteValue(HKEY, const char* name); static void setValue(HKEY, const char* name, - const CString& value); + const CString& value); static CString readValueString(HKEY, const char* name); static HKEY openNTServicesKey(); static HKEY open95ServicesKey(); diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 9a225e39..a025d372 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -1,25 +1,27 @@ #include "CXWindowsClipboard.h" #include "CXWindowsUtil.h" -#include "CLog.h" #include "CThread.h" +#include "CLog.h" #include "TMethodJob.h" -#include +#include #include // // CXWindowsClipboard // -CXWindowsClipboard::CXWindowsClipboard(Display* display, - Window window, ClipboardID id) : - m_display(display), - m_window(window), - m_id(id), - m_open(false), - m_time(0), - m_owner(false), - m_timeOwned(0), - m_timeLost(0) +CXWindowsClipboard::CXWindowsClipboard( + Display* display, + Window window, + ClipboardID id) : + m_display(display), + m_window(window), + m_id(id), + m_open(false), + m_time(0), + m_owner(false), + m_timeOwned(0), + m_timeLost(0) { // get some atoms m_atomTargets = XInternAtom(m_display, "TARGETS", False); @@ -60,7 +62,9 @@ CXWindowsClipboard::~CXWindowsClipboard() clearReplies(); } -void CXWindowsClipboard::lost(Time time) +void +CXWindowsClipboard::lost( + Time time) { log((CLOG_DEBUG "lost clipboard %d ownership at %d", m_id, time)); if (m_owner) { @@ -70,10 +74,13 @@ void CXWindowsClipboard::lost(Time time) } } -void CXWindowsClipboard::addRequest( - Window owner, - Window requestor, Atom target, - ::Time time, Atom property) +void +CXWindowsClipboard::addRequest( + Window owner, + Window requestor, + Atom target, + ::Time time, + Atom property) { // must be for our window and we must have owned the selection // at the given time. @@ -110,9 +117,12 @@ void CXWindowsClipboard::addRequest( pushReplies(); } -bool CXWindowsClipboard::addSimpleRequest( - Window requestor, Atom target, - ::Time time, Atom property) +bool +CXWindowsClipboard::addSimpleRequest( + Window requestor, + Atom target, + ::Time time, + Atom property) { // obsolete requestors may supply a None property. in // that case we use the target as the property to store @@ -151,9 +161,11 @@ bool CXWindowsClipboard::addSimpleRequest( } } -bool CXWindowsClipboard::processRequest( - Window requestor, - ::Time /*time*/, Atom property) +bool +CXWindowsClipboard::processRequest( + Window requestor, + ::Time /*time*/, + Atom property) { CReplyMap::iterator index = m_replies.find(requestor); if (index == m_replies.end()) { @@ -179,8 +191,9 @@ bool CXWindowsClipboard::processRequest( return false; } -bool CXWindowsClipboard::destroyRequest( - Window requestor) +bool +CXWindowsClipboard::destroyRequest( + Window requestor) { CReplyMap::iterator index = m_replies.find(requestor); if (index == m_replies.end()) { @@ -198,17 +211,20 @@ bool CXWindowsClipboard::destroyRequest( return true; } -Window CXWindowsClipboard::getWindow() const +Window +CXWindowsClipboard::getWindow() const { return m_window; } -Atom CXWindowsClipboard::getSelection() const +Atom +CXWindowsClipboard::getSelection() const { return m_selection; } -bool CXWindowsClipboard::empty() +bool +CXWindowsClipboard::empty() { assert(m_open); @@ -240,8 +256,10 @@ bool CXWindowsClipboard::empty() return true; } -void CXWindowsClipboard::add( - EFormat format, const CString& data) +void +CXWindowsClipboard::add( + EFormat format, + const CString& data) { assert(m_open); assert(m_owner); @@ -254,7 +272,9 @@ void CXWindowsClipboard::add( // FIXME -- set motif clipboard item? } -bool CXWindowsClipboard::open(Time time) const +bool +CXWindowsClipboard::open( + Time time) const { assert(!m_open); @@ -304,7 +324,8 @@ bool CXWindowsClipboard::open(Time time) const return true; } -void CXWindowsClipboard::close() const +void +CXWindowsClipboard::close() const { assert(m_open); @@ -319,12 +340,15 @@ void CXWindowsClipboard::close() const m_open = false; } -IClipboard::Time CXWindowsClipboard::getTime() const +IClipboard::Time +CXWindowsClipboard::getTime() const { return m_timeOwned; } -bool CXWindowsClipboard::has(EFormat format) const +bool +CXWindowsClipboard::has( + EFormat format) const { assert(m_open); @@ -332,7 +356,9 @@ bool CXWindowsClipboard::has(EFormat format) const return m_added[format]; } -CString CXWindowsClipboard::get(EFormat format) const +CString +CXWindowsClipboard::get( + EFormat format) const { assert(m_open); @@ -340,23 +366,28 @@ CString CXWindowsClipboard::get(EFormat format) const return m_data[format]; } -IClipboard::EFormat CXWindowsClipboard::getFormat(Atom src) const +IClipboard::EFormat +CXWindowsClipboard::getFormat( + Atom src) const { // FIXME -- handle more formats (especially mime-type-like formats // and various character encodings like unicode). if (src == m_atomString || src == m_atomText /*|| - src == m_atomCompoundText*/) + src == m_atomCompoundText*/) { return IClipboard::kText; + } return IClipboard::kNumFormats; } -void CXWindowsClipboard::clearCache() const +void +CXWindowsClipboard::clearCache() const { const_cast(this)->doClearCache(); } -void CXWindowsClipboard::doClearCache() +void +CXWindowsClipboard::doClearCache() { m_cached = false; for (SInt32 index = 0; index < kNumFormats; ++index) { @@ -365,7 +396,8 @@ void CXWindowsClipboard::doClearCache() } } -void CXWindowsClipboard::fillCache() const +void +CXWindowsClipboard::fillCache() const { // get the selection data if not already cached if (!m_cached) { @@ -373,7 +405,8 @@ void CXWindowsClipboard::fillCache() const } } -void CXWindowsClipboard::doFillCache() +void +CXWindowsClipboard::doFillCache() { if (m_motif) { motifFillCache(); @@ -385,7 +418,8 @@ void CXWindowsClipboard::doFillCache() m_cacheTime = m_timeOwned; } -void CXWindowsClipboard::icccmFillCache() +void +CXWindowsClipboard::icccmFillCache() { log((CLOG_DEBUG "ICCCM fill clipboard %d", m_id)); @@ -447,10 +481,11 @@ void CXWindowsClipboard::icccmFillCache() } } -bool CXWindowsClipboard::icccmGetSelection( - Atom target, - Atom* actualTarget, - CString* data) const +bool +CXWindowsClipboard::icccmGetSelection( + Atom target, + Atom* actualTarget, + CString* data) const { assert(actualTarget != NULL); assert(data != NULL); @@ -470,7 +505,8 @@ bool CXWindowsClipboard::icccmGetSelection( return true; } -IClipboard::Time CXWindowsClipboard::icccmGetTime() const +IClipboard::Time +CXWindowsClipboard::icccmGetTime() const { Atom actualTarget; CString data; @@ -487,7 +523,8 @@ IClipboard::Time CXWindowsClipboard::icccmGetTime() const } } -bool CXWindowsClipboard::motifLockClipboard() const +bool +CXWindowsClipboard::motifLockClipboard() const { // fail if anybody owns the lock (even us, so this is non-recursive) Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); @@ -510,7 +547,8 @@ bool CXWindowsClipboard::motifLockClipboard() const return true; } -void CXWindowsClipboard::motifUnlockClipboard() const +void +CXWindowsClipboard::motifUnlockClipboard() const { log((CLOG_DEBUG1 "unlocked motif clipboard")); @@ -525,7 +563,8 @@ void CXWindowsClipboard::motifUnlockClipboard() const XSetSelectionOwner(m_display, m_atomMotifClipLock, None, time); } -bool CXWindowsClipboard::motifOwnsClipboard() const +bool +CXWindowsClipboard::motifOwnsClipboard() const { // get the current selection owner // FIXME -- this can't be right. even if the window is destroyed @@ -563,7 +602,8 @@ bool CXWindowsClipboard::motifOwnsClipboard() const return false; } -void CXWindowsClipboard::motifFillCache() +void +CXWindowsClipboard::motifFillCache() { log((CLOG_DEBUG "Motif fill clipboard %d", m_id)); @@ -677,14 +717,18 @@ void CXWindowsClipboard::motifFillCache() } } -IClipboard::Time CXWindowsClipboard::motifGetTime() const +IClipboard::Time +CXWindowsClipboard::motifGetTime() const { // FIXME -- does Motif report this? return 0; } -bool CXWindowsClipboard::insertMultipleReply( - Window requestor, ::Time time, Atom property) +bool +CXWindowsClipboard::insertMultipleReply( + Window requestor, + ::Time time, + Atom property) { // get the requested targets Atom target; @@ -735,7 +779,9 @@ bool CXWindowsClipboard::insertMultipleReply( return true; } -void CXWindowsClipboard::insertReply(CReply* reply) +void +CXWindowsClipboard::insertReply( + CReply* reply) { assert(reply != NULL); @@ -780,7 +826,8 @@ void CXWindowsClipboard::insertReply(CReply* reply) } } -void CXWindowsClipboard::pushReplies() +void +CXWindowsClipboard::pushReplies() { // send the first reply for each window if that reply hasn't // been sent yet. @@ -793,10 +840,11 @@ void CXWindowsClipboard::pushReplies() } } -void CXWindowsClipboard::pushReplies( - CReplyMap::iterator mapIndex, - CReplyList& replies, - CReplyList::iterator index) +void +CXWindowsClipboard::pushReplies( + CReplyMap::iterator mapIndex, + CReplyList& replies, + CReplyList::iterator index) { CReply* reply = *index; while (sendReply(reply)) { @@ -821,7 +869,9 @@ void CXWindowsClipboard::pushReplies( } } -bool CXWindowsClipboard::sendReply(CReply* reply) +bool +CXWindowsClipboard::sendReply( + CReply* reply) { assert(reply != NULL); @@ -943,7 +993,8 @@ bool CXWindowsClipboard::sendReply(CReply* reply) return false; } -void CXWindowsClipboard::clearReplies() +void +CXWindowsClipboard::clearReplies() { for (CReplyMap::iterator index = m_replies.begin(); index != m_replies.end(); ++index) { @@ -953,7 +1004,9 @@ void CXWindowsClipboard::clearReplies() m_eventMasks.clear(); } -void CXWindowsClipboard::clearReplies(CReplyList& replies) +void +CXWindowsClipboard::clearReplies( + CReplyList& replies) { for (CReplyList::iterator index = replies.begin(); index != replies.end(); ++index) { @@ -962,9 +1015,13 @@ void CXWindowsClipboard::clearReplies(CReplyList& replies) replies.clear(); } -void CXWindowsClipboard::sendNotify( - Window requestor, Atom selection, - Atom target, Atom property, Time time) +void +CXWindowsClipboard::sendNotify( + Window requestor, + Atom selection, + Atom target, + Atom property, + Time time) { XEvent event; event.xselection.type = SelectionNotify; @@ -978,26 +1035,33 @@ void CXWindowsClipboard::sendNotify( XSendEvent(m_display, requestor, False, 0, &event); } -bool CXWindowsClipboard::wasOwnedAtTime( - ::Time time) const +bool +CXWindowsClipboard::wasOwnedAtTime( + ::Time time) const { // not owned if we've never owned the selection - if (m_timeOwned == 0) + if (m_timeOwned == 0) { return false; + } // if time is CurrentTime then return true if we still own the // selection and false if we do not. else if we still own the // selection then get the current time, otherwise use // m_timeLost as the end time. Time lost = m_timeLost; - if (m_timeLost == 0) - if (time == CurrentTime) + if (m_timeLost == 0) { + if (time == CurrentTime) { return true; - else + } + else { lost = CXWindowsUtil::getCurrentTime(m_display, m_window); - else - if (time == CurrentTime) + } + } + else { + if (time == CurrentTime) { return false; + } + } // compare time to range Time duration = lost - m_timeOwned; @@ -1005,8 +1069,10 @@ bool CXWindowsClipboard::wasOwnedAtTime( return (/*when >= 0 &&*/ when < duration); } -Atom CXWindowsClipboard::getTargetsData( - CString& data, int* format) const +Atom +CXWindowsClipboard::getTargetsData( + CString& data, + int* format) const { assert(format != NULL); @@ -1029,8 +1095,10 @@ Atom CXWindowsClipboard::getTargetsData( return m_atomTargets; } -Atom CXWindowsClipboard::getTimestampData( - CString& data, int* format) const +Atom +CXWindowsClipboard::getTimestampData( + CString& data, + int* format) const { assert(format != NULL); @@ -1040,8 +1108,10 @@ Atom CXWindowsClipboard::getTimestampData( return m_atomTimestamp; } -Atom CXWindowsClipboard::getStringData( - CString& data, int* format) const +Atom +CXWindowsClipboard::getStringData( + CString& data, + int* format) const { assert(format != NULL); @@ -1061,17 +1131,19 @@ Atom CXWindowsClipboard::getStringData( // CXWindowsClipboard::CICCCMGetClipboard::CICCCMGetClipboard( - Window requestor, Time time, Atom property) : - m_requestor(requestor), - m_time(time), - m_property(property), - m_incr(false), - m_failed(false), - m_done(false), - m_reading(false), - m_data(NULL), - m_actualTarget(NULL), - m_error(false) + Window requestor, + Time time, + Atom property) : + m_requestor(requestor), + m_time(time), + m_property(property), + m_incr(false), + m_failed(false), + m_done(false), + m_reading(false), + m_data(NULL), + m_actualTarget(NULL), + m_error(false) { // do nothing } @@ -1081,10 +1153,13 @@ CXWindowsClipboard::CICCCMGetClipboard::~CICCCMGetClipboard() // do nothing } -bool CXWindowsClipboard::CICCCMGetClipboard::readClipboard( - Display* display, - Atom selection, Atom target, - Atom* actualTarget, CString* data) +bool +CXWindowsClipboard::CICCCMGetClipboard::readClipboard( + Display* display, + Atom selection, + Atom target, + Atom* actualTarget, + CString* data) { assert(actualTarget != NULL); assert(data != NULL); @@ -1139,9 +1214,10 @@ bool CXWindowsClipboard::CICCCMGetClipboard::readClipboard( return !m_failed; } -bool CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate( - Display* display, - XEvent* xevent) +bool +CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate( + Display* display, + XEvent* xevent) { // process event switch (xevent->type) { @@ -1276,17 +1352,19 @@ log((CLOG_INFO " INCR secondary chunk")); // FIXME return !m_incr; } -Bool CXWindowsClipboard::CICCCMGetClipboard::eventPredicate( - Display* display, - XEvent* xevent, - XPointer arg) +Bool +CXWindowsClipboard::CICCCMGetClipboard::eventPredicate( + Display* display, + XEvent* xevent, + XPointer arg) { CICCCMGetClipboard* self = reinterpret_cast(arg); return self->doEventPredicate(display, xevent) ? True : False; } -void CXWindowsClipboard::CICCCMGetClipboard::timeout( - void* vdisplay) +void +CXWindowsClipboard::CICCCMGetClipboard::timeout( + void* vdisplay) { // wait CThread::sleep(0.2); // FIXME -- is this too short? @@ -1308,35 +1386,42 @@ void CXWindowsClipboard::CICCCMGetClipboard::timeout( // CXWindowsClipboard::CReply // -CXWindowsClipboard::CReply::CReply(Window requestor, - Atom target, ::Time time) : - m_requestor(requestor), - m_target(target), - m_time(time), - m_property(None), - m_replied(false), - m_done(false), - m_data(), - m_type(None), - m_format(32), - m_ptr(0) +CXWindowsClipboard::CReply::CReply( + Window requestor, + Atom target, + ::Time time) : + m_requestor(requestor), + m_target(target), + m_time(time), + m_property(None), + m_replied(false), + m_done(false), + m_data(), + m_type(None), + m_format(32), + m_ptr(0) { // do nothing } -CXWindowsClipboard::CReply::CReply(Window requestor, - Atom target, ::Time time, Atom property, - const CString& data, Atom type, int format) : - m_requestor(requestor), - m_target(target), - m_time(time), - m_property(property), - m_replied(false), - m_done(false), - m_data(data), - m_type(type), - m_format(format), - m_ptr(0) +CXWindowsClipboard::CReply::CReply( + Window requestor, + Atom target, + ::Time time, + Atom property, + const CString& data, + Atom type, + int format) : + m_requestor(requestor), + m_target(target), + m_time(time), + m_property(property), + m_replied(false), + m_done(false), + m_data(data), + m_type(type), + m_format(format), + m_ptr(0) { // do nothing } diff --git a/platform/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h index 9c04c9bc..f1d04580 100644 --- a/platform/CXWindowsClipboard.h +++ b/platform/CXWindowsClipboard.h @@ -3,7 +3,6 @@ #include "IClipboard.h" #include "ClipboardTypes.h" -#include "CString.h" #include "stdmap.h" #include "stdlist.h" #include @@ -20,13 +19,13 @@ public: // owner window isn't this clipboard's window then this simply // sends a failure event to the requestor. void addRequest(Window owner, - Window requestor, Atom target, - ::Time time, Atom property); + Window requestor, Atom target, + ::Time time, Atom property); // continue processing a selection request. returns true if the // request was handled, false if the request was unknown. bool processRequest(Window requestor, - ::Time time, Atom property); + ::Time time, Atom property); // terminate a selection request. returns true iff the request // was known and handled. @@ -56,8 +55,8 @@ private: // could be performed, false otherwise. in either case, the // reply is inserted. bool addSimpleRequest( - Window requestor, Atom target, - ::Time time, Atom property); + Window requestor, Atom target, + ::Time time, Atom property); // clear the cache, resetting the cached flag and the added flag for // each format. @@ -71,8 +70,8 @@ private: // ICCCM interoperability methods void icccmFillCache(); bool icccmGetSelection(Atom target, - Atom* actualTarget, - CString* data) const; + Atom* actualTarget, + CString* data) const; Time icccmGetTime() const; // motif interoperability methods @@ -97,15 +96,15 @@ private: // true iff the conversion was successful or the conversion // cannot be performed (in which case *actualTarget == None). bool readClipboard(Display* display, - Atom selection, Atom target, - Atom* actualTarget, CString* data); + Atom selection, Atom target, + Atom* actualTarget, CString* data); private: bool doEventPredicate(Display* display, - XEvent* event); + XEvent* event); static Bool eventPredicate(Display* display, - XEvent* event, - XPointer arg); + XEvent* event, + XPointer arg); void timeout(void*); private: @@ -177,7 +176,7 @@ private: public: CReply(Window, Atom target, ::Time); CReply(Window, Atom target, ::Time, Atom property, - const CString& data, Atom type, int format); + const CString& data, Atom type, int format); public: // information about the request @@ -209,12 +208,12 @@ private: void insertReply(CReply*); void pushReplies(); void pushReplies(CReplyMap::iterator, - CReplyList&, CReplyList::iterator); + CReplyList&, CReplyList::iterator); bool sendReply(CReply*); void clearReplies(); void clearReplies(CReplyList&); void sendNotify(Window requestor, Atom selection, - Atom target, Atom property, Time time); + Atom target, Atom property, Time time); bool wasOwnedAtTime(::Time) const; // data conversion methods diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index adbc5193..9f283283 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -2,14 +2,13 @@ #include "CXWindowsClipboard.h" #include "CXWindowsUtil.h" #include "CClipboard.h" +#include "XScreen.h" #include "CLock.h" +#include "CThread.h" #include "CLog.h" #include "CString.h" -#include "CThread.h" -#include "XScreen.h" -#include -#include -#include +#include +#include // // CXWindowsScreen @@ -18,10 +17,10 @@ CXWindowsScreen* CXWindowsScreen::s_screen = NULL; CXWindowsScreen::CXWindowsScreen() : - m_display(NULL), - m_root(None), - m_w(0), m_h(0), - m_stop(false) + m_display(NULL), + m_root(None), + m_w(0), m_h(0), + m_stop(false) { assert(s_screen == NULL); s_screen = this; @@ -35,7 +34,8 @@ CXWindowsScreen::~CXWindowsScreen() s_screen = NULL; } -void CXWindowsScreen::openDisplay() +void +CXWindowsScreen::openDisplay() { assert(m_display == NULL); @@ -51,11 +51,12 @@ void CXWindowsScreen::openDisplay() // open the display log((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); m_display = XOpenDisplay(display); - if (m_display == NULL) + if (m_display == NULL) { throw XScreenOpenFailure(); + } // get default screen - m_screen = DefaultScreen(m_display); + m_screen = DefaultScreen(m_display); Screen* screen = ScreenOfDisplay(m_display, m_screen); // get screen size @@ -75,7 +76,8 @@ void CXWindowsScreen::openDisplay() } } -void CXWindowsScreen::closeDisplay() +void +CXWindowsScreen::closeDisplay() { CLock lock(&m_mutex); @@ -96,20 +98,24 @@ void CXWindowsScreen::closeDisplay() XSetIOErrorHandler(NULL); } -int CXWindowsScreen::getScreen() const +int +CXWindowsScreen::getScreen() const { assert(m_display != NULL); return m_screen; } -Window CXWindowsScreen::getRoot() const +Window +CXWindowsScreen::getRoot() const { assert(m_display != NULL); return m_root; } -void CXWindowsScreen::getScreenSize( - SInt32* w, SInt32* h) const +void +CXWindowsScreen::getScreenSize( + SInt32* w, + SInt32* h) const { assert(m_display != NULL); assert(w != NULL && h != NULL); @@ -118,7 +124,8 @@ void CXWindowsScreen::getScreenSize( *h = m_h; } -Cursor CXWindowsScreen::createBlankCursor() const +Cursor +CXWindowsScreen::createBlankCursor() const { // this seems just a bit more complicated than really necessary @@ -153,7 +160,9 @@ Cursor CXWindowsScreen::createBlankCursor() const return cursor; } -bool CXWindowsScreen::getEvent(XEvent* xevent) const +bool +CXWindowsScreen::getEvent( + XEvent* xevent) const { // wait for an event in a cancellable way and don't lock the // display while we're waiting. @@ -182,13 +191,15 @@ bool CXWindowsScreen::getEvent(XEvent* xevent) const } } -void CXWindowsScreen::doStop() +void +CXWindowsScreen::doStop() { // caller must have locked display m_stop = true; } -ClipboardID CXWindowsScreen::getClipboardID(Atom selection) const +ClipboardID +CXWindowsScreen::getClipboardID(Atom selection) const { for (ClipboardID id = 0; id < kClipboardEnd; ++id) { if (m_clipboard[id] != NULL && @@ -199,27 +210,31 @@ ClipboardID CXWindowsScreen::getClipboardID(Atom selection) const return kClipboardEnd; } -void CXWindowsScreen::onUnexpectedClose() +void +CXWindowsScreen::onUnexpectedClose() { // do nothing } -bool CXWindowsScreen::processEvent(XEvent* xevent) +bool +CXWindowsScreen::processEvent( + XEvent* xevent) { switch (xevent->type) { - case SelectionClear: { - // we just lost the selection. that means someone else - // grabbed the selection so this screen is now the - // selection owner. report that to the subclass. - ClipboardID id = getClipboardID(xevent->xselectionclear.selection); - if (id != kClipboardEnd) { - log((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); - m_clipboard[id]->lost(xevent->xselectionclear.time); - onLostClipboard(id); - return true; + case SelectionClear: + { + // we just lost the selection. that means someone else + // grabbed the selection so this screen is now the + // selection owner. report that to the subclass. + ClipboardID id = getClipboardID(xevent->xselectionclear.selection); + if (id != kClipboardEnd) { + log((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); + m_clipboard[id]->lost(xevent->xselectionclear.time); + onLostClipboard(id); + return true; + } } break; - } case SelectionNotify: // notification of selection transferred. we shouldn't @@ -234,28 +249,30 @@ bool CXWindowsScreen::processEvent(XEvent* xevent) } return true; - case SelectionRequest: { - // somebody is asking for clipboard data - ClipboardID id = getClipboardID(xevent->xselectionrequest.selection); - if (id != kClipboardEnd) { - CLock lock(&m_mutex); - m_clipboard[id]->addRequest( + case SelectionRequest: + { + // somebody is asking for clipboard data + ClipboardID id = getClipboardID( + xevent->xselectionrequest.selection); + if (id != kClipboardEnd) { + CLock lock(&m_mutex); + m_clipboard[id]->addRequest( xevent->xselectionrequest.owner, xevent->xselectionrequest.requestor, xevent->xselectionrequest.target, xevent->xselectionrequest.time, xevent->xselectionrequest.property); - return true; + return true; + } } break; - } case PropertyNotify: // property delete may be part of a selection conversion if (xevent->xproperty.state == PropertyDelete) { processClipboardRequest(xevent->xproperty.window, - xevent->xproperty.time, - xevent->xproperty.atom); + xevent->xproperty.time, + xevent->xproperty.atom); return true; } break; @@ -270,9 +287,10 @@ bool CXWindowsScreen::processEvent(XEvent* xevent) return false; } -bool CXWindowsScreen::setDisplayClipboard( - ClipboardID id, - const IClipboard* clipboard) +bool +CXWindowsScreen::setDisplayClipboard( + ClipboardID id, + const IClipboard* clipboard) { CLock lock(&m_mutex); @@ -300,9 +318,10 @@ bool CXWindowsScreen::setDisplayClipboard( } } -bool CXWindowsScreen::getDisplayClipboard( - ClipboardID id, - IClipboard* clipboard) const +bool +CXWindowsScreen::getDisplayClipboard( + ClipboardID id, + IClipboard* clipboard) const { assert(clipboard != NULL); @@ -322,9 +341,11 @@ bool CXWindowsScreen::getDisplayClipboard( return CClipboard::copy(clipboard, m_clipboard[id], timestamp); } -void CXWindowsScreen::processClipboardRequest( - Window requestor, - Time time, Atom property) +void +CXWindowsScreen::processClipboardRequest( + Window requestor, + Time time, + Atom property) { CLock lock(&m_mutex); @@ -337,8 +358,9 @@ void CXWindowsScreen::processClipboardRequest( } } -void CXWindowsScreen::destroyClipboardRequest( - Window requestor) +void +CXWindowsScreen::destroyClipboardRequest( + Window requestor) { CLock lock(&m_mutex); @@ -351,7 +373,9 @@ void CXWindowsScreen::destroyClipboardRequest( } } -int CXWindowsScreen::ioErrorHandler(Display*) +int +CXWindowsScreen::ioErrorHandler( + Display*) { // the display has disconnected, probably because X is shutting // down. X forces us to exit at this point. that's arguably @@ -371,9 +395,10 @@ int CXWindowsScreen::ioErrorHandler(Display*) // CXWindowsScreen::CDisplayLock // -CXWindowsScreen::CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : - m_mutex(&screen->m_mutex), - m_display(screen->m_display) +CXWindowsScreen::CDisplayLock::CDisplayLock( + const CXWindowsScreen* screen) : + m_mutex(&screen->m_mutex), + m_display(screen->m_display) { assert(m_display != NULL); diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index a100d22a..404d77b8 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -1,7 +1,6 @@ #ifndef CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H -#include "BasicTypes.h" #include "ClipboardTypes.h" #include "CMutex.h" #include @@ -58,11 +57,11 @@ protected: // set the contents of the clipboard (i.e. primary selection) bool setDisplayClipboard(ClipboardID, - const IClipboard* clipboard); + const IClipboard* clipboard); // copy the clipboard contents to clipboard bool getDisplayClipboard(ClipboardID, - IClipboard* clipboard) const; + IClipboard* clipboard) const; // called by openDisplay() to allow subclasses to prepare the display. // the display is locked and passed to the subclass. @@ -93,7 +92,7 @@ private: // continue processing a selection request void processClipboardRequest(Window window, - Time time, Atom property); + Time time, Atom property); // terminate a selection request void destroyClipboardRequest(Window window); diff --git a/platform/CXWindowsUtil.cpp b/platform/CXWindowsUtil.cpp index 64b46a44..6dbb5f7e 100644 --- a/platform/CXWindowsUtil.cpp +++ b/platform/CXWindowsUtil.cpp @@ -1,18 +1,21 @@ #include "CXWindowsUtil.h" -#include "CLog.h" #include "CThread.h" -#include +#include "CLog.h" #include // // CXWindowsUtil // -bool CXWindowsUtil::getWindowProperty( - Display* display, - Window window, Atom property, - CString* data, Atom* type, - int* format, bool deleteProperty) +bool +CXWindowsUtil::getWindowProperty( + Display* display, + Window window, + Atom property, + CString* data, + Atom* type, + int* format, + bool deleteProperty) { assert(display != NULL); assert(data != NULL); @@ -84,11 +87,15 @@ bool CXWindowsUtil::getWindowProperty( return true; } -bool CXWindowsUtil::setWindowProperty( - Display* display, - Window window, Atom property, - const void* vdata, UInt32 size, - Atom type, SInt32 format) +bool +CXWindowsUtil::setWindowProperty( + Display* display, + Window window, + Atom property, + const void* vdata, + UInt32 size, + Atom type, + SInt32 format) { const UInt32 length = 4 * XMaxRequestSize(display); const unsigned char* data = reinterpret_cast(vdata); @@ -100,8 +107,9 @@ bool CXWindowsUtil::setWindowProperty( // how much data to send in first chunk? UInt32 chunkSize = size; - if (chunkSize > length) + if (chunkSize > length) { chunkSize = length; + } // send first chunk XChangeProperty(display, window, property, @@ -113,8 +121,9 @@ bool CXWindowsUtil::setWindowProperty( size -= chunkSize; while (!error && size > 0) { chunkSize = size; - if (chunkSize > length) + if (chunkSize > length) { chunkSize = length; + } XChangeProperty(display, window, property, type, format, PropModeAppend, data, chunkSize / datumSize); @@ -125,8 +134,10 @@ bool CXWindowsUtil::setWindowProperty( return !error; } -Time CXWindowsUtil::getCurrentTime( - Display* display, Window window) +Time +CXWindowsUtil::getCurrentTime( + Display* display, + Window window) { // select property events on window XWindowAttributes attr; @@ -162,8 +173,11 @@ Time CXWindowsUtil::getCurrentTime( return xevent.xproperty.time; } -Bool CXWindowsUtil::propertyNotifyPredicate( - Display*, XEvent* xevent, XPointer arg) +Bool +CXWindowsUtil::propertyNotifyPredicate( + Display*, + XEvent* xevent, + XPointer arg) { CPropertyNotifyPredicateInfo* filter = reinterpret_cast(arg); @@ -185,12 +199,15 @@ CXWindowsUtil::CErrorLock::CErrorLock() install(&CXWindowsUtil::CErrorLock::ignoreHandler, NULL); } -CXWindowsUtil::CErrorLock::CErrorLock(bool* flag) +CXWindowsUtil::CErrorLock::CErrorLock( + bool* flag) { install(&CXWindowsUtil::CErrorLock::saveHandler, flag); } -CXWindowsUtil::CErrorLock::CErrorLock(ErrorHandler handler, void* data) +CXWindowsUtil::CErrorLock::CErrorLock( + ErrorHandler handler, + void* data) { install(handler, data); } @@ -201,8 +218,10 @@ CXWindowsUtil::CErrorLock::~CErrorLock() s_top = m_next; } -void CXWindowsUtil::CErrorLock::install( - ErrorHandler handler, void* data) +void +CXWindowsUtil::CErrorLock::install( + ErrorHandler handler, + void* data) { m_handler = handler; m_userData = data; @@ -212,8 +231,10 @@ void CXWindowsUtil::CErrorLock::install( s_top = this; } -int CXWindowsUtil::CErrorLock::internalHandler( - Display* display, XErrorEvent* event) +int +CXWindowsUtil::CErrorLock::internalHandler( + Display* display, + XErrorEvent* event) { if (s_top != NULL && s_top->m_handler != NULL) { s_top->m_handler(display, event, s_top->m_userData); @@ -221,14 +242,20 @@ int CXWindowsUtil::CErrorLock::internalHandler( return 0; } -void CXWindowsUtil::CErrorLock::ignoreHandler( - Display*, XErrorEvent*, void*) +void +CXWindowsUtil::CErrorLock::ignoreHandler( + Display*, + XErrorEvent*, + void*) { // do nothing } -void CXWindowsUtil::CErrorLock::saveHandler( - Display*, XErrorEvent*, void* flag) +void +CXWindowsUtil::CErrorLock::saveHandler( + Display*, + XErrorEvent*, + void* flag) { *reinterpret_cast(flag) = true; } diff --git a/platform/CXWindowsUtil.h b/platform/CXWindowsUtil.h index 82414dd5..3f01b667 100644 --- a/platform/CXWindowsUtil.h +++ b/platform/CXWindowsUtil.h @@ -1,20 +1,20 @@ #ifndef CXWINDOWSUTIL_H #define CXWINDOWSUTIL_H -#include "BasicTypes.h" #include "CString.h" +#include "BasicTypes.h" #include class CXWindowsUtil { public: static bool getWindowProperty(Display*, - Window window, Atom property, - CString* data, Atom* type, - SInt32* format, bool deleteProperty); + Window window, Atom property, + CString* data, Atom* type, + SInt32* format, bool deleteProperty); static bool setWindowProperty(Display*, - Window window, Atom property, - const void* data, UInt32 size, - Atom type, SInt32 format); + Window window, Atom property, + const void* data, UInt32 size, + Atom type, SInt32 format); static Time getCurrentTime(Display*, Window); // class to set an X error handler in the c'tor and restore the @@ -55,7 +55,7 @@ private: }; static Bool propertyNotifyPredicate(Display*, - XEvent* xevent, XPointer arg); + XEvent* xevent, XPointer arg); }; #endif diff --git a/platform/IPlatform.h b/platform/IPlatform.h index 7012304e..a5d0bde9 100644 --- a/platform/IPlatform.h +++ b/platform/IPlatform.h @@ -1,9 +1,8 @@ #ifndef IPLATFORM_H #define IPLATFORM_H -#include "BasicTypes.h" -#include "CString.h" #include "IInterface.h" +#include "CString.h" class IPlatform : public IInterface { public: @@ -16,9 +15,9 @@ public: // include the name of program as the first argument. // FIXME -- throw on error? will get better error messages that way. virtual bool installDaemon(const char* name, - const char* description, - const char* pathname, - const char* commandLine) = 0; + const char* description, + const char* pathname, + const char* commandLine) = 0; virtual bool uninstallDaemon(const char* name) = 0; // daemonize. this should have the side effect of sending log @@ -67,8 +66,8 @@ public: // is longer than allowed by the system. we'll rely on the // system calls to tell us that. virtual CString addPathComponent( - const CString& prefix, - const CString& suffix) const = 0; + const CString& prefix, + const CString& suffix) const = 0; }; #endif diff --git a/server/CConfig.cpp b/server/CConfig.cpp index 167db361..cc3cfe46 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -1,8 +1,8 @@ #include "CConfig.h" #include "ProtocolTypes.h" +#include "XSocket.h" #include "stdistream.h" #include "stdostream.h" -#include // // CConfig @@ -18,7 +18,9 @@ CConfig::~CConfig() // do nothing } -bool CConfig::addScreen(const CString& name) +bool +CConfig::addScreen( + const CString& name) { // alias name must not exist if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) { @@ -34,7 +36,9 @@ bool CConfig::addScreen(const CString& name) return true; } -void CConfig::removeScreen(const CString& name) +void +CConfig::removeScreen( + const CString& name) { // get canonical name and find cell CString canonical = getCanonicalName(name); @@ -67,14 +71,17 @@ void CConfig::removeScreen(const CString& name) } } -void CConfig::removeAllScreens() +void +CConfig::removeAllScreens() { m_map.clear(); m_nameToCanonicalName.clear(); } -bool CConfig::addAlias(const CString& canonical, - const CString& alias) +bool +CConfig::addAlias( + const CString& canonical, + const CString& alias) { // alias name must not exist if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) { @@ -92,7 +99,9 @@ bool CConfig::addAlias(const CString& canonical, return true; } -bool CConfig::removeAlias(const CString& alias) +bool +CConfig::removeAlias( + const CString& alias) { // must not be a canonical name if (m_map.find(alias) != m_map.end()) { @@ -111,7 +120,8 @@ bool CConfig::removeAlias(const CString& alias) return true; } -void CConfig::removeAllAliases() +void +CConfig::removeAllAliases() { // remove all names m_nameToCanonicalName.clear(); @@ -124,9 +134,11 @@ void CConfig::removeAllAliases() } } -bool CConfig::connect(const CString& srcName, - EDirection srcSide, - const CString& dstName) +bool +CConfig::connect( + const CString& srcName, + EDirection srcSide, + const CString& dstName) { // find source cell CCellMap::iterator index = m_map.find(getCanonicalName(srcName)); @@ -142,8 +154,10 @@ bool CConfig::connect(const CString& srcName, return true; } -bool CConfig::disconnect(const CString& srcName, - EDirection srcSide) +bool +CConfig::disconnect( + const CString& srcName, + EDirection srcSide) { // find source cell CCellMap::iterator index = m_map.find(srcName); @@ -157,17 +171,23 @@ bool CConfig::disconnect(const CString& srcName, return true; } -void CConfig::setSynergyAddress(const CNetworkAddress& addr) +void +CConfig::setSynergyAddress( + const CNetworkAddress& addr) { m_synergyAddress = addr; } -void CConfig::setHTTPAddress(const CNetworkAddress& addr) +void +CConfig::setHTTPAddress( + const CNetworkAddress& addr) { m_httpAddress = addr; } -bool CConfig::isValidScreenName(const CString& name) const +bool +CConfig::isValidScreenName( + const CString& name) const { // name is valid if matches validname // name ::= [A-Za-z0-9] | [A-Za-z0-9][-A-Za-z0-9]*[A-Za-z0-9] @@ -211,27 +231,35 @@ bool CConfig::isValidScreenName(const CString& name) const return true; } -CConfig::const_iterator CConfig::begin() const +CConfig::const_iterator +CConfig::begin() const { return const_iterator(m_map.begin()); } -CConfig::const_iterator CConfig::end() const +CConfig::const_iterator +CConfig::end() const { return const_iterator(m_map.end()); } -bool CConfig::isScreen(const CString& name) const +bool +CConfig::isScreen( + const CString& name) const { return (m_nameToCanonicalName.count(name) > 0); } -bool CConfig::isCanonicalName(const CString& name) const +bool +CConfig::isCanonicalName( + const CString& name) const { return CStringUtil::CaselessCmp::equal(getCanonicalName(name), name); } -CString CConfig::getCanonicalName(const CString& name) const +CString +CConfig::getCanonicalName( + const CString& name) const { CNameMap::const_iterator index = m_nameToCanonicalName.find(name); if (index == m_nameToCanonicalName.end()) { @@ -242,8 +270,10 @@ CString CConfig::getCanonicalName(const CString& name) const } } -CString CConfig::getNeighbor(const CString& srcName, - EDirection srcSide) const +CString +CConfig::getNeighbor( + const CString& srcName, + EDirection srcSide) const { // find source cell CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); @@ -256,23 +286,30 @@ CString CConfig::getNeighbor(const CString& srcName, srcSide - kFirstDirection]); } -const CNetworkAddress& CConfig::getSynergyAddress() const +const CNetworkAddress& +CConfig::getSynergyAddress() const { return m_synergyAddress; } -const CNetworkAddress& CConfig::getHTTPAddress() const +const CNetworkAddress& +CConfig::getHTTPAddress() const { return m_httpAddress; } -const char* CConfig::dirName(EDirection dir) +const char* +CConfig::dirName( + EDirection dir) { static const char* s_name[] = { "left", "right", "top", "bottom" }; return s_name[dir - kFirstDirection]; } -bool CConfig::readLine(std::istream& s, CString& line) +bool +CConfig::readLine( + std::istream& s, + CString& line) { s >> std::ws; while (std::getline(s, line)) { @@ -295,7 +332,9 @@ bool CConfig::readLine(std::istream& s, CString& line) return false; } -void CConfig::readSection(std::istream& s) +void +CConfig::readSection( + std::istream& s) { static const char s_section[] = "section:"; static const char s_network[] = "network"; @@ -343,7 +382,9 @@ void CConfig::readSection(std::istream& s) } } -void CConfig::readSectionNetwork(std::istream& s) +void +CConfig::readSectionNetwork( + std::istream& s) { CString line; CString name; @@ -398,7 +439,9 @@ void CConfig::readSectionNetwork(std::istream& s) throw XConfigRead("unexpected end of screens section"); } -void CConfig::readSectionScreens(std::istream& s) +void +CConfig::readSectionScreens( + std::istream& s) { CString line; CString name; @@ -433,7 +476,9 @@ void CConfig::readSectionScreens(std::istream& s) throw XConfigRead("unexpected end of screens section"); } -void CConfig::readSectionLinks(std::istream& s) +void +CConfig::readSectionLinks( + std::istream& s) { CString line; CString screen; @@ -513,7 +558,9 @@ void CConfig::readSectionLinks(std::istream& s) throw XConfigRead("unexpected end of links section"); } -void CConfig::readSectionAliases(std::istream& s) +void +CConfig::readSectionAliases( + std::istream& s) { CString line; CString screen; @@ -559,7 +606,10 @@ void CConfig::readSectionAliases(std::istream& s) // CConfig I/O // -std::istream& operator>>(std::istream& s, CConfig& config) +std::istream& +operator>>( + std::istream& s, + CConfig& config) { // FIXME -- should track line and column to improve error reporting @@ -571,7 +621,10 @@ std::istream& operator>>(std::istream& s, CConfig& config) return s; } -std::ostream& operator<<(std::ostream& s, const CConfig& config) +std::ostream& +operator<<( + std::ostream& s, + const CConfig& config) { // network section s << "section: network" << std::endl; @@ -657,7 +710,9 @@ std::ostream& operator<<(std::ostream& s, const CConfig& config) // CConfig I/O exceptions // -XConfigRead::XConfigRead(const CString& error) : m_error(error) +XConfigRead::XConfigRead( + const CString& error) : + m_error(error) { // do nothing } @@ -667,7 +722,8 @@ XConfigRead::~XConfigRead() // do nothing } -CString XConfigRead::getWhat() const throw() +CString +XConfigRead::getWhat() const throw() { return m_error; } diff --git a/server/CConfig.h b/server/CConfig.h index 73f4a911..a12d1a03 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -1,13 +1,11 @@ #ifndef CCONFIG_H #define CCONFIG_H -#include "BasicTypes.h" -#include "CString.h" #include "CNetworkAddress.h" #include "XBase.h" -#include #include "stdmap.h" #include "stdset.h" +#include class CConfig; @@ -85,17 +83,17 @@ public: // or the canonical name is unknown. removeAlias() fails if // the alias is unknown or a canonical name. bool addAlias(const CString& canonical, - const CString& alias); + const CString& alias); bool removeAlias(const CString& alias); void removeAllAliases(); // connect/disconnect edges. both return false if srcName is // unknown. bool connect(const CString& srcName, - EDirection srcSide, - const CString& dstName); + EDirection srcSide, + const CString& dstName); bool disconnect(const CString& srcName, - EDirection srcSide); + EDirection srcSide); // set the synergy and http listen addresses. there are no // default addresses. diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index 95cb6d43..36f46967 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -1,14 +1,13 @@ #include "CHTTPServer.h" -#include "CHTTPProtocol.h" -#include "XHTTP.h" -#include "CServer.h" #include "CConfig.h" -#include "CLog.h" -#include "XThread.h" +#include "CHTTPProtocol.h" +#include "CServer.h" +#include "XHTTP.h" #include "ISocket.h" +#include "XThread.h" +#include "CLog.h" #include "stdset.h" #include "stdsstream.h" -#include // // CHTTPServer @@ -19,7 +18,9 @@ // malicious client from causing us to use too much memory. const UInt32 CHTTPServer::s_maxRequestSize = 32768; -CHTTPServer::CHTTPServer(CServer* server) : m_server(server) +CHTTPServer::CHTTPServer( + CServer* server) : + m_server(server) { // do nothing } @@ -29,7 +30,9 @@ CHTTPServer::~CHTTPServer() // do nothing } -void CHTTPServer::processRequest(ISocket* socket) +void +CHTTPServer::processRequest( + ISocket* socket) { assert(socket != NULL); @@ -88,9 +91,10 @@ void CHTTPServer::processRequest(ISocket* socket) } } -void CHTTPServer::doProcessRequest( - CHTTPRequest& request, - CHTTPReply& reply) +void +CHTTPServer::doProcessRequest( + CHTTPRequest& request, + CHTTPReply& reply) { reply.m_majorVersion = request.m_majorVersion; reply.m_minorVersion = request.m_minorVersion; @@ -118,9 +122,10 @@ void CHTTPServer::doProcessRequest( } } -void CHTTPServer::doProcessGetEditMap( - CHTTPRequest& /*request*/, - CHTTPReply& reply) +void +CHTTPServer::doProcessGetEditMap( + CHTTPRequest& /*request*/, + CHTTPReply& reply) { static const char* s_editMapProlog1 = "\r\n" @@ -228,9 +233,10 @@ void CHTTPServer::doProcessGetEditMap( reply.m_body += s_editMapEpilog; } -void CHTTPServer::doProcessPostEditMap( - CHTTPRequest& request, - CHTTPReply& reply) +void +CHTTPServer::doProcessPostEditMap( + CHTTPRequest& request, + CHTTPReply& reply) { typedef std::vector ScreenArray; typedef std::set ScreenSet; @@ -323,8 +329,11 @@ void CHTTPServer::doProcessPostEditMap( } } -bool CHTTPServer::parseXY( - const CString& xy, SInt32& x, SInt32& y) +bool +CHTTPServer::parseXY( + const CString& xy, + SInt32& x, + SInt32& y) { std::istringstream s(xy); char delimiter; @@ -339,7 +348,9 @@ bool CHTTPServer::parseXY( // CHTTPServer::CScreenArray // -CHTTPServer::CScreenArray::CScreenArray() : m_w(0), m_h(0) +CHTTPServer::CScreenArray::CScreenArray() : + m_w(0), + m_h(0) { // do nothing } @@ -349,7 +360,10 @@ CHTTPServer::CScreenArray::~CScreenArray() // do nothing } -void CHTTPServer::CScreenArray::resize(SInt32 w, SInt32 h) +void +CHTTPServer::CScreenArray::resize( + SInt32 w, + SInt32 h) { m_screens.clear(); m_screens.resize(w * h); @@ -357,7 +371,9 @@ void CHTTPServer::CScreenArray::resize(SInt32 w, SInt32 h) m_h = h; } -void CHTTPServer::CScreenArray::insertRow(SInt32 i) +void +CHTTPServer::CScreenArray::insertRow( + SInt32 i) { assert(i >= 0 && i <= m_h); @@ -379,7 +395,9 @@ void CHTTPServer::CScreenArray::insertRow(SInt32 i) ++m_h; } -void CHTTPServer::CScreenArray::insertColumn(SInt32 i) +void +CHTTPServer::CScreenArray::insertColumn( + SInt32 i) { assert(i >= 0 && i <= m_w); @@ -399,7 +417,9 @@ void CHTTPServer::CScreenArray::insertColumn(SInt32 i) ++m_w; } -void CHTTPServer::CScreenArray::eraseRow(SInt32 i) +void +CHTTPServer::CScreenArray::eraseRow( + SInt32 i) { assert(i >= 0 && i < m_h); @@ -421,7 +441,9 @@ void CHTTPServer::CScreenArray::eraseRow(SInt32 i) --m_h; } -void CHTTPServer::CScreenArray::eraseColumn(SInt32 i) +void +CHTTPServer::CScreenArray::eraseColumn( + SInt32 i) { assert(i >= 0 && i < m_w); @@ -441,7 +463,9 @@ void CHTTPServer::CScreenArray::eraseColumn(SInt32 i) --m_w; } -void CHTTPServer::CScreenArray::rotateRows(SInt32 i) +void +CHTTPServer::CScreenArray::rotateRows( + SInt32 i) { // nothing to do if no rows if (m_h == 0) { @@ -471,7 +495,9 @@ void CHTTPServer::CScreenArray::rotateRows(SInt32 i) } } -void CHTTPServer::CScreenArray::rotateColumns(SInt32 i) +void +CHTTPServer::CScreenArray::rotateColumns( + SInt32 i) { // nothing to do if no columns if (m_h == 0) { @@ -501,13 +527,19 @@ void CHTTPServer::CScreenArray::rotateColumns(SInt32 i) } } -void CHTTPServer::CScreenArray::remove(SInt32 x, SInt32 y) +void +CHTTPServer::CScreenArray::remove( + SInt32 x, + SInt32 y) { set(x, y, CString()); } -void CHTTPServer::CScreenArray::set( - SInt32 x, SInt32 y, const CString& name) +void +CHTTPServer::CScreenArray::set( + SInt32 x, + SInt32 y, + const CString& name) { assert(x >= 0 && x < m_w); assert(y >= 0 && y < m_h); @@ -515,8 +547,10 @@ void CHTTPServer::CScreenArray::set( m_screens[x + y * m_w] = name; } -bool CHTTPServer::CScreenArray::isAllowed( - SInt32 x, SInt32 y) const +bool +CHTTPServer::CScreenArray::isAllowed( + SInt32 x, + SInt32 y) const { assert(x >= 0 && x < m_w); assert(y >= 0 && y < m_h); @@ -536,8 +570,10 @@ bool CHTTPServer::CScreenArray::isAllowed( return false; } -bool CHTTPServer::CScreenArray::isSet( - SInt32 x, SInt32 y) const +bool +CHTTPServer::CScreenArray::isSet( + SInt32 x, + SInt32 y) const { assert(x >= 0 && x < m_w); assert(y >= 0 && y < m_h); @@ -545,8 +581,10 @@ bool CHTTPServer::CScreenArray::isSet( return !m_screens[x + y * m_w].empty(); } -CString CHTTPServer::CScreenArray::get( - SInt32 x, SInt32 y) const +CString +CHTTPServer::CScreenArray::get( + SInt32 x, + SInt32 y) const { assert(x >= 0 && x < m_w); assert(y >= 0 && y < m_h); @@ -554,9 +592,11 @@ CString CHTTPServer::CScreenArray::get( return m_screens[x + y * m_w]; } -bool CHTTPServer::CScreenArray::find( - const CString& name, - SInt32& xOut, SInt32& yOut) const +bool +CHTTPServer::CScreenArray::find( + const CString& name, + SInt32& xOut, + SInt32& yOut) const { for (SInt32 y = 0; y < m_h; ++y) { for (SInt32 x = 0; x < m_w; ++x) { @@ -570,7 +610,8 @@ bool CHTTPServer::CScreenArray::find( return false; } -bool CHTTPServer::CScreenArray::isValid() const +bool +CHTTPServer::CScreenArray::isValid() const { SInt32 count = 0, isolated = 0; for (SInt32 y = 0; y < m_h; ++y) { @@ -586,8 +627,9 @@ bool CHTTPServer::CScreenArray::isValid() const return (count <= 1 || isolated == 0); } -bool CHTTPServer::CScreenArray::convertFrom( - const CConfig& config) +bool +CHTTPServer::CScreenArray::convertFrom( + const CConfig& config) { typedef std::set ScreenSet; @@ -717,8 +759,9 @@ bool CHTTPServer::CScreenArray::convertFrom( return true; } -void CHTTPServer::CScreenArray::convertTo( - CConfig& config) const +void +CHTTPServer::CScreenArray::convertTo( + CConfig& config) const { config.removeAllScreens(); diff --git a/server/CHTTPServer.h b/server/CHTTPServer.h index 7c0cf311..9d9ac35e 100644 --- a/server/CHTTPServer.h +++ b/server/CHTTPServer.h @@ -1,8 +1,8 @@ #ifndef CHTTPSERVER_H #define CHTTPSERVER_H -#include "BasicTypes.h" #include "CString.h" +#include "BasicTypes.h" #include "stdvector.h" class CServer; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index b85c6909..0f81690b 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -1,29 +1,28 @@ #include "CMSWindowsPrimaryScreen.h" -#include "CMSWindowsClipboard.h" #include "CServer.h" +#include "CMSWindowsClipboard.h" #include "CPlatform.h" #include "XScreen.h" #include "XSynergy.h" -#include "CLog.h" #include "CThread.h" -#include -#include +#include "CLog.h" +#include // // CMSWindowsPrimaryScreen // CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : - m_server(NULL), - m_threadID(0), - m_desk(NULL), - m_deskName(), - m_window(NULL), - m_active(false), - m_mark(0), - m_markReceived(0), - m_nextClipboardWindow(NULL), - m_clipboardOwner(NULL) + m_server(NULL), + m_threadID(0), + m_desk(NULL), + m_deskName(), + m_window(NULL), + m_active(false), + m_mark(0), + m_markReceived(0), + m_nextClipboardWindow(NULL), + m_clipboardOwner(NULL) { // load the hook library m_hookLibrary = LoadLibrary("synrgyhk"); @@ -61,7 +60,8 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() FreeLibrary(m_hookLibrary); } -void CMSWindowsPrimaryScreen::run() +void +CMSWindowsPrimaryScreen::run() { // must call run() from same thread as open() assert(m_threadID == GetCurrentThreadId()); @@ -87,12 +87,15 @@ void CMSWindowsPrimaryScreen::run() } } -void CMSWindowsPrimaryScreen::stop() +void +CMSWindowsPrimaryScreen::stop() { doStop(); } -void CMSWindowsPrimaryScreen::open(CServer* server) +void +CMSWindowsPrimaryScreen::open( + CServer* server) { assert(m_server == NULL); assert(server != NULL); @@ -126,7 +129,8 @@ void CMSWindowsPrimaryScreen::open(CServer* server) enterNoWarp(); } -void CMSWindowsPrimaryScreen::close() +void +CMSWindowsPrimaryScreen::close() { assert(m_server != NULL); @@ -137,7 +141,10 @@ void CMSWindowsPrimaryScreen::close() m_server = NULL; } -void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) +void +CMSWindowsPrimaryScreen::enter( + SInt32 x, + SInt32 y) { log((CLOG_INFO "entering primary at %d,%d", x, y)); assert(m_active == true); @@ -149,7 +156,8 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) warpCursor(x, y); } -bool CMSWindowsPrimaryScreen::leave() +bool +CMSWindowsPrimaryScreen::leave() { log((CLOG_INFO "leaving primary")); assert(m_active == false); @@ -219,7 +227,8 @@ bool CMSWindowsPrimaryScreen::leave() return true; } -void CMSWindowsPrimaryScreen::onConfigure() +void +CMSWindowsPrimaryScreen::onConfigure() { if ((m_is95Family || m_desk != NULL) && !m_active) { SInt32 w, h; @@ -229,14 +238,19 @@ void CMSWindowsPrimaryScreen::onConfigure() } } -void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +void +CMSWindowsPrimaryScreen::warpCursor( + SInt32 x, + SInt32 y) { // set the cursor position without generating an event SetCursorPos(x, y); } -void CMSWindowsPrimaryScreen::setClipboard( - ClipboardID /*id*/, const IClipboard* src) +void +CMSWindowsPrimaryScreen::setClipboard( + ClipboardID /*id*/, + const IClipboard* src) { assert(m_window != NULL); @@ -244,8 +258,9 @@ void CMSWindowsPrimaryScreen::setClipboard( CClipboard::copy(&dst, src); } -void CMSWindowsPrimaryScreen::grabClipboard( - ClipboardID /*id*/) +void +CMSWindowsPrimaryScreen::grabClipboard( + ClipboardID /*id*/) { assert(m_window != NULL); @@ -255,19 +270,24 @@ void CMSWindowsPrimaryScreen::grabClipboard( } } -void CMSWindowsPrimaryScreen::getSize( - SInt32* width, SInt32* height) const +void +CMSWindowsPrimaryScreen::getSize( + SInt32* width, + SInt32* height) const { getScreenSize(width, height); } -SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const +SInt32 +CMSWindowsPrimaryScreen::getJumpZoneSize() const { return 1; } -void CMSWindowsPrimaryScreen::getClipboard( - ClipboardID /*id*/, IClipboard* dst) const +void +CMSWindowsPrimaryScreen::getClipboard( + ClipboardID /*id*/, + IClipboard* dst) const { assert(m_window != NULL); @@ -275,7 +295,8 @@ void CMSWindowsPrimaryScreen::getClipboard( CClipboard::copy(dst, &src); } -KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const +KeyModifierMask +CMSWindowsPrimaryScreen::getToggleMask() const { KeyModifierMask mask = 0; if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) @@ -287,7 +308,8 @@ KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const return mask; } -bool CMSWindowsPrimaryScreen::isLockedToScreen() const +bool +CMSWindowsPrimaryScreen::isLockedToScreen() const { // check buttons if (GetAsyncKeyState(VK_LBUTTON) < 0 || @@ -310,7 +332,8 @@ bool CMSWindowsPrimaryScreen::isLockedToScreen() const return false; } -void CMSWindowsPrimaryScreen::onOpenDisplay() +void +CMSWindowsPrimaryScreen::onOpenDisplay() { assert(m_window == NULL); assert(m_server != NULL); @@ -331,7 +354,8 @@ void CMSWindowsPrimaryScreen::onOpenDisplay() } } -void CMSWindowsPrimaryScreen::onCloseDisplay() +void +CMSWindowsPrimaryScreen::onCloseDisplay() { // disconnect from desktop if (m_is95Family) { @@ -348,7 +372,9 @@ void CMSWindowsPrimaryScreen::onCloseDisplay() assert(m_desk == NULL); } -bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) +bool +CMSWindowsPrimaryScreen::onPreTranslate( + MSG* msg) { // handle event switch (msg->message) { @@ -477,9 +503,12 @@ bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) return false; } -LRESULT CMSWindowsPrimaryScreen::onEvent( - HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) +LRESULT +CMSWindowsPrimaryScreen::onEvent( + HWND hwnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) { switch (msg) { case WM_QUERYENDSESSION: @@ -571,7 +600,8 @@ LRESULT CMSWindowsPrimaryScreen::onEvent( return DefWindowProc(hwnd, msg, wParam, lParam); } -void CMSWindowsPrimaryScreen::enterNoWarp() +void +CMSWindowsPrimaryScreen::enterNoWarp() { // not active anymore m_active = false; @@ -595,7 +625,8 @@ void CMSWindowsPrimaryScreen::enterNoWarp() nextMark(); } -void CMSWindowsPrimaryScreen::onEnter() +void +CMSWindowsPrimaryScreen::onEnter() { // restore the active window and hide our window. we can only set // the active window for another thread if we first attach our input @@ -613,7 +644,8 @@ void CMSWindowsPrimaryScreen::onEnter() ShowWindow(m_window, SW_HIDE); } -bool CMSWindowsPrimaryScreen::onLeave() +bool +CMSWindowsPrimaryScreen::onLeave() { // remember the active window before we leave. GetActiveWindow() // will only return the active window for the thread's queue (i.e. @@ -643,7 +675,8 @@ bool CMSWindowsPrimaryScreen::onLeave() return true; } -void CMSWindowsPrimaryScreen::nextMark() +void +CMSWindowsPrimaryScreen::nextMark() { // next mark ++m_mark; @@ -652,7 +685,8 @@ void CMSWindowsPrimaryScreen::nextMark() PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); } -bool CMSWindowsPrimaryScreen::openDesktop() +bool +CMSWindowsPrimaryScreen::openDesktop() { // install hooks m_install(m_threadID); @@ -687,7 +721,8 @@ bool CMSWindowsPrimaryScreen::openDesktop() return true; } -void CMSWindowsPrimaryScreen::closeDesktop() +void +CMSWindowsPrimaryScreen::closeDesktop() { // destroy old window if (m_window != NULL) { @@ -714,7 +749,9 @@ void CMSWindowsPrimaryScreen::closeDesktop() m_uninstall(); } -bool CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) +bool +CMSWindowsPrimaryScreen::switchDesktop( + HDESK desk) { // did we own the clipboard? bool ownClipboard = (m_clipboardOwner == m_window); @@ -818,7 +855,8 @@ bool CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) return true; } -CString CMSWindowsPrimaryScreen::getCurrentDesktopName() const +CString +CMSWindowsPrimaryScreen::getCurrentDesktopName() const { return m_deskName; } @@ -1083,9 +1121,11 @@ static const KeyID g_virtualKey[] = /* 0xff */ kKeyNone // reserved }; -KeyID CMSWindowsPrimaryScreen::mapKey( - WPARAM vkCode, LPARAM info, - KeyModifierMask* maskOut) +KeyID +CMSWindowsPrimaryScreen::mapKey( + WPARAM vkCode, + LPARAM info, + KeyModifierMask* maskOut) { // note: known microsoft bugs // Q72583 -- MapVirtualKey() maps keypad keys incorrectly @@ -1101,25 +1141,32 @@ KeyID CMSWindowsPrimaryScreen::mapKey( KeyModifierMask mask = 0; if (((m_keys[VK_LSHIFT] | m_keys[VK_RSHIFT] | - m_keys[VK_SHIFT]) & 0x80) != 0) + m_keys[VK_SHIFT]) & 0x80) != 0) { mask |= KeyModifierShift; + } if (((m_keys[VK_LCONTROL] | m_keys[VK_RCONTROL] | - m_keys[VK_CONTROL]) & 0x80) != 0) + m_keys[VK_CONTROL]) & 0x80) != 0) { mask |= KeyModifierControl; + } if (((m_keys[VK_LMENU] | m_keys[VK_RMENU] | - m_keys[VK_MENU]) & 0x80) != 0) + m_keys[VK_MENU]) & 0x80) != 0) { mask |= KeyModifierAlt; + } if (((m_keys[VK_LWIN] | - m_keys[VK_RWIN]) & 0x80) != 0) + m_keys[VK_RWIN]) & 0x80) != 0) { mask |= KeyModifierMeta; - if ((m_keys[VK_CAPITAL] & 0x01) != 0) + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { mask |= KeyModifierCapsLock; - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { mask |= KeyModifierNumLock; - if ((m_keys[VK_SCROLL] & 0x01) != 0) + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { mask |= KeyModifierScrollLock; + } *maskOut = mask; log((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask)); @@ -1133,17 +1180,20 @@ KeyID CMSWindowsPrimaryScreen::mapKey( UINT vkCode2 = MapVirtualKey(scanCode, 3); // work around bug Q72583 (bad num pad conversion in MapVirtualKey()) - if (vkCode >= VK_NUMPAD0 && vkCode <= VK_DIVIDE) + if (vkCode >= VK_NUMPAD0 && vkCode <= VK_DIVIDE) { vkCode2 = vkCode; + } // MapVirtualKey() appears to map VK_LWIN, VK_RWIN, VK_APPS to // some other meaningless virtual key. work around that bug. - else if (vkCode >= VK_LWIN && vkCode <= VK_APPS) + else if (vkCode >= VK_LWIN && vkCode <= VK_APPS) { vkCode2 = vkCode; + } // if MapVirtualKey failed then use original virtual key - else if (vkCode2 == 0) + else if (vkCode2 == 0) { vkCode2 = vkCode; + } // sadly, win32 will not distinguish between the left and right // control and alt keys using the above function. however, we @@ -1223,12 +1273,16 @@ KeyID CMSWindowsPrimaryScreen::mapKey( // set shift state required to generate key BYTE keys[256]; memset(keys, 0, sizeof(keys)); - if (vkCode & 0x0100) +// FIXME -- surely these masks should be different in each if expression + if (vkCode & 0x0100) { keys[VK_SHIFT] = 0x80; - if (vkCode & 0x0100) + } + if (vkCode & 0x0100) { keys[VK_CONTROL] = 0x80; - if (vkCode & 0x0100) + } + if (vkCode & 0x0100) { keys[VK_MENU] = 0x80; + } // strip shift state off of virtual key code vkCode &= 0x00ff; @@ -1245,8 +1299,9 @@ KeyID CMSWindowsPrimaryScreen::mapKey( return kKeyNone; } -ButtonID CMSWindowsPrimaryScreen::mapButton( - WPARAM button) const +ButtonID +CMSWindowsPrimaryScreen::mapButton( + WPARAM button) const { switch (button) { case WM_LBUTTONDOWN: @@ -1266,7 +1321,8 @@ ButtonID CMSWindowsPrimaryScreen::mapButton( } } -void CMSWindowsPrimaryScreen::updateKeys() +void +CMSWindowsPrimaryScreen::updateKeys() { // not using GetKeyboardState() because that doesn't seem to give // up-to-date results. i don't know why that is or why GetKeyState() @@ -1293,8 +1349,10 @@ void CMSWindowsPrimaryScreen::updateKeys() m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); } -void CMSWindowsPrimaryScreen::updateKey( - UINT vkCode, bool press) +void +CMSWindowsPrimaryScreen::updateKey( + UINT vkCode, + bool press) { if (press) { switch (vkCode) { diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index a568171a..c026e466 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -2,10 +2,10 @@ #define CMSWINDOWSPRIMARYSCREEN_H #include "CMSWindowsScreen.h" -#include "IPrimaryScreen.h" -#include "MouseTypes.h" -#include "CString.h" #include "CSynergyHook.h" +#include "MouseTypes.h" +#include "IPrimaryScreen.h" +#include "CString.h" class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { public: @@ -56,7 +56,7 @@ private: // key and button queries KeyID mapKey(WPARAM keycode, LPARAM info, - KeyModifierMask* maskOut); + KeyModifierMask* maskOut); ButtonID mapButton(WPARAM button) const; void updateKeys(); void updateKey(UINT vkCode, bool press); diff --git a/server/CServer.cpp b/server/CServer.cpp index dfaa86da..3bbeef49 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -2,26 +2,25 @@ #include "CHTTPServer.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" -#include "CServerProtocol.h" #include "CProtocolUtil.h" +#include "CServerProtocol.h" #include "IPrimaryScreen.h" -#include "ISocketFactory.h" #include "ProtocolTypes.h" +#include "XScreen.h" +#include "XSynergy.h" #include "CNetworkAddress.h" -#include "ISocket.h" #include "IListenSocket.h" +#include "ISocket.h" +#include "ISocketFactory.h" +#include "XSocket.h" #include "CLock.h" -#include "CLog.h" #include "CThread.h" #include "CTimerThread.h" -#include "CStopwatch.h" -#include "CFunctionJob.h" -#include "TMethodJob.h" -#include "XScreen.h" -#include "XSocket.h" -#include "XSynergy.h" #include "XThread.h" -#include +#include "CFunctionJob.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "TMethodJob.h" #include // hack to work around operator=() bug in STL in g++ prior to v3 @@ -31,32 +30,22 @@ #define assign(_dst, _src, _type) _dst = std::auto_ptr<_type >(_src) #endif - -/* XXX -#include -#include -#include -#include -if (fork() == 0) abort(); -else { wait(0); exit(1); } -*/ - // // CServer // const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; -CServer::CServer(const CString& serverName) : - m_name(serverName), - m_cleanupSize(&m_mutex, 0), - m_primary(NULL), - m_active(NULL), - m_primaryInfo(NULL), - m_seqNum(0), - m_httpServer(NULL), - m_httpAvailable(&m_mutex, - s_httpMaxSimultaneousRequests) +CServer::CServer( + const CString& serverName) : + m_name(serverName), + m_cleanupSize(&m_mutex, 0), + m_primary(NULL), + m_active(NULL), + m_primaryInfo(NULL), + m_seqNum(0), + m_httpServer(NULL), + m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests) { m_socketFactory = NULL; m_securityFactory = NULL; @@ -68,7 +57,8 @@ CServer::~CServer() // do nothing } -void CServer::run() +void +CServer::run() { try { log((CLOG_NOTE "starting server")); @@ -149,12 +139,14 @@ void CServer::run() } } -void CServer::quit() +void +CServer::quit() { m_primary->stop(); } -void CServer::shutdown() +void +CServer::shutdown() { // stop all running threads but don't wait too long since some // threads may be unable to proceed until this thread returns. @@ -167,7 +159,9 @@ void CServer::shutdown() // note -- we do not attempt to close down the primary screen } -bool CServer::setConfig(const CConfig& config) +bool +CServer::setConfig( + const CConfig& config) { typedef std::vector CThreads; CThreads threads; @@ -228,12 +222,15 @@ bool CServer::setConfig(const CConfig& config) return true; } -CString CServer::getPrimaryScreenName() const +CString +CServer::getPrimaryScreenName() const { return m_name; } -void CServer::getConfig(CConfig* config) const +void +CServer::getConfig( + CConfig* config) const { assert(config != NULL); @@ -241,7 +238,8 @@ void CServer::getConfig(CConfig* config) const *config = m_config; } -UInt32 CServer::getActivePrimarySides() const +UInt32 +CServer::getActivePrimarySides() const { UInt32 sides = 0; CLock lock(&m_mutex); @@ -264,26 +262,40 @@ UInt32 CServer::getActivePrimarySides() const return sides; } -void CServer::setInfo( - SInt32 w, SInt32 h, SInt32 zoneSize, - SInt32 x, SInt32 y) +void +CServer::setInfo( + SInt32 w, + SInt32 h, + SInt32 zoneSize, + SInt32 x, + SInt32 y) { CLock lock(&m_mutex); assert(m_primaryInfo != NULL); setInfoNoLock(m_primaryInfo->m_name, w, h, zoneSize, x, y); } -void CServer::setInfo(const CString& client, - SInt32 w, SInt32 h, SInt32 zoneSize, - SInt32 x, SInt32 y) +void +CServer::setInfo( + const CString& client, + SInt32 w, + SInt32 h, + SInt32 zoneSize, + SInt32 x, + SInt32 y) { CLock lock(&m_mutex); setInfoNoLock(client, w, h, zoneSize, x, y); } -void CServer::setInfoNoLock(const CString& screen, - SInt32 w, SInt32 h, SInt32 zoneSize, - SInt32 x, SInt32 y) +void +CServer::setInfoNoLock( + const CString& screen, + SInt32 w, + SInt32 h, + SInt32 zoneSize, + SInt32 x, + SInt32 y) { assert(!screen.empty()); assert(w > 0); @@ -327,24 +339,30 @@ void CServer::setInfoNoLock(const CString& screen, } } -void CServer::grabClipboard(ClipboardID id) +void +CServer::grabClipboard( + ClipboardID id) { CLock lock(&m_mutex); assert(m_primaryInfo != NULL); grabClipboardNoLock(id, 0, m_primaryInfo->m_name); } -void CServer::grabClipboard( - ClipboardID id, UInt32 seqNum, - const CString& client) +void +CServer::grabClipboard( + ClipboardID id, + UInt32 seqNum, + const CString& client) { CLock lock(&m_mutex); grabClipboardNoLock(id, seqNum, client); } -void CServer::grabClipboardNoLock( - ClipboardID id, UInt32 seqNum, - const CString& screen) +void +CServer::grabClipboardNoLock( + ClipboardID id, + UInt32 seqNum, + const CString& screen) { // note -- must be locked on entry CClipboardInfo& clipboard = m_clipboards[id]; @@ -402,8 +420,11 @@ void CServer::grabClipboardNoLock( } } -void CServer::setClipboard(ClipboardID id, - UInt32 seqNum, const CString& data) +void +CServer::setClipboard( + ClipboardID id, + UInt32 seqNum, + const CString& data) { CLock lock(&m_mutex); CClipboardInfo& clipboard = m_clipboards[id]; @@ -433,13 +454,19 @@ void CServer::setClipboard(ClipboardID id, sendClipboard(id); } -bool CServer::onCommandKey(KeyID /*id*/, - KeyModifierMask /*mask*/, bool /*down*/) +bool +CServer::onCommandKey( + KeyID /*id*/, + KeyModifierMask /*mask*/, + bool /*down*/) { return false; } -void CServer::onKeyDown(KeyID id, KeyModifierMask mask) +void +CServer::onKeyDown( + KeyID id, + KeyModifierMask mask) { log((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x", id, mask)); CLock lock(&m_mutex); @@ -456,7 +483,10 @@ void CServer::onKeyDown(KeyID id, KeyModifierMask mask) } } -void CServer::onKeyUp(KeyID id, KeyModifierMask mask) +void +CServer::onKeyUp( + KeyID id, + KeyModifierMask mask) { log((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x", id, mask)); CLock lock(&m_mutex); @@ -473,8 +503,11 @@ void CServer::onKeyUp(KeyID id, KeyModifierMask mask) } } -void CServer::onKeyRepeat( - KeyID id, KeyModifierMask mask, SInt32 count) +void +CServer::onKeyRepeat( + KeyID id, + KeyModifierMask mask, + SInt32 count) { log((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); CLock lock(&m_mutex); @@ -492,7 +525,9 @@ void CServer::onKeyRepeat( } } -void CServer::onMouseDown(ButtonID id) +void +CServer::onMouseDown( + ButtonID id) { log((CLOG_DEBUG1 "onMouseDown id=%d", id)); CLock lock(&m_mutex); @@ -504,7 +539,9 @@ void CServer::onMouseDown(ButtonID id) } } -void CServer::onMouseUp(ButtonID id) +void +CServer::onMouseUp( + ButtonID id) { log((CLOG_DEBUG1 "onMouseUp id=%d", id)); CLock lock(&m_mutex); @@ -516,14 +553,20 @@ void CServer::onMouseUp(ButtonID id) } } -bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) +bool +CServer::onMouseMovePrimary( + SInt32 x, + SInt32 y) { log((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); CLock lock(&m_mutex); return onMouseMovePrimaryNoLock(x, y); } -bool CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) +bool +CServer::onMouseMovePrimaryNoLock( + SInt32 x, + SInt32 y) { // mouse move on primary (server's) screen assert(m_active != NULL); @@ -577,15 +620,20 @@ bool CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) return true; } -void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) +void +CServer::onMouseMoveSecondary( + SInt32 dx, + SInt32 dy) { log((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); CLock lock(&m_mutex); onMouseMoveSecondaryNoLock(dx, dy); } -void CServer::onMouseMoveSecondaryNoLock( - SInt32 dx, SInt32 dy) +void +CServer::onMouseMoveSecondaryNoLock( + SInt32 dx, + SInt32 dy) { // mouse move on secondary (client's) screen assert(m_active != NULL); @@ -687,7 +735,9 @@ void CServer::onMouseMoveSecondaryNoLock( } } -void CServer::onMouseWheel(SInt32 delta) +void +CServer::onMouseWheel( + SInt32 delta) { log((CLOG_DEBUG1 "onMouseWheel %+d", delta)); CLock lock(&m_mutex); @@ -699,13 +749,15 @@ void CServer::onMouseWheel(SInt32 delta) } } -bool CServer::isLockedToScreen() const +bool +CServer::isLockedToScreen() const { CLock lock(&m_mutex); return isLockedToScreenNoLock(); } -bool CServer::isLockedToScreenNoLock() const +bool +CServer::isLockedToScreenNoLock() const { // locked if primary says we're locked if (m_primary->isLockedToScreen()) { @@ -721,8 +773,11 @@ bool CServer::isLockedToScreenNoLock() const return false; } -void CServer::switchScreen(CScreenInfo* dst, - SInt32 x, SInt32 y) +void +CServer::switchScreen( + CScreenInfo* dst, + SInt32 x, + SInt32 y) { assert(dst != NULL); assert(x >= 0 && y >= 0 && x < dst->m_width && y < dst->m_height); @@ -789,8 +844,10 @@ void CServer::switchScreen(CScreenInfo* dst, } } -CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, - CConfig::EDirection dir) const +CServer::CScreenInfo* +CServer::getNeighbor( + CScreenInfo* src, + CConfig::EDirection dir) const { assert(src != NULL); @@ -821,9 +878,12 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, } } -CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, - CConfig::EDirection srcSide, - SInt32& x, SInt32& y) const +CServer::CScreenInfo* +CServer::getNeighbor( + CScreenInfo* src, + CConfig::EDirection srcSide, + SInt32& x, + SInt32& y) const { assert(src != NULL); @@ -936,10 +996,13 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src, return lastGoodScreen; } -void CServer::mapPosition(CScreenInfo* src, - CConfig::EDirection srcSide, - CScreenInfo* dst, - SInt32& x, SInt32& y) const +void +CServer::mapPosition( + CScreenInfo* src, + CConfig::EDirection srcSide, + CScreenInfo* dst, + SInt32& x, + SInt32& y) const { assert(src != NULL); assert(dst != NULL); @@ -949,32 +1012,39 @@ void CServer::mapPosition(CScreenInfo* src, switch (srcSide) { case CConfig::kLeft: case CConfig::kRight: - if (y < 0) + if (y < 0) { y = 0; - else if (y >= src->m_height) + } + else if (y >= src->m_height) { y = dst->m_height - 1; - else + } + else { y = static_cast(0.5 + y * static_cast(dst->m_height - 1) / (src->m_height - 1)); + } break; case CConfig::kTop: case CConfig::kBottom: - if (x < 0) + if (x < 0) { x = 0; - else if (x >= src->m_width) + } + else if (x >= src->m_width) { x = dst->m_width - 1; - else + } + else { x = static_cast(0.5 + x * static_cast(dst->m_width - 1) / (src->m_width - 1)); + } break; } } #include "CTCPListenSocket.h" -void CServer::acceptClients(void*) +void +CServer::acceptClients(void*) { log((CLOG_DEBUG1 "starting to wait for clients")); @@ -1030,7 +1100,9 @@ void CServer::acceptClients(void*) } } -void CServer::handshakeClient(void* vsocket) +void +CServer::handshakeClient( + void* vsocket) { log((CLOG_DEBUG1 "negotiating with new client")); @@ -1162,7 +1234,8 @@ void CServer::handshakeClient(void* vsocket) } } -void CServer::acceptHTTPClients(void*) +void +CServer::acceptHTTPClients(void*) { log((CLOG_DEBUG1 "starting to wait for HTTP clients")); @@ -1229,7 +1302,9 @@ void CServer::acceptHTTPClients(void*) } } -void CServer::processHTTPRequest(void* vsocket) +void +CServer::processHTTPRequest( + void* vsocket) { // add this thread to the list of threads to cancel. remove from // list in d'tor. @@ -1266,7 +1341,9 @@ void CServer::processHTTPRequest(void* vsocket) } } -void CServer::clearGotClipboard(ClipboardID id) +void +CServer::clearGotClipboard( + ClipboardID id) { for (CScreenList::const_iterator index = m_screens.begin(); index != m_screens.end(); ++index) { @@ -1274,7 +1351,9 @@ void CServer::clearGotClipboard(ClipboardID id) } } -void CServer::sendClipboard(ClipboardID id) +void +CServer::sendClipboard( + ClipboardID id) { // do nothing if clipboard was already sent if (!m_active->m_gotClipboard[id]) { @@ -1295,7 +1374,9 @@ void CServer::sendClipboard(ClipboardID id) } } -void CServer::updatePrimaryClipboard(ClipboardID id) +void +CServer::updatePrimaryClipboard( + ClipboardID id) { CClipboardInfo& clipboard = m_clipboards[id]; @@ -1339,7 +1420,8 @@ void CServer::updatePrimaryClipboard(ClipboardID id) #elif defined(CONFIG_PLATFORM_UNIX) #include "CXWindowsPrimaryScreen.h" #endif -void CServer::openPrimaryScreen() +void +CServer::openPrimaryScreen() { assert(m_primary == NULL); @@ -1387,7 +1469,8 @@ void CServer::openPrimaryScreen() } } -void CServer::closePrimaryScreen() +void +CServer::closePrimaryScreen() { assert(m_primary != NULL); @@ -1410,14 +1493,18 @@ void CServer::closePrimaryScreen() m_primary = NULL; } -void CServer::addCleanupThread(const CThread& thread) +void +CServer::addCleanupThread( + const CThread& thread) { CLock lock(&m_mutex); m_cleanupList.insert(m_cleanupList.begin(), new CThread(thread)); m_cleanupSize = m_cleanupSize + 1; } -void CServer::removeCleanupThread(const CThread& thread) +void +CServer::removeCleanupThread( + const CThread& thread) { CLock lock(&m_mutex); for (CThreadList::iterator index = m_cleanupList.begin(); @@ -1435,7 +1522,9 @@ void CServer::removeCleanupThread(const CThread& thread) } } -void CServer::cleanupThreads(double timeout) +void +CServer::cleanupThreads( + double timeout) { log((CLOG_DEBUG1 "cleaning up threads")); @@ -1474,8 +1563,10 @@ void CServer::cleanupThreads(double timeout) log((CLOG_DEBUG1 "cleaned up threads")); } -CServer::CScreenInfo* CServer::addConnection( - const CString& name, IServerProtocol* protocol) +CServer::CScreenInfo* +CServer::addConnection( + const CString& name, + IServerProtocol* protocol) { log((CLOG_DEBUG "adding connection \"%s\"", name.c_str())); @@ -1499,7 +1590,9 @@ CServer::CScreenInfo* CServer::addConnection( return newScreen; } -void CServer::removeConnection(const CString& name) +void +CServer::removeConnection( + const CString& name) { log((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); CLock lock(&m_mutex); @@ -1534,7 +1627,9 @@ void CServer::removeConnection(const CString& name) // CServer::CCleanupNote // -CServer::CCleanupNote::CCleanupNote(CServer* server) : m_server(server) +CServer::CCleanupNote::CCleanupNote( + CServer* server) : + m_server(server) { assert(m_server != NULL); m_server->addCleanupThread(CThread::getCurrentThread()); @@ -1550,11 +1645,12 @@ CServer::CCleanupNote::~CCleanupNote() // CServer::CConnectionNote // -CServer::CConnectionNote::CConnectionNote(CServer* server, - const CString& name, - IServerProtocol* protocol) : - m_server(server), - m_name(name) +CServer::CConnectionNote::CConnectionNote( + CServer* server, + const CString& name, + IServerProtocol* protocol) : + m_server(server), + m_name(name) { assert(m_server != NULL); m_server->addConnection(m_name, protocol); @@ -1570,14 +1666,16 @@ CServer::CConnectionNote::~CConnectionNote() // CServer::CScreenInfo // -CServer::CScreenInfo::CScreenInfo(const CString& name, - IServerProtocol* protocol) : - m_thread(CThread::getCurrentThread()), - m_name(name), - m_protocol(protocol), - m_ready(false), - m_width(0), m_height(0), - m_zoneSize(0) +CServer::CScreenInfo::CScreenInfo( + const CString& name, + IServerProtocol* protocol) : + m_thread(CThread::getCurrentThread()), + m_name(name), + m_protocol(protocol), + m_ready(false), + m_width(0), + m_height(0), + m_zoneSize(0) { for (ClipboardID id = 0; id < kClipboardEnd; ++id) m_gotClipboard[id] = false; @@ -1594,11 +1692,11 @@ CServer::CScreenInfo::~CScreenInfo() // CServer::CClipboardInfo::CClipboardInfo() : - m_clipboard(), - m_clipboardData(), - m_clipboardOwner(), - m_clipboardSeqNum(0), - m_clipboardReady(false) + m_clipboard(), + m_clipboardData(), + m_clipboardOwner(), + m_clipboardSeqNum(0), + m_clipboardReady(false) { // do nothing } diff --git a/server/CServer.h b/server/CServer.h index 568d2c1d..c68a71a5 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -1,17 +1,15 @@ #ifndef CSERVER_H #define CSERVER_H +#include "CConfig.h" +#include "CClipboard.h" #include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" -#include "CConfig.h" -#include "CClipboard.h" #include "CNetworkAddress.h" #include "CCondVar.h" #include "CMutex.h" -#include "CString.h" #include "CThread.h" -#include "XBase.h" #include "stdlist.h" #include "stdmap.h" @@ -58,18 +56,18 @@ public: // handle updates from primary void setInfo(SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse); + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse); // handle messages from clients void setInfo(const CString& clientName, - SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse); + SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse); void grabClipboard(ClipboardID, - UInt32 seqNum, const CString& clientName); + UInt32 seqNum, const CString& clientName); void setClipboard(ClipboardID, - UInt32 seqNum, const CString& data); + UInt32 seqNum, const CString& data); // accessors @@ -132,13 +130,13 @@ private: // update screen info void setInfoNoLock(const CString& screenName, - SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse); + SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse); // grab the clipboard void grabClipboardNoLock(ClipboardID, - UInt32 seqNum, const CString& clientName); + UInt32 seqNum, const CString& clientName); // returns true iff mouse should be locked to the current screen bool isLockedToScreenNoLock() const; @@ -154,16 +152,16 @@ private: // if the position is sufficiently far from the source then we // cross multiple screens. CScreenInfo* getNeighbor(CScreenInfo*, - CConfig::EDirection, - SInt32& x, SInt32& y) const; + CConfig::EDirection, + SInt32& x, SInt32& y) const; // adjust coordinates to account for resolution differences. the // position is converted to a resolution independent form then // converted back to screen coordinates on the destination screen. void mapPosition(CScreenInfo* src, - CConfig::EDirection srcSide, - CScreenInfo* dst, - SInt32& x, SInt32& y) const; + CConfig::EDirection srcSide, + CScreenInfo* dst, + SInt32& x, SInt32& y) const; // open/close the primary screen void openPrimaryScreen(); diff --git a/server/CServerProtocol.cpp b/server/CServerProtocol.cpp index 73b7ecc5..dd65ba1c 100644 --- a/server/CServerProtocol.cpp +++ b/server/CServerProtocol.cpp @@ -1,20 +1,23 @@ #include "CServerProtocol.h" #include "CServerProtocol1_0.h" #include "ProtocolTypes.h" +#include "XSynergy.h" #include "IOutputStream.h" -#include -#include +#include // // CServerProtocol // -CServerProtocol::CServerProtocol(CServer* server, const CString& client, - IInputStream* input, IOutputStream* output) : - m_server(server), - m_client(client), - m_input(input), - m_output(output) +CServerProtocol::CServerProtocol( + CServer* server, + const CString& client, + IInputStream* input, + IOutputStream* output) : + m_server(server), + m_client(client), + m_input(input), + m_output(output) { assert(m_server != NULL); assert(m_input != NULL); @@ -26,29 +29,38 @@ CServerProtocol::~CServerProtocol() // do nothing } -CServer* CServerProtocol::getServer() const +CServer* +CServerProtocol::getServer() const { return m_server; } -CString CServerProtocol::getClient() const +CString +CServerProtocol::getClient() const { return m_client; } -IInputStream* CServerProtocol::getInputStream() const +IInputStream* +CServerProtocol::getInputStream() const { return m_input; } -IOutputStream* CServerProtocol::getOutputStream() const +IOutputStream* +CServerProtocol::getOutputStream() const { return m_output; } -IServerProtocol* CServerProtocol::create(SInt32 major, SInt32 minor, - CServer* server, const CString& client, - IInputStream* input, IOutputStream* output) +IServerProtocol* +CServerProtocol::create( + SInt32 major, + SInt32 minor, + CServer* server, + const CString& client, + IInputStream* input, + IOutputStream* output) { // disallow invalid version numbers if (major < 0 || minor < 0) { @@ -70,4 +82,3 @@ IServerProtocol* CServerProtocol::create(SInt32 major, SInt32 minor, // given version. return new CServerProtocol1_0(server, client, input, output); } - diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index 47ae856d..311e5d0f 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -1,7 +1,6 @@ #ifndef CSERVERPROTOCOL_H #define CSERVERPROTOCOL_H -#include "CString.h" #include "IServerProtocol.h" class CServer; @@ -11,7 +10,7 @@ class IOutputStream; class CServerProtocol : public IServerProtocol { public: CServerProtocol(CServer*, const CString& clientName, - IInputStream*, IOutputStream*); + IInputStream*, IOutputStream*); ~CServerProtocol(); // manipulators @@ -24,15 +23,15 @@ public: virtual IOutputStream* getOutputStream() const; static IServerProtocol* create(SInt32 major, SInt32 minor, - CServer*, const CString& clientName, - IInputStream*, IOutputStream*); + CServer*, const CString& clientName, + IInputStream*, IOutputStream*); // IServerProtocol overrides virtual void run() = 0; virtual void queryInfo() = 0; virtual void sendClose() = 0; virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask) = 0; + UInt32 seqNum, KeyModifierMask mask) = 0; virtual void sendLeave() = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 5fe9ffd4..4de3347f 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -3,19 +3,23 @@ #include "CClipboard.h" #include "CProtocolUtil.h" #include "ProtocolTypes.h" +#include "XSynergy.h" #include "IInputStream.h" #include "IOutputStream.h" -#include "CLog.h" #include "CThread.h" -#include +#include "CLog.h" +#include // // CServerProtocol1_0 // -CServerProtocol1_0::CServerProtocol1_0(CServer* server, const CString& client, - IInputStream* input, IOutputStream* output) : - CServerProtocol(server, client, input, output) +CServerProtocol1_0::CServerProtocol1_0( + CServer* server, + const CString& client, + IInputStream* input, + IOutputStream* output) : + CServerProtocol(server, client, input, output) { // do nothing } @@ -25,7 +29,8 @@ CServerProtocol1_0::~CServerProtocol1_0() // do nothing } -void CServerProtocol1_0::run() +void +CServerProtocol1_0::run() { // handle messages until the client hangs up for (;;) { @@ -71,7 +76,8 @@ void CServerProtocol1_0::run() } } -void CServerProtocol1_0::queryInfo() +void +CServerProtocol1_0::queryInfo() { log((CLOG_DEBUG1 "querying client \"%s\" info", getClient().c_str())); @@ -89,7 +95,8 @@ void CServerProtocol1_0::queryInfo() recvInfo(); } -void CServerProtocol1_0::sendClose() +void +CServerProtocol1_0::sendClose() { log((CLOG_DEBUG1 "send close to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClose); @@ -98,96 +105,120 @@ void CServerProtocol1_0::sendClose() getOutputStream()->flush(); } -void CServerProtocol1_0::sendEnter( - SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask) +void +CServerProtocol1_0::sendEnter( + SInt32 xAbs, + SInt32 yAbs, + UInt32 seqNum, + KeyModifierMask mask) { log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getClient().c_str(), xAbs, yAbs, seqNum, mask)); CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs, seqNum, mask); } -void CServerProtocol1_0::sendLeave() +void +CServerProtocol1_0::sendLeave() { log((CLOG_DEBUG1 "send leave to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCLeave); } -void CServerProtocol1_0::sendClipboard( - ClipboardID id, const CString& data) +void +CServerProtocol1_0::sendClipboard( + ClipboardID id, + const CString& data) { log((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getClient().c_str(), data.size())); CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); } -void CServerProtocol1_0::sendGrabClipboard(ClipboardID id) +void +CServerProtocol1_0::sendGrabClipboard( + ClipboardID id) { log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); } -void CServerProtocol1_0::sendScreenSaver(bool on) +void +CServerProtocol1_0::sendScreenSaver( + bool on) { log((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getClient().c_str(), on ? 1 : 0)); CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } -void CServerProtocol1_0::sendInfoAcknowledgment() +void +CServerProtocol1_0::sendInfoAcknowledgment() { log((CLOG_DEBUG1 "send info ack to \"%s\"", getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCInfoAck); } -void CServerProtocol1_0::sendKeyDown( - KeyID key, KeyModifierMask mask) +void +CServerProtocol1_0::sendKeyDown( + KeyID key, + KeyModifierMask mask) { log((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); } -void CServerProtocol1_0::sendKeyRepeat( - KeyID key, KeyModifierMask mask, SInt32 count) +void +CServerProtocol1_0::sendKeyRepeat( + KeyID key, + KeyModifierMask mask, + SInt32 count) { log((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getClient().c_str(), key, mask, count)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); } -void CServerProtocol1_0::sendKeyUp( - KeyID key, KeyModifierMask mask) +void +CServerProtocol1_0::sendKeyUp( + KeyID key, + KeyModifierMask mask) { log((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); } -void CServerProtocol1_0::sendMouseDown( - ButtonID button) +void +CServerProtocol1_0::sendMouseDown( + ButtonID button) { log((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getClient().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); } -void CServerProtocol1_0::sendMouseUp( - ButtonID button) +void +CServerProtocol1_0::sendMouseUp( + ButtonID button) { log((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getClient().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); } -void CServerProtocol1_0::sendMouseMove( - SInt32 xAbs, SInt32 yAbs) +void +CServerProtocol1_0::sendMouseMove( + SInt32 xAbs, + SInt32 yAbs) { log((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getClient().c_str(), xAbs, yAbs)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); } -void CServerProtocol1_0::sendMouseWheel( - SInt32 delta) +void +CServerProtocol1_0::sendMouseWheel( + SInt32 delta) { log((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); } -void CServerProtocol1_0::recvInfo() +void +CServerProtocol1_0::recvInfo() { // parse the message SInt16 x, y, w, h, zoneInfo; @@ -207,7 +238,8 @@ void CServerProtocol1_0::recvInfo() getServer()->setInfo(getClient(), w, h, zoneInfo, x, y); } -void CServerProtocol1_0::recvClipboard() +void +CServerProtocol1_0::recvClipboard() { // parse message ClipboardID id; @@ -225,7 +257,8 @@ void CServerProtocol1_0::recvClipboard() getServer()->setClipboard(id, seqNum, data); } -void CServerProtocol1_0::recvGrabClipboard() +void +CServerProtocol1_0::recvGrabClipboard() { // parse message ClipboardID id; diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index 4114643e..4e14dac7 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -17,7 +17,7 @@ public: virtual void queryInfo(); virtual void sendClose(); virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask); + UInt32 seqNum, KeyModifierMask mask); virtual void sendLeave(); virtual void sendClipboard(ClipboardID, const CString&); virtual void sendGrabClipboard(ClipboardID); diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index de47ab5f..281ab443 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -1,6 +1,5 @@ #include "CSynergyHook.h" #include "CConfig.h" -#include #include // @@ -60,7 +59,9 @@ static DWORD g_cursorThread = 0; // internal functions // -static void hideCursor(DWORD thread) +static +void +hideCursor(DWORD thread) { // we should be running the context of the window who's cursor // we want to hide so we shouldn't have to attach thread input. @@ -69,7 +70,9 @@ static void hideCursor(DWORD thread) SetCursor(NULL); } -static void restoreCursor() +static +void +restoreCursor() { // restore the show cursor in the window we hid it last if (g_cursor != NULL && g_cursorThread != 0) { @@ -84,7 +87,12 @@ static void restoreCursor() g_cursorThread = 0; } -static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) +static +LRESULT CALLBACK +keyboardHook( + int code, + WPARAM wParam, + LPARAM lParam) { if (code >= 0) { if (g_relay) { @@ -111,7 +119,12 @@ static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_keyboard, code, wParam, lParam); } -static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) +static +LRESULT CALLBACK +mouseHook( + int code, + WPARAM wParam, + LPARAM lParam) { if (code >= 0) { if (g_relay) { @@ -213,7 +226,12 @@ static LRESULT CALLBACK mouseHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_mouse, code, wParam, lParam); } -static LRESULT CALLBACK cbtHook(int code, WPARAM wParam, LPARAM lParam) +static +LRESULT CALLBACK +cbtHook( + int code, + WPARAM wParam, + LPARAM lParam) { if (code >= 0) { if (g_relay) { @@ -224,7 +242,12 @@ static LRESULT CALLBACK cbtHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_cbt, code, wParam, lParam); } -static LRESULT CALLBACK getMessageHook(int code, WPARAM wParam, LPARAM lParam) +static +LRESULT CALLBACK +getMessageHook( + int code, + WPARAM wParam, + LPARAM lParam) { if (code >= 0) { if (g_relay) { @@ -252,7 +275,12 @@ static LRESULT CALLBACK getMessageHook(int code, WPARAM wParam, LPARAM lParam) // side, key repeats are not compressed for us. // -static LRESULT CALLBACK keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) +static +LRESULT CALLBACK +keyboardLLHook( + int code, + WPARAM wParam, + LPARAM lParam) { if (code >= 0) { if (g_relay) { @@ -296,7 +324,10 @@ static LRESULT CALLBACK keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_keyboardLL, code, wParam, lParam); } -static DWORD WINAPI getKeyboardLLProc(void*) +static +DWORD WINAPI +getKeyboardLLProc( + void*) { // thread proc for low-level keyboard hook. this does nothing but // install the hook, process events, and uninstall the hook. @@ -349,7 +380,10 @@ static DWORD WINAPI getKeyboardLLProc(void*) #error foo -static DWORD WINAPI getKeyboardLLProc(void*) +static +DWORD WINAPI +getKeyboardLLProc( + void*) { g_keyHookThreadID = 0; SetEvent(g_keyHookEvent); @@ -358,7 +392,9 @@ static DWORD WINAPI getKeyboardLLProc(void*) #endif -static EWheelSupport GetWheelSupport() +static +EWheelSupport +getWheelSupport() { // get operating system OSVERSIONINFO info; @@ -404,7 +440,11 @@ static EWheelSupport GetWheelSupport() // external functions // -BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) +BOOL WINAPI +DllMain( + HINSTANCE instance, + DWORD reason, + LPVOID) { if (reason == DLL_PROCESS_ATTACH) { if (g_hinstance == NULL) { @@ -425,7 +465,9 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) extern "C" { -int install(DWORD threadID) +int +install( + DWORD threadID) { assert(g_threadID == 0); assert(g_hinstance != NULL); @@ -448,7 +490,7 @@ int install(DWORD threadID) g_cursorThread = 0; // check for mouse wheel support - g_wheelSupport = GetWheelSupport(); + g_wheelSupport = getWheelSupport(); // install keyboard hook g_keyboard = SetWindowsHookEx(WH_KEYBOARD, @@ -528,7 +570,9 @@ int install(DWORD threadID) return 1; } -int uninstall(void) +int +uninstall( + void) { assert(g_keyboard != NULL); assert(g_mouse != NULL); @@ -562,8 +606,12 @@ int uninstall(void) return 1; } -void setZone(UInt32 sides, - SInt32 w, SInt32 h, SInt32 jumpZoneSize) +void +setZone( + UInt32 sides, + SInt32 w, + SInt32 h, + SInt32 jumpZoneSize) { g_zoneSize = jumpZoneSize; g_zoneSides = sides; @@ -573,7 +621,9 @@ void setZone(UInt32 sides, restoreCursor(); } -void setRelay(void) +void +setRelay( + void) { g_relay = true; g_zoneSize = 0; diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index 4aefcafc..2d38d708 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -31,7 +31,7 @@ typedef void (*SetRelayFunc)(void); CSYNERGYHOOK_API int install(DWORD); CSYNERGYHOOK_API int uninstall(void); CSYNERGYHOOK_API void setZone(UInt32 sides, - SInt32 w, SInt32 h, SInt32 jumpZoneSize); + SInt32 w, SInt32 h, SInt32 jumpZoneSize); CSYNERGYHOOK_API void setRelay(void); } diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 52297109..b175517c 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -1,11 +1,10 @@ #include "CXWindowsPrimaryScreen.h" +#include "CServer.h" #include "CXWindowsClipboard.h" #include "CXWindowsUtil.h" -#include "CServer.h" -#include "CStopwatch.h" #include "CThread.h" #include "CLog.h" -#include +#include "CStopwatch.h" #include #include #define XK_MISCELLANY @@ -16,9 +15,9 @@ // CXWindowsPrimaryScreen::CXWindowsPrimaryScreen() : - m_server(NULL), - m_active(false), - m_window(None) + m_server(NULL), + m_active(false), + m_window(None) { // do nothing } @@ -28,7 +27,8 @@ CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() assert(m_window == None); } -void CXWindowsPrimaryScreen::run() +void +CXWindowsPrimaryScreen::run() { for (;;) { // wait for and get the next event @@ -39,152 +39,163 @@ void CXWindowsPrimaryScreen::run() // handle event switch (xevent.type) { - case CreateNotify: { - // select events on new window - CDisplayLock display(this); - selectEvents(display, xevent.xcreatewindow.window); - break; - } - - case MappingNotify: { - // keyboard mapping changed - CDisplayLock display(this); - XRefreshKeyboardMapping(&xevent.xmapping); - updateModifierMap(display); - break; - } - - case KeyPress: { - log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(&xevent.xkey); - if (key != kKeyNone) { - m_server->onKeyDown(key, mask); - if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_server->onKeyUp(key, mask | KeyModifierCapsLock); - } - else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_server->onKeyUp(key, mask | KeyModifierNumLock); - } + case CreateNotify: + { + // select events on new window + CDisplayLock display(this); + selectEvents(display, xevent.xcreatewindow.window); } break; - } - case KeyRelease: { - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(&xevent.xkey); - if (key != kKeyNone) { - // check if this is a key repeat by getting the next - // KeyPress event that has the same key and time as - // this release event, if any. first prepare the - // filter info. - CKeyEventInfo filter; - filter.m_event = KeyPress; - filter.m_window = xevent.xkey.window; - filter.m_time = xevent.xkey.time; - filter.m_keycode = xevent.xkey.keycode; - - // now check for event - XEvent xevent2; + case MappingNotify: + { + // keyboard mapping changed CDisplayLock display(this); - if (XCheckIfEvent(display, &xevent2, - &CXWindowsPrimaryScreen::findKeyEvent, - (XPointer)&filter) != True) { - // no press event follows so it's a plain release - log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + XRefreshKeyboardMapping(&xevent.xmapping); + updateModifierMap(display); + } + break; + + case KeyPress: + { + log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(&xevent.xkey); + if (key != kKeyNone) { + m_server->onKeyDown(key, mask); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_server->onKeyDown(key, mask); + m_server->onKeyUp(key, mask | KeyModifierCapsLock); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_server->onKeyDown(key, mask); + m_server->onKeyUp(key, mask | KeyModifierNumLock); } - m_server->onKeyUp(key, mask); + } + } + break; + + case KeyRelease: + { + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(&xevent.xkey); + if (key != kKeyNone) { + // check if this is a key repeat by getting the next + // KeyPress event that has the same key and time as + // this release event, if any. first prepare the + // filter info. + CKeyEventInfo filter; + filter.m_event = KeyPress; + filter.m_window = xevent.xkey.window; + filter.m_time = xevent.xkey.time; + filter.m_keycode = xevent.xkey.keycode; + + // now check for event + XEvent xevent2; + CDisplayLock display(this); + if (XCheckIfEvent(display, &xevent2, + &CXWindowsPrimaryScreen::findKeyEvent, + (XPointer)&filter) != True) { + // no press event follows so it's a plain release + log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { + m_server->onKeyDown(key, mask); + } + else if (key == XK_Num_Lock && m_numLockHalfDuplex) { + m_server->onKeyDown(key, mask); + } + m_server->onKeyUp(key, mask); + } + else { + // found a press event following so it's a repeat. + // we could attempt to count the already queued + // repeats but we'll just send a repeat of 1. + // note that we discard the press event. + log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + m_server->onKeyRepeat(key, mask, 1); + } + } + } + break; + + case ButtonPress: + { + log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_server->onMouseDown(button); + } + } + break; + + case ButtonRelease: + { + log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_server->onMouseUp(button); + } + else if (xevent.xbutton.button == 4) { + // wheel forward (away from user) + m_server->onMouseWheel(120); + } + else if (xevent.xbutton.button == 5) { + // wheel backward (toward user) + m_server->onMouseWheel(-120); + } + } + break; + + case MotionNotify: + { + log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); + SInt32 x, y; + if (!m_active) { + x = xevent.xmotion.x_root; + y = xevent.xmotion.y_root; + m_server->onMouseMovePrimary(x, y); } else { - // found a press event following so it's a repeat. - // we could attempt to count the already queued - // repeats but we'll just send a repeat of 1. - // note that we discard the press event. - log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - m_server->onKeyRepeat(key, mask, 1); + // FIXME -- slurp up all remaining motion events? + // probably not since keystrokes may go to wrong place. + + // get mouse deltas + { + CDisplayLock display(this); + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int mask; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &mask)) { + break; + } + + // compute position of center of window + SInt32 w, h; + getScreenSize(&w, &h); + x = xRoot - (w >> 1); + y = yRoot - (h >> 1); + + // warp mouse back to center + warpCursorNoLock(display, w >> 1, h >> 1); + } + + m_server->onMouseMoveSecondary(x, y); } } break; } - - case ButtonPress: { - log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNone) { - m_server->onMouseDown(button); - } - break; - } - - case ButtonRelease: { - log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNone) { - m_server->onMouseUp(button); - } - else if (xevent.xbutton.button == 4) { - // wheel forward (away from user) - m_server->onMouseWheel(120); - } - else if (xevent.xbutton.button == 5) { - // wheel backward (toward user) - m_server->onMouseWheel(-120); - } - break; - } - - case MotionNotify: { - log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); - SInt32 x, y; - if (!m_active) { - x = xevent.xmotion.x_root; - y = xevent.xmotion.y_root; - m_server->onMouseMovePrimary(x, y); - } - else { - // FIXME -- slurp up all remaining motion events? - // probably not since key strokes may go to wrong place. - - // get mouse deltas - { - CDisplayLock display(this); - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int mask; - if (!XQueryPointer(display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &mask)) - break; - - // compute position of center of window - SInt32 w, h; - getScreenSize(&w, &h); - x = xRoot - (w >> 1); - y = yRoot - (h >> 1); - - // warp mouse back to center - warpCursorNoLock(display, w >> 1, h >> 1); - } - - m_server->onMouseMoveSecondary(x, y); - } - break; - } - } } } -void CXWindowsPrimaryScreen::stop() +void +CXWindowsPrimaryScreen::stop() { CDisplayLock display(this); doStop(); } -void CXWindowsPrimaryScreen::open(CServer* server) +void +CXWindowsPrimaryScreen::open( + CServer* server) { assert(m_server == NULL); assert(server != NULL); @@ -228,7 +239,8 @@ void CXWindowsPrimaryScreen::open(CServer* server) m_server->setInfo(w, h, getJumpZoneSize(), x, y); } -void CXWindowsPrimaryScreen::close() +void +CXWindowsPrimaryScreen::close() { assert(m_server != NULL); @@ -239,7 +251,10 @@ void CXWindowsPrimaryScreen::close() m_server = NULL; } -void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) +void +CXWindowsPrimaryScreen::enter( + SInt32 x, + SInt32 y) { log((CLOG_INFO "entering primary at %d,%d", x, y)); assert(m_active == true); @@ -268,7 +283,8 @@ void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) m_active = false; } -bool CXWindowsPrimaryScreen::leave() +bool +CXWindowsPrimaryScreen::leave() { log((CLOG_INFO "leaving primary")); assert(m_active == false); @@ -333,19 +349,26 @@ bool CXWindowsPrimaryScreen::leave() return true; } -void CXWindowsPrimaryScreen::onConfigure() +void +CXWindowsPrimaryScreen::onConfigure() { // do nothing } -void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +void +CXWindowsPrimaryScreen::warpCursor( + SInt32 x, + SInt32 y) { CDisplayLock display(this); warpCursorNoLock(display, x, y); } -void CXWindowsPrimaryScreen::warpCursorNoLock( - Display* display, SInt32 x, SInt32 y) +void +CXWindowsPrimaryScreen::warpCursorNoLock( + Display* display, + SInt32 x, + SInt32 y) { assert(display != NULL); assert(m_window != None); @@ -363,35 +386,45 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( } } -void CXWindowsPrimaryScreen::setClipboard( - ClipboardID id, const IClipboard* clipboard) +void +CXWindowsPrimaryScreen::setClipboard( + ClipboardID id, + const IClipboard* clipboard) { setDisplayClipboard(id, clipboard); } -void CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) +void +CXWindowsPrimaryScreen::grabClipboard( + ClipboardID id) { setDisplayClipboard(id, NULL); } -void CXWindowsPrimaryScreen::getSize( - SInt32* width, SInt32* height) const +void +CXWindowsPrimaryScreen::getSize( + SInt32* width, + SInt32* height) const { getScreenSize(width, height); } -SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const +SInt32 +CXWindowsPrimaryScreen::getJumpZoneSize() const { return 1; } -void CXWindowsPrimaryScreen::getClipboard( - ClipboardID id, IClipboard* clipboard) const +void +CXWindowsPrimaryScreen::getClipboard( + ClipboardID id, + IClipboard* clipboard) const { getDisplayClipboard(id, clipboard); } -KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const +KeyModifierMask +CXWindowsPrimaryScreen::getToggleMask() const { CDisplayLock display(this); @@ -417,7 +450,8 @@ KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const return mask; } -bool CXWindowsPrimaryScreen::isLockedToScreen() const +bool +CXWindowsPrimaryScreen::isLockedToScreen() const { CDisplayLock display(this); @@ -449,7 +483,9 @@ bool CXWindowsPrimaryScreen::isLockedToScreen() const return false; } -void CXWindowsPrimaryScreen::onOpenDisplay(Display* display) +void +CXWindowsPrimaryScreen::onOpenDisplay( + Display* display) { assert(m_window == None); @@ -479,14 +515,17 @@ void CXWindowsPrimaryScreen::onOpenDisplay(Display* display) selectEvents(display, getRoot()); } -CXWindowsClipboard* CXWindowsPrimaryScreen::createClipboard( - ClipboardID id) +CXWindowsClipboard* +CXWindowsPrimaryScreen::createClipboard( + ClipboardID id) { CDisplayLock display(this); return new CXWindowsClipboard(display, m_window, id); } -void CXWindowsPrimaryScreen::onCloseDisplay(Display* display) +void +CXWindowsPrimaryScreen::onCloseDisplay( + Display* display) { assert(m_window != None); @@ -497,7 +536,8 @@ void CXWindowsPrimaryScreen::onCloseDisplay(Display* display) m_window = None; } -void CXWindowsPrimaryScreen::onUnexpectedClose() +void +CXWindowsPrimaryScreen::onUnexpectedClose() { // tell server to shutdown if (m_server != NULL) { @@ -505,15 +545,18 @@ void CXWindowsPrimaryScreen::onUnexpectedClose() } } -void CXWindowsPrimaryScreen::onLostClipboard( - ClipboardID id) +void +CXWindowsPrimaryScreen::onLostClipboard( + ClipboardID id) { // tell server that the clipboard was grabbed locally m_server->grabClipboard(id); } -void CXWindowsPrimaryScreen::selectEvents( - Display* display, Window w) const +void +CXWindowsPrimaryScreen::selectEvents( + Display* display, + Window w) const { // ignore errors while we adjust event masks CXWindowsUtil::CErrorLock lock; @@ -522,8 +565,10 @@ void CXWindowsPrimaryScreen::selectEvents( doSelectEvents(display, w); } -void CXWindowsPrimaryScreen::doSelectEvents( - Display* display, Window w) const +void +CXWindowsPrimaryScreen::doSelectEvents( + Display* display, + Window w) const { // we want to track the mouse everywhere on the display. to achieve // that we select PointerMotionMask on every window. we also select @@ -531,8 +576,9 @@ void CXWindowsPrimaryScreen::doSelectEvents( // select events on new windows too. // we don't want to adjust our grab window - if (w == m_window) + if (w == m_window) { return; + } // select events of interest XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask); @@ -541,14 +587,16 @@ void CXWindowsPrimaryScreen::doSelectEvents( Window rw, pw, *cw; unsigned int nc; if (XQueryTree(display, w, &rw, &pw, &cw, &nc)) { - for (unsigned int i = 0; i < nc; ++i) + for (unsigned int i = 0; i < nc; ++i) { doSelectEvents(display, cw[i]); + } XFree(cw); } } -KeyModifierMask CXWindowsPrimaryScreen::mapModifier( - unsigned int state) const +KeyModifierMask +CXWindowsPrimaryScreen::mapModifier( + unsigned int state) const { // FIXME -- should be configurable KeyModifierMask mask = 0; @@ -569,7 +617,9 @@ KeyModifierMask CXWindowsPrimaryScreen::mapModifier( return mask; } -KeyID CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const +KeyID +CXWindowsPrimaryScreen::mapKey( + XKeyEvent* event) const { KeySym keysym; char dummy[1]; @@ -579,18 +629,22 @@ KeyID CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const return static_cast(keysym); } -ButtonID CXWindowsPrimaryScreen::mapButton( - unsigned int button) const +ButtonID +CXWindowsPrimaryScreen::mapButton( + unsigned int button) const { // FIXME -- should use button mapping? - if (button >= 1 && button <= 3) + if (button >= 1 && button <= 3) { return static_cast(button); - else + } + else { return kButtonNone; + } } -void CXWindowsPrimaryScreen::updateModifierMap( - Display* display) +void +CXWindowsPrimaryScreen::updateModifierMap( + Display* display) { // get modifier map from server XModifierKeymap* keymap = XGetModifierMapping(display); @@ -624,8 +678,11 @@ void CXWindowsPrimaryScreen::updateModifierMap( XFreeModifiermap(keymap); } -Bool CXWindowsPrimaryScreen::findKeyEvent( - Display*, XEvent* xevent, XPointer arg) +Bool +CXWindowsPrimaryScreen::findKeyEvent( + Display*, + XEvent* xevent, + XPointer arg) { CKeyEventInfo* filter = reinterpret_cast(arg); return (xevent->type == filter->m_event && diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index aa30c327..fe4bbc87 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -1,10 +1,9 @@ #ifndef CXWINDOWSPRIMARYSCREEN_H #define CXWINDOWSPRIMARYSCREEN_H -#include "KeyTypes.h" -#include "MouseTypes.h" #include "CXWindowsScreen.h" #include "IPrimaryScreen.h" +#include "MouseTypes.h" class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { public: @@ -41,7 +40,7 @@ private: void selectEvents(Display*, Window) const; void doSelectEvents(Display*, Window) const; void warpCursorNoLock(Display*, - SInt32 xAbsolute, SInt32 yAbsolute); + SInt32 xAbsolute, SInt32 yAbsolute); KeyModifierMask mapModifier(unsigned int state) const; KeyID mapKey(XKeyEvent*) const; diff --git a/server/server.cpp b/server/server.cpp index 0b671e0a..aca20d12 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -1,16 +1,17 @@ #include "CServer.h" #include "CConfig.h" -#include "CLog.h" -#include "CLock.h" -#include "CMutex.h" -#include "CNetwork.h" #include "CPlatform.h" -#include "CThread.h" -#include "XThread.h" #include "ProtocolTypes.h" #include "Version.h" +#include "CNetwork.h" +#include "XSocket.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "XThread.h" +#include "CLog.h" #include "stdfstream.h" -#include +#include // platform dependent name of a daemon #if defined(CONFIG_PLATFORM_WIN32) @@ -51,7 +52,10 @@ static CConfig s_config; static CMutex* s_logMutex = NULL; -static void logLock(bool lock) +static +void +logLock( + bool lock) { assert(s_logMutex != NULL); @@ -70,7 +74,10 @@ static void logLock(bool lock) static CServer* s_server = NULL; -static int realMain(CMutex* mutex) +static +int +realMain( + CMutex* mutex) { // s_serverLock should have mutex locked on entry @@ -153,14 +160,18 @@ static int realMain(CMutex* mutex) return 0; } -static int restartMain() +static +int +restartMain() { return realMain(NULL); } // invoke realMain and wait for it. if s_restartable then keep // restarting realMain until it returns a terminate code. -static int restartableMain() +static +int +restartableMain() { if (s_restartable) { CPlatform platform; @@ -180,7 +191,9 @@ static int restartableMain() static void (*bye)(int) = &exit; -static void version() +static +void +version() { log((CLOG_PRINT "%s %d.%d.%d, protocol version %d.%d\n" @@ -194,7 +207,9 @@ static void version() kCopyright)); } -static void help() +static +void +help() { CPlatform platform; @@ -256,11 +271,14 @@ static void help() CONFIG_NAME).c_str())); } -static bool isArg(int argi, - int argc, const char** argv, - const char* name1, - const char* name2, - int minRequiredParameters = 0) +static +bool +isArg(int argi, + int argc, + const char** argv, + const char* name1, + const char* name2, + int minRequiredParameters = 0) { if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || (name2 != NULL && strcmp(argv[argi], name2) == 0)) { @@ -277,7 +295,11 @@ static bool isArg(int argi, return false; } -static void parse(int argc, const char** argv) +static +void +parse( + int argc, + const char** argv) { assert(pname != NULL); assert(argv != NULL); @@ -442,7 +464,11 @@ static void parse(int argc, const char** argv) } } -static bool loadConfig(const char* pathname, bool require) +static +bool +loadConfig( + const char* pathname, + bool require) { assert(pathname != NULL); @@ -471,7 +497,9 @@ static bool loadConfig(const char* pathname, bool require) return false; } -static void loadConfig() +static +void +loadConfig() { // load the config file, if specified if (s_configFile != NULL) { @@ -512,7 +540,11 @@ static void loadConfig() #include "CMSWindowsScreen.h" -static bool logMessageBox(int priority, const char* msg) +static +bool +logMessageBox( + int priority, + const char* msg) { if (priority <= CLog::kFATAL) { MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); @@ -523,18 +555,26 @@ static bool logMessageBox(int priority, const char* msg) } } -static void byeThrow(int x) +static +void +byeThrow(int x) { throw CWin32Platform::CDaemonFailed(x); } -static void daemonStop(void) +static +void +daemonStop(void) { s_server->quit(); } -static int daemonStartup(IPlatform* iplatform, - int argc, const char** argv) +static +int +daemonStartup( + IPlatform* iplatform, + int argc, + const char** argv) { // get platform pointer CWin32Platform* platform = static_cast(iplatform); @@ -558,19 +598,30 @@ static int daemonStartup(IPlatform* iplatform, return platform->runDaemon(realMain, daemonStop); } -static int daemonStartup95(IPlatform*, int, const char**) +static +int +daemonStartup95( + IPlatform*, + int, + const char**) { return realMain(NULL); } -static bool logDiscard(int, const char*) +static +bool +logDiscard( + int, + const char*) { return true; } static bool s_die = false; -static void checkParse(int e) +static +void +checkParse(int e) { // anything over 1 means invalid args. 1 means missing args. // 0 means graceful exit. we plan to exit for anything but @@ -580,7 +631,12 @@ static void checkParse(int e) throw s_die; } -int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) +int WINAPI +WinMain( + HINSTANCE instance, + HINSTANCE, + LPSTR, + int) { CPlatform platform; @@ -703,12 +759,20 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) #elif defined(CONFIG_PLATFORM_UNIX) -static int daemonStartup(IPlatform*, int, const char**) +static +int +daemonStartup( + IPlatform*, + int, + const char**) { return restartableMain(); } -int main(int argc, char** argv) +int +main( + int argc, + char** argv) { CPlatform platform; diff --git a/synergy/CClipboard.cpp b/synergy/CClipboard.cpp index f60c5a76..d5ec7018 100644 --- a/synergy/CClipboard.cpp +++ b/synergy/CClipboard.cpp @@ -1,11 +1,12 @@ #include "CClipboard.h" -#include // // CClipboard // -CClipboard::CClipboard() : m_open(false), m_owner(false) +CClipboard::CClipboard() : + m_open(false), + m_owner(false) { open(0); empty(); @@ -17,7 +18,8 @@ CClipboard::~CClipboard() // do nothing } -bool CClipboard::empty() +bool +CClipboard::empty() { assert(m_open); @@ -36,7 +38,10 @@ bool CClipboard::empty() return true; } -void CClipboard::add(EFormat format, const CString& data) +void +CClipboard::add( + EFormat format, + const CString& data) { assert(m_open); assert(m_owner); @@ -45,7 +50,9 @@ void CClipboard::add(EFormat format, const CString& data) m_added[format] = true; } -bool CClipboard::open(Time time) const +bool +CClipboard::open( + Time time) const { assert(!m_open); @@ -55,31 +62,40 @@ bool CClipboard::open(Time time) const return true; } -void CClipboard::close() const +void +CClipboard::close() const { assert(m_open); m_open = false; } -CClipboard::Time CClipboard::getTime() const +CClipboard::Time +CClipboard::getTime() const { return m_timeOwned; } -bool CClipboard::has(EFormat format) const +bool +CClipboard::has( + EFormat format) const { assert(m_open); return m_added[format]; } -CString CClipboard::get(EFormat format) const +CString +CClipboard::get( + EFormat format) const { assert(m_open); return m_data[format]; } -bool CClipboard::copy(IClipboard* dst, const IClipboard* src) +bool +CClipboard::copy( + IClipboard* dst, + const IClipboard* src) { assert(dst != NULL); assert(src != NULL); @@ -87,8 +103,11 @@ bool CClipboard::copy(IClipboard* dst, const IClipboard* src) return copy(dst, src, src->getTime()); } -bool CClipboard::copy(IClipboard* dst, - const IClipboard* src, Time time) +bool +CClipboard::copy( + IClipboard* dst, + const IClipboard* src, + Time time) { assert(dst != NULL); assert(src != NULL); @@ -114,7 +133,10 @@ bool CClipboard::copy(IClipboard* dst, return success; } -void CClipboard::unmarshall(const CString& data, Time time) +void +CClipboard::unmarshall( + const CString& data, + Time time) { const char* index = data.data(); @@ -146,7 +168,8 @@ void CClipboard::unmarshall(const CString& data, Time time) close(); } -CString CClipboard::marshall() const +CString +CClipboard::marshall() const { CString data; @@ -177,7 +200,9 @@ CString CClipboard::marshall() const return data; } -UInt32 CClipboard::readUInt32(const char* buf) const +UInt32 +CClipboard::readUInt32( + const char* buf) const { const unsigned char* ubuf = reinterpret_cast(buf); return (static_cast(ubuf[0]) << 24) | @@ -186,7 +211,10 @@ UInt32 CClipboard::readUInt32(const char* buf) const static_cast(ubuf[3]); } -void CClipboard::writeUInt32(CString* buf, UInt32 v) const +void +CClipboard::writeUInt32( + CString* buf, + UInt32 v) const { *buf += static_cast((v >> 24) & 0xff); *buf += static_cast((v >> 16) & 0xff); diff --git a/synergy/CClipboard.h b/synergy/CClipboard.h index 80fdec04..fea2b1d2 100644 --- a/synergy/CClipboard.h +++ b/synergy/CClipboard.h @@ -6,7 +6,6 @@ // #include "IClipboard.h" -#include "CString.h" class CClipboard : public IClipboard { public: @@ -23,6 +22,14 @@ public: // marshall clipboard data CString marshall() const; + // transfer all the data in one clipboard to another. the + // clipboards can be of any concrete clipboard type (and + // they don't have to be the same type). this also sets + // the timestamp to time, if provided, or the time in src. + // returns true iff the copy succeeded. + static bool copy(IClipboard* dst, const IClipboard* src); + static bool copy(IClipboard* dst, const IClipboard* src, Time); + // IClipboard overrides virtual bool empty(); virtual void add(EFormat, const CString& data); @@ -32,16 +39,6 @@ public: virtual bool has(EFormat) const; virtual CString get(EFormat) const; - // accessors - - // transfer all the data in one clipboard to another. the - // clipboards can be of any concrete clipboard type (and - // they don't have to be the same type). this also sets - // the timestamp to time, if provided, or the time in src. - // returns true iff the copy succeeded. - static bool copy(IClipboard* dst, const IClipboard* src); - static bool copy(IClipboard* dst, const IClipboard* src, Time); - private: UInt32 readUInt32(const char*) const; void writeUInt32(CString*, UInt32) const; diff --git a/synergy/CInputPacketStream.cpp b/synergy/CInputPacketStream.cpp index 607ac4f5..ce5b74cf 100644 --- a/synergy/CInputPacketStream.cpp +++ b/synergy/CInputPacketStream.cpp @@ -1,16 +1,17 @@ #include "CInputPacketStream.h" #include "CLock.h" -#include // // CInputPacketStream // -CInputPacketStream::CInputPacketStream(IInputStream* stream, bool adopt) : - CInputStreamFilter(stream, adopt), - m_mutex(), - m_size(0), - m_buffer(&m_mutex, NULL) +CInputPacketStream::CInputPacketStream( + IInputStream* stream, + bool adopt) : + CInputStreamFilter(stream, adopt), + m_mutex(), + m_size(0), + m_buffer(&m_mutex, NULL) { // do nothing } @@ -20,13 +21,16 @@ CInputPacketStream::~CInputPacketStream() // do nothing } -void CInputPacketStream::close() +void +CInputPacketStream::close() { getStream()->close(); } -UInt32 CInputPacketStream::read( - void* buffer, UInt32 n) +UInt32 +CInputPacketStream::read( + void* buffer, + UInt32 n) { CLock lock(&m_mutex); @@ -50,13 +54,15 @@ UInt32 CInputPacketStream::read( return n; } -UInt32 CInputPacketStream::getSize() const +UInt32 +CInputPacketStream::getSize() const { CLock lock(&m_mutex); return getSizeNoLock(); } -UInt32 CInputPacketStream::getSizeNoLock() const +UInt32 +CInputPacketStream::getSizeNoLock() const { while (!hasFullMessage()) { // read more data @@ -76,7 +82,8 @@ UInt32 CInputPacketStream::getSizeNoLock() const return m_size; } -bool CInputPacketStream::hasFullMessage() const +bool +CInputPacketStream::hasFullMessage() const { // get payload length if we don't have it yet if (m_size == 0) { @@ -106,4 +113,3 @@ bool CInputPacketStream::hasFullMessage() const // the buffer return (m_buffer.getSizeNoLock() >= m_size); } - diff --git a/synergy/COutputPacketStream.cpp b/synergy/COutputPacketStream.cpp index e9ffc68f..5e90f116 100644 --- a/synergy/COutputPacketStream.cpp +++ b/synergy/COutputPacketStream.cpp @@ -4,8 +4,10 @@ // COuputPacketStream // -COutputPacketStream::COutputPacketStream(IOutputStream* stream, bool adopt) : - COutputStreamFilter(stream, adopt) +COutputPacketStream::COutputPacketStream( + IOutputStream* stream, + bool adopt) : + COutputStreamFilter(stream, adopt) { // do nothing } @@ -15,13 +17,16 @@ COutputPacketStream::~COutputPacketStream() // do nothing } -void COutputPacketStream::close() +void +COutputPacketStream::close() { getStream()->close(); } -UInt32 COutputPacketStream::write( - const void* buffer, UInt32 count) +UInt32 +COutputPacketStream::write( + const void* buffer, + UInt32 count) { // write the length of the payload UInt8 length[4]; @@ -49,7 +54,8 @@ UInt32 COutputPacketStream::write( return count; } -void COutputPacketStream::flush() +void +COutputPacketStream::flush() { getStream()->flush(); } diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index fb0543a7..246f484d 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -2,16 +2,18 @@ #include "IInputStream.h" #include "IOutputStream.h" #include "CLog.h" -#include -#include -#include +#include +#include // // CProtocolUtil // -void CProtocolUtil::writef(IOutputStream* stream, - const char* fmt, ...) +void +CProtocolUtil::writef( + IOutputStream* stream, + const char* fmt, + ...) { assert(stream != NULL); assert(fmt != NULL); @@ -47,8 +49,11 @@ void CProtocolUtil::writef(IOutputStream* stream, delete[] buffer; } -void CProtocolUtil::readf(IInputStream* stream, - const char* fmt, ...) +void +CProtocolUtil::readf( + IInputStream* stream, + const char* fmt, + ...) { assert(stream != NULL); assert(fmt != NULL); @@ -179,8 +184,10 @@ void CProtocolUtil::readf(IInputStream* stream, va_end(args); } -UInt32 CProtocolUtil::getLength( - const char* fmt, va_list args) +UInt32 +CProtocolUtil::getLength( + const char* fmt, + va_list args) { UInt32 n = 0; while (*fmt) { @@ -228,8 +235,11 @@ UInt32 CProtocolUtil::getLength( return n; } -void CProtocolUtil::writef(void* buffer, - const char* fmt, va_list args) +void +CProtocolUtil::writef( + void* buffer, + const char* fmt, + va_list args) { UInt8* dst = reinterpret_cast(buffer); @@ -315,7 +325,9 @@ void CProtocolUtil::writef(void* buffer, } } -UInt32 CProtocolUtil::eatLength(const char** pfmt) +UInt32 +CProtocolUtil::eatLength( + const char** pfmt) { const char* fmt = *pfmt; UInt32 n = 0; @@ -339,8 +351,11 @@ UInt32 CProtocolUtil::eatLength(const char** pfmt) } } -void CProtocolUtil::read(IInputStream* stream, - void* vbuffer, UInt32 count) +void +CProtocolUtil::read( + IInputStream* stream, + void* vbuffer, + UInt32 count) { assert(stream != NULL); assert(vbuffer != NULL); @@ -367,7 +382,8 @@ void CProtocolUtil::read(IInputStream* stream, // XIOReadMismatch // -CString XIOReadMismatch::getWhat() const throw() +CString +XIOReadMismatch::getWhat() const throw() { return "CProtocolUtil::readf() mismatch"; } diff --git a/synergy/CProtocolUtil.h b/synergy/CProtocolUtil.h index 63a4376b..d6d1bce7 100644 --- a/synergy/CProtocolUtil.h +++ b/synergy/CProtocolUtil.h @@ -3,7 +3,7 @@ #include "BasicTypes.h" #include "XIO.h" -#include +#include class IInputStream; class IOutputStream; @@ -23,7 +23,7 @@ public: // %s -- converts CString* to stream of bytes // %S -- converts integer N and const UInt8* to stream of N bytes static void writef(IOutputStream*, - const char* fmt, ...); + const char* fmt, ...); // read formatted binary data from a buffer. this performs the // reverse operation of writef(). @@ -35,7 +35,7 @@ public: // %4i -- reads an NBO 4 byte integer; arg is SInt32* or UInt32* // %s -- reads bytes; argument must be a CString*, *not* a char* static void readf(IInputStream*, - const char* fmt, ...); + const char* fmt, ...); private: static UInt32 getLength(const char* fmt, va_list); diff --git a/synergy/CTCPSocketFactory.cpp b/synergy/CTCPSocketFactory.cpp index 7b10ff8d..5c5da342 100644 --- a/synergy/CTCPSocketFactory.cpp +++ b/synergy/CTCPSocketFactory.cpp @@ -16,12 +16,14 @@ CTCPSocketFactory::~CTCPSocketFactory() // do nothing } -ISocket* CTCPSocketFactory::create() const +ISocket* +CTCPSocketFactory::create() const { return new CTCPSocket; } -IListenSocket* CTCPSocketFactory::createListen() const +IListenSocket* +CTCPSocketFactory::createListen() const { return new CTCPListenSocket; } diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index 43632f02..66e35d2a 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -2,10 +2,9 @@ #define ICLIPBOARD_H #include "IInterface.h" +#include "CString.h" #include "BasicTypes.h" -class CString; - class IClipboard : public IInterface { public: // timestamp type. timestamps are in milliseconds from some diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index fbe00b46..c88207d5 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -2,7 +2,6 @@ #define IPRIMARYSCREEN_H #include "IInterface.h" -#include "BasicTypes.h" #include "KeyTypes.h" #include "ClipboardTypes.h" diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index c5bd736e..be709890 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -2,7 +2,6 @@ #define ISECONDARYSCREEN_H #include "IInterface.h" -#include "BasicTypes.h" #include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" @@ -35,7 +34,7 @@ public: // the cursor to the given coordinates and unhide it. prepare to // simulate input events. virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, - KeyModifierMask mask) = 0; + KeyModifierMask mask) = 0; // called when the user navigates off the secondary screen. clean // up input event simulation and hide the cursor. diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index ecddeeda..e329d0a4 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -1,12 +1,11 @@ #ifndef ISERVERPROTOCOL_H #define ISERVERPROTOCOL_H +#include "IInterface.h" #include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" -#include "IInterface.h" -#include "XSynergy.h" -#include "XIO.h" +#include "CString.h" class IClipboard; @@ -25,7 +24,7 @@ public: // send various messages to client virtual void sendClose() = 0; virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask) = 0; + UInt32 seqNum, KeyModifierMask mask) = 0; virtual void sendLeave() = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; diff --git a/synergy/ISocketFactory.h b/synergy/ISocketFactory.h index 287e4302..9d0f1104 100644 --- a/synergy/ISocketFactory.h +++ b/synergy/ISocketFactory.h @@ -2,7 +2,6 @@ #define ISOCKETFACTORY_H #include "IInterface.h" -#include "XSocket.h" class ISocket; class IListenSocket; diff --git a/synergy/XScreen.cpp b/synergy/XScreen.cpp index 4466525f..2354b04c 100644 --- a/synergy/XScreen.cpp +++ b/synergy/XScreen.cpp @@ -4,7 +4,8 @@ // XScreenOpenFailure // -CString XScreenOpenFailure::getWhat() const throw() +CString +XScreenOpenFailure::getWhat() const throw() { return "XScreenOpenFailure"; } diff --git a/synergy/XSynergy.cpp b/synergy/XSynergy.cpp index 94159f9a..b40e7fb3 100644 --- a/synergy/XSynergy.cpp +++ b/synergy/XSynergy.cpp @@ -4,7 +4,8 @@ // XBadClient // -CString XBadClient::getWhat() const throw() +CString +XBadClient::getWhat() const throw() { return "XBadClient"; } @@ -14,24 +15,29 @@ CString XBadClient::getWhat() const throw() // XIncompatibleClient // -XIncompatibleClient::XIncompatibleClient(int major, int minor) : - m_major(major), - m_minor(minor) +XIncompatibleClient::XIncompatibleClient( + int major, + int minor) : + m_major(major), + m_minor(minor) { // do nothing } -int XIncompatibleClient::getMajor() const throw() +int +XIncompatibleClient::getMajor() const throw() { return m_major; } -int XIncompatibleClient::getMinor() const throw() +int +XIncompatibleClient::getMinor() const throw() { return m_minor; } -CString XIncompatibleClient::getWhat() const throw() +CString +XIncompatibleClient::getWhat() const throw() { return "XIncompatibleClient"; } @@ -41,17 +47,21 @@ CString XIncompatibleClient::getWhat() const throw() // XDuplicateClient // -XDuplicateClient::XDuplicateClient(const CString& name) : m_name(name) +XDuplicateClient::XDuplicateClient( + const CString& name) : + m_name(name) { // do nothing } -const CString& XDuplicateClient::getName() const throw() +const CString& +XDuplicateClient::getName() const throw() { return m_name; } -CString XDuplicateClient::getWhat() const throw() +CString +XDuplicateClient::getWhat() const throw() { return "XDuplicateClient"; } @@ -61,17 +71,21 @@ CString XDuplicateClient::getWhat() const throw() // XUnknownClient // -XUnknownClient::XUnknownClient(const CString& name) : m_name(name) +XUnknownClient::XUnknownClient( + const CString& name) : + m_name(name) { // do nothing } -const CString& XUnknownClient::getName() const throw() +const CString& +XUnknownClient::getName() const throw() { return m_name; } -CString XUnknownClient::getWhat() const throw() +CString +XUnknownClient::getWhat() const throw() { return "XUnknownClient"; } From fba8063c17747e0376c70f8706d71110977a0b1b Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 11 Jun 2002 18:30:08 +0000 Subject: [PATCH 163/807] added missing #include . --- platform/CUnixPlatform.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index ea14d40f..aba109e7 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -8,6 +8,7 @@ #include #include #include +#include // // CUnixPlatform From 88490de5a25d172f9729c9887e2424db356861e8 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 11 Jun 2002 18:31:06 +0000 Subject: [PATCH 164/807] fixed bug with switching screens on primary when there's no link in that direction (it would assert). introduced bug when adding support for wrapping. now ignores attempts to move in a direction with no link. --- server/CServer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/CServer.cpp b/server/CServer.cpp index 3bbeef49..fb30944e 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -890,6 +890,9 @@ CServer::getNeighbor( // get the first neighbor CScreenInfo* lastGoodScreen = src; CScreenInfo* dst = getNeighbor(src, srcSide); + if (dst == NULL) { + return NULL; + } // get the source screen's size (needed for kRight and kBottom) SInt32 w = src->m_width, h = src->m_height; @@ -897,7 +900,7 @@ CServer::getNeighbor( // find destination screen, adjusting x or y (but not both) switch (srcSide) { case CConfig::kLeft: - while (dst != NULL) { + while (dst != NULL && dst != lastGoodScreen) { lastGoodScreen = dst; w = lastGoodScreen->m_width; h = lastGoodScreen->m_height; From c54d92b78fdef08d184e6de5ead4e515bbca73d1 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 11 Jun 2002 18:33:03 +0000 Subject: [PATCH 165/807] commented out half-duplex flags that should never have been uncommented. --- client/CXWindowsSecondaryScreen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 46a6b3f1..b4f12c8c 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -104,8 +104,8 @@ CXWindowsSecondaryScreen::open( // FIXME -- may have to get these from some database m_numLockHalfDuplex = false; m_capsLockHalfDuplex = false; - m_numLockHalfDuplex = true; - m_capsLockHalfDuplex = true; +// m_numLockHalfDuplex = true; +// m_capsLockHalfDuplex = true; // assume primary has all clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) { From 2b07c8091c9df5a2c74d0600ca8201235367afab Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 11 Jun 2002 20:09:59 +0000 Subject: [PATCH 166/807] windows fixes needed for formatting changes. --- base/stdpre.h | 1 + platform/CMSWindowsScreen.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/base/stdpre.h b/base/stdpre.h index c51292a4..172190ce 100644 --- a/base/stdpre.h +++ b/base/stdpre.h @@ -3,6 +3,7 @@ #pragma warning(disable: 4514) // unreferenced inline #pragma warning(disable: 4710) // not inlined #pragma warning(disable: 4663) // C++ change, template specialization +#pragma warning(disable: 4503) // decorated name length too long #pragma warning(push, 3) #pragma warning(disable: 4018) // signed/unsigned mismatch #pragma warning(disable: 4284) diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index f3db8a43..00c05dda 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -3,10 +3,10 @@ #include "IClipboard.h" #include "CMutex.h" +#include "CString.h" #define WIN32_LEAN_AND_MEAN #include -class CString; class CThread; class CMSWindowsScreen { From 21af7b2f17a7a9d3a8b1837eb17f7c3fb5866f78 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 11 Jun 2002 20:10:49 +0000 Subject: [PATCH 167/807] added a blurb about synrgyhk.dll and that the service manager will look for the binary wherever it was when --install was used. --- README | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/README b/README index 476f5a5a..56d79386 100644 --- a/README +++ b/README @@ -35,6 +35,7 @@ manifest README README this file synergy synergy.exe the synergy client synergyd synergyd.exe the synergy server + synrgyhk.dll the synergy hook dll synergy.conf synergy.conf sample configuration file synergy.linux.init startup script for client synergyd.linux.init startup script for server @@ -177,10 +178,17 @@ windows: synergyd --install [other command line options] the client/server is installed as a service and the command - line is saved and used when starting the service. note that - when installing the client you must provide the server - hostname argument. to change the arguments you must first - uninstall then reinstall. + line is saved and used when starting the service. the system + will expect to find the program wherever it was when you used + the --install option so make sure it's not on a network share + from another system because the network share will not be + available at boot time. synergyd will also try to load + synrgyhk.dll so that should be in the same directory as + synergyd.exe. + + note that when installing the client you must provide the + server hostname argument. to change the arguments you must + first uninstall then reinstall. you must also install the configuration file along with the server. it's recommended that you put it in the windows From e3dcf7febf8de68f81f82e7d61d0ce05da16de36 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 14 Jun 2002 18:08:20 +0000 Subject: [PATCH 168/807] performance fixes on win32 plus clean up of some warnings. also improved error messages when uninstalling service. --- base/CFunctionJob.cpp | 5 ++ base/CFunctionJob.h | 1 + base/CLog.cpp | 50 +++++++----- base/TMethodJob.h | 8 ++ base/common.h | 6 ++ client/CMSWindowsSecondaryScreen.cpp | 23 ++++-- client/CMSWindowsSecondaryScreen.h | 3 + client/client.cpp | 112 ++++++++++++++------------ io/CStreamBuffer.cpp | 21 ++--- io/CStreamBuffer.h | 1 + mt/CThread.cpp | 10 +++ mt/CThread.h | 9 +++ mt/CThreadRep.cpp | 40 +++++++++- mt/CThreadRep.h | 5 ++ platform/CMSWindowsScreen.cpp | 4 +- platform/CUnixPlatform.cpp | 22 +++--- platform/CUnixPlatform.h | 6 +- platform/CWin32Platform.cpp | 55 +++++++++---- platform/CWin32Platform.h | 3 +- platform/IPlatform.h | 19 +++-- server/server.cpp | 114 ++++++++++++++++----------- 21 files changed, 340 insertions(+), 177 deletions(-) diff --git a/base/CFunctionJob.cpp b/base/CFunctionJob.cpp index 7558c911..423f0a63 100644 --- a/base/CFunctionJob.cpp +++ b/base/CFunctionJob.cpp @@ -11,6 +11,11 @@ CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) : // do nothing } +CFunctionJob::~CFunctionJob() +{ + // do nothing +} + void CFunctionJob::run() { diff --git a/base/CFunctionJob.h b/base/CFunctionJob.h index f3b5272d..b1c786e0 100644 --- a/base/CFunctionJob.h +++ b/base/CFunctionJob.h @@ -6,6 +6,7 @@ class CFunctionJob : public IJob { public: CFunctionJob(void (*func)(void*), void* arg = NULL); + virtual ~CFunctionJob(); // IJob overrides virtual void run(); diff --git a/base/CLog.cpp b/base/CLog.cpp index a35ca588..bb9679d6 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -65,6 +65,11 @@ CLog::print( fmt += 3; } + // done if below priority threshold + if (priority > getMaxPriority()) { + return; + } + // compute prefix padding length int pad = g_priorityPad; @@ -98,6 +103,11 @@ CLog::printt( fmt += 3; } + // done if below priority threshold + if (priority > getMaxPriority()) { + return; + } + // compute prefix padding length char stack[1024]; sprintf(stack, "%d", line); @@ -217,34 +227,32 @@ CLog::output( char* msg) { assert(priority >= -1 && priority < g_numPriority); - assert(msg != 0); + assert(msg != NULL); - if (priority <= getMaxPriority()) { - // insert priority label - int n = -g_prioritySuffixLength; - if (priority >= 0) { - n = strlen(g_priority[priority]); - sprintf(msg + g_maxPriorityLength - n, - "%s:", g_priority[priority]); - msg[g_maxPriorityLength + 1] = ' '; - } + // insert priority label + int n = -g_prioritySuffixLength; + if (priority >= 0) { + n = strlen(g_priority[priority]); + sprintf(msg + g_maxPriorityLength - n, + "%s:", g_priority[priority]); + msg[g_maxPriorityLength + 1] = ' '; + } - // put a newline at the end + // put a newline at the end #if defined(CONFIG_PLATFORM_WIN32) - strcat(msg + g_priorityPad, "\r\n"); + strcat(msg + g_priorityPad, "\r\n"); #else - strcat(msg + g_priorityPad, "\n"); + strcat(msg + g_priorityPad, "\n"); #endif - // print it - CHoldLock lock(s_lock); - if (s_outputter == NULL || - !s_outputter(priority, msg + g_maxPriorityLength - n)) { + // print it + CHoldLock lock(s_lock); + if (s_outputter == NULL || + !s_outputter(priority, msg + g_maxPriorityLength - n)) { #if defined(CONFIG_PLATFORM_WIN32) - openConsole(); + openConsole(); #endif - fprintf(stderr, "%s", msg + g_maxPriorityLength - n); - } + fprintf(stderr, "%s", msg + g_maxPriorityLength - n); } } @@ -267,7 +275,7 @@ CLog::vsprint( } // start allocating buffers until we write the whole string - buffer = 0; + buffer = NULL; do { delete[] buffer; len *= 2; diff --git a/base/TMethodJob.h b/base/TMethodJob.h index 59a22208..950b95c8 100644 --- a/base/TMethodJob.h +++ b/base/TMethodJob.h @@ -7,6 +7,7 @@ template class TMethodJob : public IJob { public: TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL); + virtual ~TMethodJob(); // IJob overrides virtual void run(); @@ -27,6 +28,13 @@ TMethodJob::TMethodJob(T* object, void (T::*method)(void*), void* arg) : // do nothing } +template +inline +TMethodJob::~TMethodJob() +{ + // do nothing +} + template inline void diff --git a/base/common.h b/base/common.h index e31a1b73..df7d0a20 100644 --- a/base/common.h +++ b/base/common.h @@ -30,6 +30,12 @@ // this one's a little too aggressive #pragma warning(disable: 4127) // conditional expression is constant +// emitted incorrectly under release build in some circumstances +#if defined(NDEBUG) +#pragma warning(disable: 4702) // unreachable code +#pragma warning(disable: 4701) // local variable maybe used uninitialized +#endif + #endif // (_MSC_VER >= 1200) #else diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 895ae78f..b0fbdcf1 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -16,6 +16,7 @@ CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : m_client(NULL), m_threadID(0), + m_lastThreadID(0), m_desk(NULL), m_deskName(), m_window(NULL), @@ -741,17 +742,23 @@ CMSWindowsSecondaryScreen::syncDesktop() const { // note -- mutex must be locked on entry - DWORD threadID = GetCurrentThreadId(); + // change calling thread's desktop if (!m_is95Family) { - if (GetThreadDesktop(threadID) != m_desk) { - // FIXME -- this doesn't work. if we set a desktop then - // sending events doesn't work. - if (SetThreadDesktop(m_desk) == 0) { - log((CLOG_ERR "failed to set desktop: %d", GetLastError())); - } + if (SetThreadDesktop(m_desk) == 0) { + log((CLOG_WARN "failed to set desktop: %d", GetLastError())); } } - AttachThreadInput(threadID, m_threadID, TRUE); + + // attach input queues if not already attached. this has a habit + // of sucking up more and more CPU each time it's called (even if + // the threads are already attached). since we only expect one + // thread to call this more than once we can save just the last + // the attached thread. + DWORD threadID = GetCurrentThreadId(); + if (threadID != m_lastThreadID && threadID != m_threadID) { + m_lastThreadID = threadID; + AttachThreadInput(threadID, m_threadID, TRUE); + } } CString diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 176203f2..084ed47c 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -89,6 +89,9 @@ private: // the main loop's thread id DWORD m_threadID; + // the thread id of the last attached thread + mutable DWORD m_lastThreadID; + // the current desk and it's name HDESK m_desk; CString m_deskName; diff --git a/client/client.cpp b/client/client.cpp index b7c7214c..fbd89dfa 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -16,10 +16,8 @@ // platform dependent name of a daemon #if defined(CONFIG_PLATFORM_WIN32) -#define DAEMON "service" #define DAEMON_NAME "Synergy Client" #elif defined(CONFIG_PLATFORM_UNIX) -#define DAEMON "daemon" #define DAEMON_NAME "synergy" #endif @@ -127,8 +125,6 @@ realMain( // terminated return 1; } - - return 0; } static @@ -182,17 +178,34 @@ static void help() { +#if defined(CONFIG_PLATFORM_WIN32) + +# define PLATFORM_ARGS \ +" [--install]" \ +" \n" \ +"or\n" \ +" --uninstall\n" +# define PLATFORM_DESC \ +" --install install server as a service.\n" \ +" --uninstall uninstall server service.\n" + +#else + +# define PLATFORM_ARGS \ +" \n" +# define PLATFORM_DESC + +#endif + log((CLOG_PRINT "Usage: %s" -" [--"DAEMON"|--no-"DAEMON"]" " [--camp|--no-camp]" +" [--daemon|--no-daemon]" " [--debug ]" " [--name ]" " [--restart|--no-restart]" -" [--install]" -" \n" -"or\n" -" --uninstall\n" +PLATFORM_ARGS +"\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" "* --camp keep attempting to connect to the server until\n" @@ -201,8 +214,8 @@ help() " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" -" -f, --no-"DAEMON" run the client in the foreground.\n" -"* --"DAEMON" run the client as a "DAEMON".\n" +" -f, --no-daemon run the client in the foreground.\n" +"* --daemon run the client as a daemon.\n" " -n, --name use screen-name instead the hostname to identify\n" " ourself to the server.\n" " -1, --no-restart do not try to restart the client if it fails for\n" @@ -210,9 +223,8 @@ help() "* --restart restart the client automatically if it fails for\n" " some unexpected reason, including the server\n" " disconnecting but not including failing to\n" -" connect to the server." -" --install install server as a "DAEMON".\n" -" --uninstall uninstall server "DAEMON".\n" +" connect to the server.\n" +PLATFORM_DESC " -h, --help display this help and exit.\n" " --version display version information and exit.\n" "\n" @@ -223,7 +235,7 @@ help() "default port, %d.\n" "\n" "Where log messages go depends on the platform and whether or not the\n" -"client is running as a "DAEMON".", +"client is running as a daemon.", pname, kDefaultPort)); } @@ -291,12 +303,12 @@ parse( s_camp = false; } - else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) { + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { // not a daemon s_daemon = false; } - else if (isArg(i, argc, argv, NULL, "--"DAEMON)) { + else if (isArg(i, argc, argv, NULL, "--daemon")) { // daemonize s_daemon = true; } @@ -321,12 +333,8 @@ parse( bye(0); } +#if defined(CONFIG_PLATFORM_WIN32) else if (isArg(i, argc, argv, NULL, "--install")) { -#if !defined(CONFIG_PLATFORM_WIN32) - log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE, - pname, argv[i], pname)); - bye(2); -#endif s_install = true; if (s_uninstall) { log((CLOG_PRINT "%s: `--install' and `--uninstall'" @@ -335,13 +343,10 @@ parse( bye(2); } } - - else if (isArg(i, argc, argv, NULL, "--uninstall")) { -#if !defined(CONFIG_PLATFORM_WIN32) - log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE, - pname, argv[i], pname)); - bye(2); #endif + +#if defined(CONFIG_PLATFORM_WIN32) + else if (isArg(i, argc, argv, NULL, "--uninstall")) { s_uninstall = true; if (s_install) { log((CLOG_PRINT "%s: `--install' and `--uninstall'" @@ -350,6 +355,7 @@ parse( bye(2); } } +#endif else if (isArg(i, argc, argv, "--", NULL)) { // remaining arguments are not options @@ -490,16 +496,6 @@ daemonStartup( return platform->runDaemon(realMain, daemonStop); } -static -int -daemonStartup95( - IPlatform*, - int, - const char**) -{ - return realMain(NULL); -} - static bool logDiscard( @@ -556,12 +552,13 @@ WinMain( // ignore } + // send PRINT and FATAL output to a message box + CLog::setOutputter(&logMessageBox); + // if we're not starting as an NT service then reparse the command // line normally. - if (s_die || !s_daemon || s_install || s_uninstall || + if (s_die || __argc > 1 || s_install || s_uninstall || CWin32Platform::isWindows95Family()) { - // send PRINT and FATAL output to a message box - CLog::setOutputter(&logMessageBox); // exit on bye bye = &exit; @@ -570,9 +567,9 @@ WinMain( parse(__argc, const_cast(__argv)); } - // if starting as a daemon then we ignore the startup command line - // here. we'll parse the command line passed in when the service - // control manager calls us back. + // if no arguments were provided then we're hopefully starting as + // a service. we'll parse the command line passed in when the + // service control manager calls us back. else { // do nothing } @@ -588,7 +585,7 @@ WinMain( } // construct the command line to start the service with - CString commandLine = "--"DAEMON; + CString commandLine = "--daemon"; if (s_restartable) { commandLine += " --restart"; } @@ -613,29 +610,42 @@ WinMain( return 0; } else if (s_uninstall) { - if (!platform.uninstallDaemon(DAEMON_NAME)) { + switch (platform.uninstallDaemon(DAEMON_NAME)) { + case IPlatform::kSuccess: + log((CLOG_PRINT "uninstalled successfully")); + return 0; + + case IPlatform::kFailed: log((CLOG_CRIT "failed to uninstall service")); return 16; + + case IPlatform::kAlready: + log((CLOG_CRIT "service isn't installed")); + return 16; } - log((CLOG_PRINT "uninstalled successfully")); - return 0; } // daemonize if requested int result; - if (s_daemon) { + if (__argc <= 1) { if (CWin32Platform::isWindows95Family()) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup95); + result = -1; } else { result = platform.daemonize(DAEMON_NAME, &daemonStartup); } if (result == -1) { - log((CLOG_CRIT "failed to start as a service")); + log((CLOG_CRIT "failed to start as a service" BYE, pname)); return 16; } } else { + // if a daemon then redirect log + if (s_daemon) { + platform.installDaemonLogger(__argv[0]); + } + + // run result = restartableMain(); } diff --git a/io/CStreamBuffer.cpp b/io/CStreamBuffer.cpp index fee8546b..ca6b63e9 100644 --- a/io/CStreamBuffer.cpp +++ b/io/CStreamBuffer.cpp @@ -7,7 +7,8 @@ const UInt32 CStreamBuffer::kChunkSize = 4096; CStreamBuffer::CStreamBuffer() : - m_size(0) + m_size(0), + m_headUsed(0) { // do nothing } @@ -25,17 +26,17 @@ CStreamBuffer::peek( // reserve space in first chunk ChunkList::iterator head = m_chunks.begin(); - head->reserve(n); + head->reserve(n + m_headUsed); // consolidate chunks into the first chunk until it has n bytes ChunkList::iterator scan = head; ++scan; - while (head->size() < n && scan != m_chunks.end()) { + while (head->size() - m_headUsed < n && scan != m_chunks.end()) { head->insert(head->end(), scan->begin(), scan->end()); scan = m_chunks.erase(scan); } - return reinterpret_cast(head->begin()); + return reinterpret_cast(head->begin() + m_headUsed); } void @@ -44,7 +45,8 @@ CStreamBuffer::pop( { // discard all chunks if n is greater than or equal to m_size if (n >= m_size) { - m_size = 0; + m_size = 0; + m_headUsed = 0; m_chunks.clear(); return; } @@ -55,15 +57,16 @@ CStreamBuffer::pop( // discard chunks until more than n bytes would've been discarded ChunkList::iterator scan = m_chunks.begin(); assert(scan != m_chunks.end()); - while (scan->size() <= n) { - n -= scan->size(); - scan = m_chunks.erase(scan); + while (scan->size() - m_headUsed <= n) { + n -= scan->size() - m_headUsed; + m_headUsed = 0; + scan = m_chunks.erase(scan); assert(scan != m_chunks.end()); } // remove left over bytes from the head chunk if (n > 0) { - scan->erase(scan->begin(), scan->begin() + n); + m_headUsed += n; } } diff --git a/io/CStreamBuffer.h b/io/CStreamBuffer.h index 97b32296..a975e77d 100644 --- a/io/CStreamBuffer.h +++ b/io/CStreamBuffer.h @@ -33,6 +33,7 @@ private: ChunkList m_chunks; UInt32 m_size; + UInt32 m_headUsed; }; #endif diff --git a/mt/CThread.cpp b/mt/CThread.cpp index 924509f4..74d3d727 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -105,6 +105,16 @@ CThread::wait( return currentRep->wait(m_rep, timeout); } +#if defined(CONFIG_PLATFORM_WIN32) +bool +CThread::waitForEvent( + double timeout) +{ + CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); + return currentRep->waitForEvent(timeout); +} +#endif + void CThread::testCancel() { diff --git a/mt/CThread.h b/mt/CThread.h index 03dc3fd3..81c8aef8 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -1,6 +1,8 @@ #ifndef CTHREAD_H #define CTHREAD_H +#include "common.h" + class IJob; class CThreadRep; @@ -103,6 +105,13 @@ public: // (cancellation point) bool wait(double timeout = -1.0) const; +#if defined(CONFIG_PLATFORM_WIN32) + // wait for a message in the queue. returns true if a message + // is available. + // (cancellation point) + static bool waitForEvent(double timeout = -1.0); +#endif + // get the exit result. does an implicit wait(). returns NULL // immediately if called by a thread on itself. returns NULL for // threads that were cancelled. diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 06af394a..3ef5ec18 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -614,7 +614,45 @@ CThreadRep::wait( testCancel(); default: - // error + // timeout or error + return false; + } +} + +bool +CThreadRep::waitForEvent( + double timeout) +{ + // is cancellation enabled? + const DWORD n = (isCancellable() ? 1 : 0); + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for the target thread to + // terminate. + HANDLE handles[1]; + handles[0] = m_cancel; + DWORD result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT); + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 1: + // message is available + return true; + + case WAIT_OBJECT_0 + 0: + // this thread was cancelled. does not return. + testCancel(); + + default: + // timeout or error return false; } } diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h index b407ae88..448d2b4d 100644 --- a/mt/CThreadRep.h +++ b/mt/CThreadRep.h @@ -43,6 +43,11 @@ public: // wait for thread to exit or for current thread to cancel bool wait(CThreadRep*, double timeout); +#if defined(CONFIG_PLATFORM_WIN32) + // wait for a message on the queue + bool waitForEvent(double timeout); +#endif + // set the priority void setPriority(int n); diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index f093c770..693cfd84 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -202,9 +202,7 @@ CMSWindowsScreen::getEvent( MSG* msg) const { // wait for an event in a cancellable way - while (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0) { - CThread::sleep(0.01); - } + CThread::waitForEvent(); GetMessage(msg, NULL, 0, 0); } diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index aba109e7..1ab2c63b 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -35,12 +35,12 @@ CUnixPlatform::installDaemon( return true; } -bool +CUnixPlatform::EResult CUnixPlatform::uninstallDaemon( const char*) { // daemons don't require special installation - return true; + return kSuccess; } int @@ -85,12 +85,20 @@ CUnixPlatform::daemonize( dup(1); // hook up logger - setDaemonLogger(name); + installDaemonLogger(name); // invoke function return func(this, 1, &name); } +void +CUnixPlatform::installDaemonLogger( + const char* name) +{ + openlog(name, 0, LOG_DAEMON); + CLog::setOutputter(&CUnixPlatform::deamonLogger); +} + int CUnixPlatform::restart( RestartFunc func, @@ -195,14 +203,6 @@ CUnixPlatform::addPathComponent( return path; } -void -CUnixPlatform::setDaemonLogger( - const char* name) -{ - openlog(name, 0, LOG_DAEMON); - CLog::setOutputter(&CUnixPlatform::deamonLogger); -} - bool CUnixPlatform::deamonLogger( int priority, diff --git a/platform/CUnixPlatform.h b/platform/CUnixPlatform.h index d5a92ec3..9b5592b4 100644 --- a/platform/CUnixPlatform.h +++ b/platform/CUnixPlatform.h @@ -13,8 +13,9 @@ public: const char* description, const char* pathname, const char* commandLine); - virtual bool uninstallDaemon(const char* name); + virtual EResult uninstallDaemon(const char* name); virtual int daemonize(const char* name, DaemonFunc); + virtual void installDaemonLogger(const char* name); virtual int restart(RestartFunc, int minErrorCode); virtual const char* getBasename(const char* pathname) const; virtual CString getUserDirectory() const; @@ -23,9 +24,6 @@ public: const CString& prefix, const CString& suffix) const; -protected: - virtual void setDaemonLogger(const char* name); - private: static bool deamonLogger(int, const char*); }; diff --git a/platform/CWin32Platform.cpp b/platform/CWin32Platform.cpp index 75dc0c37..58cd7cd2 100644 --- a/platform/CWin32Platform.cpp +++ b/platform/CWin32Platform.cpp @@ -180,7 +180,7 @@ CWin32Platform::installDaemon( } } -bool +IPlatform::EResult CWin32Platform::uninstallDaemon( const char* name) { @@ -190,7 +190,7 @@ CWin32Platform::uninstallDaemon( HKEY key = open95ServicesKey(); if (key == NULL) { log((CLOG_ERR "cannot open RunServices registry key", GetLastError())); - return false; + return kAlready; } // remove entry @@ -199,7 +199,7 @@ CWin32Platform::uninstallDaemon( // clean up closeKey(key); - return true; + return kSuccess; } // windows NT family services @@ -216,26 +216,41 @@ CWin32Platform::uninstallDaemon( SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); if (mgr == NULL) { log((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); - return false; + return kFailed; } // open the service. oddly, you must open a service to delete it. - bool success; + EResult result; SC_HANDLE service = OpenService(mgr, name, DELETE); if (service == NULL) { - log((CLOG_ERR "OpenService failed with %d", GetLastError())); - success = false; + const DWORD e = GetLastError(); + log((CLOG_ERR "OpenService failed with %d", e)); + result = (e == ERROR_SERVICE_DOES_NOT_EXIST) ? kAlready : kFailed; } else { - success = (DeleteService(service) != 0); + if (DeleteService(service) != 0) { + result = kSuccess; + } + else { + const DWORD e = GetLastError(); + switch (e) { + case ERROR_SERVICE_MARKED_FOR_DELETE: + result = kAlready; + break; + + default: + result = kFailed; + break; + } + } CloseServiceHandle(service); } // close the manager CloseServiceHandle(mgr); - return success; + return result; } } @@ -303,6 +318,21 @@ CWin32Platform::daemonize( } } +void +CWin32Platform::installDaemonLogger( + const char* name) +{ + if (!CWin32Platform::isWindows95Family()) { + // open event log and direct log messages to it + if (s_eventLog == NULL) { + s_eventLog = RegisterEventSource(NULL, name); + if (s_eventLog != NULL) { + CLog::setOutputter(&CWin32Platform::serviceLogger); + } + } + } +} + int CWin32Platform::restart( RestartFunc func, @@ -643,12 +673,7 @@ CWin32Platform::serviceMain( const char** argv = const_cast(argvIn); // open event log and direct log messages to it - if (s_eventLog == NULL) { - s_eventLog = RegisterEventSource(NULL, argv[0]); - if (s_eventLog != NULL) { - CLog::setOutputter(&CWin32Platform::serviceLogger); - } - } + installDaemonLogger(argv[0]); // create synchronization objects CThread::init(); diff --git a/platform/CWin32Platform.h b/platform/CWin32Platform.h index 40dfbe29..45f99e29 100644 --- a/platform/CWin32Platform.h +++ b/platform/CWin32Platform.h @@ -46,8 +46,9 @@ public: const char* description, const char* pathname, const char* commandLine); - virtual bool uninstallDaemon(const char* name); + virtual EResult uninstallDaemon(const char* name); virtual int daemonize(const char* name, DaemonFunc); + virtual void installDaemonLogger(const char* name); virtual int restart(RestartFunc, int minErrorCode); virtual const char* getBasename(const char* pathname) const; virtual CString getUserDirectory() const; diff --git a/platform/IPlatform.h b/platform/IPlatform.h index a5d0bde9..0a7e4cee 100644 --- a/platform/IPlatform.h +++ b/platform/IPlatform.h @@ -9,6 +9,12 @@ public: typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv); typedef int (*RestartFunc)(); + enum EResult { + kSuccess, + kFailed, + kAlready + }; + // manipulators // install/uninstall a daemon. commandLine should *not* @@ -18,12 +24,10 @@ public: const char* description, const char* pathname, const char* commandLine) = 0; - virtual bool uninstallDaemon(const char* name) = 0; + virtual EResult uninstallDaemon(const char* name) = 0; - // daemonize. this should have the side effect of sending log - // messages to a system message logger since messages can no - // longer go to the console. returns true iff successful. - // the name is the name of the daemon. + // daemonize. this should call installDaemonLogger(). returns + // true iff successful. the name is the name of the daemon. // daemonize. this should have the side effect of sending log // messages to a system message logger since messages can no @@ -44,6 +48,11 @@ public: // SetServiceStatus(). virtual int daemonize(const char* name, DaemonFunc func) = 0; + // directs CLog to send messages to the daemon log. used when + // messages should no longer go to the console. name is used + // in the log to identify this process. + virtual void installDaemonLogger(const char* name) = 0; + // continually restart the given function in a separate process // or thread until it exits normally with a code less than the // given code then return the code. diff --git a/server/server.cpp b/server/server.cpp index aca20d12..0072cb8f 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -15,10 +15,8 @@ // platform dependent name of a daemon #if defined(CONFIG_PLATFORM_WIN32) -#define DAEMON "service" #define DAEMON_NAME "Synergy Server" #elif defined(CONFIG_PLATFORM_UNIX) -#define DAEMON "daemon" #define DAEMON_NAME "synergyd" #endif @@ -36,8 +34,10 @@ static const char* pname = NULL; static bool s_restartable = true; static bool s_daemon = true; +#if defined(CONFIG_PLATFORM_WIN32) static bool s_install = false; static bool s_uninstall = false; +#endif static const char* s_configFile = NULL; static const char* s_logFilter = NULL; static CString s_name; @@ -211,42 +211,62 @@ static void help() { +#if defined(CONFIG_PLATFORM_WIN32) + +# define PLATFORM_ARGS \ +" {--daemon|--no-daemon}" \ +" [--install]\n" \ +"or\n" \ +" --uninstall\n" +# define PLATFORM_DESC \ +" --install install server as a daemon.\n" \ +" --uninstall uninstall server daemon.\n" +# define PLATFORM_EXTRA \ +"At least one command line argument is required. If you don't otherwise\n" \ +"need an argument use `--daemon'.\n" \ +"\n" + +#else + +# define PLATFORM_ARGS \ +" [--daemon|--no-daemon]" +# define PLATFORM_DESC +# define PLATFORM_EXTRA + +#endif + CPlatform platform; log((CLOG_PRINT "Usage: %s" " [--address
]" " [--config ]" -" [--"DAEMON"|--no-"DAEMON"]" " [--debug ]" " [--name ]" " [--restart|--no-restart]\n" -" [--install]\n" -"or\n" -" --uninstall\n" +PLATFORM_ARGS "\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" " -a, --address
listen for clients on the given address.\n" -" -c, --config use the named configuration file instead\n" -" where ~ represents the user's home directory.\n" +" -c, --config use the named configuration file instead.\n" " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" -" -f, --no-"DAEMON" run the server in the foreground.\n" -"* --"DAEMON" run the server as a "DAEMON".\n" +" -f, --no-daemon run the server in the foreground.\n" +"* --daemon run the server as a daemon.\n" " -n, --name use screen-name instead the hostname to identify\n" " this screen in the configuration.\n" " -1, --no-restart do not try to restart the server if it fails for\n" " some reason.\n" "* --restart restart the server automatically if it fails.\n" -" --install install server as a "DAEMON".\n" -" --uninstall uninstall server "DAEMON".\n" +PLATFORM_DESC " -h, --help display this help and exit.\n" " --version display version information and exit.\n" "\n" "* marks defaults.\n" "\n" +PLATFORM_EXTRA "The argument for --address is of the form: [][:]. The\n" "hostname must be the address or hostname of an interface on the system.\n" "The default is to listen on all interfaces. The port overrides the\n" @@ -260,7 +280,7 @@ help() "defaults with just the server screen.\n" "\n" "Where log messages go depends on the platform and whether or not the\n" -"server is running as a "DAEMON".", +"server is running as a daemon.", pname, kDefaultPort, platform.addPathComponent( @@ -355,12 +375,12 @@ parse( s_configFile = argv[++i]; } - else if (isArg(i, argc, argv, "-f", "--no-"DAEMON)) { + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { // not a daemon s_daemon = false; } - else if (isArg(i, argc, argv, NULL, "--"DAEMON)) { + else if (isArg(i, argc, argv, NULL, "--daemon")) { // daemonize s_daemon = true; } @@ -385,12 +405,8 @@ parse( bye(0); } +#if defined(CONFIG_PLATFORM_WIN32) else if (isArg(i, argc, argv, NULL, "--install")) { -#if !defined(CONFIG_PLATFORM_WIN32) - log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE, - pname, argv[i], pname)); - bye(2); -#endif s_install = true; if (s_uninstall) { log((CLOG_PRINT "%s: `--install' and `--uninstall'" @@ -399,13 +415,10 @@ parse( bye(2); } } - - else if (isArg(i, argc, argv, NULL, "--uninstall")) { -#if !defined(CONFIG_PLATFORM_WIN32) - log((CLOG_PRINT "%s: `%s' not supported on this platform" BYE, - pname, argv[i], pname)); - bye(2); #endif + +#if defined(CONFIG_PLATFORM_WIN32) + else if (isArg(i, argc, argv, NULL, "--uninstall")) { s_uninstall = true; if (s_install) { log((CLOG_PRINT "%s: `--install' and `--uninstall'" @@ -414,6 +427,7 @@ parse( bye(2); } } +#endif else if (isArg(i, argc, argv, "--", NULL)) { // remaining arguments are not options @@ -598,16 +612,6 @@ daemonStartup( return platform->runDaemon(realMain, daemonStop); } -static -int -daemonStartup95( - IPlatform*, - int, - const char**) -{ - return realMain(NULL); -} - static bool logDiscard( @@ -664,12 +668,13 @@ WinMain( // ignore } + // send PRINT and FATAL output to a message box + CLog::setOutputter(&logMessageBox); + // if we're not starting as an NT service then reparse the command // line normally. - if (s_die || !s_daemon || s_install || s_uninstall || + if (s_die || __argc > 1 || s_install || s_uninstall || CWin32Platform::isWindows95Family()) { - // send PRINT and FATAL output to a message box - CLog::setOutputter(&logMessageBox); // exit on bye bye = &exit; @@ -678,9 +683,9 @@ WinMain( parse(__argc, const_cast(__argv)); } - // if starting as a daemon then we ignore the startup command line - // here. we'll parse the command line passed in when the service - // control manager calls us back. + // if no arguments were provided then we're hopefully starting as + // a service. we'll parse the command line passed in when the + // service control manager calls us back. else { // do nothing } @@ -697,7 +702,7 @@ WinMain( // construct the command line to start the service with CString commandLine; - commandLine += "--"DAEMON; + commandLine += "--daemon"; if (s_restartable) { commandLine += " --restart"; } @@ -725,12 +730,19 @@ WinMain( return 0; } else if (s_uninstall) { - if (!platform.uninstallDaemon(DAEMON_NAME)) { + switch (platform.uninstallDaemon(DAEMON_NAME)) { + case IPlatform::kSuccess: + log((CLOG_PRINT "uninstalled successfully")); + return 0; + + case IPlatform::kFailed: log((CLOG_CRIT "failed to uninstall service")); return 16; + + case IPlatform::kAlready: + log((CLOG_CRIT "service isn't installed")); + return 16; } - log((CLOG_PRINT "uninstalled successfully")); - return 0; } // load configuration @@ -740,17 +752,23 @@ WinMain( int result; if (s_daemon) { if (CWin32Platform::isWindows95Family()) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup95); + result = -1; } else { result = platform.daemonize(DAEMON_NAME, &daemonStartup); } if (result == -1) { - log((CLOG_CRIT "failed to start as a service")); + log((CLOG_CRIT "failed to start as a service" BYE, pname)); return 16; } } else { + // if a daemon then redirect log + if (s_daemon) { + platform.installDaemonLogger(__argv[0]); + } + + // run result = restartableMain(); } From 57fb87ad1065c337ad8fe1ce9720aeb6206db833 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 Jun 2002 12:02:26 +0000 Subject: [PATCH 169/807] refactored ISocket into IDataSocket. the latter and IListenSocket now derive from ISocket. --- client/CClient.cpp | 4 ++-- net/CTCPListenSocket.cpp | 2 +- net/CTCPListenSocket.h | 6 ++++-- net/CTCPSocket.cpp | 34 +++++++++++++++++----------------- net/CTCPSocket.h | 8 +++++--- net/IDataSocket.h | 28 ++++++++++++++++++++++++++++ net/IListenSocket.h | 19 ++++++++----------- net/ISocket.h | 10 ---------- server/CHTTPServer.cpp | 4 ++-- server/CHTTPServer.h | 4 ++-- server/CServer.cpp | 10 +++++----- synergy/CTCPSocketFactory.cpp | 2 +- synergy/CTCPSocketFactory.h | 2 +- synergy/ISocketFactory.h | 4 ++-- 14 files changed, 78 insertions(+), 59 deletions(-) create mode 100644 net/IDataSocket.h diff --git a/client/CClient.cpp b/client/CClient.cpp index 394efdf0..579f007d 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -197,7 +197,7 @@ CClient::runSession(void*) { log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); - std::auto_ptr socket; + std::auto_ptr socket; std::auto_ptr input; std::auto_ptr output; try { @@ -209,7 +209,7 @@ CClient::runSession(void*) // create socket and attempt to connect to server log((CLOG_DEBUG1 "connecting to server")); - assign(socket, new CTCPSocket(), ISocket); // FIXME -- use factory + assign(socket, new CTCPSocket(), IDataSocket); // FIXME -- use factory socket->connect(*m_serverAddress); log((CLOG_INFO "connected to server")); break; diff --git a/net/CTCPListenSocket.cpp b/net/CTCPListenSocket.cpp index f7f2266e..abf72602 100644 --- a/net/CTCPListenSocket.cpp +++ b/net/CTCPListenSocket.cpp @@ -43,7 +43,7 @@ CTCPListenSocket::bind( } } -ISocket* +IDataSocket* CTCPListenSocket::accept() { CNetwork::PollEntry pfds[1]; diff --git a/net/CTCPListenSocket.h b/net/CTCPListenSocket.h index 3120d00b..d89f0049 100644 --- a/net/CTCPListenSocket.h +++ b/net/CTCPListenSocket.h @@ -13,11 +13,13 @@ public: // accessors - // IListenSocket overrides + // ISocket overrides virtual void bind(const CNetworkAddress&); - virtual ISocket* accept(); virtual void close(); + // IListenSocket overrides + virtual IDataSocket* accept(); + private: CNetwork::Socket m_fd; }; diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index fdc640f3..6881f7b7 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -67,23 +67,6 @@ CTCPSocket::bind( } } -void -CTCPSocket::connect( - const CNetworkAddress& addr) -{ - CThread::testCancel(); - if (CNetwork::connect(m_fd, addr.getAddress(), - addr.getAddressLength()) == CNetwork::Error) { - CThread::testCancel(); - throw XSocketConnect(); - } - - // start servicing the socket - m_connected = kReadWrite; - m_thread = new CThread(new TMethodJob( - this, &CTCPSocket::ioThread)); -} - void CTCPSocket::close() { @@ -124,6 +107,23 @@ CTCPSocket::close() } } +void +CTCPSocket::connect( + const CNetworkAddress& addr) +{ + CThread::testCancel(); + if (CNetwork::connect(m_fd, addr.getAddress(), + addr.getAddressLength()) == CNetwork::Error) { + CThread::testCancel(); + throw XSocketConnect(); + } + + // start servicing the socket + m_connected = kReadWrite; + m_thread = new CThread(new TMethodJob( + this, &CTCPSocket::ioThread)); +} + IInputStream* CTCPSocket::getInputStream() { diff --git a/net/CTCPSocket.h b/net/CTCPSocket.h index 980e0dec..a38789df 100644 --- a/net/CTCPSocket.h +++ b/net/CTCPSocket.h @@ -1,7 +1,7 @@ #ifndef CTCPSOCKET_H #define CTCPSOCKET_H -#include "ISocket.h" +#include "IDataSocket.h" #include "CNetwork.h" class CMutex; @@ -11,7 +11,7 @@ class CThread; class CBufferedInputStream; class CBufferedOutputStream; -class CTCPSocket : public ISocket { +class CTCPSocket : public IDataSocket { public: CTCPSocket(); CTCPSocket(CNetwork::Socket); @@ -23,8 +23,10 @@ public: // ISocket overrides virtual void bind(const CNetworkAddress&); - virtual void connect(const CNetworkAddress&); virtual void close(); + + // IDataSocket overrides + virtual void connect(const CNetworkAddress&); virtual IInputStream* getInputStream(); virtual IOutputStream* getOutputStream(); diff --git a/net/IDataSocket.h b/net/IDataSocket.h new file mode 100644 index 00000000..faf645b8 --- /dev/null +++ b/net/IDataSocket.h @@ -0,0 +1,28 @@ +#ifndef IDATASOCKET_H +#define IDATASOCKET_H + +#include "ISocket.h" + +class IInputStream; +class IOutputStream; + +class IDataSocket : public ISocket { +public: + // manipulators + + // connect the socket + virtual void connect(const CNetworkAddress&) = 0; + + // get the input and output streams for the socket. closing + // these streams closes the appropriate half of the socket. + virtual IInputStream* getInputStream() = 0; + virtual IOutputStream* getOutputStream() = 0; + + // accessors + + // ISocket overrides + virtual void bind(const CNetworkAddress&) = 0; + virtual void close() = 0; +}; + +#endif diff --git a/net/IListenSocket.h b/net/IListenSocket.h index dd454976..51d73cfc 100644 --- a/net/IListenSocket.h +++ b/net/IListenSocket.h @@ -1,25 +1,22 @@ #ifndef ILISTENSOCKET_H #define ILISTENSOCKET_H -#include "IInterface.h" +#include "ISocket.h" -class CNetworkAddress; -class ISocket; +class IDataSocket; -class IListenSocket : public IInterface { +class IListenSocket : public ISocket { public: // manipulators - // bind the socket to a particular address - virtual void bind(const CNetworkAddress&) = 0; - // wait for a connection - virtual ISocket* accept() = 0; + virtual IDataSocket* accept() = 0; - // close the socket - virtual void close() = 0; - // accessors + + // ISocket overrides + virtual void bind(const CNetworkAddress&) = 0; + virtual void close() = 0; }; #endif diff --git a/net/ISocket.h b/net/ISocket.h index 6c853b59..8ca8fbf0 100644 --- a/net/ISocket.h +++ b/net/ISocket.h @@ -4,8 +4,6 @@ #include "IInterface.h" class CNetworkAddress; -class IInputStream; -class IOutputStream; class ISocket : public IInterface { public: @@ -14,18 +12,10 @@ public: // bind the socket to a particular address virtual void bind(const CNetworkAddress&) = 0; - // connect the socket - virtual void connect(const CNetworkAddress&) = 0; - // close the socket. this will flush the output stream if it // hasn't been closed yet. virtual void close() = 0; - // get the input and output streams for the socket. closing - // these streams closes the appropriate half of the socket. - virtual IInputStream* getInputStream() = 0; - virtual IOutputStream* getOutputStream() = 0; - // accessors }; diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index 36f46967..892cee3e 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -3,7 +3,7 @@ #include "CHTTPProtocol.h" #include "CServer.h" #include "XHTTP.h" -#include "ISocket.h" +#include "IDataSocket.h" #include "XThread.h" #include "CLog.h" #include "stdset.h" @@ -32,7 +32,7 @@ CHTTPServer::~CHTTPServer() void CHTTPServer::processRequest( - ISocket* socket) + IDataSocket* socket) { assert(socket != NULL); diff --git a/server/CHTTPServer.h b/server/CHTTPServer.h index 9d9ac35e..f8c9b241 100644 --- a/server/CHTTPServer.h +++ b/server/CHTTPServer.h @@ -9,7 +9,7 @@ class CServer; class CConfig; class CHTTPRequest; class CHTTPReply; -class ISocket; +class IDataSocket; class CHTTPServer { public: @@ -19,7 +19,7 @@ public: // manipulators // synchronously process an HTTP request on the given socket - void processRequest(ISocket*); + void processRequest(IDataSocket*); // accessors diff --git a/server/CServer.cpp b/server/CServer.cpp index fb30944e..79e59f55 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -9,8 +9,8 @@ #include "XScreen.h" #include "XSynergy.h" #include "CNetworkAddress.h" +#include "IDataSocket.h" #include "IListenSocket.h" -#include "ISocket.h" #include "ISocketFactory.h" #include "XSocket.h" #include "CLock.h" @@ -1088,7 +1088,7 @@ CServer::acceptClients(void*) for (;;) { // accept connection CThread::testCancel(); - ISocket* socket = listen->accept(); + IDataSocket* socket = listen->accept(); log((CLOG_NOTE "accepted client connection")); CThread::testCancel(); @@ -1111,7 +1111,7 @@ CServer::handshakeClient( // get the socket pointer from the argument assert(vsocket != NULL); - std::auto_ptr socket(reinterpret_cast(vsocket)); + std::auto_ptr socket(reinterpret_cast(vsocket)); // add this thread to the list of threads to cancel. remove from // list in d'tor. @@ -1289,7 +1289,7 @@ CServer::acceptHTTPClients(void*) // accept connection CThread::testCancel(); - ISocket* socket = listen->accept(); + IDataSocket* socket = listen->accept(); log((CLOG_NOTE "accepted HTTP connection")); CThread::testCancel(); @@ -1313,7 +1313,7 @@ CServer::processHTTPRequest( // list in d'tor. CCleanupNote cleanupNote(this); - ISocket* socket = reinterpret_cast(vsocket); + IDataSocket* socket = reinterpret_cast(vsocket); try { // process the request and force delivery m_httpServer->processRequest(socket); diff --git a/synergy/CTCPSocketFactory.cpp b/synergy/CTCPSocketFactory.cpp index 5c5da342..4067e6bb 100644 --- a/synergy/CTCPSocketFactory.cpp +++ b/synergy/CTCPSocketFactory.cpp @@ -16,7 +16,7 @@ CTCPSocketFactory::~CTCPSocketFactory() // do nothing } -ISocket* +IDataSocket* CTCPSocketFactory::create() const { return new CTCPSocket; diff --git a/synergy/CTCPSocketFactory.h b/synergy/CTCPSocketFactory.h index a320f9ae..2b900bb2 100644 --- a/synergy/CTCPSocketFactory.h +++ b/synergy/CTCPSocketFactory.h @@ -13,7 +13,7 @@ public: // accessors // ISocketFactory overrides - virtual ISocket* create() const; + virtual IDataSocket* create() const; virtual IListenSocket* createListen() const; }; diff --git a/synergy/ISocketFactory.h b/synergy/ISocketFactory.h index 9d0f1104..7b7ff8cd 100644 --- a/synergy/ISocketFactory.h +++ b/synergy/ISocketFactory.h @@ -3,7 +3,7 @@ #include "IInterface.h" -class ISocket; +class IDataSocket; class IListenSocket; class ISocketFactory : public IInterface { @@ -13,7 +13,7 @@ public: // accessors // create sockets - virtual ISocket* create() const = 0; + virtual IDataSocket* create() const = 0; virtual IListenSocket* createListen() const = 0; }; From fe16546a1d8561958098470edf7782e00c957c62 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 Jun 2002 13:31:21 +0000 Subject: [PATCH 170/807] formatting changes. --- base/CLog.cpp | 36 ++---- base/CString.cpp | 20 ++-- base/XBase.cpp | 4 +- client/CClient.cpp | 12 +- client/CMSWindowsSecondaryScreen.cpp | 103 +++++------------ client/CXWindowsSecondaryScreen.cpp | 117 ++++++------------- client/client.cpp | 47 ++------ http/CHTTPProtocol.cpp | 57 +++------ http/XHTTP.cpp | 19 +-- io/CBufferedInputStream.cpp | 16 +-- io/CBufferedOutputStream.cpp | 14 +-- io/CInputStreamFilter.cpp | 4 +- io/COutputStreamFilter.cpp | 4 +- io/CStreamBuffer.cpp | 10 +- mt/CCondVar.cpp | 8 +- mt/CMutex.cpp | 3 +- mt/CThread.cpp | 27 ++--- mt/CThreadRep.cpp | 26 ++--- mt/CTimerThread.cpp | 3 +- net/CNetwork.cpp | 47 +++----- net/CNetworkAddress.cpp | 7 +- net/CTCPListenSocket.cpp | 3 +- net/CTCPSocket.cpp | 6 +- net/XNetwork.cpp | 6 +- net/XSocket.cpp | 9 +- platform/CMSWindowsClipboard.cpp | 25 ++-- platform/CMSWindowsScreen.cpp | 22 +--- platform/CUnixPlatform.cpp | 32 ++---- platform/CWin32Platform.cpp | 94 +++++---------- platform/CXWindowsClipboard.cpp | 136 +++++++--------------- platform/CXWindowsScreen.cpp | 35 ++---- platform/CXWindowsUtil.cpp | 56 +++------ server/CConfig.cpp | 78 ++++--------- server/CHTTPServer.cpp | 75 ++++-------- server/CMSWindowsPrimaryScreen.cpp | 48 +++----- server/CServer.cpp | 165 ++++++++------------------- server/CServerProtocol.cpp | 17 +-- server/CServerProtocol1_0.cpp | 50 +++----- server/CSynergyHook.cpp | 51 ++------- server/CXWindowsPrimaryScreen.cpp | 70 ++++-------- server/server.cpp | 51 +++------ synergy/CClipboard.cpp | 33 ++---- synergy/CInputPacketStream.cpp | 8 +- synergy/COutputPacketStream.cpp | 8 +- synergy/CProtocolUtil.cpp | 27 +---- synergy/XSynergy.cpp | 10 +- 46 files changed, 485 insertions(+), 1214 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index bb9679d6..1bf9e0e0 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -54,9 +54,7 @@ CLog::Lock CLog::s_lock = &CLog::dummyLock; int CLog::s_maxPriority = -1; void -CLog::print( - const char* fmt, - ...) +CLog::print(const char* fmt, ...) { // check if fmt begins with a priority argument int priority = 4; @@ -90,11 +88,7 @@ CLog::print( } void -CLog::printt( - const char* file, - int line, - const char* fmt, - ...) +CLog::printt(const char* file, int line, const char* fmt, ...) { // check if fmt begins with a priority argument int priority = 4; @@ -141,8 +135,7 @@ CLog::printt( } void -CLog::setOutputter( - Outputter outputter) +CLog::setOutputter(Outputter outputter) { CHoldLock lock(s_lock); s_outputter = outputter; @@ -156,8 +149,7 @@ CLog::getOutputter() } void -CLog::setLock( - Lock newLock) +CLog::setLock(Lock newLock) { CHoldLock lock(s_lock); s_lock = (newLock == NULL) ? dummyLock : newLock; @@ -171,8 +163,7 @@ CLog::getLock() } bool -CLog::setFilter( - const char* maxPriority) +CLog::setFilter(const char* maxPriority) { if (maxPriority != NULL) { for (int i = 0; i < g_numPriority; ++i) { @@ -187,8 +178,7 @@ CLog::setFilter( } void -CLog::setFilter( - int maxPriority) +CLog::setFilter(int maxPriority) { CHoldLock lock(s_lock); s_maxPriority = maxPriority; @@ -202,8 +192,7 @@ CLog::getFilter() } void -CLog::dummyLock( - bool) +CLog::dummyLock(bool) { // do nothing } @@ -222,9 +211,7 @@ CLog::getMaxPriority() } void -CLog::output( - int priority, - char* msg) +CLog::output(int priority, char* msg) { assert(priority >= -1 && priority < g_numPriority); assert(msg != NULL); @@ -257,12 +244,7 @@ CLog::output( } char* -CLog::vsprint( - int pad, - char* buffer, - int len, - const char* fmt, - va_list args) +CLog::vsprint(int pad, char* buffer, int len, const char* fmt, va_list args) { assert(len > 0); diff --git a/base/CString.cpp b/base/CString.cpp index 2ef952ee..775fe912 100644 --- a/base/CString.cpp +++ b/base/CString.cpp @@ -8,8 +8,8 @@ bool CStringUtil::CaselessCmp::cmpEqual( - const CString::value_type& a, - const CString::value_type& b) + const CString::value_type& a, + const CString::value_type& b) { // FIXME -- use std::tolower but not in all versions of libstdc++ return tolower(a) == tolower(b); @@ -17,17 +17,15 @@ CStringUtil::CaselessCmp::cmpEqual( bool CStringUtil::CaselessCmp::cmpLess( - const CString::value_type& a, - const CString::value_type& b) + const CString::value_type& a, + const CString::value_type& b) { // FIXME -- use std::tolower but not in all versions of libstdc++ return tolower(a) < tolower(b); } bool -CStringUtil::CaselessCmp::less( - const CString& a, - const CString& b) +CStringUtil::CaselessCmp::less(const CString& a, const CString& b) { return std::lexicographical_compare( a.begin(), a.end(), @@ -36,17 +34,13 @@ CStringUtil::CaselessCmp::less( } bool -CStringUtil::CaselessCmp::equal( - const CString& a, - const CString& b) +CStringUtil::CaselessCmp::equal(const CString& a, const CString& b) { return !(less(a, b) || less(b, a)); } bool -CStringUtil::CaselessCmp::operator()( - const CString& a, - const CString& b) const +CStringUtil::CaselessCmp::operator()(const CString& a, const CString& b) const { return less(a, b); } diff --git a/base/XBase.cpp b/base/XBase.cpp index c23a77ee..575ca4f6 100644 --- a/base/XBase.cpp +++ b/base/XBase.cpp @@ -44,9 +44,7 @@ XBase::what() const } CString -XBase::format( - const char* /*id*/, - const char* fmt, ...) const throw() +XBase::format(const char* /*id*/, const char* fmt, ...) const throw() { // FIXME -- use id to lookup formating string // FIXME -- format string with arguments diff --git a/client/CClient.cpp b/client/CClient.cpp index 579f007d..0ec793fe 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -28,8 +28,7 @@ // CClient // -CClient::CClient( - const CString& clientName) : +CClient::CClient(const CString& clientName) : m_name(clientName), m_input(NULL), m_output(NULL), @@ -48,15 +47,13 @@ CClient::~CClient() } void -CClient::camp( - bool on) +CClient::camp(bool on) { m_camp = on; } bool -CClient::run( - const CNetworkAddress& serverAddress) +CClient::run(const CNetworkAddress& serverAddress) { CThread* thread = NULL; try { @@ -140,8 +137,7 @@ CClient::quit() } void -CClient::onClipboardChanged( - ClipboardID id) +CClient::onClipboardChanged(ClipboardID id) { log((CLOG_DEBUG "sending clipboard %d changed", id)); CLock lock(&m_mutex); diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index b0fbdcf1..9e7c14ef 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -69,8 +69,7 @@ CMSWindowsSecondaryScreen::stop() } void -CMSWindowsSecondaryScreen::open( - CClient* client) +CMSWindowsSecondaryScreen::open(CClient* client) { assert(m_client == NULL); assert(client != NULL); @@ -108,10 +107,7 @@ CMSWindowsSecondaryScreen::close() } void -CMSWindowsSecondaryScreen::enter( - SInt32 x, - SInt32 y, - KeyModifierMask mask) +CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -182,9 +178,7 @@ CMSWindowsSecondaryScreen::leave() } void -CMSWindowsSecondaryScreen::keyDown( - KeyID key, - KeyModifierMask mask) +CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) { Keystrokes keys; UINT virtualKey; @@ -224,10 +218,8 @@ CMSWindowsSecondaryScreen::keyDown( } void -CMSWindowsSecondaryScreen::keyRepeat( - KeyID key, - KeyModifierMask mask, - SInt32 count) +CMSWindowsSecondaryScreen::keyRepeat(KeyID key, + KeyModifierMask mask, SInt32 count) { Keystrokes keys; UINT virtualKey; @@ -248,9 +240,7 @@ CMSWindowsSecondaryScreen::keyRepeat( } void -CMSWindowsSecondaryScreen::keyUp( - KeyID key, - KeyModifierMask mask) +CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) { Keystrokes keys; UINT virtualKey; @@ -311,8 +301,7 @@ CMSWindowsSecondaryScreen::keyUp( } void -CMSWindowsSecondaryScreen::mouseDown( - ButtonID button) +CMSWindowsSecondaryScreen::mouseDown(ButtonID button) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -328,8 +317,7 @@ CMSWindowsSecondaryScreen::mouseDown( } void -CMSWindowsSecondaryScreen::mouseUp( - ButtonID button) +CMSWindowsSecondaryScreen::mouseUp(ButtonID button) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -345,9 +333,7 @@ CMSWindowsSecondaryScreen::mouseUp( } void -CMSWindowsSecondaryScreen::mouseMove( - SInt32 x, - SInt32 y) +CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -362,8 +348,7 @@ CMSWindowsSecondaryScreen::mouseMove( } void -CMSWindowsSecondaryScreen::mouseWheel( - SInt32 delta) +CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -373,9 +358,8 @@ CMSWindowsSecondaryScreen::mouseWheel( } void -CMSWindowsSecondaryScreen::setClipboard( - ClipboardID /*id*/, - const IClipboard* src) +CMSWindowsSecondaryScreen::setClipboard(ClipboardID /*id*/, + const IClipboard* src) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -385,8 +369,7 @@ CMSWindowsSecondaryScreen::setClipboard( } void -CMSWindowsSecondaryScreen::grabClipboard( - ClipboardID /*id*/) +CMSWindowsSecondaryScreen::grabClipboard(ClipboardID /*id*/) { CLock lock(&m_mutex); assert(m_window != NULL); @@ -398,9 +381,7 @@ CMSWindowsSecondaryScreen::grabClipboard( } void -CMSWindowsSecondaryScreen::getMousePos( - SInt32* x, - SInt32* y) const +CMSWindowsSecondaryScreen::getMousePos(SInt32* x, SInt32* y) const { assert(x != NULL); assert(y != NULL); @@ -421,9 +402,7 @@ CMSWindowsSecondaryScreen::getMousePos( } void -CMSWindowsSecondaryScreen::getSize( - SInt32* width, - SInt32* height) const +CMSWindowsSecondaryScreen::getSize(SInt32* width, SInt32* height) const { getScreenSize(width, height); } @@ -435,9 +414,8 @@ CMSWindowsSecondaryScreen::getJumpZoneSize() const } void -CMSWindowsSecondaryScreen::getClipboard( - ClipboardID /*id*/, - IClipboard* dst) const +CMSWindowsSecondaryScreen::getClipboard(ClipboardID /*id*/, + IClipboard* dst) const { CLock lock(&m_mutex); assert(m_window != NULL); @@ -486,8 +464,7 @@ CMSWindowsSecondaryScreen::onCloseDisplay() } bool -CMSWindowsSecondaryScreen::onPreTranslate( - MSG* msg) +CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg) { // handle event switch (msg->message) { @@ -511,11 +488,8 @@ CMSWindowsSecondaryScreen::onPreTranslate( } LRESULT -CMSWindowsSecondaryScreen::onEvent( - HWND hwnd, - UINT msg, - WPARAM wParam, - LPARAM lParam) +CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_QUERYENDSESSION: @@ -582,9 +556,7 @@ CMSWindowsSecondaryScreen::onEvent( } void -CMSWindowsSecondaryScreen::onEnter( - SInt32 x, - SInt32 y) +CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y) { // warp to requested location SInt32 w, h; @@ -655,8 +627,7 @@ CMSWindowsSecondaryScreen::closeDesktop() } bool -CMSWindowsSecondaryScreen::switchDesktop( - HDESK desk) +CMSWindowsSecondaryScreen::switchDesktop(HDESK desk) { CLock lock(&m_mutex); @@ -1255,9 +1226,7 @@ static const UINT* g_mapTable[] = }; DWORD -CMSWindowsSecondaryScreen::mapButton( - ButtonID button, - bool press) const +CMSWindowsSecondaryScreen::mapButton(ButtonID button, bool press) const { // map button id to button flag switch (button) { @@ -1276,12 +1245,8 @@ CMSWindowsSecondaryScreen::mapButton( } KeyModifierMask -CMSWindowsSecondaryScreen::mapKey( - Keystrokes& keys, - UINT& virtualKey, - KeyID id, - KeyModifierMask mask, - EKeyAction action) const +CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, + KeyID id, KeyModifierMask mask, EKeyAction action) const { // lookup the key table const UInt32 mapID = ((id >> 8) & 0xff); @@ -1597,9 +1562,7 @@ CMSWindowsSecondaryScreen::mapKey( } void -CMSWindowsSecondaryScreen::doKeystrokes( - const Keystrokes& keys, - SInt32 count) +CMSWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) { // do nothing if no keys or no repeats if (count < 1 || keys.empty()) { @@ -1687,9 +1650,7 @@ CMSWindowsSecondaryScreen::updateModifiers() } void -CMSWindowsSecondaryScreen::toggleKey( - UINT virtualKey, - KeyModifierMask mask) +CMSWindowsSecondaryScreen::toggleKey(UINT virtualKey, KeyModifierMask mask) { // send key events to simulate a press and release sendKeyEvent(virtualKey, true); @@ -1701,8 +1662,7 @@ CMSWindowsSecondaryScreen::toggleKey( } UINT -CMSWindowsSecondaryScreen::virtualKeyToScanCode( - UINT& virtualKey) +CMSWindowsSecondaryScreen::virtualKeyToScanCode(UINT& virtualKey) { // try mapping given virtual key UINT code = MapVirtualKey(virtualKey & 0xff, 0); @@ -1754,8 +1714,7 @@ CMSWindowsSecondaryScreen::virtualKeyToScanCode( } bool -CMSWindowsSecondaryScreen::isExtendedKey( - UINT virtualKey) +CMSWindowsSecondaryScreen::isExtendedKey(UINT virtualKey) { // see if we've already encoded the extended flag if ((virtualKey & 0x100) != 0) { @@ -1778,9 +1737,7 @@ CMSWindowsSecondaryScreen::isExtendedKey( } void -CMSWindowsSecondaryScreen::sendKeyEvent( - UINT virtualKey, - bool press) +CMSWindowsSecondaryScreen::sendKeyEvent(UINT virtualKey, bool press) { DWORD flags = 0; if (isExtendedKey(virtualKey)) { diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index b4f12c8c..3f9e754d 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -72,8 +72,7 @@ CXWindowsSecondaryScreen::stop() } void -CXWindowsSecondaryScreen::open( - CClient* client) +CXWindowsSecondaryScreen::open(CClient* client) { assert(m_client == NULL); assert(client != NULL); @@ -126,10 +125,7 @@ CXWindowsSecondaryScreen::close() } void -CXWindowsSecondaryScreen::enter( - SInt32 x, - SInt32 y, - KeyModifierMask mask) +CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) { assert(m_window != None); @@ -168,9 +164,7 @@ CXWindowsSecondaryScreen::leave() } void -CXWindowsSecondaryScreen::keyDown( - KeyID key, - KeyModifierMask mask) +CXWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) { Keystrokes keys; KeyCode keycode; @@ -190,10 +184,8 @@ CXWindowsSecondaryScreen::keyDown( } void -CXWindowsSecondaryScreen::keyRepeat( - KeyID key, - KeyModifierMask mask, - SInt32 count) +CXWindowsSecondaryScreen::keyRepeat(KeyID key, + KeyModifierMask mask, SInt32 count) { Keystrokes keys; KeyCode keycode; @@ -210,9 +202,7 @@ CXWindowsSecondaryScreen::keyRepeat( } void -CXWindowsSecondaryScreen::keyUp( - KeyID key, - KeyModifierMask mask) +CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) { Keystrokes keys; KeyCode keycode; @@ -232,8 +222,7 @@ CXWindowsSecondaryScreen::keyUp( } void -CXWindowsSecondaryScreen::mouseDown( - ButtonID button) +CXWindowsSecondaryScreen::mouseDown(ButtonID button) { CDisplayLock display(this); XTestFakeButtonEvent(display, mapButton(button), True, CurrentTime); @@ -241,8 +230,7 @@ CXWindowsSecondaryScreen::mouseDown( } void -CXWindowsSecondaryScreen::mouseUp( - ButtonID button) +CXWindowsSecondaryScreen::mouseUp(ButtonID button) { CDisplayLock display(this); XTestFakeButtonEvent(display, mapButton(button), False, CurrentTime); @@ -250,9 +238,7 @@ CXWindowsSecondaryScreen::mouseUp( } void -CXWindowsSecondaryScreen::mouseMove( - SInt32 x, - SInt32 y) +CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) { CDisplayLock display(this); XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime); @@ -260,8 +246,7 @@ CXWindowsSecondaryScreen::mouseMove( } void -CXWindowsSecondaryScreen::mouseWheel( - SInt32 delta) +CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) { // choose button depending on rotation direction const unsigned int button = (delta >= 0) ? 4 : 5; @@ -281,24 +266,20 @@ CXWindowsSecondaryScreen::mouseWheel( } void -CXWindowsSecondaryScreen::setClipboard( - ClipboardID id, - const IClipboard* clipboard) +CXWindowsSecondaryScreen::setClipboard(ClipboardID id, + const IClipboard* clipboard) { setDisplayClipboard(id, clipboard); } void -CXWindowsSecondaryScreen::grabClipboard( - ClipboardID id) +CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) { setDisplayClipboard(id, NULL); } void -CXWindowsSecondaryScreen::getMousePos( - SInt32* x, - SInt32* y) const +CXWindowsSecondaryScreen::getMousePos(SInt32* x, SInt32* y) const { CDisplayLock display(this); int xTmp, yTmp, dummy; @@ -311,9 +292,7 @@ CXWindowsSecondaryScreen::getMousePos( } void -CXWindowsSecondaryScreen::getSize( - SInt32* width, - SInt32* height) const +CXWindowsSecondaryScreen::getSize(SInt32* width, SInt32* height) const { getScreenSize(width, height); } @@ -325,16 +304,14 @@ CXWindowsSecondaryScreen::getJumpZoneSize() const } void -CXWindowsSecondaryScreen::getClipboard( - ClipboardID id, - IClipboard* clipboard) const +CXWindowsSecondaryScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const { getDisplayClipboard(id, clipboard); } void -CXWindowsSecondaryScreen::onOpenDisplay( - Display* display) +CXWindowsSecondaryScreen::onOpenDisplay(Display* display) { assert(m_window == None); @@ -362,16 +339,14 @@ CXWindowsSecondaryScreen::onOpenDisplay( } CXWindowsClipboard* -CXWindowsSecondaryScreen::createClipboard( - ClipboardID id) +CXWindowsSecondaryScreen::createClipboard(ClipboardID id) { CDisplayLock display(this); return new CXWindowsClipboard(display, m_window, id); } void -CXWindowsSecondaryScreen::onCloseDisplay( - Display* display) +CXWindowsSecondaryScreen::onCloseDisplay(Display* display) { assert(m_window != None); @@ -386,16 +361,14 @@ CXWindowsSecondaryScreen::onCloseDisplay( } void -CXWindowsSecondaryScreen::onLostClipboard( - ClipboardID id) +CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id) { // tell client that the clipboard was grabbed locally m_client->onClipboardChanged(id); } void -CXWindowsSecondaryScreen::leaveNoLock( - Display* display) +CXWindowsSecondaryScreen::leaveNoLock(Display* display) { assert(display != NULL); assert(m_window != None); @@ -417,20 +390,15 @@ CXWindowsSecondaryScreen::leaveNoLock( } unsigned int -CXWindowsSecondaryScreen::mapButton( - ButtonID id) const +CXWindowsSecondaryScreen::mapButton(ButtonID id) const { // FIXME -- should use button mapping? return static_cast(id); } KeyModifierMask -CXWindowsSecondaryScreen::mapKey( - Keystrokes& keys, - KeyCode& keycode, - KeyID id, - KeyModifierMask mask, - EKeyAction action) const +CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, + KeyID id, KeyModifierMask mask, EKeyAction action) const { // note -- must have display locked on entry @@ -654,11 +622,8 @@ CXWindowsSecondaryScreen::mapKey( } bool -CXWindowsSecondaryScreen::findKeyCode( - KeyCode& keycode, - unsigned int& maskOut, - KeyID id, - unsigned int maskIn) const +CXWindowsSecondaryScreen::findKeyCode(KeyCode& keycode, + unsigned int& maskOut, KeyID id, unsigned int maskIn) const { // if XK_Tab is requested with shift active then try XK_ISO_Left_Tab // instead. if that doesn't work, we'll fall back to XK_Tab with @@ -787,9 +752,7 @@ CXWindowsSecondaryScreen::findKeyCode( } void -CXWindowsSecondaryScreen::doKeystrokes( - const Keystrokes& keys, - SInt32 count) +CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) { // do nothing if no keys or no repeats if (count < 1 || keys.empty()) { @@ -830,8 +793,7 @@ CXWindowsSecondaryScreen::doKeystrokes( } unsigned int -CXWindowsSecondaryScreen::maskToX( - KeyModifierMask inMask) const +CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const { // FIXME -- should be configurable. also not using Mod3Mask. unsigned int outMask = 0; @@ -860,8 +822,7 @@ CXWindowsSecondaryScreen::maskToX( } void -CXWindowsSecondaryScreen::updateKeys( - Display* display) +CXWindowsSecondaryScreen::updateKeys(Display* display) { // ask server which keys are pressed char keys[32]; @@ -881,8 +842,7 @@ CXWindowsSecondaryScreen::updateKeys( } void -CXWindowsSecondaryScreen::updateModifiers( - Display* display) +CXWindowsSecondaryScreen::updateModifiers(Display* display) { // query the pointer to get the keyboard state Window root, window; @@ -911,8 +871,7 @@ CXWindowsSecondaryScreen::updateModifiers( } void -CXWindowsSecondaryScreen::updateKeycodeMap( - Display* display) +CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) { // get the number of keycodes int minKeycode, maxKeycode; @@ -966,8 +925,7 @@ CXWindowsSecondaryScreen::updateKeycodeMap( } void -CXWindowsSecondaryScreen::updateModifierMap( - Display* display) +CXWindowsSecondaryScreen::updateModifierMap(Display* display) { // get modifier map from server XModifierKeymap* keymap = XGetModifierMapping(display); @@ -1022,10 +980,8 @@ CXWindowsSecondaryScreen::updateModifierMap( } void -CXWindowsSecondaryScreen::toggleKey( - Display* display, - KeySym keysym, - unsigned int mask) +CXWindowsSecondaryScreen::toggleKey(Display* display, + KeySym keysym, unsigned int mask) { // lookup the keycode KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym); @@ -1051,8 +1007,7 @@ CXWindowsSecondaryScreen::toggleKey( } bool -CXWindowsSecondaryScreen::isToggleKeysym( - KeySym key) +CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) { switch (key) { case XK_Caps_Lock: diff --git a/client/client.cpp b/client/client.cpp index fbd89dfa..ea23b151 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -44,8 +44,7 @@ static CMutex* s_logMutex = NULL; static void -logLock( - bool lock) +logLock(bool lock) { assert(s_logMutex != NULL); @@ -66,8 +65,7 @@ static CClient* s_client = NULL; static int -realMain( - CMutex* mutex) +realMain(CMutex* mutex) { try { // initialize threading library @@ -242,12 +240,9 @@ PLATFORM_DESC static bool -isArg(int argi, - int argc, - const char** argv, - const char* name1, - const char* name2, - int minRequiredParameters = 0) +isArg(int argi, int argc, const char** argv, + const char* name1, const char* name2, + int minRequiredParameters = 0) { if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || (name2 != NULL && strcmp(argv[argi], name2) == 0)) { @@ -266,9 +261,7 @@ isArg(int argi, static void -parse( - int argc, - const char** argv) +parse(int argc, const char** argv) { assert(pname != NULL); assert(argv != NULL); @@ -443,9 +436,7 @@ parse( static bool -logMessageBox( - int priority, - const char* msg) +logMessageBox(int priority, const char* msg) { if (priority <= CLog::kFATAL) { MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); @@ -472,10 +463,7 @@ daemonStop(void) static int -daemonStartup( - IPlatform* iplatform, - int argc, - const char** argv) +daemonStartup(IPlatform* iplatform, int argc, const char** argv) { // get platform pointer CWin32Platform* platform = static_cast(iplatform); @@ -498,9 +486,7 @@ daemonStartup( static bool -logDiscard( - int, - const char*) +logDiscard(int, const char*) { return true; } @@ -520,11 +506,7 @@ checkParse(int e) } int WINAPI -WinMain( - HINSTANCE instance, - HINSTANCE, - LPSTR, - int) +WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { CPlatform platform; @@ -656,18 +638,13 @@ WinMain( static int -daemonStartup( - IPlatform*, - int, - const char**) +daemonStartup(IPlatform*, int, const char**) { return restartableMain(); } int -main( - int argc, - char** argv) +main(int argc, char** argv) { CPlatform platform; diff --git a/http/CHTTPProtocol.cpp b/http/CHTTPProtocol.cpp index 3a668f84..a03011f1 100644 --- a/http/CHTTPProtocol.cpp +++ b/http/CHTTPProtocol.cpp @@ -23,9 +23,7 @@ CHTTPRequest::~CHTTPRequest() } void -CHTTPRequest::insertHeader( - const CString& name, - const CString& value) +CHTTPRequest::insertHeader(const CString& name, const CString& value) { CHeaderMap::iterator index = m_headerByName.find(name); if (index != m_headerByName.end()) { @@ -39,9 +37,7 @@ CHTTPRequest::insertHeader( } void -CHTTPRequest::appendHeader( - const CString& name, - const CString& value) +CHTTPRequest::appendHeader(const CString& name, const CString& value) { CHeaderMap::iterator index = m_headerByName.find(name); if (index != m_headerByName.end()) { @@ -56,8 +52,7 @@ CHTTPRequest::appendHeader( } void -CHTTPRequest::eraseHeader( - const CString& name) +CHTTPRequest::eraseHeader(const CString& name) { CHeaderMap::iterator index = m_headerByName.find(name); if (index != m_headerByName.end()) { @@ -66,15 +61,13 @@ CHTTPRequest::eraseHeader( } bool -CHTTPRequest::isHeader( - const CString& name) const +CHTTPRequest::isHeader(const CString& name) const { return (m_headerByName.find(name) != m_headerByName.end()); } CString -CHTTPRequest::getHeader( - const CString& name) const +CHTTPRequest::getHeader(const CString& name) const { CHeaderMap::const_iterator index = m_headerByName.find(name); if (index != m_headerByName.end()) { @@ -91,9 +84,7 @@ CHTTPRequest::getHeader( // CHTTPRequest* -CHTTPProtocol::readRequest( - IInputStream* stream, - UInt32 maxSize) +CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) { CString scratch; @@ -241,9 +232,7 @@ CHTTPProtocol::readRequest( } void -CHTTPProtocol::reply( - IOutputStream* stream, - CHTTPReply& reply) +CHTTPProtocol::reply(IOutputStream* stream, CHTTPReply& reply) { // suppress body for certain replies bool hasBody = true; @@ -321,9 +310,7 @@ CHTTPProtocol::reply( } bool -CHTTPProtocol::parseFormData( - const CHTTPRequest& request, - CFormParts& parts) +CHTTPProtocol::parseFormData(const CHTTPRequest& request, CFormParts& parts) { static const char formData[] = "multipart/form-data"; static const char boundary[] = "boundary="; @@ -458,9 +445,7 @@ CHTTPProtocol::parseFormData( } CString -CHTTPProtocol::readLine( - IInputStream* stream, - CString& tmpBuffer) +CHTTPProtocol::readLine(IInputStream* stream, CString& tmpBuffer) { // read up to and including a CRLF from stream, using whatever // is in tmpBuffer as if it were at the head of the stream. @@ -493,10 +478,8 @@ CHTTPProtocol::readLine( } CString -CHTTPProtocol::readBlock( - IInputStream* stream, - UInt32 numBytes, - CString& tmpBuffer) +CHTTPProtocol::readBlock(IInputStream* stream, + UInt32 numBytes, CString& tmpBuffer) { CString data; @@ -543,10 +526,8 @@ CHTTPProtocol::readBlock( } CString -CHTTPProtocol::readChunk( - IInputStream* stream, - CString& tmpBuffer, - UInt32* maxSize) +CHTTPProtocol::readChunk(IInputStream* stream, + CString& tmpBuffer, UInt32* maxSize) { CString line; @@ -594,12 +575,9 @@ CHTTPProtocol::readChunk( } void -CHTTPProtocol::readHeaders( - IInputStream* stream, - CHTTPRequest* request, - bool isFooter, - CString& tmpBuffer, - UInt32* maxSize) +CHTTPProtocol::readHeaders(IInputStream* stream, + CHTTPRequest* request, bool isFooter, + CString& tmpBuffer, UInt32* maxSize) { // parse headers. done with headers when we get a blank line. CString name; @@ -652,8 +630,7 @@ CHTTPProtocol::readHeaders( } bool -CHTTPProtocol::isValidToken( - const CString& token) +CHTTPProtocol::isValidToken(const CString& token) { return (token.find("()<>@,;:\\\"/[]?={} " "\0\1\2\3\4\5\6\7" diff --git a/http/XHTTP.cpp b/http/XHTTP.cpp index 6232bc03..8ef27b61 100644 --- a/http/XHTTP.cpp +++ b/http/XHTTP.cpp @@ -6,8 +6,7 @@ // XHTTP // -XHTTP::XHTTP( - SInt32 statusCode) : +XHTTP::XHTTP(SInt32 statusCode) : XBase(), m_status(statusCode), m_reason(getReason(statusCode)) @@ -15,9 +14,7 @@ XHTTP::XHTTP( // do nothing } -XHTTP::XHTTP( - SInt32 statusCode, - const CString& reasonPhrase) : +XHTTP::XHTTP(SInt32 statusCode, const CString& reasonPhrase) : XBase(), m_status(statusCode), m_reason(reasonPhrase) @@ -43,8 +40,7 @@ XHTTP::getReason() const } void -XHTTP::addHeaders( - CHTTPReply&) const +XHTTP::addHeaders(CHTTPReply&) const { // do nothing } @@ -69,8 +65,7 @@ XHTTP::getWhat() const throw() } const char* -XHTTP::getReason( - SInt32 status) +XHTTP::getReason(SInt32 status) { switch (status) { case 300: return "Multiple Choices"; @@ -110,8 +105,7 @@ XHTTP::getReason( // XHTTPAllow // -XHTTPAllow::XHTTPAllow( - const CString& allowed) : +XHTTPAllow::XHTTPAllow(const CString& allowed) : XHTTP(405), m_allowed(allowed) { @@ -124,8 +118,7 @@ XHTTPAllow::~XHTTPAllow() } void -XHTTPAllow::addHeaders( - CHTTPReply& reply) const +XHTTPAllow::addHeaders(CHTTPReply& reply) const { reply.m_headers.push_back(std::make_pair(CString("Allow"), m_allowed)); } diff --git a/io/CBufferedInputStream.cpp b/io/CBufferedInputStream.cpp index 5078b39d..28c6e7a5 100644 --- a/io/CBufferedInputStream.cpp +++ b/io/CBufferedInputStream.cpp @@ -10,9 +10,7 @@ // CBufferedInputStream // -CBufferedInputStream::CBufferedInputStream( - CMutex* mutex, - IJob* closeCB) : +CBufferedInputStream::CBufferedInputStream(CMutex* mutex, IJob* closeCB) : m_mutex(mutex), m_empty(mutex, true), m_closeCB(closeCB), @@ -28,9 +26,7 @@ CBufferedInputStream::~CBufferedInputStream() } void -CBufferedInputStream::write( - const void* data, - UInt32 n) +CBufferedInputStream::write(const void* data, UInt32 n) { if (!m_hungup && n > 0) { m_buffer.write(data, n); @@ -47,9 +43,7 @@ CBufferedInputStream::hangup() } UInt32 -CBufferedInputStream::readNoLock( - void* dst, - UInt32 n) +CBufferedInputStream::readNoLock(void* dst, UInt32 n) { if (m_closed) { throw XIOClosed(); @@ -104,9 +98,7 @@ CBufferedInputStream::close() } UInt32 -CBufferedInputStream::read( - void* dst, - UInt32 n) +CBufferedInputStream::read(void* dst, UInt32 n) { CLock lock(m_mutex); return readNoLock(dst, n); diff --git a/io/CBufferedOutputStream.cpp b/io/CBufferedOutputStream.cpp index 386c2b60..64c61e33 100644 --- a/io/CBufferedOutputStream.cpp +++ b/io/CBufferedOutputStream.cpp @@ -9,9 +9,7 @@ // CBufferedOutputStream // -CBufferedOutputStream::CBufferedOutputStream( - CMutex* mutex, - IJob* closeCB) : +CBufferedOutputStream::CBufferedOutputStream(CMutex* mutex, IJob* closeCB) : m_mutex(mutex), m_closeCB(closeCB), m_empty(mutex, true), @@ -26,15 +24,13 @@ CBufferedOutputStream::~CBufferedOutputStream() } const void* -CBufferedOutputStream::peek( - UInt32 n) +CBufferedOutputStream::peek(UInt32 n) { return m_buffer.peek(n); } void -CBufferedOutputStream::pop( - UInt32 n) +CBufferedOutputStream::pop(UInt32 n) { m_buffer.pop(n); if (m_buffer.getSize() == 0) { @@ -64,9 +60,7 @@ CBufferedOutputStream::close() } UInt32 -CBufferedOutputStream::write( - const void* data, - UInt32 n) +CBufferedOutputStream::write(const void* data, UInt32 n) { CLock lock(m_mutex); if (m_closed) { diff --git a/io/CInputStreamFilter.cpp b/io/CInputStreamFilter.cpp index 276c1a34..b7f239e7 100644 --- a/io/CInputStreamFilter.cpp +++ b/io/CInputStreamFilter.cpp @@ -4,9 +4,7 @@ // CInputStreamFilter // -CInputStreamFilter::CInputStreamFilter( - IInputStream* stream, - bool adopted) : +CInputStreamFilter::CInputStreamFilter(IInputStream* stream, bool adopted) : m_stream(stream), m_adopted(adopted) { diff --git a/io/COutputStreamFilter.cpp b/io/COutputStreamFilter.cpp index 197d626a..2d9aeaef 100644 --- a/io/COutputStreamFilter.cpp +++ b/io/COutputStreamFilter.cpp @@ -4,9 +4,7 @@ // COutputStreamFilter // -COutputStreamFilter::COutputStreamFilter( - IOutputStream* stream, - bool adopted) : +COutputStreamFilter::COutputStreamFilter(IOutputStream* stream, bool adopted) : m_stream(stream), m_adopted(adopted) { diff --git a/io/CStreamBuffer.cpp b/io/CStreamBuffer.cpp index ca6b63e9..fa3e68ad 100644 --- a/io/CStreamBuffer.cpp +++ b/io/CStreamBuffer.cpp @@ -19,8 +19,7 @@ CStreamBuffer::~CStreamBuffer() } const void* -CStreamBuffer::peek( - UInt32 n) +CStreamBuffer::peek(UInt32 n) { assert(n <= m_size); @@ -40,8 +39,7 @@ CStreamBuffer::peek( } void -CStreamBuffer::pop( - UInt32 n) +CStreamBuffer::pop(UInt32 n) { // discard all chunks if n is greater than or equal to m_size if (n >= m_size) { @@ -71,9 +69,7 @@ CStreamBuffer::pop( } void -CStreamBuffer::write( - const void* vdata, - UInt32 n) +CStreamBuffer::write(const void* vdata, UInt32 n) { assert(vdata != NULL); diff --git a/mt/CCondVar.cpp b/mt/CCondVar.cpp index 53d70fc8..397388df 100644 --- a/mt/CCondVar.cpp +++ b/mt/CCondVar.cpp @@ -87,9 +87,7 @@ CCondVarBase::broadcast() } bool -CCondVarBase::wait( - CStopwatch& timer, - double timeout) const +CCondVarBase::wait(CStopwatch& timer, double timeout) const { // check timeout against timer if (timeout >= 0.0) { @@ -245,9 +243,7 @@ CCondVarBase::broadcast() } bool -CCondVarBase::wait( - CStopwatch& timer, - double timeout) const +CCondVarBase::wait(CStopwatch& timer, double timeout) const { // check timeout against timer if (timeout >= 0.0) { diff --git a/mt/CMutex.cpp b/mt/CMutex.cpp index 547dfb18..dfae9faf 100644 --- a/mt/CMutex.cpp +++ b/mt/CMutex.cpp @@ -21,8 +21,7 @@ CMutex::~CMutex() } CMutex& -CMutex::operator=( - const CMutex&) +CMutex::operator=(const CMutex&) { return *this; } diff --git a/mt/CThread.cpp b/mt/CThread.cpp index 74d3d727..c10c0636 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -32,8 +32,7 @@ CThread::~CThread() } CThread& -CThread::operator=( - const CThread& thread) +CThread::operator=(const CThread& thread) { if (thread.m_rep != m_rep) { m_rep->unref(); @@ -50,8 +49,7 @@ CThread::init() } void -CThread::sleep( - double timeout) +CThread::sleep(double timeout) { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); if (timeout >= 0.0) { @@ -62,8 +60,7 @@ CThread::sleep( } void -CThread::exit( - void* result) +CThread::exit(void* result) { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); log((CLOG_DEBUG1 "throw exit on thread %p", currentRep.operator->())); @@ -71,8 +68,7 @@ CThread::exit( } bool -CThread::enableCancel( - bool enable) +CThread::enableCancel(bool enable) { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); return currentRep->enableCancel(enable); @@ -85,8 +81,7 @@ CThread::cancel() } void -CThread::setPriority( - int n) +CThread::setPriority(int n) { m_rep->setPriority(n); } @@ -98,8 +93,7 @@ CThread::getCurrentThread() } bool -CThread::wait( - double timeout) const +CThread::wait(double timeout) const { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); return currentRep->wait(m_rep, timeout); @@ -107,8 +101,7 @@ CThread::wait( #if defined(CONFIG_PLATFORM_WIN32) bool -CThread::waitForEvent( - double timeout) +CThread::waitForEvent(double timeout) { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); return currentRep->waitForEvent(timeout); @@ -139,15 +132,13 @@ CThread::getUserData() } bool -CThread::operator==( - const CThread& thread) const +CThread::operator==(const CThread& thread) const { return (m_rep == thread.m_rep); } bool -CThread::operator!=( - const CThread& thread) const +CThread::operator!=(const CThread& thread) const { return (m_rep != thread.m_rep); } diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 3ef5ec18..cf2d4111 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -402,9 +402,7 @@ CThreadRep::testCancel() } bool -CThreadRep::wait( - CThreadRep* target, - double timeout) +CThreadRep::wait(CThreadRep* target, double timeout) { if (target == this) { return false; @@ -430,8 +428,7 @@ CThreadRep::wait( } void -CThreadRep::setPriority( - int) +CThreadRep::setPriority(int) { // FIXME } @@ -468,15 +465,13 @@ CThreadRep::threadFunc(void* arg) } void -CThreadRep::threadCancel( - int) +CThreadRep::threadCancel(int) { // do nothing } void* -CThreadRep::threadSignalHandler( - void* vrep) +CThreadRep::threadSignalHandler(void* vrep) { CThreadRep* mainThreadRep = reinterpret_cast(vrep); @@ -567,9 +562,7 @@ CThreadRep::testCancel() } bool -CThreadRep::wait( - CThreadRep* target, - double timeout) +CThreadRep::wait(CThreadRep* target, double timeout) { // get the current thread. if it's the same as the target thread // then the thread is waiting on itself. @@ -620,8 +613,7 @@ CThreadRep::wait( } bool -CThreadRep::waitForEvent( - double timeout) +CThreadRep::waitForEvent(double timeout) { // is cancellation enabled? const DWORD n = (isCancellable() ? 1 : 0); @@ -658,8 +650,7 @@ CThreadRep::waitForEvent( } void -CThreadRep::setPriority( - int n) +CThreadRep::setPriority(int n) { DWORD pClass = NORMAL_PRIORITY_CLASS; if (n < 0) { @@ -705,8 +696,7 @@ CThreadRep::getCancelEvent() const } unsigned int __stdcall -CThreadRep::threadFunc( - void* arg) +CThreadRep::threadFunc(void* arg) { CThreadRep* rep = (CThreadRep*)arg; diff --git a/mt/CTimerThread.cpp b/mt/CTimerThread.cpp index b47cc429..93713e19 100644 --- a/mt/CTimerThread.cpp +++ b/mt/CTimerThread.cpp @@ -33,8 +33,7 @@ CTimerThread::~CTimerThread() } void -CTimerThread::timer( - void*) +CTimerThread::timer(void*) { log((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); CThread::sleep(m_timeout); diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp index 23cc9701..29b5418a 100644 --- a/net/CNetwork.cpp +++ b/net/CNetwork.cpp @@ -52,9 +52,7 @@ static HMODULE s_networkModule = NULL; static FARPROC -netGetProcAddress( - HMODULE module, - LPCSTR name) +netGetProcAddress(HMODULE module, LPCSTR name) { FARPROC func = ::GetProcAddress(module, name); if (!func) { @@ -116,8 +114,7 @@ CNetwork::cleanup() } UInt32 -CNetwork::swaphtonl( - UInt32 v) +CNetwork::swaphtonl(UInt32 v) { static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; if (s_endian.b[0] == 0x34) { @@ -132,8 +129,7 @@ CNetwork::swaphtonl( } UInt16 -CNetwork::swaphtons( - UInt16 v) +CNetwork::swaphtons(UInt16 v) { static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; if (s_endian.b[0] == 0x34) { @@ -146,15 +142,13 @@ CNetwork::swaphtons( } UInt32 -CNetwork::swapntohl( - UInt32 v) +CNetwork::swapntohl(UInt32 v) { return swaphtonl(v); } UInt16 -CNetwork::swapntohs( - UInt16 v) +CNetwork::swapntohs(UInt16 v) { return swaphtons(v); } @@ -221,10 +215,7 @@ CNetwork::init2( } int PASCAL FAR -CNetwork::poll2( - PollEntry fd[], - int nfds, - int timeout) +CNetwork::poll2(PollEntry fd[], int nfds, int timeout) { int i; @@ -293,19 +284,13 @@ CNetwork::poll2( } ssize_t PASCAL FAR -CNetwork::read2( - Socket s, - void FAR* buf, - size_t len) +CNetwork::read2(Socket s, void FAR* buf, size_t len) { return recv(s, buf, len, 0); } ssize_t PASCAL FAR -CNetwork::write2( - Socket s, - const void FAR* buf, - size_t len) +CNetwork::write2(Socket s, const void FAR* buf, size_t len) { return send(s, buf, len, 0); } @@ -323,29 +308,25 @@ CNetwork::write2( #define setfunc(var, name, type) var = (type)::name UInt32 -CNetwork::swaphtonl( - UInt32 v) +CNetwork::swaphtonl(UInt32 v) { return htonl(v); } UInt16 -CNetwork::swaphtons( - UInt16 v) +CNetwork::swaphtons(UInt16 v) { return htons(v); } UInt32 -CNetwork::swapntohl( - UInt32 v) +CNetwork::swapntohl(UInt32 v) { return ntohl(v); } UInt16 -CNetwork::swapntohs( - UInt16 v) +CNetwork::swapntohs(UInt16 v) { return ntohs(v); } @@ -366,9 +347,7 @@ myherrno() static int -mygethostname( - char* name, - int namelen) +mygethostname(char* name, int namelen) { return gethostname(name, namelen); } diff --git a/net/CNetworkAddress.cpp b/net/CNetworkAddress.cpp index 28f20537..debd9d32 100644 --- a/net/CNetworkAddress.cpp +++ b/net/CNetworkAddress.cpp @@ -21,8 +21,7 @@ CNetworkAddress::CNetworkAddress() : memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } -CNetworkAddress::CNetworkAddress( - UInt16 port) : +CNetworkAddress::CNetworkAddress(UInt16 port) : m_port(port) { if (port == 0) { @@ -37,9 +36,7 @@ CNetworkAddress::CNetworkAddress( memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } -CNetworkAddress::CNetworkAddress( - const CString& hostname_, - UInt16 port) : +CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : m_hostname(hostname_), m_port(port) { diff --git a/net/CTCPListenSocket.cpp b/net/CTCPListenSocket.cpp index abf72602..fa7950ce 100644 --- a/net/CTCPListenSocket.cpp +++ b/net/CTCPListenSocket.cpp @@ -28,8 +28,7 @@ CTCPListenSocket::~CTCPListenSocket() } void -CTCPListenSocket::bind( - const CNetworkAddress& addr) +CTCPListenSocket::bind(const CNetworkAddress& addr) { if (CNetwork::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == CNetwork::Error) { diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index 6881f7b7..8b3c5bc9 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -55,8 +55,7 @@ CTCPSocket::~CTCPSocket() } void -CTCPSocket::bind( - const CNetworkAddress& addr) +CTCPSocket::bind(const CNetworkAddress& addr) { if (CNetwork::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == CNetwork::Error) { @@ -108,8 +107,7 @@ CTCPSocket::close() } void -CTCPSocket::connect( - const CNetworkAddress& addr) +CTCPSocket::connect(const CNetworkAddress& addr) { CThread::testCancel(); if (CNetwork::connect(m_fd, addr.getAddress(), diff --git a/net/XNetwork.cpp b/net/XNetwork.cpp index 26108535..ece106bd 100644 --- a/net/XNetwork.cpp +++ b/net/XNetwork.cpp @@ -26,9 +26,7 @@ XNetworkFailed::getWhat() const throw() // XNetworkVersion // -XNetworkVersion::XNetworkVersion( - int major, - int minor) throw() : +XNetworkVersion::XNetworkVersion(int major, int minor) throw() : m_major(major), m_minor(minor) { @@ -61,7 +59,7 @@ XNetworkVersion::getWhat() const throw() // XNetworkFunctionUnavailable::XNetworkFunctionUnavailable( - const char* name) throw() + const char* name) throw() { try { m_name = name; diff --git a/net/XSocket.cpp b/net/XSocket.cpp index 73731115..945fac71 100644 --- a/net/XSocket.cpp +++ b/net/XSocket.cpp @@ -4,10 +4,8 @@ // XSocketAddress // -XSocketAddress::XSocketAddress( - Error error, - const CString& hostname, - UInt16 port) throw() : +XSocketAddress::XSocketAddress(Error error, + const CString& hostname, UInt16 port) throw() : m_error(error), m_hostname(hostname), m_port(port) @@ -55,8 +53,7 @@ XSocketErrno::XSocketErrno() : // do nothing } -XSocketErrno::XSocketErrno( - int err) : +XSocketErrno::XSocketErrno(int err) : MXErrno(err) { // do nothing diff --git a/platform/CMSWindowsClipboard.cpp b/platform/CMSWindowsClipboard.cpp index 87b2b298..8a1029cd 100644 --- a/platform/CMSWindowsClipboard.cpp +++ b/platform/CMSWindowsClipboard.cpp @@ -5,8 +5,7 @@ // CMSWindowsClipboard // -CMSWindowsClipboard::CMSWindowsClipboard( - HWND window) : +CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : m_window(window), m_time(0) { @@ -32,9 +31,7 @@ CMSWindowsClipboard::empty() } void -CMSWindowsClipboard::add( - EFormat format, - const CString& data) +CMSWindowsClipboard::add(EFormat format, const CString& data) { log((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); @@ -58,8 +55,7 @@ CMSWindowsClipboard::add( } bool -CMSWindowsClipboard::open( - Time time) const +CMSWindowsClipboard::open(Time time) const { log((CLOG_DEBUG "open clipboard")); @@ -87,16 +83,14 @@ CMSWindowsClipboard::getTime() const } bool -CMSWindowsClipboard::has( - EFormat format) const +CMSWindowsClipboard::has(EFormat format) const { const UINT win32Format = convertFormatToWin32(format); return (win32Format != 0 && IsClipboardFormatAvailable(win32Format) != 0); } CString -CMSWindowsClipboard::get( - EFormat format) const +CMSWindowsClipboard::get(EFormat format) const { // get the win32 format. return empty data if unknown format. const UINT win32Format = convertFormatToWin32(format); @@ -119,8 +113,7 @@ CMSWindowsClipboard::get( } UINT -CMSWindowsClipboard::convertFormatToWin32( - EFormat format) const +CMSWindowsClipboard::convertFormatToWin32(EFormat format) const { switch (format) { case kText: @@ -132,8 +125,7 @@ CMSWindowsClipboard::convertFormatToWin32( } HANDLE -CMSWindowsClipboard::convertTextToWin32( - const CString& data) const +CMSWindowsClipboard::convertTextToWin32(const CString& data) const { // compute size of converted text UInt32 dstSize = 1; @@ -172,8 +164,7 @@ CMSWindowsClipboard::convertTextToWin32( } CString -CMSWindowsClipboard::convertTextFromWin32( - HANDLE handle) const +CMSWindowsClipboard::convertTextFromWin32(HANDLE handle) const { // get source data and it's size const char* src = (const char*)GlobalLock(handle); diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index 693cfd84..f5309074 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -30,8 +30,7 @@ CMSWindowsScreen::~CMSWindowsScreen() } void -CMSWindowsScreen::init( - HINSTANCE instance) +CMSWindowsScreen::init(HINSTANCE instance) { s_instance = instance; } @@ -151,9 +150,7 @@ CMSWindowsScreen::updateScreenSize() } void -CMSWindowsScreen::getScreenSize( - SInt32* w, - SInt32* h) const +CMSWindowsScreen::getScreenSize(SInt32* w, SInt32* h) const { assert(m_class != 0); assert(w != NULL && h != NULL); @@ -172,8 +169,7 @@ CMSWindowsScreen::openInputDesktop() const } CString -CMSWindowsScreen::getDesktopName( - HDESK desk) const +CMSWindowsScreen::getDesktopName(HDESK desk) const { if (desk == NULL) { return CString(); @@ -190,16 +186,14 @@ CMSWindowsScreen::getDesktopName( } bool -CMSWindowsScreen::isCurrentDesktop( - HDESK desk) const +CMSWindowsScreen::isCurrentDesktop(HDESK desk) const { return CStringUtil::CaselessCmp::equal(getDesktopName(desk), getCurrentDesktopName()); } void -CMSWindowsScreen::getEvent( - MSG* msg) const +CMSWindowsScreen::getEvent(MSG* msg) const { // wait for an event in a cancellable way CThread::waitForEvent(); @@ -207,11 +201,7 @@ CMSWindowsScreen::getEvent( } LRESULT CALLBACK -CMSWindowsScreen::wndProc( - HWND hwnd, - UINT msg, - WPARAM wParam, - LPARAM lParam) +CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { assert(s_screen != NULL); return s_screen->onEvent(hwnd, msg, wParam, lParam); diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index 1ab2c63b..26110dd0 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -25,28 +25,21 @@ CUnixPlatform::~CUnixPlatform() } bool -CUnixPlatform::installDaemon( - const char*, - const char*, - const char*, - const char*) +CUnixPlatform::installDaemon(const char*, const char*, const char*, const char*) { // daemons don't require special installation return true; } CUnixPlatform::EResult -CUnixPlatform::uninstallDaemon( - const char*) +CUnixPlatform::uninstallDaemon(const char*) { // daemons don't require special installation return kSuccess; } int -CUnixPlatform::daemonize( - const char* name, - DaemonFunc func) +CUnixPlatform::daemonize(const char* name, DaemonFunc func) { // fork so shell thinks we're done and so we're not a process // group leader @@ -92,17 +85,14 @@ CUnixPlatform::daemonize( } void -CUnixPlatform::installDaemonLogger( - const char* name) +CUnixPlatform::installDaemonLogger(const char* name) { openlog(name, 0, LOG_DAEMON); CLog::setOutputter(&CUnixPlatform::deamonLogger); } int -CUnixPlatform::restart( - RestartFunc func, - int minErrorCode) +CUnixPlatform::restart(RestartFunc func, int minErrorCode) { for (;;) { switch (fork()) { @@ -153,8 +143,7 @@ CUnixPlatform::restart( } const char* -CUnixPlatform::getBasename( - const char* pathname) const +CUnixPlatform::getBasename(const char* pathname) const { if (pathname == NULL) { return NULL; @@ -189,9 +178,8 @@ CUnixPlatform::getSystemDirectory() const } CString -CUnixPlatform::addPathComponent( - const CString& prefix, - const CString& suffix) const +CUnixPlatform::addPathComponent(const CString& prefix, + const CString& suffix) const { CString path; path.reserve(prefix.size() + 1 + suffix.size()); @@ -204,9 +192,7 @@ CUnixPlatform::addPathComponent( } bool -CUnixPlatform::deamonLogger( - int priority, - const char* msg) +CUnixPlatform::deamonLogger(int priority, const char* msg) { // convert priority switch (priority) { diff --git a/platform/CWin32Platform.cpp b/platform/CWin32Platform.cpp index 58cd7cd2..440ddccc 100644 --- a/platform/CWin32Platform.cpp +++ b/platform/CWin32Platform.cpp @@ -37,19 +37,14 @@ CWin32Platform::isWindows95Family() } void -CWin32Platform::setStatus( - SERVICE_STATUS_HANDLE handle, - DWORD state) +CWin32Platform::setStatus(SERVICE_STATUS_HANDLE handle, DWORD state) { setStatus(handle, state, 0, 0); } void -CWin32Platform::setStatus( - SERVICE_STATUS_HANDLE handle, - DWORD state, - DWORD step, - DWORD waitHint) +CWin32Platform::setStatus(SERVICE_STATUS_HANDLE handle, + DWORD state, DWORD step, DWORD waitHint) { SERVICE_STATUS status; status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | @@ -66,9 +61,7 @@ CWin32Platform::setStatus( } void -CWin32Platform::setStatusError( - SERVICE_STATUS_HANDLE handle, - DWORD error) +CWin32Platform::setStatusError(SERVICE_STATUS_HANDLE handle, DWORD error) { SERVICE_STATUS status; status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | @@ -85,11 +78,8 @@ CWin32Platform::setStatusError( } bool -CWin32Platform::installDaemon( - const char* name, - const char* description, - const char* pathname, - const char* commandLine) +CWin32Platform::installDaemon(const char* name, const char* description, + const char* pathname, const char* commandLine) { // windows 95 family services if (isWindows95Family()) { @@ -181,8 +171,7 @@ CWin32Platform::installDaemon( } IPlatform::EResult -CWin32Platform::uninstallDaemon( - const char* name) +CWin32Platform::uninstallDaemon(const char* name) { // windows 95 family services if (isWindows95Family()) { @@ -255,9 +244,7 @@ CWin32Platform::uninstallDaemon( } int -CWin32Platform::daemonize( - const char* name, - DaemonFunc func) +CWin32Platform::daemonize(const char* name, DaemonFunc func) { assert(name != NULL); assert(func != NULL); @@ -319,8 +306,7 @@ CWin32Platform::daemonize( } void -CWin32Platform::installDaemonLogger( - const char* name) +CWin32Platform::installDaemonLogger(const char* name) { if (!CWin32Platform::isWindows95Family()) { // open event log and direct log messages to it @@ -334,9 +320,7 @@ CWin32Platform::installDaemonLogger( } int -CWin32Platform::restart( - RestartFunc func, - int /*minErrorCode*/) +CWin32Platform::restart(RestartFunc func, int /*minErrorCode*/) { // FIXME -- start in separate process or thread. note that this // isn't too critical as win32 doesn't force us to terminate for @@ -345,8 +329,7 @@ CWin32Platform::restart( } const char* -CWin32Platform::getBasename( - const char* pathname) const +CWin32Platform::getBasename(const char* pathname) const { if (pathname == NULL) { return NULL; @@ -429,9 +412,8 @@ CWin32Platform::getSystemDirectory() const } CString -CWin32Platform::addPathComponent( - const CString& prefix, - const CString& suffix) const +CWin32Platform::addPathComponent(const CString& prefix, + const CString& suffix) const { CString path; path.reserve(prefix.size() + 1 + suffix.size()); @@ -446,9 +428,7 @@ CWin32Platform::addPathComponent( } HKEY -CWin32Platform::openKey( - HKEY key, - const char* keyName) +CWin32Platform::openKey(HKEY key, const char* keyName) { // open next key HKEY newKey; @@ -471,9 +451,7 @@ CWin32Platform::openKey( } HKEY -CWin32Platform::openKey( - HKEY key, - const char** keyNames) +CWin32Platform::openKey(HKEY key, const char** keyNames) { for (UInt32 i = 0; key != NULL && keyNames[i] != NULL; ++i) { // open next key @@ -483,17 +461,14 @@ CWin32Platform::openKey( } void -CWin32Platform::closeKey( - HKEY key) +CWin32Platform::closeKey(HKEY key) { assert(key != NULL); RegCloseKey(key); } void -CWin32Platform::deleteKey( - HKEY key, - const char* name) +CWin32Platform::deleteKey(HKEY key, const char* name) { assert(key != NULL); assert(name != NULL); @@ -501,9 +476,7 @@ CWin32Platform::deleteKey( } void -CWin32Platform::deleteValue( - HKEY key, - const char* name) +CWin32Platform::deleteValue(HKEY key, const char* name) { assert(key != NULL); assert(name != NULL); @@ -511,10 +484,7 @@ CWin32Platform::deleteValue( } void -CWin32Platform::setValue( - HKEY key, - const char* name, - const CString& value) +CWin32Platform::setValue(HKEY key, const char* name, const CString& value) { assert(key != NULL); assert(name != NULL); @@ -524,9 +494,7 @@ CWin32Platform::setValue( } CString -CWin32Platform::readValueString( - HKEY key, - const char* name) +CWin32Platform::readValueString(HKEY key, const char* name) { // get the size of the string DWORD type; @@ -582,9 +550,7 @@ CWin32Platform::open95ServicesKey() } int -CWin32Platform::runDaemon( - RunFunc run, - StopFunc stop) +CWin32Platform::runDaemon(RunFunc run, StopFunc stop) { // should only be called from DaemonFunc assert(m_serviceMutex != NULL); @@ -664,9 +630,7 @@ CWin32Platform::runDaemon( } void -CWin32Platform::serviceMain( - DWORD argc, - LPTSTR* argvIn) +CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) { typedef std::vector ArgList; typedef std::vector Arguments; @@ -784,16 +748,13 @@ CWin32Platform::serviceMain( } void WINAPI -CWin32Platform::serviceMainEntry( - DWORD argc, - LPTSTR* argv) +CWin32Platform::serviceMainEntry(DWORD argc, LPTSTR* argv) { s_daemonPlatform->serviceMain(argc, argv); } void -CWin32Platform::serviceHandler( - DWORD ctrl) +CWin32Platform::serviceHandler(DWORD ctrl) { assert(m_serviceMutex != NULL); assert(m_serviceState != NULL); @@ -866,16 +827,13 @@ CWin32Platform::serviceHandler( } void WINAPI -CWin32Platform::serviceHandlerEntry( - DWORD ctrl) +CWin32Platform::serviceHandlerEntry(DWORD ctrl) { s_daemonPlatform->serviceHandler(ctrl); } bool -CWin32Platform::serviceLogger( - int priority, - const char* msg) +CWin32Platform::serviceLogger(int priority, const char* msg) { if (s_eventLog == NULL) { return false; diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index a025d372..4e232372 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -10,10 +10,8 @@ // CXWindowsClipboard // -CXWindowsClipboard::CXWindowsClipboard( - Display* display, - Window window, - ClipboardID id) : +CXWindowsClipboard::CXWindowsClipboard(Display* display, + Window window, ClipboardID id) : m_display(display), m_window(window), m_id(id), @@ -63,8 +61,7 @@ CXWindowsClipboard::~CXWindowsClipboard() } void -CXWindowsClipboard::lost( - Time time) +CXWindowsClipboard::lost(Time time) { log((CLOG_DEBUG "lost clipboard %d ownership at %d", m_id, time)); if (m_owner) { @@ -75,12 +72,8 @@ CXWindowsClipboard::lost( } void -CXWindowsClipboard::addRequest( - Window owner, - Window requestor, - Atom target, - ::Time time, - Atom property) +CXWindowsClipboard::addRequest(Window owner, Window requestor, + Atom target, ::Time time, Atom property) { // must be for our window and we must have owned the selection // at the given time. @@ -118,11 +111,8 @@ CXWindowsClipboard::addRequest( } bool -CXWindowsClipboard::addSimpleRequest( - Window requestor, - Atom target, - ::Time time, - Atom property) +CXWindowsClipboard::addSimpleRequest(Window requestor, + Atom target, ::Time time, Atom property) { // obsolete requestors may supply a None property. in // that case we use the target as the property to store @@ -162,10 +152,8 @@ CXWindowsClipboard::addSimpleRequest( } bool -CXWindowsClipboard::processRequest( - Window requestor, - ::Time /*time*/, - Atom property) +CXWindowsClipboard::processRequest(Window requestor, + ::Time /*time*/, Atom property) { CReplyMap::iterator index = m_replies.find(requestor); if (index == m_replies.end()) { @@ -192,8 +180,7 @@ CXWindowsClipboard::processRequest( } bool -CXWindowsClipboard::destroyRequest( - Window requestor) +CXWindowsClipboard::destroyRequest(Window requestor) { CReplyMap::iterator index = m_replies.find(requestor); if (index == m_replies.end()) { @@ -257,9 +244,7 @@ CXWindowsClipboard::empty() } void -CXWindowsClipboard::add( - EFormat format, - const CString& data) +CXWindowsClipboard::add(EFormat format, const CString& data) { assert(m_open); assert(m_owner); @@ -273,8 +258,7 @@ CXWindowsClipboard::add( } bool -CXWindowsClipboard::open( - Time time) const +CXWindowsClipboard::open(Time time) const { assert(!m_open); @@ -347,8 +331,7 @@ CXWindowsClipboard::getTime() const } bool -CXWindowsClipboard::has( - EFormat format) const +CXWindowsClipboard::has(EFormat format) const { assert(m_open); @@ -357,8 +340,7 @@ CXWindowsClipboard::has( } CString -CXWindowsClipboard::get( - EFormat format) const +CXWindowsClipboard::get(EFormat format) const { assert(m_open); @@ -367,8 +349,7 @@ CXWindowsClipboard::get( } IClipboard::EFormat -CXWindowsClipboard::getFormat( - Atom src) const +CXWindowsClipboard::getFormat(Atom src) const { // FIXME -- handle more formats (especially mime-type-like formats // and various character encodings like unicode). @@ -482,10 +463,8 @@ CXWindowsClipboard::icccmFillCache() } bool -CXWindowsClipboard::icccmGetSelection( - Atom target, - Atom* actualTarget, - CString* data) const +CXWindowsClipboard::icccmGetSelection(Atom target, + Atom* actualTarget, CString* data) const { assert(actualTarget != NULL); assert(data != NULL); @@ -725,10 +704,8 @@ CXWindowsClipboard::motifGetTime() const } bool -CXWindowsClipboard::insertMultipleReply( - Window requestor, - ::Time time, - Atom property) +CXWindowsClipboard::insertMultipleReply(Window requestor, + ::Time time, Atom property) { // get the requested targets Atom target; @@ -780,8 +757,7 @@ CXWindowsClipboard::insertMultipleReply( } void -CXWindowsClipboard::insertReply( - CReply* reply) +CXWindowsClipboard::insertReply(CReply* reply) { assert(reply != NULL); @@ -841,10 +817,8 @@ CXWindowsClipboard::pushReplies() } void -CXWindowsClipboard::pushReplies( - CReplyMap::iterator mapIndex, - CReplyList& replies, - CReplyList::iterator index) +CXWindowsClipboard::pushReplies(CReplyMap::iterator mapIndex, + CReplyList& replies, CReplyList::iterator index) { CReply* reply = *index; while (sendReply(reply)) { @@ -870,8 +844,7 @@ CXWindowsClipboard::pushReplies( } bool -CXWindowsClipboard::sendReply( - CReply* reply) +CXWindowsClipboard::sendReply(CReply* reply) { assert(reply != NULL); @@ -1005,8 +978,7 @@ CXWindowsClipboard::clearReplies() } void -CXWindowsClipboard::clearReplies( - CReplyList& replies) +CXWindowsClipboard::clearReplies(CReplyList& replies) { for (CReplyList::iterator index = replies.begin(); index != replies.end(); ++index) { @@ -1016,12 +988,8 @@ CXWindowsClipboard::clearReplies( } void -CXWindowsClipboard::sendNotify( - Window requestor, - Atom selection, - Atom target, - Atom property, - Time time) +CXWindowsClipboard::sendNotify(Window requestor, + Atom selection, Atom target, Atom property, Time time) { XEvent event; event.xselection.type = SelectionNotify; @@ -1036,8 +1004,7 @@ CXWindowsClipboard::sendNotify( } bool -CXWindowsClipboard::wasOwnedAtTime( - ::Time time) const +CXWindowsClipboard::wasOwnedAtTime(::Time time) const { // not owned if we've never owned the selection if (m_timeOwned == 0) { @@ -1070,9 +1037,7 @@ CXWindowsClipboard::wasOwnedAtTime( } Atom -CXWindowsClipboard::getTargetsData( - CString& data, - int* format) const +CXWindowsClipboard::getTargetsData(CString& data, int* format) const { assert(format != NULL); @@ -1096,9 +1061,7 @@ CXWindowsClipboard::getTargetsData( } Atom -CXWindowsClipboard::getTimestampData( - CString& data, - int* format) const +CXWindowsClipboard::getTimestampData(CString& data, int* format) const { assert(format != NULL); @@ -1109,9 +1072,7 @@ CXWindowsClipboard::getTimestampData( } Atom -CXWindowsClipboard::getStringData( - CString& data, - int* format) const +CXWindowsClipboard::getStringData(CString& data, int* format) const { assert(format != NULL); @@ -1131,9 +1092,7 @@ CXWindowsClipboard::getStringData( // CXWindowsClipboard::CICCCMGetClipboard::CICCCMGetClipboard( - Window requestor, - Time time, - Atom property) : + Window requestor, Time time, Atom property) : m_requestor(requestor), m_time(time), m_property(property), @@ -1154,12 +1113,8 @@ CXWindowsClipboard::CICCCMGetClipboard::~CICCCMGetClipboard() } bool -CXWindowsClipboard::CICCCMGetClipboard::readClipboard( - Display* display, - Atom selection, - Atom target, - Atom* actualTarget, - CString* data) +CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, + Atom selection, Atom target, Atom* actualTarget, CString* data) { assert(actualTarget != NULL); assert(data != NULL); @@ -1216,8 +1171,7 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard( bool CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate( - Display* display, - XEvent* xevent) + Display* display, XEvent* xevent) { // process event switch (xevent->type) { @@ -1354,17 +1308,14 @@ log((CLOG_INFO " INCR secondary chunk")); // FIXME Bool CXWindowsClipboard::CICCCMGetClipboard::eventPredicate( - Display* display, - XEvent* xevent, - XPointer arg) + Display* display, XEvent* xevent, XPointer arg) { CICCCMGetClipboard* self = reinterpret_cast(arg); return self->doEventPredicate(display, xevent) ? True : False; } void -CXWindowsClipboard::CICCCMGetClipboard::timeout( - void* vdisplay) +CXWindowsClipboard::CICCCMGetClipboard::timeout(void* vdisplay) { // wait CThread::sleep(0.2); // FIXME -- is this too short? @@ -1386,10 +1337,7 @@ CXWindowsClipboard::CICCCMGetClipboard::timeout( // CXWindowsClipboard::CReply // -CXWindowsClipboard::CReply::CReply( - Window requestor, - Atom target, - ::Time time) : +CXWindowsClipboard::CReply::CReply(Window requestor, Atom target, ::Time time) : m_requestor(requestor), m_target(target), m_time(time), @@ -1404,14 +1352,8 @@ CXWindowsClipboard::CReply::CReply( // do nothing } -CXWindowsClipboard::CReply::CReply( - Window requestor, - Atom target, - ::Time time, - Atom property, - const CString& data, - Atom type, - int format) : +CXWindowsClipboard::CReply::CReply(Window requestor, Atom target, ::Time time, + Atom property, const CString& data, Atom type, int format) : m_requestor(requestor), m_target(target), m_time(time), diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index 9f283283..fc8d0156 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -113,9 +113,7 @@ CXWindowsScreen::getRoot() const } void -CXWindowsScreen::getScreenSize( - SInt32* w, - SInt32* h) const +CXWindowsScreen::getScreenSize(SInt32* w, SInt32* h) const { assert(m_display != NULL); assert(w != NULL && h != NULL); @@ -161,8 +159,7 @@ CXWindowsScreen::createBlankCursor() const } bool -CXWindowsScreen::getEvent( - XEvent* xevent) const +CXWindowsScreen::getEvent(XEvent* xevent) const { // wait for an event in a cancellable way and don't lock the // display while we're waiting. @@ -217,8 +214,7 @@ CXWindowsScreen::onUnexpectedClose() } bool -CXWindowsScreen::processEvent( - XEvent* xevent) +CXWindowsScreen::processEvent(XEvent* xevent) { switch (xevent->type) { case SelectionClear: @@ -288,9 +284,8 @@ CXWindowsScreen::processEvent( } bool -CXWindowsScreen::setDisplayClipboard( - ClipboardID id, - const IClipboard* clipboard) +CXWindowsScreen::setDisplayClipboard(ClipboardID id, + const IClipboard* clipboard) { CLock lock(&m_mutex); @@ -319,9 +314,8 @@ CXWindowsScreen::setDisplayClipboard( } bool -CXWindowsScreen::getDisplayClipboard( - ClipboardID id, - IClipboard* clipboard) const +CXWindowsScreen::getDisplayClipboard(ClipboardID id, + IClipboard* clipboard) const { assert(clipboard != NULL); @@ -342,10 +336,8 @@ CXWindowsScreen::getDisplayClipboard( } void -CXWindowsScreen::processClipboardRequest( - Window requestor, - Time time, - Atom property) +CXWindowsScreen::processClipboardRequest(Window requestor, + Time time, Atom property) { CLock lock(&m_mutex); @@ -359,8 +351,7 @@ CXWindowsScreen::processClipboardRequest( } void -CXWindowsScreen::destroyClipboardRequest( - Window requestor) +CXWindowsScreen::destroyClipboardRequest(Window requestor) { CLock lock(&m_mutex); @@ -374,8 +365,7 @@ CXWindowsScreen::destroyClipboardRequest( } int -CXWindowsScreen::ioErrorHandler( - Display*) +CXWindowsScreen::ioErrorHandler(Display*) { // the display has disconnected, probably because X is shutting // down. X forces us to exit at this point. that's arguably @@ -395,8 +385,7 @@ CXWindowsScreen::ioErrorHandler( // CXWindowsScreen::CDisplayLock // -CXWindowsScreen::CDisplayLock::CDisplayLock( - const CXWindowsScreen* screen) : +CXWindowsScreen::CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : m_mutex(&screen->m_mutex), m_display(screen->m_display) { diff --git a/platform/CXWindowsUtil.cpp b/platform/CXWindowsUtil.cpp index 6dbb5f7e..d7b9ba4c 100644 --- a/platform/CXWindowsUtil.cpp +++ b/platform/CXWindowsUtil.cpp @@ -8,14 +8,9 @@ // bool -CXWindowsUtil::getWindowProperty( - Display* display, - Window window, - Atom property, - CString* data, - Atom* type, - int* format, - bool deleteProperty) +CXWindowsUtil::getWindowProperty(Display* display, Window window, + Atom property, CString* data, Atom* type, + int* format, bool deleteProperty) { assert(display != NULL); assert(data != NULL); @@ -88,14 +83,9 @@ CXWindowsUtil::getWindowProperty( } bool -CXWindowsUtil::setWindowProperty( - Display* display, - Window window, - Atom property, - const void* vdata, - UInt32 size, - Atom type, - SInt32 format) +CXWindowsUtil::setWindowProperty(Display* display, Window window, + Atom property, const void* vdata, UInt32 size, + Atom type, SInt32 format) { const UInt32 length = 4 * XMaxRequestSize(display); const unsigned char* data = reinterpret_cast(vdata); @@ -135,9 +125,7 @@ CXWindowsUtil::setWindowProperty( } Time -CXWindowsUtil::getCurrentTime( - Display* display, - Window window) +CXWindowsUtil::getCurrentTime(Display* display, Window window) { // select property events on window XWindowAttributes attr; @@ -174,10 +162,7 @@ CXWindowsUtil::getCurrentTime( } Bool -CXWindowsUtil::propertyNotifyPredicate( - Display*, - XEvent* xevent, - XPointer arg) +CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) { CPropertyNotifyPredicateInfo* filter = reinterpret_cast(arg); @@ -199,15 +184,12 @@ CXWindowsUtil::CErrorLock::CErrorLock() install(&CXWindowsUtil::CErrorLock::ignoreHandler, NULL); } -CXWindowsUtil::CErrorLock::CErrorLock( - bool* flag) +CXWindowsUtil::CErrorLock::CErrorLock(bool* flag) { install(&CXWindowsUtil::CErrorLock::saveHandler, flag); } -CXWindowsUtil::CErrorLock::CErrorLock( - ErrorHandler handler, - void* data) +CXWindowsUtil::CErrorLock::CErrorLock(ErrorHandler handler, void* data) { install(handler, data); } @@ -219,9 +201,7 @@ CXWindowsUtil::CErrorLock::~CErrorLock() } void -CXWindowsUtil::CErrorLock::install( - ErrorHandler handler, - void* data) +CXWindowsUtil::CErrorLock::install(ErrorHandler handler, void* data) { m_handler = handler; m_userData = data; @@ -232,9 +212,7 @@ CXWindowsUtil::CErrorLock::install( } int -CXWindowsUtil::CErrorLock::internalHandler( - Display* display, - XErrorEvent* event) +CXWindowsUtil::CErrorLock::internalHandler(Display* display, XErrorEvent* event) { if (s_top != NULL && s_top->m_handler != NULL) { s_top->m_handler(display, event, s_top->m_userData); @@ -243,19 +221,13 @@ CXWindowsUtil::CErrorLock::internalHandler( } void -CXWindowsUtil::CErrorLock::ignoreHandler( - Display*, - XErrorEvent*, - void*) +CXWindowsUtil::CErrorLock::ignoreHandler(Display*, XErrorEvent*, void*) { // do nothing } void -CXWindowsUtil::CErrorLock::saveHandler( - Display*, - XErrorEvent*, - void* flag) +CXWindowsUtil::CErrorLock::saveHandler(Display*, XErrorEvent*, void* flag) { *reinterpret_cast(flag) = true; } diff --git a/server/CConfig.cpp b/server/CConfig.cpp index cc3cfe46..19d368a7 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -19,8 +19,7 @@ CConfig::~CConfig() } bool -CConfig::addScreen( - const CString& name) +CConfig::addScreen(const CString& name) { // alias name must not exist if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) { @@ -37,8 +36,7 @@ CConfig::addScreen( } void -CConfig::removeScreen( - const CString& name) +CConfig::removeScreen(const CString& name) { // get canonical name and find cell CString canonical = getCanonicalName(name); @@ -79,9 +77,7 @@ CConfig::removeAllScreens() } bool -CConfig::addAlias( - const CString& canonical, - const CString& alias) +CConfig::addAlias(const CString& canonical, const CString& alias) { // alias name must not exist if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) { @@ -100,8 +96,7 @@ CConfig::addAlias( } bool -CConfig::removeAlias( - const CString& alias) +CConfig::removeAlias(const CString& alias) { // must not be a canonical name if (m_map.find(alias) != m_map.end()) { @@ -135,10 +130,8 @@ CConfig::removeAllAliases() } bool -CConfig::connect( - const CString& srcName, - EDirection srcSide, - const CString& dstName) +CConfig::connect(const CString& srcName, + EDirection srcSide, const CString& dstName) { // find source cell CCellMap::iterator index = m_map.find(getCanonicalName(srcName)); @@ -155,9 +148,7 @@ CConfig::connect( } bool -CConfig::disconnect( - const CString& srcName, - EDirection srcSide) +CConfig::disconnect(const CString& srcName, EDirection srcSide) { // find source cell CCellMap::iterator index = m_map.find(srcName); @@ -172,22 +163,19 @@ CConfig::disconnect( } void -CConfig::setSynergyAddress( - const CNetworkAddress& addr) +CConfig::setSynergyAddress(const CNetworkAddress& addr) { m_synergyAddress = addr; } void -CConfig::setHTTPAddress( - const CNetworkAddress& addr) +CConfig::setHTTPAddress(const CNetworkAddress& addr) { m_httpAddress = addr; } bool -CConfig::isValidScreenName( - const CString& name) const +CConfig::isValidScreenName(const CString& name) const { // name is valid if matches validname // name ::= [A-Za-z0-9] | [A-Za-z0-9][-A-Za-z0-9]*[A-Za-z0-9] @@ -244,22 +232,19 @@ CConfig::end() const } bool -CConfig::isScreen( - const CString& name) const +CConfig::isScreen(const CString& name) const { return (m_nameToCanonicalName.count(name) > 0); } bool -CConfig::isCanonicalName( - const CString& name) const +CConfig::isCanonicalName(const CString& name) const { return CStringUtil::CaselessCmp::equal(getCanonicalName(name), name); } CString -CConfig::getCanonicalName( - const CString& name) const +CConfig::getCanonicalName(const CString& name) const { CNameMap::const_iterator index = m_nameToCanonicalName.find(name); if (index == m_nameToCanonicalName.end()) { @@ -271,9 +256,7 @@ CConfig::getCanonicalName( } CString -CConfig::getNeighbor( - const CString& srcName, - EDirection srcSide) const +CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const { // find source cell CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); @@ -299,17 +282,14 @@ CConfig::getHTTPAddress() const } const char* -CConfig::dirName( - EDirection dir) +CConfig::dirName(EDirection dir) { static const char* s_name[] = { "left", "right", "top", "bottom" }; return s_name[dir - kFirstDirection]; } bool -CConfig::readLine( - std::istream& s, - CString& line) +CConfig::readLine(std::istream& s, CString& line) { s >> std::ws; while (std::getline(s, line)) { @@ -333,8 +313,7 @@ CConfig::readLine( } void -CConfig::readSection( - std::istream& s) +CConfig::readSection(std::istream& s) { static const char s_section[] = "section:"; static const char s_network[] = "network"; @@ -383,8 +362,7 @@ CConfig::readSection( } void -CConfig::readSectionNetwork( - std::istream& s) +CConfig::readSectionNetwork(std::istream& s) { CString line; CString name; @@ -440,8 +418,7 @@ CConfig::readSectionNetwork( } void -CConfig::readSectionScreens( - std::istream& s) +CConfig::readSectionScreens(std::istream& s) { CString line; CString name; @@ -477,8 +454,7 @@ CConfig::readSectionScreens( } void -CConfig::readSectionLinks( - std::istream& s) +CConfig::readSectionLinks(std::istream& s) { CString line; CString screen; @@ -559,8 +535,7 @@ CConfig::readSectionLinks( } void -CConfig::readSectionAliases( - std::istream& s) +CConfig::readSectionAliases(std::istream& s) { CString line; CString screen; @@ -607,9 +582,7 @@ CConfig::readSectionAliases( // std::istream& -operator>>( - std::istream& s, - CConfig& config) +operator>>(std::istream& s, CConfig& config) { // FIXME -- should track line and column to improve error reporting @@ -622,9 +595,7 @@ operator>>( } std::ostream& -operator<<( - std::ostream& s, - const CConfig& config) +operator<<(std::ostream& s, const CConfig& config) { // network section s << "section: network" << std::endl; @@ -710,8 +681,7 @@ operator<<( // CConfig I/O exceptions // -XConfigRead::XConfigRead( - const CString& error) : +XConfigRead::XConfigRead(const CString& error) : m_error(error) { // do nothing diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index 892cee3e..c626462d 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -31,8 +31,7 @@ CHTTPServer::~CHTTPServer() } void -CHTTPServer::processRequest( - IDataSocket* socket) +CHTTPServer::processRequest(IDataSocket* socket) { assert(socket != NULL); @@ -92,9 +91,7 @@ CHTTPServer::processRequest( } void -CHTTPServer::doProcessRequest( - CHTTPRequest& request, - CHTTPReply& reply) +CHTTPServer::doProcessRequest(CHTTPRequest& request, CHTTPReply& reply) { reply.m_majorVersion = request.m_majorVersion; reply.m_minorVersion = request.m_minorVersion; @@ -123,9 +120,7 @@ CHTTPServer::doProcessRequest( } void -CHTTPServer::doProcessGetEditMap( - CHTTPRequest& /*request*/, - CHTTPReply& reply) +CHTTPServer::doProcessGetEditMap(CHTTPRequest& /*request*/, CHTTPReply& reply) { static const char* s_editMapProlog1 = "\r\n" @@ -234,9 +229,7 @@ CHTTPServer::doProcessGetEditMap( } void -CHTTPServer::doProcessPostEditMap( - CHTTPRequest& request, - CHTTPReply& reply) +CHTTPServer::doProcessPostEditMap(CHTTPRequest& request, CHTTPReply& reply) { typedef std::vector ScreenArray; typedef std::set ScreenSet; @@ -330,10 +323,7 @@ CHTTPServer::doProcessPostEditMap( } bool -CHTTPServer::parseXY( - const CString& xy, - SInt32& x, - SInt32& y) +CHTTPServer::parseXY(const CString& xy, SInt32& x, SInt32& y) { std::istringstream s(xy); char delimiter; @@ -361,9 +351,7 @@ CHTTPServer::CScreenArray::~CScreenArray() } void -CHTTPServer::CScreenArray::resize( - SInt32 w, - SInt32 h) +CHTTPServer::CScreenArray::resize(SInt32 w, SInt32 h) { m_screens.clear(); m_screens.resize(w * h); @@ -372,8 +360,7 @@ CHTTPServer::CScreenArray::resize( } void -CHTTPServer::CScreenArray::insertRow( - SInt32 i) +CHTTPServer::CScreenArray::insertRow(SInt32 i) { assert(i >= 0 && i <= m_h); @@ -396,8 +383,7 @@ CHTTPServer::CScreenArray::insertRow( } void -CHTTPServer::CScreenArray::insertColumn( - SInt32 i) +CHTTPServer::CScreenArray::insertColumn(SInt32 i) { assert(i >= 0 && i <= m_w); @@ -418,8 +404,7 @@ CHTTPServer::CScreenArray::insertColumn( } void -CHTTPServer::CScreenArray::eraseRow( - SInt32 i) +CHTTPServer::CScreenArray::eraseRow(SInt32 i) { assert(i >= 0 && i < m_h); @@ -442,8 +427,7 @@ CHTTPServer::CScreenArray::eraseRow( } void -CHTTPServer::CScreenArray::eraseColumn( - SInt32 i) +CHTTPServer::CScreenArray::eraseColumn(SInt32 i) { assert(i >= 0 && i < m_w); @@ -464,8 +448,7 @@ CHTTPServer::CScreenArray::eraseColumn( } void -CHTTPServer::CScreenArray::rotateRows( - SInt32 i) +CHTTPServer::CScreenArray::rotateRows(SInt32 i) { // nothing to do if no rows if (m_h == 0) { @@ -496,8 +479,7 @@ CHTTPServer::CScreenArray::rotateRows( } void -CHTTPServer::CScreenArray::rotateColumns( - SInt32 i) +CHTTPServer::CScreenArray::rotateColumns(SInt32 i) { // nothing to do if no columns if (m_h == 0) { @@ -528,18 +510,13 @@ CHTTPServer::CScreenArray::rotateColumns( } void -CHTTPServer::CScreenArray::remove( - SInt32 x, - SInt32 y) +CHTTPServer::CScreenArray::remove(SInt32 x, SInt32 y) { set(x, y, CString()); } void -CHTTPServer::CScreenArray::set( - SInt32 x, - SInt32 y, - const CString& name) +CHTTPServer::CScreenArray::set(SInt32 x, SInt32 y, const CString& name) { assert(x >= 0 && x < m_w); assert(y >= 0 && y < m_h); @@ -548,9 +525,7 @@ CHTTPServer::CScreenArray::set( } bool -CHTTPServer::CScreenArray::isAllowed( - SInt32 x, - SInt32 y) const +CHTTPServer::CScreenArray::isAllowed(SInt32 x, SInt32 y) const { assert(x >= 0 && x < m_w); assert(y >= 0 && y < m_h); @@ -571,9 +546,7 @@ CHTTPServer::CScreenArray::isAllowed( } bool -CHTTPServer::CScreenArray::isSet( - SInt32 x, - SInt32 y) const +CHTTPServer::CScreenArray::isSet(SInt32 x, SInt32 y) const { assert(x >= 0 && x < m_w); assert(y >= 0 && y < m_h); @@ -582,9 +555,7 @@ CHTTPServer::CScreenArray::isSet( } CString -CHTTPServer::CScreenArray::get( - SInt32 x, - SInt32 y) const +CHTTPServer::CScreenArray::get(SInt32 x, SInt32 y) const { assert(x >= 0 && x < m_w); assert(y >= 0 && y < m_h); @@ -593,10 +564,8 @@ CHTTPServer::CScreenArray::get( } bool -CHTTPServer::CScreenArray::find( - const CString& name, - SInt32& xOut, - SInt32& yOut) const +CHTTPServer::CScreenArray::find(const CString& name, + SInt32& xOut, SInt32& yOut) const { for (SInt32 y = 0; y < m_h; ++y) { for (SInt32 x = 0; x < m_w; ++x) { @@ -628,8 +597,7 @@ CHTTPServer::CScreenArray::isValid() const } bool -CHTTPServer::CScreenArray::convertFrom( - const CConfig& config) +CHTTPServer::CScreenArray::convertFrom(const CConfig& config) { typedef std::set ScreenSet; @@ -760,8 +728,7 @@ CHTTPServer::CScreenArray::convertFrom( } void -CHTTPServer::CScreenArray::convertTo( - CConfig& config) const +CHTTPServer::CScreenArray::convertTo(CConfig& config) const { config.removeAllScreens(); diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 0f81690b..394c1a42 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -94,8 +94,7 @@ CMSWindowsPrimaryScreen::stop() } void -CMSWindowsPrimaryScreen::open( - CServer* server) +CMSWindowsPrimaryScreen::open(CServer* server) { assert(m_server == NULL); assert(server != NULL); @@ -142,9 +141,7 @@ CMSWindowsPrimaryScreen::close() } void -CMSWindowsPrimaryScreen::enter( - SInt32 x, - SInt32 y) +CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) { log((CLOG_INFO "entering primary at %d,%d", x, y)); assert(m_active == true); @@ -239,18 +236,15 @@ CMSWindowsPrimaryScreen::onConfigure() } void -CMSWindowsPrimaryScreen::warpCursor( - SInt32 x, - SInt32 y) +CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { // set the cursor position without generating an event SetCursorPos(x, y); } void -CMSWindowsPrimaryScreen::setClipboard( - ClipboardID /*id*/, - const IClipboard* src) +CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/, + const IClipboard* src) { assert(m_window != NULL); @@ -259,8 +253,7 @@ CMSWindowsPrimaryScreen::setClipboard( } void -CMSWindowsPrimaryScreen::grabClipboard( - ClipboardID /*id*/) +CMSWindowsPrimaryScreen::grabClipboard(ClipboardID /*id*/) { assert(m_window != NULL); @@ -271,9 +264,7 @@ CMSWindowsPrimaryScreen::grabClipboard( } void -CMSWindowsPrimaryScreen::getSize( - SInt32* width, - SInt32* height) const +CMSWindowsPrimaryScreen::getSize(SInt32* width, SInt32* height) const { getScreenSize(width, height); } @@ -285,9 +276,8 @@ CMSWindowsPrimaryScreen::getJumpZoneSize() const } void -CMSWindowsPrimaryScreen::getClipboard( - ClipboardID /*id*/, - IClipboard* dst) const +CMSWindowsPrimaryScreen::getClipboard(ClipboardID /*id*/, + IClipboard* dst) const { assert(m_window != NULL); @@ -373,8 +363,7 @@ CMSWindowsPrimaryScreen::onCloseDisplay() } bool -CMSWindowsPrimaryScreen::onPreTranslate( - MSG* msg) +CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) { // handle event switch (msg->message) { @@ -504,11 +493,8 @@ CMSWindowsPrimaryScreen::onPreTranslate( } LRESULT -CMSWindowsPrimaryScreen::onEvent( - HWND hwnd, - UINT msg, - WPARAM wParam, - LPARAM lParam) +CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_QUERYENDSESSION: @@ -750,8 +736,7 @@ CMSWindowsPrimaryScreen::closeDesktop() } bool -CMSWindowsPrimaryScreen::switchDesktop( - HDESK desk) +CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) { // did we own the clipboard? bool ownClipboard = (m_clipboardOwner == m_window); @@ -1300,8 +1285,7 @@ CMSWindowsPrimaryScreen::mapKey( } ButtonID -CMSWindowsPrimaryScreen::mapButton( - WPARAM button) const +CMSWindowsPrimaryScreen::mapButton(WPARAM button) const { switch (button) { case WM_LBUTTONDOWN: @@ -1350,9 +1334,7 @@ CMSWindowsPrimaryScreen::updateKeys() } void -CMSWindowsPrimaryScreen::updateKey( - UINT vkCode, - bool press) +CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press) { if (press) { switch (vkCode) { diff --git a/server/CServer.cpp b/server/CServer.cpp index 79e59f55..d4de4240 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -36,8 +36,7 @@ const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; -CServer::CServer( - const CString& serverName) : +CServer::CServer(const CString& serverName) : m_name(serverName), m_cleanupSize(&m_mutex, 0), m_primary(NULL), @@ -160,8 +159,7 @@ CServer::shutdown() } bool -CServer::setConfig( - const CConfig& config) +CServer::setConfig(const CConfig& config) { typedef std::vector CThreads; CThreads threads; @@ -229,8 +227,7 @@ CServer::getPrimaryScreenName() const } void -CServer::getConfig( - CConfig* config) const +CServer::getConfig(CConfig* config) const { assert(config != NULL); @@ -263,12 +260,7 @@ CServer::getActivePrimarySides() const } void -CServer::setInfo( - SInt32 w, - SInt32 h, - SInt32 zoneSize, - SInt32 x, - SInt32 y) +CServer::setInfo(SInt32 w, SInt32 h, SInt32 zoneSize, SInt32 x, SInt32 y) { CLock lock(&m_mutex); assert(m_primaryInfo != NULL); @@ -276,26 +268,16 @@ CServer::setInfo( } void -CServer::setInfo( - const CString& client, - SInt32 w, - SInt32 h, - SInt32 zoneSize, - SInt32 x, - SInt32 y) +CServer::setInfo(const CString& client, + SInt32 w, SInt32 h, SInt32 zoneSize, SInt32 x, SInt32 y) { CLock lock(&m_mutex); setInfoNoLock(client, w, h, zoneSize, x, y); } void -CServer::setInfoNoLock( - const CString& screen, - SInt32 w, - SInt32 h, - SInt32 zoneSize, - SInt32 x, - SInt32 y) +CServer::setInfoNoLock(const CString& screen, + SInt32 w, SInt32 h, SInt32 zoneSize, SInt32 x, SInt32 y) { assert(!screen.empty()); assert(w > 0); @@ -340,8 +322,7 @@ CServer::setInfoNoLock( } void -CServer::grabClipboard( - ClipboardID id) +CServer::grabClipboard(ClipboardID id) { CLock lock(&m_mutex); assert(m_primaryInfo != NULL); @@ -349,20 +330,15 @@ CServer::grabClipboard( } void -CServer::grabClipboard( - ClipboardID id, - UInt32 seqNum, - const CString& client) +CServer::grabClipboard(ClipboardID id, UInt32 seqNum, const CString& client) { CLock lock(&m_mutex); grabClipboardNoLock(id, seqNum, client); } void -CServer::grabClipboardNoLock( - ClipboardID id, - UInt32 seqNum, - const CString& screen) +CServer::grabClipboardNoLock(ClipboardID id, + UInt32 seqNum, const CString& screen) { // note -- must be locked on entry CClipboardInfo& clipboard = m_clipboards[id]; @@ -421,10 +397,7 @@ CServer::grabClipboardNoLock( } void -CServer::setClipboard( - ClipboardID id, - UInt32 seqNum, - const CString& data) +CServer::setClipboard(ClipboardID id, UInt32 seqNum, const CString& data) { CLock lock(&m_mutex); CClipboardInfo& clipboard = m_clipboards[id]; @@ -455,18 +428,13 @@ CServer::setClipboard( } bool -CServer::onCommandKey( - KeyID /*id*/, - KeyModifierMask /*mask*/, - bool /*down*/) +CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) { return false; } void -CServer::onKeyDown( - KeyID id, - KeyModifierMask mask) +CServer::onKeyDown(KeyID id, KeyModifierMask mask) { log((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x", id, mask)); CLock lock(&m_mutex); @@ -484,9 +452,7 @@ CServer::onKeyDown( } void -CServer::onKeyUp( - KeyID id, - KeyModifierMask mask) +CServer::onKeyUp(KeyID id, KeyModifierMask mask) { log((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x", id, mask)); CLock lock(&m_mutex); @@ -504,10 +470,7 @@ CServer::onKeyUp( } void -CServer::onKeyRepeat( - KeyID id, - KeyModifierMask mask, - SInt32 count) +CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) { log((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); CLock lock(&m_mutex); @@ -526,8 +489,7 @@ CServer::onKeyRepeat( } void -CServer::onMouseDown( - ButtonID id) +CServer::onMouseDown(ButtonID id) { log((CLOG_DEBUG1 "onMouseDown id=%d", id)); CLock lock(&m_mutex); @@ -540,8 +502,7 @@ CServer::onMouseDown( } void -CServer::onMouseUp( - ButtonID id) +CServer::onMouseUp(ButtonID id) { log((CLOG_DEBUG1 "onMouseUp id=%d", id)); CLock lock(&m_mutex); @@ -554,9 +515,7 @@ CServer::onMouseUp( } bool -CServer::onMouseMovePrimary( - SInt32 x, - SInt32 y) +CServer::onMouseMovePrimary(SInt32 x, SInt32 y) { log((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); CLock lock(&m_mutex); @@ -564,9 +523,7 @@ CServer::onMouseMovePrimary( } bool -CServer::onMouseMovePrimaryNoLock( - SInt32 x, - SInt32 y) +CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) { // mouse move on primary (server's) screen assert(m_active != NULL); @@ -621,9 +578,7 @@ CServer::onMouseMovePrimaryNoLock( } void -CServer::onMouseMoveSecondary( - SInt32 dx, - SInt32 dy) +CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) { log((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); CLock lock(&m_mutex); @@ -631,9 +586,7 @@ CServer::onMouseMoveSecondary( } void -CServer::onMouseMoveSecondaryNoLock( - SInt32 dx, - SInt32 dy) +CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) { // mouse move on secondary (client's) screen assert(m_active != NULL); @@ -736,8 +689,7 @@ CServer::onMouseMoveSecondaryNoLock( } void -CServer::onMouseWheel( - SInt32 delta) +CServer::onMouseWheel(SInt32 delta) { log((CLOG_DEBUG1 "onMouseWheel %+d", delta)); CLock lock(&m_mutex); @@ -774,10 +726,7 @@ CServer::isLockedToScreenNoLock() const } void -CServer::switchScreen( - CScreenInfo* dst, - SInt32 x, - SInt32 y) +CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y) { assert(dst != NULL); assert(x >= 0 && y >= 0 && x < dst->m_width && y < dst->m_height); @@ -845,9 +794,7 @@ CServer::switchScreen( } CServer::CScreenInfo* -CServer::getNeighbor( - CScreenInfo* src, - CConfig::EDirection dir) const +CServer::getNeighbor(CScreenInfo* src, CConfig::EDirection dir) const { assert(src != NULL); @@ -879,11 +826,8 @@ CServer::getNeighbor( } CServer::CScreenInfo* -CServer::getNeighbor( - CScreenInfo* src, - CConfig::EDirection srcSide, - SInt32& x, - SInt32& y) const +CServer::getNeighbor(CScreenInfo* src, + CConfig::EDirection srcSide, SInt32& x, SInt32& y) const { assert(src != NULL); @@ -1000,12 +944,8 @@ CServer::getNeighbor( } void -CServer::mapPosition( - CScreenInfo* src, - CConfig::EDirection srcSide, - CScreenInfo* dst, - SInt32& x, - SInt32& y) const +CServer::mapPosition(CScreenInfo* src, CConfig::EDirection srcSide, + CScreenInfo* dst, SInt32& x, SInt32& y) const { assert(src != NULL); assert(dst != NULL); @@ -1104,8 +1044,7 @@ CServer::acceptClients(void*) } void -CServer::handshakeClient( - void* vsocket) +CServer::handshakeClient(void* vsocket) { log((CLOG_DEBUG1 "negotiating with new client")); @@ -1306,8 +1245,7 @@ CServer::acceptHTTPClients(void*) } void -CServer::processHTTPRequest( - void* vsocket) +CServer::processHTTPRequest(void* vsocket) { // add this thread to the list of threads to cancel. remove from // list in d'tor. @@ -1345,8 +1283,7 @@ CServer::processHTTPRequest( } void -CServer::clearGotClipboard( - ClipboardID id) +CServer::clearGotClipboard(ClipboardID id) { for (CScreenList::const_iterator index = m_screens.begin(); index != m_screens.end(); ++index) { @@ -1355,8 +1292,7 @@ CServer::clearGotClipboard( } void -CServer::sendClipboard( - ClipboardID id) +CServer::sendClipboard(ClipboardID id) { // do nothing if clipboard was already sent if (!m_active->m_gotClipboard[id]) { @@ -1378,8 +1314,7 @@ CServer::sendClipboard( } void -CServer::updatePrimaryClipboard( - ClipboardID id) +CServer::updatePrimaryClipboard(ClipboardID id) { CClipboardInfo& clipboard = m_clipboards[id]; @@ -1497,8 +1432,7 @@ CServer::closePrimaryScreen() } void -CServer::addCleanupThread( - const CThread& thread) +CServer::addCleanupThread(const CThread& thread) { CLock lock(&m_mutex); m_cleanupList.insert(m_cleanupList.begin(), new CThread(thread)); @@ -1506,8 +1440,7 @@ CServer::addCleanupThread( } void -CServer::removeCleanupThread( - const CThread& thread) +CServer::removeCleanupThread(const CThread& thread) { CLock lock(&m_mutex); for (CThreadList::iterator index = m_cleanupList.begin(); @@ -1526,8 +1459,7 @@ CServer::removeCleanupThread( } void -CServer::cleanupThreads( - double timeout) +CServer::cleanupThreads(double timeout) { log((CLOG_DEBUG1 "cleaning up threads")); @@ -1567,9 +1499,7 @@ CServer::cleanupThreads( } CServer::CScreenInfo* -CServer::addConnection( - const CString& name, - IServerProtocol* protocol) +CServer::addConnection(const CString& name, IServerProtocol* protocol) { log((CLOG_DEBUG "adding connection \"%s\"", name.c_str())); @@ -1594,8 +1524,7 @@ CServer::addConnection( } void -CServer::removeConnection( - const CString& name) +CServer::removeConnection(const CString& name) { log((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); CLock lock(&m_mutex); @@ -1630,8 +1559,7 @@ CServer::removeConnection( // CServer::CCleanupNote // -CServer::CCleanupNote::CCleanupNote( - CServer* server) : +CServer::CCleanupNote::CCleanupNote(CServer* server) : m_server(server) { assert(m_server != NULL); @@ -1648,10 +1576,8 @@ CServer::CCleanupNote::~CCleanupNote() // CServer::CConnectionNote // -CServer::CConnectionNote::CConnectionNote( - CServer* server, - const CString& name, - IServerProtocol* protocol) : +CServer::CConnectionNote::CConnectionNote(CServer* server, + const CString& name, IServerProtocol* protocol) : m_server(server), m_name(name) { @@ -1669,9 +1595,8 @@ CServer::CConnectionNote::~CConnectionNote() // CServer::CScreenInfo // -CServer::CScreenInfo::CScreenInfo( - const CString& name, - IServerProtocol* protocol) : +CServer::CScreenInfo::CScreenInfo(const CString& name, + IServerProtocol* protocol) : m_thread(CThread::getCurrentThread()), m_name(name), m_protocol(protocol), diff --git a/server/CServerProtocol.cpp b/server/CServerProtocol.cpp index dd65ba1c..112db9d7 100644 --- a/server/CServerProtocol.cpp +++ b/server/CServerProtocol.cpp @@ -9,11 +9,8 @@ // CServerProtocol // -CServerProtocol::CServerProtocol( - CServer* server, - const CString& client, - IInputStream* input, - IOutputStream* output) : +CServerProtocol::CServerProtocol(CServer* server, const CString& client, + IInputStream* input, IOutputStream* output) : m_server(server), m_client(client), m_input(input), @@ -54,13 +51,9 @@ CServerProtocol::getOutputStream() const } IServerProtocol* -CServerProtocol::create( - SInt32 major, - SInt32 minor, - CServer* server, - const CString& client, - IInputStream* input, - IOutputStream* output) +CServerProtocol::create(SInt32 major, SInt32 minor, + CServer* server, const CString& client, + IInputStream* input, IOutputStream* output) { // disallow invalid version numbers if (major < 0 || minor < 0) { diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 4de3347f..bfd5fef9 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -14,11 +14,8 @@ // CServerProtocol1_0 // -CServerProtocol1_0::CServerProtocol1_0( - CServer* server, - const CString& client, - IInputStream* input, - IOutputStream* output) : +CServerProtocol1_0::CServerProtocol1_0(CServer* server, const CString& client, + IInputStream* input, IOutputStream* output) : CServerProtocol(server, client, input, output) { // do nothing @@ -106,11 +103,8 @@ CServerProtocol1_0::sendClose() } void -CServerProtocol1_0::sendEnter( - SInt32 xAbs, - SInt32 yAbs, - UInt32 seqNum, - KeyModifierMask mask) +CServerProtocol1_0::sendEnter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask) { log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getClient().c_str(), xAbs, yAbs, seqNum, mask)); CProtocolUtil::writef(getOutputStream(), kMsgCEnter, @@ -125,25 +119,21 @@ CServerProtocol1_0::sendLeave() } void -CServerProtocol1_0::sendClipboard( - ClipboardID id, - const CString& data) +CServerProtocol1_0::sendClipboard(ClipboardID id, const CString& data) { log((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getClient().c_str(), data.size())); CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); } void -CServerProtocol1_0::sendGrabClipboard( - ClipboardID id) +CServerProtocol1_0::sendGrabClipboard(ClipboardID id) { log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getClient().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); } void -CServerProtocol1_0::sendScreenSaver( - bool on) +CServerProtocol1_0::sendScreenSaver(bool on) { log((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getClient().c_str(), on ? 1 : 0)); CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); @@ -157,61 +147,49 @@ CServerProtocol1_0::sendInfoAcknowledgment() } void -CServerProtocol1_0::sendKeyDown( - KeyID key, - KeyModifierMask mask) +CServerProtocol1_0::sendKeyDown(KeyID key, KeyModifierMask mask) { log((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); } void -CServerProtocol1_0::sendKeyRepeat( - KeyID key, - KeyModifierMask mask, - SInt32 count) +CServerProtocol1_0::sendKeyRepeat(KeyID key, KeyModifierMask mask, SInt32 count) { log((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getClient().c_str(), key, mask, count)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); } void -CServerProtocol1_0::sendKeyUp( - KeyID key, - KeyModifierMask mask) +CServerProtocol1_0::sendKeyUp(KeyID key, KeyModifierMask mask) { log((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); } void -CServerProtocol1_0::sendMouseDown( - ButtonID button) +CServerProtocol1_0::sendMouseDown(ButtonID button) { log((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getClient().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); } void -CServerProtocol1_0::sendMouseUp( - ButtonID button) +CServerProtocol1_0::sendMouseUp(ButtonID button) { log((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getClient().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); } void -CServerProtocol1_0::sendMouseMove( - SInt32 xAbs, - SInt32 yAbs) +CServerProtocol1_0::sendMouseMove(SInt32 xAbs, SInt32 yAbs) { log((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getClient().c_str(), xAbs, yAbs)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); } void -CServerProtocol1_0::sendMouseWheel( - SInt32 delta) +CServerProtocol1_0::sendMouseWheel(SInt32 delta) { log((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 281ab443..f834f413 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -89,10 +89,7 @@ restoreCursor() static LRESULT CALLBACK -keyboardHook( - int code, - WPARAM wParam, - LPARAM lParam) +keyboardHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { if (g_relay) { @@ -121,10 +118,7 @@ keyboardHook( static LRESULT CALLBACK -mouseHook( - int code, - WPARAM wParam, - LPARAM lParam) +mouseHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { if (g_relay) { @@ -228,10 +222,7 @@ mouseHook( static LRESULT CALLBACK -cbtHook( - int code, - WPARAM wParam, - LPARAM lParam) +cbtHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { if (g_relay) { @@ -244,10 +235,7 @@ cbtHook( static LRESULT CALLBACK -getMessageHook( - int code, - WPARAM wParam, - LPARAM lParam) +getMessageHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { if (g_relay) { @@ -277,10 +265,7 @@ getMessageHook( static LRESULT CALLBACK -keyboardLLHook( - int code, - WPARAM wParam, - LPARAM lParam) +keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { if (g_relay) { @@ -326,8 +311,7 @@ keyboardLLHook( static DWORD WINAPI -getKeyboardLLProc( - void*) +getKeyboardLLProc(void*) { // thread proc for low-level keyboard hook. this does nothing but // install the hook, process events, and uninstall the hook. @@ -382,8 +366,7 @@ getKeyboardLLProc( static DWORD WINAPI -getKeyboardLLProc( - void*) +getKeyboardLLProc(void*) { g_keyHookThreadID = 0; SetEvent(g_keyHookEvent); @@ -441,10 +424,7 @@ getWheelSupport() // BOOL WINAPI -DllMain( - HINSTANCE instance, - DWORD reason, - LPVOID) +DllMain(HINSTANCE instance, DWORD reason, LPVOID) { if (reason == DLL_PROCESS_ATTACH) { if (g_hinstance == NULL) { @@ -466,8 +446,7 @@ DllMain( extern "C" { int -install( - DWORD threadID) +install(DWORD threadID) { assert(g_threadID == 0); assert(g_hinstance != NULL); @@ -571,8 +550,7 @@ install( } int -uninstall( - void) +uninstall(void) { assert(g_keyboard != NULL); assert(g_mouse != NULL); @@ -607,11 +585,7 @@ uninstall( } void -setZone( - UInt32 sides, - SInt32 w, - SInt32 h, - SInt32 jumpZoneSize) +setZone(UInt32 sides, SInt32 w, SInt32 h, SInt32 jumpZoneSize) { g_zoneSize = jumpZoneSize; g_zoneSides = sides; @@ -622,8 +596,7 @@ setZone( } void -setRelay( - void) +setRelay(void) { g_relay = true; g_zoneSize = 0; diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index b175517c..75e3b467 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -194,8 +194,7 @@ CXWindowsPrimaryScreen::stop() } void -CXWindowsPrimaryScreen::open( - CServer* server) +CXWindowsPrimaryScreen::open(CServer* server) { assert(m_server == NULL); assert(server != NULL); @@ -252,9 +251,7 @@ CXWindowsPrimaryScreen::close() } void -CXWindowsPrimaryScreen::enter( - SInt32 x, - SInt32 y) +CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) { log((CLOG_INFO "entering primary at %d,%d", x, y)); assert(m_active == true); @@ -356,19 +353,14 @@ CXWindowsPrimaryScreen::onConfigure() } void -CXWindowsPrimaryScreen::warpCursor( - SInt32 x, - SInt32 y) +CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { CDisplayLock display(this); warpCursorNoLock(display, x, y); } void -CXWindowsPrimaryScreen::warpCursorNoLock( - Display* display, - SInt32 x, - SInt32 y) +CXWindowsPrimaryScreen::warpCursorNoLock(Display* display, SInt32 x, SInt32 y) { assert(display != NULL); assert(m_window != None); @@ -387,24 +379,20 @@ CXWindowsPrimaryScreen::warpCursorNoLock( } void -CXWindowsPrimaryScreen::setClipboard( - ClipboardID id, - const IClipboard* clipboard) +CXWindowsPrimaryScreen::setClipboard(ClipboardID id, + const IClipboard* clipboard) { setDisplayClipboard(id, clipboard); } void -CXWindowsPrimaryScreen::grabClipboard( - ClipboardID id) +CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) { setDisplayClipboard(id, NULL); } void -CXWindowsPrimaryScreen::getSize( - SInt32* width, - SInt32* height) const +CXWindowsPrimaryScreen::getSize(SInt32* width, SInt32* height) const { getScreenSize(width, height); } @@ -416,9 +404,8 @@ CXWindowsPrimaryScreen::getJumpZoneSize() const } void -CXWindowsPrimaryScreen::getClipboard( - ClipboardID id, - IClipboard* clipboard) const +CXWindowsPrimaryScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const { getDisplayClipboard(id, clipboard); } @@ -484,8 +471,7 @@ CXWindowsPrimaryScreen::isLockedToScreen() const } void -CXWindowsPrimaryScreen::onOpenDisplay( - Display* display) +CXWindowsPrimaryScreen::onOpenDisplay(Display* display) { assert(m_window == None); @@ -516,16 +502,14 @@ CXWindowsPrimaryScreen::onOpenDisplay( } CXWindowsClipboard* -CXWindowsPrimaryScreen::createClipboard( - ClipboardID id) +CXWindowsPrimaryScreen::createClipboard(ClipboardID id) { CDisplayLock display(this); return new CXWindowsClipboard(display, m_window, id); } void -CXWindowsPrimaryScreen::onCloseDisplay( - Display* display) +CXWindowsPrimaryScreen::onCloseDisplay(Display* display) { assert(m_window != None); @@ -546,17 +530,14 @@ CXWindowsPrimaryScreen::onUnexpectedClose() } void -CXWindowsPrimaryScreen::onLostClipboard( - ClipboardID id) +CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id) { // tell server that the clipboard was grabbed locally m_server->grabClipboard(id); } void -CXWindowsPrimaryScreen::selectEvents( - Display* display, - Window w) const +CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const { // ignore errors while we adjust event masks CXWindowsUtil::CErrorLock lock; @@ -566,9 +547,7 @@ CXWindowsPrimaryScreen::selectEvents( } void -CXWindowsPrimaryScreen::doSelectEvents( - Display* display, - Window w) const +CXWindowsPrimaryScreen::doSelectEvents(Display* display, Window w) const { // we want to track the mouse everywhere on the display. to achieve // that we select PointerMotionMask on every window. we also select @@ -595,8 +574,7 @@ CXWindowsPrimaryScreen::doSelectEvents( } KeyModifierMask -CXWindowsPrimaryScreen::mapModifier( - unsigned int state) const +CXWindowsPrimaryScreen::mapModifier(unsigned int state) const { // FIXME -- should be configurable KeyModifierMask mask = 0; @@ -618,8 +596,7 @@ CXWindowsPrimaryScreen::mapModifier( } KeyID -CXWindowsPrimaryScreen::mapKey( - XKeyEvent* event) const +CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const { KeySym keysym; char dummy[1]; @@ -630,8 +607,7 @@ CXWindowsPrimaryScreen::mapKey( } ButtonID -CXWindowsPrimaryScreen::mapButton( - unsigned int button) const +CXWindowsPrimaryScreen::mapButton(unsigned int button) const { // FIXME -- should use button mapping? if (button >= 1 && button <= 3) { @@ -643,8 +619,7 @@ CXWindowsPrimaryScreen::mapButton( } void -CXWindowsPrimaryScreen::updateModifierMap( - Display* display) +CXWindowsPrimaryScreen::updateModifierMap(Display* display) { // get modifier map from server XModifierKeymap* keymap = XGetModifierMapping(display); @@ -679,10 +654,7 @@ CXWindowsPrimaryScreen::updateModifierMap( } Bool -CXWindowsPrimaryScreen::findKeyEvent( - Display*, - XEvent* xevent, - XPointer arg) +CXWindowsPrimaryScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) { CKeyEventInfo* filter = reinterpret_cast(arg); return (xevent->type == filter->m_event && diff --git a/server/server.cpp b/server/server.cpp index 0072cb8f..690637f1 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -54,8 +54,7 @@ static CMutex* s_logMutex = NULL; static void -logLock( - bool lock) +logLock(bool lock) { assert(s_logMutex != NULL); @@ -76,8 +75,7 @@ static CServer* s_server = NULL; static int -realMain( - CMutex* mutex) +realMain(CMutex* mutex) { // s_serverLock should have mutex locked on entry @@ -293,12 +291,9 @@ PLATFORM_EXTRA static bool -isArg(int argi, - int argc, - const char** argv, - const char* name1, - const char* name2, - int minRequiredParameters = 0) +isArg(int argi, int argc, const char** argv, + const char* name1, const char* name2, + int minRequiredParameters = 0) { if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || (name2 != NULL && strcmp(argv[argi], name2) == 0)) { @@ -317,9 +312,7 @@ isArg(int argi, static void -parse( - int argc, - const char** argv) +parse(int argc, const char** argv) { assert(pname != NULL); assert(argv != NULL); @@ -480,9 +473,7 @@ parse( static bool -loadConfig( - const char* pathname, - bool require) +loadConfig(const char* pathname, bool require) { assert(pathname != NULL); @@ -556,9 +547,7 @@ loadConfig() static bool -logMessageBox( - int priority, - const char* msg) +logMessageBox(int priority, const char* msg) { if (priority <= CLog::kFATAL) { MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); @@ -585,10 +574,7 @@ daemonStop(void) static int -daemonStartup( - IPlatform* iplatform, - int argc, - const char** argv) +daemonStartup(IPlatform* iplatform, int argc, const char** argv) { // get platform pointer CWin32Platform* platform = static_cast(iplatform); @@ -614,9 +600,7 @@ daemonStartup( static bool -logDiscard( - int, - const char*) +logDiscard(int, const char*) { return true; } @@ -636,11 +620,7 @@ checkParse(int e) } int WINAPI -WinMain( - HINSTANCE instance, - HINSTANCE, - LPSTR, - int) +WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { CPlatform platform; @@ -779,18 +759,13 @@ WinMain( static int -daemonStartup( - IPlatform*, - int, - const char**) +daemonStartup(IPlatform*, int, const char**) { return restartableMain(); } int -main( - int argc, - char** argv) +main(int argc, char** argv) { CPlatform platform; diff --git a/synergy/CClipboard.cpp b/synergy/CClipboard.cpp index d5ec7018..8862d769 100644 --- a/synergy/CClipboard.cpp +++ b/synergy/CClipboard.cpp @@ -39,9 +39,7 @@ CClipboard::empty() } void -CClipboard::add( - EFormat format, - const CString& data) +CClipboard::add(EFormat format, const CString& data) { assert(m_open); assert(m_owner); @@ -51,8 +49,7 @@ CClipboard::add( } bool -CClipboard::open( - Time time) const +CClipboard::open(Time time) const { assert(!m_open); @@ -77,25 +74,21 @@ CClipboard::getTime() const } bool -CClipboard::has( - EFormat format) const +CClipboard::has(EFormat format) const { assert(m_open); return m_added[format]; } CString -CClipboard::get( - EFormat format) const +CClipboard::get(EFormat format) const { assert(m_open); return m_data[format]; } bool -CClipboard::copy( - IClipboard* dst, - const IClipboard* src) +CClipboard::copy(IClipboard* dst, const IClipboard* src) { assert(dst != NULL); assert(src != NULL); @@ -104,10 +97,7 @@ CClipboard::copy( } bool -CClipboard::copy( - IClipboard* dst, - const IClipboard* src, - Time time) +CClipboard::copy(IClipboard* dst, const IClipboard* src, Time time) { assert(dst != NULL); assert(src != NULL); @@ -134,9 +124,7 @@ CClipboard::copy( } void -CClipboard::unmarshall( - const CString& data, - Time time) +CClipboard::unmarshall(const CString& data, Time time) { const char* index = data.data(); @@ -201,8 +189,7 @@ CClipboard::marshall() const } UInt32 -CClipboard::readUInt32( - const char* buf) const +CClipboard::readUInt32(const char* buf) const { const unsigned char* ubuf = reinterpret_cast(buf); return (static_cast(ubuf[0]) << 24) | @@ -212,9 +199,7 @@ CClipboard::readUInt32( } void -CClipboard::writeUInt32( - CString* buf, - UInt32 v) const +CClipboard::writeUInt32(CString* buf, UInt32 v) const { *buf += static_cast((v >> 24) & 0xff); *buf += static_cast((v >> 16) & 0xff); diff --git a/synergy/CInputPacketStream.cpp b/synergy/CInputPacketStream.cpp index ce5b74cf..76c03620 100644 --- a/synergy/CInputPacketStream.cpp +++ b/synergy/CInputPacketStream.cpp @@ -5,9 +5,7 @@ // CInputPacketStream // -CInputPacketStream::CInputPacketStream( - IInputStream* stream, - bool adopt) : +CInputPacketStream::CInputPacketStream(IInputStream* stream, bool adopt) : CInputStreamFilter(stream, adopt), m_mutex(), m_size(0), @@ -28,9 +26,7 @@ CInputPacketStream::close() } UInt32 -CInputPacketStream::read( - void* buffer, - UInt32 n) +CInputPacketStream::read(void* buffer, UInt32 n) { CLock lock(&m_mutex); diff --git a/synergy/COutputPacketStream.cpp b/synergy/COutputPacketStream.cpp index 5e90f116..59923202 100644 --- a/synergy/COutputPacketStream.cpp +++ b/synergy/COutputPacketStream.cpp @@ -4,9 +4,7 @@ // COuputPacketStream // -COutputPacketStream::COutputPacketStream( - IOutputStream* stream, - bool adopt) : +COutputPacketStream::COutputPacketStream(IOutputStream* stream, bool adopt) : COutputStreamFilter(stream, adopt) { // do nothing @@ -24,9 +22,7 @@ COutputPacketStream::close() } UInt32 -COutputPacketStream::write( - const void* buffer, - UInt32 count) +COutputPacketStream::write(const void* buffer, UInt32 count) { // write the length of the payload UInt8 length[4]; diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index 246f484d..1b3102b2 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -10,10 +10,7 @@ // void -CProtocolUtil::writef( - IOutputStream* stream, - const char* fmt, - ...) +CProtocolUtil::writef(IOutputStream* stream, const char* fmt, ...) { assert(stream != NULL); assert(fmt != NULL); @@ -50,10 +47,7 @@ CProtocolUtil::writef( } void -CProtocolUtil::readf( - IInputStream* stream, - const char* fmt, - ...) +CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) { assert(stream != NULL); assert(fmt != NULL); @@ -185,9 +179,7 @@ CProtocolUtil::readf( } UInt32 -CProtocolUtil::getLength( - const char* fmt, - va_list args) +CProtocolUtil::getLength(const char* fmt, va_list args) { UInt32 n = 0; while (*fmt) { @@ -236,10 +228,7 @@ CProtocolUtil::getLength( } void -CProtocolUtil::writef( - void* buffer, - const char* fmt, - va_list args) +CProtocolUtil::writef(void* buffer, const char* fmt, va_list args) { UInt8* dst = reinterpret_cast(buffer); @@ -326,8 +315,7 @@ CProtocolUtil::writef( } UInt32 -CProtocolUtil::eatLength( - const char** pfmt) +CProtocolUtil::eatLength(const char** pfmt) { const char* fmt = *pfmt; UInt32 n = 0; @@ -352,10 +340,7 @@ CProtocolUtil::eatLength( } void -CProtocolUtil::read( - IInputStream* stream, - void* vbuffer, - UInt32 count) +CProtocolUtil::read(IInputStream* stream, void* vbuffer, UInt32 count) { assert(stream != NULL); assert(vbuffer != NULL); diff --git a/synergy/XSynergy.cpp b/synergy/XSynergy.cpp index b40e7fb3..8c6edd11 100644 --- a/synergy/XSynergy.cpp +++ b/synergy/XSynergy.cpp @@ -15,9 +15,7 @@ XBadClient::getWhat() const throw() // XIncompatibleClient // -XIncompatibleClient::XIncompatibleClient( - int major, - int minor) : +XIncompatibleClient::XIncompatibleClient(int major, int minor) : m_major(major), m_minor(minor) { @@ -47,8 +45,7 @@ XIncompatibleClient::getWhat() const throw() // XDuplicateClient // -XDuplicateClient::XDuplicateClient( - const CString& name) : +XDuplicateClient::XDuplicateClient(const CString& name) : m_name(name) { // do nothing @@ -71,8 +68,7 @@ XDuplicateClient::getWhat() const throw() // XUnknownClient // -XUnknownClient::XUnknownClient( - const CString& name) : +XUnknownClient::XUnknownClient(const CString& name) : m_name(name) { // do nothing From a81f5736660ef80613b8d0fc727270c8c3ca44fa Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 Jun 2002 14:10:25 +0000 Subject: [PATCH 171/807] updates --- README | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/README b/README index 56d79386..985e34cd 100644 --- a/README +++ b/README @@ -168,6 +168,12 @@ synergy can run in the foreground or as a daemon/service. it's recommended that you run it in the foreground until you've sorted out your configuration. +on the Windows NT family you cannot run a service directly. +instead you install the service then control it via the Services +control panel. on the Windows 95 family, you can use the +`--daemon' command line option to start synergy in the background +or you can install the service and restart your computer. + in the text below, except where noted, synergy refers to the client and/or the server. @@ -233,7 +239,9 @@ linux, unix: finally, some display managers (xdm and kdm, but not gdm) grab the keyboard and do not release it until the user logs in, also for security reasons. this prevents a synergy server - from sharing the mouse and keyboard until the user logs in. + from sharing the mouse and keyboard until the user logs in + but it doesn't prevent a synergy client from synthesizing + mouse and keyboard input. to install synergy as a daemon, you'll need to add the appropriate lines and/or files to start synergy at boot time @@ -261,7 +269,7 @@ linux: # /bin/cp synergyd.linux.init /etc/init.d/synergyd # /bin/cp synergy.conf /etc/synergy.conf # /sbin/chkconfig --add synergyd - of course, the configuration should be edited your for systems. + of course, /etc/synergy.conf should be edited your for systems. to manually start or stop the client # /etc/init.d/synergy start @@ -288,11 +296,8 @@ linux: common command line options --------------------------- -d, --debug use debugging level - --daemon run as a daemon (linux,unix) - --no-daemon run in the foreground (linux,unix) - --service run as a service (windows) - --no-service run in the foreground (windows) - -f run in the foreground + --daemon run as a daemon (linux,unix) or background (windows) + -f, --no-daemon run in the foreground -n, --name use instead of the hostname --restart automatically restart on unexpected failures -1, --no-restart do not restart on unexpected failure From 68740da942cd49e798662c680e7b3fc03e271f80 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 Jun 2002 15:44:45 +0000 Subject: [PATCH 172/807] made command line parsing a little more sane with respect to windows NT services. --- README | 8 ++--- client/client.cpp | 91 ++++++++++++++++------------------------------- server/server.cpp | 89 ++++++++++++++++----------------------------- 3 files changed, 65 insertions(+), 123 deletions(-) diff --git a/README b/README index 985e34cd..2993e486 100644 --- a/README +++ b/README @@ -169,9 +169,9 @@ recommended that you run it in the foreground until you've sorted out your configuration. on the Windows NT family you cannot run a service directly. -instead you install the service then control it via the Services -control panel. on the Windows 95 family, you can use the -`--daemon' command line option to start synergy in the background +instead you install the service then run or stop it via the +Services control panel. on the Windows 95 family, you can use +the `--daemon' command line option to start synergy as a service or you can install the service and restart your computer. in the text below, except where noted, synergy refers to the @@ -201,7 +201,7 @@ windows: directory (e.g. C:\WINNT) and call it "synergy.sgc". the server will automatically find this file. however, you can also use the --config command line option and specify an - absolute path to the file. remember that this file must be + *absolute* path to the file. remember that this file must be accessible when the system starts up, before network shares are mapped. diff --git a/client/client.cpp b/client/client.cpp index ea23b151..3b1418b8 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -485,24 +485,10 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) } static -bool -logDiscard(int, const char*) +int +daemonStartup95(IPlatform*, int, const char**) { - return true; -} - -static bool s_die = false; - -static -void -checkParse(int e) -{ - // anything over 1 means invalid args. 1 means missing args. - // 0 means graceful exit. we plan to exit for anything but - // 1 (missing args); the service control manager may supply - // the missing arguments so we don't exit in that case. - s_die = (e != 1); - throw s_die; + return restartableMain(); } int WINAPI @@ -519,42 +505,26 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // initialize network library CNetwork::init(); - // parse command line without reporting errors but recording if - // the app would've exited. this is too avoid showing a dialog - // box if we're being started as a service because we shouldn't - // take too long to startup in that case. this mostly works but - // will choke if the service control manager passes --install - // or --uninstall (but that's unlikely). - CLog::setOutputter(&logDiscard); - bye = &checkParse; - try { - parse(__argc, const_cast(__argv)); - } - catch (...) { - // ignore - } - // send PRINT and FATAL output to a message box CLog::setOutputter(&logMessageBox); - // if we're not starting as an NT service then reparse the command - // line normally. - if (s_die || __argc > 1 || s_install || s_uninstall || - CWin32Platform::isWindows95Family()) { - - // exit on bye - bye = &exit; - - // reparse - parse(__argc, const_cast(__argv)); + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + if (__argc <= 1 && !CWin32Platform::isWindows95Family()) { + int result = platform.daemonize(DAEMON_NAME, &daemonStartup); + if (result == -1) { + log((CLOG_CRIT "failed to start as a service" BYE, pname)); + return 16; + } + return result; } - // if no arguments were provided then we're hopefully starting as - // a service. we'll parse the command line passed in when the - // service control manager calls us back. - else { - // do nothing - } + // parse command line + parse(__argc, const_cast(__argv)); // install/uninstall if (s_install) { @@ -609,24 +579,25 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // daemonize if requested int result; - if (__argc <= 1) { + if (s_daemon) { + // redirect log messages + platform.installDaemonLogger(DAEMON_NAME); + + // start as a daemon if (CWin32Platform::isWindows95Family()) { - result = -1; + result = platform.daemonize(DAEMON_NAME, &daemonStartup95); + if (result == -1) { + log((CLOG_CRIT "failed to start as a service" BYE, pname)); + return 16; + } } else { - result = platform.daemonize(DAEMON_NAME, &daemonStartup); - } - if (result == -1) { - log((CLOG_CRIT "failed to start as a service" BYE, pname)); - return 16; + // cannot start a service from the command line so just + // run normally (except with log messages redirected). + result = restartableMain(); } } else { - // if a daemon then redirect log - if (s_daemon) { - platform.installDaemonLogger(__argv[0]); - } - // run result = restartableMain(); } diff --git a/server/server.cpp b/server/server.cpp index 690637f1..dbbf7e0f 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -599,24 +599,10 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) } static -bool -logDiscard(int, const char*) +int +daemonStartup95(IPlatform*, int, const char**) { - return true; -} - -static bool s_die = false; - -static -void -checkParse(int e) -{ - // anything over 1 means invalid args. 1 means missing args. - // 0 means graceful exit. we plan to exit for anything but - // 1 (missing args); the service control manager may supply - // the missing arguments so we don't exit in that case. - s_die = (e != 1); - throw s_die; + return restartableMain(); } int WINAPI @@ -633,42 +619,26 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // initialize network library CNetwork::init(); - // parse command line without reporting errors but recording if - // the app would've exited. this is too avoid showing a dialog - // box if we're being started as a service because we shouldn't - // take to long to startup as a service. this mostly works but - // will choke if the service control manager passes --install - // or --uninstall (but that's unlikely). - CLog::setOutputter(&logDiscard); - bye = &checkParse; - try { - parse(__argc, const_cast(__argv)); - } - catch (...) { - // ignore - } - // send PRINT and FATAL output to a message box CLog::setOutputter(&logMessageBox); - // if we're not starting as an NT service then reparse the command - // line normally. - if (s_die || __argc > 1 || s_install || s_uninstall || - CWin32Platform::isWindows95Family()) { - - // exit on bye - bye = &exit; - - // reparse - parse(__argc, const_cast(__argv)); + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + if (__argc <= 1 && !CWin32Platform::isWindows95Family()) { + int result = platform.daemonize(DAEMON_NAME, &daemonStartup); + if (result == -1) { + log((CLOG_CRIT "failed to start as a service" BYE, pname)); + return 16; + } + return result; } - // if no arguments were provided then we're hopefully starting as - // a service. we'll parse the command line passed in when the - // service control manager calls us back. - else { - // do nothing - } + // parse command line + parse(__argc, const_cast(__argv)); // install/uninstall if (s_install) { @@ -731,23 +701,24 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // daemonize if requested int result; if (s_daemon) { + // redirect log messages + platform.installDaemonLogger(DAEMON_NAME); + + // start as a daemon if (CWin32Platform::isWindows95Family()) { - result = -1; + result = platform.daemonize(DAEMON_NAME, &daemonStartup95); + if (result == -1) { + log((CLOG_CRIT "failed to start as a service" BYE, pname)); + return 16; + } } else { - result = platform.daemonize(DAEMON_NAME, &daemonStartup); - } - if (result == -1) { - log((CLOG_CRIT "failed to start as a service" BYE, pname)); - return 16; + // cannot start a service from the command line so just + // run normally (except with log messages redirected). + result = restartableMain(); } } else { - // if a daemon then redirect log - if (s_daemon) { - platform.installDaemonLogger(__argv[0]); - } - // run result = restartableMain(); } From 32713d0cfbc55732cd8df773474eef9a8efc8e55 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 18 Jun 2002 18:33:59 +0000 Subject: [PATCH 173/807] added FIXME to commented out code. --- net/XSocket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/XSocket.cpp b/net/XSocket.cpp index 945fac71..7cb6e995 100644 --- a/net/XSocket.cpp +++ b/net/XSocket.cpp @@ -35,7 +35,7 @@ CString XSocketAddress::getWhat() const throw() { return "no address"; -/* +/* FIXME return format("XSocketAddress", "no address: %1:%2", m_hostname.t_str(), CString::sprintf("%d", m_port).t_str()); From fadc73d348829765900639ec384545ebbf49c964 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 18 Jun 2002 18:34:55 +0000 Subject: [PATCH 174/807] now checking vsnprintf result against < 0 instead of == -1 for portability. --- base/CLog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index 1bf9e0e0..a793699f 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -252,7 +252,7 @@ CLog::vsprint(int pad, char* buffer, int len, const char* fmt, va_list args) int n; if (len >= pad) { n = vsnprintf(buffer + pad, len - pad, fmt, args); - if (n != -1 && n <= len - pad + g_newlineLength) + if (n >= 0 && n <= len - pad + g_newlineLength) return buffer; } @@ -263,7 +263,7 @@ CLog::vsprint(int pad, char* buffer, int len, const char* fmt, va_list args) len *= 2; buffer = new char[len + pad]; n = vsnprintf(buffer + pad, len - pad, fmt, args); - } while (n == -1 || n > len - pad + g_newlineLength); + } while (n < 0 || n > len - pad + g_newlineLength); return buffer; } From 1c604ecc3dba9041b3d60a43aabd89dff68eb6b9 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 18 Jun 2002 19:44:34 +0000 Subject: [PATCH 175/807] started to convert to autoconf/automake. --- Make-linux | 71 -------------- Make-solaris | 72 -------------- Makecommon | 94 ------------------ Makefile | 35 ------- Makefile.am | 16 +++ acsite.m4 | 227 +++++++++++++++++++++++++++++++++++++++++++ base/Makefile | 26 ----- base/Makefile.am | 34 +++++++ client/Makefile | 47 --------- client/Makefile.am | 29 ++++++ configure.in | 65 +++++++++++++ http/Makefile | 26 ----- http/Makefile.am | 14 +++ io/Makefile | 29 ------ io/Makefile.am | 17 ++++ mt/Makefile | 28 ------ mt/Makefile.am | 16 +++ net/Makefile | 30 ------ net/Makefile.am | 18 ++++ platform/Makefile | 28 ------ platform/Makefile.am | 16 +++ server/Makefile | 53 ---------- server/Makefile.am | 35 +++++++ synergy/Makefile | 33 ------- synergy/Makefile.am | 20 ++++ tools/depconv | 46 --------- 26 files changed, 507 insertions(+), 618 deletions(-) delete mode 100644 Make-linux delete mode 100644 Make-solaris delete mode 100644 Makecommon delete mode 100644 Makefile create mode 100644 Makefile.am create mode 100644 acsite.m4 delete mode 100644 base/Makefile create mode 100644 base/Makefile.am delete mode 100644 client/Makefile create mode 100644 client/Makefile.am create mode 100644 configure.in delete mode 100644 http/Makefile create mode 100644 http/Makefile.am delete mode 100644 io/Makefile create mode 100644 io/Makefile.am delete mode 100644 mt/Makefile create mode 100644 mt/Makefile.am delete mode 100644 net/Makefile create mode 100644 net/Makefile.am delete mode 100644 platform/Makefile create mode 100644 platform/Makefile.am delete mode 100644 server/Makefile create mode 100644 server/Makefile.am delete mode 100644 synergy/Makefile create mode 100644 synergy/Makefile.am delete mode 100755 tools/depconv diff --git a/Make-linux b/Make-linux deleted file mode 100644 index 8300b12d..00000000 --- a/Make-linux +++ /dev/null @@ -1,71 +0,0 @@ -# -# build tools -# -AR = /usr/bin/ar cru -CD = cd -CXX = /usr/bin/g++ -ECHO = echo -LD = /usr/bin/ld -MKDIR = /bin/mkdir -RM = /bin/rm -f -RMR = /bin/rm -rf - -# -# compiler options -# -GCXXDEFS = -D_XOPEN_SOURCE=600 -D_SVID_SOURCE -GCXXINCS = -I$(DEPTH)/include -I/usr/X11R6/include -GCXXOPTS = -Wall -W -fexceptions -CXXOPTIMIZER = -g -#CXXOPTIMIZER = -O2 -DNDEBUG - -# -# linker options -# -GLDLIBS = -L/usr/X11R6/lib -lX11 -lXext -lXtst -GLDOPTS = -L$(LIBDIR) - -# -# binary stuff -# -BINTARGET = $(BINDIR)/$(TARGET) - -# -# library stuff -# -LIBTARGET = $(LIBDIR)/lib$(TARGET).a - -# -# dependency generation stuff -# -MKDEP = $(DEPTH)/tools/depconv -MKDEPOPT = -MD -MKDEPPRE = -MKDEPPOST = $(MKDEP) -f $(MKDEPFILE) $*.d; $(RM) $*.d -MKDEPFILE = .depend - -# -# stuff to clean -# -DIRT = $(_FORCE) $(LDIRT) $(GDIRT) -GDIRT = *.[eoud] a.out core ar.tmp.* $(MKDEPFILE) - -# -# Rule macros for nonterminal makefiles that iterate over subdirectories, -# making the current target. Set SUBDIRS to the relevant list of kids. -# -# Set NOSUBMESG to any value to suppress a warning that subdirectories -# are not present. -# -SUBDIR_MAKERULE= \ - if test ! -d $$d; then \ - if test "$(NOSUBMESG)" = "" ; then \ - ${ECHO} "SKIPPING $$d: No such directory."; \ - fi \ - else \ - ${ECHO} "${CD} $$d; $(MAKE) $${RULE:=$@}"; \ - (${CD} $$d; ${MAKE} $${RULE:=$@}); \ - fi - -SUBDIRS_MAKERULE= \ - @for d in $(SUBDIRS); do $(SUBDIR_MAKERULE); done diff --git a/Make-solaris b/Make-solaris deleted file mode 100644 index 8730b63e..00000000 --- a/Make-solaris +++ /dev/null @@ -1,72 +0,0 @@ -# -# build tools -# -AR = /usr/ccs/bin/ar cru -CD = cd -CXX = g++ -ECHO = echo -LD = /usr/css/bin/ld -MKDIR = /bin/mkdir -RM = /bin/rm -f -RMR = /bin/rm -rf - -# -# compiler options -# -GCXXDEFS = -GCXXINCS = -I$(DEPTH)/include -I/usr/X11R6/include -GCXXOPTS = -Wall -W -fexceptions -fno-rtti -CXXOPTIMIZER = -g -#CXXOPTIMIZER = -O2 -DNDEBUG - -# -# linker options -# -#GLDLIBS = -L$(LIBDIR) -L/usr/X11R6/lib -lX11 -lXext -lXtst -GLDLIBS = -L$(LIBDIR) -lsocket -lnsl -lposix4 -GLDOPTS = -z muldefs - -# -# binary stuff -# -BINTARGET = $(BINDIR)/$(TARGET) - -# -# library stuff -# -LIBTARGET = $(LIBDIR)/lib$(TARGET).a - -# -# dependency generation stuff -# -MKDEP = $(DEPTH)/tools/depconv -MKDEPOPT = -MD -MKDEPPRE = -MKDEPPOST = $(MKDEP) -f $(MKDEPFILE) $*.d; $(RM) $*.d -MKDEPFILE = .depend - -# -# stuff to clean -# -DIRT = $(_FORCE) $(LDIRT) $(GDIRT) -GDIRT = *.[eoud] a.out core ar.tmp.* $(MKDEPFILE) - -# -# Rule macros for nonterminal makefiles that iterate over subdirectories, -# making the current target. Set SUBDIRS to the relevant list of kids. -# -# Set NOSUBMESG to any value to suppress a warning that subdirectories -# are not present. -# -SUBDIR_MAKERULE= \ - if test ! -d $$d; then \ - if test "$(NOSUBMESG)" = "" ; then \ - ${ECHO} "SKIPPING $$d: No such directory."; \ - fi \ - else \ - ${ECHO} "${CD} $$d; $(MAKE) $${RULE:=$@}"; \ - (${CD} $$d; ${MAKE} $${RULE:=$@}); \ - fi - -SUBDIRS_MAKERULE= \ - @for d in $(SUBDIRS); do $(SUBDIR_MAKERULE); done diff --git a/Makecommon b/Makecommon deleted file mode 100644 index a126b2da..00000000 --- a/Makecommon +++ /dev/null @@ -1,94 +0,0 @@ -# -# -# common definitions -# -# - -# -# empty define, used to terminate lists -# -NULL = - -# -# target directories -# -LIBDIR = $(DEPTH)/lib -BINDIR = $(DEPTH)/bin - -# -# compiler options -# -CXXFLAGS = $(LCXXFLAGS) $(GCXXFLAGS) $(CXXOPTIMIZER) $(MKDEPOPT) -LCXXFLAGS = $(LCXXDEFS) $(LCXXINCS) $(LCXXOPTS) -GCXXFLAGS = $(GCXXDEFS) $(GCXXINCS) $(GCXXOPTS) - -# -# linker options -# -LDFLAGS = $(LDOPTS) $(LDLIBS) -LDOPTS = $(LLDOPTS) $(GLDOPTS) -LDLIBS = $(LLDLIBS) $(GLDLIBS) - -# -# ar options -# -ARF = $(AR) - -# -# Convenience file list macros: -# -SOURCES = $(CXXFILES) -OBJECTS = $(CXXFILES:.cpp=.o) -TARGETS = $(BINTARGET) $(LIBTARGET) - -# -# always unsatisfied target -# -_FORCE = $(COMMONPREF)_force - -# -# -# common rules -# -# - -# -# default target. makefiles must define a target named `targets'. -# -$(COMMONPREF)default: targets - -# -# always unsatisfied target -# -$(_FORCE): - -# -# cleaners -# -COMMONTARGETS = clean clobber -$(COMMONPREF)clean: $(_FORCE) - $(RM) $(DIRT) - -$(COMMONPREF)clobber: clean $(_FORCE) - $(RM) $(TARGETS) - -# -# implicit target rules -# -.SUFFIXES: .cpp .o - -.cpp.o: - $(MKDEPPRE) - $(CXX) $(CXXFLAGS) -c $< - $(MKDEPPOST) - -# -# platform stuff -# -include $(DEPTH)/Make-linux -#include $(DEPTH)/Make-solaris - -# -# load dependencies -# -sinclude $(MKDEPFILE) diff --git a/Makefile b/Makefile deleted file mode 100644 index 29e61219..00000000 --- a/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -DEPTH=. -COMMONPREF = root -include Makecommon - -# -# subdirectories -# -SUBDIRS = \ - base \ - mt \ - io \ - http \ - net \ - synergy \ - platform \ - client \ - server \ - $(NULL) - -# -# targets -# - -default: $(COMMONPREF)_force - $(SUBDIRS_MAKERULE) - -all targets: default - -clean: - $(RMR) $(LIBDIR) - $(SUBDIRS_MAKERULE) - -clobber: - $(RMR) $(LIBDIR) - $(SUBDIRS_MAKERULE) diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..52c2c014 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,16 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = . + +EXTRA_DIST = +SUBDIRS = \ + base \ + mt \ + io \ + http \ + net \ + synergy \ + platform \ + client \ + server \ + $(NULL) diff --git a/acsite.m4 b/acsite.m4 new file mode 100644 index 00000000..4b02bee6 --- /dev/null +++ b/acsite.m4 @@ -0,0 +1,227 @@ +dnl The following macros are from http://www.gnu.org/software/ac-archive/ +dnl which distributes them under the following license: +dnl +dnl Every Autoconf macro presented on this web site is free software; you can +dnl redistribute it and/or modify it under the terms of the GNU General +dnl Public License as published by the Free Software Foundation; either +dnl version 2, or (at your option) any later version. +dnl +dnl They are distributed in the hope that they will be useful, but WITHOUT +dnl ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +dnl FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +dnl more details. (You should have received a copy of the GNU General Public +dnl License along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place -- Suite 330, Boston, MA 02111-1307, +dnl USA.) +dnl +dnl As a special exception, the Free Software Foundation gives unlimited +dnl permission to copy, distribute and modify the configure scripts that are +dnl the output of Autoconf. You need not follow the terms of the GNU General +dnl Public License when using or distributing such scripts, even though +dnl portions of the text of Autoconf appear in them. The GNU General Public +dnl License (GPL) does govern all other use of the material that constitutes +dnl the Autoconf program. +dnl +dnl Certain portions of the Autoconf source text are designed to be copied +dnl (in certain cases, depending on the input) into the output of Autoconf. +dnl We call these the "data" portions. The rest of the Autoconf source text +dnl consists of comments plus executable code that decides which of the data +dnl portions to output in any given case. We call these comments and +dnl executable code the "non-data" portions. Autoconf never copies any of the +dnl non-data portions into its output. +dnl +dnl This special exception to the GPL applies to versions of Autoconf +dnl released by the Free Software Foundation. When you make and distribute a +dnl modified version of Autoconf, you may extend this special exception to +dnl the GPL to apply to your modified version as well, *unless* your modified +dnl version has the potential to copy into its output some of the text that +dnl was the non-data portion of the version that you started with. (In other +dnl words, unless your change moves or copies text from the non-data portions +dnl to the data portions.) If your modification has such potential, you must +dnl delete any notice of this special exception to the GPL from your modified +dnl version + +AC_DEFUN([ACX_PTHREAD], [ +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + AC_MSG_CHECKING([for joinable pthread attribute]) + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_JOINABLE;], + ok=PTHREAD_CREATE_JOINABLE, ok=unknown) + if test x"$ok" = xunknown; then + AC_TRY_LINK([#include ], + [int attr=PTHREAD_CREATE_UNDETACHED;], + ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, + [Define to the necessary symbol if this constant + uses a non-standard name on your system.]) + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" = xunknown; then + AC_MSG_WARN([we do not know how to create joinable pthreads]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + *solaris* | alpha*-osf*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi + +])dnl ACX_PTHREAD diff --git a/base/Makefile b/base/Makefile deleted file mode 100644 index dcb324f4..00000000 --- a/base/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -DEPTH=.. -include $(DEPTH)/Makecommon - -# -# target file -# -TARGET = base - -# -# source files -# -LCXXINCS = \ - $(NULL) -CXXFILES = \ - XBase.cpp \ - CLog.cpp \ - CFunctionJob.cpp \ - CStopwatch.cpp \ - CString.cpp \ - $(NULL) - -targets: $(LIBTARGET) - -$(LIBTARGET): $(OBJECTS) - if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi - $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/base/Makefile.am b/base/Makefile.am new file mode 100644 index 00000000..07ee18bc --- /dev/null +++ b/base/Makefile.am @@ -0,0 +1,34 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +noinst_LIBRARIES = libbase.a +libbase_a_SOURCES = \ + CFunctionJob.cpp \ + CLog.cpp \ + CStopwatch.cpp \ + CString.cpp \ + XBase.cpp \ + BasicTypes.h \ + CFunctionJob.h \ + CLog.h \ + CStopwatch.h \ + CString.h \ + IInterface.h \ + IJob.h \ + TMethodJob.h \ + XBase.h \ + common.h \ + stdfstream.h \ + stdistream.h \ + stdlist.h \ + stdmap.h \ + stdostream.h \ + stdpost.h \ + stdpre.h \ + stdset.h \ + stdsstream.h \ + stdvector.h \ + $(NULL) +INCLUDES = \ + $(NULL) diff --git a/client/Makefile b/client/Makefile deleted file mode 100644 index 0c3d7cba..00000000 --- a/client/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -DEPTH=.. -include $(DEPTH)/Makecommon - -# -# target file -# -TARGET = synergy - -# -# source files -# -LCXXINCS = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ - -I$(DEPTH)/net \ - -I$(DEPTH)/synergy \ - -I$(DEPTH)/platform \ - $(NULL) -CXXFILES = \ - CXWindowsSecondaryScreen.cpp \ - CClient.cpp \ - client.cpp \ - $(NULL) - -# -# libraries we depend on -# -DEPLIBS = \ - $(LIBDIR)/libplatform.a \ - $(LIBDIR)/libsynergy.a \ - $(LIBDIR)/libnet.a \ - $(LIBDIR)/libio.a \ - $(LIBDIR)/libmt.a \ - $(LIBDIR)/libbase.a \ - $(NULL) -LLDLIBS = \ - $(DEPLIBS) \ - -lpthread \ - $(NULL) - -targets: $(BINTARGET) - -$(BINTARGET): $(OBJECTS) $(DEPLIBS) - if test ! -d $(BINDIR); then $(MKDIR) $(BINDIR); fi - $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) - diff --git a/client/Makefile.am b/client/Makefile.am new file mode 100644 index 00000000..fe5ae6ec --- /dev/null +++ b/client/Makefile.am @@ -0,0 +1,29 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +bin_PROGRAMS = synergy +synergy_SOURCES = \ + CXWindowsSecondaryScreen.cpp \ + CClient.cpp \ + client.cpp \ + $(NULL) +synergy_LDADD = \ + $(DEPTH)/platform/libplatform.a \ + $(DEPTH)/synergy/libsynergy.a \ + $(DEPTH)/net/libnet.a \ + $(DEPTH)/io/libio.a \ + $(DEPTH)/mt/libmt.a \ + $(DEPTH)/base/libbase.a \ + -lX11 \ + -lXext \ + -lXtst \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + -I$(DEPTH)/net \ + -I$(DEPTH)/synergy \ + -I$(DEPTH)/platform \ + $(NULL) diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..cc2ca4fc --- /dev/null +++ b/configure.in @@ -0,0 +1,65 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(base/IInterface.h) +AM_INIT_AUTOMAKE(synergy, 0.5) +AM_CONFIG_HEADER(config.h) + +dnl information on the package + +dnl checks for programs +AC_PROG_CC +AC_PROG_CXX +AC_PROG_RANLIB + +dnl checks for libraries +ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) + +dnl checks for header files +AC_HEADER_STDC +AC_PATH_X +if test "$no_x" = yes; then + AC_MSG_ERROR(You must have X Windows to compile synergy) +fi +AC_PATH_XTRA +AC_CHECK_HEADERS(sys/time.h) +dnl AC_HEADER_TIME + +dnl checks for types +dnl AC_TYPE_SIZE_T + +dnl checks for structures +AC_STRUCT_TM + +dnl checks for compiler characteristics + + +dnl checks for library functions +dnl AC_TYPE_SIGNAL +dnl AC_FUNC_FORK +AC_FUNC_MEMCMP +AC_FUNC_STRFTIME +dnl use AC_REPLACE_FUNCS() for stuff in string.h +dnl AC_HEADER_SYS_WAIT + +dnl checks for system services + + +dnl adjust variables for X11 and pthreads +LIBS="$X_PRE_LIBS $LIBS $X_EXTRA_LIBS $X_LIBS $PTHREAD_LIBS" +CFLAGS="$CFLAGS $X_CFLAGS $PTHREAD_CFLAGS" +CXXFLAGS="$CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" +CC="$PTHREAD_CC" + +AC_OUTPUT([ +Makefile +base/Makefile +mt/Makefile +io/Makefile +http/Makefile +net/Makefile +synergy/Makefile +platform/Makefile +client/Makefile +server/Makefile +]) + diff --git a/http/Makefile b/http/Makefile deleted file mode 100644 index dd89e329..00000000 --- a/http/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -DEPTH=.. -include $(DEPTH)/Makecommon - -# -# target file -# -TARGET = http - -# -# source files -# -LCXXINCS = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ - $(NULL) -CXXFILES = \ - XHTTP.cpp \ - CHTTPProtocol.cpp \ - $(NULL) - -targets: $(LIBTARGET) - -$(LIBTARGET): $(OBJECTS) $(DEPLIBS) - if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi - $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/http/Makefile.am b/http/Makefile.am new file mode 100644 index 00000000..ecdf2c9c --- /dev/null +++ b/http/Makefile.am @@ -0,0 +1,14 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +noinst_LIBRARIES = libhttp.a +libhttp_a_SOURCES = \ + CHTTPProtocol.cpp \ + XHTTP.cpp \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + $(NULL) diff --git a/io/Makefile b/io/Makefile deleted file mode 100644 index 49496cd9..00000000 --- a/io/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -DEPTH=.. -include $(DEPTH)/Makecommon - -# -# target file -# -TARGET = io - -# -# source files -# -LCXXINCS = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - $(NULL) -CXXFILES = \ - XIO.cpp \ - CInputStreamFilter.cpp \ - COutputStreamFilter.cpp \ - CStreamBuffer.cpp \ - CBufferedInputStream.cpp \ - CBufferedOutputStream.cpp \ - $(NULL) - -targets: $(LIBTARGET) - -$(LIBTARGET): $(OBJECTS) $(DEPLIBS) - if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi - $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/io/Makefile.am b/io/Makefile.am new file mode 100644 index 00000000..e0b58ce6 --- /dev/null +++ b/io/Makefile.am @@ -0,0 +1,17 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +noinst_LIBRARIES = libio.a +libio_a_SOURCES = \ + CBufferedInputStream.cpp \ + CBufferedOutputStream.cpp \ + CInputStreamFilter.cpp \ + COutputStreamFilter.cpp \ + CStreamBuffer.cpp \ + XIO.cpp \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + $(NULL) diff --git a/mt/Makefile b/mt/Makefile deleted file mode 100644 index 1a7270c8..00000000 --- a/mt/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -DEPTH=.. -include $(DEPTH)/Makecommon - -# -# target file -# -TARGET = mt - -# -# source files -# -LCXXINCS = \ - -I$(DEPTH)/base \ - $(NULL) -CXXFILES = \ - CLock.cpp \ - CMutex.cpp \ - CCondVar.cpp \ - CThread.cpp \ - CThreadRep.cpp \ - CTimerThread.cpp \ - $(NULL) - -targets: $(LIBTARGET) - -$(LIBTARGET): $(OBJECTS) - if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi - $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/mt/Makefile.am b/mt/Makefile.am new file mode 100644 index 00000000..0b23ad3f --- /dev/null +++ b/mt/Makefile.am @@ -0,0 +1,16 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +noinst_LIBRARIES = libmt.a +libmt_a_SOURCES = \ + CLock.cpp \ + CMutex.cpp \ + CCondVar.cpp \ + CThread.cpp \ + CThreadRep.cpp \ + CTimerThread.cpp \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/base \ + $(NULL) diff --git a/net/Makefile b/net/Makefile deleted file mode 100644 index 741ba8bf..00000000 --- a/net/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -DEPTH=.. -include $(DEPTH)/Makecommon - -# -# target file -# -TARGET = net - -# -# source files -# -LCXXINCS = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ - $(NULL) -CXXFILES = \ - CNetwork.cpp \ - CNetworkAddress.cpp \ - CTCPSocket.cpp \ - CTCPListenSocket.cpp \ - XNetwork.cpp \ - XSocket.cpp \ - $(NULL) - -targets: $(LIBTARGET) - -$(LIBTARGET): $(OBJECTS) - if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi - $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/net/Makefile.am b/net/Makefile.am new file mode 100644 index 00000000..6a5b59d9 --- /dev/null +++ b/net/Makefile.am @@ -0,0 +1,18 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +noinst_LIBRARIES = libnet.a +libnet_a_SOURCES = \ + CNetwork.cpp \ + CNetworkAddress.cpp \ + CTCPSocket.cpp \ + CTCPListenSocket.cpp \ + XNetwork.cpp \ + XSocket.cpp \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + $(NULL) diff --git a/platform/Makefile b/platform/Makefile deleted file mode 100644 index 26186b72..00000000 --- a/platform/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -DEPTH=.. -include $(DEPTH)/Makecommon - -# -# target file -# -TARGET = platform - -# -# source files -# -LCXXINCS = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/synergy \ - $(NULL) -CXXFILES = \ - CPlatform.cpp \ - CXWindowsClipboard.cpp \ - CXWindowsScreen.cpp \ - CXWindowsUtil.cpp \ - $(NULL) - -targets: $(LIBTARGET) - -$(LIBTARGET): $(OBJECTS) $(DEPLIBS) - if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi - $(ARF) $(LIBTARGET) $(OBJECTS) diff --git a/platform/Makefile.am b/platform/Makefile.am new file mode 100644 index 00000000..e6c4c2ca --- /dev/null +++ b/platform/Makefile.am @@ -0,0 +1,16 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +noinst_LIBRARIES = libplatform.a +libplatform_a_SOURCES = \ + CPlatform.cpp \ + CXWindowsClipboard.cpp \ + CXWindowsScreen.cpp \ + CXWindowsUtil.cpp \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/synergy \ + $(NULL) diff --git a/server/Makefile b/server/Makefile deleted file mode 100644 index eca0dcd2..00000000 --- a/server/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -DEPTH=.. -include $(DEPTH)/Makecommon - -# -# target file -# -TARGET = synergyd - -# -# source files -# -LCXXINCS = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ - -I$(DEPTH)/http \ - -I$(DEPTH)/net \ - -I$(DEPTH)/synergy \ - -I$(DEPTH)/platform \ - $(NULL) -CXXFILES = \ - CConfig.cpp \ - CServerProtocol.cpp \ - CServerProtocol1_0.cpp \ - CXWindowsPrimaryScreen.cpp \ - CServer.cpp \ - CHTTPServer.cpp \ - server.cpp \ - $(NULL) - -# -# libraries we depend on -# -DEPLIBS = \ - $(LIBDIR)/libplatform.a \ - $(LIBDIR)/libsynergy.a \ - $(LIBDIR)/libnet.a \ - $(LIBDIR)/libhttp.a \ - $(LIBDIR)/libio.a \ - $(LIBDIR)/libmt.a \ - $(LIBDIR)/libbase.a \ - $(NULL) -LLDLIBS = \ - $(DEPLIBS) \ - -lpthread \ - $(NULL) - -targets: $(BINTARGET) - -$(BINTARGET): $(OBJECTS) $(DEPLIBS) - if test ! -d $(BINDIR); then $(MKDIR) $(BINDIR); fi - $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) - diff --git a/server/Makefile.am b/server/Makefile.am new file mode 100644 index 00000000..1ed7866a --- /dev/null +++ b/server/Makefile.am @@ -0,0 +1,35 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +bin_PROGRAMS = synergyd +synergyd_SOURCES = \ + CConfig.cpp \ + CServerProtocol.cpp \ + CServerProtocol1_0.cpp \ + CXWindowsPrimaryScreen.cpp \ + CServer.cpp \ + CHTTPServer.cpp \ + server.cpp \ + $(NULL) +synergyd_LDADD = \ + $(DEPTH)/platform/libplatform.a \ + $(DEPTH)/synergy/libsynergy.a \ + $(DEPTH)/net/libnet.a \ + $(DEPTH)/http/libhttp.a \ + $(DEPTH)/io/libio.a \ + $(DEPTH)/mt/libmt.a \ + $(DEPTH)/base/libbase.a \ + -lX11 \ + -lXext \ + -lXtst \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + -I$(DEPTH)/http \ + -I$(DEPTH)/net \ + -I$(DEPTH)/synergy \ + -I$(DEPTH)/platform \ + $(NULL) diff --git a/synergy/Makefile b/synergy/Makefile deleted file mode 100644 index 6c55ab69..00000000 --- a/synergy/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -DEPTH=.. -include $(DEPTH)/Makecommon - -# -# target file -# -TARGET = synergy - -# -# source files -# -LCXXINCS = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ - -I$(DEPTH)/net \ - $(NULL) -CXXFILES = \ - CInputPacketStream.cpp \ - COutputPacketStream.cpp \ - CProtocolUtil.cpp \ - CClipboard.cpp \ - CTCPSocketFactory.cpp \ - XScreen.cpp \ - XSynergy.cpp \ - $(NULL) - -targets: $(LIBTARGET) - -$(LIBTARGET): $(OBJECTS) - if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi - $(ARF) $(LIBTARGET) $(OBJECTS) - diff --git a/synergy/Makefile.am b/synergy/Makefile.am new file mode 100644 index 00000000..3b10a96b --- /dev/null +++ b/synergy/Makefile.am @@ -0,0 +1,20 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +noinst_LIBRARIES = libsynergy.a +libsynergy_a_SOURCES = \ + CInputPacketStream.cpp \ + COutputPacketStream.cpp \ + CProtocolUtil.cpp \ + CClipboard.cpp \ + CTCPSocketFactory.cpp \ + XScreen.cpp \ + XSynergy.cpp \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/io \ + -I$(DEPTH)/net \ + $(NULL) diff --git a/tools/depconv b/tools/depconv deleted file mode 100755 index 7bccd466..00000000 --- a/tools/depconv +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -# -# add dependency info from files on command line to $depfile - -depfile="Makedepends" - -dependencies="" -targets="^$" - -# tmp directory -if test -z "$TMP"; then TMP=/tmp; fi - -while [ -n "$*" ]; do - case "$1" in - -f) - depfile=$2 - shift - shift - ;; - -*) - echo "usage: $0 [-f ] ..." - exit 1 - ;; - *) - break - esac -done - -# collect all dependencies -while [ -n "$*" ]; do - line=`cat $1 | sed -e 's/\\\\//g' | sed -e 's/ \/[^ ]*//g'` - target=`echo $line | sed -e 's/^\([^:]*\):.*/\1/'` - targets="$targets|^$target:" - dependencies="$dependencies$line\n" - shift -done - -# add new dependencies to $depfile -if [ -n "$targets" ]; then - if [ -r $depfile ]; then - (egrep -v $targets $depfile; echo -e -n $dependencies) > $TMP/dep$$ - if [ $? -eq 0 ]; then mv $TMP/dep$$ $depfile; fi - else - echo -e -n $dependencies > $depfile - fi -fi From eabfcb9e167474b1651eec770bb5c80443ab7f56 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 18 Jun 2002 19:47:52 +0000 Subject: [PATCH 176/807] added automake required tools. --- install-sh | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++ missing | 190 ++++++++++++++++++++++++++++++++++++++ mkinstalldirs | 40 ++++++++ 3 files changed, 481 insertions(+) create mode 100755 install-sh create mode 100755 missing create mode 100755 mkinstalldirs diff --git a/install-sh b/install-sh new file mode 100755 index 00000000..e9de2384 --- /dev/null +++ b/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, 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 M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/missing b/missing new file mode 100755 index 00000000..7789652e --- /dev/null +++ b/missing @@ -0,0 +1,190 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright (C) 1996, 1997 Free Software Foundation, Inc. +# Franc,ois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing - GNU libit 0.0" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`configure.in'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`configure.in'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`configure.in'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' configure.in` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`configure.in'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequirements for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 diff --git a/mkinstalldirs b/mkinstalldirs new file mode 100755 index 00000000..6b3b5fc5 --- /dev/null +++ b/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +# $Id$ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here From f85457c49f44bcb6f7382383f24d64b8d7677d34 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 19 Jun 2002 08:23:08 +0000 Subject: [PATCH 177/807] moved auxillary automake files into config directory. --- install-sh => config/install-sh | 0 missing => config/missing | 0 mkinstalldirs => config/mkinstalldirs | 0 configure.in | 3 ++- 4 files changed, 2 insertions(+), 1 deletion(-) rename install-sh => config/install-sh (100%) rename missing => config/missing (100%) rename mkinstalldirs => config/mkinstalldirs (100%) diff --git a/install-sh b/config/install-sh similarity index 100% rename from install-sh rename to config/install-sh diff --git a/missing b/config/missing similarity index 100% rename from missing rename to config/missing diff --git a/mkinstalldirs b/config/mkinstalldirs similarity index 100% rename from mkinstalldirs rename to config/mkinstalldirs diff --git a/configure.in b/configure.in index cc2ca4fc..9c5dc38f 100644 --- a/configure.in +++ b/configure.in @@ -1,8 +1,9 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(base/IInterface.h) -AM_INIT_AUTOMAKE(synergy, 0.5) AM_CONFIG_HEADER(config.h) +AC_CONFIG_AUX_DIR(config) +AM_INIT_AUTOMAKE(synergy, 0.5) dnl information on the package From 9c7e863d77da76b987e71cf99988f1d091e6c4f4 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 19 Jun 2002 11:23:49 +0000 Subject: [PATCH 178/807] checkpoint. more conversion to automake. --- base/BasicTypes.h | 116 +++++++++++++++------------- base/CLog.cpp | 19 +++-- base/CLog.h | 6 +- base/CStopwatch.cpp | 42 ++++++---- base/XBase.cpp | 2 +- base/common.h | 79 ++++++++++--------- base/stdistream.h | 6 +- base/stdostream.h | 4 +- client/CClient.cpp | 8 +- client/CXWindowsSecondaryScreen.cpp | 20 +++-- client/client.cpp | 16 ++-- configure.in | 20 +++-- mt/CCondVar.cpp | 10 +-- mt/CCondVar.h | 2 +- mt/CMutex.cpp | 8 +- mt/CThread.cpp | 2 +- mt/CThread.h | 2 +- mt/CThreadRep.cpp | 84 ++++++++++---------- mt/CThreadRep.h | 23 +++--- net/CNetwork.cpp | 4 +- net/CNetwork.h | 38 ++++----- platform/CPlatform.cpp | 8 +- platform/CPlatform.h | 8 +- platform/CXWindowsClipboard.h | 6 +- platform/CXWindowsScreen.h | 6 +- platform/CXWindowsUtil.h | 6 +- server/CServer.cpp | 8 +- server/CSynergyHook.h | 9 ++- server/CXWindowsPrimaryScreen.cpp | 12 ++- server/server.cpp | 22 +++--- synergy/CProtocolUtil.h | 2 +- 31 files changed, 328 insertions(+), 270 deletions(-) diff --git a/base/BasicTypes.h b/base/BasicTypes.h index f71d15b4..0a437caa 100644 --- a/base/BasicTypes.h +++ b/base/BasicTypes.h @@ -3,64 +3,70 @@ #include "common.h" -#if defined(CONFIG_PLATFORM_LINUX) +// +// pick types of particular sizes +// -#include - -typedef int8_t SInt8; -typedef int16_t SInt16; -typedef int32_t SInt32; -typedef int64_t SInt64; - -typedef uint8_t UInt8; -typedef uint16_t UInt16; -typedef uint32_t UInt32; -typedef uint64_t UInt64; - -#endif // CONFIG_PLATFORM_LINUX - -#if defined(CONFIG_PLATFORM_SOLARIS) - -#include - -typedef int8_t SInt8; -typedef int16_t SInt16; -typedef int32_t SInt32; -typedef int64_t SInt64; - -typedef uint8_t UInt8; -typedef uint16_t UInt16; -typedef uint32_t UInt32; -typedef uint64_t UInt64; - -#endif // CONFIG_PLATFORM_SOLARIS - -#if defined(CONFIG_PLATFORM_WIN32) - -// use VC++ extensions if available -#if defined(_MSC_VER) -typedef signed __int8 SInt8; -typedef signed __int16 SInt16; -typedef signed __int32 SInt32; -typedef signed __int64 SInt64; - -typedef unsigned __int8 UInt8; -typedef unsigned __int16 UInt16; -typedef unsigned __int32 UInt32; -typedef unsigned __int64 UInt64; -#else -typedef signed char SInt8; -typedef short SInt16; -typedef int SInt32; -typedef long long SInt64; - -typedef unsigned char UInt8; -typedef unsigned short UInt16; -typedef unsigned int UInt32; -typedef unsigned long long UInt64; +#if !defined(TYPE_OF_SIZE_1) +# if SIZEOF_CHAR == 1 +# define TYPE_OF_SIZE_1 char +# endif #endif -#endif // CONFIG_PLATFORM_WIN32 +#if !defined(TYPE_OF_SIZE_2) +# if SIZEOF_INT == 2 +# define TYPE_OF_SIZE_2 int +# else +# define TYPE_OF_SIZE_2 short +# endif +#endif + +#if !defined(TYPE_OF_SIZE_4) +# if SIZEOF_INT == 4 +# define TYPE_OF_SIZE_4 int +# else +# define TYPE_OF_SIZE_4 long +# endif +#endif + +// +// verify existence of required types +// + +#if !defined(TYPE_OF_SIZE_1) +# error No 1 byte integer type +#endif +#if !defined(TYPE_OF_SIZE_2) +# error No 2 byte integer type +#endif +#if !defined(TYPE_OF_SIZE_4) +# error No 4 byte integer type +#endif + + +// +// make typedefs +// +// except for SInt8 and UInt8 these types are only guaranteed to be +// at least as big as indicated (in bits). that is, they may be +// larger than indicated. +// + +typedef signed TYPE_OF_SIZE_1 SInt8; +typedef signed TYPE_OF_SIZE_2 SInt16; +typedef signed TYPE_OF_SIZE_4 SInt32; + +typedef unsigned TYPE_OF_SIZE_1 UInt8; +typedef unsigned TYPE_OF_SIZE_2 UInt16; +typedef unsigned TYPE_OF_SIZE_4 UInt32; + +// +// clean up +// + +#undef TYPE_OF_SIZE_1 +#undef TYPE_OF_SIZE_2 +#undef TYPE_OF_SIZE_4 #endif diff --git a/base/CLog.cpp b/base/CLog.cpp index a793699f..38bcdfd6 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -3,7 +3,7 @@ #include #include -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #define WIN32_LEAN_AND_MEAN #include #define vsnprintf _vsnprintf @@ -42,6 +42,13 @@ static const int g_prioritySuffixLength = 2; static const int g_priorityPad = g_maxPriorityLength + g_prioritySuffixLength; +// platform newline sequence +#if WINDOWS_LIKE +static const char* g_newline = "\r\n"; +#else +static const char* g_newline = "\n"; +#endif + // minimum length of a newline sequence static const int g_newlineLength = 2; @@ -226,17 +233,13 @@ CLog::output(int priority, char* msg) } // put a newline at the end -#if defined(CONFIG_PLATFORM_WIN32) - strcat(msg + g_priorityPad, "\r\n"); -#else - strcat(msg + g_priorityPad, "\n"); -#endif + strcat(msg + g_priorityPad, g_newline); // print it CHoldLock lock(s_lock); if (s_outputter == NULL || !s_outputter(priority, msg + g_maxPriorityLength - n)) { -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE openConsole(); #endif fprintf(stderr, "%s", msg + g_maxPriorityLength - n); @@ -268,7 +271,7 @@ CLog::vsprint(int pad, char* buffer, int len, const char* fmt, va_list args) return buffer; } -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE static DWORD s_thread = 0; diff --git a/base/CLog.h b/base/CLog.h index 0a790929..64fd9976 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -1,8 +1,8 @@ #ifndef CLOG_H #define CLOG_H -#include "BasicTypes.h" -#include +#include "common.h" +#include class CLog { public: @@ -67,7 +67,7 @@ private: static void output(int priority, char* msg); static char* vsprint(int pad, char*, int len, const char*, va_list); static int nprint(const char*, va_list); -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE static void openConsole(); #endif diff --git a/base/CStopwatch.cpp b/base/CStopwatch.cpp index 5cd61bc7..dbbe0ba7 100644 --- a/base/CStopwatch.cpp +++ b/base/CStopwatch.cpp @@ -110,21 +110,7 @@ CStopwatch::operator double() const return getTime(); } -#if defined(CONFIG_PLATFORM_UNIX) - -#include - -double -CStopwatch::getClock() const -{ - struct timeval t; - gettimeofday(&t, NULL); - return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; -} - -#endif // CONFIG_PLATFORM_UNIX - -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE // avoid getting a lot a crap from mmsystem.h that we don't need #define MMNODRV // Installable driver support @@ -198,4 +184,28 @@ CStopwatch::getClock() const } } -#endif // CONFIG_PLATFORM_WIN32 +#elif UNIX_LIKE + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif + +double +CStopwatch::getClock() const +{ + struct timeval t; + gettimeofday(&t, NULL); + return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; +} + +#endif // UNIX_LIKE diff --git a/base/XBase.cpp b/base/XBase.cpp index 575ca4f6..fba37293 100644 --- a/base/XBase.cpp +++ b/base/XBase.cpp @@ -2,7 +2,7 @@ #include // win32 wants a const char* argument to std::exception c'tor -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #define STDEXCEPTARG "" #endif diff --git a/base/common.h b/base/common.h index df7d0a20..00b9fd87 100644 --- a/base/common.h +++ b/base/common.h @@ -1,53 +1,58 @@ #ifndef COMMON_H #define COMMON_H -#if defined(__linux__) - -#define CONFIG_PLATFORM_LINUX -#define CONFIG_PLATFORM_UNIX -#define CONFIG_TYPES_X11 -#define CONFIG_PTHREADS - -#elif defined(__sun__) - -#define CONFIG_PLATFORM_SOLARIS -#define CONFIG_PLATFORM_UNIX -#define CONFIG_TYPES_X11 -#define CONFIG_PTHREADS - -#elif defined(_WIN32) - -#define CONFIG_PLATFORM_WIN32 - -#if (_MSC_VER >= 1200) -// work around for statement scoping bug -#define for if (false) { } else for - -// turn off bonehead warnings -#pragma warning(disable: 4786) // identifier truncated in debug info -#pragma warning(disable: 4514) // unreferenced inline function removed - -// this one's a little too aggressive -#pragma warning(disable: 4127) // conditional expression is constant - -// emitted incorrectly under release build in some circumstances -#if defined(NDEBUG) -#pragma warning(disable: 4702) // unreachable code -#pragma warning(disable: 4701) // local variable maybe used uninitialized +#if HAVE_CONFIG_H +# include "config.h" #endif -#endif // (_MSC_VER >= 1200) +// check if win32 platform +#if defined(_WIN32) || HAVE_WINDOWS_H +# define WINDOWS_LIKE 1 -#else + // VC++ specific +# if (_MSC_VER >= 1200) + // work around for statement scoping bug +# define for if (false) { } else for -#error unsupported platform + // turn off bonehead warnings +# pragma warning(disable: 4786) // identifier truncated in debug info +# pragma warning(disable: 4514) // unreferenced inline function removed + // this one's a little too aggressive +# pragma warning(disable: 4127) // conditional expression is constant + + // emitted incorrectly under release build in some circumstances +# if defined(NDEBUG) +# pragma warning(disable: 4702) // unreachable code +# pragma warning(disable: 4701) // variable maybe used uninitialized +# endif + +# endif // (_MSC_VER >= 1200) + + // VC++ has built-in sized types +# if defined(_MSC_VER) +# define TYPE_OF_SIZE_1 __int8 +# define TYPE_OF_SIZE_2 __int16 +# define TYPE_OF_SIZE_4 __int32 +# else +# define SIZE_OF_CHAR 1 +# define SIZE_OF_SHORT 2 +# define SIZE_OF_INT 4 +# define SIZE_OF_LONG 4 +# endif +#endif // defined(_WIN32) || HAVE_WINDOWS_H + +// unix-like if not like anything else +#if (!defined(WINDOWS_LIKE) || WINDOWS_LIKE == 0) +# define UNIX_LIKE 1 #endif +// define NULL #ifndef NULL #define NULL 0 #endif -#include +// make assert available since we use it a lot +#include #endif diff --git a/base/stdistream.h b/base/stdistream.h index 440a33fe..6f83b5bb 100644 --- a/base/stdistream.h +++ b/base/stdistream.h @@ -1,14 +1,12 @@ #include "stdpre.h" -#if !defined(CONFIG_PLATFORM_LINUX) +#if defined(HAVE_ISTREAM) #include #else -// some versions of libstdc++ don't have -// FIXME -- only include iostream for versions that don't have istream #include #endif #include "stdpost.h" -#if defined(CONFIG_PLATFORM_WIN32) && defined(_MSC_VER) +#if defined(_MSC_VER) // istream has no overloads for __int* types inline std::istream& operator>>(std::istream& s, SInt8& i) diff --git a/base/stdostream.h b/base/stdostream.h index fd434281..b1c37914 100644 --- a/base/stdostream.h +++ b/base/stdostream.h @@ -1,9 +1,7 @@ #include "stdpre.h" -#if !defined(CONFIG_PLATFORM_LINUX) +#if defined(HAVE_OSTREAM) #include #else -// some versions of libstdc++ don't have -// FIXME -- only include iostream for versions that don't have ostream #include #endif #include "stdpost.h" diff --git a/client/CClient.cpp b/client/CClient.cpp index 0ec793fe..5e452909 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -402,9 +402,9 @@ CClient::runSession(void*) } // FIXME -- use factory to create screen -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #include "CMSWindowsSecondaryScreen.h" -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE #include "CXWindowsSecondaryScreen.h" #endif void @@ -426,9 +426,9 @@ CClient::openSecondaryScreen() // open screen log((CLOG_DEBUG1 "creating secondary screen")); -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE m_screen = new CMSWindowsSecondaryScreen; -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE m_screen = new CXWindowsSecondaryScreen; #endif log((CLOG_DEBUG1 "opening secondary screen")); diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 3f9e754d..90b14a0a 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -4,12 +4,20 @@ #include "CXWindowsUtil.h" #include "CThread.h" #include "CLog.h" -#include -#include -#define XK_MISCELLANY -#define XK_XKB_KEYS -#include -#include +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +# define XK_MISCELLANY +# define XK_XKB_KEYS +# include +# if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# include +# else +# error The XTest extension is required to build synergy +# endif +#endif // // CXWindowsSecondaryScreen diff --git a/client/client.cpp b/client/client.cpp index 3b1418b8..9d4a13de 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -15,9 +15,9 @@ #include // platform dependent name of a daemon -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #define DAEMON_NAME "Synergy Client" -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE #define DAEMON_NAME "synergy" #endif @@ -176,7 +176,7 @@ static void help() { -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE # define PLATFORM_ARGS \ " [--install]" \ @@ -326,7 +326,7 @@ parse(int argc, const char** argv) bye(0); } -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE else if (isArg(i, argc, argv, NULL, "--install")) { s_install = true; if (s_uninstall) { @@ -338,7 +338,7 @@ parse(int argc, const char** argv) } #endif -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE else if (isArg(i, argc, argv, NULL, "--uninstall")) { s_uninstall = true; if (s_install) { @@ -404,7 +404,7 @@ parse(int argc, const char** argv) // increase default filter level for daemon. the user must // explicitly request another level for a daemon. if (s_daemon && s_logFilter == NULL) { -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE if (CPlatform::isWindows95Family()) { // windows 95 has no place for logging so avoid showing // the log console window. @@ -430,7 +430,7 @@ parse(int argc, const char** argv) // platform dependent entry points // -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #include "CMSWindowsScreen.h" @@ -605,7 +605,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) return result; } -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE static int diff --git a/configure.in b/configure.in index 9c5dc38f..2fae47f5 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(base/IInterface.h) +AC_INIT(base/common.h) AM_CONFIG_HEADER(config.h) AC_CONFIG_AUX_DIR(config) AM_INIT_AUTOMAKE(synergy, 0.5) @@ -17,13 +17,13 @@ ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) dnl checks for header files AC_HEADER_STDC +AC_HEADER_TIME +AC_CHECK_HEADERS([unistd.h sys/time.h]) +AC_CHECK_HEADERS([istream ostream]) +AC_CHECK_HEADERS([windows.h]) AC_PATH_X -if test "$no_x" = yes; then - AC_MSG_ERROR(You must have X Windows to compile synergy) -fi AC_PATH_XTRA -AC_CHECK_HEADERS(sys/time.h) -dnl AC_HEADER_TIME +AC_CHECK_HEADERS([X11/extensions/XTest.h]) dnl checks for types dnl AC_TYPE_SIZE_T @@ -32,7 +32,13 @@ dnl checks for structures AC_STRUCT_TM dnl checks for compiler characteristics - +AC_CHECK_SIZEOF(char, 1) +AC_CHECK_SIZEOF(short, 2) +AC_CHECK_SIZEOF(int, 2) +AC_CHECK_SIZEOF(long, 4) +dnl should check for bool +dnl should require template support +dnl should require exception support dnl checks for library functions dnl AC_TYPE_SIGNAL diff --git a/mt/CCondVar.cpp b/mt/CCondVar.cpp index 397388df..76035aab 100644 --- a/mt/CCondVar.cpp +++ b/mt/CCondVar.cpp @@ -7,7 +7,7 @@ CCondVarBase::CCondVarBase(CMutex* mutex) : m_mutex(mutex) -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE , m_waitCountMutex() #endif { @@ -45,7 +45,7 @@ CCondVarBase::getMutex() const return m_mutex; } -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD #include "CThread.h" #include @@ -169,9 +169,9 @@ CCondVarBase::wait(CStopwatch& timer, double timeout) const } } -#endif // CONFIG_PTHREADS +#endif // HAVE_PTHREAD -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #include "CLock.h" #include "CThreadRep.h" @@ -312,4 +312,4 @@ CCondVarBase::wait(CStopwatch& timer, double timeout) const result == WAIT_OBJECT_0 + 1); } -#endif // CONFIG_PLATFORM_WIN32 +#endif // WINDOWS_LIKE diff --git a/mt/CCondVar.h b/mt/CCondVar.h index cf0f824b..1cf9f491 100644 --- a/mt/CCondVar.h +++ b/mt/CCondVar.h @@ -56,7 +56,7 @@ private: CMutex* m_mutex; void* m_cond; -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE enum { kSignal, kBroadcast }; mutable UInt32 m_waitCount; CMutex m_waitCountMutex; diff --git a/mt/CMutex.cpp b/mt/CMutex.cpp index dfae9faf..cabb0f69 100644 --- a/mt/CMutex.cpp +++ b/mt/CMutex.cpp @@ -26,7 +26,7 @@ CMutex::operator=(const CMutex&) return *this; } -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD #include #include @@ -98,9 +98,9 @@ CMutex::unlock() const } } -#endif // CONFIG_PTHREADS +#endif // HAVE_PTHREAD -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #define WIN32_LEAN_AND_MEAN #include @@ -133,4 +133,4 @@ CMutex::unlock() const LeaveCriticalSection(reinterpret_cast(m_mutex)); } -#endif // CONFIG_PLATFORM_WIN32 +#endif // WINDOWS_LIKE diff --git a/mt/CThread.cpp b/mt/CThread.cpp index c10c0636..80637a23 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -99,7 +99,7 @@ CThread::wait(double timeout) const return currentRep->wait(m_rep, timeout); } -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE bool CThread::waitForEvent(double timeout) { diff --git a/mt/CThread.h b/mt/CThread.h index 81c8aef8..264bd2ba 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -105,7 +105,7 @@ public: // (cancellation point) bool wait(double timeout = -1.0) const; -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE // wait for a message in the queue. returns true if a message // is available. // (cancellation point) diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index cf2d4111..ad2bedca 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -6,40 +6,34 @@ #include "CLog.h" #include "IJob.h" -#if defined(CONFIG_PTHREADS) -#include -#define SIGWAKEUP SIGUSR1 -#endif +#if HAVE_PTHREAD + +# include +# define SIGWAKEUP SIGUSR1 + +#elif WINDOWS_LIKE + +# if !defined(_MT) +# error multithreading compile option is required +# endif +# include + +#else + +#error unsupported platform for multithreading -#if defined(CONFIG_PLATFORM_WIN32) -# if !defined(_MT) -# error multithreading compile option is required -# endif -#include #endif // FIXME -- temporary exception type class XThreadUnavailable { }; -#if defined(CONFIG_PLATFORM_UNIX) && !defined(NDEBUG) -#include -#include -#include -#include -static void threadDebug(int) -{ - if (fork() == 0) abort(); - else { wait(0); exit(1); } -} -#endif - // // CThreadRep // CMutex* CThreadRep::s_mutex = NULL; CThreadRep* CThreadRep::s_head = NULL; -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD pthread_t CThreadRep::s_signalThread; #endif @@ -55,10 +49,10 @@ CThreadRep::CThreadRep() : // initialize stuff init(); -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD // get main thread id m_thread = pthread_self(); -#elif defined(CONFIG_PLATFORM_WIN32) +#elif WINDOWS_LIKE // get main thread id m_thread = NULL; m_id = GetCurrentThreadId(); @@ -94,7 +88,7 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : CLock lock(s_mutex); // start the thread. throw if it doesn't start. -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD // mask some signals in all threads except the main thread sigset_t sigset, oldsigset; sigemptyset(&sigset); @@ -106,7 +100,7 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : if (status != 0) { throw XThreadUnavailable(); } -#elif defined(CONFIG_PLATFORM_WIN32) +#elif WINDOWS_LIKE unsigned int id; m_thread = reinterpret_cast(_beginthreadex(NULL, 0, threadFunc, (void*)this, 0, &id)); @@ -151,7 +145,7 @@ CThreadRep::initThreads() if (s_mutex == NULL) { s_mutex = new CMutex; -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD // install SIGWAKEUP handler struct sigaction act; sigemptyset(&act.sa_mask); @@ -162,18 +156,11 @@ CThreadRep::initThreads() # endif act.sa_handler = &threadCancel; sigaction(SIGWAKEUP, &act, NULL); -# ifndef NDEBUG - act.sa_handler = &threadDebug; - sigaction(SIGSEGV, &act, NULL); -# endif // set signal mask sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGWAKEUP); -# ifndef NDEBUG - sigaddset(&sigset, SIGSEGV); -# endif pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); sigemptyset(&sigset); sigaddset(&sigset, SIGPIPE); @@ -196,7 +183,7 @@ CThreadRep::initThreads() sigaddset(&sigset, SIGTERM); pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); } -#endif +#endif // HAVE_PTHREAD } } @@ -251,9 +238,9 @@ CThreadRep::getCurrentThreadRep() { assert(s_mutex != NULL); -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD const pthread_t thread = pthread_self(); -#elif defined(CONFIG_PLATFORM_WIN32) +#elif WINDOWS_LIKE const DWORD id = GetCurrentThreadId(); #endif @@ -263,11 +250,11 @@ CThreadRep::getCurrentThreadRep() // search CThreadRep* scan = s_head; while (scan != NULL) { -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD if (scan->m_thread == thread) { break; } -#elif defined(CONFIG_PLATFORM_WIN32) +#elif WINDOWS_LIKE if (scan->m_id == id) { break; } @@ -326,10 +313,19 @@ CThreadRep::doThreadFunc() m_result = result; } -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD #include "CStopwatch.h" -#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif void CThreadRep::init() @@ -492,7 +488,9 @@ CThreadRep::threadSignalHandler(void* vrep) } } -#elif defined(CONFIG_PLATFORM_WIN32) +#endif // HAVE_PTHREAD + +#if WINDOWS_LIKE void CThreadRep::init() @@ -713,4 +711,4 @@ CThreadRep::threadFunc(void* arg) return 0; } -#endif +#endif // WINDOWS_LIKE diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h index 448d2b4d..8869492c 100644 --- a/mt/CThreadRep.h +++ b/mt/CThreadRep.h @@ -1,11 +1,11 @@ #ifndef CTHREADREP_H #define CTHREADREP_H -#include "BasicTypes.h" +#include "common.h" -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD #include -#elif defined(CONFIG_PLATFORM_WIN32) +#elif WINDOWS_LIKE #define WIN32_LEAN_AND_MEAN #include #endif @@ -43,7 +43,7 @@ public: // wait for thread to exit or for current thread to cancel bool wait(CThreadRep*, double timeout); -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE // wait for a message on the queue bool waitForEvent(double timeout); #endif @@ -62,9 +62,9 @@ public: // get the current cancellable state bool isCancellable() const; -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD bool isExited() const; -#elif defined(CONFIG_PLATFORM_WIN32) +#elif WINDOWS_LIKE HANDLE getExitEvent() const; HANDLE getCancelEvent() const; #endif @@ -88,11 +88,11 @@ private: static CThreadRep* find(); // thread functions -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD static void* threadFunc(void* arg); static void threadCancel(int); static void* threadSignalHandler(void*); -#elif defined(CONFIG_PLATFORM_WIN32) +#elif WINDOWS_LIKE static unsigned int __stdcall threadFunc(void* arg); #endif void doThreadFunc(); @@ -108,22 +108,21 @@ private: CThreadRep* m_prev; CThreadRep* m_next; - SInt32 m_refCount; + int m_refCount; IJob* m_job; void* m_userData; void* m_result; bool m_cancellable; bool m_cancelling; - UInt32 m_signals; -#if defined(CONFIG_PTHREADS) +#if HAVE_PTHREAD pthread_t m_thread; bool m_exit; bool m_cancel; static pthread_t s_signalThread; #endif -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE HANDLE m_thread; DWORD m_id; HANDLE m_exit; diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp index 29b5418a..2122a74d 100644 --- a/net/CNetwork.cpp +++ b/net/CNetwork.cpp @@ -37,7 +37,7 @@ struct protoent FAR * (PASCAL FAR *CNetwork::getprotobyname)(const char FAR * na int (PASCAL FAR *CNetwork::getsockerror)(void); int (PASCAL FAR *CNetwork::gethosterror)(void); -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE int (PASCAL FAR *CNetwork::select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); int (PASCAL FAR *CNetwork::WSACleanup)(void); @@ -297,7 +297,7 @@ CNetwork::write2(Socket s, const void FAR* buf, size_t len) #endif -#if defined(CONFIG_PLATFORM_UNIX) +#if UNIX_LIKE #include #include diff --git a/net/CNetwork.h b/net/CNetwork.h index c66d8164..cf090ea1 100644 --- a/net/CNetwork.h +++ b/net/CNetwork.h @@ -3,21 +3,23 @@ #include "BasicTypes.h" -#if defined(CONFIG_PLATFORM_WIN32) -// declare no functions in winsock2 -# define INCL_WINSOCK_API_PROTOTYPES 0 -# define INCL_WINSOCK_API_TYPEDEFS 0 -# include +#if WINDOWS_LIKE + // declare no functions in winsock2 +# define INCL_WINSOCK_API_PROTOTYPES 0 +# define INCL_WINSOCK_API_TYPEDEFS 0 +# include typedef int ssize_t; -# if !defined(SOL_TCP) -# define SOL_TCP IPPROTO_TCP -# endif +# if !defined(SOL_TCP) +# define SOL_TCP IPPROTO_TCP +# endif #else -# define FAR -# define PASCAL +# undef FAR +# undef PASCAL +# define FAR +# define PASCAL #endif -#if defined(CONFIG_PLATFORM_UNIX) +#if UNIX_LIKE # include # include # include @@ -31,7 +33,7 @@ typedef int ssize_t; class CNetwork { public: -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE typedef SOCKET Socket; typedef struct sockaddr Address; typedef int AddressLength; @@ -48,7 +50,7 @@ public: kPOLLERR = 4, kPOLLNVAL = 8 }; -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE typedef int Socket; typedef struct sockaddr Address; typedef socklen_t AddressLength; @@ -80,9 +82,9 @@ public: // getsockerror() constants enum { -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE kEADDRINUSE = WSAEADDRINUSE, -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE kEADDRINUSE = EADDRINUSE, #endif kNone = 0 @@ -90,12 +92,12 @@ public: // gethosterror() constants enum { -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE kHOST_NOT_FOUND = WSAHOST_NOT_FOUND, kNO_DATA = WSANO_DATA, kNO_RECOVERY = WSANO_RECOVERY, kTRY_AGAIN = WSATRY_AGAIN, -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE kHOST_NOT_FOUND = HOST_NOT_FOUND, kNO_DATA = NO_DATA, kNO_RECOVERY = NO_RECOVERY, @@ -137,7 +139,7 @@ public: static int (PASCAL FAR *getsockerror)(void); static int (PASCAL FAR *gethosterror)(void); -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE private: static void init2(HMODULE); static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout); diff --git a/platform/CPlatform.cpp b/platform/CPlatform.cpp index 79d7a047..c579d216 100644 --- a/platform/CPlatform.cpp +++ b/platform/CPlatform.cpp @@ -1,11 +1,15 @@ #include "common.h" -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #include "CWin32Platform.cpp" -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE #include "CUnixPlatform.cpp" +#else + +#error Unsupported platform + #endif diff --git a/platform/CPlatform.h b/platform/CPlatform.h index 76d2dea6..3ec1db2f 100644 --- a/platform/CPlatform.h +++ b/platform/CPlatform.h @@ -3,16 +3,20 @@ #include "common.h" -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #include "CWin32Platform.h" typedef CWin32Platform CPlatform; -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE #include "CUnixPlatform.h" typedef CUnixPlatform CPlatform; +#else + +#error Unsupported platform + #endif #endif diff --git a/platform/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h index f1d04580..92657adc 100644 --- a/platform/CXWindowsClipboard.h +++ b/platform/CXWindowsClipboard.h @@ -5,7 +5,11 @@ #include "ClipboardTypes.h" #include "stdmap.h" #include "stdlist.h" -#include +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif class CXWindowsClipboard : public IClipboard { public: diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index 404d77b8..b589aba6 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -3,7 +3,11 @@ #include "ClipboardTypes.h" #include "CMutex.h" -#include +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif class IClipboard; class CXWindowsClipboard; diff --git a/platform/CXWindowsUtil.h b/platform/CXWindowsUtil.h index 3f01b667..9e8add53 100644 --- a/platform/CXWindowsUtil.h +++ b/platform/CXWindowsUtil.h @@ -3,7 +3,11 @@ #include "CString.h" #include "BasicTypes.h" -#include +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif class CXWindowsUtil { public: diff --git a/server/CServer.cpp b/server/CServer.cpp index d4de4240..91504f52 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -1353,9 +1353,9 @@ CServer::updatePrimaryClipboard(ClipboardID id) } // FIXME -- use factory to create screen -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #include "CMSWindowsPrimaryScreen.h" -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE #include "CXWindowsPrimaryScreen.h" #endif void @@ -1377,9 +1377,9 @@ CServer::openPrimaryScreen() // open screen log((CLOG_DEBUG1 "creating primary screen")); -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE m_primary = new CMSWindowsPrimaryScreen; -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE m_primary = new CXWindowsPrimaryScreen; #endif log((CLOG_DEBUG1 "opening primary screen")); diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index 2d38d708..159144c9 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -2,18 +2,19 @@ #define CSYNERGYHOOK_H #include "BasicTypes.h" + +#if WINDOWS_LIKE #define WIN32_LEAN_AND_MEAN #include +#else +#error CSynergyHook is a win32 specific file +#endif -#if defined(CONFIG_PLATFORM_WIN32) #if defined(SYNRGYHK_EXPORTS) #define CSYNERGYHOOK_API __declspec(dllexport) #else #define CSYNERGYHOOK_API __declspec(dllimport) #endif -#else -#define CSYNERGYHOOK_API -#endif #define SYNERGY_MSG_MARK WM_APP + 0x0011 // mark id; #define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 75e3b467..c8ba3a68 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -5,10 +5,14 @@ #include "CThread.h" #include "CLog.h" #include "CStopwatch.h" -#include -#include -#define XK_MISCELLANY -#include +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +# define XK_MISCELLANY +# include +#endif // // CXWindowsPrimaryScreen diff --git a/server/server.cpp b/server/server.cpp index dbbf7e0f..18f55ec0 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -14,16 +14,16 @@ #include // platform dependent name of a daemon -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #define DAEMON_NAME "Synergy Server" -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE #define DAEMON_NAME "synergyd" #endif // configuration file name -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #define CONFIG_NAME "synergy.sgc" -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE #define CONFIG_NAME "synergy.conf" #endif @@ -34,7 +34,7 @@ static const char* pname = NULL; static bool s_restartable = true; static bool s_daemon = true; -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE static bool s_install = false; static bool s_uninstall = false; #endif @@ -209,7 +209,7 @@ static void help() { -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE # define PLATFORM_ARGS \ " {--daemon|--no-daemon}" \ @@ -398,7 +398,7 @@ parse(int argc, const char** argv) bye(0); } -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE else if (isArg(i, argc, argv, NULL, "--install")) { s_install = true; if (s_uninstall) { @@ -410,7 +410,7 @@ parse(int argc, const char** argv) } #endif -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE else if (isArg(i, argc, argv, NULL, "--uninstall")) { s_uninstall = true; if (s_install) { @@ -450,7 +450,7 @@ parse(int argc, const char** argv) // increase default filter level for daemon. the user must // explicitly request another level for a daemon. if (s_daemon && s_logFilter == NULL) { -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE if (CPlatform::isWindows95Family()) { // windows 95 has no place for logging so avoid showing // the log console window. @@ -541,7 +541,7 @@ loadConfig() // platform dependent entry points // -#if defined(CONFIG_PLATFORM_WIN32) +#if WINDOWS_LIKE #include "CMSWindowsScreen.h" @@ -726,7 +726,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) return result; } -#elif defined(CONFIG_PLATFORM_UNIX) +#elif UNIX_LIKE static int diff --git a/synergy/CProtocolUtil.h b/synergy/CProtocolUtil.h index d6d1bce7..9c633b81 100644 --- a/synergy/CProtocolUtil.h +++ b/synergy/CProtocolUtil.h @@ -3,7 +3,7 @@ #include "BasicTypes.h" #include "XIO.h" -#include +#include class IInputStream; class IOutputStream; From 8a103ce63c418dc5ec523e5420bc368ccf5400a4 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 19 Jun 2002 11:58:48 +0000 Subject: [PATCH 179/807] checkpoint. automake changes for reentrant functions. --- configure.in | 2 ++ http/CHTTPProtocol.cpp | 12 ++++++++---- platform/CUnixPlatform.cpp | 16 ++++++++++++---- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/configure.in b/configure.in index 2fae47f5..3b2cddba 100644 --- a/configure.in +++ b/configure.in @@ -45,6 +45,8 @@ dnl AC_TYPE_SIGNAL dnl AC_FUNC_FORK AC_FUNC_MEMCMP AC_FUNC_STRFTIME +AC_CHECK_FUNCS(gmtime_r) +AC_CHECK_FUNCS(getpwuid_r) dnl use AC_REPLACE_FUNCS() for stuff in string.h dnl AC_HEADER_SYS_WAIT diff --git a/http/CHTTPProtocol.cpp b/http/CHTTPProtocol.cpp index a03011f1..e49bdd00 100644 --- a/http/CHTTPProtocol.cpp +++ b/http/CHTTPProtocol.cpp @@ -273,14 +273,18 @@ CHTTPProtocol::reply(IOutputStream* stream, CHTTPReply& reply) // get date // FIXME -- should use C++ locale stuff but VC++ time_put is broken. // FIXME -- double check that VC++ is broken - // FIXME -- should mutex gmtime() since the return value may not - // be thread safe char date[30]; { const char* oldLocale = setlocale(LC_TIME, "C"); time_t t = time(NULL); - struct tm* tm = gmtime(&t); - strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", tm); +#if HAVE_GMTIME_R + struct tm tm; + struct tm* tmp = &tm; + gmtime_r(&t, tmp); +#else + struct tm* tmp = gmtime(&t); +#endif + strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", tmp); setlocale(LC_TIME, oldLocale); } diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index 26110dd0..484bfb3e 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -161,10 +161,18 @@ CUnixPlatform::getBasename(const char* pathname) const CString CUnixPlatform::getUserDirectory() const { - // FIXME -- use geteuid? shouldn't run this setuid anyway. - struct passwd* pwent = getpwuid(getuid()); - if (pwent != NULL && pwent->pw_dir != NULL) { - return pwent->pw_dir; +#if HAVE_GETPWUID_R + struct passwd pwent; + struct passwd* pwentp; + long size = sysconf(_SC_GETPW_R_SIZE_MAX); + char* buffer = new char[size]; + getpwuid_r(getuid(), &pwent, buffer, size, &pwentp); + delete[] buffer; +#else + struct passwd* pwentp = getpwuid(getuid()); +#endif + if (pwentp != NULL && pwentp->pw_dir != NULL) { + return pwentp->pw_dir; } else { return CString(); From c4f1dbdae415f8b66dd1cc13359c9370d9c5833b Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 19 Jun 2002 12:21:26 +0000 Subject: [PATCH 180/807] checkpoint. automake changes for wait(). --- configure.in | 4 ++-- platform/CUnixPlatform.cpp | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/configure.in b/configure.in index 3b2cddba..9be8fbc4 100644 --- a/configure.in +++ b/configure.in @@ -17,10 +17,11 @@ ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) dnl checks for header files AC_HEADER_STDC -AC_HEADER_TIME AC_CHECK_HEADERS([unistd.h sys/time.h]) AC_CHECK_HEADERS([istream ostream]) AC_CHECK_HEADERS([windows.h]) +AC_HEADER_SYS_WAIT +AC_HEADER_TIME AC_PATH_X AC_PATH_XTRA AC_CHECK_HEADERS([X11/extensions/XTest.h]) @@ -48,7 +49,6 @@ AC_FUNC_STRFTIME AC_CHECK_FUNCS(gmtime_r) AC_CHECK_FUNCS(getpwuid_r) dnl use AC_REPLACE_FUNCS() for stuff in string.h -dnl AC_HEADER_SYS_WAIT dnl checks for system services diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index 484bfb3e..21f61710 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -5,10 +5,25 @@ #include #include #include -#include #include #include #include +#if HAVE_SYS_WAIT_H +# include +#endif +#if !defined(WIFSIGNALED) +# define WIFSIGNALED(w) (((w) & 0xff) != 0x7f && ((w) & 0xff) != 0) +#endif +#if !defined(WIFEXITED) +# define WIFEXITED(w) (((w) & 0xff) == 0) +#endif +#if !defined(WTERMSIG) +# define WTERMSIG(w) ((w) & 0x7f) +#endif +#if !defined(WEXITSTATUS) +# define WEXITSTATUS(w) (((w) >> 8) & 0xff) +#endif + // // CUnixPlatform From 29c90a3b6c9ce7591a58007e12a05ef8c371e86c Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 19 Jun 2002 14:45:22 +0000 Subject: [PATCH 181/807] fixed addition of X11 -L and -l options on link lines. --- client/Makefile.am | 7 +++++-- configure.in | 1 - server/Makefile.am | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/client/Makefile.am b/client/Makefile.am index fe5ae6ec..b747856b 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -15,9 +15,12 @@ synergy_LDADD = \ $(DEPTH)/io/libio.a \ $(DEPTH)/mt/libmt.a \ $(DEPTH)/base/libbase.a \ - -lX11 \ - -lXext \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ -I$(DEPTH)/base \ diff --git a/configure.in b/configure.in index 9be8fbc4..5c43b047 100644 --- a/configure.in +++ b/configure.in @@ -54,7 +54,6 @@ dnl checks for system services dnl adjust variables for X11 and pthreads -LIBS="$X_PRE_LIBS $LIBS $X_EXTRA_LIBS $X_LIBS $PTHREAD_LIBS" CFLAGS="$CFLAGS $X_CFLAGS $PTHREAD_CFLAGS" CXXFLAGS="$CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" CC="$PTHREAD_CC" diff --git a/server/Makefile.am b/server/Makefile.am index 1ed7866a..c8abb6a3 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -20,9 +20,10 @@ synergyd_LDADD = \ $(DEPTH)/io/libio.a \ $(DEPTH)/mt/libmt.a \ $(DEPTH)/base/libbase.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ -lX11 \ - -lXext \ - -lXtst \ + $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ -I$(DEPTH)/base \ From bebb63ac535dfbc9ed2be613e407aac30d3d7d19 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 19 Jun 2002 17:03:29 +0000 Subject: [PATCH 182/807] checkpoint. initial support for multiple displays on win32. --- client/CClient.cpp | 10 +- client/CMSWindowsSecondaryScreen.cpp | 36 +++-- client/CMSWindowsSecondaryScreen.h | 4 +- client/CXWindowsSecondaryScreen.cpp | 11 +- client/CXWindowsSecondaryScreen.h | 4 +- platform/CMSWindowsScreen.cpp | 37 +++-- platform/CMSWindowsScreen.h | 6 +- platform/CXWindowsScreen.cpp | 17 ++- platform/CXWindowsScreen.h | 6 +- server/CMSWindowsPrimaryScreen.cpp | 55 +++---- server/CMSWindowsPrimaryScreen.h | 2 +- server/CServer.cpp | 216 ++++++++++++++------------- server/CServer.h | 22 +-- server/CServerProtocol1_0.cpp | 10 +- server/CSynergyHook.cpp | 20 ++- server/CSynergyHook.h | 6 +- server/CXWindowsPrimaryScreen.cpp | 36 ++--- server/CXWindowsPrimaryScreen.h | 2 +- synergy/IPrimaryScreen.h | 5 +- synergy/ISecondaryScreen.h | 5 +- synergy/ProtocolTypes.h | 15 +- 21 files changed, 289 insertions(+), 236 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 5e452909..1206176d 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -557,13 +557,13 @@ CClient::onQueryInfo() void CClient::onQueryInfoNoLock() { - SInt32 x, y, w, h; - m_screen->getMousePos(&x, &y); - m_screen->getSize(&w, &h); + SInt32 mx, my, x, y, w, h; + m_screen->getMousePos(mx, my); + m_screen->getShape(x, y, w, h); SInt32 zoneSize = m_screen->getJumpZoneSize(); - log((CLOG_DEBUG1 "sending info size=%d,%d zone=%d pos=%d,%d", w, h, zoneSize, x, y)); - CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize, x, y); + log((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", x, y, w, h, zoneSize, mx, my)); + CProtocolUtil::writef(m_output, kMsgDInfo, x, y, w, h, zoneSize, mx, my); } void diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 9e7c14ef..dec2e2f9 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -339,11 +339,11 @@ CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) assert(m_window != NULL); syncDesktop(); - SInt32 w, h; - getScreenSize(&w, &h); + SInt32 x0, y0, w, h; + getScreenShape(x0, y0, w, h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (SInt32)(65535.99 * x / (w - 1)), - (SInt32)(65535.99 * y / (h - 1)), + (SInt32)(65535.99 * x / (w - 1)) + x0, + (SInt32)(65535.99 * y / (h - 1)) + y0, 0, 0); } @@ -381,30 +381,28 @@ CMSWindowsSecondaryScreen::grabClipboard(ClipboardID /*id*/) } void -CMSWindowsSecondaryScreen::getMousePos(SInt32* x, SInt32* y) const +CMSWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const { - assert(x != NULL); - assert(y != NULL); - CLock lock(&m_mutex); assert(m_window != NULL); syncDesktop(); POINT pos; if (GetCursorPos(&pos)) { - *x = pos.x; - *y = pos.y; + x = pos.x; + y = pos.y; } else { - *x = 0; - *y = 0; + x = 0; + y = 0; } } void -CMSWindowsSecondaryScreen::getSize(SInt32* width, SInt32* height) const +CMSWindowsSecondaryScreen::getShape( + SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { - getScreenSize(width, height); + getScreenShape(x, y, w, h); } SInt32 @@ -547,7 +545,7 @@ CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg, case WM_DISPLAYCHANGE: // screen resolution has changed - updateScreenSize(); + updateScreenShape(); m_client->onResolutionChanged(); return 0; } @@ -559,11 +557,11 @@ void CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y) { // warp to requested location - SInt32 w, h; - getScreenSize(&w, &h); + SInt32 x0, y0, w, h; + getScreenShape(x0, y0, w, h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65535.99 * x) / (w - 1)), - (DWORD)((65535.99 * y) / (h - 1)), + (DWORD)((65535.99 * x) / (w - 1)) + x0, + (DWORD)((65535.99 * y) / (h - 1)) + y0, 0, 0); // show cursor diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 084ed47c..e845d49a 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -30,8 +30,8 @@ public: virtual void mouseWheel(SInt32 delta); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); - virtual void getMousePos(SInt32* x, SInt32* y) const; - virtual void getSize(SInt32* width, SInt32* height) const; + virtual void getMousePos(SInt32& x, SInt32& y) const; + virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 90b14a0a..60b0c0dc 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -287,7 +287,7 @@ CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) } void -CXWindowsSecondaryScreen::getMousePos(SInt32* x, SInt32* y) const +CXWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const { CDisplayLock display(this); int xTmp, yTmp, dummy; @@ -295,14 +295,15 @@ CXWindowsSecondaryScreen::getMousePos(SInt32* x, SInt32* y) const Window dummyWindow; XQueryPointer(display, getRoot(), &dummyWindow, &dummyWindow, &xTmp, &yTmp, &dummy, &dummy, &dummyMask); - *x = xTmp; - *y = yTmp; + x = xTmp; + y = yTmp; } void -CXWindowsSecondaryScreen::getSize(SInt32* width, SInt32* height) const +CXWindowsSecondaryScreen::getShape( + SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { - getScreenSize(width, height); + getScreenShape(x, y, w, h); } SInt32 diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 77ebf158..296cc13e 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -29,8 +29,8 @@ public: virtual void mouseWheel(SInt32 delta); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); - virtual void getMousePos(SInt32* x, SInt32* y) const; - virtual void getSize(SInt32* width, SInt32* height) const; + virtual void getMousePos(SInt32& x, SInt32& y) const; + virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index f5309074..c3c08105 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -6,6 +6,15 @@ #include "CString.h" #include +// +// add backwards compatible multihead support (suppress bogus warning) +// +#pragma warning(push) +#pragma warning(disable: 4706) // assignment within conditional +#define COMPILE_MULTIMON_STUBS +#include +#pragma warning(pop) + // // CMSWindowsScreen // @@ -16,6 +25,7 @@ CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; CMSWindowsScreen::CMSWindowsScreen() : m_class(0), m_cursor(NULL), + m_x(0), m_y(0), m_w(0), m_h(0), m_thread(0) { @@ -99,11 +109,8 @@ CMSWindowsScreen::openDisplay() classInfo.hIconSm = NULL; m_class = RegisterClassEx(&classInfo); - // get screen size - // FIXME -- should handle multiple screens - m_w = GetSystemMetrics(SM_CXSCREEN); - m_h = GetSystemMetrics(SM_CYSCREEN); - log((CLOG_INFO "display size: %dx%d", m_w, m_h)); + // get screen shape + updateScreenShape(); // let subclass prep display onOpenDisplay(); @@ -142,21 +149,25 @@ CMSWindowsScreen::getClass() const } void -CMSWindowsScreen::updateScreenSize() +CMSWindowsScreen::updateScreenShape() { - m_w = GetSystemMetrics(SM_CXSCREEN); - m_h = GetSystemMetrics(SM_CYSCREEN); - log((CLOG_INFO "display resize: %dx%d", m_w, m_h)); + m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); + m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); + m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); + log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); } void -CMSWindowsScreen::getScreenSize(SInt32* w, SInt32* h) const +CMSWindowsScreen::getScreenShape( + SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { assert(m_class != 0); - assert(w != NULL && h != NULL); - *w = m_w; - *h = m_h; + x = m_x; + y = m_y; + w = m_w; + h = m_h; } HDESK diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index 00c05dda..85dc91bb 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -44,10 +44,11 @@ protected: ATOM getClass() const; // update screen size cache - void updateScreenSize(); + void updateScreenShape(); // get the size of the screen - void getScreenSize(SInt32* w, SInt32* h) const; + void getScreenShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; // get the input desktop. caller must CloseDesktop() the result. // do not call under windows 95/98/me. @@ -87,6 +88,7 @@ private: ATOM m_class; HICON m_icon; HCURSOR m_cursor; + SInt32 m_x, m_y; SInt32 m_w, m_h; DWORD m_thread; static CMSWindowsScreen* s_screen; diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index fc8d0156..c314c6ea 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -19,6 +19,7 @@ CXWindowsScreen* CXWindowsScreen::s_screen = NULL; CXWindowsScreen::CXWindowsScreen() : m_display(NULL), m_root(None), + m_x(0), m_y(0), m_w(0), m_h(0), m_stop(false) { @@ -59,10 +60,12 @@ CXWindowsScreen::openDisplay() m_screen = DefaultScreen(m_display); Screen* screen = ScreenOfDisplay(m_display, m_screen); - // get screen size + // get screen shape + m_x = 0; + m_y = 0; m_w = WidthOfScreen(screen); m_h = HeightOfScreen(screen); - log((CLOG_INFO "display size: %dx%d", m_w, m_h)); + log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); // get the root window m_root = RootWindow(m_display, m_screen); @@ -113,13 +116,15 @@ CXWindowsScreen::getRoot() const } void -CXWindowsScreen::getScreenSize(SInt32* w, SInt32* h) const +CXWindowsScreen::getScreenShape( + SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { assert(m_display != NULL); - assert(w != NULL && h != NULL); - *w = m_w; - *h = m_h; + x = m_x; + y = m_y; + w = m_w; + h = m_h; } Cursor diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index b589aba6..074411bb 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -41,12 +41,13 @@ protected: // is closed. void closeDisplay(); - // get the opened screen, its size, its root window. to get the + // get the opened screen, its shape, its root window. to get the // display create a CDisplayLock object passing this. while the // object exists no other threads may access the display. do not // save the Display* beyond the lifetime of the CDisplayLock. int getScreen() const; - void getScreenSize(SInt32* w, SInt32* h) const; + void getScreenShape( + SInt32& x, SInt32& y, SInt32& w, SInt32& h) const; Window getRoot() const; // create a cursor that is transparent everywhere @@ -108,6 +109,7 @@ private: Display* m_display; int m_screen; Window m_root; + SInt32 m_x, m_y; SInt32 m_w, m_h; bool m_stop; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 394c1a42..26915d71 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -111,15 +111,15 @@ CMSWindowsPrimaryScreen::open(CServer* server) nextMark(); // send screen info - SInt32 w, h; - getScreenSize(&w, &h); + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); POINT pos; GetCursorPos(&pos); - m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y); + m_server->setInfo(x, y, w, h, getJumpZoneSize(), pos.x, pos.y); - // compute center pixel of screen - m_xCenter = w >> 1; - m_yCenter = h >> 1; + // compute center pixel of primary screen + m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; + m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; // get keyboard state updateKeys(); @@ -228,10 +228,10 @@ void CMSWindowsPrimaryScreen::onConfigure() { if ((m_is95Family || m_desk != NULL) && !m_active) { - SInt32 w, h; - getScreenSize(&w, &h); + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); m_setZone(m_server->getActivePrimarySides(), - w, h, getJumpZoneSize()); + x, y, w, h, getJumpZoneSize()); } } @@ -264,9 +264,10 @@ CMSWindowsPrimaryScreen::grabClipboard(ClipboardID /*id*/) } void -CMSWindowsPrimaryScreen::getSize(SInt32* width, SInt32* height) const +CMSWindowsPrimaryScreen::getShape( + SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { - getScreenSize(width, height); + getScreenShape(x, y, w, h); } SInt32 @@ -551,17 +552,17 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, case WM_DISPLAYCHANGE: { // screen resolution may have changed - SInt32 wOld, hOld; - getScreenSize(&wOld, &hOld); - SInt32 w, h; - updateScreenSize(); - getScreenSize(&w, &h); + SInt32 xOld, yOld, wOld, hOld; + getScreenShape(xOld, yOld, wOld, hOld); + updateScreenShape(); + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); // do nothing if resolution hasn't changed - if (w != wOld || h != hOld) { - // recompute center pixel of screen - m_xCenter = w >> 1; - m_yCenter = h >> 1; + if (x != xOld || y != yOld || w != wOld || h != hOld) { + // recompute center pixel of primary screen + m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; + m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; // warp mouse to center if active if (m_active) { @@ -576,7 +577,7 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, // send new screen info POINT pos; GetCursorPos(&pos); - m_server->setInfo(w, h, getJumpZoneSize(), pos.x, pos.y); + m_server->setInfo(x, y, w, h, getJumpZoneSize(), pos.x, pos.y); } return 0; @@ -682,8 +683,8 @@ CMSWindowsPrimaryScreen::openDesktop() // with losing keyboard input (focus?) in that case. // unfortunately, hiding the full screen window (when entering // the scren causes all other windows to redraw). - SInt32 w, h; - getScreenSize(&w, &h); + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); // create the window m_window = CreateWindowEx(WS_EX_TOPMOST | @@ -692,7 +693,7 @@ CMSWindowsPrimaryScreen::openDesktop() (LPCTSTR)getClass(), "Synergy", WS_POPUP, - 0, 0, w, h, NULL, NULL, + x, y, w, h, NULL, NULL, getInstance(), NULL); if (m_window == NULL) { @@ -792,8 +793,8 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) // with losing keyboard input (focus?) in that case. // unfortunately, hiding the full screen window (when entering // the scren causes all other windows to redraw). - SInt32 w, h; - getScreenSize(&w, &h); + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); // create the window m_window = CreateWindowEx(WS_EX_TOPMOST | @@ -802,7 +803,7 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) (LPCTSTR)getClass(), "Synergy", WS_POPUP, - 0, 0, w, h, NULL, NULL, + x, y, w, h, NULL, NULL, getInstance(), NULL); if (m_window == NULL) { diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index c026e466..22d15ac9 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -25,7 +25,7 @@ public: virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); - virtual void getSize(SInt32* width, SInt32* height) const; + virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; virtual KeyModifierMask getToggleMask() const; diff --git a/server/CServer.cpp b/server/CServer.cpp index 91504f52..aca6a6c4 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -260,24 +260,27 @@ CServer::getActivePrimarySides() const } void -CServer::setInfo(SInt32 w, SInt32 h, SInt32 zoneSize, SInt32 x, SInt32 y) +CServer::setInfo(SInt32 x, SInt32 y, SInt32 w, SInt32 h, + SInt32 zoneSize, SInt32 mx, SInt32 my) { CLock lock(&m_mutex); assert(m_primaryInfo != NULL); - setInfoNoLock(m_primaryInfo->m_name, w, h, zoneSize, x, y); + setInfoNoLock(m_primaryInfo->m_name, x, y, w, h, zoneSize, mx, my); } void CServer::setInfo(const CString& client, - SInt32 w, SInt32 h, SInt32 zoneSize, SInt32 x, SInt32 y) + SInt32 x, SInt32 y, SInt32 w, SInt32 h, + SInt32 zoneSize, SInt32 mx, SInt32 my) { CLock lock(&m_mutex); - setInfoNoLock(client, w, h, zoneSize, x, y); + setInfoNoLock(client, x, y, w, h, zoneSize, mx, my); } void CServer::setInfoNoLock(const CString& screen, - SInt32 w, SInt32 h, SInt32 zoneSize, SInt32 x, SInt32 y) + SInt32 x, SInt32 y, SInt32 w, SInt32 h, + SInt32 zoneSize, SInt32 mx, SInt32 my) { assert(!screen.empty()); assert(w > 0); @@ -297,13 +300,15 @@ CServer::setInfoNoLock(const CString& screen, // update screen info if (info == m_active) { // update the remote mouse coordinates - m_x = x; - m_y = y; + m_x = mx; + m_y = my; } - info->m_width = w; - info->m_height = h; + info->m_x = x; + info->m_y = y; + info->m_w = w; + info->m_h = h; info->m_zoneSize = zoneSize; - log((CLOG_INFO "screen \"%s\" size=%dx%d zone=%d pos=%d,%d", screen.c_str(), w, h, zoneSize, x, y)); + log((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", screen.c_str(), x, y, w, h, zoneSize, mx, my)); // send acknowledgement (if screen isn't the primary) if (info->m_protocol != NULL) { @@ -313,7 +318,7 @@ CServer::setInfoNoLock(const CString& screen, // handle resolution change to primary screen else { if (info == m_active) { - onMouseMovePrimaryNoLock(x, y); + onMouseMovePrimaryNoLock(mx, my); } else { onMouseMoveSecondaryNoLock(0, 0); @@ -536,22 +541,22 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) // see if we should change screens CConfig::EDirection dir; - if (x < m_active->m_zoneSize) { + if (x < m_active->m_x + m_active->m_zoneSize) { x -= m_active->m_zoneSize; dir = CConfig::kLeft; log((CLOG_DEBUG1 "switch to left")); } - else if (x >= m_active->m_width - m_active->m_zoneSize) { + else if (x >= m_active->m_x + m_active->m_w - m_active->m_zoneSize) { x += m_active->m_zoneSize; dir = CConfig::kRight; log((CLOG_DEBUG1 "switch to right")); } - else if (y < m_active->m_zoneSize) { + else if (y < m_active->m_y + m_active->m_zoneSize) { y -= m_active->m_zoneSize; dir = CConfig::kTop; log((CLOG_DEBUG1 "switch to top")); } - else if (y >= m_active->m_height - m_active->m_zoneSize) { + else if (y >= m_active->m_y + m_active->m_h - m_active->m_zoneSize) { y += m_active->m_zoneSize; dir = CConfig::kBottom; log((CLOG_DEBUG1 "switch to bottom")); @@ -561,17 +566,13 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) return false; } - // get jump destination + // get jump destination and, if no screen in jump direction, + // then ignore the move. CScreenInfo* newScreen = getNeighbor(m_active, dir, x, y); - - // if no screen in jump direction then ignore the move if (newScreen == NULL) { return false; } - // remap position to account for resolution differences - mapPosition(m_active, dir, newScreen, x, y); - // switch screen switchScreen(newScreen, x, y); return true; @@ -615,16 +616,16 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) if (!isLockedToScreenNoLock()) { // find direction of neighbor CConfig::EDirection dir; - if (m_x < 0) { + if (m_x < m_active->m_x) { dir = CConfig::kLeft; } - else if (m_x > m_active->m_width - 1) { + else if (m_x > m_active->m_x + m_active->m_w - 1) { dir = CConfig::kRight; } - else if (m_y < 0) { + else if (m_y < m_active->m_y) { dir = CConfig::kTop; } - else if (m_y > m_active->m_height - 1) { + else if (m_y > m_active->m_y + m_active->m_h - 1) { dir = CConfig::kBottom; } else { @@ -638,39 +639,32 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) if (newScreen == NULL) { log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->m_name.c_str(), CConfig::dirName(dir))); - SInt32 x = m_x, y = m_y; - newScreen = getNeighbor(m_active, dir, x, y); - - // remap position to account for resolution differences - if (newScreen != NULL) { - mapPosition(m_active, dir, newScreen, x, y); - m_x = x; - m_y = y; - } - else { + // get new position or clamp to current screen + newScreen = getNeighbor(m_active, dir, m_x, m_y); + if (newScreen == NULL) { log((CLOG_DEBUG1 "no neighbor; clamping")); - if (m_x < 0) - m_x = 0; - else if (m_x > m_active->m_width - 1) - m_x = m_active->m_width - 1; - if (m_y < 0) - m_y = 0; - else if (m_y > m_active->m_height - 1) - m_y = m_active->m_height - 1; + if (m_x < m_active->m_x) + m_x = m_active->m_x; + else if (m_x > m_active->m_x + m_active->m_w - 1) + m_x = m_active->m_x + m_active->m_w - 1; + if (m_y < m_active->m_y) + m_y = m_active->m_y; + else if (m_y > m_active->m_y + m_active->m_h - 1) + m_y = m_active->m_y + m_active->m_h - 1; } } } else { // clamp to edge when locked log((CLOG_DEBUG1 "clamp to \"%s\"", m_active->m_name.c_str())); - if (m_x < 0) - m_x = 0; - else if (m_x > m_active->m_width - 1) - m_x = m_active->m_width - 1; - if (m_y < 0) - m_y = 0; - else if (m_y > m_active->m_height - 1) - m_y = m_active->m_height - 1; + if (m_x < m_active->m_x) + m_x = m_active->m_x; + else if (m_x > m_active->m_x + m_active->m_w - 1) + m_x = m_active->m_x + m_active->m_w - 1; + if (m_y < m_active->m_y) + m_y = m_active->m_y; + else if (m_y > m_active->m_y + m_active->m_h - 1) + m_y = m_active->m_y + m_active->m_h - 1; } // warp cursor if on same screen @@ -729,7 +723,8 @@ void CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y) { assert(dst != NULL); - assert(x >= 0 && y >= 0 && x < dst->m_width && y < dst->m_height); + assert(x >= dst->m_x && y >= dst->m_y); + assert(x < dst->m_x + dst->m_w && y < dst->m_y + dst->m_h); assert(m_active != NULL); log((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y)); @@ -832,22 +827,27 @@ CServer::getNeighbor(CScreenInfo* src, assert(src != NULL); // get the first neighbor - CScreenInfo* lastGoodScreen = src; CScreenInfo* dst = getNeighbor(src, srcSide); if (dst == NULL) { return NULL; } // get the source screen's size (needed for kRight and kBottom) - SInt32 w = src->m_width, h = src->m_height; + SInt32 w = src->m_w, h = src->m_h; - // find destination screen, adjusting x or y (but not both) + // find destination screen, adjusting x or y (but not both). the + // searches are done in a sort of canonical screen space where + // the upper-left corner is 0,0 for each screen. we adjust from + // actual to canonical position on entry to and from canonical to + // actual on exit from the search. + CScreenInfo* lastGoodScreen = src; switch (srcSide) { case CConfig::kLeft: + x -= src->m_x; while (dst != NULL && dst != lastGoodScreen) { lastGoodScreen = dst; - w = lastGoodScreen->m_width; - h = lastGoodScreen->m_height; + w = lastGoodScreen->m_w; + h = lastGoodScreen->m_h; x += w; if (x >= 0) { break; @@ -855,27 +855,33 @@ CServer::getNeighbor(CScreenInfo* src, log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } + assert(lastGoodScreen != NULL); + x += lastGoodScreen->m_x; break; case CConfig::kRight: + x -= src->m_x; while (dst != NULL) { lastGoodScreen = dst; x -= w; - w = lastGoodScreen->m_width; - h = lastGoodScreen->m_height; + w = lastGoodScreen->m_w; + h = lastGoodScreen->m_h; if (x < w) { break; } log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } + assert(lastGoodScreen != NULL); + x += lastGoodScreen->m_x; break; case CConfig::kTop: + y -= src->m_y; while (dst != NULL) { lastGoodScreen = dst; - w = lastGoodScreen->m_width; - h = lastGoodScreen->m_height; + w = lastGoodScreen->m_w; + h = lastGoodScreen->m_h; y += h; if (y >= 0) { break; @@ -883,106 +889,108 @@ CServer::getNeighbor(CScreenInfo* src, log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } + assert(lastGoodScreen != NULL); + y += lastGoodScreen->m_y; break; case CConfig::kBottom: + y -= src->m_y; while (dst != NULL) { lastGoodScreen = dst; y -= h; - w = lastGoodScreen->m_width; - h = lastGoodScreen->m_height; + w = lastGoodScreen->m_w; + h = lastGoodScreen->m_h; if (y < h) { break; } log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } + assert(lastGoodScreen != NULL); + y += lastGoodScreen->m_y; break; } - assert(lastGoodScreen != NULL); -/* allow screen to be it's own neighbor to allow wrapping - // no neighbor if best neighbor is the source itself - if (lastGoodScreen == src) - return NULL; -*/ + // save destination screen + assert(lastGoodScreen != NULL); + dst = lastGoodScreen; // if entering primary screen then be sure to move in far enough // to avoid the jump zone. if entering a side that doesn't have // a neighbor (i.e. an asymmetrical side) then we don't need to // move inwards because that side can't provoke a jump. - if (lastGoodScreen->m_protocol == NULL) { - const CString dstName(lastGoodScreen->m_name); + if (dst->m_protocol == NULL) { + const CString dstName(dst->m_name); switch (srcSide) { case CConfig::kLeft: if (!m_config.getNeighbor(dstName, CConfig::kRight).empty() && - x > w - 1 - lastGoodScreen->m_zoneSize) - x = w - 1 - lastGoodScreen->m_zoneSize; + x > dst->m_x + w - 1 - dst->m_zoneSize) + x = dst->m_x + w - 1 - dst->m_zoneSize; break; case CConfig::kRight: if (!m_config.getNeighbor(dstName, CConfig::kLeft).empty() && - x < lastGoodScreen->m_zoneSize) - x = lastGoodScreen->m_zoneSize; + x < dst->m_x + dst->m_zoneSize) + x = dst->m_x + dst->m_zoneSize; break; case CConfig::kTop: if (!m_config.getNeighbor(dstName, CConfig::kBottom).empty() && - y > h - 1 - lastGoodScreen->m_zoneSize) - y = h - 1 - lastGoodScreen->m_zoneSize; + y > dst->m_y + h - 1 - dst->m_zoneSize) + y = dst->m_y + h - 1 - dst->m_zoneSize; break; case CConfig::kBottom: if (!m_config.getNeighbor(dstName, CConfig::kTop).empty() && - y < lastGoodScreen->m_zoneSize) - y = lastGoodScreen->m_zoneSize; + y < dst->m_y + dst->m_zoneSize) + y = dst->m_y + dst->m_zoneSize; break; } } - return lastGoodScreen; -} - -void -CServer::mapPosition(CScreenInfo* src, CConfig::EDirection srcSide, - CScreenInfo* dst, SInt32& x, SInt32& y) const -{ - assert(src != NULL); - assert(dst != NULL); - assert(srcSide >= CConfig::kFirstDirection && - srcSide <= CConfig::kLastDirection); - + // adjust the coordinate orthogonal to srcSide to account for + // resolution differences. for example, if y is 200 pixels from + // the top on a screen 1000 pixels high (20% from the top) when + // we cross the left edge onto a screen 600 pixels high then y + // should be set 120 pixels from the top (again 20% from the + // top). switch (srcSide) { case CConfig::kLeft: case CConfig::kRight: + y -= src->m_y; if (y < 0) { y = 0; } - else if (y >= src->m_height) { - y = dst->m_height - 1; + else if (y >= src->m_h) { + y = dst->m_h - 1; } else { y = static_cast(0.5 + y * - static_cast(dst->m_height - 1) / - (src->m_height - 1)); + static_cast(dst->m_h - 1) / + (src->m_h - 1)); } + y += dst->m_y; break; case CConfig::kTop: case CConfig::kBottom: + x -= src->m_x; if (x < 0) { x = 0; } - else if (x >= src->m_width) { - x = dst->m_width - 1; + else if (x >= src->m_w) { + x = dst->m_w - 1; } else { x = static_cast(0.5 + x * - static_cast(dst->m_width - 1) / - (src->m_width - 1)); + static_cast(dst->m_w - 1) / + (src->m_w - 1)); } + x += dst->m_x; break; } + + return dst; } #include "CTCPListenSocket.h" @@ -1536,8 +1544,8 @@ CServer::removeConnection(const CString& name) // if this is active screen then we have to jump off of it if (m_active == index->second && m_active != m_primaryInfo) { // record new position (center of primary screen) - m_x = m_primaryInfo->m_width >> 1; - m_y = m_primaryInfo->m_height >> 1; + m_x = m_primaryInfo->m_x + (m_primaryInfo->m_w >> 1); + m_y = m_primaryInfo->m_y + (m_primaryInfo->m_h >> 1); // don't notify active screen since it probably already disconnected log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y)); @@ -1601,8 +1609,10 @@ CServer::CScreenInfo::CScreenInfo(const CString& name, m_name(name), m_protocol(protocol), m_ready(false), - m_width(0), - m_height(0), + m_x(0), + m_y(0), + m_w(0), + m_h(0), m_zoneSize(0) { for (ClipboardID id = 0; id < kClipboardEnd; ++id) diff --git a/server/CServer.h b/server/CServer.h index c68a71a5..318ec4a7 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -55,12 +55,14 @@ public: void grabClipboard(ClipboardID); // handle updates from primary - void setInfo(SInt32 wScreen, SInt32 hScreen, + void setInfo(SInt32 xScreen, SInt32 yScreen, + SInt32 wScreen, SInt32 hScreen, SInt32 zoneSize, SInt32 xMouse, SInt32 yMouse); // handle messages from clients void setInfo(const CString& clientName, + SInt32 xScreen, SInt32 yScreen, SInt32 wScreen, SInt32 hScreen, SInt32 zoneSize, SInt32 xMouse, SInt32 yMouse); @@ -119,8 +121,12 @@ private: CString m_name; IServerProtocol* m_protocol; bool m_ready; - SInt32 m_width, m_height; + + // screen shape and jump zone size + SInt32 m_x, m_y; + SInt32 m_w, m_h; SInt32 m_zoneSize; + bool m_gotClipboard[kClipboardEnd]; }; @@ -130,6 +136,7 @@ private: // update screen info void setInfoNoLock(const CString& screenName, + SInt32 xScreen, SInt32 yScreen, SInt32 wScreen, SInt32 hScreen, SInt32 zoneSize, SInt32 xMouse, SInt32 yMouse); @@ -150,19 +157,12 @@ private: // lookup neighboring screen. given a position relative to the // source screen, find the screen we should move onto and where. // if the position is sufficiently far from the source then we - // cross multiple screens. + // cross multiple screens. if there is no suitable screen then + // return NULL and x,y are not modified. CScreenInfo* getNeighbor(CScreenInfo*, CConfig::EDirection, SInt32& x, SInt32& y) const; - // adjust coordinates to account for resolution differences. the - // position is converted to a resolution independent form then - // converted back to screen coordinates on the destination screen. - void mapPosition(CScreenInfo* src, - CConfig::EDirection srcSide, - CScreenInfo* dst, - SInt32& x, SInt32& y) const; - // open/close the primary screen void openPrimaryScreen(); void closePrimaryScreen(); diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index bfd5fef9..54eaf81e 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -199,21 +199,21 @@ void CServerProtocol1_0::recvInfo() { // parse the message - SInt16 x, y, w, h, zoneInfo; + SInt16 x, y, w, h, zoneInfo, mx, my; CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, - &w, &h, &zoneInfo, &x, &y); - log((CLOG_DEBUG "received client \"%s\" info size=%dx%d, zone=%d, pos=%d,%d", getClient().c_str(), w, h, zoneInfo, x, y)); + &x, &y, &w, &h, &zoneInfo, &mx, &my); + log((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getClient().c_str(), x, y, w, h, zoneInfo, mx, my)); // validate if (w <= 0 || h <= 0 || zoneInfo < 0) { throw XBadClient(); } - if (x < 0 || y < 0 || x >= w || y >= h) { + if (mx < x || my < y || mx >= x + w || my >= y + h) { throw XBadClient(); } // tell server of change - getServer()->setInfo(getClient(), w, h, zoneInfo, x, y); + getServer()->setInfo(getClient(), x, y, w, h, zoneInfo, mx, my); } void diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index f834f413..23d0cf62 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -48,6 +48,8 @@ static HHOOK g_keyboardLL = NULL; static bool g_relay = false; static SInt32 g_zoneSize = 0; static UInt32 g_zoneSides = 0; +static SInt32 g_xScreen = 0; +static SInt32 g_yScreen = 0; static SInt32 g_wScreen = 0; static SInt32 g_hScreen = 0; static HCURSOR g_cursor = NULL; @@ -196,16 +198,16 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) SInt32 x = (SInt32)info->pt.x; SInt32 y = (SInt32)info->pt.y; if (!inside && (g_zoneSides & CConfig::kLeftMask) != 0) { - inside = (x < g_zoneSize); + inside = (x < g_xScreen + g_zoneSize); } if (!inside && (g_zoneSides & CConfig::kRightMask) != 0) { - inside = (x >= g_wScreen - g_zoneSize); + inside = (x >= g_xScreen + g_wScreen - g_zoneSize); } if (!inside && (g_zoneSides & CConfig::kTopMask) != 0) { - inside = (y < g_zoneSize); + inside = (y < g_yScreen + g_zoneSize); } if (!inside && (g_zoneSides & CConfig::kBottomMask) != 0) { - inside = (y >= g_hScreen - g_zoneSize); + inside = (y >= g_yScreen + g_hScreen - g_zoneSize); } // if inside then eat event and notify our window @@ -463,6 +465,8 @@ install(DWORD threadID) g_relay = false; g_zoneSize = 0; g_zoneSides = 0; + g_xScreen = 0; + g_yScreen = 0; g_wScreen = 0; g_hScreen = 0; g_cursor = NULL; @@ -585,10 +589,14 @@ uninstall(void) } void -setZone(UInt32 sides, SInt32 w, SInt32 h, SInt32 jumpZoneSize) +setZone(UInt32 sides, + SInt32 x, SInt32 y, SInt32 w, SInt32 h, + SInt32 jumpZoneSize) { g_zoneSize = jumpZoneSize; g_zoneSides = sides; + g_xScreen = x; + g_yScreen = y; g_wScreen = w; g_hScreen = h; g_relay = false; @@ -601,6 +609,8 @@ setRelay(void) g_relay = true; g_zoneSize = 0; g_zoneSides = 0; + g_xScreen = 0; + g_yScreen = 0; g_wScreen = 0; g_hScreen = 0; } diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index 159144c9..34078de8 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -26,13 +26,15 @@ extern "C" { typedef int (*InstallFunc)(DWORD targetQueueThreadID); typedef int (*UninstallFunc)(void); -typedef void (*SetZoneFunc)(UInt32, SInt32, SInt32, SInt32); +typedef void (*SetZoneFunc)(UInt32, + SInt32, SInt32, SInt32, SInt32, SInt32); typedef void (*SetRelayFunc)(void); CSYNERGYHOOK_API int install(DWORD); CSYNERGYHOOK_API int uninstall(void); CSYNERGYHOOK_API void setZone(UInt32 sides, - SInt32 w, SInt32 h, SInt32 jumpZoneSize); + SInt32 x, SInt32 y, SInt32 w, SInt32 h, + SInt32 jumpZoneSize); CSYNERGYHOOK_API void setRelay(void); } diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index c8ba3a68..d7f723c4 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -161,6 +161,7 @@ CXWindowsPrimaryScreen::run() // FIXME -- slurp up all remaining motion events? // probably not since keystrokes may go to wrong place. +// XXX -- why call XQueryPointer? // get mouse deltas { CDisplayLock display(this); @@ -173,8 +174,8 @@ CXWindowsPrimaryScreen::run() } // compute position of center of window - SInt32 w, h; - getScreenSize(&w, &h); + SInt32 x0, y0, w, h; + getScreenShape(x0, y0, w, h); x = xRoot - (w >> 1); y = yRoot - (h >> 1); @@ -216,11 +217,11 @@ CXWindowsPrimaryScreen::open(CServer* server) // m_numLockHalfDuplex = true; // m_capsLockHalfDuplex = true; - // get screen size - SInt32 w, h; - getScreenSize(&w, &h); + // get screen shape + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); - int x, y; + int mx, my; { CDisplayLock display(this); @@ -232,14 +233,14 @@ CXWindowsPrimaryScreen::open(CServer* server) int xWindow, yWindow; unsigned int mask; if (!XQueryPointer(display, m_window, &root, &window, - &x, &y, &xWindow, &yWindow, &mask)) { - x = w >> 1; - y = h >> 1; + &mx, &my, &xWindow, &yWindow, &mask)) { + mx = w >> 1; + my = h >> 1; } } // send screen info - m_server->setInfo(w, h, getJumpZoneSize(), x, y); + m_server->setInfo(x, y, w, h, getJumpZoneSize(), mx, my); } void @@ -340,8 +341,8 @@ CXWindowsPrimaryScreen::leave() log((CLOG_DEBUG1 "grabbed pointer and keyboard")); // move the mouse to the center of grab window - SInt32 w, h; - getScreenSize(&w, &h); + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); warpCursorNoLock(display, w >> 1, h >> 1); // local client now active @@ -396,9 +397,10 @@ CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) } void -CXWindowsPrimaryScreen::getSize(SInt32* width, SInt32* height) const +CXWindowsPrimaryScreen::getShape( + SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { - getScreenSize(width, height); + getScreenShape(x, y, w, h); } SInt32 @@ -480,8 +482,8 @@ CXWindowsPrimaryScreen::onOpenDisplay(Display* display) assert(m_window == None); // get size of screen - SInt32 w, h; - getScreenSize(&w, &h); + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); // create the grab window. this window is used to capture user // input when the user is focussed on another client. don't let @@ -494,7 +496,7 @@ CXWindowsPrimaryScreen::onOpenDisplay(Display* display) attr.do_not_propagate_mask = 0; attr.override_redirect = True; attr.cursor = createBlankCursor(); - m_window = XCreateWindow(display, getRoot(), 0, 0, w, h, 0, 0, + m_window = XCreateWindow(display, getRoot(), x, y, w, h, 0, 0, InputOnly, CopyFromParent, CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index fe4bbc87..13b63c11 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -21,7 +21,7 @@ public: virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); - virtual void getSize(SInt32* width, SInt32* height) const; + virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; virtual KeyModifierMask getToggleMask() const; diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index c88207d5..0a318c14 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -79,8 +79,9 @@ public: virtual CString getName() const = 0; */ - // get the size of the screen - virtual void getSize(SInt32* width, SInt32* height) const = 0; + // get the screen region + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; // get the size of jump zone virtual SInt32 getJumpZoneSize() const = 0; diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index be709890..57091147 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -66,10 +66,11 @@ public: // accessors // get the position of the mouse on the screen - virtual void getMousePos(SInt32* x, SInt32* y) const = 0; + virtual void getMousePos(SInt32& x, SInt32& y) const = 0; // get the size of the screen - virtual void getSize(SInt32* width, SInt32* height) const = 0; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; // get the size of jump zone virtual SInt32 getJumpZoneSize() const = 0; diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index b60fc831..3d2223e2 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -16,6 +16,10 @@ static const UInt16 kDefaultPort = 24800; // always 4 bytes optionally followed by message specific parameters. // +// +// positions and sizes are signed 16 bit integers. +// + // // command codes // @@ -101,9 +105,12 @@ static const char kMsgDMouseWheel[] = "DMWM%2i"; static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; // client data: secondary -> primary -// $1 = seconary screen width in pixels, $2 = screen height, $3 = -// size of warp zone. $4 and $5 are the x,y position of the mouse -// on the secondary screen. +// $1 = coordinate of leftmost pixel on secondary screen, +// $2 = coordinate of topmost pixel on secondary screen, +// $3 = width of secondary screen in pixels, +// $4 = height of secondary screen in pixels, +// $5 = size of warp zone, +// $6, $7 = the x,y position of the mouse on the secondary screen. // // the secondary screen must send this message in response to the // kMsgQInfo message. it must also send this message when the @@ -111,7 +118,7 @@ static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; // should ignore any kMsgDMouseMove messages until it receives a // kMsgCInfoAck in order to prevent attempts to move the mouse off // the new screen area. -static const char kMsgDInfo[] = "DINF%2i%2i%2i%2i%2i"; +static const char kMsgDInfo[] = "DINF%2i%2i%2i%2i%2i%2i%2i"; // From a16e7217ce586bbbe7eff1fc585f77220364d512 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 19 Jun 2002 20:24:35 +0000 Subject: [PATCH 183/807] fixed bugs in mouse motion. wasn't taking care to capture all motion events relative to the previous mouse position. for example, if two mouse events arrive, the first at x+1,y and the second at x+2,y, we used to compute deltas of 1,0 and 2,0 instead of 1,0 and 1,0. that's fixed. also worked around a bug (probably) in windows that caused a motion event after a SetCursorPos() to be lost or reported one pixel off from the correct position. now using mouse_event() which doesn't have that problem. also fixed calculation of normalized coordinates for mouse_event() when there are multiple displays. --- client/CMSWindowsSecondaryScreen.cpp | 8 +-- server/CMSWindowsPrimaryScreen.cpp | 55 ++++++++++++--- server/CMSWindowsPrimaryScreen.h | 7 ++ server/CSynergyHook.cpp | 13 +++- server/CSynergyHook.h | 3 +- server/CXWindowsPrimaryScreen.cpp | 102 +++++++++++++++++++-------- server/CXWindowsPrimaryScreen.h | 6 ++ 7 files changed, 144 insertions(+), 50 deletions(-) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index dec2e2f9..c6cadd15 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -342,8 +342,8 @@ CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) SInt32 x0, y0, w, h; getScreenShape(x0, y0, w, h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (SInt32)(65535.99 * x / (w - 1)) + x0, - (SInt32)(65535.99 * y / (h - 1)) + y0, + (DWORD)((65535.99 * (x - x0)) / (w - 1)), + (DWORD)((65535.99 * (y - y0)) / (h - 1)), 0, 0); } @@ -560,8 +560,8 @@ CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y) SInt32 x0, y0, w, h; getScreenShape(x0, y0, w, h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65535.99 * x) / (w - 1)) + x0, - (DWORD)((65535.99 * y) / (h - 1)) + y0, + (DWORD)((65535.99 * (x - x0)) / (w - 1)), + (DWORD)((65535.99 * (y - y0)) / (h - 1)), 0, 0); // show cursor diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 26915d71..417b5683 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -110,12 +110,16 @@ CMSWindowsPrimaryScreen::open(CServer* server) m_markReceived = 0; nextMark(); + // save cursor pos + POINT pos; + GetCursorPos(&pos); + m_x = pos.x; + m_y = pos.y; + // send screen info SInt32 x, y, w, h; getScreenShape(x, y, w, h); - POINT pos; - GetCursorPos(&pos); - m_server->setInfo(x, y, w, h, getJumpZoneSize(), pos.x, pos.y); + m_server->setInfo(x, y, w, h, getJumpZoneSize(), m_x, m_y); // compute center pixel of primary screen m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; @@ -174,7 +178,7 @@ CMSWindowsPrimaryScreen::leave() updateKeys(); // warp mouse to center of screen - warpCursor(m_xCenter, m_yCenter); + warpCursorToCenter(); // ignore this many mouse motion events (not including the already // queued events). on (at least) the win2k login desktop, one @@ -182,6 +186,7 @@ CMSWindowsPrimaryScreen::leave() // warpCursor(). i don't know why it does that and other desktops // don't have the same problem. anyway, simply ignoring that event // works around it. +// FIXME -- is this true now that we're using mouse_event? m_mouseMoveIgnore = 1; // disable ctrl+alt+del, alt+tab, etc @@ -240,6 +245,24 @@ CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { // set the cursor position without generating an event SetCursorPos(x, y); + + // save position as last position + m_x = x; + m_y = y; +} + +void +CMSWindowsPrimaryScreen::warpCursorToCenter() +{ + // warp to center. the extra info tells the hook DLL to send + // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); + mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, + (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), + (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), + 0, + 0x12345678); } void @@ -451,16 +474,21 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) m_server->onMouseMovePrimary(x, y); } else { - // get mouse deltas - x -= m_xCenter; - y -= m_yCenter; + // compute motion delta. this is relative to the + // last known mouse position. + x -= m_x; + y -= m_y; + + // save position to compute delta of next motion + m_x = static_cast(msg->wParam); + m_y = static_cast(msg->lParam); // ignore if the mouse didn't move or we're ignoring // motion. if (m_mouseMoveIgnore == 0) { - if (x != 0 && y != 0) { - // warp mouse back to center - warpCursor(m_xCenter, m_yCenter); + if (x != 0 || y != 0) { + // back to center + warpCursorToCenter(); // send motion m_server->onMouseMoveSecondary(x, y); @@ -474,6 +502,11 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) } return true; + case SYNERGY_MSG_POST_WARP: + m_x = static_cast(msg->wParam); + m_y = static_cast(msg->lParam); + return true; + case WM_TIMER: // if current desktop is not the input desktop then switch to it if (!m_is95Family) { @@ -566,7 +599,7 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, // warp mouse to center if active if (m_active) { - warpCursor(m_xCenter, m_yCenter); + warpCursorToCenter(); } // tell hook about resize if not active diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 22d15ac9..37db16ac 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -44,6 +44,10 @@ private: void onEnter(); bool onLeave(); + // warp mouse to center of primary display (used when computing + // motion deltas while mouse is on secondary screen). + void warpCursorToCenter(); + // discard posted messages void nextMark(); @@ -91,6 +95,9 @@ private: // map of key state BYTE m_keys[256]; + // last mouse position + SInt32 m_x, m_y; + // position of center pixel of screen SInt32 m_xCenter, m_yCenter; diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 23d0cf62..e3f70c5b 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -169,8 +169,6 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) { const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; - SInt32 x = (SInt32)info->pt.x; - SInt32 y = (SInt32)info->pt.y; // we want the cursor to be hidden at all times so we // hide the cursor on whatever window has it. but then @@ -186,7 +184,16 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) } // relay the motion - PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + if (info->dwExtraInfo == 0x12345678) { + PostThreadMessage(g_threadID, + SYNERGY_MSG_POST_WARP, x, y); + } + else { + PostThreadMessage(g_threadID, + SYNERGY_MSG_MOUSE_MOVE, x, y); + } } return 1; } diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index 34078de8..03f5eb00 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -20,7 +20,8 @@ #define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data #define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; #define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y -#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0015 // delta; +#define SYNERGY_MSG_POST_WARP WM_APP + 0x0015 // x; y +#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0016 // delta; extern "C" { diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index d7f723c4..bbb9eefa 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -151,39 +151,70 @@ CXWindowsPrimaryScreen::run() case MotionNotify: { log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); - SInt32 x, y; + SInt32 x = xevent.xmotion.x_root; + SInt32 y = xevent.xmotion.y_root; if (!m_active) { - x = xevent.xmotion.x_root; - y = xevent.xmotion.y_root; m_server->onMouseMovePrimary(x, y); } else { - // FIXME -- slurp up all remaining motion events? - // probably not since keystrokes may go to wrong place. + // compute motion delta. this is relative to the + // last known mouse position. + x -= m_x; + y -= m_y; -// XXX -- why call XQueryPointer? - // get mouse deltas - { + // save position to compute delta of next motion + m_x = xevent.xmotion.x_root; + m_y = xevent.xmotion.y_root; + + // if event was sent then ignore it and discard + // the event from the mouse warp. this is how we + // warp the mouse back to the center of the screen + // without that causing a corresponding motion on + // the secondary screen. + if (xevent.xmotion.send_event) { + // ignore event + x = 0; + y = 0; + + // discard events until we find the matching + // sent event. see below for where the events + // are sent. we discard the matching sent + // event and can be sure we've skipped the + // warp event. CDisplayLock display(this); - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int mask; - if (!XQueryPointer(display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &mask)) { - break; - } - - // compute position of center of window - SInt32 x0, y0, w, h; - getScreenShape(x0, y0, w, h); - x = xRoot - (w >> 1); - y = yRoot - (h >> 1); - - // warp mouse back to center - warpCursorNoLock(display, w >> 1, h >> 1); + do { + XMaskEvent(display, PointerMotionMask, &xevent); + } while (!xevent.xmotion.send_event); } - m_server->onMouseMoveSecondary(x, y); + // warp mouse back to center + if (x != 0 || y != 0) { + CDisplayLock display(this); + // send an event that we can recognize before + // the mouse warp. + XEvent eventBefore = xevent; + xevent.xmotion.window = m_window; + xevent.xmotion.time = CurrentTime; + xevent.xmotion.x = m_xCenter; + xevent.xmotion.y = m_yCenter; + xevent.xmotion.x_root = m_xCenter; + xevent.xmotion.y_root = m_yCenter; + XEvent eventAfter = eventBefore; + XSendEvent(display, m_window, False, 0, &xevent); + + // warp mouse back to center + XWarpPointer(display, None, getRoot(), + 0, 0, 0, 0, m_xCenter, m_yCenter); + + // send an event that we can recognize after + // the mouse warp. + XSendEvent(display, m_window, False, 0, &xevent); + } + + // send event if mouse moved + if (x != 0 || y != 0) { + m_server->onMouseMoveSecondary(x, y); + } } } break; @@ -221,7 +252,6 @@ CXWindowsPrimaryScreen::open(CServer* server) SInt32 x, y, w, h; getScreenShape(x, y, w, h); - int mx, my; { CDisplayLock display(this); @@ -230,17 +260,25 @@ CXWindowsPrimaryScreen::open(CServer* server) // get mouse position Window root, window; - int xWindow, yWindow; + int mx, my, xWindow, yWindow; unsigned int mask; if (!XQueryPointer(display, m_window, &root, &window, &mx, &my, &xWindow, &yWindow, &mask)) { mx = w >> 1; my = h >> 1; } + + // save mouse position + m_x = x; + m_y = y; } + // save position of center of screen + m_xCenter = x + (w >> 1); + m_yCenter = y + (h >> 1); + // send screen info - m_server->setInfo(x, y, w, h, getJumpZoneSize(), mx, my); + m_server->setInfo(x, y, w, h, getJumpZoneSize(), m_x, m_y); } void @@ -341,9 +379,7 @@ CXWindowsPrimaryScreen::leave() log((CLOG_DEBUG1 "grabbed pointer and keyboard")); // move the mouse to the center of grab window - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - warpCursorNoLock(display, w >> 1, h >> 1); + warpCursorNoLock(display, m_xCenter, m_yCenter); // local client now active m_active = true; @@ -381,6 +417,10 @@ CXWindowsPrimaryScreen::warpCursorNoLock(Display* display, SInt32 x, SInt32 y) PointerMotionMask, &xevent)) { // do nothing } + + // save position as last position + m_x = x; + m_y = y; } void diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 13b63c11..19f5feab 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -71,6 +71,12 @@ private: unsigned int m_numLockMask; unsigned int m_capsLockMask; unsigned int m_scrollLockMask; + + // last mouse position + SInt32 m_x, m_y; + + // position of center pixel of screen + SInt32 m_xCenter, m_yCenter; }; #endif From 4f418e015edfb241db3d74f417be6134e2269f8b Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 20 Jun 2002 09:19:55 +0000 Subject: [PATCH 184/807] work around for bug with mouse driver on lombard powerbook. --- server/CXWindowsPrimaryScreen.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index bbb9eefa..e5567393 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -187,8 +187,20 @@ CXWindowsPrimaryScreen::run() } while (!xevent.xmotion.send_event); } - // warp mouse back to center - if (x != 0 || y != 0) { + // warp mouse back to center. my lombard (powerbook + // g3) using the adbmouse driver has two problems: + // first, the driver only sends motions of +/-2 + // pixels and, second, it seems to discard some + // physical input after a warp. the former isn't a + // big deal (we're just limited to every other + // pixel) but the latter is a PITA. to work around + // it we only warp when the mouse has moved more + // than s_size pixels from the center. + static const SInt32 s_size = 32; + if (xevent.xmotion.x_root - m_xCenter < -s_size || + xevent.xmotion.x_root - m_xCenter > s_size || + xevent.xmotion.y_root - m_yCenter < -s_size || + xevent.xmotion.y_root - m_yCenter > s_size) { CDisplayLock display(this); // send an event that we can recognize before // the mouse warp. From c4fea1c32bc026b46c342d133fe519a9356b38ee Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 20 Jun 2002 11:13:37 +0000 Subject: [PATCH 185/807] added workaround for bug windows 98 (Me?) and multiple displays: absolute mouse_event() moves don't work except for primary display. --- client/CMSWindowsSecondaryScreen.cpp | 115 +++++++++++++++++++++++---- client/CMSWindowsSecondaryScreen.h | 15 ++++ 2 files changed, 116 insertions(+), 14 deletions(-) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index c6cadd15..0602a05f 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -9,6 +9,14 @@ #include "CLog.h" #include +// these are only defined when WINVER >= 0x0500 +#if !defined(SPI_GETMOUSESPEED) +#define SPI_GETMOUSESPEED 112 +#endif +#if !defined(SPI_SETMOUSESPEED) +#define SPI_SETMOUSESPEED 113 +#endif + // // CMSWindowsSecondaryScreen // @@ -135,7 +143,7 @@ CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) toggleKey(VK_SCROLL, KeyModifierScrollLock); } - // hide mouse + // show mouse onEnter(x, y); } @@ -338,13 +346,7 @@ CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) CLock lock(&m_mutex); assert(m_window != NULL); syncDesktop(); - - SInt32 x0, y0, w, h; - getScreenShape(x0, y0, w, h); - mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65535.99 * (x - x0)) / (w - 1)), - (DWORD)((65535.99 * (y - y0)) / (h - 1)), - 0, 0); + warpCursor(x, y); } void @@ -427,6 +429,9 @@ CMSWindowsSecondaryScreen::onOpenDisplay() { assert(m_window == NULL); + // note if using multiple monitors + m_multimon = isMultimon(); + // save thread id. we'll need to pass this to the hook library. m_threadID = GetCurrentThreadId(); @@ -546,6 +551,7 @@ CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg, case WM_DISPLAYCHANGE: // screen resolution has changed updateScreenShape(); + m_multimon = isMultimon(); m_client->onResolutionChanged(); return 0; } @@ -557,12 +563,7 @@ void CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y) { // warp to requested location - SInt32 x0, y0, w, h; - getScreenShape(x0, y0, w, h); - mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65535.99 * (x - x0)) / (w - 1)), - (DWORD)((65535.99 * (y - y0)) / (h - 1)), - 0, 0); + warpCursor(x, y); // show cursor ShowWindow(m_window, SW_HIDE); @@ -736,6 +737,92 @@ CMSWindowsSecondaryScreen::getCurrentDesktopName() const return m_deskName; } +void +CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + // move the mouse directly to target position on NT family or if + // not using multiple monitors. + if (!m_multimon || !m_is95Family) { + SInt32 x0, y0, w, h; + getScreenShape(x0, y0, w, h); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65535.99 * (x - x0)) / (w - 1)), + (DWORD)((65535.99 * (y - y0)) / (h - 1)), + 0, 0); + } + + // windows 98 (and Me?) is broken. you cannot set the absolute + // position of the mouse except on the primary monitor but you + // can do relative moves onto any monitor. this is, in microsoft's + // words, "by design." apparently the designers of windows 2000 + // we're a little less lazy and did it right. + // + // we use the microsoft recommendation (Q193003): set the absolute + // position on the primary monitor, disable mouse acceleration, + // relative move the mouse to the final location, restore mouse + // acceleration. to avoid one kind of race condition (the user + // clicking the mouse or pressing a key between the absolute and + // relative move) we'll use SendInput() which guarantees that the + // events are delivered uninterrupted. we cannot prevent changes + // to the mouse acceleration at inopportune times, though. + // + // point-to-activate (x-mouse) doesn't seem to be bothered by the + // absolute/relative combination. a window over the absolute + // position (0,0) does *not* get activated (at least not on win2k) + // if the relative move puts the cursor elsewhere. similarly, the + // app under the final mouse position does *not* get deactivated + // by the absolute move to 0,0. + else { + // save mouse speed & acceleration + int oldSpeed[4]; + bool accelChanged = + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed,0) && + SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); + + // use 1:1 motion + if (accelChanged) { + int newSpeed[4] = { 0, 0, 0, 1 }; + accelChanged = + SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || + SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); + } + + // send events + INPUT events[2]; + events[0].type = INPUT_MOUSE; + events[0].mi.dx = 0; + events[0].mi.dy = 0; + events[0].mi.mouseData = 0; + events[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; + events[0].mi.time = GetTickCount(); + events[0].mi.dwExtraInfo = 0; + events[1].type = INPUT_MOUSE; + events[1].mi.dx = x; + events[1].mi.dy = y; + events[1].mi.mouseData = 0; + events[1].mi.dwFlags = MOUSEEVENTF_MOVE; + events[1].mi.time = events[0].mi.time; + events[1].mi.dwExtraInfo = 0; + SendInput(sizeof(events) / sizeof(events[0]), + events, sizeof(events[0])); + + // restore mouse speed & acceleration + if (accelChanged) { + SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); + SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); + } + } +} + +bool +CMSWindowsSecondaryScreen::isMultimon() const +{ + SInt32 x0, y0, w, h; + getScreenShape(x0, y0, w, h); + return (w != GetSystemMetrics(SM_CXSCREEN) || + h != GetSystemMetrics(SM_CYSCREEN)); +} + // these tables map KeyID (a X windows KeySym) to virtual key codes. // if the key is an extended key then the entry is the virtual key // code | 0x100. keys that map to normal characters have a 0 entry diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index e845d49a..34d456f0 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -1,6 +1,12 @@ #ifndef CMSWINDOWSSECONDARYSCREEN_H #define CMSWINDOWSSECONDARYSCREEN_H +// ensure that we get SendInput() +#if _WIN32_WINNT <= 0x400 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x401 +#endif + #include "CMSWindowsScreen.h" #include "ISecondaryScreen.h" #include "CMutex.h" @@ -66,6 +72,12 @@ private: // get calling thread to use the input desktop void syncDesktop() const; + // warp the mouse to the specified position + void warpCursor(SInt32 x, SInt32 y); + + // returns true iff there appear to be multiple monitors + bool isMultimon() const; + // key and button queries and operations DWORD mapButton(ButtonID button, bool press) const; KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, @@ -86,6 +98,9 @@ private: // true if windows 95/98/me bool m_is95Family; + // true if system appears to have multiple monitors + bool m_multimon; + // the main loop's thread id DWORD m_threadID; From 3d27de39bb4ec75e3428f14aef3bf333bc27d2c8 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 20 Jun 2002 13:35:28 +0000 Subject: [PATCH 186/807] checkpoint. trying to fix a delay when sending clipboards on X. --- platform/CXWindowsClipboard.cpp | 140 ++++++++++++++++---------------- platform/CXWindowsClipboard.h | 10 +-- 2 files changed, 76 insertions(+), 74 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 4e232372..19ea653f 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -2,7 +2,7 @@ #include "CXWindowsUtil.h" #include "CThread.h" #include "CLog.h" -#include "TMethodJob.h" +#include "CStopwatch.h" #include #include @@ -79,7 +79,7 @@ CXWindowsClipboard::addRequest(Window owner, Window requestor, // at the given time. bool success = false; if (owner == m_window) { - log((CLOG_DEBUG "request for clipboard %d, target %d by 0x%08x (property=%d)", m_selection, target, requestor, property)); + log((CLOG_DEBUG1 "request for clipboard %d, target %d by 0x%08x (property=%d)", m_selection, target, requestor, property)); if (wasOwnedAtTime(time)) { if (target == m_atomMultiple) { // add a multiple request. property may not be None @@ -96,13 +96,13 @@ CXWindowsClipboard::addRequest(Window owner, Window requestor, } } else { - log((CLOG_DEBUG "failed, not owned at time %d", time)); + log((CLOG_DEBUG1 "failed, not owned at time %d", time)); } } if (!success) { // send failure - log((CLOG_DEBUG "failed")); + log((CLOG_DEBUG1 "failed")); insertReply(new CReply(requestor, target, time)); } @@ -138,14 +138,14 @@ CXWindowsClipboard::addSimpleRequest(Window requestor, if (type != None) { // success - log((CLOG_DEBUG "success")); + log((CLOG_DEBUG1 "success")); insertReply(new CReply(requestor, target, time, property, data, type, format)); return true; } else { // failure - log((CLOG_DEBUG "failed")); + log((CLOG_DEBUG1 "failed")); insertReply(new CReply(requestor, target, time)); return false; } @@ -286,24 +286,9 @@ CXWindowsClipboard::open(Time time) const m_open = true; m_time = time; - // get the time the clipboard ownership was taken by the current - // owner. - if (m_motif) { - m_timeOwned = motifGetTime(); - } - else { - m_timeOwned = icccmGetTime(); - } - - // if we can't get the time then use the time passed to us - if (m_timeOwned == 0) { - m_timeOwned = m_time; - } - - // if the cache is dirty then flush it - if (m_timeOwned != m_cacheTime) { - clearCache(); - } + // be sure to flush the cache later if it's dirty + m_checkCache = true; +checkCache(); return true; } @@ -327,6 +312,7 @@ CXWindowsClipboard::close() const IClipboard::Time CXWindowsClipboard::getTime() const { + checkCache(); return m_timeOwned; } @@ -361,6 +347,34 @@ CXWindowsClipboard::getFormat(Atom src) const return IClipboard::kNumFormats; } +void +CXWindowsClipboard::checkCache() const +{ + if (!m_checkCache) { + return; + } + m_checkCache = false; + + // get the time the clipboard ownership was taken by the current + // owner. + if (m_motif) { + m_timeOwned = motifGetTime(); + } + else { + m_timeOwned = icccmGetTime(); + } + + // if we can't get the time then use the time passed to us + if (m_timeOwned == 0) { + m_timeOwned = m_time; + } + + // if the cache is dirty then flush it + if (m_timeOwned != m_cacheTime) { + clearCache(); + } +} + void CXWindowsClipboard::clearCache() const { @@ -370,7 +384,8 @@ CXWindowsClipboard::clearCache() const void CXWindowsClipboard::doClearCache() { - m_cached = false; + m_checkCache = false; + m_cached = false; for (SInt32 index = 0; index < kNumFormats; ++index) { m_data[index] = ""; m_added[index] = false; @@ -381,6 +396,7 @@ void CXWindowsClipboard::fillCache() const { // get the selection data if not already cached + checkCache(); if (!m_cached) { const_cast(this)->doFillCache(); } @@ -395,8 +411,9 @@ CXWindowsClipboard::doFillCache() else { icccmFillCache(); } - m_cached = true; - m_cacheTime = m_timeOwned; + m_checkCache = false; + m_cached = true; + m_cacheTime = m_timeOwned; } void @@ -1007,6 +1024,7 @@ bool CXWindowsClipboard::wasOwnedAtTime(::Time time) const { // not owned if we've never owned the selection + checkCache(); if (m_timeOwned == 0) { return false; } @@ -1066,6 +1084,7 @@ CXWindowsClipboard::getTimestampData(CString& data, int* format) const assert(format != NULL); assert(sizeof(m_timeOwned) == 4); + checkCache(); data.append(reinterpret_cast(&m_timeOwned), 4); *format = 32; return m_atomTimestamp; @@ -1129,9 +1148,6 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, *m_actualTarget = None; *m_data = ""; - // get timeout atom - m_timeout = XInternAtom(display, "SYNERGY_TIMEOUT", False); - // delete target property XDeleteProperty(display, m_requestor, m_property); @@ -1145,21 +1161,36 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, XConvertSelection(display, selection, target, m_property, m_requestor, m_time); - // process selection events. have a separate thread send us an - // event after a timeout so we don't get locked up by badly - // behaved selection owners. - CThread timer(new TMethodJob( - this, - &CXWindowsClipboard::CICCCMGetClipboard::timeout, - display)); + // Xlib inexplicably omits the ability to wait for an event with + // a timeout. (it's inexplicable because there's no portable way + // to do it.) we'll poll until we have what we're looking for or + // a timeout expires. we use a timeout so we don't get locked up + // by badly behaved selection owners. XEvent xevent; + SInt32 lastPending = 0; + CStopwatch timeout(true); + static const double s_timeout = 0.2; // FIXME -- is this too short? while (!m_done && !m_failed) { - // process events - XIfEvent(display, &xevent, + // fail if timeout has expired + if (timeout.getTime() < s_timeout) { + m_failed = true; + break; + } + + // get how many events are pending now + SInt32 pending = XPending(display); + + // process events if there are more otherwise sleep + if (pending > lastPending) { + lastPending = pending; + XCheckIfEvent(display, &xevent, &CXWindowsClipboard::CICCCMGetClipboard::eventPredicate, reinterpret_cast(this)); + } + else { + CThread::sleep(0.01); + } } - timer.cancel(); // restore mask XSelectInput(display, m_requestor, attr.your_event_mask); @@ -1216,17 +1247,6 @@ CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate( // otherwise not interested return false; - case ClientMessage: - // done if this is the timeout message - if (xevent->xclient.window == m_requestor && - xevent->xclient.message_type == m_timeout) { - m_failed = true; - return true; - } - - // otherwise not interested - return false; - default: // not interested return false; @@ -1314,24 +1334,6 @@ CXWindowsClipboard::CICCCMGetClipboard::eventPredicate( return self->doEventPredicate(display, xevent) ? True : False; } -void -CXWindowsClipboard::CICCCMGetClipboard::timeout(void* vdisplay) -{ - // wait - CThread::sleep(0.2); // FIXME -- is this too short? - - // send wake up - Display* display = reinterpret_cast(vdisplay); - XEvent event; - event.xclient.type = ClientMessage; - event.xclient.display = display; - event.xclient.window = m_requestor; - event.xclient.message_type = m_timeout; - event.xclient.format = 8; - CXWindowsUtil::CErrorLock lock; - XSendEvent(display, m_requestor, False, 0, &event); -} - // // CXWindowsClipboard::CReply diff --git a/platform/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h index 92657adc..82d695e5 100644 --- a/platform/CXWindowsClipboard.h +++ b/platform/CXWindowsClipboard.h @@ -62,6 +62,10 @@ private: Window requestor, Atom target, ::Time time, Atom property); + // if not already checked then see if the cache is stale and, if so, + // clear it. this has the side effect of updating m_timeOwned. + void checkCache() const; + // clear the cache, resetting the cached flag and the added flag for // each format. void clearCache() const; @@ -84,7 +88,6 @@ private: bool motifOwnsClipboard() const; Time motifGetTime() const; void motifFillCache(); - // FIXME // // helper classes @@ -109,7 +112,6 @@ private: static Bool eventPredicate(Display* display, XEvent* event, XPointer arg); - void timeout(void*); private: Window m_requestor; @@ -129,9 +131,6 @@ private: // selection owner cannot convert to the requested type. Atom* m_actualTarget; - // property used in event to wake up event loop - Atom m_timeout; - public: // true iff the selection owner didn't follow ICCCM conventions bool m_error; @@ -240,6 +239,7 @@ private: mutable bool m_motif; // the added/cached clipboard data + mutable bool m_checkCache; bool m_cached; Time m_cacheTime; bool m_added[kNumFormats]; From 2423dc662d49b830352267b90883fa70b8eb7d01 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 20 Jun 2002 14:01:44 +0000 Subject: [PATCH 187/807] speeded up clipboard transfer by avoiding a selection request when it wasn't necessary. (in particular, we were getting the clipboard update time from the owner then emptying the clipboard, so we didn't need to get the time. worse, most owners don't support getting the time and we often timed out.) also fixed a multithread bug using the X display. we were using a CThread to send an event after a timeout while we were waiting in XIfEvent(). this necessarily involved two threads calling into Xlib at once, which is not allowed. now using polling to do the timeout because Xlib doesn't have a function to get events with a timeout. --- platform/CXWindowsClipboard.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 19ea653f..1d0fbb4d 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -288,7 +288,6 @@ CXWindowsClipboard::open(Time time) const // be sure to flush the cache later if it's dirty m_checkCache = true; -checkCache(); return true; } @@ -1161,6 +1160,9 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, XConvertSelection(display, selection, target, m_property, m_requestor, m_time); + // synchronize with server before we start following timeout countdown + XSync(display, False); + // Xlib inexplicably omits the ability to wait for an event with // a timeout. (it's inexplicable because there's no portable way // to do it.) we'll poll until we have what we're looking for or @@ -1169,10 +1171,10 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, XEvent xevent; SInt32 lastPending = 0; CStopwatch timeout(true); - static const double s_timeout = 0.2; // FIXME -- is this too short? + static const double s_timeout = 0.25; // FIXME -- is this too short? while (!m_done && !m_failed) { // fail if timeout has expired - if (timeout.getTime() < s_timeout) { + if (timeout.getTime() >= s_timeout) { m_failed = true; break; } @@ -1183,9 +1185,12 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, // process events if there are more otherwise sleep if (pending > lastPending) { lastPending = pending; - XCheckIfEvent(display, &xevent, + while (!m_done && !m_failed && + XCheckIfEvent(display, &xevent, &CXWindowsClipboard::CICCCMGetClipboard::eventPredicate, - reinterpret_cast(this)); + reinterpret_cast(this))) { + lastPending = XPending(display); + } } else { CThread::sleep(0.01); From cec075cb604dce1f6f7ec3797a0285f2fc692c02 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 20 Jun 2002 16:27:49 +0000 Subject: [PATCH 188/807] fixed bug introduced by previous checkin. calling XCheckIfEvent() multiple times is *not* the same as calling XIfEvent() because the former will re-encounter events that it didn't process previously. to make things simple it now pulls events off the queue and saves them if not processed for selection transfer and puts them back afterwards. --- platform/CXWindowsClipboard.cpp | 47 ++++++++++++++------------------- platform/CXWindowsClipboard.h | 6 +---- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 1d0fbb4d..f894789b 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -3,6 +3,7 @@ #include "CThread.h" #include "CLog.h" #include "CStopwatch.h" +#include "stdvector.h" #include #include @@ -1169,7 +1170,7 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, // a timeout expires. we use a timeout so we don't get locked up // by badly behaved selection owners. XEvent xevent; - SInt32 lastPending = 0; + std::vector events; CStopwatch timeout(true); static const double s_timeout = 0.25; // FIXME -- is this too short? while (!m_done && !m_failed) { @@ -1179,17 +1180,14 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, break; } - // get how many events are pending now - SInt32 pending = XPending(display); - - // process events if there are more otherwise sleep - if (pending > lastPending) { - lastPending = pending; - while (!m_done && !m_failed && - XCheckIfEvent(display, &xevent, - &CXWindowsClipboard::CICCCMGetClipboard::eventPredicate, - reinterpret_cast(this))) { - lastPending = XPending(display); + // process events if any otherwise sleep + if (XPending(display) > 0) { + while (!m_done && !m_failed && XPending(display) > 0) { + XNextEvent(display, &xevent); + if (!processEvent(display, &xevent)) { + // not processed so save it + events.push_back(xevent); + } } } else { @@ -1197,6 +1195,11 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, } } + // put unprocessed events back + for (UInt32 i = events.size(); i > 0; --i) { + XPutBackEvent(display, &events[i - 1]); + } + // restore mask XSelectInput(display, m_requestor, attr.your_event_mask); @@ -1206,7 +1209,7 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, } bool -CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate( +CXWindowsClipboard::CICCCMGetClipboard::processEvent( Display* display, XEvent* xevent) { // process event @@ -1244,7 +1247,8 @@ CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate( xevent->xproperty.atom == m_property && xevent->xproperty.state == PropertyNewValue) { if (!m_reading) { - return false; + // we haven't gotten the SelectionNotify yet + return true; } break; } @@ -1323,20 +1327,9 @@ log((CLOG_INFO " INCR secondary chunk")); // FIXME m_done = true; } - // say we're not interested in this event if the conversion is - // incremental. that'll cause this method to be called again - // when there's more data. we finally finish the incremental - // copy when we read a 0 byte property. + // this event has been processed logc(!m_incr, (CLOG_DEBUG1 " got data, %d bytes", m_data->size())); - return !m_incr; -} - -Bool -CXWindowsClipboard::CICCCMGetClipboard::eventPredicate( - Display* display, XEvent* xevent, XPointer arg) -{ - CICCCMGetClipboard* self = reinterpret_cast(arg); - return self->doEventPredicate(display, xevent) ? True : False; + return true; } diff --git a/platform/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h index 82d695e5..940ab71c 100644 --- a/platform/CXWindowsClipboard.h +++ b/platform/CXWindowsClipboard.h @@ -107,11 +107,7 @@ private: Atom* actualTarget, CString* data); private: - bool doEventPredicate(Display* display, - XEvent* event); - static Bool eventPredicate(Display* display, - XEvent* event, - XPointer arg); + bool processEvent(Display* display, XEvent* event); private: Window m_requestor; From a65bb197239d27d0a7271e0cf4835248501d0d75 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Jun 2002 15:14:32 +0000 Subject: [PATCH 189/807] signal handler thread now dies when SIGABRT is raised. ignoring SIGABRT in sigwait() seems to be a bug in the linux pthread library. --- mt/CThreadRep.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index ad2bedca..604a3650 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -168,10 +168,10 @@ CThreadRep::initThreads() sigaddset(&sigset, SIGTERM); pthread_sigmask(SIG_BLOCK, &sigset, NULL); - // fire up the INT and TERM signal handler thread - // FIXME -- i've seen this thread hanging around after the app - // asserted. should figure out how it stays alive and prevent - // it from happening. + // fire up the INT and TERM signal handler thread. we could + // instead arrange to catch and handle these signals but + // we'd be unable to cancel the main thread since no pthread + // calls are allowed in a signal handler. int status = pthread_create(&s_signalThread, NULL, &CThreadRep::threadSignalHandler, getCurrentThreadRep()); @@ -285,7 +285,9 @@ CThreadRep::doThreadFunc() void* result = NULL; try { // go + log((CLOG_DEBUG1 "thread %p entry", this)); m_job->run(); + log((CLOG_DEBUG1 "thread %p exit", this)); } catch (XThreadCancel&) { @@ -471,12 +473,25 @@ CThreadRep::threadSignalHandler(void* vrep) { CThreadRep* mainThreadRep = reinterpret_cast(vrep); + // detach + pthread_detach(pthread_self()); + // add signal to mask sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGTERM); + // also wait on SIGABRT. on linux (others?) this thread (process) + // will persist after all the other threads evaporate due to an + // assert unless we wait on SIGABRT. that means our resources (like + // the socket we're listening on) are not released and never will be + // until the lingering thread is killed. i don't know why sigwait() + // should protect the thread from being killed. note that sigwait() + // doesn't actually return if we receive SIGABRT and, for some + // reason, we don't have to block SIGABRT. + sigaddset(&sigset, SIGABRT); + // we exit the loop via thread cancellation in sigwait() for (;;) { // wait From b83c0c5928dfd78bf6e8e3e1953d9fd149680e82 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Jun 2002 15:15:34 +0000 Subject: [PATCH 190/807] now blocking SIGINT and SIGTERM in restart function. the child should handle the signal and terminate. then the restart function will exit. --- platform/CUnixPlatform.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index 21f61710..c1210458 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -109,6 +109,13 @@ CUnixPlatform::installDaemonLogger(const char* name) int CUnixPlatform::restart(RestartFunc func, int minErrorCode) { + // rely on child to catch these signals + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); + for (;;) { switch (fork()) { default: From e2ee2371e0c09f65826dddf61d3ce6f4bdc6c152 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Jun 2002 15:18:01 +0000 Subject: [PATCH 191/807] some cleanup. also fixed a race condition when adding threads to the thread list: the child thread would add itself to the list which means there could be a time interval in the parent where the child thread exists but isn't on the list. the parent now does the adding and removing. --- server/CServer.cpp | 230 +++++++++++++++++++-------------------------- server/CServer.h | 43 +++------ 2 files changed, 111 insertions(+), 162 deletions(-) diff --git a/server/CServer.cpp b/server/CServer.cpp index aca6a6c4..22c79ee5 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -38,7 +38,6 @@ const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; CServer::CServer(const CString& serverName) : m_name(serverName), - m_cleanupSize(&m_mutex, 0), m_primary(NULL), m_active(NULL), m_primaryInfo(NULL), @@ -81,12 +80,13 @@ CServer::run() } // start listening for new clients - CThread(new TMethodJob(this, &CServer::acceptClients)); + startThread(new TMethodJob(this, &CServer::acceptClients)); // start listening for HTTP requests if (m_config.getHTTPAddress().isValid()) { m_httpServer = new CHTTPServer(this); - CThread(new TMethodJob(this, &CServer::acceptHTTPClients)); + startThread(new TMethodJob(this, + &CServer::acceptHTTPClients)); } // handle events @@ -95,7 +95,7 @@ CServer::run() // clean up log((CLOG_NOTE "stopping server")); - cleanupThreads(); + stopThreads(); delete m_httpServer; m_httpServer = NULL; closePrimaryScreen(); @@ -105,7 +105,7 @@ CServer::run() // clean up log((CLOG_NOTE "stopping server")); - cleanupThreads(); + stopThreads(); delete m_httpServer; m_httpServer = NULL; if (m_primary != NULL) { @@ -115,7 +115,7 @@ CServer::run() catch (XThread&) { // clean up log((CLOG_NOTE "stopping server")); - cleanupThreads(); + stopThreads(); delete m_httpServer; m_httpServer = NULL; if (m_primary != NULL) { @@ -128,7 +128,7 @@ CServer::run() // clean up log((CLOG_NOTE "stopping server")); - cleanupThreads(); + stopThreads(); delete m_httpServer; m_httpServer = NULL; if (m_primary != NULL) { @@ -149,7 +149,7 @@ CServer::shutdown() { // stop all running threads but don't wait too long since some // threads may be unable to proceed until this thread returns. - cleanupThreads(3.0); + stopThreads(3.0); // done with the HTTP server delete m_httpServer; @@ -208,6 +208,9 @@ CServer::setConfig(const CConfig& config) index->wait(); } + // clean up thread list + reapThreads(); + // cut over CLock lock(&m_mutex); m_config = config; @@ -993,16 +996,85 @@ CServer::getNeighbor(CScreenInfo* src, return dst; } +void +CServer::startThread(IJob* job) +{ + CLock lock(&m_mutex); + doReapThreads(m_threads); + CThread* thread = new CThread(job); + m_threads.push_back(thread); + log((CLOG_DEBUG1 "started thread %p", thread)); +} + +void +CServer::stopThreads(double timeout) +{ + log((CLOG_DEBUG1 "stopping threads")); + + // swap thread list so nobody can mess with it + CThreadList threads; + { + CLock lock(&m_mutex); + threads.swap(m_threads); + } + + // cancel every thread + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + CThread* thread = *index; + thread->cancel(); + } + + // now wait for the threads + CStopwatch timer(true); + while (threads.size() > 0 && (timeout < 0.0 || timer.getTime() < timeout)) { + doReapThreads(threads); + CThread::sleep(0.01); + } + + // delete remaining threads + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + CThread* thread = *index; + log((CLOG_DEBUG1 "reaped running thread %p", thread)); + delete thread; + } + + log((CLOG_DEBUG1 "stopped threads")); +} + +void +CServer::reapThreads() +{ + CLock lock(&m_mutex); + doReapThreads(m_threads); +} + +void +CServer::doReapThreads(CThreadList& threads) +{ + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ) { + CThread* thread = *index; + if (thread->wait(0.0)) { + // thread terminated + index = threads.erase(index); + log((CLOG_DEBUG1 "reaped thread %p", thread)); + delete thread; + } + else { + // thread is running + ++index; + } + } +} + #include "CTCPListenSocket.h" void CServer::acceptClients(void*) { log((CLOG_DEBUG1 "starting to wait for clients")); - // add this thread to the list of threads to cancel. remove from - // list in d'tor. - CCleanupNote cleanupNote(this); - std::auto_ptr listen; try { // create socket listener @@ -1041,7 +1113,7 @@ CServer::acceptClients(void*) CThread::testCancel(); // start handshake thread - CThread(new TMethodJob( + startThread(new TMethodJob( this, &CServer::handshakeClient, socket)); } } @@ -1060,10 +1132,6 @@ CServer::handshakeClient(void* vsocket) assert(vsocket != NULL); std::auto_ptr socket(reinterpret_cast(vsocket)); - // add this thread to the list of threads to cancel. remove from - // list in d'tor. - CCleanupNote cleanupNote(this); - CString name(""); try { // get the input and output streams @@ -1088,8 +1156,8 @@ CServer::handshakeClient(void* vsocket) assign(input, new CInputPacketStream(srcInput, own), IInputStream); assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); + bool connected = false; std::auto_ptr protocol; - std::auto_ptr connectedNote; try { { // give the client a limited time to complete the handshake @@ -1135,8 +1203,8 @@ CServer::handshakeClient(void* vsocket) IServerProtocol); // client is now pending - assign(connectedNote, new CConnectionNote(this, - name, protocol.get()), CConnectionNote); + addConnection(name, protocol.get()); + connected = true; // ask and wait for the client's info log((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str())); @@ -1173,9 +1241,18 @@ CServer::handshakeClient(void* vsocket) log((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); CProtocolUtil::writef(output.get(), kMsgEBad); } + catch (...) { + if (connected) { + removeConnection(name); + } + throw; + } // flush any pending output output.get()->flush(); + if (connected) { + removeConnection(name); + } } catch (XBase& e) { // misc error @@ -1189,10 +1266,6 @@ CServer::acceptHTTPClients(void*) { log((CLOG_DEBUG1 "starting to wait for HTTP clients")); - // add this thread to the list of threads to cancel. remove from - // list in d'tor. - CCleanupNote cleanupNote(this); - std::auto_ptr listen; try { // create socket listener @@ -1241,7 +1314,7 @@ CServer::acceptHTTPClients(void*) CThread::testCancel(); // handle HTTP request - CThread(new TMethodJob( + startThread(new TMethodJob( this, &CServer::processHTTPRequest, socket)); } } @@ -1255,10 +1328,6 @@ CServer::acceptHTTPClients(void*) void CServer::processHTTPRequest(void* vsocket) { - // add this thread to the list of threads to cancel. remove from - // list in d'tor. - CCleanupNote cleanupNote(this); - IDataSocket* socket = reinterpret_cast(vsocket); try { // process the request and force delivery @@ -1439,73 +1508,6 @@ CServer::closePrimaryScreen() m_primary = NULL; } -void -CServer::addCleanupThread(const CThread& thread) -{ - CLock lock(&m_mutex); - m_cleanupList.insert(m_cleanupList.begin(), new CThread(thread)); - m_cleanupSize = m_cleanupSize + 1; -} - -void -CServer::removeCleanupThread(const CThread& thread) -{ - CLock lock(&m_mutex); - for (CThreadList::iterator index = m_cleanupList.begin(); - index != m_cleanupList.end(); ++index) { - if (**index == thread) { - CThread* thread = *index; - m_cleanupList.erase(index); - m_cleanupSize = m_cleanupSize - 1; - if (m_cleanupSize == 0) { - m_cleanupSize.broadcast(); - } - delete thread; - return; - } - } -} - -void -CServer::cleanupThreads(double timeout) -{ - log((CLOG_DEBUG1 "cleaning up threads")); - - // first cancel every thread except the current one (with mutex - // locked so the cleanup list won't change). - CLock lock(&m_mutex); - CThread current(CThread::getCurrentThread()); - SInt32 minCount = 0; - for (CThreadList::iterator index = m_cleanupList.begin(); - index != m_cleanupList.end(); ++index) { - CThread* thread = *index; - if (thread != ¤t) { - thread->cancel(); - } - else { - minCount = 1; - } - } - - // now wait for the threads (with mutex unlocked as each thread - // will remove itself from the list) - CStopwatch timer(true); - while (m_cleanupSize > minCount) { - m_cleanupSize.wait(timer, timeout); - } - - // delete remaining threads - for (CThreadList::iterator index = m_cleanupList.begin(); - index != m_cleanupList.end(); ++index) { - CThread* thread = *index; - delete thread; - } - m_cleanupList.clear(); - m_cleanupSize = 0; - - log((CLOG_DEBUG1 "cleaned up threads")); -} - CServer::CScreenInfo* CServer::addConnection(const CString& name, IServerProtocol* protocol) { @@ -1563,42 +1565,6 @@ CServer::removeConnection(const CString& name) } -// -// CServer::CCleanupNote -// - -CServer::CCleanupNote::CCleanupNote(CServer* server) : - m_server(server) -{ - assert(m_server != NULL); - m_server->addCleanupThread(CThread::getCurrentThread()); -} - -CServer::CCleanupNote::~CCleanupNote() -{ - m_server->removeCleanupThread(CThread::getCurrentThread()); -} - - -// -// CServer::CConnectionNote -// - -CServer::CConnectionNote::CConnectionNote(CServer* server, - const CString& name, IServerProtocol* protocol) : - m_server(server), - m_name(name) -{ - assert(m_server != NULL); - m_server->addConnection(m_name, protocol); -} - -CServer::CConnectionNote::~CConnectionNote() -{ - m_server->removeConnection(m_name); -} - - // // CServer::CScreenInfo // diff --git a/server/CServer.h b/server/CServer.h index 318ec4a7..a4e64678 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -89,25 +89,7 @@ protected: bool onCommandKey(KeyID, KeyModifierMask, bool down); private: - class CCleanupNote { - public: - CCleanupNote(CServer*); - ~CCleanupNote(); - - private: - CServer* m_server; - }; - - class CConnectionNote { - public: - CConnectionNote(CServer*, const CString&, IServerProtocol*); - ~CConnectionNote(); - - private: - bool m_pending; - CServer* m_server; - CString m_name; - }; + typedef std::list CThreadList; class CScreenInfo { public: @@ -176,8 +158,17 @@ private: // update the clipboard if owned by the primary screen void updatePrimaryClipboard(ClipboardID); - // cancel running threads - void cleanupThreads(double timeout = -1.0); + // start a thread, adding it to the list of threads + void startThread(IJob* adopted); + + // cancel running threads, waiting at most timeout seconds for + // them to finish. + void stopThreads(double timeout = -1.0); + + // reap threads, clearing finished threads from the thread list. + // doReapThreads does the work on the given thread list. + void reapThreads(); + void doReapThreads(CThreadList&); // thread method to accept incoming client connections void acceptClients(void*); @@ -191,18 +182,11 @@ private: // thread method to process HTTP requests void processHTTPRequest(void*); - // thread cleanup list maintenance - friend class CCleanupNote; - void addCleanupThread(const CThread& thread); - void removeCleanupThread(const CThread& thread); - // connection list maintenance - friend class CConnectionNote; CScreenInfo* addConnection(const CString& name, IServerProtocol*); void removeConnection(const CString& name); private: - typedef std::list CThreadList; typedef std::map CScreenList; class CClipboardInfo { public: @@ -225,8 +209,7 @@ private: ISocketFactory* m_socketFactory; ISecurityFactory* m_securityFactory; - CThreadList m_cleanupList; - CCondVar m_cleanupSize; + CThreadList m_threads; IPrimaryScreen* m_primary; CScreenList m_screens; From 327af03d3da032d40e12f1755c8a7ccdfdd48680 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Jun 2002 16:19:08 +0000 Subject: [PATCH 192/807] fixed CTCPSocket::connect() to allow cancellation. --- net/CNetwork.cpp | 37 ++++++++++++++++++++++++++++++--- net/CNetwork.h | 7 +++++++ net/CTCPListenSocket.cpp | 1 + net/CTCPSocket.cpp | 45 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 84 insertions(+), 6 deletions(-) diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp index 2122a74d..0dd37ee8 100644 --- a/net/CNetwork.cpp +++ b/net/CNetwork.cpp @@ -36,6 +36,7 @@ struct protoent FAR * (PASCAL FAR *CNetwork::getprotobynumber)(int proto); struct protoent FAR * (PASCAL FAR *CNetwork::getprotobyname)(const char FAR * name); int (PASCAL FAR *CNetwork::getsockerror)(void); int (PASCAL FAR *CNetwork::gethosterror)(void); +int (PASCAL FAR *CNetwork::setblocking)(CNetwork::Socket s, bool blocking); #if WINDOWS_LIKE @@ -207,9 +208,10 @@ CNetwork::init2( setfunc(WSACleanup, WSACleanup, int (PASCAL FAR *)(void)); setfunc(__WSAFDIsSet, __WSAFDIsSet, int (PASCAL FAR *)(CNetwork::Socket, fd_set FAR *)); setfunc(select, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); - poll = poll2; - read = read2; - write = write2; + poll = poll2; + read = read2; + write = write2; + setblocking = setblocking2; s_networkModule = module; } @@ -295,11 +297,19 @@ CNetwork::write2(Socket s, const void FAR* buf, size_t len) return send(s, buf, len, 0); } +int PASCAL FAR +CNetwork::setblocking2(CNetwork::Socket s, bool blocking) +{ + int flag = blocking ? 0 : 1; + return ioctlsocket(s, FIONBIO, &flag); +} + #endif #if UNIX_LIKE #include +#include #include #include @@ -352,6 +362,26 @@ mygethostname(char* name, int namelen) return gethostname(name, namelen); } +static +int +mysetblocking(CNetwork::Socket s, bool blocking) +{ + int mode = fcntl(s, F_GETFL, 0); + if (mode == -1) { + return -1; + } + if (blocking) { + mode &= ~O_NDELAY; + } + else { + mode |= O_NDELAY; + } + if (fcntl(s, F_SETFL, mode) < 0) { + return -1; + } + return 0; +} + const int CNetwork::Error = -1; const CNetwork::Socket CNetwork::Null = -1; @@ -388,6 +418,7 @@ CNetwork::init() setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); setfunc(getsockerror, myerrno, int (PASCAL FAR *)(void)); setfunc(gethosterror, myherrno, int (PASCAL FAR *)(void)); + setfunc(setblocking, mysetblocking, int (PASCAL FAR *)(Socket, bool)); } void diff --git a/net/CNetwork.h b/net/CNetwork.h index cf090ea1..2f2a7883 100644 --- a/net/CNetwork.h +++ b/net/CNetwork.h @@ -84,8 +84,10 @@ public: enum { #if WINDOWS_LIKE kEADDRINUSE = WSAEADDRINUSE, + kECONNECTING = WSAEWOULDBLOCK, #elif UNIX_LIKE kEADDRINUSE = EADDRINUSE, + kECONNECTING = EINPROGRESS, #endif kNone = 0 }; @@ -139,12 +141,17 @@ public: static int (PASCAL FAR *getsockerror)(void); static int (PASCAL FAR *gethosterror)(void); + // convenience functions (only available after init()) + + static int (PASCAL FAR *setblocking)(CNetwork::Socket s, bool blocking); + #if WINDOWS_LIKE private: static void init2(HMODULE); static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout); static ssize_t PASCAL FAR read2(Socket s, void FAR * buf, size_t len); static ssize_t PASCAL FAR write2(Socket s, const void FAR * buf, size_t len); + static int PASCAL FAR setblocking2(CNetwork::Socket s, bool blocking); static int (PASCAL FAR *WSACleanup)(void); static int (PASCAL FAR *__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); static int (PASCAL FAR *select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); diff --git a/net/CTCPListenSocket.cpp b/net/CTCPListenSocket.cpp index fa7950ce..8e8f36d9 100644 --- a/net/CTCPListenSocket.cpp +++ b/net/CTCPListenSocket.cpp @@ -45,6 +45,7 @@ CTCPListenSocket::bind(const CNetworkAddress& addr) IDataSocket* CTCPListenSocket::accept() { + // accept asynchronously so we can check for cancellation CNetwork::PollEntry pfds[1]; pfds[0].fd = m_fd; pfds[0].events = CNetwork::kPOLLIN; diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index 8b3c5bc9..c6e1970e 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -109,13 +109,52 @@ CTCPSocket::close() void CTCPSocket::connect(const CNetworkAddress& addr) { - CThread::testCancel(); + // connect asynchronously so we can check for cancellation + CNetwork::setblocking(m_fd, false); if (CNetwork::connect(m_fd, addr.getAddress(), addr.getAddressLength()) == CNetwork::Error) { - CThread::testCancel(); - throw XSocketConnect(); + // check for failure + if (CNetwork::getsockerror() != CNetwork::kECONNECTING) { + CNetwork::setblocking(m_fd, true); + throw XSocketConnect(); + } + + // wait for connection or failure + CNetwork::PollEntry pfds[1]; + pfds[0].fd = m_fd; + pfds[0].events = CNetwork::kPOLLOUT; + for (;;) { + CThread::testCancel(); + const int status = CNetwork::poll(pfds, 1, 10); + if (status > 0) { + if ((pfds[0].revents & (CNetwork::kPOLLERR | + CNetwork::kPOLLNVAL)) != 0) { + // connection failed + CNetwork::setblocking(m_fd, true); + throw XSocketConnect(); + } + if ((pfds[0].revents & CNetwork::kPOLLOUT) != 0) { + int error; + CNetwork::AddressLength size = sizeof(error); + if (CNetwork::getsockopt(m_fd, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), + &size) == CNetwork::Error || + error != 0) { + // connection failed + CNetwork::setblocking(m_fd, true); + throw XSocketConnect(); + } + + // connected! + break; + } + } + } } + // back to blocking + CNetwork::setblocking(m_fd, true); + // start servicing the socket m_connected = kReadWrite; m_thread = new CThread(new TMethodJob( From a996db6600acb5af0f0ec65ec9e320ec7d807ef7 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Jun 2002 16:29:35 +0000 Subject: [PATCH 193/807] now trying to convert hostname as a dot notation address before trying name lookup. not all platforms will do this for us in gethostbyname(). --- net/CNetworkAddress.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/CNetworkAddress.cpp b/net/CNetworkAddress.cpp index debd9d32..726e4392 100644 --- a/net/CNetworkAddress.cpp +++ b/net/CNetworkAddress.cpp @@ -99,6 +99,18 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : return; } + // convert dot notation to address + unsigned long addr = CNetwork::inet_addr(hostname.c_str()); + if (addr != INADDR_NONE) { + struct sockaddr_in* inetAddress = reinterpret_cast< + struct sockaddr_in*>(&m_address); + inetAddress->sin_family = AF_INET; + inetAddress->sin_port = CNetwork::swaphtons(port); + inetAddress->sin_addr.s_addr = addr; + memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); + return; + } + // look up name struct hostent* hent = CNetwork::gethostbyname(hostname.c_str()); if (hent == NULL) { From e0f66d162fc793a0183d50927ea09dee67be675e Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Jun 2002 17:54:22 +0000 Subject: [PATCH 194/807] ported network changes to win32. --- net/CNetwork.cpp | 8 ++++---- net/CNetwork.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/net/CNetwork.cpp b/net/CNetwork.cpp index 0dd37ee8..f0056d60 100644 --- a/net/CNetwork.cpp +++ b/net/CNetwork.cpp @@ -10,7 +10,7 @@ CNetwork::Socket (PASCAL FAR *CNetwork::accept)(CNetwork::Socket s, CNetwork::Ad int (PASCAL FAR *CNetwork::bind)(CNetwork::Socket s, const CNetwork::Address FAR *addr, CNetwork::AddressLength namelen); int (PASCAL FAR *CNetwork::close)(CNetwork::Socket s); int (PASCAL FAR *CNetwork::connect)(CNetwork::Socket s, const CNetwork::Address FAR *name, CNetwork::AddressLength namelen); -int (PASCAL FAR *CNetwork::ioctl)(CNetwork::Socket s, int cmd, ...); +int (PASCAL FAR *CNetwork::ioctl)(CNetwork::Socket s, int cmd, void FAR *); int (PASCAL FAR *CNetwork::getpeername)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); int (PASCAL FAR *CNetwork::getsockname)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); int (PASCAL FAR *CNetwork::getsockopt)(CNetwork::Socket s, int level, int optname, void FAR * optval, CNetwork::AddressLength FAR *optlen); @@ -182,7 +182,7 @@ CNetwork::init2( setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); setfunc(close, closesocket, int (PASCAL FAR *)(Socket s)); setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); - setfunc(ioctl, ioctlsocket, int (PASCAL FAR *)(Socket s, int cmd, ...)); + setfunc(ioctl, ioctlsocket, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); @@ -301,7 +301,7 @@ int PASCAL FAR CNetwork::setblocking2(CNetwork::Socket s, bool blocking) { int flag = blocking ? 0 : 1; - return ioctlsocket(s, FIONBIO, &flag); + return ioctl(s, FIONBIO, &flag); } #endif @@ -392,7 +392,7 @@ CNetwork::init() setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); setfunc(close, close, int (PASCAL FAR *)(Socket s)); setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); - setfunc(ioctl, ioctl, int (PASCAL FAR *)(Socket s, int cmd, ...)); + setfunc(ioctl, ioctl, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); diff --git a/net/CNetwork.h b/net/CNetwork.h index 2f2a7883..1bc2e4e2 100644 --- a/net/CNetwork.h +++ b/net/CNetwork.h @@ -114,7 +114,7 @@ public: static int (PASCAL FAR *bind)(Socket s, const Address FAR *addr, AddressLength namelen); static int (PASCAL FAR *close)(Socket s); static int (PASCAL FAR *connect)(Socket s, const Address FAR *name, AddressLength namelen); - static int (PASCAL FAR *ioctl)(Socket s, int cmd, ...); + static int (PASCAL FAR *ioctl)(Socket s, int cmd, void FAR *); static int (PASCAL FAR *getpeername)(Socket s, Address FAR *name, AddressLength FAR * namelen); static int (PASCAL FAR *getsockname)(Socket s, Address FAR *name, AddressLength FAR * namelen); static int (PASCAL FAR *getsockopt)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen); From ede18cb7f3853aa45adda56393eb5f67e9fe304e Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Jun 2002 17:55:47 +0000 Subject: [PATCH 195/807] cleaned up some minor bugs. --- client/CClient.cpp | 43 +++++++++++++++----------- client/CClient.h | 8 +++-- client/CXWindowsSecondaryScreen.cpp | 1 + client/client.cpp | 11 +++++-- server/CServer.cpp | 47 ++++++++++++++++++----------- server/CServer.h | 7 ++++- server/server.cpp | 13 ++++++-- 7 files changed, 86 insertions(+), 44 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 1206176d..e2af4f15 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -52,25 +52,36 @@ CClient::camp(bool on) m_camp = on; } +bool +CClient::open() +{ + // open the screen + try { + log((CLOG_INFO "opening screen")); + openSecondaryScreen(); + return true; + } + catch (XScreenOpenFailure&) { + // can't open screen yet. wait a few seconds to retry. + CThread::sleep(3.0); + log((CLOG_INFO "failed to open screen")); + return false; + } +} + bool CClient::run(const CNetworkAddress& serverAddress) { + // check preconditions + { + CLock lock(&m_mutex); + assert(m_screen != NULL); + } + CThread* thread = NULL; try { log((CLOG_NOTE "starting client")); - // connect to secondary screen - while (m_screen == NULL) { - try { - openSecondaryScreen(); - } - catch (XScreenOpenFailure&) { - // can't open screen yet. wait a few seconds to retry. - log((CLOG_INFO "failed to open screen. waiting to retry.")); - CThread::sleep(3.0); - } - } - // start server interactions m_serverAddress = &serverAddress; thread = new CThread(new TMethodJob(this, &CClient::runSession)); @@ -108,9 +119,7 @@ CClient::run(const CNetworkAddress& serverAddress) thread->wait(); delete thread; } - if (m_screen != NULL) { - closeSecondaryScreen(); - } + closeSecondaryScreen(); throw; } catch (...) { @@ -123,9 +132,7 @@ CClient::run(const CNetworkAddress& serverAddress) thread->wait(); delete thread; } - if (m_screen != NULL) { - closeSecondaryScreen(); - } + closeSecondaryScreen(); throw; } } diff --git a/client/CClient.h b/client/CClient.h index d2dacc07..2a99d1ec 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -24,13 +24,17 @@ public: // not call this while in run(). void camp(bool on); + // open the client's screen + bool open(); + // start the client. does not return until quit() is called. // returns true if the client ever connected to the server // successfully. may also throw exceptions after successfully - // connecting. + // connecting. a successful open() must preceed this call. bool run(const CNetworkAddress& serverAddress); - // tell client to exit gracefully + // tell client to exit run() gracefully. this must only be called + // after a successful open(). void quit(); // handle events on client's screen diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 60b0c0dc..ba881460 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -76,6 +76,7 @@ CXWindowsSecondaryScreen::run() void CXWindowsSecondaryScreen::stop() { + CDisplayLock display(this); doStop(); } diff --git a/client/client.cpp b/client/client.cpp index 9d4a13de..21d23b2d 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -81,6 +81,11 @@ realMain(CMutex* mutex) // create client s_client = new CClient(s_name); s_client->camp(s_camp); + if (!s_client->open()) { + delete s_client; + s_client = NULL; + return 16; + } // run client if (mutex != NULL) { @@ -96,7 +101,6 @@ realMain(CMutex* mutex) // clean up delete s_client; s_client = NULL; - CNetwork::cleanup(); CLog::setLock(NULL); s_logMutex = NULL; @@ -109,7 +113,6 @@ realMain(CMutex* mutex) } delete s_client; s_client = NULL; - CNetwork::cleanup(); CLog::setLock(NULL); s_logMutex = NULL; throw; @@ -602,6 +605,8 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) result = restartableMain(); } + CNetwork::cleanup(); + return result; } @@ -641,6 +646,8 @@ main(int argc, char** argv) result = restartableMain(); } + CNetwork::cleanup(); + return result; } diff --git a/server/CServer.cpp b/server/CServer.cpp index 22c79ee5..67b6d562 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -55,30 +55,41 @@ CServer::~CServer() // do nothing } +bool +CServer::open() +{ + // open the screen + try { + log((CLOG_INFO "opening screen")); + openPrimaryScreen(); + return true; + } + catch (XScreenOpenFailure&) { + // can't open screen yet. wait a few seconds to retry. + CThread::sleep(3.0); + log((CLOG_INFO "failed to open screen")); + return false; + } + catch (XUnknownClient& e) { + // can't open screen yet. wait a few seconds to retry. + CThread::sleep(3.0); + log((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); + return false; + } +} + void CServer::run() { + // check preconditions + { + CLock lock(&m_mutex); + assert(m_primary != NULL); + } + try { log((CLOG_NOTE "starting server")); - // connect to primary screen - while (m_primary == NULL) { - try { - openPrimaryScreen(); - } - catch (XScreenOpenFailure&) { - // can't open screen yet. wait a few seconds to retry. - log((CLOG_INFO "failed to open screen. waiting to retry.")); - CThread::sleep(3.0); - } - catch (XUnknownClient& e) { - // can't open screen yet. wait a few seconds to retry. - log((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); - log((CLOG_NOTE "stopping server")); - return; - } - } - // start listening for new clients startThread(new TMethodJob(this, &CServer::acceptClients)); diff --git a/server/CServer.h b/server/CServer.h index a4e64678..a589ecb4 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -27,10 +27,15 @@ public: // manipulators + // open the server's screen + bool open(); + // start the server. does not return until quit() is called. + // this must be preceeded by a successful call to open(). void run(); - // tell server to exit gracefully + // tell server to exit gracefully. this may only be called + // after a successful open(). void quit(); // tell the server to shutdown. this is called in an emergency diff --git a/server/server.cpp b/server/server.cpp index 18f55ec0..5e3ec8b3 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -113,13 +113,18 @@ realMain(CMutex* mutex) // create server s_server = new CServer(s_name); + s_server->setConfig(s_config); + if (!s_server->open()) { + delete s_server; + s_server = NULL; + return 16; + } // run server (unlocked) if (mutex != NULL) { mutex->unlock(); } locked = false; - s_server->setConfig(s_config); s_server->run(); locked = true; if (mutex != NULL) { @@ -129,7 +134,6 @@ realMain(CMutex* mutex) // clean up delete s_server; s_server = NULL; - CNetwork::cleanup(); CLog::setLock(NULL); s_logMutex = NULL; } @@ -140,7 +144,6 @@ realMain(CMutex* mutex) } delete s_server; s_server = NULL; - CNetwork::cleanup(); CLog::setLock(NULL); s_logMutex = NULL; throw; @@ -723,6 +726,8 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) result = restartableMain(); } + CNetwork::cleanup(); + return result; } @@ -765,6 +770,8 @@ main(int argc, char** argv) result = restartableMain(); } + CNetwork::cleanup(); + return result; } From c961115686e702ae3173a50528e5b23c476d7353 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Jun 2002 12:09:49 +0000 Subject: [PATCH 196/807] cleanup. --- client/CXWindowsSecondaryScreen.cpp | 59 ++++++++++++++--------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index ba881460..da46fa46 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -465,8 +465,8 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, unsigned int bit = (1 << i); if ((outMask & bit) != (m_mask & bit)) { // get list of keycodes for the modifier. if there isn't - // one then there's no key mapped to this modifier. - // we can't generate the desired key so bail. + // one then there's no key mapped to this modifier and we + // can't generate the desired key so bail. const KeyCode* modifierKeys = &m_modifierToKeycode[i * m_keysPerModifier]; KeyCode modifierKey = modifierKeys[0]; @@ -478,24 +478,26 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, return m_mask; } - if (modifierKey != 0 && (outMask & bit) != 0) { + keystroke.m_keycode = modifierKey; + keystroke.m_repeat = false; + if ((outMask & bit) != 0) { // modifier is not active but should be. if the // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a // press. use the first keycode for the modifier. log((CLOG_DEBUG2 "modifier 0x%04x is not active", bit)); - keystroke.m_keycode = modifierKey; - keystroke.m_press = True; - keystroke.m_repeat = false; - keys.push_back(keystroke); if ((bit & m_toggleModifierMask) != 0) { log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || (bit == m_numLockMask && m_numLockHalfDuplex)) { + keystroke.m_press = True; + keys.push_back(keystroke); keystroke.m_press = False; undo.push_back(keystroke); } else { + keystroke.m_press = True; + keys.push_back(keystroke); keystroke.m_press = False; keys.push_back(keystroke); undo.push_back(keystroke); @@ -504,12 +506,14 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, } } else { + keystroke.m_press = True; + keys.push_back(keystroke); keystroke.m_press = False; undo.push_back(keystroke); } } - else if ((outMask & bit) == 0) { + else { // modifier is active but should not be. if the // modifier is a toggle then toggle it off with a // press/release, otherwise deactivate it with a @@ -517,28 +521,22 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // modifier if not a toggle. log((CLOG_DEBUG2 "modifier 0x%04x is active", bit)); if ((bit & m_toggleModifierMask) != 0) { - if (modifierKey != 0) { - log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); - keystroke.m_keycode = modifierKey; - keystroke.m_repeat = false; - if ((bit == m_capsLockMask && - m_capsLockHalfDuplex) || - (bit == m_numLockMask && - m_numLockHalfDuplex)) { - keystroke.m_press = False; - keys.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); - } - else { - keystroke.m_press = True; - keys.push_back(keystroke); - keystroke.m_press = False; - keys.push_back(keystroke); - undo.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); - } + log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); + if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || + (bit == m_numLockMask && m_numLockHalfDuplex)) { + keystroke.m_press = False; + keys.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); + } + else { + keystroke.m_press = True; + keys.push_back(keystroke); + keystroke.m_press = False; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); } } else { @@ -547,7 +545,6 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, if (key != 0 && m_keys[key]) { keystroke.m_keycode = key; keystroke.m_press = False; - keystroke.m_repeat = false; keys.push_back(keystroke); keystroke.m_press = True; undo.push_back(keystroke); From 6c6afcc8cbef849dfd37c139e12dca14745929ae Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Jun 2002 13:55:45 +0000 Subject: [PATCH 197/807] added comments. --- server/CXWindowsPrimaryScreen.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index e5567393..c08dcd74 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -597,7 +597,9 @@ CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id) void CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const { - // ignore errors while we adjust event masks + // ignore errors while we adjust event masks. windows could be + // destroyed at any time after the XQueryTree() in doSelectEvents() + // so we must ignore BadWindow errors. CXWindowsUtil::CErrorLock lock; // adjust event masks @@ -611,13 +613,28 @@ CXWindowsPrimaryScreen::doSelectEvents(Display* display, Window w) const // that we select PointerMotionMask on every window. we also select // SubstructureNotifyMask in order to get CreateNotify events so we // select events on new windows too. + // + // note that this can break certain clients due a design flaw of X. + // X will deliver a PointerMotion event to the deepest window in the + // hierarchy that contains the pointer and has PointerMotionMask + // selected by *any* client. if another client doesn't select + // motion events in a subwindow so the parent window will get them + // then by selecting for motion events on the subwindow we break + // that client because the parent will no longer get the events. + + // FIXME -- should provide some workaround for event selection + // design flaw. perhaps only select for motion events on windows + // that already do or are top-level windows or don't propagate + // pointer events. or maybe an option to simply poll the mouse. // we don't want to adjust our grab window if (w == m_window) { return; } - // select events of interest + // select events of interest. do this before querying the tree so + // we'll get notifications of children created after the XQueryTree() + // so we won't miss them. XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask); // recurse on child windows From 4e37691a9ce2b891588d31abff669e7d0809cf50 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Jun 2002 17:31:24 +0000 Subject: [PATCH 198/807] added header files to _SOURCES. --- client/Makefile.am | 8 +++++--- http/Makefile.am | 2 ++ io/Makefile.am | 8 ++++++++ mt/Makefile.am | 7 +++++++ net/Makefile.am | 9 +++++++++ platform/Makefile.am | 7 +++++++ server/Makefile.am | 23 +++++++++++++++-------- synergy/Makefile.am | 17 +++++++++++++++++ 8 files changed, 70 insertions(+), 11 deletions(-) diff --git a/client/Makefile.am b/client/Makefile.am index b747856b..744ba730 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -3,10 +3,12 @@ NULL = DEPTH = .. bin_PROGRAMS = synergy -synergy_SOURCES = \ +synergy_SOURCES = \ CXWindowsSecondaryScreen.cpp \ - CClient.cpp \ - client.cpp \ + CClient.cpp \ + client.cpp \ + CClient.h \ + CXWindowsSecondaryScreen.h \ $(NULL) synergy_LDADD = \ $(DEPTH)/platform/libplatform.a \ diff --git a/http/Makefile.am b/http/Makefile.am index ecdf2c9c..08404a0a 100644 --- a/http/Makefile.am +++ b/http/Makefile.am @@ -6,6 +6,8 @@ noinst_LIBRARIES = libhttp.a libhttp_a_SOURCES = \ CHTTPProtocol.cpp \ XHTTP.cpp \ + CHTTPProtocol.h \ + XHTTP.h \ $(NULL) INCLUDES = \ -I$(DEPTH)/base \ diff --git a/io/Makefile.am b/io/Makefile.am index e0b58ce6..897daf14 100644 --- a/io/Makefile.am +++ b/io/Makefile.am @@ -10,6 +10,14 @@ libio_a_SOURCES = \ COutputStreamFilter.cpp \ CStreamBuffer.cpp \ XIO.cpp \ + CBufferedInputStream.h \ + CBufferedOutputStream.h \ + CInputStreamFilter.h \ + COutputStreamFilter.h \ + CStreamBuffer.h \ + IInputStream.h \ + IOutputStream.h \ + XIO.h \ $(NULL) INCLUDES = \ -I$(DEPTH)/base \ diff --git a/mt/Makefile.am b/mt/Makefile.am index 0b23ad3f..e7f69865 100644 --- a/mt/Makefile.am +++ b/mt/Makefile.am @@ -10,6 +10,13 @@ libmt_a_SOURCES = \ CThread.cpp \ CThreadRep.cpp \ CTimerThread.cpp \ + CCondVar.h \ + CLock.h \ + CMutex.h \ + CThread.h \ + CThreadRep.h \ + CTimerThread.h \ + XThread.h \ $(NULL) INCLUDES = \ -I$(DEPTH)/base \ diff --git a/net/Makefile.am b/net/Makefile.am index 6a5b59d9..5262f015 100644 --- a/net/Makefile.am +++ b/net/Makefile.am @@ -10,6 +10,15 @@ libnet_a_SOURCES = \ CTCPListenSocket.cpp \ XNetwork.cpp \ XSocket.cpp \ + CNetwork.h \ + CNetworkAddress.h \ + CTCPListenSocket.h \ + CTCPSocket.h \ + IDataSocket.h \ + IListenSocket.h \ + ISocket.h \ + XNetwork.h \ + XSocket.h \ $(NULL) INCLUDES = \ -I$(DEPTH)/base \ diff --git a/platform/Makefile.am b/platform/Makefile.am index e6c4c2ca..388d0541 100644 --- a/platform/Makefile.am +++ b/platform/Makefile.am @@ -2,12 +2,19 @@ NULL = DEPTH = .. +# FIXME -- add CUnixPlatform.cpp as an unbuilt source noinst_LIBRARIES = libplatform.a libplatform_a_SOURCES = \ CPlatform.cpp \ CXWindowsClipboard.cpp \ CXWindowsScreen.cpp \ CXWindowsUtil.cpp \ + CPlatform.h \ + CUnixPlatform.h \ + CXWindowsClipboard.h \ + CXWindowsScreen.h \ + CXWindowsUtil.h \ + IPlatform.h \ $(NULL) INCLUDES = \ -I$(DEPTH)/base \ diff --git a/server/Makefile.am b/server/Makefile.am index c8abb6a3..0d145475 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -3,14 +3,21 @@ NULL = DEPTH = .. bin_PROGRAMS = synergyd -synergyd_SOURCES = \ - CConfig.cpp \ - CServerProtocol.cpp \ - CServerProtocol1_0.cpp \ - CXWindowsPrimaryScreen.cpp \ - CServer.cpp \ - CHTTPServer.cpp \ - server.cpp \ +synergyd_SOURCES = \ + CConfig.cpp \ + CServerProtocol.cpp \ + CServerProtocol1_0.cpp \ + CXWindowsPrimaryScreen.cpp \ + CServer.cpp \ + CHTTPServer.cpp \ + server.cpp \ + CConfig.h \ + CHTTPServer.h \ + CServer.h \ + CServerProtocol.h \ + CServerProtocol1_0.h \ + CSynergyHook.h \ + CXWindowsPrimaryScreen.h \ $(NULL) synergyd_LDADD = \ $(DEPTH)/platform/libplatform.a \ diff --git a/synergy/Makefile.am b/synergy/Makefile.am index 3b10a96b..8b0b6318 100644 --- a/synergy/Makefile.am +++ b/synergy/Makefile.am @@ -11,6 +11,23 @@ libsynergy_a_SOURCES = \ CTCPSocketFactory.cpp \ XScreen.cpp \ XSynergy.cpp \ + CClipboard.h \ + CInputPacketStream.h \ + COutputPacketStream.h \ + CProtocolUtil.h \ + CTCPSocketFactory.h \ + ClipboardTypes.h \ + IClipboard.h \ + IPrimaryScreen.h \ + ISecondaryScreen.h \ + IServerProtocol.h \ + ISocketFactory.h \ + KeyTypes.h \ + MouseTypes.h \ + ProtocolTypes.h \ + Version.h \ + XScreen.h \ + XSynergy.h \ $(NULL) INCLUDES = \ -I$(DEPTH)/base \ From 504bfa2def9dd6bf9f4833eaba833816a808899c Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Jun 2002 19:20:21 +0000 Subject: [PATCH 199/807] checkpoint. adding screen saver support. only on X so far and untested. also some known problems: not detecting an xscreensaver started after us and not detecting built-in screen saver activation (not sure if we can without using ugly extensions). --- client/CClient.cpp | 2 +- client/CXWindowsSecondaryScreen.cpp | 19 +++ client/CXWindowsSecondaryScreen.h | 1 + platform/CXWindowsScreen.cpp | 23 ++- platform/CXWindowsScreen.h | 9 + platform/CXWindowsScreenSaver.cpp | 255 ++++++++++++++++++++++++++++ platform/CXWindowsScreenSaver.h | 80 +++++++++ platform/Makefile.am | 24 +-- server/CServer.cpp | 68 ++++++++ server/CServer.h | 10 ++ server/CXWindowsPrimaryScreen.cpp | 17 ++ server/CXWindowsPrimaryScreen.h | 3 + synergy/IPrimaryScreen.h | 12 -- synergy/IScreenSaver.h | 30 ++++ synergy/ISecondaryScreen.h | 8 +- synergy/Makefile.am | 1 + 16 files changed, 532 insertions(+), 30 deletions(-) create mode 100644 platform/CXWindowsScreenSaver.cpp create mode 100644 platform/CXWindowsScreenSaver.h create mode 100644 synergy/IScreenSaver.h diff --git a/client/CClient.cpp b/client/CClient.cpp index e2af4f15..b7284df1 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -551,7 +551,7 @@ CClient::onScreenSaver() CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on); } log((CLOG_DEBUG1 "recv screen saver on=%d", on)); - // FIXME + m_screen->screenSaver(on != 0); } void diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index da46fa46..4ae2c723 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -1,6 +1,7 @@ #include "CXWindowsSecondaryScreen.h" #include "CClient.h" #include "CXWindowsClipboard.h" +#include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CThread.h" #include "CLog.h" @@ -119,6 +120,9 @@ CXWindowsSecondaryScreen::open(CClient* client) for (ClipboardID id = 0; id < kClipboardEnd; ++id) { grabClipboard(id); } + + // disable the screen saver + getScreenSaver()->disable(); } void @@ -126,6 +130,9 @@ CXWindowsSecondaryScreen::close() { assert(m_client != NULL); + // restore the screen saver settings + getScreenSaver()->enable(); + // close the display closeDisplay(); @@ -287,6 +294,18 @@ CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) setDisplayClipboard(id, NULL); } +void +CXWindowsSecondaryScreen::screenSaver(bool activate) +{ + CDisplayLock display(this); + if (activate) { + getScreenSaver()->activate(); + } + else { + getScreenSaver()->deactivate(); + } +} + void CXWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const { diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 296cc13e..0e07c4ea 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -29,6 +29,7 @@ public: virtual void mouseWheel(SInt32 delta); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); + virtual void screenSaver(bool activate); virtual void getMousePos(SInt32& x, SInt32& y) const; virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; virtual SInt32 getJumpZoneSize() const; diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index c314c6ea..0ac1a791 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -1,5 +1,6 @@ #include "CXWindowsScreen.h" #include "CXWindowsClipboard.h" +#include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CClipboard.h" #include "XScreen.h" @@ -21,7 +22,8 @@ CXWindowsScreen::CXWindowsScreen() : m_root(None), m_x(0), m_y(0), m_w(0), m_h(0), - m_stop(false) + m_stop(false), + m_screenSaver(NULL) { assert(s_screen == NULL); s_screen = this; @@ -77,6 +79,9 @@ CXWindowsScreen::openDisplay() for (ClipboardID id = 0; id < kClipboardEnd; ++id) { m_clipboard[id] = createClipboard(id); } + + // initialize the screen saver + m_screenSaver = new CXWindowsScreenSaver(m_display); } void @@ -87,9 +92,14 @@ CXWindowsScreen::closeDisplay() // let subclass close down display onCloseDisplay(m_display); + // done with screen saver + delete m_screenSaver; + m_screenSaver = NULL; + // destroy clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) { delete m_clipboard[id]; + m_clipboard[id] = NULL; } // close the display @@ -285,9 +295,20 @@ CXWindowsScreen::processEvent(XEvent* xevent) return true; } + // let screen saver have a go + if (m_screenSaver->processEvent(xevent)) { + return true; + } + return false; } +CXWindowsScreenSaver* +CXWindowsScreen::getScreenSaver() const +{ + return m_screenSaver; +} + bool CXWindowsScreen::setDisplayClipboard(ClipboardID id, const IClipboard* clipboard) diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index 074411bb..b7a13fdd 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -10,7 +10,9 @@ #endif class IClipboard; +class IScreenSaver; class CXWindowsClipboard; +class CXWindowsScreenSaver; class CXWindowsScreen { public: @@ -68,6 +70,10 @@ protected: bool getDisplayClipboard(ClipboardID, IClipboard* clipboard) const; + // get the screen saver object + CXWindowsScreenSaver* + getScreenSaver() const; + // called by openDisplay() to allow subclasses to prepare the display. // the display is locked and passed to the subclass. virtual void onOpenDisplay(Display*) = 0; @@ -116,6 +122,9 @@ private: // clipboards CXWindowsClipboard* m_clipboard[kClipboardEnd]; + // screen saver + CXWindowsScreenSaver* m_screenSaver; + // X is not thread safe CMutex m_mutex; diff --git a/platform/CXWindowsScreenSaver.cpp b/platform/CXWindowsScreenSaver.cpp new file mode 100644 index 00000000..89070e3a --- /dev/null +++ b/platform/CXWindowsScreenSaver.cpp @@ -0,0 +1,255 @@ +#include "CXWindowsScreenSaver.h" +#include "CXWindowsUtil.h" +#include + +// +// CXWindowsScreenSaver +// + +CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : + m_display(display), + m_notify(None), + m_xscreensaver(None) +{ + // get atoms + m_atomScreenSaver = XInternAtom(m_display, + "SCREENSAVER", False); + m_atomScreenSaverVersion = XInternAtom(m_display, + "_SCREENSAVER_VERSION", False); + m_atomScreenSaverActivate = XInternAtom(m_display, + "ACTIVATE", False); + m_atomScreenSaverDeactivate = XInternAtom(m_display, + "DEACTIVATE", False); + + // watch top-level windows for changes + { + bool error = false; + CXWindowsUtil::CErrorLock lock(&error); + Window root = DefaultRootWindow(m_display); + XWindowAttributes attr; + XGetWindowAttributes(m_display, root, &attr); + m_rootEventMask = attr.your_event_mask; + XSelectInput(m_display, root, m_rootEventMask | SubstructureNotifyMask); + if (error) { + m_rootEventMask = 0; + } + } + + // get the xscreensaver window, if any + updateXScreenSaver(); + + // get the built-in settings + XGetScreenSaver(m_display, &m_timeout, &m_interval, + &m_preferBlanking, &m_allowExposures); +} + +CXWindowsScreenSaver::~CXWindowsScreenSaver() +{ + // stop watching root for events + CXWindowsUtil::CErrorLock lock; + Window root = DefaultRootWindow(m_display); + XSelectInput(m_display, root, m_rootEventMask); +} + +bool +CXWindowsScreenSaver::processEvent(XEvent* xevent) +{ + switch (xevent->type) { + case DestroyNotify: + if (xevent->xdestroywindow.window == m_xscreensaver) { + // xscreensaver is gone + setXScreenSaver(false); + m_xscreensaver = None; + return true; + } + break; + + case MapNotify: + if (xevent->xmap.window == m_xscreensaver) { + // xscreensaver has activated + setXScreenSaver(true); + return true; + } + break; + + case UnmapNotify: + if (xevent->xunmap.window == m_xscreensaver) { + // xscreensaver has deactivated + setXScreenSaver(false); + return true; + } + break; + } + + return false; +} + +void +CXWindowsScreenSaver::setNotify(Window notify) +{ + m_notify = notify; +} + +void +CXWindowsScreenSaver::enable() +{ + // try xscreensaver + updateXScreenSaver(); + if (m_xscreensaver) { + // FIXME + return; + } + + // use built-in X screen saver + XSetScreenSaver(m_display, m_timeout, m_interval, + m_preferBlanking, m_allowExposures); +} + +void +CXWindowsScreenSaver::disable() +{ + // try xscreensaver + updateXScreenSaver(); + if (m_xscreensaver) { + // FIXME + return; + } + + // use built-in X screen saver + XGetScreenSaver(m_display, &m_timeout, &m_interval, + &m_preferBlanking, &m_allowExposures); + XSetScreenSaver(m_display, 0, m_interval, + m_preferBlanking, m_allowExposures); + // FIXME -- now deactivate? +} + +void +CXWindowsScreenSaver::activate() +{ + // try xscreensaver + updateXScreenSaver(); + if (m_xscreensaver) { + sendXScreenSaverCommand(m_atomScreenSaverActivate); + return; + } + + // use built-in X screen saver + XForceScreenSaver(m_display, ScreenSaverActive); +} + +void +CXWindowsScreenSaver::deactivate() +{ + // try xscreensaver + updateXScreenSaver(); + if (m_xscreensaver) { + sendXScreenSaverCommand(m_atomScreenSaverDeactivate); + return; + } + + // use built-in X screen saver + XForceScreenSaver(m_display, ScreenSaverReset); +} + +bool +CXWindowsScreenSaver::isActive() const +{ + // check xscreensaver + if (m_xscreensaver != None) { + return m_xscreensaverActive; + } + + // can't check built-in X screen saver activity + return false; +} + +void +CXWindowsScreenSaver::sendNotify(bool activated) +{ + if (m_notify != None) { + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.display = m_display; + event.xclient.window = m_notify; + event.xclient.message_type = m_atomScreenSaver; + event.xclient.format = 32; + event.xclient.data.l[0] = activated ? 1 : 0; + event.xclient.data.l[1] = 0; + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + CXWindowsUtil::CErrorLock lock; + XSendEvent(m_display, m_notify, False, 0, &event); + } +} + +void +CXWindowsScreenSaver::setXScreenSaver(bool activated) +{ + if (m_xscreensaverActive != activated) { + m_xscreensaverActive = activated; + sendNotify(activated); + } +} + +void +CXWindowsScreenSaver::updateXScreenSaver() +{ + // do nothing if we've already got the xscreensaver window + if (m_xscreensaver != None) { + return; + } + + // find top-level window with m_atomScreenSaverVersion string property + CXWindowsUtil::CErrorLock lock; + Window root = DefaultRootWindow(m_display); + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { + CString data; + Atom type; + for (unsigned int i = 0; i < nc; ++i) { + if (CXWindowsUtil::getWindowProperty(m_display, cw[i], + m_atomScreenSaverVersion, + &data, &type, NULL, False) && + type == XA_STRING) { + m_xscreensaver = cw[i]; + break; + } + } + XFree(cw); + } + + // see if xscreensaver is active + if (m_xscreensaver != None) { + bool error = false; + CXWindowsUtil::CErrorLock lock(&error); + XWindowAttributes attr; + XGetWindowAttributes(m_display, m_xscreensaver, &attr); + setXScreenSaver(!error && attr.map_state != IsUnmapped); + } + else { + setXScreenSaver(false); + } +} + +void +CXWindowsScreenSaver::sendXScreenSaverCommand( + Atom cmd, long arg1, long arg2) const +{ + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.display = m_display; + event.xclient.window = None; + event.xclient.message_type = m_atomScreenSaver; + event.xclient.format = 32; + event.xclient.data.l[0] = static_cast(cmd); + event.xclient.data.l[1] = arg1; + event.xclient.data.l[2] = arg2; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + CXWindowsUtil::CErrorLock lock; + XSendEvent(m_display, m_xscreensaver, False, 0, &event); +} diff --git a/platform/CXWindowsScreenSaver.h b/platform/CXWindowsScreenSaver.h new file mode 100644 index 00000000..7ea9e061 --- /dev/null +++ b/platform/CXWindowsScreenSaver.h @@ -0,0 +1,80 @@ +#ifndef CXWINDOWSSCREENSAVER_H +#define CXWINDOWSSCREENSAVER_H + +#include "IScreenSaver.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +class CXWindowsScreenSaver : public IScreenSaver { +public: + // note -- the caller must ensure that Display* passed to c'tor isn't + // being used in another call to Xlib when calling any method on this + // object (including during the c'tor and d'tor) except processEvent(). + CXWindowsScreenSaver(Display*); + virtual ~CXWindowsScreenSaver(); + + // process X event. returns true if the event was handled. + bool processEvent(XEvent*); + + // tells this object to send a ClientMessage to the given window + // when the screen saver activates or deactivates. only one + // window can be notified. the message type is the "SCREENSAVER" + // atom, the format is 32, and the data.l[0] member is non-zero + // if activated, zero if deactivated. pass in None to disable + // notification; + void setNotify(Window); + + // IScreenSaver overrides + virtual void enable(); + virtual void disable(); + virtual void activate(); + virtual void deactivate(); + virtual bool isActive() const; + +private: + // send a notification + void sendNotify(bool activated); + + // set xscreensaver's activation state flag. sends notification + // if the state has changed. + void setXScreenSaver(bool activated); + + // find the running xscreensaver's window + void updateXScreenSaver(); + + // send a command to xscreensaver + void sendXScreenSaverCommand(Atom, long = 0, long = 0) const; + +private: + // the X display + Display* m_display; + + // old event mask on root window + long m_rootEventMask; + + // window to notify on screen saver activation/deactivation + Window m_notify; + + // xscreensaver's window + Window m_xscreensaver; + + // xscreensaver activation state + bool m_xscreensaverActive; + + // atoms used to communicate with xscreensaver's window + Atom m_atomScreenSaver; + Atom m_atomScreenSaverVersion; + Atom m_atomScreenSaverActivate; + Atom m_atomScreenSaverDeactivate; + + // built-in screen saver settings + int m_timeout; + int m_interval; + int m_preferBlanking; + int m_allowExposures; +}; + +#endif diff --git a/platform/Makefile.am b/platform/Makefile.am index 388d0541..704526b0 100644 --- a/platform/Makefile.am +++ b/platform/Makefile.am @@ -4,17 +4,19 @@ DEPTH = .. # FIXME -- add CUnixPlatform.cpp as an unbuilt source noinst_LIBRARIES = libplatform.a -libplatform_a_SOURCES = \ - CPlatform.cpp \ - CXWindowsClipboard.cpp \ - CXWindowsScreen.cpp \ - CXWindowsUtil.cpp \ - CPlatform.h \ - CUnixPlatform.h \ - CXWindowsClipboard.h \ - CXWindowsScreen.h \ - CXWindowsUtil.h \ - IPlatform.h \ +libplatform_a_SOURCES = \ + CPlatform.cpp \ + CXWindowsClipboard.cpp \ + CXWindowsScreen.cpp \ + CXWindowsScreenSaver.cpp \ + CXWindowsUtil.cpp \ + CPlatform.h \ + CUnixPlatform.h \ + CXWindowsClipboard.h \ + CXWindowsScreen.h \ + CXWindowsScreenSaver.h \ + CXWindowsUtil.h \ + IPlatform.h \ $(NULL) INCLUDES = \ -I$(DEPTH)/base \ diff --git a/server/CServer.cpp b/server/CServer.cpp index 67b6d562..fdbb808f 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -709,6 +709,66 @@ CServer::onMouseWheel(SInt32 delta) } } +void +CServer::onScreenSaver(bool activated) +{ + log((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); + CLock lock(&m_mutex); + + if (activated) { + // save current screen and position + m_activeSaver = m_active; + m_xSaver = m_x; + m_ySaver = m_y; + + // jump to primary screen + if (m_active != m_primaryInfo) { +// FIXME -- should have separate "center" pixel reported by screen + m_x = m_primaryInfo->m_x + (m_primaryInfo->m_w >> 1); + m_y = m_primaryInfo->m_y + (m_primaryInfo->m_h >> 1); + m_active = m_primaryInfo; + m_primary->enter(m_x, m_y); + } + } + else { + // jump back to previous screen and position. we must check + // that the position is still valid since the screen may have + // changed resolutions while the screen saver was running. + if (m_activeSaver != NULL && m_activeSaver != m_primaryInfo) { + // check position + CScreenInfo* screen = m_activeSaver; + if (m_xSaver < screen->m_x + screen->m_zoneSize) { + m_xSaver = screen->m_x + screen->m_zoneSize; + } + else if (m_xSaver >= screen->m_x + + screen->m_w - screen->m_zoneSize) { + m_xSaver = screen->m_x + screen->m_w - screen->m_zoneSize - 1; + } + if (m_ySaver < screen->m_y + screen->m_zoneSize) { + m_ySaver = screen->m_y + screen->m_zoneSize; + } + else if (m_ySaver >= screen->m_y + + screen->m_h - screen->m_zoneSize) { + m_ySaver = screen->m_y + screen->m_h - screen->m_zoneSize - 1; + } + + // now jump + switchScreen(screen, m_xSaver, m_ySaver); + } + + // reset state + m_activeSaver = NULL; + } + + // send message to all secondary screens + for (CScreenList::const_iterator index = m_screens.begin(); + index != m_screens.end(); ++index) { + if (index->second->m_protocol != NULL) { + index->second->m_protocol->sendScreenSaver(activated); + } + } +} + bool CServer::isLockedToScreen() const { @@ -1557,6 +1617,7 @@ CServer::removeConnection(const CString& name) // if this is active screen then we have to jump off of it if (m_active == index->second && m_active != m_primaryInfo) { // record new position (center of primary screen) +// FIXME -- should have separate "center" pixel reported by screen m_x = m_primaryInfo->m_x + (m_primaryInfo->m_w >> 1); m_y = m_primaryInfo->m_y + (m_primaryInfo->m_h >> 1); @@ -1570,6 +1631,13 @@ CServer::removeConnection(const CString& name) m_primary->enter(m_x, m_y); } + // if this screen had the cursor when the screen saver activated + // then we can't switch back to it when the screen saver + // deactivates. + if (m_activeSaver == index->second) { + m_activeSaver = NULL; + } + // done with screen info delete index->second; m_screens.erase(index); diff --git a/server/CServer.h b/server/CServer.h index a589ecb4..188ccc86 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -58,6 +58,7 @@ public: void onMouseMoveSecondary(SInt32 dx, SInt32 dy); void onMouseWheel(SInt32 delta); void grabClipboard(ClipboardID); + void onScreenSaver(bool activated); // handle updates from primary void setInfo(SInt32 xScreen, SInt32 yScreen, @@ -207,6 +208,7 @@ private: CMutex m_mutex; + // the name of the primary screen CString m_name; double m_bindTimeout; @@ -214,8 +216,10 @@ private: ISocketFactory* m_socketFactory; ISecurityFactory* m_securityFactory; + // running threads CThreadList m_threads; + // the screens IPrimaryScreen* m_primary; CScreenList m_screens; CScreenInfo* m_active; @@ -227,10 +231,16 @@ private: // current mouse position (in absolute secondary screen coordinates) SInt32 m_x, m_y; + // current configuration CConfig m_config; + // clipboard cache CClipboardInfo m_clipboards[kClipboardEnd]; + // state saved when screen saver activates + CScreenInfo* m_activeSaver; + SInt32 m_xSaver, m_ySaver; + // HTTP request processing stuff CHTTPServer* m_httpServer; CCondVar m_httpAvailable; diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index c08dcd74..a500e545 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -1,6 +1,7 @@ #include "CXWindowsPrimaryScreen.h" #include "CServer.h" #include "CXWindowsClipboard.h" +#include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CThread.h" #include "CLog.h" @@ -60,6 +61,14 @@ CXWindowsPrimaryScreen::run() } break; + case ClientMessage: + if (xevent.xclient.message_type == m_atomScreenSaver || + xevent.xclient.format == 32) { + // screen saver activation/deactivation event + m_server->onScreenSaver(xevent.xclient.data.l[0] != 0); + } + break; + case KeyPress: { log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); @@ -267,6 +276,10 @@ CXWindowsPrimaryScreen::open(CServer* server) { CDisplayLock display(this); + // get notified of screen saver activation/deactivation + m_atomScreenSaver = XInternAtom(display, "SCREENSAVER", False); + getScreenSaver()->setNotify(m_window); + // update key state updateModifierMap(display); @@ -298,6 +311,10 @@ CXWindowsPrimaryScreen::close() { assert(m_server != NULL); + // stop being notified of screen saver activation/deactivation + getScreenSaver()->setNotify(None); + m_atomScreenSaver = None; + // close the display closeDisplay(); diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 19f5feab..b3f0829f 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -62,6 +62,9 @@ private: bool m_active; Window m_window; + // atom for screen saver messages + Atom m_atomScreenSaver; + // note toggle keys that toggle on up/down (false) or on // transition (true) bool m_numLockHalfDuplex; diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 0a318c14..036e4885 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -62,23 +62,11 @@ public: // soon after an enter(). virtual void setClipboard(ClipboardID, const IClipboard*) = 0; -/* - // show or hide the screen saver - virtual void onScreenSaver(bool show) = 0; -*/ - // synergy should own the clipboard virtual void grabClipboard(ClipboardID) = 0; // accessors -/* - // get the screen's name. all screens must have a name unique on - // the server they connect to. the hostname is usually an - // appropriate name. - virtual CString getName() const = 0; -*/ - // get the screen region virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; diff --git a/synergy/IScreenSaver.h b/synergy/IScreenSaver.h new file mode 100644 index 00000000..d1bccf45 --- /dev/null +++ b/synergy/IScreenSaver.h @@ -0,0 +1,30 @@ +#ifndef ISCREENSAVER_H +#define ISCREENSAVER_H + +#include "IInterface.h" + +class IScreenSaver : public IInterface { +public: + // note -- the c'tor/d'tor must *not* enable/disable the screen saver + + // manipulators + + // enable/disable the screen saver. enable() should restore the + // screen saver settings to what they were when disable() was + // previously called. if disable() wasn't previously called then + // it should keep the current settings or use reasonable defaults. + virtual void enable() = 0; + virtual void disable() = 0; + + // activate/deactivate (i.e. show/hide) the screen saver. + // deactivate() also resets the screen saver timer. + virtual void activate() = 0; + virtual void deactivate() = 0; + + // accessors + + // returns true iff the screen saver is active + virtual bool isActive() const = 0; +}; + +#endif diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 57091147..a31bc563 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -55,14 +55,12 @@ public: // soon after an enter(). virtual void setClipboard(ClipboardID, const IClipboard*) = 0; -/* - // show or hide the screen saver - virtual void screenSaver(bool show) = 0; -*/ - // take ownership of clipboard virtual void grabClipboard(ClipboardID) = 0; + // activate or deactivate the screen saver + virtual void screenSaver(bool activate) = 0; + // accessors // get the position of the mouse on the screen diff --git a/synergy/Makefile.am b/synergy/Makefile.am index 8b0b6318..93afea71 100644 --- a/synergy/Makefile.am +++ b/synergy/Makefile.am @@ -19,6 +19,7 @@ libsynergy_a_SOURCES = \ ClipboardTypes.h \ IClipboard.h \ IPrimaryScreen.h \ + IScreenSaver.h \ ISecondaryScreen.h \ IServerProtocol.h \ ISocketFactory.h \ From 95a1ce8798fc77cd72fc876b29b0e2638542a508 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Jun 2002 19:47:27 +0000 Subject: [PATCH 200/807] CXWindowsUtil::CErrorLock wasn't XSync()'ing the display before installing and uninstalling the new error handler, causing errors before the lock to be caught and errors during the lock to not be caught. had to add Display* as argument to c'tor. --- platform/CXWindowsClipboard.cpp | 8 ++++---- platform/CXWindowsScreenSaver.cpp | 12 ++++++------ platform/CXWindowsUtil.cpp | 29 +++++++++++++++++++++-------- platform/CXWindowsUtil.h | 7 ++++--- server/CXWindowsPrimaryScreen.cpp | 2 +- 5 files changed, 36 insertions(+), 22 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index f894789b..08cb1f98 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -800,7 +800,7 @@ CXWindowsClipboard::insertReply(CReply* reply) if (newWindow) { // note errors while we adjust event masks bool error = false; - CXWindowsUtil::CErrorLock lock(&error); + CXWindowsUtil::CErrorLock lock(m_display, &error); // get and save the current event mask XWindowAttributes attr; @@ -852,7 +852,7 @@ CXWindowsClipboard::pushReplies(CReplyMap::iterator mapIndex, // if there are no more replies in the list then remove the list // and stop watching the requestor for events. if (replies.empty()) { - CXWindowsUtil::CErrorLock lock; + CXWindowsUtil::CErrorLock lock(m_display); Window requestor = mapIndex->first; XSelectInput(m_display, requestor, m_eventMasks[requestor]); m_replies.erase(mapIndex); @@ -926,7 +926,7 @@ CXWindowsClipboard::sendReply(CReply* reply) log((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); reply->m_done = true; if (reply->m_property != None) { - CXWindowsUtil::CErrorLock lock; + CXWindowsUtil::CErrorLock lock(m_display); XDeleteProperty(m_display, reply->m_requestor, reply->m_property); } @@ -1016,7 +1016,7 @@ CXWindowsClipboard::sendNotify(Window requestor, event.xselection.target = target; event.xselection.property = property; event.xselection.time = time; - CXWindowsUtil::CErrorLock lock; + CXWindowsUtil::CErrorLock lock(m_display); XSendEvent(m_display, requestor, False, 0, &event); } diff --git a/platform/CXWindowsScreenSaver.cpp b/platform/CXWindowsScreenSaver.cpp index 89070e3a..f197b88a 100644 --- a/platform/CXWindowsScreenSaver.cpp +++ b/platform/CXWindowsScreenSaver.cpp @@ -24,7 +24,7 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : // watch top-level windows for changes { bool error = false; - CXWindowsUtil::CErrorLock lock(&error); + CXWindowsUtil::CErrorLock lock(m_display, &error); Window root = DefaultRootWindow(m_display); XWindowAttributes attr; XGetWindowAttributes(m_display, root, &attr); @@ -46,7 +46,7 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : CXWindowsScreenSaver::~CXWindowsScreenSaver() { // stop watching root for events - CXWindowsUtil::CErrorLock lock; + CXWindowsUtil::CErrorLock lock(m_display); Window root = DefaultRootWindow(m_display); XSelectInput(m_display, root, m_rootEventMask); } @@ -179,7 +179,7 @@ CXWindowsScreenSaver::sendNotify(bool activated) event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; - CXWindowsUtil::CErrorLock lock; + CXWindowsUtil::CErrorLock lock(m_display); XSendEvent(m_display, m_notify, False, 0, &event); } } @@ -202,7 +202,7 @@ CXWindowsScreenSaver::updateXScreenSaver() } // find top-level window with m_atomScreenSaverVersion string property - CXWindowsUtil::CErrorLock lock; + CXWindowsUtil::CErrorLock lock(m_display); Window root = DefaultRootWindow(m_display); Window rw, pw, *cw; unsigned int nc; @@ -224,7 +224,7 @@ CXWindowsScreenSaver::updateXScreenSaver() // see if xscreensaver is active if (m_xscreensaver != None) { bool error = false; - CXWindowsUtil::CErrorLock lock(&error); + CXWindowsUtil::CErrorLock lock(m_display, &error); XWindowAttributes attr; XGetWindowAttributes(m_display, m_xscreensaver, &attr); setXScreenSaver(!error && attr.map_state != IsUnmapped); @@ -250,6 +250,6 @@ CXWindowsScreenSaver::sendXScreenSaverCommand( event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; - CXWindowsUtil::CErrorLock lock; + CXWindowsUtil::CErrorLock lock(m_display); XSendEvent(m_display, m_xscreensaver, False, 0, &event); } diff --git a/platform/CXWindowsUtil.cpp b/platform/CXWindowsUtil.cpp index d7b9ba4c..8279d084 100644 --- a/platform/CXWindowsUtil.cpp +++ b/platform/CXWindowsUtil.cpp @@ -19,7 +19,7 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, int actualDatumSize; // ignore errors. XGetWindowProperty() will report failure. - CXWindowsUtil::CErrorLock lock; + CXWindowsUtil::CErrorLock lock(display); // read the property const long length = XMaxRequestSize(display); @@ -93,7 +93,7 @@ CXWindowsUtil::setWindowProperty(Display* display, Window window, // save errors bool error = false; - CXWindowsUtil::CErrorLock lock(&error); + CXWindowsUtil::CErrorLock lock(display, &error); // how much data to send in first chunk? UInt32 chunkSize = size; @@ -179,23 +179,31 @@ CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) CXWindowsUtil::CErrorLock* CXWindowsUtil::CErrorLock::s_top = NULL; -CXWindowsUtil::CErrorLock::CErrorLock() +CXWindowsUtil::CErrorLock::CErrorLock(Display* display) : + m_display(display) { install(&CXWindowsUtil::CErrorLock::ignoreHandler, NULL); } -CXWindowsUtil::CErrorLock::CErrorLock(bool* flag) +CXWindowsUtil::CErrorLock::CErrorLock(Display* display, bool* flag) : + m_display(display) { install(&CXWindowsUtil::CErrorLock::saveHandler, flag); } -CXWindowsUtil::CErrorLock::CErrorLock(ErrorHandler handler, void* data) +CXWindowsUtil::CErrorLock::CErrorLock(Display* display, + ErrorHandler handler, void* data) : + m_display(display) { install(handler, data); } CXWindowsUtil::CErrorLock::~CErrorLock() { + // make sure everything finishes before uninstalling handler + XSync(m_display, False); + + // restore old handler XSetErrorHandler(m_oldXHandler); s_top = m_next; } @@ -203,6 +211,10 @@ CXWindowsUtil::CErrorLock::~CErrorLock() void CXWindowsUtil::CErrorLock::install(ErrorHandler handler, void* data) { + // make sure everything finishes before installing handler + XSync(m_display, False); + + // install handler m_handler = handler; m_userData = data; m_oldXHandler = XSetErrorHandler( @@ -221,13 +233,14 @@ CXWindowsUtil::CErrorLock::internalHandler(Display* display, XErrorEvent* event) } void -CXWindowsUtil::CErrorLock::ignoreHandler(Display*, XErrorEvent*, void*) +CXWindowsUtil::CErrorLock::ignoreHandler(Display*, XErrorEvent* e, void*) { - // do nothing + log((CLOG_DEBUG "ignoring X error: %d", e->error_code)); } void -CXWindowsUtil::CErrorLock::saveHandler(Display*, XErrorEvent*, void* flag) +CXWindowsUtil::CErrorLock::saveHandler(Display*, XErrorEvent* e, void* flag) { + log((CLOG_DEBUG "flagging X error: %d", e->error_code)); *reinterpret_cast(flag) = true; } diff --git a/platform/CXWindowsUtil.h b/platform/CXWindowsUtil.h index 9e8add53..fefcc92a 100644 --- a/platform/CXWindowsUtil.h +++ b/platform/CXWindowsUtil.h @@ -30,9 +30,9 @@ public: class CErrorLock { public: typedef void (*ErrorHandler)(Display*, XErrorEvent*, void* userData); - CErrorLock(); - CErrorLock(bool* errorFlag); - CErrorLock(ErrorHandler, void* userData); + CErrorLock(Display*); + CErrorLock(Display*, bool* errorFlag); + CErrorLock(Display*, ErrorHandler, void* userData); ~CErrorLock(); private: @@ -44,6 +44,7 @@ public: private: typedef int (*XErrorHandler)(Display*, XErrorEvent*); + Display* m_display; ErrorHandler m_handler; void* m_userData; XErrorHandler m_oldXHandler; diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index a500e545..1537af18 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -617,7 +617,7 @@ CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const // ignore errors while we adjust event masks. windows could be // destroyed at any time after the XQueryTree() in doSelectEvents() // so we must ignore BadWindow errors. - CXWindowsUtil::CErrorLock lock; + CXWindowsUtil::CErrorLock lock(display); // adjust event masks doSelectEvents(display, w); From 4d113aa235dc347705dd71ca9f50c28d5ce41feb Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Jun 2002 20:29:59 +0000 Subject: [PATCH 201/807] fixes to get xscreensaver integration working. --- platform/CXWindowsScreen.cpp | 4 +++- platform/CXWindowsScreenSaver.cpp | 32 +++++++++++++++++++++++++++---- platform/CXWindowsScreenSaver.h | 5 ++++- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index 0ac1a791..f1eb3992 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -292,7 +292,9 @@ CXWindowsScreen::processEvent(XEvent* xevent) // looks like one of the windows that requested a clipboard // transfer has gone bye-bye. destroyClipboardRequest(xevent->xdestroywindow.window); - return true; + + // we don't know if the event was handled or not so continue + break; } // let screen saver have a go diff --git a/platform/CXWindowsScreenSaver.cpp b/platform/CXWindowsScreenSaver.cpp index f197b88a..ab5ec8e6 100644 --- a/platform/CXWindowsScreenSaver.cpp +++ b/platform/CXWindowsScreenSaver.cpp @@ -1,5 +1,6 @@ #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" +#include "CLog.h" #include // @@ -21,6 +22,21 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : m_atomScreenSaverDeactivate = XInternAtom(m_display, "DEACTIVATE", False); + // create dummy window to receive xscreensaver responses. earlier + // versions of xscreensaver will die if we pass None as the window. + XSetWindowAttributes attr; + attr.event_mask = 0;//PropertyChangeMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + m_xscreensaverSink = XCreateWindow(m_display, + DefaultRootWindow(m_display), + 0, 0, 1, 1, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect, + &attr); + log((CLOG_DEBUG "xscreensaver sink window is 0x%08x", m_xscreensaverSink)); + // watch top-level windows for changes { bool error = false; @@ -31,6 +47,7 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : m_rootEventMask = attr.your_event_mask; XSelectInput(m_display, root, m_rootEventMask | SubstructureNotifyMask); if (error) { + log((CLOG_DEBUG "didn't set root event mask")); m_rootEventMask = 0; } } @@ -59,6 +76,7 @@ CXWindowsScreenSaver::processEvent(XEvent* xevent) if (xevent->xdestroywindow.window == m_xscreensaver) { // xscreensaver is gone setXScreenSaver(false); + log((CLOG_DEBUG "xscreensaver died")); m_xscreensaver = None; return true; } @@ -188,6 +206,7 @@ void CXWindowsScreenSaver::setXScreenSaver(bool activated) { if (m_xscreensaverActive != activated) { + log((CLOG_DEBUG "xscreensaver %s", activated ? "activated" : "deactivated")); m_xscreensaverActive = activated; sendNotify(activated); } @@ -215,6 +234,7 @@ CXWindowsScreenSaver::updateXScreenSaver() &data, &type, NULL, False) && type == XA_STRING) { m_xscreensaver = cw[i]; + log((CLOG_DEBUG "found xscreensaver: 0x%08x", m_xscreensaver)); break; } } @@ -235,13 +255,12 @@ CXWindowsScreenSaver::updateXScreenSaver() } void -CXWindowsScreenSaver::sendXScreenSaverCommand( - Atom cmd, long arg1, long arg2) const +CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2) { XEvent event; event.xclient.type = ClientMessage; event.xclient.display = m_display; - event.xclient.window = None; + event.xclient.window = m_xscreensaverSink; event.xclient.message_type = m_atomScreenSaver; event.xclient.format = 32; event.xclient.data.l[0] = static_cast(cmd); @@ -250,6 +269,11 @@ CXWindowsScreenSaver::sendXScreenSaverCommand( event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; - CXWindowsUtil::CErrorLock lock(m_display); + log((CLOG_DEBUG "send xscreensaver command: %d %d %d", (long)cmd, arg1, arg2)); + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); XSendEvent(m_display, m_xscreensaver, False, 0, &event); + if (error) { + updateXScreenSaver(); + } } diff --git a/platform/CXWindowsScreenSaver.h b/platform/CXWindowsScreenSaver.h index 7ea9e061..67ac2347 100644 --- a/platform/CXWindowsScreenSaver.h +++ b/platform/CXWindowsScreenSaver.h @@ -46,7 +46,7 @@ private: void updateXScreenSaver(); // send a command to xscreensaver - void sendXScreenSaverCommand(Atom, long = 0, long = 0) const; + void sendXScreenSaverCommand(Atom, long = 0, long = 0); private: // the X display @@ -64,6 +64,9 @@ private: // xscreensaver activation state bool m_xscreensaverActive; + // dummy window to receive xscreensaver repsonses + Window m_xscreensaverSink; + // atoms used to communicate with xscreensaver's window Atom m_atomScreenSaver; Atom m_atomScreenSaverVersion; From a5391a0a1d8a8a4a4f3db7d2c4e08c6e5222c872 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 23 Jun 2002 15:43:40 +0000 Subject: [PATCH 202/807] checkpoint screensaver changes. now handling xscreensaver dying and restarting or starting after synergy does. also now disabling the screen saver on the client. next step: win32 support. --- platform/CXWindowsScreen.cpp | 140 +++++++++++++- platform/CXWindowsScreen.h | 131 +++++++++++++ platform/CXWindowsScreenSaver.cpp | 296 +++++++++++++++++++++++------- platform/CXWindowsScreenSaver.h | 45 ++++- platform/CXWindowsUtil.cpp | 11 +- server/CServer.cpp | 6 + server/CXWindowsPrimaryScreen.cpp | 7 + server/CXWindowsPrimaryScreen.h | 1 + server/Makefile.am | 2 + synergy/IPrimaryScreen.h | 3 + 10 files changed, 560 insertions(+), 82 deletions(-) diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index f1eb3992..4e88b5ed 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -7,10 +7,61 @@ #include "CLock.h" #include "CThread.h" #include "CLog.h" +#include "IJob.h" #include "CString.h" #include #include +// +// CXWindowsScreen::CTimer +// + +CXWindowsScreen::CTimer::CTimer(IJob* job, double timeout) : + m_job(job), + m_timeout(timeout) +{ + assert(m_job != NULL); + assert(m_timeout > 0.0); + + reset(); +} + +CXWindowsScreen::CTimer::~CTimer() +{ + // do nothing +} + +void +CXWindowsScreen::CTimer::run() +{ + m_job->run(); +} + +void +CXWindowsScreen::CTimer::reset() +{ + m_time = m_timeout; +} + +CXWindowsScreen::CTimer::CTimer& +CXWindowsScreen::CTimer::operator-=(double dt) +{ + m_time -= dt; + return *this; +} + +CXWindowsScreen::CTimer::operator double() const +{ + return m_time; +} + +bool +CXWindowsScreen::CTimer::operator<(const CTimer& t) const +{ + return m_time < t.m_time; +} + + // // CXWindowsScreen // @@ -37,6 +88,38 @@ CXWindowsScreen::~CXWindowsScreen() s_screen = NULL; } +void +CXWindowsScreen::addTimer(IJob* job, double timeout) +{ + CLock lock(&m_timersMutex); + removeTimerNoLock(job); + m_timers.push(CTimer(job, timeout)); +} + +void +CXWindowsScreen::removeTimer(IJob* job) +{ + CLock lock(&m_timersMutex); + removeTimerNoLock(job); +} + +void +CXWindowsScreen::removeTimerNoLock(IJob* job) +{ + // do it the hard way. first collect all jobs that are not + // the removed job. + CTimerPriorityQueue::container_type tmp; + for (CTimerPriorityQueue::iterator index = m_timers.begin(); + index != m_timers.end(); ++index) { + if (index->getJob() != job) { + tmp.push_back(*index); + } + } + + // now swap in the new list + m_timers.swap(tmp); +} + void CXWindowsScreen::openDisplay() { @@ -81,7 +164,7 @@ CXWindowsScreen::openDisplay() } // initialize the screen saver - m_screenSaver = new CXWindowsScreenSaver(m_display); + m_screenSaver = new CXWindowsScreenSaver(this, m_display); } void @@ -181,6 +264,12 @@ CXWindowsScreen::getEvent(XEvent* xevent) const m_mutex.lock(); for (;;) { while (!m_stop && XPending(m_display) == 0) { + // check timers + if (const_cast(this)->processTimers()) { + continue; + } + + // wait m_mutex.unlock(); CThread::sleep(0.01); m_mutex.lock(); @@ -298,13 +387,56 @@ CXWindowsScreen::processEvent(XEvent* xevent) } // let screen saver have a go - if (m_screenSaver->processEvent(xevent)) { - return true; - } + m_screenSaver->processEvent(xevent); return false; } +bool +CXWindowsScreen::processTimers() +{ + std::vector jobs; + { + CLock lock(&m_timersMutex); + + // get current time + const double time = m_time.getTime(); + + // done if no timers have expired + if (m_timers.empty() || m_timers.top() > time) { + return false; + } + + // subtract current time from all timers. note that this won't + // change the order of elements in the priority queue (except + // for floating point round off which we'll ignore). + for (CTimerPriorityQueue::iterator index = m_timers.begin(); + index != m_timers.end(); ++index) { + (*index) -= time; + } + + // process all timers at or below zero, saving the jobs + while (m_timers.top() <= 0.0) { + CTimer timer = m_timers.top(); + jobs.push_back(timer.getJob()); + timer.reset(); + m_timers.pop(); + m_timers.push(timer); + } + + // reset the clock + m_time.reset(); + } + + // now run the jobs. note that if one of these jobs removes + // a timer later in the jobs list and deletes that job pointer + // then this will crash when it tries to run that job. + for (std::vector::iterator index = jobs.begin(); + index != jobs.end(); ++index) { + (*index)->run(); + } +} + CXWindowsScreenSaver* CXWindowsScreen::getScreenSaver() const { diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index b7a13fdd..e4ed2cb3 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -3,13 +3,18 @@ #include "ClipboardTypes.h" #include "CMutex.h" +#include "CStopwatch.h" +#include "stdvector.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else # include #endif +#include +#include class IClipboard; +class IJob; class IScreenSaver; class CXWindowsClipboard; class CXWindowsScreenSaver; @@ -19,6 +24,15 @@ public: CXWindowsScreen(); virtual ~CXWindowsScreen(); + // manipulators + + // add/remove a job to invoke every timeout seconds. the job is + // called with the display locked. if a job timeout expires twice + // or more before the job can be called then the job is called + // just once. the caller retains ownership of the job. + void addTimer(IJob*, double timeout); + void removeTimer(IJob*); + protected: class CDisplayLock { public: @@ -94,9 +108,15 @@ protected: virtual void onLostClipboard(ClipboardID) = 0; private: + // remove a timer without locking + void removeTimerNoLock(IJob*); + // internal event processing bool processEvent(XEvent*); + // process timers + bool processTimers(); + // determine the clipboard from the X selection. returns // kClipboardEnd if no such clipboard. ClipboardID getClipboardID(Atom selection) const; @@ -112,6 +132,112 @@ private: static int ioErrorHandler(Display*); private: + // a priority queue will direct access to the elements + template , + class Compare = std::greater > + class CPriorityQueue { + public: + typedef typename Container::value_type value_type; + typedef typename Container::size_type size_type; + typedef typename Container::iterator iterator; + typedef Container container_type; + + CPriorityQueue() { } + CPriorityQueue(Container& swappedIn); + ~CPriorityQueue() { } + + // manipulators + + void push(const value_type& v) + { + c.push_back(v); + std::push_heap(c.begin(), c.end(), comp); + } + + void pop() + { + std::pop_heap(c.begin(), c.end(), comp); + c.pop_back(); + } + + iterator begin() + { + return c.begin(); + } + + iterator end() + { + return c.end(); + } + + void swap(CPriorityQueue& q) + { + c.swap(q.c); + } + + void swap(Container& c2) + { + c.swap(c2); + std::make_heap(c.begin(), c.end(), comp); + } + + // accessors + + bool empty() const + { + return c.empty(); + } + + size_type size() const + { + return c.size(); + } + + const value_type& + top() const + { + return c.front(); + } + + private: + Container c; + Compare comp; + }; + + // a timer priority queue element + class CTimer { + public: + CTimer(IJob* job, double timeout); + ~CTimer(); + + // manipulators + + void run(); + + void reset(); + + CTimer& operator-=(double); + + // accessors + + IJob* getJob() const + { + return m_job; + } + + operator double() const; + + bool operator<(const CTimer&) const; + + private: + IJob* m_job; + double m_timeout; + double m_time; + }; + +private: + typedef CPriorityQueue CTimerPriorityQueue; + Display* m_display; int m_screen; Window m_root; @@ -125,6 +251,11 @@ private: // screen saver CXWindowsScreenSaver* m_screenSaver; + // timers, the stopwatch used to time, and a mutex for the timers + CTimerPriorityQueue m_timers; + CStopwatch m_time; + CMutex m_timersMutex; + // X is not thread safe CMutex m_mutex; diff --git a/platform/CXWindowsScreenSaver.cpp b/platform/CXWindowsScreenSaver.cpp index ab5ec8e6..c6dbddc6 100644 --- a/platform/CXWindowsScreenSaver.cpp +++ b/platform/CXWindowsScreenSaver.cpp @@ -1,17 +1,31 @@ #include "CXWindowsScreenSaver.h" +#include "CXWindowsScreen.h" #include "CXWindowsUtil.h" #include "CLog.h" +#include "TMethodJob.h" #include +#if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# include +#else +# error The XTest extension is required to build synergy +#endif // // CXWindowsScreenSaver // -CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : +CXWindowsScreenSaver::CXWindowsScreenSaver( + CXWindowsScreen* screen, Display* display) : + m_screen(screen), m_display(display), m_notify(None), - m_xscreensaver(None) + m_xscreensaver(None), + m_xscreensaverActive(false) { + // screen saver disable callback + m_disableJob = new TMethodJob(this, + &CXWindowsScreenSaver::disableCallback); + // get atoms m_atomScreenSaver = XInternAtom(m_display, "SCREENSAVER", False); @@ -22,8 +36,9 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : m_atomScreenSaverDeactivate = XInternAtom(m_display, "DEACTIVATE", False); - // create dummy window to receive xscreensaver responses. earlier - // versions of xscreensaver will die if we pass None as the window. + // create dummy window to receive xscreensaver responses. this + // shouldn't be necessary (we should be able to send responses + // to None) but it doesn't hurt. XSetWindowAttributes attr; attr.event_mask = 0;//PropertyChangeMask; attr.do_not_propagate_mask = 0; @@ -53,7 +68,9 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : } // get the xscreensaver window, if any - updateXScreenSaver(); + if (!findXScreenSaver()) { + setXScreenSaver(None); + } // get the built-in settings XGetScreenSaver(m_display, &m_timeout, &m_interval, @@ -62,30 +79,67 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(Display* display) : CXWindowsScreenSaver::~CXWindowsScreenSaver() { + // clear watch list + clearWatchForXScreenSaver(); + // stop watching root for events CXWindowsUtil::CErrorLock lock(m_display); Window root = DefaultRootWindow(m_display); XSelectInput(m_display, root, m_rootEventMask); + + // destroy dummy sink window + XDestroyWindow(m_display, m_xscreensaverSink); + + // done with disable job + m_screen->removeTimer(m_disableJob); + delete m_disableJob; } bool CXWindowsScreenSaver::processEvent(XEvent* xevent) { switch (xevent->type) { + case CreateNotify: + if (m_xscreensaver == None) { + if (isXScreenSaver(xevent->xcreatewindow.window)) { + // found the xscreensaver + setXScreenSaver(xevent->xcreatewindow.window); + } + else { + // another window to watch. to detect the xscreensaver + // window we look for a property but that property may + // not yet exist by the time we get this event so we + // have to watch the window for property changes. + // this would be so much easier if xscreensaver did the + // smart thing and stored its window in a property on + // the root window. + addWatchXScreenSaver(xevent->xcreatewindow.window); + } + } + break; + case DestroyNotify: if (xevent->xdestroywindow.window == m_xscreensaver) { // xscreensaver is gone - setXScreenSaver(false); log((CLOG_DEBUG "xscreensaver died")); - m_xscreensaver = None; + setXScreenSaver(None); return true; } break; + case PropertyNotify: + if (xevent->xproperty.state == PropertyNewValue) { + if (isXScreenSaver(xevent->xproperty.window)) { + // found the xscreensaver + setXScreenSaver(xevent->xcreatewindow.window); + } + } + break; + case MapNotify: if (xevent->xmap.window == m_xscreensaver) { // xscreensaver has activated - setXScreenSaver(true); + setXScreenSaverActive(true); return true; } break; @@ -93,7 +147,7 @@ CXWindowsScreenSaver::processEvent(XEvent* xevent) case UnmapNotify: if (xevent->xunmap.window == m_xscreensaver) { // xscreensaver has deactivated - setXScreenSaver(false); + setXScreenSaverActive(false); return true; } break; @@ -111,14 +165,11 @@ CXWindowsScreenSaver::setNotify(Window notify) void CXWindowsScreenSaver::enable() { - // try xscreensaver - updateXScreenSaver(); - if (m_xscreensaver) { - // FIXME - return; - } +log((CLOG_INFO "enable screensaver")); + // for xscreensaver + m_screen->removeTimer(m_disableJob); - // use built-in X screen saver + // for built-in X screen saver XSetScreenSaver(m_display, m_timeout, m_interval, m_preferBlanking, m_allowExposures); } @@ -126,12 +177,10 @@ CXWindowsScreenSaver::enable() void CXWindowsScreenSaver::disable() { - // try xscreensaver - updateXScreenSaver(); - if (m_xscreensaver) { - // FIXME - return; - } +log((CLOG_INFO "disable screensaver")); + // for xscreensaver. 5 seconds should be plenty often to + // suppress the screen saver. + m_screen->addTimer(m_disableJob, 5.0); // use built-in X screen saver XGetScreenSaver(m_display, &m_timeout, &m_interval, @@ -145,8 +194,8 @@ void CXWindowsScreenSaver::activate() { // try xscreensaver - updateXScreenSaver(); - if (m_xscreensaver) { + findXScreenSaver(); + if (m_xscreensaver != None) { sendXScreenSaverCommand(m_atomScreenSaverActivate); return; } @@ -159,8 +208,8 @@ void CXWindowsScreenSaver::deactivate() { // try xscreensaver - updateXScreenSaver(); - if (m_xscreensaver) { + findXScreenSaver(); + if (m_xscreensaver != None) { sendXScreenSaverCommand(m_atomScreenSaverDeactivate); return; } @@ -202,8 +251,70 @@ CXWindowsScreenSaver::sendNotify(bool activated) } } +bool +CXWindowsScreenSaver::findXScreenSaver() +{ + // do nothing if we've already got the xscreensaver window + if (m_xscreensaver == None) { + // find top-level window xscreensaver window + Window root = DefaultRootWindow(m_display); + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + if (isXScreenSaver(cw[i])) { + setXScreenSaver(cw[i]); + break; + } + } + XFree(cw); + } + } + + return (m_xscreensaver != None); +} + void -CXWindowsScreenSaver::setXScreenSaver(bool activated) +CXWindowsScreenSaver::setXScreenSaver(Window window) +{ + log((CLOG_DEBUG "xscreensaver window: 0x%08x", window)); + + // save window + m_xscreensaver = window; + + if (m_xscreensaver != None) { + // clear old watch list + clearWatchForXScreenSaver(); + + // see if xscreensaver is active + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + XWindowAttributes attr; + XGetWindowAttributes(m_display, m_xscreensaver, &attr); + setXScreenSaverActive(!error && attr.map_state != IsUnmapped); + } + else { + // screen saver can't be active if it doesn't exist + setXScreenSaverActive(false); + + // start watching for xscreensaver + watchForXScreenSaver(); + } +} + +bool +CXWindowsScreenSaver::isXScreenSaver(Window w) const +{ + // check for m_atomScreenSaverVersion string property + Atom type; + return (CXWindowsUtil::getWindowProperty(m_display, w, + m_atomScreenSaverVersion, + NULL, &type, NULL, False) && + type == XA_STRING); +} + +void +CXWindowsScreenSaver::setXScreenSaverActive(bool activated) { if (m_xscreensaverActive != activated) { log((CLOG_DEBUG "xscreensaver %s", activated ? "activated" : "deactivated")); @@ -212,48 +323,6 @@ CXWindowsScreenSaver::setXScreenSaver(bool activated) } } -void -CXWindowsScreenSaver::updateXScreenSaver() -{ - // do nothing if we've already got the xscreensaver window - if (m_xscreensaver != None) { - return; - } - - // find top-level window with m_atomScreenSaverVersion string property - CXWindowsUtil::CErrorLock lock(m_display); - Window root = DefaultRootWindow(m_display); - Window rw, pw, *cw; - unsigned int nc; - if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { - CString data; - Atom type; - for (unsigned int i = 0; i < nc; ++i) { - if (CXWindowsUtil::getWindowProperty(m_display, cw[i], - m_atomScreenSaverVersion, - &data, &type, NULL, False) && - type == XA_STRING) { - m_xscreensaver = cw[i]; - log((CLOG_DEBUG "found xscreensaver: 0x%08x", m_xscreensaver)); - break; - } - } - XFree(cw); - } - - // see if xscreensaver is active - if (m_xscreensaver != None) { - bool error = false; - CXWindowsUtil::CErrorLock lock(m_display, &error); - XWindowAttributes attr; - XGetWindowAttributes(m_display, m_xscreensaver, &attr); - setXScreenSaver(!error && attr.map_state != IsUnmapped); - } - else { - setXScreenSaver(false); - } -} - void CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2) { @@ -274,6 +343,93 @@ CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2) CXWindowsUtil::CErrorLock lock(m_display, &error); XSendEvent(m_display, m_xscreensaver, False, 0, &event); if (error) { - updateXScreenSaver(); + findXScreenSaver(); } } + +void +CXWindowsScreenSaver::watchForXScreenSaver() +{ + // clear old watch list + clearWatchForXScreenSaver(); + + // add every child of the root to the list of windows to watch + Window root = DefaultRootWindow(m_display); + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + addWatchXScreenSaver(cw[i]); + } + XFree(cw); + } + + // now check for xscreensaver window in case it set the property + // before we could request property change events. + if (findXScreenSaver()) { + // found it so clear out our watch list + clearWatchForXScreenSaver(); + } +} + +void +CXWindowsScreenSaver::clearWatchForXScreenSaver() +{ + // stop watching all windows + CXWindowsUtil::CErrorLock lock(m_display); + for (CWatchList::iterator index = m_watchWindows.begin(); + index != m_watchWindows.end(); ++index) { + XSelectInput(m_display, index->first, index->second); + } + m_watchWindows.clear(); +} + +void +CXWindowsScreenSaver::addWatchXScreenSaver(Window window) +{ + bool error = false; + CXWindowsUtil::CErrorLock lock(m_display, &error); + + // get window attributes + XWindowAttributes attr; + XGetWindowAttributes(m_display, window, &attr); + + // if successful and window uses override_redirect (like xscreensaver + // does) then watch it for property changes. + if (!error && attr.override_redirect == True) { + XSelectInput(m_display, window, + attr.your_event_mask | PropertyChangeMask); + if (!error) { + // if successful then add the window to our list + m_watchWindows.insert(std::make_pair(window, attr.your_event_mask)); + } + } +} + +void +CXWindowsScreenSaver::disableCallback(void*) +{ + // send fake mouse motion directly to xscreensaver + if (m_xscreensaver != None) { + XEvent event; + event.xmotion.type = MotionNotify; + event.xmotion.display = m_display; + event.xmotion.window = m_xscreensaver; + event.xmotion.root = DefaultRootWindow(m_display); + event.xmotion.subwindow = None; + event.xmotion.time = CurrentTime; + event.xmotion.x = 0; + event.xmotion.y = 0; + event.xmotion.x_root = 0; + event.xmotion.y_root = 0; + event.xmotion.state = 0; + event.xmotion.is_hint = NotifyNormal; + event.xmotion.same_screen = True; + + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, m_xscreensaver, False, 0, &event); + } + + // force screen saver off and reset the timer + XForceScreenSaver(m_display, ScreenSaverReset); +} diff --git a/platform/CXWindowsScreenSaver.h b/platform/CXWindowsScreenSaver.h index 67ac2347..cf5c97e0 100644 --- a/platform/CXWindowsScreenSaver.h +++ b/platform/CXWindowsScreenSaver.h @@ -2,18 +2,22 @@ #define CXWINDOWSSCREENSAVER_H #include "IScreenSaver.h" +#include "stdmap.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else # include #endif +class IJob; +class CXWindowsScreen; + class CXWindowsScreenSaver : public IScreenSaver { public: // note -- the caller must ensure that Display* passed to c'tor isn't // being used in another call to Xlib when calling any method on this // object (including during the c'tor and d'tor) except processEvent(). - CXWindowsScreenSaver(Display*); + CXWindowsScreenSaver(CXWindowsScreen*, Display*); virtual ~CXWindowsScreenSaver(); // process X event. returns true if the event was handled. @@ -38,17 +42,42 @@ private: // send a notification void sendNotify(bool activated); + // find and set the running xscreensaver's window. returns true iff + // found. + bool findXScreenSaver(); + + // set the xscreensaver's window, updating the activation state flag + void setXScreenSaver(Window); + + // returns true if the window appears to be the xscreensaver window + bool isXScreenSaver(Window) const; + // set xscreensaver's activation state flag. sends notification // if the state has changed. - void setXScreenSaver(bool activated); - - // find the running xscreensaver's window - void updateXScreenSaver(); + void setXScreenSaverActive(bool activated); // send a command to xscreensaver void sendXScreenSaverCommand(Atom, long = 0, long = 0); + // watch all windows that could potentially be the xscreensaver for + // the events that will confirm it. + void watchForXScreenSaver(); + + // stop watching all watched windows + void clearWatchForXScreenSaver(); + + // add window to the watch list + void addWatchXScreenSaver(Window window); + + // called periodically to prevent the screen saver from starting + void disableCallback(void*); + private: + typedef std::map CWatchList; + + // the event loop object + CXWindowsScreen* m_screen; + // the X display Display* m_display; @@ -67,6 +96,9 @@ private: // dummy window to receive xscreensaver repsonses Window m_xscreensaverSink; + // potential xscreensaver windows being watched + CWatchList m_watchWindows; + // atoms used to communicate with xscreensaver's window Atom m_atomScreenSaver; Atom m_atomScreenSaverVersion; @@ -78,6 +110,9 @@ private: int m_interval; int m_preferBlanking; int m_allowExposures; + + // the job used to invoke disableCallback + IJob* m_disableJob; }; #endif diff --git a/platform/CXWindowsUtil.cpp b/platform/CXWindowsUtil.cpp index 8279d084..253a3362 100644 --- a/platform/CXWindowsUtil.cpp +++ b/platform/CXWindowsUtil.cpp @@ -13,7 +13,6 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, int* format, bool deleteProperty) { assert(display != NULL); - assert(data != NULL); Atom actualType; int actualDatumSize; @@ -59,7 +58,13 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, } // append data - data->append((char*)rawData, numBytes); + if (data != NULL) { + data->append((char*)rawData, numBytes); + } + else { + // data is not required so don't try to get any more + bytesLeft = 0; + } // done with returned data XFree(rawData); @@ -78,7 +83,7 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, *format = static_cast(actualDatumSize); } - log((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, data->size())); + log((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); return true; } diff --git a/server/CServer.cpp b/server/CServer.cpp index fdbb808f..d869d8e9 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -1284,6 +1284,12 @@ CServer::handshakeClient(void* vsocket) // now connected; client no longer subject to timeout. } + // activate screen saver on new client if active on the + // primary screen + if (m_primary->isScreenSaverActive()) { + protocol->sendScreenSaver(true); + } + // handle messages from client. returns when the client // disconnects. log((CLOG_NOTE "client \"%s\" has connected", name.c_str())); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 1537af18..24d97652 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -545,6 +545,13 @@ CXWindowsPrimaryScreen::isLockedToScreen() const return false; } +bool +CXWindowsPrimaryScreen::isScreenSaverActive() const +{ + CDisplayLock display(this); + return getScreenSaver()->isActive(); +} + void CXWindowsPrimaryScreen::onOpenDisplay(Display* display) { diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index b3f0829f..408c0a2d 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -26,6 +26,7 @@ public: virtual void getClipboard(ClipboardID, IClipboard*) const; virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; + virtual bool isScreenSaverActive() const; protected: // CXWindowsScreen overrides diff --git a/server/Makefile.am b/server/Makefile.am index 0d145475..7c7af55f 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -29,6 +29,8 @@ synergyd_LDADD = \ $(DEPTH)/base/libbase.a \ $(X_LIBS) \ $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ -lX11 \ $(X_EXTRA_LIBS) \ $(NULL) diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 036e4885..94db6bfa 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -88,6 +88,9 @@ public: // any other reason that the user should not be allowed to switch // screens. virtual bool isLockedToScreen() const = 0; + + // return true if the screen saver is activated + virtual bool isScreenSaverActive() const = 0; }; #endif From 7100e07d2bae4d495b6e456677061ce8150ecb32 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 23 Jun 2002 21:48:33 +0000 Subject: [PATCH 203/807] now disabling disable job timer when forcing screen saver activation. previously the timer would deactivate the screen saver shortly after activation. job timer is restored when the screen saver is deactivated. --- platform/CXWindowsScreenSaver.cpp | 17 ++++++++++++++--- platform/CXWindowsScreenSaver.h | 3 +++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/platform/CXWindowsScreenSaver.cpp b/platform/CXWindowsScreenSaver.cpp index c6dbddc6..6e6c7116 100644 --- a/platform/CXWindowsScreenSaver.cpp +++ b/platform/CXWindowsScreenSaver.cpp @@ -20,7 +20,8 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( m_display(display), m_notify(None), m_xscreensaver(None), - m_xscreensaverActive(false) + m_xscreensaverActive(false), + m_disabled(false) { // screen saver disable callback m_disableJob = new TMethodJob(this, @@ -165,8 +166,8 @@ CXWindowsScreenSaver::setNotify(Window notify) void CXWindowsScreenSaver::enable() { -log((CLOG_INFO "enable screensaver")); // for xscreensaver + m_disabled = false; m_screen->removeTimer(m_disableJob); // for built-in X screen saver @@ -177,9 +178,9 @@ log((CLOG_INFO "enable screensaver")); void CXWindowsScreenSaver::disable() { -log((CLOG_INFO "disable screensaver")); // for xscreensaver. 5 seconds should be plenty often to // suppress the screen saver. + m_disabled = true; m_screen->addTimer(m_disableJob, 5.0); // use built-in X screen saver @@ -193,6 +194,11 @@ log((CLOG_INFO "disable screensaver")); void CXWindowsScreenSaver::activate() { + // remove disable job timer + if (m_disabled) { + m_screen->removeTimer(m_disableJob); + } + // try xscreensaver findXScreenSaver(); if (m_xscreensaver != None) { @@ -207,6 +213,11 @@ CXWindowsScreenSaver::activate() void CXWindowsScreenSaver::deactivate() { + // reinstall disable job timer + if (m_disabled) { + m_screen->addTimer(m_disableJob, 5.0); + } + // try xscreensaver findXScreenSaver(); if (m_xscreensaver != None) { diff --git a/platform/CXWindowsScreenSaver.h b/platform/CXWindowsScreenSaver.h index cf5c97e0..95eb9830 100644 --- a/platform/CXWindowsScreenSaver.h +++ b/platform/CXWindowsScreenSaver.h @@ -111,6 +111,9 @@ private: int m_preferBlanking; int m_allowExposures; + // true iff the disabled job timer is installed + bool m_disabled; + // the job used to invoke disableCallback IJob* m_disableJob; }; From 76db45783046e6a0b244c34bed3467a7b4d34622 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 23 Jun 2002 21:53:31 +0000 Subject: [PATCH 204/807] win32 screen saver now handled. --- client/CMSWindowsSecondaryScreen.cpp | 18 +++++ client/CMSWindowsSecondaryScreen.h | 1 + platform/CMSWindowsScreen.cpp | 21 ++++- platform/CMSWindowsScreen.h | 6 ++ platform/platform.dsp | 8 ++ server/CMSWindowsPrimaryScreen.cpp | 59 +++++++++++++- server/CMSWindowsPrimaryScreen.h | 5 ++ server/CSynergyHook.cpp | 116 ++++++++++++++++++++++----- server/CSynergyHook.h | 13 ++- 9 files changed, 221 insertions(+), 26 deletions(-) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 0602a05f..c040479b 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -2,6 +2,7 @@ #include "CClient.h" #include "CClipboard.h" #include "CMSWindowsClipboard.h" +#include "CMSWindowsScreenSaver.h" #include "CPlatform.h" #include "XScreen.h" #include "CLock.h" @@ -97,6 +98,9 @@ CMSWindowsSecondaryScreen::open(CClient* client) grabClipboard(id); } + // disable the screen saver + getScreenSaver()->disable(); + // hide the cursor m_active = true; leave(); @@ -107,6 +111,9 @@ CMSWindowsSecondaryScreen::close() { assert(m_client != NULL); + // restore the screen saver settings + getScreenSaver()->enable(); + // close the display closeDisplay(); @@ -382,6 +389,17 @@ CMSWindowsSecondaryScreen::grabClipboard(ClipboardID /*id*/) } } +void +CMSWindowsSecondaryScreen::screenSaver(bool activate) +{ + if (activate) { + getScreenSaver()->activate(); + } + else { + getScreenSaver()->deactivate(); + } +} + void CMSWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const { diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 34d456f0..e5a6452d 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -36,6 +36,7 @@ public: virtual void mouseWheel(SInt32 delta); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); + virtual void screenSaver(bool activate); virtual void getMousePos(SInt32& x, SInt32& y) const; virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; virtual SInt32 getJumpZoneSize() const; diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index c3c08105..a9b78907 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -1,4 +1,5 @@ #include "CMSWindowsScreen.h" +#include "CMSWindowsScreenSaver.h" #include "CThread.h" #include "CLock.h" #include "TMethodJob.h" @@ -27,7 +28,8 @@ CMSWindowsScreen::CMSWindowsScreen() : m_cursor(NULL), m_x(0), m_y(0), m_w(0), m_h(0), - m_thread(0) + m_thread(0), + m_screenSaver(NULL) { assert(s_screen == NULL); s_screen = this; @@ -114,13 +116,20 @@ CMSWindowsScreen::openDisplay() // let subclass prep display onOpenDisplay(); + + // initialize the screen saver + m_screenSaver = new CMSWindowsScreenSaver(); } void CMSWindowsScreen::closeDisplay() { - assert(s_instance != NULL); - assert(m_class != 0); + assert(s_instance != NULL); + assert(m_class != 0); + + // done with screen saver + delete m_screenSaver; + m_screenSaver = NULL; // let subclass close down display onCloseDisplay(); @@ -203,6 +212,12 @@ CMSWindowsScreen::isCurrentDesktop(HDESK desk) const getCurrentDesktopName()); } +CMSWindowsScreenSaver* +CMSWindowsScreen::getScreenSaver() const +{ + return m_screenSaver; +} + void CMSWindowsScreen::getEvent(MSG* msg) const { diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index 85dc91bb..110bc499 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -7,6 +7,7 @@ #define WIN32_LEAN_AND_MEAN #include +class CMSWindowsScreenSaver; class CThread; class CMSWindowsScreen { @@ -61,6 +62,10 @@ protected: // windows 95/98/me. bool isCurrentDesktop(HDESK desk) const; + // get the screen saver object + CMSWindowsScreenSaver* + getScreenSaver() const; + // wait for and get the next message. cancellable. void getEvent(MSG*) const; @@ -91,6 +96,7 @@ private: SInt32 m_x, m_y; SInt32 m_w, m_h; DWORD m_thread; + CMSWindowsScreenSaver* m_screenSaver; static CMSWindowsScreen* s_screen; }; diff --git a/platform/platform.dsp b/platform/platform.dsp index 1a7efbca..7ebcc139 100644 --- a/platform/platform.dsp +++ b/platform/platform.dsp @@ -95,6 +95,10 @@ SOURCE=.\CMSWindowsScreen.cpp # End Source File # Begin Source File +SOURCE=.\CMSWindowsScreenSaver.cpp +# End Source File +# Begin Source File + SOURCE=.\CPlatform.cpp # End Source File # End Group @@ -111,6 +115,10 @@ SOURCE=.\CMSWindowsScreen.h # End Source File # Begin Source File +SOURCE=.\CMSWindowsScreenSaver.h +# End Source File +# Begin Source File + SOURCE=.\CPlatform.h # End Source File # Begin Source File diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 417b5683..bf83c4cf 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -1,6 +1,7 @@ #include "CMSWindowsPrimaryScreen.h" #include "CServer.h" #include "CMSWindowsClipboard.h" +#include "CMSWindowsScreenSaver.h" #include "CPlatform.h" #include "XScreen.h" #include "XSynergy.h" @@ -34,15 +35,29 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : m_setRelay = (SetRelayFunc)GetProcAddress(m_hookLibrary, "setRelay"); m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install"); m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall"); + m_init = (InitFunc)GetProcAddress(m_hookLibrary, "init"); + m_cleanup = (CleanupFunc)GetProcAddress(m_hookLibrary, "cleanup"); if (m_setZone == NULL || m_setRelay == NULL || m_install == NULL || - m_uninstall == NULL) { + m_uninstall == NULL || + m_init == NULL || + m_cleanup == NULL) { log((CLOG_ERR "invalid hook library")); FreeLibrary(m_hookLibrary); throw XScreenOpenFailure(); } + // get the screen saver functions + m_installScreenSaver = (InstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "installScreenSaver"); + m_uninstallScreenSaver = (UninstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "uninstallScreenSaver"); + if (m_installScreenSaver == NULL || m_uninstallScreenSaver == NULL) { + // disable uninstall if install is unavailable + m_uninstallScreenSaver = NULL; + } + // detect operating system m_is95Family = CPlatform::isWindows95Family(); @@ -56,6 +71,11 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() assert(m_hookLibrary != NULL); assert(m_window == NULL); + // uninstall screen saver hook + if (m_uninstallScreenSaver != NULL) { + m_uninstallScreenSaver(); + } + // done with hook library FreeLibrary(m_hookLibrary); } @@ -346,6 +366,12 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const return false; } +bool +CMSWindowsPrimaryScreen::isScreenSaverActive() const +{ + return getScreenSaver()->isActive(); +} + void CMSWindowsPrimaryScreen::onOpenDisplay() { @@ -355,6 +381,14 @@ CMSWindowsPrimaryScreen::onOpenDisplay() // save thread id. we'll need to pass this to the hook library. m_threadID = GetCurrentThreadId(); + // initialize hook library + m_init(m_threadID); + + // install the screen saver hook + if (m_installScreenSaver != NULL) { + m_installScreenSaver(); + } + // get the input desktop and switch to it if (m_is95Family) { if (!openDesktop()) { @@ -379,6 +413,14 @@ CMSWindowsPrimaryScreen::onCloseDisplay() switchDesktop(NULL); } + // uninstall the screen saver hook + if (m_uninstallScreenSaver != NULL) { + m_uninstallScreenSaver(); + } + + // cleanup hook library + m_cleanup(); + // clear thread id m_threadID = 0; @@ -507,6 +549,17 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) m_y = static_cast(msg->lParam); return true; + case SYNERGY_MSG_SCREEN_SAVER: + if (msg->wParam != 0) { + if (getScreenSaver()->checkStarted(msg->message, FALSE, 0)) { + m_server->onScreenSaver(true); + } + } + else { + m_server->onScreenSaver(false); + } + return true; + case WM_TIMER: // if current desktop is not the input desktop then switch to it if (!m_is95Family) { @@ -709,7 +762,7 @@ bool CMSWindowsPrimaryScreen::openDesktop() { // install hooks - m_install(m_threadID); + m_install(); // note -- we use a fullscreen window to grab input. it should // be possible to use a 1x1 window but i've run into problems @@ -819,7 +872,7 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) } // install hooks - m_install(m_threadID); + m_install(); // note -- we use a fullscreen window to grab input. it should // be possible to use a 1x1 window but i've run into problems diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 37db16ac..5e4a493f 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -30,6 +30,7 @@ public: virtual void getClipboard(ClipboardID, IClipboard*) const; virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; + virtual bool isScreenSaverActive() const; protected: // CMSWindowsScreen overrides @@ -106,10 +107,14 @@ private: // hook library stuff HINSTANCE m_hookLibrary; + InitFunc m_init; + CleanupFunc m_cleanup; InstallFunc m_install; UninstallFunc m_uninstall; SetZoneFunc m_setZone; SetRelayFunc m_setRelay; + InstallScreenSaverFunc m_installScreenSaver; + UninstallScreenSaverFunc m_uninstallScreenSaver; // stuff for restoring active window HWND m_lastForegroundWindow; diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index e3f70c5b..8440013b 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -45,6 +45,7 @@ static HANDLE g_keyHookThread = NULL; static DWORD g_keyHookThreadID = 0; static HANDLE g_keyHookEvent = NULL; static HHOOK g_keyboardLL = NULL; +static bool g_screenSaver = false; static bool g_relay = false; static SInt32 g_zoneSize = 0; static UInt32 g_zoneSides = 0; @@ -247,6 +248,14 @@ LRESULT CALLBACK getMessageHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { + if (g_screenSaver) { + MSG* msg = reinterpret_cast(lParam); + if (msg->message == WM_SYSCOMMAND && msg->wParam == SC_SCREENSAVE) { + // broadcast screen saver started message + PostThreadMessage(g_threadID, + SYNERGY_MSG_SCREEN_SAVER, TRUE, 0); + } + } if (g_relay) { MSG* msg = reinterpret_cast(lParam); if (msg->message == g_wmMouseWheel) { @@ -455,19 +464,46 @@ DllMain(HINSTANCE instance, DWORD reason, LPVOID) extern "C" { int -install(DWORD threadID) +init(DWORD threadID) { - assert(g_threadID == 0); - assert(g_hinstance != NULL); - assert(g_keyboard == NULL); - assert(g_mouse == NULL); - assert(g_cbt == NULL); - assert(g_wheelSupport != kWheelOld || g_getMessage == NULL); + assert(g_hinstance != NULL); + + // see if already initialized + if (g_threadID != 0) { + return 0; + } // save thread id. we'll post messages to this thread's // message queue. g_threadID = threadID; + return 1; +} + +int +cleanup(void) +{ + assert(g_hinstance != NULL); + + g_threadID = 0; + + return 1; +} + +int +install() +{ + assert(g_hinstance != NULL); + assert(g_keyboard == NULL); + assert(g_mouse == NULL); + assert(g_cbt == NULL); + assert(g_getMessage == NULL || g_screenSaver); + + // must be initialized + if (g_threadID == 0) { + return 0; + } + // set defaults g_relay = false; g_zoneSize = 0; @@ -520,8 +556,8 @@ install(DWORD threadID) return 0; } - // install GetMessage hook - if (g_wheelSupport == kWheelOld) { + // install GetMessage hook (unless already installed) + if (g_wheelSupport == kWheelOld && g_getMessage == NULL) { g_getMessage = SetWindowsHookEx(WH_GETMESSAGE, &getMessageHook, g_hinstance, @@ -563,9 +599,7 @@ install(DWORD threadID) int uninstall(void) { - assert(g_keyboard != NULL); - assert(g_mouse != NULL); - assert(g_cbt != NULL); + assert(g_hinstance != NULL); // uninstall hooks if (g_keyHookThread != NULL) { @@ -577,17 +611,22 @@ uninstall(void) g_keyHookThread = NULL; g_keyHookThreadID = 0; } - UnhookWindowsHookEx(g_keyboard); - UnhookWindowsHookEx(g_mouse); - UnhookWindowsHookEx(g_cbt); - if (g_getMessage != NULL) { + if (g_keyboard != NULL) { + UnhookWindowsHookEx(g_keyboard); + } + if (g_mouse != NULL) { + UnhookWindowsHookEx(g_mouse); + } + if (g_cbt != NULL) { + UnhookWindowsHookEx(g_cbt); + } + if (g_getMessage != NULL && !g_screenSaver) { UnhookWindowsHookEx(g_getMessage); + g_getMessage = NULL; } g_keyboard = NULL; g_mouse = NULL; g_cbt = NULL; - g_getMessage = NULL; - g_threadID = 0; // show the cursor restoreCursor(); @@ -595,6 +634,47 @@ uninstall(void) return 1; } +int +installScreenSaver(void) +{ + assert(g_hinstance != NULL); + + // must be initialized + if (g_threadID == 0) { + return 0; + } + + // generate screen saver messages + g_screenSaver = true; + + // install hook unless it's already installed + if (g_getMessage == NULL) { + g_getMessage = SetWindowsHookEx(WH_GETMESSAGE, + &getMessageHook, + g_hinstance, + 0); + } + + return (g_getMessage != NULL) ? 1 : 0; +} + +int +uninstallScreenSaver(void) +{ + assert(g_hinstance != NULL); + + // uninstall hook unless the mouse wheel hook is installed + if (g_getMessage != NULL && g_threadID == 0) { + UnhookWindowsHookEx(g_getMessage); + g_getMessage = NULL; + } + + // screen saver hook is no longer installed + g_screenSaver = false; + + return 1; +} + void setZone(UInt32 sides, SInt32 x, SInt32 y, SInt32 w, SInt32 h, diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index 03f5eb00..026a1c3c 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -22,17 +22,26 @@ #define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y #define SYNERGY_MSG_POST_WARP WM_APP + 0x0015 // x; y #define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0016 // delta; +#define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0017 // activated; extern "C" { -typedef int (*InstallFunc)(DWORD targetQueueThreadID); +typedef int (*InitFunc)(DWORD targetQueueThreadID); +typedef int (*CleanupFunc)(void); +typedef int (*InstallFunc)(void); typedef int (*UninstallFunc)(void); +typedef int (*InstallScreenSaverFunc)(void); +typedef int (*UninstallScreenSaverFunc)(void); typedef void (*SetZoneFunc)(UInt32, SInt32, SInt32, SInt32, SInt32, SInt32); typedef void (*SetRelayFunc)(void); -CSYNERGYHOOK_API int install(DWORD); +CSYNERGYHOOK_API int init(DWORD); +CSYNERGYHOOK_API int cleanup(void); +CSYNERGYHOOK_API int install(void); CSYNERGYHOOK_API int uninstall(void); +CSYNERGYHOOK_API int installScreenSaver(void); +CSYNERGYHOOK_API int uninstallScreenSaver(void); CSYNERGYHOOK_API void setZone(UInt32 sides, SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize); From 80d11df2f9fd1f197d702836aafddc166f6c5c3a Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 23 Jun 2002 21:54:05 +0000 Subject: [PATCH 205/807] checkpoint. --- notes | 423 ++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 294 insertions(+), 129 deletions(-) diff --git a/notes b/notes index 75a2c492..a1ebdb65 100644 --- a/notes +++ b/notes @@ -9,15 +9,7 @@ query info --> --- use automake - -order includes according to source directory -fix function definitions - return type on first line - function name in 1st column on 2nd line - parameters at 1st tab stop starting 3rd line, one per line -fix parameter indentation - in header file: 7th tab stop (not 8th) - in source file: 1st tab stop (not 8th) +use doxygen --- HTTP stuff @@ -30,13 +22,54 @@ HTTP stuff handy for debugging at least --- -CThreadRep::threadSignalHandler not shutdown on assert - thread stays around and holds our resources (like sockets) - should ensure that it terminates correctly +make generic input devices? + replace mouse and keyboard with button and valuator devices? + could fairly easily add new devices then + valuators would have to come in groups that change together + mouse position would be two ganged valuators + key events are not simply button presses though + they have a non-boolean character, shift state, and repeat count + maybe another device type: {data, state, repeat} + +must handle on-the-fly addition/removal of devices + USB allows hotplugging and we should support, say, an added joystick + +must have device negotiation + at very least it negotiates mouse/keyboard at start + +--- +should add clipboard data type format negotiation + server sends permitted data type formats + client responds with known formats in permitted list + client must only send permitted formats + server can discard non-permitted formats + server must only send known, permitted formats to client + formats are names of the form [a-zA-Z0-9_/]+ + server must be able to translate between all permitted formats + example: text formats: + text/plain/CRLF -- windows plain text + text/plain/LF -- unix plain text + text/plain/CR -- mac plain text + text/rtf -- rich text (does that require a particular newline?) + or image formats: + image/BMP + image/RAW + image/XPM + +this will allow future versions to interoperate better. it also +allows compatibility with versions that are identical except for +the support clipboard formats, obviating bumping up the protocol +version number. + +--- +hot keys + should have keyboard shortcuts to jump to screens --- win32: + need icon provide taskbar icon + provide control panel? win32 dll: should make this as small as possible @@ -44,15 +77,15 @@ win32 dll: use custom _DllMainCRTStartup(); just call DllMain() from it. use /MERGE linker switch to merge sections +win32 service: + need message table for proper event reporting + clean up c:\winnt\synergy.sgc desktop switcher program failure when running synergy as service returns access denied from CreateDesktop() don't know why -client will need to be able to retry connecting to server - in case server isn't ready yet, client can camp - --- bug with half-duplex keys win32 server sent num-lock to grace @@ -68,60 +101,6 @@ must send up key events when leaving screen for keys still down ctrl and alt up not sent to grace grace thinks ctrl and alt are down 'til user physically press/releases them ---- -CClient and CServer: - not checking in stop() that we're actually running - must mutex m_primary/m_screen - they could be NULL if in the middle of shutting down - ---- -win32 screen saver - win95 etc: - keybd_event can terminate screen saver but not mouse_event - keybd_event resets screen saver start timer but not mouse_event - to kill screen saver (if it's known to be running): - PostMessage (GetActiveWindow(), WM_CLOSE, 0, 0L); - to find if screen saver is running: - FindWindow ("WindowsScreenSaverClass", NULL); - win nt 4: - mouse_event resets screen saver start timer but not keybd_event - neither can stop screen saver because it runs in a separate desktop - to kill screen saver: - BOOL CALLBACK KillScreenSaverFunc(HWND hwnd, LPARAM lParam) - { - if(IsWindowVisible(hwnd)) - PostMessage(hwnd, WM_CLOSE, 0, 0); - return TRUE; - } - - HDESK hdesk; - - hdesk = OpenDesktop(TEXT("Screen-saver"), - 0, - FALSE, - DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS); - if (hdesk) - { - EnumDesktopWindows(hdesk, KillScreenSaverFunc, 0); - CloseDesktop(hdesk); - } - ---- -unix: - restarting fails to connect until a user logs in: - Xlib: no protocol specified - this is caused by login manager - all clients are rejected, except those started by manager itself - workaround is to have synergy started by login manager - should then use --no-restart, though that's not necessary - affects client and server - cannot switch screens on login screen: - xdm,kdm grab keyboard for duration of login screen - synergy cannot switch unless it can grab keyboard - gdm doesn't appear to grab keyboard or mouse for duration - affects server, only - check if DISPLAY env var is unset. use ":0.0" as default. - --- bug in updating screens saw a goofy set of screens after update @@ -130,11 +109,24 @@ bug in updating screens C D E +--- +should switch to user nobody (or something similar) if running as root + for security reasons + will make it impossible to overwrite /etc/synergy.conf + if that file is only root writable (as it should be) + +--- +Xsetup file may have to kill previous synergy + or synergy should exit if there's already a synergy running + --- not handling international characters --- -not handling screen saver at all yet +screen saver won't lock screen if on secondary screen + because synergy has mouse/keyboard grabbed + synergy will immediately release grabs when screen saver activates + but looks like xscreensaver wont map window until it grabs --- not distinguishing between caps lock and shift lock @@ -152,17 +144,15 @@ currently sending all clipboards to all clients add filtering mechanism --- -sometimes not sending correct clipboard - some kind of race condition i think - can select text then switch and get empty clipboard - switching back to owner screen then out gets correct clipboard - dunno if this is still a problem +sometimes jumping too far into secondary screen + probably bogus delta (adding in center pixel coords into delta?) + this is with an X primary and secondary + don't know which matters but probably X primary --- -win32 not sending correct clipboard - got 43 bytes sent for a 3 byte selection - remaining bytes were 0 - problem persisted; also got 43 bytes for a 2 byte selection +not handling large motif selections + there must be a limit because properties can only be so big + don't know protocol for transfer, though --- need timeout on CXWindowsClipboard::CReply objects @@ -188,7 +178,50 @@ win32 hook DLL sometimes not unloaded when server is killed? try to figure out why it's not unloading --- -test: +avoid fullscreen transparent window on win32 + using CBT hook to discard activation/focus messages + but it's not being called when we lose keyboard input + also, we sometimes don't get the keyboard input to start with + +--- +keyboard hook problems (win32) + now passing keyboard events through in most cases + this fixes problem on isabel where toggle lights don't toggle + and num-lock behaved incorrectly (generated wrong keys). + seems the DefWindowProc() needed to process the keys + unfortunately, keys sometimes leak into wrong app. seems related + to times when we don't get the keyboard input. key events are + not delivered to app on primary until cursor returns to primary. + no idea how that's possible. + current have some code to check active window and reset it in + keyboard hook. that code doesn't work. + seem to be having problems with windows key now, too. looks like + a down is sent but not the corresponding up so secondary system + thinks the key is always down. + +--- +try to determine keyboard quirks automatically + in particular, set the half-duplex flags + maybe KBD extension will help determine this + +--- +key events sent to console on win me (95/98?) are very slow + 1/4 to 1/2 second delay before key up is processed + key up log message is also delayed + console causing a system-wide delay? + +--- +adjust thread priorities on win32 + maybe remove changes altogether + currently isabel starts single-stepping mouse when dragging window + sometimes it goes normal speed but it's mostly very slow + even a very high priority doesn't seem to help + haven't tried real-time priority + a very high priority on client fixes delay when typeing into console + is it the output console causing the slowness? + +--- +results: X primary ctrl+alt+keypad_add, ctrl+alt_keypad_minus -> eaten by primary X server ctrl+alt+backspace probably eaten by primary X server if so configured @@ -254,49 +287,6 @@ test: check accessibility shortcuts work on secondary check accessibility features work on secondary ---- -avoid fullscreen transparent window on win32 - using CBT hook to discard activation/focus messages - but it's not being called when we lose keyboard input - also, we sometimes don't get the keyboard input to start with - ---- -keyboard hook problems (win32) - now passing keyboard events through in most cases - this fixes problem on isabel where toggle lights don't toggle - and num-lock behaved incorrectly (generated wrong keys). - seems the DefWindowProc() needed to process the keys - unfortunately, keys sometimes leak into wrong app. seems related - to times when we don't get the keyboard input. key events are - not delivered to app on primary until cursor returns to primary. - no idea how that's possible. - current have some code to check active window and reset it in - keyboard hook. that code doesn't work. - seem to be having problems with windows key now, too. looks like - a down is sent but not the corresponding up so secondary system - thinks the key is always down. - ---- -try to determine keyboard quirks automatically - in particular, set the half-duplex flags - maybe KBD extension will help determine this - ---- -key events sent to console on win me (95/98?) are very slow - 1/4 to 1/2 second delay before key up is processed - key up log message is also delayed - console causing a system-wide delay? - ---- -adjust thread priorities on win32 - maybe remove changes altogether - currently isabel starts single-stepping mouse when dragging window - sometimes it goes normal speed but it's mostly very slow - even a very high priority doesn't seem to help - haven't tried real-time priority - a very high priority on client fixes delay when typeing into console - is it the output console causing the slowness? - --- Accessibility Shortcuts @@ -340,12 +330,6 @@ Q179905 suggestion for bypassing hook code when called by debugger. may prevent system from getting locked up when debugging. ---- -how is CTCPSocket::connect() able to cancel? - looks like it can't - make it asynchronous and use poll() to wait for connection - use testCancel() to make it cancellable - --- win32 rel notes: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows\NoInteractiveServices @@ -359,3 +343,184 @@ win32 rel notes: maybe possible to get around NoInteractiveServices modify DACLs on windows station/desktops to impersonate logged on user see "Interacting with the User by a Win32 Service" + +--- +unix release notes: + restarting fails to connect until a user logs in: + Xlib: no protocol specified + this is caused by login manager + all clients are rejected, except those started by manager itself + workaround is to have synergy started by login manager + should then use --no-restart, though that's not necessary + affects client and server + cannot switch screens on login screen: + xdm,kdm grab keyboard for duration of login screen + synergy cannot switch unless it can grab keyboard + gdm doesn't appear to grab keyboard or mouse for duration + affects server, only + +=== feedback === +send all toggle states instead of (individual) toggle ups/downs (gspencer) + Actually, Teleffect already does that too, but what I meant was that + each time the state of a "toggle" key (caps lock, scroll lock, num lock) + changes on the master, I think it should force the client to take on + that state, instead of just toggling on the client. That way when Word + (on the client) helpfullly turns off the caps lock, it doesn't get + toggled back on when I turn off the caps lock on the master. Ideally, + it would just notice that Word turned off the caps lock on the client + and turn it off on the master (and all other clients) too, but I think + that's probably too complicated to implement. + +dragging windows is too slow + grace (as client) exhibits this + dunno if it happens with audrey2 as the client + liz says her windows laptop does it too + +double clicking NT binaries fails (liz) + add note to README or use separate binaries for services. client + would also need a dialog to query options. server would probably + need one too. use dialogs if there are no command line args. + +how to exit (liz) + no good way to exit on windows + +win32 console disappears too quickly (liz) + maybe show a message box if exiting to due an error + +better support for reloading configuration (liz) + use SIGHUP on unix + use an app message on win32 + provide access through a control panel or taskbar icon + +non-functional on ctrl+alt+del screen in win2k (kurt) + i suppose it must be when win2k is the client. mouse input wasn't + working. keyboard probably didn't work either. + +--- +automake stuff + rebuild: + * 'autoheader' - creates config.h.in + * 'touch NEWS README AUTHORS ChangeLog' + * 'touch stamp-h' + * aclocal - adds aclocal.m4 to directory. Defines some m4 macros used by the auto tools. + * 'autoconf '- creates configure from configure.in + * 'automake' - Creates Makefile.in from Makefile.am + * './configure' - creates Makefile from Makefile.in + * 'make' + + funky functions: + snprintf -- ick! + some return negative value -- no problem + some return truncated length -- uh oh + some return buffer length without truncation -- huh? + some actually overrun the buffer -- must not build on those! + + use AC_LIBOBJ(function) for missing/broken functions + adds function.c to build + must add @LIBOBJS@ to *_LIBADD + or AC_REPLACE_FUNCS(function ...) + automatically calls AC_LIBOBJ for missing functions + use AC_CHECK_FUNC or AC_CHECK_FUNCS to detect missing functions + use AC_CHECK_LIB first if function not in std C lib + + maybe AC_CHECK_TYPE for size_t and ssize_t + +replace sys/time.h with: +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + + what does `make install' do? + + check in derived files? + aclocal.m4 + config.h.in + configure + +used library functions: + accept + bind + chdir + close + connect + dup + fork + fprintf + getenv + gethostbyaddr + gethostbyname + gethostname + getpeername + getprotobyname + getprotobynumber + getservbyname + getservbyport + getsockname + getsockopt + gettimeofday + inet_addr + inet_ntoa + ioctl + listen + memcpy + memmove + memset + nanosleep + open + openlog + poll + read + recv + recvfrom + send + sendto + setsid + setsockopt + shutdown + sigaction + sigaddset + sigemptyset + socket + sprintf + strcat + strcmp + strerror + strrchr + syslog + umask + vsnprintf + write + +included files + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +these put stuff in std::. replace with .h versions or use std::? + 3 #include + 3 #include + 1 #include + 3 #include + 4 #include + 12 #include + 1 #include + +can we use AC_REPLACE_FUNCS and only build the missing function files +in a single directory. do we have to have a separate configure.in +script to do that? From 1377882a9f44a0a00ed3c7d8137903d7010c00db Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 23 Jun 2002 23:24:22 +0000 Subject: [PATCH 206/807] fixed handling of jumping to primary when screen saver starts and back to secondary when it stops. also now redirecting keyboard input to root window when screen saver starts; this allows the user to type in the lock dialog and also effectively discards any input used to deactivate the screen saver. --- server/CMSWindowsPrimaryScreen.cpp | 6 ++++-- server/CMSWindowsPrimaryScreen.h | 2 +- server/CServer.cpp | 30 +++++++++++++++--------------- server/CServer.h | 3 ++- server/CXWindowsPrimaryScreen.cpp | 13 ++++++++++--- server/CXWindowsPrimaryScreen.h | 2 +- synergy/IPrimaryScreen.h | 6 ++++-- 7 files changed, 37 insertions(+), 25 deletions(-) diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index bf83c4cf..e35d2811 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -165,7 +165,7 @@ CMSWindowsPrimaryScreen::close() } void -CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) +CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) { log((CLOG_INFO "entering primary at %d,%d", x, y)); assert(m_active == true); @@ -174,7 +174,9 @@ CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) enterNoWarp(); // warp to requested location - warpCursor(x, y); + if (!forScreenSaver) { + warpCursor(x, y); + } } bool diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 5e4a493f..3a3bfdfd 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -19,7 +19,7 @@ public: virtual void stop(); virtual void open(CServer*); virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool); virtual bool leave(); virtual void onConfigure(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); diff --git a/server/CServer.cpp b/server/CServer.cpp index d869d8e9..6ae97436 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -588,7 +588,7 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) } // switch screen - switchScreen(newScreen, x, y); + switchScreen(newScreen, x, y, false); return true; } @@ -692,7 +692,7 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) // otherwise screen screens else { - switchScreen(newScreen, m_x, m_y); + switchScreen(newScreen, m_x, m_y, false); } } @@ -723,11 +723,7 @@ CServer::onScreenSaver(bool activated) // jump to primary screen if (m_active != m_primaryInfo) { -// FIXME -- should have separate "center" pixel reported by screen - m_x = m_primaryInfo->m_x + (m_primaryInfo->m_w >> 1); - m_y = m_primaryInfo->m_y + (m_primaryInfo->m_h >> 1); - m_active = m_primaryInfo; - m_primary->enter(m_x, m_y); + switchScreen(m_primaryInfo, 0, 0, true); } } else { @@ -752,8 +748,8 @@ CServer::onScreenSaver(bool activated) m_ySaver = screen->m_y + screen->m_h - screen->m_zoneSize - 1; } - // now jump - switchScreen(screen, m_xSaver, m_ySaver); + // jump + switchScreen(screen, m_xSaver, m_ySaver, false); } // reset state @@ -794,7 +790,7 @@ CServer::isLockedToScreenNoLock() const } void -CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y) +CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y, bool screenSaver) { assert(dst != NULL); assert(x >= dst->m_x && y >= dst->m_y); @@ -840,7 +836,7 @@ CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y) // enter new screen if (m_active->m_protocol == NULL) { - m_primary->enter(x, y); + m_primary->enter(x, y, screenSaver); } else { m_active->m_protocol->sendEnter(x, y, m_seqNum, @@ -1621,20 +1617,24 @@ CServer::removeConnection(const CString& name) assert(index != m_screens.end()); // if this is active screen then we have to jump off of it - if (m_active == index->second && m_active != m_primaryInfo) { + CScreenInfo* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; + if (active == index->second && active != m_primaryInfo) { // record new position (center of primary screen) // FIXME -- should have separate "center" pixel reported by screen m_x = m_primaryInfo->m_x + (m_primaryInfo->m_w >> 1); m_y = m_primaryInfo->m_y + (m_primaryInfo->m_h >> 1); // don't notify active screen since it probably already disconnected - log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y)); + log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y)); // cut over m_active = m_primaryInfo; - // enter new screen - m_primary->enter(m_x, m_y); + // enter new screen (unless we already have because of the + // screen saver) + if (m_activeSaver == NULL) { + m_primary->enter(m_x, m_y, false); + } } // if this screen had the cursor when the screen saver activated diff --git a/server/CServer.h b/server/CServer.h index 188ccc86..ed344095 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -137,7 +137,8 @@ private: bool isLockedToScreenNoLock() const; // change the active screen - void switchScreen(CScreenInfo*, SInt32 x, SInt32 y); + void switchScreen(CScreenInfo*, + SInt32 x, SInt32 y, bool forScreenSaver); // lookup neighboring screen CScreenInfo* getNeighbor(CScreenInfo*, CConfig::EDirection) const; diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 24d97652..fe16effa 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -323,20 +323,27 @@ CXWindowsPrimaryScreen::close() } void -CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) +CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) { - log((CLOG_INFO "entering primary at %d,%d", x, y)); + log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreenSaver ? " for screen saver" : "")); assert(m_active == true); assert(m_window != None); CDisplayLock display(this); // warp to requested location - XWarpPointer(display, None, m_window, 0, 0, 0, 0, x, y); + if (!forScreenSaver) { + XWarpPointer(display, None, m_window, 0, 0, 0, 0, x, y); + } // unmap the grab window. this also ungrabs the mouse and keyboard. XUnmapWindow(display, m_window); + // redirect input to root window + if (forScreenSaver) { + XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); + } + // remove all input events for grab window XEvent event; while (XCheckWindowEvent(display, m_window, diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 408c0a2d..c8996650 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -15,7 +15,7 @@ public: virtual void stop(); virtual void open(CServer*); virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool); virtual bool leave(); virtual void onConfigure(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 94db6bfa..c68d0bad 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -41,9 +41,11 @@ public: // called when the user navigates back to the primary screen. // warp the cursor to the given coordinates, unhide it, and // ungrab the input devices. every call to enter has a matching - // call to leave() which preceeds it, however the screen can + // call to leave() which preceeds it, however the screen should // assume an implicit call to enter() in the call to open(). - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + // if warpCursor is false then do not warp the mouse. + virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, + bool forScreenSaver) = 0; // called when the user navigates off the primary screen. hide // the cursor and grab exclusive access to the input devices. From 6cc3b50d3bcf9e5302668a20e592647d3ca37f4f Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 26 Jun 2002 12:44:52 +0000 Subject: [PATCH 207/807] fixed re-entrant calls to X bug. --- platform/CXWindowsScreen.cpp | 5 ++++- platform/CXWindowsScreenSaver.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index 4e88b5ed..4f223569 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -387,7 +387,10 @@ CXWindowsScreen::processEvent(XEvent* xevent) } // let screen saver have a go - m_screenSaver->processEvent(xevent); + { + CLock lock(&m_mutex); + m_screenSaver->processEvent(xevent); + } return false; } diff --git a/platform/CXWindowsScreenSaver.h b/platform/CXWindowsScreenSaver.h index 95eb9830..c97eba39 100644 --- a/platform/CXWindowsScreenSaver.h +++ b/platform/CXWindowsScreenSaver.h @@ -16,7 +16,7 @@ class CXWindowsScreenSaver : public IScreenSaver { public: // note -- the caller must ensure that Display* passed to c'tor isn't // being used in another call to Xlib when calling any method on this - // object (including during the c'tor and d'tor) except processEvent(). + // object (including during the c'tor and d'tor). CXWindowsScreenSaver(CXWindowsScreen*, Display*); virtual ~CXWindowsScreenSaver(); From 78d28fd6e50b0d0029be4b63ba1711ad04af0982 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 26 Jun 2002 13:31:06 +0000 Subject: [PATCH 208/807] fixed getSize() to be non-blocking in CInputPacketStream. --- synergy/CInputPacketStream.cpp | 50 ++++++++++++++++++++++++++-------- synergy/CInputPacketStream.h | 2 ++ 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/synergy/CInputPacketStream.cpp b/synergy/CInputPacketStream.cpp index 76c03620..c29a6a04 100644 --- a/synergy/CInputPacketStream.cpp +++ b/synergy/CInputPacketStream.cpp @@ -32,7 +32,7 @@ CInputPacketStream::read(void* buffer, UInt32 n) // wait for entire message to be read. return immediately if // stream hungup. - if (getSizeNoLock() == 0) { + if (!waitForFullMessage()) { return 0; } @@ -60,24 +60,50 @@ CInputPacketStream::getSize() const UInt32 CInputPacketStream::getSizeNoLock() const { - while (!hasFullMessage()) { + while (!hasFullMessage() && getStream()->getSize() > 0) { // read more data - char buffer[4096]; - UInt32 n = getStream()->read(buffer, sizeof(buffer)); - - // return if stream hungup - if (n == 0) { - m_buffer.hangup(); - return 0; + if (!getMoreMessage()) { + // stream hungup + return false; } - - // append to our buffer - m_buffer.write(buffer, n); } return m_size; } +bool +CInputPacketStream::waitForFullMessage() const +{ + while (!hasFullMessage()) { + // read more data + if (!getMoreMessage()) { + // stream hungup + return false; + } + } + + return true; +} + +bool +CInputPacketStream::getMoreMessage() const +{ + // read more data + char buffer[4096]; + UInt32 n = getStream()->read(buffer, sizeof(buffer)); + + // return if stream hungup + if (n == 0) { + m_buffer.hangup(); + return false; + } + + // append to our buffer + m_buffer.write(buffer, n); + + return true; +} + bool CInputPacketStream::hasFullMessage() const { diff --git a/synergy/CInputPacketStream.h b/synergy/CInputPacketStream.h index 11d41703..03fd32be 100644 --- a/synergy/CInputPacketStream.h +++ b/synergy/CInputPacketStream.h @@ -21,6 +21,8 @@ public: private: UInt32 getSizeNoLock() const; + bool waitForFullMessage() const; + bool getMoreMessage() const; bool hasFullMessage() const; private: From d9b2c59d02b434ef63957ea75e213ebd2da93d48 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 26 Jun 2002 13:48:08 +0000 Subject: [PATCH 209/807] client now compresses mouse motion events. this fixes slow dragging on grace, possibly on win32 too. --- client/CClient.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++++ client/CClient.h | 8 ++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index b7284df1..453d6d45 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -297,8 +297,16 @@ CClient::runSession(void*) bool fail = false; try { + // no compressed mouse motion yet + m_compressMouse = false; + // handle messages from server for (;;) { + // if no input is pending then flush compressed mouse motion + if (input->getSize() == 0) { + flushCompressedMouse(); + } + // wait for reply log((CLOG_DEBUG2 "waiting for message")); UInt8 code[4]; @@ -462,6 +470,15 @@ CClient::closeSecondaryScreen() m_screen = NULL; } +void +CClient::flushCompressedMouse() +{ + if (m_compressMouse) { + m_compressMouse = false; + m_screen->mouseMove(m_xMouse, m_yMouse); + } +} + void CClient::onEnter() { @@ -473,6 +490,11 @@ CClient::onEnter() m_active = true; } log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, m_seqNum, mask)); + + // discard old compressed mouse motion, if any + m_compressMouse = false; + + // tell screen we're entering m_screen->enter(x, y, static_cast(mask)); } @@ -481,6 +503,9 @@ CClient::onLeave() { log((CLOG_DEBUG1 "recv leave")); + // send last mouse motion + flushCompressedMouse(); + // tell screen we're leaving m_screen->leave(); @@ -610,6 +635,9 @@ CClient::onSetClipboard() void CClient::onKeyDown() { + // get mouse up to date + flushCompressedMouse(); + UInt16 id, mask; { CLock lock(&m_mutex); @@ -623,6 +651,9 @@ CClient::onKeyDown() void CClient::onKeyRepeat() { + // get mouse up to date + flushCompressedMouse(); + UInt16 id, mask, count; { CLock lock(&m_mutex); @@ -637,6 +668,9 @@ CClient::onKeyRepeat() void CClient::onKeyUp() { + // get mouse up to date + flushCompressedMouse(); + UInt16 id, mask; { CLock lock(&m_mutex); @@ -650,6 +684,9 @@ CClient::onKeyUp() void CClient::onMouseDown() { + // get mouse up to date + flushCompressedMouse(); + SInt8 id; { CLock lock(&m_mutex); @@ -662,6 +699,9 @@ CClient::onMouseDown() void CClient::onMouseUp() { + // get mouse up to date + flushCompressedMouse(); + SInt8 id; { CLock lock(&m_mutex); @@ -680,6 +720,16 @@ CClient::onMouseMove() CLock lock(&m_mutex); CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); ignore = m_ignoreMove; + + // compress mouse motion events if more input follows + if (!ignore && !m_compressMouse && m_input->getSize() > 0) { + m_compressMouse = true; + } + if (m_compressMouse) { + ignore = true; + m_xMouse = x; + m_yMouse = y; + } } log((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); if (!ignore) { @@ -690,6 +740,9 @@ CClient::onMouseMove() void CClient::onMouseWheel() { + // get mouse up to date + flushCompressedMouse(); + SInt16 delta; { CLock lock(&m_mutex); diff --git a/client/CClient.h b/client/CClient.h index 2a99d1ec..66927660 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -47,10 +47,13 @@ public: private: void runSession(void*); - // open/close the primary screen + // open/close the secondary screen void openSecondaryScreen(); void closeSecondaryScreen(); + // if compressing mouse motion then send the last motion now + void flushCompressedMouse(); + // message handlers void onEnter(); void onLeave(); @@ -86,6 +89,9 @@ private: bool m_ownClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; CString m_dataClipboard[kClipboardEnd]; + + bool m_compressMouse; + SInt32 m_xMouse, m_yMouse; }; #endif From ed8ed72f2600e9422c1b1952533b6bd3f08eb155 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 26 Jun 2002 16:31:48 +0000 Subject: [PATCH 210/807] synergy hook DLL will now restart itself if a client tries to init() it while it's already running. fixed an uninitialized pointer bug in CServer and some cleanup-on-error code in CMSWindowsPrimaryScreen. also added timeout to read() on IInputStream and a heartbeat sent by clients so the server can disconnect clients that are dead but never reset the TCP connection. previously the server would keep these dead clients around forever and if the user was locked on the client screen for some reason then the server would have to be rebooted (or the server would have to be killed via a remote login). --- client/CClient.cpp | 26 ++++++++++-- http/CHTTPProtocol.cpp | 4 +- io/CBufferedInputStream.cpp | 15 ++++--- io/CBufferedInputStream.h | 4 +- io/CInputStreamFilter.h | 2 +- io/IInputStream.h | 7 +++- server/CMSWindowsPrimaryScreen.cpp | 30 ++++++++------ server/CServer.cpp | 1 + server/CServerProtocol1_0.cpp | 41 +++++++++++++++---- server/CSynergyHook.cpp | 17 +++++--- synergy/CInputPacketStream.cpp | 66 ++++++++++++++++++++++-------- synergy/CInputPacketStream.h | 8 ++-- synergy/CProtocolUtil.cpp | 2 +- synergy/ProtocolTypes.h | 9 ++++ 14 files changed, 169 insertions(+), 63 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 453d6d45..83d2164f 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -13,6 +13,7 @@ #include "CTimerThread.h" #include "XThread.h" #include "CLog.h" +#include "CStopwatch.h" #include "TMethodJob.h" #include @@ -301,23 +302,36 @@ CClient::runSession(void*) m_compressMouse = false; // handle messages from server + CStopwatch heartbeat; for (;;) { // if no input is pending then flush compressed mouse motion if (input->getSize() == 0) { flushCompressedMouse(); } - // wait for reply + // wait for a message log((CLOG_DEBUG2 "waiting for message")); UInt8 code[4]; - UInt32 n = input->read(code, 4); + UInt32 n = input->read(code, 4, kHeartRate); - // verify we got an entire code + // check if server hungup if (n == 0) { log((CLOG_NOTE "server disconnected")); - // server hungup break; } + + // check for time out + if (n == (UInt32)-1 || heartbeat.getTime() > kHeartRate) { + // send heartbeat + CProtocolUtil::writef(m_output, kMsgCNoop); + heartbeat.reset(); + if (n == (UInt32)-1) { + // no message to process + continue; + } + } + + // verify we got an entire code if (n != 4) { // client sent an incomplete message log((CLOG_ERR "incomplete message from server")); @@ -347,6 +361,10 @@ CClient::runSession(void*) else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { onKeyRepeat(); } + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + continue; + } else if (memcmp(code, kMsgCEnter, 4) == 0) { onEnter(); } diff --git a/http/CHTTPProtocol.cpp b/http/CHTTPProtocol.cpp index e49bdd00..cea0d64c 100644 --- a/http/CHTTPProtocol.cpp +++ b/http/CHTTPProtocol.cpp @@ -468,7 +468,7 @@ CHTTPProtocol::readLine(IInputStream* stream, CString& tmpBuffer) // read more from stream char buffer[4096]; - UInt32 n = stream->read(buffer, sizeof(buffer)); + UInt32 n = stream->read(buffer, sizeof(buffer), -1.0); if (n == 0) { // stream is empty. return what's leftover. CString line = tmpBuffer; @@ -514,7 +514,7 @@ CHTTPProtocol::readBlock(IInputStream* stream, if (n > numBytes) { n = numBytes; } - n = stream->read(buffer, n); + n = stream->read(buffer, n, -1.0); // if stream is empty then return what we've got so far if (n == 0) { diff --git a/io/CBufferedInputStream.cpp b/io/CBufferedInputStream.cpp index 28c6e7a5..06f553ea 100644 --- a/io/CBufferedInputStream.cpp +++ b/io/CBufferedInputStream.cpp @@ -2,6 +2,7 @@ #include "CLock.h" #include "CMutex.h" #include "CThread.h" +#include "CStopwatch.h" #include "IJob.h" #include "XIO.h" #include @@ -43,15 +44,19 @@ CBufferedInputStream::hangup() } UInt32 -CBufferedInputStream::readNoLock(void* dst, UInt32 n) +CBufferedInputStream::readNoLock(void* dst, UInt32 n, double timeout) { if (m_closed) { throw XIOClosed(); } - // wait for data (or hangup) + // wait for data, hangup, or timeout + CStopwatch timer(true); while (!m_hungup && m_empty == true) { - m_empty.wait(); + if (!m_empty.wait(timer, timeout)) { + // timed out + return (UInt32)-1; + } } // read data @@ -98,10 +103,10 @@ CBufferedInputStream::close() } UInt32 -CBufferedInputStream::read(void* dst, UInt32 n) +CBufferedInputStream::read(void* dst, UInt32 n, double timeout) { CLock lock(m_mutex); - return readNoLock(dst, n); + return readNoLock(dst, n, timeout); } UInt32 diff --git a/io/CBufferedInputStream.h b/io/CBufferedInputStream.h index f47658f3..22ded242 100644 --- a/io/CBufferedInputStream.h +++ b/io/CBufferedInputStream.h @@ -26,7 +26,7 @@ public: void hangup(); // same as read() but caller must lock the mutex - UInt32 readNoLock(void*, UInt32 count); + UInt32 readNoLock(void*, UInt32 count, double timeout); // accessors @@ -36,7 +36,7 @@ public: // IInputStream overrides // these all lock the mutex for their duration virtual void close(); - virtual UInt32 read(void*, UInt32 count); + virtual UInt32 read(void*, UInt32 count, double timeout); virtual UInt32 getSize() const; private: diff --git a/io/CInputStreamFilter.h b/io/CInputStreamFilter.h index 85487226..bb25a261 100644 --- a/io/CInputStreamFilter.h +++ b/io/CInputStreamFilter.h @@ -14,7 +14,7 @@ public: // IInputStream overrides virtual void close() = 0; - virtual UInt32 read(void*, UInt32 maxCount) = 0; + virtual UInt32 read(void*, UInt32 maxCount, double timeout) = 0; virtual UInt32 getSize() const = 0; protected: diff --git a/io/IInputStream.h b/io/IInputStream.h index f025ed40..fc5abaa3 100644 --- a/io/IInputStream.h +++ b/io/IInputStream.h @@ -13,8 +13,11 @@ public: // read up to maxCount bytes into buffer, return number read. // blocks if no data is currently available. if buffer is NULL - // then the data is discarded. - virtual UInt32 read(void* buffer, UInt32 maxCount) = 0; + // then the data is discarded. returns (UInt32)-1 if there's + // no data for timeout seconds; if timeout < 0 then it blocks + // until data is available. + // (cancellation point) + virtual UInt32 read(void* buffer, UInt32 maxCount, double timeout) = 0; // accessors diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index e35d2811..1631ab7f 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -386,21 +386,27 @@ CMSWindowsPrimaryScreen::onOpenDisplay() // initialize hook library m_init(m_threadID); - // install the screen saver hook - if (m_installScreenSaver != NULL) { - m_installScreenSaver(); - } + try { + // install the screen saver hook + if (m_installScreenSaver != NULL) { + m_installScreenSaver(); + } - // get the input desktop and switch to it - if (m_is95Family) { - if (!openDesktop()) { - throw XScreenOpenFailure(); + // get the input desktop and switch to it + if (m_is95Family) { + if (!openDesktop()) { + throw XScreenOpenFailure(); + } + } + else { + if (!switchDesktop(openInputDesktop())) { + throw XScreenOpenFailure(); + } } } - else { - if (!switchDesktop(openInputDesktop())) { - throw XScreenOpenFailure(); - } + catch (...) { + m_cleanup(); + throw; } } diff --git a/server/CServer.cpp b/server/CServer.cpp index 6ae97436..5e7004ba 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -42,6 +42,7 @@ CServer::CServer(const CString& serverName) : m_active(NULL), m_primaryInfo(NULL), m_seqNum(0), + m_activeSaver(NULL), m_httpServer(NULL), m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests) { diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 54eaf81e..ad6f559d 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -8,6 +8,7 @@ #include "IOutputStream.h" #include "CThread.h" #include "CLog.h" +#include "CStopwatch.h" #include // @@ -29,22 +30,35 @@ CServerProtocol1_0::~CServerProtocol1_0() void CServerProtocol1_0::run() { - // handle messages until the client hangs up + // handle messages until the client hangs up or stops sending heartbeats + CStopwatch heartTimer; for (;;) { CThread::testCancel(); // wait for a message UInt8 code[4]; - UInt32 n = getInputStream()->read(code, 4); + UInt32 n = getInputStream()->read(code, 4, kHeartRate); CThread::testCancel(); - // verify we got an entire code + // check if client hungup if (n == 0) { log((CLOG_NOTE "client \"%s\" disconnected", getClient().c_str())); - - // client hungup return; } + + // check if client has stopped sending heartbeats + if (n == (UInt32)-1) { + if (heartTimer.getTime() > kHeartDeath) { + log((CLOG_NOTE "client \"%s\" is dead", getClient().c_str())); + return; + } + continue; + } + + // got a message so reset heartbeat monitor + heartTimer.reset(); + + // verify we got an entire code if (n != 4) { log((CLOG_ERR "incomplete message from \"%s\": %d bytes", getClient().c_str(), n)); @@ -57,6 +71,10 @@ CServerProtocol1_0::run() if (memcmp(code, kMsgDInfo, 4) == 0) { recvInfo(); } + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // discard no-ops + continue; + } else if (memcmp(code, kMsgCClipboard, 4) == 0) { recvGrabClipboard(); } @@ -83,8 +101,17 @@ CServerProtocol1_0::queryInfo() // wait for and verify reply UInt8 code[4]; - UInt32 n = getInputStream()->read(code, 4); - if (n != 4 && memcmp(code, kMsgDInfo, 4) != 0) { + for (;;) { + UInt32 n = getInputStream()->read(code, 4, -1.0); + if (n == 4) { + if (memcmp(code, kMsgCNoop, 4) == 0) { + // discard heartbeats + continue; + } + if (memcmp(code, kMsgDInfo, 4) == 0) { + break; + } + } throw XBadClient(); } diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 8440013b..262c27a6 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -468,9 +468,13 @@ init(DWORD threadID) { assert(g_hinstance != NULL); - // see if already initialized + // see if already initialized. if it is we'll shut down and + // reinitialize. this allows the hook DLL to be reclaimed by + // a new synergyd if the previous one died unexpectedly. if (g_threadID != 0) { - return 0; + uninstallScreenSaver(); + uninstall(); + cleanup(); } // save thread id. we'll post messages to this thread's @@ -624,9 +628,10 @@ uninstall(void) UnhookWindowsHookEx(g_getMessage); g_getMessage = NULL; } - g_keyboard = NULL; - g_mouse = NULL; - g_cbt = NULL; + g_keyboard = NULL; + g_mouse = NULL; + g_cbt = NULL; + g_wheelSupport = kWheelNone; // show the cursor restoreCursor(); @@ -664,7 +669,7 @@ uninstallScreenSaver(void) assert(g_hinstance != NULL); // uninstall hook unless the mouse wheel hook is installed - if (g_getMessage != NULL && g_threadID == 0) { + if (g_getMessage != NULL && g_wheelSupport == kWheelNone) { UnhookWindowsHookEx(g_getMessage); g_getMessage = NULL; } diff --git a/synergy/CInputPacketStream.cpp b/synergy/CInputPacketStream.cpp index c29a6a04..7b1b792c 100644 --- a/synergy/CInputPacketStream.cpp +++ b/synergy/CInputPacketStream.cpp @@ -1,5 +1,6 @@ #include "CInputPacketStream.h" #include "CLock.h" +#include "CStopwatch.h" // // CInputPacketStream @@ -26,14 +27,21 @@ CInputPacketStream::close() } UInt32 -CInputPacketStream::read(void* buffer, UInt32 n) +CInputPacketStream::read(void* buffer, UInt32 n, double timeout) { CLock lock(&m_mutex); - // wait for entire message to be read. return immediately if - // stream hungup. - if (!waitForFullMessage()) { + // wait for entire message to be read. return if stream + // hungup or timeout. + switch (waitForFullMessage(timeout)) { + case kData: + break; + + case kHungup: return 0; + + case kTimedout: + return (UInt32)-1; } // limit number of bytes to read to the number of bytes left in the @@ -43,7 +51,7 @@ CInputPacketStream::read(void* buffer, UInt32 n) } // now read from our buffer - n = m_buffer.readNoLock(buffer, n); + n = m_buffer.readNoLock(buffer, n, -1.0); assert(n <= m_size); m_size -= n; @@ -60,48 +68,70 @@ CInputPacketStream::getSize() const UInt32 CInputPacketStream::getSizeNoLock() const { + CStopwatch timer(true); while (!hasFullMessage() && getStream()->getSize() > 0) { // read more data - if (!getMoreMessage()) { + if (getMoreMessage(-1.0) != kData) { // stream hungup - return false; + return 0; } } return m_size; } -bool -CInputPacketStream::waitForFullMessage() const +CInputPacketStream::EResult +CInputPacketStream::waitForFullMessage(double timeout) const { + CStopwatch timer(true); while (!hasFullMessage()) { + // compute remaining timeout + double t = timeout - timer.getTime(); + if (timeout >= 0.0 && t <= 0.0) { + // timeout + return kTimedout; + } + // read more data - if (!getMoreMessage()) { + switch (getMoreMessage(t)) { + case kData: + break; + + case kHungup: // stream hungup - return false; + return kHungup; + + case kTimedout: + // stream timed out + return kTimedout; } } - return true; + return kData; } -bool -CInputPacketStream::getMoreMessage() const +CInputPacketStream::EResult +CInputPacketStream::getMoreMessage(double timeout) const { // read more data char buffer[4096]; - UInt32 n = getStream()->read(buffer, sizeof(buffer)); + UInt32 n = getStream()->read(buffer, sizeof(buffer), timeout); + + // return if stream timed out + if (n == (UInt32)-1) { + return kTimedout; + } // return if stream hungup if (n == 0) { m_buffer.hangup(); - return false; + return kHungup; } // append to our buffer m_buffer.write(buffer, n); - return true; + return kData; } bool @@ -117,7 +147,7 @@ CInputPacketStream::hasFullMessage() const // save payload length UInt8 buffer[4]; - UInt32 n = m_buffer.readNoLock(buffer, sizeof(buffer)); + UInt32 n = m_buffer.readNoLock(buffer, sizeof(buffer), -1.0); assert(n == 4); m_size = ((UInt32)buffer[0] << 24) | ((UInt32)buffer[1] << 16) | diff --git a/synergy/CInputPacketStream.h b/synergy/CInputPacketStream.h index 03fd32be..c8b123d1 100644 --- a/synergy/CInputPacketStream.h +++ b/synergy/CInputPacketStream.h @@ -16,13 +16,15 @@ public: // IInputStream overrides virtual void close(); - virtual UInt32 read(void*, UInt32 maxCount); + virtual UInt32 read(void*, UInt32 maxCount, double timeout); virtual UInt32 getSize() const; private: + enum EResult { kData, kHungup, kTimedout }; + UInt32 getSizeNoLock() const; - bool waitForFullMessage() const; - bool getMoreMessage() const; + EResult waitForFullMessage(double timeout) const; + EResult getMoreMessage(double timeout) const; bool hasFullMessage() const; private: diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index 1b3102b2..9426a2f6 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -348,7 +348,7 @@ CProtocolUtil::read(IInputStream* stream, void* vbuffer, UInt32 count) UInt8* buffer = reinterpret_cast(vbuffer); while (count > 0) { // read more - UInt32 n = stream->read(buffer, count); + UInt32 n = stream->read(buffer, count, -1.0); // bail if stream has hungup if (n == 0) { diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index 3d2223e2..872a1d7e 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -10,6 +10,12 @@ static const SInt16 kProtocolMinorVersion = 1; // contact port number static const UInt16 kDefaultPort = 24800; +// time between heartbeats (in seconds) +static const double kHeartRate = 2.0; + +// time without a heartbeat that we call death +static const double kHeartDeath = 3.0 * kHeartRate; + // // message codes (trailing NUL is not part of code). in comments, $n // refers to the n'th argument (counting from one). message codes are @@ -24,6 +30,9 @@ static const UInt16 kDefaultPort = 24800; // command codes // +// no operation; secondary -> primary +static const char kMsgCNoop[] = "CNOP"; + // close connection; primary -> secondary static const char kMsgCClose[] = "CBYE"; From f4a73c28a290778855184d9a7c3bcc1880927a67 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Jul 2002 12:58:52 +0000 Subject: [PATCH 211/807] added win32 screen saver class forgotten in previous checkins. --- platform/CMSWindowsScreenSaver.cpp | 298 +++++++++++++++++++++++++++++ platform/CMSWindowsScreenSaver.h | 59 ++++++ 2 files changed, 357 insertions(+) create mode 100755 platform/CMSWindowsScreenSaver.cpp create mode 100755 platform/CMSWindowsScreenSaver.h diff --git a/platform/CMSWindowsScreenSaver.cpp b/platform/CMSWindowsScreenSaver.cpp new file mode 100755 index 00000000..2c4b3760 --- /dev/null +++ b/platform/CMSWindowsScreenSaver.cpp @@ -0,0 +1,298 @@ +#include "CMSWindowsScreenSaver.h" +#include "CThread.h" +#include "CLog.h" +#include "TMethodJob.h" + +#if !defined(SPI_GETSCREENSAVERRUNNING) +#define SPI_GETSCREENSAVERRUNNING 114 +#endif + +// +// CMSWindowsScreenSaver +// + +CMSWindowsScreenSaver::CMSWindowsScreenSaver() : + m_process(NULL), + m_threadID(0), + m_watch(NULL) +{ + // detect OS + m_is95Family = false; + m_is95 = false; + m_isNT = false; + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + if (GetVersionEx(&info)) { + m_is95Family = (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT && + info.dwMajorVersion <= 4) { + m_isNT = true; + } + else if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && + info.dwMajorVersion == 4 && + info.dwMinorVersion == 0) { + m_is95 = true; + } + } + + // check if screen saver is enabled + SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); +} + +CMSWindowsScreenSaver::~CMSWindowsScreenSaver() +{ + unwatchProcess(); +} + +bool +CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) +{ + // screen saver may have started. look for it and get + // the process. if we can't find it then assume it + // didn't really start. we wait a moment before + // looking to give the screen saver a chance to start. + // this shouldn't be a problem since we only get here + // if the screen saver wants to kick in, meaning that + // the system is idle or the user deliberately started + // the screen saver. + Sleep(250); + HWND hwnd = findScreenSaver(); + if (hwnd == NULL) { + // didn't start + log((CLOG_DEBUG "can't find screen saver window")); + return false; + } + + // get the process + DWORD processID; + GetWindowThreadProcessId(hwnd, &processID); + HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID); + if (process == NULL) { + // didn't start + log((CLOG_DEBUG "can't open screen saver process")); + return false; + } + + // watch for the process to exit + m_threadID = GetCurrentThreadId(); + m_msg = msg; + m_wParam = wParam; + m_lParam = lParam; + watchProcess(process); + + return true; +} + +void +CMSWindowsScreenSaver::enable() +{ + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, m_wasEnabled, 0, 0); +} + +void +CMSWindowsScreenSaver::disable() +{ + SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, 0); +} + +void +CMSWindowsScreenSaver::activate() +{ + // don't activate if already active + if (!isActive()) { + HWND hwnd = GetForegroundWindow(); + if (hwnd != NULL) { + PostMessage(hwnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0); + } + else { + // no foreground window. pretend we got the event instead. + DefWindowProc(NULL, WM_SYSCOMMAND, SC_SCREENSAVE, 0); + } + } +} + +void +CMSWindowsScreenSaver::deactivate() +{ + bool killed = false; + if (!m_is95Family) { + // NT runs screen saver in another desktop + HDESK desktop = OpenDesktop("screen-saver", 0, FALSE, + DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS); + if (desktop != NULL) { + EnumDesktopWindows(desktop, + &CMSWindowsScreenSaver::killScreenSaverFunc, + reinterpret_cast(&killed)); + CloseDesktop(desktop); + } + } + + // if above failed or wasn't tried, try the windows 95 way + if (!killed) { + // find screen saver window and close it + HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL); + if (hwnd != NULL) { + PostMessage(hwnd, WM_CLOSE, 0, 0); + } + } + + // force timer to restart + SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, + !m_wasEnabled, 0, SPIF_SENDWININICHANGE); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, + m_wasEnabled, 0, SPIF_SENDWININICHANGE); +} + +bool +CMSWindowsScreenSaver::isActive() const +{ + if (m_is95) { + return (FindWindow("WindowsScreenSaverClass", NULL) != NULL); + } + else if (m_isNT) { + // screen saver runs on a separate desktop + HDESK desktop = OpenDesktop("screen-saver", 0, FALSE, MAXIMUM_ALLOWED); + if (desktop == NULL && GetLastError() != ERROR_ACCESS_DENIED) { + // desktop doesn't exist so screen saver is not running + return false; + } + + // desktop exists. this should indicate that the screen saver + // is running but an OS bug can cause a valid handle to be + // returned even if the screen saver isn't running (Q230117). + // we'll try to enumerate the windows on the desktop and, if + // there are any, we assume the screen saver is running. (note + // that if we don't have permission to enumerate then we'll + // assume that the screen saver is not running.) that'd be + // easy enough except there's another OS bug (Q198590) that can + // cause EnumDesktopWindows() to enumerate the windows of + // another desktop if the requested desktop has no windows. to + // work around that we have to verify that the enumerated + // windows are, in fact, on the expected desktop. + CFindScreenSaverInfo info; + info.m_desktop = desktop; + info.m_window = NULL; + EnumDesktopWindows(desktop, + &CMSWindowsScreenSaver::findScreenSaverFunc, + reinterpret_cast(&info)); + + // done with desktop + CloseDesktop(desktop); + + // screen saver is running if a window was found + return (info.m_window != NULL); + } + else { + BOOL running; + SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0); + return (running != FALSE); + } +} + +BOOL CALLBACK +CMSWindowsScreenSaver::findScreenSaverFunc(HWND hwnd, LPARAM arg) +{ + CFindScreenSaverInfo* info = reinterpret_cast(arg); + + if (info->m_desktop != NULL) { + DWORD threadID = GetWindowThreadProcessId(hwnd, NULL); + HDESK desktop = GetThreadDesktop(threadID); + if (desktop != NULL && desktop != info->m_desktop) { + // stop enumerating -- wrong desktop + return FALSE; + } + } + + // found a window + info->m_window = hwnd; + + // don't need to enumerate further + return FALSE; +} + +BOOL CALLBACK +CMSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg) +{ + if (IsWindowVisible(hwnd)) { + PostMessage(hwnd, WM_CLOSE, 0, 0); + *reinterpret_cast(arg) = true; + } + return TRUE; +} + +HWND +CMSWindowsScreenSaver::findScreenSaver() +{ + if (!m_is95Family) { + // screen saver runs on a separate desktop + HDESK desktop = OpenDesktop("screen-saver", 0, FALSE, MAXIMUM_ALLOWED); + if (desktop != NULL) { + // search + CFindScreenSaverInfo info; + info.m_desktop = desktop; + info.m_window = NULL; + EnumDesktopWindows(desktop, + &CMSWindowsScreenSaver::findScreenSaverFunc, + reinterpret_cast(&info)); + + // done with desktop + CloseDesktop(desktop); + + // return window if found + if (info.m_window != NULL) { + return info.m_window; + } + } + } + + // try windows 95 way + return FindWindow("WindowsScreenSaverClass", NULL); +} + +void +CMSWindowsScreenSaver::watchProcess(HANDLE process) +{ + // stop watching previous process + unwatchProcess(); + + // watch new process in another thread + if (process != NULL) { + log((CLOG_DEBUG "watching screen saver process")); + m_process = process; + m_watch = new CThread(new TMethodJob(this, + &CMSWindowsScreenSaver::watchProcessThread)); + } +} + +void +CMSWindowsScreenSaver::unwatchProcess() +{ + if (m_watch != NULL) { + log((CLOG_DEBUG "stopped watching screen saver process")); + m_watch->cancel(); + m_watch->wait(); + delete m_watch; + m_watch = NULL; + } + if (m_process != NULL) { + CloseHandle(m_process); + m_process = NULL; + } +} + +void +CMSWindowsScreenSaver::watchProcessThread(void*) +{ + for (;;) { + CThread::testCancel(); + if (WaitForSingleObject(m_process, 50) == WAIT_OBJECT_0) { + // process terminated. send screen saver deactivation + // message. + log((CLOG_DEBUG "screen saver died")); + PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); + return; + } + } +} diff --git a/platform/CMSWindowsScreenSaver.h b/platform/CMSWindowsScreenSaver.h new file mode 100755 index 00000000..df4a342b --- /dev/null +++ b/platform/CMSWindowsScreenSaver.h @@ -0,0 +1,59 @@ +#ifndef CMSWINDOWSSCREENSAVER_H +#define CMSWINDOWSSCREENSAVER_H + +#include "IScreenSaver.h" +#define WIN32_LEAN_AND_MEAN +#include + +class CThread; + +class CMSWindowsScreenSaver : public IScreenSaver { +public: + CMSWindowsScreenSaver(); + virtual ~CMSWindowsScreenSaver(); + + // manipulators + + // check if the screen saver really started. returns false if it + // hasn't, true otherwise. when the screen saver stops msg will + // be posted to the current thread's message queue with the given + // parameters. + bool checkStarted(UINT msg, WPARAM, LPARAM); + + // IScreenSaver overrides + virtual void enable(); + virtual void disable(); + virtual void activate(); + virtual void deactivate(); + virtual bool isActive() const; + +private: + class CFindScreenSaverInfo { + public: + HDESK m_desktop; + HWND m_window; + }; + + static BOOL CALLBACK findScreenSaverFunc(HWND hwnd, LPARAM lParam); + static BOOL CALLBACK killScreenSaverFunc(HWND hwnd, LPARAM lParam); + + HWND findScreenSaver(); + void watchProcess(HANDLE process); + void unwatchProcess(); + void watchProcessThread(void*); + +private: + bool m_is95Family; + bool m_is95; + bool m_isNT; + BOOL m_wasEnabled; + + HANDLE m_process; + CThread* m_watch; + DWORD m_threadID; + UINT m_msg; + WPARAM m_wParam; + LPARAM m_lParam; +}; + +#endif From 684ac64742c2d28612f5e3be65ae2368ed3d823e Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Jul 2002 13:00:12 +0000 Subject: [PATCH 212/807] fixed locking to screen on win32. was using GetKeyboardState() to query keys but that doesn't give us up-to-date information. now using GetAsyncKeyState() if on primary and m_keys if on secondary. --- server/CMSWindowsPrimaryScreen.cpp | 74 ++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 1631ab7f..55b43441 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -347,22 +347,44 @@ CMSWindowsPrimaryScreen::getToggleMask() const bool CMSWindowsPrimaryScreen::isLockedToScreen() const { - // check buttons - if (GetAsyncKeyState(VK_LBUTTON) < 0 || - GetAsyncKeyState(VK_MBUTTON) < 0 || - GetAsyncKeyState(VK_RBUTTON) < 0) { - return true; - } + // virtual key table. the table defines the virtual keys that are + // mapped to something (including mouse buttons, OEM and kanji keys + // but not unassigned or undefined keys). + static const UInt32 s_mappedKeys[] = { + 0xfbff331e, + 0x03ffffff, + 0x3ffffffe, + 0xffffffff, + 0x000300ff, + 0xfc000000, + 0xf8000001, + 0x7ffffe5f + }; - // check keys - BYTE keys[256]; - if (GetKeyboardState(keys)) { - for (unsigned int i = 0; i < sizeof(keys); ++i) { - if ((keys[i] & 0x80) != 0) { + // check each key. note that we cannot use GetKeyboardState() here + // since it reports the state of keys according to key messages + // that have been pulled off the queue. in general, we won't get + // these key messages because they're not for our window. if any + // key (or mouse button) is down then we're locked to the screen. + if (m_active) { + // use shadow keyboard state in m_keys + for (UInt32 i = 0; i < 256; ++i) { + if ((m_keys[i] & 0x80) != 0) { return true; } } } + else { + for (UInt32 i = 0; i < 256 / 32; ++i) { + for (UInt32 b = 1, j = 0; j < 32; b <<= 1, ++j) { + if ((s_mappedKeys[i] & b) != 0) { + if (GetAsyncKeyState(i * 32 + j) < 0) { + return true; + } + } + } + } + } // not locked return false; @@ -1454,18 +1476,19 @@ CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press) m_keys[VK_MENU] |= 0x80; break; - case VK_LWIN: - case VK_RWIN: - case VK_APPS: - m_keys[vkCode] |= 0x80; - break; - case VK_CAPITAL: case VK_NUMLOCK: case VK_SCROLL: // toggle keys m_keys[vkCode] |= 0x80; break; + + default: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + m_keys[vkCode] |= 0x80; + break; } } else { @@ -1497,19 +1520,20 @@ CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press) } break; + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // toggle keys + m_keys[vkCode] &= ~0x80; + m_keys[vkCode] ^= 0x01; + break; + + default: case VK_LWIN: case VK_RWIN: case VK_APPS: m_keys[vkCode] &= ~0x80; break; - - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - // toggle keys - m_keys[vkCode] &= ~0x80; - m_keys[vkCode] ^= 0x01; - break; } } } From a0eba4c33737f26b576b18c65543f41bcbed9dbb Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Jul 2002 13:01:16 +0000 Subject: [PATCH 213/807] disabled removing client if no heartbeat is received. we don't want that while testing because it might hide bugs. --- server/CServerProtocol1_0.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index ad6f559d..7c673415 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -48,10 +48,12 @@ CServerProtocol1_0::run() // check if client has stopped sending heartbeats if (n == (UInt32)-1) { +/* FIXME -- disabled to avoid masking bugs if (heartTimer.getTime() > kHeartDeath) { log((CLOG_NOTE "client \"%s\" is dead", getClient().c_str())); return; } +*/ continue; } From e267d1dc1cb3d15dac350f566e034756ee51e2e7 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Jul 2002 13:03:16 +0000 Subject: [PATCH 214/807] now synthesizing key release events for each pressed key when the client screen is closed. this fixes the bug where the client's keyboard was left with some keys logically pressed when the client died (e.g. using ctrl+c on the client program from the server's keyboard would leave the ctrl key logically pressed). --- client/CMSWindowsSecondaryScreen.cpp | 54 ++++++++++++++++++++++++++++ client/CMSWindowsSecondaryScreen.h | 1 + client/CXWindowsSecondaryScreen.cpp | 23 +++++++++++- client/CXWindowsSecondaryScreen.h | 1 + synergy/ISecondaryScreen.h | 5 ++- 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index c040479b..7bd1dee5 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -111,6 +111,9 @@ CMSWindowsSecondaryScreen::close() { assert(m_client != NULL); + // release keys that are logically pressed + releaseKeys(); + // restore the screen saver settings getScreenSaver()->enable(); @@ -1698,6 +1701,57 @@ CMSWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) } } +void +CMSWindowsSecondaryScreen::releaseKeys() +{ + CLock lock(&m_mutex); + + syncDesktop(); + + // release left/right modifier keys first. if the platform doesn't + // support them then they won't be set and the non-side-distinuishing + // key will retain its state. if the platform does support them then + // the non-side-distinguishing will be reset. + if ((m_keys[VK_LSHIFT] & 0x80) != 0) { + sendKeyEvent(VK_LSHIFT, false); + m_keys[VK_SHIFT] = 0; + m_keys[VK_LSHIFT] = 0; + } + if ((m_keys[VK_RSHIFT] & 0x80) != 0) { + sendKeyEvent(VK_RSHIFT, false); + m_keys[VK_SHIFT] = 0; + m_keys[VK_RSHIFT] = 0; + } + if ((m_keys[VK_LCONTROL] & 0x80) != 0) { + sendKeyEvent(VK_LCONTROL, false); + m_keys[VK_CONTROL] = 0; + m_keys[VK_LCONTROL] = 0; + } + if ((m_keys[VK_RCONTROL] & 0x80) != 0) { + sendKeyEvent(VK_RCONTROL, false); + m_keys[VK_CONTROL] = 0; + m_keys[VK_RCONTROL] = 0; + } + if ((m_keys[VK_LMENU] & 0x80) != 0) { + sendKeyEvent(VK_LMENU, false); + m_keys[VK_MENU] = 0; + m_keys[VK_LMENU] = 0; + } + if ((m_keys[VK_RMENU] & 0x80) != 0) { + sendKeyEvent(VK_RMENU, false); + m_keys[VK_MENU] = 0; + m_keys[VK_RMENU] = 0; + } + + // now check all the other keys + for (UInt32 i = 0; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { + if ((m_keys[i] & 0x80) != 0) { + sendKeyEvent(i, false); + m_keys[i] = 0; + } + } +} + void CMSWindowsSecondaryScreen::updateKeys() { diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index e5a6452d..6db408f2 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -85,6 +85,7 @@ private: KeyModifierMask, EKeyAction) const; void doKeystrokes(const Keystrokes&, SInt32 count); + void releaseKeys(); void updateKeys(); void updateModifiers(); void toggleKey(UINT virtualKey, KeyModifierMask mask); diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 4ae2c723..32eb1419 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -130,6 +130,9 @@ CXWindowsSecondaryScreen::close() { assert(m_client != NULL); + // release keys that are logically pressed + releaseKeys(); + // restore the screen saver settings getScreenSaver()->enable(); @@ -847,6 +850,24 @@ CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const return outMask; } +void +CXWindowsSecondaryScreen::releaseKeys() +{ + CDisplayLock display(this); + + // key up for each key that's down + for (UInt32 i = 0; i < 256; ++i) { + if (m_keys[i]) { + XTestFakeKeyEvent(display, i, False, CurrentTime); + m_keys[i] = false; + } + } + + // update + XSync(display, False); + +} + void CXWindowsSecondaryScreen::updateKeys(Display* display) { @@ -855,7 +876,7 @@ CXWindowsSecondaryScreen::updateKeys(Display* display) XQueryKeymap(display, keys); // transfer to our state - for (unsigned int i = 0, j = 0; i < 32; j += 8, ++i) { + for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) { m_keys[j + 0] = ((keys[i] & 0x01) != 0); m_keys[j + 1] = ((keys[i] & 0x02) != 0); m_keys[j + 2] = ((keys[i] & 0x04) != 0); diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 0e07c4ea..44343d23 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -72,6 +72,7 @@ private: void doKeystrokes(const Keystrokes&, SInt32 count); unsigned int maskToX(KeyModifierMask) const; + void releaseKeys(); void updateKeys(Display* display); void updateKeycodeMap(Display* display); void updateModifiers(Display* display); diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index a31bc563..66fed53d 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -27,7 +27,10 @@ public: // stolen and screen size changed). virtual void open(CClient*) = 0; - // close the screen. should restore the screen saver. + // close the screen. should restore the screen saver. it should + // also simulate key up events for any keys that have simulate key + // down events without a matching key up. without this the client + // will leave its keyboard in the wrong logical state. virtual void close() = 0; // called when the user navigates to the secondary screen. warp From 350cd7e2caef21387688b087989d17ac92fa75bc Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Jul 2002 14:01:23 +0000 Subject: [PATCH 215/807] checkpoint. --- README | 66 +++++++++++++++++++++++++++--------------------- notes | 79 +++++++++++++++++++++++++++++++++------------------------- 2 files changed, 82 insertions(+), 63 deletions(-) diff --git a/README b/README index 2993e486..cc5da6c5 100644 --- a/README +++ b/README @@ -3,10 +3,13 @@ synergy synergy: [noun] a mutually advantageous conjunction of distinct elements synergy lets you easily share a single mouse and keyboard between -multiple computers through software. it also merges each system's -clipboards into one, allowing cut-and-paste between systems. -redirecting the mouse and keyboard is as simple as moving the -mouse off the edge of your screen. +multiple computers, each with its own display, using software only. +redirecting the mouse and keyboard is as simple as moving the mouse +off the edge of your screen. synergy merges the clipboards of all +the systems into one, allowing cut-and-paste between systems. it +also synchronizes screensavers so they all start and stop together +and, if screen locking is enabled, only one screen requires a +password to unlock them all. system requirements @@ -147,9 +150,7 @@ connected screen. if there is no connected screen in that direction then the mouse will not leave the starting screen. disconnecting a client while the mouse is on it causes the mouse -to instantly jump to the center of the server screen. note that -there's a bug in this situation where keys may be left in the -logical pressed state; see the "known bugs" section. +to instantly jump to the center of the server screen. the clipboard is automatically transferred between screens. if you copy on one screen you just switch to another screen and paste. @@ -161,6 +162,17 @@ clipboard. whichever X clipboard was changed last becomes the Windows clipboard. end-of-line sequences (LF on linux and unix, CRLF on Windows) are automatically converted as necessary. +synergy synchronizes screensavers. the screensavers on client +screens are disabled when they connect to the server. when the +primary screen's screensaver starts, the screensaver on each +secondary screen starts too. all the secondary screensavers are +stopped when the primary screensaver stops. moving the mouse or +pressing a key will stop the primary screensaver, regardless of +which screen the mouse was on when the screensavers started. if +the primary screensaver requires a password to unlock then the +user is prevented from switching to the secondary screens until +the primary screen is unlocked. + installing as a daemon/service ------------------------------ @@ -217,7 +229,9 @@ linux, unix: connect to the X server until it succeeds. the DISPLAY env var should be set appropriately before starting synergy. note that it probably will not be set when running startup scripts so you - have to set it yourself (probably to ":0"). + have to set it yourself (probably to ":0"). if it's not set + then synergy will use the default ":0.0" which is correct in + most cases. if the X server goes down then all the clients connected to it must terminate. synergy has to deal with this because some @@ -228,13 +242,13 @@ linux, unix: some display managers also prevent any X client other than those they themselves start from connecting to the X server - for security reasons. for these display managers, synergy - should be part of the manager's screen initialization script - and synergy should be started with the --no-restart option - since the display manager will do the restarting. otherwise, - synergy should not use the --no-restart option and it can be - started at boot time (sometime after the network is started) - or when the X server is started. + during login for security reasons. for these display managers, + synergy should be part of the manager's screen initialization + script and synergy should be started with the --no-restart + option since the display manager will do the restarting. + otherwise, synergy should not use the --no-restart option and + it can be started at boot time (sometime after the network is + started) or when the X server is started. finally, some display managers (xdm and kdm, but not gdm) grab the keyboard and do not release it until the user logs @@ -325,12 +339,13 @@ server command line options -a, --address
listen for connections on the given address -c, --config read configuration from -an address has one of the following forms: +
has one of the following forms: : : is a hostname or address of a network interface on the -server system. is a port number from 1 to 65535. +server system. is a port number from 1 to 65535. +defaults to the system's hostname and defaults to 24800. client command line options @@ -339,7 +354,9 @@ client command line options --no-camp try connection to server only once
address of server -see the "server command line options" for a description of
. +see the "server command line options" for a description of
+but note that there is no default though there is a +default . release notes @@ -397,22 +414,15 @@ best server. known bugs ---------- all: - * screen savers are not handled * non-ASCII characters are not supported - * keys may be left in the logical pressed state on a client if it - terminates while the mouse is on that client. physically pressing - and releasing the key fixes the problem. * plain text is the only supported clipboard format windows: * screen flashes when entering the screen - * synergy may interfere with desktop switcher programs, however + * synergy may interfere with desktop switcher programs. however, synergy understands and handles multiple desktops. * there should be a control panel * there should be a taskbar icon - * hook DLL sometimes not properly shutdown. this is usually only - a problem when forcibly killing the synergy server then trying - to restart it. logging off and on should fix it. windows 95 family: * typing into a console window can be slow @@ -424,9 +434,6 @@ windows NT family: * does not gracefully handle NoInteractiveServices being enabled linux: - * one thread may linger if the application crashes. - use kill or killall to terminate this process (threads are - processes under linux) before restarting. * some keyboards have toggle keys that toggle on on key press and toggle off on the key release after the next key press. synergy doesn't handle these properly. @@ -448,3 +455,4 @@ tips and tricks * turn off mouse driven virtual desktop switching on X windows. it will interfere with synergy. use keyboard shortcuts instead. +* synergy's screensaver synchronization works best with xscreensaver. diff --git a/notes b/notes index a1ebdb65..c0ff39ba 100644 --- a/notes +++ b/notes @@ -7,6 +7,17 @@ hangup if invalid query info --> <-- info (size) +--- +enable heartbeat disconnection or remove heartbeat sending in client + should either ship with it fully enabled or fully disabled + possibly make it part of startup negotiation + +--- +sometimes jumping too far into secondary screen + probably bogus delta (adding in center pixel coords into delta?) + this is with an X primary and secondary + don't know which matters but probably X primary + --- use automake use doxygen @@ -93,14 +104,6 @@ bug with half-duplex keys now grace starts server and is locked to screen grace should ignore half duplex keys when seeing if locked to screen ---- -must send up key events when leaving screen for keys still down - some key combos get eaten and the key ups aren't sent - e.g. ctrl+alt+del on win2k to grace - mouse is forced back to to win2k - ctrl and alt up not sent to grace - grace thinks ctrl and alt are down 'til user physically press/releases them - --- bug in updating screens saw a goofy set of screens after update @@ -123,10 +126,17 @@ Xsetup file may have to kill previous synergy not handling international characters --- -screen saver won't lock screen if on secondary screen - because synergy has mouse/keyboard grabbed - synergy will immediately release grabs when screen saver activates - but looks like xscreensaver wont map window until it grabs +xscreensaver won't start if pointer on secondary screen + unless using MIT or SGI screen saver extensions + otherwise it cannot know when to stop the screen saver + cos it can't grab mouse and keyboard +--- +all screensavers hidden when showing win32 screensaver lock window + win32 kills the screen saver and shows the lock window + synergy thinks the screen saver is dead (cos the process is) + deactivates secondary screen screen savers + user is constrained to locked display, though + also, we don't detect screen saver restarting if user doesn't unlock screen --- not distinguishing between caps lock and shift lock @@ -142,12 +152,7 @@ not distinguishing between caps lock and shift lock currently sending all clipboards to all clients some clients may not need all clipboards add filtering mechanism - ---- -sometimes jumping too far into secondary screen - probably bogus delta (adding in center pixel coords into delta?) - this is with an X primary and secondary - don't know which matters but probably X primary + setup is probably part of clipboard (format) negotiation --- not handling large motif selections @@ -167,16 +172,6 @@ inconsistencies should use pointers for any in/out parameter but using references in some places ---- -win32 hook DLL sometimes not unloaded when server is killed? - will get assertion on restart as if DLL was still loaded. - can tell because all DLL copies share data segment. - should prevent multiple copies from loading (or initializing) - replace asserts with (multithread locked) check of variables - return 0 if busy - server must handle failure - try to figure out why it's not unloading - --- avoid fullscreen transparent window on win32 using CBT hook to discard activation/focus messages @@ -200,7 +195,7 @@ keyboard hook problems (win32) thinks the key is always down. --- -try to determine keyboard quirks automatically +try to determine X11 keyboard quirks automatically in particular, set the half-duplex flags maybe KBD extension will help determine this @@ -219,6 +214,9 @@ adjust thread priorities on win32 haven't tried real-time priority a very high priority on client fixes delay when typeing into console is it the output console causing the slowness? + mouse motion compression seems to handle slow dragging on grace + pretty sure nothing else was wrong on grace + maybe same problem and solution on win32 --- results: @@ -371,11 +369,6 @@ send all toggle states instead of (individual) toggle ups/downs (gspencer) and turn it off on the master (and all other clients) too, but I think that's probably too complicated to implement. -dragging windows is too slow - grace (as client) exhibits this - dunno if it happens with audrey2 as the client - liz says her windows laptop does it too - double clicking NT binaries fails (liz) add note to README or use separate binaries for services. client would also need a dialog to query options. server would probably @@ -396,6 +389,24 @@ non-functional on ctrl+alt+del screen in win2k (kurt) i suppose it must be when win2k is the client. mouse input wasn't working. keyboard probably didn't work either. +gspencer: + OK, I've narrowed it down a little -- it seems to happen if you have any + key down (I tried the shift key and the letter 's' key) when you cross + over from master to client, which would make me suspect the "capture" + code, since if you have a key down you aren't supposed to be able to + cross over... + + +== fixed? === +dragging windows is too slow + grace (as client) exhibits this + dunno if it happens with audrey2 as the client + liz says her windows laptop does it too + also closing/iconifying has a delay + dragging multiple files also sluggish + +liz seems to still be seeing sluggish cursor updates + --- automake stuff rebuild: From d813329c0c44f58ba6056a6959aadbebb8880609 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Jul 2002 15:05:49 +0000 Subject: [PATCH 216/807] mistakenly removed mouse button checks when on secondary screens from isLockedToScreen() in earlier checkin. --- server/CMSWindowsPrimaryScreen.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 55b43441..69ead88b 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -506,6 +506,13 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) case SYNERGY_MSG_MOUSE_BUTTON: // ignore message if posted prior to last mark change if (m_markReceived == m_mark) { + static const int s_vkButton[] = { + 0, // kButtonNone + VK_LBUTTON, // kButtonLeft, etc. + VK_MBUTTON, + VK_RBUTTON + }; + const ButtonID button = mapButton(msg->wParam); switch (msg->wParam) { case WM_LBUTTONDOWN: @@ -514,6 +521,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) log((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { m_server->onMouseDown(button); + m_keys[s_vkButton[button]] |= 0x80; } break; @@ -523,6 +531,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) log((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { m_server->onMouseUp(button); + m_keys[s_vkButton[button]] &= ~0x80; } break; } @@ -1432,7 +1441,8 @@ CMSWindowsPrimaryScreen::updateKeys() // clear key state memset(m_keys, 0, sizeof(m_keys)); - // we only care about the modifier key states + // we only care about the modifier key states. other keys and the + // mouse buttons should be up. m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); From 217313e0136a6327b73389aae7c8cb6fa7659eaa Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 3 Jul 2002 16:25:36 +0000 Subject: [PATCH 217/807] fixed spurious mouse motions when entering/leaving primary screen on X11. --- server/CXWindowsPrimaryScreen.cpp | 173 +++++++++++++++--------------- server/CXWindowsPrimaryScreen.h | 2 + 2 files changed, 90 insertions(+), 85 deletions(-) diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index fe16effa..0a9ecb36 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -160,44 +160,37 @@ CXWindowsPrimaryScreen::run() case MotionNotify: { log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); - SInt32 x = xevent.xmotion.x_root; - SInt32 y = xevent.xmotion.y_root; - if (!m_active) { - m_server->onMouseMovePrimary(x, y); + + // compute motion delta (relative to the last known + // mouse position) + SInt32 x = xevent.xmotion.x_root - m_x; + SInt32 y = xevent.xmotion.y_root - m_y; + + // save position to compute delta of next motion + m_x = xevent.xmotion.x_root; + m_y = xevent.xmotion.y_root; + + if (xevent.xmotion.send_event) { + // we warped the mouse. discard events until we + // find the matching sent event. see + // warpCursorNoLockNoFlush() for where the events + // are sent. we discard the matching sent event + // and can be sure we've skipped the warp event. + CDisplayLock display(this); + do { + XMaskEvent(display, PointerMotionMask, &xevent); + } while (!xevent.xmotion.send_event); + } + else if (!m_active) { + // motion on primary screen + m_server->onMouseMovePrimary(m_x, m_y); } else { - // compute motion delta. this is relative to the - // last known mouse position. - x -= m_x; - y -= m_y; - - // save position to compute delta of next motion - m_x = xevent.xmotion.x_root; - m_y = xevent.xmotion.y_root; - - // if event was sent then ignore it and discard - // the event from the mouse warp. this is how we - // warp the mouse back to the center of the screen - // without that causing a corresponding motion on - // the secondary screen. - if (xevent.xmotion.send_event) { - // ignore event - x = 0; - y = 0; - - // discard events until we find the matching - // sent event. see below for where the events - // are sent. we discard the matching sent - // event and can be sure we've skipped the - // warp event. - CDisplayLock display(this); - do { - XMaskEvent(display, PointerMotionMask, &xevent); - } while (!xevent.xmotion.send_event); - } - - // warp mouse back to center. my lombard (powerbook - // g3) using the adbmouse driver has two problems: + // motion on secondary screen. warp mouse back to + // center. + // + // my lombard (powerbook g3) running linux and + // using the adbmouse driver has two problems: // first, the driver only sends motions of +/-2 // pixels and, second, it seems to discard some // physical input after a warp. the former isn't a @@ -211,28 +204,15 @@ CXWindowsPrimaryScreen::run() xevent.xmotion.y_root - m_yCenter < -s_size || xevent.xmotion.y_root - m_yCenter > s_size) { CDisplayLock display(this); - // send an event that we can recognize before - // the mouse warp. - XEvent eventBefore = xevent; - xevent.xmotion.window = m_window; - xevent.xmotion.time = CurrentTime; - xevent.xmotion.x = m_xCenter; - xevent.xmotion.y = m_yCenter; - xevent.xmotion.x_root = m_xCenter; - xevent.xmotion.y_root = m_yCenter; - XEvent eventAfter = eventBefore; - XSendEvent(display, m_window, False, 0, &xevent); - - // warp mouse back to center - XWarpPointer(display, None, getRoot(), - 0, 0, 0, 0, m_xCenter, m_yCenter); - - // send an event that we can recognize after - // the mouse warp. - XSendEvent(display, m_window, False, 0, &xevent); + warpCursorNoLockNoFlush(display, m_xCenter, m_yCenter); } - // send event if mouse moved + // send event if mouse moved. do this after warping + // back to center in case the motion takes us onto + // the primary screen. if we sent the event first + // in that case then the warp would happen after + // warping to the primary screen's enter position, + // effectively overriding it. if (x != 0 || y != 0) { m_server->onMouseMoveSecondary(x, y); } @@ -331,28 +311,18 @@ CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) CDisplayLock display(this); - // warp to requested location - if (!forScreenSaver) { - XWarpPointer(display, None, m_window, 0, 0, 0, 0, x, y); - } - // unmap the grab window. this also ungrabs the mouse and keyboard. XUnmapWindow(display, m_window); - // redirect input to root window - if (forScreenSaver) { - XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); + // warp to requested location + if (!forScreenSaver) { + warpCursorNoLock(display, x, y); } - // remove all input events for grab window - XEvent event; - while (XCheckWindowEvent(display, m_window, - PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask, - &event)) { - // do nothing + // redirect input to root window. do not warp the mouse because + // that will deactivate the screen saver. + else { + XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); } // not active anymore @@ -414,7 +384,7 @@ CXWindowsPrimaryScreen::leave() } while (result != GrabSuccess); log((CLOG_DEBUG1 "grabbed pointer and keyboard")); - // move the mouse to the center of grab window + // warp mouse to center warpCursorNoLock(display, m_xCenter, m_yCenter); // local client now active @@ -439,18 +409,16 @@ CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) void CXWindowsPrimaryScreen::warpCursorNoLock(Display* display, SInt32 x, SInt32 y) { - assert(display != NULL); - assert(m_window != None); + // warp mouse + warpCursorNoLockNoFlush(display, x, y); - // warp the mouse - XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); - XSync(display, False); - log((CLOG_DEBUG2 "warped to %d,%d", x, y)); - - // discard mouse events since we just added one we don't want - XEvent xevent; - while (XCheckWindowEvent(display, m_window, - PointerMotionMask, &xevent)) { + // remove all input events before and including warp + XEvent event; + while (XCheckMaskEvent(display, PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask, + &event)) { // do nothing } @@ -459,6 +427,41 @@ CXWindowsPrimaryScreen::warpCursorNoLock(Display* display, SInt32 x, SInt32 y) m_y = y; } +void +CXWindowsPrimaryScreen::warpCursorNoLockNoFlush( + Display* display, SInt32 x, SInt32 y) +{ + assert(display != NULL); + assert(m_window != None); + + // send an event that we can recognize before the mouse warp + XEvent eventBefore; + eventBefore.type = MotionNotify; + eventBefore.xmotion.display = display; + eventBefore.xmotion.window = m_window; + eventBefore.xmotion.root = getRoot(); + eventBefore.xmotion.subwindow = m_window; + eventBefore.xmotion.time = CurrentTime; + eventBefore.xmotion.x = x; + eventBefore.xmotion.y = y; + eventBefore.xmotion.x_root = x; + eventBefore.xmotion.y_root = y; + eventBefore.xmotion.state = 0; + eventBefore.xmotion.is_hint = False; + eventBefore.xmotion.same_screen = True; + XEvent eventAfter = eventBefore; + XSendEvent(display, m_window, False, 0, &eventBefore); + + // warp mouse + XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); + + // send an event that we can recognize after the mouse warp + XSendEvent(display, m_window, False, 0, &eventAfter); + XSync(display, False); + + log((CLOG_DEBUG2 "warped to %d,%d", x, y)); +} + void CXWindowsPrimaryScreen::setClipboard(ClipboardID id, const IClipboard* clipboard) diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index c8996650..8cabc11f 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -42,6 +42,8 @@ private: void doSelectEvents(Display*, Window) const; void warpCursorNoLock(Display*, SInt32 xAbsolute, SInt32 yAbsolute); + void warpCursorNoLockNoFlush(Display*, + SInt32 xAbsolute, SInt32 yAbsolute); KeyModifierMask mapModifier(unsigned int state) const; KeyID mapKey(XKeyEvent*) const; From 3138ba373d19df3eb8920bd9f85cf98a7ea2ccbc Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 7 Jul 2002 15:15:34 +0000 Subject: [PATCH 218/807] moved IServerProtocol to server from synergy directory. --- {synergy => server}/IServerProtocol.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {synergy => server}/IServerProtocol.h (100%) diff --git a/synergy/IServerProtocol.h b/server/IServerProtocol.h similarity index 100% rename from synergy/IServerProtocol.h rename to server/IServerProtocol.h From bdfdc8e816aa4ee3484b1a7e54c9cc333846f159 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 9 Jul 2002 17:31:45 +0000 Subject: [PATCH 219/807] checkpoint. moved IPrimaryScreen.h. --- {synergy => server}/IPrimaryScreen.h | 0 synergy/Makefile.am | 1 - 2 files changed, 1 deletion(-) rename {synergy => server}/IPrimaryScreen.h (100%) diff --git a/synergy/IPrimaryScreen.h b/server/IPrimaryScreen.h similarity index 100% rename from synergy/IPrimaryScreen.h rename to server/IPrimaryScreen.h diff --git a/synergy/Makefile.am b/synergy/Makefile.am index 93afea71..2cb3df73 100644 --- a/synergy/Makefile.am +++ b/synergy/Makefile.am @@ -18,7 +18,6 @@ libsynergy_a_SOURCES = \ CTCPSocketFactory.h \ ClipboardTypes.h \ IClipboard.h \ - IPrimaryScreen.h \ IScreenSaver.h \ ISecondaryScreen.h \ IServerProtocol.h \ From 64232c7854d1bad1c27f5d9b0825ada91e86fb97 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 9 Jul 2002 21:22:31 +0000 Subject: [PATCH 220/807] updated to new automake and refactored server stuff. the server now speaks to the primary screen and secondary screens almost everywhere the same way through an IClient interface; only special primary screen calls are accessed through a different interface, the CPrimaryClient interface. this simplifies the server since it no longer needs to test whether the active screen is the primary or a secondary in most cases. the server no longer speaks directly to the primary screen; all that goes through the CPrimaryClient, which often just forwards the call. the primary screen no longer speaks directly to the server either, again going through the CPrimaryClient via a IPrimaryReceiver interface. CServerProtocol classes have been replaced by CClientProxy classes which are very similar. the name makes more sense though. --- acsite.m4 => acinclude.m4 | 0 config/depcomp | 411 ++++++++++ config/missing | 109 ++- mt/CThread.cpp | 3 +- mt/CThread.h | 7 +- notes | 109 ++- platform/CXWindowsClipboard.cpp | 2 + server/CClientProxy.cpp | 47 ++ server/CClientProxy.h | 61 ++ server/CClientProxy1_0.cpp | 337 ++++++++ server/CClientProxy1_0.h | 52 ++ server/CPrimaryClient.cpp | 308 +++++++ server/CPrimaryClient.h | 91 +++ server/CServer.cpp | 1270 ++++++++++++++--------------- server/CServer.h | 149 ++-- server/CServerProtocol.cpp | 77 -- server/CServerProtocol.h | 61 -- server/CServerProtocol1_0.cpp | 283 ------- server/CServerProtocol1_0.h | 41 - server/CXWindowsPrimaryScreen.cpp | 78 +- server/CXWindowsPrimaryScreen.h | 13 +- server/IPrimaryReceiver.h | 46 ++ server/IPrimaryScreen.h | 43 +- server/IServerProtocol.h | 53 -- server/Makefile.am | 17 +- synergy/IClient.h | 91 +++ synergy/IServer.h | 43 + synergy/Makefile.am | 3 +- 28 files changed, 2415 insertions(+), 1390 deletions(-) rename acsite.m4 => acinclude.m4 (100%) create mode 100755 config/depcomp create mode 100644 server/CClientProxy.cpp create mode 100644 server/CClientProxy.h create mode 100644 server/CClientProxy1_0.cpp create mode 100644 server/CClientProxy1_0.h create mode 100644 server/CPrimaryClient.cpp create mode 100644 server/CPrimaryClient.h delete mode 100644 server/CServerProtocol.cpp delete mode 100644 server/CServerProtocol.h delete mode 100644 server/CServerProtocol1_0.cpp delete mode 100644 server/CServerProtocol1_0.h create mode 100644 server/IPrimaryReceiver.h delete mode 100644 server/IServerProtocol.h create mode 100644 synergy/IClient.h create mode 100644 synergy/IServer.h diff --git a/acsite.m4 b/acinclude.m4 similarity index 100% rename from acsite.m4 rename to acinclude.m4 diff --git a/config/depcomp b/config/depcomp new file mode 100755 index 00000000..65899658 --- /dev/null +++ b/config/depcomp @@ -0,0 +1,411 @@ +#! /bin/sh + +# depcomp - compile a program generating dependencies as side-effects +# Copyright 1999, 2000 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi +# `libtool' can also be set to `yes' or `no'. + +depfile=${depfile-`echo "$object" | sed 's,\([^/]*\)$,.deps/\1,;s/\.\([^.]*\)$/.P\1/'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. This file always lives in the current directory. + # Also, the AIX compiler puts `$object:' at the start of each line; + # $object doesn't have directory information. + stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + outname="$stripped.o" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 AIX compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + + tmpdepfile1="$object.d" + tmpdepfile2=`echo "$object" | sed -e 's/.o$/.d/'` + if test "$libtool" = yes; then + "$@" -Wc,-MD + else + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + if test -f "$tmpdepfile1"; then + tmpdepfile="$tmpdepfile1" + else + tmpdepfile="$tmpdepfile2" + fi + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a space and a tab in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + test -z "$dashmflag" && dashmflag=-M + ( IFS=" " + case " $* " in + *" --mode=compile "*) # this is libtool, let us make it quiet + for arg + do # cycle over the arguments + case "$arg" in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" $dashmflag | sed 's:^[^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + # X makedepend + ( + shift + cleared=no + for arg in "$@"; do + case $cleared in no) + set ""; shift + cleared=yes + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift;; + -*) + ;; + *) + set fnord "$@" "$arg"; shift;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} 2>/dev/null -o"$obj_suffix" -f"$tmpdepfile" "$@" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tail +3 "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + ( IFS=" " + case " $* " in + *" --mode=compile "*) + for arg + do # cycle over the arguments + case $arg in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" -E | + sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + ( IFS=" " + case " $* " in + *" --mode=compile "*) + for arg + do # cycle over the arguments + case $arg in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 diff --git a/config/missing b/config/missing index 7789652e..0a7fb5a2 100755 --- a/config/missing +++ b/config/missing @@ -1,7 +1,7 @@ #! /bin/sh # Common stub for a few missing GNU programs while installing. -# Copyright (C) 1996, 1997 Free Software Foundation, Inc. -# Franc,ois Pinard , 1996. +# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,11 +18,37 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + if test $# -eq 0; then echo 1>&2 "Try \`$0 --help' for more information" exit 1 fi +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. case "$1" in -h|--h|--he|--hel|--help) @@ -35,6 +61,7 @@ error status if there is no known handling for PROGRAM. Options: -h, --help display this help and exit -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails Supported PROGRAM values: aclocal touch file \`aclocal.m4' @@ -43,13 +70,15 @@ Supported PROGRAM values: automake touch all \`Makefile.in' files bison create \`y.tab.[ch]', if possible, from existing .[ch] flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file lex create \`lex.yy.c', if possible, from existing .c makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags yacc create \`y.tab.[ch]', if possible, from existing .[ch]" ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) - echo "missing - GNU libit 0.0" + echo "missing 0.3 - GNU automake" ;; -*) @@ -61,7 +90,7 @@ Supported PROGRAM values: aclocal) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if - you modified \`acinclude.m4' or \`configure.in'. You might want + you modified \`acinclude.m4' or \`${configure_ac}'. You might want to install the \`Automake' and \`Perl' packages. Grab them from any GNU archive site." touch aclocal.m4 @@ -70,7 +99,7 @@ WARNING: \`$1' is missing on your system. You should only need it if autoconf) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if - you modified \`configure.in'. You might want to install the + you modified \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." touch configure @@ -79,10 +108,10 @@ WARNING: \`$1' is missing on your system. You should only need it if autoheader) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if - you modified \`acconfig.h' or \`configure.in'. You might want + you modified \`acconfig.h' or \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." - files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' configure.in` + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` test -z "$files" && files="config.h" touch_files= for f in $files; do @@ -98,7 +127,7 @@ WARNING: \`$1' is missing on your system. You should only need it if automake) echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if - you modified \`Makefile.am', \`acinclude.m4' or \`configure.in'. + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. You might want to install the \`Automake' and \`Perl' packages. Grab them from any GNU archive site." find . -type f -name Makefile.am -print | @@ -159,7 +188,32 @@ WARNING: \`$1' is missing on your system. You should only need it if fi ;; + help2man) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + makeinfo) + if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then + # We have makeinfo, but it failed. + exit 1 + fi + echo 1>&2 "\ WARNING: \`$1' is missing on your system. You should only need it if you modified a \`.texi' or \`.texinfo' file, or any other file @@ -175,6 +229,45 @@ WARNING: \`$1' is missing on your system. You should only need it if touch $file ;; + tar) + shift + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + fi + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar ${1+"$@"} && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar ${1+"$@"} && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" ${1+"$@"} && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" ${1+"$@"} && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + *) echo 1>&2 "\ WARNING: \`$1' is needed, and you do not seem to have it handy on your diff --git a/mt/CThread.cpp b/mt/CThread.cpp index 80637a23..2741a4c4 100644 --- a/mt/CThread.cpp +++ b/mt/CThread.cpp @@ -127,8 +127,7 @@ CThread::getResult() const void* CThread::getUserData() { - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - return currentRep->getUserData(); + return m_rep->getUserData(); } bool diff --git a/mt/CThread.h b/mt/CThread.h index 264bd2ba..a6243707 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -88,15 +88,14 @@ public: // return a thread object representing the calling thread static CThread getCurrentThread(); - // get the user data passed to the constructor for the current - // thread. - static void* getUserData(); - // testCancel() does nothing but is a cancellation point. call // this to make a function itself a cancellation point. // (cancellation point) static void testCancel(); + // get the user data passed to the constructor for this thread. + void* getUserData(); + // waits for the thread to terminate (by exit() or cancel() or // by returning from the thread job). returns immediately if // the thread has already terminated. returns immediately with diff --git a/notes b/notes index c0ff39ba..eda240ce 100644 --- a/notes +++ b/notes @@ -7,16 +7,27 @@ hangup if invalid query info --> <-- info (size) +--- +nedit doesn't seem to be playing nice with the motif clipboard lock + it's not releasing it + may need to learn more about how the motif clipboard lock works + not sure if this a new problem with synergy or a problem with new nedit + --- enable heartbeat disconnection or remove heartbeat sending in client should either ship with it fully enabled or fully disabled possibly make it part of startup negotiation --- -sometimes jumping too far into secondary screen - probably bogus delta (adding in center pixel coords into delta?) - this is with an X primary and secondary - don't know which matters but probably X primary +getting a stuttering when leaving win32 server screen + +--- +merge platform dependent code into platform independent where possible + also try to simplify where possible + +--- +IServer and IPrimaryReceiver are really similar + can we merge them into one? --- use automake @@ -33,20 +44,59 @@ HTTP stuff handy for debugging at least --- -make generic input devices? - replace mouse and keyboard with button and valuator devices? - could fairly easily add new devices then - valuators would have to come in groups that change together - mouse position would be two ganged valuators - key events are not simply button presses though - they have a non-boolean character, shift state, and repeat count - maybe another device type: {data, state, repeat} +negotiation: + use a generic negotiation message (name/value pair) or specific + messages (name/values)? later allows more parsing to be done by + CProtocolUtil but how can we skip unknown messages? would have + to have a method on CInputPacketStream to discard to end of + message and we'd need to know the stream is a CInputPacketStream. -must handle on-the-fly addition/removal of devices - USB allows hotplugging and we should support, say, an added joystick + how does negotiation proceed? + could have sequence of queries and replies: + server -> client -> server ... end + client -> server -> client ... end + or a sequence of messages: + server -> client ... end + client -> server ... end + probably go with latter because it's more efficient but make it + 3-way: + client -> server ... end + server -> client ... end + client -> server ... end + first messages from client must be version and name. the server + can create the appropriate protocol object right away then. -must have device negotiation - at very least it negotiates mouse/keyboard at start + example: clipboard formats: + # CNEG = negotiation command CNEG%s%s + # CNGE = end of negotiation command + # cbfa = clipboard, format, add (permitted format) + # cbia = clipboard, id, add (clipboard with id exists) + client -> server: CNEG "vers" "1.0" + client -> server: CNEG "name" "foobar" + client -> server: CNGE + server -> client: CNEG "cbfa" "text/plain/LF" + server -> client: CNEG "cbfa" "image/BMP" + server -> client: CNGE + client -> server: CNEG "cbia" "0" + client -> server: CNGE + + server should just ask CProtocol (renamed from CServerProtocol) to + return a protocol. CProtocol should do negotiation, create the + appropriate protocol object, finish negotiation, and return the + new protocol object. unless connection is registered with server, + though, we can't save any screen info. perhaps CProtocol should + also return a screen info object or that should be part of the + protocol object (currently the protocol is part of the screen info). + if screen info is available to CProtocol then it should finish with + requesting the screen info, waiting for and then handling the reply. + the server can then just ask CProtocol for the protocol object then + add it to the connections. + + maybe call the protocol types CClientProxy since that's how the + server uses it: as a stand-in for the client object. the interface + should closely reflect the CClient interface. do the reverse for + a server proxy for the client. maybe even have interface classes + for the client and server methods. --- should add clipboard data type format negotiation @@ -72,6 +122,28 @@ allows compatibility with versions that are identical except for the support clipboard formats, obviating bumping up the protocol version number. +maybe config file can list format shared libraries to load. user +can then pick formats to support. but why would you ever want +fewer formats? + +should the major burden of translation be on client or server? +probably client since it knows the formats preferred by the +platform and may be able to use platform utilities to convert. +server would then just support formats that minimize loss of +information. + +desired formats to support: + text (LF) + text with character set + unicode (LF) + rich text (or some kind of formatting) + bitmap (BMP, PNG?) + sound (WAV) + +note that formats should be added to the win32 clipboard in the +order of most descriptive to least because they're kept in order +and apps will generally use the first suitable match. + --- hot keys should have keyboard shortcuts to jump to screens @@ -130,6 +202,7 @@ xscreensaver won't start if pointer on secondary screen unless using MIT or SGI screen saver extensions otherwise it cannot know when to stop the screen saver cos it can't grab mouse and keyboard + --- all screensavers hidden when showing win32 screensaver lock window win32 kills the screen saver and shows the lock window @@ -396,6 +469,10 @@ gspencer: code, since if you have a key down you aren't supposed to be able to cross over... +gspencer: + bouncy mouse at edge of screen (win32 server). almost certainly + related to changes that fixed mouse delta calculation. + == fixed? === dragging windows is too slow diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 08cb1f98..c2cd225e 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -525,6 +525,7 @@ CXWindowsClipboard::motifLockClipboard() const // fail if anybody owns the lock (even us, so this is non-recursive) Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); if (lockOwner != None) { + log((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner)); return false; } @@ -536,6 +537,7 @@ CXWindowsClipboard::motifLockClipboard() const XSetSelectionOwner(m_display, m_atomMotifClipLock, m_window, time); lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); if (lockOwner != m_window) { + log((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner)); return false; } diff --git a/server/CClientProxy.cpp b/server/CClientProxy.cpp new file mode 100644 index 00000000..372475c1 --- /dev/null +++ b/server/CClientProxy.cpp @@ -0,0 +1,47 @@ +#include "CClientProxy.h" +#include "IInputStream.h" +#include "IOutputStream.h" + +// +// CClientProxy +// + +CClientProxy::CClientProxy(IServer* server, const CString& name, + IInputStream* input, IOutputStream* output) : + m_server(server), + m_name(name), + m_input(input), + m_output(output) +{ + // do nothing +} + +CClientProxy::~CClientProxy() +{ + delete m_output; + delete m_input; +} + +IServer* +CClientProxy::getServer() const +{ + return m_server; +} + +IInputStream* +CClientProxy::getInputStream() const +{ + return m_input; +} + +IOutputStream* +CClientProxy::getOutputStream() const +{ + return m_output; +} + +CString +CClientProxy::getName() const +{ + return m_name; +} diff --git a/server/CClientProxy.h b/server/CClientProxy.h new file mode 100644 index 00000000..1b6c17bb --- /dev/null +++ b/server/CClientProxy.h @@ -0,0 +1,61 @@ +#ifndef CCLIENTPROXY_H +#define CCLIENTPROXY_H + +#include "IClient.h" +#include "CString.h" + +class IInputStream; +class IOutputStream; +class IServer; + +class CClientProxy : public IClient { +public: + CClientProxy(IServer* server, const CString& name, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CClientProxy(); + + // manipulators + + // accessors + + // get the server + IServer* getServer() const; + + // get the input and output streams for the client + IInputStream* getInputStream() const; + IOutputStream* getOutputStream() const; + + // IClient overrides + virtual void open() = 0; + virtual void run() = 0; + virtual void close() = 0; + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool screenSaver) = 0; + virtual bool leave() = 0; + virtual void setClipboard(ClipboardID, const CString&) = 0; + virtual void grabClipboard(ClipboardID) = 0; + virtual void setClipboardDirty(ClipboardID, bool) = 0; + virtual void keyDown(KeyID, KeyModifierMask) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void keyUp(KeyID, KeyModifierMask) = 0; + virtual void mouseDown(ButtonID) = 0; + virtual void mouseUp(ButtonID) = 0; + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void mouseWheel(SInt32 delta) = 0; + virtual void screenSaver(bool activate) = 0; + virtual CString getName() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + virtual void getCenter(SInt32& x, SInt32& y) const = 0; + virtual SInt32 getJumpZoneSize() const = 0; + +private: + IServer* m_server; + CString m_name; + IInputStream* m_input; + IOutputStream* m_output; +}; + +#endif diff --git a/server/CClientProxy1_0.cpp b/server/CClientProxy1_0.cpp new file mode 100644 index 00000000..b8139b7a --- /dev/null +++ b/server/CClientProxy1_0.cpp @@ -0,0 +1,337 @@ +#include "CClientProxy1_0.h" +#include "CServer.h" +#include "CClipboard.h" +#include "CProtocolUtil.h" +#include "ProtocolTypes.h" +#include "XSynergy.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" +#include "CStopwatch.h" +#include + +// +// CClientProxy1_0 +// + +CClientProxy1_0::CClientProxy1_0(IServer* server, const CString& name, + IInputStream* input, IOutputStream* output) : + CClientProxy(server, name, input, output) +{ + for (UInt32 i = 0; i < kClipboardEnd; ++i) { + m_clipboardDirty[i] = true; + } +} + +CClientProxy1_0::~CClientProxy1_0() +{ + // do nothing +} + +void +CClientProxy1_0::open() +{ + // send request + log((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgQInfo); + getOutputStream()->flush(); + + // wait for and verify reply + UInt8 code[4]; + for (;;) { + UInt32 n = getInputStream()->read(code, 4, -1.0); + if (n == 4) { + if (memcmp(code, kMsgCNoop, 4) == 0) { + // discard heartbeats + continue; + } + if (memcmp(code, kMsgDInfo, 4) == 0) { + break; + } + } + throw XBadClient(); + } + + // handle reply + recvInfo(false); +} + +void +CClientProxy1_0::run() +{ + // handle messages until the client hangs up or stops sending heartbeats + CStopwatch heartTimer; + for (;;) { + CThread::testCancel(); + + // wait for a message + UInt8 code[4]; + UInt32 n = getInputStream()->read(code, 4, kHeartRate); + CThread::testCancel(); + + // check if client hungup + if (n == 0) { + log((CLOG_NOTE "client \"%s\" disconnected", getName().c_str())); + return; + } + + // check if client has stopped sending heartbeats + if (n == (UInt32)-1) { +/* FIXME -- disabled to avoid masking bugs + if (heartTimer.getTime() > kHeartDeath) { + log((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); + return; + } +*/ + continue; + } + + // got a message so reset heartbeat monitor + heartTimer.reset(); + + // verify we got an entire code + if (n != 4) { + log((CLOG_ERR "incomplete message from \"%s\": %d bytes", getName().c_str(), n)); + + // client sent an incomplete message + throw XBadClient(); + } + + // parse message + log((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + if (memcmp(code, kMsgDInfo, 4) == 0) { + recvInfo(true); + } + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // discard no-ops + continue; + } + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + recvGrabClipboard(); + } + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + recvClipboard(); + } + // note -- more message handlers go here + else { + log((CLOG_ERR "invalid message from client \"%s\"", getName().c_str())); + + // unknown message + throw XBadClient(); + } + } +} + +void +CClientProxy1_0::close() +{ + log((CLOG_DEBUG1 "send close to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCClose); + + // force the close to be sent before we return + getOutputStream()->flush(); +} + +void +CClientProxy1_0::enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, bool) +{ + log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getName().c_str(), xAbs, yAbs, seqNum, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgCEnter, + xAbs, yAbs, seqNum, mask); +} + +bool +CClientProxy1_0::leave() +{ + log((CLOG_DEBUG1 "send leave to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCLeave); + + // we can never prevent the user from leaving + return true; +} + +void +CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) +{ + // ignore if this clipboard is already clean + CLock lock(&m_mutex); + if (m_clipboardDirty[id]) { + // this clipboard is now clean + m_clipboardDirty[id] = false; + + log((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); + } +} + +void +CClientProxy1_0::grabClipboard(ClipboardID id) +{ + log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); + + // this clipboard is now dirty + CLock lock(&m_mutex); + m_clipboardDirty[id] = true; +} + +void +CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty) +{ + CLock lock(&m_mutex); + m_clipboardDirty[id] = dirty; +} + +void +CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask) +{ + log((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); +} + +void +CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count) +{ + log((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); +} + +void +CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask) +{ + log((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); +} + +void +CClientProxy1_0::mouseDown(ButtonID button) +{ + log((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getName().c_str(), button)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); +} + +void +CClientProxy1_0::mouseUp(ButtonID button) +{ + log((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getName().c_str(), button)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); +} + +void +CClientProxy1_0::mouseMove(SInt32 xAbs, SInt32 yAbs) +{ + log((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getName().c_str(), xAbs, yAbs)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); +} + +void +CClientProxy1_0::mouseWheel(SInt32 delta) +{ + log((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), delta)); + CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); +} + +void +CClientProxy1_0::screenSaver(bool on) +{ + log((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getName().c_str(), on ? 1 : 0)); + CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); +} + +void +CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + CLock lock(&m_mutex); + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +CClientProxy1_0::getCenter(SInt32& x, SInt32& y) const +{ + CLock lock(&m_mutex); + x = m_cx; + y = m_cy; +} + +SInt32 +CClientProxy1_0::getJumpZoneSize() const +{ + CLock lock(&m_mutex); + return m_zoneSize; +} + +void +CClientProxy1_0::recvInfo(bool notify) +{ + { + CLock lock(&m_mutex); + + // parse the message + CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, + &m_x, &m_y, &m_w, &m_h, + &m_zoneSize, &m_cx, &m_cy); + log((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), m_x, m_y, m_w, m_h, m_zoneSize, m_cx, m_cy)); + + // validate + if (m_w <= 0 || m_h <= 0 || m_zoneSize < 0) { + throw XBadClient(); + } + if (m_cx < m_x || m_cy < m_y || m_cx >= m_x + m_w || m_cy >= m_y + m_h) { + throw XBadClient(); + } + } + + // tell server of change + if (notify) { + getServer()->onInfoChanged(getName()); + } + + // acknowledge receipt + log((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCInfoAck); +} + +void +CClientProxy1_0::recvClipboard() +{ + // parse message + ClipboardID id; + UInt32 seqNum; + CString data; + CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); + log((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, data.size())); + + // validate + if (id >= kClipboardEnd) { + throw XBadClient(); + } + + // send update. this calls us back to reset our clipboard dirty flag + // so don't hold a lock during the call. + getServer()->onClipboardChanged(id, seqNum, data); +} + +void +CClientProxy1_0::recvGrabClipboard() +{ + // parse message + ClipboardID id; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getName().c_str(), id, seqNum)); + + // validate + if (id >= kClipboardEnd) { + throw XBadClient(); + } + + // send update. this calls us back to reset our clipboard dirty flag + // so don't hold a lock during the call. + getServer()->onGrabClipboard(id, seqNum, getName()); +} diff --git a/server/CClientProxy1_0.h b/server/CClientProxy1_0.h new file mode 100644 index 00000000..4d43da64 --- /dev/null +++ b/server/CClientProxy1_0.h @@ -0,0 +1,52 @@ +#ifndef CCLIENTPROXY1_0_H +#define CCLIENTPROXY1_0_H + +#include "CClientProxy.h" +#include "CMutex.h" + +class CClientProxy1_0 : public CClientProxy { +public: + CClientProxy1_0(IServer* server, const CString& name, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CClientProxy1_0(); + + // IClient overrides + virtual void open(); + virtual void run(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool screenSaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool); + virtual void keyDown(KeyID, KeyModifierMask); + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void keyUp(KeyID, KeyModifierMask); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseWheel(SInt32 delta); + virtual void screenSaver(bool activate); + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCenter(SInt32& x, SInt32& y) const; + virtual SInt32 getJumpZoneSize() const; + +private: + void recvInfo(bool notify); + void recvClipboard(); + void recvGrabClipboard(); + +private: + CMutex m_mutex; + SInt16 m_x, m_y; + SInt16 m_w, m_h; + SInt16 m_zoneSize; + SInt16 m_cx, m_cy; + bool m_clipboardDirty[kClipboardEnd]; +}; + +#endif diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp new file mode 100644 index 00000000..b646aef1 --- /dev/null +++ b/server/CPrimaryClient.cpp @@ -0,0 +1,308 @@ +#include "CPrimaryClient.h" +#include "IServer.h" +#include "IPrimaryScreen.h" +#include "CClipboard.h" +#include "CLog.h" + +// FIXME -- use factory to create screen +#if WINDOWS_LIKE +#include "CMSWindowsPrimaryScreen.h" +#elif UNIX_LIKE +#include "CXWindowsPrimaryScreen.h" +#endif + +// +// CPrimaryClient +// + +CPrimaryClient::CPrimaryClient(IServer* server, const CString& name) : + m_server(server), + m_name(name), + m_seqNum(0) +{ + assert(m_server != NULL); + + // create screen + log((CLOG_DEBUG1 "creating primary screen")); +#if WINDOWS_LIKE + m_screen = new CMSWindowsPrimaryScreen(this); +#elif UNIX_LIKE + m_screen = new CXWindowsPrimaryScreen(this); +#endif +} + +CPrimaryClient::~CPrimaryClient() +{ + delete m_screen; +} + +void +CPrimaryClient::stop() +{ + m_screen->stop(); +} + +void +CPrimaryClient::reconfigure() +{ + m_screen->reconfigure(); +} + +void +CPrimaryClient::getClipboard(ClipboardID id, CString& data) const +{ + CClipboard clipboard; + m_screen->getClipboard(id, &clipboard); + data = clipboard.marshall(); +} + +bool +CPrimaryClient::isLockedToScreen() const +{ + return m_screen->isLockedToScreen(); +} + +KeyModifierMask +CPrimaryClient::getToggleMask() const +{ + return m_screen->getToggleMask(); +} + +void +CPrimaryClient::onError() +{ + m_server->onError(); +} + +void +CPrimaryClient::onInfoChanged(SInt32 x, SInt32 y, SInt32 w, SInt32 h, + SInt32 zoneSize, SInt32 cx, SInt32 cy) +{ + m_x = x; + m_y = y; + m_w = w; + m_h = h; + m_zoneSize = zoneSize; + m_cx = cx; + m_cy = cy; + m_server->onInfoChanged(getName()); +} + +bool +CPrimaryClient::onGrabClipboard(ClipboardID id) +{ + m_clipboardOwner[id] = m_server->onGrabClipboard(id, m_seqNum, getName()); +} + +bool +CPrimaryClient::onClipboardChanged(ClipboardID id, const CString& data) +{ + m_server->onClipboardChanged(id, m_seqNum, data); +} + +void +CPrimaryClient::onKeyDown(KeyID id, KeyModifierMask mask) +{ + m_server->onKeyDown(id, mask); +} + +void +CPrimaryClient::onKeyUp(KeyID id, KeyModifierMask mask) +{ + m_server->onKeyUp(id, mask); +} + +void +CPrimaryClient::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) +{ + m_server->onKeyRepeat(id, mask, count); +} + +void +CPrimaryClient::onMouseDown(ButtonID id) +{ + m_server->onMouseDown(id); +} + +void +CPrimaryClient::onMouseUp(ButtonID id) +{ + m_server->onMouseUp(id); +} + +bool +CPrimaryClient::onMouseMovePrimary(SInt32 x, SInt32 y) +{ + return m_server->onMouseMovePrimary(x, y); +} + +void +CPrimaryClient::onMouseMoveSecondary(SInt32 dx, SInt32 dy) +{ + m_server->onMouseMoveSecondary(dx, dy); +} + +void +CPrimaryClient::onMouseWheel(SInt32 delta) +{ + m_server->onMouseWheel(delta); +} + +void +CPrimaryClient::onScreenSaver(bool activated) +{ + m_server->onScreenSaver(activated); +} + +void +CPrimaryClient::open() +{ + // all clipboards are clean and owned by us + for (UInt32 i = 0; i < kClipboardEnd; ++i) { + m_clipboardOwner[i] = true; + m_clipboardDirty[i] = false; + } + + // now open the screen + m_screen->open(); +} + +void +CPrimaryClient::run() +{ + m_screen->run(); +} + +void +CPrimaryClient::close() +{ + m_screen->close(); +} + +void +CPrimaryClient::enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask, bool screensaver) +{ + // note -- we must not call any server methods except onError(). + m_seqNum = seqNum; + m_screen->enter(xAbs, yAbs, screensaver); +} + +bool +CPrimaryClient::leave() +{ + // note -- we must not call any server methods except onError(). + return m_screen->leave(); +} + +void +CPrimaryClient::setClipboard(ClipboardID id, const CString& data) +{ + // note -- we must not call any server methods except onError(). + + // ignore if this clipboard is already clean + if (m_clipboardDirty[id]) { + // this clipboard is now clean + m_clipboardDirty[id] = false; + + // unmarshall data + CClipboard clipboard; + clipboard.unmarshall(data, 0); + + // set clipboard + m_screen->setClipboard(id, &clipboard); + } +} + +void +CPrimaryClient::grabClipboard(ClipboardID id) +{ + // grab clipboard + m_screen->grabClipboard(id); + + // clipboard is dirty (because someone else owns it now) + m_clipboardOwner[id] = false; + m_clipboardDirty[id] = true; +} + +void +CPrimaryClient::setClipboardDirty(ClipboardID id, bool dirty) +{ + m_clipboardDirty[id] = dirty; +} + +void +CPrimaryClient::keyDown(KeyID, KeyModifierMask) +{ + // ignore +} + +void +CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32) +{ + // ignore +} + +void +CPrimaryClient::keyUp(KeyID, KeyModifierMask) +{ + // ignore +} + +void +CPrimaryClient::mouseDown(ButtonID) +{ + // ignore +} + +void +CPrimaryClient::mouseUp(ButtonID) +{ + // ignore +} + +void +CPrimaryClient::mouseMove(SInt32 x, SInt32 y) +{ + m_screen->warpCursor(x, y); +} + +void +CPrimaryClient::mouseWheel(SInt32) +{ + // ignore +} + +void +CPrimaryClient::screenSaver(bool) +{ + // ignore +} + +CString +CPrimaryClient::getName() const +{ + return m_name; +} + +void +CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +CPrimaryClient::getCenter(SInt32& x, SInt32& y) const +{ + x = m_cx; + y = m_cy; +} + +SInt32 +CPrimaryClient::getJumpZoneSize() const +{ + return m_zoneSize; +} diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h new file mode 100644 index 00000000..a7017edf --- /dev/null +++ b/server/CPrimaryClient.h @@ -0,0 +1,91 @@ +#ifndef CPRIMARYCLIENT_H +#define CPRIMARYCLIENT_H + +#include "IPrimaryReceiver.h" +#include "IClient.h" + +class IClipboard; +class IPrimaryScreen; +class IServer; + +class CPrimaryClient : public IPrimaryReceiver, public IClient { +public: + CPrimaryClient(IServer*, const CString& name); + ~CPrimaryClient(); + + // manipulators + + // cause run() to return + void stop(); + + // called by server when the configuration changes + void reconfigure(); + + // accessors + + // return the contents of the given clipboard. + void getClipboard(ClipboardID, CString&) const; + + // returns true iff the user is locked to the primary screen + bool isLockedToScreen() const; + + // returns the state of the toggle keys on the primary screen + KeyModifierMask getToggleMask() const; + + // IPrimaryReceiver overrides + virtual void onError(); + virtual void onInfoChanged(SInt32 xScreen, SInt32 yScreen, + SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse); + virtual bool onGrabClipboard(ClipboardID); + virtual bool onClipboardChanged(ClipboardID, const CString& data); + virtual void onKeyDown(KeyID, KeyModifierMask); + virtual void onKeyUp(KeyID, KeyModifierMask); + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void onMouseDown(ButtonID); + virtual void onMouseUp(ButtonID); + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); + virtual void onMouseWheel(SInt32 delta); + virtual void onScreenSaver(bool activated); + + // IClient overrides + virtual void open(); + virtual void run(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool screenSaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool); + virtual void keyDown(KeyID, KeyModifierMask); + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void keyUp(KeyID, KeyModifierMask); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseWheel(SInt32 delta); + virtual void screenSaver(bool activate); + virtual CString getName() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCenter(SInt32& x, SInt32& y) const; + virtual SInt32 getJumpZoneSize() const; + +private: + IServer* m_server; + IPrimaryScreen* m_screen; + CString m_name; + UInt32 m_seqNum; + SInt16 m_x, m_y; + SInt16 m_w, m_h; + SInt16 m_zoneSize; + SInt16 m_cx, m_cy; + bool m_clipboardOwner[kClipboardEnd]; + bool m_clipboardDirty[kClipboardEnd]; +}; + +#endif diff --git a/server/CServer.cpp b/server/CServer.cpp index 5e7004ba..f414bfd0 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -2,9 +2,9 @@ #include "CHTTPServer.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" +#include "CPrimaryClient.h" #include "CProtocolUtil.h" -#include "CServerProtocol.h" -#include "IPrimaryScreen.h" +#include "CClientProxy1_0.h" #include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" @@ -38,9 +38,8 @@ const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; CServer::CServer(const CString& serverName) : m_name(serverName), - m_primary(NULL), m_active(NULL), - m_primaryInfo(NULL), + m_primaryClient(NULL), m_seqNum(0), m_activeSaver(NULL), m_httpServer(NULL), @@ -85,7 +84,7 @@ CServer::run() // check preconditions { CLock lock(&m_mutex); - assert(m_primary != NULL); + assert(m_primaryClient != NULL); } try { @@ -103,36 +102,38 @@ CServer::run() // handle events log((CLOG_DEBUG "starting event handling")); - m_primary->run(); + m_primaryClient->run(); // clean up log((CLOG_NOTE "stopping server")); - stopThreads(); - delete m_httpServer; - m_httpServer = NULL; - closePrimaryScreen(); + + // use a macro to write the stuff that should go into a finally + // block so we can repeat it easily. stroustrup's view that + // "resource acquistion is initialization" is a better solution + // than a finally block is parochial. they both have their + // place. adding finally to C++ would've been a drop in a big + // bucket. +#define FINALLY do { \ + stopThreads(); \ + delete m_httpServer; \ + m_httpServer = NULL; \ + if (m_primaryClient != NULL) { \ + closePrimaryScreen(); \ + } \ + } while (false) + FINALLY; } catch (XBase& e) { log((CLOG_ERR "server error: %s", e.what())); // clean up log((CLOG_NOTE "stopping server")); - stopThreads(); - delete m_httpServer; - m_httpServer = NULL; - if (m_primary != NULL) { - closePrimaryScreen(); - } + FINALLY; } catch (XThread&) { // clean up log((CLOG_NOTE "stopping server")); - stopThreads(); - delete m_httpServer; - m_httpServer = NULL; - if (m_primary != NULL) { - closePrimaryScreen(); - } + FINALLY; throw; } catch (...) { @@ -140,96 +141,41 @@ CServer::run() // clean up log((CLOG_NOTE "stopping server")); - stopThreads(); - delete m_httpServer; - m_httpServer = NULL; - if (m_primary != NULL) { - closePrimaryScreen(); - } + FINALLY; throw; } +#undef FINALLY } void CServer::quit() { - m_primary->stop(); -} - -void -CServer::shutdown() -{ - // stop all running threads but don't wait too long since some - // threads may be unable to proceed until this thread returns. - stopThreads(3.0); - - // done with the HTTP server - delete m_httpServer; - m_httpServer = NULL; - - // note -- we do not attempt to close down the primary screen + m_primaryClient->stop(); } bool CServer::setConfig(const CConfig& config) { - typedef std::vector CThreads; - CThreads threads; + // refuse configuration if it doesn't include the primary screen { CLock lock(&m_mutex); - - // refuse configuration if it doesn't include the primary screen - if (m_primaryInfo != NULL && - !config.isScreen(m_primaryInfo->m_name)) { + if (m_primaryClient != NULL && + !config.isScreen(m_primaryClient->getName())) { return false; } - - // get the set of screens that are connected but are being - // dropped from the configuration (or who's canonical name - // is changing). don't add the primary screen. also tell - // the secondary screen to disconnect. - for (CScreenList::const_iterator index = m_screens.begin(); - index != m_screens.end(); ++index) { - if (index->second != m_primaryInfo && - !config.isCanonicalName(index->first)) { - assert(index->second->m_protocol != NULL); - index->second->m_protocol->sendClose(); - threads.push_back(index->second->m_thread); - } - } } - // wait a moment to allow each secondary screen to close - // its connection before we close it (to avoid having our - // socket enter TIME_WAIT). - if (threads.size() > 0) { - CThread::sleep(1.0); - } - - // cancel the old secondary screen threads - for (CThreads::iterator index = threads.begin(); - index != threads.end(); ++index) { - index->cancel(); - } - - // wait for old secondary screen threads to disconnect. must - // not hold lock while we do this so those threads can finish - // any calls to this object. - for (CThreads::iterator index = threads.begin(); - index != threads.end(); ++index) { - index->wait(); - } - - // clean up thread list - reapThreads(); + // close clients that are connected but being dropped from the + // configuration. + closeClients(config); // cut over CLock lock(&m_mutex); m_config = config; // tell primary screen about reconfiguration - if (m_primary != NULL) { - m_primary->onConfigure(); + if (m_primaryClient != NULL) { + m_primaryClient->reconfigure(); } return true; @@ -275,65 +221,50 @@ CServer::getActivePrimarySides() const } void -CServer::setInfo(SInt32 x, SInt32 y, SInt32 w, SInt32 h, - SInt32 zoneSize, SInt32 mx, SInt32 my) +CServer::onError() { - CLock lock(&m_mutex); - assert(m_primaryInfo != NULL); - setInfoNoLock(m_primaryInfo->m_name, x, y, w, h, zoneSize, mx, my); + // stop all running threads but don't wait too long since some + // threads may be unable to proceed until this thread returns. + stopThreads(3.0); + + // done with the HTTP server + delete m_httpServer; + m_httpServer = NULL; + + // note -- we do not attempt to close down the primary screen } void -CServer::setInfo(const CString& client, - SInt32 x, SInt32 y, SInt32 w, SInt32 h, - SInt32 zoneSize, SInt32 mx, SInt32 my) +CServer::onInfoChanged(const CString& name) { CLock lock(&m_mutex); - setInfoNoLock(client, x, y, w, h, zoneSize, mx, my); -} -void -CServer::setInfoNoLock(const CString& screen, - SInt32 x, SInt32 y, SInt32 w, SInt32 h, - SInt32 zoneSize, SInt32 mx, SInt32 my) -{ - assert(!screen.empty()); - assert(w > 0); - assert(h > 0); - assert(zoneSize >= 0); - - // screen must be connected - CScreenList::iterator index = m_screens.find(screen); - if (index == m_screens.end()) { + // look up client + CClientList::iterator index = m_clients.find(name); + if (index == m_clients.end()) { throw XBadClient(); } + IClient* client = index->second; + assert(client != NULL); - // screen is now ready (i.e. available to user) - CScreenInfo* info = index->second; - info->m_ready = true; - - // update screen info - if (info == m_active) { - // update the remote mouse coordinates - m_x = mx; - m_y = my; + // update the remote mouse coordinates + if (client == m_active) { + client->getCenter(m_x, m_y); } - info->m_x = x; - info->m_y = y; - info->m_w = w; - info->m_h = h; - info->m_zoneSize = zoneSize; - log((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", screen.c_str(), x, y, w, h, zoneSize, mx, my)); - // send acknowledgement (if screen isn't the primary) - if (info->m_protocol != NULL) { - info->m_protocol->sendInfoAcknowledgment(); +#ifndef NDEBUG + { + SInt32 x, y, w, h, mx, my; + client->getShape(x, y, w, h); + client->getCenter(mx, my); + log((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", name.c_str(), x, y, w, h, client->getJumpZoneSize(), m_x, m_y)); } +#endif // handle resolution change to primary screen - else { - if (info == m_active) { - onMouseMovePrimaryNoLock(mx, my); + if (client == m_primaryClient) { + if (client == m_active) { + onMouseMovePrimaryNoLock(m_x, m_y); } else { onMouseMoveSecondaryNoLock(0, 0); @@ -342,109 +273,111 @@ CServer::setInfoNoLock(const CString& screen, } void -CServer::grabClipboard(ClipboardID id) +CServer::onGrabClipboard(ClipboardID id) { CLock lock(&m_mutex); - assert(m_primaryInfo != NULL); - grabClipboardNoLock(id, 0, m_primaryInfo->m_name); + assert(m_primaryClient != NULL); + grabClipboardNoLock(id, 0, m_primaryClient->getName()); } -void -CServer::grabClipboard(ClipboardID id, UInt32 seqNum, const CString& client) +bool +CServer::onGrabClipboard(ClipboardID id, UInt32 seqNum, const CString& client) { CLock lock(&m_mutex); - grabClipboardNoLock(id, seqNum, client); + return grabClipboardNoLock(id, seqNum, client); } -void +bool CServer::grabClipboardNoLock(ClipboardID id, - UInt32 seqNum, const CString& screen) + UInt32 seqNum, const CString& name) { // note -- must be locked on entry CClipboardInfo& clipboard = m_clipboards[id]; // screen must be connected - CScreenList::iterator index = m_screens.find(screen); - if (index == m_screens.end()) { + CClientList::iterator grabber = m_clients.find(name); + if (grabber == m_clients.end()) { throw XBadClient(); } // ignore grab if sequence number is old. always allow primary // screen to grab. - if (screen != m_primaryInfo->m_name && + if (name != m_primaryClient->getName() && seqNum < clipboard.m_clipboardSeqNum) { - log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", screen.c_str(), id)); - return; + log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", name.c_str(), id)); + return false; } // mark screen as owning clipboard - log((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str())); - clipboard.m_clipboardOwner = screen; + log((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", name.c_str(), id, clipboard.m_clipboardOwner.c_str())); + clipboard.m_clipboardOwner = name; clipboard.m_clipboardSeqNum = seqNum; - // no screens have the new clipboard except the sender - clearGotClipboard(id); - index->second->m_gotClipboard[id] = true; + // clear the clipboard data (since it's not known at this point) + if (clipboard.m_clipboard.open(0)) { + clipboard.m_clipboard.empty(); + clipboard.m_clipboard.close(); + } + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); - // tell all other screens to take ownership of clipboard - for (index = m_screens.begin(); index != m_screens.end(); ++index) { - if (index->first != screen) { - CScreenInfo* info = index->second; - if (info->m_protocol == NULL) { - m_primary->grabClipboard(id); - } - else { - info->m_protocol->sendGrabClipboard(id); - } + // tell all other screens to take ownership of clipboard. tell the + // grabber that it's clipboard isn't dirty. + for (CClientList::iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + if (index == grabber) { + client->setClipboardDirty(id, false); + } + else { + client->grabClipboard(id); } } - // get the clipboard data if primary has it, otherwise mark the - // clipboard data as unknown. - if (m_active->m_protocol == NULL) { - // get clipboard immediately from primary screen - m_primary->getClipboard(id, &clipboard.m_clipboard); - clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); - clipboard.m_clipboardReady = true; - } - else { - // clear out the clipboard since existing data is now out of date. - if (clipboard.m_clipboard.open(0)) { - clipboard.m_clipboard.close(); - } - clipboard.m_clipboardReady = false; - } + return true; } void -CServer::setClipboard(ClipboardID id, UInt32 seqNum, const CString& data) +CServer::onClipboardChanged(ClipboardID id, + UInt32 seqNum, const CString& data) { CLock lock(&m_mutex); + onClipboardChangedNoLock(id, seqNum, data); +} + +void +CServer::onClipboardChangedNoLock(ClipboardID id, + UInt32 seqNum, const CString& data) +{ CClipboardInfo& clipboard = m_clipboards[id]; // ignore update if sequence number is old if (seqNum < clipboard.m_clipboardSeqNum) { - log((CLOG_INFO "ignored screen \"%s\" update of clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + log((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", clipboard.m_clipboardOwner.c_str(), id)); + return; + } + + // ignore if data hasn't changed + if (data == clipboard.m_clipboardData) { + log((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id)); return; } // unmarshall into our clipboard buffer log((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); - clipboard.m_clipboardReady = true; - clipboard.m_clipboardData = data; + clipboard.m_clipboardData = data; clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); - // all screens have an out-of-date clipboard except the sender - clearGotClipboard(id); - CScreenList::const_iterator index = - m_screens.find(clipboard.m_clipboardOwner); - if (index != m_screens.end()) { - index->second->m_gotClipboard[id] = true; + // tell all clients except the sender that the clipboard is dirty + CClientList::const_iterator sender = + m_clients.find(clipboard.m_clipboardOwner); + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + client->setClipboardDirty(id, index != sender); } - // send the new clipboard to the active screen (will do nothing if - // the active screen is the one sending the new clipboard) - sendClipboard(id); + // send the new clipboard to the active screen + m_active->setClipboard(id, m_clipboards[id].m_clipboardData); } bool @@ -466,9 +399,7 @@ CServer::onKeyDown(KeyID id, KeyModifierMask mask) } // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendKeyDown(id, mask); - } + m_active->keyDown(id, mask); } void @@ -484,9 +415,7 @@ CServer::onKeyUp(KeyID id, KeyModifierMask mask) } // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendKeyUp(id, mask); - } + m_active->keyUp(id, mask); } void @@ -503,9 +432,7 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) } // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendKeyRepeat(id, mask, count); - } + m_active->keyRepeat(id, mask, count); } void @@ -516,9 +443,7 @@ CServer::onMouseDown(ButtonID id) assert(m_active != NULL); // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendMouseDown(id); - } + m_active->mouseDown(id); } void @@ -529,9 +454,7 @@ CServer::onMouseUp(ButtonID id) assert(m_active != NULL); // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendMouseUp(id); - } + m_active->mouseUp(id); } bool @@ -546,33 +469,38 @@ bool CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) { // mouse move on primary (server's) screen - assert(m_active != NULL); - assert(m_active->m_protocol == NULL); + assert(m_primaryClient != NULL); + assert(m_active == m_primaryClient); // ignore if mouse is locked to screen if (isLockedToScreenNoLock()) { return false; } + // get screen shape + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + SInt32 zoneSize = m_active->getJumpZoneSize(); + // see if we should change screens CConfig::EDirection dir; - if (x < m_active->m_x + m_active->m_zoneSize) { - x -= m_active->m_zoneSize; + if (x < ax + zoneSize) { + x -= zoneSize; dir = CConfig::kLeft; log((CLOG_DEBUG1 "switch to left")); } - else if (x >= m_active->m_x + m_active->m_w - m_active->m_zoneSize) { - x += m_active->m_zoneSize; + else if (x >= ax + aw - zoneSize) { + x += zoneSize; dir = CConfig::kRight; log((CLOG_DEBUG1 "switch to right")); } - else if (y < m_active->m_y + m_active->m_zoneSize) { - y -= m_active->m_zoneSize; + else if (y < ay + zoneSize) { + y -= zoneSize; dir = CConfig::kTop; log((CLOG_DEBUG1 "switch to top")); } - else if (y >= m_active->m_y + m_active->m_h - m_active->m_zoneSize) { - y += m_active->m_zoneSize; + else if (y >= ay + ah - zoneSize) { + y += zoneSize; dir = CConfig::kBottom; log((CLOG_DEBUG1 "switch to bottom")); } @@ -583,7 +511,7 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) // get jump destination and, if no screen in jump direction, // then ignore the move. - CScreenInfo* newScreen = getNeighbor(m_active, dir, x, y); + IClient* newScreen = getNeighbor(m_active, dir, x, y); if (newScreen == NULL) { return false; } @@ -606,7 +534,7 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) { // mouse move on secondary (client's) screen assert(m_active != NULL); - if (m_active->m_protocol == NULL) { + if (m_active == m_primaryClient) { // we're actually on the primary screen. this can happen // when the primary screen begins processing a mouse move // for a secondary screen, then the active (secondary) @@ -625,22 +553,26 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) m_x += dx; m_y += dy; + // get screen shape + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + // switch screens if the mouse is outside the screen and not // locked to the screen - CScreenInfo* newScreen = NULL; + IClient* newScreen = NULL; if (!isLockedToScreenNoLock()) { // find direction of neighbor CConfig::EDirection dir; - if (m_x < m_active->m_x) { + if (m_x < ax) { dir = CConfig::kLeft; } - else if (m_x > m_active->m_x + m_active->m_w - 1) { + else if (m_x > ax + aw - 1) { dir = CConfig::kRight; } - else if (m_y < m_active->m_y) { + else if (m_y < ay) { dir = CConfig::kTop; } - else if (m_y > m_active->m_y + m_active->m_h - 1) { + else if (m_y > ay + ah - 1) { dir = CConfig::kBottom; } else { @@ -652,42 +584,50 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) // get neighbor if we should switch if (newScreen == NULL) { - log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->m_name.c_str(), CConfig::dirName(dir))); + log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); // get new position or clamp to current screen newScreen = getNeighbor(m_active, dir, m_x, m_y); if (newScreen == NULL) { log((CLOG_DEBUG1 "no neighbor; clamping")); - if (m_x < m_active->m_x) - m_x = m_active->m_x; - else if (m_x > m_active->m_x + m_active->m_w - 1) - m_x = m_active->m_x + m_active->m_w - 1; - if (m_y < m_active->m_y) - m_y = m_active->m_y; - else if (m_y > m_active->m_y + m_active->m_h - 1) - m_y = m_active->m_y + m_active->m_h - 1; + if (m_x < ax) { + m_x = ax; + } + else if (m_x > ax + aw - 1) { + m_x = ax + aw - 1; + } + if (m_y < ay) { + m_y = ay; + } + else if (m_y > ay + ah - 1) { + m_y = ay + ah - 1; + } } } } else { // clamp to edge when locked - log((CLOG_DEBUG1 "clamp to \"%s\"", m_active->m_name.c_str())); - if (m_x < m_active->m_x) - m_x = m_active->m_x; - else if (m_x > m_active->m_x + m_active->m_w - 1) - m_x = m_active->m_x + m_active->m_w - 1; - if (m_y < m_active->m_y) - m_y = m_active->m_y; - else if (m_y > m_active->m_y + m_active->m_h - 1) - m_y = m_active->m_y + m_active->m_h - 1; + log((CLOG_DEBUG1 "clamp to \"%s\"", m_active->getName().c_str())); + if (m_x < ax) { + m_x = ax; + } + else if (m_x > ax + aw - 1) { + m_x = ax + aw - 1; + } + if (m_y < ay) { + m_y = ay; + } + else if (m_y > ay + ah - 1) { + m_y = ay + ah - 1; + } } // warp cursor if on same screen if (newScreen == NULL || newScreen == m_active) { // do nothing if mouse didn't move if (m_x != xOld || m_y != yOld) { - log((CLOG_DEBUG2 "move on %s to %d,%d", m_active->m_name.c_str(), m_x, m_y)); - m_active->m_protocol->sendMouseMove(m_x, m_y); + log((CLOG_DEBUG2 "move on %s to %d,%d", m_active->getName().c_str(), m_x, m_y)); + m_active->mouseMove(m_x, m_y); } } @@ -705,9 +645,7 @@ CServer::onMouseWheel(SInt32 delta) assert(m_active != NULL); // relay - if (m_active->m_protocol != NULL) { - m_active->m_protocol->sendMouseWheel(delta); - } + m_active->mouseWheel(delta); } void @@ -723,30 +661,31 @@ CServer::onScreenSaver(bool activated) m_ySaver = m_y; // jump to primary screen - if (m_active != m_primaryInfo) { - switchScreen(m_primaryInfo, 0, 0, true); + if (m_active != m_primaryClient) { + switchScreen(m_primaryClient, 0, 0, true); } } else { // jump back to previous screen and position. we must check // that the position is still valid since the screen may have // changed resolutions while the screen saver was running. - if (m_activeSaver != NULL && m_activeSaver != m_primaryInfo) { + if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { // check position - CScreenInfo* screen = m_activeSaver; - if (m_xSaver < screen->m_x + screen->m_zoneSize) { - m_xSaver = screen->m_x + screen->m_zoneSize; + IClient* screen = m_activeSaver; + SInt32 x, y, w, h; + screen->getShape(x, y, w, h); + SInt32 zoneSize = screen->getJumpZoneSize(); + if (m_xSaver < x + zoneSize) { + m_xSaver = x + zoneSize; } - else if (m_xSaver >= screen->m_x + - screen->m_w - screen->m_zoneSize) { - m_xSaver = screen->m_x + screen->m_w - screen->m_zoneSize - 1; + else if (m_xSaver >= x + w - zoneSize) { + m_xSaver = x + w - zoneSize - 1; } - if (m_ySaver < screen->m_y + screen->m_zoneSize) { - m_ySaver = screen->m_y + screen->m_zoneSize; + if (m_ySaver < y + zoneSize) { + m_ySaver = y + zoneSize; } - else if (m_ySaver >= screen->m_y + - screen->m_h - screen->m_zoneSize) { - m_ySaver = screen->m_y + screen->m_h - screen->m_zoneSize - 1; + else if (m_ySaver >= y + h - zoneSize) { + m_ySaver = y + h - zoneSize - 1; } // jump @@ -757,32 +696,24 @@ CServer::onScreenSaver(bool activated) m_activeSaver = NULL; } - // send message to all secondary screens - for (CScreenList::const_iterator index = m_screens.begin(); - index != m_screens.end(); ++index) { - if (index->second->m_protocol != NULL) { - index->second->m_protocol->sendScreenSaver(activated); - } + // send message to all clients + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + client->screenSaver(activated); } } -bool -CServer::isLockedToScreen() const -{ - CLock lock(&m_mutex); - return isLockedToScreenNoLock(); -} - bool CServer::isLockedToScreenNoLock() const { // locked if primary says we're locked - if (m_primary->isLockedToScreen()) { + if (m_primaryClient->isLockedToScreen()) { return true; } // locked if scroll-lock is toggled on - if ((m_primary->getToggleMask() & KeyModifierScrollLock) != 0) { + if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) { return true; } @@ -791,14 +722,19 @@ CServer::isLockedToScreenNoLock() const } void -CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y, bool screenSaver) +CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool screenSaver) { assert(dst != NULL); - assert(x >= dst->m_x && y >= dst->m_y); - assert(x < dst->m_x + dst->m_w && y < dst->m_y + dst->m_h); +#ifndef NDEBUG + { + SInt32 dx, dy, dw, dh; + dst->getShape(dx, dy, dw, dh); + assert(x >= dx && y >= dy && x < dx + dw && y < dy + dh); + } +#endif assert(m_active != NULL); - log((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y)); + log((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y)); // FIXME -- we're not locked here but we probably should be // record new position @@ -809,24 +745,24 @@ CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y, bool screenSaver) // since that's a waste of time we skip that and just warp the // mouse. if (m_active != dst) { - // note if we're leaving the primary screen - const bool leavingPrimary = (m_active->m_protocol == NULL); - // leave active screen - if (leavingPrimary) { - if (!m_primary->leave()) { - // cannot leave primary screen - log((CLOG_WARN "can't leave primary screen")); - return; - } - - // update the clipboards that the primary screen owns - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - updatePrimaryClipboard(id); - } + if (!m_active->leave()) { + // cannot leave screen + log((CLOG_WARN "can't leave screen")); + return; } - else { - m_active->m_protocol->sendLeave(); + + // update the primary client's clipboards if we're leaving the + // primary screen. + if (m_active == m_primaryClient) { + for (UInt32 id = 0; id < kClipboardEnd; ++id) { + CClipboardInfo& clipboard = m_clipboards[id]; + if (clipboard.m_clipboardOwner == m_primaryClient->getName()) { + CString clipboardData; + m_primaryClient->getClipboard(id, clipboardData); + onClipboardChangedNoLock(id, m_seqNum, clipboardData); + } + } } // cut over @@ -836,35 +772,25 @@ CServer::switchScreen(CScreenInfo* dst, SInt32 x, SInt32 y, bool screenSaver) ++m_seqNum; // enter new screen - if (m_active->m_protocol == NULL) { - m_primary->enter(x, y, screenSaver); - } - else { - m_active->m_protocol->sendEnter(x, y, m_seqNum, - m_primary->getToggleMask()); - } + m_active->enter(x, y, m_seqNum, + m_primaryClient->getToggleMask(), screenSaver); // send the clipboard data to new active screen for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - sendClipboard(id); + m_active->setClipboard(id, m_clipboards[id].m_clipboardData); } } else { - if (m_active->m_protocol == NULL) { - m_primary->warpCursor(x, y); - } - else { - m_active->m_protocol->sendMouseMove(x, y); - } + m_active->mouseMove(x, y); } } -CServer::CScreenInfo* -CServer::getNeighbor(CScreenInfo* src, CConfig::EDirection dir) const +IClient* +CServer::getNeighbor(IClient* src, CConfig::EDirection dir) const { assert(src != NULL); - CString srcName = src->m_name; + CString srcName = src->getName(); assert(!srcName.empty()); log((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); for (;;) { @@ -880,8 +806,8 @@ CServer::getNeighbor(CScreenInfo* src, CConfig::EDirection dir) const // look up neighbor cell. if the screen is connected and // ready then we can stop. otherwise we skip over an // unconnected screen. - CScreenList::const_iterator index = m_screens.find(dstName); - if (index != m_screens.end() && index->second->m_ready) { + CClientList::const_iterator index = m_clients.find(dstName); + if (index != m_clients.end()) { log((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); return index->second; } @@ -891,94 +817,93 @@ CServer::getNeighbor(CScreenInfo* src, CConfig::EDirection dir) const } } -CServer::CScreenInfo* -CServer::getNeighbor(CScreenInfo* src, +IClient* +CServer::getNeighbor(IClient* src, CConfig::EDirection srcSide, SInt32& x, SInt32& y) const { assert(src != NULL); // get the first neighbor - CScreenInfo* dst = getNeighbor(src, srcSide); + IClient* dst = getNeighbor(src, srcSide); if (dst == NULL) { return NULL; } // get the source screen's size (needed for kRight and kBottom) - SInt32 w = src->m_w, h = src->m_h; + SInt32 sx, sy, sw, sh; + SInt32 dx, dy, dw, dh; + IClient* lastGoodScreen = src; + lastGoodScreen->getShape(sx, sy, sw, sh); + lastGoodScreen->getShape(dx, dy, dw, dh); // find destination screen, adjusting x or y (but not both). the // searches are done in a sort of canonical screen space where // the upper-left corner is 0,0 for each screen. we adjust from // actual to canonical position on entry to and from canonical to // actual on exit from the search. - CScreenInfo* lastGoodScreen = src; switch (srcSide) { case CConfig::kLeft: - x -= src->m_x; + x -= dx; while (dst != NULL && dst != lastGoodScreen) { lastGoodScreen = dst; - w = lastGoodScreen->m_w; - h = lastGoodScreen->m_h; - x += w; + lastGoodScreen->getShape(dx, dy, dw, dh); + x += dw; if (x >= 0) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); - x += lastGoodScreen->m_x; + x += dx; break; case CConfig::kRight: - x -= src->m_x; + x -= dx; while (dst != NULL) { + x -= dw; lastGoodScreen = dst; - x -= w; - w = lastGoodScreen->m_w; - h = lastGoodScreen->m_h; - if (x < w) { + lastGoodScreen->getShape(dx, dy, dw, dh); + if (x < dw) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); - x += lastGoodScreen->m_x; + x += dx; break; case CConfig::kTop: - y -= src->m_y; + y -= dy; while (dst != NULL) { lastGoodScreen = dst; - w = lastGoodScreen->m_w; - h = lastGoodScreen->m_h; - y += h; + lastGoodScreen->getShape(dx, dy, dw, dh); + y += dh; if (y >= 0) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); - y += lastGoodScreen->m_y; + y += dy; break; case CConfig::kBottom: - y -= src->m_y; + y -= dy; while (dst != NULL) { + y -= dh; lastGoodScreen = dst; - y -= h; - w = lastGoodScreen->m_w; - h = lastGoodScreen->m_h; - if (y < h) { + lastGoodScreen->getShape(dx, dy, dw, dh); + if (y < sh) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->m_name.c_str())); + log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); - y += lastGoodScreen->m_y; + y += dy; break; } @@ -990,31 +915,31 @@ CServer::getNeighbor(CScreenInfo* src, // to avoid the jump zone. if entering a side that doesn't have // a neighbor (i.e. an asymmetrical side) then we don't need to // move inwards because that side can't provoke a jump. - if (dst->m_protocol == NULL) { - const CString dstName(dst->m_name); + if (dst == m_primaryClient) { + const CString dstName(dst->getName()); switch (srcSide) { case CConfig::kLeft: if (!m_config.getNeighbor(dstName, CConfig::kRight).empty() && - x > dst->m_x + w - 1 - dst->m_zoneSize) - x = dst->m_x + w - 1 - dst->m_zoneSize; + x > dx + dw - 1 - dst->getJumpZoneSize()) + x = dx + dw - 1 - dst->getJumpZoneSize(); break; case CConfig::kRight: if (!m_config.getNeighbor(dstName, CConfig::kLeft).empty() && - x < dst->m_x + dst->m_zoneSize) - x = dst->m_x + dst->m_zoneSize; + x < dx + dst->getJumpZoneSize()) + x = dx + dst->getJumpZoneSize(); break; case CConfig::kTop: if (!m_config.getNeighbor(dstName, CConfig::kBottom).empty() && - y > dst->m_y + h - 1 - dst->m_zoneSize) - y = dst->m_y + h - 1 - dst->m_zoneSize; + y > dy + dh - 1 - dst->getJumpZoneSize()) + y = dy + dh - 1 - dst->getJumpZoneSize(); break; case CConfig::kBottom: if (!m_config.getNeighbor(dstName, CConfig::kTop).empty() && - y < dst->m_y + dst->m_zoneSize) - y = dst->m_y + dst->m_zoneSize; + y < dy + dst->getJumpZoneSize()) + y = dy + dst->getJumpZoneSize(); break; } } @@ -1028,50 +953,110 @@ CServer::getNeighbor(CScreenInfo* src, switch (srcSide) { case CConfig::kLeft: case CConfig::kRight: - y -= src->m_y; + y -= sy; if (y < 0) { y = 0; } - else if (y >= src->m_h) { - y = dst->m_h - 1; + else if (y >= sh) { + y = dh - 1; } else { y = static_cast(0.5 + y * - static_cast(dst->m_h - 1) / - (src->m_h - 1)); + static_cast(dh - 1) / (sh - 1)); } - y += dst->m_y; + y += dy; break; case CConfig::kTop: case CConfig::kBottom: - x -= src->m_x; + x -= sx; if (x < 0) { x = 0; } - else if (x >= src->m_w) { - x = dst->m_w - 1; + else if (x >= sw) { + x = dw - 1; } else { x = static_cast(0.5 + x * - static_cast(dst->m_w - 1) / - (src->m_w - 1)); + static_cast(dw - 1) / (sw - 1)); } - x += dst->m_x; + x += dx; break; } return dst; } +void +CServer::closeClients(const CConfig& config) +{ + CThreadList threads; + { + CLock lock(&m_mutex); + + // get the set of clients that are connected but are being + // dropped from the configuration (or who's canonical name + // is changing) and tell them to disconnect. note that + // since m_clientThreads doesn't include a thread for the + // primary client we will not close it. + for (CClientThreadList::iterator + index = m_clientThreads.begin(); + index != m_clientThreads.end(); ) { + const CString& name = index->first; + if (!config.isCanonicalName(name)) { + // save the thread and remove it from m_clientThreads + threads.push_back(index->second); + m_clientThreads.erase(index++); + + // lookup IClient with name + CClientList::const_iterator index2 = m_clients.find(name); + assert(index2 != m_clients.end()); + + // close that client + assert(index2->second != m_primaryClient); + index2->second->close(); + } + else { + ++index; + } + } + } + + // wait a moment to allow each client to close its connection + // before we close it (to avoid having our socket enter TIME_WAIT). + if (threads.size() > 0) { + CThread::sleep(1.0); + } + + // cancel the old client threads + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + index->cancel(); + } + + // wait for old client threads to terminate. we must not hold + // the lock while we do this so those threads can finish any + // calls to this object. + for (CThreadList::iterator index = threads.begin(); + index != threads.end(); ++index) { + index->wait(); + } + + // clean up thread list + reapThreads(); +} + void CServer::startThread(IJob* job) { CLock lock(&m_mutex); + + // reap completed threads doReapThreads(m_threads); - CThread* thread = new CThread(job); - m_threads.push_back(thread); - log((CLOG_DEBUG1 "started thread %p", thread)); + + // add new thread to list. use the job as user data for logging. + m_threads.push_back(CThread(job, job)); + log((CLOG_DEBUG1 "started thread %p", m_threads.back().getUserData())); } void @@ -1079,6 +1064,12 @@ CServer::stopThreads(double timeout) { log((CLOG_DEBUG1 "stopping threads")); + // close all clients (except the primary) + { + CConfig emptyConfig; + closeClients(emptyConfig); + } + // swap thread list so nobody can mess with it CThreadList threads; { @@ -1089,8 +1080,7 @@ CServer::stopThreads(double timeout) // cancel every thread for (CThreadList::iterator index = threads.begin(); index != threads.end(); ++index) { - CThread* thread = *index; - thread->cancel(); + index->cancel(); } // now wait for the threads @@ -1103,9 +1093,7 @@ CServer::stopThreads(double timeout) // delete remaining threads for (CThreadList::iterator index = threads.begin(); index != threads.end(); ++index) { - CThread* thread = *index; - log((CLOG_DEBUG1 "reaped running thread %p", thread)); - delete thread; + log((CLOG_DEBUG1 "reaped running thread %p", index->getUserData())); } log((CLOG_DEBUG1 "stopped threads")); @@ -1123,12 +1111,10 @@ CServer::doReapThreads(CThreadList& threads) { for (CThreadList::iterator index = threads.begin(); index != threads.end(); ) { - CThread* thread = *index; - if (thread->wait(0.0)) { + if (index->wait(0.0)) { // thread terminated + log((CLOG_DEBUG1 "reaped thread %p", index->getUserData())); index = threads.erase(index); - log((CLOG_DEBUG1 "reaped thread %p", thread)); - delete thread; } else { // thread is running @@ -1182,7 +1168,7 @@ CServer::acceptClients(void*) // start handshake thread startThread(new TMethodJob( - this, &CServer::handshakeClient, socket)); + this, &CServer::runClient, socket)); } } catch (XBase& e) { @@ -1192,147 +1178,212 @@ CServer::acceptClients(void*) } void -CServer::handshakeClient(void* vsocket) +CServer::runClient(void* vsocket) { - log((CLOG_DEBUG1 "negotiating with new client")); - // get the socket pointer from the argument assert(vsocket != NULL); std::auto_ptr socket(reinterpret_cast(vsocket)); + // create proxy + CClientProxy* proxy = handshakeClient(socket.get()); + if (proxy == NULL) { + return; + } + + // add the connection + try { + addConnection(proxy); + + // save this client's thread + CLock lock(&m_mutex); + m_clientThreads.insert(std::make_pair(proxy->getName(), + CThread::getCurrentThread())); + } + catch (XDuplicateClient& e) { + // client has duplicate name + log((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); + delete proxy; + return; + } + catch (XUnknownClient& e) { + // client has unknown name + log((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); + delete proxy; + return; + } + catch (...) { + delete proxy; + throw; + } + + // activate screen saver on new client if active on the primary screen + { + CLock lock(&m_mutex); + if (m_activeSaver != NULL) { + proxy->screenSaver(true); + } + } + + // handle client messages + try { + log((CLOG_NOTE "client \"%s\" has connected", proxy->getName().c_str())); + proxy->run(); + } + catch (XBadClient&) { + // client not behaving + // FIXME -- could print network address if socket had suitable method + log((CLOG_WARN "protocol error from client \"%s\"", proxy->getName().c_str())); + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); + } + catch (XBase& e) { + // misc error + log((CLOG_WARN "error communicating with client \"%s\": %s", proxy->getName().c_str(), e.what())); + // FIXME -- could print network address if socket had suitable method + } + catch (...) { + // run() was probably cancelled + removeConnection(proxy->getName()); + throw; + } + + // remove the connection + removeConnection(proxy->getName()); +} + +CClientProxy* +CServer::handshakeClient(IDataSocket* socket) +{ + log((CLOG_DEBUG1 "negotiating with new client")); + + // get the input and output streams + IInputStream* input = socket->getInputStream(); + IOutputStream* output = socket->getOutputStream(); + bool own = false; + + // attach the encryption layer + if (m_securityFactory != NULL) { +/* FIXME -- implement ISecurityFactory + input = m_securityFactory->createInputFilter(input, own); + output = m_securityFactory->createOutputFilter(output, own); + own = true; +*/ + } + + // attach the packetizing filters + input = new CInputPacketStream(input, own); + output = new COutputPacketStream(output, own); + + CClientProxy* proxy = NULL; CString name(""); try { - // get the input and output streams - IInputStream* srcInput = socket->getInputStream(); - IOutputStream* srcOutput = socket->getOutputStream(); - std::auto_ptr input; - std::auto_ptr output; + // give the client a limited time to complete the handshake + CTimerThread timer(30.0); - // attach the encryption layer - bool own = false; - if (m_securityFactory != NULL) { -/* FIXME -- implement ISecurityFactory - input.reset(m_securityFactory->createInputFilter(srcInput, own)); - output.reset(m_securityFactory->createOutputFilter(srcOutput, own)); - srcInput = input.get(); - srcOutput = output.get(); - own = true; -*/ - } + // limit the maximum length of the hello +// FIXME -- should be in protocol types; may become obsolete anyway + static const UInt32 maxHelloLen = 1024; - // attach the packetizing filters - assign(input, new CInputPacketStream(srcInput, own), IInputStream); - assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); - - bool connected = false; - std::auto_ptr protocol; - try { - { - // give the client a limited time to complete the handshake - CTimerThread timer(30.0); - - // limit the maximum length of the hello - static const UInt32 maxHelloLen = 1024; - - // say hello - log((CLOG_DEBUG1 "saying hello")); - CProtocolUtil::writef(output.get(), "Synergy%2i%2i", + // say hello + log((CLOG_DEBUG1 "saying hello")); + CProtocolUtil::writef(output, "Synergy%2i%2i", kProtocolMajorVersion, kProtocolMinorVersion); - output->flush(); + output->flush(); - // wait for the reply - log((CLOG_DEBUG1 "waiting for hello reply")); - UInt32 n = input->getSize(); - if (n > maxHelloLen) { - throw XBadClient(); - } + // wait for the reply + log((CLOG_DEBUG1 "waiting for hello reply")); + UInt32 n = input->getSize(); + if (n > maxHelloLen) { + throw XBadClient(); + } - // get and parse the reply to hello - SInt16 major, minor; - try { - log((CLOG_DEBUG1 "parsing hello reply")); - CProtocolUtil::readf(input.get(), "Synergy%2i%2i%s", + // get and parse the reply to hello + SInt16 major, minor; + try { + log((CLOG_DEBUG1 "parsing hello reply")); + CProtocolUtil::readf(input, "Synergy%2i%2i%s", &major, &minor, &name); - } - catch (XIO&) { - throw XBadClient(); - } - - // convert name to canonical form (if any) - if (m_config.isScreen(name)) { - name = m_config.getCanonicalName(name); - } - - // create a protocol interpreter for the version - log((CLOG_DEBUG1 "creating interpreter for client \"%s\" version %d.%d", name.c_str(), major, minor)); - assign(protocol, CServerProtocol::create(major, minor, - this, name, input.get(), output.get()), - IServerProtocol); - - // client is now pending - addConnection(name, protocol.get()); - connected = true; - - // ask and wait for the client's info - log((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str())); - protocol->queryInfo(); - - // now connected; client no longer subject to timeout. - } - - // activate screen saver on new client if active on the - // primary screen - if (m_primary->isScreenSaverActive()) { - protocol->sendScreenSaver(true); - } - - // handle messages from client. returns when the client - // disconnects. - log((CLOG_NOTE "client \"%s\" has connected", name.c_str())); - protocol->run(); } - catch (XDuplicateClient& e) { - // client has duplicate name - log((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); - CProtocolUtil::writef(output.get(), kMsgEBusy); - } - catch (XUnknownClient& e) { - // client has unknown name - log((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); - CProtocolUtil::writef(output.get(), kMsgEUnknown); - } - catch (XIncompatibleClient& e) { - // client is incompatible - // FIXME -- could print network address if socket had suitable method - log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); - CProtocolUtil::writef(output.get(), kMsgEIncompatible, - kProtocolMajorVersion, kProtocolMinorVersion); - } - catch (XBadClient&) { - // client not behaving - // FIXME -- could print network address if socket had suitable method - log((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); - CProtocolUtil::writef(output.get(), kMsgEBad); - } - catch (...) { - if (connected) { - removeConnection(name); - } - throw; + catch (XIO&) { + throw XBadClient(); } - // flush any pending output - output.get()->flush(); - if (connected) { - removeConnection(name); + // disallow invalid version numbers + if (major < 0 || minor < 0) { + throw XIncompatibleClient(major, minor); } + + // disallow connection from test versions to release versions + if (major == 0 && kProtocolMajorVersion != 0) { + throw XIncompatibleClient(major, minor); + } + + // hangup (with error) if version isn't supported + if (major > kProtocolMajorVersion || + (major == kProtocolMajorVersion && minor > kProtocolMinorVersion)) { + throw XIncompatibleClient(major, minor); + } + + // convert name to canonical form (if any) + if (m_config.isScreen(name)) { + name = m_config.getCanonicalName(name); + } + + // create client proxy for highest version supported by the client + log((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor)); + proxy = new CClientProxy1_0(this, name, input, output); + + // negotiate + // FIXME + + // ask and wait for the client's info + log((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str())); + proxy->open(); + + return proxy; + } + catch (XIncompatibleClient& e) { + // client is incompatible + // FIXME -- could print network address if socket had suitable method + log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); + CProtocolUtil::writef(output, kMsgEIncompatible, + kProtocolMajorVersion, kProtocolMinorVersion); + } + catch (XBadClient&) { + // client not behaving + // FIXME -- could print network address if socket had suitable method + log((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); + CProtocolUtil::writef(output, kMsgEBad); } catch (XBase& e) { // misc error log((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); // FIXME -- could print network address if socket had suitable method } + catch (...) { + // probably timed out + if (proxy != NULL) { + delete proxy; + } + else { + delete input; + delete output; + } + throw; + } + + // failed + if (proxy != NULL) { + delete proxy; + } + else { + delete input; + delete output; + } + + return NULL; } void @@ -1433,178 +1484,98 @@ CServer::processHTTPRequest(void* vsocket) } } -void -CServer::clearGotClipboard(ClipboardID id) -{ - for (CScreenList::const_iterator index = m_screens.begin(); - index != m_screens.end(); ++index) { - index->second->m_gotClipboard[id] = false; - } -} - -void -CServer::sendClipboard(ClipboardID id) -{ - // do nothing if clipboard was already sent - if (!m_active->m_gotClipboard[id]) { - CClipboardInfo& clipboard = m_clipboards[id]; - if (clipboard.m_clipboardReady) { - // send it - if (m_active->m_protocol == NULL) { - m_primary->setClipboard(id, &clipboard.m_clipboard); - } - else { - m_active->m_protocol->sendClipboard(id, - clipboard.m_clipboardData); - } - - // clipboard has been sent - m_active->m_gotClipboard[id] = true; - } - } -} - -void -CServer::updatePrimaryClipboard(ClipboardID id) -{ - CClipboardInfo& clipboard = m_clipboards[id]; - - // if leaving primary and the primary owns the clipboard - // then update it. - if (clipboard.m_clipboardOwner == m_primaryInfo->m_name) { - assert(clipboard.m_clipboardReady == true); - - // save clipboard time - IClipboard::Time time = clipboard.m_clipboard.getTime(); - - // update - m_primary->getClipboard(id, &clipboard.m_clipboard); - - // if clipboard changed then other screens have an - // out-of-date clipboard. - if (time != clipboard.m_clipboard.getTime()) { - log((CLOG_DEBUG "clipboard %d time changed (%08x to %08x)", id, time, clipboard.m_clipboard.getTime())); - - // marshall data - CString newData = clipboard.m_clipboard.marshall(); - - // compare old and new data. if identical then the clipboard - // hasn't really changed. - if (newData != clipboard.m_clipboardData) { - log((CLOG_DEBUG "clipboard %d changed", id)); - clipboard.m_clipboardData = newData; - clearGotClipboard(id); - } - else { - log((CLOG_DEBUG "clipboard %d unchanged", id)); - } - m_primaryInfo->m_gotClipboard[id] = true; - } - } -} - -// FIXME -- use factory to create screen -#if WINDOWS_LIKE -#include "CMSWindowsPrimaryScreen.h" -#elif UNIX_LIKE -#include "CXWindowsPrimaryScreen.h" -#endif void CServer::openPrimaryScreen() { - assert(m_primary == NULL); + assert(m_primaryClient == NULL); // reset sequence number m_seqNum = 0; - CString primary = m_config.getCanonicalName(m_name); + // canonicalize the primary screen name + CString primary = m_config.getCanonicalName(getPrimaryScreenName()); if (primary.empty()) { - throw XUnknownClient(m_name); + throw XUnknownClient(getPrimaryScreenName()); } - try { - // add connection - m_active = addConnection(primary, NULL); - m_primaryInfo = m_active; - // open screen - log((CLOG_DEBUG1 "creating primary screen")); -#if WINDOWS_LIKE - m_primary = new CMSWindowsPrimaryScreen; -#elif UNIX_LIKE - m_primary = new CXWindowsPrimaryScreen; -#endif + // clear clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + CClipboardInfo& clipboard = m_clipboards[id]; + clipboard.m_clipboardOwner = primary; + clipboard.m_clipboardSeqNum = m_seqNum; + if (clipboard.m_clipboard.open(0)) { + clipboard.m_clipboard.empty(); + clipboard.m_clipboard.close(); + } + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + } + + // create the primary client and open it + try { + m_primaryClient = new CPrimaryClient(this, primary); + + // add connection + addConnection(m_primaryClient); + m_active = m_primaryClient; + + // open the screen log((CLOG_DEBUG1 "opening primary screen")); - m_primary->open(this); + m_primaryClient->open(); } catch (...) { - if (m_primary != NULL) { + if (m_active != NULL) { removeConnection(primary); - delete m_primary; } - m_primary = NULL; - m_primaryInfo = NULL; - m_active = NULL; + else { + delete m_primaryClient; + } + m_active = NULL; + m_primaryClient = NULL; throw; } - - // set the clipboard owner to the primary screen and then get the - // current clipboard data. - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - CClipboardInfo& clipboard = m_clipboards[id]; - m_primary->getClipboard(id, &clipboard.m_clipboard); - clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); - clipboard.m_clipboardReady = true; - clipboard.m_clipboardOwner = m_active->m_name; - } } void CServer::closePrimaryScreen() { - assert(m_primary != NULL); - - // remove connection - CString primary = m_config.getCanonicalName(m_name); - removeConnection(primary); + assert(m_primaryClient != NULL); // close the primary screen try { log((CLOG_DEBUG1 "closing primary screen")); - m_primary->close(); + m_primaryClient->close(); } catch (...) { // ignore } - // clean up - log((CLOG_DEBUG1 "destroying primary screen")); - delete m_primary; - m_primary = NULL; + // remove connection + removeConnection(m_primaryClient->getName()); + m_primaryClient = NULL; } -CServer::CScreenInfo* -CServer::addConnection(const CString& name, IServerProtocol* protocol) +void +CServer::addConnection(IClient* client) { - log((CLOG_DEBUG "adding connection \"%s\"", name.c_str())); + assert(client != NULL); + + log((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str())); CLock lock(&m_mutex); // name must be in our configuration - if (!m_config.isScreen(name)) { - throw XUnknownClient(name); + if (!m_config.isScreen(client->getName())) { + throw XUnknownClient(client->getName()); } // can only have one screen with a given name at any given time - if (m_screens.count(name) != 0) { - throw XDuplicateClient(name); + if (m_clients.count(client->getName()) != 0) { + throw XDuplicateClient(client->getName()); } // save screen info - CScreenInfo* newScreen = new CScreenInfo(name, protocol); - m_screens.insert(std::make_pair(name, newScreen)); - log((CLOG_DEBUG "added connection \"%s\"", name.c_str())); - - return newScreen; + m_clients.insert(std::make_pair(client->getName(), client)); + log((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str())); } void @@ -1613,28 +1584,27 @@ CServer::removeConnection(const CString& name) log((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); CLock lock(&m_mutex); - // find screen info - CScreenList::iterator index = m_screens.find(name); - assert(index != m_screens.end()); + // find client + CClientList::iterator index = m_clients.find(name); + assert(index != m_clients.end()); // if this is active screen then we have to jump off of it - CScreenInfo* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; - if (active == index->second && active != m_primaryInfo) { + IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; + if (active == index->second && active != m_primaryClient) { // record new position (center of primary screen) -// FIXME -- should have separate "center" pixel reported by screen - m_x = m_primaryInfo->m_x + (m_primaryInfo->m_w >> 1); - m_y = m_primaryInfo->m_y + (m_primaryInfo->m_h >> 1); + m_primaryClient->getCenter(m_x, m_y); // don't notify active screen since it probably already disconnected - log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y)); + log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); // cut over - m_active = m_primaryInfo; + m_active = m_primaryClient; // enter new screen (unless we already have because of the // screen saver) if (m_activeSaver == NULL) { - m_primary->enter(m_x, m_y, false); + m_primaryClient->enter(m_x, m_y, m_seqNum, + m_primaryClient->getToggleMask(), false); } } @@ -1645,35 +1615,12 @@ CServer::removeConnection(const CString& name) m_activeSaver = NULL; } - // done with screen info + // done with client delete index->second; - m_screens.erase(index); -} + m_clients.erase(index); - -// -// CServer::CScreenInfo -// - -CServer::CScreenInfo::CScreenInfo(const CString& name, - IServerProtocol* protocol) : - m_thread(CThread::getCurrentThread()), - m_name(name), - m_protocol(protocol), - m_ready(false), - m_x(0), - m_y(0), - m_w(0), - m_h(0), - m_zoneSize(0) -{ - for (ClipboardID id = 0; id < kClipboardEnd; ++id) - m_gotClipboard[id] = false; -} - -CServer::CScreenInfo::~CScreenInfo() -{ - // do nothing + // remove any thread for this client + m_clientThreads.erase(name); } @@ -1685,8 +1632,7 @@ CServer::CClipboardInfo::CClipboardInfo() : m_clipboard(), m_clipboardData(), m_clipboardOwner(), - m_clipboardSeqNum(0), - m_clipboardReady(false) + m_clipboardSeqNum(0) { // do nothing } diff --git a/server/CServer.h b/server/CServer.h index ed344095..0baf0696 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -1,6 +1,7 @@ #ifndef CSERVER_H #define CSERVER_H +#include "IServer.h" #include "CConfig.h" #include "CClipboard.h" #include "ClipboardTypes.h" @@ -13,14 +14,17 @@ #include "stdlist.h" #include "stdmap.h" +class CClientProxy; +class CHTTPServer; +class CPrimaryClient; class CThread; +class IClient; +class IDataSocket; class IServerProtocol; class ISocketFactory; class ISecurityFactory; -class IPrimaryScreen; -class CHTTPServer; -class CServer { +class CServer : public IServer { public: CServer(const CString& serverName); ~CServer(); @@ -38,50 +42,12 @@ public: // after a successful open(). void quit(); - // tell the server to shutdown. this is called in an emergency - // when we need to tell the server that we cannot continue. the - // server will attempt to clean up. - void shutdown(); - // update screen map. returns true iff the new configuration was // accepted. bool setConfig(const CConfig&); - // handle events on server's screen. onMouseMovePrimary() returns - // true iff the mouse enters a jump zone and jumps. - void onKeyDown(KeyID, KeyModifierMask); - void onKeyUp(KeyID, KeyModifierMask); - void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); - void onMouseDown(ButtonID); - void onMouseUp(ButtonID); - bool onMouseMovePrimary(SInt32 x, SInt32 y); - void onMouseMoveSecondary(SInt32 dx, SInt32 dy); - void onMouseWheel(SInt32 delta); - void grabClipboard(ClipboardID); - void onScreenSaver(bool activated); - - // handle updates from primary - void setInfo(SInt32 xScreen, SInt32 yScreen, - SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse); - - // handle messages from clients - void setInfo(const CString& clientName, - SInt32 xScreen, SInt32 yScreen, - SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse); - void grabClipboard(ClipboardID, - UInt32 seqNum, const CString& clientName); - void setClipboard(ClipboardID, - UInt32 seqNum, const CString& data); - // accessors - // returns true if the mouse should be locked to the current screen - bool isLockedToScreen() const; - // get the current screen map void getConfig(CConfig*) const; @@ -91,64 +57,58 @@ public: // get the sides of the primary screen that have neighbors UInt32 getActivePrimarySides() const; + // IServer overrides + virtual void onError(); + virtual void onInfoChanged(const CString& clientName); + virtual bool onGrabClipboard(ClipboardID, + UInt32 seqNum, const CString& clientName); + virtual void onClipboardChanged(ClipboardID, + UInt32 seqNum, const CString& data); + virtual void onKeyDown(KeyID, KeyModifierMask); + virtual void onKeyUp(KeyID, KeyModifierMask); + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void onMouseDown(ButtonID); + virtual void onMouseUp(ButtonID); + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); + virtual void onMouseWheel(SInt32 delta); + virtual void onGrabClipboard(ClipboardID); + virtual void onScreenSaver(bool activated); + protected: bool onCommandKey(KeyID, KeyModifierMask, bool down); private: - typedef std::list CThreadList; - - class CScreenInfo { - public: - CScreenInfo(const CString& name, IServerProtocol*); - ~CScreenInfo(); - - public: - // the thread handling this screen's connection. used when - // forcing a screen to disconnect. - CThread m_thread; - CString m_name; - IServerProtocol* m_protocol; - bool m_ready; - - // screen shape and jump zone size - SInt32 m_x, m_y; - SInt32 m_w, m_h; - SInt32 m_zoneSize; - - bool m_gotClipboard[kClipboardEnd]; - }; + typedef std::list CThreadList; // handle mouse motion bool onMouseMovePrimaryNoLock(SInt32 x, SInt32 y); void onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy); - // update screen info - void setInfoNoLock(const CString& screenName, - SInt32 xScreen, SInt32 yScreen, - SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse); - // grab the clipboard - void grabClipboardNoLock(ClipboardID, + bool grabClipboardNoLock(ClipboardID, UInt32 seqNum, const CString& clientName); + // set the clipboard + void onClipboardChangedNoLock(ClipboardID, + UInt32 seqNum, const CString& data); + // returns true iff mouse should be locked to the current screen bool isLockedToScreenNoLock() const; // change the active screen - void switchScreen(CScreenInfo*, + void switchScreen(IClient*, SInt32 x, SInt32 y, bool forScreenSaver); // lookup neighboring screen - CScreenInfo* getNeighbor(CScreenInfo*, CConfig::EDirection) const; + IClient* getNeighbor(IClient*, CConfig::EDirection) const; // lookup neighboring screen. given a position relative to the // source screen, find the screen we should move onto and where. // if the position is sufficiently far from the source then we // cross multiple screens. if there is no suitable screen then // return NULL and x,y are not modified. - CScreenInfo* getNeighbor(CScreenInfo*, + IClient* getNeighbor(IClient*, CConfig::EDirection, SInt32& x, SInt32& y) const; @@ -156,15 +116,13 @@ private: void openPrimaryScreen(); void closePrimaryScreen(); - // clear gotClipboard flags in all screens - void clearGotClipboard(ClipboardID); - - // send clipboard to the active screen if it doesn't already have it - void sendClipboard(ClipboardID); - // update the clipboard if owned by the primary screen void updatePrimaryClipboard(ClipboardID); + // close all clients that are *not* in config, not including the + // primary client. + void closeClients(const CConfig& config); + // start a thread, adding it to the list of threads void startThread(IJob* adopted); @@ -180,8 +138,9 @@ private: // thread method to accept incoming client connections void acceptClients(void*); - // thread method to do startup handshake with client - void handshakeClient(void*); + // thread method to do client interaction + void runClient(void*); + CClientProxy* handshakeClient(IDataSocket*); // thread method to accept incoming HTTP connections void acceptHTTPClients(void*); @@ -190,11 +149,10 @@ private: void processHTTPRequest(void*); // connection list maintenance - CScreenInfo* addConnection(const CString& name, IServerProtocol*); + void addConnection(IClient*); void removeConnection(const CString& name); private: - typedef std::map CScreenList; class CClipboardInfo { public: CClipboardInfo(); @@ -204,7 +162,6 @@ private: CString m_clipboardData; CString m_clipboardOwner; UInt32 m_clipboardSeqNum; - bool m_clipboardReady; }; CMutex m_mutex; @@ -212,6 +169,7 @@ private: // the name of the primary screen CString m_name; + // how long to wait to bind our socket until we give up double m_bindTimeout; ISocketFactory* m_socketFactory; @@ -221,10 +179,21 @@ private: CThreadList m_threads; // the screens - IPrimaryScreen* m_primary; - CScreenList m_screens; - CScreenInfo* m_active; - CScreenInfo* m_primaryInfo; + typedef std::map CClientList; + typedef std::map CClientThreadList; + + // all clients indexed by name + CClientList m_clients; + + // run thread of all secondary screen clients. does not include the + // primary screen's run thread. + CClientThreadList m_clientThreads; + + // the primary screen client + CPrimaryClient* m_primaryClient; + + // the client with focus + IClient* m_active; // the sequence number of enter messages UInt32 m_seqNum; @@ -239,7 +208,7 @@ private: CClipboardInfo m_clipboards[kClipboardEnd]; // state saved when screen saver activates - CScreenInfo* m_activeSaver; + IClient* m_activeSaver; SInt32 m_xSaver, m_ySaver; // HTTP request processing stuff diff --git a/server/CServerProtocol.cpp b/server/CServerProtocol.cpp deleted file mode 100644 index 112db9d7..00000000 --- a/server/CServerProtocol.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "CServerProtocol.h" -#include "CServerProtocol1_0.h" -#include "ProtocolTypes.h" -#include "XSynergy.h" -#include "IOutputStream.h" -#include - -// -// CServerProtocol -// - -CServerProtocol::CServerProtocol(CServer* server, const CString& client, - IInputStream* input, IOutputStream* output) : - m_server(server), - m_client(client), - m_input(input), - m_output(output) -{ - assert(m_server != NULL); - assert(m_input != NULL); - assert(m_output != NULL); -} - -CServerProtocol::~CServerProtocol() -{ - // do nothing -} - -CServer* -CServerProtocol::getServer() const -{ - return m_server; -} - -CString -CServerProtocol::getClient() const -{ - return m_client; -} - -IInputStream* -CServerProtocol::getInputStream() const -{ - return m_input; -} - -IOutputStream* -CServerProtocol::getOutputStream() const -{ - return m_output; -} - -IServerProtocol* -CServerProtocol::create(SInt32 major, SInt32 minor, - CServer* server, const CString& client, - IInputStream* input, IOutputStream* output) -{ - // disallow invalid version numbers - if (major < 0 || minor < 0) { - throw XIncompatibleClient(major, minor); - } - - // disallow connection from test versions to release versions - if (major == 0 && kProtocolMajorVersion != 0) { - throw XIncompatibleClient(major, minor); - } - - // hangup (with error) if version isn't supported - if (major > kProtocolMajorVersion || - (major == kProtocolMajorVersion && minor > kProtocolMinorVersion)) { - throw XIncompatibleClient(major, minor); - } - - // create highest version protocol object not higher than the - // given version. - return new CServerProtocol1_0(server, client, input, output); -} diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h deleted file mode 100644 index 311e5d0f..00000000 --- a/server/CServerProtocol.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef CSERVERPROTOCOL_H -#define CSERVERPROTOCOL_H - -#include "IServerProtocol.h" - -class CServer; -class IInputStream; -class IOutputStream; - -class CServerProtocol : public IServerProtocol { -public: - CServerProtocol(CServer*, const CString& clientName, - IInputStream*, IOutputStream*); - ~CServerProtocol(); - - // manipulators - - // accessors - - virtual CServer* getServer() const; - virtual CString getClient() const; - virtual IInputStream* getInputStream() const; - virtual IOutputStream* getOutputStream() const; - - static IServerProtocol* create(SInt32 major, SInt32 minor, - CServer*, const CString& clientName, - IInputStream*, IOutputStream*); - - // IServerProtocol overrides - virtual void run() = 0; - virtual void queryInfo() = 0; - virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask) = 0; - virtual void sendLeave() = 0; - virtual void sendClipboard(ClipboardID, const CString&) = 0; - virtual void sendGrabClipboard(ClipboardID) = 0; - virtual void sendScreenSaver(bool on) = 0; - virtual void sendInfoAcknowledgment() = 0; - virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; - virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; - virtual void sendMouseDown(ButtonID) = 0; - virtual void sendMouseUp(ButtonID) = 0; - virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) = 0; - virtual void sendMouseWheel(SInt32 delta) = 0; - -protected: - //IServerProtocol overrides - virtual void recvInfo() = 0; - virtual void recvClipboard() = 0; - virtual void recvGrabClipboard() = 0; - -private: - CServer* m_server; - CString m_client; - IInputStream* m_input; - IOutputStream* m_output; -}; - -#endif diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp deleted file mode 100644 index 7c673415..00000000 --- a/server/CServerProtocol1_0.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "CServerProtocol1_0.h" -#include "CServer.h" -#include "CClipboard.h" -#include "CProtocolUtil.h" -#include "ProtocolTypes.h" -#include "XSynergy.h" -#include "IInputStream.h" -#include "IOutputStream.h" -#include "CThread.h" -#include "CLog.h" -#include "CStopwatch.h" -#include - -// -// CServerProtocol1_0 -// - -CServerProtocol1_0::CServerProtocol1_0(CServer* server, const CString& client, - IInputStream* input, IOutputStream* output) : - CServerProtocol(server, client, input, output) -{ - // do nothing -} - -CServerProtocol1_0::~CServerProtocol1_0() -{ - // do nothing -} - -void -CServerProtocol1_0::run() -{ - // handle messages until the client hangs up or stops sending heartbeats - CStopwatch heartTimer; - for (;;) { - CThread::testCancel(); - - // wait for a message - UInt8 code[4]; - UInt32 n = getInputStream()->read(code, 4, kHeartRate); - CThread::testCancel(); - - // check if client hungup - if (n == 0) { - log((CLOG_NOTE "client \"%s\" disconnected", getClient().c_str())); - return; - } - - // check if client has stopped sending heartbeats - if (n == (UInt32)-1) { -/* FIXME -- disabled to avoid masking bugs - if (heartTimer.getTime() > kHeartDeath) { - log((CLOG_NOTE "client \"%s\" is dead", getClient().c_str())); - return; - } -*/ - continue; - } - - // got a message so reset heartbeat monitor - heartTimer.reset(); - - // verify we got an entire code - if (n != 4) { - log((CLOG_ERR "incomplete message from \"%s\": %d bytes", getClient().c_str(), n)); - - // client sent an incomplete message - throw XBadClient(); - } - - // parse message - log((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getClient().c_str(), code[0], code[1], code[2], code[3])); - if (memcmp(code, kMsgDInfo, 4) == 0) { - recvInfo(); - } - else if (memcmp(code, kMsgCNoop, 4) == 0) { - // discard no-ops - continue; - } - else if (memcmp(code, kMsgCClipboard, 4) == 0) { - recvGrabClipboard(); - } - else if (memcmp(code, kMsgDClipboard, 4) == 0) { - recvClipboard(); - } - // FIXME -- more message here - else { - log((CLOG_ERR "invalid message from client \"%s\"", getClient().c_str())); - - // unknown message - throw XBadClient(); - } - } -} - -void -CServerProtocol1_0::queryInfo() -{ - log((CLOG_DEBUG1 "querying client \"%s\" info", getClient().c_str())); - - // send request - CProtocolUtil::writef(getOutputStream(), kMsgQInfo); - - // wait for and verify reply - UInt8 code[4]; - for (;;) { - UInt32 n = getInputStream()->read(code, 4, -1.0); - if (n == 4) { - if (memcmp(code, kMsgCNoop, 4) == 0) { - // discard heartbeats - continue; - } - if (memcmp(code, kMsgDInfo, 4) == 0) { - break; - } - } - throw XBadClient(); - } - - // handle reply - recvInfo(); -} - -void -CServerProtocol1_0::sendClose() -{ - log((CLOG_DEBUG1 "send close to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCClose); - - // force the close to be sent before we return - getOutputStream()->flush(); -} - -void -CServerProtocol1_0::sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask) -{ - log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getClient().c_str(), xAbs, yAbs, seqNum, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgCEnter, - xAbs, yAbs, seqNum, mask); -} - -void -CServerProtocol1_0::sendLeave() -{ - log((CLOG_DEBUG1 "send leave to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCLeave); -} - -void -CServerProtocol1_0::sendClipboard(ClipboardID id, const CString& data) -{ - log((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getClient().c_str(), data.size())); - CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); -} - -void -CServerProtocol1_0::sendGrabClipboard(ClipboardID id) -{ - log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); -} - -void -CServerProtocol1_0::sendScreenSaver(bool on) -{ - log((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getClient().c_str(), on ? 1 : 0)); - CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); -} - -void -CServerProtocol1_0::sendInfoAcknowledgment() -{ - log((CLOG_DEBUG1 "send info ack to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCInfoAck); -} - -void -CServerProtocol1_0::sendKeyDown(KeyID key, KeyModifierMask mask) -{ - log((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); -} - -void -CServerProtocol1_0::sendKeyRepeat(KeyID key, KeyModifierMask mask, SInt32 count) -{ - log((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getClient().c_str(), key, mask, count)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); -} - -void -CServerProtocol1_0::sendKeyUp(KeyID key, KeyModifierMask mask) -{ - log((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); -} - -void -CServerProtocol1_0::sendMouseDown(ButtonID button) -{ - log((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getClient().c_str(), button)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); -} - -void -CServerProtocol1_0::sendMouseUp(ButtonID button) -{ - log((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getClient().c_str(), button)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); -} - -void -CServerProtocol1_0::sendMouseMove(SInt32 xAbs, SInt32 yAbs) -{ - log((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getClient().c_str(), xAbs, yAbs)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); -} - -void -CServerProtocol1_0::sendMouseWheel(SInt32 delta) -{ - log((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); -} - -void -CServerProtocol1_0::recvInfo() -{ - // parse the message - SInt16 x, y, w, h, zoneInfo, mx, my; - CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, - &x, &y, &w, &h, &zoneInfo, &mx, &my); - log((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getClient().c_str(), x, y, w, h, zoneInfo, mx, my)); - - // validate - if (w <= 0 || h <= 0 || zoneInfo < 0) { - throw XBadClient(); - } - if (mx < x || my < y || mx >= x + w || my >= y + h) { - throw XBadClient(); - } - - // tell server of change - getServer()->setInfo(getClient(), x, y, w, h, zoneInfo, mx, my); -} - -void -CServerProtocol1_0::recvClipboard() -{ - // parse message - ClipboardID id; - UInt32 seqNum; - CString data; - CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); - log((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getClient().c_str(), id, seqNum, data.size())); - - // validate - if (id >= kClipboardEnd) { - throw XBadClient(); - } - - // send update - getServer()->setClipboard(id, seqNum, data); -} - -void -CServerProtocol1_0::recvGrabClipboard() -{ - // parse message - ClipboardID id; - UInt32 seqNum; - CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); - log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getClient().c_str(), id, seqNum)); - - // validate - if (id >= kClipboardEnd) { - throw XBadClient(); - } - - // send update - getServer()->grabClipboard(id, seqNum, getClient()); -} diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h deleted file mode 100644 index 4e14dac7..00000000 --- a/server/CServerProtocol1_0.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CSERVERPROTOCOL1_0_H -#define CSERVERPROTOCOL1_0_H - -#include "CServerProtocol.h" - -class CServerProtocol1_0 : public CServerProtocol { -public: - CServerProtocol1_0(CServer*, const CString&, IInputStream*, IOutputStream*); - ~CServerProtocol1_0(); - - // manipulators - - // accessors - - // IServerProtocol overrides - virtual void run(); - virtual void queryInfo(); - virtual void sendClose(); - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask); - virtual void sendLeave(); - virtual void sendClipboard(ClipboardID, const CString&); - virtual void sendGrabClipboard(ClipboardID); - virtual void sendScreenSaver(bool on); - virtual void sendInfoAcknowledgment(); - virtual void sendKeyDown(KeyID, KeyModifierMask); - virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void sendKeyUp(KeyID, KeyModifierMask); - virtual void sendMouseDown(ButtonID); - virtual void sendMouseUp(ButtonID); - virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs); - virtual void sendMouseWheel(SInt32 delta); - -protected: - // IServerProtocol overrides - virtual void recvInfo(); - virtual void recvClipboard(); - virtual void recvGrabClipboard(); -}; - -#endif diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 0a9ecb36..5209a461 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -1,8 +1,9 @@ #include "CXWindowsPrimaryScreen.h" -#include "CServer.h" +#include "IPrimaryReceiver.h" #include "CXWindowsClipboard.h" #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" +#include "CClipboard.h" #include "CThread.h" #include "CLog.h" #include "CStopwatch.h" @@ -19,8 +20,8 @@ // CXWindowsPrimaryScreen // -CXWindowsPrimaryScreen::CXWindowsPrimaryScreen() : - m_server(NULL), +CXWindowsPrimaryScreen::CXWindowsPrimaryScreen(IPrimaryReceiver* receiver) : + m_receiver(receiver), m_active(false), m_window(None) { @@ -65,7 +66,7 @@ CXWindowsPrimaryScreen::run() if (xevent.xclient.message_type == m_atomScreenSaver || xevent.xclient.format == 32) { // screen saver activation/deactivation event - m_server->onScreenSaver(xevent.xclient.data.l[0] != 0); + m_receiver->onScreenSaver(xevent.xclient.data.l[0] != 0); } break; @@ -75,12 +76,12 @@ CXWindowsPrimaryScreen::run() const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { - m_server->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_server->onKeyUp(key, mask | KeyModifierCapsLock); + m_receiver->onKeyUp(key, mask | KeyModifierCapsLock); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_server->onKeyUp(key, mask | KeyModifierNumLock); + m_receiver->onKeyUp(key, mask | KeyModifierNumLock); } } } @@ -110,12 +111,12 @@ CXWindowsPrimaryScreen::run() // no press event follows so it's a plain release log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_server->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_server->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); } - m_server->onKeyUp(key, mask); + m_receiver->onKeyUp(key, mask); } else { // found a press event following so it's a repeat. @@ -123,7 +124,7 @@ CXWindowsPrimaryScreen::run() // repeats but we'll just send a repeat of 1. // note that we discard the press event. log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - m_server->onKeyRepeat(key, mask, 1); + m_receiver->onKeyRepeat(key, mask, 1); } } } @@ -134,7 +135,7 @@ CXWindowsPrimaryScreen::run() log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_server->onMouseDown(button); + m_receiver->onMouseDown(button); } } break; @@ -144,15 +145,15 @@ CXWindowsPrimaryScreen::run() log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_server->onMouseUp(button); + m_receiver->onMouseUp(button); } else if (xevent.xbutton.button == 4) { // wheel forward (away from user) - m_server->onMouseWheel(120); + m_receiver->onMouseWheel(120); } else if (xevent.xbutton.button == 5) { // wheel backward (toward user) - m_server->onMouseWheel(-120); + m_receiver->onMouseWheel(-120); } } break; @@ -183,7 +184,7 @@ CXWindowsPrimaryScreen::run() } else if (!m_active) { // motion on primary screen - m_server->onMouseMovePrimary(m_x, m_y); + m_receiver->onMouseMovePrimary(m_x, m_y); } else { // motion on secondary screen. warp mouse back to @@ -214,7 +215,7 @@ CXWindowsPrimaryScreen::run() // warping to the primary screen's enter position, // effectively overriding it. if (x != 0 || y != 0) { - m_server->onMouseMoveSecondary(x, y); + m_receiver->onMouseMoveSecondary(x, y); } } } @@ -231,17 +232,11 @@ CXWindowsPrimaryScreen::stop() } void -CXWindowsPrimaryScreen::open(CServer* server) +CXWindowsPrimaryScreen::open() { - assert(m_server == NULL); - assert(server != NULL); - // open the display openDisplay(); - // set the server - m_server = server; - // check for peculiarities // FIXME -- may have to get these from some database m_numLockHalfDuplex = false; @@ -283,23 +278,18 @@ CXWindowsPrimaryScreen::open(CServer* server) m_yCenter = y + (h >> 1); // send screen info - m_server->setInfo(x, y, w, h, getJumpZoneSize(), m_x, m_y); + m_receiver->onInfoChanged(x, y, w, h, 1, m_x, m_y); } void CXWindowsPrimaryScreen::close() { - assert(m_server != NULL); - // stop being notified of screen saver activation/deactivation getScreenSaver()->setNotify(None); m_atomScreenSaver = None; // close the display closeDisplay(); - - // done with server - m_server = NULL; } void @@ -394,7 +384,7 @@ CXWindowsPrimaryScreen::leave() } void -CXWindowsPrimaryScreen::onConfigure() +CXWindowsPrimaryScreen::reconfigure() { // do nothing } @@ -475,19 +465,6 @@ CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) setDisplayClipboard(id, NULL); } -void -CXWindowsPrimaryScreen::getShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - getScreenShape(x, y, w, h); -} - -SInt32 -CXWindowsPrimaryScreen::getJumpZoneSize() const -{ - return 1; -} - void CXWindowsPrimaryScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const @@ -555,13 +532,6 @@ CXWindowsPrimaryScreen::isLockedToScreen() const return false; } -bool -CXWindowsPrimaryScreen::isScreenSaverActive() const -{ - CDisplayLock display(this); - return getScreenSaver()->isActive(); -} - void CXWindowsPrimaryScreen::onOpenDisplay(Display* display) { @@ -616,16 +586,14 @@ void CXWindowsPrimaryScreen::onUnexpectedClose() { // tell server to shutdown - if (m_server != NULL) { - m_server->shutdown(); - } + m_receiver->onError(); } void CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id) { // tell server that the clipboard was grabbed locally - m_server->grabClipboard(id); + m_receiver->onGrabClipboard(id); } void diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 8cabc11f..708d8376 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -5,28 +5,27 @@ #include "IPrimaryScreen.h" #include "MouseTypes.h" +class IPrimaryReceiver; + class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { public: - CXWindowsPrimaryScreen(); + CXWindowsPrimaryScreen(IPrimaryReceiver*); virtual ~CXWindowsPrimaryScreen(); // IPrimaryScreen overrides virtual void run(); virtual void stop(); - virtual void open(CServer*); + virtual void open(); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool); virtual bool leave(); - virtual void onConfigure(); + virtual void reconfigure(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); - virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; - virtual bool isScreenSaverActive() const; protected: // CXWindowsScreen overrides @@ -61,7 +60,7 @@ private: static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: - CServer* m_server; + IPrimaryReceiver* m_receiver; bool m_active; Window m_window; diff --git a/server/IPrimaryReceiver.h b/server/IPrimaryReceiver.h new file mode 100644 index 00000000..081a0c95 --- /dev/null +++ b/server/IPrimaryReceiver.h @@ -0,0 +1,46 @@ +#ifndef IPRIMARYRECEIVER_H +#define IPRIMARYRECEIVER_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CString.h" + +class IPrimaryReceiver : public IInterface { +public: + // manipulators + + // notify of serious error. this implies that the primary screen + // cannot continue to function. + virtual void onError() = 0; + + // notify of info change + virtual void onInfoChanged(SInt32 xScreen, SInt32 yScreen, + SInt32 wScreen, SInt32 hScreen, + SInt32 zoneSize, + SInt32 xMouse, SInt32 yMouse) = 0; + + // notify of clipboard grab. returns true if the grab was honored, + // false otherwise. + virtual bool onGrabClipboard(ClipboardID) = 0; + + // notify of new clipboard data. returns true if the clipboard data + // was accepted, false if the change was rejected. + virtual bool onClipboardChanged(ClipboardID, + const CString& data) = 0; + + // call to notify of events. onMouseMovePrimary() returns + // true iff the mouse enters a jump zone and jumps. + virtual void onKeyDown(KeyID, KeyModifierMask) = 0; + virtual void onKeyUp(KeyID, KeyModifierMask) = 0; + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void onMouseDown(ButtonID) = 0; + virtual void onMouseUp(ButtonID) = 0; + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; + virtual void onMouseWheel(SInt32 delta) = 0; + virtual void onScreenSaver(bool activated) = 0; +}; + +#endif diff --git a/server/IPrimaryScreen.h b/server/IPrimaryScreen.h index c68d0bad..62af410f 100644 --- a/server/IPrimaryScreen.h +++ b/server/IPrimaryScreen.h @@ -5,7 +5,6 @@ #include "KeyTypes.h" #include "ClipboardTypes.h" -class CServer; class IClipboard; class IPrimaryScreen : public IInterface { @@ -21,7 +20,8 @@ public: // cause run() to return virtual void stop() = 0; - // initialize the screen and start reporting events to the server. + // initialize the screen and start reporting events to the receiver + // (which is set through some interface of the derived class). // events should be reported no matter where on the screen they // occur but do not interfere with normal event dispatch. the // screen saver engaging should be reported as an event. if that @@ -29,10 +29,11 @@ public: // screen saver timer and should start the screen saver after // idling for an appropriate time. // - // open() must call server->setInfo() to notify the server of the - // primary screen's resolution and jump zone size. the mouse - // position is ignored and may be 0,0. - virtual void open(CServer* server) = 0; + // open() must call receiver->onInfoChanged() to notify of the + // primary screen's initial resolution and jump zone size. it + // must also call receiver->onClipboardChanged() for each + // clipboard that the primary screen has. + virtual void open() = 0; // close the screen. should restore the screen saver timer if it // was disabled. @@ -44,24 +45,30 @@ public: // call to leave() which preceeds it, however the screen should // assume an implicit call to enter() in the call to open(). // if warpCursor is false then do not warp the mouse. + // + // enter() must not call any receiver methods except onError(). virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool forScreenSaver) = 0; - // called when the user navigates off the primary screen. hide - // the cursor and grab exclusive access to the input devices. - // return true iff successful. + // called when the user navigates off the primary screen. hide the + // cursor and grab exclusive access to the input devices. return + // true iff successful. + // + // leave() must not call any receiver methods except onError(). virtual bool leave() = 0; // called when the configuration has changed. subclasses may need // to adjust things (like the jump zones) after the configuration // changes. - virtual void onConfigure() = 0; + virtual void reconfigure() = 0; // warp the cursor to the given position virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; // set the screen's clipboard contents. this is usually called // soon after an enter(). + // + // setClipboard() must not call any receiver methods except onError(). virtual void setClipboard(ClipboardID, const IClipboard*) = 0; // synergy should own the clipboard @@ -69,16 +76,9 @@ public: // accessors - // get the screen region - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const = 0; - - // get the size of jump zone - virtual SInt32 getJumpZoneSize() const = 0; - - // get the screen's clipboard contents. the implementation can - // and should avoid setting the clipboard object if the screen's - // clipboard hasn't changed. + // return the contents of the given clipboard. + // + // getClipboard() must not call any receiver methods except onError(). virtual void getClipboard(ClipboardID, IClipboard*) const = 0; // get the primary screen's current toggle modifier key state. @@ -90,9 +90,6 @@ public: // any other reason that the user should not be allowed to switch // screens. virtual bool isLockedToScreen() const = 0; - - // return true if the screen saver is activated - virtual bool isScreenSaverActive() const = 0; }; #endif diff --git a/server/IServerProtocol.h b/server/IServerProtocol.h deleted file mode 100644 index e329d0a4..00000000 --- a/server/IServerProtocol.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef ISERVERPROTOCOL_H -#define ISERVERPROTOCOL_H - -#include "IInterface.h" -#include "ClipboardTypes.h" -#include "KeyTypes.h" -#include "MouseTypes.h" -#include "CString.h" - -class IClipboard; - -class IServerProtocol : public IInterface { -public: - // manipulators - - // process messages from the client and insert the appropriate - // events into the server's event queue. return when the client - // disconnects. - virtual void run() = 0; - - // send client info query and process reply - virtual void queryInfo() = 0; - - // send various messages to client - virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask) = 0; - virtual void sendLeave() = 0; - virtual void sendClipboard(ClipboardID, const CString&) = 0; - virtual void sendGrabClipboard(ClipboardID) = 0; - virtual void sendScreenSaver(bool on) = 0; - virtual void sendInfoAcknowledgment() = 0; - virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; - virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void sendKeyUp(KeyID, KeyModifierMask) = 0; - virtual void sendMouseDown(ButtonID) = 0; - virtual void sendMouseUp(ButtonID) = 0; - virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) = 0; - virtual void sendMouseWheel(SInt32 delta) = 0; - - // accessors - -protected: - // manipulators - - virtual void recvInfo() = 0; - virtual void recvClipboard() = 0; - virtual void recvGrabClipboard() = 0; - - // accessors -}; - -#endif diff --git a/server/Makefile.am b/server/Makefile.am index 7c7af55f..02ec9040 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -4,20 +4,23 @@ DEPTH = .. bin_PROGRAMS = synergyd synergyd_SOURCES = \ + CClientProxy.cpp \ + CClientProxy1_0.cpp \ CConfig.cpp \ - CServerProtocol.cpp \ - CServerProtocol1_0.cpp \ - CXWindowsPrimaryScreen.cpp \ - CServer.cpp \ CHTTPServer.cpp \ + CPrimaryClient.cpp \ + CServer.cpp \ + CXWindowsPrimaryScreen.cpp \ server.cpp \ + CClientProxy.h \ + CClientProxy1_0.h \ CConfig.h \ CHTTPServer.h \ + CPrimaryClient.h \ CServer.h \ - CServerProtocol.h \ - CServerProtocol1_0.h \ - CSynergyHook.h \ CXWindowsPrimaryScreen.h \ + IPrimaryReceiver.h \ + IPrimaryScreen.h \ $(NULL) synergyd_LDADD = \ $(DEPTH)/platform/libplatform.a \ diff --git a/synergy/IClient.h b/synergy/IClient.h new file mode 100644 index 00000000..7a151457 --- /dev/null +++ b/synergy/IClient.h @@ -0,0 +1,91 @@ +#ifndef ICLIENT_H +#define ICLIENT_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CString.h" + +class IClient : public IInterface { +public: + // manipulators + + // open client + virtual void open() = 0; + + // service client + virtual void run() = 0; + + // close client + virtual void close() = 0; + + // enter the screen. the cursor should be warped to xAbs,yAbs. + // the client should record seqNum for future reporting of + // clipboard changes. mask is the expected toggle button state. + // screenSaver is true if the screen is being entered because + // the screen saver is starting. + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool screenSaver) = 0; + + // leave the screen. returns false if the user may not leave the + // client's screen. + virtual bool leave() = 0; + + // FIXME -- methods for setting clipboard. on proxy side these + // will set/reset the gotClipboard flag and send the clipboard + // when not gotClipboard is false. grabbing goes in here too. + // basically, figure out semantics of these methods. note that + // ISecondaryScreen wants an IClipboard* passed to set not a + // string; will that be a problem? + + // update the client's clipboard. this implies that the client's + // clipboard is now up to date. if the client's clipboard was + // already known to be up to date then this can do nothing. + virtual void setClipboard(ClipboardID, const CString&) = 0; + + // grab the client's clipboard. since this is called when another + // client takes ownership of the clipboard it implies that the + // client's clipboard is dirty. + virtual void grabClipboard(ClipboardID) = 0; + + // called to set the client's clipboard as dirty or clean + virtual void setClipboardDirty(ClipboardID, bool dirty) = 0; + + // handle input events + virtual void keyDown(KeyID, KeyModifierMask) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void keyUp(KeyID, KeyModifierMask) = 0; + virtual void mouseDown(ButtonID) = 0; + virtual void mouseUp(ButtonID) = 0; + // FIXME -- if server uses IClient as interface to primary screen + // (should this class be renamed?) then be careful of absolute/relative + // coordinates here; move on primary and move on secondary take + // different values. probably not relevant, though. + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void mouseWheel(SInt32 delta) = 0; + virtual void screenSaver(bool activate) = 0; + + // accessors + + // get the client's identifier + virtual CString getName() const = 0; + + // get the screen's shape + // FIXME -- may want center pixel too + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + + // get the center pixel + virtual void getCenter(SInt32& x, SInt32& y) const = 0; + + // get the size of jump zone + virtual SInt32 getJumpZoneSize() const = 0; + + // what about getClipboard()? don't really want proxy to ask for it; + // it's a push data model. screen info is cached in proxy so it's + // different. will need to keep onClipboardChanged() in CClient. +}; + +#endif diff --git a/synergy/IServer.h b/synergy/IServer.h new file mode 100644 index 00000000..c109f148 --- /dev/null +++ b/synergy/IServer.h @@ -0,0 +1,43 @@ +#ifndef ISERVER_H +#define ISERVER_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CString.h" + +class IServer : public IInterface { +public: + // manipulators + + // notify of serious error. this implies that the server should + // shutdown. + virtual void onError() = 0; + + // notify of client info change (maybe IClient should be named IScreen) + virtual void onInfoChanged(const CString& clientName) = 0; + + // notify of clipboard grab. returns true if the grab was honored, + // false otherwise. + virtual bool onGrabClipboard(ClipboardID, + UInt32 seqNum, const CString& clientName) = 0; + + // notify of new clipboard data + virtual void onClipboardChanged(ClipboardID, + UInt32 seqNum, const CString& data) = 0; + + // call to notify of events. onMouseMovePrimary() returns + // true iff the mouse enters a jump zone and jumps. + virtual void onKeyDown(KeyID, KeyModifierMask) = 0; + virtual void onKeyUp(KeyID, KeyModifierMask) = 0; + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void onMouseDown(ButtonID) = 0; + virtual void onMouseUp(ButtonID) = 0; + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; + virtual void onMouseWheel(SInt32 delta) = 0; + virtual void onScreenSaver(bool activated) = 0; +}; + +#endif diff --git a/synergy/Makefile.am b/synergy/Makefile.am index 2cb3df73..78a56b29 100644 --- a/synergy/Makefile.am +++ b/synergy/Makefile.am @@ -17,10 +17,11 @@ libsynergy_a_SOURCES = \ CProtocolUtil.h \ CTCPSocketFactory.h \ ClipboardTypes.h \ + IClient.h \ IClipboard.h \ IScreenSaver.h \ ISecondaryScreen.h \ - IServerProtocol.h \ + IServer.h \ ISocketFactory.h \ KeyTypes.h \ MouseTypes.h \ From f90076938b6418d4e39b03aa8e9c8c9eb6721945 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 10 Jul 2002 14:15:17 +0000 Subject: [PATCH 221/807] removed IPrimaryReceiver in favor of IServer, which required a few minor changes to support IPrimaryReciever's functionality. this does mean that the IPrimaryScreen class will be calling some methods with dummy arguments. those are documented in CPrimaryClient. --- server/CClientProxy1_0.cpp | 38 +++++++++++++--------- server/CClientProxy1_0.h | 6 ++-- server/CPrimaryClient.cpp | 37 ++++++++++----------- server/CPrimaryClient.h | 26 +++++++-------- server/CServer.cpp | 45 +++++++------------------- server/CServer.h | 17 ++-------- server/CXWindowsPrimaryScreen.cpp | 53 ++++++++++++++++++------------- server/CXWindowsPrimaryScreen.h | 6 ++-- server/IPrimaryReceiver.h | 46 --------------------------- server/Makefile.am | 1 - synergy/IServer.h | 11 ++++--- synergy/ProtocolTypes.h | 18 +++++++++++ 12 files changed, 126 insertions(+), 178 deletions(-) delete mode 100644 server/IPrimaryReceiver.h diff --git a/server/CClientProxy1_0.cpp b/server/CClientProxy1_0.cpp index b8139b7a..b1854d65 100644 --- a/server/CClientProxy1_0.cpp +++ b/server/CClientProxy1_0.cpp @@ -2,7 +2,6 @@ #include "CServer.h" #include "CClipboard.h" #include "CProtocolUtil.h" -#include "ProtocolTypes.h" #include "XSynergy.h" #include "IInputStream.h" #include "IOutputStream.h" @@ -245,25 +244,25 @@ void CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { CLock lock(&m_mutex); - x = m_x; - y = m_y; - w = m_w; - h = m_h; + x = m_info.m_x; + y = m_info.m_y; + w = m_info.m_w; + h = m_info.m_h; } void CClientProxy1_0::getCenter(SInt32& x, SInt32& y) const { CLock lock(&m_mutex); - x = m_cx; - y = m_cy; + x = m_info.m_mx; + y = m_info.m_my; } SInt32 CClientProxy1_0::getJumpZoneSize() const { CLock lock(&m_mutex); - return m_zoneSize; + return m_info.m_zoneSize; } void @@ -273,23 +272,32 @@ CClientProxy1_0::recvInfo(bool notify) CLock lock(&m_mutex); // parse the message + SInt16 x, y, w, h, zoneSize, mx, my; CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, - &m_x, &m_y, &m_w, &m_h, - &m_zoneSize, &m_cx, &m_cy); - log((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), m_x, m_y, m_w, m_h, m_zoneSize, m_cx, m_cy)); + &x, &y, &w, &h, &zoneSize, &mx, &my); + log((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my)); // validate - if (m_w <= 0 || m_h <= 0 || m_zoneSize < 0) { + if (w <= 0 || h <= 0 || zoneSize < 0) { throw XBadClient(); } - if (m_cx < m_x || m_cy < m_y || m_cx >= m_x + m_w || m_cy >= m_y + m_h) { + if (mx < x || my < y || mx >= x + w || my >= y + h) { throw XBadClient(); } + + // save + m_info.m_x = x; + m_info.m_y = y; + m_info.m_w = w; + m_info.m_h = h; + m_info.m_zoneSize = zoneSize; + m_info.m_mx = mx; + m_info.m_my = my; } // tell server of change if (notify) { - getServer()->onInfoChanged(getName()); + getServer()->onInfoChanged(getName(), m_info); } // acknowledge receipt @@ -333,5 +341,5 @@ CClientProxy1_0::recvGrabClipboard() // send update. this calls us back to reset our clipboard dirty flag // so don't hold a lock during the call. - getServer()->onGrabClipboard(id, seqNum, getName()); + getServer()->onGrabClipboard(getName(), id, seqNum); } diff --git a/server/CClientProxy1_0.h b/server/CClientProxy1_0.h index 4d43da64..fae25eb7 100644 --- a/server/CClientProxy1_0.h +++ b/server/CClientProxy1_0.h @@ -2,6 +2,7 @@ #define CCLIENTPROXY1_0_H #include "CClientProxy.h" +#include "ProtocolTypes.h" #include "CMutex.h" class CClientProxy1_0 : public CClientProxy { @@ -42,10 +43,7 @@ private: private: CMutex m_mutex; - SInt16 m_x, m_y; - SInt16 m_w, m_h; - SInt16 m_zoneSize; - SInt16 m_cx, m_cy; + CClientInfo m_info; bool m_clipboardDirty[kClipboardEnd]; }; diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index b646aef1..57f11963 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -75,27 +75,22 @@ CPrimaryClient::onError() } void -CPrimaryClient::onInfoChanged(SInt32 x, SInt32 y, SInt32 w, SInt32 h, - SInt32 zoneSize, SInt32 cx, SInt32 cy) +CPrimaryClient::onInfoChanged(const CString&, const CClientInfo& info) { - m_x = x; - m_y = y; - m_w = w; - m_h = h; - m_zoneSize = zoneSize; - m_cx = cx; - m_cy = cy; - m_server->onInfoChanged(getName()); + m_info = info; + m_server->onInfoChanged(getName(), m_info); } bool -CPrimaryClient::onGrabClipboard(ClipboardID id) +CPrimaryClient::onGrabClipboard(const CString&, ClipboardID id, UInt32) { - m_clipboardOwner[id] = m_server->onGrabClipboard(id, m_seqNum, getName()); + bool result = m_server->onGrabClipboard(getName(), id, m_seqNum); + m_clipboardOwner[id] = result; + return result; } -bool -CPrimaryClient::onClipboardChanged(ClipboardID id, const CString& data) +void +CPrimaryClient::onClipboardChanged(ClipboardID id, UInt32, const CString& data) { m_server->onClipboardChanged(id, m_seqNum, data); } @@ -288,21 +283,21 @@ CPrimaryClient::getName() const void CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { - x = m_x; - y = m_y; - w = m_w; - h = m_h; + x = m_info.m_x; + y = m_info.m_y; + w = m_info.m_w; + h = m_info.m_h; } void CPrimaryClient::getCenter(SInt32& x, SInt32& y) const { - x = m_cx; - y = m_cy; + x = m_info.m_mx; + y = m_info.m_my; } SInt32 CPrimaryClient::getJumpZoneSize() const { - return m_zoneSize; + return m_info.m_zoneSize; } diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h index a7017edf..bfc4244f 100644 --- a/server/CPrimaryClient.h +++ b/server/CPrimaryClient.h @@ -1,14 +1,15 @@ #ifndef CPRIMARYCLIENT_H #define CPRIMARYCLIENT_H -#include "IPrimaryReceiver.h" +#include "IServer.h" #include "IClient.h" +#include "ProtocolTypes.h" class IClipboard; class IPrimaryScreen; class IServer; -class CPrimaryClient : public IPrimaryReceiver, public IClient { +class CPrimaryClient : public IServer, public IClient { public: CPrimaryClient(IServer*, const CString& name); ~CPrimaryClient(); @@ -32,14 +33,14 @@ public: // returns the state of the toggle keys on the primary screen KeyModifierMask getToggleMask() const; - // IPrimaryReceiver overrides + // IServer overrides + // onInfoChanged() ignores the client name. + // onGrabClipboard() ignores the client name and sequence number. + // onClipboardChanged() ignores the sequence number. virtual void onError(); - virtual void onInfoChanged(SInt32 xScreen, SInt32 yScreen, - SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse); - virtual bool onGrabClipboard(ClipboardID); - virtual bool onClipboardChanged(ClipboardID, const CString& data); + virtual void onInfoChanged(const CString&, const CClientInfo&); + virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); + virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); virtual void onKeyDown(KeyID, KeyModifierMask); virtual void onKeyUp(KeyID, KeyModifierMask); virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -80,11 +81,8 @@ private: IPrimaryScreen* m_screen; CString m_name; UInt32 m_seqNum; - SInt16 m_x, m_y; - SInt16 m_w, m_h; - SInt16 m_zoneSize; - SInt16 m_cx, m_cy; - bool m_clipboardOwner[kClipboardEnd]; + CClientInfo m_info; + bool m_clipboardOwner[kClipboardEnd]; // FIXME -- unneeded? bool m_clipboardDirty[kClipboardEnd]; }; diff --git a/server/CServer.cpp b/server/CServer.cpp index f414bfd0..8ddd9e13 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -8,7 +8,7 @@ #include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" -#include "CNetworkAddress.h" +// XXX #include "CNetworkAddress.h" #include "IDataSocket.h" #include "IListenSocket.h" #include "ISocketFactory.h" @@ -235,7 +235,7 @@ CServer::onError() } void -CServer::onInfoChanged(const CString& name) +CServer::onInfoChanged(const CString& name, const CClientInfo& info) { CLock lock(&m_mutex); @@ -249,17 +249,10 @@ CServer::onInfoChanged(const CString& name) // update the remote mouse coordinates if (client == m_active) { - client->getCenter(m_x, m_y); + m_x = info.m_mx; + m_y = info.m_my; } - -#ifndef NDEBUG - { - SInt32 x, y, w, h, mx, my; - client->getShape(x, y, w, h); - client->getCenter(mx, my); - log((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", name.c_str(), x, y, w, h, client->getJumpZoneSize(), m_x, m_y)); - } -#endif + log((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", name.c_str(), info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); // handle resolution change to primary screen if (client == m_primaryClient) { @@ -272,27 +265,10 @@ CServer::onInfoChanged(const CString& name) } } -void -CServer::onGrabClipboard(ClipboardID id) +bool +CServer::onGrabClipboard(const CString& name, ClipboardID id, UInt32 seqNum) { CLock lock(&m_mutex); - assert(m_primaryClient != NULL); - grabClipboardNoLock(id, 0, m_primaryClient->getName()); -} - -bool -CServer::onGrabClipboard(ClipboardID id, UInt32 seqNum, const CString& client) -{ - CLock lock(&m_mutex); - return grabClipboardNoLock(id, seqNum, client); -} - -bool -CServer::grabClipboardNoLock(ClipboardID id, - UInt32 seqNum, const CString& name) -{ - // note -- must be locked on entry - CClipboardInfo& clipboard = m_clipboards[id]; // screen must be connected CClientList::iterator grabber = m_clients.find(name); @@ -302,6 +278,7 @@ CServer::grabClipboardNoLock(ClipboardID id, // ignore grab if sequence number is old. always allow primary // screen to grab. + CClipboardInfo& clipboard = m_clipboards[id]; if (name != m_primaryClient->getName() && seqNum < clipboard.m_clipboardSeqNum) { log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", name.c_str(), id)); @@ -337,8 +314,7 @@ CServer::grabClipboardNoLock(ClipboardID id, } void -CServer::onClipboardChanged(ClipboardID id, - UInt32 seqNum, const CString& data) +CServer::onClipboardChanged(ClipboardID id, UInt32 seqNum, const CString& data) { CLock lock(&m_mutex); onClipboardChangedNoLock(id, seqNum, data); @@ -760,7 +736,8 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool screenSaver) if (clipboard.m_clipboardOwner == m_primaryClient->getName()) { CString clipboardData; m_primaryClient->getClipboard(id, clipboardData); - onClipboardChangedNoLock(id, m_seqNum, clipboardData); + onClipboardChangedNoLock(id, + clipboard.m_clipboardSeqNum, clipboardData); } } } diff --git a/server/CServer.h b/server/CServer.h index 0baf0696..74056d3e 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -4,10 +4,6 @@ #include "IServer.h" #include "CConfig.h" #include "CClipboard.h" -#include "ClipboardTypes.h" -#include "KeyTypes.h" -#include "MouseTypes.h" -#include "CNetworkAddress.h" #include "CCondVar.h" #include "CMutex.h" #include "CThread.h" @@ -59,11 +55,9 @@ public: // IServer overrides virtual void onError(); - virtual void onInfoChanged(const CString& clientName); - virtual bool onGrabClipboard(ClipboardID, - UInt32 seqNum, const CString& clientName); - virtual void onClipboardChanged(ClipboardID, - UInt32 seqNum, const CString& data); + virtual void onInfoChanged(const CString&, const CClientInfo&); + virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); + virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); virtual void onKeyDown(KeyID, KeyModifierMask); virtual void onKeyUp(KeyID, KeyModifierMask); virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -72,7 +66,6 @@ public: virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); virtual void onMouseWheel(SInt32 delta); - virtual void onGrabClipboard(ClipboardID); virtual void onScreenSaver(bool activated); protected: @@ -85,10 +78,6 @@ private: bool onMouseMovePrimaryNoLock(SInt32 x, SInt32 y); void onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy); - // grab the clipboard - bool grabClipboardNoLock(ClipboardID, - UInt32 seqNum, const CString& clientName); - // set the clipboard void onClipboardChangedNoLock(ClipboardID, UInt32 seqNum, const CString& data); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 5209a461..fb5b179a 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -1,9 +1,10 @@ #include "CXWindowsPrimaryScreen.h" -#include "IPrimaryReceiver.h" #include "CXWindowsClipboard.h" #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CClipboard.h" +#include "IServer.h" +#include "ProtocolTypes.h" #include "CThread.h" #include "CLog.h" #include "CStopwatch.h" @@ -20,8 +21,8 @@ // CXWindowsPrimaryScreen // -CXWindowsPrimaryScreen::CXWindowsPrimaryScreen(IPrimaryReceiver* receiver) : - m_receiver(receiver), +CXWindowsPrimaryScreen::CXWindowsPrimaryScreen(IServer* server) : + m_server(server), m_active(false), m_window(None) { @@ -66,7 +67,7 @@ CXWindowsPrimaryScreen::run() if (xevent.xclient.message_type == m_atomScreenSaver || xevent.xclient.format == 32) { // screen saver activation/deactivation event - m_receiver->onScreenSaver(xevent.xclient.data.l[0] != 0); + m_server->onScreenSaver(xevent.xclient.data.l[0] != 0); } break; @@ -76,12 +77,12 @@ CXWindowsPrimaryScreen::run() const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { - m_receiver->onKeyDown(key, mask); + m_server->onKeyDown(key, mask); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_receiver->onKeyUp(key, mask | KeyModifierCapsLock); + m_server->onKeyUp(key, mask | KeyModifierCapsLock); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_receiver->onKeyUp(key, mask | KeyModifierNumLock); + m_server->onKeyUp(key, mask | KeyModifierNumLock); } } } @@ -111,12 +112,12 @@ CXWindowsPrimaryScreen::run() // no press event follows so it's a plain release log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_receiver->onKeyDown(key, mask); + m_server->onKeyDown(key, mask); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_receiver->onKeyDown(key, mask); + m_server->onKeyDown(key, mask); } - m_receiver->onKeyUp(key, mask); + m_server->onKeyUp(key, mask); } else { // found a press event following so it's a repeat. @@ -124,7 +125,7 @@ CXWindowsPrimaryScreen::run() // repeats but we'll just send a repeat of 1. // note that we discard the press event. log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - m_receiver->onKeyRepeat(key, mask, 1); + m_server->onKeyRepeat(key, mask, 1); } } } @@ -135,7 +136,7 @@ CXWindowsPrimaryScreen::run() log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_receiver->onMouseDown(button); + m_server->onMouseDown(button); } } break; @@ -145,15 +146,15 @@ CXWindowsPrimaryScreen::run() log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_receiver->onMouseUp(button); + m_server->onMouseUp(button); } else if (xevent.xbutton.button == 4) { // wheel forward (away from user) - m_receiver->onMouseWheel(120); + m_server->onMouseWheel(120); } else if (xevent.xbutton.button == 5) { // wheel backward (toward user) - m_receiver->onMouseWheel(-120); + m_server->onMouseWheel(-120); } } break; @@ -184,7 +185,7 @@ CXWindowsPrimaryScreen::run() } else if (!m_active) { // motion on primary screen - m_receiver->onMouseMovePrimary(m_x, m_y); + m_server->onMouseMovePrimary(m_x, m_y); } else { // motion on secondary screen. warp mouse back to @@ -215,7 +216,7 @@ CXWindowsPrimaryScreen::run() // warping to the primary screen's enter position, // effectively overriding it. if (x != 0 || y != 0) { - m_receiver->onMouseMoveSecondary(x, y); + m_server->onMouseMoveSecondary(x, y); } } } @@ -269,8 +270,8 @@ CXWindowsPrimaryScreen::open() } // save mouse position - m_x = x; - m_y = y; + m_x = mx; + m_y = my; } // save position of center of screen @@ -278,7 +279,15 @@ CXWindowsPrimaryScreen::open() m_yCenter = y + (h >> 1); // send screen info - m_receiver->onInfoChanged(x, y, w, h, 1, m_x, m_y); + CClientInfo info; + info.m_x = x; + info.m_y = y; + info.m_w = w; + info.m_h = h; + info.m_zoneSize = 1; + info.m_mx = m_x; + info.m_my = m_y; + m_server->onInfoChanged("", info); } void @@ -586,14 +595,14 @@ void CXWindowsPrimaryScreen::onUnexpectedClose() { // tell server to shutdown - m_receiver->onError(); + m_server->onError(); } void CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id) { // tell server that the clipboard was grabbed locally - m_receiver->onGrabClipboard(id); + m_server->onGrabClipboard("", id, 0); } void diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 708d8376..e43af82b 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -5,11 +5,11 @@ #include "IPrimaryScreen.h" #include "MouseTypes.h" -class IPrimaryReceiver; +class IServer; class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { public: - CXWindowsPrimaryScreen(IPrimaryReceiver*); + CXWindowsPrimaryScreen(IServer*); virtual ~CXWindowsPrimaryScreen(); // IPrimaryScreen overrides @@ -60,7 +60,7 @@ private: static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: - IPrimaryReceiver* m_receiver; + IServer* m_server; bool m_active; Window m_window; diff --git a/server/IPrimaryReceiver.h b/server/IPrimaryReceiver.h deleted file mode 100644 index 081a0c95..00000000 --- a/server/IPrimaryReceiver.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef IPRIMARYRECEIVER_H -#define IPRIMARYRECEIVER_H - -#include "IInterface.h" -#include "ClipboardTypes.h" -#include "KeyTypes.h" -#include "MouseTypes.h" -#include "CString.h" - -class IPrimaryReceiver : public IInterface { -public: - // manipulators - - // notify of serious error. this implies that the primary screen - // cannot continue to function. - virtual void onError() = 0; - - // notify of info change - virtual void onInfoChanged(SInt32 xScreen, SInt32 yScreen, - SInt32 wScreen, SInt32 hScreen, - SInt32 zoneSize, - SInt32 xMouse, SInt32 yMouse) = 0; - - // notify of clipboard grab. returns true if the grab was honored, - // false otherwise. - virtual bool onGrabClipboard(ClipboardID) = 0; - - // notify of new clipboard data. returns true if the clipboard data - // was accepted, false if the change was rejected. - virtual bool onClipboardChanged(ClipboardID, - const CString& data) = 0; - - // call to notify of events. onMouseMovePrimary() returns - // true iff the mouse enters a jump zone and jumps. - virtual void onKeyDown(KeyID, KeyModifierMask) = 0; - virtual void onKeyUp(KeyID, KeyModifierMask) = 0; - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void onMouseDown(ButtonID) = 0; - virtual void onMouseUp(ButtonID) = 0; - virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; - virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; - virtual void onMouseWheel(SInt32 delta) = 0; - virtual void onScreenSaver(bool activated) = 0; -}; - -#endif diff --git a/server/Makefile.am b/server/Makefile.am index 02ec9040..397ff64e 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -19,7 +19,6 @@ synergyd_SOURCES = \ CPrimaryClient.h \ CServer.h \ CXWindowsPrimaryScreen.h \ - IPrimaryReceiver.h \ IPrimaryScreen.h \ $(NULL) synergyd_LDADD = \ diff --git a/synergy/IServer.h b/synergy/IServer.h index c109f148..62d949bc 100644 --- a/synergy/IServer.h +++ b/synergy/IServer.h @@ -7,6 +7,8 @@ #include "MouseTypes.h" #include "CString.h" +class CClientInfo; + class IServer : public IInterface { public: // manipulators @@ -15,13 +17,14 @@ public: // shutdown. virtual void onError() = 0; - // notify of client info change (maybe IClient should be named IScreen) - virtual void onInfoChanged(const CString& clientName) = 0; + // notify of client info change + virtual void onInfoChanged(const CString& clientName, + const CClientInfo&) = 0; // notify of clipboard grab. returns true if the grab was honored, // false otherwise. - virtual bool onGrabClipboard(ClipboardID, - UInt32 seqNum, const CString& clientName) = 0; + virtual bool onGrabClipboard(const CString& clientName, + ClipboardID, UInt32 seqNum) = 0; // notify of new clipboard data virtual void onClipboardChanged(ClipboardID, diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index 872a1d7e..a47f7d73 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -159,5 +159,23 @@ static const char kMsgEUnknown[] = "EUNK"; // primary should disconnect after sending this message. static const char kMsgEBad[] = "EBAD"; + +// +// structures +// + +class CClientInfo { +public: + // the coordinates of the screen + SInt32 m_x, m_y; + SInt32 m_w, m_h; + + // the size of the jump zone + SInt32 m_zoneSize; + + // mouse position + SInt32 m_mx, m_my; +}; + #endif From df6748f669a9314081b4496667b1b61e2e1fb61d Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 10 Jul 2002 14:29:50 +0000 Subject: [PATCH 222/807] removed some obsolete comments. --- synergy/IClient.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/synergy/IClient.h b/synergy/IClient.h index 7a151457..d538294f 100644 --- a/synergy/IClient.h +++ b/synergy/IClient.h @@ -33,13 +33,6 @@ public: // client's screen. virtual bool leave() = 0; - // FIXME -- methods for setting clipboard. on proxy side these - // will set/reset the gotClipboard flag and send the clipboard - // when not gotClipboard is false. grabbing goes in here too. - // basically, figure out semantics of these methods. note that - // ISecondaryScreen wants an IClipboard* passed to set not a - // string; will that be a problem? - // update the client's clipboard. this implies that the client's // clipboard is now up to date. if the client's clipboard was // already known to be up to date then this can do nothing. @@ -59,10 +52,6 @@ public: virtual void keyUp(KeyID, KeyModifierMask) = 0; virtual void mouseDown(ButtonID) = 0; virtual void mouseUp(ButtonID) = 0; - // FIXME -- if server uses IClient as interface to primary screen - // (should this class be renamed?) then be careful of absolute/relative - // coordinates here; move on primary and move on secondary take - // different values. probably not relevant, though. virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; virtual void mouseWheel(SInt32 delta) = 0; virtual void screenSaver(bool activate) = 0; @@ -73,7 +62,6 @@ public: virtual CString getName() const = 0; // get the screen's shape - // FIXME -- may want center pixel too virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; @@ -82,10 +70,6 @@ public: // get the size of jump zone virtual SInt32 getJumpZoneSize() const = 0; - - // what about getClipboard()? don't really want proxy to ask for it; - // it's a push data model. screen info is cached in proxy so it's - // different. will need to keep onClipboardChanged() in CClient. }; #endif From 710e1bdd47e75511d59d89c1f0ac5e7dc13b4b1d Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 10 Jul 2002 20:18:32 +0000 Subject: [PATCH 223/807] refactored client code. it now uses IClient and IServer and has a CServerProxy, making it's design similar to the server code. --- client/CClient.cpp | 995 ++++++++++--------------- client/CClient.h | 104 +-- client/CServerProxy.cpp | 576 ++++++++++++++ client/CServerProxy.h | 89 +++ {synergy => client}/ISecondaryScreen.h | 0 client/Makefile.am | 5 +- client/client.cpp | 8 +- server/CClientProxy.h | 3 +- server/CClientProxy1_0.cpp | 10 +- server/CClientProxy1_0.h | 3 +- server/CPrimaryClient.cpp | 10 +- server/CPrimaryClient.h | 3 +- server/CServer.cpp | 62 +- synergy/IClient.h | 7 +- synergy/Makefile.am | 1 - 15 files changed, 1207 insertions(+), 669 deletions(-) create mode 100644 client/CServerProxy.cpp create mode 100644 client/CServerProxy.h rename {synergy => client}/ISecondaryScreen.h (100%) diff --git a/client/CClient.cpp b/client/CClient.cpp index 83d2164f..09b31c0f 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -1,9 +1,11 @@ #include "CClient.h" +#include "CServerProxy.h" #include "CClipboard.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" #include "ISecondaryScreen.h" +#include "IServer.h" #include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" @@ -15,15 +17,6 @@ #include "CLog.h" #include "CStopwatch.h" #include "TMethodJob.h" -#include - -// hack to work around operator=() bug in STL in g++ prior to v3 -#if defined(__GNUC__) && (__GNUC__ < 3) -#define assign(_dst, _src, _type) _dst.reset(_src) -#else -#define assign(_dst, _src, _type) _dst = std::auto_ptr<_type >(_src) -#endif - // // CClient @@ -31,13 +24,12 @@ CClient::CClient(const CString& clientName) : m_name(clientName), - m_input(NULL), - m_output(NULL), m_screen(NULL), + m_server(NULL), m_camp(false), m_active(false), m_seqNum(0), - m_ignoreMove(false) + m_rejected(true) { // do nothing } @@ -50,9 +42,69 @@ CClient::~CClient() void CClient::camp(bool on) { + CLock lock(&m_mutex); m_camp = on; } +void +CClient::setAddress(const CNetworkAddress& serverAddress) +{ + CLock lock(&m_mutex); + m_serverAddress = serverAddress; +} + +void +CClient::quit() +{ + m_screen->stop(); +} + +bool +CClient::wasRejected() const +{ + return m_rejected; +} + +void +CClient::onClipboardChanged(ClipboardID id) +{ + CLock lock(&m_mutex); + if (m_server == NULL) { + // m_server can be NULL if the screen calls this method + // before we've gotten around to connecting to the server. + // we simply ignore the clipboard change in that case. + return; + } + + // grab ownership + m_server->onGrabClipboard(m_name, id, m_seqNum); + + // we now own the clipboard and it has not been sent to the server + m_ownClipboard[id] = true; + m_timeClipboard[id] = 0; + + // if we're not the active screen then send the clipboard now, + // otherwise we'll wait until we leave. + if (!m_active) { + sendClipboard(id); + } +} + +void +CClient::onResolutionChanged() +{ + log((CLOG_DEBUG "resolution changed")); + + CLock lock(&m_mutex); + if (m_server != NULL) { + CClientInfo info; + m_screen->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + m_screen->getMousePos(info.m_mx, info.m_my); + info.m_zoneSize = m_screen->getJumpZoneSize(); + m_server->onInfoChanged("", info); + } +} + bool CClient::open() { @@ -70,368 +122,205 @@ CClient::open() } } -bool -CClient::run(const CNetworkAddress& serverAddress) +void +CClient::run() { - // check preconditions { CLock lock(&m_mutex); + + // check preconditions assert(m_screen != NULL); + assert(m_server == NULL); + + // connection starts as unsuccessful + m_rejected = true; } CThread* thread = NULL; try { - log((CLOG_NOTE "starting client")); + log((CLOG_NOTE "starting client \"%s\"", m_name.c_str())); // start server interactions - m_serverAddress = &serverAddress; - thread = new CThread(new TMethodJob(this, &CClient::runSession)); + thread = new CThread(new TMethodJob( + this, &CClient::runSession)); // handle events log((CLOG_DEBUG "starting event handling")); m_screen->run(); + log((CLOG_DEBUG "stopped event handling")); // clean up - log((CLOG_NOTE "stopping client")); - thread->cancel(); - void* result = thread->getResult(); - delete thread; - closeSecondaryScreen(); - return (result != NULL); + deleteSession(thread); + log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); } catch (XBase& e) { log((CLOG_ERR "client error: %s", e.what())); // clean up - log((CLOG_NOTE "stopping client")); - if (thread != NULL) { - thread->cancel(); - thread->wait(); - delete thread; - } - closeSecondaryScreen(); - return true; + log((CLOG_DEBUG "stopped event handling")); + deleteSession(thread); + log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + CLock lock(&m_mutex); + m_rejected = false; } catch (XThread&) { // clean up - log((CLOG_NOTE "stopping client")); - if (thread != NULL) { - thread->cancel(); - thread->wait(); - delete thread; - } - closeSecondaryScreen(); + log((CLOG_DEBUG "stopped event handling")); + deleteSession(thread); + log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; } catch (...) { log((CLOG_DEBUG "unknown client error")); // clean up - log((CLOG_NOTE "stopping client")); - if (thread != NULL) { - thread->cancel(); - thread->wait(); - delete thread; - } - closeSecondaryScreen(); + log((CLOG_DEBUG "stopped event handling")); + deleteSession(thread); + log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; } } void -CClient::quit() +CClient::close() { - m_screen->stop(); + closeSecondaryScreen(); } void -CClient::onClipboardChanged(ClipboardID id) +CClient::enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, bool) { - log((CLOG_DEBUG "sending clipboard %d changed", id)); - CLock lock(&m_mutex); - if (m_output != NULL) { - // m_output can be NULL if the screen calls this method - // before we've gotten around to connecting to the server. - CProtocolUtil::writef(m_output, kMsgCClipboard, id, m_seqNum); - } - - // we now own the clipboard and it has not been sent to the server - m_ownClipboard[id] = true; - m_timeClipboard[id] = 0; - - // if we're not the active screen then send the clipboard now, - // otherwise we'll wait until we leave. - if (!m_active) { - // get clipboard - CClipboard clipboard; - m_screen->getClipboard(id, &clipboard); - - // save new time - m_timeClipboard[id] = clipboard.getTime(); - - // marshall the data - CString data = clipboard.marshall(); - - // send data - log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); - if (m_output != NULL) { -// FIXME -- will we send the clipboard when we connect? - CProtocolUtil::writef(m_output, kMsgDClipboard, id, m_seqNum, &data); - } - } -} - -void -CClient::onResolutionChanged() -{ - log((CLOG_DEBUG "resolution changed")); - - CLock lock(&m_mutex); - - // start ignoring mouse movement until we get an acknowledgment - m_ignoreMove = true; - - // send notification of resolution change - onQueryInfoNoLock(); -} - -#include "CTCPSocket.h" // FIXME -void -CClient::runSession(void*) -{ - log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); - - std::auto_ptr socket; - std::auto_ptr input; - std::auto_ptr output; - try { - for (;;) { - try { - // allow connect this much time to succeed - // FIXME -- timeout in member - CTimerThread timer(m_camp ? -1.0 : 30.0); - - // create socket and attempt to connect to server - log((CLOG_DEBUG1 "connecting to server")); - assign(socket, new CTCPSocket(), IDataSocket); // FIXME -- use factory - socket->connect(*m_serverAddress); - log((CLOG_INFO "connected to server")); - break; - } - catch (XSocketConnect&) { - // failed to connect. if not camping then rethrow. - if (!m_camp) { - throw; - } - - // we're camping. wait a bit before retrying - CThread::sleep(5.0); - } - } - - // get the input and output streams - IInputStream* srcInput = socket->getInputStream(); - IOutputStream* srcOutput = socket->getOutputStream(); - - // attach the encryption layer - bool own = false; -/* FIXME -- implement ISecurityFactory - if (m_securityFactory != NULL) { - input.reset(m_securityFactory->createInputFilter(srcInput, own)); - output.reset(m_securityFactory->createOutputFilter(srcOutput, own)); - srcInput = input.get(); - srcOutput = output.get(); - own = true; - } -*/ - - // give handshake some time - CTimerThread timer(30.0); - - // attach the packetizing filters - assign(input, new CInputPacketStream(srcInput, own), IInputStream); - assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); - - // wait for hello from server - log((CLOG_DEBUG1 "wait for hello")); - SInt16 major, minor; - CProtocolUtil::readf(input.get(), "Synergy%2i%2i", &major, &minor); - - // check versions - log((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); - if (major < kProtocolMajorVersion || - (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) { - throw XIncompatibleClient(major, minor); - } - - // say hello back - log((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); - CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s", - kProtocolMajorVersion, - kProtocolMinorVersion, &m_name); - - // record streams in a more useful place + { CLock lock(&m_mutex); - m_input = input.get(); - m_output = output.get(); - } - catch (XIncompatibleClient& e) { - log((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); - m_screen->stop(); - CThread::exit(NULL); - } - catch (XThread&) { - log((CLOG_ERR "connection timed out")); - m_screen->stop(); - throw; - } - catch (XBase& e) { - log((CLOG_ERR "connection failed: %s", e.what())); - m_screen->stop(); - CThread::exit(NULL); - } - catch (...) { - log((CLOG_ERR "connection failed: ")); - m_screen->stop(); - CThread::exit(NULL); + m_active = true; + m_seqNum = seqNum; } - bool fail = false; - try { - // no compressed mouse motion yet - m_compressMouse = false; + m_screen->enter(xAbs, yAbs, mask); +} - // handle messages from server - CStopwatch heartbeat; - for (;;) { - // if no input is pending then flush compressed mouse motion - if (input->getSize() == 0) { - flushCompressedMouse(); - } +bool +CClient::leave() +{ + m_screen->leave(); - // wait for a message - log((CLOG_DEBUG2 "waiting for message")); - UInt8 code[4]; - UInt32 n = input->read(code, 4, kHeartRate); + CLock lock(&m_mutex); + m_active = false; - // check if server hungup - if (n == 0) { - log((CLOG_NOTE "server disconnected")); - break; - } - - // check for time out - if (n == (UInt32)-1 || heartbeat.getTime() > kHeartRate) { - // send heartbeat - CProtocolUtil::writef(m_output, kMsgCNoop); - heartbeat.reset(); - if (n == (UInt32)-1) { - // no message to process - continue; - } - } - - // verify we got an entire code - if (n != 4) { - // client sent an incomplete message - log((CLOG_ERR "incomplete message from server")); - break; - } - - // parse message - log((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); - if (memcmp(code, kMsgDMouseMove, 4) == 0) { - onMouseMove(); - } - else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { - onMouseWheel(); - } - else if (memcmp(code, kMsgDKeyDown, 4) == 0) { - onKeyDown(); - } - else if (memcmp(code, kMsgDKeyUp, 4) == 0) { - onKeyUp(); - } - else if (memcmp(code, kMsgDMouseDown, 4) == 0) { - onMouseDown(); - } - else if (memcmp(code, kMsgDMouseUp, 4) == 0) { - onMouseUp(); - } - else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { - onKeyRepeat(); - } - else if (memcmp(code, kMsgCNoop, 4) == 0) { - // accept and discard no-op - continue; - } - else if (memcmp(code, kMsgCEnter, 4) == 0) { - onEnter(); - } - else if (memcmp(code, kMsgCLeave, 4) == 0) { - onLeave(); - } - else if (memcmp(code, kMsgCClipboard, 4) == 0) { - onGrabClipboard(); - } - else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { - onScreenSaver(); - } - else if (memcmp(code, kMsgQInfo, 4) == 0) { - onQueryInfo(); - } - else if (memcmp(code, kMsgCInfoAck, 4) == 0) { - onInfoAcknowledgment(); - } - else if (memcmp(code, kMsgDClipboard, 4) == 0) { - onSetClipboard(); - } - else if (memcmp(code, kMsgCClose, 4) == 0) { - // server wants us to hangup - log((CLOG_DEBUG1 "recv close")); - break; - } - else if (memcmp(code, kMsgEIncompatible, 4) == 0) { - onErrorIncompatible(); - fail = true; - break; - } - else if (memcmp(code, kMsgEBusy, 4) == 0) { - onErrorBusy(); - fail = true; - break; - } - else if (memcmp(code, kMsgEUnknown, 4) == 0) { - onErrorUnknown(); - fail = true; - break; - } - else if (memcmp(code, kMsgEBad, 4) == 0) { - onErrorBad(); - fail = true; - break; - } - else { - // unknown message - log((CLOG_ERR "unknown message from server")); - break; - } + // send clipboards that we own and that have changed + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_ownClipboard[id]) { + sendClipboard(id); } } - catch (XBase& e) { - log((CLOG_ERR "error: %s", e.what())); - m_screen->stop(); - CThread::exit(reinterpret_cast(1)); +} + +void +CClient::setClipboard(ClipboardID id, const CString& data) +{ + // unmarshall + CClipboard clipboard; + clipboard.unmarshall(data, 0); + + // set screen's clipboard + m_screen->setClipboard(id, &clipboard); +} + +void +CClient::grabClipboard(ClipboardID id) +{ + // we no longer own the clipboard + { + CLock lock(&m_mutex); + m_ownClipboard[id] = false; } - // done with socket - log((CLOG_DEBUG "disconnecting from server")); - socket->close(); + m_screen->grabClipboard(id); +} - // exit event loop - m_screen->stop(); +void +CClient::setClipboardDirty(ClipboardID, bool) +{ + assert(0 && "shouldn't be called"); +} - CThread::exit(fail ? NULL : reinterpret_cast(1)); +void +CClient::keyDown(KeyID id, KeyModifierMask mask) +{ + m_screen->keyDown(id, mask); +} + +void +CClient::keyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) +{ + m_screen->keyRepeat(id, mask, count); +} + +void +CClient::keyUp(KeyID id, KeyModifierMask mask) +{ + m_screen->keyUp(id, mask); +} + +void +CClient::mouseDown(ButtonID id) +{ + m_screen->mouseDown(id); +} + +void +CClient::mouseUp(ButtonID id) +{ + m_screen->mouseUp(id); +} + +void +CClient::mouseMove(SInt32 x, SInt32 y) +{ + m_screen->mouseMove(x, y); +} + +void +CClient::mouseWheel(SInt32 delta) +{ + m_screen->mouseWheel(delta); +} + +void +CClient::screenSaver(bool activate) +{ + m_screen->screenSaver(activate); +} + +CString +CClient::getName() const +{ + return m_name; +} + +void +CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + m_screen->getShape(x, y, w, h); +} + +void +CClient::getCenter(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + +void +CClient::getMousePos(SInt32& x, SInt32& y) const +{ + m_screen->getMousePos(x, y); +} + +SInt32 +CClient::getJumpZoneSize() const +{ + return m_screen->getJumpZoneSize(); } // FIXME -- use factory to create screen @@ -489,310 +378,228 @@ CClient::closeSecondaryScreen() } void -CClient::flushCompressedMouse() +CClient::sendClipboard(ClipboardID id) { - if (m_compressMouse) { - m_compressMouse = false; - m_screen->mouseMove(m_xMouse, m_yMouse); + // note -- m_mutex must be locked on entry + assert(m_screen != NULL); + assert(m_server != NULL); + + // get clipboard data. set the clipboard time to the last + // clipboard time before getting the data from the screen + // as the screen may detect an unchanged clipboard and + // avoid copying the data. + CClipboard clipboard; + if (clipboard.open(m_timeClipboard[id])) { + clipboard.close(); + } + m_screen->getClipboard(id, &clipboard); + + // check time + if (m_timeClipboard[id] == 0 || + clipboard.getTime() != m_timeClipboard[id]) { + // save new time + m_timeClipboard[id] = clipboard.getTime(); + + // marshall the data + CString data = clipboard.marshall(); + + // save and send data if different + if (data != m_dataClipboard[id]) { + m_dataClipboard[id] = data; + m_server->onClipboardChanged(id, m_seqNum, data); + } } } void -CClient::onEnter() +CClient::runSession(void*) { - SInt16 x, y; - UInt16 mask; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum, &mask); - m_active = true; + try { + log((CLOG_DEBUG "starting server proxy")); + runServer(); + m_screen->stop(); + log((CLOG_DEBUG "stopping server proxy")); + } + catch (...) { + m_screen->stop(); + log((CLOG_DEBUG "stopping server proxy")); + throw; } - log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, m_seqNum, mask)); - - // discard old compressed mouse motion, if any - m_compressMouse = false; - - // tell screen we're entering - m_screen->enter(x, y, static_cast(mask)); } void -CClient::onLeave() +CClient::deleteSession(CThread* thread) { - log((CLOG_DEBUG1 "recv leave")); + if (thread != NULL) { + thread->cancel(); + thread->wait(); + delete thread; + } +} - // send last mouse motion - flushCompressedMouse(); +#include "CTCPSocket.h" // FIXME +void +CClient::runServer() +{ + IDataSocket* socket = NULL; + CServerProxy* proxy = NULL; + try { + for (;;) { + try { + // allow connect this much time to succeed + // FIXME -- timeout in member + CTimerThread timer(m_camp ? -1.0 : 30.0); - // tell screen we're leaving - m_screen->leave(); - - // no longer the active screen - CLock lock(&m_mutex); - m_active = false; - - // send clipboards that we own and that have changed - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - if (m_ownClipboard[id]) { - // get clipboard data. set the clipboard time to the last - // clipboard time before getting the data from the screen - // as the screen may detect an unchanged clipboard and - // avoid copying the data. - CClipboard clipboard; - if (clipboard.open(m_timeClipboard[id])) - clipboard.close(); - m_screen->getClipboard(id, &clipboard); - - // check time - if (m_timeClipboard[id] == 0 || - clipboard.getTime() != m_timeClipboard[id]) { - // save new time - m_timeClipboard[id] = clipboard.getTime(); - - // marshall the data - CString data = clipboard.marshall(); - - // save and send data if different - if (data != m_dataClipboard[id]) { - log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); - m_dataClipboard[id] = data; - CProtocolUtil::writef(m_output, - kMsgDClipboard, id, m_seqNum, &data); + // create socket and attempt to connect to server + log((CLOG_DEBUG1 "connecting to server")); + socket = new CTCPSocket; // FIXME -- use factory + socket->connect(m_serverAddress); + log((CLOG_INFO "connected to server")); + break; + } + catch (XSocketConnect&) { + // failed to connect. if not camping then rethrow. + if (!m_camp) { + throw; } + + // we're camping. wait a bit before retrying + CThread::sleep(5.0); } } - } -} -void -CClient::onGrabClipboard() -{ - ClipboardID id; - UInt32 seqNum; - { + // create proxy + log((CLOG_DEBUG1 "negotiating with server")); + proxy = handshakeServer(socket); CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id, &seqNum); - log((CLOG_DEBUG "recv grab clipboard %d", id)); - - // validate - if (id >= kClipboardEnd) { - return; - } - - // we no longer own the clipboard - m_ownClipboard[id] = false; + m_server = proxy; } - m_screen->grabClipboard(id); -} - -void -CClient::onScreenSaver() -{ - SInt8 on; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on); + catch (XThread&) { + log((CLOG_ERR "connection timed out")); + delete socket; + throw; } - log((CLOG_DEBUG1 "recv screen saver on=%d", on)); - m_screen->screenSaver(on != 0); -} - -void -CClient::onQueryInfo() -{ - CLock lock(&m_mutex); - onQueryInfoNoLock(); -} - -void -CClient::onQueryInfoNoLock() -{ - SInt32 mx, my, x, y, w, h; - m_screen->getMousePos(mx, my); - m_screen->getShape(x, y, w, h); - SInt32 zoneSize = m_screen->getJumpZoneSize(); - - log((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", x, y, w, h, zoneSize, mx, my)); - CProtocolUtil::writef(m_output, kMsgDInfo, x, y, w, h, zoneSize, mx, my); -} - -void -CClient::onInfoAcknowledgment() -{ - log((CLOG_DEBUG1 "recv info acknowledgment")); - CLock lock(&m_mutex); - m_ignoreMove = false; -} - -void -CClient::onSetClipboard() -{ - ClipboardID id; - CString data; - { - // parse message - UInt32 seqNum; - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDClipboard + 4, &id, &seqNum, &data); + catch (XBase& e) { + log((CLOG_ERR "connection failed: %s", e.what())); + log((CLOG_DEBUG "disconnecting from server")); + delete socket; + return; } - log((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); - - // validate - if (id >= kClipboardEnd) { + catch (...) { + log((CLOG_ERR "connection failed: ")); + log((CLOG_DEBUG "disconnecting from server")); + delete socket; return; } - // unmarshall - CClipboard clipboard; - clipboard.unmarshall(data, 0); - - // set screen's clipboard - m_screen->setClipboard(id, &clipboard); -} - -void -CClient::onKeyDown() -{ - // get mouse up to date - flushCompressedMouse(); - - UInt16 id, mask; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); - } - log((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask)); - m_screen->keyDown(static_cast(id), - static_cast(mask)); -} - -void -CClient::onKeyRepeat() -{ - // get mouse up to date - flushCompressedMouse(); - - UInt16 id, mask, count; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); - } - log((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count)); - m_screen->keyRepeat(static_cast(id), - static_cast(mask), - count); -} - -void -CClient::onKeyUp() -{ - // get mouse up to date - flushCompressedMouse(); - - UInt16 id, mask; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); - } - log((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask)); - m_screen->keyUp(static_cast(id), - static_cast(mask)); -} - -void -CClient::onMouseDown() -{ - // get mouse up to date - flushCompressedMouse(); - - SInt8 id; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); - } - log((CLOG_DEBUG1 "recv mouse down id=%d", id)); - m_screen->mouseDown(static_cast(id)); -} - -void -CClient::onMouseUp() -{ - // get mouse up to date - flushCompressedMouse(); - - SInt8 id; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); - } - log((CLOG_DEBUG1 "recv mouse up id=%d", id)); - m_screen->mouseUp(static_cast(id)); -} - -void -CClient::onMouseMove() -{ - bool ignore; - SInt16 x, y; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); - ignore = m_ignoreMove; - - // compress mouse motion events if more input follows - if (!ignore && !m_compressMouse && m_input->getSize() > 0) { - m_compressMouse = true; + try { + // process messages + bool rejected = true; + if (proxy != NULL) { + log((CLOG_DEBUG1 "communicating with server")); + rejected = !proxy->run(); } - if (m_compressMouse) { - ignore = true; - m_xMouse = x; - m_yMouse = y; - } - } - log((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); - if (!ignore) { - m_screen->mouseMove(x, y); - } -} -void -CClient::onMouseWheel() -{ - // get mouse up to date - flushCompressedMouse(); - - SInt16 delta; - { + // clean up CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); + m_rejected = rejected; + m_server = NULL; + delete proxy; + log((CLOG_DEBUG "disconnecting from server")); + socket->close(); + delete socket; + } + catch (...) { + CLock lock(&m_mutex); + m_rejected = false; + m_server = NULL; + delete proxy; + log((CLOG_DEBUG "disconnecting from server")); + socket->close(); + delete socket; + throw; } - log((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); - m_screen->mouseWheel(delta); } -void -CClient::onErrorIncompatible() +CServerProxy* +CClient::handshakeServer(IDataSocket* socket) { - SInt32 major, minor; - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgEIncompatible + 4, &major, &minor); - log((CLOG_ERR "server has incompatible version %d.%d", major, minor)); -} + // get the input and output streams + IInputStream* input = socket->getInputStream(); + IOutputStream* output = socket->getOutputStream(); + bool own = false; -void -CClient::onErrorBusy() -{ - log((CLOG_ERR "server already has a connected client with name \"%s\"", m_name.c_str())); -} + // attach the encryption layer +/* FIXME -- implement ISecurityFactory + if (m_securityFactory != NULL) { + input = m_securityFactory->createInputFilter(input, own); + output = m_securityFactory->createOutputFilter(output, own); + own = true; + } +*/ -void -CClient::onErrorUnknown() -{ - log((CLOG_ERR "server refused client with name \"%s\"", m_name.c_str())); -} + // attach the packetizing filters + input = new CInputPacketStream(input, own); + output = new COutputPacketStream(output, own); + own = true; -void -CClient::onErrorBad() -{ - log((CLOG_ERR "server disconnected due to a protocol error")); + CServerProxy* proxy = NULL; + try { + // give handshake some time + CTimerThread timer(30.0); + + // wait for hello from server + log((CLOG_DEBUG1 "wait for hello")); + SInt16 major, minor; + CProtocolUtil::readf(input, "Synergy%2i%2i", &major, &minor); + + // check versions + log((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); + if (major < kProtocolMajorVersion || + (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) { + throw XIncompatibleClient(major, minor); + } + + // say hello back + log((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); + CProtocolUtil::writef(output, "Synergy%2i%2i%s", + kProtocolMajorVersion, + kProtocolMinorVersion, &m_name); + + // create server proxy + proxy = new CServerProxy(this, input, output); + + // negotiate + // FIXME + + return proxy; + } + catch (XIncompatibleClient& e) { + log((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); + } + catch (XBase& e) { + log((CLOG_WARN "error communicating with server: %s", e.what())); + } + catch (...) { + // probably timed out + if (proxy != NULL) { + delete proxy; + } + else if (own) { + delete input; + delete output; + } + throw; + } + + // failed + if (proxy != NULL) { + delete proxy; + } + else if (own) { + delete input; + delete output; + } + + return NULL; } diff --git a/client/CClient.h b/client/CClient.h index 66927660..73ef8714 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -1,17 +1,18 @@ #ifndef CCLIENT_H #define CCLIENT_H -#include "ClipboardTypes.h" +#include "IClient.h" #include "IClipboard.h" +#include "CNetworkAddress.h" #include "CMutex.h" -#include "CString.h" -class CNetworkAddress; -class IInputStream; -class IOutputStream; +class CServerProxy; +class CThread; +class IDataSocket; class ISecondaryScreen; +class IServer; -class CClient { +class CClient : public IClient { public: CClient(const CString& clientName); ~CClient(); @@ -24,74 +25,87 @@ public: // not call this while in run(). void camp(bool on); - // open the client's screen - bool open(); - - // start the client. does not return until quit() is called. - // returns true if the client ever connected to the server - // successfully. may also throw exceptions after successfully - // connecting. a successful open() must preceed this call. - bool run(const CNetworkAddress& serverAddress); + // set the server's address that the client should connect to + void setAddress(const CNetworkAddress& serverAddress); // tell client to exit run() gracefully. this must only be called // after a successful open(). void quit(); // handle events on client's screen +// FIXME -- this should mimic methods on IServer +// FIXME -- maybe create a IScreenReceiver with these methods and +// have CPrimaryClient and CClient inherit from them. IServer +// still needs similar methods with extra parameters, though. so +// CServerProxy +// CPrimaryClient +// CClient +// need IScreenReceiver. these classes effective receive notifications +// from screens. note that there's another class of notifications that +// only the server needs (key, mouyse, screensaver). so maybe we have +// IPrimaryScreenReceiver and ISecondaryScreenReceiver (the latter is +// derived with no extra methods from IScreenReceiver). void onClipboardChanged(ClipboardID); void onResolutionChanged(); // accessors + // returns true if the server rejected us + bool wasRejected() const; + + // IClient overrides + virtual bool open(); + virtual void run(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool screenSaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool dirty); + virtual void keyDown(KeyID, KeyModifierMask); + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void keyUp(KeyID, KeyModifierMask); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseWheel(SInt32 delta); + virtual void screenSaver(bool activate); + virtual CString getName() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCenter(SInt32& x, SInt32& y) const; + virtual void getMousePos(SInt32& x, SInt32& y) const; + virtual SInt32 getJumpZoneSize() const; private: - void runSession(void*); - // open/close the secondary screen void openSecondaryScreen(); void closeSecondaryScreen(); - // if compressing mouse motion then send the last motion now - void flushCompressedMouse(); + // send the clipboard to the server + void sendClipboard(ClipboardID); - // message handlers - void onEnter(); - void onLeave(); - void onGrabClipboard(); - void onScreenSaver(); - void onQueryInfo(); - void onQueryInfoNoLock(); - void onInfoAcknowledgment(); - void onSetClipboard(); - void onKeyDown(); - void onKeyRepeat(); - void onKeyUp(); - void onMouseDown(); - void onMouseUp(); - void onMouseMove(); - void onMouseWheel(); - void onErrorIncompatible(); - void onErrorBusy(); - void onErrorUnknown(); - void onErrorBad(); + // handle server messaging + void runSession(void*); + void deleteSession(CThread*); + void runServer(); + CServerProxy* handshakeServer(IDataSocket*); private: CMutex m_mutex; CString m_name; - IInputStream* m_input; - IOutputStream* m_output; ISecondaryScreen* m_screen; - const CNetworkAddress* m_serverAddress; + IServer* m_server; + CNetworkAddress m_serverAddress; bool m_camp; bool m_active; UInt32 m_seqNum; - bool m_ignoreMove; + bool m_rejected; bool m_ownClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; CString m_dataClipboard[kClipboardEnd]; - - bool m_compressMouse; - SInt32 m_xMouse, m_yMouse; }; #endif diff --git a/client/CServerProxy.cpp b/client/CServerProxy.cpp new file mode 100644 index 00000000..b8e5866d --- /dev/null +++ b/client/CServerProxy.cpp @@ -0,0 +1,576 @@ +#include "CServerProxy.h" +#include "CProtocolUtil.h" +#include "IClient.h" +#include "ProtocolTypes.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLock.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "XBase.h" +#include + +// +// CServerProxy +// + +CServerProxy::CServerProxy(IClient* client, + IInputStream* input, IOutputStream* output) : + m_client(client), + m_input(input), + m_output(output) +{ + assert(m_client != NULL); + assert(m_input != NULL); + assert(m_output != NULL); +} + +CServerProxy::~CServerProxy() +{ + delete m_input; + delete m_output; +} + +bool +CServerProxy::run() +{ + bool failedToConnect = false; + try { + // no compressed mouse motion yet + m_compressMouse = false; + + // not ignoring mouse motions + m_ignoreMouse = false; + + // handle messages from server + CStopwatch heartbeat; + for (;;) { + // if no input is pending then flush compressed mouse motion + if (getInputStream()->getSize() == 0) { + flushCompressedMouse(); + } + + // wait for a message + log((CLOG_DEBUG2 "waiting for message")); + UInt8 code[4]; + UInt32 n = getInputStream()->read(code, 4, kHeartRate); + + // check if server hungup + if (n == 0) { + log((CLOG_NOTE "server disconnected")); + break; + } + + // check for time out + if (n == (UInt32)-1 || heartbeat.getTime() > kHeartRate) { + // send heartbeat + CLock lock(&m_mutex); + CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + heartbeat.reset(); + if (n == (UInt32)-1) { + // no message to process + continue; + } + } + + // verify we got an entire code + if (n != 4) { + // client sent an incomplete message + log((CLOG_ERR "incomplete message from server")); + break; + } + + // parse message + log((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + if (memcmp(code, kMsgDMouseMove, 4) == 0) { + mouseMove(); + } + + else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { + mouseWheel(); + } + + else if (memcmp(code, kMsgDKeyDown, 4) == 0) { + keyDown(); + } + + else if (memcmp(code, kMsgDKeyUp, 4) == 0) { + keyUp(); + } + + else if (memcmp(code, kMsgDMouseDown, 4) == 0) { + mouseDown(); + } + + else if (memcmp(code, kMsgDMouseUp, 4) == 0) { + mouseUp(); + } + + else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { + keyRepeat(); + } + + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + } + + else if (memcmp(code, kMsgCEnter, 4) == 0) { + enter(); + } + + else if (memcmp(code, kMsgCLeave, 4) == 0) { + leave(); + } + + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + grabClipboard(); + } + + else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { + screenSaver(); + } + + else if (memcmp(code, kMsgQInfo, 4) == 0) { + queryInfo(); + } + + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + infoAcknowledgment(); + } + + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + setClipboard(); + } + + else if (memcmp(code, kMsgCClose, 4) == 0) { + // server wants us to hangup + log((CLOG_DEBUG1 "recv close")); + break; + } + + else if (memcmp(code, kMsgEIncompatible, 4) == 0) { + SInt32 major, minor; + CProtocolUtil::readf(getInputStream(), + kMsgEIncompatible + 4, &major, &minor); + log((CLOG_ERR "server has incompatible version %d.%d", major, minor)); + failedToConnect = true; + break; + } + + else if (memcmp(code, kMsgEBusy, 4) == 0) { + log((CLOG_ERR "server already has a connected client with name \"%s\"", getName().c_str())); + failedToConnect = true; + break; + } + + else if (memcmp(code, kMsgEUnknown, 4) == 0) { + log((CLOG_ERR "server refused client with name \"%s\"", getName().c_str())); + failedToConnect = true; + break; + } + + else if (memcmp(code, kMsgEBad, 4) == 0) { + log((CLOG_ERR "server disconnected due to a protocol error")); + failedToConnect = true; + break; + } + + else { + // unknown message + log((CLOG_ERR "unknown message from server")); + failedToConnect = true; + break; + } + } + } + catch (XBase& e) { + log((CLOG_ERR "error: %s", e.what())); + } + catch (...) { + throw; + } + + return !failedToConnect; +} + +IClient* +CServerProxy::getClient() const +{ + return m_client; +} + +CString +CServerProxy::getName() const +{ + return m_client->getName(); +} + +IInputStream* +CServerProxy::getInputStream() const +{ + return m_input; +} + +IOutputStream* +CServerProxy::getOutputStream() const +{ + return m_output; +} + +void +CServerProxy::onError() +{ + // ignore +} + +void +CServerProxy::onInfoChanged(const CString&, const CClientInfo& info) +{ + // ignore mouse motion until we receive acknowledgment of our info + // change message. + CLock lock(&m_mutex); + m_ignoreMouse = true; + + // send info update + sendInfo(info); +} + +bool +CServerProxy::onGrabClipboard(const CString&, ClipboardID id, UInt32 seqNum) +{ + log((CLOG_DEBUG1 "sending clipboard %d changed", id)); + CLock lock(&m_mutex); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, seqNum); +} + +void +CServerProxy::onClipboardChanged(ClipboardID id, + UInt32 seqNum, const CString& data) +{ + log((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, seqNum, data.size())); + CLock lock(&m_mutex); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, seqNum, &data); +} + +void +CServerProxy::onKeyDown(KeyID, KeyModifierMask) +{ + // ignore +} + +void +CServerProxy::onKeyUp(KeyID, KeyModifierMask) +{ + // ignore +} + +void +CServerProxy::onKeyRepeat(KeyID, KeyModifierMask, SInt32) +{ + // ignore +} + +void +CServerProxy::onMouseDown(ButtonID) +{ + // ignore +} + +void +CServerProxy::onMouseUp(ButtonID) +{ + // ignore +} + +bool +CServerProxy::onMouseMovePrimary(SInt32, SInt32) +{ + return false; +} + +void +CServerProxy::onMouseMoveSecondary(SInt32, SInt32) +{ + // ignore +} + +void +CServerProxy::onMouseWheel(SInt32) +{ + // ignore +} + +void +CServerProxy::onScreenSaver(bool) +{ + // ignore +} + +void +CServerProxy::flushCompressedMouse() +{ + bool send = false; + SInt32 x, y; + { + CLock lock(&m_mutex); + if (m_compressMouse) { + m_compressMouse = false; + x = m_xMouse; + y = m_yMouse; + send = true; + } + } + + if (send) { + getClient()->mouseMove(x, y); + } +} + +void +CServerProxy::sendInfo(const CClientInfo& info) +{ + // note -- m_mutex should be locked on entry + log((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); + CProtocolUtil::writef(getOutputStream(), kMsgDInfo, + info.m_x, info.m_y, + info.m_w, info.m_h, + info.m_zoneSize, + info.m_mx, info.m_my); +} + +void +CServerProxy::enter() +{ + // parse + SInt16 x, y; + UInt16 mask; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), + kMsgCEnter + 4, &x, &y, &seqNum, &mask); + log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask)); + + // discard old compressed mouse motion, if any + { + CLock lock(&m_mutex); + m_compressMouse = false; + } + + // forward + getClient()->enter(x, y, seqNum, static_cast(mask), false); +} + +void +CServerProxy::leave() +{ + // parse + log((CLOG_DEBUG1 "recv leave")); + + // send last mouse motion + flushCompressedMouse(); + + // forward + getClient()->leave(); +} + +void +CServerProxy::setClipboard() +{ + // parse + ClipboardID id; + UInt32 seqNum; + CString data; + CProtocolUtil::readf(getInputStream(), + kMsgDClipboard + 4, &id, &seqNum, &data); + log((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); + + // validate + if (id >= kClipboardEnd) { + return; + } + + // forward + getClient()->setClipboard(id, data); +} + +void +CServerProxy::grabClipboard() +{ + // parse + ClipboardID id; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + log((CLOG_DEBUG "recv grab clipboard %d", id)); + + // validate + if (id >= kClipboardEnd) { + return; + } + + // forward + getClient()->grabClipboard(id); +} + +void +CServerProxy::keyDown() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask; + CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, &id, &mask); + log((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask)); + + // forward + getClient()->keyDown(static_cast(id), + static_cast(mask)); +} + +void +CServerProxy::keyRepeat() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask, count; + CProtocolUtil::readf(getInputStream(), + kMsgDKeyRepeat + 4, &id, &mask, &count); + log((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count)); + + // forward + getClient()->keyRepeat(static_cast(id), + static_cast(mask), + count); +} + +void +CServerProxy::keyUp() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask; + CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask); + log((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask)); + + // forward + getClient()->keyUp(static_cast(id), + static_cast(mask)); +} + +void +CServerProxy::mouseDown() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt8 id; + CProtocolUtil::readf(getInputStream(), kMsgDMouseDown + 4, &id); + log((CLOG_DEBUG1 "recv mouse down id=%d", id)); + + // forward + getClient()->mouseDown(static_cast(id)); +} + +void +CServerProxy::mouseUp() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt8 id; + CProtocolUtil::readf(getInputStream(), kMsgDMouseUp + 4, &id); + log((CLOG_DEBUG1 "recv mouse up id=%d", id)); + + // forward + getClient()->mouseUp(static_cast(id)); +} + +void +CServerProxy::mouseMove() +{ + // parse + bool ignore; + SInt16 x, y; + CProtocolUtil::readf(getInputStream(), kMsgDMouseMove + 4, &x, &y); + + { + // note if we should ignore the move + CLock lock(&m_mutex); + ignore = m_ignoreMouse; + + // compress mouse motion events if more input follows + if (!ignore && !m_compressMouse && getInputStream()->getSize() > 0) { + m_compressMouse = true; + } + + // if compressing then ignore the motion but record it + if (m_compressMouse) { + ignore = true; + m_xMouse = x; + m_yMouse = y; + } + } + log((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); + + // forward + if (!ignore) { + getClient()->mouseMove(x, y); + } +} + +void +CServerProxy::mouseWheel() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt16 delta; + CProtocolUtil::readf(getInputStream(), kMsgDMouseWheel + 4, &delta); + log((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); + + // forward + getClient()->mouseWheel(delta); +} + +void +CServerProxy::screenSaver() +{ + // parse + SInt8 on; + CProtocolUtil::readf(getInputStream(), kMsgCScreenSaver + 4, &on); + log((CLOG_DEBUG1 "recv screen saver on=%d", on)); + + // forward + getClient()->screenSaver(on != 0); +} + +void +CServerProxy::queryInfo() +{ + // get current info + CClientInfo info; + getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getClient()->getMousePos(info.m_mx, info.m_my); + info.m_zoneSize = getClient()->getJumpZoneSize(); + + // send it + CLock lock(&m_mutex); + sendInfo(info); +} + +void +CServerProxy::infoAcknowledgment() +{ + // parse + log((CLOG_DEBUG1 "recv info acknowledgment")); + + // now allow mouse motion + CLock lock(&m_mutex); + m_ignoreMouse = false; +} diff --git a/client/CServerProxy.h b/client/CServerProxy.h new file mode 100644 index 00000000..c404f7ed --- /dev/null +++ b/client/CServerProxy.h @@ -0,0 +1,89 @@ +#ifndef CSERVERPROXY_H +#define CSERVERPROXY_H + +#include "IServer.h" +#include "CMutex.h" + +class IClient; +class IInputStream; +class IOutputStream; + +class CServerProxy : public IServer { +public: + CServerProxy(IClient* client, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CServerProxy(); + + // manipulators + + // handle messages. returns true iff server didn't reject our + // connection. + bool run(); + + // accessors + + // get the client + IClient* getClient() const; + + // get the client name + CString getName() const; + + // get the input and output streams for the server + IInputStream* getInputStream() const; + IOutputStream* getOutputStream() const; + + // IServer overrides + virtual void onError(); + virtual void onInfoChanged(const CString& clientName, + const CClientInfo&); + virtual bool onGrabClipboard(const CString& clientName, + ClipboardID, UInt32 seqNum); + virtual void onClipboardChanged(ClipboardID, + UInt32 seqNum, const CString& data); + virtual void onKeyDown(KeyID, KeyModifierMask); + virtual void onKeyUp(KeyID, KeyModifierMask); + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void onMouseDown(ButtonID); + virtual void onMouseUp(ButtonID); + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); + virtual void onMouseWheel(SInt32 delta); + virtual void onScreenSaver(bool activated); + +private: + // if compressing mouse motion then send the last motion now + void flushCompressedMouse(); + + void sendInfo(const CClientInfo&); + + // message handlers + void enter(); + void leave(); + void setClipboard(); + void grabClipboard(); + void keyDown(); + void keyRepeat(); + void keyUp(); + void mouseDown(); + void mouseUp(); + void mouseMove(); + void mouseWheel(); + void screenSaver(); + void queryInfo(); + void infoAcknowledgment(); + +private: + CMutex m_mutex; + + IClient* m_client; + IInputStream* m_input; + IOutputStream* m_output; + + bool m_compressMouse; + SInt32 m_xMouse, m_yMouse; + + bool m_ignoreMouse; +}; + +#endif diff --git a/synergy/ISecondaryScreen.h b/client/ISecondaryScreen.h similarity index 100% rename from synergy/ISecondaryScreen.h rename to client/ISecondaryScreen.h diff --git a/client/Makefile.am b/client/Makefile.am index 744ba730..27d35cc9 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -4,11 +4,14 @@ DEPTH = .. bin_PROGRAMS = synergy synergy_SOURCES = \ - CXWindowsSecondaryScreen.cpp \ CClient.cpp \ + CServerProxy.cpp \ + CXWindowsSecondaryScreen.cpp \ client.cpp \ CClient.h \ + CServerProxy.h \ CXWindowsSecondaryScreen.h \ + ISecondaryScreen.h \ $(NULL) synergy_LDADD = \ $(DEPTH)/platform/libplatform.a \ diff --git a/client/client.cpp b/client/client.cpp index 21d23b2d..620e7846 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -81,6 +81,7 @@ realMain(CMutex* mutex) // create client s_client = new CClient(s_name); s_client->camp(s_camp); + s_client->setAddress(s_serverAddress); if (!s_client->open()) { delete s_client; s_client = NULL; @@ -92,13 +93,17 @@ realMain(CMutex* mutex) mutex->unlock(); } locked = false; - bool success = s_client->run(s_serverAddress); + s_client->run(); locked = true; if (mutex != NULL) { mutex->lock(); } + // get client status + bool success = !s_client->wasRejected(); + // clean up + s_client->close(); delete s_client; s_client = NULL; CLog::setLock(NULL); @@ -111,6 +116,7 @@ realMain(CMutex* mutex) if (!locked && mutex != NULL) { mutex->lock(); } + s_client->close(); delete s_client; s_client = NULL; CLog::setLock(NULL); diff --git a/server/CClientProxy.h b/server/CClientProxy.h index 1b6c17bb..ef5d158f 100644 --- a/server/CClientProxy.h +++ b/server/CClientProxy.h @@ -27,7 +27,7 @@ public: IOutputStream* getOutputStream() const; // IClient overrides - virtual void open() = 0; + virtual bool open() = 0; virtual void run() = 0; virtual void close() = 0; virtual void enter(SInt32 xAbs, SInt32 yAbs, @@ -49,6 +49,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; virtual void getCenter(SInt32& x, SInt32& y) const = 0; + virtual void getMousePos(SInt32& x, SInt32& y) const = 0; virtual SInt32 getJumpZoneSize() const = 0; private: diff --git a/server/CClientProxy1_0.cpp b/server/CClientProxy1_0.cpp index b1854d65..6a214d0d 100644 --- a/server/CClientProxy1_0.cpp +++ b/server/CClientProxy1_0.cpp @@ -29,7 +29,7 @@ CClientProxy1_0::~CClientProxy1_0() // do nothing } -void +bool CClientProxy1_0::open() { // send request @@ -55,6 +55,8 @@ CClientProxy1_0::open() // handle reply recvInfo(false); + + return true; } void @@ -258,6 +260,12 @@ CClientProxy1_0::getCenter(SInt32& x, SInt32& y) const y = m_info.m_my; } +void +CClientProxy1_0::getMousePos(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + SInt32 CClientProxy1_0::getJumpZoneSize() const { diff --git a/server/CClientProxy1_0.h b/server/CClientProxy1_0.h index fae25eb7..59d463b7 100644 --- a/server/CClientProxy1_0.h +++ b/server/CClientProxy1_0.h @@ -13,7 +13,7 @@ public: ~CClientProxy1_0(); // IClient overrides - virtual void open(); + virtual bool open(); virtual void run(); virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, @@ -34,6 +34,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCenter(SInt32& x, SInt32& y) const; + virtual void getMousePos(SInt32& x, SInt32& y) const; virtual SInt32 getJumpZoneSize() const; private: diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index 57f11963..a36aaa83 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -149,7 +149,7 @@ CPrimaryClient::onScreenSaver(bool activated) m_server->onScreenSaver(activated); } -void +bool CPrimaryClient::open() { // all clipboards are clean and owned by us @@ -160,6 +160,8 @@ CPrimaryClient::open() // now open the screen m_screen->open(); + + return true; } void @@ -296,6 +298,12 @@ CPrimaryClient::getCenter(SInt32& x, SInt32& y) const y = m_info.m_my; } +void +CPrimaryClient::getMousePos(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + SInt32 CPrimaryClient::getJumpZoneSize() const { diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h index bfc4244f..a8fd12d3 100644 --- a/server/CPrimaryClient.h +++ b/server/CPrimaryClient.h @@ -52,7 +52,7 @@ public: virtual void onScreenSaver(bool activated); // IClient overrides - virtual void open(); + virtual bool open(); virtual void run(); virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, @@ -74,6 +74,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCenter(SInt32& x, SInt32& y) const; + virtual void getMousePos(SInt32& x, SInt32& y) const; virtual SInt32 getJumpZoneSize() const; private: diff --git a/server/CServer.cpp b/server/CServer.cpp index 8ddd9e13..89b72575 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -21,14 +21,6 @@ #include "CLog.h" #include "CStopwatch.h" #include "TMethodJob.h" -#include - -// hack to work around operator=() bug in STL in g++ prior to v3 -#if defined(__GNUC__) && (__GNUC__ < 3) -#define assign(_dst, _src, _type) _dst.reset(_src) -#else -#define assign(_dst, _src, _type) _dst = std::auto_ptr<_type >(_src) -#endif // // CServer @@ -1106,11 +1098,11 @@ CServer::acceptClients(void*) { log((CLOG_DEBUG1 "starting to wait for clients")); - std::auto_ptr listen; + IListenSocket* listen = NULL; try { // create socket listener // listen = std::auto_ptr(m_socketFactory->createListen()); - assign(listen, new CTCPListenSocket, IListenSocket); // FIXME + listen = new CTCPListenSocket; // FIXME -- use factory // bind to the desired port. keep retrying if we can't bind // the address immediately. @@ -1147,11 +1139,19 @@ CServer::acceptClients(void*) startThread(new TMethodJob( this, &CServer::runClient, socket)); } + + // clean up + delete listen; } catch (XBase& e) { log((CLOG_ERR "cannot listen for clients: %s", e.what())); + delete listen; quit(); } + catch (...) { + delete listen; + throw; + } } void @@ -1159,12 +1159,20 @@ CServer::runClient(void* vsocket) { // get the socket pointer from the argument assert(vsocket != NULL); - std::auto_ptr socket(reinterpret_cast(vsocket)); + IDataSocket* socket = reinterpret_cast(vsocket); // create proxy - CClientProxy* proxy = handshakeClient(socket.get()); - if (proxy == NULL) { - return; + CClientProxy* proxy = NULL; + try { + proxy = handshakeClient(socket); + if (proxy == NULL) { + delete socket; + return; + } + } + catch (...) { + delete socket; + throw; } // add the connection @@ -1181,6 +1189,7 @@ CServer::runClient(void* vsocket) log((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); delete proxy; + delete socket; return; } catch (XUnknownClient& e) { @@ -1188,10 +1197,12 @@ CServer::runClient(void* vsocket) log((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); delete proxy; + delete socket; return; } catch (...) { delete proxy; + delete socket; throw; } @@ -1222,11 +1233,13 @@ CServer::runClient(void* vsocket) catch (...) { // run() was probably cancelled removeConnection(proxy->getName()); + delete socket; throw; } - // remove the connection + // clean up removeConnection(proxy->getName()); + delete socket; } CClientProxy* @@ -1244,13 +1257,14 @@ CServer::handshakeClient(IDataSocket* socket) /* FIXME -- implement ISecurityFactory input = m_securityFactory->createInputFilter(input, own); output = m_securityFactory->createOutputFilter(output, own); - own = true; + own = true; */ } // attach the packetizing filters input = new CInputPacketStream(input, own); output = new COutputPacketStream(output, own); + own = true; CClientProxy* proxy = NULL; CString name(""); @@ -1344,7 +1358,7 @@ CServer::handshakeClient(IDataSocket* socket) if (proxy != NULL) { delete proxy; } - else { + else if (own) { delete input; delete output; } @@ -1355,7 +1369,7 @@ CServer::handshakeClient(IDataSocket* socket) if (proxy != NULL) { delete proxy; } - else { + else if (own) { delete input; delete output; } @@ -1368,11 +1382,11 @@ CServer::acceptHTTPClients(void*) { log((CLOG_DEBUG1 "starting to wait for HTTP clients")); - std::auto_ptr listen; + IListenSocket* listen = NULL; try { // create socket listener // listen = std::auto_ptr(m_socketFactory->createListen()); - assign(listen, new CTCPListenSocket, IListenSocket); // FIXME + listen = new CTCPListenSocket; // FIXME -- use factory // bind to the desired port. keep retrying if we can't bind // the address immediately. @@ -1419,12 +1433,20 @@ CServer::acceptHTTPClients(void*) startThread(new TMethodJob( this, &CServer::processHTTPRequest, socket)); } + + // clean up + delete listen; } catch (XBase& e) { log((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); + delete listen; // FIXME -- quit? quit(); } + catch (...) { + delete listen; + throw; + } } void diff --git a/synergy/IClient.h b/synergy/IClient.h index d538294f..2c363368 100644 --- a/synergy/IClient.h +++ b/synergy/IClient.h @@ -11,8 +11,8 @@ class IClient : public IInterface { public: // manipulators - // open client - virtual void open() = 0; + // open client. return true iff successful. + virtual bool open() = 0; // service client virtual void run() = 0; @@ -68,6 +68,9 @@ public: // get the center pixel virtual void getCenter(SInt32& x, SInt32& y) const = 0; + // get the mouse position + virtual void getMousePos(SInt32& x, SInt32& y) const = 0; + // get the size of jump zone virtual SInt32 getJumpZoneSize() const = 0; }; diff --git a/synergy/Makefile.am b/synergy/Makefile.am index 78a56b29..58a9a15f 100644 --- a/synergy/Makefile.am +++ b/synergy/Makefile.am @@ -20,7 +20,6 @@ libsynergy_a_SOURCES = \ IClient.h \ IClipboard.h \ IScreenSaver.h \ - ISecondaryScreen.h \ IServer.h \ ISocketFactory.h \ KeyTypes.h \ From 3468f3d5030de04723b17bdcd75f0882ca902dcc Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 10 Jul 2002 21:22:28 +0000 Subject: [PATCH 224/807] more refactoring. --- client/CClient.cpp | 45 ++++++++-------- client/CClient.h | 30 ++++------- client/CServerProxy.cpp | 82 +++++------------------------ client/CServerProxy.h | 27 +++------- client/CXWindowsSecondaryScreen.cpp | 19 ++----- client/CXWindowsSecondaryScreen.h | 8 +-- client/ISecondaryScreen.h | 7 ++- server/CPrimaryClient.cpp | 70 ++---------------------- server/CPrimaryClient.h | 25 +++------ server/CServer.h | 10 ++-- server/CXWindowsPrimaryScreen.cpp | 53 +++++++++++-------- server/CXWindowsPrimaryScreen.h | 9 ++-- synergy/IPrimaryScreenReceiver.h | 28 ++++++++++ synergy/IScreenReceiver.h | 23 ++++++++ synergy/IServer.h | 14 ++--- synergy/Makefile.am | 2 + 16 files changed, 176 insertions(+), 276 deletions(-) create mode 100644 synergy/IPrimaryScreenReceiver.h create mode 100644 synergy/IScreenReceiver.h diff --git a/client/CClient.cpp b/client/CClient.cpp index 09b31c0f..fefd20c7 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -28,7 +28,6 @@ CClient::CClient(const CString& clientName) : m_server(NULL), m_camp(false), m_active(false), - m_seqNum(0), m_rejected(true) { // do nothing @@ -66,18 +65,29 @@ CClient::wasRejected() const } void -CClient::onClipboardChanged(ClipboardID id) +CClient::onInfoChanged(const CClientInfo& info) +{ + log((CLOG_DEBUG "resolution changed")); + + CLock lock(&m_mutex); + if (m_server != NULL) { + m_server->onInfoChanged(info); + } +} + +bool +CClient::onGrabClipboard(ClipboardID id) { CLock lock(&m_mutex); if (m_server == NULL) { // m_server can be NULL if the screen calls this method // before we've gotten around to connecting to the server. // we simply ignore the clipboard change in that case. - return; + return false; } // grab ownership - m_server->onGrabClipboard(m_name, id, m_seqNum); + m_server->onGrabClipboard(id); // we now own the clipboard and it has not been sent to the server m_ownClipboard[id] = true; @@ -88,21 +98,14 @@ CClient::onClipboardChanged(ClipboardID id) if (!m_active) { sendClipboard(id); } + + return true; } void -CClient::onResolutionChanged() +CClient::onClipboardChanged(ClipboardID, const CString&) { - log((CLOG_DEBUG "resolution changed")); - - CLock lock(&m_mutex); - if (m_server != NULL) { - CClientInfo info; - m_screen->getShape(info.m_x, info.m_y, info.m_w, info.m_h); - m_screen->getMousePos(info.m_mx, info.m_my); - info.m_zoneSize = m_screen->getJumpZoneSize(); - m_server->onInfoChanged("", info); - } + // ignore -- we'll check the clipboard when we leave } bool @@ -194,7 +197,6 @@ CClient::enter(SInt32 xAbs, SInt32 yAbs, { CLock lock(&m_mutex); m_active = true; - m_seqNum = seqNum; } m_screen->enter(xAbs, yAbs, mask); @@ -337,9 +339,6 @@ CClient::openSecondaryScreen() // not active m_active = false; - // reset last sequence number - m_seqNum = 0; - // reset clipboard state for (ClipboardID id = 0; id < kClipboardEnd; ++id) { m_ownClipboard[id] = false; @@ -349,12 +348,12 @@ CClient::openSecondaryScreen() // open screen log((CLOG_DEBUG1 "creating secondary screen")); #if WINDOWS_LIKE - m_screen = new CMSWindowsSecondaryScreen; + m_screen = new CMSWindowsSecondaryScreen(this); #elif UNIX_LIKE - m_screen = new CXWindowsSecondaryScreen; + m_screen = new CXWindowsSecondaryScreen(this); #endif log((CLOG_DEBUG1 "opening secondary screen")); - m_screen->open(this); + m_screen->open(); } void @@ -406,7 +405,7 @@ CClient::sendClipboard(ClipboardID id) // save and send data if different if (data != m_dataClipboard[id]) { m_dataClipboard[id] = data; - m_server->onClipboardChanged(id, m_seqNum, data); + m_server->onClipboardChanged(id, data); } } } diff --git a/client/CClient.h b/client/CClient.h index 73ef8714..b36fa98c 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -1,6 +1,7 @@ #ifndef CCLIENT_H #define CCLIENT_H +#include "IScreenReceiver.h" #include "IClient.h" #include "IClipboard.h" #include "CNetworkAddress.h" @@ -10,9 +11,9 @@ class CServerProxy; class CThread; class IDataSocket; class ISecondaryScreen; -class IServer; +class IScreenReceiver; -class CClient : public IClient { +class CClient : public IScreenReceiver, public IClient { public: CClient(const CString& clientName); ~CClient(); @@ -32,31 +33,21 @@ public: // after a successful open(). void quit(); - // handle events on client's screen -// FIXME -- this should mimic methods on IServer -// FIXME -- maybe create a IScreenReceiver with these methods and -// have CPrimaryClient and CClient inherit from them. IServer -// still needs similar methods with extra parameters, though. so -// CServerProxy -// CPrimaryClient -// CClient -// need IScreenReceiver. these classes effective receive notifications -// from screens. note that there's another class of notifications that -// only the server needs (key, mouyse, screensaver). so maybe we have -// IPrimaryScreenReceiver and ISecondaryScreenReceiver (the latter is -// derived with no extra methods from IScreenReceiver). - void onClipboardChanged(ClipboardID); - void onResolutionChanged(); - // accessors // returns true if the server rejected us bool wasRejected() const; + // IScreenReceiver overrides + virtual void onInfoChanged(const CClientInfo&); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const CString&); + // IClient overrides virtual bool open(); virtual void run(); virtual void close(); +// FIXME -- can we avoid passing everything here? virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, bool screenSaver); @@ -97,11 +88,10 @@ private: CMutex m_mutex; CString m_name; ISecondaryScreen* m_screen; - IServer* m_server; + IScreenReceiver* m_server; CNetworkAddress m_serverAddress; bool m_camp; bool m_active; - UInt32 m_seqNum; bool m_rejected; bool m_ownClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; diff --git a/client/CServerProxy.cpp b/client/CServerProxy.cpp index b8e5866d..759cafdc 100644 --- a/client/CServerProxy.cpp +++ b/client/CServerProxy.cpp @@ -18,7 +18,8 @@ CServerProxy::CServerProxy(IClient* client, IInputStream* input, IOutputStream* output) : m_client(client), m_input(input), - m_output(output) + m_output(output), + m_seqNum(0) { assert(m_client != NULL); assert(m_input != NULL); @@ -40,7 +41,10 @@ CServerProxy::run() m_compressMouse = false; // not ignoring mouse motions - m_ignoreMouse = false; + m_ignoreMouse = false; + + // reset sequence number + m_seqNum = 0; // handle messages from server CStopwatch heartbeat; @@ -218,13 +222,7 @@ CServerProxy::getOutputStream() const } void -CServerProxy::onError() -{ - // ignore -} - -void -CServerProxy::onInfoChanged(const CString&, const CClientInfo& info) +CServerProxy::onInfoChanged(const CClientInfo& info) { // ignore mouse motion until we receive acknowledgment of our info // change message. @@ -236,74 +234,19 @@ CServerProxy::onInfoChanged(const CString&, const CClientInfo& info) } bool -CServerProxy::onGrabClipboard(const CString&, ClipboardID id, UInt32 seqNum) +CServerProxy::onGrabClipboard(ClipboardID id) { log((CLOG_DEBUG1 "sending clipboard %d changed", id)); CLock lock(&m_mutex); - CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, seqNum); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, m_seqNum); } void -CServerProxy::onClipboardChanged(ClipboardID id, - UInt32 seqNum, const CString& data) +CServerProxy::onClipboardChanged(ClipboardID id, const CString& data) { - log((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, seqNum, data.size())); CLock lock(&m_mutex); - CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, seqNum, &data); -} - -void -CServerProxy::onKeyDown(KeyID, KeyModifierMask) -{ - // ignore -} - -void -CServerProxy::onKeyUp(KeyID, KeyModifierMask) -{ - // ignore -} - -void -CServerProxy::onKeyRepeat(KeyID, KeyModifierMask, SInt32) -{ - // ignore -} - -void -CServerProxy::onMouseDown(ButtonID) -{ - // ignore -} - -void -CServerProxy::onMouseUp(ButtonID) -{ - // ignore -} - -bool -CServerProxy::onMouseMovePrimary(SInt32, SInt32) -{ - return false; -} - -void -CServerProxy::onMouseMoveSecondary(SInt32, SInt32) -{ - // ignore -} - -void -CServerProxy::onMouseWheel(SInt32) -{ - // ignore -} - -void -CServerProxy::onScreenSaver(bool) -{ - // ignore + log((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, m_seqNum, &data); } void @@ -353,6 +296,7 @@ CServerProxy::enter() { CLock lock(&m_mutex); m_compressMouse = false; + m_seqNum = seqNum; } // forward diff --git a/client/CServerProxy.h b/client/CServerProxy.h index c404f7ed..198b3d9b 100644 --- a/client/CServerProxy.h +++ b/client/CServerProxy.h @@ -1,14 +1,14 @@ #ifndef CSERVERPROXY_H #define CSERVERPROXY_H -#include "IServer.h" +#include "IScreenReceiver.h" #include "CMutex.h" class IClient; class IInputStream; class IOutputStream; -class CServerProxy : public IServer { +class CServerProxy : public IScreenReceiver { public: CServerProxy(IClient* client, IInputStream* adoptedInput, @@ -33,23 +33,10 @@ public: IInputStream* getInputStream() const; IOutputStream* getOutputStream() const; - // IServer overrides - virtual void onError(); - virtual void onInfoChanged(const CString& clientName, - const CClientInfo&); - virtual bool onGrabClipboard(const CString& clientName, - ClipboardID, UInt32 seqNum); - virtual void onClipboardChanged(ClipboardID, - UInt32 seqNum, const CString& data); - virtual void onKeyDown(KeyID, KeyModifierMask); - virtual void onKeyUp(KeyID, KeyModifierMask); - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void onMouseDown(ButtonID); - virtual void onMouseUp(ButtonID); - virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); - virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); - virtual void onMouseWheel(SInt32 delta); - virtual void onScreenSaver(bool activated); + // IScreenReceiver overrides + virtual void onInfoChanged(const CClientInfo&); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const CString& data); private: // if compressing mouse motion then send the last motion now @@ -80,6 +67,8 @@ private: IInputStream* m_input; IOutputStream* m_output; + UInt32 m_seqNum; + bool m_compressMouse; SInt32 m_xMouse, m_yMouse; diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 32eb1419..46b4d7a2 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -24,8 +24,8 @@ // CXWindowsSecondaryScreen // -CXWindowsSecondaryScreen::CXWindowsSecondaryScreen() : - m_client(NULL), +CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) : + m_receiver(receiver), m_window(None) { // do nothing @@ -82,13 +82,9 @@ CXWindowsSecondaryScreen::stop() } void -CXWindowsSecondaryScreen::open(CClient* client) +CXWindowsSecondaryScreen::open() { - assert(m_client == NULL); - assert(client != NULL); - - // set the client - m_client = client; + assert(m_receiver != NULL); // open the display openDisplay(); @@ -128,8 +124,6 @@ CXWindowsSecondaryScreen::open(CClient* client) void CXWindowsSecondaryScreen::close() { - assert(m_client != NULL); - // release keys that are logically pressed releaseKeys(); @@ -138,9 +132,6 @@ CXWindowsSecondaryScreen::close() // close the display closeDisplay(); - - // done with client - m_client = NULL; } void @@ -396,7 +387,7 @@ void CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id) { // tell client that the clipboard was grabbed locally - m_client->onClipboardChanged(id); + m_receiver->onGrabClipboard(id); } void diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 44343d23..a8e4fc99 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -6,16 +6,18 @@ #include "stdmap.h" #include "stdvector.h" +class IScreenReceiver; + class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen { public: - CXWindowsSecondaryScreen(); + CXWindowsSecondaryScreen(IScreenReceiver*); virtual ~CXWindowsSecondaryScreen(); // ISecondaryScreen overrides virtual void run(); virtual void stop(); - virtual void open(CClient*); + virtual void open(); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, KeyModifierMask mask); @@ -81,7 +83,7 @@ private: static bool isToggleKeysym(KeySym); private: - CClient* m_client; + IScreenReceiver* m_receiver; Window m_window; // note toggle keys that toggles on up/down (false) or on diff --git a/client/ISecondaryScreen.h b/client/ISecondaryScreen.h index 66fed53d..fe0264f7 100644 --- a/client/ISecondaryScreen.h +++ b/client/ISecondaryScreen.h @@ -6,7 +6,6 @@ #include "KeyTypes.h" #include "MouseTypes.h" -class CClient; class IClipboard; class ISecondaryScreen : public IInterface { @@ -23,9 +22,9 @@ public: virtual void stop() = 0; // initialize the screen, hide the cursor, and disable the screen - // saver. start reporting certain events to the client (clipboard - // stolen and screen size changed). - virtual void open(CClient*) = 0; + // saver. start reporting events to the IScreenReceiver (which is + // set through some other interface). + virtual void open() = 0; // close the screen. should restore the screen saver. it should // also simulate key up events for any keys that have simulate key diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index a36aaa83..9e71f011 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -25,9 +25,9 @@ CPrimaryClient::CPrimaryClient(IServer* server, const CString& name) : // create screen log((CLOG_DEBUG1 "creating primary screen")); #if WINDOWS_LIKE - m_screen = new CMSWindowsPrimaryScreen(this); + m_screen = new CMSWindowsPrimaryScreen(this, m_server); #elif UNIX_LIKE - m_screen = new CXWindowsPrimaryScreen(this); + m_screen = new CXWindowsPrimaryScreen(this, m_server); #endif } @@ -69,20 +69,14 @@ CPrimaryClient::getToggleMask() const } void -CPrimaryClient::onError() -{ - m_server->onError(); -} - -void -CPrimaryClient::onInfoChanged(const CString&, const CClientInfo& info) +CPrimaryClient::onInfoChanged(const CClientInfo& info) { m_info = info; m_server->onInfoChanged(getName(), m_info); } bool -CPrimaryClient::onGrabClipboard(const CString&, ClipboardID id, UInt32) +CPrimaryClient::onGrabClipboard(ClipboardID id) { bool result = m_server->onGrabClipboard(getName(), id, m_seqNum); m_clipboardOwner[id] = result; @@ -90,65 +84,11 @@ CPrimaryClient::onGrabClipboard(const CString&, ClipboardID id, UInt32) } void -CPrimaryClient::onClipboardChanged(ClipboardID id, UInt32, const CString& data) +CPrimaryClient::onClipboardChanged(ClipboardID id, const CString& data) { m_server->onClipboardChanged(id, m_seqNum, data); } -void -CPrimaryClient::onKeyDown(KeyID id, KeyModifierMask mask) -{ - m_server->onKeyDown(id, mask); -} - -void -CPrimaryClient::onKeyUp(KeyID id, KeyModifierMask mask) -{ - m_server->onKeyUp(id, mask); -} - -void -CPrimaryClient::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) -{ - m_server->onKeyRepeat(id, mask, count); -} - -void -CPrimaryClient::onMouseDown(ButtonID id) -{ - m_server->onMouseDown(id); -} - -void -CPrimaryClient::onMouseUp(ButtonID id) -{ - m_server->onMouseUp(id); -} - -bool -CPrimaryClient::onMouseMovePrimary(SInt32 x, SInt32 y) -{ - return m_server->onMouseMovePrimary(x, y); -} - -void -CPrimaryClient::onMouseMoveSecondary(SInt32 dx, SInt32 dy) -{ - m_server->onMouseMoveSecondary(dx, dy); -} - -void -CPrimaryClient::onMouseWheel(SInt32 delta) -{ - m_server->onMouseWheel(delta); -} - -void -CPrimaryClient::onScreenSaver(bool activated) -{ - m_server->onScreenSaver(activated); -} - bool CPrimaryClient::open() { diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h index a8fd12d3..1607cc00 100644 --- a/server/CPrimaryClient.h +++ b/server/CPrimaryClient.h @@ -1,15 +1,15 @@ #ifndef CPRIMARYCLIENT_H #define CPRIMARYCLIENT_H -#include "IServer.h" #include "IClient.h" +#include "IScreenReceiver.h" #include "ProtocolTypes.h" class IClipboard; class IPrimaryScreen; class IServer; -class CPrimaryClient : public IServer, public IClient { +class CPrimaryClient : public IScreenReceiver, public IClient { public: CPrimaryClient(IServer*, const CString& name); ~CPrimaryClient(); @@ -33,23 +33,10 @@ public: // returns the state of the toggle keys on the primary screen KeyModifierMask getToggleMask() const; - // IServer overrides - // onInfoChanged() ignores the client name. - // onGrabClipboard() ignores the client name and sequence number. - // onClipboardChanged() ignores the sequence number. - virtual void onError(); - virtual void onInfoChanged(const CString&, const CClientInfo&); - virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); - virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); - virtual void onKeyDown(KeyID, KeyModifierMask); - virtual void onKeyUp(KeyID, KeyModifierMask); - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void onMouseDown(ButtonID); - virtual void onMouseUp(ButtonID); - virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); - virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); - virtual void onMouseWheel(SInt32 delta); - virtual void onScreenSaver(bool activated); + // IScreenReceiver overrides + virtual void onInfoChanged(const CClientInfo&); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const CString&); // IClient overrides virtual bool open(); diff --git a/server/CServer.h b/server/CServer.h index 74056d3e..73f1523d 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -53,11 +53,8 @@ public: // get the sides of the primary screen that have neighbors UInt32 getActivePrimarySides() const; - // IServer overrides + // IPrimaryScreenReceiver overrides virtual void onError(); - virtual void onInfoChanged(const CString&, const CClientInfo&); - virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); - virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); virtual void onKeyDown(KeyID, KeyModifierMask); virtual void onKeyUp(KeyID, KeyModifierMask); virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -68,6 +65,11 @@ public: virtual void onMouseWheel(SInt32 delta); virtual void onScreenSaver(bool activated); + // IServer overrides + virtual void onInfoChanged(const CString&, const CClientInfo&); + virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); + virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); + protected: bool onCommandKey(KeyID, KeyModifierMask, bool down); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index fb5b179a..6677f6ac 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -1,9 +1,10 @@ #include "CXWindowsPrimaryScreen.h" +#include "IScreenReceiver.h" +#include "IPrimaryScreenReceiver.h" #include "CXWindowsClipboard.h" #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CClipboard.h" -#include "IServer.h" #include "ProtocolTypes.h" #include "CThread.h" #include "CLog.h" @@ -21,8 +22,11 @@ // CXWindowsPrimaryScreen // -CXWindowsPrimaryScreen::CXWindowsPrimaryScreen(IServer* server) : - m_server(server), +CXWindowsPrimaryScreen::CXWindowsPrimaryScreen( + IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) : + m_receiver(receiver), + m_primaryReceiver(primaryReceiver), m_active(false), m_window(None) { @@ -67,7 +71,7 @@ CXWindowsPrimaryScreen::run() if (xevent.xclient.message_type == m_atomScreenSaver || xevent.xclient.format == 32) { // screen saver activation/deactivation event - m_server->onScreenSaver(xevent.xclient.data.l[0] != 0); + m_primaryReceiver->onScreenSaver(xevent.xclient.data.l[0] != 0); } break; @@ -77,12 +81,12 @@ CXWindowsPrimaryScreen::run() const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { - m_server->onKeyDown(key, mask); + m_primaryReceiver->onKeyDown(key, mask); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_server->onKeyUp(key, mask | KeyModifierCapsLock); + m_primaryReceiver->onKeyUp(key, mask | KeyModifierCapsLock); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_server->onKeyUp(key, mask | KeyModifierNumLock); + m_primaryReceiver->onKeyUp(key, mask | KeyModifierNumLock); } } } @@ -112,12 +116,12 @@ CXWindowsPrimaryScreen::run() // no press event follows so it's a plain release log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_server->onKeyDown(key, mask); + m_primaryReceiver->onKeyDown(key, mask); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_server->onKeyDown(key, mask); + m_primaryReceiver->onKeyDown(key, mask); } - m_server->onKeyUp(key, mask); + m_primaryReceiver->onKeyUp(key, mask); } else { // found a press event following so it's a repeat. @@ -125,7 +129,7 @@ CXWindowsPrimaryScreen::run() // repeats but we'll just send a repeat of 1. // note that we discard the press event. log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - m_server->onKeyRepeat(key, mask, 1); + m_primaryReceiver->onKeyRepeat(key, mask, 1); } } } @@ -136,7 +140,7 @@ CXWindowsPrimaryScreen::run() log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_server->onMouseDown(button); + m_primaryReceiver->onMouseDown(button); } } break; @@ -146,15 +150,15 @@ CXWindowsPrimaryScreen::run() log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_server->onMouseUp(button); + m_primaryReceiver->onMouseUp(button); } else if (xevent.xbutton.button == 4) { // wheel forward (away from user) - m_server->onMouseWheel(120); + m_primaryReceiver->onMouseWheel(120); } else if (xevent.xbutton.button == 5) { // wheel backward (toward user) - m_server->onMouseWheel(-120); + m_primaryReceiver->onMouseWheel(-120); } } break; @@ -185,7 +189,7 @@ CXWindowsPrimaryScreen::run() } else if (!m_active) { // motion on primary screen - m_server->onMouseMovePrimary(m_x, m_y); + m_primaryReceiver->onMouseMovePrimary(m_x, m_y); } else { // motion on secondary screen. warp mouse back to @@ -216,7 +220,7 @@ CXWindowsPrimaryScreen::run() // warping to the primary screen's enter position, // effectively overriding it. if (x != 0 || y != 0) { - m_server->onMouseMoveSecondary(x, y); + m_primaryReceiver->onMouseMoveSecondary(x, y); } } } @@ -287,7 +291,7 @@ CXWindowsPrimaryScreen::open() info.m_zoneSize = 1; info.m_mx = m_x; info.m_my = m_y; - m_server->onInfoChanged("", info); + m_receiver->onInfoChanged(info); } void @@ -498,12 +502,15 @@ CXWindowsPrimaryScreen::getToggleMask() const // convert to KeyModifierMask KeyModifierMask mask = 0; - if (state & m_numLockMask) + if (state & m_numLockMask) { mask |= KeyModifierNumLock; - if (state & m_capsLockMask) + } + if (state & m_capsLockMask) { mask |= KeyModifierCapsLock; - if (state & m_scrollLockMask) + } + if (state & m_scrollLockMask) { mask |= KeyModifierScrollLock; + } return mask; } @@ -595,14 +602,14 @@ void CXWindowsPrimaryScreen::onUnexpectedClose() { // tell server to shutdown - m_server->onError(); + m_primaryReceiver->onError(); } void CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id) { // tell server that the clipboard was grabbed locally - m_server->onGrabClipboard("", id, 0); + m_receiver->onGrabClipboard(id); } void diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index e43af82b..5c82fc37 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -5,11 +5,12 @@ #include "IPrimaryScreen.h" #include "MouseTypes.h" -class IServer; +class IScreenReceiver; +class IPrimaryScreenReceiver; class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { public: - CXWindowsPrimaryScreen(IServer*); + CXWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); virtual ~CXWindowsPrimaryScreen(); // IPrimaryScreen overrides @@ -60,7 +61,9 @@ private: static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: - IServer* m_server; + IScreenReceiver* m_receiver; + IPrimaryScreenReceiver* m_primaryReceiver; + bool m_active; Window m_window; diff --git a/synergy/IPrimaryScreenReceiver.h b/synergy/IPrimaryScreenReceiver.h new file mode 100644 index 00000000..60758141 --- /dev/null +++ b/synergy/IPrimaryScreenReceiver.h @@ -0,0 +1,28 @@ +#ifndef IPRIMARYSCREENRECEIVER_H +#define IPRIMARYSCREENRECEIVER_H + +#include "IInterface.h" +#include "KeyTypes.h" +#include "MouseTypes.h" + +class IPrimaryScreenReceiver : public IInterface { +public: + // notify of serious error. this implies that the server should + // shutdown. + virtual void onError() = 0; + + // call to notify of events. onMouseMovePrimary() returns + // true iff the mouse enters a jump zone and jumps. + virtual void onKeyDown(KeyID, KeyModifierMask) = 0; + virtual void onKeyUp(KeyID, KeyModifierMask) = 0; + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void onMouseDown(ButtonID) = 0; + virtual void onMouseUp(ButtonID) = 0; + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; + virtual void onMouseWheel(SInt32 delta) = 0; + virtual void onScreenSaver(bool activated) = 0; + +}; + +#endif diff --git a/synergy/IScreenReceiver.h b/synergy/IScreenReceiver.h new file mode 100644 index 00000000..097e4405 --- /dev/null +++ b/synergy/IScreenReceiver.h @@ -0,0 +1,23 @@ +#ifndef ISCREENRECEIVER_H +#define ISCREENRECEIVER_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "ProtocolTypes.h" +#include "CString.h" + +class IScreenReceiver : public IInterface { +public: + // notify of client info change + virtual void onInfoChanged(const CClientInfo&) = 0; + + // notify of clipboard grab. returns true if the grab was honored, + // false otherwise. + virtual bool onGrabClipboard(ClipboardID) = 0; + + // notify of new clipboard data + virtual void onClipboardChanged(ClipboardID, + const CString& data) = 0; +}; + +#endif diff --git a/synergy/IServer.h b/synergy/IServer.h index 62d949bc..67033b59 100644 --- a/synergy/IServer.h +++ b/synergy/IServer.h @@ -1,22 +1,16 @@ #ifndef ISERVER_H #define ISERVER_H -#include "IInterface.h" +#include "IPrimaryScreenReceiver.h" #include "ClipboardTypes.h" -#include "KeyTypes.h" -#include "MouseTypes.h" #include "CString.h" class CClientInfo; -class IServer : public IInterface { +class IServer : public IPrimaryScreenReceiver { public: // manipulators - // notify of serious error. this implies that the server should - // shutdown. - virtual void onError() = 0; - // notify of client info change virtual void onInfoChanged(const CString& clientName, const CClientInfo&) = 0; @@ -30,8 +24,8 @@ public: virtual void onClipboardChanged(ClipboardID, UInt32 seqNum, const CString& data) = 0; - // call to notify of events. onMouseMovePrimary() returns - // true iff the mouse enters a jump zone and jumps. + // IPrimaryScreenReceiver overrides + virtual void onError() = 0; virtual void onKeyDown(KeyID, KeyModifierMask) = 0; virtual void onKeyUp(KeyID, KeyModifierMask) = 0; virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; diff --git a/synergy/Makefile.am b/synergy/Makefile.am index 58a9a15f..aedd0e68 100644 --- a/synergy/Makefile.am +++ b/synergy/Makefile.am @@ -19,6 +19,8 @@ libsynergy_a_SOURCES = \ ClipboardTypes.h \ IClient.h \ IClipboard.h \ + IPrimaryScreenReceiver.h\ + IScreenReceiver.h \ IScreenSaver.h \ IServer.h \ ISocketFactory.h \ From feeb15a08da358d82295f2fad61e7eaf1ed59c8b Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 11 Jul 2002 13:13:37 +0000 Subject: [PATCH 225/807] applied refactoring to win32 code. --- client/CClient.cpp | 5 +- client/CMSWindowsSecondaryScreen.cpp | 38 ++++---- client/CMSWindowsSecondaryScreen.h | 8 +- client/CServerProxy.cpp | 3 +- client/client.dsp | 16 ++++ server/CMSWindowsPrimaryScreen.cpp | 129 +++++++++++++-------------- server/CMSWindowsPrimaryScreen.h | 22 +++-- server/CPrimaryClient.cpp | 4 +- server/CPrimaryClient.h | 2 +- server/CServer.cpp | 18 ++-- server/CServer.h | 6 +- server/CSynergyHook.cpp | 38 ++++---- server/CSynergyHook.h | 12 +-- server/CXWindowsPrimaryScreen.cpp | 2 +- server/CXWindowsPrimaryScreen.h | 2 +- server/IPrimaryScreen.h | 8 +- server/server.dsp | 38 +++++--- synergy/synergy.dsp | 14 ++- 18 files changed, 201 insertions(+), 164 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index fefd20c7..15514275 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -191,8 +191,7 @@ CClient::close() } void -CClient::enter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask mask, bool) +CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool) { { CLock lock(&m_mutex); @@ -216,6 +215,8 @@ CClient::leave() sendClipboard(id); } } + + return true; } void diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 7bd1dee5..3b0ff758 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -1,5 +1,5 @@ #include "CMSWindowsSecondaryScreen.h" -#include "CClient.h" +#include "IScreenReceiver.h" #include "CClipboard.h" #include "CMSWindowsClipboard.h" #include "CMSWindowsScreenSaver.h" @@ -22,8 +22,9 @@ // CMSWindowsSecondaryScreen // -CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : - m_client(NULL), +CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( + IScreenReceiver* receiver) : + m_receiver(receiver), m_threadID(0), m_lastThreadID(0), m_desk(NULL), @@ -32,6 +33,8 @@ CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() : m_active(false), m_nextClipboardWindow(NULL) { + assert(m_receiver != NULL); + m_is95Family = CPlatform::isWindows95Family(); // make sure this thread has a message queue @@ -78,14 +81,8 @@ CMSWindowsSecondaryScreen::stop() } void -CMSWindowsSecondaryScreen::open(CClient* client) +CMSWindowsSecondaryScreen::open() { - assert(m_client == NULL); - assert(client != NULL); - - // set the client - m_client = client; - // open the display openDisplay(); @@ -109,8 +106,6 @@ CMSWindowsSecondaryScreen::open(CClient* client) void CMSWindowsSecondaryScreen::close() { - assert(m_client != NULL); - // release keys that are logically pressed releaseKeys(); @@ -119,9 +114,6 @@ CMSWindowsSecondaryScreen::close() // close the display closeDisplay(); - - // done with client - m_client = NULL; } void @@ -189,8 +181,8 @@ CMSWindowsSecondaryScreen::leave() if (m_clipboardOwner != clipboardOwner) { m_clipboardOwner = clipboardOwner; if (m_clipboardOwner != m_window) { - m_client->onClipboardChanged(kClipboardClipboard); - m_client->onClipboardChanged(kClipboardSelection); + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); } } } @@ -555,8 +547,8 @@ CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg, // window to do that). m_clipboardOwner = GetClipboardOwner(); if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_client->onClipboardChanged(kClipboardClipboard); - m_client->onClipboardChanged(kClipboardSelection); + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); } return 0; @@ -573,7 +565,13 @@ CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg, // screen resolution has changed updateScreenShape(); m_multimon = isMultimon(); - m_client->onResolutionChanged(); + + // send new info + CClientInfo info; + getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getMousePos(info.m_mx, info.m_my); + info.m_zoneSize = getJumpZoneSize(); + m_receiver->onInfoChanged(info); return 0; } diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 6db408f2..8feb6d3f 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -13,16 +13,18 @@ #include "CString.h" #include "stdvector.h" +class IScreenReceiver; + class CMSWindowsSecondaryScreen : public CMSWindowsScreen, public ISecondaryScreen { public: - CMSWindowsSecondaryScreen(); + CMSWindowsSecondaryScreen(IScreenReceiver*); virtual ~CMSWindowsSecondaryScreen(); // ISecondaryScreen overrides virtual void run(); virtual void stop(); - virtual void open(CClient*); + virtual void open(); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, KeyModifierMask mask); @@ -95,7 +97,7 @@ private: private: CMutex m_mutex; - CClient* m_client; + IScreenReceiver* m_receiver; // true if windows 95/98/me bool m_is95Family; diff --git a/client/CServerProxy.cpp b/client/CServerProxy.cpp index 759cafdc..3b8216a9 100644 --- a/client/CServerProxy.cpp +++ b/client/CServerProxy.cpp @@ -239,6 +239,7 @@ CServerProxy::onGrabClipboard(ClipboardID id) log((CLOG_DEBUG1 "sending clipboard %d changed", id)); CLock lock(&m_mutex); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, m_seqNum); + return true; } void @@ -253,7 +254,7 @@ void CServerProxy::flushCompressedMouse() { bool send = false; - SInt32 x, y; + SInt32 x = 0, y = 0; { CLock lock(&m_mutex); if (m_compressMouse) { diff --git a/client/client.dsp b/client/client.dsp index e5755909..7311f204 100644 --- a/client/client.dsp +++ b/client/client.dsp @@ -108,6 +108,10 @@ SOURCE=.\client.rc SOURCE=.\CMSWindowsSecondaryScreen.cpp # End Source File +# Begin Source File + +SOURCE=.\CServerProxy.cpp +# End Source File # End Group # Begin Group "Header Files" @@ -120,6 +124,18 @@ SOURCE=.\CClient.h SOURCE=.\CMSWindowsSecondaryScreen.h # End Source File +# Begin Source File + +SOURCE=.\CServerProxy.h +# End Source File +# Begin Source File + +SOURCE=.\ISecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File # End Group # Begin Group "Resource Files" diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 69ead88b..c56e93c7 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -1,8 +1,11 @@ #include "CMSWindowsPrimaryScreen.h" -#include "CServer.h" +#include "IScreenReceiver.h" +#include "IPrimaryScreenReceiver.h" #include "CMSWindowsClipboard.h" #include "CMSWindowsScreenSaver.h" #include "CPlatform.h" +#include "CClipboard.h" +#include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" #include "CThread.h" @@ -13,8 +16,11 @@ // CMSWindowsPrimaryScreen // -CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : - m_server(NULL), +CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( + IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) : + m_receiver(receiver), + m_primaryReceiver(primaryReceiver), m_threadID(0), m_desk(NULL), m_deskName(), @@ -25,19 +31,24 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() : m_nextClipboardWindow(NULL), m_clipboardOwner(NULL) { + assert(m_receiver != NULL); + assert(m_primaryReceiver != NULL); + // load the hook library m_hookLibrary = LoadLibrary("synrgyhk"); if (m_hookLibrary == NULL) { log((CLOG_ERR "failed to load hook library")); throw XScreenOpenFailure(); } + m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides"); m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone"); m_setRelay = (SetRelayFunc)GetProcAddress(m_hookLibrary, "setRelay"); m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install"); m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall"); m_init = (InitFunc)GetProcAddress(m_hookLibrary, "init"); m_cleanup = (CleanupFunc)GetProcAddress(m_hookLibrary, "cleanup"); - if (m_setZone == NULL || + if (m_setSides == NULL || + m_setZone == NULL || m_setRelay == NULL || m_install == NULL || m_uninstall == NULL || @@ -114,14 +125,8 @@ CMSWindowsPrimaryScreen::stop() } void -CMSWindowsPrimaryScreen::open(CServer* server) +CMSWindowsPrimaryScreen::open() { - assert(m_server == NULL); - assert(server != NULL); - - // set the server - m_server = server; - // open the display openDisplay(); @@ -137,9 +142,12 @@ CMSWindowsPrimaryScreen::open(CServer* server) m_y = pos.y; // send screen info - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - m_server->setInfo(x, y, w, h, getJumpZoneSize(), m_x, m_y); + CClientInfo info; + getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); + info.m_zoneSize = getJumpZoneSize(); + info.m_mx = m_x; + info.m_my = m_y; + m_receiver->onInfoChanged(info); // compute center pixel of primary screen m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; @@ -148,6 +156,9 @@ CMSWindowsPrimaryScreen::open(CServer* server) // get keyboard state updateKeys(); + // set jump zones + m_setZone(info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize); + // enter the screen enterNoWarp(); } @@ -155,13 +166,8 @@ CMSWindowsPrimaryScreen::open(CServer* server) void CMSWindowsPrimaryScreen::close() { - assert(m_server != NULL); - // close the display closeDisplay(); - - // done with server - m_server = NULL; } void @@ -194,7 +200,7 @@ CMSWindowsPrimaryScreen::leave() } // relay all mouse and keyboard events - m_setRelay(); + m_setRelay(true); // get state of keys as we leave updateKeys(); @@ -239,8 +245,8 @@ CMSWindowsPrimaryScreen::leave() try { m_clipboardOwner = clipboardOwner; if (m_clipboardOwner != m_window) { - m_server->grabClipboard(kClipboardClipboard); - m_server->grabClipboard(kClipboardSelection); + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); } } catch (XBadClient&) { @@ -252,14 +258,9 @@ CMSWindowsPrimaryScreen::leave() } void -CMSWindowsPrimaryScreen::onConfigure() +CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) { - if ((m_is95Family || m_desk != NULL) && !m_active) { - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - m_setZone(m_server->getActivePrimarySides(), - x, y, w, h, getJumpZoneSize()); - } + m_setSides(activeSides); } void @@ -308,19 +309,6 @@ CMSWindowsPrimaryScreen::grabClipboard(ClipboardID /*id*/) } } -void -CMSWindowsPrimaryScreen::getShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - getScreenShape(x, y, w, h); -} - -SInt32 -CMSWindowsPrimaryScreen::getJumpZoneSize() const -{ - return 1; -} - void CMSWindowsPrimaryScreen::getClipboard(ClipboardID /*id*/, IClipboard* dst) const @@ -390,17 +378,10 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const return false; } -bool -CMSWindowsPrimaryScreen::isScreenSaverActive() const -{ - return getScreenSaver()->isActive(); -} - void CMSWindowsPrimaryScreen::onOpenDisplay() { assert(m_window == NULL); - assert(m_server != NULL); // save thread id. we'll need to pass this to the hook library. m_threadID = GetCurrentThreadId(); @@ -478,11 +459,11 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); if (repeat >= 2) { log((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); - m_server->onKeyRepeat(key, mask, repeat); + m_primaryReceiver->onKeyRepeat(key, mask, repeat); } else { log((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x", key, mask)); - m_server->onKeyDown(key, mask); + m_primaryReceiver->onKeyDown(key, mask); } // update key state @@ -491,7 +472,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) else { // key release log((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask)); - m_server->onKeyUp(key, mask); + m_primaryReceiver->onKeyUp(key, mask); // update key state updateKey(msg->wParam, false); @@ -520,7 +501,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) case WM_RBUTTONDOWN: log((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { - m_server->onMouseDown(button); + m_primaryReceiver->onMouseDown(button); m_keys[s_vkButton[button]] |= 0x80; } break; @@ -530,7 +511,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) case WM_RBUTTONUP: log((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { - m_server->onMouseUp(button); + m_primaryReceiver->onMouseUp(button); m_keys[s_vkButton[button]] &= ~0x80; } break; @@ -542,7 +523,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) // ignore message if posted prior to last mark change if (m_markReceived == m_mark) { log((CLOG_ERR "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); - m_server->onMouseWheel(msg->wParam); + m_primaryReceiver->onMouseWheel(msg->wParam); } return true; @@ -552,7 +533,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) SInt32 x = static_cast(msg->wParam); SInt32 y = static_cast(msg->lParam); if (!m_active) { - m_server->onMouseMovePrimary(x, y); + m_primaryReceiver->onMouseMovePrimary(x, y); } else { // compute motion delta. this is relative to the @@ -572,7 +553,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) warpCursorToCenter(); // send motion - m_server->onMouseMoveSecondary(x, y); + m_primaryReceiver->onMouseMoveSecondary(x, y); } } else { @@ -591,11 +572,11 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) case SYNERGY_MSG_SCREEN_SAVER: if (msg->wParam != 0) { if (getScreenSaver()->checkStarted(msg->message, FALSE, 0)) { - m_server->onScreenSaver(true); + m_primaryReceiver->onScreenSaver(true); } } else { - m_server->onScreenSaver(false); + m_primaryReceiver->onScreenSaver(false); } return true; @@ -655,8 +636,8 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, try { m_clipboardOwner = GetClipboardOwner(); if (m_clipboardOwner != m_window) { - m_server->grabClipboard(kClipboardClipboard); - m_server->grabClipboard(kClipboardSelection); + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); } } catch (XBadClient&) { @@ -696,13 +677,21 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, // tell hook about resize if not active else { - onConfigure(); + m_setZone(x, y, w, h, getJumpZoneSize()); } // send new screen info POINT pos; GetCursorPos(&pos); - m_server->setInfo(x, y, w, h, getJumpZoneSize(), pos.x, pos.y); + CClientInfo info; + info.m_x = x; + info.m_y = y; + info.m_w = w; + info.m_h = h; + info.m_zoneSize = getJumpZoneSize(); + info.m_mx = pos.x; + info.m_my = pos.y; + m_receiver->onInfoChanged(info); } return 0; @@ -727,8 +716,8 @@ CMSWindowsPrimaryScreen::enterNoWarp() SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0); } - // install jump zones - onConfigure(); + // watch jump zones + m_setRelay(false); // restore active window and hide our window onEnter(); @@ -787,6 +776,12 @@ CMSWindowsPrimaryScreen::onLeave() return true; } +SInt32 +CMSWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + void CMSWindowsPrimaryScreen::nextMark() { @@ -956,8 +951,8 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) onLeave(); } else { - // set jump zones - onConfigure(); + // watch jump zones + m_setRelay(false); // all messages prior to now are invalid nextMark(); diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 3a3bfdfd..e0814917 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -2,36 +2,36 @@ #define CMSWINDOWSPRIMARYSCREEN_H #include "CMSWindowsScreen.h" +#include "IPrimaryScreen.h" #include "CSynergyHook.h" #include "MouseTypes.h" -#include "IPrimaryScreen.h" #include "CString.h" +class IScreenReceiver; +class IPrimaryScreenReceiver; + class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { public: typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM); - CMSWindowsPrimaryScreen(); + CMSWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); virtual ~CMSWindowsPrimaryScreen(); // IPrimaryScreen overrides virtual void run(); virtual void stop(); - virtual void open(CServer*); + virtual void open(); virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool); virtual bool leave(); - virtual void onConfigure(); + virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); - virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - virtual SInt32 getJumpZoneSize() const; virtual void getClipboard(ClipboardID, IClipboard*) const; virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; - virtual bool isScreenSaverActive() const; - + protected: // CMSWindowsScreen overrides virtual bool onPreTranslate(MSG*); @@ -45,6 +45,8 @@ private: void onEnter(); bool onLeave(); + SInt32 getJumpZoneSize() const; + // warp mouse to center of primary display (used when computing // motion deltas while mouse is on secondary screen). void warpCursorToCenter(); @@ -67,7 +69,8 @@ private: void updateKey(UINT vkCode, bool press); private: - CServer* m_server; + IScreenReceiver* m_receiver; + IPrimaryScreenReceiver* m_primaryReceiver; // true if windows 95/98/me bool m_is95Family; @@ -111,6 +114,7 @@ private: CleanupFunc m_cleanup; InstallFunc m_install; UninstallFunc m_uninstall; + SetSidesFunc m_setSides; SetZoneFunc m_setZone; SetRelayFunc m_setRelay; InstallScreenSaverFunc m_installScreenSaver; diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index 9e71f011..d76d1734 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -43,9 +43,9 @@ CPrimaryClient::stop() } void -CPrimaryClient::reconfigure() +CPrimaryClient::reconfigure(UInt32 activeSides) { - m_screen->reconfigure(); + m_screen->reconfigure(activeSides); } void diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h index 1607cc00..358afac5 100644 --- a/server/CPrimaryClient.h +++ b/server/CPrimaryClient.h @@ -20,7 +20,7 @@ public: void stop(); // called by server when the configuration changes - void reconfigure(); + void reconfigure(UInt32 activeSides); // accessors diff --git a/server/CServer.cpp b/server/CServer.cpp index 89b72575..7457c99b 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -8,7 +8,6 @@ #include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" -// XXX #include "CNetworkAddress.h" #include "IDataSocket.h" #include "IListenSocket.h" #include "ISocketFactory.h" @@ -167,7 +166,7 @@ CServer::setConfig(const CConfig& config) // tell primary screen about reconfiguration if (m_primaryClient != NULL) { - m_primaryClient->reconfigure(); + m_primaryClient->reconfigure(getActivePrimarySides()); } return true; @@ -191,8 +190,8 @@ CServer::getConfig(CConfig* config) const UInt32 CServer::getActivePrimarySides() const { + // note -- m_mutex must be locked on entry UInt32 sides = 0; - CLock lock(&m_mutex); if (!m_config.getNeighbor(getPrimaryScreenName(), CConfig::kLeft).empty()) { sides |= CConfig::kLeftMask; @@ -723,7 +722,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool screenSaver) // update the primary client's clipboards if we're leaving the // primary screen. if (m_active == m_primaryClient) { - for (UInt32 id = 0; id < kClipboardEnd; ++id) { + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { CClipboardInfo& clipboard = m_clipboards[id]; if (clipboard.m_clipboardOwner == m_primaryClient->getName()) { CString clipboardData; @@ -973,14 +972,14 @@ CServer::closeClients(const CConfig& config) index != m_clientThreads.end(); ) { const CString& name = index->first; if (!config.isCanonicalName(name)) { - // save the thread and remove it from m_clientThreads - threads.push_back(index->second); - m_clientThreads.erase(index++); - // lookup IClient with name CClientList::const_iterator index2 = m_clients.find(name); assert(index2 != m_clients.end()); + // save the thread and remove it from m_clientThreads + threads.push_back(index->second); + m_clientThreads.erase(index++); + // close that client assert(index2->second != m_primaryClient); index2->second->close(); @@ -1520,6 +1519,9 @@ CServer::openPrimaryScreen() // open the screen log((CLOG_DEBUG1 "opening primary screen")); m_primaryClient->open(); + + // tell it about the active sides + m_primaryClient->reconfigure(getActivePrimarySides()); } catch (...) { if (m_active != NULL) { diff --git a/server/CServer.h b/server/CServer.h index 73f1523d..d8998116 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -50,9 +50,6 @@ public: // get the primary screen's name CString getPrimaryScreenName() const; - // get the sides of the primary screen that have neighbors - UInt32 getActivePrimarySides() const; - // IPrimaryScreenReceiver overrides virtual void onError(); virtual void onKeyDown(KeyID, KeyModifierMask); @@ -76,6 +73,9 @@ protected: private: typedef std::list CThreadList; + // get the sides of the primary screen that have neighbors + UInt32 getActivePrimarySides() const; + // handle mouse motion bool onMouseMovePrimaryNoLock(SInt32 x, SInt32 y); void onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy); diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 262c27a6..9bc1f3a7 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -47,8 +47,8 @@ static HANDLE g_keyHookEvent = NULL; static HHOOK g_keyboardLL = NULL; static bool g_screenSaver = false; static bool g_relay = false; -static SInt32 g_zoneSize = 0; static UInt32 g_zoneSides = 0; +static SInt32 g_zoneSize = 0; static SInt32 g_xScreen = 0; static SInt32 g_yScreen = 0; static SInt32 g_wScreen = 0; @@ -510,8 +510,8 @@ install() // set defaults g_relay = false; - g_zoneSize = 0; g_zoneSides = 0; + g_zoneSize = 0; g_xScreen = 0; g_yScreen = 0; g_wScreen = 0; @@ -681,30 +681,28 @@ uninstallScreenSaver(void) } void -setZone(UInt32 sides, - SInt32 x, SInt32 y, SInt32 w, SInt32 h, - SInt32 jumpZoneSize) +setSides(UInt32 sides) { - g_zoneSize = jumpZoneSize; g_zoneSides = sides; - g_xScreen = x; - g_yScreen = y; - g_wScreen = w; - g_hScreen = h; - g_relay = false; - restoreCursor(); } void -setRelay(void) +setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize) { - g_relay = true; - g_zoneSize = 0; - g_zoneSides = 0; - g_xScreen = 0; - g_yScreen = 0; - g_wScreen = 0; - g_hScreen = 0; + g_zoneSize = jumpZoneSize; + g_xScreen = x; + g_yScreen = y; + g_wScreen = w; + g_hScreen = h; +} + +void +setRelay(int enable) +{ + g_relay = (enable != 0); + if (!g_relay) { + restoreCursor(); + } } } diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index 026a1c3c..893f2242 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -32,9 +32,9 @@ typedef int (*InstallFunc)(void); typedef int (*UninstallFunc)(void); typedef int (*InstallScreenSaverFunc)(void); typedef int (*UninstallScreenSaverFunc)(void); -typedef void (*SetZoneFunc)(UInt32, - SInt32, SInt32, SInt32, SInt32, SInt32); -typedef void (*SetRelayFunc)(void); +typedef void (*SetSidesFunc)(UInt32); +typedef void (*SetZoneFunc)(SInt32, SInt32, SInt32, SInt32, SInt32); +typedef void (*SetRelayFunc)(int); CSYNERGYHOOK_API int init(DWORD); CSYNERGYHOOK_API int cleanup(void); @@ -42,10 +42,10 @@ CSYNERGYHOOK_API int install(void); CSYNERGYHOOK_API int uninstall(void); CSYNERGYHOOK_API int installScreenSaver(void); CSYNERGYHOOK_API int uninstallScreenSaver(void); -CSYNERGYHOOK_API void setZone(UInt32 sides, - SInt32 x, SInt32 y, SInt32 w, SInt32 h, +CSYNERGYHOOK_API void setSides(UInt32 sides); +CSYNERGYHOOK_API void setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize); -CSYNERGYHOOK_API void setRelay(void); +CSYNERGYHOOK_API void setRelay(int enable); // relay iff enable != 0 } diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 6677f6ac..0c7b10dc 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -397,7 +397,7 @@ CXWindowsPrimaryScreen::leave() } void -CXWindowsPrimaryScreen::reconfigure() +CXWindowsPrimaryScreen::reconfigure(UInt32) { // do nothing } diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 5c82fc37..0d4b2ac7 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -20,7 +20,7 @@ public: virtual void close(); virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool); virtual bool leave(); - virtual void reconfigure(); + virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); diff --git a/server/IPrimaryScreen.h b/server/IPrimaryScreen.h index 62af410f..be63f11f 100644 --- a/server/IPrimaryScreen.h +++ b/server/IPrimaryScreen.h @@ -57,10 +57,10 @@ public: // leave() must not call any receiver methods except onError(). virtual bool leave() = 0; - // called when the configuration has changed. subclasses may need - // to adjust things (like the jump zones) after the configuration - // changes. - virtual void reconfigure() = 0; + // called when the configuration has changed. activeSides is a + // bitmask of CConfig::EDirectionMask indicating which sides of + // the primary screen are linked to clients. + virtual void reconfigure(UInt32 activeSides) = 0; // warp the cursor to the given position virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; diff --git a/server/server.dsp b/server/server.dsp index b50dd9f7..7586b091 100644 --- a/server/server.dsp +++ b/server/server.dsp @@ -94,6 +94,14 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CClientProxy.cpp +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.cpp +# End Source File +# Begin Source File + SOURCE=.\CConfig.cpp # End Source File # Begin Source File @@ -106,18 +114,14 @@ SOURCE=.\CMSWindowsPrimaryScreen.cpp # End Source File # Begin Source File +SOURCE=.\CPrimaryClient.cpp +# End Source File +# Begin Source File + SOURCE=.\CServer.cpp # End Source File # Begin Source File -SOURCE=.\CServerProtocol.cpp -# End Source File -# Begin Source File - -SOURCE=.\CServerProtocol1_0.cpp -# End Source File -# Begin Source File - SOURCE=.\server.cpp # End Source File # Begin Source File @@ -130,6 +134,14 @@ SOURCE=.\server.rc # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CClientProxy.h +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.h +# End Source File +# Begin Source File + SOURCE=.\CConfig.h # End Source File # Begin Source File @@ -142,15 +154,15 @@ SOURCE=.\CMSWindowsPrimaryScreen.h # End Source File # Begin Source File +SOURCE=.\CPrimaryClient.h +# End Source File +# Begin Source File + SOURCE=.\CServer.h # End Source File # Begin Source File -SOURCE=.\CServerProtocol.h -# End Source File -# Begin Source File - -SOURCE=.\CServerProtocol1_0.h +SOURCE=.\IPrimaryScreen.h # End Source File # Begin Source File diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp index e4dae53d..f7d26af0 100644 --- a/synergy/synergy.dsp +++ b/synergy/synergy.dsp @@ -143,19 +143,27 @@ SOURCE=.\CTCPSocketFactory.h # End Source File # Begin Source File +SOURCE=.\IClient.h +# End Source File +# Begin Source File + SOURCE=.\IClipboard.h # End Source File # Begin Source File -SOURCE=.\IPrimaryScreen.h +SOURCE=.\IPrimaryScreenReceiver.h # End Source File # Begin Source File -SOURCE=.\ISecondaryScreen.h +SOURCE=.\IScreenReceiver.h # End Source File # Begin Source File -SOURCE=.\IServerProtocol.h +SOURCE=.\IScreenSaver.h +# End Source File +# Begin Source File + +SOURCE=.\IServer.h # End Source File # Begin Source File From ef7fe1f2833d151341ac4adec22a95efa4ede322 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 11 Jul 2002 18:58:49 +0000 Subject: [PATCH 226/807] checkpoint. making win32 and X primary screen code more similar in order to share code later. --- client/CXWindowsSecondaryScreen.cpp | 99 +++---- client/CXWindowsSecondaryScreen.h | 4 - platform/CMSWindowsScreen.cpp | 74 ++++-- platform/CMSWindowsScreen.h | 15 +- platform/CXWindowsScreen.cpp | 94 +++++-- platform/CXWindowsScreen.h | 49 ++-- server/CMSWindowsPrimaryScreen.cpp | 326 ++++++++++++----------- server/CMSWindowsPrimaryScreen.h | 18 +- server/CXWindowsPrimaryScreen.cpp | 385 ++++++++++++++++------------ server/CXWindowsPrimaryScreen.h | 28 +- 10 files changed, 625 insertions(+), 467 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 46b4d7a2..2eaa1fd8 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -85,19 +85,46 @@ void CXWindowsSecondaryScreen::open() { assert(m_receiver != NULL); + assert(m_window == None); // open the display openDisplay(); { - // verify the availability of the XTest extension CDisplayLock display(this); + + // verify the availability of the XTest extension int majorOpcode, firstEvent, firstError; if (!XQueryExtension(display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) { throw int(6); // FIXME -- make exception for this } + // create the cursor hiding window. this window is used to hide the + // cursor when it's not on the screen. the window is hidden as soon + // as the cursor enters the screen or the display's real cursor is + // moved. + XSetWindowAttributes attr; + attr.event_mask = LeaveWindowMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = getBlankCursor(); + m_window = XCreateWindow(display, getRoot(), 0, 0, 1, 1, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + log((CLOG_DEBUG "window is 0x%08x", m_window)); + + // become impervious to server grabs + XTestGrabControl(display, True); + + // hide the cursor + leaveNoLock(display); + + // initialize the clipboards + initClipboards(m_window); + // update key state updateKeys(display); updateKeycodeMap(display); @@ -130,6 +157,18 @@ CXWindowsSecondaryScreen::close() // restore the screen saver settings getScreenSaver()->enable(); + { + CDisplayLock display(this); + if (display != NULL) { + // no longer impervious to server grabs + XTestGrabControl(display, False); + + // destroy window + XDestroyWindow(display, m_window); + } + m_window = None; + } + // close the display closeDisplay(); } @@ -304,13 +343,7 @@ void CXWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const { CDisplayLock display(this); - int xTmp, yTmp, dummy; - unsigned int dummyMask; - Window dummyWindow; - XQueryPointer(display, getRoot(), &dummyWindow, &dummyWindow, - &xTmp, &yTmp, &dummy, &dummy, &dummyMask); - x = xTmp; - y = yTmp; + getCursorPos(x, y); } void @@ -333,56 +366,6 @@ CXWindowsSecondaryScreen::getClipboard(ClipboardID id, getDisplayClipboard(id, clipboard); } -void -CXWindowsSecondaryScreen::onOpenDisplay(Display* display) -{ - assert(m_window == None); - - // create the cursor hiding window. this window is used to hide the - // cursor when it's not on the screen. the window is hidden as soon - // as the cursor enters the screen or the display's real cursor is - // moved. - XSetWindowAttributes attr; - attr.event_mask = LeaveWindowMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = createBlankCursor(); - m_window = XCreateWindow(display, getRoot(), 0, 0, 1, 1, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - log((CLOG_DEBUG "window is 0x%08x", m_window)); - - // become impervious to server grabs - XTestGrabControl(display, True); - - // hide the cursor - leaveNoLock(display); -} - -CXWindowsClipboard* -CXWindowsSecondaryScreen::createClipboard(ClipboardID id) -{ - CDisplayLock display(this); - return new CXWindowsClipboard(display, m_window, id); -} - -void -CXWindowsSecondaryScreen::onCloseDisplay(Display* display) -{ - assert(m_window != None); - - if (display != NULL) { - // no longer impervious to server grabs - XTestGrabControl(display, False); - - // destroy window - XDestroyWindow(display, m_window); - } - m_window = None; -} - void CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id) { diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index a8e4fc99..aeba39ec 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -39,10 +39,6 @@ public: protected: // CXWindowsScreen overrides - virtual void onOpenDisplay(Display*); - virtual CXWindowsClipboard* - createClipboard(ClipboardID); - virtual void onCloseDisplay(Display*); virtual void onLostClipboard(ClipboardID); private: diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index a9b78907..f7ed98ac 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -84,16 +84,8 @@ CMSWindowsScreen::openDisplay() assert(s_instance != NULL); assert(m_class == 0); - // create a transparent cursor - int cw = GetSystemMetrics(SM_CXCURSOR); - int ch = GetSystemMetrics(SM_CYCURSOR); - UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; - UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; - memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); - memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); - m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); - delete[] cursorXOR; - delete[] cursorAND; + // create the transparent cursor + createBlankCursor(); // register a window class WNDCLASSEX classInfo; @@ -109,14 +101,11 @@ CMSWindowsScreen::openDisplay() classInfo.lpszMenuName = NULL; classInfo.lpszClassName = "Synergy"; classInfo.hIconSm = NULL; - m_class = RegisterClassEx(&classInfo); + m_class = RegisterClassEx(&classInfo); // get screen shape updateScreenShape(); - // let subclass prep display - onOpenDisplay(); - // initialize the screen saver m_screenSaver = new CMSWindowsScreenSaver(); } @@ -125,22 +114,22 @@ void CMSWindowsScreen::closeDisplay() { assert(s_instance != NULL); - assert(m_class != 0); // done with screen saver delete m_screenSaver; m_screenSaver = NULL; - // let subclass close down display - onCloseDisplay(); - // unregister the window class - UnregisterClass((LPCTSTR)m_class, s_instance); - m_class = 0; + if (m_class != 0) { + UnregisterClass((LPCTSTR)m_class, s_instance); + m_class = 0; + } // delete resources - DestroyCursor(m_cursor); - m_cursor = NULL; + if (m_cursor != NULL) { + DestroyCursor(m_cursor); + m_cursor = NULL; + } log((CLOG_DEBUG "closed display")); } @@ -168,8 +157,8 @@ CMSWindowsScreen::updateScreenShape() } void -CMSWindowsScreen::getScreenShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +CMSWindowsScreen::getScreenShape(SInt32& x, SInt32& y, + SInt32& w, SInt32& h) const { assert(m_class != 0); @@ -179,6 +168,43 @@ CMSWindowsScreen::getScreenShape( h = m_h; } +void +CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + POINT pos; + GetCursorPos(&pos); + x = pos.x; + y = pos.y; +} + +void +CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + x = GetSystemMetrics(SM_CXSCREEN) >> 1; + y = GetSystemMetrics(SM_CYSCREEN) >> 1; +} + +HCURSOR +CMSWindowsScreen::getBlankCursor() const +{ + return m_cursor; +} + +void +CMSWindowsScreen::createBlankCursor() +{ + // create a transparent cursor + int cw = GetSystemMetrics(SM_CXCURSOR); + int ch = GetSystemMetrics(SM_CYCURSOR); + UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; + UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; + memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); + memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); + m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); + delete[] cursorXOR; + delete[] cursorAND; +} + HDESK CMSWindowsScreen::openInputDesktop() const { diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index 110bc499..08a95d73 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -51,6 +51,12 @@ protected: void getScreenShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; + // get the current cursor position + void getCursorPos(SInt32& x, SInt32& y) const; + + // get the cursor center position + void getCursorCenter(SInt32& x, SInt32& y) const; + // get the input desktop. caller must CloseDesktop() the result. // do not call under windows 95/98/me. HDESK openInputDesktop() const; @@ -76,16 +82,13 @@ protected: // called by window proc. subclass must call DefWindowProc() if necessary virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM) = 0; - // called by openDisplay() to allow subclasses to prepare the display - virtual void onOpenDisplay() = 0; - - // called by closeDisplay() to - virtual void onCloseDisplay() = 0; - // called by isCurrentDesktop() to get the current desktop name virtual CString getCurrentDesktopName() const = 0; private: + // create the transparent cursor + void createBlankCursor(); + static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); private: diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index 4f223569..f54decde 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -141,27 +141,15 @@ CXWindowsScreen::openDisplay() throw XScreenOpenFailure(); } - // get default screen - m_screen = DefaultScreen(m_display); - Screen* screen = ScreenOfDisplay(m_display, m_screen); + // get default screen and root window + m_screen = DefaultScreen(m_display); + m_root = RootWindow(m_display, m_screen); + + // create the transparent cursor + createBlankCursor(); // get screen shape - m_x = 0; - m_y = 0; - m_w = WidthOfScreen(screen); - m_h = HeightOfScreen(screen); - log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); - - // get the root window - m_root = RootWindow(m_display, m_screen); - - // let subclass prep display - onOpenDisplay(m_display); - - // initialize clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_clipboard[id] = createClipboard(id); - } + updateScreenShape(); // initialize the screen saver m_screenSaver = new CXWindowsScreenSaver(this, m_display); @@ -172,9 +160,6 @@ CXWindowsScreen::closeDisplay() { CLock lock(&m_mutex); - // let subclass close down display - onCloseDisplay(m_display); - // done with screen saver delete m_screenSaver; m_screenSaver = NULL; @@ -209,8 +194,30 @@ CXWindowsScreen::getRoot() const } void -CXWindowsScreen::getScreenShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +CXWindowsScreen::initClipboards(Window window) +{ + assert(m_display != NULL); + assert(window != None); + + // initialize clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = new CXWindowsClipboard(m_display, window, id); + } +} + +void +CXWindowsScreen::updateScreenShape() +{ + m_x = 0; + m_y = 0; + m_w = WidthOfScreen(ScreenOfDisplay(m_display, m_screen)); + m_h = HeightOfScreen(ScreenOfDisplay(m_display, m_screen)); + log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); +} + +void +CXWindowsScreen::getScreenShape(SInt32& x, SInt32& y, + SInt32& w, SInt32& h) const { assert(m_display != NULL); @@ -220,8 +227,35 @@ CXWindowsScreen::getScreenShape( h = m_h; } -Cursor -CXWindowsScreen::createBlankCursor() const +void +CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + assert(m_display != NULL); + + Window root, window; + int mx, my, xWindow, yWindow; + unsigned int mask; + if (XQueryPointer(m_display, getRoot(), &root, &window, + &mx, &my, &xWindow, &yWindow, &mask)) { + x = mx; + y = my; + } + else { + getCursorCenter(x, y); + } +} + +void +CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + assert(m_display != NULL); + + x = m_x + (m_w >> 1); + y = m_y + (m_h >> 1); +} + +void +CXWindowsScreen::createBlankCursor() { // this seems just a bit more complicated than really necessary @@ -246,14 +280,18 @@ CXWindowsScreen::createBlankCursor() const color.flags = DoRed | DoGreen | DoBlue; // make cursor from bitmap - Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, + m_cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, &color, &color, 0, 0); // don't need bitmap or the data anymore delete[] data; XFreePixmap(m_display, bitmap); +} - return cursor; +Cursor +CXWindowsScreen::getBlankCursor() const +{ + return m_cursor; } bool diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index e4ed2cb3..8431c990 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -57,17 +57,31 @@ protected: // is closed. void closeDisplay(); - // get the opened screen, its shape, its root window. to get the - // display create a CDisplayLock object passing this. while the - // object exists no other threads may access the display. do not - // save the Display* beyond the lifetime of the CDisplayLock. + // get the opened screen and its root window. to get the display + // create a CDisplayLock object passing this. while the object + // exists no other threads may access the display. do not save + // the Display* beyond the lifetime of the CDisplayLock. int getScreen() const; - void getScreenShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const; Window getRoot() const; - // create a cursor that is transparent everywhere - Cursor createBlankCursor() const; + // initialize the clipboards + void initClipboards(Window); + + // update screen size cache + void updateScreenShape(); + + // get the shape of the screen + void getScreenShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + + // get the current cursor position + void getCursorPos(SInt32& x, SInt32& y) const; + + // get the cursor center position + void getCursorCenter(SInt32& x, SInt32& y) const; + + // get a cursor that is transparent everywhere + Cursor getBlankCursor() const; // wait for and get the next X event. cancellable. bool getEvent(XEvent*) const; @@ -88,19 +102,6 @@ protected: CXWindowsScreenSaver* getScreenSaver() const; - // called by openDisplay() to allow subclasses to prepare the display. - // the display is locked and passed to the subclass. - virtual void onOpenDisplay(Display*) = 0; - - // called by openDisplay() after onOpenDisplay() to create each clipboard - virtual CXWindowsClipboard* - createClipboard(ClipboardID) = 0; - - // called by closeDisplay() to allow subclasses to clean up the display. - // the display is locked and passed to the subclass. note that the - // display may be NULL if the display has unexpectedly disconnected. - virtual void onCloseDisplay(Display*) = 0; - // called if the display is unexpectedly closing. default does nothing. virtual void onUnexpectedClose(); @@ -108,6 +109,9 @@ protected: virtual void onLostClipboard(ClipboardID) = 0; private: + // create the transparent cursor + void createBlankCursor(); + // remove a timer without locking void removeTimerNoLock(IJob*); @@ -248,6 +252,9 @@ private: // clipboards CXWindowsClipboard* m_clipboard[kClipboardEnd]; + // the transparent cursor + Cursor m_cursor; + // screen saver CXWindowsScreenSaver* m_screenSaver; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index c56e93c7..3db7ebd1 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -82,11 +82,6 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() assert(m_hookLibrary != NULL); assert(m_window == NULL); - // uninstall screen saver hook - if (m_uninstallScreenSaver != NULL) { - m_uninstallScreenSaver(); - } - // done with hook library FreeLibrary(m_hookLibrary); } @@ -127,54 +122,74 @@ CMSWindowsPrimaryScreen::stop() void CMSWindowsPrimaryScreen::open() { - // open the display - openDisplay(); + assert(m_window == NULL); - // initialize marks - m_mark = 0; - m_markReceived = 0; - nextMark(); - - // save cursor pos - POINT pos; - GetCursorPos(&pos); - m_x = pos.x; - m_y = pos.y; - - // send screen info CClientInfo info; - getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); - info.m_zoneSize = getJumpZoneSize(); - info.m_mx = m_x; - info.m_my = m_y; - m_receiver->onInfoChanged(info); + try { + // initialize hook library + m_threadID = GetCurrentThreadId(); + m_init(m_threadID); - // compute center pixel of primary screen - m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; - m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; + // open the display + openDisplay(); - // get keyboard state - updateKeys(); + // create and prepare our window + createWindow(); - // set jump zones - m_setZone(info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize); + // set jump zones + m_setZone(info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize); + + // initialize marks + m_mark = 0; + m_markReceived = 0; + nextMark(); + + // collect screen info + getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); + getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getJumpZoneSize(); + + // save mouse position + m_x = info.m_mx; + m_y = info.m_my; + + // compute center pixel of primary screen + getCursorCenter(m_xCenter, m_yCenter); + + // get keyboard state + updateKeys(); + + // get notified of screen saver activation/deactivation + installScreenSaver(); + } + catch (...) { + close(); + throw; + } // enter the screen enterNoWarp(); + + // send screen info + m_receiver->onInfoChanged(info); } void CMSWindowsPrimaryScreen::close() { - // close the display + uninstallScreenSaver(); + destroyWindow(); closeDisplay(); + m_cleanup(); + m_threadID = 0; } void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) { - log((CLOG_INFO "entering primary at %d,%d", x, y)); + log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreenSaver ? " for screen saver" : "")); assert(m_active == true); + assert(m_window != NULL); // enter the screen enterNoWarp(); @@ -190,12 +205,13 @@ CMSWindowsPrimaryScreen::leave() { log((CLOG_INFO "leaving primary")); assert(m_active == false); + assert(m_window != NULL); // all messages prior to now are invalid nextMark(); - // save active window, show ours, and grab mouse/keyboard - if (!onLeave()) { + // show our window + if (!showWindow()) { return false; } @@ -229,6 +245,48 @@ CMSWindowsPrimaryScreen::leave() // local client now active m_active = true; + // make sure our idea of clipboard ownership is correct + checkClipboard(); + + return true; +} + +void +CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) +{ + m_setSides(activeSides); +} + +void +CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + // set the cursor position without generating an event +// FIXME -- doesn't this generate an event anyway? + SetCursorPos(x, y); + + // save position as last position + m_x = x; + m_y = y; +} + +void +CMSWindowsPrimaryScreen::warpCursorToCenter() +{ + // warp to center. the extra info tells the hook DLL to send + // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); + mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, + (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), + (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), + 0, + 0x12345678); +// FIXME -- ignore mouse until we get warp notification? +} + +void +CMSWindowsPrimaryScreen::checkClipboard() +{ // if we think we own the clipboard but we don't then somebody // grabbed the clipboard on this screen without us knowing. // tell the server that this screen grabbed the clipboard. @@ -253,45 +311,13 @@ CMSWindowsPrimaryScreen::leave() // ignore } } - - return true; -} - -void -CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) -{ - m_setSides(activeSides); -} - -void -CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) -{ - // set the cursor position without generating an event - SetCursorPos(x, y); - - // save position as last position - m_x = x; - m_y = y; -} - -void -CMSWindowsPrimaryScreen::warpCursorToCenter() -{ - // warp to center. the extra info tells the hook DLL to send - // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, - (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), - (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), - 0, - 0x12345678); } void CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/, const IClipboard* src) { + // FIXME -- this is identical to CMSWindowsSecondaryScreen's code assert(m_window != NULL); CMSWindowsClipboard dst(m_window); @@ -301,10 +327,12 @@ CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/, void CMSWindowsPrimaryScreen::grabClipboard(ClipboardID /*id*/) { + // FIXME -- this is identical to CMSWindowsSecondaryScreen's code assert(m_window != NULL); CMSWindowsClipboard clipboard(m_window); if (clipboard.open(0)) { + // FIXME -- don't we need to empty it? clipboard.close(); } } @@ -313,6 +341,7 @@ void CMSWindowsPrimaryScreen::getClipboard(ClipboardID /*id*/, IClipboard* dst) const { + // FIXME -- this is identical to CMSWindowsSecondaryScreen's code assert(m_window != NULL); CMSWindowsClipboard src(m_window); @@ -378,67 +407,6 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const return false; } -void -CMSWindowsPrimaryScreen::onOpenDisplay() -{ - assert(m_window == NULL); - - // save thread id. we'll need to pass this to the hook library. - m_threadID = GetCurrentThreadId(); - - // initialize hook library - m_init(m_threadID); - - try { - // install the screen saver hook - if (m_installScreenSaver != NULL) { - m_installScreenSaver(); - } - - // get the input desktop and switch to it - if (m_is95Family) { - if (!openDesktop()) { - throw XScreenOpenFailure(); - } - } - else { - if (!switchDesktop(openInputDesktop())) { - throw XScreenOpenFailure(); - } - } - } - catch (...) { - m_cleanup(); - throw; - } -} - -void -CMSWindowsPrimaryScreen::onCloseDisplay() -{ - // disconnect from desktop - if (m_is95Family) { - closeDesktop(); - } - else { - switchDesktop(NULL); - } - - // uninstall the screen saver hook - if (m_uninstallScreenSaver != NULL) { - m_uninstallScreenSaver(); - } - - // cleanup hook library - m_cleanup(); - - // clear thread id - m_threadID = 0; - - assert(m_window == NULL); - assert(m_desk == NULL); -} - bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) { @@ -667,8 +635,7 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, // do nothing if resolution hasn't changed if (x != xOld || y != yOld || w != wOld || h != hOld) { // recompute center pixel of primary screen - m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; - m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; + getCursorCenter(m_xCenter, m_yCenter); // warp mouse to center if active if (m_active) { @@ -720,33 +687,14 @@ CMSWindowsPrimaryScreen::enterNoWarp() m_setRelay(false); // restore active window and hide our window - onEnter(); + hideWindow(); // all messages prior to now are invalid nextMark(); } -void -CMSWindowsPrimaryScreen::onEnter() -{ - // restore the active window and hide our window. we can only set - // the active window for another thread if we first attach our input - // to that thread. - ReleaseCapture(); - if (m_lastActiveWindow != NULL) { - DWORD myThread = GetCurrentThreadId(); - if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { - // FIXME -- shouldn't raise window if X-Mouse is enabled - // but i have no idea how to do that or check if enabled. - SetActiveWindow(m_lastActiveWindow); - AttachThreadInput(myThread, m_lastActiveThread, FALSE); - } - } - ShowWindow(m_window, SW_HIDE); -} - bool -CMSWindowsPrimaryScreen::onLeave() +CMSWindowsPrimaryScreen::showWindow() { // remember the active window before we leave. GetActiveWindow() // will only return the active window for the thread's queue (i.e. @@ -776,6 +724,25 @@ CMSWindowsPrimaryScreen::onLeave() return true; } +void +CMSWindowsPrimaryScreen::hideWindow() +{ + // restore the active window and hide our window. we can only set + // the active window for another thread if we first attach our input + // to that thread. + ReleaseCapture(); + if (m_lastActiveWindow != NULL) { + DWORD myThread = GetCurrentThreadId(); + if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { + // FIXME -- shouldn't raise window if X-Mouse is enabled + // but i have no idea how to do that or check if enabled. + SetActiveWindow(m_lastActiveWindow); + AttachThreadInput(myThread, m_lastActiveThread, FALSE); + } + } + ShowWindow(m_window, SW_HIDE); +} + SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const { @@ -792,6 +759,55 @@ CMSWindowsPrimaryScreen::nextMark() PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); } +void +CMSWindowsPrimaryScreen::createWindow() +{ + // get the input desktop and switch to it + if (m_is95Family) { + if (!openDesktop()) { + throw XScreenOpenFailure(); + } + } + else { + if (!switchDesktop(openInputDesktop())) { + throw XScreenOpenFailure(); + } + } +} + +void +CMSWindowsPrimaryScreen::destroyWindow() +{ + // disconnect from desktop + if (m_is95Family) { + closeDesktop(); + } + else { + switchDesktop(NULL); + } + + assert(m_window == NULL); + assert(m_desk == NULL); +} + +void +CMSWindowsPrimaryScreen::installScreenSaver() +{ + // install the screen saver hook + if (m_installScreenSaver != NULL) { + m_installScreenSaver(); + } +} + +void +CMSWindowsPrimaryScreen::uninstallScreenSaver() +{ + // uninstall the screen saver hook + if (m_uninstallScreenSaver != NULL) { + m_uninstallScreenSaver(); + } +} + bool CMSWindowsPrimaryScreen::openDesktop() { @@ -835,7 +851,7 @@ CMSWindowsPrimaryScreen::closeDesktop() if (m_window != NULL) { // restore active window and hide ours if (m_active) { - onEnter(); + hideWindow(); } // first remove clipboard snooper @@ -866,7 +882,7 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) if (m_window != NULL) { // restore active window and hide ours if (m_active) { - onEnter(); + hideWindow(); } // first remove clipboard snooper @@ -948,7 +964,7 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) // get active window and show ours if (m_active) { - onLeave(); + showWindow(); } else { // watch jump zones diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index e0814917..8b083fd5 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -42,8 +42,8 @@ protected: private: void enterNoWarp(); - void onEnter(); - bool onLeave(); + bool showWindow(); + void hideWindow(); SInt32 getJumpZoneSize() const; @@ -51,9 +51,23 @@ private: // motion deltas while mouse is on secondary screen). void warpCursorToCenter(); + // check clipboard ownership and, if necessary, tell the receiver + // of a grab. + void checkClipboard(); + // discard posted messages void nextMark(); + // create/destroy window + // also attach to desktop; this destroys and recreates the window + // as necessary. + void createWindow(); + void destroyWindow(); + + // start/stop watch for screen saver changes + void installScreenSaver(); + void uninstallScreenSaver(); + // open/close desktop (for windows 95/98/me) bool openDesktop(); void closeDesktop(); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 0c7b10dc..fd490586 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -6,6 +6,7 @@ #include "CXWindowsUtil.h" #include "CClipboard.h" #include "ProtocolTypes.h" +#include "XScreen.h" #include "CThread.h" #include "CLog.h" #include "CStopwatch.h" @@ -179,9 +180,9 @@ CXWindowsPrimaryScreen::run() if (xevent.xmotion.send_event) { // we warped the mouse. discard events until we // find the matching sent event. see - // warpCursorNoLockNoFlush() for where the events - // are sent. we discard the matching sent event - // and can be sure we've skipped the warp event. + // warpCursorNoFlush() for where the events are + // sent. we discard the matching sent event and + // can be sure we've skipped the warp event. CDisplayLock display(this); do { XMaskEvent(display, PointerMotionMask, &xevent); @@ -210,7 +211,7 @@ CXWindowsPrimaryScreen::run() xevent.xmotion.y_root - m_yCenter < -s_size || xevent.xmotion.y_root - m_yCenter > s_size) { CDisplayLock display(this); - warpCursorNoLockNoFlush(display, m_xCenter, m_yCenter); + warpCursorNoFlush(display, m_xCenter, m_yCenter); } // send event if mouse moved. do this after warping @@ -239,69 +240,67 @@ CXWindowsPrimaryScreen::stop() void CXWindowsPrimaryScreen::open() { - // open the display - openDisplay(); + assert(m_window == None); - // check for peculiarities - // FIXME -- may have to get these from some database - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; -// m_numLockHalfDuplex = true; -// m_capsLockHalfDuplex = true; + CClientInfo info; + try { + // open the display + openDisplay(); - // get screen shape - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); + // create and prepare our window + createWindow(); - { + // get the display CDisplayLock display(this); - // get notified of screen saver activation/deactivation + // initialize the clipboards + initClipboards(m_window); + + // miscellaneous initialization m_atomScreenSaver = XInternAtom(display, "SCREENSAVER", False); - getScreenSaver()->setNotify(m_window); + + // check for peculiarities + // FIXME -- may have to get these from some database + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +// m_numLockHalfDuplex = true; +// m_capsLockHalfDuplex = true; + + // collect screen info + getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); + getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getJumpZoneSize(); + + // save mouse position + m_x = info.m_mx; + m_y = info.m_my; + + // compute center pixel of primary screen + getCursorCenter(m_xCenter, m_yCenter); // update key state updateModifierMap(display); - // get mouse position - Window root, window; - int mx, my, xWindow, yWindow; - unsigned int mask; - if (!XQueryPointer(display, m_window, &root, &window, - &mx, &my, &xWindow, &yWindow, &mask)) { - mx = w >> 1; - my = h >> 1; - } - - // save mouse position - m_x = mx; - m_y = my; + // get notified of screen saver activation/deactivation + installScreenSaver(); + } + catch (...) { + close(); + throw; } - // save position of center of screen - m_xCenter = x + (w >> 1); - m_yCenter = y + (h >> 1); + // enter the screen + enterNoWarp(); // send screen info - CClientInfo info; - info.m_x = x; - info.m_y = y; - info.m_w = w; - info.m_h = h; - info.m_zoneSize = 1; - info.m_mx = m_x; - info.m_my = m_y; m_receiver->onInfoChanged(info); } void CXWindowsPrimaryScreen::close() { - // stop being notified of screen saver activation/deactivation - getScreenSaver()->setNotify(None); - m_atomScreenSaver = None; - - // close the display + uninstallScreenSaver(); + destroyWindow(); closeDisplay(); } @@ -312,24 +311,20 @@ CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) assert(m_active == true); assert(m_window != None); - CDisplayLock display(this); - - // unmap the grab window. this also ungrabs the mouse and keyboard. - XUnmapWindow(display, m_window); + // enter the screen + enterNoWarp(); // warp to requested location if (!forScreenSaver) { - warpCursorNoLock(display, x, y); + warpCursor(x, y); } // redirect input to root window. do not warp the mouse because // that will deactivate the screen saver. else { + CDisplayLock display(this); XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); } - - // not active anymore - m_active = false; } bool @@ -339,6 +334,116 @@ CXWindowsPrimaryScreen::leave() assert(m_active == false); assert(m_window != None); + // show our window + if (!showWindow()) { + return false; + } + + // warp mouse to center + warpCursorToCenter(); + // FIXME -- this doesn't match the win32 version. that just does + // the warp while we flush the input queue until we find the warp + // and we discard that too. would prefer to at least match our + // own warping when we receive MotionNotify; that just does the + // warp. however, the win32 version sometimes stutters when + // leaving and perhaps this is why. hmm, win32 does ignore the + // events until after the warp (via the mark). + + // local client now active + m_active = true; + + // make sure our idea of clipboard ownership is correct + checkClipboard(); + + return true; +} + +void +CXWindowsPrimaryScreen::reconfigure(UInt32) +{ + // do nothing +} + +void +CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + CDisplayLock display(this); + + // warp mouse + warpCursorNoFlush(display, x, y); + + // remove all input events before and including warp + XEvent event; + while (XCheckMaskEvent(display, PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask, + &event)) { + // do nothing + } + + // save position as last position + m_x = x; + m_y = y; +} + +void +CXWindowsPrimaryScreen::warpCursorToCenter() +{ + warpCursor(m_xCenter, m_yCenter); +} + +void +CXWindowsPrimaryScreen::warpCursorNoFlush( + Display* display, SInt32 x, SInt32 y) +{ + assert(display != NULL); + assert(m_window != None); + + // send an event that we can recognize before the mouse warp + XEvent eventBefore; + eventBefore.type = MotionNotify; + eventBefore.xmotion.display = display; + eventBefore.xmotion.window = m_window; + eventBefore.xmotion.root = getRoot(); + eventBefore.xmotion.subwindow = m_window; + eventBefore.xmotion.time = CurrentTime; + eventBefore.xmotion.x = x; + eventBefore.xmotion.y = y; + eventBefore.xmotion.x_root = x; + eventBefore.xmotion.y_root = y; + eventBefore.xmotion.state = 0; + eventBefore.xmotion.is_hint = False; + eventBefore.xmotion.same_screen = True; + XEvent eventAfter = eventBefore; + XSendEvent(display, m_window, False, 0, &eventBefore); + + // warp mouse + XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); + + // send an event that we can recognize after the mouse warp + XSendEvent(display, m_window, False, 0, &eventAfter); + XSync(display, False); + + log((CLOG_DEBUG2 "warped to %d,%d", x, y)); +} + +void +CXWindowsPrimaryScreen::checkClipboard() +{ + // do nothing, we're always up to date +} + +void +CXWindowsPrimaryScreen::enterNoWarp() +{ + m_active = false; + hideWindow(); +} + +bool +CXWindowsPrimaryScreen::showWindow() +{ CDisplayLock display(this); // raise and show the input window @@ -387,82 +492,89 @@ CXWindowsPrimaryScreen::leave() } while (result != GrabSuccess); log((CLOG_DEBUG1 "grabbed pointer and keyboard")); - // warp mouse to center - warpCursorNoLock(display, m_xCenter, m_yCenter); - - // local client now active - m_active = true; - return true; } void -CXWindowsPrimaryScreen::reconfigure(UInt32) -{ - // do nothing -} - -void -CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +CXWindowsPrimaryScreen::hideWindow() { CDisplayLock display(this); - warpCursorNoLock(display, x, y); + + // unmap the grab window. this also ungrabs the mouse and keyboard. + XUnmapWindow(display, m_window); +} + +SInt32 +CXWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; } void -CXWindowsPrimaryScreen::warpCursorNoLock(Display* display, SInt32 x, SInt32 y) +CXWindowsPrimaryScreen::createWindow() { - // warp mouse - warpCursorNoLockNoFlush(display, x, y); + assert(m_window == None); - // remove all input events before and including warp - XEvent event; - while (XCheckMaskEvent(display, PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask, - &event)) { - // do nothing + // get size of screen + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); + + // grab window attributes. this window is used to capture user + // input when the user is focused on another client. don't let + // the window manager mess with it. + XSetWindowAttributes attr; + attr.event_mask = PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask | PropertyChangeMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = getBlankCursor(); + + // create the grab window + CDisplayLock display(this); + m_window = XCreateWindow(display, getRoot(), + x, y, w, h, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + if (m_window == None) { + throw XScreenOpenFailure(); } + log((CLOG_DEBUG "window is 0x%08x", m_window)); - // save position as last position - m_x = x; - m_y = y; + // start watching for events on other windows + selectEvents(display, getRoot()); } void -CXWindowsPrimaryScreen::warpCursorNoLockNoFlush( - Display* display, SInt32 x, SInt32 y) +CXWindowsPrimaryScreen::destroyWindow() { - assert(display != NULL); - assert(m_window != None); + // display can be NULL if the server unexpectedly disconnected + CDisplayLock display(this); + if (display != NULL && m_window != None) { + XDestroyWindow(display, m_window); + } + m_window = None; +} - // send an event that we can recognize before the mouse warp - XEvent eventBefore; - eventBefore.type = MotionNotify; - eventBefore.xmotion.display = display; - eventBefore.xmotion.window = m_window; - eventBefore.xmotion.root = getRoot(); - eventBefore.xmotion.subwindow = m_window; - eventBefore.xmotion.time = CurrentTime; - eventBefore.xmotion.x = x; - eventBefore.xmotion.y = y; - eventBefore.xmotion.x_root = x; - eventBefore.xmotion.y_root = y; - eventBefore.xmotion.state = 0; - eventBefore.xmotion.is_hint = False; - eventBefore.xmotion.same_screen = True; - XEvent eventAfter = eventBefore; - XSendEvent(display, m_window, False, 0, &eventBefore); +void +CXWindowsPrimaryScreen::installScreenSaver() +{ + assert(getScreenSaver() != NULL); - // warp mouse - XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); + getScreenSaver()->setNotify(m_window); +} - // send an event that we can recognize after the mouse warp - XSendEvent(display, m_window, False, 0, &eventAfter); - XSync(display, False); - - log((CLOG_DEBUG2 "warped to %d,%d", x, y)); +void +CXWindowsPrimaryScreen::uninstallScreenSaver() +{ + // stop being notified of screen saver activation/deactivation + if (getScreenSaver() != NULL) { + getScreenSaver()->setNotify(None); + } + m_atomScreenSaver = None; } void @@ -491,7 +603,6 @@ CXWindowsPrimaryScreen::getToggleMask() const CDisplayLock display(this); // query the pointer to get the keyboard state - // FIXME -- is there a better way to do this? Window root, window; int xRoot, yRoot, xWindow, yWindow; unsigned int state; @@ -548,56 +659,6 @@ CXWindowsPrimaryScreen::isLockedToScreen() const return false; } -void -CXWindowsPrimaryScreen::onOpenDisplay(Display* display) -{ - assert(m_window == None); - - // get size of screen - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - - // create the grab window. this window is used to capture user - // input when the user is focussed on another client. don't let - // the window manager mess with it. - XSetWindowAttributes attr; - attr.event_mask = PointerMotionMask |// PointerMotionHintMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask | PropertyChangeMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = createBlankCursor(); - m_window = XCreateWindow(display, getRoot(), x, y, w, h, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - log((CLOG_DEBUG "window is 0x%08x", m_window)); - - // start watching for events on other windows - selectEvents(display, getRoot()); -} - -CXWindowsClipboard* -CXWindowsPrimaryScreen::createClipboard(ClipboardID id) -{ - CDisplayLock display(this); - return new CXWindowsClipboard(display, m_window, id); -} - -void -CXWindowsPrimaryScreen::onCloseDisplay(Display* display) -{ - assert(m_window != None); - - // destroy window - if (display != NULL) { - XDestroyWindow(display, m_window); - } - m_window = None; -} - void CXWindowsPrimaryScreen::onUnexpectedClose() { diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 0d4b2ac7..b7eb345a 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -30,21 +30,35 @@ public: protected: // CXWindowsScreen overrides - virtual void onOpenDisplay(Display*); - virtual CXWindowsClipboard* - createClipboard(ClipboardID); - virtual void onCloseDisplay(Display*); virtual void onUnexpectedClose(); virtual void onLostClipboard(ClipboardID); private: void selectEvents(Display*, Window) const; void doSelectEvents(Display*, Window) const; - void warpCursorNoLock(Display*, - SInt32 xAbsolute, SInt32 yAbsolute); - void warpCursorNoLockNoFlush(Display*, + + void enterNoWarp(); + bool showWindow(); + void hideWindow(); + + SInt32 getJumpZoneSize() const; + + void warpCursorToCenter(); + void warpCursorNoFlush(Display*, SInt32 xAbsolute, SInt32 yAbsolute); + // check clipboard ownership and, if necessary, tell the receiver + // of a grab. + void checkClipboard(); + + // create/destroy window + void createWindow(); + void destroyWindow(); + + // start/stop watch for screen saver changes + void installScreenSaver(); + void uninstallScreenSaver(); + KeyModifierMask mapModifier(unsigned int state) const; KeyID mapKey(XKeyEvent*) const; ButtonID mapButton(unsigned int button) const; From 52b60d51753e39c74731386f3f6a751579bdcf56 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 12 Jul 2002 20:41:23 +0000 Subject: [PATCH 227/807] refactoring. refactored stuff in client (with changes to server as necessary). --- client/CMSWindowsSecondaryScreen.cpp | 532 ++++++++++-------- client/CMSWindowsSecondaryScreen.h | 33 +- client/CXWindowsSecondaryScreen.cpp | 350 +++++++----- client/CXWindowsSecondaryScreen.h | 26 +- platform/CMSWindowsScreen.cpp | 59 +- platform/CMSWindowsScreen.h | 30 +- platform/CXWindowsScreen.cpp | 250 ++++----- platform/CXWindowsScreen.h | 35 +- platform/CXWindowsScreenSaver.cpp | 2 +- platform/CXWindowsScreenSaver.h | 6 +- server/CMSWindowsPrimaryScreen.cpp | 234 ++++---- server/CMSWindowsPrimaryScreen.h | 23 +- server/CXWindowsPrimaryScreen.cpp | 774 ++++++++++++++------------- server/CXWindowsPrimaryScreen.h | 16 +- 14 files changed, 1325 insertions(+), 1045 deletions(-) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 3b0ff758..e49ec3d4 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -50,53 +50,60 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() void CMSWindowsSecondaryScreen::run() { + assert(m_window != NULL); + // must call run() from same thread as open() assert(m_threadID == GetCurrentThreadId()); // change our priority CThread::getCurrentThread().setPriority(-7); - // poll input desktop to see if it changes (onPreTranslate() - // handles WM_TIMER) - UINT timer = 0; - if (!m_is95Family) { - SetTimer(NULL, 0, 200, NULL); - } - // run event loop - log((CLOG_INFO "entering event loop")); - doRun(); - log((CLOG_INFO "exiting event loop")); - - // remove timer - if (!m_is95Family) { - KillTimer(NULL, timer); + try { + log((CLOG_INFO "entering event loop")); + mainLoop(); + log((CLOG_INFO "exiting event loop")); + } + catch (...) { + log((CLOG_INFO "exiting event loop")); + throw; } } void CMSWindowsSecondaryScreen::stop() { - doStop(); + exitMainLoop(); } void CMSWindowsSecondaryScreen::open() { - // open the display - openDisplay(); + assert(m_window == NULL); - // update key state - updateKeys(); - updateModifiers(); + try { + // open the display + openDisplay(); - // assume primary has all clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - grabClipboard(id); + // create and prepare our window + createWindow(); + + // initialize the clipboards; assume primary has all clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + grabClipboard(id); + } + + // get keyboard state + updateKeys(); + updateModifiers(); + + // disable the screen saver + installScreenSaver(); + } + catch (...) { + close(); + throw; } - - // disable the screen saver - getScreenSaver()->disable(); // hide the cursor m_active = true; @@ -106,13 +113,8 @@ CMSWindowsSecondaryScreen::open() void CMSWindowsSecondaryScreen::close() { - // release keys that are logically pressed - releaseKeys(); - - // restore the screen saver settings - getScreenSaver()->enable(); - - // close the display + uninstallScreenSaver(); + destroyWindow(); closeDisplay(); } @@ -145,8 +147,11 @@ CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) toggleKey(VK_SCROLL, KeyModifierScrollLock); } + // warp to requested location + warpCursor(x, y); + // show mouse - onEnter(x, y); + hideWindow(); } void @@ -161,30 +166,13 @@ CMSWindowsSecondaryScreen::leave() syncDesktop(); // hide mouse - onLeave(); + showWindow(); // not active anymore m_active = false; - // if we think we own the clipboard but we don't then somebody - // grabbed the clipboard on this screen without us knowing. - // tell the server that this screen grabbed the clipboard. - // - // this works around bugs in the clipboard viewer chain. - // sometimes NT will simply never send WM_DRAWCLIPBOARD - // messages for no apparent reason and rebooting fixes the - // problem. since we don't want a broken clipboard until the - // next reboot we do this double check. clipboard ownership - // won't be reflected on other screens until we leave but at - // least the clipboard itself will work. - HWND clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != clipboardOwner) { - m_clipboardOwner = clipboardOwner; - if (m_clipboardOwner != m_window) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } + // make sure our idea of clipboard ownership is correct + checkClipboard(); } void @@ -402,15 +390,7 @@ CMSWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const assert(m_window != NULL); syncDesktop(); - POINT pos; - if (GetCursorPos(&pos)) { - x = pos.x; - y = pos.y; - } - else { - x = 0; - y = 0; - } + getCursorPos(x, y); } void @@ -437,52 +417,18 @@ CMSWindowsSecondaryScreen::getClipboard(ClipboardID /*id*/, CClipboard::copy(dst, &src); } -void -CMSWindowsSecondaryScreen::onOpenDisplay() -{ - assert(m_window == NULL); - - // note if using multiple monitors - m_multimon = isMultimon(); - - // save thread id. we'll need to pass this to the hook library. - m_threadID = GetCurrentThreadId(); - - // get the input desktop and switch to it - if (m_is95Family) { - if (!openDesktop()) { - throw XScreenOpenFailure(); - } - } - else { - if (!switchDesktop(openInputDesktop())) { - throw XScreenOpenFailure(); - } - } -} - -void -CMSWindowsSecondaryScreen::onCloseDisplay() -{ - // disconnect from desktop - if (m_is95Family) { - closeDesktop(); - } - else { - switchDesktop(NULL); - } - - // clear thread id - m_threadID = 0; - - assert(m_window == NULL); - assert(m_desk == NULL); -} - bool -CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg) +CMSWindowsSecondaryScreen::onPreDispatch(const CEvent* event) { + assert(event != NULL); + + // forward to superclass + if (CMSWindowsScreen::onPreDispatch(event)) { + return true; + } + // handle event + const MSG* msg = &event->m_msg; switch (msg->message) { case WM_TIMER: // if current desktop is not the input desktop then switch to it @@ -503,32 +449,35 @@ CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg) return false; } -LRESULT -CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) +bool +CMSWindowsSecondaryScreen::onEvent(CEvent* event) { - switch (msg) { + assert(event != NULL); + + const MSG& msg = event->msg; + switch (msg.message) { case WM_QUERYENDSESSION: if (m_is95Family) { - return TRUE; + event->m_result = TRUE; + return true; } break; case WM_ENDSESSION: if (m_is95Family) { - if (wParam == TRUE && lParam == 0) { + if (msg.wParam == TRUE && msg.lParam == 0) { stop(); } - return 0; + return true; } break; case WM_PAINT: - ValidateRect(hwnd, NULL); - return 0; + ValidateRect(msg.hwnd, NULL); + return true; case WM_ACTIVATEAPP: - if (wParam == FALSE) { + if (msg.wParam == FALSE) { // some other app activated. hide the hider window. ShowWindow(m_window, SW_HIDE); } @@ -538,67 +487,265 @@ CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg, log((CLOG_DEBUG "clipboard was taken")); // first pass it on - SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + if (m_nextClipboardWindow != NULL) { + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); + } // now notify client that somebody changed the clipboard (unless // we're now the owner, in which case it's because we took // ownership, or now it's owned by nobody, which will happen if // we owned it and switched desktops because we destroy our // window to do that). - m_clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); + try { + m_clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } } - return 0; + catch (XBadClient&) { + // ignore. this can happen if we receive this event + // before we've fully started up. + } + return true; case WM_CHANGECBCHAIN: - if (m_nextClipboardWindow == (HWND)wParam) { - m_nextClipboardWindow = (HWND)lParam; + if (m_nextClipboardWindow == (HWND)msg.wParam) { + m_nextClipboardWindow = (HWND)msg.lParam; } - else { - SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + else if (m_nextClipboardWindow != NULL) { + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); } - return 0; + return true; case WM_DISPLAYCHANGE: - // screen resolution has changed - updateScreenShape(); - m_multimon = isMultimon(); + { + // screen resolution may have changed. get old shape. + SInt32 xOld, yOld, wOld, hOld; + getScreenShape(xOld, yOld, wOld, hOld); - // send new info - CClientInfo info; - getShape(info.m_x, info.m_y, info.m_w, info.m_h); - getMousePos(info.m_mx, info.m_my); - info.m_zoneSize = getJumpZoneSize(); - m_receiver->onInfoChanged(info); - return 0; + // update shape + updateScreenShape(); + m_multimon = isMultimon(); + + // collect new screen info + CClientInfo info; + getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); + getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getJumpZoneSize(); + + // do nothing if resolution hasn't changed + if (info.m_x != xOld || info.m_y != yOld || + info.m_w != wOld || info.m_h != hOld) { + // send new screen info + m_receiver->onInfoChanged(info); + } + + return true; + } } - return DefWindowProc(hwnd, msg, wParam, lParam); + return false; +} + +CString +CMSWindowsSecondaryScreen::getCurrentDesktopName() const +{ + return m_deskName; } void -CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y) +CMSWindowsSecondaryScreen::showWindow() { - // warp to requested location - warpCursor(x, y); + // move hider window under the mouse (rather than moving the mouse + // somewhere else on the screen) + SInt32 x, y; + getCursorPos(x, y); + MoveWindow(m_window, x, y, 1, 1, FALSE); - // show cursor + // raise and show the hider window. take activation. + ShowWindow(m_window, SW_SHOWNORMAL); +} + +void +CMSWindowsSecondaryScreen::hideWindow() +{ ShowWindow(m_window, SW_HIDE); } void -CMSWindowsSecondaryScreen::onLeave() +CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) { - // move hider window under the mouse (rather than moving the mouse - // somewhere else on the screen) - POINT point; - GetCursorPos(&point); - MoveWindow(m_window, point.x, point.y, 1, 1, FALSE); + // move the mouse directly to target position on NT family or if + // not using multiple monitors. + if (!m_multimon || !m_is95Family) { + SInt32 x0, y0, w, h; + getScreenShape(x0, y0, w, h); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65535.99 * (x - x0)) / (w - 1)), + (DWORD)((65535.99 * (y - y0)) / (h - 1)), + 0, 0); + } - // raise and show the hider window. take activation. - ShowWindow(m_window, SW_SHOWNORMAL); + // windows 98 (and Me?) is broken. you cannot set the absolute + // position of the mouse except on the primary monitor but you + // can do relative moves onto any monitor. this is, in microsoft's + // words, "by design." apparently the designers of windows 2000 + // we're a little less lazy and did it right. + // + // we use the microsoft recommendation (Q193003): set the absolute + // position on the primary monitor, disable mouse acceleration, + // relative move the mouse to the final location, restore mouse + // acceleration. to avoid one kind of race condition (the user + // clicking the mouse or pressing a key between the absolute and + // relative move) we'll use SendInput() which guarantees that the + // events are delivered uninterrupted. we cannot prevent changes + // to the mouse acceleration at inopportune times, though. + // + // point-to-activate (x-mouse) doesn't seem to be bothered by the + // absolute/relative combination. a window over the absolute + // position (0,0) does *not* get activated (at least not on win2k) + // if the relative move puts the cursor elsewhere. similarly, the + // app under the final mouse position does *not* get deactivated + // by the absolute move to 0,0. + else { + // save mouse speed & acceleration + int oldSpeed[4]; + bool accelChanged = + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed,0) && + SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); + + // use 1:1 motion + if (accelChanged) { + int newSpeed[4] = { 0, 0, 0, 1 }; + accelChanged = + SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || + SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); + } + + // send events + INPUT events[2]; + events[0].type = INPUT_MOUSE; + events[0].mi.dx = 0; + events[0].mi.dy = 0; + events[0].mi.mouseData = 0; + events[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; + events[0].mi.time = GetTickCount(); + events[0].mi.dwExtraInfo = 0; + events[1].type = INPUT_MOUSE; + events[1].mi.dx = x; + events[1].mi.dy = y; + events[1].mi.mouseData = 0; + events[1].mi.dwFlags = MOUSEEVENTF_MOVE; + events[1].mi.time = events[0].mi.time; + events[1].mi.dwExtraInfo = 0; + SendInput(sizeof(events) / sizeof(events[0]), + events, sizeof(events[0])); + + // restore mouse speed & acceleration + if (accelChanged) { + SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); + SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); + } + } +} + +void +CMSWindowsSecondaryScreen::checkClipboard() +{ + // if we think we own the clipboard but we don't then somebody + // grabbed the clipboard on this screen without us knowing. + // tell the server that this screen grabbed the clipboard. + // + // this works around bugs in the clipboard viewer chain. + // sometimes NT will simply never send WM_DRAWCLIPBOARD + // messages for no apparent reason and rebooting fixes the + // problem. since we don't want a broken clipboard until the + // next reboot we do this double check. clipboard ownership + // won't be reflected on other screens until we leave but at + // least the clipboard itself will work. + HWND clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != clipboardOwner) { + try { + m_clipboardOwner = clipboardOwner; + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + catch (XBadClient&) { + // ignore + } + } +} + +void +CMSWindowsPrimaryScreen::createWindow() +{ + // save thread id + m_threadID = GetCurrentThreadId(); + + // note if using multiple monitors + m_multimon = isMultimon(); + + // get the input desktop and switch to it + if (m_is95Family) { + if (!openDesktop()) { + throw XScreenOpenFailure(); + } + } + else { + if (!switchDesktop(openInputDesktop())) { + throw XScreenOpenFailure(); + } + } + + // poll input desktop to see if it changes (onPreDispatch() + // handles WM_TIMER) + m_timer = 0; + if (!m_is95Family) { + m_timer = SetTimer(NULL, 0, 200, NULL); + } +} + +void +CMSWindowsPrimaryScreen::destroyWindow() +{ + // remove timer + if (m_timer != 0) { + KillTimer(NULL, m_timer); + } + + // release keys that are logically pressed + releaseKeys(); + + // disconnect from desktop + if (m_is95Family) { + closeDesktop(); + } + else { + switchDesktop(NULL); + } + + // clear thread id + m_threadID = 0; + + assert(m_window == NULL); + assert(m_desk == NULL); +} + +void +CMSWindowsSecondaryScreen::installScreenSaver() +{ + getScreenSaver()->disable(); +} + +void +CMSWindowsSecondaryScreen::uninstallScreenSaver() +{ + getScreenSaver()->enable(); } bool @@ -720,7 +867,7 @@ CMSWindowsSecondaryScreen::switchDesktop(HDESK desk) // get desktop up to date if (!m_active) { - onLeave(); + showWindow(); } return true; @@ -750,89 +897,6 @@ CMSWindowsSecondaryScreen::syncDesktop() const } } -CString -CMSWindowsSecondaryScreen::getCurrentDesktopName() const -{ - return m_deskName; -} - -void -CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) -{ - // move the mouse directly to target position on NT family or if - // not using multiple monitors. - if (!m_multimon || !m_is95Family) { - SInt32 x0, y0, w, h; - getScreenShape(x0, y0, w, h); - mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65535.99 * (x - x0)) / (w - 1)), - (DWORD)((65535.99 * (y - y0)) / (h - 1)), - 0, 0); - } - - // windows 98 (and Me?) is broken. you cannot set the absolute - // position of the mouse except on the primary monitor but you - // can do relative moves onto any monitor. this is, in microsoft's - // words, "by design." apparently the designers of windows 2000 - // we're a little less lazy and did it right. - // - // we use the microsoft recommendation (Q193003): set the absolute - // position on the primary monitor, disable mouse acceleration, - // relative move the mouse to the final location, restore mouse - // acceleration. to avoid one kind of race condition (the user - // clicking the mouse or pressing a key between the absolute and - // relative move) we'll use SendInput() which guarantees that the - // events are delivered uninterrupted. we cannot prevent changes - // to the mouse acceleration at inopportune times, though. - // - // point-to-activate (x-mouse) doesn't seem to be bothered by the - // absolute/relative combination. a window over the absolute - // position (0,0) does *not* get activated (at least not on win2k) - // if the relative move puts the cursor elsewhere. similarly, the - // app under the final mouse position does *not* get deactivated - // by the absolute move to 0,0. - else { - // save mouse speed & acceleration - int oldSpeed[4]; - bool accelChanged = - SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed,0) && - SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); - - // use 1:1 motion - if (accelChanged) { - int newSpeed[4] = { 0, 0, 0, 1 }; - accelChanged = - SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || - SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); - } - - // send events - INPUT events[2]; - events[0].type = INPUT_MOUSE; - events[0].mi.dx = 0; - events[0].mi.dy = 0; - events[0].mi.mouseData = 0; - events[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; - events[0].mi.time = GetTickCount(); - events[0].mi.dwExtraInfo = 0; - events[1].type = INPUT_MOUSE; - events[1].mi.dx = x; - events[1].mi.dy = y; - events[1].mi.mouseData = 0; - events[1].mi.dwFlags = MOUSEEVENTF_MOVE; - events[1].mi.time = events[0].mi.time; - events[1].mi.dwExtraInfo = 0; - SendInput(sizeof(events) / sizeof(events[0]), - events, sizeof(events[0])); - - // restore mouse speed & acceleration - if (accelChanged) { - SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); - SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); - } - } -} - bool CMSWindowsSecondaryScreen::isMultimon() const { diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 8feb6d3f..c6f9c5e3 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -46,10 +46,8 @@ public: protected: // CMSWindowsScreen overrides - virtual bool onPreTranslate(MSG*); - virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); - virtual void onOpenDisplay(); - virtual void onCloseDisplay(); + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); virtual CString getCurrentDesktopName() const; private: @@ -62,8 +60,25 @@ private: }; typedef std::vector Keystrokes; - void onEnter(SInt32 x, SInt32 y); - void onLeave(); + void showWindow(); + void hideWindow(); + + // warp the mouse to the specified position + void warpCursor(SInt32 x, SInt32 y); + + // check clipboard ownership and, if necessary, tell the receiver + // of a grab. + void checkClipboard(); + + // create/destroy window + // also attach to desktop; this destroys and recreates the window + // as necessary. + void createWindow(); + void destroyWindow(); + + // start/stop watch for screen saver changes + void installScreenSaver(); + void uninstallScreenSaver(); // open/close desktop (for windows 95/98/me) bool openDesktop(); @@ -75,9 +90,6 @@ private: // get calling thread to use the input desktop void syncDesktop() const; - // warp the mouse to the specified position - void warpCursor(SInt32 x, SInt32 y); - // returns true iff there appear to be multiple monitors bool isMultimon() const; @@ -108,6 +120,9 @@ private: // the main loop's thread id DWORD m_threadID; + // the timer used to check for desktop switching + UINT m_timer; + // the thread id of the last attached thread mutable DWORD m_lastThreadID; diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 2eaa1fd8..aea09204 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -1,8 +1,9 @@ #include "CXWindowsSecondaryScreen.h" -#include "CClient.h" +#include "IScreenReceiver.h" #include "CXWindowsClipboard.h" #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" +#include "XScreen.h" #include "CThread.h" #include "CLog.h" #if defined(X_DISPLAY_MISSING) @@ -26,9 +27,10 @@ CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) : m_receiver(receiver), - m_window(None) + m_window(None), + m_active(false) { - // do nothing + assert(m_receiver != NULL); } CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() @@ -41,135 +43,79 @@ CXWindowsSecondaryScreen::run() { assert(m_window != None); - for (;;) { - // wait for and get the next event - XEvent xevent; - if (!getEvent(&xevent)) { - break; - } + // change our priority + CThread::getCurrentThread().setPriority(-7); - // handle event - switch (xevent.type) { - case MappingNotify: - { - // keyboard mapping changed - CDisplayLock display(this); - XRefreshKeyboardMapping(&xevent.xmapping); - updateKeys(display); - updateKeycodeMap(display); - updateModifierMap(display); - updateModifiers(display); - } - break; - - case LeaveNotify: - { - // mouse moved out of hider window somehow. hide the window. - assert(m_window != None); - CDisplayLock display(this); - XUnmapWindow(display, m_window); - } - break; - } + // run event loop + try { + log((CLOG_INFO "entering event loop")); + mainLoop(); + log((CLOG_INFO "exiting event loop")); + } + catch (...) { + log((CLOG_INFO "exiting event loop")); + throw; } } void CXWindowsSecondaryScreen::stop() { - CDisplayLock display(this); - doStop(); + exitMainLoop(); } void CXWindowsSecondaryScreen::open() { - assert(m_receiver != NULL); assert(m_window == None); - // open the display - openDisplay(); + try { + // open the display + openDisplay(); - { - CDisplayLock display(this); + // create and prepare our window + createWindow(); - // verify the availability of the XTest extension - int majorOpcode, firstEvent, firstError; - if (!XQueryExtension(display, XTestExtensionName, - &majorOpcode, &firstEvent, &firstError)) { - throw int(6); // FIXME -- make exception for this + // initialize the clipboards; assume primary has all clipboards + initClipboards(m_window); + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + grabClipboard(id); } - // create the cursor hiding window. this window is used to hide the - // cursor when it's not on the screen. the window is hidden as soon - // as the cursor enters the screen or the display's real cursor is - // moved. - XSetWindowAttributes attr; - attr.event_mask = LeaveWindowMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = getBlankCursor(); - m_window = XCreateWindow(display, getRoot(), 0, 0, 1, 1, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - log((CLOG_DEBUG "window is 0x%08x", m_window)); + // check for peculiarities + // FIXME -- may have to get these from some database + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +// m_numLockHalfDuplex = true; +// m_capsLockHalfDuplex = true; - // become impervious to server grabs - XTestGrabControl(display, True); - - // hide the cursor - leaveNoLock(display); - - // initialize the clipboards - initClipboards(m_window); + // get the display + CDisplayLock display(this); // update key state updateKeys(display); updateKeycodeMap(display); updateModifierMap(display); updateModifiers(display); + + // disable the screen saver + installScreenSaver(); + } + catch (...) { + close(); + throw; } - // check for peculiarities - // FIXME -- may have to get these from some database - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; -// m_numLockHalfDuplex = true; -// m_capsLockHalfDuplex = true; - - // assume primary has all clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - grabClipboard(id); - } - - // disable the screen saver - getScreenSaver()->disable(); + // hide the cursor + m_active = true; + leave(); } void CXWindowsSecondaryScreen::close() { - // release keys that are logically pressed - releaseKeys(); - - // restore the screen saver settings - getScreenSaver()->enable(); - - { - CDisplayLock display(this); - if (display != NULL) { - // no longer impervious to server grabs - XTestGrabControl(display, False); - - // destroy window - XDestroyWindow(display, m_window); - } - m_window = None; - } - - // close the display + uninstallScreenSaver(); + destroyWindow(); closeDisplay(); } @@ -177,15 +123,14 @@ void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) { assert(m_window != None); + assert(m_active == false); + + log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); CDisplayLock display(this); - // warp to requested location - XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime); - XSync(display, False); - - // show cursor - XUnmapWindow(display, m_window); + // now active + m_active = true; // update our keyboard state to reflect the local state updateKeys(display); @@ -202,14 +147,32 @@ CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { toggleKey(display, XK_Scroll_Lock, m_scrollLockMask); } - XSync(display, False); + + // warp to requested location + warpCursor(x, y); + + // show mouse + hideWindow(); } void CXWindowsSecondaryScreen::leave() { + assert(m_window != None); + assert(m_active == true); + + log((CLOG_INFO "leaving screen")); + CDisplayLock display(this); - leaveNoLock(display); + + // hide mouse + showWindow(); + + // not active anymore + m_active = false; + + // make sure our idea of clipboard ownership is correct + checkClipboard(); } void @@ -290,8 +253,7 @@ void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) { CDisplayLock display(this); - XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime); - XSync(display, False); + warpCursor(x, y); } void @@ -366,6 +328,44 @@ CXWindowsSecondaryScreen::getClipboard(ClipboardID id, getDisplayClipboard(id, clipboard); } +bool +CXWindowsSecondaryScreen::onPreDispatch(const CEvent* event) +{ + // forward to superclass + return CXWindowsScreen::onPreDispatch(event); +} + +bool +CXWindowsSecondaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + XEvent& xevent = event->m_event; + + // handle event + switch (xevent.type) { + case MappingNotify: + { + // keyboard mapping changed + CDisplayLock display(this); + XRefreshKeyboardMapping(&xevent.xmapping); + updateKeys(display); + updateKeycodeMap(display); + updateModifierMap(display); + updateModifiers(display); + } + return true; + + case LeaveNotify: + { + // mouse moved out of hider window somehow. hide the window. + assert(m_window != None); + CDisplayLock display(this); + hideWindow(); + } + return true; + } +} + void CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id) { @@ -374,25 +374,110 @@ CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id) } void -CXWindowsSecondaryScreen::leaveNoLock(Display* display) +CXWindowsSecondaryScreen::showWindow() { - assert(display != NULL); - assert(m_window != None); - // move hider window under the mouse (rather than moving the mouse // somewhere else on the screen) - int x, y, dummy; - unsigned int dummyMask; - Window dummyWindow; - XQueryPointer(display, getRoot(), &dummyWindow, &dummyWindow, - &x, &y, &dummy, &dummy, &dummyMask); - XMoveWindow(display, m_window, x, y); + SInt32 x, y; + getCursorPos(x, y); + XMoveWindow(getDisplay(), m_window, x, y); - // raise and show the hider window - XMapRaised(display, m_window); + // raise and show the hider window. take activation. + // FIXME -- take focus? + XMapRaised(getDisplay(), m_window); +/* XXX -- this should have no effect // hide cursor by moving it into the hider window - XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0); + XWarpPointer(getDisplay(), None, m_window, 0, 0, 0, 0, 0, 0); +*/ +} + +void +CXWindowsSecondaryScreen::hideWindow() +{ + XUnmapWindow(getDisplay(), m_window); +} + +void +CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + XTestFakeMotionEvent(getDisplay(), getScreen(), x, y, CurrentTime); + XSync(getDisplay(), False); +} + +void +CXWindowsSecondaryScreen::checkClipboard() +{ + // do nothing, we're always up to date +} + +void +CXWindowsSecondaryScreen::createWindow() +{ + CDisplayLock display(this); + + // verify the availability of the XTest extension + int majorOpcode, firstEvent, firstError; + if (!XQueryExtension(display, XTestExtensionName, + &majorOpcode, &firstEvent, &firstError)) { + // FIXME -- subclass exception for more info? + throw XScreenOpenFailure(); + } + + // cursor hider window attributes. this window is used to hide the + // cursor when it's not on the screen. the window is hidden as soon + // as the cursor enters the screen or the display's real cursor is + // moved. + XSetWindowAttributes attr; + attr.event_mask = LeaveWindowMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = getBlankCursor(); + + // create the cursor hider window + m_window = XCreateWindow(display, getRoot(), + 0, 0, 1, 1, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + if (m_window == None) { + throw XScreenOpenFailure(); + } + log((CLOG_DEBUG "window is 0x%08x", m_window)); + + // become impervious to server grabs + XTestGrabControl(display, True); +} + +void +CXWindowsSecondaryScreen::destroyWindow() +{ + releaseKeys(); + + CDisplayLock display(this); + if (display != NULL) { + // no longer impervious to server grabs + XTestGrabControl(display, False); + + // destroy window + if (m_window != None) { + XDestroyWindow(display, m_window); + m_window = None; + } + } +} + +void +CXWindowsSecondaryScreen::installScreenSaver() +{ + getScreenSaver()->disable(); +} + +void +CXWindowsSecondaryScreen::uninstallScreenSaver() +{ + getScreenSaver()->enable(); } unsigned int @@ -829,17 +914,18 @@ CXWindowsSecondaryScreen::releaseKeys() { CDisplayLock display(this); - // key up for each key that's down - for (UInt32 i = 0; i < 256; ++i) { - if (m_keys[i]) { - XTestFakeKeyEvent(display, i, False, CurrentTime); - m_keys[i] = false; + if (display != NULL) { + // key up for each key that's down + for (UInt32 i = 0; i < 256; ++i) { + if (m_keys[i]) { + XTestFakeKeyEvent(display, i, False, CurrentTime); + m_keys[i] = false; + } } + + // update + XSync(display, False); } - - // update - XSync(display, False); - } void diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index aeba39ec..558b7526 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -39,6 +39,8 @@ public: protected: // CXWindowsScreen overrides + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); virtual void onLostClipboard(ClipboardID); private: @@ -60,7 +62,26 @@ private: typedef std::map KeyCodeMap; typedef std::map ModifierMap; - void leaveNoLock(Display*); + void showWindow(); + void hideWindow(); + + // warp the mouse to the specified position + void warpCursor(SInt32 x, SInt32 y); + + // check clipboard ownership and, if necessary, tell the receiver + // of a grab. + void checkClipboard(); + + // create/destroy window + // also attach to desktop; this destroys and recreates the window + // as necessary. + void createWindow(); + void destroyWindow(); + + // start/stop watch for screen saver changes + void installScreenSaver(); + void uninstallScreenSaver(); + unsigned int mapButton(ButtonID button) const; unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, @@ -82,6 +103,9 @@ private: IScreenReceiver* m_receiver; Window m_window; + // m_active is true if this screen has been entered + bool m_active; + // note toggle keys that toggles on up/down (false) or on // transition (true) bool m_numLockHalfDuplex; diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index f7ed98ac..3a6afd57 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -48,36 +48,44 @@ CMSWindowsScreen::init(HINSTANCE instance) } void -CMSWindowsScreen::doRun() +CMSWindowsScreen::mainLoop() { // save thread id for posting quit message m_thread = GetCurrentThreadId(); // event loop + CEvent event; + event.m_result = 0; for (;;) { - // wait for and get the next event - MSG msg; - getEvent(&msg); + // wait for an event in a cancellable way + CThread::waitForEvent(); + GetMessage(&event.m_msg, NULL, 0, 0); // handle quit message - if (msg.message == WM_QUIT) { + if (event.m_msg.message == WM_QUIT) { break; } // dispatch message - if (!onPreTranslate(&msg)) { - TranslateMessage(&msg); - DispatchMessage(&msg); + if (!onPreDispatch(&event)) { + TranslateMessage(&event.m_msg); + DispatchMessage(&event.m_msg); } } } void -CMSWindowsScreen::doStop() +CMSWindowsScreen::exitMainLoop() { PostThreadMessage(m_thread, WM_QUIT, 0, 0); } +bool +CMSWindowsScreen::onPreDispatch(const CEvent*) +{ + return false; +} + void CMSWindowsScreen::openDisplay() { @@ -172,9 +180,13 @@ void CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const { POINT pos; - GetCursorPos(&pos); - x = pos.x; - y = pos.y; + if (GetCursorPos(&pos)) { + x = pos.x; + y = pos.y; + } + else { + getCursorCenter(x, y); + } } void @@ -244,17 +256,22 @@ CMSWindowsScreen::getScreenSaver() const return m_screenSaver; } -void -CMSWindowsScreen::getEvent(MSG* msg) const -{ - // wait for an event in a cancellable way - CThread::waitForEvent(); - GetMessage(msg, NULL, 0, 0); -} - LRESULT CALLBACK CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { assert(s_screen != NULL); - return s_screen->onEvent(hwnd, msg, wParam, lParam); + + CEvent event; + event.m_msg.hwnd = hwnd; + event.m_msg.message = msg; + event.m_msg.wParam = wParam; + event.m_msg.lParam = lParam; + event.m_result = 0; + + if (s_screen->onEvent(&event)) { + return event.m_result; + } + else { + return DefWindowProc(hwnd, msg, wParam, lParam); + } } diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index 08a95d73..c45b258a 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -10,6 +10,12 @@ class CMSWindowsScreenSaver; class CThread; +class CEvent { +public: + MSG m_msg; + LRESULT m_result; +}; + class CMSWindowsScreen { public: CMSWindowsScreen(); @@ -25,11 +31,11 @@ public: static HINSTANCE getInstance(); protected: - // runs an event loop and returns when WM_QUIT is received - void doRun(); + // runs an event loop and returns when exitMainLoop() is called + void mainLoop(); - // sends WM_QUIT to force doRun() to return - void doStop(); + // force mainLoop() to return + void exitMainLoop(); // open the X display. calls onOpenDisplay() after opening the display, // getting the screen, its size, and root window. then it starts the @@ -72,15 +78,14 @@ protected: CMSWindowsScreenSaver* getScreenSaver() const; - // wait for and get the next message. cancellable. - void getEvent(MSG*) const; + // called for each event before event translation and dispatch. return + // true to skip translation and dispatch. subclasses should call the + // superclass's version first and return true if it returns true. + virtual bool onPreDispatch(const CEvent* event); - // called by doRun() to handle an event. return true to skip - // event translation and dispatch. - virtual bool onPreTranslate(MSG*) = 0; - - // called by window proc. subclass must call DefWindowProc() if necessary - virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM) = 0; + // called by mainLoop(). iff the event was handled return true and + // store the result, if any, in m_result, which defaults to zero. + virtual bool onEvent(CEvent* event) = 0; // called by isCurrentDesktop() to get the current desktop name virtual CString getCurrentDesktopName() const = 0; @@ -89,6 +94,7 @@ private: // create the transparent cursor void createBlankCursor(); + // our window proc static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); private: diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index f54decde..3176ce69 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -120,6 +120,129 @@ CXWindowsScreen::removeTimerNoLock(IJob* job) m_timers.swap(tmp); } +void +CXWindowsScreen::mainLoop() +{ + // wait for an event in a cancellable way and don't lock the + // display while we're waiting. + CEvent event; + m_mutex.lock(); + while (!m_stop) { + while (!m_stop && XPending(m_display) == 0) { + // check timers + if (processTimers()) { + continue; + } + + // wait + m_mutex.unlock(); + CThread::sleep(0.01); + m_mutex.lock(); + } + if (!m_stop) { + // get the event + XNextEvent(m_display, &event.m_event); + + // process the event. if unhandled then let the subclass + // have a go at it. + m_mutex.unlock(); + if (!onPreDispatch(&event)) { + onEvent(&event); + } + m_mutex.lock(); + } + } + m_mutex.unlock(); +} + +void +CXWindowsScreen::exitMainLoop() +{ + CLock lock(&m_mutex); + m_stop = true; +} + +bool +CXWindowsScreen::onPreDispatch(const CEvent* event) +{ + assert(event != NULL); + const XEvent* xevent = &event->m_event; + + switch (xevent->type) { + case SelectionClear: + { + // we just lost the selection. that means someone else + // grabbed the selection so this screen is now the + // selection owner. report that to the subclass. + ClipboardID id = getClipboardID(xevent->xselectionclear.selection); + if (id != kClipboardEnd) { + log((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); + m_clipboard[id]->lost(xevent->xselectionclear.time); + onLostClipboard(id); + return true; + } + } + break; + + case SelectionNotify: + // notification of selection transferred. we shouldn't + // get this here because we handle them in the selection + // retrieval methods. we'll just delete the property + // with the data (satisfying the usual ICCCM protocol). + if (xevent->xselection.property != None) { + CLock lock(&m_mutex); + XDeleteProperty(m_display, + xevent->xselection.requestor, + xevent->xselection.property); + } + return true; + + case SelectionRequest: + { + // somebody is asking for clipboard data + ClipboardID id = getClipboardID( + xevent->xselectionrequest.selection); + if (id != kClipboardEnd) { + CLock lock(&m_mutex); + m_clipboard[id]->addRequest( + xevent->xselectionrequest.owner, + xevent->xselectionrequest.requestor, + xevent->xselectionrequest.target, + xevent->xselectionrequest.time, + xevent->xselectionrequest.property); + return true; + } + } + break; + + case PropertyNotify: + // property delete may be part of a selection conversion + if (xevent->xproperty.state == PropertyDelete) { + processClipboardRequest(xevent->xproperty.window, + xevent->xproperty.time, + xevent->xproperty.atom); + return true; + } + break; + + case DestroyNotify: + // looks like one of the windows that requested a clipboard + // transfer has gone bye-bye. + destroyClipboardRequest(xevent->xdestroywindow.window); + + // we don't know if the event was handled or not so continue + break; + } + + // let screen saver have a go + { + CLock lock(&m_mutex); + m_screenSaver->onPreDispatch(xevent); + } + + return false; +} + void CXWindowsScreen::openDisplay() { @@ -179,6 +302,12 @@ CXWindowsScreen::closeDisplay() XSetIOErrorHandler(NULL); } +Display* +CXWindowsScreen::getDisplay() const +{ + return m_display; +} + int CXWindowsScreen::getScreen() const { @@ -294,49 +423,6 @@ CXWindowsScreen::getBlankCursor() const return m_cursor; } -bool -CXWindowsScreen::getEvent(XEvent* xevent) const -{ - // wait for an event in a cancellable way and don't lock the - // display while we're waiting. - m_mutex.lock(); - for (;;) { - while (!m_stop && XPending(m_display) == 0) { - // check timers - if (const_cast(this)->processTimers()) { - continue; - } - - // wait - m_mutex.unlock(); - CThread::sleep(0.01); - m_mutex.lock(); - } - if (m_stop) { - m_mutex.unlock(); - return false; - } - else { - // get the event - XNextEvent(m_display, xevent); - - // process the event. return the event if unhandled. - m_mutex.unlock(); - if (!const_cast(this)->processEvent(xevent)) { - return true; - } - m_mutex.lock(); - } - } -} - -void -CXWindowsScreen::doStop() -{ - // caller must have locked display - m_stop = true; -} - ClipboardID CXWindowsScreen::getClipboardID(Atom selection) const { @@ -355,84 +441,6 @@ CXWindowsScreen::onUnexpectedClose() // do nothing } -bool -CXWindowsScreen::processEvent(XEvent* xevent) -{ - switch (xevent->type) { - case SelectionClear: - { - // we just lost the selection. that means someone else - // grabbed the selection so this screen is now the - // selection owner. report that to the subclass. - ClipboardID id = getClipboardID(xevent->xselectionclear.selection); - if (id != kClipboardEnd) { - log((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); - m_clipboard[id]->lost(xevent->xselectionclear.time); - onLostClipboard(id); - return true; - } - } - break; - - case SelectionNotify: - // notification of selection transferred. we shouldn't - // get this here because we handle them in the selection - // retrieval methods. we'll just delete the property - // with the data (satisfying the usual ICCCM protocol). - if (xevent->xselection.property != None) { - CLock lock(&m_mutex); - XDeleteProperty(m_display, - xevent->xselection.requestor, - xevent->xselection.property); - } - return true; - - case SelectionRequest: - { - // somebody is asking for clipboard data - ClipboardID id = getClipboardID( - xevent->xselectionrequest.selection); - if (id != kClipboardEnd) { - CLock lock(&m_mutex); - m_clipboard[id]->addRequest( - xevent->xselectionrequest.owner, - xevent->xselectionrequest.requestor, - xevent->xselectionrequest.target, - xevent->xselectionrequest.time, - xevent->xselectionrequest.property); - return true; - } - } - break; - - case PropertyNotify: - // property delete may be part of a selection conversion - if (xevent->xproperty.state == PropertyDelete) { - processClipboardRequest(xevent->xproperty.window, - xevent->xproperty.time, - xevent->xproperty.atom); - return true; - } - break; - - case DestroyNotify: - // looks like one of the windows that requested a clipboard - // transfer has gone bye-bye. - destroyClipboardRequest(xevent->xdestroywindow.window); - - // we don't know if the event was handled or not so continue - break; - } - - // let screen saver have a go - { - CLock lock(&m_mutex); - m_screenSaver->processEvent(xevent); - } - - return false; -} - bool CXWindowsScreen::processTimers() { diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index 8431c990..9900b7ec 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -19,6 +19,12 @@ class IScreenSaver; class CXWindowsClipboard; class CXWindowsScreenSaver; +class CEvent { +public: + XEvent m_event; + SInt32 m_result; +}; + class CXWindowsScreen { public: CXWindowsScreen(); @@ -47,6 +53,12 @@ protected: }; friend class CDisplayLock; + // runs an event loop and returns when exitMainLoop() is called + void mainLoop(); + + // force mainLoop() to return + void exitMainLoop(); + // open the X display. calls onOpenDisplay() after opening the display, // getting the screen, its size, and root window. then it starts the // event thread. @@ -57,6 +69,10 @@ protected: // is closed. void closeDisplay(); + // get the Display*. only use this when you know the display is + // locked but don't have the CDisplayLock available. + Display* getDisplay() const; + // get the opened screen and its root window. to get the display // create a CDisplayLock object passing this. while the object // exists no other threads may access the display. do not save @@ -83,13 +99,6 @@ protected: // get a cursor that is transparent everywhere Cursor getBlankCursor() const; - // wait for and get the next X event. cancellable. - bool getEvent(XEvent*) const; - - // cause getEvent() to return false immediately and forever after. - // the caller must have locked the display. - void doStop(); - // set the contents of the clipboard (i.e. primary selection) bool setDisplayClipboard(ClipboardID, const IClipboard* clipboard); @@ -102,6 +111,15 @@ protected: CXWindowsScreenSaver* getScreenSaver() const; + // called for each event before event translation and dispatch. return + // true to skip translation and dispatch. subclasses should call the + // superclass's version first and return true if it returns true. + virtual bool onPreDispatch(const CEvent* event) = 0; + + // called by mainLoop(). iff the event was handled return true and + // store the result, if any, in m_result, which defaults to zero. + virtual bool onEvent(CEvent* event) = 0; + // called if the display is unexpectedly closing. default does nothing. virtual void onUnexpectedClose(); @@ -115,9 +133,6 @@ private: // remove a timer without locking void removeTimerNoLock(IJob*); - // internal event processing - bool processEvent(XEvent*); - // process timers bool processTimers(); diff --git a/platform/CXWindowsScreenSaver.cpp b/platform/CXWindowsScreenSaver.cpp index 6e6c7116..a3914860 100644 --- a/platform/CXWindowsScreenSaver.cpp +++ b/platform/CXWindowsScreenSaver.cpp @@ -97,7 +97,7 @@ CXWindowsScreenSaver::~CXWindowsScreenSaver() } bool -CXWindowsScreenSaver::processEvent(XEvent* xevent) +CXWindowsScreenSaver::onPreDispatch(const XEvent* xevent) { switch (xevent->type) { case CreateNotify: diff --git a/platform/CXWindowsScreenSaver.h b/platform/CXWindowsScreenSaver.h index c97eba39..78712ab0 100644 --- a/platform/CXWindowsScreenSaver.h +++ b/platform/CXWindowsScreenSaver.h @@ -20,8 +20,10 @@ public: CXWindowsScreenSaver(CXWindowsScreen*, Display*); virtual ~CXWindowsScreenSaver(); - // process X event. returns true if the event was handled. - bool processEvent(XEvent*); + // called for each event before event translation and dispatch. return + // true to skip translation and dispatch. subclasses should call the + // superclass's version first and return true if it returns true. + bool onPreDispatch(const XEvent*); // tells this object to send a ClientMessage to the given window // when the screen saver activates or deactivates. only one diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 3db7ebd1..f1a2dad7 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -22,6 +22,7 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( m_receiver(receiver), m_primaryReceiver(primaryReceiver), m_threadID(0), + m_timer(0), m_desk(NULL), m_deskName(), m_window(NULL), @@ -95,28 +96,22 @@ CMSWindowsPrimaryScreen::run() // change our priority CThread::getCurrentThread().setPriority(-3); - // poll input desktop to see if it changes (preTranslateMessage() - // handles WM_TIMER) - UINT timer = 0; - if (!m_is95Family) { - SetTimer(NULL, 0, 200, NULL); - } - // run event loop - log((CLOG_INFO "entering event loop")); - doRun(); - log((CLOG_INFO "exiting event loop")); - - // remove timer - if (!m_is95Family) { - KillTimer(NULL, timer); + try { + log((CLOG_INFO "entering event loop")); + mainLoop(); + log((CLOG_INFO "exiting event loop")); + } + catch (...) { + log((CLOG_INFO "exiting event loop")); + throw; } } void CMSWindowsPrimaryScreen::stop() { - doStop(); + exitMainLoop(); } void @@ -269,50 +264,6 @@ CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) m_y = y; } -void -CMSWindowsPrimaryScreen::warpCursorToCenter() -{ - // warp to center. the extra info tells the hook DLL to send - // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, - (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), - (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), - 0, - 0x12345678); -// FIXME -- ignore mouse until we get warp notification? -} - -void -CMSWindowsPrimaryScreen::checkClipboard() -{ - // if we think we own the clipboard but we don't then somebody - // grabbed the clipboard on this screen without us knowing. - // tell the server that this screen grabbed the clipboard. - // - // this works around bugs in the clipboard viewer chain. - // sometimes NT will simply never send WM_DRAWCLIPBOARD - // messages for no apparent reason and rebooting fixes the - // problem. since we don't want a broken clipboard until the - // next reboot we do this double check. clipboard ownership - // won't be reflected on other screens until we leave but at - // least the clipboard itself will work. - HWND clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != clipboardOwner) { - try { - m_clipboardOwner = clipboardOwner; - if (m_clipboardOwner != m_window) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore - } - } -} - void CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/, const IClipboard* src) @@ -408,9 +359,17 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const } bool -CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) +CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) { + assert(event != NULL); + + // forward to superclass + if (CMSWindowsScreen::onPreTranslate(event)) { + return true; + } + // handle event + const MSG* msg = &event->m_msg; switch (msg->message) { case SYNERGY_MSG_MARK: m_markReceived = msg->wParam; @@ -567,43 +526,47 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) return false; } -LRESULT -CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) +bool +CMSWindowsPrimaryScreen::onEvent(CEvent* event) { - switch (msg) { + assert(event != NULL); + + const MSG& msg = event->msg; + switch (msg.message) { case WM_QUERYENDSESSION: if (m_is95Family) { - return TRUE; + event->m_result = TRUE; + return true; } break; case WM_ENDSESSION: if (m_is95Family) { - if (wParam == TRUE && lParam == 0) { + if (msg.wParam == TRUE && msg.lParam == 0) { stop(); } - return 0; + return true; } break; case WM_PAINT: - ValidateRect(hwnd, NULL); - return 0; + ValidateRect(msg.hwnd, NULL); + return true; case WM_DRAWCLIPBOARD: log((CLOG_DEBUG "clipboard was taken")); // first pass it on if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); } // now notify server that somebody changed the clipboard. // skip that if we're the new owner. try { m_clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != m_window) { + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { m_receiver->onGrabClipboard(kClipboardClipboard); m_receiver->onGrabClipboard(kClipboardSelection); } @@ -612,28 +575,36 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, // ignore. this can happen if we receive this event // before we've fully started up. } - return 0; + return true; case WM_CHANGECBCHAIN: - if (m_nextClipboardWindow == (HWND)wParam) { - m_nextClipboardWindow = (HWND)lParam; + if (m_nextClipboardWindow == (HWND)msg.wParam) { + m_nextClipboardWindow = (HWND)msg.lParam; } else if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, msg, wParam, lParam); + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); } - return 0; + return true; case WM_DISPLAYCHANGE: { - // screen resolution may have changed + // screen resolution may have changed. get old shape. SInt32 xOld, yOld, wOld, hOld; getScreenShape(xOld, yOld, wOld, hOld); + + // update shape updateScreenShape(); - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); + + // collect new screen info + CClientInfo info; + getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); + getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getJumpZoneSize(); // do nothing if resolution hasn't changed - if (x != xOld || y != yOld || w != wOld || h != hOld) { + if (info.m_x != xOld || info.m_y != yOld || + info.m_w != wOld || info.m_h != hOld) { // recompute center pixel of primary screen getCursorCenter(m_xCenter, m_yCenter); @@ -644,28 +615,46 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, // tell hook about resize if not active else { - m_setZone(x, y, w, h, getJumpZoneSize()); + m_setZone(info.m_x, info.m_y, + info.m_w, info.m_h, info.m_zoneSize); } // send new screen info - POINT pos; - GetCursorPos(&pos); - CClientInfo info; - info.m_x = x; - info.m_y = y; - info.m_w = w; - info.m_h = h; - info.m_zoneSize = getJumpZoneSize(); - info.m_mx = pos.x; - info.m_my = pos.y; m_receiver->onInfoChanged(info); } - return 0; + return true; } } - return DefWindowProc(hwnd, msg, wParam, lParam); + return false; +} + +CString +CMSWindowsPrimaryScreen::getCurrentDesktopName() const +{ + return m_deskName; +} + +SInt32 +CMSWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + +void +CMSWindowsPrimaryScreen::warpCursorToCenter() +{ + // warp to center. the extra info tells the hook DLL to send + // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); + mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, + (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), + (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), + 0, + 0x12345678); +// FIXME -- ignore mouse until we get warp notification? } void @@ -743,20 +732,33 @@ CMSWindowsPrimaryScreen::hideWindow() ShowWindow(m_window, SW_HIDE); } -SInt32 -CMSWindowsPrimaryScreen::getJumpZoneSize() const -{ - return 1; -} - void -CMSWindowsPrimaryScreen::nextMark() +CMSWindowsPrimaryScreen::checkClipboard() { - // next mark - ++m_mark; - - // mark point in message queue where the mark was changed - PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); + // if we think we own the clipboard but we don't then somebody + // grabbed the clipboard on this screen without us knowing. + // tell the server that this screen grabbed the clipboard. + // + // this works around bugs in the clipboard viewer chain. + // sometimes NT will simply never send WM_DRAWCLIPBOARD + // messages for no apparent reason and rebooting fixes the + // problem. since we don't want a broken clipboard until the + // next reboot we do this double check. clipboard ownership + // won't be reflected on other screens until we leave but at + // least the clipboard itself will work. + HWND clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != clipboardOwner) { + try { + m_clipboardOwner = clipboardOwner; + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + catch (XBadClient&) { + // ignore + } + } } void @@ -773,11 +775,23 @@ CMSWindowsPrimaryScreen::createWindow() throw XScreenOpenFailure(); } } + + // poll input desktop to see if it changes (preTranslateMessage() + // handles WM_TIMER) + m_timer = 0; + if (!m_is95Family) { + m_timer = SetTimer(NULL, 0, 200, NULL); + } } void CMSWindowsPrimaryScreen::destroyWindow() { + // remove timer + if (m_timer != 0) { + KillTimer(NULL, m_timer); + } + // disconnect from desktop if (m_is95Family) { closeDesktop(); @@ -977,10 +991,14 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) return true; } -CString -CMSWindowsPrimaryScreen::getCurrentDesktopName() const +void +CMSWindowsPrimaryScreen::nextMark() { - return m_deskName; + // next mark + ++m_mark; + + // mark point in message queue where the mark was changed + PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); } static const KeyID g_virtualKey[] = diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 8b083fd5..7856dccb 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -34,30 +34,25 @@ public: protected: // CMSWindowsScreen overrides - virtual bool onPreTranslate(MSG*); - virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM); - virtual void onOpenDisplay(); - virtual void onCloseDisplay(); + virtual bool onPreTranslate(const CEvent* event); + virtual bool onEvent(CEvent* event); virtual CString getCurrentDesktopName() const; private: - void enterNoWarp(); - bool showWindow(); - void hideWindow(); - SInt32 getJumpZoneSize() const; // warp mouse to center of primary display (used when computing // motion deltas while mouse is on secondary screen). void warpCursorToCenter(); + void enterNoWarp(); + bool showWindow(); + void hideWindow(); + // check clipboard ownership and, if necessary, tell the receiver // of a grab. void checkClipboard(); - // discard posted messages - void nextMark(); - // create/destroy window // also attach to desktop; this destroys and recreates the window // as necessary. @@ -75,6 +70,9 @@ private: // make desk the thread desktop (for windows NT/2000/XP) bool switchDesktop(HDESK desk); + // discard posted messages + void nextMark(); + // key and button queries KeyID mapKey(WPARAM keycode, LPARAM info, KeyModifierMask* maskOut); @@ -92,6 +90,9 @@ private: // the main loop's thread id DWORD m_threadID; + // the timer used to check for desktop switching + UINT m_timer; + // the current desk and it's name HDESK m_desk; CString m_deskName; diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index fd490586..a4cdd4b1 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -42,199 +42,25 @@ CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() void CXWindowsPrimaryScreen::run() { - for (;;) { - // wait for and get the next event - XEvent xevent; - if (!getEvent(&xevent)) { - break; - } + // change our priority + CThread::getCurrentThread().setPriority(-3); - // handle event - switch (xevent.type) { - case CreateNotify: - { - // select events on new window - CDisplayLock display(this); - selectEvents(display, xevent.xcreatewindow.window); - } - break; - - case MappingNotify: - { - // keyboard mapping changed - CDisplayLock display(this); - XRefreshKeyboardMapping(&xevent.xmapping); - updateModifierMap(display); - } - break; - - case ClientMessage: - if (xevent.xclient.message_type == m_atomScreenSaver || - xevent.xclient.format == 32) { - // screen saver activation/deactivation event - m_primaryReceiver->onScreenSaver(xevent.xclient.data.l[0] != 0); - } - break; - - case KeyPress: - { - log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(&xevent.xkey); - if (key != kKeyNone) { - m_primaryReceiver->onKeyDown(key, mask); - if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_primaryReceiver->onKeyUp(key, mask | KeyModifierCapsLock); - } - else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_primaryReceiver->onKeyUp(key, mask | KeyModifierNumLock); - } - } - } - break; - - case KeyRelease: - { - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(&xevent.xkey); - if (key != kKeyNone) { - // check if this is a key repeat by getting the next - // KeyPress event that has the same key and time as - // this release event, if any. first prepare the - // filter info. - CKeyEventInfo filter; - filter.m_event = KeyPress; - filter.m_window = xevent.xkey.window; - filter.m_time = xevent.xkey.time; - filter.m_keycode = xevent.xkey.keycode; - - // now check for event - XEvent xevent2; - CDisplayLock display(this); - if (XCheckIfEvent(display, &xevent2, - &CXWindowsPrimaryScreen::findKeyEvent, - (XPointer)&filter) != True) { - // no press event follows so it's a plain release - log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_primaryReceiver->onKeyDown(key, mask); - } - else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_primaryReceiver->onKeyDown(key, mask); - } - m_primaryReceiver->onKeyUp(key, mask); - } - else { - // found a press event following so it's a repeat. - // we could attempt to count the already queued - // repeats but we'll just send a repeat of 1. - // note that we discard the press event. - log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - m_primaryReceiver->onKeyRepeat(key, mask, 1); - } - } - } - break; - - case ButtonPress: - { - log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNone) { - m_primaryReceiver->onMouseDown(button); - } - } - break; - - case ButtonRelease: - { - log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNone) { - m_primaryReceiver->onMouseUp(button); - } - else if (xevent.xbutton.button == 4) { - // wheel forward (away from user) - m_primaryReceiver->onMouseWheel(120); - } - else if (xevent.xbutton.button == 5) { - // wheel backward (toward user) - m_primaryReceiver->onMouseWheel(-120); - } - } - break; - - case MotionNotify: - { - log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); - - // compute motion delta (relative to the last known - // mouse position) - SInt32 x = xevent.xmotion.x_root - m_x; - SInt32 y = xevent.xmotion.y_root - m_y; - - // save position to compute delta of next motion - m_x = xevent.xmotion.x_root; - m_y = xevent.xmotion.y_root; - - if (xevent.xmotion.send_event) { - // we warped the mouse. discard events until we - // find the matching sent event. see - // warpCursorNoFlush() for where the events are - // sent. we discard the matching sent event and - // can be sure we've skipped the warp event. - CDisplayLock display(this); - do { - XMaskEvent(display, PointerMotionMask, &xevent); - } while (!xevent.xmotion.send_event); - } - else if (!m_active) { - // motion on primary screen - m_primaryReceiver->onMouseMovePrimary(m_x, m_y); - } - else { - // motion on secondary screen. warp mouse back to - // center. - // - // my lombard (powerbook g3) running linux and - // using the adbmouse driver has two problems: - // first, the driver only sends motions of +/-2 - // pixels and, second, it seems to discard some - // physical input after a warp. the former isn't a - // big deal (we're just limited to every other - // pixel) but the latter is a PITA. to work around - // it we only warp when the mouse has moved more - // than s_size pixels from the center. - static const SInt32 s_size = 32; - if (xevent.xmotion.x_root - m_xCenter < -s_size || - xevent.xmotion.x_root - m_xCenter > s_size || - xevent.xmotion.y_root - m_yCenter < -s_size || - xevent.xmotion.y_root - m_yCenter > s_size) { - CDisplayLock display(this); - warpCursorNoFlush(display, m_xCenter, m_yCenter); - } - - // send event if mouse moved. do this after warping - // back to center in case the motion takes us onto - // the primary screen. if we sent the event first - // in that case then the warp would happen after - // warping to the primary screen's enter position, - // effectively overriding it. - if (x != 0 || y != 0) { - m_primaryReceiver->onMouseMoveSecondary(x, y); - } - } - } - break; - } + // run event loop + try { + log((CLOG_INFO "entering event loop")); + mainLoop(); + log((CLOG_INFO "exiting event loop")); + } + catch (...) { + log((CLOG_INFO "exiting event loop")); + throw; } } void CXWindowsPrimaryScreen::stop() { - CDisplayLock display(this); - doStop(); + exitMainLoop(); } void @@ -387,196 +213,6 @@ CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) m_y = y; } -void -CXWindowsPrimaryScreen::warpCursorToCenter() -{ - warpCursor(m_xCenter, m_yCenter); -} - -void -CXWindowsPrimaryScreen::warpCursorNoFlush( - Display* display, SInt32 x, SInt32 y) -{ - assert(display != NULL); - assert(m_window != None); - - // send an event that we can recognize before the mouse warp - XEvent eventBefore; - eventBefore.type = MotionNotify; - eventBefore.xmotion.display = display; - eventBefore.xmotion.window = m_window; - eventBefore.xmotion.root = getRoot(); - eventBefore.xmotion.subwindow = m_window; - eventBefore.xmotion.time = CurrentTime; - eventBefore.xmotion.x = x; - eventBefore.xmotion.y = y; - eventBefore.xmotion.x_root = x; - eventBefore.xmotion.y_root = y; - eventBefore.xmotion.state = 0; - eventBefore.xmotion.is_hint = False; - eventBefore.xmotion.same_screen = True; - XEvent eventAfter = eventBefore; - XSendEvent(display, m_window, False, 0, &eventBefore); - - // warp mouse - XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); - - // send an event that we can recognize after the mouse warp - XSendEvent(display, m_window, False, 0, &eventAfter); - XSync(display, False); - - log((CLOG_DEBUG2 "warped to %d,%d", x, y)); -} - -void -CXWindowsPrimaryScreen::checkClipboard() -{ - // do nothing, we're always up to date -} - -void -CXWindowsPrimaryScreen::enterNoWarp() -{ - m_active = false; - hideWindow(); -} - -bool -CXWindowsPrimaryScreen::showWindow() -{ - CDisplayLock display(this); - - // raise and show the input window - XMapRaised(display, m_window); - - // grab the mouse and keyboard. keep trying until we get them. - // if we can't grab one after grabbing the other then ungrab - // and wait before retrying. give up after s_timeout seconds. - static const double s_timeout = 1.0; - int result; - CStopwatch timer; - do { - // keyboard first - do { - result = XGrabKeyboard(display, m_window, True, - GrabModeAsync, GrabModeAsync, CurrentTime); - assert(result != GrabNotViewable); - if (result != GrabSuccess) { - log((CLOG_DEBUG2 "waiting to grab keyboard")); - CThread::sleep(0.05); - if (timer.getTime() >= s_timeout) { - log((CLOG_DEBUG2 "grab keyboard timed out")); - XUnmapWindow(display, m_window); - return false; - } - } - } while (result != GrabSuccess); - log((CLOG_DEBUG2 "grabbed keyboard")); - - // now the mouse - result = XGrabPointer(display, m_window, True, 0, - GrabModeAsync, GrabModeAsync, - m_window, None, CurrentTime); - assert(result != GrabNotViewable); - if (result != GrabSuccess) { - // back off to avoid grab deadlock - XUngrabKeyboard(display, CurrentTime); - log((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); - CThread::sleep(0.05); - if (timer.getTime() >= s_timeout) { - log((CLOG_DEBUG2 "grab pointer timed out")); - XUnmapWindow(display, m_window); - return false; - } - } - } while (result != GrabSuccess); - log((CLOG_DEBUG1 "grabbed pointer and keyboard")); - - return true; -} - -void -CXWindowsPrimaryScreen::hideWindow() -{ - CDisplayLock display(this); - - // unmap the grab window. this also ungrabs the mouse and keyboard. - XUnmapWindow(display, m_window); -} - -SInt32 -CXWindowsPrimaryScreen::getJumpZoneSize() const -{ - return 1; -} - -void -CXWindowsPrimaryScreen::createWindow() -{ - assert(m_window == None); - - // get size of screen - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - - // grab window attributes. this window is used to capture user - // input when the user is focused on another client. don't let - // the window manager mess with it. - XSetWindowAttributes attr; - attr.event_mask = PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask | PropertyChangeMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = getBlankCursor(); - - // create the grab window - CDisplayLock display(this); - m_window = XCreateWindow(display, getRoot(), - x, y, w, h, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - if (m_window == None) { - throw XScreenOpenFailure(); - } - log((CLOG_DEBUG "window is 0x%08x", m_window)); - - // start watching for events on other windows - selectEvents(display, getRoot()); -} - -void -CXWindowsPrimaryScreen::destroyWindow() -{ - // display can be NULL if the server unexpectedly disconnected - CDisplayLock display(this); - if (display != NULL && m_window != None) { - XDestroyWindow(display, m_window); - } - m_window = None; -} - -void -CXWindowsPrimaryScreen::installScreenSaver() -{ - assert(getScreenSaver() != NULL); - - getScreenSaver()->setNotify(m_window); -} - -void -CXWindowsPrimaryScreen::uninstallScreenSaver() -{ - // stop being notified of screen saver activation/deactivation - if (getScreenSaver() != NULL) { - getScreenSaver()->setNotify(None); - } - m_atomScreenSaver = None; -} - void CXWindowsPrimaryScreen::setClipboard(ClipboardID id, const IClipboard* clipboard) @@ -659,6 +295,202 @@ CXWindowsPrimaryScreen::isLockedToScreen() const return false; } +bool +CXWindowsPrimaryScreen::onPreDispatch(const CEvent* event) +{ + // forward to superclass + return CXWindowsScreen::onPreDispatch(event); +} + +bool +CXWindowsPrimaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + XEvent& xevent = event->m_event; + + // handle event + switch (xevent.type) { + case CreateNotify: + { + // select events on new window + CDisplayLock display(this); + selectEvents(display, xevent.xcreatewindow.window); + } + return true; + + case MappingNotify: + { + // keyboard mapping changed + CDisplayLock display(this); + XRefreshKeyboardMapping(&xevent.xmapping); + updateModifierMap(display); + } + return true; + + case ClientMessage: + if (xevent.xclient.message_type == m_atomScreenSaver || + xevent.xclient.format == 32) { + // screen saver activation/deactivation event + m_primaryReceiver->onScreenSaver(xevent.xclient.data.l[0] != 0); + return true; + } + break; + + case KeyPress: + { + log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(&xevent.xkey); + if (key != kKeyNone) { + m_primaryReceiver->onKeyDown(key, mask); + if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { + m_primaryReceiver->onKeyUp(key, mask | KeyModifierCapsLock); + } + else if (key == XK_Num_Lock && m_numLockHalfDuplex) { + m_primaryReceiver->onKeyUp(key, mask | KeyModifierNumLock); + } + } + } + return true; + + case KeyRelease: + { + const KeyModifierMask mask = mapModifier(xevent.xkey.state); + const KeyID key = mapKey(&xevent.xkey); + if (key != kKeyNone) { + // check if this is a key repeat by getting the next + // KeyPress event that has the same key and time as + // this release event, if any. first prepare the + // filter info. + CKeyEventInfo filter; + filter.m_event = KeyPress; + filter.m_window = xevent.xkey.window; + filter.m_time = xevent.xkey.time; + filter.m_keycode = xevent.xkey.keycode; + + // now check for event + XEvent xevent2; + CDisplayLock display(this); + if (XCheckIfEvent(display, &xevent2, + &CXWindowsPrimaryScreen::findKeyEvent, + (XPointer)&filter) != True) { + // no press event follows so it's a plain release + log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { + m_primaryReceiver->onKeyDown(key, mask); + } + else if (key == XK_Num_Lock && m_numLockHalfDuplex) { + m_primaryReceiver->onKeyDown(key, mask); + } + m_primaryReceiver->onKeyUp(key, mask); + } + else { + // found a press event following so it's a repeat. + // we could attempt to count the already queued + // repeats but we'll just send a repeat of 1. + // note that we discard the press event. + log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + m_primaryReceiver->onKeyRepeat(key, mask, 1); + } + } + } + return true; + + case ButtonPress: + { + log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_primaryReceiver->onMouseDown(button); + } + } + return true; + + case ButtonRelease: + { + log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); + const ButtonID button = mapButton(xevent.xbutton.button); + if (button != kButtonNone) { + m_primaryReceiver->onMouseUp(button); + } + else if (xevent.xbutton.button == 4) { + // wheel forward (away from user) + m_primaryReceiver->onMouseWheel(120); + } + else if (xevent.xbutton.button == 5) { + // wheel backward (toward user) + m_primaryReceiver->onMouseWheel(-120); + } + } + return true; + + case MotionNotify: + { + log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); + + // compute motion delta (relative to the last known + // mouse position) + SInt32 x = xevent.xmotion.x_root - m_x; + SInt32 y = xevent.xmotion.y_root - m_y; + + // save position to compute delta of next motion + m_x = xevent.xmotion.x_root; + m_y = xevent.xmotion.y_root; + + if (xevent.xmotion.send_event) { + // we warped the mouse. discard events until we + // find the matching sent event. see + // warpCursorNoFlush() for where the events are + // sent. we discard the matching sent event and + // can be sure we've skipped the warp event. + CDisplayLock display(this); + do { + XMaskEvent(display, PointerMotionMask, &xevent); + } while (!xevent.xmotion.send_event); + } + else if (!m_active) { + // motion on primary screen + m_primaryReceiver->onMouseMovePrimary(m_x, m_y); + } + else { + // motion on secondary screen. warp mouse back to + // center. + // + // my lombard (powerbook g3) running linux and + // using the adbmouse driver has two problems: + // first, the driver only sends motions of +/-2 + // pixels and, second, it seems to discard some + // physical input after a warp. the former isn't a + // big deal (we're just limited to every other + // pixel) but the latter is a PITA. to work around + // it we only warp when the mouse has moved more + // than s_size pixels from the center. + static const SInt32 s_size = 32; + if (xevent.xmotion.x_root - m_xCenter < -s_size || + xevent.xmotion.x_root - m_xCenter > s_size || + xevent.xmotion.y_root - m_yCenter < -s_size || + xevent.xmotion.y_root - m_yCenter > s_size) { + CDisplayLock display(this); + warpCursorNoFlush(display, m_xCenter, m_yCenter); + } + + // send event if mouse moved. do this after warping + // back to center in case the motion takes us onto + // the primary screen. if we sent the event first + // in that case then the warp would happen after + // warping to the primary screen's enter position, + // effectively overriding it. + if (x != 0 || y != 0) { + m_primaryReceiver->onMouseMoveSecondary(x, y); + } + } + } + return true; + } + + return false; +} + void CXWindowsPrimaryScreen::onUnexpectedClose() { @@ -673,6 +505,196 @@ CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id) m_receiver->onGrabClipboard(id); } +SInt32 +CXWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + +void +CXWindowsPrimaryScreen::warpCursorToCenter() +{ + warpCursor(m_xCenter, m_yCenter); +} + +void +CXWindowsPrimaryScreen::warpCursorNoFlush( + Display* display, SInt32 x, SInt32 y) +{ + assert(display != NULL); + assert(m_window != None); + + // send an event that we can recognize before the mouse warp + XEvent eventBefore; + eventBefore.type = MotionNotify; + eventBefore.xmotion.display = display; + eventBefore.xmotion.window = m_window; + eventBefore.xmotion.root = getRoot(); + eventBefore.xmotion.subwindow = m_window; + eventBefore.xmotion.time = CurrentTime; + eventBefore.xmotion.x = x; + eventBefore.xmotion.y = y; + eventBefore.xmotion.x_root = x; + eventBefore.xmotion.y_root = y; + eventBefore.xmotion.state = 0; + eventBefore.xmotion.is_hint = False; + eventBefore.xmotion.same_screen = True; + XEvent eventAfter = eventBefore; + XSendEvent(display, m_window, False, 0, &eventBefore); + + // warp mouse + XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); + + // send an event that we can recognize after the mouse warp + XSendEvent(display, m_window, False, 0, &eventAfter); + XSync(display, False); + + log((CLOG_DEBUG2 "warped to %d,%d", x, y)); +} + +void +CXWindowsPrimaryScreen::enterNoWarp() +{ + m_active = false; + hideWindow(); +} + +bool +CXWindowsPrimaryScreen::showWindow() +{ + CDisplayLock display(this); + + // raise and show the input window + XMapRaised(display, m_window); + + // grab the mouse and keyboard. keep trying until we get them. + // if we can't grab one after grabbing the other then ungrab + // and wait before retrying. give up after s_timeout seconds. + static const double s_timeout = 1.0; + int result; + CStopwatch timer; + do { + // keyboard first + do { + result = XGrabKeyboard(display, m_window, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + log((CLOG_DEBUG2 "waiting to grab keyboard")); + CThread::sleep(0.05); + if (timer.getTime() >= s_timeout) { + log((CLOG_DEBUG2 "grab keyboard timed out")); + XUnmapWindow(display, m_window); + return false; + } + } + } while (result != GrabSuccess); + log((CLOG_DEBUG2 "grabbed keyboard")); + + // now the mouse + result = XGrabPointer(display, m_window, True, 0, + GrabModeAsync, GrabModeAsync, + m_window, None, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + // back off to avoid grab deadlock + XUngrabKeyboard(display, CurrentTime); + log((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); + CThread::sleep(0.05); + if (timer.getTime() >= s_timeout) { + log((CLOG_DEBUG2 "grab pointer timed out")); + XUnmapWindow(display, m_window); + return false; + } + } + } while (result != GrabSuccess); + log((CLOG_DEBUG1 "grabbed pointer and keyboard")); + + return true; +} + +void +CXWindowsPrimaryScreen::hideWindow() +{ + CDisplayLock display(this); + + // unmap the grab window. this also ungrabs the mouse and keyboard. + XUnmapWindow(display, m_window); +} + +void +CXWindowsPrimaryScreen::checkClipboard() +{ + // do nothing, we're always up to date +} + +void +CXWindowsPrimaryScreen::createWindow() +{ + assert(m_window == None); + + // get size of screen + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); + + // grab window attributes. this window is used to capture user + // input when the user is focused on another client. don't let + // the window manager mess with it. + XSetWindowAttributes attr; + attr.event_mask = PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask | PropertyChangeMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = getBlankCursor(); + + // create the grab window + CDisplayLock display(this); + m_window = XCreateWindow(display, getRoot(), + x, y, w, h, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + if (m_window == None) { + throw XScreenOpenFailure(); + } + log((CLOG_DEBUG "window is 0x%08x", m_window)); + + // start watching for events on other windows + selectEvents(display, getRoot()); +} + +void +CXWindowsPrimaryScreen::destroyWindow() +{ + // display can be NULL if the server unexpectedly disconnected + CDisplayLock display(this); + if (display != NULL && m_window != None) { + XDestroyWindow(display, m_window); + } + m_window = None; +} + +void +CXWindowsPrimaryScreen::installScreenSaver() +{ + assert(getScreenSaver() != NULL); + + getScreenSaver()->setNotify(m_window); +} + +void +CXWindowsPrimaryScreen::uninstallScreenSaver() +{ + // stop being notified of screen saver activation/deactivation + if (getScreenSaver() != NULL) { + getScreenSaver()->setNotify(None); + } + m_atomScreenSaver = None; +} + void CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const { diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index b7eb345a..8bf41373 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -30,23 +30,22 @@ public: protected: // CXWindowsScreen overrides + virtual bool onPreDispatch(const CEvent* event); + virtual bool onEvent(CEvent* event); virtual void onUnexpectedClose(); virtual void onLostClipboard(ClipboardID); private: - void selectEvents(Display*, Window) const; - void doSelectEvents(Display*, Window) const; - - void enterNoWarp(); - bool showWindow(); - void hideWindow(); - SInt32 getJumpZoneSize() const; void warpCursorToCenter(); void warpCursorNoFlush(Display*, SInt32 xAbsolute, SInt32 yAbsolute); + void enterNoWarp(); + bool showWindow(); + void hideWindow(); + // check clipboard ownership and, if necessary, tell the receiver // of a grab. void checkClipboard(); @@ -59,6 +58,9 @@ private: void installScreenSaver(); void uninstallScreenSaver(); + void selectEvents(Display*, Window) const; + void doSelectEvents(Display*, Window) const; + KeyModifierMask mapModifier(unsigned int state) const; KeyID mapKey(XKeyEvent*) const; ButtonID mapButton(unsigned int button) const; From f48a5fe387c9329030ee507986936aaf2ab548dc Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 13 Jul 2002 22:00:38 +0000 Subject: [PATCH 228/807] checkpoint. still refactoring. merged common code from primary screens into CPrimaryScreen and merged common code from secondary screens into CSecondaryScreen. changed is-a relationship to a has-a between the primary and secondary screen classes and the generic platform dependent screen class to avoid multiple inheritance of implementation. also standardized the interface for those generic screen classes. adding a platform now involves implementing simpler interfaces: IScreen for the generic screen, IScreenEventHandler and some methods of CPrimaryScreen for the primary screen, and IScreenEventHandler and some methods of CSecondaryScreen for the secondary screen. did X11 platform but not win32 platform. --- client/CClient.cpp | 32 +- client/CClient.h | 14 +- client/CSecondaryScreen.cpp | 263 ++++++++++++++ client/CSecondaryScreen.h | 141 +++++++ client/CServerProxy.cpp | 8 +- client/CServerProxy.h | 2 +- client/CXWindowsSecondaryScreen.cpp | 454 ++++++++--------------- client/CXWindowsSecondaryScreen.h | 82 ++--- client/ISecondaryScreen.h | 82 ----- client/Makefile.am | 3 +- client/client.cpp | 8 +- platform/CXWindowsScreen.cpp | 544 ++++++++++++++++------------ platform/CXWindowsScreen.h | 148 +++----- server/CClientProxy.h | 10 +- server/CClientProxy1_0.cpp | 26 +- server/CClientProxy1_0.h | 10 +- server/CPrimaryClient.cpp | 33 +- server/CPrimaryClient.h | 17 +- server/CPrimaryScreen.cpp | 270 ++++++++++++++ server/CPrimaryScreen.h | 157 ++++++++ server/CServer.cpp | 164 +++++---- server/CServer.h | 18 +- server/CXWindowsPrimaryScreen.cpp | 536 ++++++++++----------------- server/CXWindowsPrimaryScreen.h | 81 ++--- server/IPrimaryScreen.h | 95 ----- server/Makefile.am | 3 +- server/server.cpp | 8 +- synergy/IClient.h | 20 +- synergy/IPrimaryScreenReceiver.h | 16 +- synergy/IScreen.h | 66 ++++ synergy/IScreenEventHandler.h | 39 ++ synergy/IScreenReceiver.h | 2 + synergy/IServer.h | 22 +- synergy/Makefile.am | 2 + 34 files changed, 1945 insertions(+), 1431 deletions(-) create mode 100644 client/CSecondaryScreen.cpp create mode 100644 client/CSecondaryScreen.h delete mode 100644 client/ISecondaryScreen.h create mode 100644 server/CPrimaryScreen.cpp create mode 100644 server/CPrimaryScreen.h delete mode 100644 server/IPrimaryScreen.h create mode 100644 synergy/IScreen.h create mode 100644 synergy/IScreenEventHandler.h diff --git a/client/CClient.cpp b/client/CClient.cpp index 15514275..ab221a7e 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -4,7 +4,7 @@ #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" -#include "ISecondaryScreen.h" +#include "CSecondaryScreen.h" #include "IServer.h" #include "ProtocolTypes.h" #include "XScreen.h" @@ -148,9 +148,7 @@ CClient::run() this, &CClient::runSession)); // handle events - log((CLOG_DEBUG "starting event handling")); m_screen->run(); - log((CLOG_DEBUG "stopped event handling")); // clean up deleteSession(thread); @@ -160,7 +158,6 @@ CClient::run() log((CLOG_ERR "client error: %s", e.what())); // clean up - log((CLOG_DEBUG "stopped event handling")); deleteSession(thread); log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); CLock lock(&m_mutex); @@ -168,7 +165,6 @@ CClient::run() } catch (XThread&) { // clean up - log((CLOG_DEBUG "stopped event handling")); deleteSession(thread); log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; @@ -177,7 +173,6 @@ CClient::run() log((CLOG_DEBUG "unknown client error")); // clean up - log((CLOG_DEBUG "stopped event handling")); deleteSession(thread); log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; @@ -188,6 +183,7 @@ void CClient::close() { closeSecondaryScreen(); + log((CLOG_INFO "closed screen")); } void @@ -291,9 +287,9 @@ CClient::mouseWheel(SInt32 delta) } void -CClient::screenSaver(bool activate) +CClient::screensaver(bool activate) { - m_screen->screenSaver(activate); + m_screen->screensaver(activate); } CString @@ -302,6 +298,12 @@ CClient::getName() const return m_name; } +SInt32 +CClient::getJumpZoneSize() const +{ + return m_screen->getJumpZoneSize(); +} + void CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { @@ -309,21 +311,15 @@ CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const } void -CClient::getCenter(SInt32&, SInt32&) const +CClient::getCursorPos(SInt32& x, SInt32& y) const { - assert(0 && "shouldn't be called"); + m_screen->getCursorPos(x, y); } void -CClient::getMousePos(SInt32& x, SInt32& y) const +CClient::getCursorCenter(SInt32&, SInt32&) const { - m_screen->getMousePos(x, y); -} - -SInt32 -CClient::getJumpZoneSize() const -{ - return m_screen->getJumpZoneSize(); + assert(0 && "shouldn't be called"); } // FIXME -- use factory to create screen diff --git a/client/CClient.h b/client/CClient.h index b36fa98c..c555d87c 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -7,10 +7,10 @@ #include "CNetworkAddress.h" #include "CMutex.h" +class CSecondaryScreen; class CServerProxy; class CThread; class IDataSocket; -class ISecondaryScreen; class IScreenReceiver; class CClient : public IScreenReceiver, public IClient { @@ -50,7 +50,7 @@ public: // FIXME -- can we avoid passing everything here? virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, - bool screenSaver); + bool forScreensaver); virtual bool leave(); virtual void setClipboard(ClipboardID, const CString&); virtual void grabClipboard(ClipboardID); @@ -62,13 +62,13 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseWheel(SInt32 delta); - virtual void screenSaver(bool activate); + virtual void screensaver(bool activate); virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; - virtual void getCenter(SInt32& x, SInt32& y) const; - virtual void getMousePos(SInt32& x, SInt32& y) const; - virtual SInt32 getJumpZoneSize() const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; private: // open/close the secondary screen @@ -87,7 +87,7 @@ private: private: CMutex m_mutex; CString m_name; - ISecondaryScreen* m_screen; + CSecondaryScreen* m_screen; IScreenReceiver* m_server; CNetworkAddress m_serverAddress; bool m_camp; diff --git a/client/CSecondaryScreen.cpp b/client/CSecondaryScreen.cpp new file mode 100644 index 00000000..12358b2a --- /dev/null +++ b/client/CSecondaryScreen.cpp @@ -0,0 +1,263 @@ +#include "CSecondaryScreen.h" +#include "IScreen.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" + +// +// CSecondaryScreen +// + +CSecondaryScreen::CSecondaryScreen() +{ + // do nothing +} + +CSecondaryScreen::~CSecondaryScreen() +{ + // do nothing +} + +bool +CSecondaryScreen::isActive() const +{ + CLock lock(&m_mutex); + return m_active; +} + +void +CSecondaryScreen::run() +{ + // change our priority + CThread::getCurrentThread().setPriority(-7); + + // run event loop + try { + log((CLOG_DEBUG "entering event loop")); + onPreRun(); + getScreen()->mainLoop(); + onPostRun(); + log((CLOG_DEBUG "exiting event loop")); + } + catch (...) { + onPostRun(); + log((CLOG_DEBUG "exiting event loop")); + throw; + } +} + +void +CSecondaryScreen::stop() +{ + getScreen()->exitMainLoop(); +} + +void +CSecondaryScreen::open() +{ + try { + // subclass hook + onPreOpen(); + + // open the screen + getScreen()->open(); + + // create and prepare our window + createWindow(); + + // assume primary has all clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + grabClipboard(id); + } + + // update keyboard state + updateKeys(); + + // disable the screen saver + getScreen()->openScreenSaver(false); + + // subclass hook + onPostOpen(); + } + catch (...) { + close(); + throw; + } + + // hide the cursor + m_active = true; + leave(); +} + +void +CSecondaryScreen::close() +{ + onPreClose(); + getScreen()->closeScreenSaver(); + destroyWindow(); + getScreen()->close(); + onPostClose(); +} + +void +CSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) +{ + CLock lock(&m_mutex); + assert(m_active == false); + + log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); + + getScreen()->syncDesktop(); + + // now active + m_active = true; + + // subclass hook + onPreEnter(); + + // update our keyboard state to reflect the local state + updateKeys(); + + // toggle modifiers that don't match the desired state + setToggleState(mask); + + // warp to requested location + warpCursor(x, y); + + // show mouse + hideWindow(); + + // subclass hook + onPostEnter(); +} + +void +CSecondaryScreen::leave() +{ + log((CLOG_INFO "leaving screen")); + CLock lock(&m_mutex); + assert(m_active == true); + + getScreen()->syncDesktop(); + + // subclass hook + onPreLeave(); + + // hide mouse + showWindow(); + + // subclass hook + onPostLeave(); + + // not active anymore + m_active = false; + + // make sure our idea of clipboard ownership is correct + getScreen()->checkClipboards(); +} + +void +CSecondaryScreen::setClipboard(ClipboardID id, + const IClipboard* clipboard) +{ + getScreen()->setClipboard(id, clipboard); +} + +void +CSecondaryScreen::grabClipboard(ClipboardID id) +{ + getScreen()->setClipboard(id, NULL); +} + +void +CSecondaryScreen::screensaver(bool activate) +{ + getScreen()->screensaver(activate); +} + +void +CSecondaryScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const +{ + getScreen()->getClipboard(id, clipboard); +} + +SInt32 +CSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + +void +CSecondaryScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + getScreen()->syncDesktop(); + getScreen()->getShape(x, y, w, h); +} + +void +CSecondaryScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + getScreen()->syncDesktop(); + getScreen()->getCursorPos(x, y); +} + +void +CSecondaryScreen::onPreRun() +{ + // do nothing +} + +void +CSecondaryScreen::onPostRun() +{ + // do nothing +} + +void +CSecondaryScreen::onPreOpen() +{ + // do nothing +} + +void +CSecondaryScreen::onPostOpen() +{ + // do nothing +} + +void +CSecondaryScreen::onPreClose() +{ + // do nothing +} + +void +CSecondaryScreen::onPostClose() +{ + // do nothing +} + +void +CSecondaryScreen::onPreEnter() +{ + // do nothing +} + +void +CSecondaryScreen::onPostEnter() +{ + // do nothing +} + +void +CSecondaryScreen::onPreLeave() +{ + // do nothing +} + +void +CSecondaryScreen::onPostLeave() +{ + // do nothing +} diff --git a/client/CSecondaryScreen.h b/client/CSecondaryScreen.h new file mode 100644 index 00000000..736fc4d9 --- /dev/null +++ b/client/CSecondaryScreen.h @@ -0,0 +1,141 @@ +#ifndef CSECONDARYSCREEN_H +#define CSECONDARYSCREEN_H + +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CMutex.h" + +class IClipboard; +class IScreen; + +// platform independent base class for secondary screen implementations. +// each platform will derive a class from CSecondaryScreen to handle +// platform dependent operations. +class CSecondaryScreen { +public: + CSecondaryScreen(); + virtual ~CSecondaryScreen(); + + // manipulators + + // enter the screen's message loop. this returns when it detects + // the application should terminate or when stop() is called. + // run() may only be called between open() and close(). + void run(); + + // cause run() to return + void stop(); + + // initialize the screen, hide the cursor, and disable the screen + // saver. start reporting events to the IScreenReceiver (which is + // set through some other interface). + void open(); + + // close the screen. should restore the screen saver. it should + // also simulate key up events for any keys that have simulate key + // down events without a matching key up. without this the client + // will leave its keyboard in the wrong logical state. + void close(); + + // called when the user navigates to this secondary screen. warps + // the cursor to the given absoltue coordinates and unhide it. prepare to + // simulate input events. + void enter(SInt32 x, SInt32 y, KeyModifierMask mask); + + // called when the user navigates off the secondary screen. clean + // up input event simulation and hide the cursor. + void leave(); + + // set the screen's clipboard contents. this is usually called + // soon after an enter(). + void setClipboard(ClipboardID, const IClipboard*); + + // synergy should own the clipboard + void grabClipboard(ClipboardID); + + // activate or deactivate the screen saver + void screensaver(bool activate); + + // keyboard input event synthesis + virtual void keyDown(KeyID, KeyModifierMask) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void keyUp(KeyID, KeyModifierMask) = 0; + + // mouse input event synthesis + virtual void mouseDown(ButtonID) = 0; + virtual void mouseUp(ButtonID) = 0; + virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + virtual void mouseWheel(SInt32 delta) = 0; + + // accessors + + // returns true iff the screen is active (i.e. the user has entered + // the screen) + bool isActive() const; + + // return the contents of the given clipboard + void getClipboard(ClipboardID, IClipboard*) const; + + // returns the size of the zone on the edges of the screen that + // causes the cursor to jump to another screen. default returns 0. + virtual SInt32 getJumpZoneSize() const; + + // get the shape (position of upper-left corner and size) of the + // screen + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + + // get the position of the mouse on the screen + virtual void getCursorPos(SInt32& x, SInt32& y) const; + + // get the platform dependent screen object + virtual IScreen* getScreen() const = 0; + +protected: + // template method hooks. these are called on entry/exit to the + // named method. override to do platform specific operations. + // defaults do nothing. + virtual void onPreRun(); + virtual void onPostRun(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreClose(); + virtual void onPostClose(); + virtual void onPreEnter(); + virtual void onPostEnter(); + virtual void onPreLeave(); + virtual void onPostLeave(); + + // create/destroy the window. this window is generally used to + // receive events and hide the cursor. + virtual void createWindow() = 0; + virtual void destroyWindow() = 0; + + // called when the user navigates off the secondary screen. hide + // the cursor. + virtual void showWindow() = 0; + + // called when the user navigates to this secondary screen. show + // the cursor and prepare to synthesize input events. + virtual void hideWindow() = 0; + + // warp the cursor to the given absolute coordinates + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + + // check the current keyboard state. normally a screen will save + // the keyboard state in this method and use this shadow state + // when synthesizing events. + virtual void updateKeys() = 0; + + // toggle modifiers that don't match the given state + virtual void setToggleState(KeyModifierMask) = 0; + +private: + CMutex m_mutex; + + // m_active is true if this screen has been entered + bool m_active; +}; + +#endif diff --git a/client/CServerProxy.cpp b/client/CServerProxy.cpp index 3b8216a9..2aa59a4a 100644 --- a/client/CServerProxy.cpp +++ b/client/CServerProxy.cpp @@ -131,7 +131,7 @@ CServerProxy::run() } else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { - screenSaver(); + screensaver(); } else if (memcmp(code, kMsgQInfo, 4) == 0) { @@ -484,7 +484,7 @@ CServerProxy::mouseWheel() } void -CServerProxy::screenSaver() +CServerProxy::screensaver() { // parse SInt8 on; @@ -492,7 +492,7 @@ CServerProxy::screenSaver() log((CLOG_DEBUG1 "recv screen saver on=%d", on)); // forward - getClient()->screenSaver(on != 0); + getClient()->screensaver(on != 0); } void @@ -501,7 +501,7 @@ CServerProxy::queryInfo() // get current info CClientInfo info; getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); - getClient()->getMousePos(info.m_mx, info.m_my); + getClient()->getCursorPos(info.m_mx, info.m_my); info.m_zoneSize = getClient()->getJumpZoneSize(); // send it diff --git a/client/CServerProxy.h b/client/CServerProxy.h index 198b3d9b..0370a646 100644 --- a/client/CServerProxy.h +++ b/client/CServerProxy.h @@ -56,7 +56,7 @@ private: void mouseUp(); void mouseMove(); void mouseWheel(); - void screenSaver(); + void screensaver(); void queryInfo(); void infoAcknowledgment(); diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index aea09204..1207cd8f 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -1,8 +1,9 @@ #include "CXWindowsSecondaryScreen.h" -#include "IScreenReceiver.h" #include "CXWindowsClipboard.h" +#include "CXWindowsScreen.h" #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" +#include "IScreenReceiver.h" #include "XScreen.h" #include "CThread.h" #include "CLog.h" @@ -26,153 +27,16 @@ // CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) : - m_receiver(receiver), - m_window(None), - m_active(false) + CSecondaryScreen(), + m_window(None) { - assert(m_receiver != NULL); + m_screen = new CXWindowsScreen(receiver, this); } CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() { assert(m_window == None); -} - -void -CXWindowsSecondaryScreen::run() -{ - assert(m_window != None); - - // change our priority - CThread::getCurrentThread().setPriority(-7); - - // run event loop - try { - log((CLOG_INFO "entering event loop")); - mainLoop(); - log((CLOG_INFO "exiting event loop")); - } - catch (...) { - log((CLOG_INFO "exiting event loop")); - throw; - } -} - -void -CXWindowsSecondaryScreen::stop() -{ - exitMainLoop(); -} - -void -CXWindowsSecondaryScreen::open() -{ - assert(m_window == None); - - try { - // open the display - openDisplay(); - - // create and prepare our window - createWindow(); - - // initialize the clipboards; assume primary has all clipboards - initClipboards(m_window); - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - grabClipboard(id); - } - - // check for peculiarities - // FIXME -- may have to get these from some database - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; -// m_numLockHalfDuplex = true; -// m_capsLockHalfDuplex = true; - - // get the display - CDisplayLock display(this); - - // update key state - updateKeys(display); - updateKeycodeMap(display); - updateModifierMap(display); - updateModifiers(display); - - // disable the screen saver - installScreenSaver(); - } - catch (...) { - close(); - throw; - } - - // hide the cursor - m_active = true; - leave(); -} - -void -CXWindowsSecondaryScreen::close() -{ - uninstallScreenSaver(); - destroyWindow(); - closeDisplay(); -} - -void -CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) -{ - assert(m_window != None); - assert(m_active == false); - - log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); - - CDisplayLock display(this); - - // now active - m_active = true; - - // update our keyboard state to reflect the local state - updateKeys(display); - updateModifiers(display); - - // toggle modifiers that don't match the desired state - unsigned int xMask = maskToX(mask); - if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) { - toggleKey(display, XK_Caps_Lock, m_capsLockMask); - } - if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) { - toggleKey(display, XK_Num_Lock, m_numLockMask); - } - if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { - toggleKey(display, XK_Scroll_Lock, m_scrollLockMask); - } - - // warp to requested location - warpCursor(x, y); - - // show mouse - hideWindow(); -} - -void -CXWindowsSecondaryScreen::leave() -{ - assert(m_window != None); - assert(m_active == true); - - log((CLOG_INFO "leaving screen")); - - CDisplayLock display(this); - - // hide mouse - showWindow(); - - // not active anymore - m_active = false; - - // make sure our idea of clipboard ownership is correct - checkClipboard(); + delete m_screen; } void @@ -236,7 +100,7 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) void CXWindowsSecondaryScreen::mouseDown(ButtonID button) { - CDisplayLock display(this); + CDisplayLock display(m_screen); XTestFakeButtonEvent(display, mapButton(button), True, CurrentTime); XSync(display, False); } @@ -244,7 +108,7 @@ CXWindowsSecondaryScreen::mouseDown(ButtonID button) void CXWindowsSecondaryScreen::mouseUp(ButtonID button) { - CDisplayLock display(this); + CDisplayLock display(m_screen); XTestFakeButtonEvent(display, mapButton(button), False, CurrentTime); XSync(display, False); } @@ -252,7 +116,6 @@ CXWindowsSecondaryScreen::mouseUp(ButtonID button) void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) { - CDisplayLock display(this); warpCursor(x, y); } @@ -268,7 +131,7 @@ CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) } // send as many clicks as necessary - CDisplayLock display(this); + CDisplayLock display(m_screen); for (; delta >= 120; delta -= 120) { XTestFakeButtonEvent(display, button, True, CurrentTime); XTestFakeButtonEvent(display, button, False, CurrentTime); @@ -276,63 +139,29 @@ CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) XSync(display, False); } -void -CXWindowsSecondaryScreen::setClipboard(ClipboardID id, - const IClipboard* clipboard) +IScreen* +CXWindowsSecondaryScreen::getScreen() const { - setDisplayClipboard(id, clipboard); + return m_screen; } void -CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) +CXWindowsSecondaryScreen::onScreensaver(bool) { - setDisplayClipboard(id, NULL); + // ignore } void -CXWindowsSecondaryScreen::screenSaver(bool activate) +CXWindowsSecondaryScreen::onError() { - CDisplayLock display(this); - if (activate) { - getScreenSaver()->activate(); - } - else { - getScreenSaver()->deactivate(); - } -} - -void -CXWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const -{ - CDisplayLock display(this); - getCursorPos(x, y); -} - -void -CXWindowsSecondaryScreen::getShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - getScreenShape(x, y, w, h); -} - -SInt32 -CXWindowsSecondaryScreen::getJumpZoneSize() const -{ - return 0; -} - -void -CXWindowsSecondaryScreen::getClipboard(ClipboardID id, - IClipboard* clipboard) const -{ - getDisplayClipboard(id, clipboard); + // ignore + // FIXME -- forward this? to whom? } bool -CXWindowsSecondaryScreen::onPreDispatch(const CEvent* event) +CXWindowsSecondaryScreen::onPreDispatch(const CEvent*) { - // forward to superclass - return CXWindowsScreen::onPreDispatch(event); + return false; } bool @@ -344,33 +173,117 @@ CXWindowsSecondaryScreen::onEvent(CEvent* event) // handle event switch (xevent.type) { case MappingNotify: - { - // keyboard mapping changed - CDisplayLock display(this); - XRefreshKeyboardMapping(&xevent.xmapping); - updateKeys(display); - updateKeycodeMap(display); - updateModifierMap(display); - updateModifiers(display); - } + // keyboard mapping changed + updateKeys(); return true; case LeaveNotify: - { - // mouse moved out of hider window somehow. hide the window. - assert(m_window != None); - CDisplayLock display(this); - hideWindow(); - } + // mouse moved out of hider window somehow. hide the window. + hideWindow(); return true; } } void -CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id) +CXWindowsSecondaryScreen::onPreRun() { - // tell client that the clipboard was grabbed locally - m_receiver->onGrabClipboard(id); + assert(m_window != None); +} + +void +CXWindowsSecondaryScreen::onPreOpen() +{ + assert(m_window == None); +} + +void +CXWindowsSecondaryScreen::onPostOpen() +{ + // check for peculiarities + // FIXME -- may have to get these from some database + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +// m_numLockHalfDuplex = true; +// m_capsLockHalfDuplex = true; + +} + +void +CXWindowsSecondaryScreen::onPreEnter() +{ + assert(m_window != None); +} + +void +CXWindowsSecondaryScreen::onPreLeave() +{ + assert(m_window != None); +} + +void +CXWindowsSecondaryScreen::createWindow() +{ + { + CDisplayLock display(m_screen); + + // verify the availability of the XTest extension + int majorOpcode, firstEvent, firstError; + if (!XQueryExtension(display, XTestExtensionName, + &majorOpcode, &firstEvent, &firstError)) { + // FIXME -- subclass exception for more info? + throw XScreenOpenFailure(); + } + + // cursor hider window attributes. this window is used to hide the + // cursor when it's not on the screen. the window is hidden as soon + // as the cursor enters the screen or the display's real cursor is + // moved. + XSetWindowAttributes attr; + attr.event_mask = LeaveWindowMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = m_screen->getBlankCursor(); + + // create the cursor hider window + m_window = XCreateWindow(display, m_screen->getRoot(), + 0, 0, 1, 1, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + if (m_window == None) { + throw XScreenOpenFailure(); + } + log((CLOG_DEBUG "window is 0x%08x", m_window)); + + // become impervious to server grabs + XTestGrabControl(display, True); + } + + // tell our superclass about the window + m_screen->setWindow(m_window); +} + +void +CXWindowsSecondaryScreen::destroyWindow() +{ + CDisplayLock display(m_screen); + if (display != NULL) { + // release keys that are still pressed + releaseKeys(display); + + // no longer impervious to server grabs + XTestGrabControl(display, False); + + // destroy window + if (m_window != None) { + XDestroyWindow(display, m_window); + m_window = None; + } + + // update + XSync(display, False); + } } void @@ -380,106 +293,55 @@ CXWindowsSecondaryScreen::showWindow() // somewhere else on the screen) SInt32 x, y; getCursorPos(x, y); - XMoveWindow(getDisplay(), m_window, x, y); + CDisplayLock display(m_screen); + XMoveWindow(display, m_window, x, y); // raise and show the hider window. take activation. // FIXME -- take focus? - XMapRaised(getDisplay(), m_window); + XMapRaised(display, m_window); /* XXX -- this should have no effect // hide cursor by moving it into the hider window - XWarpPointer(getDisplay(), None, m_window, 0, 0, 0, 0, 0, 0); + XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0); */ } void CXWindowsSecondaryScreen::hideWindow() { - XUnmapWindow(getDisplay(), m_window); + assert(m_window != None); + + CDisplayLock display(m_screen); + XUnmapWindow(display, m_window); } void CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) { - XTestFakeMotionEvent(getDisplay(), getScreen(), x, y, CurrentTime); - XSync(getDisplay(), False); + CDisplayLock display(m_screen); + Display* pDisplay = display; + XTestFakeMotionEvent(display, DefaultScreen(pDisplay), x, y, CurrentTime); + XSync(display, False); } void -CXWindowsSecondaryScreen::checkClipboard() +CXWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) { - // do nothing, we're always up to date -} + CDisplayLock display(m_screen); -void -CXWindowsSecondaryScreen::createWindow() -{ - CDisplayLock display(this); - - // verify the availability of the XTest extension - int majorOpcode, firstEvent, firstError; - if (!XQueryExtension(display, XTestExtensionName, - &majorOpcode, &firstEvent, &firstError)) { - // FIXME -- subclass exception for more info? - throw XScreenOpenFailure(); + // toggle modifiers that don't match the desired state + unsigned int xMask = maskToX(mask); + if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) { + toggleKey(display, XK_Caps_Lock, m_capsLockMask); } - - // cursor hider window attributes. this window is used to hide the - // cursor when it's not on the screen. the window is hidden as soon - // as the cursor enters the screen or the display's real cursor is - // moved. - XSetWindowAttributes attr; - attr.event_mask = LeaveWindowMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = getBlankCursor(); - - // create the cursor hider window - m_window = XCreateWindow(display, getRoot(), - 0, 0, 1, 1, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - if (m_window == None) { - throw XScreenOpenFailure(); + if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) { + toggleKey(display, XK_Num_Lock, m_numLockMask); } - log((CLOG_DEBUG "window is 0x%08x", m_window)); - - // become impervious to server grabs - XTestGrabControl(display, True); -} - -void -CXWindowsSecondaryScreen::destroyWindow() -{ - releaseKeys(); - - CDisplayLock display(this); - if (display != NULL) { - // no longer impervious to server grabs - XTestGrabControl(display, False); - - // destroy window - if (m_window != None) { - XDestroyWindow(display, m_window); - m_window = None; - } + if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { + toggleKey(display, XK_Scroll_Lock, m_scrollLockMask); } } -void -CXWindowsSecondaryScreen::installScreenSaver() -{ - getScreenSaver()->disable(); -} - -void -CXWindowsSecondaryScreen::uninstallScreenSaver() -{ - getScreenSaver()->enable(); -} - unsigned int CXWindowsSecondaryScreen::mapButton(ButtonID id) const { @@ -848,7 +710,7 @@ CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) } // lock display - CDisplayLock display(this); + CDisplayLock display(m_screen); // generate key events for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { @@ -910,27 +772,24 @@ CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const } void -CXWindowsSecondaryScreen::releaseKeys() +CXWindowsSecondaryScreen::releaseKeys(Display* display) { - CDisplayLock display(this); + assert(display != NULL); - if (display != NULL) { - // key up for each key that's down - for (UInt32 i = 0; i < 256; ++i) { - if (m_keys[i]) { - XTestFakeKeyEvent(display, i, False, CurrentTime); - m_keys[i] = false; - } + // key up for each key that's down + for (UInt32 i = 0; i < 256; ++i) { + if (m_keys[i]) { + XTestFakeKeyEvent(display, i, False, CurrentTime); + m_keys[i] = false; } - - // update - XSync(display, False); } } void -CXWindowsSecondaryScreen::updateKeys(Display* display) +CXWindowsSecondaryScreen::updateKeys() { + CDisplayLock display(m_screen); + // ask server which keys are pressed char keys[32]; XQueryKeymap(display, keys); @@ -946,6 +805,11 @@ CXWindowsSecondaryScreen::updateKeys(Display* display) m_keys[j + 6] = ((keys[i] & 0x40) != 0); m_keys[j + 7] = ((keys[i] & 0x80) != 0); } + + // update mappings and current modifiers + updateKeycodeMap(display); + updateModifierMap(display); + updateModifiers(display); } void diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 558b7526..d2797d65 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -1,47 +1,55 @@ #ifndef CXWINDOWSSECONDARYSCREEN_H #define CXWINDOWSSECONDARYSCREEN_H -#include "CXWindowsScreen.h" -#include "ISecondaryScreen.h" +#include "CSecondaryScreen.h" +#include "IScreenEventHandler.h" #include "stdmap.h" #include "stdvector.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif +class CXWindowsScreen; class IScreenReceiver; -class CXWindowsSecondaryScreen : public CXWindowsScreen, - public ISecondaryScreen { +class CXWindowsSecondaryScreen : + public CSecondaryScreen, public IScreenEventHandler { public: CXWindowsSecondaryScreen(IScreenReceiver*); virtual ~CXWindowsSecondaryScreen(); - // ISecondaryScreen overrides - virtual void run(); - virtual void stop(); - virtual void open(); - virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, - KeyModifierMask mask); - virtual void leave(); + // CSecondaryScreen overrides virtual void keyDown(KeyID, KeyModifierMask); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); virtual void keyUp(KeyID, KeyModifierMask); virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); - virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void mouseMove(SInt32 x, SInt32 y); virtual void mouseWheel(SInt32 delta); - virtual void setClipboard(ClipboardID, const IClipboard*); - virtual void grabClipboard(ClipboardID); - virtual void screenSaver(bool activate); - virtual void getMousePos(SInt32& x, SInt32& y) const; - virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - virtual SInt32 getJumpZoneSize() const; - virtual void getClipboard(ClipboardID, IClipboard*) const; + virtual IScreen* getScreen() const; -protected: - // CXWindowsScreen overrides + // IScreenEventHandler overrides + virtual void onError(); + virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); - virtual void onLostClipboard(ClipboardID); + +protected: + // CSecondaryScreen overrides + virtual void onPreRun(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreEnter(); + virtual void onPreLeave(); + virtual void createWindow(); + virtual void destroyWindow(); + virtual void showWindow(); + virtual void hideWindow(); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void updateKeys(); + virtual void setToggleState(KeyModifierMask); private: enum EKeyAction { kPress, kRelease, kRepeat }; @@ -62,26 +70,6 @@ private: typedef std::map KeyCodeMap; typedef std::map ModifierMap; - void showWindow(); - void hideWindow(); - - // warp the mouse to the specified position - void warpCursor(SInt32 x, SInt32 y); - - // check clipboard ownership and, if necessary, tell the receiver - // of a grab. - void checkClipboard(); - - // create/destroy window - // also attach to desktop; this destroys and recreates the window - // as necessary. - void createWindow(); - void destroyWindow(); - - // start/stop watch for screen saver changes - void installScreenSaver(); - void uninstallScreenSaver(); - unsigned int mapButton(ButtonID button) const; unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, @@ -91,8 +79,7 @@ private: void doKeystrokes(const Keystrokes&, SInt32 count); unsigned int maskToX(KeyModifierMask) const; - void releaseKeys(); - void updateKeys(Display* display); + void releaseKeys(Display*); void updateKeycodeMap(Display* display); void updateModifiers(Display* display); void updateModifierMap(Display* display); @@ -100,12 +87,9 @@ private: static bool isToggleKeysym(KeySym); private: - IScreenReceiver* m_receiver; + CXWindowsScreen* m_screen; Window m_window; - // m_active is true if this screen has been entered - bool m_active; - // note toggle keys that toggles on up/down (false) or on // transition (true) bool m_numLockHalfDuplex; diff --git a/client/ISecondaryScreen.h b/client/ISecondaryScreen.h deleted file mode 100644 index fe0264f7..00000000 --- a/client/ISecondaryScreen.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef ISECONDARYSCREEN_H -#define ISECONDARYSCREEN_H - -#include "IInterface.h" -#include "ClipboardTypes.h" -#include "KeyTypes.h" -#include "MouseTypes.h" - -class IClipboard; - -class ISecondaryScreen : public IInterface { -public: - // manipulators - - // enter the screen's message loop. this returns when it detects - // the application should terminate or when stop() is called. - // the screen must be open()'d before run() and must not be - // close()'d until run() returns. - virtual void run() = 0; - - // cause run() to return - virtual void stop() = 0; - - // initialize the screen, hide the cursor, and disable the screen - // saver. start reporting events to the IScreenReceiver (which is - // set through some other interface). - virtual void open() = 0; - - // close the screen. should restore the screen saver. it should - // also simulate key up events for any keys that have simulate key - // down events without a matching key up. without this the client - // will leave its keyboard in the wrong logical state. - virtual void close() = 0; - - // called when the user navigates to the secondary screen. warp - // the cursor to the given coordinates and unhide it. prepare to - // simulate input events. - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, - KeyModifierMask mask) = 0; - - // called when the user navigates off the secondary screen. clean - // up input event simulation and hide the cursor. - virtual void leave() = 0; - - // keyboard input simulation - virtual void keyDown(KeyID, KeyModifierMask) = 0; - virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void keyUp(KeyID, KeyModifierMask) = 0; - - // mouse input simulation - virtual void mouseDown(ButtonID) = 0; - virtual void mouseUp(ButtonID) = 0; - virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; - virtual void mouseWheel(SInt32 delta) = 0; - - // set the screen's clipboard contents. this is usually called - // soon after an enter(). - virtual void setClipboard(ClipboardID, const IClipboard*) = 0; - - // take ownership of clipboard - virtual void grabClipboard(ClipboardID) = 0; - - // activate or deactivate the screen saver - virtual void screenSaver(bool activate) = 0; - - // accessors - - // get the position of the mouse on the screen - virtual void getMousePos(SInt32& x, SInt32& y) const = 0; - - // get the size of the screen - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const = 0; - - // get the size of jump zone - virtual SInt32 getJumpZoneSize() const = 0; - - // get the screen's clipboard contents - virtual void getClipboard(ClipboardID, IClipboard*) const = 0; -}; - -#endif diff --git a/client/Makefile.am b/client/Makefile.am index 27d35cc9..5c06a480 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -5,13 +5,14 @@ DEPTH = .. bin_PROGRAMS = synergy synergy_SOURCES = \ CClient.cpp \ + CSecondaryScreen.cpp \ CServerProxy.cpp \ CXWindowsSecondaryScreen.cpp \ client.cpp \ CClient.h \ + CSecondaryScreen.h \ CServerProxy.h \ CXWindowsSecondaryScreen.h \ - ISecondaryScreen.h \ $(NULL) synergy_LDADD = \ $(DEPTH)/platform/libplatform.a \ diff --git a/client/client.cpp b/client/client.cpp index 620e7846..cda8e0a9 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -116,9 +116,11 @@ realMain(CMutex* mutex) if (!locked && mutex != NULL) { mutex->lock(); } - s_client->close(); - delete s_client; - s_client = NULL; + if (s_client != NULL) { + s_client->close(); + delete s_client; + s_client = NULL; + } CLog::setLock(NULL); s_logMutex = NULL; throw; diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index 3176ce69..e9b46721 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -3,14 +3,22 @@ #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CClipboard.h" +#include "IScreenEventHandler.h" +#include "IScreenReceiver.h" #include "XScreen.h" #include "CLock.h" #include "CThread.h" #include "CLog.h" #include "IJob.h" -#include "CString.h" -#include +//#include "CString.h" +//#include #include +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +#endif // // CXWindowsScreen::CTimer @@ -68,15 +76,24 @@ CXWindowsScreen::CTimer::operator<(const CTimer& t) const CXWindowsScreen* CXWindowsScreen::s_screen = NULL; -CXWindowsScreen::CXWindowsScreen() : +CXWindowsScreen::CXWindowsScreen(IScreenReceiver* receiver, + IScreenEventHandler* eventHandler) : m_display(NULL), m_root(None), + m_stop(false), + m_receiver(receiver), + m_eventHandler(eventHandler), + m_window(None), m_x(0), m_y(0), m_w(0), m_h(0), - m_stop(false), - m_screenSaver(NULL) + m_screensaver(NULL), + m_screensaverNotify(false), + m_atomScreensaver(None) { - assert(s_screen == NULL); + assert(s_screen == NULL); + assert(m_receiver != NULL); + assert(m_eventHandler != NULL); + s_screen = this; } @@ -120,6 +137,77 @@ CXWindowsScreen::removeTimerNoLock(IJob* job) m_timers.swap(tmp); } +void +CXWindowsScreen::setWindow(Window window) +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + // destroy the clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + delete m_clipboard[id]; + m_clipboard[id] = NULL; + } + + // save the new window + m_window = window; + + // initialize the clipboards + if (m_window != None) { + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = new CXWindowsClipboard(m_display, m_window, id); + } + } +} + +Window +CXWindowsScreen::getRoot() const +{ + assert(m_display != NULL); + return m_root; +} + +Cursor +CXWindowsScreen::getBlankCursor() const +{ + return m_cursor; +} + +void +CXWindowsScreen::open() +{ + assert(m_display == NULL); + + // set the X I/O error handler so we catch the display disconnecting + XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); + + // get the DISPLAY + const char* display = getenv("DISPLAY"); + if (display == NULL) { + display = ":0.0"; + } + + // open the display + log((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); + m_display = XOpenDisplay(display); + if (m_display == NULL) { + throw XScreenOpenFailure(); + } + + // get root window + m_root = DefaultRootWindow(m_display); + + // create the transparent cursor + createBlankCursor(); + + // get screen shape + updateScreenShape(); + + // initialize the screen saver + m_atomScreensaver = XInternAtom(m_display, "SCREENSAVER", False); + m_screensaver = new CXWindowsScreenSaver(this, m_display); +} + void CXWindowsScreen::mainLoop() { @@ -147,7 +235,7 @@ CXWindowsScreen::mainLoop() // have a go at it. m_mutex.unlock(); if (!onPreDispatch(&event)) { - onEvent(&event); + m_eventHandler->onEvent(&event); } m_mutex.lock(); } @@ -162,23 +250,210 @@ CXWindowsScreen::exitMainLoop() m_stop = true; } +void +CXWindowsScreen::close() +{ + CLock lock(&m_mutex); + + // done with screen saver + delete m_screensaver; + m_screensaver = NULL; + + // destroy clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + delete m_clipboard[id]; + m_clipboard[id] = NULL; + } + + // close the display + if (m_display != NULL) { + XCloseDisplay(m_display); + m_display = NULL; + log((CLOG_DEBUG "closed display")); + } + XSetIOErrorHandler(NULL); +} + bool -CXWindowsScreen::onPreDispatch(const CEvent* event) +CXWindowsScreen::setClipboard(ClipboardID id, const IClipboard* clipboard) +{ + CLock lock(&m_mutex); + + // fail if we don't have the requested clipboard + if (m_clipboard[id] == NULL) { + return false; + } + + // get the actual time. ICCCM does not allow CurrentTime. + Time timestamp = CXWindowsUtil::getCurrentTime( + m_display, m_clipboard[id]->getWindow()); + + if (clipboard != NULL) { + // save clipboard data + return CClipboard::copy(m_clipboard[id], clipboard, timestamp); + } + else { + // assert clipboard ownership + if (!m_clipboard[id]->open(timestamp)) { + return false; + } + m_clipboard[id]->empty(); + m_clipboard[id]->close(); + return true; + } +} + +void +CXWindowsScreen::checkClipboards() +{ + // do nothing, we're always up to date +} + +void +CXWindowsScreen::openScreenSaver(bool notify) +{ + CLock lock(&m_mutex); + assert(m_screensaver != NULL); + + m_screensaverNotify = notify; + if (m_screensaverNotify) { + m_screensaver->setNotify(m_window); + } + else { + m_screensaver->disable(); + } +} + +void +CXWindowsScreen::closeScreenSaver() +{ + CLock lock(&m_mutex); + if (m_screensaver != NULL) { + if (m_screensaverNotify) { + m_screensaver->setNotify(None); + } + else { + m_screensaver->enable(); + } + } +} + +void +CXWindowsScreen::screensaver(bool activate) +{ + CLock lock(&m_mutex); + assert(m_screensaver != NULL); + + if (activate) { + m_screensaver->activate(); + } + else { + m_screensaver->deactivate(); + } +} + +void +CXWindowsScreen::syncDesktop() +{ + // do nothing; X doesn't suffer from this bogosity +} + +bool +CXWindowsScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const +{ + assert(clipboard != NULL); + + // block others from using the display while we get the clipboard + CLock lock(&m_mutex); + + // fail if we don't have the requested clipboard + if (m_clipboard[id] == NULL) { + return false; + } + + // get the actual time. ICCCM does not allow CurrentTime. + Time timestamp = CXWindowsUtil::getCurrentTime( + m_display, m_clipboard[id]->getWindow()); + + // copy the clipboard + return CClipboard::copy(clipboard, m_clipboard[id], timestamp); +} + +void +CXWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + Window root, window; + int mx, my, xWindow, yWindow; + unsigned int mask; + if (XQueryPointer(m_display, getRoot(), &root, &window, + &mx, &my, &xWindow, &yWindow, &mask)) { + x = mx; + y = my; + } + else { + getCursorCenter(x, y); + } +} + +void +CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + x = m_x + (m_w >> 1); + y = m_y + (m_h >> 1); +} + +void +CXWindowsScreen::updateScreenShape() +{ + m_x = 0; + m_y = 0; + m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); + m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); + log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); +} + +bool +CXWindowsScreen::onPreDispatch(CEvent* event) { assert(event != NULL); - const XEvent* xevent = &event->m_event; + XEvent* xevent = &event->m_event; switch (xevent->type) { + case MappingNotify: + // keyboard mapping changed + XRefreshKeyboardMapping(&xevent->xmapping); + + // pass event on + break; + case SelectionClear: { // we just lost the selection. that means someone else // grabbed the selection so this screen is now the - // selection owner. report that to the subclass. + // selection owner. report that to the receiver. ClipboardID id = getClipboardID(xevent->xselectionclear.selection); if (id != kClipboardEnd) { log((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); m_clipboard[id]->lost(xevent->xselectionclear.time); - onLostClipboard(id); + m_receiver->onGrabClipboard(id); return true; } } @@ -225,6 +500,15 @@ CXWindowsScreen::onPreDispatch(const CEvent* event) } break; + case ClientMessage: + if (xevent->xclient.message_type == m_atomScreensaver || + xevent->xclient.format == 32) { + // screen saver activation/deactivation event + m_eventHandler->onScreensaver(xevent->xclient.data.l[0] != 0); + return true; + } + break; + case DestroyNotify: // looks like one of the windows that requested a clipboard // transfer has gone bye-bye. @@ -237,150 +521,10 @@ CXWindowsScreen::onPreDispatch(const CEvent* event) // let screen saver have a go { CLock lock(&m_mutex); - m_screenSaver->onPreDispatch(xevent); + m_screensaver->onPreDispatch(xevent); } - return false; -} - -void -CXWindowsScreen::openDisplay() -{ - assert(m_display == NULL); - - // set the X I/O error handler so we catch the display disconnecting - XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); - - // get the DISPLAY - const char* display = getenv("DISPLAY"); - if (display == NULL) { - display = ":0.0"; - } - - // open the display - log((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); - m_display = XOpenDisplay(display); - if (m_display == NULL) { - throw XScreenOpenFailure(); - } - - // get default screen and root window - m_screen = DefaultScreen(m_display); - m_root = RootWindow(m_display, m_screen); - - // create the transparent cursor - createBlankCursor(); - - // get screen shape - updateScreenShape(); - - // initialize the screen saver - m_screenSaver = new CXWindowsScreenSaver(this, m_display); -} - -void -CXWindowsScreen::closeDisplay() -{ - CLock lock(&m_mutex); - - // done with screen saver - delete m_screenSaver; - m_screenSaver = NULL; - - // destroy clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - delete m_clipboard[id]; - m_clipboard[id] = NULL; - } - - // close the display - if (m_display != NULL) { - XCloseDisplay(m_display); - m_display = NULL; - log((CLOG_DEBUG "closed display")); - } - XSetIOErrorHandler(NULL); -} - -Display* -CXWindowsScreen::getDisplay() const -{ - return m_display; -} - -int -CXWindowsScreen::getScreen() const -{ - assert(m_display != NULL); - return m_screen; -} - -Window -CXWindowsScreen::getRoot() const -{ - assert(m_display != NULL); - return m_root; -} - -void -CXWindowsScreen::initClipboards(Window window) -{ - assert(m_display != NULL); - assert(window != None); - - // initialize clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_clipboard[id] = new CXWindowsClipboard(m_display, window, id); - } -} - -void -CXWindowsScreen::updateScreenShape() -{ - m_x = 0; - m_y = 0; - m_w = WidthOfScreen(ScreenOfDisplay(m_display, m_screen)); - m_h = HeightOfScreen(ScreenOfDisplay(m_display, m_screen)); - log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); -} - -void -CXWindowsScreen::getScreenShape(SInt32& x, SInt32& y, - SInt32& w, SInt32& h) const -{ - assert(m_display != NULL); - - x = m_x; - y = m_y; - w = m_w; - h = m_h; -} - -void -CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const -{ - assert(m_display != NULL); - - Window root, window; - int mx, my, xWindow, yWindow; - unsigned int mask; - if (XQueryPointer(m_display, getRoot(), &root, &window, - &mx, &my, &xWindow, &yWindow, &mask)) { - x = mx; - y = my; - } - else { - getCursorCenter(x, y); - } -} - -void -CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const -{ - assert(m_display != NULL); - - x = m_x + (m_w >> 1); - y = m_y + (m_h >> 1); + return m_eventHandler->onPreDispatch(event); } void @@ -417,30 +561,6 @@ CXWindowsScreen::createBlankCursor() XFreePixmap(m_display, bitmap); } -Cursor -CXWindowsScreen::getBlankCursor() const -{ - return m_cursor; -} - -ClipboardID -CXWindowsScreen::getClipboardID(Atom selection) const -{ - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - if (m_clipboard[id] != NULL && - m_clipboard[id]->getSelection() == selection) { - return id; - } - } - return kClipboardEnd; -} - -void -CXWindowsScreen::onUnexpectedClose() -{ - // do nothing -} - bool CXWindowsScreen::processTimers() { @@ -486,62 +606,16 @@ CXWindowsScreen::processTimers() } } -CXWindowsScreenSaver* -CXWindowsScreen::getScreenSaver() const +ClipboardID +CXWindowsScreen::getClipboardID(Atom selection) const { - return m_screenSaver; -} - -bool -CXWindowsScreen::setDisplayClipboard(ClipboardID id, - const IClipboard* clipboard) -{ - CLock lock(&m_mutex); - - // fail if we don't have the requested clipboard - if (m_clipboard[id] == NULL) { - return false; - } - - // get the actual time. ICCCM does not allow CurrentTime. - Time timestamp = CXWindowsUtil::getCurrentTime( - m_display, m_clipboard[id]->getWindow()); - - if (clipboard != NULL) { - // save clipboard data - return CClipboard::copy(m_clipboard[id], clipboard, timestamp); - } - else { - // assert clipboard ownership - if (!m_clipboard[id]->open(timestamp)) { - return false; + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_clipboard[id] != NULL && + m_clipboard[id]->getSelection() == selection) { + return id; } - m_clipboard[id]->empty(); - m_clipboard[id]->close(); - return true; } -} - -bool -CXWindowsScreen::getDisplayClipboard(ClipboardID id, - IClipboard* clipboard) const -{ - assert(clipboard != NULL); - - // block others from using the display while we get the clipboard - CLock lock(&m_mutex); - - // fail if we don't have the requested clipboard - if (m_clipboard[id] == NULL) { - return false; - } - - // get the actual time. ICCCM does not allow CurrentTime. - Time timestamp = CXWindowsUtil::getCurrentTime( - m_display, m_clipboard[id]->getWindow()); - - // copy the clipboard - return CClipboard::copy(clipboard, m_clipboard[id], timestamp); + return kClipboardEnd; } void @@ -584,17 +658,17 @@ CXWindowsScreen::ioErrorHandler(Display*) // so we set it to NULL), and exit. log((CLOG_WARN "X display has unexpectedly disconnected")); s_screen->m_display = NULL; - s_screen->onUnexpectedClose(); + s_screen->m_eventHandler->onError(); log((CLOG_CRIT "quiting due to X display disconnection")); exit(17); } // -// CXWindowsScreen::CDisplayLock +// CDisplayLock // -CXWindowsScreen::CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : +CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : m_mutex(&screen->m_mutex), m_display(screen->m_display) { @@ -603,12 +677,12 @@ CXWindowsScreen::CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : m_mutex->lock(); } -CXWindowsScreen::CDisplayLock::~CDisplayLock() +CDisplayLock::~CDisplayLock() { m_mutex->unlock(); } -CXWindowsScreen::CDisplayLock::operator Display*() const +CDisplayLock::operator Display*() const { return m_display; } diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index 9900b7ec..e164c583 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -1,6 +1,7 @@ #ifndef CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H +#include "IScreen.h" #include "ClipboardTypes.h" #include "CMutex.h" #include "CStopwatch.h" @@ -13,9 +14,9 @@ #include #include -class IClipboard; class IJob; -class IScreenSaver; +class IScreenEventHandler; +class IScreenReceiver; class CXWindowsClipboard; class CXWindowsScreenSaver; @@ -25,9 +26,9 @@ public: SInt32 m_result; }; -class CXWindowsScreen { +class CXWindowsScreen : public IScreen { public: - CXWindowsScreen(); + CXWindowsScreen(IScreenReceiver*, IScreenEventHandler*); virtual ~CXWindowsScreen(); // manipulators @@ -39,94 +40,41 @@ public: void addTimer(IJob*, double timeout); void removeTimer(IJob*); -protected: - class CDisplayLock { - public: - CDisplayLock(const CXWindowsScreen*); - ~CDisplayLock(); + // set the window (created by the subclass). this performs some + // initialization and saves the window in case it's needed later. + void setWindow(Window); - operator Display*() const; + // accessors - private: - const CMutex* m_mutex; - Display* m_display; - }; - friend class CDisplayLock; - - // runs an event loop and returns when exitMainLoop() is called - void mainLoop(); - - // force mainLoop() to return - void exitMainLoop(); - - // open the X display. calls onOpenDisplay() after opening the display, - // getting the screen, its size, and root window. then it starts the - // event thread. - void openDisplay(); - - // destroy the window and close the display. calls onCloseDisplay() - // after the event thread has been shut down but before the display - // is closed. - void closeDisplay(); - - // get the Display*. only use this when you know the display is - // locked but don't have the CDisplayLock available. - Display* getDisplay() const; - - // get the opened screen and its root window. to get the display - // create a CDisplayLock object passing this. while the object - // exists no other threads may access the display. do not save - // the Display* beyond the lifetime of the CDisplayLock. - int getScreen() const; + // get the root window of the screen Window getRoot() const; - // initialize the clipboards - void initClipboards(Window); - - // update screen size cache - void updateScreenShape(); - - // get the shape of the screen - void getScreenShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const; - - // get the current cursor position - void getCursorPos(SInt32& x, SInt32& y) const; - - // get the cursor center position - void getCursorCenter(SInt32& x, SInt32& y) const; - // get a cursor that is transparent everywhere Cursor getBlankCursor() const; - // set the contents of the clipboard (i.e. primary selection) - bool setDisplayClipboard(ClipboardID, - const IClipboard* clipboard); - - // copy the clipboard contents to clipboard - bool getDisplayClipboard(ClipboardID, - IClipboard* clipboard) const; - - // get the screen saver object - CXWindowsScreenSaver* - getScreenSaver() const; - - // called for each event before event translation and dispatch. return - // true to skip translation and dispatch. subclasses should call the - // superclass's version first and return true if it returns true. - virtual bool onPreDispatch(const CEvent* event) = 0; - - // called by mainLoop(). iff the event was handled return true and - // store the result, if any, in m_result, which defaults to zero. - virtual bool onEvent(CEvent* event) = 0; - - // called if the display is unexpectedly closing. default does nothing. - virtual void onUnexpectedClose(); - - // called when a clipboard is lost - virtual void onLostClipboard(ClipboardID) = 0; + // IScreen overrides + void open(); + void mainLoop(); + void exitMainLoop(); + void close(); + bool setClipboard(ClipboardID, const IClipboard*); + void checkClipboards(); + void openScreenSaver(bool notify); + void closeScreenSaver(); + void screensaver(bool activate); + void syncDesktop(); + bool getClipboard(ClipboardID, IClipboard*) const; + void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; + void getCursorPos(SInt32&, SInt32&) const; + void getCursorCenter(SInt32&, SInt32&) const; private: + // update screen size cache + void updateScreenShape(); + + // process events before dispatching to receiver + bool onPreDispatch(CEvent* event); + // create the transparent cursor void createBlankCursor(); @@ -255,14 +203,23 @@ private: }; private: + friend class CDisplayLock; + typedef CPriorityQueue CTimerPriorityQueue; + // X is not thread safe + CMutex m_mutex; + Display* m_display; - int m_screen; Window m_root; + bool m_stop; + + IScreenReceiver* m_receiver; + IScreenEventHandler* m_eventHandler; + Window m_window; + SInt32 m_x, m_y; SInt32 m_w, m_h; - bool m_stop; // clipboards CXWindowsClipboard* m_clipboard[kClipboardEnd]; @@ -270,20 +227,31 @@ private: // the transparent cursor Cursor m_cursor; - // screen saver - CXWindowsScreenSaver* m_screenSaver; + // screen saver stuff + CXWindowsScreenSaver* m_screensaver; + bool m_screensaverNotify; + Atom m_atomScreensaver; // timers, the stopwatch used to time, and a mutex for the timers CTimerPriorityQueue m_timers; CStopwatch m_time; CMutex m_timersMutex; - // X is not thread safe - CMutex m_mutex; - // pointer to (singleton) screen. this is only needed by // ioErrorHandler(). static CXWindowsScreen* s_screen; }; +class CDisplayLock { +public: + CDisplayLock(const CXWindowsScreen*); + ~CDisplayLock(); + + operator Display*() const; + +private: + const CMutex* m_mutex; + Display* m_display; +}; + #endif diff --git a/server/CClientProxy.h b/server/CClientProxy.h index ef5d158f..8eb07a93 100644 --- a/server/CClientProxy.h +++ b/server/CClientProxy.h @@ -32,7 +32,7 @@ public: virtual void close() = 0; virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, - bool screenSaver) = 0; + bool forScreensaver) = 0; virtual bool leave() = 0; virtual void setClipboard(ClipboardID, const CString&) = 0; virtual void grabClipboard(ClipboardID) = 0; @@ -44,13 +44,13 @@ public: virtual void mouseUp(ButtonID) = 0; virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; virtual void mouseWheel(SInt32 delta) = 0; - virtual void screenSaver(bool activate) = 0; + virtual void screensaver(bool activate) = 0; virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const = 0; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; - virtual void getCenter(SInt32& x, SInt32& y) const = 0; - virtual void getMousePos(SInt32& x, SInt32& y) const = 0; - virtual SInt32 getJumpZoneSize() const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; private: IServer* m_server; diff --git a/server/CClientProxy1_0.cpp b/server/CClientProxy1_0.cpp index 6a214d0d..4a969b93 100644 --- a/server/CClientProxy1_0.cpp +++ b/server/CClientProxy1_0.cpp @@ -236,12 +236,19 @@ CClientProxy1_0::mouseWheel(SInt32 delta) } void -CClientProxy1_0::screenSaver(bool on) +CClientProxy1_0::screensaver(bool on) { log((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getName().c_str(), on ? 1 : 0)); CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } +SInt32 +CClientProxy1_0::getJumpZoneSize() const +{ + CLock lock(&m_mutex); + return m_info.m_zoneSize; +} + void CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { @@ -253,24 +260,17 @@ CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const } void -CClientProxy1_0::getCenter(SInt32& x, SInt32& y) const -{ - CLock lock(&m_mutex); - x = m_info.m_mx; - y = m_info.m_my; -} - -void -CClientProxy1_0::getMousePos(SInt32&, SInt32&) const +CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const { assert(0 && "shouldn't be called"); } -SInt32 -CClientProxy1_0::getJumpZoneSize() const +void +CClientProxy1_0::getCursorCenter(SInt32& x, SInt32& y) const { CLock lock(&m_mutex); - return m_info.m_zoneSize; + x = m_info.m_mx; + y = m_info.m_my; } void diff --git a/server/CClientProxy1_0.h b/server/CClientProxy1_0.h index 59d463b7..80b20cb1 100644 --- a/server/CClientProxy1_0.h +++ b/server/CClientProxy1_0.h @@ -18,7 +18,7 @@ public: virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, - bool screenSaver); + bool forScreensaver); virtual bool leave(); virtual void setClipboard(ClipboardID, const CString&); virtual void grabClipboard(ClipboardID); @@ -30,12 +30,12 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseWheel(SInt32 delta); - virtual void screenSaver(bool activate); + virtual void screensaver(bool activate); + virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; - virtual void getCenter(SInt32& x, SInt32& y) const; - virtual void getMousePos(SInt32& x, SInt32& y) const; - virtual SInt32 getJumpZoneSize() const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; private: void recvInfo(bool notify); diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index d76d1734..89668c22 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -1,6 +1,6 @@ #include "CPrimaryClient.h" #include "IServer.h" -#include "IPrimaryScreen.h" +#include "CPrimaryScreen.h" #include "CClipboard.h" #include "CLog.h" @@ -15,7 +15,8 @@ // CPrimaryClient // -CPrimaryClient::CPrimaryClient(IServer* server, const CString& name) : +CPrimaryClient::CPrimaryClient(IServer* server, + IPrimaryScreenReceiver* receiver, const CString& name) : m_server(server), m_name(name), m_seqNum(0) @@ -25,9 +26,9 @@ CPrimaryClient::CPrimaryClient(IServer* server, const CString& name) : // create screen log((CLOG_DEBUG1 "creating primary screen")); #if WINDOWS_LIKE - m_screen = new CMSWindowsPrimaryScreen(this, m_server); + m_screen = new CMSWindowsPrimaryScreen(this, receiver); #elif UNIX_LIKE - m_screen = new CXWindowsPrimaryScreen(this, m_server); + m_screen = new CXWindowsPrimaryScreen(this, receiver); #endif } @@ -211,7 +212,7 @@ CPrimaryClient::mouseWheel(SInt32) } void -CPrimaryClient::screenSaver(bool) +CPrimaryClient::screensaver(bool) { // ignore } @@ -222,6 +223,12 @@ CPrimaryClient::getName() const return m_name; } +SInt32 +CPrimaryClient::getJumpZoneSize() const +{ + return m_info.m_zoneSize; +} + void CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { @@ -232,20 +239,14 @@ CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const } void -CPrimaryClient::getCenter(SInt32& x, SInt32& y) const -{ - x = m_info.m_mx; - y = m_info.m_my; -} - -void -CPrimaryClient::getMousePos(SInt32&, SInt32&) const +CPrimaryClient::getCursorPos(SInt32&, SInt32&) const { assert(0 && "shouldn't be called"); } -SInt32 -CPrimaryClient::getJumpZoneSize() const +void +CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const { - return m_info.m_zoneSize; + x = m_info.m_mx; + y = m_info.m_my; } diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h index 358afac5..3605a8e1 100644 --- a/server/CPrimaryClient.h +++ b/server/CPrimaryClient.h @@ -6,12 +6,13 @@ #include "ProtocolTypes.h" class IClipboard; -class IPrimaryScreen; +class CPrimaryScreen; +class IPrimaryScreenReceiver; class IServer; class CPrimaryClient : public IScreenReceiver, public IClient { public: - CPrimaryClient(IServer*, const CString& name); + CPrimaryClient(IServer*, IPrimaryScreenReceiver*, const CString& name); ~CPrimaryClient(); // manipulators @@ -44,7 +45,7 @@ public: virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, - bool screenSaver); + bool forScreensaver); virtual bool leave(); virtual void setClipboard(ClipboardID, const CString&); virtual void grabClipboard(ClipboardID); @@ -56,17 +57,17 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseWheel(SInt32 delta); - virtual void screenSaver(bool activate); + virtual void screensaver(bool activate); virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; - virtual void getCenter(SInt32& x, SInt32& y) const; - virtual void getMousePos(SInt32& x, SInt32& y) const; - virtual SInt32 getJumpZoneSize() const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; private: IServer* m_server; - IPrimaryScreen* m_screen; + CPrimaryScreen* m_screen; CString m_name; UInt32 m_seqNum; CClientInfo m_info; diff --git a/server/CPrimaryScreen.cpp b/server/CPrimaryScreen.cpp new file mode 100644 index 00000000..5dafa752 --- /dev/null +++ b/server/CPrimaryScreen.cpp @@ -0,0 +1,270 @@ +#include "CPrimaryScreen.h" +#include "IScreen.h" +#include "IScreenReceiver.h" +#include "ProtocolTypes.h" +#include "CThread.h" +#include "CLog.h" + +// FIXME -- should be locking + +// +// CPrimaryScreen +// + +CPrimaryScreen::CPrimaryScreen(IScreenReceiver* receiver) : + m_receiver(receiver), + m_active(false) +{ + // do nothing +} + +CPrimaryScreen::~CPrimaryScreen() +{ + // do nothing +} + +void +CPrimaryScreen::run() +{ + // change our priority + CThread::getCurrentThread().setPriority(-3); + + // run event loop + try { + log((CLOG_DEBUG "entering event loop")); + onPreRun(); + getScreen()->mainLoop(); + onPostRun(); + log((CLOG_DEBUG "exiting event loop")); + } + catch (...) { + onPostRun(); + log((CLOG_DEBUG "exiting event loop")); + throw; + } +} + +void +CPrimaryScreen::stop() +{ + getScreen()->exitMainLoop(); +} + +void +CPrimaryScreen::open() +{ + CClientInfo info; + try { + // subclass hook + onPreOpen(); + + // open the screen + getScreen()->open(); + + // create and prepare our window + createWindow(); + + // collect screen info + getScreen()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getScreen()->getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getJumpZoneSize(); + + // update keyboard state + updateKeys(); + + // get notified of screen saver activation/deactivation + getScreen()->openScreenSaver(true); + + // subclass hook + onPostOpen(); + } + catch (...) { + close(); + throw; + } + + // enter the screen + enterNoWarp(); + + // send screen info + m_receiver->onInfoChanged(info); +} + +void +CPrimaryScreen::close() +{ + onPreClose(); + getScreen()->closeScreenSaver(); + destroyWindow(); + getScreen()->close(); + onPostClose(); +} + +void +CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver) +{ + log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreensaver ? " for screen saver" : "")); + assert(m_active == true); + + enterNoWarp(); + if (!forScreensaver) { + warpCursor(x, y); + } + else { + onEnterScreensaver(); + } +} + +void +CPrimaryScreen::enterNoWarp() +{ + // not active anymore + m_active = false; + + // subclass hook + onPreEnter(); + + // restore active window and hide our window + hideWindow(); + + // subclass hook + onPostEnter(); +} + +bool +CPrimaryScreen::leave() +{ + log((CLOG_INFO "leaving primary")); + assert(m_active == false); + + // subclass hook + onPreLeave(); + + // show our window + if (!showWindow()) { + onPostLeave(false); + return false; + } + + // get keyboard state as we leave + updateKeys(); + + // warp mouse to center + warpCursorToCenter(); + // FIXME -- this doesn't match the win32 version. that just does + // the warp while we flush the input queue until we find the warp + // and we discard that too. would prefer to at least match our + // own warping when we receive MotionNotify; that just does the + // warp. however, the win32 version sometimes stutters when + // leaving and perhaps this is why. hmm, win32 does ignore the + // events until after the warp (via the mark). + + // subclass hook + onPostLeave(true); + + // local client now active + m_active = true; + + // make sure our idea of clipboard ownership is correct + getScreen()->checkClipboards(); + + return true; +} + +void +CPrimaryScreen::setClipboard(ClipboardID id, + const IClipboard* clipboard) +{ + getScreen()->setClipboard(id, clipboard); +} + +void +CPrimaryScreen::grabClipboard(ClipboardID id) +{ + getScreen()->setClipboard(id, NULL); +} + +bool +CPrimaryScreen::isActive() const +{ + return m_active; +} + +void +CPrimaryScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const +{ + getScreen()->getClipboard(id, clipboard); +} + +SInt32 +CPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + +void +CPrimaryScreen::onPreRun() +{ + // do nothing +} + +void +CPrimaryScreen::onPostRun() +{ + // do nothing +} + +void +CPrimaryScreen::onPreOpen() +{ + // do nothing +} + +void +CPrimaryScreen::onPostOpen() +{ + // do nothing +} + +void +CPrimaryScreen::onPreClose() +{ + // do nothing +} + +void +CPrimaryScreen::onPostClose() +{ + // do nothing +} + +void +CPrimaryScreen::onPreEnter() +{ + // do nothing +} + +void +CPrimaryScreen::onPostEnter() +{ + // do nothing +} + +void +CPrimaryScreen::onEnterScreensaver() +{ + // do nothing +} + +void +CPrimaryScreen::onPreLeave() +{ + // do nothing +} + +void +CPrimaryScreen::onPostLeave(bool) +{ + // do nothing +} diff --git a/server/CPrimaryScreen.h b/server/CPrimaryScreen.h new file mode 100644 index 00000000..06b020dd --- /dev/null +++ b/server/CPrimaryScreen.h @@ -0,0 +1,157 @@ +#ifndef CPRIMARYSCREEN_H +#define CPRIMARYSCREEN_H + +#include "ClipboardTypes.h" +#include "KeyTypes.h" + +class IClipboard; +class IScreen; +class IScreenReceiver; + +// platform independent base class for primary screen implementations. +// each platform will derive a class from CPrimaryScreen to handle +// platform dependent operations. +class CPrimaryScreen { +public: + CPrimaryScreen(IScreenReceiver*); + virtual ~CPrimaryScreen(); + + // manipulators + + // enter the screen's message loop. this returns when it detects + // the application should terminate or when stop() is called. + // run() may only be called between open() and close(). + void run(); + + // cause run() to return + void stop(); + + // initializes the screen and starts reporting events + void open(); + + // close the screen + void close(); + + // called when the user navigates to the primary screen. + // forScreensaver == true means that we're entering the primary + // screen because the screensaver has activated. + void enter(SInt32 x, SInt32 y, bool forScreensaver); + + // called when the user navigates off the primary screen. returns + // true iff successful. + bool leave(); + + // called when the configuration has changed. activeSides is a + // bitmask of CConfig::EDirectionMask indicating which sides of + // the primary screen are linked to clients. + virtual void reconfigure(UInt32 activeSides) = 0; + + // warp the cursor to the given absolute coordinates + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + + // set the screen's clipboard contents. this is usually called + // soon after an enter(). + void setClipboard(ClipboardID, const IClipboard*); + + // synergy should own the clipboard + void grabClipboard(ClipboardID); + + // accessors + + // returns true iff the screen is active (i.e. the user has left + // the screen) + bool isActive() const; + + // return the contents of the given clipboard + void getClipboard(ClipboardID, IClipboard*) const; + + // returns the size of the zone on the edges of the screen that + // causes the cursor to jump to another screen. default returns 1. + virtual SInt32 getJumpZoneSize() const; + + // get the primary screen's current toggle modifier key state. + // the returned mask should have the corresponding bit set for + // each toggle key that is active. + virtual KeyModifierMask getToggleMask() const = 0; + + // return true if any key or button is being pressed or if there's + // any other reason that the user should not be allowed to switch + // screens. + virtual bool isLockedToScreen() const = 0; + + // get the platform dependent screen object + virtual IScreen* getScreen() const = 0; + +protected: + // template method hooks. these are called on entry/exit to the + // named method. onEnterScreensaver() is called by enter() iff + // forScreensaver is true. onPostLeave() is passed the result of + // showWindow(). override to do platform specific operations. + // defaults do nothing. + virtual void onPreRun(); + virtual void onPostRun(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreClose(); + virtual void onPostClose(); + virtual void onPreEnter(); + virtual void onPostEnter(); + virtual void onEnterScreensaver(); + virtual void onPreLeave(); + virtual void onPostLeave(bool success); + + // create/destroy the window. this window is generally used to + // receive events and, when the user navigates to another screen, + // to capture keyboard and mouse input. + virtual void createWindow() = 0; + virtual void destroyWindow() = 0; + + // called when the user navigates off the primary screen. hide the + // cursor and grab exclusive access to the input devices. returns + // true iff successful. every call to showWindow() has a matching + // call to hideWindow() which preceeds it. return true iff + // successful (in particular, iff the input devices were grabbed). + // + // after a successful showWindow(), user input events and + // screensaver activation/deactivation should be reported to an + // IPrimaryScreenReceiver until hideWindow() is called. report + // mouse motion to IPrimaryScreenReceiver::onMouseMoveSecondary(). + // user input should not be delivered to any application except + // synergy. + virtual bool showWindow() = 0; + + // called when the user navigates back to the primary screen. show + // the cursor and ungab the input devices. + // + // after hideWindow(), user input events should be delivered normally. + // mouse motion over (at least) the jump zones must be reported to + // an IPrimaryScreenReceiver::onMouseMovePrimary(). + virtual void hideWindow() = 0; + + // prepare the cursor to report relative motion. when the user has + // navigated to another screen, synergy requires the cursor motion + // deltas, not the absolute coordinates. typically this is done by + // warping the cursor to the center of the primary screen and then + // every time it moves compute the motion and warp back to the + // center (but without reporting that warp as motion). this is + // only called after a successful showWindow(). + virtual void warpCursorToCenter() = 0; + + // check the current keyboard state. normally a screen will save + // the keyboard state in this method and use this shadow state + // when handling user input and in methods like isLockedToScreen(). + virtual void updateKeys() = 0; + +private: + void enterNoWarp(); + +private: + // FIXME -- should have a mutex + + IScreenReceiver* m_receiver; + + // m_active is true if this screen has been left + bool m_active; +}; + +#endif diff --git a/server/CServer.cpp b/server/CServer.cpp index 7457c99b..f13af415 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -92,7 +92,6 @@ CServer::run() } // handle events - log((CLOG_DEBUG "starting event handling")); m_primaryClient->run(); // clean up @@ -108,9 +107,6 @@ CServer::run() stopThreads(); \ delete m_httpServer; \ m_httpServer = NULL; \ - if (m_primaryClient != NULL) { \ - closePrimaryScreen(); \ - } \ } while (false) FINALLY; } @@ -144,6 +140,15 @@ CServer::quit() m_primaryClient->stop(); } +void +CServer::close() +{ + if (m_primaryClient != NULL) { + closePrimaryScreen(); + } + log((CLOG_INFO "closed screen")); +} + bool CServer::setConfig(const CConfig& config) { @@ -211,20 +216,6 @@ CServer::getActivePrimarySides() const return sides; } -void -CServer::onError() -{ - // stop all running threads but don't wait too long since some - // threads may be unable to proceed until this thread returns. - stopThreads(3.0); - - // done with the HTTP server - delete m_httpServer; - m_httpServer = NULL; - - // note -- we do not attempt to close down the primary screen -} - void CServer::onInfoChanged(const CString& name, const CClientInfo& info) { @@ -347,10 +338,74 @@ CServer::onClipboardChangedNoLock(ClipboardID id, m_active->setClipboard(id, m_clipboards[id].m_clipboardData); } -bool -CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) +void +CServer::onError() { - return false; + // stop all running threads but don't wait too long since some + // threads may be unable to proceed until this thread returns. + stopThreads(3.0); + + // done with the HTTP server + delete m_httpServer; + m_httpServer = NULL; + + // note -- we do not attempt to close down the primary screen +} + +void +CServer::onScreensaver(bool activated) +{ + log((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); + CLock lock(&m_mutex); + + if (activated) { + // save current screen and position + m_activeSaver = m_active; + m_xSaver = m_x; + m_ySaver = m_y; + + // jump to primary screen + if (m_active != m_primaryClient) { + switchScreen(m_primaryClient, 0, 0, true); + } + } + else { + // jump back to previous screen and position. we must check + // that the position is still valid since the screen may have + // changed resolutions while the screen saver was running. + if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { + // check position + IClient* screen = m_activeSaver; + SInt32 x, y, w, h; + screen->getShape(x, y, w, h); + SInt32 zoneSize = screen->getJumpZoneSize(); + if (m_xSaver < x + zoneSize) { + m_xSaver = x + zoneSize; + } + else if (m_xSaver >= x + w - zoneSize) { + m_xSaver = x + w - zoneSize - 1; + } + if (m_ySaver < y + zoneSize) { + m_ySaver = y + zoneSize; + } + else if (m_ySaver >= y + h - zoneSize) { + m_ySaver = y + h - zoneSize - 1; + } + + // jump + switchScreen(screen, m_xSaver, m_ySaver, false); + } + + // reset state + m_activeSaver = NULL; + } + + // send message to all clients + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + client->screensaver(activated); + } } void @@ -615,60 +670,10 @@ CServer::onMouseWheel(SInt32 delta) m_active->mouseWheel(delta); } -void -CServer::onScreenSaver(bool activated) +bool +CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) { - log((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); - CLock lock(&m_mutex); - - if (activated) { - // save current screen and position - m_activeSaver = m_active; - m_xSaver = m_x; - m_ySaver = m_y; - - // jump to primary screen - if (m_active != m_primaryClient) { - switchScreen(m_primaryClient, 0, 0, true); - } - } - else { - // jump back to previous screen and position. we must check - // that the position is still valid since the screen may have - // changed resolutions while the screen saver was running. - if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { - // check position - IClient* screen = m_activeSaver; - SInt32 x, y, w, h; - screen->getShape(x, y, w, h); - SInt32 zoneSize = screen->getJumpZoneSize(); - if (m_xSaver < x + zoneSize) { - m_xSaver = x + zoneSize; - } - else if (m_xSaver >= x + w - zoneSize) { - m_xSaver = x + w - zoneSize - 1; - } - if (m_ySaver < y + zoneSize) { - m_ySaver = y + zoneSize; - } - else if (m_ySaver >= y + h - zoneSize) { - m_ySaver = y + h - zoneSize - 1; - } - - // jump - switchScreen(screen, m_xSaver, m_ySaver, false); - } - - // reset state - m_activeSaver = NULL; - } - - // send message to all clients - for (CClientList::const_iterator index = m_clients.begin(); - index != m_clients.end(); ++index) { - IClient* client = index->second; - client->screenSaver(activated); - } + return false; } bool @@ -689,7 +694,7 @@ CServer::isLockedToScreenNoLock() const } void -CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool screenSaver) +CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) { assert(dst != NULL); #ifndef NDEBUG @@ -741,7 +746,8 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool screenSaver) // enter new screen m_active->enter(x, y, m_seqNum, - m_primaryClient->getToggleMask(), screenSaver); + m_primaryClient->getToggleMask(), + forScreensaver); // send the clipboard data to new active screen for (ClipboardID id = 0; id < kClipboardEnd; ++id) { @@ -1209,7 +1215,7 @@ CServer::runClient(void* vsocket) { CLock lock(&m_mutex); if (m_activeSaver != NULL) { - proxy->screenSaver(true); + proxy->screensaver(true); } } @@ -1510,7 +1516,7 @@ CServer::openPrimaryScreen() // create the primary client and open it try { - m_primaryClient = new CPrimaryClient(this, primary); + m_primaryClient = new CPrimaryClient(this, this, primary); // add connection addConnection(m_primaryClient); @@ -1593,7 +1599,7 @@ CServer::removeConnection(const CString& name) IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; if (active == index->second && active != m_primaryClient) { // record new position (center of primary screen) - m_primaryClient->getCenter(m_x, m_y); + m_primaryClient->getCursorCenter(m_x, m_y); // don't notify active screen since it probably already disconnected log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); diff --git a/server/CServer.h b/server/CServer.h index d8998116..7ddb4956 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -2,6 +2,7 @@ #define CSERVER_H #include "IServer.h" +#include "IPrimaryScreenReceiver.h" #include "CConfig.h" #include "CClipboard.h" #include "CCondVar.h" @@ -20,7 +21,7 @@ class IServerProtocol; class ISocketFactory; class ISecurityFactory; -class CServer : public IServer { +class CServer : public IServer, public IPrimaryScreenReceiver { public: CServer(const CString& serverName); ~CServer(); @@ -38,6 +39,9 @@ public: // after a successful open(). void quit(); + // close the server's screen + void close(); + // update screen map. returns true iff the new configuration was // accepted. bool setConfig(const CConfig&); @@ -50,8 +54,14 @@ public: // get the primary screen's name CString getPrimaryScreenName() const; + // IServer overrides + virtual void onInfoChanged(const CString&, const CClientInfo&); + virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); + virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); + // IPrimaryScreenReceiver overrides virtual void onError(); + virtual void onScreensaver(bool activated); virtual void onKeyDown(KeyID, KeyModifierMask); virtual void onKeyUp(KeyID, KeyModifierMask); virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -60,12 +70,6 @@ public: virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); virtual void onMouseWheel(SInt32 delta); - virtual void onScreenSaver(bool activated); - - // IServer overrides - virtual void onInfoChanged(const CString&, const CClientInfo&); - virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); - virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); protected: bool onCommandKey(KeyID, KeyModifierMask, bool down); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index a4cdd4b1..dff594c8 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -1,11 +1,7 @@ #include "CXWindowsPrimaryScreen.h" -#include "IScreenReceiver.h" -#include "IPrimaryScreenReceiver.h" -#include "CXWindowsClipboard.h" -#include "CXWindowsScreenSaver.h" +#include "CXWindowsScreen.h" #include "CXWindowsUtil.h" -#include "CClipboard.h" -#include "ProtocolTypes.h" +#include "IPrimaryScreenReceiver.h" #include "XScreen.h" #include "CThread.h" #include "CLog.h" @@ -26,162 +22,17 @@ CXWindowsPrimaryScreen::CXWindowsPrimaryScreen( IScreenReceiver* receiver, IPrimaryScreenReceiver* primaryReceiver) : - m_receiver(receiver), - m_primaryReceiver(primaryReceiver), - m_active(false), + CPrimaryScreen(receiver), + m_receiver(primaryReceiver), m_window(None) { - // do nothing + m_screen = new CXWindowsScreen(receiver, this); } CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() { assert(m_window == None); -} - -void -CXWindowsPrimaryScreen::run() -{ - // change our priority - CThread::getCurrentThread().setPriority(-3); - - // run event loop - try { - log((CLOG_INFO "entering event loop")); - mainLoop(); - log((CLOG_INFO "exiting event loop")); - } - catch (...) { - log((CLOG_INFO "exiting event loop")); - throw; - } -} - -void -CXWindowsPrimaryScreen::stop() -{ - exitMainLoop(); -} - -void -CXWindowsPrimaryScreen::open() -{ - assert(m_window == None); - - CClientInfo info; - try { - // open the display - openDisplay(); - - // create and prepare our window - createWindow(); - - // get the display - CDisplayLock display(this); - - // initialize the clipboards - initClipboards(m_window); - - // miscellaneous initialization - m_atomScreenSaver = XInternAtom(display, "SCREENSAVER", False); - - // check for peculiarities - // FIXME -- may have to get these from some database - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; -// m_numLockHalfDuplex = true; -// m_capsLockHalfDuplex = true; - - // collect screen info - getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); - getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getJumpZoneSize(); - - // save mouse position - m_x = info.m_mx; - m_y = info.m_my; - - // compute center pixel of primary screen - getCursorCenter(m_xCenter, m_yCenter); - - // update key state - updateModifierMap(display); - - // get notified of screen saver activation/deactivation - installScreenSaver(); - } - catch (...) { - close(); - throw; - } - - // enter the screen - enterNoWarp(); - - // send screen info - m_receiver->onInfoChanged(info); -} - -void -CXWindowsPrimaryScreen::close() -{ - uninstallScreenSaver(); - destroyWindow(); - closeDisplay(); -} - -void -CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) -{ - log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreenSaver ? " for screen saver" : "")); - assert(m_active == true); - assert(m_window != None); - - // enter the screen - enterNoWarp(); - - // warp to requested location - if (!forScreenSaver) { - warpCursor(x, y); - } - - // redirect input to root window. do not warp the mouse because - // that will deactivate the screen saver. - else { - CDisplayLock display(this); - XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); - } -} - -bool -CXWindowsPrimaryScreen::leave() -{ - log((CLOG_INFO "leaving primary")); - assert(m_active == false); - assert(m_window != None); - - // show our window - if (!showWindow()) { - return false; - } - - // warp mouse to center - warpCursorToCenter(); - // FIXME -- this doesn't match the win32 version. that just does - // the warp while we flush the input queue until we find the warp - // and we discard that too. would prefer to at least match our - // own warping when we receive MotionNotify; that just does the - // warp. however, the win32 version sometimes stutters when - // leaving and perhaps this is why. hmm, win32 does ignore the - // events until after the warp (via the mark). - - // local client now active - m_active = true; - - // make sure our idea of clipboard ownership is correct - checkClipboard(); - - return true; + delete m_screen; } void @@ -193,7 +44,7 @@ CXWindowsPrimaryScreen::reconfigure(UInt32) void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { - CDisplayLock display(this); + CDisplayLock display(m_screen); // warp mouse warpCursorNoFlush(display, x, y); @@ -213,30 +64,10 @@ CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) m_y = y; } -void -CXWindowsPrimaryScreen::setClipboard(ClipboardID id, - const IClipboard* clipboard) -{ - setDisplayClipboard(id, clipboard); -} - -void -CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) -{ - setDisplayClipboard(id, NULL); -} - -void -CXWindowsPrimaryScreen::getClipboard(ClipboardID id, - IClipboard* clipboard) const -{ - getDisplayClipboard(id, clipboard); -} - KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const { - CDisplayLock display(this); + CDisplayLock display(m_screen); // query the pointer to get the keyboard state Window root, window; @@ -265,7 +96,7 @@ CXWindowsPrimaryScreen::getToggleMask() const bool CXWindowsPrimaryScreen::isLockedToScreen() const { - CDisplayLock display(this); + CDisplayLock display(m_screen); // query the pointer to get the button state Window root, window; @@ -295,11 +126,29 @@ CXWindowsPrimaryScreen::isLockedToScreen() const return false; } -bool -CXWindowsPrimaryScreen::onPreDispatch(const CEvent* event) +IScreen* +CXWindowsPrimaryScreen::getScreen() const { - // forward to superclass - return CXWindowsScreen::onPreDispatch(event); + return m_screen; +} + +void +CXWindowsPrimaryScreen::onError() +{ + // tell server to shutdown + m_receiver->onError(); +} + +void +CXWindowsPrimaryScreen::onScreensaver(bool activated) +{ + m_receiver->onScreensaver(activated); +} + +bool +CXWindowsPrimaryScreen::onPreDispatch(const CEvent*) +{ + return false; } bool @@ -313,41 +162,28 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) case CreateNotify: { // select events on new window - CDisplayLock display(this); + CDisplayLock display(m_screen); selectEvents(display, xevent.xcreatewindow.window); } return true; case MappingNotify: - { - // keyboard mapping changed - CDisplayLock display(this); - XRefreshKeyboardMapping(&xevent.xmapping); - updateModifierMap(display); - } + // keyboard mapping changed + updateKeys(); return true; - case ClientMessage: - if (xevent.xclient.message_type == m_atomScreenSaver || - xevent.xclient.format == 32) { - // screen saver activation/deactivation event - m_primaryReceiver->onScreenSaver(xevent.xclient.data.l[0] != 0); - return true; - } - break; - case KeyPress: { log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { - m_primaryReceiver->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_primaryReceiver->onKeyUp(key, mask | KeyModifierCapsLock); + m_receiver->onKeyUp(key, mask | KeyModifierCapsLock); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_primaryReceiver->onKeyUp(key, mask | KeyModifierNumLock); + m_receiver->onKeyUp(key, mask | KeyModifierNumLock); } } } @@ -369,20 +205,24 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) filter.m_keycode = xevent.xkey.keycode; // now check for event - XEvent xevent2; - CDisplayLock display(this); - if (XCheckIfEvent(display, &xevent2, - &CXWindowsPrimaryScreen::findKeyEvent, - (XPointer)&filter) != True) { + bool hasPress; + { + XEvent xevent2; + CDisplayLock display(m_screen); + hasPress = (XCheckIfEvent(display, &xevent2, + &CXWindowsPrimaryScreen::findKeyEvent, + (XPointer)&filter) == True); + } + if (!hasPress) { // no press event follows so it's a plain release log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_primaryReceiver->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_primaryReceiver->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); } - m_primaryReceiver->onKeyUp(key, mask); + m_receiver->onKeyUp(key, mask); } else { // found a press event following so it's a repeat. @@ -390,7 +230,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) // repeats but we'll just send a repeat of 1. // note that we discard the press event. log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - m_primaryReceiver->onKeyRepeat(key, mask, 1); + m_receiver->onKeyRepeat(key, mask, 1); } } } @@ -401,7 +241,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_primaryReceiver->onMouseDown(button); + m_receiver->onMouseDown(button); } } return true; @@ -411,15 +251,15 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_primaryReceiver->onMouseUp(button); + m_receiver->onMouseUp(button); } else if (xevent.xbutton.button == 4) { // wheel forward (away from user) - m_primaryReceiver->onMouseWheel(120); + m_receiver->onMouseWheel(120); } else if (xevent.xbutton.button == 5) { // wheel backward (toward user) - m_primaryReceiver->onMouseWheel(-120); + m_receiver->onMouseWheel(-120); } } return true; @@ -443,14 +283,14 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) // warpCursorNoFlush() for where the events are // sent. we discard the matching sent event and // can be sure we've skipped the warp event. - CDisplayLock display(this); + CDisplayLock display(m_screen); do { XMaskEvent(display, PointerMotionMask, &xevent); } while (!xevent.xmotion.send_event); } - else if (!m_active) { + else if (!isActive()) { // motion on primary screen - m_primaryReceiver->onMouseMovePrimary(m_x, m_y); + m_receiver->onMouseMovePrimary(m_x, m_y); } else { // motion on secondary screen. warp mouse back to @@ -470,7 +310,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) xevent.xmotion.x_root - m_xCenter > s_size || xevent.xmotion.y_root - m_yCenter < -s_size || xevent.xmotion.y_root - m_yCenter > s_size) { - CDisplayLock display(this); + CDisplayLock display(m_screen); warpCursorNoFlush(display, m_xCenter, m_yCenter); } @@ -481,7 +321,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) // warping to the primary screen's enter position, // effectively overriding it. if (x != 0 || y != 0) { - m_primaryReceiver->onMouseMoveSecondary(x, y); + m_receiver->onMouseMoveSecondary(x, y); } } } @@ -492,77 +332,117 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) } void -CXWindowsPrimaryScreen::onUnexpectedClose() +CXWindowsPrimaryScreen::onPreRun() { - // tell server to shutdown - m_primaryReceiver->onError(); -} - -void -CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id) -{ - // tell server that the clipboard was grabbed locally - m_receiver->onGrabClipboard(id); -} - -SInt32 -CXWindowsPrimaryScreen::getJumpZoneSize() const -{ - return 1; -} - -void -CXWindowsPrimaryScreen::warpCursorToCenter() -{ - warpCursor(m_xCenter, m_yCenter); -} - -void -CXWindowsPrimaryScreen::warpCursorNoFlush( - Display* display, SInt32 x, SInt32 y) -{ - assert(display != NULL); assert(m_window != None); - - // send an event that we can recognize before the mouse warp - XEvent eventBefore; - eventBefore.type = MotionNotify; - eventBefore.xmotion.display = display; - eventBefore.xmotion.window = m_window; - eventBefore.xmotion.root = getRoot(); - eventBefore.xmotion.subwindow = m_window; - eventBefore.xmotion.time = CurrentTime; - eventBefore.xmotion.x = x; - eventBefore.xmotion.y = y; - eventBefore.xmotion.x_root = x; - eventBefore.xmotion.y_root = y; - eventBefore.xmotion.state = 0; - eventBefore.xmotion.is_hint = False; - eventBefore.xmotion.same_screen = True; - XEvent eventAfter = eventBefore; - XSendEvent(display, m_window, False, 0, &eventBefore); - - // warp mouse - XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); - - // send an event that we can recognize after the mouse warp - XSendEvent(display, m_window, False, 0, &eventAfter); - XSync(display, False); - - log((CLOG_DEBUG2 "warped to %d,%d", x, y)); } void -CXWindowsPrimaryScreen::enterNoWarp() +CXWindowsPrimaryScreen::onPreOpen() { - m_active = false; - hideWindow(); + assert(m_window == None); +} + +void +CXWindowsPrimaryScreen::onPostOpen() +{ + // get cursor info + m_screen->getCursorPos(m_x, m_y); + m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // check for peculiarities + // FIXME -- may have to get these from some database + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +// m_numLockHalfDuplex = true; +// m_capsLockHalfDuplex = true; +} + +void +CXWindowsPrimaryScreen::onPreEnter() +{ + assert(m_window != None); +} + +void +CXWindowsPrimaryScreen::onPreLeave() +{ + assert(m_window != None); +} + +void +CXWindowsPrimaryScreen::onEnterScreenSaver() +{ + CDisplayLock display(m_screen); + + // set keyboard focus to root window. the screensaver should then + // pick up key events for when the user enters a password to unlock. + XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); +} + +void +CXWindowsPrimaryScreen::createWindow() +{ + assert(m_window == None); + + // get size of screen + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + + // grab window attributes. this window is used to capture user + // input when the user is focused on another client. don't let + // the window manager mess with it. + XSetWindowAttributes attr; + attr.event_mask = PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask | PropertyChangeMask; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = m_screen->getBlankCursor(); + + { + // create the grab window + CDisplayLock display(m_screen); + m_window = XCreateWindow(display, m_screen->getRoot(), + x, y, w, h, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + if (m_window == None) { + throw XScreenOpenFailure(); + } + log((CLOG_DEBUG "window is 0x%08x", m_window)); + + // start watching for events on other windows + selectEvents(display, m_screen->getRoot()); + } + + // tell our superclass about the window + m_screen->setWindow(m_window); +} + +void +CXWindowsPrimaryScreen::destroyWindow() +{ + // display can be NULL if the server unexpectedly disconnected + if (m_window != None) { + m_screen->setWindow(None); + CDisplayLock display(m_screen); + if (display != NULL) { + XDestroyWindow(display, m_window); + } + m_window = None; + } } bool CXWindowsPrimaryScreen::showWindow() { - CDisplayLock display(this); + assert(m_window != None); + + CDisplayLock display(m_screen); // raise and show the input window XMapRaised(display, m_window); @@ -616,83 +496,51 @@ CXWindowsPrimaryScreen::showWindow() void CXWindowsPrimaryScreen::hideWindow() { - CDisplayLock display(this); + CDisplayLock display(m_screen); // unmap the grab window. this also ungrabs the mouse and keyboard. XUnmapWindow(display, m_window); } void -CXWindowsPrimaryScreen::checkClipboard() +CXWindowsPrimaryScreen::warpCursorToCenter() { - // do nothing, we're always up to date + warpCursor(m_xCenter, m_yCenter); } void -CXWindowsPrimaryScreen::createWindow() +CXWindowsPrimaryScreen::warpCursorNoFlush( + Display* display, SInt32 x, SInt32 y) { - assert(m_window == None); + assert(display != NULL); + assert(m_window != None); - // get size of screen - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); + // send an event that we can recognize before the mouse warp + XEvent eventBefore; + eventBefore.type = MotionNotify; + eventBefore.xmotion.display = display; + eventBefore.xmotion.window = m_window; + eventBefore.xmotion.root = m_screen->getRoot(); + eventBefore.xmotion.subwindow = m_window; + eventBefore.xmotion.time = CurrentTime; + eventBefore.xmotion.x = x; + eventBefore.xmotion.y = y; + eventBefore.xmotion.x_root = x; + eventBefore.xmotion.y_root = y; + eventBefore.xmotion.state = 0; + eventBefore.xmotion.is_hint = False; + eventBefore.xmotion.same_screen = True; + XEvent eventAfter = eventBefore; + XSendEvent(display, m_window, False, 0, &eventBefore); - // grab window attributes. this window is used to capture user - // input when the user is focused on another client. don't let - // the window manager mess with it. - XSetWindowAttributes attr; - attr.event_mask = PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask | PropertyChangeMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = getBlankCursor(); + // warp mouse + XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); - // create the grab window - CDisplayLock display(this); - m_window = XCreateWindow(display, getRoot(), - x, y, w, h, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - if (m_window == None) { - throw XScreenOpenFailure(); - } - log((CLOG_DEBUG "window is 0x%08x", m_window)); + // send an event that we can recognize after the mouse warp + XSendEvent(display, m_window, False, 0, &eventAfter); + XSync(display, False); - // start watching for events on other windows - selectEvents(display, getRoot()); -} - -void -CXWindowsPrimaryScreen::destroyWindow() -{ - // display can be NULL if the server unexpectedly disconnected - CDisplayLock display(this); - if (display != NULL && m_window != None) { - XDestroyWindow(display, m_window); - } - m_window = None; -} - -void -CXWindowsPrimaryScreen::installScreenSaver() -{ - assert(getScreenSaver() != NULL); - - getScreenSaver()->setNotify(m_window); -} - -void -CXWindowsPrimaryScreen::uninstallScreenSaver() -{ - // stop being notified of screen saver activation/deactivation - if (getScreenSaver() != NULL) { - getScreenSaver()->setNotify(None); - } - m_atomScreenSaver = None; + log((CLOG_DEBUG2 "warped to %d,%d", x, y)); } void @@ -777,7 +625,7 @@ CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const KeySym keysym; char dummy[1]; - CDisplayLock display(this); + CDisplayLock display(m_screen); XLookupString(event, dummy, 0, &keysym, NULL); return static_cast(keysym); } @@ -795,8 +643,10 @@ CXWindowsPrimaryScreen::mapButton(unsigned int button) const } void -CXWindowsPrimaryScreen::updateModifierMap(Display* display) +CXWindowsPrimaryScreen::updateKeys() { + CDisplayLock display(m_screen); + // get modifier map from server XModifierKeymap* keymap = XGetModifierMapping(display); diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 8bf41373..6b261400 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -1,63 +1,59 @@ #ifndef CXWINDOWSPRIMARYSCREEN_H #define CXWINDOWSPRIMARYSCREEN_H -#include "CXWindowsScreen.h" -#include "IPrimaryScreen.h" +#include "CPrimaryScreen.h" +#include "IScreenEventHandler.h" #include "MouseTypes.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif +class CXWindowsScreen; class IScreenReceiver; class IPrimaryScreenReceiver; -class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { +class CXWindowsPrimaryScreen : + public CPrimaryScreen, public IScreenEventHandler { public: CXWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); virtual ~CXWindowsPrimaryScreen(); - // IPrimaryScreen overrides - virtual void run(); - virtual void stop(); - virtual void open(); - virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool); - virtual bool leave(); + // CPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); - virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void setClipboard(ClipboardID, const IClipboard*); - virtual void grabClipboard(ClipboardID); - virtual void getClipboard(ClipboardID, IClipboard*) const; + virtual void warpCursor(SInt32 x, SInt32 y); virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; + virtual IScreen* getScreen() const; -protected: - // CXWindowsScreen overrides + // IScreenEventHandler overrides + virtual void onError(); + virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); - virtual void onUnexpectedClose(); - virtual void onLostClipboard(ClipboardID); + +protected: + // CPrimaryScreen overrides + virtual void onPreRun(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreEnter(); + virtual void onPreLeave(); + virtual void onEnterScreenSaver(); + + virtual void createWindow(); + virtual void destroyWindow(); + virtual bool showWindow(); + virtual void hideWindow(); + virtual void warpCursorToCenter(); + + virtual void updateKeys(); private: - SInt32 getJumpZoneSize() const; - - void warpCursorToCenter(); void warpCursorNoFlush(Display*, SInt32 xAbsolute, SInt32 yAbsolute); - void enterNoWarp(); - bool showWindow(); - void hideWindow(); - - // check clipboard ownership and, if necessary, tell the receiver - // of a grab. - void checkClipboard(); - - // create/destroy window - void createWindow(); - void destroyWindow(); - - // start/stop watch for screen saver changes - void installScreenSaver(); - void uninstallScreenSaver(); - void selectEvents(Display*, Window) const; void doSelectEvents(Display*, Window) const; @@ -65,8 +61,6 @@ private: KeyID mapKey(XKeyEvent*) const; ButtonID mapButton(unsigned int button) const; - void updateModifierMap(Display* display); - class CKeyEventInfo { public: int m_event; @@ -77,15 +71,12 @@ private: static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: - IScreenReceiver* m_receiver; - IPrimaryScreenReceiver* m_primaryReceiver; + CXWindowsScreen* m_screen; + IPrimaryScreenReceiver* m_receiver; - bool m_active; + // our window Window m_window; - // atom for screen saver messages - Atom m_atomScreenSaver; - // note toggle keys that toggle on up/down (false) or on // transition (true) bool m_numLockHalfDuplex; diff --git a/server/IPrimaryScreen.h b/server/IPrimaryScreen.h deleted file mode 100644 index be63f11f..00000000 --- a/server/IPrimaryScreen.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef IPRIMARYSCREEN_H -#define IPRIMARYSCREEN_H - -#include "IInterface.h" -#include "KeyTypes.h" -#include "ClipboardTypes.h" - -class IClipboard; - -class IPrimaryScreen : public IInterface { -public: - // manipulators - - // enter the screen's message loop. this returns when it detects - // the application should terminate or when stop() is called. - // the screen must be open()'d before run() and must not be - // close()'d until run() returns. - virtual void run() = 0; - - // cause run() to return - virtual void stop() = 0; - - // initialize the screen and start reporting events to the receiver - // (which is set through some interface of the derived class). - // events should be reported no matter where on the screen they - // occur but do not interfere with normal event dispatch. the - // screen saver engaging should be reported as an event. if that - // can't be detected then this object should disable the system's - // screen saver timer and should start the screen saver after - // idling for an appropriate time. - // - // open() must call receiver->onInfoChanged() to notify of the - // primary screen's initial resolution and jump zone size. it - // must also call receiver->onClipboardChanged() for each - // clipboard that the primary screen has. - virtual void open() = 0; - - // close the screen. should restore the screen saver timer if it - // was disabled. - virtual void close() = 0; - - // called when the user navigates back to the primary screen. - // warp the cursor to the given coordinates, unhide it, and - // ungrab the input devices. every call to enter has a matching - // call to leave() which preceeds it, however the screen should - // assume an implicit call to enter() in the call to open(). - // if warpCursor is false then do not warp the mouse. - // - // enter() must not call any receiver methods except onError(). - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, - bool forScreenSaver) = 0; - - // called when the user navigates off the primary screen. hide the - // cursor and grab exclusive access to the input devices. return - // true iff successful. - // - // leave() must not call any receiver methods except onError(). - virtual bool leave() = 0; - - // called when the configuration has changed. activeSides is a - // bitmask of CConfig::EDirectionMask indicating which sides of - // the primary screen are linked to clients. - virtual void reconfigure(UInt32 activeSides) = 0; - - // warp the cursor to the given position - virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; - - // set the screen's clipboard contents. this is usually called - // soon after an enter(). - // - // setClipboard() must not call any receiver methods except onError(). - virtual void setClipboard(ClipboardID, const IClipboard*) = 0; - - // synergy should own the clipboard - virtual void grabClipboard(ClipboardID) = 0; - - // accessors - - // return the contents of the given clipboard. - // - // getClipboard() must not call any receiver methods except onError(). - virtual void getClipboard(ClipboardID, IClipboard*) const = 0; - - // get the primary screen's current toggle modifier key state. - // the returned mask should have the corresponding bit set for - // each toggle key that is active. - virtual KeyModifierMask getToggleMask() const = 0; - - // return true if any key or button is being pressed or if there's - // any other reason that the user should not be allowed to switch - // screens. - virtual bool isLockedToScreen() const = 0; -}; - -#endif diff --git a/server/Makefile.am b/server/Makefile.am index 397ff64e..bcab55a3 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -9,6 +9,7 @@ synergyd_SOURCES = \ CConfig.cpp \ CHTTPServer.cpp \ CPrimaryClient.cpp \ + CPrimaryScreen.cpp \ CServer.cpp \ CXWindowsPrimaryScreen.cpp \ server.cpp \ @@ -17,9 +18,9 @@ synergyd_SOURCES = \ CConfig.h \ CHTTPServer.h \ CPrimaryClient.h \ + CPrimaryScreen.h \ CServer.h \ CXWindowsPrimaryScreen.h \ - IPrimaryScreen.h \ $(NULL) synergyd_LDADD = \ $(DEPTH)/platform/libplatform.a \ diff --git a/server/server.cpp b/server/server.cpp index 5e3ec8b3..9fcaee93 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -132,6 +132,7 @@ realMain(CMutex* mutex) } // clean up + s_server->close(); delete s_server; s_server = NULL; CLog::setLock(NULL); @@ -142,8 +143,11 @@ realMain(CMutex* mutex) if (!locked && mutex != NULL) { mutex->lock(); } - delete s_server; - s_server = NULL; + if (s_server != NULL) { + s_server->close(); + delete s_server; + s_server = NULL; + } CLog::setLock(NULL); s_logMutex = NULL; throw; diff --git a/synergy/IClient.h b/synergy/IClient.h index 2c363368..bf570772 100644 --- a/synergy/IClient.h +++ b/synergy/IClient.h @@ -7,6 +7,8 @@ #include "MouseTypes.h" #include "CString.h" +// the client interface. this provides all the methods necessary for +// the server to communicate with a client. class IClient : public IInterface { public: // manipulators @@ -23,11 +25,11 @@ public: // enter the screen. the cursor should be warped to xAbs,yAbs. // the client should record seqNum for future reporting of // clipboard changes. mask is the expected toggle button state. - // screenSaver is true if the screen is being entered because + // forScreensaver is true if the screen is being entered because // the screen saver is starting. virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, - bool screenSaver) = 0; + bool forScreensaver) = 0; // leave the screen. returns false if the user may not leave the // client's screen. @@ -54,25 +56,25 @@ public: virtual void mouseUp(ButtonID) = 0; virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; virtual void mouseWheel(SInt32 delta) = 0; - virtual void screenSaver(bool activate) = 0; + virtual void screensaver(bool activate) = 0; // accessors // get the client's identifier virtual CString getName() const = 0; + // get the size of jump zone + virtual SInt32 getJumpZoneSize() const = 0; + // get the screen's shape virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; - // get the center pixel - virtual void getCenter(SInt32& x, SInt32& y) const = 0; - // get the mouse position - virtual void getMousePos(SInt32& x, SInt32& y) const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; - // get the size of jump zone - virtual SInt32 getJumpZoneSize() const = 0; + // get the center pixel + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; }; #endif diff --git a/synergy/IPrimaryScreenReceiver.h b/synergy/IPrimaryScreenReceiver.h index 60758141..ff816928 100644 --- a/synergy/IPrimaryScreenReceiver.h +++ b/synergy/IPrimaryScreenReceiver.h @@ -5,12 +5,22 @@ #include "KeyTypes.h" #include "MouseTypes.h" +// the interface for receiving notification of events on the primary +// screen. the server implements this interface to handle user input. +// platform dependent primary screen implementation will need to take +// an IPrimaryScreenReceiver* and notify it of events. class IPrimaryScreenReceiver : public IInterface { public: - // notify of serious error. this implies that the server should - // shutdown. + // called if the display is unexpectedly closing. + + // called if the screen is unexpectedly closing. this implies that + // the screen is no longer usable and that the program should + // close the screen and possibly terminate. virtual void onError() = 0; + // called when the screensaver is activated or deactivated + virtual void onScreensaver(bool activated) = 0; + // call to notify of events. onMouseMovePrimary() returns // true iff the mouse enters a jump zone and jumps. virtual void onKeyDown(KeyID, KeyModifierMask) = 0; @@ -21,8 +31,6 @@ public: virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; virtual void onMouseWheel(SInt32 delta) = 0; - virtual void onScreenSaver(bool activated) = 0; - }; #endif diff --git a/synergy/IScreen.h b/synergy/IScreen.h new file mode 100644 index 00000000..88697926 --- /dev/null +++ b/synergy/IScreen.h @@ -0,0 +1,66 @@ +#ifndef ISCREEN_H +#define ISCREEN_H + +#include "IInterface.h" +#include "ClipboardTypes.h" + +class IClipboard; + +// the interface for platform dependent screen implementations. each +// platform will derive a type from IScreen for interaction with the +// platform's screen that's common to primary and secondary screens. +class IScreen : public IInterface { +public: + // manipulators + + // open the screen + virtual void open() = 0; + + // runs an event loop and returns when exitMainLoop() is called. + // must be called between open() and close(). + virtual void mainLoop() = 0; + + // force mainLoop() to return + virtual void exitMainLoop() = 0; + + // close the screen + virtual void close() = 0; + + // set the contents of the clipboard + virtual bool setClipboard(ClipboardID, const IClipboard*) = 0; + + // check clipboard ownership and notify IScreenReceiver (set through + // some other interface) if any changed + virtual void checkClipboards() = 0; + + // open/close the screen saver. if notify is true then this object + // will call IScreenEventHandler's onScreenSaver() when the screensaver + // activates or deactivates until close. if notify is false then + // the screen saver is disabled on open and restored on close. + virtual void openScreenSaver(bool notify) = 0; + virtual void closeScreenSaver() = 0; + + // activate or deactivate the screen saver + virtual void screensaver(bool activate) = 0; + + // FIXME -- need explanation + virtual void syncDesktop() = 0; + + // accessors + + // get the contents of the clipboard + virtual bool getClipboard(ClipboardID, IClipboard*) const = 0; + + // get the shape of the screen + virtual void getShape(SInt32& x, SInt32& y, + SInt32& w, SInt32& h) const = 0; + + // get the current cursor coordinates + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + // get the cursor center position + // FIXME -- need better explanation + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; +}; + +#endif diff --git a/synergy/IScreenEventHandler.h b/synergy/IScreenEventHandler.h new file mode 100644 index 00000000..d4e04ab3 --- /dev/null +++ b/synergy/IScreenEventHandler.h @@ -0,0 +1,39 @@ +#ifndef ISCREENEVENTHANDLER_H +#define ISCREENEVENTHANDLER_H + +#include "IInterface.h" + +// the platform screen should define this +class CEvent; + +class IScreen; + +// the interface through which IScreen sends notification of events. +// each platform will derive two types from IScreenEventHandler, one +// for handling events on the primary screen and one for the +// secondary screen. the header file with the IScreen subclass for +// each platform should define the CEvent type, which depends on the +// type of native events for that platform. +class IScreenEventHandler : public IInterface { +public: + // manipulators + + // called if the screen is unexpectedly closing. this implies that + // the screen is no longer usable and that the program should + // close the screen and possibly terminate. + virtual void onError() = 0; + + // called when the screensaver is activated or deactivated + virtual void onScreensaver(bool activated) = 0; + + // called for each event before event translation and dispatch. return + // true to skip translation and dispatch. subclasses should call the + // superclass's version first and return true if it returns true. + virtual bool onPreDispatch(const CEvent* event) = 0; + + // called by mainLoop(). iff the event was handled return true and + // store the result, if any, in m_result, which defaults to zero. + virtual bool onEvent(CEvent* event) = 0; +}; + +#endif diff --git a/synergy/IScreenReceiver.h b/synergy/IScreenReceiver.h index 097e4405..02fcf936 100644 --- a/synergy/IScreenReceiver.h +++ b/synergy/IScreenReceiver.h @@ -6,6 +6,8 @@ #include "ProtocolTypes.h" #include "CString.h" +// the interface for types that receive screen resize and clipboard +// notifications (indirectly) from the system. class IScreenReceiver : public IInterface { public: // notify of client info change diff --git a/synergy/IServer.h b/synergy/IServer.h index 67033b59..f6a4e458 100644 --- a/synergy/IServer.h +++ b/synergy/IServer.h @@ -1,13 +1,19 @@ #ifndef ISERVER_H #define ISERVER_H -#include "IPrimaryScreenReceiver.h" +#include "IInterface.h" #include "ClipboardTypes.h" #include "CString.h" class CClientInfo; -class IServer : public IPrimaryScreenReceiver { +// the server interface. this provides all the methods necessary for +// clients to communicate with the server. note that the methods +// in this interface are similar to the methods in IScreenReceiver but +// include extra parameters. this interface is suitable for server-side +// client proxies. client-side objects should use the IScreenReceiver +// interface since the extra parameters are meaningless on the client-side. +class IServer : public IInterface { public: // manipulators @@ -23,18 +29,6 @@ public: // notify of new clipboard data virtual void onClipboardChanged(ClipboardID, UInt32 seqNum, const CString& data) = 0; - - // IPrimaryScreenReceiver overrides - virtual void onError() = 0; - virtual void onKeyDown(KeyID, KeyModifierMask) = 0; - virtual void onKeyUp(KeyID, KeyModifierMask) = 0; - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void onMouseDown(ButtonID) = 0; - virtual void onMouseUp(ButtonID) = 0; - virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; - virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; - virtual void onMouseWheel(SInt32 delta) = 0; - virtual void onScreenSaver(bool activated) = 0; }; #endif diff --git a/synergy/Makefile.am b/synergy/Makefile.am index aedd0e68..9522e1cc 100644 --- a/synergy/Makefile.am +++ b/synergy/Makefile.am @@ -20,6 +20,8 @@ libsynergy_a_SOURCES = \ IClient.h \ IClipboard.h \ IPrimaryScreenReceiver.h\ + IScreen.h \ + IScreenEventHandler.h \ IScreenReceiver.h \ IScreenSaver.h \ IServer.h \ From 4b468620261e116c26a9202dd1b85299f277461a Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 15 Jul 2002 15:01:36 +0000 Subject: [PATCH 229/807] checkpoint. refactored win32 code. had to edit and rename some files so this is only a checkpoint. --- client/CMSWindowsSecondaryScreen.cpp | 783 ++++------------------ client/CMSWindowsSecondaryScreen.h | 101 +-- client/CSecondaryScreen.cpp | 10 +- client/CSecondaryScreen.h | 4 +- client/CXWindowsSecondaryScreen.cpp | 36 +- client/CXWindowsSecondaryScreen.h | 1 + client/client.dsp | 8 +- platform/CMSWindowsScreen.cpp | 602 ++++++++++++++--- platform/CMSWindowsScreen.h | 134 ++-- {server => platform}/CSynergyHook.cpp | 0 {server => platform}/CSynergyHook.h | 0 platform/CXWindowsScreen.cpp | 6 +- platform/CXWindowsScreen.h | 5 +- platform/IMSWindowsScreenEventHandler.h | 26 + {server => platform}/makehook.dsp | 0 platform/platform.dsp | 4 + {server => platform}/synrgyhk.dsp | 0 server/CConfig.cpp | 8 +- server/CConfig.h | 6 +- server/CHTTPServer.cpp | 32 +- server/CMSWindowsPrimaryScreen.cpp | 847 ++++++------------------ server/CMSWindowsPrimaryScreen.h | 106 ++- server/CPrimaryScreen.cpp | 10 +- server/CPrimaryScreen.h | 8 +- server/CServer.cpp | 78 ++- server/CServer.h | 5 +- server/CXWindowsPrimaryScreen.cpp | 8 +- server/CXWindowsPrimaryScreen.h | 1 + server/server.dsp | 8 +- synergy/IScreen.h | 4 +- synergy/IScreenEventHandler.h | 5 + synergy/ProtocolTypes.h | 17 + synergy/synergy.dsp | 8 + 33 files changed, 1210 insertions(+), 1661 deletions(-) rename {server => platform}/CSynergyHook.cpp (100%) rename {server => platform}/CSynergyHook.h (100%) create mode 100755 platform/IMSWindowsScreenEventHandler.h rename {server => platform}/makehook.dsp (100%) rename {server => platform}/synrgyhk.dsp (100%) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index e49ec3d4..78b514f4 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -1,12 +1,8 @@ #include "CMSWindowsSecondaryScreen.h" -#include "IScreenReceiver.h" -#include "CClipboard.h" -#include "CMSWindowsClipboard.h" -#include "CMSWindowsScreenSaver.h" +#include "CMSWindowsScreen.h" #include "CPlatform.h" #include "XScreen.h" #include "CLock.h" -#include "CThread.h" #include "CLog.h" #include @@ -24,155 +20,18 @@ CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( IScreenReceiver* receiver) : - m_receiver(receiver), - m_threadID(0), - m_lastThreadID(0), - m_desk(NULL), - m_deskName(), + m_is95Family(CPlatform::isWindows95Family()), m_window(NULL), - m_active(false), - m_nextClipboardWindow(NULL) + m_mask(0) { - assert(m_receiver != NULL); - - m_is95Family = CPlatform::isWindows95Family(); - - // make sure this thread has a message queue - MSG dummy; - PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); + m_screen = new CMSWindowsScreen(receiver, this); } CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() { assert(m_window == NULL); -} -void -CMSWindowsSecondaryScreen::run() -{ - assert(m_window != NULL); - - // must call run() from same thread as open() - assert(m_threadID == GetCurrentThreadId()); - - // change our priority - CThread::getCurrentThread().setPriority(-7); - - // run event loop - try { - log((CLOG_INFO "entering event loop")); - mainLoop(); - log((CLOG_INFO "exiting event loop")); - } - catch (...) { - log((CLOG_INFO "exiting event loop")); - throw; - } -} - -void -CMSWindowsSecondaryScreen::stop() -{ - exitMainLoop(); -} - -void -CMSWindowsSecondaryScreen::open() -{ - assert(m_window == NULL); - - try { - // open the display - openDisplay(); - - // create and prepare our window - createWindow(); - - // initialize the clipboards; assume primary has all clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - grabClipboard(id); - } - - // get keyboard state - updateKeys(); - updateModifiers(); - - // disable the screen saver - installScreenSaver(); - } - catch (...) { - close(); - throw; - } - - // hide the cursor - m_active = true; - leave(); -} - -void -CMSWindowsSecondaryScreen::close() -{ - uninstallScreenSaver(); - destroyWindow(); - closeDisplay(); -} - -void -CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) -{ - CLock lock(&m_mutex); - assert(m_window != NULL); - assert(m_active == false); - - log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); - - syncDesktop(); - - // now active - m_active = true; - - // update our keyboard state to reflect the local state - updateKeys(); - updateModifiers(); - - // toggle modifiers that don't match the desired state - if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { - toggleKey(VK_CAPITAL, KeyModifierCapsLock); - } - if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { - toggleKey(VK_NUMLOCK | 0x100, KeyModifierNumLock); - } - if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { - toggleKey(VK_SCROLL, KeyModifierScrollLock); - } - - // warp to requested location - warpCursor(x, y); - - // show mouse - hideWindow(); -} - -void -CMSWindowsSecondaryScreen::leave() -{ - CLock lock(&m_mutex); - assert(m_window != NULL); - assert(m_active == true); - - log((CLOG_INFO "leaving screen")); - - syncDesktop(); - - // hide mouse - showWindow(); - - // not active anymore - m_active = false; - - // make sure our idea of clipboard ownership is correct - checkClipboard(); + delete m_screen; } void @@ -182,8 +41,7 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) UINT virtualKey; CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); // get the sequence of keys to simulate key press and the final // modifier state. @@ -223,8 +81,7 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key, UINT virtualKey; CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); // get the sequence of keys to simulate key repeat and the final // modifier state. @@ -244,8 +101,7 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) UINT virtualKey; CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); // get the sequence of keys to simulate key release and the final // modifier state. @@ -302,8 +158,7 @@ void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) { CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); // map button id to button flag DWORD flags = mapButton(button, true); @@ -318,8 +173,7 @@ void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) { CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); // map button id to button flag DWORD flags = mapButton(button, false); @@ -334,8 +188,7 @@ void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) { CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); warpCursor(x, y); } @@ -343,61 +196,50 @@ void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) { CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); - + m_screen->syncDesktop(); mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); } -void -CMSWindowsSecondaryScreen::setClipboard(ClipboardID /*id*/, - const IClipboard* src) +IScreen* +CMSWindowsSecondaryScreen::getScreen() const { - CLock lock(&m_mutex); - assert(m_window != NULL); - - CMSWindowsClipboard dst(m_window); - CClipboard::copy(&dst, src); + return m_screen; } void -CMSWindowsSecondaryScreen::grabClipboard(ClipboardID /*id*/) +CMSWindowsSecondaryScreen::onError() { - CLock lock(&m_mutex); - assert(m_window != NULL); + // ignore +} - CMSWindowsClipboard clipboard(m_window); - if (clipboard.open(0)) { - clipboard.close(); +void +CMSWindowsSecondaryScreen::onScreensaver(bool) +{ + // ignore +} + +bool +CMSWindowsSecondaryScreen::onPreDispatch(const CEvent*) +{ + return false; +} + +bool +CMSWindowsSecondaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + + const MSG& msg = event->m_msg; + switch (msg.message) { + case WM_ACTIVATEAPP: + if (msg.wParam == FALSE) { + // some other app activated. hide the hider window. + ShowWindow(m_window, SW_HIDE); + } + break; } -} -void -CMSWindowsSecondaryScreen::screenSaver(bool activate) -{ - if (activate) { - getScreenSaver()->activate(); - } - else { - getScreenSaver()->deactivate(); - } -} - -void -CMSWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const -{ - CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); - - getCursorPos(x, y); -} - -void -CMSWindowsSecondaryScreen::getShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - getScreenShape(x, y, w, h); + return false; } SInt32 @@ -407,153 +249,63 @@ CMSWindowsSecondaryScreen::getJumpZoneSize() const } void -CMSWindowsSecondaryScreen::getClipboard(ClipboardID /*id*/, - IClipboard* dst) const +CMSWindowsSecondaryScreen::postCreateWindow(HWND window) +{ + m_window = window; + if (!isActive()) { + showWindow(); + } +} + +void +CMSWindowsSecondaryScreen::preDestroyWindow(HWND) +{ + // do nothing +} + +void +CMSWindowsSecondaryScreen::onPreRun() { - CLock lock(&m_mutex); assert(m_window != NULL); - - CMSWindowsClipboard src(m_window); - CClipboard::copy(dst, &src); } -bool -CMSWindowsSecondaryScreen::onPreDispatch(const CEvent* event) +void +CMSWindowsSecondaryScreen::onPreOpen() { - assert(event != NULL); - - // forward to superclass - if (CMSWindowsScreen::onPreDispatch(event)) { - return true; - } - - // handle event - const MSG* msg = &event->m_msg; - switch (msg->message) { - case WM_TIMER: - // if current desktop is not the input desktop then switch to it - if (!m_is95Family) { - HDESK desk = openInputDesktop(); - if (desk != NULL) { - if (isCurrentDesktop(desk)) { - CloseDesktop(desk); - } - else { - switchDesktop(desk); - } - } - } - return true; - } - - return false; + assert(m_window == NULL); } -bool -CMSWindowsSecondaryScreen::onEvent(CEvent* event) +void +CMSWindowsSecondaryScreen::onPreEnter() { - assert(event != NULL); - - const MSG& msg = event->msg; - switch (msg.message) { - case WM_QUERYENDSESSION: - if (m_is95Family) { - event->m_result = TRUE; - return true; - } - break; - - case WM_ENDSESSION: - if (m_is95Family) { - if (msg.wParam == TRUE && msg.lParam == 0) { - stop(); - } - return true; - } - break; - - case WM_PAINT: - ValidateRect(msg.hwnd, NULL); - return true; - - case WM_ACTIVATEAPP: - if (msg.wParam == FALSE) { - // some other app activated. hide the hider window. - ShowWindow(m_window, SW_HIDE); - } - break; - - case WM_DRAWCLIPBOARD: - log((CLOG_DEBUG "clipboard was taken")); - - // first pass it on - if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, - msg.message, msg.wParam, msg.lParam); - } - - // now notify client that somebody changed the clipboard (unless - // we're now the owner, in which case it's because we took - // ownership, or now it's owned by nobody, which will happen if - // we owned it and switched desktops because we destroy our - // window to do that). - try { - m_clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore. this can happen if we receive this event - // before we've fully started up. - } - return true; - - case WM_CHANGECBCHAIN: - if (m_nextClipboardWindow == (HWND)msg.wParam) { - m_nextClipboardWindow = (HWND)msg.lParam; - } - else if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, - msg.message, msg.wParam, msg.lParam); - } - return true; - - case WM_DISPLAYCHANGE: - { - // screen resolution may have changed. get old shape. - SInt32 xOld, yOld, wOld, hOld; - getScreenShape(xOld, yOld, wOld, hOld); - - // update shape - updateScreenShape(); - m_multimon = isMultimon(); - - // collect new screen info - CClientInfo info; - getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); - getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getJumpZoneSize(); - - // do nothing if resolution hasn't changed - if (info.m_x != xOld || info.m_y != yOld || - info.m_w != wOld || info.m_h != hOld) { - // send new screen info - m_receiver->onInfoChanged(info); - } - - return true; - } - } - - return false; + assert(m_window != NULL); } -CString -CMSWindowsSecondaryScreen::getCurrentDesktopName() const +void +CMSWindowsSecondaryScreen::onPreLeave() { - return m_deskName; + assert(m_window != NULL); +} + +void +CMSWindowsSecondaryScreen::createWindow() +{ + // open the desktop and the window + m_window = m_screen->openDesktop(); + if (m_window == NULL) { + throw XScreenOpenFailure(); + } +} + +void +CMSWindowsSecondaryScreen::destroyWindow() +{ + // release keys that are logically pressed + releaseKeys(); + + // close the desktop and the window + m_screen->closeDesktop(); + m_window = NULL; } void @@ -580,9 +332,9 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) { // move the mouse directly to target position on NT family or if // not using multiple monitors. - if (!m_multimon || !m_is95Family) { + if (m_screen->isMultimon() || !m_is95Family) { SInt32 x0, y0, w, h; - getScreenShape(x0, y0, w, h); + m_screen->getShape(x0, y0, w, h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, (DWORD)((65535.99 * (x - x0)) / (w - 1)), (DWORD)((65535.99 * (y - y0)) / (h - 1)), @@ -653,259 +405,70 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) } void -CMSWindowsSecondaryScreen::checkClipboard() +CMSWindowsSecondaryScreen::updateKeys() { - // if we think we own the clipboard but we don't then somebody - // grabbed the clipboard on this screen without us knowing. - // tell the server that this screen grabbed the clipboard. - // - // this works around bugs in the clipboard viewer chain. - // sometimes NT will simply never send WM_DRAWCLIPBOARD - // messages for no apparent reason and rebooting fixes the - // problem. since we don't want a broken clipboard until the - // next reboot we do this double check. clipboard ownership - // won't be reflected on other screens until we leave but at - // least the clipboard itself will work. - HWND clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != clipboardOwner) { - try { - m_clipboardOwner = clipboardOwner; - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore - } + // clear key state + memset(m_keys, 0, sizeof(m_keys)); + + // we only care about the modifier key states + m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); + m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); + m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); + m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL)); + m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL)); + m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL)); + m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU)); + m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU)); + m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU)); + m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN)); + m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN)); + m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS)); + m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); + m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); + m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); + + // update active modifier mask + m_mask = 0; + if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) { + m_mask |= KeyModifierShift; } + if ((m_keys[VK_LCONTROL] & 0x80) != 0 || + (m_keys[VK_RCONTROL] & 0x80) != 0) { + m_mask |= KeyModifierControl; + } + if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) { + m_mask |= KeyModifierAlt; + } + if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) { + m_mask |= KeyModifierMeta; + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { + m_mask |= KeyModifierCapsLock; + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { + m_mask |= KeyModifierNumLock; + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { + m_mask |= KeyModifierScrollLock; + } + log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); } void -CMSWindowsPrimaryScreen::createWindow() +CMSWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) { - // save thread id - m_threadID = GetCurrentThreadId(); - - // note if using multiple monitors - m_multimon = isMultimon(); - - // get the input desktop and switch to it - if (m_is95Family) { - if (!openDesktop()) { - throw XScreenOpenFailure(); - } + // toggle modifiers that don't match the desired state + if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { + toggleKey(VK_CAPITAL, KeyModifierCapsLock); } - else { - if (!switchDesktop(openInputDesktop())) { - throw XScreenOpenFailure(); - } + if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { + toggleKey(VK_NUMLOCK | 0x100, KeyModifierNumLock); } - - // poll input desktop to see if it changes (onPreDispatch() - // handles WM_TIMER) - m_timer = 0; - if (!m_is95Family) { - m_timer = SetTimer(NULL, 0, 200, NULL); + if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { + toggleKey(VK_SCROLL, KeyModifierScrollLock); } } -void -CMSWindowsPrimaryScreen::destroyWindow() -{ - // remove timer - if (m_timer != 0) { - KillTimer(NULL, m_timer); - } - - // release keys that are logically pressed - releaseKeys(); - - // disconnect from desktop - if (m_is95Family) { - closeDesktop(); - } - else { - switchDesktop(NULL); - } - - // clear thread id - m_threadID = 0; - - assert(m_window == NULL); - assert(m_desk == NULL); -} - -void -CMSWindowsSecondaryScreen::installScreenSaver() -{ - getScreenSaver()->disable(); -} - -void -CMSWindowsSecondaryScreen::uninstallScreenSaver() -{ - getScreenSaver()->enable(); -} - -bool -CMSWindowsSecondaryScreen::openDesktop() -{ - CLock lock(&m_mutex); - - // initialize clipboard owner to current owner. we don't want - // to take ownership of the clipboard just by starting up. - m_clipboardOwner = GetClipboardOwner(); - - // create the cursor hiding window. this window is used to hide the - // cursor when it's not on the screen. the window is hidden as soon - // as the cursor enters the screen or the display's real cursor is - // moved. - m_window = CreateWindowEx(WS_EX_TOPMOST | - WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - (LPCTSTR)getClass(), "Synergy", - WS_POPUP, - 0, 0, 1, 1, NULL, NULL, - getInstance(), - NULL); - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - return true; -} - -void -CMSWindowsSecondaryScreen::closeDesktop() -{ - CLock lock(&m_mutex); - - if (m_window != NULL) { - // remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // destroy window - DestroyWindow(m_window); - m_window = NULL; - } -} - -bool -CMSWindowsSecondaryScreen::switchDesktop(HDESK desk) -{ - CLock lock(&m_mutex); - - bool ownClipboard = false; - if (m_window != NULL) { - // note if we own the clipboard - ownClipboard = (m_clipboardOwner == m_window); - - // remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // destroy window - DestroyWindow(m_window); - m_window = NULL; - } - - // done with desktop - if (m_desk != NULL) { - CloseDesktop(m_desk); - m_desk = NULL; - m_deskName = ""; - } - - // if no new desktop then we're done - if (desk == NULL) { - log((CLOG_INFO "disconnecting desktop")); - return true; - } - - // set the desktop. can only do this when there are no windows - // and hooks on the current desktop owned by this thread. - if (SetThreadDesktop(desk) == 0) { - log((CLOG_ERR "failed to set desktop: %d", GetLastError())); - CloseDesktop(desk); - return false; - } - - // initialize clipboard owner to current owner. we don't want - // to take ownership of the clipboard just by starting up. - m_clipboardOwner = GetClipboardOwner(); - - // create the cursor hiding window. this window is used to hide the - // cursor when it's not on the screen. the window is hidden as soon - // as the cursor enters the screen or the display's real cursor is - // moved. - m_window = CreateWindowEx(WS_EX_TOPMOST | - WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - (LPCTSTR)getClass(), "Synergy", - WS_POPUP, - 0, 0, 1, 1, NULL, NULL, - getInstance(), - NULL); - if (m_window == NULL) { - log((CLOG_ERR "failed to create window: %d", GetLastError())); - CloseDesktop(desk); - return false; - } - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - // if we owned the desktop then set the clipboard owner - if (ownClipboard) { - m_clipboardOwner = GetClipboardOwner(); - } - - // save new desktop - m_desk = desk; - m_deskName = getDesktopName(m_desk); - log((CLOG_INFO "switched to desktop %s", m_deskName.c_str())); - - // get desktop up to date - if (!m_active) { - showWindow(); - } - - return true; -} - -void -CMSWindowsSecondaryScreen::syncDesktop() const -{ - // note -- mutex must be locked on entry - - // change calling thread's desktop - if (!m_is95Family) { - if (SetThreadDesktop(m_desk) == 0) { - log((CLOG_WARN "failed to set desktop: %d", GetLastError())); - } - } - - // attach input queues if not already attached. this has a habit - // of sucking up more and more CPU each time it's called (even if - // the threads are already attached). since we only expect one - // thread to call this more than once we can save just the last - // the attached thread. - DWORD threadID = GetCurrentThreadId(); - if (threadID != m_lastThreadID && threadID != m_threadID) { - m_lastThreadID = threadID; - AttachThreadInput(threadID, m_threadID, TRUE); - } -} - -bool -CMSWindowsSecondaryScreen::isMultimon() const -{ - SInt32 x0, y0, w, h; - getScreenShape(x0, y0, w, h); - return (w != GetSystemMetrics(SM_CXSCREEN) || - h != GetSystemMetrics(SM_CYSCREEN)); -} - // these tables map KeyID (a X windows KeySym) to virtual key codes. // if the key is an extended key then the entry is the virtual key // code | 0x100. keys that map to normal characters have a 0 entry @@ -1768,7 +1331,7 @@ CMSWindowsSecondaryScreen::releaseKeys() { CLock lock(&m_mutex); - syncDesktop(); + m_screen->syncDesktop(); // release left/right modifier keys first. if the platform doesn't // support them then they won't be set and the non-side-distinuishing @@ -1814,60 +1377,6 @@ CMSWindowsSecondaryScreen::releaseKeys() } } -void -CMSWindowsSecondaryScreen::updateKeys() -{ - // clear key state - memset(m_keys, 0, sizeof(m_keys)); - - // we only care about the modifier key states - m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); - m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); - m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); - m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL)); - m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL)); - m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL)); - m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU)); - m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU)); - m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU)); - m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN)); - m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN)); - m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS)); - m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); - m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); - m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); -} - -void -CMSWindowsSecondaryScreen::updateModifiers() -{ - // update active modifier mask - m_mask = 0; - if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) { - m_mask |= KeyModifierShift; - } - if ((m_keys[VK_LCONTROL] & 0x80) != 0 || - (m_keys[VK_RCONTROL] & 0x80) != 0) { - m_mask |= KeyModifierControl; - } - if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) { - m_mask |= KeyModifierAlt; - } - if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) { - m_mask |= KeyModifierMeta; - } - if ((m_keys[VK_CAPITAL] & 0x01) != 0) { - m_mask |= KeyModifierCapsLock; - } - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { - m_mask |= KeyModifierNumLock; - } - if ((m_keys[VK_SCROLL] & 0x01) != 0) { - m_mask |= KeyModifierScrollLock; - } - log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); -} - void CMSWindowsSecondaryScreen::toggleKey(UINT virtualKey, KeyModifierMask mask) { diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index c6f9c5e3..e9b956c7 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -7,28 +7,22 @@ #define _WIN32_WINNT 0x401 #endif -#include "CMSWindowsScreen.h" -#include "ISecondaryScreen.h" +#include "CSecondaryScreen.h" +#include "IMSWindowsScreenEventHandler.h" #include "CMutex.h" #include "CString.h" #include "stdvector.h" +class CMSWindowsScreen; class IScreenReceiver; -class CMSWindowsSecondaryScreen : public CMSWindowsScreen, - public ISecondaryScreen { +class CMSWindowsSecondaryScreen : + public CSecondaryScreen, public IMSWindowsScreenEventHandler { public: CMSWindowsSecondaryScreen(IScreenReceiver*); virtual ~CMSWindowsSecondaryScreen(); - // ISecondaryScreen overrides - virtual void run(); - virtual void stop(); - virtual void open(); - virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, - KeyModifierMask mask); - virtual void leave(); + // CSecondaryScreen overrides virtual void keyDown(KeyID, KeyModifierMask); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); virtual void keyUp(KeyID, KeyModifierMask); @@ -36,19 +30,30 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); virtual void mouseWheel(SInt32 delta); - virtual void setClipboard(ClipboardID, const IClipboard*); - virtual void grabClipboard(ClipboardID); - virtual void screenSaver(bool activate); - virtual void getMousePos(SInt32& x, SInt32& y) const; - virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - virtual SInt32 getJumpZoneSize() const; - virtual void getClipboard(ClipboardID, IClipboard*) const; + virtual IScreen* getScreen() const; -protected: - // CMSWindowsScreen overrides + // IMSWindowsScreenEventHandler overrides + virtual void onError(); + virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); - virtual CString getCurrentDesktopName() const; + virtual SInt32 getJumpZoneSize() const; + virtual void postCreateWindow(HWND); + virtual void preDestroyWindow(HWND); + +protected: + // CSecondaryScreen overrides + virtual void onPreRun(); + virtual void onPreOpen(); + virtual void onPreEnter(); + virtual void onPreLeave(); + virtual void createWindow(); + virtual void destroyWindow(); + virtual void showWindow(); + virtual void hideWindow(); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void updateKeys(); + virtual void setToggleState(KeyModifierMask); private: enum EKeyAction { kPress, kRelease, kRepeat }; @@ -60,26 +65,6 @@ private: }; typedef std::vector Keystrokes; - void showWindow(); - void hideWindow(); - - // warp the mouse to the specified position - void warpCursor(SInt32 x, SInt32 y); - - // check clipboard ownership and, if necessary, tell the receiver - // of a grab. - void checkClipboard(); - - // create/destroy window - // also attach to desktop; this destroys and recreates the window - // as necessary. - void createWindow(); - void destroyWindow(); - - // start/stop watch for screen saver changes - void installScreenSaver(); - void uninstallScreenSaver(); - // open/close desktop (for windows 95/98/me) bool openDesktop(); void closeDesktop(); @@ -87,9 +72,6 @@ private: // make desk the thread desktop (for windows NT/2000/XP) bool switchDesktop(HDESK desk); - // get calling thread to use the input desktop - void syncDesktop() const; - // returns true iff there appear to be multiple monitors bool isMultimon() const; @@ -100,8 +82,6 @@ private: void doKeystrokes(const Keystrokes&, SInt32 count); void releaseKeys(); - void updateKeys(); - void updateModifiers(); void toggleKey(UINT virtualKey, KeyModifierMask mask); UINT virtualKeyToScanCode(UINT& virtualKey); bool isExtendedKey(UINT virtualKey); @@ -109,37 +89,14 @@ private: private: CMutex m_mutex; - IScreenReceiver* m_receiver; + CMSWindowsScreen* m_screen; // true if windows 95/98/me bool m_is95Family; - // true if system appears to have multiple monitors - bool m_multimon; - - // the main loop's thread id - DWORD m_threadID; - - // the timer used to check for desktop switching - UINT m_timer; - - // the thread id of the last attached thread - mutable DWORD m_lastThreadID; - - // the current desk and it's name - HDESK m_desk; - CString m_deskName; - - // our window (for getting clipboard changes) + // our window HWND m_window; - // m_active is true if this screen has been entered - bool m_active; - - // clipboard stuff - HWND m_nextClipboardWindow; - HWND m_clipboardOwner; - // virtual key states BYTE m_keys[256]; diff --git a/client/CSecondaryScreen.cpp b/client/CSecondaryScreen.cpp index 12358b2a..a19bc4e4 100644 --- a/client/CSecondaryScreen.cpp +++ b/client/CSecondaryScreen.cpp @@ -74,7 +74,7 @@ CSecondaryScreen::open() updateKeys(); // disable the screen saver - getScreen()->openScreenSaver(false); + getScreen()->openScreensaver(false); // subclass hook onPostOpen(); @@ -93,7 +93,7 @@ void CSecondaryScreen::close() { onPreClose(); - getScreen()->closeScreenSaver(); + getScreen()->closeScreensaver(); destroyWindow(); getScreen()->close(); onPostClose(); @@ -182,12 +182,6 @@ CSecondaryScreen::getClipboard(ClipboardID id, getScreen()->getClipboard(id, clipboard); } -SInt32 -CSecondaryScreen::getJumpZoneSize() const -{ - return 0; -} - void CSecondaryScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { diff --git a/client/CSecondaryScreen.h b/client/CSecondaryScreen.h index 736fc4d9..4db665f1 100644 --- a/client/CSecondaryScreen.h +++ b/client/CSecondaryScreen.h @@ -78,8 +78,8 @@ public: void getClipboard(ClipboardID, IClipboard*) const; // returns the size of the zone on the edges of the screen that - // causes the cursor to jump to another screen. default returns 0. - virtual SInt32 getJumpZoneSize() const; + // causes the cursor to jump to another screen. + virtual SInt32 getJumpZoneSize() const = 0; // get the shape (position of upper-left corner and size) of the // screen diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 1207cd8f..d8e5ab66 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -145,12 +145,6 @@ CXWindowsSecondaryScreen::getScreen() const return m_screen; } -void -CXWindowsSecondaryScreen::onScreensaver(bool) -{ - // ignore -} - void CXWindowsSecondaryScreen::onError() { @@ -158,6 +152,12 @@ CXWindowsSecondaryScreen::onError() // FIXME -- forward this? to whom? } +void +CXWindowsSecondaryScreen::onScreensaver(bool) +{ + // ignore +} + bool CXWindowsSecondaryScreen::onPreDispatch(const CEvent*) { @@ -184,6 +184,12 @@ CXWindowsSecondaryScreen::onEvent(CEvent* event) } } +SInt32 +CXWindowsSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + void CXWindowsSecondaryScreen::onPreRun() { @@ -260,7 +266,7 @@ CXWindowsSecondaryScreen::createWindow() XTestGrabControl(display, True); } - // tell our superclass about the window + // tell generic screen about the window m_screen->setWindow(m_window); } @@ -275,15 +281,19 @@ CXWindowsSecondaryScreen::destroyWindow() // no longer impervious to server grabs XTestGrabControl(display, False); - // destroy window - if (m_window != None) { - XDestroyWindow(display, m_window); - m_window = None; - } - // update XSync(display, False); } + + // destroy window + if (m_window != None) { + m_screen->setWindow(None); + CDisplayLock display(m_screen); + if (display != NULL) { + XDestroyWindow(display, m_window); + } + m_window = None; + } } void diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index d2797d65..6237996b 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -35,6 +35,7 @@ public: virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); + virtual SInt32 getJumpZoneSize() const; protected: // CSecondaryScreen overrides diff --git a/client/client.dsp b/client/client.dsp index 7311f204..d48c3aaf 100644 --- a/client/client.dsp +++ b/client/client.dsp @@ -110,6 +110,10 @@ SOURCE=.\CMSWindowsSecondaryScreen.cpp # End Source File # Begin Source File +SOURCE=.\CSecondaryScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\CServerProxy.cpp # End Source File # End Group @@ -126,11 +130,11 @@ SOURCE=.\CMSWindowsSecondaryScreen.h # End Source File # Begin Source File -SOURCE=.\CServerProxy.h +SOURCE=.\CSecondaryScreen.h # End Source File # Begin Source File -SOURCE=.\ISecondaryScreen.h +SOURCE=.\CServerProxy.h # End Source File # Begin Source File diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index 3a6afd57..9cc1a096 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -1,5 +1,10 @@ #include "CMSWindowsScreen.h" +#include "CMSWindowsClipboard.h" #include "CMSWindowsScreenSaver.h" +#include "CClipboard.h" +#include "IMSWindowsScreenEventHandler.h" +#include "IScreenReceiver.h" +#include "XSynergy.h" #include "CThread.h" #include "CLock.h" #include "TMethodJob.h" @@ -8,7 +13,7 @@ #include // -// add backwards compatible multihead support (suppress bogus warning) +// add backwards compatible multihead support (and suppress bogus warning) // #pragma warning(push) #pragma warning(disable: 4706) // assignment within conditional @@ -23,21 +28,46 @@ HINSTANCE CMSWindowsScreen::s_instance = NULL; CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; -CMSWindowsScreen::CMSWindowsScreen() : - m_class(0), +CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, + IMSWindowsScreenEventHandler* eventHandler) : + m_receiver(receiver), + m_eventHandler(eventHandler), + m_class(NULL), + m_icon(NULL), m_cursor(NULL), + m_window(NULL), m_x(0), m_y(0), m_w(0), m_h(0), - m_thread(0), - m_screenSaver(NULL) + m_multimon(false), + m_threadID(0), + m_lastThreadID(0), + m_nextClipboardWindow(NULL), + m_clipboardOwner(NULL), + m_timer(0), + m_desk(NULL), + m_deskName(), + m_hookLibrary(NULL), + m_installScreensaver(NULL), + m_uninstallScreensaver(NULL), + m_screensaver(NULL), + m_screensaverNotify(false) { - assert(s_screen == NULL); + assert(s_screen == NULL); + assert(m_receiver != NULL); + assert(m_eventHandler != NULL); + s_screen = this; + + // make sure this thread has a message queue + MSG dummy; + PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); } CMSWindowsScreen::~CMSWindowsScreen() { - assert(m_class == 0); + assert(s_screen != NULL); + assert(m_class == 0); + s_screen = NULL; } @@ -47,11 +77,110 @@ CMSWindowsScreen::init(HINSTANCE instance) s_instance = instance; } +HWND +CMSWindowsScreen::openDesktop() +{ + // save thread id + m_threadID = GetCurrentThreadId(); + + // get the input desktop and switch to it + if (!switchDesktop(openInputDesktop())) { + return NULL; + } + + // poll input desktop to see if it changes (onPreDispatch() + // handles WM_TIMER) + m_timer = 0; + if (!m_is95Family) { + m_timer = SetTimer(NULL, 0, 200, NULL); + } + + return m_window; +} + +void +CMSWindowsScreen::closeDesktop() +{ + // remove timer + if (m_timer != 0) { + KillTimer(NULL, m_timer); + } + + // disconnect from desktop + switchDesktop(NULL); + + // clear thread id + m_threadID = 0; + + assert(m_window == NULL); + assert(m_desk == NULL); +} + +bool +CMSWindowsScreen::isMultimon() const +{ + return m_multimon; +} + +HINSTANCE +CMSWindowsScreen::getInstance() +{ + return s_instance; +} + +void +CMSWindowsScreen::open() +{ + assert(s_instance != NULL); + assert(m_class == 0); + + log((CLOG_DEBUG "opening display")); + + // create the transparent cursor + createBlankCursor(); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = s_instance; + classInfo.hIcon = NULL; + classInfo.hCursor = m_cursor; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "Synergy"; + classInfo.hIconSm = NULL; + m_class = RegisterClassEx(&classInfo); + + // get screen shape + updateScreenShape(); + + // initialize the screen saver + m_screensaver = new CMSWindowsScreenSaver(); + + // load the hook library and get the screen saver functions + m_hookLibrary = LoadLibrary("synrgyhk"); + if (m_hookLibrary != NULL) { + m_installScreensaver = (InstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "installScreenSaver"); + m_uninstallScreensaver = (UninstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "uninstallScreenSaver"); + if (m_installScreensaver == NULL || m_uninstallScreensaver == NULL) { + // disable if either install or uninstall is unavailable + m_installScreensaver = NULL; + m_uninstallScreensaver = NULL; + } + } +} + void CMSWindowsScreen::mainLoop() { - // save thread id for posting quit message - m_thread = GetCurrentThreadId(); + // must call mainLoop() from same thread as openDesktop() + assert(m_threadID == GetCurrentThreadId()); // event loop CEvent event; @@ -77,55 +206,25 @@ CMSWindowsScreen::mainLoop() void CMSWindowsScreen::exitMainLoop() { - PostThreadMessage(m_thread, WM_QUIT, 0, 0); -} - -bool -CMSWindowsScreen::onPreDispatch(const CEvent*) -{ - return false; + PostThreadMessage(m_threadID, WM_QUIT, 0, 0); } void -CMSWindowsScreen::openDisplay() +CMSWindowsScreen::close() { assert(s_instance != NULL); - assert(m_class == 0); - // create the transparent cursor - createBlankCursor(); - - // register a window class - WNDCLASSEX classInfo; - classInfo.cbSize = sizeof(classInfo); - classInfo.style = CS_DBLCLKS | CS_NOCLOSE; - classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; - classInfo.cbClsExtra = 0; - classInfo.cbWndExtra = 0; - classInfo.hInstance = s_instance; - classInfo.hIcon = NULL; - classInfo.hCursor = m_cursor; - classInfo.hbrBackground = NULL; - classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = "Synergy"; - classInfo.hIconSm = NULL; - m_class = RegisterClassEx(&classInfo); - - // get screen shape - updateScreenShape(); - - // initialize the screen saver - m_screenSaver = new CMSWindowsScreenSaver(); -} - -void -CMSWindowsScreen::closeDisplay() -{ - assert(s_instance != NULL); + // done with hook library + if (m_hookLibrary != NULL) { + FreeLibrary(m_hookLibrary); + m_installScreensaver = NULL; + m_uninstallScreensaver = NULL; + m_hookLibrary = NULL; + } // done with screen saver - delete m_screenSaver; - m_screenSaver = NULL; + delete m_screensaver; + m_screensaver = NULL; // unregister the window class if (m_class != 0) { @@ -142,30 +241,130 @@ CMSWindowsScreen::closeDisplay() log((CLOG_DEBUG "closed display")); } -HINSTANCE -CMSWindowsScreen::getInstance() +bool +CMSWindowsScreen::setClipboard(ClipboardID, const IClipboard* src) { - return s_instance; -} - -ATOM -CMSWindowsScreen::getClass() const -{ - return m_class; + CMSWindowsClipboard dst(m_window); + if (src != NULL) { + // save clipboard data + return CClipboard::copy(&dst, src); + } + else { + // assert clipboard ownership + if (!dst.open(0)) { + return false; + } + dst.empty(); + dst.close(); + return true; + } } void -CMSWindowsScreen::updateScreenShape() +CMSWindowsScreen::checkClipboards() { - m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); - m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); - m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); - m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); - log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + // if we think we own the clipboard but we don't then somebody + // grabbed the clipboard on this screen without us knowing. + // tell the server that this screen grabbed the clipboard. + // + // this works around bugs in the clipboard viewer chain. + // sometimes NT will simply never send WM_DRAWCLIPBOARD + // messages for no apparent reason and rebooting fixes the + // problem. since we don't want a broken clipboard until the + // next reboot we do this double check. clipboard ownership + // won't be reflected on other screens until we leave but at + // least the clipboard itself will work. + HWND clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != clipboardOwner) { + try { + m_clipboardOwner = clipboardOwner; + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + catch (XBadClient&) { + // ignore + } + } } void -CMSWindowsScreen::getScreenShape(SInt32& x, SInt32& y, +CMSWindowsScreen::openScreensaver(bool notify) +{ + assert(m_screensaver != NULL); + + m_screensaverNotify = notify; + if (m_screensaverNotify) { + if (m_installScreensaver != NULL) { + m_installScreensaver(); + } + } + else { + m_screensaver->disable(); + } +} + +void +CMSWindowsScreen::closeScreensaver() +{ + if (m_screensaver != NULL) { + if (m_screensaverNotify) { + if (m_uninstallScreensaver != NULL) { + m_uninstallScreensaver(); + } + } + else { + m_screensaver->enable(); + } + } + m_screensaverNotify = false; +} + +void +CMSWindowsScreen::screensaver(bool activate) +{ + assert(m_screensaver != NULL); + if (activate) { + m_screensaver->activate(); + } + else { + m_screensaver->deactivate(); + } +} + +void +CMSWindowsScreen::syncDesktop() +{ + // change calling thread's desktop + if (!m_is95Family) { + if (SetThreadDesktop(m_desk) == 0) { + log((CLOG_WARN "failed to set desktop: %d", GetLastError())); + } + } + + // attach input queues if not already attached. this has a habit + // of sucking up more and more CPU each time it's called (even if + // the threads are already attached). since we only expect one + // thread to call this more than once we can save just the last + // the attached thread. + DWORD threadID = GetCurrentThreadId(); + if (threadID != m_lastThreadID && threadID != m_threadID) { + m_lastThreadID = threadID; + AttachThreadInput(threadID, m_threadID, TRUE); + } +} + +bool +CMSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const +{ + CMSWindowsClipboard src(m_window); + CClipboard::copy(dst, &src); + return true; +} + +void +CMSWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { assert(m_class != 0); @@ -196,10 +395,153 @@ CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const y = GetSystemMetrics(SM_CYSCREEN) >> 1; } -HCURSOR -CMSWindowsScreen::getBlankCursor() const +void +CMSWindowsScreen::updateScreenShape() { - return m_cursor; + // get shape + m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); + m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); + m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); + log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + + // check for multiple monitors + m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || + m_h != GetSystemMetrics(SM_CYSCREEN)); +} + +bool +CMSWindowsScreen::onPreDispatch(const CEvent* event) +{ + // handle event + const MSG* msg = &event->m_msg; + switch (msg->message) { + case SYNERGY_MSG_SCREEN_SAVER: + if (msg->wParam != 0) { + if (m_screensaver->checkStarted(msg->message, FALSE, 0)) { + m_eventHandler->onScreensaver(true); + } + } + else { + m_eventHandler->onScreensaver(false); + } + return true; + + case WM_TIMER: + // if current desktop is not the input desktop then switch to it. + // windows 95 doesn't support multiple desktops so don't bother + // to check under it. + if (!m_is95Family) { + HDESK desk = openInputDesktop(); + if (desk != NULL) { + if (isCurrentDesktop(desk)) { + CloseDesktop(desk); + } + else { + switchDesktop(desk); + } + } + } + return true; + } + + return m_eventHandler->onPreDispatch(event); +} + +bool +CMSWindowsScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + + const MSG& msg = event->m_msg; + switch (msg.message) { + case WM_QUERYENDSESSION: + if (m_is95Family) { + event->m_result = TRUE; + return true; + } + break; + + case WM_ENDSESSION: + if (m_is95Family) { + if (msg.wParam == TRUE && msg.lParam == 0) { + exitMainLoop(); + } + return true; + } + break; + + case WM_PAINT: + ValidateRect(msg.hwnd, NULL); + return true; + + case WM_DRAWCLIPBOARD: + log((CLOG_DEBUG "clipboard was taken")); + + // first pass it on + if (m_nextClipboardWindow != NULL) { + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); + } + + // now notify client that somebody changed the clipboard (unless + // we're now the owner, in which case it's because we took + // ownership, or now it's owned by nobody, which will happen if + // we owned it and switched desktops because we destroy our + // window to do that). + try { + m_clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + catch (XBadClient&) { + // ignore. this can happen if we receive this event + // before we've fully started up. + } + return true; + + case WM_CHANGECBCHAIN: + if (m_nextClipboardWindow == (HWND)msg.wParam) { + m_nextClipboardWindow = (HWND)msg.lParam; + } + else if (m_nextClipboardWindow != NULL) { + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); + } + return true; + + case WM_DISPLAYCHANGE: + { + // screen resolution may have changed. get old shape. + SInt32 xOld, yOld, wOld, hOld; + getShape(xOld, yOld, wOld, hOld); + + // update shape + updateScreenShape(); + + // collect new screen info + CClientInfo info; + getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = m_eventHandler->getJumpZoneSize(); + + // do nothing if resolution hasn't changed + if (info.m_x != xOld || info.m_y != yOld || + info.m_w != wOld || info.m_h != hOld) { + // forward event + m_eventHandler->onEvent(event); + + // send new screen info + m_receiver->onInfoChanged(info); + } + + return true; + } + } + + return m_eventHandler->onEvent(event); } void @@ -217,13 +559,121 @@ CMSWindowsScreen::createBlankCursor() delete[] cursorAND; } +bool +CMSWindowsScreen::switchDesktop(HDESK desk) +{ + // did we own the clipboard? + bool ownClipboard = (m_clipboardOwner == m_window && m_window != NULL); + + // destroy old window + if (m_window != NULL) { + // first remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + // we no longer own the clipboard + if (ownClipboard) { + m_clipboardOwner = NULL; + } + + // let client clean up before we destroy the window + m_eventHandler->preDestroyWindow(m_window); + + // now destroy window + DestroyWindow(m_window); + m_window = NULL; + + // done with desk + if (!m_is95Family) { + CloseDesktop(m_desk); + } + m_desk = NULL; + m_deskName = ""; + } + + // if no new desktop then we're done + if (desk == NULL) { + log((CLOG_INFO "disconnecting desktop")); + return true; + } + + // uninstall screen saver hooks + if (m_screensaverNotify) { + if (m_uninstallScreensaver != NULL) { + m_uninstallScreensaver(); + } + } + + // set the desktop. can only do this when there are no windows + // and hooks on the current desktop owned by this thread. + if (SetThreadDesktop(desk) == 0) { + log((CLOG_ERR "failed to set desktop: %d", GetLastError())); + if (!m_is95Family) { + CloseDesktop(desk); + } + return false; + } + + // create the window + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | + WS_EX_TOOLWINDOW, + (LPCTSTR)m_class, + "Synergy", + WS_POPUP, + 0, 0, 1, 1, + NULL, NULL, + getInstance(), + NULL); + if (m_window == NULL) { + log((CLOG_ERR "failed to create window: %d", GetLastError())); + if (!m_is95Family) { + CloseDesktop(desk); + } + return false; + } + + // reinstall screen saver hooks + if (m_screensaverNotify) { + if (m_installScreensaver != NULL) { + m_installScreensaver(); + } + } + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + + // reassert clipboard ownership + if (ownClipboard) { + // FIXME -- take clipboard ownership, but we should also set + // the clipboard data. + } + m_clipboardOwner = GetClipboardOwner(); + + // save new desktop + m_desk = desk; + m_deskName = getDesktopName(m_desk); + log((CLOG_INFO "switched to desktop \"%s\"", m_deskName.c_str())); + + // let client prepare the window + m_eventHandler->postCreateWindow(m_window); + + return true; +} + HDESK CMSWindowsScreen::openInputDesktop() const { - return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, + if (m_is95Family) { + // there's only one desktop on windows 95 et al. + return GetThreadDesktop(GetCurrentThreadId()); + } + else { + return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, DESKTOP_CREATEWINDOW | DESKTOP_HOOKCONTROL | GENERIC_WRITE); + } } CString @@ -232,6 +682,9 @@ CMSWindowsScreen::getDesktopName(HDESK desk) const if (desk == NULL) { return CString(); } + else if (m_is95Family) { + return "desktop"; + } else { DWORD size; GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); @@ -246,14 +699,7 @@ CMSWindowsScreen::getDesktopName(HDESK desk) const bool CMSWindowsScreen::isCurrentDesktop(HDESK desk) const { - return CStringUtil::CaselessCmp::equal(getDesktopName(desk), - getCurrentDesktopName()); -} - -CMSWindowsScreenSaver* -CMSWindowsScreen::getScreenSaver() const -{ - return m_screenSaver; + return CStringUtil::CaselessCmp::equal(getDesktopName(desk), m_deskName); } LRESULT CALLBACK diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index c45b258a..9a2f7c92 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -1,7 +1,8 @@ #ifndef CMSWINDOWSSCREEN_H #define CMSWINDOWSSCREEN_H -#include "IClipboard.h" +#include "IScreen.h" +#include "CSynergyHook.h" #include "CMutex.h" #include "CString.h" #define WIN32_LEAN_AND_MEAN @@ -16,52 +17,69 @@ public: LRESULT m_result; }; -class CMSWindowsScreen { +class IScreenReceiver; +class IMSWindowsScreenEventHandler; + +class CMSWindowsScreen : public IScreen { public: - CMSWindowsScreen(); + CMSWindowsScreen(IScreenReceiver*, IMSWindowsScreenEventHandler*); virtual ~CMSWindowsScreen(); // manipulators static void init(HINSTANCE); + // open the desktop and create and return the window. returns NULL + // on failure. + HWND openDesktop(); + + // close the window and desktop + void closeDesktop(); + // accessors + // returns true iff the system appears to have multiple monitors + bool isMultimon() const; + // get the application instance handle static HINSTANCE getInstance(); -protected: - // runs an event loop and returns when exitMainLoop() is called + // IScreen overrides + // note -- this class expects the hook DLL to have been loaded + // and initialized before open() is called. + void open(); void mainLoop(); - - // force mainLoop() to return void exitMainLoop(); + void close(); + bool setClipboard(ClipboardID, const IClipboard*); + void checkClipboards(); + void openScreensaver(bool notify); + void closeScreensaver(); + void screensaver(bool activate); + void syncDesktop(); + bool getClipboard(ClipboardID, IClipboard*) const; + void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; + void getCursorPos(SInt32&, SInt32&) const; + void getCursorCenter(SInt32&, SInt32&) const; - // open the X display. calls onOpenDisplay() after opening the display, - // getting the screen, its size, and root window. then it starts the - // event thread. - void openDisplay(); - - // destroy the window and close the display. calls onCloseDisplay() - // after the event thread has been shut down but before the display - // is closed. - void closeDisplay(); - - // get the registered window class atom - ATOM getClass() const; - +private: // update screen size cache void updateScreenShape(); - // get the size of the screen - void getScreenShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const; + // internal pre-dispatch event processing + bool onPreDispatch(const CEvent* event); - // get the current cursor position - void getCursorPos(SInt32& x, SInt32& y) const; + // internal (post-dispatch) event processing + bool onEvent(CEvent* event); - // get the cursor center position - void getCursorCenter(SInt32& x, SInt32& y) const; + // create the transparent cursor + void createBlankCursor(); + + // switch to the given desktop. this destroys the window and unhooks + // all hooks, switches the desktop, then creates the window and rehooks + // all hooks (because you can't switch the thread's desktop if it has + // any windows or hooks). + bool switchDesktop(HDESK desk); // get the input desktop. caller must CloseDesktop() the result. // do not call under windows 95/98/me. @@ -74,38 +92,56 @@ protected: // windows 95/98/me. bool isCurrentDesktop(HDESK desk) const; - // get the screen saver object - CMSWindowsScreenSaver* - getScreenSaver() const; - - // called for each event before event translation and dispatch. return - // true to skip translation and dispatch. subclasses should call the - // superclass's version first and return true if it returns true. - virtual bool onPreDispatch(const CEvent* event); - - // called by mainLoop(). iff the event was handled return true and - // store the result, if any, in m_result, which defaults to zero. - virtual bool onEvent(CEvent* event) = 0; - - // called by isCurrentDesktop() to get the current desktop name - virtual CString getCurrentDesktopName() const = 0; - -private: - // create the transparent cursor - void createBlankCursor(); - // our window proc static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); private: static HINSTANCE s_instance; + + IScreenReceiver* m_receiver; + IMSWindowsScreenEventHandler* m_eventHandler; + ATOM m_class; HICON m_icon; HCURSOR m_cursor; + + // true if windows 95/98/me + bool m_is95Family; + + // our window + HWND m_window; + + // screen shape SInt32 m_x, m_y; SInt32 m_w, m_h; - DWORD m_thread; - CMSWindowsScreenSaver* m_screenSaver; + + // true if system appears to have multiple monitors + bool m_multimon; + + // the main loop's thread id + DWORD m_threadID; + + // the thread id of the last attached thread + DWORD m_lastThreadID; + + // clipboard stuff + HWND m_nextClipboardWindow; + HWND m_clipboardOwner; + + // the timer used to check for desktop switching + UINT m_timer; + + // the current desk and it's name + HDESK m_desk; + CString m_deskName; + + // screen saver stuff + HINSTANCE m_hookLibrary; + InstallScreenSaverFunc m_installScreensaver; + UninstallScreenSaverFunc m_uninstallScreensaver; + CMSWindowsScreenSaver* m_screensaver; + bool m_screensaverNotify; + static CMSWindowsScreen* s_screen; }; diff --git a/server/CSynergyHook.cpp b/platform/CSynergyHook.cpp similarity index 100% rename from server/CSynergyHook.cpp rename to platform/CSynergyHook.cpp diff --git a/server/CSynergyHook.h b/platform/CSynergyHook.h similarity index 100% rename from server/CSynergyHook.h rename to platform/CSynergyHook.h diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index e9b46721..ed573cbf 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -10,8 +10,6 @@ #include "CThread.h" #include "CLog.h" #include "IJob.h" -//#include "CString.h" -//#include #include #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy @@ -310,7 +308,7 @@ CXWindowsScreen::checkClipboards() } void -CXWindowsScreen::openScreenSaver(bool notify) +CXWindowsScreen::openScreensaver(bool notify) { CLock lock(&m_mutex); assert(m_screensaver != NULL); @@ -325,7 +323,7 @@ CXWindowsScreen::openScreenSaver(bool notify) } void -CXWindowsScreen::closeScreenSaver() +CXWindowsScreen::closeScreensaver() { CLock lock(&m_mutex); if (m_screensaver != NULL) { diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index e164c583..5c7bf18b 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -2,7 +2,6 @@ #define CXWINDOWSSCREEN_H #include "IScreen.h" -#include "ClipboardTypes.h" #include "CMutex.h" #include "CStopwatch.h" #include "stdvector.h" @@ -59,8 +58,8 @@ public: void close(); bool setClipboard(ClipboardID, const IClipboard*); void checkClipboards(); - void openScreenSaver(bool notify); - void closeScreenSaver(); + void openScreensaver(bool notify); + void closeScreensaver(); void screensaver(bool activate); void syncDesktop(); bool getClipboard(ClipboardID, IClipboard*) const; diff --git a/platform/IMSWindowsScreenEventHandler.h b/platform/IMSWindowsScreenEventHandler.h new file mode 100755 index 00000000..33804a96 --- /dev/null +++ b/platform/IMSWindowsScreenEventHandler.h @@ -0,0 +1,26 @@ +#ifndef IMSWINDOWSSCREENEVENTHANDLER_H +#define IMSWINDOWSSCREENEVENTHANDLER_H + +#include "IScreenEventHandler.h" +#define WIN32_LEAN_AND_MEAN +#include + +class IMSWindowsScreenEventHandler : public IScreenEventHandler { +public: + // manipulators + + // called after the window is created + virtual void postCreateWindow(HWND) = 0; + + // called before the window is destroyed + virtual void preDestroyWindow(HWND) = 0; + + // IScreenEventHandler overrides + virtual void onError() = 0; + virtual void onScreensaver(bool activated) = 0; + virtual bool onPreDispatch(const CEvent* event) = 0; + virtual bool onEvent(CEvent* event) = 0; + virtual SInt32 getJumpZoneSize() const = 0; +}; + +#endif diff --git a/server/makehook.dsp b/platform/makehook.dsp similarity index 100% rename from server/makehook.dsp rename to platform/makehook.dsp diff --git a/platform/platform.dsp b/platform/platform.dsp index 7ebcc139..ad92fece 100644 --- a/platform/platform.dsp +++ b/platform/platform.dsp @@ -127,6 +127,10 @@ SOURCE=.\CWin32Platform.h # End Source File # Begin Source File +SOURCE=.\IMSWindowsScreenEventHandler.h +# End Source File +# Begin Source File + SOURCE=.\IPlatform.h # End Source File # End Group diff --git a/server/synrgyhk.dsp b/platform/synrgyhk.dsp similarity index 100% rename from server/synrgyhk.dsp rename to platform/synrgyhk.dsp diff --git a/server/CConfig.cpp b/server/CConfig.cpp index 19d368a7..082f7a47 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -624,22 +624,22 @@ operator<<(std::ostream& s, const CConfig& config) screen != config.end(); ++screen) { s << "\t" << screen->c_str() << ":" << std::endl; - neighbor = config.getNeighbor(*screen, CConfig::kLeft); + neighbor = config.getNeighbor(*screen, kLeft); if (!neighbor.empty()) { s << "\t\tleft=" << neighbor.c_str() << std::endl; } - neighbor = config.getNeighbor(*screen, CConfig::kRight); + neighbor = config.getNeighbor(*screen, kRight); if (!neighbor.empty()) { s << "\t\tright=" << neighbor.c_str() << std::endl; } - neighbor = config.getNeighbor(*screen, CConfig::kTop); + neighbor = config.getNeighbor(*screen, kTop); if (!neighbor.empty()) { s << "\t\tup=" << neighbor.c_str() << std::endl; } - neighbor = config.getNeighbor(*screen, CConfig::kBottom); + neighbor = config.getNeighbor(*screen, kBottom); if (!neighbor.empty()) { s << "\t\tdown=" << neighbor.c_str() << std::endl; } diff --git a/server/CConfig.h b/server/CConfig.h index a12d1a03..a67d211f 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -1,6 +1,7 @@ #ifndef CCONFIG_H #define CCONFIG_H +#include "ProtocolTypes.h" #include "CNetworkAddress.h" #include "XBase.h" #include "stdmap.h" @@ -20,11 +21,6 @@ struct iterator_traits { }; class CConfig { -public: - enum EDirection { kLeft, kRight, kTop, kBottom, - kFirstDirection = kLeft, kLastDirection = kBottom }; - enum EDirectionMask { kLeftMask = 1, kRightMask = 2, - kTopMask = 4, kBottomMask = 8 }; private: class CCell { public: diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index c626462d..1ac810ac 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -649,7 +649,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config) // insert the screen's neighbors // FIXME -- handle edge wrapping CString neighbor; - neighbor = config.getNeighbor(name, CConfig::kLeft); + neighbor = config.getNeighbor(name, kLeft); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert left neighbor, adding a column if necessary if (x == 0 || get(x - 1, y) != neighbor) { @@ -659,7 +659,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config) } screenStack.push_back(neighbor); } - neighbor = config.getNeighbor(name, CConfig::kRight); + neighbor = config.getNeighbor(name, kRight); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert right neighbor, adding a column if necessary if (x == m_w - 1 || get(x + 1, y) != neighbor) { @@ -668,7 +668,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config) } screenStack.push_back(neighbor); } - neighbor = config.getNeighbor(name, CConfig::kTop); + neighbor = config.getNeighbor(name, kTop); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert top neighbor, adding a row if necessary if (y == 0 || get(x, y - 1) != neighbor) { @@ -678,7 +678,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config) } screenStack.push_back(neighbor); } - neighbor = config.getNeighbor(name, CConfig::kBottom); + neighbor = config.getNeighbor(name, kBottom); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert bottom neighbor, adding a row if necessary if (y == m_h - 1 || get(x, y + 1) != neighbor) { @@ -699,25 +699,25 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config) } CString neighbor; - neighbor = config.getNeighbor(name, CConfig::kLeft); + neighbor = config.getNeighbor(name, kLeft); if ((x == 0 && !neighbor.empty()) || (x > 0 && get(x - 1, y) != neighbor)) { return false; } - neighbor = config.getNeighbor(name, CConfig::kRight); + neighbor = config.getNeighbor(name, kRight); if ((x == m_w - 1 && !neighbor.empty()) || (x < m_w - 1 && get(x + 1, y) != neighbor)) { return false; } - neighbor = config.getNeighbor(name, CConfig::kTop); + neighbor = config.getNeighbor(name, kTop); if ((y == 0 && !neighbor.empty()) || (y > 0 && get(x, y - 1) != neighbor)) { return false; } - neighbor = config.getNeighbor(name, CConfig::kBottom); + neighbor = config.getNeighbor(name, kBottom); if ((y == m_h - 1 && !neighbor.empty()) || (y < m_h - 1 && get(x, y + 1) != neighbor)) { return false; @@ -764,24 +764,16 @@ CHTTPServer::CScreenArray::convertTo(CConfig& config) const continue; } if (x > x0 && isSet(x - 1, y)) { - config.connect(get(x, y), - CConfig::kLeft, - get(x - 1, y)); + config.connect(get(x, y), kLeft, get(x - 1, y)); } if (x < x1 && isSet(x + 1, y)) { - config.connect(get(x, y), - CConfig::kRight, - get(x + 1, y)); + config.connect(get(x, y), kRight, get(x + 1, y)); } if (y > y0 && isSet(x, y - 1)) { - config.connect(get(x, y), - CConfig::kTop, - get(x, y - 1)); + config.connect(get(x, y), kTop, get(x, y - 1)); } if (y < y1 && isSet(x, y + 1)) { - config.connect(get(x, y), - CConfig::kBottom, - get(x, y + 1)); + config.connect(get(x, y), kBottom, get(x, y + 1)); } } } diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index f1a2dad7..3906bcaf 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -1,14 +1,8 @@ #include "CMSWindowsPrimaryScreen.h" -#include "IScreenReceiver.h" +#include "CMSWindowsScreen.h" #include "IPrimaryScreenReceiver.h" -#include "CMSWindowsClipboard.h" -#include "CMSWindowsScreenSaver.h" #include "CPlatform.h" -#include "CClipboard.h" -#include "ProtocolTypes.h" #include "XScreen.h" -#include "XSynergy.h" -#include "CThread.h" #include "CLog.h" #include @@ -19,21 +13,16 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( IScreenReceiver* receiver, IPrimaryScreenReceiver* primaryReceiver) : - m_receiver(receiver), - m_primaryReceiver(primaryReceiver), + CPrimaryScreen(receiver), + m_receiver(primaryReceiver), + m_is95Family(CPlatform::isWindows95Family()), m_threadID(0), - m_timer(0), - m_desk(NULL), - m_deskName(), m_window(NULL), - m_active(false), m_mark(0), m_markReceived(0), - m_nextClipboardWindow(NULL), - m_clipboardOwner(NULL) + m_mouseMoveIgnore(0) { - assert(m_receiver != NULL); - assert(m_primaryReceiver != NULL); + assert(m_receiver != NULL); // load the hook library m_hookLibrary = LoadLibrary("synrgyhk"); @@ -60,22 +49,8 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( throw XScreenOpenFailure(); } - // get the screen saver functions - m_installScreenSaver = (InstallScreenSaverFunc)GetProcAddress( - m_hookLibrary, "installScreenSaver"); - m_uninstallScreenSaver = (UninstallScreenSaverFunc)GetProcAddress( - m_hookLibrary, "uninstallScreenSaver"); - if (m_installScreenSaver == NULL || m_uninstallScreenSaver == NULL) { - // disable uninstall if install is unavailable - m_uninstallScreenSaver = NULL; - } - - // detect operating system - m_is95Family = CPlatform::isWindows95Family(); - - // make sure this thread has a message queue - MSG dummy; - PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); + // create screen + m_screen = new CMSWindowsScreen(receiver, this); } CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() @@ -83,169 +58,10 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() assert(m_hookLibrary != NULL); assert(m_window == NULL); - // done with hook library + delete m_screen; FreeLibrary(m_hookLibrary); } -void -CMSWindowsPrimaryScreen::run() -{ - // must call run() from same thread as open() - assert(m_threadID == GetCurrentThreadId()); - - // change our priority - CThread::getCurrentThread().setPriority(-3); - - // run event loop - try { - log((CLOG_INFO "entering event loop")); - mainLoop(); - log((CLOG_INFO "exiting event loop")); - } - catch (...) { - log((CLOG_INFO "exiting event loop")); - throw; - } -} - -void -CMSWindowsPrimaryScreen::stop() -{ - exitMainLoop(); -} - -void -CMSWindowsPrimaryScreen::open() -{ - assert(m_window == NULL); - - CClientInfo info; - try { - // initialize hook library - m_threadID = GetCurrentThreadId(); - m_init(m_threadID); - - // open the display - openDisplay(); - - // create and prepare our window - createWindow(); - - // set jump zones - m_setZone(info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize); - - // initialize marks - m_mark = 0; - m_markReceived = 0; - nextMark(); - - // collect screen info - getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); - getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getJumpZoneSize(); - - // save mouse position - m_x = info.m_mx; - m_y = info.m_my; - - // compute center pixel of primary screen - getCursorCenter(m_xCenter, m_yCenter); - - // get keyboard state - updateKeys(); - - // get notified of screen saver activation/deactivation - installScreenSaver(); - } - catch (...) { - close(); - throw; - } - - // enter the screen - enterNoWarp(); - - // send screen info - m_receiver->onInfoChanged(info); -} - -void -CMSWindowsPrimaryScreen::close() -{ - uninstallScreenSaver(); - destroyWindow(); - closeDisplay(); - m_cleanup(); - m_threadID = 0; -} - -void -CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) -{ - log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreenSaver ? " for screen saver" : "")); - assert(m_active == true); - assert(m_window != NULL); - - // enter the screen - enterNoWarp(); - - // warp to requested location - if (!forScreenSaver) { - warpCursor(x, y); - } -} - -bool -CMSWindowsPrimaryScreen::leave() -{ - log((CLOG_INFO "leaving primary")); - assert(m_active == false); - assert(m_window != NULL); - - // all messages prior to now are invalid - nextMark(); - - // show our window - if (!showWindow()) { - return false; - } - - // relay all mouse and keyboard events - m_setRelay(true); - - // get state of keys as we leave - updateKeys(); - - // warp mouse to center of screen - warpCursorToCenter(); - - // ignore this many mouse motion events (not including the already - // queued events). on (at least) the win2k login desktop, one - // motion event is reported using a position from before the above - // warpCursor(). i don't know why it does that and other desktops - // don't have the same problem. anyway, simply ignoring that event - // works around it. -// FIXME -- is this true now that we're using mouse_event? - m_mouseMoveIgnore = 1; - - // disable ctrl+alt+del, alt+tab, etc - if (m_is95Family) { - DWORD dummy = 0; - SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); - } - - // discard messages until after the warp - nextMark(); - - // local client now active - m_active = true; - - // make sure our idea of clipboard ownership is correct - checkClipboard(); - - return true; -} - void CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) { @@ -264,41 +80,6 @@ CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) m_y = y; } -void -CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/, - const IClipboard* src) -{ - // FIXME -- this is identical to CMSWindowsSecondaryScreen's code - assert(m_window != NULL); - - CMSWindowsClipboard dst(m_window); - CClipboard::copy(&dst, src); -} - -void -CMSWindowsPrimaryScreen::grabClipboard(ClipboardID /*id*/) -{ - // FIXME -- this is identical to CMSWindowsSecondaryScreen's code - assert(m_window != NULL); - - CMSWindowsClipboard clipboard(m_window); - if (clipboard.open(0)) { - // FIXME -- don't we need to empty it? - clipboard.close(); - } -} - -void -CMSWindowsPrimaryScreen::getClipboard(ClipboardID /*id*/, - IClipboard* dst) const -{ - // FIXME -- this is identical to CMSWindowsSecondaryScreen's code - assert(m_window != NULL); - - CMSWindowsClipboard src(m_window); - CClipboard::copy(dst, &src); -} - KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { @@ -334,7 +115,7 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const // that have been pulled off the queue. in general, we won't get // these key messages because they're not for our window. if any // key (or mouse button) is down then we're locked to the screen. - if (m_active) { + if (isActive()) { // use shadow keyboard state in m_keys for (UInt32 i = 0; i < 256; ++i) { if ((m_keys[i] & 0x80) != 0) { @@ -358,16 +139,29 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const return false; } +IScreen* +CMSWindowsPrimaryScreen::getScreen() const +{ + return m_screen; +} + +void +CMSWindowsPrimaryScreen::onError() +{ + // ignore +} + +void +CMSWindowsPrimaryScreen::onScreensaver(bool activated) +{ + m_receiver->onScreensaver(activated); +} + bool -CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) +CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) { assert(event != NULL); - // forward to superclass - if (CMSWindowsScreen::onPreTranslate(event)) { - return true; - } - // handle event const MSG* msg = &event->m_msg; switch (msg->message) { @@ -386,11 +180,11 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); if (repeat >= 2) { log((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); - m_primaryReceiver->onKeyRepeat(key, mask, repeat); + m_receiver->onKeyRepeat(key, mask, repeat); } else { log((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x", key, mask)); - m_primaryReceiver->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); } // update key state @@ -399,7 +193,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) else { // key release log((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask)); - m_primaryReceiver->onKeyUp(key, mask); + m_receiver->onKeyUp(key, mask); // update key state updateKey(msg->wParam, false); @@ -428,7 +222,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) case WM_RBUTTONDOWN: log((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { - m_primaryReceiver->onMouseDown(button); + m_receiver->onMouseDown(button); m_keys[s_vkButton[button]] |= 0x80; } break; @@ -438,7 +232,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) case WM_RBUTTONUP: log((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { - m_primaryReceiver->onMouseUp(button); + m_receiver->onMouseUp(button); m_keys[s_vkButton[button]] &= ~0x80; } break; @@ -450,7 +244,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) // ignore message if posted prior to last mark change if (m_markReceived == m_mark) { log((CLOG_ERR "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); - m_primaryReceiver->onMouseWheel(msg->wParam); + m_receiver->onMouseWheel(msg->wParam); } return true; @@ -459,8 +253,8 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) if (m_markReceived == m_mark) { SInt32 x = static_cast(msg->wParam); SInt32 y = static_cast(msg->lParam); - if (!m_active) { - m_primaryReceiver->onMouseMovePrimary(x, y); + if (!isActive()) { + m_receiver->onMouseMovePrimary(x, y); } else { // compute motion delta. this is relative to the @@ -480,7 +274,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) warpCursorToCenter(); // send motion - m_primaryReceiver->onMouseMoveSecondary(x, y); + m_receiver->onMouseMoveSecondary(x, y); } } else { @@ -495,32 +289,6 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) m_x = static_cast(msg->wParam); m_y = static_cast(msg->lParam); return true; - - case SYNERGY_MSG_SCREEN_SAVER: - if (msg->wParam != 0) { - if (getScreenSaver()->checkStarted(msg->message, FALSE, 0)) { - m_primaryReceiver->onScreenSaver(true); - } - } - else { - m_primaryReceiver->onScreenSaver(false); - } - return true; - - case WM_TIMER: - // if current desktop is not the input desktop then switch to it - if (!m_is95Family) { - HDESK desk = openInputDesktop(); - if (desk != NULL) { - if (isCurrentDesktop(desk)) { - CloseDesktop(desk); - } - else { - switchDesktop(desk); - } - } - } - return true; } return false; @@ -531,111 +299,29 @@ CMSWindowsPrimaryScreen::onEvent(CEvent* event) { assert(event != NULL); - const MSG& msg = event->msg; + const MSG& msg = event->m_msg; switch (msg.message) { - case WM_QUERYENDSESSION: - if (m_is95Family) { - event->m_result = TRUE; - return true; - } - break; - - case WM_ENDSESSION: - if (m_is95Family) { - if (msg.wParam == TRUE && msg.lParam == 0) { - stop(); - } - return true; - } - break; - - case WM_PAINT: - ValidateRect(msg.hwnd, NULL); - return true; - - case WM_DRAWCLIPBOARD: - log((CLOG_DEBUG "clipboard was taken")); - - // first pass it on - if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, - msg.message, msg.wParam, msg.lParam); - } - - // now notify server that somebody changed the clipboard. - // skip that if we're the new owner. - try { - m_clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore. this can happen if we receive this event - // before we've fully started up. - } - return true; - - case WM_CHANGECBCHAIN: - if (m_nextClipboardWindow == (HWND)msg.wParam) { - m_nextClipboardWindow = (HWND)msg.lParam; - } - else if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, - msg.message, msg.wParam, msg.lParam); - } - return true; - case WM_DISPLAYCHANGE: - { - // screen resolution may have changed. get old shape. - SInt32 xOld, yOld, wOld, hOld; - getScreenShape(xOld, yOld, wOld, hOld); + // recompute center pixel of primary screen + m_screen->getCursorCenter(m_xCenter, m_yCenter); - // update shape - updateScreenShape(); - - // collect new screen info - CClientInfo info; - getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); - getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getJumpZoneSize(); - - // do nothing if resolution hasn't changed - if (info.m_x != xOld || info.m_y != yOld || - info.m_w != wOld || info.m_h != hOld) { - // recompute center pixel of primary screen - getCursorCenter(m_xCenter, m_yCenter); - - // warp mouse to center if active - if (m_active) { - warpCursorToCenter(); - } - - // tell hook about resize if not active - else { - m_setZone(info.m_x, info.m_y, - info.m_w, info.m_h, info.m_zoneSize); - } - - // send new screen info - m_receiver->onInfoChanged(info); - } - - return true; + // warp mouse to center if active + if (isActive()) { + warpCursorToCenter(); } + + // tell hook about resize if not active + else { + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + m_setZone(x, y, w, h, getJumpZoneSize()); + } + return true; } return false; } -CString -CMSWindowsPrimaryScreen::getCurrentDesktopName() const -{ - return m_deskName; -} - SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const { @@ -643,25 +329,96 @@ CMSWindowsPrimaryScreen::getJumpZoneSize() const } void -CMSWindowsPrimaryScreen::warpCursorToCenter() +CMSWindowsPrimaryScreen::postCreateWindow(HWND window) { - // warp to center. the extra info tells the hook DLL to send - // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. + // save window + m_window = window; + + // install hooks + m_install(); + + // resize window + // note -- we use a fullscreen window to grab input. it should + // be possible to use a 1x1 window but i've run into problems + // with losing keyboard input (focus?) in that case. + // unfortunately, hiding the full screen window (when entering + // the screen) causes all other windows to redraw. SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, - (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), - (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), - 0, - 0x12345678); -// FIXME -- ignore mouse until we get warp notification? + m_screen->getShape(x, y, w, h); + MoveWindow(m_window, x, y, w, h, FALSE); + + if (isActive()) { + // hide the cursor + showWindow(); + } + else { + // watch jump zones + m_setRelay(false); + + // all messages prior to now are invalid + nextMark(); + } } void -CMSWindowsPrimaryScreen::enterNoWarp() +CMSWindowsPrimaryScreen::preDestroyWindow(HWND) { - // not active anymore - m_active = false; + // hide the window if it's visible + if (isActive()) { + hideWindow(); + } + + // uninstall hooks + m_uninstall(); +} + +void +CMSWindowsPrimaryScreen::onPreRun() +{ + // must call run() from same thread as open() + assert(m_threadID == GetCurrentThreadId()); + assert(m_window != NULL); +} + +void +CMSWindowsPrimaryScreen::onPreOpen() +{ + assert(m_window == NULL); + + // initialize hook library + m_threadID = GetCurrentThreadId(); + m_init(m_threadID); +} + +void +CMSWindowsPrimaryScreen::onPostOpen() +{ + // get cursor info + m_screen->getCursorPos(m_x, m_y); + m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // set jump zones + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + m_setZone(x, y, w, h, getJumpZoneSize()); + + // initialize marks + m_mark = 0; + m_markReceived = 0; + nextMark(); +} + +void +CMSWindowsPrimaryScreen::onPostClose() +{ + m_cleanup(); + m_threadID = 0; +} + +void +CMSWindowsPrimaryScreen::onPreEnter() +{ + assert(m_window != NULL); // reset motion ignore count m_mouseMoveIgnore = 0; @@ -674,14 +431,78 @@ CMSWindowsPrimaryScreen::enterNoWarp() // watch jump zones m_setRelay(false); +} - // restore active window and hide our window - hideWindow(); +void +CMSWindowsPrimaryScreen::onPostEnter() +{ + // all messages prior to now are invalid + nextMark(); +} + +void +CMSWindowsPrimaryScreen::onPreLeave() +{ + assert(m_window != NULL); // all messages prior to now are invalid nextMark(); } +void +CMSWindowsPrimaryScreen::onPostLeave(bool success) +{ + if (success) { + // relay all mouse and keyboard events + m_setRelay(true); + + // ignore this many mouse motion events (not including the already + // queued events). on (at least) the win2k login desktop, one + // motion event is reported using a position from before the above + // warpCursor(). i don't know why it does that and other desktops + // don't have the same problem. anyway, simply ignoring that event + // works around it. +// FIXME -- is this true now that we're using mouse_event? + m_mouseMoveIgnore = 1; + + // disable ctrl+alt+del, alt+tab, etc + if (m_is95Family) { + DWORD dummy = 0; + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); + } + + // discard messages until after the warp + nextMark(); + } +} + +void +CMSWindowsPrimaryScreen::createWindow() +{ + // open the desktop and the window + m_window = m_screen->openDesktop(); + if (m_window == NULL) { + throw XScreenOpenFailure(); + } + + // note -- we use a fullscreen window to grab input. it should + // be possible to use a 1x1 window but i've run into problems + // with losing keyboard input (focus?) in that case. + // unfortunately, hiding the full screen window (when entering + // the scren causes all other windows to redraw). + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + MoveWindow(m_window, x, y, w, h, FALSE); +} + +void +CMSWindowsPrimaryScreen::destroyWindow() +{ + // close the desktop and the window + m_screen->closeDesktop(); + m_window = NULL; +} + bool CMSWindowsPrimaryScreen::showWindow() { @@ -733,262 +554,18 @@ CMSWindowsPrimaryScreen::hideWindow() } void -CMSWindowsPrimaryScreen::checkClipboard() +CMSWindowsPrimaryScreen::warpCursorToCenter() { - // if we think we own the clipboard but we don't then somebody - // grabbed the clipboard on this screen without us knowing. - // tell the server that this screen grabbed the clipboard. - // - // this works around bugs in the clipboard viewer chain. - // sometimes NT will simply never send WM_DRAWCLIPBOARD - // messages for no apparent reason and rebooting fixes the - // problem. since we don't want a broken clipboard until the - // next reboot we do this double check. clipboard ownership - // won't be reflected on other screens until we leave but at - // least the clipboard itself will work. - HWND clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != clipboardOwner) { - try { - m_clipboardOwner = clipboardOwner; - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore - } - } -} - -void -CMSWindowsPrimaryScreen::createWindow() -{ - // get the input desktop and switch to it - if (m_is95Family) { - if (!openDesktop()) { - throw XScreenOpenFailure(); - } - } - else { - if (!switchDesktop(openInputDesktop())) { - throw XScreenOpenFailure(); - } - } - - // poll input desktop to see if it changes (preTranslateMessage() - // handles WM_TIMER) - m_timer = 0; - if (!m_is95Family) { - m_timer = SetTimer(NULL, 0, 200, NULL); - } -} - -void -CMSWindowsPrimaryScreen::destroyWindow() -{ - // remove timer - if (m_timer != 0) { - KillTimer(NULL, m_timer); - } - - // disconnect from desktop - if (m_is95Family) { - closeDesktop(); - } - else { - switchDesktop(NULL); - } - - assert(m_window == NULL); - assert(m_desk == NULL); -} - -void -CMSWindowsPrimaryScreen::installScreenSaver() -{ - // install the screen saver hook - if (m_installScreenSaver != NULL) { - m_installScreenSaver(); - } -} - -void -CMSWindowsPrimaryScreen::uninstallScreenSaver() -{ - // uninstall the screen saver hook - if (m_uninstallScreenSaver != NULL) { - m_uninstallScreenSaver(); - } -} - -bool -CMSWindowsPrimaryScreen::openDesktop() -{ - // install hooks - m_install(); - - // note -- we use a fullscreen window to grab input. it should - // be possible to use a 1x1 window but i've run into problems - // with losing keyboard input (focus?) in that case. - // unfortunately, hiding the full screen window (when entering - // the scren causes all other windows to redraw). + // warp to center. the extra info tells the hook DLL to send + // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - - // create the window - m_window = CreateWindowEx(WS_EX_TOPMOST | - WS_EX_TRANSPARENT | - WS_EX_TOOLWINDOW, - (LPCTSTR)getClass(), - "Synergy", - WS_POPUP, - x, y, w, h, NULL, NULL, - getInstance(), - NULL); - if (m_window == NULL) { - log((CLOG_ERR "failed to create window: %d", GetLastError())); - m_uninstall(); - return false; - } - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - return true; -} - -void -CMSWindowsPrimaryScreen::closeDesktop() -{ - // destroy old window - if (m_window != NULL) { - // restore active window and hide ours - if (m_active) { - hideWindow(); - } - - // first remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // we no longer own the clipboard - if (m_clipboardOwner == m_window) { - m_clipboardOwner = NULL; - } - - // now destroy window - DestroyWindow(m_window); - m_window = NULL; - } - - // unhook - m_uninstall(); -} - -bool -CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) -{ - // did we own the clipboard? - bool ownClipboard = (m_clipboardOwner == m_window); - - // destroy old window - if (m_window != NULL) { - // restore active window and hide ours - if (m_active) { - hideWindow(); - } - - // first remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // we no longer own the clipboard - if (ownClipboard) { - m_clipboardOwner = NULL; - } - - // now destroy window - DestroyWindow(m_window); - m_window = NULL; - } - - // unhook - if (m_desk != NULL) { - m_uninstall(); - CloseDesktop(m_desk); - m_desk = NULL; - m_deskName = ""; - } - - // if no new desktop then we're done - if (desk == NULL) { - log((CLOG_INFO "disconnecting desktop")); - return true; - } - - // set the desktop. can only do this when there are no windows - // and hooks on the current desktop owned by this thread. - if (SetThreadDesktop(desk) == 0) { - log((CLOG_ERR "failed to set desktop: %d", GetLastError())); - CloseDesktop(desk); - return false; - } - - // install hooks - m_install(); - - // note -- we use a fullscreen window to grab input. it should - // be possible to use a 1x1 window but i've run into problems - // with losing keyboard input (focus?) in that case. - // unfortunately, hiding the full screen window (when entering - // the scren causes all other windows to redraw). - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - - // create the window - m_window = CreateWindowEx(WS_EX_TOPMOST | - WS_EX_TRANSPARENT | - WS_EX_TOOLWINDOW, - (LPCTSTR)getClass(), - "Synergy", - WS_POPUP, - x, y, w, h, NULL, NULL, - getInstance(), - NULL); - if (m_window == NULL) { - log((CLOG_ERR "failed to create window: %d", GetLastError())); - m_uninstall(); - CloseDesktop(desk); - return false; - } - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - // reassert clipboard ownership - if (ownClipboard) { - // FIXME - } - - // save new desktop - m_desk = desk; - m_deskName = getDesktopName(m_desk); - log((CLOG_INFO "switched to desktop %s", m_deskName.c_str())); - - // get active window and show ours - if (m_active) { - showWindow(); - } - else { - // watch jump zones - m_setRelay(false); - - // all messages prior to now are invalid - nextMark(); - } - - return true; + m_screen->getShape(x, y, w, h); + mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, + (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), + (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), + 0, + 0x12345678); +// FIXME -- ignore mouse until we get warp notification? } void diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 7856dccb..5414d633 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -1,74 +1,61 @@ #ifndef CMSWINDOWSPRIMARYSCREEN_H #define CMSWINDOWSPRIMARYSCREEN_H -#include "CMSWindowsScreen.h" -#include "IPrimaryScreen.h" +#include "CPrimaryScreen.h" +#include "IMSWindowsScreenEventHandler.h" #include "CSynergyHook.h" #include "MouseTypes.h" #include "CString.h" +class CMSWindowsScreen; class IScreenReceiver; class IPrimaryScreenReceiver; -class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { +class CMSWindowsPrimaryScreen : + public CPrimaryScreen, public IMSWindowsScreenEventHandler { public: typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM); CMSWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); virtual ~CMSWindowsPrimaryScreen(); - // IPrimaryScreen overrides - virtual void run(); - virtual void stop(); - virtual void open(); - virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool); - virtual bool leave(); + // CPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); - virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void setClipboard(ClipboardID, const IClipboard*); - virtual void grabClipboard(ClipboardID); - virtual void getClipboard(ClipboardID, IClipboard*) const; + virtual void warpCursor(SInt32 x, SInt32 y); virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; - -protected: - // CMSWindowsScreen overrides - virtual bool onPreTranslate(const CEvent* event); + virtual IScreen* getScreen() const; + + // IMSWindowsScreenEventHandler overrides + virtual void onError(); + virtual void onScreensaver(bool activated); + virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); - virtual CString getCurrentDesktopName() const; + virtual SInt32 getJumpZoneSize() const; + virtual void postCreateWindow(HWND); + virtual void preDestroyWindow(HWND); + +protected: + // CPrimaryScreen overrides + virtual void onPreRun(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPostClose(); + virtual void onPreEnter(); + virtual void onPostEnter(); + virtual void onPreLeave(); + virtual void onPostLeave(bool); + + virtual void createWindow(); + virtual void destroyWindow(); + virtual bool showWindow(); + virtual void hideWindow(); + virtual void warpCursorToCenter(); + + virtual void updateKeys(); private: - SInt32 getJumpZoneSize() const; - - // warp mouse to center of primary display (used when computing - // motion deltas while mouse is on secondary screen). - void warpCursorToCenter(); - void enterNoWarp(); - bool showWindow(); - void hideWindow(); - - // check clipboard ownership and, if necessary, tell the receiver - // of a grab. - void checkClipboard(); - - // create/destroy window - // also attach to desktop; this destroys and recreates the window - // as necessary. - void createWindow(); - void destroyWindow(); - - // start/stop watch for screen saver changes - void installScreenSaver(); - void uninstallScreenSaver(); - - // open/close desktop (for windows 95/98/me) - bool openDesktop(); - void closeDesktop(); - - // make desk the thread desktop (for windows NT/2000/XP) - bool switchDesktop(HDESK desk); // discard posted messages void nextMark(); @@ -77,12 +64,11 @@ private: KeyID mapKey(WPARAM keycode, LPARAM info, KeyModifierMask* maskOut); ButtonID mapButton(WPARAM button) const; - void updateKeys(); void updateKey(UINT vkCode, bool press); private: - IScreenReceiver* m_receiver; - IPrimaryScreenReceiver* m_primaryReceiver; + IPrimaryScreenReceiver* m_receiver; + CMSWindowsScreen* m_screen; // true if windows 95/98/me bool m_is95Family; @@ -90,27 +76,13 @@ private: // the main loop's thread id DWORD m_threadID; - // the timer used to check for desktop switching - UINT m_timer; - - // the current desk and it's name - HDESK m_desk; - CString m_deskName; - - // our window (for getting clipboard changes) + // our window HWND m_window; - // m_active is true the hooks are relaying events - bool m_active; - // used to discard queued messages that are no longer needed UInt32 m_mark; UInt32 m_markReceived; - // clipboard stuff - HWND m_nextClipboardWindow; - HWND m_clipboardOwner; - // map of key state BYTE m_keys[256]; @@ -132,8 +104,6 @@ private: SetSidesFunc m_setSides; SetZoneFunc m_setZone; SetRelayFunc m_setRelay; - InstallScreenSaverFunc m_installScreenSaver; - UninstallScreenSaverFunc m_uninstallScreenSaver; // stuff for restoring active window HWND m_lastForegroundWindow; diff --git a/server/CPrimaryScreen.cpp b/server/CPrimaryScreen.cpp index 5dafa752..a649a227 100644 --- a/server/CPrimaryScreen.cpp +++ b/server/CPrimaryScreen.cpp @@ -73,7 +73,7 @@ CPrimaryScreen::open() updateKeys(); // get notified of screen saver activation/deactivation - getScreen()->openScreenSaver(true); + getScreen()->openScreensaver(true); // subclass hook onPostOpen(); @@ -94,7 +94,7 @@ void CPrimaryScreen::close() { onPreClose(); - getScreen()->closeScreenSaver(); + getScreen()->closeScreensaver(); destroyWindow(); getScreen()->close(); onPostClose(); @@ -197,12 +197,6 @@ CPrimaryScreen::getClipboard(ClipboardID id, getScreen()->getClipboard(id, clipboard); } -SInt32 -CPrimaryScreen::getJumpZoneSize() const -{ - return 1; -} - void CPrimaryScreen::onPreRun() { diff --git a/server/CPrimaryScreen.h b/server/CPrimaryScreen.h index 06b020dd..9040222d 100644 --- a/server/CPrimaryScreen.h +++ b/server/CPrimaryScreen.h @@ -42,8 +42,8 @@ public: bool leave(); // called when the configuration has changed. activeSides is a - // bitmask of CConfig::EDirectionMask indicating which sides of - // the primary screen are linked to clients. + // bitmask of EDirectionMask indicating which sides of the + // primary screen are linked to clients. virtual void reconfigure(UInt32 activeSides) = 0; // warp the cursor to the given absolute coordinates @@ -66,8 +66,8 @@ public: void getClipboard(ClipboardID, IClipboard*) const; // returns the size of the zone on the edges of the screen that - // causes the cursor to jump to another screen. default returns 1. - virtual SInt32 getJumpZoneSize() const; + // causes the cursor to jump to another screen. + virtual SInt32 getJumpZoneSize() const = 0; // get the primary screen's current toggle modifier key state. // the returned mask should have the corresponding bit set for diff --git a/server/CServer.cpp b/server/CServer.cpp index f13af415..1491f6c1 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -197,21 +197,17 @@ CServer::getActivePrimarySides() const { // note -- m_mutex must be locked on entry UInt32 sides = 0; - if (!m_config.getNeighbor(getPrimaryScreenName(), - CConfig::kLeft).empty()) { - sides |= CConfig::kLeftMask; + if (!m_config.getNeighbor(getPrimaryScreenName(), kLeft).empty()) { + sides |= kLeftMask; } - if (!m_config.getNeighbor(getPrimaryScreenName(), - CConfig::kRight).empty()) { - sides |= CConfig::kRightMask; + if (!m_config.getNeighbor(getPrimaryScreenName(), kRight).empty()) { + sides |= kRightMask; } - if (!m_config.getNeighbor(getPrimaryScreenName(), - CConfig::kTop).empty()) { - sides |= CConfig::kTopMask; + if (!m_config.getNeighbor(getPrimaryScreenName(), kTop).empty()) { + sides |= kTopMask; } - if (!m_config.getNeighbor(getPrimaryScreenName(), - CConfig::kBottom).empty()) { - sides |= CConfig::kBottomMask; + if (!m_config.getNeighbor(getPrimaryScreenName(), kBottom).empty()) { + sides |= kBottomMask; } return sides; } @@ -505,25 +501,25 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) SInt32 zoneSize = m_active->getJumpZoneSize(); // see if we should change screens - CConfig::EDirection dir; + EDirection dir; if (x < ax + zoneSize) { x -= zoneSize; - dir = CConfig::kLeft; + dir = kLeft; log((CLOG_DEBUG1 "switch to left")); } else if (x >= ax + aw - zoneSize) { x += zoneSize; - dir = CConfig::kRight; + dir = kRight; log((CLOG_DEBUG1 "switch to right")); } else if (y < ay + zoneSize) { y -= zoneSize; - dir = CConfig::kTop; + dir = kTop; log((CLOG_DEBUG1 "switch to top")); } else if (y >= ay + ah - zoneSize) { y += zoneSize; - dir = CConfig::kBottom; + dir = kBottom; log((CLOG_DEBUG1 "switch to bottom")); } else { @@ -584,24 +580,24 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) IClient* newScreen = NULL; if (!isLockedToScreenNoLock()) { // find direction of neighbor - CConfig::EDirection dir; + EDirection dir; if (m_x < ax) { - dir = CConfig::kLeft; + dir = kLeft; } else if (m_x > ax + aw - 1) { - dir = CConfig::kRight; + dir = kRight; } else if (m_y < ay) { - dir = CConfig::kTop; + dir = kTop; } else if (m_y > ay + ah - 1) { - dir = CConfig::kBottom; + dir = kBottom; } else { newScreen = m_active; // keep compiler quiet about unset variable - dir = CConfig::kLeft; + dir = kLeft; } // get neighbor if we should switch @@ -760,7 +756,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) } IClient* -CServer::getNeighbor(IClient* src, CConfig::EDirection dir) const +CServer::getNeighbor(IClient* src, EDirection dir) const { assert(src != NULL); @@ -793,7 +789,7 @@ CServer::getNeighbor(IClient* src, CConfig::EDirection dir) const IClient* CServer::getNeighbor(IClient* src, - CConfig::EDirection srcSide, SInt32& x, SInt32& y) const + EDirection srcSide, SInt32& x, SInt32& y) const { assert(src != NULL); @@ -816,7 +812,7 @@ CServer::getNeighbor(IClient* src, // actual to canonical position on entry to and from canonical to // actual on exit from the search. switch (srcSide) { - case CConfig::kLeft: + case kLeft: x -= dx; while (dst != NULL && dst != lastGoodScreen) { lastGoodScreen = dst; @@ -832,7 +828,7 @@ CServer::getNeighbor(IClient* src, x += dx; break; - case CConfig::kRight: + case kRight: x -= dx; while (dst != NULL) { x -= dw; @@ -848,7 +844,7 @@ CServer::getNeighbor(IClient* src, x += dx; break; - case CConfig::kTop: + case kTop: y -= dy; while (dst != NULL) { lastGoodScreen = dst; @@ -864,7 +860,7 @@ CServer::getNeighbor(IClient* src, y += dy; break; - case CConfig::kBottom: + case kBottom: y -= dy; while (dst != NULL) { y -= dh; @@ -892,26 +888,26 @@ CServer::getNeighbor(IClient* src, if (dst == m_primaryClient) { const CString dstName(dst->getName()); switch (srcSide) { - case CConfig::kLeft: - if (!m_config.getNeighbor(dstName, CConfig::kRight).empty() && + case kLeft: + if (!m_config.getNeighbor(dstName, kRight).empty() && x > dx + dw - 1 - dst->getJumpZoneSize()) x = dx + dw - 1 - dst->getJumpZoneSize(); break; - case CConfig::kRight: - if (!m_config.getNeighbor(dstName, CConfig::kLeft).empty() && + case kRight: + if (!m_config.getNeighbor(dstName, kLeft).empty() && x < dx + dst->getJumpZoneSize()) x = dx + dst->getJumpZoneSize(); break; - case CConfig::kTop: - if (!m_config.getNeighbor(dstName, CConfig::kBottom).empty() && + case kTop: + if (!m_config.getNeighbor(dstName, kBottom).empty() && y > dy + dh - 1 - dst->getJumpZoneSize()) y = dy + dh - 1 - dst->getJumpZoneSize(); break; - case CConfig::kBottom: - if (!m_config.getNeighbor(dstName, CConfig::kTop).empty() && + case kBottom: + if (!m_config.getNeighbor(dstName, kTop).empty() && y < dy + dst->getJumpZoneSize()) y = dy + dst->getJumpZoneSize(); break; @@ -925,8 +921,8 @@ CServer::getNeighbor(IClient* src, // should be set 120 pixels from the top (again 20% from the // top). switch (srcSide) { - case CConfig::kLeft: - case CConfig::kRight: + case kLeft: + case kRight: y -= sy; if (y < 0) { y = 0; @@ -941,8 +937,8 @@ CServer::getNeighbor(IClient* src, y += dy; break; - case CConfig::kTop: - case CConfig::kBottom: + case kTop: + case kBottom: x -= sx; if (x < 0) { x = 0; diff --git a/server/CServer.h b/server/CServer.h index 7ddb4956..b8d49038 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -96,15 +96,14 @@ private: SInt32 x, SInt32 y, bool forScreenSaver); // lookup neighboring screen - IClient* getNeighbor(IClient*, CConfig::EDirection) const; + IClient* getNeighbor(IClient*, EDirection) const; // lookup neighboring screen. given a position relative to the // source screen, find the screen we should move onto and where. // if the position is sufficiently far from the source then we // cross multiple screens. if there is no suitable screen then // return NULL and x,y are not modified. - IClient* getNeighbor(IClient*, - CConfig::EDirection, + IClient* getNeighbor(IClient*, EDirection, SInt32& x, SInt32& y) const; // open/close the primary screen diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index dff594c8..1f7fa7b1 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -331,6 +331,12 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) return false; } +SInt32 +CXWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + void CXWindowsPrimaryScreen::onPreRun() { @@ -419,7 +425,7 @@ CXWindowsPrimaryScreen::createWindow() selectEvents(display, m_screen->getRoot()); } - // tell our superclass about the window + // tell generic screen about the window m_screen->setWindow(m_window); } diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 6b261400..2a97a51c 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -32,6 +32,7 @@ public: virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); + virtual SInt32 getJumpZoneSize() const; protected: // CPrimaryScreen overrides diff --git a/server/server.dsp b/server/server.dsp index 7586b091..61acba15 100644 --- a/server/server.dsp +++ b/server/server.dsp @@ -118,6 +118,10 @@ SOURCE=.\CPrimaryClient.cpp # End Source File # Begin Source File +SOURCE=.\CPrimaryScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\CServer.cpp # End Source File # Begin Source File @@ -158,11 +162,11 @@ SOURCE=.\CPrimaryClient.h # End Source File # Begin Source File -SOURCE=.\CServer.h +SOURCE=.\CPrimaryScreen.h # End Source File # Begin Source File -SOURCE=.\IPrimaryScreen.h +SOURCE=.\CServer.h # End Source File # Begin Source File diff --git a/synergy/IScreen.h b/synergy/IScreen.h index 88697926..861f69c5 100644 --- a/synergy/IScreen.h +++ b/synergy/IScreen.h @@ -37,8 +37,8 @@ public: // will call IScreenEventHandler's onScreenSaver() when the screensaver // activates or deactivates until close. if notify is false then // the screen saver is disabled on open and restored on close. - virtual void openScreenSaver(bool notify) = 0; - virtual void closeScreenSaver() = 0; + virtual void openScreensaver(bool notify) = 0; + virtual void closeScreensaver() = 0; // activate or deactivate the screen saver virtual void screensaver(bool activate) = 0; diff --git a/synergy/IScreenEventHandler.h b/synergy/IScreenEventHandler.h index d4e04ab3..123956be 100644 --- a/synergy/IScreenEventHandler.h +++ b/synergy/IScreenEventHandler.h @@ -34,6 +34,11 @@ public: // called by mainLoop(). iff the event was handled return true and // store the result, if any, in m_result, which defaults to zero. virtual bool onEvent(CEvent* event) = 0; + + // accessors + + // called to get the jump zone size + virtual SInt32 getJumpZoneSize() const = 0; }; #endif diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index a47f7d73..391c8ea3 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -16,6 +16,23 @@ static const double kHeartRate = 2.0; // time without a heartbeat that we call death static const double kHeartDeath = 3.0 * kHeartRate; +// direction constants +enum EDirection { + kLeft, + kRight, + kTop, + kBottom, + kFirstDirection = kLeft, + kLastDirection = kBottom +}; +enum EDirectionMask { + kLeftMask = 1 << kLeft, + kRightMask = 1 << kRight, + kTopMask = 1 << kTop, + kBottomMask = 1 << kBottom +}; + + // // message codes (trailing NUL is not part of code). in comments, $n // refers to the n'th argument (counting from one). message codes are diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp index f7d26af0..eac75dde 100644 --- a/synergy/synergy.dsp +++ b/synergy/synergy.dsp @@ -155,6 +155,14 @@ SOURCE=.\IPrimaryScreenReceiver.h # End Source File # Begin Source File +SOURCE=.\IScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenEventHandler.h +# End Source File +# Begin Source File + SOURCE=.\IScreenReceiver.h # End Source File # Begin Source File From 0bfe12d6ab9cda00e91c644e0e72ad8103255b14 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 15 Jul 2002 15:03:04 +0000 Subject: [PATCH 230/807] completing previous checkin. --- platform/CSynergyHook.cpp | 32 ++++++++++++++++---------------- platform/synrgyhk.dsp | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/platform/CSynergyHook.cpp b/platform/CSynergyHook.cpp index 9bc1f3a7..83857454 100644 --- a/platform/CSynergyHook.cpp +++ b/platform/CSynergyHook.cpp @@ -1,5 +1,5 @@ #include "CSynergyHook.h" -#include "CConfig.h" +#include "ProtocolTypes.h" #include // @@ -205,16 +205,16 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; SInt32 x = (SInt32)info->pt.x; SInt32 y = (SInt32)info->pt.y; - if (!inside && (g_zoneSides & CConfig::kLeftMask) != 0) { + if (!inside && (g_zoneSides & kLeftMask) != 0) { inside = (x < g_xScreen + g_zoneSize); } - if (!inside && (g_zoneSides & CConfig::kRightMask) != 0) { + if (!inside && (g_zoneSides & kRightMask) != 0) { inside = (x >= g_xScreen + g_wScreen - g_zoneSize); } - if (!inside && (g_zoneSides & CConfig::kTopMask) != 0) { + if (!inside && (g_zoneSides & kTopMask) != 0) { inside = (y < g_yScreen + g_zoneSize); } - if (!inside && (g_zoneSides & CConfig::kBottomMask) != 0) { + if (!inside && (g_zoneSides & kBottomMask) != 0) { inside = (y >= g_yScreen + g_hScreen - g_zoneSize); } @@ -481,6 +481,17 @@ init(DWORD threadID) // message queue. g_threadID = threadID; + // set defaults + g_relay = false; + g_zoneSides = 0; + g_zoneSize = 0; + g_xScreen = 0; + g_yScreen = 0; + g_wScreen = 0; + g_hScreen = 0; + g_cursor = NULL; + g_cursorThread = 0; + return 1; } @@ -508,17 +519,6 @@ install() return 0; } - // set defaults - g_relay = false; - g_zoneSides = 0; - g_zoneSize = 0; - g_xScreen = 0; - g_yScreen = 0; - g_wScreen = 0; - g_hScreen = 0; - g_cursor = NULL; - g_cursorThread = 0; - // check for mouse wheel support g_wheelSupport = getWheelSupport(); diff --git a/platform/synrgyhk.dsp b/platform/synrgyhk.dsp index 1f04aabb..42dbb32e 100644 --- a/platform/synrgyhk.dsp +++ b/platform/synrgyhk.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Intermediate_Dir "ReleaseHook" # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -68,7 +68,7 @@ LINK32=link.exe # PROP Intermediate_Dir "DebugHook" # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\synergy" /I "..\base" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 From 7c391a0f3523e61f88a67a369e6d80eb15953195 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 16 Jul 2002 16:52:26 +0000 Subject: [PATCH 231/807] moved onError() method to IScreenReceiver from IPrimaryScreenReceiver. also implemented onError in CClient which previously did not have any way to handle display disconnection. --- client/CClient.cpp | 34 ++++++++++++++++++++----- client/CClient.h | 5 ++-- client/CMSWindowsSecondaryScreen.cpp | 6 ----- client/CMSWindowsSecondaryScreen.h | 1 - client/CSecondaryScreen.cpp | 19 ++++++++------ client/CServerProxy.cpp | 6 +++++ client/CServerProxy.h | 1 + client/CXWindowsSecondaryScreen.cpp | 25 ++++++++---------- client/CXWindowsSecondaryScreen.h | 1 - platform/CXWindowsScreen.cpp | 2 +- platform/IMSWindowsScreenEventHandler.h | 1 - server/CMSWindowsPrimaryScreen.cpp | 6 ----- server/CMSWindowsPrimaryScreen.h | 1 - server/CPrimaryClient.cpp | 15 ++++++----- server/CPrimaryClient.h | 2 +- server/CPrimaryScreen.cpp | 13 +++++++--- server/CPrimaryScreen.h | 4 ++- server/CServer.cpp | 28 ++++++++++---------- server/CServer.h | 2 +- server/CXWindowsPrimaryScreen.cpp | 7 ----- server/CXWindowsPrimaryScreen.h | 1 - synergy/IPrimaryScreenReceiver.h | 7 ----- synergy/IScreen.h | 10 +++++--- synergy/IScreenEventHandler.h | 5 ---- synergy/IScreenReceiver.h | 5 ++++ synergy/IServer.h | 5 ++++ 26 files changed, 114 insertions(+), 98 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index ab221a7e..69e6026c 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -27,6 +27,7 @@ CClient::CClient(const CString& clientName) : m_screen(NULL), m_server(NULL), m_camp(false), + m_session(NULL), m_active(false), m_rejected(true) { @@ -64,6 +65,13 @@ CClient::wasRejected() const return m_rejected; } +void +CClient::onError() +{ + // close down session but don't wait too long + deleteSession(3.0); +} + void CClient::onInfoChanged(const CClientInfo& info) { @@ -144,28 +152,31 @@ CClient::run() log((CLOG_NOTE "starting client \"%s\"", m_name.c_str())); // start server interactions - thread = new CThread(new TMethodJob( + { + CLock lock(&m_mutex); + m_session = new CThread(new TMethodJob( this, &CClient::runSession)); + } // handle events m_screen->run(); // clean up - deleteSession(thread); + deleteSession(); log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); } catch (XBase& e) { log((CLOG_ERR "client error: %s", e.what())); // clean up - deleteSession(thread); + deleteSession(); log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); CLock lock(&m_mutex); m_rejected = false; } catch (XThread&) { // clean up - deleteSession(thread); + deleteSession(); log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; } @@ -173,7 +184,7 @@ CClient::run() log((CLOG_DEBUG "unknown client error")); // clean up - deleteSession(thread); + deleteSession(); log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; } @@ -424,11 +435,20 @@ CClient::runSession(void*) } void -CClient::deleteSession(CThread* thread) +CClient::deleteSession(double timeout) { + // get session thread object + CThread* thread; + { + CLock lock(&m_mutex); + thread = m_session; + m_session = NULL; + } + + // shut it down if (thread != NULL) { thread->cancel(); - thread->wait(); + thread->wait(timeout); delete thread; } } diff --git a/client/CClient.h b/client/CClient.h index c555d87c..2e8e2256 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -39,6 +39,7 @@ public: bool wasRejected() const; // IScreenReceiver overrides + virtual void onError(); virtual void onInfoChanged(const CClientInfo&); virtual bool onGrabClipboard(ClipboardID); virtual void onClipboardChanged(ClipboardID, const CString&); @@ -47,7 +48,6 @@ public: virtual bool open(); virtual void run(); virtual void close(); -// FIXME -- can we avoid passing everything here? virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, bool forScreensaver); @@ -80,7 +80,7 @@ private: // handle server messaging void runSession(void*); - void deleteSession(CThread*); + void deleteSession(double timeout = -1.0); void runServer(); CServerProxy* handshakeServer(IDataSocket*); @@ -91,6 +91,7 @@ private: IScreenReceiver* m_server; CNetworkAddress m_serverAddress; bool m_camp; + CThread* m_session; bool m_active; bool m_rejected; bool m_ownClipboard[kClipboardEnd]; diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 78b514f4..3c93ac0c 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -206,12 +206,6 @@ CMSWindowsSecondaryScreen::getScreen() const return m_screen; } -void -CMSWindowsSecondaryScreen::onError() -{ - // ignore -} - void CMSWindowsSecondaryScreen::onScreensaver(bool) { diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index e9b956c7..b98a4251 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -33,7 +33,6 @@ public: virtual IScreen* getScreen() const; // IMSWindowsScreenEventHandler overrides - virtual void onError(); virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); diff --git a/client/CSecondaryScreen.cpp b/client/CSecondaryScreen.cpp index a19bc4e4..9452ffae 100644 --- a/client/CSecondaryScreen.cpp +++ b/client/CSecondaryScreen.cpp @@ -18,13 +18,6 @@ CSecondaryScreen::~CSecondaryScreen() // do nothing } -bool -CSecondaryScreen::isActive() const -{ - CLock lock(&m_mutex); - return m_active; -} - void CSecondaryScreen::run() { @@ -85,7 +78,10 @@ CSecondaryScreen::open() } // hide the cursor - m_active = true; + { + CLock lock(&m_mutex); + m_active = true; + } leave(); } @@ -175,6 +171,13 @@ CSecondaryScreen::screensaver(bool activate) getScreen()->screensaver(activate); } +bool +CSecondaryScreen::isActive() const +{ + CLock lock(&m_mutex); + return m_active; +} + void CSecondaryScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const diff --git a/client/CServerProxy.cpp b/client/CServerProxy.cpp index 2aa59a4a..b8a9a3b3 100644 --- a/client/CServerProxy.cpp +++ b/client/CServerProxy.cpp @@ -221,6 +221,12 @@ CServerProxy::getOutputStream() const return m_output; } +void +CServerProxy::onError() +{ + // ignore +} + void CServerProxy::onInfoChanged(const CClientInfo& info) { diff --git a/client/CServerProxy.h b/client/CServerProxy.h index 0370a646..e7f48d19 100644 --- a/client/CServerProxy.h +++ b/client/CServerProxy.h @@ -34,6 +34,7 @@ public: IOutputStream* getOutputStream() const; // IScreenReceiver overrides + virtual void onError(); virtual void onInfoChanged(const CClientInfo&); virtual bool onGrabClipboard(ClipboardID); virtual void onClipboardChanged(ClipboardID, const CString& data); diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index d8e5ab66..b0187ccb 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -145,13 +145,6 @@ CXWindowsSecondaryScreen::getScreen() const return m_screen; } -void -CXWindowsSecondaryScreen::onError() -{ - // ignore - // FIXME -- forward this? to whom? -} - void CXWindowsSecondaryScreen::onScreensaver(bool) { @@ -273,16 +266,18 @@ CXWindowsSecondaryScreen::createWindow() void CXWindowsSecondaryScreen::destroyWindow() { - CDisplayLock display(m_screen); - if (display != NULL) { - // release keys that are still pressed - releaseKeys(display); + { + CDisplayLock display(m_screen); + if (display != NULL) { + // release keys that are still pressed + releaseKeys(display); - // no longer impervious to server grabs - XTestGrabControl(display, False); + // no longer impervious to server grabs + XTestGrabControl(display, False); - // update - XSync(display, False); + // update + XSync(display, False); + } } // destroy window diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 6237996b..07ddb346 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -31,7 +31,6 @@ public: virtual IScreen* getScreen() const; // IScreenEventHandler overrides - virtual void onError(); virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index ed573cbf..5ae7f31e 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -656,7 +656,7 @@ CXWindowsScreen::ioErrorHandler(Display*) // so we set it to NULL), and exit. log((CLOG_WARN "X display has unexpectedly disconnected")); s_screen->m_display = NULL; - s_screen->m_eventHandler->onError(); + s_screen->m_receiver->onError(); log((CLOG_CRIT "quiting due to X display disconnection")); exit(17); } diff --git a/platform/IMSWindowsScreenEventHandler.h b/platform/IMSWindowsScreenEventHandler.h index 33804a96..eb39542b 100755 --- a/platform/IMSWindowsScreenEventHandler.h +++ b/platform/IMSWindowsScreenEventHandler.h @@ -16,7 +16,6 @@ public: virtual void preDestroyWindow(HWND) = 0; // IScreenEventHandler overrides - virtual void onError() = 0; virtual void onScreensaver(bool activated) = 0; virtual bool onPreDispatch(const CEvent* event) = 0; virtual bool onEvent(CEvent* event) = 0; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 3906bcaf..d2e53b6f 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -145,12 +145,6 @@ CMSWindowsPrimaryScreen::getScreen() const return m_screen; } -void -CMSWindowsPrimaryScreen::onError() -{ - // ignore -} - void CMSWindowsPrimaryScreen::onScreensaver(bool activated) { diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 5414d633..0b8af7b1 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -27,7 +27,6 @@ public: virtual IScreen* getScreen() const; // IMSWindowsScreenEventHandler overrides - virtual void onError(); virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index 89668c22..56bd3464 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -69,6 +69,13 @@ CPrimaryClient::getToggleMask() const return m_screen->getToggleMask(); } +void +CPrimaryClient::onError() +{ + // forward to server + m_server->onError(); +} + void CPrimaryClient::onInfoChanged(const CClientInfo& info) { @@ -79,9 +86,7 @@ CPrimaryClient::onInfoChanged(const CClientInfo& info) bool CPrimaryClient::onGrabClipboard(ClipboardID id) { - bool result = m_server->onGrabClipboard(getName(), id, m_seqNum); - m_clipboardOwner[id] = result; - return result; + return m_server->onGrabClipboard(getName(), id, m_seqNum); } void @@ -93,9 +98,8 @@ CPrimaryClient::onClipboardChanged(ClipboardID id, const CString& data) bool CPrimaryClient::open() { - // all clipboards are clean and owned by us + // all clipboards are clean for (UInt32 i = 0; i < kClipboardEnd; ++i) { - m_clipboardOwner[i] = true; m_clipboardDirty[i] = false; } @@ -159,7 +163,6 @@ CPrimaryClient::grabClipboard(ClipboardID id) m_screen->grabClipboard(id); // clipboard is dirty (because someone else owns it now) - m_clipboardOwner[id] = false; m_clipboardDirty[id] = true; } diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h index 3605a8e1..a94da39e 100644 --- a/server/CPrimaryClient.h +++ b/server/CPrimaryClient.h @@ -35,6 +35,7 @@ public: KeyModifierMask getToggleMask() const; // IScreenReceiver overrides + virtual void onError(); virtual void onInfoChanged(const CClientInfo&); virtual bool onGrabClipboard(ClipboardID); virtual void onClipboardChanged(ClipboardID, const CString&); @@ -71,7 +72,6 @@ private: CString m_name; UInt32 m_seqNum; CClientInfo m_info; - bool m_clipboardOwner[kClipboardEnd]; // FIXME -- unneeded? bool m_clipboardDirty[kClipboardEnd]; }; diff --git a/server/CPrimaryScreen.cpp b/server/CPrimaryScreen.cpp index a649a227..4184c7fd 100644 --- a/server/CPrimaryScreen.cpp +++ b/server/CPrimaryScreen.cpp @@ -2,11 +2,10 @@ #include "IScreen.h" #include "IScreenReceiver.h" #include "ProtocolTypes.h" +#include "CLock.h" #include "CThread.h" #include "CLog.h" -// FIXME -- should be locking - // // CPrimaryScreen // @@ -84,7 +83,10 @@ CPrimaryScreen::open() } // enter the screen - enterNoWarp(); + { + CLock lock(&m_mutex); + enterNoWarp(); + } // send screen info m_receiver->onInfoChanged(info); @@ -104,6 +106,7 @@ void CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver) { log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreensaver ? " for screen saver" : "")); + CLock lock(&m_mutex); assert(m_active == true); enterNoWarp(); @@ -118,6 +121,8 @@ CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver) void CPrimaryScreen::enterNoWarp() { + // note -- must be locked on entry + // not active anymore m_active = false; @@ -135,6 +140,7 @@ bool CPrimaryScreen::leave() { log((CLOG_INFO "leaving primary")); + CLock lock(&m_mutex); assert(m_active == false); // subclass hook @@ -187,6 +193,7 @@ CPrimaryScreen::grabClipboard(ClipboardID id) bool CPrimaryScreen::isActive() const { + CLock lock(&m_mutex); return m_active; } diff --git a/server/CPrimaryScreen.h b/server/CPrimaryScreen.h index 9040222d..5df1987c 100644 --- a/server/CPrimaryScreen.h +++ b/server/CPrimaryScreen.h @@ -3,6 +3,7 @@ #include "ClipboardTypes.h" #include "KeyTypes.h" +#include "CMutex.h" class IClipboard; class IScreen; @@ -146,8 +147,9 @@ private: void enterNoWarp(); private: - // FIXME -- should have a mutex + CMutex m_mutex; + // object to notify of changes IScreenReceiver* m_receiver; // m_active is true if this screen has been left diff --git a/server/CServer.cpp b/server/CServer.cpp index 1491f6c1..d10606b1 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -212,6 +212,20 @@ CServer::getActivePrimarySides() const return sides; } +void +CServer::onError() +{ + // stop all running threads but don't wait too long since some + // threads may be unable to proceed until this thread returns. + stopThreads(3.0); + + // done with the HTTP server + delete m_httpServer; + m_httpServer = NULL; + + // note -- we do not attempt to close down the primary screen +} + void CServer::onInfoChanged(const CString& name, const CClientInfo& info) { @@ -334,20 +348,6 @@ CServer::onClipboardChangedNoLock(ClipboardID id, m_active->setClipboard(id, m_clipboards[id].m_clipboardData); } -void -CServer::onError() -{ - // stop all running threads but don't wait too long since some - // threads may be unable to proceed until this thread returns. - stopThreads(3.0); - - // done with the HTTP server - delete m_httpServer; - m_httpServer = NULL; - - // note -- we do not attempt to close down the primary screen -} - void CServer::onScreensaver(bool activated) { diff --git a/server/CServer.h b/server/CServer.h index b8d49038..734d8906 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -55,12 +55,12 @@ public: CString getPrimaryScreenName() const; // IServer overrides + virtual void onError(); virtual void onInfoChanged(const CString&, const CClientInfo&); virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); // IPrimaryScreenReceiver overrides - virtual void onError(); virtual void onScreensaver(bool activated); virtual void onKeyDown(KeyID, KeyModifierMask); virtual void onKeyUp(KeyID, KeyModifierMask); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 1f7fa7b1..1e75254e 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -132,13 +132,6 @@ CXWindowsPrimaryScreen::getScreen() const return m_screen; } -void -CXWindowsPrimaryScreen::onError() -{ - // tell server to shutdown - m_receiver->onError(); -} - void CXWindowsPrimaryScreen::onScreensaver(bool activated) { diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 2a97a51c..ad625ff7 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -28,7 +28,6 @@ public: virtual IScreen* getScreen() const; // IScreenEventHandler overrides - virtual void onError(); virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); diff --git a/synergy/IPrimaryScreenReceiver.h b/synergy/IPrimaryScreenReceiver.h index ff816928..f347786e 100644 --- a/synergy/IPrimaryScreenReceiver.h +++ b/synergy/IPrimaryScreenReceiver.h @@ -11,13 +11,6 @@ // an IPrimaryScreenReceiver* and notify it of events. class IPrimaryScreenReceiver : public IInterface { public: - // called if the display is unexpectedly closing. - - // called if the screen is unexpectedly closing. this implies that - // the screen is no longer usable and that the program should - // close the screen and possibly terminate. - virtual void onError() = 0; - // called when the screensaver is activated or deactivated virtual void onScreensaver(bool activated) = 0; diff --git a/synergy/IScreen.h b/synergy/IScreen.h index 861f69c5..08c337ab 100644 --- a/synergy/IScreen.h +++ b/synergy/IScreen.h @@ -43,7 +43,10 @@ public: // activate or deactivate the screen saver virtual void screensaver(bool activate) = 0; - // FIXME -- need explanation + // ensure that this thread attached with the visible desktop. this is + // mainly intended for windows which has an artificial distinction + // between desktops and a thread cannot interact with the visible + // desktop unless the thread is attached to that desktop. virtual void syncDesktop() = 0; // accessors @@ -58,8 +61,9 @@ public: // get the current cursor coordinates virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; - // get the cursor center position - // FIXME -- need better explanation + // get the cursor center position. this is where we park the + // cursor to compute cursor motion deltas and should be far from + // the edges of the screen, typically the center. virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; }; diff --git a/synergy/IScreenEventHandler.h b/synergy/IScreenEventHandler.h index 123956be..4c6cd14e 100644 --- a/synergy/IScreenEventHandler.h +++ b/synergy/IScreenEventHandler.h @@ -18,11 +18,6 @@ class IScreenEventHandler : public IInterface { public: // manipulators - // called if the screen is unexpectedly closing. this implies that - // the screen is no longer usable and that the program should - // close the screen and possibly terminate. - virtual void onError() = 0; - // called when the screensaver is activated or deactivated virtual void onScreensaver(bool activated) = 0; diff --git a/synergy/IScreenReceiver.h b/synergy/IScreenReceiver.h index 02fcf936..da2d40c9 100644 --- a/synergy/IScreenReceiver.h +++ b/synergy/IScreenReceiver.h @@ -10,6 +10,11 @@ // notifications (indirectly) from the system. class IScreenReceiver : public IInterface { public: + // called if the screen is unexpectedly closing. this implies that + // the screen is no longer usable and that the program should + // close the screen and possibly terminate. + virtual void onError() = 0; + // notify of client info change virtual void onInfoChanged(const CClientInfo&) = 0; diff --git a/synergy/IServer.h b/synergy/IServer.h index f6a4e458..ee3798d6 100644 --- a/synergy/IServer.h +++ b/synergy/IServer.h @@ -17,6 +17,11 @@ class IServer : public IInterface { public: // manipulators + // called if the screen is unexpectedly closing. this implies that + // the screen is no longer usable and that the program should + // close the screen and possibly terminate. + virtual void onError() = 0; + // notify of client info change virtual void onInfoChanged(const CString& clientName, const CClientInfo&) = 0; From 22ba640940e9b816b1340878a7c654f88066d381 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 16 Jul 2002 19:07:15 +0000 Subject: [PATCH 232/807] fixes to get it compiling on .NET. --- base/stdistream.h | 4 ++-- io/CStreamBuffer.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base/stdistream.h b/base/stdistream.h index 6f83b5bb..d19ef632 100644 --- a/base/stdistream.h +++ b/base/stdistream.h @@ -6,8 +6,8 @@ #endif #include "stdpost.h" -#if defined(_MSC_VER) -// istream has no overloads for __int* types +#if defined(_MSC_VER) && _MSC_VER <= 1200 +// VC++6 istream has no overloads for __int* types, .NET does inline std::istream& operator>>(std::istream& s, SInt8& i) { return s >> (signed char&)i; } diff --git a/io/CStreamBuffer.cpp b/io/CStreamBuffer.cpp index fa3e68ad..d906d94b 100644 --- a/io/CStreamBuffer.cpp +++ b/io/CStreamBuffer.cpp @@ -35,7 +35,7 @@ CStreamBuffer::peek(UInt32 n) scan = m_chunks.erase(scan); } - return reinterpret_cast(head->begin() + m_headUsed); + return reinterpret_cast(&(head->begin()[m_headUsed])); } void From 82cdfb478a112c618c812ff5c637f8bfcb253fef Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 17 Jul 2002 17:24:44 +0000 Subject: [PATCH 233/807] removed unnecessary local variable. --- client/CClient.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 69e6026c..7c2d5fa4 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -147,7 +147,6 @@ CClient::run() m_rejected = true; } - CThread* thread = NULL; try { log((CLOG_NOTE "starting client \"%s\"", m_name.c_str())); From 76cc62d133210b2ff64f79df8c86dfa0b3f56ea3 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 17 Jul 2002 17:27:41 +0000 Subject: [PATCH 234/807] attempt to fix stuttering when leaving win32 screen. seems to work but will let testers make the final call. also fixed desktop synchronization by setting a variable that was mistakenly left unset. and tried to work around an apparent bug in MsgWaitForMultipleObjects() that prevented the service from closing down properly. start/pause/continue/stop sequence still doesn't shut down correctly. start/pause/stop and start/stop work fine. --- platform/CMSWindowsScreen.cpp | 6 ++ platform/CSynergyHook.cpp | 9 +- platform/CSynergyHook.h | 11 ++- server/CMSWindowsPrimaryScreen.cpp | 152 ++++++++++++++++------------- server/CMSWindowsPrimaryScreen.h | 9 +- server/CPrimaryScreen.cpp | 13 +-- server/CPrimaryScreen.h | 4 +- 7 files changed, 108 insertions(+), 96 deletions(-) diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index 9cc1a096..63e34d1f 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -1,6 +1,7 @@ #include "CMSWindowsScreen.h" #include "CMSWindowsClipboard.h" #include "CMSWindowsScreenSaver.h" +#include "CPlatform.h" #include "CClipboard.h" #include "IMSWindowsScreenEventHandler.h" #include "IScreenReceiver.h" @@ -35,6 +36,7 @@ CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, m_class(NULL), m_icon(NULL), m_cursor(NULL), + m_is95Family(CPlatform::isWindows95Family()), m_window(NULL), m_x(0), m_y(0), m_w(0), m_h(0), @@ -206,7 +208,11 @@ CMSWindowsScreen::mainLoop() void CMSWindowsScreen::exitMainLoop() { + // post an arbitrary message after the quit because + // MsgWaitForMultipleObjects() is broken and might not wake up if + // just WM_QUIT is in the queue. PostThreadMessage(m_threadID, WM_QUIT, 0, 0); + PostThreadMessage(m_threadID, WM_APP + 1, 0, 0); } void diff --git a/platform/CSynergyHook.cpp b/platform/CSynergyHook.cpp index 83857454..2f602de6 100644 --- a/platform/CSynergyHook.cpp +++ b/platform/CSynergyHook.cpp @@ -187,14 +187,7 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) // relay the motion SInt32 x = (SInt32)info->pt.x; SInt32 y = (SInt32)info->pt.y; - if (info->dwExtraInfo == 0x12345678) { - PostThreadMessage(g_threadID, - SYNERGY_MSG_POST_WARP, x, y); - } - else { - PostThreadMessage(g_threadID, - SYNERGY_MSG_MOUSE_MOVE, x, y); - } + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); } return 1; } diff --git a/platform/CSynergyHook.h b/platform/CSynergyHook.h index 893f2242..4466c5f7 100644 --- a/platform/CSynergyHook.h +++ b/platform/CSynergyHook.h @@ -19,10 +19,13 @@ #define SYNERGY_MSG_MARK WM_APP + 0x0011 // mark id; #define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data #define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; -#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y -#define SYNERGY_MSG_POST_WARP WM_APP + 0x0015 // x; y -#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0016 // delta; -#define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0017 // activated; +#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0014 // delta; +#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0015 // x; y +#define SYNERGY_MSG_POST_WARP WM_APP + 0x0016 // ; +#define SYNERGY_MSG_PRE_WARP WM_APP + 0x0017 // x; y +#define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; +#define SYNERGY_MSG_INPUT_FIRST SYNERGY_MSG_KEY +#define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP extern "C" { diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index d2e53b6f..2a4f8715 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -19,8 +19,7 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( m_threadID(0), m_window(NULL), m_mark(0), - m_markReceived(0), - m_mouseMoveIgnore(0) + m_markReceived(0) { assert(m_receiver != NULL); @@ -71,9 +70,15 @@ CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { - // set the cursor position without generating an event -// FIXME -- doesn't this generate an event anyway? - SetCursorPos(x, y); + // warp mouse + warpCursorNoFlush(x, y); + + // remove all input events before and including warp + MSG msg; + while (PeekMessage(&msg, NULL, SYNERGY_MSG_INPUT_FIRST, + SYNERGY_MSG_INPUT_LAST, PM_REMOVE)) { + // do nothing + } // save position as last position m_x = x; @@ -165,7 +170,7 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) case SYNERGY_MSG_KEY: // ignore message if posted prior to last mark change - if (m_markReceived == m_mark) { + if (!ignore()) { KeyModifierMask mask; const KeyID key = mapKey(msg->wParam, msg->lParam, &mask); if (key != kKeyNone) { @@ -201,7 +206,7 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) case SYNERGY_MSG_MOUSE_BUTTON: // ignore message if posted prior to last mark change - if (m_markReceived == m_mark) { + if (!ignore()) { static const int s_vkButton[] = { 0, // kButtonNone VK_LBUTTON, // kButtonLeft, etc. @@ -236,53 +241,65 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) case SYNERGY_MSG_MOUSE_WHEEL: // ignore message if posted prior to last mark change - if (m_markReceived == m_mark) { - log((CLOG_ERR "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); + if (!ignore()) { + log((CLOG_DEBUG1 "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); m_receiver->onMouseWheel(msg->wParam); } return true; + case SYNERGY_MSG_PRE_WARP: + { + // save position to compute delta of next motion + m_x = static_cast(msg->wParam); + m_y = static_cast(msg->lParam); + + // we warped the mouse. discard events until we find the + // matching post warp event. see warpCursorNoFlush() for + // where the events are sent. we discard the matching + // post warp event and can be sure we've skipped the warp + // event. + MSG msg; + do { + GetMessage(&msg, NULL, SYNERGY_MSG_MOUSE_MOVE, + SYNERGY_MSG_POST_WARP); + } while (msg.message != SYNERGY_MSG_POST_WARP); + + return true; + } + + case SYNERGY_MSG_POST_WARP: + log((CLOG_WARN "unmatched post warp")); + return true; + case SYNERGY_MSG_MOUSE_MOVE: // ignore message if posted prior to last mark change - if (m_markReceived == m_mark) { - SInt32 x = static_cast(msg->wParam); - SInt32 y = static_cast(msg->lParam); + if (!ignore()) { + // compute motion delta (relative to the last known + // mouse position) + SInt32 x = static_cast(msg->wParam) - m_x; + SInt32 y = static_cast(msg->lParam) - m_y; + + // save position to compute delta of next motion + m_x = static_cast(msg->wParam); + m_y = static_cast(msg->lParam); + if (!isActive()) { - m_receiver->onMouseMovePrimary(x, y); + // motion on primary screen + m_receiver->onMouseMovePrimary(m_x, m_y); } else { - // compute motion delta. this is relative to the - // last known mouse position. - x -= m_x; - y -= m_y; + // motion on secondary screen. warp mouse back to + // center. + if (x != 0 || y != 0) { + // back to center + warpCursorNoFlush(m_xCenter, m_yCenter); - // save position to compute delta of next motion - m_x = static_cast(msg->wParam); - m_y = static_cast(msg->lParam); - - // ignore if the mouse didn't move or we're ignoring - // motion. - if (m_mouseMoveIgnore == 0) { - if (x != 0 || y != 0) { - // back to center - warpCursorToCenter(); - - // send motion - m_receiver->onMouseMoveSecondary(x, y); - } - } - else { - // ignored one more motion event - --m_mouseMoveIgnore; + // send motion + m_receiver->onMouseMoveSecondary(x, y); } } } return true; - - case SYNERGY_MSG_POST_WARP: - m_x = static_cast(msg->wParam); - m_y = static_cast(msg->lParam); - return true; } return false; @@ -414,9 +431,6 @@ CMSWindowsPrimaryScreen::onPreEnter() { assert(m_window != NULL); - // reset motion ignore count - m_mouseMoveIgnore = 0; - // enable ctrl+alt+del, alt+tab, etc if (m_is95Family) { DWORD dummy = 0; @@ -450,23 +464,11 @@ CMSWindowsPrimaryScreen::onPostLeave(bool success) // relay all mouse and keyboard events m_setRelay(true); - // ignore this many mouse motion events (not including the already - // queued events). on (at least) the win2k login desktop, one - // motion event is reported using a position from before the above - // warpCursor(). i don't know why it does that and other desktops - // don't have the same problem. anyway, simply ignoring that event - // works around it. -// FIXME -- is this true now that we're using mouse_event? - m_mouseMoveIgnore = 1; - // disable ctrl+alt+del, alt+tab, etc if (m_is95Family) { DWORD dummy = 0; SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); } - - // discard messages until after the warp - nextMark(); } } @@ -550,16 +552,21 @@ CMSWindowsPrimaryScreen::hideWindow() void CMSWindowsPrimaryScreen::warpCursorToCenter() { - // warp to center. the extra info tells the hook DLL to send - // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. - SInt32 x, y, w, h; - m_screen->getShape(x, y, w, h); - mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, - (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), - (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), - 0, - 0x12345678); -// FIXME -- ignore mouse until we get warp notification? + warpCursor(m_xCenter, m_yCenter); +} + +void +CMSWindowsPrimaryScreen::warpCursorNoFlush(SInt32 x, SInt32 y) +{ + // send an event that we can recognize before the mouse warp + PostThreadMessage(m_threadID, SYNERGY_MSG_PRE_WARP, x, y); + + // warp mouse. hopefully this inserts a mouse motion event + // between the previous message and the following message. + SetCursorPos(x, y); + + // send an event that we can recognize after the mouse warp + PostThreadMessage(m_threadID, SYNERGY_MSG_POST_WARP, 0, 0); } void @@ -572,6 +579,12 @@ CMSWindowsPrimaryScreen::nextMark() PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); } +bool +CMSWindowsPrimaryScreen::ignore() const +{ + return (m_mark != m_markReceived); +} + static const KeyID g_virtualKey[] = { /* 0x00 */ kKeyNone, // reserved @@ -984,15 +997,14 @@ CMSWindowsPrimaryScreen::mapKey( // set shift state required to generate key BYTE keys[256]; memset(keys, 0, sizeof(keys)); -// FIXME -- surely these masks should be different in each if expression if (vkCode & 0x0100) { - keys[VK_SHIFT] = 0x80; + keys[VK_SHIFT] = 0x80; } - if (vkCode & 0x0100) { + if (vkCode & 0x0200) { keys[VK_CONTROL] = 0x80; } - if (vkCode & 0x0100) { - keys[VK_MENU] = 0x80; + if (vkCode & 0x0400) { + keys[VK_MENU] = 0x80; } // strip shift state off of virtual key code diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 0b8af7b1..3dc2d161 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -56,9 +56,15 @@ protected: private: void enterNoWarp(); + // warp cursor without discarding queued events + void warpCursorNoFlush(SInt32 x, SInt32 y); + // discard posted messages void nextMark(); + // test if event should be ignored + bool ignore() const; + // key and button queries KeyID mapKey(WPARAM keycode, LPARAM info, KeyModifierMask* maskOut); @@ -91,9 +97,6 @@ private: // position of center pixel of screen SInt32 m_xCenter, m_yCenter; - // used to ignore mouse motion - SInt32 m_mouseMoveIgnore; - // hook library stuff HINSTANCE m_hookLibrary; InitFunc m_init; diff --git a/server/CPrimaryScreen.cpp b/server/CPrimaryScreen.cpp index 4184c7fd..cfe7d014 100644 --- a/server/CPrimaryScreen.cpp +++ b/server/CPrimaryScreen.cpp @@ -155,19 +155,12 @@ CPrimaryScreen::leave() // get keyboard state as we leave updateKeys(); - // warp mouse to center - warpCursorToCenter(); - // FIXME -- this doesn't match the win32 version. that just does - // the warp while we flush the input queue until we find the warp - // and we discard that too. would prefer to at least match our - // own warping when we receive MotionNotify; that just does the - // warp. however, the win32 version sometimes stutters when - // leaving and perhaps this is why. hmm, win32 does ignore the - // events until after the warp (via the mark). - // subclass hook onPostLeave(true); + // warp mouse to center + warpCursorToCenter(); + // local client now active m_active = true; diff --git a/server/CPrimaryScreen.h b/server/CPrimaryScreen.h index 5df1987c..7fab4eba 100644 --- a/server/CPrimaryScreen.h +++ b/server/CPrimaryScreen.h @@ -47,7 +47,9 @@ public: // primary screen are linked to clients. virtual void reconfigure(UInt32 activeSides) = 0; - // warp the cursor to the given absolute coordinates + // warp the cursor to the given absolute coordinates. also + // discard input events up to and including the warp before + // returning. virtual void warpCursor(SInt32 x, SInt32 y) = 0; // set the screen's clipboard contents. this is usually called From e94f308e210dd781667edeb9b452f182abe88546 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 18 Jul 2002 08:54:17 +0000 Subject: [PATCH 235/807] fixed incorrect paths to makehook and synrgyhk project files. --- synergy.dsw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synergy.dsw b/synergy.dsw index 13b664f3..2ba5beae 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -87,7 +87,7 @@ Package=<4> ############################################################################### -Project: "makehook"=.\server\makehook.dsp - Package Owner=<4> +Project: "makehook"=.\platform\makehook.dsp - Package Owner=<4> Package=<5> {{{ @@ -186,7 +186,7 @@ Package=<4> ############################################################################### -Project: "synrgyhk"=.\server\synrgyhk.dsp - Package Owner=<4> +Project: "synrgyhk"=.\platform\synrgyhk.dsp - Package Owner=<4> Package=<5> {{{ From 0759cbc1049ba2e4e85a811fcdda8f43c817eaeb Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 18 Jul 2002 16:58:08 +0000 Subject: [PATCH 236/807] changed waitForEvent() to handle a peculiar feature of MsgWaitForMultipleObjects(): it will not return immediately if an event already in the queue when it's called was already in the queue during the last call to GetMessage()/PeekMessage(). also now discarding screen saver events if there are any other screen saver events in the queue already. this prevents these events from piling up in the queue, which they'd do because we sleep for 250ms when handling each one. --- mt/CThreadRep.cpp | 8 ++++++++ platform/CMSWindowsScreen.cpp | 36 ++++++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/mt/CThreadRep.cpp b/mt/CThreadRep.cpp index 604a3650..82e15992 100644 --- a/mt/CThreadRep.cpp +++ b/mt/CThreadRep.cpp @@ -628,6 +628,14 @@ CThreadRep::wait(CThreadRep* target, double timeout) bool CThreadRep::waitForEvent(double timeout) { + // check if messages are available first. if we don't do this then + // MsgWaitForMultipleObjects() will block even if the queue isn't + // empty if the messages in the queue were there before the last + // call to GetMessage()/PeekMessage(). + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + return true; + } + // is cancellation enabled? const DWORD n = (isCancellable() ? 1 : 0); diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index 63e34d1f..b6e87f56 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -208,11 +208,7 @@ CMSWindowsScreen::mainLoop() void CMSWindowsScreen::exitMainLoop() { - // post an arbitrary message after the quit because - // MsgWaitForMultipleObjects() is broken and might not wake up if - // just WM_QUIT is in the queue. PostThreadMessage(m_threadID, WM_QUIT, 0, 0); - PostThreadMessage(m_threadID, WM_APP + 1, 0, 0); } void @@ -423,15 +419,33 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) const MSG* msg = &event->m_msg; switch (msg->message) { case SYNERGY_MSG_SCREEN_SAVER: - if (msg->wParam != 0) { - if (m_screensaver->checkStarted(msg->message, FALSE, 0)) { - m_eventHandler->onScreensaver(true); + { + // activating or deactivating? + bool activate = (msg->wParam != 0); + + // ignore this message if there are any other screen saver + // messages already in the queue. this is important because + // our checkStarted() function has a deliberate delay, so it + // can't respond to events at full CPU speed and will fall + // behind if a lot of screen saver events are generated. + // that can easily happen because windows will continually + // send SC_SCREENSAVE until the screen saver starts, even if + // the screen saver is disabled! + MSG msg; + if (!PeekMessage(&msg, NULL, SYNERGY_MSG_SCREEN_SAVER, + SYNERGY_MSG_SCREEN_SAVER, PM_NOREMOVE)) { + if (activate) { + if (m_screensaver->checkStarted( + SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) { + m_eventHandler->onScreensaver(true); + } + } + else { + m_eventHandler->onScreensaver(false); + } } + return true; } - else { - m_eventHandler->onScreensaver(false); - } - return true; case WM_TIMER: // if current desktop is not the input desktop then switch to it. From 635c3d1c62749fd2f83ac26e0b427430d930f11b Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 18 Jul 2002 17:00:48 +0000 Subject: [PATCH 237/807] now cancelling and waiting for the accept client thread before cancelling any other threads. this prevents a race condition where we disconnect a client but it reconnects before we manage to shutdown. that might leave a thread running and the connection won't be closed down properly. --- server/CServer.cpp | 35 ++++++++++++++++++++++++++++------- server/CServer.h | 4 ++-- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/server/CServer.cpp b/server/CServer.cpp index d10606b1..89ff7150 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -29,6 +29,10 @@ const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; CServer::CServer(const CString& serverName) : m_name(serverName), + m_bindTimeout(5.0 * 60.0), + m_socketFactory(NULL), + m_securityFactory(NULL), + m_acceptClientThread(NULL), m_active(NULL), m_primaryClient(NULL), m_seqNum(0), @@ -36,9 +40,7 @@ CServer::CServer(const CString& serverName) : m_httpServer(NULL), m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests) { - m_socketFactory = NULL; - m_securityFactory = NULL; - m_bindTimeout = 5.0 * 60.0; + // do nothing } CServer::~CServer() @@ -82,7 +84,9 @@ CServer::run() log((CLOG_NOTE "starting server")); // start listening for new clients - startThread(new TMethodJob(this, &CServer::acceptClients)); + m_acceptClientThread = new CThread(startThread( + new TMethodJob(this, + &CServer::acceptClients))); // start listening for HTTP requests if (m_config.getHTTPAddress().isValid()) { @@ -220,6 +224,7 @@ CServer::onError() stopThreads(3.0); // done with the HTTP server + CLock lock(&m_mutex); delete m_httpServer; m_httpServer = NULL; @@ -1016,7 +1021,7 @@ CServer::closeClients(const CConfig& config) reapThreads(); } -void +CThread CServer::startThread(IJob* job) { CLock lock(&m_mutex); @@ -1025,8 +1030,10 @@ CServer::startThread(IJob* job) doReapThreads(m_threads); // add new thread to list. use the job as user data for logging. - m_threads.push_back(CThread(job, job)); - log((CLOG_DEBUG1 "started thread %p", m_threads.back().getUserData())); + CThread thread(job, job); + m_threads.push_back(thread); + log((CLOG_DEBUG1 "started thread %p", thread.getUserData())); + return thread; } void @@ -1034,6 +1041,20 @@ CServer::stopThreads(double timeout) { log((CLOG_DEBUG1 "stopping threads")); + // cancel the accept client thread to prevent more clients from + // connecting while we're shutting down. + CThread* acceptClientThread; + { + CLock lock(&m_mutex); + acceptClientThread = m_acceptClientThread; + m_acceptClientThread = NULL; + } + if (acceptClientThread != NULL) { + acceptClientThread->cancel(); + acceptClientThread->wait(timeout); + delete acceptClientThread; + } + // close all clients (except the primary) { CConfig emptyConfig; diff --git a/server/CServer.h b/server/CServer.h index 734d8906..0a34c9a6 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -14,7 +14,6 @@ class CClientProxy; class CHTTPServer; class CPrimaryClient; -class CThread; class IClient; class IDataSocket; class IServerProtocol; @@ -118,7 +117,7 @@ private: void closeClients(const CConfig& config); // start a thread, adding it to the list of threads - void startThread(IJob* adopted); + CThread startThread(IJob* adopted); // cancel running threads, waiting at most timeout seconds for // them to finish. @@ -171,6 +170,7 @@ private: // running threads CThreadList m_threads; + CThread* m_acceptClientThread; // the screens typedef std::map CClientList; From cf71aec730a44047147a8864c595b98a3e8bfecc Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 18 Jul 2002 17:03:10 +0000 Subject: [PATCH 238/807] fixed handling of calling init() when a previous process did not call cleanup(). if that process still appears to exist then the init() fails. otherwise some cleanup is performed and the init() proceeds. a synergy server started while another is running will now exit immediately without interfering the original server. --- platform/CSynergyHook.cpp | 53 ++++++++++++++++++++++-------- server/CMSWindowsPrimaryScreen.cpp | 4 ++- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/platform/CSynergyHook.cpp b/platform/CSynergyHook.cpp index 2f602de6..dc711685 100644 --- a/platform/CSynergyHook.cpp +++ b/platform/CSynergyHook.cpp @@ -33,7 +33,7 @@ typedef struct tagMOUSEHOOKSTRUCTWin2000 { // all data in this shared section *must* be initialized static HINSTANCE g_hinstance = NULL; -static DWORD g_process = NULL; +static DWORD g_processID = 0; static EWheelSupport g_wheelSupport = kWheelNone; static UINT g_wmMouseWheel = 0; static DWORD g_threadID = 0; @@ -438,17 +438,19 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) { if (reason == DLL_PROCESS_ATTACH) { - if (g_hinstance == NULL) { + DisableThreadLibraryCalls(instance); + if (g_processID == 0) { g_hinstance = instance; - g_process = GetCurrentProcessId(); + g_processID = GetCurrentProcessId(); } } else if (reason == DLL_PROCESS_DETACH) { - if (g_process == GetCurrentProcessId()) { + if (g_processID == GetCurrentProcessId()) { if (g_keyboard != NULL || g_mouse != NULL || g_cbt != NULL) { uninstall(); } - g_process = NULL; + g_processID = 0; + g_hinstance = NULL; } } return TRUE; @@ -461,18 +463,39 @@ init(DWORD threadID) { assert(g_hinstance != NULL); - // see if already initialized. if it is we'll shut down and - // reinitialize. this allows the hook DLL to be reclaimed by - // a new synergyd if the previous one died unexpectedly. - if (g_threadID != 0) { - uninstallScreenSaver(); - uninstall(); - cleanup(); + // try to open process that last called init() to see if it's + // still running or if it died without cleaning up. + if (g_processID != 0 && g_processID != GetCurrentProcessId()) { + HANDLE process = OpenProcess(STANDARD_RIGHTS_REQUIRED, + FALSE, g_processID); + if (process != NULL) { + // old process (probably) still exists so refuse to + // reinitialize this DLL (and thus steal it from the + // old process). + CloseHandle(process); + return 0; + } + + // clean up after old process. the system should've already + // removed the hooks so we just need to reset our state. + g_hinstance = GetModuleHandle("synrgyhk"); + g_processID = GetCurrentProcessId(); + g_wheelSupport = kWheelNone; + g_threadID = 0; + g_keyboard = NULL; + g_mouse = NULL; + g_cbt = NULL; + g_getMessage = NULL; + g_keyHookThread = NULL; + g_keyHookThreadID = 0; + g_keyHookEvent = NULL; + g_keyboardLL = NULL; + g_screenSaver = false; } // save thread id. we'll post messages to this thread's // message queue. - g_threadID = threadID; + g_threadID = threadID; // set defaults g_relay = false; @@ -493,7 +516,9 @@ cleanup(void) { assert(g_hinstance != NULL); - g_threadID = 0; + if (g_processID == GetCurrentProcessId()) { + g_threadID = 0; + } return 1; } diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 2a4f8715..9c0193a3 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -398,7 +398,9 @@ CMSWindowsPrimaryScreen::onPreOpen() // initialize hook library m_threadID = GetCurrentThreadId(); - m_init(m_threadID); + if (m_init(m_threadID) == 0) { + throw XScreenOpenFailure(); + } } void From 89ab1caa110f2609dc93082f7196fee720689e89 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 19 Jul 2002 17:38:34 +0000 Subject: [PATCH 239/807] reordered operations to reduce cursor flashing when entering primary screen. --- server/CPrimaryScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CPrimaryScreen.cpp b/server/CPrimaryScreen.cpp index cfe7d014..cbd114a4 100644 --- a/server/CPrimaryScreen.cpp +++ b/server/CPrimaryScreen.cpp @@ -109,13 +109,13 @@ CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver) CLock lock(&m_mutex); assert(m_active == true); - enterNoWarp(); if (!forScreensaver) { warpCursor(x, y); } else { onEnterScreensaver(); } + enterNoWarp(); } void From 5f865a9b07c8d33c999eb26b0c3ae2cdbe8abc7b Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 19 Jul 2002 17:39:45 +0000 Subject: [PATCH 240/807] removed from previous change. --- server/CPrimaryScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CPrimaryScreen.cpp b/server/CPrimaryScreen.cpp index cbd114a4..5c8369e0 100644 --- a/server/CPrimaryScreen.cpp +++ b/server/CPrimaryScreen.cpp @@ -115,7 +115,7 @@ CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver) else { onEnterScreensaver(); } - enterNoWarp(); + enterNoWarp(); } void From 47f7e3c4d539fda835d4284ae4335a5175d260be Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 19 Jul 2002 18:12:41 +0000 Subject: [PATCH 241/807] formatting. --- server/CXWindowsPrimaryScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 1e75254e..0dc659e9 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -455,7 +455,7 @@ CXWindowsPrimaryScreen::showWindow() do { // keyboard first do { - result = XGrabKeyboard(display, m_window, True, + result = XGrabKeyboard(display, m_window, True, GrabModeAsync, GrabModeAsync, CurrentTime); assert(result != GrabNotViewable); if (result != GrabSuccess) { From 587f5247d87657575676661713d7b14b10660d72 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 19 Jul 2002 20:44:57 +0000 Subject: [PATCH 242/807] updated init.d scripts to work with SuSE. however, it looks as if they cannot be used on an X server using authentication because the daemons they start are not authorized to connect to the X server. X users should modify Xsetup or Xsession. --- examples/synergy.linux.init | 86 +++++++++++++++++++++++++----------- examples/synergyd.linux.init | 86 +++++++++++++++++++++++++----------- 2 files changed, 118 insertions(+), 54 deletions(-) diff --git a/examples/synergy.linux.init b/examples/synergy.linux.init index 4901f6e0..9e143d8b 100755 --- a/examples/synergy.linux.init +++ b/examples/synergy.linux.init @@ -3,41 +3,73 @@ # Startup script for synergy client # # chkconfig: 5 98 02 -# description: Starts/stops synergy client when X server is started/stopped +# description: Starts/stops synergy client # # processname: synergy +# +### BEGIN INIT INFO +# Provides: synergy +# Required-Start: $network $syslog +# Required-Stop: $network $syslog +# Default-Start: 5 +# Default-Stop: 0 1 2 3 6 +# Description: Start synergy client daemon. +### END INIT INFO + +SYNERGY_BIN=/usr/sbin/synergy +test -x $SYNERGY_BIN || exit 5 # startup command line arguments ARGS=192.168.1.3 -# Source function library. +# load function library +if test -f /etc/rc.status; then +. /etc/rc.status +rc_reset +else . /etc/rc.d/init.d/functions +function startproc () +{ + daemon $* +} +function checkproc () +{ + status $* +} +function rc_status () +{ + RETVAL=$? +} +function rc_exit () +{ + exit $RETVAL +} +fi case "$1" in - start) - echo -n "Starting synergy client: " - daemon synergy $ARGS - RETVAL=$? - echo - ;; - stop) - echo -n "Stopping synergy client: " - killproc synergy - RETVAL=$? - echo - ;; - status) - status synergy - RETVAL=$? - ;; - restart) - $0 stop - $0 start - RETVAL=$? - ;; - *) - echo "Usage: synergy {start|stop|status|restart}" - exit 1 + start) + echo -n "Starting synergy client: " + startproc $SYNERGY_BIN $ARGS + rc_status -v + ;; + stop) + echo -n "Stopping synergy client: " + killproc -TERM $SYNERGY_BIN + rc_status -v + ;; + restart) + $0 stop + $0 start + rc_status + ;; + status) + echo -n "Checking for synergy client: " + checkproc $SYNERGY_BIN + rc_status -v + ;; + *) + echo "Usage: synergy {start|stop|status|restart}" + exit 1 esac -exit $RETVAL +rc_exit diff --git a/examples/synergyd.linux.init b/examples/synergyd.linux.init index 9f4e3626..426bcaa1 100755 --- a/examples/synergyd.linux.init +++ b/examples/synergyd.linux.init @@ -3,41 +3,73 @@ # Startup script for synergy server # # chkconfig: 5 98 02 -# description: Starts/stops synergy server when X server is started/stopped +# description: Starts/stops synergy server # # processname: synergyd +# +### BEGIN INIT INFO +# Provides: synergyd +# Required-Start: $network $syslog +# Required-Stop: $network $syslog +# Default-Start: 5 +# Default-Stop: 0 1 2 3 6 +# Description: Start synergy server daemon. +### END INIT INFO + +SYNERGYD_BIN=/usr/sbin/synergyd +test -x $SYNERGYD_BIN || exit 5 # startup command line arguments ARGS= -# Source function library. +# load function library +if test -f /etc/rc.status; then +. /etc/rc.status +rc_reset +else . /etc/rc.d/init.d/functions +function startproc () +{ + daemon $* +} +function checkproc () +{ + status $* +} +function rc_status () +{ + RETVAL=$? +} +function rc_exit () +{ + exit $RETVAL +} +fi case "$1" in - start) - echo -n "Starting synergy server: " - daemon synergyd $ARGS - RETVAL=$? - echo - ;; - stop) - echo -n "Stopping synergy server: " - killproc synergyd - RETVAL=$? - echo - ;; - status) - status synergyd - RETVAL=$? - ;; - restart) - $0 stop - $0 start - RETVAL=$? - ;; - *) - echo "Usage: synergyd {start|stop|status|restart}" - exit 1 + start) + echo -n "Starting synergy server: " + startproc $SYNERGYD_BIN $ARGS + rc_status -v + ;; + stop) + echo -n "Stopping synergy server: " + killproc -TERM $SYNERGYD_BIN + rc_status -v + ;; + restart) + $0 stop + $0 start + rc_status + ;; + status) + echo -n "Checking for synergy server: " + checkproc $SYNERGYD_BIN + rc_status -v + ;; + *) + echo "Usage: synergyd {start|stop|status|restart}" + exit 1 esac -exit $RETVAL +rc_exit From 4c2cbb9f03a4e1e2c818091adc42c9eef26496e2 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 19 Jul 2002 21:27:59 +0000 Subject: [PATCH 243/807] changed notes about how to startup configure synergy. it now discourages using boot scripts, which can't handle X servers requiring authorization, and suggests modifying xdm's Xsetup. --- README | 75 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/README b/README index cc5da6c5..def4b265 100644 --- a/README +++ b/README @@ -226,29 +226,16 @@ linux, unix: before starting synergy as a daemon you should understand that synergy requires an X server that it can connect to. synergy can start before the X server does and will repeatly attempt to - connect to the X server until it succeeds. the DISPLAY env var - should be set appropriately before starting synergy. note that - it probably will not be set when running startup scripts so you - have to set it yourself (probably to ":0"). if it's not set - then synergy will use the default ":0.0" which is correct in - most cases. + connect to the X server until it succeeds. however, if the + server requires authorization then it's unlikely that synergy + will ever succeed. in that case synergy should be (re)started + by the X display manager. - if the X server goes down then all the clients connected to it - must terminate. synergy has to deal with this because some - display managers will automatically restart the X server when - a user logs out. synergy can automatically restart itself and - it does so by default. the `--no-restart' option turns this - feature off. - - some display managers also prevent any X client other than - those they themselves start from connecting to the X server - during login for security reasons. for these display managers, - synergy should be part of the manager's screen initialization - script and synergy should be started with the --no-restart - option since the display manager will do the restarting. - otherwise, synergy should not use the --no-restart option and - it can be started at boot time (sometime after the network is - started) or when the X server is started. + the DISPLAY env var should be set appropriately before starting + synergy. note that it probably will not be set when running + boot scripts so you have to set it yourself (probably to ":0"). + if it's not set then synergy will use the default ":0.0" which + is correct in most cases. finally, some display managers (xdm and kdm, but not gdm) grab the keyboard and do not release it until the user logs @@ -259,21 +246,40 @@ linux, unix: to install synergy as a daemon, you'll need to add the appropriate lines and/or files to start synergy at boot time - or as part of a display manager screen initialization script. + or modify the display manager screen initialization scripts. do not use the `-f' or `--no-daemon' options. for the server use the `--config' option to specify the path to the configuration file or just put the configuration in /etc/synergy.conf. linux: - if starting synergy from xdm's or kdm's screen initialization - script you'll want add a line to start synergy in - /etc/X11/xdm/Xsetup_0, probably near the bottom. you'll also - want to put the configuration file in /etc/synergy.conf and - the synergy executable in /usr/sbin. to uninstall, remove the - added line and files. + you should modify xdm's Xsetup script to start the synergy + client or server. for example, somewhere near the bottom of + Xsetup (but someplace before anywhere the script calls exit) + you might add: + /usr/bin/killall synergy + /usr/sbin/synergy 192.168.1.101 + this assumes synergy is installed in /usr/sbin. these lines + make sure any already running synergy is terminated and starts + a fresh copy. it's important to kill old copies so that you + don't end up with multiple synergy instances fighting each + other or, at the very least, using up system resources. - if starting the synergy client using init.d then: + to start the synergy server you might use: + /usr/bin/killall synergyd + /usr/sbin/synergyd --config /root/synergy.conf + assuming synergyd is installed in /usr/sbin. if you've put + the configuration data in /etc/synergy.conf then you don't + need the --config option. + + another option is to put the synergy startup in .Xsession in + your home directory. that allows users without root access to + start synergy when they login. in this case synergy will not + be running while on the login screen. + + if your X server does not require authorization then you can + start synergy at boot time. if starting the synergy client + using init.d then: # /bin/cp synergy /usr/sbin/synergy # /bin/cp synergy.linux.init /etc/init.d/synergy # /sbin/chkconfig --add synergy @@ -305,7 +311,11 @@ linux: # /bin/rm /etc/synergy.conf # /bin/rm /etc/init.d/synergyd # /bin/rm /usr/sbin/synergyd - + + note that synergy.linux.init and synergyd.linux.init are not + tested on a wide variety of platforms and may need editing + for your platform. + common command line options --------------------------- @@ -455,4 +465,5 @@ tips and tricks * turn off mouse driven virtual desktop switching on X windows. it will interfere with synergy. use keyboard shortcuts instead. -* synergy's screensaver synchronization works best with xscreensaver. +* synergy's screensaver synchronization works best with xscreensaver + under X windows. From 643d0f10895681a3855e35dfedb4602e54287dde Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 22 Jul 2002 17:32:51 +0000 Subject: [PATCH 244/807] checkpoint. adding support for unicode in clipboard. --- base/CUnicode.cpp | 530 +++++++++++++++++++ base/CUnicode.h | 48 ++ base/Makefile.am | 2 + platform/CXWindowsClipboard.cpp | 176 +++--- platform/CXWindowsClipboard.h | 47 +- platform/CXWindowsClipboardTextConverter.cpp | 48 ++ platform/CXWindowsClipboardTextConverter.h | 23 + platform/CXWindowsClipboardUCS2Converter.cpp | 48 ++ platform/CXWindowsClipboardUCS2Converter.h | 23 + platform/CXWindowsClipboardUTF8Converter.cpp | 47 ++ platform/CXWindowsClipboardUTF8Converter.h | 23 + platform/Makefile.am | 40 +- synergy/IClipboard.h | 8 +- 13 files changed, 966 insertions(+), 97 deletions(-) create mode 100644 base/CUnicode.cpp create mode 100644 base/CUnicode.h create mode 100644 platform/CXWindowsClipboardTextConverter.cpp create mode 100644 platform/CXWindowsClipboardTextConverter.h create mode 100644 platform/CXWindowsClipboardUCS2Converter.cpp create mode 100644 platform/CXWindowsClipboardUCS2Converter.h create mode 100644 platform/CXWindowsClipboardUTF8Converter.cpp create mode 100644 platform/CXWindowsClipboardUTF8Converter.h diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp new file mode 100644 index 00000000..48834192 --- /dev/null +++ b/base/CUnicode.cpp @@ -0,0 +1,530 @@ +#include "CUnicode.h" +#include + +// +// local utility functions +// + +inline +static +UInt16 +decode16(const UInt8* n) +{ + union x16 { + UInt8 n8[2]; + UInt16 n16; + } c; + c.n8[0] = n[0]; + c.n8[1] = n[1]; + return c.n16; +} + +inline +static +UInt32 +decode32(const UInt8* n) +{ + union x32 { + UInt8 n8[4]; + UInt32 n32; + } c; + c.n8[0] = n[0]; + c.n8[1] = n[1]; + c.n8[2] = n[2]; + c.n8[3] = n[3]; + return c.n32; +} + +// +// CUnicode +// + +UInt32 CUnicode::s_invalid = 0x0000ffff; + +CString +CUnicode::UTF8ToUCS2(const CString& src) +{ + // get size of input string and reserve some space in output. + // include UTF8's nul terminator. + UInt32 n = src.size() + 1; + CString dst; + dst.reserve(2 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c != s_invalid && c < 0x0000ffff) { + UInt16 ucs2 = static_cast(c); + dst.append(reinterpret_cast(&ucs2), 2); + } + } + + return dst; +} + +CString +CUnicode::UTF8ToUCS4(const CString& src) +{ + // get size of input string and reserve some space in output. + // include UTF8's nul terminator. + UInt32 n = src.size() + 1; + CString dst; + dst.reserve(4 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c != s_invalid) { + dst.append(reinterpret_cast(&c), 4); + } + } + + return dst; +} + +CString +CUnicode::UTF8ToUTF16(const CString& src) +{ + // get size of input string and reserve some space in output. + // include UTF8's nul terminator. + UInt32 n = src.size() + 1; + CString dst; + dst.reserve(2 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c != s_invalid && c < 0x0010ffff) { + if (c < 0x00010000) { + UInt16 ucs2 = static_cast(c); + dst.append(reinterpret_cast(&ucs2), 2); + } + else { + c -= 0x00010000; + UInt16 utf16h = static_cast(c >> 10) + 0xd800; + UInt16 utf16l = (static_cast(c) & 0x03ff) + 0xdc00; + dst.append(reinterpret_cast(&utf16h), 2); + dst.append(reinterpret_cast(&utf16l), 2); + } + } + } + + return dst; +} + +CString +CUnicode::UTF8ToUTF32(const CString& src) +{ + // FIXME -- should ensure dst has no characters over U-0010FFFF + return UTF8ToUCS4(src); +} + +CString +CUnicode::UCS2ToUTF8(const CString& src) +{ + UInt32 n = src.size() >> 1; + return doUCS2ToUTF8(reinterpret_cast(src.data()), n); +} + +CString +CUnicode::UCS4ToUTF8(const CString& src) +{ + UInt32 n = src.size() >> 2; + return doUCS4ToUTF8(reinterpret_cast(src.data()), n); +} + +CString +CUnicode::UTF16ToUTF8(const CString& src) +{ + UInt32 n = src.size() >> 1; + return doUTF16ToUTF8(reinterpret_cast(src.data()), n); +} + +CString +CUnicode::UTF32ToUTF8(const CString& src) +{ + UInt32 n = src.size() >> 2; + return doUTF32ToUTF8(reinterpret_cast(src.data()), n); +} + +CString +CUnicode::UTF8ToText(const CString& src) +{ + // convert to wide char + wchar_t* tmp = UTF8ToWideChar(src); + + // get length of multibyte string + mbstate_t state; + memset(&state, 0, sizeof(state)); + const wchar_t* scratch = tmp; + size_t len = wcsrtombs(NULL, &scratch, 0, &state); + if (len == (size_t)-1) { + // invalid character in src + delete[] tmp; + return CString(); + } + + // convert to multibyte + scratch = tmp; + char* dst = new char[len + 1]; + wcsrtombs(dst, &scratch, len + 1, &state); + CString text(dst); + + // clean up + delete[] dst; + delete[] tmp; + + return text; +} + +CString +CUnicode::textToUTF8(const CString& src) +{ + // get length of wide char string + mbstate_t state; + memset(&state, 0, sizeof(state)); + const char* scratch = src.c_str(); + size_t len = mbsrtowcs(NULL, &scratch, 0, &state); + if (len == (size_t)-1) { + // invalid character in src + return CString(); + } + + // convert multibyte to wide char + scratch = src.c_str(); + wchar_t* dst = new wchar_t[len + 1]; + mbsrtowcs(dst, &scratch, len + 1, &state); + + // convert to UTF8 + CString utf8 = wideCharToUTF8(dst); + + // clean up + delete[] dst; + + return utf8; +} + +wchar_t* +CUnicode::UTF8ToWideChar(const CString& src) +{ + // convert to platform's wide character encoding. + // note -- this must include a wide nul character (independent of + // the CString's nul character). +#if WINDOWS_LIKE + CString tmp = UTF8ToUCS16(src); + UInt32 size = tmp.size() >> 1; +#elif UNIX_LIKE + CString tmp = UTF8ToUCS4(src); + UInt32 size = tmp.size() >> 2; +#endif + + // copy to a wchar_t array + wchar_t* dst = new wchar_t[size]; + ::memcpy(dst, src.data(), sizeof(wchar_t) * size); + return dst; +} + +CString +CUnicode::wideCharToUTF8(const wchar_t* src) +{ + // convert from platform's wide character encoding. + // note -- this must include a wide nul character (independent of + // the CString's nul character). +#if WINDOWS_LIKE + return doUCS16ToUTF8(reinterpret_cast(src), wcslen(src)); +#elif UNIX_LIKE + return doUCS4ToUTF8(reinterpret_cast(src), wcslen(src)); +#endif +} + +CString +CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 2, --n) { + UInt32 c = decode16(data); + toUTF8(dst, c); + } + + // remove extra trailing nul + if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { + dst.resize(dst.size() - 1); + } + + return dst; +} + +CString +CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 4, --n) { + UInt32 c = decode32(data); + toUTF8(dst, c); + } + + // remove extra trailing nul + if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { + dst.resize(dst.size() - 1); + } + + return dst; +} + +CString +CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 2, --n) { + UInt32 c = decode16(data); + if (c < 0x0000d800 || c > 0x0000dfff) { + toUTF8(dst, c); + } + else if (n == 1) { + // error -- missing second word + } + else if (c >= 0x0000d800 && c <= 0x0000dbff) { + UInt32 c2 = decode16(data); + data += 2; + --n; + if (c2 < 0x0000dc00 || c2 > 0x0000dfff) { + // error -- [d800,dbff] not followed by [dc00,dfff] + } + else { + c = (((c - 0x0000d800) << 10) | (c2 - 0x0000dc00)) + 0x00010000; + toUTF8(dst, c); + } + } + else { + // error -- [dc00,dfff] without leading [d800,dbff] + } + } + + // remove extra trailing nul + if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { + dst.resize(dst.size() - 1); + } + + return dst; +} + +CString +CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n) +{ + // FIXME -- should check that src has no characters over U-0010FFFF + return doUCS4ToUTF8(data, n); +} + +UInt32 +CUnicode::fromUTF8(const UInt8*& data, UInt32& n) +{ + assert(data != NULL); + assert(n != 0); + + // compute character encoding length, checking for overlong + // sequences (i.e. characters that don't use the shortest + // possible encoding). + UInt32 size; + if (data[0] < 0x80) { + // 0xxxxxxx + size = 1; + } + else if (data[0] < 0xc0) { + // 10xxxxxx -- in the middle of a multibyte character. skip + // until we find a start byte and return error. + do { + --n; + ++data; + } while (n > 0 && (data[0] & 0xc0) == 0x80); + return s_invalid; + } + else if (data[0] < 0xe0) { + // 110xxxxx + size = 2; + } + else if (data[0] < 0xf0) { + // 1110xxxx + size = 3; + } + else if (data[0] < 0xf8) { + // 11110xxx + size = 4; + } + else if (data[0] < 0xfc) { + // 111110xx + size = 5; + } + else if (data[0] < 0xfe) { + // 1111110x + size = 6; + } + else { + // invalid sequence. dunno how many bytes to skip so skip one. + --n; + ++data; + return s_invalid; + } + + // make sure we have enough data + if (size > n) { + data += n; + n = 0; + return s_invalid; + } + + // extract character + UInt32 c; + switch (size) { + case 1: + c = static_cast(data[0]); + break; + + case 2: + c = ((static_cast(data[0]) & 0x1f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 3: + c = ((static_cast(data[0]) & 0x0f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[2]) & 0x3f) ); + break; + + case 4: + c = ((static_cast(data[0]) & 0x07) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 5: + c = ((static_cast(data[0]) & 0x03) << 24) | + ((static_cast(data[1]) & 0x3f) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 6: + c = ((static_cast(data[0]) & 0x01) << 30) | + ((static_cast(data[1]) & 0x3f) << 24) | + ((static_cast(data[1]) & 0x3f) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + default: + assert(0 && "invalid size"); + } + + // update parameters + data += size; + n -= size; + + // check for characters that didn't use the smallest possible encoding + static UInt32 s_minChar[] = { + 0, + 0x00000000, + 0x00000080, + 0x00000800, + 0x00010000, + 0x00200000, + 0x04000000 + }; + if (c < s_minChar[size]) { + return s_invalid; + } + + // check that all bytes after the first have the pattern 10xxxxxx. + UInt8 a = 0x80; + switch (size) { + case 6: + a |= data[5]; + // fall through + + case 5: + a |= data[4]; + // fall through + + case 4: + a |= data[3]; + // fall through + + case 3: + a |= data[2]; + // fall through + + case 2: + a |= data[1]; + } + if ((a & 0xc0) != 0x80) { + return s_invalid; + } + + return c; +} + +void +CUnicode::toUTF8(CString& dst, const UInt32 c) +{ + UInt8 data[6]; + + if (c < 0x00000080) { + data[0] = static_cast(c); + dst.append(reinterpret_cast(data), 1); + } + else if (c < 0x00000800) { + data[0] = static_cast((c >> 6) & 0x0000001f) + 0xc0; + data[1] = static_cast(c & 0x0000003f) + 0x80; + dst.append(reinterpret_cast(data), 2); + } + else if (c < 0x00010000) { + data[0] = static_cast((c >> 12) & 0x0000000f) + 0xe0; + data[1] = static_cast((c >> 6) & 0x0000003f) + 0x80; + data[2] = static_cast(c & 0x0000003f) + 0x80; + dst.append(reinterpret_cast(data), 3); + } + else if (c < 0x00200000) { + data[0] = static_cast((c >> 18) & 0x00000007) + 0xf0; + data[1] = static_cast((c >> 12) & 0x0000003f) + 0x80; + data[2] = static_cast((c >> 6) & 0x0000003f) + 0x80; + data[3] = static_cast(c & 0x0000003f) + 0x80; + dst.append(reinterpret_cast(data), 4); + } + else if (c < 0x04000000) { + data[0] = static_cast((c >> 24) & 0x00000003) + 0xf8; + data[1] = static_cast((c >> 18) & 0x0000003f) + 0x80; + data[2] = static_cast((c >> 12) & 0x0000003f) + 0x80; + data[3] = static_cast((c >> 6) & 0x0000003f) + 0x80; + data[4] = static_cast(c & 0x0000003f) + 0x80; + dst.append(reinterpret_cast(data), 5); + } + else if (c < 0x80000000) { + data[0] = static_cast((c >> 30) & 0x00000001) + 0xfc; + data[1] = static_cast((c >> 24) & 0x0000003f) + 0x80; + data[2] = static_cast((c >> 18) & 0x0000003f) + 0x80; + data[3] = static_cast((c >> 12) & 0x0000003f) + 0x80; + data[4] = static_cast((c >> 6) & 0x0000003f) + 0x80; + data[5] = static_cast(c & 0x0000003f) + 0x80; + dst.append(reinterpret_cast(data), 6); + } + else { + // invalid character + } +} diff --git a/base/CUnicode.h b/base/CUnicode.h new file mode 100644 index 00000000..4bcabe60 --- /dev/null +++ b/base/CUnicode.h @@ -0,0 +1,48 @@ +#ifndef CUNICODE_H +#define CUNICODE_H + +#include "CString.h" +#include "BasicTypes.h" +#include + +class CUnicode { +public: + static CString UTF8ToUCS2(const CString&); + static CString UTF8ToUCS4(const CString&); + static CString UTF8ToUTF16(const CString&); + static CString UTF8ToUTF32(const CString&); + + static CString UCS2ToUTF8(const CString&); + static CString UCS4ToUTF8(const CString&); + static CString UTF16ToUTF8(const CString&); + static CString UTF32ToUTF8(const CString&); + + // convert UTF-8 to/from the current locale's encoding + static CString UTF8ToText(const CString&); + static CString textToUTF8(const CString&); + +private: + // convert UTF8 to nul terminated wchar_t string (using whatever + // encoding is native to the platform). caller must delete[] + // the returned string. + static wchar_t* UTF8ToWideChar(const CString&); + + // convert nul terminated wchar_t string (in platform's native + // encoding) to UTF8. + static CString wideCharToUTF8(const wchar_t*); + + // internal conversion to UTF8 + static CString doUCS2ToUTF8(const UInt8* src, UInt32 n); + static CString doUCS4ToUTF8(const UInt8* src, UInt32 n); + static CString doUTF16ToUTF8(const UInt8* src, UInt32 n); + static CString doUTF32ToUTF8(const UInt8* src, UInt32 n); + + // convert characters to/from UTF8 + static UInt32 fromUTF8(const UInt8*& src, UInt32& size); + static void toUTF8(CString& dst, const UInt32 c); + +private: + static UInt32 s_invalid; +}; + +#endif diff --git a/base/Makefile.am b/base/Makefile.am index 07ee18bc..fe67d1ce 100644 --- a/base/Makefile.am +++ b/base/Makefile.am @@ -8,12 +8,14 @@ libbase_a_SOURCES = \ CLog.cpp \ CStopwatch.cpp \ CString.cpp \ + CUnicode.cpp \ XBase.cpp \ BasicTypes.h \ CFunctionJob.h \ CLog.h \ CStopwatch.h \ CString.h \ + CUnicode.h \ IInterface.h \ IJob.h \ TMethodJob.h \ diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index c2cd225e..a0f0de55 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -1,4 +1,7 @@ #include "CXWindowsClipboard.h" +#include "CXWindowsClipboardTextConverter.h" +#include "CXWindowsClipboardUCS2Converter.h" +#include "CXWindowsClipboardUTF8Converter.h" #include "CXWindowsUtil.h" #include "CThread.h" #include "CLog.h" @@ -52,6 +55,20 @@ CXWindowsClipboard::CXWindowsClipboard(Display* display, break; } + // add converters + m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, + "text/plain;charset=UTF-8")); + m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, + "UTF8_STRING")); + m_converters.push_back(new CXWindowsClipboardUCS2Converter(m_display, + "text/plain;charset=ISO-10646-UCS-2")); + m_converters.push_back(new CXWindowsClipboardUCS2Converter(m_display, + "text/unicode")); + m_converters.push_back(new CXWindowsClipboardTextConverter(m_display, + "text/plain")); + m_converters.push_back(new CXWindowsClipboardTextConverter(m_display, + "STRING")); + // we have no data clearCache(); } @@ -59,6 +76,7 @@ CXWindowsClipboard::CXWindowsClipboard(Display* display, CXWindowsClipboard::~CXWindowsClipboard() { clearReplies(); + clearConverters(); } void @@ -124,7 +142,7 @@ CXWindowsClipboard::addSimpleRequest(Window requestor, // handle targets CString data; - Atom type = None; + Atom type = None; int format = 0; if (target == m_atomTargets) { type = getTargetsData(data, &format); @@ -132,9 +150,17 @@ CXWindowsClipboard::addSimpleRequest(Window requestor, else if (target == m_atomTimestamp) { type = getTimestampData(data, &format); } - else if (target == m_atomString || - target == m_atomText) { - type = getStringData(data, &format); + else { + IXWindowsClipboardConverter* converter = getConverter(target); + IClipboard::EFormat clipboardFormat = converter->getFormat(); + if (!m_added[clipboardFormat]) { + type = None; + } + else { + type = converter->getAtom(); + format = converter->getDataSize(); + data = converter->fromIClipboard(m_data[clipboardFormat]); + } } if (type != None) { @@ -334,17 +360,41 @@ CXWindowsClipboard::get(EFormat format) const return m_data[format]; } -IClipboard::EFormat -CXWindowsClipboard::getFormat(Atom src) const +void +CXWindowsClipboard::clearConverters() { - // FIXME -- handle more formats (especially mime-type-like formats - // and various character encodings like unicode). - if (src == m_atomString || - src == m_atomText /*|| - src == m_atomCompoundText*/) { - return IClipboard::kText; + for (ConverterList::iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + delete *index; } - return IClipboard::kNumFormats; + m_converters.clear(); +} + +IXWindowsClipboardConverter* +CXWindowsClipboard::getConverter(Atom target, bool onlyIfNotAdded) const +{ + IXWindowsClipboardConverter* converter = NULL; + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + converter = *index; + if (converter->getAtom() == target) { + break; + } + } + if (converter == NULL) { + log((CLOG_DEBUG1 " no converter for target %d", target)); + return NULL; + } + + // optionally skip already handled targets + if (onlyIfNotAdded) { + if (m_added[converter->getFormat()]) { + log((CLOG_DEBUG1 " skipping handled format %d", converter->getFormat())); + return NULL; + } + } + + return converter; } void @@ -438,44 +488,29 @@ CXWindowsClipboard::icccmFillCache() const Atom* targets = reinterpret_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); for (UInt32 i = 0; i < numTargets; ++i) { - // determine the expected clipboard format + // see if we have a converter for this target Atom target = targets[i]; - IClipboard::EFormat expectedFormat = getFormat(target); - if (expectedFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG1 " no format for target %d", target)); - continue; - } - log((CLOG_DEBUG1 " source target %d -> %d", target, expectedFormat)); - - // skip already handled targets - if (m_added[expectedFormat]) { - log((CLOG_DEBUG1 " skipping handled format %d", expectedFormat)); + IXWindowsClipboardConverter* converter = getConverter(target, true); + if (converter == NULL) { continue; } + // get the data Atom actualTarget; CString targetData; if (!icccmGetSelection(target, &actualTarget, &targetData)) { log((CLOG_DEBUG1 " no data for target", target)); continue; } - logc(actualTarget != target, (CLOG_DEBUG1 " actual target is %d", actualTarget)); - - // use the actual format, not the expected - IClipboard::EFormat actualFormat = getFormat(actualTarget); - if (actualFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG1 " no format for target %d", actualTarget)); - continue; - } - if (m_added[actualFormat]) { - log((CLOG_DEBUG1 " skipping handled format %d", actualFormat)); + if (actualTarget != target) { + log((CLOG_DEBUG1 " expected (%d) and actual (%d) targets differ", target, actualTarget)); continue; } // add to clipboard and note we've done it - m_data[actualFormat] = targetData; - m_added[actualFormat] = true; - log((CLOG_DEBUG " added format %d for target %d", actualFormat, target)); + m_data[converter->getFormat()] = converter->toIClipboard(targetData); + m_added[converter->getFormat()] = true; + log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); } } @@ -676,42 +711,36 @@ CXWindowsClipboard::motifFillCache() continue; } - // determine the expected clipboard format - Atom target = motifFormat->m_type; - IClipboard::EFormat expectedFormat = getFormat(target); - if (expectedFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG1 " no format for target %d", target)); - continue; - } - log((CLOG_DEBUG1 " source target %d -> %d", target, expectedFormat)); - // skip already handled targets - if (m_added[expectedFormat]) { - log((CLOG_DEBUG1 " skipping handled format %d", expectedFormat)); + // see if we have a converter for this target + Atom target = motifFormat->m_type; + IXWindowsClipboardConverter* converter = getConverter(target, true); + if (converter == NULL) { continue; } // get the data (finally) SInt32 length = motifFormat->m_length; sprintf(name, "_MOTIF_CLIP_ITEM_%d", motifFormat->m_data); - Atom atomData = XInternAtom(m_display, name, False); - data = ""; + Atom atomData = XInternAtom(m_display, name, False), atomTarget; + CString targetData; if (!CXWindowsUtil::getWindowProperty(m_display, root, - atomData, &data, - &target, &format, False)) { + atomData, &targetData, + &atomTarget, &format, False)) { + log((CLOG_DEBUG1 " no data for target", target)); continue; } - if (target != atomData) { + if (atomTarget != atomData) { continue; } // truncate data to length specified in the format - data.erase(length); + targetData.erase(length); // add to clipboard and note we've done it - m_data[expectedFormat] = data; - m_added[expectedFormat] = true; - log((CLOG_DEBUG " added format %d for target %d", expectedFormat, motifFormat->m_type)); + m_data[converter->getFormat()] = converter->toIClipboard(targetData); + m_added[converter->getFormat()] = true; + log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); } } @@ -1061,7 +1090,7 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const { assert(format != NULL); - // construct response + // add standard targets Atom atom; atom = m_atomTargets; data.append(reinterpret_cast(&atom), sizeof(Atom)); @@ -1069,11 +1098,17 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const data.append(reinterpret_cast(&atom), sizeof(Atom)); atom = m_atomTimestamp; data.append(reinterpret_cast(&atom), sizeof(Atom)); - if (m_added[kText]) { - atom = m_atomString; - data.append(reinterpret_cast(&atom), sizeof(Atom)); - atom = m_atomText; - data.append(reinterpret_cast(&atom), sizeof(Atom)); + + // add targets we can convert to + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IXWindowsClipboardConverter* converter = *index; + + // skip formats we don't have + if (!m_added[converter->getFormat()]) { + atom = converter->getAtom(); + data.append(reinterpret_cast(&atom), sizeof(Atom)); + } } *format = 32; @@ -1092,21 +1127,6 @@ CXWindowsClipboard::getTimestampData(CString& data, int* format) const return m_atomTimestamp; } -Atom -CXWindowsClipboard::getStringData(CString& data, int* format) const -{ - assert(format != NULL); - - if (m_added[kText]) { - data = m_data[kText]; - *format = 8; - return m_atomString; - } - else { - return None; - } -} - // // CXWindowsClipboard::CICCCMGetClipboard diff --git a/platform/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h index 940ab71c..e738b07e 100644 --- a/platform/CXWindowsClipboard.h +++ b/platform/CXWindowsClipboard.h @@ -5,12 +5,15 @@ #include "ClipboardTypes.h" #include "stdmap.h" #include "stdlist.h" +#include "stdvector.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else # include #endif +class IXWindowsClipboardConverter; + class CXWindowsClipboard : public IClipboard { public: CXWindowsClipboard(Display*, Window, ClipboardID); @@ -51,6 +54,17 @@ public: virtual CString get(EFormat) const; private: + // remove all converters from our list + void clearConverters(); + + // get the converter for a clipboard format. returns NULL if no + // suitable converter. iff onlyIfNotAdded is true then also + // return NULL if a suitable converter was found but we already + // have data of the converter's clipboard format. + IXWindowsClipboardConverter* + getConverter(Atom target, + bool onlyIfNotAdded = false) const; + // convert target atom to clipboard format EFormat getFormat(Atom target) const; @@ -218,9 +232,10 @@ private: // data conversion methods Atom getTargetsData(CString&, int* format) const; Atom getTimestampData(CString&, int* format) const; - Atom getStringData(CString&, int* format) const; private: + typedef std::vector ConverterList; + Display* m_display; Window m_window; ClipboardID m_id; @@ -245,6 +260,9 @@ private: CReplyMap m_replies; CReplyEventMask m_eventMasks; + // clipboard format converters + ConverterList m_converters; + // atoms we'll need Atom m_atomTargets; Atom m_atomMultiple; @@ -263,4 +281,31 @@ private: Atom m_atomGDKSelection; }; +class IXWindowsClipboardConverter : public IInterface { +public: + // accessors + + // return the clipboard format this object converts from/to + virtual IClipboard::EFormat + getFormat() const = 0; + + // return the atom representing the X selection format that + // this object converts from/to + virtual Atom getAtom() const = 0; + + // return the size (in bits) of data elements returned by + // toIClipboard(). + virtual int getDataSize() const = 0; + + // convert from the IClipboard format to the X selection format. + // the input data must be in the IClipboard format returned by + // getFormat(). the return data will be in the X selection + // format returned by getAtom(). + virtual CString fromIClipboard(const CString&) const = 0; + + // convert from the X selection format to the IClipboard format + // (i.e., the reverse of fromIClipboard()). + virtual CString toIClipboard(const CString&) const = 0; +}; + #endif diff --git a/platform/CXWindowsClipboardTextConverter.cpp b/platform/CXWindowsClipboardTextConverter.cpp new file mode 100644 index 00000000..be3b8c93 --- /dev/null +++ b/platform/CXWindowsClipboardTextConverter.cpp @@ -0,0 +1,48 @@ +#include "CXWindowsClipboardTextConverter.h" +#include "CUnicode.h" + +// +// CXWindowsClipboardTextConverter +// + +CXWindowsClipboardTextConverter::CXWindowsClipboardTextConverter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardTextConverter::~CXWindowsClipboardTextConverter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardTextConverter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardTextConverter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardTextConverter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardTextConverter::fromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToText(data); +} + +CString +CXWindowsClipboardTextConverter::toIClipboard(const CString& data) const +{ + return CUnicode::textToUTF8(data); +} diff --git a/platform/CXWindowsClipboardTextConverter.h b/platform/CXWindowsClipboardTextConverter.h new file mode 100644 index 00000000..b73e06a5 --- /dev/null +++ b/platform/CXWindowsClipboardTextConverter.h @@ -0,0 +1,23 @@ +#ifndef CXWINDOWSCLIPBOARDTEXTCONVERTER_H +#define CXWINDOWSCLIPBOARDTEXTCONVERTER_H + +#include "CXWindowsClipboard.h" + +class CXWindowsClipboardTextConverter : public IXWindowsClipboardConverter { +public: + CXWindowsClipboardTextConverter(Display* display, const char* name); + virtual ~CXWindowsClipboardTextConverter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/platform/CXWindowsClipboardUCS2Converter.cpp b/platform/CXWindowsClipboardUCS2Converter.cpp new file mode 100644 index 00000000..1f892f55 --- /dev/null +++ b/platform/CXWindowsClipboardUCS2Converter.cpp @@ -0,0 +1,48 @@ +#include "CXWindowsClipboardUCS2Converter.h" +#include "CUnicode.h" + +// +// CXWindowsClipboardUCS2Converter +// + +CXWindowsClipboardUCS2Converter::CXWindowsClipboardUCS2Converter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardUCS2Converter::~CXWindowsClipboardUCS2Converter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardUCS2Converter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardUCS2Converter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardUCS2Converter::getDataSize() const +{ + return 16; +} + +CString +CXWindowsClipboardUCS2Converter::fromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToUCS2(data); +} + +CString +CXWindowsClipboardUCS2Converter::toIClipboard(const CString& data) const +{ + return CUnicode::UCS2ToUTF8(data); +} diff --git a/platform/CXWindowsClipboardUCS2Converter.h b/platform/CXWindowsClipboardUCS2Converter.h new file mode 100644 index 00000000..1a87c84d --- /dev/null +++ b/platform/CXWindowsClipboardUCS2Converter.h @@ -0,0 +1,23 @@ +#ifndef CXWINDOWSCLIPBOARDUCS2CONVERTER_H +#define CXWINDOWSCLIPBOARDUCS2CONVERTER_H + +#include "CXWindowsClipboard.h" + +class CXWindowsClipboardUCS2Converter : public IXWindowsClipboardConverter { +public: + CXWindowsClipboardUCS2Converter(Display* display, const char* name); + virtual ~CXWindowsClipboardUCS2Converter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/platform/CXWindowsClipboardUTF8Converter.cpp b/platform/CXWindowsClipboardUTF8Converter.cpp new file mode 100644 index 00000000..c461a321 --- /dev/null +++ b/platform/CXWindowsClipboardUTF8Converter.cpp @@ -0,0 +1,47 @@ +#include "CXWindowsClipboardUTF8Converter.h" + +// +// CXWindowsClipboardUTF8Converter +// + +CXWindowsClipboardUTF8Converter::CXWindowsClipboardUTF8Converter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardUTF8Converter::~CXWindowsClipboardUTF8Converter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardUTF8Converter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardUTF8Converter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardUTF8Converter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardUTF8Converter::fromIClipboard(const CString& data) const +{ + return data; +} + +CString +CXWindowsClipboardUTF8Converter::toIClipboard(const CString& data) const +{ + return data; +} diff --git a/platform/CXWindowsClipboardUTF8Converter.h b/platform/CXWindowsClipboardUTF8Converter.h new file mode 100644 index 00000000..ed7313a1 --- /dev/null +++ b/platform/CXWindowsClipboardUTF8Converter.h @@ -0,0 +1,23 @@ +#ifndef CXWINDOWSCLIPBOARDUTF8CONVERTER_H +#define CXWINDOWSCLIPBOARDUTF8CONVERTER_H + +#include "CXWindowsClipboard.h" + +class CXWindowsClipboardUTF8Converter : public IXWindowsClipboardConverter { +public: + CXWindowsClipboardUTF8Converter(Display* display, const char* name); + virtual ~CXWindowsClipboardUTF8Converter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/platform/Makefile.am b/platform/Makefile.am index 704526b0..4257ecbd 100644 --- a/platform/Makefile.am +++ b/platform/Makefile.am @@ -4,22 +4,28 @@ DEPTH = .. # FIXME -- add CUnixPlatform.cpp as an unbuilt source noinst_LIBRARIES = libplatform.a -libplatform_a_SOURCES = \ - CPlatform.cpp \ - CXWindowsClipboard.cpp \ - CXWindowsScreen.cpp \ - CXWindowsScreenSaver.cpp \ - CXWindowsUtil.cpp \ - CPlatform.h \ - CUnixPlatform.h \ - CXWindowsClipboard.h \ - CXWindowsScreen.h \ - CXWindowsScreenSaver.h \ - CXWindowsUtil.h \ - IPlatform.h \ +libplatform_a_SOURCES = \ + CPlatform.cpp \ + CXWindowsClipboard.cpp \ + CXWindowsClipboardTextConverter.cpp \ + CXWindowsClipboardUCS2Converter.cpp \ + CXWindowsClipboardUTF8Converter.cpp \ + CXWindowsScreen.cpp \ + CXWindowsScreenSaver.cpp \ + CXWindowsUtil.cpp \ + CPlatform.h \ + CUnixPlatform.h \ + CXWindowsClipboard.h \ + CXWindowsClipboardTextConverter.h \ + CXWindowsClipboardUCS2Converter.h \ + CXWindowsClipboardUTF8Converter.h \ + CXWindowsScreen.h \ + CXWindowsScreenSaver.h \ + CXWindowsUtil.h \ + IPlatform.h \ $(NULL) -INCLUDES = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/synergy \ +INCLUDES = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/synergy \ $(NULL) diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index 66e35d2a..2c145315 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -13,7 +13,13 @@ public: typedef UInt32 Time; // known clipboard formats. kNumFormats must be last and - // formats must be sequential starting from zero. + // formats must be sequential starting from zero. clipboard + // data set via add() and retrieved via get() is in one of + // these formats. platform dependent clipboard subclasses + // can and should present any suitable formats derivable + // from these formats (e.g. UCS-16 encoded unicode). + // + // kText: UTF-8 encoded unicode (ISO-10646), newline is LF enum EFormat { kText, kNumFormats }; // manipulators From bb966cdd655333b4d5abf718d9287da51373cc7b Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 22 Jul 2002 18:03:44 +0000 Subject: [PATCH 245/807] checkpoint. working on UTF8 clipboard transfer. --- platform/CXWindowsClipboard.cpp | 87 +++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index a0f0de55..135a4b13 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -55,7 +55,7 @@ CXWindowsClipboard::CXWindowsClipboard(Display* display, break; } - // add converters + // add converters, most desired first m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, "text/plain;charset=UTF-8")); m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, @@ -484,14 +484,28 @@ CXWindowsClipboard::icccmFillCache() data.append(reinterpret_cast(&target), sizeof(target)); } - // try getting each format + // try each converter in order (because they're in order of + // preference). const Atom* targets = reinterpret_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); - for (UInt32 i = 0; i < numTargets; ++i) { - // see if we have a converter for this target - Atom target = targets[i]; - IXWindowsClipboardConverter* converter = getConverter(target, true); - if (converter == NULL) { + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IXWindowsClipboardConverter* converter = *index; + + // skip already handled targets + if (m_added[converter->getFormat()]) { + continue; + } + + // see if atom is in target list + Atom target = None; + for (UInt32 i = 0; i < numTargets; ++i) { + if (converter->getAtom() == targets[i]) { + target = targets[i]; + break; + } + } + if (target == None) { continue; } @@ -503,13 +517,19 @@ CXWindowsClipboard::icccmFillCache() continue; } if (actualTarget != target) { - log((CLOG_DEBUG1 " expected (%d) and actual (%d) targets differ", target, actualTarget)); + log((CLOG_DEBUG1 " expeted (%d) and actual (%d) targets differ", target, actualTarget)); continue; } // add to clipboard and note we've done it m_data[converter->getFormat()] = converter->toIClipboard(targetData); m_added[converter->getFormat()] = true; +// XXX +char* name = XGetAtomName(m_display, target); +log((CLOG_INFO "src atom: %d %s", target, name)); +XFree(name); +log((CLOG_INFO "src data size: %d", targetData.size())); +log((CLOG_INFO "utf8 data size: %d", m_data[converter->getFormat()].size())); log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); } } @@ -686,7 +706,8 @@ CXWindowsClipboard::motifFillCache() return; } - // convert each available format + // get the available formats + std::vector motifFormats; for (SInt32 i = 0; i < item->m_numFormats; ++i) { // get Motif format property from the root window sprintf(name, "_MOTIF_CLIP_ITEM_%d", item->m_formats[i]); @@ -711,17 +732,42 @@ CXWindowsClipboard::motifFillCache() continue; } + // save it + motifFormats.push_back(*motifFormat); + } + const UInt32 numMotifFormats = motifFormats.size(); - // see if we have a converter for this target - Atom target = motifFormat->m_type; - IXWindowsClipboardConverter* converter = getConverter(target, true); - if (converter == NULL) { + + + // try each converter in order (because they're in order of + // preference). + const Atom* targets = reinterpret_cast(data.data()); + const UInt32 numTargets = data.size() / sizeof(Atom); + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IXWindowsClipboardConverter* converter = *index; + + // skip already handled targets + if (m_added[converter->getFormat()]) { + continue; + } + + // see if atom is in target list + UInt32 i; + Atom target = None; + for (i = 0; i < numMotifFormats; ++i) { + if (converter->getAtom() == motifFormats[i].m_type) { + target = motifFormats[i].m_type; + break; + } + } + if (target == None) { continue; } // get the data (finally) - SInt32 length = motifFormat->m_length; - sprintf(name, "_MOTIF_CLIP_ITEM_%d", motifFormat->m_data); + SInt32 length = motifFormats[i].m_length; + sprintf(name, "_MOTIF_CLIP_ITEM_%d", motifFormats[i].m_data); Atom atomData = XInternAtom(m_display, name, False), atomTarget; CString targetData; if (!CXWindowsUtil::getWindowProperty(m_display, root, @@ -740,6 +786,12 @@ CXWindowsClipboard::motifFillCache() // add to clipboard and note we've done it m_data[converter->getFormat()] = converter->toIClipboard(targetData); m_added[converter->getFormat()] = true; +// XXX +char* name = XGetAtomName(m_display, target); +log((CLOG_INFO "src atom: %d %s", target, name)); +XFree(name); +log((CLOG_INFO "src data size: %d", targetData.size())); +log((CLOG_INFO "utf8 data size: %d", m_data[converter->getFormat()].size())); log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); } } @@ -1100,6 +1152,8 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const data.append(reinterpret_cast(&atom), sizeof(Atom)); // add targets we can convert to +// XXX +log((CLOG_INFO "targets")); for (ConverterList::const_iterator index = m_converters.begin(); index != m_converters.end(); ++index) { IXWindowsClipboardConverter* converter = *index; @@ -1108,6 +1162,9 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const if (!m_added[converter->getFormat()]) { atom = converter->getAtom(); data.append(reinterpret_cast(&atom), sizeof(Atom)); +char* name = XGetAtomName(m_display, atom); +log((CLOG_INFO " %d %s", atom, name)); +XFree(name); } } From c4f21ce29b67d22a2ef28aa6e06a6d8ee4a91484 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 22 Jul 2002 18:17:21 +0000 Subject: [PATCH 246/807] checkpoint. more UTF8 clipboard stuff. --- platform/CXWindowsClipboard.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 135a4b13..caff177a 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -151,15 +151,20 @@ CXWindowsClipboard::addSimpleRequest(Window requestor, type = getTimestampData(data, &format); } else { +char* name = XGetAtomName(m_display, target); +log((CLOG_INFO "request target: %d %s", target, name)); +XFree(name); IXWindowsClipboardConverter* converter = getConverter(target); - IClipboard::EFormat clipboardFormat = converter->getFormat(); - if (!m_added[clipboardFormat]) { - type = None; - } - else { - type = converter->getAtom(); - format = converter->getDataSize(); - data = converter->fromIClipboard(m_data[clipboardFormat]); + if (converter != NULL) { +log((CLOG_INFO "found converter")); + IClipboard::EFormat clipboardFormat = converter->getFormat(); +log((CLOG_INFO "clipboard format: %d", clipboardFormat)); + if (m_added[clipboardFormat]) { +log((CLOG_INFO "added")); + type = converter->getAtom(); + format = converter->getDataSize(); + data = converter->fromIClipboard(m_data[clipboardFormat]); + } } } @@ -1152,19 +1157,14 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const data.append(reinterpret_cast(&atom), sizeof(Atom)); // add targets we can convert to -// XXX -log((CLOG_INFO "targets")); for (ConverterList::const_iterator index = m_converters.begin(); index != m_converters.end(); ++index) { IXWindowsClipboardConverter* converter = *index; // skip formats we don't have - if (!m_added[converter->getFormat()]) { + if (m_added[converter->getFormat()]) { atom = converter->getAtom(); data.append(reinterpret_cast(&atom), sizeof(Atom)); -char* name = XGetAtomName(m_display, atom); -log((CLOG_INFO " %d %s", atom, name)); -XFree(name); } } From fcd99c95104fda007001301ee455bcd15973cda0 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 22 Jul 2002 18:46:57 +0000 Subject: [PATCH 247/807] checkpoint. more UTF8 clipboard stuff. --- base/CUnicode.cpp | 4 ++-- platform/CXWindowsClipboard.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp index 48834192..8925752d 100644 --- a/base/CUnicode.cpp +++ b/base/CUnicode.cpp @@ -54,7 +54,7 @@ CUnicode::UTF8ToUCS2(const CString& src) const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); - if (c != s_invalid && c < 0x0000ffff) { + if (c != s_invalid && c < 0x00010000) { UInt16 ucs2 = static_cast(c); dst.append(reinterpret_cast(&ucs2), 2); } @@ -223,7 +223,7 @@ CUnicode::UTF8ToWideChar(const CString& src) // copy to a wchar_t array wchar_t* dst = new wchar_t[size]; - ::memcpy(dst, src.data(), sizeof(wchar_t) * size); + ::memcpy(dst, tmp.data(), sizeof(wchar_t) * size); return dst; } diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index caff177a..e1f19eff 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -164,6 +164,8 @@ log((CLOG_INFO "added")); type = converter->getAtom(); format = converter->getDataSize(); data = converter->fromIClipboard(m_data[clipboardFormat]); +log((CLOG_INFO " src: (%d) %s", m_data[clipboardFormat].size(), m_data[clipboardFormat].c_str())); +log((CLOG_INFO " dst: (%d) %s", data.size(), data.c_str())); } } } From 16cc05d56bd9d75b857fe2c952b8381ce74d8913 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 09:33:50 +0000 Subject: [PATCH 248/807] checkpoint. more UTF8 clipboard testing. --- base/CUnicode.cpp | 210 +++++++++++++++++++++++--------- base/CUnicode.h | 3 +- platform/CXWindowsClipboard.cpp | 51 ++++---- 3 files changed, 185 insertions(+), 79 deletions(-) diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp index 8925752d..ccabd227 100644 --- a/base/CUnicode.cpp +++ b/base/CUnicode.cpp @@ -1,4 +1,5 @@ #include "CUnicode.h" +#include #include // @@ -39,7 +40,8 @@ decode32(const UInt8* n) // CUnicode // -UInt32 CUnicode::s_invalid = 0x0000ffff; +UInt32 CUnicode::s_invalid = 0x0000ffff; +UInt32 CUnicode::s_replacement = 0x0000fffd; CString CUnicode::UTF8ToUCS2(const CString& src) @@ -54,10 +56,11 @@ CUnicode::UTF8ToUCS2(const CString& src) const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); - if (c != s_invalid && c < 0x00010000) { - UInt16 ucs2 = static_cast(c); - dst.append(reinterpret_cast(&ucs2), 2); + if (c == s_invalid || c >= 0x00010000) { + c = s_replacement; } + UInt16 ucs2 = static_cast(c); + dst.append(reinterpret_cast(&ucs2), 2); } return dst; @@ -76,9 +79,10 @@ CUnicode::UTF8ToUCS4(const CString& src) const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); - if (c != s_invalid) { - dst.append(reinterpret_cast(&c), 4); + if (c == s_invalid) { + c = s_replacement; } + dst.append(reinterpret_cast(&c), 4); } return dst; @@ -97,18 +101,19 @@ CUnicode::UTF8ToUTF16(const CString& src) const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); - if (c != s_invalid && c < 0x0010ffff) { - if (c < 0x00010000) { - UInt16 ucs2 = static_cast(c); - dst.append(reinterpret_cast(&ucs2), 2); - } - else { - c -= 0x00010000; - UInt16 utf16h = static_cast(c >> 10) + 0xd800; - UInt16 utf16l = (static_cast(c) & 0x03ff) + 0xdc00; - dst.append(reinterpret_cast(&utf16h), 2); - dst.append(reinterpret_cast(&utf16l), 2); - } + if (c == s_invalid || c >= 0x00110000) { + c = s_replacement; + } + if (c < 0x00010000) { + UInt16 ucs2 = static_cast(c); + dst.append(reinterpret_cast(&ucs2), 2); + } + else { + c -= 0x00010000; + UInt16 utf16h = static_cast(c >> 10) + 0xd800; + UInt16 utf16l = (static_cast(c) & 0x03ff) + 0xdc00; + dst.append(reinterpret_cast(&utf16h), 2); + dst.append(reinterpret_cast(&utf16l), 2); } } @@ -118,8 +123,23 @@ CUnicode::UTF8ToUTF16(const CString& src) CString CUnicode::UTF8ToUTF32(const CString& src) { - // FIXME -- should ensure dst has no characters over U-0010FFFF - return UTF8ToUCS4(src); + // get size of input string and reserve some space in output. + // include UTF8's nul terminator. + UInt32 n = src.size() + 1; + CString dst; + dst.reserve(4 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c == s_invalid || c >= 0x00110000) { + c = s_replacement; + } + dst.append(reinterpret_cast(&c), 4); + } + + return dst; } CString @@ -157,24 +177,48 @@ CUnicode::UTF8ToText(const CString& src) wchar_t* tmp = UTF8ToWideChar(src); // get length of multibyte string + size_t len = 0; + char mbc[MB_LEN_MAX]; mbstate_t state; memset(&state, 0, sizeof(state)); - const wchar_t* scratch = tmp; - size_t len = wcsrtombs(NULL, &scratch, 0, &state); - if (len == (size_t)-1) { - // invalid character in src - delete[] tmp; - return CString(); + for (const wchar_t* scan = tmp; *scan != 0; ++scan) { + size_t mblen = wcrtomb(mbc, *scan, &state); + if (mblen == -1) { + // unconvertable character + len += 1; + } + else { + len += mblen; + } } + // check if state is in initial state. if not then count the + // bytes for returning it to the initial state. + if (mbsinit(&state) == 0) { + len += wcrtomb(mbc, L'\0', &state) - 1; + } + assert(mbsinit(&state) != 0); + + // allocate multibyte string + char* mbs = new char[len + 1]; + // convert to multibyte - scratch = tmp; - char* dst = new char[len + 1]; - wcsrtombs(dst, &scratch, len + 1, &state); - CString text(dst); + char* dst = mbs; + for (const wchar_t* scan = tmp; *scan != 0; ++scan) { + size_t mblen = wcrtomb(dst, *scan, &state); + if (mblen == -1) { + // unconvertable character + *dst++ = '?'; + } + else { + dst += len; + } + } + *dst = '\0'; + CString text(mbs); // clean up - delete[] dst; + delete[] mbs; delete[] tmp; return text; @@ -297,6 +341,7 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n) } else if (n == 1) { // error -- missing second word + toUTF8(dst, s_replacement); } else if (c >= 0x0000d800 && c <= 0x0000dbff) { UInt32 c2 = decode16(data); @@ -304,6 +349,7 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n) --n; if (c2 < 0x0000dc00 || c2 > 0x0000dfff) { // error -- [d800,dbff] not followed by [dc00,dfff] + toUTF8(dst, s_replacement); } else { c = (((c - 0x0000d800) << 10) | (c2 - 0x0000dc00)) + 0x00010000; @@ -312,6 +358,7 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n) } else { // error -- [dc00,dfff] without leading [d800,dbff] + toUTF8(dst, s_replacement); } } @@ -326,8 +373,25 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n) CString CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n) { - // FIXME -- should check that src has no characters over U-0010FFFF - return doUCS4ToUTF8(data, n); + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 4, --n) { + UInt32 c = decode32(data); + if (c >= 0x00110000) { + c = s_replacement; + } + toUTF8(dst, c); + } + + // remove extra trailing nul + if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { + dst.resize(dst.size() - 1); + } + + return dst; } UInt32 @@ -433,10 +497,54 @@ CUnicode::fromUTF8(const UInt8*& data, UInt32& n) assert(0 && "invalid size"); } + // check that all bytes after the first have the pattern 10xxxxxx. + // truncated sequences are treated as a single malformed character. + bool truncated = false; + switch (size) { + case 6: + if ((data[5] & 0xc0) != 0x80) { + truncated = true; + size = 5; + } + // fall through + + case 5: + if ((data[4] & 0xc0) != 0x80) { + truncated = true; + size = 4; + } + // fall through + + case 4: + if ((data[3] & 0xc0) != 0x80) { + truncated = true; + size = 3; + } + // fall through + + case 3: + if ((data[2] & 0xc0) != 0x80) { + truncated = true; + size = 2; + } + // fall through + + case 2: + if ((data[1] & 0xc0) != 0x80) { + truncated = true; + size = 1; + } + } + // update parameters data += size; n -= size; + // invalid if sequence was truncated + if (truncated) { + return s_invalid; + } + // check for characters that didn't use the smallest possible encoding static UInt32 s_minChar[] = { 0, @@ -451,29 +559,11 @@ CUnicode::fromUTF8(const UInt8*& data, UInt32& n) return s_invalid; } - // check that all bytes after the first have the pattern 10xxxxxx. - UInt8 a = 0x80; - switch (size) { - case 6: - a |= data[5]; - // fall through - - case 5: - a |= data[4]; - // fall through - - case 4: - a |= data[3]; - // fall through - - case 3: - a |= data[2]; - // fall through - - case 2: - a |= data[1]; + // check for characters not in ISO-10646 + if (c >= 0x0000d800 && c <= 0x0000dfff) { + return s_invalid; } - if ((a & 0xc0) != 0x80) { + if (c >= 0x0000fffe && c <= 0x0000ffff) { return s_invalid; } @@ -481,10 +571,16 @@ CUnicode::fromUTF8(const UInt8*& data, UInt32& n) } void -CUnicode::toUTF8(CString& dst, const UInt32 c) +CUnicode::toUTF8(CString& dst, UInt32 c) { UInt8 data[6]; + // handle characters outside the valid range + if (c >= 0x80000000) { + c = s_replacement; + } + + // convert to UTF-8 if (c < 0x00000080) { data[0] = static_cast(c); dst.append(reinterpret_cast(data), 1); @@ -525,6 +621,6 @@ CUnicode::toUTF8(CString& dst, const UInt32 c) dst.append(reinterpret_cast(data), 6); } else { - // invalid character + assert(0 && "character out of range"); } } diff --git a/base/CUnicode.h b/base/CUnicode.h index 4bcabe60..81ff95d7 100644 --- a/base/CUnicode.h +++ b/base/CUnicode.h @@ -39,10 +39,11 @@ private: // convert characters to/from UTF8 static UInt32 fromUTF8(const UInt8*& src, UInt32& size); - static void toUTF8(CString& dst, const UInt32 c); + static void toUTF8(CString& dst, UInt32 c); private: static UInt32 s_invalid; + static UInt32 s_replacement; }; #endif diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index e1f19eff..76c766bf 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -161,11 +161,16 @@ log((CLOG_INFO "found converter")); log((CLOG_INFO "clipboard format: %d", clipboardFormat)); if (m_added[clipboardFormat]) { log((CLOG_INFO "added")); - type = converter->getAtom(); - format = converter->getDataSize(); - data = converter->fromIClipboard(m_data[clipboardFormat]); + try { + data = converter->fromIClipboard(m_data[clipboardFormat]); + format = converter->getDataSize(); + type = converter->getAtom(); log((CLOG_INFO " src: (%d) %s", m_data[clipboardFormat].size(), m_data[clipboardFormat].c_str())); log((CLOG_INFO " dst: (%d) %s", data.size(), data.c_str())); + } + catch (...) { + // ignore -- cannot convert + } } } } @@ -529,15 +534,17 @@ CXWindowsClipboard::icccmFillCache() } // add to clipboard and note we've done it - m_data[converter->getFormat()] = converter->toIClipboard(targetData); - m_added[converter->getFormat()] = true; -// XXX -char* name = XGetAtomName(m_display, target); -log((CLOG_INFO "src atom: %d %s", target, name)); -XFree(name); -log((CLOG_INFO "src data size: %d", targetData.size())); -log((CLOG_INFO "utf8 data size: %d", m_data[converter->getFormat()].size())); - log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); + IClipboard::EFormat format = converter->getFormat(); + try { + m_data[format] = converter->toIClipboard(targetData); + if (!m_data[format].empty()) { + m_added[format] = true; + log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); + } + } + catch (...) { + // ignore -- could not convert data + } } } @@ -791,15 +798,17 @@ CXWindowsClipboard::motifFillCache() targetData.erase(length); // add to clipboard and note we've done it - m_data[converter->getFormat()] = converter->toIClipboard(targetData); - m_added[converter->getFormat()] = true; -// XXX -char* name = XGetAtomName(m_display, target); -log((CLOG_INFO "src atom: %d %s", target, name)); -XFree(name); -log((CLOG_INFO "src data size: %d", targetData.size())); -log((CLOG_INFO "utf8 data size: %d", m_data[converter->getFormat()].size())); - log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); + IClipboard::EFormat format = converter->getFormat(); + try { + m_data[format] = converter->toIClipboard(targetData); + if (!m_data[format].empty()) { + m_added[format] = true; + log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); + } + } + catch (...) { + // ignore -- could not convert data + } } } From 1f5cb6a96faa685a26307130f7df694c524a8ccb Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 11:36:18 +0000 Subject: [PATCH 249/807] checkpoint. more UTF8 clipboard stuff. --- base/CUnicode.cpp | 268 ++++++++++++++----- base/CUnicode.h | 47 ++-- platform/CXWindowsClipboard.cpp | 34 +-- platform/CXWindowsClipboardTextConverter.cpp | 14 +- 4 files changed, 249 insertions(+), 114 deletions(-) diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp index ccabd227..45a3c7e8 100644 --- a/base/CUnicode.cpp +++ b/base/CUnicode.cpp @@ -36,6 +36,26 @@ decode32(const UInt8* n) return c.n32; } +inline +static +void +resetError(bool* errors) +{ + if (errors != NULL) { + *errors = false; + } +} + +inline +static +void +setError(bool* errors) +{ + if (errors != NULL) { + *errors = true; + } +} + // // CUnicode // @@ -43,9 +63,25 @@ decode32(const UInt8* n) UInt32 CUnicode::s_invalid = 0x0000ffff; UInt32 CUnicode::s_replacement = 0x0000fffd; -CString -CUnicode::UTF8ToUCS2(const CString& src) +bool +CUnicode::isUTF8(const CString& src) { + // convert and test each character + const UInt8* data = reinterpret_cast(src.c_str()); + for (UInt32 n = src.size(); n > 0; ) { + if (fromUTF8(data, n) == s_invalid) { + return false; + } + } + return true; +} + +CString +CUnicode::UTF8ToUCS2(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + // get size of input string and reserve some space in output. // include UTF8's nul terminator. UInt32 n = src.size() + 1; @@ -56,7 +92,11 @@ CUnicode::UTF8ToUCS2(const CString& src) const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); - if (c == s_invalid || c >= 0x00010000) { + if (c == s_invalid) { + c = s_replacement; + } + else if (c >= 0x00010000) { + setError(errors); c = s_replacement; } UInt16 ucs2 = static_cast(c); @@ -67,8 +107,11 @@ CUnicode::UTF8ToUCS2(const CString& src) } CString -CUnicode::UTF8ToUCS4(const CString& src) +CUnicode::UTF8ToUCS4(const CString& src, bool* errors) { + // default to success + resetError(errors); + // get size of input string and reserve some space in output. // include UTF8's nul terminator. UInt32 n = src.size() + 1; @@ -89,8 +132,11 @@ CUnicode::UTF8ToUCS4(const CString& src) } CString -CUnicode::UTF8ToUTF16(const CString& src) +CUnicode::UTF8ToUTF16(const CString& src, bool* errors) { + // default to success + resetError(errors); + // get size of input string and reserve some space in output. // include UTF8's nul terminator. UInt32 n = src.size() + 1; @@ -101,7 +147,11 @@ CUnicode::UTF8ToUTF16(const CString& src) const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); - if (c == s_invalid || c >= 0x00110000) { + if (c == s_invalid) { + c = s_replacement; + } + else if (c >= 0x00110000) { + setError(errors); c = s_replacement; } if (c < 0x00010000) { @@ -121,8 +171,11 @@ CUnicode::UTF8ToUTF16(const CString& src) } CString -CUnicode::UTF8ToUTF32(const CString& src) +CUnicode::UTF8ToUTF32(const CString& src, bool* errors) { + // default to success + resetError(errors); + // get size of input string and reserve some space in output. // include UTF8's nul terminator. UInt32 n = src.size() + 1; @@ -133,7 +186,11 @@ CUnicode::UTF8ToUTF32(const CString& src) const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); - if (c == s_invalid || c >= 0x00110000) { + if (c == s_invalid) { + c = s_replacement; + } + else if (c >= 0x00110000) { + setError(errors); c = s_replacement; } dst.append(reinterpret_cast(&c), 4); @@ -143,38 +200,13 @@ CUnicode::UTF8ToUTF32(const CString& src) } CString -CUnicode::UCS2ToUTF8(const CString& src) +CUnicode::UTF8ToText(const CString& src, bool* errors) { - UInt32 n = src.size() >> 1; - return doUCS2ToUTF8(reinterpret_cast(src.data()), n); -} + // default to success + resetError(errors); -CString -CUnicode::UCS4ToUTF8(const CString& src) -{ - UInt32 n = src.size() >> 2; - return doUCS4ToUTF8(reinterpret_cast(src.data()), n); -} - -CString -CUnicode::UTF16ToUTF8(const CString& src) -{ - UInt32 n = src.size() >> 1; - return doUTF16ToUTF8(reinterpret_cast(src.data()), n); -} - -CString -CUnicode::UTF32ToUTF8(const CString& src) -{ - UInt32 n = src.size() >> 2; - return doUTF32ToUTF8(reinterpret_cast(src.data()), n); -} - -CString -CUnicode::UTF8ToText(const CString& src) -{ // convert to wide char - wchar_t* tmp = UTF8ToWideChar(src); + wchar_t* tmp = UTF8ToWideChar(src, errors); // get length of multibyte string size_t len = 0; @@ -185,6 +217,7 @@ CUnicode::UTF8ToText(const CString& src) size_t mblen = wcrtomb(mbc, *scan, &state); if (mblen == -1) { // unconvertable character + setError(errors); len += 1; } else { @@ -225,34 +258,116 @@ CUnicode::UTF8ToText(const CString& src) } CString -CUnicode::textToUTF8(const CString& src) +CUnicode::UCS2ToUTF8(const CString& src, bool* errors) { - // get length of wide char string + UInt32 n = src.size() >> 1; + return doUCS2ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::UCS4ToUTF8(const CString& src, bool* errors) +{ + UInt32 n = src.size() >> 2; + return doUCS4ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::UTF16ToUTF8(const CString& src, bool* errors) +{ + UInt32 n = src.size() >> 1; + return doUTF16ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::UTF32ToUTF8(const CString& src, bool* errors) +{ + UInt32 n = src.size() >> 2; + return doUTF32ToUTF8(reinterpret_cast(src.data()), n, errors); +} + +CString +CUnicode::textToUTF8(const CString& src, bool* errors) +{ + // default to success + resetError(errors); + + // get length of multibyte string + UInt32 n = src.size(); + size_t len = 0; mbstate_t state; memset(&state, 0, sizeof(state)); - const char* scratch = src.c_str(); - size_t len = mbsrtowcs(NULL, &scratch, 0, &state); - if (len == (size_t)-1) { - // invalid character in src - return CString(); + for (const char* scan = src.c_str(); n > 0 && *scan != 0; ) { + size_t mblen = mbrtowc(NULL, scan, n, &state); + switch (mblen) { + case (size_t)2: + // incomplete last character. convert to unknown character. + setError(errors); + len += 1; + n = 0; + break; + + case (size_t)1: + // invalid character. count one unknown character and + // start at the next byte. + setError(errors); + len += 1; + scan += 1; + n -= 1; + break; + + default: + // normal character + len += 1; + scan += mblen; + n -= mblen; + break; + } } + memset(&state, 0, sizeof(state)); + + // allocate wide character string + wchar_t* wcs = new wchar_t[len + 1]; // convert multibyte to wide char - scratch = src.c_str(); - wchar_t* dst = new wchar_t[len + 1]; - mbsrtowcs(dst, &scratch, len + 1, &state); + n = src.size(); + wchar_t* dst = wcs; + for (const char* scan = src.c_str(); n > 0 && *scan != 0; ++dst) { + size_t mblen = mbrtowc(dst, scan, n, &state); + switch (mblen) { + case (size_t)2: + // incomplete character. convert to unknown character. + *dst = (wchar_t)0xfffd; + n = 0; + break; + + case (size_t)1: + // invalid character. count one unknown character and + // start at the next byte. + scan += 1; + n -= 1; + *dst = (wchar_t)0xfffd; + break; + + default: + // normal character + scan += mblen; + n -= mblen; + break; + } + } + *dst = L'\0'; // convert to UTF8 - CString utf8 = wideCharToUTF8(dst); + CString utf8 = wideCharToUTF8(wcs, errors); // clean up - delete[] dst; + delete[] wcs; return utf8; } wchar_t* -CUnicode::UTF8ToWideChar(const CString& src) +CUnicode::UTF8ToWideChar(const CString& src, bool* errors) { // convert to platform's wide character encoding. // note -- this must include a wide nul character (independent of @@ -272,21 +387,26 @@ CUnicode::UTF8ToWideChar(const CString& src) } CString -CUnicode::wideCharToUTF8(const wchar_t* src) +CUnicode::wideCharToUTF8(const wchar_t* src, bool* errors) { // convert from platform's wide character encoding. // note -- this must include a wide nul character (independent of // the CString's nul character). #if WINDOWS_LIKE - return doUCS16ToUTF8(reinterpret_cast(src), wcslen(src)); + return doUCS16ToUTF8(reinterpret_cast(src), + wcslen(src), errors); #elif UNIX_LIKE - return doUCS4ToUTF8(reinterpret_cast(src), wcslen(src)); + return doUCS4ToUTF8(reinterpret_cast(src), + wcslen(src), errors); #endif } CString -CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n) +CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors) { + // default to success + resetError(errors); + // make some space CString dst; dst.reserve(n); @@ -294,7 +414,7 @@ CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n) // convert each character for (; n > 0; data += 2, --n) { UInt32 c = decode16(data); - toUTF8(dst, c); + toUTF8(dst, c, errors); } // remove extra trailing nul @@ -306,8 +426,11 @@ CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n) } CString -CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n) +CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors) { + // default to success + resetError(errors); + // make some space CString dst; dst.reserve(n); @@ -315,7 +438,7 @@ CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n) // convert each character for (; n > 0; data += 4, --n) { UInt32 c = decode32(data); - toUTF8(dst, c); + toUTF8(dst, c, errors); } // remove extra trailing nul @@ -327,8 +450,11 @@ CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n) } CString -CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n) +CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors) { + // default to success + resetError(errors); + // make some space CString dst; dst.reserve(n); @@ -337,11 +463,12 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n) for (; n > 0; data += 2, --n) { UInt32 c = decode16(data); if (c < 0x0000d800 || c > 0x0000dfff) { - toUTF8(dst, c); + toUTF8(dst, c, errors); } else if (n == 1) { // error -- missing second word - toUTF8(dst, s_replacement); + setError(errors); + toUTF8(dst, s_replacement, NULL); } else if (c >= 0x0000d800 && c <= 0x0000dbff) { UInt32 c2 = decode16(data); @@ -349,16 +476,18 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n) --n; if (c2 < 0x0000dc00 || c2 > 0x0000dfff) { // error -- [d800,dbff] not followed by [dc00,dfff] - toUTF8(dst, s_replacement); + setError(errors); + toUTF8(dst, s_replacement, NULL); } else { c = (((c - 0x0000d800) << 10) | (c2 - 0x0000dc00)) + 0x00010000; - toUTF8(dst, c); + toUTF8(dst, c, errors); } } else { // error -- [dc00,dfff] without leading [d800,dbff] - toUTF8(dst, s_replacement); + setError(errors); + toUTF8(dst, s_replacement, NULL); } } @@ -371,8 +500,11 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n) } CString -CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n) +CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors) { + // default to success + resetError(errors); + // make some space CString dst; dst.reserve(n); @@ -381,9 +513,10 @@ CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n) for (; n > 0; data += 4, --n) { UInt32 c = decode32(data); if (c >= 0x00110000) { + setError(errors); c = s_replacement; } - toUTF8(dst, c); + toUTF8(dst, c, errors); } // remove extra trailing nul @@ -571,12 +704,13 @@ CUnicode::fromUTF8(const UInt8*& data, UInt32& n) } void -CUnicode::toUTF8(CString& dst, UInt32 c) +CUnicode::toUTF8(CString& dst, UInt32 c, bool* errors) { UInt8 data[6]; // handle characters outside the valid range - if (c >= 0x80000000) { + if ((c >= 0x0000d800 && c <= 0x0000dfff) || c >= 0x80000000) { + setError(errors); c = s_replacement; } diff --git a/base/CUnicode.h b/base/CUnicode.h index 81ff95d7..2676e086 100644 --- a/base/CUnicode.h +++ b/base/CUnicode.h @@ -7,39 +7,50 @@ class CUnicode { public: - static CString UTF8ToUCS2(const CString&); - static CString UTF8ToUCS4(const CString&); - static CString UTF8ToUTF16(const CString&); - static CString UTF8ToUTF32(const CString&); + // returns true iff the string contains a valid sequence of UTF-8 + // encoded characters. + static bool isUTF8(const CString&); - static CString UCS2ToUTF8(const CString&); - static CString UCS4ToUTF8(const CString&); - static CString UTF16ToUTF8(const CString&); - static CString UTF32ToUTF8(const CString&); + // convert from UTF-8 encoding to other encodings. if errors is + // not NULL then it gets true if any characters could not be + // encoded in the target encoding and false otherwise. note + // that decoding errors do not set errors to error. UTF8ToText() + // converts to the current locale's (multibyte) encoding. + static CString UTF8ToUCS2(const CString&, bool* errors = NULL); + static CString UTF8ToUCS4(const CString&, bool* errors = NULL); + static CString UTF8ToUTF16(const CString&, bool* errors = NULL); + static CString UTF8ToUTF32(const CString&, bool* errors = NULL); + static CString UTF8ToText(const CString&, bool* errors = NULL); - // convert UTF-8 to/from the current locale's encoding - static CString UTF8ToText(const CString&); - static CString textToUTF8(const CString&); + // convert from some encoding to UTF-8. if errors is not NULL + // then it gets true if any characters could not be decoded and + // false otherwise. textToUTF8() converts from the current + // locale's (multibyte) encoding. + static CString UCS2ToUTF8(const CString&, bool* errors = NULL); + static CString UCS4ToUTF8(const CString&, bool* errors = NULL); + static CString UTF16ToUTF8(const CString&, bool* errors = NULL); + static CString UTF32ToUTF8(const CString&, bool* errors = NULL); + static CString textToUTF8(const CString&, bool* errors = NULL); private: // convert UTF8 to nul terminated wchar_t string (using whatever // encoding is native to the platform). caller must delete[] // the returned string. - static wchar_t* UTF8ToWideChar(const CString&); + static wchar_t* UTF8ToWideChar(const CString&, bool* errors); // convert nul terminated wchar_t string (in platform's native // encoding) to UTF8. - static CString wideCharToUTF8(const wchar_t*); + static CString wideCharToUTF8(const wchar_t*, bool* errors); // internal conversion to UTF8 - static CString doUCS2ToUTF8(const UInt8* src, UInt32 n); - static CString doUCS4ToUTF8(const UInt8* src, UInt32 n); - static CString doUTF16ToUTF8(const UInt8* src, UInt32 n); - static CString doUTF32ToUTF8(const UInt8* src, UInt32 n); + static CString doUCS2ToUTF8(const UInt8* src, UInt32 n, bool* errors); + static CString doUCS4ToUTF8(const UInt8* src, UInt32 n, bool* errors); + static CString doUTF16ToUTF8(const UInt8* src, UInt32 n, bool* errors); + static CString doUTF32ToUTF8(const UInt8* src, UInt32 n, bool* errors); // convert characters to/from UTF8 static UInt32 fromUTF8(const UInt8*& src, UInt32& size); - static void toUTF8(CString& dst, UInt32 c); + static void toUTF8(CString& dst, UInt32 c, bool* errors); private: static UInt32 s_invalid; diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 76c766bf..75efae08 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -151,22 +151,14 @@ CXWindowsClipboard::addSimpleRequest(Window requestor, type = getTimestampData(data, &format); } else { -char* name = XGetAtomName(m_display, target); -log((CLOG_INFO "request target: %d %s", target, name)); -XFree(name); IXWindowsClipboardConverter* converter = getConverter(target); if (converter != NULL) { -log((CLOG_INFO "found converter")); IClipboard::EFormat clipboardFormat = converter->getFormat(); -log((CLOG_INFO "clipboard format: %d", clipboardFormat)); if (m_added[clipboardFormat]) { -log((CLOG_INFO "added")); try { data = converter->fromIClipboard(m_data[clipboardFormat]); format = converter->getDataSize(); type = converter->getAtom(); -log((CLOG_INFO " src: (%d) %s", m_data[clipboardFormat].size(), m_data[clipboardFormat].c_str())); -log((CLOG_INFO " dst: (%d) %s", data.size(), data.c_str())); } catch (...) { // ignore -- cannot convert @@ -535,16 +527,9 @@ CXWindowsClipboard::icccmFillCache() // add to clipboard and note we've done it IClipboard::EFormat format = converter->getFormat(); - try { - m_data[format] = converter->toIClipboard(targetData); - if (!m_data[format].empty()) { - m_added[format] = true; - log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); - } - } - catch (...) { - // ignore -- could not convert data - } + m_data[format] = converter->toIClipboard(targetData); + m_added[format] = true; + log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); } } @@ -799,16 +784,9 @@ CXWindowsClipboard::motifFillCache() // add to clipboard and note we've done it IClipboard::EFormat format = converter->getFormat(); - try { - m_data[format] = converter->toIClipboard(targetData); - if (!m_data[format].empty()) { - m_added[format] = true; - log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); - } - } - catch (...) { - // ignore -- could not convert data - } + m_data[format] = converter->toIClipboard(targetData); + m_added[format] = true; + log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); } } diff --git a/platform/CXWindowsClipboardTextConverter.cpp b/platform/CXWindowsClipboardTextConverter.cpp index be3b8c93..ae791730 100644 --- a/platform/CXWindowsClipboardTextConverter.cpp +++ b/platform/CXWindowsClipboardTextConverter.cpp @@ -44,5 +44,17 @@ CXWindowsClipboardTextConverter::fromIClipboard(const CString& data) const CString CXWindowsClipboardTextConverter::toIClipboard(const CString& data) const { - return CUnicode::textToUTF8(data); + // convert to UTF-8 + bool errors; + CString utf8 = CUnicode::textToUTF8(data, &errors); + + // if there were decoding errors then, to support old applications + // that don't understand UTF-8 but can report the exact binary + // UTF-8 representation, see if the data appears to be UTF-8. if + // so then use it as is. + if (errors && CUnicode::isUTF8(data)) { + return data; + } + + return utf8; } From e93a12868d36f353bb6a1ca9cd076561b0c80fb8 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 11:42:54 +0000 Subject: [PATCH 250/807] checkpoint. fixed cases for mbrtowc (was using 1 and 2 instead of -1 and -2). --- base/CUnicode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp index 45a3c7e8..a4a07038 100644 --- a/base/CUnicode.cpp +++ b/base/CUnicode.cpp @@ -299,14 +299,14 @@ CUnicode::textToUTF8(const CString& src, bool* errors) for (const char* scan = src.c_str(); n > 0 && *scan != 0; ) { size_t mblen = mbrtowc(NULL, scan, n, &state); switch (mblen) { - case (size_t)2: + case (size_t)-2: // incomplete last character. convert to unknown character. setError(errors); len += 1; n = 0; break; - case (size_t)1: + case (size_t)-1: // invalid character. count one unknown character and // start at the next byte. setError(errors); From 2fa9b263f90f7d30559b3a1c5997870d041033ee Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 11:51:13 +0000 Subject: [PATCH 251/807] checkpoint. fixed the other cases in the same function as the previous checkin. also prevented the errors flag from getting reset after the multibyte to wide character conversion. --- base/CUnicode.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp index a4a07038..5a677874 100644 --- a/base/CUnicode.cpp +++ b/base/CUnicode.cpp @@ -260,6 +260,10 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) CString CUnicode::UCS2ToUTF8(const CString& src, bool* errors) { + // default to success + resetError(errors); + + // convert UInt32 n = src.size() >> 1; return doUCS2ToUTF8(reinterpret_cast(src.data()), n, errors); } @@ -267,6 +271,10 @@ CUnicode::UCS2ToUTF8(const CString& src, bool* errors) CString CUnicode::UCS4ToUTF8(const CString& src, bool* errors) { + // default to success + resetError(errors); + + // convert UInt32 n = src.size() >> 2; return doUCS4ToUTF8(reinterpret_cast(src.data()), n, errors); } @@ -274,6 +282,10 @@ CUnicode::UCS4ToUTF8(const CString& src, bool* errors) CString CUnicode::UTF16ToUTF8(const CString& src, bool* errors) { + // default to success + resetError(errors); + + // convert UInt32 n = src.size() >> 1; return doUTF16ToUTF8(reinterpret_cast(src.data()), n, errors); } @@ -281,6 +293,10 @@ CUnicode::UTF16ToUTF8(const CString& src, bool* errors) CString CUnicode::UTF32ToUTF8(const CString& src, bool* errors) { + // default to success + resetError(errors); + + // convert UInt32 n = src.size() >> 2; return doUTF32ToUTF8(reinterpret_cast(src.data()), n, errors); } @@ -334,13 +350,13 @@ CUnicode::textToUTF8(const CString& src, bool* errors) for (const char* scan = src.c_str(); n > 0 && *scan != 0; ++dst) { size_t mblen = mbrtowc(dst, scan, n, &state); switch (mblen) { - case (size_t)2: + case (size_t)-2: // incomplete character. convert to unknown character. *dst = (wchar_t)0xfffd; n = 0; break; - case (size_t)1: + case (size_t)-1: // invalid character. count one unknown character and // start at the next byte. scan += 1; @@ -404,9 +420,6 @@ CUnicode::wideCharToUTF8(const wchar_t* src, bool* errors) CString CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors) { - // default to success - resetError(errors); - // make some space CString dst; dst.reserve(n); @@ -428,9 +441,6 @@ CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors) CString CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors) { - // default to success - resetError(errors); - // make some space CString dst; dst.reserve(n); @@ -452,9 +462,6 @@ CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors) CString CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors) { - // default to success - resetError(errors); - // make some space CString dst; dst.reserve(n); @@ -502,9 +509,6 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors) CString CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors) { - // default to success - resetError(errors); - // make some space CString dst; dst.reserve(n); From 1fd07567ccfa27d30003f9ffccee1e9be420eea7 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 12:08:30 +0000 Subject: [PATCH 252/807] checkpoint. more CUnicode fixes. --- base/CUnicode.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp index 5a677874..f36a82d7 100644 --- a/base/CUnicode.cpp +++ b/base/CUnicode.cpp @@ -244,11 +244,11 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) *dst++ = '?'; } else { - dst += len; + dst += mblen; } } - *dst = '\0'; - CString text(mbs); + *dst++ = '\0'; + CString text(mbs, dst - mbs); // clean up delete[] mbs; From b1163aa59325f803a5c853bc1ddb2010c8b0d0af Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 12:35:36 +0000 Subject: [PATCH 253/807] removed unnecessary atoms from X clipboard object. --- platform/CXWindowsClipboard.cpp | 5 ----- platform/CXWindowsClipboard.h | 5 ----- 2 files changed, 10 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 75efae08..8ba736b0 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -29,14 +29,9 @@ CXWindowsClipboard::CXWindowsClipboard(Display* display, m_atomTargets = XInternAtom(m_display, "TARGETS", False); m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False); m_atomTimestamp = XInternAtom(m_display, "TIMESTAMP", False); - m_atomAtom = XInternAtom(m_display, "ATOM", False); m_atomAtomPair = XInternAtom(m_display, "ATOM_PAIR", False); - m_atomInteger = XInternAtom(m_display, "INTEGER", False); m_atomData = XInternAtom(m_display, "CLIP_TEMPORARY", False); m_atomINCR = XInternAtom(m_display, "INCR", False); - m_atomString = XInternAtom(m_display, "STRING", False); - m_atomText = XInternAtom(m_display, "TEXT", False); - m_atomCompoundText = XInternAtom(m_display, "COMPOUND_TEXT", False); m_atomMotifClipLock = XInternAtom(m_display, "_MOTIF_CLIP_LOCK", False); m_atomMotifClipHeader = XInternAtom(m_display, "_MOTIF_CLIP_HEADER", False); m_atomMotifClipAccess = XInternAtom(m_display, diff --git a/platform/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h index e738b07e..cbd2898e 100644 --- a/platform/CXWindowsClipboard.h +++ b/platform/CXWindowsClipboard.h @@ -267,14 +267,9 @@ private: Atom m_atomTargets; Atom m_atomMultiple; Atom m_atomTimestamp; - Atom m_atomAtom; Atom m_atomAtomPair; - Atom m_atomInteger; Atom m_atomData; Atom m_atomINCR; - Atom m_atomString; - Atom m_atomText; - Atom m_atomCompoundText; Atom m_atomMotifClipLock; Atom m_atomMotifClipHeader; Atom m_atomMotifClipAccess; From 8ada1e8a7252b6cf8666f27c03bcee8a04f38e2c Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 15:26:40 +0000 Subject: [PATCH 254/807] unicode clipboard changes for win32 plus some bug fixes. --- base/CUnicode.cpp | 107 +++++----- base/CUnicode.h | 14 +- base/base.dsp | 8 + platform/CMSWindowsClipboard.cpp | 191 ++++++------------ platform/CMSWindowsClipboard.h | 31 +++ .../CMSWindowsClipboardAnyTextConverter.cpp | 131 ++++++++++++ .../CMSWindowsClipboardAnyTextConverter.h | 32 +++ platform/CMSWindowsClipboardTextConverter.cpp | 34 ++++ platform/CMSWindowsClipboardTextConverter.h | 21 ++ .../CMSWindowsClipboardUTF16Converter.cpp | 34 ++++ platform/CMSWindowsClipboardUTF16Converter.h | 21 ++ platform/platform.dsp | 24 +++ 12 files changed, 470 insertions(+), 178 deletions(-) create mode 100755 platform/CMSWindowsClipboardAnyTextConverter.cpp create mode 100755 platform/CMSWindowsClipboardAnyTextConverter.h create mode 100755 platform/CMSWindowsClipboardTextConverter.cpp create mode 100755 platform/CMSWindowsClipboardTextConverter.h create mode 100755 platform/CMSWindowsClipboardUTF16Converter.cpp create mode 100755 platform/CMSWindowsClipboardUTF16Converter.h diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp index f36a82d7..183499cf 100644 --- a/base/CUnicode.cpp +++ b/base/CUnicode.cpp @@ -160,8 +160,8 @@ CUnicode::UTF8ToUTF16(const CString& src, bool* errors) } else { c -= 0x00010000; - UInt16 utf16h = static_cast(c >> 10) + 0xd800; - UInt16 utf16l = (static_cast(c) & 0x03ff) + 0xdc00; + UInt16 utf16h = static_cast((c >> 10) + 0xd800); + UInt16 utf16l = static_cast((c & 0x03ff) + 0xdc00); dst.append(reinterpret_cast(&utf16h), 2); dst.append(reinterpret_cast(&utf16l), 2); } @@ -206,14 +206,16 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) resetError(errors); // convert to wide char - wchar_t* tmp = UTF8ToWideChar(src, errors); + UInt32 size; + wchar_t* tmp = UTF8ToWideChar(src, size, errors); // get length of multibyte string - size_t len = 0; char mbc[MB_LEN_MAX]; mbstate_t state; memset(&state, 0, sizeof(state)); - for (const wchar_t* scan = tmp; *scan != 0; ++scan) { + size_t len = 0; + UInt32 n = size; + for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { size_t mblen = wcrtomb(mbc, *scan, &state); if (mblen == -1) { // unconvertable character @@ -237,7 +239,8 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) // convert to multibyte char* dst = mbs; - for (const wchar_t* scan = tmp; *scan != 0; ++scan) { + n = size; + for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { size_t mblen = wcrtomb(dst, *scan, &state); if (mblen == -1) { // unconvertable character @@ -312,7 +315,7 @@ CUnicode::textToUTF8(const CString& src, bool* errors) size_t len = 0; mbstate_t state; memset(&state, 0, sizeof(state)); - for (const char* scan = src.c_str(); n > 0 && *scan != 0; ) { + for (const char* scan = src.c_str(); n > 0; ) { size_t mblen = mbrtowc(NULL, scan, n, &state); switch (mblen) { case (size_t)-2: @@ -331,6 +334,12 @@ CUnicode::textToUTF8(const CString& src, bool* errors) n -= 1; break; + case 0: + len += 1; + scan += 1; + n -= 1; + break; + default: // normal character len += 1; @@ -342,12 +351,12 @@ CUnicode::textToUTF8(const CString& src, bool* errors) memset(&state, 0, sizeof(state)); // allocate wide character string - wchar_t* wcs = new wchar_t[len + 1]; + wchar_t* wcs = new wchar_t[len]; // convert multibyte to wide char n = src.size(); wchar_t* dst = wcs; - for (const char* scan = src.c_str(); n > 0 && *scan != 0; ++dst) { + for (const char* scan = src.c_str(); n > 0; ++dst) { size_t mblen = mbrtowc(dst, scan, n, &state); switch (mblen) { case (size_t)-2: @@ -359,9 +368,15 @@ CUnicode::textToUTF8(const CString& src, bool* errors) case (size_t)-1: // invalid character. count one unknown character and // start at the next byte. + *dst = (wchar_t)0xfffd; + scan += 1; + n -= 1; + break; + + case 0: + *dst = (wchar_t)0x0000; scan += 1; n -= 1; - *dst = (wchar_t)0xfffd; break; default: @@ -371,10 +386,9 @@ CUnicode::textToUTF8(const CString& src, bool* errors) break; } } - *dst = L'\0'; // convert to UTF8 - CString utf8 = wideCharToUTF8(wcs, errors); + CString utf8 = wideCharToUTF8(wcs, len, errors); // clean up delete[] wcs; @@ -383,17 +397,17 @@ CUnicode::textToUTF8(const CString& src, bool* errors) } wchar_t* -CUnicode::UTF8ToWideChar(const CString& src, bool* errors) +CUnicode::UTF8ToWideChar(const CString& src, UInt32& size, bool* errors) { // convert to platform's wide character encoding. // note -- this must include a wide nul character (independent of // the CString's nul character). #if WINDOWS_LIKE - CString tmp = UTF8ToUCS16(src); - UInt32 size = tmp.size() >> 1; + CString tmp = UTF8ToUTF16(src, errors); + size = tmp.size() >> 1; #elif UNIX_LIKE - CString tmp = UTF8ToUCS4(src); - UInt32 size = tmp.size() >> 2; + CString tmp = UTF8ToUCS4(src, errors); + size = tmp.size() >> 2; #endif // copy to a wchar_t array @@ -403,17 +417,15 @@ CUnicode::UTF8ToWideChar(const CString& src, bool* errors) } CString -CUnicode::wideCharToUTF8(const wchar_t* src, bool* errors) +CUnicode::wideCharToUTF8(const wchar_t* src, UInt32 size, bool* errors) { // convert from platform's wide character encoding. // note -- this must include a wide nul character (independent of // the CString's nul character). #if WINDOWS_LIKE - return doUCS16ToUTF8(reinterpret_cast(src), - wcslen(src), errors); + return doUTF16ToUTF8(reinterpret_cast(src), size, errors); #elif UNIX_LIKE - return doUCS4ToUTF8(reinterpret_cast(src), - wcslen(src), errors); + return doUCS4ToUTF8(reinterpret_cast(src), size, errors); #endif } @@ -546,12 +558,10 @@ CUnicode::fromUTF8(const UInt8*& data, UInt32& n) size = 1; } else if (data[0] < 0xc0) { - // 10xxxxxx -- in the middle of a multibyte character. skip - // until we find a start byte and return error. - do { - --n; - ++data; - } while (n > 0 && (data[0] & 0xc0) == 0x80); + // 10xxxxxx -- in the middle of a multibyte character. counts + // as one invalid character. + --n; + ++data; return s_invalid; } else if (data[0] < 0xe0) { @@ -632,6 +642,7 @@ CUnicode::fromUTF8(const UInt8*& data, UInt32& n) default: assert(0 && "invalid size"); + return s_invalid; } // check that all bytes after the first have the pattern 10xxxxxx. @@ -724,38 +735,38 @@ CUnicode::toUTF8(CString& dst, UInt32 c, bool* errors) dst.append(reinterpret_cast(data), 1); } else if (c < 0x00000800) { - data[0] = static_cast((c >> 6) & 0x0000001f) + 0xc0; - data[1] = static_cast(c & 0x0000003f) + 0x80; + data[0] = static_cast(((c >> 6) & 0x0000001f) + 0xc0); + data[1] = static_cast((c & 0x0000003f) + 0x80); dst.append(reinterpret_cast(data), 2); } else if (c < 0x00010000) { - data[0] = static_cast((c >> 12) & 0x0000000f) + 0xe0; - data[1] = static_cast((c >> 6) & 0x0000003f) + 0x80; - data[2] = static_cast(c & 0x0000003f) + 0x80; + data[0] = static_cast(((c >> 12) & 0x0000000f) + 0xe0); + data[1] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[2] = static_cast((c & 0x0000003f) + 0x80); dst.append(reinterpret_cast(data), 3); } else if (c < 0x00200000) { - data[0] = static_cast((c >> 18) & 0x00000007) + 0xf0; - data[1] = static_cast((c >> 12) & 0x0000003f) + 0x80; - data[2] = static_cast((c >> 6) & 0x0000003f) + 0x80; - data[3] = static_cast(c & 0x0000003f) + 0x80; + data[0] = static_cast(((c >> 18) & 0x00000007) + 0xf0); + data[1] = static_cast(((c >> 12) & 0x0000003f) + 0x80); + data[2] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[3] = static_cast((c & 0x0000003f) + 0x80); dst.append(reinterpret_cast(data), 4); } else if (c < 0x04000000) { - data[0] = static_cast((c >> 24) & 0x00000003) + 0xf8; - data[1] = static_cast((c >> 18) & 0x0000003f) + 0x80; - data[2] = static_cast((c >> 12) & 0x0000003f) + 0x80; - data[3] = static_cast((c >> 6) & 0x0000003f) + 0x80; - data[4] = static_cast(c & 0x0000003f) + 0x80; + data[0] = static_cast(((c >> 24) & 0x00000003) + 0xf8); + data[1] = static_cast(((c >> 18) & 0x0000003f) + 0x80); + data[2] = static_cast(((c >> 12) & 0x0000003f) + 0x80); + data[3] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[4] = static_cast((c & 0x0000003f) + 0x80); dst.append(reinterpret_cast(data), 5); } else if (c < 0x80000000) { - data[0] = static_cast((c >> 30) & 0x00000001) + 0xfc; - data[1] = static_cast((c >> 24) & 0x0000003f) + 0x80; - data[2] = static_cast((c >> 18) & 0x0000003f) + 0x80; - data[3] = static_cast((c >> 12) & 0x0000003f) + 0x80; - data[4] = static_cast((c >> 6) & 0x0000003f) + 0x80; - data[5] = static_cast(c & 0x0000003f) + 0x80; + data[0] = static_cast(((c >> 30) & 0x00000001) + 0xfc); + data[1] = static_cast(((c >> 24) & 0x0000003f) + 0x80); + data[2] = static_cast(((c >> 18) & 0x0000003f) + 0x80); + data[3] = static_cast(((c >> 12) & 0x0000003f) + 0x80); + data[4] = static_cast(((c >> 6) & 0x0000003f) + 0x80); + data[5] = static_cast((c & 0x0000003f) + 0x80); dst.append(reinterpret_cast(data), 6); } else { diff --git a/base/CUnicode.h b/base/CUnicode.h index 2676e086..89d67235 100644 --- a/base/CUnicode.h +++ b/base/CUnicode.h @@ -15,7 +15,9 @@ public: // not NULL then it gets true if any characters could not be // encoded in the target encoding and false otherwise. note // that decoding errors do not set errors to error. UTF8ToText() - // converts to the current locale's (multibyte) encoding. + // converts to the current locale's (multibyte) encoding. all of + // these methods include the nul terminator in the returned + // string (independent of the CString's own nul terminator). static CString UTF8ToUCS2(const CString&, bool* errors = NULL); static CString UTF8ToUCS4(const CString&, bool* errors = NULL); static CString UTF8ToUTF16(const CString&, bool* errors = NULL); @@ -25,7 +27,9 @@ public: // convert from some encoding to UTF-8. if errors is not NULL // then it gets true if any characters could not be decoded and // false otherwise. textToUTF8() converts from the current - // locale's (multibyte) encoding. + // locale's (multibyte) encoding. all of these methods strip + // a terminating nul so the returned UTF-8 string uses the + // CString's own nul terminator for termination. static CString UCS2ToUTF8(const CString&, bool* errors = NULL); static CString UCS4ToUTF8(const CString&, bool* errors = NULL); static CString UTF16ToUTF8(const CString&, bool* errors = NULL); @@ -36,11 +40,13 @@ private: // convert UTF8 to nul terminated wchar_t string (using whatever // encoding is native to the platform). caller must delete[] // the returned string. - static wchar_t* UTF8ToWideChar(const CString&, bool* errors); + static wchar_t* UTF8ToWideChar(const CString&, + UInt32& size, bool* errors); // convert nul terminated wchar_t string (in platform's native // encoding) to UTF8. - static CString wideCharToUTF8(const wchar_t*, bool* errors); + static CString wideCharToUTF8(const wchar_t*, + UInt32 size, bool* errors); // internal conversion to UTF8 static CString doUCS2ToUTF8(const UInt8* src, UInt32 n, bool* errors); diff --git a/base/base.dsp b/base/base.dsp index b3495e9e..e55fdedc 100644 --- a/base/base.dsp +++ b/base/base.dsp @@ -103,6 +103,10 @@ SOURCE=.\CString.cpp # End Source File # Begin Source File +SOURCE=.\CUnicode.cpp +# End Source File +# Begin Source File + SOURCE=.\XBase.cpp # End Source File # End Group @@ -135,6 +139,10 @@ SOURCE=.\CString.h # End Source File # Begin Source File +SOURCE=.\CUnicode.h +# End Source File +# Begin Source File + SOURCE=.\IInterface.h # End Source File # Begin Source File diff --git a/platform/CMSWindowsClipboard.cpp b/platform/CMSWindowsClipboard.cpp index 8a1029cd..7cefd466 100644 --- a/platform/CMSWindowsClipboard.cpp +++ b/platform/CMSWindowsClipboard.cpp @@ -1,4 +1,6 @@ #include "CMSWindowsClipboard.h" +#include "CMSWindowsClipboardTextConverter.h" +#include "CMSWindowsClipboardUTF16Converter.h" #include "CLog.h" // @@ -9,12 +11,14 @@ CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : m_window(window), m_time(0) { - // do nothing + // add converters, most desired first + m_converters.push_back(new CMSWindowsClipboardUTF16Converter); + m_converters.push_back(new CMSWindowsClipboardTextConverter); } CMSWindowsClipboard::~CMSWindowsClipboard() { - // do nothing + clearConverters(); } bool @@ -35,22 +39,23 @@ CMSWindowsClipboard::add(EFormat format, const CString& data) { log((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); - // convert data to win32 required form - const UINT win32Format = convertFormatToWin32(format); - HANDLE win32Data; - switch (win32Format) { - case CF_TEXT: - win32Data = convertTextToWin32(data); - break; + // convert data to win32 form + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IMSWindowsClipboardConverter* converter = *index; - default: - win32Data = NULL; - break; - } - - // put the data on the clipboard - if (win32Data != NULL) { - SetClipboardData(win32Format, win32Data); + // skip converters for other formats + if (converter->getFormat() == format) { + HANDLE win32Data = converter->fromIClipboard(data); + if (win32Data != NULL) { + UINT win32Format = converter->getWin32Format(); + if (SetClipboardData(win32Format, win32Data) == NULL) { + // free converted data if we couldn't put it on + // the clipboard + GlobalFree(win32Data); + } + } + } } } @@ -85,124 +90,58 @@ CMSWindowsClipboard::getTime() const bool CMSWindowsClipboard::has(EFormat format) const { - const UINT win32Format = convertFormatToWin32(format); - return (win32Format != 0 && IsClipboardFormatAvailable(win32Format) != 0); + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IMSWindowsClipboardConverter* converter = *index; + if (converter->getFormat() == format) { + if (IsClipboardFormatAvailable(converter->getWin32Format())) { + return true; + } + } + } + return false; } CString CMSWindowsClipboard::get(EFormat format) const { - // get the win32 format. return empty data if unknown format. - const UINT win32Format = convertFormatToWin32(format); - if (win32Format == 0) { + // find the converter for the first clipboard format we can handle + IMSWindowsClipboardConverter* converter = NULL; + UINT win32Format = EnumClipboardFormats(0); + while (win32Format != 0) { + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + converter = *index; + if (converter->getWin32Format() == win32Format && + converter->getFormat() == format) { + break; + } + converter = NULL; + } + win32Format = EnumClipboardFormats(win32Format); + } + + // if no converter then we don't recognize any formats + if (converter == NULL) { return CString(); } - // get a handle to the clipboard data and convert it - HANDLE win32Data = GetClipboardData(win32Format); - CString data; - if (win32Data != NULL) { - // convert the data - switch (win32Format) { - case CF_TEXT: - data = convertTextFromWin32(win32Data); - } - } - - return data; -} - -UINT -CMSWindowsClipboard::convertFormatToWin32(EFormat format) const -{ - switch (format) { - case kText: - return CF_TEXT; - - default: - return 0; - } -} - -HANDLE -CMSWindowsClipboard::convertTextToWin32(const CString& data) const -{ - // compute size of converted text - UInt32 dstSize = 1; - const UInt32 srcSize = data.size(); - const char* src = data.c_str(); - for (UInt32 index = 0; index < srcSize; ++index) { - if (src[index] == '\n') { - // add \r - ++dstSize; - } - ++dstSize; - } - - // allocate - HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dstSize); - if (gData != NULL) { - // get a pointer to the allocated memory - char* dst = (char*)GlobalLock(gData); - if (dst != NULL) { - // convert text. we change LF to CRLF. - dstSize = 0; - for (UInt32 index = 0; index < srcSize; ++index) { - if (src[index] == '\n') { - // add \r - dst[dstSize++] = '\r'; - } - dst[dstSize++] = src[index]; - } - dst[dstSize] = '\0'; - - // done converting - GlobalUnlock(gData); - } - } - return gData; -} - -CString -CMSWindowsClipboard::convertTextFromWin32(HANDLE handle) const -{ - // get source data and it's size - const char* src = (const char*)GlobalLock(handle); - UInt32 srcSize = (SInt32)GlobalSize(handle); - if (src == NULL || srcSize <= 1) { + // get a handle to the clipboard data + HANDLE win32Data = GetClipboardData(converter->getWin32Format()); + if (win32Data == NULL) { return CString(); } - // ignore trailing NUL - --srcSize; - - // compute size of converted text - UInt32 dstSize = 0; - UInt32 index; - for (index = 0; index < srcSize; ++index) { - if (src[index] == '\r') { - // skip \r - if (index + 1 < srcSize && src[index + 1] == '\n') { - ++index; - } - } - ++dstSize; - } - - // allocate - CString data; - data.reserve(dstSize); - - // convert text. we change CRLF to LF. - for (index = 0; index < srcSize; ++index) { - if (src[index] == '\r') { - // skip \r - if (index + 1 < srcSize && src[index + 1] == '\n') { - ++index; - } - } - data += src[index]; - } - - return data; + // convert + return converter->toIClipboard(win32Data); +} + +void +CMSWindowsClipboard::clearConverters() +{ + for (ConverterList::iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + delete *index; + } + m_converters.clear(); } diff --git a/platform/CMSWindowsClipboard.h b/platform/CMSWindowsClipboard.h index 6179ee30..fa253c1e 100644 --- a/platform/CMSWindowsClipboard.h +++ b/platform/CMSWindowsClipboard.h @@ -2,9 +2,12 @@ #define CMSWINDOWSCLIPBOARD_H #include "IClipboard.h" +#include "stdvector.h" #define WIN32_LEAN_AND_MEAN #include +class IMSWindowsClipboardConverter; + class CMSWindowsClipboard : public IClipboard { public: CMSWindowsClipboard(HWND window); @@ -20,13 +23,41 @@ public: virtual CString get(EFormat) const; private: + void clearConverters(); + UINT convertFormatToWin32(EFormat) const; HANDLE convertTextToWin32(const CString& data) const; CString convertTextFromWin32(HANDLE) const; private: + typedef std::vector ConverterList; + HWND m_window; mutable Time m_time; + ConverterList m_converters; +}; + +class IMSWindowsClipboardConverter : public IInterface { +public: + // accessors + + // return the clipboard format this object converts from/to + virtual IClipboard::EFormat + getFormat() const = 0; + + // return the atom representing the win32 clipboard format that + // this object converts from/to + virtual UINT getWin32Format() const = 0; + + // convert from the IClipboard format to the win32 clipboard format. + // the input data must be in the IClipboard format returned by + // getFormat(). the return data will be in the win32 clipboard + // format returned by getWin32Format(), allocated by GlobalAlloc(). + virtual HANDLE fromIClipboard(const CString&) const = 0; + + // convert from the win32 clipboard format to the IClipboard format + // (i.e., the reverse of fromIClipboard()). + virtual CString toIClipboard(HANDLE data) const = 0; }; #endif diff --git a/platform/CMSWindowsClipboardAnyTextConverter.cpp b/platform/CMSWindowsClipboardAnyTextConverter.cpp new file mode 100755 index 00000000..66035da5 --- /dev/null +++ b/platform/CMSWindowsClipboardAnyTextConverter.cpp @@ -0,0 +1,131 @@ +#include "CMSWindowsClipboardAnyTextConverter.h" + +// +// CMSWindowsClipboardAnyTextConverter +// + +CMSWindowsClipboardAnyTextConverter::CMSWindowsClipboardAnyTextConverter() +{ + // do nothing +} + +CMSWindowsClipboardAnyTextConverter::~CMSWindowsClipboardAnyTextConverter() +{ + // do nothing +} + +IClipboard::EFormat +CMSWindowsClipboardAnyTextConverter::getFormat() const +{ + return IClipboard::kText; +} + +HANDLE +CMSWindowsClipboardAnyTextConverter::fromIClipboard(const CString& data) const +{ + // convert linefeeds and then convert to desired encoding + CString text = doFromIClipboard(convertLinefeedToWin32(data)); + UInt32 size = text.size(); + + // copy to memory handle + HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size); + if (gData != NULL) { + // get a pointer to the allocated memory + char* dst = (char*)GlobalLock(gData); + if (dst != NULL) { + memcpy(dst, text.data(), size); + GlobalUnlock(gData); + } + else { + GlobalFree(gData); + gData = NULL; + } + } + + return gData; +} + +CString +CMSWindowsClipboardAnyTextConverter::toIClipboard(HANDLE data) const +{ + // get datator + const char* src = (const char*)GlobalLock(data); + UInt32 srcSize = (UInt32)GlobalSize(data); + if (src == NULL || srcSize <= 1) { + return CString(); + } + + // convert text + CString text = doToIClipboard(CString(src, srcSize)); + + // release handle + GlobalUnlock(data); + + // convert newlines + return convertLinefeedToUnix(text); +} + +CString +CMSWindowsClipboardAnyTextConverter::convertLinefeedToWin32( + const CString& src) const +{ + // note -- we assume src is a valid UTF-8 string + + // count newlines in string + UInt32 numNewlines = 0; + UInt32 n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (*scan == '\n') { + ++numNewlines; + } + } + if (numNewlines == 0) { + return src; + } + + // allocate new string + CString dst; + dst.reserve(src.size() + numNewlines); + + // copy string, converting newlines + n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (scan[0] == '\n') { + dst += '\r'; + } + dst += scan[0]; + } + + return dst; +} + +CString +CMSWindowsClipboardAnyTextConverter::convertLinefeedToUnix( + const CString& src) const +{ + // count newlines in string + UInt32 numNewlines = 0; + UInt32 n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (scan[0] == '\r' && scan[1] == '\n') { + ++numNewlines; + } + } + if (numNewlines == 0) { + return src; + } + + // allocate new string + CString dst; + dst.reserve(src.size()); + + // copy string, converting newlines + n = src.size(); + for (const char* scan = src.c_str(); n > 0; ++scan, --n) { + if (scan[0] != '\r' || scan[1] != '\n') { + dst += scan[0]; + } + } + + return dst; +} diff --git a/platform/CMSWindowsClipboardAnyTextConverter.h b/platform/CMSWindowsClipboardAnyTextConverter.h new file mode 100755 index 00000000..497702f0 --- /dev/null +++ b/platform/CMSWindowsClipboardAnyTextConverter.h @@ -0,0 +1,32 @@ +#ifndef CMSWINDOWSCLIPBOARDANYTEXTCONVERTER_H +#define CMSWINDOWSCLIPBOARDANYTEXTCONVERTER_H + +#include "CMSWindowsClipboard.h" + +class CMSWindowsClipboardAnyTextConverter : + public IMSWindowsClipboardConverter { +public: + CMSWindowsClipboardAnyTextConverter(); + virtual ~CMSWindowsClipboardAnyTextConverter(); + + // IMSWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual UINT getWin32Format() const = 0; + virtual HANDLE fromIClipboard(const CString&) const; + virtual CString toIClipboard(HANDLE) const; + +protected: + // do UTF-8 conversion only. memory handle allocation and + // linefeed conversion is done by this class. doFromIClipboard() + // must include the nul terminator in the returned string (not + // including the CString's nul terminator). + virtual CString doFromIClipboard(const CString&) const = 0; + virtual CString doToIClipboard(const CString&) const = 0; + +private: + CString convertLinefeedToWin32(const CString&) const; + CString convertLinefeedToUnix(const CString&) const; +}; + +#endif diff --git a/platform/CMSWindowsClipboardTextConverter.cpp b/platform/CMSWindowsClipboardTextConverter.cpp new file mode 100755 index 00000000..195d88a1 --- /dev/null +++ b/platform/CMSWindowsClipboardTextConverter.cpp @@ -0,0 +1,34 @@ +#include "CMSWindowsClipboardTextConverter.h" +#include "CUnicode.h" + +// +// CMSWindowsClipboardTextConverter +// + +CMSWindowsClipboardTextConverter::CMSWindowsClipboardTextConverter() +{ + // do nothing +} + +CMSWindowsClipboardTextConverter::~CMSWindowsClipboardTextConverter() +{ + // do nothing +} + +UINT +CMSWindowsClipboardTextConverter::getWin32Format() const +{ + return CF_TEXT; +} + +CString +CMSWindowsClipboardTextConverter::doFromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToText(data); +} + +CString +CMSWindowsClipboardTextConverter::doToIClipboard(const CString& data) const +{ + return CUnicode::textToUTF8(data); +} diff --git a/platform/CMSWindowsClipboardTextConverter.h b/platform/CMSWindowsClipboardTextConverter.h new file mode 100755 index 00000000..7edab3e5 --- /dev/null +++ b/platform/CMSWindowsClipboardTextConverter.h @@ -0,0 +1,21 @@ +#ifndef CMSWINDOWSCLIPBOARDTEXTCONVERTER_H +#define CMSWINDOWSCLIPBOARDTEXTCONVERTER_H + +#include "CMSWindowsClipboardAnyTextConverter.h" + +class CMSWindowsClipboardTextConverter : + public CMSWindowsClipboardAnyTextConverter { +public: + CMSWindowsClipboardTextConverter(); + virtual ~CMSWindowsClipboardTextConverter(); + + // IMSWindowsClipboardConverter overrides + virtual UINT getWin32Format() const; + +protected: + // CMSWindowsClipboardAnyTextConverter overrides + virtual CString doFromIClipboard(const CString&) const; + virtual CString doToIClipboard(const CString&) const; +}; + +#endif diff --git a/platform/CMSWindowsClipboardUTF16Converter.cpp b/platform/CMSWindowsClipboardUTF16Converter.cpp new file mode 100755 index 00000000..3c9da906 --- /dev/null +++ b/platform/CMSWindowsClipboardUTF16Converter.cpp @@ -0,0 +1,34 @@ +#include "CMSWindowsClipboardUTF16Converter.h" +#include "CUnicode.h" + +// +// CMSWindowsClipboardUTF16Converter +// + +CMSWindowsClipboardUTF16Converter::CMSWindowsClipboardUTF16Converter() +{ + // do nothing +} + +CMSWindowsClipboardUTF16Converter::~CMSWindowsClipboardUTF16Converter() +{ + // do nothing +} + +UINT +CMSWindowsClipboardUTF16Converter::getWin32Format() const +{ + return CF_UNICODETEXT; +} + +CString +CMSWindowsClipboardUTF16Converter::doFromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToUTF16(data); +} + +CString +CMSWindowsClipboardUTF16Converter::doToIClipboard(const CString& data) const +{ + return CUnicode::UTF16ToUTF8(data); +} diff --git a/platform/CMSWindowsClipboardUTF16Converter.h b/platform/CMSWindowsClipboardUTF16Converter.h new file mode 100755 index 00000000..6ccaa3f8 --- /dev/null +++ b/platform/CMSWindowsClipboardUTF16Converter.h @@ -0,0 +1,21 @@ +#ifndef CMSWINDOWSCLIPBOARDUTF16CONVERTER_H +#define CMSWINDOWSCLIPBOARDUTF16CONVERTER_H + +#include "CMSWindowsClipboardAnyTextConverter.h" + +class CMSWindowsClipboardUTF16Converter : + public CMSWindowsClipboardAnyTextConverter { +public: + CMSWindowsClipboardUTF16Converter(); + virtual ~CMSWindowsClipboardUTF16Converter(); + + // IMSWindowsClipboardConverter overrides + virtual UINT getWin32Format() const; + +protected: + // CMSWindowsClipboardAnyTextConverter overrides + virtual CString doFromIClipboard(const CString&) const; + virtual CString doToIClipboard(const CString&) const; +}; + +#endif diff --git a/platform/platform.dsp b/platform/platform.dsp index ad92fece..080d4de2 100644 --- a/platform/platform.dsp +++ b/platform/platform.dsp @@ -91,6 +91,18 @@ SOURCE=.\CMSWindowsClipboard.cpp # End Source File # Begin Source File +SOURCE=.\CMSWindowsClipboardAnyTextConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardTextConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp +# End Source File +# Begin Source File + SOURCE=.\CMSWindowsScreen.cpp # End Source File # Begin Source File @@ -111,6 +123,18 @@ SOURCE=.\CMSWindowsClipboard.h # End Source File # Begin Source File +SOURCE=.\CMSWindowsClipboardAnyTextConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardTextConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardUTF16Converter.h +# End Source File +# Begin Source File + SOURCE=.\CMSWindowsScreen.h # End Source File # Begin Source File From 57b64f1fe7f02e31132bb317e531d15eacf6e81b Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 15:34:05 +0000 Subject: [PATCH 255/807] no longer attempting to unmarshall clipboard formats that aren't known to the caller. if the client supports more formats than the server then the server could get a clipboard format greater than kNumFormats. with this change the server discards the extra formats instead of crashing. --- synergy/CClipboard.cpp | 10 +++++++--- synergy/IClipboard.h | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/synergy/CClipboard.cpp b/synergy/CClipboard.cpp index 8862d769..56a2d41a 100644 --- a/synergy/CClipboard.cpp +++ b/synergy/CClipboard.cpp @@ -146,9 +146,13 @@ CClipboard::unmarshall(const CString& data, Time time) UInt32 size = readUInt32(index); index += 4; - // save the data - m_added[format] = true; - m_data[format] = CString(index, size); + // save the data if it's a known format. if either the client + // or server supports more clipboard formats than the other + // then one of them will get a format >= kNumFormats here. + if (format < static_cast(IClipboard::kNumFormats)) { + m_added[format] = true; + m_data[format] = CString(index, size); + } index += size; } diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index 2c145315..cd56d9e2 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -17,9 +17,9 @@ public: // data set via add() and retrieved via get() is in one of // these formats. platform dependent clipboard subclasses // can and should present any suitable formats derivable - // from these formats (e.g. UCS-16 encoded unicode). + // from these formats (e.g. UTF-16 encoded unicode). // - // kText: UTF-8 encoded unicode (ISO-10646), newline is LF + // kText: UTF-8 encoded unicode (ISO-10646), newline is LF. enum EFormat { kText, kNumFormats }; // manipulators From 8271c8accc160543acb5bedd00a7a18033f70f47 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 17:04:41 +0000 Subject: [PATCH 256/807] checkpoint. converting KeyID to use UTF-32 encoding instead of X11 keysyms. --- client/CXWindowsSecondaryScreen.cpp | 74 +++++++++----- server/CXWindowsPrimaryScreen.cpp | 38 +++++-- synergy/KeyTypes.h | 148 +++++++++++++++++++++++++++- 3 files changed, 225 insertions(+), 35 deletions(-) diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index b0187ccb..968cf5cf 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -371,8 +371,8 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // that cannot be accomodated. // note if the key is the caps lock and it's "half-duplex" - const bool isHalfDuplex = ((id == XK_Caps_Lock && m_capsLockHalfDuplex) || - (id == XK_Num_Lock && m_numLockHalfDuplex)); + const bool isHalfDuplex = ((id == kKeyCapsLock && m_capsLockHalfDuplex) || + (id == kKeyNumLock && m_numLockHalfDuplex)); // ignore releases and repeats for half-duplex keys if (isHalfDuplex && action != kPress) { @@ -385,10 +385,10 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, if (!findKeyCode(keycode, outMask, id, maskToX(mask))) { // we cannot generate the desired keysym because no key // maps to that keysym. just return the current mask. - log((CLOG_DEBUG2 "no keycode for keysym %d modifiers 0x%04x", id, mask)); + log((CLOG_DEBUG2 "no keycode for KeyID %d modifiers 0x%04x", id, mask)); return m_mask; } - log((CLOG_DEBUG2 "keysym %d -> keycode %d modifiers 0x%04x", id, keycode, outMask)); + log((CLOG_DEBUG2 "keysym %d -> KeyID %d modifiers 0x%04x", id, keycode, outMask)); // if we cannot match the modifier mask then don't return any // keys and just return the current mask. @@ -580,67 +580,93 @@ bool CXWindowsSecondaryScreen::findKeyCode(KeyCode& keycode, unsigned int& maskOut, KeyID id, unsigned int maskIn) const { - // if XK_Tab is requested with shift active then try XK_ISO_Left_Tab + // convert id to keysym + KeySym keysym = 0; + switch (id & 0xffffff00) { + case 0x0000: + // Latin-1 + keysym = static_cast(id); + break; + + case 0xee00: + // ISO 9995 Function and Modifier Keys + if (id == kKeyLeftTab) { + keysym = XK_ISO_Left_Tab; + } + break; + + case 0xef00: + // MISCELLANY + keysym = static_cast(id - 0xef00 + 0xff00); + break; + } + + // fail if unknown key + if (keysym == 0) { + return false; + } + + // if kKeyTab is requested with shift active then try XK_ISO_Left_Tab // instead. if that doesn't work, we'll fall back to XK_Tab with // shift active. this is to handle primary screens that don't map // XK_ISO_Left_Tab sending events to secondary screens that do. - if (id == XK_Tab && (maskIn & ShiftMask) != 0) { - id = XK_ISO_Left_Tab; + if (keysym == XK_Tab && (maskIn & ShiftMask) != 0) { + keysym = XK_ISO_Left_Tab; maskIn &= ~ShiftMask; } // find a keycode to generate id. XKeysymToKeycode() almost does // what we need but won't tell us which index to use with the // keycode. return false if there's no keycode to generate id. - KeyCodeMap::const_iterator index = m_keycodeMap.find(id); + KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym); if (index == m_keycodeMap.end()) { // try backup keysym for certain keys (particularly the numpad // keys since most laptops don't have a separate numpad and the // numpad overlaying the main keyboard may not have movement // key bindings). - switch (id) { + switch (keysym) { case XK_KP_Home: - id = XK_Home; + keysym = XK_Home; break; case XK_KP_Left: - id = XK_Left; + keysym = XK_Left; break; case XK_KP_Up: - id = XK_Up; + keysym = XK_Up; break; case XK_KP_Right: - id = XK_Right; + keysym = XK_Right; break; case XK_KP_Down: - id = XK_Down; + keysym = XK_Down; break; case XK_KP_Prior: - id = XK_Prior; + keysym = XK_Prior; break; case XK_KP_Next: - id = XK_Next; + keysym = XK_Next; break; case XK_KP_End: - id = XK_End; + keysym = XK_End; break; case XK_KP_Insert: - id = XK_Insert; + keysym = XK_Insert; break; case XK_KP_Delete: - id = XK_Delete; + keysym = XK_Delete; break; case XK_ISO_Left_Tab: - id = XK_Tab; + keysym = XK_Tab; maskIn |= ShiftMask; break; @@ -648,7 +674,7 @@ CXWindowsSecondaryScreen::findKeyCode(KeyCode& keycode, return false; } - index = m_keycodeMap.find(id); + index = m_keycodeMap.find(keysym); if (index == m_keycodeMap.end()) { return false; } @@ -659,14 +685,14 @@ CXWindowsSecondaryScreen::findKeyCode(KeyCode& keycode, // compute output mask. that's the set of modifiers that need to // be enabled when the keycode event is encountered in order to - // generate the id keysym and match maskIn. it's possible that + // generate the keysym and match maskIn. it's possible that // maskIn wants, say, a shift key to be down but that would make // it impossible to generate the keysym. in that case we must // override maskIn. this is complicated by caps/shift-lock and // num-lock. maskOut = (maskIn & ~index->second.m_keyMaskMask); log((CLOG_DEBUG2 "maskIn(0x%04x) & ~maskMask(0x%04x) -> 0x%04x", maskIn, index->second.m_keyMaskMask, maskOut)); - if (IsKeypadKey(id) || IsPrivateKeypadKey(id)) { + if (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)) { if ((m_mask & m_numLockMask) != 0) { maskOut &= ~index->second.m_keyMask; maskOut |= m_numLockMask; @@ -687,7 +713,7 @@ CXWindowsSecondaryScreen::findKeyCode(KeyCode& keycode, // characters that are not case conversions. see if // case conversion is necessary. KeySym lKey, uKey; - XConvertCase(id, &lKey, &uKey); + XConvertCase(keysym, &lKey, &uKey); if (lKey != uKey) { log((CLOG_DEBUG2 "case convertable, shift && capsLock -> caps lock")); maskShift = m_capsLockMask; diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 0dc659e9..ba4e8a39 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -12,6 +12,7 @@ # include # include # define XK_MISCELLANY +# define XK_XKB_KEYS # include #endif @@ -172,10 +173,10 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { m_receiver->onKeyDown(key, mask); - if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { + if (key == kKeyCapsLock && m_capsLockHalfDuplex) { m_receiver->onKeyUp(key, mask | KeyModifierCapsLock); } - else if (key == XK_Num_Lock && m_numLockHalfDuplex) { + else if (key == kKeyNumLock && m_numLockHalfDuplex) { m_receiver->onKeyUp(key, mask | KeyModifierNumLock); } } @@ -209,10 +210,10 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) if (!hasPress) { // no press event follows so it's a plain release log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { + if (key == kKeyCapsLock && m_capsLockHalfDuplex) { m_receiver->onKeyDown(key, mask); } - else if (key == XK_Num_Lock && m_numLockHalfDuplex) { + else if (key == kKeyNumLock && m_numLockHalfDuplex) { m_receiver->onKeyDown(key, mask); } m_receiver->onKeyUp(key, mask); @@ -621,12 +622,35 @@ CXWindowsPrimaryScreen::mapModifier(unsigned int state) const KeyID CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const { + CDisplayLock display(m_screen); + + // convert to a keysym + // FIXME -- we're not properly handling unicode KeySym keysym; char dummy[1]; - - CDisplayLock display(m_screen); XLookupString(event, dummy, 0, &keysym, NULL); - return static_cast(keysym); + + // convert key + switch (keysym & 0xffffff00) { + case 0x0000: + // Latin-1 + return static_cast(keysym); + + case 0xfe00: + // ISO 9995 Function and Modifier Keys + if (keysym == XK_ISO_Left_Tab) { + return kKeyLeftTab; + } + return kKeyNone; + + case 0xff00: + // MISCELLANY + return static_cast(keysym - 0xff00 + 0xef00); + + default: + // FIXME -- support unicode characters + return kKeyNone; + } } ButtonID diff --git a/synergy/KeyTypes.h b/synergy/KeyTypes.h index f9ca8241..947f48f6 100644 --- a/synergy/KeyTypes.h +++ b/synergy/KeyTypes.h @@ -3,15 +3,14 @@ #include "BasicTypes.h" -// type to hold a key identifier +// type to hold a key identifier. the encoding is UTF-32, using +// U+E000 through U+EFFF for the various control keys (e.g. arrow +// keys, function keys, modifier keys, etc). typedef UInt32 KeyID; // type to hold bitmask of key modifiers (e.g. shift keys) typedef UInt32 KeyModifierMask; -// key codes -static const KeyID kKeyNone = 0; - // modifier key bitmasks static const KeyModifierMask KeyModifierShift = 0x0001; static const KeyModifierMask KeyModifierControl = 0x0002; @@ -21,4 +20,145 @@ static const KeyModifierMask KeyModifierCapsLock = 0x1000; static const KeyModifierMask KeyModifierNumLock = 0x2000; static const KeyModifierMask KeyModifierScrollLock = 0x4000; +// +// key codes. all codes except kKeyNone are equal to the corresponding +// X11 keysym - 0x1000. +// + +// no key +static const KeyID kKeyNone = 0x0000; + +// TTY functions +static const KeyID kKeyBackSpace = 0xEF08; /* back space, back char */ +static const KeyID kKeyTab = 0xEF09; +static const KeyID kKeyLinefeed = 0xEF0A; /* Linefeed, LF */ +static const KeyID kKeyClear = 0xEF0B; +static const KeyID kKeyReturn = 0xEF0D; /* Return, enter */ +static const KeyID kKeyPause = 0xEF13; /* Pause, hold */ +static const KeyID kKeyScrollLock = 0xEF14; +static const KeyID kKeySysReq = 0xEF15; +static const KeyID kKeyEscape = 0xEF1B; +static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ + +// cursor control +static const KeyID kKeyHome = 0xEF50; +static const KeyID kKeyLeft = 0xEF51; /* Move left, left arrow */ +static const KeyID kKeyUp = 0xEF52; /* Move up, up arrow */ +static const KeyID kKeyRight = 0xEF53; /* Move right, right arrow */ +static const KeyID kKeyDown = 0xEF54; /* Move down, down arrow */ +static const KeyID kKeyPageUp = 0xEF55; +static const KeyID kKeyPageDown = 0xEF56; +static const KeyID kKeyEnd = 0xEF57; /* EOL */ +static const KeyID kKeyBegin = 0xEF58; /* BOL */ + +// misc functions +static const KeyID kKeySelect = 0xEF60; /* Select, mark */ +static const KeyID kKeyPrint = 0xEF61; +static const KeyID kKeyExecute = 0xEF62; /* Execute, run, do */ +static const KeyID kKeyInsert = 0xEF63; /* Insert, insert here */ +static const KeyID kKeyUndo = 0xEF65; /* Undo, oops */ +static const KeyID kKeyRedo = 0xEF66; /* redo, again */ +static const KeyID kKeyMenu = 0xEF67; +static const KeyID kKeyFind = 0xEF68; /* Find, search */ +static const KeyID kKeyCancel = 0xEF69; /* Cancel, stop, abort, exit */ +static const KeyID kKeyHelp = 0xEF6A; /* Help */ +static const KeyID kKeyBreak = 0xEF6B; +static const KeyID kKeyModeSwitch = 0xEF7E; /* Character set switch */ +static const KeyID kKeyNumLock = 0xEF7F; + +// keypad +static const KeyID kKeyKP_Space = 0xEF80; /* space */ +static const KeyID kKeyKP_Tab = 0xEF89; +static const KeyID kKeyKP_Enter = 0xEF8D; /* enter */ +static const KeyID kKeyKP_F1 = 0xEF91; /* PF1, KP_A, ... */ +static const KeyID kKeyKP_F2 = 0xEF92; +static const KeyID kKeyKP_F3 = 0xEF93; +static const KeyID kKeyKP_F4 = 0xEF94; +static const KeyID kKeyKP_Home = 0xEF95; +static const KeyID kKeyKP_Left = 0xEF96; +static const KeyID kKeyKP_Up = 0xEF97; +static const KeyID kKeyKP_Right = 0xEF98; +static const KeyID kKeyKP_Down = 0xEF99; +static const KeyID kKeyKP_Prior = 0xEF9A; +static const KeyID kKeyKP_PageUp = 0xEF9A; +static const KeyID kKeyKP_Next = 0xEF9B; +static const KeyID kKeyKP_PageDown = 0xEF9B; +static const KeyID kKeyKP_End = 0xEF9C; +static const KeyID kKeyKP_Begin = 0xEF9D; +static const KeyID kKeyKP_Insert = 0xEF9E; +static const KeyID kKeyKP_Delete = 0xEF9F; +static const KeyID kKeyKP_Equal = 0xEFBD; /* equals */ +static const KeyID kKeyKP_Multiply = 0xEFAA; +static const KeyID kKeyKP_Add = 0xEFAB; +static const KeyID kKeyKP_Separator= 0xEFAC; /* separator, often comma */ +static const KeyID kKeyKP_Subtract = 0xEFAD; +static const KeyID kKeyKP_Decimal = 0xEFAE; +static const KeyID kKeyKP_Divide = 0xEFAF; +static const KeyID kKeyKP_0 = 0xEFB0; +static const KeyID kKeyKP_1 = 0xEFB1; +static const KeyID kKeyKP_2 = 0xEFB2; +static const KeyID kKeyKP_3 = 0xEFB3; +static const KeyID kKeyKP_4 = 0xEFB4; +static const KeyID kKeyKP_5 = 0xEFB5; +static const KeyID kKeyKP_6 = 0xEFB6; +static const KeyID kKeyKP_7 = 0xEFB7; +static const KeyID kKeyKP_8 = 0xEFB8; +static const KeyID kKeyKP_9 = 0xEFB9; + +// function keys +static const KeyID kKeyF1 = 0xEFBE; +static const KeyID kKeyF2 = 0xEFBF; +static const KeyID kKeyF3 = 0xEFC0; +static const KeyID kKeyF4 = 0xEFC1; +static const KeyID kKeyF5 = 0xEFC2; +static const KeyID kKeyF6 = 0xEFC3; +static const KeyID kKeyF7 = 0xEFC4; +static const KeyID kKeyF8 = 0xEFC5; +static const KeyID kKeyF9 = 0xEFC6; +static const KeyID kKeyF10 = 0xEFC7; +static const KeyID kKeyF11 = 0xEFC8; +static const KeyID kKeyF12 = 0xEFC9; +static const KeyID kKeyF13 = 0xEFCA; +static const KeyID kKeyF14 = 0xEFCB; +static const KeyID kKeyF15 = 0xEFCC; +static const KeyID kKeyF16 = 0xEFCD; +static const KeyID kKeyF17 = 0xEFCE; +static const KeyID kKeyF18 = 0xEFCF; +static const KeyID kKeyF19 = 0xEFD0; +static const KeyID kKeyF20 = 0xEFD1; +static const KeyID kKeyF21 = 0xEFD2; +static const KeyID kKeyF22 = 0xEFD3; +static const KeyID kKeyF23 = 0xEFD4; +static const KeyID kKeyF24 = 0xEFD5; +static const KeyID kKeyF25 = 0xEFD6; +static const KeyID kKeyF26 = 0xEFD7; +static const KeyID kKeyF27 = 0xEFD8; +static const KeyID kKeyF28 = 0xEFD9; +static const KeyID kKeyF29 = 0xEFDA; +static const KeyID kKeyF30 = 0xEFDB; +static const KeyID kKeyF31 = 0xEFDC; +static const KeyID kKeyF32 = 0xEFDD; +static const KeyID kKeyF33 = 0xEFDE; +static const KeyID kKeyF34 = 0xEFDF; +static const KeyID kKeyF35 = 0xEFE0; + +// modifiers +static const KeyID kKeyShift_L = 0xEFE1; /* Left shift */ +static const KeyID kKeyShift_R = 0xEFE2; /* Right shift */ +static const KeyID kKeyControl_L = 0xEFE3; /* Left control */ +static const KeyID kKeyControl_R = 0xEFE4; /* Right control */ +static const KeyID kKeyCapsLock = 0xEFE5; /* Caps lock */ +static const KeyID kKeyShiftLock = 0xEFE6; /* Shift lock */ +static const KeyID kKeyMeta_L = 0xEFE7; /* Left meta */ +static const KeyID kKeyMeta_R = 0xEFE8; /* Right meta */ +static const KeyID kKeyAlt_L = 0xEFE9; /* Left alt */ +static const KeyID kKeyAlt_R = 0xEFEA; /* Right alt */ +static const KeyID kKeySuper_L = 0xEFEB; /* Left super */ +static const KeyID kKeySuper_R = 0xEFEC; /* Right super */ +static const KeyID kKeyHyper_L = 0xEFED; /* Left hyper */ +static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */ + +// more function and modifier keys +static const KeyID kKeyLeftTab = 0xEE20; + #endif From 7129efc9ab3334fdfd239d08e5c62922a57793ed Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 18:59:15 +0000 Subject: [PATCH 257/807] converted win32 to use unicode based KeyID. --- client/CMSWindowsSecondaryScreen.cpp | 468 ++---------------------- server/CMSWindowsPrimaryScreen.cpp | 521 +++++++++++++-------------- synergy/KeyTypes.h | 3 + 3 files changed, 294 insertions(+), 698 deletions(-) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 3c93ac0c..93e8179d 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -463,361 +463,10 @@ CMSWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) } } -// these tables map KeyID (a X windows KeySym) to virtual key codes. -// if the key is an extended key then the entry is the virtual key -// code | 0x100. keys that map to normal characters have a 0 entry -// and the conversion is done elsewhere. -static const UINT g_latin1[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_latin2[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_latin3[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_latin4[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_latin5[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_latin6[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_latin7[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_latin8[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_latin9[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_terminal[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_function[] = +// map special KeyID keys to virtual key codes. if the key is an +// extended key then the entry is the virtual key code | 0x100. +// unmapped keys have a 0 entry. +static const UINT g_mapEE00[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -852,8 +501,7 @@ static const UINT g_function[] = /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 }; -/* XK_KP_Space to XK_KP_Equal */ -static const UINT g_miscellany[] = +static const UINT g_mapEF00[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, @@ -894,61 +542,6 @@ static const UINT g_miscellany[] = /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100 }; -static const UINT* g_katakana = NULL; -static const UINT* g_arabic = NULL; -static const UINT* g_cyrillic = NULL; -static const UINT* g_greek = NULL; -static const UINT* g_technical = NULL; -static const UINT* g_special = NULL; -static const UINT* g_publishing = NULL; -static const UINT* g_apl = NULL; -static const UINT* g_hebrew = NULL; -static const UINT* g_thai = NULL; -static const UINT* g_korean = NULL; -static const UINT* g_armenian = NULL; -static const UINT* g_georgian = NULL; -static const UINT* g_azeri = NULL; -static const UINT* g_vietnamese = NULL; -static const UINT* g_currency = NULL; -static const UINT* g_mapTable[] = -{ - /* 0x00 */ g_latin1, g_latin2, g_latin3, g_latin4, - /* 0x04 */ g_katakana, g_arabic, g_cyrillic, g_greek, - /* 0x08 */ g_technical, g_special, g_publishing, g_apl, - /* 0x0c */ g_hebrew, g_thai, g_korean, NULL, - /* 0x10 */ NULL, NULL, g_latin8, g_latin9, - /* 0x14 */ g_armenian, g_georgian, g_azeri, NULL, - /* 0x18 */ NULL, NULL, NULL, NULL, NULL, NULL, g_vietnamese, NULL, - /* 0x20 */ g_currency, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x28 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x30 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x38 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x40 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x48 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x50 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x58 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x60 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x68 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x70 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x78 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x80 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x88 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x90 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0x98 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xa0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xa8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xb0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xb8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xc0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xc8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xd0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xd8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xe0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xe8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xf0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 0xf8 */ NULL, NULL, NULL, NULL, - /* 0xfc */ NULL, g_terminal, g_function, g_miscellany -}; DWORD CMSWindowsSecondaryScreen::mapButton(ButtonID button, bool press) const @@ -973,19 +566,25 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, KeyID id, KeyModifierMask mask, EKeyAction action) const { - // lookup the key table - const UInt32 mapID = ((id >> 8) & 0xff); - const UINT* map = g_mapTable[mapID]; - if (map == NULL) { - // unknown key - return m_mask; + virtualKey = 0; + + // check for special keys + if ((id & 0xfffff000) == 0xe000) { + if ((id & 0xff00) == 0xee00) { + virtualKey = g_mapEE00[id & 0xff]; + } + else if ((id & 0xff00) == 0xef00) { + virtualKey = g_mapEF00[id & 0xff]; + } + if (virtualKey == 0) { + log((CLOG_DEBUG2 "unknown special key")); + return m_mask; + } } - // look up virtual key for id. default output mask carries over - // the current toggle modifier states and includes desired shift, - // control, alt, and meta states. - const UInt32 code = (id & 0xff); - virtualKey = map[code]; + // get output mask. default output mask carries over the current + // toggle modifier states and includes desired shift, control, alt, + // and meta states. KeyModifierMask outMask = (m_mask & (KeyModifierCapsLock | KeyModifierNumLock | @@ -995,24 +594,19 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, KeyModifierControl | KeyModifierAlt | KeyModifierMeta)); - log((CLOG_DEBUG2 "key id %d -> virtual key %d", id, virtualKey)); // extract extended key flag const bool isExtended = ((virtualKey & 0x100) != 0); virtualKey &= ~0x100; - // if not in map then ask system to convert ascii character + // if not in map then ask system to convert character if (virtualKey == 0) { - if (mapID != 0) { - // not ascii - log((CLOG_DEBUG2 "not ascii")); - return m_mask; - } - // translate. return no keys if unknown key. - SHORT vk = VkKeyScan(static_cast(code)); + // FIXME -- handle unicode + TCHAR ascii = static_cast(id & 0x000000ff); + SHORT vk = VkKeyScan(ascii); if (vk == 0xffff) { - log((CLOG_DEBUG2 "no virtual key for character %d", code)); + log((CLOG_DEBUG2 "no virtual key for character %d", id)); return m_mask; } @@ -1032,14 +626,13 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, if (HIBYTE(vk) & 4) { outMask |= KeyModifierAlt; } - log((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%04x", code, LOBYTE(vk), outMask)); // handle combination of caps-lock and shift. if caps-lock is // off locally then use shift as necessary. if caps-lock is on // locally then it reverses the meaning of shift for keys that // are subject to case conversion. if ((outMask & KeyModifierCapsLock) != 0) { - if (tolower(code) != toupper(code)) { + if (tolower(ascii) != toupper(ascii)) { log((CLOG_DEBUG2 "flip shift")); outMask ^= KeyModifierShift; } @@ -1072,11 +665,12 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, } } - // check for ISO_Left_Tab - else if (id == 0xfe20) { + // check for left tab + else if (id == kKeyLeftTab) { outMask |= KeyModifierShift; } } + log((CLOG_DEBUG2 "KeyID %d to virtual key %d mask 0x%04x", id, virtualKey, outMask)); // a list of modifier key info class CModifierInfo { diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 9c0193a3..fa78e8fb 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -589,262 +589,262 @@ CMSWindowsPrimaryScreen::ignore() const static const KeyID g_virtualKey[] = { - /* 0x00 */ kKeyNone, // reserved - /* 0x01 */ kKeyNone, // VK_LBUTTON - /* 0x02 */ kKeyNone, // VK_RBUTTON - /* 0x03 */ 0xff6b, // VK_CANCEL XK_Break - /* 0x04 */ kKeyNone, // VK_MBUTTON - /* 0x05 */ kKeyNone, // undefined - /* 0x06 */ kKeyNone, // undefined - /* 0x07 */ kKeyNone, // undefined - /* 0x08 */ 0xff08, // VK_BACK XK_Backspace - /* 0x09 */ 0xff09, // VK_TAB VK_Tab - /* 0x0a */ kKeyNone, // undefined - /* 0x0b */ kKeyNone, // undefined - /* 0x0c */ 0xff0b, // VK_CLEAR XK_Clear - /* 0x0d */ 0xff0d, // VK_RETURN XK_Return - /* 0x0e */ kKeyNone, // undefined - /* 0x0f */ kKeyNone, // undefined - /* 0x10 */ 0xffe1, // VK_SHIFT XK_Shift_L - /* 0x11 */ 0xffe3, // VK_CONTROL XK_Control_L - /* 0x12 */ 0xffe9, // VK_MENU XK_Alt_L - /* 0x13 */ 0xff13, // VK_PAUSE XK_Pause - /* 0x14 */ 0xffe5, // VK_CAPITAL XK_Caps_Lock - /* 0x15 */ kKeyNone, // VK_KANA - /* 0x16 */ kKeyNone, // VK_HANGUL - /* 0x17 */ kKeyNone, // VK_JUNJA - /* 0x18 */ kKeyNone, // VK_FINAL - /* 0x19 */ kKeyNone, // VK_KANJI - /* 0x1a */ kKeyNone, // undefined - /* 0x1b */ 0xff1b, // VK_ESCAPE XK_Escape - /* 0x1c */ kKeyNone, // VK_CONVERT - /* 0x1d */ kKeyNone, // VK_NONCONVERT - /* 0x1e */ kKeyNone, // VK_ACCEPT - /* 0x1f */ kKeyNone, // VK_MODECHANGE - /* 0x20 */ 0x0020, // VK_SPACE XK_space - /* 0x21 */ 0xff55, // VK_PRIOR XK_Prior - /* 0x22 */ 0xff56, // VK_NEXT XK_Next - /* 0x23 */ 0xff57, // VK_END XK_End - /* 0x24 */ 0xff50, // VK_HOME XK_Home - /* 0x25 */ 0xff51, // VK_LEFT XK_Left - /* 0x26 */ 0xff52, // VK_UP XK_Up - /* 0x27 */ 0xff53, // VK_RIGHT XK_Right - /* 0x28 */ 0xff54, // VK_DOWN XK_Down - /* 0x29 */ 0xff60, // VK_SELECT XK_Select - /* 0x2a */ kKeyNone, // VK_PRINT - /* 0x2b */ 0xff62, // VK_EXECUTE XK_Execute - /* 0x2c */ 0xff61, // VK_SNAPSHOT XK_Print - /* 0x2d */ 0xff63, // VK_INSERT XK_Insert - /* 0x2e */ 0xffff, // VK_DELETE XK_Delete - /* 0x2f */ 0xff6a, // VK_HELP XK_Help - /* 0x30 */ kKeyNone, // VK_0 XK_0 - /* 0x31 */ kKeyNone, // VK_1 XK_1 - /* 0x32 */ kKeyNone, // VK_2 XK_2 - /* 0x33 */ kKeyNone, // VK_3 XK_3 - /* 0x34 */ kKeyNone, // VK_4 XK_4 - /* 0x35 */ kKeyNone, // VK_5 XK_5 - /* 0x36 */ kKeyNone, // VK_6 XK_6 - /* 0x37 */ kKeyNone, // VK_7 XK_7 - /* 0x38 */ kKeyNone, // VK_8 XK_8 - /* 0x39 */ kKeyNone, // VK_9 XK_9 - /* 0x3a */ kKeyNone, // undefined - /* 0x3b */ kKeyNone, // undefined - /* 0x3c */ kKeyNone, // undefined - /* 0x3d */ kKeyNone, // undefined - /* 0x3e */ kKeyNone, // undefined - /* 0x3f */ kKeyNone, // undefined - /* 0x40 */ kKeyNone, // undefined - /* 0x41 */ kKeyNone, // VK_A XK_A - /* 0x42 */ kKeyNone, // VK_B XK_B - /* 0x43 */ kKeyNone, // VK_C XK_C - /* 0x44 */ kKeyNone, // VK_D XK_D - /* 0x45 */ kKeyNone, // VK_E XK_E - /* 0x46 */ kKeyNone, // VK_F XK_F - /* 0x47 */ kKeyNone, // VK_G XK_G - /* 0x48 */ kKeyNone, // VK_H XK_H - /* 0x49 */ kKeyNone, // VK_I XK_I - /* 0x4a */ kKeyNone, // VK_J XK_J - /* 0x4b */ kKeyNone, // VK_K XK_K - /* 0x4c */ kKeyNone, // VK_L XK_L - /* 0x4d */ kKeyNone, // VK_M XK_M - /* 0x4e */ kKeyNone, // VK_N XK_N - /* 0x4f */ kKeyNone, // VK_O XK_O - /* 0x50 */ kKeyNone, // VK_P XK_P - /* 0x51 */ kKeyNone, // VK_Q XK_Q - /* 0x52 */ kKeyNone, // VK_R XK_R - /* 0x53 */ kKeyNone, // VK_S XK_S - /* 0x54 */ kKeyNone, // VK_T XK_T - /* 0x55 */ kKeyNone, // VK_U XK_U - /* 0x56 */ kKeyNone, // VK_V XK_V - /* 0x57 */ kKeyNone, // VK_W XK_W - /* 0x58 */ kKeyNone, // VK_X XK_X - /* 0x59 */ kKeyNone, // VK_Y XK_Y - /* 0x5a */ kKeyNone, // VK_Z XK_Z - /* 0x5b */ 0xffe7, // VK_LWIN XK_Meta_L - /* 0x5c */ 0xffe8, // VK_RWIN XK_Meta_R - /* 0x5d */ 0xff67, // VK_APPS XK_Menu - /* 0x5e */ kKeyNone, // undefined - /* 0x5f */ kKeyNone, // undefined - /* 0x60 */ 0xffb0, // VK_NUMPAD0 XK_KP_0 - /* 0x61 */ 0xffb1, // VK_NUMPAD1 XK_KP_1 - /* 0x62 */ 0xffb2, // VK_NUMPAD2 XK_KP_2 - /* 0x63 */ 0xffb3, // VK_NUMPAD3 XK_KP_3 - /* 0x64 */ 0xffb4, // VK_NUMPAD4 XK_KP_4 - /* 0x65 */ 0xffb5, // VK_NUMPAD5 XK_KP_5 - /* 0x66 */ 0xffb6, // VK_NUMPAD6 XK_KP_6 - /* 0x67 */ 0xffb7, // VK_NUMPAD7 XK_KP_7 - /* 0x68 */ 0xffb8, // VK_NUMPAD8 XK_KP_8 - /* 0x69 */ 0xffb9, // VK_NUMPAD9 XK_KP_9 - /* 0x6a */ 0xffaa, // VK_MULTIPLY XK_KP_Multiply - /* 0x6b */ 0xffab, // VK_ADD XK_KP_Add - /* 0x6c */ 0xffac, // VK_SEPARATOR XK_KP_Separator - /* 0x6d */ 0xffad, // VK_SUBTRACT XK_KP_Subtract - /* 0x6e */ 0xffae, // VK_DECIMAL XK_KP_Decimal - /* 0x6f */ 0xffaf, // VK_DIVIDE XK_KP_Divide - /* 0x70 */ 0xffbe, // VK_F1 XK_F1 - /* 0x71 */ 0xffbf, // VK_F2 XK_F2 - /* 0x72 */ 0xffc0, // VK_F3 XK_F3 - /* 0x73 */ 0xffc1, // VK_F4 XK_F4 - /* 0x74 */ 0xffc2, // VK_F5 XK_F5 - /* 0x75 */ 0xffc3, // VK_F6 XK_F6 - /* 0x76 */ 0xffc4, // VK_F7 XK_F7 - /* 0x77 */ 0xffc5, // VK_F8 XK_F8 - /* 0x78 */ 0xffc6, // VK_F9 XK_F9 - /* 0x79 */ 0xffc7, // VK_F10 XK_F10 - /* 0x7a */ 0xffc8, // VK_F11 XK_F11 - /* 0x7b */ 0xffc9, // VK_F12 XK_F12 - /* 0x7c */ 0xffca, // VK_F13 XK_F13 - /* 0x7d */ 0xffcb, // VK_F14 XK_F14 - /* 0x7e */ 0xffcc, // VK_F15 XK_F15 - /* 0x7f */ 0xffcd, // VK_F16 XK_F16 - /* 0x80 */ 0xffce, // VK_F17 XK_F17 - /* 0x81 */ 0xffcf, // VK_F18 XK_F18 - /* 0x82 */ 0xffd0, // VK_F19 XK_F19 - /* 0x83 */ 0xffd1, // VK_F20 XK_F20 - /* 0x84 */ 0xffd2, // VK_F21 XK_F21 - /* 0x85 */ 0xffd3, // VK_F22 XK_F22 - /* 0x86 */ 0xffd4, // VK_F23 XK_F23 - /* 0x87 */ 0xffd5, // VK_F24 XK_F24 - /* 0x88 */ kKeyNone, // unassigned - /* 0x89 */ kKeyNone, // unassigned - /* 0x8a */ kKeyNone, // unassigned - /* 0x8b */ kKeyNone, // unassigned - /* 0x8c */ kKeyNone, // unassigned - /* 0x8d */ kKeyNone, // unassigned - /* 0x8e */ kKeyNone, // unassigned - /* 0x8f */ kKeyNone, // unassigned - /* 0x90 */ 0xff7f, // VK_NUMLOCK XK_Num_Lock - /* 0x91 */ 0xff14, // VK_SCROLL XK_Scroll_Lock - /* 0x92 */ kKeyNone, // unassigned - /* 0x93 */ kKeyNone, // unassigned - /* 0x94 */ kKeyNone, // unassigned - /* 0x95 */ kKeyNone, // unassigned - /* 0x96 */ kKeyNone, // unassigned - /* 0x97 */ kKeyNone, // unassigned - /* 0x98 */ kKeyNone, // unassigned - /* 0x99 */ kKeyNone, // unassigned - /* 0x9a */ kKeyNone, // unassigned - /* 0x9b */ kKeyNone, // unassigned - /* 0x9c */ kKeyNone, // unassigned - /* 0x9d */ kKeyNone, // unassigned - /* 0x9e */ kKeyNone, // unassigned - /* 0x9f */ kKeyNone, // unassigned - /* 0xa0 */ 0xffe1, // VK_LSHIFT XK_Shift_L - /* 0xa1 */ 0xffe2, // VK_RSHIFT XK_Shift_R - /* 0xa2 */ 0xffe3, // VK_LCONTROL XK_Control_L - /* 0xa3 */ 0xffe4, // VK_RCONTROL XK_Control_R - /* 0xa4 */ 0xffe9, // VK_LMENU XK_Alt_L - /* 0xa5 */ 0xffea, // VK_RMENU XK_Alt_R - /* 0xa6 */ kKeyNone, // unassigned - /* 0xa7 */ kKeyNone, // unassigned - /* 0xa8 */ kKeyNone, // unassigned - /* 0xa9 */ kKeyNone, // unassigned - /* 0xaa */ kKeyNone, // unassigned - /* 0xab */ kKeyNone, // unassigned - /* 0xac */ kKeyNone, // unassigned - /* 0xad */ kKeyNone, // unassigned - /* 0xae */ kKeyNone, // unassigned - /* 0xaf */ kKeyNone, // unassigned - /* 0xb0 */ kKeyNone, // unassigned - /* 0xb1 */ kKeyNone, // unassigned - /* 0xb2 */ kKeyNone, // unassigned - /* 0xb3 */ kKeyNone, // unassigned - /* 0xb4 */ kKeyNone, // unassigned - /* 0xb5 */ kKeyNone, // unassigned - /* 0xb6 */ kKeyNone, // unassigned - /* 0xb7 */ kKeyNone, // unassigned - /* 0xb8 */ kKeyNone, // unassigned - /* 0xb9 */ kKeyNone, // unassigned - /* 0xba */ kKeyNone, // OEM specific - /* 0xbb */ kKeyNone, // OEM specific - /* 0xbc */ kKeyNone, // OEM specific - /* 0xbd */ kKeyNone, // OEM specific - /* 0xbe */ kKeyNone, // OEM specific - /* 0xbf */ kKeyNone, // OEM specific - /* 0xc0 */ kKeyNone, // OEM specific - /* 0xc1 */ kKeyNone, // unassigned - /* 0xc2 */ kKeyNone, // unassigned - /* 0xc3 */ kKeyNone, // unassigned - /* 0xc4 */ kKeyNone, // unassigned - /* 0xc5 */ kKeyNone, // unassigned - /* 0xc6 */ kKeyNone, // unassigned - /* 0xc7 */ kKeyNone, // unassigned - /* 0xc8 */ kKeyNone, // unassigned - /* 0xc9 */ kKeyNone, // unassigned - /* 0xca */ kKeyNone, // unassigned - /* 0xcb */ kKeyNone, // unassigned - /* 0xcc */ kKeyNone, // unassigned - /* 0xcd */ kKeyNone, // unassigned - /* 0xce */ kKeyNone, // unassigned - /* 0xcf */ kKeyNone, // unassigned - /* 0xd0 */ kKeyNone, // unassigned - /* 0xd1 */ kKeyNone, // unassigned - /* 0xd2 */ kKeyNone, // unassigned - /* 0xd3 */ kKeyNone, // unassigned - /* 0xd4 */ kKeyNone, // unassigned - /* 0xd5 */ kKeyNone, // unassigned - /* 0xd6 */ kKeyNone, // unassigned - /* 0xd7 */ kKeyNone, // unassigned - /* 0xd8 */ kKeyNone, // unassigned - /* 0xd9 */ kKeyNone, // unassigned - /* 0xda */ kKeyNone, // unassigned - /* 0xdb */ kKeyNone, // OEM specific - /* 0xdc */ kKeyNone, // OEM specific - /* 0xdd */ kKeyNone, // OEM specific - /* 0xde */ kKeyNone, // OEM specific - /* 0xdf */ kKeyNone, // OEM specific - /* 0xe0 */ kKeyNone, // OEM specific - /* 0xe1 */ kKeyNone, // OEM specific - /* 0xe2 */ kKeyNone, // OEM specific - /* 0xe3 */ kKeyNone, // OEM specific - /* 0xe4 */ kKeyNone, // OEM specific - /* 0xe5 */ kKeyNone, // unassigned - /* 0xe6 */ kKeyNone, // OEM specific - /* 0xe7 */ kKeyNone, // unassigned - /* 0xe8 */ kKeyNone, // unassigned - /* 0xe9 */ kKeyNone, // OEM specific - /* 0xea */ kKeyNone, // OEM specific - /* 0xeb */ kKeyNone, // OEM specific - /* 0xec */ kKeyNone, // OEM specific - /* 0xed */ kKeyNone, // OEM specific - /* 0xee */ kKeyNone, // OEM specific - /* 0xef */ kKeyNone, // OEM specific - /* 0xf0 */ kKeyNone, // OEM specific - /* 0xf1 */ kKeyNone, // OEM specific - /* 0xf2 */ kKeyNone, // OEM specific - /* 0xf3 */ kKeyNone, // OEM specific - /* 0xf4 */ kKeyNone, // OEM specific - /* 0xf5 */ kKeyNone, // OEM specific - /* 0xf6 */ kKeyNone, // VK_ATTN - /* 0xf7 */ kKeyNone, // VK_CRSEL - /* 0xf8 */ kKeyNone, // VK_EXSEL - /* 0xf9 */ kKeyNone, // VK_EREOF - /* 0xfa */ kKeyNone, // VK_PLAY - /* 0xfb */ kKeyNone, // VK_ZOOM - /* 0xfc */ kKeyNone, // reserved - /* 0xfd */ kKeyNone, // VK_PA1 - /* 0xfe */ kKeyNone, // VK_OEM_CLEAR - /* 0xff */ kKeyNone // reserved + /* 0x00 */ kKeyNone, // reserved + /* 0x01 */ kKeyNone, // VK_LBUTTON + /* 0x02 */ kKeyNone, // VK_RBUTTON + /* 0x03 */ kKeyBreak, // VK_CANCEL + /* 0x04 */ kKeyNone, // VK_MBUTTON + /* 0x05 */ kKeyNone, // undefined + /* 0x06 */ kKeyNone, // undefined + /* 0x07 */ kKeyNone, // undefined + /* 0x08 */ kKeyBackSpace, // VK_BACK + /* 0x09 */ kKeyTab, // VK_TAB + /* 0x0a */ kKeyNone, // undefined + /* 0x0b */ kKeyNone, // undefined + /* 0x0c */ kKeyClear, // VK_CLEAR + /* 0x0d */ kKeyReturn, // VK_RETURN + /* 0x0e */ kKeyNone, // undefined + /* 0x0f */ kKeyNone, // undefined + /* 0x10 */ kKeyShift_L, // VK_SHIFT + /* 0x11 */ kKeyControl_L, // VK_CONTROL + /* 0x12 */ kKeyAlt_L, // VK_MENU + /* 0x13 */ kKeyPause, // VK_PAUSE + /* 0x14 */ kKeyCapsLock, // VK_CAPITAL + /* 0x15 */ kKeyNone, // VK_KANA + /* 0x16 */ kKeyNone, // VK_HANGUL + /* 0x17 */ kKeyNone, // VK_JUNJA + /* 0x18 */ kKeyNone, // VK_FINAL + /* 0x19 */ kKeyNone, // VK_KANJI + /* 0x1a */ kKeyNone, // undefined + /* 0x1b */ kKeyEscape, // VK_ESCAPE + /* 0x1c */ kKeyNone, // VK_CONVERT + /* 0x1d */ kKeyNone, // VK_NONCONVERT + /* 0x1e */ kKeyNone, // VK_ACCEPT + /* 0x1f */ kKeyNone, // VK_MODECHANGE + /* 0x20 */ 0x0020, // VK_SPACE + /* 0x21 */ kKeyPageUp, // VK_PRIOR + /* 0x22 */ kKeyPageDown, // VK_NEXT + /* 0x23 */ kKeyEnd, // VK_END + /* 0x24 */ kKeyHome, // VK_HOME + /* 0x25 */ kKeyLeft, // VK_LEFT + /* 0x26 */ kKeyUp, // VK_UP + /* 0x27 */ kKeyRight, // VK_RIGHT + /* 0x28 */ kKeyDown, // VK_DOWN + /* 0x29 */ kKeySelect, // VK_SELECT + /* 0x2a */ kKeyNone, // VK_PRINT + /* 0x2b */ kKeyExecute, // VK_EXECUTE + /* 0x2c */ kKeyPrint, // VK_SNAPSHOT + /* 0x2d */ kKeyInsert, // VK_INSERT + /* 0x2e */ kKeyDelete, // VK_DELETE + /* 0x2f */ kKeyHelp, // VK_HELP + /* 0x30 */ kKeyNone, // VK_0 + /* 0x31 */ kKeyNone, // VK_1 + /* 0x32 */ kKeyNone, // VK_2 + /* 0x33 */ kKeyNone, // VK_3 + /* 0x34 */ kKeyNone, // VK_4 + /* 0x35 */ kKeyNone, // VK_5 + /* 0x36 */ kKeyNone, // VK_6 + /* 0x37 */ kKeyNone, // VK_7 + /* 0x38 */ kKeyNone, // VK_8 + /* 0x39 */ kKeyNone, // VK_9 + /* 0x3a */ kKeyNone, // undefined + /* 0x3b */ kKeyNone, // undefined + /* 0x3c */ kKeyNone, // undefined + /* 0x3d */ kKeyNone, // undefined + /* 0x3e */ kKeyNone, // undefined + /* 0x3f */ kKeyNone, // undefined + /* 0x40 */ kKeyNone, // undefined + /* 0x41 */ kKeyNone, // VK_A + /* 0x42 */ kKeyNone, // VK_B + /* 0x43 */ kKeyNone, // VK_C + /* 0x44 */ kKeyNone, // VK_D + /* 0x45 */ kKeyNone, // VK_E + /* 0x46 */ kKeyNone, // VK_F + /* 0x47 */ kKeyNone, // VK_G + /* 0x48 */ kKeyNone, // VK_H + /* 0x49 */ kKeyNone, // VK_I + /* 0x4a */ kKeyNone, // VK_J + /* 0x4b */ kKeyNone, // VK_K + /* 0x4c */ kKeyNone, // VK_L + /* 0x4d */ kKeyNone, // VK_M + /* 0x4e */ kKeyNone, // VK_N + /* 0x4f */ kKeyNone, // VK_O + /* 0x50 */ kKeyNone, // VK_P + /* 0x51 */ kKeyNone, // VK_Q + /* 0x52 */ kKeyNone, // VK_R + /* 0x53 */ kKeyNone, // VK_S + /* 0x54 */ kKeyNone, // VK_T + /* 0x55 */ kKeyNone, // VK_U + /* 0x56 */ kKeyNone, // VK_V + /* 0x57 */ kKeyNone, // VK_W + /* 0x58 */ kKeyNone, // VK_X + /* 0x59 */ kKeyNone, // VK_Y + /* 0x5a */ kKeyNone, // VK_Z + /* 0x5b */ kKeyMeta_L, // VK_LWIN + /* 0x5c */ kKeyMeta_R, // VK_RWIN + /* 0x5d */ kKeyMenu, // VK_APPS + /* 0x5e */ kKeyNone, // undefined + /* 0x5f */ kKeyNone, // undefined + /* 0x60 */ kKeyKP_0, // VK_NUMPAD0 + /* 0x61 */ kKeyKP_1, // VK_NUMPAD1 + /* 0x62 */ kKeyKP_2, // VK_NUMPAD2 + /* 0x63 */ kKeyKP_3, // VK_NUMPAD3 + /* 0x64 */ kKeyKP_4, // VK_NUMPAD4 + /* 0x65 */ kKeyKP_5, // VK_NUMPAD5 + /* 0x66 */ kKeyKP_6, // VK_NUMPAD6 + /* 0x67 */ kKeyKP_7, // VK_NUMPAD7 + /* 0x68 */ kKeyKP_8, // VK_NUMPAD8 + /* 0x69 */ kKeyKP_9, // VK_NUMPAD9 + /* 0x6a */ kKeyKP_Multiply, // VK_MULTIPLY + /* 0x6b */ kKeyKP_Add, // VK_ADD + /* 0x6c */ kKeyKP_Separator,// VK_SEPARATOR + /* 0x6d */ kKeyKP_Subtract, // VK_SUBTRACT + /* 0x6e */ kKeyKP_Decimal, // VK_DECIMAL + /* 0x6f */ kKeyKP_Divide, // VK_DIVIDE + /* 0x70 */ kKeyF1, // VK_F1 + /* 0x71 */ kKeyF2, // VK_F2 + /* 0x72 */ kKeyF3, // VK_F3 + /* 0x73 */ kKeyF4, // VK_F4 + /* 0x74 */ kKeyF5, // VK_F5 + /* 0x75 */ kKeyF6, // VK_F6 + /* 0x76 */ kKeyF7, // VK_F7 + /* 0x77 */ kKeyF8, // VK_F8 + /* 0x78 */ kKeyF9, // VK_F9 + /* 0x79 */ kKeyF10, // VK_F10 + /* 0x7a */ kKeyF11, // VK_F11 + /* 0x7b */ kKeyF12, // VK_F12 + /* 0x7c */ kKeyF13, // VK_F13 + /* 0x7d */ kKeyF14, // VK_F14 + /* 0x7e */ kKeyF15, // VK_F15 + /* 0x7f */ kKeyF16, // VK_F16 + /* 0x80 */ kKeyF17, // VK_F17 + /* 0x81 */ kKeyF18, // VK_F18 + /* 0x82 */ kKeyF19, // VK_F19 + /* 0x83 */ kKeyF20, // VK_F20 + /* 0x84 */ kKeyF21, // VK_F21 + /* 0x85 */ kKeyF22, // VK_F22 + /* 0x86 */ kKeyF23, // VK_F23 + /* 0x87 */ kKeyF24, // VK_F24 + /* 0x88 */ kKeyNone, // unassigned + /* 0x89 */ kKeyNone, // unassigned + /* 0x8a */ kKeyNone, // unassigned + /* 0x8b */ kKeyNone, // unassigned + /* 0x8c */ kKeyNone, // unassigned + /* 0x8d */ kKeyNone, // unassigned + /* 0x8e */ kKeyNone, // unassigned + /* 0x8f */ kKeyNone, // unassigned + /* 0x90 */ kKeyNumLock, // VK_NUMLOCK + /* 0x91 */ kKeyScrollLock, // VK_SCROLL + /* 0x92 */ kKeyNone, // unassigned + /* 0x93 */ kKeyNone, // unassigned + /* 0x94 */ kKeyNone, // unassigned + /* 0x95 */ kKeyNone, // unassigned + /* 0x96 */ kKeyNone, // unassigned + /* 0x97 */ kKeyNone, // unassigned + /* 0x98 */ kKeyNone, // unassigned + /* 0x99 */ kKeyNone, // unassigned + /* 0x9a */ kKeyNone, // unassigned + /* 0x9b */ kKeyNone, // unassigned + /* 0x9c */ kKeyNone, // unassigned + /* 0x9d */ kKeyNone, // unassigned + /* 0x9e */ kKeyNone, // unassigned + /* 0x9f */ kKeyNone, // unassigned + /* 0xa0 */ kKeyShift_L, // VK_LSHIFT + /* 0xa1 */ kKeyShift_R, // VK_RSHIFT + /* 0xa2 */ kKeyControl_L, // VK_LCONTROL + /* 0xa3 */ kKeyControl_R, // VK_RCONTROL + /* 0xa4 */ kKeyAlt_L, // VK_LMENU + /* 0xa5 */ kKeyAlt_R, // VK_RMENU + /* 0xa6 */ kKeyNone, // unassigned + /* 0xa7 */ kKeyNone, // unassigned + /* 0xa8 */ kKeyNone, // unassigned + /* 0xa9 */ kKeyNone, // unassigned + /* 0xaa */ kKeyNone, // unassigned + /* 0xab */ kKeyNone, // unassigned + /* 0xac */ kKeyNone, // unassigned + /* 0xad */ kKeyNone, // unassigned + /* 0xae */ kKeyNone, // unassigned + /* 0xaf */ kKeyNone, // unassigned + /* 0xb0 */ kKeyNone, // unassigned + /* 0xb1 */ kKeyNone, // unassigned + /* 0xb2 */ kKeyNone, // unassigned + /* 0xb3 */ kKeyNone, // unassigned + /* 0xb4 */ kKeyNone, // unassigned + /* 0xb5 */ kKeyNone, // unassigned + /* 0xb6 */ kKeyNone, // unassigned + /* 0xb7 */ kKeyNone, // unassigned + /* 0xb8 */ kKeyNone, // unassigned + /* 0xb9 */ kKeyNone, // unassigned + /* 0xba */ kKeyNone, // OEM specific + /* 0xbb */ kKeyNone, // OEM specific + /* 0xbc */ kKeyNone, // OEM specific + /* 0xbd */ kKeyNone, // OEM specific + /* 0xbe */ kKeyNone, // OEM specific + /* 0xbf */ kKeyNone, // OEM specific + /* 0xc0 */ kKeyNone, // OEM specific + /* 0xc1 */ kKeyNone, // unassigned + /* 0xc2 */ kKeyNone, // unassigned + /* 0xc3 */ kKeyNone, // unassigned + /* 0xc4 */ kKeyNone, // unassigned + /* 0xc5 */ kKeyNone, // unassigned + /* 0xc6 */ kKeyNone, // unassigned + /* 0xc7 */ kKeyNone, // unassigned + /* 0xc8 */ kKeyNone, // unassigned + /* 0xc9 */ kKeyNone, // unassigned + /* 0xca */ kKeyNone, // unassigned + /* 0xcb */ kKeyNone, // unassigned + /* 0xcc */ kKeyNone, // unassigned + /* 0xcd */ kKeyNone, // unassigned + /* 0xce */ kKeyNone, // unassigned + /* 0xcf */ kKeyNone, // unassigned + /* 0xd0 */ kKeyNone, // unassigned + /* 0xd1 */ kKeyNone, // unassigned + /* 0xd2 */ kKeyNone, // unassigned + /* 0xd3 */ kKeyNone, // unassigned + /* 0xd4 */ kKeyNone, // unassigned + /* 0xd5 */ kKeyNone, // unassigned + /* 0xd6 */ kKeyNone, // unassigned + /* 0xd7 */ kKeyNone, // unassigned + /* 0xd8 */ kKeyNone, // unassigned + /* 0xd9 */ kKeyNone, // unassigned + /* 0xda */ kKeyNone, // unassigned + /* 0xdb */ kKeyNone, // OEM specific + /* 0xdc */ kKeyNone, // OEM specific + /* 0xdd */ kKeyNone, // OEM specific + /* 0xde */ kKeyNone, // OEM specific + /* 0xdf */ kKeyNone, // OEM specific + /* 0xe0 */ kKeyNone, // OEM specific + /* 0xe1 */ kKeyNone, // OEM specific + /* 0xe2 */ kKeyNone, // OEM specific + /* 0xe3 */ kKeyNone, // OEM specific + /* 0xe4 */ kKeyNone, // OEM specific + /* 0xe5 */ kKeyNone, // unassigned + /* 0xe6 */ kKeyNone, // OEM specific + /* 0xe7 */ kKeyNone, // unassigned + /* 0xe8 */ kKeyNone, // unassigned + /* 0xe9 */ kKeyNone, // OEM specific + /* 0xea */ kKeyNone, // OEM specific + /* 0xeb */ kKeyNone, // OEM specific + /* 0xec */ kKeyNone, // OEM specific + /* 0xed */ kKeyNone, // OEM specific + /* 0xee */ kKeyNone, // OEM specific + /* 0xef */ kKeyNone, // OEM specific + /* 0xf0 */ kKeyNone, // OEM specific + /* 0xf1 */ kKeyNone, // OEM specific + /* 0xf2 */ kKeyNone, // OEM specific + /* 0xf3 */ kKeyNone, // OEM specific + /* 0xf4 */ kKeyNone, // OEM specific + /* 0xf5 */ kKeyNone, // OEM specific + /* 0xf6 */ kKeyNone, // VK_ATTN + /* 0xf7 */ kKeyNone, // VK_CRSEL + /* 0xf8 */ kKeyNone, // VK_EXSEL + /* 0xf9 */ kKeyNone, // VK_EREOF + /* 0xfa */ kKeyNone, // VK_PLAY + /* 0xfb */ kKeyNone, // VK_ZOOM + /* 0xfc */ kKeyNone, // reserved + /* 0xfd */ kKeyNone, // VK_PA1 + /* 0xfe */ kKeyNone, // VK_OEM_CLEAR + /* 0xff */ kKeyNone // reserved }; KeyID @@ -859,8 +859,6 @@ CMSWindowsPrimaryScreen::mapKey( // 95,98,NT4: num pad scan code -> bad vk code except // SEPARATOR, MULTIPLY, SUBTRACT, ADD - static const KeyID XK_Multi_key = 0xff20; - assert(maskOut != NULL); // map modifier key @@ -952,7 +950,7 @@ CMSWindowsPrimaryScreen::mapKey( // check for dead keys if (MapVirtualKey(vkCode, 2) >= 0x8000) { - return XK_Multi_key; + return kKeyMultiKey; } // ToAscii() maps ctrl+letter to the corresponding control code @@ -968,6 +966,7 @@ CMSWindowsPrimaryScreen::mapKey( m_keys[VK_CONTROL] = 0; // convert to ascii + // FIXME -- support unicode WORD ascii; int result = ToAscii(vkCode, scanCode, m_keys, &ascii, 0); @@ -981,7 +980,7 @@ CMSWindowsPrimaryScreen::mapKey( // by calling ToAscii() again with arbitrary arguments. if (result < 0) { ToAscii(vkCode, scanCode, m_keys, &ascii, 0); - return XK_Multi_key; + return kKeyMultiKey; } // if result is 1 then the key was succesfully converted @@ -1017,7 +1016,7 @@ CMSWindowsPrimaryScreen::mapKey( // put it back ToAscii(vkCode, scanCode, keys, &ascii, 0); - return XK_Multi_key; + return kKeyMultiKey; } // cannot convert key diff --git a/synergy/KeyTypes.h b/synergy/KeyTypes.h index 947f48f6..37e54184 100644 --- a/synergy/KeyTypes.h +++ b/synergy/KeyTypes.h @@ -40,6 +40,9 @@ static const KeyID kKeySysReq = 0xEF15; static const KeyID kKeyEscape = 0xEF1B; static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ +// multi-key character composition +static const KeyID kKeyMultiKey = 0xEF20; /* Multi-key character compose */ + // cursor control static const KeyID kKeyHome = 0xEF50; static const KeyID kKeyLeft = 0xEF51; /* Move left, left arrow */ From 7d7b7f85ca76c032933c094c131c90fedea1e0a9 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 18:59:44 +0000 Subject: [PATCH 258/807] fixed a bug in clipboard conversion (was using wrong converter or no converter when one was available). --- platform/CMSWindowsClipboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/CMSWindowsClipboard.cpp b/platform/CMSWindowsClipboard.cpp index 7cefd466..57fe856b 100644 --- a/platform/CMSWindowsClipboard.cpp +++ b/platform/CMSWindowsClipboard.cpp @@ -108,7 +108,7 @@ CMSWindowsClipboard::get(EFormat format) const // find the converter for the first clipboard format we can handle IMSWindowsClipboardConverter* converter = NULL; UINT win32Format = EnumClipboardFormats(0); - while (win32Format != 0) { + while (converter == NULL && win32Format != 0) { for (ConverterList::const_iterator index = m_converters.begin(); index != m_converters.end(); ++index) { converter = *index; From d9ec8802917e02aa5c69a084e78adf7f196e98a1 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 23 Jul 2002 19:00:01 +0000 Subject: [PATCH 259/807] checkpoint. --- notes | 161 +++------------------------------------------------------- 1 file changed, 6 insertions(+), 155 deletions(-) diff --git a/notes b/notes index eda240ce..69debad0 100644 --- a/notes +++ b/notes @@ -1,13 +1,3 @@ -server client ------- ------ -[accept] <-- connect -challenge --> [encrypt] -[verify] <-- response (encrypted challenge, client name) -hangup if invalid -query info --> - <-- info (size) - ---- nedit doesn't seem to be playing nice with the motif clipboard lock it's not releasing it may need to learn more about how the motif clipboard lock works @@ -19,15 +9,14 @@ enable heartbeat disconnection or remove heartbeat sending in client possibly make it part of startup negotiation --- -getting a stuttering when leaving win32 server screen +not handling large motif selections + there must be a limit because properties can only be so big + don't know protocol for transfer, though --- -merge platform dependent code into platform independent where possible - also try to simplify where possible - ---- -IServer and IPrimaryReceiver are really similar - can we merge them into one? +synergy should never restart if X server connection was lost? + if synergy server connection was lost then restart + unless --no-restart --- use automake @@ -43,107 +32,6 @@ HTTP stuff provide way to query why locked to screen? handy for debugging at least ---- -negotiation: - use a generic negotiation message (name/value pair) or specific - messages (name/values)? later allows more parsing to be done by - CProtocolUtil but how can we skip unknown messages? would have - to have a method on CInputPacketStream to discard to end of - message and we'd need to know the stream is a CInputPacketStream. - - how does negotiation proceed? - could have sequence of queries and replies: - server -> client -> server ... end - client -> server -> client ... end - or a sequence of messages: - server -> client ... end - client -> server ... end - probably go with latter because it's more efficient but make it - 3-way: - client -> server ... end - server -> client ... end - client -> server ... end - first messages from client must be version and name. the server - can create the appropriate protocol object right away then. - - example: clipboard formats: - # CNEG = negotiation command CNEG%s%s - # CNGE = end of negotiation command - # cbfa = clipboard, format, add (permitted format) - # cbia = clipboard, id, add (clipboard with id exists) - client -> server: CNEG "vers" "1.0" - client -> server: CNEG "name" "foobar" - client -> server: CNGE - server -> client: CNEG "cbfa" "text/plain/LF" - server -> client: CNEG "cbfa" "image/BMP" - server -> client: CNGE - client -> server: CNEG "cbia" "0" - client -> server: CNGE - - server should just ask CProtocol (renamed from CServerProtocol) to - return a protocol. CProtocol should do negotiation, create the - appropriate protocol object, finish negotiation, and return the - new protocol object. unless connection is registered with server, - though, we can't save any screen info. perhaps CProtocol should - also return a screen info object or that should be part of the - protocol object (currently the protocol is part of the screen info). - if screen info is available to CProtocol then it should finish with - requesting the screen info, waiting for and then handling the reply. - the server can then just ask CProtocol for the protocol object then - add it to the connections. - - maybe call the protocol types CClientProxy since that's how the - server uses it: as a stand-in for the client object. the interface - should closely reflect the CClient interface. do the reverse for - a server proxy for the client. maybe even have interface classes - for the client and server methods. - ---- -should add clipboard data type format negotiation - server sends permitted data type formats - client responds with known formats in permitted list - client must only send permitted formats - server can discard non-permitted formats - server must only send known, permitted formats to client - formats are names of the form [a-zA-Z0-9_/]+ - server must be able to translate between all permitted formats - example: text formats: - text/plain/CRLF -- windows plain text - text/plain/LF -- unix plain text - text/plain/CR -- mac plain text - text/rtf -- rich text (does that require a particular newline?) - or image formats: - image/BMP - image/RAW - image/XPM - -this will allow future versions to interoperate better. it also -allows compatibility with versions that are identical except for -the support clipboard formats, obviating bumping up the protocol -version number. - -maybe config file can list format shared libraries to load. user -can then pick formats to support. but why would you ever want -fewer formats? - -should the major burden of translation be on client or server? -probably client since it knows the formats preferred by the -platform and may be able to use platform utilities to convert. -server would then just support formats that minimize loss of -information. - -desired formats to support: - text (LF) - text with character set - unicode (LF) - rich text (or some kind of formatting) - bitmap (BMP, PNG?) - sound (WAV) - -note that formats should be added to the win32 clipboard in the -order of most descriptive to least because they're kept in order -and apps will generally use the first suitable match. - --- hot keys should have keyboard shortcuts to jump to screens @@ -190,10 +78,6 @@ should switch to user nobody (or something similar) if running as root will make it impossible to overwrite /etc/synergy.conf if that file is only root writable (as it should be) ---- -Xsetup file may have to kill previous synergy - or synergy should exit if there's already a synergy running - --- not handling international characters @@ -221,17 +105,6 @@ not distinguishing between caps lock and shift lock and releasing the Lock key turns on the mode, and pressing and releasing either the Lock or the Shift key turns off the mode. ---- -currently sending all clipboards to all clients - some clients may not need all clipboards - add filtering mechanism - setup is probably part of clipboard (format) negotiation - ---- -not handling large motif selections - there must be a limit because properties can only be so big - don't know protocol for transfer, though - --- need timeout on CXWindowsClipboard::CReply objects should flush replies that are too old @@ -462,28 +335,6 @@ non-functional on ctrl+alt+del screen in win2k (kurt) i suppose it must be when win2k is the client. mouse input wasn't working. keyboard probably didn't work either. -gspencer: - OK, I've narrowed it down a little -- it seems to happen if you have any - key down (I tried the shift key and the letter 's' key) when you cross - over from master to client, which would make me suspect the "capture" - code, since if you have a key down you aren't supposed to be able to - cross over... - -gspencer: - bouncy mouse at edge of screen (win32 server). almost certainly - related to changes that fixed mouse delta calculation. - - -== fixed? === -dragging windows is too slow - grace (as client) exhibits this - dunno if it happens with audrey2 as the client - liz says her windows laptop does it too - also closing/iconifying has a delay - dragging multiple files also sluggish - -liz seems to still be seeing sluggish cursor updates - --- automake stuff rebuild: From c6ecc79c0d8937a7f5f7dba9eb3aef6775253881 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 24 Jul 2002 13:01:18 +0000 Subject: [PATCH 260/807] removed restart function from platform. no longer trying to restart if the X server connection was lost; since synergy is likely to be started by xdm or the user's xsession, it's better for synergy to simply terminate when the connection is lost. synergy will still restart due to other errors. also fixed numerous other minor bugs and cleaned some stuff up (like app error codes are now consistent and enumerated in Version.h, for lack of a better place). and boosted version and protocol numbers. --- client/CClient.cpp | 15 ++- client/client.cpp | 221 ++++++++++++++++---------------- net/CTCPSocket.cpp | 11 +- net/XSocket.h | 18 ++- platform/CUnixPlatform.cpp | 74 ----------- platform/CUnixPlatform.h | 1 - platform/CWin32Platform.cpp | 9 -- platform/CWin32Platform.h | 1 - platform/CXWindowsScreen.cpp | 10 +- platform/CXWindowsUtil.cpp | 8 +- platform/IPlatform.h | 6 - server/CPrimaryClient.cpp | 1 + server/CServer.cpp | 12 +- server/server.cpp | 236 +++++++++++++++++------------------ synergy/ProtocolTypes.h | 10 +- synergy/Version.h | 15 ++- 16 files changed, 301 insertions(+), 347 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 7c2d5fa4..6efa48b0 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -127,7 +127,6 @@ CClient::open() } catch (XScreenOpenFailure&) { // can't open screen yet. wait a few seconds to retry. - CThread::sleep(3.0); log((CLOG_INFO "failed to open screen")); return false; } @@ -360,7 +359,15 @@ CClient::openSecondaryScreen() m_screen = new CXWindowsSecondaryScreen(this); #endif log((CLOG_DEBUG1 "opening secondary screen")); - m_screen->open(); + try { + m_screen->open(); + } + catch (...) { + log((CLOG_DEBUG1 "destroying secondary screen")); + delete m_screen; + m_screen = NULL; + throw; + } } void @@ -472,7 +479,9 @@ CClient::runServer() log((CLOG_INFO "connected to server")); break; } - catch (XSocketConnect&) { + catch (XSocketConnect& e) { + log((CLOG_DEBUG1 "failed to connect to server: %s", e.getErrstr())); + // failed to connect. if not camping then rethrow. if (!m_camp) { throw; diff --git a/client/client.cpp b/client/client.cpp index cda8e0a9..0bda2676 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -67,95 +67,99 @@ static int realMain(CMutex* mutex) { - try { - // initialize threading library - CThread::init(); + // caller should have mutex locked on entry - // make logging thread safe - CMutex logMutex; - s_logMutex = &logMutex; - CLog::setLock(&logLock); - - bool locked = true; + int result = kExitSuccess; + do { try { - // create client - s_client = new CClient(s_name); - s_client->camp(s_camp); - s_client->setAddress(s_serverAddress); - if (!s_client->open()) { + // initialize threading library + CThread::init(); + + // make logging thread safe + CMutex logMutex; + s_logMutex = &logMutex; + CLog::setLock(&logLock); + + bool opened = false; + bool locked = true; + try { + // create client + s_client = new CClient(s_name); + s_client->camp(s_camp); + s_client->setAddress(s_serverAddress); + if (s_client->open()) { + opened = true; + + // run client + if (mutex != NULL) { + mutex->unlock(); + } + locked = false; + s_client->run(); + locked = true; + if (mutex != NULL) { + mutex->lock(); + } + + // get client status + if (s_client->wasRejected()) { + // wait a while before retrying. we don't want + // to bother the server very often if it doesn't + // want us. + if (s_restartable) { + CThread::sleep(60.0); + } + else { + result = kExitFailed; + } + } + + // clean up + s_client->close(); + } + else { + // wait a few seconds before retrying + if (s_restartable) { + CThread::sleep(3.0); + } + else { + result = kExitFailed; + } + } + + // clean up delete s_client; s_client = NULL; - return 16; + CLog::setLock(NULL); + s_logMutex = NULL; } - - // run client - if (mutex != NULL) { - mutex->unlock(); + catch (...) { + // clean up + if (!locked && mutex != NULL) { + mutex->lock(); + } + if (s_client != NULL) { + if (opened) { + s_client->close(); + } + delete s_client; + s_client = NULL; + } + CLog::setLock(NULL); + s_logMutex = NULL; + throw; } - locked = false; - s_client->run(); - locked = true; - if (mutex != NULL) { - mutex->lock(); - } - - // get client status - bool success = !s_client->wasRejected(); - - // clean up - s_client->close(); - delete s_client; - s_client = NULL; - CLog::setLock(NULL); - s_logMutex = NULL; - - return success ? 16 : 0; } - catch (...) { - // clean up - if (!locked && mutex != NULL) { - mutex->lock(); - } - if (s_client != NULL) { - s_client->close(); - delete s_client; - s_client = NULL; - } - CLog::setLock(NULL); - s_logMutex = NULL; - throw; + catch (XBase& e) { + log((CLOG_CRIT "failed: %s", e.what())); } - } - catch (XBase& e) { - log((CLOG_CRIT "failed: %s", e.what())); - return 16; - } - catch (XThread&) { - // terminated - return 1; - } -} + catch (XThread&) { + // terminated + return kExitTerminated; + } + } while (s_restartable); -static -int -restartMain() -{ - return realMain(NULL); -} - -// invoke realMain and wait for it. if s_restartable then keep -// restarting realMain until it returns a terminate code. -static -int -restartableMain() -{ - if (s_restartable) { - CPlatform platform; - return platform.restart(restartMain, 16); - } - else { - return realMain(NULL); - } + return result; } @@ -229,10 +233,7 @@ PLATFORM_ARGS " ourself to the server.\n" " -1, --no-restart do not try to restart the client if it fails for\n" " some reason.\n" -"* --restart restart the client automatically if it fails for\n" -" some unexpected reason, including the server\n" -" disconnecting but not including failing to\n" -" connect to the server.\n" +"* --restart restart the client automatically if it fails.\n" PLATFORM_DESC " -h, --help display this help and exit.\n" " --version display version information and exit.\n" @@ -261,7 +262,7 @@ isArg(int argi, int argc, const char** argv, if (argi + minRequiredParameters >= argc) { log((CLOG_PRINT "%s: missing arguments for `%s'" BYE, pname, argv[argi], pname)); - bye(2); + bye(kExitArgs); } return true; } @@ -329,12 +330,12 @@ parse(int argc, const char** argv) else if (isArg(i, argc, argv, "-h", "--help")) { help(); - bye(0); + bye(kExitSuccess); } else if (isArg(i, argc, argv, NULL, "--version")) { version(); - bye(0); + bye(kExitSuccess); } #if WINDOWS_LIKE @@ -344,7 +345,7 @@ parse(int argc, const char** argv) log((CLOG_PRINT "%s: `--install' and `--uninstall'" " are mutually exclusive" BYE, pname, argv[i], pname)); - bye(2); + bye(kExitArgs); } } #endif @@ -356,7 +357,7 @@ parse(int argc, const char** argv) log((CLOG_PRINT "%s: `--install' and `--uninstall'" " are mutually exclusive" BYE, pname, argv[i], pname)); - bye(2); + bye(kExitArgs); } } #endif @@ -370,7 +371,7 @@ parse(int argc, const char** argv) else if (argv[i][0] == '-') { log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, pname, argv[i], pname)); - bye(2); + bye(kExitArgs); } else { @@ -386,19 +387,19 @@ parse(int argc, const char** argv) log((CLOG_PRINT "%s: unrecognized option `%s' to `%s'" BYE, pname, argv[i], pname, s_install ? "--install" : "--uninstall")); - bye(2); + bye(kExitArgs); } } else { if (i == argc) { log((CLOG_PRINT "%s: a server address or name is required" BYE, pname, pname)); - bye(1); + bye(kExitArgs); } if (i + 1 != argc) { log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, pname, argv[i], pname)); - bye(2); + bye(kExitArgs); } // save server address @@ -408,7 +409,7 @@ parse(int argc, const char** argv) catch (XSocketAddress&) { log((CLOG_PRINT "%s: invalid server address" BYE, pname, pname)); - bye(2); + bye(kExitArgs); } } @@ -432,7 +433,7 @@ parse(int argc, const char** argv) if (!CLog::setFilter(s_logFilter)) { log((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, pname, s_logFilter, pname)); - bye(2); + bye(kExitArgs); } } @@ -488,7 +489,7 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) parse(argc, argv); if (s_install || s_uninstall) { // not allowed to install/uninstall from service - throw CWin32Platform::CDaemonFailed(1); + throw CWin32Platform::CDaemonFailed(kExitArgs); } // run as a service @@ -499,7 +500,7 @@ static int daemonStartup95(IPlatform*, int, const char**) { - return restartableMain(); + return realMain(NULL); } int WINAPI @@ -529,7 +530,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) int result = platform.daemonize(DAEMON_NAME, &daemonStartup); if (result == -1) { log((CLOG_CRIT "failed to start as a service" BYE, pname)); - return 16; + return kExitFailed; } return result; } @@ -544,7 +545,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) if (GetModuleFileName(NULL, path, sizeof(path) / sizeof(path[0])) == 0) { log((CLOG_CRIT "cannot determine absolute path to program")); - return 16; + return kExitFailed; } // construct the command line to start the service with @@ -567,24 +568,24 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) "Shares this system's mouse and keyboard with others.", path, commandLine.c_str())) { log((CLOG_CRIT "failed to install service")); - return 16; + return kExitFailed; } log((CLOG_PRINT "installed successfully")); - return 0; + return kExitSuccess; } else if (s_uninstall) { switch (platform.uninstallDaemon(DAEMON_NAME)) { case IPlatform::kSuccess: log((CLOG_PRINT "uninstalled successfully")); - return 0; + return kExitSuccess; case IPlatform::kFailed: log((CLOG_CRIT "failed to uninstall service")); - return 16; + return kExitFailed; case IPlatform::kAlready: log((CLOG_CRIT "service isn't installed")); - return 16; + return kExitFailed; } } @@ -599,18 +600,18 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) result = platform.daemonize(DAEMON_NAME, &daemonStartup95); if (result == -1) { log((CLOG_CRIT "failed to start as a service" BYE, pname)); - return 16; + return kExitFailed; } } else { // cannot start a service from the command line so just // run normally (except with log messages redirected). - result = restartableMain(); + result = realMain(NULL); } } else { // run - result = restartableMain(); + result = realMain(NULL); } CNetwork::cleanup(); @@ -624,7 +625,7 @@ static int daemonStartup(IPlatform*, int, const char**) { - return restartableMain(); + return realMain(NULL); } int @@ -647,11 +648,11 @@ main(int argc, char** argv) result = platform.daemonize(DAEMON_NAME, &daemonStartup); if (result == -1) { log((CLOG_CRIT "failed to daemonize")); - return 16; + return kExitFailed; } } else { - result = restartableMain(); + result = realMain(NULL); } CNetwork::cleanup(); diff --git a/net/CTCPSocket.cpp b/net/CTCPSocket.cpp index c6e1970e..35a51244 100644 --- a/net/CTCPSocket.cpp +++ b/net/CTCPSocket.cpp @@ -115,8 +115,9 @@ CTCPSocket::connect(const CNetworkAddress& addr) addr.getAddressLength()) == CNetwork::Error) { // check for failure if (CNetwork::getsockerror() != CNetwork::kECONNECTING) { + XSocketConnect e; CNetwork::setblocking(m_fd, true); - throw XSocketConnect(); + throw e; } // wait for connection or failure @@ -130,8 +131,12 @@ CTCPSocket::connect(const CNetworkAddress& addr) if ((pfds[0].revents & (CNetwork::kPOLLERR | CNetwork::kPOLLNVAL)) != 0) { // connection failed + int error = 0; + CNetwork::AddressLength size = sizeof(error); CNetwork::setblocking(m_fd, true); - throw XSocketConnect(); + CNetwork::getsockopt(m_fd, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &size); + throw XSocketConnect(error); } if ((pfds[0].revents & CNetwork::kPOLLOUT) != 0) { int error; @@ -142,7 +147,7 @@ CTCPSocket::connect(const CNetworkAddress& addr) error != 0) { // connection failed CNetwork::setblocking(m_fd, true); - throw XSocketConnect(); + throw XSocketConnect(error); } // connected! diff --git a/net/XSocket.h b/net/XSocket.h index 4b0fbf7c..354e57af 100644 --- a/net/XSocket.h +++ b/net/XSocket.h @@ -36,20 +36,36 @@ public: }; class XSocketBind : public XSocketErrno { +public: + XSocketBind() { } + XSocketBind(int e) : XSocketErrno(e) { } + protected: // XBase overrides virtual CString getWhat() const throw(); }; -class XSocketAddressInUse : public XSocketBind { }; +class XSocketAddressInUse : public XSocketBind { +public: + XSocketAddressInUse() { } + XSocketAddressInUse(int e) : XSocketBind(e) { } +}; class XSocketConnect : public XSocketErrno { +public: + XSocketConnect() { } + XSocketConnect(int e) : XSocketErrno(e) { } + protected: // XBase overrides virtual CString getWhat() const throw(); }; class XSocketCreate : public XSocketErrno { +public: + XSocketCreate() { } + XSocketCreate(int e) : XSocketErrno(e) { } + protected: // XBase overrides virtual CString getWhat() const throw(); diff --git a/platform/CUnixPlatform.cpp b/platform/CUnixPlatform.cpp index c1210458..9e3c10e2 100644 --- a/platform/CUnixPlatform.cpp +++ b/platform/CUnixPlatform.cpp @@ -7,22 +7,6 @@ #include #include #include -#include -#if HAVE_SYS_WAIT_H -# include -#endif -#if !defined(WIFSIGNALED) -# define WIFSIGNALED(w) (((w) & 0xff) != 0x7f && ((w) & 0xff) != 0) -#endif -#if !defined(WIFEXITED) -# define WIFEXITED(w) (((w) & 0xff) == 0) -#endif -#if !defined(WTERMSIG) -# define WTERMSIG(w) ((w) & 0x7f) -#endif -#if !defined(WEXITSTATUS) -# define WEXITSTATUS(w) (((w) >> 8) & 0xff) -#endif // @@ -106,64 +90,6 @@ CUnixPlatform::installDaemonLogger(const char* name) CLog::setOutputter(&CUnixPlatform::deamonLogger); } -int -CUnixPlatform::restart(RestartFunc func, int minErrorCode) -{ - // rely on child to catch these signals - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - pthread_sigmask(SIG_BLOCK, &sigset, NULL); - - for (;;) { - switch (fork()) { - default: - { - // parent process. wait for child to exit. - int status; - if (wait(&status) == -1) { - // wait failed. this is unexpected so bail. - log((CLOG_CRIT "wait() failed")); - return minErrorCode; - } - - // what happened? if the child exited normally with a - // status less than 16 then the child was deliberately - // terminated so we also terminate. - if (WIFEXITED(status) && WEXITSTATUS(status) < minErrorCode) { - return WEXITSTATUS(status); - } - - // did child die horribly? - if (WIFSIGNALED(status)) { - switch (WTERMSIG(status)) { - case SIGHUP: - case SIGINT: - case SIGQUIT: - case SIGTERM: - break; - - default: - // uh oh. bail out. - return 16; - } - } - } - break; - - case -1: - // fork() failed. log the error and proceed as a child - log((CLOG_WARN "fork() failed; cannot automatically restart on error")); - // fall through - - case 0: - // child process - return func(); - } - } -} - const char* CUnixPlatform::getBasename(const char* pathname) const { diff --git a/platform/CUnixPlatform.h b/platform/CUnixPlatform.h index 9b5592b4..8fbe56c9 100644 --- a/platform/CUnixPlatform.h +++ b/platform/CUnixPlatform.h @@ -16,7 +16,6 @@ public: virtual EResult uninstallDaemon(const char* name); virtual int daemonize(const char* name, DaemonFunc); virtual void installDaemonLogger(const char* name); - virtual int restart(RestartFunc, int minErrorCode); virtual const char* getBasename(const char* pathname) const; virtual CString getUserDirectory() const; virtual CString getSystemDirectory() const; diff --git a/platform/CWin32Platform.cpp b/platform/CWin32Platform.cpp index 440ddccc..3553c38a 100644 --- a/platform/CWin32Platform.cpp +++ b/platform/CWin32Platform.cpp @@ -319,15 +319,6 @@ CWin32Platform::installDaemonLogger(const char* name) } } -int -CWin32Platform::restart(RestartFunc func, int /*minErrorCode*/) -{ - // FIXME -- start in separate process or thread. note that this - // isn't too critical as win32 doesn't force us to terminate for - // any reason so we should never have to restart. - return func(); -} - const char* CWin32Platform::getBasename(const char* pathname) const { diff --git a/platform/CWin32Platform.h b/platform/CWin32Platform.h index 45f99e29..0fed1011 100644 --- a/platform/CWin32Platform.h +++ b/platform/CWin32Platform.h @@ -49,7 +49,6 @@ public: virtual EResult uninstallDaemon(const char* name); virtual int daemonize(const char* name, DaemonFunc); virtual void installDaemonLogger(const char* name); - virtual int restart(RestartFunc, int minErrorCode); virtual const char* getBasename(const char* pathname) const; virtual CString getUserDirectory() const; virtual CString getSystemDirectory() const; diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index 5ae7f31e..ae1936f5 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -93,6 +93,11 @@ CXWindowsScreen::CXWindowsScreen(IScreenReceiver* receiver, assert(m_eventHandler != NULL); s_screen = this; + + // no clipboards to start with + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = NULL; + } } CXWindowsScreen::~CXWindowsScreen() @@ -670,7 +675,10 @@ CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : m_mutex(&screen->m_mutex), m_display(screen->m_display) { - assert(m_display != NULL); + // note -- it's permitted for m_display to be NULL. that might + // happen if we couldn't connect to the display or if the + // display unexpectedly disconnected. the caller is expected + // to check for NULL as necessary. m_mutex->lock(); } diff --git a/platform/CXWindowsUtil.cpp b/platform/CXWindowsUtil.cpp index 253a3362..f65fe020 100644 --- a/platform/CXWindowsUtil.cpp +++ b/platform/CXWindowsUtil.cpp @@ -206,7 +206,9 @@ CXWindowsUtil::CErrorLock::CErrorLock(Display* display, CXWindowsUtil::CErrorLock::~CErrorLock() { // make sure everything finishes before uninstalling handler - XSync(m_display, False); + if (m_display != NULL) { + XSync(m_display, False); + } // restore old handler XSetErrorHandler(m_oldXHandler); @@ -217,7 +219,9 @@ void CXWindowsUtil::CErrorLock::install(ErrorHandler handler, void* data) { // make sure everything finishes before installing handler - XSync(m_display, False); + if (m_display != NULL) { + XSync(m_display, False); + } // install handler m_handler = handler; diff --git a/platform/IPlatform.h b/platform/IPlatform.h index 0a7e4cee..60056575 100644 --- a/platform/IPlatform.h +++ b/platform/IPlatform.h @@ -7,7 +7,6 @@ class IPlatform : public IInterface { public: typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv); - typedef int (*RestartFunc)(); enum EResult { kSuccess, @@ -53,11 +52,6 @@ public: // in the log to identify this process. virtual void installDaemonLogger(const char* name) = 0; - // continually restart the given function in a separate process - // or thread until it exits normally with a code less than the - // given code then return the code. - virtual int restart(RestartFunc, int minErrorCode) = 0; - // accessors // find the basename in the given pathname diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index 56bd3464..236fd57c 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -34,6 +34,7 @@ CPrimaryClient::CPrimaryClient(IServer* server, CPrimaryClient::~CPrimaryClient() { + log((CLOG_DEBUG1 "destroying primary screen")); delete m_screen; } diff --git a/server/CServer.cpp b/server/CServer.cpp index 89ff7150..9c4f3467 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -59,13 +59,11 @@ CServer::open() } catch (XScreenOpenFailure&) { // can't open screen yet. wait a few seconds to retry. - CThread::sleep(3.0); log((CLOG_INFO "failed to open screen")); return false; } catch (XUnknownClient& e) { // can't open screen yet. wait a few seconds to retry. - CThread::sleep(3.0); log((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); return false; } @@ -1135,7 +1133,9 @@ CServer::acceptClients(void*) listen->bind(m_config.getSynergyAddress()); break; } - catch (XSocketAddressInUse&) { + catch (XSocketBind& e) { + log((CLOG_DEBUG1 "bind failed: %s", e.getErrstr())); + // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { log((CLOG_DEBUG1 "waited too long to bind, giving up")); @@ -1143,7 +1143,6 @@ CServer::acceptClients(void*) } // wait a bit before retrying - log((CLOG_DEBUG1 "bind failed; waiting to retry")); CThread::sleep(5.0); } } @@ -1419,7 +1418,9 @@ CServer::acceptHTTPClients(void*) listen->bind(m_config.getHTTPAddress()); break; } - catch (XSocketAddressInUse&) { + catch (XSocketBind& e) { + log((CLOG_DEBUG1 "bind HTTP failed: %s", e.getErrstr())); + // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { log((CLOG_DEBUG1 "waited too long to bind HTTP, giving up")); @@ -1427,7 +1428,6 @@ CServer::acceptHTTPClients(void*) } // wait a bit before retrying - log((CLOG_DEBUG1 "bind HTTP failed; waiting to retry")); CThread::sleep(5.0); } } diff --git a/server/server.cpp b/server/server.cpp index 9fcaee93..14e1f696 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -77,114 +77,106 @@ static int realMain(CMutex* mutex) { - // s_serverLock should have mutex locked on entry + // caller should have mutex locked on entry - try { - // initialize threading library - CThread::init(); - - // make logging thread safe - CMutex logMutex; - s_logMutex = &logMutex; - CLog::setLock(&logLock); - - bool locked = true; + int result = kExitSuccess; + do { try { - // if configuration has no screens then add this system - // as the default - if (s_config.begin() == s_config.end()) { - s_config.addScreen(s_name); - } + // initialize threading library + CThread::init(); - // set the contact address, if provided, in the config. - // otherwise, if the config doesn't have an address, use - // the default. - if (s_synergyAddress.isValid()) { - s_config.setSynergyAddress(s_synergyAddress); - } - else if (!s_config.getSynergyAddress().isValid()) { - s_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); - } + // make logging thread safe + CMutex logMutex; + s_logMutex = &logMutex; + CLog::setLock(&logLock); - // set HTTP address is provided - if (s_httpAddress.isValid()) { - s_config.setHTTPAddress(s_httpAddress); - } + bool opened = false; + bool locked = true; + try { + // if configuration has no screens then add this system + // as the default + if (s_config.begin() == s_config.end()) { + s_config.addScreen(s_name); + } - // create server - s_server = new CServer(s_name); - s_server->setConfig(s_config); - if (!s_server->open()) { + // set the contact address, if provided, in the config. + // otherwise, if the config doesn't have an address, use + // the default. + if (s_synergyAddress.isValid()) { + s_config.setSynergyAddress(s_synergyAddress); + } + else if (!s_config.getSynergyAddress().isValid()) { + s_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); + } + + // set HTTP address if provided + if (s_httpAddress.isValid()) { + s_config.setHTTPAddress(s_httpAddress); + } + + // create server + s_server = new CServer(s_name); + s_server->setConfig(s_config); + if (s_server->open()) { + opened = true; + + // run server (unlocked) + if (mutex != NULL) { + mutex->unlock(); + } + locked = false; + s_server->run(); + locked = true; + if (mutex != NULL) { + mutex->lock(); + } + + // clean up + s_server->close(); + } + else { + // wait a few seconds before retrying + if (s_restartable) { + CThread::sleep(3.0); + } + else { + result = kExitFailed; + } + } + + // clean up delete s_server; s_server = NULL; - return 16; + CLog::setLock(NULL); + s_logMutex = NULL; } - - // run server (unlocked) - if (mutex != NULL) { - mutex->unlock(); + catch (...) { + // clean up + if (!locked && mutex != NULL) { + mutex->lock(); + } + if (s_server != NULL) { + if (opened) { + s_server->close(); + } + delete s_server; + s_server = NULL; + } + CLog::setLock(NULL); + s_logMutex = NULL; + throw; } - locked = false; - s_server->run(); - locked = true; - if (mutex != NULL) { - mutex->lock(); - } - - // clean up - s_server->close(); - delete s_server; - s_server = NULL; - CLog::setLock(NULL); - s_logMutex = NULL; } - catch (...) { - // clean up - if (!locked && mutex != NULL) { - mutex->lock(); - } - if (s_server != NULL) { - s_server->close(); - delete s_server; - s_server = NULL; - } - CLog::setLock(NULL); - s_logMutex = NULL; - throw; + catch (XBase& e) { + log((CLOG_CRIT "failed: %s", e.what())); } - } - catch (XBase& e) { - log((CLOG_CRIT "failed: %s", e.what())); - return 16; - } - catch (XThread&) { - // terminated - return 1; - } + catch (XThread&) { + // terminated + return kExitTerminated; + } + } while (s_restartable); - return 0; -} - -static -int -restartMain() -{ - return realMain(NULL); -} - -// invoke realMain and wait for it. if s_restartable then keep -// restarting realMain until it returns a terminate code. -static -int -restartableMain() -{ - if (s_restartable) { - CPlatform platform; - return platform.restart(restartMain, 16); - } - else { - return realMain(NULL); - } + return result; } @@ -308,7 +300,7 @@ isArg(int argi, int argc, const char** argv, if (argi + minRequiredParameters >= argc) { log((CLOG_PRINT "%s: missing arguments for `%s'" BYE, pname, argv[argi], pname)); - bye(2); + bye(kExitArgs); } return true; } @@ -347,7 +339,7 @@ parse(int argc, const char** argv) catch (XSocketAddress&) { log((CLOG_PRINT "%s: invalid address for `%s'" BYE, pname, argv[i], pname)); - bye(2); + bye(kExitArgs); } ++i; } @@ -360,7 +352,7 @@ parse(int argc, const char** argv) catch (XSocketAddress&) { log((CLOG_PRINT "%s: invalid address for `%s'" BYE, pname, argv[i], pname)); - bye(2); + bye(kExitArgs); } ++i; } @@ -397,12 +389,12 @@ parse(int argc, const char** argv) else if (isArg(i, argc, argv, "-h", "--help")) { help(); - bye(0); + bye(kExitSuccess); } else if (isArg(i, argc, argv, NULL, "--version")) { version(); - bye(0); + bye(kExitSuccess); } #if WINDOWS_LIKE @@ -412,7 +404,7 @@ parse(int argc, const char** argv) log((CLOG_PRINT "%s: `--install' and `--uninstall'" " are mutually exclusive" BYE, pname, argv[i], pname)); - bye(2); + bye(kExitArgs); } } #endif @@ -424,7 +416,7 @@ parse(int argc, const char** argv) log((CLOG_PRINT "%s: `--install' and `--uninstall'" " are mutually exclusive" BYE, pname, argv[i], pname)); - bye(2); + bye(kExitArgs); } } #endif @@ -438,7 +430,7 @@ parse(int argc, const char** argv) else if (argv[i][0] == '-') { log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, pname, argv[i], pname)); - bye(2); + bye(kExitArgs); } else { @@ -451,7 +443,7 @@ parse(int argc, const char** argv) if (i != argc) { log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, pname, argv[i], pname)); - bye(2); + bye(kExitArgs); } // increase default filter level for daemon. the user must @@ -474,7 +466,7 @@ parse(int argc, const char** argv) if (!CLog::setFilter(s_logFilter)) { log((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, pname, s_logFilter, pname)); - bye(2); + bye(kExitArgs); } } @@ -499,7 +491,7 @@ loadConfig(const char* pathname, bool require) if (require) { log((CLOG_PRINT "%s: cannot read configuration '%s': %s", pname, pathname, e.what())); - bye(3); + bye(kExitConfig); } else { log((CLOG_DEBUG "cannot read configuration \"%s\": %s", @@ -595,7 +587,7 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) parse(argc, argv); if (s_install || s_uninstall) { // not allowed to install/uninstall from service - throw CWin32Platform::CDaemonFailed(1); + throw CWin32Platform::CDaemonFailed(kExitArgs); } // load configuration @@ -609,7 +601,7 @@ static int daemonStartup95(IPlatform*, int, const char**) { - return restartableMain(); + return realMain(NULL); } int WINAPI @@ -639,7 +631,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) int result = platform.daemonize(DAEMON_NAME, &daemonStartup); if (result == -1) { log((CLOG_CRIT "failed to start as a service" BYE, pname)); - return 16; + return kExitFailed; } return result; } @@ -654,7 +646,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) if (GetModuleFileName(NULL, path, sizeof(path) / sizeof(path[0])) == 0) { log((CLOG_CRIT "cannot determine absolute path to program")); - return 16; + return kExitFailed; } // construct the command line to start the service with @@ -681,24 +673,24 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) "Shares this system's mouse and keyboard with others.", path, commandLine.c_str())) { log((CLOG_CRIT "failed to install service")); - return 16; + return kExitFailed; } log((CLOG_PRINT "installed successfully")); - return 0; + return kExitSuccess; } else if (s_uninstall) { switch (platform.uninstallDaemon(DAEMON_NAME)) { case IPlatform::kSuccess: log((CLOG_PRINT "uninstalled successfully")); - return 0; + return kExitSuccess; case IPlatform::kFailed: log((CLOG_CRIT "failed to uninstall service")); - return 16; + return kExitFailed; case IPlatform::kAlready: log((CLOG_CRIT "service isn't installed")); - return 16; + return kExitFailed; } } @@ -716,18 +708,18 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) result = platform.daemonize(DAEMON_NAME, &daemonStartup95); if (result == -1) { log((CLOG_CRIT "failed to start as a service" BYE, pname)); - return 16; + return kExitFailed; } } else { // cannot start a service from the command line so just // run normally (except with log messages redirected). - result = restartableMain(); + result = realMain(NULL); } } else { // run - result = restartableMain(); + result = realMain(NULL); } CNetwork::cleanup(); @@ -741,7 +733,7 @@ static int daemonStartup(IPlatform*, int, const char**) { - return restartableMain(); + return realMain(NULL); } int @@ -767,11 +759,11 @@ main(int argc, char** argv) result = platform.daemonize(DAEMON_NAME, &daemonStartup); if (result == -1) { log((CLOG_CRIT "failed to daemonize")); - return 16; + return kExitFailed; } } else { - result = restartableMain(); + result = realMain(NULL); } CNetwork::cleanup(); diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index 391c8ea3..c4d48ae4 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -5,7 +5,7 @@ // version number static const SInt16 kProtocolMajorVersion = 0; -static const SInt16 kProtocolMinorVersion = 1; +static const SInt16 kProtocolMinorVersion = 7; // contact port number static const UInt16 kDefaultPort = 24800; @@ -87,7 +87,7 @@ static const char kMsgCScreenSaver[] = "CSEC%1i"; // sent by primary in response to a secondary screen's kMsgDInfo. // this is sent for every kMsgDInfo, whether or not the primary // had sent a kMsgQInfo. -static const char kMsgCInfoAck[] = "CIAK"; +static const char kMsgCInfoAck[] = "CIAK"; // @@ -165,16 +165,16 @@ static const char kMsgQInfo[] = "QINF"; static const char kMsgEIncompatible[] = "EICV%2i%2i"; // name provided when connecting is already in use: primary -> secondary -static const char kMsgEBusy[] = "EBSY"; +static const char kMsgEBusy[] = "EBSY"; // unknown client: primary -> secondary // name provided when connecting is not in primary's screen // configuration map. -static const char kMsgEUnknown[] = "EUNK"; +static const char kMsgEUnknown[] = "EUNK"; // protocol violation: primary -> secondary // primary should disconnect after sending this message. -static const char kMsgEBad[] = "EBAD"; +static const char kMsgEBad[] = "EBAD"; // diff --git a/synergy/Version.h b/synergy/Version.h index 24367162..5b8ae1c7 100644 --- a/synergy/Version.h +++ b/synergy/Version.h @@ -4,10 +4,19 @@ #include "BasicTypes.h" static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; +static const char* kContact = "Chris Schoeneman crs23@bigfoot.com"; -// build version +// build version. follows linux kernel style: minor number even implies +// a release version, odd implies development version. static const SInt16 kMajorVersion = 0; -static const SInt16 kMinorVersion = 5; -static const SInt16 kReleaseVersion = 0; +static const SInt16 kMinorVersion = 9; +static const SInt16 kReleaseVersion = 7; + +// exit codes +static const int kExitSuccess = 0; // successful completion +static const int kExitFailed = 1; // general failure +static const int kExitTerminated = 2; // killed by signal +static const int kExitArgs = 3; // bad arguments +static const int kExitConfig = 4; // cannot read configuration #endif From 6fc6805a06370077a52897ef7aa52b956e558253 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 24 Jul 2002 17:07:52 +0000 Subject: [PATCH 261/807] some fixes for motif clipboard. still not handling incremental transfer through root window property because not sure of the protocol. --- platform/CXWindowsClipboard.cpp | 113 ++++++++++++++++---------------- platform/CXWindowsClipboard.h | 42 ++++++------ 2 files changed, 81 insertions(+), 74 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 8ba736b0..1168247a 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -29,6 +29,7 @@ CXWindowsClipboard::CXWindowsClipboard(Display* display, m_atomTargets = XInternAtom(m_display, "TARGETS", False); m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False); m_atomTimestamp = XInternAtom(m_display, "TIMESTAMP", False); + m_atomInteger = XInternAtom(m_display, "INTEGER", False); m_atomAtomPair = XInternAtom(m_display, "ATOM_PAIR", False); m_atomData = XInternAtom(m_display, "CLIP_TEMPORARY", False); m_atomINCR = XInternAtom(m_display, "INCR", False); @@ -512,11 +513,7 @@ CXWindowsClipboard::icccmFillCache() Atom actualTarget; CString targetData; if (!icccmGetSelection(target, &actualTarget, &targetData)) { - log((CLOG_DEBUG1 " no data for target", target)); - continue; - } - if (actualTarget != target) { - log((CLOG_DEBUG1 " expeted (%d) and actual (%d) targets differ", target, actualTarget)); + log((CLOG_DEBUG1 " no data for target %d", target)); continue; } @@ -524,7 +521,7 @@ CXWindowsClipboard::icccmFillCache() IClipboard::EFormat format = converter->getFormat(); m_data[format] = converter->toIClipboard(targetData); m_added[format] = true; - log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); + log((CLOG_DEBUG " added format %d for target %d", format, target)); } } @@ -556,7 +553,7 @@ CXWindowsClipboard::icccmGetTime() const Atom actualTarget; CString data; if (icccmGetSelection(m_atomTimestamp, &actualTarget, &data) && - actualTarget == m_atomTimestamp) { + actualTarget == m_atomInteger) { Time time = *reinterpret_cast(data.data()); log((CLOG_DEBUG1 "got ICCCM time %d", time)); return time; @@ -626,15 +623,12 @@ CXWindowsClipboard::motifOwnsClipboard() const Atom target; SInt32 format; CString data; - Window root = RootWindow(m_display, DefaultScreen(m_display)); + Window root = RootWindow(m_display, DefaultScreen(m_display)); if (!CXWindowsUtil::getWindowProperty(m_display, root, m_atomMotifClipHeader, &data, &target, &format, False)) { return false; } - if (target != m_atomMotifClipHeader) { - return false; - } // check the owner window against the current clipboard owner const CMotifClipHeader* header = @@ -664,9 +658,6 @@ CXWindowsClipboard::motifFillCache() &data, &target, &format, False)) { return; } - if (target != m_atomMotifClipHeader) { - return; - } // check that the header is okay const CMotifClipHeader* header = @@ -687,24 +678,27 @@ CXWindowsClipboard::motifFillCache() &target, &format, False)) { return; } - if (target != atomItem) { - return; - } // check that the item is okay const CMotifClipItem* item = reinterpret_cast(data.data()); if (data.size() < sizeof(CMotifClipItem) || item->m_id != kMotifClipItem || - item->m_numFormats < 1) { + item->m_numFormats - item->m_numDeletedFormats < 1) { return; } + // format list is after static item structure elements + const SInt32 numFormats = item->m_numFormats - item->m_numDeletedFormats; + const SInt32* formats = reinterpret_cast(item->m_size + + reinterpret_cast(data.data())); + // get the available formats - std::vector motifFormats; - for (SInt32 i = 0; i < item->m_numFormats; ++i) { + typedef std::map CMotifFormatMap; + CMotifFormatMap motifFormats; + for (SInt32 i = 0; i < numFormats; ++i) { // get Motif format property from the root window - sprintf(name, "_MOTIF_CLIP_ITEM_%d", item->m_formats[i]); + sprintf(name, "_MOTIF_CLIP_ITEM_%d", formats[i]); Atom atomFormat = XInternAtom(m_display, name, False); CString data; if (!CXWindowsUtil::getWindowProperty(m_display, root, @@ -712,9 +706,6 @@ CXWindowsClipboard::motifFillCache() &target, &format, False)) { continue; } - if (target != atomFormat) { - continue; - } // check that the format is okay const CMotifClipFormat* motifFormat = @@ -722,21 +713,18 @@ CXWindowsClipboard::motifFillCache() if (data.size() < sizeof(CMotifClipFormat) || motifFormat->m_id != kMotifClipFormat || motifFormat->m_length < 0 || - motifFormat->m_type == None) { + motifFormat->m_type == None || + motifFormat->m_deleted != 0) { continue; } // save it - motifFormats.push_back(*motifFormat); + motifFormats.insert(std::make_pair(motifFormat->m_type, data)); } const UInt32 numMotifFormats = motifFormats.size(); - - // try each converter in order (because they're in order of // preference). - const Atom* targets = reinterpret_cast(data.data()); - const UInt32 numTargets = data.size() / sizeof(Atom); for (ConverterList::const_iterator index = m_converters.begin(); index != m_converters.end(); ++index) { IXWindowsClipboardConverter* converter = *index; @@ -747,48 +735,63 @@ CXWindowsClipboard::motifFillCache() } // see if atom is in target list - UInt32 i; - Atom target = None; - for (i = 0; i < numMotifFormats; ++i) { - if (converter->getAtom() == motifFormats[i].m_type) { - target = motifFormats[i].m_type; - break; - } - } - if (target == None) { + CMotifFormatMap::const_iterator index = + motifFormats.find(converter->getAtom()); + if (index == motifFormats.end()) { continue; } + // get format + const CMotifClipFormat* motifFormat = + reinterpret_cast( + index->second.data()); + const Atom target = motifFormat->m_type; + // get the data (finally) - SInt32 length = motifFormats[i].m_length; - sprintf(name, "_MOTIF_CLIP_ITEM_%d", motifFormats[i].m_data); - Atom atomData = XInternAtom(m_display, name, False), atomTarget; + Atom actualTarget; CString targetData; - if (!CXWindowsUtil::getWindowProperty(m_display, root, - atomData, &targetData, - &atomTarget, &format, False)) { - log((CLOG_DEBUG1 " no data for target", target)); + if (!motifGetSelection(motifFormat, &actualTarget, &targetData)) { + log((CLOG_DEBUG1 " no data for target %d", target)); continue; } - if (atomTarget != atomData) { - continue; - } - - // truncate data to length specified in the format - targetData.erase(length); // add to clipboard and note we've done it IClipboard::EFormat format = converter->getFormat(); m_data[format] = converter->toIClipboard(targetData); m_added[format] = true; - log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); + log((CLOG_DEBUG " added format %d for target %d", format, target)); } } +bool +CXWindowsClipboard::motifGetSelection(const CMotifClipFormat* format, + Atom* actualTarget, CString* data) const +{ + // if the current clipboard owner and the owner indicated by the + // motif clip header are the same then transfer via a property on + // the root window, otherwise transfer as a normal ICCCM client. + if (!motifOwnsClipboard()) { + return icccmGetSelection(format->m_type, actualTarget, data); + } + + // use motif way + // FIXME -- this isn't right. it'll only work if the data is + // already stored on the root window and only if it fits in a + // property. motif has some scheme for transferring part by + // part that i don't know. + char name[18 + 20]; + sprintf(name, "_MOTIF_CLIP_ITEM_%d", format->m_data); + Atom target = XInternAtom(m_display, name, False); + Window root = RootWindow(m_display, DefaultScreen(m_display)); + return CXWindowsUtil::getWindowProperty(m_display, root, + target, data, + actualTarget, NULL, False); +} + IClipboard::Time CXWindowsClipboard::motifGetTime() const { - // FIXME -- does Motif report this? + // FIXME -- where does Motif report this? return 0; } @@ -1165,7 +1168,7 @@ CXWindowsClipboard::getTimestampData(CString& data, int* format) const checkCache(); data.append(reinterpret_cast(&m_timeOwned), 4); *format = 32; - return m_atomTimestamp; + return m_atomInteger; } diff --git a/platform/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h index cbd2898e..7c1a7d6e 100644 --- a/platform/CXWindowsClipboard.h +++ b/platform/CXWindowsClipboard.h @@ -89,20 +89,6 @@ private: void fillCache() const; void doFillCache(); - // ICCCM interoperability methods - void icccmFillCache(); - bool icccmGetSelection(Atom target, - Atom* actualTarget, - CString* data) const; - Time icccmGetTime() const; - - // motif interoperability methods - bool motifLockClipboard() const; - void motifUnlockClipboard() const; - bool motifOwnsClipboard() const; - Time motifGetTime() const; - void motifFillCache(); - // // helper classes // @@ -160,17 +146,17 @@ private: SInt32 m_pad3[3]; Window m_selectionOwner; SInt32 m_pad4[2]; - SInt32 m_items[1]; // m_numItems items }; // Motif clip item structure class CMotifClipItem { public: SInt32 m_id; // kMotifClipItem - SInt32 m_pad1[6]; + SInt32 m_pad1[5]; + SInt32 m_size; SInt32 m_numFormats; - SInt32 m_pad2[7]; - SInt32 m_formats[1]; // m_numFormats formats + SInt32 m_numDeletedFormats; + SInt32 m_pad2[6]; }; // Motif clip format structure @@ -181,7 +167,9 @@ private: SInt32 m_length; SInt32 m_data; Atom m_type; - SInt32 m_pad2[6]; + SInt32 m_pad2[1]; + int m_deleted; + SInt32 m_pad3[4]; }; // stores data needed to respond to a selection request @@ -216,6 +204,21 @@ private: typedef std::map CReplyMap; typedef std::map CReplyEventMask; + // ICCCM interoperability methods + void icccmFillCache(); + bool icccmGetSelection(Atom target, + Atom* actualTarget, CString* data) const; + Time icccmGetTime() const; + + // motif interoperability methods + bool motifLockClipboard() const; + void motifUnlockClipboard() const; + bool motifOwnsClipboard() const; + void motifFillCache(); + bool motifGetSelection(const CMotifClipFormat*, + Atom* actualTarget, CString* data) const; + Time motifGetTime() const; + // reply methods bool insertMultipleReply(Window, ::Time, Atom); void insertReply(CReply*); @@ -267,6 +270,7 @@ private: Atom m_atomTargets; Atom m_atomMultiple; Atom m_atomTimestamp; + Atom m_atomInteger; Atom m_atomAtomPair; Atom m_atomData; Atom m_atomINCR; From 5fe7763d376b9d043245c5b38393c4d78cb3a1ec Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 24 Jul 2002 17:22:01 +0000 Subject: [PATCH 262/807] made handling of nul terminators in CUnicode more sane. --- base/CUnicode.cpp | 67 +++++++------------ base/CUnicode.h | 15 ++--- platform/CMSWindowsClipboardTextConverter.cpp | 10 ++- .../CMSWindowsClipboardUTF16Converter.cpp | 10 ++- 4 files changed, 45 insertions(+), 57 deletions(-) diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp index 183499cf..066f8e5f 100644 --- a/base/CUnicode.cpp +++ b/base/CUnicode.cpp @@ -82,9 +82,8 @@ CUnicode::UTF8ToUCS2(const CString& src, bool* errors) // default to success resetError(errors); - // get size of input string and reserve some space in output. - // include UTF8's nul terminator. - UInt32 n = src.size() + 1; + // get size of input string and reserve some space in output + UInt32 n = src.size(); CString dst; dst.reserve(2 * n); @@ -112,9 +111,8 @@ CUnicode::UTF8ToUCS4(const CString& src, bool* errors) // default to success resetError(errors); - // get size of input string and reserve some space in output. - // include UTF8's nul terminator. - UInt32 n = src.size() + 1; + // get size of input string and reserve some space in output + UInt32 n = src.size(); CString dst; dst.reserve(4 * n); @@ -137,9 +135,8 @@ CUnicode::UTF8ToUTF16(const CString& src, bool* errors) // default to success resetError(errors); - // get size of input string and reserve some space in output. - // include UTF8's nul terminator. - UInt32 n = src.size() + 1; + // get size of input string and reserve some space in output + UInt32 n = src.size(); CString dst; dst.reserve(2 * n); @@ -176,9 +173,8 @@ CUnicode::UTF8ToUTF32(const CString& src, bool* errors) // default to success resetError(errors); - // get size of input string and reserve some space in output. - // include UTF8's nul terminator. - UInt32 n = src.size() + 1; + // get size of input string and reserve some space in output + UInt32 n = src.size(); CString dst; dst.reserve(4 * n); @@ -211,12 +207,13 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) // get length of multibyte string char mbc[MB_LEN_MAX]; + size_t mblen; mbstate_t state; memset(&state, 0, sizeof(state)); size_t len = 0; UInt32 n = size; for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { - size_t mblen = wcrtomb(mbc, *scan, &state); + mblen = wcrtomb(mbc, *scan, &state); if (mblen == -1) { // unconvertable character setError(errors); @@ -227,21 +224,21 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) } } - // check if state is in initial state. if not then count the - // bytes for returning it to the initial state. - if (mbsinit(&state) == 0) { - len += wcrtomb(mbc, L'\0', &state) - 1; + // handle nul terminator + mblen = wcrtomb(mbc, L'\0', &state); + if (mblen != -1) { + len += mblen - 1; } assert(mbsinit(&state) != 0); // allocate multibyte string - char* mbs = new char[len + 1]; + char* mbs = new char[len]; // convert to multibyte char* dst = mbs; n = size; for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { - size_t mblen = wcrtomb(dst, *scan, &state); + mblen = wcrtomb(dst, *scan, &state); if (mblen == -1) { // unconvertable character *dst++ = '?'; @@ -250,7 +247,11 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) dst += mblen; } } - *dst++ = '\0'; + mblen = wcrtomb(dst, L'\0', &state); + if (mblen != -1) { + // don't include nul terminator + dst += mblen - 1; + } CString text(mbs, dst - mbs); // clean up @@ -311,7 +312,7 @@ CUnicode::textToUTF8(const CString& src, bool* errors) resetError(errors); // get length of multibyte string - UInt32 n = src.size(); + UInt32 n = src.size() + 1; size_t len = 0; mbstate_t state; memset(&state, 0, sizeof(state)); @@ -399,9 +400,7 @@ CUnicode::textToUTF8(const CString& src, bool* errors) wchar_t* CUnicode::UTF8ToWideChar(const CString& src, UInt32& size, bool* errors) { - // convert to platform's wide character encoding. - // note -- this must include a wide nul character (independent of - // the CString's nul character). + // convert to platform's wide character encoding #if WINDOWS_LIKE CString tmp = UTF8ToUTF16(src, errors); size = tmp.size() >> 1; @@ -442,11 +441,6 @@ CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors) toUTF8(dst, c, errors); } - // remove extra trailing nul - if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { - dst.resize(dst.size() - 1); - } - return dst; } @@ -463,11 +457,6 @@ CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors) toUTF8(dst, c, errors); } - // remove extra trailing nul - if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { - dst.resize(dst.size() - 1); - } - return dst; } @@ -510,11 +499,6 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors) } } - // remove extra trailing nul - if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { - dst.resize(dst.size() - 1); - } - return dst; } @@ -535,11 +519,6 @@ CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors) toUTF8(dst, c, errors); } - // remove extra trailing nul - if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { - dst.resize(dst.size() - 1); - } - return dst; } diff --git a/base/CUnicode.h b/base/CUnicode.h index 89d67235..dc2f4df2 100644 --- a/base/CUnicode.h +++ b/base/CUnicode.h @@ -15,9 +15,7 @@ public: // not NULL then it gets true if any characters could not be // encoded in the target encoding and false otherwise. note // that decoding errors do not set errors to error. UTF8ToText() - // converts to the current locale's (multibyte) encoding. all of - // these methods include the nul terminator in the returned - // string (independent of the CString's own nul terminator). + // converts to the current locale's (multibyte) encoding. static CString UTF8ToUCS2(const CString&, bool* errors = NULL); static CString UTF8ToUCS4(const CString&, bool* errors = NULL); static CString UTF8ToUTF16(const CString&, bool* errors = NULL); @@ -27,9 +25,7 @@ public: // convert from some encoding to UTF-8. if errors is not NULL // then it gets true if any characters could not be decoded and // false otherwise. textToUTF8() converts from the current - // locale's (multibyte) encoding. all of these methods strip - // a terminating nul so the returned UTF-8 string uses the - // CString's own nul terminator for termination. + // locale's (multibyte) encoding. static CString UCS2ToUTF8(const CString&, bool* errors = NULL); static CString UCS4ToUTF8(const CString&, bool* errors = NULL); static CString UTF16ToUTF8(const CString&, bool* errors = NULL); @@ -37,9 +33,10 @@ public: static CString textToUTF8(const CString&, bool* errors = NULL); private: - // convert UTF8 to nul terminated wchar_t string (using whatever - // encoding is native to the platform). caller must delete[] - // the returned string. + // convert UTF8 to wchar_t string (using whatever encoding is native + // to the platform). caller must delete[] the returned string. the + // string is *not* nul terminated; the length (in characters) is + // returned in size. static wchar_t* UTF8ToWideChar(const CString&, UInt32& size, bool* errors); diff --git a/platform/CMSWindowsClipboardTextConverter.cpp b/platform/CMSWindowsClipboardTextConverter.cpp index 195d88a1..bde47fd4 100755 --- a/platform/CMSWindowsClipboardTextConverter.cpp +++ b/platform/CMSWindowsClipboardTextConverter.cpp @@ -24,11 +24,17 @@ CMSWindowsClipboardTextConverter::getWin32Format() const CString CMSWindowsClipboardTextConverter::doFromIClipboard(const CString& data) const { - return CUnicode::UTF8ToText(data); + // convert and add nul terminator + return CUnicode::UTF8ToText(data) += '\0'; } CString CMSWindowsClipboardTextConverter::doToIClipboard(const CString& data) const { - return CUnicode::textToUTF8(data); + // convert and strip nul terminator + CString dst = CUnicode::textToUTF8(data); + if (dst.size() > 0 && dst[size() - 1] == '\0') { + dst.erase(dst.size() - 1); + } + return dst; } diff --git a/platform/CMSWindowsClipboardUTF16Converter.cpp b/platform/CMSWindowsClipboardUTF16Converter.cpp index 3c9da906..ab314f0c 100755 --- a/platform/CMSWindowsClipboardUTF16Converter.cpp +++ b/platform/CMSWindowsClipboardUTF16Converter.cpp @@ -24,11 +24,17 @@ CMSWindowsClipboardUTF16Converter::getWin32Format() const CString CMSWindowsClipboardUTF16Converter::doFromIClipboard(const CString& data) const { - return CUnicode::UTF8ToUTF16(data); + // convert and add nul terminator + return CUnicode::UTF8ToUTF16(data).append(sizeof(wchar_t), 0); } CString CMSWindowsClipboardUTF16Converter::doToIClipboard(const CString& data) const { - return CUnicode::UTF16ToUTF8(data); + // convert and strip nul terminator + CString dst = CUnicode::UTF16ToUTF8(data); + if (dst.size() > 0 && dst[size() - 1] == '\0') { + dst.erase(dst.size() - 1); + } + return dst; } From 67051556bb6476885a3af624ce0de57149dc5739 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 24 Jul 2002 17:30:32 +0000 Subject: [PATCH 263/807] fixed type of TARGETS target. --- platform/CXWindowsClipboard.cpp | 6 ++++-- platform/CXWindowsClipboard.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 1168247a..df6a377f 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -30,6 +30,7 @@ CXWindowsClipboard::CXWindowsClipboard(Display* display, m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False); m_atomTimestamp = XInternAtom(m_display, "TIMESTAMP", False); m_atomInteger = XInternAtom(m_display, "INTEGER", False); + m_atomAtom = XInternAtom(m_display, "ATOM", False); m_atomAtomPair = XInternAtom(m_display, "ATOM_PAIR", False); m_atomData = XInternAtom(m_display, "CLIP_TEMPORARY", False); m_atomINCR = XInternAtom(m_display, "INCR", False); @@ -476,7 +477,8 @@ CXWindowsClipboard::icccmFillCache() const Atom atomTargets = m_atomTargets; Atom target; CString data; - if (!icccmGetSelection(atomTargets, &target, &data)) { + if (!icccmGetSelection(atomTargets, &target, &data) || + target != m_atomAtom) { log((CLOG_DEBUG1 "selection doesn't support TARGETS")); data = ""; @@ -1156,7 +1158,7 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const } *format = 32; - return m_atomTargets; + return m_atomAtom; } Atom diff --git a/platform/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h index 7c1a7d6e..f32ad0d0 100644 --- a/platform/CXWindowsClipboard.h +++ b/platform/CXWindowsClipboard.h @@ -271,6 +271,7 @@ private: Atom m_atomMultiple; Atom m_atomTimestamp; Atom m_atomInteger; + Atom m_atomAtom; Atom m_atomAtomPair; Atom m_atomData; Atom m_atomINCR; From 4c38178fb9f84a5a7fd12410ec78d6ebe6bed4f9 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 24 Jul 2002 17:39:52 +0000 Subject: [PATCH 264/807] fixed an off-by-one error in textToUTF8(). --- base/CUnicode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp index 066f8e5f..cc22f3c1 100644 --- a/base/CUnicode.cpp +++ b/base/CUnicode.cpp @@ -312,7 +312,7 @@ CUnicode::textToUTF8(const CString& src, bool* errors) resetError(errors); // get length of multibyte string - UInt32 n = src.size() + 1; + UInt32 n = src.size(); size_t len = 0; mbstate_t state; memset(&state, 0, sizeof(state)); From a150f64e9c5cc96cc5ff0644f0ab3255c30991ba Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 24 Jul 2002 19:23:46 +0000 Subject: [PATCH 265/807] fixed an off-by-one error in UTF8ToText(). --- base/CUnicode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp index cc22f3c1..1c17afdd 100644 --- a/base/CUnicode.cpp +++ b/base/CUnicode.cpp @@ -227,7 +227,7 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) // handle nul terminator mblen = wcrtomb(mbc, L'\0', &state); if (mblen != -1) { - len += mblen - 1; + len += mblen; } assert(mbsinit(&state) != 0); From 2e6e8e179a699f45ea549c8e1db520f55fc97826 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 24 Jul 2002 19:24:21 +0000 Subject: [PATCH 266/807] fixes for win32 clipboard due to CUnicode nul terminator changes. --- platform/CMSWindowsClipboardTextConverter.cpp | 2 +- platform/CMSWindowsClipboardUTF16Converter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/CMSWindowsClipboardTextConverter.cpp b/platform/CMSWindowsClipboardTextConverter.cpp index bde47fd4..da5847e4 100755 --- a/platform/CMSWindowsClipboardTextConverter.cpp +++ b/platform/CMSWindowsClipboardTextConverter.cpp @@ -33,7 +33,7 @@ CMSWindowsClipboardTextConverter::doToIClipboard(const CString& data) const { // convert and strip nul terminator CString dst = CUnicode::textToUTF8(data); - if (dst.size() > 0 && dst[size() - 1] == '\0') { + if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { dst.erase(dst.size() - 1); } return dst; diff --git a/platform/CMSWindowsClipboardUTF16Converter.cpp b/platform/CMSWindowsClipboardUTF16Converter.cpp index ab314f0c..3422b938 100755 --- a/platform/CMSWindowsClipboardUTF16Converter.cpp +++ b/platform/CMSWindowsClipboardUTF16Converter.cpp @@ -33,7 +33,7 @@ CMSWindowsClipboardUTF16Converter::doToIClipboard(const CString& data) const { // convert and strip nul terminator CString dst = CUnicode::UTF16ToUTF8(data); - if (dst.size() > 0 && dst[size() - 1] == '\0') { + if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { dst.erase(dst.size() - 1); } return dst; From 63b1d4397a87cdd0cfa2e18762b84b88c7a33b79 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 24 Jul 2002 19:26:18 +0000 Subject: [PATCH 267/807] fixes for win32 due to changes in how s_restartable is handled. the main change is that WM_QUIT now causes the thread to be cancelled instead of mainLoop() just returning. this also requires runDaemon() to call the run function in a new thread each time it calls it because it could can cancelled. --- platform/CMSWindowsScreen.cpp | 2 +- platform/CWin32Platform.cpp | 16 ++++++++++++++-- platform/CWin32Platform.h | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index b6e87f56..45041f72 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -194,7 +194,7 @@ CMSWindowsScreen::mainLoop() // handle quit message if (event.m_msg.message == WM_QUIT) { - break; + CThread::getCurrentThread().cancel(); } // dispatch message diff --git a/platform/CWin32Platform.cpp b/platform/CWin32Platform.cpp index 3553c38a..b30f0bc6 100644 --- a/platform/CWin32Platform.cpp +++ b/platform/CWin32Platform.cpp @@ -2,6 +2,7 @@ #include "CLock.h" #include "CThread.h" #include "CLog.h" +#include "TMethodJob.h" #include "stdvector.h" #include #include @@ -556,9 +557,13 @@ CWin32Platform::runDaemon(RunFunc run, StopFunc stop) // mark server as running setStatus(m_statusHandle, SERVICE_RUNNING); - // run callback + // run callback in another thread m_serviceRunning = true; - result = run(m_serviceMutex); + { + CThread thread(new TMethodJob(this, + &CWin32Platform::runDaemonThread, run)); + result = reinterpret_cast(thread.getResult()); + } m_serviceRunning = false; // notify handler that the server stopped. if handler @@ -620,6 +625,13 @@ CWin32Platform::runDaemon(RunFunc run, StopFunc stop) } } +void +CWin32Platform::runDaemonThread(void* vrun) +{ + RunFunc run = reinterpret_cast(vrun); + CThread::exit(reinterpret_cast(run(m_serviceMutex))); +} + void CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) { diff --git a/platform/CWin32Platform.h b/platform/CWin32Platform.h index 0fed1011..8d0985c7 100644 --- a/platform/CWin32Platform.h +++ b/platform/CWin32Platform.h @@ -71,6 +71,8 @@ private: void serviceMain(DWORD, LPTSTR*); static void WINAPI serviceMainEntry(DWORD, LPTSTR*); + void runDaemonThread(void*); + void serviceHandler(DWORD ctrl); static void WINAPI serviceHandlerEntry(DWORD ctrl); From d3ea39149a8066150c4fa975ae559e2704062103 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Jul 2002 08:57:46 +0000 Subject: [PATCH 268/807] checkpoint. working on INCR transfers. --- platform/CXWindowsClipboard.cpp | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index df6a377f..778c3d8f 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -955,15 +955,18 @@ CXWindowsClipboard::sendReply(CReply* reply) // send using INCR if already sending incrementally or if reply // is too large, otherwise just send it. - const UInt32 maxRequestSize = 4 * XMaxRequestSize(m_display); + const UInt32 maxRequestSize = 50000;//XXX 4 * XMaxRequestSize(m_display); const bool useINCR = (reply->m_data.size() > maxRequestSize); +log((CLOG_INFO "useINCR: %s", useINCR ? "true" : "false")); // send INCR reply if incremental and we haven't replied yet if (useINCR && !reply->m_replied) { UInt32 size = reply->m_data.size(); +log((CLOG_INFO "send first INCR, size=%d", size)); if (!CXWindowsUtil::setWindowProperty(m_display, reply->m_requestor, reply->m_property, &size, 4, m_atomINCR, 32)) { +log((CLOG_INFO "send first INCR failed")); failed = true; } } @@ -974,6 +977,8 @@ CXWindowsClipboard::sendReply(CReply* reply) UInt32 size = reply->m_data.size() - reply->m_ptr; if (size > maxRequestSize) size = maxRequestSize; +if (useINCR) +log((CLOG_INFO "more INCR, size=%d, ptr=%d", size, reply->m_ptr)); // send it if (!CXWindowsUtil::setWindowProperty(m_display, @@ -981,6 +986,8 @@ CXWindowsClipboard::sendReply(CReply* reply) reply->m_data.data() + reply->m_ptr, size, reply->m_type, reply->m_format)) { +if (useINCR) +log((CLOG_INFO "more INCR failed")); failed = true; } else { @@ -989,6 +996,8 @@ CXWindowsClipboard::sendReply(CReply* reply) // we've finished the reply if we just sent the zero // size incremental chunk or if we're not incremental. reply->m_done = (size == 0 || !useINCR); +if (useINCR) +log((CLOG_INFO "more INCR sent, done = %s", reply->m_done ? "true" : "false")); } } } @@ -1000,6 +1009,7 @@ CXWindowsClipboard::sendReply(CReply* reply) // the final zero-length property. // FIXME -- how do you gracefully cancel an incremental transfer? if (failed) { +log((CLOG_INFO "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); log((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); reply->m_done = true; if (reply->m_property != None) { @@ -1030,6 +1040,7 @@ CXWindowsClipboard::sendReply(CReply* reply) // send notification if we haven't yet if (!reply->m_replied) { +log((CLOG_INFO "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); log((CLOG_DEBUG1 "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); reply->m_replied = true; @@ -1241,6 +1252,7 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, std::vector events; CStopwatch timeout(true); static const double s_timeout = 0.25; // FIXME -- is this too short? + bool noWait = false; while (!m_done && !m_failed) { // fail if timeout has expired if (timeout.getTime() >= s_timeout) { @@ -1249,13 +1261,23 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, } // process events if any otherwise sleep - if (XPending(display) > 0) { - while (!m_done && !m_failed && XPending(display) > 0) { + if (noWait || XPending(display) > 0) { + while (!m_done && !m_failed && (noWait || XPending(display) > 0)) { XNextEvent(display, &xevent); if (!processEvent(display, &xevent)) { // not processed so save it events.push_back(xevent); } + else { + // reset timer since we've made some progress + timeout.reset(); + + // don't sleep anymore, just block waiting for events. + // we're assuming here that the clipboard owner will + // complete the protocol correctly. if we continue to + // sleep we'll get very bad performance. + noWait = true; + } } } else { @@ -1343,19 +1365,15 @@ CXWindowsClipboard::CICCCMGetClipboard::processEvent( // selection owner is busted. if the INCR property has no size // then the selection owner is busted. if (target == XInternAtom(display, "INCR", False)) { -log((CLOG_INFO " INCR")); // FIXME if (m_incr) { -log((CLOG_INFO " INCR repeat")); // FIXME m_failed = true; m_error = true; } else if (m_data->size() == oldSize) { -log((CLOG_INFO " INCR zero size")); // FIXME m_failed = true; m_error = true; } else { -log((CLOG_INFO " INCR start")); // FIXME m_incr = true; // discard INCR data @@ -1373,7 +1391,6 @@ log((CLOG_INFO " INCR start")); // FIXME // secondary chunks must have the same target else { -log((CLOG_INFO " INCR secondary chunk")); // FIXME if (target != *m_actualTarget) { log((CLOG_WARN " INCR target mismatch")); m_failed = true; From 3bfd2e252e1bc8d733ef428c8ec037f1b0f4f873 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Jul 2002 09:23:24 +0000 Subject: [PATCH 269/807] finished INCR transfer changes. also made motifGetTime() return icccmGetTime() because it seems motif does TIMESTAMP like ICCCM. --- platform/CXWindowsClipboard.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index 778c3d8f..29c4270c 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -793,8 +793,7 @@ CXWindowsClipboard::motifGetSelection(const CMotifClipFormat* format, IClipboard::Time CXWindowsClipboard::motifGetTime() const { - // FIXME -- where does Motif report this? - return 0; + return icccmGetTime(); } bool @@ -955,18 +954,15 @@ CXWindowsClipboard::sendReply(CReply* reply) // send using INCR if already sending incrementally or if reply // is too large, otherwise just send it. - const UInt32 maxRequestSize = 50000;//XXX 4 * XMaxRequestSize(m_display); + const UInt32 maxRequestSize = 3 * XMaxRequestSize(m_display); const bool useINCR = (reply->m_data.size() > maxRequestSize); -log((CLOG_INFO "useINCR: %s", useINCR ? "true" : "false")); // send INCR reply if incremental and we haven't replied yet if (useINCR && !reply->m_replied) { UInt32 size = reply->m_data.size(); -log((CLOG_INFO "send first INCR, size=%d", size)); if (!CXWindowsUtil::setWindowProperty(m_display, reply->m_requestor, reply->m_property, &size, 4, m_atomINCR, 32)) { -log((CLOG_INFO "send first INCR failed")); failed = true; } } @@ -977,8 +973,6 @@ log((CLOG_INFO "send first INCR failed")); UInt32 size = reply->m_data.size() - reply->m_ptr; if (size > maxRequestSize) size = maxRequestSize; -if (useINCR) -log((CLOG_INFO "more INCR, size=%d, ptr=%d", size, reply->m_ptr)); // send it if (!CXWindowsUtil::setWindowProperty(m_display, @@ -986,8 +980,6 @@ log((CLOG_INFO "more INCR, size=%d, ptr=%d", size, reply->m_ptr)); reply->m_data.data() + reply->m_ptr, size, reply->m_type, reply->m_format)) { -if (useINCR) -log((CLOG_INFO "more INCR failed")); failed = true; } else { @@ -996,8 +988,6 @@ log((CLOG_INFO "more INCR failed")); // we've finished the reply if we just sent the zero // size incremental chunk or if we're not incremental. reply->m_done = (size == 0 || !useINCR); -if (useINCR) -log((CLOG_INFO "more INCR sent, done = %s", reply->m_done ? "true" : "false")); } } } @@ -1009,7 +999,6 @@ log((CLOG_INFO "more INCR sent, done = %s", reply->m_done ? "true" : "false")); // the final zero-length property. // FIXME -- how do you gracefully cancel an incremental transfer? if (failed) { -log((CLOG_INFO "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); log((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); reply->m_done = true; if (reply->m_property != None) { @@ -1040,7 +1029,6 @@ log((CLOG_INFO "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, // send notification if we haven't yet if (!reply->m_replied) { -log((CLOG_INFO "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); log((CLOG_DEBUG1 "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); reply->m_replied = true; From 8334d987f584f883d63e35113a627146259f1a64 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Jul 2002 09:55:01 +0000 Subject: [PATCH 270/807] added unix specific implementation of CXWindowsScreen::mainLoop() that uses poll() to process events more efficiently. it won't wake up nor sleep any more than necessary, unlike the platform independent implementation that polls and sleeps. --- platform/CXWindowsScreen.cpp | 75 ++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index ae1936f5..e5e2fd8f 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -17,6 +17,9 @@ # include # include #endif +#if UNIX_LIKE +#include +#endif // // CXWindowsScreen::CTimer @@ -218,19 +221,32 @@ CXWindowsScreen::mainLoop() // display while we're waiting. CEvent event; m_mutex.lock(); - while (!m_stop) { - while (!m_stop && XPending(m_display) == 0) { - // check timers - if (processTimers()) { - continue; - } - // wait - m_mutex.unlock(); - CThread::sleep(0.01); - m_mutex.lock(); - } - if (!m_stop) { +#if UNIX_LIKE + + // use poll() to wait for a message from the X server or for timeout. + // this is a good deal more efficient than polling and sleeping. + struct pollfd pfds[1]; + pfds[0].fd = ConnectionNumber(m_display); + pfds[0].events = POLLIN; + while (!m_stop) { + // compute timeout to next timer + int timeout = (m_timers.empty() ? -1 : + static_cast(1000.0 * m_timers.top())); + + // wait for message from X server or for timeout. also check + // if the thread has been cancelled. poll() should return -1 + // with EINTR when the thread is cancelled. + m_mutex.unlock(); + poll(pfds, 1, timeout); + CThread::testCancel(); + m_mutex.lock(); + + // process timers + processTimers(); + + // handle pending events + while (!m_stop && XPending(m_display) > 0) { // get the event XNextEvent(m_display, &event.m_event); @@ -243,6 +259,41 @@ CXWindowsScreen::mainLoop() m_mutex.lock(); } } + +#else // !UNIX_LIKE + + // poll and sleep + while (!m_stop) { + // poll for pending events and process timers + while (!m_stop && XPending(m_display) == 0) { + // check timers + if (processTimers()) { + continue; + } + + // wait + m_mutex.unlock(); + CThread::sleep(0.01); + m_mutex.lock(); + } + + // process events + while (!m_stop && XPending(m_display) > 0) { + // get the event + XNextEvent(m_display, &event.m_event); + + // process the event. if unhandled then let the subclass + // have a go at it. + m_mutex.unlock(); + if (!onPreDispatch(&event)) { + m_eventHandler->onEvent(&event); + } + m_mutex.lock(); + } + } + +#endif // !UNIX_LIKE + m_mutex.unlock(); } From 1fd8e25f7d76fbd48b94c0ba9d596266575575b6 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Jul 2002 17:23:35 +0000 Subject: [PATCH 271/807] moved string formatting into CStringUtil from CLog and added methods for format positional string arguments. --- base/CLog.cpp | 41 +++---------- base/CLog.h | 2 - base/CString.cpp | 156 ++++++++++++++++++++++++++++++++++++++++++++++- base/CString.h | 31 ++++++++-- 4 files changed, 191 insertions(+), 39 deletions(-) diff --git a/base/CLog.cpp b/base/CLog.cpp index 38bcdfd6..b104e5cc 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -1,4 +1,5 @@ #include "CLog.h" +#include "CString.h" #include #include #include @@ -82,8 +83,9 @@ CLog::print(const char* fmt, ...) char stack[1024]; va_list args; va_start(args, fmt); - char* buffer = vsprint(pad, stack, - sizeof(stack) / sizeof(stack[0]), fmt, args); + char* buffer = CStringUtil::vsprint(stack, + sizeof(stack) / sizeof(stack[0]), + pad, g_newlineLength, fmt, args); va_end(args); // output buffer @@ -119,8 +121,9 @@ CLog::printt(const char* file, int line, const char* fmt, ...) // print to buffer, leaving space for a newline at the end va_list args; va_start(args, fmt); - char* buffer = vsprint(pad, stack, - sizeof(stack) / sizeof(stack[0]), fmt, args); + char* buffer = CStringUtil::vsprint(stack, + sizeof(stack) / sizeof(stack[0]), + pad, g_newlineLength, fmt, args); va_end(args); // print the prefix to the buffer. leave space for priority label. @@ -227,8 +230,9 @@ CLog::output(int priority, char* msg) int n = -g_prioritySuffixLength; if (priority >= 0) { n = strlen(g_priority[priority]); - sprintf(msg + g_maxPriorityLength - n, - "%s:", g_priority[priority]); + strcpy(msg + g_maxPriorityLength - n, g_priority[priority]); + msg[g_maxPriorityLength + 0] = ':'; + msg[g_maxPriorityLength + 1] = ' '; msg[g_maxPriorityLength + 1] = ' '; } @@ -246,31 +250,6 @@ CLog::output(int priority, char* msg) } } -char* -CLog::vsprint(int pad, char* buffer, int len, const char* fmt, va_list args) -{ - assert(len > 0); - - // try writing to input buffer - int n; - if (len >= pad) { - n = vsnprintf(buffer + pad, len - pad, fmt, args); - if (n >= 0 && n <= len - pad + g_newlineLength) - return buffer; - } - - // start allocating buffers until we write the whole string - buffer = NULL; - do { - delete[] buffer; - len *= 2; - buffer = new char[len + pad]; - n = vsnprintf(buffer + pad, len - pad, fmt, args); - } while (n < 0 || n > len - pad + g_newlineLength); - - return buffer; -} - #if WINDOWS_LIKE static DWORD s_thread = 0; diff --git a/base/CLog.h b/base/CLog.h index 64fd9976..834b5bec 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -65,8 +65,6 @@ private: static void dummyLock(bool); static int getMaxPriority(); static void output(int priority, char* msg); - static char* vsprint(int pad, char*, int len, const char*, va_list); - static int nprint(const char*, va_list); #if WINDOWS_LIKE static void openConsole(); #endif diff --git a/base/CString.cpp b/base/CString.cpp index 775fe912..f6d30794 100644 --- a/base/CString.cpp +++ b/base/CString.cpp @@ -1,7 +1,159 @@ #include "CString.h" +#include "stdvector.h" #include +#include +#include #include +#if WINDOWS_LIKE +#define WIN32_LEAN_AND_MEAN +#include +#define vsnprintf _vsnprintf +#endif + +// +// CStringUtil +// + +CString +CStringUtil::format(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + CString result = vformat(fmt, args); + va_end(args); + return result; +} + +CString +CStringUtil::vformat(const char* fmt, va_list args) +{ + // find highest indexed substitution and the locations of substitutions + std::vector pos; + std::vector width; + std::vector index; + int maxIndex = 0; + for (const char* scan = fmt; *scan != '\0'; ++scan) { + if (*scan == '%') { + ++scan; + if (*scan == '\0') { + break; + } + else if (*scan == '%') { + // literal + index.push_back(0); + pos.push_back(static_cast(scan - 1 - fmt)); + width.push_back(2); + } + else if (*scan == '{') { + // get argument index + char* end; + int i = static_cast(strtol(scan + 1, &end, 10)); + if (*end != '}') { + // invalid index -- ignore + scan = end - 1; + } + else { + index.push_back(i); + pos.push_back(static_cast(scan - 1 - fmt)); + width.push_back(static_cast(end - scan + 2)); + if (i > maxIndex) { + maxIndex = i; + } + scan = end; + } + } + else { + // improper escape -- ignore + } + } + } + + // get args + std::vector value; + std::vector length; + value.push_back("%"); + length.push_back(1); + for (int i = 0; i < maxIndex; ++i) { + const char* arg = va_arg(args, const char*); + value.push_back(arg); + length.push_back(strlen(arg)); + } + + // compute final length + size_t resultLength = strlen(fmt); + const int n = static_cast(pos.size()); + for (int i = 0; i < n; ++i) { + resultLength -= width[i]; + resultLength += length[index[i]]; + } + + // substitute + CString result; + result.reserve(resultLength); + size_t src = 0; + for (int i = 0; i < n; ++i) { + result.append(fmt + src, pos[i] - src); + result.append(value[index[i]]); + src = pos[i] + width[i]; + } + result.append(fmt + src); + + return result; +} + +CString +CStringUtil::print(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + CString result = vprint(fmt, args); + va_end(args); + return result; +} + +CString +CStringUtil::vprint(const char* fmt, va_list args) +{ + char tmp[1024]; + char* buffer = vsprint(tmp, sizeof(tmp) / sizeof(tmp[0]), 0, 0, fmt, args); + if (buffer == tmp) { + return buffer; + } + else { + CString result(buffer); + delete[] buffer; + return result; + } +} + +char* +CStringUtil::vsprint(char* buffer, int len, + int prefix, int suffix, const char* fmt, va_list args) +{ + assert(len > 0); + + // try writing to input buffer + int n; + if (buffer != NULL && len >= prefix + suffix) { + n = vsnprintf(buffer + prefix, len - (prefix + suffix), fmt, args); + if (n >= 0 && n <= len - (prefix + suffix)) + return buffer; + } + + // start allocating buffers until we write the whole string + buffer = NULL; + do { + delete[] buffer; + len *= 2; + buffer = new char[len + (prefix + suffix)]; + n = vsnprintf(buffer + prefix, len - (prefix + suffix), fmt, args); + } while (n < 0 || n > len - (prefix + suffix)); + + return buffer; +} + + // // CStringUtil::CaselessCmp // @@ -11,7 +163,7 @@ CStringUtil::CaselessCmp::cmpEqual( const CString::value_type& a, const CString::value_type& b) { - // FIXME -- use std::tolower but not in all versions of libstdc++ + // FIXME -- use std::tolower but not in all versions of libstdc++ have it return tolower(a) == tolower(b); } @@ -20,7 +172,7 @@ CStringUtil::CaselessCmp::cmpLess( const CString::value_type& a, const CString::value_type& b) { - // FIXME -- use std::tolower but not in all versions of libstdc++ + // FIXME -- use std::tolower but not in all versions of libstdc++ have it return tolower(a) < tolower(b); } diff --git a/base/CString.h b/base/CString.h index 78fd9ad2..56c3a690 100644 --- a/base/CString.h +++ b/base/CString.h @@ -1,18 +1,41 @@ #ifndef CSTRING_H #define CSTRING_H +#include #include "stdpre.h" #include #include "stdpost.h" -// use to get appropriate type for string constants. it depends on -// the internal representation type of CString. -#define _CS(_x) _x - typedef std::string CString; class CStringUtil { public: + // format a string using positional arguments. fmt has literal + // characters and conversion specifications introduced by `%': + // %% literal `%' + // %{n} positional element n, n a positive integer, {} are literal + // all arguments in the variable list are const char*. positional + // elements are indexed from 1. + static CString format(const char* fmt, ...); + static CString vformat(const char* fmt, va_list); + + // print a string using printf-style formatting + static CString print(const char* fmt, ...); + static CString vprint(const char* fmt, va_list); + + // like print but print into a given buffer. if the resulting string + // will not fit into the buffer then a new buffer is allocated and + // returned, otherwise the input buffer is returned. the caller must + // delete[] the returned buffer if is not the passed-in buffer. + // + // prefix and suffix must be >= 0. exactly prefix characters and + // at least suffix characters are available in the buffer before + // and after the printed string, respectively. bufferLength is the + // length of buffer and should not be adjusted by the caller to + // account for prefix or suffix. + static char* vsprint(char* buffer, int bufferLength, + int prefix, int suffix, const char* fmt, va_list); + class CaselessCmp { public: bool operator()(const CString&, const CString&) const; From f129841b385a991e9c7caeaf40f340dc0beb6ff1 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Jul 2002 17:52:40 +0000 Subject: [PATCH 272/807] made all getWhat() methods on exceptions consistent. they now all use format() the same way. also changed format() to actually do formatting. however, it doesn't try looking up formatting strings by id, it just uses the fallback format string. --- base/XBase.cpp | 20 +++++++++++++++++--- http/XHTTP.cpp | 20 ++++++++------------ io/XIO.cpp | 2 +- net/XNetwork.cpp | 7 ++++--- net/XSocket.cpp | 21 +++++++++++++++------ server/CConfig.cpp | 2 +- server/server.cpp | 12 ++++++------ synergy/CProtocolUtil.cpp | 2 +- synergy/XScreen.cpp | 2 +- synergy/XSynergy.cpp | 8 +++++--- 10 files changed, 59 insertions(+), 37 deletions(-) diff --git a/base/XBase.cpp b/base/XBase.cpp index fba37293..19a93a13 100644 --- a/base/XBase.cpp +++ b/base/XBase.cpp @@ -1,5 +1,6 @@ #include "XBase.h" #include +#include // win32 wants a const char* argument to std::exception c'tor #if WINDOWS_LIKE @@ -46,9 +47,22 @@ XBase::what() const CString XBase::format(const char* /*id*/, const char* fmt, ...) const throw() { - // FIXME -- use id to lookup formating string - // FIXME -- format string with arguments - return fmt; + // FIXME -- lookup message string using id as an index. set + // fmt to that string if it exists. + + // format + CString result; + va_list args; + va_start(args, fmt); + try { + result = CStringUtil::vformat(fmt, args); + } + catch (...) { + // ignore + } + va_end(args); + + return result; } diff --git a/http/XHTTP.cpp b/http/XHTTP.cpp index 8ef27b61..b6535d9f 100644 --- a/http/XHTTP.cpp +++ b/http/XHTTP.cpp @@ -48,20 +48,16 @@ XHTTP::addHeaders(CHTTPReply&) const CString XHTTP::getWhat() const throw() { - try { - std::ostringstream s; - s << m_status << " "; - if (!m_reason.empty()) { - s << m_reason.c_str(); - } - else { - s << getReason(m_status); - } - return s.str(); + const char* reason; + if (m_reason.empty()) { + reason = getReason(m_status); } - catch (...) { - return CString(); + else { + reason = m_reason.c_str(); } + return format("XHTTP", "%{1} %{2}", + CStringUtil::print("%d", m_status).c_str(), + reason); } const char* diff --git a/io/XIO.cpp b/io/XIO.cpp index 67508865..e5b07e3d 100644 --- a/io/XIO.cpp +++ b/io/XIO.cpp @@ -24,7 +24,7 @@ XIOErrno::XIOErrno(int err) : CString XIOClose::getWhat() const throw() { - return format("XIOClose", "close: %1", XIOErrno::getErrstr()); + return format("XIOClose", "close: %{1}", XIOErrno::getErrstr()); } diff --git a/net/XNetwork.cpp b/net/XNetwork.cpp index ece106bd..a6e77e58 100644 --- a/net/XNetwork.cpp +++ b/net/XNetwork.cpp @@ -49,8 +49,9 @@ CString XNetworkVersion::getWhat() const throw() { return format("XNetworkVersion", - "unsupported network version %d.%d", - m_major, m_minor); + "unsupported network version %{1}.%{2}", + CStringUtil::print("%d", m_major).c_str(), + CStringUtil::print("%d", m_minor).c_str()); } @@ -73,6 +74,6 @@ CString XNetworkFunctionUnavailable::getWhat() const throw() { return format("XNetworkFunctionUnavailable", - "missing network function %s", + "missing network function %{1}", m_name.c_str()); } diff --git a/net/XSocket.cpp b/net/XSocket.cpp index 7cb6e995..f746abc5 100644 --- a/net/XSocket.cpp +++ b/net/XSocket.cpp @@ -34,12 +34,21 @@ XSocketAddress::getPort() const throw() CString XSocketAddress::getWhat() const throw() { - return "no address"; -/* FIXME - return format("XSocketAddress", "no address: %1:%2", - m_hostname.t_str(), - CString::sprintf("%d", m_port).t_str()); -*/ + static const char* s_errorID[] = { + "XSocketAddressUnknown", + "XSocketAddressNotFound", + "XSocketAddressNoAddress", + "XSocketAddressBadPort" + }; + static const char* s_errorMsg[] = { + "unknown error for: %{1}:%{2}", + "address not found for: %{1}", + "no address for: %{1}", + "invalid port: %{2}" + }; + return format(s_errorID[m_error], s_errorMsg[m_error], + m_hostname.c_str(), + CStringUtil::print("%d", m_port).c_str()); } diff --git a/server/CConfig.cpp b/server/CConfig.cpp index 082f7a47..62fcf35e 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -695,5 +695,5 @@ XConfigRead::~XConfigRead() CString XConfigRead::getWhat() const throw() { - return m_error; + return format("XConfigRead", "read error: %s", m_error.c_str()); } diff --git a/server/server.cpp b/server/server.cpp index 14e1f696..86081fa5 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -336,9 +336,9 @@ parse(int argc, const char** argv) try { s_synergyAddress = CNetworkAddress(argv[i + 1], kDefaultPort); } - catch (XSocketAddress&) { - log((CLOG_PRINT "%s: invalid address for `%s'" BYE, - pname, argv[i], pname)); + catch (XSocketAddress& e) { + log((CLOG_PRINT "%s: invalid address for `%s'. %s." BYE, + pname, argv[i], e.what(), pname)); bye(kExitArgs); } ++i; @@ -349,9 +349,9 @@ parse(int argc, const char** argv) try { s_httpAddress = CNetworkAddress(argv[i + 1], kDefaultPort + 1); } - catch (XSocketAddress&) { - log((CLOG_PRINT "%s: invalid address for `%s'" BYE, - pname, argv[i], pname)); + catch (XSocketAddress& e) { + log((CLOG_PRINT "%s: invalid address for `%s'. %s." BYE, + pname, argv[i], e.what(), pname)); bye(kExitArgs); } ++i; diff --git a/synergy/CProtocolUtil.cpp b/synergy/CProtocolUtil.cpp index 9426a2f6..0853a8fb 100644 --- a/synergy/CProtocolUtil.cpp +++ b/synergy/CProtocolUtil.cpp @@ -370,5 +370,5 @@ CProtocolUtil::read(IInputStream* stream, void* vbuffer, UInt32 count) CString XIOReadMismatch::getWhat() const throw() { - return "CProtocolUtil::readf() mismatch"; + return format("XIOReadMismatch", "CProtocolUtil::readf() mismatch"); } diff --git a/synergy/XScreen.cpp b/synergy/XScreen.cpp index 2354b04c..b011b131 100644 --- a/synergy/XScreen.cpp +++ b/synergy/XScreen.cpp @@ -7,5 +7,5 @@ CString XScreenOpenFailure::getWhat() const throw() { - return "XScreenOpenFailure"; + return format("XScreenOpenFailure", "unable to open screen"); } diff --git a/synergy/XSynergy.cpp b/synergy/XSynergy.cpp index 8c6edd11..c8b976f8 100644 --- a/synergy/XSynergy.cpp +++ b/synergy/XSynergy.cpp @@ -37,7 +37,9 @@ XIncompatibleClient::getMinor() const throw() CString XIncompatibleClient::getWhat() const throw() { - return "XIncompatibleClient"; + return format("XIncompatibleClient", "incompatible client %{1}.%{2}", + CStringUtil::print("%d", m_major).c_str(), + CStringUtil::print("%d", m_minor).c_str()); } @@ -60,7 +62,7 @@ XDuplicateClient::getName() const throw() CString XDuplicateClient::getWhat() const throw() { - return "XDuplicateClient"; + return format("XDuplicateClient", "duplicate client %{1}", m_name.c_str()); } @@ -83,5 +85,5 @@ XUnknownClient::getName() const throw() CString XUnknownClient::getWhat() const throw() { - return "XUnknownClient"; + return format("XUnknownClient", "unknown client %{1}", m_name.c_str()); } From 4688c99ee4b046544494fe12370b8b5ca868a1a2 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Jul 2002 17:58:01 +0000 Subject: [PATCH 273/807] improved error messages for bad addresses. --- client/client.cpp | 6 +++--- net/XSocket.cpp | 2 +- server/server.cpp | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/client.cpp b/client/client.cpp index 0bda2676..cc2243ed 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -406,9 +406,9 @@ parse(int argc, const char** argv) try { s_serverAddress = CNetworkAddress(argv[i], kDefaultPort); } - catch (XSocketAddress&) { - log((CLOG_PRINT "%s: invalid server address" BYE, - pname, pname)); + catch (XSocketAddress& e) { + log((CLOG_PRINT "%s: %s" BYE, + pname, e.what(), pname)); bye(kExitArgs); } } diff --git a/net/XSocket.cpp b/net/XSocket.cpp index f746abc5..63f9fc03 100644 --- a/net/XSocket.cpp +++ b/net/XSocket.cpp @@ -44,7 +44,7 @@ XSocketAddress::getWhat() const throw() "unknown error for: %{1}:%{2}", "address not found for: %{1}", "no address for: %{1}", - "invalid port: %{2}" + "invalid port" // m_port may not be set to the bad port }; return format(s_errorID[m_error], s_errorMsg[m_error], m_hostname.c_str(), diff --git a/server/server.cpp b/server/server.cpp index 86081fa5..c49d4a8e 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -337,8 +337,8 @@ parse(int argc, const char** argv) s_synergyAddress = CNetworkAddress(argv[i + 1], kDefaultPort); } catch (XSocketAddress& e) { - log((CLOG_PRINT "%s: invalid address for `%s'. %s." BYE, - pname, argv[i], e.what(), pname)); + log((CLOG_PRINT "%s: %s" BYE, + pname, e.what(), pname)); bye(kExitArgs); } ++i; @@ -350,8 +350,8 @@ parse(int argc, const char** argv) s_httpAddress = CNetworkAddress(argv[i + 1], kDefaultPort + 1); } catch (XSocketAddress& e) { - log((CLOG_PRINT "%s: invalid address for `%s'. %s." BYE, - pname, argv[i], e.what(), pname)); + log((CLOG_PRINT "%s: %s" BYE, + pname, e.what(), pname)); bye(kExitArgs); } ++i; From 4bf6cf2adff84e749130bf1cc651835cafcd2879 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 25 Jul 2002 18:08:00 +0000 Subject: [PATCH 274/807] checkpoint. --- notes | 61 ++++++++++++++++++++--------------------------------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/notes b/notes index 69debad0..b18e1af2 100644 --- a/notes +++ b/notes @@ -1,23 +1,25 @@ -nedit doesn't seem to be playing nice with the motif clipboard lock - it's not releasing it - may need to learn more about how the motif clipboard lock works - not sure if this a new problem with synergy or a problem with new nedit +not handling large motif selections + don't know protocol for transfer + +--- +not handling international characters + +--- +try to determine X11 keyboard quirks automatically + in particular, set the half-duplex flags + maybe KBD extension will help determine this + +bug with half-duplex keys + win32 server sent num-lock to grace + grace converts to half-duplex so only synthesizes key down + now grace starts server and is locked to screen + grace should ignore half duplex keys when seeing if locked to screen --- enable heartbeat disconnection or remove heartbeat sending in client should either ship with it fully enabled or fully disabled possibly make it part of startup negotiation ---- -not handling large motif selections - there must be a limit because properties can only be so big - don't know protocol for transfer, though - ---- -synergy should never restart if X server connection was lost? - if synergy server connection was lost then restart - unless --no-restart - --- use automake use doxygen @@ -32,10 +34,6 @@ HTTP stuff provide way to query why locked to screen? handy for debugging at least ---- -hot keys - should have keyboard shortcuts to jump to screens - --- win32: need icon @@ -57,13 +55,6 @@ desktop switcher program failure when running synergy as service returns access denied from CreateDesktop() don't know why ---- -bug with half-duplex keys - win32 server sent num-lock to grace - grace converts to half-duplex so only synthesizes key down - now grace starts server and is locked to screen - grace should ignore half duplex keys when seeing if locked to screen - --- bug in updating screens saw a goofy set of screens after update @@ -72,15 +63,16 @@ bug in updating screens C D E +--- +hot keys + should have keyboard shortcuts to jump to screens + --- should switch to user nobody (or something similar) if running as root for security reasons will make it impossible to overwrite /etc/synergy.conf if that file is only root writable (as it should be) ---- -not handling international characters - --- xscreensaver won't start if pointer on secondary screen unless using MIT or SGI screen saver extensions @@ -110,14 +102,6 @@ need timeout on CXWindowsClipboard::CReply objects should flush replies that are too old assumption is that requestor is broken ---- -inconsistencies - exceptions - using getWhat() in some places to return a code, elsewhere returns message - references/pointers - should use pointers for any in/out parameter - but using references in some places - --- avoid fullscreen transparent window on win32 using CBT hook to discard activation/focus messages @@ -140,11 +124,6 @@ keyboard hook problems (win32) a down is sent but not the corresponding up so secondary system thinks the key is always down. ---- -try to determine X11 keyboard quirks automatically - in particular, set the half-duplex flags - maybe KBD extension will help determine this - --- key events sent to console on win me (95/98?) are very slow 1/4 to 1/2 second delay before key up is processed From 3f046bb48ac7e2cf1d8dcf5686bcf1c5ef2401e9 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 26 Jul 2002 15:22:25 +0000 Subject: [PATCH 275/807] now deleting property when so requested even if read failed. --- platform/CXWindowsUtil.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/platform/CXWindowsUtil.cpp b/platform/CXWindowsUtil.cpp index f65fe020..f7ecb357 100644 --- a/platform/CXWindowsUtil.cpp +++ b/platform/CXWindowsUtil.cpp @@ -21,6 +21,7 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, CXWindowsUtil::CErrorLock lock(display); // read the property + int result = Success; const long length = XMaxRequestSize(display); long offset = 0; unsigned long bytesLeft = 1; @@ -28,13 +29,13 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, // get more data unsigned long numItems; unsigned char* rawData; - const int result = XGetWindowProperty(display, window, property, + result = XGetWindowProperty(display, window, property, offset, length, False, AnyPropertyType, &actualType, &actualDatumSize, &numItems, &bytesLeft, &rawData); if (result != Success || actualType == None || actualDatumSize == 0) { // failed - return false; + break; } // compute bytes read and advance offset @@ -83,8 +84,14 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, *format = static_cast(actualDatumSize); } - log((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); - return true; + if (result == Success) { + log((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); + return true; + } + else { + log((CLOG_DEBUG1 "can't read property %d on window 0x%08x", property, window)); + return false; + } } bool From 0bf455e79a767e68977e79c4750f33dc51c97274 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 26 Jul 2002 16:05:59 +0000 Subject: [PATCH 276/807] added configuration file for building doxygen documentation. the code is not yet doxygen documented, though. --- doc/doxygen.cfg | 899 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 899 insertions(+) create mode 100644 doc/doxygen.cfg diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg new file mode 100644 index 00000000..4e83cbcb --- /dev/null +++ b/doc/doxygen.cfg @@ -0,0 +1,899 @@ +# Doxyfile 1.2.13.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Synergy + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1.0.0 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/doxygen + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French, +# German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish, +# Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES +#XXX EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consist of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl + +FILE_PATTERNS = *.cpp *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse. + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 3 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the Html help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, +# or Internet explorer 4.0+). Note that for large projects the tree generation +# can take a very long time. In such cases it is better to disable this feature. +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line and do not end with a semicolon. Such function macros are typically +# used for boiler-plate code, and will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yield more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermedate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = From 879cf26f8d1b134457223801b445ffe26704bdeb Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 26 Jul 2002 18:27:31 +0000 Subject: [PATCH 277/807] fixed type mismatch (SInt32 vs int) in definition of getWindowProperty(). --- platform/CXWindowsUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/CXWindowsUtil.cpp b/platform/CXWindowsUtil.cpp index f7ecb357..60d355bd 100644 --- a/platform/CXWindowsUtil.cpp +++ b/platform/CXWindowsUtil.cpp @@ -10,7 +10,7 @@ bool CXWindowsUtil::getWindowProperty(Display* display, Window window, Atom property, CString* data, Atom* type, - int* format, bool deleteProperty) + SInt32* format, bool deleteProperty) { assert(display != NULL); From 7a461855eb52db188902f96512f550a102b4bccf Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 26 Jul 2002 18:28:18 +0000 Subject: [PATCH 278/807] added doxygen comments for all relevant headers in base. --- base/CFunctionJob.h | 5 ++ base/CLog.h | 163 +++++++++++++++++++++++++++++++++++--------- base/CStopwatch.h | 79 +++++++++++++++------ base/CString.h | 86 ++++++++++++++++------- base/CUnicode.h | 89 +++++++++++++++++++++--- base/IInterface.h | 6 ++ base/IJob.h | 5 ++ base/TMethodJob.h | 5 ++ base/XBase.h | 34 +++++++-- base/common.h | 2 + 10 files changed, 380 insertions(+), 94 deletions(-) diff --git a/base/CFunctionJob.h b/base/CFunctionJob.h index b1c786e0..311ceaf9 100644 --- a/base/CFunctionJob.h +++ b/base/CFunctionJob.h @@ -3,8 +3,13 @@ #include "IJob.h" +//! Use a function as a job +/*! +A job class that invokes a function. +*/ class CFunctionJob : public IJob { public: + //! run() invokes \c func(arg) CFunctionJob(void (*func)(void*), void* arg = NULL); virtual ~CFunctionJob(); diff --git a/base/CLog.h b/base/CLog.h index 834b5bec..085016e2 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -4,54 +4,114 @@ #include "common.h" #include +//! Logging facility +/*! +The logging class; all console output should go through this class. +It supports multithread safe operation, several message priority levels, +filtering by priority, and output redirection. The macros log() and +clog() provide convenient access. +*/ class CLog { public: - enum { - kFATAL, - kERROR, - kWARNING, - kNOTE, - kINFO, - kDEBUG, - kDEBUG1, - kDEBUG2 + //! Log levels + /*! + The logging priority levels in order of highest to lowest priority. + */ + enum ELevel { + kFATAL, //!< For fatal errors + kERROR, //!< For serious errors + kWARNING, //!< For minor errors and warnings + kNOTE, //!< For messages about notable events + kINFO, //!< For informational messages + kDEBUG, //!< For important debugging messages + kDEBUG1, //!< For more detailed debugging messages + kDEBUG2 //!< For even more detailed debugging messages }; - // type of outputter function. return false if CLog should use - // the default outputter, true otherwise. - typedef bool (*Outputter)(int priority, const char*); + //! Outputter function. + /*! + Type of outputter function. The outputter should write \c message, + which has the given \c priority, to a log and return true. Or it can + return false to let CLog use the default outputter. + */ + typedef bool (*Outputter)(int priority, const char* message); - // type of lock/unlock function + //! Locking function + /*! + Type of lock/unlock function. If \c lock is true then block other + threads that try to lock until this thread unlocks. If \c lock is + false then unlock and allow another (waiting) thread to lock. + */ typedef void (*Lock)(bool lock); - // print a log message - static void print(const char*, ...); - static void printt(const char* file, int line, const char*, ...); + //! @name manipulators + //@{ - // get/set the function used to write the log. a NULL outputter - // means to use the default which is fprintf(stderr, ...). note - // that the outputter should not call CLog methods but, if it - // does, the current lock function must permit recursive locks. + //! Set the function used to write the log + /*! + Sets the function used to write to the log. The outputter function + is called with the formatted string to write and the priority level. + CLog will have already filtered messages below the current filter + priority. A NULL outputter means to use the default which is to print + to stderr. Note that the outputter should not call CLog methods but, + if it does, the current lock function must permit recursive locks. + */ static void setOutputter(Outputter); - static Outputter getOutputter(); - // get/set the lock/unlock function. use setLock(NULL) to remove - // the locking function. note that the lock function is used when - // retrieving the lock function. there is no default lock function. + //! Set the lock/unlock function + /*! + Set the lock/unlock function. Use setLock(NULL) to remove the + locking function. There is no default lock function; do not call + CLog from multiple threads unless a working lock function has been + installed. + */ static void setLock(Lock); - static Lock getLock(); - // get/set the minimum priority filter. any message below this - // priority is discarded. the default priority is 4 (INFO) - // (unless built without NDEBUG in which case it's 5 (DEBUG)). - // the default can be overridden by setting the SYN_LOG_PRI env - // var to "FATAL", "ERROR", etc. setFilter(const char*) returns - // true if the priority name was recognized; if name == NULL - // then it simply returns true. + //! Set the minimum priority filter. + /*! + Set the filter. Messages below this priority are discarded. + The default priority is 4 (INFO) (unless built without NDEBUG + in which case it's 5 (DEBUG)). The default can be overridden + by setting the SYN_LOG_PRI env var to "FATAL", "ERROR", etc. + setFilter(const char*) returns true if the priority \c name was + recognized; if \c name is NULL then it simply returns true. + */ static bool setFilter(const char* name); static void setFilter(int); + + //@} + //! @name accessors + //@{ + + //! Print a log message + /*! + Print a log message using the printf-like \c format and arguments. + */ + static void print(const char* format, ...); + + //! Print a log message + /*! + Print a log message using the printf-like \c format and arguments + preceded by the filename and line number. + */ + static void printt(const char* file, int line, + const char* format, ...); + + //! Get the function used to write the log + static Outputter getOutputter(); + + //! Get the lock/unlock function + /*! + Get the lock/unlock function. Note that the lock function is + used when retrieving the lock function. + */ + static Lock getLock(); + + //! Get the minimum priority level. static int getFilter(); + //@} + private: class CHoldLock { public: @@ -75,6 +135,45 @@ private: static int s_maxPriority; }; +/*! +\def log(arg) +Write to the log. Because macros cannot accept variable arguments, this +should be invoked like so: +\code +log((CLOG_XXX "%d and %d are %s", x, y, x == y ? "equal" : "not equal")); +\endcode +In particular, notice the double open and close parentheses. Also note +that there is no comma after the \c CLOG_XXX. The \c XXX should be +replaced by one of enumerants in \c CLog::ELevel without the leading +\c k. For example, \c CLOG_INFO. The special \c CLOG_PRINT level will +not be filtered and is never prefixed by the filename and line number. + +If \c NOLOGGING is defined during the build then this macro expands to +nothing. If \c NDEBUG is defined during the build then it expands to a +call to CLog::print. Otherwise it expands to a call to CLog::printt, +which includes the filename and line number. +*/ + +/*! +\def logc(expr, arg) +Write to the log if and only if expr is true. Because macros cannot accept +variable arguments, this should be invoked like so: +\code +clog(x == y, (CLOG_XXX "%d and %d are equal", x, y)); +\endcode +In particular, notice the parentheses around everything after the boolean +expression. Also note that there is no comma after the \c CLOG_XXX. +The \c XXX should be replaced by one of enumerants in \c CLog::ELevel +without the leading \c k. For example, \c CLOG_INFO. The special +\c CLOG_PRINT level will not be filtered and is never prefixed by the +filename and line number. + +If \c NOLOGGING is defined during the build then this macro expands to +nothing. If \c NDEBUG is defined during the build then it expands to a +call to CLog::print. Otherwise it expands to a call to CLog::printt, +which includes the filename and line number. +*/ + #if defined(NOLOGGING) #define log(_a1) #define logc(_a1, _a2) diff --git a/base/CStopwatch.h b/base/CStopwatch.h index 791729f2..8817a67a 100644 --- a/base/CStopwatch.h +++ b/base/CStopwatch.h @@ -3,47 +3,84 @@ #include "common.h" +//! A timer class +/*! +This class measures time intervals. All time interval measurement +should use this class. +*/ class CStopwatch { public: - // the default constructor does an implicit reset() or setTrigger(). - // if triggered == false then the clock starts ticking. + /*! + The default constructor does an implicit reset() or setTrigger(). + If triggered == false then the clock starts ticking. + */ CStopwatch(bool triggered = false); ~CStopwatch(); - // manipulators + //! @name manipulators + //@{ - // set the start time to the current time, returning the time since - // the last reset. this does not remove the trigger if it's set nor - // does it start a stopped clock. if the clock is stopped then - // subsequent reset()'s will return 0. + //! Reset the timer to zero + /*! + Set the start time to the current time, returning the time since + the last reset. This does not remove the trigger if it's set nor + does it start a stopped clock. If the clock is stopped then + subsequent reset()'s will return 0. + */ double reset(); - // stop and start the stopwatch. while stopped, no time elapses. - // stop() does not remove the trigger but start() does, even if - // the clock was already started. + //! Stop the timer + /*! + Stop the stopwatch. The time interval while stopped is not + counted by the stopwatch. stop() does not remove the trigger. + Has no effect if already stopped. + */ void stop(); + + //! Start the timer + /*! + Start the stopwatch. start() removes the trigger, even if the + stopwatch was already started. + */ void start(); - // setTrigger() stops the clock like stop() except there's an - // implicit start() the next time (non-const) getTime() is called. - // this is useful when you want the clock to start the first time - // you check it. + //! Stop the timer and set the trigger + /*! + setTrigger() stops the clock like stop() except there's an + implicit start() the next time (non-const) getTime() is called. + This is useful when you want the clock to start the first time + you check it. + */ void setTrigger(); - // return the time since the last reset() (or call reset() and - // return zero if the trigger is set). + //! Get elapsed time + /*! + Returns the time since the last reset() (or calls reset() and + returns zero if the trigger is set). + */ double getTime(); + //! Same as getTime() operator double(); + //@} + //! @name accessors + //@{ - // accessors - - // returns true if the watch is stopped + //! Check if timer is stopped + /*! + Returns true if the stopwatch is stopped. + */ bool isStopped() const; - // return the time since the last reset(). these cannot trigger - // the clock to start so if the trigger is set it's as if it wasn't. + // return the time since the last reset(). + //! Get elapsed time + /*! + Returns the time since the last reset(). This cannot trigger the + stopwatch to start and will not clear the trigger. + */ double getTime() const; + //! Same as getTime() const operator double() const; + //@} private: double getClock() const; diff --git a/base/CString.h b/base/CString.h index 56c3a690..0f74cba5 100644 --- a/base/CString.h +++ b/base/CString.h @@ -6,45 +6,83 @@ #include #include "stdpost.h" +// use standard C++ string class for our string class typedef std::string CString; +//! String utilities +/*! +This class provides various functions for string manipulation. +*/ class CStringUtil { public: - // format a string using positional arguments. fmt has literal - // characters and conversion specifications introduced by `%': - // %% literal `%' - // %{n} positional element n, n a positive integer, {} are literal - // all arguments in the variable list are const char*. positional - // elements are indexed from 1. + //! Format positional arguments + /*! + Format a string using positional arguments. fmt has literal + characters and conversion specifications introduced by `\%': + - \c\%\% -- literal `\%' + - \c\%{n} -- positional element n, n a positive integer, {} are literal + + All arguments in the variable list are const char*. Positional + elements are indexed from 1. + */ static CString format(const char* fmt, ...); + + //! Format positional arguments + /*! + Same as format() except takes va_list. + */ static CString vformat(const char* fmt, va_list); - // print a string using printf-style formatting + //! Print a string using printf-style formatting + /*! + Equivalent to printf() except the result is returned as a CString. + */ static CString print(const char* fmt, ...); + + //! Print a string using printf-style formatting + /*! + Same as print() except takes va_list. + */ static CString vprint(const char* fmt, va_list); - // like print but print into a given buffer. if the resulting string - // will not fit into the buffer then a new buffer is allocated and - // returned, otherwise the input buffer is returned. the caller must - // delete[] the returned buffer if is not the passed-in buffer. - // - // prefix and suffix must be >= 0. exactly prefix characters and - // at least suffix characters are available in the buffer before - // and after the printed string, respectively. bufferLength is the - // length of buffer and should not be adjusted by the caller to - // account for prefix or suffix. + //! Print a string using printf-style formatting into a buffer + /*! + This is like print but print into a given buffer. If the resulting + string will not fit into \c buffer then a new buffer is allocated and + returned, otherwise \c buffer is returned. the caller must delete[] + the returned memory if is not \c buffer. + + \c prefix and \c suffix must be >= 0. Exactly \c prefix characters and + at least \c suffix characters are available in the buffer before + and after the printed string, respectively. \c bufferLength is the + length of buffer and should not be adjusted by the caller to + account for \c prefix or \c suffix. + */ static char* vsprint(char* buffer, int bufferLength, int prefix, int suffix, const char* fmt, va_list); + //! Case-insensitive comparisons + /*! + This class provides case-insensitve comparison functions. + */ class CaselessCmp { public: - bool operator()(const CString&, const CString&) const; - static bool less(const CString&, const CString&); - static bool equal(const CString&, const CString&); - static bool cmpLess(const CString::value_type&, - const CString::value_type&); - static bool cmpEqual(const CString::value_type&, - const CString::value_type&); + //! Same as less() + bool operator()(const CString& a, const CString& b) const; + + //! Returns true iff \c a is lexicographically less than \c b + static bool less(const CString& a, const CString& b); + + //! Returns true iff \c a is lexicographically equal to \c b + static bool equal(const CString& a, const CString& b); + + //! Returns true iff \c a is lexicographically less than \c b + static bool cmpLess(const CString::value_type& a, + const CString::value_type& b); + + //! Returns true iff \c a is lexicographically equal to \c b + static bool cmpEqual(const CString::value_type& a, + const CString::value_type& b); }; }; diff --git a/base/CUnicode.h b/base/CUnicode.h index dc2f4df2..aa6d07c3 100644 --- a/base/CUnicode.h +++ b/base/CUnicode.h @@ -5,33 +5,100 @@ #include "BasicTypes.h" #include +//! Unicode utility functions +/*! +This class provides functions for converting between various Unicode +encodings and the current locale encoding. +*/ class CUnicode { public: - // returns true iff the string contains a valid sequence of UTF-8 - // encoded characters. + //! @name accessors + //@{ + + //! Test UTF-8 string for validity + /*! + Returns true iff the string contains a valid sequence of UTF-8 + encoded characters. + */ static bool isUTF8(const CString&); - // convert from UTF-8 encoding to other encodings. if errors is - // not NULL then it gets true if any characters could not be - // encoded in the target encoding and false otherwise. note - // that decoding errors do not set errors to error. UTF8ToText() - // converts to the current locale's (multibyte) encoding. + //! Convert from UTF-8 to UCS-2 encoding + /*! + Convert from UTF-8 to UCS-2. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UCS-2. + Decoding errors do not set *errors. + */ static CString UTF8ToUCS2(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to UCS-4 encoding + /*! + Convert from UTF-8 to UCS-4. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UCS-4. + Decoding errors do not set *errors. + */ static CString UTF8ToUCS4(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to UTF-16 encoding + /*! + Convert from UTF-8 to UTF-16. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UTF-16. + Decoding errors do not set *errors. + */ static CString UTF8ToUTF16(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to UTF-32 encoding + /*! + Convert from UTF-8 to UTF-32. If errors is not NULL then *errors + is set to true iff any character could not be encoded in UTF-32. + Decoding errors do not set *errors. + */ static CString UTF8ToUTF32(const CString&, bool* errors = NULL); + + //! Convert from UTF-8 to the current locale encoding + /*! + Convert from UTF-8 to the current locale encoding. If errors is not + NULL then *errors is set to true iff any character could not be encoded. + Decoding errors do not set *errors. + */ static CString UTF8ToText(const CString&, bool* errors = NULL); - // convert from some encoding to UTF-8. if errors is not NULL - // then it gets true if any characters could not be decoded and - // false otherwise. textToUTF8() converts from the current - // locale's (multibyte) encoding. + //! Convert from UCS-2 to UTF-8 + /*! + Convert from UCS-2 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ static CString UCS2ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from UCS-4 to UTF-8 + /*! + Convert from UCS-4 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ static CString UCS4ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from UTF-16 to UTF-8 + /*! + Convert from UTF-16 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ static CString UTF16ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from UTF-32 to UTF-8 + /*! + Convert from UTF-32 to UTF-8. If errors is not NULL then *errors is + set to true iff any character could not be decoded. + */ static CString UTF32ToUTF8(const CString&, bool* errors = NULL); + + //! Convert from the current locale encoding to UTF-8 + /*! + Convert from the current locale encoding to UTF-8. If errors is not + NULL then *errors is set to true iff any character could not be decoded. + */ static CString textToUTF8(const CString&, bool* errors = NULL); + //@} + private: // convert UTF8 to wchar_t string (using whatever encoding is native // to the platform). caller must delete[] the returned string. the diff --git a/base/IInterface.h b/base/IInterface.h index 52b4bde4..4f85e1be 100644 --- a/base/IInterface.h +++ b/base/IInterface.h @@ -3,8 +3,14 @@ #include "common.h" +//! Base class of interfaces +/*! +This is the base class of all interface classes. An interface class has +only pure virtual methods. +*/ class IInterface { public: + //! Interface destructor does nothing virtual ~IInterface() { } }; diff --git a/base/IJob.h b/base/IJob.h index f13a359c..b83c0d80 100644 --- a/base/IJob.h +++ b/base/IJob.h @@ -3,8 +3,13 @@ #include "IInterface.h" +//! Job interface +/*! +A job is an interface for executing some function. +*/ class IJob : public IInterface { public: + //! Run the job virtual void run() = 0; }; diff --git a/base/TMethodJob.h b/base/TMethodJob.h index 950b95c8..ef63e22e 100644 --- a/base/TMethodJob.h +++ b/base/TMethodJob.h @@ -3,9 +3,14 @@ #include "IJob.h" +//! Use a function as a job +/*! +A job class that invokes a member function. +*/ template class TMethodJob : public IJob { public: + //! run() invokes \c object->method(arg) TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL); virtual ~TMethodJob(); diff --git a/base/XBase.h b/base/XBase.h index 1e4c208c..a35a33be 100644 --- a/base/XBase.h +++ b/base/XBase.h @@ -6,9 +6,15 @@ #include #include "stdpost.h" +//! Exception base class +/*! +This is the base class of most exception types. +*/ class XBase : public std::exception { public: + //! Use getWhat() as the result of what() XBase(); + //! Use \c msg as the result of what() XBase(const CString& msg); virtual ~XBase(); @@ -16,10 +22,15 @@ public: virtual const char* what() const; protected: - // returns a human readable string describing the exception + //! Get a human readable string describing the exception virtual CString getWhat() const throw() = 0; - // look up a message and format it + //! Format a string + /*! + Looks up a message format using \c id, using \c defaultFormat if + no format can be found, then replaces positional parameters in + the format string and returns the result. + */ virtual CString format(const char* id, const char* defaultFormat, ...) const throw(); @@ -27,18 +38,29 @@ private: mutable CString m_what; }; +//! Mix-in for handling \c errno +/*! +This mix-in class for exception classes provides storage and query of +\c errno. +*/ class MXErrno { public: + //! Save \c errno as the error code MXErrno(); - MXErrno(int); + //! Save \c err as the error code + MXErrno(int err); - // manipulators - - // accessors + //! @name accessors + //@{ + //! Get the error code int getErrno() const; + + //! Get the human readable string for the error code const char* getErrstr() const; + //@} + private: int m_errno; }; diff --git a/base/common.h b/base/common.h index 00b9fd87..a03fd9c1 100644 --- a/base/common.h +++ b/base/common.h @@ -1,6 +1,8 @@ #ifndef COMMON_H #define COMMON_H +// this file should be included, directly or indirectly by every other. + #if HAVE_CONFIG_H # include "config.h" #endif From 24d54fca53a61504b3b0072a78b00e18c9daac3b Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Jul 2002 13:34:19 +0000 Subject: [PATCH 279/807] Added doxygen comments for all relevant headers in mt. --- mt/CCondVar.h | 135 +++++++++++++++++++------ mt/CLock.h | 10 ++ mt/CMutex.h | 47 +++++++-- mt/CThread.h | 252 ++++++++++++++++++++++++++++++++-------------- mt/CThreadRep.h | 1 + mt/CTimerThread.h | 15 ++- mt/XThread.h | 28 ++++-- 7 files changed, 364 insertions(+), 124 deletions(-) diff --git a/mt/CCondVar.h b/mt/CCondVar.h index 1cf9f491..af9d88dd 100644 --- a/mt/CCondVar.h +++ b/mt/CCondVar.h @@ -6,44 +6,97 @@ class CStopwatch; +//! Generic condition variable +/*! +This class provides functionality common to all condition variables +but doesn't provide the actual variable storage. A condition variable +is a multiprocessing primitive that can be waited on. Every condition +variable has an associated mutex. +*/ class CCondVarBase { public: - // mutex must be supplied. all condition variables have an - // associated mutex. + /*! + \c mutex must not be NULL. All condition variables have an + associated mutex. The mutex needn't be unique to one condition + variable. + */ CCondVarBase(CMutex* mutex); ~CCondVarBase(); - // manipulators + //! @name manipulators + //@{ - // lock/unlock the mutex. see CMutex. + //! Lock the condition variable's mutex + /*! + Lock the condition variable's mutex. The condition variable should + be locked before reading or writing it. It must be locked for a + call to wait(). Locks are not recursive; locking a locked mutex + will deadlock the thread. + */ void lock() const; + + //! Unlock the condition variable's mutex void unlock() const; - // signal the condition. Signal() wakes one waiting thread. - // Broadcast() wakes all waiting threads. + //! Signal the condition variable + /*! + Wake up one waiting thread, if there are any. Which thread gets + woken is undefined. + */ void signal(); + + //! Signal the condition variable + /*! + Wake up all waiting threads, if any. + */ void broadcast(); - // accessors + //@} + //! @name accessors + //@{ - // wait on the condition. if timeout < 0 then wait until signalled, - // otherwise up to timeout seconds or until signalled, whichever - // comes first. since clients normally wait on condition variables - // in a loop, clients can provide a CStopwatch that acts as the - // timeout clock. using it, clients don't have to recalculate the - // timeout on each iteration. passing a stopwatch with a negative - // timeout is pointless but permitted. - // - // returns true if the object was signalled during the wait, false - // otherwise. - // - // (cancellation point) + //! Wait on the condition variable + /*! + Wait on the condition variable. If \c timeout < 0 then wait until + signalled, otherwise up to \c timeout seconds or until signalled, + whichever comes first. Returns true if the object was signalled + during the wait, false otherwise. + + The proper way to wait for a condition is: + \code + cv.lock(); + while (cv-expr) { + cv.wait(); + } + cv.unlock(); + \endcode + where \c cv-expr involves the value of \c cv and is false when the + condition is satisfied. + + (cancellation point) + */ bool wait(double timeout = -1.0) const; - bool wait(CStopwatch&, double timeout) const; - // get the mutex passed to the c'tor + //! Wait on the condition variable + /*! + Same as \c wait(double) but use \c timer to compare against \timeout. + Since clients normally wait on condition variables in a loop, clients + can use this to avoid recalculating \c timeout on each iteration. + Passing a stopwatch with a negative \c timeout is pointless (it will + never time out) but permitted. + + (cancellation point) + */ + bool wait(CStopwatch& timer, double timeout) const; + + //! Get the mutex + /*! + Get the mutex passed to the c'tor. + */ CMutex* getMutex() const; + //@} + private: void init(); void fini(); @@ -63,27 +116,49 @@ private: #endif }; +//! Condition variable +/*! +A condition variable with storage for type \c T. +*/ template class CCondVar : public CCondVarBase { public: - CCondVar(CMutex* mutex, const T&); + //! Initialize using \c value + CCondVar(CMutex* mutex, const T& value); + //! Initialize using another condition variable's value CCondVar(const CCondVar&); ~CCondVar(); - // manipulators + //! @name manipulators + //@{ - // assigns the value of the variable - CCondVar& operator=(const CCondVar&); + //! Assigns the value of \c cv to this + /*! + Set the variable's value. The condition variable should be locked + before calling this method. + */ + CCondVar& operator=(const CCondVar& cv); - // assign the value - CCondVar& operator=(const T&); + //! Assigns \c value to this + /*! + Set the variable's value. The condition variable should be locked + before calling this method. + */ + CCondVar& operator=(const T& v); - // accessors + //@} + //! @name accessors + //@{ - // get the const value. this object should be locked before - // calling this method. + //! Get the variable's value + /*! + Get the variable's value. The condition variable should be locked + before calling this method. + */ operator const T&() const; + //@} + private: T m_data; }; diff --git a/mt/CLock.h b/mt/CLock.h index 2044855c..5e12f73e 100644 --- a/mt/CLock.h +++ b/mt/CLock.h @@ -4,10 +4,20 @@ class CMutex; class CCondVarBase; +//! Mutual exclusion lock utility +/*! +This class locks a mutex or condition variable in the c'tor and unlocks +it in the d'tor. It's easier and safer than manually locking and +unlocking since unlocking must usually be done no matter how a function +exits (including by unwinding due to an exception). +*/ class CLock { public: + //! Lock the mutex \c mutex CLock(const CMutex* mutex); + //! Lock the condition variable \c cv CLock(const CCondVarBase* cv); + //! Unlock the mutex or condition variable ~CLock(); private: diff --git a/mt/CMutex.h b/mt/CMutex.h index 2f6b9483..d3a87136 100644 --- a/mt/CMutex.h +++ b/mt/CMutex.h @@ -1,26 +1,59 @@ #ifndef CMUTEX_H #define CMUTEX_H -// recursive mutex class +//! Mutual exclusion +/*! +A non-recursive mutual exclusion object. Only one thread at a time can +hold a lock on a mutex. Any thread that attempts to lock a locked mutex +will block until the mutex is unlocked. At that time, if any threads are +blocked, exactly one waiting thread will acquire the lock and continue +running. A thread may not lock a mutex it already owns the lock on; if +it tries it will deadlock itself. +*/ class CMutex { public: - // copy c'tor is equivalent to default c'tor. it's here to - // allow copying of objects that have mutexes. CMutex(); + //! Equivalent to default c'tor + /*! + Copy c'tor doesn't copy anything. It just makes it possible to + copy objects that contain a mutex. + */ CMutex(const CMutex&); ~CMutex(); - // manipulators + //! @name manipulators + //@{ - // this has no effect. it's only here to allow assignment of - // objects that have mutexes. + //! Does nothing + /*! + This does nothing. It just makes it possible to assign objects + that contain a mutex. + */ CMutex& operator=(const CMutex&); - // accessors + //@} + //! @name accessors + //@{ + //! Lock the mutex + /*! + Locks the mutex, which must not have been previously locked by the + calling thread. This blocks if the mutex is already locked by another + thread. + + (cancellation point) + */ void lock() const; + + //! Unlock the mutex + /*! + Unlocks the mutex, which must have been previously locked by the + calling thread. + */ void unlock() const; + //@} + private: void init(); void fini(); diff --git a/mt/CThread.h b/mt/CThread.h index a6243707..d11626da 100644 --- a/mt/CThread.h +++ b/mt/CThread.h @@ -6,121 +6,219 @@ class IJob; class CThreadRep; +//! Thread handle +/*! +Creating a CThread creates a new context of execution (i.e. thread) that +runs simulatenously with the calling thread. A CThread is only a handle +to a thread; deleting a CThread does not cancel or destroy the thread it +refers to and multiple CThread objects can refer to the same thread. + +Threads can terminate themselves but cannot be forced to terminate by +other threads. However, other threads can signal a thread to terminate +itself by cancelling it. And a thread can wait (block) on another thread +to terminate. + +Most functions that can block for an arbitrary time are cancellation +points. A cancellation point is a function that can be interrupted by +a request to cancel the thread. Cancellation points are noted in the +documentation. +*/ // note -- do not derive from this class class CThread { public: - // create and start a new thread executing the job. - // the user data can be retrieved with getUserData(). - CThread(IJob* adopted, void* userData = 0); + //! Run \c adoptedJob in a new thread + /*! + Create and start a new thread executing the \c adoptedJob. The + user data can be retrieved with getUserData(). The new thread + takes ownership of \c adoptedJob and will delete it. + */ + CThread(IJob* adoptedJob, void* userData = 0); - // make a new thread object that refers to an existing thread. - // this does *not* start a new thread. + //! Duplicate a thread handle + /*! + Make a new thread object that refers to an existing thread. + This does \b not start a new thread. + */ CThread(const CThread&); - // release thread. this does not terminate the thread. a thread - // will keep running until the job completes or calls exit(). + //! Release a thread handle + /*! + Release a thread handle. This does not terminate the thread. A thread + will keep running until the job completes or calls exit() or allows + itself to be cancelled. + */ ~CThread(); - // manipulators + //! @name manipulators + //@{ - // assign thread. this has no effect on the threads. it simply - // makes this thread object refer to another thread. it does *not* - // start a new thread. + //! Assign thread handle + /*! + Assign a thread handle. This has no effect on the threads, it simply + makes this thread object refer to another thread. It does \b not + start a new thread. + */ CThread& operator=(const CThread&); - // initialize the thread library. this must be called before - // any other thread methods or creating a thread object. it - // is harmless to call init() multiple times. + //! Initialize the thread library + /*! + Initialize the thread library. This \b must be called before + any other thread methods or creating a thread object. It is + harmless to call init() multiple times. + */ static void init(); - // the calling thread sleeps for the given number of seconds. if - // timeout <= 0.0 then the call returns immediately. if timeout - // == 0.0 then the calling thread yields the CPU. - // (cancellation point) + //! Sleep + /*! + Blocks the calling thread for \c timeout seconds. If + \c timeout < 0.0 then the call returns immediately. If \c timeout + == 0.0 then the calling thread yields the CPU. + + (cancellation point) + */ static void sleep(double timeout); - // terminate the calling thread. this function does not return but - // the stack is unwound and automatic objects are destroyed, as if - // exit() threw an exception (which is, in fact, what it does). the - // argument is saved as the result returned by getResult(). if you - // have a catch(...) block then you should add the following before - // it to avoid catching the exit: catch(CThreadExit&) { throw; } + //! Terminate the calling thread + /*! + Terminate the calling thread. This function does not return but + the stack is unwound and automatic objects are destroyed, as if + exit() threw an exception (which is, in fact, what it does). The + argument is saved as the result returned by getResult(). If you + have \c catch(...) blocks then you should add the following before + each to avoid catching the exit: + \code + catch(CThreadExit&) { throw; } + \endcode + or add the \c RETHROW_XTHREAD macro to the \c catch(...) block. + */ static void exit(void*); - // enable/disable cancellation. default is enabled. this is not - // a cancellation point so if you enabled cancellation and want to - // allow immediate cancellation you need to call testCancel(). - // return value is the previous state. + //! Enable or disable cancellation + /*! + Enable or disable cancellation. The default is enabled. This is not + a cancellation point so if you just enabled cancellation and want to + allow immediate cancellation you need to call testCancel(). + Returns the previous state. + */ static bool enableCancel(bool); - // cancel the thread. cancel() never waits for the thread to - // terminate; it just posts the cancel and returns. a thread will - // terminate when it enters a cancellation point with cancellation - // enabled. if cancellation is disabled then the cancel is - // remembered but not acted on until the first call to a - // cancellation point after cancellation is enabled. - // - // a cancellation point is a function that can act on cancellation. - // a cancellation point does not return if there's a cancel pending. - // instead, it unwinds the stack and destroys automatic objects, as - // if cancel() threw an exception (which is, in fact, what it does). - // threads must take care to clean up and release any resources they - // may have, especially mutexes. they can catch (XThreadCancel) to - // do that then rethrow the exception or they can let it happen - // automatically by doing clean up in the d'tors of automatic - // objects. clients are strongly encouraged to do the latter. - // during cancellation, further cancel() calls are ignored (i.e. - // a thread cannot be interrupted by a cancel during cancellation). - // - // clients that catch (XThreadCancel) must always rethrow the - // exception. clients that catch(...) must either rethrow the - // exception or include a catch (XThreadCancel) handler that - // rethrows. + //! Cancel thread + /*! + Cancel the thread. cancel() never waits for the thread to + terminate; it just posts the cancel and returns. A thread will + terminate when it enters a cancellation point with cancellation + enabled. If cancellation is disabled then the cancel is + remembered but not acted on until the first call to a + cancellation point after cancellation is enabled. + + A cancellation point is a function that can act on cancellation. + A cancellation point does not return if there's a cancel pending. + Instead, it unwinds the stack and destroys automatic objects, as + if cancel() threw an exception (which is, in fact, what it does). + Threads must take care to unlock and clean up any resources they + may have, especially mutexes. They can \c catch(XThreadCancel) to + do that then rethrow the exception or they can let it happen + automatically by doing clean up in the d'tors of automatic + objects (like CLock). Clients are strongly encouraged to do the latter. + During cancellation, further cancel() calls are ignored (i.e. + a thread cannot be interrupted by a cancel during cancellation). + + Clients that \c catch(XThreadCancel) must always rethrow the + exception. Clients that \c catch(...) must either rethrow the + exception or include a \c catch(XThreadCancel) handler that + rethrows. The \c RETHROW_XTHREAD macro may be useful for that. + */ void cancel(); - // change the priority of the thread. normal priority is 0, 1 is - // the next lower, etc. -1 is the next higher, etc. but boosting - // the priority may not be available. + //! Change thread priority + /*! + Change the priority of the thread. Normal priority is 0, 1 is + the next lower, etc. -1 is the next higher, etc. but boosting + the priority may not be permitted and will be silenty ignored. + */ void setPriority(int n); - // accessors + //@} + //! @name accessors + //@{ - // return a thread object representing the calling thread + //! Get current thread's handle + /*! + Return a CThread object representing the calling thread. + */ static CThread getCurrentThread(); - // testCancel() does nothing but is a cancellation point. call - // this to make a function itself a cancellation point. - // (cancellation point) + //! Test for cancellation + /*! + testCancel() does nothing but is a cancellation point. Call + this to make a function itself a cancellation point. If the + thread was cancelled and cancellation is enabled this will + cause the thread to unwind the stack and terminate. + + (cancellation point) + */ static void testCancel(); - // get the user data passed to the constructor for this thread. + //! Get the thread user data + /*! + Gets the user data passed to the c'tor that created this thread. + */ void* getUserData(); - // waits for the thread to terminate (by exit() or cancel() or - // by returning from the thread job). returns immediately if - // the thread has already terminated. returns immediately with - // false if called by a thread on itself. returns false on - // timeout (or error) and true on success. - // (cancellation point) + //! Wait for thread to terminate + /*! + Waits for the thread to terminate (by exit() or cancel() or + by returning from the thread job) for up to \c timeout seconds, + returning true if the thread terminated and false otherwise. + This returns immediately with false if called by a thread on + itself and immediately with true if the thread has already + terminated. This will wait forever if \c timeout < 0.0. + + (cancellation point) + */ bool wait(double timeout = -1.0) const; #if WINDOWS_LIKE - // wait for a message in the queue. returns true if a message - // is available. - // (cancellation point) + //! Wait for an event (win32) + /*! + Wait for the message queue to contain a message for up to \c timeout + seconds. This returns immediately if any message is available + (including messages that were already in the queue during the last + call to \c GetMessage() or \c PeekMessage() or waitForEvent(). + Returns true iff a message is available. This will wait forever + if \c timeout < 0.0. + + This method is available under win32 only. + + (cancellation point) + */ static bool waitForEvent(double timeout = -1.0); #endif - // get the exit result. does an implicit wait(). returns NULL - // immediately if called by a thread on itself. returns NULL for - // threads that were cancelled. - // (cancellation point) + //! Get the exit result + /*! + Returns the exit result. This does an implicit wait(). It returns + NULL immediately if called by a thread on itself or on a thread that + was cancelled. + + (cancellation point) + */ void* getResult() const; - // compare threads for (in)equality + //! Compare thread handles + /*! + Returns true if two CThread objects refer to the same thread. + */ bool operator==(const CThread&) const; + + //! Compare thread handles + /*! + Returns true if two CThread objects do not refer to the same thread. + */ bool operator!=(const CThread&) const; + //@} + private: CThread(CThreadRep*); @@ -128,7 +226,11 @@ private: CThreadRep* m_rep; }; -// disables cancellation in the c'tor and enables it in the d'tor. +//! Disable cancellation utility +/*! +This class disables cancellation for the current thread in the c'tor +and enables it in the d'tor. +*/ class CThreadMaskCancel { public: CThreadMaskCancel(); diff --git a/mt/CThreadRep.h b/mt/CThreadRep.h index 8869492c..4177f50a 100644 --- a/mt/CThreadRep.h +++ b/mt/CThreadRep.h @@ -13,6 +13,7 @@ class CMutex; class IJob; +//! Internal thread class; do not use directly class CThreadRep { public: CThreadRep(IJob*, void* userData); diff --git a/mt/CTimerThread.h b/mt/CTimerThread.h index 5605fadc..722ecd21 100644 --- a/mt/CTimerThread.h +++ b/mt/CTimerThread.h @@ -3,12 +3,21 @@ class CThread; +//! A timer thread +/*! +An object of this class cancels the thread that called the c'tor unless +the object is destroyed before a given timeout. +*/ class CTimerThread { public: - // cancels the calling thread after timeout seconds unless destroyed - // before then. if timeout is less than zero then it never times - // out and is a no-op. + //! Cancel calling thread after \c timeout seconds + /*! + Cancels the calling thread after \c timeout seconds unless destroyed + before then. If \c timeout is less than zero then it never times + out and this is a no-op. + */ CTimerThread(double timeout); + //! Cancel the timer thread ~CTimerThread(); private: diff --git a/mt/XThread.h b/mt/XThread.h index 18c22f09..3a5e59e1 100644 --- a/mt/XThread.h +++ b/mt/XThread.h @@ -1,14 +1,18 @@ #ifndef XTHREAD_H #define XTHREAD_H -// generic thread exception +//! Generic thread exception class XThread { }; -// thrown by CThread::Exit() to exit a thread. clients of CThread -// must not throw this type but must rethrow it if caught (by -// XThreadExit, XThread, or ...). +//! Thread exception to exit +/*! +Thrown by CThread::exit() to exit a thread. Clients of CThread +must not throw this type but must rethrow it if caught (by +XThreadExit, XThread, or ...). +*/ class XThreadExit : public XThread { public: + //! \c result is the result of the thread XThreadExit(void* result) : m_result(result) { } ~XThreadExit() { } @@ -16,13 +20,19 @@ public: void* m_result; }; -// thrown to cancel a thread. clients must not throw this type, but -// must rethrow it if caught (by XThreadCancel, XThread, or ...). +//! Thread exception to cancel +/*! +Thrown to cancel a thread. Clients must not throw this type, but +must rethrow it if caught (by XThreadCancel, XThread, or ...). +*/ class XThreadCancel : public XThread { }; -// convenience macro to rethrow an XThread exception but ignore other -// exceptions. put this in your catch (...) handler after necessary -// cleanup but before leaving or returning from the handler. +/*! +\def RETHROW_XTHREAD +Convenience macro to rethrow an XThread exception but ignore other +exceptions. Put this in your catch (...) handler after necessary +cleanup but before leaving or returning from the handler. +*/ #define RETHROW_XTHREAD \ try { throw; } catch (XThread&) { throw; } catch (...) { } From b8ce70d0f074fe06bc33914745c957472f8d51b4 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Jul 2002 17:25:13 +0000 Subject: [PATCH 280/807] Added doxygen comments for all relevant headers in io. --- io/CBufferedInputStream.cpp | 21 +++++++------ io/CBufferedInputStream.h | 59 +++++++++++++++++++++++++++--------- io/CBufferedOutputStream.cpp | 11 ++++--- io/CBufferedOutputStream.h | 49 ++++++++++++++++++++++++------ io/CInputStreamFilter.h | 22 ++++++++++---- io/COutputStreamFilter.h | 20 +++++++++--- io/CStreamBuffer.h | 39 +++++++++++++++++++----- io/IInputStream.h | 50 +++++++++++++++++++++--------- io/IOutputStream.h | 33 ++++++++++++++++---- io/XIO.h | 15 +++++++++ 10 files changed, 242 insertions(+), 77 deletions(-) diff --git a/io/CBufferedInputStream.cpp b/io/CBufferedInputStream.cpp index 06f553ea..c4474fd5 100644 --- a/io/CBufferedInputStream.cpp +++ b/io/CBufferedInputStream.cpp @@ -11,10 +11,11 @@ // CBufferedInputStream // -CBufferedInputStream::CBufferedInputStream(CMutex* mutex, IJob* closeCB) : +CBufferedInputStream::CBufferedInputStream( + CMutex* mutex, IJob* adoptedCloseCB) : m_mutex(mutex), m_empty(mutex, true), - m_closeCB(closeCB), + m_closeCB(adoptedCloseCB), m_closed(false), m_hungup(false) { @@ -27,10 +28,10 @@ CBufferedInputStream::~CBufferedInputStream() } void -CBufferedInputStream::write(const void* data, UInt32 n) +CBufferedInputStream::write(const void* buffer, UInt32 n) { if (!m_hungup && n > 0) { - m_buffer.write(data, n); + m_buffer.write(buffer, n); m_empty = (m_buffer.getSize() == 0); m_empty.broadcast(); } @@ -44,7 +45,7 @@ CBufferedInputStream::hangup() } UInt32 -CBufferedInputStream::readNoLock(void* dst, UInt32 n, double timeout) +CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout) { if (m_closed) { throw XIOClosed(); @@ -65,8 +66,8 @@ CBufferedInputStream::readNoLock(void* dst, UInt32 n, double timeout) n = count; } if (n > 0) { - if (dst != NULL) { - memcpy(dst, m_buffer.peek(n), n); + if (buffer != NULL) { + memcpy(buffer, m_buffer.peek(n), n); } m_buffer.pop(n); } @@ -97,16 +98,16 @@ CBufferedInputStream::close() m_hungup = true; m_buffer.pop(m_buffer.getSize()); m_empty.broadcast(); - if (m_closeCB) { + if (m_closeCB != NULL) { m_closeCB->run(); } } UInt32 -CBufferedInputStream::read(void* dst, UInt32 n, double timeout) +CBufferedInputStream::read(void* buffer, UInt32 n, double timeout) { CLock lock(m_mutex); - return readNoLock(dst, n, timeout); + return readNoLock(buffer, n, timeout); } UInt32 diff --git a/io/CBufferedInputStream.h b/io/CBufferedInputStream.h index 22ded242..94055155 100644 --- a/io/CBufferedInputStream.h +++ b/io/CBufferedInputStream.h @@ -8,35 +8,66 @@ class CMutex; class IJob; +//! Memory buffer input stream +/*! +This class provides an input stream that reads from a memory buffer. +It also provides a means for the owner to ensure thread safe access. +Typically, an owner object will make this object visible to clients +that need access to an IInputStream while using the CBufferedInputStream +methods to write data to the stream. +*/ class CBufferedInputStream : public IInputStream { public: - CBufferedInputStream(CMutex*, IJob* adoptedCloseCB); + /*! + The \c mutex must not be NULL and will be used to ensure thread + safe access. If \c adoptedCloseCB is not NULL it will be called + when close() is called, allowing the creator to detect the close. + */ + CBufferedInputStream(CMutex* mutex, IJob* adoptedCloseCB); ~CBufferedInputStream(); - // the caller is expected to lock the mutex before calling - // methods unless otherwise noted. + //! @name manipulators + //@{ - // manipulators + //! Write data to stream + /*! + Write \c n bytes from \c buffer to the stream. The mutex must + be locked before calling this. + */ + void write(const void* buffer, UInt32 n); - // write() appends n bytes to the buffer - void write(const void*, UInt32 n); - - // causes read() to always return immediately. if there is no - // more data then it returns 0. further writes are discarded. + //! Hangup stream + /*! + Causes read() to always return immediately. If there is no + more data to read then it returns 0. Further writes are discarded. + The mutex must be locked before calling this. + */ void hangup(); - // same as read() but caller must lock the mutex - UInt32 readNoLock(void*, UInt32 count, double timeout); + //! Read from stream + /*! + This is the same as read() but the mutex must be locked before + calling this. + */ + UInt32 readNoLock(void*, UInt32 n, double timeout); - // accessors + //@} + //! @name accessors + //@{ - // same as getSize() but caller must lock the mutex + //! Get remaining size of stream + /*! + This is the same as getSize() but the mutex must be locked before + calling this. + */ UInt32 getSizeNoLock() const; + //@} + // IInputStream overrides // these all lock the mutex for their duration virtual void close(); - virtual UInt32 read(void*, UInt32 count, double timeout); + virtual UInt32 read(void*, UInt32 n, double timeout); virtual UInt32 getSize() const; private: diff --git a/io/CBufferedOutputStream.cpp b/io/CBufferedOutputStream.cpp index 64c61e33..fe03e558 100644 --- a/io/CBufferedOutputStream.cpp +++ b/io/CBufferedOutputStream.cpp @@ -9,9 +9,10 @@ // CBufferedOutputStream // -CBufferedOutputStream::CBufferedOutputStream(CMutex* mutex, IJob* closeCB) : +CBufferedOutputStream::CBufferedOutputStream( + CMutex* mutex, IJob* adoptedCloseCB) : m_mutex(mutex), - m_closeCB(closeCB), + m_closeCB(adoptedCloseCB), m_empty(mutex, true), m_closed(false) { @@ -54,20 +55,20 @@ CBufferedOutputStream::close() m_closed = true; m_buffer.pop(m_buffer.getSize()); - if (m_closeCB) { + if (m_closeCB != NULL) { m_closeCB->run(); } } UInt32 -CBufferedOutputStream::write(const void* data, UInt32 n) +CBufferedOutputStream::write(const void* buffer, UInt32 n) { CLock lock(m_mutex); if (m_closed) { throw XIOClosed(); } - m_buffer.write(data, n); + m_buffer.write(buffer, n); return n; } diff --git a/io/CBufferedOutputStream.h b/io/CBufferedOutputStream.h index 6f0ba6a1..eed8f499 100644 --- a/io/CBufferedOutputStream.h +++ b/io/CBufferedOutputStream.h @@ -8,30 +8,59 @@ class CMutex; class IJob; +//! Memory buffer output stream +/*! +This class provides an output stream that writes to a memory buffer. +It also provides a means for the owner to ensure thread safe access. +Typically, an owner object will make this object visible to clients +that need access to an IOutputStream while using the CBufferedOutputStream +methods to read the data written to the stream. +*/ class CBufferedOutputStream : public IOutputStream { public: - CBufferedOutputStream(CMutex*, IJob* adoptedCloseCB); + /*! + The \c mutex must not be NULL and will be used to ensure thread + safe access. If \c adoptedCloseCB is not NULL it will be called + when close() is called, allowing the creator to detect the close. + */ + CBufferedOutputStream(CMutex* mutex, IJob* adoptedCloseCB); ~CBufferedOutputStream(); - // the caller is expected to lock the mutex before calling - // methods unless otherwise noted. + //! @name manipulators + //@{ - // manipulators - - // peek() returns a buffer of n bytes (which must be <= getSize()). - // pop() discards the next n bytes. + //! Read data without removing from buffer + /*! + Returns a buffer of \c n bytes (which must be <= getSize()). The + caller must not modify the buffer nor delete it. The mutex must + be locked before calling this. + */ const void* peek(UInt32 n); + + //! Discard data + /*! + Discards the next \c n bytes. If \c n >= getSize() then the buffer + is cleared. The mutex must be locked before calling this. + */ void pop(UInt32 n); - // accessors + //@} + //! @name accessors + //@{ - // return the number of bytes in the buffer + //! Get size of buffer + /*! + Returns the number of bytes in the buffer. The mutex must be locked + before calling this. + */ UInt32 getSize() const; + //@} + // IOutputStream overrides // these all lock the mutex for their duration virtual void close(); - virtual UInt32 write(const void*, UInt32 count); + virtual UInt32 write(const void*, UInt32 n); virtual void flush(); private: diff --git a/io/CInputStreamFilter.h b/io/CInputStreamFilter.h index bb25a261..575fb49f 100644 --- a/io/CInputStreamFilter.h +++ b/io/CInputStreamFilter.h @@ -3,21 +3,31 @@ #include "IInputStream.h" +//! A filtering input stream +/*! +This class wraps an input stream. Subclasses provide indirect access +to the stream, typically performing some filtering. +*/ class CInputStreamFilter : public IInputStream { public: - CInputStreamFilter(IInputStream*, bool adoptStream = true); + /*! + Create a wrapper around \c stream. Iff \c adoptStream is true then + this object takes ownership of the stream and will delete it in the + d'tor. + */ + CInputStreamFilter(IInputStream* stream, bool adoptStream = true); ~CInputStreamFilter(); - // manipulators - - // accessors - // IInputStream overrides virtual void close() = 0; - virtual UInt32 read(void*, UInt32 maxCount, double timeout) = 0; + virtual UInt32 read(void*, UInt32 n, double timeout) = 0; virtual UInt32 getSize() const = 0; protected: + //! Get the stream + /*! + Returns the stream passed to the c'tor. + */ IInputStream* getStream() const; private: diff --git a/io/COutputStreamFilter.h b/io/COutputStreamFilter.h index 781fa2d4..2fd51d2c 100644 --- a/io/COutputStreamFilter.h +++ b/io/COutputStreamFilter.h @@ -3,21 +3,31 @@ #include "IOutputStream.h" +//! A filtering output stream +/*! +This class wraps an output stream. Subclasses provide indirect access +to the stream, typically performing some filtering. +*/ class COutputStreamFilter : public IOutputStream { public: - COutputStreamFilter(IOutputStream*, bool adoptStream = true); + /*! + Create a wrapper around \c stream. Iff \c adoptStream is true then + this object takes ownership of the stream and will delete it in the + d'tor. + */ + COutputStreamFilter(IOutputStream* stream, bool adoptStream = true); ~COutputStreamFilter(); - // manipulators - - // accessors - // IOutputStream overrides virtual void close() = 0; virtual UInt32 write(const void*, UInt32 count) = 0; virtual void flush() = 0; protected: + //! Get the stream + /*! + Returns the stream passed to the c'tor. + */ IOutputStream* getStream() const; private: diff --git a/io/CStreamBuffer.h b/io/CStreamBuffer.h index a975e77d..90081bd1 100644 --- a/io/CStreamBuffer.h +++ b/io/CStreamBuffer.h @@ -5,26 +5,51 @@ #include "stdlist.h" #include "stdvector.h" +//! FIFO of bytes +/*! +This class maintains a FIFO (first-in, last-out) buffer of bytes. +*/ class CStreamBuffer { public: CStreamBuffer(); ~CStreamBuffer(); - // manipulators + //! @name manipulators + //@{ - // peek() returns a buffer of n bytes (which must be <= getSize()). - // pop() discards the next n bytes. + //! Read data without removing from buffer + /*! + Return a pointer to memory with the next \c n bytes in the buffer + (which must be <= getSize()). The caller must not modify the returned + memory nor delete it. + */ const void* peek(UInt32 n); + + //! Discard data + /*! + Discards the next \c n bytes. If \c n >= getSize() then the buffer + is cleared. + */ void pop(UInt32 n); - // write() appends n bytes to the buffer - void write(const void*, UInt32 n); + //! Write data to buffer + /*! + Appends \c n bytes from \c data to the buffer. + */ + void write(const void* data, UInt32 n); - // accessors + //@} + //! @name accessors + //@{ - // return the number of bytes in the buffer + //! Get size of buffer + /*! + Returns the number of bytes in the buffer. + */ UInt32 getSize() const; + //@} + private: static const UInt32 kChunkSize; diff --git a/io/IInputStream.h b/io/IInputStream.h index fc5abaa3..23d7a201 100644 --- a/io/IInputStream.h +++ b/io/IInputStream.h @@ -4,28 +4,50 @@ #include "IInterface.h" #include "BasicTypes.h" +//! Input stream interface +/*! +Defines the interface for all input streams. +*/ class IInputStream : public IInterface { public: - // manipulators + //! @name manipulators + //@{ - // close the stream + //! Close the stream + /*! + Closes the stream. Attempting to read() after close() throws + XIOClosed and getSize() always returns zero. + */ virtual void close() = 0; - // read up to maxCount bytes into buffer, return number read. - // blocks if no data is currently available. if buffer is NULL - // then the data is discarded. returns (UInt32)-1 if there's - // no data for timeout seconds; if timeout < 0 then it blocks - // until data is available. - // (cancellation point) - virtual UInt32 read(void* buffer, UInt32 maxCount, double timeout) = 0; + //! Read from stream + /*! + Read up to \c n bytes into buffer, returning the number read. + Blocks for up to \c timeout seconds if no data is available but does + not wait if any data is available, even if less than \c n bytes. + If \c timeout < 0 then it blocks indefinitely until data is available. + If \c buffer is NULL then the data is discarded. Returns (UInt32)-1 if + it times out and 0 if no data is available and the other end of the + stream has hungup. - // accessors + (cancellation point) + */ + virtual UInt32 read(void* buffer, UInt32 n, double timeout) = 0; - // get a conservative estimate of the available bytes to read - // (i.e. a number not greater than the actual number of bytes). - // some streams may not be able to determine this and will always - // return zero. + //@} + //! @name accessors + //@{ + + //! Get remaining size of stream + /*! + Returns a conservative estimate of the available bytes to read + (i.e. a number not greater than the actual number of bytes). + Some streams may not be able to determine this and will always + return zero. + */ virtual UInt32 getSize() const = 0; + + //@} }; #endif diff --git a/io/IOutputStream.h b/io/IOutputStream.h index 301ce482..478468c7 100644 --- a/io/IOutputStream.h +++ b/io/IOutputStream.h @@ -4,20 +4,41 @@ #include "IInterface.h" #include "BasicTypes.h" +//! Output stream interface +/*! +Defines the interface for all output streams. +*/ class IOutputStream : public IInterface { public: - // manipulators + //! @name manipulators + //@{ - // close the stream + //! Close the stream + /*! + Closes the stream. Attempting to write() after close() throws + XIOClosed. + */ virtual void close() = 0; - // write count bytes to stream - virtual UInt32 write(const void*, UInt32 count) = 0; + //! Write to stream + /*! + Write \c n bytes from \c buffer to the stream. If this can't + complete immeditely it will block. If cancelled, an indeterminate + amount of data may have been written. - // flush the stream + (cancellation point) + */ + virtual UInt32 write(const void* buffer, UInt32 n) = 0; + + //! Flush the stream + /*! + Waits until all buffered data has been written to the stream. + + (cancellation point) + */ virtual void flush() = 0; - // accessors + //@} }; #endif diff --git a/io/XIO.h b/io/XIO.h index 4c4e4315..f65ad3f8 100644 --- a/io/XIO.h +++ b/io/XIO.h @@ -3,26 +3,41 @@ #include "XBase.h" +//! Generic I/O exception class XIO : public XBase { }; +//! Generic I/O exception using \c errno class XIOErrno : public XIO, public MXErrno { public: XIOErrno(); XIOErrno(int); }; +//! I/O closing exception +/*! +Thrown if a stream cannot be closed. +*/ class XIOClose: public XIOErrno { protected: // XBase overrides virtual CString getWhat() const throw(); }; +//! I/O already closed exception +/*! +Thrown when attempting to close or perform I/O on an already closed. +stream. +*/ class XIOClosed : public XIO { protected: // XBase overrides virtual CString getWhat() const throw(); }; +//! I/O end of stream exception +/*! +Thrown when attempting to read beyond the end of a stream. +*/ class XIOEndOfStream : public XIO { protected: // XBase overrides From 46140549fca263267c159b03b71075e5b4142914 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Jul 2002 17:55:59 +0000 Subject: [PATCH 281/807] Added doxygen comments for all relevant headers in http. --- http/CHTTPProtocol.h | 128 ++++++++++++++++++++++++++++++++++--------- http/XHTTP.cpp | 4 +- http/XHTTP.h | 24 ++++++++ 3 files changed, 127 insertions(+), 29 deletions(-) diff --git a/http/CHTTPProtocol.h b/http/CHTTPProtocol.h index 19979ed1..7c6fd000 100644 --- a/http/CHTTPProtocol.h +++ b/http/CHTTPProtocol.h @@ -10,88 +10,162 @@ class IInputStream; class IOutputStream; +//! HTTP request type +/*! +This class encapsulates an HTTP request. +*/ class CHTTPRequest { -public: +private: typedef std::list > CHeaderList; - typedef std::map CHeaderMap; +public: + //! Iterator on headers + /*! + An iterator on the headers. Each element is a std::pair; first is + the header name as a CString, second is the header value as a CString. + */ typedef CHeaderList::const_iterator const_iterator; CHTTPRequest(); ~CHTTPRequest(); - // manipulators + //! @name manipulators + //@{ - // add a header by name. replaces existing header, if any. - // headers are sent in the order they're inserted. replacing - // a header does not change its original position in the order. + //! Insert header + /*! + Add a header by name replacing the existing header, if any. + Headers are sent in the order they're inserted. Replacing + a header does not change its original position in the order. + */ void insertHeader(const CString& name, const CString& value); - // append a header. equivalent to insertHeader() if the header - // doesn't exist, otherwise it appends a comma and the value to - // the existing header. + //! Append header + /*! + Append a header. Equivalent to insertHeader() if the header + doesn't exist, otherwise it appends a comma and the value to + the existing header. + */ void appendHeader(const CString& name, const CString& value); - // remove a header by name. does nothing if no such header. + //! Remove header + /*! + Remove a header by name. Does nothing if the header doesn't exist. + */ void eraseHeader(const CString& name); - // accessors + //@} + //! @name accessors + //@{ - // returns true iff the header exists + //! Check header existence + /*! + Returns true iff the header exists. + */ bool isHeader(const CString& name) const; - // get a header by name. returns the empty string if no such header. + //! Get header + /*! + Get a header by name. Returns the empty string if the header + doesn't exist. + */ CString getHeader(const CString& name) const; - // get iterator over all headers in the order they were added + // headers are iterated in the order they were added. + //! Get beginning header iterator const_iterator begin() const { return m_headers.begin(); } + //! Get ending header iterator const_iterator end() const { return m_headers.end(); } + //@} + public: // note -- these members are public for convenience + //! The HTTP method CString m_method; + //! The HTTP URI CString m_uri; + //! The HTTP major version number SInt32 m_majorVersion; + //! The HTTP minor version number SInt32 m_minorVersion; + //! The HTTP body, after transfer decoding CString m_body; private: + typedef std::map CHeaderMap; + CHeaderList m_headers; CHeaderMap m_headerByName; }; +//! HTTP reply type +/*! +This class encapsulates an HTTP reply. +*/ class CHTTPReply { public: + //! Header list + /*! + The type of the reply header list. Each pair is the header name + and value, respectively for first and second. + */ typedef std::vector > CHeaderList; + // note -- these members are public for convenience + //! The HTTP major version number SInt32 m_majorVersion; + //! The HTTP minor version number SInt32 m_minorVersion; + //! The HTTP status code SInt32 m_status; + //! The HTTP reason phrase CString m_reason; + //! The HTTP method CString m_method; - + //! The HTTP headers CHeaderList m_headers; - + //! The HTTP body CString m_body; }; +//! HTTP protocol utilities +/*! +This class provides utility functions for HTTP. +*/ class CHTTPProtocol { public: - // read and parse an HTTP request. result is returned in a - // CHTTPRequest which the client must delete. throws an - // XHTTP if there was a parse error. throws an XIO exception - // if there was a read error. if maxSize is greater than - // zero and the request is larger than maxSize bytes then - // throws XHTTP(413). + //! Multipart form parts + /*! + Each element is the contents of a multipart form part indexed by + it's name. + */ + typedef std::map CFormParts; + + //! Read HTTP request + /*! + Read and parse an HTTP request. The result is returned in a + CHTTPRequest which the client must delete. Throws an + XHTTP if there was a parse error. Throws an XIO exception + if there was a read error. If \c maxSize is greater than + zero and the request is larger than \c maxSize bytes then + throws XHTTP(413) (request entity too large). + */ static CHTTPRequest* readRequest(IInputStream*, UInt32 maxSize = 0); - // send an HTTP reply on the stream + //! Send HTTP response + /*! + Send an HTTP reply. The Content-Length and Date headers are set + automatically. + */ static void reply(IOutputStream*, CHTTPReply&); - // parse a multipart/form-data body into its parts. returns true - // iff the entire body was correctly parsed. + //! Parse multipart form data + /*! + Parse a multipart/form-data body into its parts. Returns true + iff the entire body was correctly parsed. + */ // FIXME -- name/value pairs insufficient to save part headers - typedef std::map CFormParts; static bool parseFormData(const CHTTPRequest&, CFormParts& parts); diff --git a/http/XHTTP.cpp b/http/XHTTP.cpp index b6535d9f..690cb4f8 100644 --- a/http/XHTTP.cpp +++ b/http/XHTTP.cpp @@ -101,9 +101,9 @@ XHTTP::getReason(SInt32 status) // XHTTPAllow // -XHTTPAllow::XHTTPAllow(const CString& allowed) : +XHTTPAllow::XHTTPAllow(const CString& allowedMethods) : XHTTP(405), - m_allowed(allowed) + m_allowed(allowedMethods) { // do nothing } diff --git a/http/XHTTP.h b/http/XHTTP.h index e098a713..384881b6 100644 --- a/http/XHTTP.h +++ b/http/XHTTP.h @@ -6,17 +6,37 @@ class CHTTPReply; +//! Generic HTTP exception class XHTTP : public XBase { public: + /*! + Use the HTTP \c statusCode as the failure reason. + */ XHTTP(SInt32 statusCode); + /*! + Use the HTTP \c statusCode as the failure reason. Use \c reasonPhrase + as the human readable reason for the failure. + */ XHTTP(SInt32 statusCode, const CString& reasonPhrase); ~XHTTP(); + //@{ + //! @name accessors + + //! Get the HTTP status code SInt32 getStatus() const; + + //! Get the reason phrase CString getReason() const; + //! Modify reply for error + /*! + Override to modify an HTTP reply to further describe the error. + */ virtual void addHeaders(CHTTPReply&) const; + //@} + protected: virtual CString getWhat() const throw(); @@ -30,6 +50,10 @@ private: class XHTTPAllow : public XHTTP { public: + /*! + \c allowedMethods is added as an `Allow' header to a reply in + addHeaders(). + */ XHTTPAllow(const CString& allowedMethods); ~XHTTPAllow(); From 4651a3f821138947c5ffd1920a25d0451967b7b9 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Jul 2002 19:06:52 +0000 Subject: [PATCH 282/807] Added doxygen comments for all relevant headers in net. --- net/CNetwork.h | 28 +++++++++++++++++-- net/CNetworkAddress.h | 62 ++++++++++++++++++++++++++++++++---------- net/CTCPListenSocket.h | 8 +++--- net/CTCPSocket.h | 8 +++--- net/IDataSocket.h | 31 +++++++++++++++++---- net/IListenSocket.h | 20 +++++++++++--- net/ISocket.h | 20 ++++++++++---- net/XNetwork.h | 31 ++++++++++++++++++++- net/XSocket.cpp | 4 +-- net/XSocket.h | 49 ++++++++++++++++++++++++++++----- 10 files changed, 213 insertions(+), 48 deletions(-) diff --git a/net/CNetwork.h b/net/CNetwork.h index 1bc2e4e2..f1d674a0 100644 --- a/net/CNetwork.h +++ b/net/CNetwork.h @@ -31,8 +31,10 @@ typedef int ssize_t; # endif #endif +//! Networking functions class CNetwork { public: + // platform dependent types #if WINDOWS_LIKE typedef SOCKET Socket; typedef struct sockaddr Address; @@ -64,20 +66,39 @@ public: }; #endif - // manipulators + //! @name manipulators + //@{ + //! Initialize network subsystem + /*! + This \b must be called before any other calls to the network subsystem. + */ static void init(); + + //! Clean up network subsystem + /*! + This should be called when the network subsystem is no longer needed + and no longer in use. + */ static void cleanup(); // byte swapping functions + //! Swap bytes to network order static UInt32 swaphtonl(UInt32 hostlong); + //! Swap bytes to network order static UInt16 swaphtons(UInt16 hostshort); + //! Swap bytes to host order static UInt32 swapntohl(UInt32 netlong); + //! Swap bytes to host order static UInt16 swapntohs(UInt16 netshort); - // constants + //@} + //! @name constants + //@{ + //! The error type static const int Error; + //! The non-socket static const Socket Null; // getsockerror() constants @@ -108,6 +129,8 @@ public: kHNone = 0 }; + //@} + // socket interface (only available after init()) static Socket (PASCAL FAR *accept)(Socket s, Address FAR *addr, AddressLength FAR *addrlen); @@ -143,6 +166,7 @@ public: // convenience functions (only available after init()) + //! Set socket to (non-)blocking operation static int (PASCAL FAR *setblocking)(CNetwork::Socket s, bool blocking); #if WINDOWS_LIKE diff --git a/net/CNetworkAddress.h b/net/CNetworkAddress.h index 1fd60fd4..ed7be4cc 100644 --- a/net/CNetworkAddress.h +++ b/net/CNetworkAddress.h @@ -5,39 +5,73 @@ #include "CString.h" #include "BasicTypes.h" +//! Network address type +/*! +This class represents a network address. +*/ class CNetworkAddress { public: - // invalid address + /*! + Constructs the invalid address + */ CNetworkAddress(); - // wildcard address and given port. port must not be zero. + /*! + Construct the wildcard address with the given port. \c port must + not be zero. + */ CNetworkAddress(UInt16 port); - // given address and port. if hostname can be parsed as numerical - // address then that's how it's used, otherwise the hostname is - // looked up. if lookup fails then it throws XSocketAddress. if - // hostname ends in ":[0-9]+" then that suffix is extracted and - // used as the port, overridding the port parameter. neither - // port may be zero. + /*! + Construct the network address for the given \c hostname and \c port. + If \c hostname can be parsed as a numerical address then that's how + it's used, otherwise the host name is looked up. If the lookup fails + then this throws XSocketAddress. If \c hostname ends in ":[0-9]+" then + that suffix is extracted and used as the port, overridding the port + parameter. Neither the extracted port or \c port may be zero. + */ CNetworkAddress(const CString& hostname, UInt16 port); ~CNetworkAddress(); - // manipulators + //! @name accessors + //@{ - // accessors - - // returns true if this is not the invalid address + //! Check address validity + /*! + Returns true if this is not the invalid address. + */ bool isValid() const; - // get the address + //! Get address + /*! + Returns the address in the platform's native network address + structure. + */ const CNetwork::Address* getAddress() const; + + //! Get address length + /*! + Returns the length of the address in the platform's native network + address structure. + */ CNetwork::AddressLength getAddressLength() const; - // get the hostname and port (as provided in the c'tor) + //! Get hostname + /*! + Returns the hostname passed to the c'tor sans the port suffix. + */ CString getHostname() const; + + //! Get port + /*! + Returns the port passed to the c'tor as a suffix to the hostname, + if that existed, otherwise as passed directly to the c'tor. + */ UInt16 getPort() const; + //@} + private: CNetwork::Address m_address; CString m_hostname; diff --git a/net/CTCPListenSocket.h b/net/CTCPListenSocket.h index d89f0049..25c3a6ab 100644 --- a/net/CTCPListenSocket.h +++ b/net/CTCPListenSocket.h @@ -4,15 +4,15 @@ #include "IListenSocket.h" #include "CNetwork.h" +//! TCP listen socket +/*! +A listen socket using TCP. +*/ class CTCPListenSocket : public IListenSocket { public: CTCPListenSocket(); ~CTCPListenSocket(); - // manipulators - - // accessors - // ISocket overrides virtual void bind(const CNetworkAddress&); virtual void close(); diff --git a/net/CTCPSocket.h b/net/CTCPSocket.h index a38789df..d30f75e6 100644 --- a/net/CTCPSocket.h +++ b/net/CTCPSocket.h @@ -11,16 +11,16 @@ class CThread; class CBufferedInputStream; class CBufferedOutputStream; +//! TCP data socket +/*! +A data socket using TCP. +*/ class CTCPSocket : public IDataSocket { public: CTCPSocket(); CTCPSocket(CNetwork::Socket); ~CTCPSocket(); - // manipulators - - // accessors - // ISocket overrides virtual void bind(const CNetworkAddress&); virtual void close(); diff --git a/net/IDataSocket.h b/net/IDataSocket.h index faf645b8..f10854b4 100644 --- a/net/IDataSocket.h +++ b/net/IDataSocket.h @@ -6,19 +6,40 @@ class IInputStream; class IOutputStream; +//! Data stream socket interface +/*! +This interface defines the methods common to all network sockets that +represent a full-duplex data stream. +*/ class IDataSocket : public ISocket { public: - // manipulators + //! @name manipulators + //@{ - // connect the socket + //! Connect socket + /*! + Attempt to connect to a remote endpoint. This waits until the + connection is established or fails. If it fails it throws an + XSocketConnect exception. + + (cancellation point) + */ virtual void connect(const CNetworkAddress&) = 0; - // get the input and output streams for the socket. closing - // these streams closes the appropriate half of the socket. + //! Get input stream + /*! + Returns the input stream for reading from the socket. Closing this + stream will shutdown the socket for reading. + */ virtual IInputStream* getInputStream() = 0; + //! Get output stream + /*! + Returns the output stream for writing to the socket. Closing this + stream will shutdown the socket for writing. + */ virtual IOutputStream* getOutputStream() = 0; - // accessors + //@} // ISocket overrides virtual void bind(const CNetworkAddress&) = 0; diff --git a/net/IListenSocket.h b/net/IListenSocket.h index 51d73cfc..4b35f249 100644 --- a/net/IListenSocket.h +++ b/net/IListenSocket.h @@ -5,14 +5,26 @@ class IDataSocket; +//! Listen socket interface +/*! +This interface defines the methods common to all network sockets that +listen for incoming connections. +*/ class IListenSocket : public ISocket { public: - // manipulators + //! @name manipulators + //@{ - // wait for a connection + //! Accept connection + /*! + Wait for and accept a connection, returning a socket representing + the full-duplex data stream. + + (cancellation point) + */ virtual IDataSocket* accept() = 0; - - // accessors + + //@} // ISocket overrides virtual void bind(const CNetworkAddress&) = 0; diff --git a/net/ISocket.h b/net/ISocket.h index 8ca8fbf0..72790960 100644 --- a/net/ISocket.h +++ b/net/ISocket.h @@ -5,18 +5,28 @@ class CNetworkAddress; +//! Generic socket interface +/*! +This interface defines the methods common to all network sockets. +*/ class ISocket : public IInterface { public: - // manipulators + //! @name manipulators + //@{ - // bind the socket to a particular address + //! Bind socket to address + /*! + Binds the socket to a particular address. + */ virtual void bind(const CNetworkAddress&) = 0; - // close the socket. this will flush the output stream if it - // hasn't been closed yet. + //! Close socket + /*! + Closes the socket. This should flush the output stream. + */ virtual void close() = 0; - // accessors + //@} }; #endif diff --git a/net/XNetwork.h b/net/XNetwork.h index ac0e5a24..aa495655 100644 --- a/net/XNetwork.h +++ b/net/XNetwork.h @@ -4,29 +4,53 @@ #include "XBase.h" #include "CString.h" +//! Generic network exception +/*! +Network exceptions are thrown when initializing the network subsystem +and not during normal network use. +*/ class XNetwork : public XBase { }; +//! Network subsystem not available exception +/*! +Thrown when the network subsystem is unavailable, typically because +the necessary shared library is unavailable. +*/ class XNetworkUnavailable : public XNetwork { protected: // XBase overrides virtual CString getWhat() const throw(); }; +//! Network subsystem failed exception +/*! +Thrown when the network subsystem cannot be initialized. +*/ class XNetworkFailed : public XNetwork { protected: // XBase overrides virtual CString getWhat() const throw(); }; +//! Network subsystem vesion unsupported exception +/*! +Thrown when the network subsystem has a version incompatible with +what's expected. +*/ class XNetworkVersion : public XNetwork { public: XNetworkVersion(int major, int minor) throw(); - // accessors + //! @name accessors + //@{ + //! Get the network subsystem's major version int getMajor() const throw(); + //! Get the network subsystem's minor version int getMinor() const throw(); + //@} + protected: // XBase overrides virtual CString getWhat() const throw(); @@ -36,6 +60,11 @@ private: int m_minor; }; +//! Network subsystem incomplete exception +/*! +Thrown when the network subsystem is missing an expected and required +function. +*/ class XNetworkFunctionUnavailable : public XNetwork { public: XNetworkFunctionUnavailable(const char* name) throw(); diff --git a/net/XSocket.cpp b/net/XSocket.cpp index 63f9fc03..d1c5f661 100644 --- a/net/XSocket.cpp +++ b/net/XSocket.cpp @@ -4,7 +4,7 @@ // XSocketAddress // -XSocketAddress::XSocketAddress(Error error, +XSocketAddress::XSocketAddress(EError error, const CString& hostname, UInt16 port) throw() : m_error(error), m_hostname(hostname), @@ -13,7 +13,7 @@ XSocketAddress::XSocketAddress(Error error, // do nothing } -XSocketAddress::Error +XSocketAddress::EError XSocketAddress::getError() const throw() { return m_error; diff --git a/net/XSocket.h b/net/XSocket.h index 354e57af..4adca77b 100644 --- a/net/XSocket.h +++ b/net/XSocket.h @@ -5,36 +5,58 @@ #include "CString.h" #include "BasicTypes.h" +//! Generic socket exception class XSocket : public XBase { }; +//! Socket bad address exception +/*! +Thrown when attempting to create an invalid network address. +*/ class XSocketAddress : public XSocket { public: - enum Error { kUnknown, kNotFound, kNoAddress, kBadPort }; + //! Failure codes + enum EError { + kUnknown, //!< Unknown error + kNotFound, //!< The hostname is unknown + kNoAddress, //!< The hostname is valid but has no IP address + kBadPort //!< The port is invalid + }; - XSocketAddress(Error, const CString& hostname, UInt16 port) throw(); + XSocketAddress(EError, const CString& hostname, UInt16 port) throw(); - // accessors + //! @name accessors + //@{ - virtual Error getError() const throw(); - virtual CString getHostname() const throw(); - virtual UInt16 getPort() const throw(); + //! Get the error code + EError getError() const throw(); + //! Get the hostname + CString getHostname() const throw(); + //! Get the port + UInt16 getPort() const throw(); + + //@} protected: // XBase overrides virtual CString getWhat() const throw(); private: - Error m_error; + EError m_error; CString m_hostname; UInt16 m_port; }; +//! Generic socket exception using \c errno class XSocketErrno : public XSocket, public MXErrno { public: XSocketErrno(); XSocketErrno(int); }; +//! Socket cannot bind address exception +/*! +Thrown when a socket cannot be bound to an address. +*/ class XSocketBind : public XSocketErrno { public: XSocketBind() { } @@ -45,12 +67,21 @@ protected: virtual CString getWhat() const throw(); }; +//! Socket address in use exception +/*! +Thrown when a socket cannot be bound to an address because the address +is already in use. +*/ class XSocketAddressInUse : public XSocketBind { public: XSocketAddressInUse() { } XSocketAddressInUse(int e) : XSocketBind(e) { } }; +//! Cannot connect socket exception +/*! +Thrown when a socket cannot connect to a remote endpoint. +*/ class XSocketConnect : public XSocketErrno { public: XSocketConnect() { } @@ -61,6 +92,10 @@ protected: virtual CString getWhat() const throw(); }; +//! Cannot create socket exception +/*! +Thrown when a socket cannot be created (by the operating system). +*/ class XSocketCreate : public XSocketErrno { public: XSocketCreate() { } From 5c4e4fdf08600652ef66c8b3cd3b0da240770a3e Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 29 Jul 2002 16:05:59 +0000 Subject: [PATCH 283/807] changed doxygen configuration. --- doc/doxygen.cfg | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg index 4e83cbcb..ceae4d8a 100644 --- a/doc/doxygen.cfg +++ b/doc/doxygen.cfg @@ -47,8 +47,7 @@ OUTPUT_LANGUAGE = English # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES -EXTRACT_ALL = YES -#XXX EXTRACT_ALL = NO +EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. @@ -519,7 +518,7 @@ TREEVIEW_WIDTH = 250 # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. -GENERATE_LATEX = YES +GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be From 50eee03f6dbd2ec8bab2e2be87cb7bff785d58c9 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 29 Jul 2002 16:06:52 +0000 Subject: [PATCH 284/807] moved try/catch block from CMSWindowsScreen to CPrimaryClient. this means CMSWindowsScreen doesn't need to include XSynergy.h. --- platform/CMSWindowsScreen.cpp | 28 ++++++++-------------------- server/CPrimaryClient.cpp | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index 45041f72..68e3f78a 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -5,7 +5,6 @@ #include "CClipboard.h" #include "IMSWindowsScreenEventHandler.h" #include "IScreenReceiver.h" -#include "XSynergy.h" #include "CThread.h" #include "CLock.h" #include "TMethodJob.h" @@ -278,15 +277,10 @@ CMSWindowsScreen::checkClipboards() // least the clipboard itself will work. HWND clipboardOwner = GetClipboardOwner(); if (m_clipboardOwner != clipboardOwner) { - try { - m_clipboardOwner = clipboardOwner; - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore + m_clipboardOwner = clipboardOwner; + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); } } } @@ -509,16 +503,10 @@ CMSWindowsScreen::onEvent(CEvent* event) // ownership, or now it's owned by nobody, which will happen if // we owned it and switched desktops because we destroy our // window to do that). - try { - m_clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore. this can happen if we receive this event - // before we've fully started up. + m_clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); } return true; diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index 236fd57c..1f53cb01 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -1,5 +1,6 @@ #include "CPrimaryClient.h" #include "IServer.h" +#include "XSynergy.h" #include "CPrimaryScreen.h" #include "CClipboard.h" #include "CLog.h" @@ -81,13 +82,23 @@ void CPrimaryClient::onInfoChanged(const CClientInfo& info) { m_info = info; - m_server->onInfoChanged(getName(), m_info); + try { + m_server->onInfoChanged(getName(), m_info); + } + catch (XBadClient&) { + // ignore + } } bool CPrimaryClient::onGrabClipboard(ClipboardID id) { - return m_server->onGrabClipboard(getName(), id, m_seqNum); + try { + return m_server->onGrabClipboard(getName(), id, m_seqNum); + } + catch (XBadClient&) { + return false; + } } void From b5a8ae11acc2135a74c823fcae378ddf421b53bc Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 29 Jul 2002 16:07:26 +0000 Subject: [PATCH 285/807] Added doxygen comments for all relevant headers in synergy. --- synergy/CClipboard.h | 52 +++++++--- synergy/CInputPacketStream.h | 8 +- synergy/COutputPacketStream.h | 9 +- synergy/CProtocolUtil.h | 58 +++++++---- synergy/CTCPSocketFactory.h | 5 +- synergy/ClipboardTypes.h | 8 +- synergy/IClient.h | 161 +++++++++++++++++++++++++------ synergy/IClipboard.h | 110 ++++++++++++++------- synergy/IPrimaryScreenReceiver.h | 33 ++++++- synergy/IScreen.h | 123 +++++++++++++++++------ synergy/IScreenEventHandler.h | 50 +++++++--- synergy/IScreenReceiver.h | 38 ++++++-- synergy/IScreenSaver.h | 47 +++++++-- synergy/IServer.h | 49 +++++++--- synergy/ISocketFactory.h | 16 ++- synergy/KeyTypes.h | 25 +++-- synergy/MouseTypes.h | 9 +- synergy/ProtocolTypes.h | 31 +++++- synergy/Version.h | 6 +- synergy/XScreen.h | 6 +- synergy/XSynergy.h | 48 ++++++--- 21 files changed, 659 insertions(+), 233 deletions(-) diff --git a/synergy/CClipboard.h b/synergy/CClipboard.h index fea2b1d2..d7bfc9f0 100644 --- a/synergy/CClipboard.h +++ b/synergy/CClipboard.h @@ -1,35 +1,59 @@ #ifndef CCLIPBOARD_H #define CCLIPBOARD_H -// -// CClipboard -- stores clipboard data in a memory buffer -// - #include "IClipboard.h" +//! Memory buffer clipboard +/*! +This class implements a clipboard that stores data in memory. +*/ class CClipboard : public IClipboard { public: CClipboard(); virtual ~CClipboard(); - // manipulators + //! @name manipulators + //@{ - // unmarshall clipboard data - void unmarshall(const CString& data, Time); + //! Unmarshall clipboard data + /*! + Extract marshalled clipboard data and store it in this clipboard. + Sets the clipboard time to \c time. + */ + void unmarshall(const CString& data, Time time); - // accessors + //@} + //! @name accessors + //@{ - // marshall clipboard data + //! Marshall clipboard data + /*! + Merge this clipboard's data into a single buffer that can be later + unmarshalled to restore the clipboard and return the buffer. + */ CString marshall() const; - // transfer all the data in one clipboard to another. the - // clipboards can be of any concrete clipboard type (and - // they don't have to be the same type). this also sets - // the timestamp to time, if provided, or the time in src. - // returns true iff the copy succeeded. + //! Copy clipboard + /*! + Transfers all the data in one clipboard to another. The + clipboards can be of any concrete clipboard type (and + they don't have to be the same type). This also sets + the destination clipboard's timestamp to source clipboard's + timestamp. Returns true iff the copy succeeded. + */ static bool copy(IClipboard* dst, const IClipboard* src); + + //! Copy clipboard + /*! + Transfers all the data in one clipboard to another. The + clipboards can be of any concrete clipboard type (and they + don't have to be the same type). This also sets the + timestamp to \c time. Returns true iff the copy succeeded. + */ static bool copy(IClipboard* dst, const IClipboard* src, Time); + //@} + // IClipboard overrides virtual bool empty(); virtual void add(EFormat, const CString& data); diff --git a/synergy/CInputPacketStream.h b/synergy/CInputPacketStream.h index c8b123d1..5e4762f9 100644 --- a/synergy/CInputPacketStream.h +++ b/synergy/CInputPacketStream.h @@ -5,15 +5,15 @@ #include "CBufferedInputStream.h" #include "CMutex.h" +//! Packetizing input stream filter +/*! +Filters an input stream to extract packet by packet. +*/ class CInputPacketStream : public CInputStreamFilter { public: CInputPacketStream(IInputStream*, bool adoptStream = true); ~CInputPacketStream(); - // manipulators - - // accessors - // IInputStream overrides virtual void close(); virtual UInt32 read(void*, UInt32 maxCount, double timeout); diff --git a/synergy/COutputPacketStream.h b/synergy/COutputPacketStream.h index 07ee40a5..dda632c4 100644 --- a/synergy/COutputPacketStream.h +++ b/synergy/COutputPacketStream.h @@ -3,15 +3,16 @@ #include "COutputStreamFilter.h" +//! Packetizing output stream filter +/*! +Filters an output stream to create packets that include message +boundaries. Each write() is considered a single packet. +*/ class COutputPacketStream : public COutputStreamFilter { public: COutputPacketStream(IOutputStream*, bool adoptStream = true); ~COutputPacketStream(); - // manipulators - - // accessors - // IOutputStream overrides virtual void close(); virtual UInt32 write(const void*, UInt32 count); diff --git a/synergy/CProtocolUtil.h b/synergy/CProtocolUtil.h index 9c633b81..e277a757 100644 --- a/synergy/CProtocolUtil.h +++ b/synergy/CProtocolUtil.h @@ -8,32 +8,43 @@ class IInputStream; class IOutputStream; +//! Synergy protocol utilities +/*! +This class provides various functions for implementing the synergy +protocol. +*/ class CProtocolUtil { public: - // write formatted binary data to a stream. fmt consists of - // regular characters and format specifiers. format specifiers - // begin with %. all characters not part of a format specifier - // are regular and are transmitted unchanged. - // - // format specifiers are: - // %% -- writes % - // %1i -- converts integer argument to 1 byte integer - // %2i -- converts integer argument to 2 byte integer in NBO - // %4i -- converts integer argument to 4 byte integer in NBO - // %s -- converts CString* to stream of bytes - // %S -- converts integer N and const UInt8* to stream of N bytes + //! Write formatted data + /*! + Write formatted binary data to a stream. \c fmt consists of + regular characters and format specifiers. Format specifiers + begin with \%. All characters not part of a format specifier + are regular and are transmitted unchanged. + + Format specifiers are: + - \%\% -- literal `\%' + - \%1i -- converts integer argument to 1 byte integer + - \%2i -- converts integer argument to 2 byte integer in NBO + - \%4i -- converts integer argument to 4 byte integer in NBO + - \%s -- converts CString* to stream of bytes + - \%S -- converts integer N and const UInt8* to stream of N bytes + */ static void writef(IOutputStream*, const char* fmt, ...); - // read formatted binary data from a buffer. this performs the - // reverse operation of writef(). - // - // format specifiers are: - // %% -- read (and discard) a % - // %1i -- reads a 1 byte integer; argument is a SInt32* or UInt32* - // %2i -- reads an NBO 2 byte integer; arg is SInt32* or UInt32* - // %4i -- reads an NBO 4 byte integer; arg is SInt32* or UInt32* - // %s -- reads bytes; argument must be a CString*, *not* a char* + //! Read formatted data + /*! + Read formatted binary data from a buffer. This performs the + reverse operation of writef(). + + Format specifiers are: + - \%\% -- read (and discard) a literal `\%' + - \%1i -- reads a 1 byte integer; argument is a SInt32* or UInt32* + - \%2i -- reads an NBO 2 byte integer; arg is SInt32* or UInt32* + - \%4i -- reads an NBO 4 byte integer; arg is SInt32* or UInt32* + - \%s -- reads bytes; argument must be a CString*, \b not a char* + */ static void readf(IInputStream*, const char* fmt, ...); @@ -44,6 +55,11 @@ private: static void read(IInputStream*, void*, UInt32); }; +//! Mismatched read exception +/*! +Thrown by CProtocolUtil::readf() when the data being read does not +match the format. +*/ class XIOReadMismatch : public XIO { public: // XBase overrides diff --git a/synergy/CTCPSocketFactory.h b/synergy/CTCPSocketFactory.h index 2b900bb2..766c2170 100644 --- a/synergy/CTCPSocketFactory.h +++ b/synergy/CTCPSocketFactory.h @@ -3,15 +3,12 @@ #include "ISocketFactory.h" +//! Socket factory for TCP sockets class CTCPSocketFactory : public ISocketFactory { public: CTCPSocketFactory(); virtual ~CTCPSocketFactory(); - // manipulators - - // accessors - // ISocketFactory overrides virtual IDataSocket* create() const; virtual IListenSocket* createListen() const; diff --git a/synergy/ClipboardTypes.h b/synergy/ClipboardTypes.h index 63c44c91..478627bf 100644 --- a/synergy/ClipboardTypes.h +++ b/synergy/ClipboardTypes.h @@ -3,9 +3,14 @@ #include "BasicTypes.h" -// type to hold a clipboard identifier +//! Clipboard ID +/*! +Type to hold a clipboard identifier. +*/ typedef UInt8 ClipboardID; +//! @name Clipboard identifiers +//@{ // clipboard identifiers. kClipboardClipboard is what is normally // considered the clipboard (e.g. the cut/copy/paste menu items // affect it). kClipboardSelection is the selection on those @@ -17,5 +22,6 @@ static const ClipboardID kClipboardSelection = 1; // the number of clipboards (i.e. one greater than the last clipboard id) static const ClipboardID kClipboardEnd = 2; +//@} #endif diff --git a/synergy/IClient.h b/synergy/IClient.h index bf570772..a58f2859 100644 --- a/synergy/IClient.h +++ b/synergy/IClient.h @@ -7,74 +7,171 @@ #include "MouseTypes.h" #include "CString.h" -// the client interface. this provides all the methods necessary for -// the server to communicate with a client. +//! Client interface +/*! +This interface defines the methods necessary for the server to +communicate with a client. +*/ class IClient : public IInterface { public: - // manipulators + //! @name manipulators + //@{ - // open client. return true iff successful. + //! Open client + /*! + Open the client and return true iff successful. + */ virtual bool open() = 0; - // service client + //! Run client + /*! + Service the client. This method is typically called in a separate + thread and is exited by cancelling the thread. + + (cancellation point) + */ virtual void run() = 0; - // close client + //! Close client + /*! + Close the client. + */ virtual void close() = 0; - // enter the screen. the cursor should be warped to xAbs,yAbs. - // the client should record seqNum for future reporting of - // clipboard changes. mask is the expected toggle button state. - // forScreensaver is true if the screen is being entered because - // the screen saver is starting. + //! Enter screen + /*! + Enter the screen. The cursor should be warped to \c xAbs,yAbs. + The client should record seqNum for future reporting of + clipboard changes. \c mask is the expected toggle button state + and the client should update its state to match. \c forScreensaver + is true iff the screen is being entered because the screen saver is + starting. + */ virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, bool forScreensaver) = 0; - // leave the screen. returns false if the user may not leave the - // client's screen. + //! Leave screen + /*! + Leave the screen. Return false iff the user may not leave the + client's screen (because, for example, a button is down). + */ virtual bool leave() = 0; - // update the client's clipboard. this implies that the client's - // clipboard is now up to date. if the client's clipboard was - // already known to be up to date then this can do nothing. + //! Set clipboard + /*! + Update the client's clipboard. This implies that the client's + clipboard is now up to date. If the client's clipboard was + already known to be up to date then this may do nothing. + */ virtual void setClipboard(ClipboardID, const CString&) = 0; - // grab the client's clipboard. since this is called when another - // client takes ownership of the clipboard it implies that the - // client's clipboard is dirty. + //! Grab clipboard + /*! + Grab (i.e. take ownership of) the client's clipboard. Since this + is called when another client takes ownership of the clipboard it + implies that the client's clipboard is out of date. + */ virtual void grabClipboard(ClipboardID) = 0; - // called to set the client's clipboard as dirty or clean + //! Mark clipboard dirty + /*! + Mark the client's clipboard as dirty (out of date) or clean (up to + date). + */ virtual void setClipboardDirty(ClipboardID, bool dirty) = 0; - // handle input events - virtual void keyDown(KeyID, KeyModifierMask) = 0; - virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void keyUp(KeyID, KeyModifierMask) = 0; - virtual void mouseDown(ButtonID) = 0; - virtual void mouseUp(ButtonID) = 0; + //! Notify of key press + /*! + Synthesize key events to generate a press of key \c id. If possible + match the given modifier mask. + */ + virtual void keyDown(KeyID id, KeyModifierMask) = 0; + + //! Notify of key repeat + /*! + Synthesize key events to generate a press and release of key \c id + \c count times. If possible match the given modifier mask. + */ + virtual void keyRepeat(KeyID id, KeyModifierMask, SInt32 count) = 0; + + //! Notify of key release + /*! + Synthesize key events to generate a release of key \c id. If possible + match the given modifier mask. + */ + virtual void keyUp(KeyID id, KeyModifierMask) = 0; + + //! Notify of mouse press + /*! + Synthesize mouse events to generate a press of mouse button \c id. + */ + virtual void mouseDown(ButtonID id) = 0; + + //! Notify of mouse release + /*! + Synthesize mouse events to generate a release of mouse button \c id. + */ + virtual void mouseUp(ButtonID id) = 0; + + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion to the absolute + screen position \c xAbs,yAbs. + */ virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + + //! Notify of mouse wheel motion + /*! + Synthesize mouse events to generate mouse wheel motion of \c delta. + \c delta is positive for motion away from the user and negative for + motion towards the user. Each wheel click should generate a delta + of +/-120. + */ virtual void mouseWheel(SInt32 delta) = 0; + + //! Notify of screen saver change virtual void screensaver(bool activate) = 0; - // accessors + //@} + //! @name accessors + //@{ - // get the client's identifier + //! Get client name + /*! + Return the client's name. + */ virtual CString getName() const = 0; - // get the size of jump zone + //! Get jump zone size + /*! + Called to get the jump zone size. + */ virtual SInt32 getJumpZoneSize() const = 0; - // get the screen's shape + //! Get screen shape + /*! + Return the position of the upper-left corner of the screen in \c x and + \c y and the size of the screen in \c w (width) and \c h (height). + */ virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; - // get the mouse position + //! Get cursor position + /*! + Return the current position of the cursor in \c x and \c y. + */ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; - // get the center pixel + //! Get cursor center position + /*! + Return the cursor center position which is where we park the + cursor to compute cursor motion deltas and should be far from + the edges of the screen, typically the center. + */ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + + //@} }; #endif diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index cd56d9e2..9ad913eb 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -5,61 +5,99 @@ #include "CString.h" #include "BasicTypes.h" +//! Clipboard interface +/*! +This interface defines the methods common to all clipboards. +*/ class IClipboard : public IInterface { public: - // timestamp type. timestamps are in milliseconds from some - // arbitrary starting time. timestamps will wrap around to 0 - // after about 49 3/4 days. + //! Timestamp type + /*! + Timestamp type. Timestamps are in milliseconds from some + arbitrary starting time. Timestamps will wrap around to 0 + after about 49 3/4 days. + */ typedef UInt32 Time; - // known clipboard formats. kNumFormats must be last and - // formats must be sequential starting from zero. clipboard - // data set via add() and retrieved via get() is in one of - // these formats. platform dependent clipboard subclasses - // can and should present any suitable formats derivable - // from these formats (e.g. UTF-16 encoded unicode). - // - // kText: UTF-8 encoded unicode (ISO-10646), newline is LF. - enum EFormat { kText, kNumFormats }; + //! Clipboard formats + /*! + The list of known clipboard formats. kNumFormats must be last and + formats must be sequential starting from zero. Clipboard data set + via add() and retrieved via get() must be in one of these formats. + Platform dependent clipboard subclasses can and should present any + suitable formats derivable from these formats. + */ + enum EFormat { + kText, //!< Text format, UTF-8, newline is LF + kNumFormats //!< The number of clipboard formats + }; - // manipulators + //! @name manipulators + //@{ - // take ownership of the clipboard and clear all data from it. - // must be called between an open() and close(). if returns - // false then the clipboard ownership could not be taken; the - // clipboard should not be emptied in this case. + //! Empty clipboard + /*! + Take ownership of the clipboard and clear all data from it. + This must be called between a successful open() and close(). + Return false if the clipboard ownership could not be taken; + the clipboard should not be emptied in this case. + */ virtual bool empty() = 0; - // add data in the given format to the clipboard. data is - // passed as a string but the contents are generally not - // interpreted. may only be called after a successful empty(). + //! Add data + /*! + Add data in the given format to the clipboard. May only be + called after a successful empty(). + */ virtual void add(EFormat, const CString& data) = 0; - // accessors + //@} + //! @name accessors + //@{ - // open the clipboard. return true iff the clipboard could - // be opened. if open() returns true then it must be followed - // by a close() at some later time; if it returns false then - // close() must not be called. - virtual bool open(Time) const = 0; + //! Open clipboard + /*! + Open the clipboard. Return true iff the clipboard could be + opened. If open() returns true then the client must call + close() at some later time; if it returns false then close() + must not be called. \c time should be the current time or + a time in the past when the open should effectively have taken + place. + */ + virtual bool open(Time time) const = 0; - // close the clipboard. close() must match a preceding open(). - // this signals that the clipboard has been filled with all the - // necessary data. it does not mean the clipboard ownership - // should be released. + //! Close clipboard + /*! + Close the clipboard. close() must match a preceding successful + open(). This signals that the clipboard has been filled with + all the necessary data or all data has been read. It does not + mean the clipboard ownership should be released (if it was + taken). + */ virtual void close() const = 0; - // returns the timestamp passed to the last successful open(). + //! Get time + /*! + Return the timestamp passed to the last successful open(). + */ virtual Time getTime() const = 0; - // returns true iff the clipboard contains data in the given - // format. must be called between an open() and close(). + //! Check for data + /*! + Return true iff the clipboard contains data in the given + format. Must be called between a successful open() and close(). + */ virtual bool has(EFormat) const = 0; - // returns data in the given format. rturns the empty string - // if there is no data in that format. must be called between - // an open() and close(). + //! Get data + /*! + Return the data in the given format. Returns the empty string + if there is no data in that format. Must be called between + a successful open() and close(). + */ virtual CString get(EFormat) const = 0; + + //@} }; #endif diff --git a/synergy/IPrimaryScreenReceiver.h b/synergy/IPrimaryScreenReceiver.h index f347786e..1d34dfc3 100644 --- a/synergy/IPrimaryScreenReceiver.h +++ b/synergy/IPrimaryScreenReceiver.h @@ -5,24 +5,47 @@ #include "KeyTypes.h" #include "MouseTypes.h" -// the interface for receiving notification of events on the primary -// screen. the server implements this interface to handle user input. -// platform dependent primary screen implementation will need to take -// an IPrimaryScreenReceiver* and notify it of events. +//! Primary screen event receiver interface +/*! +The interface for receiving notification of events on the primary +screen. The server implements this interface to handle user input. +Platform dependent primary screen implementation will need to take +an IPrimaryScreenReceiver* and notify it of events. +*/ class IPrimaryScreenReceiver : public IInterface { public: - // called when the screensaver is activated or deactivated + //! Notify of screen saver change + /*! + Called when the screensaver is activated or deactivated. + */ virtual void onScreensaver(bool activated) = 0; // call to notify of events. onMouseMovePrimary() returns // true iff the mouse enters a jump zone and jumps. + //! Notify of key press virtual void onKeyDown(KeyID, KeyModifierMask) = 0; + //! Notify of key release virtual void onKeyUp(KeyID, KeyModifierMask) = 0; + //! Notify of key repeat virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + //! Notify of mouse button press virtual void onMouseDown(ButtonID) = 0; + //! Notify of mouse button release virtual void onMouseUp(ButtonID) = 0; + //! Notify of mouse motion + /*! + Called when the mouse has moved while on the primary screen. \c x + and \c y are the absolute screen position of the mouse. Return + true iff the mouse enters a jump zone and jumps. + */ virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; + //! Notify of mouse motion + /*! + Called when the mouse has moved while on the secondary screen. + \c dx and \c dy are the relative motion from the last position. + */ virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; + //! Notify of mouse wheen motion virtual void onMouseWheel(SInt32 delta) = 0; }; diff --git a/synergy/IScreen.h b/synergy/IScreen.h index 08c337ab..7cb8b8d1 100644 --- a/synergy/IScreen.h +++ b/synergy/IScreen.h @@ -6,65 +6,128 @@ class IClipboard; -// the interface for platform dependent screen implementations. each -// platform will derive a type from IScreen for interaction with the -// platform's screen that's common to primary and secondary screens. +//! Screen interface +/*! +This interface defines the methods common to all platform dependent +screen implementations that are use by both primary and secondary +screens. +*/ class IScreen : public IInterface { public: - // manipulators + //! @name manipulators + //@{ - // open the screen + //! Open screen + /*! + Called to open and initialize the screen. + */ virtual void open() = 0; - // runs an event loop and returns when exitMainLoop() is called. - // must be called between open() and close(). + //! Run event loop + /*! + Run the event loop and return when exitMainLoop() is called. + This must be called between a successful open() and close(). + */ virtual void mainLoop() = 0; - // force mainLoop() to return + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mailLoop() does (i.e. asynchronously). + */ virtual void exitMainLoop() = 0; - // close the screen + //! Close screen + /*! + Called to close the screen. close() should quietly ignore calls + that don't have a matching successful call to open(). + */ virtual void close() = 0; - // set the contents of the clipboard - virtual bool setClipboard(ClipboardID, const IClipboard*) = 0; + //! Set clipboard + /*! + Set the contents of the system clipboard indicated by \c id. + */ + virtual bool setClipboard(ClipboardID id, const IClipboard*) = 0; - // check clipboard ownership and notify IScreenReceiver (set through - // some other interface) if any changed + //! Check clipboard owner + /*! + Check ownership of all clipboards and notify an IScreenReceiver (set + through some other interface) if any changed. This is used as a + backup in case the system doesn't reliably report clipboard ownership + changes. + */ virtual void checkClipboards() = 0; - // open/close the screen saver. if notify is true then this object - // will call IScreenEventHandler's onScreenSaver() when the screensaver - // activates or deactivates until close. if notify is false then - // the screen saver is disabled on open and restored on close. + //! Open screen saver + /*! + Open the screen saver. If \c notify is true then this object must + call an IScreenEventHandler's (set through some other interface) + onScreenSaver() when the screensaver activates or deactivates until + it's closed. If \c notify is false then the screen saver is + disabled on open and restored on close. + */ virtual void openScreensaver(bool notify) = 0; + + //! Close screen saver + /*! + // Close the screen saver. Stop reporting screen saver activation + and deactivation and, if the screen saver was disabled by + openScreensaver(), enable the screen saver. + */ virtual void closeScreensaver() = 0; - // activate or deactivate the screen saver + //! Activate/deactivate screen saver + /*! + Forcibly activate the screen saver if \c activate is true otherwise + forcibly deactivate it. + */ virtual void screensaver(bool activate) = 0; - // ensure that this thread attached with the visible desktop. this is - // mainly intended for windows which has an artificial distinction - // between desktops and a thread cannot interact with the visible - // desktop unless the thread is attached to that desktop. + //! Attach to desktop + /*! + Called to ensure that this thread is attached to the visible desktop. + This is mainly intended for microsoft windows which has an artificial + distinction between desktops where a thread cannot interact with the + visible desktop unless the thread is attached to that desktop. Since + it doesn't report when the visible desktop changes we must poll. + */ virtual void syncDesktop() = 0; - // accessors + //@} + //! @name accessors + //@{ - // get the contents of the clipboard - virtual bool getClipboard(ClipboardID, IClipboard*) const = 0; + //! Get clipboard + /*! + Save the contents of the clipboard indicated by \c id and return + true iff successful. + */ + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; - // get the shape of the screen + //! Get screen shape + /*! + Return the position of the upper-left corner of the screen in \c x and + \c y and the size of the screen in \c w (width) and \c h (height). + */ virtual void getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const = 0; - // get the current cursor coordinates + //! Get cursor position + /*! + Return the current position of the cursor in \c x and \c y. + */ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; - // get the cursor center position. this is where we park the - // cursor to compute cursor motion deltas and should be far from - // the edges of the screen, typically the center. + //! Get cursor center position + /*! + Return the cursor center position which is where we park the + cursor to compute cursor motion deltas and should be far from + the edges of the screen, typically the center. + */ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + + //@} }; #endif diff --git a/synergy/IScreenEventHandler.h b/synergy/IScreenEventHandler.h index 4c6cd14e..196fbed9 100644 --- a/synergy/IScreenEventHandler.h +++ b/synergy/IScreenEventHandler.h @@ -8,32 +8,52 @@ class CEvent; class IScreen; -// the interface through which IScreen sends notification of events. -// each platform will derive two types from IScreenEventHandler, one -// for handling events on the primary screen and one for the -// secondary screen. the header file with the IScreen subclass for -// each platform should define the CEvent type, which depends on the -// type of native events for that platform. +//! Screen event handler interface +/*! +This is the interface through which IScreen sends notification of events. +Each platform will derive two types from IScreenEventHandler, one +for handling events on the primary screen and one for the +secondary screen. The header file with the IScreen subclass for +each platform should define the CEvent type, which depends on the +type of native events for that platform. +*/ class IScreenEventHandler : public IInterface { public: - // manipulators + //! @name manipulators + //@{ - // called when the screensaver is activated or deactivated + //! Notify of screen saver change + /*! + Called when the screensaver is activated or deactivated. + */ virtual void onScreensaver(bool activated) = 0; - // called for each event before event translation and dispatch. return - // true to skip translation and dispatch. subclasses should call the - // superclass's version first and return true if it returns true. + //! Event filtering + /*! + Called for each event before event translation and dispatch. Return + true to skip translation and dispatch. Subclasses should call the + superclass's version first and return true if it returns true. + */ virtual bool onPreDispatch(const CEvent* event) = 0; - // called by mainLoop(). iff the event was handled return true and - // store the result, if any, in m_result, which defaults to zero. + //! Event handling + /*! + Called to handle an event. Iff the event was handled return true and + store the result, if any, in event->m_result, which defaults to zero. + */ virtual bool onEvent(CEvent* event) = 0; - // accessors + //@} + //! @name accessors + //@{ - // called to get the jump zone size + //! Get jump zone size + /*! + Called to get the jump zone size. + */ virtual SInt32 getJumpZoneSize() const = 0; + + //@} }; #endif diff --git a/synergy/IScreenReceiver.h b/synergy/IScreenReceiver.h index da2d40c9..1cd2a79e 100644 --- a/synergy/IScreenReceiver.h +++ b/synergy/IScreenReceiver.h @@ -6,23 +6,43 @@ #include "ProtocolTypes.h" #include "CString.h" -// the interface for types that receive screen resize and clipboard -// notifications (indirectly) from the system. +//! Screen event receiver interface +/*! +This interface defines the methods common to most types that receive +events for changes to a screen. Note that the methods in this +interface are similar to the methods in IServer but have different +parameters. This interface is suitable for client-side types. +*/ class IScreenReceiver : public IInterface { public: - // called if the screen is unexpectedly closing. this implies that - // the screen is no longer usable and that the program should - // close the screen and possibly terminate. + //! Notify of error + /*! + Called when the screen is unexpectedly closing. This implies that + the screen is no longer usable and that the program should close + the screen and probably terminate. + */ virtual void onError() = 0; - // notify of client info change + //! Notify of client screen change + /*! + Called when the client's info has changed. For example, when the + screen resolution has changed. + */ virtual void onInfoChanged(const CClientInfo&) = 0; - // notify of clipboard grab. returns true if the grab was honored, - // false otherwise. + //! Notify of clipboad grab + /*! + Called when the clipboard was grabbed by another program and, + therefore, we no longer own it. Returns true if the grab was + honored, false otherwise. + */ virtual bool onGrabClipboard(ClipboardID) = 0; - // notify of new clipboard data + //! Notify of new clipboard data + /*! + Called when the data on the clipboard has changed because some + other program has changed it. + */ virtual void onClipboardChanged(ClipboardID, const CString& data) = 0; }; diff --git a/synergy/IScreenSaver.h b/synergy/IScreenSaver.h index d1bccf45..e0662011 100644 --- a/synergy/IScreenSaver.h +++ b/synergy/IScreenSaver.h @@ -3,28 +3,57 @@ #include "IInterface.h" +//! Screen saver interface +/*! +This interface defines the methods common to all screen savers. +*/ class IScreenSaver : public IInterface { public: // note -- the c'tor/d'tor must *not* enable/disable the screen saver - // manipulators + //! @name manipulators + //@{ - // enable/disable the screen saver. enable() should restore the - // screen saver settings to what they were when disable() was - // previously called. if disable() wasn't previously called then - // it should keep the current settings or use reasonable defaults. + //! Enable screen saver + /*! + Enable the screen saver, restoring the screen saver settings to + what they were when disable() was previously called. If disable() + wasn't previously called then it should keep the current settings + or use reasonable defaults. + */ virtual void enable() = 0; + + //! Disable screen saver + /*! + Disable the screen saver, saving the old settings for the next + call to enable(). + */ virtual void disable() = 0; - // activate/deactivate (i.e. show/hide) the screen saver. - // deactivate() also resets the screen saver timer. + //! Activate screen saver + /*! + Activate (i.e. show) the screen saver. + */ virtual void activate() = 0; + + //! Deactivate screen saver + /*! + Deactivate (i.e. hide) the screen saver, reseting the screen saver + timer. + */ virtual void deactivate() = 0; - // accessors + //@} + //! @name accessors + //@{ - // returns true iff the screen saver is active + //! Test if screen saver on + /*! + Returns true iff the screen saver is currently active (showing). + */ virtual bool isActive() const = 0; + + //@} }; #endif diff --git a/synergy/IServer.h b/synergy/IServer.h index ee3798d6..7dd819eb 100644 --- a/synergy/IServer.h +++ b/synergy/IServer.h @@ -7,33 +7,54 @@ class CClientInfo; -// the server interface. this provides all the methods necessary for -// clients to communicate with the server. note that the methods -// in this interface are similar to the methods in IScreenReceiver but -// include extra parameters. this interface is suitable for server-side -// client proxies. client-side objects should use the IScreenReceiver -// interface since the extra parameters are meaningless on the client-side. +//! Server interface +/*! +This interface defines the methods necessary for clients to +communicate with the server. Note that the methods in this +interface are similar to the methods in IScreenReceiver but +include extra parameters. This interface is suitable for +server-side client proxies. Client-side objects should use +the IScreenReceiver interface since the extra parameters are +meaningless on the client-side. +*/ class IServer : public IInterface { public: - // manipulators + //! @name manipulators + //@{ - // called if the screen is unexpectedly closing. this implies that - // the screen is no longer usable and that the program should - // close the screen and possibly terminate. + //! Notify of error + /*! + Called when the screen is unexpectedly closing. This implies that + the screen is no longer usable and that the program should close + the screen and probably terminate. + */ virtual void onError() = 0; - // notify of client info change + //! Notify of client screen change + /*! + Called when the client's info has changed. + */ virtual void onInfoChanged(const CString& clientName, const CClientInfo&) = 0; - // notify of clipboard grab. returns true if the grab was honored, - // false otherwise. + //! Notify of clipboad grab + /*! + Called when the clipboard was grabbed by another program and, + therefore, we no longer own it. Returns true if the grab was + honored, false otherwise. + */ virtual bool onGrabClipboard(const CString& clientName, ClipboardID, UInt32 seqNum) = 0; - // notify of new clipboard data + //! Notify of new clipboard data + /*! + Called when the data on the clipboard has changed because some + other program has changed it. + */ virtual void onClipboardChanged(ClipboardID, UInt32 seqNum, const CString& data) = 0; + + //@} }; #endif diff --git a/synergy/ISocketFactory.h b/synergy/ISocketFactory.h index 7b7ff8cd..cb719a5e 100644 --- a/synergy/ISocketFactory.h +++ b/synergy/ISocketFactory.h @@ -6,15 +6,23 @@ class IDataSocket; class IListenSocket; +//! Socket factory +/*! +This interface defines the methods common to all factories used to +create sockets. +*/ class ISocketFactory : public IInterface { public: - // manipulators + //! @name accessors + //@{ - // accessors - - // create sockets + //! Create data socket virtual IDataSocket* create() const = 0; + + //! Create listen socket virtual IListenSocket* createListen() const = 0; + + //@} }; #endif diff --git a/synergy/KeyTypes.h b/synergy/KeyTypes.h index 37e54184..0623961a 100644 --- a/synergy/KeyTypes.h +++ b/synergy/KeyTypes.h @@ -3,15 +3,22 @@ #include "BasicTypes.h" -// type to hold a key identifier. the encoding is UTF-32, using -// U+E000 through U+EFFF for the various control keys (e.g. arrow -// keys, function keys, modifier keys, etc). +//! Key ID +/*! +Type to hold a key identifier. The encoding is UTF-32, using +U+E000 through U+EFFF for the various control keys (e.g. arrow +keys, function keys, modifier keys, etc). +*/ typedef UInt32 KeyID; -// type to hold bitmask of key modifiers (e.g. shift keys) +//! Modifier key ID +/*! +Type to hold a bitmask of key modifiers (e.g. shift keys). +*/ typedef UInt32 KeyModifierMask; -// modifier key bitmasks +//! @name Modifier key identifiers +//@{ static const KeyModifierMask KeyModifierShift = 0x0001; static const KeyModifierMask KeyModifierControl = 0x0002; static const KeyModifierMask KeyModifierAlt = 0x0004; @@ -19,11 +26,12 @@ static const KeyModifierMask KeyModifierMeta = 0x0008; static const KeyModifierMask KeyModifierCapsLock = 0x1000; static const KeyModifierMask KeyModifierNumLock = 0x2000; static const KeyModifierMask KeyModifierScrollLock = 0x4000; +//@} -// -// key codes. all codes except kKeyNone are equal to the corresponding +//! @name Key identifiers +//@{ +// all identifiers except kKeyNone are equal to the corresponding // X11 keysym - 0x1000. -// // no key static const KeyID kKeyNone = 0x0000; @@ -163,5 +171,6 @@ static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */ // more function and modifier keys static const KeyID kKeyLeftTab = 0xEE20; +//@} #endif diff --git a/synergy/MouseTypes.h b/synergy/MouseTypes.h index ad5f0c7a..8b6719de 100644 --- a/synergy/MouseTypes.h +++ b/synergy/MouseTypes.h @@ -3,13 +3,18 @@ #include "BasicTypes.h" -// type to hold mouse button identifier +//! Mouse button ID +/*! +Type to hold a mouse button identifier. +*/ typedef UInt8 ButtonID; -// mouse button identifiers +//! @name Mouse button identifiers +//@{ static const ButtonID kButtonNone = 0; static const ButtonID kButtonLeft = 1; static const ButtonID kButtonMiddle = 2; static const ButtonID kButtonRight = 3; +//@} #endif diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index c4d48ae4..3e14cd13 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -3,11 +3,11 @@ #include "BasicTypes.h" -// version number +// protocol version number static const SInt16 kProtocolMajorVersion = 0; static const SInt16 kProtocolMinorVersion = 7; -// contact port number +// default contact port number static const UInt16 kDefaultPort = 24800; // time between heartbeats (in seconds) @@ -181,16 +181,37 @@ static const char kMsgEBad[] = "EBAD"; // structures // +//! Screen information +/*! +This class contains information about a screen. +*/ class CClientInfo { public: - // the coordinates of the screen + //! Screen position + /*! + The position of the upper-left corner of the screen. This is + typically 0,0. + */ SInt32 m_x, m_y; + + //! Screen size + /*! + The size of the screen in pixels. + */ SInt32 m_w, m_h; - // the size of the jump zone + //! Jump zone size + /*! + This is the size of the jump zone. The cursor jumps to the adjacent + screen when it comes within this many pixels of the edge of the screen. + */ SInt32 m_zoneSize; - // mouse position + //! Mouse position + /*! + The position of the cursor. This is not kept up-to-date so it's + only meaningful when receiving an update. + */ SInt32 m_mx, m_my; }; diff --git a/synergy/Version.h b/synergy/Version.h index 5b8ae1c7..128f500c 100644 --- a/synergy/Version.h +++ b/synergy/Version.h @@ -3,10 +3,12 @@ #include "BasicTypes.h" +// important strings static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; -static const char* kContact = "Chris Schoeneman crs23@bigfoot.com"; +static const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; +static const char* kWebsite = ""; -// build version. follows linux kernel style: minor number even implies +// build version. follows linux kernel style: an even minor number implies // a release version, odd implies development version. static const SInt16 kMajorVersion = 0; static const SInt16 kMinorVersion = 9; diff --git a/synergy/XScreen.h b/synergy/XScreen.h index 23ea45e5..2b71402a 100644 --- a/synergy/XScreen.h +++ b/synergy/XScreen.h @@ -3,9 +3,13 @@ #include "XBase.h" +//! Generic screen exception class XScreen : public XBase { }; -// screen cannot be opened +//! Cannot open screen exception +/*! +Thrown when a screen cannot be opened or initialized. +*/ class XScreenOpenFailure : public XScreen { protected: virtual CString getWhat() const throw(); diff --git a/synergy/XSynergy.h b/synergy/XSynergy.h index dda1d106..86ce98ee 100644 --- a/synergy/XSynergy.h +++ b/synergy/XSynergy.h @@ -3,26 +3,36 @@ #include "XBase.h" +//! Generic synergy exception class XSynergy : public XBase { }; -// client is misbehaving +//! Client error exception +/*! +Thrown when the client fails to follow the protocol. +*/ class XBadClient : public XSynergy { protected: virtual CString getWhat() const throw(); }; -// client has incompatible version +//! Incompatible client exception +/*! +Thrown when a client attempting to connect has an incompatible version. +*/ class XIncompatibleClient : public XSynergy { public: XIncompatibleClient(int major, int minor); - // manipulators - - // accessors + //! @name accessors + //@{ + //! Get client's major version number int getMajor() const throw(); + //! Get client's minor version number int getMinor() const throw(); + //@} + protected: virtual CString getWhat() const throw(); @@ -31,18 +41,24 @@ private: int m_minor; }; -// client has duplicate name (i.e. client with name is already connected) +//! Client already connected exception +/*! +Thrown when a client attempting to connect is using the same name as +a client that is already connected. +*/ class XDuplicateClient : public XSynergy { public: XDuplicateClient(const CString& name); - // manipulators - - // accessors + //! @name accessors + //@{ + //! Get client's name virtual const CString& getName() const throw(); + //@} + protected: virtual CString getWhat() const throw(); @@ -50,18 +66,24 @@ private: CString m_name; }; -// client has unknown name (i.e. name is not in server's screen map) +//! Client not in map +/*! +Thrown when a client attempting to connect is using a name that is +unknown to the server. +*/ class XUnknownClient : public XSynergy { public: XUnknownClient(const CString& name); - // manipulators - - // accessors + //! @name accessors + //@{ + //! Get the client's name virtual const CString& getName() const throw(); + //@} + protected: virtual CString getWhat() const throw(); From 3a05ffe3c44bf1e831477d3bb1c5041dda4dfaac Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 29 Jul 2002 17:03:55 +0000 Subject: [PATCH 286/807] Added doxygen comments for all relevant headers in platform. --- platform/CMSWindowsClipboard.h | 6 + .../CMSWindowsClipboardAnyTextConverter.h | 18 ++- platform/CMSWindowsClipboardTextConverter.h | 1 + platform/CMSWindowsClipboardUTF16Converter.h | 1 + platform/CMSWindowsScreen.h | 38 ++++-- platform/CMSWindowsScreenSaver.h | 17 ++- platform/CUnixPlatform.h | 1 + platform/CWin32Platform.h | 30 +++-- platform/CXWindowsClipboard.h | 93 ++++++++++---- platform/CXWindowsClipboardTextConverter.h | 4 + platform/CXWindowsClipboardUCS2Converter.h | 4 + platform/CXWindowsClipboardUTF8Converter.h | 4 + platform/CXWindowsScreen.h | 44 +++++-- platform/CXWindowsScreenSaver.h | 30 +++-- platform/CXWindowsUtil.h | 51 ++++++-- platform/IMSWindowsScreenEventHandler.h | 16 ++- platform/IPlatform.h | 113 ++++++++++++------ 17 files changed, 356 insertions(+), 115 deletions(-) diff --git a/platform/CMSWindowsClipboard.h b/platform/CMSWindowsClipboard.h index fa253c1e..4992c57c 100644 --- a/platform/CMSWindowsClipboard.h +++ b/platform/CMSWindowsClipboard.h @@ -8,6 +8,7 @@ class IMSWindowsClipboardConverter; +//! Microsoft windows clipboard implementation class CMSWindowsClipboard : public IClipboard { public: CMSWindowsClipboard(HWND window); @@ -37,6 +38,11 @@ private: ConverterList m_converters; }; +//! Clipboard format converter interface +/*! +This interface defines the methods common to all win32 clipboard format +converters. +*/ class IMSWindowsClipboardConverter : public IInterface { public: // accessors diff --git a/platform/CMSWindowsClipboardAnyTextConverter.h b/platform/CMSWindowsClipboardAnyTextConverter.h index 497702f0..e124b5ae 100755 --- a/platform/CMSWindowsClipboardAnyTextConverter.h +++ b/platform/CMSWindowsClipboardAnyTextConverter.h @@ -3,6 +3,7 @@ #include "CMSWindowsClipboard.h" +//! Convert to/from some text encoding class CMSWindowsClipboardAnyTextConverter : public IMSWindowsClipboardConverter { public: @@ -17,11 +18,20 @@ public: virtual CString toIClipboard(HANDLE) const; protected: - // do UTF-8 conversion only. memory handle allocation and - // linefeed conversion is done by this class. doFromIClipboard() - // must include the nul terminator in the returned string (not - // including the CString's nul terminator). + //! Convert from IClipboard format + /*! + Do UTF-8 conversion only. Memory handle allocation and + linefeed conversion is done by this class. doFromIClipboard() + must include the nul terminator in the returned string (not + including the CString's nul terminator). + */ virtual CString doFromIClipboard(const CString&) const = 0; + + //! Convert to IClipboard format + /*! + Do UTF-8 conversion only. Memory handle allocation and + linefeed conversion is done by this class. + */ virtual CString doToIClipboard(const CString&) const = 0; private: diff --git a/platform/CMSWindowsClipboardTextConverter.h b/platform/CMSWindowsClipboardTextConverter.h index 7edab3e5..0b52a6ae 100755 --- a/platform/CMSWindowsClipboardTextConverter.h +++ b/platform/CMSWindowsClipboardTextConverter.h @@ -3,6 +3,7 @@ #include "CMSWindowsClipboardAnyTextConverter.h" +//! Convert to/from locale text encoding class CMSWindowsClipboardTextConverter : public CMSWindowsClipboardAnyTextConverter { public: diff --git a/platform/CMSWindowsClipboardUTF16Converter.h b/platform/CMSWindowsClipboardUTF16Converter.h index 6ccaa3f8..2a7ff114 100755 --- a/platform/CMSWindowsClipboardUTF16Converter.h +++ b/platform/CMSWindowsClipboardUTF16Converter.h @@ -3,6 +3,7 @@ #include "CMSWindowsClipboardAnyTextConverter.h" +//! Convert to/from UTF-16 encoding class CMSWindowsClipboardUTF16Converter : public CMSWindowsClipboardAnyTextConverter { public: diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index 9a2f7c92..335a5531 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -11,6 +11,7 @@ class CMSWindowsScreenSaver; class CThread; +// Microsoft windows event class CEvent { public: MSG m_msg; @@ -20,30 +21,53 @@ public: class IScreenReceiver; class IMSWindowsScreenEventHandler; +// Microsoft windows screen implementation class CMSWindowsScreen : public IScreen { public: CMSWindowsScreen(IScreenReceiver*, IMSWindowsScreenEventHandler*); virtual ~CMSWindowsScreen(); - // manipulators + //! @name manipulators + //@{ + //! Initialize + /*! + Saves the application's HINSTANCE. This \b must be called by + WinMain with the HINSTANCE it was passed. + */ static void init(HINSTANCE); - // open the desktop and create and return the window. returns NULL - // on failure. + //! Open desktop + /*! + Open the desktop and create and return the window. Returns NULL + on failure. + */ HWND openDesktop(); - // close the window and desktop + //! Close desktop + /*! + Close the window and desktop. + */ void closeDesktop(); - // accessors + //@} + //! @name accessors + //@{ - // returns true iff the system appears to have multiple monitors + //! Test for multiple monitors + /*! + Returns true iff the system appears to have multiple monitors. + */ bool isMultimon() const; - // get the application instance handle + //! Get instance + /*! + Returns the application instance handle passed to init(). + */ static HINSTANCE getInstance(); + //@} + // IScreen overrides // note -- this class expects the hook DLL to have been loaded // and initialized before open() is called. diff --git a/platform/CMSWindowsScreenSaver.h b/platform/CMSWindowsScreenSaver.h index df4a342b..85f95859 100755 --- a/platform/CMSWindowsScreenSaver.h +++ b/platform/CMSWindowsScreenSaver.h @@ -7,19 +7,26 @@ class CThread; +//! Microsoft windows screen saver implementation class CMSWindowsScreenSaver : public IScreenSaver { public: CMSWindowsScreenSaver(); virtual ~CMSWindowsScreenSaver(); - // manipulators + //! @name manipulators + //@{ - // check if the screen saver really started. returns false if it - // hasn't, true otherwise. when the screen saver stops msg will - // be posted to the current thread's message queue with the given - // parameters. + //! Check if screen saver started + /*! + Check if the screen saver really started. Returns false if it + hasn't, true otherwise. When the screen saver stops, \c msg will + be posted to the current thread's message queue with the given + parameters. + */ bool checkStarted(UINT msg, WPARAM, LPARAM); + //@} + // IScreenSaver overrides virtual void enable(); virtual void disable(); diff --git a/platform/CUnixPlatform.h b/platform/CUnixPlatform.h index 8fbe56c9..7209411c 100644 --- a/platform/CUnixPlatform.h +++ b/platform/CUnixPlatform.h @@ -3,6 +3,7 @@ #include "IPlatform.h" +//! Unix platform dependent functions class CUnixPlatform : public IPlatform { public: CUnixPlatform(); diff --git a/platform/CWin32Platform.h b/platform/CWin32Platform.h index 8d0985c7..09cb542f 100644 --- a/platform/CWin32Platform.h +++ b/platform/CWin32Platform.h @@ -7,6 +7,7 @@ #define WIN32_LEAN_AND_MEAN #include +//! Microsoft windows platform dependent functions class CWin32Platform : public IPlatform { public: typedef int (*RunFunc)(CMutex*); @@ -15,24 +16,35 @@ public: CWin32Platform(); virtual ~CWin32Platform(); - // returns true iff the platform is win95/98/me + //! Test if windows 95, et al. + /*! + Returns true iff the platform is win95/98/me. + */ static bool isWindows95Family(); - // utility for calling SetServiceStatus() + //! Utility for calling SetServiceStatus() static void setStatus(SERVICE_STATUS_HANDLE, DWORD state); + //! Utility for calling SetServiceStatus() static void setStatus(SERVICE_STATUS_HANDLE, DWORD state, DWORD step, DWORD waitHint); + //! Utility for calling SetServiceStatus() static void setStatusError(SERVICE_STATUS_HANDLE, DWORD error); - // run a service. the RunFunc should unlock the passed in mutex - // (which will be locked on entry) when not initializing or - // shutting down (i.e. when running its loop). StopFunc should - // cause the RunFunc() to return. returns what RunFunc returns. - // RunFunc should throw CDaemonFailed if the service fails. + //! Run service + /*! + Run a service. The RunFunc should unlock the passed in mutex + (which will be locked on entry) when not initializing or + shutting down (i.e. when running its loop). StopFunc should + cause the RunFunc() to return. Returns what RunFunc returns. + RunFunc should throw CDaemonFailed if the service fails. + */ int runDaemon(RunFunc, StopFunc); - // thrown by RunFunc on service failure. result is the error - // code reported by the service. + //! Daemon failed exception + /*! + Thrown by RunFunc on service failure. Result is the error + code reported by the service. + */ class CDaemonFailed { public: CDaemonFailed(int result) : m_result(result) { } diff --git a/platform/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h index f32ad0d0..af8ef27b 100644 --- a/platform/CXWindowsClipboard.h +++ b/platform/CXWindowsClipboard.h @@ -14,34 +14,58 @@ class IXWindowsClipboardConverter; +//! X11 clipboard implementation class CXWindowsClipboard : public IClipboard { public: - CXWindowsClipboard(Display*, Window, ClipboardID); + /*! + Use \c window as the window that owns or interacts with the + clipboard identified by \c id. + */ + CXWindowsClipboard(Display*, Window window, ClipboardID id); virtual ~CXWindowsClipboard(); - // tell clipboard it lost ownership + //! Notify clipboard was lost + /*! + Tells clipboard it lost ownership at the given time. + */ void lost(Time); - // add a selection request to the request list. if the given - // owner window isn't this clipboard's window then this simply - // sends a failure event to the requestor. + //! Add clipboard request + /*! + Adds a selection request to the request list. If the given + owner window isn't this clipboard's window then this simply + sends a failure event to the requestor. + */ void addRequest(Window owner, Window requestor, Atom target, ::Time time, Atom property); - // continue processing a selection request. returns true if the - // request was handled, false if the request was unknown. + //! Process clipboard request + /*! + Continues processing a selection request. Returns true if the + request was handled, false if the request was unknown. + */ bool processRequest(Window requestor, ::Time time, Atom property); - // terminate a selection request. returns true iff the request - // was known and handled. + //! Cancel clipboard request + /*! + Terminate a selection request. Returns true iff the request + was known and handled. + */ bool destroyRequest(Window requestor); - // get the clipboard's window + //! Get window + /*! + Returns the clipboard's window (passed the c'tor). + */ Window getWindow() const; - // get the clipboard selection atom + //! Get selection atom + /*! + Returns the selection atom that identifies the clipboard to X11 + (e.g. XA_PRIMARY). + */ Atom getSelection() const; // IClipboard overrides @@ -281,31 +305,54 @@ private: Atom m_atomGDKSelection; }; +//! Clipboard format converter interface +/*! +This interface defines the methods common to all X11 clipboard format +converters. +*/ class IXWindowsClipboardConverter : public IInterface { public: - // accessors + //! @name accessors + //@{ - // return the clipboard format this object converts from/to + //! Get clipboard format + /*! + Return the clipboard format this object converts from/to. + */ virtual IClipboard::EFormat getFormat() const = 0; - // return the atom representing the X selection format that - // this object converts from/to + //! Get X11 format atom + /*! + Return the atom representing the X selection format that + this object converts from/to. + */ virtual Atom getAtom() const = 0; - // return the size (in bits) of data elements returned by - // toIClipboard(). + //! Get X11 property datum size + /*! + Return the size (in bits) of data elements returned by + toIClipboard(). + */ virtual int getDataSize() const = 0; - // convert from the IClipboard format to the X selection format. - // the input data must be in the IClipboard format returned by - // getFormat(). the return data will be in the X selection - // format returned by getAtom(). + //! Convert from IClipboard format + /*! + Convert from the IClipboard format to the X selection format. + The input data must be in the IClipboard format returned by + getFormat(). The return data will be in the X selection + format returned by getAtom(). + */ virtual CString fromIClipboard(const CString&) const = 0; - // convert from the X selection format to the IClipboard format - // (i.e., the reverse of fromIClipboard()). + //! Convert to IClipboard format + /*! + Convert from the X selection format to the IClipboard format + (i.e., the reverse of fromIClipboard()). + */ virtual CString toIClipboard(const CString&) const = 0; + + //@} }; #endif diff --git a/platform/CXWindowsClipboardTextConverter.h b/platform/CXWindowsClipboardTextConverter.h index b73e06a5..b601eea1 100644 --- a/platform/CXWindowsClipboardTextConverter.h +++ b/platform/CXWindowsClipboardTextConverter.h @@ -3,8 +3,12 @@ #include "CXWindowsClipboard.h" +//! Convert to/from locale text encoding class CXWindowsClipboardTextConverter : public IXWindowsClipboardConverter { public: + /*! + \c name is converted to an atom and that is reported by getAtom(). + */ CXWindowsClipboardTextConverter(Display* display, const char* name); virtual ~CXWindowsClipboardTextConverter(); diff --git a/platform/CXWindowsClipboardUCS2Converter.h b/platform/CXWindowsClipboardUCS2Converter.h index 1a87c84d..b2cb0c6f 100644 --- a/platform/CXWindowsClipboardUCS2Converter.h +++ b/platform/CXWindowsClipboardUCS2Converter.h @@ -3,8 +3,12 @@ #include "CXWindowsClipboard.h" +//! Convert to/from UCS-2 encoding class CXWindowsClipboardUCS2Converter : public IXWindowsClipboardConverter { public: + /*! + \c name is converted to an atom and that is reported by getAtom(). + */ CXWindowsClipboardUCS2Converter(Display* display, const char* name); virtual ~CXWindowsClipboardUCS2Converter(); diff --git a/platform/CXWindowsClipboardUTF8Converter.h b/platform/CXWindowsClipboardUTF8Converter.h index ed7313a1..457ad9de 100644 --- a/platform/CXWindowsClipboardUTF8Converter.h +++ b/platform/CXWindowsClipboardUTF8Converter.h @@ -3,8 +3,12 @@ #include "CXWindowsClipboard.h" +//! Convert to/from UTF-8 encoding class CXWindowsClipboardUTF8Converter : public IXWindowsClipboardConverter { public: + /*! + \c name is converted to an atom and that is reported by getAtom(). + */ CXWindowsClipboardUTF8Converter(Display* display, const char* name); virtual ~CXWindowsClipboardUTF8Converter(); diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index 5c7bf18b..5d2ca594 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -19,38 +19,62 @@ class IScreenReceiver; class CXWindowsClipboard; class CXWindowsScreenSaver; +// X11 event class CEvent { public: XEvent m_event; SInt32 m_result; }; +// X11 screen implementation class CXWindowsScreen : public IScreen { public: CXWindowsScreen(IScreenReceiver*, IScreenEventHandler*); virtual ~CXWindowsScreen(); - // manipulators + //! @name manipulators + //@{ - // add/remove a job to invoke every timeout seconds. the job is - // called with the display locked. if a job timeout expires twice - // or more before the job can be called then the job is called - // just once. the caller retains ownership of the job. + //! Add timer + /*! + Add a job to invoke every timeout seconds. The job is + called with the display locked. If a job timeout expires twice + or more before the job can be called then the job is called + just once. The caller retains ownership of the job. + */ void addTimer(IJob*, double timeout); + + //! Remove timer + /*! + Remove a job. The caller retains ownership of the job. + */ void removeTimer(IJob*); - // set the window (created by the subclass). this performs some - // initialization and saves the window in case it's needed later. + //! Set window + /*! + Set the window (created by the subclass). This performs some + initialization and saves the window in case it's needed later. + */ void setWindow(Window); - // accessors + //@} + //! @name accessors + //@{ - // get the root window of the screen + //! Get window + /*! + Returns the root window of the screen. + */ Window getRoot() const; - // get a cursor that is transparent everywhere + //! Get transparent cursor + /*! + Returns a cursor that is transparent everywhere. + */ Cursor getBlankCursor() const; + //@} + // IScreen overrides void open(); void mainLoop(); diff --git a/platform/CXWindowsScreenSaver.h b/platform/CXWindowsScreenSaver.h index 78712ab0..f4320087 100644 --- a/platform/CXWindowsScreenSaver.h +++ b/platform/CXWindowsScreenSaver.h @@ -12,6 +12,7 @@ class IJob; class CXWindowsScreen; +//! X11 screen saver implementation class CXWindowsScreenSaver : public IScreenSaver { public: // note -- the caller must ensure that Display* passed to c'tor isn't @@ -20,19 +21,30 @@ public: CXWindowsScreenSaver(CXWindowsScreen*, Display*); virtual ~CXWindowsScreenSaver(); - // called for each event before event translation and dispatch. return - // true to skip translation and dispatch. subclasses should call the - // superclass's version first and return true if it returns true. + //! @name manipulators + //@{ + + //! Event filtering + /*! + Called for each event before event translation and dispatch. Return + true to skip translation and dispatch. Subclasses should call the + superclass's version first and return true if it returns true. + */ bool onPreDispatch(const XEvent*); - // tells this object to send a ClientMessage to the given window - // when the screen saver activates or deactivates. only one - // window can be notified. the message type is the "SCREENSAVER" - // atom, the format is 32, and the data.l[0] member is non-zero - // if activated, zero if deactivated. pass in None to disable - // notification; + //! Set notify target + /*! + Tells this object to send a ClientMessage to the given window + when the screen saver activates or deactivates. Only one window + can be notified at a time. The message type is the "SCREENSAVER" + atom, the format is 32, and the data.l[0] member is non-zero + if activated, zero if deactivated. Pass None to disable + notification. + */ void setNotify(Window); + //@} + // IScreenSaver overrides virtual void enable(); virtual void disable(); diff --git a/platform/CXWindowsUtil.h b/platform/CXWindowsUtil.h index fefcc92a..f6866db4 100644 --- a/platform/CXWindowsUtil.h +++ b/platform/CXWindowsUtil.h @@ -9,30 +9,67 @@ # include #endif +//! X11 utility functions class CXWindowsUtil { public: + //! Get property + /*! + Gets property \c property on \c window. \b Appends the data to + \c *data if \c data is not NULL, saves the property type in \c *type + if \c type is not NULL, and saves the property format in \c *format + if \c format is not NULL. If \c deleteProperty is true then the + property is deleted after being read. + */ static bool getWindowProperty(Display*, Window window, Atom property, CString* data, Atom* type, SInt32* format, bool deleteProperty); + + //! Set property + /*! + Sets property \c property on \c window to \c size bytes of data from + \c data. + */ static bool setWindowProperty(Display*, Window window, Atom property, const void* data, UInt32 size, Atom type, SInt32 format); + + //! Get X server time + /*! + Returns the current X server time. + */ static Time getCurrentTime(Display*, Window); - // class to set an X error handler in the c'tor and restore the - // previous error handler in the d'tor. a lock should only - // be installed while the display is locked by the thread. - // - // CErrorLock() ignores errors - // CErrorLock(bool* flag) sets *flag to true if any error occurs + //! X11 error handler + /*! + This class sets an X error handler in the c'tor and restores the + previous error handler in the d'tor. A lock should only be + installed while the display is locked by the thread. + + CErrorLock() ignores errors + CErrorLock(bool* flag) sets *flag to true if any error occurs + */ class CErrorLock { public: + //! Error handler type typedef void (*ErrorHandler)(Display*, XErrorEvent*, void* userData); + + /*! + Ignore X11 errors. + */ CErrorLock(Display*); + + /*! + Set \c *errorFlag if any error occurs. + */ CErrorLock(Display*, bool* errorFlag); - CErrorLock(Display*, ErrorHandler, void* userData); + + /*! + Call \c handler on each error. + */ + CErrorLock(Display*, ErrorHandler handler, void* userData); + ~CErrorLock(); private: diff --git a/platform/IMSWindowsScreenEventHandler.h b/platform/IMSWindowsScreenEventHandler.h index eb39542b..134e0082 100755 --- a/platform/IMSWindowsScreenEventHandler.h +++ b/platform/IMSWindowsScreenEventHandler.h @@ -5,16 +5,26 @@ #define WIN32_LEAN_AND_MEAN #include +//! MS Windows screen event handler interface class IMSWindowsScreenEventHandler : public IScreenEventHandler { public: - // manipulators + //! @name manipulators + //@{ - // called after the window is created + //! Notify of window creation + /*! + This is called after the window is created. + */ virtual void postCreateWindow(HWND) = 0; - // called before the window is destroyed + //! Notify of window destruction + /*! + This is called before the window is destroyed. + */ virtual void preDestroyWindow(HWND) = 0; + //@} + // IScreenEventHandler overrides virtual void onScreensaver(bool activated) = 0; virtual bool onPreDispatch(const CEvent* event) = 0; diff --git a/platform/IPlatform.h b/platform/IPlatform.h index 60056575..36644b7c 100644 --- a/platform/IPlatform.h +++ b/platform/IPlatform.h @@ -4,73 +4,110 @@ #include "IInterface.h" #include "CString.h" +//! Platform dependent functions interface +/*! +This interface provides methods to query platform specific data and +perform operations that are inherently non-portable. +*/ class IPlatform : public IInterface { public: typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv); + //! Uninstall result codes enum EResult { - kSuccess, - kFailed, - kAlready + kSuccess, //!< Uninstall succeeded + kFailed, //!< Uninstall failed + kAlready //!< Not installed }; - // manipulators + //! @name manipulators + //@{ - // install/uninstall a daemon. commandLine should *not* - // include the name of program as the first argument. + //! Install daemon + /*! + Install a daemon. \c name is the name of the daemon passed to the + system and \c description is a short human readable description of + the daemon. \c pathname is the path to the daemon executable. + \c commandLine should \b not include the name of program as the + first argument. + */ // FIXME -- throw on error? will get better error messages that way. virtual bool installDaemon(const char* name, const char* description, const char* pathname, const char* commandLine) = 0; + //! Uninstall daemon + /*! + Uninstall a daemon. + */ virtual EResult uninstallDaemon(const char* name) = 0; - // daemonize. this should call installDaemonLogger(). returns - // true iff successful. the name is the name of the daemon. - - // daemonize. this should have the side effect of sending log - // messages to a system message logger since messages can no - // longer go to the console. name is the name of the daemon. - // once daemonized, func is invoked and daemonize returns when - // and what func does. daemonize() returns -1 on error. - // - // exactly what happens when daemonizing depends on the platform. - // unix: - // detaches from terminal. func gets one argument, the name - // passed to daemonize(). - // win32: - // becomes a service. argument 0 is the name of the service - // and the rest are the arguments passed to StartService(). - // func is only called when the service is actually started. - // func must behave like a proper ServiceMain() function; in - // particular, it must call RegisterServiceCtrlHandler() and - // SetServiceStatus(). + //! Daemonize the process + /*! + Daemonize. This should call installDaemonLogger(). Returns + true iff successful. \c name is the name of the daemon. + Once daemonized, \c func is invoked and daemonize returns when + and what it does. daemonize() returns -1 on error. + + Exactly what happens when daemonizing depends on the platform. +
    +
  • unix: + Detaches from terminal. \c func gets passed one argument, the + name passed to daemonize(). +
  • win32: + Becomes a service. Argument 0 is the name of the service + and the rest are the arguments passed to StartService(). + \c func is only called when the service is actually started. + \c func must behave like a proper ServiceMain() function; in + particular, it must call RegisterServiceCtrlHandler() and + SetServiceStatus(). +
+ */ virtual int daemonize(const char* name, DaemonFunc func) = 0; - // directs CLog to send messages to the daemon log. used when - // messages should no longer go to the console. name is used - // in the log to identify this process. + //! Install daemon logger + /*! + Directs CLog to send messages to the daemon log. Used when + messages should no longer go to the console. \c name is used + in the log to identify this process. + */ virtual void installDaemonLogger(const char* name) = 0; - // accessors + //@} + //! @name accessors + //@{ - // find the basename in the given pathname + //! Extract base name + /*! + Find the base name in the given \c pathname. + */ virtual const char* getBasename(const char* pathname) const = 0; - // get the user's home directory. returns the empty string if - // this cannot be determined. + //! Get user's home directory + /*! + Returns the user's home directory. Returns the empty string if + this cannot be determined. + */ virtual CString getUserDirectory() const = 0; - // get the system configuration file directory + //! Get system directory + /*! + Returns the ussystem configuration file directory. + */ virtual CString getSystemDirectory() const = 0; - // concatenate pathname components with a directory separator - // between them. this should not check if the resulting path - // is longer than allowed by the system. we'll rely on the - // system calls to tell us that. + //! Concatenate path components + /*! + Concatenate pathname components with a directory separator + between them. This should not check if the resulting path + is longer than allowed by the system; we'll rely on the + system calls to tell us that. + */ virtual CString addPathComponent( const CString& prefix, const CString& suffix) const = 0; + + //@} }; #endif From 8913acac345c93129a6df19f6f0fb0684895f219 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Jul 2002 14:59:36 +0000 Subject: [PATCH 287/807] Added doxygen comments for all relevant headers in client and server. --- client/CClient.h | 46 ++++- client/CMSWindowsSecondaryScreen.h | 1 + client/CSecondaryScreen.h | 301 ++++++++++++++++++++++------ client/CServerProxy.cpp | 6 +- client/CServerProxy.h | 51 ++++- client/CXWindowsSecondaryScreen.h | 1 + server/CClientProxy.h | 26 ++- server/CClientProxy1_0.h | 1 + server/CConfig.h | 156 +++++++++++---- server/CHTTPServer.cpp | 23 ++- server/CHTTPServer.h | 32 ++- server/CMSWindowsPrimaryScreen.h | 1 + server/CPrimaryClient.h | 47 ++++- server/CPrimaryScreen.h | 305 +++++++++++++++++++++-------- server/CServer.h | 66 +++++-- server/CXWindowsPrimaryScreen.h | 1 + synergy/IClient.h | 14 +- synergy/IScreen.h | 3 +- synergy/IScreenReceiver.h | 3 +- synergy/IServer.h | 3 +- 20 files changed, 848 insertions(+), 239 deletions(-) diff --git a/client/CClient.h b/client/CClient.h index 2e8e2256..7b3925e0 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -13,31 +13,57 @@ class CThread; class IDataSocket; class IScreenReceiver; +//! Synergy client +/*! +This class implements the top-level client algorithms for synergy. +*/ class CClient : public IScreenReceiver, public IClient { public: + /*! + This client will attempt to connect the server using \c clientName + as its name. + */ CClient(const CString& clientName); ~CClient(); - // manipulators + //! @name manipulators + //@{ - // turn camping on or off. when camping the client will keep - // trying to connect to the server until it succeeds. this - // is useful if the client may start before the server. do - // not call this while in run(). + //! Set camping state + /*! + Turns camping on or off. When camping the client will keep + trying to connect to the server until it succeeds. This + is useful if the client may start before the server. Do + not call this while in run(). + */ void camp(bool on); - // set the server's address that the client should connect to + //! Set server address + /*! + Sets the server's address that the client should connect to. + */ void setAddress(const CNetworkAddress& serverAddress); - // tell client to exit run() gracefully. this must only be called - // after a successful open(). + //! Exit event loop + /*! + Force run() to return. This call can return before + run() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ void quit(); - // accessors + //@} + //! @name accessors + //@{ - // returns true if the server rejected us + //! + /*! + Returns true if the server rejected our connection. + */ bool wasRejected() const; + //@} + // IScreenReceiver overrides virtual void onError(); virtual void onInfoChanged(const CClientInfo&); diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index b98a4251..67d24694 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -16,6 +16,7 @@ class CMSWindowsScreen; class IScreenReceiver; +//! Microsoft windows secondary screen implementation class CMSWindowsSecondaryScreen : public CSecondaryScreen, public IMSWindowsScreenEventHandler { public: diff --git a/client/CSecondaryScreen.h b/client/CSecondaryScreen.h index 4db665f1..5c4d9fe8 100644 --- a/client/CSecondaryScreen.h +++ b/client/CSecondaryScreen.h @@ -9,126 +9,305 @@ class IClipboard; class IScreen; -// platform independent base class for secondary screen implementations. -// each platform will derive a class from CSecondaryScreen to handle -// platform dependent operations. +//! Generic client-side screen +/*! +This is a platform independent base class for secondary screen +implementations. A secondary screen is a client-side screen. +Each platform will derive a class from CSecondaryScreen to handle +platform dependent operations. +*/ class CSecondaryScreen { public: CSecondaryScreen(); virtual ~CSecondaryScreen(); - // manipulators + //! @name manipulators + //@{ - // enter the screen's message loop. this returns when it detects - // the application should terminate or when stop() is called. - // run() may only be called between open() and close(). - void run(); - - // cause run() to return - void stop(); - - // initialize the screen, hide the cursor, and disable the screen - // saver. start reporting events to the IScreenReceiver (which is - // set through some other interface). + //! Open screen + /*! + Opens the screen. This includes initializing the screen, + hiding the cursor, and disabling the screen saver. It also causes + events to the reported to an IScreenReceiver (which is set through + some other interface). Calls close() before returning (rethrowing) + if it fails for any reason. + */ void open(); - // close the screen. should restore the screen saver. it should - // also simulate key up events for any keys that have simulate key - // down events without a matching key up. without this the client - // will leave its keyboard in the wrong logical state. + //! Run event loop + /*! + Run the screen's event loop. This returns when it detects + the application should terminate or when stop() is called. + run() may only be called between open() and close(). + */ + void run(); + + //! Exit event loop + /*! + Force run() to return. This call can return before + run() does (i.e. asynchronously). + */ + void stop(); + + //! Close screen + /*! + Closes the screen. This restores the screen saver, shows the cursor + and closes the screen. It also synthesizes key up events for any + keys that are logically down; without this the client will leave + its keyboard in the wrong logical state. + */ void close(); - // called when the user navigates to this secondary screen. warps - // the cursor to the given absoltue coordinates and unhide it. prepare to - // simulate input events. + //! Enter screen + /*! + Called when the user navigates to this secondary screen. Warps + the cursor to the absolute coordinates \c x,y and unhides + it. Also prepares to synthesize input events. + */ void enter(SInt32 x, SInt32 y, KeyModifierMask mask); - // called when the user navigates off the secondary screen. clean - // up input event simulation and hide the cursor. + //! Leave screen + /*! + Called when the user navigates off the secondary screen. Cleans + up input event synthesis and hides the cursor. + */ void leave(); - // set the screen's clipboard contents. this is usually called - // soon after an enter(). + //! Set clipboard + /*! + Sets the system's clipboard contents. This is usually called + soon after an enter(). + */ void setClipboard(ClipboardID, const IClipboard*); - // synergy should own the clipboard + //! Grab clipboard + /*! + Grabs (i.e. take ownership of) the system clipboard. + */ void grabClipboard(ClipboardID); - // activate or deactivate the screen saver + //! Activate/deactivate screen saver + /*! + Forcibly activates the screen saver if \c activate is true otherwise + forcibly deactivates it. + */ void screensaver(bool activate); - // keyboard input event synthesis - virtual void keyDown(KeyID, KeyModifierMask) = 0; - virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void keyUp(KeyID, KeyModifierMask) = 0; + //! Notify of key press + /*! + Synthesize key events to generate a press of key \c id. If possible + match the given modifier mask. + */ + virtual void keyDown(KeyID id, KeyModifierMask) = 0; - // mouse input event synthesis - virtual void mouseDown(ButtonID) = 0; - virtual void mouseUp(ButtonID) = 0; - virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + //! Notify of key repeat + /*! + Synthesize key events to generate a press and release of key \c id + \c count times. If possible match the given modifier mask. + */ + virtual void keyRepeat(KeyID id, KeyModifierMask, SInt32 count) = 0; + + //! Notify of key release + /*! + Synthesize key events to generate a release of key \c id. If possible + match the given modifier mask. + */ + virtual void keyUp(KeyID id, KeyModifierMask) = 0; + + //! Notify of mouse press + /*! + Synthesize mouse events to generate a press of mouse button \c id. + */ + virtual void mouseDown(ButtonID id) = 0; + + //! Notify of mouse release + /*! + Synthesize mouse events to generate a release of mouse button \c id. + */ + virtual void mouseUp(ButtonID id) = 0; + + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion to the absolute + screen position \c xAbs,yAbs. + */ + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + + //! Notify of mouse wheel motion + /*! + Synthesize mouse events to generate mouse wheel motion of \c delta. + \c delta is positive for motion away from the user and negative for + motion towards the user. Each wheel click should generate a delta + of +/-120. + */ virtual void mouseWheel(SInt32 delta) = 0; - // accessors + //@} + //! @name accessors + //@{ - // returns true iff the screen is active (i.e. the user has entered - // the screen) + //! Test if active + /*! + Returns true iff the screen is active (i.e. the user has entered + the screen). Note this is the reverse of a primary screen. + */ bool isActive() const; - // return the contents of the given clipboard - void getClipboard(ClipboardID, IClipboard*) const; + //! Get clipboard + /*! + Saves the contents of the system clipboard indicated by \c id. + */ + void getClipboard(ClipboardID id, IClipboard*) const; - // returns the size of the zone on the edges of the screen that - // causes the cursor to jump to another screen. + //! Get jump zone size + /*! + Return the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. + */ virtual SInt32 getJumpZoneSize() const = 0; - // get the shape (position of upper-left corner and size) of the - // screen + //! Get screen shape + /*! + Return the position of the upper-left corner of the screen in \c x and + \c y and the size of the screen in \c width and \c height. + */ virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; - // get the position of the mouse on the screen + //! Get cursor position + /*! + Return the current position of the cursor in \c x,y. + */ virtual void getCursorPos(SInt32& x, SInt32& y) const; - // get the platform dependent screen object + //! Get screen + /*! + Return the platform dependent screen. + */ virtual IScreen* getScreen() const = 0; + //@} + protected: - // template method hooks. these are called on entry/exit to the - // named method. override to do platform specific operations. - // defaults do nothing. + //! Pre-run() hook + /*! + Called on entry to run(). Override to perform platform specific + operations. Default does nothing. May throw. + */ virtual void onPreRun(); + + //! Post-run() hook + /*! + Called on exit from run(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ virtual void onPostRun(); + + //! Pre-open() hook + /*! + Called on entry to open(). Override to perform platform specific + operations. Default does nothing. May throw. + */ virtual void onPreOpen(); + + //! Post-open() hook + /*! + Called on exit from open() iff the open was successful. Default + does nothing. May throw. + */ virtual void onPostOpen(); + + //! Pre-close() hook + /*! + Called on entry to close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ virtual void onPreClose(); + + //! Post-close() hook + /*! + Called on exit from close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ virtual void onPostClose(); + + //! Pre-enter() hook + /*! + Called on entry to enter() after desktop synchronization. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ virtual void onPreEnter(); + + //! Post-enter() hook + /*! + Called on exit from enter(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ virtual void onPostEnter(); + + //! Pre-leave() hook + /*! + Called on entry to leave() after desktop synchronization. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ virtual void onPreLeave(); + + //! Post-leave() hook + /*! + Called on exit from leave(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ virtual void onPostLeave(); - // create/destroy the window. this window is generally used to - // receive events and hide the cursor. + //! Create window + /*! + Called to create the window. This window is generally used to + receive events and hide the cursor. + */ virtual void createWindow() = 0; + + //! Destroy window + /*! + Called to destroy the window created by createWindow(). + */ virtual void destroyWindow() = 0; - // called when the user navigates off the secondary screen. hide - // the cursor. + //! Show window + /*! + Called when the user navigates off this secondary screen. It needn't + actually show the window created by createWindow() but it must hide + the cursor and clean up event synthesis. + */ virtual void showWindow() = 0; - // called when the user navigates to this secondary screen. show - // the cursor and prepare to synthesize input events. + //! Hide window + /*! + Called when the user navigates to this secondary screen. It should + hide the window (if shown), show the cursor, and prepare to synthesize + input events. + */ virtual void hideWindow() = 0; - // warp the cursor to the given absolute coordinates + //! Warp cursor + /*! + Warp the cursor to the absolute coordinates \c x,y. + */ virtual void warpCursor(SInt32 x, SInt32 y) = 0; - // check the current keyboard state. normally a screen will save - // the keyboard state in this method and use this shadow state - // when synthesizing events. + //! Synchronize key state + /*! + Check the current keyboard state. Normally a screen will save + the keyboard state in this method and use this shadow state + when synthesizing events. + */ virtual void updateKeys() = 0; - // toggle modifiers that don't match the given state + //! Synchronize toggle key state + /*! + Toggle modifiers that don't match the given state so that they do. + */ virtual void setToggleState(KeyModifierMask) = 0; private: diff --git a/client/CServerProxy.cpp b/client/CServerProxy.cpp index b8a9a3b3..bec9d1ce 100644 --- a/client/CServerProxy.cpp +++ b/client/CServerProxy.cpp @@ -15,10 +15,10 @@ // CServerProxy::CServerProxy(IClient* client, - IInputStream* input, IOutputStream* output) : + IInputStream* adoptedInput, IOutputStream* adoptedOutput) : m_client(client), - m_input(input), - m_output(output), + m_input(adoptedInput), + m_output(adoptedOutput), m_seqNum(0) { assert(m_client != NULL); diff --git a/client/CServerProxy.h b/client/CServerProxy.h index e7f48d19..d0eecc21 100644 --- a/client/CServerProxy.h +++ b/client/CServerProxy.h @@ -8,31 +8,60 @@ class IClient; class IInputStream; class IOutputStream; +//! Proxy for server +/*! +This class acts a proxy for the server, converting calls into messages +to the server and messages from the server to calls on the client. +*/ class CServerProxy : public IScreenReceiver { public: + /*! \c adoptedInput is the stream from the server and + \c adoptedOutput is the stream to the server. This object + takes ownership of both and destroys them in the d'tor. + Messages from the server are converted to calls on \c client. + */ CServerProxy(IClient* client, IInputStream* adoptedInput, IOutputStream* adoptedOutput); ~CServerProxy(); - // manipulators + //! @name manipulators + //@{ - // handle messages. returns true iff server didn't reject our - // connection. + //! Run event loop + /*! + Run the event loop and return when the server disconnects or + requests the client to disconnect. Return true iff the server + didn't reject our connection. + + (cancellation point) + */ bool run(); - // accessors + //@} + //! @name accessors + //@{ - // get the client + //! Get client + /*! + Returns the client passed to the c'tor. + */ IClient* getClient() const; - // get the client name - CString getName() const; - - // get the input and output streams for the server + //! Get input stream + /*! + Return the input stream passed to the c'tor. + */ IInputStream* getInputStream() const; + + //! Get output stream + /*! + Return the output stream passed to the c'tor. + */ IOutputStream* getOutputStream() const; + //@} + // IScreenReceiver overrides virtual void onError(); virtual void onInfoChanged(const CClientInfo&); @@ -40,6 +69,10 @@ public: virtual void onClipboardChanged(ClipboardID, const CString& data); private: + + // get the client name (from the client) + CString getName() const; + // if compressing mouse motion then send the last motion now void flushCompressedMouse(); diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 07ddb346..113eb03e 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -14,6 +14,7 @@ class CXWindowsScreen; class IScreenReceiver; +//! X11 secondary screen implementation class CXWindowsSecondaryScreen : public CSecondaryScreen, public IScreenEventHandler { public: diff --git a/server/CClientProxy.h b/server/CClientProxy.h index 8eb07a93..7d7c4c31 100644 --- a/server/CClientProxy.h +++ b/server/CClientProxy.h @@ -8,24 +8,40 @@ class IInputStream; class IOutputStream; class IServer; +//! Generic proxy for client class CClientProxy : public IClient { public: + /*! + \c name is the name of the client. + */ CClientProxy(IServer* server, const CString& name, IInputStream* adoptedInput, IOutputStream* adoptedOutput); ~CClientProxy(); - // manipulators + //! @name accessors + //@{ - // accessors - - // get the server + //! Get server + /*! + Returns the server passed to the c'tor. + */ IServer* getServer() const; - // get the input and output streams for the client + //! Get input stream + /*! + Returns the input stream passed to the c'tor. + */ IInputStream* getInputStream() const; + + //! Get output stream + /*! + Returns the output stream passed to the c'tor. + */ IOutputStream* getOutputStream() const; + //@} + // IClient overrides virtual bool open() = 0; virtual void run() = 0; diff --git a/server/CClientProxy1_0.h b/server/CClientProxy1_0.h index 80b20cb1..0f5679a0 100644 --- a/server/CClientProxy1_0.h +++ b/server/CClientProxy1_0.h @@ -5,6 +5,7 @@ #include "ProtocolTypes.h" #include "CMutex.h" +//! Proxy for client implementing protocol version 1.0 class CClientProxy1_0 : public CClientProxy { public: CClientProxy1_0(IServer* server, const CString& name, diff --git a/server/CConfig.h b/server/CConfig.h index a67d211f..dfbd4b50 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -20,6 +20,16 @@ struct iterator_traits { }; }; +//! Server configuration +/*! +This class holds server configuration information. That includes +the names of screens and their aliases, the links between them, +and network addresses. + +Note that case is preserved in screen names but is ignored when +comparing names. Screen names and their aliases share a +namespace and must be unique. +*/ class CConfig { private: class CCell { @@ -59,79 +69,155 @@ public: CConfig(); virtual ~CConfig(); - // manipulators + //! @name manipulators + //@{ - // note that case is preserved in screen names but is ignored when - // comparing names. screen names and their aliases share a - // namespace and must be unique. - - // add/remove screens. addScreen() returns false if the name - // already exists. the remove methods automatically remove - // aliases for the named screen and disconnect any connections - // to the removed screen(s). + //! Add screen + /*! + Adds a screen, returning true iff successful. If a screen or + alias with the given name exists then it fails. + */ bool addScreen(const CString& name); + + //! Remove screen + /*! + Removes a screen. This also removes aliases for the screen and + disconnects any connections to the screen. \c name may be an + alias. + */ void removeScreen(const CString& name); + + //! Remove all screens + /*! + Removes all screens, aliases, and connections. + */ void removeAllScreens(); - // add/remove alias for a screen name. an alias can be used - // any place the canonical screen name can (except addScreen). - // addAlias() returns false if the alias name already exists - // or the canonical name is unknown. removeAlias() fails if - // the alias is unknown or a canonical name. + //! Add alias + /*! + Adds an alias for a screen name. An alias can be used + any place the canonical screen name can (except addScreen()). + Returns false if the alias name already exists or the canonical + name is unknown, otherwise returns true. + */ bool addAlias(const CString& canonical, const CString& alias); + + //! Remove alias + /*! + Removes an alias for a screen name. It returns false if the + alias is unknown or a canonical name, otherwise returns true. + */ bool removeAlias(const CString& alias); + + //! Remove all aliases + /*! + This removes all aliases but not the screens. + */ void removeAllAliases(); - // connect/disconnect edges. both return false if srcName is - // unknown. + //! Connect screens + /*! + Establishes a one-way connection between opposite edges of two + screens. The user will be able to jump from the \c srcSide of + screen \c srcName to the opposite side of screen \c dstName + when both screens are connected to the server and the user + isn't locked to a screen. Returns false if \c srcName is + unknown. + */ bool connect(const CString& srcName, EDirection srcSide, const CString& dstName); + + //! Disconnect screens + /*! + Removes a connection created by connect(). Returns false if + \c srcName is unknown. + */ bool disconnect(const CString& srcName, EDirection srcSide); - // set the synergy and http listen addresses. there are no - // default addresses. + //! Set server address + /*! + Set the synergy listen addresses. There is no default address so + this must be called to run a server using this configuration. + */ void setSynergyAddress(const CNetworkAddress&); + + //! Set HTTP server address + /*! + Set the HTTP listen addresses. There is no default address so + this must be called to run an HTTP server using this configuration. + */ void setHTTPAddress(const CNetworkAddress&); - // accessors + //@} + //! @name accessors + //@{ - // returns true iff the given name is a valid screen name. - bool isValidScreenName(const CString&) const; + //! Test screen name validity + /*! + Returns true iff \c name is a valid screen name. + */ + bool isValidScreenName(const CString& name) const; - // iterators over (canonical) screen names + //! Get beginning (canonical) screen name iterator const_iterator begin() const; + //! Get ending (canonical) screen name iterator const_iterator end() const; - // returns true iff name names a screen + //! Test for screen name + /*! + Returns true iff \c name names a screen. + */ bool isScreen(const CString& name) const; - // returns true iff name is the canonical name of a screen + //! Test for canonical screen name + /*! + Returns true iff \c name is the canonical name of a screen. + */ bool isCanonicalName(const CString& name) const; - // returns the canonical name of a screen or the empty string if - // the name is unknown. returns the canonical name if one is given. + //! Get canonical name + /*! + Returns the canonical name of a screen or the empty string if + the name is unknown. Returns the canonical name if one is given. + */ CString getCanonicalName(const CString& name) const; - // get the neighbor in the given direction. returns the empty string - // if there is no neighbor in that direction. returns the canonical - // screen name. + //! Get neighbor + /*! + Returns the canonical screen name of the neighbor in the given + direction (set through connect()). Returns the empty string + if there is no neighbor in that direction. + */ CString getNeighbor(const CString&, EDirection) const; - // get the listen addresses + //! Get the server address const CNetworkAddress& getSynergyAddress() const; + //! Get the HTTP server address const CNetworkAddress& getHTTPAddress() const; - // read/write a configuration. operator>> will throw XConfigRead - // on error. + //! Read configuration + /*! + Reads a configuration from a stream. Throws XConfigRead on error. + */ friend std::istream& operator>>(std::istream&, CConfig&); + + //! Write configuration + /*! + Writes a configuration to a stream. + */ friend std::ostream& operator<<(std::ostream&, const CConfig&); - // get the name of a direction (for debugging) + //! Get direction name + /*! + Returns the name of a direction (for debugging). + */ static const char* dirName(EDirection); + //@} + private: static bool readLine(std::istream&, CString&); void readSection(std::istream&); @@ -149,6 +235,10 @@ private: CNetworkAddress m_httpAddress; }; +//! Configuration stream read exception +/*! +Thrown when a configuration stream cannot be parsed. +*/ class XConfigRead : public XBase { public: XConfigRead(const CString&); diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index 1ac810ac..e5066fe4 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -53,8 +53,15 @@ CHTTPServer::processRequest(IDataSocket* socket) request->m_uri = request->m_uri.substr(n); } - // process + // prepare reply CHTTPReply reply; + reply.m_majorVersion = request->m_majorVersion; + reply.m_minorVersion = request->m_minorVersion; + reply.m_status = 200; + reply.m_reason = "OK"; + reply.m_method = request->m_method; + + // process doProcessRequest(*request, reply); // send reply @@ -93,21 +100,19 @@ CHTTPServer::processRequest(IDataSocket* socket) void CHTTPServer::doProcessRequest(CHTTPRequest& request, CHTTPReply& reply) { - reply.m_majorVersion = request.m_majorVersion; - reply.m_minorVersion = request.m_minorVersion; - reply.m_status = 200; - reply.m_reason = "OK"; - reply.m_method = request.m_method; - reply.m_headers.push_back(std::make_pair(CString("Content-Type"), - CString("text/html"))); - // switch based on uri if (request.m_uri == "/editmap") { if (request.m_method == "GET" || request.m_method == "HEAD") { doProcessGetEditMap(request, reply); + reply.m_headers.push_back(std::make_pair( + CString("Content-Type"), + CString("text/html"))); } else if (request.m_method == "POST") { doProcessPostEditMap(request, reply); + reply.m_headers.push_back(std::make_pair( + CString("Content-Type"), + CString("text/html"))); } else { throw XHTTPAllow("GET, HEAD, POST"); diff --git a/server/CHTTPServer.h b/server/CHTTPServer.h index f8c9b241..041f029a 100644 --- a/server/CHTTPServer.h +++ b/server/CHTTPServer.h @@ -11,26 +11,52 @@ class CHTTPRequest; class CHTTPReply; class IDataSocket; +//! Simple HTTP server +/*! +This class implements a simple HTTP server for interacting with the +synergy server. +*/ class CHTTPServer { public: CHTTPServer(CServer*); virtual ~CHTTPServer(); - // manipulators + //! @name manipulators + //@{ - // synchronously process an HTTP request on the given socket + //! Process HTTP request + /*! + Synchronously processes an HTTP request on the given socket. + */ void processRequest(IDataSocket*); - // accessors + //@} protected: + //! Process HTTP request + /*! + Processes a successfully read HTTP request. The reply is partially + filled in (version, method, status (200) and reason (OK)). This + method checks the URI and handles the request, filling in the rest + of the reply. If the request cannot be satisfied it throws an + appropriate XHTTP exception. + */ virtual void doProcessRequest(CHTTPRequest&, CHTTPReply&); + //! Process request for map virtual void doProcessGetEditMap(CHTTPRequest&, CHTTPReply&); + + //! Process request for changing map virtual void doProcessPostEditMap(CHTTPRequest&, CHTTPReply&); + //! Parse coordinate string static bool parseXY(const CString&, SInt32& x, SInt32& y); + //! Screen map helper + /*! + This class represents the screen map as a resizable array. It's + used to handle map requests. + */ class CScreenArray { public: CScreenArray(); diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 3dc2d161..30c06f5d 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -11,6 +11,7 @@ class CMSWindowsScreen; class IScreenReceiver; class IPrimaryScreenReceiver; +//! Microsoft windows primary screen implementation class CMSWindowsPrimaryScreen : public CPrimaryScreen, public IMSWindowsScreenEventHandler { public: diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h index a94da39e..468133d0 100644 --- a/server/CPrimaryClient.h +++ b/server/CPrimaryClient.h @@ -10,29 +10,60 @@ class CPrimaryScreen; class IPrimaryScreenReceiver; class IServer; +//! Primary screen as pseudo-client +/*! +The primary screen does not have a client associated with it. This +class provides a pseudo-client to allow the primary screen to be +treated as if it was on a client. +*/ class CPrimaryClient : public IScreenReceiver, public IClient { public: + /*! + \c name is the name of the server. + */ CPrimaryClient(IServer*, IPrimaryScreenReceiver*, const CString& name); ~CPrimaryClient(); - // manipulators + //! @name manipulators + //@{ - // cause run() to return + //! Exit event loop + /*! + Force run() to return. This call can return before + run() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ void stop(); - // called by server when the configuration changes + //! Update configuration + /*! + Handles reconfiguration of jump zones. + */ void reconfigure(UInt32 activeSides); - // accessors + //@} + //! @name accessors + //@{ - // return the contents of the given clipboard. + //! Get clipboard + /*! + Save the marshalled contents of the clipboard indicated by \c id. + */ void getClipboard(ClipboardID, CString&) const; - // returns true iff the user is locked to the primary screen + //! Get toggle key state + /*! + Returns the primary screen's current toggle modifier key state. + */ + KeyModifierMask getToggleMask() const; + + //! Get screen lock state + /*! + Returns true if the user is locked to the screen. + */ bool isLockedToScreen() const; - // returns the state of the toggle keys on the primary screen - KeyModifierMask getToggleMask() const; + //@} // IScreenReceiver overrides virtual void onError(); diff --git a/server/CPrimaryScreen.h b/server/CPrimaryScreen.h index 7fab4eba..1bde5f92 100644 --- a/server/CPrimaryScreen.h +++ b/server/CPrimaryScreen.h @@ -9,140 +9,291 @@ class IClipboard; class IScreen; class IScreenReceiver; -// platform independent base class for primary screen implementations. -// each platform will derive a class from CPrimaryScreen to handle -// platform dependent operations. +//! Generic server-side screen +/*! +This is a platform independent base class for primary screen +implementations. A primary screen is a server-side screen. +Each platform will derive a class from CPrimaryScreen to handle +platform dependent operations. +*/ class CPrimaryScreen { public: CPrimaryScreen(IScreenReceiver*); virtual ~CPrimaryScreen(); - // manipulators + //! @name manipulators + //@{ - // enter the screen's message loop. this returns when it detects - // the application should terminate or when stop() is called. - // run() may only be called between open() and close(). - void run(); - - // cause run() to return - void stop(); - - // initializes the screen and starts reporting events + //! Open screen + /*! + Opens the screen. This includes initializing the screen, opening + the screen saver, synchronizing keyboard state, and causing events + to be reported to an IPrimaryScreenReceiver (set through another + interface). Calls close() before returning (rethrowing) if it + fails for any reason. + */ void open(); - // close the screen + //! Run event loop + /*! + Run the screen's event loop. This returns when it detects + the application should terminate or when stop() is called. + run() may only be called between open() and close(). + */ + void run(); + + //! Exit event loop + /*! + Force run() to return. This call can return before + run() does (i.e. asynchronously). + */ + void stop(); + + //! Close screen + /*! + Closes the screen. This close the screen saver and the screen. + */ void close(); - // called when the user navigates to the primary screen. - // forScreensaver == true means that we're entering the primary - // screen because the screensaver has activated. + //! Enter screen + /*! + Called when the user navigates to the primary screen. Warps + the cursor to the absolute coordinates \c x,y and unhides + it. If \c forScreensaver is true then we're entering because + the screen saver started and the cursor is not warped. + */ void enter(SInt32 x, SInt32 y, bool forScreensaver); - // called when the user navigates off the primary screen. returns - // true iff successful. + //! Leave screen + /*! + Called when the user navigates off the primary screen. Returns + true iff successful. + */ bool leave(); - // called when the configuration has changed. activeSides is a - // bitmask of EDirectionMask indicating which sides of the - // primary screen are linked to clients. + //! Update configuration + /*! + This is called when the configuration has changed. \c activeSides + is a bitmask of EDirectionMask indicating which sides of the + primary screen are linked to clients. Override to handle the + possible change in jump zones. + */ virtual void reconfigure(UInt32 activeSides) = 0; - // warp the cursor to the given absolute coordinates. also - // discard input events up to and including the warp before - // returning. + //! Warp cursor + /*! + Warp the cursor to the absolute coordinates \c x,y. Also + discard input events up to and including the warp before + returning. + */ virtual void warpCursor(SInt32 x, SInt32 y) = 0; - // set the screen's clipboard contents. this is usually called - // soon after an enter(). + //! Set clipboard + /*! + Sets the system's clipboard contents. This is usually called + soon after an enter(). + */ void setClipboard(ClipboardID, const IClipboard*); - // synergy should own the clipboard + //! Grab clipboard + /*! + Grabs (i.e. take ownership of) the system clipboard. + */ void grabClipboard(ClipboardID); - // accessors + //@} + //! @name accessors + //@{ - // returns true iff the screen is active (i.e. the user has left - // the screen) + //! Test if active + /*! + Returns true iff the screen is active (i.e. the user has left + the screen). Note this is the reverse of a secdonary screen. + */ bool isActive() const; - // return the contents of the given clipboard + //! Get clipboard + /*! + Saves the contents of the system clipboard indicated by \c id. + */ void getClipboard(ClipboardID, IClipboard*) const; - // returns the size of the zone on the edges of the screen that - // causes the cursor to jump to another screen. + //! Get jump zone size + /*! + Return the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. + */ virtual SInt32 getJumpZoneSize() const = 0; - // get the primary screen's current toggle modifier key state. - // the returned mask should have the corresponding bit set for - // each toggle key that is active. + //! Get toggle key state + /*! + Return the primary screen's current toggle modifier key state. + The returned mask should have the corresponding bit set for + each toggle key that is active. For example, if caps lock is + on then the returned mask should have \c KeyModifierCapsLock set. + */ virtual KeyModifierMask getToggleMask() const = 0; - // return true if any key or button is being pressed or if there's - // any other reason that the user should not be allowed to switch - // screens. + //! Get screen lock state + /*! + Return true if any key or button is being pressed or if there's + any other reason that the user should not be allowed to switch + screens. Active toggle keys (including the scroll lock key) + should not be counted as reasons to lock to the screen. + */ virtual bool isLockedToScreen() const = 0; - // get the platform dependent screen object + //! Get screen + /*! + Return the platform dependent screen. + */ virtual IScreen* getScreen() const = 0; + //@} + protected: - // template method hooks. these are called on entry/exit to the - // named method. onEnterScreensaver() is called by enter() iff - // forScreensaver is true. onPostLeave() is passed the result of - // showWindow(). override to do platform specific operations. - // defaults do nothing. + //! Pre-run() hook + /*! + Called on entry to run(). Override to perform platform specific + operations. Default does nothing. May throw. + */ virtual void onPreRun(); + + //! Post-run() hook + /*! + Called on exit from run(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ virtual void onPostRun(); + + //! Pre-open() hook + /*! + Called on entry to open(). Override to perform platform specific + operations. Default does nothing. May throw. + */ virtual void onPreOpen(); + + //! Post-open() hook + /*! + Called on exit from open() iff the open was successful. Default + does nothing. May throw. + */ virtual void onPostOpen(); + + //! Pre-close() hook + /*! + Called on entry to close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ virtual void onPreClose(); + + //! Post-close() hook + /*! + Called on exit from close(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ virtual void onPostClose(); + + //! Pre-enter() hook + /*! + Called from enter() after the cursor has been warped or, if + \c forScreensaver is true, onEnterScreensaver() was called. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ virtual void onPreEnter(); + + //! Post-enter() hook + /*! + Called on exit from enter(). Override to perform platform specific + operations. Default does nothing. May \b not throw. + */ virtual void onPostEnter(); + + //! Pre-enter() for screen saver hook + /*! + Called on entry to enter() if the \c forScreensaver passed to it was + true. Override to perform platform specific operations. Default + does nothing. May \b not throw. + */ virtual void onEnterScreensaver(); + + //! Pre-leave() hook + /*! + Called on entry to leave() after desktop synchronization. Override + to perform platform specific operations. Default does nothing. May + \b not throw. + */ virtual void onPreLeave(); + + //! Post-leave() hook + /*! + Called on exit from leave(). \c success is the value returned by + showWindow(). Override to perform platform specific operations. + Default does nothing. May \b not throw. + */ virtual void onPostLeave(bool success); - // create/destroy the window. this window is generally used to - // receive events and, when the user navigates to another screen, - // to capture keyboard and mouse input. + //! Create window + /*! + Called to create the window. This window is generally used to + receive events, hide the cursor, and to capture keyboard and mouse + input. + */ virtual void createWindow() = 0; + + //! Destroy window + /*! + Called to destroy the window created by createWindow(). + */ virtual void destroyWindow() = 0; - // called when the user navigates off the primary screen. hide the - // cursor and grab exclusive access to the input devices. returns - // true iff successful. every call to showWindow() has a matching - // call to hideWindow() which preceeds it. return true iff - // successful (in particular, iff the input devices were grabbed). - // - // after a successful showWindow(), user input events and - // screensaver activation/deactivation should be reported to an - // IPrimaryScreenReceiver until hideWindow() is called. report - // mouse motion to IPrimaryScreenReceiver::onMouseMoveSecondary(). - // user input should not be delivered to any application except - // synergy. + //! Show window + /*! + Called when the user navigates off the primary screen. Hide the + cursor and grab exclusive access to the input devices. Every call + to showWindow() has a matching call to hideWindow() which preceeds + it. Return true iff successful (in particular, iff the input + devices were grabbed). + + After a successful showWindow(), user input events and + screensaver activation/deactivation should be reported to an + IPrimaryScreenReceiver (set through another interface) until + hideWindow() is called. Report mouse motion to its + onMouseMoveSecondary(). User input should not be delivered to + any application except this one. + */ virtual bool showWindow() = 0; - // called when the user navigates back to the primary screen. show - // the cursor and ungab the input devices. - // - // after hideWindow(), user input events should be delivered normally. - // mouse motion over (at least) the jump zones must be reported to - // an IPrimaryScreenReceiver::onMouseMovePrimary(). + //! Hide window + /*! + Called when the user navigates back to the primary screen. Show + the cursor and ungrab the input devices. + + After hideWindow(), user input events should be delivered normally + to other applications. Mouse motion over (at least) the jump zones + must be reported to an IPrimaryScreenReceiver's onMouseMovePrimary(). + */ virtual void hideWindow() = 0; - // prepare the cursor to report relative motion. when the user has - // navigated to another screen, synergy requires the cursor motion - // deltas, not the absolute coordinates. typically this is done by - // warping the cursor to the center of the primary screen and then - // every time it moves compute the motion and warp back to the - // center (but without reporting that warp as motion). this is - // only called after a successful showWindow(). + //! Warp cursor for relative motion + /*! + Prepare the cursor to report relative motion. When the user has + navigated to another screen, synergy requires the cursor motion + deltas, not the absolute coordinates. Typically this is done by + warping the cursor to the center of the primary screen and then + every time it moves compute the motion and warp back to the + center (but without reporting that warp as motion). This is + only called after a successful showWindow(). + */ virtual void warpCursorToCenter() = 0; - // check the current keyboard state. normally a screen will save - // the keyboard state in this method and use this shadow state - // when handling user input and in methods like isLockedToScreen(). + //! Synchronize key state + /*! + Check the current keyboard state. Normally a screen will save + the keyboard state in this method and use this shadow state + when handling user input and in methods like isLockedToScreen(). + */ virtual void updateKeys() = 0; private: diff --git a/server/CServer.h b/server/CServer.h index 0a34c9a6..a40823fd 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -20,39 +20,77 @@ class IServerProtocol; class ISocketFactory; class ISecurityFactory; +//! Synergy server +/*! +This class implements the top-level server algorithms for synergy. +*/ class CServer : public IServer, public IPrimaryScreenReceiver { public: + /*! + The server will look itself up in the configuration using \c serverName + as its name. + */ CServer(const CString& serverName); ~CServer(); - // manipulators + //! @name manipulators + //@{ - // open the server's screen + //! Open server + /*! + Open the server and return true iff successful. + */ bool open(); - // start the server. does not return until quit() is called. - // this must be preceeded by a successful call to open(). + //! Server main loop + /*! + Run server's event loop and return when quit() is called. + This must be called between a successful open() and close(). + + (cancellation point) + */ void run(); - // tell server to exit gracefully. this may only be called - // after a successful open(). + //! Exit event loop + /*! + Force run() to return. This call can return before + run() does (i.e. asynchronously). This may only be + called between a successful open() and close(). + */ void quit(); - // close the server's screen + //! Close server + /*! + Close the server. + */ void close(); - // update screen map. returns true iff the new configuration was - // accepted. + //! Set configuration + /*! + Change the server's configuration. Returns true iff the new + configuration was accepted (it must include the server's name). + This will disconnect any clients no longer in the configuration. + */ bool setConfig(const CConfig&); - // accessors + //@} + //! @name accessors + //@{ - // get the current screen map + //! Get configuration + /*! + Returns the current configuration. + */ void getConfig(CConfig*) const; - // get the primary screen's name + //! Get name + /*! + Returns the server's name passed to the c'tor + */ CString getPrimaryScreenName() const; + //@} + // IServer overrides virtual void onError(); virtual void onInfoChanged(const CString&, const CClientInfo&); @@ -71,6 +109,10 @@ public: virtual void onMouseWheel(SInt32 delta); protected: + //! Handle special keys + /*! + Handles keys with special meaning. + */ bool onCommandKey(KeyID, KeyModifierMask, bool down); private: diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index ad625ff7..08e244fd 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -14,6 +14,7 @@ class CXWindowsScreen; class IScreenReceiver; class IPrimaryScreenReceiver; +//! X11 primary screen implementation class CXWindowsPrimaryScreen : public CPrimaryScreen, public IScreenEventHandler { public: diff --git a/synergy/IClient.h b/synergy/IClient.h index a58f2859..c93c021c 100644 --- a/synergy/IClient.h +++ b/synergy/IClient.h @@ -23,10 +23,11 @@ public: */ virtual bool open() = 0; - //! Run client + //! Client main loop /*! - Service the client. This method is typically called in a separate - thread and is exited by cancelling the thread. + Run client's event loop. This method is typically called in a + separate thread and is exited by cancelling the thread. This + must be called between a successful open() and close(). (cancellation point) */ @@ -62,9 +63,10 @@ public: /*! Update the client's clipboard. This implies that the client's clipboard is now up to date. If the client's clipboard was - already known to be up to date then this may do nothing. + already known to be up to date then this may do nothing. \c data + has marshalled clipboard data. */ - virtual void setClipboard(ClipboardID, const CString&) = 0; + virtual void setClipboard(ClipboardID, const CString& data) = 0; //! Grab clipboard /*! @@ -152,7 +154,7 @@ public: //! Get screen shape /*! Return the position of the upper-left corner of the screen in \c x and - \c y and the size of the screen in \c w (width) and \c h (height). + \c y and the size of the screen in \c width and \c height. */ virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; diff --git a/synergy/IScreen.h b/synergy/IScreen.h index 7cb8b8d1..b60f56d6 100644 --- a/synergy/IScreen.h +++ b/synergy/IScreen.h @@ -33,7 +33,8 @@ public: //! Exit event loop /*! Force mainLoop() to return. This call can return before - mailLoop() does (i.e. asynchronously). + mainLoop() does (i.e. asynchronously). This may only be + called between a successful open() and close(). */ virtual void exitMainLoop() = 0; diff --git a/synergy/IScreenReceiver.h b/synergy/IScreenReceiver.h index 1cd2a79e..7ce2f7b3 100644 --- a/synergy/IScreenReceiver.h +++ b/synergy/IScreenReceiver.h @@ -41,7 +41,8 @@ public: //! Notify of new clipboard data /*! Called when the data on the clipboard has changed because some - other program has changed it. + other program has changed it. \c data will have marshalled + clipboard data. */ virtual void onClipboardChanged(ClipboardID, const CString& data) = 0; diff --git a/synergy/IServer.h b/synergy/IServer.h index 7dd819eb..b59b7af5 100644 --- a/synergy/IServer.h +++ b/synergy/IServer.h @@ -49,7 +49,8 @@ public: //! Notify of new clipboard data /*! Called when the data on the clipboard has changed because some - other program has changed it. + other program has changed it. \c data has the marshalled clipboard + data. */ virtual void onClipboardChanged(ClipboardID, UInt32 seqNum, const CString& data) = 0; From 9792d35a6bb915220a9ce00ff1e6839ff71967e3 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Jul 2002 15:17:44 +0000 Subject: [PATCH 288/807] Replaced method name `run' with `mainLoop', and `stop' and `quit' with `exitMainLoop' in most places. --- client/CClient.cpp | 14 +++++++------- client/CClient.h | 10 +++++----- client/CMSWindowsSecondaryScreen.cpp | 2 +- client/CMSWindowsSecondaryScreen.h | 2 +- client/CSecondaryScreen.cpp | 14 +++++++------- client/CSecondaryScreen.h | 24 ++++++++++++------------ client/CServerProxy.cpp | 2 +- client/CServerProxy.h | 2 +- client/CXWindowsSecondaryScreen.cpp | 2 +- client/CXWindowsSecondaryScreen.h | 2 +- client/client.cpp | 4 ++-- server/CClientProxy.h | 2 +- server/CClientProxy1_0.cpp | 2 +- server/CClientProxy1_0.h | 2 +- server/CMSWindowsPrimaryScreen.cpp | 4 ++-- server/CMSWindowsPrimaryScreen.h | 2 +- server/CPrimaryClient.cpp | 8 ++++---- server/CPrimaryClient.h | 8 ++++---- server/CPrimaryScreen.cpp | 14 +++++++------- server/CPrimaryScreen.h | 24 ++++++++++++------------ server/CServer.cpp | 16 ++++++++-------- server/CServer.h | 10 +++++----- server/CXWindowsPrimaryScreen.cpp | 2 +- server/CXWindowsPrimaryScreen.h | 2 +- server/server.cpp | 4 ++-- synergy/IClient.h | 2 +- 26 files changed, 90 insertions(+), 90 deletions(-) diff --git a/client/CClient.cpp b/client/CClient.cpp index 6efa48b0..54a28ee2 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -54,9 +54,9 @@ CClient::setAddress(const CNetworkAddress& serverAddress) } void -CClient::quit() +CClient::exitMainLoop() { - m_screen->stop(); + m_screen->exitMainLoop(); } bool @@ -133,7 +133,7 @@ CClient::open() } void -CClient::run() +CClient::mainLoop() { { CLock lock(&m_mutex); @@ -157,7 +157,7 @@ CClient::run() } // handle events - m_screen->run(); + m_screen->mainLoop(); // clean up deleteSession(); @@ -430,11 +430,11 @@ CClient::runSession(void*) try { log((CLOG_DEBUG "starting server proxy")); runServer(); - m_screen->stop(); + m_screen->exitMainLoop(); log((CLOG_DEBUG "stopping server proxy")); } catch (...) { - m_screen->stop(); + m_screen->exitMainLoop(); log((CLOG_DEBUG "stopping server proxy")); throw; } @@ -521,7 +521,7 @@ CClient::runServer() bool rejected = true; if (proxy != NULL) { log((CLOG_DEBUG1 "communicating with server")); - rejected = !proxy->run(); + rejected = !proxy->mainLoop(); } // clean up diff --git a/client/CClient.h b/client/CClient.h index 7b3925e0..e4be951f 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -34,7 +34,7 @@ public: Turns camping on or off. When camping the client will keep trying to connect to the server until it succeeds. This is useful if the client may start before the server. Do - not call this while in run(). + not call this while in mainLoop(). */ void camp(bool on); @@ -46,11 +46,11 @@ public: //! Exit event loop /*! - Force run() to return. This call can return before - run() does (i.e. asynchronously). This may only be + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be called between a successful open() and close(). */ - void quit(); + void exitMainLoop(); //@} //! @name accessors @@ -72,7 +72,7 @@ public: // IClient overrides virtual bool open(); - virtual void run(); + virtual void mainLoop(); virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index 93e8179d..21b17bac 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -258,7 +258,7 @@ CMSWindowsSecondaryScreen::preDestroyWindow(HWND) } void -CMSWindowsSecondaryScreen::onPreRun() +CMSWindowsSecondaryScreen::onPreMainLoop() { assert(m_window != NULL); } diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index 67d24694..b1d7575b 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -43,7 +43,7 @@ public: protected: // CSecondaryScreen overrides - virtual void onPreRun(); + virtual void onPreMainLoop(); virtual void onPreOpen(); virtual void onPreEnter(); virtual void onPreLeave(); diff --git a/client/CSecondaryScreen.cpp b/client/CSecondaryScreen.cpp index 9452ffae..91ea7c65 100644 --- a/client/CSecondaryScreen.cpp +++ b/client/CSecondaryScreen.cpp @@ -19,7 +19,7 @@ CSecondaryScreen::~CSecondaryScreen() } void -CSecondaryScreen::run() +CSecondaryScreen::mainLoop() { // change our priority CThread::getCurrentThread().setPriority(-7); @@ -27,20 +27,20 @@ CSecondaryScreen::run() // run event loop try { log((CLOG_DEBUG "entering event loop")); - onPreRun(); + onPreMainLoop(); getScreen()->mainLoop(); - onPostRun(); + onPostMainLoop(); log((CLOG_DEBUG "exiting event loop")); } catch (...) { - onPostRun(); + onPostMainLoop(); log((CLOG_DEBUG "exiting event loop")); throw; } } void -CSecondaryScreen::stop() +CSecondaryScreen::exitMainLoop() { getScreen()->exitMainLoop(); } @@ -200,13 +200,13 @@ CSecondaryScreen::getCursorPos(SInt32& x, SInt32& y) const } void -CSecondaryScreen::onPreRun() +CSecondaryScreen::onPreMainLoop() { // do nothing } void -CSecondaryScreen::onPostRun() +CSecondaryScreen::onPostMainLoop() { // do nothing } diff --git a/client/CSecondaryScreen.h b/client/CSecondaryScreen.h index 5c4d9fe8..1ff70a29 100644 --- a/client/CSecondaryScreen.h +++ b/client/CSecondaryScreen.h @@ -37,17 +37,17 @@ public: //! Run event loop /*! Run the screen's event loop. This returns when it detects - the application should terminate or when stop() is called. - run() may only be called between open() and close(). + the application should terminate or when exitMainLoop() is called. + mainLoop() may only be called between open() and close(). */ - void run(); + void mainLoop(); //! Exit event loop /*! - Force run() to return. This call can return before - run() does (i.e. asynchronously). + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). */ - void stop(); + void exitMainLoop(); //! Close screen /*! @@ -189,19 +189,19 @@ public: //@} protected: - //! Pre-run() hook + //! Pre-mainLoop() hook /*! - Called on entry to run(). Override to perform platform specific + Called on entry to mainLoop(). Override to perform platform specific operations. Default does nothing. May throw. */ - virtual void onPreRun(); + virtual void onPreMainLoop(); - //! Post-run() hook + //! Post-mainLoop() hook /*! - Called on exit from run(). Override to perform platform specific + Called on exit from mainLoop(). Override to perform platform specific operations. Default does nothing. May \b not throw. */ - virtual void onPostRun(); + virtual void onPostMainLoop(); //! Pre-open() hook /*! diff --git a/client/CServerProxy.cpp b/client/CServerProxy.cpp index bec9d1ce..ce094faf 100644 --- a/client/CServerProxy.cpp +++ b/client/CServerProxy.cpp @@ -33,7 +33,7 @@ CServerProxy::~CServerProxy() } bool -CServerProxy::run() +CServerProxy::mainLoop() { bool failedToConnect = false; try { diff --git a/client/CServerProxy.h b/client/CServerProxy.h index d0eecc21..145da7f1 100644 --- a/client/CServerProxy.h +++ b/client/CServerProxy.h @@ -36,7 +36,7 @@ public: (cancellation point) */ - bool run(); + bool mainLoop(); //@} //! @name accessors diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 968cf5cf..4c921927 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -184,7 +184,7 @@ CXWindowsSecondaryScreen::getJumpZoneSize() const } void -CXWindowsSecondaryScreen::onPreRun() +CXWindowsSecondaryScreen::onPreMainLoop() { assert(m_window != None); } diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 113eb03e..30facac4 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -39,7 +39,7 @@ public: protected: // CSecondaryScreen overrides - virtual void onPreRun(); + virtual void onPreMainLoop(); virtual void onPreOpen(); virtual void onPostOpen(); virtual void onPreEnter(); diff --git a/client/client.cpp b/client/client.cpp index cc2243ed..a8b5b3f9 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -95,7 +95,7 @@ realMain(CMutex* mutex) mutex->unlock(); } locked = false; - s_client->run(); + s_client->mainLoop(); locked = true; if (mutex != NULL) { mutex->lock(); @@ -470,7 +470,7 @@ static void daemonStop(void) { - s_client->quit(); + s_client->exitMainLoop(); } static diff --git a/server/CClientProxy.h b/server/CClientProxy.h index 7d7c4c31..83cb3e14 100644 --- a/server/CClientProxy.h +++ b/server/CClientProxy.h @@ -44,7 +44,7 @@ public: // IClient overrides virtual bool open() = 0; - virtual void run() = 0; + virtual void mainLoop() = 0; virtual void close() = 0; virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, diff --git a/server/CClientProxy1_0.cpp b/server/CClientProxy1_0.cpp index 4a969b93..88b8b429 100644 --- a/server/CClientProxy1_0.cpp +++ b/server/CClientProxy1_0.cpp @@ -60,7 +60,7 @@ CClientProxy1_0::open() } void -CClientProxy1_0::run() +CClientProxy1_0::mainLoop() { // handle messages until the client hangs up or stops sending heartbeats CStopwatch heartTimer; diff --git a/server/CClientProxy1_0.h b/server/CClientProxy1_0.h index 0f5679a0..befb480c 100644 --- a/server/CClientProxy1_0.h +++ b/server/CClientProxy1_0.h @@ -15,7 +15,7 @@ public: // IClient overrides virtual bool open(); - virtual void run(); + virtual void mainLoop(); virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index fa78e8fb..3b257840 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -384,9 +384,9 @@ CMSWindowsPrimaryScreen::preDestroyWindow(HWND) } void -CMSWindowsPrimaryScreen::onPreRun() +CMSWindowsPrimaryScreen::onPreMainLoop() { - // must call run() from same thread as open() + // must call mainLoop() from same thread as open() assert(m_threadID == GetCurrentThreadId()); assert(m_window != NULL); } diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 30c06f5d..aa61b492 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -37,7 +37,7 @@ public: protected: // CPrimaryScreen overrides - virtual void onPreRun(); + virtual void onPreMainLoop(); virtual void onPreOpen(); virtual void onPostOpen(); virtual void onPostClose(); diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index 1f53cb01..a1e3113d 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -40,9 +40,9 @@ CPrimaryClient::~CPrimaryClient() } void -CPrimaryClient::stop() +CPrimaryClient::exitMainLoop() { - m_screen->stop(); + m_screen->exitMainLoop(); } void @@ -122,9 +122,9 @@ CPrimaryClient::open() } void -CPrimaryClient::run() +CPrimaryClient::mainLoop() { - m_screen->run(); + m_screen->mainLoop(); } void diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h index 468133d0..6631203e 100644 --- a/server/CPrimaryClient.h +++ b/server/CPrimaryClient.h @@ -29,11 +29,11 @@ public: //! Exit event loop /*! - Force run() to return. This call can return before - run() does (i.e. asynchronously). This may only be + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be called between a successful open() and close(). */ - void stop(); + void exitMainLoop(); //! Update configuration /*! @@ -73,7 +73,7 @@ public: // IClient overrides virtual bool open(); - virtual void run(); + virtual void mainLoop(); virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, diff --git a/server/CPrimaryScreen.cpp b/server/CPrimaryScreen.cpp index 5c8369e0..d8f1bc9b 100644 --- a/server/CPrimaryScreen.cpp +++ b/server/CPrimaryScreen.cpp @@ -23,7 +23,7 @@ CPrimaryScreen::~CPrimaryScreen() } void -CPrimaryScreen::run() +CPrimaryScreen::mainLoop() { // change our priority CThread::getCurrentThread().setPriority(-3); @@ -31,20 +31,20 @@ CPrimaryScreen::run() // run event loop try { log((CLOG_DEBUG "entering event loop")); - onPreRun(); + onPreMainLoop(); getScreen()->mainLoop(); - onPostRun(); + onPostMainLoop(); log((CLOG_DEBUG "exiting event loop")); } catch (...) { - onPostRun(); + onPostMainLoop(); log((CLOG_DEBUG "exiting event loop")); throw; } } void -CPrimaryScreen::stop() +CPrimaryScreen::exitMainLoop() { getScreen()->exitMainLoop(); } @@ -198,13 +198,13 @@ CPrimaryScreen::getClipboard(ClipboardID id, } void -CPrimaryScreen::onPreRun() +CPrimaryScreen::onPreMainLoop() { // do nothing } void -CPrimaryScreen::onPostRun() +CPrimaryScreen::onPostMainLoop() { // do nothing } diff --git a/server/CPrimaryScreen.h b/server/CPrimaryScreen.h index 1bde5f92..772f88f9 100644 --- a/server/CPrimaryScreen.h +++ b/server/CPrimaryScreen.h @@ -37,17 +37,17 @@ public: //! Run event loop /*! Run the screen's event loop. This returns when it detects - the application should terminate or when stop() is called. - run() may only be called between open() and close(). + the application should terminate or when exitMainLoop() is called. + mainLoop() may only be called between open() and close(). */ - void run(); + void mainLoop(); //! Exit event loop /*! - Force run() to return. This call can return before - run() does (i.e. asynchronously). + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). */ - void stop(); + void exitMainLoop(); //! Close screen /*! @@ -152,19 +152,19 @@ public: //@} protected: - //! Pre-run() hook + //! Pre-mainLoop() hook /*! - Called on entry to run(). Override to perform platform specific + Called on entry to mainLoop(). Override to perform platform specific operations. Default does nothing. May throw. */ - virtual void onPreRun(); + virtual void onPreMainLoop(); - //! Post-run() hook + //! Post-mainLoop() hook /*! - Called on exit from run(). Override to perform platform specific + Called on exit from mainLoop(). Override to perform platform specific operations. Default does nothing. May \b not throw. */ - virtual void onPostRun(); + virtual void onPostMainLoop(); //! Pre-open() hook /*! diff --git a/server/CServer.cpp b/server/CServer.cpp index 9c4f3467..a4138b1e 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -70,7 +70,7 @@ CServer::open() } void -CServer::run() +CServer::mainLoop() { // check preconditions { @@ -94,7 +94,7 @@ CServer::run() } // handle events - m_primaryClient->run(); + m_primaryClient->mainLoop(); // clean up log((CLOG_NOTE "stopping server")); @@ -137,9 +137,9 @@ CServer::run() } void -CServer::quit() +CServer::exitMainLoop() { - m_primaryClient->stop(); + m_primaryClient->exitMainLoop(); } void @@ -1167,7 +1167,7 @@ CServer::acceptClients(void*) catch (XBase& e) { log((CLOG_ERR "cannot listen for clients: %s", e.what())); delete listen; - quit(); + exitMainLoop(); } catch (...) { delete listen; @@ -1238,7 +1238,7 @@ CServer::runClient(void* vsocket) // handle client messages try { log((CLOG_NOTE "client \"%s\" has connected", proxy->getName().c_str())); - proxy->run(); + proxy->mainLoop(); } catch (XBadClient&) { // client not behaving @@ -1252,7 +1252,7 @@ CServer::runClient(void* vsocket) // FIXME -- could print network address if socket had suitable method } catch (...) { - // run() was probably cancelled + // mainLoop() was probably cancelled removeConnection(proxy->getName()); delete socket; throw; @@ -1463,7 +1463,7 @@ CServer::acceptHTTPClients(void*) log((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); delete listen; // FIXME -- quit? - quit(); + exitMainLoop(); } catch (...) { delete listen; diff --git a/server/CServer.h b/server/CServer.h index a40823fd..43d58879 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -44,20 +44,20 @@ public: //! Server main loop /*! - Run server's event loop and return when quit() is called. + Run server's event loop and return when exitMainLoop() is called. This must be called between a successful open() and close(). (cancellation point) */ - void run(); + void mainLoop(); //! Exit event loop /*! - Force run() to return. This call can return before - run() does (i.e. asynchronously). This may only be + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). This may only be called between a successful open() and close(). */ - void quit(); + void exitMainLoop(); //! Close server /*! diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index ba4e8a39..e1463aee 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -332,7 +332,7 @@ CXWindowsPrimaryScreen::getJumpZoneSize() const } void -CXWindowsPrimaryScreen::onPreRun() +CXWindowsPrimaryScreen::onPreMainLoop() { assert(m_window != None); } diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 08e244fd..b9b4ce22 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -36,7 +36,7 @@ public: protected: // CPrimaryScreen overrides - virtual void onPreRun(); + virtual void onPreMainLoop(); virtual void onPreOpen(); virtual void onPostOpen(); virtual void onPreEnter(); diff --git a/server/server.cpp b/server/server.cpp index c49d4a8e..0a308d34 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -125,7 +125,7 @@ realMain(CMutex* mutex) mutex->unlock(); } locked = false; - s_server->run(); + s_server->mainLoop(); locked = true; if (mutex != NULL) { mutex->lock(); @@ -568,7 +568,7 @@ static void daemonStop(void) { - s_server->quit(); + s_server->exitMainLoop(); } static diff --git a/synergy/IClient.h b/synergy/IClient.h index c93c021c..01027abb 100644 --- a/synergy/IClient.h +++ b/synergy/IClient.h @@ -31,7 +31,7 @@ public: (cancellation point) */ - virtual void run() = 0; + virtual void mainLoop() = 0; //! Close client /*! From fee40956243e594cf70135dfb7c5fe67b9880cf7 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Jul 2002 16:52:46 +0000 Subject: [PATCH 289/807] Reorganized source tree. Moved client.cpp into cmd/synergy as synergy.cpp and server.cpp into cmd/synergyd as synergyd.cpp. Moved and renamed related files. Moved remaining source files into lib/.... Modified and added makefiles as appropriate. Result is that library files are under lib with each library in its own directory and program files are under cmd with each command in its own directory. --- Makefile.am | 11 +- client/Makefile.am | 38 ---- cmd/Makefile.am | 9 + cmd/synergy/Makefile.am | 32 +++ {client => cmd/synergy}/resource.h | 0 client/client.cpp => cmd/synergy/synergy.cpp | 0 client/client.dsp => cmd/synergy/synergy.dsp | 68 ++---- client/client.rc => cmd/synergy/synergy.rc | 0 cmd/synergyd/Makefile.am | 34 +++ {server => cmd/synergyd}/resource.h | 0 .../server.cpp => cmd/synergyd/synergyd.cpp | 0 .../server.dsp => cmd/synergyd/synergyd.dsp | 100 ++------- server/server.rc => cmd/synergyd/synergyd.rc | 0 configure.in | 24 ++- io/Makefile.am | 25 --- lib/Makefile.am | 16 ++ {base => lib/base}/BasicTypes.h | 0 {base => lib/base}/CFunctionJob.cpp | 0 {base => lib/base}/CFunctionJob.h | 0 {base => lib/base}/CLog.cpp | 0 {base => lib/base}/CLog.h | 0 {base => lib/base}/CStopwatch.cpp | 0 {base => lib/base}/CStopwatch.h | 0 {base => lib/base}/CString.cpp | 1 + {base => lib/base}/CString.h | 0 {base => lib/base}/CUnicode.cpp | 0 {base => lib/base}/CUnicode.h | 0 {base => lib/base}/IInterface.h | 0 {base => lib/base}/IJob.h | 0 {base => lib/base}/Makefile.am | 2 +- {base => lib/base}/TMethodJob.h | 0 {base => lib/base}/XBase.cpp | 0 {base => lib/base}/XBase.h | 0 {base => lib/base}/base.dsp | 0 {base => lib/base}/common.h | 0 {base => lib/base}/stdfstream.h | 0 {base => lib/base}/stdistream.h | 0 {base => lib/base}/stdlist.h | 0 {base => lib/base}/stdmap.h | 0 {base => lib/base}/stdostream.h | 0 {base => lib/base}/stdpost.h | 0 {base => lib/base}/stdpre.h | 0 {base => lib/base}/stdset.h | 0 {base => lib/base}/stdsstream.h | 0 {base => lib/base}/stdvector.h | 0 {client => lib/client}/CClient.cpp | 0 {client => lib/client}/CClient.h | 0 .../client}/CMSWindowsSecondaryScreen.cpp | 0 .../client}/CMSWindowsSecondaryScreen.h | 0 {client => lib/client}/CSecondaryScreen.cpp | 0 {client => lib/client}/CSecondaryScreen.h | 0 {client => lib/client}/CServerProxy.cpp | 0 {client => lib/client}/CServerProxy.h | 0 .../client}/CXWindowsSecondaryScreen.cpp | 0 .../client}/CXWindowsSecondaryScreen.h | 0 lib/client/Makefile.am | 23 +++ {http => lib/http}/CHTTPProtocol.cpp | 0 {http => lib/http}/CHTTPProtocol.h | 0 {http => lib/http}/Makefile.am | 8 +- {http => lib/http}/XHTTP.cpp | 0 {http => lib/http}/XHTTP.h | 0 {http => lib/http}/http.dsp | 0 {io => lib/io}/CBufferedInputStream.cpp | 0 {io => lib/io}/CBufferedInputStream.h | 0 {io => lib/io}/CBufferedOutputStream.cpp | 0 {io => lib/io}/CBufferedOutputStream.h | 0 {io => lib/io}/CInputStreamFilter.cpp | 0 {io => lib/io}/CInputStreamFilter.h | 0 {io => lib/io}/COutputStreamFilter.cpp | 0 {io => lib/io}/COutputStreamFilter.h | 0 {io => lib/io}/CStreamBuffer.cpp | 0 {io => lib/io}/CStreamBuffer.h | 0 {io => lib/io}/IInputStream.h | 0 {io => lib/io}/IOutputStream.h | 0 lib/io/Makefile.am | 25 +++ {io => lib/io}/XIO.cpp | 0 {io => lib/io}/XIO.h | 0 {io => lib/io}/io.dsp | 0 {mt => lib/mt}/CCondVar.cpp | 0 {mt => lib/mt}/CCondVar.h | 0 {mt => lib/mt}/CLock.cpp | 0 {mt => lib/mt}/CLock.h | 0 {mt => lib/mt}/CMutex.cpp | 0 {mt => lib/mt}/CMutex.h | 0 {mt => lib/mt}/CThread.cpp | 0 {mt => lib/mt}/CThread.h | 0 {mt => lib/mt}/CThreadRep.cpp | 0 {mt => lib/mt}/CThreadRep.h | 0 {mt => lib/mt}/CTimerThread.cpp | 0 {mt => lib/mt}/CTimerThread.h | 0 {mt => lib/mt}/Makefile.am | 4 +- {mt => lib/mt}/XThread.h | 0 {mt => lib/mt}/mt.dsp | 0 {net => lib/net}/CNetwork.cpp | 0 {net => lib/net}/CNetwork.h | 0 {net => lib/net}/CNetworkAddress.cpp | 0 {net => lib/net}/CNetworkAddress.h | 0 {net => lib/net}/CTCPListenSocket.cpp | 0 {net => lib/net}/CTCPListenSocket.h | 0 {net => lib/net}/CTCPSocket.cpp | 0 {net => lib/net}/CTCPSocket.h | 0 {net => lib/net}/IDataSocket.h | 0 {net => lib/net}/IListenSocket.h | 0 {net => lib/net}/ISocket.h | 0 {net => lib/net}/Makefile.am | 8 +- {net => lib/net}/XNetwork.cpp | 0 {net => lib/net}/XNetwork.h | 0 {net => lib/net}/XSocket.cpp | 0 {net => lib/net}/XSocket.h | 0 {net => lib/net}/net.dsp | 0 .../platform}/CMSWindowsClipboard.cpp | 0 .../platform}/CMSWindowsClipboard.h | 0 .../CMSWindowsClipboardAnyTextConverter.cpp | 0 .../CMSWindowsClipboardAnyTextConverter.h | 0 .../CMSWindowsClipboardTextConverter.cpp | 0 .../CMSWindowsClipboardTextConverter.h | 0 .../CMSWindowsClipboardUTF16Converter.cpp | 0 .../CMSWindowsClipboardUTF16Converter.h | 0 .../platform}/CMSWindowsScreen.cpp | 0 {platform => lib/platform}/CMSWindowsScreen.h | 0 .../platform}/CMSWindowsScreenSaver.cpp | 0 .../platform}/CMSWindowsScreenSaver.h | 0 {platform => lib/platform}/CPlatform.cpp | 0 {platform => lib/platform}/CPlatform.h | 0 {platform => lib/platform}/CSynergyHook.cpp | 0 {platform => lib/platform}/CSynergyHook.h | 0 {platform => lib/platform}/CUnixPlatform.cpp | 0 {platform => lib/platform}/CUnixPlatform.h | 0 {platform => lib/platform}/CWin32Platform.cpp | 0 {platform => lib/platform}/CWin32Platform.h | 0 .../platform}/CXWindowsClipboard.cpp | 0 .../platform}/CXWindowsClipboard.h | 0 .../CXWindowsClipboardTextConverter.cpp | 0 .../CXWindowsClipboardTextConverter.h | 0 .../CXWindowsClipboardUCS2Converter.cpp | 0 .../CXWindowsClipboardUCS2Converter.h | 0 .../CXWindowsClipboardUTF8Converter.cpp | 0 .../CXWindowsClipboardUTF8Converter.h | 0 .../platform}/CXWindowsScreen.cpp | 0 {platform => lib/platform}/CXWindowsScreen.h | 0 .../platform}/CXWindowsScreenSaver.cpp | 0 .../platform}/CXWindowsScreenSaver.h | 0 {platform => lib/platform}/CXWindowsUtil.cpp | 0 {platform => lib/platform}/CXWindowsUtil.h | 0 .../platform}/IMSWindowsScreenEventHandler.h | 0 {platform => lib/platform}/IPlatform.h | 0 {platform => lib/platform}/Makefile.am | 8 +- {platform => lib/platform}/makehook.dsp | 0 {platform => lib/platform}/platform.dsp | 0 {platform => lib/platform}/synrgyhk.dsp | 4 +- {server => lib/server}/CClientProxy.cpp | 0 {server => lib/server}/CClientProxy.h | 0 {server => lib/server}/CClientProxy1_0.cpp | 0 {server => lib/server}/CClientProxy1_0.h | 0 {server => lib/server}/CConfig.cpp | 0 {server => lib/server}/CConfig.h | 0 {server => lib/server}/CHTTPServer.cpp | 0 {server => lib/server}/CHTTPServer.h | 0 .../server}/CMSWindowsPrimaryScreen.cpp | 0 .../server}/CMSWindowsPrimaryScreen.h | 0 {server => lib/server}/CPrimaryClient.cpp | 0 {server => lib/server}/CPrimaryClient.h | 0 {server => lib/server}/CPrimaryScreen.cpp | 0 {server => lib/server}/CPrimaryScreen.h | 0 {server => lib/server}/CServer.cpp | 0 {server => lib/server}/CServer.h | 0 .../server}/CXWindowsPrimaryScreen.cpp | 0 .../server}/CXWindowsPrimaryScreen.h | 0 lib/server/Makefile.am | 32 +++ {synergy => lib/synergy}/CClipboard.cpp | 0 {synergy => lib/synergy}/CClipboard.h | 0 .../synergy}/CInputPacketStream.cpp | 0 {synergy => lib/synergy}/CInputPacketStream.h | 0 .../synergy}/COutputPacketStream.cpp | 0 .../synergy}/COutputPacketStream.h | 0 {synergy => lib/synergy}/CProtocolUtil.cpp | 0 {synergy => lib/synergy}/CProtocolUtil.h | 0 .../synergy}/CTCPSocketFactory.cpp | 0 {synergy => lib/synergy}/CTCPSocketFactory.h | 0 {synergy => lib/synergy}/ClipboardTypes.h | 0 {synergy => lib/synergy}/IClient.h | 0 {synergy => lib/synergy}/IClipboard.h | 0 .../synergy}/IPrimaryScreenReceiver.h | 0 {synergy => lib/synergy}/IScreen.h | 0 .../synergy}/IScreenEventHandler.h | 0 {synergy => lib/synergy}/IScreenReceiver.h | 0 {synergy => lib/synergy}/IScreenSaver.h | 0 {synergy => lib/synergy}/IServer.h | 0 {synergy => lib/synergy}/ISocketFactory.h | 0 {synergy => lib/synergy}/KeyTypes.h | 0 {synergy => lib/synergy}/Makefile.am | 10 +- {synergy => lib/synergy}/MouseTypes.h | 0 {synergy => lib/synergy}/ProtocolTypes.h | 0 {synergy => lib/synergy}/Version.h | 0 {synergy => lib/synergy}/XScreen.cpp | 0 {synergy => lib/synergy}/XScreen.h | 0 {synergy => lib/synergy}/XSynergy.cpp | 0 {synergy => lib/synergy}/XSynergy.h | 0 .../synergy.dsp => lib/synergy/libsynergy.dsp | 18 +- server/Makefile.am | 48 ----- synergy.dsw | 194 ++++++++++-------- 201 files changed, 367 insertions(+), 375 deletions(-) delete mode 100644 client/Makefile.am create mode 100644 cmd/Makefile.am create mode 100644 cmd/synergy/Makefile.am rename {client => cmd/synergy}/resource.h (100%) rename client/client.cpp => cmd/synergy/synergy.cpp (100%) rename client/client.dsp => cmd/synergy/synergy.dsp (70%) rename client/client.rc => cmd/synergy/synergy.rc (100%) create mode 100644 cmd/synergyd/Makefile.am rename {server => cmd/synergyd}/resource.h (100%) rename server/server.cpp => cmd/synergyd/synergyd.cpp (100%) rename server/server.dsp => cmd/synergyd/synergyd.dsp (60%) rename server/server.rc => cmd/synergyd/synergyd.rc (100%) delete mode 100644 io/Makefile.am create mode 100644 lib/Makefile.am rename {base => lib/base}/BasicTypes.h (100%) rename {base => lib/base}/CFunctionJob.cpp (100%) rename {base => lib/base}/CFunctionJob.h (100%) rename {base => lib/base}/CLog.cpp (100%) rename {base => lib/base}/CLog.h (100%) rename {base => lib/base}/CStopwatch.cpp (100%) rename {base => lib/base}/CStopwatch.h (100%) rename {base => lib/base}/CString.cpp (99%) rename {base => lib/base}/CString.h (100%) rename {base => lib/base}/CUnicode.cpp (100%) rename {base => lib/base}/CUnicode.h (100%) rename {base => lib/base}/IInterface.h (100%) rename {base => lib/base}/IJob.h (100%) rename {base => lib/base}/Makefile.am (97%) rename {base => lib/base}/TMethodJob.h (100%) rename {base => lib/base}/XBase.cpp (100%) rename {base => lib/base}/XBase.h (100%) rename {base => lib/base}/base.dsp (100%) rename {base => lib/base}/common.h (100%) rename {base => lib/base}/stdfstream.h (100%) rename {base => lib/base}/stdistream.h (100%) rename {base => lib/base}/stdlist.h (100%) rename {base => lib/base}/stdmap.h (100%) rename {base => lib/base}/stdostream.h (100%) rename {base => lib/base}/stdpost.h (100%) rename {base => lib/base}/stdpre.h (100%) rename {base => lib/base}/stdset.h (100%) rename {base => lib/base}/stdsstream.h (100%) rename {base => lib/base}/stdvector.h (100%) rename {client => lib/client}/CClient.cpp (100%) rename {client => lib/client}/CClient.h (100%) rename {client => lib/client}/CMSWindowsSecondaryScreen.cpp (100%) rename {client => lib/client}/CMSWindowsSecondaryScreen.h (100%) rename {client => lib/client}/CSecondaryScreen.cpp (100%) rename {client => lib/client}/CSecondaryScreen.h (100%) rename {client => lib/client}/CServerProxy.cpp (100%) rename {client => lib/client}/CServerProxy.h (100%) rename {client => lib/client}/CXWindowsSecondaryScreen.cpp (100%) rename {client => lib/client}/CXWindowsSecondaryScreen.h (100%) create mode 100644 lib/client/Makefile.am rename {http => lib/http}/CHTTPProtocol.cpp (100%) rename {http => lib/http}/CHTTPProtocol.h (100%) rename {http => lib/http}/Makefile.am (73%) rename {http => lib/http}/XHTTP.cpp (100%) rename {http => lib/http}/XHTTP.h (100%) rename {http => lib/http}/http.dsp (100%) rename {io => lib/io}/CBufferedInputStream.cpp (100%) rename {io => lib/io}/CBufferedInputStream.h (100%) rename {io => lib/io}/CBufferedOutputStream.cpp (100%) rename {io => lib/io}/CBufferedOutputStream.h (100%) rename {io => lib/io}/CInputStreamFilter.cpp (100%) rename {io => lib/io}/CInputStreamFilter.h (100%) rename {io => lib/io}/COutputStreamFilter.cpp (100%) rename {io => lib/io}/COutputStreamFilter.h (100%) rename {io => lib/io}/CStreamBuffer.cpp (100%) rename {io => lib/io}/CStreamBuffer.h (100%) rename {io => lib/io}/IInputStream.h (100%) rename {io => lib/io}/IOutputStream.h (100%) create mode 100644 lib/io/Makefile.am rename {io => lib/io}/XIO.cpp (100%) rename {io => lib/io}/XIO.h (100%) rename {io => lib/io}/io.dsp (100%) rename {mt => lib/mt}/CCondVar.cpp (100%) rename {mt => lib/mt}/CCondVar.h (100%) rename {mt => lib/mt}/CLock.cpp (100%) rename {mt => lib/mt}/CLock.h (100%) rename {mt => lib/mt}/CMutex.cpp (100%) rename {mt => lib/mt}/CMutex.h (100%) rename {mt => lib/mt}/CThread.cpp (100%) rename {mt => lib/mt}/CThread.h (100%) rename {mt => lib/mt}/CThreadRep.cpp (100%) rename {mt => lib/mt}/CThreadRep.h (100%) rename {mt => lib/mt}/CTimerThread.cpp (100%) rename {mt => lib/mt}/CTimerThread.h (100%) rename {mt => lib/mt}/Makefile.am (90%) rename {mt => lib/mt}/XThread.h (100%) rename {mt => lib/mt}/mt.dsp (100%) rename {net => lib/net}/CNetwork.cpp (100%) rename {net => lib/net}/CNetwork.h (100%) rename {net => lib/net}/CNetworkAddress.cpp (100%) rename {net => lib/net}/CNetworkAddress.h (100%) rename {net => lib/net}/CTCPListenSocket.cpp (100%) rename {net => lib/net}/CTCPListenSocket.h (100%) rename {net => lib/net}/CTCPSocket.cpp (100%) rename {net => lib/net}/CTCPSocket.h (100%) rename {net => lib/net}/IDataSocket.h (100%) rename {net => lib/net}/IListenSocket.h (100%) rename {net => lib/net}/ISocket.h (100%) rename {net => lib/net}/Makefile.am (84%) rename {net => lib/net}/XNetwork.cpp (100%) rename {net => lib/net}/XNetwork.h (100%) rename {net => lib/net}/XSocket.cpp (100%) rename {net => lib/net}/XSocket.h (100%) rename {net => lib/net}/net.dsp (100%) rename {platform => lib/platform}/CMSWindowsClipboard.cpp (100%) rename {platform => lib/platform}/CMSWindowsClipboard.h (100%) rename {platform => lib/platform}/CMSWindowsClipboardAnyTextConverter.cpp (100%) rename {platform => lib/platform}/CMSWindowsClipboardAnyTextConverter.h (100%) rename {platform => lib/platform}/CMSWindowsClipboardTextConverter.cpp (100%) rename {platform => lib/platform}/CMSWindowsClipboardTextConverter.h (100%) rename {platform => lib/platform}/CMSWindowsClipboardUTF16Converter.cpp (100%) rename {platform => lib/platform}/CMSWindowsClipboardUTF16Converter.h (100%) rename {platform => lib/platform}/CMSWindowsScreen.cpp (100%) rename {platform => lib/platform}/CMSWindowsScreen.h (100%) rename {platform => lib/platform}/CMSWindowsScreenSaver.cpp (100%) rename {platform => lib/platform}/CMSWindowsScreenSaver.h (100%) rename {platform => lib/platform}/CPlatform.cpp (100%) rename {platform => lib/platform}/CPlatform.h (100%) rename {platform => lib/platform}/CSynergyHook.cpp (100%) rename {platform => lib/platform}/CSynergyHook.h (100%) rename {platform => lib/platform}/CUnixPlatform.cpp (100%) rename {platform => lib/platform}/CUnixPlatform.h (100%) rename {platform => lib/platform}/CWin32Platform.cpp (100%) rename {platform => lib/platform}/CWin32Platform.h (100%) rename {platform => lib/platform}/CXWindowsClipboard.cpp (100%) rename {platform => lib/platform}/CXWindowsClipboard.h (100%) rename {platform => lib/platform}/CXWindowsClipboardTextConverter.cpp (100%) rename {platform => lib/platform}/CXWindowsClipboardTextConverter.h (100%) rename {platform => lib/platform}/CXWindowsClipboardUCS2Converter.cpp (100%) rename {platform => lib/platform}/CXWindowsClipboardUCS2Converter.h (100%) rename {platform => lib/platform}/CXWindowsClipboardUTF8Converter.cpp (100%) rename {platform => lib/platform}/CXWindowsClipboardUTF8Converter.h (100%) rename {platform => lib/platform}/CXWindowsScreen.cpp (100%) rename {platform => lib/platform}/CXWindowsScreen.h (100%) rename {platform => lib/platform}/CXWindowsScreenSaver.cpp (100%) rename {platform => lib/platform}/CXWindowsScreenSaver.h (100%) rename {platform => lib/platform}/CXWindowsUtil.cpp (100%) rename {platform => lib/platform}/CXWindowsUtil.h (100%) rename {platform => lib/platform}/IMSWindowsScreenEventHandler.h (100%) rename {platform => lib/platform}/IPlatform.h (100%) rename {platform => lib/platform}/Makefile.am (88%) rename {platform => lib/platform}/makehook.dsp (100%) rename {platform => lib/platform}/platform.dsp (100%) rename {platform => lib/platform}/synrgyhk.dsp (98%) rename {server => lib/server}/CClientProxy.cpp (100%) rename {server => lib/server}/CClientProxy.h (100%) rename {server => lib/server}/CClientProxy1_0.cpp (100%) rename {server => lib/server}/CClientProxy1_0.h (100%) rename {server => lib/server}/CConfig.cpp (100%) rename {server => lib/server}/CConfig.h (100%) rename {server => lib/server}/CHTTPServer.cpp (100%) rename {server => lib/server}/CHTTPServer.h (100%) rename {server => lib/server}/CMSWindowsPrimaryScreen.cpp (100%) rename {server => lib/server}/CMSWindowsPrimaryScreen.h (100%) rename {server => lib/server}/CPrimaryClient.cpp (100%) rename {server => lib/server}/CPrimaryClient.h (100%) rename {server => lib/server}/CPrimaryScreen.cpp (100%) rename {server => lib/server}/CPrimaryScreen.h (100%) rename {server => lib/server}/CServer.cpp (100%) rename {server => lib/server}/CServer.h (100%) rename {server => lib/server}/CXWindowsPrimaryScreen.cpp (100%) rename {server => lib/server}/CXWindowsPrimaryScreen.h (100%) create mode 100644 lib/server/Makefile.am rename {synergy => lib/synergy}/CClipboard.cpp (100%) rename {synergy => lib/synergy}/CClipboard.h (100%) rename {synergy => lib/synergy}/CInputPacketStream.cpp (100%) rename {synergy => lib/synergy}/CInputPacketStream.h (100%) rename {synergy => lib/synergy}/COutputPacketStream.cpp (100%) rename {synergy => lib/synergy}/COutputPacketStream.h (100%) rename {synergy => lib/synergy}/CProtocolUtil.cpp (100%) rename {synergy => lib/synergy}/CProtocolUtil.h (100%) rename {synergy => lib/synergy}/CTCPSocketFactory.cpp (100%) rename {synergy => lib/synergy}/CTCPSocketFactory.h (100%) rename {synergy => lib/synergy}/ClipboardTypes.h (100%) rename {synergy => lib/synergy}/IClient.h (100%) rename {synergy => lib/synergy}/IClipboard.h (100%) rename {synergy => lib/synergy}/IPrimaryScreenReceiver.h (100%) rename {synergy => lib/synergy}/IScreen.h (100%) rename {synergy => lib/synergy}/IScreenEventHandler.h (100%) rename {synergy => lib/synergy}/IScreenReceiver.h (100%) rename {synergy => lib/synergy}/IScreenSaver.h (100%) rename {synergy => lib/synergy}/IServer.h (100%) rename {synergy => lib/synergy}/ISocketFactory.h (100%) rename {synergy => lib/synergy}/KeyTypes.h (100%) rename {synergy => lib/synergy}/Makefile.am (87%) rename {synergy => lib/synergy}/MouseTypes.h (100%) rename {synergy => lib/synergy}/ProtocolTypes.h (100%) rename {synergy => lib/synergy}/Version.h (100%) rename {synergy => lib/synergy}/XScreen.cpp (100%) rename {synergy => lib/synergy}/XScreen.h (100%) rename {synergy => lib/synergy}/XSynergy.cpp (100%) rename {synergy => lib/synergy}/XSynergy.h (100%) rename synergy/synergy.dsp => lib/synergy/libsynergy.dsp (89%) delete mode 100644 server/Makefile.am diff --git a/Makefile.am b/Makefile.am index 52c2c014..ab3bf2b0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,13 +4,6 @@ DEPTH = . EXTRA_DIST = SUBDIRS = \ - base \ - mt \ - io \ - http \ - net \ - synergy \ - platform \ - client \ - server \ + lib \ + cmd \ $(NULL) diff --git a/client/Makefile.am b/client/Makefile.am deleted file mode 100644 index 5c06a480..00000000 --- a/client/Makefile.am +++ /dev/null @@ -1,38 +0,0 @@ -## Process this file with automake to produce Makefile.in -NULL = -DEPTH = .. - -bin_PROGRAMS = synergy -synergy_SOURCES = \ - CClient.cpp \ - CSecondaryScreen.cpp \ - CServerProxy.cpp \ - CXWindowsSecondaryScreen.cpp \ - client.cpp \ - CClient.h \ - CSecondaryScreen.h \ - CServerProxy.h \ - CXWindowsSecondaryScreen.h \ - $(NULL) -synergy_LDADD = \ - $(DEPTH)/platform/libplatform.a \ - $(DEPTH)/synergy/libsynergy.a \ - $(DEPTH)/net/libnet.a \ - $(DEPTH)/io/libio.a \ - $(DEPTH)/mt/libmt.a \ - $(DEPTH)/base/libbase.a \ - $(X_LIBS) \ - $(X_PRE_LIBS) \ - -lXtst \ - -lXext \ - -lX11 \ - $(X_EXTRA_LIBS) \ - $(NULL) -INCLUDES = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ - -I$(DEPTH)/net \ - -I$(DEPTH)/synergy \ - -I$(DEPTH)/platform \ - $(NULL) diff --git a/cmd/Makefile.am b/cmd/Makefile.am new file mode 100644 index 00000000..7219f8ee --- /dev/null +++ b/cmd/Makefile.am @@ -0,0 +1,9 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +EXTRA_DIST = +SUBDIRS = \ + synergy \ + synergyd \ + $(NULL) diff --git a/cmd/synergy/Makefile.am b/cmd/synergy/Makefile.am new file mode 100644 index 00000000..3b48a5a5 --- /dev/null +++ b/cmd/synergy/Makefile.am @@ -0,0 +1,32 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. + +bin_PROGRAMS = synergy +synergy_SOURCES = \ + synergy.cpp \ + $(NULL) +synergy_LDADD = \ + $(DEPTH)/lib/client/libclient.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a \ + $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/lib/base \ + -I$(DEPTH)/lib/mt \ + -I$(DEPTH)/lib/io \ + -I$(DEPTH)/lib/net \ + -I$(DEPTH)/lib/synergy \ + -I$(DEPTH)/lib/platform \ + -I$(DEPTH)/lib/client \ + $(NULL) diff --git a/client/resource.h b/cmd/synergy/resource.h similarity index 100% rename from client/resource.h rename to cmd/synergy/resource.h diff --git a/client/client.cpp b/cmd/synergy/synergy.cpp similarity index 100% rename from client/client.cpp rename to cmd/synergy/synergy.cpp diff --git a/client/client.dsp b/cmd/synergy/synergy.dsp similarity index 70% rename from client/client.dsp rename to cmd/synergy/synergy.dsp index d48c3aaf..991cdc26 100644 --- a/client/client.dsp +++ b/cmd/synergy/synergy.dsp @@ -1,24 +1,24 @@ -# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4> +# Microsoft Developer Studio Project File - Name="synergy" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 -CFG=client - Win32 Debug +CFG=synergy - Win32 Debug !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 "client.mak". +!MESSAGE NMAKE /f "synergy.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 "client.mak" CFG="client - Win32 Debug" +!MESSAGE NMAKE /f "synergy.mak" CFG="synergy - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE -!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "client - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE "synergy - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "synergy - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project @@ -29,7 +29,7 @@ CPP=cl.exe MTL=midl.exe RSC=rc.exe -!IF "$(CFG)" == "client - Win32 Release" +!IF "$(CFG)" == "synergy - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 @@ -38,12 +38,12 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../Release" +# PROP Output_Dir "../../Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -54,9 +54,9 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../Release/synergy.exe" +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -!ELSEIF "$(CFG)" == "client - Win32 Debug" +!ELSEIF "$(CFG)" == "synergy - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 @@ -65,12 +65,12 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../Debug" +# PROP Output_Dir "../../Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -81,40 +81,24 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../Debug/synergy.exe" /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target -# Name "client - Win32 Release" -# Name "client - Win32 Debug" +# Name "synergy - Win32 Release" +# Name "synergy - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=.\CClient.cpp +SOURCE=.\synergy.cpp # End Source File # Begin Source File -SOURCE=.\client.cpp -# End Source File -# Begin Source File - -SOURCE=.\client.rc -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsSecondaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CSecondaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CServerProxy.cpp +SOURCE=.\synergy.rc # End Source File # End Group # Begin Group "Header Files" @@ -122,22 +106,6 @@ SOURCE=.\CServerProxy.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\CClient.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsSecondaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CSecondaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CServerProxy.h -# End Source File -# Begin Source File - SOURCE=.\resource.h # End Source File # End Group diff --git a/client/client.rc b/cmd/synergy/synergy.rc similarity index 100% rename from client/client.rc rename to cmd/synergy/synergy.rc diff --git a/cmd/synergyd/Makefile.am b/cmd/synergyd/Makefile.am new file mode 100644 index 00000000..e0a424b2 --- /dev/null +++ b/cmd/synergyd/Makefile.am @@ -0,0 +1,34 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. + +bin_PROGRAMS = synergyd +synergyd_SOURCES = \ + synergyd.cpp \ + $(NULL) +synergyd_LDADD = \ + $(DEPTH)/lib/server/libserver.a \ + $(DEPTH)/lib/platform/libplatform.a \ + $(DEPTH)/lib/synergy/libsynergy.a \ + $(DEPTH)/lib/net/libnet.a \ + $(DEPTH)/lib/http/libhttp.a \ + $(DEPTH)/lib/io/libio.a \ + $(DEPTH)/lib/mt/libmt.a \ + $(DEPTH)/lib/base/libbase.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/lib/base \ + -I$(DEPTH)/lib/mt \ + -I$(DEPTH)/lib/io \ + -I$(DEPTH)/lib/http \ + -I$(DEPTH)/lib/net \ + -I$(DEPTH)/lib/synergy \ + -I$(DEPTH)/lib/platform \ + -I$(DEPTH)/lib/server \ + $(NULL) diff --git a/server/resource.h b/cmd/synergyd/resource.h similarity index 100% rename from server/resource.h rename to cmd/synergyd/resource.h diff --git a/server/server.cpp b/cmd/synergyd/synergyd.cpp similarity index 100% rename from server/server.cpp rename to cmd/synergyd/synergyd.cpp diff --git a/server/server.dsp b/cmd/synergyd/synergyd.dsp similarity index 60% rename from server/server.dsp rename to cmd/synergyd/synergyd.dsp index 61acba15..f7665026 100644 --- a/server/server.dsp +++ b/cmd/synergyd/synergyd.dsp @@ -1,24 +1,24 @@ -# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> +# Microsoft Developer Studio Project File - Name="synergyd" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 -CFG=server - Win32 Debug +CFG=synergyd - Win32 Debug !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 "server.mak". +!MESSAGE NMAKE /f "synergyd.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 "server.mak" CFG="server - Win32 Debug" +!MESSAGE NMAKE /f "synergyd.mak" CFG="synergyd - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE -!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "server - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE "synergyd - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "synergyd - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project @@ -29,7 +29,7 @@ CPP=cl.exe MTL=midl.exe RSC=rc.exe -!IF "$(CFG)" == "server - Win32 Release" +!IF "$(CFG)" == "synergyd - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 @@ -38,12 +38,12 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../Release" +# PROP Output_Dir "../../Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -54,9 +54,9 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../Release/synergyd.exe" +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -!ELSEIF "$(CFG)" == "server - Win32 Debug" +!ELSEIF "$(CFG)" == "synergyd - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 @@ -65,12 +65,12 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../Debug" +# PROP Output_Dir "../../Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -81,56 +81,24 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../Debug/synergyd.exe" /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target -# Name "server - Win32 Release" -# Name "server - Win32 Debug" +# Name "synergyd - Win32 Release" +# Name "synergyd - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=.\CClientProxy.cpp +SOURCE=.\synergyd.cpp # End Source File # Begin Source File -SOURCE=.\CClientProxy1_0.cpp -# End Source File -# Begin Source File - -SOURCE=.\CConfig.cpp -# End Source File -# Begin Source File - -SOURCE=.\CHTTPServer.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsPrimaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryClient.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CServer.cpp -# End Source File -# Begin Source File - -SOURCE=.\server.cpp -# End Source File -# Begin Source File - -SOURCE=.\server.rc +SOURCE=.\synergyd.rc # End Source File # End Group # Begin Group "Header Files" @@ -138,38 +106,6 @@ SOURCE=.\server.rc # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\CClientProxy.h -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_0.h -# End Source File -# Begin Source File - -SOURCE=.\CConfig.h -# End Source File -# Begin Source File - -SOURCE=.\CHTTPServer.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsPrimaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryClient.h -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CServer.h -# End Source File -# Begin Source File - SOURCE=.\resource.h # End Source File # End Group diff --git a/server/server.rc b/cmd/synergyd/synergyd.rc similarity index 100% rename from server/server.rc rename to cmd/synergyd/synergyd.rc diff --git a/configure.in b/configure.in index 5c43b047..69a99df6 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(base/common.h) +AC_INIT(lib/base/common.h) AM_CONFIG_HEADER(config.h) AC_CONFIG_AUX_DIR(config) AM_INIT_AUTOMAKE(synergy, 0.5) @@ -60,14 +60,18 @@ CC="$PTHREAD_CC" AC_OUTPUT([ Makefile -base/Makefile -mt/Makefile -io/Makefile -http/Makefile -net/Makefile -synergy/Makefile -platform/Makefile -client/Makefile -server/Makefile +lib/Makefile +lib/base/Makefile +lib/mt/Makefile +lib/io/Makefile +lib/http/Makefile +lib/net/Makefile +lib/synergy/Makefile +lib/platform/Makefile +lib/client/Makefile +lib/server/Makefile +cmd/Makefile +cmd/synergy/Makefile +cmd/synergyd/Makefile ]) diff --git a/io/Makefile.am b/io/Makefile.am deleted file mode 100644 index 897daf14..00000000 --- a/io/Makefile.am +++ /dev/null @@ -1,25 +0,0 @@ -## Process this file with automake to produce Makefile.in -NULL = -DEPTH = .. - -noinst_LIBRARIES = libio.a -libio_a_SOURCES = \ - CBufferedInputStream.cpp \ - CBufferedOutputStream.cpp \ - CInputStreamFilter.cpp \ - COutputStreamFilter.cpp \ - CStreamBuffer.cpp \ - XIO.cpp \ - CBufferedInputStream.h \ - CBufferedOutputStream.h \ - CInputStreamFilter.h \ - COutputStreamFilter.h \ - CStreamBuffer.h \ - IInputStream.h \ - IOutputStream.h \ - XIO.h \ - $(NULL) -INCLUDES = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - $(NULL) diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 00000000..0892a6b7 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,16 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. + +EXTRA_DIST = +SUBDIRS = \ + base \ + mt \ + io \ + http \ + net \ + synergy \ + platform \ + client \ + server \ + $(NULL) diff --git a/base/BasicTypes.h b/lib/base/BasicTypes.h similarity index 100% rename from base/BasicTypes.h rename to lib/base/BasicTypes.h diff --git a/base/CFunctionJob.cpp b/lib/base/CFunctionJob.cpp similarity index 100% rename from base/CFunctionJob.cpp rename to lib/base/CFunctionJob.cpp diff --git a/base/CFunctionJob.h b/lib/base/CFunctionJob.h similarity index 100% rename from base/CFunctionJob.h rename to lib/base/CFunctionJob.h diff --git a/base/CLog.cpp b/lib/base/CLog.cpp similarity index 100% rename from base/CLog.cpp rename to lib/base/CLog.cpp diff --git a/base/CLog.h b/lib/base/CLog.h similarity index 100% rename from base/CLog.h rename to lib/base/CLog.h diff --git a/base/CStopwatch.cpp b/lib/base/CStopwatch.cpp similarity index 100% rename from base/CStopwatch.cpp rename to lib/base/CStopwatch.cpp diff --git a/base/CStopwatch.h b/lib/base/CStopwatch.h similarity index 100% rename from base/CStopwatch.h rename to lib/base/CStopwatch.h diff --git a/base/CString.cpp b/lib/base/CString.cpp similarity index 99% rename from base/CString.cpp rename to lib/base/CString.cpp index f6d30794..8854df6d 100644 --- a/base/CString.cpp +++ b/lib/base/CString.cpp @@ -1,4 +1,5 @@ #include "CString.h" +#include "common.h" #include "stdvector.h" #include #include diff --git a/base/CString.h b/lib/base/CString.h similarity index 100% rename from base/CString.h rename to lib/base/CString.h diff --git a/base/CUnicode.cpp b/lib/base/CUnicode.cpp similarity index 100% rename from base/CUnicode.cpp rename to lib/base/CUnicode.cpp diff --git a/base/CUnicode.h b/lib/base/CUnicode.h similarity index 100% rename from base/CUnicode.h rename to lib/base/CUnicode.h diff --git a/base/IInterface.h b/lib/base/IInterface.h similarity index 100% rename from base/IInterface.h rename to lib/base/IInterface.h diff --git a/base/IJob.h b/lib/base/IJob.h similarity index 100% rename from base/IJob.h rename to lib/base/IJob.h diff --git a/base/Makefile.am b/lib/base/Makefile.am similarity index 97% rename from base/Makefile.am rename to lib/base/Makefile.am index fe67d1ce..82f537e8 100644 --- a/base/Makefile.am +++ b/lib/base/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = .. +DEPTH = ../.. noinst_LIBRARIES = libbase.a libbase_a_SOURCES = \ diff --git a/base/TMethodJob.h b/lib/base/TMethodJob.h similarity index 100% rename from base/TMethodJob.h rename to lib/base/TMethodJob.h diff --git a/base/XBase.cpp b/lib/base/XBase.cpp similarity index 100% rename from base/XBase.cpp rename to lib/base/XBase.cpp diff --git a/base/XBase.h b/lib/base/XBase.h similarity index 100% rename from base/XBase.h rename to lib/base/XBase.h diff --git a/base/base.dsp b/lib/base/base.dsp similarity index 100% rename from base/base.dsp rename to lib/base/base.dsp diff --git a/base/common.h b/lib/base/common.h similarity index 100% rename from base/common.h rename to lib/base/common.h diff --git a/base/stdfstream.h b/lib/base/stdfstream.h similarity index 100% rename from base/stdfstream.h rename to lib/base/stdfstream.h diff --git a/base/stdistream.h b/lib/base/stdistream.h similarity index 100% rename from base/stdistream.h rename to lib/base/stdistream.h diff --git a/base/stdlist.h b/lib/base/stdlist.h similarity index 100% rename from base/stdlist.h rename to lib/base/stdlist.h diff --git a/base/stdmap.h b/lib/base/stdmap.h similarity index 100% rename from base/stdmap.h rename to lib/base/stdmap.h diff --git a/base/stdostream.h b/lib/base/stdostream.h similarity index 100% rename from base/stdostream.h rename to lib/base/stdostream.h diff --git a/base/stdpost.h b/lib/base/stdpost.h similarity index 100% rename from base/stdpost.h rename to lib/base/stdpost.h diff --git a/base/stdpre.h b/lib/base/stdpre.h similarity index 100% rename from base/stdpre.h rename to lib/base/stdpre.h diff --git a/base/stdset.h b/lib/base/stdset.h similarity index 100% rename from base/stdset.h rename to lib/base/stdset.h diff --git a/base/stdsstream.h b/lib/base/stdsstream.h similarity index 100% rename from base/stdsstream.h rename to lib/base/stdsstream.h diff --git a/base/stdvector.h b/lib/base/stdvector.h similarity index 100% rename from base/stdvector.h rename to lib/base/stdvector.h diff --git a/client/CClient.cpp b/lib/client/CClient.cpp similarity index 100% rename from client/CClient.cpp rename to lib/client/CClient.cpp diff --git a/client/CClient.h b/lib/client/CClient.h similarity index 100% rename from client/CClient.h rename to lib/client/CClient.h diff --git a/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp similarity index 100% rename from client/CMSWindowsSecondaryScreen.cpp rename to lib/client/CMSWindowsSecondaryScreen.cpp diff --git a/client/CMSWindowsSecondaryScreen.h b/lib/client/CMSWindowsSecondaryScreen.h similarity index 100% rename from client/CMSWindowsSecondaryScreen.h rename to lib/client/CMSWindowsSecondaryScreen.h diff --git a/client/CSecondaryScreen.cpp b/lib/client/CSecondaryScreen.cpp similarity index 100% rename from client/CSecondaryScreen.cpp rename to lib/client/CSecondaryScreen.cpp diff --git a/client/CSecondaryScreen.h b/lib/client/CSecondaryScreen.h similarity index 100% rename from client/CSecondaryScreen.h rename to lib/client/CSecondaryScreen.h diff --git a/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp similarity index 100% rename from client/CServerProxy.cpp rename to lib/client/CServerProxy.cpp diff --git a/client/CServerProxy.h b/lib/client/CServerProxy.h similarity index 100% rename from client/CServerProxy.h rename to lib/client/CServerProxy.h diff --git a/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp similarity index 100% rename from client/CXWindowsSecondaryScreen.cpp rename to lib/client/CXWindowsSecondaryScreen.cpp diff --git a/client/CXWindowsSecondaryScreen.h b/lib/client/CXWindowsSecondaryScreen.h similarity index 100% rename from client/CXWindowsSecondaryScreen.h rename to lib/client/CXWindowsSecondaryScreen.h diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am new file mode 100644 index 00000000..42001eae --- /dev/null +++ b/lib/client/Makefile.am @@ -0,0 +1,23 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. + +noinst_LIBRARIES = libclient.a +libclient_a_SOURCES = \ + CClient.cpp \ + CSecondaryScreen.cpp \ + CServerProxy.cpp \ + CXWindowsSecondaryScreen.cpp \ + CClient.h \ + CSecondaryScreen.h \ + CServerProxy.h \ + CXWindowsSecondaryScreen.h \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/lib/base \ + -I$(DEPTH)/lib/mt \ + -I$(DEPTH)/lib/io \ + -I$(DEPTH)/lib/net \ + -I$(DEPTH)/lib/synergy \ + -I$(DEPTH)/lib/platform \ + $(NULL) diff --git a/http/CHTTPProtocol.cpp b/lib/http/CHTTPProtocol.cpp similarity index 100% rename from http/CHTTPProtocol.cpp rename to lib/http/CHTTPProtocol.cpp diff --git a/http/CHTTPProtocol.h b/lib/http/CHTTPProtocol.h similarity index 100% rename from http/CHTTPProtocol.h rename to lib/http/CHTTPProtocol.h diff --git a/http/Makefile.am b/lib/http/Makefile.am similarity index 73% rename from http/Makefile.am rename to lib/http/Makefile.am index 08404a0a..cee66bf8 100644 --- a/http/Makefile.am +++ b/lib/http/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = .. +DEPTH = ../.. noinst_LIBRARIES = libhttp.a libhttp_a_SOURCES = \ @@ -10,7 +10,7 @@ libhttp_a_SOURCES = \ XHTTP.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ + -I$(DEPTH)/lib/base \ + -I$(DEPTH)/lib/mt \ + -I$(DEPTH)/lib/io \ $(NULL) diff --git a/http/XHTTP.cpp b/lib/http/XHTTP.cpp similarity index 100% rename from http/XHTTP.cpp rename to lib/http/XHTTP.cpp diff --git a/http/XHTTP.h b/lib/http/XHTTP.h similarity index 100% rename from http/XHTTP.h rename to lib/http/XHTTP.h diff --git a/http/http.dsp b/lib/http/http.dsp similarity index 100% rename from http/http.dsp rename to lib/http/http.dsp diff --git a/io/CBufferedInputStream.cpp b/lib/io/CBufferedInputStream.cpp similarity index 100% rename from io/CBufferedInputStream.cpp rename to lib/io/CBufferedInputStream.cpp diff --git a/io/CBufferedInputStream.h b/lib/io/CBufferedInputStream.h similarity index 100% rename from io/CBufferedInputStream.h rename to lib/io/CBufferedInputStream.h diff --git a/io/CBufferedOutputStream.cpp b/lib/io/CBufferedOutputStream.cpp similarity index 100% rename from io/CBufferedOutputStream.cpp rename to lib/io/CBufferedOutputStream.cpp diff --git a/io/CBufferedOutputStream.h b/lib/io/CBufferedOutputStream.h similarity index 100% rename from io/CBufferedOutputStream.h rename to lib/io/CBufferedOutputStream.h diff --git a/io/CInputStreamFilter.cpp b/lib/io/CInputStreamFilter.cpp similarity index 100% rename from io/CInputStreamFilter.cpp rename to lib/io/CInputStreamFilter.cpp diff --git a/io/CInputStreamFilter.h b/lib/io/CInputStreamFilter.h similarity index 100% rename from io/CInputStreamFilter.h rename to lib/io/CInputStreamFilter.h diff --git a/io/COutputStreamFilter.cpp b/lib/io/COutputStreamFilter.cpp similarity index 100% rename from io/COutputStreamFilter.cpp rename to lib/io/COutputStreamFilter.cpp diff --git a/io/COutputStreamFilter.h b/lib/io/COutputStreamFilter.h similarity index 100% rename from io/COutputStreamFilter.h rename to lib/io/COutputStreamFilter.h diff --git a/io/CStreamBuffer.cpp b/lib/io/CStreamBuffer.cpp similarity index 100% rename from io/CStreamBuffer.cpp rename to lib/io/CStreamBuffer.cpp diff --git a/io/CStreamBuffer.h b/lib/io/CStreamBuffer.h similarity index 100% rename from io/CStreamBuffer.h rename to lib/io/CStreamBuffer.h diff --git a/io/IInputStream.h b/lib/io/IInputStream.h similarity index 100% rename from io/IInputStream.h rename to lib/io/IInputStream.h diff --git a/io/IOutputStream.h b/lib/io/IOutputStream.h similarity index 100% rename from io/IOutputStream.h rename to lib/io/IOutputStream.h diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am new file mode 100644 index 00000000..67ff14a8 --- /dev/null +++ b/lib/io/Makefile.am @@ -0,0 +1,25 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. + +noinst_LIBRARIES = libio.a +libio_a_SOURCES = \ + CBufferedInputStream.cpp \ + CBufferedOutputStream.cpp \ + CInputStreamFilter.cpp \ + COutputStreamFilter.cpp \ + CStreamBuffer.cpp \ + XIO.cpp \ + CBufferedInputStream.h \ + CBufferedOutputStream.h \ + CInputStreamFilter.h \ + COutputStreamFilter.h \ + CStreamBuffer.h \ + IInputStream.h \ + IOutputStream.h \ + XIO.h \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/lib/base \ + -I$(DEPTH)/lib/mt \ + $(NULL) diff --git a/io/XIO.cpp b/lib/io/XIO.cpp similarity index 100% rename from io/XIO.cpp rename to lib/io/XIO.cpp diff --git a/io/XIO.h b/lib/io/XIO.h similarity index 100% rename from io/XIO.h rename to lib/io/XIO.h diff --git a/io/io.dsp b/lib/io/io.dsp similarity index 100% rename from io/io.dsp rename to lib/io/io.dsp diff --git a/mt/CCondVar.cpp b/lib/mt/CCondVar.cpp similarity index 100% rename from mt/CCondVar.cpp rename to lib/mt/CCondVar.cpp diff --git a/mt/CCondVar.h b/lib/mt/CCondVar.h similarity index 100% rename from mt/CCondVar.h rename to lib/mt/CCondVar.h diff --git a/mt/CLock.cpp b/lib/mt/CLock.cpp similarity index 100% rename from mt/CLock.cpp rename to lib/mt/CLock.cpp diff --git a/mt/CLock.h b/lib/mt/CLock.h similarity index 100% rename from mt/CLock.h rename to lib/mt/CLock.h diff --git a/mt/CMutex.cpp b/lib/mt/CMutex.cpp similarity index 100% rename from mt/CMutex.cpp rename to lib/mt/CMutex.cpp diff --git a/mt/CMutex.h b/lib/mt/CMutex.h similarity index 100% rename from mt/CMutex.h rename to lib/mt/CMutex.h diff --git a/mt/CThread.cpp b/lib/mt/CThread.cpp similarity index 100% rename from mt/CThread.cpp rename to lib/mt/CThread.cpp diff --git a/mt/CThread.h b/lib/mt/CThread.h similarity index 100% rename from mt/CThread.h rename to lib/mt/CThread.h diff --git a/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp similarity index 100% rename from mt/CThreadRep.cpp rename to lib/mt/CThreadRep.cpp diff --git a/mt/CThreadRep.h b/lib/mt/CThreadRep.h similarity index 100% rename from mt/CThreadRep.h rename to lib/mt/CThreadRep.h diff --git a/mt/CTimerThread.cpp b/lib/mt/CTimerThread.cpp similarity index 100% rename from mt/CTimerThread.cpp rename to lib/mt/CTimerThread.cpp diff --git a/mt/CTimerThread.h b/lib/mt/CTimerThread.h similarity index 100% rename from mt/CTimerThread.h rename to lib/mt/CTimerThread.h diff --git a/mt/Makefile.am b/lib/mt/Makefile.am similarity index 90% rename from mt/Makefile.am rename to lib/mt/Makefile.am index e7f69865..a3c4b165 100644 --- a/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = .. +DEPTH = ../.. noinst_LIBRARIES = libmt.a libmt_a_SOURCES = \ @@ -19,5 +19,5 @@ libmt_a_SOURCES = \ XThread.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/base \ + -I$(DEPTH)/lib/base \ $(NULL) diff --git a/mt/XThread.h b/lib/mt/XThread.h similarity index 100% rename from mt/XThread.h rename to lib/mt/XThread.h diff --git a/mt/mt.dsp b/lib/mt/mt.dsp similarity index 100% rename from mt/mt.dsp rename to lib/mt/mt.dsp diff --git a/net/CNetwork.cpp b/lib/net/CNetwork.cpp similarity index 100% rename from net/CNetwork.cpp rename to lib/net/CNetwork.cpp diff --git a/net/CNetwork.h b/lib/net/CNetwork.h similarity index 100% rename from net/CNetwork.h rename to lib/net/CNetwork.h diff --git a/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp similarity index 100% rename from net/CNetworkAddress.cpp rename to lib/net/CNetworkAddress.cpp diff --git a/net/CNetworkAddress.h b/lib/net/CNetworkAddress.h similarity index 100% rename from net/CNetworkAddress.h rename to lib/net/CNetworkAddress.h diff --git a/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp similarity index 100% rename from net/CTCPListenSocket.cpp rename to lib/net/CTCPListenSocket.cpp diff --git a/net/CTCPListenSocket.h b/lib/net/CTCPListenSocket.h similarity index 100% rename from net/CTCPListenSocket.h rename to lib/net/CTCPListenSocket.h diff --git a/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp similarity index 100% rename from net/CTCPSocket.cpp rename to lib/net/CTCPSocket.cpp diff --git a/net/CTCPSocket.h b/lib/net/CTCPSocket.h similarity index 100% rename from net/CTCPSocket.h rename to lib/net/CTCPSocket.h diff --git a/net/IDataSocket.h b/lib/net/IDataSocket.h similarity index 100% rename from net/IDataSocket.h rename to lib/net/IDataSocket.h diff --git a/net/IListenSocket.h b/lib/net/IListenSocket.h similarity index 100% rename from net/IListenSocket.h rename to lib/net/IListenSocket.h diff --git a/net/ISocket.h b/lib/net/ISocket.h similarity index 100% rename from net/ISocket.h rename to lib/net/ISocket.h diff --git a/net/Makefile.am b/lib/net/Makefile.am similarity index 84% rename from net/Makefile.am rename to lib/net/Makefile.am index 5262f015..f12a316f 100644 --- a/net/Makefile.am +++ b/lib/net/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = .. +DEPTH = ../.. noinst_LIBRARIES = libnet.a libnet_a_SOURCES = \ @@ -21,7 +21,7 @@ libnet_a_SOURCES = \ XSocket.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ + -I$(DEPTH)/lib/base \ + -I$(DEPTH)/lib/mt \ + -I$(DEPTH)/lib/io \ $(NULL) diff --git a/net/XNetwork.cpp b/lib/net/XNetwork.cpp similarity index 100% rename from net/XNetwork.cpp rename to lib/net/XNetwork.cpp diff --git a/net/XNetwork.h b/lib/net/XNetwork.h similarity index 100% rename from net/XNetwork.h rename to lib/net/XNetwork.h diff --git a/net/XSocket.cpp b/lib/net/XSocket.cpp similarity index 100% rename from net/XSocket.cpp rename to lib/net/XSocket.cpp diff --git a/net/XSocket.h b/lib/net/XSocket.h similarity index 100% rename from net/XSocket.h rename to lib/net/XSocket.h diff --git a/net/net.dsp b/lib/net/net.dsp similarity index 100% rename from net/net.dsp rename to lib/net/net.dsp diff --git a/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp similarity index 100% rename from platform/CMSWindowsClipboard.cpp rename to lib/platform/CMSWindowsClipboard.cpp diff --git a/platform/CMSWindowsClipboard.h b/lib/platform/CMSWindowsClipboard.h similarity index 100% rename from platform/CMSWindowsClipboard.h rename to lib/platform/CMSWindowsClipboard.h diff --git a/platform/CMSWindowsClipboardAnyTextConverter.cpp b/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp similarity index 100% rename from platform/CMSWindowsClipboardAnyTextConverter.cpp rename to lib/platform/CMSWindowsClipboardAnyTextConverter.cpp diff --git a/platform/CMSWindowsClipboardAnyTextConverter.h b/lib/platform/CMSWindowsClipboardAnyTextConverter.h similarity index 100% rename from platform/CMSWindowsClipboardAnyTextConverter.h rename to lib/platform/CMSWindowsClipboardAnyTextConverter.h diff --git a/platform/CMSWindowsClipboardTextConverter.cpp b/lib/platform/CMSWindowsClipboardTextConverter.cpp similarity index 100% rename from platform/CMSWindowsClipboardTextConverter.cpp rename to lib/platform/CMSWindowsClipboardTextConverter.cpp diff --git a/platform/CMSWindowsClipboardTextConverter.h b/lib/platform/CMSWindowsClipboardTextConverter.h similarity index 100% rename from platform/CMSWindowsClipboardTextConverter.h rename to lib/platform/CMSWindowsClipboardTextConverter.h diff --git a/platform/CMSWindowsClipboardUTF16Converter.cpp b/lib/platform/CMSWindowsClipboardUTF16Converter.cpp similarity index 100% rename from platform/CMSWindowsClipboardUTF16Converter.cpp rename to lib/platform/CMSWindowsClipboardUTF16Converter.cpp diff --git a/platform/CMSWindowsClipboardUTF16Converter.h b/lib/platform/CMSWindowsClipboardUTF16Converter.h similarity index 100% rename from platform/CMSWindowsClipboardUTF16Converter.h rename to lib/platform/CMSWindowsClipboardUTF16Converter.h diff --git a/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp similarity index 100% rename from platform/CMSWindowsScreen.cpp rename to lib/platform/CMSWindowsScreen.cpp diff --git a/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h similarity index 100% rename from platform/CMSWindowsScreen.h rename to lib/platform/CMSWindowsScreen.h diff --git a/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp similarity index 100% rename from platform/CMSWindowsScreenSaver.cpp rename to lib/platform/CMSWindowsScreenSaver.cpp diff --git a/platform/CMSWindowsScreenSaver.h b/lib/platform/CMSWindowsScreenSaver.h similarity index 100% rename from platform/CMSWindowsScreenSaver.h rename to lib/platform/CMSWindowsScreenSaver.h diff --git a/platform/CPlatform.cpp b/lib/platform/CPlatform.cpp similarity index 100% rename from platform/CPlatform.cpp rename to lib/platform/CPlatform.cpp diff --git a/platform/CPlatform.h b/lib/platform/CPlatform.h similarity index 100% rename from platform/CPlatform.h rename to lib/platform/CPlatform.h diff --git a/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp similarity index 100% rename from platform/CSynergyHook.cpp rename to lib/platform/CSynergyHook.cpp diff --git a/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h similarity index 100% rename from platform/CSynergyHook.h rename to lib/platform/CSynergyHook.h diff --git a/platform/CUnixPlatform.cpp b/lib/platform/CUnixPlatform.cpp similarity index 100% rename from platform/CUnixPlatform.cpp rename to lib/platform/CUnixPlatform.cpp diff --git a/platform/CUnixPlatform.h b/lib/platform/CUnixPlatform.h similarity index 100% rename from platform/CUnixPlatform.h rename to lib/platform/CUnixPlatform.h diff --git a/platform/CWin32Platform.cpp b/lib/platform/CWin32Platform.cpp similarity index 100% rename from platform/CWin32Platform.cpp rename to lib/platform/CWin32Platform.cpp diff --git a/platform/CWin32Platform.h b/lib/platform/CWin32Platform.h similarity index 100% rename from platform/CWin32Platform.h rename to lib/platform/CWin32Platform.h diff --git a/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp similarity index 100% rename from platform/CXWindowsClipboard.cpp rename to lib/platform/CXWindowsClipboard.cpp diff --git a/platform/CXWindowsClipboard.h b/lib/platform/CXWindowsClipboard.h similarity index 100% rename from platform/CXWindowsClipboard.h rename to lib/platform/CXWindowsClipboard.h diff --git a/platform/CXWindowsClipboardTextConverter.cpp b/lib/platform/CXWindowsClipboardTextConverter.cpp similarity index 100% rename from platform/CXWindowsClipboardTextConverter.cpp rename to lib/platform/CXWindowsClipboardTextConverter.cpp diff --git a/platform/CXWindowsClipboardTextConverter.h b/lib/platform/CXWindowsClipboardTextConverter.h similarity index 100% rename from platform/CXWindowsClipboardTextConverter.h rename to lib/platform/CXWindowsClipboardTextConverter.h diff --git a/platform/CXWindowsClipboardUCS2Converter.cpp b/lib/platform/CXWindowsClipboardUCS2Converter.cpp similarity index 100% rename from platform/CXWindowsClipboardUCS2Converter.cpp rename to lib/platform/CXWindowsClipboardUCS2Converter.cpp diff --git a/platform/CXWindowsClipboardUCS2Converter.h b/lib/platform/CXWindowsClipboardUCS2Converter.h similarity index 100% rename from platform/CXWindowsClipboardUCS2Converter.h rename to lib/platform/CXWindowsClipboardUCS2Converter.h diff --git a/platform/CXWindowsClipboardUTF8Converter.cpp b/lib/platform/CXWindowsClipboardUTF8Converter.cpp similarity index 100% rename from platform/CXWindowsClipboardUTF8Converter.cpp rename to lib/platform/CXWindowsClipboardUTF8Converter.cpp diff --git a/platform/CXWindowsClipboardUTF8Converter.h b/lib/platform/CXWindowsClipboardUTF8Converter.h similarity index 100% rename from platform/CXWindowsClipboardUTF8Converter.h rename to lib/platform/CXWindowsClipboardUTF8Converter.h diff --git a/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp similarity index 100% rename from platform/CXWindowsScreen.cpp rename to lib/platform/CXWindowsScreen.cpp diff --git a/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h similarity index 100% rename from platform/CXWindowsScreen.h rename to lib/platform/CXWindowsScreen.h diff --git a/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp similarity index 100% rename from platform/CXWindowsScreenSaver.cpp rename to lib/platform/CXWindowsScreenSaver.cpp diff --git a/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h similarity index 100% rename from platform/CXWindowsScreenSaver.h rename to lib/platform/CXWindowsScreenSaver.h diff --git a/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp similarity index 100% rename from platform/CXWindowsUtil.cpp rename to lib/platform/CXWindowsUtil.cpp diff --git a/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h similarity index 100% rename from platform/CXWindowsUtil.h rename to lib/platform/CXWindowsUtil.h diff --git a/platform/IMSWindowsScreenEventHandler.h b/lib/platform/IMSWindowsScreenEventHandler.h similarity index 100% rename from platform/IMSWindowsScreenEventHandler.h rename to lib/platform/IMSWindowsScreenEventHandler.h diff --git a/platform/IPlatform.h b/lib/platform/IPlatform.h similarity index 100% rename from platform/IPlatform.h rename to lib/platform/IPlatform.h diff --git a/platform/Makefile.am b/lib/platform/Makefile.am similarity index 88% rename from platform/Makefile.am rename to lib/platform/Makefile.am index 4257ecbd..516961c1 100644 --- a/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = .. +DEPTH = ../.. # FIXME -- add CUnixPlatform.cpp as an unbuilt source noinst_LIBRARIES = libplatform.a @@ -25,7 +25,7 @@ libplatform_a_SOURCES = \ IPlatform.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/synergy \ + -I$(DEPTH)/lib/base \ + -I$(DEPTH)/lib/mt \ + -I$(DEPTH)/lib/synergy \ $(NULL) diff --git a/platform/makehook.dsp b/lib/platform/makehook.dsp similarity index 100% rename from platform/makehook.dsp rename to lib/platform/makehook.dsp diff --git a/platform/platform.dsp b/lib/platform/platform.dsp similarity index 100% rename from platform/platform.dsp rename to lib/platform/platform.dsp diff --git a/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp similarity index 98% rename from platform/synrgyhk.dsp rename to lib/platform/synrgyhk.dsp index 42dbb32e..df4809e7 100644 --- a/platform/synrgyhk.dsp +++ b/lib/platform/synrgyhk.dsp @@ -38,7 +38,7 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../Release" +# PROP Output_Dir "../../Release" # PROP Intermediate_Dir "ReleaseHook" # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c @@ -64,7 +64,7 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../Debug" +# PROP Output_Dir "../../Debug" # PROP Intermediate_Dir "DebugHook" # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c diff --git a/server/CClientProxy.cpp b/lib/server/CClientProxy.cpp similarity index 100% rename from server/CClientProxy.cpp rename to lib/server/CClientProxy.cpp diff --git a/server/CClientProxy.h b/lib/server/CClientProxy.h similarity index 100% rename from server/CClientProxy.h rename to lib/server/CClientProxy.h diff --git a/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp similarity index 100% rename from server/CClientProxy1_0.cpp rename to lib/server/CClientProxy1_0.cpp diff --git a/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h similarity index 100% rename from server/CClientProxy1_0.h rename to lib/server/CClientProxy1_0.h diff --git a/server/CConfig.cpp b/lib/server/CConfig.cpp similarity index 100% rename from server/CConfig.cpp rename to lib/server/CConfig.cpp diff --git a/server/CConfig.h b/lib/server/CConfig.h similarity index 100% rename from server/CConfig.h rename to lib/server/CConfig.h diff --git a/server/CHTTPServer.cpp b/lib/server/CHTTPServer.cpp similarity index 100% rename from server/CHTTPServer.cpp rename to lib/server/CHTTPServer.cpp diff --git a/server/CHTTPServer.h b/lib/server/CHTTPServer.h similarity index 100% rename from server/CHTTPServer.h rename to lib/server/CHTTPServer.h diff --git a/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp similarity index 100% rename from server/CMSWindowsPrimaryScreen.cpp rename to lib/server/CMSWindowsPrimaryScreen.cpp diff --git a/server/CMSWindowsPrimaryScreen.h b/lib/server/CMSWindowsPrimaryScreen.h similarity index 100% rename from server/CMSWindowsPrimaryScreen.h rename to lib/server/CMSWindowsPrimaryScreen.h diff --git a/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp similarity index 100% rename from server/CPrimaryClient.cpp rename to lib/server/CPrimaryClient.cpp diff --git a/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h similarity index 100% rename from server/CPrimaryClient.h rename to lib/server/CPrimaryClient.h diff --git a/server/CPrimaryScreen.cpp b/lib/server/CPrimaryScreen.cpp similarity index 100% rename from server/CPrimaryScreen.cpp rename to lib/server/CPrimaryScreen.cpp diff --git a/server/CPrimaryScreen.h b/lib/server/CPrimaryScreen.h similarity index 100% rename from server/CPrimaryScreen.h rename to lib/server/CPrimaryScreen.h diff --git a/server/CServer.cpp b/lib/server/CServer.cpp similarity index 100% rename from server/CServer.cpp rename to lib/server/CServer.cpp diff --git a/server/CServer.h b/lib/server/CServer.h similarity index 100% rename from server/CServer.h rename to lib/server/CServer.h diff --git a/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp similarity index 100% rename from server/CXWindowsPrimaryScreen.cpp rename to lib/server/CXWindowsPrimaryScreen.cpp diff --git a/server/CXWindowsPrimaryScreen.h b/lib/server/CXWindowsPrimaryScreen.h similarity index 100% rename from server/CXWindowsPrimaryScreen.h rename to lib/server/CXWindowsPrimaryScreen.h diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am new file mode 100644 index 00000000..36f6e393 --- /dev/null +++ b/lib/server/Makefile.am @@ -0,0 +1,32 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. + +noinst_LIBRARIES = libserver.a +libserver_a_SOURCES = \ + CClientProxy.cpp \ + CClientProxy1_0.cpp \ + CConfig.cpp \ + CHTTPServer.cpp \ + CPrimaryClient.cpp \ + CPrimaryScreen.cpp \ + CServer.cpp \ + CXWindowsPrimaryScreen.cpp \ + CClientProxy.h \ + CClientProxy1_0.h \ + CConfig.h \ + CHTTPServer.h \ + CPrimaryClient.h \ + CPrimaryScreen.h \ + CServer.h \ + CXWindowsPrimaryScreen.h \ + $(NULL) +INCLUDES = \ + -I$(DEPTH)/lib/base \ + -I$(DEPTH)/lib/mt \ + -I$(DEPTH)/lib/io \ + -I$(DEPTH)/lib/http \ + -I$(DEPTH)/lib/net \ + -I$(DEPTH)/lib/synergy \ + -I$(DEPTH)/lib/platform \ + $(NULL) diff --git a/synergy/CClipboard.cpp b/lib/synergy/CClipboard.cpp similarity index 100% rename from synergy/CClipboard.cpp rename to lib/synergy/CClipboard.cpp diff --git a/synergy/CClipboard.h b/lib/synergy/CClipboard.h similarity index 100% rename from synergy/CClipboard.h rename to lib/synergy/CClipboard.h diff --git a/synergy/CInputPacketStream.cpp b/lib/synergy/CInputPacketStream.cpp similarity index 100% rename from synergy/CInputPacketStream.cpp rename to lib/synergy/CInputPacketStream.cpp diff --git a/synergy/CInputPacketStream.h b/lib/synergy/CInputPacketStream.h similarity index 100% rename from synergy/CInputPacketStream.h rename to lib/synergy/CInputPacketStream.h diff --git a/synergy/COutputPacketStream.cpp b/lib/synergy/COutputPacketStream.cpp similarity index 100% rename from synergy/COutputPacketStream.cpp rename to lib/synergy/COutputPacketStream.cpp diff --git a/synergy/COutputPacketStream.h b/lib/synergy/COutputPacketStream.h similarity index 100% rename from synergy/COutputPacketStream.h rename to lib/synergy/COutputPacketStream.h diff --git a/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp similarity index 100% rename from synergy/CProtocolUtil.cpp rename to lib/synergy/CProtocolUtil.cpp diff --git a/synergy/CProtocolUtil.h b/lib/synergy/CProtocolUtil.h similarity index 100% rename from synergy/CProtocolUtil.h rename to lib/synergy/CProtocolUtil.h diff --git a/synergy/CTCPSocketFactory.cpp b/lib/synergy/CTCPSocketFactory.cpp similarity index 100% rename from synergy/CTCPSocketFactory.cpp rename to lib/synergy/CTCPSocketFactory.cpp diff --git a/synergy/CTCPSocketFactory.h b/lib/synergy/CTCPSocketFactory.h similarity index 100% rename from synergy/CTCPSocketFactory.h rename to lib/synergy/CTCPSocketFactory.h diff --git a/synergy/ClipboardTypes.h b/lib/synergy/ClipboardTypes.h similarity index 100% rename from synergy/ClipboardTypes.h rename to lib/synergy/ClipboardTypes.h diff --git a/synergy/IClient.h b/lib/synergy/IClient.h similarity index 100% rename from synergy/IClient.h rename to lib/synergy/IClient.h diff --git a/synergy/IClipboard.h b/lib/synergy/IClipboard.h similarity index 100% rename from synergy/IClipboard.h rename to lib/synergy/IClipboard.h diff --git a/synergy/IPrimaryScreenReceiver.h b/lib/synergy/IPrimaryScreenReceiver.h similarity index 100% rename from synergy/IPrimaryScreenReceiver.h rename to lib/synergy/IPrimaryScreenReceiver.h diff --git a/synergy/IScreen.h b/lib/synergy/IScreen.h similarity index 100% rename from synergy/IScreen.h rename to lib/synergy/IScreen.h diff --git a/synergy/IScreenEventHandler.h b/lib/synergy/IScreenEventHandler.h similarity index 100% rename from synergy/IScreenEventHandler.h rename to lib/synergy/IScreenEventHandler.h diff --git a/synergy/IScreenReceiver.h b/lib/synergy/IScreenReceiver.h similarity index 100% rename from synergy/IScreenReceiver.h rename to lib/synergy/IScreenReceiver.h diff --git a/synergy/IScreenSaver.h b/lib/synergy/IScreenSaver.h similarity index 100% rename from synergy/IScreenSaver.h rename to lib/synergy/IScreenSaver.h diff --git a/synergy/IServer.h b/lib/synergy/IServer.h similarity index 100% rename from synergy/IServer.h rename to lib/synergy/IServer.h diff --git a/synergy/ISocketFactory.h b/lib/synergy/ISocketFactory.h similarity index 100% rename from synergy/ISocketFactory.h rename to lib/synergy/ISocketFactory.h diff --git a/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h similarity index 100% rename from synergy/KeyTypes.h rename to lib/synergy/KeyTypes.h diff --git a/synergy/Makefile.am b/lib/synergy/Makefile.am similarity index 87% rename from synergy/Makefile.am rename to lib/synergy/Makefile.am index 9522e1cc..55adf09e 100644 --- a/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = .. +DEPTH = ../.. noinst_LIBRARIES = libsynergy.a libsynergy_a_SOURCES = \ @@ -34,8 +34,8 @@ libsynergy_a_SOURCES = \ XSynergy.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ - -I$(DEPTH)/net \ + -I$(DEPTH)/lib/base \ + -I$(DEPTH)/lib/mt \ + -I$(DEPTH)/lib/io \ + -I$(DEPTH)/lib/net \ $(NULL) diff --git a/synergy/MouseTypes.h b/lib/synergy/MouseTypes.h similarity index 100% rename from synergy/MouseTypes.h rename to lib/synergy/MouseTypes.h diff --git a/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h similarity index 100% rename from synergy/ProtocolTypes.h rename to lib/synergy/ProtocolTypes.h diff --git a/synergy/Version.h b/lib/synergy/Version.h similarity index 100% rename from synergy/Version.h rename to lib/synergy/Version.h diff --git a/synergy/XScreen.cpp b/lib/synergy/XScreen.cpp similarity index 100% rename from synergy/XScreen.cpp rename to lib/synergy/XScreen.cpp diff --git a/synergy/XScreen.h b/lib/synergy/XScreen.h similarity index 100% rename from synergy/XScreen.h rename to lib/synergy/XScreen.h diff --git a/synergy/XSynergy.cpp b/lib/synergy/XSynergy.cpp similarity index 100% rename from synergy/XSynergy.cpp rename to lib/synergy/XSynergy.cpp diff --git a/synergy/XSynergy.h b/lib/synergy/XSynergy.h similarity index 100% rename from synergy/XSynergy.h rename to lib/synergy/XSynergy.h diff --git a/synergy/synergy.dsp b/lib/synergy/libsynergy.dsp similarity index 89% rename from synergy/synergy.dsp rename to lib/synergy/libsynergy.dsp index eac75dde..99cc99ac 100644 --- a/synergy/synergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -1,4 +1,4 @@ -# Microsoft Developer Studio Project File - Name="synergy" - Package Owner=<4> +# Microsoft Developer Studio Project File - Name="libsynergy" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** @@ -8,17 +8,17 @@ CFG=synergy - Win32 Debug !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 "synergy.mak". +!MESSAGE NMAKE /f "libsynergy.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 "synergy.mak" CFG="synergy - Win32 Debug" +!MESSAGE NMAKE /f "libsynergy.mak" CFG="libsynergy - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE -!MESSAGE "synergy - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "synergy - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE "libsynergy - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libsynergy - Win32 Debug" (based on "Win32 (x86) Static Library") !MESSAGE # Begin Project @@ -28,7 +28,7 @@ CFG=synergy - Win32 Debug CPP=cl.exe RSC=rc.exe -!IF "$(CFG)" == "synergy - Win32 Release" +!IF "$(CFG)" == "libsynergy - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 @@ -52,7 +52,7 @@ LIB32=link.exe -lib # ADD BASE LIB32 /nologo # ADD LIB32 /nologo -!ELSEIF "$(CFG)" == "synergy - Win32 Debug" +!ELSEIF "$(CFG)" == "libsynergy - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 @@ -80,8 +80,8 @@ LIB32=link.exe -lib # Begin Target -# Name "synergy - Win32 Release" -# Name "synergy - Win32 Debug" +# Name "libsynergy - Win32 Release" +# Name "libsynergy - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" diff --git a/server/Makefile.am b/server/Makefile.am deleted file mode 100644 index bcab55a3..00000000 --- a/server/Makefile.am +++ /dev/null @@ -1,48 +0,0 @@ -## Process this file with automake to produce Makefile.in -NULL = -DEPTH = .. - -bin_PROGRAMS = synergyd -synergyd_SOURCES = \ - CClientProxy.cpp \ - CClientProxy1_0.cpp \ - CConfig.cpp \ - CHTTPServer.cpp \ - CPrimaryClient.cpp \ - CPrimaryScreen.cpp \ - CServer.cpp \ - CXWindowsPrimaryScreen.cpp \ - server.cpp \ - CClientProxy.h \ - CClientProxy1_0.h \ - CConfig.h \ - CHTTPServer.h \ - CPrimaryClient.h \ - CPrimaryScreen.h \ - CServer.h \ - CXWindowsPrimaryScreen.h \ - $(NULL) -synergyd_LDADD = \ - $(DEPTH)/platform/libplatform.a \ - $(DEPTH)/synergy/libsynergy.a \ - $(DEPTH)/net/libnet.a \ - $(DEPTH)/http/libhttp.a \ - $(DEPTH)/io/libio.a \ - $(DEPTH)/mt/libmt.a \ - $(DEPTH)/base/libbase.a \ - $(X_LIBS) \ - $(X_PRE_LIBS) \ - -lXtst \ - -lXext \ - -lX11 \ - $(X_EXTRA_LIBS) \ - $(NULL) -INCLUDES = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/io \ - -I$(DEPTH)/http \ - -I$(DEPTH)/net \ - -I$(DEPTH)/synergy \ - -I$(DEPTH)/platform \ - $(NULL) diff --git a/synergy.dsw b/synergy.dsw index 2ba5beae..efbf7453 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -12,16 +12,16 @@ Package=<5> Package=<4> {{{ Begin Project Dependency - Project_Dep_Name client + Project_Dep_Name synergy End Project Dependency Begin Project Dependency - Project_Dep_Name server + Project_Dep_Name synergyd End Project Dependency }}} ############################################################################### -Project: "base"=.\base\base.dsp - Package Owner=<4> +Project: "base"=.\lib\base\base.dsp - Package Owner=<4> Package=<5> {{{ @@ -33,7 +33,106 @@ Package=<4> ############################################################################### -Project: "client"=.\client\client.dsp - Package Owner=<4> +Project: "client"=.\lib\client\client.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "http"=.\lib\HTTP\http.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "io"=.\lib\io\io.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "makehook"=.\lib\platform\makehook.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name synrgyhk + End Project Dependency +}}} + +############################################################################### + +Project: "mt"=.\lib\mt\mt.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "net"=.\lib\net\net.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "platform"=.\lib\platform\platform.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "server"=.\lib\server\server.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "synergy"=.\cmd\synergy\synergy.dsp - Package Owner=<4> Package=<5> {{{ @@ -54,91 +153,19 @@ Package=<4> Project_Dep_Name net End Project Dependency Begin Project Dependency - Project_Dep_Name synergy + Project_Dep_Name libsynergy End Project Dependency Begin Project Dependency Project_Dep_Name platform End Project Dependency -}}} - -############################################################################### - -Project: "http"=.\HTTP\http.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "io"=.\io\io.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "makehook"=.\platform\makehook.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ Begin Project Dependency - Project_Dep_Name synrgyhk + Project_Dep_Name client End Project Dependency }}} ############################################################################### -Project: "mt"=.\mt\mt.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "net"=.\net\net.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "platform"=.\platform\platform.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "server"=.\server\server.dsp - Package Owner=<4> +Project: "synergyd"=.\cmd\synergyd\synergyd.dsp - Package Owner=<4> Package=<5> {{{ @@ -162,7 +189,7 @@ Package=<4> Project_Dep_Name net End Project Dependency Begin Project Dependency - Project_Dep_Name synergy + Project_Dep_Name libsynergy End Project Dependency Begin Project Dependency Project_Dep_Name platform @@ -170,11 +197,14 @@ Package=<4> Begin Project Dependency Project_Dep_Name makehook End Project Dependency + Begin Project Dependency + Project_Dep_Name server + End Project Dependency }}} ############################################################################### -Project: "synergy"=.\synergy\synergy.dsp - Package Owner=<4> +Project: "libsynergy"=.\lib\synergy\libsynergy.dsp - Package Owner=<4> Package=<5> {{{ @@ -186,7 +216,7 @@ Package=<4> ############################################################################### -Project: "synrgyhk"=.\platform\synrgyhk.dsp - Package Owner=<4> +Project: "synrgyhk"=.\lib\platform\synrgyhk.dsp - Package Owner=<4> Package=<5> {{{ From 69496abbab8b9829b5a407b1f4dd53d7ea639508 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Jul 2002 18:31:00 +0000 Subject: [PATCH 290/807] now using class factories to move some decisions from the libraries into the application. --- cmd/synergy/synergy.cpp | 38 +++++++++ cmd/synergyd/synergyd.cpp | 39 +++++++++ lib/client/CClient.cpp | 78 ++++++++++++------ lib/client/CClient.h | 30 +++++++ lib/client/ISecondaryScreenFactory.h | 24 ++++++ lib/client/Makefile.am | 1 + lib/io/IStreamFilterFactory.h | 34 ++++++++ lib/io/Makefile.am | 1 + lib/{synergy => net}/CTCPSocketFactory.cpp | 0 lib/{synergy => net}/CTCPSocketFactory.h | 0 lib/{synergy => net}/ISocketFactory.h | 0 lib/net/Makefile.am | 5 +- lib/server/CPrimaryClient.cpp | 21 ++--- lib/server/CPrimaryClient.h | 7 +- lib/server/CServer.cpp | 92 ++++++++++++++-------- lib/server/CServer.h | 33 +++++++- lib/server/IPrimaryScreenFactory.h | 25 ++++++ lib/server/Makefile.am | 1 + lib/synergy/Makefile.am | 3 - lib/synergy/ProtocolTypes.h | 22 +++++- 20 files changed, 370 insertions(+), 84 deletions(-) create mode 100644 lib/client/ISecondaryScreenFactory.h create mode 100644 lib/io/IStreamFilterFactory.h rename lib/{synergy => net}/CTCPSocketFactory.cpp (100%) rename lib/{synergy => net}/CTCPSocketFactory.h (100%) rename lib/{synergy => net}/ISocketFactory.h (100%) create mode 100644 lib/server/IPrimaryScreenFactory.h diff --git a/cmd/synergy/synergy.cpp b/cmd/synergy/synergy.cpp index a8b5b3f9..65256702 100644 --- a/cmd/synergy/synergy.cpp +++ b/cmd/synergy/synergy.cpp @@ -1,9 +1,11 @@ #include "CClient.h" +#include "ISecondaryScreenFactory.h" #include "CPlatform.h" #include "ProtocolTypes.h" #include "Version.h" #include "CNetwork.h" #include "CNetworkAddress.h" +#include "CTCPSocketFactory.h" #include "XSocket.h" #include "CCondVar.h" #include "CLock.h" @@ -14,6 +16,12 @@ #include "CString.h" #include +#if WINDOWS_LIKE +#include "CMSWindowsSecondaryScreen.h" +#elif UNIX_LIKE +#include "CXWindowsSecondaryScreen.h" +#endif + // platform dependent name of a daemon #if WINDOWS_LIKE #define DAEMON_NAME "Synergy Client" @@ -57,6 +65,31 @@ logLock(bool lock) } +// +// platform dependent factories +// + +class CSecondaryScreenFactory : public ISecondaryScreenFactory { +public: + CSecondaryScreenFactory() { } + virtual ~CSecondaryScreenFactory() { } + + // ISecondaryScreenFactory overrides + virtual CSecondaryScreen* + create(IScreenReceiver*); +}; + +CSecondaryScreen* +CSecondaryScreenFactory::create(IScreenReceiver* receiver) +{ +#if WINDOWS_LIKE + return new CMSWindowsSecondaryScreen(receiver); +#elif UNIX_LIKE + return new CXWindowsSecondaryScreen(receiver); +#endif +} + + // // platform independent main // @@ -87,6 +120,11 @@ realMain(CMutex* mutex) s_client = new CClient(s_name); s_client->camp(s_camp); s_client->setAddress(s_serverAddress); + s_client->setScreenFactory(new CSecondaryScreenFactory); + s_client->setSocketFactory(new CTCPSocketFactory); + s_client->setStreamFilterFactory(NULL); + + // open client if (s_client->open()) { opened = true; diff --git a/cmd/synergyd/synergyd.cpp b/cmd/synergyd/synergyd.cpp index 0a308d34..2db390a3 100644 --- a/cmd/synergyd/synergyd.cpp +++ b/cmd/synergyd/synergyd.cpp @@ -1,9 +1,11 @@ #include "CServer.h" #include "CConfig.h" +#include "IPrimaryScreenFactory.h" #include "CPlatform.h" #include "ProtocolTypes.h" #include "Version.h" #include "CNetwork.h" +#include "CTCPSocketFactory.h" #include "XSocket.h" #include "CLock.h" #include "CMutex.h" @@ -13,6 +15,12 @@ #include "stdfstream.h" #include +#if WINDOWS_LIKE +#include "CMSWindowsPrimaryScreen.h" +#elif UNIX_LIKE +#include "CXWindowsPrimaryScreen.h" +#endif + // platform dependent name of a daemon #if WINDOWS_LIKE #define DAEMON_NAME "Synergy Server" @@ -67,6 +75,32 @@ logLock(bool lock) } +// +// platform dependent factories +// + +class CPrimaryScreenFactory : public IPrimaryScreenFactory { +public: + CPrimaryScreenFactory() { } + virtual ~CPrimaryScreenFactory() { } + + // IPrimaryScreenFactory overrides + virtual CPrimaryScreen* + create(IScreenReceiver*, IPrimaryScreenReceiver*); +}; + +CPrimaryScreen* +CPrimaryScreenFactory::create(IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) +{ +#if WINDOWS_LIKE + return new CMSWindowsPrimaryScreen(receiver, primaryReceiver); +#elif UNIX_LIKE + return new CXWindowsPrimaryScreen(receiver, primaryReceiver); +#endif +} + + // // platform independent main // @@ -117,6 +151,11 @@ realMain(CMutex* mutex) // create server s_server = new CServer(s_name); s_server->setConfig(s_config); + s_server->setScreenFactory(new CPrimaryScreenFactory); + s_server->setSocketFactory(new CTCPSocketFactory); + s_server->setStreamFilterFactory(NULL); + + // open server if (s_server->open()) { opened = true; diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 54a28ee2..6a70c2f8 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -1,5 +1,6 @@ #include "CClient.h" #include "CServerProxy.h" +#include "ISecondaryScreenFactory.h" #include "CClipboard.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" @@ -9,7 +10,10 @@ #include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" +#include "IDataSocket.h" +#include "ISocketFactory.h" #include "XSocket.h" +#include "IStreamFilterFactory.h" #include "CLock.h" #include "CThread.h" #include "CTimerThread.h" @@ -27,6 +31,9 @@ CClient::CClient(const CString& clientName) : m_screen(NULL), m_server(NULL), m_camp(false), + m_screenFactory(NULL), + m_socketFactory(NULL), + m_streamFilterFactory(NULL), m_session(NULL), m_active(false), m_rejected(true) @@ -36,7 +43,9 @@ CClient::CClient(const CString& clientName) : CClient::~CClient() { - // do nothing + delete m_screenFactory; + delete m_socketFactory; + delete m_streamFilterFactory; } void @@ -53,6 +62,30 @@ CClient::setAddress(const CNetworkAddress& serverAddress) m_serverAddress = serverAddress; } +void +CClient::setScreenFactory(ISecondaryScreenFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_screenFactory; + m_screenFactory = adopted; +} + +void +CClient::setSocketFactory(ISocketFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_socketFactory; + m_socketFactory = adopted; +} + +void +CClient::setStreamFilterFactory(IStreamFilterFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_streamFilterFactory; + m_streamFilterFactory = adopted; +} + void CClient::exitMainLoop() { @@ -331,16 +364,11 @@ CClient::getCursorCenter(SInt32&, SInt32&) const assert(0 && "shouldn't be called"); } -// FIXME -- use factory to create screen -#if WINDOWS_LIKE -#include "CMSWindowsSecondaryScreen.h" -#elif UNIX_LIKE -#include "CXWindowsSecondaryScreen.h" -#endif void CClient::openSecondaryScreen() { assert(m_screen == NULL); + assert(m_screenFactory != NULL); // not active m_active = false; @@ -351,15 +379,16 @@ CClient::openSecondaryScreen() m_timeClipboard[id] = 0; } - // open screen + // create screen log((CLOG_DEBUG1 "creating secondary screen")); -#if WINDOWS_LIKE - m_screen = new CMSWindowsSecondaryScreen(this); -#elif UNIX_LIKE - m_screen = new CXWindowsSecondaryScreen(this); -#endif - log((CLOG_DEBUG1 "opening secondary screen")); + m_screen = m_screenFactory->create(this); + if (m_screen == NULL) { + throw XScreenOpenFailure(); + } + + // open screen try { + log((CLOG_DEBUG1 "opening secondary screen")); m_screen->open(); } catch (...) { @@ -459,7 +488,6 @@ CClient::deleteSession(double timeout) } } -#include "CTCPSocket.h" // FIXME void CClient::runServer() { @@ -469,12 +497,14 @@ CClient::runServer() for (;;) { try { // allow connect this much time to succeed - // FIXME -- timeout in member CTimerThread timer(m_camp ? -1.0 : 30.0); // create socket and attempt to connect to server log((CLOG_DEBUG1 "connecting to server")); - socket = new CTCPSocket; // FIXME -- use factory + if (m_socketFactory != NULL) { + socket = m_socketFactory->create(); + } + assert(socket != NULL); socket->connect(m_serverAddress); log((CLOG_INFO "connected to server")); break; @@ -553,14 +583,12 @@ CClient::handshakeServer(IDataSocket* socket) IOutputStream* output = socket->getOutputStream(); bool own = false; - // attach the encryption layer -/* FIXME -- implement ISecurityFactory - if (m_securityFactory != NULL) { - input = m_securityFactory->createInputFilter(input, own); - output = m_securityFactory->createOutputFilter(output, own); + // attach filters + if (m_streamFilterFactory != NULL) { + input = m_streamFilterFactory->createInput(input, own); + output = m_streamFilterFactory->createOutput(output, own); own = true; } -*/ // attach the packetizing filters input = new CInputPacketStream(input, own); @@ -575,7 +603,7 @@ CClient::handshakeServer(IDataSocket* socket) // wait for hello from server log((CLOG_DEBUG1 "wait for hello")); SInt16 major, minor; - CProtocolUtil::readf(input, "Synergy%2i%2i", &major, &minor); + CProtocolUtil::readf(input, kMsgHello, &major, &minor); // check versions log((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); @@ -586,7 +614,7 @@ CClient::handshakeServer(IDataSocket* socket) // say hello back log((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); - CProtocolUtil::writef(output, "Synergy%2i%2i%s", + CProtocolUtil::writef(output, kMsgHelloBack, kProtocolMajorVersion, kProtocolMinorVersion, &m_name); diff --git a/lib/client/CClient.h b/lib/client/CClient.h index e4be951f..3cfb4543 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -12,6 +12,9 @@ class CServerProxy; class CThread; class IDataSocket; class IScreenReceiver; +class ISecondaryScreenFactory; +class ISocketFactory; +class IStreamFilterFactory; //! Synergy client /*! @@ -44,6 +47,30 @@ public: */ void setAddress(const CNetworkAddress& serverAddress); + //! Set secondary screen factory + /*! + Sets the factory for creating secondary screens. This must be + set before calling open(). This object takes ownership of the + factory. + */ + void setScreenFactory(ISecondaryScreenFactory*); + + //! Set socket factory + /*! + Sets the factory used to create a socket to connect to the server. + This must be set before calling mainLoop(). This object takes + ownership of the factory. + */ + void setSocketFactory(ISocketFactory*); + + //! Set stream filter factory + /*! + Sets the factory used to filter the socket streams used to + communicate with the server. This object takes ownership + of the factory. + */ + void setStreamFilterFactory(IStreamFilterFactory*); + //! Exit event loop /*! Force mainLoop() to return. This call can return before @@ -117,6 +144,9 @@ private: IScreenReceiver* m_server; CNetworkAddress m_serverAddress; bool m_camp; + ISecondaryScreenFactory* m_screenFactory; + ISocketFactory* m_socketFactory; + IStreamFilterFactory* m_streamFilterFactory; CThread* m_session; bool m_active; bool m_rejected; diff --git a/lib/client/ISecondaryScreenFactory.h b/lib/client/ISecondaryScreenFactory.h new file mode 100644 index 00000000..57ae3b16 --- /dev/null +++ b/lib/client/ISecondaryScreenFactory.h @@ -0,0 +1,24 @@ +#ifndef ISECONDARYSCREENFACTORY_H +#define ISECONDARYSCREENFACTORY_H + +#include "IInterface.h" + +class CSecondaryScreen; +class IScreenReceiver; + +//! Secondary screen factory interface +/*! +This interface provides factory methods to create secondary screens. +*/ +class ISecondaryScreenFactory : public IInterface { +public: + //! Create screen + /*! + Create and return a secondary screen. The caller must delete the + returned object. + */ + virtual CSecondaryScreen* + create(IScreenReceiver*) = 0; +}; + +#endif diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index 42001eae..c489709c 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -12,6 +12,7 @@ libclient_a_SOURCES = \ CSecondaryScreen.h \ CServerProxy.h \ CXWindowsSecondaryScreen.h \ + ISecondaryScreenFactory.h \ $(NULL) INCLUDES = \ -I$(DEPTH)/lib/base \ diff --git a/lib/io/IStreamFilterFactory.h b/lib/io/IStreamFilterFactory.h new file mode 100644 index 00000000..e7cd50a2 --- /dev/null +++ b/lib/io/IStreamFilterFactory.h @@ -0,0 +1,34 @@ +#ifndef ISTREAMFILTERFACTORY_H +#define ISTREAMFILTERFACTORY_H + +#include "IInterface.h" + +class CInputStreamFilter; +class COutputStreamFilter; +class IInputStream; +class IOutputStream; + +//! Stream filter factory interface +/*! +This interface provides factory methods to create stream filters. +*/ +class IStreamFilterFactory : public IInterface { +public: + //! Create input filter + /*! + Create and return an input stream filter. The caller must delete the + returned object. + */ + virtual CInputStreamFilter* + createInput(IInputStream*, bool adoptStream) = 0; + + //! Create output filter + /*! + Create and return an output stream filter. The caller must delete the + returned object. + */ + virtual COutputStreamFilter* + createOutput(IOutputStream*, bool adoptStream) = 0; +}; + +#endif diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index 67ff14a8..6e134dfa 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -17,6 +17,7 @@ libio_a_SOURCES = \ CStreamBuffer.h \ IInputStream.h \ IOutputStream.h \ + IStreamFilterFactory.h \ XIO.h \ $(NULL) INCLUDES = \ diff --git a/lib/synergy/CTCPSocketFactory.cpp b/lib/net/CTCPSocketFactory.cpp similarity index 100% rename from lib/synergy/CTCPSocketFactory.cpp rename to lib/net/CTCPSocketFactory.cpp diff --git a/lib/synergy/CTCPSocketFactory.h b/lib/net/CTCPSocketFactory.h similarity index 100% rename from lib/synergy/CTCPSocketFactory.h rename to lib/net/CTCPSocketFactory.h diff --git a/lib/synergy/ISocketFactory.h b/lib/net/ISocketFactory.h similarity index 100% rename from lib/synergy/ISocketFactory.h rename to lib/net/ISocketFactory.h diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index f12a316f..cc996705 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -6,17 +6,20 @@ noinst_LIBRARIES = libnet.a libnet_a_SOURCES = \ CNetwork.cpp \ CNetworkAddress.cpp \ - CTCPSocket.cpp \ CTCPListenSocket.cpp \ + CTCPSocket.cpp \ + CTCPSocketFactory.cpp \ XNetwork.cpp \ XSocket.cpp \ CNetwork.h \ CNetworkAddress.h \ CTCPListenSocket.h \ CTCPSocket.h \ + CTCPSocketFactory.h \ IDataSocket.h \ IListenSocket.h \ ISocket.h \ + ISocketFactory.h \ XNetwork.h \ XSocket.h \ $(NULL) diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index a1e3113d..44f01109 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -1,36 +1,29 @@ #include "CPrimaryClient.h" +#include "IPrimaryScreenFactory.h" #include "IServer.h" #include "XSynergy.h" #include "CPrimaryScreen.h" #include "CClipboard.h" #include "CLog.h" -// FIXME -- use factory to create screen -#if WINDOWS_LIKE -#include "CMSWindowsPrimaryScreen.h" -#elif UNIX_LIKE -#include "CXWindowsPrimaryScreen.h" -#endif - // // CPrimaryClient // -CPrimaryClient::CPrimaryClient(IServer* server, - IPrimaryScreenReceiver* receiver, const CString& name) : +CPrimaryClient::CPrimaryClient(IPrimaryScreenFactory* screenFactory, + IServer* server, + IPrimaryScreenReceiver* receiver, + const CString& name) : m_server(server), m_name(name), m_seqNum(0) { assert(m_server != NULL); + assert(screenFactory != NULL); // create screen log((CLOG_DEBUG1 "creating primary screen")); -#if WINDOWS_LIKE - m_screen = new CMSWindowsPrimaryScreen(this, receiver); -#elif UNIX_LIKE - m_screen = new CXWindowsPrimaryScreen(this, receiver); -#endif + m_screen = screenFactory->create(this, receiver); } CPrimaryClient::~CPrimaryClient() diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 6631203e..84ea0bad 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -7,6 +7,7 @@ class IClipboard; class CPrimaryScreen; +class IPrimaryScreenFactory; class IPrimaryScreenReceiver; class IServer; @@ -19,9 +20,11 @@ treated as if it was on a client. class CPrimaryClient : public IScreenReceiver, public IClient { public: /*! - \c name is the name of the server. + \c name is the name of the server. the caller retains ownership of + \c factory. */ - CPrimaryClient(IServer*, IPrimaryScreenReceiver*, const CString& name); + CPrimaryClient(IPrimaryScreenFactory* factory, IServer*, + IPrimaryScreenReceiver*, const CString& name); ~CPrimaryClient(); //! @name manipulators diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index a4138b1e..822bac97 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -1,17 +1,19 @@ #include "CServer.h" #include "CHTTPServer.h" +#include "CPrimaryClient.h" +#include "IPrimaryScreenFactory.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" -#include "CPrimaryClient.h" #include "CProtocolUtil.h" #include "CClientProxy1_0.h" #include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" +#include "CTCPListenSocket.h" #include "IDataSocket.h" -#include "IListenSocket.h" #include "ISocketFactory.h" #include "XSocket.h" +#include "IStreamFilterFactory.h" #include "CLock.h" #include "CThread.h" #include "CTimerThread.h" @@ -30,8 +32,9 @@ const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; CServer::CServer(const CString& serverName) : m_name(serverName), m_bindTimeout(5.0 * 60.0), + m_screenFactory(NULL), m_socketFactory(NULL), - m_securityFactory(NULL), + m_streamFilterFactory(NULL), m_acceptClientThread(NULL), m_active(NULL), m_primaryClient(NULL), @@ -45,7 +48,9 @@ CServer::CServer(const CString& serverName) : CServer::~CServer() { - // do nothing + delete m_screenFactory; + delete m_socketFactory; + delete m_streamFilterFactory; } bool @@ -179,6 +184,30 @@ CServer::setConfig(const CConfig& config) return true; } +void +CServer::setScreenFactory(IPrimaryScreenFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_screenFactory; + m_screenFactory = adopted; +} + +void +CServer::setSocketFactory(ISocketFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_socketFactory; + m_socketFactory = adopted; +} + +void +CServer::setStreamFilterFactory(IStreamFilterFactory* adopted) +{ + CLock lock(&m_mutex); + delete m_streamFilterFactory; + m_streamFilterFactory = adopted; +} + CString CServer::getPrimaryScreenName() const { @@ -695,6 +724,8 @@ CServer::isLockedToScreenNoLock() const void CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) { + // note -- must be locked on entry + assert(dst != NULL); #ifndef NDEBUG { @@ -706,7 +737,6 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) assert(m_active != NULL); log((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y)); - // FIXME -- we're not locked here but we probably should be // record new position m_x = x; @@ -1112,7 +1142,6 @@ CServer::doReapThreads(CThreadList& threads) } } -#include "CTCPListenSocket.h" void CServer::acceptClients(void*) { @@ -1121,8 +1150,10 @@ CServer::acceptClients(void*) IListenSocket* listen = NULL; try { // create socket listener -// listen = std::auto_ptr(m_socketFactory->createListen()); - listen = new CTCPListenSocket; // FIXME -- use factory + if (m_socketFactory != NULL) { + listen = m_socketFactory->createListen(); + } + assert(listen != NULL); // bind to the desired port. keep retrying if we can't bind // the address immediately. @@ -1242,14 +1273,12 @@ CServer::runClient(void* vsocket) } catch (XBadClient&) { // client not behaving - // FIXME -- could print network address if socket had suitable method log((CLOG_WARN "protocol error from client \"%s\"", proxy->getName().c_str())); CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); } catch (XBase& e) { // misc error log((CLOG_WARN "error communicating with client \"%s\": %s", proxy->getName().c_str(), e.what())); - // FIXME -- could print network address if socket had suitable method } catch (...) { // mainLoop() was probably cancelled @@ -1273,13 +1302,11 @@ CServer::handshakeClient(IDataSocket* socket) IOutputStream* output = socket->getOutputStream(); bool own = false; - // attach the encryption layer - if (m_securityFactory != NULL) { -/* FIXME -- implement ISecurityFactory - input = m_securityFactory->createInputFilter(input, own); - output = m_securityFactory->createOutputFilter(output, own); + // attach filters + if (m_streamFilterFactory != NULL) { + input = m_streamFilterFactory->createInput(input, own); + output = m_streamFilterFactory->createOutput(output, own); own = true; -*/ } // attach the packetizing filters @@ -1293,13 +1320,9 @@ CServer::handshakeClient(IDataSocket* socket) // give the client a limited time to complete the handshake CTimerThread timer(30.0); - // limit the maximum length of the hello -// FIXME -- should be in protocol types; may become obsolete anyway - static const UInt32 maxHelloLen = 1024; - // say hello log((CLOG_DEBUG1 "saying hello")); - CProtocolUtil::writef(output, "Synergy%2i%2i", + CProtocolUtil::writef(output, kMsgHello, kProtocolMajorVersion, kProtocolMinorVersion); output->flush(); @@ -1307,7 +1330,9 @@ CServer::handshakeClient(IDataSocket* socket) // wait for the reply log((CLOG_DEBUG1 "waiting for hello reply")); UInt32 n = input->getSize(); - if (n > maxHelloLen) { + + // limit the maximum length of the hello + if (n > kMaxHelloLength) { throw XBadClient(); } @@ -1315,7 +1340,7 @@ CServer::handshakeClient(IDataSocket* socket) SInt16 major, minor; try { log((CLOG_DEBUG1 "parsing hello reply")); - CProtocolUtil::readf(input, "Synergy%2i%2i%s", + CProtocolUtil::readf(input, kMsgHelloBack, &major, &minor, &name); } catch (XIO&) { @@ -1358,21 +1383,18 @@ CServer::handshakeClient(IDataSocket* socket) } catch (XIncompatibleClient& e) { // client is incompatible - // FIXME -- could print network address if socket had suitable method log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); CProtocolUtil::writef(output, kMsgEIncompatible, kProtocolMajorVersion, kProtocolMinorVersion); } catch (XBadClient&) { // client not behaving - // FIXME -- could print network address if socket had suitable method log((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); CProtocolUtil::writef(output, kMsgEBad); } catch (XBase& e) { // misc error log((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); - // FIXME -- could print network address if socket had suitable method } catch (...) { // probably timed out @@ -1406,8 +1428,7 @@ CServer::acceptHTTPClients(void*) IListenSocket* listen = NULL; try { // create socket listener -// listen = std::auto_ptr(m_socketFactory->createListen()); - listen = new CTCPListenSocket; // FIXME -- use factory + listen = new CTCPListenSocket; // bind to the desired port. keep retrying if we can't bind // the address immediately. @@ -1462,7 +1483,6 @@ CServer::acceptHTTPClients(void*) catch (XBase& e) { log((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); delete listen; - // FIXME -- quit? exitMainLoop(); } catch (...) { @@ -1509,20 +1529,21 @@ void CServer::openPrimaryScreen() { assert(m_primaryClient == NULL); + assert(m_screenFactory != NULL); // reset sequence number m_seqNum = 0; // canonicalize the primary screen name - CString primary = m_config.getCanonicalName(getPrimaryScreenName()); - if (primary.empty()) { + CString primaryName = m_config.getCanonicalName(getPrimaryScreenName()); + if (primaryName.empty()) { throw XUnknownClient(getPrimaryScreenName()); } // clear clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) { CClipboardInfo& clipboard = m_clipboards[id]; - clipboard.m_clipboardOwner = primary; + clipboard.m_clipboardOwner = primaryName; clipboard.m_clipboardSeqNum = m_seqNum; if (clipboard.m_clipboard.open(0)) { clipboard.m_clipboard.empty(); @@ -1531,9 +1552,10 @@ CServer::openPrimaryScreen() clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); } - // create the primary client and open it try { - m_primaryClient = new CPrimaryClient(this, this, primary); + // create the primary client + m_primaryClient = new CPrimaryClient(m_screenFactory, + this, this, primaryName); // add connection addConnection(m_primaryClient); @@ -1548,7 +1570,7 @@ CServer::openPrimaryScreen() } catch (...) { if (m_active != NULL) { - removeConnection(primary); + removeConnection(primaryName); } else { delete m_primaryClient; diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 43d58879..a119fa6a 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -16,9 +16,10 @@ class CHTTPServer; class CPrimaryClient; class IClient; class IDataSocket; +class IPrimaryScreenFactory; class IServerProtocol; class ISocketFactory; -class ISecurityFactory; +class IStreamFilterFactory; //! Synergy server /*! @@ -73,6 +74,30 @@ public: */ bool setConfig(const CConfig&); + //! Set primary screen factory + /*! + Sets the factory for creating primary screens. This must be + set before calling open(). This object takes ownership of the + factory. + */ + void setScreenFactory(IPrimaryScreenFactory*); + + //! Set socket factory + /*! + Sets the factory used to create a socket to connect to the server. + This must be set before calling mainLoop(). This object takes + ownership of the factory. + */ + void setSocketFactory(ISocketFactory*); + + //! Set stream filter factory + /*! + Sets the factory used to filter the socket streams used to + communicate with the server. This object takes ownership + of the factory. + */ + void setStreamFilterFactory(IStreamFilterFactory*); + //@} //! @name accessors //@{ @@ -207,8 +232,10 @@ private: // how long to wait to bind our socket until we give up double m_bindTimeout; - ISocketFactory* m_socketFactory; - ISecurityFactory* m_securityFactory; + // factories + IPrimaryScreenFactory* m_screenFactory; + ISocketFactory* m_socketFactory; + IStreamFilterFactory* m_streamFilterFactory; // running threads CThreadList m_threads; diff --git a/lib/server/IPrimaryScreenFactory.h b/lib/server/IPrimaryScreenFactory.h new file mode 100644 index 00000000..ccb62951 --- /dev/null +++ b/lib/server/IPrimaryScreenFactory.h @@ -0,0 +1,25 @@ +#ifndef IPRIMARYSCREENFACTORY_H +#define IPRIMARYSCREENFACTORY_H + +#include "IInterface.h" + +class CPrimaryScreen; +class IPrimaryScreenReceiver; +class IScreenReceiver; + +//! Primary screen factory interface +/*! +This interface provides factory methods to create primary screens. +*/ +class IPrimaryScreenFactory : public IInterface { +public: + //! Create screen + /*! + Create and return a primary screen. The caller must delete the + returned object. + */ + virtual CPrimaryScreen* + create(IScreenReceiver*, IPrimaryScreenReceiver*) = 0; +}; + +#endif diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index 36f6e393..db86137b 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -20,6 +20,7 @@ libserver_a_SOURCES = \ CPrimaryScreen.h \ CServer.h \ CXWindowsPrimaryScreen.h \ + IPrimaryScreenFactory.h \ $(NULL) INCLUDES = \ -I$(DEPTH)/lib/base \ diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 55adf09e..de13e200 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -8,14 +8,12 @@ libsynergy_a_SOURCES = \ COutputPacketStream.cpp \ CProtocolUtil.cpp \ CClipboard.cpp \ - CTCPSocketFactory.cpp \ XScreen.cpp \ XSynergy.cpp \ CClipboard.h \ CInputPacketStream.h \ COutputPacketStream.h \ CProtocolUtil.h \ - CTCPSocketFactory.h \ ClipboardTypes.h \ IClient.h \ IClipboard.h \ @@ -25,7 +23,6 @@ libsynergy_a_SOURCES = \ IScreenReceiver.h \ IScreenSaver.h \ IServer.h \ - ISocketFactory.h \ KeyTypes.h \ MouseTypes.h \ ProtocolTypes.h \ diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index 3e14cd13..7417b270 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -10,6 +10,9 @@ static const SInt16 kProtocolMinorVersion = 7; // default contact port number static const UInt16 kDefaultPort = 24800; +// maximum total length for greeting returned by client +static const UInt32 kMaxHelloLength = 1024; + // time between heartbeats (in seconds) static const double kHeartRate = 2.0; @@ -36,13 +39,30 @@ enum EDirectionMask { // // message codes (trailing NUL is not part of code). in comments, $n // refers to the n'th argument (counting from one). message codes are -// always 4 bytes optionally followed by message specific parameters. +// always 4 bytes optionally followed by message specific parameters +// except those for the greeting handshake. // // // positions and sizes are signed 16 bit integers. // +// +// greeting handshake messages +// + +// say hello to client; primary -> secondary +// $1 = protocol major version number supported by server. $2 = +// protocol minor version number supported by server. +static const char kMsgHello[] = "Synergy%2i%2i"; + +// respond to hello from server; secondary -> primary +// $1 = protocol major version number supported by client. $2 = +// protocol minor version number supported by client. $3 = client +// name. +static const char kMsgHelloBack[] = "Synergy%2i%2i%s"; + + // // command codes // From da361e9686d581b44e6c09c47955a28603e04f04 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Jul 2002 18:31:21 +0000 Subject: [PATCH 291/807] moved exception definition to header file. --- lib/mt/CThreadRep.cpp | 3 --- lib/mt/XThread.h | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp index 82e15992..ceff76c2 100644 --- a/lib/mt/CThreadRep.cpp +++ b/lib/mt/CThreadRep.cpp @@ -24,9 +24,6 @@ #endif -// FIXME -- temporary exception type -class XThreadUnavailable { }; - // // CThreadRep // diff --git a/lib/mt/XThread.h b/lib/mt/XThread.h index 3a5e59e1..700c195b 100644 --- a/lib/mt/XThread.h +++ b/lib/mt/XThread.h @@ -27,6 +27,12 @@ must rethrow it if caught (by XThreadCancel, XThread, or ...). */ class XThreadCancel : public XThread { }; +//! Thread creation exception +/*! +Thrown when a thread cannot be created. +*/ +class XThreadUnavailable { }; + /*! \def RETHROW_XTHREAD Convenience macro to rethrow an XThread exception but ignore other From 0d70150589a01d52c000e734c61545b07fefbf09 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Jul 2002 18:49:31 +0000 Subject: [PATCH 292/807] made it so a negative kHeartRate disables heartbeats and set kHeartRate to -1. --- lib/client/CServerProxy.cpp | 3 ++- lib/server/CClientProxy1_0.cpp | 4 +--- lib/synergy/ProtocolTypes.h | 7 ++++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index ce094faf..8436a62a 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -66,7 +66,8 @@ CServerProxy::mainLoop() } // check for time out - if (n == (UInt32)-1 || heartbeat.getTime() > kHeartRate) { + if (n == (UInt32)-1 || + (kHeartRate >= 0.0 && heartbeat.getTime() > kHeartRate)) { // send heartbeat CLock lock(&m_mutex); CProtocolUtil::writef(getOutputStream(), kMsgCNoop); diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 88b8b429..ad0d45f1 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -80,12 +80,10 @@ CClientProxy1_0::mainLoop() // check if client has stopped sending heartbeats if (n == (UInt32)-1) { -/* FIXME -- disabled to avoid masking bugs - if (heartTimer.getTime() > kHeartDeath) { + if (kHeartDeath >= 0.0 && heartTimer.getTime() > kHeartDeath) { log((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); return; } -*/ continue; } diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index 7417b270..d524d98b 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -13,10 +13,11 @@ static const UInt16 kDefaultPort = 24800; // maximum total length for greeting returned by client static const UInt32 kMaxHelloLength = 1024; -// time between heartbeats (in seconds) -static const double kHeartRate = 2.0; +// time between heartbeats (in seconds). negative value disables +// heartbeat. +static const double kHeartRate = -1.0; -// time without a heartbeat that we call death +// time without a heartbeat that constitutes death static const double kHeartDeath = 3.0 * kHeartRate; // direction constants From f5795a6630c67f11975d275b71b8d8b378ebdcb6 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Jul 2002 19:03:40 +0000 Subject: [PATCH 293/807] added new files to projects and added two project files that should've been adding in change 530. --- lib/client/client.dsp | 134 ++++++++++++++++++++++++++++++ lib/io/io.dsp | 4 + lib/net/net.dsp | 12 +++ lib/server/server.dsp | 166 +++++++++++++++++++++++++++++++++++++ lib/synergy/libsynergy.dsp | 12 --- 5 files changed, 316 insertions(+), 12 deletions(-) create mode 100644 lib/client/client.dsp create mode 100644 lib/server/server.dsp diff --git a/lib/client/client.dsp b/lib/client/client.dsp new file mode 100644 index 00000000..985ffe88 --- /dev/null +++ b/lib/client/client.dsp @@ -0,0 +1,134 @@ +# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=client - Win32 Debug +!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 "client.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 "client.mak" CFG="client - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "client - Win32 Debug" (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)" == "client - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "client - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "client - Win32 Release" +# Name "client - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSecondaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerProxy.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClient.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CSecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CServerProxy.h +# End Source File +# Begin Source File + +SOURCE=.\ISecondaryScreenFactory.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/io/io.dsp b/lib/io/io.dsp index b53b3236..0631be2f 100644 --- a/lib/io/io.dsp +++ b/lib/io/io.dsp @@ -143,6 +143,10 @@ SOURCE=.\IOutputStream.h # End Source File # Begin Source File +SOURCE=.\IStreamFilterFactory.h +# End Source File +# Begin Source File + SOURCE=.\XIO.h # End Source File # End Group diff --git a/lib/net/net.dsp b/lib/net/net.dsp index 2e11719b..04cfe3ef 100644 --- a/lib/net/net.dsp +++ b/lib/net/net.dsp @@ -103,6 +103,10 @@ SOURCE=.\CTCPSocket.cpp # End Source File # Begin Source File +SOURCE=.\CTCPSocketFactory.cpp +# End Source File +# Begin Source File + SOURCE=.\XNetwork.cpp # End Source File # Begin Source File @@ -131,6 +135,10 @@ SOURCE=.\CTCPSocket.h # End Source File # Begin Source File +SOURCE=.\CTCPSocketFactory.h +# End Source File +# Begin Source File + SOURCE=.\IListenSocket.h # End Source File # Begin Source File @@ -139,6 +147,10 @@ SOURCE=.\ISocket.h # End Source File # Begin Source File +SOURCE=.\ISocketFactory.h +# End Source File +# Begin Source File + SOURCE=.\XNetwork.h # End Source File # Begin Source File diff --git a/lib/server/server.dsp b/lib/server/server.dsp new file mode 100644 index 00000000..fb28378e --- /dev/null +++ b/lib/server/server.dsp @@ -0,0 +1,166 @@ +# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=server - Win32 Debug +!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 "server.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 "server.mak" CFG="server - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "server - Win32 Debug" (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)" == "server - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "server - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "server - Win32 Release" +# Name "server - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClientProxy.cpp +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.cpp +# End Source File +# Begin Source File + +SOURCE=.\CConfig.cpp +# End Source File +# Begin Source File + +SOURCE=.\CHTTPServer.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServer.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClientProxy.h +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.h +# End Source File +# Begin Source File + +SOURCE=.\CConfig.h +# End Source File +# Begin Source File + +SOURCE=.\CHTTPServer.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryClient.h +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CServer.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreenFactory.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 99cc99ac..79a88cc4 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -103,10 +103,6 @@ SOURCE=.\CProtocolUtil.cpp # End Source File # Begin Source File -SOURCE=.\CTCPSocketFactory.cpp -# End Source File -# Begin Source File - SOURCE=.\XScreen.cpp # End Source File # Begin Source File @@ -139,10 +135,6 @@ SOURCE=.\CProtocolUtil.h # End Source File # Begin Source File -SOURCE=.\CTCPSocketFactory.h -# End Source File -# Begin Source File - SOURCE=.\IClient.h # End Source File # Begin Source File @@ -175,10 +167,6 @@ SOURCE=.\IServer.h # End Source File # Begin Source File -SOURCE=.\ISocketFactory.h -# End Source File -# Begin Source File - SOURCE=.\KeyTypes.h # End Source File # Begin Source File From 5e40de48f9fdd3d31d464a6cd9ce796ece0a11e5 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 12:39:34 +0000 Subject: [PATCH 294/807] fixed problem with opening client and server. in some cases it would fail to open in such a way that it could never succeed but it'd never stop retrying. now terminating when open fails such that it'll never succeed. --- cmd/synergy/synergy.cpp | 36 ++++++++++++++----------- cmd/synergyd/synergyd.cpp | 18 ++++++++++--- lib/client/CClient.cpp | 20 +++++++------- lib/client/CClient.h | 2 +- lib/client/CXWindowsSecondaryScreen.cpp | 2 +- lib/platform/CXWindowsScreen.cpp | 2 +- lib/server/CClientProxy.h | 2 +- lib/server/CClientProxy1_0.cpp | 4 +-- lib/server/CClientProxy1_0.h | 2 +- lib/server/CMSWindowsPrimaryScreen.cpp | 1 + lib/server/CPrimaryClient.cpp | 13 +++++---- lib/server/CPrimaryClient.h | 7 ++--- lib/server/CServer.cpp | 14 +++++----- lib/server/CServer.h | 7 +++-- lib/synergy/IClient.h | 6 +++-- lib/synergy/IScreen.h | 4 ++- lib/synergy/XScreen.cpp | 28 +++++++++++++++++++ lib/synergy/XScreen.h | 32 ++++++++++++++++++++++ 18 files changed, 141 insertions(+), 59 deletions(-) diff --git a/cmd/synergy/synergy.cpp b/cmd/synergy/synergy.cpp index 65256702..06d0481c 100644 --- a/cmd/synergy/synergy.cpp +++ b/cmd/synergy/synergy.cpp @@ -3,6 +3,7 @@ #include "CPlatform.h" #include "ProtocolTypes.h" #include "Version.h" +#include "XScreen.h" #include "CNetwork.h" #include "CNetworkAddress.h" #include "CTCPSocketFactory.h" @@ -125,7 +126,8 @@ realMain(CMutex* mutex) s_client->setStreamFilterFactory(NULL); // open client - if (s_client->open()) { + try { + s_client->open(); opened = true; // run client @@ -139,31 +141,33 @@ realMain(CMutex* mutex) mutex->lock(); } - // get client status - if (s_client->wasRejected()) { - // wait a while before retrying. we don't want - // to bother the server very often if it doesn't - // want us. - if (s_restartable) { - CThread::sleep(60.0); - } - else { - result = kExitFailed; - } - } - // clean up s_client->close(); + + // get client status + if (s_client->wasRejected()) { + // try again later. we don't want to bother + // the server very often if it doesn't want us. + throw XScreenUnavailable(60.0); + } } - else { + catch (XScreenUnavailable& e) { // wait a few seconds before retrying if (s_restartable) { - CThread::sleep(3.0); + CThread::sleep(e.getRetryTime()); } else { result = kExitFailed; } } + catch (...) { + // rethrow thread exceptions + RETHROW_XTHREAD + + // don't try to restart and fail + s_restartable = false; + result = kExitFailed; + } // clean up delete s_client; diff --git a/cmd/synergyd/synergyd.cpp b/cmd/synergyd/synergyd.cpp index 2db390a3..60d070c4 100644 --- a/cmd/synergyd/synergyd.cpp +++ b/cmd/synergyd/synergyd.cpp @@ -4,6 +4,7 @@ #include "CPlatform.h" #include "ProtocolTypes.h" #include "Version.h" +#include "XScreen.h" #include "CNetwork.h" #include "CTCPSocketFactory.h" #include "XSocket.h" @@ -156,7 +157,8 @@ realMain(CMutex* mutex) s_server->setStreamFilterFactory(NULL); // open server - if (s_server->open()) { + try { + s_server->open(); opened = true; // run server (unlocked) @@ -173,15 +175,23 @@ realMain(CMutex* mutex) // clean up s_server->close(); } - else { - // wait a few seconds before retrying + catch (XScreenUnavailable& e) { + // wait before retrying if we're going to retry if (s_restartable) { - CThread::sleep(3.0); + CThread::sleep(e.getRetryTime()); } else { result = kExitFailed; } } + catch (...) { + // rethrow thread exceptions + RETHROW_XTHREAD + + // don't try to restart and fail + s_restartable = false; + result = kExitFailed; + } // clean up delete s_server; diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 6a70c2f8..bb3a8b98 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -149,19 +149,18 @@ CClient::onClipboardChanged(ClipboardID, const CString&) // ignore -- we'll check the clipboard when we leave } -bool +void CClient::open() { // open the screen try { log((CLOG_INFO "opening screen")); openSecondaryScreen(); - return true; } catch (XScreenOpenFailure&) { - // can't open screen yet. wait a few seconds to retry. + // can't open screen log((CLOG_INFO "failed to open screen")); - return false; + throw; } } @@ -368,7 +367,6 @@ void CClient::openSecondaryScreen() { assert(m_screen == NULL); - assert(m_screenFactory != NULL); // not active m_active = false; @@ -381,7 +379,9 @@ CClient::openSecondaryScreen() // create screen log((CLOG_DEBUG1 "creating secondary screen")); - m_screen = m_screenFactory->create(this); + if (m_screenFactory != NULL) { + m_screen = m_screenFactory->create(this); + } if (m_screen == NULL) { throw XScreenOpenFailure(); } @@ -402,12 +402,12 @@ CClient::openSecondaryScreen() void CClient::closeSecondaryScreen() { - assert(m_screen != NULL); - // close the secondary screen try { - log((CLOG_DEBUG1 "closing secondary screen")); - m_screen->close(); + if (m_screen != NULL) { + log((CLOG_DEBUG1 "closing secondary screen")); + m_screen->close(); + } } catch (...) { // ignore diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 3cfb4543..61f0fe2b 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -98,7 +98,7 @@ public: virtual void onClipboardChanged(ClipboardID, const CString&); // IClient overrides - virtual bool open(); + virtual void open(); virtual void mainLoop(); virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index 4c921927..7c1f55a3 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -229,7 +229,7 @@ CXWindowsSecondaryScreen::createWindow() int majorOpcode, firstEvent, firstError; if (!XQueryExtension(display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) { - // FIXME -- subclass exception for more info? + log((CLOG_ERR "XTEST extension not available")); throw XScreenOpenFailure(); } diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index e5e2fd8f..0b908981 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -197,7 +197,7 @@ CXWindowsScreen::open() log((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); m_display = XOpenDisplay(display); if (m_display == NULL) { - throw XScreenOpenFailure(); + throw XScreenUnavailable(60.0); } // get root window diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index 83cb3e14..46c47953 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -43,7 +43,7 @@ public: //@} // IClient overrides - virtual bool open() = 0; + virtual void open() = 0; virtual void mainLoop() = 0; virtual void close() = 0; virtual void enter(SInt32 xAbs, SInt32 yAbs, diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index ad0d45f1..eb76b3cc 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -29,7 +29,7 @@ CClientProxy1_0::~CClientProxy1_0() // do nothing } -bool +void CClientProxy1_0::open() { // send request @@ -55,8 +55,6 @@ CClientProxy1_0::open() // handle reply recvInfo(false); - - return true; } void diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index befb480c..01522865 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -14,7 +14,7 @@ public: ~CClientProxy1_0(); // IClient overrides - virtual bool open(); + virtual void open(); virtual void mainLoop(); virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index 3b257840..4b909412 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -399,6 +399,7 @@ CMSWindowsPrimaryScreen::onPreOpen() // initialize hook library m_threadID = GetCurrentThreadId(); if (m_init(m_threadID) == 0) { + log((CLOG_ERR "cannot initialize hook library")); throw XScreenOpenFailure(); } } diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 44f01109..638c37c4 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -1,6 +1,7 @@ #include "CPrimaryClient.h" #include "IPrimaryScreenFactory.h" #include "IServer.h" +#include "XScreen.h" #include "XSynergy.h" #include "CPrimaryScreen.h" #include "CClipboard.h" @@ -19,11 +20,15 @@ CPrimaryClient::CPrimaryClient(IPrimaryScreenFactory* screenFactory, m_seqNum(0) { assert(m_server != NULL); - assert(screenFactory != NULL); // create screen log((CLOG_DEBUG1 "creating primary screen")); - m_screen = screenFactory->create(this, receiver); + if (screenFactory != NULL) { + m_screen = screenFactory->create(this, receiver); + } + if (m_screen == NULL) { + throw XScreenOpenFailure(); + } } CPrimaryClient::~CPrimaryClient() @@ -100,7 +105,7 @@ CPrimaryClient::onClipboardChanged(ClipboardID id, const CString& data) m_server->onClipboardChanged(id, m_seqNum, data); } -bool +void CPrimaryClient::open() { // all clipboards are clean @@ -110,8 +115,6 @@ CPrimaryClient::open() // now open the screen m_screen->open(); - - return true; } void diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 84ea0bad..0c98e956 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -20,8 +20,9 @@ treated as if it was on a client. class CPrimaryClient : public IScreenReceiver, public IClient { public: /*! - \c name is the name of the server. the caller retains ownership of - \c factory. + \c name is the name of the server. The caller retains ownership of + \c factory. Throws XScreenOpenFailure or whatever the factory can + throw if the screen cannot be created. */ CPrimaryClient(IPrimaryScreenFactory* factory, IServer*, IPrimaryScreenReceiver*, const CString& name); @@ -75,7 +76,7 @@ public: virtual void onClipboardChanged(ClipboardID, const CString&); // IClient overrides - virtual bool open(); + virtual void open(); virtual void mainLoop(); virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 822bac97..0b0bc327 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -53,24 +53,23 @@ CServer::~CServer() delete m_streamFilterFactory; } -bool +void CServer::open() { // open the screen try { log((CLOG_INFO "opening screen")); openPrimaryScreen(); - return true; } - catch (XScreenOpenFailure&) { - // can't open screen yet. wait a few seconds to retry. + catch (XScreen&) { + // can't open screen log((CLOG_INFO "failed to open screen")); - return false; + throw; } catch (XUnknownClient& e) { - // can't open screen yet. wait a few seconds to retry. + // can't open screen log((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); - return false; + throw; } } @@ -1529,7 +1528,6 @@ void CServer::openPrimaryScreen() { assert(m_primaryClient == NULL); - assert(m_screenFactory != NULL); // reset sequence number m_seqNum = 0; diff --git a/lib/server/CServer.h b/lib/server/CServer.h index a119fa6a..494da9ae 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -39,9 +39,12 @@ public: //! Open server /*! - Open the server and return true iff successful. + Open the server. Throws XScreenUnavailable if the server's + screen cannot be opened but might be available after some time. + Otherwise throws some other exception if the server's screen or + the server cannot be opened and retrying won't help. */ - bool open(); + void open(); //! Server main loop /*! diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index 01027abb..12c534d1 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -19,9 +19,11 @@ public: //! Open client /*! - Open the client and return true iff successful. + Open the client. Throw if the client cannot be opened. If the + screen cannot be opened but retrying later may succeed then throw + XScreenUnavailable. */ - virtual bool open() = 0; + virtual void open() = 0; //! Client main loop /*! diff --git a/lib/synergy/IScreen.h b/lib/synergy/IScreen.h index b60f56d6..48417984 100644 --- a/lib/synergy/IScreen.h +++ b/lib/synergy/IScreen.h @@ -19,7 +19,9 @@ public: //! Open screen /*! - Called to open and initialize the screen. + Called to open and initialize the screen. Throw XScreenUnavailable + if the screen cannot be opened but retrying later may succeed. + Otherwise throw some other XScreenOpenFailure exception. */ virtual void open() = 0; diff --git a/lib/synergy/XScreen.cpp b/lib/synergy/XScreen.cpp index b011b131..63684f5d 100644 --- a/lib/synergy/XScreen.cpp +++ b/lib/synergy/XScreen.cpp @@ -9,3 +9,31 @@ XScreenOpenFailure::getWhat() const throw() { return format("XScreenOpenFailure", "unable to open screen"); } + + +// +// XScreenUnavailable +// + +XScreenUnavailable::XScreenUnavailable(double timeUntilRetry) : + m_timeUntilRetry(timeUntilRetry) +{ + // do nothing +} + +XScreenUnavailable::~XScreenUnavailable() +{ + // do nothing +} + +double +XScreenUnavailable::getRetryTime() const +{ + return m_timeUntilRetry; +} + +CString +XScreenUnavailable::getWhat() const throw() +{ + return format("XScreenUnavailable", "unable to open screen"); +} diff --git a/lib/synergy/XScreen.h b/lib/synergy/XScreen.h index 2b71402a..b69fd61a 100644 --- a/lib/synergy/XScreen.h +++ b/lib/synergy/XScreen.h @@ -15,4 +15,36 @@ protected: virtual CString getWhat() const throw(); }; +//! Screen unavailable exception +/*! +Thrown when a screen cannot be opened or initialized but retrying later +may be successful. +*/ +class XScreenUnavailable : public XScreenOpenFailure { +public: + /*! + \c timeUntilRetry is the suggested time the caller should wait until + trying to open the screen again. + */ + XScreenUnavailable(double timeUntilRetry); + virtual ~XScreenUnavailable(); + + //! @name manipulators + //@{ + + //! Get retry time + /*! + Returns the suggested time to wait until retrying to open the screen. + */ + double getRetryTime() const; + + //@} + +protected: + virtual CString getWhat() const throw(); + +private: + double m_timeUntilRetry; +}; + #endif From c6d98af056e89238bc089d8177ad149f37c30fda Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 12:40:41 +0000 Subject: [PATCH 295/807] now building hook dll for release without linking in standard C runtime. need C runtime for debug build for asserts. --- lib/platform/CSynergyHook.cpp | 8 ++++++++ lib/platform/synrgyhk.dsp | 10 +++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index dc711685..5163aff4 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -58,6 +58,14 @@ static DWORD g_cursorThread = 0; #pragma data_seg() +// keep linker quiet about floating point stuff. we don't use any +// floating point operations but our includes may define some +// (unused) floating point values. +#ifndef _DEBUG +extern "C" int _fltused=0; +#endif + + // // internal functions // diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp index df4809e7..4f1f593c 100644 --- a/lib/platform/synrgyhk.dsp +++ b/lib/platform/synrgyhk.dsp @@ -40,9 +40,10 @@ RSC=rc.exe # PROP Use_Debug_Libraries 0 # PROP Output_Dir "../../Release" # PROP Intermediate_Dir "ReleaseHook" +# PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -53,7 +54,8 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"DllMain" /dll /machine:I386 /nodefaultlib +# SUBTRACT LINK32 /verbose !ELSEIF "$(CFG)" == "synrgyhk - Win32 Debug" @@ -66,6 +68,7 @@ LINK32=link.exe # PROP Use_Debug_Libraries 1 # PROP Output_Dir "../../Debug" # PROP Intermediate_Dir "DebugHook" +# PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\synergy" /I "..\base" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c @@ -79,7 +82,8 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"" /dll /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /nodefaultlib !ENDIF From bc72cac780248216e0f8ada6c2b4b944dc472fb7 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 13:10:15 +0000 Subject: [PATCH 296/807] updated README. --- README | 95 ++++++++++++++++------------------------------------------ 1 file changed, 26 insertions(+), 69 deletions(-) diff --git a/README b/README index def4b265..e003a892 100644 --- a/README +++ b/README @@ -40,8 +40,6 @@ manifest synergyd synergyd.exe the synergy server synrgyhk.dll the synergy hook dll synergy.conf synergy.conf sample configuration file - synergy.linux.init startup script for client - synergyd.linux.init startup script for server running synergy @@ -160,7 +158,10 @@ Microsoft Windows only supports the clipboard. the Windows clipboard is transferred to both the X primary selection and the clipboard. whichever X clipboard was changed last becomes the Windows clipboard. end-of-line sequences (LF on linux and unix, -CRLF on Windows) are automatically converted as necessary. +CRLF on Windows) are automatically converted as necessary. the +clipboards are transferred using Unicode; if your platforms and +applications understand Unicode then you should be able to cut +and paste any Unicode character. synergy synchronizes screensavers. the screensavers on client screens are disabled when they connect to the server. when the @@ -228,31 +229,16 @@ linux, unix: can start before the X server does and will repeatly attempt to connect to the X server until it succeeds. however, if the server requires authorization then it's unlikely that synergy - will ever succeed. in that case synergy should be (re)started + will ever succeed. so, in general, synergy should be (re)started by the X display manager. - the DISPLAY env var should be set appropriately before starting - synergy. note that it probably will not be set when running - boot scripts so you have to set it yourself (probably to ":0"). - if it's not set then synergy will use the default ":0.0" which - is correct in most cases. + some display managers (xdm and kdm, but not gdm) grab the + keyboard and do not release it until the user logs in, also + for security reasons. this prevents a synergy server from + sharing the mouse and keyboard until the user logs in but + it doesn't prevent a synergy client from synthesizing mouse + and keyboard input. - finally, some display managers (xdm and kdm, but not gdm) - grab the keyboard and do not release it until the user logs - in, also for security reasons. this prevents a synergy server - from sharing the mouse and keyboard until the user logs in - but it doesn't prevent a synergy client from synthesizing - mouse and keyboard input. - - to install synergy as a daemon, you'll need to add the - appropriate lines and/or files to start synergy at boot time - or modify the display manager screen initialization scripts. - do not use the `-f' or `--no-daemon' options. for the server - use the `--config' option to specify the path to the - configuration file or just put the configuration in - /etc/synergy.conf. - -linux: you should modify xdm's Xsetup script to start the synergy client or server. for example, somewhere near the bottom of Xsetup (but someplace before anywhere the script calls exit) @@ -277,45 +263,6 @@ linux: start synergy when they login. in this case synergy will not be running while on the login screen. - if your X server does not require authorization then you can - start synergy at boot time. if starting the synergy client - using init.d then: - # /bin/cp synergy /usr/sbin/synergy - # /bin/cp synergy.linux.init /etc/init.d/synergy - # /sbin/chkconfig --add synergy - - if starting the synergy server using init.d then: - # /bin/cp synergyd /usr/sbin/synergyd - # /bin/cp synergyd.linux.init /etc/init.d/synergyd - # /bin/cp synergy.conf /etc/synergy.conf - # /sbin/chkconfig --add synergyd - of course, /etc/synergy.conf should be edited your for systems. - - to manually start or stop the client - # /etc/init.d/synergy start - # /etc/init.d/synergy stop - - to manually start or stop the server - # /etc/init.d/synergyd start - # /etc/init.d/synergyd stop - - to uninstall the client: - # /etc/init.d/synergy stop - # /sbin/chkconfig --del synergy - # /bin/rm /etc/init.d/synergy - # /bin/rm /usr/sbin/synergy - - to uninstall the server: - # /etc/init.d/synergyd stop - # /sbin/chkconfig --del synergyd - # /bin/rm /etc/synergy.conf - # /bin/rm /etc/init.d/synergyd - # /bin/rm /usr/sbin/synergyd - - note that synergy.linux.init and synergyd.linux.init are not - tested on a wide variety of platforms and may need editing - for your platform. - common command line options --------------------------- @@ -323,8 +270,8 @@ common command line options --daemon run as a daemon (linux,unix) or background (windows) -f, --no-daemon run in the foreground -n, --name use instead of the hostname - --restart automatically restart on unexpected failures - -1, --no-restart do not restart on unexpected failure + --restart automatically restart on failures + -1, --no-restart do not restart on failure -h, --help print help and exit --version print version information and exit --install install as a service (windows) @@ -343,6 +290,13 @@ the `--name' option lets the client or server use a name other than its hostname for its screen. this name is used when checking the configuration. +neither the client nor server will automatically restart if an error +occurs that is sure to happen every time. for example, the server +will exit immediately if it can't find itself in the screen +configuration. on X11 both the client and server will also terminate +if the connection to the X server is lost. since xdm will normally +restart the server and then synergy this is the correct behavior. + server command line options --------------------------- @@ -424,8 +378,7 @@ best server. known bugs ---------- all: - * non-ASCII characters are not supported - * plain text is the only supported clipboard format + * non-ASCII keyboard characters are not supported windows: * screen flashes when entering the screen @@ -466,4 +419,8 @@ tips and tricks will interfere with synergy. use keyboard shortcuts instead. * synergy's screensaver synchronization works best with xscreensaver - under X windows. + under X windows. synergy works better with xscreensaver if it is + using of the screensaver extensions. prior to xscreensaver 4.0 + you can use `-mit-extension', `-sgi-extension', or `-xidle-extension' + command line options to enable an extension. starting with 4.0 + you must enable the corresponding option in your .xscreensaver file. From 1a830c227b0f018b8942a2ba4cb42702e169c2ad Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 13:18:27 +0000 Subject: [PATCH 297/807] added comment about large motif clipboard items to README. --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index e003a892..847c914f 100644 --- a/README +++ b/README @@ -401,6 +401,7 @@ linux: toggle off on the key release after the next key press. synergy doesn't handle these properly. * shift-lock (as opposed to caps-lock) is not supported + * large (~256k) motif clipboard items are not copied properly tips and tricks From 32c98816f5dd542983bcacf1b43904f6caa6a50e Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 13:29:33 +0000 Subject: [PATCH 298/807] checkpoint notes. --- notes | 90 +++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/notes b/notes index b18e1af2..5b0ca199 100644 --- a/notes +++ b/notes @@ -1,9 +1,60 @@ +final preparation for release: + create files: + INSTALL -- configuration, build, and installation instructions + NEWS -- recent project news + COPYING -- pointer to LICENSE + AUTHORS -- list of contributors + ChangeLog + LICENSE -- license + MANIFEST -- list of files in distribution (?) + FAQ -- frequently asked questions for project + HISTORY -- project history + +add copyright to every file + +change README: + should have: + A brief description of the project. + A pointer to the project website (if it has one) + Notes on the developer's build environment and potential portability problems. + A roadmap describing important files and subdirectories. + Either build/installation instructions or a pointer to a file containing same (usually INSTALL). + Either a maintainers/credits list or a pointer to a file containing same (usually CREDITS). + Either recent project news or a pointer to a file containing same (usually NEWS). + +make man pages + docbook? + should have makefile target to build these + +add make rules to install files + +custom make targets: + make doxygen documents + make binary and/or source rpm + should auto generate spec file, pulling in proper version number + source rpm should use BuildRoot to build in /tmp or /var/tmp + create tarball: + foobar-$(VERS).tar.gz: + @ls $(SRC) | sed s:^:foobar-$(VERS)/: >MANIFEST + @(cd ..; ln -s foobar foobar-$(VERS)) + (cd ..; tar -czvf foobar/foobar-$(VERS).tar.gz `cat foobar/MANIFEST`) + @(cd ..; rm foobar-$(VERS)) + +configure not caching results + is that normal or did we disable it unintentionally + +--- not handling large motif selections don't know protocol for transfer --- not handling international characters +--- +CServer::setConfig() should update network ports + if the server address or http address change then the old ports + should be closed and new ones opened. + --- try to determine X11 keyboard quirks automatically in particular, set the half-duplex flags @@ -15,15 +66,6 @@ bug with half-duplex keys now grace starts server and is locked to screen grace should ignore half duplex keys when seeing if locked to screen ---- -enable heartbeat disconnection or remove heartbeat sending in client - should either ship with it fully enabled or fully disabled - possibly make it part of startup negotiation - ---- -use automake -use doxygen - --- HTTP stuff no way to save config using HTTP @@ -34,6 +76,13 @@ HTTP stuff provide way to query why locked to screen? handy for debugging at least + bug in updating screens + saw a goofy set of screens after update + i think the config was similar to: + A B + C + D E + --- win32: need icon @@ -49,20 +98,10 @@ win32 dll: win32 service: need message table for proper event reporting -clean up c:\winnt\synergy.sgc - desktop switcher program failure when running synergy as service returns access denied from CreateDesktop() don't know why ---- -bug in updating screens - saw a goofy set of screens after update - i think the config was similar to: - A B - C - D E - --- hot keys should have keyboard shortcuts to jump to screens @@ -137,11 +176,8 @@ adjust thread priorities on win32 sometimes it goes normal speed but it's mostly very slow even a very high priority doesn't seem to help haven't tried real-time priority - a very high priority on client fixes delay when typeing into console + a very high priority on client fixes delay when typing into console is it the output console causing the slowness? - mouse motion compression seems to handle slow dragging on grace - pretty sure nothing else was wrong on grace - maybe same problem and solution on win32 --- results: @@ -317,12 +353,12 @@ non-functional on ctrl+alt+del screen in win2k (kurt) --- automake stuff rebuild: - * 'autoheader' - creates config.h.in * 'touch NEWS README AUTHORS ChangeLog' * 'touch stamp-h' - * aclocal - adds aclocal.m4 to directory. Defines some m4 macros used by the auto tools. - * 'autoconf '- creates configure from configure.in - * 'automake' - Creates Makefile.in from Makefile.am + * 'aclocal' - creates aclocal.m4 + * 'autoconf' - creates configure from configure.in + * 'autoheader' - creates config.h.in + * 'automake' - Creates Makefile.in from Makefile.am * './configure' - creates Makefile from Makefile.in * 'make' From 58ff62843854be0bb75bdf8f3fa452dff6c3d7a7 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 13:34:18 +0000 Subject: [PATCH 299/807] removed unintentional executable flag. --- notes | 6 ------ 1 file changed, 6 deletions(-) diff --git a/notes b/notes index 5b0ca199..ff76a503 100644 --- a/notes +++ b/notes @@ -89,12 +89,6 @@ win32: provide taskbar icon provide control panel? -win32 dll: - should make this as small as possible - don't use standard libraries - use custom _DllMainCRTStartup(); just call DllMain() from it. - use /MERGE linker switch to merge sections - win32 service: need message table for proper event reporting From cfe5b99f9d98876c71a3411fdf914a51291b2c5f Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 13:41:58 +0000 Subject: [PATCH 300/807] okay, now the files should no longer be executable. --- lib/http/http.dsp | 0 lib/platform/CMSWindowsClipboardAnyTextConverter.cpp | 0 lib/platform/CMSWindowsClipboardAnyTextConverter.h | 0 lib/platform/CMSWindowsClipboardTextConverter.cpp | 0 lib/platform/CMSWindowsClipboardTextConverter.h | 0 lib/platform/CMSWindowsClipboardUTF16Converter.cpp | 0 lib/platform/CMSWindowsClipboardUTF16Converter.h | 0 lib/platform/CMSWindowsScreenSaver.cpp | 0 lib/platform/CMSWindowsScreenSaver.h | 0 lib/platform/IMSWindowsScreenEventHandler.h | 0 10 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 lib/http/http.dsp mode change 100755 => 100644 lib/platform/CMSWindowsClipboardAnyTextConverter.cpp mode change 100755 => 100644 lib/platform/CMSWindowsClipboardAnyTextConverter.h mode change 100755 => 100644 lib/platform/CMSWindowsClipboardTextConverter.cpp mode change 100755 => 100644 lib/platform/CMSWindowsClipboardTextConverter.h mode change 100755 => 100644 lib/platform/CMSWindowsClipboardUTF16Converter.cpp mode change 100755 => 100644 lib/platform/CMSWindowsClipboardUTF16Converter.h mode change 100755 => 100644 lib/platform/CMSWindowsScreenSaver.cpp mode change 100755 => 100644 lib/platform/CMSWindowsScreenSaver.h mode change 100755 => 100644 lib/platform/IMSWindowsScreenEventHandler.h diff --git a/lib/http/http.dsp b/lib/http/http.dsp old mode 100755 new mode 100644 diff --git a/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp b/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp old mode 100755 new mode 100644 diff --git a/lib/platform/CMSWindowsClipboardAnyTextConverter.h b/lib/platform/CMSWindowsClipboardAnyTextConverter.h old mode 100755 new mode 100644 diff --git a/lib/platform/CMSWindowsClipboardTextConverter.cpp b/lib/platform/CMSWindowsClipboardTextConverter.cpp old mode 100755 new mode 100644 diff --git a/lib/platform/CMSWindowsClipboardTextConverter.h b/lib/platform/CMSWindowsClipboardTextConverter.h old mode 100755 new mode 100644 diff --git a/lib/platform/CMSWindowsClipboardUTF16Converter.cpp b/lib/platform/CMSWindowsClipboardUTF16Converter.cpp old mode 100755 new mode 100644 diff --git a/lib/platform/CMSWindowsClipboardUTF16Converter.h b/lib/platform/CMSWindowsClipboardUTF16Converter.h old mode 100755 new mode 100644 diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp old mode 100755 new mode 100644 diff --git a/lib/platform/CMSWindowsScreenSaver.h b/lib/platform/CMSWindowsScreenSaver.h old mode 100755 new mode 100644 diff --git a/lib/platform/IMSWindowsScreenEventHandler.h b/lib/platform/IMSWindowsScreenEventHandler.h old mode 100755 new mode 100644 From 12f1cb58b9c03145cb22f0d3d0318c2da5928bde Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 13:56:59 +0000 Subject: [PATCH 301/807] removed now unnecssary #define. --- lib/base/CLog.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index b104e5cc..bbc55a8c 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -7,7 +7,6 @@ #if WINDOWS_LIKE #define WIN32_LEAN_AND_MEAN #include -#define vsnprintf _vsnprintf #endif // names of priorities From 8c1f5f623b942af0fd8e45cc987b6ec709381372 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 16:27:06 +0000 Subject: [PATCH 302/807] added EXTRA_* files to get `make dist' doing the right thing. --- Makefile.am | 14 +++++-- cmd/synergy/Makefile.am | 6 +++ cmd/synergyd/Makefile.am | 6 +++ examples/synergy.linux.init | 75 ------------------------------------ examples/synergyd.linux.init | 75 ------------------------------------ lib/base/Makefile.am | 4 ++ lib/client/Makefile.am | 6 +++ lib/http/Makefile.am | 4 ++ lib/io/Makefile.am | 4 ++ lib/mt/Makefile.am | 4 ++ lib/net/Makefile.am | 4 ++ lib/platform/Makefile.am | 27 ++++++++++++- lib/server/Makefile.am | 6 +++ lib/synergy/Makefile.am | 66 ++++++++++++++++--------------- notes => nodist/notes | 0 15 files changed, 115 insertions(+), 186 deletions(-) delete mode 100755 examples/synergy.linux.init delete mode 100755 examples/synergyd.linux.init rename notes => nodist/notes (100%) diff --git a/Makefile.am b/Makefile.am index ab3bf2b0..674b2d5a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,8 +2,14 @@ NULL = DEPTH = . -EXTRA_DIST = -SUBDIRS = \ - lib \ - cmd \ +EXTRA_DIST = \ + all.dsp \ + synergy.dsw \ + doc/doxygen.cfg \ + examples/synergy.conf \ + $(NULL) + +SUBDIRS = \ + lib \ + cmd \ $(NULL) diff --git a/cmd/synergy/Makefile.am b/cmd/synergy/Makefile.am index 3b48a5a5..5eff699d 100644 --- a/cmd/synergy/Makefile.am +++ b/cmd/synergy/Makefile.am @@ -2,6 +2,12 @@ NULL = DEPTH = ../.. +EXTRA_DIST = \ + synergy.dsp \ + synergy.rc \ + resource.h \ + $(NULL) + bin_PROGRAMS = synergy synergy_SOURCES = \ synergy.cpp \ diff --git a/cmd/synergyd/Makefile.am b/cmd/synergyd/Makefile.am index e0a424b2..2593eb51 100644 --- a/cmd/synergyd/Makefile.am +++ b/cmd/synergyd/Makefile.am @@ -2,6 +2,12 @@ NULL = DEPTH = ../.. +EXTRA_DIST = \ + synergyd.dsp \ + synergyd.rc \ + resource.h \ + $(NULL) + bin_PROGRAMS = synergyd synergyd_SOURCES = \ synergyd.cpp \ diff --git a/examples/synergy.linux.init b/examples/synergy.linux.init deleted file mode 100755 index 9e143d8b..00000000 --- a/examples/synergy.linux.init +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh -# -# Startup script for synergy client -# -# chkconfig: 5 98 02 -# description: Starts/stops synergy client -# -# processname: synergy -# -### BEGIN INIT INFO -# Provides: synergy -# Required-Start: $network $syslog -# Required-Stop: $network $syslog -# Default-Start: 5 -# Default-Stop: 0 1 2 3 6 -# Description: Start synergy client daemon. -### END INIT INFO - -SYNERGY_BIN=/usr/sbin/synergy -test -x $SYNERGY_BIN || exit 5 - -# startup command line arguments -ARGS=192.168.1.3 - -# load function library -if test -f /etc/rc.status; then -. /etc/rc.status -rc_reset -else -. /etc/rc.d/init.d/functions -function startproc () -{ - daemon $* -} -function checkproc () -{ - status $* -} -function rc_status () -{ - RETVAL=$? -} -function rc_exit () -{ - exit $RETVAL -} -fi - -case "$1" in - start) - echo -n "Starting synergy client: " - startproc $SYNERGY_BIN $ARGS - rc_status -v - ;; - stop) - echo -n "Stopping synergy client: " - killproc -TERM $SYNERGY_BIN - rc_status -v - ;; - restart) - $0 stop - $0 start - rc_status - ;; - status) - echo -n "Checking for synergy client: " - checkproc $SYNERGY_BIN - rc_status -v - ;; - *) - echo "Usage: synergy {start|stop|status|restart}" - exit 1 -esac - -rc_exit diff --git a/examples/synergyd.linux.init b/examples/synergyd.linux.init deleted file mode 100755 index 426bcaa1..00000000 --- a/examples/synergyd.linux.init +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh -# -# Startup script for synergy server -# -# chkconfig: 5 98 02 -# description: Starts/stops synergy server -# -# processname: synergyd -# -### BEGIN INIT INFO -# Provides: synergyd -# Required-Start: $network $syslog -# Required-Stop: $network $syslog -# Default-Start: 5 -# Default-Stop: 0 1 2 3 6 -# Description: Start synergy server daemon. -### END INIT INFO - -SYNERGYD_BIN=/usr/sbin/synergyd -test -x $SYNERGYD_BIN || exit 5 - -# startup command line arguments -ARGS= - -# load function library -if test -f /etc/rc.status; then -. /etc/rc.status -rc_reset -else -. /etc/rc.d/init.d/functions -function startproc () -{ - daemon $* -} -function checkproc () -{ - status $* -} -function rc_status () -{ - RETVAL=$? -} -function rc_exit () -{ - exit $RETVAL -} -fi - -case "$1" in - start) - echo -n "Starting synergy server: " - startproc $SYNERGYD_BIN $ARGS - rc_status -v - ;; - stop) - echo -n "Stopping synergy server: " - killproc -TERM $SYNERGYD_BIN - rc_status -v - ;; - restart) - $0 stop - $0 start - rc_status - ;; - status) - echo -n "Checking for synergy server: " - checkproc $SYNERGYD_BIN - rc_status -v - ;; - *) - echo "Usage: synergyd {start|stop|status|restart}" - exit 1 -esac - -rc_exit diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index 82f537e8..f52d5bf6 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -2,6 +2,10 @@ NULL = DEPTH = ../.. +EXTRA_DIST = \ + base.dsp \ + $(NULL) + noinst_LIBRARIES = libbase.a libbase_a_SOURCES = \ CFunctionJob.cpp \ diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index c489709c..1801c61b 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -2,6 +2,12 @@ NULL = DEPTH = ../.. +EXTRA_DIST = \ + client.dsp \ + CMSWindowsSecondaryScreen.cpp \ + CMSWindowsSecondaryScreen.h \ + $(NULL) + noinst_LIBRARIES = libclient.a libclient_a_SOURCES = \ CClient.cpp \ diff --git a/lib/http/Makefile.am b/lib/http/Makefile.am index cee66bf8..07f4ad44 100644 --- a/lib/http/Makefile.am +++ b/lib/http/Makefile.am @@ -2,6 +2,10 @@ NULL = DEPTH = ../.. +EXTRA_DIST = \ + http.dsp \ + $(NULL) + noinst_LIBRARIES = libhttp.a libhttp_a_SOURCES = \ CHTTPProtocol.cpp \ diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index 6e134dfa..e244a402 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -2,6 +2,10 @@ NULL = DEPTH = ../.. +EXTRA_DIST = \ + io.dsp \ + $(NULL) + noinst_LIBRARIES = libio.a libio_a_SOURCES = \ CBufferedInputStream.cpp \ diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index a3c4b165..88e2c06c 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -2,6 +2,10 @@ NULL = DEPTH = ../.. +EXTRA_DIST = \ + mt.dsp \ + $(NULL) + noinst_LIBRARIES = libmt.a libmt_a_SOURCES = \ CLock.cpp \ diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index cc996705..dc9f1417 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -2,6 +2,10 @@ NULL = DEPTH = ../.. +EXTRA_DIST = \ + net.dsp \ + $(NULL) + noinst_LIBRARIES = libnet.a libnet_a_SOURCES = \ CNetwork.cpp \ diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 516961c1..9cf62784 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -2,7 +2,29 @@ NULL = DEPTH = ../.. -# FIXME -- add CUnixPlatform.cpp as an unbuilt source +EXTRA_DIST = \ + makehook.dsp \ + platform.dsp \ + synrgyhk.dsp \ + CMSWindowsClipboard.cpp \ + CMSWindowsClipboardAnyTextConverter.cpp \ + CMSWindowsClipboardTextConverter.cpp \ + CMSWindowsClipboardUTF16Converter.cpp \ + CMSWindowsScreen.cpp \ + CMSWindowsScreenSaver.cpp \ + CSynergyHook.cpp \ + CWin32Platform.cpp \ + CMSWindowsClipboard.h \ + CMSWindowsClipboardAnyTextConverter.h \ + CMSWindowsClipboardTextConverter.h \ + CMSWindowsClipboardUTF16Converter.h \ + CMSWindowsScreen.h \ + CMSWindowsScreenSaver.h \ + CSynergyHook.h \ + CWin32Platform.h \ + IMSWindowsScreenEventHandler.h \ + $(NULL) + noinst_LIBRARIES = libplatform.a libplatform_a_SOURCES = \ CPlatform.cpp \ @@ -24,6 +46,9 @@ libplatform_a_SOURCES = \ CXWindowsUtil.h \ IPlatform.h \ $(NULL) +EXTRA_libplatform_a_SOURCES = \ + CUnixPlatform.cpp \ + $(NULL) INCLUDES = \ -I$(DEPTH)/lib/base \ -I$(DEPTH)/lib/mt \ diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index db86137b..d2f666f5 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -2,6 +2,12 @@ NULL = DEPTH = ../.. +EXTRA_DIST = \ + server.dsp \ + CMSWindowsPrimaryScreen.cpp \ + CMSWindowsPrimaryScreen.h \ + $(NULL) + noinst_LIBRARIES = libserver.a libserver_a_SOURCES = \ CClientProxy.cpp \ diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index de13e200..e52e7cfc 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -2,37 +2,41 @@ NULL = DEPTH = ../.. +EXTRA_DIST = \ + libsynergy.dsp \ + $(NULL) + noinst_LIBRARIES = libsynergy.a -libsynergy_a_SOURCES = \ - CInputPacketStream.cpp \ - COutputPacketStream.cpp \ - CProtocolUtil.cpp \ - CClipboard.cpp \ - XScreen.cpp \ - XSynergy.cpp \ - CClipboard.h \ - CInputPacketStream.h \ - COutputPacketStream.h \ - CProtocolUtil.h \ - ClipboardTypes.h \ - IClient.h \ - IClipboard.h \ - IPrimaryScreenReceiver.h\ - IScreen.h \ - IScreenEventHandler.h \ - IScreenReceiver.h \ - IScreenSaver.h \ - IServer.h \ - KeyTypes.h \ - MouseTypes.h \ - ProtocolTypes.h \ - Version.h \ - XScreen.h \ - XSynergy.h \ +libsynergy_a_SOURCES = \ + CInputPacketStream.cpp \ + COutputPacketStream.cpp \ + CProtocolUtil.cpp \ + CClipboard.cpp \ + XScreen.cpp \ + XSynergy.cpp \ + CClipboard.h \ + CInputPacketStream.h \ + COutputPacketStream.h \ + CProtocolUtil.h \ + ClipboardTypes.h \ + IClient.h \ + IClipboard.h \ + IPrimaryScreenReceiver.h \ + IScreen.h \ + IScreenEventHandler.h \ + IScreenReceiver.h \ + IScreenSaver.h \ + IServer.h \ + KeyTypes.h \ + MouseTypes.h \ + ProtocolTypes.h \ + Version.h \ + XScreen.h \ + XSynergy.h \ $(NULL) -INCLUDES = \ - -I$(DEPTH)/lib/base \ - -I$(DEPTH)/lib/mt \ - -I$(DEPTH)/lib/io \ - -I$(DEPTH)/lib/net \ +INCLUDES = \ + -I$(DEPTH)/lib/base \ + -I$(DEPTH)/lib/mt \ + -I$(DEPTH)/lib/io \ + -I$(DEPTH)/lib/net \ $(NULL) diff --git a/notes b/nodist/notes similarity index 100% rename from notes rename to nodist/notes From fae797e220f5a020e731e8563d3831e9d43b423c Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 16:57:26 +0000 Subject: [PATCH 303/807] Moved version header to base and it now uses VERSION macro from config.h if available (which means version is now a string, not three integers). Changed version to 1.0.0 and protocol version to 1.0. And added MAINTAINERCLEANFILES to makefiles to remove generated files. --- Makefile.am | 30 ++++++++++++++++++++++-------- cmd/Makefile.am | 8 +++++++- cmd/synergy/Makefile.am | 12 ++++++++---- cmd/synergy/synergy.cpp | 6 ++---- cmd/synergyd/Makefile.am | 12 ++++++++---- cmd/synergyd/synergyd.cpp | 6 ++---- configure.in | 2 +- lib/Makefile.am | 8 +++++++- lib/base/Makefile.am | 5 +++++ lib/{synergy => base}/Version.h | 11 +++++++---- lib/client/Makefile.am | 4 ++++ lib/http/Makefile.am | 4 ++++ lib/io/Makefile.am | 8 ++++++-- lib/mt/Makefile.am | 4 ++++ lib/net/Makefile.am | 4 ++++ lib/platform/Makefile.am | 4 ++++ lib/server/Makefile.am | 4 ++++ lib/synergy/Makefile.am | 5 ++++- lib/synergy/ProtocolTypes.h | 4 ++-- 19 files changed, 105 insertions(+), 36 deletions(-) rename lib/{synergy => base}/Version.h (82%) diff --git a/Makefile.am b/Makefile.am index 674b2d5a..09e0b5c5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,14 +2,28 @@ NULL = DEPTH = . -EXTRA_DIST = \ - all.dsp \ - synergy.dsw \ - doc/doxygen.cfg \ - examples/synergy.conf \ +SUBDIRS = \ + lib \ + cmd \ $(NULL) -SUBDIRS = \ - lib \ - cmd \ +EXTRA_DIST = \ + all.dsp \ + synergy.dsw \ + doc/doxygen.cfg \ + examples/synergy.conf \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + aclocal.m4 \ + config.h \ + config.h.in \ + config.guess \ + config.log \ + config.status \ + config.sub \ + configure \ + stamp-h.in \ + stamp-h1 \ $(NULL) diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 7219f8ee..60341d6a 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -2,8 +2,14 @@ NULL = DEPTH = .. -EXTRA_DIST = SUBDIRS = \ synergy \ synergyd \ $(NULL) + +EXTRA_DIST = \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/cmd/synergy/Makefile.am b/cmd/synergy/Makefile.am index 5eff699d..0345af5a 100644 --- a/cmd/synergy/Makefile.am +++ b/cmd/synergy/Makefile.am @@ -2,10 +2,14 @@ NULL = DEPTH = ../.. -EXTRA_DIST = \ - synergy.dsp \ - synergy.rc \ - resource.h \ +EXTRA_DIST = \ + synergy.dsp \ + synergy.rc \ + resource.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ $(NULL) bin_PROGRAMS = synergy diff --git a/cmd/synergy/synergy.cpp b/cmd/synergy/synergy.cpp index 06d0481c..aeb206e3 100644 --- a/cmd/synergy/synergy.cpp +++ b/cmd/synergy/synergy.cpp @@ -218,12 +218,10 @@ void version() { log((CLOG_PRINT -"%s %d.%d.%d, protocol version %d.%d\n" +"%s %s, protocol version %d.%d\n" "%s", pname, - kMajorVersion, - kMinorVersion, - kReleaseVersion, + kVersion, kProtocolMajorVersion, kProtocolMinorVersion, kCopyright)); diff --git a/cmd/synergyd/Makefile.am b/cmd/synergyd/Makefile.am index 2593eb51..f3e75fce 100644 --- a/cmd/synergyd/Makefile.am +++ b/cmd/synergyd/Makefile.am @@ -2,10 +2,14 @@ NULL = DEPTH = ../.. -EXTRA_DIST = \ - synergyd.dsp \ - synergyd.rc \ - resource.h \ +EXTRA_DIST = \ + synergyd.dsp \ + synergyd.rc \ + resource.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ $(NULL) bin_PROGRAMS = synergyd diff --git a/cmd/synergyd/synergyd.cpp b/cmd/synergyd/synergyd.cpp index 60d070c4..4a971071 100644 --- a/cmd/synergyd/synergyd.cpp +++ b/cmd/synergyd/synergyd.cpp @@ -242,12 +242,10 @@ void version() { log((CLOG_PRINT -"%s %d.%d.%d, protocol version %d.%d\n" +"%s %s, protocol version %d.%d\n" "%s", pname, - kMajorVersion, - kMinorVersion, - kReleaseVersion, + kVersion, kProtocolMajorVersion, kProtocolMinorVersion, kCopyright)); diff --git a/configure.in b/configure.in index 69a99df6..53c87cbc 100644 --- a/configure.in +++ b/configure.in @@ -3,7 +3,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(lib/base/common.h) AM_CONFIG_HEADER(config.h) AC_CONFIG_AUX_DIR(config) -AM_INIT_AUTOMAKE(synergy, 0.5) +AM_INIT_AUTOMAKE(synergy, 1.0.0) dnl information on the package diff --git a/lib/Makefile.am b/lib/Makefile.am index 0892a6b7..c7dda5af 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -2,7 +2,6 @@ NULL = DEPTH = .. -EXTRA_DIST = SUBDIRS = \ base \ mt \ @@ -14,3 +13,10 @@ SUBDIRS = \ client \ server \ $(NULL) + +EXTRA_DIST = \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index f52d5bf6..d32a3f6a 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -6,6 +6,10 @@ EXTRA_DIST = \ base.dsp \ $(NULL) +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + noinst_LIBRARIES = libbase.a libbase_a_SOURCES = \ CFunctionJob.cpp \ @@ -23,6 +27,7 @@ libbase_a_SOURCES = \ IInterface.h \ IJob.h \ TMethodJob.h \ + Version.h \ XBase.h \ common.h \ stdfstream.h \ diff --git a/lib/synergy/Version.h b/lib/base/Version.h similarity index 82% rename from lib/synergy/Version.h rename to lib/base/Version.h index 128f500c..34ce990b 100644 --- a/lib/synergy/Version.h +++ b/lib/base/Version.h @@ -1,7 +1,12 @@ #ifndef VERSION_H #define VERSION_H -#include "BasicTypes.h" +#include "common.h" + +// set version macro if not set yet +#if !defined(VERSION) +# define VERSION "1.0.0" +#endif // important strings static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; @@ -10,9 +15,7 @@ static const char* kWebsite = ""; // build version. follows linux kernel style: an even minor number implies // a release version, odd implies development version. -static const SInt16 kMajorVersion = 0; -static const SInt16 kMinorVersion = 9; -static const SInt16 kReleaseVersion = 7; +static const char* kVersion = VERSION; // exit codes static const int kExitSuccess = 0; // successful completion diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index 1801c61b..b0516091 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -8,6 +8,10 @@ EXTRA_DIST = \ CMSWindowsSecondaryScreen.h \ $(NULL) +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + noinst_LIBRARIES = libclient.a libclient_a_SOURCES = \ CClient.cpp \ diff --git a/lib/http/Makefile.am b/lib/http/Makefile.am index 07f4ad44..26adc718 100644 --- a/lib/http/Makefile.am +++ b/lib/http/Makefile.am @@ -6,6 +6,10 @@ EXTRA_DIST = \ http.dsp \ $(NULL) +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + noinst_LIBRARIES = libhttp.a libhttp_a_SOURCES = \ CHTTPProtocol.cpp \ diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index e244a402..c64ee7a4 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -2,8 +2,12 @@ NULL = DEPTH = ../.. -EXTRA_DIST = \ - io.dsp \ +EXTRA_DIST = \ + io.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ $(NULL) noinst_LIBRARIES = libio.a diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index 88e2c06c..9c4ca47f 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -6,6 +6,10 @@ EXTRA_DIST = \ mt.dsp \ $(NULL) +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + noinst_LIBRARIES = libmt.a libmt_a_SOURCES = \ CLock.cpp \ diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index dc9f1417..561ea886 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -6,6 +6,10 @@ EXTRA_DIST = \ net.dsp \ $(NULL) +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + noinst_LIBRARIES = libnet.a libnet_a_SOURCES = \ CNetwork.cpp \ diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 9cf62784..ffca7356 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -25,6 +25,10 @@ EXTRA_DIST = \ IMSWindowsScreenEventHandler.h \ $(NULL) +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + noinst_LIBRARIES = libplatform.a libplatform_a_SOURCES = \ CPlatform.cpp \ diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index d2f666f5..774509cf 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -8,6 +8,10 @@ EXTRA_DIST = \ CMSWindowsPrimaryScreen.h \ $(NULL) +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + noinst_LIBRARIES = libserver.a libserver_a_SOURCES = \ CClientProxy.cpp \ diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index e52e7cfc..316d9b4e 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -6,6 +6,10 @@ EXTRA_DIST = \ libsynergy.dsp \ $(NULL) +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + noinst_LIBRARIES = libsynergy.a libsynergy_a_SOURCES = \ CInputPacketStream.cpp \ @@ -30,7 +34,6 @@ libsynergy_a_SOURCES = \ KeyTypes.h \ MouseTypes.h \ ProtocolTypes.h \ - Version.h \ XScreen.h \ XSynergy.h \ $(NULL) diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index d524d98b..2920d339 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -4,8 +4,8 @@ #include "BasicTypes.h" // protocol version number -static const SInt16 kProtocolMajorVersion = 0; -static const SInt16 kProtocolMinorVersion = 7; +static const SInt16 kProtocolMajorVersion = 1; +static const SInt16 kProtocolMinorVersion = 0; // default contact port number static const UInt16 kDefaultPort = 24800; From 5bb5542a130c0abed0b5138395ea875d3e02f154 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 17:34:05 +0000 Subject: [PATCH 304/807] fixes to get vpath builds working (necessary for `make distcheck'). --- Makefile.am | 1 + cmd/Makefile.am | 1 + cmd/synergy/Makefile.am | 15 ++++++++------- cmd/synergyd/Makefile.am | 17 +++++++++-------- lib/Makefile.am | 1 + lib/base/Makefile.am | 1 + lib/client/Makefile.am | 13 +++++++------ lib/http/Makefile.am | 7 ++++--- lib/io/Makefile.am | 5 +++-- lib/mt/Makefile.am | 3 ++- lib/net/Makefile.am | 7 ++++--- lib/platform/Makefile.am | 7 ++++--- lib/server/Makefile.am | 15 ++++++++------- lib/synergy/Makefile.am | 9 +++++---- 14 files changed, 58 insertions(+), 44 deletions(-) diff --git a/Makefile.am b/Makefile.am index 09e0b5c5..2b3bf549 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = . +VDEPTH = ./$(VPATH) SUBDIRS = \ lib \ diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 60341d6a..ade29001 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) SUBDIRS = \ synergy \ diff --git a/cmd/synergy/Makefile.am b/cmd/synergy/Makefile.am index 0345af5a..87df1dfc 100644 --- a/cmd/synergy/Makefile.am +++ b/cmd/synergy/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ synergy.dsp \ @@ -32,11 +33,11 @@ synergy_LDADD = \ $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ - -I$(DEPTH)/lib/base \ - -I$(DEPTH)/lib/mt \ - -I$(DEPTH)/lib/io \ - -I$(DEPTH)/lib/net \ - -I$(DEPTH)/lib/synergy \ - -I$(DEPTH)/lib/platform \ - -I$(DEPTH)/lib/client \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + -I$(VDEPTH)/lib/client \ $(NULL) diff --git a/cmd/synergyd/Makefile.am b/cmd/synergyd/Makefile.am index f3e75fce..287a620c 100644 --- a/cmd/synergyd/Makefile.am +++ b/cmd/synergyd/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ synergyd.dsp \ @@ -33,12 +34,12 @@ synergyd_LDADD = \ $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ - -I$(DEPTH)/lib/base \ - -I$(DEPTH)/lib/mt \ - -I$(DEPTH)/lib/io \ - -I$(DEPTH)/lib/http \ - -I$(DEPTH)/lib/net \ - -I$(DEPTH)/lib/synergy \ - -I$(DEPTH)/lib/platform \ - -I$(DEPTH)/lib/server \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/http \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ + -I$(VDEPTH)/lib/server \ $(NULL) diff --git a/lib/Makefile.am b/lib/Makefile.am index c7dda5af..c98329de 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) SUBDIRS = \ base \ diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index d32a3f6a..e37143fb 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ base.dsp \ diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index b0516091..70762077 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ client.dsp \ @@ -25,10 +26,10 @@ libclient_a_SOURCES = \ ISecondaryScreenFactory.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/lib/base \ - -I$(DEPTH)/lib/mt \ - -I$(DEPTH)/lib/io \ - -I$(DEPTH)/lib/net \ - -I$(DEPTH)/lib/synergy \ - -I$(DEPTH)/lib/platform \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ $(NULL) diff --git a/lib/http/Makefile.am b/lib/http/Makefile.am index 26adc718..9b1f6d49 100644 --- a/lib/http/Makefile.am +++ b/lib/http/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ http.dsp \ @@ -18,7 +19,7 @@ libhttp_a_SOURCES = \ XHTTP.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/lib/base \ - -I$(DEPTH)/lib/mt \ - -I$(DEPTH)/lib/io \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ $(NULL) diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index c64ee7a4..f4ecea1d 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ io.dsp \ @@ -29,6 +30,6 @@ libio_a_SOURCES = \ XIO.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/lib/base \ - -I$(DEPTH)/lib/mt \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ $(NULL) diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index 9c4ca47f..e4868bfa 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ mt.dsp \ @@ -27,5 +28,5 @@ libmt_a_SOURCES = \ XThread.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/lib/base \ + -I$(VDEPTH)/lib/base \ $(NULL) diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index 561ea886..280640bd 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ net.dsp \ @@ -32,7 +33,7 @@ libnet_a_SOURCES = \ XSocket.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/lib/base \ - -I$(DEPTH)/lib/mt \ - -I$(DEPTH)/lib/io \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ $(NULL) diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index ffca7356..a618eee8 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ makehook.dsp \ @@ -54,7 +55,7 @@ EXTRA_libplatform_a_SOURCES = \ CUnixPlatform.cpp \ $(NULL) INCLUDES = \ - -I$(DEPTH)/lib/base \ - -I$(DEPTH)/lib/mt \ - -I$(DEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/synergy \ $(NULL) diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index 774509cf..10979ae6 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ server.dsp \ @@ -33,11 +34,11 @@ libserver_a_SOURCES = \ IPrimaryScreenFactory.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/lib/base \ - -I$(DEPTH)/lib/mt \ - -I$(DEPTH)/lib/io \ - -I$(DEPTH)/lib/http \ - -I$(DEPTH)/lib/net \ - -I$(DEPTH)/lib/synergy \ - -I$(DEPTH)/lib/platform \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/http \ + -I$(VDEPTH)/lib/net \ + -I$(VDEPTH)/lib/synergy \ + -I$(VDEPTH)/lib/platform \ $(NULL) diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 316d9b4e..3a1b9117 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ libsynergy.dsp \ @@ -38,8 +39,8 @@ libsynergy_a_SOURCES = \ XSynergy.h \ $(NULL) INCLUDES = \ - -I$(DEPTH)/lib/base \ - -I$(DEPTH)/lib/mt \ - -I$(DEPTH)/lib/io \ - -I$(DEPTH)/lib/net \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ + -I$(VDEPTH)/lib/net \ $(NULL) From 5c654d531e23e67ab08fffc0e910e26fed803f42 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 17:35:43 +0000 Subject: [PATCH 305/807] removed two programs from files to clean. --- Makefile.am | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 2b3bf549..daa8d36c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,10 +20,8 @@ MAINTAINERCLEANFILES = \ aclocal.m4 \ config.h \ config.h.in \ - config.guess \ config.log \ config.status \ - config.sub \ configure \ stamp-h.in \ stamp-h1 \ From ee46c5e35a08c3d25edf2366852855a6c4c93650 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 17:40:21 +0000 Subject: [PATCH 306/807] fixed comment. --- lib/synergy/XSynergy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/synergy/XSynergy.h b/lib/synergy/XSynergy.h index 86ce98ee..e836b7de 100644 --- a/lib/synergy/XSynergy.h +++ b/lib/synergy/XSynergy.h @@ -66,7 +66,7 @@ private: CString m_name; }; -//! Client not in map +//! Client not in map exception /*! Thrown when a client attempting to connect is using a name that is unknown to the server. From 7900d9fe419e4233b23e3fff45aaec32ba22c44a Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Jul 2002 17:40:36 +0000 Subject: [PATCH 307/807] added simple rule to build doxygen. --- Makefile.am | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile.am b/Makefile.am index daa8d36c..3b7ec105 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,3 +26,7 @@ MAINTAINERCLEANFILES = \ stamp-h.in \ stamp-h1 \ $(NULL) + +# build doxygen documentation +doxygen: + doxygen doc/doxygen.cfg From 3f13217929f4aff34975dc9ff016524b9766149c Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 1 Aug 2002 11:45:21 +0000 Subject: [PATCH 308/807] minor automake fixes. --- configure.in | 27 +++++++++++++++++---------- lib/base/CStopwatch.cpp | 3 --- lib/mt/CCondVar.cpp | 11 ++++++++++- lib/platform/CUnixPlatform.cpp | 2 ++ 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/configure.in b/configure.in index 53c87cbc..4cbb590a 100644 --- a/configure.in +++ b/configure.in @@ -1,14 +1,21 @@ dnl Process this file with autoconf to produce a configure script. +dnl initialize AC_INIT(lib/base/common.h) -AM_CONFIG_HEADER(config.h) AC_CONFIG_AUX_DIR(config) -AM_INIT_AUTOMAKE(synergy, 1.0.0) + +dnl current version +MAJOR_VERSION=0 +MINOR_VERSION=9 +RELEASE_VERSION=8 + +dnl initialize automake +AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) +AM_CONFIG_HEADER(config.h) dnl information on the package dnl checks for programs -AC_PROG_CC AC_PROG_CXX AC_PROG_RANLIB @@ -20,7 +27,6 @@ AC_HEADER_STDC AC_CHECK_HEADERS([unistd.h sys/time.h]) AC_CHECK_HEADERS([istream ostream]) AC_CHECK_HEADERS([windows.h]) -AC_HEADER_SYS_WAIT AC_HEADER_TIME AC_PATH_X AC_PATH_XTRA @@ -37,26 +43,27 @@ AC_CHECK_SIZEOF(char, 1) AC_CHECK_SIZEOF(short, 2) AC_CHECK_SIZEOF(int, 2) AC_CHECK_SIZEOF(long, 4) -dnl should check for bool -dnl should require template support -dnl should require exception support +dnl require bool support +dnl require template support +dnl require exception support +dnl require C++ casting support +dnl require mutable support dnl checks for library functions dnl AC_TYPE_SIGNAL -dnl AC_FUNC_FORK +AC_FUNC_FORK AC_FUNC_MEMCMP AC_FUNC_STRFTIME AC_CHECK_FUNCS(gmtime_r) AC_CHECK_FUNCS(getpwuid_r) +AC_CHECK_FUNCS(vsnprintf) dnl use AC_REPLACE_FUNCS() for stuff in string.h dnl checks for system services dnl adjust variables for X11 and pthreads -CFLAGS="$CFLAGS $X_CFLAGS $PTHREAD_CFLAGS" CXXFLAGS="$CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" -CC="$PTHREAD_CC" AC_OUTPUT([ Makefile diff --git a/lib/base/CStopwatch.cpp b/lib/base/CStopwatch.cpp index dbbe0ba7..99792d19 100644 --- a/lib/base/CStopwatch.cpp +++ b/lib/base/CStopwatch.cpp @@ -196,9 +196,6 @@ CStopwatch::getClock() const # include # endif #endif -#if HAVE_UNISTD_H -# include -#endif double CStopwatch::getClock() const diff --git a/lib/mt/CCondVar.cpp b/lib/mt/CCondVar.cpp index 76035aab..cf6f9f96 100644 --- a/lib/mt/CCondVar.cpp +++ b/lib/mt/CCondVar.cpp @@ -49,7 +49,16 @@ CCondVarBase::getMutex() const #include "CThread.h" #include -#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif #include void diff --git a/lib/platform/CUnixPlatform.cpp b/lib/platform/CUnixPlatform.cpp index 9e3c10e2..8bd1f6de 100644 --- a/lib/platform/CUnixPlatform.cpp +++ b/lib/platform/CUnixPlatform.cpp @@ -40,6 +40,7 @@ CUnixPlatform::uninstallDaemon(const char*) int CUnixPlatform::daemonize(const char* name, DaemonFunc func) { +#if HAVE_WORKING_FORK // fork so shell thinks we're done and so we're not a process // group leader switch (fork()) { @@ -55,6 +56,7 @@ CUnixPlatform::daemonize(const char* name, DaemonFunc func) // parent exits exit(0); } +#endif // become leader of a new session setsid(); From ffea42bf917180030cd7df01007ba92a7d72ba07 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 1 Aug 2002 18:07:48 +0000 Subject: [PATCH 309/807] added files for release. --- AUTHORS | 1 + BUGS | 102 ++ COPYING | 283 ++++ ChangeLog | 4617 +++++++++++++++++++++++++++++++++++++++++++++++++++++ FAQ | 9 + INSTALL | 352 ++++ NEWS | 5 + README | 582 ++----- 8 files changed, 5545 insertions(+), 406 deletions(-) create mode 100644 AUTHORS create mode 100644 BUGS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 FAQ create mode 100644 INSTALL create mode 100644 NEWS diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..87723923 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Chris Schoeneman diff --git a/BUGS b/BUGS new file mode 100644 index 00000000..4212ef23 --- /dev/null +++ b/BUGS @@ -0,0 +1,102 @@ +This file lists the known bugs in synergy. + +Report bugs to: synergy@groundhog.pair.com + +When reporting bugs, please include the version of the operating +system you're using and what locale you use. + +* Not all keystrokes are handled + + Certain keystrokes are not captured by the synergy server and, + therefore, are not transmitted to secondary screens. Which keys + depends on the primary screen's platform: + + Linux: + * ctrl+alt+del + * ctrl+alt+backspace (only if used by the X server) + * ctrl+alt+keypad_plus (only if used by the X server) + * ctrl+alt+keypad_minus (only if used by the X server) + + Windows NT family (except NT prior to service pack 3) + * ctrl+alt+del + * accessibility shortcuts (e.g. pressing shift 5 times for sticky keys) + + Windows 95 family (at NT prior to service pack 3) + * ctrl+alt+del + * ctrl+esc + * alt+[shift+]tab + * alt+[shift+]esc + * windows+E + * windows+[ctrl+]F + * windows+[shift+]M + * windows+R + * windows+F1 + * windows+tab + * windows+break + * accessibility shortcuts (e.g. press shift 5 times for sticky keys) + +* Non-ASCII keystrokes unsupported + + Only ASCII characters are decoded and transmitted by the server. + However, the clipboard does support Unicode. + +* Screen flashes (Windows) + + On Windows, the primary screen flashes when the cursor enters it. + This is a consequence of how synergy captures input. + +* Slow console text entry (Windows 95 family) + + Typing into a command prompt window on the Windows 95 family has + very slow visual feedback. + +* Event logs (Windows NT family) + + Synergy doesn't support event logs properly on the Windows NT family. + In particular, the event message is reported in the extra data portion + of the event instead of the normal message portion. + +* NoInteractiveServices not handled (Windows NT family) + + Synergy does not (cannot) work when the NoInteractiveServices registry + entry is set. Synergy doesn't not gracefully handle it being set. + +* Incorrectly hiding screen savers in certain situations (Windows) + + If turning off the screen saver on the primary screen requires a + password on Windows then all of the secondary screen screen savers + are deactivated when the password dialog appears. If the password + is not entered only the primary screen's screen saver is activated. + +* Not handling strange keyboards (X11) + + Synergy does not detect keyboards with unusual behavior and may + misbehave as a result. Unusual behavior includes keyboards that + don't report KeyRelease events and toggle keys that report KeyPress + when toggled on and KeyRelease when toggled off (instead of KeyPress + and KeyRelease for each physical press and release). + +* Not handling mode-switch and shift-lock (X11) + + Synergy neither handles the mode-switch key nor shift-lock behavior + (as opposed to caps-lock). + +* Large Motif clipboard items are truncated (X11) + + Synergy doesn't yet fully implement Motif's protocol for large + clipboard items. As a result it silently truncates those items. + Large depends on the X server but is probably around 256kB. + + Synergy also doesn't support by-name Motif clipboard items. It + will read no data at all for those items. + +* Automake isn't fully configured (Linux) + + The automake configuration isn't complete so synergy won't build + properly on some (many) systems. + +* Only text is supported on the clipboard + + The clipboard supports Unicode and current locale encoded text but + there are numerous other data types it should support, e.g. images, + sound, raw binary data, etc. diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..895e5896 --- /dev/null +++ b/COPYING @@ -0,0 +1,283 @@ +Synergy is copyright (C) 2002 Chris Schoeneman. +Synergy is distributed under the following license. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..fa681562 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,4617 @@ +2002/08/01 18:07:32 crs +AUTHORS +BUGS +COPYING +ChangeLog +FAQ +INSTALL +NEWS +README + +added files for release. + +---------- +2002/08/01 11:45:21 crs +configure.in +lib/base/CStopwatch.cpp +lib/mt/CCondVar.cpp +lib/platform/CUnixPlatform.cpp + +minor automake fixes. + +---------- +2002/07/31 17:40:36 crs +Makefile.am + +added simple rule to build doxygen. + +---------- +2002/07/31 17:40:21 crs +lib/synergy/XSynergy.h + +fixed comment. + +---------- +2002/07/31 17:35:43 crs +Makefile.am + +removed two programs from files to clean. + +---------- +2002/07/31 17:34:05 crs +Makefile.am +cmd/Makefile.am +cmd/synergy/Makefile.am +cmd/synergyd/Makefile.am +lib/Makefile.am +lib/base/Makefile.am +lib/client/Makefile.am +lib/http/Makefile.am +lib/io/Makefile.am +lib/mt/Makefile.am +lib/net/Makefile.am +lib/platform/Makefile.am +lib/server/Makefile.am +lib/synergy/Makefile.am + +fixes to get vpath builds working (necessary for `make distcheck'). + +---------- +2002/07/31 16:57:26 crs +Makefile.am +cmd/Makefile.am +cmd/synergy/Makefile.am +cmd/synergy/synergy.cpp +cmd/synergyd/Makefile.am +cmd/synergyd/synergyd.cpp +configure.in +lib/Makefile.am +lib/base/Makefile.am +lib/base/Version.h +lib/client/Makefile.am +lib/http/Makefile.am +lib/io/Makefile.am +lib/mt/Makefile.am +lib/net/Makefile.am +lib/platform/Makefile.am +lib/server/Makefile.am +lib/synergy/Makefile.am +lib/synergy/ProtocolTypes.h +lib/synergy/Version.h + +Moved version header to base and it now uses VERSION macro +from config.h if available (which means version is now a +string, not three integers). Changed version to 1.0.0 and +protocol version to 1.0. And added MAINTAINERCLEANFILES +to makefiles to remove generated files. + +---------- +2002/07/31 16:27:06 crs +Makefile.am +cmd/synergy/Makefile.am +cmd/synergyd/Makefile.am +examples/synergy.linux.init +examples/synergyd.linux.init +lib/base/Makefile.am +lib/client/Makefile.am +lib/http/Makefile.am +lib/io/Makefile.am +lib/mt/Makefile.am +lib/net/Makefile.am +lib/platform/Makefile.am +lib/server/Makefile.am +lib/synergy/Makefile.am +nodist/notes +notes + +added EXTRA_* files to get `make dist' doing the right thing. + +---------- +2002/07/31 16:24:45 crs +notes + +checkpoint notes. + +---------- +2002/07/31 13:56:59 crs +lib/base/CLog.cpp + +removed now unnecssary #define. + +---------- +2002/07/31 13:41:58 crs +lib/http/http.dsp +lib/platform/CMSWindowsClipboardAnyTextConverter.cpp +lib/platform/CMSWindowsClipboardAnyTextConverter.h +lib/platform/CMSWindowsClipboardTextConverter.cpp +lib/platform/CMSWindowsClipboardTextConverter.h +lib/platform/CMSWindowsClipboardUTF16Converter.cpp +lib/platform/CMSWindowsClipboardUTF16Converter.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h +lib/platform/IMSWindowsScreenEventHandler.h + +okay, now the files should no longer be executable. + +---------- +2002/07/31 13:34:18 crs +lib/http/http.dsp +lib/platform/CMSWindowsClipboardAnyTextConverter.cpp +lib/platform/CMSWindowsClipboardAnyTextConverter.h +lib/platform/CMSWindowsClipboardTextConverter.cpp +lib/platform/CMSWindowsClipboardTextConverter.h +lib/platform/CMSWindowsClipboardUTF16Converter.cpp +lib/platform/CMSWindowsClipboardUTF16Converter.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h +lib/platform/IMSWindowsScreenEventHandler.h +notes + +removed unintentional executable flag. + +---------- +2002/07/31 13:29:33 crs +notes + +checkpoint notes. + +---------- +2002/07/31 13:18:27 crs +README + +added comment about large motif clipboard items to README. + +---------- +2002/07/31 13:10:15 crs +README + +updated README. + +---------- +2002/07/31 12:40:41 crs +lib/platform/CSynergyHook.cpp +lib/platform/synrgyhk.dsp + +now building hook dll for release without linking in standard +C runtime. need C runtime for debug build for asserts. + +---------- +2002/07/31 12:39:34 crs +cmd/synergy/synergy.cpp +cmd/synergyd/synergyd.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsScreen.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/IClient.h +lib/synergy/IScreen.h +lib/synergy/XScreen.cpp +lib/synergy/XScreen.h + +fixed problem with opening client and server. in some cases it +would fail to open in such a way that it could never succeed +but it'd never stop retrying. now terminating when open fails +such that it'll never succeed. + +---------- +2002/07/30 19:03:40 crs +lib/client/client.dsp +lib/io/io.dsp +lib/net/net.dsp +lib/server/server.dsp +lib/synergy/libsynergy.dsp + +added new files to projects and added two project files that +should've been adding in change 530. + +---------- +2002/07/30 18:49:31 crs +lib/client/CServerProxy.cpp +lib/server/CClientProxy1_0.cpp +lib/synergy/ProtocolTypes.h + +made it so a negative kHeartRate disables heartbeats and set +kHeartRate to -1. + +---------- +2002/07/30 18:31:21 crs +lib/mt/CThreadRep.cpp +lib/mt/XThread.h + +moved exception definition to header file. + +---------- +2002/07/30 18:31:00 crs +cmd/synergy/synergy.cpp +cmd/synergyd/synergyd.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/ISecondaryScreenFactory.h +lib/client/Makefile.am +lib/io/IStreamFilterFactory.h +lib/io/Makefile.am +lib/net/CTCPSocketFactory.cpp +lib/net/CTCPSocketFactory.h +lib/net/ISocketFactory.h +lib/net/Makefile.am +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/IPrimaryScreenFactory.h +lib/server/Makefile.am +lib/synergy/CTCPSocketFactory.cpp +lib/synergy/CTCPSocketFactory.h +lib/synergy/ISocketFactory.h +lib/synergy/Makefile.am +lib/synergy/ProtocolTypes.h + +now using class factories to move some decisions from the libraries +into the application. + +---------- +2002/07/30 16:52:46 crs +Makefile.am +base/BasicTypes.h +base/CFunctionJob.cpp +base/CFunctionJob.h +base/CLog.cpp +base/CLog.h +base/CStopwatch.cpp +base/CStopwatch.h +base/CString.cpp +base/CString.h +base/CUnicode.cpp +base/CUnicode.h +base/IInterface.h +base/IJob.h +base/Makefile.am +base/TMethodJob.h +base/XBase.cpp +base/XBase.h +base/base.dsp +base/common.h +base/stdfstream.h +base/stdistream.h +base/stdlist.h +base/stdmap.h +base/stdostream.h +base/stdpost.h +base/stdpre.h +base/stdset.h +base/stdsstream.h +base/stdvector.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/Makefile.am +client/client.cpp +client/client.dsp +client/client.rc +client/resource.h +cmd/Makefile.am +cmd/synergy/Makefile.am +cmd/synergy/resource.h +cmd/synergy/synergy.cpp +cmd/synergy/synergy.dsp +cmd/synergy/synergy.rc +cmd/synergyd/Makefile.am +cmd/synergyd/resource.h +cmd/synergyd/synergyd.cpp +cmd/synergyd/synergyd.dsp +cmd/synergyd/synergyd.rc +configure.in +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/Makefile.am +http/XHTTP.cpp +http/XHTTP.h +http/http.dsp +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/CInputStreamFilter.h +io/COutputStreamFilter.cpp +io/COutputStreamFilter.h +io/CStreamBuffer.cpp +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/Makefile.am +io/XIO.cpp +io/XIO.h +io/io.dsp +lib/Makefile.am +lib/base/BasicTypes.h +lib/base/CFunctionJob.cpp +lib/base/CFunctionJob.h +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/CStopwatch.cpp +lib/base/CStopwatch.h +lib/base/CString.cpp +lib/base/CString.h +lib/base/CUnicode.cpp +lib/base/CUnicode.h +lib/base/IInterface.h +lib/base/IJob.h +lib/base/Makefile.am +lib/base/TMethodJob.h +lib/base/XBase.cpp +lib/base/XBase.h +lib/base/base.dsp +lib/base/common.h +lib/base/stdfstream.h +lib/base/stdistream.h +lib/base/stdlist.h +lib/base/stdmap.h +lib/base/stdostream.h +lib/base/stdpost.h +lib/base/stdpre.h +lib/base/stdset.h +lib/base/stdsstream.h +lib/base/stdvector.h +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/client/Makefile.am +lib/http/CHTTPProtocol.cpp +lib/http/CHTTPProtocol.h +lib/http/Makefile.am +lib/http/XHTTP.cpp +lib/http/XHTTP.h +lib/http/http.dsp +lib/io/CBufferedInputStream.cpp +lib/io/CBufferedInputStream.h +lib/io/CBufferedOutputStream.cpp +lib/io/CBufferedOutputStream.h +lib/io/CInputStreamFilter.cpp +lib/io/CInputStreamFilter.h +lib/io/COutputStreamFilter.cpp +lib/io/COutputStreamFilter.h +lib/io/CStreamBuffer.cpp +lib/io/CStreamBuffer.h +lib/io/IInputStream.h +lib/io/IOutputStream.h +lib/io/Makefile.am +lib/io/XIO.cpp +lib/io/XIO.h +lib/io/io.dsp +lib/mt/CCondVar.cpp +lib/mt/CCondVar.h +lib/mt/CLock.cpp +lib/mt/CLock.h +lib/mt/CMutex.cpp +lib/mt/CMutex.h +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/mt/CThreadRep.cpp +lib/mt/CThreadRep.h +lib/mt/CTimerThread.cpp +lib/mt/CTimerThread.h +lib/mt/Makefile.am +lib/mt/XThread.h +lib/mt/mt.dsp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp +lib/net/CNetworkAddress.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPListenSocket.h +lib/net/CTCPSocket.cpp +lib/net/CTCPSocket.h +lib/net/IDataSocket.h +lib/net/IListenSocket.h +lib/net/ISocket.h +lib/net/Makefile.am +lib/net/XNetwork.cpp +lib/net/XNetwork.h +lib/net/XSocket.cpp +lib/net/XSocket.h +lib/net/net.dsp +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsClipboard.h +lib/platform/CMSWindowsClipboardAnyTextConverter.cpp +lib/platform/CMSWindowsClipboardAnyTextConverter.h +lib/platform/CMSWindowsClipboardTextConverter.cpp +lib/platform/CMSWindowsClipboardTextConverter.h +lib/platform/CMSWindowsClipboardUTF16Converter.cpp +lib/platform/CMSWindowsClipboardUTF16Converter.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h +lib/platform/CPlatform.cpp +lib/platform/CPlatform.h +lib/platform/CSynergyHook.cpp +lib/platform/CSynergyHook.h +lib/platform/CUnixPlatform.cpp +lib/platform/CUnixPlatform.h +lib/platform/CWin32Platform.cpp +lib/platform/CWin32Platform.h +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsClipboard.h +lib/platform/CXWindowsClipboardTextConverter.cpp +lib/platform/CXWindowsClipboardTextConverter.h +lib/platform/CXWindowsClipboardUCS2Converter.cpp +lib/platform/CXWindowsClipboardUCS2Converter.h +lib/platform/CXWindowsClipboardUTF8Converter.cpp +lib/platform/CXWindowsClipboardUTF8Converter.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h +lib/platform/IMSWindowsScreenEventHandler.h +lib/platform/IPlatform.h +lib/platform/Makefile.am +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CHTTPServer.cpp +lib/server/CHTTPServer.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CMSWindowsPrimaryScreen.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CPrimaryScreen.cpp +lib/server/CPrimaryScreen.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/server/Makefile.am +lib/synergy/CClipboard.cpp +lib/synergy/CClipboard.h +lib/synergy/CInputPacketStream.cpp +lib/synergy/CInputPacketStream.h +lib/synergy/COutputPacketStream.cpp +lib/synergy/COutputPacketStream.h +lib/synergy/CProtocolUtil.cpp +lib/synergy/CProtocolUtil.h +lib/synergy/CTCPSocketFactory.cpp +lib/synergy/CTCPSocketFactory.h +lib/synergy/ClipboardTypes.h +lib/synergy/IClient.h +lib/synergy/IClipboard.h +lib/synergy/IPrimaryScreenReceiver.h +lib/synergy/IScreen.h +lib/synergy/IScreenEventHandler.h +lib/synergy/IScreenReceiver.h +lib/synergy/IScreenSaver.h +lib/synergy/IServer.h +lib/synergy/ISocketFactory.h +lib/synergy/KeyTypes.h +lib/synergy/Makefile.am +lib/synergy/MouseTypes.h +lib/synergy/ProtocolTypes.h +lib/synergy/Version.h +lib/synergy/XScreen.cpp +lib/synergy/XScreen.h +lib/synergy/XSynergy.cpp +lib/synergy/XSynergy.h +lib/synergy/libsynergy.dsp +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.cpp +mt/CTimerThread.h +mt/Makefile.am +mt/XThread.h +mt/mt.dsp +net/CNetwork.cpp +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IDataSocket.h +net/IListenSocket.h +net/ISocket.h +net/Makefile.am +net/XNetwork.cpp +net/XNetwork.h +net/XSocket.cpp +net/XSocket.h +net/net.dsp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsClipboard.h +platform/CMSWindowsClipboardAnyTextConverter.cpp +platform/CMSWindowsClipboardAnyTextConverter.h +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardTextConverter.h +platform/CMSWindowsClipboardUTF16Converter.cpp +platform/CMSWindowsClipboardUTF16Converter.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CMSWindowsScreenSaver.cpp +platform/CMSWindowsScreenSaver.h +platform/CPlatform.cpp +platform/CPlatform.h +platform/CSynergyHook.cpp +platform/CSynergyHook.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsClipboardTextConverter.cpp +platform/CXWindowsClipboardTextConverter.h +platform/CXWindowsClipboardUCS2Converter.cpp +platform/CXWindowsClipboardUCS2Converter.h +platform/CXWindowsClipboardUTF8Converter.cpp +platform/CXWindowsClipboardUTF8Converter.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +platform/IMSWindowsScreenEventHandler.h +platform/IPlatform.h +platform/Makefile.am +platform/makehook.dsp +platform/platform.dsp +platform/synrgyhk.dsp +server/CClientProxy.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile.am +server/resource.h +server/server.cpp +server/server.dsp +server/server.rc +synergy.dsw +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/COutputPacketStream.cpp +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/ClipboardTypes.h +synergy/IClient.h +synergy/IClipboard.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IScreenSaver.h +synergy/IServer.h +synergy/ISocketFactory.h +synergy/KeyTypes.h +synergy/Makefile.am +synergy/MouseTypes.h +synergy/ProtocolTypes.h +synergy/Version.h +synergy/XScreen.cpp +synergy/XScreen.h +synergy/XSynergy.cpp +synergy/XSynergy.h +synergy/synergy.dsp + +Reorganized source tree. Moved client.cpp into cmd/synergy as +synergy.cpp and server.cpp into cmd/synergyd as synergyd.cpp. +Moved and renamed related files. Moved remaining source files +into lib/.... Modified and added makefiles as appropriate. +Result is that library files are under lib with each library +in its own directory and program files are under cmd with each +command in its own directory. + +---------- +2002/07/30 15:17:44 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/server.cpp +synergy/IClient.h + +Replaced method name `run' with `mainLoop', and `stop' and `quit' +with `exitMainLoop' in most places. + +---------- +2002/07/30 14:59:36 crs +client/CClient.h +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.h +server/CClientProxy.h +server/CClientProxy1_0.h +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.h +server/CPrimaryScreen.h +server/CServer.h +server/CXWindowsPrimaryScreen.h +synergy/IClient.h +synergy/IScreen.h +synergy/IScreenReceiver.h +synergy/IServer.h + +Added doxygen comments for all relevant headers in client and server. + +---------- +2002/07/29 17:03:55 crs +platform/CMSWindowsClipboard.h +platform/CMSWindowsClipboardAnyTextConverter.h +platform/CMSWindowsClipboardTextConverter.h +platform/CMSWindowsClipboardUTF16Converter.h +platform/CMSWindowsScreen.h +platform/CMSWindowsScreenSaver.h +platform/CPlatform.h +platform/CSynergyHook.h +platform/CUnixPlatform.h +platform/CWin32Platform.h +platform/CXWindowsClipboard.h +platform/CXWindowsClipboardTextConverter.h +platform/CXWindowsClipboardUCS2Converter.h +platform/CXWindowsClipboardUTF8Converter.h +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.h +platform/CXWindowsUtil.h +platform/IMSWindowsScreenEventHandler.h +platform/IPlatform.h + +Added doxygen comments for all relevant headers in platform. + +---------- +2002/07/29 16:07:26 crs +synergy/CClipboard.h +synergy/CInputPacketStream.h +synergy/COutputPacketStream.h +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.h +synergy/ClipboardTypes.h +synergy/IClient.h +synergy/IClipboard.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IScreenSaver.h +synergy/IServer.h +synergy/ISocketFactory.h +synergy/KeyTypes.h +synergy/MouseTypes.h +synergy/ProtocolTypes.h +synergy/Version.h +synergy/XScreen.h +synergy/XSynergy.h + +Added doxygen comments for all relevant headers in synergy. + +---------- +2002/07/29 16:06:52 crs +platform/CMSWindowsScreen.cpp +server/CPrimaryClient.cpp + +moved try/catch block from CMSWindowsScreen to CPrimaryClient. +this means CMSWindowsScreen doesn't need to include XSynergy.h. + +---------- +2002/07/29 16:05:59 crs +doc/doxygen.cfg + +changed doxygen configuration. + +---------- +2002/07/28 19:06:52 crs +net/CNetwork.h +net/CNetworkAddress.h +net/CTCPListenSocket.h +net/CTCPSocket.h +net/IDataSocket.h +net/IListenSocket.h +net/ISocket.h +net/XNetwork.h +net/XSocket.cpp +net/XSocket.h + +Added doxygen comments for all relevant headers in net. + +---------- +2002/07/28 17:55:59 crs +http/CHTTPProtocol.h +http/XHTTP.cpp +http/XHTTP.h + +Added doxygen comments for all relevant headers in http. + +---------- +2002/07/28 17:25:13 crs +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.h +io/COutputStreamFilter.h +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/XIO.h + +Added doxygen comments for all relevant headers in io. + +---------- +2002/07/28 13:34:19 crs +mt/CCondVar.h +mt/CLock.h +mt/CMutex.h +mt/CThread.h +mt/CThreadRep.h +mt/CTimerThread.h +mt/XThread.h + +Added doxygen comments for all relevant headers in mt. + +---------- +2002/07/26 18:28:18 crs +base/CFunctionJob.h +base/CLog.h +base/CStopwatch.h +base/CString.h +base/CUnicode.h +base/IInterface.h +base/IJob.h +base/TMethodJob.h +base/XBase.h +base/common.h + +added doxygen comments for all relevant headers in base. + +---------- +2002/07/26 18:27:31 crs +platform/CXWindowsUtil.cpp + +fixed type mismatch (SInt32 vs int) in definition of +getWindowProperty(). + +---------- +2002/07/26 16:05:59 crs +doc/doxygen.cfg + +added configuration file for building doxygen documentation. +the code is not yet doxygen documented, though. + +---------- +2002/07/26 15:22:25 crs +platform/CXWindowsUtil.cpp + +now deleting property when so requested even if read failed. + +---------- +2002/07/25 18:08:00 crs +notes + +checkpoint. + +---------- +2002/07/25 17:58:01 crs +client/client.cpp +net/XSocket.cpp +server/server.cpp + +improved error messages for bad addresses. + +---------- +2002/07/25 17:52:40 crs +base/XBase.cpp +http/XHTTP.cpp +io/XIO.cpp +net/XNetwork.cpp +net/XSocket.cpp +server/CConfig.cpp +server/server.cpp +synergy/CProtocolUtil.cpp +synergy/XScreen.cpp +synergy/XSynergy.cpp + +made all getWhat() methods on exceptions consistent. they now +all use format() the same way. also changed format() to actually +do formatting. however, it doesn't try looking up formatting +strings by id, it just uses the fallback format string. + +---------- +2002/07/25 17:23:35 crs +base/CLog.cpp +base/CLog.h +base/CString.cpp +base/CString.h + +moved string formatting into CStringUtil from CLog and added +methods for format positional string arguments. + +---------- +2002/07/25 09:55:01 crs +platform/CXWindowsScreen.cpp + +added unix specific implementation of CXWindowsScreen::mainLoop() +that uses poll() to process events more efficiently. it won't +wake up nor sleep any more than necessary, unlike the platform +independent implementation that polls and sleeps. + +---------- +2002/07/25 09:23:24 crs +platform/CXWindowsClipboard.cpp + +finished INCR transfer changes. also made motifGetTime() return +icccmGetTime() because it seems motif does TIMESTAMP like ICCCM. + +---------- +2002/07/25 08:57:46 crs +platform/CXWindowsClipboard.cpp + +checkpoint. working on INCR transfers. + +---------- +2002/07/24 19:26:18 crs +platform/CMSWindowsScreen.cpp +platform/CWin32Platform.cpp +platform/CWin32Platform.h + +fixes for win32 due to changes in how s_restartable is handled. +the main change is that WM_QUIT now causes the thread to be +cancelled instead of mainLoop() just returning. this also +requires runDaemon() to call the run function in a new thread +each time it calls it because it could can cancelled. + +---------- +2002/07/24 19:24:21 crs +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardUTF16Converter.cpp + +fixes for win32 clipboard due to CUnicode nul terminator changes. + +---------- +2002/07/24 19:23:46 crs +base/CUnicode.cpp + +fixed an off-by-one error in UTF8ToText(). + +---------- +2002/07/24 17:39:52 crs +base/CUnicode.cpp + +fixed an off-by-one error in textToUTF8(). + +---------- +2002/07/24 17:30:32 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +fixed type of TARGETS target. + +---------- +2002/07/24 17:22:01 crs +base/CUnicode.cpp +base/CUnicode.h +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardUTF16Converter.cpp + +made handling of nul terminators in CUnicode more sane. + +---------- +2002/07/24 17:07:52 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +some fixes for motif clipboard. still not handling incremental +transfer through root window property because not sure of the +protocol. + +---------- +2002/07/24 13:01:18 crs +client/CClient.cpp +client/client.cpp +net/CTCPSocket.cpp +net/XSocket.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsScreen.cpp +platform/CXWindowsUtil.cpp +platform/IPlatform.h +server/CPrimaryClient.cpp +server/CServer.cpp +server/server.cpp +synergy/ProtocolTypes.h +synergy/Version.h + +removed restart function from platform. no longer trying to +restart if the X server connection was lost; since synergy +is likely to be started by xdm or the user's xsession, it's +better for synergy to simply terminate when the connection +is lost. synergy will still restart due to other errors. +also fixed numerous other minor bugs and cleaned some stuff +up (like app error codes are now consistent and enumerated +in Version.h, for lack of a better place). and boosted +version and protocol numbers. + +---------- +2002/07/23 19:00:01 crs +notes + +checkpoint. + +---------- +2002/07/23 18:59:44 crs +platform/CMSWindowsClipboard.cpp + +fixed a bug in clipboard conversion (was using wrong converter or +no converter when one was available). + +---------- +2002/07/23 18:59:15 crs +client/CMSWindowsSecondaryScreen.cpp +server/CMSWindowsPrimaryScreen.cpp +synergy/KeyTypes.h + +converted win32 to use unicode based KeyID. + +---------- +2002/07/23 17:04:41 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/KeyTypes.h + +checkpoint. converting KeyID to use UTF-32 encoding instead of +X11 keysyms. + +---------- +2002/07/23 15:34:05 crs +synergy/CClipboard.cpp +synergy/IClipboard.h + +no longer attempting to unmarshall clipboard formats that aren't +known to the caller. if the client supports more formats than +the server then the server could get a clipboard format greater +than kNumFormats. with this change the server discards the +extra formats instead of crashing. + +---------- +2002/07/23 15:26:40 crs +base/CUnicode.cpp +base/CUnicode.h +base/base.dsp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsClipboard.h +platform/CMSWindowsClipboardAnyTextConverter.cpp +platform/CMSWindowsClipboardAnyTextConverter.h +platform/CMSWindowsClipboardTextConverter.cpp +platform/CMSWindowsClipboardTextConverter.h +platform/CMSWindowsClipboardUTF16Converter.cpp +platform/CMSWindowsClipboardUTF16Converter.h +platform/platform.dsp + +unicode clipboard changes for win32 plus some bug fixes. + +---------- +2002/07/23 12:35:36 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +removed unnecessary atoms from X clipboard object. + +---------- +2002/07/23 12:08:30 crs +base/CUnicode.cpp + +checkpoint. more CUnicode fixes. + +---------- +2002/07/23 11:51:13 crs +base/CUnicode.cpp + +checkpoint. fixed the other cases in the same function as the +previous checkin. also prevented the errors flag from getting +reset after the multibyte to wide character conversion. + +---------- +2002/07/23 11:42:54 crs +base/CUnicode.cpp + +checkpoint. fixed cases for mbrtowc (was using 1 and 2 instead +of -1 and -2). + +---------- +2002/07/23 11:36:18 crs +base/CUnicode.cpp +base/CUnicode.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboardTextConverter.cpp + +checkpoint. more UTF8 clipboard stuff. + +---------- +2002/07/23 09:33:50 crs +base/CUnicode.cpp +base/CUnicode.h +platform/CXWindowsClipboard.cpp + +checkpoint. more UTF8 clipboard testing. + +---------- +2002/07/22 18:46:57 crs +base/CUnicode.cpp +platform/CXWindowsClipboard.cpp + +checkpoint. more UTF8 clipboard stuff. + +---------- +2002/07/22 18:17:21 crs +platform/CXWindowsClipboard.cpp + +checkpoint. more UTF8 clipboard stuff. + +---------- +2002/07/22 18:03:44 crs +platform/CXWindowsClipboard.cpp + +checkpoint. working on UTF8 clipboard transfer. + +---------- +2002/07/22 17:32:51 crs +base/CUnicode.cpp +base/CUnicode.h +base/Makefile.am +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsClipboardTextConverter.cpp +platform/CXWindowsClipboardTextConverter.h +platform/CXWindowsClipboardUCS2Converter.cpp +platform/CXWindowsClipboardUCS2Converter.h +platform/CXWindowsClipboardUTF8Converter.cpp +platform/CXWindowsClipboardUTF8Converter.h +platform/Makefile.am +synergy/IClipboard.h + +checkpoint. adding support for unicode in clipboard. + +---------- +2002/07/19 21:27:59 crs +README + +changed notes about how to startup configure synergy. it now +discourages using boot scripts, which can't handle X servers +requiring authorization, and suggests modifying xdm's Xsetup. + +---------- +2002/07/19 20:44:57 crs +examples/synergy.linux.init +examples/synergyd.linux.init + +updated init.d scripts to work with SuSE. however, it looks as +if they cannot be used on an X server using authentication +because the daemons they start are not authorized to connect to +the X server. X users should modify Xsetup or Xsession. + +---------- +2002/07/19 18:12:41 crs +server/CXWindowsPrimaryScreen.cpp + +formatting. + +---------- +2002/07/19 17:39:45 crs +server/CPrimaryScreen.cpp + +removed from previous change. + +---------- +2002/07/19 17:38:34 crs +server/CPrimaryScreen.cpp + +reordered operations to reduce cursor flashing when entering +primary screen. + +---------- +2002/07/18 17:03:10 crs +platform/CSynergyHook.cpp +server/CMSWindowsPrimaryScreen.cpp + +fixed handling of calling init() when a previous process did not +call cleanup(). if that process still appears to exist then the +init() fails. otherwise some cleanup is performed and the init() +proceeds. a synergy server started while another is running will +now exit immediately without interfering the original server. + +---------- +2002/07/18 17:00:48 crs +server/CServer.cpp +server/CServer.h + +now cancelling and waiting for the accept client thread before +cancelling any other threads. this prevents a race condition +where we disconnect a client but it reconnects before we +manage to shutdown. that might leave a thread running and +the connection won't be closed down properly. + +---------- +2002/07/18 16:58:08 crs +mt/CThreadRep.cpp +platform/CMSWindowsScreen.cpp + +changed waitForEvent() to handle a peculiar feature of +MsgWaitForMultipleObjects(): it will not return immediately +if an event already in the queue when it's called was already +in the queue during the last call to GetMessage()/PeekMessage(). +also now discarding screen saver events if there are any other +screen saver events in the queue already. this prevents these +events from piling up in the queue, which they'd do because we +sleep for 250ms when handling each one. + +---------- +2002/07/18 08:54:17 crs +synergy.dsw + +fixed incorrect paths to makehook and synrgyhk project files. + +---------- +2002/07/17 17:27:41 crs +platform/CMSWindowsScreen.cpp +platform/CSynergyHook.cpp +platform/CSynergyHook.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h + +attempt to fix stuttering when leaving win32 screen. seems to +work but will let testers make the final call. also fixed +desktop synchronization by setting a variable that was +mistakenly left unset. and tried to work around an apparent +bug in MsgWaitForMultipleObjects() that prevented the service +from closing down properly. start/pause/continue/stop +sequence still doesn't shut down correctly. start/pause/stop +and start/stop work fine. + +---------- +2002/07/17 17:24:44 crs +client/CClient.cpp + +removed unnecessary local variable. + +---------- +2002/07/16 19:07:15 crs +base/stdistream.h +io/CStreamBuffer.cpp + +fixes to get it compiling on .NET. + +---------- +2002/07/16 16:52:26 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CXWindowsScreen.cpp +platform/IMSWindowsScreenEventHandler.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IServer.h + +moved onError() method to IScreenReceiver from IPrimaryScreenReceiver. +also implemented onError in CClient which previously did not have +any way to handle display disconnection. + +---------- +2002/07/15 15:03:04 crs +platform/CSynergyHook.cpp +platform/synrgyhk.dsp + +completing previous checkin. + +---------- +2002/07/15 15:01:36 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.dsp +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CSynergyHook.cpp +platform/CSynergyHook.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/IMSWindowsScreenEventHandler.h +platform/makehook.dsp +platform/platform.dsp +platform/synrgyhk.dsp +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/makehook.dsp +server/server.dsp +server/synrgyhk.dsp +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/ProtocolTypes.h +synergy/synergy.dsp + +checkpoint. refactored win32 code. had to edit and rename some +files so this is only a checkpoint. + +---------- +2002/07/13 22:00:38 crs +client/CClient.cpp +client/CClient.h +client/CSecondaryScreen.cpp +client/CSecondaryScreen.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/ISecondaryScreen.h +client/Makefile.am +client/client.cpp +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CPrimaryScreen.cpp +server/CPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryScreen.h +server/Makefile.am +server/server.cpp +synergy/IClient.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreen.h +synergy/IScreenEventHandler.h +synergy/IScreenReceiver.h +synergy/IServer.h +synergy/Makefile.am + +checkpoint. still refactoring. merged common code from primary +screens into CPrimaryScreen and merged common code from secondary +screens into CSecondaryScreen. changed is-a relationship to a +has-a between the primary and secondary screen classes and the +generic platform dependent screen class to avoid multiple +inheritance of implementation. also standardized the interface +for those generic screen classes. adding a platform now involves +implementing simpler interfaces: IScreen for the generic screen, +IScreenEventHandler and some methods of CPrimaryScreen for the +primary screen, and IScreenEventHandler and some methods of +CSecondaryScreen for the secondary screen. did X11 platform +but not win32 platform. + +---------- +2002/07/12 20:41:23 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +refactoring. refactored stuff in client (with changes to server +as necessary). + +---------- +2002/07/11 18:58:49 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +checkpoint. making win32 and X primary screen code more similar +in order to share code later. + +---------- +2002/07/11 13:13:37 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CServerProxy.cpp +client/client.dsp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryScreen.h +server/server.dsp +synergy/synergy.dsp + +applied refactoring to win32 code. + +---------- +2002/07/10 21:22:28 crs +client/CClient.cpp +client/CClient.h +client/CServerProxy.cpp +client/CServerProxy.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/ISecondaryScreen.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreenReceiver.h +synergy/IScreenReceiver.h +synergy/IServer.h +synergy/Makefile.am + +more refactoring. + +---------- +2002/07/10 20:18:32 crs +client/CClient.cpp +client/CClient.h +client/CServerProxy.cpp +client/CServerProxy.h +client/ISecondaryScreen.h +client/Makefile.am +client/client.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +synergy/IClient.h +synergy/ISecondaryScreen.h +synergy/Makefile.am + +refactored client code. it now uses IClient and IServer and +has a CServerProxy, making it's design similar to the server +code. + +---------- +2002/07/10 14:29:50 crs +synergy/IClient.h + +removed some obsolete comments. + +---------- +2002/07/10 14:15:17 crs +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryReceiver.h +server/Makefile.am +synergy/IServer.h +synergy/ProtocolTypes.h + +removed IPrimaryReceiver in favor of IServer, which required a few +minor changes to support IPrimaryReciever's functionality. this +does mean that the IPrimaryScreen class will be calling some +methods with dummy arguments. those are documented in +CPrimaryClient. + +---------- +2002/07/09 21:22:31 crs +acinclude.m4 +acsite.m4 +config/depcomp +config/install-sh +config/missing +config/mkinstalldirs +mt/CThread.cpp +mt/CThread.h +notes +platform/CXWindowsClipboard.cpp +server/CClientProxy.cpp +server/CClientProxy.h +server/CClientProxy1_0.cpp +server/CClientProxy1_0.h +server/CPrimaryClient.cpp +server/CPrimaryClient.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/IPrimaryReceiver.h +server/IPrimaryScreen.h +server/IServerProtocol.h +server/Makefile.am +synergy/IClient.h +synergy/IServer.h +synergy/Makefile.am + +updated to new automake and refactored server stuff. the server +now speaks to the primary screen and secondary screens almost +everywhere the same way through an IClient interface; only +special primary screen calls are accessed through a different +interface, the CPrimaryClient interface. this simplifies the +server since it no longer needs to test whether the active screen +is the primary or a secondary in most cases. + +the server no longer speaks directly to the primary screen; all +that goes through the CPrimaryClient, which often just forwards +the call. the primary screen no longer speaks directly to the +server either, again going through the CPrimaryClient via a +IPrimaryReceiver interface. + +CServerProtocol classes have been replaced by CClientProxy +classes which are very similar. the name makes more sense +though. + +---------- +2002/07/09 17:31:45 crs +server/IPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/Makefile.am + +checkpoint. moved IPrimaryScreen.h. + +---------- +2002/07/07 15:15:34 crs +server/IServerProtocol.h +synergy/IServerProtocol.h + +moved IServerProtocol to server from synergy directory. + +---------- +2002/07/03 16:25:36 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +fixed spurious mouse motions when entering/leaving primary +screen on X11. + +---------- +2002/07/01 15:05:49 crs +server/CMSWindowsPrimaryScreen.cpp + +mistakenly removed mouse button checks when on secondary screens +from isLockedToScreen() in earlier checkin. + +---------- +2002/07/01 14:01:23 crs +README +notes + +checkpoint. + +---------- +2002/07/01 13:03:16 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +synergy/ISecondaryScreen.h + +now synthesizing key release events for each pressed key when +the client screen is closed. this fixes the bug where the +client's keyboard was left with some keys logically pressed +when the client died (e.g. using ctrl+c on the client program +from the server's keyboard would leave the ctrl key logically +pressed). + +---------- +2002/07/01 13:01:16 crs +server/CServerProtocol1_0.cpp + +disabled removing client if no heartbeat is received. we don't +want that while testing because it might hide bugs. + +---------- +2002/07/01 13:00:12 crs +server/CMSWindowsPrimaryScreen.cpp + +fixed locking to screen on win32. was using GetKeyboardState() +to query keys but that doesn't give us up-to-date information. +now using GetAsyncKeyState() if on primary and m_keys if on +secondary. + +---------- +2002/07/01 12:58:52 crs +platform/CMSWindowsScreenSaver.cpp +platform/CMSWindowsScreenSaver.h + +added win32 screen saver class forgotten in previous checkins. + +---------- +2002/06/26 16:31:48 crs +client/CClient.cpp +http/CHTTPProtocol.cpp +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CInputStreamFilter.h +io/IInputStream.h +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp +server/CSynergyHook.cpp +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/ProtocolTypes.h + +synergy hook DLL will now restart itself if a client tries to +init() it while it's already running. fixed an uninitialized +pointer bug in CServer and some cleanup-on-error code in +CMSWindowsPrimaryScreen. also added timeout to read() on +IInputStream and a heartbeat sent by clients so the server +can disconnect clients that are dead but never reset the TCP +connection. previously the server would keep these dead +clients around forever and if the user was locked on the +client screen for some reason then the server would have to +be rebooted (or the server would have to be killed via a +remote login). + +---------- +2002/06/26 13:48:08 crs +client/CClient.cpp +client/CClient.h + +client now compresses mouse motion events. this fixes slow +dragging on grace, possibly on win32 too. + +---------- +2002/06/26 13:31:06 crs +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h + +fixed getSize() to be non-blocking in CInputPacketStream. + +---------- +2002/06/26 12:44:52 crs +platform/CXWindowsScreen.cpp +platform/CXWindowsScreenSaver.h + +fixed re-entrant calls to X bug. + +---------- +2002/06/23 23:24:22 crs +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h + +fixed handling of jumping to primary when screen saver starts +and back to secondary when it stops. also now redirecting +keyboard input to root window when screen saver starts; this +allows the user to type in the lock dialog and also effectively +discards any input used to deactivate the screen saver. + +---------- +2002/06/23 21:54:05 crs +notes + +checkpoint. + +---------- +2002/06/23 21:53:31 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/platform.dsp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CSynergyHook.cpp +server/CSynergyHook.h + +win32 screen saver now handled. + +---------- +2002/06/23 21:48:33 crs +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h + +now disabling disable job timer when forcing screen saver +activation. previously the timer would deactivate the screen +saver shortly after activation. job timer is restored when +the screen saver is deactivated. + +---------- +2002/06/23 15:43:40 crs +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +platform/CXWindowsUtil.cpp +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile.am +synergy/IPrimaryScreen.h + +checkpoint screensaver changes. now handling xscreensaver +dying and restarting or starting after synergy does. also +now disabling the screen saver on the client. next step: +win32 support. + +---------- +2002/06/22 20:29:59 crs +platform/CXWindowsScreen.cpp +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h + +fixes to get xscreensaver integration working. + +---------- +2002/06/22 19:47:27 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +server/CXWindowsPrimaryScreen.cpp + +CXWindowsUtil::CErrorLock wasn't XSync()'ing the display before +installing and uninstalling the new error handler, causing +errors before the lock to be caught and errors during the lock +to not be caught. had to add Display* as argument to c'tor. + +---------- +2002/06/22 19:20:21 crs +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsScreenSaver.cpp +platform/CXWindowsScreenSaver.h +platform/Makefile.am +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/IScreenSaver.h +synergy/ISecondaryScreen.h +synergy/Makefile.am + +checkpoint. adding screen saver support. only on X so far +and untested. also some known problems: not detecting an +xscreensaver started after us and not detecting built-in +screen saver activation (not sure if we can without using +ugly extensions). + +---------- +2002/06/22 17:31:24 crs +client/Makefile.am +http/Makefile.am +io/Makefile.am +mt/Makefile.am +net/Makefile.am +platform/Makefile.am +server/Makefile.am +synergy/Makefile.am + +added header files to _SOURCES. + +---------- +2002/06/22 13:55:45 crs +server/CXWindowsPrimaryScreen.cpp + +added comments. + +---------- +2002/06/22 12:09:49 crs +client/CXWindowsSecondaryScreen.cpp + +cleanup. + +---------- +2002/06/21 17:55:47 crs +client/CClient.cpp +client/CClient.h +client/CXWindowsSecondaryScreen.cpp +client/client.cpp +server/CServer.cpp +server/CServer.h +server/server.cpp + +cleaned up some minor bugs. + +---------- +2002/06/21 17:54:22 crs +net/CNetwork.cpp +net/CNetwork.h + +ported network changes to win32. + +---------- +2002/06/21 16:29:35 crs +net/CNetworkAddress.cpp + +now trying to convert hostname as a dot notation address before +trying name lookup. not all platforms will do this for us in +gethostbyname(). + +---------- +2002/06/21 16:19:08 crs +net/CNetwork.cpp +net/CNetwork.h +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp + +fixed CTCPSocket::connect() to allow cancellation. + +---------- +2002/06/21 15:18:01 crs +server/CServer.cpp +server/CServer.h + +some cleanup. also fixed a race condition when adding threads +to the thread list: the child thread would add itself to the +list which means there could be a time interval in the parent +where the child thread exists but isn't on the list. the +parent now does the adding and removing. + +---------- +2002/06/21 15:15:34 crs +platform/CUnixPlatform.cpp + +now blocking SIGINT and SIGTERM in restart function. the child +should handle the signal and terminate. then the restart +function will exit. + +---------- +2002/06/21 15:14:32 crs +mt/CThreadRep.cpp + +signal handler thread now dies when SIGABRT is raised. ignoring +SIGABRT in sigwait() seems to be a bug in the linux pthread +library. + +---------- +2002/06/20 16:27:49 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +fixed bug introduced by previous checkin. calling XCheckIfEvent() +multiple times is *not* the same as calling XIfEvent() because the +former will re-encounter events that it didn't process previously. +to make things simple it now pulls events off the queue and saves +them if not processed for selection transfer and puts them back +afterwards. + +---------- +2002/06/20 14:01:44 crs +platform/CXWindowsClipboard.cpp + +speeded up clipboard transfer by avoiding a selection request +when it wasn't necessary. (in particular, we were getting the +clipboard update time from the owner then emptying the clipboard, +so we didn't need to get the time. worse, most owners don't +support getting the time and we often timed out.) + +also fixed a multithread bug using the X display. we were using +a CThread to send an event after a timeout while we were waiting +in XIfEvent(). this necessarily involved two threads calling +into Xlib at once, which is not allowed. now using polling to +do the timeout because Xlib doesn't have a function to get +events with a timeout. + +---------- +2002/06/20 13:35:28 crs +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h + +checkpoint. trying to fix a delay when sending clipboards on X. + +---------- +2002/06/20 11:13:37 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h + +added workaround for bug windows 98 (Me?) and multiple displays: +absolute mouse_event() moves don't work except for primary +display. + +---------- +2002/06/20 09:19:55 crs +server/CXWindowsPrimaryScreen.cpp + +work around for bug with mouse driver on lombard powerbook. + +---------- +2002/06/19 20:24:35 crs +client/CMSWindowsSecondaryScreen.cpp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +fixed bugs in mouse motion. wasn't taking care to capture all +motion events relative to the previous mouse position. for +example, if two mouse events arrive, the first at x+1,y and +the second at x+2,y, we used to compute deltas of 1,0 and 2,0 +instead of 1,0 and 1,0. that's fixed. also worked around a +bug (probably) in windows that caused a motion event after a +SetCursorPos() to be lost or reported one pixel off from the +correct position. now using mouse_event() which doesn't +have that problem. also fixed calculation of normalized +coordinates for mouse_event() when there are multiple +displays. + +---------- +2002/06/19 17:03:29 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol1_0.cpp +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/ProtocolTypes.h + +checkpoint. initial support for multiple displays on win32. + +---------- +2002/06/19 14:45:22 crs +client/Makefile.am +configure.in +server/Makefile.am + +fixed addition of X11 -L and -l options on link lines. + +---------- +2002/06/19 12:21:26 crs +configure.in +platform/CUnixPlatform.cpp + +checkpoint. automake changes for wait(). + +---------- +2002/06/19 11:58:48 crs +configure.in +http/CHTTPProtocol.cpp +platform/CUnixPlatform.cpp + +checkpoint. automake changes for reentrant functions. + +---------- +2002/06/19 11:23:49 crs +base/BasicTypes.h +base/CLog.cpp +base/CLog.h +base/CStopwatch.cpp +base/XBase.cpp +base/common.h +base/stdistream.h +base/stdostream.h +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +client/client.cpp +configure.in +mt/CCondVar.cpp +mt/CCondVar.h +mt/CMutex.cpp +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +net/CNetwork.cpp +net/CNetwork.h +platform/CPlatform.cpp +platform/CPlatform.h +platform/CXWindowsClipboard.h +platform/CXWindowsScreen.h +platform/CXWindowsUtil.h +server/CServer.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/server.cpp +synergy/CProtocolUtil.h + +checkpoint. more conversion to automake. + +---------- +2002/06/19 08:23:08 crs +config/install-sh +config/missing +config/mkinstalldirs +configure.in +install-sh +missing +mkinstalldirs + +moved auxillary automake files into config directory. + +---------- +2002/06/18 19:47:52 crs +install-sh +missing +mkinstalldirs + +added automake required tools. + +---------- +2002/06/18 19:44:34 crs +Make-linux +Make-solaris +Makecommon +Makefile +Makefile.am +acsite.m4 +base/Makefile +base/Makefile.am +client/Makefile +client/Makefile.am +configure.in +http/Makefile +http/Makefile.am +io/Makefile +io/Makefile.am +mt/Makefile +mt/Makefile.am +net/Makefile +net/Makefile.am +platform/Makefile +platform/Makefile.am +server/Makefile +server/Makefile.am +synergy/Makefile +synergy/Makefile.am +tools/depconv + +started to convert to autoconf/automake. + +---------- +2002/06/18 18:34:55 crs +base/CLog.cpp + +now checking vsnprintf result against < 0 instead of == -1 +for portability. + +---------- +2002/06/18 18:33:59 crs +net/XSocket.cpp + +added FIXME to commented out code. + +---------- +2002/06/17 15:44:45 crs +README +client/client.cpp +server/server.cpp + +made command line parsing a little more sane with respect to +windows NT services. + +---------- +2002/06/17 14:10:25 crs +README + +updates + +---------- +2002/06/17 13:31:21 crs +base/CLog.cpp +base/CString.cpp +base/XBase.cpp +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.cpp +client/client.cpp +http/CHTTPProtocol.cpp +http/XHTTP.cpp +io/CBufferedInputStream.cpp +io/CBufferedOutputStream.cpp +io/CInputStreamFilter.cpp +io/COutputStreamFilter.cpp +io/CStreamBuffer.cpp +mt/CCondVar.cpp +mt/CMutex.cpp +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CTimerThread.cpp +net/CNetwork.cpp +net/CNetworkAddress.cpp +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp +net/XNetwork.cpp +net/XSocket.cpp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsScreen.cpp +platform/CUnixPlatform.cpp +platform/CWin32Platform.cpp +platform/CXWindowsClipboard.cpp +platform/CXWindowsScreen.cpp +platform/CXWindowsUtil.cpp +server/CConfig.cpp +server/CHTTPServer.cpp +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CServerProtocol.cpp +server/CServerProtocol1_0.cpp +server/CSynergyHook.cpp +server/CXWindowsPrimaryScreen.cpp +server/server.cpp +synergy/CClipboard.cpp +synergy/CInputPacketStream.cpp +synergy/COutputPacketStream.cpp +synergy/CProtocolUtil.cpp +synergy/XSynergy.cpp + +formatting changes. + +---------- +2002/06/17 12:02:26 crs +client/CClient.cpp +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IDataSocket.h +net/IListenSocket.h +net/ISocket.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CServer.cpp +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/ISocketFactory.h + +refactored ISocket into IDataSocket. the latter and IListenSocket +now derive from ISocket. + +---------- +2002/06/14 18:08:20 crs +base/CFunctionJob.cpp +base/CFunctionJob.h +base/CLog.cpp +base/TMethodJob.h +base/common.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/client.cpp +io/CStreamBuffer.cpp +io/CStreamBuffer.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +platform/CMSWindowsScreen.cpp +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/IPlatform.h +server/server.cpp + +performance fixes on win32 plus clean up of some warnings. also +improved error messages when uninstalling service. + +---------- +2002/06/11 20:10:49 crs +README + +added a blurb about synrgyhk.dll and that the service manager +will look for the binary wherever it was when --install was +used. + +---------- +2002/06/11 20:09:59 crs +base/stdpre.h +platform/CMSWindowsScreen.h + +windows fixes needed for formatting changes. + +---------- +2002/06/11 18:33:03 crs +client/CXWindowsSecondaryScreen.cpp + +commented out half-duplex flags that should never have been +uncommented. + +---------- +2002/06/11 18:31:06 crs +server/CServer.cpp + +fixed bug with switching screens on primary when there's no +link in that direction (it would assert). introduced bug +when adding support for wrapping. now ignores attempts to +move in a direction with no link. + +---------- +2002/06/11 18:30:08 crs +platform/CUnixPlatform.cpp + +added missing #include . + +---------- +2002/06/10 22:06:45 crs +base/BasicTypes.h +base/CFunctionJob.cpp +base/CLog.cpp +base/CLog.h +base/CString.cpp +base/CString.h +base/TMethodJob.h +base/XBase.cpp +base/XBase.h +base/common.h +base/stdistream.h +base/stdostream.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.cpp +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/XHTTP.cpp +http/XHTTP.h +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/COutputStreamFilter.cpp +io/CStreamBuffer.cpp +io/IInputStream.h +io/IOutputStream.h +io/XIO.cpp +io/XIO.h +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CTimerThread.cpp +mt/CTimerThread.h +mt/XThread.h +net/CNetwork.cpp +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +net/Makefile +net/XNetwork.cpp +net/XNetwork.h +net/XSocket.cpp +net/XSocket.h +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CPlatform.cpp +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +platform/IPlatform.h +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/server.cpp +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CInputPacketStream.cpp +synergy/COutputPacketStream.cpp +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.cpp +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h +synergy/XScreen.cpp +synergy/XSynergy.cpp + +indentation and other formatting changes. also cleaned up +#includes. + +---------- +2002/06/10 16:49:46 crs +base/CLog.cpp +base/CStopwatch.cpp +mt/CCondVar.cpp +mt/CMutex.cpp +mt/CThreadRep.h +net/CNetwork.cpp +net/CNetwork.h +platform/CMSWindowsClipboard.h +platform/CMSWindowsScreen.h +platform/CWin32Platform.h +server/CSynergyHook.h +server/synrgyhk.dsp + +win32 changes. now including windows.h with WIN32_LEAN_AND_MEAN +to avoid including some stuff we don't want (like winsock). + +---------- +2002/06/10 11:09:02 crs +README + +fixes. + +---------- +2002/06/10 11:08:02 crs +README + +updates. + +---------- +2002/06/10 11:00:55 crs +README +examples/synergy.conf +examples/synergy.linux.init +examples/synergyd.linux.init + +added example files and a README. + +---------- +2002/06/10 10:08:36 crs +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp + +now allowing a screen to be its own neighbor to allow wrapping. +also no longer warping mouse to 0,0 when setting server screen +info. that was causing the mouse to jump if the server screen +had itself as its left or top neighbor (directly or indirectly) +once a screen could be its own neighbor. + +---------- +2002/06/10 09:49:21 crs +server/CConfig.cpp + +fixed stripping of comments from configuration streams. + +---------- +2002/06/10 09:49:03 crs +client/client.cpp +server/server.cpp + +changed "permitted" to "supported" in error messages. + +---------- +2002/06/09 23:08:18 crs +client/CClient.cpp + +no longer camps if the server sends an error message. + +---------- +2002/06/09 22:20:28 crs +client/CClient.cpp +client/CClient.h +client/client.cpp + +added support for camping, i.e. repeatly trying to connect to the +server until we succeed. + +---------- +2002/06/09 22:20:01 crs +mt/CTimerThread.cpp +mt/CTimerThread.h + +CTimerThread now allows zero and negative timeouts. a negative +timeout never times out and CTimerThread is a no-op. + +---------- +2002/06/09 18:03:32 crs +platform/CXWindowsScreen.cpp + +now using ":0.0" as the display if DISPLAY isn't set. + +---------- +2002/06/09 18:00:03 crs +notes + +checkpoint. + +---------- +2002/06/09 17:59:32 crs +client/client.cpp +server/CServer.cpp +server/CServer.h +server/server.cpp + +added command line option to choose the screen name. also now +using the hostname as the default name. this is on both client +and server. + +---------- +2002/06/09 17:35:28 crs +mt/CThreadRep.cpp + +added FIXME comment. + +---------- +2002/06/09 17:21:33 crs +server/CServer.cpp + +fixed problem with setConfig(). if the new config didn't +include a screen that was already connected under an alias +then that screen wouldn't be disconnected and removed from +the screen list until the screen voluntarily disconnected. +at that time removeConnection() would assert because the +screen name would not be found. now using the canonical +name in the protocol object as well as CServer. this +allows setConfig() to always detect removed screens and +disconnect them. + +---------- +2002/06/09 16:53:57 crs +platform/CUnixPlatform.cpp + +now exits instead of restarting if child dies due to an +unexpected signal. + +---------- +2002/06/09 16:53:25 crs +client/client.cpp +net/CNetwork.cpp +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/XSocket.cpp +net/XSocket.h +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CServer.cpp +server/CServer.h +server/server.cpp +synergy/ProtocolTypes.h + +added command line and configuration file arguments to choose +the address and port to listen on or connect to. changed the +default port and put it in ProtocolTypes.h. the HTTP port is +now no longer opened unless the --http argument is supplied +or the config file includes it. + +---------- +2002/06/08 23:24:40 crs +server/CConfig.cpp +server/CConfig.h +server/CServer.cpp +server/server.cpp + +added aliases to configuration. an alias is another name for +a screen. it's expected that the server will want to accept +a given client under several names (e.g. the hostname and the +FQDN). + +---------- +2002/06/08 21:48:16 crs +notes + +checkpoint. + +---------- +2002/06/08 21:48:00 crs +all.dsp +base/CLog.cpp +base/CLog.h +base/base.dsp +base/common.h +base/stdpre.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/client.cpp +client/client.dsp +http/http.dsp +io/io.dsp +mt/CThreadRep.cpp +mt/mt.dsp +net/net.dsp +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/IPlatform.h +platform/platform.dsp +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CSynergyHook.cpp +server/CSynergyHook.h +server/makehook.dsp +server/server.cpp +server/server.dsp +server/synrgyhk.dsp +synergy/IPrimaryScreen.h +synergy/synergy.dsp + +win32 changes. changed names of binaries. added support for +running as (and installing/installing) a service. added +support for multiple desktops (NT only, 95 doesn't support +multiple desktops). + +---------- +2002/06/04 12:26:23 crs +Makefile +client/Makefile +client/client.cpp +client/client.dsp +platform/CMSWindowsClipboard.cpp +platform/CMSWindowsClipboard.h +platform/CMSWindowsScreen.cpp +platform/CMSWindowsScreen.h +platform/CPlatform.cpp +platform/CPlatform.h +platform/CUnixPlatform.cpp +platform/CUnixPlatform.h +platform/CWin32Platform.cpp +platform/CWin32Platform.h +platform/CXWindowsClipboard.cpp +platform/CXWindowsClipboard.h +platform/CXWindowsScreen.cpp +platform/CXWindowsScreen.h +platform/CXWindowsUtil.cpp +platform/CXWindowsUtil.h +platform/IPlatform.h +platform/Makefile +server/Makefile +server/server.cpp +server/server.dsp +synergy.dsw +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/CXWindowsUtil.cpp +synergy/CXWindowsUtil.h +synergy/Makefile +synergy/synergy.dsp +test.cpp + +refactored some common platform dependent stuff into a new +library: platform. also removed test.cpp. + +---------- +2002/06/04 11:06:26 crs +client/CClient.cpp +client/client.cpp +server/CServer.cpp +server/CServerProtocol.cpp +server/server.cpp +synergy/ProtocolTypes.h +synergy/Version.h + +added command line parsing, restartability, and daemonizing to +client. broke win32 stuff though. also moved version and +copyright constants into a new file and renamed protocol +version constants. + +---------- +2002/06/04 11:03:34 crs +base/CLog.cpp + +fixed delete bug in printt -- when skipping file and line the +deleted pointer was wrong. + +---------- +2002/06/04 11:02:33 crs +synergy/CXWindowsClipboard.cpp + +fixed timeout when getting selection -- forgot to set flag to +terminate event loop. + +---------- +2002/06/03 18:53:18 crs +base/CLog.cpp +base/CLog.h +server/CConfig.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/server.cpp +synergy/CXWindowsScreen.cpp +synergy/IPrimaryScreen.h + +changes to add command line arguments. also added automatic +restarting and daemonizing on unix. daemon sends log messages +to syslog. unix now reads config file from file named on +command line; if no command line arg then uses effective +user's config file and if that's not there it finally tries +/etc/synergy.conf. if there are no screens configured then +one is added for the primary screen. broke some startup +stuff on win32. + +also now timing out if X primary screen can't grab the mouse +and keyboard. the server will just give up trying to switch +screens. the grabs will fail is some other app has a grab +and won't release it. note that kdm grabs the keyboard for +the duration that the login window is displayed, effectively +disabling synergy. + +---------- +2002/06/03 16:36:45 crs +base/CLog.cpp +base/CLog.h + +added a method to set the filter given a priority string (instead +of a number). fixed a comment related to what those priority +strings are. added a CLOG_PRINT priority which is never filtered +and suppresses the trace info and the priority level message. +it's intended as a way to output a message through the logger +without getting extra output. + +---------- +2002/06/03 16:34:22 crs +base/CString.cpp +base/CString.h +base/Makefile +base/base.dsp +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h + +moved case insensitive comparison utility functions into CString +from CHTTPProtocol. + +---------- +2002/06/03 13:45:30 crs +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CServer.cpp +server/CServer.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/Makefile +synergy/XScreen.cpp +synergy/XScreen.h +synergy/synergy.dsp + +added better handling of X server disconnecting unexpectedly. +the apps still exit but they do it in a mostly controlled +manner. in particular, the server threads except the one +processing primary screen events will terminate gracefully. +this will be important should the server ever allow HTTP +clients to rewrite the configuration file. + +note that X makes it effectively impossible to continue once +the X server disconnects. even if it didn't it would be +difficult for synergy to recover. users will have to add +synergy to the X display manager's startup script if they +expect the server to be restarted. alternatively, we could +add code to fork synergy at startup; the child would do +the normal work while the parent would simply wait for the +child to exit and restart it. + +---------- +2002/06/02 23:07:57 crs +net/CTCPListenSocket.cpp + +shortened poll() timeout. + +---------- +2002/06/02 22:57:50 crs +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +net/CTCPSocket.cpp + +changed buffered output stream to wait() when flush()ing instead +of polling/sleeping. changed CTCPSocket to not use thread +cancellation but to instead use m_connected to exit the thread. +also shortened poll timeout. + +---------- +2002/06/02 21:35:20 crs +synergy/CMSWindowsScreen.cpp +synergy/CXWindowsScreen.cpp + +make sleep shorter in poll/sleep getEvent() loops. + +---------- +2002/06/02 21:03:38 crs +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsUtil.cpp + +removed poll/sleep code to improve performance. + +---------- +2002/06/02 19:04:24 crs +client/CXWindowsSecondaryScreen.cpp + +now ignores key if there's no key mapped for a required modifier. +was asserting (on the wrong expression). + +---------- +2002/06/02 18:49:35 crs +client/CClient.cpp +client/client.cpp +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +server/CServer.cpp +server/server.cpp + +added SIGINT and SIGTERM handling to unix client and server. +either signal causes the main thread to be cancelled. added +necessary code to make main thread cancellation clean up +nicely. + +---------- +2002/06/02 13:34:35 crs +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +server/CHTTPServer.cpp +server/CHTTPServer.h + +added a maximum request size to CHTTPProtocol so we can bail +on clients that cause us to use too much memory. also put +methods in CHTTPRequest to get/set headers and changed the +data structure used to store them. fixed a couple of other +miscellaneous bugs in CHTTPProtocol.cpp. + +---------- +2002/06/02 11:49:46 crs +mt/CCondVar.h +server/CServer.cpp +server/CServer.h + +now limiting number of simultaneous HTTP requests being handled +at once. this is to prevent denial of service. + +---------- +2002/06/01 19:26:11 crs +base/CLog.cpp +base/CLog.h +base/CString.h +base/XBase.h +base/base.dsp +base/common.h +base/stdfstream.h +base/stdistream.h +base/stdlist.h +base/stdmap.h +base/stdostream.h +base/stdpost.h +base/stdpre.h +base/stdset.h +base/stdsstream.h +base/stdvector.h +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.h +client/client.cpp +client/client.dsp +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/XHTTP.cpp +http/http.dsp +io/CStreamBuffer.h +io/io.dsp +mt/mt.dsp +net/net.dsp +server/CConfig.cpp +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/makehook.dsp +server/server.cpp +server/server.dsp +server/synrgyhk.dsp +synergy.dsw +synergy/CXWindowsClipboard.h +synergy/IPrimaryScreen.h +synergy/synergy.dsp + +fixes, mainly for windows. first, had to add a notification from +CServer to the primary screen when the configuration changes so it +can make necessary adjustments (the win32 primary screen must tell +the hook dll about the new jump zones). + +changed includes of some std c++ library files to go through +our own include files. these wrap the include with stuff to +keep vc++ quiet when compiling at warning level 4, which is +what it does now. it also works around missing and + on g++2.96. + +added missing std:: where necessary. g++ doesn't really support +namespaces so it lets references without the namespace slip +through. + +added workaround or fix. not sure if istringstream::str(string) +should reset eofbit. it does on g++ but does not on vc++. +added clear() after str() so it works either way. + +added low-level keyboard hook to win32. if available (it's only +available on NT SP3 and up) it allows us to catch and handle +alt+tab, alt+esc, ctrl+esc, and windows key hot keys. i think +that leaves only ctrl+alt+del and accessibility functions +uncaught on those systems. + +---------- +2002/06/01 10:52:02 crs +server/CServer.cpp +server/CServer.h +server/CServerProtocol1_0.cpp + +added mutex to all public methods that didn't already have it. +fixed two blown assertions. first, if user tried to switch to +a client that had connected but hadn't yet sent the first info +message it would assert on the zero size screen. second, if +the primary screen was handling a mouse motion on behalf of a +secondary screen when that secondary screen disconnected then +an assert would blow because the primary screen would call +onMouseMoveSecondary() but m_protocol on the active screen is +NULL because disconnecting the active secondary screen caused +the mouse to jump to the primary screen. + +---------- +2002/05/31 18:35:53 crs +server/CConfig.h + +changed iterator to use iterator_traits directly instead of +std::iterator to support the old STL on grace. + +---------- +2002/05/31 18:18:29 crs +client/CClient.cpp +client/CClient.h +server/CServer.cpp +synergy/ProtocolTypes.h +synergy/XSynergy.cpp +synergy/XSynergy.h + +server now rejects clients that are not in the configuration. +added a protocol message to indicate this. + +---------- +2002/05/31 18:09:43 crs +server/CServer.cpp +server/CServer.h +server/CServerProtocol1_0.cpp + +fixed setConfig() to disconnect secondary screens that aren't +in the new configuration. + +---------- +2002/05/31 18:08:08 crs +server/CConfig.cpp +server/CConfig.h + +made isScreen() a const method. + +---------- +2002/05/31 17:32:26 crs +server/CConfig.cpp +server/CConfig.h +server/server.cpp + +added I/O for configuration files and changed the server to use +an external file for its configuration (was hard coding a config +for testing). + +---------- +2002/05/31 14:44:54 crs +server/CConfig.cpp +server/CScreenMap.cpp +server/Makefile +server/server.dsp + +finished renaming CScreenMap to CConfig. + +---------- +2002/05/31 14:43:23 crs +server/CConfig.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CScreenMap.cpp +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/server.cpp + +checkpoint. changed CScreenMap to CConfig. must still change +CScreenMap.cpp to CConfig.cpp. + +---------- +2002/05/31 14:34:16 crs +server/CConfig.h +server/CHTTPServer.cpp +server/CScreenMap.cpp +server/CScreenMap.h +server/CServer.h +server/CSynergyHook.cpp +server/server.cpp + +checkpoint. renamed CScreenMap.h to CConfig.h. will be +changing CScreenMap to CConfig everywhere. + +---------- +2002/05/31 14:25:26 crs +base/CLog.cpp +base/CLog.h +client/client.cpp +server/server.cpp + +added methods to CLog for getting the outputter, getting and +setting the priority filter, and added code for thread safety. +added code to apps to enable thread safety in CLog. + +---------- +2002/05/30 16:13:16 crs +Makefile +base/common.h +http/CHTTPProtocol.cpp +http/CHTTPProtocol.h +http/Makefile +http/XHTTP.cpp +http/XHTTP.h +server/CHTTPServer.cpp +server/CHTTPServer.h +server/CScreenMap.cpp +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/Makefile + +added basic support for an embedded HTTP server. server +currently supports editing the screen map but changing +the map won't behave correctly if there are connected +screens. + +---------- +2002/05/30 16:11:59 crs +net/CTCPSocket.cpp +net/CTCPSocket.h + +fixed bug in closing down a socket. + +---------- +2002/05/27 18:55:51 crs +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h + +updated win32 clipboard to match new model. + +---------- +2002/05/27 18:35:14 crs +notes + +checkpoint + +---------- +2002/05/27 18:30:13 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsScreen.h +synergy/CXWindowsUtil.cpp +synergy/CXWindowsUtil.h + +removed getEventMask() from primary screen. added a class to +CXWindowsUtil that installs/uninstalls an X error hander. +using that in primary screen, clipboard, and util to ensure +that certain errors don't kill the app. + +---------- +2002/05/27 18:28:06 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h + +removed getEventMask() and fixed some comments. also now using +toggle key states in updateModifiers(). + +---------- +2002/05/27 17:05:34 crs +synergy/CXWindowsClipboard.cpp + +changed lesstif hack to only apply to the CLIPBOARD selection. +apprently the PRIMARY selection must follow the ICCCM protocol +correctly. + +---------- +2002/05/27 16:51:07 crs +synergy/CXWindowsUtil.cpp +synergy/CXWindowsUtil.h + +added missing files from previous submit. + +---------- +2002/05/27 16:22:59 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/Makefile + +checkpoint. changed clipboard model. the clipboard can only +be accessed now between open()/close(). ownership of the +clipboard is asserted via the empty() method. this parallels +the win32 model (but the win32 code hasn't been updated yet). + +refactored X11 clipboard code. moved the bulk of it into +CXWindowsClipboard and moved some comment event handling into +CXWindowsScreen. changed how requests are processed into a +hopefully easier to understand model. added support for getting +clipboard from and sending clipboard to motif (or at least +lesstif) clients. sending to lesstif required a hack to work +around an apparent bug in lesstif. + +---------- +2002/05/24 17:54:34 crs +notes + +checkpoint + +---------- +2002/05/24 17:54:28 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +added screen locking support to win32. added support for +resolution changing (only semi-supported on X because that +has no means for resizing screen anyway). also fixed some +clipboard problems on win32. + +---------- +2002/05/24 14:37:12 crs +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h + +added support for locking to a screen when the sroll lock is +toggled on or when any key or button is pressed. fully +implemented on X but stubbed out for now on win32. + +---------- +2002/05/23 18:35:15 crs +notes + +checkpoint + +---------- +2002/05/23 18:35:08 crs +server/CMSWindowsPrimaryScreen.cpp +server/CSynergyHook.cpp +server/CSynergyHook.h + +added support for mouse wheel on win32. + +---------- +2002/05/23 15:50:38 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp + +added support for mouse wheel on X. + +---------- +2002/05/23 15:00:39 crs +server/server.cpp + +added a third screen to hard coded map for testing purposes. + +---------- +2002/05/23 15:00:13 crs +server/CServer.cpp + +fixed log message. + +---------- +2002/05/23 14:56:03 crs +client/CClient.cpp +client/CClient.h +server/CServer.cpp +server/CServerProtocol.cpp +server/CServerProtocol1_0.cpp +synergy/ProtocolTypes.h +synergy/XSynergy.cpp +synergy/XSynergy.h + +server no longer asserts when a client connects with a name that's +already in use by another client. also added reporting of errors +from the server to clients so clients can report meaningful +messages to users. + +---------- +2002/05/23 14:04:43 crs +notes + +checkpoint + +---------- +2002/05/23 14:04:35 crs +net/CNetwork.h +synergy/CXWindowsScreen.h + +changed structs to classes. there should be no more structs now. + +---------- +2002/05/22 17:09:08 crs +notes + +checkpoint. + +---------- +2002/05/22 17:08:37 crs +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CProtocolUtil.cpp + +removed unnecessary call in screen class, added logging calls +in clipboard class, and added another cast in protocol util +to avoid warning on win32. + +---------- +2002/05/22 17:05:26 crs +server/CSynergyHook.cpp + +now letting some key events filter through. this allows the +keyboard lights to track toggle changes. however, it also +seems to let through keyboard events that shouldn't get +through. + +---------- +2002/05/22 17:02:58 crs +server/CScreenMap.cpp + +fixed incorrect for-loop over directions conditional. + +---------- +2002/05/22 17:01:17 crs +base/CLog.cpp +base/CLog.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h + +win32 changes. replaced log dialog hack with a windows console +window. now attaching thread input queues as necessary. shifted +code around so toggling toggle keys is immediately reflected by +secondary screen's keyboard. now setting extended key flag for +keys that need it. fixed handling of shift + caps-lock. added +handling of keys that should distinguish between left and right +but don't. fixed get/set of active window on leave/enter of +primary screen. replaced 1x1 primary window with a full screen +window to work around a problem with losing key events. changed +calculation of mouse move deltas. + +---------- +2002/05/22 16:56:06 crs +net/CTCPListenSocket.cpp + +fixed type of socket handle (from int to CNetwork::Socket). + +---------- +2002/05/22 16:55:19 crs +mt/CTimerThread.cpp + +removed blank line. + +---------- +2002/05/22 16:55:05 crs +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp + +changed un-inlined code to avoid bogus VC++ level 4 warnings. +added support for more win32 thread priorities. + +---------- +2002/05/22 16:51:59 crs +client/client.cpp + +fixed parameter type for socket port. + +---------- +2002/05/22 16:43:14 crs +base/common.h + +changed set of disabled win32 warnings. + +---------- +2002/05/22 16:42:48 crs +client/CClient.cpp + +fixed NULL dereference. + +---------- +2002/05/22 16:41:24 crs +base/common.h + +changed set of disabled win32 warnings. + +---------- +2002/05/22 16:40:51 crs +base/CLog.cpp +base/CLog.h + +replaced logging dialog hack with a windows console window. + +---------- +2002/05/22 16:40:38 crs +client/CClient.cpp + +fixed NULL dereference. + +---------- +2002/05/05 23:37:12 crs +net/CTCPSocket.cpp + +removed setting send buffer to zero size. it just reduced +performance. + +---------- +2002/05/05 19:52:03 crs +client/CXWindowsSecondaryScreen.cpp + +replaced True/False with true/false when assigning to m_repeat. +also should now work if the first element of a modifier +keymapping is 0. that won't normally be the case but xmodmap +was doing weird things on grace. if the first element is 0 +it'll try the second element. if that's also zero then that +modifier will be ignored. + +---------- +2002/05/05 19:38:09 crs +client/CMSWindowsSecondaryScreen.cpp +server/CMSWindowsPrimaryScreen.cpp + +fixes for win32 keyboard. + +---------- +2002/05/04 19:43:20 crs +client/CXWindowsSecondaryScreen.cpp + +fixed caps-lock handling. + +---------- +2002/05/04 18:33:48 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +checkpoint. added half duplex for num lock. + +---------- +2002/05/04 18:31:54 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h + +checkpoint. fixing up handling of half-duplex num-lock. + +---------- +2002/05/04 18:09:02 crs +client/CXWindowsSecondaryScreen.cpp + +checkpoint. changed when toggle keys toggle (now always on +release). must see if this works. + +---------- +2002/05/04 18:08:22 crs +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +net/CTCPSocket.cpp +server/CMSWindowsPrimaryScreen.cpp + +Fixes for win32 key handling. + +---------- +2002/05/04 11:23:11 crs +client/CXWindowsSecondaryScreen.cpp + +fixed handling of shift + caps-lock. those two modifiers should +cancel out if the keysym is subject to case conversion, but not +otherwise. also added logging of key lookup code. + +---------- +2002/05/03 12:23:48 crs +client/CXWindowsSecondaryScreen.cpp + +fixed handling of shift+tab on a system that can map ISO_Left_Tab. +now tries to map ISO_Left_Tab without shift first then falls back +to Tab (note that if ISO_Left_Tab can be mapped but requires a +modifier then the modifier will be added). also changed attempt +to map ISO_Left_Tab as a backup to Tab to request the shift +modifier whether or not the primary screen requested it. + +---------- +2002/05/03 12:14:55 crs +client/CXWindowsSecondaryScreen.cpp + +fixed handling of ISO_Left_Tab when that is not mapped to a +keycode by mapping it to tab with shift pressed. + +---------- +2002/05/03 11:49:30 crs +client/CXWindowsSecondaryScreen.cpp + +removed attempt to make release/press of a repeating key use +the same server time. was getting what appears to be deadlock +but not sure why. + +---------- +2002/05/03 11:26:44 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +checkpoint. made changes to support key autorepeats on X. + +---------- +2002/05/02 11:44:21 crs +synergy/COutputPacketStream.cpp + +Indentation change. + +---------- +2002/05/02 11:43:52 crs +io/CStreamBuffer.cpp +net/CTCPSocket.cpp + +Fixed bug in stream buffer that could cause data to be +inserted out of order. Also removed unnecessary limit +on writes to the TCP socket. + +---------- +2002/05/02 11:33:34 crs +io/CStreamBuffer.cpp + +checkpoint debugging of stream buffer. + +---------- +2002/05/01 16:30:20 crs +synergy/CXWindowsScreen.cpp + +Was trying to avoid sending clipboard if timestamp wasn't +changed but clipboard owners may not update that timestamp +when the selection is changed. Disabled the timestamp check. + +---------- +2002/05/01 16:17:57 crs +server/CServer.cpp +synergy/CXWindowsScreen.cpp + +Added more checks to avoid sending unchanged clipboard data. +Still takes too long to query the clipboard owner for info +(maybe 1/10th second) but not sure why or if that can be +improved. + +---------- +2002/05/01 15:31:47 crs +Make-linux +net/CNetwork.h +net/CTCPSocket.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +checkpoint. turned off nagle and send buffering. also +added test to skip clipboard conversion if a previous +conversion from that owner failed. + +---------- +2002/05/01 14:36:52 crs +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp + +Fixed uninitialized variable when computing toggle mask. Also +reduced priority of some mouse motion log messages. + +---------- +2002/05/01 14:35:55 crs +net/CSocketInputStream.cpp +net/CSocketInputStream.h +net/CSocketOutputStream.cpp +net/CSocketOutputStream.h +net/CSocketStreamBuffer.cpp +net/CSocketStreamBuffer.h +net/net.dsp + +removed obsolete files. + +---------- +2002/04/30 18:30:05 crs +client/CXWindowsSecondaryScreen.cpp + +added fallback for missing numpad movement keys (if there's no +mapping for those keys then the non-keypad versions are tried). + +---------- +2002/04/30 17:48:11 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +checkpoint. now sending toggle modifier state when entering +a screen. this allows the secondary screen to set it's +modifier state to match the primary screen's state. this is +not strictly necessary since each keystroke should adjust the +modifier state as needed to get the right result. + +---------- +2002/04/30 16:25:29 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h + +Added logging and handling of "half-duplex" caps-lock key. + +---------- +2002/04/30 16:23:30 crs +Make-linux +Make-solaris + +Changed name for auto-generated dependency files from +Makedepend to .depend. + +---------- +2002/04/30 16:23:03 crs +client/CClient.cpp +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServerProtocol1_0.cpp +server/server.rc +synergy/CClipboard.cpp +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/synergy.dsp + +Fixes to get win32 client and server up to date. + +---------- +2002/04/29 14:40:01 crs +base/CFunctionJob.h +base/CLog.h +base/CStopwatch.cpp +base/CStopwatch.h +base/CString.h +base/IInterface.h +base/IJob.h +base/TMethodJob.h +base/XBase.h +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +io/CBufferedInputStream.h +io/CBufferedOutputStream.h +io/CInputStreamFilter.h +io/COutputStreamFilter.h +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/XIO.h +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.h +mt/XThread.h +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CSocketInputStream.h +net/CSocketOutputStream.h +net/CSocketStreamBuffer.h +net/CTCPListenSocket.h +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +net/XNetwork.h +net/XSocket.h +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.h +server/CSynergyHook.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CClipboard.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.h +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CTCPSocketFactory.h +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h +synergy/XSynergy.h + +Indentation changes. + +---------- +2002/04/29 14:25:24 crs +client/CClient.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp + +Added some validation of protocol message parameters. + +---------- +2002/04/29 14:12:48 crs +synergy/CXWindowsScreen.cpp + +Shortened timeout on waiting for clipboard response. + +---------- +2002/04/29 14:08:48 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CServer.cpp +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +Made event selection a little more robust. Also fixed failure +to marshall clipboard data when updating primary clipboards. + +---------- +2002/04/29 13:49:56 crs +client/CXWindowsSecondaryScreen.cpp + +Added missing event mask. + +---------- +2002/04/29 13:31:44 crs +client/CClient.cpp +client/CClient.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +checkpoint. changed protocol to better handle clipboards. now +sending a sequence number with enter messages. screens use that +sequence number in clipboard grab and data messages. the server +uses the sequence number to order messages across clients. also +changed secondary screens to send clipboard updates on leaving +(or when grab occurs when not active) instead of on a query from +the server. primary effectively does the same. the query +message has been removed. + +---------- +2002/04/29 11:58:17 crs +client/CClient.cpp + +changed logging levels. + +---------- +2002/04/28 00:46:15 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +Clipboard improvements. Still not working right. Nedit +doesn't work at all but at least now there's a timeout to +prevent synergy from hanging waiting on a reply. + +---------- +2002/04/27 18:49:03 crs +base/CLog.cpp +base/CLog.h +client/CClient.cpp +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CTimerThread.cpp +net/CNetwork.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CProtocolUtil.cpp +synergy/CXWindowsScreen.cpp + +Added more debug levels and moved some annoying debug messages +to those levels. Default log level is now DEBUG for debug +builds and INFO for release builds. + +---------- +2002/04/27 18:06:40 crs +client/CClient.cpp +client/CXWindowsSecondaryScreen.cpp +server/CServer.cpp +server/CServerProtocol1_0.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CProtocolUtil.cpp +synergy/CXWindowsScreen.cpp +synergy/ProtocolTypes.h + +checkpoint. changed CProtocolUtil::readf() to store 1 and 2 +byte integers into pointers to 1 and 2 byte integers. was +always assuming pointers to 4 byte integers. + +---------- +2002/04/27 14:19:53 crs +client/CClient.cpp +client/CClient.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/ClipboardTypes.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ProtocolTypes.h + +Added support for multiple clipboards. This is mainly to +support both PRIMARY and CLIPBOARD selections on X windows. + +---------- +2002/04/27 14:19:19 crs +Makecommon + +set TARGETS macro to BIN and LIB targets. + +---------- +2002/04/26 20:15:59 crs +notes + +updated + +---------- +2002/04/26 20:14:46 crs +client/CXWindowsSecondaryScreen.cpp + +Fixed caps-lock and num-lock behavior. It seems to work okay +now but did notice one problem: when powerbook is primary and +num-lock is on the keypad works fine until shift is pressed +(and released); after that the keypad only works while the +shift key is down. + +---------- +2002/04/26 20:12:55 crs +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h + +Added hack to handle "half-duplex" caps-lock key on powerbook. +That key only reports press when pressed and released when +caps-lock is activated and only reports release when pressed +and released when caps-lock is deactivated. I don't know of a +way to detect this behavior so it may have to be configured by +the user. The code assumes normal behavior; will have to add +code to set the flag (perhaps from a user configuration). + +---------- +2002/04/26 17:38:01 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/KeyTypes.h + +changed processing of key events in X. secondary screen now +activates/deactivates modifiers as necessary to get a keycode +interpreted as the expected keysym. still some work and +testing to do on this. + +---------- +2002/04/25 10:44:01 crs +notes + +Added notes on keyboard handling. + +---------- +2002/04/25 10:43:53 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +added handling for DestroyNotify of clipboard requestors. + +---------- +2001/11/26 22:36:51 crs +synergy/CXWindowsScreen.cpp + +checkpoint. improvements to clipboard transfer on X windows. +not detecting a change to clipboard when synergy window isn't +the owner (since there's no event for this; we'll have to +check when we leave the screen i guess). large transfers +don't seem to work. + +---------- +2001/11/26 22:09:53 crs +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +checkpoint. testing clipboard transfer on X windows. + +---------- +2001/11/25 22:20:41 crs +client/CXWindowsSecondaryScreen.cpp +server/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h + +checkpoint. implementing clipboard owner in x windows. + +---------- +2001/11/25 18:44:13 crs +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h + +fixed function signature. + +---------- +2001/11/25 18:42:13 crs +Make-linux +Make-solaris +Makecommon +client/Makefile +server/Makefile + +executables are now built into a common area on unix (and they +already were on win32). + +---------- +2001/11/25 18:32:41 crs +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/client.cpp +client/client.dsp +net/CNetwork.cpp +notes +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/makehook.dsp +server/server.cpp +server/server.dsp +server/synrgyhk.dsp +synergy.dsw +synergy/CClipboard.cpp +synergy/CClipboard.h +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/Makefile +synergy/ProtocolTypes.h +synergy/synergy.dsp + +added platform independent clipboard transfer stuff +clipboard owner support (MS windows done, X windows partial) +added key transfer on ms windows +mutex fixes in CClient (had race conditions) +faster debug output in ms windows +changed temporary screen name to "secondary" +network fixes on ms windows (poll returned wrong result) +fixed transparent cursor on ms windows + +---------- +2001/11/19 00:33:36 crs +Make-linux +all.dsp +base/BasicTypes.h +base/CLog.cpp +base/CLog.h +base/XBase.cpp +base/base.dsp +base/common.h +client/CClient.cpp +client/CClient.h +client/CMSWindowsSecondaryScreen.cpp +client/CMSWindowsSecondaryScreen.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/Makefile +client/client.cpp +client/client.dsp +client/client.rc +client/resource.h +io/io.dsp +mt/CCondVar.cpp +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/mt.dsp +net/CNetwork.cpp +net/CNetwork.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/Makefile +net/XNetwork.cpp +net/XNetwork.h +net/net.dsp +notes +server/CMSWindowsPrimaryScreen.cpp +server/CMSWindowsPrimaryScreen.h +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/CSynergyHook.cpp +server/CSynergyHook.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile +server/makehook.dsp +server/resource.h +server/server.cpp +server/server.dsp +server/server.rc +server/synrgyhk.dsp +synergy.dsw +synergy/CMSWindowsClipboard.cpp +synergy/CMSWindowsClipboard.h +synergy/CMSWindowsScreen.cpp +synergy/CMSWindowsScreen.h +synergy/CXWindowsClipboard.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/synergy.dsp + +checkpoint. merging win32 code. server on X is currently broken +and client probably is. + +---------- +2001/11/18 23:14:28 crs +Makefile +client/CClient.cpp +client/CClient.h +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +client/Makefile +client/client.cpp +server/CScreenMap.cpp +server/CScreenMap.h +server/CServer.cpp +server/CServer.h +server/CServerProtocol.cpp +server/CServerProtocol.h +server/CServerProtocol1_0.cpp +server/CServerProtocol1_0.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +server/Makefile +server/server.cpp +synergy/CClient.cpp +synergy/CClient.h +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServer.h +synergy/CServerProtocol.cpp +synergy/CServerProtocol.h +synergy/CServerProtocol1_0.cpp +synergy/CServerProtocol1_0.h +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/Makefile +synergy/client.cpp +synergy/server.cpp + +moved client and server files into their own respective +directories. + +---------- +2001/11/13 23:34:12 crs +synergy/CServer.cpp +synergy/CXWindowsClipboard.cpp +synergy/CXWindowsClipboard.h +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/IClipboard.h +synergy/IPrimaryScreen.h +synergy/Makefile + +added preliminary support for getting the X selection. + +---------- +2001/11/11 21:27:36 crs +synergy/CServer.cpp + +fixed clamping when mapping to a different screen when beyond +bottom or right of source screen. + +---------- +2001/11/11 21:15:30 crs +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsScreen.cpp +synergy/CXWindowsScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/Makefile + +factored common X windows screen stuff into a common base class. + +---------- +2001/11/10 22:28:37 crs +notes + +updated notes. + +---------- +2001/11/10 22:28:30 crs +Makefile + +added main app directory to build. + +---------- +2001/10/25 22:17:17 crs +io/CBufferedInputStream.cpp +mt/CCondVar.cpp +mt/CMutex.cpp +mt/CThreadRep.cpp +net/CNetworkAddress.cpp +net/CSocketInputStream.cpp +net/CTCPListenSocket.cpp +net/CTCPSocket.cpp +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsSecondaryScreen.cpp + +removed unnecessary global scoping operators. + +---------- +2001/10/25 22:09:27 crs +synergy/CXWindowsSecondaryScreen.cpp + +changed hider window to move underneath mouse when leaving the +screen. this makes it so if the mouse is moved locally, it'll +reappear where it was last seen. + +---------- +2001/10/25 21:40:29 crs +synergy/CClient.cpp +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/ISecondaryScreen.h + +changed some method names and removed warpCursor() from +secondary screen interface. + +---------- +2001/10/24 23:29:29 crs +synergy/CServer.cpp +synergy/CServer.h + +now handling disconnect of secondary screen that has the cursor +by jumping back to the primary screen (without trying to notify +the now disconnected secondary screen). also fixed blown assert +in mapPosition(). + +---------- +2001/10/24 22:33:24 crs +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h + +made calls to X thread safe. + +---------- +2001/10/23 22:45:59 crs +notes + +more notes. + +---------- +2001/10/23 22:41:46 crs +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h + +added cursor hiding. + +---------- +2001/10/23 21:23:29 crs +base/CLog.cpp + +can now filter logging by level. + +---------- +2001/10/23 21:13:08 crs +synergy/CServer.cpp + +fixed blown assert trying to find neighbor when there was none. + +---------- +2001/10/21 00:21:21 crs +synergy/CClient.cpp + +fixed handling of stream ownership. + +---------- +2001/10/21 00:21:02 crs +io/CBufferedInputStream.cpp +io/CBufferedOutputStream.cpp +mt/CThreadRep.cpp +net/CTCPSocket.cpp +net/CTCPSocket.h +synergy/CServer.cpp +synergy/server.cpp + +fixed bugs in handling streams. + +---------- +2001/10/20 20:43:31 crs +Make-linux +mt/CThreadRep.cpp + +threading fixes. had sigmask set in wrong place. was setting +m_exit flag potentially after the object had been destroyed. +most importantly, RTTI must be enabled on PPC to avoid SIGILL. + +---------- +2001/10/14 19:16:54 crs +mt/CThread.cpp +mt/CThreadRep.cpp +mt/CTimerThread.cpp + +some debugging code. + +---------- +2001/10/14 18:29:43 crs +base/CLog.h +mt/CMutex.cpp +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.cpp +synergy/CClient.cpp +synergy/CClient.h +synergy/CProtocolUtil.cpp +synergy/CServerProtocol1_0.cpp +synergy/client.cpp +synergy/server.cpp + +fixed timeout bug in CThreadRep::wait() (negative timeout wouldn't +wait forever). also fixed early return from sleep due to signal. +now forcing client to initialize CThread to ensure global mutex +gets initialized before threads are used. + +---------- +2001/10/14 16:58:01 crs +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/CInputStreamFilter.h +io/COutputStreamFilter.cpp +io/COutputStreamFilter.h +io/CStreamBuffer.cpp +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CSocketInputStream.cpp +net/CSocketInputStream.h +net/CSocketOutputStream.cpp +net/CSocketOutputStream.h +net/CSocketStreamBuffer.cpp +net/CSocketStreamBuffer.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/COutputPacketStream.cpp +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServer.h +synergy/CServerProtocol.cpp +synergy/CServerProtocol.h +synergy/CServerProtocol1_0.cpp +synergy/CServerProtocol1_0.h +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h + +removed exception specifications. thread exceptions weren't +being listed and they'd have to be added to every one. just +doesn't seem worth the trouble. + +---------- +2001/10/14 14:56:06 crs +synergy/CProtocolUtil.cpp + +stupid bug fixes. writef() used the wrong variable as the number +of bytes to write. readf() forgot to prepare the va_list. + +---------- +2001/10/14 14:38:45 crs +base/CLog.cpp +base/CLog.h + +forgot to add the logger files. + +---------- +2001/10/14 14:37:41 crs +Make-linux +base/Makefile +synergy/CClient.cpp +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServerProtocol1_0.cpp +synergy/CXWindowsPrimaryScreen.cpp + +added logging facility and added a bunch of log messages. + +---------- +2001/10/08 19:24:46 crs +Makefile +notes +synergy/CClient.cpp +synergy/CClient.h +synergy/CServer.cpp +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsSecondaryScreen.cpp +synergy/CXWindowsSecondaryScreen.h +synergy/ISecondaryScreen.h +synergy/Makefile +synergy/client.cpp +synergy/server.cpp + +checkpoint. first cut of client and server apps. not tested +yet but they compile and *should* work as is. + +---------- +2001/10/06 14:18:01 crs +Make-linux +Makefile + +updated old files to new implementation + +---------- +2001/10/06 14:13:28 crs +BasicTypes.h +CClient.cpp +CClient.h +CEvent.h +CEventQueue.cpp +CEventQueue.h +CMessageSocket.cpp +CMessageSocket.h +CProtocol.h +CScreenProxy.cpp +CScreenProxy.h +CServer.cpp +CServer.h +CSocket.cpp +CSocket.h +CSocketFactory.cpp +CSocketFactory.h +CString.h +CTrace.cpp +CTrace.h +CUnixEventQueue.cpp +CUnixEventQueue.h +CUnixTCPSocket.cpp +CUnixTCPSocket.h +CUnixXScreen.cpp +CUnixXScreen.h +CXScreen.cpp +CXScreen.h +IClient.h +IClipboard.h +IEventQueue.h +IJob.h +IScreen.h +IServer.h +ISocket.h +KeyTypes.h +Make-linux +Make-solaris +Makecommon +Makefile +MouseTypes.h +TMethodJob.h +XBase.cpp +XBase.h +XSocket.h +base/BasicTypes.h +base/CFunctionJob.cpp +base/CFunctionJob.h +base/CStopwatch.cpp +base/CStopwatch.h +base/CString.h +base/IInterface.h +base/IJob.h +base/Makefile +base/TMethodJob.h +base/XBase.cpp +base/XBase.h +base/common.h +io/CBufferedInputStream.cpp +io/CBufferedInputStream.h +io/CBufferedOutputStream.cpp +io/CBufferedOutputStream.h +io/CInputStreamFilter.cpp +io/CInputStreamFilter.h +io/COutputStreamFilter.cpp +io/COutputStreamFilter.h +io/CStreamBuffer.cpp +io/CStreamBuffer.h +io/IInputStream.h +io/IOutputStream.h +io/Makefile +io/XIO.cpp +io/XIO.h +main.cpp +mt/CCondVar.cpp +mt/CCondVar.h +mt/CLock.cpp +mt/CLock.h +mt/CMutex.cpp +mt/CMutex.h +mt/CThread.cpp +mt/CThread.h +mt/CThreadRep.cpp +mt/CThreadRep.h +mt/CTimerThread.cpp +mt/CTimerThread.h +mt/Makefile +mt/XThread.h +net/CNetworkAddress.cpp +net/CNetworkAddress.h +net/CSocketInputStream.cpp +net/CSocketInputStream.h +net/CSocketOutputStream.cpp +net/CSocketOutputStream.h +net/CSocketStreamBuffer.cpp +net/CSocketStreamBuffer.h +net/CTCPListenSocket.cpp +net/CTCPListenSocket.h +net/CTCPSocket.cpp +net/CTCPSocket.h +net/IListenSocket.h +net/ISocket.h +net/Makefile +net/XSocket.cpp +net/XSocket.h +notes +synergy/CClient.cpp +synergy/CClient.h +synergy/CInputPacketStream.cpp +synergy/CInputPacketStream.h +synergy/COutputPacketStream.cpp +synergy/COutputPacketStream.h +synergy/CProtocolUtil.cpp +synergy/CProtocolUtil.h +synergy/CScreenMap.cpp +synergy/CScreenMap.h +synergy/CServer.cpp +synergy/CServer.h +synergy/CServerProtocol.cpp +synergy/CServerProtocol.h +synergy/CServerProtocol1_0.cpp +synergy/CServerProtocol1_0.h +synergy/CTCPSocketFactory.cpp +synergy/CTCPSocketFactory.h +synergy/CXWindowsPrimaryScreen.cpp +synergy/CXWindowsPrimaryScreen.h +synergy/IPrimaryScreen.h +synergy/ISecondaryScreen.h +synergy/IServerProtocol.h +synergy/ISocketFactory.h +synergy/KeyTypes.h +synergy/Makefile +synergy/MouseTypes.h +synergy/ProtocolTypes.h +synergy/XSynergy.cpp +synergy/XSynergy.h +test.cpp + +Started over. + +---------- +2001/05/14 21:14:49 crs +MouseTypes.h + +flipped order of buttons to match default X setup. + +---------- +2001/05/14 21:14:25 crs +CClient.cpp +CEvent.h +CScreenProxy.cpp +CScreenProxy.h +CServer.cpp +CXScreen.cpp +CXScreen.h +IScreen.h +KeyTypes.h + +added other mouse and key event handling to CXScreen. key repeat +isn't implemented and modifier masks are ignored. modifier masks +are new; they indicate the modifier key (shift, ctrl, etc) state +at the time of the key event. + +---------- +2001/05/13 12:43:16 crs +CUnixTCPSocket.cpp +CUnixTCPSocket.h + +more fixes to reduce latency. nagle agorithm doesn't seem to +stay off on a socket on linux because a connection clearly +doesn't send data as often as possible. will have to implement +a UDP socket to reduce overhead and avoid these delays. wanted +to do that anyway. + +---------- +2001/05/13 12:21:11 crs +CUnixTCPSocket.cpp +CXScreen.cpp + +fixes to avoid update delays. + +---------- +2001/05/13 12:07:32 crs +CMessageSocket.cpp + +fixed bug in read() that miscalculated the message length. + +---------- +2001/05/13 11:40:29 crs +BasicTypes.h +CClient.cpp +CClient.h +CEvent.h +CEventQueue.cpp +CEventQueue.h +CMessageSocket.cpp +CMessageSocket.h +CProtocol.h +CScreenProxy.cpp +CScreenProxy.h +CServer.cpp +CServer.h +CSocket.cpp +CSocket.h +CSocketFactory.cpp +CSocketFactory.h +CString.h +CTrace.cpp +CTrace.h +CUnixEventQueue.cpp +CUnixEventQueue.h +CUnixTCPSocket.cpp +CUnixTCPSocket.h +CUnixXScreen.cpp +CUnixXScreen.h +CXScreen.cpp +CXScreen.h +IClient.h +IClipboard.h +IEventQueue.h +IJob.h +IScreen.h +IServer.h +ISocket.h +KeyTypes.h +Make-linux +Makefile +MouseTypes.h +TMethodJob.h +XBase.cpp +XBase.h +XSocket.h +main.cpp +tools/depconv + +initial revision of synergy. currently semi-supports X windows +on unix, but client screens don't simulate events other than +mouse move. also not supporting clipboard at all yet and the +main app is just a temporary framework to test with. must +clean up protocol and communication. + +---------- diff --git a/FAQ b/FAQ new file mode 100644 index 00000000..270f0f74 --- /dev/null +++ b/FAQ @@ -0,0 +1,9 @@ +Frequently Asked Questions about synergy. + +This will be added to as questions come in. + +1. ctrl+alt+del doesn't work on secondary screens. + Synergy isn't able to capture ctrl+alt+del on PC compatible + systems because it's handled completely differently than + other keystrokes. If you need to use ctrl+alt+del you'll + have to keep your other keyboards handy just for that. diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..74e02ea8 --- /dev/null +++ b/INSTALL @@ -0,0 +1,352 @@ +Installation instructions for synergy. + +Prerequisites for building +-------------------------- + +To build synergy from the sources you'll need the following: + + Windows: + * VC++ 6.0 or up + + Linux: + * gcc 2.95 (or up?) + * X11R4 or up headers and libraries + +If building from CVS on Linux you'll also need: + * autoconf 2.52 (older versions may work) + * automake 1.5 (older versions may work) + + +Configuring the build +--------------------- + +This step is only necessary when building on Linux. + +If you're building synergy from CVS you need to create the configure +script and the Makefile.in files. To do that: + + % aclocal + % autoheader + % autoconf + % automake + +To configure the build for your platform use the configure script: + + % ./configure + +For a list of options to configure use: + + % ./configure --help + + +Building +-------- + +Windows: + Start VC++ and open `synergy.dsw'. Set the active configuration + (Build | Set Active Configuration) to `All - Debug' or `All - Release'. + Then build. + + VC++ 6.0 cannot handle project files that lack \r\n newlines. If you + retrieved synergy via CVS then the files should be fine. But if the + workspace appears to be empty then close VC++ and add \r to each line. + A convenient way to do that is to get `fixcrlf.exe' from the Internet, + search the synergy files for `*.dsp' and drag-and-drop all the project + files onto fixcrlf.exe. + + Binaries are built into ./Debug or ./Release. + +Linux: + Simply enter: + + % make + + This will build the client and server and leave them in their + respective source directories. + + +Installing +---------- + +Windows: + There is no support for creating an installer for synergy or installing + the files in a particular location. The only requirement for installed + files is that synrgyhk.dll is in the same directory as synergyd.exe or + in one of the system directories. + +Linux: + % make install + + will install the client and server into /usr/local/bin unless you + specified a different directory when you ran configure. + +See `Starting Automatically' for details on how to have synergy start up +automatically when the computer starts. + + +Configuring the Server +---------------------- + +The synergy server requires configuration. The configuration file is a +plain text file broken into sections. Each section has the form: + + section: + + end + +Comments are introduced by `#' and continue to the end of the line. +The file can have the following sections. + + * screens + is a list of screen names, one name per line, each + followed by a colon. Names are arbitrary strings but they + must be unique. The hostname of each computer is recommended. + There must be a screen name for the server and each client. + + Example: + + section: screens + moe: + larry: + curly: + end + + This declares three screens named: moe, larry, and curly. + + * links + is a list of screen names just like in the `screens' + section except each screen is followed by a list of links. + Each link has the form ` = '. + A link indicates which screen is adjacent in the given + direction. + + Example: + + section: links + moe: + right = larry + up = curly + larry: + left = moe + up = curly + curly: + down = larry + end + + This indicates that screen `larry' is to the right of screen + `moe' (so moving the cursor off the right edge of moe would + make it appear at the left edge of larry), `curly' is above + 'moe', `moe' is to the left of `larry', `curly' is above + `larry', and `larry' is below `curly'. Note that links do + not have to be symmetrical; moving up from moe then down + from curly lands the cursor on larry. + + * aliases + is a list of screen names just like in the `screens' + section except each screen is followed by a list of aliases, + one per line *not* followed by a colon. An alias is a + screen name and must be unique. During screen name lookup + each alias is equivalent to the screen name it aliases. So + a client can connect using its canonical screen name or any + of its aliases. + + Example: + + section: aliases + larry: + larry.stooges.com + curly: + shemp + end + + Screen `larry' is also known as `larry.stooges.com' and can + connect as either name. Screen `curly' is also known as + `shemp'. (Hey, it's just an example.) + +The synergy server will try certain pathnames to load the configuration +file if the user doesn't specify a path using the `--config' command +line option. `synergyd --help' reports those pathnames. + + +Running the Server +------------------ + +Run the server on the computer that has the keyboard and mouse to +be shared. You must have prepared a configuration file before +starting the server. The server should be started before the +clients but that's not required. + +Run the synergy server on the server system using the following +command line: + + synergyd -f --config + +Replace with the path to the configuration file. +The `-f' option causes synergyd to run in the foreground. This is +recommended until you've verified that the configuration works. +If you didn't include the system's hostname in the configuration +file (either as a screen name or an alias) then you'll have to add +`--name ' to the command line, where is +a name in the configuration file. You can use `synergyd --help' +for a list of command line options. + + +Running the Client +------------------ + +Run the client on all computers that aren't the server using the +following command line: + + synergy -f --no-camp + +Replace with the hostname or address of the +server system. The `-f' option causes synergy to run in the +foreground. The `--no-camp' prevents synergy from retrying to +connect to the server until it succeeds. Both are recommended +until you've verified that the configuration works. If you +didn't include the system's hostname in the configuration file +(either as a screen name or an alias) then you'll have to add +`--name ' to the command line, where +is a name in the configuration file. + +The client should quickly report `connected to server'. If it +does not but doesn't print an error and exit immediately then +it's trying to connect to the server but cannot. It will time +out in 30 seconds and exit (use ctrl+c to exit earlier). You +should check that the server is running and is reachable over +the network and try again. + +If the client fails and exits it should print an error describing +the problem. Here are typical problems and possible solutions: + + * failed to open screen: + check permission to open the X display; + check that the DISPLAY environment variable is set. + * already connected: + check that synergy isn't already running. + * refused client: + add client to the server's configuration file. + * connection failed: + check ; + the server cannot open the desired port, stop the + program using that port (24800) and restart the + server. + +Once all the clients are running, try moving the mouse to each +screen. Be sure to check all the configured links. + + +Starting Automatically +---------------------- + +You can setup synergy to start automatically when your computer does. + +Windows: + Start the client or server normally except add `--install' as the + first option on the command line, followed by the usual options + except do not include `-f' or `--no-daemon'. This will install + synergy as a service that will be started at system boot. + + On the Windows NT family you can start and stop the service at any + time using the Services control panel (under Administrative Tools + on Windows 2000 and XP). On the Windows 95 family you cannot + start or stop the service. + + To uninstall the service, run the client or server with just the + `--uninstall' command line option. + +Linux: + Synergy requires an X server. That means a server must be + running and synergy must be authorized to connect to that server. + It's best to have the display manager start synergy. You'll need + the necessary (probably root) permission to modify the display + manager configuration files. If you don't have that permission + you can start synergy after logging in via the .xsession file. + + To have the display manager start synergy, edit the Xsetup script. + The location of this file depends on your installation. It might + be /etc/X11/xdm/Xsetup. Near the end of the file but before + anyplace the script calls exit, start the client with something + like: + + /usr/bin/killall synergy + /usr/local/bin/synergy [] + + must not include `-f' or `--no-daemon'. Change the + paths as necessary. It's important to make sure no old copies + of synergy are running so they can't interfere with the new one. + + To start the server use something like: + + /usr/bin/killall synergyd + /usr/local/bin/synergyd [] --config + + Again, must not include `-f' or `--no-daemon'. If + the configuration pathname is one of the default locations then + you don't need the `--config' option. + + Note that some display managers (xdm and kdm, but not gdm) grab + the keyboard and do not release it until the user logs in, for + security reasons. This prevents a synergy server from sharing + the mouse and keyboard until the user logs in. It doesn't + prevent a synergy client from synthesizing mouse and keyboard + input, though. + + +Common Command Line Options +--------------------------- + -d, --debug use debugging level + --daemon run as a daemon (Linux) or background (Windows) + -f, --no-daemon run in the foreground + -n, --name use instead of the hostname + --restart automatically restart on failures + -1, --no-restart do not restart on failure + -h, --help print help and exit + --version print version information and exit + --install install as a service (Windows only) + --uninstall uninstall service (Windows only) + +Debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, +INFO, DEBUG, DEBUG1, and DEBUG2. Only messages at or above the given +level are logged. Messages are logged to a terminal window when +running in the foreground. Unix logs messages to syslog when running +as a daemon. The Windows NT family logs messages to the event log +when running as a service. The Windows 95 family shows FATAL log +messages in a message box and others in a terminal window when running +as a service. + +The `--name' option lets the client or server use a name other than +its hostname for its screen. This name is used when checking the +configuration. + +Neither the client nor server will automatically restart if an error +occurs that is sure to happen every time. For example, the server +will exit immediately if it can't find itself in the configuration. +On X11 both the client and server will also terminate if the +connection to the X server is lost. Since xdm will normally restart +the server and synergy, this is the correct behavior. + + +Server Command Line Options +--------------------------- + -a, --address
listen for connections on the given address + -c, --config read configuration from + +
has one of the following forms: + + : + : + is a hostname or address of a network interface on the +server system. is a port number from 1 to 65535. +defaults to the system's hostname and defaults to 24800. + + +Client Command Line Options +--------------------------- + --camp retry connection to server until successful + --no-camp try connection to server only once +
address of server + +see the "server command line options" for a description of
+but note that there is no default though there is a +default . diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..7fdcb34b --- /dev/null +++ b/NEWS @@ -0,0 +1,5 @@ +News about the synergy project. + +* Initial version of synergy released. Supports ASCII keys, 5 button + wheel mouse, Unicode text clipboard, and screen saver synchronization. + Runs on Linux and Microsoft Windows. diff --git a/README b/README index 847c914f..b2f6ab2b 100644 --- a/README +++ b/README @@ -1,427 +1,197 @@ -synergy -------- +Synergy +======= + synergy: [noun] a mutually advantageous conjunction of distinct elements -synergy lets you easily share a single mouse and keyboard between -multiple computers, each with its own display, using software only. -redirecting the mouse and keyboard is as simple as moving the mouse -off the edge of your screen. synergy merges the clipboards of all -the systems into one, allowing cut-and-paste between systems. it -also synchronizes screensavers so they all start and stop together -and, if screen locking is enabled, only one screen requires a -password to unlock them all. +Synergy lets you easily share a single mouse and keyboard between +multiple computers with different operating systems, each with its +own display, without special hardware. It's intended for users +with multiple computers on their desk since each system uses its +own display. + +Redirecting the mouse and keyboard is as simple as moving the mouse +off the edge of your screen. Synergy also merges the clipboards of +all the systems into one, allowing cut-and-paste between systems. +Furthermore, it synchronizes screen savers so they all start and stop +together and, if screen locking is enabled, only one screen requires +a password to unlock them all. + +Synergy is open source and released under the GNU Public License (GPL). + +The synergy home page is: +FIXME + +The synergy project page is: +FIXME + +Report bugs to: +synergy@groundhog.pair.com -system requirements +Please see the following files for more information: +AUTHORS -- The list of synergy's authors +BUGS -- A list of known bugs and limitations +COPYING -- The license synergy is release under +INSTALL -- Detailed build and installation instructions +NEWS -- News about the synergy project +PORTING -- Porting guide for developers + + +System Requirements ------------------- -all systems: - keyboard - mouse - TCP/IP networking -Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family): - ??? MB RAM +* All operating systems: + keyboard, + mouse, + TCP/IP networking; -Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family): - ??? MB RAM +* Microsoft Windows 95, Windows 98, Windows Me (the Windows 96 family); -Linux, Unix: - ??? MB RAM - X Windows, revision 4 or up with the XTEST extension - use `xdpyinfo | grep XTEST' to check +* Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family); + +* Linux: + X Windows version 11 revision 4 or up with the XTEST extension + (use `xdpyinfo | grep XTEST' to check for XTEST). -manifest --------- - linux windows - ----- ------- - README README this file - synergy synergy.exe the synergy client - synergyd synergyd.exe the synergy server - synrgyhk.dll the synergy hook dll - synergy.conf synergy.conf sample configuration file +Installation +------------ + +See INSTALL for detailed build and installation instructions. -running synergy +Quick Start +----------- +Synergy lets you use one keyboard and mouse across multiple computers. +To do so it requires that all the computers are connected to each other +via TCP/IP networking. Most systems come with this installed. + +The first step is to pick which keyboard and mouse you want to share. +The computer with that keyboard and mouse is called the "primary +screen" and it runs the synergy server. All of the other computers +are "secondary screens" and run the synergy client. The Windows NT +family, starting with NT 4 with service pack 3, is the best choice +for the primary screen but Linux is good too. (This is based on the +known bugs; see BUGS for more details.) + +Next you must install the software. You must install the synergy +server on the primary screen and the synergy client on all the +secondary screens. If it's easier just install both on all of the +systems. + +Third, you create a configuration file for the server. Only the +server requires configuration. Create a text file named +synergy.conf with the following: + + section: screens + : + : + end + section: links + : + right = + : + left = + end + +Replace each occurrence of `' with the host name of the +primary screen computer and `' with the host name of a +secondary screen computer. In the above example, is to +the right of and is to the left of . +If necessary you should replace `right' and `left' with `left', +`right', 'up', or `down'. If you have more than two computers +you can add those too: add each computer's host name in the +`screens' section and add the appropriate links. + +Finally, you start the server then all of the clients. Normally +synergy wants to run "in the background." It detaches from the +terminal and doesn't have a visible window, effectively +disappearing from view. Until you're sure your configuration +works you should start synergy "in the foreground" using the `-f' +command line option. + +To get a command line on Windows, choose Run... from the Start +menu. Type `cmd' if you're using the Windows NT family or +`command' if you're using the Windows 95 family and press enter. + +To start the server, enter the following on the command line: + + synergyd -f --config synergy.conf + +To start a client, enter the following: + + synergy -f + +where `' is replaced by the name of the computer +running the synergy server. + +Both the client and server should immediately report the connection +or an error. If successful, you should now be able to move the +mouse off the appropriate edge of your server's screen and have it +appear on the client's screen. Use the mouse and keyboard normally +except use the edge of the screens to jump to other screens. You +can also cut-and-paste across computers. Currently, only text +transfers between computers. Start the remaining clients. + +Be aware that not all keystrokes can be handled by synergy. In +particular, ctrl+alt+del is not handled. You cannot use synergy +to log into a Windows NT family system that requires the user to +press ctrl+alt+del to log on. You'll need to keep that computer's +keyboard handy in order to log on. + +Once the configuration is verified, see the instructions in INSTALL +under `Starting Automatically' for details on running synergy in +the background and on starting synergy automatically when you start +your computers. + + +Tips and Tricks --------------- -synergy is simple to configure. the server uses a configuration file -and command line options while the client uses only command line -options. it's recommended that both the client and server be run in -the foreground until the configuration is verified to work. - -step 1: create a configuration file - edit the sample configuration file. there are two sections you - must fill in and a third optional section. you should delete - the existing lines inside the sections. - - in the "screens" section, add a line for each computer you'll - be using (server and clients). put the hostname of the computer - followed by a colon (with no space in between). the computers - can be listed in any order. - - in the "links" section you define how screens are connected. - each screen is listed as in the "screens" section except - following each screen is a list of links to other screens in - the form " = " where is "left", - "right", "up", or "down" and is a screen listed in - the "screens" section. - - as an example, if we have "left=foo" under the "bar" screen - then screen "foo" is on the left of screen "bar". the user - will be able to move the mouse off the left edge of "foo" and - will appear on the opposite (right) edge of "bar". note that - it's entirely possible to have one-way (asymmetric) links and - screens with only links into them. the latter should be - avoided since there's no way to move the mouse off those - screens. - - in the "aliases" section you can list other names for each - screen. this is especially useful for dealing with fully - qualified domain names versus simple hostnames. - -step 2: start the server - the server is the system with the mouse and keyboard to be - shared. each platform has its own tradeoffs when running as - the server. see the release notes below for more information. - - run the synergy server on the server system using the following - command line: - - synergyd -f --config - - replacing with the path to the configuration - file. you can use `synergyd --help' for a list of command line - options. - -step 3: start the clients - on each client system start the synergy client using the - following command line: - - synergy -f --debug INFO --no-camp - - replacing with the hostname or address of the - server system. - - the client should quickly report `connected to server'. if it - does not but doesn't print an error and exit immeditately then - it's trying to connect to the server but cannot. it will time - out in 30 seconds and exit (use ctrl+c to exit earlier). you - should check that the server is running and try again. - - otherwise, if the client doesn't connect it should print an - error describing the problem. here are typical problems and - possible solutions: - - failed to open screen: - check permission to open the X display; - check that the DISPLAY environment variable is set. - already connected: - check that synergy isn't already running. - refused client: - add client to the server's configuration file. - connection failed: - check server-hostname; - the server cannot open the desired port, stop the - program using that port (24800) and restart the - server. - -step 4: verify the configuration - once the clients are connected, use the mouse to check that - the screens are properly linked. moving the mouse off the - edge of a screen with a link should cause it to appear on - the opposite edge of the linked-to screen. - - -using synergy -------------- -using synergy is very easy. once clients have connected to the -server all you do to redirect keyboard and mouse input to a screen -(i.e. switch screens) is move the mouse cursor off the edge of the -screen you're on. which edges go to which screens depends on the -configuration. - -clients can be connected and disconnected at any time. until a -client is connected, switching to it works as if you switched to -it then moved all the way across it in the same direction and -switched to the next screen. this repeats until you reach a -connected screen. if there is no connected screen in that -direction then the mouse will not leave the starting screen. - -disconnecting a client while the mouse is on it causes the mouse -to instantly jump to the center of the server screen. - -the clipboard is automatically transferred between screens. if -you copy on one screen you just switch to another screen and paste. -note that X Windows has two major clipboards: the primary -selection and the clipboard. synergy supports both. however, -Microsoft Windows only supports the clipboard. the Windows -clipboard is transferred to both the X primary selection and the -clipboard. whichever X clipboard was changed last becomes the -Windows clipboard. end-of-line sequences (LF on linux and unix, -CRLF on Windows) are automatically converted as necessary. the -clipboards are transferred using Unicode; if your platforms and -applications understand Unicode then you should be able to cut -and paste any Unicode character. - -synergy synchronizes screensavers. the screensavers on client -screens are disabled when they connect to the server. when the -primary screen's screensaver starts, the screensaver on each -secondary screen starts too. all the secondary screensavers are -stopped when the primary screensaver stops. moving the mouse or -pressing a key will stop the primary screensaver, regardless of -which screen the mouse was on when the screensavers started. if -the primary screensaver requires a password to unlock then the -user is prevented from switching to the secondary screens until -the primary screen is unlocked. - - -installing as a daemon/service ------------------------------- -synergy can run in the foreground or as a daemon/service. it's -recommended that you run it in the foreground until you've sorted -out your configuration. - -on the Windows NT family you cannot run a service directly. -instead you install the service then run or stop it via the -Services control panel. on the Windows 95 family, you can use -the `--daemon' command line option to start synergy as a service -or you can install the service and restart your computer. - -in the text below, except where noted, synergy refers to the -client and/or the server. - -windows: - to install synergy just run one of the following: - - synergy --install [other command line options] - synergyd --install [other command line options] - - the client/server is installed as a service and the command - line is saved and used when starting the service. the system - will expect to find the program wherever it was when you used - the --install option so make sure it's not on a network share - from another system because the network share will not be - available at boot time. synergyd will also try to load - synrgyhk.dll so that should be in the same directory as - synergyd.exe. - - note that when installing the client you must provide the - server hostname argument. to change the arguments you must - first uninstall then reinstall. - - you must also install the configuration file along with the - server. it's recommended that you put it in the windows - directory (e.g. C:\WINNT) and call it "synergy.sgc". the - server will automatically find this file. however, you can - also use the --config command line option and specify an - *absolute* path to the file. remember that this file must be - accessible when the system starts up, before network shares - are mapped. - - to uninstall use: - - synergy --uninstall - synergyd --uninstall - -linux, unix: - before starting synergy as a daemon you should understand that - synergy requires an X server that it can connect to. synergy - can start before the X server does and will repeatly attempt to - connect to the X server until it succeeds. however, if the - server requires authorization then it's unlikely that synergy - will ever succeed. so, in general, synergy should be (re)started - by the X display manager. - - some display managers (xdm and kdm, but not gdm) grab the - keyboard and do not release it until the user logs in, also - for security reasons. this prevents a synergy server from - sharing the mouse and keyboard until the user logs in but - it doesn't prevent a synergy client from synthesizing mouse - and keyboard input. - - you should modify xdm's Xsetup script to start the synergy - client or server. for example, somewhere near the bottom of - Xsetup (but someplace before anywhere the script calls exit) - you might add: - /usr/bin/killall synergy - /usr/sbin/synergy 192.168.1.101 - this assumes synergy is installed in /usr/sbin. these lines - make sure any already running synergy is terminated and starts - a fresh copy. it's important to kill old copies so that you - don't end up with multiple synergy instances fighting each - other or, at the very least, using up system resources. - - to start the synergy server you might use: - /usr/bin/killall synergyd - /usr/sbin/synergyd --config /root/synergy.conf - assuming synergyd is installed in /usr/sbin. if you've put - the configuration data in /etc/synergy.conf then you don't - need the --config option. - - another option is to put the synergy startup in .Xsession in - your home directory. that allows users without root access to - start synergy when they login. in this case synergy will not - be running while on the login screen. - - -common command line options ---------------------------- - -d, --debug use debugging level - --daemon run as a daemon (linux,unix) or background (windows) - -f, --no-daemon run in the foreground - -n, --name use instead of the hostname - --restart automatically restart on failures - -1, --no-restart do not restart on failure - -h, --help print help and exit - --version print version information and exit - --install install as a service (windows) - --uninstall uninstall service (windows) - -debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, -INFO, DEBUG, DEBUG1, and DEBUG2. only messages at or above the given -level are logged. messages are logged to a terminal window when -running in the foreground. unix logs messages to syslog when running -as a daemon. the Windows NT family logs messages to the event log -when running as a service. the Windows 95 family shows FATAL log -messages in a message box and others in a terminal window when running -as a service. - -the `--name' option lets the client or server use a name other than -its hostname for its screen. this name is used when checking the -configuration. - -neither the client nor server will automatically restart if an error -occurs that is sure to happen every time. for example, the server -will exit immediately if it can't find itself in the screen -configuration. on X11 both the client and server will also terminate -if the connection to the X server is lost. since xdm will normally -restart the server and then synergy this is the correct behavior. - - -server command line options ---------------------------- - -a, --address
listen for connections on the given address - -c, --config read configuration from - -
has one of the following forms: - - : - : - is a hostname or address of a network interface on the -server system. is a port number from 1 to 65535. -defaults to the system's hostname and defaults to 24800. - - -client command line options ---------------------------- - --camp retry connection to server until successful - --no-camp try connection to server only once -
address of server - -see the "server command line options" for a description of
-but note that there is no default though there is a -default . - - -release notes -------------- -synergy does not yet fully capture all possible input or have full -control over the mouse and keyboard on all platforms. each platform -has its own limitations and these limitations may influence your -choice for the server. - -the following lists enumerate the limitations of each platform. a -key (combination) that cannot be captured is not detected by synergy. -a key (combination) that cannot be blocked will be passed through to -the server system even when the mouse is on a client system. if a -key cannot be captured then it also cannot be blocked. - -windows 95 family, windows NT prior to service pack 3: - * cannot capture: - * ctrl+alt+del - * ctrl+esc - * alt+[shift+]tab - * alt+[shift+]esc - * windows+E - * windows+[ctrl+]F - * windows+[shift+]M - * windows+R - * windows+F1 - * windows+tab - * windows+break - * accessibility shortcuts (e.g. press shift 5 times for sticky keys) - * the individual keys are captured but the dialogs still appear - * cannot synthesize: - * accessibility shortcuts - -windows NT family (except NT prior to SP3): - * cannot block: - * ctrl+alt+del - * accessibility shortcuts (e.g. press shift 5 times for sticky keys) - * the individual keys are captured but the dialogs still appear - * cannot synthesize: - * accessibility shortcuts - -linux, unix: - * cannot capture: - * ctrl+alt+del - * ctrl+alt+backspace (only if used by the X server) - * ctrl+alt+keypad_plus (only if used by the X server) - * ctrl+alt+keypad_minus (only if used by the X server) - * keyboard/mouse grabs prevent switching screens for their duration - * some display managers grab the keyboard until login - -currently, the windows NT family (except NT prior to SP3) makes the -best server. - - -known bugs ----------- -all: - * non-ASCII keyboard characters are not supported - -windows: - * screen flashes when entering the screen - * synergy may interfere with desktop switcher programs. however, - synergy understands and handles multiple desktops. - * there should be a control panel - * there should be a taskbar icon - -windows 95 family: - * typing into a console window can be slow - -windows NT family: - * the event viewer reports a message lookup error for synergy logs. - however, the full synergy message is in the extra data portion of - the event dialog. - * does not gracefully handle NoInteractiveServices being enabled - -linux: - * some keyboards have toggle keys that toggle on on key press and - toggle off on the key release after the next key press. synergy - doesn't handle these properly. - * shift-lock (as opposed to caps-lock) is not supported - * large (~256k) motif clipboard items are not copied properly - - -tips and tricks ---------------- -* a screen can be its own neighbor. that allows a screen to "wrap". - for example, if a configuration linked the left and right sides of +* A screen can be its own neighbor. That allows a screen to "wrap". + For example, if a configuration linked the left and right sides of a screen to itself then moving off the left of the screen would put the mouse at the right of the screen and vice versa. -* you cannot switch screens when a key or mouse button is pressed. +* You cannot switch screens when a key or mouse button is pressed. -* you cannot switch screens when the scroll lock it toggled on. use +* You cannot switch screens when the scroll lock it toggled on. Use this to prevent unintentional switching. -* turn off mouse driven virtual desktop switching on X windows. it - will interfere with synergy. use keyboard shortcuts instead. +* Turn off mouse driven virtual desktop switching on X windows. It + will interfere with synergy. Use keyboard shortcuts instead. -* synergy's screensaver synchronization works best with xscreensaver - under X windows. synergy works better with xscreensaver if it is - using of the screensaver extensions. prior to xscreensaver 4.0 +* Synergy's screen saver synchronization works best with xscreensaver + under X windows. Synergy works better with xscreensaver if it is + using of the screen saver extensions. Prior to xscreensaver 4.0 you can use `-mit-extension', `-sgi-extension', or `-xidle-extension' - command line options to enable an extension. starting with 4.0 - you must enable the corresponding option in your .xscreensaver file. + command line options to enable an extension (assuming your server has + the extension). Starting with 4.0 you must enable the corresponding + option in your .xscreensaver file. + +* To work around the lack of ctrl+alt+del, you can configure Windows + 2000 and XP to not require ctrl+alt+del to log on using the System + control panel. If you're the only user of an NT system you might + want to enable auto-logon. In any case, you should keep each + computer's keyboard handy, perhaps under the desk or on top of the + computer itself. If the system supports USB you should also be able + to attach/detach a keyboard as necessary. + +* Synergy automatically converts newlines in clipboard text (Linux + expects \n to end each line while Windows expects \r\n). + +* Clients can be started and stopped at any time. When a screen is + not connected, the mouse will jump over that screen as if the mouse + had moved all the way across it and jumped to the next screen. + +* A client's keyboard and mouse are fully functional while synergy is + running. You can use them in case synergy hangs. + + +Bug Reports +----------- + +Synergy is being improved all the time but we can only fix problems +that we know about. Please let us know of any problems you encounter, +including confusing or unhelpful documentation. Send reports to: + + synergy@groundhog.pair.com From ed38bc081858e8eba7fa5a9d0d47122398710258 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 1 Aug 2002 18:56:54 +0000 Subject: [PATCH 310/807] Added preliminary RPM spec file. --- Makefile.am | 1 + configure.in | 3 +++ dist/Makefile.am | 15 +++++++++++++++ dist/rpm/Makefile.am | 11 +++++++++++ dist/rpm/synergy.spec.in | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+) create mode 100644 dist/Makefile.am create mode 100644 dist/rpm/Makefile.am create mode 100644 dist/rpm/synergy.spec.in diff --git a/Makefile.am b/Makefile.am index 3b7ec105..a5552932 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,6 +6,7 @@ VDEPTH = ./$(VPATH) SUBDIRS = \ lib \ cmd \ + dist \ $(NULL) EXTRA_DIST = \ diff --git a/configure.in b/configure.in index 4cbb590a..1bd1fdd6 100644 --- a/configure.in +++ b/configure.in @@ -80,5 +80,8 @@ lib/server/Makefile cmd/Makefile cmd/synergy/Makefile cmd/synergyd/Makefile +dist/Makefile +dist/rpm/Makefile +dist/rpm/synergy.spec ]) diff --git a/dist/Makefile.am b/dist/Makefile.am new file mode 100644 index 00000000..75079e4b --- /dev/null +++ b/dist/Makefile.am @@ -0,0 +1,15 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = .. +VDEPTH = ./$(VPATH)/$(DEPTH) + +SUBDIRS = \ + rpm \ + $(NULL) + +EXTRA_DIST = \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/dist/rpm/Makefile.am b/dist/rpm/Makefile.am new file mode 100644 index 00000000..b6716d33 --- /dev/null +++ b/dist/rpm/Makefile.am @@ -0,0 +1,11 @@ +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in new file mode 100644 index 00000000..d7e7a4ad --- /dev/null +++ b/dist/rpm/synergy.spec.in @@ -0,0 +1,37 @@ +Summary: Mouse and keyboard sharing utility +Name: @PACKAGE@ +Version: @VERSION@ +Release: 1 +Copyright: Copyright (C) 2002 Chris Schoeneman +Packager: Chris Schoeneman +Group: FIXME +Prefixes: FIXME +Source: @PACKAGE@-@VERSION@.tar.gz +URL: FIXME +BuildRoot: /var/tmp/@PACKAGE@-@VERSION@-root + +%description +FIXME + +%prep +%setup +CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=/usr + +%build +make + +%install +# FIXME -- install should install doc files +make install DESTDIR=$RPM_BUILD_ROOT +# FIXME -- or maybe: make PREFIX=$RPM_BUILD_ROOT%{prefix} install + +strip $RPM_BUILD_ROOT/usr/bin/synergy +strip $RPM_BUILD_ROOT/usr/bin/synergyd + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%attr(755, root, root) /usr/bin/synergy +%attr(755, root, root) /usr/bin/synergyd +# FIXME -- add doc files From 1abf0e0eb921efdf475faebaf117718d3622061c Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 2 Aug 2002 17:53:23 +0000 Subject: [PATCH 311/807] changed formatting and other documentation edits. --- AUTHORS | 3 + BUGS | 8 ++- FAQ | 3 +- HISTORY | 16 +++++ INSTALL | 15 +++- NEWS | 3 +- PORTING | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ README | 5 +- 8 files changed, 254 insertions(+), 6 deletions(-) create mode 100644 HISTORY create mode 100644 PORTING diff --git a/AUTHORS b/AUTHORS index 87723923..a7ebc105 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,4 @@ +Synergy Authors +=============== + Chris Schoeneman diff --git a/BUGS b/BUGS index 4212ef23..912ef100 100644 --- a/BUGS +++ b/BUGS @@ -1,10 +1,16 @@ -This file lists the known bugs in synergy. +Known Bugs in Synergy +===================== Report bugs to: synergy@groundhog.pair.com When reporting bugs, please include the version of the operating system you're using and what locale you use. +* Documentation is missing + + There are currently no man pages or other documents describing the + usage of the various commands. + * Not all keystrokes are handled Certain keystrokes are not captured by the synergy server and, diff --git a/FAQ b/FAQ index 270f0f74..0369b305 100644 --- a/FAQ +++ b/FAQ @@ -1,4 +1,5 @@ -Frequently Asked Questions about synergy. +Synergy Frequently Asked Questions +================================== This will be added to as questions come in. diff --git a/HISTORY b/HISTORY new file mode 100644 index 00000000..64c306d7 --- /dev/null +++ b/HISTORY @@ -0,0 +1,16 @@ +History of Synergy +================== + +The first incarnation of synergy was CosmoSynergy, created by +Richard Lee and Adam Feder then at Cosmo Software, Inc., a +subsidiary of SGI (nee Silicon Graphics, Inc.), at the end of +1996. They wrote it, and Chris Schoeneman contributed, to +solve a problem: most of the engineers in Cosmo Software had +both an Irix and a Windows box on their desks and switchboxes +were expensive and annoying. CosmoSynergy was a great success +but Cosmo Software declined to productize it and the company +was later closed. + +Synergy is a from-scratch reimplementation of CosmoSynergy. +It provides most of the features of the original and adds a +few improvements. diff --git a/INSTALL b/INSTALL index 74e02ea8..ea076b28 100644 --- a/INSTALL +++ b/INSTALL @@ -1,4 +1,5 @@ -Installation instructions for synergy. +Synergy Installation Instructions +================================= Prerequisites for building -------------------------- @@ -190,6 +191,10 @@ file (either as a screen name or an alias) then you'll have to add a name in the configuration file. You can use `synergyd --help' for a list of command line options. +On Windows, the file synrgyhk.dll must be in the same directory as +synergyd.exe or in a system directory otherwise the server will +refuse to start. + Running the Client ------------------ @@ -247,6 +252,14 @@ Windows: except do not include `-f' or `--no-daemon'. This will install synergy as a service that will be started at system boot. + Note that the system will look for synergy wherever it was when it + was installed. The server will also look for the configuration + file in the path specified by the `--config' option, if supplied, + and for synrgyhk.dll in the same directory as synergyd.exe or in + a system directory. Therefore, it's important that these files be + on local hard drives; network drives or removable devices may not + be available at boot. + On the Windows NT family you can start and stop the service at any time using the Services control panel (under Administrative Tools on Windows 2000 and XP). On the Windows 95 family you cannot diff --git a/NEWS b/NEWS index 7fdcb34b..1473e6db 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,5 @@ -News about the synergy project. +Synergy News +============ * Initial version of synergy released. Supports ASCII keys, 5 button wheel mouse, Unicode text clipboard, and screen saver synchronization. diff --git a/PORTING b/PORTING new file mode 100644 index 00000000..6740b032 --- /dev/null +++ b/PORTING @@ -0,0 +1,207 @@ +Synergy Developer and Porting Guide +=================================== + +This document is under development. + +Code Organization +----------------- + +The synergy source code organization is: + +. -- root makefiles, some standard documentation +cmd -- program source code + synergy -- synergy client + synergyd -- synergy server +config -- stuff for autoconf/automake +dist -- files for creating distributions + rpm -- files for creating RPMs +doc -- placeholder for documentation +examples -- example files +lib -- library source code + base -- simple utilities + client -- synergy client library + http -- simple http tools + io -- I/O + mt -- multithreading + net -- networking + platform -- platform dependent display/window/event stuff + server -- synergy server library + synergy -- synergy shared client/server code library + +Note how the utility code required by the programs is placed into +separate library directories. This makes the makefiles a little +more awkward but makes for a cleaner organization. The top level +directory has only the standard documentation files and the files +necessary to configure and build the rest of the project. + + +Coding Style Guide +------------------ + +Synergy uses many coding conventions. Contributed code should +following these guidelines. + +- Dependencies + * No circular library dependencies + Library dependencies form an acyclic graph. Conceptually + libraries can be arranged in layers where each library only + references libraries in layers below it, not in the same + layer or layers above it. The makefiles build the lowest + layer libraries first and work upwards. + + * Avoid circular uses-a relationships + When possible, design classes with one-way uses-a relationships + and avoid cycles. This makes it easier to understand the code. + However, sometimes it's not always practical so it is permitted. + +- C++ + * C++ features + Synergy uses the following more recent C++ features: + * bool + * templates + * exceptions + * mutable + * new scoping rules + * the standard C++ library + + Do not use the following C++ features: + * dynamic_cast + * run time type information + * namespaces and using (use std:: where necessary) + + The new scoping rules say that the scope of a variable declared + in a for statement is limited to the for loop. For example: + + for (int i = 0; i < 10; ++i) { + // i is in scope here + } + // i is not in scope here + + for (int i = -10; i < 0; ++i) { + // an entirely new i is in scope here + } + // i is not in scope here + + This is used routinely in synergy, but only in for loops. There + is a macro for `for' in lib/base/common.h when building under + Microsoft Visual C++ that works around the fact that that compiler + doesn't follow the new scoping rules. Use the macro if your + compiler using the old scoping rules. + + * Standard C++ library + The standard C++ library containers should always be used in favor + of custom containers wherever reasonable. std::string is used + throughout synergy but only as the CString typedef; always use + CString, never std::string. Synergy avoids using auto_ptr due to + some portability problems. Synergy makes limited use of standard + algorithms and streams but they can be freely used in new code. + + * Limited multiple inheritance + Classes should inherit implementation from at most one superclass. + Inheriting implementation from multiple classes can have unpleasant + consequences in C++ due to it's limited capabilities. Classes can + inherit from any number of interface classes. An interface class + provides only pure virtual methods. + + Synergy breaks this rule in two places. First is that IInterface + implements the virtual destructor. However, it's just an empty + method so it doesn't really count. Second is MXErrno which provides + implementation for exceptions that use the global errno variable. + + * No globals + Avoid global variables. All global variables must be static, making + it visible only with its source file. Most uses of global variables + are better served by static data members of a class. Global + constants are permitted in some circumstances. + + Also avoid global functions. Use public static member functions in + a class instead. + + These rules are violated by the main source file for each program + (except that the globals are still static). They could easily be + rewritten to put all the variables and functions into a class but + there's little to be gained by that. + + * Private data only + If a class is plain-old-data (i.e. it has no methods) all of its + data members should be public. Otherwise all of its data members + should be private, not public or protected. This makes it much + easier to track the use of a member when reading code. Protected + data is not allowed because `protected' is a synonym for `public + to my subclasses' and public data is Bad Thing. While it might + seem okay in this limited situation, the situation is not at all + limited since an arbitrary number of classes can be derived, + directly or indirectly, from the class and any of those classes + have full access to the protected data. + + * Don't test for NULL when using `delete' or `delete[]' + It's unnecessary since delete does it anyway. + +- Naming + Names always begin with a letter (never an underscore). The first + letter of interior names are always capitalized. Acronyms should + be all uppercase. For example: myTextAsASCII. + + Names come it two flavors: leading capital and leading lowercase. + The former have the first character capitalized and the latter + don't. In the following table, leading capital names are indicated + by `Name' and leading lowercase names by `name'. + + The naming convention for various things are: + + * Exceptions -- X + Name XMyException + * Interfaces -- I + Name IMyInterface + * Template Classes -- T + Name TMyTemplate<> + * Other Classes -- C + Name CMyClass + * Enumerations -- E + Name EMyEnumeration + * Constants -- k + Name kMyConstant + * Data Members -- m_ + name m_myDataMember + * Methods -- name myMethod + * Functions -- name myFunction + * Variables -- name myVariable + + Exceptions are types that get thrown and are generally derived + (possibly indirectly) from XBase. Interfaces are derived (possibly + indirectly) from IInterface and have only pure virtual functions. + Other classes are classes that aren't exceptions or interfaces. + Constants include global constants and enumerants. + +- Formatting + Every project has its own formatting style and no style satisfies + everyone. New code should be consistent with existing code: + + * Use tabs to indent + * Tabs are 4 columns + * Open braces ({) go on same line as introducing statement + `for (i = 0; i < 10; ++i) {' not + for (i = 0; i < 10; ++i) + { + * Close braces line up with introducing statement + * Open brace for function is on a line by itself in first column + * Close brace for function lines up with open brace + * Always use braces on: if, else, for, while, do, switch + * `else {' goes on it's own line + * Always explicitly test pointers against NULL + e.g. `if (ptr == NULL)' not `if (ptr)' + * Put spaces around binary operators and after statements + e.g. `if (a == b) {' not `if(a==b){' + * C'tor initializers are one per line, indented one tab stop + * Other indentation should follow existing practice + * Use Qt style comments for extraction by doxygen + * Mark incomplete or buggy code with `FIXME' + +Class Relationships +------------------- + +The doxygen documentation can help in understanding the relationships +between objects. Use `make doxygen' in the top level directory to +create the doxygen documentation into doc/doxygen/html. You must have +doxygen installed, of course. + +FIXME -- high level overview of class relationships + + +Portability +----------- + +FIXME -- information about porting to new platforms diff --git a/README b/README index b2f6ab2b..351eb35d 100644 --- a/README +++ b/README @@ -19,10 +19,10 @@ a password to unlock them all. Synergy is open source and released under the GNU Public License (GPL). The synergy home page is: -FIXME +http://synergy2.sourceforge.net/ The synergy project page is: -FIXME +http://sourceforge.net/projects/synergy2/ Report bugs to: synergy@groundhog.pair.com @@ -32,6 +32,7 @@ Please see the following files for more information: AUTHORS -- The list of synergy's authors BUGS -- A list of known bugs and limitations COPYING -- The license synergy is release under +HISTORY -- A brief history of synergy INSTALL -- Detailed build and installation instructions NEWS -- News about the synergy project PORTING -- Porting guide for developers From 24e1d6fe995d2c7e7a177e2aaf7c5dd9d3392d94 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 2 Aug 2002 17:53:44 +0000 Subject: [PATCH 312/807] Removed commented out code. --- lib/client/CXWindowsSecondaryScreen.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index 7c1f55a3..af5c6338 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -304,11 +304,6 @@ CXWindowsSecondaryScreen::showWindow() // raise and show the hider window. take activation. // FIXME -- take focus? XMapRaised(display, m_window); - -/* XXX -- this should have no effect - // hide cursor by moving it into the hider window - XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0); -*/ } void From 17d3ef6bdc08caeee6b299cab252d24d780fbb2c Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 2 Aug 2002 17:57:54 +0000 Subject: [PATCH 313/807] finished RPM build rules and changed doxygen configuration to be built by configure and the doxygen documentation to be removed by maintainer-clean. --- Makefile.am | 14 ++++++++++++++ configure.in | 1 + dist/rpm/synergy.spec.in | 21 ++++++++++++--------- doc/{doxygen.cfg => doxygen.cfg.in} | 6 +++--- 4 files changed, 30 insertions(+), 12 deletions(-) rename doc/{doxygen.cfg => doxygen.cfg.in} (99%) diff --git a/Makefile.am b/Makefile.am index a5552932..5c654d54 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,8 +26,22 @@ MAINTAINERCLEANFILES = \ configure \ stamp-h.in \ stamp-h1 \ + doc/doxygen.cfg \ + doc/doxygen/html/* \ $(NULL) # build doxygen documentation doxygen: doxygen doc/doxygen.cfg + +# build RPMs +RPMTOPDIR=/var/tmp/@PACKAGE@-@VERSION@ +dist-rpm: dist + rm -rf $(RPMTOPDIR) + mkdir $(RPMTOPDIR) + (cd $(RPMTOPDIR); mkdir BUILD SOURCES SPECS SRPMS RPMS) + cp @PACKAGE@-@VERSION@.tar.gz $(RPMTOPDIR)/SOURCES + rpm --define '_topdir $(RPMTOPDIR)' -ba dist/rpm/synergy.spec && \ + mv -f $(RPMTOPDIR)/SRPMS/*.rpm . && \ + mv -f $(RPMTOPDIR)/RPMS/*/*.rpm . && \ + rm -rf $(RPMTOPDIR) diff --git a/configure.in b/configure.in index 1bd1fdd6..31983895 100644 --- a/configure.in +++ b/configure.in @@ -83,5 +83,6 @@ cmd/synergyd/Makefile dist/Makefile dist/rpm/Makefile dist/rpm/synergy.spec +doc/doxygen.cfg ]) diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in index d7e7a4ad..7e8f886b 100644 --- a/dist/rpm/synergy.spec.in +++ b/dist/rpm/synergy.spec.in @@ -2,16 +2,19 @@ Summary: Mouse and keyboard sharing utility Name: @PACKAGE@ Version: @VERSION@ Release: 1 -Copyright: Copyright (C) 2002 Chris Schoeneman +License: GPL Packager: Chris Schoeneman -Group: FIXME -Prefixes: FIXME +Group: System Environment/Daemons +Prefixes: /usr/bin Source: @PACKAGE@-@VERSION@.tar.gz -URL: FIXME -BuildRoot: /var/tmp/@PACKAGE@-@VERSION@-root +Buildroot: /var/tmp/@PACKAGE@-@VERSION@-root %description -FIXME +Synergy lets you easily share a single mouse and keyboard between +multiple computers with different operating systems, each with its +own display, without special hardware. It's intended for users +with multiple computers on their desk since each system uses its +own display. %prep %setup @@ -23,7 +26,6 @@ make %install # FIXME -- install should install doc files make install DESTDIR=$RPM_BUILD_ROOT -# FIXME -- or maybe: make PREFIX=$RPM_BUILD_ROOT%{prefix} install strip $RPM_BUILD_ROOT/usr/bin/synergy strip $RPM_BUILD_ROOT/usr/bin/synergyd @@ -32,6 +34,7 @@ strip $RPM_BUILD_ROOT/usr/bin/synergyd rm -rf $RPM_BUILD_ROOT %files -%attr(755, root, root) /usr/bin/synergy -%attr(755, root, root) /usr/bin/synergyd +%defattr(-, root, root) +/usr/bin/synergy +/usr/bin/synergyd # FIXME -- add doc files diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg.in similarity index 99% rename from doc/doxygen.cfg rename to doc/doxygen.cfg.in index ceae4d8a..7ffc56c6 100644 --- a/doc/doxygen.cfg +++ b/doc/doxygen.cfg.in @@ -17,13 +17,13 @@ # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. -PROJECT_NAME = Synergy +PROJECT_NAME = @PACKAGE@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 1.0.0 +PROJECT_NUMBER = @VERSION@ # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. @@ -378,7 +378,7 @@ FILTER_SOURCE_FILES = NO # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. -SOURCE_BROWSER = NO +SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. From fe73362f20893a9f2134ac65ea36a2d9dd49a8cf Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 2 Aug 2002 18:01:26 +0000 Subject: [PATCH 314/807] updated ChangeLog. --- ChangeLog | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ChangeLog b/ChangeLog index fa681562..092c13e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2002/08/02 17:57:54 crs +Makefile.am +configure.in +dist/Makefile.am +dist/rpm/Makefile.am +dist/rpm/synergy.spec.in + +added build rule to create RPMs. + +---------- +2002/08/02 17:53:23 crs +AUTHORS +BUGS +FAQ +HISTORY +INSTALL +NEWS +PORTING +README + +minor documentation updates. added HISTORY and PORTING. + +---------- 2002/08/01 18:07:32 crs AUTHORS BUGS From f6c3a9453bd3d2a76800a4166325c8936fd511e3 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 2 Aug 2002 19:57:46 +0000 Subject: [PATCH 315/807] added copyright to top of each file. --- Makefile.am | 12 ++++++++++++ acinclude.m4 | 12 ++++++++++++ cmd/Makefile.am | 12 ++++++++++++ cmd/synergy/Makefile.am | 12 ++++++++++++ cmd/synergy/resource.h | 14 ++++++++++++++ cmd/synergy/synergy.cpp | 14 ++++++++++++++ cmd/synergyd/Makefile.am | 12 ++++++++++++ cmd/synergyd/resource.h | 14 ++++++++++++++ cmd/synergyd/synergyd.cpp | 14 ++++++++++++++ configure.in | 12 ++++++++++++ dist/Makefile.am | 12 ++++++++++++ dist/rpm/Makefile.am | 12 ++++++++++++ lib/Makefile.am | 12 ++++++++++++ lib/base/BasicTypes.h | 14 ++++++++++++++ lib/base/CFunctionJob.cpp | 14 ++++++++++++++ lib/base/CFunctionJob.h | 14 ++++++++++++++ lib/base/CLog.cpp | 14 ++++++++++++++ lib/base/CLog.h | 14 ++++++++++++++ lib/base/CStopwatch.cpp | 14 ++++++++++++++ lib/base/CStopwatch.h | 14 ++++++++++++++ lib/base/CString.cpp | 14 ++++++++++++++ lib/base/CString.h | 14 ++++++++++++++ lib/base/CUnicode.cpp | 14 ++++++++++++++ lib/base/CUnicode.h | 14 ++++++++++++++ lib/base/IInterface.h | 14 ++++++++++++++ lib/base/IJob.h | 14 ++++++++++++++ lib/base/Makefile.am | 12 ++++++++++++ lib/base/TMethodJob.h | 14 ++++++++++++++ lib/base/Version.h | 14 ++++++++++++++ lib/base/XBase.cpp | 14 ++++++++++++++ lib/base/XBase.h | 14 ++++++++++++++ lib/base/common.h | 14 ++++++++++++++ lib/base/stdfstream.h | 14 ++++++++++++++ lib/base/stdistream.h | 14 ++++++++++++++ lib/base/stdlist.h | 14 ++++++++++++++ lib/base/stdmap.h | 14 ++++++++++++++ lib/base/stdostream.h | 14 ++++++++++++++ lib/base/stdpost.h | 14 ++++++++++++++ lib/base/stdpre.h | 14 ++++++++++++++ lib/base/stdset.h | 14 ++++++++++++++ lib/base/stdsstream.h | 14 ++++++++++++++ lib/base/stdvector.h | 14 ++++++++++++++ lib/client/CClient.cpp | 14 ++++++++++++++ lib/client/CClient.h | 14 ++++++++++++++ lib/client/CMSWindowsSecondaryScreen.cpp | 14 ++++++++++++++ lib/client/CMSWindowsSecondaryScreen.h | 14 ++++++++++++++ lib/client/CSecondaryScreen.cpp | 14 ++++++++++++++ lib/client/CSecondaryScreen.h | 14 ++++++++++++++ lib/client/CServerProxy.cpp | 14 ++++++++++++++ lib/client/CServerProxy.h | 14 ++++++++++++++ lib/client/CXWindowsSecondaryScreen.cpp | 14 ++++++++++++++ lib/client/CXWindowsSecondaryScreen.h | 14 ++++++++++++++ lib/client/ISecondaryScreenFactory.h | 14 ++++++++++++++ lib/client/Makefile.am | 12 ++++++++++++ lib/http/CHTTPProtocol.cpp | 14 ++++++++++++++ lib/http/CHTTPProtocol.h | 14 ++++++++++++++ lib/http/Makefile.am | 12 ++++++++++++ lib/http/XHTTP.cpp | 14 ++++++++++++++ lib/http/XHTTP.h | 14 ++++++++++++++ lib/io/CBufferedInputStream.cpp | 14 ++++++++++++++ lib/io/CBufferedInputStream.h | 14 ++++++++++++++ lib/io/CBufferedOutputStream.cpp | 14 ++++++++++++++ lib/io/CBufferedOutputStream.h | 14 ++++++++++++++ lib/io/CInputStreamFilter.cpp | 14 ++++++++++++++ lib/io/CInputStreamFilter.h | 14 ++++++++++++++ lib/io/COutputStreamFilter.cpp | 14 ++++++++++++++ lib/io/COutputStreamFilter.h | 14 ++++++++++++++ lib/io/CStreamBuffer.cpp | 14 ++++++++++++++ lib/io/CStreamBuffer.h | 14 ++++++++++++++ lib/io/IInputStream.h | 14 ++++++++++++++ lib/io/IOutputStream.h | 14 ++++++++++++++ lib/io/IStreamFilterFactory.h | 14 ++++++++++++++ lib/io/Makefile.am | 12 ++++++++++++ lib/io/XIO.cpp | 14 ++++++++++++++ lib/io/XIO.h | 14 ++++++++++++++ lib/mt/CCondVar.cpp | 14 ++++++++++++++ lib/mt/CCondVar.h | 14 ++++++++++++++ lib/mt/CLock.cpp | 14 ++++++++++++++ lib/mt/CLock.h | 14 ++++++++++++++ lib/mt/CMutex.cpp | 14 ++++++++++++++ lib/mt/CMutex.h | 14 ++++++++++++++ lib/mt/CThread.cpp | 14 ++++++++++++++ lib/mt/CThread.h | 14 ++++++++++++++ lib/mt/CThreadRep.cpp | 14 ++++++++++++++ lib/mt/CThreadRep.h | 14 ++++++++++++++ lib/mt/CTimerThread.cpp | 14 ++++++++++++++ lib/mt/CTimerThread.h | 14 ++++++++++++++ lib/mt/Makefile.am | 12 ++++++++++++ lib/mt/XThread.h | 14 ++++++++++++++ lib/net/CNetwork.cpp | 14 ++++++++++++++ lib/net/CNetwork.h | 14 ++++++++++++++ lib/net/CNetworkAddress.cpp | 14 ++++++++++++++ lib/net/CNetworkAddress.h | 14 ++++++++++++++ lib/net/CTCPListenSocket.cpp | 14 ++++++++++++++ lib/net/CTCPListenSocket.h | 14 ++++++++++++++ lib/net/CTCPSocket.cpp | 14 ++++++++++++++ lib/net/CTCPSocket.h | 14 ++++++++++++++ lib/net/CTCPSocketFactory.cpp | 14 ++++++++++++++ lib/net/CTCPSocketFactory.h | 14 ++++++++++++++ lib/net/IDataSocket.h | 14 ++++++++++++++ lib/net/IListenSocket.h | 14 ++++++++++++++ lib/net/ISocket.h | 14 ++++++++++++++ lib/net/ISocketFactory.h | 14 ++++++++++++++ lib/net/Makefile.am | 12 ++++++++++++ lib/net/XNetwork.cpp | 14 ++++++++++++++ lib/net/XNetwork.h | 14 ++++++++++++++ lib/net/XSocket.cpp | 14 ++++++++++++++ lib/net/XSocket.h | 14 ++++++++++++++ lib/platform/CMSWindowsClipboard.cpp | 14 ++++++++++++++ lib/platform/CMSWindowsClipboard.h | 14 ++++++++++++++ .../CMSWindowsClipboardAnyTextConverter.cpp | 14 ++++++++++++++ lib/platform/CMSWindowsClipboardAnyTextConverter.h | 14 ++++++++++++++ lib/platform/CMSWindowsClipboardTextConverter.cpp | 14 ++++++++++++++ lib/platform/CMSWindowsClipboardTextConverter.h | 14 ++++++++++++++ lib/platform/CMSWindowsClipboardUTF16Converter.cpp | 14 ++++++++++++++ lib/platform/CMSWindowsClipboardUTF16Converter.h | 14 ++++++++++++++ lib/platform/CMSWindowsScreen.cpp | 14 ++++++++++++++ lib/platform/CMSWindowsScreen.h | 14 ++++++++++++++ lib/platform/CMSWindowsScreenSaver.cpp | 14 ++++++++++++++ lib/platform/CMSWindowsScreenSaver.h | 14 ++++++++++++++ lib/platform/CPlatform.cpp | 14 ++++++++++++++ lib/platform/CPlatform.h | 14 ++++++++++++++ lib/platform/CSynergyHook.cpp | 14 ++++++++++++++ lib/platform/CSynergyHook.h | 14 ++++++++++++++ lib/platform/CUnixPlatform.cpp | 14 ++++++++++++++ lib/platform/CUnixPlatform.h | 14 ++++++++++++++ lib/platform/CWin32Platform.cpp | 14 ++++++++++++++ lib/platform/CWin32Platform.h | 14 ++++++++++++++ lib/platform/CXWindowsClipboard.cpp | 14 ++++++++++++++ lib/platform/CXWindowsClipboard.h | 14 ++++++++++++++ lib/platform/CXWindowsClipboardTextConverter.cpp | 14 ++++++++++++++ lib/platform/CXWindowsClipboardTextConverter.h | 14 ++++++++++++++ lib/platform/CXWindowsClipboardUCS2Converter.cpp | 14 ++++++++++++++ lib/platform/CXWindowsClipboardUCS2Converter.h | 14 ++++++++++++++ lib/platform/CXWindowsClipboardUTF8Converter.cpp | 14 ++++++++++++++ lib/platform/CXWindowsClipboardUTF8Converter.h | 14 ++++++++++++++ lib/platform/CXWindowsScreen.cpp | 14 ++++++++++++++ lib/platform/CXWindowsScreen.h | 14 ++++++++++++++ lib/platform/CXWindowsScreenSaver.cpp | 14 ++++++++++++++ lib/platform/CXWindowsScreenSaver.h | 14 ++++++++++++++ lib/platform/CXWindowsUtil.cpp | 14 ++++++++++++++ lib/platform/CXWindowsUtil.h | 14 ++++++++++++++ lib/platform/IMSWindowsScreenEventHandler.h | 14 ++++++++++++++ lib/platform/IPlatform.h | 14 ++++++++++++++ lib/platform/Makefile.am | 12 ++++++++++++ lib/server/CClientProxy.cpp | 14 ++++++++++++++ lib/server/CClientProxy.h | 14 ++++++++++++++ lib/server/CClientProxy1_0.cpp | 14 ++++++++++++++ lib/server/CClientProxy1_0.h | 14 ++++++++++++++ lib/server/CConfig.cpp | 14 ++++++++++++++ lib/server/CConfig.h | 14 ++++++++++++++ lib/server/CHTTPServer.cpp | 14 ++++++++++++++ lib/server/CHTTPServer.h | 14 ++++++++++++++ lib/server/CMSWindowsPrimaryScreen.cpp | 14 ++++++++++++++ lib/server/CMSWindowsPrimaryScreen.h | 14 ++++++++++++++ lib/server/CPrimaryClient.cpp | 14 ++++++++++++++ lib/server/CPrimaryClient.h | 14 ++++++++++++++ lib/server/CPrimaryScreen.cpp | 14 ++++++++++++++ lib/server/CPrimaryScreen.h | 14 ++++++++++++++ lib/server/CServer.cpp | 14 ++++++++++++++ lib/server/CServer.h | 14 ++++++++++++++ lib/server/CXWindowsPrimaryScreen.cpp | 14 ++++++++++++++ lib/server/CXWindowsPrimaryScreen.h | 14 ++++++++++++++ lib/server/IPrimaryScreenFactory.h | 14 ++++++++++++++ lib/server/Makefile.am | 12 ++++++++++++ lib/synergy/CClipboard.cpp | 14 ++++++++++++++ lib/synergy/CClipboard.h | 14 ++++++++++++++ lib/synergy/CInputPacketStream.cpp | 14 ++++++++++++++ lib/synergy/CInputPacketStream.h | 14 ++++++++++++++ lib/synergy/COutputPacketStream.cpp | 14 ++++++++++++++ lib/synergy/COutputPacketStream.h | 14 ++++++++++++++ lib/synergy/CProtocolUtil.cpp | 14 ++++++++++++++ lib/synergy/CProtocolUtil.h | 14 ++++++++++++++ lib/synergy/ClipboardTypes.h | 14 ++++++++++++++ lib/synergy/IClient.h | 14 ++++++++++++++ lib/synergy/IClipboard.h | 14 ++++++++++++++ lib/synergy/IPrimaryScreenReceiver.h | 14 ++++++++++++++ lib/synergy/IScreen.h | 14 ++++++++++++++ lib/synergy/IScreenEventHandler.h | 14 ++++++++++++++ lib/synergy/IScreenReceiver.h | 14 ++++++++++++++ lib/synergy/IScreenSaver.h | 14 ++++++++++++++ lib/synergy/IServer.h | 14 ++++++++++++++ lib/synergy/KeyTypes.h | 14 ++++++++++++++ lib/synergy/Makefile.am | 12 ++++++++++++ lib/synergy/MouseTypes.h | 14 ++++++++++++++ lib/synergy/ProtocolTypes.h | 14 ++++++++++++++ lib/synergy/XScreen.cpp | 14 ++++++++++++++ lib/synergy/XScreen.h | 14 ++++++++++++++ lib/synergy/XSynergy.cpp | 14 ++++++++++++++ lib/synergy/XSynergy.h | 14 ++++++++++++++ 190 files changed, 2624 insertions(+) diff --git a/Makefile.am b/Makefile.am index 5c654d54..d88626f9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = . diff --git a/acinclude.m4 b/acinclude.m4 index 4b02bee6..121a3380 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1,3 +1,15 @@ +dnl synergy -- mouse and keyboard sharing utility +dnl Copyright (C) 2002 Chris Schoeneman +dnl +dnl This package is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU General Public License +dnl found in the file COPYING that should have accompanied this file. +dnl +dnl This package is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. + dnl The following macros are from http://www.gnu.org/software/ac-archive/ dnl which distributes them under the following license: dnl diff --git a/cmd/Makefile.am b/cmd/Makefile.am index ade29001..3fe79050 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = .. diff --git a/cmd/synergy/Makefile.am b/cmd/synergy/Makefile.am index 87df1dfc..3b8c4499 100644 --- a/cmd/synergy/Makefile.am +++ b/cmd/synergy/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/cmd/synergy/resource.h b/cmd/synergy/resource.h index 993c9edc..33361a75 100644 --- a/cmd/synergy/resource.h +++ b/cmd/synergy/resource.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by client.rc diff --git a/cmd/synergy/synergy.cpp b/cmd/synergy/synergy.cpp index aeb206e3..f1bb35ed 100644 --- a/cmd/synergy/synergy.cpp +++ b/cmd/synergy/synergy.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CClient.h" #include "ISecondaryScreenFactory.h" #include "CPlatform.h" diff --git a/cmd/synergyd/Makefile.am b/cmd/synergyd/Makefile.am index 287a620c..a16ea951 100644 --- a/cmd/synergyd/Makefile.am +++ b/cmd/synergyd/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/cmd/synergyd/resource.h b/cmd/synergyd/resource.h index 2b751aa3..2c43f29d 100644 --- a/cmd/synergyd/resource.h +++ b/cmd/synergyd/resource.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by server.rc diff --git a/cmd/synergyd/synergyd.cpp b/cmd/synergyd/synergyd.cpp index 4a971071..abdbed4d 100644 --- a/cmd/synergyd/synergyd.cpp +++ b/cmd/synergyd/synergyd.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CServer.h" #include "CConfig.h" #include "IPrimaryScreenFactory.h" diff --git a/configure.in b/configure.in index 31983895..c938eda3 100644 --- a/configure.in +++ b/configure.in @@ -1,3 +1,15 @@ +dnl synergy -- mouse and keyboard sharing utility +dnl Copyright (C) 2002 Chris Schoeneman +dnl +dnl This package is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU General Public License +dnl found in the file COPYING that should have accompanied this file. +dnl +dnl This package is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. + dnl Process this file with autoconf to produce a configure script. dnl initialize diff --git a/dist/Makefile.am b/dist/Makefile.am index 75079e4b..bf906cbf 100644 --- a/dist/Makefile.am +++ b/dist/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = .. diff --git a/dist/rpm/Makefile.am b/dist/rpm/Makefile.am index b6716d33..1f0c046b 100644 --- a/dist/rpm/Makefile.am +++ b/dist/rpm/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/lib/Makefile.am b/lib/Makefile.am index c98329de..5b23e06b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = .. diff --git a/lib/base/BasicTypes.h b/lib/base/BasicTypes.h index 0a437caa..c7176637 100644 --- a/lib/base/BasicTypes.h +++ b/lib/base/BasicTypes.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef BASICTYPES_H #define BASICTYPES_H diff --git a/lib/base/CFunctionJob.cpp b/lib/base/CFunctionJob.cpp index 423f0a63..bc16c509 100644 --- a/lib/base/CFunctionJob.cpp +++ b/lib/base/CFunctionJob.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CFunctionJob.h" // diff --git a/lib/base/CFunctionJob.h b/lib/base/CFunctionJob.h index 311ceaf9..e30bbfa2 100644 --- a/lib/base/CFunctionJob.h +++ b/lib/base/CFunctionJob.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CFUNCTIONJOB_H #define CFUNCTIONJOB_H diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index bbc55a8c..0cc48f08 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CLog.h" #include "CString.h" #include diff --git a/lib/base/CLog.h b/lib/base/CLog.h index 085016e2..e364e8ac 100644 --- a/lib/base/CLog.h +++ b/lib/base/CLog.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CLOG_H #define CLOG_H diff --git a/lib/base/CStopwatch.cpp b/lib/base/CStopwatch.cpp index 99792d19..02b99d77 100644 --- a/lib/base/CStopwatch.cpp +++ b/lib/base/CStopwatch.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CStopwatch.h" // diff --git a/lib/base/CStopwatch.h b/lib/base/CStopwatch.h index 8817a67a..e6a34719 100644 --- a/lib/base/CStopwatch.h +++ b/lib/base/CStopwatch.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CSTOPWATCH_H #define CSTOPWATCH_H diff --git a/lib/base/CString.cpp b/lib/base/CString.cpp index 8854df6d..2aedb36e 100644 --- a/lib/base/CString.cpp +++ b/lib/base/CString.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CString.h" #include "common.h" #include "stdvector.h" diff --git a/lib/base/CString.h b/lib/base/CString.h index 0f74cba5..7add626f 100644 --- a/lib/base/CString.h +++ b/lib/base/CString.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CSTRING_H #define CSTRING_H diff --git a/lib/base/CUnicode.cpp b/lib/base/CUnicode.cpp index 1c17afdd..383d2381 100644 --- a/lib/base/CUnicode.cpp +++ b/lib/base/CUnicode.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CUnicode.h" #include #include diff --git a/lib/base/CUnicode.h b/lib/base/CUnicode.h index aa6d07c3..43ee5470 100644 --- a/lib/base/CUnicode.h +++ b/lib/base/CUnicode.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CUNICODE_H #define CUNICODE_H diff --git a/lib/base/IInterface.h b/lib/base/IInterface.h index 4f85e1be..97df31c2 100644 --- a/lib/base/IInterface.h +++ b/lib/base/IInterface.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef IINTERFACE_H #define IINTERFACE_H diff --git a/lib/base/IJob.h b/lib/base/IJob.h index b83c0d80..30ef5f5a 100644 --- a/lib/base/IJob.h +++ b/lib/base/IJob.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef IJOB_H #define IJOB_H diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index e37143fb..c02cf460 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/lib/base/TMethodJob.h b/lib/base/TMethodJob.h index ef63e22e..54cd936b 100644 --- a/lib/base/TMethodJob.h +++ b/lib/base/TMethodJob.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CMETHODJOB_H #define CMETHODJOB_H diff --git a/lib/base/Version.h b/lib/base/Version.h index 34ce990b..afaea6c5 100644 --- a/lib/base/Version.h +++ b/lib/base/Version.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef VERSION_H #define VERSION_H diff --git a/lib/base/XBase.cpp b/lib/base/XBase.cpp index 19a93a13..0d1d5b32 100644 --- a/lib/base/XBase.cpp +++ b/lib/base/XBase.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "XBase.h" #include #include diff --git a/lib/base/XBase.h b/lib/base/XBase.h index a35a33be..579e328e 100644 --- a/lib/base/XBase.h +++ b/lib/base/XBase.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef XBASE_H #define XBASE_H diff --git a/lib/base/common.h b/lib/base/common.h index a03fd9c1..9ba541bf 100644 --- a/lib/base/common.h +++ b/lib/base/common.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef COMMON_H #define COMMON_H diff --git a/lib/base/stdfstream.h b/lib/base/stdfstream.h index 1b9db065..9d1f5aa6 100644 --- a/lib/base/stdfstream.h +++ b/lib/base/stdfstream.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "stdpre.h" #include #include "stdpost.h" diff --git a/lib/base/stdistream.h b/lib/base/stdistream.h index d19ef632..bd903578 100644 --- a/lib/base/stdistream.h +++ b/lib/base/stdistream.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "stdpre.h" #if defined(HAVE_ISTREAM) #include diff --git a/lib/base/stdlist.h b/lib/base/stdlist.h index 98503b7b..6f0df8e8 100644 --- a/lib/base/stdlist.h +++ b/lib/base/stdlist.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "stdpre.h" #include #include "stdpost.h" diff --git a/lib/base/stdmap.h b/lib/base/stdmap.h index a77f22b6..da501615 100644 --- a/lib/base/stdmap.h +++ b/lib/base/stdmap.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "stdpre.h" #include #include "stdpost.h" diff --git a/lib/base/stdostream.h b/lib/base/stdostream.h index b1c37914..e7496f5b 100644 --- a/lib/base/stdostream.h +++ b/lib/base/stdostream.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "stdpre.h" #if defined(HAVE_OSTREAM) #include diff --git a/lib/base/stdpost.h b/lib/base/stdpost.h index 658cd5d5..0f06b74a 100644 --- a/lib/base/stdpost.h +++ b/lib/base/stdpost.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #if defined(_MSC_VER) #pragma warning(pop) #endif diff --git a/lib/base/stdpre.h b/lib/base/stdpre.h index 172190ce..10fa7d5d 100644 --- a/lib/base/stdpre.h +++ b/lib/base/stdpre.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #if defined(_MSC_VER) #pragma warning(disable: 4786) // identifier truncated #pragma warning(disable: 4514) // unreferenced inline diff --git a/lib/base/stdset.h b/lib/base/stdset.h index fc818573..aeb491b6 100644 --- a/lib/base/stdset.h +++ b/lib/base/stdset.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "stdpre.h" #include #include "stdpost.h" diff --git a/lib/base/stdsstream.h b/lib/base/stdsstream.h index b486dc62..1bd2b970 100644 --- a/lib/base/stdsstream.h +++ b/lib/base/stdsstream.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "stdpre.h" #include #include "stdpost.h" diff --git a/lib/base/stdvector.h b/lib/base/stdvector.h index 8c7cd443..1056bb46 100644 --- a/lib/base/stdvector.h +++ b/lib/base/stdvector.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "stdpre.h" #include #include "stdpost.h" diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index bb3a8b98..b4c5cbda 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CClient.h" #include "CServerProxy.h" #include "ISecondaryScreenFactory.h" diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 61f0fe2b..664eeb2a 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CCLIENT_H #define CCLIENT_H diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index 21b17bac..44267977 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CMSWindowsSecondaryScreen.h" #include "CMSWindowsScreen.h" #include "CPlatform.h" diff --git a/lib/client/CMSWindowsSecondaryScreen.h b/lib/client/CMSWindowsSecondaryScreen.h index b1d7575b..a2b556c2 100644 --- a/lib/client/CMSWindowsSecondaryScreen.h +++ b/lib/client/CMSWindowsSecondaryScreen.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CMSWINDOWSSECONDARYSCREEN_H #define CMSWINDOWSSECONDARYSCREEN_H diff --git a/lib/client/CSecondaryScreen.cpp b/lib/client/CSecondaryScreen.cpp index 91ea7c65..dd2b9169 100644 --- a/lib/client/CSecondaryScreen.cpp +++ b/lib/client/CSecondaryScreen.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CSecondaryScreen.h" #include "IScreen.h" #include "CLock.h" diff --git a/lib/client/CSecondaryScreen.h b/lib/client/CSecondaryScreen.h index 1ff70a29..20900ec3 100644 --- a/lib/client/CSecondaryScreen.h +++ b/lib/client/CSecondaryScreen.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CSECONDARYSCREEN_H #define CSECONDARYSCREEN_H diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 8436a62a..44cb8e52 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CServerProxy.h" #include "CProtocolUtil.h" #include "IClient.h" diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index 145da7f1..f67b72f6 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CSERVERPROXY_H #define CSERVERPROXY_H diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index af5c6338..364a234a 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CXWindowsSecondaryScreen.h" #include "CXWindowsClipboard.h" #include "CXWindowsScreen.h" diff --git a/lib/client/CXWindowsSecondaryScreen.h b/lib/client/CXWindowsSecondaryScreen.h index 30facac4..556de6b4 100644 --- a/lib/client/CXWindowsSecondaryScreen.h +++ b/lib/client/CXWindowsSecondaryScreen.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CXWINDOWSSECONDARYSCREEN_H #define CXWINDOWSSECONDARYSCREEN_H diff --git a/lib/client/ISecondaryScreenFactory.h b/lib/client/ISecondaryScreenFactory.h index 57ae3b16..03667ec8 100644 --- a/lib/client/ISecondaryScreenFactory.h +++ b/lib/client/ISecondaryScreenFactory.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ISECONDARYSCREENFACTORY_H #define ISECONDARYSCREENFACTORY_H diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index 70762077..28673161 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/lib/http/CHTTPProtocol.cpp b/lib/http/CHTTPProtocol.cpp index cea0d64c..c02198db 100644 --- a/lib/http/CHTTPProtocol.cpp +++ b/lib/http/CHTTPProtocol.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CHTTPProtocol.h" #include "XHTTP.h" #include "IInputStream.h" diff --git a/lib/http/CHTTPProtocol.h b/lib/http/CHTTPProtocol.h index 7c6fd000..d4e06b43 100644 --- a/lib/http/CHTTPProtocol.h +++ b/lib/http/CHTTPProtocol.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CHTTPPROTOCOL_H #define CHTTPPROTOCOL_H diff --git a/lib/http/Makefile.am b/lib/http/Makefile.am index 9b1f6d49..ba51677e 100644 --- a/lib/http/Makefile.am +++ b/lib/http/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/lib/http/XHTTP.cpp b/lib/http/XHTTP.cpp index 690cb4f8..493a0383 100644 --- a/lib/http/XHTTP.cpp +++ b/lib/http/XHTTP.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "XHTTP.h" #include "CHTTPProtocol.h" #include "stdsstream.h" diff --git a/lib/http/XHTTP.h b/lib/http/XHTTP.h index 384881b6..e601600a 100644 --- a/lib/http/XHTTP.h +++ b/lib/http/XHTTP.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef XHTTP_H #define XHTTP_H diff --git a/lib/io/CBufferedInputStream.cpp b/lib/io/CBufferedInputStream.cpp index c4474fd5..27b7cb6f 100644 --- a/lib/io/CBufferedInputStream.cpp +++ b/lib/io/CBufferedInputStream.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CBufferedInputStream.h" #include "CLock.h" #include "CMutex.h" diff --git a/lib/io/CBufferedInputStream.h b/lib/io/CBufferedInputStream.h index 94055155..132d172b 100644 --- a/lib/io/CBufferedInputStream.h +++ b/lib/io/CBufferedInputStream.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CBUFFEREDINPUTSTREAM_H #define CBUFFEREDINPUTSTREAM_H diff --git a/lib/io/CBufferedOutputStream.cpp b/lib/io/CBufferedOutputStream.cpp index fe03e558..3406232c 100644 --- a/lib/io/CBufferedOutputStream.cpp +++ b/lib/io/CBufferedOutputStream.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CBufferedOutputStream.h" #include "XIO.h" #include "CLock.h" diff --git a/lib/io/CBufferedOutputStream.h b/lib/io/CBufferedOutputStream.h index eed8f499..675aaac0 100644 --- a/lib/io/CBufferedOutputStream.h +++ b/lib/io/CBufferedOutputStream.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CBUFFEREDOUTPUTSTREAM_H #define CBUFFEREDOUTPUTSTREAM_H diff --git a/lib/io/CInputStreamFilter.cpp b/lib/io/CInputStreamFilter.cpp index b7f239e7..6d058b4a 100644 --- a/lib/io/CInputStreamFilter.cpp +++ b/lib/io/CInputStreamFilter.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CInputStreamFilter.h" // diff --git a/lib/io/CInputStreamFilter.h b/lib/io/CInputStreamFilter.h index 575fb49f..9274a810 100644 --- a/lib/io/CInputStreamFilter.h +++ b/lib/io/CInputStreamFilter.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CINPUTSTREAMFILTER_H #define CINPUTSTREAMFILTER_H diff --git a/lib/io/COutputStreamFilter.cpp b/lib/io/COutputStreamFilter.cpp index 2d9aeaef..d90465a7 100644 --- a/lib/io/COutputStreamFilter.cpp +++ b/lib/io/COutputStreamFilter.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "COutputStreamFilter.h" // diff --git a/lib/io/COutputStreamFilter.h b/lib/io/COutputStreamFilter.h index 2fd51d2c..027e32dc 100644 --- a/lib/io/COutputStreamFilter.h +++ b/lib/io/COutputStreamFilter.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef COUTPUTSTREAMFILTER_H #define COUTPUTSTREAMFILTER_H diff --git a/lib/io/CStreamBuffer.cpp b/lib/io/CStreamBuffer.cpp index d906d94b..2f10cac2 100644 --- a/lib/io/CStreamBuffer.cpp +++ b/lib/io/CStreamBuffer.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CStreamBuffer.h" // diff --git a/lib/io/CStreamBuffer.h b/lib/io/CStreamBuffer.h index 90081bd1..fafcc72b 100644 --- a/lib/io/CStreamBuffer.h +++ b/lib/io/CStreamBuffer.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CSTREAMBUFFER_H #define CSTREAMBUFFER_H diff --git a/lib/io/IInputStream.h b/lib/io/IInputStream.h index 23d7a201..a1b250de 100644 --- a/lib/io/IInputStream.h +++ b/lib/io/IInputStream.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef IINPUTSTREAM_H #define IINPUTSTREAM_H diff --git a/lib/io/IOutputStream.h b/lib/io/IOutputStream.h index 478468c7..5540ce0a 100644 --- a/lib/io/IOutputStream.h +++ b/lib/io/IOutputStream.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef IOUTPUTSTREAM_H #define IOUTPUTSTREAM_H diff --git a/lib/io/IStreamFilterFactory.h b/lib/io/IStreamFilterFactory.h index e7cd50a2..2f459ef5 100644 --- a/lib/io/IStreamFilterFactory.h +++ b/lib/io/IStreamFilterFactory.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ISTREAMFILTERFACTORY_H #define ISTREAMFILTERFACTORY_H diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index f4ecea1d..1309e7d5 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/lib/io/XIO.cpp b/lib/io/XIO.cpp index e5b07e3d..6c7a0552 100644 --- a/lib/io/XIO.cpp +++ b/lib/io/XIO.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "XIO.h" // diff --git a/lib/io/XIO.h b/lib/io/XIO.h index f65ad3f8..4bbd10c4 100644 --- a/lib/io/XIO.h +++ b/lib/io/XIO.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef XIO_H #define XIO_H diff --git a/lib/mt/CCondVar.cpp b/lib/mt/CCondVar.cpp index cf6f9f96..ae618849 100644 --- a/lib/mt/CCondVar.cpp +++ b/lib/mt/CCondVar.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CCondVar.h" #include "CStopwatch.h" diff --git a/lib/mt/CCondVar.h b/lib/mt/CCondVar.h index af9d88dd..90552f2e 100644 --- a/lib/mt/CCondVar.h +++ b/lib/mt/CCondVar.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CCONDVAR_H #define CCONDVAR_H diff --git a/lib/mt/CLock.cpp b/lib/mt/CLock.cpp index d9f6f7d2..c943395b 100644 --- a/lib/mt/CLock.cpp +++ b/lib/mt/CLock.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CLock.h" #include "CCondVar.h" #include "CMutex.h" diff --git a/lib/mt/CLock.h b/lib/mt/CLock.h index 5e12f73e..33a14f68 100644 --- a/lib/mt/CLock.h +++ b/lib/mt/CLock.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CLOCK_H #define CLOCK_H diff --git a/lib/mt/CMutex.cpp b/lib/mt/CMutex.cpp index cabb0f69..94b654b0 100644 --- a/lib/mt/CMutex.cpp +++ b/lib/mt/CMutex.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CMutex.h" #include "CLog.h" diff --git a/lib/mt/CMutex.h b/lib/mt/CMutex.h index d3a87136..4ae6fa03 100644 --- a/lib/mt/CMutex.h +++ b/lib/mt/CMutex.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CMUTEX_H #define CMUTEX_H diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp index 2741a4c4..eb5ca957 100644 --- a/lib/mt/CThread.cpp +++ b/lib/mt/CThread.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CThread.h" #include "CLock.h" #include "CThreadRep.h" diff --git a/lib/mt/CThread.h b/lib/mt/CThread.h index d11626da..f9dc9ac7 100644 --- a/lib/mt/CThread.h +++ b/lib/mt/CThread.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CTHREAD_H #define CTHREAD_H diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp index ceff76c2..4c15acfe 100644 --- a/lib/mt/CThreadRep.cpp +++ b/lib/mt/CThreadRep.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CThreadRep.h" #include "CLock.h" #include "CMutex.h" diff --git a/lib/mt/CThreadRep.h b/lib/mt/CThreadRep.h index 4177f50a..13ea27be 100644 --- a/lib/mt/CThreadRep.h +++ b/lib/mt/CThreadRep.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CTHREADREP_H #define CTHREADREP_H diff --git a/lib/mt/CTimerThread.cpp b/lib/mt/CTimerThread.cpp index 93713e19..0d3e0b36 100644 --- a/lib/mt/CTimerThread.cpp +++ b/lib/mt/CTimerThread.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CTimerThread.h" #include "CThread.h" #include "TMethodJob.h" diff --git a/lib/mt/CTimerThread.h b/lib/mt/CTimerThread.h index 722ecd21..11a0da73 100644 --- a/lib/mt/CTimerThread.h +++ b/lib/mt/CTimerThread.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CTIMERTHREAD_H #define CTIMERTHREAD_H diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index e4868bfa..5db01822 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/lib/mt/XThread.h b/lib/mt/XThread.h index 700c195b..ac820795 100644 --- a/lib/mt/XThread.h +++ b/lib/mt/XThread.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef XTHREAD_H #define XTHREAD_H diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp index f0056d60..9a74f553 100644 --- a/lib/net/CNetwork.cpp +++ b/lib/net/CNetwork.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CNetwork.h" #include "XNetwork.h" #include "CLog.h" diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h index f1d674a0..96650a99 100644 --- a/lib/net/CNetwork.h +++ b/lib/net/CNetwork.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CNETWORK_H #define CNETWORK_H diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp index 726e4392..ff0515d6 100644 --- a/lib/net/CNetworkAddress.cpp +++ b/lib/net/CNetworkAddress.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CNetworkAddress.h" #include "XSocket.h" #include diff --git a/lib/net/CNetworkAddress.h b/lib/net/CNetworkAddress.h index ed7be4cc..fe68dcf6 100644 --- a/lib/net/CNetworkAddress.h +++ b/lib/net/CNetworkAddress.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CNETWORKADDRESS_H #define CNETWORKADDRESS_H diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index 8e8f36d9..57203c31 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CTCPListenSocket.h" #include "CTCPSocket.h" #include "CNetworkAddress.h" diff --git a/lib/net/CTCPListenSocket.h b/lib/net/CTCPListenSocket.h index 25c3a6ab..15460f30 100644 --- a/lib/net/CTCPListenSocket.h +++ b/lib/net/CTCPListenSocket.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CTCPLISTENSOCKET_H #define CTCPLISTENSOCKET_H diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 35a51244..b0d6f18b 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CTCPSocket.h" #include "CBufferedInputStream.h" #include "CBufferedOutputStream.h" diff --git a/lib/net/CTCPSocket.h b/lib/net/CTCPSocket.h index d30f75e6..8aafa19c 100644 --- a/lib/net/CTCPSocket.h +++ b/lib/net/CTCPSocket.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CTCPSOCKET_H #define CTCPSOCKET_H diff --git a/lib/net/CTCPSocketFactory.cpp b/lib/net/CTCPSocketFactory.cpp index 4067e6bb..f590efa0 100644 --- a/lib/net/CTCPSocketFactory.cpp +++ b/lib/net/CTCPSocketFactory.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CTCPSocketFactory.h" #include "CTCPSocket.h" #include "CTCPListenSocket.h" diff --git a/lib/net/CTCPSocketFactory.h b/lib/net/CTCPSocketFactory.h index 766c2170..2b946d19 100644 --- a/lib/net/CTCPSocketFactory.h +++ b/lib/net/CTCPSocketFactory.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CTCPSOCKETFACTORY_H #define CTCPSOCKETFACTORY_H diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h index f10854b4..0193e1d1 100644 --- a/lib/net/IDataSocket.h +++ b/lib/net/IDataSocket.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef IDATASOCKET_H #define IDATASOCKET_H diff --git a/lib/net/IListenSocket.h b/lib/net/IListenSocket.h index 4b35f249..7a7ddaae 100644 --- a/lib/net/IListenSocket.h +++ b/lib/net/IListenSocket.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ILISTENSOCKET_H #define ILISTENSOCKET_H diff --git a/lib/net/ISocket.h b/lib/net/ISocket.h index 72790960..623a25a7 100644 --- a/lib/net/ISocket.h +++ b/lib/net/ISocket.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ISOCKET_H #define ISOCKET_H diff --git a/lib/net/ISocketFactory.h b/lib/net/ISocketFactory.h index cb719a5e..dbf9ae26 100644 --- a/lib/net/ISocketFactory.h +++ b/lib/net/ISocketFactory.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ISOCKETFACTORY_H #define ISOCKETFACTORY_H diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index 280640bd..899fa938 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/lib/net/XNetwork.cpp b/lib/net/XNetwork.cpp index a6e77e58..61ba2555 100644 --- a/lib/net/XNetwork.cpp +++ b/lib/net/XNetwork.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "XNetwork.h" // diff --git a/lib/net/XNetwork.h b/lib/net/XNetwork.h index aa495655..1e9eee5b 100644 --- a/lib/net/XNetwork.h +++ b/lib/net/XNetwork.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef XNETWORK_H #define XNETWORK_H diff --git a/lib/net/XSocket.cpp b/lib/net/XSocket.cpp index d1c5f661..ccc7ab04 100644 --- a/lib/net/XSocket.cpp +++ b/lib/net/XSocket.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "XSocket.h" // diff --git a/lib/net/XSocket.h b/lib/net/XSocket.h index 4adca77b..fd67ecb1 100644 --- a/lib/net/XSocket.h +++ b/lib/net/XSocket.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef XSOCKET_H #define XSOCKET_H diff --git a/lib/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp index 57fe856b..b746f56e 100644 --- a/lib/platform/CMSWindowsClipboard.cpp +++ b/lib/platform/CMSWindowsClipboard.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CMSWindowsClipboard.h" #include "CMSWindowsClipboardTextConverter.h" #include "CMSWindowsClipboardUTF16Converter.h" diff --git a/lib/platform/CMSWindowsClipboard.h b/lib/platform/CMSWindowsClipboard.h index 4992c57c..a2a65b57 100644 --- a/lib/platform/CMSWindowsClipboard.h +++ b/lib/platform/CMSWindowsClipboard.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CMSWINDOWSCLIPBOARD_H #define CMSWINDOWSCLIPBOARD_H diff --git a/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp b/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp index 66035da5..730f3424 100644 --- a/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp +++ b/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CMSWindowsClipboardAnyTextConverter.h" // diff --git a/lib/platform/CMSWindowsClipboardAnyTextConverter.h b/lib/platform/CMSWindowsClipboardAnyTextConverter.h index e124b5ae..254099f2 100644 --- a/lib/platform/CMSWindowsClipboardAnyTextConverter.h +++ b/lib/platform/CMSWindowsClipboardAnyTextConverter.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CMSWINDOWSCLIPBOARDANYTEXTCONVERTER_H #define CMSWINDOWSCLIPBOARDANYTEXTCONVERTER_H diff --git a/lib/platform/CMSWindowsClipboardTextConverter.cpp b/lib/platform/CMSWindowsClipboardTextConverter.cpp index da5847e4..7b11cdce 100644 --- a/lib/platform/CMSWindowsClipboardTextConverter.cpp +++ b/lib/platform/CMSWindowsClipboardTextConverter.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CMSWindowsClipboardTextConverter.h" #include "CUnicode.h" diff --git a/lib/platform/CMSWindowsClipboardTextConverter.h b/lib/platform/CMSWindowsClipboardTextConverter.h index 0b52a6ae..6f00d475 100644 --- a/lib/platform/CMSWindowsClipboardTextConverter.h +++ b/lib/platform/CMSWindowsClipboardTextConverter.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CMSWINDOWSCLIPBOARDTEXTCONVERTER_H #define CMSWINDOWSCLIPBOARDTEXTCONVERTER_H diff --git a/lib/platform/CMSWindowsClipboardUTF16Converter.cpp b/lib/platform/CMSWindowsClipboardUTF16Converter.cpp index 3422b938..d0ec7ea3 100644 --- a/lib/platform/CMSWindowsClipboardUTF16Converter.cpp +++ b/lib/platform/CMSWindowsClipboardUTF16Converter.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CMSWindowsClipboardUTF16Converter.h" #include "CUnicode.h" diff --git a/lib/platform/CMSWindowsClipboardUTF16Converter.h b/lib/platform/CMSWindowsClipboardUTF16Converter.h index 2a7ff114..51f477fa 100644 --- a/lib/platform/CMSWindowsClipboardUTF16Converter.h +++ b/lib/platform/CMSWindowsClipboardUTF16Converter.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CMSWINDOWSCLIPBOARDUTF16CONVERTER_H #define CMSWINDOWSCLIPBOARDUTF16CONVERTER_H diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 68e3f78a..534d9905 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CMSWindowsScreen.h" #include "CMSWindowsClipboard.h" #include "CMSWindowsScreenSaver.h" diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 335a5531..89565260 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CMSWINDOWSSCREEN_H #define CMSWINDOWSSCREEN_H diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index 2c4b3760..d08c9b9e 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CMSWindowsScreenSaver.h" #include "CThread.h" #include "CLog.h" diff --git a/lib/platform/CMSWindowsScreenSaver.h b/lib/platform/CMSWindowsScreenSaver.h index 85f95859..f637035e 100644 --- a/lib/platform/CMSWindowsScreenSaver.h +++ b/lib/platform/CMSWindowsScreenSaver.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CMSWINDOWSSCREENSAVER_H #define CMSWINDOWSSCREENSAVER_H diff --git a/lib/platform/CPlatform.cpp b/lib/platform/CPlatform.cpp index c579d216..091cd650 100644 --- a/lib/platform/CPlatform.cpp +++ b/lib/platform/CPlatform.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "common.h" #if WINDOWS_LIKE diff --git a/lib/platform/CPlatform.h b/lib/platform/CPlatform.h index 3ec1db2f..dcfb6b02 100644 --- a/lib/platform/CPlatform.h +++ b/lib/platform/CPlatform.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CPLATFORM_H #define CPLATFORM_H diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 5163aff4..f9e5cbf1 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CSynergyHook.h" #include "ProtocolTypes.h" #include diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h index 4466c5f7..7f306665 100644 --- a/lib/platform/CSynergyHook.h +++ b/lib/platform/CSynergyHook.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CSYNERGYHOOK_H #define CSYNERGYHOOK_H diff --git a/lib/platform/CUnixPlatform.cpp b/lib/platform/CUnixPlatform.cpp index 8bd1f6de..0a6518c2 100644 --- a/lib/platform/CUnixPlatform.cpp +++ b/lib/platform/CUnixPlatform.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CUnixPlatform.h" #include "CLog.h" #include diff --git a/lib/platform/CUnixPlatform.h b/lib/platform/CUnixPlatform.h index 7209411c..91f73565 100644 --- a/lib/platform/CUnixPlatform.h +++ b/lib/platform/CUnixPlatform.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CUNIXPLATFORM_H #define CUNIXPLATFORM_H diff --git a/lib/platform/CWin32Platform.cpp b/lib/platform/CWin32Platform.cpp index b30f0bc6..f2d5c1ba 100644 --- a/lib/platform/CWin32Platform.cpp +++ b/lib/platform/CWin32Platform.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CWin32Platform.h" #include "CLock.h" #include "CThread.h" diff --git a/lib/platform/CWin32Platform.h b/lib/platform/CWin32Platform.h index 09cb542f..644d4408 100644 --- a/lib/platform/CWin32Platform.h +++ b/lib/platform/CWin32Platform.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CWIN32PLATFORM_H #define CWIN32PLATFORM_H diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 29c4270c..39418dc8 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CXWindowsClipboard.h" #include "CXWindowsClipboardTextConverter.h" #include "CXWindowsClipboardUCS2Converter.h" diff --git a/lib/platform/CXWindowsClipboard.h b/lib/platform/CXWindowsClipboard.h index af8ef27b..25c221bd 100644 --- a/lib/platform/CXWindowsClipboard.h +++ b/lib/platform/CXWindowsClipboard.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CXWINDOWSCLIPBOARD_H #define CXWINDOWSCLIPBOARD_H diff --git a/lib/platform/CXWindowsClipboardTextConverter.cpp b/lib/platform/CXWindowsClipboardTextConverter.cpp index ae791730..bd1a520c 100644 --- a/lib/platform/CXWindowsClipboardTextConverter.cpp +++ b/lib/platform/CXWindowsClipboardTextConverter.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CXWindowsClipboardTextConverter.h" #include "CUnicode.h" diff --git a/lib/platform/CXWindowsClipboardTextConverter.h b/lib/platform/CXWindowsClipboardTextConverter.h index b601eea1..3840b7df 100644 --- a/lib/platform/CXWindowsClipboardTextConverter.h +++ b/lib/platform/CXWindowsClipboardTextConverter.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CXWINDOWSCLIPBOARDTEXTCONVERTER_H #define CXWINDOWSCLIPBOARDTEXTCONVERTER_H diff --git a/lib/platform/CXWindowsClipboardUCS2Converter.cpp b/lib/platform/CXWindowsClipboardUCS2Converter.cpp index 1f892f55..86b8d13f 100644 --- a/lib/platform/CXWindowsClipboardUCS2Converter.cpp +++ b/lib/platform/CXWindowsClipboardUCS2Converter.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CXWindowsClipboardUCS2Converter.h" #include "CUnicode.h" diff --git a/lib/platform/CXWindowsClipboardUCS2Converter.h b/lib/platform/CXWindowsClipboardUCS2Converter.h index b2cb0c6f..e848e302 100644 --- a/lib/platform/CXWindowsClipboardUCS2Converter.h +++ b/lib/platform/CXWindowsClipboardUCS2Converter.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CXWINDOWSCLIPBOARDUCS2CONVERTER_H #define CXWINDOWSCLIPBOARDUCS2CONVERTER_H diff --git a/lib/platform/CXWindowsClipboardUTF8Converter.cpp b/lib/platform/CXWindowsClipboardUTF8Converter.cpp index c461a321..7edc850f 100644 --- a/lib/platform/CXWindowsClipboardUTF8Converter.cpp +++ b/lib/platform/CXWindowsClipboardUTF8Converter.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CXWindowsClipboardUTF8Converter.h" // diff --git a/lib/platform/CXWindowsClipboardUTF8Converter.h b/lib/platform/CXWindowsClipboardUTF8Converter.h index 457ad9de..5ac8b153 100644 --- a/lib/platform/CXWindowsClipboardUTF8Converter.h +++ b/lib/platform/CXWindowsClipboardUTF8Converter.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CXWINDOWSCLIPBOARDUTF8CONVERTER_H #define CXWINDOWSCLIPBOARDUTF8CONVERTER_H diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 0b908981..225d9c4e 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CXWindowsScreen.h" #include "CXWindowsClipboard.h" #include "CXWindowsScreenSaver.h" diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index 5d2ca594..f74b8389 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index a3914860..3581ed6a 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CXWindowsScreenSaver.h" #include "CXWindowsScreen.h" #include "CXWindowsUtil.h" diff --git a/lib/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h index f4320087..7e6d4304 100644 --- a/lib/platform/CXWindowsScreenSaver.h +++ b/lib/platform/CXWindowsScreenSaver.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CXWINDOWSSCREENSAVER_H #define CXWINDOWSSCREENSAVER_H diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 60d355bd..2a915e29 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CXWindowsUtil.h" #include "CThread.h" #include "CLog.h" diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h index f6866db4..4faaafb8 100644 --- a/lib/platform/CXWindowsUtil.h +++ b/lib/platform/CXWindowsUtil.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CXWINDOWSUTIL_H #define CXWINDOWSUTIL_H diff --git a/lib/platform/IMSWindowsScreenEventHandler.h b/lib/platform/IMSWindowsScreenEventHandler.h index 134e0082..2ce8b90a 100644 --- a/lib/platform/IMSWindowsScreenEventHandler.h +++ b/lib/platform/IMSWindowsScreenEventHandler.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef IMSWINDOWSSCREENEVENTHANDLER_H #define IMSWINDOWSSCREENEVENTHANDLER_H diff --git a/lib/platform/IPlatform.h b/lib/platform/IPlatform.h index 36644b7c..95d10cdb 100644 --- a/lib/platform/IPlatform.h +++ b/lib/platform/IPlatform.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef IPLATFORM_H #define IPLATFORM_H diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index a618eee8..8da68bf3 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/lib/server/CClientProxy.cpp b/lib/server/CClientProxy.cpp index 372475c1..938fe89d 100644 --- a/lib/server/CClientProxy.cpp +++ b/lib/server/CClientProxy.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CClientProxy.h" #include "IInputStream.h" #include "IOutputStream.h" diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index 46c47953..8890de18 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CCLIENTPROXY_H #define CCLIENTPROXY_H diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index eb76b3cc..656d4ccd 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CClientProxy1_0.h" #include "CServer.h" #include "CClipboard.h" diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 01522865..0c0cb48b 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CCLIENTPROXY1_0_H #define CCLIENTPROXY1_0_H diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 62fcf35e..feb68ccd 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CConfig.h" #include "ProtocolTypes.h" #include "XSocket.h" diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index dfbd4b50..3fe3cf88 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CCONFIG_H #define CCONFIG_H diff --git a/lib/server/CHTTPServer.cpp b/lib/server/CHTTPServer.cpp index e5066fe4..c202d795 100644 --- a/lib/server/CHTTPServer.cpp +++ b/lib/server/CHTTPServer.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CHTTPServer.h" #include "CConfig.h" #include "CHTTPProtocol.h" diff --git a/lib/server/CHTTPServer.h b/lib/server/CHTTPServer.h index 041f029a..18f80440 100644 --- a/lib/server/CHTTPServer.h +++ b/lib/server/CHTTPServer.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CHTTPSERVER_H #define CHTTPSERVER_H diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index 4b909412..c66162ee 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CMSWindowsPrimaryScreen.h" #include "CMSWindowsScreen.h" #include "IPrimaryScreenReceiver.h" diff --git a/lib/server/CMSWindowsPrimaryScreen.h b/lib/server/CMSWindowsPrimaryScreen.h index aa61b492..f443f1e8 100644 --- a/lib/server/CMSWindowsPrimaryScreen.h +++ b/lib/server/CMSWindowsPrimaryScreen.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CMSWINDOWSPRIMARYSCREEN_H #define CMSWINDOWSPRIMARYSCREEN_H diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 638c37c4..4a557fea 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CPrimaryClient.h" #include "IPrimaryScreenFactory.h" #include "IServer.h" diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 0c98e956..10f744a6 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CPRIMARYCLIENT_H #define CPRIMARYCLIENT_H diff --git a/lib/server/CPrimaryScreen.cpp b/lib/server/CPrimaryScreen.cpp index d8f1bc9b..19fd9b1e 100644 --- a/lib/server/CPrimaryScreen.cpp +++ b/lib/server/CPrimaryScreen.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CPrimaryScreen.h" #include "IScreen.h" #include "IScreenReceiver.h" diff --git a/lib/server/CPrimaryScreen.h b/lib/server/CPrimaryScreen.h index 772f88f9..089c6eab 100644 --- a/lib/server/CPrimaryScreen.h +++ b/lib/server/CPrimaryScreen.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CPRIMARYSCREEN_H #define CPRIMARYSCREEN_H diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 0b0bc327..5d1b59c6 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CServer.h" #include "CHTTPServer.h" #include "CPrimaryClient.h" diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 494da9ae..e9a1224f 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CSERVER_H #define CSERVER_H diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp index e1463aee..b673430d 100644 --- a/lib/server/CXWindowsPrimaryScreen.cpp +++ b/lib/server/CXWindowsPrimaryScreen.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CXWindowsPrimaryScreen.h" #include "CXWindowsScreen.h" #include "CXWindowsUtil.h" diff --git a/lib/server/CXWindowsPrimaryScreen.h b/lib/server/CXWindowsPrimaryScreen.h index b9b4ce22..3a870c3e 100644 --- a/lib/server/CXWindowsPrimaryScreen.h +++ b/lib/server/CXWindowsPrimaryScreen.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CXWINDOWSPRIMARYSCREEN_H #define CXWINDOWSPRIMARYSCREEN_H diff --git a/lib/server/IPrimaryScreenFactory.h b/lib/server/IPrimaryScreenFactory.h index ccb62951..630449a5 100644 --- a/lib/server/IPrimaryScreenFactory.h +++ b/lib/server/IPrimaryScreenFactory.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef IPRIMARYSCREENFACTORY_H #define IPRIMARYSCREENFACTORY_H diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index 10979ae6..e874aa37 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/lib/synergy/CClipboard.cpp b/lib/synergy/CClipboard.cpp index 56a2d41a..d82448ac 100644 --- a/lib/synergy/CClipboard.cpp +++ b/lib/synergy/CClipboard.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CClipboard.h" // diff --git a/lib/synergy/CClipboard.h b/lib/synergy/CClipboard.h index d7bfc9f0..55d12938 100644 --- a/lib/synergy/CClipboard.h +++ b/lib/synergy/CClipboard.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CCLIPBOARD_H #define CCLIPBOARD_H diff --git a/lib/synergy/CInputPacketStream.cpp b/lib/synergy/CInputPacketStream.cpp index 7b1b792c..8b0096fc 100644 --- a/lib/synergy/CInputPacketStream.cpp +++ b/lib/synergy/CInputPacketStream.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CInputPacketStream.h" #include "CLock.h" #include "CStopwatch.h" diff --git a/lib/synergy/CInputPacketStream.h b/lib/synergy/CInputPacketStream.h index 5e4762f9..a4122f33 100644 --- a/lib/synergy/CInputPacketStream.h +++ b/lib/synergy/CInputPacketStream.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CINPUTPACKETSTREAM_H #define CINPUTPACKETSTREAM_H diff --git a/lib/synergy/COutputPacketStream.cpp b/lib/synergy/COutputPacketStream.cpp index 59923202..7fbf1ad2 100644 --- a/lib/synergy/COutputPacketStream.cpp +++ b/lib/synergy/COutputPacketStream.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "COutputPacketStream.h" // diff --git a/lib/synergy/COutputPacketStream.h b/lib/synergy/COutputPacketStream.h index dda632c4..7e25d47d 100644 --- a/lib/synergy/COutputPacketStream.h +++ b/lib/synergy/COutputPacketStream.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef COUTPUTPACKETSTREAM_H #define COUTPUTPACKETSTREAM_H diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp index 0853a8fb..30274f08 100644 --- a/lib/synergy/CProtocolUtil.cpp +++ b/lib/synergy/CProtocolUtil.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "CProtocolUtil.h" #include "IInputStream.h" #include "IOutputStream.h" diff --git a/lib/synergy/CProtocolUtil.h b/lib/synergy/CProtocolUtil.h index e277a757..9228a132 100644 --- a/lib/synergy/CProtocolUtil.h +++ b/lib/synergy/CProtocolUtil.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CPROTOCOLUTIL_H #define CPROTOCOLUTIL_H diff --git a/lib/synergy/ClipboardTypes.h b/lib/synergy/ClipboardTypes.h index 478627bf..a638e821 100644 --- a/lib/synergy/ClipboardTypes.h +++ b/lib/synergy/ClipboardTypes.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef CLIPBOARDTYPES_H #define CLIPBOARDTYPES_H diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index 12c534d1..5031bcbc 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ICLIENT_H #define ICLIENT_H diff --git a/lib/synergy/IClipboard.h b/lib/synergy/IClipboard.h index 9ad913eb..25defae6 100644 --- a/lib/synergy/IClipboard.h +++ b/lib/synergy/IClipboard.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ICLIPBOARD_H #define ICLIPBOARD_H diff --git a/lib/synergy/IPrimaryScreenReceiver.h b/lib/synergy/IPrimaryScreenReceiver.h index 1d34dfc3..430b169a 100644 --- a/lib/synergy/IPrimaryScreenReceiver.h +++ b/lib/synergy/IPrimaryScreenReceiver.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef IPRIMARYSCREENRECEIVER_H #define IPRIMARYSCREENRECEIVER_H diff --git a/lib/synergy/IScreen.h b/lib/synergy/IScreen.h index 48417984..16769821 100644 --- a/lib/synergy/IScreen.h +++ b/lib/synergy/IScreen.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ISCREEN_H #define ISCREEN_H diff --git a/lib/synergy/IScreenEventHandler.h b/lib/synergy/IScreenEventHandler.h index 196fbed9..46c05d14 100644 --- a/lib/synergy/IScreenEventHandler.h +++ b/lib/synergy/IScreenEventHandler.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ISCREENEVENTHANDLER_H #define ISCREENEVENTHANDLER_H diff --git a/lib/synergy/IScreenReceiver.h b/lib/synergy/IScreenReceiver.h index 7ce2f7b3..0dd55075 100644 --- a/lib/synergy/IScreenReceiver.h +++ b/lib/synergy/IScreenReceiver.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ISCREENRECEIVER_H #define ISCREENRECEIVER_H diff --git a/lib/synergy/IScreenSaver.h b/lib/synergy/IScreenSaver.h index e0662011..f2da8114 100644 --- a/lib/synergy/IScreenSaver.h +++ b/lib/synergy/IScreenSaver.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ISCREENSAVER_H #define ISCREENSAVER_H diff --git a/lib/synergy/IServer.h b/lib/synergy/IServer.h index b59b7af5..42b7a675 100644 --- a/lib/synergy/IServer.h +++ b/lib/synergy/IServer.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef ISERVER_H #define ISERVER_H diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 0623961a..d66396eb 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef KEYTYPES_H #define KEYTYPES_H diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 3a1b9117..2661667c 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -1,3 +1,15 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + ## Process this file with automake to produce Makefile.in NULL = DEPTH = ../.. diff --git a/lib/synergy/MouseTypes.h b/lib/synergy/MouseTypes.h index 8b6719de..6d68ee52 100644 --- a/lib/synergy/MouseTypes.h +++ b/lib/synergy/MouseTypes.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef MOUSETYPES_H #define MOUSETYPES_H diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index 2920d339..53b6bd1e 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef PROTOCOLTYPES_H #define PROTOCOLTYPES_H diff --git a/lib/synergy/XScreen.cpp b/lib/synergy/XScreen.cpp index 63684f5d..8a694bfa 100644 --- a/lib/synergy/XScreen.cpp +++ b/lib/synergy/XScreen.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "XScreen.h" // diff --git a/lib/synergy/XScreen.h b/lib/synergy/XScreen.h index b69fd61a..3bfa7eb2 100644 --- a/lib/synergy/XScreen.h +++ b/lib/synergy/XScreen.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef XSCREEN_H #define XSCREEN_H diff --git a/lib/synergy/XSynergy.cpp b/lib/synergy/XSynergy.cpp index c8b976f8..c50d542f 100644 --- a/lib/synergy/XSynergy.cpp +++ b/lib/synergy/XSynergy.cpp @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #include "XSynergy.h" // diff --git a/lib/synergy/XSynergy.h b/lib/synergy/XSynergy.h index e836b7de..eb5b5167 100644 --- a/lib/synergy/XSynergy.h +++ b/lib/synergy/XSynergy.h @@ -1,3 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + #ifndef XSYNERGY_H #define XSYNERGY_H From bebef102e54b079b95ee30d904a305dedb56fee0 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 2 Aug 2002 21:55:51 +0000 Subject: [PATCH 316/807] added document files to distribution list and to RPMs. also changed doxygen.cfg to doxygen.cfg.in EXTRA_DIST. --- Makefile.am | 6 +++++- dist/rpm/synergy.spec.in | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index d88626f9..2bd4b9e6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,9 +22,13 @@ SUBDIRS = \ $(NULL) EXTRA_DIST = \ + BUGS \ + FAQ \ + HISTORY \ + PORTING \ all.dsp \ synergy.dsw \ - doc/doxygen.cfg \ + doc/doxygen.cfg.in \ examples/synergy.conf \ $(NULL) diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in index 7e8f886b..81b75a00 100644 --- a/dist/rpm/synergy.spec.in +++ b/dist/rpm/synergy.spec.in @@ -24,9 +24,7 @@ CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=/usr make %install -# FIXME -- install should install doc files make install DESTDIR=$RPM_BUILD_ROOT - strip $RPM_BUILD_ROOT/usr/bin/synergy strip $RPM_BUILD_ROOT/usr/bin/synergyd @@ -37,4 +35,4 @@ rm -rf $RPM_BUILD_ROOT %defattr(-, root, root) /usr/bin/synergy /usr/bin/synergyd -# FIXME -- add doc files +%doc AUTHORS BUGS COPYING ChangeLog FAQ INSTALL NEWS README From ea78e022792c97fe9f84f65dc411c7f1fc77f3fc Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 2 Aug 2002 21:57:03 +0000 Subject: [PATCH 317/807] added some scripts and files not for distribution. --- nodist/addheader | 96 +++++++++++ nodist/copyright | 11 ++ nodist/p4tolog | 31 ++++ nodist/readme | 427 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 565 insertions(+) create mode 100755 nodist/addheader create mode 100644 nodist/copyright create mode 100755 nodist/p4tolog create mode 100644 nodist/readme diff --git a/nodist/addheader b/nodist/addheader new file mode 100755 index 00000000..3411aab0 --- /dev/null +++ b/nodist/addheader @@ -0,0 +1,96 @@ +#!/bin/sh +# add header to file. intended to add copyright info to files. +# +# usage: addheader + +# choose comment character +ext=`echo $2 | sed -e 's/.*\.\([^.]*\)$/\1/'` +case $ext in + c|cpp|h) + openComment='/*' + closeComment=' */' + innerComment=' * ' + ;; + am) + openComment='' + closeComment='' + innerComment='# ' + ;; + m4) + openComment='' + closeComment='' + innerComment='dnl ' + ;; + in) + if test $2 = "configure.in"; then + openComment='' + closeComment='' + innerComment='dnl ' + fi + ;; + *) + ;; +esac +if test -z "$innerComment"; then + echo Unrecognized extension $ext. Exiting. + exit 1 +fi + +# create awk program to strip old header. we have to use a heuristic +# to determine what's a header. we'll strip any continuous comment +# at the start of a file that includes a line with `Copyright'. +body=".$$.ahb" +ocMatch=`echo "$openComment" | sed -e 's/\*/\\\\*/g' -e 's#/#\\\\/#g'` +ccMatch=`echo "$closeComment" | sed -e 's/\*/\\\\*/g' -e 's/^ +/ */' -e 's#/#\\\\/#g'` +icMatch=`echo "$innerComment" | sed -e 's/\*/\\\\*/g' -e 's#/#\\\\/#g'` +awkGetHeader="m==4" +if test -n "$ocMatch"; then + awkGetHeader="$awkGetHeader;m==0 && /${ocMatch}/{m=1}" +else + awkGetHeader="BEGIN{m=1};$awkGetHeader" +fi +if test -n "$ccMatch"; then + awkGetHeader="$awkGetHeader;m==1 && /^${icMatch}/{m=2}" + awkGetHeader="$awkGetHeader;/${ccMatch}/{m=4}" +else + awkGetHeader="m==3 && !/^${icMatch}/{m=4};$awkGetHeader" + awkGetHeader="$awkGetHeader;m==1 && /^${icMatch}/{m=3}" +fi + +# strip old header +awk "$awkGetHeader" $2 > $body +if test $? -ne 0; then + rm -f $body + echo "can't parse file" +fi + +# if body is empty then there was no header +if test ! -s $body; then + cp $2 $body +else + fLength=`cat $2 | wc -l` + bLength=`cat $body | wc -l` + hLength=`expr $fLength - $bLength` + crLine=`grep -n -i "copyright" $2 | sed -e 's/:.*//'` + test -z "$crLine" && crLine=`expr $hLength + 1` + if test $crLine -gt $hLength; then + # no copyright in header + cp $2 $body + fi +fi + +# add new header +echo -n "" > $2 +if test -n "$openComment"; then + echo "$openComment" >> $2 +fi +cat $1 | sed -e "s/^/$innerComment/" >> $2 +if test -n "$closeComment"; then + echo "$closeComment" >> $2 +fi +head -1 $body | tr '\t' ' ' | grep "^ *$" > /dev/null +if test $? -eq 1; then + echo "" >> $2 +fi +cat $body >> $2 +rm -f $body diff --git a/nodist/copyright b/nodist/copyright new file mode 100644 index 00000000..9e64584b --- /dev/null +++ b/nodist/copyright @@ -0,0 +1,11 @@ +synergy -- mouse and keyboard sharing utility +Copyright (C) 2002 Chris Schoeneman + +This package is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +found in the file COPYING that should have accompanied this file. + +This package is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. diff --git a/nodist/p4tolog b/nodist/p4tolog new file mode 100755 index 00000000..476dffaa --- /dev/null +++ b/nodist/p4tolog @@ -0,0 +1,31 @@ +#!/bin/sh +# convert p4 logs to ChangeLog +# +# usage: p4tolog [file[revRange] ...] +# +# changes are written to stdout + +# location of perforce client +P4=/home/perforce/bin/p4 + +# get relevant changes +changes=`$P4 changes $* | sed -e 's/Change \([0-9]*\).*/\1/'` +if test -z "$changes"; then + echo "No changes" + exit 0 +fi + +# convert each change +for change in $changes; do + $P4 describe -s $change | head -1 | \ + sed -e 's/.*by \([^ ]*\) on \([^ ]*\) \([^ ]*\)/\2 \3 \1/' + $P4 describe -s $change | \ + awk 'p==1 && !/^$/;/^Affected/{p=1}' | \ + sed -e 's/^[^ ]* \([^#]*\)#.*$/\1/' + echo + $P4 describe -s $change | \ + awk 'p==1 && !/$^/;/^$/{if (p==1) exit; else p=1}' | \ + sed -e 's/^.//' + echo + echo "----------" +done diff --git a/nodist/readme b/nodist/readme new file mode 100644 index 00000000..847c914f --- /dev/null +++ b/nodist/readme @@ -0,0 +1,427 @@ +synergy +------- +synergy: [noun] a mutually advantageous conjunction of distinct elements + +synergy lets you easily share a single mouse and keyboard between +multiple computers, each with its own display, using software only. +redirecting the mouse and keyboard is as simple as moving the mouse +off the edge of your screen. synergy merges the clipboards of all +the systems into one, allowing cut-and-paste between systems. it +also synchronizes screensavers so they all start and stop together +and, if screen locking is enabled, only one screen requires a +password to unlock them all. + + +system requirements +------------------- +all systems: + keyboard + mouse + TCP/IP networking + +Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family): + ??? MB RAM + +Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family): + ??? MB RAM + +Linux, Unix: + ??? MB RAM + X Windows, revision 4 or up with the XTEST extension + use `xdpyinfo | grep XTEST' to check + + +manifest +-------- + linux windows + ----- ------- + README README this file + synergy synergy.exe the synergy client + synergyd synergyd.exe the synergy server + synrgyhk.dll the synergy hook dll + synergy.conf synergy.conf sample configuration file + + +running synergy +--------------- +synergy is simple to configure. the server uses a configuration file +and command line options while the client uses only command line +options. it's recommended that both the client and server be run in +the foreground until the configuration is verified to work. + +step 1: create a configuration file + edit the sample configuration file. there are two sections you + must fill in and a third optional section. you should delete + the existing lines inside the sections. + + in the "screens" section, add a line for each computer you'll + be using (server and clients). put the hostname of the computer + followed by a colon (with no space in between). the computers + can be listed in any order. + + in the "links" section you define how screens are connected. + each screen is listed as in the "screens" section except + following each screen is a list of links to other screens in + the form " = " where is "left", + "right", "up", or "down" and is a screen listed in + the "screens" section. + + as an example, if we have "left=foo" under the "bar" screen + then screen "foo" is on the left of screen "bar". the user + will be able to move the mouse off the left edge of "foo" and + will appear on the opposite (right) edge of "bar". note that + it's entirely possible to have one-way (asymmetric) links and + screens with only links into them. the latter should be + avoided since there's no way to move the mouse off those + screens. + + in the "aliases" section you can list other names for each + screen. this is especially useful for dealing with fully + qualified domain names versus simple hostnames. + +step 2: start the server + the server is the system with the mouse and keyboard to be + shared. each platform has its own tradeoffs when running as + the server. see the release notes below for more information. + + run the synergy server on the server system using the following + command line: + + synergyd -f --config + + replacing with the path to the configuration + file. you can use `synergyd --help' for a list of command line + options. + +step 3: start the clients + on each client system start the synergy client using the + following command line: + + synergy -f --debug INFO --no-camp + + replacing with the hostname or address of the + server system. + + the client should quickly report `connected to server'. if it + does not but doesn't print an error and exit immeditately then + it's trying to connect to the server but cannot. it will time + out in 30 seconds and exit (use ctrl+c to exit earlier). you + should check that the server is running and try again. + + otherwise, if the client doesn't connect it should print an + error describing the problem. here are typical problems and + possible solutions: + + failed to open screen: + check permission to open the X display; + check that the DISPLAY environment variable is set. + already connected: + check that synergy isn't already running. + refused client: + add client to the server's configuration file. + connection failed: + check server-hostname; + the server cannot open the desired port, stop the + program using that port (24800) and restart the + server. + +step 4: verify the configuration + once the clients are connected, use the mouse to check that + the screens are properly linked. moving the mouse off the + edge of a screen with a link should cause it to appear on + the opposite edge of the linked-to screen. + + +using synergy +------------- +using synergy is very easy. once clients have connected to the +server all you do to redirect keyboard and mouse input to a screen +(i.e. switch screens) is move the mouse cursor off the edge of the +screen you're on. which edges go to which screens depends on the +configuration. + +clients can be connected and disconnected at any time. until a +client is connected, switching to it works as if you switched to +it then moved all the way across it in the same direction and +switched to the next screen. this repeats until you reach a +connected screen. if there is no connected screen in that +direction then the mouse will not leave the starting screen. + +disconnecting a client while the mouse is on it causes the mouse +to instantly jump to the center of the server screen. + +the clipboard is automatically transferred between screens. if +you copy on one screen you just switch to another screen and paste. +note that X Windows has two major clipboards: the primary +selection and the clipboard. synergy supports both. however, +Microsoft Windows only supports the clipboard. the Windows +clipboard is transferred to both the X primary selection and the +clipboard. whichever X clipboard was changed last becomes the +Windows clipboard. end-of-line sequences (LF on linux and unix, +CRLF on Windows) are automatically converted as necessary. the +clipboards are transferred using Unicode; if your platforms and +applications understand Unicode then you should be able to cut +and paste any Unicode character. + +synergy synchronizes screensavers. the screensavers on client +screens are disabled when they connect to the server. when the +primary screen's screensaver starts, the screensaver on each +secondary screen starts too. all the secondary screensavers are +stopped when the primary screensaver stops. moving the mouse or +pressing a key will stop the primary screensaver, regardless of +which screen the mouse was on when the screensavers started. if +the primary screensaver requires a password to unlock then the +user is prevented from switching to the secondary screens until +the primary screen is unlocked. + + +installing as a daemon/service +------------------------------ +synergy can run in the foreground or as a daemon/service. it's +recommended that you run it in the foreground until you've sorted +out your configuration. + +on the Windows NT family you cannot run a service directly. +instead you install the service then run or stop it via the +Services control panel. on the Windows 95 family, you can use +the `--daemon' command line option to start synergy as a service +or you can install the service and restart your computer. + +in the text below, except where noted, synergy refers to the +client and/or the server. + +windows: + to install synergy just run one of the following: + + synergy --install [other command line options] + synergyd --install [other command line options] + + the client/server is installed as a service and the command + line is saved and used when starting the service. the system + will expect to find the program wherever it was when you used + the --install option so make sure it's not on a network share + from another system because the network share will not be + available at boot time. synergyd will also try to load + synrgyhk.dll so that should be in the same directory as + synergyd.exe. + + note that when installing the client you must provide the + server hostname argument. to change the arguments you must + first uninstall then reinstall. + + you must also install the configuration file along with the + server. it's recommended that you put it in the windows + directory (e.g. C:\WINNT) and call it "synergy.sgc". the + server will automatically find this file. however, you can + also use the --config command line option and specify an + *absolute* path to the file. remember that this file must be + accessible when the system starts up, before network shares + are mapped. + + to uninstall use: + + synergy --uninstall + synergyd --uninstall + +linux, unix: + before starting synergy as a daemon you should understand that + synergy requires an X server that it can connect to. synergy + can start before the X server does and will repeatly attempt to + connect to the X server until it succeeds. however, if the + server requires authorization then it's unlikely that synergy + will ever succeed. so, in general, synergy should be (re)started + by the X display manager. + + some display managers (xdm and kdm, but not gdm) grab the + keyboard and do not release it until the user logs in, also + for security reasons. this prevents a synergy server from + sharing the mouse and keyboard until the user logs in but + it doesn't prevent a synergy client from synthesizing mouse + and keyboard input. + + you should modify xdm's Xsetup script to start the synergy + client or server. for example, somewhere near the bottom of + Xsetup (but someplace before anywhere the script calls exit) + you might add: + /usr/bin/killall synergy + /usr/sbin/synergy 192.168.1.101 + this assumes synergy is installed in /usr/sbin. these lines + make sure any already running synergy is terminated and starts + a fresh copy. it's important to kill old copies so that you + don't end up with multiple synergy instances fighting each + other or, at the very least, using up system resources. + + to start the synergy server you might use: + /usr/bin/killall synergyd + /usr/sbin/synergyd --config /root/synergy.conf + assuming synergyd is installed in /usr/sbin. if you've put + the configuration data in /etc/synergy.conf then you don't + need the --config option. + + another option is to put the synergy startup in .Xsession in + your home directory. that allows users without root access to + start synergy when they login. in this case synergy will not + be running while on the login screen. + + +common command line options +--------------------------- + -d, --debug use debugging level + --daemon run as a daemon (linux,unix) or background (windows) + -f, --no-daemon run in the foreground + -n, --name use instead of the hostname + --restart automatically restart on failures + -1, --no-restart do not restart on failure + -h, --help print help and exit + --version print version information and exit + --install install as a service (windows) + --uninstall uninstall service (windows) + +debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, +INFO, DEBUG, DEBUG1, and DEBUG2. only messages at or above the given +level are logged. messages are logged to a terminal window when +running in the foreground. unix logs messages to syslog when running +as a daemon. the Windows NT family logs messages to the event log +when running as a service. the Windows 95 family shows FATAL log +messages in a message box and others in a terminal window when running +as a service. + +the `--name' option lets the client or server use a name other than +its hostname for its screen. this name is used when checking the +configuration. + +neither the client nor server will automatically restart if an error +occurs that is sure to happen every time. for example, the server +will exit immediately if it can't find itself in the screen +configuration. on X11 both the client and server will also terminate +if the connection to the X server is lost. since xdm will normally +restart the server and then synergy this is the correct behavior. + + +server command line options +--------------------------- + -a, --address
listen for connections on the given address + -c, --config read configuration from + +
has one of the following forms: + + : + : + is a hostname or address of a network interface on the +server system. is a port number from 1 to 65535. +defaults to the system's hostname and defaults to 24800. + + +client command line options +--------------------------- + --camp retry connection to server until successful + --no-camp try connection to server only once +
address of server + +see the "server command line options" for a description of
+but note that there is no default though there is a +default . + + +release notes +------------- +synergy does not yet fully capture all possible input or have full +control over the mouse and keyboard on all platforms. each platform +has its own limitations and these limitations may influence your +choice for the server. + +the following lists enumerate the limitations of each platform. a +key (combination) that cannot be captured is not detected by synergy. +a key (combination) that cannot be blocked will be passed through to +the server system even when the mouse is on a client system. if a +key cannot be captured then it also cannot be blocked. + +windows 95 family, windows NT prior to service pack 3: + * cannot capture: + * ctrl+alt+del + * ctrl+esc + * alt+[shift+]tab + * alt+[shift+]esc + * windows+E + * windows+[ctrl+]F + * windows+[shift+]M + * windows+R + * windows+F1 + * windows+tab + * windows+break + * accessibility shortcuts (e.g. press shift 5 times for sticky keys) + * the individual keys are captured but the dialogs still appear + * cannot synthesize: + * accessibility shortcuts + +windows NT family (except NT prior to SP3): + * cannot block: + * ctrl+alt+del + * accessibility shortcuts (e.g. press shift 5 times for sticky keys) + * the individual keys are captured but the dialogs still appear + * cannot synthesize: + * accessibility shortcuts + +linux, unix: + * cannot capture: + * ctrl+alt+del + * ctrl+alt+backspace (only if used by the X server) + * ctrl+alt+keypad_plus (only if used by the X server) + * ctrl+alt+keypad_minus (only if used by the X server) + * keyboard/mouse grabs prevent switching screens for their duration + * some display managers grab the keyboard until login + +currently, the windows NT family (except NT prior to SP3) makes the +best server. + + +known bugs +---------- +all: + * non-ASCII keyboard characters are not supported + +windows: + * screen flashes when entering the screen + * synergy may interfere with desktop switcher programs. however, + synergy understands and handles multiple desktops. + * there should be a control panel + * there should be a taskbar icon + +windows 95 family: + * typing into a console window can be slow + +windows NT family: + * the event viewer reports a message lookup error for synergy logs. + however, the full synergy message is in the extra data portion of + the event dialog. + * does not gracefully handle NoInteractiveServices being enabled + +linux: + * some keyboards have toggle keys that toggle on on key press and + toggle off on the key release after the next key press. synergy + doesn't handle these properly. + * shift-lock (as opposed to caps-lock) is not supported + * large (~256k) motif clipboard items are not copied properly + + +tips and tricks +--------------- +* a screen can be its own neighbor. that allows a screen to "wrap". + for example, if a configuration linked the left and right sides of + a screen to itself then moving off the left of the screen would put + the mouse at the right of the screen and vice versa. + +* you cannot switch screens when a key or mouse button is pressed. + +* you cannot switch screens when the scroll lock it toggled on. use + this to prevent unintentional switching. + +* turn off mouse driven virtual desktop switching on X windows. it + will interfere with synergy. use keyboard shortcuts instead. + +* synergy's screensaver synchronization works best with xscreensaver + under X windows. synergy works better with xscreensaver if it is + using of the screensaver extensions. prior to xscreensaver 4.0 + you can use `-mit-extension', `-sgi-extension', or `-xidle-extension' + command line options to enable an extension. starting with 4.0 + you must enable the corresponding option in your .xscreensaver file. From 3d41e1c7fd4da577904fcd40351542655df13d1d Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 2 Aug 2002 21:57:29 +0000 Subject: [PATCH 318/807] checkpointing notes. --- nodist/notes | 130 ++++++++++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 58 deletions(-) diff --git a/nodist/notes b/nodist/notes index ff76a503..ec9f5296 100644 --- a/nodist/notes +++ b/nodist/notes @@ -1,47 +1,26 @@ -final preparation for release: - create files: - INSTALL -- configuration, build, and installation instructions - NEWS -- recent project news - COPYING -- pointer to LICENSE - AUTHORS -- list of contributors - ChangeLog - LICENSE -- license - MANIFEST -- list of files in distribution (?) - FAQ -- frequently asked questions for project - HISTORY -- project history - -add copyright to every file - -change README: - should have: - A brief description of the project. - A pointer to the project website (if it has one) - Notes on the developer's build environment and potential portability problems. - A roadmap describing important files and subdirectories. - Either build/installation instructions or a pointer to a file containing same (usually INSTALL). - Either a maintainers/credits list or a pointer to a file containing same (usually CREDITS). - Either recent project news or a pointer to a file containing same (usually NEWS). +edit FIXME's in: + PORTING make man pages docbook? should have makefile target to build these + remove line from BUGS about missing man pages -add make rules to install files +--- +not removing all doxygen generated files in dist-clean or maintainer-clean + should remove them all in dist-clean -custom make targets: - make doxygen documents - make binary and/or source rpm - should auto generate spec file, pulling in proper version number - source rpm should use BuildRoot to build in /tmp or /var/tmp - create tarball: - foobar-$(VERS).tar.gz: - @ls $(SRC) | sed s:^:foobar-$(VERS)/: >MANIFEST - @(cd ..; ln -s foobar foobar-$(VERS)) - (cd ..; tar -czvf foobar/foobar-$(VERS).tar.gz `cat foobar/MANIFEST`) - @(cd ..; rm foobar-$(VERS)) - -configure not caching results - is that normal or did we disable it unintentionally +--- +check for cancellation points: + following functions may be interrupted (returning -1, errno=EINTR): + read(), write(), ioctl(), select(), poll(), pause(), connect(), + accept(), syslog(), TCP operations + creat(), close(), getmsg(), putmsg(), msgrcv(), msgsnd(), + recv(), send(), wait(), waitpid(), wait3(), sigpause() + should ensure that we handle EINTR anywhere these are called. in + particular we must call testCancel() (should do that anyway) and + sometimes restart the call. + make sure unbounded time win32 calls do testCancel(). --- not handling large motif selections @@ -350,8 +329,8 @@ automake stuff * 'touch NEWS README AUTHORS ChangeLog' * 'touch stamp-h' * 'aclocal' - creates aclocal.m4 - * 'autoconf' - creates configure from configure.in * 'autoheader' - creates config.h.in + * 'autoconf' - creates configure from configure.in * 'automake' - Creates Makefile.in from Makefile.am * './configure' - creates Makefile from Makefile.in * 'make' @@ -371,26 +350,47 @@ automake stuff use AC_CHECK_FUNC or AC_CHECK_FUNCS to detect missing functions use AC_CHECK_LIB first if function not in std C lib - maybe AC_CHECK_TYPE for size_t and ssize_t + AC_FUNC_MEMCMP -- adds memcmp.o to LIBOBJS if memcmp is broken + AC_FUNC_STRFTIME -- check for strftime(), define HAVE_STRFTIME + AC_FUNC_VPRINTF -- check for vprintf(), define HAVE_VPRINTF + also checks for _doprnt(), define HAVE_DOPRNT -replace sys/time.h with: -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif + #if STDC_HEADERS + # include + #else + # if !HAVE_STRCHR + # define strchr index + # define strrchr rindex + # endif + char *strchr (), *strrchr (); + # if !HAVE_MEMCPY + # define memcpy(d, s, n) bcopy ((s), (d), (n)) + # define memmove(d, s, n) bcopy ((s), (d), (n)) + # endif + #endif - what does `make install' do? - - check in derived files? - aclocal.m4 - config.h.in - configure +must provide fallbacks for stuff: + non-reentrant functions: + put (shared) lock around non-reentrant function + provide reentrant API + gethostbyaddr + gethostbyname + getprotobyname + getprotobynumber + getservbyname + getservbyport + gmtime + getpwuid + strerror + string/memory functions: + memcpy + memcmp + memmove + memset + strcat + strcmp + strcpy + strrchr used library functions: accept @@ -399,8 +399,11 @@ used library functions: close connect dup - fork + exit + fcntl +* fork fprintf +* free getenv gethostbyaddr gethostbyname @@ -408,16 +411,22 @@ used library functions: getpeername getprotobyname getprotobynumber + getpwuid_r getservbyname getservbyport getsockname getsockopt gettimeofday + getuid inet_addr inet_ntoa ioctl listen +* malloc + mbrtowc + mbsinit memcpy +! memcmp memmove memset nanosleep @@ -435,15 +444,20 @@ used library functions: sigaction sigaddset sigemptyset + sigwait socket sprintf strcat strcmp + strcpy strerror strrchr + strtol + sysconf syslog umask vsnprintf + wcrtomb write included files From 435bb738e6075912b0e9330406ffd37b2a8f6830 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Aug 2002 11:49:36 +0000 Subject: [PATCH 319/807] added win32 launcher program. also changed VC++ dsp and dsw files to binary form so \r\n aren't converted. added icons to client and server apps on win32. --- all.dsp | 126 +-- cmd/Makefile.am | 1 + cmd/launcher/Makefile.am | 28 + cmd/launcher/launcher.cpp | 1183 +++++++++++++++++++++++++++++ cmd/launcher/launcher.dsp | 122 +++ cmd/launcher/launcher.rc | 218 ++++++ cmd/launcher/resource.h | 62 ++ cmd/launcher/synergy.ico | Bin 0 -> 1078 bytes cmd/synergy/Makefile.am | 5 +- cmd/synergy/resource.h | 50 +- cmd/synergy/synergy.cpp | 175 +++-- cmd/synergy/synergy.dsp | 238 +++--- cmd/synergy/synergy.ico | Bin 0 -> 1078 bytes cmd/synergy/synergy.rc | 214 +++--- cmd/synergyd/Makefile.am | 3 +- cmd/synergyd/resource.h | 50 +- cmd/synergyd/synergy.ico | Bin 0 -> 1078 bytes cmd/synergyd/synergyd.cpp | 213 +++--- cmd/synergyd/synergyd.dsp | 238 +++--- cmd/synergyd/synergyd.rc | 215 +++--- configure.in | 2 +- lib/base/CString.cpp | 3 +- lib/base/Version.h | 4 +- lib/base/base.dsp | 408 +++++----- lib/client/client.dsp | 268 +++---- lib/http/http.dsp | 220 +++--- lib/io/io.dsp | 308 ++++---- lib/mt/mt.dsp | 292 +++---- lib/net/net.dsp | 324 ++++---- lib/platform/CMSWindowsScreen.cpp | 2 +- lib/platform/makehook.dsp | 126 +-- lib/platform/platform.dsp | 350 ++++----- lib/platform/synrgyhk.dsp | 232 +++--- lib/server/CConfig.cpp | 124 ++- lib/server/CConfig.h | 22 +- lib/server/server.dsp | 332 ++++---- lib/synergy/libsynergy.dsp | 388 +++++----- synergy.dsw | 520 +++++++------ 38 files changed, 4463 insertions(+), 2603 deletions(-) create mode 100644 cmd/launcher/Makefile.am create mode 100644 cmd/launcher/launcher.cpp create mode 100644 cmd/launcher/launcher.dsp create mode 100644 cmd/launcher/launcher.rc create mode 100644 cmd/launcher/resource.h create mode 100644 cmd/launcher/synergy.ico create mode 100644 cmd/synergy/synergy.ico create mode 100644 cmd/synergyd/synergy.ico diff --git a/all.dsp b/all.dsp index 0c7635eb..6f01a095 100644 --- a/all.dsp +++ b/all.dsp @@ -1,63 +1,63 @@ -# Microsoft Developer Studio Project File - Name="all" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Generic Project" 0x010a - -CFG=all - Win32 Debug -!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 "all.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 "all.mak" CFG="all - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "all - Win32 Release" (based on "Win32 (x86) Generic Project") -!MESSAGE "all - Win32 Debug" (based on "Win32 (x86) Generic Project") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -MTL=midl.exe - -!IF "$(CFG)" == "all - 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" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "all - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "all___Win32_Debug" -# PROP BASE Intermediate_Dir "all___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "all - Win32 Release" -# Name "all - Win32 Debug" -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="all" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=all - Win32 Debug +!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 "all.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 "all.mak" CFG="all - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "all - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "all - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "all - 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" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "all - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "all___Win32_Debug" +# PROP BASE Intermediate_Dir "all___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "all - Win32 Release" +# Name "all - Win32 Debug" +# End Target +# End Project diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 3fe79050..e267fba6 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -16,6 +16,7 @@ DEPTH = .. VDEPTH = ./$(VPATH)/$(DEPTH) SUBDIRS = \ + launcher \ synergy \ synergyd \ $(NULL) diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am new file mode 100644 index 00000000..79e6a7e4 --- /dev/null +++ b/cmd/launcher/Makefile.am @@ -0,0 +1,28 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + launcher.cpp \ + launcher.dsp \ + launcher.rc \ + resource.h \ + synergy.ico \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp new file mode 100644 index 00000000..e8d96cc6 --- /dev/null +++ b/cmd/launcher/launcher.cpp @@ -0,0 +1,1183 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "ProtocolTypes.h" +#include "CPlatform.h" +#include "CNetwork.h" +#include "Version.h" +#include "stdfstream.h" +#include "stdvector.h" +#include "resource.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +#define CONFIG_NAME "synergy.sgc" +#define CLIENT_APP "synergyc.exe" +#define SERVER_APP "synergyd.exe" + +typedef std::vector CStringList; + +class CScreenInfo { +public: + CString m_screen; + CStringList m_aliases; +}; + +class CChildWaitInfo { +public: + HWND m_dialog; + HANDLE m_child; + DWORD m_childID; + HANDLE m_ready; + HANDLE m_stop; +}; + +static const TCHAR* s_mainClass = TEXT("GoSynergy"); +static const TCHAR* s_layoutClass = TEXT("SynergyLayout"); +static HINSTANCE s_instance = NULL; + +static HWND s_mainWindow; +static CConfig s_config; +static CConfig s_oldConfig; +static CStringList s_screens; + +static +BOOL CALLBACK +addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +static +void +tokenize(CStringList& tokens, const CString& src) +{ + // find first non-whitespace + CString::size_type x = src.find_first_not_of(" \t\r\n"); + if (x == CString::npos) { + return; + } + + // find next whitespace + do { + CString::size_type y = src.find_first_of(" \t\r\n", x); + if (y == CString::npos) { + y = src.size(); + } + tokens.push_back(src.substr(x, y - x)); + x = src.find_first_not_of(" \t\r\n", y); + } while (x != CString::npos); +} + +static +bool +isNameInList(const CStringList& names, const CString& name) +{ + for (CStringList::const_iterator index = names.begin(); + index != names.end(); ++index) { + if (CStringUtil::CaselessCmp::equal(name, *index)) { + return true; + } + } + return false; +} + +static +CString +getString(DWORD id) +{ + char buffer[1024]; + buffer[0] = '\0'; + LoadString(s_instance, id, buffer, sizeof(buffer) / sizeof(buffer[0])); + return buffer; +} + +static +CString +getErrorString(DWORD error) +{ + char* buffer; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&buffer, + 0, + NULL) == 0) { + return getString(IDS_ERROR); + } + else { + CString result(buffer); + LocalFree(buffer); + return result; + } +} + +static +void +showError(HWND hwnd, const CString& msg) +{ + CString title = getString(IDS_ERROR); + MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); +} + +static +void +askOkay(HWND hwnd, const CString& title, const CString& msg) +{ + MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); +} + +static +bool +askVerify(HWND hwnd, const CString& msg) +{ + CString title = getString(IDS_VERIFY); + int result = MessageBox(hwnd, msg.c_str(), + title.c_str(), MB_OKCANCEL | MB_APPLMODAL); + return (result == IDOK); +} + +static +CString +getWindowText(HWND hwnd) +{ + LRESULT size = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0); + char* buffer = new char[size + 1]; + SendMessage(hwnd, WM_GETTEXT, size + 1, (LPARAM)buffer); + buffer[size] = '\0'; + CString result(buffer); + delete[] buffer; + return result; +} + +static +void +enableItem(HWND hwnd, int id, bool enabled) +{ + EnableWindow(GetDlgItem(hwnd, id), enabled); +} + +static +bool +isClientChecked(HWND hwnd) +{ + HWND child = GetDlgItem(hwnd, IDC_MAIN_CLIENT_RADIO); + return (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED); +} + +static +void +enableScreensControls(HWND hwnd) +{ + // decide if edit and remove buttons should be enabled + bool client = isClientChecked(hwnd); + bool screenSelected = false; + if (!client) { + HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + if (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR) { + screenSelected = true; + } + } + + // enable/disable controls + enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LABEL, !client); + enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST, !client); + enableItem(hwnd, IDC_MAIN_SERVER_ADD_BUTTON, !client); + enableItem(hwnd, IDC_MAIN_SERVER_EDIT_BUTTON, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_REMOVE_BUTTON, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_LAYOUT_LABEL, !client); + enableItem(hwnd, IDC_MAIN_SERVER_LEFT_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_RIGHT_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_TOP_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_BOTTOM_COMBO, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_LEFT_LABEL, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_RIGHT_LABEL, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_TOP_LABEL, screenSelected); + enableItem(hwnd, IDC_MAIN_SERVER_BOTTOM_LABEL, screenSelected); +} + +static +void +enableMainWindowControls(HWND hwnd) +{ + bool client = isClientChecked(hwnd); + enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_LABEL, client); + enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT, client); + enableScreensControls(hwnd); +} + +static +void +updateNeighbor(HWND hwnd, const CString& screen, EDirection direction) +{ + // remove all neighbors from combo box + SendMessage(hwnd, CB_RESETCONTENT, 0, 0); + + // add all screens to combo box + if (!screen.empty()) { + for (CConfig::const_iterator index = s_config.begin(); + index != s_config.end(); ++index) { + SendMessage(hwnd, CB_INSERTSTRING, + (WPARAM)-1, (LPARAM)index->c_str()); + } + } + + // add empty neighbor to combo box + SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"---"); + + // select neighbor in combo box + LRESULT index = 0; + if (!screen.empty()) { + const CString& neighbor = s_config.getNeighbor(screen, direction); + if (!neighbor.empty()) { + index = SendMessage(hwnd, CB_FINDSTRINGEXACT, + 0, (LPARAM)neighbor.c_str()); + if (index == LB_ERR) { + index = 0; + } + } + } + SendMessage(hwnd, CB_SETCURSEL, index, 0); +} + +static +void +updateNeighbors(HWND hwnd) +{ + // get selected screen name or empty string if no selection + CString screen; + HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index != LB_ERR) { + screen = s_screens[index]; + } + + // set neighbor combo boxes + child = GetDlgItem(hwnd, IDC_MAIN_SERVER_LEFT_COMBO); + updateNeighbor(child, screen, kLeft); + child = GetDlgItem(hwnd, IDC_MAIN_SERVER_RIGHT_COMBO); + updateNeighbor(child, screen, kRight); + child = GetDlgItem(hwnd, IDC_MAIN_SERVER_TOP_COMBO); + updateNeighbor(child, screen, kTop); + child = GetDlgItem(hwnd, IDC_MAIN_SERVER_BOTTOM_COMBO); + updateNeighbor(child, screen, kBottom); +} + +static +void +addScreen(HWND hwnd) +{ + // empty screen info + CScreenInfo info; + + // run dialog + if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), + hwnd, addDlgProc, (LPARAM)&info) != 0) { + // get current number of screens + UInt32 i = s_screens.size(); + + // add screen to list control + HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + CString item = CStringUtil::print("%d. %s", + i + 1, info.m_screen.c_str()); + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); + + // add screen to screen list + s_screens.push_back(info.m_screen); + + // add screen to config + s_config.addScreen(info.m_screen); + + // add aliases to config + for (CStringList::const_iterator index = info.m_aliases.begin(); + index != info.m_aliases.end(); ++index) { + s_config.addAlias(info.m_screen, *index); + } + + // update neighbors + updateNeighbors(hwnd); + enableScreensControls(hwnd); + } +} + +static +void +editScreen(HWND hwnd) +{ + // get selected list item + HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + // no selection + return; + } + + // fill in screen info + CScreenInfo info; + info.m_screen = s_screens[index]; + for (CConfig::all_const_iterator index = s_config.beginAll(); + index != s_config.endAll(); ++index) { + if (CStringUtil::CaselessCmp::equal(index->second, info.m_screen) && + !CStringUtil::CaselessCmp::equal(index->second, index->first)) { + info.m_aliases.push_back(index->first); + } + } + + // save current info + CScreenInfo oldInfo = info; + + // run dialog + if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), + hwnd, addDlgProc, (LPARAM)&info) != 0) { + // replace screen + s_screens[index] = info.m_screen; + + // remove old aliases + for (CStringList::const_iterator index = oldInfo.m_aliases.begin(); + index != oldInfo.m_aliases.end(); ++index) { + s_config.removeAlias(*index); + } + + // replace name + s_config.renameScreen(oldInfo.m_screen, info.m_screen); + + // add new aliases + for (CStringList::const_iterator index = info.m_aliases.begin(); + index != info.m_aliases.end(); ++index) { + s_config.addAlias(info.m_screen, *index); + } + + // update list + CString item = CStringUtil::print("%d. %s", + index + 1, info.m_screen.c_str()); + SendMessage(child, LB_DELETESTRING, index, 0); + SendMessage(child, LB_INSERTSTRING, index, + (LPARAM)item.c_str()); + SendMessage(child, LB_SETCURSEL, index, 0); + + // update neighbors + updateNeighbors(hwnd); + } +} + +static +void +removeScreen(HWND hwnd) +{ + // get selected list item + HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + // no selection + return; + } + + // get screen name + CString name = s_screens[index]; + + // remove screen from list control + SendMessage(child, LB_DELETESTRING, index, 0); + + // remove screen from screen list + s_screens.erase(&s_screens[index]); + + // remove screen from config (this also removes aliases) + s_config.removeScreen(name); + + // update neighbors + updateNeighbors(hwnd); + enableScreensControls(hwnd); +} + +static +void +changeNeighbor(HWND hwnd, HWND combo, EDirection direction) +{ + // get selected screen + HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + // no selection + return; + } + + // get screen name + CString screen = s_screens[index]; + + // get selected neighbor + index = SendMessage(combo, CB_GETCURSEL, 0, 0); + + // remove old connection + s_config.disconnect(screen, direction); + + // add new connection + if (index != LB_ERR && index != 0) { + LRESULT size = SendMessage(combo, CB_GETLBTEXTLEN, index, 0); + char* neighbor = new char[size + 1]; + SendMessage(combo, CB_GETLBTEXT, index, (LPARAM)neighbor); + s_config.connect(screen, direction, CString(neighbor)); + delete[] neighbor; + } +} + +static +bool +execApp(const char* app, const CString& cmdLine, PROCESS_INFORMATION* procInfo) +{ + // prepare startup info + STARTUPINFO startup; + startup.cb = sizeof(startup); + startup.lpReserved = NULL; + startup.lpDesktop = NULL; + startup.lpTitle = NULL; + startup.dwX = (DWORD)CW_USEDEFAULT; + startup.dwY = (DWORD)CW_USEDEFAULT; + startup.dwXSize = (DWORD)CW_USEDEFAULT; + startup.dwYSize = (DWORD)CW_USEDEFAULT; + startup.dwXCountChars = 0; + startup.dwYCountChars = 0; + startup.dwFillAttribute = 0; + startup.dwFlags = STARTF_FORCEONFEEDBACK; + startup.wShowWindow = SW_SHOWDEFAULT; + startup.cbReserved2 = 0; + startup.lpReserved2 = NULL; + startup.hStdInput = NULL; + startup.hStdOutput = NULL; + startup.hStdError = NULL; + + // prepare path to app + CPlatform platform; + char myPathname[MAX_PATH]; + GetModuleFileName(s_instance, myPathname, MAX_PATH); + const char* myBasename = platform.getBasename(myPathname); + CString appPath = CString(myPathname, myBasename - myPathname); + appPath += app; + + // start child + if (CreateProcess(appPath.c_str(), + (char*)cmdLine.c_str(), + NULL, + NULL, + FALSE, + CREATE_DEFAULT_ERROR_MODE | + CREATE_NEW_PROCESS_GROUP | + NORMAL_PRIORITY_CLASS, + NULL, + NULL, + &startup, + procInfo) == 0) { + return false; + } + else { + return true; + } +} + +static +bool +uninstallApp(const char* app) +{ + PROCESS_INFORMATION procInfo; + + // uninstall + DWORD exitCode = kExitFailed; + if (execApp(app, "-z --uninstall", &procInfo)) { + WaitForSingleObject(procInfo.hProcess, INFINITE); + GetExitCodeProcess(procInfo.hProcess, &exitCode); + CloseHandle(procInfo.hProcess); + CloseHandle(procInfo.hThread); + } + + return (exitCode == kExitSuccess); +} + +static +HANDLE +launchApp(HWND hwnd, bool testing, DWORD* threadID) +{ + // decide if client or server + const bool isClient = isClientChecked(hwnd); + const char* app = isClient ? CLIENT_APP : SERVER_APP; + + // get and verify screen name + HWND child = GetDlgItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); + CString name = getWindowText(child); + if (!s_config.isValidScreenName(name)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + name.c_str())); + SetFocus(child); + return NULL; + } + if (!isClient && !s_config.isScreen(name)) { + showError(hwnd, CStringUtil::format( + getString(IDS_UNKNOWN_SCREEN_NAME).c_str(), + name.c_str())); + SetFocus(child); + return NULL; + } + + // get and verify port + child = GetDlgItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT); + CString portString = getWindowText(child); + UInt32 port = (UInt32)atoi(portString.c_str()); + if (port < 1 || port > 65535) { + CString defaultPortString = CStringUtil::print("%d", kDefaultPort); + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_PORT).c_str(), + portString.c_str(), + defaultPortString.c_str())); + SetFocus(child); + return NULL; + } + + // prepare command line + CString cmdLine; + if (testing) { + cmdLine += " -z --no-restart --no-daemon"; + } + cmdLine += " --name "; + cmdLine += name; + if (isClient) { + // check server name + child = GetDlgItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + CString server = getWindowText(child); + if (!s_config.isValidScreenName(server)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + server.c_str())); + SetFocus(child); + return NULL; + } + + if (testing) { + cmdLine += " --no-camp"; + } + cmdLine += " "; + cmdLine += server; + cmdLine += ":"; + cmdLine += portString; + } + else { + cmdLine += " --address :"; + cmdLine += portString; + } + + // uninstall client and server then reinstall one of them + if (!testing) { + // uninstall client and server + uninstallApp(CLIENT_APP); + uninstallApp(SERVER_APP); + + // install client or server + PROCESS_INFORMATION procInfo; + DWORD exitCode = kExitFailed; + if (execApp(app, CString("-z --install") + cmdLine, &procInfo)) { + WaitForSingleObject(procInfo.hProcess, INFINITE); + GetExitCodeProcess(procInfo.hProcess, &exitCode); + CloseHandle(procInfo.hProcess); + CloseHandle(procInfo.hThread); + } + + // see if install succeeded + if (exitCode != kExitSuccess) { + showError(hwnd, getString(IDS_INSTALL_FAILED).c_str()); + return NULL; + } + } + + // start child + PROCESS_INFORMATION procInfo; + if (!execApp(app, cmdLine, &procInfo)) { + showError(hwnd, CStringUtil::format( + getString(IDS_STARTUP_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + return NULL; + } + + // don't need process handle + CloseHandle(procInfo.hProcess); + + // save thread ID if desired + if (threadID != NULL) { + *threadID = procInfo.dwThreadId; + } + + // return thread handle + return procInfo.hThread; +} + +static +BOOL CALLBACK +waitDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + // only one wait dialog at a time! + static CChildWaitInfo* info = NULL; + + switch (message) { + case WM_INITDIALOG: + // save info pointer + info = reinterpret_cast(lParam); + + // save hwnd + info->m_dialog = hwnd; + + // signal ready + SetEvent(info->m_ready); + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + case IDOK: + // signal stop + SetEvent(info->m_stop); + + // done + EndDialog(hwnd, 0); + return TRUE; + } + } + + return FALSE; +} + +static +DWORD WINAPI +waitForChildThread(LPVOID vinfo) +{ + CChildWaitInfo* info = reinterpret_cast(vinfo); + + // wait for ready + WaitForSingleObject(info->m_ready, INFINITE); + + // wait for thread to complete or stop event + HANDLE handles[2]; + handles[0] = info->m_child; + handles[1] = info->m_stop; + DWORD n = WaitForMultipleObjects(2, handles, FALSE, INFINITE); + + // if stop was raised then terminate child and wait for it + if (n == WAIT_OBJECT_0 + 1) { + PostThreadMessage(info->m_childID, WM_QUIT, 0, 0); + WaitForSingleObject(info->m_child, INFINITE); + } + + // otherwise post IDOK to dialog box + else { + PostMessage(info->m_dialog, WM_COMMAND, MAKEWPARAM(IDOK, 0), 0); + } + + return 0; +} + +static +void +waitForChild(HWND hwnd, HANDLE thread, DWORD threadID) +{ + // prepare info for child wait dialog and thread + CChildWaitInfo info; + info.m_dialog = NULL; + info.m_child = thread; + info.m_childID = threadID; + info.m_ready = CreateEvent(NULL, TRUE, FALSE, NULL); + info.m_stop = CreateEvent(NULL, TRUE, FALSE, NULL); + + // create a thread to wait on the child thread and event + DWORD id; + HANDLE waiter = CreateThread(NULL, 0, &waitForChildThread, &info,0, &id); + + // do dialog that let's the user terminate the test + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_WAIT), hwnd, + waitDlgProc, (LPARAM)&info); + + // force the waiter thread to finish and wait for it + SetEvent(info.m_ready); + SetEvent(info.m_stop); + WaitForSingleObject(waiter, INFINITE); + + // clean up + CloseHandle(waiter); + CloseHandle(info.m_ready); + CloseHandle(info.m_stop); +} + +static +bool +loadConfig(const CString& pathname, CConfig& config) +{ + try { + std::ifstream stream(pathname.c_str()); + if (stream) { + stream >> config; + return true; + } + } + catch (...) { + // ignore + } + return false; +} + +static +bool +saveConfig(const CString& pathname, const CConfig& config) +{ + try { + std::ofstream stream(pathname.c_str()); + if (stream) { + stream << config; + return !!stream; + } + } + catch (...) { + // ignore + } + return false; +} + +static +bool +saveConfig(const CConfig& config) +{ + CPlatform platform; + + CString path = platform.getUserDirectory(); + if (!path.empty()) { + // try loading the user's configuration + path = platform.addPathComponent(path, CONFIG_NAME); + if (saveConfig(path, config)) { + return true; + } + } + + // try the system-wide config file + path = platform.getSystemDirectory(); + if (!path.empty()) { + path = platform.addPathComponent(path, CONFIG_NAME); + if (saveConfig(path, config)) { + return true; + } + } + + return false; +} + +static +void +initMainWindow(HWND hwnd) +{ + CPlatform platform; + + // load configuration + bool configLoaded = false; + CString path = platform.getUserDirectory(); + if (!path.empty()) { + // try loading the user's configuration + path = platform.addPathComponent(path, CONFIG_NAME); + if (loadConfig(path, s_config)) { + configLoaded = true; + } + else { + // try the system-wide config file + path = platform.getSystemDirectory(); + if (!path.empty()) { + path = platform.addPathComponent(path, CONFIG_NAME); + if (loadConfig(path, s_config)) { + configLoaded = true; + } + } + } + } + s_oldConfig = s_config; + + // choose client/server radio buttons + HWND child; + child = GetDlgItem(hwnd, IDC_MAIN_CLIENT_RADIO); + SendMessage(child, BM_SETCHECK, !configLoaded ? + BST_CHECKED : BST_UNCHECKED, 0); + child = GetDlgItem(hwnd, IDC_MAIN_SERVER_RADIO); + SendMessage(child, BM_SETCHECK, configLoaded ? + BST_CHECKED : BST_UNCHECKED, 0); + + // if config is loaded then initialize server controls + if (configLoaded) { + int i = 1; + child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + for (CConfig::const_iterator index = s_config.begin(); + index != s_config.end(); ++i, ++index) { + s_screens.push_back(*index); + CString item = CStringUtil::print("%d. %s", i, index->c_str()); + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); + } + } + + // initialize other controls + char buffer[256]; + sprintf(buffer, "%d", kDefaultPort); + child = GetDlgItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); + + CNetwork::gethostname(buffer, sizeof(buffer)); + child = GetDlgItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); + + // update neighbor combo boxes + enableMainWindowControls(hwnd); + updateNeighbors(hwnd); +} + +static +BOOL CALLBACK +addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + // only one add dialog at a time! + static CScreenInfo* info = NULL; + + switch (message) { + case WM_INITDIALOG: { + info = (CScreenInfo*)lParam; + + // fill in screen name + HWND child = GetDlgItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)info->m_screen.c_str()); + + // fill in aliases + CString aliases; + for (CStringList::const_iterator index = info->m_aliases.begin(); + index != info->m_aliases.end(); ++index) { + if (!aliases.empty()) { + aliases += "\r\n"; + } + aliases += *index; + } + child = GetDlgItem(hwnd, IDC_ADD_ALIASES_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)aliases.c_str()); + + return TRUE; + } + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: { + CString newName; + CStringList newAliases; + + // extract name and aliases + HWND child = GetDlgItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + newName = getWindowText(child); + child = GetDlgItem(hwnd, IDC_ADD_ALIASES_EDIT); + tokenize(newAliases, getWindowText(child)); + + // name must be valid + if (!s_config.isValidScreenName(newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + newName.c_str())); + return TRUE; + } + + // aliases must be valid + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + if (!s_config.isValidScreenName(*index)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + index->c_str())); + return TRUE; + } + } + + // new name may not be in the new alias list + if (isNameInList(newAliases, newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SCREEN_NAME_IS_ALIAS).c_str(), + newName.c_str())); + return TRUE; + } + + // name must not exist in config but allow same name. also + // allow name if it exists in the old alias list but not the + // new one. + if (s_config.isScreen(newName) && + !CStringUtil::CaselessCmp::equal(newName, info->m_screen) && + !isNameInList(info->m_aliases, newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), + newName.c_str())); + return TRUE; + } + + // aliases must not exist in config but allow same aliases and + // allow an alias to be the old name. + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + if (s_config.isScreen(*index) && + !CStringUtil::CaselessCmp::equal(*index, info->m_screen) && + !isNameInList(info->m_aliases, *index)) { + showError(hwnd, CStringUtil::format( + getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), + index->c_str())); + return TRUE; + } + } + + // save data + info->m_screen = newName; + info->m_aliases = newAliases; + + // success + EndDialog(hwnd, 1); + info = NULL; + return TRUE; + } + + case IDCANCEL: + EndDialog(hwnd, 0); + info = NULL; + return TRUE; + } + + default: + break; + } + + return FALSE; +} + +static +LRESULT CALLBACK +mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + // test for unsaved data + if (s_config != s_oldConfig) { + if (!askVerify(hwnd, getString(IDS_UNSAVED_DATA_REALLY_QUIT))) { + return 0; + } + } + + // quit + PostQuitMessage(0); + return 0; + + case IDOK: { + // save data + if (s_config != s_oldConfig) { + if (!saveConfig(s_config)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SAVE_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + return 0; + } + s_oldConfig = s_config; + } + + // launch child app + HANDLE thread = launchApp(hwnd, false, NULL); + if (thread == NULL) { + return 0; + } + CloseHandle(thread); + + // notify of success + askOkay(hwnd, getString(IDS_STARTED_TITLE), + getString(IDS_STARTED)); + + // quit + PostQuitMessage(0); + return 0; + } + + case IDC_MAIN_TEST: { + // save data + if (s_config != s_oldConfig) { + if (!saveConfig(s_config)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SAVE_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + return 0; + } + s_oldConfig = s_config; + } + + // launch child app + DWORD threadID; + HANDLE thread = launchApp(hwnd, true, &threadID); + if (thread == NULL) { + return 0; + } + + // wait for process to stop, allowing the user to kill it + waitForChild(hwnd, thread, threadID); + + // clean up + CloseHandle(thread); + return 0; + } + + case IDC_MAIN_UNINSTALL: { + // uninstall client and server + bool removedClient = uninstallApp(CLIENT_APP); + bool removedServer = uninstallApp(SERVER_APP); + if (!removedClient) { + showError(hwnd, CStringUtil::format( + getString(IDS_UNINSTALL_FAILED).c_str(), + getString(IDS_CLIENT).c_str())); + } + else if (!removedServer) { + showError(hwnd, CStringUtil::format( + getString(IDS_UNINSTALL_FAILED).c_str(), + getString(IDS_SERVER).c_str())); + } + else { + askOkay(hwnd, getString(IDS_UNINSTALL_TITLE), + getString(IDS_UNINSTALLED)); + } + return 0; + } + + case IDC_MAIN_CLIENT_RADIO: + case IDC_MAIN_SERVER_RADIO: + enableMainWindowControls(hwnd); + return 0; + + case IDC_MAIN_SERVER_ADD_BUTTON: + addScreen(hwnd); + return 0; + + case IDC_MAIN_SERVER_EDIT_BUTTON: + editScreen(hwnd); + return 0; + + case IDC_MAIN_SERVER_REMOVE_BUTTON: + removeScreen(hwnd); + return 0; + + case IDC_MAIN_SERVER_SCREENS_LIST: + if (HIWORD(wParam) == LBN_SELCHANGE) { + enableScreensControls(hwnd); + updateNeighbors(hwnd); + } + else if (HIWORD(wParam) == LBN_DBLCLK) { + editScreen(hwnd); + return 0; + } + break; + + case IDC_MAIN_SERVER_LEFT_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kLeft); + return 0; + } + break; + + case IDC_MAIN_SERVER_RIGHT_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kRight); + return 0; + } + break; + + case IDC_MAIN_SERVER_TOP_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kTop); + return 0; + } + break; + + case IDC_MAIN_SERVER_BOTTOM_COMBO: + if (HIWORD(wParam) == CBN_SELENDOK) { + changeNeighbor(hwnd, (HWND)lParam, kBottom); + return 0; + } + break; + } + + default: + break; + } + return DefDlgProc(hwnd, message, wParam, lParam); +} + +int WINAPI +WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) +{ + s_instance = instance; + + // initialize network library + CNetwork::init(); + + // register main window (dialog) class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_HREDRAW | CS_VREDRAW; + classInfo.lpfnWndProc = &mainWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = DLGWINDOWEXTRA; + classInfo.hInstance = instance; + classInfo.hIcon = (HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 32, 32, LR_SHARED); + classInfo.hCursor = LoadCursor(NULL, IDC_ARROW); + classInfo.hbrBackground = reinterpret_cast(COLOR_3DFACE + 1); + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = s_mainClass; + classInfo.hIconSm = (HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 16, 16, LR_SHARED); + RegisterClassEx(&classInfo); + + // create main window + s_mainWindow = CreateDialog(s_instance, MAKEINTRESOURCE(IDD_MAIN), 0, NULL); + + // prep window + initMainWindow(s_mainWindow); + + // show window + ShowWindow(s_mainWindow, nCmdShow); + + // main loop + MSG msg; + bool done = false; + do { + switch (GetMessage(&msg, NULL, 0, 0)) { + case -1: + // error + break; + + case 0: + // quit + done = true; + break; + + default: + if (!IsDialogMessage(s_mainWindow, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + break; + } + } while (!done); + + return msg.wParam; +} diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp new file mode 100644 index 00000000..79301495 --- /dev/null +++ b/cmd/launcher/launcher.dsp @@ -0,0 +1,122 @@ +# Microsoft Developer Studio Project File - Name="launcher" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=launcher - Win32 Debug +!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 "launcher.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 "launcher.mak" CFG="launcher - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "launcher - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "launcher - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "launcher - 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" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../../Release/synergy.exe" +# SUBTRACT LINK32 /map + +!ELSEIF "$(CFG)" == "launcher - 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" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../../Debug/synergy.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "launcher - Win32 Release" +# Name "launcher - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\launcher.cpp +# End Source File +# Begin Source File + +SOURCE=.\launcher.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\synergy.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc new file mode 100644 index 00000000..f70cc55b --- /dev/null +++ b/cmd/launcher/launcher.rc @@ -0,0 +1,218 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 241 +STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU +CAPTION "Synergy" +CLASS "GoSynergy" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Choose to start either the Client or Server and provide the requested information. Then click Test to check your settings or Start to save your settings and start Synergy.", + IDC_STATIC,7,7,286,19 + GROUPBOX "",IDC_STATIC,7,29,286,31 + GROUPBOX "",IDC_STATIC,7,67,286,103 + GROUPBOX "Advanced Options",IDC_STATIC,7,177,286,34 + CONTROL "Client",IDC_MAIN_CLIENT_RADIO,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,29,33,10 + CONTROL "Server",IDC_MAIN_SERVER_RADIO,"Button", + BS_AUTORADIOBUTTON,11,67,37,10 + LTEXT "Server Host Name:",IDC_MAIN_CLIENT_SERVER_NAME_LABEL,12, + 41,61,8 + EDITTEXT IDC_MAIN_CLIENT_SERVER_NAME_EDIT,79,39,106,12, + ES_AUTOHSCROLL + LTEXT "Screens:",IDC_MAIN_SERVER_SCREENS_LABEL,12,79,29,8 + LISTBOX IDC_MAIN_SERVER_SCREENS_LIST,12,91,106,36, + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add",IDC_MAIN_SERVER_ADD_BUTTON,12,132,50,14 + PUSHBUTTON "Edit",IDC_MAIN_SERVER_EDIT_BUTTON,68,132,50,14 + PUSHBUTTON "Remove",IDC_MAIN_SERVER_REMOVE_BUTTON,12,150,50,14 + LTEXT "Layout:",IDC_MAIN_SERVER_LAYOUT_LABEL,138,79,24,8 + LTEXT "Left:",IDC_MAIN_SERVER_LEFT_LABEL,144,93,15,8 + COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,118,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Right:",IDC_MAIN_SERVER_RIGHT_LABEL,144,109,20,8 + COMBOBOX IDC_MAIN_SERVER_RIGHT_COMBO,175,107,118,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Above:",IDC_MAIN_SERVER_TOP_LABEL,144,125,24,8 + COMBOBOX IDC_MAIN_SERVER_TOP_COMBO,175,123,118,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Below:",IDC_MAIN_SERVER_BOTTOM_LABEL,144,141,22,8 + COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,118,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Screen Name:",IDC_STATIC,12,192,46,8 + EDITTEXT IDC_MAIN_ADVANCED_NAME_EDIT,64,190,106,12,ES_AUTOHSCROLL + LTEXT "Port:",IDC_STATIC,194,192,16,8 + EDITTEXT IDC_MAIN_ADVANCED_PORT_EDIT,216,190,40,12,ES_AUTOHSCROLL | + ES_NUMBER + DEFPUSHBUTTON "Test",IDC_MAIN_TEST,75,220,50,14 + PUSHBUTTON "Start",IDOK,131,220,50,14 + PUSHBUTTON "No Auto-Start",IDC_MAIN_UNINSTALL,187,220,50,14 + PUSHBUTTON "Quit",IDCANCEL,243,220,50,14 +END + +IDD_ADD DIALOG DISCARDABLE 0, 0, 172, 95 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "Add Screen" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Screen Name:",IDC_STATIC,7,9,46,8 + EDITTEXT IDC_ADD_SCREEN_NAME_EDIT,59,7,106,12,ES_AUTOHSCROLL + LTEXT "Aliases:",IDC_STATIC,7,25,25,8 + EDITTEXT IDC_ADD_ALIASES_EDIT,59,26,106,40,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "OK",IDOK,59,74,50,14 + PUSHBUTTON "Cancel",IDCANCEL,115,74,50,14 +END + +IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "Running Test..." +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Stop",IDOK,129,33,50,14 + LTEXT "Running synergy. Press Stop to end the test.", + IDC_STATIC,7,7,172,15 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_MAIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 234 + END + + IDD_ADD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 165 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END + + IDD_WAIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 47 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergy.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_ERROR "Error" + IDS_INVALID_SCREEN_NAME "Screen name `%{1}' is invalid." + IDS_DUPLICATE_SCREEN_NAME "The screen name `%{1}' is already being used." + IDS_SCREEN_NAME_IS_ALIAS "A name may not be an alias of itself." + IDS_VERIFY "Confirm" + IDS_UNSAVED_DATA_REALLY_QUIT "You have unsaved changes. Really quit?" + IDS_UNKNOWN_SCREEN_NAME "The screen name `%{1}' is not in the layout." + IDS_INVALID_PORT "The port `%{1}' is invalid. It must be between 1 and 65535 inclusive. %{2} is the standard port." + IDS_SAVE_FAILED "Failed to save configuration: %{1}" + IDS_STARTUP_FAILED "Failed to start synergy: %{1}" + IDS_STARTED_TITLE "Started" + IDS_STARTED "Synergy was successfully started. Use the task manager to terminate it." + IDS_INSTALL_FAILED "Failed to install synergy auto-starter. Synergy will not be started now and it will not automatically start each time you start or reboot your computer." + IDS_UNINSTALL_TITLE "Removed Auto-Start" + IDS_UNINSTALLED "Removed auto-start. Synergy will not automatically start each time you start or reboot your computer." +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_UNINSTALL_FAILED "Failed to remove auto-start of %{1}. You might not have permission to remove it or it might be in use.\n\nOn Windows NT, 2000, or XP you should open the Services control panel and stop the synergy %{1} service then try again." + IDS_CLIENT "client" + IDS_SERVER "server" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h new file mode 100644 index 00000000..679b3e97 --- /dev/null +++ b/cmd/launcher/resource.h @@ -0,0 +1,62 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by launcher.rc +// +#define IDS_ERROR 1 +#define IDS_INVALID_SCREEN_NAME 2 +#define IDS_DUPLICATE_SCREEN_NAME 3 +#define IDS_SCREEN_NAME_IS_ALIAS 4 +#define IDS_VERIFY 5 +#define IDS_UNSAVED_DATA_REALLY_QUIT 6 +#define IDS_UNKNOWN_SCREEN_NAME 7 +#define IDS_INVALID_PORT 8 +#define IDS_SAVE_FAILED 9 +#define IDS_STARTUP_FAILED 10 +#define IDS_STARTED_TITLE 11 +#define IDS_STARTED 12 +#define IDS_INSTALL_FAILED 13 +#define IDS_UNINSTALL_TITLE 14 +#define IDS_UNINSTALLED 15 +#define IDS_UNINSTALL_FAILED 16 +#define IDS_CLIENT 17 +#define IDS_SERVER 18 +#define IDD_MAIN 101 +#define IDD_ADD 102 +#define IDD_WAIT 103 +#define IDI_SYNERGY 104 +#define IDC_MAIN_CLIENT_RADIO 1000 +#define IDC_MAIN_SERVER_RADIO 1001 +#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1002 +#define IDC_MAIN_ADVANCED_NAME_EDIT 1006 +#define IDC_MAIN_ADVANCED_PORT_EDIT 1008 +#define IDC_MAIN_TEST 1009 +#define IDC_MAIN_CLIENT_SERVER_NAME_LABEL 1011 +#define IDC_MAIN_SERVER_SCREENS_LIST 1012 +#define IDC_MAIN_SERVER_SCREENS_LABEL 1013 +#define IDC_MAIN_SERVER_LAYOUT_LABEL 1014 +#define IDC_MAIN_SERVER_ADD_BUTTON 1018 +#define IDC_MAIN_SERVER_EDIT_BUTTON 1019 +#define IDC_ADD_SCREEN_NAME_EDIT 1020 +#define IDC_MAIN_SERVER_REMOVE_BUTTON 1020 +#define IDC_ADD_ALIASES_EDIT 1021 +#define IDC_MAIN_SERVER_LEFT_COMBO 1022 +#define IDC_MAIN_SERVER_RIGHT_COMBO 1023 +#define IDC_MAIN_SERVER_TOP_COMBO 1024 +#define IDC_MAIN_SERVER_BOTTOM_COMBO 1025 +#define IDC_MAIN_SERVER_LEFT_LABEL 1026 +#define IDC_MAIN_SERVER_RIGHT_LABEL 1027 +#define IDC_MAIN_SERVER_TOP_LABEL 1028 +#define IDC_MAIN_SERVER_BOTTOM_LABEL 1029 +#define IDC_MAIN_UNINSTALL 1030 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1031 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/launcher/synergy.ico b/cmd/launcher/synergy.ico new file mode 100644 index 0000000000000000000000000000000000000000..39e0ff8d3a06d50e01aeab55c816f77d949c772d GIT binary patch literal 1078 zcmcIjF>ZrE5FBhIED^>0AXJf`ktfl)y)G-^$50||bEOL&;nF2tnn+}JFIZr?iL8vh zyEE(_9Op5RVS~D^V7P98ch;&(`j7#yTj0QJHRFSkA!h~xXCEW5$xaBVFq0z2$ZCzr zznCUt41VmArGJJv>>-Jz+yJ}8xQA)_q84#F7>Wj+$hY&P%aul{OC3v1I=iZr3+-$OY_kFlMUs(<&!6Qm*oKUsy=X4hhYoZPM#moz)C`vDH;Q(pi8 literal 0 HcmV?d00001 diff --git a/cmd/synergy/Makefile.am b/cmd/synergy/Makefile.am index 3b8c4499..f1b78a25 100644 --- a/cmd/synergy/Makefile.am +++ b/cmd/synergy/Makefile.am @@ -16,9 +16,10 @@ DEPTH = ../.. VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ - synergy.dsp \ - synergy.rc \ resource.h \ + synergy.dsp \ + synergy.ico \ + synergy.rc \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/cmd/synergy/resource.h b/cmd/synergy/resource.h index 33361a75..ec3e44aa 100644 --- a/cmd/synergy/resource.h +++ b/cmd/synergy/resource.h @@ -1,31 +1,19 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by client.rc -// -#define IDD_SYNERGY 101 -#define IDC_LOG 1000 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by synergy.rc +// +#define IDS_FAILED 1 +#define IDD_SYNERGY 101 +#define IDI_SYNERGY 103 +#define IDC_LOG 1000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/synergy/synergy.cpp b/cmd/synergy/synergy.cpp index f1bb35ed..f9dbb118 100644 --- a/cmd/synergy/synergy.cpp +++ b/cmd/synergy/synergy.cpp @@ -33,6 +33,7 @@ #if WINDOWS_LIKE #include "CMSWindowsSecondaryScreen.h" +#include "resource.h" #elif UNIX_LIKE #include "CXWindowsSecondaryScreen.h" #endif @@ -49,6 +50,7 @@ // static const char* pname = NULL; +static bool s_backend = false; static bool s_restartable = true; static bool s_daemon = true; static bool s_camp = true; @@ -117,104 +119,103 @@ realMain(CMutex* mutex) { // caller should have mutex locked on entry + // initialize threading library + CThread::init(); + + // make logging thread safe + CMutex logMutex; + s_logMutex = &logMutex; + CLog::setLock(&logLock); + int result = kExitSuccess; do { + bool opened = false; + bool locked = true; try { - // initialize threading library - CThread::init(); + // create client + s_client = new CClient(s_name); + s_client->camp(s_camp); + s_client->setAddress(s_serverAddress); + s_client->setScreenFactory(new CSecondaryScreenFactory); + s_client->setSocketFactory(new CTCPSocketFactory); + s_client->setStreamFilterFactory(NULL); - // make logging thread safe - CMutex logMutex; - s_logMutex = &logMutex; - CLog::setLock(&logLock); - - bool opened = false; - bool locked = true; + // open client try { - // create client - s_client = new CClient(s_name); - s_client->camp(s_camp); - s_client->setAddress(s_serverAddress); - s_client->setScreenFactory(new CSecondaryScreenFactory); - s_client->setSocketFactory(new CTCPSocketFactory); - s_client->setStreamFilterFactory(NULL); + s_client->open(); + opened = true; - // open client - try { - s_client->open(); - opened = true; - - // run client - if (mutex != NULL) { - mutex->unlock(); - } - locked = false; - s_client->mainLoop(); - locked = true; - if (mutex != NULL) { - mutex->lock(); - } - - // clean up - s_client->close(); - - // get client status - if (s_client->wasRejected()) { - // try again later. we don't want to bother - // the server very often if it doesn't want us. - throw XScreenUnavailable(60.0); - } + // run client + if (mutex != NULL) { + mutex->unlock(); } - catch (XScreenUnavailable& e) { - // wait a few seconds before retrying - if (s_restartable) { - CThread::sleep(e.getRetryTime()); - } - else { - result = kExitFailed; - } - } - catch (...) { - // rethrow thread exceptions - RETHROW_XTHREAD + locked = false; + s_client->mainLoop(); + locked = true; - // don't try to restart and fail - s_restartable = false; - result = kExitFailed; + // get client status + if (s_client->wasRejected()) { + // try again later. we don't want to bother + // the server very often if it doesn't want us. + throw XScreenUnavailable(60.0); } // clean up - delete s_client; - s_client = NULL; - CLog::setLock(NULL); - s_logMutex = NULL; +#define FINALLY do { \ + if (!locked && mutex != NULL) { \ + mutex->lock(); \ + } \ + if (s_client != NULL) { \ + if (opened) { \ + s_client->close(); \ + } \ + } \ + delete s_client; \ + s_client = NULL; \ + } while (false) + FINALLY; } - catch (...) { - // clean up - if (!locked && mutex != NULL) { - mutex->lock(); + catch (XScreenUnavailable& e) { + // wait before retrying if we're going to retry + if (s_restartable) { + CThread::sleep(e.getRetryTime()); } - if (s_client != NULL) { - if (opened) { - s_client->close(); - } - delete s_client; - s_client = NULL; + else { + result = kExitFailed; } - CLog::setLock(NULL); - s_logMutex = NULL; + FINALLY; + } + catch (XThread&) { + FINALLY; throw; } + catch (...) { + // don't try to restart and fail + s_restartable = false; + result = kExitFailed; + FINALLY; + } +#undef FINALLY } catch (XBase& e) { log((CLOG_CRIT "failed: %s", e.what())); } catch (XThread&) { // terminated - return kExitTerminated; + s_restartable = false; + result = kExitTerminated; + } + catch (...) { + CLog::setLock(NULL); + s_logMutex = NULL; + throw; } } while (s_restartable); + // clean up + CLog::setLock(NULL); + s_logMutex = NULL; + return result; } @@ -382,6 +383,10 @@ parse(int argc, const char** argv) s_restartable = true; } + else if (isArg(i, argc, argv, "-z", NULL)) { + s_backend = true; + } + else if (isArg(i, argc, argv, "-h", "--help")) { help(); bye(kExitSuccess); @@ -463,7 +468,7 @@ parse(int argc, const char** argv) catch (XSocketAddress& e) { log((CLOG_PRINT "%s: %s" BYE, pname, e.what(), pname)); - bye(kExitArgs); + bye(kExitFailed); } } @@ -500,10 +505,18 @@ parse(int argc, const char** argv) #include "CMSWindowsScreen.h" +static bool s_errors = false; + static bool logMessageBox(int priority, const char* msg) { + if (priority <= CLog::kERROR) { + s_errors = true; + } + if (s_backend) { + return true; + } if (priority <= CLog::kFATAL) { MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); return true; @@ -639,7 +652,8 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) case IPlatform::kAlready: log((CLOG_CRIT "service isn't installed")); - return kExitFailed; + // return success since service is uninstalled + return kExitSuccess; } } @@ -654,7 +668,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) result = platform.daemonize(DAEMON_NAME, &daemonStartup95); if (result == -1) { log((CLOG_CRIT "failed to start as a service" BYE, pname)); - return kExitFailed; + result = kExitFailed; } } else { @@ -670,6 +684,15 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) CNetwork::cleanup(); + // if running as a non-daemon backend and there was an error then + // wait for the user to click okay so he can see the error messages. + if (s_backend && !s_daemon && (result == kExitFailed || s_errors)) { + char msg[1024]; + msg[0] = '\0'; + LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); + MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); + } + return result; } diff --git a/cmd/synergy/synergy.dsp b/cmd/synergy/synergy.dsp index 991cdc26..a55230a2 100644 --- a/cmd/synergy/synergy.dsp +++ b/cmd/synergy/synergy.dsp @@ -1,117 +1,121 @@ -# Microsoft Developer Studio Project File - Name="synergy" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Application" 0x0101 - -CFG=synergy - Win32 Debug -!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 "synergy.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 "synergy.mak" CFG="synergy - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "synergy - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "synergy - Win32 Debug" (based on "Win32 (x86) Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "synergy - 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" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 - -!ELSEIF "$(CFG)" == "synergy - 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" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "synergy - Win32 Release" -# Name "synergy - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\synergy.cpp -# End Source File -# Begin Source File - -SOURCE=.\synergy.rc -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\resource.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="synergy" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=synergy - Win32 Debug +!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 "synergy.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 "synergy.mak" CFG="synergy - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synergy - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "synergy - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synergy - 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" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../../Release/synergyc.exe" + +!ELSEIF "$(CFG)" == "synergy - 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" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../../Debug/synergyc.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "synergy - Win32 Release" +# Name "synergy - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\synergy.cpp +# End Source File +# Begin Source File + +SOURCE=.\synergy.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\synergy.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/cmd/synergy/synergy.ico b/cmd/synergy/synergy.ico new file mode 100644 index 0000000000000000000000000000000000000000..23d9a0906462ab9c0d97b7493376c20036ea471e GIT binary patch literal 1078 zcmcIjF>ZrE5FC6+xQh8fs3Mn0+fY(a_&Id6Ct)f3ES};rr3*_b$?RURz;Y8=8GCnU z*gZJTV<5v0RaL_9wF5p_%QER>2D}B}$ZHj&Wn{>ifymkCh-|VGV=By~n5K!<8nb^f z&&C-1*da;TyJ!;jP^Q6N@Bgk5}L6UBBn>K0sicl|7 z>c~aHzIP$D4S{V|pr=T(rQ-7!*>yJK8teUVc(XssjjkWGz2AEMUHkF#ec5+9I81*g z>e-HOCD}%iv@d!g`Jq1~9Im|o1qc%Zpk4onNYgUHDh}lDlX-3f^Lfdr7>my*^5=;3 zI6L&T7iQ9ze6GqH(2HHmL*4TyYI|+Gsx*5&cPi41(wy8OavnuT5$#UelluIqoTrDl j{)%DX{(iHu1?m5W`_UE`Ag|c9SRp63>%}F_F6({(d|mCB literal 0 HcmV?d00001 diff --git a/cmd/synergy/synergy.rc b/cmd/synergy/synergy.rc index 60c3a920..3c6fc383 100644 --- a/cmd/synergy/synergy.rc +++ b/cmd/synergy/synergy.rc @@ -1,97 +1,117 @@ -//Microsoft Developer Studio generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE DISCARDABLE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE DISCARDABLE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE DISCARDABLE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_SYNERGY DIALOG DISCARDABLE 0, 0, 329, 158 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Synergy" -FONT 8, "MS Sans Serif" -BEGIN - EDITTEXT IDC_LOG,7,7,315,144,ES_MULTILINE | ES_AUTOHSCROLL | - ES_READONLY | WS_VSCROLL | WS_HSCROLL -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO DISCARDABLE -BEGIN - IDD_SYNERGY, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 322 - TOPMARGIN, 7 - BOTTOMMARGIN, 151 - END -END -#endif // APSTUDIO_INVOKED - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_SYNERGY DIALOG DISCARDABLE 0, 0, 329, 158 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Synergy" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_LOG,7,7,315,144,ES_MULTILINE | ES_AUTOHSCROLL | + ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_SYNERGY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 322 + TOPMARGIN, 7 + BOTTOMMARGIN, 151 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergy.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_FAILED "Synergy is about to quit with an error. Please check the log for error messages then click OK." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cmd/synergyd/Makefile.am b/cmd/synergyd/Makefile.am index a16ea951..5868830f 100644 --- a/cmd/synergyd/Makefile.am +++ b/cmd/synergyd/Makefile.am @@ -16,9 +16,10 @@ DEPTH = ../.. VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ + resource.h \ + synergy.ico \ synergyd.dsp \ synergyd.rc \ - resource.h \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/cmd/synergyd/resource.h b/cmd/synergyd/resource.h index 2c43f29d..5b67a903 100644 --- a/cmd/synergyd/resource.h +++ b/cmd/synergyd/resource.h @@ -1,31 +1,19 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by server.rc -// -#define IDD_SYNERGY 101 -#define IDC_LOG 1000 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by synergyd.rc +// +#define IDS_FAILED 1 +#define IDD_SYNERGY 101 +#define IDI_SYNERGY 102 +#define IDC_LOG 1000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1002 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/synergyd/synergy.ico b/cmd/synergyd/synergy.ico new file mode 100644 index 0000000000000000000000000000000000000000..23d9a0906462ab9c0d97b7493376c20036ea471e GIT binary patch literal 1078 zcmcIjF>ZrE5FC6+xQh8fs3Mn0+fY(a_&Id6Ct)f3ES};rr3*_b$?RURz;Y8=8GCnU z*gZJTV<5v0RaL_9wF5p_%QER>2D}B}$ZHj&Wn{>ifymkCh-|VGV=By~n5K!<8nb^f z&&C-1*da;TyJ!;jP^Q6N@Bgk5}L6UBBn>K0sicl|7 z>c~aHzIP$D4S{V|pr=T(rQ-7!*>yJK8teUVc(XssjjkWGz2AEMUHkF#ec5+9I81*g z>e-HOCD}%iv@d!g`Jq1~9Im|o1qc%Zpk4onNYgUHDh}lDlX-3f^Lfdr7>my*^5=;3 zI6L&T7iQ9ze6GqH(2HHmL*4TyYI|+Gsx*5&cPi41(wy8OavnuT5$#UelluIqoTrDl j{)%DX{(iHu1?m5W`_UE`Ag|c9SRp63>%}F_F6({(d|mCB literal 0 HcmV?d00001 diff --git a/cmd/synergyd/synergyd.cpp b/cmd/synergyd/synergyd.cpp index abdbed4d..43b84a8e 100644 --- a/cmd/synergyd/synergyd.cpp +++ b/cmd/synergyd/synergyd.cpp @@ -32,6 +32,7 @@ #if WINDOWS_LIKE #include "CMSWindowsPrimaryScreen.h" +#include "resource.h" #elif UNIX_LIKE #include "CXWindowsPrimaryScreen.h" #endif @@ -55,6 +56,7 @@ // static const char* pname = NULL; +static bool s_backend = false; static bool s_restartable = true; static bool s_daemon = true; #if WINDOWS_LIKE @@ -128,117 +130,116 @@ realMain(CMutex* mutex) { // caller should have mutex locked on entry + // initialize threading library + CThread::init(); + + // make logging thread safe + CMutex logMutex; + s_logMutex = &logMutex; + CLog::setLock(&logLock); + int result = kExitSuccess; do { + bool opened = false; + bool locked = true; try { - // initialize threading library - CThread::init(); - - // make logging thread safe - CMutex logMutex; - s_logMutex = &logMutex; - CLog::setLock(&logLock); - - bool opened = false; - bool locked = true; - try { - // if configuration has no screens then add this system - // as the default - if (s_config.begin() == s_config.end()) { - s_config.addScreen(s_name); - } - - // set the contact address, if provided, in the config. - // otherwise, if the config doesn't have an address, use - // the default. - if (s_synergyAddress.isValid()) { - s_config.setSynergyAddress(s_synergyAddress); - } - else if (!s_config.getSynergyAddress().isValid()) { - s_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); - } - - // set HTTP address if provided - if (s_httpAddress.isValid()) { - s_config.setHTTPAddress(s_httpAddress); - } - - // create server - s_server = new CServer(s_name); - s_server->setConfig(s_config); - s_server->setScreenFactory(new CPrimaryScreenFactory); - s_server->setSocketFactory(new CTCPSocketFactory); - s_server->setStreamFilterFactory(NULL); - - // open server - try { - s_server->open(); - opened = true; - - // run server (unlocked) - if (mutex != NULL) { - mutex->unlock(); - } - locked = false; - s_server->mainLoop(); - locked = true; - if (mutex != NULL) { - mutex->lock(); - } - - // clean up - s_server->close(); - } - catch (XScreenUnavailable& e) { - // wait before retrying if we're going to retry - if (s_restartable) { - CThread::sleep(e.getRetryTime()); - } - else { - result = kExitFailed; - } - } - catch (...) { - // rethrow thread exceptions - RETHROW_XTHREAD - - // don't try to restart and fail - s_restartable = false; - result = kExitFailed; - } - - // clean up - delete s_server; - s_server = NULL; - CLog::setLock(NULL); - s_logMutex = NULL; + // if configuration has no screens then add this system + // as the default + if (s_config.begin() == s_config.end()) { + s_config.addScreen(s_name); } - catch (...) { + + // set the contact address, if provided, in the config. + // otherwise, if the config doesn't have an address, use + // the default. + if (s_synergyAddress.isValid()) { + s_config.setSynergyAddress(s_synergyAddress); + } + else if (!s_config.getSynergyAddress().isValid()) { + s_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); + } + + // set HTTP address if provided + if (s_httpAddress.isValid()) { + s_config.setHTTPAddress(s_httpAddress); + } + + // create server + s_server = new CServer(s_name); + s_server->setConfig(s_config); + s_server->setScreenFactory(new CPrimaryScreenFactory); + s_server->setSocketFactory(new CTCPSocketFactory); + s_server->setStreamFilterFactory(NULL); + + // open server + try { + s_server->open(); + opened = true; + + // run server (unlocked) + if (mutex != NULL) { + mutex->unlock(); + } + locked = false; + s_server->mainLoop(); + locked = true; + // clean up - if (!locked && mutex != NULL) { - mutex->lock(); +#define FINALLY do { \ + if (!locked && mutex != NULL) { \ + mutex->lock(); \ + } \ + if (s_server != NULL) { \ + if (opened) { \ + s_server->close(); \ + } \ + } \ + delete s_server; \ + s_server = NULL; \ + } while (false) + FINALLY; + } + catch (XScreenUnavailable& e) { + // wait before retrying if we're going to retry + if (s_restartable) { + CThread::sleep(e.getRetryTime()); } - if (s_server != NULL) { - if (opened) { - s_server->close(); - } - delete s_server; - s_server = NULL; + else { + result = kExitFailed; } - CLog::setLock(NULL); - s_logMutex = NULL; + FINALLY; + } + catch (XThread&) { + FINALLY; throw; } + catch (...) { + // don't try to restart and fail + s_restartable = false; + result = kExitFailed; + FINALLY; + } +#undef FINALLY } catch (XBase& e) { log((CLOG_CRIT "failed: %s", e.what())); } catch (XThread&) { // terminated - return kExitTerminated; + s_restartable = false; + result = kExitTerminated; + } + catch (...) { + CLog::setLock(NULL); + s_logMutex = NULL; + throw; } } while (s_restartable); + // clean up + CLog::setLock(NULL); + s_logMutex = NULL; + return result; } @@ -448,6 +449,10 @@ parse(int argc, const char** argv) s_restartable = true; } + else if (isArg(i, argc, argv, "-z", NULL)) { + s_backend = true; + } + else if (isArg(i, argc, argv, "-h", "--help")) { help(); bye(kExitSuccess); @@ -605,10 +610,18 @@ loadConfig() #include "CMSWindowsScreen.h" +static bool s_errors = false; + static bool logMessageBox(int priority, const char* msg) { + if (priority <= CLog::kERROR) { + s_errors = true; + } + if (s_backend) { + return true; + } if (priority <= CLog::kFATAL) { MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); return true; @@ -751,7 +764,8 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) case IPlatform::kAlready: log((CLOG_CRIT "service isn't installed")); - return kExitFailed; + // return success since service is uninstalled + return kExitSuccess; } } @@ -769,7 +783,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) result = platform.daemonize(DAEMON_NAME, &daemonStartup95); if (result == -1) { log((CLOG_CRIT "failed to start as a service" BYE, pname)); - return kExitFailed; + result = kExitFailed; } } else { @@ -785,6 +799,15 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) CNetwork::cleanup(); + // if running as a non-daemon backend and there was an error then + // wait for the user to click okay so he can see the error messages. + if (s_backend && !s_daemon && (result == kExitFailed || s_errors)) { + char msg[1024]; + msg[0] = '\0'; + LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); + MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); + } + return result; } diff --git a/cmd/synergyd/synergyd.dsp b/cmd/synergyd/synergyd.dsp index f7665026..31195ecc 100644 --- a/cmd/synergyd/synergyd.dsp +++ b/cmd/synergyd/synergyd.dsp @@ -1,117 +1,121 @@ -# Microsoft Developer Studio Project File - Name="synergyd" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Application" 0x0101 - -CFG=synergyd - Win32 Debug -!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 "synergyd.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 "synergyd.mak" CFG="synergyd - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "synergyd - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "synergyd - Win32 Debug" (based on "Win32 (x86) Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "synergyd - 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" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 - -!ELSEIF "$(CFG)" == "synergyd - 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" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "synergyd - Win32 Release" -# Name "synergyd - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\synergyd.cpp -# End Source File -# Begin Source File - -SOURCE=.\synergyd.rc -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\resource.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="synergyd" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=synergyd - Win32 Debug +!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 "synergyd.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 "synergyd.mak" CFG="synergyd - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synergyd - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "synergyd - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synergyd - 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" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "synergyd - 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" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "synergyd - Win32 Release" +# Name "synergyd - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\synergyd.cpp +# End Source File +# Begin Source File + +SOURCE=.\synergyd.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\synergy.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/cmd/synergyd/synergyd.rc b/cmd/synergyd/synergyd.rc index 731c32ce..a289e737 100644 --- a/cmd/synergyd/synergyd.rc +++ b/cmd/synergyd/synergyd.rc @@ -1,97 +1,118 @@ -//Microsoft Developer Studio generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE DISCARDABLE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE DISCARDABLE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE DISCARDABLE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_SYNERGY DIALOG DISCARDABLE 0, 0, 531, 159 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Synergy" -FONT 8, "MS Sans Serif" -BEGIN - EDITTEXT IDC_LOG,7,7,517,145,ES_MULTILINE | ES_AUTOHSCROLL | - ES_READONLY | WS_VSCROLL | WS_HSCROLL -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO DISCARDABLE -BEGIN - IDD_SYNERGY, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 524 - TOPMARGIN, 7 - BOTTOMMARGIN, 152 - END -END -#endif // APSTUDIO_INVOKED - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_SYNERGY DIALOG DISCARDABLE 0, 0, 531, 159 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Synergy" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LOG,7,7,517,145,NOT LBS_NOTIFY | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | + WS_TABSTOP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_SYNERGY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 524 + TOPMARGIN, 7 + BOTTOMMARGIN, 152 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergy.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_FAILED "Synergy is about to quit with an error. Please check the log for error messages then click OK." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/configure.in b/configure.in index c938eda3..babd1f72 100644 --- a/configure.in +++ b/configure.in @@ -90,6 +90,7 @@ lib/platform/Makefile lib/client/Makefile lib/server/Makefile cmd/Makefile +cmd/launcher/Makefile cmd/synergy/Makefile cmd/synergyd/Makefile dist/Makefile @@ -97,4 +98,3 @@ dist/rpm/Makefile dist/rpm/synergy.spec doc/doxygen.cfg ]) - diff --git a/lib/base/CString.cpp b/lib/base/CString.cpp index 2aedb36e..13e78239 100644 --- a/lib/base/CString.cpp +++ b/lib/base/CString.cpp @@ -91,8 +91,9 @@ CStringUtil::vformat(const char* fmt, va_list args) length.push_back(1); for (int i = 0; i < maxIndex; ++i) { const char* arg = va_arg(args, const char*); + size_t len = strlen(arg); value.push_back(arg); - length.push_back(strlen(arg)); + length.push_back(len); } // compute final length diff --git a/lib/base/Version.h b/lib/base/Version.h index afaea6c5..daebab8d 100644 --- a/lib/base/Version.h +++ b/lib/base/Version.h @@ -19,13 +19,13 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.0" +# define VERSION "0.9.8" #endif // important strings static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; static const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; -static const char* kWebsite = ""; +static const char* kWebsite = "http://synergy2.sourceforge.net/"; // build version. follows linux kernel style: an even minor number implies // a release version, odd implies development version. diff --git a/lib/base/base.dsp b/lib/base/base.dsp index e55fdedc..66dc0a8e 100644 --- a/lib/base/base.dsp +++ b/lib/base/base.dsp @@ -1,202 +1,206 @@ -# Microsoft Developer Studio Project File - Name="base" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=base - Win32 Debug -!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 "base.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 "base.mak" CFG="base - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "base - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "base - Win32 Debug" (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)" == "base - 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" -# 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 /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "base - 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" -# 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 /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "base - Win32 Release" -# Name "base - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CFunctionJob.cpp -# End Source File -# Begin Source File - -SOURCE=.\CLog.cpp -# End Source File -# Begin Source File - -SOURCE=.\CStopwatch.cpp -# End Source File -# Begin Source File - -SOURCE=.\CString.cpp -# End Source File -# Begin Source File - -SOURCE=.\CUnicode.cpp -# End Source File -# Begin Source File - -SOURCE=.\XBase.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\BasicTypes.h -# End Source File -# Begin Source File - -SOURCE=.\CFunctionJob.h -# End Source File -# Begin Source File - -SOURCE=.\CLog.h -# End Source File -# Begin Source File - -SOURCE=.\common.h -# End Source File -# Begin Source File - -SOURCE=.\CStopwatch.h -# End Source File -# Begin Source File - -SOURCE=.\CString.h -# End Source File -# Begin Source File - -SOURCE=.\CUnicode.h -# End Source File -# Begin Source File - -SOURCE=.\IInterface.h -# End Source File -# Begin Source File - -SOURCE=.\IJob.h -# End Source File -# Begin Source File - -SOURCE=.\stdfstream.h -# End Source File -# Begin Source File - -SOURCE=.\stdistream.h -# End Source File -# Begin Source File - -SOURCE=.\stdlist.h -# End Source File -# Begin Source File - -SOURCE=.\stdmap.h -# End Source File -# Begin Source File - -SOURCE=.\stdostream.h -# End Source File -# Begin Source File - -SOURCE=.\stdpost.h -# End Source File -# Begin Source File - -SOURCE=.\stdpre.h -# End Source File -# Begin Source File - -SOURCE=.\stdset.h -# End Source File -# Begin Source File - -SOURCE=.\stdsstream.h -# End Source File -# Begin Source File - -SOURCE=.\stdvector.h -# End Source File -# Begin Source File - -SOURCE=.\TMethodJob.h -# End Source File -# Begin Source File - -SOURCE=.\XBase.h -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="base" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=base - Win32 Debug +!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 "base.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 "base.mak" CFG="base - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "base - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "base - Win32 Debug" (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)" == "base - 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" +# 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 /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "base - 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" +# 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 /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "base - Win32 Release" +# Name "base - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CFunctionJob.cpp +# End Source File +# Begin Source File + +SOURCE=.\CLog.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStopwatch.cpp +# End Source File +# Begin Source File + +SOURCE=.\CString.cpp +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.cpp +# End Source File +# Begin Source File + +SOURCE=.\XBase.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\BasicTypes.h +# End Source File +# Begin Source File + +SOURCE=.\CFunctionJob.h +# End Source File +# Begin Source File + +SOURCE=.\CLog.h +# End Source File +# Begin Source File + +SOURCE=.\common.h +# End Source File +# Begin Source File + +SOURCE=.\CStopwatch.h +# End Source File +# Begin Source File + +SOURCE=.\CString.h +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.h +# End Source File +# Begin Source File + +SOURCE=.\IInterface.h +# End Source File +# Begin Source File + +SOURCE=.\IJob.h +# End Source File +# Begin Source File + +SOURCE=.\stdfstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdistream.h +# End Source File +# Begin Source File + +SOURCE=.\stdlist.h +# End Source File +# Begin Source File + +SOURCE=.\stdmap.h +# End Source File +# Begin Source File + +SOURCE=.\stdostream.h +# End Source File +# Begin Source File + +SOURCE=.\stdpost.h +# End Source File +# Begin Source File + +SOURCE=.\stdpre.h +# End Source File +# Begin Source File + +SOURCE=.\stdset.h +# End Source File +# Begin Source File + +SOURCE=.\stdsstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdvector.h +# End Source File +# Begin Source File + +SOURCE=.\TMethodJob.h +# End Source File +# Begin Source File + +SOURCE=.\Version.h +# End Source File +# Begin Source File + +SOURCE=.\XBase.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/client/client.dsp b/lib/client/client.dsp index 985ffe88..0fd4a4d8 100644 --- a/lib/client/client.dsp +++ b/lib/client/client.dsp @@ -1,134 +1,134 @@ -# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=client - Win32 Debug -!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 "client.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 "client.mak" CFG="client - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "client - Win32 Debug" (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)" == "client - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "client - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "client - Win32 Release" -# Name "client - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CClient.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsSecondaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CSecondaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CServerProxy.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CClient.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsSecondaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CSecondaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CServerProxy.h -# End Source File -# Begin Source File - -SOURCE=.\ISecondaryScreenFactory.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=client - Win32 Debug +!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 "client.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 "client.mak" CFG="client - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "client - Win32 Debug" (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)" == "client - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "client - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "client - Win32 Release" +# Name "client - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSecondaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerProxy.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClient.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CSecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CServerProxy.h +# End Source File +# Begin Source File + +SOURCE=.\ISecondaryScreenFactory.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/http/http.dsp b/lib/http/http.dsp index 2330ca65..2f050878 100644 --- a/lib/http/http.dsp +++ b/lib/http/http.dsp @@ -1,110 +1,110 @@ -# Microsoft Developer Studio Project File - Name="http" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=http - Win32 Debug -!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 "http.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 "http.mak" CFG="http - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "http - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "http - Win32 Debug" (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)" == "http - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "http - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "http - Win32 Release" -# Name "http - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CHTTPProtocol.cpp -# End Source File -# Begin Source File - -SOURCE=.\XHTTP.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CHTTPProtocol.h -# End Source File -# Begin Source File - -SOURCE=.\XHTTP.h -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="http" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=http - Win32 Debug +!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 "http.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 "http.mak" CFG="http - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "http - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "http - Win32 Debug" (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)" == "http - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "http - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "http - Win32 Release" +# Name "http - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CHTTPProtocol.cpp +# End Source File +# Begin Source File + +SOURCE=.\XHTTP.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CHTTPProtocol.h +# End Source File +# Begin Source File + +SOURCE=.\XHTTP.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/io/io.dsp b/lib/io/io.dsp index 0631be2f..c9983c9c 100644 --- a/lib/io/io.dsp +++ b/lib/io/io.dsp @@ -1,154 +1,154 @@ -# Microsoft Developer Studio Project File - Name="io" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=io - Win32 Debug -!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 "io.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 "io.mak" CFG="io - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "io - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "io - Win32 Debug" (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)" == "io - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "io - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "io - Win32 Release" -# Name "io - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CBufferedInputStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\CBufferedOutputStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\CInputStreamFilter.cpp -# End Source File -# Begin Source File - -SOURCE=.\COutputStreamFilter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CStreamBuffer.cpp -# End Source File -# Begin Source File - -SOURCE=.\XIO.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CBufferedInputStream.h -# End Source File -# Begin Source File - -SOURCE=.\CBufferedOutputStream.h -# End Source File -# Begin Source File - -SOURCE=.\CInputStreamFilter.h -# End Source File -# Begin Source File - -SOURCE=.\COutputStreamFilter.h -# End Source File -# Begin Source File - -SOURCE=.\CStreamBuffer.h -# End Source File -# Begin Source File - -SOURCE=.\IInputStream.h -# End Source File -# Begin Source File - -SOURCE=.\IOutputStream.h -# End Source File -# Begin Source File - -SOURCE=.\IStreamFilterFactory.h -# End Source File -# Begin Source File - -SOURCE=.\XIO.h -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="io" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=io - Win32 Debug +!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 "io.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 "io.mak" CFG="io - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "io - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "io - Win32 Debug" (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)" == "io - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "io - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "io - Win32 Release" +# Name "io - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CBufferedInputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBufferedOutputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInputStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStreamBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=.\XIO.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CBufferedInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CBufferedOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CInputStreamFilter.h +# End Source File +# Begin Source File + +SOURCE=.\COutputStreamFilter.h +# End Source File +# Begin Source File + +SOURCE=.\CStreamBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\IInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\IOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\IStreamFilterFactory.h +# End Source File +# Begin Source File + +SOURCE=.\XIO.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp index baa08de9..e179518e 100644 --- a/lib/mt/mt.dsp +++ b/lib/mt/mt.dsp @@ -1,146 +1,146 @@ -# Microsoft Developer Studio Project File - Name="mt" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=mt - Win32 Debug -!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 "mt.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 "mt.mak" CFG="mt - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "mt - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "mt - Win32 Debug" (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)" == "mt - 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" -# 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 /W4 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "mt - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "mt - Win32 Release" -# Name "mt - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CCondVar.cpp -# End Source File -# Begin Source File - -SOURCE=.\CLock.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMutex.cpp -# End Source File -# Begin Source File - -SOURCE=.\CThread.cpp -# End Source File -# Begin Source File - -SOURCE=.\CThreadRep.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTimerThread.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CCondVar.h -# End Source File -# Begin Source File - -SOURCE=.\CLock.h -# End Source File -# Begin Source File - -SOURCE=.\CMutex.h -# End Source File -# Begin Source File - -SOURCE=.\CThread.h -# End Source File -# Begin Source File - -SOURCE=.\CThreadRep.h -# End Source File -# Begin Source File - -SOURCE=.\CTimerThread.h -# End Source File -# Begin Source File - -SOURCE=.\XThread.h -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="mt" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=mt - Win32 Debug +!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 "mt.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 "mt.mak" CFG="mt - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mt - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "mt - Win32 Debug" (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)" == "mt - 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" +# 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 /W4 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "mt - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "mt - Win32 Release" +# Name "mt - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CCondVar.cpp +# End Source File +# Begin Source File + +SOURCE=.\CLock.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMutex.cpp +# End Source File +# Begin Source File + +SOURCE=.\CThread.cpp +# End Source File +# Begin Source File + +SOURCE=.\CThreadRep.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CCondVar.h +# End Source File +# Begin Source File + +SOURCE=.\CLock.h +# End Source File +# Begin Source File + +SOURCE=.\CMutex.h +# End Source File +# Begin Source File + +SOURCE=.\CThread.h +# End Source File +# Begin Source File + +SOURCE=.\CThreadRep.h +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.h +# End Source File +# Begin Source File + +SOURCE=.\XThread.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/net/net.dsp b/lib/net/net.dsp index 04cfe3ef..13a976e9 100644 --- a/lib/net/net.dsp +++ b/lib/net/net.dsp @@ -1,162 +1,162 @@ -# Microsoft Developer Studio Project File - Name="net" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=net - Win32 Debug -!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 "net.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 "net.mak" CFG="net - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "net - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "net - Win32 Debug" (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)" == "net - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "net - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "net - Win32 Release" -# Name "net - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CNetwork.cpp -# End Source File -# Begin Source File - -SOURCE=.\CNetworkAddress.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTCPListenSocket.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocket.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocketFactory.cpp -# End Source File -# Begin Source File - -SOURCE=.\XNetwork.cpp -# End Source File -# Begin Source File - -SOURCE=.\XSocket.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CNetwork.h -# End Source File -# Begin Source File - -SOURCE=.\CNetworkAddress.h -# End Source File -# Begin Source File - -SOURCE=.\CTCPListenSocket.h -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocket.h -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocketFactory.h -# End Source File -# Begin Source File - -SOURCE=.\IListenSocket.h -# End Source File -# Begin Source File - -SOURCE=.\ISocket.h -# End Source File -# Begin Source File - -SOURCE=.\ISocketFactory.h -# End Source File -# Begin Source File - -SOURCE=.\XNetwork.h -# End Source File -# Begin Source File - -SOURCE=.\XSocket.h -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="net" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=net - Win32 Debug +!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 "net.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 "net.mak" CFG="net - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "net - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "net - Win32 Debug" (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)" == "net - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "net - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "net - Win32 Release" +# Name "net - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CNetwork.cpp +# End Source File +# Begin Source File + +SOURCE=.\CNetworkAddress.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPListenSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.cpp +# End Source File +# Begin Source File + +SOURCE=.\XNetwork.cpp +# End Source File +# Begin Source File + +SOURCE=.\XSocket.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\CNetworkAddress.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocket.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\IListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\ISocket.h +# End Source File +# Begin Source File + +SOURCE=.\ISocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\XNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\XSocket.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 534d9905..56f054f4 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -349,7 +349,7 @@ CMSWindowsScreen::syncDesktop() // change calling thread's desktop if (!m_is95Family) { if (SetThreadDesktop(m_desk) == 0) { - log((CLOG_WARN "failed to set desktop: %d", GetLastError())); +// log((CLOG_WARN "failed to set desktop: %d", GetLastError())); } } diff --git a/lib/platform/makehook.dsp b/lib/platform/makehook.dsp index 507157ef..fc0bc115 100644 --- a/lib/platform/makehook.dsp +++ b/lib/platform/makehook.dsp @@ -1,63 +1,63 @@ -# Microsoft Developer Studio Project File - Name="makehook" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Generic Project" 0x010a - -CFG=makehook - Win32 Debug -!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 "makehook.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 "makehook.mak" CFG="makehook - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "makehook - Win32 Release" (based on "Win32 (x86) Generic Project") -!MESSAGE "makehook - Win32 Debug" (based on "Win32 (x86) Generic Project") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -MTL=midl.exe - -!IF "$(CFG)" == "makehook - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "makehook___Win32_Release" -# PROP BASE Intermediate_Dir "makehook___Win32_Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "makehook - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "makehook___Win32_Debug" -# PROP BASE Intermediate_Dir "makehook___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "makehook - Win32 Release" -# Name "makehook - Win32 Debug" -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="makehook" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=makehook - Win32 Debug +!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 "makehook.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 "makehook.mak" CFG="makehook - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "makehook - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "makehook - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "makehook - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "makehook___Win32_Release" +# PROP BASE Intermediate_Dir "makehook___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "makehook - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "makehook___Win32_Debug" +# PROP BASE Intermediate_Dir "makehook___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "makehook - Win32 Release" +# Name "makehook - Win32 Debug" +# End Target +# End Project diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index 080d4de2..d78f7c92 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -1,175 +1,175 @@ -# Microsoft Developer Studio Project File - Name="platform" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=platform - Win32 Debug -!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 "platform.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 "platform.mak" CFG="platform - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "platform - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "platform - Win32 Debug" (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)" == "platform - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "platform - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "platform - Win32 Release" -# Name "platform - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CMSWindowsClipboard.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardAnyTextConverter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardTextConverter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreenSaver.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPlatform.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CMSWindowsClipboard.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardAnyTextConverter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardTextConverter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardUTF16Converter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreenSaver.h -# End Source File -# Begin Source File - -SOURCE=.\CPlatform.h -# End Source File -# Begin Source File - -SOURCE=.\CWin32Platform.h -# End Source File -# Begin Source File - -SOURCE=.\IMSWindowsScreenEventHandler.h -# End Source File -# Begin Source File - -SOURCE=.\IPlatform.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# Begin Group "Included Files" - -# PROP Default_Filter "inc" -# Begin Source File - -SOURCE=.\CWin32Platform.cpp -# PROP Exclude_From_Build 1 -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="platform" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=platform - Win32 Debug +!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 "platform.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 "platform.mak" CFG="platform - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "platform - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "platform - Win32 Debug" (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)" == "platform - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "platform - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "platform - Win32 Release" +# Name "platform - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardAnyTextConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardTextConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreenSaver.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPlatform.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardAnyTextConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardTextConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardUTF16Converter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreenSaver.h +# End Source File +# Begin Source File + +SOURCE=.\CPlatform.h +# End Source File +# Begin Source File + +SOURCE=.\CWin32Platform.h +# End Source File +# Begin Source File + +SOURCE=.\IMSWindowsScreenEventHandler.h +# End Source File +# Begin Source File + +SOURCE=.\IPlatform.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# Begin Group "Included Files" + +# PROP Default_Filter "inc" +# Begin Source File + +SOURCE=.\CWin32Platform.cpp +# PROP Exclude_From_Build 1 +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp index 4f1f593c..cf1717e3 100644 --- a/lib/platform/synrgyhk.dsp +++ b/lib/platform/synrgyhk.dsp @@ -1,116 +1,116 @@ -# Microsoft Developer Studio Project File - Name="synrgyhk" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=synrgyhk - Win32 Debug -!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 "synrgyhk.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 "synrgyhk.mak" CFG="synrgyhk - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "synrgyhk - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "synrgyhk - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "synrgyhk - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "synrgyhk___Win32_Release" -# PROP BASE Intermediate_Dir "synrgyhk___Win32_Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../../Release" -# PROP Intermediate_Dir "ReleaseHook" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"DllMain" /dll /machine:I386 /nodefaultlib -# SUBTRACT LINK32 /verbose - -!ELSEIF "$(CFG)" == "synrgyhk - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "synrgyhk___Win32_Debug" -# PROP BASE Intermediate_Dir "synrgyhk___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../../Debug" -# PROP Intermediate_Dir "DebugHook" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\synergy" /I "..\base" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"" /dll /debug /machine:I386 /pdbtype:sept -# SUBTRACT LINK32 /nodefaultlib - -!ENDIF - -# Begin Target - -# Name "synrgyhk - Win32 Release" -# Name "synrgyhk - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CSynergyHook.cpp -# ADD CPP /D _WIN32_WINNT=0x0400 -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CSynergyHook.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="synrgyhk" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=synrgyhk - Win32 Debug +!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 "synrgyhk.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 "synrgyhk.mak" CFG="synrgyhk - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synrgyhk - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "synrgyhk - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synrgyhk - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "synrgyhk___Win32_Release" +# PROP BASE Intermediate_Dir "synrgyhk___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../Release" +# PROP Intermediate_Dir "ReleaseHook" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"DllMain" /dll /machine:I386 /nodefaultlib +# SUBTRACT LINK32 /verbose + +!ELSEIF "$(CFG)" == "synrgyhk - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "synrgyhk___Win32_Debug" +# PROP BASE Intermediate_Dir "synrgyhk___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../Debug" +# PROP Intermediate_Dir "DebugHook" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\synergy" /I "..\base" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"" /dll /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "synrgyhk - Win32 Release" +# Name "synrgyhk - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CSynergyHook.cpp +# ADD CPP /D _WIN32_WINNT=0x0400 +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CSynergyHook.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index feb68ccd..942e23c9 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -49,6 +49,57 @@ CConfig::addScreen(const CString& name) return true; } +bool +CConfig::renameScreen(const CString& oldName, + const CString& newName) +{ + // get canonical name and find cell + CString oldCanonical = getCanonicalName(oldName); + CCellMap::iterator index = m_map.find(oldCanonical); + if (index == m_map.end()) { + return false; + } + + // accept if names are equal but replace with new name to maintain + // case. otherwise, the new name must not exist. + if (!CStringUtil::CaselessCmp::equal(oldName, newName) && + m_nameToCanonicalName.find(newName) != m_nameToCanonicalName.end()) { + return false; + } + + // update cell + CCell tmpCell = index->second; + m_map.erase(index); + m_map.insert(std::make_pair(newName, tmpCell)); + + // update name + m_nameToCanonicalName.erase(oldCanonical); + m_nameToCanonicalName.insert(std::make_pair(newName, newName)); + + // update connections + for (index = m_map.begin(); index != m_map.end(); ++index) { + for (UInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) { + if (CStringUtil::CaselessCmp::equal(getCanonicalName( + index->second.m_neighbor[i]), oldCanonical)) { + index->second.m_neighbor[i] = newName; + } + } + } + + // update alias targets + if (CStringUtil::CaselessCmp::equal(oldName, oldCanonical)) { + for (CNameMap::iterator index = m_nameToCanonicalName.begin(); + index != m_nameToCanonicalName.end(); ++index) { + if (CStringUtil::CaselessCmp::equal( + index->second, oldCanonical)) { + index->second = newName; + } + } + } + + return true; +} + void CConfig::removeScreen(const CString& name) { @@ -65,10 +116,11 @@ CConfig::removeScreen(const CString& name) // disconnect for (index = m_map.begin(); index != m_map.end(); ++index) { CCell& cell = index->second; - for (SInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) + for (UInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) { if (getCanonicalName(cell.m_neighbor[i]) == canonical) { cell.m_neighbor[i].erase(); } + } } // remove aliases (and canonical name) @@ -99,7 +151,7 @@ CConfig::addAlias(const CString& canonical, const CString& alias) } // canonical name must be known - if (m_nameToCanonicalName.find(canonical) == m_nameToCanonicalName.end()) { + if (m_map.find(canonical) == m_map.end()) { return false; } @@ -245,6 +297,18 @@ CConfig::end() const return const_iterator(m_map.end()); } +CConfig::all_const_iterator +CConfig::beginAll() const +{ + return m_nameToCanonicalName.begin(); +} + +CConfig::all_const_iterator +CConfig::endAll() const +{ + return m_nameToCanonicalName.end(); +} + bool CConfig::isScreen(const CString& name) const { @@ -295,6 +359,54 @@ CConfig::getHTTPAddress() const return m_httpAddress; } +bool +CConfig::operator==(const CConfig& x) const +{ +/* FIXME -- no compare available for CNetworkAddress + if (m_synergyAddress != x.m_synergyAddress) { + return false; + } + if (m_httpAddress != x.m_httpAddress) { + return false; + } +*/ + if (m_map.size() != x.m_map.size()) { + return false; + } + if (m_nameToCanonicalName.size() != x.m_nameToCanonicalName.size()) { + return false; + } + + for (CCellMap::const_iterator index1 = m_map.begin(), + index2 = x.m_map.begin(); + index1 != m_map.end(); ++index1, ++index2) { + for (UInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) { + if (!CStringUtil::CaselessCmp::equal(index1->second.m_neighbor[i], + index2->second.m_neighbor[i])) { + return false; + } + } + } + + for (CNameMap::const_iterator index1 = m_nameToCanonicalName.begin(), + index2 = x.m_nameToCanonicalName.begin(); + index1 != m_nameToCanonicalName.end(); + ++index1, ++index2) { + if (!CStringUtil::CaselessCmp::equal(index1->first, index2->first) || + !CStringUtil::CaselessCmp::equal(index1->second, index2->second)) { + return false; + } + } + + return true; +} + +bool +CConfig::operator!=(const CConfig& x) const +{ + return !operator==(x); +} + const char* CConfig::dirName(EDirection dir) { @@ -663,7 +775,9 @@ operator<<(std::ostream& s, const CConfig& config) // aliases section (if there are any) if (config.m_map.size() != config.m_nameToCanonicalName.size()) { // map canonical to alias - CConfig::CNameMap aliases; + typedef std::multimap CMNameMap; + CMNameMap aliases; for (CConfig::CNameMap::const_iterator index = config.m_nameToCanonicalName.begin(); index != config.m_nameToCanonicalName.end(); @@ -676,11 +790,11 @@ operator<<(std::ostream& s, const CConfig& config) // dump it CString screen; s << "section: aliases" << std::endl; - for (CConfig::CNameMap::const_iterator index = aliases.begin(); + for (CMNameMap::const_iterator index = aliases.begin(); index != aliases.end(); ++index) { if (index->first != screen) { screen = index->first; - s << "\t" << screen.c_str() << std::endl; + s << "\t" << screen.c_str() << ":" << std::endl; } s << "\t\t" << index->second.c_str() << std::endl; } diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index 3fe3cf88..c028ebf5 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -51,9 +51,11 @@ private: CString m_neighbor[kLastDirection - kFirstDirection + 1]; }; typedef std::map CCellMap; + typedef std::map CNameMap; public: typedef CCellMap::const_iterator internal_const_iterator; + typedef CNameMap::const_iterator all_const_iterator; class const_iterator : std::iterator_traits { public: explicit const_iterator() : m_i() { } @@ -93,6 +95,14 @@ public: */ bool addScreen(const CString& name); + //! Rename screen + /*! + Renames a screen. All references to the name are updated. + Returns true iff successful. + */ + bool renameScreen(const CString& oldName, + const CString& newName); + //! Remove screen /*! Removes a screen. This also removes aliases for the screen and @@ -180,6 +190,11 @@ public: //! Get ending (canonical) screen name iterator const_iterator end() const; + //! Get beginning screen name iterator + all_const_iterator beginAll() const; + //! Get ending screen name iterator + all_const_iterator endAll() const; + //! Test for screen name /*! Returns true iff \c name names a screen. @@ -212,6 +227,11 @@ public: //! Get the HTTP server address const CNetworkAddress& getHTTPAddress() const; + //! Compare configurations + bool operator==(const CConfig&) const; + //! Compare configurations + bool operator!=(const CConfig&) const; + //! Read configuration /*! Reads a configuration from a stream. Throws XConfigRead on error. @@ -241,8 +261,6 @@ private: void readSectionAliases(std::istream&); private: - typedef std::map CNameMap; - CCellMap m_map; CNameMap m_nameToCanonicalName; CNetworkAddress m_synergyAddress; diff --git a/lib/server/server.dsp b/lib/server/server.dsp index fb28378e..8d5f98df 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -1,166 +1,166 @@ -# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=server - Win32 Debug -!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 "server.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 "server.mak" CFG="server - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "server - Win32 Debug" (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)" == "server - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "server - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "server - Win32 Release" -# Name "server - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CClientProxy.cpp -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_0.cpp -# End Source File -# Begin Source File - -SOURCE=.\CConfig.cpp -# End Source File -# Begin Source File - -SOURCE=.\CHTTPServer.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsPrimaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryClient.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CServer.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CClientProxy.h -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_0.h -# End Source File -# Begin Source File - -SOURCE=.\CConfig.h -# End Source File -# Begin Source File - -SOURCE=.\CHTTPServer.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsPrimaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryClient.h -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CServer.h -# End Source File -# Begin Source File - -SOURCE=.\IPrimaryScreenFactory.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=server - Win32 Debug +!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 "server.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 "server.mak" CFG="server - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "server - Win32 Debug" (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)" == "server - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "server - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "server - Win32 Release" +# Name "server - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClientProxy.cpp +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.cpp +# End Source File +# Begin Source File + +SOURCE=.\CConfig.cpp +# End Source File +# Begin Source File + +SOURCE=.\CHTTPServer.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServer.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClientProxy.h +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.h +# End Source File +# Begin Source File + +SOURCE=.\CConfig.h +# End Source File +# Begin Source File + +SOURCE=.\CHTTPServer.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryClient.h +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CServer.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreenFactory.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 79a88cc4..9a71482f 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -1,194 +1,194 @@ -# Microsoft Developer Studio Project File - Name="libsynergy" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=synergy - Win32 Debug -!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 "libsynergy.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 "libsynergy.mak" CFG="libsynergy - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "libsynergy - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "libsynergy - Win32 Debug" (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)" == "libsynergy - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "libsynergy - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "libsynergy - Win32 Release" -# Name "libsynergy - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CClipboard.cpp -# End Source File -# Begin Source File - -SOURCE=.\CInputPacketStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\COutputPacketStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\CProtocolUtil.cpp -# End Source File -# Begin Source File - -SOURCE=.\XScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\XSynergy.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CClipboard.h -# End Source File -# Begin Source File - -SOURCE=.\CInputPacketStream.h -# End Source File -# Begin Source File - -SOURCE=.\ClipboardTypes.h -# End Source File -# Begin Source File - -SOURCE=.\COutputPacketStream.h -# End Source File -# Begin Source File - -SOURCE=.\CProtocolUtil.h -# End Source File -# Begin Source File - -SOURCE=.\IClient.h -# End Source File -# Begin Source File - -SOURCE=.\IClipboard.h -# End Source File -# Begin Source File - -SOURCE=.\IPrimaryScreenReceiver.h -# End Source File -# Begin Source File - -SOURCE=.\IScreen.h -# End Source File -# Begin Source File - -SOURCE=.\IScreenEventHandler.h -# End Source File -# Begin Source File - -SOURCE=.\IScreenReceiver.h -# End Source File -# Begin Source File - -SOURCE=.\IScreenSaver.h -# End Source File -# Begin Source File - -SOURCE=.\IServer.h -# End Source File -# Begin Source File - -SOURCE=.\KeyTypes.h -# End Source File -# Begin Source File - -SOURCE=.\MouseTypes.h -# End Source File -# Begin Source File - -SOURCE=.\ProtocolTypes.h -# End Source File -# Begin Source File - -SOURCE=.\XScreen.h -# End Source File -# Begin Source File - -SOURCE=.\XSynergy.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="libsynergy" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=synergy - Win32 Debug +!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 "libsynergy.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 "libsynergy.mak" CFG="libsynergy - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libsynergy - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libsynergy - Win32 Debug" (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)" == "libsynergy - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "libsynergy - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "libsynergy - Win32 Release" +# Name "libsynergy - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\XScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\XSynergy.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CInputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\ClipboardTypes.h +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.h +# End Source File +# Begin Source File + +SOURCE=.\IClient.h +# End Source File +# Begin Source File + +SOURCE=.\IClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreenReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\IScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenEventHandler.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenSaver.h +# End Source File +# Begin Source File + +SOURCE=.\IServer.h +# End Source File +# Begin Source File + +SOURCE=.\KeyTypes.h +# End Source File +# Begin Source File + +SOURCE=.\MouseTypes.h +# End Source File +# Begin Source File + +SOURCE=.\ProtocolTypes.h +# End Source File +# Begin Source File + +SOURCE=.\XScreen.h +# End Source File +# Begin Source File + +SOURCE=.\XSynergy.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/synergy.dsw b/synergy.dsw index efbf7453..b5b1ab12 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -1,242 +1,278 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "all"=.\all.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name synergy - End Project Dependency - Begin Project Dependency - Project_Dep_Name synergyd - End Project Dependency -}}} - -############################################################################### - -Project: "base"=.\lib\base\base.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "client"=.\lib\client\client.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "http"=.\lib\HTTP\http.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "io"=.\lib\io\io.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "makehook"=.\lib\platform\makehook.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name synrgyhk - End Project Dependency -}}} - -############################################################################### - -Project: "mt"=.\lib\mt\mt.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "net"=.\lib\net\net.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "platform"=.\lib\platform\platform.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "server"=.\lib\server\server.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "synergy"=.\cmd\synergy\synergy.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name base - End Project Dependency - Begin Project Dependency - Project_Dep_Name io - End Project Dependency - Begin Project Dependency - Project_Dep_Name mt - End Project Dependency - Begin Project Dependency - Project_Dep_Name net - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsynergy - End Project Dependency - Begin Project Dependency - Project_Dep_Name platform - End Project Dependency - Begin Project Dependency - Project_Dep_Name client - End Project Dependency -}}} - -############################################################################### - -Project: "synergyd"=.\cmd\synergyd\synergyd.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name base - End Project Dependency - Begin Project Dependency - Project_Dep_Name http - End Project Dependency - Begin Project Dependency - Project_Dep_Name io - End Project Dependency - Begin Project Dependency - Project_Dep_Name mt - End Project Dependency - Begin Project Dependency - Project_Dep_Name net - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsynergy - End Project Dependency - Begin Project Dependency - Project_Dep_Name platform - End Project Dependency - Begin Project Dependency - Project_Dep_Name makehook - End Project Dependency - Begin Project Dependency - Project_Dep_Name server - End Project Dependency -}}} - -############################################################################### - -Project: "libsynergy"=.\lib\synergy\libsynergy.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "synrgyhk"=.\lib\platform\synrgyhk.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "all"=.\all.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name synergy + End Project Dependency + Begin Project Dependency + Project_Dep_Name synergyd + End Project Dependency + Begin Project Dependency + Project_Dep_Name launcher + End Project Dependency +}}} + +############################################################################### + +Project: "base"=.\lib\base\base.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "client"=.\lib\client\client.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "http"=.\lib\HTTP\http.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "io"=.\lib\io\io.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "launcher"=.\cmd\launcher\launcher.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name http + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name platform + End Project Dependency + Begin Project Dependency + Project_Dep_Name server + End Project Dependency +}}} + +############################################################################### + +Project: "libsynergy"=.\lib\synergy\libsynergy.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "makehook"=.\lib\platform\makehook.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name synrgyhk + End Project Dependency +}}} + +############################################################################### + +Project: "mt"=.\lib\mt\mt.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "net"=.\lib\net\net.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "platform"=.\lib\platform\platform.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "server"=.\lib\server\server.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "synergy"=.\cmd\synergy\synergy.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name libsynergy + End Project Dependency + Begin Project Dependency + Project_Dep_Name platform + End Project Dependency + Begin Project Dependency + Project_Dep_Name client + End Project Dependency +}}} + +############################################################################### + +Project: "synergyd"=.\cmd\synergyd\synergyd.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name base + End Project Dependency + Begin Project Dependency + Project_Dep_Name http + End Project Dependency + Begin Project Dependency + Project_Dep_Name io + End Project Dependency + Begin Project Dependency + Project_Dep_Name mt + End Project Dependency + Begin Project Dependency + Project_Dep_Name net + End Project Dependency + Begin Project Dependency + Project_Dep_Name libsynergy + End Project Dependency + Begin Project Dependency + Project_Dep_Name platform + End Project Dependency + Begin Project Dependency + Project_Dep_Name makehook + End Project Dependency + Begin Project Dependency + Project_Dep_Name server + End Project Dependency +}}} + +############################################################################### + +Project: "synrgyhk"=.\lib\platform\synrgyhk.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + From 639cb26a7d70b35a4d787a45a07d3a1cfe2da7fa Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Aug 2002 11:50:07 +0000 Subject: [PATCH 320/807] removed pre-instantiation of templates in header file. --- lib/mt/CCondVar.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/mt/CCondVar.h b/lib/mt/CCondVar.h index 90552f2e..5310e148 100644 --- a/lib/mt/CCondVar.h +++ b/lib/mt/CCondVar.h @@ -232,9 +232,4 @@ CCondVar::operator const T&() const return m_data; } - -// force instantiation of these common types -template class CCondVar; -template class CCondVar; - #endif From 1d9efb0e3a7dea9a06cb0c3dd634fd2040eb2dc4 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Aug 2002 11:50:49 +0000 Subject: [PATCH 321/807] added TODO file and top-level rule to make zip file of distribution files. --- Makefile.am | 7 +++++++ TODO | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 TODO diff --git a/Makefile.am b/Makefile.am index 2bd4b9e6..0ed794fe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,6 +26,7 @@ EXTRA_DIST = \ FAQ \ HISTORY \ PORTING \ + TODO \ all.dsp \ synergy.dsw \ doc/doxygen.cfg.in \ @@ -61,3 +62,9 @@ dist-rpm: dist mv -f $(RPMTOPDIR)/SRPMS/*.rpm . && \ mv -f $(RPMTOPDIR)/RPMS/*/*.rpm . && \ rm -rf $(RPMTOPDIR) + +# build zip +# FIXME -- have automake generate this rule for us +dist-zip: distdir + zip -r $(distdir).zip $(distdir) + -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) diff --git a/TODO b/TODO new file mode 100644 index 00000000..b8a1efac --- /dev/null +++ b/TODO @@ -0,0 +1,53 @@ +Synergy To Do List +================== + +Things to do to synergy, in no particular order: + +* Provide GUI configuration + + There's a GUI tool on win32 but no other platforms. It'd be nice + if the tool allowed users to drag screen icons around to set the + links between them, but this complicated because links aren't + necessarily symmetrical. + +* Provide taskbar feedback + + An icon indicating synergy's state in the taskbar would be nice. + It could also provide a means for viewing recent log messages, + stopping synergy, and forcing disconnection. + +* Support non-ASCII keyboards + + Synergy currently supports only ASCII characters typed on the + keyboard. It does, however, support Unicode clipboard text. + +* Write man pages + +* Finish PORTING guide + +* Port to other platforms + + An incomplete list of desired platforms: + * MacOS 9 + * MacOS 10 + * BSD + * Irix + * Solaris + * HP-UX + * AIX + +* Provide a nice win32 installer/uninstaller + + Synergy doesn't have any special needs so even just unzipping is + satisfactory, but a proper installer would be nice. And, more + importantly, it should provide an uninstaller. + +* Add more clipboard formats + + Synergy currently supports only text on the clipboard. It should + support more formats, such as images and sound. For each format, + some canonical type must be chosen. For text, that's UTF-8 with + \n for newlines. For images, it might be BMP or PNG. Whatever it + is it should losslessly support any type it might be converted to. + The type is converted to each platform's native type. For example, + BMP for images on win32. From 2bea3d14605609b4e2733430db36699ab0488483 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Aug 2002 11:51:25 +0000 Subject: [PATCH 322/807] added TODO and example/synergy.conf to documenation files. --- dist/rpm/synergy.spec.in | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in index 81b75a00..124f0e55 100644 --- a/dist/rpm/synergy.spec.in +++ b/dist/rpm/synergy.spec.in @@ -35,4 +35,15 @@ rm -rf $RPM_BUILD_ROOT %defattr(-, root, root) /usr/bin/synergy /usr/bin/synergyd -%doc AUTHORS BUGS COPYING ChangeLog FAQ INSTALL NEWS README +%doc AUTHORS +%doc BUGS +%doc COPYING +%doc ChangeLog +%doc FAQ +%doc HISTORY +%doc INSTALL +%doc NEWS +%doc PORTING +%doc README +%doc TODO +%doc examples/synergy.conf From 4c6eb241a643e7d1851f889d359739065c3caf43 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Aug 2002 22:43:07 +0000 Subject: [PATCH 323/807] Moved synergy client to cmd/synergyc and renamed it synergyc. Moved synergy server to cmd/synergys and renamed it synergys. Updated documentation to reflect that and the win32 launcher. --- BUGS | 11 -- INSTALL | 183 +++++++++++------- NEWS | 10 +- PORTING | 156 ++++++++++++--- README | 138 ++++++++----- cmd/Makefile.am | 4 +- cmd/launcher/launcher.cpp | 2 +- cmd/{synergy => synergyc}/Makefile.am | 14 +- cmd/{synergy => synergyc}/resource.h | 0 .../synergy.cpp => synergyc/synergyc.cpp} | 2 +- .../synergy.dsp => synergyc/synergyc.dsp} | 28 +-- .../synergy.ico => synergyc/synergyc.ico} | Bin .../synergy.rc => synergyc/synergyc.rc} | 0 cmd/{synergyd => synergys}/Makefile.am | 14 +- cmd/{synergyd => synergys}/resource.h | 0 .../synergyd.cpp => synergys/synergys.cpp} | 16 +- .../synergyd.dsp => synergys/synergys.dsp} | 26 +-- .../synergy.ico => synergys/synergys.ico} | Bin .../synergyd.rc => synergys/synergys.rc} | 0 configure.in | 4 +- dist/rpm/synergy.spec.in | 8 +- synergy.dsw | 8 +- 22 files changed, 398 insertions(+), 226 deletions(-) rename cmd/{synergy => synergyc}/Makefile.am (89%) rename cmd/{synergy => synergyc}/resource.h (100%) rename cmd/{synergy/synergy.cpp => synergyc/synergyc.cpp} (99%) rename cmd/{synergy/synergy.dsp => synergyc/synergyc.dsp} (78%) rename cmd/{synergy/synergy.ico => synergyc/synergyc.ico} (100%) rename cmd/{synergy/synergy.rc => synergyc/synergyc.rc} (100%) rename cmd/{synergyd => synergys}/Makefile.am (90%) rename cmd/{synergyd => synergys}/resource.h (100%) rename cmd/{synergyd/synergyd.cpp => synergys/synergys.cpp} (98%) rename cmd/{synergyd/synergyd.dsp => synergys/synergys.dsp} (85%) rename cmd/{synergyd/synergy.ico => synergys/synergys.ico} (100%) rename cmd/{synergyd/synergyd.rc => synergys/synergys.rc} (100%) diff --git a/BUGS b/BUGS index 912ef100..e429fda7 100644 --- a/BUGS +++ b/BUGS @@ -6,11 +6,6 @@ Report bugs to: synergy@groundhog.pair.com When reporting bugs, please include the version of the operating system you're using and what locale you use. -* Documentation is missing - - There are currently no man pages or other documents describing the - usage of the various commands. - * Not all keystrokes are handled Certain keystrokes are not captured by the synergy server and, @@ -100,9 +95,3 @@ system you're using and what locale you use. The automake configuration isn't complete so synergy won't build properly on some (many) systems. - -* Only text is supported on the clipboard - - The clipboard supports Unicode and current locale encoded text but - there are numerous other data types it should support, e.g. images, - sound, raw binary data, etc. diff --git a/INSTALL b/INSTALL index ea076b28..d5a49793 100644 --- a/INSTALL +++ b/INSTALL @@ -13,31 +13,19 @@ To build synergy from the sources you'll need the following: * gcc 2.95 (or up?) * X11R4 or up headers and libraries -If building from CVS on Linux you'll also need: - * autoconf 2.52 (older versions may work) - * automake 1.5 (older versions may work) - Configuring the build --------------------- This step is only necessary when building on Linux. -If you're building synergy from CVS you need to create the configure -script and the Makefile.in files. To do that: - - % aclocal - % autoheader - % autoconf - % automake - To configure the build for your platform use the configure script: - % ./configure + ./configure For a list of options to configure use: - % ./configure --help + ./configure --help Building @@ -45,22 +33,13 @@ Building Windows: Start VC++ and open `synergy.dsw'. Set the active configuration - (Build | Set Active Configuration) to `All - Debug' or `All - Release'. - Then build. - - VC++ 6.0 cannot handle project files that lack \r\n newlines. If you - retrieved synergy via CVS then the files should be fine. But if the - workspace appears to be empty then close VC++ and add \r to each line. - A convenient way to do that is to get `fixcrlf.exe' from the Internet, - search the synergy files for `*.dsp' and drag-and-drop all the project - files onto fixcrlf.exe. - - Binaries are built into ./Debug or ./Release. + (Build | Set Active Configuration) to `All - Debug' or `All - Release' + then build. Binaries are built into ./Debug or ./Release. Linux: Simply enter: - % make + make This will build the client and server and leave them in their respective source directories. @@ -71,22 +50,84 @@ Installing Windows: There is no support for creating an installer for synergy or installing - the files in a particular location. The only requirement for installed - files is that synrgyhk.dll is in the same directory as synergyd.exe or - in one of the system directories. + the files in a particular location. Instead, just copy the following + files from the Debug or Release directory to a directory you choose + (perhaps under the Program Files directory): + + * synergy.exe + * synergyc.exe + * synergys.exe + * synrgyhk.dll Linux: - % make install + make install will install the client and server into /usr/local/bin unless you specified a different directory when you ran configure. -See `Starting Automatically' for details on how to have synergy start up -automatically when the computer starts. +See `Starting Automatically on ...' below for details on how to have +synergy start up automatically when the computer starts. -Configuring the Server ----------------------- +Running on Windows +------------------ + +Double click `synergy' on the server computer. The server is the +computer who's mouse and keyboard will be shared. This brings up a +dialog that lets you configure the server then test out the +configuration or start the server. + +First configure the server. Click the `Server' radio button + + * Click the `Server' radio button + * Click `Add' to add the server to the `Screens' list + * Enter the name of server (the computer name is recommended) + * Enter other names the server is known by + * Click OK + * Use `Add' to add your other computers + * Use the controls under `Layout' to link screens together + * Click (once) on the server's name in the `Screens' list + * Choose the screen to the left of the server + * Use `---' if there is no screen to the left of the server + * Choose the screens to the right, above and below the server + * Repeat the above steps for all the other screens + * Enter the server's screen name next to `Screen Name' + * This defaults to the computer's name (recommended) + * Click `Test' + +The server will start and you'll see a console window with log messages +telling you about synergy's progress. If an error occurs you'll get a +dialog box telling you synergy is about to quit; read the log messages +to determine the problem then correct it and try `Test' again. + +Now that the server is running, you'll need to start a client. On any +client computer, double click `synergy'. Of course, you'll need to +have installed the four files listed under `Installing' above on the +client computer. Then configure the client: + + * Click the `Client' radio button + * Enter the server's computer name in `Server Host Name' + * Do not use any of the server's screen names, unless one of those + is also the computer name + * Enter the client's screen name next to `Screen Name' + * This defaults to the computer's name (recommended) + * This must be one of the names in the server's `Screens' list + * Click `Test' + +If all goes well, the client connects to the server successfully and +the mouse and keyboard are shared. If an error occurs you'll get a +dialog box telling you synergy is about to quit; read the log messages +to determine the problem then correct it and try `Test' again. When +everything is working correctly, install the software on the other +client computers (if any) and repeat the steps for configuring the +client on each. + +See `Starting Automatically on Windows' below for configuring synergy +to start automatically when the computer starts. + + +Configuring the Server on Linux +------------------------------- The synergy server requires configuration. The configuration file is a plain text file broken into sections. Each section has the form: @@ -166,11 +207,11 @@ The file can have the following sections. The synergy server will try certain pathnames to load the configuration file if the user doesn't specify a path using the `--config' command -line option. `synergyd --help' reports those pathnames. +line option. `synergys --help' reports those pathnames. -Running the Server ------------------- +Running the Server on Linux +--------------------------- Run the server on the computer that has the keyboard and mouse to be shared. You must have prepared a configuration file before @@ -180,29 +221,28 @@ clients but that's not required. Run the synergy server on the server system using the following command line: - synergyd -f --config + synergys -f --config Replace with the path to the configuration file. -The `-f' option causes synergyd to run in the foreground. This is +The `-f' option causes synergys to run in the foreground. This is recommended until you've verified that the configuration works. If you didn't include the system's hostname in the configuration file (either as a screen name or an alias) then you'll have to add `--name ' to the command line, where is -a name in the configuration file. You can use `synergyd --help' +a name in the configuration file. You can use `synergys --help' for a list of command line options. -On Windows, the file synrgyhk.dll must be in the same directory as -synergyd.exe or in a system directory otherwise the server will -refuse to start. +See `Starting Automatically on Linux' below for running synergy +automatically when the X server starts. -Running the Client ------------------- +Running the Client on Linux +--------------------------- Run the client on all computers that aren't the server using the following command line: - synergy -f --no-camp + synergyc -f --no-camp Replace with the hostname or address of the server system. The `-f' option causes synergy to run in the @@ -228,7 +268,7 @@ the problem. Here are typical problems and possible solutions: check permission to open the X display; check that the DISPLAY environment variable is set. * already connected: - check that synergy isn't already running. + check that the synergy client isn't already running. * refused client: add client to the server's configuration file. * connection failed: @@ -240,33 +280,30 @@ the problem. Here are typical problems and possible solutions: Once all the clients are running, try moving the mouse to each screen. Be sure to check all the configured links. +See `Starting Automatically on Linux' below for running synergy +automatically when the X server starts. -Starting Automatically ----------------------- -You can setup synergy to start automatically when your computer does. +Starting Automatically on Windows +--------------------------------- -Windows: - Start the client or server normally except add `--install' as the - first option on the command line, followed by the usual options - except do not include `-f' or `--no-daemon'. This will install - synergy as a service that will be started at system boot. +When all the clients work you're ready to have synergy start +automatically each time the system (re)starts. Click `Stop' on all +the clients then on the server'. Then Click `Start' on the server. +If it starts successfully then click `OK' to close the message box +and the dialog. The synergy server is now running and will be +automatically started each time the system is started. Click `Start' +then `OK' on each of the clients to start the synergy client and +automatically have it start each time the computer starts. - Note that the system will look for synergy wherever it was when it - was installed. The server will also look for the configuration - file in the path specified by the `--config' option, if supplied, - and for synrgyhk.dll in the same directory as synergyd.exe or in - a system directory. Therefore, it's important that these files be - on local hard drives; network drives or removable devices may not - be available at boot. +If you ever want to prevent synergy from starting automatically when +the computer does, double click `synergy', choose `Client' or `Server' +(whichever the computer is running as), then click `No Auto-Start'. +Click `OK' to dismiss the message box then `Quit' to close the dialog. - On the Windows NT family you can start and stop the service at any - time using the Services control panel (under Administrative Tools - on Windows 2000 and XP). On the Windows 95 family you cannot - start or stop the service. - To uninstall the service, run the client or server with just the - `--uninstall' command line option. +Starting Automatically on Linux +------------------------------- Linux: Synergy requires an X server. That means a server must be @@ -282,8 +319,8 @@ Linux: anyplace the script calls exit, start the client with something like: - /usr/bin/killall synergy - /usr/local/bin/synergy [] + /usr/bin/killall synergyc + /usr/local/bin/synergyc [] must not include `-f' or `--no-daemon'. Change the paths as necessary. It's important to make sure no old copies @@ -291,8 +328,8 @@ Linux: To start the server use something like: - /usr/bin/killall synergyd - /usr/local/bin/synergyd [] --config + /usr/bin/killall synergys + /usr/local/bin/synergys [] --config Again, must not include `-f' or `--no-daemon'. If the configuration pathname is one of the default locations then @@ -337,7 +374,7 @@ occurs that is sure to happen every time. For example, the server will exit immediately if it can't find itself in the configuration. On X11 both the client and server will also terminate if the connection to the X server is lost. Since xdm will normally restart -the server and synergy, this is the correct behavior. +the X server and synergy, this is the correct behavior. Server Command Line Options diff --git a/NEWS b/NEWS index 1473e6db..fd79e553 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@ Synergy News ============ -* Initial version of synergy released. Supports ASCII keys, 5 button - wheel mouse, Unicode text clipboard, and screen saver synchronization. - Runs on Linux and Microsoft Windows. +* Aug-11-2002 - Synergy 0.9.8 released + + Supports any number of clients under Linux or Windows 95 or NT4 + or later. Includes mouse and keyboard sharing, clipboard + synchronization and screen saver synchronization. Supports ASCII + keystrokes, 5 button mouse with wheel, and Unicode text clipboard + format. diff --git a/PORTING b/PORTING index 6740b032..2689c73f 100644 --- a/PORTING +++ b/PORTING @@ -41,19 +41,99 @@ Coding Style Guide Synergy uses many coding conventions. Contributed code should following these guidelines. +- Symbol Naming + Names always begin with a letter (never an underscore). The first + letter of interior names are always capitalized. Acronyms should + be all uppercase. For example: myTextAsASCII. + + Names come it two flavors: leading capital and leading lowercase. + The former have the first character capitalized and the latter + don't. In the following table, leading capital names are indicated + by `Name' and leading lowercase names by `name'. + + The naming convention for various things are: + + * Exceptions -- X + Name XMyException + * Interfaces -- I + Name IMyInterface + * Template Classes -- T + Name TMyTemplate<> + * Other Classes -- C + Name CMyClass + * Enumerations -- E + Name EMyEnumeration + * Constants -- k + Name kMyConstant + * Data Members -- m_ + name m_myDataMember + * Methods -- name myMethod + * Functions -- name myFunction + * Variables -- name myVariable + + Exceptions are types that get thrown and are generally derived + (possibly indirectly) from XBase. Interfaces are derived (possibly + indirectly) from IInterface and have only pure virtual functions. + Other classes are classes that aren't exceptions or interfaces. + Constants include global constants and enumerants. + + Method names should usually have the form `verbObject'. For example: + * isGameOn() + * getBeer() + * pressPowerButton() + * setChannel() + In general, use `get' and `set' to read and write state but use `is' + to read boolean state. Note that classes that contain only `is', + `get', and `set' are probably plain old data; you might want to + consider using public data members only or, better, refactor your + design to have classes that actually do something more than just + hold data. + +- File Naming + Each class should have one source and one header file. If the + class is named `CMyClass' then the source file should be named + `CMyClass.cpp' and the header file `CMyClass.h'. + + Headers files not containing a class should have some meaningful + name with a leading capital (e.g. `Version.h'). + + Source files without a header file have a leading lowercase name. + Only files containing the entry point for an application should + lack a header file. + - Dependencies * No circular library dependencies Library dependencies form an acyclic graph. Conceptually libraries can be arranged in layers where each library only - references libraries in layers below it, not in the same - layer or layers above it. The makefiles build the lowest - layer libraries first and work upwards. + references libraries in layers below it, not in the same layer + or layers above it. The makefiles build the lowest layer + libraries first and work upwards. * Avoid circular uses-a relationships When possible, design classes with one-way uses-a relationships and avoid cycles. This makes it easier to understand the code. However, sometimes it's not always practical so it is permitted. + * Included files in headers + Headers should #include only the necessary headers. In + particular, if a class is referenced in a header file only as a + pointer or a reference then use `class COtherClass;' instead of + `#include "COtherClass.h".' + + * #include syntax + Non-synergy header files must be included using angle brackets + while synergy header files must be included using double quotes. + #include "CSynergyHeader.h" + #include + The file name in a #include must not be a relative path unless + it's a system header file and it's customary to use a relative + path, e.g. `#include '. Use compiler options to + add necessary directories to the include search path. + + * Included file ordering + Files should be included in the following order: + * Header for source file + The first include for CMyClass.cpp must be CMyClass.h. + * Other headers in directory, sorted alphabetically + * Headers for each library, sorted alphabetically per library + Include headers from the library closest in the dependency graph + first, then the next farthest, etc. sort alphabetically within + each library. + * System headers + - C++ * C++ features Synergy uses the following more recent C++ features: @@ -134,42 +214,53 @@ following these guidelines. directly or indirectly, from the class and any of those classes have full access to the protected data. + * Plain old data + A class that merely contains data and doesn't perform operations + on that data (other than reads and writes) is plain old data (POD). + POD should have only public data members and non-copy constructors. + It must not have any methods other than constructors, not even a + destructor or assignment operators, nor protected or private data. + + * Avoid using friend + Avoid declaring friend functions or classes. They're sometimes + necessary for operator overloading. If you find it necessary to + add friends to some class C, consider creating a utility class U. + A utility class is declared as the only friend of C and provides + only static methods. Each method forwards to a private method on + an object of C type (passed as a parameter to the U's method). + This makes maintenance easier since only U has friend access to C + and finding any call to U is trivial (they're prefixed by U::). + * Don't test for NULL when using `delete' or `delete[]' It's unnecessary since delete does it anyway. -- Naming - Names always begin with a letter (never an underscore). The first - letter of interior names are always capitalized. Acronyms should - be all uppercase. For example: myTextAsASCII. +- Makefiles + Automake's makefiles (named Makefile.am) have a few requirements: + * Define the following macros at the top of the file: + NULL = + DEPTH = + VDEPTH = ./$(VPATH)/$(DEPTH) + is `..', `../..', `../../..', etc, + whichever references the top directory of the synergy tree. For + example, for a subdirectory of the top level use `..', for a + subdirectory of a subdirectory of the top level use `../..'. + * Lists should have one item per line and end in $(NULL). For + example: + EXTRA_DIST = \ + kiwi.txt \ + mango.cpp \ + papaya.h \ + $(NULL) + Indentation must use tabs in a makefile. Line continuations + (backslashes) should be aligned using tabs. + * Lists of files should be sorted alphabetically. Lists of + subdirectories must be in the desired build order. - Names come it two flavors: leading capital and leading lowercase. - The former have the first character capitalized and the latter - don't. In the following table, leading capital names are indicated - by `Name' and leading lowercase names by `name'. - - The naming convention for various things are: - - * Exceptions -- X + Name XMyException - * Interfaces -- I + Name IMyInterface - * Template Classes -- T + Name TMyTemplate<> - * Other Classes -- C + Name CMyClass - * Enumerations -- E + Name EMyEnumeration - * Constants -- k + Name kMyConstant - * Data Members -- m_ + name m_myDataMember - * Methods -- name myMethod - * Functions -- name myFunction - * Variables -- name myVariable - - Exceptions are types that get thrown and are generally derived - (possibly indirectly) from XBase. Interfaces are derived (possibly - indirectly) from IInterface and have only pure virtual functions. - Other classes are classes that aren't exceptions or interfaces. - Constants include global constants and enumerants. - -- Formatting +- Source Formatting Every project has its own formatting style and no style satisfies everyone. New code should be consistent with existing code: + * All files should include the copyright and license notice * Use tabs to indent * Tabs are 4 columns * Open braces ({) go on same line as introducing statement @@ -190,6 +281,7 @@ following these guidelines. * Use Qt style comments for extraction by doxygen * Mark incomplete or buggy code with `FIXME' + Class Relationships ------------------- diff --git a/README b/README index 351eb35d..87245fbd 100644 --- a/README +++ b/README @@ -25,17 +25,19 @@ The synergy project page is: http://sourceforge.net/projects/synergy2/ Report bugs to: -synergy@groundhog.pair.com +http://sourceforge.net/tracker/?func=add&group_id=59275&atid=490467 Please see the following files for more information: AUTHORS -- The list of synergy's authors BUGS -- A list of known bugs and limitations COPYING -- The license synergy is release under +FAQ -- Frequently asked questions about synergy HISTORY -- A brief history of synergy INSTALL -- Detailed build and installation instructions NEWS -- News about the synergy project PORTING -- Porting guide for developers +TODO -- List of things to add to synergy System Requirements @@ -80,9 +82,36 @@ server on the primary screen and the synergy client on all the secondary screens. If it's easier just install both on all of the systems. -Third, you create a configuration file for the server. Only the -server requires configuration. Create a text file named -synergy.conf with the following: +Third, you configure and start the server. + Windows + ------- + Run `synergy' by double clicking on it. This brings up a dialog. + Configure the server: + + * Click the `Server' radio button + * Click `Add' to add the server to the `Screens' list + * Enter the name of server (the computer name is recommended) + * Enter other names the server is known by + * Click OK + * Use `Add' to add your other computers + * Use the controls under `Layout' to link screens together + * Click (once) on the server's name in the `Screens' list + * Choose the screen to the left of the server + * Use `---' if there is no screen to the left of the server + * Choose the screens to the right, above and below the server + * Repeat the above steps for all the other screens + * Enter the server's screen name next to `Screen Name' + * This defaults to the computer's name (recommended) + + Now click `Test'. The server will start and you'll see a console window + with log messages telling you about synergy's progress. If an error + occurs you'll get a dialog box telling you synergy is about to quit; + read the log messages to determine the problem then correct it and try + `Test' again. + + Linux + ----- + Create a text file named synergy.conf with the following: section: screens : @@ -95,36 +124,55 @@ synergy.conf with the following: left = end -Replace each occurrence of `' with the host name of the -primary screen computer and `' with the host name of a -secondary screen computer. In the above example, is to -the right of and is to the left of . -If necessary you should replace `right' and `left' with `left', -`right', 'up', or `down'. If you have more than two computers -you can add those too: add each computer's host name in the -`screens' section and add the appropriate links. + Replace each occurrence of `' with the host name of the + primary screen computer and `' with the host name of a + secondary screen computer. In the above example, is to + the right of and is to the left of . + If necessary you should replace `right' and `left' with `left', + `right', 'up', or `down'. If you have more than two computers + you can add those too: add each computer's host name in the + `screens' section and add the appropriate links. -Finally, you start the server then all of the clients. Normally -synergy wants to run "in the background." It detaches from the -terminal and doesn't have a visible window, effectively -disappearing from view. Until you're sure your configuration -works you should start synergy "in the foreground" using the `-f' -command line option. + Now start the server. Normally synergy wants to run "in the + background." It detaches from the terminal and doesn't have a + visible window, effectively disappearing from view. Until you're + sure your configuration works you should start synergy "in the + foreground" using the `-f' command line option: -To get a command line on Windows, choose Run... from the Start -menu. Type `cmd' if you're using the Windows NT family or -`command' if you're using the Windows 95 family and press enter. + synergys -f --config synergy.conf -To start the server, enter the following on the command line: + Check the reported messages for errors. Use ctrl+c to stop synergy, + correct any problems, and start it again. - synergyd -f --config synergy.conf +Finally, start the clients. + Windows + ------- + Run `synergy' on the client by double clicking on it. Configure the + client: -To start a client, enter the following: + * Click the `Client' radio button + * Enter the server's computer name in `Server Host Name' + * Do not use any of the server's screen names, unless one of those + is also the computer name + * Enter the client's screen name next to `Screen Name' + * This defaults to the computer's name (recommended) + * This must be one of the names in the server's `Screens' list + * Click `Test' - synergy -f + If an error occurs you'll get a dialog box telling you synergy is + about to quit; read the log messages to determine the problem then + correct it and try `Test' again. -where `' is replaced by the name of the computer -running the synergy server. + Linux + ----- + + To start a client, enter the following: + + synergyc -f + + where `' is replaced by the name of the computer + running the synergy server. If an error is reported use ctrl+c to + stop synergy, fix the error, and try again. Both the client and server should immediately report the connection or an error. If successful, you should now be able to move the @@ -134,20 +182,28 @@ except use the edge of the screens to jump to other screens. You can also cut-and-paste across computers. Currently, only text transfers between computers. Start the remaining clients. -Be aware that not all keystrokes can be handled by synergy. In -particular, ctrl+alt+del is not handled. You cannot use synergy -to log into a Windows NT family system that requires the user to -press ctrl+alt+del to log on. You'll need to keep that computer's -keyboard handy in order to log on. - Once the configuration is verified, see the instructions in INSTALL -under `Starting Automatically' for details on running synergy in -the background and on starting synergy automatically when you start +under `Starting Automatically on ...' for details on running synergy +in the background and on starting synergy automatically when you start your computers. Tips and Tricks --------------- +* Be aware that not all keystrokes can be handled by synergy. In + particular, ctrl+alt+del is not handled. You cannot use synergy + to log into a Windows NT family system that requires the user to + press ctrl+alt+del to log on. You'll need to keep that computer's + keyboard handy in order to log on. + +* To work around the lack of ctrl+alt+del, you can configure Windows + 2000 and XP to not require ctrl+alt+del to log on using the System + control panel. If you're the only user of an NT system you might + want to enable auto-logon. In any case, you should keep each + computer's keyboard handy, perhaps under the desk or on top of the + computer itself. If the system supports USB you should also be able + to attach/detach a keyboard as necessary. + * A screen can be its own neighbor. That allows a screen to "wrap". For example, if a configuration linked the left and right sides of a screen to itself then moving off the left of the screen would put @@ -169,14 +225,6 @@ Tips and Tricks the extension). Starting with 4.0 you must enable the corresponding option in your .xscreensaver file. -* To work around the lack of ctrl+alt+del, you can configure Windows - 2000 and XP to not require ctrl+alt+del to log on using the System - control panel. If you're the only user of an NT system you might - want to enable auto-logon. In any case, you should keep each - computer's keyboard handy, perhaps under the desk or on top of the - computer itself. If the system supports USB you should also be able - to attach/detach a keyboard as necessary. - * Synergy automatically converts newlines in clipboard text (Linux expects \n to end each line while Windows expects \r\n). @@ -193,6 +241,6 @@ Bug Reports Synergy is being improved all the time but we can only fix problems that we know about. Please let us know of any problems you encounter, -including confusing or unhelpful documentation. Send reports to: +including confusing or unhelpful documentation. File reports at: - synergy@groundhog.pair.com + http://sourceforge.net/tracker/?func=add&group_id=59275&atid=490467 diff --git a/cmd/Makefile.am b/cmd/Makefile.am index e267fba6..c1953b4c 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -17,8 +17,8 @@ VDEPTH = ./$(VPATH)/$(DEPTH) SUBDIRS = \ launcher \ - synergy \ - synergyd \ + synergyc \ + synergys \ $(NULL) EXTRA_DIST = \ diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index e8d96cc6..e18aa892 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -26,7 +26,7 @@ #define CONFIG_NAME "synergy.sgc" #define CLIENT_APP "synergyc.exe" -#define SERVER_APP "synergyd.exe" +#define SERVER_APP "synergys.exe" typedef std::vector CStringList; diff --git a/cmd/synergy/Makefile.am b/cmd/synergyc/Makefile.am similarity index 89% rename from cmd/synergy/Makefile.am rename to cmd/synergyc/Makefile.am index f1b78a25..98ab2824 100644 --- a/cmd/synergy/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -17,20 +17,20 @@ VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ resource.h \ - synergy.dsp \ - synergy.ico \ - synergy.rc \ + synergyc.dsp \ + synergyc.ico \ + synergyc.rc \ $(NULL) MAINTAINERCLEANFILES = \ Makefile.in \ $(NULL) -bin_PROGRAMS = synergy -synergy_SOURCES = \ - synergy.cpp \ +bin_PROGRAMS = synergyc +synergyc_SOURCES = \ + synergyc.cpp \ $(NULL) -synergy_LDADD = \ +synergyc_LDADD = \ $(DEPTH)/lib/client/libclient.a \ $(DEPTH)/lib/platform/libplatform.a \ $(DEPTH)/lib/synergy/libsynergy.a \ diff --git a/cmd/synergy/resource.h b/cmd/synergyc/resource.h similarity index 100% rename from cmd/synergy/resource.h rename to cmd/synergyc/resource.h diff --git a/cmd/synergy/synergy.cpp b/cmd/synergyc/synergyc.cpp similarity index 99% rename from cmd/synergy/synergy.cpp rename to cmd/synergyc/synergyc.cpp index f9dbb118..94771f79 100644 --- a/cmd/synergy/synergy.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -42,7 +42,7 @@ #if WINDOWS_LIKE #define DAEMON_NAME "Synergy Client" #elif UNIX_LIKE -#define DAEMON_NAME "synergy" +#define DAEMON_NAME "synergyc" #endif // diff --git a/cmd/synergy/synergy.dsp b/cmd/synergyc/synergyc.dsp similarity index 78% rename from cmd/synergy/synergy.dsp rename to cmd/synergyc/synergyc.dsp index a55230a2..ad4e1f1c 100644 --- a/cmd/synergy/synergy.dsp +++ b/cmd/synergyc/synergyc.dsp @@ -1,24 +1,24 @@ -# Microsoft Developer Studio Project File - Name="synergy" - Package Owner=<4> +# Microsoft Developer Studio Project File - Name="synergyc" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 -CFG=synergy - Win32 Debug +CFG=synergyc - Win32 Debug !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 "synergy.mak". +!MESSAGE NMAKE /f "synergyc.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 "synergy.mak" CFG="synergy - Win32 Debug" +!MESSAGE NMAKE /f "synergyc.mak" CFG="synergyc - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE -!MESSAGE "synergy - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "synergy - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE "synergyc - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "synergyc - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project @@ -29,7 +29,7 @@ CPP=cl.exe MTL=midl.exe RSC=rc.exe -!IF "$(CFG)" == "synergy - Win32 Release" +!IF "$(CFG)" == "synergyc - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -56,7 +56,7 @@ LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../../Release/synergyc.exe" -!ELSEIF "$(CFG)" == "synergy - Win32 Debug" +!ELSEIF "$(CFG)" == "synergyc - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 @@ -87,18 +87,18 @@ LINK32=link.exe # Begin Target -# Name "synergy - Win32 Release" -# Name "synergy - Win32 Debug" +# Name "synergyc - Win32 Release" +# Name "synergyc - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=.\synergy.cpp +SOURCE=.\synergyc.cpp # End Source File # Begin Source File -SOURCE=.\synergy.rc +SOURCE=.\synergyc.rc # End Source File # End Group # Begin Group "Header Files" @@ -114,7 +114,7 @@ SOURCE=.\resource.h # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File -SOURCE=.\synergy.ico +SOURCE=.\synergyc.ico # End Source File # End Group # End Target diff --git a/cmd/synergy/synergy.ico b/cmd/synergyc/synergyc.ico similarity index 100% rename from cmd/synergy/synergy.ico rename to cmd/synergyc/synergyc.ico diff --git a/cmd/synergy/synergy.rc b/cmd/synergyc/synergyc.rc similarity index 100% rename from cmd/synergy/synergy.rc rename to cmd/synergyc/synergyc.rc diff --git a/cmd/synergyd/Makefile.am b/cmd/synergys/Makefile.am similarity index 90% rename from cmd/synergyd/Makefile.am rename to cmd/synergys/Makefile.am index 5868830f..9a200c51 100644 --- a/cmd/synergyd/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -17,20 +17,20 @@ VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ resource.h \ - synergy.ico \ - synergyd.dsp \ - synergyd.rc \ + synergys.ico \ + synergys.dsp \ + synergys.rc \ $(NULL) MAINTAINERCLEANFILES = \ Makefile.in \ $(NULL) -bin_PROGRAMS = synergyd -synergyd_SOURCES = \ - synergyd.cpp \ +bin_PROGRAMS = synergys +synergys_SOURCES = \ + synergys.cpp \ $(NULL) -synergyd_LDADD = \ +synergys_LDADD = \ $(DEPTH)/lib/server/libserver.a \ $(DEPTH)/lib/platform/libplatform.a \ $(DEPTH)/lib/synergy/libsynergy.a \ diff --git a/cmd/synergyd/resource.h b/cmd/synergys/resource.h similarity index 100% rename from cmd/synergyd/resource.h rename to cmd/synergys/resource.h diff --git a/cmd/synergyd/synergyd.cpp b/cmd/synergys/synergys.cpp similarity index 98% rename from cmd/synergyd/synergyd.cpp rename to cmd/synergys/synergys.cpp index 43b84a8e..0d0a479f 100644 --- a/cmd/synergyd/synergyd.cpp +++ b/cmd/synergys/synergys.cpp @@ -41,14 +41,16 @@ #if WINDOWS_LIKE #define DAEMON_NAME "Synergy Server" #elif UNIX_LIKE -#define DAEMON_NAME "synergyd" +#define DAEMON_NAME "synergys" #endif // configuration file name #if WINDOWS_LIKE -#define CONFIG_NAME "synergy.sgc" +#define USR_CONFIG_NAME "synergy.sgc" +#define SYS_CONFIG_NAME "synergy.sgc" #elif UNIX_LIKE -#define CONFIG_NAME "synergy.conf" +#define USR_CONFIG_NAME ".synergy.conf" +#define SYS_CONFIG_NAME "synergy.conf" #endif // @@ -344,10 +346,10 @@ PLATFORM_EXTRA kDefaultPort, platform.addPathComponent( platform.getUserDirectory(), - CONFIG_NAME).c_str(), + USR_CONFIG_NAME).c_str(), platform.addPathComponent( platform.getSystemDirectory(), - CONFIG_NAME).c_str())); + SYS_CONFIG_NAME).c_str())); } static @@ -586,7 +588,7 @@ loadConfig() CString path = platform.getUserDirectory(); if (!path.empty()) { // complete path - path = platform.addPathComponent(path, CONFIG_NAME); + path = platform.addPathComponent(path, USR_CONFIG_NAME); // now try loading the user's configuration loaded = loadConfig(path.c_str(), false); @@ -595,7 +597,7 @@ loadConfig() // try the system-wide config file path = platform.getSystemDirectory(); if (!path.empty()) { - path = platform.addPathComponent(path, CONFIG_NAME); + path = platform.addPathComponent(path, SYS_CONFIG_NAME); loadConfig(path.c_str(), false); } } diff --git a/cmd/synergyd/synergyd.dsp b/cmd/synergys/synergys.dsp similarity index 85% rename from cmd/synergyd/synergyd.dsp rename to cmd/synergys/synergys.dsp index 31195ecc..c9853f5d 100644 --- a/cmd/synergyd/synergyd.dsp +++ b/cmd/synergys/synergys.dsp @@ -1,24 +1,24 @@ -# Microsoft Developer Studio Project File - Name="synergyd" - Package Owner=<4> +# Microsoft Developer Studio Project File - Name="synergys" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 -CFG=synergyd - Win32 Debug +CFG=synergys - Win32 Debug !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 "synergyd.mak". +!MESSAGE NMAKE /f "synergys.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 "synergyd.mak" CFG="synergyd - Win32 Debug" +!MESSAGE NMAKE /f "synergys.mak" CFG="synergys - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE -!MESSAGE "synergyd - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "synergyd - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE "synergys - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "synergys - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project @@ -29,7 +29,7 @@ CPP=cl.exe MTL=midl.exe RSC=rc.exe -!IF "$(CFG)" == "synergyd - Win32 Release" +!IF "$(CFG)" == "synergys - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 @@ -56,7 +56,7 @@ LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -!ELSEIF "$(CFG)" == "synergyd - Win32 Debug" +!ELSEIF "$(CFG)" == "synergys - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 @@ -87,18 +87,18 @@ LINK32=link.exe # Begin Target -# Name "synergyd - Win32 Release" -# Name "synergyd - Win32 Debug" +# Name "synergys - Win32 Release" +# Name "synergys - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=.\synergyd.cpp +SOURCE=.\synergys.cpp # End Source File # Begin Source File -SOURCE=.\synergyd.rc +SOURCE=.\synergys.rc # End Source File # End Group # Begin Group "Header Files" @@ -114,7 +114,7 @@ SOURCE=.\resource.h # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File -SOURCE=.\synergy.ico +SOURCE=.\synergys.ico # End Source File # End Group # End Target diff --git a/cmd/synergyd/synergy.ico b/cmd/synergys/synergys.ico similarity index 100% rename from cmd/synergyd/synergy.ico rename to cmd/synergys/synergys.ico diff --git a/cmd/synergyd/synergyd.rc b/cmd/synergys/synergys.rc similarity index 100% rename from cmd/synergyd/synergyd.rc rename to cmd/synergys/synergys.rc diff --git a/configure.in b/configure.in index babd1f72..213705f3 100644 --- a/configure.in +++ b/configure.in @@ -91,8 +91,8 @@ lib/client/Makefile lib/server/Makefile cmd/Makefile cmd/launcher/Makefile -cmd/synergy/Makefile -cmd/synergyd/Makefile +cmd/synergyc/Makefile +cmd/synergys/Makefile dist/Makefile dist/rpm/Makefile dist/rpm/synergy.spec diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in index 124f0e55..a8a7d5fd 100644 --- a/dist/rpm/synergy.spec.in +++ b/dist/rpm/synergy.spec.in @@ -25,16 +25,16 @@ make %install make install DESTDIR=$RPM_BUILD_ROOT -strip $RPM_BUILD_ROOT/usr/bin/synergy -strip $RPM_BUILD_ROOT/usr/bin/synergyd +strip $RPM_BUILD_ROOT/usr/bin/synergyc +strip $RPM_BUILD_ROOT/usr/bin/synergys %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-, root, root) -/usr/bin/synergy -/usr/bin/synergyd +/usr/bin/synergyc +/usr/bin/synergys %doc AUTHORS %doc BUGS %doc COPYING diff --git a/synergy.dsw b/synergy.dsw index b5b1ab12..baae1dce 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -12,10 +12,10 @@ Package=<5> Package=<4> {{{ Begin Project Dependency - Project_Dep_Name synergy + Project_Dep_Name synergyc End Project Dependency Begin Project Dependency - Project_Dep_Name synergyd + Project_Dep_Name synergys End Project Dependency Begin Project Dependency Project_Dep_Name launcher @@ -180,7 +180,7 @@ Package=<4> ############################################################################### -Project: "synergy"=.\cmd\synergy\synergy.dsp - Package Owner=<4> +Project: "synergyc"=.\cmd\synergyc\synergyc.dsp - Package Owner=<4> Package=<5> {{{ @@ -213,7 +213,7 @@ Package=<4> ############################################################################### -Project: "synergyd"=.\cmd\synergyd\synergyd.dsp - Package Owner=<4> +Project: "synergys"=.\cmd\synergys\synergys.dsp - Package Owner=<4> Package=<5> {{{ From 2f5120acbaec9bd94bd3d9a59e8ceeb4a193f02f Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Aug 2002 22:59:59 +0000 Subject: [PATCH 324/807] fixed references to renamed icon files. --- cmd/synergyc/synergyc.rc | 2 +- cmd/synergys/synergys.rc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index 3c6fc383..097279bb 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -88,7 +88,7 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_SYNERGY ICON DISCARDABLE "synergy.ico" +IDI_SYNERGY ICON DISCARDABLE "synergyc.ico" ///////////////////////////////////////////////////////////////////////////// // diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index a289e737..b24adecc 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -89,7 +89,7 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_SYNERGY ICON DISCARDABLE "synergy.ico" +IDI_SYNERGY ICON DISCARDABLE "synergys.ico" ///////////////////////////////////////////////////////////////////////////// // From 8e232346e1a1ea61d86d0fd2b4fcb110a0c05885 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Aug 2002 23:00:16 +0000 Subject: [PATCH 325/807] Updated win32 installation instructions. --- README | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README b/README index 87245fbd..fb190422 100644 --- a/README +++ b/README @@ -77,10 +77,12 @@ family, starting with NT 4 with service pack 3, is the best choice for the primary screen but Linux is good too. (This is based on the known bugs; see BUGS for more details.) -Next you must install the software. You must install the synergy -server on the primary screen and the synergy client on all the -secondary screens. If it's easier just install both on all of the -systems. +Second, you install the software. Choose the appropriate package +and install it. On Windows you should unzip the files into the +`Program Files' directory; all the files will be put into a new +directory named `Synergy' under `Program Files'. You must install +the software on all the computers that will share the mouse and +keyboard. Third, you configure and start the server. Windows From f49b1867224824eb89b4069172108788fdccb2ce Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Aug 2002 23:00:28 +0000 Subject: [PATCH 326/807] Updated ChangeLog. --- ChangeLog | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/ChangeLog b/ChangeLog index 092c13e9..98f03d07 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,103 @@ +2002/08/11 22:43:07 crs +BUGS +INSTALL +NEWS +PORTING +README +cmd/Makefile.am +cmd/launcher/launcher.cpp +cmd/synergy/Makefile.am +cmd/synergy/resource.h +cmd/synergy/synergy.cpp +cmd/synergy/synergy.dsp +cmd/synergy/synergy.ico +cmd/synergy/synergy.rc +cmd/synergyc/Makefile.am +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.dsp +cmd/synergyc/synergyc.ico +cmd/synergyc/synergyc.rc +cmd/synergyd/Makefile.am +cmd/synergyd/resource.h +cmd/synergyd/synergy.ico +cmd/synergyd/synergyd.cpp +cmd/synergyd/synergyd.dsp +cmd/synergyd/synergyd.rc +cmd/synergys/Makefile.am +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.dsp +cmd/synergys/synergys.ico +cmd/synergys/synergys.rc +configure.in +dist/rpm/synergy.spec.in +synergy.dsw + +Moved synergy client to cmd/synergyc and renamed it synergyc. +Moved synergy server to cmd/synergys and renamed it synergys. +Updated documentation to reflect that and the win32 launcher. + +---------- +2002/08/11 11:50:49 crs +Makefile.am +TODO + +added TODO file and top-level rule to make zip file of distribution +files. + +---------- +2002/08/03 11:50:07 crs +lib/mt/CCondVar.h + +removed pre-instantiation of templates in header file. + +---------- +2002/08/03 11:49:36 crs +all.dsp +cmd/Makefile.am +cmd/launcher/Makefile.am +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/launcher/synergy.ico +cmd/synergy/Makefile.am +cmd/synergy/resource.h +cmd/synergy/synergy.cpp +cmd/synergy/synergy.dsp +cmd/synergy/synergy.ico +cmd/synergy/synergy.rc +cmd/synergyd/Makefile.am +cmd/synergyd/resource.h +cmd/synergyd/synergy.ico +cmd/synergyd/synergyd.cpp +cmd/synergyd/synergyd.dsp +cmd/synergyd/synergyd.rc +configure.in +lib/base/CString.cpp +lib/base/Version.h +lib/base/base.dsp +lib/client/client.dsp +lib/http/http.dsp +lib/io/io.dsp +lib/mt/mt.dsp +lib/net/net.dsp +lib/platform/CMSWindowsScreen.cpp +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/server.dsp +lib/synergy/libsynergy.dsp +synergy.dsw + +added win32 launcher program. also changed VC++ dsp and dsw +files to binary form so \r\n aren't converted. added icons +to client and server apps on win32. + +---------- 2002/08/02 17:57:54 crs Makefile.am configure.in From 305b01edf928fc204c148d3d9d9cd72909908750 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 18 Aug 2002 17:31:48 +0000 Subject: [PATCH 327/807] no longer sending fake events for unmapped logical buttons. --- lib/client/CXWindowsSecondaryScreen.cpp | 51 +++++++++++++++++++------ lib/client/CXWindowsSecondaryScreen.h | 4 ++ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index 364a234a..80429c49 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -114,17 +114,23 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) void CXWindowsSecondaryScreen::mouseDown(ButtonID button) { - CDisplayLock display(m_screen); - XTestFakeButtonEvent(display, mapButton(button), True, CurrentTime); - XSync(display, False); + const unsigned int xButton = mapButton(button); + if (xButton != 0) { + CDisplayLock display(m_screen); + XTestFakeButtonEvent(display, xButton, True, CurrentTime); + XSync(display, False); + } } void CXWindowsSecondaryScreen::mouseUp(ButtonID button) { - CDisplayLock display(m_screen); - XTestFakeButtonEvent(display, mapButton(button), False, CurrentTime); - XSync(display, False); + const unsigned int xButton = mapButton(button); + if (xButton != 0) { + CDisplayLock display(m_screen); + XTestFakeButtonEvent(display, xButton, False, CurrentTime); + XSync(display, False); + } } void @@ -137,7 +143,10 @@ void CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) { // choose button depending on rotation direction - const unsigned int button = (delta >= 0) ? 4 : 5; + const unsigned int xButton = mapButton((delta >= 0) ? 4 : 5); + if (xButton == 0) { + return; + } // now use absolute value of delta if (delta < 0) { @@ -147,8 +156,8 @@ CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) // send as many clicks as necessary CDisplayLock display(m_screen); for (; delta >= 120; delta -= 120) { - XTestFakeButtonEvent(display, button, True, CurrentTime); - XTestFakeButtonEvent(display, button, False, CurrentTime); + XTestFakeButtonEvent(display, xButton, True, CurrentTime); + XTestFakeButtonEvent(display, xButton, False, CurrentTime); } XSync(display, False); } @@ -359,8 +368,17 @@ CXWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) unsigned int CXWindowsSecondaryScreen::mapButton(ButtonID id) const { - // FIXME -- should use button mapping? - return static_cast(id); + if (id < 1 || id > sizeof(m_buttons) / sizeof(m_buttons[0])) { + // out of range + return 0; + } + else if (m_buttons[id - 1] == 0) { + // button not mapped + return 0; + } + else { + return static_cast(id); + } } KeyModifierMask @@ -830,6 +848,17 @@ CXWindowsSecondaryScreen::updateKeys() { CDisplayLock display(m_screen); + // get pointer mapping + static const UInt32 maxButtons = sizeof(m_buttons) / sizeof(m_buttons[0]); + unsigned char tmpButtons[sizeof(m_buttons) / sizeof(m_buttons[0])]; + UInt32 numButtons = XGetPointerMapping(display, tmpButtons, maxButtons); + for (UInt32 i = 0; i < maxButtons; ++i) { + m_buttons[i] = 0; + } + for (UInt32 i = 0; i < numButtons; ++i) { + m_buttons[tmpButtons[i] - 1] = i + 1; + } + // ask server which keys are pressed char keys[32]; XQueryKeymap(display, keys); diff --git a/lib/client/CXWindowsSecondaryScreen.h b/lib/client/CXWindowsSecondaryScreen.h index 556de6b4..2d20be82 100644 --- a/lib/client/CXWindowsSecondaryScreen.h +++ b/lib/client/CXWindowsSecondaryScreen.h @@ -113,6 +113,10 @@ private: // set entries indicate keys that are pressed. indexed by keycode. bool m_keys[256]; + // logical to physical button mapping. m_buttons[i] gives the + // physical button for logical button i+1. + unsigned char m_buttons[5]; + // current active modifiers (X key masks) unsigned int m_mask; From a0c2cd10dd7fc85f3a432836c432697661a10a5e Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 18 Aug 2002 17:32:27 +0000 Subject: [PATCH 328/807] removed bogus #error. --- lib/platform/CSynergyHook.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index f9e5cbf1..2b9d1321 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -395,8 +395,6 @@ getKeyboardLLProc(void*) #else // (_WIN32_WINNT < 0x0400) -#error foo - static DWORD WINAPI getKeyboardLLProc(void*) From a4db7f0005932b0d49ca511a2e06331500be5449 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 18 Aug 2002 17:35:10 +0000 Subject: [PATCH 329/807] fixed PrintScrn handling; it was being changed to keypad multiply. --- lib/client/CMSWindowsSecondaryScreen.cpp | 31 +++++++++++++++++++++--- lib/client/CMSWindowsSecondaryScreen.h | 4 +-- lib/server/CMSWindowsPrimaryScreen.cpp | 9 +++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index 44267977..a7301199 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -596,6 +596,31 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, } } + // special handling of VK_SNAPSHOT + if ((virtualKey & 0xff) == VK_SNAPSHOT) { + // ignore key repeats on print screen + if (action != kRepeat) { + // get event flags + DWORD flags = 0; + if (isExtendedKey(virtualKey)) { + flags |= KEYEVENTF_EXTENDEDKEY; + } + if (action != kPress) { + flags |= KEYEVENTF_KEYUP; + } + + // active window or fullscreen? + BYTE scan = 0; + if ((mask & KeyModifierAlt) == 0) { + scan = 1; + } + + // send event + keybd_event(static_cast(virtualKey & 0xff), scan, flags, 0); + } + return m_mask; + } + // get output mask. default output mask carries over the current // toggle modifier states and includes desired shift, control, alt, // and meta states. @@ -992,7 +1017,7 @@ CMSWindowsSecondaryScreen::toggleKey(UINT virtualKey, KeyModifierMask mask) } UINT -CMSWindowsSecondaryScreen::virtualKeyToScanCode(UINT& virtualKey) +CMSWindowsSecondaryScreen::virtualKeyToScanCode(UINT& virtualKey) const { // try mapping given virtual key UINT code = MapVirtualKey(virtualKey & 0xff, 0); @@ -1044,7 +1069,7 @@ CMSWindowsSecondaryScreen::virtualKeyToScanCode(UINT& virtualKey) } bool -CMSWindowsSecondaryScreen::isExtendedKey(UINT virtualKey) +CMSWindowsSecondaryScreen::isExtendedKey(UINT virtualKey) const { // see if we've already encoded the extended flag if ((virtualKey & 0x100) != 0) { @@ -1079,5 +1104,5 @@ CMSWindowsSecondaryScreen::sendKeyEvent(UINT virtualKey, bool press) const UINT code = virtualKeyToScanCode(virtualKey); keybd_event(static_cast(virtualKey & 0xff), static_cast(code), flags, 0); - log((CLOG_DEBUG2 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : ""))); + log((CLOG_DEBUG1 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : ""))); } diff --git a/lib/client/CMSWindowsSecondaryScreen.h b/lib/client/CMSWindowsSecondaryScreen.h index a2b556c2..577413d7 100644 --- a/lib/client/CMSWindowsSecondaryScreen.h +++ b/lib/client/CMSWindowsSecondaryScreen.h @@ -97,8 +97,8 @@ private: void releaseKeys(); void toggleKey(UINT virtualKey, KeyModifierMask mask); - UINT virtualKeyToScanCode(UINT& virtualKey); - bool isExtendedKey(UINT virtualKey); + UINT virtualKeyToScanCode(UINT& virtualKey) const; + bool isExtendedKey(UINT virtualKey) const; void sendKeyEvent(UINT virtualKey, bool press); private: diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index c66162ee..5fe8b1b0 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -929,6 +929,15 @@ CMSWindowsPrimaryScreen::mapKey( vkCode2 = vkCode; } + // if the original vkCode is VK_SNAPSHOT then use it. oddly enough, + // some keyboards use the same scan code for keypad multiply and + // print screen. the difference is that the latter has the extended + // key flag set. but MapVirtualKey() doesn't seem capable of using + // that flag. + else if (vkCode == VK_SNAPSHOT) { + vkCode2 = vkCode; + } + // if MapVirtualKey failed then use original virtual key else if (vkCode2 == 0) { vkCode2 = vkCode; From 7b3999b1661fc814f392cec41137e2a6c7b94816 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 18 Aug 2002 17:40:10 +0000 Subject: [PATCH 330/807] fixed win32 deadlock. when a client disconnects the server will warp the mouse to the primary screen. entering the primary screen causes the primary screen's window to be hidden. the deadlock occurs because hiding the window seems to post a message then wait for it to be handled (or possibly it won't send a message while a posted message is being handled). thread A locks the mutex, warps the mouse, the hides the window. thread B begins processing the mouse warp then tries to lock the mutex. thread A is waiting on the event loop owned by B while B is waiting on the mutex owned by A. this fix simply hides the window asynchronously. however, there may be other ways to cause a similar deadlock that have not been found. --- lib/server/CMSWindowsPrimaryScreen.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index 5fe8b1b0..b9622694 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -563,7 +563,19 @@ CMSWindowsPrimaryScreen::hideWindow() AttachThreadInput(myThread, m_lastActiveThread, FALSE); } } - ShowWindow(m_window, SW_HIDE); + + // hide the window. do not wait for it, though, since ShowWindow() + // waits for the event loop to process the show-window event, but + // that thread may need to lock the mutex that this thread has + // already locked. in particular, that deadlock will occur unless + // we use the asynchronous version of show window when a client + // disconnects: thread A will lock the mutex and enter the primary + // screen which warps the mouse and calls this method while thread B + // will handle the mouse warp event and call methods that try to + // lock the mutex. thread A owns the mutex and is waiting for the + // event loop, thread B owns the event loop and is waiting for the + // mutex causing deadlock. + ShowWindowAsync(m_window, SW_HIDE); } void From 79ce1dab4f121a41780bee82e2e8511587911fbe Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 18 Aug 2002 17:45:59 +0000 Subject: [PATCH 331/807] Changed version number to 0.9.9. --- configure.in | 2 +- lib/base/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 213705f3..3fe32f27 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=0 MINOR_VERSION=9 -RELEASE_VERSION=8 +RELEASE_VERSION=9 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/base/Version.h b/lib/base/Version.h index daebab8d..219b05c0 100644 --- a/lib/base/Version.h +++ b/lib/base/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "0.9.8" +# define VERSION "0.9.9" #endif // important strings From 604599c17855b6e9303fd25cfb8e6e8867b29423 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 18 Aug 2002 17:46:24 +0000 Subject: [PATCH 332/807] Updated ChangeLog. --- ChangeLog | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/ChangeLog b/ChangeLog index 98f03d07..f1163775 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,42 @@ +2002/08/18 17:45:59 crs +configure.in +lib/base/Version.h + +Changed version number to 0.9.9. + +---------- +2002/08/18 17:40:10 crs +lib/server/CMSWindowsPrimaryScreen.cpp + +fixed win32 deadlock. when a client disconnects the server will +warp the mouse to the primary screen. entering the primary +screen causes the primary screen's window to be hidden. the +deadlock occurs because hiding the window seems to post a +message then wait for it to be handled (or possibly it won't +send a message while a posted message is being handled). +thread A locks the mutex, warps the mouse, the hides the window. +thread B begins processing the mouse warp then tries to lock +the mutex. thread A is waiting on the event loop owned by B +while B is waiting on the mutex owned by A. this fix simply +hides the window asynchronously. however, there may be other +ways to cause a similar deadlock that have not been found. + +---------- +2002/08/18 17:35:10 crs +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/server/CMSWindowsPrimaryScreen.cpp + +fixed PrintScrn handling; it was being changed to keypad multiply. + +---------- +2002/08/18 17:31:48 crs +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h + +no longer sending fake events for unmapped logical buttons. + +---------- 2002/08/11 22:43:07 crs BUGS INSTALL From b0b494b8fa9d16395758a2459a07e7753b3b4929 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Sep 2002 09:28:54 +0000 Subject: [PATCH 333/807] lowered severity of some debug messages. --- lib/platform/CXWindowsUtil.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 2a915e29..376febe3 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -265,12 +265,12 @@ CXWindowsUtil::CErrorLock::internalHandler(Display* display, XErrorEvent* event) void CXWindowsUtil::CErrorLock::ignoreHandler(Display*, XErrorEvent* e, void*) { - log((CLOG_DEBUG "ignoring X error: %d", e->error_code)); + log((CLOG_DEBUG1 "ignoring X error: %d", e->error_code)); } void CXWindowsUtil::CErrorLock::saveHandler(Display*, XErrorEvent* e, void* flag) { - log((CLOG_DEBUG "flagging X error: %d", e->error_code)); + log((CLOG_DEBUG1 "flagging X error: %d", e->error_code)); *reinterpret_cast(flag) = true; } From 210ed8682729c8f96a29bee70eebacaff68975a0 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Sep 2002 10:31:10 +0000 Subject: [PATCH 334/807] added more tests to autoconf. also now handling missing sstream header in gcc 2.95 by including sstream header backported from v3. --- acinclude.m4 | 65 ++++++++ configure.in | 17 +- lib/base/stdsstream.h | 353 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+), 7 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 121a3380..685209d4 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -10,6 +10,71 @@ dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. +AC_DEFUN([ACX_CHECK_CXX_BOOL], [ + AC_MSG_CHECKING([for bool support]) + AC_TRY_COMPILE(, [bool t = true, f = false;],[acx_cxx_bool_ok=yes]) + AC_MSG_RESULT($acx_cxx_bool_ok) + if test x"$acx_cxx_bool_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_BOOL,1,[Define if your compiler has bool support.]),[$1]) + : + else + acx_cxx_bool_ok=no + $2 + fi +])dnl ACX_CHECK_BOOL + +AC_DEFUN([ACX_CHECK_CXX_EXCEPTIONS], [ + AC_MSG_CHECKING([for exception support]) + AC_TRY_COMPILE(, [try{throw int(4);}catch(int){throw;}catch(...){}],[acx_cxx_exception_ok=yes]) + AC_MSG_RESULT($acx_cxx_exception_ok) + if test x"$acx_cxx_exception_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_EXCEPTIONS,1,[Define if your compiler has exceptions support.]),[$1]) + : + else + acx_cxx_exception_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_EXCEPTIONS + +AC_DEFUN([ACX_CHECK_CXX_CASTS], [ + AC_MSG_CHECKING([for C++ cast support]) + AC_TRY_COMPILE(, [const char* f="a";const_cast(f);reinterpret_cast(f);static_cast(4.5);],[acx_cxx_cast_ok=yes]) + AC_MSG_RESULT($acx_cxx_cast_ok) + if test x"$acx_cxx_cast_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_CASTS,1,[Define if your compiler has C++ cast support.]),[$1]) + : + else + acx_cxx_cast_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_CASTS + +AC_DEFUN([ACX_CHECK_CXX_MUTABLE], [ + AC_MSG_CHECKING([for mutable support]) + AC_TRY_COMPILE(, [struct A{mutable int b;void f() const {b=0;}};A a;a.f();],[acx_cxx_mutable_ok=yes]) + AC_MSG_RESULT($acx_cxx_mutable_ok) + if test x"$acx_cxx_mutable_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_MUTABLE,1,[Define if your compiler has mutable support.]),[$1]) + : + else + acx_cxx_mutable_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_MUTABLE + +AC_DEFUN([ACX_CHECK_CXX_STDLIB], [ + AC_MSG_CHECKING([for C++ standard library]) + AC_TRY_LINK([#include ], [std::set a; a.insert(3);],[acx_cxx_stdlib_ok=yes]) + AC_MSG_RESULT($acx_cxx_stdlib_ok) + if test x"$acx_cxx_stdlib_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_CXX_STDLIB,1,[Define if your compiler has standard C++ library support.]),[$1]) + : + else + acx_cxx_stdlib_ok=no + $2 + fi +])dnl ACX_CHECK_CXX_STDLIB + dnl The following macros are from http://www.gnu.org/software/ac-archive/ dnl which distributes them under the following license: dnl diff --git a/configure.in b/configure.in index 3fe32f27..f32f01d0 100644 --- a/configure.in +++ b/configure.in @@ -34,10 +34,13 @@ AC_PROG_RANLIB dnl checks for libraries ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) +dnl do checks using C++ +AC_LANG(C++) + dnl checks for header files AC_HEADER_STDC AC_CHECK_HEADERS([unistd.h sys/time.h]) -AC_CHECK_HEADERS([istream ostream]) +AC_CHECK_HEADERS([istream ostream sstream]) AC_CHECK_HEADERS([windows.h]) AC_HEADER_TIME AC_PATH_X @@ -45,7 +48,7 @@ AC_PATH_XTRA AC_CHECK_HEADERS([X11/extensions/XTest.h]) dnl checks for types -dnl AC_TYPE_SIZE_T +AC_TYPE_SIZE_T dnl checks for structures AC_STRUCT_TM @@ -55,11 +58,11 @@ AC_CHECK_SIZEOF(char, 1) AC_CHECK_SIZEOF(short, 2) AC_CHECK_SIZEOF(int, 2) AC_CHECK_SIZEOF(long, 4) -dnl require bool support -dnl require template support -dnl require exception support -dnl require C++ casting support -dnl require mutable support +ACX_CHECK_CXX_BOOL(,AC_MSG_ERROR(Your compiler must support bool to compile synergy)) +ACX_CHECK_CXX_EXCEPTIONS(,AC_MSG_ERROR(Your compiler must support exceptions to compile synergy)) +ACX_CHECK_CXX_CASTS(,AC_MSG_ERROR(Your compiler must support C++ casts to compile synergy)) +ACX_CHECK_CXX_MUTABLE(,AC_MSG_ERROR(Your compiler must support mutable to compile synergy)) +ACX_CHECK_CXX_STDLIB(,AC_MSG_ERROR(Your compiler must support the C++ standard library to compile synergy)) dnl checks for library functions dnl AC_TYPE_SIGNAL diff --git a/lib/base/stdsstream.h b/lib/base/stdsstream.h index 1bd2b970..ed8e723f 100644 --- a/lib/base/stdsstream.h +++ b/lib/base/stdsstream.h @@ -13,6 +13,359 @@ */ #include "stdpre.h" + +#if defined(HAVE_SSTREAM) || !defined(__GNUC__) || (__GNUC__ >= 3) + #include + +#elif defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 95) +// g++ 2.95 didn't ship with sstream. the following is a backport +// by Magnus Fromreide of the sstream in g++ 3.0. + +/* This is part of libio/iostream, providing -*- C++ -*- input/output. +Copyright (C) 2000 Free Software Foundation + +This file is part of the GNU IO Library. This library is free +software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this library; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +As a special exception, if you link this library with files +compiled with a GNU compiler to produce an executable, this does not cause +the resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why +the executable file might be covered by the GNU General Public License. */ + +/* Written by Magnus Fromreide (magfr@lysator.liu.se). */ +/* seekoff and ideas for overflow is largely borrowed from libstdc++-v3 */ + +#include +#include +#include + +namespace std +{ + class stringbuf : public streambuf + { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + stringbuf(int which=ios::in|ios::out) + : streambuf(), mode(static_cast(which)), + stream(NULL), stream_len(0) + { + stringbuf_init(); + } + + explicit + stringbuf(const string &str, int which=ios::in|ios::out) + : streambuf(), mode(static_cast(which)), + stream(NULL), stream_len(0) + { + if (mode & (ios::in|ios::out)) + { + stream_len = str.size(); + stream = new char_type[stream_len]; + str.copy(stream, stream_len); + } + stringbuf_init(); + } + + virtual + ~stringbuf() + { + delete[] stream; + } + + string + str() const + { + if (pbase() != 0) + return string(stream, pptr()-pbase()); + else + return string(); + } + + void + str(const string& str) + { + delete[] stream; + stream_len = str.size(); + stream = new char_type[stream_len]; + str.copy(stream, stream_len); + stringbuf_init(); + } + + protected: + // The buffer is already in gptr, so if it ends then it is out of data. + virtual int + underflow() + { + return EOF; + } + + virtual int + overflow(int c = EOF) + { + int res; + if (mode & ios::out) + { + if (c != EOF) + { + streamsize old_stream_len = stream_len; + stream_len += 1; + char_type* new_stream = new char_type[stream_len]; + memcpy(new_stream, stream, old_stream_len); + delete[] stream; + stream = new_stream; + stringbuf_sync(gptr()-eback(), pptr()-pbase()); + sputc(c); + res = c; + } + else + res = EOF; + } + else + res = 0; + return res; + } + + virtual streambuf* + setbuf(char_type* s, streamsize n) + { + if (n != 0) + { + delete[] stream; + stream = new char_type[n]; + memcpy(stream, s, n); + stream_len = n; + stringbuf_sync(0, 0); + } + return this; + } + + virtual pos_type + seekoff(off_type off, ios::seek_dir way, int which = ios::in | ios::out) + { + pos_type ret = pos_type(off_type(-1)); + bool testin = which & ios::in && mode & ios::in; + bool testout = which & ios::out && mode & ios::out; + bool testboth = testin && testout && way != ios::cur; + + if (stream_len && ((testin != testout) || testboth)) + { + char_type* beg = stream; + char_type* curi = NULL; + char_type* curo = NULL; + char_type* endi = NULL; + char_type* endo = NULL; + + if (testin) + { + curi = gptr(); + endi = egptr(); + } + if (testout) + { + curo = pptr(); + endo = epptr(); + } + + off_type newoffi = 0; + off_type newoffo = 0; + if (way == ios::beg) + { + newoffi = beg - curi; + newoffo = beg - curo; + } + else if (way == ios::end) + { + newoffi = endi - curi; + newoffo = endo - curo; + } + + if (testin && newoffi + off + curi - beg >= 0 && + endi - beg >= newoffi + off + curi - beg) + { + gbump(newoffi + off); + ret = pos_type(newoffi + off + curi); + } + if (testout && newoffo + off + curo - beg >= 0 && + endo - beg >= newoffo + off + curo - beg) + { + pbump(newoffo + off); + ret = pos_type(newoffo + off + curo); + } + } + return ret; + } + + virtual pos_type + seekpos(pos_type sp, int which = ios::in | ios::out) + { + pos_type ret = seekoff(sp, ios::beg, which); + return ret; + } + + private: + void + stringbuf_sync(streamsize i, streamsize o) + { + if (mode & ios::in) + setg(stream, stream + i, stream + stream_len); + if (mode & ios::out) + { + setp(stream, stream + stream_len); + pbump(o); + } + } + void + stringbuf_init() + { + if (mode & ios::ate) + stringbuf_sync(0, stream_len); + else + stringbuf_sync(0, 0); + } + + private: + ios::open_mode mode; + char_type* stream; + streamsize stream_len; + }; + + class istringstream : public istream { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + istringstream(int which=ios::in) + : istream(&sb), sb(which | ios::in) + { } + + explicit + istringstream(const string& str, int which=ios::in) + : istream(&sb), sb(str, which | ios::in) + { } + + stringbuf* + rdbuf() const + { + return const_cast(&sb); + } + + string + str() const + { + return rdbuf()->str(); + } + void + str(const string& s) + { + rdbuf()->str(s); + } + private: + stringbuf sb; + }; + + class ostringstream : public ostream { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + ostringstream(int which=ios::out) + : ostream(&sb), sb(which | ios::out) + { } + + explicit + ostringstream(const string& str, int which=ios::out) + : ostream(&sb), sb(str, which | ios::out) + { } + + stringbuf* + rdbuf() const + { + return const_cast(&sb); + } + + string + str() const + { + return rdbuf()->str(); + } + + void str(const string& s) + { + rdbuf()->str(s); + } + private: + stringbuf sb; + }; + + class stringstream : public iostream { + public: + typedef char char_type; + typedef int int_type; + typedef streampos pos_type; + typedef streamoff off_type; + + explicit + stringstream(int which=ios::out|ios::in) + : iostream(&sb), sb(which) + { } + + explicit + stringstream(const string& str, int which=ios::out|ios::in) + : iostream(&sb), sb(str, which) + { } + + stringbuf* + rdbuf() const + { + return const_cast(&sb); + } + + string + str() const + { + return rdbuf()->str(); + } + + void + str(const string& s) + { + rdbuf()->str(s); + } + private: + stringbuf sb; + }; +}; + +#else /* not g++ 2.95 and no */ + +#error "Standard C++ library is missing required sstream header." + +#endif /* not g++ 2.95 and no */ + #include "stdpost.h" #include "stdistream.h" From 3295705f97dc46864c6e8adfb51006d1e38f4824 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Sep 2002 15:30:00 +0000 Subject: [PATCH 335/807] checkpoint. trying to add support for mode switch key to X11 screens. --- lib/client/CXWindowsSecondaryScreen.cpp | 117 ++++++++++++++++-------- lib/client/CXWindowsSecondaryScreen.h | 6 +- lib/server/CXWindowsPrimaryScreen.cpp | 50 +++++++--- lib/server/CXWindowsPrimaryScreen.h | 5 +- lib/synergy/KeyTypes.h | 1 + 5 files changed, 125 insertions(+), 54 deletions(-) diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index 80429c49..fe62e9f3 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -812,10 +812,13 @@ CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const outMask |= ControlMask; } if (inMask & KeyModifierAlt) { - outMask |= Mod1Mask; + outMask |= m_altMask; } if (inMask & KeyModifierMeta) { - outMask |= Mod4Mask; + outMask |= m_metaMask; + } + if (inMask & KeyModifierModeSwitch) { + outMask |= m_modeSwitchMask; } if (inMask & KeyModifierCapsLock) { outMask |= m_capsLockMask; @@ -876,8 +879,8 @@ CXWindowsSecondaryScreen::updateKeys() } // update mappings and current modifiers - updateKeycodeMap(display); updateModifierMap(display); + updateKeycodeMap(display); updateModifiers(display); } @@ -913,6 +916,9 @@ CXWindowsSecondaryScreen::updateModifiers(Display* display) void CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) { + // there are up to 4 keysyms per keycode + static const int numKeysyms = 4; + // get the number of keycodes int minKeycode, maxKeycode; XDisplayKeycodes(display, &minKeycode, &maxKeycode); @@ -924,39 +930,31 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) minKeycode, numKeycodes, &keysymsPerKeycode); - // restrict keysyms per keycode to 2 because, frankly, i have no - // idea how/what modifiers are used to access keysyms beyond the - // first 2. - int numKeysyms = 2; // keysymsPerKeycode - // initialize KeyCodeMask entry; m_keycodeMap.clear(); // insert keys for (int i = 0; i < numKeycodes; ++i) { - // how many keysyms for this keycode? - int n; - for (n = 0; n < numKeysyms; ++n) { - if (keysyms[i * keysymsPerKeycode + n] == NoSymbol) { - break; + // compute mask over all mapped keysyms. if a keycode has, say, + // no shifted keysym then we can ignore the shift state when + // synthesizing an event to generate it. + entry.m_keyMaskMask = 0; + for (int j = 0; j < numKeysyms; ++j) { + const KeySym keySym = keysyms[i * keysymsPerKeycode + j]; + if (keySym != NoSymbol) { + entry.m_keyMaskMask |= indexToModifierMask(j); } } - // move to next keycode if there are no keysyms - if (n == 0) { - continue; - } - - // set the mask of modifiers that this keycode uses - entry.m_keyMaskMask = (n == 1) ? 0 : (ShiftMask | LockMask); - // add entries for this keycode entry.m_keycode = static_cast(minKeycode + i); for (int j = 0; j < numKeysyms; ++j) { - entry.m_keyMask = (j == 0) ? 0 : ShiftMask; - m_keycodeMap.insert(std::make_pair(keysyms[i * - keysymsPerKeycode + j], entry)); + const KeySym keySym = keysyms[i * keysymsPerKeycode + j]; + if (keySym != NoSymbol) { + entry.m_keyMask = indexToModifierMask(j) & ~LockMask; + m_keycodeMap.insert(std::make_pair(keySym, entry)); + } } } @@ -964,6 +962,26 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) XFree(keysyms); } +unsigned int +CXWindowsSecondaryScreen::indexToModifierMask(int index) const +{ + assert(index >= 0 && index <= 3); + + switch (index) { + case 0: + return 0; + + case 1: + return ShiftMask | LockMask; + + case 2: + return m_modeSwitchMask; + + case 3: + return ShiftMask | LockMask | m_modeSwitchMask; + } +} + void CXWindowsSecondaryScreen::updateModifierMap(Display* display) { @@ -973,6 +991,9 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display) // initialize m_modifierMask = 0; m_toggleModifierMask = 0; + m_altMask = 0; + m_metaMask = 0; + m_modeSwitchMask = 0; m_numLockMask = 0; m_capsLockMask = 0; m_scrollLockMask = 0; @@ -989,29 +1010,49 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display) // save in modifier to keycode m_modifierToKeycode[i * m_keysPerModifier + j] = keycode; + // no further interest in unmapped modifier + if (keycode == 0) { + continue; + } + // save in keycode to modifier m_keycodeToModifier.insert(std::make_pair(keycode, i)); - // modifier is enabled if keycode isn't 0 - if (keycode != 0) { - m_modifierMask |= bit; - } + // save bit in all-modifiers mask + m_modifierMask |= bit; // modifier is a toggle if the keysym is a toggle modifier const KeySym keysym = XKeycodeToKeysym(display, keycode, 0); if (isToggleKeysym(keysym)) { m_toggleModifierMask |= bit; + } - // note num/caps-lock - if (keysym == XK_Num_Lock) { - m_numLockMask |= bit; - } - else if (keysym == XK_Caps_Lock) { - m_capsLockMask |= bit; - } - else if (keysym == XK_Scroll_Lock) { - m_scrollLockMask |= bit; - } + // note mask for particular modifiers + switch (keysym) { + case XK_Alt_L: + case XK_Alt_R: + m_altMask |= bit; + break; + + case XK_Meta_L: + case XK_Meta_R: + m_metaMask |= bit; + break; + + case XK_Mode_switch: + m_modeSwitchMask |= bit; + break; + + case XK_Num_Lock: + m_numLockMask |= bit; + break; + + case XK_Caps_Lock: + m_capsLockMask |= bit; + break; + + case XK_Scroll_Lock: + m_scrollLockMask |= bit; } } } diff --git a/lib/client/CXWindowsSecondaryScreen.h b/lib/client/CXWindowsSecondaryScreen.h index 2d20be82..5dc08c0b 100644 --- a/lib/client/CXWindowsSecondaryScreen.h +++ b/lib/client/CXWindowsSecondaryScreen.h @@ -98,6 +98,7 @@ private: void updateKeycodeMap(Display* display); void updateModifiers(Display* display); void updateModifierMap(Display* display); + unsigned int indexToModifierMask(int index) const; void toggleKey(Display*, KeySym, unsigned int mask); static bool isToggleKeysym(KeySym); @@ -130,7 +131,10 @@ private: // set bits indicate modifiers that toggle (e.g. caps-lock) unsigned int m_toggleModifierMask; - // masks that indicate which modifier bits are for toggle keys + // modifier masks + unsigned int m_altMask; + unsigned int m_metaMask; + unsigned int m_modeSwitchMask; unsigned int m_numLockMask; unsigned int m_capsLockMask; unsigned int m_scrollLockMask; diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp index b673430d..1a23eb79 100644 --- a/lib/server/CXWindowsPrimaryScreen.cpp +++ b/lib/server/CXWindowsPrimaryScreen.cpp @@ -614,7 +614,6 @@ CXWindowsPrimaryScreen::doSelectEvents(Display* display, Window w) const KeyModifierMask CXWindowsPrimaryScreen::mapModifier(unsigned int state) const { - // FIXME -- should be configurable KeyModifierMask mask = 0; if (state & ShiftMask) mask |= KeyModifierShift; @@ -622,13 +621,15 @@ CXWindowsPrimaryScreen::mapModifier(unsigned int state) const mask |= KeyModifierCapsLock; if (state & ControlMask) mask |= KeyModifierControl; - if (state & Mod1Mask) + if (state & m_altMask) mask |= KeyModifierAlt; - if (state & Mod2Mask) - mask |= KeyModifierNumLock; - if (state & Mod4Mask) + if (state & m_metaMask) mask |= KeyModifierMeta; - if (state & Mod5Mask) + if (state & m_modeSwitchMask) + mask |= KeyModifierModeSwitch; + if (state & m_numLockMask) + mask |= KeyModifierNumLock; + if (state & m_scrollLockMask) mask |= KeyModifierScrollLock; return mask; } @@ -688,6 +689,9 @@ CXWindowsPrimaryScreen::updateKeys() XModifierKeymap* keymap = XGetModifierMapping(display); // initialize + m_altMask = 0; + m_metaMask = 0; + m_modeSwitchMask = 0; m_numLockMask = 0; m_capsLockMask = 0; m_scrollLockMask = 0; @@ -699,16 +703,34 @@ CXWindowsPrimaryScreen::updateKeys() KeyCode keycode = keymap->modifiermap[i * keymap->max_keypermod + j]; - // note toggle modifier bits + // note mask for particular modifiers const KeySym keysym = XKeycodeToKeysym(display, keycode, 0); - if (keysym == XK_Num_Lock) { - m_numLockMask |= bit; - } - else if (keysym == XK_Caps_Lock) { - m_capsLockMask |= bit; - } - else if (keysym == XK_Scroll_Lock) { + switch (keysym) { + case XK_Alt_L: + case XK_Alt_R: + m_altMask |= bit; + break; + + case XK_Meta_L: + case XK_Meta_R: + m_metaMask |= bit; + break; + + case XK_Mode_switch: + m_modeSwitchMask |= bit; + break; + + case XK_Num_Lock: + m_numLockMask |= bit; + break; + + case XK_Caps_Lock: + m_capsLockMask |= bit; + break; + + case XK_Scroll_Lock: m_scrollLockMask |= bit; + break; } } } diff --git a/lib/server/CXWindowsPrimaryScreen.h b/lib/server/CXWindowsPrimaryScreen.h index 3a870c3e..8486ab5e 100644 --- a/lib/server/CXWindowsPrimaryScreen.h +++ b/lib/server/CXWindowsPrimaryScreen.h @@ -97,7 +97,10 @@ private: bool m_numLockHalfDuplex; bool m_capsLockHalfDuplex; - // masks that indicate which modifier bits are for toggle keys + // modifier masks + unsigned int m_altMask; + unsigned int m_metaMask; + unsigned int m_modeSwitchMask; unsigned int m_numLockMask; unsigned int m_capsLockMask; unsigned int m_scrollLockMask; diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index d66396eb..954b5305 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -37,6 +37,7 @@ static const KeyModifierMask KeyModifierShift = 0x0001; static const KeyModifierMask KeyModifierControl = 0x0002; static const KeyModifierMask KeyModifierAlt = 0x0004; static const KeyModifierMask KeyModifierMeta = 0x0008; +static const KeyModifierMask KeyModifierModeSwitch = 0x0010; static const KeyModifierMask KeyModifierCapsLock = 0x1000; static const KeyModifierMask KeyModifierNumLock = 0x2000; static const KeyModifierMask KeyModifierScrollLock = 0x4000; From 7dadc86caf69159d2848dce8c60595dacefd74c9 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Sep 2002 16:43:47 +0000 Subject: [PATCH 336/807] fixed language setting to be compatible with older autoconf's. --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index f32f01d0..803baa55 100644 --- a/configure.in +++ b/configure.in @@ -35,7 +35,7 @@ dnl checks for libraries ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) dnl do checks using C++ -AC_LANG(C++) +AC_LANG_CPLUSPLUS dnl checks for header files AC_HEADER_STDC From cc8272e258eaf584bfcb802d83650ed0f7fde640 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Sep 2002 16:45:44 +0000 Subject: [PATCH 337/807] work around to get mode switch working. --- lib/server/CXWindowsPrimaryScreen.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp index 1a23eb79..d9cce9bd 100644 --- a/lib/server/CXWindowsPrimaryScreen.cpp +++ b/lib/server/CXWindowsPrimaryScreen.cpp @@ -696,6 +696,11 @@ CXWindowsPrimaryScreen::updateKeys() m_capsLockMask = 0; m_scrollLockMask = 0; + // work around for my system, which reports this state bit when + // mode switch is down, instead of the appropriate modifier bit. + // should have no effect on other systems. -crs 9/02. + m_modeSwitchMask |= (1 << 13); + // set keycodes and masks for (unsigned int i = 0; i < 8; ++i) { const unsigned int bit = (1 << i); From 1038e290a32289a40908e28f51b194d80d1970d6 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Sep 2002 17:14:21 +0000 Subject: [PATCH 338/807] added notes to PORTING. --- PORTING | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PORTING b/PORTING index 2689c73f..55fc63c5 100644 --- a/PORTING +++ b/PORTING @@ -263,6 +263,7 @@ following these guidelines. * All files should include the copyright and license notice * Use tabs to indent * Tabs are 4 columns + * Lines should not extend past the 80th column * Open braces ({) go on same line as introducing statement `for (i = 0; i < 10; ++i) {' not for (i = 0; i < 10; ++i) @@ -281,6 +282,9 @@ following these guidelines. * Use Qt style comments for extraction by doxygen * Mark incomplete or buggy code with `FIXME' +- Other + * calls to log() should always be all on one line (even past column 80) + Class Relationships ------------------- From c95e991aeb848dbf2fe843416d6ad398be5a5d1a Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Sep 2002 17:15:43 +0000 Subject: [PATCH 339/807] Updated news with release of version 0.9.9. --- NEWS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/NEWS b/NEWS index fd79e553..aff2ffc1 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,14 @@ Synergy News ============ +* Aug-18-2002 - Synergy 0.9.9 released + + Fixed three bugs: + * The PrintScrn button was not working correctly on windows + * The Win32 server could hang when a client disconnected + * Using the mouse wheel could hang the X server + + * Aug-11-2002 - Synergy 0.9.8 released Supports any number of clients under Linux or Windows 95 or NT4 From a729e33cfb20134ad278ce9a33f609da10ff314a Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Sep 2002 17:30:04 +0000 Subject: [PATCH 340/807] Fixed win32 config saving, keyboard mapping, and AltGr bugs. Made extensive changes to the launcher to provide more control over setting up auto-start and it now saves configuration to the user's documents directory if auto-starting at login and saves to the system directory if auto-starting at boot. Replaced MapVirtualKey() with table lookup to work around that function's lack of support for extended keyboard scan codes. Added first cut at support for AltGr. --- BUGS | 10 +- INSTALL | 48 +- cmd/launcher/CAutoStart.cpp | 262 ++++++++++ cmd/launcher/CAutoStart.h | 81 +++ cmd/launcher/LaunchUtil.cpp | 211 ++++++++ cmd/launcher/LaunchUtil.h | 50 ++ cmd/launcher/launcher.cpp | 366 ++++--------- cmd/launcher/launcher.dsp | 16 + cmd/launcher/launcher.rc | 95 +++- cmd/launcher/resource.h | 147 +++--- cmd/synergyc/resource.h | 38 +- cmd/synergyc/synergyc.cpp | 171 +----- cmd/synergyc/synergyc.rc | 11 - cmd/synergys/resource.h | 38 +- cmd/synergys/synergys.cpp | 122 +---- lib/client/CClient.cpp | 4 +- lib/client/CMSWindowsSecondaryScreen.cpp | 6 + lib/platform/CUnixPlatform.cpp | 17 +- lib/platform/CUnixPlatform.h | 9 +- lib/platform/CWin32Platform.cpp | 135 ++++- lib/platform/CWin32Platform.h | 10 +- lib/platform/IPlatform.h | 28 +- lib/server/CMSWindowsPrimaryScreen.cpp | 632 +++++++++++------------ 23 files changed, 1455 insertions(+), 1052 deletions(-) create mode 100755 cmd/launcher/CAutoStart.cpp create mode 100755 cmd/launcher/CAutoStart.h create mode 100755 cmd/launcher/LaunchUtil.cpp create mode 100755 cmd/launcher/LaunchUtil.h diff --git a/BUGS b/BUGS index e429fda7..854f27e8 100644 --- a/BUGS +++ b/BUGS @@ -1,7 +1,8 @@ Known Bugs in Synergy ===================== -Report bugs to: synergy@groundhog.pair.com +Report bugs at: +http://sourceforge.net/tracker/?func=browse&group_id=59275&atid=490467 When reporting bugs, please include the version of the operating system you're using and what locale you use. @@ -77,10 +78,9 @@ system you're using and what locale you use. when toggled on and KeyRelease when toggled off (instead of KeyPress and KeyRelease for each physical press and release). -* Not handling mode-switch and shift-lock (X11) +* Not handling shift-lock (X11) - Synergy neither handles the mode-switch key nor shift-lock behavior - (as opposed to caps-lock). + Synergy doesn't handle shift-lock behavior (as opposed to caps-lock). * Large Motif clipboard items are truncated (X11) @@ -94,4 +94,4 @@ system you're using and what locale you use. * Automake isn't fully configured (Linux) The automake configuration isn't complete so synergy won't build - properly on some (many) systems. + or fail to build properly on some (many) systems. diff --git a/INSTALL b/INSTALL index d5a49793..40b1355a 100644 --- a/INSTALL +++ b/INSTALL @@ -96,9 +96,9 @@ First configure the server. Click the `Server' radio button * Click `Test' The server will start and you'll see a console window with log messages -telling you about synergy's progress. If an error occurs you'll get a -dialog box telling you synergy is about to quit; read the log messages -to determine the problem then correct it and try `Test' again. +telling you about synergy's progress. If an error occurs you'll get one +or more dialog boxes telling you what the errors are; read the errors +to determine the problem then correct them and try `Test' again. Now that the server is running, you'll need to start a client. On any client computer, double click `synergy'. Of course, you'll need to @@ -115,9 +115,9 @@ client computer. Then configure the client: * Click `Test' If all goes well, the client connects to the server successfully and -the mouse and keyboard are shared. If an error occurs you'll get a -dialog box telling you synergy is about to quit; read the log messages -to determine the problem then correct it and try `Test' again. When +the mouse and keyboard are shared. If an error occurs you'll get one +or more dialog boxes telling you what the errors are; read the errors +to determine the problem then correct them and try `Test' again. When everything is working correctly, install the software on the other client computers (if any) and repeat the steps for configuring the client on each. @@ -289,17 +289,31 @@ Starting Automatically on Windows When all the clients work you're ready to have synergy start automatically each time the system (re)starts. Click `Stop' on all -the clients then on the server'. Then Click `Start' on the server. -If it starts successfully then click `OK' to close the message box -and the dialog. The synergy server is now running and will be -automatically started each time the system is started. Click `Start' -then `OK' on each of the clients to start the synergy client and -automatically have it start each time the computer starts. +the clients then on the server'. Now click the `Configure' button by +the text `Automatic Startup'. The `Auto Start' dialog will pop up. +If an error occurs then correct the problem and click `Configure' +again. -If you ever want to prevent synergy from starting automatically when -the computer does, double click `synergy', choose `Client' or `Server' -(whichever the computer is running as), then click `No Auto-Start'. -Click `OK' to dismiss the message box then `Quit' to close the dialog. +On the `Auto Start' dialog you'll configure synergy to start +automatically when the computer starts or when you log in. You can +also configure synergy to not start automatically. You can only +start synergy automatically when the computer starts if you have +sufficient access rights. The dialog will let you know if you have +sufficient permission. + +If synergy is already configured to automatically start then there +will be two `Uninstall' buttons, at most one of which is enabled. +Click the enabled button, if any, to configure synergy to not start +automatically. + +If synergy is not configured to start automatically then there will +be two `Install' buttons. If you have sufficient permission to +have synergy start automatically when the computer does then the +`Install' button in the `When Computer Starts' box will be enabled. +Click it to have synergy start for all users when the computer starts. +In this case, synergy will be available during the login screen. +Otherwise, click the `Install' button in the `When You Log In' box +to have synergy automatically start when you log in. Starting Automatically on Linux @@ -353,8 +367,6 @@ Common Command Line Options -1, --no-restart do not restart on failure -h, --help print help and exit --version print version information and exit - --install install as a service (Windows only) - --uninstall uninstall service (Windows only) Debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, INFO, DEBUG, DEBUG1, and DEBUG2. Only messages at or above the given diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp new file mode 100755 index 00000000..f13345e7 --- /dev/null +++ b/cmd/launcher/CAutoStart.cpp @@ -0,0 +1,262 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CPlatform.h" +#include "CLog.h" +#include "CAutoStart.h" +#include "LaunchUtil.h" +#include "resource.h" + +#define CLIENT_DAEMON_NAME "Synergy Client" +#define SERVER_DAEMON_NAME "Synergy Server" +#define CLIENT_DAEMON_INFO "Shares this system's mouse and keyboard with others." +#define SERVER_DAEMON_INFO "Shares this system's mouse and keyboard with others." + +// +// CAutoStart +// + +CAutoStart* CAutoStart::s_singleton = NULL; + +CAutoStart::CAutoStart(HWND parent, CConfig* config, const CString& cmdLine) : + m_parent(parent), + m_config(config), + m_isServer(config != NULL), + m_cmdLine(cmdLine), + m_name((config != NULL) ? SERVER_DAEMON_NAME : CLIENT_DAEMON_NAME), + m_userConfigSaved(false) + +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CAutoStart::~CAutoStart() +{ + s_singleton = NULL; +} + +void +CAutoStart::doModal() +{ + // install our log outputter + CLog::Outputter oldOutputter = CLog::getOutputter(); + CLog::setOutputter(&CAutoStart::onLog); + + // reset saved flag + m_userConfigSaved = false; + + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_AUTOSTART), + m_parent, dlgProc, (LPARAM)this); + + // restore log outputter + CLog::setOutputter(oldOutputter); +} + +bool +CAutoStart::wasUserConfigSaved() const +{ + return m_userConfigSaved; +} + +void +CAutoStart::update() +{ + // need a platform object + CPlatform platform; + + // get installation state + const bool installedSystem = platform.isDaemonInstalled( + m_name.c_str(), true); + const bool installedUser = platform.isDaemonInstalled( + m_name.c_str(), false); + + // get user's permissions + const bool canInstallSystem = platform.canInstallDaemon( + m_name.c_str(), true); + const bool canInstallUser = platform.canInstallDaemon( + m_name.c_str(), false); + + // update messages + CString msg, label; + if (canInstallSystem) { + msg = getString(IDS_AUTOSTART_PERMISSION_SYSTEM); + } + else if (canInstallUser) { + msg = getString(IDS_AUTOSTART_PERMISSION_USER); + } + else { + msg = getString(IDS_AUTOSTART_PERMISSION_NONE); + } + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_PERMISSION_MSG), msg); + if (installedSystem) { + msg = getString(IDS_AUTOSTART_INSTALLED_SYSTEM); + label = getString(IDS_UNINSTALL_LABEL); + } + else if (installedUser) { + msg = getString(IDS_AUTOSTART_INSTALLED_USER); + label = getString(IDS_UNINSTALL_LABEL); + } + else { + msg = getString(IDS_AUTOSTART_INSTALLED_NONE); + label = getString(IDS_INSTALL_LABEL); + } + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALLED_MSG), msg); + + // update buttons + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM), label); + setWindowText(getItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER), label); + if (installedSystem) { + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, canInstallSystem); + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, false); + m_install = false; + } + else if (installedUser) { + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, false); + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, canInstallUser); + m_install = false; + } + else { + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_SYSTEM, canInstallSystem); + enableItem(m_hwnd, IDC_AUTOSTART_INSTALL_USER, canInstallUser); + m_install = true; + } +} + +bool +CAutoStart::onInstall(bool allUsers) +{ + if (!m_install) { + return onUninstall(allUsers); + } + + // try saving configuration. if we can't then don't try + // installing the daemon. + if (m_config != NULL) { + if (!saveConfig(*m_config, allUsers)) { + showError(m_hwnd, CStringUtil::format( + getString(IDS_SAVE_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + return false; + } + + // note if we've saved the user's configuration + if (!allUsers) { + m_userConfigSaved = true; + } + } + + // get the app path + CString appPath = getAppPath(m_isServer ? SERVER_APP : CLIENT_APP); + + // clear error message + m_errorMessage = ""; + + // install + CPlatform platform; + if (!platform.installDaemon(m_name.c_str(), + m_isServer ? SERVER_DAEMON_INFO : CLIENT_DAEMON_INFO, + appPath.c_str(), m_cmdLine.c_str(), allUsers)) { + if (m_errorMessage.empty()) { + m_errorMessage = getString(IDS_INSTALL_GENERIC_ERROR); + } + showError(m_hwnd, m_errorMessage); + return false; + } + + askOkay(m_hwnd, getString(IDS_INSTALL_TITLE), + getString(allUsers ? + IDS_INSTALLED_SYSTEM : + IDS_INSTALLED_USER)); + return true; +} + +bool +CAutoStart::onUninstall(bool allUsers) +{ + // clear error message + m_errorMessage = ""; + + // uninstall + CPlatform platform; + if (platform.uninstallDaemon(m_name.c_str(), allUsers) != + IPlatform::kSuccess) { + if (m_errorMessage.empty()) { + m_errorMessage = getString(IDS_UNINSTALL_GENERIC_ERROR); + } + showError(m_hwnd, m_errorMessage); + return false; + } + + askOkay(m_hwnd, getString(IDS_UNINSTALL_TITLE), + getString(allUsers ? + IDS_UNINSTALLED_SYSTEM : + IDS_UNINSTALLED_USER)); + return true; +} + +bool +CAutoStart::onLog(int priority, const char* message) +{ + if (priority <= CLog::kERROR) { + s_singleton->m_errorMessage = message; + } + return true; +} + +BOOL +CAutoStart::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + // save our hwnd + m_hwnd = hwnd; + + // update the controls + update(); + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_AUTOSTART_INSTALL_SYSTEM: + onInstall(true); + update(); + return TRUE; + + case IDC_AUTOSTART_INSTALL_USER: + onInstall(false); + update(); + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + m_hwnd = NULL; + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CAutoStart::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CAutoStart.h b/cmd/launcher/CAutoStart.h new file mode 100755 index 00000000..954801ab --- /dev/null +++ b/cmd/launcher/CAutoStart.h @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CAUTOSTART_H +#define CAUTOSTART_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Auto start dialog for Microsoft Windows launcher +class CAutoStart { +public: + // if config == NULL then it's assumed we're installing/uninstalling + // the client, otherwise the server. + CAutoStart(HWND parent, CConfig* config, const CString& cmdLine); + ~CAutoStart(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + //! Test if user configuration was saved + /*! + Returns true if the user's configuration (as opposed to the system-wide + configuration) was saved successfully while in doModal(). + */ + bool wasUserConfigSaved() const; + + //@} + +private: + void update(); + bool onInstall(bool allUsers); + bool onUninstall(bool allUsers); + + // log handling + static bool onLog(int priority, const char* message); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CAutoStart* s_singleton; + + HWND m_parent; + CConfig* m_config; + bool m_isServer; + CString m_cmdLine; + CString m_name; + HWND m_hwnd; + bool m_install; + CString m_errorMessage; + bool m_userConfigSaved; +}; + +#endif diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp new file mode 100755 index 00000000..57e31554 --- /dev/null +++ b/cmd/launcher/LaunchUtil.cpp @@ -0,0 +1,211 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "CPlatform.h" +#include "LaunchUtil.h" +#include "resource.h" +#include "stdfstream.h" + +#define CONFIG_NAME "synergy.sgc" + +CString +getString(DWORD id) +{ + char buffer[1024]; + buffer[0] = '\0'; + LoadString(s_instance, id, buffer, sizeof(buffer) / sizeof(buffer[0])); + return buffer; +} + +CString +getErrorString(DWORD error) +{ + char* buffer; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&buffer, + 0, + NULL) == 0) { + return getString(IDS_ERROR); + } + else { + CString result(buffer); + LocalFree(buffer); + return result; + } +} + +void +showError(HWND hwnd, const CString& msg) +{ + CString title = getString(IDS_ERROR); + MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); +} + +void +askOkay(HWND hwnd, const CString& title, const CString& msg) +{ + MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); +} + +bool +askVerify(HWND hwnd, const CString& msg) +{ + CString title = getString(IDS_VERIFY); + int result = MessageBox(hwnd, msg.c_str(), + title.c_str(), MB_OKCANCEL | MB_APPLMODAL); + return (result == IDOK); +} + +void +setWindowText(HWND hwnd, const CString& msg) +{ + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)msg.c_str()); +} + +CString +getWindowText(HWND hwnd) +{ + LRESULT size = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0); + char* buffer = new char[size + 1]; + SendMessage(hwnd, WM_GETTEXT, size + 1, (LPARAM)buffer); + buffer[size] = '\0'; + CString result(buffer); + delete[] buffer; + return result; +} + +HWND +getItem(HWND hwnd, int id) +{ + return GetDlgItem(hwnd, id); +} + +void +enableItem(HWND hwnd, int id, bool enabled) +{ + EnableWindow(GetDlgItem(hwnd, id), enabled); +} + +CString +getAppPath(const CString& appName) +{ + // prepare path to app + CPlatform platform; + char myPathname[MAX_PATH]; + GetModuleFileName(s_instance, myPathname, MAX_PATH); + const char* myBasename = platform.getBasename(myPathname); + CString appPath = CString(myPathname, myBasename - myPathname); + appPath += appName; + return appPath; +} + +static +bool +loadConfig(const CString& pathname, CConfig& config) +{ + try { + std::ifstream stream(pathname.c_str()); + if (stream) { + stream >> config; + return true; + } + } + catch (...) { + // ignore + } + return false; +} + +bool +loadConfig(CConfig& config) +{ + CPlatform platform; + + // load configuration + bool configLoaded = false; + CString path = platform.getUserDirectory(); + if (!path.empty()) { + CPlatform platform; + + // try loading the user's configuration + path = platform.addPathComponent(path, CONFIG_NAME); + if (loadConfig(path, config)) { + configLoaded = true; + } + else { + // try the system-wide config file + path = platform.getSystemDirectory(); + if (!path.empty()) { + path = platform.addPathComponent(path, CONFIG_NAME); + if (loadConfig(path, config)) { + configLoaded = true; + } + } + } + } + return configLoaded; +} + +static +bool +saveConfig(const CString& pathname, const CConfig& config) +{ + try { + std::ofstream stream(pathname.c_str()); + if (stream) { + stream << config; + return !!stream; + } + } + catch (...) { + // ignore + } + return false; +} + +bool +saveConfig(const CConfig& config, bool sysOnly) +{ + CPlatform platform; + + // try saving the user's configuration + if (!sysOnly) { + CString path = platform.getUserDirectory(); + if (!path.empty()) { + path = platform.addPathComponent(path, CONFIG_NAME); + if (saveConfig(path, config)) { + return true; + } + } + } + + // try the system-wide config file + else { + CString path = platform.getSystemDirectory(); + if (!path.empty()) { + path = platform.addPathComponent(path, CONFIG_NAME); + if (saveConfig(path, config)) { + return true; + } + } + } + + return false; +} diff --git a/cmd/launcher/LaunchUtil.h b/cmd/launcher/LaunchUtil.h new file mode 100755 index 00000000..414246e6 --- /dev/null +++ b/cmd/launcher/LaunchUtil.h @@ -0,0 +1,50 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LAUNCHUTIL_H +#define LAUNCHUTIL_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +#define CLIENT_APP "synergyc.exe" +#define SERVER_APP "synergys.exe" + +class CConfig; + +// client must define this and set it before calling any function here +extern HINSTANCE s_instance; + +CString getString(DWORD id); +CString getErrorString(DWORD error); + +void showError(HWND hwnd, const CString& msg); +void askOkay(HWND hwnd, const CString& title, + const CString& msg); +bool askVerify(HWND hwnd, const CString& msg); + +void setWindowText(HWND hwnd, const CString& msg); +CString getWindowText(HWND hwnd); + +HWND getItem(HWND hwnd, int id); +void enableItem(HWND hwnd, int id, bool enabled); + +CString getAppPath(const CString& appName); + +bool loadConfig(CConfig& config); +bool saveConfig(const CConfig& config, bool sysOnly); + +#endif diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index e18aa892..df1a9661 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -17,12 +17,12 @@ #include "CPlatform.h" #include "CNetwork.h" #include "Version.h" -#include "stdfstream.h" #include "stdvector.h" #include "resource.h" -#define WINDOWS_LEAN_AND_MEAN -#include +// these must come after the above because it includes windows.h +#include "LaunchUtil.h" +#include "CAutoStart.h" #define CONFIG_NAME "synergy.sgc" #define CLIENT_APP "synergyc.exe" @@ -45,9 +45,10 @@ public: HANDLE m_stop; }; +HINSTANCE s_instance = NULL; + static const TCHAR* s_mainClass = TEXT("GoSynergy"); static const TCHAR* s_layoutClass = TEXT("SynergyLayout"); -static HINSTANCE s_instance = NULL; static HWND s_mainWindow; static CConfig s_config; @@ -92,84 +93,6 @@ isNameInList(const CStringList& names, const CString& name) return false; } -static -CString -getString(DWORD id) -{ - char buffer[1024]; - buffer[0] = '\0'; - LoadString(s_instance, id, buffer, sizeof(buffer) / sizeof(buffer[0])); - return buffer; -} - -static -CString -getErrorString(DWORD error) -{ - char* buffer; - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM, - 0, - error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&buffer, - 0, - NULL) == 0) { - return getString(IDS_ERROR); - } - else { - CString result(buffer); - LocalFree(buffer); - return result; - } -} - -static -void -showError(HWND hwnd, const CString& msg) -{ - CString title = getString(IDS_ERROR); - MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); -} - -static -void -askOkay(HWND hwnd, const CString& title, const CString& msg) -{ - MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); -} - -static -bool -askVerify(HWND hwnd, const CString& msg) -{ - CString title = getString(IDS_VERIFY); - int result = MessageBox(hwnd, msg.c_str(), - title.c_str(), MB_OKCANCEL | MB_APPLMODAL); - return (result == IDOK); -} - -static -CString -getWindowText(HWND hwnd) -{ - LRESULT size = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0); - char* buffer = new char[size + 1]; - SendMessage(hwnd, WM_GETTEXT, size + 1, (LPARAM)buffer); - buffer[size] = '\0'; - CString result(buffer); - delete[] buffer; - return result; -} - -static -void -enableItem(HWND hwnd, int id, bool enabled) -{ - EnableWindow(GetDlgItem(hwnd, id), enabled); -} - static bool isClientChecked(HWND hwnd) @@ -178,6 +101,13 @@ isClientChecked(HWND hwnd) return (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED); } +static +void +enableSaveControls(HWND hwnd) +{ + enableItem(hwnd, IDC_MAIN_SAVE, s_config != s_oldConfig); +} + static void enableScreensControls(HWND hwnd) @@ -217,6 +147,7 @@ enableMainWindowControls(HWND hwnd) enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_LABEL, client); enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT, client); enableScreensControls(hwnd); + enableSaveControls(hwnd); } static @@ -310,6 +241,7 @@ addScreen(HWND hwnd) // update neighbors updateNeighbors(hwnd); enableScreensControls(hwnd); + enableSaveControls(hwnd); } } @@ -370,6 +302,7 @@ editScreen(HWND hwnd) // update neighbors updateNeighbors(hwnd); + enableSaveControls(hwnd); } } @@ -400,6 +333,7 @@ removeScreen(HWND hwnd) // update neighbors updateNeighbors(hwnd); enableScreensControls(hwnd); + enableSaveControls(hwnd); } static @@ -431,6 +365,8 @@ changeNeighbor(HWND hwnd, HWND combo, EDirection direction) s_config.connect(screen, direction, CString(neighbor)); delete[] neighbor; } + + enableSaveControls(hwnd); } static @@ -459,16 +395,16 @@ execApp(const char* app, const CString& cmdLine, PROCESS_INFORMATION* procInfo) startup.hStdError = NULL; // prepare path to app - CPlatform platform; - char myPathname[MAX_PATH]; - GetModuleFileName(s_instance, myPathname, MAX_PATH); - const char* myBasename = platform.getBasename(myPathname); - CString appPath = CString(myPathname, myBasename - myPathname); - appPath += app; + CString appPath = getAppPath(app); + + // put path to app in command line + CString commandLine = "\""; + commandLine += appPath; + commandLine += "\" "; + commandLine += cmdLine; // start child - if (CreateProcess(appPath.c_str(), - (char*)cmdLine.c_str(), + if (CreateProcess(NULL, (char*)commandLine.c_str(), NULL, NULL, FALSE, @@ -487,30 +423,11 @@ execApp(const char* app, const CString& cmdLine, PROCESS_INFORMATION* procInfo) } static -bool -uninstallApp(const char* app) -{ - PROCESS_INFORMATION procInfo; - - // uninstall - DWORD exitCode = kExitFailed; - if (execApp(app, "-z --uninstall", &procInfo)) { - WaitForSingleObject(procInfo.hProcess, INFINITE); - GetExitCodeProcess(procInfo.hProcess, &exitCode); - CloseHandle(procInfo.hProcess); - CloseHandle(procInfo.hThread); - } - - return (exitCode == kExitSuccess); -} - -static -HANDLE -launchApp(HWND hwnd, bool testing, DWORD* threadID) +CString +getCommandLine(HWND hwnd, bool testing) { // decide if client or server const bool isClient = isClientChecked(hwnd); - const char* app = isClient ? CLIENT_APP : SERVER_APP; // get and verify screen name HWND child = GetDlgItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); @@ -520,14 +437,14 @@ launchApp(HWND hwnd, bool testing, DWORD* threadID) getString(IDS_INVALID_SCREEN_NAME).c_str(), name.c_str())); SetFocus(child); - return NULL; + return CString(); } if (!isClient && !s_config.isScreen(name)) { showError(hwnd, CStringUtil::format( getString(IDS_UNKNOWN_SCREEN_NAME).c_str(), name.c_str())); SetFocus(child); - return NULL; + return CString(); } // get and verify port @@ -541,7 +458,7 @@ launchApp(HWND hwnd, bool testing, DWORD* threadID) portString.c_str(), defaultPortString.c_str())); SetFocus(child); - return NULL; + return CString(); } // prepare command line @@ -557,10 +474,10 @@ launchApp(HWND hwnd, bool testing, DWORD* threadID) CString server = getWindowText(child); if (!s_config.isValidScreenName(server)) { showError(hwnd, CStringUtil::format( - getString(IDS_INVALID_SCREEN_NAME).c_str(), + getString(IDS_INVALID_SERVER_NAME).c_str(), server.c_str())); SetFocus(child); - return NULL; + return CString(); } if (testing) { @@ -576,27 +493,21 @@ launchApp(HWND hwnd, bool testing, DWORD* threadID) cmdLine += portString; } - // uninstall client and server then reinstall one of them - if (!testing) { - // uninstall client and server - uninstallApp(CLIENT_APP); - uninstallApp(SERVER_APP); + return cmdLine; +} - // install client or server - PROCESS_INFORMATION procInfo; - DWORD exitCode = kExitFailed; - if (execApp(app, CString("-z --install") + cmdLine, &procInfo)) { - WaitForSingleObject(procInfo.hProcess, INFINITE); - GetExitCodeProcess(procInfo.hProcess, &exitCode); - CloseHandle(procInfo.hProcess); - CloseHandle(procInfo.hThread); - } +static +HANDLE +launchApp(HWND hwnd, bool testing, DWORD* threadID) +{ + // decide if client or server + const bool isClient = isClientChecked(hwnd); + const char* app = isClient ? CLIENT_APP : SERVER_APP; - // see if install succeeded - if (exitCode != kExitSuccess) { - showError(hwnd, getString(IDS_INSTALL_FAILED).c_str()); - return NULL; - } + // prepare command line + CString cmdLine = getCommandLine(hwnd, testing); + if (cmdLine.empty()) { + return NULL; } // start child @@ -716,67 +627,6 @@ waitForChild(HWND hwnd, HANDLE thread, DWORD threadID) CloseHandle(info.m_stop); } -static -bool -loadConfig(const CString& pathname, CConfig& config) -{ - try { - std::ifstream stream(pathname.c_str()); - if (stream) { - stream >> config; - return true; - } - } - catch (...) { - // ignore - } - return false; -} - -static -bool -saveConfig(const CString& pathname, const CConfig& config) -{ - try { - std::ofstream stream(pathname.c_str()); - if (stream) { - stream << config; - return !!stream; - } - } - catch (...) { - // ignore - } - return false; -} - -static -bool -saveConfig(const CConfig& config) -{ - CPlatform platform; - - CString path = platform.getUserDirectory(); - if (!path.empty()) { - // try loading the user's configuration - path = platform.addPathComponent(path, CONFIG_NAME); - if (saveConfig(path, config)) { - return true; - } - } - - // try the system-wide config file - path = platform.getSystemDirectory(); - if (!path.empty()) { - path = platform.addPathComponent(path, CONFIG_NAME); - if (saveConfig(path, config)) { - return true; - } - } - - return false; -} - static void initMainWindow(HWND hwnd) @@ -784,26 +634,9 @@ initMainWindow(HWND hwnd) CPlatform platform; // load configuration - bool configLoaded = false; - CString path = platform.getUserDirectory(); - if (!path.empty()) { - // try loading the user's configuration - path = platform.addPathComponent(path, CONFIG_NAME); - if (loadConfig(path, s_config)) { - configLoaded = true; - } - else { - // try the system-wide config file - path = platform.getSystemDirectory(); - if (!path.empty()) { - path = platform.addPathComponent(path, CONFIG_NAME); - if (loadConfig(path, s_config)) { - configLoaded = true; - } - } - } - } + bool configLoaded = loadConfig(s_config); s_oldConfig = s_config; + enableSaveControls(hwnd); // choose client/server radio buttons HWND child; @@ -978,82 +811,81 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) PostQuitMessage(0); return 0; - case IDOK: { - // save data - if (s_config != s_oldConfig) { - if (!saveConfig(s_config)) { - showError(hwnd, CStringUtil::format( - getString(IDS_SAVE_FAILED).c_str(), - getErrorString(GetLastError()).c_str())); - return 0; - } - s_oldConfig = s_config; - } - - // launch child app - HANDLE thread = launchApp(hwnd, false, NULL); - if (thread == NULL) { - return 0; - } - CloseHandle(thread); - - // notify of success - askOkay(hwnd, getString(IDS_STARTED_TITLE), - getString(IDS_STARTED)); - - // quit - PostQuitMessage(0); - return 0; - } - + case IDOK: case IDC_MAIN_TEST: { + // note if testing + const bool testing = (LOWORD(wParam) == IDC_MAIN_TEST); + // save data if (s_config != s_oldConfig) { - if (!saveConfig(s_config)) { + if (!saveConfig(s_config, false)) { showError(hwnd, CStringUtil::format( getString(IDS_SAVE_FAILED).c_str(), getErrorString(GetLastError()).c_str())); return 0; } s_oldConfig = s_config; + enableSaveControls(hwnd); } // launch child app DWORD threadID; - HANDLE thread = launchApp(hwnd, true, &threadID); + HANDLE thread = launchApp(hwnd, testing, &threadID); if (thread == NULL) { return 0; } - // wait for process to stop, allowing the user to kill it - waitForChild(hwnd, thread, threadID); + // handle child program + if (testing) { + // wait for process to stop, allowing the user to kill it + waitForChild(hwnd, thread, threadID); - // clean up - CloseHandle(thread); - return 0; - } - - case IDC_MAIN_UNINSTALL: { - // uninstall client and server - bool removedClient = uninstallApp(CLIENT_APP); - bool removedServer = uninstallApp(SERVER_APP); - if (!removedClient) { - showError(hwnd, CStringUtil::format( - getString(IDS_UNINSTALL_FAILED).c_str(), - getString(IDS_CLIENT).c_str())); - } - else if (!removedServer) { - showError(hwnd, CStringUtil::format( - getString(IDS_UNINSTALL_FAILED).c_str(), - getString(IDS_SERVER).c_str())); + // clean up + CloseHandle(thread); } else { - askOkay(hwnd, getString(IDS_UNINSTALL_TITLE), - getString(IDS_UNINSTALLED)); + // don't need thread handle + CloseHandle(thread); + + // notify of success + askOkay(hwnd, getString(IDS_STARTED_TITLE), + getString(IDS_STARTED)); + + // quit + PostQuitMessage(0); } return 0; } + case IDC_MAIN_AUTOSTART: { + // construct command line + CString cmdLine = getCommandLine(hwnd, false); + if (!cmdLine.empty()) { + // run dialog + CAutoStart autoStart(hwnd, + isClientChecked(hwnd) ? NULL : &s_config, + cmdLine); + autoStart.doModal(); + if (autoStart.wasUserConfigSaved()) { + s_oldConfig = s_config; + enableSaveControls(hwnd); + } + } + return 0; + } + + case IDC_MAIN_SAVE: + if (!saveConfig(s_config, false)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SAVE_FAILED).c_str(), + getErrorString(GetLastError()).c_str())); + } + else { + s_oldConfig = s_config; + enableSaveControls(hwnd); + } + return 0; + case IDC_MAIN_CLIENT_RADIO: case IDC_MAIN_SERVER_RADIO: enableMainWindowControls(hwnd); diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp index 79301495..f93739aa 100644 --- a/cmd/launcher/launcher.dsp +++ b/cmd/launcher/launcher.dsp @@ -95,18 +95,34 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CAutoStart.cpp +# End Source File +# Begin Source File + SOURCE=.\launcher.cpp # End Source File # Begin Source File SOURCE=.\launcher.rc # End Source File +# Begin Source File + +SOURCE=.\LaunchUtil.cpp +# End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CAutoStart.h +# End Source File +# Begin Source File + +SOURCE=.\LaunchUtil.h +# End Source File +# Begin Source File + SOURCE=.\resource.h # End Source File # End Group diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index f70cc55b..60ff371c 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -52,7 +52,7 @@ END // Dialog // -IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 241 +IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 262 STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU CAPTION "Synergy" CLASS "GoSynergy" @@ -62,22 +62,22 @@ BEGIN IDC_STATIC,7,7,286,19 GROUPBOX "",IDC_STATIC,7,29,286,31 GROUPBOX "",IDC_STATIC,7,67,286,103 - GROUPBOX "Advanced Options",IDC_STATIC,7,177,286,34 - CONTROL "Client",IDC_MAIN_CLIENT_RADIO,"Button", + GROUPBOX "Advanced Options",IDC_STATIC,7,177,286,56 + CONTROL "&Client",IDC_MAIN_CLIENT_RADIO,"Button", BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,29,33,10 CONTROL "Server",IDC_MAIN_SERVER_RADIO,"Button", BS_AUTORADIOBUTTON,11,67,37,10 - LTEXT "Server Host Name:",IDC_MAIN_CLIENT_SERVER_NAME_LABEL,12, - 41,61,8 + LTEXT "Server &Host Name:",IDC_MAIN_CLIENT_SERVER_NAME_LABEL, + 12,41,61,8 EDITTEXT IDC_MAIN_CLIENT_SERVER_NAME_EDIT,79,39,106,12, ES_AUTOHSCROLL - LTEXT "Screens:",IDC_MAIN_SERVER_SCREENS_LABEL,12,79,29,8 + LTEXT "&Screens:",IDC_MAIN_SERVER_SCREENS_LABEL,12,79,29,8 LISTBOX IDC_MAIN_SERVER_SCREENS_LIST,12,91,106,36, LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Add",IDC_MAIN_SERVER_ADD_BUTTON,12,132,50,14 - PUSHBUTTON "Edit",IDC_MAIN_SERVER_EDIT_BUTTON,68,132,50,14 - PUSHBUTTON "Remove",IDC_MAIN_SERVER_REMOVE_BUTTON,12,150,50,14 - LTEXT "Layout:",IDC_MAIN_SERVER_LAYOUT_LABEL,138,79,24,8 + PUSHBUTTON "&Add",IDC_MAIN_SERVER_ADD_BUTTON,12,132,50,14 + PUSHBUTTON "&Edit",IDC_MAIN_SERVER_EDIT_BUTTON,68,132,50,14 + PUSHBUTTON "&Remove",IDC_MAIN_SERVER_REMOVE_BUTTON,12,150,50,14 + LTEXT "&Layout:",IDC_MAIN_SERVER_LAYOUT_LABEL,138,79,24,8 LTEXT "Left:",IDC_MAIN_SERVER_LEFT_LABEL,144,93,15,8 COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,118,46, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP @@ -90,15 +90,17 @@ BEGIN LTEXT "Below:",IDC_MAIN_SERVER_BOTTOM_LABEL,144,141,22,8 COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,118,46, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Screen Name:",IDC_STATIC,12,192,46,8 + LTEXT "Screen &Name:",IDC_STATIC,12,192,46,8 EDITTEXT IDC_MAIN_ADVANCED_NAME_EDIT,64,190,106,12,ES_AUTOHSCROLL - LTEXT "Port:",IDC_STATIC,194,192,16,8 + LTEXT "&Port:",IDC_STATIC,194,192,16,8 EDITTEXT IDC_MAIN_ADVANCED_PORT_EDIT,216,190,40,12,ES_AUTOHSCROLL | ES_NUMBER - DEFPUSHBUTTON "Test",IDC_MAIN_TEST,75,220,50,14 - PUSHBUTTON "Start",IDOK,131,220,50,14 - PUSHBUTTON "No Auto-Start",IDC_MAIN_UNINSTALL,187,220,50,14 - PUSHBUTTON "Quit",IDCANCEL,243,220,50,14 + LTEXT "Automatic Startup:",IDC_STATIC,12,212,59,8 + PUSHBUTTON "Con&figure...",IDC_MAIN_AUTOSTART,78,210,50,14 + PUSHBUTTON "Sa&ve",IDC_MAIN_SAVE,75,241,50,14 + DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,241,50,14 + PUSHBUTTON "Start",IDOK,187,241,50,14 + PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 END IDD_ADD DIALOG DISCARDABLE 0, 0, 172, 95 @@ -125,6 +127,26 @@ BEGIN IDC_STATIC,7,7,172,15 END +IDD_AUTOSTART DIALOG DISCARDABLE 0, 0, 195, 189 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Auto Start" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDCANCEL,138,168,50,14 + LTEXT "Synergy can be configured to start automatically when you log in. If you have sufficient access rights, you can instead configure synergy to start automatically when your computer starts.", + IDC_STATIC,7,7,181,33 + LTEXT "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself.", + IDC_AUTOSTART_PERMISSION_MSG,7,69,181,17 + LTEXT "Synergy is configured to start automatically when the system starts.", + IDC_AUTOSTART_INSTALLED_MSG,7,93,181,17 + GROUPBOX "When &You Log In",IDC_STATIC,7,119,84,40 + PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_USER,23,133,50,14 + GROUPBOX "When &Computer Starts",IDC_STATIC,104,119,84,40 + PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_SYSTEM,119,134,50,14 + LTEXT "Synergy can be configured to start automatically when the computer starts or when you log in but not both.", + IDC_STATIC,7,43,181,17 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -139,7 +161,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 293 TOPMARGIN, 7 - BOTTOMMARGIN, 234 + BOTTOMMARGIN, 255 END IDD_ADD, DIALOG @@ -157,6 +179,14 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 47 END + + IDD_AUTOSTART, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 188 + TOPMARGIN, 7 + BOTTOMMARGIN, 182 + END END #endif // APSTUDIO_INVOKED @@ -189,16 +219,37 @@ BEGIN IDS_STARTUP_FAILED "Failed to start synergy: %{1}" IDS_STARTED_TITLE "Started" IDS_STARTED "Synergy was successfully started. Use the task manager to terminate it." - IDS_INSTALL_FAILED "Failed to install synergy auto-starter. Synergy will not be started now and it will not automatically start each time you start or reboot your computer." IDS_UNINSTALL_TITLE "Removed Auto-Start" - IDS_UNINSTALLED "Removed auto-start. Synergy will not automatically start each time you start or reboot your computer." END STRINGTABLE DISCARDABLE BEGIN - IDS_UNINSTALL_FAILED "Failed to remove auto-start of %{1}. You might not have permission to remove it or it might be in use.\n\nOn Windows NT, 2000, or XP you should open the Services control panel and stop the synergy %{1} service then try again." - IDS_CLIENT "client" - IDS_SERVER "server" + IDS_AUTOSTART_PERMISSION_SYSTEM + "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself." + IDS_AUTOSTART_PERMISSION_USER + "You have sufficient access rights to install and uninstall Auto Start for just yourself." + IDS_AUTOSTART_PERMISSION_NONE + "You do not have sufficient access rights to install or uninstall Auto Start." + IDS_AUTOSTART_INSTALLED_SYSTEM + "Synergy is configured to start automatically when the system starts." + IDS_AUTOSTART_INSTALLED_USER + "Synergy is configured to start automatically when you log in." + IDS_AUTOSTART_INSTALLED_NONE + "Synergy is not configured to start automatically." + IDS_INSTALL_LABEL "Install" + IDS_UNINSTALL_LABEL "Uninstall" + IDS_INSTALL_GENERIC_ERROR "Install failed for an unknown reason." + IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed for an unknown reason." + IDS_INSTALL_TITLE "Installed Auto-Start" + IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." + IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_UNINSTALLED_SYSTEM "Removed auto-start. Synergy will not automatically start each time you start or reboot your computer." + IDS_UNINSTALLED_USER "Removed auto-start. Synergy will not automatically start each time you log in." + IDS_INVALID_SERVER_NAME "Server name `%{1}' is invalid." END #endif // English (U.S.) resources diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index 679b3e97..93c263cf 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -1,62 +1,85 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by launcher.rc -// -#define IDS_ERROR 1 -#define IDS_INVALID_SCREEN_NAME 2 -#define IDS_DUPLICATE_SCREEN_NAME 3 -#define IDS_SCREEN_NAME_IS_ALIAS 4 -#define IDS_VERIFY 5 -#define IDS_UNSAVED_DATA_REALLY_QUIT 6 -#define IDS_UNKNOWN_SCREEN_NAME 7 -#define IDS_INVALID_PORT 8 -#define IDS_SAVE_FAILED 9 -#define IDS_STARTUP_FAILED 10 -#define IDS_STARTED_TITLE 11 -#define IDS_STARTED 12 -#define IDS_INSTALL_FAILED 13 -#define IDS_UNINSTALL_TITLE 14 -#define IDS_UNINSTALLED 15 -#define IDS_UNINSTALL_FAILED 16 -#define IDS_CLIENT 17 -#define IDS_SERVER 18 -#define IDD_MAIN 101 -#define IDD_ADD 102 -#define IDD_WAIT 103 -#define IDI_SYNERGY 104 -#define IDC_MAIN_CLIENT_RADIO 1000 -#define IDC_MAIN_SERVER_RADIO 1001 -#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1002 -#define IDC_MAIN_ADVANCED_NAME_EDIT 1006 -#define IDC_MAIN_ADVANCED_PORT_EDIT 1008 -#define IDC_MAIN_TEST 1009 -#define IDC_MAIN_CLIENT_SERVER_NAME_LABEL 1011 -#define IDC_MAIN_SERVER_SCREENS_LIST 1012 -#define IDC_MAIN_SERVER_SCREENS_LABEL 1013 -#define IDC_MAIN_SERVER_LAYOUT_LABEL 1014 -#define IDC_MAIN_SERVER_ADD_BUTTON 1018 -#define IDC_MAIN_SERVER_EDIT_BUTTON 1019 -#define IDC_ADD_SCREEN_NAME_EDIT 1020 -#define IDC_MAIN_SERVER_REMOVE_BUTTON 1020 -#define IDC_ADD_ALIASES_EDIT 1021 -#define IDC_MAIN_SERVER_LEFT_COMBO 1022 -#define IDC_MAIN_SERVER_RIGHT_COMBO 1023 -#define IDC_MAIN_SERVER_TOP_COMBO 1024 -#define IDC_MAIN_SERVER_BOTTOM_COMBO 1025 -#define IDC_MAIN_SERVER_LEFT_LABEL 1026 -#define IDC_MAIN_SERVER_RIGHT_LABEL 1027 -#define IDC_MAIN_SERVER_TOP_LABEL 1028 -#define IDC_MAIN_SERVER_BOTTOM_LABEL 1029 -#define IDC_MAIN_UNINSTALL 1030 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 105 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1031 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by launcher.rc +// +#define IDS_ERROR 1 +#define IDS_INVALID_SCREEN_NAME 2 +#define IDS_DUPLICATE_SCREEN_NAME 3 +#define IDS_SCREEN_NAME_IS_ALIAS 4 +#define IDS_VERIFY 5 +#define IDS_UNSAVED_DATA_REALLY_QUIT 6 +#define IDS_UNKNOWN_SCREEN_NAME 7 +#define IDS_INVALID_PORT 8 +#define IDS_SAVE_FAILED 9 +#define IDS_STARTUP_FAILED 10 +#define IDS_STARTED_TITLE 11 +#define IDS_STARTED 12 +#define IDS_INSTALL_FAILED 13 +#define IDS_UNINSTALL_TITLE 14 +#define IDS_UNINSTALLED 15 +#define IDS_UNINSTALL_FAILED 16 +#define IDS_CLIENT 17 +#define IDS_SERVER 18 +#define IDS_AUTOSTART_PERMISSION_SYSTEM 19 +#define IDS_AUTOSTART_PERMISSION_USER 20 +#define IDS_AUTOSTART_PERMISSION_NONE 21 +#define IDS_AUTOSTART_INSTALLED_SYSTEM 22 +#define IDS_AUTOSTART_INSTALLED_USER 23 +#define IDS_AUTOSTART_INSTALLED_NONE 24 +#define IDS_INSTALL_LABEL 25 +#define IDS_UNINSTALL_LABEL 26 +#define IDS_INSTALL_GENERIC_ERROR 27 +#define IDS_UNINSTALL_GENERIC_ERROR 28 +#define IDS_INSTALL_TITLE 29 +#define IDS_INSTALLED_SYSTEM 30 +#define IDS_INSTALLED_USER 31 +#define IDS_UNINSTALLED_SYSTEM 32 +#define IDS_UNINSTALLED_USER 33 +#define IDS_INVALID_SERVER_NAME 34 +#define IDD_MAIN 101 +#define IDD_ADD 102 +#define IDD_WAIT 103 +#define IDI_SYNERGY 104 +#define IDD_AUTOSTART 105 +#define IDC_MAIN_CLIENT_RADIO 1000 +#define IDC_MAIN_SERVER_RADIO 1001 +#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1002 +#define IDC_MAIN_ADVANCED_NAME_EDIT 1006 +#define IDC_MAIN_ADVANCED_PORT_EDIT 1008 +#define IDC_MAIN_TEST 1009 +#define IDC_MAIN_SAVE 1010 +#define IDC_MAIN_CLIENT_SERVER_NAME_LABEL 1011 +#define IDC_MAIN_SERVER_SCREENS_LIST 1012 +#define IDC_MAIN_SERVER_SCREENS_LABEL 1013 +#define IDC_MAIN_SERVER_LAYOUT_LABEL 1014 +#define IDC_MAIN_SERVER_ADD_BUTTON 1018 +#define IDC_MAIN_SERVER_EDIT_BUTTON 1019 +#define IDC_ADD_SCREEN_NAME_EDIT 1020 +#define IDC_MAIN_SERVER_REMOVE_BUTTON 1020 +#define IDC_ADD_ALIASES_EDIT 1021 +#define IDC_MAIN_SERVER_LEFT_COMBO 1022 +#define IDC_MAIN_SERVER_RIGHT_COMBO 1023 +#define IDC_MAIN_SERVER_TOP_COMBO 1024 +#define IDC_MAIN_SERVER_BOTTOM_COMBO 1025 +#define IDC_MAIN_SERVER_LEFT_LABEL 1026 +#define IDC_MAIN_SERVER_RIGHT_LABEL 1027 +#define IDC_MAIN_SERVER_TOP_LABEL 1028 +#define IDC_MAIN_SERVER_BOTTOM_LABEL 1029 +#define IDC_MAIN_UNINSTALL 1030 +#define IDC_AUTOSTART_INSTALLED_MSG 1031 +#define IDC_AUTOSTART_PERMISSION_MSG 1032 +#define IDC_AUTOSTART_INSTALL_USER 1033 +#define IDC_AUTOSTART_INSTALL_SYSTEM 1034 +#define IDC_MAIN_AUTOSTART 1035 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1036 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h index ec3e44aa..9c299850 100644 --- a/cmd/synergyc/resource.h +++ b/cmd/synergyc/resource.h @@ -1,19 +1,19 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by synergy.rc -// -#define IDS_FAILED 1 -#define IDD_SYNERGY 101 -#define IDI_SYNERGY 103 -#define IDC_LOG 1000 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 104 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by synergyc.rc +// +#define IDS_FAILED 1 +#define IDD_SYNERGY 101 +#define IDI_SYNERGY 103 +#define IDC_LOG 1000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 94771f79..d2e45f31 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -54,8 +54,6 @@ static bool s_backend = false; static bool s_restartable = true; static bool s_daemon = true; static bool s_camp = true; -static bool s_install = false; -static bool s_uninstall = false; static const char* s_logFilter = NULL; static CString s_name; static CNetworkAddress s_serverAddress; @@ -246,25 +244,6 @@ static void help() { -#if WINDOWS_LIKE - -# define PLATFORM_ARGS \ -" [--install]" \ -" \n" \ -"or\n" \ -" --uninstall\n" -# define PLATFORM_DESC \ -" --install install server as a service.\n" \ -" --uninstall uninstall server service.\n" - -#else - -# define PLATFORM_ARGS \ -" \n" -# define PLATFORM_DESC - -#endif - log((CLOG_PRINT "Usage: %s" " [--camp|--no-camp]" @@ -272,7 +251,7 @@ help() " [--debug ]" " [--name ]" " [--restart|--no-restart]" -PLATFORM_ARGS +" \n" "\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" @@ -289,7 +268,6 @@ PLATFORM_ARGS " -1, --no-restart do not try to restart the client if it fails for\n" " some reason.\n" "* --restart restart the client automatically if it fails.\n" -PLATFORM_DESC " -h, --help display this help and exit.\n" " --version display version information and exit.\n" "\n" @@ -397,30 +375,6 @@ parse(int argc, const char** argv) bye(kExitSuccess); } -#if WINDOWS_LIKE - else if (isArg(i, argc, argv, NULL, "--install")) { - s_install = true; - if (s_uninstall) { - log((CLOG_PRINT "%s: `--install' and `--uninstall'" - " are mutually exclusive" BYE, - pname, argv[i], pname)); - bye(kExitArgs); - } - } -#endif - -#if WINDOWS_LIKE - else if (isArg(i, argc, argv, NULL, "--uninstall")) { - s_uninstall = true; - if (s_install) { - log((CLOG_PRINT "%s: `--install' and `--uninstall'" - " are mutually exclusive" BYE, - pname, argv[i], pname)); - bye(kExitArgs); - } - } -#endif - else if (isArg(i, argc, argv, "--", NULL)) { // remaining arguments are not options ++i; @@ -439,37 +393,26 @@ parse(int argc, const char** argv) } } - // exactly one non-option argument (server-address) unless using - // --uninstall. - if (s_uninstall) { - if (i != argc) { - log((CLOG_PRINT "%s: unrecognized option `%s' to `%s'" BYE, - pname, argv[i], pname, - s_install ? "--install" : "--uninstall")); - bye(kExitArgs); - } - } - else { - if (i == argc) { - log((CLOG_PRINT "%s: a server address or name is required" BYE, + // exactly one non-option argument (server-address) + if (i == argc) { + log((CLOG_PRINT "%s: a server address or name is required" BYE, pname, pname)); - bye(kExitArgs); - } - if (i + 1 != argc) { - log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + bye(kExitArgs); + } + if (i + 1 != argc) { + log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, pname, argv[i], pname)); - bye(kExitArgs); - } + bye(kExitArgs); + } - // save server address - try { - s_serverAddress = CNetworkAddress(argv[i], kDefaultPort); - } - catch (XSocketAddress& e) { - log((CLOG_PRINT "%s: %s" BYE, + // save server address + try { + s_serverAddress = CNetworkAddress(argv[i], kDefaultPort); + } + catch (XSocketAddress& e) { + log((CLOG_PRINT "%s: %s" BYE, pname, e.what(), pname)); - bye(kExitFailed); - } + bye(kExitFailed); } // increase default filter level for daemon. the user must @@ -496,7 +439,6 @@ parse(int argc, const char** argv) } } - // // platform dependent entry points // @@ -505,24 +447,16 @@ parse(int argc, const char** argv) #include "CMSWindowsScreen.h" -static bool s_errors = false; - static bool logMessageBox(int priority, const char* msg) { - if (priority <= CLog::kERROR) { - s_errors = true; - } - if (s_backend) { - return true; - } - if (priority <= CLog::kFATAL) { + if (priority <= (s_backend ? CLog::kERROR : CLog::kFATAL)) { MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); return true; } else { - return false; + return s_backend; } } @@ -551,13 +485,7 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) bye = &byeThrow; // parse command line - s_install = false; - s_uninstall = false; parse(argc, argv); - if (s_install || s_uninstall) { - // not allowed to install/uninstall from service - throw CWin32Platform::CDaemonFailed(kExitArgs); - } // run as a service return platform->runDaemon(realMain, daemonStop); @@ -605,58 +533,6 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // parse command line parse(__argc, const_cast(__argv)); - // install/uninstall - if (s_install) { - // get the full path to this program - TCHAR path[MAX_PATH]; - if (GetModuleFileName(NULL, path, - sizeof(path) / sizeof(path[0])) == 0) { - log((CLOG_CRIT "cannot determine absolute path to program")); - return kExitFailed; - } - - // construct the command line to start the service with - CString commandLine = "--daemon"; - if (s_restartable) { - commandLine += " --restart"; - } - else { - commandLine += " --no-restart"; - } - if (s_logFilter != NULL) { - commandLine += " --debug "; - commandLine += s_logFilter; - } - commandLine += " "; - commandLine += s_serverAddress.getHostname().c_str(); - - // install - if (!platform.installDaemon(DAEMON_NAME, - "Shares this system's mouse and keyboard with others.", - path, commandLine.c_str())) { - log((CLOG_CRIT "failed to install service")); - return kExitFailed; - } - log((CLOG_PRINT "installed successfully")); - return kExitSuccess; - } - else if (s_uninstall) { - switch (platform.uninstallDaemon(DAEMON_NAME)) { - case IPlatform::kSuccess: - log((CLOG_PRINT "uninstalled successfully")); - return kExitSuccess; - - case IPlatform::kFailed: - log((CLOG_CRIT "failed to uninstall service")); - return kExitFailed; - - case IPlatform::kAlready: - log((CLOG_CRIT "service isn't installed")); - // return success since service is uninstalled - return kExitSuccess; - } - } - // daemonize if requested int result; if (s_daemon) { @@ -684,15 +560,6 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) CNetwork::cleanup(); - // if running as a non-daemon backend and there was an error then - // wait for the user to click okay so he can see the error messages. - if (s_backend && !s_daemon && (result == kExitFailed || s_errors)) { - char msg[1024]; - msg[0] = '\0'; - LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); - MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); - } - return result; } diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index 097279bb..f1523300 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -89,17 +89,6 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_SYNERGY ICON DISCARDABLE "synergyc.ico" - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE DISCARDABLE -BEGIN - IDS_FAILED "Synergy is about to quit with an error. Please check the log for error messages then click OK." -END - #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index 5b67a903..6142fc38 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -1,19 +1,19 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by synergyd.rc -// -#define IDS_FAILED 1 -#define IDD_SYNERGY 101 -#define IDI_SYNERGY 102 -#define IDC_LOG 1000 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 103 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1002 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by synergyd.rc +// +#define IDS_FAILED 1 +#define IDD_SYNERGY 101 +#define IDI_SYNERGY 102 +#define IDC_LOG 1000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1002 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 0d0a479f..496cf5a7 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -61,10 +61,6 @@ static const char* pname = NULL; static bool s_backend = false; static bool s_restartable = true; static bool s_daemon = true; -#if WINDOWS_LIKE -static bool s_install = false; -static bool s_uninstall = false; -#endif static const char* s_configFile = NULL; static const char* s_logFilter = NULL; static CString s_name; @@ -275,13 +271,8 @@ help() #if WINDOWS_LIKE # define PLATFORM_ARGS \ -" {--daemon|--no-daemon}" \ -" [--install]\n" \ -"or\n" \ -" --uninstall\n" -# define PLATFORM_DESC \ -" --install install server as a daemon.\n" \ -" --uninstall uninstall server daemon.\n" +" {--daemon|--no-daemon}" +# define PLATFORM_DESC # define PLATFORM_EXTRA \ "At least one command line argument is required. If you don't otherwise\n" \ "need an argument use `--daemon'.\n" \ @@ -334,7 +325,7 @@ PLATFORM_EXTRA "default port, %d.\n" "\n" "If no configuration file pathname is provided then the first of the\n" -"following to load sets the configuration:\n" +"following to load successfully sets the configuration:\n" " %s\n" " %s\n" "If no configuration file can be loaded then the configuration uses its\n" @@ -465,30 +456,6 @@ parse(int argc, const char** argv) bye(kExitSuccess); } -#if WINDOWS_LIKE - else if (isArg(i, argc, argv, NULL, "--install")) { - s_install = true; - if (s_uninstall) { - log((CLOG_PRINT "%s: `--install' and `--uninstall'" - " are mutually exclusive" BYE, - pname, argv[i], pname)); - bye(kExitArgs); - } - } -#endif - -#if WINDOWS_LIKE - else if (isArg(i, argc, argv, NULL, "--uninstall")) { - s_uninstall = true; - if (s_install) { - log((CLOG_PRINT "%s: `--install' and `--uninstall'" - " are mutually exclusive" BYE, - pname, argv[i], pname)); - bye(kExitArgs); - } - } -#endif - else if (isArg(i, argc, argv, "--", NULL)) { // remaining arguments are not options ++i; @@ -612,24 +579,16 @@ loadConfig() #include "CMSWindowsScreen.h" -static bool s_errors = false; - static bool logMessageBox(int priority, const char* msg) { - if (priority <= CLog::kERROR) { - s_errors = true; - } - if (s_backend) { - return true; - } - if (priority <= CLog::kFATAL) { + if (priority <= (s_backend ? CLog::kERROR : CLog::kFATAL)) { MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); return true; } else { - return false; + return s_backend; } } @@ -658,13 +617,7 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) bye = &byeThrow; // parse command line - s_install = false; - s_uninstall = false; parse(argc, argv); - if (s_install || s_uninstall) { - // not allowed to install/uninstall from service - throw CWin32Platform::CDaemonFailed(kExitArgs); - } // load configuration loadConfig(); @@ -715,62 +668,6 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // parse command line parse(__argc, const_cast(__argv)); - // install/uninstall - if (s_install) { - // get the full path to this program - TCHAR path[MAX_PATH]; - if (GetModuleFileName(NULL, path, - sizeof(path) / sizeof(path[0])) == 0) { - log((CLOG_CRIT "cannot determine absolute path to program")); - return kExitFailed; - } - - // construct the command line to start the service with - CString commandLine; - commandLine += "--daemon"; - if (s_restartable) { - commandLine += " --restart"; - } - else { - commandLine += " --no-restart"; - } - if (s_logFilter != NULL) { - commandLine += " --debug "; - commandLine += s_logFilter; - } - if (s_configFile != NULL) { - commandLine += " --config \""; - commandLine += s_configFile; - commandLine += "\""; - } - - // install - if (!platform.installDaemon(DAEMON_NAME, - "Shares this system's mouse and keyboard with others.", - path, commandLine.c_str())) { - log((CLOG_CRIT "failed to install service")); - return kExitFailed; - } - log((CLOG_PRINT "installed successfully")); - return kExitSuccess; - } - else if (s_uninstall) { - switch (platform.uninstallDaemon(DAEMON_NAME)) { - case IPlatform::kSuccess: - log((CLOG_PRINT "uninstalled successfully")); - return kExitSuccess; - - case IPlatform::kFailed: - log((CLOG_CRIT "failed to uninstall service")); - return kExitFailed; - - case IPlatform::kAlready: - log((CLOG_CRIT "service isn't installed")); - // return success since service is uninstalled - return kExitSuccess; - } - } - // load configuration loadConfig(); @@ -801,15 +698,6 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) CNetwork::cleanup(); - // if running as a non-daemon backend and there was an error then - // wait for the user to click okay so he can see the error messages. - if (s_backend && !s_daemon && (result == kExitFailed || s_errors)) { - char msg[1024]; - msg[0] = '\0'; - LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); - MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); - } - return result; } diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index b4c5cbda..de6b086d 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -511,7 +511,7 @@ CClient::runServer() for (;;) { try { // allow connect this much time to succeed - CTimerThread timer(m_camp ? -1.0 : 30.0); + CTimerThread timer(15.0); // create socket and attempt to connect to server log((CLOG_DEBUG1 "connecting to server")); @@ -532,7 +532,7 @@ CClient::runServer() } // we're camping. wait a bit before retrying - CThread::sleep(5.0); + CThread::sleep(15.0); } } diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index a7301199..5a96156e 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -459,6 +459,7 @@ CMSWindowsSecondaryScreen::updateKeys() if ((m_keys[VK_SCROLL] & 0x01) != 0) { m_mask |= KeyModifierScrollLock; } + // note -- do not save KeyModifierModeSwitch in m_mask log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); } @@ -634,6 +635,11 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, KeyModifierAlt | KeyModifierMeta)); + // set control and alt if mode shift (AltGr) is requested + if ((mask & KeyModifierModeSwitch) != 0) { + outMask |= KeyModifierControl | KeyModifierAlt; + } + // extract extended key flag const bool isExtended = ((virtualKey & 0x100) != 0); virtualKey &= ~0x100; diff --git a/lib/platform/CUnixPlatform.cpp b/lib/platform/CUnixPlatform.cpp index 0a6518c2..f0e447eb 100644 --- a/lib/platform/CUnixPlatform.cpp +++ b/lib/platform/CUnixPlatform.cpp @@ -38,14 +38,15 @@ CUnixPlatform::~CUnixPlatform() } bool -CUnixPlatform::installDaemon(const char*, const char*, const char*, const char*) +CUnixPlatform::installDaemon(const char*, const char*, + const char*, const char*, bool) { // daemons don't require special installation return true; } CUnixPlatform::EResult -CUnixPlatform::uninstallDaemon(const char*) +CUnixPlatform::uninstallDaemon(const char*, bool) { // daemons don't require special installation return kSuccess; @@ -106,6 +107,18 @@ CUnixPlatform::installDaemonLogger(const char* name) CLog::setOutputter(&CUnixPlatform::deamonLogger); } +bool +CUnixPlatform::canInstallDaemon(const char*, bool) const +{ + return false; +} + +bool +CUnixPlatform::isDaemonInstalled(const char*, bool) const +{ + return false; +} + const char* CUnixPlatform::getBasename(const char* pathname) const { diff --git a/lib/platform/CUnixPlatform.h b/lib/platform/CUnixPlatform.h index 91f73565..00e5ea23 100644 --- a/lib/platform/CUnixPlatform.h +++ b/lib/platform/CUnixPlatform.h @@ -27,10 +27,15 @@ public: virtual bool installDaemon(const char* name, const char* description, const char* pathname, - const char* commandLine); - virtual EResult uninstallDaemon(const char* name); + const char* commandLine, + bool allUsers); + virtual EResult uninstallDaemon(const char* name, bool allUsers); virtual int daemonize(const char* name, DaemonFunc); virtual void installDaemonLogger(const char* name); + virtual bool canInstallDaemon(const char* name, + bool allUsers) const; + virtual bool isDaemonInstalled(const char* name, + bool allUsers) const; virtual const char* getBasename(const char* pathname) const; virtual CString getUserDirectory() const; virtual CString getSystemDirectory() const; diff --git a/lib/platform/CWin32Platform.cpp b/lib/platform/CWin32Platform.cpp index f2d5c1ba..29dece5b 100644 --- a/lib/platform/CWin32Platform.cpp +++ b/lib/platform/CWin32Platform.cpp @@ -94,14 +94,16 @@ CWin32Platform::setStatusError(SERVICE_STATUS_HANDLE handle, DWORD error) bool CWin32Platform::installDaemon(const char* name, const char* description, - const char* pathname, const char* commandLine) + const char* pathname, const char* commandLine, bool allUsers) { - // windows 95 family services - if (isWindows95Family()) { + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || isWindows95Family()) { // open registry - HKEY key = open95ServicesKey(); + HKEY key = isWindows95Family() ? open95ServicesKey() : + openUserStartupKey(); if (key == NULL) { - log((CLOG_ERR "cannot open RunServices registry key", GetLastError())); + log((CLOG_ERR "cannot open registry key", GetLastError())); return false; } @@ -152,6 +154,8 @@ CWin32Platform::installDaemon(const char* name, const char* description, CloseServiceHandle(mgr); } else { +// FIXME -- handle ERROR_SERVICE_EXISTS + log((CLOG_ERR "CreateService failed with %d", GetLastError())); CloseServiceHandle(mgr); return false; @@ -162,7 +166,7 @@ CWin32Platform::installDaemon(const char* name, const char* description, key = openKey(key, name); if (key == NULL) { // can't open key - uninstallDaemon(name); + uninstallDaemon(name, allUsers); return false; } @@ -173,7 +177,7 @@ CWin32Platform::installDaemon(const char* name, const char* description, key = openKey(key, "Parameters"); if (key == NULL) { // can't open key - uninstallDaemon(name); + uninstallDaemon(name, allUsers); return false; } setValue(key, "CommandLine", commandLine); @@ -186,14 +190,16 @@ CWin32Platform::installDaemon(const char* name, const char* description, } IPlatform::EResult -CWin32Platform::uninstallDaemon(const char* name) +CWin32Platform::uninstallDaemon(const char* name, bool allUsers) { - // windows 95 family services - if (isWindows95Family()) { + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || isWindows95Family()) { // open registry - HKEY key = open95ServicesKey(); + HKEY key = isWindows95Family() ? open95ServicesKey() : + openUserStartupKey(); if (key == NULL) { - log((CLOG_ERR "cannot open RunServices registry key", GetLastError())); + log((CLOG_ERR "cannot open registry key", GetLastError())); return kAlready; } @@ -334,6 +340,91 @@ CWin32Platform::installDaemonLogger(const char* name) } } +bool +CWin32Platform::canInstallDaemon(const char* name, bool allUsers) const +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || isWindows95Family()) { + // check if we can open the registry key + HKEY key = isWindows95Family() ? open95ServicesKey() : + openUserStartupKey(); + closeKey(key); + return (key != NULL); + } + + // windows NT family services + else { + // check if we can open service manager for write + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + return false; + } + CloseServiceHandle(mgr); + + // check if we can open the registry key for this service + HKEY key = openNTServicesKey(); + key = openKey(key, name); + key = openKey(key, "Parameters"); + closeKey(key); + + return (key != NULL); + } +} + +bool +CWin32Platform::isDaemonInstalled(const char* name, bool allUsers) const +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || isWindows95Family()) { + // check if we can open the registry key + HKEY key = isWindows95Family() ? open95ServicesKey() : + openUserStartupKey(); + if (key == NULL) { + return false; + } + + // check for entry + const bool installed = !readValueString(key, name).empty(); + + // clean up + closeKey(key); + + return installed; + } + + // windows NT family services + else { + // check parameters for this service + HKEY key = openNTServicesKey(); + key = openKey(key, name); + key = openKey(key, "Parameters"); + if (key != NULL) { + const bool installed = !readValueString(key, "CommandLine").empty(); + closeKey(key); + if (!installed) { + return false; + } + } + + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ); + if (mgr == NULL) { + return false; + } + + // open the service + SC_HANDLE service = OpenService(mgr, name, GENERIC_READ); + + // clean up + CloseServiceHandle(service); + CloseServiceHandle(mgr); + + return (service != NULL); + } +} + const char* CWin32Platform::getBasename(const char* pathname) const { @@ -436,6 +527,11 @@ CWin32Platform::addPathComponent(const CString& prefix, HKEY CWin32Platform::openKey(HKEY key, const char* keyName) { + // ignore if parent is NULL + if (key == NULL) { + return NULL; + } + // open next key HKEY newKey; LONG result = RegOpenKeyEx(key, keyName, 0, @@ -555,6 +651,21 @@ CWin32Platform::open95ServicesKey() return openKey(HKEY_LOCAL_MACHINE, s_keyNames); } +HKEY +CWin32Platform::openUserStartupKey() +{ + static const char* s_keyNames[] = { + _T("Software"), + _T("Microsoft"), + _T("Windows"), + _T("CurrentVersion"), + _T("Run"), + NULL + }; + + return openKey(HKEY_CURRENT_USER, s_keyNames); +} + int CWin32Platform::runDaemon(RunFunc run, StopFunc stop) { diff --git a/lib/platform/CWin32Platform.h b/lib/platform/CWin32Platform.h index 644d4408..dadcb4f2 100644 --- a/lib/platform/CWin32Platform.h +++ b/lib/platform/CWin32Platform.h @@ -71,10 +71,15 @@ public: virtual bool installDaemon(const char* name, const char* description, const char* pathname, - const char* commandLine); - virtual EResult uninstallDaemon(const char* name); + const char* commandLine, + bool allUsers); + virtual EResult uninstallDaemon(const char* name, bool allUsers); virtual int daemonize(const char* name, DaemonFunc); virtual void installDaemonLogger(const char* name); + virtual bool canInstallDaemon(const char* name, + bool allUsers) const; + virtual bool isDaemonInstalled(const char* name, + bool allUsers) const; virtual const char* getBasename(const char* pathname) const; virtual CString getUserDirectory() const; virtual CString getSystemDirectory() const; @@ -93,6 +98,7 @@ private: static CString readValueString(HKEY, const char* name); static HKEY openNTServicesKey(); static HKEY open95ServicesKey(); + static HKEY openUserStartupKey(); void serviceMain(DWORD, LPTSTR*); static void WINAPI serviceMainEntry(DWORD, LPTSTR*); diff --git a/lib/platform/IPlatform.h b/lib/platform/IPlatform.h index 95d10cdb..2962f4f2 100644 --- a/lib/platform/IPlatform.h +++ b/lib/platform/IPlatform.h @@ -43,18 +43,22 @@ public: system and \c description is a short human readable description of the daemon. \c pathname is the path to the daemon executable. \c commandLine should \b not include the name of program as the - first argument. + first argument. If \c allUsers is true then the daemon will be + installed to start at boot time, otherwise it will be installed to + start when the current user logs in. */ // FIXME -- throw on error? will get better error messages that way. virtual bool installDaemon(const char* name, const char* description, const char* pathname, - const char* commandLine) = 0; + const char* commandLine, + bool allUsers) = 0; + //! Uninstall daemon /*! Uninstall a daemon. */ - virtual EResult uninstallDaemon(const char* name) = 0; + virtual EResult uninstallDaemon(const char* name, bool allUsers) = 0; //! Daemonize the process /*! @@ -91,6 +95,24 @@ public: //! @name accessors //@{ + //! Check if user has permission to install the daemon + /*! + Returns true iff the caller has permission to install or + uninstall the daemon. Note that even if this method returns + true it's possible that installing/uninstalling the service + may still fail. This method ignores whether or not the + service is already installed. + */ + virtual bool canInstallDaemon(const char* name, + bool allUsers) const = 0; + + //! Check if the daemon is installed + /*! + Returns true iff the daemon is installed. + */ + virtual bool isDaemonInstalled(const char* name, + bool allUsers) const = 0; + //! Extract base name /*! Find the base name in the given \c pathname. diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index b9622694..0bd15164 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -614,264 +614,266 @@ CMSWindowsPrimaryScreen::ignore() const return (m_mark != m_markReceived); } -static const KeyID g_virtualKey[] = +// map virtual keys to synergy key enumeration. use extended keyboard +// bit to distinguish some keys. +static const KeyID g_virtualKey[][2] = { - /* 0x00 */ kKeyNone, // reserved - /* 0x01 */ kKeyNone, // VK_LBUTTON - /* 0x02 */ kKeyNone, // VK_RBUTTON - /* 0x03 */ kKeyBreak, // VK_CANCEL - /* 0x04 */ kKeyNone, // VK_MBUTTON - /* 0x05 */ kKeyNone, // undefined - /* 0x06 */ kKeyNone, // undefined - /* 0x07 */ kKeyNone, // undefined - /* 0x08 */ kKeyBackSpace, // VK_BACK - /* 0x09 */ kKeyTab, // VK_TAB - /* 0x0a */ kKeyNone, // undefined - /* 0x0b */ kKeyNone, // undefined - /* 0x0c */ kKeyClear, // VK_CLEAR - /* 0x0d */ kKeyReturn, // VK_RETURN - /* 0x0e */ kKeyNone, // undefined - /* 0x0f */ kKeyNone, // undefined - /* 0x10 */ kKeyShift_L, // VK_SHIFT - /* 0x11 */ kKeyControl_L, // VK_CONTROL - /* 0x12 */ kKeyAlt_L, // VK_MENU - /* 0x13 */ kKeyPause, // VK_PAUSE - /* 0x14 */ kKeyCapsLock, // VK_CAPITAL - /* 0x15 */ kKeyNone, // VK_KANA - /* 0x16 */ kKeyNone, // VK_HANGUL - /* 0x17 */ kKeyNone, // VK_JUNJA - /* 0x18 */ kKeyNone, // VK_FINAL - /* 0x19 */ kKeyNone, // VK_KANJI - /* 0x1a */ kKeyNone, // undefined - /* 0x1b */ kKeyEscape, // VK_ESCAPE - /* 0x1c */ kKeyNone, // VK_CONVERT - /* 0x1d */ kKeyNone, // VK_NONCONVERT - /* 0x1e */ kKeyNone, // VK_ACCEPT - /* 0x1f */ kKeyNone, // VK_MODECHANGE - /* 0x20 */ 0x0020, // VK_SPACE - /* 0x21 */ kKeyPageUp, // VK_PRIOR - /* 0x22 */ kKeyPageDown, // VK_NEXT - /* 0x23 */ kKeyEnd, // VK_END - /* 0x24 */ kKeyHome, // VK_HOME - /* 0x25 */ kKeyLeft, // VK_LEFT - /* 0x26 */ kKeyUp, // VK_UP - /* 0x27 */ kKeyRight, // VK_RIGHT - /* 0x28 */ kKeyDown, // VK_DOWN - /* 0x29 */ kKeySelect, // VK_SELECT - /* 0x2a */ kKeyNone, // VK_PRINT - /* 0x2b */ kKeyExecute, // VK_EXECUTE - /* 0x2c */ kKeyPrint, // VK_SNAPSHOT - /* 0x2d */ kKeyInsert, // VK_INSERT - /* 0x2e */ kKeyDelete, // VK_DELETE - /* 0x2f */ kKeyHelp, // VK_HELP - /* 0x30 */ kKeyNone, // VK_0 - /* 0x31 */ kKeyNone, // VK_1 - /* 0x32 */ kKeyNone, // VK_2 - /* 0x33 */ kKeyNone, // VK_3 - /* 0x34 */ kKeyNone, // VK_4 - /* 0x35 */ kKeyNone, // VK_5 - /* 0x36 */ kKeyNone, // VK_6 - /* 0x37 */ kKeyNone, // VK_7 - /* 0x38 */ kKeyNone, // VK_8 - /* 0x39 */ kKeyNone, // VK_9 - /* 0x3a */ kKeyNone, // undefined - /* 0x3b */ kKeyNone, // undefined - /* 0x3c */ kKeyNone, // undefined - /* 0x3d */ kKeyNone, // undefined - /* 0x3e */ kKeyNone, // undefined - /* 0x3f */ kKeyNone, // undefined - /* 0x40 */ kKeyNone, // undefined - /* 0x41 */ kKeyNone, // VK_A - /* 0x42 */ kKeyNone, // VK_B - /* 0x43 */ kKeyNone, // VK_C - /* 0x44 */ kKeyNone, // VK_D - /* 0x45 */ kKeyNone, // VK_E - /* 0x46 */ kKeyNone, // VK_F - /* 0x47 */ kKeyNone, // VK_G - /* 0x48 */ kKeyNone, // VK_H - /* 0x49 */ kKeyNone, // VK_I - /* 0x4a */ kKeyNone, // VK_J - /* 0x4b */ kKeyNone, // VK_K - /* 0x4c */ kKeyNone, // VK_L - /* 0x4d */ kKeyNone, // VK_M - /* 0x4e */ kKeyNone, // VK_N - /* 0x4f */ kKeyNone, // VK_O - /* 0x50 */ kKeyNone, // VK_P - /* 0x51 */ kKeyNone, // VK_Q - /* 0x52 */ kKeyNone, // VK_R - /* 0x53 */ kKeyNone, // VK_S - /* 0x54 */ kKeyNone, // VK_T - /* 0x55 */ kKeyNone, // VK_U - /* 0x56 */ kKeyNone, // VK_V - /* 0x57 */ kKeyNone, // VK_W - /* 0x58 */ kKeyNone, // VK_X - /* 0x59 */ kKeyNone, // VK_Y - /* 0x5a */ kKeyNone, // VK_Z - /* 0x5b */ kKeyMeta_L, // VK_LWIN - /* 0x5c */ kKeyMeta_R, // VK_RWIN - /* 0x5d */ kKeyMenu, // VK_APPS - /* 0x5e */ kKeyNone, // undefined - /* 0x5f */ kKeyNone, // undefined - /* 0x60 */ kKeyKP_0, // VK_NUMPAD0 - /* 0x61 */ kKeyKP_1, // VK_NUMPAD1 - /* 0x62 */ kKeyKP_2, // VK_NUMPAD2 - /* 0x63 */ kKeyKP_3, // VK_NUMPAD3 - /* 0x64 */ kKeyKP_4, // VK_NUMPAD4 - /* 0x65 */ kKeyKP_5, // VK_NUMPAD5 - /* 0x66 */ kKeyKP_6, // VK_NUMPAD6 - /* 0x67 */ kKeyKP_7, // VK_NUMPAD7 - /* 0x68 */ kKeyKP_8, // VK_NUMPAD8 - /* 0x69 */ kKeyKP_9, // VK_NUMPAD9 - /* 0x6a */ kKeyKP_Multiply, // VK_MULTIPLY - /* 0x6b */ kKeyKP_Add, // VK_ADD - /* 0x6c */ kKeyKP_Separator,// VK_SEPARATOR - /* 0x6d */ kKeyKP_Subtract, // VK_SUBTRACT - /* 0x6e */ kKeyKP_Decimal, // VK_DECIMAL - /* 0x6f */ kKeyKP_Divide, // VK_DIVIDE - /* 0x70 */ kKeyF1, // VK_F1 - /* 0x71 */ kKeyF2, // VK_F2 - /* 0x72 */ kKeyF3, // VK_F3 - /* 0x73 */ kKeyF4, // VK_F4 - /* 0x74 */ kKeyF5, // VK_F5 - /* 0x75 */ kKeyF6, // VK_F6 - /* 0x76 */ kKeyF7, // VK_F7 - /* 0x77 */ kKeyF8, // VK_F8 - /* 0x78 */ kKeyF9, // VK_F9 - /* 0x79 */ kKeyF10, // VK_F10 - /* 0x7a */ kKeyF11, // VK_F11 - /* 0x7b */ kKeyF12, // VK_F12 - /* 0x7c */ kKeyF13, // VK_F13 - /* 0x7d */ kKeyF14, // VK_F14 - /* 0x7e */ kKeyF15, // VK_F15 - /* 0x7f */ kKeyF16, // VK_F16 - /* 0x80 */ kKeyF17, // VK_F17 - /* 0x81 */ kKeyF18, // VK_F18 - /* 0x82 */ kKeyF19, // VK_F19 - /* 0x83 */ kKeyF20, // VK_F20 - /* 0x84 */ kKeyF21, // VK_F21 - /* 0x85 */ kKeyF22, // VK_F22 - /* 0x86 */ kKeyF23, // VK_F23 - /* 0x87 */ kKeyF24, // VK_F24 - /* 0x88 */ kKeyNone, // unassigned - /* 0x89 */ kKeyNone, // unassigned - /* 0x8a */ kKeyNone, // unassigned - /* 0x8b */ kKeyNone, // unassigned - /* 0x8c */ kKeyNone, // unassigned - /* 0x8d */ kKeyNone, // unassigned - /* 0x8e */ kKeyNone, // unassigned - /* 0x8f */ kKeyNone, // unassigned - /* 0x90 */ kKeyNumLock, // VK_NUMLOCK - /* 0x91 */ kKeyScrollLock, // VK_SCROLL - /* 0x92 */ kKeyNone, // unassigned - /* 0x93 */ kKeyNone, // unassigned - /* 0x94 */ kKeyNone, // unassigned - /* 0x95 */ kKeyNone, // unassigned - /* 0x96 */ kKeyNone, // unassigned - /* 0x97 */ kKeyNone, // unassigned - /* 0x98 */ kKeyNone, // unassigned - /* 0x99 */ kKeyNone, // unassigned - /* 0x9a */ kKeyNone, // unassigned - /* 0x9b */ kKeyNone, // unassigned - /* 0x9c */ kKeyNone, // unassigned - /* 0x9d */ kKeyNone, // unassigned - /* 0x9e */ kKeyNone, // unassigned - /* 0x9f */ kKeyNone, // unassigned - /* 0xa0 */ kKeyShift_L, // VK_LSHIFT - /* 0xa1 */ kKeyShift_R, // VK_RSHIFT - /* 0xa2 */ kKeyControl_L, // VK_LCONTROL - /* 0xa3 */ kKeyControl_R, // VK_RCONTROL - /* 0xa4 */ kKeyAlt_L, // VK_LMENU - /* 0xa5 */ kKeyAlt_R, // VK_RMENU - /* 0xa6 */ kKeyNone, // unassigned - /* 0xa7 */ kKeyNone, // unassigned - /* 0xa8 */ kKeyNone, // unassigned - /* 0xa9 */ kKeyNone, // unassigned - /* 0xaa */ kKeyNone, // unassigned - /* 0xab */ kKeyNone, // unassigned - /* 0xac */ kKeyNone, // unassigned - /* 0xad */ kKeyNone, // unassigned - /* 0xae */ kKeyNone, // unassigned - /* 0xaf */ kKeyNone, // unassigned - /* 0xb0 */ kKeyNone, // unassigned - /* 0xb1 */ kKeyNone, // unassigned - /* 0xb2 */ kKeyNone, // unassigned - /* 0xb3 */ kKeyNone, // unassigned - /* 0xb4 */ kKeyNone, // unassigned - /* 0xb5 */ kKeyNone, // unassigned - /* 0xb6 */ kKeyNone, // unassigned - /* 0xb7 */ kKeyNone, // unassigned - /* 0xb8 */ kKeyNone, // unassigned - /* 0xb9 */ kKeyNone, // unassigned - /* 0xba */ kKeyNone, // OEM specific - /* 0xbb */ kKeyNone, // OEM specific - /* 0xbc */ kKeyNone, // OEM specific - /* 0xbd */ kKeyNone, // OEM specific - /* 0xbe */ kKeyNone, // OEM specific - /* 0xbf */ kKeyNone, // OEM specific - /* 0xc0 */ kKeyNone, // OEM specific - /* 0xc1 */ kKeyNone, // unassigned - /* 0xc2 */ kKeyNone, // unassigned - /* 0xc3 */ kKeyNone, // unassigned - /* 0xc4 */ kKeyNone, // unassigned - /* 0xc5 */ kKeyNone, // unassigned - /* 0xc6 */ kKeyNone, // unassigned - /* 0xc7 */ kKeyNone, // unassigned - /* 0xc8 */ kKeyNone, // unassigned - /* 0xc9 */ kKeyNone, // unassigned - /* 0xca */ kKeyNone, // unassigned - /* 0xcb */ kKeyNone, // unassigned - /* 0xcc */ kKeyNone, // unassigned - /* 0xcd */ kKeyNone, // unassigned - /* 0xce */ kKeyNone, // unassigned - /* 0xcf */ kKeyNone, // unassigned - /* 0xd0 */ kKeyNone, // unassigned - /* 0xd1 */ kKeyNone, // unassigned - /* 0xd2 */ kKeyNone, // unassigned - /* 0xd3 */ kKeyNone, // unassigned - /* 0xd4 */ kKeyNone, // unassigned - /* 0xd5 */ kKeyNone, // unassigned - /* 0xd6 */ kKeyNone, // unassigned - /* 0xd7 */ kKeyNone, // unassigned - /* 0xd8 */ kKeyNone, // unassigned - /* 0xd9 */ kKeyNone, // unassigned - /* 0xda */ kKeyNone, // unassigned - /* 0xdb */ kKeyNone, // OEM specific - /* 0xdc */ kKeyNone, // OEM specific - /* 0xdd */ kKeyNone, // OEM specific - /* 0xde */ kKeyNone, // OEM specific - /* 0xdf */ kKeyNone, // OEM specific - /* 0xe0 */ kKeyNone, // OEM specific - /* 0xe1 */ kKeyNone, // OEM specific - /* 0xe2 */ kKeyNone, // OEM specific - /* 0xe3 */ kKeyNone, // OEM specific - /* 0xe4 */ kKeyNone, // OEM specific - /* 0xe5 */ kKeyNone, // unassigned - /* 0xe6 */ kKeyNone, // OEM specific - /* 0xe7 */ kKeyNone, // unassigned - /* 0xe8 */ kKeyNone, // unassigned - /* 0xe9 */ kKeyNone, // OEM specific - /* 0xea */ kKeyNone, // OEM specific - /* 0xeb */ kKeyNone, // OEM specific - /* 0xec */ kKeyNone, // OEM specific - /* 0xed */ kKeyNone, // OEM specific - /* 0xee */ kKeyNone, // OEM specific - /* 0xef */ kKeyNone, // OEM specific - /* 0xf0 */ kKeyNone, // OEM specific - /* 0xf1 */ kKeyNone, // OEM specific - /* 0xf2 */ kKeyNone, // OEM specific - /* 0xf3 */ kKeyNone, // OEM specific - /* 0xf4 */ kKeyNone, // OEM specific - /* 0xf5 */ kKeyNone, // OEM specific - /* 0xf6 */ kKeyNone, // VK_ATTN - /* 0xf7 */ kKeyNone, // VK_CRSEL - /* 0xf8 */ kKeyNone, // VK_EXSEL - /* 0xf9 */ kKeyNone, // VK_EREOF - /* 0xfa */ kKeyNone, // VK_PLAY - /* 0xfb */ kKeyNone, // VK_ZOOM - /* 0xfc */ kKeyNone, // reserved - /* 0xfd */ kKeyNone, // VK_PA1 - /* 0xfe */ kKeyNone, // VK_OEM_CLEAR - /* 0xff */ kKeyNone // reserved + /* 0x00 */ kKeyNone, kKeyNone, // reserved + /* 0x01 */ kKeyNone, kKeyNone, // VK_LBUTTON + /* 0x02 */ kKeyNone, kKeyNone, // VK_RBUTTON + /* 0x03 */ kKeyNone, kKeyBreak, // VK_CANCEL + /* 0x04 */ kKeyNone, kKeyNone, // VK_MBUTTON + /* 0x05 */ kKeyNone, kKeyNone, // undefined + /* 0x06 */ kKeyNone, kKeyNone, // undefined + /* 0x07 */ kKeyNone, kKeyNone, // undefined + /* 0x08 */ kKeyBackSpace, kKeyNone, // VK_BACK + /* 0x09 */ kKeyTab, kKeyNone, // VK_TAB + /* 0x0a */ kKeyNone, kKeyNone, // undefined + /* 0x0b */ kKeyNone, kKeyNone, // undefined + /* 0x0c */ kKeyClear, kKeyClear, // VK_CLEAR + /* 0x0d */ kKeyReturn, kKeyKP_Enter, // VK_RETURN + /* 0x0e */ kKeyNone, kKeyNone, // undefined + /* 0x0f */ kKeyNone, kKeyNone, // undefined + /* 0x10 */ kKeyShift_L, kKeyShift_R, // VK_SHIFT + /* 0x11 */ kKeyControl_L, kKeyControl_R, // VK_CONTROL + /* 0x12 */ kKeyAlt_L, kKeyAlt_R, // VK_MENU + /* 0x13 */ kKeyPause, kKeyNone, // VK_PAUSE + /* 0x14 */ kKeyCapsLock, kKeyNone, // VK_CAPITAL + /* 0x15 */ kKeyNone, kKeyNone, // VK_KANA + /* 0x16 */ kKeyNone, kKeyNone, // VK_HANGUL + /* 0x17 */ kKeyNone, kKeyNone, // VK_JUNJA + /* 0x18 */ kKeyNone, kKeyNone, // VK_FINAL + /* 0x19 */ kKeyNone, kKeyNone, // VK_KANJI + /* 0x1a */ kKeyNone, kKeyNone, // undefined + /* 0x1b */ kKeyEscape, kKeyNone, // VK_ESCAPE + /* 0x1c */ kKeyNone, kKeyNone, // VK_CONVERT + /* 0x1d */ kKeyNone, kKeyNone, // VK_NONCONVERT + /* 0x1e */ kKeyNone, kKeyNone, // VK_ACCEPT + /* 0x1f */ kKeyNone, kKeyNone, // VK_MODECHANGE + /* 0x20 */ 0x0020, kKeyNone, // VK_SPACE + /* 0x21 */ kKeyKP_PageUp, kKeyPageUp, // VK_PRIOR + /* 0x22 */ kKeyKP_PageDown, kKeyPageDown, // VK_NEXT + /* 0x23 */ kKeyKP_End, kKeyEnd, // VK_END + /* 0x24 */ kKeyKP_Home, kKeyHome, // VK_HOME + /* 0x25 */ kKeyKP_Left, kKeyLeft, // VK_LEFT + /* 0x26 */ kKeyKP_Up, kKeyUp, // VK_UP + /* 0x27 */ kKeyKP_Right, kKeyRight, // VK_RIGHT + /* 0x28 */ kKeyKP_Down, kKeyDown, // VK_DOWN + /* 0x29 */ kKeySelect, kKeySelect, // VK_SELECT + /* 0x2a */ kKeyNone, kKeyNone, // VK_PRINT + /* 0x2b */ kKeyExecute, kKeyExecute, // VK_EXECUTE + /* 0x2c */ kKeyPrint, kKeyPrint, // VK_SNAPSHOT + /* 0x2d */ kKeyKP_Insert, kKeyInsert, // VK_INSERT + /* 0x2e */ kKeyKP_Delete, kKeyDelete, // VK_DELETE + /* 0x2f */ kKeyHelp, kKeyHelp, // VK_HELP + /* 0x30 */ kKeyNone, kKeyNone, // VK_0 + /* 0x31 */ kKeyNone, kKeyNone, // VK_1 + /* 0x32 */ kKeyNone, kKeyNone, // VK_2 + /* 0x33 */ kKeyNone, kKeyNone, // VK_3 + /* 0x34 */ kKeyNone, kKeyNone, // VK_4 + /* 0x35 */ kKeyNone, kKeyNone, // VK_5 + /* 0x36 */ kKeyNone, kKeyNone, // VK_6 + /* 0x37 */ kKeyNone, kKeyNone, // VK_7 + /* 0x38 */ kKeyNone, kKeyNone, // VK_8 + /* 0x39 */ kKeyNone, kKeyNone, // VK_9 + /* 0x3a */ kKeyNone, kKeyNone, // undefined + /* 0x3b */ kKeyNone, kKeyNone, // undefined + /* 0x3c */ kKeyNone, kKeyNone, // undefined + /* 0x3d */ kKeyNone, kKeyNone, // undefined + /* 0x3e */ kKeyNone, kKeyNone, // undefined + /* 0x3f */ kKeyNone, kKeyNone, // undefined + /* 0x40 */ kKeyNone, kKeyNone, // undefined + /* 0x41 */ kKeyNone, kKeyNone, // VK_A + /* 0x42 */ kKeyNone, kKeyNone, // VK_B + /* 0x43 */ kKeyNone, kKeyNone, // VK_C + /* 0x44 */ kKeyNone, kKeyNone, // VK_D + /* 0x45 */ kKeyNone, kKeyNone, // VK_E + /* 0x46 */ kKeyNone, kKeyNone, // VK_F + /* 0x47 */ kKeyNone, kKeyNone, // VK_G + /* 0x48 */ kKeyNone, kKeyNone, // VK_H + /* 0x49 */ kKeyNone, kKeyNone, // VK_I + /* 0x4a */ kKeyNone, kKeyNone, // VK_J + /* 0x4b */ kKeyNone, kKeyNone, // VK_K + /* 0x4c */ kKeyNone, kKeyNone, // VK_L + /* 0x4d */ kKeyNone, kKeyNone, // VK_M + /* 0x4e */ kKeyNone, kKeyNone, // VK_N + /* 0x4f */ kKeyNone, kKeyNone, // VK_O + /* 0x50 */ kKeyNone, kKeyNone, // VK_P + /* 0x51 */ kKeyNone, kKeyNone, // VK_Q + /* 0x52 */ kKeyNone, kKeyNone, // VK_R + /* 0x53 */ kKeyNone, kKeyNone, // VK_S + /* 0x54 */ kKeyNone, kKeyNone, // VK_T + /* 0x55 */ kKeyNone, kKeyNone, // VK_U + /* 0x56 */ kKeyNone, kKeyNone, // VK_V + /* 0x57 */ kKeyNone, kKeyNone, // VK_W + /* 0x58 */ kKeyNone, kKeyNone, // VK_X + /* 0x59 */ kKeyNone, kKeyNone, // VK_Y + /* 0x5a */ kKeyNone, kKeyNone, // VK_Z + /* 0x5b */ kKeyNone, kKeyMeta_L, // VK_LWIN + /* 0x5c */ kKeyNone, kKeyMeta_R, // VK_RWIN + /* 0x5d */ kKeyMenu, kKeyMenu, // VK_APPS + /* 0x5e */ kKeyNone, kKeyNone, // undefined + /* 0x5f */ kKeyNone, kKeyNone, // undefined + /* 0x60 */ kKeyKP_0, kKeyNone, // VK_NUMPAD0 + /* 0x61 */ kKeyKP_1, kKeyNone, // VK_NUMPAD1 + /* 0x62 */ kKeyKP_2, kKeyNone, // VK_NUMPAD2 + /* 0x63 */ kKeyKP_3, kKeyNone, // VK_NUMPAD3 + /* 0x64 */ kKeyKP_4, kKeyNone, // VK_NUMPAD4 + /* 0x65 */ kKeyKP_5, kKeyNone, // VK_NUMPAD5 + /* 0x66 */ kKeyKP_6, kKeyNone, // VK_NUMPAD6 + /* 0x67 */ kKeyKP_7, kKeyNone, // VK_NUMPAD7 + /* 0x68 */ kKeyKP_8, kKeyNone, // VK_NUMPAD8 + /* 0x69 */ kKeyKP_9, kKeyNone, // VK_NUMPAD9 + /* 0x6a */ kKeyKP_Multiply, kKeyNone, // VK_MULTIPLY + /* 0x6b */ kKeyKP_Add, kKeyNone, // VK_ADD + /* 0x6c */ kKeyKP_Separator,kKeyKP_Separator,// VK_SEPARATOR + /* 0x6d */ kKeyKP_Subtract, kKeyNone, // VK_SUBTRACT + /* 0x6e */ kKeyKP_Decimal, kKeyNone, // VK_DECIMAL + /* 0x6f */ kKeyNone, kKeyKP_Divide, // VK_DIVIDE + /* 0x70 */ kKeyF1, kKeyNone, // VK_F1 + /* 0x71 */ kKeyF2, kKeyNone, // VK_F2 + /* 0x72 */ kKeyF3, kKeyNone, // VK_F3 + /* 0x73 */ kKeyF4, kKeyNone, // VK_F4 + /* 0x74 */ kKeyF5, kKeyNone, // VK_F5 + /* 0x75 */ kKeyF6, kKeyNone, // VK_F6 + /* 0x76 */ kKeyF7, kKeyNone, // VK_F7 + /* 0x77 */ kKeyF8, kKeyNone, // VK_F8 + /* 0x78 */ kKeyF9, kKeyNone, // VK_F9 + /* 0x79 */ kKeyF10, kKeyNone, // VK_F10 + /* 0x7a */ kKeyF11, kKeyNone, // VK_F11 + /* 0x7b */ kKeyF12, kKeyNone, // VK_F12 + /* 0x7c */ kKeyF13, kKeyF13, // VK_F13 + /* 0x7d */ kKeyF14, kKeyF14, // VK_F14 + /* 0x7e */ kKeyF15, kKeyF15, // VK_F15 + /* 0x7f */ kKeyF16, kKeyF16, // VK_F16 + /* 0x80 */ kKeyF17, kKeyF17, // VK_F17 + /* 0x81 */ kKeyF18, kKeyF18, // VK_F18 + /* 0x82 */ kKeyF19, kKeyF19, // VK_F19 + /* 0x83 */ kKeyF20, kKeyF20, // VK_F20 + /* 0x84 */ kKeyF21, kKeyF21, // VK_F21 + /* 0x85 */ kKeyF22, kKeyF22, // VK_F22 + /* 0x86 */ kKeyF23, kKeyF23, // VK_F23 + /* 0x87 */ kKeyF24, kKeyF24, // VK_F24 + /* 0x88 */ kKeyNone, kKeyNone, // unassigned + /* 0x89 */ kKeyNone, kKeyNone, // unassigned + /* 0x8a */ kKeyNone, kKeyNone, // unassigned + /* 0x8b */ kKeyNone, kKeyNone, // unassigned + /* 0x8c */ kKeyNone, kKeyNone, // unassigned + /* 0x8d */ kKeyNone, kKeyNone, // unassigned + /* 0x8e */ kKeyNone, kKeyNone, // unassigned + /* 0x8f */ kKeyNone, kKeyNone, // unassigned + /* 0x90 */ kKeyNumLock, kKeyNumLock, // VK_NUMLOCK + /* 0x91 */ kKeyScrollLock, kKeyNone, // VK_SCROLL + /* 0x92 */ kKeyNone, kKeyNone, // unassigned + /* 0x93 */ kKeyNone, kKeyNone, // unassigned + /* 0x94 */ kKeyNone, kKeyNone, // unassigned + /* 0x95 */ kKeyNone, kKeyNone, // unassigned + /* 0x96 */ kKeyNone, kKeyNone, // unassigned + /* 0x97 */ kKeyNone, kKeyNone, // unassigned + /* 0x98 */ kKeyNone, kKeyNone, // unassigned + /* 0x99 */ kKeyNone, kKeyNone, // unassigned + /* 0x9a */ kKeyNone, kKeyNone, // unassigned + /* 0x9b */ kKeyNone, kKeyNone, // unassigned + /* 0x9c */ kKeyNone, kKeyNone, // unassigned + /* 0x9d */ kKeyNone, kKeyNone, // unassigned + /* 0x9e */ kKeyNone, kKeyNone, // unassigned + /* 0x9f */ kKeyNone, kKeyNone, // unassigned + /* 0xa0 */ kKeyShift_L, kKeyShift_L, // VK_LSHIFT + /* 0xa1 */ kKeyShift_R, kKeyShift_R, // VK_RSHIFT + /* 0xa2 */ kKeyControl_L, kKeyControl_L, // VK_LCONTROL + /* 0xa3 */ kKeyControl_R, kKeyControl_R, // VK_RCONTROL + /* 0xa4 */ kKeyAlt_L, kKeyAlt_L, // VK_LMENU + /* 0xa5 */ kKeyAlt_R, kKeyAlt_R, // VK_RMENU + /* 0xa6 */ kKeyNone, kKeyNone, // unassigned + /* 0xa7 */ kKeyNone, kKeyNone, // unassigned + /* 0xa8 */ kKeyNone, kKeyNone, // unassigned + /* 0xa9 */ kKeyNone, kKeyNone, // unassigned + /* 0xaa */ kKeyNone, kKeyNone, // unassigned + /* 0xab */ kKeyNone, kKeyNone, // unassigned + /* 0xac */ kKeyNone, kKeyNone, // unassigned + /* 0xad */ kKeyNone, kKeyNone, // unassigned + /* 0xae */ kKeyNone, kKeyNone, // unassigned + /* 0xaf */ kKeyNone, kKeyNone, // unassigned + /* 0xb0 */ kKeyNone, kKeyNone, // unassigned + /* 0xb1 */ kKeyNone, kKeyNone, // unassigned + /* 0xb2 */ kKeyNone, kKeyNone, // unassigned + /* 0xb3 */ kKeyNone, kKeyNone, // unassigned + /* 0xb4 */ kKeyNone, kKeyNone, // unassigned + /* 0xb5 */ kKeyNone, kKeyNone, // unassigned + /* 0xb6 */ kKeyNone, kKeyNone, // unassigned + /* 0xb7 */ kKeyNone, kKeyNone, // unassigned + /* 0xb8 */ kKeyNone, kKeyNone, // unassigned + /* 0xb9 */ kKeyNone, kKeyNone, // unassigned + /* 0xba */ kKeyNone, kKeyNone, // OEM specific + /* 0xbb */ kKeyNone, kKeyNone, // OEM specific + /* 0xbc */ kKeyNone, kKeyNone, // OEM specific + /* 0xbd */ kKeyNone, kKeyNone, // OEM specific + /* 0xbe */ kKeyNone, kKeyNone, // OEM specific + /* 0xbf */ kKeyNone, kKeyNone, // OEM specific + /* 0xc0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xc1 */ kKeyNone, kKeyNone, // unassigned + /* 0xc2 */ kKeyNone, kKeyNone, // unassigned + /* 0xc3 */ kKeyNone, kKeyNone, // unassigned + /* 0xc4 */ kKeyNone, kKeyNone, // unassigned + /* 0xc5 */ kKeyNone, kKeyNone, // unassigned + /* 0xc6 */ kKeyNone, kKeyNone, // unassigned + /* 0xc7 */ kKeyNone, kKeyNone, // unassigned + /* 0xc8 */ kKeyNone, kKeyNone, // unassigned + /* 0xc9 */ kKeyNone, kKeyNone, // unassigned + /* 0xca */ kKeyNone, kKeyNone, // unassigned + /* 0xcb */ kKeyNone, kKeyNone, // unassigned + /* 0xcc */ kKeyNone, kKeyNone, // unassigned + /* 0xcd */ kKeyNone, kKeyNone, // unassigned + /* 0xce */ kKeyNone, kKeyNone, // unassigned + /* 0xcf */ kKeyNone, kKeyNone, // unassigned + /* 0xd0 */ kKeyNone, kKeyNone, // unassigned + /* 0xd1 */ kKeyNone, kKeyNone, // unassigned + /* 0xd2 */ kKeyNone, kKeyNone, // unassigned + /* 0xd3 */ kKeyNone, kKeyNone, // unassigned + /* 0xd4 */ kKeyNone, kKeyNone, // unassigned + /* 0xd5 */ kKeyNone, kKeyNone, // unassigned + /* 0xd6 */ kKeyNone, kKeyNone, // unassigned + /* 0xd7 */ kKeyNone, kKeyNone, // unassigned + /* 0xd8 */ kKeyNone, kKeyNone, // unassigned + /* 0xd9 */ kKeyNone, kKeyNone, // unassigned + /* 0xda */ kKeyNone, kKeyNone, // unassigned + /* 0xdb */ kKeyNone, kKeyNone, // OEM specific + /* 0xdc */ kKeyNone, kKeyNone, // OEM specific + /* 0xdd */ kKeyNone, kKeyNone, // OEM specific + /* 0xde */ kKeyNone, kKeyNone, // OEM specific + /* 0xdf */ kKeyNone, kKeyNone, // OEM specific + /* 0xe0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe1 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe2 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe3 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe4 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe5 */ kKeyNone, kKeyNone, // unassigned + /* 0xe6 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe7 */ kKeyNone, kKeyNone, // unassigned + /* 0xe8 */ kKeyNone, kKeyNone, // unassigned + /* 0xe9 */ kKeyNone, kKeyNone, // OEM specific + /* 0xea */ kKeyNone, kKeyNone, // OEM specific + /* 0xeb */ kKeyNone, kKeyNone, // OEM specific + /* 0xec */ kKeyNone, kKeyNone, // OEM specific + /* 0xed */ kKeyNone, kKeyNone, // OEM specific + /* 0xee */ kKeyNone, kKeyNone, // OEM specific + /* 0xef */ kKeyNone, kKeyNone, // OEM specific + /* 0xf0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf1 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf2 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf3 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf4 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf5 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf6 */ kKeyNone, kKeyNone, // VK_ATTN + /* 0xf7 */ kKeyNone, kKeyNone, // VK_CRSEL + /* 0xf8 */ kKeyNone, kKeyNone, // VK_EXSEL + /* 0xf9 */ kKeyNone, kKeyNone, // VK_EREOF + /* 0xfa */ kKeyNone, kKeyNone, // VK_PLAY + /* 0xfb */ kKeyNone, kKeyNone, // VK_ZOOM + /* 0xfc */ kKeyNone, kKeyNone, // reserved + /* 0xfd */ kKeyNone, kKeyNone, // VK_PA1 + /* 0xfe */ kKeyNone, kKeyNone, // VK_OEM_CLEAR + /* 0xff */ kKeyNone, kKeyNone // reserved }; KeyID @@ -918,68 +920,22 @@ CMSWindowsPrimaryScreen::mapKey( if ((m_keys[VK_SCROLL] & 0x01) != 0) { mask |= KeyModifierScrollLock; } + if ((mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + // ctrl+alt => AltGr on windows + mask |= KeyModifierModeSwitch; + mask &= ~(KeyModifierControl | KeyModifierAlt); + } *maskOut = mask; log((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask)); - // get the scan code - UINT scanCode = static_cast((info & 0xff0000) >> 16); - - // convert virtual key to one that distinguishes between left and - // right for keys that have left/right versions. known scan codes - // that don't have left/right versions are passed through unchanged. - // unknown scan codes return 0. - UINT vkCode2 = MapVirtualKey(scanCode, 3); - - // work around bug Q72583 (bad num pad conversion in MapVirtualKey()) - if (vkCode >= VK_NUMPAD0 && vkCode <= VK_DIVIDE) { - vkCode2 = vkCode; - } - - // MapVirtualKey() appears to map VK_LWIN, VK_RWIN, VK_APPS to - // some other meaningless virtual key. work around that bug. - else if (vkCode >= VK_LWIN && vkCode <= VK_APPS) { - vkCode2 = vkCode; - } - - // if the original vkCode is VK_SNAPSHOT then use it. oddly enough, - // some keyboards use the same scan code for keypad multiply and - // print screen. the difference is that the latter has the extended - // key flag set. but MapVirtualKey() doesn't seem capable of using - // that flag. - else if (vkCode == VK_SNAPSHOT) { - vkCode2 = vkCode; - } - - // if MapVirtualKey failed then use original virtual key - else if (vkCode2 == 0) { - vkCode2 = vkCode; - } - - // sadly, win32 will not distinguish between the left and right - // control and alt keys using the above function. however, we - // can check for those: if bit 24 of info is set then the key - // is a "extended" key, such as the right control and right alt - // keys. - if ((info & 0x1000000) != 0) { - switch (vkCode2) { - case VK_CONTROL: - case VK_LCONTROL: - vkCode2 = VK_RCONTROL; - break; - - case VK_MENU: - case VK_LMENU: - vkCode2 = VK_RMENU; - break; - } - } - - // use left/right distinguishing virtual key - vkCode = vkCode2; - log((CLOG_DEBUG1 "key vk=%d scan=%d", vkCode, scanCode)); + // get the scan code and the extended keyboard flag + UINT scanCode = static_cast((info & 0x00ff0000u) >> 16); + int extended = ((info & 0x01000000) == 0) ? 0 : 1; + log((CLOG_DEBUG1 "key vk=%d ext=%d scan=%d", vkCode, extended, scanCode)); // handle some keys via table lookup - KeyID id = g_virtualKey[vkCode]; + KeyID id = g_virtualKey[vkCode][extended]; if (id != kKeyNone) { return id; } @@ -989,17 +945,19 @@ CMSWindowsPrimaryScreen::mapKey( return kKeyMultiKey; } - // ToAscii() maps ctrl+letter to the corresponding control code - // and ctrl+backspace to delete. if we've got a control code or - // delete then do ToAscii() again but without the control state. - // ToAscii() interprets the control modifier state which we don't - // want. so save the control state then clear it. - BYTE lControl = m_keys[VK_LCONTROL]; - BYTE rControl = m_keys[VK_RCONTROL]; - BYTE control = m_keys[VK_CONTROL]; - m_keys[VK_LCONTROL] = 0; - m_keys[VK_RCONTROL] = 0; - m_keys[VK_CONTROL] = 0; + // save the control state then clear it. ToAscii() maps ctrl+letter + // to the corresponding control code and ctrl+backspace to delete. + // we don't want that translation so we clear the control modifier + // state. however, if we want to simulate AltGr (which is ctrl+alt) + // then we must not clear it. + BYTE lControl = m_keys[VK_LCONTROL]; + BYTE rControl = m_keys[VK_RCONTROL]; + BYTE control = m_keys[VK_CONTROL]; + if ((mask & KeyModifierModeSwitch) == 0) { + m_keys[VK_LCONTROL] = 0; + m_keys[VK_RCONTROL] = 0; + m_keys[VK_CONTROL] = 0; + } // convert to ascii // FIXME -- support unicode @@ -1092,18 +1050,18 @@ CMSWindowsPrimaryScreen::updateKeys() // we only care about the modifier key states. other keys and the // mouse buttons should be up. - m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); - m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); - m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); - m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL)); - m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL)); - m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL)); - m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU)); - m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU)); - m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU)); - m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN)); - m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN)); - m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS)); + m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT) & 0x80); + m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT) & 0x80); + m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT) & 0x80); + m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL) & 0x80); + m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL) & 0x80); + m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL) & 0x80); + m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU) & 0x80); + m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU) & 0x80); + m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU) & 0x80); + m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN) & 0x80); + m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN) & 0x80); + m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS) & 0x80); m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); From a7d413dd7595f8c53a174368ff228e9a046415e1 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Sep 2002 17:36:25 +0000 Subject: [PATCH 341/807] Changed version number to 0.9.10. --- configure.in | 2 +- lib/base/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 803baa55..7c11f499 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=0 MINOR_VERSION=9 -RELEASE_VERSION=9 +RELEASE_VERSION=10 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/base/Version.h b/lib/base/Version.h index 219b05c0..54b23ec6 100644 --- a/lib/base/Version.h +++ b/lib/base/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "0.9.9" +# define VERSION "0.9.10" #endif // important strings From 08867440c1059fe961cd1ecb2c9ecc9a29e035c5 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Sep 2002 17:36:52 +0000 Subject: [PATCH 342/807] updating ChangeLog. --- ChangeLog | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/ChangeLog b/ChangeLog index f1163775..f7b8a7e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,70 @@ +2002/09/02 17:36:25 crs +configure.in +lib/base/Version.h + +Changed version number to 0.9.10. + +---------- +2002/09/02 17:30:04 crs +BUGS +INSTALL +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +lib/client/CClient.cpp +lib/client/CMSWindowsSecondaryScreen.cpp +lib/platform/CUnixPlatform.cpp +lib/platform/CUnixPlatform.h +lib/platform/CWin32Platform.cpp +lib/platform/CWin32Platform.h +lib/platform/IPlatform.h +lib/server/CMSWindowsPrimaryScreen.cpp + +Fixed win32 config saving, keyboard mapping, and AltGr bugs. +Made extensive changes to the launcher to provide more control +over setting up auto-start and it now saves configuration to +the user's documents directory if auto-starting at login and +saves to the system directory if auto-starting at boot. +Replaced MapVirtualKey() with table lookup to work around that +function's lack of support for extended keyboard scan codes. +Added first cut at support for AltGr. + +---------- +2002/09/01 15:30:00 crs +client/CXWindowsSecondaryScreen.cpp +client/CXWindowsSecondaryScreen.h +server/CXWindowsPrimaryScreen.cpp +server/CXWindowsPrimaryScreen.h +synergy/KeyTypes.h + +Added support for mode switch key to X11 screens. + +---------- +2002/09/01 10:31:10 crs +acinclude.m4 +configure.in +lib/base/stdsstream.h + +added more tests to autoconf. also now handling missing sstream +header in gcc 2.95 by including sstream header backported from v3. + +---------- +2002/09/01 09:28:54 crs +lib/platform/CXWindowsUtil.cpp + +lowered severity of some debug messages. + +---------- 2002/08/18 17:45:59 crs configure.in lib/base/Version.h From 87d973e4d08727f74a4ef086ecc536cf4e53ef29 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Sep 2002 17:48:51 +0000 Subject: [PATCH 343/807] removed executable bit on new source files and added them to the makefile. --- cmd/launcher/CAutoStart.cpp | 0 cmd/launcher/CAutoStart.h | 0 cmd/launcher/LaunchUtil.cpp | 0 cmd/launcher/LaunchUtil.h | 0 cmd/launcher/Makefile.am | 4 ++++ 5 files changed, 4 insertions(+) mode change 100755 => 100644 cmd/launcher/CAutoStart.cpp mode change 100755 => 100644 cmd/launcher/CAutoStart.h mode change 100755 => 100644 cmd/launcher/LaunchUtil.cpp mode change 100755 => 100644 cmd/launcher/LaunchUtil.h diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp old mode 100755 new mode 100644 diff --git a/cmd/launcher/CAutoStart.h b/cmd/launcher/CAutoStart.h old mode 100755 new mode 100644 diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp old mode 100755 new mode 100644 diff --git a/cmd/launcher/LaunchUtil.h b/cmd/launcher/LaunchUtil.h old mode 100755 new mode 100644 diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am index 79e6a7e4..acf31abd 100644 --- a/cmd/launcher/Makefile.am +++ b/cmd/launcher/Makefile.am @@ -16,6 +16,10 @@ DEPTH = ../.. VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ + CAutoStart.cpp \ + CAutoStart.h \ + LaunchUtil.cpp \ + LaunchUtil.h \ launcher.cpp \ launcher.dsp \ launcher.rc \ From af5a90059934f717ac6603e6acfc622270711e3f Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Sep 2002 17:49:05 +0000 Subject: [PATCH 344/807] removed PORTING from binary distribution. --- dist/rpm/synergy.spec.in | 1 - 1 file changed, 1 deletion(-) diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in index a8a7d5fd..073f2f68 100644 --- a/dist/rpm/synergy.spec.in +++ b/dist/rpm/synergy.spec.in @@ -43,7 +43,6 @@ rm -rf $RPM_BUILD_ROOT %doc HISTORY %doc INSTALL %doc NEWS -%doc PORTING %doc README %doc TODO %doc examples/synergy.conf From e4c29179d9c0aef4fdb4b4114865ee69cc92be7d Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Sep 2002 17:49:18 +0000 Subject: [PATCH 345/807] added version 0.9.10 to NEWS. --- NEWS | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NEWS b/NEWS index aff2ffc1..e7d1f783 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,17 @@ Synergy News ============ +* Sep-02-2002 - Synergy 0.9.10 released + + Fixed following bugs: + * The Pause/Break and KP_Enter buttons were not working correctly on windows + * Configuration options were being lost on windows after a reboot + * Added support for AltGr/ModeSwitch keys + * Added support for auto-start on windows when not administrator + * Improved autoconf + * Added workaround for lack of sstream header on g++ 2.95. + + * Aug-18-2002 - Synergy 0.9.9 released Fixed three bugs: From 7f21588e9bb4989c3088a2b8e5a13c6b420d0c5d Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 4 Sep 2002 20:17:54 +0000 Subject: [PATCH 346/807] fixed bug that caused the wrong keycode to be used for most, possibly all, keysyms. was reading past the end of an array of keysyms. --- lib/client/CXWindowsSecondaryScreen.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index fe62e9f3..3f6c2ee3 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -917,7 +917,7 @@ void CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) { // there are up to 4 keysyms per keycode - static const int numKeysyms = 4; + static const int maxKeysyms = 4; // get the number of keycodes int minKeycode, maxKeycode; @@ -930,6 +930,12 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) minKeycode, numKeycodes, &keysymsPerKeycode); + // we only understand up to maxKeysyms keysyms per keycodes + int numKeysyms = keysymsPerKeycode; + if (numKeysyms > maxKeysyms) { + numKeysyms = maxKeysyms; + } + // initialize KeyCodeMask entry; m_keycodeMap.clear(); From ecb2fbb3429396a609428068fd1cd5db8b069948 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 4 Sep 2002 21:14:18 +0000 Subject: [PATCH 347/807] now looking up SendEvent() using GetProcAddress() so win95 systems can run the synergy client. --- lib/client/CMSWindowsSecondaryScreen.cpp | 35 ++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index 5a96156e..974a189a 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -338,6 +338,10 @@ CMSWindowsSecondaryScreen::hideWindow() void CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) { + typedef UINT (WINAPI *SendInput_t)(UINT, LPINPUT, int); + static bool gotSendInput = false; + static SendInput_t SendInput = NULL; + // move the mouse directly to target position on NT family or if // not using multiple monitors. if (m_screen->isMultimon() || !m_is95Family) { @@ -362,7 +366,9 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) // clicking the mouse or pressing a key between the absolute and // relative move) we'll use SendInput() which guarantees that the // events are delivered uninterrupted. we cannot prevent changes - // to the mouse acceleration at inopportune times, though. + // to the mouse acceleration at inopportune times, though. if + // SendInput() is unavailable then use mouse_event(); SendInput() + // is not available on Windows 95 and NT 4.0 prior to SP3. // // point-to-activate (x-mouse) doesn't seem to be bothered by the // absolute/relative combination. a window over the absolute @@ -371,6 +377,17 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) // app under the final mouse position does *not* get deactivated // by the absolute move to 0,0. else { + // lookup SendInput() function + if (!gotSendInput) { + gotSendInput = true; + HINSTANCE user32 = LoadLibrary("user32.dll"); + if (user32 != NULL) { + SendInput = reinterpret_cast( + GetProcAddress(user32, "SendInput")); + FreeLibrary(user32); + } + } + // save mouse speed & acceleration int oldSpeed[4]; bool accelChanged = @@ -401,8 +418,22 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) events[1].mi.dwFlags = MOUSEEVENTF_MOVE; events[1].mi.time = events[0].mi.time; events[1].mi.dwExtraInfo = 0; - SendInput(sizeof(events) / sizeof(events[0]), + if (SendInput != NULL) { + SendInput(sizeof(events) / sizeof(events[0]), events, sizeof(events[0])); + } + else { + mouse_event(events[0].mi.dwFlags, + events[0].mi.dx, + events[0].mi.dy, + events[0].mi.mouseData, + events[0].mi.dwExtraInfo); + mouse_event(events[1].mi.dwFlags, + events[1].mi.dx, + events[1].mi.dy, + events[1].mi.mouseData, + events[1].mi.dwExtraInfo); + } // restore mouse speed & acceleration if (accelChanged) { From a210c8c1ecdf628e4896eb0cdfc11cba01a013c0 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 4 Sep 2002 21:17:01 +0000 Subject: [PATCH 348/807] Changed version number to 0.9.11. Added NEWS item. --- NEWS | 7 +++++++ configure.in | 2 +- lib/base/Version.h | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index e7d1f783..1b88ddc1 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,13 @@ Synergy News ============ +* Sep-04-2002 - Synergy 0.9.11 released + + Fixed following bugs: + * Worked around missing SendInput() on windows 95/NT 4 prior to SP3 + * Fixed keyboard mapping on X11 synergy client + + * Sep-02-2002 - Synergy 0.9.10 released Fixed following bugs: diff --git a/configure.in b/configure.in index 7c11f499..7a53a807 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=0 MINOR_VERSION=9 -RELEASE_VERSION=10 +RELEASE_VERSION=11 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/base/Version.h b/lib/base/Version.h index 54b23ec6..a9e5ebb1 100644 --- a/lib/base/Version.h +++ b/lib/base/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "0.9.10" +# define VERSION "0.9.11" #endif // important strings From c4f8373e9a565c532af084a368ca826a81cf507d Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 4 Sep 2002 22:07:02 +0000 Subject: [PATCH 349/807] Changes for version 0.9.11. --- ChangeLog | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ChangeLog b/ChangeLog index f7b8a7e9..be45c69a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2002/09/04 21:17:01 crs +NEWS +configure.in +lib/base/Version.h + +Changed version number to 0.9.11. Added NEWS item. + +---------- +2002/09/04 21:14:18 crs +lib/client/CMSWindowsSecondaryScreen.cpp + +now looking up SendEvent() using GetProcAddress() so win95 +systems can run the synergy client. + +---------- +2002/09/04 20:17:54 crs +lib/client/CXWindowsSecondaryScreen.cpp + +fixed bug that caused the wrong keycode to be used for most, +possibly all, keysyms. was reading past the end of an array +of keysyms. + +---------- 2002/09/02 17:36:25 crs configure.in lib/base/Version.h From 152eddc0c665f0e2099d9eea1270ebe191b672f1 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Sep 2002 11:59:56 +0000 Subject: [PATCH 350/807] Changed version to 0.9.12. --- configure.in | 2 +- lib/base/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 7a53a807..ea56066b 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=0 MINOR_VERSION=9 -RELEASE_VERSION=11 +RELEASE_VERSION=12 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/base/Version.h b/lib/base/Version.h index a9e5ebb1..2f638d4d 100644 --- a/lib/base/Version.h +++ b/lib/base/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "0.9.11" +# define VERSION "0.9.12" #endif // important strings From 06856e170d2a878ecc79c450868cb4c900c8f4d6 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Sep 2002 12:03:43 +0000 Subject: [PATCH 351/807] Fixed backend mode. Now reports log messages and, if any are serious, shows a message box before exiting. --- cmd/synergyc/resource.h | 6 +- cmd/synergyc/synergyc.cpp | 26 ++++- cmd/synergyc/synergyc.rc | 188 ++++++++++++++++------------------- cmd/synergys/resource.h | 10 +- cmd/synergys/synergys.cpp | 26 ++++- cmd/synergys/synergys.rc | 201 ++++++++++++++++---------------------- 6 files changed, 219 insertions(+), 238 deletions(-) diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h index 9c299850..c5887fb2 100644 --- a/cmd/synergyc/resource.h +++ b/cmd/synergyc/resource.h @@ -3,15 +3,13 @@ // Used by synergyc.rc // #define IDS_FAILED 1 -#define IDD_SYNERGY 101 -#define IDI_SYNERGY 103 -#define IDC_LOG 1000 +#define IDI_SYNERGY 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index d2e45f31..aa7906d3 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -447,16 +447,26 @@ parse(int argc, const char** argv) #include "CMSWindowsScreen.h" +static bool s_hasImportantLogMessages = false; + static bool logMessageBox(int priority, const char* msg) { - if (priority <= (s_backend ? CLog::kERROR : CLog::kFATAL)) { + // note any important messages the user may need to know about + if (priority <= CLog::kWARNING) { + s_hasImportantLogMessages = true; + } + + // FATAL and PRINT messages get a dialog box if not running as + // backend. if we're running as a backend the user will have + // a chance to see the messages when we exit. + if (!s_backend && priority <= CLog::kFATAL) { MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); return true; } else { - return s_backend; + return false; } } @@ -487,6 +497,9 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) // parse command line parse(argc, argv); + // cannot run as backend if running as a service + s_backend = false; + // run as a service return platform->runDaemon(realMain, daemonStop); } @@ -560,6 +573,15 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) CNetwork::cleanup(); + // let user examine any messages if we're running as a backend + // by putting up a dialog box before exiting. + if (s_backend && s_hasImportantLogMessages) { + char msg[1024]; + msg[0] = '\0'; + LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); + MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); + } + return result; } diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index f1523300..74ecee37 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -1,106 +1,82 @@ -//Microsoft Developer Studio generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE DISCARDABLE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE DISCARDABLE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE DISCARDABLE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_SYNERGY DIALOG DISCARDABLE 0, 0, 329, 158 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Synergy" -FONT 8, "MS Sans Serif" -BEGIN - EDITTEXT IDC_LOG,7,7,315,144,ES_MULTILINE | ES_AUTOHSCROLL | - ES_READONLY | WS_VSCROLL | WS_HSCROLL -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO DISCARDABLE -BEGIN - IDD_SYNERGY, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 322 - TOPMARGIN, 7 - BOTTOMMARGIN, 151 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_SYNERGY ICON DISCARDABLE "synergyc.ico" -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergyc.ico" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." +END + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index 6142fc38..6bfc7486 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -1,19 +1,17 @@ //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. -// Used by synergyd.rc +// Used by synergys.rc // #define IDS_FAILED 1 -#define IDD_SYNERGY 101 -#define IDI_SYNERGY 102 -#define IDC_LOG 1000 +#define IDI_SYNERGY 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1002 +#define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 496cf5a7..4f60ff9e 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -579,16 +579,26 @@ loadConfig() #include "CMSWindowsScreen.h" +static bool s_hasImportantLogMessages = false; + static bool logMessageBox(int priority, const char* msg) { - if (priority <= (s_backend ? CLog::kERROR : CLog::kFATAL)) { + // note any important messages the user may need to know about + if (priority <= CLog::kWARNING) { + s_hasImportantLogMessages = true; + } + + // FATAL and PRINT messages get a dialog box if not running as + // backend. if we're running as a backend the user will have + // a chance to see the messages when we exit. + if (!s_backend && priority <= CLog::kFATAL) { MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); return true; } else { - return s_backend; + return false; } } @@ -619,6 +629,9 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) // parse command line parse(argc, argv); + // cannot run as backend if running as a service + s_backend = false; + // load configuration loadConfig(); @@ -698,6 +711,15 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) CNetwork::cleanup(); + // let user examine any messages if we're running as a backend + // by putting up a dialog box before exiting. + if (s_backend && s_hasImportantLogMessages) { + char msg[1024]; + msg[0] = '\0'; + LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); + MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); + } + return result; } diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index b24adecc..61bb42f1 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -1,118 +1,83 @@ -//Microsoft Developer Studio generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE DISCARDABLE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE DISCARDABLE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE DISCARDABLE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_SYNERGY DIALOG DISCARDABLE 0, 0, 531, 159 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Synergy" -FONT 8, "MS Sans Serif" -BEGIN - LISTBOX IDC_LOG,7,7,517,145,NOT LBS_NOTIFY | - LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | - WS_TABSTOP -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO DISCARDABLE -BEGIN - IDD_SYNERGY, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 524 - TOPMARGIN, 7 - BOTTOMMARGIN, 152 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_SYNERGY ICON DISCARDABLE "synergys.ico" - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE DISCARDABLE -BEGIN - IDS_FAILED "Synergy is about to quit with an error. Please check the log for error messages then click OK." -END - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergys.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + From 10bbf6f82409e6caaf6837ed94e11cc6b8d98e8d Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Sep 2002 12:05:06 +0000 Subject: [PATCH 352/807] Added debug level combo box and version number to title bar. --- cmd/launcher/launcher.cpp | 77 ++++-- cmd/launcher/launcher.rc | 542 +++++++++++++++++++------------------- cmd/launcher/resource.h | 4 +- 3 files changed, 329 insertions(+), 294 deletions(-) diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index df1a9661..603354fd 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -16,6 +16,7 @@ #include "ProtocolTypes.h" #include "CPlatform.h" #include "CNetwork.h" +#include "CLog.h" #include "Version.h" #include "stdvector.h" #include "resource.h" @@ -50,6 +51,17 @@ HINSTANCE s_instance = NULL; static const TCHAR* s_mainClass = TEXT("GoSynergy"); static const TCHAR* s_layoutClass = TEXT("SynergyLayout"); +static const char* s_debugName[][2] = { + { TEXT("Error"), "ERROR" }, + { TEXT("Warning"), "WARNING" }, + { TEXT("Note"), "NOTE" }, + { TEXT("Info"), "INFO" }, + { TEXT("Debug"), "DEBUG" }, + { TEXT("Debug1"), "DEBUG1" }, + { TEXT("Debug2"), "DEBUG2" } +}; +static const int s_defaultDebug = 3; // INFO + static HWND s_mainWindow; static CConfig s_config; static CConfig s_oldConfig; @@ -97,7 +109,7 @@ static bool isClientChecked(HWND hwnd) { - HWND child = GetDlgItem(hwnd, IDC_MAIN_CLIENT_RADIO); + HWND child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); return (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED); } @@ -116,7 +128,7 @@ enableScreensControls(HWND hwnd) bool client = isClientChecked(hwnd); bool screenSelected = false; if (!client) { - HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); if (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR) { screenSelected = true; } @@ -167,7 +179,7 @@ updateNeighbor(HWND hwnd, const CString& screen, EDirection direction) } // add empty neighbor to combo box - SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"---"); + SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)TEXT("---")); // select neighbor in combo box LRESULT index = 0; @@ -190,20 +202,20 @@ updateNeighbors(HWND hwnd) { // get selected screen name or empty string if no selection CString screen; - HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); if (index != LB_ERR) { screen = s_screens[index]; } // set neighbor combo boxes - child = GetDlgItem(hwnd, IDC_MAIN_SERVER_LEFT_COMBO); + child = getItem(hwnd, IDC_MAIN_SERVER_LEFT_COMBO); updateNeighbor(child, screen, kLeft); - child = GetDlgItem(hwnd, IDC_MAIN_SERVER_RIGHT_COMBO); + child = getItem(hwnd, IDC_MAIN_SERVER_RIGHT_COMBO); updateNeighbor(child, screen, kRight); - child = GetDlgItem(hwnd, IDC_MAIN_SERVER_TOP_COMBO); + child = getItem(hwnd, IDC_MAIN_SERVER_TOP_COMBO); updateNeighbor(child, screen, kTop); - child = GetDlgItem(hwnd, IDC_MAIN_SERVER_BOTTOM_COMBO); + child = getItem(hwnd, IDC_MAIN_SERVER_BOTTOM_COMBO); updateNeighbor(child, screen, kBottom); } @@ -221,7 +233,7 @@ addScreen(HWND hwnd) UInt32 i = s_screens.size(); // add screen to list control - HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); CString item = CStringUtil::print("%d. %s", i + 1, info.m_screen.c_str()); SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); @@ -250,7 +262,7 @@ void editScreen(HWND hwnd) { // get selected list item - HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); if (index == LB_ERR) { // no selection @@ -311,7 +323,7 @@ void removeScreen(HWND hwnd) { // get selected list item - HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); if (index == LB_ERR) { // no selection @@ -341,7 +353,7 @@ void changeNeighbor(HWND hwnd, HWND combo, EDirection direction) { // get selected screen - HWND child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); if (index == LB_ERR) { // no selection @@ -430,7 +442,7 @@ getCommandLine(HWND hwnd, bool testing) const bool isClient = isClientChecked(hwnd); // get and verify screen name - HWND child = GetDlgItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); + HWND child = getItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); CString name = getWindowText(child); if (!s_config.isValidScreenName(name)) { showError(hwnd, CStringUtil::format( @@ -448,7 +460,7 @@ getCommandLine(HWND hwnd, bool testing) } // get and verify port - child = GetDlgItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT); + child = getItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT); CString portString = getWindowText(child); UInt32 port = (UInt32)atoi(portString.c_str()); if (port < 1 || port > 65535) { @@ -464,13 +476,19 @@ getCommandLine(HWND hwnd, bool testing) // prepare command line CString cmdLine; if (testing) { + // constant testing args cmdLine += " -z --no-restart --no-daemon"; + + // debug level testing arg + child = getItem(hwnd, IDC_MAIN_DEBUG); + cmdLine += " --debug "; + cmdLine += s_debugName[SendMessage(child, CB_GETCURSEL, 0, 0)][1]; } cmdLine += " --name "; cmdLine += name; if (isClient) { // check server name - child = GetDlgItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); CString server = getWindowText(child); if (!s_config.isValidScreenName(server)) { showError(hwnd, CStringUtil::format( @@ -633,6 +651,10 @@ initMainWindow(HWND hwnd) { CPlatform platform; + // append version number to title + CString titleFormat = getString(IDS_TITLE); + setWindowText(hwnd, CStringUtil::format(titleFormat.c_str(), VERSION)); + // load configuration bool configLoaded = loadConfig(s_config); s_oldConfig = s_config; @@ -640,17 +662,17 @@ initMainWindow(HWND hwnd) // choose client/server radio buttons HWND child; - child = GetDlgItem(hwnd, IDC_MAIN_CLIENT_RADIO); + child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); SendMessage(child, BM_SETCHECK, !configLoaded ? BST_CHECKED : BST_UNCHECKED, 0); - child = GetDlgItem(hwnd, IDC_MAIN_SERVER_RADIO); + child = getItem(hwnd, IDC_MAIN_SERVER_RADIO); SendMessage(child, BM_SETCHECK, configLoaded ? BST_CHECKED : BST_UNCHECKED, 0); // if config is loaded then initialize server controls if (configLoaded) { int i = 1; - child = GetDlgItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); + child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); for (CConfig::const_iterator index = s_config.begin(); index != s_config.end(); ++i, ++index) { s_screens.push_back(*index); @@ -662,13 +684,20 @@ initMainWindow(HWND hwnd) // initialize other controls char buffer[256]; sprintf(buffer, "%d", kDefaultPort); - child = GetDlgItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT); + child = getItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT); SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); CNetwork::gethostname(buffer, sizeof(buffer)); - child = GetDlgItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); + child = getItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); + child = getItem(hwnd, IDC_MAIN_DEBUG); + for (unsigned int i = 0; i < sizeof(s_debugName) / + sizeof(s_debugName[0]); ++i) { + SendMessage(child, CB_ADDSTRING, 0, (LPARAM)s_debugName[i][0]); + } + SendMessage(child, CB_SETCURSEL, s_defaultDebug, 0); + // update neighbor combo boxes enableMainWindowControls(hwnd); updateNeighbors(hwnd); @@ -686,7 +715,7 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) info = (CScreenInfo*)lParam; // fill in screen name - HWND child = GetDlgItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); SendMessage(child, WM_SETTEXT, 0, (LPARAM)info->m_screen.c_str()); // fill in aliases @@ -698,7 +727,7 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } aliases += *index; } - child = GetDlgItem(hwnd, IDC_ADD_ALIASES_EDIT); + child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); SendMessage(child, WM_SETTEXT, 0, (LPARAM)aliases.c_str()); return TRUE; @@ -711,9 +740,9 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) CStringList newAliases; // extract name and aliases - HWND child = GetDlgItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); newName = getWindowText(child); - child = GetDlgItem(hwnd, IDC_ADD_ALIASES_EDIT); + child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); tokenize(newAliases, getWindowText(child)); // name must be valid diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 60ff371c..af23a29a 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -1,269 +1,273 @@ -//Microsoft Developer Studio generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE DISCARDABLE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE DISCARDABLE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE DISCARDABLE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 262 -STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU -CAPTION "Synergy" -CLASS "GoSynergy" -FONT 8, "MS Sans Serif" -BEGIN - LTEXT "Choose to start either the Client or Server and provide the requested information. Then click Test to check your settings or Start to save your settings and start Synergy.", - IDC_STATIC,7,7,286,19 - GROUPBOX "",IDC_STATIC,7,29,286,31 - GROUPBOX "",IDC_STATIC,7,67,286,103 - GROUPBOX "Advanced Options",IDC_STATIC,7,177,286,56 - CONTROL "&Client",IDC_MAIN_CLIENT_RADIO,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,29,33,10 - CONTROL "Server",IDC_MAIN_SERVER_RADIO,"Button", - BS_AUTORADIOBUTTON,11,67,37,10 - LTEXT "Server &Host Name:",IDC_MAIN_CLIENT_SERVER_NAME_LABEL, - 12,41,61,8 - EDITTEXT IDC_MAIN_CLIENT_SERVER_NAME_EDIT,79,39,106,12, - ES_AUTOHSCROLL - LTEXT "&Screens:",IDC_MAIN_SERVER_SCREENS_LABEL,12,79,29,8 - LISTBOX IDC_MAIN_SERVER_SCREENS_LIST,12,91,106,36, - LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "&Add",IDC_MAIN_SERVER_ADD_BUTTON,12,132,50,14 - PUSHBUTTON "&Edit",IDC_MAIN_SERVER_EDIT_BUTTON,68,132,50,14 - PUSHBUTTON "&Remove",IDC_MAIN_SERVER_REMOVE_BUTTON,12,150,50,14 - LTEXT "&Layout:",IDC_MAIN_SERVER_LAYOUT_LABEL,138,79,24,8 - LTEXT "Left:",IDC_MAIN_SERVER_LEFT_LABEL,144,93,15,8 - COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,118,46, - CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Right:",IDC_MAIN_SERVER_RIGHT_LABEL,144,109,20,8 - COMBOBOX IDC_MAIN_SERVER_RIGHT_COMBO,175,107,118,46, - CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Above:",IDC_MAIN_SERVER_TOP_LABEL,144,125,24,8 - COMBOBOX IDC_MAIN_SERVER_TOP_COMBO,175,123,118,46, - CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Below:",IDC_MAIN_SERVER_BOTTOM_LABEL,144,141,22,8 - COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,118,46, - CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Screen &Name:",IDC_STATIC,12,192,46,8 - EDITTEXT IDC_MAIN_ADVANCED_NAME_EDIT,64,190,106,12,ES_AUTOHSCROLL - LTEXT "&Port:",IDC_STATIC,194,192,16,8 - EDITTEXT IDC_MAIN_ADVANCED_PORT_EDIT,216,190,40,12,ES_AUTOHSCROLL | - ES_NUMBER - LTEXT "Automatic Startup:",IDC_STATIC,12,212,59,8 - PUSHBUTTON "Con&figure...",IDC_MAIN_AUTOSTART,78,210,50,14 - PUSHBUTTON "Sa&ve",IDC_MAIN_SAVE,75,241,50,14 - DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,241,50,14 - PUSHBUTTON "Start",IDOK,187,241,50,14 - PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 -END - -IDD_ADD DIALOG DISCARDABLE 0, 0, 172, 95 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION -CAPTION "Add Screen" -FONT 8, "MS Sans Serif" -BEGIN - LTEXT "Screen Name:",IDC_STATIC,7,9,46,8 - EDITTEXT IDC_ADD_SCREEN_NAME_EDIT,59,7,106,12,ES_AUTOHSCROLL - LTEXT "Aliases:",IDC_STATIC,7,25,25,8 - EDITTEXT IDC_ADD_ALIASES_EDIT,59,26,106,40,ES_MULTILINE | - ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN - DEFPUSHBUTTON "OK",IDOK,59,74,50,14 - PUSHBUTTON "Cancel",IDCANCEL,115,74,50,14 -END - -IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION -CAPTION "Running Test..." -FONT 8, "MS Sans Serif" -BEGIN - DEFPUSHBUTTON "Stop",IDOK,129,33,50,14 - LTEXT "Running synergy. Press Stop to end the test.", - IDC_STATIC,7,7,172,15 -END - -IDD_AUTOSTART DIALOG DISCARDABLE 0, 0, 195, 189 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Auto Start" -FONT 8, "MS Sans Serif" -BEGIN - DEFPUSHBUTTON "Close",IDCANCEL,138,168,50,14 - LTEXT "Synergy can be configured to start automatically when you log in. If you have sufficient access rights, you can instead configure synergy to start automatically when your computer starts.", - IDC_STATIC,7,7,181,33 - LTEXT "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself.", - IDC_AUTOSTART_PERMISSION_MSG,7,69,181,17 - LTEXT "Synergy is configured to start automatically when the system starts.", - IDC_AUTOSTART_INSTALLED_MSG,7,93,181,17 - GROUPBOX "When &You Log In",IDC_STATIC,7,119,84,40 - PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_USER,23,133,50,14 - GROUPBOX "When &Computer Starts",IDC_STATIC,104,119,84,40 - PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_SYSTEM,119,134,50,14 - LTEXT "Synergy can be configured to start automatically when the computer starts or when you log in but not both.", - IDC_STATIC,7,43,181,17 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO DISCARDABLE -BEGIN - IDD_MAIN, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - TOPMARGIN, 7 - BOTTOMMARGIN, 255 - END - - IDD_ADD, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 165 - TOPMARGIN, 7 - BOTTOMMARGIN, 88 - END - - IDD_WAIT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - TOPMARGIN, 7 - BOTTOMMARGIN, 47 - END - - IDD_AUTOSTART, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 188 - TOPMARGIN, 7 - BOTTOMMARGIN, 182 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_SYNERGY ICON DISCARDABLE "synergy.ico" - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE DISCARDABLE -BEGIN - IDS_ERROR "Error" - IDS_INVALID_SCREEN_NAME "Screen name `%{1}' is invalid." - IDS_DUPLICATE_SCREEN_NAME "The screen name `%{1}' is already being used." - IDS_SCREEN_NAME_IS_ALIAS "A name may not be an alias of itself." - IDS_VERIFY "Confirm" - IDS_UNSAVED_DATA_REALLY_QUIT "You have unsaved changes. Really quit?" - IDS_UNKNOWN_SCREEN_NAME "The screen name `%{1}' is not in the layout." - IDS_INVALID_PORT "The port `%{1}' is invalid. It must be between 1 and 65535 inclusive. %{2} is the standard port." - IDS_SAVE_FAILED "Failed to save configuration: %{1}" - IDS_STARTUP_FAILED "Failed to start synergy: %{1}" - IDS_STARTED_TITLE "Started" - IDS_STARTED "Synergy was successfully started. Use the task manager to terminate it." - IDS_UNINSTALL_TITLE "Removed Auto-Start" -END - -STRINGTABLE DISCARDABLE -BEGIN - IDS_AUTOSTART_PERMISSION_SYSTEM - "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself." - IDS_AUTOSTART_PERMISSION_USER - "You have sufficient access rights to install and uninstall Auto Start for just yourself." - IDS_AUTOSTART_PERMISSION_NONE - "You do not have sufficient access rights to install or uninstall Auto Start." - IDS_AUTOSTART_INSTALLED_SYSTEM - "Synergy is configured to start automatically when the system starts." - IDS_AUTOSTART_INSTALLED_USER - "Synergy is configured to start automatically when you log in." - IDS_AUTOSTART_INSTALLED_NONE - "Synergy is not configured to start automatically." - IDS_INSTALL_LABEL "Install" - IDS_UNINSTALL_LABEL "Uninstall" - IDS_INSTALL_GENERIC_ERROR "Install failed for an unknown reason." - IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed for an unknown reason." - IDS_INSTALL_TITLE "Installed Auto-Start" - IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." - IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." -END - -STRINGTABLE DISCARDABLE -BEGIN - IDS_UNINSTALLED_SYSTEM "Removed auto-start. Synergy will not automatically start each time you start or reboot your computer." - IDS_UNINSTALLED_USER "Removed auto-start. Synergy will not automatically start each time you log in." - IDS_INVALID_SERVER_NAME "Server name `%{1}' is invalid." -END - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 262 +STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU +CAPTION "Synergy" +CLASS "GoSynergy" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Choose to start either the Client or Server and provide the requested information. Then click Test to check your settings or Start to save your settings and start Synergy.", + IDC_STATIC,7,7,286,19 + GROUPBOX "",IDC_STATIC,7,29,286,31 + GROUPBOX "",IDC_STATIC,7,67,286,103 + GROUPBOX "Advanced Options",IDC_STATIC,7,177,286,56 + CONTROL "&Client",IDC_MAIN_CLIENT_RADIO,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,29,33,10 + CONTROL "Server",IDC_MAIN_SERVER_RADIO,"Button", + BS_AUTORADIOBUTTON,11,67,37,10 + LTEXT "Server &Host Name:",IDC_MAIN_CLIENT_SERVER_NAME_LABEL, + 12,41,61,8 + EDITTEXT IDC_MAIN_CLIENT_SERVER_NAME_EDIT,79,39,106,12, + ES_AUTOHSCROLL + LTEXT "&Screens:",IDC_MAIN_SERVER_SCREENS_LABEL,12,79,29,8 + LISTBOX IDC_MAIN_SERVER_SCREENS_LIST,12,91,106,36, + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Add",IDC_MAIN_SERVER_ADD_BUTTON,12,132,50,14 + PUSHBUTTON "&Edit",IDC_MAIN_SERVER_EDIT_BUTTON,68,132,50,14 + PUSHBUTTON "&Remove",IDC_MAIN_SERVER_REMOVE_BUTTON,12,150,50,14 + LTEXT "&Layout:",IDC_MAIN_SERVER_LAYOUT_LABEL,138,79,24,8 + LTEXT "Left:",IDC_MAIN_SERVER_LEFT_LABEL,144,93,15,8 + COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,118,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Right:",IDC_MAIN_SERVER_RIGHT_LABEL,144,109,20,8 + COMBOBOX IDC_MAIN_SERVER_RIGHT_COMBO,175,107,118,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Above:",IDC_MAIN_SERVER_TOP_LABEL,144,125,24,8 + COMBOBOX IDC_MAIN_SERVER_TOP_COMBO,175,123,118,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Below:",IDC_MAIN_SERVER_BOTTOM_LABEL,144,141,22,8 + COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,118,46, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Screen &Name:",IDC_STATIC,12,192,46,8 + EDITTEXT IDC_MAIN_ADVANCED_NAME_EDIT,64,190,106,12,ES_AUTOHSCROLL + LTEXT "&Port:",IDC_STATIC,194,192,16,8 + EDITTEXT IDC_MAIN_ADVANCED_PORT_EDIT,216,190,40,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "Automatic Startup:",IDC_STATIC,12,212,59,8 + PUSHBUTTON "Con&figure...",IDC_MAIN_AUTOSTART,78,210,50,14 + LTEXT "Test &Debug Level:",IDC_STATIC,151,212,60,8 + COMBOBOX IDC_MAIN_DEBUG,216,210,61,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Sa&ve",IDC_MAIN_SAVE,75,241,50,14 + DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,241,50,14 + PUSHBUTTON "Start",IDOK,187,241,50,14 + PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 +END + +IDD_ADD DIALOG DISCARDABLE 0, 0, 172, 95 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "Add Screen" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Screen Name:",IDC_STATIC,7,9,46,8 + EDITTEXT IDC_ADD_SCREEN_NAME_EDIT,59,7,106,12,ES_AUTOHSCROLL + LTEXT "Aliases:",IDC_STATIC,7,25,25,8 + EDITTEXT IDC_ADD_ALIASES_EDIT,59,26,106,40,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "OK",IDOK,59,74,50,14 + PUSHBUTTON "Cancel",IDCANCEL,115,74,50,14 +END + +IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "Running Test..." +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Stop",IDOK,129,33,50,14 + LTEXT "Running synergy. Press Stop to end the test.", + IDC_STATIC,7,7,172,15 +END + +IDD_AUTOSTART DIALOG DISCARDABLE 0, 0, 195, 189 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Auto Start" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDCANCEL,138,168,50,14 + LTEXT "Synergy can be configured to start automatically when you log in. If you have sufficient access rights, you can instead configure synergy to start automatically when your computer starts.", + IDC_STATIC,7,7,181,33 + LTEXT "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself.", + IDC_AUTOSTART_PERMISSION_MSG,7,69,181,17 + LTEXT "Synergy is configured to start automatically when the system starts.", + IDC_AUTOSTART_INSTALLED_MSG,7,93,181,17 + GROUPBOX "When &You Log In",IDC_STATIC,7,119,84,40 + PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_USER,23,133,50,14 + GROUPBOX "When &Computer Starts",IDC_STATIC,104,119,84,40 + PUSHBUTTON "Install",IDC_AUTOSTART_INSTALL_SYSTEM,119,134,50,14 + LTEXT "Synergy can be configured to start automatically when the computer starts or when you log in but not both.", + IDC_STATIC,7,43,181,17 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_MAIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 255 + END + + IDD_ADD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 165 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END + + IDD_WAIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 47 + END + + IDD_AUTOSTART, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 188 + TOPMARGIN, 7 + BOTTOMMARGIN, 182 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_SYNERGY ICON DISCARDABLE "synergy.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_ERROR "Error" + IDS_INVALID_SCREEN_NAME "Screen name `%{1}' is invalid." + IDS_DUPLICATE_SCREEN_NAME "The screen name `%{1}' is already being used." + IDS_SCREEN_NAME_IS_ALIAS "A name may not be an alias of itself." + IDS_VERIFY "Confirm" + IDS_UNSAVED_DATA_REALLY_QUIT "You have unsaved changes. Really quit?" + IDS_UNKNOWN_SCREEN_NAME "The screen name `%{1}' is not in the layout." + IDS_INVALID_PORT "The port `%{1}' is invalid. It must be between 1 and 65535 inclusive. %{2} is the standard port." + IDS_SAVE_FAILED "Failed to save configuration: %{1}" + IDS_STARTUP_FAILED "Failed to start synergy: %{1}" + IDS_STARTED_TITLE "Started" + IDS_STARTED "Synergy was successfully started. Use the task manager to terminate it." + IDS_UNINSTALL_TITLE "Removed Auto-Start" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_AUTOSTART_PERMISSION_SYSTEM + "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself." + IDS_AUTOSTART_PERMISSION_USER + "You have sufficient access rights to install and uninstall Auto Start for just yourself." + IDS_AUTOSTART_PERMISSION_NONE + "You do not have sufficient access rights to install or uninstall Auto Start." + IDS_AUTOSTART_INSTALLED_SYSTEM + "Synergy is configured to start automatically when the system starts." + IDS_AUTOSTART_INSTALLED_USER + "Synergy is configured to start automatically when you log in." + IDS_AUTOSTART_INSTALLED_NONE + "Synergy is not configured to start automatically." + IDS_INSTALL_LABEL "Install" + IDS_UNINSTALL_LABEL "Uninstall" + IDS_INSTALL_GENERIC_ERROR "Install failed for an unknown reason." + IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed for an unknown reason." + IDS_INSTALL_TITLE "Installed Auto-Start" + IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." + IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_UNINSTALLED_SYSTEM "Removed auto-start. Synergy will not automatically start each time you start or reboot your computer." + IDS_UNINSTALLED_USER "Removed auto-start. Synergy will not automatically start each time you log in." + IDS_INVALID_SERVER_NAME "Server name `%{1}' is invalid." + IDS_TITLE "Synergy - Version %{1}" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index 93c263cf..f02d00ff 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -36,6 +36,7 @@ #define IDS_UNINSTALLED_SYSTEM 32 #define IDS_UNINSTALLED_USER 33 #define IDS_INVALID_SERVER_NAME 34 +#define IDS_TITLE 35 #define IDD_MAIN 101 #define IDD_ADD 102 #define IDD_WAIT 103 @@ -71,6 +72,7 @@ #define IDC_AUTOSTART_INSTALL_USER 1033 #define IDC_AUTOSTART_INSTALL_SYSTEM 1034 #define IDC_MAIN_AUTOSTART 1035 +#define IDC_MAIN_DEBUG 1036 // Next default values for new objects // @@ -79,7 +81,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 106 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1036 +#define _APS_NEXT_CONTROL_VALUE 1037 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif From 4586f8818805a7aa4ca019323f92d6333bb1a57e Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Sep 2002 12:07:02 +0000 Subject: [PATCH 353/807] Rewrote handling of key press on X11 client; it should be much more robust now. Also added handling of Super modifier key and changed windows keys to map to Super instead of Meta, which is the default on my keyboard. --- lib/client/CMSWindowsSecondaryScreen.cpp | 16 +- lib/client/CXWindowsSecondaryScreen.cpp | 660 +++++++++++++++-------- lib/client/CXWindowsSecondaryScreen.h | 23 +- lib/server/CMSWindowsPrimaryScreen.cpp | 4 +- lib/server/CXWindowsPrimaryScreen.cpp | 8 + lib/server/CXWindowsPrimaryScreen.h | 1 + lib/synergy/KeyTypes.h | 3 +- 7 files changed, 488 insertions(+), 227 deletions(-) diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index 974a189a..a42f3763 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -478,8 +478,9 @@ CMSWindowsSecondaryScreen::updateKeys() if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) { m_mask |= KeyModifierAlt; } + // note -- no keys for KeyModifierMeta if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) { - m_mask |= KeyModifierMeta; + m_mask |= KeyModifierSuper; } if ((m_keys[VK_CAPITAL] & 0x01) != 0) { m_mask |= KeyModifierCapsLock; @@ -583,8 +584,9 @@ static const UINT g_mapEF00[] = /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, - /* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, VK_LWIN|0x100, - /* 0xe8 */ VK_RWIN|0x100, VK_LMENU, VK_RMENU|0x100, 0, 0, 0, 0, 0, + /* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, 0, + /* 0xe8 */ 0, VK_LMENU, VK_RMENU|0x100, VK_LWIN|0x100, + /* 0xec */ VK_RWIN|0x100, 0, 0, 0, /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100 }; @@ -655,7 +657,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // get output mask. default output mask carries over the current // toggle modifier states and includes desired shift, control, alt, - // and meta states. + // meta, and super states. KeyModifierMask outMask = (m_mask & (KeyModifierCapsLock | KeyModifierNumLock | @@ -664,7 +666,8 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, (KeyModifierShift | KeyModifierControl | KeyModifierAlt | - KeyModifierMeta)); + KeyModifierMeta | + KeyModifierSuper)); // set control and alt if mode shift (AltGr) is requested if ((mask & KeyModifierModeSwitch) != 0) { @@ -760,7 +763,8 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, { KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false }, { KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false }, - { KeyModifierMeta, VK_LWIN | 0x100, VK_RWIN | 0x100, false }, + // note -- no keys for KeyModifierMeta + { KeyModifierSuper, VK_LWIN | 0x100, VK_RWIN | 0x100, false }, { KeyModifierCapsLock, VK_CAPITAL, 0, true }, { KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true }, { KeyModifierScrollLock,VK_SCROLL, 0, true } diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index 3f6c2ee3..4cb71fba 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -36,6 +36,47 @@ # endif #endif +// +// utility functions +// + +inline +static +unsigned int getBits(unsigned int src, unsigned int mask) +{ + return src & mask; +} + +inline +static +unsigned int setBits(unsigned int src, unsigned int mask) +{ + return src | mask; +} + +inline +static +unsigned int clearBits(unsigned int src, unsigned int mask) +{ + return src & ~mask; +} + +inline +static +unsigned int flipBits(unsigned int src, unsigned int mask) +{ + return src ^ mask; +} + +inline +static +unsigned int assignBits(unsigned int src, + unsigned int mask, unsigned int value) +{ + return setBits(clearBits(src, mask), clearBits(value, ~mask)); +} + + // // CXWindowsSecondaryScreen // @@ -406,27 +447,154 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, return m_mask; } - // lookup the a keycode for this key id. also return the - // key modifier mask required. - unsigned int outMask; - if (!findKeyCode(keycode, outMask, id, maskToX(mask))) { - // we cannot generate the desired keysym because no key - // maps to that keysym. just return the current mask. - log((CLOG_DEBUG2 "no keycode for KeyID %d modifiers 0x%04x", id, mask)); + // convert the id to a keysym and adjust the mask if necessary + unsigned int outMask = m_mask; + KeyCodeIndex keyIndex = findKey(id, outMask); + if (keyIndex == noKey()) { + // cannot convert id to keysym return m_mask; } - log((CLOG_DEBUG2 "keysym %d -> KeyID %d modifiers 0x%04x", id, keycode, outMask)); - // if we cannot match the modifier mask then don't return any - // keys and just return the current mask. - if ((outMask & m_modifierMask) != outMask) { - log((CLOG_DEBUG2 "cannot match modifiers to mask 0x%04x", m_modifierMask)); + // get the keysym we're trying to generate and possible keycodes + KeySym keysym = keyIndex->first; + const KeyCodeMask& entry = keyIndex->second; + + // we can choose any of the available keycode/modifier states to + // generate our keysym. the most desireable is the one most + // closely matching the input mask. determine the order we + // should try modifier states, from best match to worst. this + // doesn't concern itself with whether or not a given modifier + // state has an associated keycode. we'll just skip those later + // if necessary. + + // default is none, shift, mode switch, shift + mode switch + unsigned int desired = maskToX(mask); + unsigned int index[4]; + index[0] = 0; + index[1] = 1; + index[2] = 2; + index[3] = 3; + + // if mode switch is active then 2 and 3 are better than 0 and 1 + if (getBits(desired, m_modeSwitchMask) != 0) { + index[0] ^= 2; + index[1] ^= 2; + index[2] ^= 2; + index[3] ^= 2; + } + + // if shift is active then 1 and 3 are better than 0 and 2. however, + // if the key is affected by NumLock and NumLock is active then 1 and + // 3 are better if shift is *not* down (because NumLock acts like + // shift for those keysyms and shift cancels NumLock). similarly for + // keys affected by CapsLock. + bool desireShift = (getBits(desired, ShiftMask) != 0); + bool invertShift = false; +log((CLOG_DEBUG1 "desire shift 1: %s", desireShift?"yes":"no")); + if (adjustForNumLock(keysym)) { +log((CLOG_DEBUG1 "num lock sensitive")); + if (m_numLockMask != 0) { +log((CLOG_DEBUG1 "we have num lock")); + if (getBits(desired, m_numLockMask) != 0) { +log((CLOG_DEBUG1 "num lock desired, invert shift")); + invertShift = true; + } + } + } + else if (adjustForCapsLock(keysym)) { +log((CLOG_DEBUG1 "caps lock sensitive")); + if (m_capsLockMask != 0) { +log((CLOG_DEBUG1 "we have caps lock")); + if (getBits(desired, m_capsLockMask) != 0) { +log((CLOG_DEBUG1 "caps lock desired, invert shift")); + invertShift = true; + } + } + } +log((CLOG_DEBUG1 "desire shift 2: %s", desireShift?"yes":"no")); + if (desireShift != invertShift) { + index[0] ^= 1; + index[1] ^= 1; + index[2] ^= 1; + index[3] ^= 1; + } + + // find the first modifier state with a keycode we can generate. + // note that if m_modeSwitchMask is 0 then we can't generate + // m_keycode[2] and m_keycode[3]. + unsigned int bestIndex; + for (bestIndex = 0; bestIndex < 4; ++bestIndex) { + if (entry.m_keycode[index[bestIndex]] != 0) { + if (index[bestIndex] < 2 || m_modeSwitchMask != 0) { + bestIndex = index[bestIndex]; + break; + } + } + } + if (bestIndex == 4) { + // no keycode/modifiers to generate the keysym return m_mask; } + // get the keycode + keycode = entry.m_keycode[bestIndex]; +log((CLOG_DEBUG1 "bestIndex = %d, keycode = %d", bestIndex, keycode)); + // note if the key is a modifier - ModifierMap::const_iterator index = m_keycodeToModifier.find(keycode); - const bool isModifier = (index != m_keycodeToModifier.end()); + ModifierMap::const_iterator modIndex = m_keycodeToModifier.find(keycode); + unsigned int modifierBit = 0; + if (modIndex != m_keycodeToModifier.end()) { + modifierBit = (1 << modIndex->second); + } + + // if the key is a modifier and that modifier is already in the + // desired state then ignore the request since there's nothing + // to do. never ignore a toggle modifier on press or release, + // though. + if (modifierBit != 0) { + if (action == kRepeat) { +log((CLOG_DEBUG1 "ignore repeating modifier")); + return m_mask; + } + if (getBits(m_toggleModifierMask, modifierBit) == 0) { + if ((action == kPress && (m_mask & modifierBit) != 0) || + (action == kRelease && (m_mask & modifierBit) == 0)) { +log((CLOG_DEBUG1 "modifier in proper state: 0x%04x", m_mask)); + return m_mask; + } + } + } + + // bestIndex tells us if shift and mode switch should be on or off, + // except if caps lock or num lock was down then we invert the sense + // of bestIndex's lowest bit. + // we must match both. + unsigned int required = ShiftMask | m_modeSwitchMask; + if (((bestIndex & 1) == 0) != invertShift) { + desired = clearBits(desired, ShiftMask); + } + else { + desired = setBits(desired, ShiftMask); + } + if ((bestIndex & 2) == 0) { + desired = clearBits(desired, m_modeSwitchMask); + } + else { + desired = setBits(desired, m_modeSwitchMask); + } + + // if the key is a modifier then remove it from the desired mask. + // we'll be matching the modifiers in the desired mask then adding + // a key press or release for the keysym. if we don't clear the + // modifier bit from the desired mask we'll end up dealing with + // that key twice, once while matching modifiers and once while + // handling the keysym. + // + // note that instead of clearing the bit, we make it identical to + // the same bit in m_mask, meaning it's already in the right state. + desired = assignBits(desired, modifierBit, m_mask); + required = clearBits(required, modifierBit); +log((CLOG_DEBUG1 "desired = 0x%04x, current = 0x%04x", desired, m_mask)); // add the key events required to get to the modifier state // necessary to generate an event yielding id. also save the @@ -435,33 +603,35 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // modify modifiers. Keystrokes undo; Keystroke keystroke; - if (outMask != m_mask && !isModifier) { + if (desired != m_mask) { for (unsigned int i = 0; i < 8; ++i) { unsigned int bit = (1 << i); - if ((outMask & bit) != (m_mask & bit)) { - // get list of keycodes for the modifier. if there isn't - // one then there's no key mapped to this modifier and we - // can't generate the desired key so bail. - const KeyCode* modifierKeys = - &m_modifierToKeycode[i * m_keysPerModifier]; - KeyCode modifierKey = modifierKeys[0]; - if (modifierKey == 0) { - modifierKey = modifierKeys[1]; - } + if (getBits(desired, bit) != getBits(m_mask, bit)) { +log((CLOG_DEBUG1 "fix modifier %d", i)); + // get the keycode we're using for this modifier. if + // there isn't one then bail if the modifier is required + // or ignore it if not required. + KeyCode modifierKey = m_modifierToKeycode[i]; if (modifierKey == 0) { log((CLOG_DEBUG1 "no key mapped to modifier 0x%04x", bit)); - return m_mask; + if (getBits(required, bit) != 0) { + keys.clear(); + return m_mask; + } + else { + continue; + } } keystroke.m_keycode = modifierKey; keystroke.m_repeat = false; - if ((outMask & bit) != 0) { + if (getBits(desired, bit)) { // modifier is not active but should be. if the // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a // press. use the first keycode for the modifier. log((CLOG_DEBUG2 "modifier 0x%04x is not active", bit)); - if ((bit & m_toggleModifierMask) != 0) { + if (getBits(m_toggleModifierMask, bit) != 0) { log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || (bit == m_numLockMask && m_numLockHalfDuplex)) { @@ -495,7 +665,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // release. we must check each keycode for the // modifier if not a toggle. log((CLOG_DEBUG2 "modifier 0x%04x is active", bit)); - if ((bit & m_toggleModifierMask) != 0) { + if (getBits(m_toggleModifierMask, bit) != 0) { log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || (bit == m_numLockMask && m_numLockHalfDuplex)) { @@ -516,7 +686,8 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, } else { for (unsigned int j = 0; j < m_keysPerModifier; ++j) { - const KeyCode key = modifierKeys[j]; + const KeyCode key = + m_modifierToKeycodes[i * m_keysPerModifier + j]; if (key != 0 && m_keys[key]) { keystroke.m_keycode = key; keystroke.m_press = False; @@ -532,7 +703,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, } // note if the press of a half-duplex key should be treated as a release - if (isHalfDuplex && (m_mask & (1 << index->second)) != 0) { + if (isHalfDuplex && getBits(m_mask, modifierBit) != 0) { action = kRelease; } @@ -568,197 +739,44 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, } // if the key is a modifier key then compute the modifier map after - // this key is pressed or released. if repeating then ignore. + // this key is pressed or released. mask = m_mask; - if (isModifier && action != kRepeat) { - // get modifier - const unsigned int modifierBit = (1 << index->second); + if (modifierBit != 0) { + // can't be repeating if we've gotten here + assert(action != kRepeat); // toggle keys modify the state on release. other keys set the // bit on press and clear the bit on release. if half-duplex // then toggle each time we get here. - if ((modifierBit & m_toggleModifierMask) != 0) { + if (getBits(m_toggleModifierMask, modifierBit) != 0) { if (isHalfDuplex || action == kRelease) { - mask ^= modifierBit; + mask = flipBits(mask, modifierBit); } } else if (action == kPress) { - mask |= modifierBit; + mask = setBits(mask, modifierBit); } - else { + else if (action == kRelease) { // can't reset bit until all keys that set it are released. // scan those keys to see if any (except keycode) are pressed. bool down = false; - const KeyCode* modifierKeys = &m_modifierToKeycode[ - index->second * m_keysPerModifier]; for (unsigned int j = 0; !down && j < m_keysPerModifier; ++j) { - if (modifierKeys[j] != 0 && m_keys[modifierKeys[j]]) - down = true; + KeyCode modKeycode = m_modifierToKeycodes[modIndex->second * + m_keysPerModifier + j]; + if (modKeycode != 0 && modKeycode != keycode) { + down = m_keys[modKeycode]; + } + } + if (!down) { + mask = clearBits(mask, modifierBit); } - if (!down) - mask &= ~modifierBit; } } +log((CLOG_DEBUG1 "final mask: 0x%04x", mask)); return mask; } -bool -CXWindowsSecondaryScreen::findKeyCode(KeyCode& keycode, - unsigned int& maskOut, KeyID id, unsigned int maskIn) const -{ - // convert id to keysym - KeySym keysym = 0; - switch (id & 0xffffff00) { - case 0x0000: - // Latin-1 - keysym = static_cast(id); - break; - - case 0xee00: - // ISO 9995 Function and Modifier Keys - if (id == kKeyLeftTab) { - keysym = XK_ISO_Left_Tab; - } - break; - - case 0xef00: - // MISCELLANY - keysym = static_cast(id - 0xef00 + 0xff00); - break; - } - - // fail if unknown key - if (keysym == 0) { - return false; - } - - // if kKeyTab is requested with shift active then try XK_ISO_Left_Tab - // instead. if that doesn't work, we'll fall back to XK_Tab with - // shift active. this is to handle primary screens that don't map - // XK_ISO_Left_Tab sending events to secondary screens that do. - if (keysym == XK_Tab && (maskIn & ShiftMask) != 0) { - keysym = XK_ISO_Left_Tab; - maskIn &= ~ShiftMask; - } - - // find a keycode to generate id. XKeysymToKeycode() almost does - // what we need but won't tell us which index to use with the - // keycode. return false if there's no keycode to generate id. - KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym); - if (index == m_keycodeMap.end()) { - // try backup keysym for certain keys (particularly the numpad - // keys since most laptops don't have a separate numpad and the - // numpad overlaying the main keyboard may not have movement - // key bindings). - switch (keysym) { - case XK_KP_Home: - keysym = XK_Home; - break; - - case XK_KP_Left: - keysym = XK_Left; - break; - - case XK_KP_Up: - keysym = XK_Up; - break; - - case XK_KP_Right: - keysym = XK_Right; - break; - - case XK_KP_Down: - keysym = XK_Down; - break; - - case XK_KP_Prior: - keysym = XK_Prior; - break; - - case XK_KP_Next: - keysym = XK_Next; - break; - - case XK_KP_End: - keysym = XK_End; - break; - - case XK_KP_Insert: - keysym = XK_Insert; - break; - - case XK_KP_Delete: - keysym = XK_Delete; - break; - - case XK_ISO_Left_Tab: - keysym = XK_Tab; - maskIn |= ShiftMask; - break; - - default: - return false; - } - - index = m_keycodeMap.find(keysym); - if (index == m_keycodeMap.end()) { - return false; - } - } - - // save the keycode - keycode = index->second.m_keycode; - - // compute output mask. that's the set of modifiers that need to - // be enabled when the keycode event is encountered in order to - // generate the keysym and match maskIn. it's possible that - // maskIn wants, say, a shift key to be down but that would make - // it impossible to generate the keysym. in that case we must - // override maskIn. this is complicated by caps/shift-lock and - // num-lock. - maskOut = (maskIn & ~index->second.m_keyMaskMask); - log((CLOG_DEBUG2 "maskIn(0x%04x) & ~maskMask(0x%04x) -> 0x%04x", maskIn, index->second.m_keyMaskMask, maskOut)); - if (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)) { - if ((m_mask & m_numLockMask) != 0) { - maskOut &= ~index->second.m_keyMask; - maskOut |= m_numLockMask; - log((CLOG_DEBUG2 "keypad key: & ~mask(0x%04x) | numLockMask(0x%04x) -> 0x%04x", index->second.m_keyMask, m_numLockMask, maskOut)); - } - else { - maskOut |= index->second.m_keyMask; - maskOut &= ~m_numLockMask; - log((CLOG_DEBUG2 "keypad key: | mask(0x%04x) & ~numLockMask(0x%04x) -> 0x%04x", index->second.m_keyMask, m_numLockMask, maskOut)); - } - } - else { - unsigned int maskShift = (index->second.m_keyMask & ShiftMask); - log((CLOG_DEBUG2 "maskShift = 0x%04x", maskShift)); - if (maskShift != 0 && (m_mask & m_capsLockMask) != 0) { - // shift and capsLock cancel out for keysyms subject to - // case conversion but not for keys with shifted - // characters that are not case conversions. see if - // case conversion is necessary. - KeySym lKey, uKey; - XConvertCase(keysym, &lKey, &uKey); - if (lKey != uKey) { - log((CLOG_DEBUG2 "case convertable, shift && capsLock -> caps lock")); - maskShift = m_capsLockMask; - } - else { - log((CLOG_DEBUG2 "case unconvertable, shift && capsLock -> shift, caps lock")); - maskShift |= m_capsLockMask; - } - } - log((CLOG_DEBUG2 "maskShift = 0x%04x", maskShift)); - maskOut |= maskShift; - maskOut |= (index->second.m_keyMask & ~(ShiftMask | LockMask)); - log((CLOG_DEBUG2 "| maskShift(0x%04x) | other (0x%04x) -> 0x%04x", maskShift, (index->second.m_keyMask & ~(ShiftMask | LockMask)), maskOut)); - } - - return true; -} - void CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) { @@ -817,6 +835,9 @@ CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const if (inMask & KeyModifierMeta) { outMask |= m_metaMask; } + if (inMask & KeyModifierSuper) { + outMask |= m_superMask; + } if (inMask & KeyModifierModeSwitch) { outMask |= m_modeSwitchMask; } @@ -902,7 +923,7 @@ CXWindowsSecondaryScreen::updateModifiers(Display* display) const unsigned int bit = (1 << i); if ((bit & m_toggleModifierMask) == 0) { for (unsigned int j = 0; j < m_keysPerModifier; ++j) { - if (m_keys[m_modifierToKeycode[i * m_keysPerModifier + j]]) + if (m_keys[m_modifierToKeycodes[i * m_keysPerModifier + j]]) m_mask |= bit; } } @@ -917,7 +938,10 @@ void CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) { // there are up to 4 keysyms per keycode - static const int maxKeysyms = 4; + static const unsigned int maxKeysyms = 4; + + // table for counting 1 bits + static const int s_numBits[maxKeysyms] = { 0, 1, 1, 2 }; // get the number of keycodes int minKeycode, maxKeycode; @@ -931,24 +955,71 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) &keysymsPerKeycode); // we only understand up to maxKeysyms keysyms per keycodes - int numKeysyms = keysymsPerKeycode; + unsigned int numKeysyms = keysymsPerKeycode; if (numKeysyms > maxKeysyms) { numKeysyms = maxKeysyms; } // initialize - KeyCodeMask entry; +// KeyCodeMask entry; m_keycodeMap.clear(); // insert keys for (int i = 0; i < numKeycodes; ++i) { + // compute mask over all mapped keysyms. if a keycode has, say, + // no shifted keysym then we can ignore the shift state when + // synthesizing an event to generate it. + unsigned int globalMask = 0; + for (unsigned int j = 0; j < numKeysyms; ++j) { + const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; + if (keysym != NoSymbol) { + globalMask |= j; + } + } + + // map each keysym to it's keycode/modifier mask + for (unsigned int j = 0; j < numKeysyms; ++j) { + // get keysym + KeySym keysym = keysyms[i * keysymsPerKeycode + j]; + + // get modifier mask required for this keysym. note that + // a keysym of NoSymbol means that a keysym using fewer + // modifiers would be generated using these modifiers. + // for example, given + // keycode 86 = KP_Add + // then we'll generate KP_Add regardless of the modifiers. + // we add an entry for that keysym for these modifiers. + unsigned int index = j; + if (keysym == NoSymbol && (index == 1 || index == 3)) { + // shift doesn't matter + index = index - 1; + keysym = keysyms[i * keysymsPerKeycode + index]; + } + if (keysym == NoSymbol && index == 2) { + // mode switch doesn't matter + index = 0; + keysym = keysyms[i * keysymsPerKeycode + index]; + } + if (keysym == NoSymbol && index == 0) { + // no symbols at all for this keycode + continue; + } + + // look it up, creating a new entry if necessary + KeyCodeMask& entry = m_keycodeMap[keysym]; + + // save keycode for keysym and modifiers + entry.m_keycode[j] = static_cast(minKeycode + i); + } + +/* // compute mask over all mapped keysyms. if a keycode has, say, // no shifted keysym then we can ignore the shift state when // synthesizing an event to generate it. entry.m_keyMaskMask = 0; for (int j = 0; j < numKeysyms; ++j) { - const KeySym keySym = keysyms[i * keysymsPerKeycode + j]; - if (keySym != NoSymbol) { + const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; + if (keysym != NoSymbol) { entry.m_keyMaskMask |= indexToModifierMask(j); } } @@ -956,12 +1027,15 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) // add entries for this keycode entry.m_keycode = static_cast(minKeycode + i); for (int j = 0; j < numKeysyms; ++j) { - const KeySym keySym = keysyms[i * keysymsPerKeycode + j]; - if (keySym != NoSymbol) { - entry.m_keyMask = indexToModifierMask(j) & ~LockMask; - m_keycodeMap.insert(std::make_pair(keySym, entry)); + const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; + if (keysym != NoSymbol) { +// FIXME +// entry.m_keyMask = indexToModifierMask(j) & ~LockMask; +entry.m_keyMask = 0; + m_keycodeMap[i].insert(std::make_pair(keysym, entry)); } } +*/ } // clean up @@ -999,13 +1073,16 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display) m_toggleModifierMask = 0; m_altMask = 0; m_metaMask = 0; + m_superMask = 0; m_modeSwitchMask = 0; m_numLockMask = 0; m_capsLockMask = 0; m_scrollLockMask = 0; m_keysPerModifier = keymap->max_keypermod; m_modifierToKeycode.clear(); - m_modifierToKeycode.resize(8 * m_keysPerModifier); + m_modifierToKeycode.resize(8); + m_modifierToKeycodes.clear(); + m_modifierToKeycodes.resize(8 * m_keysPerModifier); // set keycodes and masks for (unsigned int i = 0; i < 8; ++i) { @@ -1014,13 +1091,18 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display) KeyCode keycode = keymap->modifiermap[i * m_keysPerModifier + j]; // save in modifier to keycode - m_modifierToKeycode[i * m_keysPerModifier + j] = keycode; + m_modifierToKeycodes[i * m_keysPerModifier + j] = keycode; // no further interest in unmapped modifier if (keycode == 0) { continue; } + // save keycode for modifier if we don't have one yet + if (m_modifierToKeycode[i] == 0) { + m_modifierToKeycode[i] = keycode; + } + // save in keycode to modifier m_keycodeToModifier.insert(std::make_pair(keycode, i)); @@ -1045,6 +1127,11 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display) m_metaMask |= bit; break; + case XK_Super_L: + case XK_Super_R: + m_superMask |= bit; + break; + case XK_Mode_switch: m_modeSwitchMask |= bit; break; @@ -1075,7 +1162,8 @@ CXWindowsSecondaryScreen::toggleKey(Display* display, if (index == m_keycodeMap.end()) { return; } - KeyCode keycode = index->second.m_keycode; + // FIXME -- which keycode? + KeyCode keycode = index->second.m_keycode[0]; // toggle the key if ((keysym == XK_Caps_Lock && m_capsLockHalfDuplex) || @@ -1107,3 +1195,147 @@ CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) return false; } } + +CXWindowsSecondaryScreen::KeyCodeIndex +CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask& mask) const +{ + // convert id to keysym + KeySym keysym = NoSymbol; + switch (id & 0xffffff00) { + case 0x0000: + // Latin-1 + keysym = static_cast(id); + break; + + case 0xee00: + // ISO 9995 Function and Modifier Keys + if (id == kKeyLeftTab) { + keysym = XK_ISO_Left_Tab; + } + break; + + case 0xef00: + // MISCELLANY + keysym = static_cast(id - 0xef00 + 0xff00); + break; + } + + // fail if unknown key + if (keysym == NoSymbol) { + return m_keycodeMap.end(); + } + + // if kKeyTab is requested with shift active then try XK_ISO_Left_Tab + // instead. if that doesn't work, we'll fall back to XK_Tab with + // shift active. this is to handle primary screens that don't map + // XK_ISO_Left_Tab sending events to secondary screens that do. + if (keysym == XK_Tab && (mask & ShiftMask) != 0) { + keysym = XK_ISO_Left_Tab; + mask &= ~ShiftMask; + } + + // find the keycodes that generate the keysym + KeyCodeIndex index = m_keycodeMap.find(keysym); + if (index == noKey()) { + // try backup keysym for certain keys (particularly the numpad + // keys since most laptops don't have a separate numpad and the + // numpad overlaying the main keyboard may not have movement + // key bindings). + switch (keysym) { + case XK_KP_Home: + keysym = XK_Home; + break; + + case XK_KP_Left: + keysym = XK_Left; + break; + + case XK_KP_Up: + keysym = XK_Up; + break; + + case XK_KP_Right: + keysym = XK_Right; + break; + + case XK_KP_Down: + keysym = XK_Down; + break; + + case XK_KP_Prior: + keysym = XK_Prior; + break; + + case XK_KP_Next: + keysym = XK_Next; + break; + + case XK_KP_End: + keysym = XK_End; + break; + + case XK_KP_Insert: + keysym = XK_Insert; + break; + + case XK_KP_Delete: + keysym = XK_Delete; + break; + + case XK_ISO_Left_Tab: + keysym = XK_Tab; + mask |= ShiftMask; + break; + + default: + return index; + } + + index = m_keycodeMap.find(keysym); + } + + return index; +} + +CXWindowsSecondaryScreen::KeyCodeIndex +CXWindowsSecondaryScreen::noKey() const +{ + return m_keycodeMap.end(); +} + +bool +CXWindowsSecondaryScreen::adjustForNumLock(KeySym keysym) const +{ + if (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)) { + // it's NumLock sensitive + log((CLOG_DEBUG2 "keypad key: NumLock %s", ((m_mask & m_numLockMask) != 0) ? "active" : "inactive")); + return true; + } + return false; +} + +bool +CXWindowsSecondaryScreen::adjustForCapsLock(KeySym keysym) const +{ + KeySym lKey, uKey; + XConvertCase(keysym, &lKey, &uKey); + if (lKey != uKey) { + // it's CapsLock sensitive + log((CLOG_DEBUG2 "case convertible: CapsLock %s", ((m_mask & m_capsLockMask) != 0) ? "active" : "inactive")); + return true; + } + return false; +} + + +// +// CXWindowsSecondaryScreen::KeyCodeMask +// + +CXWindowsSecondaryScreen::KeyCodeMask::KeyCodeMask() +{ + m_keycode[0] = 0; + m_keycode[1] = 0; + m_keycode[2] = 0; + m_keycode[3] = 0; +} diff --git a/lib/client/CXWindowsSecondaryScreen.h b/lib/client/CXWindowsSecondaryScreen.h index 5dc08c0b..d055016b 100644 --- a/lib/client/CXWindowsSecondaryScreen.h +++ b/lib/client/CXWindowsSecondaryScreen.h @@ -70,9 +70,12 @@ private: enum EKeyAction { kPress, kRelease, kRepeat }; class KeyCodeMask { public: - KeyCode m_keycode; - unsigned int m_keyMask; - unsigned int m_keyMaskMask; + KeyCodeMask(); + public: + KeyCode m_keycode[4]; + // FIXME -- don't need masks + unsigned int m_keyMask[4]; + unsigned int m_keyMaskMask[4]; }; class Keystroke { public: @@ -82,15 +85,18 @@ private: }; typedef std::vector Keystrokes; typedef std::vector KeyCodes; - typedef std::map KeyCodeMap; + typedef std::map KeyCodeMap; + typedef KeyCodeMap::const_iterator KeyCodeIndex; typedef std::map ModifierMap; unsigned int mapButton(ButtonID button) const; unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, KeyModifierMask, EKeyAction) const; +/* bool findKeyCode(KeyCode&, unsigned int&, KeyID id, unsigned int) const; +*/ void doKeystrokes(const Keystrokes&, SInt32 count); unsigned int maskToX(KeyModifierMask) const; @@ -102,7 +108,14 @@ private: void toggleKey(Display*, KeySym, unsigned int mask); static bool isToggleKeysym(KeySym); + KeyCodeIndex findKey(KeyID keysym, KeyModifierMask& mask) const; + KeyCodeIndex noKey() const; + bool adjustForNumLock(KeySym) const; + bool adjustForCapsLock(KeySym) const; + private: + enum { kNONE, kSHIFT, kALTGR, kSHIFT_ALTGR }; + CXWindowsScreen* m_screen; Window m_window; @@ -134,6 +147,7 @@ private: // modifier masks unsigned int m_altMask; unsigned int m_metaMask; + unsigned int m_superMask; unsigned int m_modeSwitchMask; unsigned int m_numLockMask; unsigned int m_capsLockMask; @@ -142,6 +156,7 @@ private: // map X modifier key indices to the key codes bound to them unsigned int m_keysPerModifier; KeyCodes m_modifierToKeycode; + KeyCodes m_modifierToKeycodes; // maps keycodes to modifier indices ModifierMap m_keycodeToModifier; diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index 0bd15164..912cc219 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -709,8 +709,8 @@ static const KeyID g_virtualKey[][2] = /* 0x58 */ kKeyNone, kKeyNone, // VK_X /* 0x59 */ kKeyNone, kKeyNone, // VK_Y /* 0x5a */ kKeyNone, kKeyNone, // VK_Z - /* 0x5b */ kKeyNone, kKeyMeta_L, // VK_LWIN - /* 0x5c */ kKeyNone, kKeyMeta_R, // VK_RWIN + /* 0x5b */ kKeyNone, kKeySuper_L, // VK_LWIN + /* 0x5c */ kKeyNone, kKeySuper_R, // VK_RWIN /* 0x5d */ kKeyMenu, kKeyMenu, // VK_APPS /* 0x5e */ kKeyNone, kKeyNone, // undefined /* 0x5f */ kKeyNone, kKeyNone, // undefined diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp index d9cce9bd..756dcb03 100644 --- a/lib/server/CXWindowsPrimaryScreen.cpp +++ b/lib/server/CXWindowsPrimaryScreen.cpp @@ -625,6 +625,8 @@ CXWindowsPrimaryScreen::mapModifier(unsigned int state) const mask |= KeyModifierAlt; if (state & m_metaMask) mask |= KeyModifierMeta; + if (state & m_superMask) + mask |= KeyModifierSuper; if (state & m_modeSwitchMask) mask |= KeyModifierModeSwitch; if (state & m_numLockMask) @@ -691,6 +693,7 @@ CXWindowsPrimaryScreen::updateKeys() // initialize m_altMask = 0; m_metaMask = 0; + m_superMask = 0; m_modeSwitchMask = 0; m_numLockMask = 0; m_capsLockMask = 0; @@ -721,6 +724,11 @@ CXWindowsPrimaryScreen::updateKeys() m_metaMask |= bit; break; + case XK_Super_L: + case XK_Super_R: + m_superMask |= bit; + break; + case XK_Mode_switch: m_modeSwitchMask |= bit; break; diff --git a/lib/server/CXWindowsPrimaryScreen.h b/lib/server/CXWindowsPrimaryScreen.h index 8486ab5e..b67f3009 100644 --- a/lib/server/CXWindowsPrimaryScreen.h +++ b/lib/server/CXWindowsPrimaryScreen.h @@ -100,6 +100,7 @@ private: // modifier masks unsigned int m_altMask; unsigned int m_metaMask; + unsigned int m_superMask; unsigned int m_modeSwitchMask; unsigned int m_numLockMask; unsigned int m_capsLockMask; diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 954b5305..49b10c63 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -37,7 +37,8 @@ static const KeyModifierMask KeyModifierShift = 0x0001; static const KeyModifierMask KeyModifierControl = 0x0002; static const KeyModifierMask KeyModifierAlt = 0x0004; static const KeyModifierMask KeyModifierMeta = 0x0008; -static const KeyModifierMask KeyModifierModeSwitch = 0x0010; +static const KeyModifierMask KeyModifierSuper = 0x0010; +static const KeyModifierMask KeyModifierModeSwitch = 0x0020; static const KeyModifierMask KeyModifierCapsLock = 0x1000; static const KeyModifierMask KeyModifierNumLock = 0x2000; static const KeyModifierMask KeyModifierScrollLock = 0x4000; From fec679cfe5b83bb909a396091e052bb447ce964c Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Sep 2002 20:56:28 +0000 Subject: [PATCH 354/807] added better network error message support. --- lib/base/XBase.cpp | 38 +++++++++++++- lib/base/XBase.h | 6 ++- lib/io/XIO.cpp | 28 ----------- lib/io/XIO.h | 13 +---- lib/net/CTCPListenSocket.cpp | 2 +- lib/net/CTCPSocket.cpp | 2 +- lib/net/XSocket.cpp | 98 ++++++++++++++++++++++++++++++++++-- lib/net/XSocket.h | 22 ++++++-- 8 files changed, 155 insertions(+), 54 deletions(-) diff --git a/lib/base/XBase.cpp b/lib/base/XBase.cpp index 0d1d5b32..c72c425e 100644 --- a/lib/base/XBase.cpp +++ b/lib/base/XBase.cpp @@ -18,6 +18,7 @@ // win32 wants a const char* argument to std::exception c'tor #if WINDOWS_LIKE +#include #define STDEXCEPTARG "" #endif @@ -85,17 +86,32 @@ XBase::format(const char* /*id*/, const char* fmt, ...) const throw() // MXErrno::MXErrno() : - m_errno(errno) +#if WINDOWS_LIKE + m_errno(GetLastError()), +#else + m_errno(errno), +#endif + m_string(NULL) { // do nothing } MXErrno::MXErrno(int err) : - m_errno(err) + m_errno(err), + m_string(NULL) { // do nothing } +MXErrno::~MXErrno() +{ + if (m_string != NULL) { +#if WINDOWS_LIKE + LocalFree(m_string); +#endif + } +} + int MXErrno::getErrno() const { @@ -105,5 +121,23 @@ MXErrno::getErrno() const const char* MXErrno::getErrstr() const { +#if WINDOWS_LIKE + if (m_string != NULL) { + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&m_string, + 0, + NULL) == 0) { + m_string = NULL; + return "unknown error"; + } + } + return m_string; +#else return strerror(m_errno); +#endif } diff --git a/lib/base/XBase.h b/lib/base/XBase.h index 579e328e..aac354f0 100644 --- a/lib/base/XBase.h +++ b/lib/base/XBase.h @@ -55,7 +55,7 @@ private: //! Mix-in for handling \c errno /*! This mix-in class for exception classes provides storage and query of -\c errno. +\c errno. On Windows, it uses GetLastError() instead of errno. */ class MXErrno { public: @@ -63,6 +63,7 @@ public: MXErrno(); //! Save \c err as the error code MXErrno(int err); + virtual ~MXErrno(); //! @name accessors //@{ @@ -71,12 +72,13 @@ public: int getErrno() const; //! Get the human readable string for the error code - const char* getErrstr() const; + virtual const char* getErrstr() const; //@} private: int m_errno; + mutable char* m_string; }; #endif diff --git a/lib/io/XIO.cpp b/lib/io/XIO.cpp index 6c7a0552..710c4190 100644 --- a/lib/io/XIO.cpp +++ b/lib/io/XIO.cpp @@ -14,34 +14,6 @@ #include "XIO.h" -// -// XIOErrno -// - -XIOErrno::XIOErrno() : - MXErrno() -{ - // do nothing -} - -XIOErrno::XIOErrno(int err) : - MXErrno(err) -{ - // do nothing -} - - -// -// XIOClose -// - -CString -XIOClose::getWhat() const throw() -{ - return format("XIOClose", "close: %{1}", XIOErrno::getErrstr()); -} - - // // XIOClosed // diff --git a/lib/io/XIO.h b/lib/io/XIO.h index 4bbd10c4..3cb1d944 100644 --- a/lib/io/XIO.h +++ b/lib/io/XIO.h @@ -20,22 +20,11 @@ //! Generic I/O exception class XIO : public XBase { }; -//! Generic I/O exception using \c errno -class XIOErrno : public XIO, public MXErrno { -public: - XIOErrno(); - XIOErrno(int); -}; - //! I/O closing exception /*! Thrown if a stream cannot be closed. */ -class XIOClose: public XIOErrno { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +class XIOClose : public XIO { }; //! I/O already closed exception /*! diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index 57203c31..83278166 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -84,7 +84,7 @@ CTCPListenSocket::close() throw XIOClosed(); } if (CNetwork::close(m_fd) == CNetwork::Error) { - throw XIOClose(); + throw XSocketIOClose(); } m_fd = CNetwork::Null; } diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index b0d6f18b..aa8dd065 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -114,7 +114,7 @@ CTCPSocket::close() // close socket if (m_fd != CNetwork::Null) { if (CNetwork::close(m_fd) == CNetwork::Error) { - throw XIOClose(); + throw XSocketIOClose(); } m_fd = CNetwork::Null; } diff --git a/lib/net/XSocket.cpp b/lib/net/XSocket.cpp index ccc7ab04..8d372675 100644 --- a/lib/net/XSocket.cpp +++ b/lib/net/XSocket.cpp @@ -13,6 +13,7 @@ */ #include "XSocket.h" +#include "CNetwork.h" // // XSocketAddress @@ -71,7 +72,7 @@ XSocketAddress::getWhat() const throw() // XSocketErrno::XSocketErrno() : - MXErrno() + MXErrno(CNetwork::getsockerror()) { // do nothing } @@ -82,6 +83,92 @@ XSocketErrno::XSocketErrno(int err) : // do nothing } +const char* +XSocketErrno::getErrstr() const +{ +#if WINDOWS_LIKE + // built-in windows function for looking up error message strings + // may not look up network error messages correctly. we'll have + // to do it ourself. + static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = { + /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, + /* 10009 */{WSAEBADF, "Bad file handle"}, + /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, + /* 10014 */{WSAEFAULT, "WSAEFAULT"}, + /* 10022 */{WSAEINVAL, "WSAEINVAL"}, + /* 10024 */{WSAEMFILE, "No more file descriptors available"}, + /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, + /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, + /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"}, + /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"}, + /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"}, + /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"}, + /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, + /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, + /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"}, + /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"}, + /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, + /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"}, + /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"}, + /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, + /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, + /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"}, + /* 10051 */{WSAENETUNREACH, "The network can't be reached from this hos at this time"}, + /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, + /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"}, + /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, + /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"}, + /* 10056 */{WSAEISCONN, "The socket is already connected"}, + /* 10057 */{WSAENOTCONN, "The socket is not connected"}, + /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, + /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"}, + /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, + /* 10061 */{WSAECONNREFUSED, "The attempt to connect was forcefully rejected"}, + /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, + /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"}, + /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, + /* 10065 */{WSAEHOSTUNREACH, "No route to host"}, + /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, + /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"}, + /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, + /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, + /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, + /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, + /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"}, + /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, + /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, + /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, + /* 11001 */{WSAHOST_NOT_FOUND, "Host not found"}, + /* 11002 */{WSATRY_AGAIN, "Host not found"}, + /* 11003 */{WSANO_RECOVERY, "Host name lookup error"}, + /* 11004 */{WSANO_DATA, "No data for host"}, + /* end */{0, NULL} + }; + + const int err = getErrno(); + for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) { + if (s_netErrorCodes[i].m_code == err) { + return s_netErrorCodes[i].m_msg; + } + } +#endif + + // not a network error code. fallback to system error message. + return MXErrno::getErrstr(); +} + + +// +// XSocketIOClose +// + +CString +XSocketIOClose::getWhat() const throw() +{ + return format("XSocketIOClose", "close: %{1}", + getErrstr()); +} + // // XSocketBind @@ -90,7 +177,8 @@ XSocketErrno::XSocketErrno(int err) : CString XSocketBind::getWhat() const throw() { - return format("XSocketBind", "cannot bind address"); + return format("XSocketBind", "cannot bind address: %{1}", + getErrstr()); } @@ -101,7 +189,8 @@ XSocketBind::getWhat() const throw() CString XSocketConnect::getWhat() const throw() { - return format("XSocketConnect", "cannot connect socket"); + return format("XSocketConnect", "cannot connect socket: %{1}", + getErrstr()); } @@ -112,5 +201,6 @@ XSocketConnect::getWhat() const throw() CString XSocketCreate::getWhat() const throw() { - return format("XSocketCreate", "cannot create socket"); + return format("XSocketCreate", "cannot create socket: %{1}", + getErrstr()); } diff --git a/lib/net/XSocket.h b/lib/net/XSocket.h index fd67ecb1..131a3f1f 100644 --- a/lib/net/XSocket.h +++ b/lib/net/XSocket.h @@ -15,6 +15,7 @@ #ifndef XSOCKET_H #define XSOCKET_H +#include "XIO.h" #include "XBase.h" #include "CString.h" #include "BasicTypes.h" @@ -61,17 +62,30 @@ private: }; //! Generic socket exception using \c errno -class XSocketErrno : public XSocket, public MXErrno { +class XSocketErrno : public MXErrno { public: XSocketErrno(); XSocketErrno(int); + + // MXErrno overrides + virtual const char* getErrstr() const; +}; + +//! I/O closing exception +/*! +Thrown if a stream cannot be closed. +*/ +class XSocketIOClose : public XIOClose, public XSocketErrno { +protected: + // XBase overrides + virtual CString getWhat() const throw(); }; //! Socket cannot bind address exception /*! Thrown when a socket cannot be bound to an address. */ -class XSocketBind : public XSocketErrno { +class XSocketBind : public XSocket, public XSocketErrno { public: XSocketBind() { } XSocketBind(int e) : XSocketErrno(e) { } @@ -96,7 +110,7 @@ public: /*! Thrown when a socket cannot connect to a remote endpoint. */ -class XSocketConnect : public XSocketErrno { +class XSocketConnect : public XSocket, public XSocketErrno { public: XSocketConnect() { } XSocketConnect(int e) : XSocketErrno(e) { } @@ -110,7 +124,7 @@ protected: /*! Thrown when a socket cannot be created (by the operating system). */ -class XSocketCreate : public XSocketErrno { +class XSocketCreate : public XSocket, public XSocketErrno { public: XSocketCreate() { } XSocketCreate(int e) : XSocketErrno(e) { } From 5e4289dc809e67f71fc55a4d5530b3dc36255c9b Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Sep 2002 20:56:50 +0000 Subject: [PATCH 355/807] now logging bind failures as warnings. --- lib/server/CServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 5d1b59c6..4df38195 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -1178,11 +1178,11 @@ CServer::acceptClients(void*) break; } catch (XSocketBind& e) { - log((CLOG_DEBUG1 "bind failed: %s", e.getErrstr())); + log((CLOG_WARN "bind failed: %s", e.getErrstr())); // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { - log((CLOG_DEBUG1 "waited too long to bind, giving up")); + log((CLOG_ERR "waited too long to bind, giving up")); throw; } From 13c734d8225fb1a4c6a0c4dae221707908804872 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Sep 2002 21:31:35 +0000 Subject: [PATCH 356/807] removed std::exception from base class list of XBase. this is a workaround for gcc 3.2 until everything necessary has throw() specifiers. --- lib/base/XBase.cpp | 4 ++-- lib/base/XBase.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/base/XBase.cpp b/lib/base/XBase.cpp index c72c425e..057024b0 100644 --- a/lib/base/XBase.cpp +++ b/lib/base/XBase.cpp @@ -32,14 +32,14 @@ // XBase::XBase() : - exception(STDEXCEPTARG), +// exception(STDEXCEPTARG), m_what() { // do nothing } XBase::XBase(const CString& msg) : - exception(STDEXCEPTARG), +// exception(STDEXCEPTARG), m_what(msg) { // do nothing diff --git a/lib/base/XBase.h b/lib/base/XBase.h index aac354f0..72a871a4 100644 --- a/lib/base/XBase.h +++ b/lib/base/XBase.h @@ -24,7 +24,7 @@ /*! This is the base class of most exception types. */ -class XBase : public std::exception { +class XBase /*: public std::exception*/ { public: //! Use getWhat() as the result of what() XBase(); From 9e7b411f782e9ed7ab70737f2c4f31fe3b84b213 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Sep 2002 21:47:29 +0000 Subject: [PATCH 357/807] Changes for version 0.9.12. --- ChangeLog | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ NEWS | 10 +++++++++ 2 files changed, 74 insertions(+) diff --git a/ChangeLog b/ChangeLog index be45c69a..df0535e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,67 @@ +2002/09/14 21:31:35 crs +lib/base/XBase.cpp +lib/base/XBase.h + +removed std::exception from base class list of XBase. this +is a workaround for gcc 3.2 until everything necessary has +throw() specifiers. + +---------- +2002/09/14 20:56:50 crs +lib/server/CServer.cpp + +now logging bind failures as warnings. + +---------- +2002/09/14 20:56:28 crs +lib/base/XBase.cpp +lib/base/XBase.h +lib/io/XIO.cpp +lib/io/XIO.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPSocket.cpp +lib/net/XSocket.cpp +lib/net/XSocket.h + +added better network error message support. + +---------- +2002/09/14 12:07:02 crs +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/synergy/KeyTypes.h + +Rewrote handling of key press on X11 client; it should be much +more robust now. Also added handling of Super modifier key and +changed windows keys to map to Super instead of Meta, which is +the default on my keyboard. + +---------- +2002/09/14 12:05:06 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added debug level combo box and version number to title bar of win32 +launcher. + +---------- +2002/09/14 12:03:43 crs +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc + +Fixed backend mode. Now reports log messages and, if any are +serious, shows a message box before exiting. + +---------- 2002/09/04 21:17:01 crs NEWS configure.in diff --git a/NEWS b/NEWS index 1b88ddc1..2fba0052 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,16 @@ Synergy News ============ +* Sep-14-2002 - Synergy 0.9.12 released + + Made following changes: + * Win32 was not reporting log messages properly when run from synergy.exe + * Network error messages weren't reporting useful information + * Synergy won't build on gcc 3.2; added workaround for known problem + * X11 wasn't handling some keys/key combinations correctly + * Added option to change logging level when testing from synergy.exe + + * Sep-04-2002 - Synergy 0.9.11 released Fixed following bugs: From d8dde48c2b5b40fc3bccd7d7eb52983ca4478447 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 15 Oct 2002 21:29:44 +0000 Subject: [PATCH 358/807] Changed log() and logc() macros to LOG() and LOGC(), respectively. This avoids a conflict with the standard math library log() function. --- cmd/synergyc/synergyc.cpp | 24 ++-- cmd/synergys/synergys.cpp | 32 ++--- lib/base/CLog.h | 24 ++-- lib/client/CClient.cpp | 72 +++++----- lib/client/CMSWindowsSecondaryScreen.cpp | 18 +-- lib/client/CSecondaryScreen.cpp | 10 +- lib/client/CServerProxy.cpp | 54 ++++---- lib/client/CXWindowsSecondaryScreen.cpp | 46 +++---- lib/http/CHTTPProtocol.cpp | 28 ++-- lib/mt/CMutex.cpp | 6 +- lib/mt/CThread.cpp | 2 +- lib/mt/CThreadRep.cpp | 18 +-- lib/mt/CTimerThread.cpp | 8 +- lib/net/CNetwork.cpp | 8 +- lib/platform/CMSWindowsClipboard.cpp | 12 +- lib/platform/CMSWindowsScreen.cpp | 18 +-- lib/platform/CMSWindowsScreenSaver.cpp | 10 +- lib/platform/CWin32Platform.cpp | 24 ++-- lib/platform/CXWindowsClipboard.cpp | 86 ++++++------ lib/platform/CXWindowsScreen.cpp | 12 +- lib/platform/CXWindowsScreenSaver.cpp | 12 +- lib/platform/CXWindowsUtil.cpp | 8 +- lib/server/CClientProxy1_0.cpp | 46 +++---- lib/server/CHTTPServer.cpp | 14 +- lib/server/CMSWindowsPrimaryScreen.cpp | 26 ++-- lib/server/CPrimaryClient.cpp | 4 +- lib/server/CPrimaryScreen.cpp | 10 +- lib/server/CServer.cpp | 166 +++++++++++------------ lib/server/CXWindowsPrimaryScreen.cpp | 28 ++-- lib/synergy/CProtocolUtil.cpp | 18 +-- 30 files changed, 422 insertions(+), 422 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index aa7906d3..495c0b6f 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -196,7 +196,7 @@ realMain(CMutex* mutex) #undef FINALLY } catch (XBase& e) { - log((CLOG_CRIT "failed: %s", e.what())); + LOG((CLOG_CRIT "failed: %s", e.what())); } catch (XThread&) { // terminated @@ -230,7 +230,7 @@ static void version() { - log((CLOG_PRINT + LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n" "%s", pname, @@ -244,7 +244,7 @@ static void help() { - log((CLOG_PRINT + LOG((CLOG_PRINT "Usage: %s" " [--camp|--no-camp]" " [--daemon|--no-daemon]" @@ -293,7 +293,7 @@ isArg(int argi, int argc, const char** argv, (name2 != NULL && strcmp(argv[argi], name2) == 0)) { // match. check args left. if (argi + minRequiredParameters >= argc) { - log((CLOG_PRINT "%s: missing arguments for `%s'" BYE, + LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, pname, argv[argi], pname)); bye(kExitArgs); } @@ -382,7 +382,7 @@ parse(int argc, const char** argv) } else if (argv[i][0] == '-') { - log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, pname, argv[i], pname)); bye(kExitArgs); } @@ -395,12 +395,12 @@ parse(int argc, const char** argv) // exactly one non-option argument (server-address) if (i == argc) { - log((CLOG_PRINT "%s: a server address or name is required" BYE, + LOG((CLOG_PRINT "%s: a server address or name is required" BYE, pname, pname)); bye(kExitArgs); } if (i + 1 != argc) { - log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, pname, argv[i], pname)); bye(kExitArgs); } @@ -410,7 +410,7 @@ parse(int argc, const char** argv) s_serverAddress = CNetworkAddress(argv[i], kDefaultPort); } catch (XSocketAddress& e) { - log((CLOG_PRINT "%s: %s" BYE, + LOG((CLOG_PRINT "%s: %s" BYE, pname, e.what(), pname)); bye(kExitFailed); } @@ -433,7 +433,7 @@ parse(int argc, const char** argv) // set log filter if (!CLog::setFilter(s_logFilter)) { - log((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, + LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, pname, s_logFilter, pname)); bye(kExitArgs); } @@ -537,7 +537,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) if (__argc <= 1 && !CWin32Platform::isWindows95Family()) { int result = platform.daemonize(DAEMON_NAME, &daemonStartup); if (result == -1) { - log((CLOG_CRIT "failed to start as a service" BYE, pname)); + LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); return kExitFailed; } return result; @@ -556,7 +556,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) if (CWin32Platform::isWindows95Family()) { result = platform.daemonize(DAEMON_NAME, &daemonStartup95); if (result == -1) { - log((CLOG_CRIT "failed to start as a service" BYE, pname)); + LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); result = kExitFailed; } } @@ -613,7 +613,7 @@ main(int argc, char** argv) if (s_daemon) { result = platform.daemonize(DAEMON_NAME, &daemonStartup); if (result == -1) { - log((CLOG_CRIT "failed to daemonize")); + LOG((CLOG_CRIT "failed to daemonize")); return kExitFailed; } } diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 4f60ff9e..7191a440 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -220,7 +220,7 @@ realMain(CMutex* mutex) #undef FINALLY } catch (XBase& e) { - log((CLOG_CRIT "failed: %s", e.what())); + LOG((CLOG_CRIT "failed: %s", e.what())); } catch (XThread&) { // terminated @@ -254,7 +254,7 @@ static void version() { - log((CLOG_PRINT + LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n" "%s", pname, @@ -289,7 +289,7 @@ help() CPlatform platform; - log((CLOG_PRINT + LOG((CLOG_PRINT "Usage: %s" " [--address
]" " [--config ]" @@ -353,7 +353,7 @@ isArg(int argi, int argc, const char** argv, (name2 != NULL && strcmp(argv[argi], name2) == 0)) { // match. check args left. if (argi + minRequiredParameters >= argc) { - log((CLOG_PRINT "%s: missing arguments for `%s'" BYE, + LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, pname, argv[argi], pname)); bye(kExitArgs); } @@ -392,7 +392,7 @@ parse(int argc, const char** argv) s_synergyAddress = CNetworkAddress(argv[i + 1], kDefaultPort); } catch (XSocketAddress& e) { - log((CLOG_PRINT "%s: %s" BYE, + LOG((CLOG_PRINT "%s: %s" BYE, pname, e.what(), pname)); bye(kExitArgs); } @@ -405,7 +405,7 @@ parse(int argc, const char** argv) s_httpAddress = CNetworkAddress(argv[i + 1], kDefaultPort + 1); } catch (XSocketAddress& e) { - log((CLOG_PRINT "%s: %s" BYE, + LOG((CLOG_PRINT "%s: %s" BYE, pname, e.what(), pname)); bye(kExitArgs); } @@ -463,7 +463,7 @@ parse(int argc, const char** argv) } else if (argv[i][0] == '-') { - log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, pname, argv[i], pname)); bye(kExitArgs); } @@ -476,7 +476,7 @@ parse(int argc, const char** argv) // no non-option arguments are allowed if (i != argc) { - log((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, pname, argv[i], pname)); bye(kExitArgs); } @@ -499,7 +499,7 @@ parse(int argc, const char** argv) // set log filter if (!CLog::setFilter(s_logFilter)) { - log((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, + LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, pname, s_logFilter, pname)); bye(kExitArgs); } @@ -513,23 +513,23 @@ loadConfig(const char* pathname, bool require) try { // load configuration - log((CLOG_DEBUG "opening configuration \"%s\"", pathname)); + LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname)); std::ifstream configStream(pathname); if (!configStream) { throw XConfigRead("cannot open configuration"); } configStream >> s_config; - log((CLOG_DEBUG "configuration read successfully")); + LOG((CLOG_DEBUG "configuration read successfully")); return true; } catch (XConfigRead& e) { if (require) { - log((CLOG_PRINT "%s: cannot read configuration '%s': %s", + LOG((CLOG_PRINT "%s: cannot read configuration '%s': %s", pname, pathname, e.what())); bye(kExitConfig); } else { - log((CLOG_DEBUG "cannot read configuration \"%s\": %s", + LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", pathname, e.what())); } } @@ -672,7 +672,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) if (__argc <= 1 && !CWin32Platform::isWindows95Family()) { int result = platform.daemonize(DAEMON_NAME, &daemonStartup); if (result == -1) { - log((CLOG_CRIT "failed to start as a service" BYE, pname)); + LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); return kExitFailed; } return result; @@ -694,7 +694,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) if (CWin32Platform::isWindows95Family()) { result = platform.daemonize(DAEMON_NAME, &daemonStartup95); if (result == -1) { - log((CLOG_CRIT "failed to start as a service" BYE, pname)); + LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); result = kExitFailed; } } @@ -754,7 +754,7 @@ main(int argc, char** argv) if (s_daemon) { result = platform.daemonize(DAEMON_NAME, &daemonStartup); if (result == -1) { - log((CLOG_CRIT "failed to daemonize")); + LOG((CLOG_CRIT "failed to daemonize")); return kExitFailed; } } diff --git a/lib/base/CLog.h b/lib/base/CLog.h index e364e8ac..302e748a 100644 --- a/lib/base/CLog.h +++ b/lib/base/CLog.h @@ -22,8 +22,8 @@ /*! The logging class; all console output should go through this class. It supports multithread safe operation, several message priority levels, -filtering by priority, and output redirection. The macros log() and -clog() provide convenient access. +filtering by priority, and output redirection. The macros LOG() and +LOGC() provide convenient access. */ class CLog { public: @@ -150,11 +150,11 @@ private: }; /*! -\def log(arg) +\def LOG(arg) Write to the log. Because macros cannot accept variable arguments, this should be invoked like so: \code -log((CLOG_XXX "%d and %d are %s", x, y, x == y ? "equal" : "not equal")); +LOG((CLOG_XXX "%d and %d are %s", x, y, x == y ? "equal" : "not equal")); \endcode In particular, notice the double open and close parentheses. Also note that there is no comma after the \c CLOG_XXX. The \c XXX should be @@ -169,11 +169,11 @@ which includes the filename and line number. */ /*! -\def logc(expr, arg) +\def LOGC(expr, arg) Write to the log if and only if expr is true. Because macros cannot accept variable arguments, this should be invoked like so: \code -clog(x == y, (CLOG_XXX "%d and %d are equal", x, y)); +LOGC(x == y, (CLOG_XXX "%d and %d are equal", x, y)); \endcode In particular, notice the parentheses around everything after the boolean expression. Also note that there is no comma after the \c CLOG_XXX. @@ -189,16 +189,16 @@ which includes the filename and line number. */ #if defined(NOLOGGING) -#define log(_a1) -#define logc(_a1, _a2) +#define LOG(_a1) +#define LOGC(_a1, _a2) #define CLOG_TRACE #elif defined(NDEBUG) -#define log(_a1) CLog::print _a1 -#define logc(_a1, _a2) if (_a1) CLog::print _a2 +#define LOG(_a1) CLog::print _a1 +#define LOGC(_a1, _a2) if (_a1) CLog::print _a2 #define CLOG_TRACE #else -#define log(_a1) CLog::printt _a1 -#define logc(_a1, _a2) if (_a1) CLog::printt _a2 +#define LOG(_a1) CLog::printt _a1 +#define LOGC(_a1, _a2) if (_a1) CLog::printt _a2 #define CLOG_TRACE __FILE__, __LINE__, #endif diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index de6b086d..44076bd0 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -122,7 +122,7 @@ CClient::onError() void CClient::onInfoChanged(const CClientInfo& info) { - log((CLOG_DEBUG "resolution changed")); + LOG((CLOG_DEBUG "resolution changed")); CLock lock(&m_mutex); if (m_server != NULL) { @@ -168,12 +168,12 @@ CClient::open() { // open the screen try { - log((CLOG_INFO "opening screen")); + LOG((CLOG_INFO "opening screen")); openSecondaryScreen(); } catch (XScreenOpenFailure&) { // can't open screen - log((CLOG_INFO "failed to open screen")); + LOG((CLOG_INFO "failed to open screen")); throw; } } @@ -193,7 +193,7 @@ CClient::mainLoop() } try { - log((CLOG_NOTE "starting client \"%s\"", m_name.c_str())); + LOG((CLOG_NOTE "starting client \"%s\"", m_name.c_str())); // start server interactions { @@ -207,29 +207,29 @@ CClient::mainLoop() // clean up deleteSession(); - log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); } catch (XBase& e) { - log((CLOG_ERR "client error: %s", e.what())); + LOG((CLOG_ERR "client error: %s", e.what())); // clean up deleteSession(); - log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); CLock lock(&m_mutex); m_rejected = false; } catch (XThread&) { // clean up deleteSession(); - log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; } catch (...) { - log((CLOG_DEBUG "unknown client error")); + LOG((CLOG_DEBUG "unknown client error")); // clean up deleteSession(); - log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; } } @@ -238,7 +238,7 @@ void CClient::close() { closeSecondaryScreen(); - log((CLOG_INFO "closed screen")); + LOG((CLOG_INFO "closed screen")); } void @@ -392,7 +392,7 @@ CClient::openSecondaryScreen() } // create screen - log((CLOG_DEBUG1 "creating secondary screen")); + LOG((CLOG_DEBUG1 "creating secondary screen")); if (m_screenFactory != NULL) { m_screen = m_screenFactory->create(this); } @@ -402,11 +402,11 @@ CClient::openSecondaryScreen() // open screen try { - log((CLOG_DEBUG1 "opening secondary screen")); + LOG((CLOG_DEBUG1 "opening secondary screen")); m_screen->open(); } catch (...) { - log((CLOG_DEBUG1 "destroying secondary screen")); + LOG((CLOG_DEBUG1 "destroying secondary screen")); delete m_screen; m_screen = NULL; throw; @@ -419,7 +419,7 @@ CClient::closeSecondaryScreen() // close the secondary screen try { if (m_screen != NULL) { - log((CLOG_DEBUG1 "closing secondary screen")); + LOG((CLOG_DEBUG1 "closing secondary screen")); m_screen->close(); } } @@ -428,7 +428,7 @@ CClient::closeSecondaryScreen() } // clean up - log((CLOG_DEBUG1 "destroying secondary screen")); + LOG((CLOG_DEBUG1 "destroying secondary screen")); delete m_screen; m_screen = NULL; } @@ -471,14 +471,14 @@ void CClient::runSession(void*) { try { - log((CLOG_DEBUG "starting server proxy")); + LOG((CLOG_DEBUG "starting server proxy")); runServer(); m_screen->exitMainLoop(); - log((CLOG_DEBUG "stopping server proxy")); + LOG((CLOG_DEBUG "stopping server proxy")); } catch (...) { m_screen->exitMainLoop(); - log((CLOG_DEBUG "stopping server proxy")); + LOG((CLOG_DEBUG "stopping server proxy")); throw; } } @@ -514,17 +514,17 @@ CClient::runServer() CTimerThread timer(15.0); // create socket and attempt to connect to server - log((CLOG_DEBUG1 "connecting to server")); + LOG((CLOG_DEBUG1 "connecting to server")); if (m_socketFactory != NULL) { socket = m_socketFactory->create(); } assert(socket != NULL); socket->connect(m_serverAddress); - log((CLOG_INFO "connected to server")); + LOG((CLOG_INFO "connected to server")); break; } catch (XSocketConnect& e) { - log((CLOG_DEBUG1 "failed to connect to server: %s", e.getErrstr())); + LOG((CLOG_DEBUG1 "failed to connect to server: %s", e.getErrstr())); // failed to connect. if not camping then rethrow. if (!m_camp) { @@ -537,25 +537,25 @@ CClient::runServer() } // create proxy - log((CLOG_DEBUG1 "negotiating with server")); + LOG((CLOG_DEBUG1 "negotiating with server")); proxy = handshakeServer(socket); CLock lock(&m_mutex); m_server = proxy; } catch (XThread&) { - log((CLOG_ERR "connection timed out")); + LOG((CLOG_ERR "connection timed out")); delete socket; throw; } catch (XBase& e) { - log((CLOG_ERR "connection failed: %s", e.what())); - log((CLOG_DEBUG "disconnecting from server")); + LOG((CLOG_ERR "connection failed: %s", e.what())); + LOG((CLOG_DEBUG "disconnecting from server")); delete socket; return; } catch (...) { - log((CLOG_ERR "connection failed: ")); - log((CLOG_DEBUG "disconnecting from server")); + LOG((CLOG_ERR "connection failed: ")); + LOG((CLOG_DEBUG "disconnecting from server")); delete socket; return; } @@ -564,7 +564,7 @@ CClient::runServer() // process messages bool rejected = true; if (proxy != NULL) { - log((CLOG_DEBUG1 "communicating with server")); + LOG((CLOG_DEBUG1 "communicating with server")); rejected = !proxy->mainLoop(); } @@ -573,7 +573,7 @@ CClient::runServer() m_rejected = rejected; m_server = NULL; delete proxy; - log((CLOG_DEBUG "disconnecting from server")); + LOG((CLOG_DEBUG "disconnecting from server")); socket->close(); delete socket; } @@ -582,7 +582,7 @@ CClient::runServer() m_rejected = false; m_server = NULL; delete proxy; - log((CLOG_DEBUG "disconnecting from server")); + LOG((CLOG_DEBUG "disconnecting from server")); socket->close(); delete socket; throw; @@ -615,19 +615,19 @@ CClient::handshakeServer(IDataSocket* socket) CTimerThread timer(30.0); // wait for hello from server - log((CLOG_DEBUG1 "wait for hello")); + LOG((CLOG_DEBUG1 "wait for hello")); SInt16 major, minor; CProtocolUtil::readf(input, kMsgHello, &major, &minor); // check versions - log((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); + LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); if (major < kProtocolMajorVersion || (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) { throw XIncompatibleClient(major, minor); } // say hello back - log((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); + LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); CProtocolUtil::writef(output, kMsgHelloBack, kProtocolMajorVersion, kProtocolMinorVersion, &m_name); @@ -641,10 +641,10 @@ CClient::handshakeServer(IDataSocket* socket) return proxy; } catch (XIncompatibleClient& e) { - log((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); + LOG((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); } catch (XBase& e) { - log((CLOG_WARN "error communicating with server: %s", e.what())); + LOG((CLOG_WARN "error communicating with server: %s", e.what())); } catch (...) { // probably timed out diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index a42f3763..4357e615 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -492,7 +492,7 @@ CMSWindowsSecondaryScreen::updateKeys() m_mask |= KeyModifierScrollLock; } // note -- do not save KeyModifierModeSwitch in m_mask - log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); + LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); } void @@ -625,7 +625,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, virtualKey = g_mapEF00[id & 0xff]; } if (virtualKey == 0) { - log((CLOG_DEBUG2 "unknown special key")); + LOG((CLOG_DEBUG2 "unknown special key")); return m_mask; } } @@ -685,7 +685,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, TCHAR ascii = static_cast(id & 0x000000ff); SHORT vk = VkKeyScan(ascii); if (vk == 0xffff) { - log((CLOG_DEBUG2 "no virtual key for character %d", id)); + LOG((CLOG_DEBUG2 "no virtual key for character %d", id)); return m_mask; } @@ -712,7 +712,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // are subject to case conversion. if ((outMask & KeyModifierCapsLock) != 0) { if (tolower(ascii) != toupper(ascii)) { - log((CLOG_DEBUG2 "flip shift")); + LOG((CLOG_DEBUG2 "flip shift")); outMask ^= KeyModifierShift; } } @@ -734,11 +734,11 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // set required shift state based on current numlock state if ((outMask & KeyModifierNumLock) == 0) { if ((m_mask & KeyModifierNumLock) == 0) { - log((CLOG_DEBUG2 "turn on num lock for keypad key")); + LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); outMask |= KeyModifierNumLock; } else { - log((CLOG_DEBUG2 "turn on shift for keypad key")); + LOG((CLOG_DEBUG2 "turn on shift for keypad key")); outMask |= KeyModifierShift; } } @@ -749,7 +749,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, outMask |= KeyModifierShift; } } - log((CLOG_DEBUG2 "KeyID %d to virtual key %d mask 0x%04x", id, virtualKey, outMask)); + LOG((CLOG_DEBUG2 "KeyID %d to virtual key %d mask 0x%04x", id, virtualKey, outMask)); // a list of modifier key info class CModifierInfo { @@ -956,7 +956,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, } } - log((CLOG_DEBUG2 "previous modifiers 0x%04x, final modifiers 0x%04x", m_mask, mask)); + LOG((CLOG_DEBUG2 "previous modifiers 0x%04x, final modifiers 0x%04x", m_mask, mask)); return mask; } @@ -1145,5 +1145,5 @@ CMSWindowsSecondaryScreen::sendKeyEvent(UINT virtualKey, bool press) const UINT code = virtualKeyToScanCode(virtualKey); keybd_event(static_cast(virtualKey & 0xff), static_cast(code), flags, 0); - log((CLOG_DEBUG1 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : ""))); + LOG((CLOG_DEBUG1 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : ""))); } diff --git a/lib/client/CSecondaryScreen.cpp b/lib/client/CSecondaryScreen.cpp index dd2b9169..71283bc5 100644 --- a/lib/client/CSecondaryScreen.cpp +++ b/lib/client/CSecondaryScreen.cpp @@ -40,15 +40,15 @@ CSecondaryScreen::mainLoop() // run event loop try { - log((CLOG_DEBUG "entering event loop")); + LOG((CLOG_DEBUG "entering event loop")); onPreMainLoop(); getScreen()->mainLoop(); onPostMainLoop(); - log((CLOG_DEBUG "exiting event loop")); + LOG((CLOG_DEBUG "exiting event loop")); } catch (...) { onPostMainLoop(); - log((CLOG_DEBUG "exiting event loop")); + LOG((CLOG_DEBUG "exiting event loop")); throw; } } @@ -115,7 +115,7 @@ CSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) CLock lock(&m_mutex); assert(m_active == false); - log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); + LOG((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); getScreen()->syncDesktop(); @@ -144,7 +144,7 @@ CSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) void CSecondaryScreen::leave() { - log((CLOG_INFO "leaving screen")); + LOG((CLOG_INFO "leaving screen")); CLock lock(&m_mutex); assert(m_active == true); diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 44cb8e52..920be85d 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -69,13 +69,13 @@ CServerProxy::mainLoop() } // wait for a message - log((CLOG_DEBUG2 "waiting for message")); + LOG((CLOG_DEBUG2 "waiting for message")); UInt8 code[4]; UInt32 n = getInputStream()->read(code, 4, kHeartRate); // check if server hungup if (n == 0) { - log((CLOG_NOTE "server disconnected")); + LOG((CLOG_NOTE "server disconnected")); break; } @@ -95,12 +95,12 @@ CServerProxy::mainLoop() // verify we got an entire code if (n != 4) { // client sent an incomplete message - log((CLOG_ERR "incomplete message from server")); + LOG((CLOG_ERR "incomplete message from server")); break; } // parse message - log((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); if (memcmp(code, kMsgDMouseMove, 4) == 0) { mouseMove(); } @@ -163,7 +163,7 @@ CServerProxy::mainLoop() else if (memcmp(code, kMsgCClose, 4) == 0) { // server wants us to hangup - log((CLOG_DEBUG1 "recv close")); + LOG((CLOG_DEBUG1 "recv close")); break; } @@ -171,39 +171,39 @@ CServerProxy::mainLoop() SInt32 major, minor; CProtocolUtil::readf(getInputStream(), kMsgEIncompatible + 4, &major, &minor); - log((CLOG_ERR "server has incompatible version %d.%d", major, minor)); + LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); failedToConnect = true; break; } else if (memcmp(code, kMsgEBusy, 4) == 0) { - log((CLOG_ERR "server already has a connected client with name \"%s\"", getName().c_str())); + LOG((CLOG_ERR "server already has a connected client with name \"%s\"", getName().c_str())); failedToConnect = true; break; } else if (memcmp(code, kMsgEUnknown, 4) == 0) { - log((CLOG_ERR "server refused client with name \"%s\"", getName().c_str())); + LOG((CLOG_ERR "server refused client with name \"%s\"", getName().c_str())); failedToConnect = true; break; } else if (memcmp(code, kMsgEBad, 4) == 0) { - log((CLOG_ERR "server disconnected due to a protocol error")); + LOG((CLOG_ERR "server disconnected due to a protocol error")); failedToConnect = true; break; } else { // unknown message - log((CLOG_ERR "unknown message from server")); + LOG((CLOG_ERR "unknown message from server")); failedToConnect = true; break; } } } catch (XBase& e) { - log((CLOG_ERR "error: %s", e.what())); + LOG((CLOG_ERR "error: %s", e.what())); } catch (...) { throw; @@ -257,7 +257,7 @@ CServerProxy::onInfoChanged(const CClientInfo& info) bool CServerProxy::onGrabClipboard(ClipboardID id) { - log((CLOG_DEBUG1 "sending clipboard %d changed", id)); + LOG((CLOG_DEBUG1 "sending clipboard %d changed", id)); CLock lock(&m_mutex); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, m_seqNum); return true; @@ -267,7 +267,7 @@ void CServerProxy::onClipboardChanged(ClipboardID id, const CString& data) { CLock lock(&m_mutex); - log((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); + LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, m_seqNum, &data); } @@ -295,7 +295,7 @@ void CServerProxy::sendInfo(const CClientInfo& info) { // note -- m_mutex should be locked on entry - log((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); + LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); CProtocolUtil::writef(getOutputStream(), kMsgDInfo, info.m_x, info.m_y, info.m_w, info.m_h, @@ -312,7 +312,7 @@ CServerProxy::enter() UInt32 seqNum; CProtocolUtil::readf(getInputStream(), kMsgCEnter + 4, &x, &y, &seqNum, &mask); - log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask)); + LOG((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask)); // discard old compressed mouse motion, if any { @@ -329,7 +329,7 @@ void CServerProxy::leave() { // parse - log((CLOG_DEBUG1 "recv leave")); + LOG((CLOG_DEBUG1 "recv leave")); // send last mouse motion flushCompressedMouse(); @@ -347,7 +347,7 @@ CServerProxy::setClipboard() CString data; CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); - log((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); + LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); // validate if (id >= kClipboardEnd) { @@ -365,7 +365,7 @@ CServerProxy::grabClipboard() ClipboardID id; UInt32 seqNum; CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); - log((CLOG_DEBUG "recv grab clipboard %d", id)); + LOG((CLOG_DEBUG "recv grab clipboard %d", id)); // validate if (id >= kClipboardEnd) { @@ -385,7 +385,7 @@ CServerProxy::keyDown() // parse UInt16 id, mask; CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, &id, &mask); - log((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask)); + LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask)); // forward getClient()->keyDown(static_cast(id), @@ -402,7 +402,7 @@ CServerProxy::keyRepeat() UInt16 id, mask, count; CProtocolUtil::readf(getInputStream(), kMsgDKeyRepeat + 4, &id, &mask, &count); - log((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count)); + LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count)); // forward getClient()->keyRepeat(static_cast(id), @@ -419,7 +419,7 @@ CServerProxy::keyUp() // parse UInt16 id, mask; CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask); - log((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask)); + LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask)); // forward getClient()->keyUp(static_cast(id), @@ -435,7 +435,7 @@ CServerProxy::mouseDown() // parse SInt8 id; CProtocolUtil::readf(getInputStream(), kMsgDMouseDown + 4, &id); - log((CLOG_DEBUG1 "recv mouse down id=%d", id)); + LOG((CLOG_DEBUG1 "recv mouse down id=%d", id)); // forward getClient()->mouseDown(static_cast(id)); @@ -450,7 +450,7 @@ CServerProxy::mouseUp() // parse SInt8 id; CProtocolUtil::readf(getInputStream(), kMsgDMouseUp + 4, &id); - log((CLOG_DEBUG1 "recv mouse up id=%d", id)); + LOG((CLOG_DEBUG1 "recv mouse up id=%d", id)); // forward getClient()->mouseUp(static_cast(id)); @@ -481,7 +481,7 @@ CServerProxy::mouseMove() m_yMouse = y; } } - log((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); + LOG((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); // forward if (!ignore) { @@ -498,7 +498,7 @@ CServerProxy::mouseWheel() // parse SInt16 delta; CProtocolUtil::readf(getInputStream(), kMsgDMouseWheel + 4, &delta); - log((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); + LOG((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); // forward getClient()->mouseWheel(delta); @@ -510,7 +510,7 @@ CServerProxy::screensaver() // parse SInt8 on; CProtocolUtil::readf(getInputStream(), kMsgCScreenSaver + 4, &on); - log((CLOG_DEBUG1 "recv screen saver on=%d", on)); + LOG((CLOG_DEBUG1 "recv screen saver on=%d", on)); // forward getClient()->screensaver(on != 0); @@ -534,7 +534,7 @@ void CServerProxy::infoAcknowledgment() { // parse - log((CLOG_DEBUG1 "recv info acknowledgment")); + LOG((CLOG_DEBUG1 "recv info acknowledgment")); // now allow mouse motion CLock lock(&m_mutex); diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index 4cb71fba..6095a478 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -293,7 +293,7 @@ CXWindowsSecondaryScreen::createWindow() int majorOpcode, firstEvent, firstError; if (!XQueryExtension(display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) { - log((CLOG_ERR "XTEST extension not available")); + LOG((CLOG_ERR "XTEST extension not available")); throw XScreenOpenFailure(); } @@ -317,7 +317,7 @@ CXWindowsSecondaryScreen::createWindow() if (m_window == None) { throw XScreenOpenFailure(); } - log((CLOG_DEBUG "window is 0x%08x", m_window)); + LOG((CLOG_DEBUG "window is 0x%08x", m_window)); // become impervious to server grabs XTestGrabControl(display, True); @@ -490,28 +490,28 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // keys affected by CapsLock. bool desireShift = (getBits(desired, ShiftMask) != 0); bool invertShift = false; -log((CLOG_DEBUG1 "desire shift 1: %s", desireShift?"yes":"no")); +LOG((CLOG_DEBUG1 "desire shift 1: %s", desireShift?"yes":"no")); if (adjustForNumLock(keysym)) { -log((CLOG_DEBUG1 "num lock sensitive")); +LOG((CLOG_DEBUG1 "num lock sensitive")); if (m_numLockMask != 0) { -log((CLOG_DEBUG1 "we have num lock")); +LOG((CLOG_DEBUG1 "we have num lock")); if (getBits(desired, m_numLockMask) != 0) { -log((CLOG_DEBUG1 "num lock desired, invert shift")); +LOG((CLOG_DEBUG1 "num lock desired, invert shift")); invertShift = true; } } } else if (adjustForCapsLock(keysym)) { -log((CLOG_DEBUG1 "caps lock sensitive")); +LOG((CLOG_DEBUG1 "caps lock sensitive")); if (m_capsLockMask != 0) { -log((CLOG_DEBUG1 "we have caps lock")); +LOG((CLOG_DEBUG1 "we have caps lock")); if (getBits(desired, m_capsLockMask) != 0) { -log((CLOG_DEBUG1 "caps lock desired, invert shift")); +LOG((CLOG_DEBUG1 "caps lock desired, invert shift")); invertShift = true; } } } -log((CLOG_DEBUG1 "desire shift 2: %s", desireShift?"yes":"no")); +LOG((CLOG_DEBUG1 "desire shift 2: %s", desireShift?"yes":"no")); if (desireShift != invertShift) { index[0] ^= 1; index[1] ^= 1; @@ -538,7 +538,7 @@ log((CLOG_DEBUG1 "desire shift 2: %s", desireShift?"yes":"no")); // get the keycode keycode = entry.m_keycode[bestIndex]; -log((CLOG_DEBUG1 "bestIndex = %d, keycode = %d", bestIndex, keycode)); +LOG((CLOG_DEBUG1 "bestIndex = %d, keycode = %d", bestIndex, keycode)); // note if the key is a modifier ModifierMap::const_iterator modIndex = m_keycodeToModifier.find(keycode); @@ -553,13 +553,13 @@ log((CLOG_DEBUG1 "bestIndex = %d, keycode = %d", bestIndex, keycode)); // though. if (modifierBit != 0) { if (action == kRepeat) { -log((CLOG_DEBUG1 "ignore repeating modifier")); +LOG((CLOG_DEBUG1 "ignore repeating modifier")); return m_mask; } if (getBits(m_toggleModifierMask, modifierBit) == 0) { if ((action == kPress && (m_mask & modifierBit) != 0) || (action == kRelease && (m_mask & modifierBit) == 0)) { -log((CLOG_DEBUG1 "modifier in proper state: 0x%04x", m_mask)); +LOG((CLOG_DEBUG1 "modifier in proper state: 0x%04x", m_mask)); return m_mask; } } @@ -594,7 +594,7 @@ log((CLOG_DEBUG1 "modifier in proper state: 0x%04x", m_mask)); // the same bit in m_mask, meaning it's already in the right state. desired = assignBits(desired, modifierBit, m_mask); required = clearBits(required, modifierBit); -log((CLOG_DEBUG1 "desired = 0x%04x, current = 0x%04x", desired, m_mask)); +LOG((CLOG_DEBUG1 "desired = 0x%04x, current = 0x%04x", desired, m_mask)); // add the key events required to get to the modifier state // necessary to generate an event yielding id. also save the @@ -607,13 +607,13 @@ log((CLOG_DEBUG1 "desired = 0x%04x, current = 0x%04x", desired, m_mask)); for (unsigned int i = 0; i < 8; ++i) { unsigned int bit = (1 << i); if (getBits(desired, bit) != getBits(m_mask, bit)) { -log((CLOG_DEBUG1 "fix modifier %d", i)); +LOG((CLOG_DEBUG1 "fix modifier %d", i)); // get the keycode we're using for this modifier. if // there isn't one then bail if the modifier is required // or ignore it if not required. KeyCode modifierKey = m_modifierToKeycode[i]; if (modifierKey == 0) { - log((CLOG_DEBUG1 "no key mapped to modifier 0x%04x", bit)); + LOG((CLOG_DEBUG1 "no key mapped to modifier 0x%04x", bit)); if (getBits(required, bit) != 0) { keys.clear(); return m_mask; @@ -630,9 +630,9 @@ log((CLOG_DEBUG1 "fix modifier %d", i)); // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a // press. use the first keycode for the modifier. - log((CLOG_DEBUG2 "modifier 0x%04x is not active", bit)); + LOG((CLOG_DEBUG2 "modifier 0x%04x is not active", bit)); if (getBits(m_toggleModifierMask, bit) != 0) { - log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); + LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || (bit == m_numLockMask && m_numLockHalfDuplex)) { keystroke.m_press = True; @@ -664,9 +664,9 @@ log((CLOG_DEBUG1 "fix modifier %d", i)); // press/release, otherwise deactivate it with a // release. we must check each keycode for the // modifier if not a toggle. - log((CLOG_DEBUG2 "modifier 0x%04x is active", bit)); + LOG((CLOG_DEBUG2 "modifier 0x%04x is active", bit)); if (getBits(m_toggleModifierMask, bit) != 0) { - log((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); + LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || (bit == m_numLockMask && m_numLockHalfDuplex)) { keystroke.m_press = False; @@ -773,7 +773,7 @@ log((CLOG_DEBUG1 "fix modifier %d", i)); } } -log((CLOG_DEBUG1 "final mask: 0x%04x", mask)); +LOG((CLOG_DEBUG1 "final mask: 0x%04x", mask)); return mask; } @@ -1308,7 +1308,7 @@ CXWindowsSecondaryScreen::adjustForNumLock(KeySym keysym) const { if (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)) { // it's NumLock sensitive - log((CLOG_DEBUG2 "keypad key: NumLock %s", ((m_mask & m_numLockMask) != 0) ? "active" : "inactive")); + LOG((CLOG_DEBUG2 "keypad key: NumLock %s", ((m_mask & m_numLockMask) != 0) ? "active" : "inactive")); return true; } return false; @@ -1321,7 +1321,7 @@ CXWindowsSecondaryScreen::adjustForCapsLock(KeySym keysym) const XConvertCase(keysym, &lKey, &uKey); if (lKey != uKey) { // it's CapsLock sensitive - log((CLOG_DEBUG2 "case convertible: CapsLock %s", ((m_mask & m_capsLockMask) != 0) ? "active" : "inactive")); + LOG((CLOG_DEBUG2 "case convertible: CapsLock %s", ((m_mask & m_capsLockMask) != 0) ? "active" : "inactive")); return true; } return false; diff --git a/lib/http/CHTTPProtocol.cpp b/lib/http/CHTTPProtocol.cpp index c02198db..ae385d6d 100644 --- a/lib/http/CHTTPProtocol.cpp +++ b/lib/http/CHTTPProtocol.cpp @@ -128,7 +128,7 @@ CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) CString version; s >> request->m_method >> request->m_uri >> version; if (!s || request->m_uri.empty() || version.find("HTTP/") != 0) { - log((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); + LOG((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); throw XHTTP(400); } @@ -141,16 +141,16 @@ CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) s.get(dot); s >> request->m_minorVersion; if (!s || dot != '.') { - log((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); + LOG((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); throw XHTTP(400); } } if (!isValidToken(request->m_method)) { - log((CLOG_DEBUG1 "invalid HTTP method: %s", line.c_str())); + LOG((CLOG_DEBUG1 "invalid HTTP method: %s", line.c_str())); throw XHTTP(400); } if (request->m_majorVersion < 1 || request->m_minorVersion < 0) { - log((CLOG_DEBUG1 "invalid HTTP version: %s", line.c_str())); + LOG((CLOG_DEBUG1 "invalid HTTP version: %s", line.c_str())); throw XHTTP(400); } @@ -162,7 +162,7 @@ CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) if (request->m_majorVersion > 1 || (request->m_majorVersion == 1 && request->m_minorVersion >= 1)) { if (request->isHeader("Host") == 0) { - log((CLOG_DEBUG1 "Host header missing")); + LOG((CLOG_DEBUG1 "Host header missing")); throw XHTTP(400); } } @@ -174,7 +174,7 @@ CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) request->isHeader("Content-Length")) == (request->m_method == "GET" || request->m_method == "HEAD")) { - log((CLOG_DEBUG1 "HTTP method (%s)/body mismatch", request->m_method.c_str())); + LOG((CLOG_DEBUG1 "HTTP method (%s)/body mismatch", request->m_method.c_str())); throw XHTTP(400); } @@ -187,7 +187,7 @@ CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) if (!(header = request->getHeader("Transfer-Encoding")).empty()) { // we only understand "chunked" encodings if (!CStringUtil::CaselessCmp::equal(header, "chunked")) { - log((CLOG_DEBUG1 "unsupported Transfer-Encoding %s", header.c_str())); + LOG((CLOG_DEBUG1 "unsupported Transfer-Encoding %s", header.c_str())); throw XHTTP(501); } @@ -218,7 +218,7 @@ CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) s.exceptions(std::ios::goodbit); s >> length; if (!s) { - log((CLOG_DEBUG1 "cannot parse Content-Length", header.c_str())); + LOG((CLOG_DEBUG1 "cannot parse Content-Length", header.c_str())); throw XHTTP(400); } } @@ -232,7 +232,7 @@ CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) request->m_body = readBlock(stream, length, scratch); if (request->m_body.size() != length) { // length must match size of body - log((CLOG_DEBUG1 "Content-Length/actual length mismatch (%d vs %d)", length, request->m_body.size())); + LOG((CLOG_DEBUG1 "Content-Length/actual length mismatch (%d vs %d)", length, request->m_body.size())); throw XHTTP(400); } } @@ -559,7 +559,7 @@ CHTTPProtocol::readChunk(IInputStream* stream, s.exceptions(std::ios::goodbit); s >> std::hex >> size; if (!s) { - log((CLOG_DEBUG1 "cannot parse chunk size", line.c_str())); + LOG((CLOG_DEBUG1 "cannot parse chunk size", line.c_str())); throw XHTTP(400); } } @@ -578,14 +578,14 @@ CHTTPProtocol::readChunk(IInputStream* stream, // read size bytes CString data = readBlock(stream, size, tmpBuffer); if (data.size() != size) { - log((CLOG_DEBUG1 "expected/actual chunk size mismatch", size, data.size())); + LOG((CLOG_DEBUG1 "expected/actual chunk size mismatch", size, data.size())); throw XHTTP(400); } // read an discard CRLF line = readLine(stream, tmpBuffer); if (!line.empty()) { - log((CLOG_DEBUG1 "missing CRLF after chunk")); + LOG((CLOG_DEBUG1 "missing CRLF after chunk")); throw XHTTP(400); } @@ -614,7 +614,7 @@ CHTTPProtocol::readHeaders(IInputStream* stream, // throw. if (line[0] == ' ' || line[0] == '\t') { if (name.empty()) { - log((CLOG_DEBUG1 "first header is a continuation")); + LOG((CLOG_DEBUG1 "first header is a continuation")); throw XHTTP(400); } request->appendHeader(name, line); @@ -628,7 +628,7 @@ CHTTPProtocol::readHeaders(IInputStream* stream, s.exceptions(std::ios::goodbit); std::getline(s, name, ':'); if (!s || !isValidToken(name)) { - log((CLOG_DEBUG1 "invalid header: %s", line.c_str())); + LOG((CLOG_DEBUG1 "invalid header: %s", line.c_str())); throw XHTTP(400); } std::getline(s, value); diff --git a/lib/mt/CMutex.cpp b/lib/mt/CMutex.cpp index 94b654b0..6e51ea7e 100644 --- a/lib/mt/CMutex.cpp +++ b/lib/mt/CMutex.cpp @@ -61,7 +61,7 @@ CMutex::fini() { pthread_mutex_t* mutex = reinterpret_cast(m_mutex); int status = pthread_mutex_destroy(mutex); - logc(status != 0, (CLOG_ERR "pthread_mutex_destroy status %d", status)); + LOGC(status != 0, (CLOG_ERR "pthread_mutex_destroy status %d", status)); assert(status == 0); delete mutex; } @@ -86,7 +86,7 @@ CMutex::lock() const break; default: - log((CLOG_ERR "pthread_mutex_lock status %d", status)); + LOG((CLOG_ERR "pthread_mutex_lock status %d", status)); assert(0 && "unexpected error"); } } @@ -107,7 +107,7 @@ CMutex::unlock() const break; default: - log((CLOG_ERR "pthread_mutex_unlock status %d", status)); + LOG((CLOG_ERR "pthread_mutex_unlock status %d", status)); assert(0 && "unexpected error"); } } diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp index eb5ca957..5afd3122 100644 --- a/lib/mt/CThread.cpp +++ b/lib/mt/CThread.cpp @@ -77,7 +77,7 @@ void CThread::exit(void* result) { CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - log((CLOG_DEBUG1 "throw exit on thread %p", currentRep.operator->())); + LOG((CLOG_DEBUG1 "throw exit on thread %p", currentRep.operator->())); throw XThreadExit(result); } diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp index 4c15acfe..f79a2787 100644 --- a/lib/mt/CThreadRep.cpp +++ b/lib/mt/CThreadRep.cpp @@ -296,23 +296,23 @@ CThreadRep::doThreadFunc() void* result = NULL; try { // go - log((CLOG_DEBUG1 "thread %p entry", this)); + LOG((CLOG_DEBUG1 "thread %p entry", this)); m_job->run(); - log((CLOG_DEBUG1 "thread %p exit", this)); + LOG((CLOG_DEBUG1 "thread %p exit", this)); } catch (XThreadCancel&) { // client called cancel() - log((CLOG_DEBUG1 "caught cancel on thread %p", this)); + LOG((CLOG_DEBUG1 "caught cancel on thread %p", this)); } catch (XThreadExit& e) { // client called exit() result = e.m_result; - log((CLOG_DEBUG1 "caught exit on thread %p", this)); + LOG((CLOG_DEBUG1 "caught exit on thread %p", this)); } catch (...) { - log((CLOG_DEBUG1 "exception on thread %p", this)); + LOG((CLOG_DEBUG1 "exception on thread %p", this)); // note -- don't catch (...) to avoid masking bugs delete m_job; throw; @@ -385,7 +385,7 @@ CThreadRep::cancel() } // break out of system calls - log((CLOG_DEBUG1 "cancel thread %p", this)); + LOG((CLOG_DEBUG1 "cancel thread %p", this)); pthread_kill(m_thread, SIGWAKEUP); } @@ -406,7 +406,7 @@ CThreadRep::testCancel() } // start cancel - log((CLOG_DEBUG1 "throw cancel on thread %p", this)); + LOG((CLOG_DEBUG1 "throw cancel on thread %p", this)); throw XThreadCancel(); } @@ -556,7 +556,7 @@ CThreadRep::sleep( void CThreadRep::cancel() { - log((CLOG_DEBUG1 "cancel thread %p", this)); + LOG((CLOG_DEBUG1 "cancel thread %p", this)); SetEvent(m_cancel); } @@ -581,7 +581,7 @@ CThreadRep::testCancel() } // start cancel - log((CLOG_DEBUG1 "throw cancel on thread %p", this)); + LOG((CLOG_DEBUG1 "throw cancel on thread %p", this)); throw XThreadCancel(); } diff --git a/lib/mt/CTimerThread.cpp b/lib/mt/CTimerThread.cpp index 0d3e0b36..91355bc7 100644 --- a/lib/mt/CTimerThread.cpp +++ b/lib/mt/CTimerThread.cpp @@ -37,10 +37,10 @@ CTimerThread::CTimerThread(double timeout) : m_timeout(timeout) CTimerThread::~CTimerThread() { if (m_timingThread != NULL) { - log((CLOG_DEBUG1 "cancelling timeout")); + LOG((CLOG_DEBUG1 "cancelling timeout")); m_timingThread->cancel(); m_timingThread->wait(); - log((CLOG_DEBUG1 "cancelled timeout")); + LOG((CLOG_DEBUG1 "cancelled timeout")); delete m_timingThread; delete m_callingThread; } @@ -49,8 +49,8 @@ CTimerThread::~CTimerThread() void CTimerThread::timer(void*) { - log((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); + LOG((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); CThread::sleep(m_timeout); - log((CLOG_DEBUG1 "timeout")); + LOG((CLOG_DEBUG1 "timeout")); m_callingThread->cancel(); } diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp index 9a74f553..918095c9 100644 --- a/lib/net/CNetwork.cpp +++ b/lib/net/CNetwork.cpp @@ -85,7 +85,7 @@ CNetwork::init() // try winsock 2 HMODULE module = (HMODULE)::LoadLibrary("ws2_32.dll"); if (module == NULL) { - log((CLOG_NOTE "ws2_32.dll not found")); + LOG((CLOG_NOTE "ws2_32.dll not found")); } else { try { @@ -93,14 +93,14 @@ CNetwork::init() return; } catch (XNetwork& e) { - log((CLOG_NOTE "ws2_32.dll error: %s", e.what())); + LOG((CLOG_NOTE "ws2_32.dll error: %s", e.what())); } } // try winsock 1 module = (HMODULE)::LoadLibrary("wsock32.dll"); if (module == NULL) { - log((CLOG_NOTE "wsock32.dll not found")); + LOG((CLOG_NOTE "wsock32.dll not found")); } else { try { @@ -108,7 +108,7 @@ CNetwork::init() return; } catch (XNetwork& e) { - log((CLOG_NOTE "wsock32.dll error: %s", e.what())); + LOG((CLOG_NOTE "wsock32.dll error: %s", e.what())); } } diff --git a/lib/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp index b746f56e..32aad8e1 100644 --- a/lib/platform/CMSWindowsClipboard.cpp +++ b/lib/platform/CMSWindowsClipboard.cpp @@ -38,10 +38,10 @@ CMSWindowsClipboard::~CMSWindowsClipboard() bool CMSWindowsClipboard::empty() { - log((CLOG_DEBUG "empty clipboard")); + LOG((CLOG_DEBUG "empty clipboard")); if (!EmptyClipboard()) { - log((CLOG_DEBUG "failed to grab clipboard")); + LOG((CLOG_DEBUG "failed to grab clipboard")); return false; } @@ -51,7 +51,7 @@ CMSWindowsClipboard::empty() void CMSWindowsClipboard::add(EFormat format, const CString& data) { - log((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); + LOG((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); // convert data to win32 form for (ConverterList::const_iterator index = m_converters.begin(); @@ -76,10 +76,10 @@ CMSWindowsClipboard::add(EFormat format, const CString& data) bool CMSWindowsClipboard::open(Time time) const { - log((CLOG_DEBUG "open clipboard")); + LOG((CLOG_DEBUG "open clipboard")); if (!OpenClipboard(m_window)) { - log((CLOG_WARN "failed to open clipboard")); + LOG((CLOG_WARN "failed to open clipboard")); return false; } @@ -91,7 +91,7 @@ CMSWindowsClipboard::open(Time time) const void CMSWindowsClipboard::close() const { - log((CLOG_DEBUG "close clipboard")); + LOG((CLOG_DEBUG "close clipboard")); CloseClipboard(); } diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 56f054f4..ee33dead 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -149,7 +149,7 @@ CMSWindowsScreen::open() assert(s_instance != NULL); assert(m_class == 0); - log((CLOG_DEBUG "opening display")); + LOG((CLOG_DEBUG "opening display")); // create the transparent cursor createBlankCursor(); @@ -253,7 +253,7 @@ CMSWindowsScreen::close() m_cursor = NULL; } - log((CLOG_DEBUG "closed display")); + LOG((CLOG_DEBUG "closed display")); } bool @@ -349,7 +349,7 @@ CMSWindowsScreen::syncDesktop() // change calling thread's desktop if (!m_is95Family) { if (SetThreadDesktop(m_desk) == 0) { -// log((CLOG_WARN "failed to set desktop: %d", GetLastError())); +// LOG((CLOG_WARN "failed to set desktop: %d", GetLastError())); } } @@ -413,7 +413,7 @@ CMSWindowsScreen::updateScreenShape() m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); - log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + LOG((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); // check for multiple monitors m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || @@ -504,7 +504,7 @@ CMSWindowsScreen::onEvent(CEvent* event) return true; case WM_DRAWCLIPBOARD: - log((CLOG_DEBUG "clipboard was taken")); + LOG((CLOG_DEBUG "clipboard was taken")); // first pass it on if (m_nextClipboardWindow != NULL) { @@ -615,7 +615,7 @@ CMSWindowsScreen::switchDesktop(HDESK desk) // if no new desktop then we're done if (desk == NULL) { - log((CLOG_INFO "disconnecting desktop")); + LOG((CLOG_INFO "disconnecting desktop")); return true; } @@ -629,7 +629,7 @@ CMSWindowsScreen::switchDesktop(HDESK desk) // set the desktop. can only do this when there are no windows // and hooks on the current desktop owned by this thread. if (SetThreadDesktop(desk) == 0) { - log((CLOG_ERR "failed to set desktop: %d", GetLastError())); + LOG((CLOG_ERR "failed to set desktop: %d", GetLastError())); if (!m_is95Family) { CloseDesktop(desk); } @@ -648,7 +648,7 @@ CMSWindowsScreen::switchDesktop(HDESK desk) getInstance(), NULL); if (m_window == NULL) { - log((CLOG_ERR "failed to create window: %d", GetLastError())); + LOG((CLOG_ERR "failed to create window: %d", GetLastError())); if (!m_is95Family) { CloseDesktop(desk); } @@ -675,7 +675,7 @@ CMSWindowsScreen::switchDesktop(HDESK desk) // save new desktop m_desk = desk; m_deskName = getDesktopName(m_desk); - log((CLOG_INFO "switched to desktop \"%s\"", m_deskName.c_str())); + LOG((CLOG_INFO "switched to desktop \"%s\"", m_deskName.c_str())); // let client prepare the window m_eventHandler->postCreateWindow(m_window); diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index d08c9b9e..7ee439d7 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -73,7 +73,7 @@ CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) HWND hwnd = findScreenSaver(); if (hwnd == NULL) { // didn't start - log((CLOG_DEBUG "can't find screen saver window")); + LOG((CLOG_DEBUG "can't find screen saver window")); return false; } @@ -83,7 +83,7 @@ CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID); if (process == NULL) { // didn't start - log((CLOG_DEBUG "can't open screen saver process")); + LOG((CLOG_DEBUG "can't open screen saver process")); return false; } @@ -273,7 +273,7 @@ CMSWindowsScreenSaver::watchProcess(HANDLE process) // watch new process in another thread if (process != NULL) { - log((CLOG_DEBUG "watching screen saver process")); + LOG((CLOG_DEBUG "watching screen saver process")); m_process = process; m_watch = new CThread(new TMethodJob(this, &CMSWindowsScreenSaver::watchProcessThread)); @@ -284,7 +284,7 @@ void CMSWindowsScreenSaver::unwatchProcess() { if (m_watch != NULL) { - log((CLOG_DEBUG "stopped watching screen saver process")); + LOG((CLOG_DEBUG "stopped watching screen saver process")); m_watch->cancel(); m_watch->wait(); delete m_watch; @@ -304,7 +304,7 @@ CMSWindowsScreenSaver::watchProcessThread(void*) if (WaitForSingleObject(m_process, 50) == WAIT_OBJECT_0) { // process terminated. send screen saver deactivation // message. - log((CLOG_DEBUG "screen saver died")); + LOG((CLOG_DEBUG "screen saver died")); PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); return; } diff --git a/lib/platform/CWin32Platform.cpp b/lib/platform/CWin32Platform.cpp index 29dece5b..fc1fa4ae 100644 --- a/lib/platform/CWin32Platform.cpp +++ b/lib/platform/CWin32Platform.cpp @@ -45,7 +45,7 @@ CWin32Platform::isWindows95Family() OSVERSIONINFO version; version.dwOSVersionInfoSize = sizeof(version); if (GetVersionEx(&version) == 0) { - log((CLOG_WARN "cannot determine OS: %d", GetLastError())); + LOG((CLOG_WARN "cannot determine OS: %d", GetLastError())); return true; } return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); @@ -103,7 +103,7 @@ CWin32Platform::installDaemon(const char* name, const char* description, HKEY key = isWindows95Family() ? open95ServicesKey() : openUserStartupKey(); if (key == NULL) { - log((CLOG_ERR "cannot open registry key", GetLastError())); + LOG((CLOG_ERR "cannot open registry key", GetLastError())); return false; } @@ -128,7 +128,7 @@ CWin32Platform::installDaemon(const char* name, const char* description, // open service manager SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); if (mgr == NULL) { - log((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); + LOG((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); return false; } @@ -156,7 +156,7 @@ CWin32Platform::installDaemon(const char* name, const char* description, else { // FIXME -- handle ERROR_SERVICE_EXISTS - log((CLOG_ERR "CreateService failed with %d", GetLastError())); + LOG((CLOG_ERR "CreateService failed with %d", GetLastError())); CloseServiceHandle(mgr); return false; } @@ -199,7 +199,7 @@ CWin32Platform::uninstallDaemon(const char* name, bool allUsers) HKEY key = isWindows95Family() ? open95ServicesKey() : openUserStartupKey(); if (key == NULL) { - log((CLOG_ERR "cannot open registry key", GetLastError())); + LOG((CLOG_ERR "cannot open registry key", GetLastError())); return kAlready; } @@ -225,7 +225,7 @@ CWin32Platform::uninstallDaemon(const char* name, bool allUsers) // open service manager SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); if (mgr == NULL) { - log((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); + LOG((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); return kFailed; } @@ -234,7 +234,7 @@ CWin32Platform::uninstallDaemon(const char* name, bool allUsers) SC_HANDLE service = OpenService(mgr, name, DELETE); if (service == NULL) { const DWORD e = GetLastError(); - log((CLOG_ERR "OpenService failed with %d", e)); + LOG((CLOG_ERR "OpenService failed with %d", e)); result = (e == ERROR_SERVICE_DOES_NOT_EXIST) ? kAlready : kFailed; } @@ -278,7 +278,7 @@ CWin32Platform::daemonize(const char* name, DaemonFunc func) // user logs off. HINSTANCE kernel = LoadLibrary("kernel32.dll"); if (kernel == NULL) { - log((CLOG_ERR "LoadLibrary failed with %d", GetLastError())); + LOG((CLOG_ERR "LoadLibrary failed with %d", GetLastError())); return -1; } RegisterServiceProcessT RegisterServiceProcess = @@ -286,12 +286,12 @@ CWin32Platform::daemonize(const char* name, DaemonFunc func) GetProcAddress(kernel, _T("RegisterServiceProcess"))); if (RegisterServiceProcess == NULL) { - log((CLOG_ERR "can't lookup RegisterServiceProcess: %d", GetLastError())); + LOG((CLOG_ERR "can't lookup RegisterServiceProcess: %d", GetLastError())); FreeLibrary(kernel); return -1; } if (RegisterServiceProcess(NULL, 1) == 0) { - log((CLOG_ERR "RegisterServiceProcess failed with %d", GetLastError())); + LOG((CLOG_ERR "RegisterServiceProcess failed with %d", GetLastError())); FreeLibrary(kernel); return -1; } @@ -320,7 +320,7 @@ CWin32Platform::daemonize(const char* name, DaemonFunc func) s_daemonPlatform = NULL; return m_daemonResult; } - log((CLOG_ERR "StartServiceCtrlDispatcher failed with %d", GetLastError())); + LOG((CLOG_ERR "StartServiceCtrlDispatcher failed with %d", GetLastError())); s_daemonPlatform = NULL; return -1; } @@ -943,7 +943,7 @@ CWin32Platform::serviceHandler(DWORD ctrl) break; default: - log((CLOG_WARN "unknown service command: %d", ctrl)); + LOG((CLOG_WARN "unknown service command: %d", ctrl)); // fall through case SERVICE_CONTROL_INTERROGATE: diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 39418dc8..32ae8cde 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -93,7 +93,7 @@ CXWindowsClipboard::~CXWindowsClipboard() void CXWindowsClipboard::lost(Time time) { - log((CLOG_DEBUG "lost clipboard %d ownership at %d", m_id, time)); + LOG((CLOG_DEBUG "lost clipboard %d ownership at %d", m_id, time)); if (m_owner) { m_owner = false; m_timeLost = time; @@ -109,7 +109,7 @@ CXWindowsClipboard::addRequest(Window owner, Window requestor, // at the given time. bool success = false; if (owner == m_window) { - log((CLOG_DEBUG1 "request for clipboard %d, target %d by 0x%08x (property=%d)", m_selection, target, requestor, property)); + LOG((CLOG_DEBUG1 "request for clipboard %d, target %d by 0x%08x (property=%d)", m_selection, target, requestor, property)); if (wasOwnedAtTime(time)) { if (target == m_atomMultiple) { // add a multiple request. property may not be None @@ -126,13 +126,13 @@ CXWindowsClipboard::addRequest(Window owner, Window requestor, } } else { - log((CLOG_DEBUG1 "failed, not owned at time %d", time)); + LOG((CLOG_DEBUG1 "failed, not owned at time %d", time)); } } if (!success) { // send failure - log((CLOG_DEBUG1 "failed")); + LOG((CLOG_DEBUG1 "failed")); insertReply(new CReply(requestor, target, time)); } @@ -180,14 +180,14 @@ CXWindowsClipboard::addSimpleRequest(Window requestor, if (type != None) { // success - log((CLOG_DEBUG1 "success")); + LOG((CLOG_DEBUG1 "success")); insertReply(new CReply(requestor, target, time, property, data, type, format)); return true; } else { // failure - log((CLOG_DEBUG1 "failed")); + LOG((CLOG_DEBUG1 "failed")); insertReply(new CReply(requestor, target, time)); return false; } @@ -202,7 +202,7 @@ CXWindowsClipboard::processRequest(Window requestor, // unknown requestor window return false; } - log((CLOG_DEBUG1 "received property %d delete from 0x08%x", property, requestor)); + LOG((CLOG_DEBUG1 "received property %d delete from 0x08%x", property, requestor)); // find the property in the known requests. it should be the // first property but we'll check 'em all if we have to. @@ -257,12 +257,12 @@ CXWindowsClipboard::empty() { assert(m_open); - log((CLOG_DEBUG "empty clipboard %d", m_id)); + LOG((CLOG_DEBUG "empty clipboard %d", m_id)); // assert ownership of clipboard XSetSelectionOwner(m_display, m_selection, m_window, m_time); if (XGetSelectionOwner(m_display, m_selection) != m_window) { - log((CLOG_DEBUG "failed to grab clipboard %d", m_id)); + LOG((CLOG_DEBUG "failed to grab clipboard %d", m_id)); return false; } @@ -280,7 +280,7 @@ CXWindowsClipboard::empty() // we're the owner now m_owner = true; - log((CLOG_DEBUG "grabbed clipboard %d", m_id)); + LOG((CLOG_DEBUG "grabbed clipboard %d", m_id)); return true; } @@ -291,7 +291,7 @@ CXWindowsClipboard::add(EFormat format, const CString& data) assert(m_open); assert(m_owner); - log((CLOG_DEBUG "add %d bytes to clipboard %d format: %d", data.size(), m_id, format)); + LOG((CLOG_DEBUG "add %d bytes to clipboard %d format: %d", data.size(), m_id, format)); m_data[format] = data; m_added[format] = true; @@ -304,7 +304,7 @@ CXWindowsClipboard::open(Time time) const { assert(!m_open); - log((CLOG_DEBUG "open clipboard %d", m_id)); + LOG((CLOG_DEBUG "open clipboard %d", m_id)); // assume not motif m_motif = false; @@ -318,7 +318,7 @@ CXWindowsClipboard::open(Time time) const // check if motif owns the selection. unlock motif clipboard // if it does not. m_motif = motifOwnsClipboard(); - log((CLOG_DEBUG1 "motif does %sown clipboard", m_motif ? "" : "not ")); + LOG((CLOG_DEBUG1 "motif does %sown clipboard", m_motif ? "" : "not ")); if (!m_motif) { motifUnlockClipboard(); } @@ -339,7 +339,7 @@ CXWindowsClipboard::close() const { assert(m_open); - log((CLOG_DEBUG "close clipboard %d", m_id)); + LOG((CLOG_DEBUG "close clipboard %d", m_id)); // unlock clipboard if (m_motif) { @@ -397,14 +397,14 @@ CXWindowsClipboard::getConverter(Atom target, bool onlyIfNotAdded) const } } if (converter == NULL) { - log((CLOG_DEBUG1 " no converter for target %d", target)); + LOG((CLOG_DEBUG1 " no converter for target %d", target)); return NULL; } // optionally skip already handled targets if (onlyIfNotAdded) { if (m_added[converter->getFormat()]) { - log((CLOG_DEBUG1 " skipping handled format %d", converter->getFormat())); + LOG((CLOG_DEBUG1 " skipping handled format %d", converter->getFormat())); return NULL; } } @@ -484,7 +484,7 @@ CXWindowsClipboard::doFillCache() void CXWindowsClipboard::icccmFillCache() { - log((CLOG_DEBUG "ICCCM fill clipboard %d", m_id)); + LOG((CLOG_DEBUG "ICCCM fill clipboard %d", m_id)); // see if we can get the list of available formats from the selection. // if not then use a default list of formats. @@ -493,7 +493,7 @@ CXWindowsClipboard::icccmFillCache() CString data; if (!icccmGetSelection(atomTargets, &target, &data) || target != m_atomAtom) { - log((CLOG_DEBUG1 "selection doesn't support TARGETS")); + LOG((CLOG_DEBUG1 "selection doesn't support TARGETS")); data = ""; target = XA_STRING; @@ -529,7 +529,7 @@ CXWindowsClipboard::icccmFillCache() Atom actualTarget; CString targetData; if (!icccmGetSelection(target, &actualTarget, &targetData)) { - log((CLOG_DEBUG1 " no data for target %d", target)); + LOG((CLOG_DEBUG1 " no data for target %d", target)); continue; } @@ -537,7 +537,7 @@ CXWindowsClipboard::icccmFillCache() IClipboard::EFormat format = converter->getFormat(); m_data[format] = converter->toIClipboard(targetData); m_added[format] = true; - log((CLOG_DEBUG " added format %d for target %d", format, target)); + LOG((CLOG_DEBUG " added format %d for target %d", format, target)); } } @@ -552,12 +552,12 @@ CXWindowsClipboard::icccmGetSelection(Atom target, CICCCMGetClipboard getter(m_window, m_time, m_atomData); if (!getter.readClipboard(m_display, m_selection, target, actualTarget, data)) { - log((CLOG_DEBUG1 "can't get data for selection target %d", target)); - logc(getter.m_error, (CLOG_WARN "ICCCM violation by clipboard owner")); + LOG((CLOG_DEBUG1 "can't get data for selection target %d", target)); + LOGC(getter.m_error, (CLOG_WARN "ICCCM violation by clipboard owner")); return false; } else if (*actualTarget == None) { - log((CLOG_DEBUG1 "selection conversion failed for target %d", target)); + LOG((CLOG_DEBUG1 "selection conversion failed for target %d", target)); return false; } return true; @@ -571,12 +571,12 @@ CXWindowsClipboard::icccmGetTime() const if (icccmGetSelection(m_atomTimestamp, &actualTarget, &data) && actualTarget == m_atomInteger) { Time time = *reinterpret_cast(data.data()); - log((CLOG_DEBUG1 "got ICCCM time %d", time)); + LOG((CLOG_DEBUG1 "got ICCCM time %d", time)); return time; } else { // no timestamp - log((CLOG_DEBUG1 "can't get ICCCM time")); + LOG((CLOG_DEBUG1 "can't get ICCCM time")); return 0; } } @@ -587,7 +587,7 @@ CXWindowsClipboard::motifLockClipboard() const // fail if anybody owns the lock (even us, so this is non-recursive) Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); if (lockOwner != None) { - log((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner)); + LOG((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner)); return false; } @@ -599,18 +599,18 @@ CXWindowsClipboard::motifLockClipboard() const XSetSelectionOwner(m_display, m_atomMotifClipLock, m_window, time); lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); if (lockOwner != m_window) { - log((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner)); + LOG((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner)); return false; } - log((CLOG_DEBUG1 "locked motif clipboard")); + LOG((CLOG_DEBUG1 "locked motif clipboard")); return true; } void CXWindowsClipboard::motifUnlockClipboard() const { - log((CLOG_DEBUG1 "unlocked motif clipboard")); + LOG((CLOG_DEBUG1 "unlocked motif clipboard")); // fail if we don't own the lock Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock); @@ -662,7 +662,7 @@ CXWindowsClipboard::motifOwnsClipboard() const void CXWindowsClipboard::motifFillCache() { - log((CLOG_DEBUG "Motif fill clipboard %d", m_id)); + LOG((CLOG_DEBUG "Motif fill clipboard %d", m_id)); // get the Motif clipboard header property from the root window Atom target; @@ -767,7 +767,7 @@ CXWindowsClipboard::motifFillCache() Atom actualTarget; CString targetData; if (!motifGetSelection(motifFormat, &actualTarget, &targetData)) { - log((CLOG_DEBUG1 " no data for target %d", target)); + LOG((CLOG_DEBUG1 " no data for target %d", target)); continue; } @@ -775,7 +775,7 @@ CXWindowsClipboard::motifFillCache() IClipboard::EFormat format = converter->getFormat(); m_data[format] = converter->toIClipboard(targetData); m_added[format] = true; - log((CLOG_DEBUG " added format %d for target %d", format, target)); + LOG((CLOG_DEBUG " added format %d for target %d", format, target)); } } @@ -957,14 +957,14 @@ CXWindowsClipboard::sendReply(CReply* reply) // bail out immediately if reply is done if (reply->m_done) { - log((CLOG_DEBUG1 "clipboard: finished reply to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + LOG((CLOG_DEBUG1 "clipboard: finished reply to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); return true; } // start in failed state if property is None bool failed = (reply->m_property == None); if (!failed) { - log((CLOG_DEBUG1 "clipboard: setting property on 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + LOG((CLOG_DEBUG1 "clipboard: setting property on 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); // send using INCR if already sending incrementally or if reply // is too large, otherwise just send it. @@ -1013,7 +1013,7 @@ CXWindowsClipboard::sendReply(CReply* reply) // the final zero-length property. // FIXME -- how do you gracefully cancel an incremental transfer? if (failed) { - log((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + LOG((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); reply->m_done = true; if (reply->m_property != None) { CXWindowsUtil::CErrorLock lock(m_display); @@ -1043,7 +1043,7 @@ CXWindowsClipboard::sendReply(CReply* reply) // send notification if we haven't yet if (!reply->m_replied) { - log((CLOG_DEBUG1 "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); + LOG((CLOG_DEBUG1 "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); reply->m_replied = true; // HACK -- work around apparent bug in lesstif, which doesn't @@ -1219,7 +1219,7 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, assert(actualTarget != NULL); assert(data != NULL); - log((CLOG_DEBUG1 "request selection=%d, target=%d, window=%x", selection, target, m_requestor)); + LOG((CLOG_DEBUG1 "request selection=%d, target=%d, window=%x", selection, target, m_requestor)); // save output pointers m_actualTarget = actualTarget; @@ -1296,7 +1296,7 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, XSelectInput(display, m_requestor, attr.your_event_mask); // return success or failure - log((CLOG_DEBUG1 "request %s", m_failed ? "failed" : "succeeded")); + LOG((CLOG_DEBUG1 "request %s", m_failed ? "failed" : "succeeded")); return !m_failed; } @@ -1387,14 +1387,14 @@ CXWindowsClipboard::CICCCMGetClipboard::processEvent( else if (m_incr) { // if first incremental chunk then save target if (oldSize == 0) { - log((CLOG_DEBUG1 " INCR first chunk, target %d", target)); + LOG((CLOG_DEBUG1 " INCR first chunk, target %d", target)); *m_actualTarget = target; } // secondary chunks must have the same target else { if (target != *m_actualTarget) { - log((CLOG_WARN " INCR target mismatch")); + LOG((CLOG_WARN " INCR target mismatch")); m_failed = true; m_error = true; } @@ -1402,20 +1402,20 @@ CXWindowsClipboard::CICCCMGetClipboard::processEvent( // note if this is the final chunk if (m_data->size() == oldSize) { - log((CLOG_DEBUG1 " INCR final chunk: %d bytes total", m_data->size())); + LOG((CLOG_DEBUG1 " INCR final chunk: %d bytes total", m_data->size())); m_done = true; } } // not incremental; save the target. else { - log((CLOG_DEBUG1 " target %d", target)); + LOG((CLOG_DEBUG1 " target %d", target)); *m_actualTarget = target; m_done = true; } // this event has been processed - logc(!m_incr, (CLOG_DEBUG1 " got data, %d bytes", m_data->size())); + LOGC(!m_incr, (CLOG_DEBUG1 " got data, %d bytes", m_data->size())); return true; } diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 225d9c4e..920c98de 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -208,7 +208,7 @@ CXWindowsScreen::open() } // open the display - log((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); + LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); m_display = XOpenDisplay(display); if (m_display == NULL) { throw XScreenUnavailable(60.0); @@ -337,7 +337,7 @@ CXWindowsScreen::close() if (m_display != NULL) { XCloseDisplay(m_display); m_display = NULL; - log((CLOG_DEBUG "closed display")); + LOG((CLOG_DEBUG "closed display")); } XSetIOErrorHandler(NULL); } @@ -495,7 +495,7 @@ CXWindowsScreen::updateScreenShape() m_y = 0; m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); - log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + LOG((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); } bool @@ -519,7 +519,7 @@ CXWindowsScreen::onPreDispatch(CEvent* event) // selection owner. report that to the receiver. ClipboardID id = getClipboardID(xevent->xselectionclear.selection); if (id != kClipboardEnd) { - log((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); + LOG((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); m_clipboard[id]->lost(xevent->xselectionclear.time); m_receiver->onGrabClipboard(id); return true; @@ -724,10 +724,10 @@ CXWindowsScreen::ioErrorHandler(Display*) // handle not having a Display* anymore. we'll simply log the // error, notify the subclass (which must not use the display // so we set it to NULL), and exit. - log((CLOG_WARN "X display has unexpectedly disconnected")); + LOG((CLOG_WARN "X display has unexpectedly disconnected")); s_screen->m_display = NULL; s_screen->m_receiver->onError(); - log((CLOG_CRIT "quiting due to X display disconnection")); + LOG((CLOG_CRIT "quiting due to X display disconnection")); exit(17); } diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index 3581ed6a..e32f13c3 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -65,7 +65,7 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( CWDontPropagate | CWEventMask | CWOverrideRedirect, &attr); - log((CLOG_DEBUG "xscreensaver sink window is 0x%08x", m_xscreensaverSink)); + LOG((CLOG_DEBUG "xscreensaver sink window is 0x%08x", m_xscreensaverSink)); // watch top-level windows for changes { @@ -77,7 +77,7 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( m_rootEventMask = attr.your_event_mask; XSelectInput(m_display, root, m_rootEventMask | SubstructureNotifyMask); if (error) { - log((CLOG_DEBUG "didn't set root event mask")); + LOG((CLOG_DEBUG "didn't set root event mask")); m_rootEventMask = 0; } } @@ -136,7 +136,7 @@ CXWindowsScreenSaver::onPreDispatch(const XEvent* xevent) case DestroyNotify: if (xevent->xdestroywindow.window == m_xscreensaver) { // xscreensaver is gone - log((CLOG_DEBUG "xscreensaver died")); + LOG((CLOG_DEBUG "xscreensaver died")); setXScreenSaver(None); return true; } @@ -302,7 +302,7 @@ CXWindowsScreenSaver::findXScreenSaver() void CXWindowsScreenSaver::setXScreenSaver(Window window) { - log((CLOG_DEBUG "xscreensaver window: 0x%08x", window)); + LOG((CLOG_DEBUG "xscreensaver window: 0x%08x", window)); // save window m_xscreensaver = window; @@ -342,7 +342,7 @@ void CXWindowsScreenSaver::setXScreenSaverActive(bool activated) { if (m_xscreensaverActive != activated) { - log((CLOG_DEBUG "xscreensaver %s", activated ? "activated" : "deactivated")); + LOG((CLOG_DEBUG "xscreensaver %s", activated ? "activated" : "deactivated")); m_xscreensaverActive = activated; sendNotify(activated); } @@ -363,7 +363,7 @@ CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2) event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; - log((CLOG_DEBUG "send xscreensaver command: %d %d %d", (long)cmd, arg1, arg2)); + LOG((CLOG_DEBUG "send xscreensaver command: %d %d %d", (long)cmd, arg1, arg2)); bool error = false; CXWindowsUtil::CErrorLock lock(m_display, &error); XSendEvent(m_display, m_xscreensaver, False, 0, &event); diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 376febe3..63cd1ca7 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -99,11 +99,11 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, } if (result == Success) { - log((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); + LOG((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); return true; } else { - log((CLOG_DEBUG1 "can't read property %d on window 0x%08x", property, window)); + LOG((CLOG_DEBUG1 "can't read property %d on window 0x%08x", property, window)); return false; } } @@ -265,12 +265,12 @@ CXWindowsUtil::CErrorLock::internalHandler(Display* display, XErrorEvent* event) void CXWindowsUtil::CErrorLock::ignoreHandler(Display*, XErrorEvent* e, void*) { - log((CLOG_DEBUG1 "ignoring X error: %d", e->error_code)); + LOG((CLOG_DEBUG1 "ignoring X error: %d", e->error_code)); } void CXWindowsUtil::CErrorLock::saveHandler(Display*, XErrorEvent* e, void* flag) { - log((CLOG_DEBUG1 "flagging X error: %d", e->error_code)); + LOG((CLOG_DEBUG1 "flagging X error: %d", e->error_code)); *reinterpret_cast(flag) = true; } diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 656d4ccd..b21c8cfc 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -47,7 +47,7 @@ void CClientProxy1_0::open() { // send request - log((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str())); + LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgQInfo); getOutputStream()->flush(); @@ -86,14 +86,14 @@ CClientProxy1_0::mainLoop() // check if client hungup if (n == 0) { - log((CLOG_NOTE "client \"%s\" disconnected", getName().c_str())); + LOG((CLOG_NOTE "client \"%s\" disconnected", getName().c_str())); return; } // check if client has stopped sending heartbeats if (n == (UInt32)-1) { if (kHeartDeath >= 0.0 && heartTimer.getTime() > kHeartDeath) { - log((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); + LOG((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); return; } continue; @@ -104,14 +104,14 @@ CClientProxy1_0::mainLoop() // verify we got an entire code if (n != 4) { - log((CLOG_ERR "incomplete message from \"%s\": %d bytes", getName().c_str(), n)); + LOG((CLOG_ERR "incomplete message from \"%s\": %d bytes", getName().c_str(), n)); // client sent an incomplete message throw XBadClient(); } // parse message - log((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); if (memcmp(code, kMsgDInfo, 4) == 0) { recvInfo(true); } @@ -127,7 +127,7 @@ CClientProxy1_0::mainLoop() } // note -- more message handlers go here else { - log((CLOG_ERR "invalid message from client \"%s\"", getName().c_str())); + LOG((CLOG_ERR "invalid message from client \"%s\"", getName().c_str())); // unknown message throw XBadClient(); @@ -138,7 +138,7 @@ CClientProxy1_0::mainLoop() void CClientProxy1_0::close() { - log((CLOG_DEBUG1 "send close to \"%s\"", getName().c_str())); + LOG((CLOG_DEBUG1 "send close to \"%s\"", getName().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClose); // force the close to be sent before we return @@ -149,7 +149,7 @@ void CClientProxy1_0::enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, bool) { - log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getName().c_str(), xAbs, yAbs, seqNum, mask)); + LOG((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getName().c_str(), xAbs, yAbs, seqNum, mask)); CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs, seqNum, mask); } @@ -157,7 +157,7 @@ CClientProxy1_0::enter(SInt32 xAbs, SInt32 yAbs, bool CClientProxy1_0::leave() { - log((CLOG_DEBUG1 "send leave to \"%s\"", getName().c_str())); + LOG((CLOG_DEBUG1 "send leave to \"%s\"", getName().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCLeave); // we can never prevent the user from leaving @@ -173,7 +173,7 @@ CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) // this clipboard is now clean m_clipboardDirty[id] = false; - log((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); + LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); } } @@ -181,7 +181,7 @@ CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) void CClientProxy1_0::grabClipboard(ClipboardID id) { - log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getName().c_str())); + LOG((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getName().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); // this clipboard is now dirty @@ -199,56 +199,56 @@ CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty) void CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask) { - log((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); + LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); } void CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count) { - log((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count)); + LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); } void CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask) { - log((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); + LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); } void CClientProxy1_0::mouseDown(ButtonID button) { - log((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getName().c_str(), button)); + LOG((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getName().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); } void CClientProxy1_0::mouseUp(ButtonID button) { - log((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getName().c_str(), button)); + LOG((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getName().c_str(), button)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); } void CClientProxy1_0::mouseMove(SInt32 xAbs, SInt32 yAbs) { - log((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getName().c_str(), xAbs, yAbs)); + LOG((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getName().c_str(), xAbs, yAbs)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); } void CClientProxy1_0::mouseWheel(SInt32 delta) { - log((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), delta)); + LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), delta)); CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); } void CClientProxy1_0::screensaver(bool on) { - log((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getName().c_str(), on ? 1 : 0)); + LOG((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getName().c_str(), on ? 1 : 0)); CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } @@ -293,7 +293,7 @@ CClientProxy1_0::recvInfo(bool notify) SInt16 x, y, w, h, zoneSize, mx, my; CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, &x, &y, &w, &h, &zoneSize, &mx, &my); - log((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my)); + LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my)); // validate if (w <= 0 || h <= 0 || zoneSize < 0) { @@ -319,7 +319,7 @@ CClientProxy1_0::recvInfo(bool notify) } // acknowledge receipt - log((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); + LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCInfoAck); } @@ -331,7 +331,7 @@ CClientProxy1_0::recvClipboard() UInt32 seqNum; CString data; CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); - log((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, data.size())); + LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, data.size())); // validate if (id >= kClipboardEnd) { @@ -350,7 +350,7 @@ CClientProxy1_0::recvGrabClipboard() ClipboardID id; UInt32 seqNum; CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); - log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getName().c_str(), id, seqNum)); + LOG((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getName().c_str(), id, seqNum)); // validate if (id >= kClipboardEnd) { diff --git a/lib/server/CHTTPServer.cpp b/lib/server/CHTTPServer.cpp index c202d795..d3582ac4 100644 --- a/lib/server/CHTTPServer.cpp +++ b/lib/server/CHTTPServer.cpp @@ -80,13 +80,13 @@ CHTTPServer::processRequest(IDataSocket* socket) // send reply CHTTPProtocol::reply(socket->getOutputStream(), reply); - log((CLOG_INFO "HTTP reply %d for %s %s", reply.m_status, request->m_method.c_str(), request->m_uri.c_str())); + LOG((CLOG_INFO "HTTP reply %d for %s %s", reply.m_status, request->m_method.c_str(), request->m_uri.c_str())); // clean up delete request; } catch (XHTTP& e) { - log((CLOG_WARN "returning HTTP error %d %s for %s", e.getStatus(), e.getReason().c_str(), (request != NULL) ? request->m_uri.c_str() : "")); + LOG((CLOG_WARN "returning HTTP error %d %s for %s", e.getStatus(), e.getReason().c_str(), (request != NULL) ? request->m_uri.c_str() : "")); // clean up delete request; @@ -256,7 +256,7 @@ CHTTPServer::doProcessPostEditMap(CHTTPRequest& request, CHTTPReply& reply) // parse the result CHTTPProtocol::CFormParts parts; if (!CHTTPProtocol::parseFormData(request, parts)) { - log((CLOG_WARN "editmap: cannot parse form data")); + LOG((CLOG_WARN "editmap: cannot parse form data")); throw XHTTP(400); } @@ -270,7 +270,7 @@ CHTTPServer::doProcessPostEditMap(CHTTPRequest& request, CHTTPReply& reply) if (index == parts.end() || !parseXY(index->second, w, h) || w <= 0 || h <= 0) { - log((CLOG_WARN "editmap: cannot parse size or size is invalid")); + LOG((CLOG_WARN "editmap: cannot parse size or size is invalid")); throw XHTTP(400); } ScreenSet screenNames; @@ -297,7 +297,7 @@ CHTTPServer::doProcessPostEditMap(CHTTPRequest& request, CHTTPReply& reply) // already been seen. if (screenNames.count(name)) { // FIXME -- better error message - log((CLOG_WARN "editmap: duplicate name %s", name.c_str())); + LOG((CLOG_WARN "editmap: duplicate name %s", name.c_str())); throw XHTTP(400); } // FIXME -- check that name is legal @@ -315,12 +315,12 @@ CHTTPServer::doProcessPostEditMap(CHTTPRequest& request, CHTTPReply& reply) if (screenNames.empty()) { // no screens // FIXME -- need better no screens - log((CLOG_WARN "editmap: no screens")); + LOG((CLOG_WARN "editmap: no screens")); throw XHTTP(400); } if (!screens.isValid()) { // FIXME -- need better unconnected screens error - log((CLOG_WARN "editmap: unconnected screens")); + LOG((CLOG_WARN "editmap: unconnected screens")); throw XHTTP(400); } diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index 912cc219..05188335 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -40,7 +40,7 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( // load the hook library m_hookLibrary = LoadLibrary("synrgyhk"); if (m_hookLibrary == NULL) { - log((CLOG_ERR "failed to load hook library")); + LOG((CLOG_ERR "failed to load hook library")); throw XScreenOpenFailure(); } m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides"); @@ -57,7 +57,7 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( m_uninstall == NULL || m_init == NULL || m_cleanup == NULL) { - log((CLOG_ERR "invalid hook library")); + LOG((CLOG_ERR "invalid hook library")); FreeLibrary(m_hookLibrary); throw XScreenOpenFailure(); } @@ -192,11 +192,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) // key press const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); if (repeat >= 2) { - log((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); + LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); m_receiver->onKeyRepeat(key, mask, repeat); } else { - log((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x", key, mask)); + LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x", key, mask)); m_receiver->onKeyDown(key, mask); } @@ -205,7 +205,7 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) } else { // key release - log((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask)); + LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask)); m_receiver->onKeyUp(key, mask); // update key state @@ -213,7 +213,7 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) } } else { - log((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", msg->wParam, msg->lParam)); + LOG((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", msg->wParam, msg->lParam)); } } return true; @@ -233,7 +233,7 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: - log((CLOG_DEBUG1 "event: button press button=%d", button)); + LOG((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { m_receiver->onMouseDown(button); m_keys[s_vkButton[button]] |= 0x80; @@ -243,7 +243,7 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: - log((CLOG_DEBUG1 "event: button release button=%d", button)); + LOG((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { m_receiver->onMouseUp(button); m_keys[s_vkButton[button]] &= ~0x80; @@ -256,7 +256,7 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) case SYNERGY_MSG_MOUSE_WHEEL: // ignore message if posted prior to last mark change if (!ignore()) { - log((CLOG_DEBUG1 "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); + LOG((CLOG_DEBUG1 "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); m_receiver->onMouseWheel(msg->wParam); } return true; @@ -282,7 +282,7 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) } case SYNERGY_MSG_POST_WARP: - log((CLOG_WARN "unmatched post warp")); + LOG((CLOG_WARN "unmatched post warp")); return true; case SYNERGY_MSG_MOUSE_MOVE: @@ -413,7 +413,7 @@ CMSWindowsPrimaryScreen::onPreOpen() // initialize hook library m_threadID = GetCurrentThreadId(); if (m_init(m_threadID) == 0) { - log((CLOG_ERR "cannot initialize hook library")); + LOG((CLOG_ERR "cannot initialize hook library")); throw XScreenOpenFailure(); } } @@ -927,12 +927,12 @@ CMSWindowsPrimaryScreen::mapKey( mask &= ~(KeyModifierControl | KeyModifierAlt); } *maskOut = mask; - log((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask)); + LOG((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask)); // get the scan code and the extended keyboard flag UINT scanCode = static_cast((info & 0x00ff0000u) >> 16); int extended = ((info & 0x01000000) == 0) ? 0 : 1; - log((CLOG_DEBUG1 "key vk=%d ext=%d scan=%d", vkCode, extended, scanCode)); + LOG((CLOG_DEBUG1 "key vk=%d ext=%d scan=%d", vkCode, extended, scanCode)); // handle some keys via table lookup KeyID id = g_virtualKey[vkCode][extended]; diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 4a557fea..47279c5c 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -36,7 +36,7 @@ CPrimaryClient::CPrimaryClient(IPrimaryScreenFactory* screenFactory, assert(m_server != NULL); // create screen - log((CLOG_DEBUG1 "creating primary screen")); + LOG((CLOG_DEBUG1 "creating primary screen")); if (screenFactory != NULL) { m_screen = screenFactory->create(this, receiver); } @@ -47,7 +47,7 @@ CPrimaryClient::CPrimaryClient(IPrimaryScreenFactory* screenFactory, CPrimaryClient::~CPrimaryClient() { - log((CLOG_DEBUG1 "destroying primary screen")); + LOG((CLOG_DEBUG1 "destroying primary screen")); delete m_screen; } diff --git a/lib/server/CPrimaryScreen.cpp b/lib/server/CPrimaryScreen.cpp index 19fd9b1e..a9cd4297 100644 --- a/lib/server/CPrimaryScreen.cpp +++ b/lib/server/CPrimaryScreen.cpp @@ -44,15 +44,15 @@ CPrimaryScreen::mainLoop() // run event loop try { - log((CLOG_DEBUG "entering event loop")); + LOG((CLOG_DEBUG "entering event loop")); onPreMainLoop(); getScreen()->mainLoop(); onPostMainLoop(); - log((CLOG_DEBUG "exiting event loop")); + LOG((CLOG_DEBUG "exiting event loop")); } catch (...) { onPostMainLoop(); - log((CLOG_DEBUG "exiting event loop")); + LOG((CLOG_DEBUG "exiting event loop")); throw; } } @@ -119,7 +119,7 @@ CPrimaryScreen::close() void CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver) { - log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreensaver ? " for screen saver" : "")); + LOG((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreensaver ? " for screen saver" : "")); CLock lock(&m_mutex); assert(m_active == true); @@ -153,7 +153,7 @@ CPrimaryScreen::enterNoWarp() bool CPrimaryScreen::leave() { - log((CLOG_INFO "leaving primary")); + LOG((CLOG_INFO "leaving primary")); CLock lock(&m_mutex); assert(m_active == false); diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 4df38195..9be9b299 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -72,17 +72,17 @@ CServer::open() { // open the screen try { - log((CLOG_INFO "opening screen")); + LOG((CLOG_INFO "opening screen")); openPrimaryScreen(); } catch (XScreen&) { // can't open screen - log((CLOG_INFO "failed to open screen")); + LOG((CLOG_INFO "failed to open screen")); throw; } catch (XUnknownClient& e) { // can't open screen - log((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); + LOG((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); throw; } } @@ -97,7 +97,7 @@ CServer::mainLoop() } try { - log((CLOG_NOTE "starting server")); + LOG((CLOG_NOTE "starting server")); // start listening for new clients m_acceptClientThread = new CThread(startThread( @@ -115,7 +115,7 @@ CServer::mainLoop() m_primaryClient->mainLoop(); // clean up - log((CLOG_NOTE "stopping server")); + LOG((CLOG_NOTE "stopping server")); // use a macro to write the stuff that should go into a finally // block so we can repeat it easily. stroustrup's view that @@ -131,23 +131,23 @@ CServer::mainLoop() FINALLY; } catch (XBase& e) { - log((CLOG_ERR "server error: %s", e.what())); + LOG((CLOG_ERR "server error: %s", e.what())); // clean up - log((CLOG_NOTE "stopping server")); + LOG((CLOG_NOTE "stopping server")); FINALLY; } catch (XThread&) { // clean up - log((CLOG_NOTE "stopping server")); + LOG((CLOG_NOTE "stopping server")); FINALLY; throw; } catch (...) { - log((CLOG_DEBUG "unknown server error")); + LOG((CLOG_DEBUG "unknown server error")); // clean up - log((CLOG_NOTE "stopping server")); + LOG((CLOG_NOTE "stopping server")); FINALLY; throw; } @@ -166,7 +166,7 @@ CServer::close() if (m_primaryClient != NULL) { closePrimaryScreen(); } - log((CLOG_INFO "closed screen")); + LOG((CLOG_INFO "closed screen")); } bool @@ -289,7 +289,7 @@ CServer::onInfoChanged(const CString& name, const CClientInfo& info) m_x = info.m_mx; m_y = info.m_my; } - log((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", name.c_str(), info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); + LOG((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", name.c_str(), info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); // handle resolution change to primary screen if (client == m_primaryClient) { @@ -318,12 +318,12 @@ CServer::onGrabClipboard(const CString& name, ClipboardID id, UInt32 seqNum) CClipboardInfo& clipboard = m_clipboards[id]; if (name != m_primaryClient->getName() && seqNum < clipboard.m_clipboardSeqNum) { - log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", name.c_str(), id)); + LOG((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", name.c_str(), id)); return false; } // mark screen as owning clipboard - log((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", name.c_str(), id, clipboard.m_clipboardOwner.c_str())); + LOG((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", name.c_str(), id, clipboard.m_clipboardOwner.c_str())); clipboard.m_clipboardOwner = name; clipboard.m_clipboardSeqNum = seqNum; @@ -365,18 +365,18 @@ CServer::onClipboardChangedNoLock(ClipboardID id, // ignore update if sequence number is old if (seqNum < clipboard.m_clipboardSeqNum) { - log((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", clipboard.m_clipboardOwner.c_str(), id)); + LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", clipboard.m_clipboardOwner.c_str(), id)); return; } // ignore if data hasn't changed if (data == clipboard.m_clipboardData) { - log((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id)); + LOG((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id)); return; } // unmarshall into our clipboard buffer - log((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + LOG((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); clipboard.m_clipboardData = data; clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); @@ -396,7 +396,7 @@ CServer::onClipboardChangedNoLock(ClipboardID id, void CServer::onScreensaver(bool activated) { - log((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); + LOG((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); CLock lock(&m_mutex); if (activated) { @@ -452,7 +452,7 @@ CServer::onScreensaver(bool activated) void CServer::onKeyDown(KeyID id, KeyModifierMask mask) { - log((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x", id, mask)); + LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x", id, mask)); CLock lock(&m_mutex); assert(m_active != NULL); @@ -468,7 +468,7 @@ CServer::onKeyDown(KeyID id, KeyModifierMask mask) void CServer::onKeyUp(KeyID id, KeyModifierMask mask) { - log((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x", id, mask)); + LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x", id, mask)); CLock lock(&m_mutex); assert(m_active != NULL); @@ -484,7 +484,7 @@ CServer::onKeyUp(KeyID id, KeyModifierMask mask) void CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) { - log((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); + LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); CLock lock(&m_mutex); assert(m_active != NULL); @@ -501,7 +501,7 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) void CServer::onMouseDown(ButtonID id) { - log((CLOG_DEBUG1 "onMouseDown id=%d", id)); + LOG((CLOG_DEBUG1 "onMouseDown id=%d", id)); CLock lock(&m_mutex); assert(m_active != NULL); @@ -512,7 +512,7 @@ CServer::onMouseDown(ButtonID id) void CServer::onMouseUp(ButtonID id) { - log((CLOG_DEBUG1 "onMouseUp id=%d", id)); + LOG((CLOG_DEBUG1 "onMouseUp id=%d", id)); CLock lock(&m_mutex); assert(m_active != NULL); @@ -523,7 +523,7 @@ CServer::onMouseUp(ButtonID id) bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) { - log((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); + LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); CLock lock(&m_mutex); return onMouseMovePrimaryNoLock(x, y); } @@ -550,22 +550,22 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) if (x < ax + zoneSize) { x -= zoneSize; dir = kLeft; - log((CLOG_DEBUG1 "switch to left")); + LOG((CLOG_DEBUG1 "switch to left")); } else if (x >= ax + aw - zoneSize) { x += zoneSize; dir = kRight; - log((CLOG_DEBUG1 "switch to right")); + LOG((CLOG_DEBUG1 "switch to right")); } else if (y < ay + zoneSize) { y -= zoneSize; dir = kTop; - log((CLOG_DEBUG1 "switch to top")); + LOG((CLOG_DEBUG1 "switch to top")); } else if (y >= ay + ah - zoneSize) { y += zoneSize; dir = kBottom; - log((CLOG_DEBUG1 "switch to bottom")); + LOG((CLOG_DEBUG1 "switch to bottom")); } else { // still on local screen @@ -587,7 +587,7 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) { - log((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); + LOG((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); CLock lock(&m_mutex); onMouseMoveSecondaryNoLock(dx, dy); } @@ -647,12 +647,12 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) // get neighbor if we should switch if (newScreen == NULL) { - log((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); + LOG((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); // get new position or clamp to current screen newScreen = getNeighbor(m_active, dir, m_x, m_y); if (newScreen == NULL) { - log((CLOG_DEBUG1 "no neighbor; clamping")); + LOG((CLOG_DEBUG1 "no neighbor; clamping")); if (m_x < ax) { m_x = ax; } @@ -670,7 +670,7 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) } else { // clamp to edge when locked - log((CLOG_DEBUG1 "clamp to \"%s\"", m_active->getName().c_str())); + LOG((CLOG_DEBUG1 "clamp to \"%s\"", m_active->getName().c_str())); if (m_x < ax) { m_x = ax; } @@ -689,7 +689,7 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) if (newScreen == NULL || newScreen == m_active) { // do nothing if mouse didn't move if (m_x != xOld || m_y != yOld) { - log((CLOG_DEBUG2 "move on %s to %d,%d", m_active->getName().c_str(), m_x, m_y)); + LOG((CLOG_DEBUG2 "move on %s to %d,%d", m_active->getName().c_str(), m_x, m_y)); m_active->mouseMove(m_x, m_y); } } @@ -703,7 +703,7 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) void CServer::onMouseWheel(SInt32 delta) { - log((CLOG_DEBUG1 "onMouseWheel %+d", delta)); + LOG((CLOG_DEBUG1 "onMouseWheel %+d", delta)); CLock lock(&m_mutex); assert(m_active != NULL); @@ -749,7 +749,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) #endif assert(m_active != NULL); - log((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y)); + LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y)); // record new position m_x = x; @@ -762,7 +762,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) // leave active screen if (!m_active->leave()) { // cannot leave screen - log((CLOG_WARN "can't leave screen")); + LOG((CLOG_WARN "can't leave screen")); return; } @@ -808,14 +808,14 @@ CServer::getNeighbor(IClient* src, EDirection dir) const CString srcName = src->getName(); assert(!srcName.empty()); - log((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); + LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); for (;;) { // look up name of neighbor const CString dstName(m_config.getNeighbor(srcName, dir)); // if nothing in that direction then return NULL if (dstName.empty()) { - log((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); + LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); return NULL; } @@ -824,11 +824,11 @@ CServer::getNeighbor(IClient* src, EDirection dir) const // unconnected screen. CClientList::const_iterator index = m_clients.find(dstName); if (index != m_clients.end()) { - log((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); + LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); return index->second; } - log((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); + LOG((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); srcName = dstName; } } @@ -867,7 +867,7 @@ CServer::getNeighbor(IClient* src, if (x >= 0) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); @@ -883,7 +883,7 @@ CServer::getNeighbor(IClient* src, if (x < dw) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); @@ -899,7 +899,7 @@ CServer::getNeighbor(IClient* src, if (y >= 0) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); @@ -915,7 +915,7 @@ CServer::getNeighbor(IClient* src, if (y < sh) { break; } - log((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); @@ -1073,14 +1073,14 @@ CServer::startThread(IJob* job) // add new thread to list. use the job as user data for logging. CThread thread(job, job); m_threads.push_back(thread); - log((CLOG_DEBUG1 "started thread %p", thread.getUserData())); + LOG((CLOG_DEBUG1 "started thread %p", thread.getUserData())); return thread; } void CServer::stopThreads(double timeout) { - log((CLOG_DEBUG1 "stopping threads")); + LOG((CLOG_DEBUG1 "stopping threads")); // cancel the accept client thread to prevent more clients from // connecting while we're shutting down. @@ -1125,10 +1125,10 @@ CServer::stopThreads(double timeout) // delete remaining threads for (CThreadList::iterator index = threads.begin(); index != threads.end(); ++index) { - log((CLOG_DEBUG1 "reaped running thread %p", index->getUserData())); + LOG((CLOG_DEBUG1 "reaped running thread %p", index->getUserData())); } - log((CLOG_DEBUG1 "stopped threads")); + LOG((CLOG_DEBUG1 "stopped threads")); } void @@ -1145,7 +1145,7 @@ CServer::doReapThreads(CThreadList& threads) index != threads.end(); ) { if (index->wait(0.0)) { // thread terminated - log((CLOG_DEBUG1 "reaped thread %p", index->getUserData())); + LOG((CLOG_DEBUG1 "reaped thread %p", index->getUserData())); index = threads.erase(index); } else { @@ -1158,7 +1158,7 @@ CServer::doReapThreads(CThreadList& threads) void CServer::acceptClients(void*) { - log((CLOG_DEBUG1 "starting to wait for clients")); + LOG((CLOG_DEBUG1 "starting to wait for clients")); IListenSocket* listen = NULL; try { @@ -1173,16 +1173,16 @@ CServer::acceptClients(void*) CStopwatch timer; for (;;) { try { - log((CLOG_DEBUG1 "binding listen socket")); + LOG((CLOG_DEBUG1 "binding listen socket")); listen->bind(m_config.getSynergyAddress()); break; } catch (XSocketBind& e) { - log((CLOG_WARN "bind failed: %s", e.getErrstr())); + LOG((CLOG_WARN "bind failed: %s", e.getErrstr())); // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { - log((CLOG_ERR "waited too long to bind, giving up")); + LOG((CLOG_ERR "waited too long to bind, giving up")); throw; } @@ -1192,12 +1192,12 @@ CServer::acceptClients(void*) } // accept connections and begin processing them - log((CLOG_DEBUG1 "waiting for client connections")); + LOG((CLOG_DEBUG1 "waiting for client connections")); for (;;) { // accept connection CThread::testCancel(); IDataSocket* socket = listen->accept(); - log((CLOG_NOTE "accepted client connection")); + LOG((CLOG_NOTE "accepted client connection")); CThread::testCancel(); // start handshake thread @@ -1209,7 +1209,7 @@ CServer::acceptClients(void*) delete listen; } catch (XBase& e) { - log((CLOG_ERR "cannot listen for clients: %s", e.what())); + LOG((CLOG_ERR "cannot listen for clients: %s", e.what())); delete listen; exitMainLoop(); } @@ -1251,7 +1251,7 @@ CServer::runClient(void* vsocket) } catch (XDuplicateClient& e) { // client has duplicate name - log((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); + LOG((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); delete proxy; delete socket; @@ -1259,7 +1259,7 @@ CServer::runClient(void* vsocket) } catch (XUnknownClient& e) { // client has unknown name - log((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); + LOG((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); delete proxy; delete socket; @@ -1281,17 +1281,17 @@ CServer::runClient(void* vsocket) // handle client messages try { - log((CLOG_NOTE "client \"%s\" has connected", proxy->getName().c_str())); + LOG((CLOG_NOTE "client \"%s\" has connected", proxy->getName().c_str())); proxy->mainLoop(); } catch (XBadClient&) { // client not behaving - log((CLOG_WARN "protocol error from client \"%s\"", proxy->getName().c_str())); + LOG((CLOG_WARN "protocol error from client \"%s\"", proxy->getName().c_str())); CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); } catch (XBase& e) { // misc error - log((CLOG_WARN "error communicating with client \"%s\": %s", proxy->getName().c_str(), e.what())); + LOG((CLOG_WARN "error communicating with client \"%s\": %s", proxy->getName().c_str(), e.what())); } catch (...) { // mainLoop() was probably cancelled @@ -1308,7 +1308,7 @@ CServer::runClient(void* vsocket) CClientProxy* CServer::handshakeClient(IDataSocket* socket) { - log((CLOG_DEBUG1 "negotiating with new client")); + LOG((CLOG_DEBUG1 "negotiating with new client")); // get the input and output streams IInputStream* input = socket->getInputStream(); @@ -1334,14 +1334,14 @@ CServer::handshakeClient(IDataSocket* socket) CTimerThread timer(30.0); // say hello - log((CLOG_DEBUG1 "saying hello")); + LOG((CLOG_DEBUG1 "saying hello")); CProtocolUtil::writef(output, kMsgHello, kProtocolMajorVersion, kProtocolMinorVersion); output->flush(); // wait for the reply - log((CLOG_DEBUG1 "waiting for hello reply")); + LOG((CLOG_DEBUG1 "waiting for hello reply")); UInt32 n = input->getSize(); // limit the maximum length of the hello @@ -1352,7 +1352,7 @@ CServer::handshakeClient(IDataSocket* socket) // get and parse the reply to hello SInt16 major, minor; try { - log((CLOG_DEBUG1 "parsing hello reply")); + LOG((CLOG_DEBUG1 "parsing hello reply")); CProtocolUtil::readf(input, kMsgHelloBack, &major, &minor, &name); } @@ -1382,32 +1382,32 @@ CServer::handshakeClient(IDataSocket* socket) } // create client proxy for highest version supported by the client - log((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor)); + LOG((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor)); proxy = new CClientProxy1_0(this, name, input, output); // negotiate // FIXME // ask and wait for the client's info - log((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str())); + LOG((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str())); proxy->open(); return proxy; } catch (XIncompatibleClient& e) { // client is incompatible - log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); + LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); CProtocolUtil::writef(output, kMsgEIncompatible, kProtocolMajorVersion, kProtocolMinorVersion); } catch (XBadClient&) { // client not behaving - log((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); + LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); CProtocolUtil::writef(output, kMsgEBad); } catch (XBase& e) { // misc error - log((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); + LOG((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); } catch (...) { // probably timed out @@ -1436,7 +1436,7 @@ CServer::handshakeClient(IDataSocket* socket) void CServer::acceptHTTPClients(void*) { - log((CLOG_DEBUG1 "starting to wait for HTTP clients")); + LOG((CLOG_DEBUG1 "starting to wait for HTTP clients")); IListenSocket* listen = NULL; try { @@ -1448,16 +1448,16 @@ CServer::acceptHTTPClients(void*) CStopwatch timer; for (;;) { try { - log((CLOG_DEBUG1 "binding HTTP listen socket")); + LOG((CLOG_DEBUG1 "binding HTTP listen socket")); listen->bind(m_config.getHTTPAddress()); break; } catch (XSocketBind& e) { - log((CLOG_DEBUG1 "bind HTTP failed: %s", e.getErrstr())); + LOG((CLOG_DEBUG1 "bind HTTP failed: %s", e.getErrstr())); // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { - log((CLOG_DEBUG1 "waited too long to bind HTTP, giving up")); + LOG((CLOG_DEBUG1 "waited too long to bind HTTP, giving up")); throw; } @@ -1467,7 +1467,7 @@ CServer::acceptHTTPClients(void*) } // accept connections and begin processing them - log((CLOG_DEBUG1 "waiting for HTTP connections")); + LOG((CLOG_DEBUG1 "waiting for HTTP connections")); for (;;) { // limit the number of HTTP requests being handled at once { @@ -1482,7 +1482,7 @@ CServer::acceptHTTPClients(void*) // accept connection CThread::testCancel(); IDataSocket* socket = listen->accept(); - log((CLOG_NOTE "accepted HTTP connection")); + LOG((CLOG_NOTE "accepted HTTP connection")); CThread::testCancel(); // handle HTTP request @@ -1494,7 +1494,7 @@ CServer::acceptHTTPClients(void*) delete listen; } catch (XBase& e) { - log((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); + LOG((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); delete listen; exitMainLoop(); } @@ -1574,7 +1574,7 @@ CServer::openPrimaryScreen() m_active = m_primaryClient; // open the screen - log((CLOG_DEBUG1 "opening primary screen")); + LOG((CLOG_DEBUG1 "opening primary screen")); m_primaryClient->open(); // tell it about the active sides @@ -1600,7 +1600,7 @@ CServer::closePrimaryScreen() // close the primary screen try { - log((CLOG_DEBUG1 "closing primary screen")); + LOG((CLOG_DEBUG1 "closing primary screen")); m_primaryClient->close(); } catch (...) { @@ -1617,7 +1617,7 @@ CServer::addConnection(IClient* client) { assert(client != NULL); - log((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str())); + LOG((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str())); CLock lock(&m_mutex); @@ -1633,13 +1633,13 @@ CServer::addConnection(IClient* client) // save screen info m_clients.insert(std::make_pair(client->getName(), client)); - log((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str())); + LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str())); } void CServer::removeConnection(const CString& name) { - log((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); + LOG((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); CLock lock(&m_mutex); // find client @@ -1653,7 +1653,7 @@ CServer::removeConnection(const CString& name) m_primaryClient->getCursorCenter(m_x, m_y); // don't notify active screen since it probably already disconnected - log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); + LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); // cut over m_active = m_primaryClient; diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp index 756dcb03..57e0092d 100644 --- a/lib/server/CXWindowsPrimaryScreen.cpp +++ b/lib/server/CXWindowsPrimaryScreen.cpp @@ -182,7 +182,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) case KeyPress: { - log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { @@ -223,7 +223,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) } if (!hasPress) { // no press event follows so it's a plain release - log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); if (key == kKeyCapsLock && m_capsLockHalfDuplex) { m_receiver->onKeyDown(key, mask); } @@ -237,7 +237,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) // we could attempt to count the already queued // repeats but we'll just send a repeat of 1. // note that we discard the press event. - log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); + LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); m_receiver->onKeyRepeat(key, mask, 1); } } @@ -246,7 +246,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) case ButtonPress: { - log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); + LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { m_receiver->onMouseDown(button); @@ -256,7 +256,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) case ButtonRelease: { - log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); + LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { m_receiver->onMouseUp(button); @@ -274,7 +274,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) case MotionNotify: { - log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); + LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); // compute motion delta (relative to the last known // mouse position) @@ -427,7 +427,7 @@ CXWindowsPrimaryScreen::createWindow() if (m_window == None) { throw XScreenOpenFailure(); } - log((CLOG_DEBUG "window is 0x%08x", m_window)); + LOG((CLOG_DEBUG "window is 0x%08x", m_window)); // start watching for events on other windows selectEvents(display, m_screen->getRoot()); @@ -474,16 +474,16 @@ CXWindowsPrimaryScreen::showWindow() GrabModeAsync, GrabModeAsync, CurrentTime); assert(result != GrabNotViewable); if (result != GrabSuccess) { - log((CLOG_DEBUG2 "waiting to grab keyboard")); + LOG((CLOG_DEBUG2 "waiting to grab keyboard")); CThread::sleep(0.05); if (timer.getTime() >= s_timeout) { - log((CLOG_DEBUG2 "grab keyboard timed out")); + LOG((CLOG_DEBUG2 "grab keyboard timed out")); XUnmapWindow(display, m_window); return false; } } } while (result != GrabSuccess); - log((CLOG_DEBUG2 "grabbed keyboard")); + LOG((CLOG_DEBUG2 "grabbed keyboard")); // now the mouse result = XGrabPointer(display, m_window, True, 0, @@ -493,16 +493,16 @@ CXWindowsPrimaryScreen::showWindow() if (result != GrabSuccess) { // back off to avoid grab deadlock XUngrabKeyboard(display, CurrentTime); - log((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); + LOG((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); CThread::sleep(0.05); if (timer.getTime() >= s_timeout) { - log((CLOG_DEBUG2 "grab pointer timed out")); + LOG((CLOG_DEBUG2 "grab pointer timed out")); XUnmapWindow(display, m_window); return false; } } } while (result != GrabSuccess); - log((CLOG_DEBUG1 "grabbed pointer and keyboard")); + LOG((CLOG_DEBUG1 "grabbed pointer and keyboard")); return true; } @@ -554,7 +554,7 @@ CXWindowsPrimaryScreen::warpCursorNoFlush( XSendEvent(display, m_window, False, 0, &eventAfter); XSync(display, False); - log((CLOG_DEBUG2 "warped to %d,%d", x, y)); + LOG((CLOG_DEBUG2 "warped to %d,%d", x, y)); } void diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp index 30274f08..c68b9a7e 100644 --- a/lib/synergy/CProtocolUtil.cpp +++ b/lib/synergy/CProtocolUtil.cpp @@ -28,7 +28,7 @@ CProtocolUtil::writef(IOutputStream* stream, const char* fmt, ...) { assert(stream != NULL); assert(fmt != NULL); - log((CLOG_DEBUG2 "writef(%s)", fmt)); + LOG((CLOG_DEBUG2 "writef(%s)", fmt)); va_list args; @@ -52,7 +52,7 @@ CProtocolUtil::writef(IOutputStream* stream, const char* fmt, ...) UInt8* scan = buffer; while (count > 0) { const UInt32 n = stream->write(scan, count); - log((CLOG_DEBUG2 "wrote %d of %d bytes", n, count)); + LOG((CLOG_DEBUG2 "wrote %d of %d bytes", n, count)); count -= n; scan += n; } @@ -65,7 +65,7 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) { assert(stream != NULL); assert(fmt != NULL); - log((CLOG_DEBUG2 "readf(%s)", fmt)); + LOG((CLOG_DEBUG2 "readf(%s)", fmt)); va_list args; va_start(args, fmt); @@ -91,7 +91,7 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) case 1: // 1 byte integer *reinterpret_cast(v) = buffer[0]; - log((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; case 2: @@ -100,7 +100,7 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) static_cast( (static_cast(buffer[0]) << 8) | static_cast(buffer[1])); - log((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; case 4: @@ -110,7 +110,7 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) (static_cast(buffer[1]) << 16) | (static_cast(buffer[2]) << 8) | static_cast(buffer[3]); - log((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); break; } break; @@ -149,7 +149,7 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) } throw; } - log((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer)); + LOG((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer)); // save the data CString* dst = va_arg(args, CString*); @@ -180,7 +180,7 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) // verify match if (buffer[0] != *fmt) { - log((CLOG_DEBUG2 "readf: format mismatch: %c vs %c", *fmt, buffer[0])); + LOG((CLOG_DEBUG2 "readf: format mismatch: %c vs %c", *fmt, buffer[0])); throw XIOReadMismatch(); } @@ -366,7 +366,7 @@ CProtocolUtil::read(IInputStream* stream, void* vbuffer, UInt32 count) // bail if stream has hungup if (n == 0) { - log((CLOG_DEBUG2 "unexpected disconnect in readf(), %d bytes left", count)); + LOG((CLOG_DEBUG2 "unexpected disconnect in readf(), %d bytes left", count)); throw XIOEndOfStream(); } From abee021db21de54c96232153f67f6b936e40f4ab Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 15 Oct 2002 21:35:12 +0000 Subject: [PATCH 359/807] Workaround for pthread bug on RedHat 7.2 on multiprocessor systems. --- lib/mt/CThreadRep.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp index f79a2787..a60254b5 100644 --- a/lib/mt/CThreadRep.cpp +++ b/lib/mt/CThreadRep.cpp @@ -106,7 +106,11 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGTERM); pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); - int status = pthread_create(&m_thread, NULL, threadFunc, (void*)this); + // pthread_create() RedHat 7.2 smp fails with a NULL attr. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) + status = pthread_create(&m_thread, &attr, threadFunc, (void*)this); pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); if (status != 0) { throw XThreadUnavailable(); @@ -183,7 +187,9 @@ CThreadRep::initThreads() // instead arrange to catch and handle these signals but // we'd be unable to cancel the main thread since no pthread // calls are allowed in a signal handler. - int status = pthread_create(&s_signalThread, NULL, + pthread_attr_t attr; + pthread_attr_init(&attr); + int status = pthread_create(&s_signalThread, &attr, &CThreadRep::threadSignalHandler, getCurrentThreadRep()); if (status != 0) { From c405c58c64d9a78d47b3b0f10e769f9b4de6f711 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 15 Oct 2002 22:01:41 +0000 Subject: [PATCH 360/807] Renamed XThreadUnavailable to XMTThreadUnavailable and derived it from XBase so it can be caught normally. Changed client and server to handle unavailable threads (in main loop, anyway). --- lib/client/CClient.cpp | 9 +++++++++ lib/mt/CThreadRep.cpp | 5 +++-- lib/mt/Makefile.am | 2 ++ lib/mt/XMT.cpp | 25 +++++++++++++++++++++++++ lib/mt/XMT.h | 33 +++++++++++++++++++++++++++++++++ lib/mt/XThread.h | 11 +++++------ lib/server/CServer.cpp | 9 +++++++++ 7 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 lib/mt/XMT.cpp create mode 100644 lib/mt/XMT.h diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 44076bd0..2238f287 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -31,6 +31,7 @@ #include "CLock.h" #include "CThread.h" #include "CTimerThread.h" +#include "XMT.h" #include "XThread.h" #include "CLog.h" #include "CStopwatch.h" @@ -209,6 +210,14 @@ CClient::mainLoop() deleteSession(); LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); } + catch (XMT& e) { + LOG((CLOG_ERR "client error: %s", e.what())); + + // clean up + deleteSession(); + LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + throw; + } catch (XBase& e) { LOG((CLOG_ERR "client error: %s", e.what())); diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp index a60254b5..56ec0c41 100644 --- a/lib/mt/CThreadRep.cpp +++ b/lib/mt/CThreadRep.cpp @@ -16,6 +16,7 @@ #include "CLock.h" #include "CMutex.h" #include "CThread.h" +#include "XMT.h" #include "XThread.h" #include "CLog.h" #include "IJob.h" @@ -113,7 +114,7 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : status = pthread_create(&m_thread, &attr, threadFunc, (void*)this); pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); if (status != 0) { - throw XThreadUnavailable(); + throw XMTThreadUnavailable(); } #elif WINDOWS_LIKE unsigned int id; @@ -121,7 +122,7 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : threadFunc, (void*)this, 0, &id)); m_id = static_cast(id); if (m_thread == 0) { - throw XThreadUnavailable(); + throw XMTThreadUnavailable(); } #endif diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index 5db01822..baf32d7e 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -31,12 +31,14 @@ libmt_a_SOURCES = \ CThread.cpp \ CThreadRep.cpp \ CTimerThread.cpp \ + XMT.cpp \ CCondVar.h \ CLock.h \ CMutex.h \ CThread.h \ CThreadRep.h \ CTimerThread.h \ + XMT.h \ XThread.h \ $(NULL) INCLUDES = \ diff --git a/lib/mt/XMT.cpp b/lib/mt/XMT.cpp new file mode 100644 index 00000000..b10d2d79 --- /dev/null +++ b/lib/mt/XMT.cpp @@ -0,0 +1,25 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XMT.h" + +// +// XMTThreadUnavailable +// + +CString +XMTThreadUnavailable::getWhat() const throw() +{ + return format("XMTThreadUnavailable", "cannot create thread"); +} diff --git a/lib/mt/XMT.h b/lib/mt/XMT.h new file mode 100644 index 00000000..32ca9aaa --- /dev/null +++ b/lib/mt/XMT.h @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XMT_H +#define XMT_H + +#include "XBase.h" + +//! Generic multithreading exception +class XMT : public XBase { }; + +//! Thread creation exception +/*! +Thrown when a thread cannot be created. +*/ +class XMTThreadUnavailable : public XMT { +protected: + // XBase overrides + virtual CString getWhat() const throw(); +}; + +#endif diff --git a/lib/mt/XThread.h b/lib/mt/XThread.h index ac820795..b76c9933 100644 --- a/lib/mt/XThread.h +++ b/lib/mt/XThread.h @@ -16,6 +16,11 @@ #define XTHREAD_H //! Generic thread exception +/*! +Exceptions derived from this class are used by the multithreading +library to perform stack unwinding when a thread terminates. These +exceptions must always be rethrown by clients when caught. +*/ class XThread { }; //! Thread exception to exit @@ -41,12 +46,6 @@ must rethrow it if caught (by XThreadCancel, XThread, or ...). */ class XThreadCancel : public XThread { }; -//! Thread creation exception -/*! -Thrown when a thread cannot be created. -*/ -class XThreadUnavailable { }; - /*! \def RETHROW_XTHREAD Convenience macro to rethrow an XThread exception but ignore other diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 9be9b299..a4d124f0 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -31,6 +31,7 @@ #include "CLock.h" #include "CThread.h" #include "CTimerThread.h" +#include "XMT.h" #include "XThread.h" #include "CFunctionJob.h" #include "CLog.h" @@ -130,6 +131,14 @@ CServer::mainLoop() } while (false) FINALLY; } + catch (XMT& e) { + LOG((CLOG_ERR "server error: %s", e.what())); + + // clean up + LOG((CLOG_NOTE "stopping server")); + FINALLY; + throw; + } catch (XBase& e) { LOG((CLOG_ERR "server error: %s", e.what())); From a18b1462cf05eb6bcd2b6a3dd635cd0a047e58b7 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 15 Oct 2002 22:08:10 +0000 Subject: [PATCH 361/807] Fixed use of %s instead of %{1} in format() call. --- cmd/synergys/synergys.cpp | 2 +- lib/server/CConfig.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 7191a440..522f6710 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -516,7 +516,7 @@ loadConfig(const char* pathname, bool require) LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname)); std::ifstream configStream(pathname); if (!configStream) { - throw XConfigRead("cannot open configuration"); + throw XConfigRead("cannot open file"); } configStream >> s_config; LOG((CLOG_DEBUG "configuration read successfully")); diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 942e23c9..c068790c 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -823,5 +823,5 @@ XConfigRead::~XConfigRead() CString XConfigRead::getWhat() const throw() { - return format("XConfigRead", "read error: %s", m_error.c_str()); + return format("XConfigRead", "read error: %{1}", m_error.c_str()); } From d9c622ae042b20567a167fbcfd91303049795bf6 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 15 Oct 2002 22:17:41 +0000 Subject: [PATCH 362/807] CConfig now accepts and discards \r at the end of a line. This allows the unix server to read configuration files created on microsoft windows platforms. --- lib/server/CConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index c068790c..7e706cf4 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -424,7 +424,7 @@ CConfig::readLine(std::istream& s, CString& line) if (i != CString::npos) { line.erase(i); } - i = line.find_last_not_of(" \t"); + i = line.find_last_not_of(" \r\t"); if (i != CString::npos) { line.erase(i + 1); } From c1797c68677e7aaa0674baa12f8156a353017982 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Oct 2002 19:40:29 +0000 Subject: [PATCH 363/807] Added matching pthread_attr_destroy() for each pthread_attr_init() in change 610. --- lib/mt/CThreadRep.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp index 56ec0c41..1e487cf2 100644 --- a/lib/mt/CThreadRep.cpp +++ b/lib/mt/CThreadRep.cpp @@ -110,8 +110,10 @@ CThreadRep::CThreadRep(IJob* job, void* userData) : // pthread_create() RedHat 7.2 smp fails with a NULL attr. pthread_attr_t attr; int status = pthread_attr_init(&attr); - if (status == 0) + if (status == 0) { status = pthread_create(&m_thread, &attr, threadFunc, (void*)this); + pthread_attr_destroy(&attr); + } pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); if (status != 0) { throw XMTThreadUnavailable(); @@ -189,10 +191,13 @@ CThreadRep::initThreads() // we'd be unable to cancel the main thread since no pthread // calls are allowed in a signal handler. pthread_attr_t attr; - pthread_attr_init(&attr); - int status = pthread_create(&s_signalThread, &attr, + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&s_signalThread, &attr, &CThreadRep::threadSignalHandler, getCurrentThreadRep()); + pthread_attr_destroy(&attr); + } if (status != 0) { // can't create thread to wait for signal so don't block // the signals. From 11e29ff7eb173bcce3365d0673f4850f45052dc9 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Oct 2002 22:01:50 +0000 Subject: [PATCH 364/807] Added support for using select() instead of poll(). --- acinclude.m4 | 15 ++ configure.in | 2 + lib/net/CNetwork.cpp | 316 ++++++++++++++++++------------- lib/net/CNetwork.h | 76 +++++--- lib/net/CTCPSocket.cpp | 3 +- lib/platform/CXWindowsScreen.cpp | 46 ++++- 6 files changed, 301 insertions(+), 157 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 685209d4..155c47ea 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -75,6 +75,21 @@ AC_DEFUN([ACX_CHECK_CXX_STDLIB], [ fi ])dnl ACX_CHECK_CXX_STDLIB +AC_DEFUN([ACX_CHECK_POLL], [ + AC_MSG_CHECKING([for poll]) + AC_TRY_LINK([#include ], + [struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10);], + acx_poll_ok=yes, acx_poll_ok=no) + AC_MSG_RESULT($acx_poll_ok) + if test x"$acx_poll_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_POLL,1,[Define if you have the `poll' function.]),[$1]) + : + else + acx_poll_ok=no + $2 + fi +])dnl ACX_CHECK_POLL + dnl The following macros are from http://www.gnu.org/software/ac-archive/ dnl which distributes them under the following license: dnl diff --git a/configure.in b/configure.in index ea56066b..56346272 100644 --- a/configure.in +++ b/configure.in @@ -72,6 +72,8 @@ AC_FUNC_STRFTIME AC_CHECK_FUNCS(gmtime_r) AC_CHECK_FUNCS(getpwuid_r) AC_CHECK_FUNCS(vsnprintf) +AC_FUNC_SELECT_ARGTYPES +ACX_CHECK_POLL dnl use AC_REPLACE_FUNCS() for stuff in string.h dnl checks for system services diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp index 918095c9..c844503f 100644 --- a/lib/net/CNetwork.cpp +++ b/lib/net/CNetwork.cpp @@ -51,6 +51,7 @@ struct protoent FAR * (PASCAL FAR *CNetwork::getprotobyname)(const char FAR * na int (PASCAL FAR *CNetwork::getsockerror)(void); int (PASCAL FAR *CNetwork::gethosterror)(void); int (PASCAL FAR *CNetwork::setblocking)(CNetwork::Socket s, bool blocking); +int (PASCAL FAR *CNetwork::setnodelay)(CNetwork::Socket s, bool blocking); #if WINDOWS_LIKE @@ -226,79 +227,11 @@ CNetwork::init2( read = read2; write = write2; setblocking = setblocking2; + setnodelay = setnodelay2; s_networkModule = module; } -int PASCAL FAR -CNetwork::poll2(PollEntry fd[], int nfds, int timeout) -{ - int i; - - // prepare sets for select - fd_set readSet, writeSet, errSet; - fd_set* readSetP = NULL; - fd_set* writeSetP = NULL; - fd_set* errSetP = NULL; - FD_ZERO(&readSet); - FD_ZERO(&writeSet); - FD_ZERO(&errSet); - for (i = 0; i < nfds; ++i) { - if (fd[i].events & kPOLLIN) { - FD_SET(fd[i].fd, &readSet); - readSetP = &readSet; - } - if (fd[i].events & kPOLLOUT) { - FD_SET(fd[i].fd, &writeSet); - writeSetP = &writeSet; - } - if (true) { - FD_SET(fd[i].fd, &errSet); - errSetP = &errSet; - } - } - - // prepare timeout for select - struct timeval timeout2; - struct timeval* timeout2P; - if (timeout < 0) { - timeout2P = NULL; - } - else { - timeout2P = &timeout2; - timeout2.tv_sec = timeout / 1000; - timeout2.tv_usec = 1000 * (timeout % 1000); - } - - // do the select. note that winsock ignores the first argument. - int n = select(0, readSetP, writeSetP, errSetP, timeout2P); - - // handle results - if (n == Error) { - return Error; - } - if (n == 0) { - return 0; - } - n = 0; - for (i = 0; i < nfds; ++i) { - fd[i].revents = 0; - if (FD_ISSET(fd[i].fd, &readSet)) { - fd[i].revents |= kPOLLIN; - } - if (FD_ISSET(fd[i].fd, &writeSet)) { - fd[i].revents |= kPOLLOUT; - } - if (FD_ISSET(fd[i].fd, &errSet)) { - fd[i].revents |= kPOLLERR; - } - if (fd[i].revents != 0) { - ++n; - } - } - return n; -} - ssize_t PASCAL FAR CNetwork::read2(Socket s, void FAR* buf, size_t len) { @@ -318,17 +251,39 @@ CNetwork::setblocking2(CNetwork::Socket s, bool blocking) return ioctl(s, FIONBIO, &flag); } +int PASCAL FAR +CNetwork::setnodelay2(CNetwork::Socket s, bool nodelay) +{ + BOOL flag = nodelay ? 1 : 0; + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); +} + #endif #if UNIX_LIKE -#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif #include #include +#include #include +#if !defined(TCP_NODELAY) || !defined(SOL_TCP) +# include +#endif // FIXME -- use reentrant versions of non-reentrant functions +const int CNetwork::Error = -1; +const CNetwork::Socket CNetwork::Null = -1; + #define setfunc(var, name, type) var = (type)::name UInt32 @@ -355,30 +310,73 @@ CNetwork::swapntohs(UInt16 v) return ntohs(v); } -static -int -myerrno() +void +CNetwork::init() +{ + setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); + setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); + setfunc(close, close, int (PASCAL FAR *)(Socket s)); + setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); + setfunc(ioctl, ioctl, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); + setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); + setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); +#if HAVE_POLL + setfunc(poll, poll, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); +#else + setfunc(poll, CNetwork::poll2, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); +#endif + setfunc(read, read, ssize_t (PASCAL FAR *)(CNetwork::Socket s, void FAR * buf, size_t len)); + setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); + setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); + setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); + setfunc(sendto, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); + setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); + setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); + setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(write, write, ssize_t (PASCAL FAR *)(CNetwork::Socket s, const void FAR * buf, size_t len)); + setfunc(gethostbyaddr, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(getservbyport, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); + setfunc(getservbyname, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); + setfunc(getprotobynumber, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); + setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); + gethostname = gethostname2; + getsockerror = getsockerror2; + gethosterror = gethosterror2; + setblocking = setblocking2; + setnodelay = setnodelay2; +} + +void +CNetwork::cleanup() +{ + // do nothing +} + +int PASCAL FAR +CNetwork::gethostname2(char* name, int namelen) +{ + return ::gethostname(name, namelen); +} + +int PASCAL FAR +CNetwork::getsockerror2(void) { return errno; } -static -int -myherrno() +int PASCAL FAR +CNetwork::gethosterror2(void) { return h_errno; } -static -int -mygethostname(char* name, int namelen) -{ - return gethostname(name, namelen); -} - -static -int -mysetblocking(CNetwork::Socket s, bool blocking) +int PASCAL FAR +CNetwork::setblocking2(CNetwork::Socket s, bool blocking) { int mode = fcntl(s, F_GETFL, 0); if (mode == -1) { @@ -396,49 +394,111 @@ mysetblocking(CNetwork::Socket s, bool blocking) return 0; } -const int CNetwork::Error = -1; -const CNetwork::Socket CNetwork::Null = -1; - -void -CNetwork::init() +int PASCAL FAR +CNetwork::setnodelay2(CNetwork::Socket s, bool nodelay) { - setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); - setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); - setfunc(close, close, int (PASCAL FAR *)(Socket s)); - setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); - setfunc(ioctl, ioctl, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); - setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); - setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); - setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); - setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); - setfunc(poll, poll, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); - setfunc(read, read, ssize_t (PASCAL FAR *)(CNetwork::Socket s, void FAR * buf, size_t len)); - setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); - setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); - setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); - setfunc(sendto, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); - setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); - setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); - setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); - setfunc(write, write, ssize_t (PASCAL FAR *)(CNetwork::Socket s, const void FAR * buf, size_t len)); - setfunc(gethostbyaddr, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); - setfunc(gethostbyname, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(gethostname, mygethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); - setfunc(getservbyport, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); - setfunc(getservbyname, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); - setfunc(getprotobynumber, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); - setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(getsockerror, myerrno, int (PASCAL FAR *)(void)); - setfunc(gethosterror, myherrno, int (PASCAL FAR *)(void)); - setfunc(setblocking, mysetblocking, int (PASCAL FAR *)(Socket, bool)); -} - -void -CNetwork::cleanup() -{ - // do nothing + int flag = nodelay ? 1 : 0; + setsockopt(s, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); +} + +#endif + +#if WINDOWS_LIKE || !HAVE_POLL + +#if HAVE_SYS_SELECT_H +# include +#endif +#if HAVE_SYS_TIME_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif + +int PASCAL FAR +CNetwork::poll2(PollEntry fd[], int nfds, int timeout) +{ + int i; + + // prepare sets for select + int n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < nfds; ++i) { + if (fd[i].events & kPOLLIN) { + FD_SET(fd[i].fd, &readSet); + readSetP = &readSet; + if (fd[i].fd > n) { + n = fd[i].fd; + } + } + if (fd[i].events & kPOLLOUT) { + FD_SET(fd[i].fd, &writeSet); + writeSetP = &writeSet; + if (fd[i].fd > n) { + n = fd[i].fd; + } + } + if (true) { + FD_SET(fd[i].fd, &errSet); + errSetP = &errSet; + if (fd[i].fd > n) { + n = fd[i].fd; + } + } + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = timeout / 1000; + timeout2.tv_usec = 1000 * (timeout % 1000); + } + + // do the select. note that winsock ignores the first argument. + n = select((SELECT_TYPE_ARG1) n + 1, + SELECT_TYPE_ARG234 readSetP, + SELECT_TYPE_ARG234 writeSetP, + SELECT_TYPE_ARG234 errSetP, + SELECT_TYPE_ARG5 timeout2P); + + // handle results + if (n == Error) { + return Error; + } + if (n == 0) { + return 0; + } + n = 0; + for (i = 0; i < nfds; ++i) { + fd[i].revents = 0; + if (FD_ISSET(fd[i].fd, &readSet)) { + fd[i].revents |= kPOLLIN; + } + if (FD_ISSET(fd[i].fd, &writeSet)) { + fd[i].revents |= kPOLLOUT; + } + if (FD_ISSET(fd[i].fd, &errSet)) { + fd[i].revents |= kPOLLERR; + } + if (fd[i].revents != 0) { + ++n; + } + } + return n; } #endif diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h index 96650a99..7b3ef64c 100644 --- a/lib/net/CNetwork.h +++ b/lib/net/CNetwork.h @@ -17,15 +17,22 @@ #include "BasicTypes.h" +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif +#if HAVE_POLL +# include +#endif + #if WINDOWS_LIKE // declare no functions in winsock2 # define INCL_WINSOCK_API_PROTOTYPES 0 # define INCL_WINSOCK_API_TYPEDEFS 0 # include typedef int ssize_t; -# if !defined(SOL_TCP) -# define SOL_TCP IPPROTO_TCP -# endif #else # undef FAR # undef PASCAL @@ -34,15 +41,8 @@ typedef int ssize_t; #endif #if UNIX_LIKE -# include -# include -# include -# include -# include -# include -# if !defined(TCP_NODELAY) || !defined(SOL_TCP) -# include -# endif +# include +# include #endif //! Networking functions @@ -53,7 +53,13 @@ public: typedef SOCKET Socket; typedef struct sockaddr Address; typedef int AddressLength; - typedef BOOL TCPNoDelayType; +#elif UNIX_LIKE + typedef int Socket; + typedef struct sockaddr Address; + typedef socklen_t AddressLength; +#endif + +#if WINDOWS_LIKE || !HAVE_POLL class PollEntry { public: Socket fd; @@ -61,21 +67,17 @@ public: short revents; }; enum { - kPOLLIN = 1, - kPOLLOUT = 2, - kPOLLERR = 4, + kPOLLIN = 1, + kPOLLOUT = 2, + kPOLLERR = 4, kPOLLNVAL = 8 }; -#elif UNIX_LIKE - typedef int Socket; - typedef struct sockaddr Address; - typedef socklen_t AddressLength; +#else typedef struct pollfd PollEntry; - typedef int TCPNoDelayType; enum { - kPOLLIN = POLLIN, - kPOLLOUT = POLLOUT, - kPOLLERR = POLLERR, + kPOLLIN = POLLIN, + kPOLLOUT = POLLOUT, + kPOLLERR = POLLERR, kPOLLNVAL = POLLNVAL }; #endif @@ -183,17 +185,39 @@ public: //! Set socket to (non-)blocking operation static int (PASCAL FAR *setblocking)(CNetwork::Socket s, bool blocking); -#if WINDOWS_LIKE + //! Turn Nagle algorithm on or off on socket + /*! + Set socket to send messages immediately (true) or to collect small + messages into one packet (false). + */ + static int (PASCAL FAR *setnodelay)(CNetwork::Socket s, bool nodelay); + private: +#if WINDOWS_LIKE +#define SELECT_TYPE_ARG1 int +#define SELECT_TYPE_ARG234 (fd_set *) +#define SELECT_TYPE_ARG5 (struct timeval *) static void init2(HMODULE); - static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout); static ssize_t PASCAL FAR read2(Socket s, void FAR * buf, size_t len); static ssize_t PASCAL FAR write2(Socket s, const void FAR * buf, size_t len); static int PASCAL FAR setblocking2(CNetwork::Socket s, bool blocking); + static int PASCAL FAR setnodelay2(CNetwork::Socket s, bool nodelay); static int (PASCAL FAR *WSACleanup)(void); static int (PASCAL FAR *__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); static int (PASCAL FAR *select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); #endif + +#if UNIX_LIKE + static int PASCAL FAR gethostname2(char FAR * name, int namelen); + static int PASCAL FAR getsockerror2(void); + static int PASCAL FAR gethosterror2(void); + static int PASCAL FAR setblocking2(CNetwork::Socket s, bool blocking); + static int PASCAL FAR setnodelay2(CNetwork::Socket s, bool nodelay); +#endif + +#if WINDOWS_LIKE || !HAVE_POLL + static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout); +#endif }; #endif diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index aa8dd065..a9937537 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -208,8 +208,7 @@ CTCPSocket::init() // turn off Nagle algorithm. we send lots of very short messages // that should be sent without (much) delay. for example, the // mouse motion messages are much less useful if they're delayed. - CNetwork::TCPNoDelayType flag = 1; - CNetwork::setsockopt(m_fd, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); + CNetwork::setnodelay(m_fd, true); } void diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 920c98de..5cf8fe80 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -32,7 +32,22 @@ # include #endif #if UNIX_LIKE -#include +# if HAVE_POLL +# include +# else +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TIME_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include +# endif +# endif #endif // @@ -240,19 +255,48 @@ CXWindowsScreen::mainLoop() // use poll() to wait for a message from the X server or for timeout. // this is a good deal more efficient than polling and sleeping. +#if HAVE_POLL struct pollfd pfds[1]; pfds[0].fd = ConnectionNumber(m_display); pfds[0].events = POLLIN; +#endif while (!m_stop) { // compute timeout to next timer +#if HAVE_POLL int timeout = (m_timers.empty() ? -1 : static_cast(1000.0 * m_timers.top())); +#else + struct timeval timeout; + struct timeval* timeoutPtr; + if (m_timers.empty()) { + timeoutPtr = NULL; + } + else { + timeout.tv_sec = static_cast(m_timers.top()); + timeout.tv_usec = static_cast(1.0e+6 * + (m_timers.top() - timeout.tv_sec)); + timeoutPtr = &timeout; + } + + // initialize file descriptor sets + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(ConnectionNumber(m_display), &rfds); +#endif // wait for message from X server or for timeout. also check // if the thread has been cancelled. poll() should return -1 // with EINTR when the thread is cancelled. m_mutex.unlock(); +#if HAVE_POLL poll(pfds, 1, timeout); +#else + select(ConnectionNumber(m_display) + 1, + SELECT_TYPE_ARG234 &rfds, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 timeoutPtr); +#endif CThread::testCancel(); m_mutex.lock(); From 586a5a81ab055abe1ec22dd2b46004f43dcf94e3 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 17 Oct 2002 20:56:28 +0000 Subject: [PATCH 365/807] Changed non-reentrant network functions to be reentrant and thread safe. --- lib/net/CNetwork.cpp | 407 ++++++++++++++++++++++++++++++++---- lib/net/CNetwork.h | 109 ++++++++-- lib/net/CNetworkAddress.cpp | 30 +-- 3 files changed, 475 insertions(+), 71 deletions(-) diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp index c844503f..7f7398a0 100644 --- a/lib/net/CNetwork.cpp +++ b/lib/net/CNetwork.cpp @@ -15,6 +15,7 @@ #include "CNetwork.h" #include "XNetwork.h" #include "CLog.h" +#include // // CNetwork @@ -29,7 +30,6 @@ int (PASCAL FAR *CNetwork::getpeername)(CNetwork::Socket s, CNetwork::Address FA int (PASCAL FAR *CNetwork::getsockname)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); int (PASCAL FAR *CNetwork::getsockopt)(CNetwork::Socket s, int level, int optname, void FAR * optval, CNetwork::AddressLength FAR *optlen); unsigned long (PASCAL FAR *CNetwork::inet_addr)(const char FAR * cp); -char FAR * (PASCAL FAR *CNetwork::inet_ntoa)(struct in_addr in); int (PASCAL FAR *CNetwork::listen)(CNetwork::Socket s, int backlog); ssize_t (PASCAL FAR *CNetwork::read)(CNetwork::Socket s, void FAR * buf, size_t len); ssize_t (PASCAL FAR *CNetwork::recv)(CNetwork::Socket s, void FAR * buf, size_t len, int flags); @@ -41,23 +41,20 @@ int (PASCAL FAR *CNetwork::setsockopt)(CNetwork::Socket s, int level, int optnam int (PASCAL FAR *CNetwork::shutdown)(CNetwork::Socket s, int how); CNetwork::Socket (PASCAL FAR *CNetwork::socket)(int af, int type, int protocol); ssize_t (PASCAL FAR *CNetwork::write)(CNetwork::Socket s, const void FAR * buf, size_t len); -struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyaddr)(const char FAR * addr, int len, int type); -struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyname)(const char FAR * name); int (PASCAL FAR *CNetwork::gethostname)(char FAR * name, int namelen); -struct servent FAR * (PASCAL FAR *CNetwork::getservbyport)(int port, const char FAR * proto); -struct servent FAR * (PASCAL FAR *CNetwork::getservbyname)(const char FAR * name, const char FAR * proto); -struct protoent FAR * (PASCAL FAR *CNetwork::getprotobynumber)(int proto); -struct protoent FAR * (PASCAL FAR *CNetwork::getprotobyname)(const char FAR * name); int (PASCAL FAR *CNetwork::getsockerror)(void); -int (PASCAL FAR *CNetwork::gethosterror)(void); -int (PASCAL FAR *CNetwork::setblocking)(CNetwork::Socket s, bool blocking); -int (PASCAL FAR *CNetwork::setnodelay)(CNetwork::Socket s, bool blocking); #if WINDOWS_LIKE int (PASCAL FAR *CNetwork::select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); int (PASCAL FAR *CNetwork::WSACleanup)(void); int (PASCAL FAR *CNetwork::__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); +struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyaddr_n)(const char FAR * addr, int len, int type); +struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyname_n)(const char FAR * name); +struct servent FAR * (PASCAL FAR *CNetwork::getservbyport_n)(int port, const char FAR * proto); +struct servent FAR * (PASCAL FAR *CNetwork::getservbyname_n)(const char FAR * name, const char FAR * proto); +struct protoent FAR * (PASCAL FAR *CNetwork::getprotobynumber_n)(int proto); +struct protoent FAR * (PASCAL FAR *CNetwork::getprotobyname_n)(const char FAR * name); const int CNetwork::Error = SOCKET_ERROR; const CNetwork::Socket CNetwork::Null = INVALID_SOCKET; @@ -202,7 +199,7 @@ CNetwork::init2( setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); - setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(inet_ntoa_n, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); @@ -211,23 +208,20 @@ CNetwork::init2( setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); - setfunc(gethostbyaddr, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); - setfunc(gethostbyname, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); setfunc(gethostname, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); - setfunc(getservbyport, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); - setfunc(getservbyname, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); - setfunc(getprotobynumber, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); - setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(gethostbyaddr_n, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname_n, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(getservbyport_n, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); + setfunc(getservbyname_n, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); + setfunc(getprotobynumber_n, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); + setfunc(getprotobyname_n, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); setfunc(getsockerror, WSAGetLastError, int (PASCAL FAR *)(void)); - setfunc(gethosterror, WSAGetLastError, int (PASCAL FAR *)(void)); setfunc(WSACleanup, WSACleanup, int (PASCAL FAR *)(void)); setfunc(__WSAFDIsSet, __WSAFDIsSet, int (PASCAL FAR *)(CNetwork::Socket, fd_set FAR *)); setfunc(select, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); - poll = poll2; - read = read2; - write = write2; - setblocking = setblocking2; - setnodelay = setnodelay2; + poll = poll2; + read = read2; + write = write2; s_networkModule = module; } @@ -244,15 +238,130 @@ CNetwork::write2(Socket s, const void FAR* buf, size_t len) return send(s, buf, len, 0); } +CString PASCAL FAR +CNetwork::inet_ntoa(struct in_addr in) +{ + // winsock returns strings per-thread + return CString(inet_ntoa_n(in)); +} + int PASCAL FAR -CNetwork::setblocking2(CNetwork::Socket s, bool blocking) +CNetwork::gethostbyaddr(CHostInfo* hostinfo, + const char FAR * addr, int len, int type) +{ + assert(hostinfo != NULL); + + // winsock returns structures per-thread + struct hostent FAR* info = gethostbyaddr_n(addr, len, type); + if (info == NULL) { + return WSAGetLastError(); + } + else { + CHostInfo tmp(info); + hostinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::gethostbyname(CHostInfo* hostinfo, + const char FAR * name) +{ + assert(hostinfo != NULL); + + // winsock returns structures per-thread + struct hostent FAR* info = gethostbyname_n(name); + if (info == NULL) { + return WSAGetLastError(); + } + else { + CHostInfo tmp(info); + hostinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::getservbyport(CServiceInfo* servinfo, + int port, const char FAR * proto) +{ + assert(servinfo != NULL); + + // winsock returns structures per-thread + struct servent FAR* info = getservbyport_n(port, proto); + if (info == NULL) { + return WSAGetLastError(); + } + else { + CServiceInfo tmp(info); + servinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::getservbyname(CServiceInfo* servinfo, + const char FAR * name, const char FAR * proto) +{ + assert(servinfo != NULL); + + // winsock returns structures per-thread + struct servent FAR* info = getservbyname_n(name, proto); + if (info == NULL) { + return WSAGetLastError(); + } + else { + CServiceInfo tmp(info); + servinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::getprotobynumber(CProtocolInfo* protoinfo, + int proto) +{ + assert(protoinfo != NULL); + + // winsock returns structures per-thread + struct protoinfo FAR* info = getprotobynumber_n(proto); + if (info == NULL) { + return WSAGetLastError(); + } + else { + CProtocolInfo tmp(info); + protoinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::getprotobyname(CProtocolInfo* protoinfo, + const char FAR * name) +{ + assert(protoinfo != NULL); + + // winsock returns structures per-thread + struct protoinfo FAR* info = getprotobyname_n(name); + if (info == NULL) { + return WSAGetLastError(); + } + else { + CProtocolInfo tmp(info); + protoinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::setblocking(CNetwork::Socket s, bool blocking) { int flag = blocking ? 0 : 1; return ioctl(s, FIONBIO, &flag); } int PASCAL FAR -CNetwork::setnodelay2(CNetwork::Socket s, bool nodelay) +CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) { BOOL flag = nodelay ? 1 : 0; setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); @@ -262,6 +371,9 @@ CNetwork::setnodelay2(CNetwork::Socket s, bool nodelay) #if UNIX_LIKE +#include "CMutex.h" +#include "CLock.h" + #if HAVE_SYS_TYPES_H # include #endif @@ -284,6 +396,8 @@ CNetwork::setnodelay2(CNetwork::Socket s, bool nodelay) const int CNetwork::Error = -1; const CNetwork::Socket CNetwork::Null = -1; +static CMutex* s_networkMutex = NULL; + #define setfunc(var, name, type) var = (type)::name UInt32 @@ -313,6 +427,15 @@ CNetwork::swapntohs(UInt16 v) void CNetwork::init() { + assert(s_networkMutex == NULL); + + try { + s_networkMutex = new CMutex; + } + catch (...) { + throw XNetworkFailed(); + } + setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); setfunc(close, close, int (PASCAL FAR *)(Socket s)); @@ -322,7 +445,6 @@ CNetwork::init() setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); - setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); #if HAVE_POLL setfunc(poll, poll, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); @@ -338,23 +460,15 @@ CNetwork::init() setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); setfunc(write, write, ssize_t (PASCAL FAR *)(CNetwork::Socket s, const void FAR * buf, size_t len)); - setfunc(gethostbyaddr, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); - setfunc(gethostbyname, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(getservbyport, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); - setfunc(getservbyname, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); - setfunc(getprotobynumber, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); - setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); gethostname = gethostname2; getsockerror = getsockerror2; - gethosterror = gethosterror2; - setblocking = setblocking2; - setnodelay = setnodelay2; } void CNetwork::cleanup() { - // do nothing + delete s_networkMutex; + s_networkMutex = NULL; } int PASCAL FAR @@ -369,14 +483,130 @@ CNetwork::getsockerror2(void) return errno; } -int PASCAL FAR -CNetwork::gethosterror2(void) +CString PASCAL FAR +CNetwork::inet_ntoa(struct in_addr in) { - return h_errno; + // single threaded access to inet_ntoa functions + CLock lock(s_networkMutex); + return CString(::inet_ntoa(in)); } int PASCAL FAR -CNetwork::setblocking2(CNetwork::Socket s, bool blocking) +CNetwork::gethostbyaddr(CHostInfo* hostinfo, + const char FAR * addr, int len, int type) +{ + assert(hostinfo != NULL); + + // single threaded access to netdb functions + CLock lock(s_networkMutex); + struct hostent FAR* info = ::gethostbyaddr(addr, len, type); + if (info == NULL) { + return h_errno; + } + else { + CHostInfo tmp(info); + hostinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::gethostbyname(CHostInfo* hostinfo, + const char FAR * name) +{ + assert(hostinfo != NULL); + + // single threaded access to netdb functions + CLock lock(s_networkMutex); + struct hostent FAR* info = ::gethostbyname(name); + if (info == NULL) { + return h_errno; + } + else { + CHostInfo tmp(info); + hostinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::getservbyport(CServiceInfo* servinfo, + int port, const char FAR * proto) +{ + assert(servinfo != NULL); + + // single threaded access to netdb functions + CLock lock(s_networkMutex); + struct servent FAR* info = ::getservbyport(port, proto); + if (info == NULL) { + return -1; + } + else { + CServiceInfo tmp(info); + servinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::getservbyname(CServiceInfo* servinfo, + const char FAR * name, const char FAR * proto) +{ + assert(servinfo != NULL); + + // single threaded access to netdb functions + CLock lock(s_networkMutex); + struct servent FAR* info = ::getservbyname(name, proto); + if (info == NULL) { + return -1; + } + else { + CServiceInfo tmp(info); + servinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::getprotobynumber(CProtocolInfo* protoinfo, + int proto) +{ + assert(protoinfo != NULL); + + // single threaded access to netdb functions + CLock lock(s_networkMutex); + struct protoent FAR* info = ::getprotobynumber(proto); + if (info == NULL) { + return -1; + } + else { + CProtocolInfo tmp(info); + protoinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::getprotobyname(CProtocolInfo* protoinfo, + const char FAR * name) +{ + assert(protoinfo != NULL); + + // single threaded access to netdb functions + CLock lock(s_networkMutex); + struct protoent FAR* info = ::getprotobyname(name); + if (info == NULL) { + return -1; + } + else { + CProtocolInfo tmp(info); + protoinfo->swap(tmp); + return 0; + } +} + +int PASCAL FAR +CNetwork::setblocking(CNetwork::Socket s, bool blocking) { int mode = fcntl(s, F_GETFL, 0); if (mode == -1) { @@ -395,7 +625,7 @@ CNetwork::setblocking2(CNetwork::Socket s, bool blocking) } int PASCAL FAR -CNetwork::setnodelay2(CNetwork::Socket s, bool nodelay) +CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) { int flag = nodelay ? 1 : 0; setsockopt(s, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); @@ -502,3 +732,98 @@ CNetwork::poll2(PollEntry fd[], int nfds, int timeout) } #endif + + +// +// CNetwork::CHostInfo +// + +CNetwork::CHostInfo::CHostInfo(const struct hostent* hent) +{ + assert(hent != NULL); + + m_name = hent->h_name; + m_addressType = hent->h_addrtype; + m_addressLength = hent->h_length; + for (char** scan = hent->h_aliases; *scan != NULL; ++scan) { + m_aliases.push_back(*scan); + } + + // concatenate addresses together + UInt32 n = 0; + for (char** scan = hent->h_addr_list; *scan != NULL; ++scan) { + ++n; + } + m_addressData.reserve(n); + for (char** scan = hent->h_addr_list; *scan != NULL; ++scan) { + m_addressData.append(*scan, m_addressLength); + } + + // set pointers into concatenated data + const char* data = m_addressData.data(); + for (char** scan = hent->h_addr_list; *scan != NULL; ++scan) { + m_addresses.push_back(data); + data += m_addressLength; + } +} + +void +CNetwork::CHostInfo::swap(CHostInfo& v) +{ + std::swap(m_name, v.m_name); + std::swap(m_aliases, v.m_aliases); + std::swap(m_addressType, v.m_addressType); + std::swap(m_addressLength, v.m_addressLength); + std::swap(m_addresses, v.m_addresses); + std::swap(m_addressData, v.m_addressData); +} + + +// +// CNetwork::CServiceInfo +// + +CNetwork::CServiceInfo::CServiceInfo(const struct servent* sent) +{ + assert(sent != NULL); + + m_name = sent->s_name; + m_port = sent->s_port; + m_protocol = sent->s_proto; + for (char** scan = sent->s_aliases; *scan != NULL; ++scan) { + m_aliases.push_back(*scan); + } +} + +void +CNetwork::CServiceInfo::swap(CServiceInfo& v) +{ + std::swap(m_name, v.m_name); + std::swap(m_aliases, v.m_aliases); + std::swap(m_port, v.m_port); + std::swap(m_protocol, v.m_protocol); +} + + +// +// CNetwork::CProtocolInfo +// + +CNetwork::CProtocolInfo::CProtocolInfo(const struct protoent* pent) +{ + assert(pent != NULL); + + m_name = pent->p_name; + m_protocol = pent->p_proto; + for (char** scan = pent->p_aliases; *scan != NULL; ++scan) { + m_aliases.push_back(*scan); + } +} + +void +CNetwork::CProtocolInfo::swap(CProtocolInfo& v) +{ + std::swap(m_name, v.m_name); + std::swap(m_aliases, v.m_aliases); + std::swap(m_protocol, v.m_protocol); +} diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h index 7b3ef64c..92205679 100644 --- a/lib/net/CNetwork.h +++ b/lib/net/CNetwork.h @@ -16,6 +16,8 @@ #define CNETWORK_H #include "BasicTypes.h" +#include "CString.h" +#include "stdvector.h" #if HAVE_SYS_TYPES_H # include @@ -82,6 +84,61 @@ public: }; #endif + //! Host name information + class CHostInfo { + public: + CHostInfo() { } + CHostInfo(const struct hostent*); + + void swap(CHostInfo&); + + public: + typedef std::vector AliasList; + typedef std::vector AddressList; + + CString m_name; + AliasList m_aliases; + int m_addressType; + int m_addressLength; + AddressList m_addresses; + + private: + std::string m_addressData; + }; + + //! Network service information + class CServiceInfo { + public: + CServiceInfo() { } + CServiceInfo(const struct servent*); + + void swap(CServiceInfo&); + + public: + typedef std::vector AliasList; + + CString m_name; + AliasList m_aliases; + int m_port; + CString m_protocol; + }; + + //! Network protocol information + class CProtocolInfo { + public: + CProtocolInfo() { } + CProtocolInfo(const struct protoent*); + + void swap(CProtocolInfo&); + + public: + typedef std::vector AliasList; + + CString m_name; + AliasList m_aliases; + int m_protocol; + }; + //! @name manipulators //@{ @@ -142,7 +199,7 @@ public: kNO_RECOVERY = NO_RECOVERY, kTRY_AGAIN = TRY_AGAIN, #endif - kHNone = 0 + kHOST_OK = 0 }; //@} @@ -158,7 +215,7 @@ public: static int (PASCAL FAR *getsockname)(Socket s, Address FAR *name, AddressLength FAR * namelen); static int (PASCAL FAR *getsockopt)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen); static unsigned long (PASCAL FAR *inet_addr)(const char FAR * cp); - static char FAR * (PASCAL FAR *inet_ntoa)(struct in_addr in); + static CString PASCAL FAR inet_ntoa(struct in_addr in); static int (PASCAL FAR *listen)(Socket s, int backlog); static ssize_t (PASCAL FAR *read)(Socket s, void FAR * buf, size_t len); static ssize_t (PASCAL FAR *recv)(Socket s, void FAR * buf, size_t len, int flags); @@ -170,27 +227,26 @@ public: static int (PASCAL FAR *shutdown)(Socket s, int how); static Socket (PASCAL FAR *socket)(int af, int type, int protocol); static ssize_t (PASCAL FAR *write)(Socket s, const void FAR * buf, size_t len); - static struct hostent FAR * (PASCAL FAR *gethostbyaddr)(const char FAR * addr, int len, int type); - static struct hostent FAR * (PASCAL FAR *gethostbyname)(const char FAR * name); static int (PASCAL FAR *gethostname)(char FAR * name, int namelen); - static struct servent FAR * (PASCAL FAR *getservbyport)(int port, const char FAR * proto); - static struct servent FAR * (PASCAL FAR *getservbyname)(const char FAR * name, const char FAR * proto); - static struct protoent FAR * (PASCAL FAR *getprotobynumber)(int proto); - static struct protoent FAR * (PASCAL FAR *getprotobyname)(const char FAR * name); + static int PASCAL FAR gethostbyaddr(CHostInfo* hostinfo, const char FAR * addr, int len, int type); + static int PASCAL FAR gethostbyname(CHostInfo* hostinfo, const char FAR * name); + static int PASCAL FAR getservbyport(CServiceInfo* servinfo, int port, const char FAR * proto); + static int PASCAL FAR getservbyname(CServiceInfo* servinfo, const char FAR * name, const char FAR * proto); + static int PASCAL FAR getprotobynumber(CProtocolInfo* protoinfo, int proto); + static int PASCAL FAR getprotobyname(CProtocolInfo* protoinfo, const char FAR * name); static int (PASCAL FAR *getsockerror)(void); - static int (PASCAL FAR *gethosterror)(void); // convenience functions (only available after init()) //! Set socket to (non-)blocking operation - static int (PASCAL FAR *setblocking)(CNetwork::Socket s, bool blocking); + static int PASCAL FAR setblocking(CNetwork::Socket s, bool blocking); //! Turn Nagle algorithm on or off on socket /*! Set socket to send messages immediately (true) or to collect small messages into one packet (false). */ - static int (PASCAL FAR *setnodelay)(CNetwork::Socket s, bool nodelay); + static int PASCAL FAR setnodelay(CNetwork::Socket s, bool nodelay); private: #if WINDOWS_LIKE @@ -200,19 +256,40 @@ private: static void init2(HMODULE); static ssize_t PASCAL FAR read2(Socket s, void FAR * buf, size_t len); static ssize_t PASCAL FAR write2(Socket s, const void FAR * buf, size_t len); - static int PASCAL FAR setblocking2(CNetwork::Socket s, bool blocking); - static int PASCAL FAR setnodelay2(CNetwork::Socket s, bool nodelay); static int (PASCAL FAR *WSACleanup)(void); static int (PASCAL FAR *__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); static int (PASCAL FAR *select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); + static char FAR * (PASCAL FAR *inet_ntoa_n)(struct in_addr in); + static struct hostent FAR * (PASCAL FAR *gethostbyaddr_n)(const char FAR * addr, int len, int type); + static struct hostent FAR * (PASCAL FAR *gethostbyname_n)(const char FAR * name); + static struct servent FAR * (PASCAL FAR *getservbyport_n)(int port, const char FAR * proto); + static struct servent FAR * (PASCAL FAR *getservbyname_n)(const char FAR * name, const char FAR * proto); + static struct protoent FAR * (PASCAL FAR *getprotobynumber_n)(int proto); + static struct protoent FAR * (PASCAL FAR *getprotobyname_n)(const char FAR * name); #endif #if UNIX_LIKE static int PASCAL FAR gethostname2(char FAR * name, int namelen); static int PASCAL FAR getsockerror2(void); - static int PASCAL FAR gethosterror2(void); - static int PASCAL FAR setblocking2(CNetwork::Socket s, bool blocking); - static int PASCAL FAR setnodelay2(CNetwork::Socket s, bool nodelay); +#endif + +#if WINDOWS_LIKE || UNIX_LIKE +/* FIXME -- reentrant netdb stuff +create classes for hostent, servent, protoent. +each class can clean itself up automatically. +inside CNetwork we'll convert from netdb structs to classes. +clients will pass a class pointer which CNetwork will assign to (or swap into). +won't need free...() functions to clean up structs. +each class should know how to copy from respective netdb struct. +will need to fix CNetworkAddress to use classes. +*/ + static void copyhostent(struct hostent FAR * dst, const struct hostent FAR * src); + static void copyservent(struct servent FAR * dst, const struct servent FAR * src); + static void copyprotoent(struct protoent FAR * dst, const struct protoent FAR * src); + + static void freehostent(struct hostent FAR * ent); + static void freeservent(struct servent FAR * ent); + static void freeprotoent(struct protoent FAR * ent); #endif #if WINDOWS_LIKE || !HAVE_POLL diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp index ff0515d6..7e284208 100644 --- a/lib/net/CNetworkAddress.cpp +++ b/lib/net/CNetworkAddress.cpp @@ -126,27 +126,29 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : } // look up name - struct hostent* hent = CNetwork::gethostbyname(hostname.c_str()); - if (hent == NULL) { - switch (CNetwork::gethosterror()) { - case CNetwork::kHOST_NOT_FOUND: - throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); + CNetwork::CHostInfo hostInfo; + switch (CNetwork::gethostbyname(&hostInfo, hostname.c_str())) { + case CNetwork::kHOST_OK: + break; - case CNetwork::kNO_DATA: - throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); + case CNetwork::kHOST_NOT_FOUND: + throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); - case CNetwork::kNO_RECOVERY: - case CNetwork::kTRY_AGAIN: - default: - throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); - } + case CNetwork::kNO_DATA: + throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); + + case CNetwork::kNO_RECOVERY: + case CNetwork::kTRY_AGAIN: + default: + throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); } struct sockaddr_in* inetAddress = reinterpret_cast< struct sockaddr_in*>(&m_address); - inetAddress->sin_family = hent->h_addrtype; + inetAddress->sin_family = hostInfo.m_addressType; inetAddress->sin_port = CNetwork::swaphtons(port); - memcpy(&inetAddress->sin_addr, hent->h_addr_list[0], hent->h_length); + memcpy(&inetAddress->sin_addr, hostInfo.m_addresses[0], + hostInfo.m_addressLength); memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } From 1d7f3d2aaf851bc22b50e1b800980d02bc6d6a5e Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 17 Oct 2002 21:37:31 +0000 Subject: [PATCH 366/807] Changed server to fail with an error if in can't bind() the listen socket for any reason other than it's in use. --- lib/server/CServer.cpp | 34 +++++++++++++++++++++++++++++++--- lib/server/CServer.h | 22 ++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index a4d124f0..aede8ca9 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -46,6 +46,7 @@ const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; CServer::CServer(const CString& serverName) : m_name(serverName), + m_error(false), m_bindTimeout(5.0 * 60.0), m_screenFactory(NULL), m_socketFactory(NULL), @@ -161,6 +162,12 @@ CServer::mainLoop() throw; } #undef FINALLY + + // throw if there was an error + if (m_error) { + LOG((CLOG_DEBUG "forwarding child thread exception")); + throw XServerRethrow(); + } } void @@ -169,6 +176,16 @@ CServer::exitMainLoop() m_primaryClient->exitMainLoop(); } +void +CServer::exitMainLoopWithError() +{ + { + CLock lock(&m_mutex); + m_error = true; + } + exitMainLoop(); +} + void CServer::close() { @@ -1186,7 +1203,7 @@ CServer::acceptClients(void*) listen->bind(m_config.getSynergyAddress()); break; } - catch (XSocketBind& e) { + catch (XSocketAddressInUse& e) { LOG((CLOG_WARN "bind failed: %s", e.getErrstr())); // give up if we've waited too long @@ -1220,7 +1237,7 @@ CServer::acceptClients(void*) catch (XBase& e) { LOG((CLOG_ERR "cannot listen for clients: %s", e.what())); delete listen; - exitMainLoop(); + exitMainLoopWithError(); } catch (...) { delete listen; @@ -1505,7 +1522,7 @@ CServer::acceptHTTPClients(void*) catch (XBase& e) { LOG((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); delete listen; - exitMainLoop(); + exitMainLoopWithError(); } catch (...) { delete listen; @@ -1691,6 +1708,17 @@ CServer::removeConnection(const CString& name) } +// +// CServer::CClipboardInfo +// + +CString +CServer::XServerRethrow::getWhat() const throw() +{ + return format("XServerRethrow", "child thread failed"); +} + + // // CServer::CClipboardInfo // diff --git a/lib/server/CServer.h b/lib/server/CServer.h index e9a1224f..49583c6e 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -157,6 +157,14 @@ protected: */ bool onCommandKey(KeyID, KeyModifierMask, bool down); + //! Exit event loop and note an error condition + /*! + Force mainLoop() to return by throwing an exception. This call + can return before mainLoop() does (i.e. asynchronously). This + may only be called between a successful open() and close(). + */ + void exitMainLoopWithError(); + private: typedef std::list CThreadList; @@ -230,6 +238,12 @@ private: void removeConnection(const CString& name); private: + class XServerRethrow : public XBase { + protected: + // XBase overrides + virtual CString getWhat() const throw(); + }; + class CClipboardInfo { public: CClipboardInfo(); @@ -246,6 +260,14 @@ private: // the name of the primary screen CString m_name; + // true if we should exit the main loop by throwing an exception. + // this is used to propagate an exception from one of our threads + // to the mainLoop() thread. but, since we can't make a copy of + // the original exception, we return an arbitrary, unique + // exception type. the caller of mainLoop() cannot catch this + // exception except through XBase or .... + bool m_error; + // how long to wait to bind our socket until we give up double m_bindTimeout; From 09e0750a1147bc7eff061959ea2a986e38d5e596 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 17 Oct 2002 21:37:37 +0000 Subject: [PATCH 367/807] Fixed CThreadRep to not raise a signal on the thread if it's already dead. Otherwise the signal could propagate to the parent thread (at least on linux threads) and cause havoc. --- lib/mt/CThreadRep.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp index 1e487cf2..bc92ba8b 100644 --- a/lib/mt/CThreadRep.cpp +++ b/lib/mt/CThreadRep.cpp @@ -389,7 +389,7 @@ void CThreadRep::cancel() { CLock lock(s_mutex); - if (m_cancellable && !m_cancelling) { + if (!m_exit && m_cancellable && !m_cancelling) { m_cancel = true; } else { From 285cc3abc02fea4aa47d43758a1341f44cd964d2 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 17 Oct 2002 21:37:41 +0000 Subject: [PATCH 368/807] Fixed CXWindowsScreen to force the event loop to wake up when exitMainLoop() is called. --- lib/platform/CXWindowsScreen.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 5cf8fe80..2ebbbaf9 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -288,6 +288,7 @@ CXWindowsScreen::mainLoop() // if the thread has been cancelled. poll() should return -1 // with EINTR when the thread is cancelled. m_mutex.unlock(); + CThread::testCancel(); #if HAVE_POLL poll(pfds, 1, timeout); #else @@ -358,8 +359,28 @@ CXWindowsScreen::mainLoop() void CXWindowsScreen::exitMainLoop() { + // m_stop should be a condition variable that we signal here + // but we can't wait on both the condition variable and the + // X connection so there's no point. however, we do need + // to wake up the X connection so send ourself some event. CLock lock(&m_mutex); m_stop = true; + + if (m_display != NULL && m_window != None) { + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.display = m_display; + event.xclient.window = m_window; + event.xclient.message_type = XInternAtom(m_display, "ATOM", False); + event.xclient.format = 32; + event.xclient.data.l[0] = 0; + event.xclient.data.l[1] = 0; + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + CXWindowsUtil::CErrorLock lock(m_display); + XSendEvent(m_display, m_window, False, 0, &event); + } } void From 8f229393b814919cb85abc9344878f21cc4924ec Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 20 Oct 2002 22:36:24 +0000 Subject: [PATCH 369/807] Replaced inet_addr() with inet_aton(), which is a better function anyway but isn't implemented in winsock, removed use of INADDR_NONE which some platforms don't define except on winsock which does define it, and changed SOL_TCP to IPPROTO_TCP which should work on more platforms. --- lib/net/CNetwork.cpp | 30 +++++++++++++++++++++++++----- lib/net/CNetwork.h | 5 ++++- lib/net/CNetworkAddress.cpp | 16 +++++----------- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp index 7f7398a0..1410876f 100644 --- a/lib/net/CNetwork.cpp +++ b/lib/net/CNetwork.cpp @@ -29,7 +29,6 @@ int (PASCAL FAR *CNetwork::ioctl)(CNetwork::Socket s, int cmd, void FAR *); int (PASCAL FAR *CNetwork::getpeername)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); int (PASCAL FAR *CNetwork::getsockname)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); int (PASCAL FAR *CNetwork::getsockopt)(CNetwork::Socket s, int level, int optname, void FAR * optval, CNetwork::AddressLength FAR *optlen); -unsigned long (PASCAL FAR *CNetwork::inet_addr)(const char FAR * cp); int (PASCAL FAR *CNetwork::listen)(CNetwork::Socket s, int backlog); ssize_t (PASCAL FAR *CNetwork::read)(CNetwork::Socket s, void FAR * buf, size_t len); ssize_t (PASCAL FAR *CNetwork::recv)(CNetwork::Socket s, void FAR * buf, size_t len, int flags); @@ -198,7 +197,7 @@ CNetwork::init2( setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); - setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(inet_addr_n, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); setfunc(inet_ntoa_n, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); @@ -238,6 +237,22 @@ CNetwork::write2(Socket s, const void FAR* buf, size_t len) return send(s, buf, len, 0); } +int PASCAL FAR +CNetwork::inet_aton(const char FAR * cp, InternetAddress FAR * addr) +{ + assert(addr != NULL); + + // fake it with inet_addr + unsigned long inetAddr = inet_addr_n(cp); + if (inetAddr == INADDR_NONE) { + return 0; + } + else { + *(addr->s_addr) = inetAddr; + return 1; + } +} + CString PASCAL FAR CNetwork::inet_ntoa(struct in_addr in) { @@ -387,7 +402,7 @@ CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) #include #include #include -#if !defined(TCP_NODELAY) || !defined(SOL_TCP) +#if !defined(TCP_NODELAY) # include #endif @@ -444,7 +459,6 @@ CNetwork::init() setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); - setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); #if HAVE_POLL setfunc(poll, poll, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); @@ -483,6 +497,12 @@ CNetwork::getsockerror2(void) return errno; } +int PASCAL FAR +CNetwork::inet_aton(const char FAR * cp, InternetAddress FAR * addr) +{ + return ::inet_aton(cp, addr); +} + CString PASCAL FAR CNetwork::inet_ntoa(struct in_addr in) { @@ -628,7 +648,7 @@ int PASCAL FAR CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) { int flag = nodelay ? 1 : 0; - setsockopt(s, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); } #endif diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h index 92205679..e0869ea6 100644 --- a/lib/net/CNetwork.h +++ b/lib/net/CNetwork.h @@ -55,10 +55,12 @@ public: typedef SOCKET Socket; typedef struct sockaddr Address; typedef int AddressLength; + typedef struct in_addr InternetAddress; #elif UNIX_LIKE typedef int Socket; typedef struct sockaddr Address; typedef socklen_t AddressLength; + typedef struct in_addr InternetAddress; #endif #if WINDOWS_LIKE || !HAVE_POLL @@ -214,7 +216,7 @@ public: static int (PASCAL FAR *getpeername)(Socket s, Address FAR *name, AddressLength FAR * namelen); static int (PASCAL FAR *getsockname)(Socket s, Address FAR *name, AddressLength FAR * namelen); static int (PASCAL FAR *getsockopt)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen); - static unsigned long (PASCAL FAR *inet_addr)(const char FAR * cp); + static int PASCAL FAR inet_aton(const char FAR * cp, InternetAddress FAR * addr); static CString PASCAL FAR inet_ntoa(struct in_addr in); static int (PASCAL FAR *listen)(Socket s, int backlog); static ssize_t (PASCAL FAR *read)(Socket s, void FAR * buf, size_t len); @@ -260,6 +262,7 @@ private: static int (PASCAL FAR *__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); static int (PASCAL FAR *select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); static char FAR * (PASCAL FAR *inet_ntoa_n)(struct in_addr in); + static unsigned long (PASCAL FAR *inet_addr_n)(const char FAR * cp); static struct hostent FAR * (PASCAL FAR *gethostbyaddr_n)(const char FAR * addr, int len, int type); static struct hostent FAR * (PASCAL FAR *gethostbyname_n)(const char FAR * name); static struct servent FAR * (PASCAL FAR *getservbyport_n)(int port, const char FAR * proto); diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp index 7e284208..d626b31d 100644 --- a/lib/net/CNetworkAddress.cpp +++ b/lib/net/CNetworkAddress.cpp @@ -103,9 +103,9 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : } // if hostname is empty then use wildcard address - if (hostname.empty()) { - struct sockaddr_in* inetAddress = reinterpret_cast< + struct sockaddr_in* inetAddress = reinterpret_cast< struct sockaddr_in*>(&m_address); + if (hostname.empty()) { inetAddress->sin_family = AF_INET; inetAddress->sin_port = CNetwork::swaphtons(port); inetAddress->sin_addr.s_addr = INADDR_ANY; @@ -114,13 +114,9 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : } // convert dot notation to address - unsigned long addr = CNetwork::inet_addr(hostname.c_str()); - if (addr != INADDR_NONE) { - struct sockaddr_in* inetAddress = reinterpret_cast< - struct sockaddr_in*>(&m_address); - inetAddress->sin_family = AF_INET; - inetAddress->sin_port = CNetwork::swaphtons(port); - inetAddress->sin_addr.s_addr = addr; + if (CNetwork::inet_aton(hostname.c_str(), &inetAddress->sin_addr) != 0) { + inetAddress->sin_family = AF_INET; + inetAddress->sin_port = CNetwork::swaphtons(port); memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); return; } @@ -143,8 +139,6 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); } - struct sockaddr_in* inetAddress = reinterpret_cast< - struct sockaddr_in*>(&m_address); inetAddress->sin_family = hostInfo.m_addressType; inetAddress->sin_port = CNetwork::swaphtons(port); memcpy(&inetAddress->sin_addr, hostInfo.m_addresses[0], From 66c4b66d7c0292aec8a0bf6f0aa3f732c87cf4e6 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 20 Oct 2002 22:39:54 +0000 Subject: [PATCH 370/807] Fixed conditional to test for multimon to do nasty win32 mouse positioning hack. Was doing hack if *not* a multiple monitor system but should've been doing it if is *is* a multimon system. --- lib/client/CMSWindowsSecondaryScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index 4357e615..a7fc1246 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -344,7 +344,7 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) // move the mouse directly to target position on NT family or if // not using multiple monitors. - if (m_screen->isMultimon() || !m_is95Family) { + if (!m_screen->isMultimon() || !m_is95Family) { SInt32 x0, y0, w, h; m_screen->getShape(x0, y0, w, h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, From 0ce15c1a9b8007ccbd4a887ab8c65079d73d760c Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 22 Oct 2002 21:30:48 +0000 Subject: [PATCH 371/807] Moved CUnicode to lib/io. That's a reasonable place for it that's after lib/mt. It needs to be after lib/mt in preparation for supporting platforms without the reentrant wide char and multi-byte functions. --- lib/base/Makefile.am | 2 -- lib/{base => io}/CUnicode.cpp | 0 lib/{base => io}/CUnicode.h | 0 lib/io/Makefile.am | 2 ++ lib/platform/Makefile.am | 1 + 5 files changed, 3 insertions(+), 2 deletions(-) rename lib/{base => io}/CUnicode.cpp (100%) rename lib/{base => io}/CUnicode.h (100%) diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index c02cf460..19ba6847 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -29,14 +29,12 @@ libbase_a_SOURCES = \ CLog.cpp \ CStopwatch.cpp \ CString.cpp \ - CUnicode.cpp \ XBase.cpp \ BasicTypes.h \ CFunctionJob.h \ CLog.h \ CStopwatch.h \ CString.h \ - CUnicode.h \ IInterface.h \ IJob.h \ TMethodJob.h \ diff --git a/lib/base/CUnicode.cpp b/lib/io/CUnicode.cpp similarity index 100% rename from lib/base/CUnicode.cpp rename to lib/io/CUnicode.cpp diff --git a/lib/base/CUnicode.h b/lib/io/CUnicode.h similarity index 100% rename from lib/base/CUnicode.h rename to lib/io/CUnicode.h diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index 1309e7d5..69665e80 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -30,12 +30,14 @@ libio_a_SOURCES = \ CInputStreamFilter.cpp \ COutputStreamFilter.cpp \ CStreamBuffer.cpp \ + CUnicode.cpp \ XIO.cpp \ CBufferedInputStream.h \ CBufferedOutputStream.h \ CInputStreamFilter.h \ COutputStreamFilter.h \ CStreamBuffer.h \ + CUnicode.h s\ IInputStream.h \ IOutputStream.h \ IStreamFilterFactory.h \ diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 8da68bf3..53511b5d 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -69,5 +69,6 @@ EXTRA_libplatform_a_SOURCES = \ INCLUDES = \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ -I$(VDEPTH)/lib/synergy \ $(NULL) From 8dbc9d62bc047accdf9814cb958cb95f3fc3723d Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 22 Oct 2002 22:35:13 +0000 Subject: [PATCH 372/807] Added workarounds for missing reentrant versions of wide char to/from multi-byte conversion functions. --- configure.in | 2 ++ lib/io/CUnicode.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/configure.in b/configure.in index 56346272..b92c7539 100644 --- a/configure.in +++ b/configure.in @@ -52,6 +52,7 @@ AC_TYPE_SIZE_T dnl checks for structures AC_STRUCT_TM +AC_CHECK_TYPES(mbstate_t,,,[#include ]) dnl checks for compiler characteristics AC_CHECK_SIZEOF(char, 1) @@ -72,6 +73,7 @@ AC_FUNC_STRFTIME AC_CHECK_FUNCS(gmtime_r) AC_CHECK_FUNCS(getpwuid_r) AC_CHECK_FUNCS(vsnprintf) +AC_CHECK_FUNCS(wcrtomb mbrtowc mbsinit) AC_FUNC_SELECT_ARGTYPES ACX_CHECK_POLL dnl use AC_REPLACE_FUNCS() for stuff in string.h diff --git a/lib/io/CUnicode.cpp b/lib/io/CUnicode.cpp index 383d2381..ada84841 100644 --- a/lib/io/CUnicode.cpp +++ b/lib/io/CUnicode.cpp @@ -70,6 +70,61 @@ setError(bool* errors) } } +// +// multibyte conversion stuff when reentrant versions not available +// + +#if !HAVE_MBSTATE_T +struct mbstate_t { int m_dummy; }; +#undef HAVE_MBSINIT +#undef HAVE_MBRTOWC +#undef HAVE_WCRTOMB +#endif + +#if !HAVE_MBSINIT +static +int +mbsinit(const mbstate_t*) +{ + return 1; +} +#endif + +#if !HAVE_MBRTOWC +#include "CLock.h" +#include "CMutex.h" + +static CMutex s_mbrtowcMutex; + +static +size_t +mbrtowc(wchar_t* pwc, const char* s, size_t n, mbstate_t*) +{ + CLock lock(&s_mbrtowcMutex); + int result = mbtowc(pwc, s, n); + if (result < 0) + return (size_t)-1; + else + return result; +} +#endif + +#if !HAVE_WCRTOMB +#include "CLock.h" +#include "CMutex.h" + +static CMutex s_wcrtombMutex; + +static +size_t +wcrtomb(char* s, wchar_t wc, mbstate_t*) +{ + CLock lock(&s_wcrtombMutex); + return (size_t)wctomb(s, wc); +} +#endif + + // // CUnicode // From 24119802c6086a66030a28eb79d5995cf5cd230d Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 28 Oct 2002 21:33:48 +0000 Subject: [PATCH 373/807] Removed obsolete comment. --- lib/net/CNetwork.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp index 1410876f..906eeb2d 100644 --- a/lib/net/CNetwork.cpp +++ b/lib/net/CNetwork.cpp @@ -406,8 +406,6 @@ CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) # include #endif -// FIXME -- use reentrant versions of non-reentrant functions - const int CNetwork::Error = -1; const CNetwork::Socket CNetwork::Null = -1; From 541276595ea0487b3922583f257ea472b2336259 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 28 Oct 2002 22:49:21 +0000 Subject: [PATCH 374/807] solaris configure and build fixes. without having solaris i can only hope that these changes actually work. --- acinclude.m4 | 57 +++++++++++++++++++++++++++++++++++++++++++ configure.in | 8 +++--- lib/mt/CThreadRep.cpp | 30 +++++++++++++++++++++++ 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 155c47ea..841621f0 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -90,6 +90,63 @@ AC_DEFUN([ACX_CHECK_POLL], [ fi ])dnl ACX_CHECK_POLL +dnl See if we need extra libraries for nanosleep +AC_DEFUN([ACX_CHECK_NANOSLEEP], [ + acx_nanosleep_ok=no + acx_nanosleep_list="" + + dnl check if user has set NANOSLEEP_LIBS + save_user_NANOSLEEP_LIBS="$NANOSLEEP_LIBS" + if test x"$NANOSLEEP_LIBS" != x; then + acx_nanosleep_list=user + fi + + dnl check various libraries (including no extra libraries) for + dnl nanosleep. `none' should appear first. + acx_nanosleep_list="none $acx_nanosleep_list rt" + for flag in $acx_nanosleep_list; do + case $flag in + none) + AC_MSG_CHECKING([for nanosleep]) + NANOSLEEP_LIBS="" + ;; + + user) + AC_MSG_CHECKING([for nanosleep in $save_user_NANOSLEEP_LIBS]) + NANOSLEEP_LIBS="$save_user_NANOSLEEP_LIBS" + ;; + + *) + AC_MSG_CHECKING([for nanosleep in -l$flag]) + NANOSLEEP_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$NANOSLEEP_LIBS $LIBS" + AC_TRY_LINK([#include ], + [struct timespec t = { 1, 1000 }; nanosleep(&t, NULL);], + acx_nanosleep_ok=yes, acx_nanosleep_ok=no) + LIBS="$save_LIBS" + AC_MSG_RESULT($acx_nanosleep_ok) + if test x"$acx_nanosleep_ok" = xyes; then + break; + fi + NANOSLEEP_LIBS="" + done + + AC_SUBST(NANOSLEEP_LIBS) + + # execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: + if test x"$acx_nanosleep_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_NANOSLEEP,1,[Define if you have the `nanosleep' function.]),[$1]) + : + else + acx_nanosleep_ok=no + $2 + fi +])dnl ACX_CHECK_POLL + dnl The following macros are from http://www.gnu.org/software/ac-archive/ dnl which distributes them under the following license: dnl diff --git a/configure.in b/configure.in index b92c7539..75ec64de 100644 --- a/configure.in +++ b/configure.in @@ -31,12 +31,13 @@ dnl checks for programs AC_PROG_CXX AC_PROG_RANLIB -dnl checks for libraries -ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) - dnl do checks using C++ AC_LANG_CPLUSPLUS +dnl checks for libraries +ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) +ACX_CHECK_NANOSLEEP + dnl checks for header files AC_HEADER_STDC AC_CHECK_HEADERS([unistd.h sys/time.h]) @@ -83,6 +84,7 @@ dnl checks for system services dnl adjust variables for X11 and pthreads CXXFLAGS="$CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" +LIBS="$NANOSLEEP_LIBS $PTHREAD_LIBS $LIBS" AC_OUTPUT([ Makefile diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp index bc92ba8b..37ff52cf 100644 --- a/lib/mt/CThreadRep.cpp +++ b/lib/mt/CThreadRep.cpp @@ -351,6 +351,17 @@ CThreadRep::doThreadFunc() # include # endif #endif +#if !HAVE_NANOSLEEP +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include +# endif +#endif void CThreadRep::init() @@ -378,11 +389,30 @@ CThreadRep::sleep( if (timeout < 0.0) { return; } +#if HAVE_NANOSLEEP struct timespec t; t.tv_sec = (long)timeout; t.tv_nsec = (long)(1000000000.0 * (timeout - (double)t.tv_sec)); while (nanosleep(&t, &t) < 0) testCancel(); +#else + /* emulate nanosleep() with select() */ + CStopwatch timer(true); + double timeLeft = timeout - timer.getTime(); + while (timeLeft > 0.0) { + struct timeval timeout2; + timeout2.tv_sec = static_cast(timeLeft); + timeout2.tv_usec = static_cast(1.0e+6 * (timeLeft - + timeout2.tv_sec)); + select((SELECT_TYPE_ARG1) 0, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 &timeout2); + testCancel(); + timeLeft = timeout - timer.getTime(); + } +#endif } void From e06368b9507981e554070309b27d574ef601f4f1 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 29 Oct 2002 07:40:43 +0000 Subject: [PATCH 375/807] fixed typo in makefile. --- lib/io/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index 69665e80..3ea4e3af 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -37,7 +37,7 @@ libio_a_SOURCES = \ CInputStreamFilter.h \ COutputStreamFilter.h \ CStreamBuffer.h \ - CUnicode.h s\ + CUnicode.h \ IInputStream.h \ IOutputStream.h \ IStreamFilterFactory.h \ From 9102fb80b943a58bbdd65d5a8692fedda47bd11e Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 29 Oct 2002 22:07:55 +0000 Subject: [PATCH 376/807] Ported recent changes to win32 and fixed CRLF problems with project files (most had CRCRCRLF). --- all.dsp | 126 ++++++------ lib/base/base.dsp | 8 - lib/client/client.dsp | 268 ++++++++++++------------- lib/http/http.dsp | 220 ++++++++++----------- lib/io/CUnicode.cpp | 7 + lib/io/io.dsp | 316 +++++++++++++++--------------- lib/mt/mt.dsp | 300 ++++++++++++++-------------- lib/net/CNetwork.cpp | 35 ++-- lib/net/CNetwork.h | 4 +- lib/net/net.dsp | 324 +++++++++++++++---------------- lib/platform/makehook.dsp | 126 ++++++------ lib/platform/platform.dsp | 350 ++++++++++++++++----------------- lib/platform/synrgyhk.dsp | 232 +++++++++++----------- lib/server/server.dsp | 332 +++++++++++++++---------------- lib/synergy/libsynergy.dsp | 388 ++++++++++++++++++------------------- 15 files changed, 1528 insertions(+), 1508 deletions(-) diff --git a/all.dsp b/all.dsp index 6f01a095..e2ce73d3 100644 --- a/all.dsp +++ b/all.dsp @@ -1,63 +1,63 @@ -# Microsoft Developer Studio Project File - Name="all" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Generic Project" 0x010a - -CFG=all - Win32 Debug -!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 "all.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 "all.mak" CFG="all - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "all - Win32 Release" (based on "Win32 (x86) Generic Project") -!MESSAGE "all - Win32 Debug" (based on "Win32 (x86) Generic Project") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -MTL=midl.exe - -!IF "$(CFG)" == "all - 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" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "all - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "all___Win32_Debug" -# PROP BASE Intermediate_Dir "all___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "all - Win32 Release" -# Name "all - Win32 Debug" -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="all" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=all - Win32 Debug +!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 "all.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 "all.mak" CFG="all - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "all - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "all - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "all - 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" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "all - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "all___Win32_Debug" +# PROP BASE Intermediate_Dir "all___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "all - Win32 Release" +# Name "all - Win32 Debug" +# End Target +# End Project diff --git a/lib/base/base.dsp b/lib/base/base.dsp index 66dc0a8e..42a3ec67 100644 --- a/lib/base/base.dsp +++ b/lib/base/base.dsp @@ -103,10 +103,6 @@ SOURCE=.\CString.cpp # End Source File # Begin Source File -SOURCE=.\CUnicode.cpp -# End Source File -# Begin Source File - SOURCE=.\XBase.cpp # End Source File # End Group @@ -139,10 +135,6 @@ SOURCE=.\CString.h # End Source File # Begin Source File -SOURCE=.\CUnicode.h -# End Source File -# Begin Source File - SOURCE=.\IInterface.h # End Source File # Begin Source File diff --git a/lib/client/client.dsp b/lib/client/client.dsp index 0fd4a4d8..5880f495 100644 --- a/lib/client/client.dsp +++ b/lib/client/client.dsp @@ -1,134 +1,134 @@ -# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=client - Win32 Debug -!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 "client.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 "client.mak" CFG="client - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "client - Win32 Debug" (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)" == "client - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "client - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "client - Win32 Release" -# Name "client - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CClient.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsSecondaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CSecondaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CServerProxy.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CClient.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsSecondaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CSecondaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CServerProxy.h -# End Source File -# Begin Source File - -SOURCE=.\ISecondaryScreenFactory.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=client - Win32 Debug +!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 "client.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 "client.mak" CFG="client - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "client - Win32 Debug" (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)" == "client - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "client - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "client - Win32 Release" +# Name "client - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CSecondaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerProxy.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClient.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CSecondaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CServerProxy.h +# End Source File +# Begin Source File + +SOURCE=.\ISecondaryScreenFactory.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/http/http.dsp b/lib/http/http.dsp index 2f050878..5d3a6e7b 100644 --- a/lib/http/http.dsp +++ b/lib/http/http.dsp @@ -1,110 +1,110 @@ -# Microsoft Developer Studio Project File - Name="http" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=http - Win32 Debug -!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 "http.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 "http.mak" CFG="http - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "http - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "http - Win32 Debug" (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)" == "http - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "http - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "http - Win32 Release" -# Name "http - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CHTTPProtocol.cpp -# End Source File -# Begin Source File - -SOURCE=.\XHTTP.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CHTTPProtocol.h -# End Source File -# Begin Source File - -SOURCE=.\XHTTP.h -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="http" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=http - Win32 Debug +!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 "http.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 "http.mak" CFG="http - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "http - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "http - Win32 Debug" (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)" == "http - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "http - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "http - Win32 Release" +# Name "http - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CHTTPProtocol.cpp +# End Source File +# Begin Source File + +SOURCE=.\XHTTP.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CHTTPProtocol.h +# End Source File +# Begin Source File + +SOURCE=.\XHTTP.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/io/CUnicode.cpp b/lib/io/CUnicode.cpp index ada84841..0fe85dc3 100644 --- a/lib/io/CUnicode.cpp +++ b/lib/io/CUnicode.cpp @@ -74,6 +74,13 @@ setError(bool* errors) // multibyte conversion stuff when reentrant versions not available // +#if WINDOWS_LIKE +#define HAVE_MBSTATE_T 1 +#define HAVE_MBSINIT 1 +#define HAVE_MBRTOWC 1 +#define HAVE_WCRTOMB 1 +#endif + #if !HAVE_MBSTATE_T struct mbstate_t { int m_dummy; }; #undef HAVE_MBSINIT diff --git a/lib/io/io.dsp b/lib/io/io.dsp index c9983c9c..02159a1a 100644 --- a/lib/io/io.dsp +++ b/lib/io/io.dsp @@ -1,154 +1,162 @@ -# Microsoft Developer Studio Project File - Name="io" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=io - Win32 Debug -!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 "io.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 "io.mak" CFG="io - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "io - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "io - Win32 Debug" (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)" == "io - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "io - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "io - Win32 Release" -# Name "io - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CBufferedInputStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\CBufferedOutputStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\CInputStreamFilter.cpp -# End Source File -# Begin Source File - -SOURCE=.\COutputStreamFilter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CStreamBuffer.cpp -# End Source File -# Begin Source File - -SOURCE=.\XIO.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CBufferedInputStream.h -# End Source File -# Begin Source File - -SOURCE=.\CBufferedOutputStream.h -# End Source File -# Begin Source File - -SOURCE=.\CInputStreamFilter.h -# End Source File -# Begin Source File - -SOURCE=.\COutputStreamFilter.h -# End Source File -# Begin Source File - -SOURCE=.\CStreamBuffer.h -# End Source File -# Begin Source File - -SOURCE=.\IInputStream.h -# End Source File -# Begin Source File - -SOURCE=.\IOutputStream.h -# End Source File -# Begin Source File - -SOURCE=.\IStreamFilterFactory.h -# End Source File -# Begin Source File - -SOURCE=.\XIO.h -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="io" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=io - Win32 Debug +!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 "io.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 "io.mak" CFG="io - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "io - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "io - Win32 Debug" (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)" == "io - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "io - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "io - Win32 Release" +# Name "io - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CBufferedInputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CBufferedOutputStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInputStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CStreamBuffer.cpp +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.cpp +# End Source File +# Begin Source File + +SOURCE=.\XIO.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CBufferedInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CBufferedOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\CInputStreamFilter.h +# End Source File +# Begin Source File + +SOURCE=.\COutputStreamFilter.h +# End Source File +# Begin Source File + +SOURCE=.\CStreamBuffer.h +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.h +# End Source File +# Begin Source File + +SOURCE=.\IInputStream.h +# End Source File +# Begin Source File + +SOURCE=.\IOutputStream.h +# End Source File +# Begin Source File + +SOURCE=.\IStreamFilterFactory.h +# End Source File +# Begin Source File + +SOURCE=.\XIO.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp index e179518e..2cfdc7a1 100644 --- a/lib/mt/mt.dsp +++ b/lib/mt/mt.dsp @@ -1,146 +1,154 @@ -# Microsoft Developer Studio Project File - Name="mt" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=mt - Win32 Debug -!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 "mt.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 "mt.mak" CFG="mt - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "mt - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "mt - Win32 Debug" (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)" == "mt - 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" -# 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 /W4 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "mt - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "mt - Win32 Release" -# Name "mt - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CCondVar.cpp -# End Source File -# Begin Source File - -SOURCE=.\CLock.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMutex.cpp -# End Source File -# Begin Source File - -SOURCE=.\CThread.cpp -# End Source File -# Begin Source File - -SOURCE=.\CThreadRep.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTimerThread.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CCondVar.h -# End Source File -# Begin Source File - -SOURCE=.\CLock.h -# End Source File -# Begin Source File - -SOURCE=.\CMutex.h -# End Source File -# Begin Source File - -SOURCE=.\CThread.h -# End Source File -# Begin Source File - -SOURCE=.\CThreadRep.h -# End Source File -# Begin Source File - -SOURCE=.\CTimerThread.h -# End Source File -# Begin Source File - -SOURCE=.\XThread.h -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="mt" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=mt - Win32 Debug +!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 "mt.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 "mt.mak" CFG="mt - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mt - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "mt - Win32 Debug" (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)" == "mt - 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" +# 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 /W4 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "mt - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "mt - Win32 Release" +# Name "mt - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CCondVar.cpp +# End Source File +# Begin Source File + +SOURCE=.\CLock.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMutex.cpp +# End Source File +# Begin Source File + +SOURCE=.\CThread.cpp +# End Source File +# Begin Source File + +SOURCE=.\CThreadRep.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.cpp +# End Source File +# Begin Source File + +SOURCE=.\XMT.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CCondVar.h +# End Source File +# Begin Source File + +SOURCE=.\CLock.h +# End Source File +# Begin Source File + +SOURCE=.\CMutex.h +# End Source File +# Begin Source File + +SOURCE=.\CThread.h +# End Source File +# Begin Source File + +SOURCE=.\CThreadRep.h +# End Source File +# Begin Source File + +SOURCE=.\CTimerThread.h +# End Source File +# Begin Source File + +SOURCE=.\XMT.h +# End Source File +# Begin Source File + +SOURCE=.\XThread.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp index 906eeb2d..e2184cd8 100644 --- a/lib/net/CNetwork.cpp +++ b/lib/net/CNetwork.cpp @@ -48,6 +48,8 @@ int (PASCAL FAR *CNetwork::getsockerror)(void); int (PASCAL FAR *CNetwork::select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); int (PASCAL FAR *CNetwork::WSACleanup)(void); int (PASCAL FAR *CNetwork::__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); +char FAR * (PASCAL FAR *CNetwork::inet_ntoa_n)(struct in_addr in); +unsigned long (PASCAL FAR *CNetwork::inet_addr_n)(const char FAR * cp); struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyaddr_n)(const char FAR * addr, int len, int type); struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyname_n)(const char FAR * name); struct servent FAR * (PASCAL FAR *CNetwork::getservbyport_n)(int port, const char FAR * proto); @@ -248,7 +250,7 @@ CNetwork::inet_aton(const char FAR * cp, InternetAddress FAR * addr) return 0; } else { - *(addr->s_addr) = inetAddr; + addr->s_addr = inetAddr; return 1; } } @@ -269,7 +271,7 @@ CNetwork::gethostbyaddr(CHostInfo* hostinfo, // winsock returns structures per-thread struct hostent FAR* info = gethostbyaddr_n(addr, len, type); if (info == NULL) { - return WSAGetLastError(); + return getsockerror(); } else { CHostInfo tmp(info); @@ -287,7 +289,7 @@ CNetwork::gethostbyname(CHostInfo* hostinfo, // winsock returns structures per-thread struct hostent FAR* info = gethostbyname_n(name); if (info == NULL) { - return WSAGetLastError(); + return getsockerror(); } else { CHostInfo tmp(info); @@ -305,7 +307,7 @@ CNetwork::getservbyport(CServiceInfo* servinfo, // winsock returns structures per-thread struct servent FAR* info = getservbyport_n(port, proto); if (info == NULL) { - return WSAGetLastError(); + return getsockerror(); } else { CServiceInfo tmp(info); @@ -323,7 +325,7 @@ CNetwork::getservbyname(CServiceInfo* servinfo, // winsock returns structures per-thread struct servent FAR* info = getservbyname_n(name, proto); if (info == NULL) { - return WSAGetLastError(); + return getsockerror(); } else { CServiceInfo tmp(info); @@ -339,9 +341,9 @@ CNetwork::getprotobynumber(CProtocolInfo* protoinfo, assert(protoinfo != NULL); // winsock returns structures per-thread - struct protoinfo FAR* info = getprotobynumber_n(proto); + struct protoent FAR* info = getprotobynumber_n(proto); if (info == NULL) { - return WSAGetLastError(); + return getsockerror(); } else { CProtocolInfo tmp(info); @@ -357,9 +359,9 @@ CNetwork::getprotobyname(CProtocolInfo* protoinfo, assert(protoinfo != NULL); // winsock returns structures per-thread - struct protoinfo FAR* info = getprotobyname_n(name); + struct protoent FAR* info = getprotobyname_n(name); if (info == NULL) { - return WSAGetLastError(); + return getsockerror(); } else { CProtocolInfo tmp(info); @@ -379,7 +381,7 @@ int PASCAL FAR CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) { BOOL flag = nodelay ? 1 : 0; - setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); + return setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); } #endif @@ -681,25 +683,26 @@ CNetwork::poll2(PollEntry fd[], int nfds, int timeout) FD_ZERO(&writeSet); FD_ZERO(&errSet); for (i = 0; i < nfds; ++i) { + int fdi = static_cast(fd[i].fd); if (fd[i].events & kPOLLIN) { FD_SET(fd[i].fd, &readSet); readSetP = &readSet; - if (fd[i].fd > n) { - n = fd[i].fd; + if (fdi > n) { + n = fdi; } } if (fd[i].events & kPOLLOUT) { FD_SET(fd[i].fd, &writeSet); writeSetP = &writeSet; - if (fd[i].fd > n) { - n = fd[i].fd; + if (fdi > n) { + n = fdi; } } if (true) { FD_SET(fd[i].fd, &errSet); errSetP = &errSet; - if (fd[i].fd > n) { - n = fd[i].fd; + if (fdi > n) { + n = fdi; } } } diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h index e0869ea6..06c2e411 100644 --- a/lib/net/CNetwork.h +++ b/lib/net/CNetwork.h @@ -55,11 +55,13 @@ public: typedef SOCKET Socket; typedef struct sockaddr Address; typedef int AddressLength; + typedef short AddressType; typedef struct in_addr InternetAddress; #elif UNIX_LIKE typedef int Socket; typedef struct sockaddr Address; typedef socklen_t AddressLength; + typedef int AddressType; typedef struct in_addr InternetAddress; #endif @@ -100,7 +102,7 @@ public: CString m_name; AliasList m_aliases; - int m_addressType; + AddressType m_addressType; int m_addressLength; AddressList m_addresses; diff --git a/lib/net/net.dsp b/lib/net/net.dsp index 13a976e9..fb7df902 100644 --- a/lib/net/net.dsp +++ b/lib/net/net.dsp @@ -1,162 +1,162 @@ -# Microsoft Developer Studio Project File - Name="net" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=net - Win32 Debug -!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 "net.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 "net.mak" CFG="net - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "net - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "net - Win32 Debug" (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)" == "net - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "net - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "net - Win32 Release" -# Name "net - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CNetwork.cpp -# End Source File -# Begin Source File - -SOURCE=.\CNetworkAddress.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTCPListenSocket.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocket.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocketFactory.cpp -# End Source File -# Begin Source File - -SOURCE=.\XNetwork.cpp -# End Source File -# Begin Source File - -SOURCE=.\XSocket.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CNetwork.h -# End Source File -# Begin Source File - -SOURCE=.\CNetworkAddress.h -# End Source File -# Begin Source File - -SOURCE=.\CTCPListenSocket.h -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocket.h -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocketFactory.h -# End Source File -# Begin Source File - -SOURCE=.\IListenSocket.h -# End Source File -# Begin Source File - -SOURCE=.\ISocket.h -# End Source File -# Begin Source File - -SOURCE=.\ISocketFactory.h -# End Source File -# Begin Source File - -SOURCE=.\XNetwork.h -# End Source File -# Begin Source File - -SOURCE=.\XSocket.h -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="net" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=net - Win32 Debug +!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 "net.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 "net.mak" CFG="net - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "net - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "net - Win32 Debug" (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)" == "net - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "net - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "net - Win32 Release" +# Name "net - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CNetwork.cpp +# End Source File +# Begin Source File + +SOURCE=.\CNetworkAddress.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPListenSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.cpp +# End Source File +# Begin Source File + +SOURCE=.\XNetwork.cpp +# End Source File +# Begin Source File + +SOURCE=.\XSocket.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\CNetworkAddress.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocket.h +# End Source File +# Begin Source File + +SOURCE=.\CTCPSocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\IListenSocket.h +# End Source File +# Begin Source File + +SOURCE=.\ISocket.h +# End Source File +# Begin Source File + +SOURCE=.\ISocketFactory.h +# End Source File +# Begin Source File + +SOURCE=.\XNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\XSocket.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/platform/makehook.dsp b/lib/platform/makehook.dsp index fc0bc115..507fa6e9 100644 --- a/lib/platform/makehook.dsp +++ b/lib/platform/makehook.dsp @@ -1,63 +1,63 @@ -# Microsoft Developer Studio Project File - Name="makehook" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Generic Project" 0x010a - -CFG=makehook - Win32 Debug -!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 "makehook.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 "makehook.mak" CFG="makehook - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "makehook - Win32 Release" (based on "Win32 (x86) Generic Project") -!MESSAGE "makehook - Win32 Debug" (based on "Win32 (x86) Generic Project") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -MTL=midl.exe - -!IF "$(CFG)" == "makehook - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "makehook___Win32_Release" -# PROP BASE Intermediate_Dir "makehook___Win32_Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "makehook - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "makehook___Win32_Debug" -# PROP BASE Intermediate_Dir "makehook___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "makehook - Win32 Release" -# Name "makehook - Win32 Debug" -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="makehook" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=makehook - Win32 Debug +!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 "makehook.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 "makehook.mak" CFG="makehook - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "makehook - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "makehook - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "makehook - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "makehook___Win32_Release" +# PROP BASE Intermediate_Dir "makehook___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "makehook - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "makehook___Win32_Debug" +# PROP BASE Intermediate_Dir "makehook___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "makehook - Win32 Release" +# Name "makehook - Win32 Debug" +# End Target +# End Project diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index d78f7c92..b05d246e 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -1,175 +1,175 @@ -# Microsoft Developer Studio Project File - Name="platform" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=platform - Win32 Debug -!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 "platform.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 "platform.mak" CFG="platform - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "platform - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "platform - Win32 Debug" (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)" == "platform - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "platform - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "platform - Win32 Release" -# Name "platform - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CMSWindowsClipboard.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardAnyTextConverter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardTextConverter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreenSaver.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPlatform.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CMSWindowsClipboard.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardAnyTextConverter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardTextConverter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardUTF16Converter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreenSaver.h -# End Source File -# Begin Source File - -SOURCE=.\CPlatform.h -# End Source File -# Begin Source File - -SOURCE=.\CWin32Platform.h -# End Source File -# Begin Source File - -SOURCE=.\IMSWindowsScreenEventHandler.h -# End Source File -# Begin Source File - -SOURCE=.\IPlatform.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# Begin Group "Included Files" - -# PROP Default_Filter "inc" -# Begin Source File - -SOURCE=.\CWin32Platform.cpp -# PROP Exclude_From_Build 1 -# End Source File -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="platform" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=platform - Win32 Debug +!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 "platform.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 "platform.mak" CFG="platform - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "platform - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "platform - Win32 Debug" (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)" == "platform - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "platform - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "platform - Win32 Release" +# Name "platform - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardAnyTextConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardTextConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreenSaver.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPlatform.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CMSWindowsClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardAnyTextConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardTextConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardUTF16Converter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsScreenSaver.h +# End Source File +# Begin Source File + +SOURCE=.\CPlatform.h +# End Source File +# Begin Source File + +SOURCE=.\CWin32Platform.h +# End Source File +# Begin Source File + +SOURCE=.\IMSWindowsScreenEventHandler.h +# End Source File +# Begin Source File + +SOURCE=.\IPlatform.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# Begin Group "Included Files" + +# PROP Default_Filter "inc" +# Begin Source File + +SOURCE=.\CWin32Platform.cpp +# PROP Exclude_From_Build 1 +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp index cf1717e3..a7735c3e 100644 --- a/lib/platform/synrgyhk.dsp +++ b/lib/platform/synrgyhk.dsp @@ -1,116 +1,116 @@ -# Microsoft Developer Studio Project File - Name="synrgyhk" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=synrgyhk - Win32 Debug -!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 "synrgyhk.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 "synrgyhk.mak" CFG="synrgyhk - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "synrgyhk - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "synrgyhk - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "synrgyhk - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "synrgyhk___Win32_Release" -# PROP BASE Intermediate_Dir "synrgyhk___Win32_Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../../Release" -# PROP Intermediate_Dir "ReleaseHook" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"DllMain" /dll /machine:I386 /nodefaultlib -# SUBTRACT LINK32 /verbose - -!ELSEIF "$(CFG)" == "synrgyhk - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "synrgyhk___Win32_Debug" -# PROP BASE Intermediate_Dir "synrgyhk___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../../Debug" -# PROP Intermediate_Dir "DebugHook" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\synergy" /I "..\base" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"" /dll /debug /machine:I386 /pdbtype:sept -# SUBTRACT LINK32 /nodefaultlib - -!ENDIF - -# Begin Target - -# Name "synrgyhk - Win32 Release" -# Name "synrgyhk - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CSynergyHook.cpp -# ADD CPP /D _WIN32_WINNT=0x0400 -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CSynergyHook.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="synrgyhk" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=synrgyhk - Win32 Debug +!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 "synrgyhk.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 "synrgyhk.mak" CFG="synrgyhk - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "synrgyhk - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "synrgyhk - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "synrgyhk - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "synrgyhk___Win32_Release" +# PROP BASE Intermediate_Dir "synrgyhk___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../Release" +# PROP Intermediate_Dir "ReleaseHook" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"DllMain" /dll /machine:I386 /nodefaultlib +# SUBTRACT LINK32 /verbose + +!ELSEIF "$(CFG)" == "synrgyhk - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "synrgyhk___Win32_Debug" +# PROP BASE Intermediate_Dir "synrgyhk___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../Debug" +# PROP Intermediate_Dir "DebugHook" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\synergy" /I "..\base" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"" /dll /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "synrgyhk - Win32 Release" +# Name "synrgyhk - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CSynergyHook.cpp +# ADD CPP /D _WIN32_WINNT=0x0400 +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CSynergyHook.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/server/server.dsp b/lib/server/server.dsp index 8d5f98df..fa732ab7 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -1,166 +1,166 @@ -# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=server - Win32 Debug -!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 "server.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 "server.mak" CFG="server - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "server - Win32 Debug" (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)" == "server - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "server - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "server - Win32 Release" -# Name "server - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CClientProxy.cpp -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_0.cpp -# End Source File -# Begin Source File - -SOURCE=.\CConfig.cpp -# End Source File -# Begin Source File - -SOURCE=.\CHTTPServer.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsPrimaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryClient.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CServer.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CClientProxy.h -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_0.h -# End Source File -# Begin Source File - -SOURCE=.\CConfig.h -# End Source File -# Begin Source File - -SOURCE=.\CHTTPServer.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsPrimaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryClient.h -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CServer.h -# End Source File -# Begin Source File - -SOURCE=.\IPrimaryScreenFactory.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=server - Win32 Debug +!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 "server.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 "server.mak" CFG="server - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "server - Win32 Debug" (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)" == "server - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "server - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "server - Win32 Release" +# Name "server - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClientProxy.cpp +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.cpp +# End Source File +# Begin Source File + +SOURCE=.\CConfig.cpp +# End Source File +# Begin Source File + +SOURCE=.\CHTTPServer.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryClient.cpp +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServer.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClientProxy.h +# End Source File +# Begin Source File + +SOURCE=.\CClientProxy1_0.h +# End Source File +# Begin Source File + +SOURCE=.\CConfig.h +# End Source File +# Begin Source File + +SOURCE=.\CHTTPServer.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryClient.h +# End Source File +# Begin Source File + +SOURCE=.\CPrimaryScreen.h +# End Source File +# Begin Source File + +SOURCE=.\CServer.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreenFactory.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 9a71482f..890ec743 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -1,194 +1,194 @@ -# Microsoft Developer Studio Project File - Name="libsynergy" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=synergy - Win32 Debug -!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 "libsynergy.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 "libsynergy.mak" CFG="libsynergy - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "libsynergy - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "libsynergy - Win32 Debug" (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)" == "libsynergy - 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" -# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "libsynergy - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "libsynergy - Win32 Release" -# Name "libsynergy - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CClipboard.cpp -# End Source File -# Begin Source File - -SOURCE=.\CInputPacketStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\COutputPacketStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\CProtocolUtil.cpp -# End Source File -# Begin Source File - -SOURCE=.\XScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\XSynergy.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CClipboard.h -# End Source File -# Begin Source File - -SOURCE=.\CInputPacketStream.h -# End Source File -# Begin Source File - -SOURCE=.\ClipboardTypes.h -# End Source File -# Begin Source File - -SOURCE=.\COutputPacketStream.h -# End Source File -# Begin Source File - -SOURCE=.\CProtocolUtil.h -# End Source File -# Begin Source File - -SOURCE=.\IClient.h -# End Source File -# Begin Source File - -SOURCE=.\IClipboard.h -# End Source File -# Begin Source File - -SOURCE=.\IPrimaryScreenReceiver.h -# End Source File -# Begin Source File - -SOURCE=.\IScreen.h -# End Source File -# Begin Source File - -SOURCE=.\IScreenEventHandler.h -# End Source File -# Begin Source File - -SOURCE=.\IScreenReceiver.h -# End Source File -# Begin Source File - -SOURCE=.\IScreenSaver.h -# End Source File -# Begin Source File - -SOURCE=.\IServer.h -# End Source File -# Begin Source File - -SOURCE=.\KeyTypes.h -# End Source File -# Begin Source File - -SOURCE=.\MouseTypes.h -# End Source File -# Begin Source File - -SOURCE=.\ProtocolTypes.h -# End Source File -# Begin Source File - -SOURCE=.\XScreen.h -# End Source File -# Begin Source File - -SOURCE=.\XSynergy.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="libsynergy" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=synergy - Win32 Debug +!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 "libsynergy.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 "libsynergy.mak" CFG="libsynergy - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libsynergy - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libsynergy - Win32 Debug" (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)" == "libsynergy - 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" +# 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "libsynergy - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "libsynergy - Win32 Release" +# Name "libsynergy - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.cpp +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\XScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\XSynergy.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\CInputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\ClipboardTypes.h +# End Source File +# Begin Source File + +SOURCE=.\COutputPacketStream.h +# End Source File +# Begin Source File + +SOURCE=.\CProtocolUtil.h +# End Source File +# Begin Source File + +SOURCE=.\IClient.h +# End Source File +# Begin Source File + +SOURCE=.\IClipboard.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreenReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\IScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenEventHandler.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenSaver.h +# End Source File +# Begin Source File + +SOURCE=.\IServer.h +# End Source File +# Begin Source File + +SOURCE=.\KeyTypes.h +# End Source File +# Begin Source File + +SOURCE=.\MouseTypes.h +# End Source File +# Begin Source File + +SOURCE=.\ProtocolTypes.h +# End Source File +# Begin Source File + +SOURCE=.\XScreen.h +# End Source File +# Begin Source File + +SOURCE=.\XSynergy.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project From cf13980bb80acf308d44fe91e9907127a56ec987 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 30 Oct 2002 22:16:30 +0000 Subject: [PATCH 377/807] Fixed bugs in error handling in CTCPSocket; previously was not handling read errors at all and error handling for writes was never being used. Now the socket disconnects if a read or write fails on the socket for any reason except EINTR. Also added to includes in CNetwork.h because it's needed on some platforms. --- lib/net/CNetwork.h | 3 +++ lib/net/CTCPSocket.cpp | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h index 06c2e411..30401ff2 100644 --- a/lib/net/CNetwork.h +++ b/lib/net/CNetwork.h @@ -43,6 +43,7 @@ typedef int ssize_t; #endif #if UNIX_LIKE +# include # include # include #endif @@ -183,9 +184,11 @@ public: #if WINDOWS_LIKE kEADDRINUSE = WSAEADDRINUSE, kECONNECTING = WSAEWOULDBLOCK, + kEINTR = WSAEINTR, #elif UNIX_LIKE kEADDRINUSE = EADDRINUSE, kECONNECTING = EINPROGRESS, + kEINTR = EINTR, #endif kNone = 0 }; diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index a9937537..0832904f 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -292,6 +292,12 @@ CTCPSocket::ioService() m_input->hangup(); m_connected &= ~kRead; } + else { + // socket failed + if (CNetwork::getsockerror() != CNetwork::kEINTR) { + return; + } + } } // write some data @@ -303,14 +309,17 @@ CTCPSocket::ioService() // write data const void* buffer = m_output->peek(n); - n = (UInt32)CNetwork::write(m_fd, buffer, n); + ssize_t n2 = (UInt32)CNetwork::write(m_fd, buffer, n); // discard written data - if (n > 0) { + if (n2 > 0) { m_output->pop(n); } - else if (n == (UInt32)-1 && CNetwork::getsockerror() == EPIPE) { - return; + else if (n2 < 0) { + // socket failed + if (CNetwork::getsockerror() != CNetwork::kEINTR) { + return; + } } } } From c256cf062f60beee0016c2812a1537e48743eb36 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 30 Oct 2002 22:22:16 +0000 Subject: [PATCH 378/807] Escaped quotes to satisfy older autoheader versions. --- acinclude.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 841621f0..709fb287 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -82,7 +82,7 @@ AC_DEFUN([ACX_CHECK_POLL], [ acx_poll_ok=yes, acx_poll_ok=no) AC_MSG_RESULT($acx_poll_ok) if test x"$acx_poll_ok" = xyes; then - ifelse([$1],,AC_DEFINE(HAVE_POLL,1,[Define if you have the `poll' function.]),[$1]) + ifelse([$1],,AC_DEFINE(HAVE_POLL,1,[Define if you have the \`poll\' function.]),[$1]) : else acx_poll_ok=no @@ -139,7 +139,7 @@ AC_DEFUN([ACX_CHECK_NANOSLEEP], [ # execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_nanosleep_ok" = xyes; then - ifelse([$1],,AC_DEFINE(HAVE_NANOSLEEP,1,[Define if you have the `nanosleep' function.]),[$1]) + ifelse([$1],,AC_DEFINE(HAVE_NANOSLEEP,1,[Define if you have the \`nanosleep\' function.]),[$1]) : else acx_nanosleep_ok=no From 17e8ba2dbd0a1d2ba5f562334b8f99824ec3d428 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 3 Nov 2002 18:09:28 +0000 Subject: [PATCH 379/807] Merged fixes for building on MacOS X. It dies on one file with an internal compiler error; building that file without optimization works around the compiler bug. Sadly, synergy can only interact with X windows, not native MacOS windows. --- acinclude.m4 | 16 ++++++++++++++++ configure.in | 7 +++++-- lib/io/CUnicode.h | 2 +- lib/net/CNetwork.h | 7 +++++++ lib/platform/CUnixPlatform.cpp | 4 ++++ 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 709fb287..c10437d2 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -75,6 +75,22 @@ AC_DEFUN([ACX_CHECK_CXX_STDLIB], [ fi ])dnl ACX_CHECK_CXX_STDLIB +AC_DEFUN([ACX_CHECK_GETPWUID_R], [ + AC_MSG_CHECKING([for working getpwuid_r]) + AC_TRY_LINK([#include ], + [char buffer[4096]; struct passwd pwd, *pwdp; + getpwuid_r(0, &pwd, buffer, sizeof(buffer), &pwdp);], + acx_getpwuid_r_ok=yes, acx_getpwuid_r_ok=no) + AC_MSG_RESULT($acx_getpwuid_r_ok) + if test x"$acx_getpwuid_r_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_GETPWUID_R,1,[Define if you have a working \`getpwuid_r\' function.]),[$1]) + : + else + acx_getpwuid_r_ok=no + $2 + fi +])dnl ACX_CHECK_GETPWUID_R + AC_DEFUN([ACX_CHECK_POLL], [ AC_MSG_CHECKING([for poll]) AC_TRY_LINK([#include ], diff --git a/configure.in b/configure.in index 75ec64de..9d6b7b93 100644 --- a/configure.in +++ b/configure.in @@ -46,14 +46,17 @@ AC_CHECK_HEADERS([windows.h]) AC_HEADER_TIME AC_PATH_X AC_PATH_XTRA +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$X_CFLAGS $CPPFLAGS" AC_CHECK_HEADERS([X11/extensions/XTest.h]) +CPPFLAGS="$save_CPPFLAGS" dnl checks for types AC_TYPE_SIZE_T dnl checks for structures AC_STRUCT_TM -AC_CHECK_TYPES(mbstate_t,,,[#include ]) +AC_CHECK_TYPES(mbstate_t,,,[#include ]) dnl checks for compiler characteristics AC_CHECK_SIZEOF(char, 1) @@ -72,7 +75,7 @@ AC_FUNC_FORK AC_FUNC_MEMCMP AC_FUNC_STRFTIME AC_CHECK_FUNCS(gmtime_r) -AC_CHECK_FUNCS(getpwuid_r) +ACX_CHECK_GETPWUID_R AC_CHECK_FUNCS(vsnprintf) AC_CHECK_FUNCS(wcrtomb mbrtowc mbsinit) AC_FUNC_SELECT_ARGTYPES diff --git a/lib/io/CUnicode.h b/lib/io/CUnicode.h index 43ee5470..19816b17 100644 --- a/lib/io/CUnicode.h +++ b/lib/io/CUnicode.h @@ -17,7 +17,7 @@ #include "CString.h" #include "BasicTypes.h" -#include +#include //! Unicode utility functions /*! diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h index 30401ff2..eacff229 100644 --- a/lib/net/CNetwork.h +++ b/lib/net/CNetwork.h @@ -19,6 +19,13 @@ #include "CString.h" #include "stdvector.h" +// Darwin is so unsure what to use for socklen_t it makes us choose +#if defined(__APPLE__) +# if !defined(_BSD_SOCKLEN_T_) +# define _BSD_SOCKLEN_T_ int +# endif +#endif + #if HAVE_SYS_TYPES_H # include #endif diff --git a/lib/platform/CUnixPlatform.cpp b/lib/platform/CUnixPlatform.cpp index f0e447eb..feed3318 100644 --- a/lib/platform/CUnixPlatform.cpp +++ b/lib/platform/CUnixPlatform.cpp @@ -141,7 +141,11 @@ CUnixPlatform::getUserDirectory() const #if HAVE_GETPWUID_R struct passwd pwent; struct passwd* pwentp; +#if defined(_SC_GETPW_R_SIZE_MAX) long size = sysconf(_SC_GETPW_R_SIZE_MAX); +#else + long size = BUFSIZ; +#endif char* buffer = new char[size]; getpwuid_r(getuid(), &pwent, buffer, size, &pwentp); delete[] buffer; From ca984acb91a7a9ee3d641ccc3886b0b8dbabce7e Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 3 Nov 2002 18:20:21 +0000 Subject: [PATCH 380/807] Removed configure check for mbstate_t and uses of it. --- configure.in | 1 - lib/io/CUnicode.cpp | 8 -------- 2 files changed, 9 deletions(-) diff --git a/configure.in b/configure.in index 9d6b7b93..e18814fc 100644 --- a/configure.in +++ b/configure.in @@ -56,7 +56,6 @@ AC_TYPE_SIZE_T dnl checks for structures AC_STRUCT_TM -AC_CHECK_TYPES(mbstate_t,,,[#include ]) dnl checks for compiler characteristics AC_CHECK_SIZEOF(char, 1) diff --git a/lib/io/CUnicode.cpp b/lib/io/CUnicode.cpp index 0fe85dc3..040159f5 100644 --- a/lib/io/CUnicode.cpp +++ b/lib/io/CUnicode.cpp @@ -75,19 +75,11 @@ setError(bool* errors) // #if WINDOWS_LIKE -#define HAVE_MBSTATE_T 1 #define HAVE_MBSINIT 1 #define HAVE_MBRTOWC 1 #define HAVE_WCRTOMB 1 #endif -#if !HAVE_MBSTATE_T -struct mbstate_t { int m_dummy; }; -#undef HAVE_MBSINIT -#undef HAVE_MBRTOWC -#undef HAVE_WCRTOMB -#endif - #if !HAVE_MBSINIT static int From 52d8edb747541adc46b42601dc6796f836850b7b Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 5 Nov 2002 19:23:05 +0000 Subject: [PATCH 381/807] Fixed bug in detecting screen saver activation. Was using || instead of && in conditional. --- lib/platform/CXWindowsScreen.cpp | 4 ++-- lib/platform/CXWindowsScreenSaver.cpp | 6 ++++-- lib/platform/CXWindowsScreenSaver.h | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 2ebbbaf9..40e8591d 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -239,7 +239,7 @@ CXWindowsScreen::open() updateScreenShape(); // initialize the screen saver - m_atomScreensaver = XInternAtom(m_display, "SCREENSAVER", False); + m_atomScreensaver = XInternAtom(m_display, "SYNERGY_SCREENSAVER", False); m_screensaver = new CXWindowsScreenSaver(this, m_display); } @@ -634,7 +634,7 @@ CXWindowsScreen::onPreDispatch(CEvent* event) break; case ClientMessage: - if (xevent->xclient.message_type == m_atomScreensaver || + if (xevent->xclient.message_type == m_atomScreensaver && xevent->xclient.format == 32) { // screen saver activation/deactivation event m_eventHandler->onScreensaver(xevent->xclient.data.l[0] != 0); diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index e32f13c3..c712f826 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -50,6 +50,8 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( "ACTIVATE", False); m_atomScreenSaverDeactivate = XInternAtom(m_display, "DEACTIVATE", False); + m_atomSynergyScreenSaver = XInternAtom(m_display, + "SYNERGY_SCREENSAVER", False); // create dummy window to receive xscreensaver responses. this // shouldn't be necessary (we should be able to send responses @@ -263,7 +265,7 @@ CXWindowsScreenSaver::sendNotify(bool activated) event.xclient.type = ClientMessage; event.xclient.display = m_display; event.xclient.window = m_notify; - event.xclient.message_type = m_atomScreenSaver; + event.xclient.message_type = m_atomSynergyScreenSaver; event.xclient.format = 32; event.xclient.data.l[0] = activated ? 1 : 0; event.xclient.data.l[1] = 0; @@ -342,7 +344,7 @@ void CXWindowsScreenSaver::setXScreenSaverActive(bool activated) { if (m_xscreensaverActive != activated) { - LOG((CLOG_DEBUG "xscreensaver %s", activated ? "activated" : "deactivated")); + LOG((CLOG_DEBUG "xscreensaver %s on window 0x%08x", activated ? "activated" : "deactivated", m_xscreensaver)); m_xscreensaverActive = activated; sendNotify(activated); } diff --git a/lib/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h index 7e6d4304..dd1050cb 100644 --- a/lib/platform/CXWindowsScreenSaver.h +++ b/lib/platform/CXWindowsScreenSaver.h @@ -132,6 +132,7 @@ private: Atom m_atomScreenSaverVersion; Atom m_atomScreenSaverActivate; Atom m_atomScreenSaverDeactivate; + Atom m_atomSynergyScreenSaver; // built-in screen saver settings int m_timeout; From 48fc3c77c8626ac5b94a982d0281a4aaab830d89 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 5 Nov 2002 19:43:37 +0000 Subject: [PATCH 382/807] Added -D_POSIX_PTHREAD_SEMANTICS for solaris. --- acinclude.m4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index c10437d2..42ebc789 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -359,8 +359,9 @@ if test "x$acx_pthread_ok" = xyes; then AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in - *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; - *solaris* | alpha*-osf*) flag="-D_REENTRANT";; + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + alpha*-osf*) flag="-D_REENTRANT";; + *solaris*) flag="-D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then From 3ae1c67aef1d7bbcb5117e805025fa1007db4da7 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 5 Nov 2002 19:56:56 +0000 Subject: [PATCH 383/807] Changes for version 0.9.13. --- ChangeLog | 229 +++++++++++++++++++++++++++++++++++++++++++++ NEWS | 15 +++ configure.in | 2 +- lib/base/Version.h | 2 +- 4 files changed, 246 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index df0535e9..694f93d8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,232 @@ +2002/11/05 19:23:05 crs +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h + +Fixed bug in detecting screen saver activation. Was using || instead +of && in conditional. + +---------- +2002/11/03 18:09:28 crs +acinclude.m4 +configure.in +lib/io/CUnicode.h +lib/net/CNetwork.h +lib/platform/CUnixPlatform.cpp + +Merged fixes for building on MacOS X. It dies on one file with +an internal compiler error; building that file without +optimization works around the compiler bug. Sadly, synergy can +only interact with X windows, not native MacOS windows. + +---------- +2002/10/30 22:22:16 crs +acinclude.m4 + +Escaped quotes to satisfy older autoheader versions. + +---------- +2002/10/30 22:16:30 crs +lib/net/CNetwork.h +lib/net/CTCPSocket.cpp + +Fixed bugs in error handling in CTCPSocket; previously was not +handling read errors at all and error handling for writes was +never being used. Now the socket disconnects if a read or write +fails on the socket for any reason except EINTR. Also added + to includes in CNetwork.h because it's needed on +some platforms. + +---------- +2002/10/29 22:07:55 crs +all.dsp +lib/base/base.dsp +lib/client/client.dsp +lib/http/http.dsp +lib/io/CUnicode.cpp +lib/io/io.dsp +lib/mt/mt.dsp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/net.dsp +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/server.dsp +lib/synergy/libsynergy.dsp + +Ported recent changes to win32 and fixed CRLF problems with project +files (most had CRCRCRLF). + +---------- +2002/10/28 22:49:21 crs +acinclude.m4 +configure.in +lib/mt/CThreadRep.cpp + +solaris configure and build fixes. without having solaris i +can only hope that these changes actually work. + +---------- +2002/10/22 22:35:13 crs +configure.in +lib/io/CUnicode.cpp + +Added workarounds for missing reentrant versions of wide char +to/from multi-byte conversion functions. + +---------- +2002/10/22 21:30:48 crs +lib/base/CUnicode.cpp +lib/base/CUnicode.h +lib/base/Makefile.am +lib/io/CUnicode.cpp +lib/io/CUnicode.h +lib/io/Makefile.am +lib/platform/Makefile.am + +Moved CUnicode to lib/io. That's a reasonable place for it +that's after lib/mt. It needs to be after lib/mt in preparation +for supporting platforms without the reentrant wide char and +multi-byte functions. + +---------- +2002/10/20 22:39:54 crs +lib/client/CMSWindowsSecondaryScreen.cpp + +Fixed conditional to test for multimon to do nasty win32 mouse +positioning hack. Was doing hack if *not* a multiple monitor +system but should've been doing it if is *is* a multimon system. + +---------- +2002/10/20 22:36:24 crs +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp + +Replaced inet_addr() with inet_aton(), which is a better function +anyway but isn't implemented in winsock, removed use of INADDR_NONE +which some platforms don't define except on winsock which does +define it, and changed SOL_TCP to IPPROTO_TCP which should work on +more platforms. + +---------- +2002/10/17 21:37:41 crs +lib/platform/CXWindowsScreen.cpp + +Fixed CXWindowsScreen to force the event loop to wake up when +exitMainLoop() is called. + +---------- +2002/10/17 21:37:37 crs +lib/mt/CThreadRep.cpp + +Fixed CThreadRep to not raise a signal on the thread if it's +already dead. Otherwise the signal could propagate to the +parent thread (at least on linux threads) and cause havoc. + +---------- +2002/10/17 21:37:31 crs +lib/server/CServer.cpp +lib/server/CServer.h + +Changed server to fail with an error if in can't bind() the listen +socket for any reason other than it's in use. + +---------- +2002/10/17 20:56:28 crs +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp + +Changed non-reentrant network functions to be reentrant and +thread safe. + +---------- +2002/10/16 22:01:50 crs +acinclude.m4 +configure.in +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CTCPSocket.cpp +lib/platform/CXWindowsScreen.cpp + +Added support for using select() instead of poll(). + +---------- +2002/10/15 22:17:41 crs +lib/server/CConfig.cpp + +CConfig now accepts and discards \r at the end of a line. This +allows the unix server to read configuration files created on +microsoft windows platforms. + +---------- +2002/10/15 22:08:10 crs +cmd/synergys/synergys.cpp +lib/server/CConfig.cpp + +Fixed use of %s instead of %{1} in format() call. + +---------- +2002/10/15 22:01:41 crs +lib/client/CClient.cpp +lib/mt/CThreadRep.cpp +lib/mt/Makefile.am +lib/mt/XMT.cpp +lib/mt/XMT.h +lib/mt/XThread.h +lib/server/CServer.cpp + +Renamed XThreadUnavailable to XMTThreadUnavailable and derived it +from XBase so it can be caught normally. Changed client and server +to handle unavailable threads (in main loop, anyway). + +---------- +2002/10/15 21:35:12 crs +lib/mt/CThreadRep.cpp + +Workaround for pthread bug on RedHat 7.2 on multiprocessor +systems. + +---------- +2002/10/15 21:29:44 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/base/CLog.h +lib/client/CClient.cpp +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CSecondaryScreen.cpp +lib/client/CServerProxy.cpp +lib/client/CXWindowsSecondaryScreen.cpp +lib/http/CHTTPProtocol.cpp +lib/mt/CMutex.cpp +lib/mt/CThread.cpp +lib/mt/CThreadRep.cpp +lib/mt/CTimerThread.cpp +lib/net/CNetwork.cpp +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CWin32Platform.cpp +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsUtil.cpp +lib/server/CClientProxy1_0.cpp +lib/server/CHTTPServer.cpp +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryScreen.cpp +lib/server/CServer.cpp +lib/server/CXWindowsPrimaryScreen.cpp +lib/synergy/CProtocolUtil.cpp + +Changed log() and logc() macros to LOG() and LOGC(), respectively. +This avoids a conflict with the standard math library log() +function. + +---------- 2002/09/14 21:31:35 crs lib/base/XBase.cpp lib/base/XBase.h diff --git a/NEWS b/NEWS index 2fba0052..38eb3be5 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,21 @@ Synergy News ============ +* Nov-05-2002 - Synergy 0.9.13 released + + Made following changes: + * Fixed solaris compile problems (untested) + * Fixed MacOS X compile problems (semi-functional) + * Fixed gcc-3.2 compile problems + * Fixed some thread startup and shutdown bugs + * Server now quits if bind() fails with an error other than in use + * Fixed bug in moving mouse on Win98 without multiple monitors + * Fixed bug in handling TCP socket errors on read and write + * Fixed spurious screen saver activation on X11 + * Unix platforms can now read Win32 configuration files + * Minor error reporting fixes + + * Sep-14-2002 - Synergy 0.9.12 released Made following changes: diff --git a/configure.in b/configure.in index e18814fc..0566a688 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=0 MINOR_VERSION=9 -RELEASE_VERSION=12 +RELEASE_VERSION=13 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/base/Version.h b/lib/base/Version.h index 2f638d4d..9374a3f1 100644 --- a/lib/base/Version.h +++ b/lib/base/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "0.9.12" +# define VERSION "0.9.13" #endif // important strings From 0ab692a8e8f4427311c1c98df001460094e21e11 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 15 Dec 2002 11:12:39 +0000 Subject: [PATCH 384/807] Enabled dot and class diagrams. --- doc/doxygen.cfg.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index 7ffc56c6..b950205a 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -761,14 +761,14 @@ PERL_PATH = # option is superceded by the HAVE_DOT option below. This is only a fallback. It is # recommended to install and use dot, since it yield more powerful graphs. -CLASS_DIAGRAMS = YES +CLASS_DIAGRAMS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) -HAVE_DOT = NO +HAVE_DOT = YES # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and From eda93fc20d7588f4f61d714ff4c43631998961ed Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 15 Dec 2002 19:57:28 +0000 Subject: [PATCH 385/807] Cleanup and changed some DEBUG1 messages to DEBUG2. --- lib/client/CXWindowsSecondaryScreen.cpp | 60 +++++++------------------ 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index 6095a478..b7147dd6 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -268,7 +268,6 @@ CXWindowsSecondaryScreen::onPostOpen() m_capsLockHalfDuplex = false; // m_numLockHalfDuplex = true; // m_capsLockHalfDuplex = true; - } void @@ -438,7 +437,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // keysym with that mask. we override the bits in the mask // that cannot be accomodated. - // note if the key is the caps lock and it's "half-duplex" + // note if the key is "half-duplex" const bool isHalfDuplex = ((id == kKeyCapsLock && m_capsLockHalfDuplex) || (id == kKeyNumLock && m_numLockHalfDuplex)); @@ -452,6 +451,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, KeyCodeIndex keyIndex = findKey(id, outMask); if (keyIndex == noKey()) { // cannot convert id to keysym + LOG((CLOG_DEBUG2 "no keysym for key")); return m_mask; } @@ -490,28 +490,27 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // keys affected by CapsLock. bool desireShift = (getBits(desired, ShiftMask) != 0); bool invertShift = false; -LOG((CLOG_DEBUG1 "desire shift 1: %s", desireShift?"yes":"no")); + LOG((CLOG_DEBUG2 "desire shift: %s", desireShift ? "yes" : "no")); if (adjustForNumLock(keysym)) { -LOG((CLOG_DEBUG1 "num lock sensitive")); + LOG((CLOG_DEBUG2 "num lock sensitive")); if (m_numLockMask != 0) { -LOG((CLOG_DEBUG1 "we have num lock")); + LOG((CLOG_DEBUG2 "we have num lock")); if (getBits(desired, m_numLockMask) != 0) { -LOG((CLOG_DEBUG1 "num lock desired, invert shift")); + LOG((CLOG_DEBUG2 "num lock desired, invert shift")); invertShift = true; } } } else if (adjustForCapsLock(keysym)) { -LOG((CLOG_DEBUG1 "caps lock sensitive")); + LOG((CLOG_DEBUG2 "caps lock sensitive")); if (m_capsLockMask != 0) { -LOG((CLOG_DEBUG1 "we have caps lock")); + LOG((CLOG_DEBUG2 "we have caps lock")); if (getBits(desired, m_capsLockMask) != 0) { -LOG((CLOG_DEBUG1 "caps lock desired, invert shift")); + LOG((CLOG_DEBUG2 "caps lock desired, invert shift")); invertShift = true; } } } -LOG((CLOG_DEBUG1 "desire shift 2: %s", desireShift?"yes":"no")); if (desireShift != invertShift) { index[0] ^= 1; index[1] ^= 1; @@ -538,7 +537,7 @@ LOG((CLOG_DEBUG1 "desire shift 2: %s", desireShift?"yes":"no")); // get the keycode keycode = entry.m_keycode[bestIndex]; -LOG((CLOG_DEBUG1 "bestIndex = %d, keycode = %d", bestIndex, keycode)); + LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode)); // note if the key is a modifier ModifierMap::const_iterator modIndex = m_keycodeToModifier.find(keycode); @@ -553,13 +552,13 @@ LOG((CLOG_DEBUG1 "bestIndex = %d, keycode = %d", bestIndex, keycode)); // though. if (modifierBit != 0) { if (action == kRepeat) { -LOG((CLOG_DEBUG1 "ignore repeating modifier")); + LOG((CLOG_DEBUG2 "ignore repeating modifier")); return m_mask; } if (getBits(m_toggleModifierMask, modifierBit) == 0) { if ((action == kPress && (m_mask & modifierBit) != 0) || (action == kRelease && (m_mask & modifierBit) == 0)) { -LOG((CLOG_DEBUG1 "modifier in proper state: 0x%04x", m_mask)); + LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", m_mask)); return m_mask; } } @@ -594,7 +593,7 @@ LOG((CLOG_DEBUG1 "modifier in proper state: 0x%04x", m_mask)); // the same bit in m_mask, meaning it's already in the right state. desired = assignBits(desired, modifierBit, m_mask); required = clearBits(required, modifierBit); -LOG((CLOG_DEBUG1 "desired = 0x%04x, current = 0x%04x", desired, m_mask)); + LOG((CLOG_DEBUG2 "desired = 0x%04x, current = 0x%04x", desired, m_mask)); // add the key events required to get to the modifier state // necessary to generate an event yielding id. also save the @@ -607,13 +606,13 @@ LOG((CLOG_DEBUG1 "desired = 0x%04x, current = 0x%04x", desired, m_mask)); for (unsigned int i = 0; i < 8; ++i) { unsigned int bit = (1 << i); if (getBits(desired, bit) != getBits(m_mask, bit)) { -LOG((CLOG_DEBUG1 "fix modifier %d", i)); + LOG((CLOG_DEBUG2 "fix modifier %d", i)); // get the keycode we're using for this modifier. if // there isn't one then bail if the modifier is required // or ignore it if not required. KeyCode modifierKey = m_modifierToKeycode[i]; if (modifierKey == 0) { - LOG((CLOG_DEBUG1 "no key mapped to modifier 0x%04x", bit)); + LOG((CLOG_DEBUG2 "no key mapped to modifier 0x%04x", bit)); if (getBits(required, bit) != 0) { keys.clear(); return m_mask; @@ -773,7 +772,7 @@ LOG((CLOG_DEBUG1 "fix modifier %d", i)); } } -LOG((CLOG_DEBUG1 "final mask: 0x%04x", mask)); + LOG((CLOG_DEBUG2 "final mask: 0x%04x", mask)); return mask; } @@ -821,7 +820,6 @@ CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) unsigned int CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const { - // FIXME -- should be configurable. also not using Mod3Mask. unsigned int outMask = 0; if (inMask & KeyModifierShift) { outMask |= ShiftMask; @@ -961,7 +959,6 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) } // initialize -// KeyCodeMask entry; m_keycodeMap.clear(); // insert keys @@ -1011,31 +1008,6 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) // save keycode for keysym and modifiers entry.m_keycode[j] = static_cast(minKeycode + i); } - -/* - // compute mask over all mapped keysyms. if a keycode has, say, - // no shifted keysym then we can ignore the shift state when - // synthesizing an event to generate it. - entry.m_keyMaskMask = 0; - for (int j = 0; j < numKeysyms; ++j) { - const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; - if (keysym != NoSymbol) { - entry.m_keyMaskMask |= indexToModifierMask(j); - } - } - - // add entries for this keycode - entry.m_keycode = static_cast(minKeycode + i); - for (int j = 0; j < numKeysyms; ++j) { - const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; - if (keysym != NoSymbol) { -// FIXME -// entry.m_keyMask = indexToModifierMask(j) & ~LockMask; -entry.m_keyMask = 0; - m_keycodeMap[i].insert(std::make_pair(keysym, entry)); - } - } -*/ } // clean up From 773fcae8b3cd67d850fd51146c23392c494da7c7 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 15 Dec 2002 19:58:41 +0000 Subject: [PATCH 386/807] Fixed client not reconnecting when server dies bug. --- lib/platform/CMSWindowsScreen.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index ee33dead..192d05bc 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -207,7 +207,14 @@ CMSWindowsScreen::mainLoop() // handle quit message if (event.m_msg.message == WM_QUIT) { - CThread::getCurrentThread().cancel(); + if (event.m_msg.wParam == 0) { + // force termination + CThread::getCurrentThread().cancel(); + } + else { + // just exit the main loop + break; + } } // dispatch message @@ -221,7 +228,8 @@ CMSWindowsScreen::mainLoop() void CMSWindowsScreen::exitMainLoop() { - PostThreadMessage(m_threadID, WM_QUIT, 0, 0); + // close down cleanly + PostThreadMessage(m_threadID, WM_QUIT, 1, 0); } void From 21283023070e92d92e7647944182b29aedea219d Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 15 Dec 2002 20:00:52 +0000 Subject: [PATCH 387/807] Fixed loss of ctrl+alt when transmitted to non-windows platforms from a windows server. Was converting ctrl+alt on windows to mode switch on the server. No longer doing that; windows clients will interpret ctrl+alt as AltGr and other clients will just see ctrl+alt. Also made the right alt key mode switch on windows servers in case the user wants to force a mode switch, but that means the right alt key no longer acts as alt on clients. --- lib/server/CMSWindowsPrimaryScreen.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index 05188335..d6b96cc6 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -902,9 +902,12 @@ CMSWindowsPrimaryScreen::mapKey( m_keys[VK_CONTROL]) & 0x80) != 0) { mask |= KeyModifierControl; } - if (((m_keys[VK_LMENU] | - m_keys[VK_RMENU] | - m_keys[VK_MENU]) & 0x80) != 0) { + if ((m_keys[VK_RMENU] & 0x80) != 0) { + // right alt => AltGr on windows + mask |= KeyModifierModeSwitch; + } + else if (((m_keys[VK_LMENU] | + m_keys[VK_MENU]) & 0x80) != 0) { mask |= KeyModifierAlt; } if (((m_keys[VK_LWIN] | @@ -920,12 +923,19 @@ CMSWindowsPrimaryScreen::mapKey( if ((m_keys[VK_SCROLL] & 0x01) != 0) { mask |= KeyModifierScrollLock; } + // ctrl+alt => AltGr on windows +/* don't convert ctrl+alt to mode switch. if we do that then we can + * never send ctrl+alt+[key] from windows to some platform that + * doesn't treat ctrl+alt as mode switch (i.e. all other platforms). + * instead, let windows clients automatically treat ctrl+alt as + * AltGr and let other clients use ctrl+alt as is. the right alt + * key serves as a mode switch key. if ((mask & (KeyModifierControl | KeyModifierAlt)) == (KeyModifierControl | KeyModifierAlt)) { - // ctrl+alt => AltGr on windows mask |= KeyModifierModeSwitch; mask &= ~(KeyModifierControl | KeyModifierAlt); } +*/ *maskOut = mask; LOG((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask)); From 7649afa00af49dd3c6b7d15d533cfc8cc46e9f2e Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 15 Dec 2002 22:14:49 +0000 Subject: [PATCH 388/807] Now restoring toggle key states on leaving a client screen to their state when the screen was entered. Previously when leaving a client screen the toggle keys kept their state so, say, caps lock, would remain on. This was inconvenient if you then used the client's keyboard directly. --- lib/client/CMSWindowsSecondaryScreen.cpp | 8 ++++++++ lib/client/CMSWindowsSecondaryScreen.h | 1 + lib/client/CSecondaryScreen.cpp | 10 +++++++++- lib/client/CSecondaryScreen.h | 9 +++++++++ lib/client/CXWindowsSecondaryScreen.cpp | 16 ++++++++++++++++ lib/client/CXWindowsSecondaryScreen.h | 1 + 6 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index a7fc1246..cba2ba4e 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -510,6 +510,14 @@ CMSWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) } } +KeyModifierMask +CMSWindowsSecondaryScreen::getToggleState() const +{ + return (m_mask & (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)); +} + // map special KeyID keys to virtual key codes. if the key is an // extended key then the entry is the virtual key code | 0x100. // unmapped keys have a 0 entry. diff --git a/lib/client/CMSWindowsSecondaryScreen.h b/lib/client/CMSWindowsSecondaryScreen.h index 577413d7..83c9db52 100644 --- a/lib/client/CMSWindowsSecondaryScreen.h +++ b/lib/client/CMSWindowsSecondaryScreen.h @@ -68,6 +68,7 @@ protected: virtual void warpCursor(SInt32 x, SInt32 y); virtual void updateKeys(); virtual void setToggleState(KeyModifierMask); + virtual KeyModifierMask getToggleState() const; private: enum EKeyAction { kPress, kRelease, kRepeat }; diff --git a/lib/client/CSecondaryScreen.cpp b/lib/client/CSecondaryScreen.cpp index 71283bc5..049042f2 100644 --- a/lib/client/CSecondaryScreen.cpp +++ b/lib/client/CSecondaryScreen.cpp @@ -22,7 +22,9 @@ // CSecondaryScreen // -CSecondaryScreen::CSecondaryScreen() +CSecondaryScreen::CSecondaryScreen() : + m_active(false), + m_toggleKeys(0) { // do nothing } @@ -128,6 +130,9 @@ CSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) // update our keyboard state to reflect the local state updateKeys(); + // remember toggle key state + m_toggleKeys = getToggleState(); + // toggle modifiers that don't match the desired state setToggleState(mask); @@ -153,6 +158,9 @@ CSecondaryScreen::leave() // subclass hook onPreLeave(); + // restore toggle key state + setToggleState(m_toggleKeys); + // hide mouse showWindow(); diff --git a/lib/client/CSecondaryScreen.h b/lib/client/CSecondaryScreen.h index 20900ec3..53d50788 100644 --- a/lib/client/CSecondaryScreen.h +++ b/lib/client/CSecondaryScreen.h @@ -324,11 +324,20 @@ protected: */ virtual void setToggleState(KeyModifierMask) = 0; + //! Get the toggle key state + /*! + Returns the current state of the toggle keys. + */ + virtual KeyModifierMask getToggleState() const = 0; + private: CMutex m_mutex; // m_active is true if this screen has been entered bool m_active; + + // the toggle key state when this screen was last entered + KeyModifierMask m_toggleKeys; }; #endif diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index b7147dd6..bad3d9f1 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -405,6 +405,22 @@ CXWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) } } +KeyModifierMask +CXWindowsSecondaryScreen::getToggleState() const +{ + KeyModifierMask mask = 0; + if ((m_mask & m_capsLockMask) != 0) { + mask |= KeyModifierCapsLock; + } + if ((m_mask & m_numLockMask) != 0) { + mask |= KeyModifierNumLock; + } + if ((m_mask & m_scrollLockMask) != 0) { + mask |= KeyModifierScrollLock; + } + return mask; +} + unsigned int CXWindowsSecondaryScreen::mapButton(ButtonID id) const { diff --git a/lib/client/CXWindowsSecondaryScreen.h b/lib/client/CXWindowsSecondaryScreen.h index d055016b..3a9f1284 100644 --- a/lib/client/CXWindowsSecondaryScreen.h +++ b/lib/client/CXWindowsSecondaryScreen.h @@ -65,6 +65,7 @@ protected: virtual void warpCursor(SInt32 x, SInt32 y); virtual void updateKeys(); virtual void setToggleState(KeyModifierMask); + virtual KeyModifierMask getToggleState() const; private: enum EKeyAction { kPress, kRelease, kRepeat }; From 2559dd2f05d3bb7d3eba83bb07ff3128126ca766 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 15 Dec 2002 22:17:18 +0000 Subject: [PATCH 389/807] Now ignoring half-duplex keys that are down when deciding if the mouse is locked to the screen. We can't tell if a half- duplex key is physically down and logically down just means it's active so there's no point in letting it lock the mouse to the screen. --- lib/server/CXWindowsPrimaryScreen.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp index 57e0092d..06452696 100644 --- a/lib/server/CXWindowsPrimaryScreen.cpp +++ b/lib/server/CXWindowsPrimaryScreen.cpp @@ -133,6 +133,31 @@ CXWindowsPrimaryScreen::isLockedToScreen() const // locked if any key is down for (unsigned int i = 0; i < sizeof(keyMap); ++i) { if (keyMap[i] != 0) { + // if any key is half-duplex then it'll be down when + // toggled on but shouldn't count as a reason to lock + // to the screen. + if (m_numLockHalfDuplex || m_capsLockHalfDuplex) { + for (unsigned int j = 0; j < 8; ++j) { + if ((keyMap[i] & (1 << j)) != 0) { + const KeyCode keycode = 8 * i + j; + const KeySym keysym = XKeycodeToKeysym(display, + keycode, 0); + if (m_numLockHalfDuplex && keysym == XK_Num_Lock) { + continue; + } + if (m_capsLockHalfDuplex && keysym == XK_Caps_Lock) { + continue; + } + + // non-half-duplex key down + return true; + } + } + + // only half-duplex keys down + continue; + } + return true; } } From 9c709215250fc58c3623f113e802187e431d0073 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 15 Dec 2002 22:39:59 +0000 Subject: [PATCH 390/807] Now handling any number of pointer buttons. --- lib/client/CXWindowsSecondaryScreen.cpp | 29 ++++++++++++++++++++----- lib/client/CXWindowsSecondaryScreen.h | 2 +- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index bad3d9f1..ee1cfc0e 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -424,7 +424,7 @@ CXWindowsSecondaryScreen::getToggleState() const unsigned int CXWindowsSecondaryScreen::mapButton(ButtonID id) const { - if (id < 1 || id > sizeof(m_buttons) / sizeof(m_buttons[0])) { + if (id < 1 || id > m_buttons.size()) { // out of range return 0; } @@ -886,17 +886,34 @@ CXWindowsSecondaryScreen::updateKeys() { CDisplayLock display(m_screen); - // get pointer mapping - static const UInt32 maxButtons = sizeof(m_buttons) / sizeof(m_buttons[0]); - unsigned char tmpButtons[sizeof(m_buttons) / sizeof(m_buttons[0])]; - UInt32 numButtons = XGetPointerMapping(display, tmpButtons, maxButtons); - for (UInt32 i = 0; i < maxButtons; ++i) { + // query the button mapping + UInt32 numButtons = XGetPointerMapping(display, NULL, 0); + unsigned char* tmpButtons = new unsigned char[numButtons]; + XGetPointerMapping(display, tmpButtons, numButtons); + + // find the largest logical button id + unsigned char maxButton = 0; + for (UInt32 i = 0; i < numButtons; ++i) { + if (tmpButtons[i] > maxButton) { + maxButton = tmpButtons[i]; + } + } + + // allocate button array + m_buttons.resize(maxButton); + + // fill in button array values. m_buttons[i] is the physical + // button number for logical button i+1. + for (UInt32 i = 0; i < numButtons; ++i) { m_buttons[i] = 0; } for (UInt32 i = 0; i < numButtons; ++i) { m_buttons[tmpButtons[i] - 1] = i + 1; } + // clean up + delete[] tmpButtons; + // ask server which keys are pressed char keys[32]; XQueryKeymap(display, keys); diff --git a/lib/client/CXWindowsSecondaryScreen.h b/lib/client/CXWindowsSecondaryScreen.h index 3a9f1284..705f401e 100644 --- a/lib/client/CXWindowsSecondaryScreen.h +++ b/lib/client/CXWindowsSecondaryScreen.h @@ -130,7 +130,7 @@ private: // logical to physical button mapping. m_buttons[i] gives the // physical button for logical button i+1. - unsigned char m_buttons[5]; + std::vector m_buttons; // current active modifiers (X key masks) unsigned int m_mask; From 72578b80613ae582c5982d00a82cff1e7a018442 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 22 Dec 2002 14:51:41 +0000 Subject: [PATCH 391/807] Doxygen config file now sets HAVE_DOT to YES only if dot is found by configure. --- configure.in | 1 + doc/doxygen.cfg.in | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 0566a688..aba03541 100644 --- a/configure.in +++ b/configure.in @@ -30,6 +30,7 @@ dnl information on the package dnl checks for programs AC_PROG_CXX AC_PROG_RANLIB +AC_CHECK_PROG(HAVE_DOT, dot, YES, NO) dnl do checks using C++ AC_LANG_CPLUSPLUS diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index b950205a..30ce8fa3 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -768,7 +768,7 @@ CLASS_DIAGRAMS = NO # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) -HAVE_DOT = YES +HAVE_DOT = @HAVE_DOT@ # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and From 3fc1ddf6cedb1354a09ee0e9f179c3a1db899dc2 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 23 Dec 2002 13:55:21 +0000 Subject: [PATCH 392/807] Added support for per-screen options in the configuration file and sending those options to the appropriate client screens. Currently, two options are supported: halfDuplexCapsLock and halfDuplexNumLock mark the caps lock and num lock keys, respectively, as being half-duplex. --- lib/client/CClient.cpp | 12 +++ lib/client/CClient.h | 2 + lib/client/CMSWindowsSecondaryScreen.cpp | 12 +++ lib/client/CMSWindowsSecondaryScreen.h | 2 + lib/client/CSecondaryScreen.cpp | 3 + lib/client/CSecondaryScreen.h | 14 +++ lib/client/CXWindowsSecondaryScreen.cpp | 29 +++-- lib/client/CXWindowsSecondaryScreen.h | 2 + lib/server/CClientProxy.h | 2 + lib/server/CClientProxy1_0.cpp | 14 +++ lib/server/CClientProxy1_0.h | 2 + lib/server/CConfig.cpp | 130 +++++++++++++++++++++-- lib/server/CConfig.h | 33 ++++++ lib/server/CMSWindowsPrimaryScreen.cpp | 12 +++ lib/server/CMSWindowsPrimaryScreen.h | 2 + lib/server/CPrimaryClient.cpp | 12 +++ lib/server/CPrimaryClient.h | 2 + lib/server/CPrimaryScreen.cpp | 3 + lib/server/CPrimaryScreen.h | 14 +++ lib/server/CServer.cpp | 41 +++++++ lib/server/CServer.h | 3 + lib/server/CXWindowsPrimaryScreen.cpp | 31 ++++-- lib/server/CXWindowsPrimaryScreen.h | 2 + lib/synergy/CProtocolUtil.cpp | 115 ++++++++++++++++++++ lib/synergy/CProtocolUtil.h | 6 ++ lib/synergy/IClient.h | 14 +++ lib/synergy/Makefile.am | 1 + lib/synergy/OptionTypes.h | 52 +++++++++ lib/synergy/ProtocolTypes.h | 9 ++ 29 files changed, 557 insertions(+), 19 deletions(-) create mode 100644 lib/synergy/OptionTypes.h diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 2238f287..d84ac270 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -356,6 +356,18 @@ CClient::screensaver(bool activate) m_screen->screensaver(activate); } +void +CClient::resetOptions() +{ + m_screen->resetOptions(); +} + +void +CClient::setOptions(const COptionsList& options) +{ + m_screen->setOptions(options); +} + CString CClient::getName() const { diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 664eeb2a..de0818a5 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -130,6 +130,8 @@ public: virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseWheel(SInt32 delta); virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); virtual CString getName() const; virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index cba2ba4e..11536616 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -214,6 +214,18 @@ CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); } +void +CMSWindowsSecondaryScreen::resetOptions() +{ + // no options +} + +void +CMSWindowsSecondaryScreen::setOptions(const COptionsList& /*options*/) +{ + // no options +} + IScreen* CMSWindowsSecondaryScreen::getScreen() const { diff --git a/lib/client/CMSWindowsSecondaryScreen.h b/lib/client/CMSWindowsSecondaryScreen.h index 83c9db52..0681e236 100644 --- a/lib/client/CMSWindowsSecondaryScreen.h +++ b/lib/client/CMSWindowsSecondaryScreen.h @@ -45,6 +45,8 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); virtual void mouseWheel(SInt32 delta); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); virtual IScreen* getScreen() const; // IMSWindowsScreenEventHandler overrides diff --git a/lib/client/CSecondaryScreen.cpp b/lib/client/CSecondaryScreen.cpp index 049042f2..554e91db 100644 --- a/lib/client/CSecondaryScreen.cpp +++ b/lib/client/CSecondaryScreen.cpp @@ -87,6 +87,9 @@ CSecondaryScreen::open() // subclass hook onPostOpen(); + + // reset options + resetOptions(); } catch (...) { close(); diff --git a/lib/client/CSecondaryScreen.h b/lib/client/CSecondaryScreen.h index 53d50788..bca229b8 100644 --- a/lib/client/CSecondaryScreen.h +++ b/lib/client/CSecondaryScreen.h @@ -18,6 +18,7 @@ #include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" +#include "OptionTypes.h" #include "CMutex.h" class IClipboard; @@ -156,6 +157,19 @@ public: */ virtual void mouseWheel(SInt32 delta) = 0; + //! Notify of options changes + /*! + Reset all options to their default values. + */ + virtual void resetOptions() = 0; + + //! Notify of options changes + /*! + Set options to given values. Ignore unknown options and don't + modify our options that aren't given in \c options. + */ + virtual void setOptions(const COptionsList& options) = 0; + //@} //! @name accessors //@{ diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/client/CXWindowsSecondaryScreen.cpp index ee1cfc0e..996fb445 100644 --- a/lib/client/CXWindowsSecondaryScreen.cpp +++ b/lib/client/CXWindowsSecondaryScreen.cpp @@ -203,6 +203,28 @@ CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) XSync(display, False); } +void +CXWindowsSecondaryScreen::resetOptions() +{ + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +} + +void +CXWindowsSecondaryScreen::setOptions(const COptionsList& options) +{ + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionHalfDuplexCapsLock) { + m_capsLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); + } + else if (options[i] == kOptionHalfDuplexNumLock) { + m_numLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); + } + } +} + IScreen* CXWindowsSecondaryScreen::getScreen() const { @@ -262,12 +284,7 @@ CXWindowsSecondaryScreen::onPreOpen() void CXWindowsSecondaryScreen::onPostOpen() { - // check for peculiarities - // FIXME -- may have to get these from some database - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; -// m_numLockHalfDuplex = true; -// m_capsLockHalfDuplex = true; + assert(m_window != None); } void diff --git a/lib/client/CXWindowsSecondaryScreen.h b/lib/client/CXWindowsSecondaryScreen.h index 705f401e..ad2403c2 100644 --- a/lib/client/CXWindowsSecondaryScreen.h +++ b/lib/client/CXWindowsSecondaryScreen.h @@ -43,6 +43,8 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 x, SInt32 y); virtual void mouseWheel(SInt32 delta); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); virtual IScreen* getScreen() const; // IScreenEventHandler overrides diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index 8890de18..40447a6b 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -75,6 +75,8 @@ public: virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; virtual void mouseWheel(SInt32 delta) = 0; virtual void screensaver(bool activate) = 0; + virtual void resetOptions() = 0; + virtual void setOptions(const COptionsList& options) = 0; virtual CString getName() const; virtual SInt32 getJumpZoneSize() const = 0; virtual void getShape(SInt32& x, SInt32& y, diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index b21c8cfc..6ce07a43 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -252,6 +252,20 @@ CClientProxy1_0::screensaver(bool on) CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } +void +CClientProxy1_0::resetOptions() +{ + LOG((CLOG_DEBUG1 "send reset options to \"%s\"", getName().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCResetOptions); +} + +void +CClientProxy1_0::setOptions(const COptionsList& options) +{ + LOG((CLOG_DEBUG1 "send set options to \"%s\" size=%d", getName().c_str(), options.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDSetOptions, &options); +} + SInt32 CClientProxy1_0::getJumpZoneSize() const { diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 0c0cb48b..ac94f0f5 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -46,6 +46,8 @@ public: virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseWheel(SInt32 delta); virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 7e706cf4..66a5e94a 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -240,6 +240,34 @@ CConfig::setHTTPAddress(const CNetworkAddress& addr) m_httpAddress = addr; } +bool +CConfig::addOption(const CString& name, UInt32 option, SInt32 value) +{ + // find cell + CCellMap::iterator index = m_map.find(name); + if (index == m_map.end()) { + return false; + } + + // add option + index->second.m_options.insert(std::make_pair(option, value)); + return true; +} + +bool +CConfig::removeOption(const CString& name, UInt32 option) +{ + // find cell + CCellMap::iterator index = m_map.find(name); + if (index == m_map.end()) { + return false; + } + + // remove option + index->second.m_options.erase(option); + return true; +} + bool CConfig::isValidScreenName(const CString& name) const { @@ -359,6 +387,19 @@ CConfig::getHTTPAddress() const return m_httpAddress; } +const CConfig::CScreenOptions* +CConfig::getOptions(const CString& name) const +{ + // find cell + CCellMap::const_iterator index = m_map.find(name); + if (index == m_map.end()) { + return NULL; + } + + // return options + return &index->second.m_options; +} + bool CConfig::operator==(const CConfig& x) const { @@ -438,6 +479,39 @@ CConfig::readLine(std::istream& s, CString& line) return false; } +bool +CConfig::parseBoolean(const CString& arg) +{ + if (CStringUtil::CaselessCmp::equal(arg, "true")) + return true; + if (CStringUtil::CaselessCmp::equal(arg, "false")) + return false; + throw XConfigRead("invalid argument"); +} + +const char* +CConfig::getOptionName(OptionID id) +{ + if (id == kOptionHalfDuplexCapsLock) { + return "halfDuplexCapsLock"; + } + if (id == kOptionHalfDuplexNumLock) { + return "halfDuplexNumLock"; + } + return NULL; +} + +const char* +CConfig::getOptionValue(OptionID id, OptionValue value) +{ + if (id == kOptionHalfDuplexCapsLock || + id == kOptionHalfDuplexNumLock) { + return (value != 0) ? "true" : "false"; + } + + return ""; +} + void CConfig::readSection(std::istream& s) { @@ -547,7 +621,7 @@ void CConfig::readSectionScreens(std::istream& s) { CString line; - CString name; + CString screen; while (readLine(s, line)) { // check for end of section if (line == "end") { @@ -557,23 +631,54 @@ CConfig::readSectionScreens(std::istream& s) // see if it's the next screen if (line[line.size() - 1] == ':') { // strip : - name = line.substr(0, line.size() - 1); + screen = line.substr(0, line.size() - 1); // verify validity of screen name - if (!isValidScreenName(name)) { + if (!isValidScreenName(screen)) { throw XConfigRead("invalid screen name"); } // add the screen to the configuration - if (!addScreen(name)) { + if (!addScreen(screen)) { throw XConfigRead("duplicate screen name"); } } - else if (name.empty()) { + else if (screen.empty()) { throw XConfigRead("argument before first screen"); } else { - throw XConfigRead("unknown argument"); + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead("missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead("missing = in argument"); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead("missing = in argument"); + } + i = line.find_first_not_of(" \t", i + 1); + CString value; + if (i != CString::npos) { + value = line.substr(i); + } + + // handle argument + if (name == "halfDuplexCapsLock") { + addOption(screen, kOptionHalfDuplexCapsLock, + parseBoolean(value)); + } + else if (name == "halfDuplexNumLock") { + addOption(screen, kOptionHalfDuplexNumLock, + parseBoolean(value)); + } + else { + // unknown argument + throw XConfigRead("unknown argument"); + } } } throw XConfigRead("unexpected end of screens section"); @@ -740,6 +845,19 @@ operator<<(std::ostream& s, const CConfig& config) for (CConfig::const_iterator screen = config.begin(); screen != config.end(); ++screen) { s << "\t" << screen->c_str() << ":" << std::endl; + const CConfig::CScreenOptions* options = config.getOptions(*screen); + if (options != NULL && options->size() > 0) { + for (CConfig::CScreenOptions::const_iterator + option = options->begin(); + option != options->end(); ++option) { + const char* name = CConfig::getOptionName(option->first); + const char* value = CConfig::getOptionValue(option->first, + option->second); + if (name != NULL && value != NULL) { + s << "\t\t" << name << " = " << value << std::endl; + } + } + } } s << "end" << std::endl; diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index c028ebf5..6fce3dda 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -15,6 +15,7 @@ #ifndef CCONFIG_H #define CCONFIG_H +#include "OptionTypes.h" #include "ProtocolTypes.h" #include "CNetworkAddress.h" #include "XBase.h" @@ -45,10 +46,14 @@ comparing names. Screen names and their aliases share a namespace and must be unique. */ class CConfig { +public: + typedef std::map CScreenOptions; + private: class CCell { public: CString m_neighbor[kLastDirection - kFirstDirection + 1]; + CScreenOptions m_options; }; typedef std::map CCellMap; typedef std::map CNameMap; @@ -175,6 +180,23 @@ public: */ void setHTTPAddress(const CNetworkAddress&); + //! Add a screen option + /*! + Adds an option and its value to the named screen. Replaces the + existing option's value if there is one. Returns true iff \c name + is a known screen. + */ + bool addOption(const CString& name, + UInt32 option, SInt32 value); + + //! Remove a screen option + /*! + Removes an option and its value from the named screen. Does + nothing if the option doesn't exist on the screen. Returns true + iff \c name is a known screen. + */ + bool removeOption(const CString& name, UInt32 option); + //@} //! @name accessors //@{ @@ -227,6 +249,14 @@ public: //! Get the HTTP server address const CNetworkAddress& getHTTPAddress() const; + //! Get the screen options + /*! + Returns all the added options for the named screen. Returns NULL + if the screen is unknown and an empty collection if there are no + options. + */ + const CScreenOptions* getOptions(const CString& name) const; + //! Compare configurations bool operator==(const CConfig&) const; //! Compare configurations @@ -254,6 +284,9 @@ public: private: static bool readLine(std::istream&, CString&); + static bool parseBoolean(const CString&); + static const char* getOptionName(OptionID); + static const char* getOptionValue(OptionID, OptionValue); void readSection(std::istream&); void readSectionNetwork(std::istream&); void readSectionScreens(std::istream&); diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index d6b96cc6..6a28a092 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -99,6 +99,18 @@ CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) m_y = y; } +void +CMSWindowsPrimaryScreen::resetOptions() +{ + // no options +} + +void +CMSWindowsPrimaryScreen::setOptions(const COptionsList& /*options*/) +{ + // no options +} + KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { diff --git a/lib/server/CMSWindowsPrimaryScreen.h b/lib/server/CMSWindowsPrimaryScreen.h index f443f1e8..91dc2b11 100644 --- a/lib/server/CMSWindowsPrimaryScreen.h +++ b/lib/server/CMSWindowsPrimaryScreen.h @@ -37,6 +37,8 @@ public: // CPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; virtual IScreen* getScreen() const; diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 47279c5c..ca7d37d8 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -242,6 +242,18 @@ CPrimaryClient::screensaver(bool) // ignore } +void +CPrimaryClient::resetOptions() +{ + m_screen->resetOptions(); +} + +void +CPrimaryClient::setOptions(const COptionsList& options) +{ + m_screen->setOptions(options); +} + CString CPrimaryClient::getName() const { diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 10f744a6..5cd0c259 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -108,6 +108,8 @@ public: virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseWheel(SInt32 delta); virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); virtual CString getName() const; virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, diff --git a/lib/server/CPrimaryScreen.cpp b/lib/server/CPrimaryScreen.cpp index a9cd4297..e2b743d7 100644 --- a/lib/server/CPrimaryScreen.cpp +++ b/lib/server/CPrimaryScreen.cpp @@ -90,6 +90,9 @@ CPrimaryScreen::open() // subclass hook onPostOpen(); + + // reset options + resetOptions(); } catch (...) { close(); diff --git a/lib/server/CPrimaryScreen.h b/lib/server/CPrimaryScreen.h index 089c6eab..2e953c25 100644 --- a/lib/server/CPrimaryScreen.h +++ b/lib/server/CPrimaryScreen.h @@ -17,6 +17,7 @@ #include "ClipboardTypes.h" #include "KeyTypes.h" +#include "OptionTypes.h" #include "CMutex.h" class IClipboard; @@ -115,6 +116,19 @@ public: */ void grabClipboard(ClipboardID); + //! Notify of options changes + /*! + Reset all options to their default values. + */ + virtual void resetOptions() = 0; + + //! Notify of options changes + /*! + Set options to given values. Ignore unknown options and don't + modify our options that aren't given in \c options. + */ + virtual void setOptions(const COptionsList& options) = 0; + //@} //! @name accessors //@{ diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index aede8ca9..78d1c633 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -20,6 +20,7 @@ #include "COutputPacketStream.h" #include "CProtocolUtil.h" #include "CClientProxy1_0.h" +#include "OptionTypes.h" #include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" @@ -220,6 +221,8 @@ CServer::setConfig(const CConfig& config) m_primaryClient->reconfigure(getActivePrimarySides()); } + // FIXME -- tell all (connected) clients about current options + return true; } @@ -830,6 +833,8 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) IClient* CServer::getNeighbor(IClient* src, EDirection dir) const { + // note -- must be locked on entry + assert(src != NULL); CString srcName = src->getName(); @@ -863,6 +868,8 @@ IClient* CServer::getNeighbor(IClient* src, EDirection srcSide, SInt32& x, SInt32& y) const { + // note -- must be locked on entry + assert(src != NULL); // get the first neighbor @@ -1274,6 +1281,9 @@ CServer::runClient(void* vsocket) CLock lock(&m_mutex); m_clientThreads.insert(std::make_pair(proxy->getName(), CThread::getCurrentThread())); + + // send configuration options + sendOptions(proxy); } catch (XDuplicateClient& e) { // client has duplicate name @@ -1564,6 +1574,32 @@ CServer::processHTTPRequest(void* vsocket) } } +void +CServer::sendOptions(IClient* client) const +{ + // note -- must be locked on entry + + // look up options for client. we're done if there aren't any. + const CConfig::CScreenOptions* options = + m_config.getOptions(client->getName()); + if (options == NULL) { + return; + } + + // convert options to a more convenient form for sending + COptionsList optionsList; + optionsList.reserve(2 * options->size()); + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + optionsList.push_back(index->first); + optionsList.push_back(static_cast(index->second)); + } + + // send the options + client->resetOptions(); + client->setOptions(optionsList); +} + void CServer::openPrimaryScreen() { @@ -1605,8 +1641,13 @@ CServer::openPrimaryScreen() // tell it about the active sides m_primaryClient->reconfigure(getActivePrimarySides()); + + // tell primary client about its options + sendOptions(m_primaryClient); } catch (...) { + // if m_active is NULL then we haven't added the connection + // for the primary client so we don't try to remove it. if (m_active != NULL) { removeConnection(primaryName); } diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 49583c6e..885d0e08 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -197,6 +197,9 @@ private: IClient* getNeighbor(IClient*, EDirection, SInt32& x, SInt32& y) const; + // send screen options to \c client + void sendOptions(IClient* client) const; + // open/close the primary screen void openPrimaryScreen(); void closePrimaryScreen(); diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp index 06452696..b544235b 100644 --- a/lib/server/CXWindowsPrimaryScreen.cpp +++ b/lib/server/CXWindowsPrimaryScreen.cpp @@ -79,6 +79,28 @@ CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) m_y = y; } +void +CXWindowsPrimaryScreen::resetOptions() +{ + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +} + +void +CXWindowsPrimaryScreen::setOptions(const COptionsList& options) +{ + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionHalfDuplexCapsLock) { + m_capsLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); + } + else if (options[i] == kOptionHalfDuplexNumLock) { + m_numLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); + } + } +} + KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const { @@ -385,16 +407,11 @@ CXWindowsPrimaryScreen::onPreOpen() void CXWindowsPrimaryScreen::onPostOpen() { + assert(m_window != None); + // get cursor info m_screen->getCursorPos(m_x, m_y); m_screen->getCursorCenter(m_xCenter, m_yCenter); - - // check for peculiarities - // FIXME -- may have to get these from some database - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; -// m_numLockHalfDuplex = true; -// m_capsLockHalfDuplex = true; } void diff --git a/lib/server/CXWindowsPrimaryScreen.h b/lib/server/CXWindowsPrimaryScreen.h index b67f3009..c24464b8 100644 --- a/lib/server/CXWindowsPrimaryScreen.h +++ b/lib/server/CXWindowsPrimaryScreen.h @@ -38,6 +38,8 @@ public: // CPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; virtual IScreen* getScreen() const; diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp index c68b9a7e..41dff388 100644 --- a/lib/synergy/CProtocolUtil.cpp +++ b/lib/synergy/CProtocolUtil.cpp @@ -16,6 +16,7 @@ #include "IInputStream.h" #include "IOutputStream.h" #include "CLog.h" +#include "stdvector.h" #include #include @@ -116,6 +117,56 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) break; } + case 'I': { + // check for valid length + assert(len == 1 || len == 2 || len == 4); + + // read the vector length + UInt8 buffer[4]; + read(stream, buffer, 4); + UInt32 len = (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); + + // convert it + void* v = va_arg(args, void*); + switch (len) { + case 1: + // 1 byte integer + for (UInt32 i = 0; i < len; ++i) { + reinterpret_cast*>(v)->push_back( + buffer[0]); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + } + break; + + case 2: + // 2 byte integer + for (UInt32 i = 0; i < len; ++i) { + reinterpret_cast*>(v)->push_back( + static_cast( + (static_cast(buffer[0]) << 8) | + static_cast(buffer[1]))); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + } + break; + + case 4: + // 4 byte integer + for (UInt32 i = 0; i < len; ++i) { + reinterpret_cast*>(v)->push_back( + (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3])); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + } + break; + } + break; + } + case 's': { assert(len == 0); @@ -207,6 +258,24 @@ CProtocolUtil::getLength(const char* fmt, va_list args) (void)va_arg(args, UInt32); break; + case 'I': + assert(len == 1 || len == 2 || len == 4); + switch (len) { + case 1: + len = (va_arg(args, std::vector*))->size() + 4; + break; + + case 2: + len = 2 * (va_arg(args, std::vector*))->size() + 4; + break; + + case 4: + len = 4 * (va_arg(args, std::vector*))->size() + 4; + break; + } + (void)va_arg(args, void*); + break; + case 's': assert(len == 0); len = (va_arg(args, CString*))->size() + 4; @@ -281,6 +350,52 @@ CProtocolUtil::writef(void* buffer, const char* fmt, va_list args) break; } + case 'I': { + const UInt32 v = va_arg(args, UInt32); + switch (len) { + case 1: { + // 1 byte integers + const std::vector* list = + va_arg(args, const std::vector*); + for (UInt32 i = 0, n = list->size(); i < n; ++i) { + *dst++ = (*list)[i]; + } + break; + } + + case 2: { + // 2 byte integers + const std::vector* list = + va_arg(args, const std::vector*); + for (UInt32 i = 0, n = list->size(); i < n; ++i) { + const UInt16 v = (*list)[i]; + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + } + break; + } + + case 4: { + // 4 byte integers + const std::vector* list = + va_arg(args, const std::vector*); + for (UInt32 i = 0, n = list->size(); i < n; ++i) { + const UInt32 v = (*list)[i]; + *dst++ = static_cast((v >> 24) & 0xff); + *dst++ = static_cast((v >> 16) & 0xff); + *dst++ = static_cast((v >> 8) & 0xff); + *dst++ = static_cast( v & 0xff); + } + break; + } + + default: + assert(0 && "invalid integer vector format length"); + return; + } + break; + } + case 's': { assert(len == 0); const CString* src = va_arg(args, CString*); diff --git a/lib/synergy/CProtocolUtil.h b/lib/synergy/CProtocolUtil.h index 9228a132..1e0db39f 100644 --- a/lib/synergy/CProtocolUtil.h +++ b/lib/synergy/CProtocolUtil.h @@ -41,6 +41,9 @@ public: - \%1i -- converts integer argument to 1 byte integer - \%2i -- converts integer argument to 2 byte integer in NBO - \%4i -- converts integer argument to 4 byte integer in NBO + - \%1I -- converts std::vector* to 1 byte integers + - \%2I -- converts std::vector* to 2 byte integers in NBO + - \%4I -- converts std::vector* to 4 byte integers in NBO - \%s -- converts CString* to stream of bytes - \%S -- converts integer N and const UInt8* to stream of N bytes */ @@ -57,6 +60,9 @@ public: - \%1i -- reads a 1 byte integer; argument is a SInt32* or UInt32* - \%2i -- reads an NBO 2 byte integer; arg is SInt32* or UInt32* - \%4i -- reads an NBO 4 byte integer; arg is SInt32* or UInt32* + - \%1I -- reads 1 byte integers; arg is std::vector* + - \%2I -- reads NBO 2 byte integers; arg is std::vector* + - \%4I -- reads NBO 4 byte integers; arg is std::vector* - \%s -- reads bytes; argument must be a CString*, \b not a char* */ static void readf(IInputStream*, diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index 5031bcbc..18804bce 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -19,6 +19,7 @@ #include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" +#include "OptionTypes.h" #include "CString.h" //! Client interface @@ -151,6 +152,19 @@ public: //! Notify of screen saver change virtual void screensaver(bool activate) = 0; + //! Notify of options changes + /*! + Reset all options to their default values. + */ + virtual void resetOptions() = 0; + + //! Notify of options changes + /*! + Set options to given values. Ignore unknown options and don't + modify our options that aren't given in \c options. + */ + virtual void setOptions(const COptionsList& options) = 0; + //@} //! @name accessors //@{ diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 2661667c..f7e5d64c 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -46,6 +46,7 @@ libsynergy_a_SOURCES = \ IServer.h \ KeyTypes.h \ MouseTypes.h \ + OptionTypes.h \ ProtocolTypes.h \ XScreen.h \ XSynergy.h \ diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h new file mode 100644 index 00000000..3310ace0 --- /dev/null +++ b/lib/synergy/OptionTypes.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef OPTIONTYPES_H +#define OPTIONTYPES_H + +#include "BasicTypes.h" +#include "stdvector.h" + +//! Option ID +/*! +Type to hold an option identifier. +*/ +typedef UInt32 OptionID; + +//! Option Value +/*! +Type to hold an option value. +*/ +typedef SInt32 OptionValue; + +// for now, options are just pairs of integers +typedef std::vector COptionsList; + +// macro for packing 4 character strings into 4 byte integers +#define OPTION_CODE(_s) \ + (static_cast(static_cast(_s[0]) << 24) | \ + static_cast(static_cast(_s[1]) << 16) | \ + static_cast(static_cast(_s[2]) << 8) | \ + static_cast(static_cast(_s[3]) )) + +//! @name Option identifiers +//@{ +static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); +static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); +static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); +//@} + +#undef OPTION_CODE + +#endif diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index 53b6bd1e..52201320 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -118,6 +118,10 @@ static const char kMsgCClipboard[] = "CCLP%1i%4i"; // screensaver on primary has started ($1 == 1) or closed ($1 == 0) static const char kMsgCScreenSaver[] = "CSEC%1i"; +// reset options: primary -> secondary +// client should reset all of its options to their defaults. +static const char kMsgCResetOptions[] = "CROP"; + // resolution change acknowledgment: primary -> secondary // sent by primary in response to a secondary screen's kMsgDInfo. // this is sent for every kMsgDInfo, whether or not the primary @@ -181,6 +185,11 @@ static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; // the new screen area. static const char kMsgDInfo[] = "DINF%2i%2i%2i%2i%2i%2i%2i"; +// set options: primary -> secondary +// client should set the given option/value pairs. $1 = option/value +// pairs. +static const char kMsgDSetOptions[] = "DSOP%4I"; + // // query codes From 78538da7543bf48ea09d55d194b62fd9c6cb3e64 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 23 Dec 2002 14:47:44 +0000 Subject: [PATCH 393/807] Added code to process set/reset options messages from server. --- lib/client/CServerProxy.cpp | 32 ++++++++++++++++++++++++++++++++ lib/client/CServerProxy.h | 2 ++ 2 files changed, 34 insertions(+) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 920be85d..22db4143 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -15,6 +15,7 @@ #include "CServerProxy.h" #include "CProtocolUtil.h" #include "IClient.h" +#include "OptionTypes.h" #include "ProtocolTypes.h" #include "IInputStream.h" #include "IOutputStream.h" @@ -161,6 +162,14 @@ CServerProxy::mainLoop() setClipboard(); } + else if (memcmp(code, kMsgCResetOptions, 4) == 0) { + resetOptions(); + } + + else if (memcmp(code, kMsgDSetOptions, 4) == 0) { + setOptions(); + } + else if (memcmp(code, kMsgCClose, 4) == 0) { // server wants us to hangup LOG((CLOG_DEBUG1 "recv close")); @@ -197,6 +206,7 @@ CServerProxy::mainLoop() else { // unknown message LOG((CLOG_ERR "unknown message from server")); + LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3])); failedToConnect = true; break; } @@ -516,6 +526,28 @@ CServerProxy::screensaver() getClient()->screensaver(on != 0); } +void +CServerProxy::resetOptions() +{ + // parse + LOG((CLOG_DEBUG1 "recv reset options")); + + // forward + getClient()->resetOptions(); +} + +void +CServerProxy::setOptions() +{ + // parse + COptionsList options; + CProtocolUtil::readf(getInputStream(), kMsgDSetOptions + 4, &options); + LOG((CLOG_DEBUG1 "recv set options size=%d", options.size())); + + // forward + getClient()->setOptions(options); +} + void CServerProxy::queryInfo() { diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index f67b72f6..a54ccdff 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -105,6 +105,8 @@ private: void mouseMove(); void mouseWheel(); void screensaver(); + void resetOptions(); + void setOptions(); void queryInfo(); void infoAcknowledgment(); From 50e9e855b0efaff37310b536adb3f27ae51de252 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 23 Dec 2002 14:48:12 +0000 Subject: [PATCH 394/807] Fixed handling of %I arguments to readf() and writef(). --- lib/synergy/CProtocolUtil.cpp | 44 ++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp index 41dff388..7ef9de6a 100644 --- a/lib/synergy/CProtocolUtil.cpp +++ b/lib/synergy/CProtocolUtil.cpp @@ -124,17 +124,18 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) // read the vector length UInt8 buffer[4]; read(stream, buffer, 4); - UInt32 len = (static_cast(buffer[0]) << 24) | - (static_cast(buffer[1]) << 16) | - (static_cast(buffer[2]) << 8) | - static_cast(buffer[3]); + UInt32 n = (static_cast(buffer[0]) << 24) | + (static_cast(buffer[1]) << 16) | + (static_cast(buffer[2]) << 8) | + static_cast(buffer[3]); // convert it void* v = va_arg(args, void*); switch (len) { case 1: // 1 byte integer - for (UInt32 i = 0; i < len; ++i) { + for (UInt32 i = 0; i < n; ++i) { + read(stream, buffer, 1); reinterpret_cast*>(v)->push_back( buffer[0]); LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); @@ -143,7 +144,8 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) case 2: // 2 byte integer - for (UInt32 i = 0; i < len; ++i) { + for (UInt32 i = 0; i < n; ++i) { + read(stream, buffer, 2); reinterpret_cast*>(v)->push_back( static_cast( (static_cast(buffer[0]) << 8) | @@ -154,7 +156,8 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) case 4: // 4 byte integer - for (UInt32 i = 0; i < len; ++i) { + for (UInt32 i = 0; i < n; ++i) { + read(stream, buffer, 4); reinterpret_cast*>(v)->push_back( (static_cast(buffer[0]) << 24) | (static_cast(buffer[1]) << 16) | @@ -266,14 +269,13 @@ CProtocolUtil::getLength(const char* fmt, va_list args) break; case 2: - len = 2 * (va_arg(args, std::vector*))->size() + 4; + len = 2 * (va_arg(args, std::vector*))->size() + 4; break; case 4: - len = 4 * (va_arg(args, std::vector*))->size() + 4; + len = 4 * (va_arg(args, std::vector*))->size() + 4; break; } - (void)va_arg(args, void*); break; case 's': @@ -351,13 +353,17 @@ CProtocolUtil::writef(void* buffer, const char* fmt, va_list args) } case 'I': { - const UInt32 v = va_arg(args, UInt32); switch (len) { case 1: { // 1 byte integers const std::vector* list = va_arg(args, const std::vector*); - for (UInt32 i = 0, n = list->size(); i < n; ++i) { + const UInt32 n = list->size(); + *dst++ = static_cast((n >> 24) & 0xff); + *dst++ = static_cast((n >> 16) & 0xff); + *dst++ = static_cast((n >> 8) & 0xff); + *dst++ = static_cast( n & 0xff); + for (UInt32 i = 0; i < n; ++i) { *dst++ = (*list)[i]; } break; @@ -367,7 +373,12 @@ CProtocolUtil::writef(void* buffer, const char* fmt, va_list args) // 2 byte integers const std::vector* list = va_arg(args, const std::vector*); - for (UInt32 i = 0, n = list->size(); i < n; ++i) { + const UInt32 n = list->size(); + *dst++ = static_cast((n >> 24) & 0xff); + *dst++ = static_cast((n >> 16) & 0xff); + *dst++ = static_cast((n >> 8) & 0xff); + *dst++ = static_cast( n & 0xff); + for (UInt32 i = 0; i < n; ++i) { const UInt16 v = (*list)[i]; *dst++ = static_cast((v >> 8) & 0xff); *dst++ = static_cast( v & 0xff); @@ -379,7 +390,12 @@ CProtocolUtil::writef(void* buffer, const char* fmt, va_list args) // 4 byte integers const std::vector* list = va_arg(args, const std::vector*); - for (UInt32 i = 0, n = list->size(); i < n; ++i) { + const UInt32 n = list->size(); + *dst++ = static_cast((n >> 24) & 0xff); + *dst++ = static_cast((n >> 16) & 0xff); + *dst++ = static_cast((n >> 8) & 0xff); + *dst++ = static_cast( n & 0xff); + for (UInt32 i = 0; i < n; ++i) { const UInt32 v = (*list)[i]; *dst++ = static_cast((v >> 24) & 0xff); *dst++ = static_cast((v >> 16) & 0xff); From 1eb2ed2d3f7e1a900ec1b8ade9725d9f1aa5d19e Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 23 Dec 2002 14:49:14 +0000 Subject: [PATCH 395/807] No longer sending options if there aren't any and no longer sending a reset before sending options (the caller can do that if necessary). --- lib/server/CServer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 78d1c633..e08db389 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -1582,7 +1582,7 @@ CServer::sendOptions(IClient* client) const // look up options for client. we're done if there aren't any. const CConfig::CScreenOptions* options = m_config.getOptions(client->getName()); - if (options == NULL) { + if (options == NULL || options->size() == 0) { return; } @@ -1596,7 +1596,6 @@ CServer::sendOptions(IClient* client) const } // send the options - client->resetOptions(); client->setOptions(optionsList); } From de1fadc027bf8653259284a3530172de531e78e9 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 24 Dec 2002 10:01:28 +0000 Subject: [PATCH 396/807] Added OptionTypes.h to VC++ project. --- lib/synergy/libsynergy.dsp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 890ec743..b53b957d 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -175,6 +175,10 @@ SOURCE=.\MouseTypes.h # End Source File # Begin Source File +SOURCE=.\OptionTypes.h +# End Source File +# Begin Source File + SOURCE=.\ProtocolTypes.h # End Source File # Begin Source File From 9567a970a82ebf6b8f2a8868091aa6373d717a5a Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 25 Dec 2002 10:35:59 +0000 Subject: [PATCH 397/807] Changes to support building on solaris, irix, and darwin. Also removed test for working fork (AC_FORK). --- acinclude.m4 | 149 +++- config/config.guess | 1327 ++++++++++++++++++++++++++++++ config/config.sub | 1410 ++++++++++++++++++++++++++++++++ configure.in | 17 +- lib/base/common.h | 4 +- lib/mt/CThreadRep.cpp | 4 + lib/net/CNetwork.cpp | 546 ++++++++----- lib/net/CNetwork.h | 145 ++-- lib/platform/CUnixPlatform.cpp | 2 - 9 files changed, 3297 insertions(+), 307 deletions(-) create mode 100755 config/config.guess create mode 100755 config/config.sub diff --git a/acinclude.m4 b/acinclude.m4 index 42ebc789..8a57a75d 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -10,9 +10,41 @@ dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. +AC_DEFUN([ACX_CHECK_SOCKLEN_T], [ + AC_MSG_CHECKING([for socklen_t]) + AC_TRY_COMPILE([ + #include + #include + ], + [socklen_t len;],[acx_socklen_t_ok=yes],[acx_socklen_t_ok=no]) + AC_MSG_RESULT($acx_socklen_t_ok) + if test x"$acx_socklen_t_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_SOCKLEN_T,1,[Define if your compiler defines socklen_t.]),[$1]) + : + else + acx_socklen_t_ok=no + $2 + fi +])dnl ACX_CHECK_SOCKLEN_T + +AC_DEFUN([ACX_CHECK_CXX], [ + AC_MSG_CHECKING([if g++ defines correct C++ macro]) + AC_TRY_COMPILE(, [ + #if defined(_LANGUAGE_C) && !defined(_LANGUAGE_C_PLUS_PLUS) + #error wrong macro + #endif],[acx_cxx_macro_ok=yes],[acx_cxx_macro_ok=no]) + AC_MSG_RESULT($acx_cxx_macro_ok) + if test x"$acx_cxx_macro_ok" = xyes; then + SYNERGY_CXXFLAGS="" + else + SYNERGY_CXXFLAGS="-U_LANGUAGE_C -D_LANGUAGE_C_PLUS_PLUS" + fi +])dnl ACX_CHECK_CXX + AC_DEFUN([ACX_CHECK_CXX_BOOL], [ AC_MSG_CHECKING([for bool support]) - AC_TRY_COMPILE(, [bool t = true, f = false;],[acx_cxx_bool_ok=yes]) + AC_TRY_COMPILE(, [bool t = true, f = false;], + [acx_cxx_bool_ok=yes],[acx_cxx_bool_ok=no]) AC_MSG_RESULT($acx_cxx_bool_ok) if test x"$acx_cxx_bool_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_CXX_BOOL,1,[Define if your compiler has bool support.]),[$1]) @@ -21,11 +53,12 @@ AC_DEFUN([ACX_CHECK_CXX_BOOL], [ acx_cxx_bool_ok=no $2 fi -])dnl ACX_CHECK_BOOL +])dnl ACX_CHECK_CXX_BOOL AC_DEFUN([ACX_CHECK_CXX_EXCEPTIONS], [ AC_MSG_CHECKING([for exception support]) - AC_TRY_COMPILE(, [try{throw int(4);}catch(int){throw;}catch(...){}],[acx_cxx_exception_ok=yes]) + AC_TRY_COMPILE(, [try{throw int(4);}catch(int){throw;}catch(...){}], + [acx_cxx_exception_ok=yes],[acx_cxx_exception_ok=no]) AC_MSG_RESULT($acx_cxx_exception_ok) if test x"$acx_cxx_exception_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_CXX_EXCEPTIONS,1,[Define if your compiler has exceptions support.]),[$1]) @@ -38,7 +71,9 @@ AC_DEFUN([ACX_CHECK_CXX_EXCEPTIONS], [ AC_DEFUN([ACX_CHECK_CXX_CASTS], [ AC_MSG_CHECKING([for C++ cast support]) - AC_TRY_COMPILE(, [const char* f="a";const_cast(f);reinterpret_cast(f);static_cast(4.5);],[acx_cxx_cast_ok=yes]) + AC_TRY_COMPILE(, [const char* f="a";const_cast(f); + reinterpret_cast(f);static_cast(4.5);], + [acx_cxx_cast_ok=yes],[acx_cxx_cast_ok=no]) AC_MSG_RESULT($acx_cxx_cast_ok) if test x"$acx_cxx_cast_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_CXX_CASTS,1,[Define if your compiler has C++ cast support.]),[$1]) @@ -51,7 +86,8 @@ AC_DEFUN([ACX_CHECK_CXX_CASTS], [ AC_DEFUN([ACX_CHECK_CXX_MUTABLE], [ AC_MSG_CHECKING([for mutable support]) - AC_TRY_COMPILE(, [struct A{mutable int b;void f() const {b=0;}};A a;a.f();],[acx_cxx_mutable_ok=yes]) + AC_TRY_COMPILE(, [struct A{mutable int b;void f() const {b=0;}}; + A a;a.f();],[acx_cxx_mutable_ok=yes],[acx_cxx_mutable_ok=no]) AC_MSG_RESULT($acx_cxx_mutable_ok) if test x"$acx_cxx_mutable_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_CXX_MUTABLE,1,[Define if your compiler has mutable support.]),[$1]) @@ -64,7 +100,8 @@ AC_DEFUN([ACX_CHECK_CXX_MUTABLE], [ AC_DEFUN([ACX_CHECK_CXX_STDLIB], [ AC_MSG_CHECKING([for C++ standard library]) - AC_TRY_LINK([#include ], [std::set a; a.insert(3);],[acx_cxx_stdlib_ok=yes]) + AC_TRY_LINK([#include ], [std::set a; a.insert(3);], + [acx_cxx_stdlib_ok=yes],[acx_cxx_stdlib_ok=no]) AC_MSG_RESULT($acx_cxx_stdlib_ok) if test x"$acx_cxx_stdlib_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_CXX_STDLIB,1,[Define if your compiler has standard C++ library support.]),[$1]) @@ -161,7 +198,58 @@ AC_DEFUN([ACX_CHECK_NANOSLEEP], [ acx_nanosleep_ok=no $2 fi -])dnl ACX_CHECK_POLL +])dnl ACX_CHECK_NANOSLEEP + +dnl See if we need extra libraries for inet_aton +AC_DEFUN([ACX_CHECK_INET_ATON], [ + acx_inet_aton_ok=no + acx_inet_aton_list="" + + dnl check if user has set INET_ATON_LIBS + save_user_INET_ATON_LIBS="$INET_ATON_LIBS" + if test x"$INET_ATON_LIBS" != x; then + acx_inet_aton_list=user + fi + + dnl check various libraries (including no extra libraries) for + dnl inet_aton. `none' should appear first. + acx_inet_aton_list="none $acx_inet_aton_list resolv" + for flag in $acx_inet_aton_list; do + case $flag in + none) + AC_MSG_CHECKING([for inet_aton]) + INET_ATON_LIBS="" + ;; + + user) + AC_MSG_CHECKING([for inet_aton in $save_user_INET_ATON_LIBS]) + INET_ATON_LIBS="$save_user_INET_ATON_LIBS" + ;; + + *) + AC_MSG_CHECKING([for inet_aton in -l$flag]) + INET_ATON_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + LIBS="$INET_ATON_LIBS $LIBS" + AC_TRY_LINK([#include + #include + #include + #include ], + [struct in_addr addr; inet_aton("foo.bar", &addr);], + acx_inet_aton_ok=yes, acx_inet_aton_ok=no) + LIBS="$save_LIBS" + AC_MSG_RESULT($acx_inet_aton_ok) + if test x"$acx_inet_aton_ok" = xyes; then + break; + fi + INET_ATON_LIBS="" + done + + AC_SUBST(INET_ATON_LIBS) +])dnl ACX_CHECK_INET_ATON dnl The following macros are from http://www.gnu.org/software/ac-archive/ dnl which distributes them under the following license: @@ -207,6 +295,7 @@ dnl delete any notice of this special exception to the GPL from your modified dnl version AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h @@ -217,11 +306,11 @@ acx_pthread_ok=no # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then - save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then @@ -229,7 +318,7 @@ if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" fi # We must check for the threads library under a number of different @@ -296,9 +385,9 @@ for flag in $acx_pthread_flags; do esac save_LIBS="$LIBS" - save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" LIBS="$PTHREAD_LIBS $LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we @@ -316,7 +405,7 @@ for flag in $acx_pthread_flags; do [acx_pthread_ok=yes]) LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then @@ -332,8 +421,8 @@ fi if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" - save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: threads are created detached by default # and the JOINABLE attribute has a nonstandard name (UNDETACHED). @@ -361,15 +450,38 @@ if test "x$acx_pthread_ok" = xyes; then case "${host_cpu}-${host_os}" in *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; alpha*-osf*) flag="-D_REENTRANT";; - *solaris*) flag="-D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS";; + *solaris*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi + # Detect POSIX sigwait() + AC_MSG_CHECKING([for POSIX sigwait]) + AC_TRY_LINK([#include + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=yes, ok=unknown) + if test x"$ok" = xunknown; then + save_CXXFLAGS2="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -D_POSIX_PTHREAD_SEMANTICS" + AC_TRY_LINK([#include + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=-D_POSIX_PTHREAD_SEMANTICS, ok=no) + CXXFLAGS="$save_CXXFLAGS2" + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" != xno; then + AC_DEFINE(HAVE_POSIX_SIGWAIT,1,[Define if you have a POSIX \`sigwait\' function.]) + if test x"$ok" != xyes; then + PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS" + fi + fi + LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" # More AIX lossage: must compile with cc_r AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) @@ -389,5 +501,4 @@ else acx_pthread_ok=no $2 fi - ])dnl ACX_PTHREAD diff --git a/config/config.guess b/config/config.guess new file mode 100755 index 00000000..6ead80a0 --- /dev/null +++ b/config/config.guess @@ -0,0 +1,1327 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-08-21' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Written by Per Bothner . +# Please send patches to . +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + + +dummy=dummy-$$ +trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int dummy(){}" > $dummy.c ; + for c in cc gcc c89 ; do + ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ; + if test $? = 0 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + rm -f $dummy.c $dummy.o $dummy.rel ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_MACHINE}" in + i?86) + test -z "$VENDOR" && VENDOR=pc + ;; + *) + test -z "$VENDOR" && VENDOR=unknown + ;; +esac +test -f /etc/SuSE-release && VENDOR=suse + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # Netbsd (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # Determine the machine/vendor (is the vendor relevant). + case "${UNAME_MACHINE}" in + amiga) machine=m68k-unknown ;; + arm32) machine=arm-unknown ;; + atari*) machine=m68k-atari ;; + sun3*) machine=m68k-sun ;; + mac68k) machine=m68k-apple ;; + macppc) machine=powerpc-apple ;; + hp3[0-9][05]) machine=m68k-hp ;; + ibmrt|romp-ibm) machine=romp-ibm ;; + *) machine=${UNAME_MACHINE}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE}" in + i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + cat <$dummy.s + .data +\$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + + .text + .globl main + .align 4 + .ent main +main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) + UNAME_MACHINE="alpha" + ;; + 1-0) + UNAME_MACHINE="alphaev5" + ;; + 1-1) + UNAME_MACHINE="alphaev56" + ;; + 1-101) + UNAME_MACHINE="alphapca56" + ;; + 2-303) + UNAME_MACHINE="alphaev6" + ;; + 2-307) + UNAME_MACHINE="alphaev67" + ;; + 2-1307) + UNAME_MACHINE="alphaev68" + ;; + esac + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + arc64:OpenBSD:*:*) + echo mips64el-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hkmips:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + sparc*:NetBSD:*) + echo `uname -p`-unknown-netbsd${UNAME_RELEASE} + exit 0 ;; + atari*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + sun3*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy \ + && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + case "${HPUX_REV}" in + 11.[0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + esac ;; + esac + fi ;; + esac + if [ "${HP_ARCH}" = "" ]; then + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + eval $set_cc_for_build + (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy` + if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi + rm -f $dummy.c $dummy + fi ;; + esac + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + hppa*:OpenBSD:*:*) + echo hppa-unknown-openbsd + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3D:*:*:*) + echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i386-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + mips:Linux:*:*) + case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in + big) echo mips-${VENDOR}-linux && exit 0 ;; + little) echo mipsel-${VENDOR}-linux && exit 0 ;; + esac + case `sed -n '/^system type/s/^.*: \([^ ]*\).*/\1/p' < /proc/cpuinfo` in + SGI|sgi) echo mips-${VENDOR}-linux-gnu && exit 0 ;; + esac + ;; + ppc:Linux:*:*|ppc64:Linux:*:*) + echo powerpc-${VENDOR}-linux + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-${VENDOR}-linux${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-${VENDOR}-linux ;; + PA8*) echo hppa2.0-${VENDOR}-linux ;; + *) echo hppa-${VENDOR}-linux ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-${VENDOR}-linux + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-${VENDOR}-linux + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-${VENDOR}-linux + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + ld_supported_targets=`cd /; ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-${VENDOR}-linux" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-${VENDOR}-linuxaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-${VENDOR}-linuxcoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linuxoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-${VENDOR}-linuxoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + cat >$dummy.c < +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __ELF__ +# ifdef __GLIBC__ +# if __GLIBC__ >= 2 + printf ("%s-${VENDOR}-linux\n", argv[1]); +# else + printf ("%s-${VENDOR}-linuxlibc1\n", argv[1]); +# endif +# else + printf ("%s-${VENDOR}-linuxlibc1\n", argv[1]); +# endif +#else + printf ("%s-${VENDOR}-linuxaout\n", argv[1]); +#endif + return 0; +} +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + echo `uname -p`-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + if test "${UNAME_MACHINE}" = "x86pc"; then + UNAME_MACHINE=pc + fi + echo `uname -p`-${UNAME_MACHINE}-nto-qnx + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[KW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +eval $set_cc_for_build +$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0 +rm -f $dummy.c $dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config/config.sub b/config/config.sub new file mode 100755 index 00000000..83f4b015 --- /dev/null +++ b/config/config.sub @@ -0,0 +1,1410 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-08-13' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dsp16xx \ + | fr30 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips16 | mips64 | mips64el | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el | mips64vr4300 \ + | mips64vr4300el | mips64vr5000 | mips64vr5000el \ + | mipsbe | mipsel | mipsle | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | ns16k | ns32k \ + | openrisc \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | s390 | s390x \ + | sh | sh[34] | sh[34]eb | shbe | shle \ + | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic80 | tron \ + | v850 \ + | we32k \ + | x86 | xscale \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alphapca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armv*-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c54x-* \ + | clipper-* | cray2-* | cydra-* \ + | d10v-* | d30v-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | m32r-* \ + | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \ + | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipsel-* \ + | mipsle-* | mipstx39-* | mipstx39el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | s390-* | s390x-* \ + | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* \ + | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \ + | v850-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [cjt]90) + basic_machine=${basic_machine}-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mipsel*-linux*) + basic_machine=mipsel-unknown + ;; + mips*-linux*) + basic_machine=mips-unknown + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i686-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc64) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=t3e-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + windows32) + basic_machine=i386-pc + os=-windows32-msvcrt + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + mips) + case $os in + linux*) + basic_machine=mips-unknown + ;; + *) + basic_machine=mips-mips + ;; + esac + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh3eb | sh4eb) + basic_machine=sh-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + c4x*) + basic_machine=c4x-none + os=-coff + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto*) + os=-nto-qnx + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure.in b/configure.in index aba03541..1d576dca 100644 --- a/configure.in +++ b/configure.in @@ -35,15 +35,22 @@ AC_CHECK_PROG(HAVE_DOT, dot, YES, NO) dnl do checks using C++ AC_LANG_CPLUSPLUS +dnl our files end in .cpp not .C so tests should also end in .cpp +ac_ext=cpp + +dnl check compiler +ACX_CHECK_CXX + dnl checks for libraries ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) ACX_CHECK_NANOSLEEP +ACX_CHECK_INET_ATON dnl checks for header files AC_HEADER_STDC -AC_CHECK_HEADERS([unistd.h sys/time.h]) +AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h]) +AC_CHECK_HEADERS([sys/socket.h sys/select.h]) AC_CHECK_HEADERS([istream ostream sstream]) -AC_CHECK_HEADERS([windows.h]) AC_HEADER_TIME AC_PATH_X AC_PATH_XTRA @@ -54,6 +61,7 @@ CPPFLAGS="$save_CPPFLAGS" dnl checks for types AC_TYPE_SIZE_T +ACX_CHECK_SOCKLEN_T dnl checks for structures AC_STRUCT_TM @@ -71,7 +79,6 @@ ACX_CHECK_CXX_STDLIB(,AC_MSG_ERROR(Your compiler must support the C++ standard l dnl checks for library functions dnl AC_TYPE_SIGNAL -AC_FUNC_FORK AC_FUNC_MEMCMP AC_FUNC_STRFTIME AC_CHECK_FUNCS(gmtime_r) @@ -86,8 +93,8 @@ dnl checks for system services dnl adjust variables for X11 and pthreads -CXXFLAGS="$CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" -LIBS="$NANOSLEEP_LIBS $PTHREAD_LIBS $LIBS" +CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" +LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" AC_OUTPUT([ Makefile diff --git a/lib/base/common.h b/lib/base/common.h index 9ba541bf..74b8f1d9 100644 --- a/lib/base/common.h +++ b/lib/base/common.h @@ -22,7 +22,7 @@ #endif // check if win32 platform -#if defined(_WIN32) || HAVE_WINDOWS_H +#if defined(_WIN32) # define WINDOWS_LIKE 1 // VC++ specific @@ -56,7 +56,7 @@ # define SIZE_OF_INT 4 # define SIZE_OF_LONG 4 # endif -#endif // defined(_WIN32) || HAVE_WINDOWS_H +#endif // defined(_WIN32) // unix-like if not like anything else #if (!defined(WINDOWS_LIKE) || WINDOWS_LIKE == 0) diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp index 37ff52cf..cf1c8276 100644 --- a/lib/mt/CThreadRep.cpp +++ b/lib/mt/CThreadRep.cpp @@ -548,8 +548,12 @@ CThreadRep::threadSignalHandler(void* vrep) // we exit the loop via thread cancellation in sigwait() for (;;) { // wait +#if HAVE_POSIX_SIGWAIT int signal; sigwait(&sigset, &signal); +#else + sigwait(&sigset); +#endif // if we get here then the signal was raised. cancel the thread. mainThreadRep->cancel(); diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp index e2184cd8..3c0f53f1 100644 --- a/lib/net/CNetwork.cpp +++ b/lib/net/CNetwork.cpp @@ -21,46 +21,46 @@ // CNetwork // -CNetwork::Socket (PASCAL FAR *CNetwork::accept)(CNetwork::Socket s, CNetwork::Address FAR *addr, CNetwork::AddressLength FAR *addrlen); -int (PASCAL FAR *CNetwork::bind)(CNetwork::Socket s, const CNetwork::Address FAR *addr, CNetwork::AddressLength namelen); -int (PASCAL FAR *CNetwork::close)(CNetwork::Socket s); -int (PASCAL FAR *CNetwork::connect)(CNetwork::Socket s, const CNetwork::Address FAR *name, CNetwork::AddressLength namelen); -int (PASCAL FAR *CNetwork::ioctl)(CNetwork::Socket s, int cmd, void FAR *); -int (PASCAL FAR *CNetwork::getpeername)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); -int (PASCAL FAR *CNetwork::getsockname)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); -int (PASCAL FAR *CNetwork::getsockopt)(CNetwork::Socket s, int level, int optname, void FAR * optval, CNetwork::AddressLength FAR *optlen); -int (PASCAL FAR *CNetwork::listen)(CNetwork::Socket s, int backlog); -ssize_t (PASCAL FAR *CNetwork::read)(CNetwork::Socket s, void FAR * buf, size_t len); -ssize_t (PASCAL FAR *CNetwork::recv)(CNetwork::Socket s, void FAR * buf, size_t len, int flags); -ssize_t (PASCAL FAR *CNetwork::recvfrom)(CNetwork::Socket s, void FAR * buf, size_t len, int flags, CNetwork::Address FAR *from, CNetwork::AddressLength FAR * fromlen); -int (PASCAL FAR *CNetwork::poll)(CNetwork::PollEntry fds[], int nfds, int timeout); -ssize_t (PASCAL FAR *CNetwork::send)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags); -ssize_t (PASCAL FAR *CNetwork::sendto)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags, const CNetwork::Address FAR *to, CNetwork::AddressLength tolen); -int (PASCAL FAR *CNetwork::setsockopt)(CNetwork::Socket s, int level, int optname, const void FAR * optval, CNetwork::AddressLength optlen); -int (PASCAL FAR *CNetwork::shutdown)(CNetwork::Socket s, int how); -CNetwork::Socket (PASCAL FAR *CNetwork::socket)(int af, int type, int protocol); -ssize_t (PASCAL FAR *CNetwork::write)(CNetwork::Socket s, const void FAR * buf, size_t len); -int (PASCAL FAR *CNetwork::gethostname)(char FAR * name, int namelen); -int (PASCAL FAR *CNetwork::getsockerror)(void); - #if WINDOWS_LIKE -int (PASCAL FAR *CNetwork::select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); -int (PASCAL FAR *CNetwork::WSACleanup)(void); -int (PASCAL FAR *CNetwork::__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); -char FAR * (PASCAL FAR *CNetwork::inet_ntoa_n)(struct in_addr in); -unsigned long (PASCAL FAR *CNetwork::inet_addr_n)(const char FAR * cp); -struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyaddr_n)(const char FAR * addr, int len, int type); -struct hostent FAR * (PASCAL FAR *CNetwork::gethostbyname_n)(const char FAR * name); -struct servent FAR * (PASCAL FAR *CNetwork::getservbyport_n)(int port, const char FAR * proto); -struct servent FAR * (PASCAL FAR *CNetwork::getservbyname_n)(const char FAR * name, const char FAR * proto); -struct protoent FAR * (PASCAL FAR *CNetwork::getprotobynumber_n)(int proto); -struct protoent FAR * (PASCAL FAR *CNetwork::getprotobyname_n)(const char FAR * name); +static CNetwork::Socket (PASCAL FAR *accept_winsock)(CNetwork::Socket s, CNetwork::Address FAR *addr, CNetwork::AddressLength FAR *addrlen); +static int (PASCAL FAR *bind_winsock)(CNetwork::Socket s, const CNetwork::Address FAR *addr, CNetwork::AddressLength namelen); +static int (PASCAL FAR *close_winsock)(CNetwork::Socket s); +static int (PASCAL FAR *connect_winsock)(CNetwork::Socket s, const CNetwork::Address FAR *name, CNetwork::AddressLength namelen); +static int (PASCAL FAR *gethostname_winsock)(char FAR * name, int namelen); +static int (PASCAL FAR *getpeername_winsock)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); +static int (PASCAL FAR *getsockerror_winsock)(void); +static int (PASCAL FAR *getsockname_winsock)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); +static int (PASCAL FAR *getsockopt_winsock)(CNetwork::Socket s, int level, int optname, void FAR * optval, CNetwork::AddressLength FAR *optlen); +static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in); +static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp); +static int (PASCAL FAR *ioctl_winsock)(CNetwork::Socket s, int cmd, void FAR *); +static int (PASCAL FAR *listen_winsock)(CNetwork::Socket s, int backlog); +static ssize_t (PASCAL FAR *recv_winsock)(CNetwork::Socket s, void FAR * buf, size_t len, int flags); +static ssize_t (PASCAL FAR *recvfrom_winsock)(CNetwork::Socket s, void FAR * buf, size_t len, int flags, CNetwork::Address FAR *from, CNetwork::AddressLength FAR * fromlen); +static int (PASCAL FAR *select_winsock)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); +static ssize_t (PASCAL FAR *send_winsock)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags); +static ssize_t (PASCAL FAR *sendto_winsock)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags, const CNetwork::Address FAR *to, CNetwork::AddressLength tolen); +static int (PASCAL FAR *setsockopt_winsock)(CNetwork::Socket s, int level, int optname, const void FAR * optval, CNetwork::AddressLength optlen); +static int (PASCAL FAR *shutdown_winsock)(CNetwork::Socket s, int how); +static CNetwork::Socket (PASCAL FAR *socket_winsock)(int af, int type, int protocol); +static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type); +static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); +static struct servent FAR * (PASCAL FAR *getservbyport_winsock)(int port, const char FAR * proto); +static struct servent FAR * (PASCAL FAR *getservbyname_winsock)(const char FAR * name, const char FAR * proto); +static struct protoent FAR * (PASCAL FAR *getprotobynumber_winsock)(int proto); +static struct protoent FAR * (PASCAL FAR *getprotobyname_winsock)(const char FAR * name); +static int (PASCAL FAR *WSACleanup_winsock)(void); +static int (PASCAL FAR *WSAFDIsSet_winsock)(CNetwork::Socket, fd_set FAR *); + const int CNetwork::Error = SOCKET_ERROR; -const CNetwork::Socket CNetwork::Null = INVALID_SOCKET; +const CNetwork::Socket CNetwork::Null = INVALID_SOCKET; #undef FD_ISSET -#define FD_ISSET(fd, set) CNetwork::__WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set)) +#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) + +// have poll() pick up our select() pointer +#define select select_winsock static HMODULE s_networkModule = NULL; @@ -78,8 +78,8 @@ netGetProcAddress(HMODULE module, LPCSTR name) void CNetwork::init() { - assert(WSACleanup == NULL); - assert(s_networkModule == NULL); + assert(WSACleanup_winsock == NULL); + assert(s_networkModule == NULL); // try winsock 2 HMODULE module = (HMODULE)::LoadLibrary("ws2_32.dll"); @@ -119,11 +119,11 @@ void CNetwork::cleanup() { if (s_networkModule != NULL) { - WSACleanup(); + WSACleanup_winsock(); ::FreeLibrary(s_networkModule); - WSACleanup = NULL; - s_networkModule = NULL; + WSACleanup_winsock = NULL; + s_networkModule = NULL; } } @@ -191,61 +191,100 @@ CNetwork::init2( } // get function addresses - setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); - setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); - setfunc(close, closesocket, int (PASCAL FAR *)(Socket s)); - setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); - setfunc(ioctl, ioctlsocket, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); - setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); - setfunc(inet_addr_n, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); - setfunc(inet_ntoa_n, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); - setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); - setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); - setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); - setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); - setfunc(sendto, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); - setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); - setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); - setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); - setfunc(gethostname, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); - setfunc(gethostbyaddr_n, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); - setfunc(gethostbyname_n, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(getservbyport_n, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); - setfunc(getservbyname_n, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); - setfunc(getprotobynumber_n, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); - setfunc(getprotobyname_n, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(getsockerror, WSAGetLastError, int (PASCAL FAR *)(void)); - setfunc(WSACleanup, WSACleanup, int (PASCAL FAR *)(void)); - setfunc(__WSAFDIsSet, __WSAFDIsSet, int (PASCAL FAR *)(CNetwork::Socket, fd_set FAR *)); - setfunc(select, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); - poll = poll2; - read = read2; - write = write2; + setfunc(accept_winsock, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); + setfunc(bind_winsock, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); + setfunc(close_winsock, closesocket, int (PASCAL FAR *)(Socket s)); + setfunc(connect_winsock, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); + setfunc(gethostname_winsock, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); + setfunc(getpeername_winsock, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockerror_winsock, WSAGetLastError, int (PASCAL FAR *)(void)); + setfunc(getsockname_winsock, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockopt_winsock, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); + setfunc(inet_addr_winsock, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(inet_ntoa_winsock, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(ioctl_winsock, ioctlsocket, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); + setfunc(listen_winsock, listen, int (PASCAL FAR *)(Socket s, int backlog)); + setfunc(recv_winsock, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); + setfunc(recvfrom_winsock, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); + setfunc(select_winsock, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); + setfunc(send_winsock, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); + setfunc(sendto_winsock, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); + setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); + setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(Socket s, int how)); + setfunc(socket,_winsock socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(getservbyport_winsock, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); + setfunc(getservbyname_winsock, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); + setfunc(getprotobynumber_winsock, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); + setfunc(getprotobyname_winsock, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void)); + setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(CNetwork::Socket, fd_set FAR *)); s_networkModule = module; } -ssize_t PASCAL FAR -CNetwork::read2(Socket s, void FAR* buf, size_t len) +CNetwork::Socket +CNetwork::accept(Socket s, Address* addr, AddressLength* addrlen) { - return recv(s, buf, len, 0); + return accept_winsock(s, addr, addrlen); } -ssize_t PASCAL FAR -CNetwork::write2(Socket s, const void FAR* buf, size_t len) +int +CNetwork::bind(Socket s, const Address* addr, AddressLength namelen) { - return send(s, buf, len, 0); + return bind_winsock(s, addr, namelen); } -int PASCAL FAR -CNetwork::inet_aton(const char FAR * cp, InternetAddress FAR * addr) +int +CNetwork::close(Socket s) +{ + return close_winsock(s); +} + +int +CNetwork::connect(Socket s, const Address* name, AddressLength namelen) +{ + return connect_winsock(s); +} + +int +CNetwork::gethostname(char* name, int namelen) +{ + return gethostname_winsock(name, namelen); +} + +int +CNetwork::getpeername(Socket s, Address* name, AddressLength* namelen) +{ + return getpeername_winsock(s, name, namelen); +} + +int +CNetwork::getsockerror(void) +{ + return getsockerrror_winsock(); +} + +int +CNetwork::getsockname(Socket s, Address* name, AddressLength* namelen) +{ + return getsockname_winsock(s, name, namelen); +} + +int +CNetwork::getsockopt(Socket s, int level, int optname, void* optval, AddressLength* optlen) +{ + return getsockopt_winsock(s, level, optname, optval, optlen); +} + +int +CNetwork::inet_aton(const char* cp, InternetAddress* addr) { assert(addr != NULL); - // fake it with inet_addr - unsigned long inetAddr = inet_addr_n(cp); + // fake it with inet_addr, which is per-thread on winsock + unsigned long inetAddr = inet_addr_winsock(cp); if (inetAddr == INADDR_NONE) { return 0; } @@ -255,21 +294,86 @@ CNetwork::inet_aton(const char FAR * cp, InternetAddress FAR * addr) } } -CString PASCAL FAR +CString CNetwork::inet_ntoa(struct in_addr in) { // winsock returns strings per-thread - return CString(inet_ntoa_n(in)); + return CString(inet_ntoa_winsock(in)); } -int PASCAL FAR -CNetwork::gethostbyaddr(CHostInfo* hostinfo, - const char FAR * addr, int len, int type) +int +CNetwork::ioctl(Socket s, int cmd, void* arg) +{ + return ioctl_winsock(s, cmd, arg); +} + +int +CNetwork::listen(Socket s, int backlog) +{ + return listen_winsock(s, backlog); +} + +ssize_t +CNetwork::read(Socket s, void* buf, size_t len) +{ + return recv_winsock(s, buf, len, 0); +} + +ssize_t +CNetwork::recv(Socket s, void* buf, size_t len, int flags) +{ + return recv_winsock(s, buf, len, flags); +} + +ssize_t +CNetwork::recvfrom(Socket s, void* buf, size_t len, int flags, Address* from, AddressLength* fromlen) +{ + return recvfrom_winsock(s, buf, len, flags, from, fromlen); +} + +ssize_t +CNetwork::send(Socket s, const void* buf, size_t len, int flags) +{ + return send_winsock(s, buf, len, flags); +} + +ssize_t +CNetwork::sendto(Socket s, const void* buf, size_t len, int flags, const Address* to, AddressLength tolen) +{ + return sendto_winsock(s, buf, len, flags, to, tolen); +} + +int +CNetwork::setsockopt(Socket s, int level, int optname, const void* optval, AddressLength optlen) +{ + return setsockopt_winsock(s, level, optname, optval, optlen); +} + +int +CNetwork::shutdown(Socket s, int how) +{ + return shutdown_winsock(s, how); +} + +CNetwork::Socket +CNetwork::socket(int af, int type, int protocol) +{ + return socket_winsock(af, type, protocol); +} + +ssize_t +CNetwork::write(Socket s, const void* buf, size_t len) +{ + return send_winsock(s, buf, len, 0); +} + +int +CNetwork::gethostbyaddr(CHostInfo* hostinfo, const char* addr, int len, int type) { assert(hostinfo != NULL); // winsock returns structures per-thread - struct hostent FAR* info = gethostbyaddr_n(addr, len, type); + struct hostent FAR* info = gethostbyaddr_winsock(addr, len, type); if (info == NULL) { return getsockerror(); } @@ -280,14 +384,13 @@ CNetwork::gethostbyaddr(CHostInfo* hostinfo, } } -int PASCAL FAR -CNetwork::gethostbyname(CHostInfo* hostinfo, - const char FAR * name) +int +CNetwork::gethostbyname(CHostInfo* hostinfo, const char* name) { assert(hostinfo != NULL); // winsock returns structures per-thread - struct hostent FAR* info = gethostbyname_n(name); + struct hostent FAR* info = gethostbyname_winsock(name); if (info == NULL) { return getsockerror(); } @@ -298,14 +401,13 @@ CNetwork::gethostbyname(CHostInfo* hostinfo, } } -int PASCAL FAR -CNetwork::getservbyport(CServiceInfo* servinfo, - int port, const char FAR * proto) +int +CNetwork::getservbyport(CServiceInfo* servinfo, int port, const char* proto) { assert(servinfo != NULL); // winsock returns structures per-thread - struct servent FAR* info = getservbyport_n(port, proto); + struct servent FAR* info = getservbyport_winsock(port, proto); if (info == NULL) { return getsockerror(); } @@ -316,14 +418,13 @@ CNetwork::getservbyport(CServiceInfo* servinfo, } } -int PASCAL FAR -CNetwork::getservbyname(CServiceInfo* servinfo, - const char FAR * name, const char FAR * proto) +int +CNetwork::getservbyname(CServiceInfo* servinfo, const char* name, const char* proto) { assert(servinfo != NULL); // winsock returns structures per-thread - struct servent FAR* info = getservbyname_n(name, proto); + struct servent FAR* info = getservbyname_winsock(name, proto); if (info == NULL) { return getsockerror(); } @@ -334,14 +435,13 @@ CNetwork::getservbyname(CServiceInfo* servinfo, } } -int PASCAL FAR -CNetwork::getprotobynumber(CProtocolInfo* protoinfo, - int proto) +int +CNetwork::getprotobynumber(CProtocolInfo* protoinfo, int proto) { assert(protoinfo != NULL); // winsock returns structures per-thread - struct protoent FAR* info = getprotobynumber_n(proto); + struct protoent FAR* info = getprotobynumber_winsock(proto); if (info == NULL) { return getsockerror(); } @@ -352,14 +452,13 @@ CNetwork::getprotobynumber(CProtocolInfo* protoinfo, } } -int PASCAL FAR -CNetwork::getprotobyname(CProtocolInfo* protoinfo, - const char FAR * name) +int +CNetwork::getprotobyname(CProtocolInfo* protoinfo, const char* name) { assert(protoinfo != NULL); // winsock returns structures per-thread - struct protoent FAR* info = getprotobyname_n(name); + struct protoent FAR* info = getprotobyname_winsock(name); if (info == NULL) { return getsockerror(); } @@ -370,15 +469,15 @@ CNetwork::getprotobyname(CProtocolInfo* protoinfo, } } -int PASCAL FAR -CNetwork::setblocking(CNetwork::Socket s, bool blocking) +int +CNetwork::setblocking(Socket s, bool blocking) { int flag = blocking ? 0 : 1; return ioctl(s, FIONBIO, &flag); } -int PASCAL FAR -CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) +int +CNetwork::setnodelay(Socket s, bool nodelay) { BOOL flag = nodelay ? 1 : 0; return setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); @@ -391,25 +490,18 @@ CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) #include "CMutex.h" #include "CLock.h" -#if HAVE_SYS_TYPES_H -# include -#endif -#if HAVE_SYS_STAT_H -# include -#endif #if HAVE_UNISTD_H # include #endif #include #include -#include #include #if !defined(TCP_NODELAY) # include #endif const int CNetwork::Error = -1; -const CNetwork::Socket CNetwork::Null = -1; +const CNetwork::Socket CNetwork::Null = -1; static CMutex* s_networkMutex = NULL; @@ -450,32 +542,6 @@ CNetwork::init() catch (...) { throw XNetworkFailed(); } - - setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); - setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); - setfunc(close, close, int (PASCAL FAR *)(Socket s)); - setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); - setfunc(ioctl, ioctl, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); - setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); - setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); -#if HAVE_POLL - setfunc(poll, poll, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); -#else - setfunc(poll, CNetwork::poll2, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); -#endif - setfunc(read, read, ssize_t (PASCAL FAR *)(CNetwork::Socket s, void FAR * buf, size_t len)); - setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); - setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); - setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); - setfunc(sendto, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); - setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); - setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); - setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); - setfunc(write, write, ssize_t (PASCAL FAR *)(CNetwork::Socket s, const void FAR * buf, size_t len)); - gethostname = gethostname2; - getsockerror = getsockerror2; } void @@ -485,25 +551,67 @@ CNetwork::cleanup() s_networkMutex = NULL; } -int PASCAL FAR -CNetwork::gethostname2(char* name, int namelen) +CNetwork::Socket +CNetwork::accept(Socket s, Address* addr, AddressLength* addrlen) +{ + return ::accept(s, addr, addrlen); +} + +int +CNetwork::bind(Socket s, const Address* addr, AddressLength namelen) +{ + return ::bind(s, addr, namelen); +} + +int +CNetwork::close(Socket s) +{ + return ::close(s); +} + +int +CNetwork::connect(Socket s, const Address* name, AddressLength namelen) +{ + return ::connect(s, name, namelen); +} + +int +CNetwork::gethostname(char* name, int namelen) { return ::gethostname(name, namelen); } -int PASCAL FAR -CNetwork::getsockerror2(void) +int +CNetwork::getpeername(Socket s, Address* name, AddressLength* namelen) +{ + return ::getpeername(s, name, namelen); +} + +int +CNetwork::getsockerror(void) { return errno; } -int PASCAL FAR -CNetwork::inet_aton(const char FAR * cp, InternetAddress FAR * addr) +int +CNetwork::getsockname(Socket s, Address* name, AddressLength* namelen) +{ + return ::getsockname(s, name, namelen); +} + +int +CNetwork::getsockopt(Socket s, int level, int optname, void* optval, AddressLength* optlen) +{ + return ::getsockopt(s, level, optname, optval, optlen); +} + +int +CNetwork::inet_aton(const char* cp, InternetAddress* addr) { return ::inet_aton(cp, addr); } -CString PASCAL FAR +CString CNetwork::inet_ntoa(struct in_addr in) { // single threaded access to inet_ntoa functions @@ -511,15 +619,80 @@ CNetwork::inet_ntoa(struct in_addr in) return CString(::inet_ntoa(in)); } -int PASCAL FAR -CNetwork::gethostbyaddr(CHostInfo* hostinfo, - const char FAR * addr, int len, int type) +int +CNetwork::ioctl(Socket s, int cmd, void* arg) +{ + return ::ioctl(s, cmd, arg); +} + +int +CNetwork::listen(Socket s, int backlog) +{ + return ::listen(s, backlog); +} + +ssize_t +CNetwork::read(Socket s, void* buf, size_t len) +{ + return ::read(s, buf, len); +} + +ssize_t +CNetwork::recv(Socket s, void* buf, size_t len, int flags) +{ + return ::recv(s, buf, len, flags); +} + +ssize_t +CNetwork::recvfrom(Socket s, void* buf, size_t len, int flags, Address* from, AddressLength* fromlen) +{ + return ::recvfrom(s, buf, len, flags, from, fromlen); +} + +ssize_t +CNetwork::send(Socket s, const void* buf, size_t len, int flags) +{ + return ::send(s, buf, len, flags); +} + +ssize_t +CNetwork::sendto(Socket s, const void* buf, size_t len, int flags, const Address* to, AddressLength tolen) +{ + return ::sendto(s, buf, len, flags, to, tolen); +} + +int +CNetwork::setsockopt(Socket s, int level, int optname, const void* optval, AddressLength optlen) +{ + return ::setsockopt(s, level, optname, optval, optlen); +} + +int +CNetwork::shutdown(Socket s, int how) +{ + return ::shutdown(s, how); +} + +CNetwork::Socket +CNetwork::socket(int af, int type, int protocol) +{ + return ::socket(af, type, protocol); +} + +ssize_t +CNetwork::write(Socket s, const void* buf, size_t len) +{ + return ::write(s, buf, len); +} + +int +CNetwork::gethostbyaddr(CHostInfo* hostinfo, const char* addr, int len, int type) { assert(hostinfo != NULL); // single threaded access to netdb functions CLock lock(s_networkMutex); - struct hostent FAR* info = ::gethostbyaddr(addr, len, type); + struct hostent* info = ::gethostbyaddr(addr, len, type); if (info == NULL) { return h_errno; } @@ -530,15 +703,14 @@ CNetwork::gethostbyaddr(CHostInfo* hostinfo, } } -int PASCAL FAR -CNetwork::gethostbyname(CHostInfo* hostinfo, - const char FAR * name) +int +CNetwork::gethostbyname(CHostInfo* hostinfo, const char* name) { assert(hostinfo != NULL); // single threaded access to netdb functions CLock lock(s_networkMutex); - struct hostent FAR* info = ::gethostbyname(name); + struct hostent* info = ::gethostbyname(name); if (info == NULL) { return h_errno; } @@ -549,15 +721,14 @@ CNetwork::gethostbyname(CHostInfo* hostinfo, } } -int PASCAL FAR -CNetwork::getservbyport(CServiceInfo* servinfo, - int port, const char FAR * proto) +int +CNetwork::getservbyport(CServiceInfo* servinfo, int port, const char* proto) { assert(servinfo != NULL); // single threaded access to netdb functions CLock lock(s_networkMutex); - struct servent FAR* info = ::getservbyport(port, proto); + struct servent* info = ::getservbyport(port, proto); if (info == NULL) { return -1; } @@ -568,15 +739,14 @@ CNetwork::getservbyport(CServiceInfo* servinfo, } } -int PASCAL FAR -CNetwork::getservbyname(CServiceInfo* servinfo, - const char FAR * name, const char FAR * proto) +int +CNetwork::getservbyname(CServiceInfo* servinfo, const char* name, const char* proto) { assert(servinfo != NULL); // single threaded access to netdb functions CLock lock(s_networkMutex); - struct servent FAR* info = ::getservbyname(name, proto); + struct servent* info = ::getservbyname(name, proto); if (info == NULL) { return -1; } @@ -587,15 +757,14 @@ CNetwork::getservbyname(CServiceInfo* servinfo, } } -int PASCAL FAR -CNetwork::getprotobynumber(CProtocolInfo* protoinfo, - int proto) +int +CNetwork::getprotobynumber(CProtocolInfo* protoinfo, int proto) { assert(protoinfo != NULL); // single threaded access to netdb functions CLock lock(s_networkMutex); - struct protoent FAR* info = ::getprotobynumber(proto); + struct protoent* info = ::getprotobynumber(proto); if (info == NULL) { return -1; } @@ -606,15 +775,14 @@ CNetwork::getprotobynumber(CProtocolInfo* protoinfo, } } -int PASCAL FAR -CNetwork::getprotobyname(CProtocolInfo* protoinfo, - const char FAR * name) +int +CNetwork::getprotobyname(CProtocolInfo* protoinfo, const char* name) { assert(protoinfo != NULL); // single threaded access to netdb functions CLock lock(s_networkMutex); - struct protoent FAR* info = ::getprotobyname(name); + struct protoent* info = ::getprotobyname(name); if (info == NULL) { return -1; } @@ -625,7 +793,7 @@ CNetwork::getprotobyname(CProtocolInfo* protoinfo, } } -int PASCAL FAR +int CNetwork::setblocking(CNetwork::Socket s, bool blocking) { int mode = fcntl(s, F_GETFL, 0); @@ -644,18 +812,26 @@ CNetwork::setblocking(CNetwork::Socket s, bool blocking) return 0; } -int PASCAL FAR +int CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) { int flag = nodelay ? 1 : 0; - setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); + return setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); } #endif -#if WINDOWS_LIKE || !HAVE_POLL +#if HAVE_POLL -#if HAVE_SYS_SELECT_H +int +CNetwork::poll(PollEntry fd[], int nfds, int timeout) +{ + return ::poll(fd, nfds, timeout); +} + +#else // !HAVE_POLL + +#if HAVE_SYS_SELECT_H && !WINDOWS_LIKE # include #endif #if HAVE_SYS_TIME_H @@ -668,8 +844,8 @@ CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) # include #endif -int PASCAL FAR -CNetwork::poll2(PollEntry fd[], int nfds, int timeout) +int +CNetwork::poll(PollEntry fd[], int nfds, int timeout) { int i; @@ -752,7 +928,7 @@ CNetwork::poll2(PollEntry fd[], int nfds, int timeout) return n; } -#endif +#endif // !HAVE_POLL // diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h index eacff229..80873a55 100644 --- a/lib/net/CNetwork.h +++ b/lib/net/CNetwork.h @@ -19,37 +19,38 @@ #include "CString.h" #include "stdvector.h" +#if !defined(HAVE_SOCKLEN_T) // Darwin is so unsure what to use for socklen_t it makes us choose -#if defined(__APPLE__) -# if !defined(_BSD_SOCKLEN_T_) -# define _BSD_SOCKLEN_T_ int +# if defined(__APPLE__) +# if !defined(_BSD_SOCKLEN_T_) +# define _BSD_SOCKLEN_T_ int +# endif +# else +typedef int socklen_t; # endif #endif -#if HAVE_SYS_TYPES_H -# include -#endif -#if HAVE_SYS_SOCKET_H -# include -#endif -#if HAVE_POLL -# include -#endif - #if WINDOWS_LIKE // declare no functions in winsock2 # define INCL_WINSOCK_API_PROTOTYPES 0 # define INCL_WINSOCK_API_TYPEDEFS 0 # include typedef int ssize_t; -#else -# undef FAR -# undef PASCAL -# define FAR -# define PASCAL +# define SELECT_TYPE_ARG1 int +# define SELECT_TYPE_ARG234 (fd_set *) +# define SELECT_TYPE_ARG5 (struct timeval *) #endif #if UNIX_LIKE +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_SYS_SOCKET_H +# include +# endif +# if HAVE_POLL +# include +# endif # include # include # include @@ -73,7 +74,7 @@ public: typedef struct in_addr InternetAddress; #endif -#if WINDOWS_LIKE || !HAVE_POLL +#if !HAVE_POLL class PollEntry { public: Socket fd; @@ -220,95 +221,51 @@ public: // socket interface (only available after init()) - static Socket (PASCAL FAR *accept)(Socket s, Address FAR *addr, AddressLength FAR *addrlen); - static int (PASCAL FAR *bind)(Socket s, const Address FAR *addr, AddressLength namelen); - static int (PASCAL FAR *close)(Socket s); - static int (PASCAL FAR *connect)(Socket s, const Address FAR *name, AddressLength namelen); - static int (PASCAL FAR *ioctl)(Socket s, int cmd, void FAR *); - static int (PASCAL FAR *getpeername)(Socket s, Address FAR *name, AddressLength FAR * namelen); - static int (PASCAL FAR *getsockname)(Socket s, Address FAR *name, AddressLength FAR * namelen); - static int (PASCAL FAR *getsockopt)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen); - static int PASCAL FAR inet_aton(const char FAR * cp, InternetAddress FAR * addr); - static CString PASCAL FAR inet_ntoa(struct in_addr in); - static int (PASCAL FAR *listen)(Socket s, int backlog); - static ssize_t (PASCAL FAR *read)(Socket s, void FAR * buf, size_t len); - static ssize_t (PASCAL FAR *recv)(Socket s, void FAR * buf, size_t len, int flags); - static ssize_t (PASCAL FAR *recvfrom)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen); - static int (PASCAL FAR *poll)(PollEntry[], int nfds, int timeout); - static ssize_t (PASCAL FAR *send)(Socket s, const void FAR * buf, size_t len, int flags); - static ssize_t (PASCAL FAR *sendto)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen); - static int (PASCAL FAR *setsockopt)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen); - static int (PASCAL FAR *shutdown)(Socket s, int how); - static Socket (PASCAL FAR *socket)(int af, int type, int protocol); - static ssize_t (PASCAL FAR *write)(Socket s, const void FAR * buf, size_t len); - static int (PASCAL FAR *gethostname)(char FAR * name, int namelen); - static int PASCAL FAR gethostbyaddr(CHostInfo* hostinfo, const char FAR * addr, int len, int type); - static int PASCAL FAR gethostbyname(CHostInfo* hostinfo, const char FAR * name); - static int PASCAL FAR getservbyport(CServiceInfo* servinfo, int port, const char FAR * proto); - static int PASCAL FAR getservbyname(CServiceInfo* servinfo, const char FAR * name, const char FAR * proto); - static int PASCAL FAR getprotobynumber(CProtocolInfo* protoinfo, int proto); - static int PASCAL FAR getprotobyname(CProtocolInfo* protoinfo, const char FAR * name); - static int (PASCAL FAR *getsockerror)(void); + static Socket accept(Socket s, Address* addr, AddressLength* addrlen); + static int bind(Socket s, const Address* addr, AddressLength namelen); + static int close(Socket s); + static int connect(Socket s, const Address* name, AddressLength namelen); + static int gethostname(char* name, int namelen); + static int getpeername(Socket s, Address* name, AddressLength* namelen); + static int getsockerror(void); + static int getsockname(Socket s, Address* name, AddressLength* namelen); + static int getsockopt(Socket s, int level, int optname, void* optval, AddressLength* optlen); + static int inet_aton(const char* cp, InternetAddress* addr); + static CString inet_ntoa(struct in_addr in); + static int ioctl(Socket s, int cmd, void* ); + static int listen(Socket s, int backlog); + static int poll(PollEntry[], int nfds, int timeout); + static ssize_t read(Socket s, void* buf, size_t len); + static ssize_t recv(Socket s, void* buf, size_t len, int flags); + static ssize_t recvfrom(Socket s, void* buf, size_t len, int flags, Address* from, AddressLength* fromlen); + static ssize_t send(Socket s, const void* buf, size_t len, int flags); + static ssize_t sendto(Socket s, const void* buf, size_t len, int flags, const Address* to, AddressLength tolen); + static int setsockopt(Socket s, int level, int optname, const void* optval, AddressLength optlen); + static int shutdown(Socket s, int how); + static Socket socket(int af, int type, int protocol); + static ssize_t write(Socket s, const void* buf, size_t len); + static int gethostbyaddr(CHostInfo* hostinfo, const char* addr, int len, int type); + static int gethostbyname(CHostInfo* hostinfo, const char* name); + static int getservbyport(CServiceInfo* servinfo, int port, const char* proto); + static int getservbyname(CServiceInfo* servinfo, const char* name, const char* proto); + static int getprotobynumber(CProtocolInfo* protoinfo, int proto); + static int getprotobyname(CProtocolInfo* protoinfo, const char* name); // convenience functions (only available after init()) //! Set socket to (non-)blocking operation - static int PASCAL FAR setblocking(CNetwork::Socket s, bool blocking); + static int setblocking(Socket s, bool blocking); //! Turn Nagle algorithm on or off on socket /*! Set socket to send messages immediately (true) or to collect small messages into one packet (false). */ - static int PASCAL FAR setnodelay(CNetwork::Socket s, bool nodelay); + static int setnodelay(Socket s, bool nodelay); private: #if WINDOWS_LIKE -#define SELECT_TYPE_ARG1 int -#define SELECT_TYPE_ARG234 (fd_set *) -#define SELECT_TYPE_ARG5 (struct timeval *) static void init2(HMODULE); - static ssize_t PASCAL FAR read2(Socket s, void FAR * buf, size_t len); - static ssize_t PASCAL FAR write2(Socket s, const void FAR * buf, size_t len); - static int (PASCAL FAR *WSACleanup)(void); - static int (PASCAL FAR *__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); - static int (PASCAL FAR *select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); - static char FAR * (PASCAL FAR *inet_ntoa_n)(struct in_addr in); - static unsigned long (PASCAL FAR *inet_addr_n)(const char FAR * cp); - static struct hostent FAR * (PASCAL FAR *gethostbyaddr_n)(const char FAR * addr, int len, int type); - static struct hostent FAR * (PASCAL FAR *gethostbyname_n)(const char FAR * name); - static struct servent FAR * (PASCAL FAR *getservbyport_n)(int port, const char FAR * proto); - static struct servent FAR * (PASCAL FAR *getservbyname_n)(const char FAR * name, const char FAR * proto); - static struct protoent FAR * (PASCAL FAR *getprotobynumber_n)(int proto); - static struct protoent FAR * (PASCAL FAR *getprotobyname_n)(const char FAR * name); -#endif - -#if UNIX_LIKE - static int PASCAL FAR gethostname2(char FAR * name, int namelen); - static int PASCAL FAR getsockerror2(void); -#endif - -#if WINDOWS_LIKE || UNIX_LIKE -/* FIXME -- reentrant netdb stuff -create classes for hostent, servent, protoent. -each class can clean itself up automatically. -inside CNetwork we'll convert from netdb structs to classes. -clients will pass a class pointer which CNetwork will assign to (or swap into). -won't need free...() functions to clean up structs. -each class should know how to copy from respective netdb struct. -will need to fix CNetworkAddress to use classes. -*/ - static void copyhostent(struct hostent FAR * dst, const struct hostent FAR * src); - static void copyservent(struct servent FAR * dst, const struct servent FAR * src); - static void copyprotoent(struct protoent FAR * dst, const struct protoent FAR * src); - - static void freehostent(struct hostent FAR * ent); - static void freeservent(struct servent FAR * ent); - static void freeprotoent(struct protoent FAR * ent); -#endif - -#if WINDOWS_LIKE || !HAVE_POLL - static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout); #endif }; diff --git a/lib/platform/CUnixPlatform.cpp b/lib/platform/CUnixPlatform.cpp index feed3318..02848633 100644 --- a/lib/platform/CUnixPlatform.cpp +++ b/lib/platform/CUnixPlatform.cpp @@ -55,7 +55,6 @@ CUnixPlatform::uninstallDaemon(const char*, bool) int CUnixPlatform::daemonize(const char* name, DaemonFunc func) { -#if HAVE_WORKING_FORK // fork so shell thinks we're done and so we're not a process // group leader switch (fork()) { @@ -71,7 +70,6 @@ CUnixPlatform::daemonize(const char* name, DaemonFunc func) // parent exits exit(0); } -#endif // become leader of a new session setsid(); From 41dc621579f9923b9c03105fcf966cfcc8efb37d Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 25 Dec 2002 18:42:39 +0000 Subject: [PATCH 398/807] Fixed typos. --- lib/net/CNetwork.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp index 3c0f53f1..e473b933 100644 --- a/lib/net/CNetwork.cpp +++ b/lib/net/CNetwork.cpp @@ -211,7 +211,7 @@ CNetwork::init2( setfunc(sendto_winsock, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(Socket s, int how)); - setfunc(socket,_winsock socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(socket_winsock, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); setfunc(getservbyport_winsock, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); @@ -245,7 +245,7 @@ CNetwork::close(Socket s) int CNetwork::connect(Socket s, const Address* name, AddressLength namelen) { - return connect_winsock(s); + return connect_winsock(s, name, namelen); } int @@ -263,7 +263,7 @@ CNetwork::getpeername(Socket s, Address* name, AddressLength* namelen) int CNetwork::getsockerror(void) { - return getsockerrror_winsock(); + return getsockerror_winsock(); } int From fd5625ba99a6e13d005313e46802cbea46a6be09 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 25 Dec 2002 18:44:54 +0000 Subject: [PATCH 399/807] Improved handling of screen saver handling when windows 2k is the client and the screen saver is password protected. It used to immediately turn off the screen saver (unintentionally) in that case. --- lib/platform/CMSWindowsScreen.cpp | 9 ++++++++- lib/platform/CMSWindowsScreenSaver.cpp | 10 +++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 192d05bc..1b8e74c7 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -473,9 +473,16 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) if (isCurrentDesktop(desk)) { CloseDesktop(desk); } - else { + else if (!m_screensaver->isActive()) { + // don't switch desktops when the screensaver is + // active. we'd most likely switch to the + // screensaver desktop which would have the side + // effect of forcing the screensaver to stop. switchDesktop(desk); } + else { + CloseDesktop(desk); + } } } return true; diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index 7ee439d7..df3322a7 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -132,7 +132,7 @@ CMSWindowsScreenSaver::deactivate() bool killed = false; if (!m_is95Family) { // NT runs screen saver in another desktop - HDESK desktop = OpenDesktop("screen-saver", 0, FALSE, + HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS); if (desktop != NULL) { EnumDesktopWindows(desktop, @@ -146,6 +146,10 @@ CMSWindowsScreenSaver::deactivate() if (!killed) { // find screen saver window and close it HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL); + if (hwnd == NULL) { + // win2k may use a different class + hwnd = FindWindow("Default Screen Saver", NULL); + } if (hwnd != NULL) { PostMessage(hwnd, WM_CLOSE, 0, 0); } @@ -167,7 +171,7 @@ CMSWindowsScreenSaver::isActive() const } else if (m_isNT) { // screen saver runs on a separate desktop - HDESK desktop = OpenDesktop("screen-saver", 0, FALSE, MAXIMUM_ALLOWED); + HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, MAXIMUM_ALLOWED); if (desktop == NULL && GetLastError() != ERROR_ACCESS_DENIED) { // desktop doesn't exist so screen saver is not running return false; @@ -241,7 +245,7 @@ CMSWindowsScreenSaver::findScreenSaver() { if (!m_is95Family) { // screen saver runs on a separate desktop - HDESK desktop = OpenDesktop("screen-saver", 0, FALSE, MAXIMUM_ALLOWED); + HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, MAXIMUM_ALLOWED); if (desktop != NULL) { // search CFindScreenSaverInfo info; From d2a871d0d97444b27d1e0bd124d4d7c2bbf04e22 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 25 Dec 2002 19:20:12 +0000 Subject: [PATCH 400/807] Added ability to filter out filespec prefixes and removed @... from user on each change. --- nodist/p4tolog | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/nodist/p4tolog b/nodist/p4tolog index 476dffaa..561a25a3 100755 --- a/nodist/p4tolog +++ b/nodist/p4tolog @@ -8,6 +8,21 @@ # location of perforce client P4=/home/perforce/bin/p4 +prefix="" +while [ -n "$1" ]; do + case "$1" in + -p) + # get depot filespec prefix to strip and escape slashes + prefix=`echo $2 | sed -e 's#/#\\\\/#g'` + shift + ;; + *) + break + ;; + esac + shift +done + # get relevant changes changes=`$P4 changes $* | sed -e 's/Change \([0-9]*\).*/\1/'` if test -z "$changes"; then @@ -18,10 +33,10 @@ fi # convert each change for change in $changes; do $P4 describe -s $change | head -1 | \ - sed -e 's/.*by \([^ ]*\) on \([^ ]*\) \([^ ]*\)/\2 \3 \1/' + sed -e 's/.*by \([^ @]*\)[^ ]* on \([^ ]*\) \([^ ]*\)/\2 \3 \1/' $P4 describe -s $change | \ awk 'p==1 && !/^$/;/^Affected/{p=1}' | \ - sed -e 's/^[^ ]* \([^#]*\)#.*$/\1/' + sed -e 's/^[^ ]* \([^#]*\)#.*$/\1/' | sed -e "s/^$prefix//" echo $P4 describe -s $change | \ awk 'p==1 && !/$^/;/^$/{if (p==1) exit; else p=1}' | \ From 5220314c4f67b70b5ac6ccd167939eae388e2f65 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 25 Dec 2002 19:21:17 +0000 Subject: [PATCH 401/807] Changed version number to 0.9.14. Added NEWS item. --- NEWS | 15 +++++++++++++++ configure.in | 2 +- lib/base/Version.h | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 38eb3be5..44e68298 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,21 @@ Synergy News ============ +* Dec-25-2002 - Synergy 0.9.14 released + + Made following changes: + * Fixed solaris compile problems (untested) + * Fixed irix compile problems (untested) + * Fixed windows client not reconnecting when server dies bug + * Fixed loss of ctrl+alt from windows server to non-windows clients + * Fixed handling of password protected windows client screen saver + * Now handling any number of pointer buttons on X11 + * Toggle key states now restored when leaving clients + * Added support for per-screen config options + * Added config options for half-duplex toggle keys on X11 + * Enabled class diagrams in doxygen documentation + + * Nov-05-2002 - Synergy 0.9.13 released Made following changes: diff --git a/configure.in b/configure.in index 1d576dca..6064df92 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=0 MINOR_VERSION=9 -RELEASE_VERSION=13 +RELEASE_VERSION=14 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/base/Version.h b/lib/base/Version.h index 9374a3f1..ebc57b63 100644 --- a/lib/base/Version.h +++ b/lib/base/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "0.9.13" +# define VERSION "0.9.14" #endif // important strings From 435cc11d6261fe97adc2c51986ce34e92dec70b1 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 25 Dec 2002 19:22:03 +0000 Subject: [PATCH 402/807] Added 0.9.14 log entries. --- ChangeLog | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/ChangeLog b/ChangeLog index 694f93d8..e8025a37 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,151 @@ +2002/12/25 19:21:17 crs +NEWS +configure.in +lib/base/Version.h + +Changed version number to 0.9.14. Added NEWS item. + +---------- +2002/12/25 18:44:54 crs +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreenSaver.cpp + +Improved handling of screen saver handling when windows 2k is +the client and the screen saver is password protected. It used +to immediately turn off the screen saver (unintentionally) in +that case. + +---------- +2002/12/25 10:35:59 crs +acinclude.m4 +config/config.guess +config/config.sub +configure.in +lib/base/common.h +lib/mt/CThreadRep.cpp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/platform/CUnixPlatform.cpp + +Changes to support building on solaris, irix, and darwin. Also +removed test for working fork (AC_FORK). + +---------- +2002/12/23 14:47:44 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h + +Added code to process set/reset options messages from server. + +---------- +2002/12/23 13:55:21 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CMSWindowsPrimaryScreen.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CPrimaryScreen.cpp +lib/server/CPrimaryScreen.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/synergy/CProtocolUtil.cpp +lib/synergy/CProtocolUtil.h +lib/synergy/IClient.h +lib/synergy/Makefile.am +lib/synergy/OptionTypes.h +lib/synergy/ProtocolTypes.h + +Added support for per-screen options in the configuration file +and sending those options to the appropriate client screens. +Currently, two options are supported: halfDuplexCapsLock and +halfDuplexNumLock mark the caps lock and num lock keys, +respectively, as being half-duplex. + +---------- +2002/12/22 14:51:41 crs +configure.in +doc/doxygen.cfg.in + +Doxygen config file now sets HAVE_DOT to YES only if dot is found +by configure. + +---------- +2002/12/15 22:39:59 crs +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h + +Now handling any number of pointer buttons. + +---------- +2002/12/15 22:17:18 crs +lib/server/CXWindowsPrimaryScreen.cpp + +Now ignoring half-duplex keys that are down when deciding if +the mouse is locked to the screen. We can't tell if a half- +duplex key is physically down and logically down just means +it's active so there's no point in letting it lock the mouse +to the screen. + +---------- +2002/12/15 22:14:49 crs +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h + +Now restoring toggle key states on leaving a client screen to +their state when the screen was entered. Previously when +leaving a client screen the toggle keys kept their state so, +say, caps lock, would remain on. This was inconvenient if +you then used the client's keyboard directly. + +---------- +2002/12/15 20:00:52 crs +lib/server/CMSWindowsPrimaryScreen.cpp + +Fixed loss of ctrl+alt when transmitted to non-windows platforms +from a windows server. Was converting ctrl+alt on windows to +mode switch on the server. No longer doing that; windows clients +will interpret ctrl+alt as AltGr and other clients will just see +ctrl+alt. Also made the right alt key mode switch on windows +servers in case the user wants to force a mode switch, but that +means the right alt key no longer acts as alt on clients. + +---------- +2002/12/15 19:58:41 crs +lib/platform/CMSWindowsScreen.cpp + +Fixed client not reconnecting when server dies bug. + +---------- +2002/12/15 19:57:28 crs +lib/client/CXWindowsSecondaryScreen.cpp + +Cleanup and changed some DEBUG1 messages to DEBUG2. + +---------- +2002/12/15 11:12:39 crs +doc/doxygen.cfg.in + +Enabled using dot and class diagrams. + +---------- 2002/11/05 19:23:05 crs lib/platform/CXWindowsScreen.cpp lib/platform/CXWindowsScreenSaver.cpp From 22457c78fe7cefbe71f79cc270c4bbb8dfcc3a4c Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 25 Dec 2002 22:56:09 +0000 Subject: [PATCH 403/807] Made synrgyhk.dll error messages less cryptic. --- lib/server/CMSWindowsPrimaryScreen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index 6a28a092..3e43ca51 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -40,7 +40,7 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( // load the hook library m_hookLibrary = LoadLibrary("synrgyhk"); if (m_hookLibrary == NULL) { - LOG((CLOG_ERR "failed to load hook library")); + LOG((CLOG_ERR "Failed to load hook library; synrgyhk.dll is missing")); throw XScreenOpenFailure(); } m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides"); @@ -57,7 +57,7 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( m_uninstall == NULL || m_init == NULL || m_cleanup == NULL) { - LOG((CLOG_ERR "invalid hook library")); + LOG((CLOG_ERR "Invalid hook library; use a newer synrgyhk.dll")); FreeLibrary(m_hookLibrary); throw XScreenOpenFailure(); } @@ -425,7 +425,7 @@ CMSWindowsPrimaryScreen::onPreOpen() // initialize hook library m_threadID = GetCurrentThreadId(); if (m_init(m_threadID) == 0) { - LOG((CLOG_ERR "cannot initialize hook library")); + LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?")); throw XScreenOpenFailure(); } } From 6ec79dbddf3fa5362190f1dcc8565bef79453897 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 25 Dec 2002 23:49:42 +0000 Subject: [PATCH 404/807] Documentation update. --- BUGS | 97 +++--------------------------------------------- FAQ | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- INSTALL | 92 +++++++++++++++++++++++---------------------- README | 21 ++++++----- TODO | 2 - 5 files changed, 176 insertions(+), 149 deletions(-) diff --git a/BUGS b/BUGS index 854f27e8..7cbc4373 100644 --- a/BUGS +++ b/BUGS @@ -1,97 +1,12 @@ Known Bugs in Synergy ===================== +View known bugs at: +http://sourceforge.net/tracker/?group_id=59275&atid=490467 + Report bugs at: -http://sourceforge.net/tracker/?func=browse&group_id=59275&atid=490467 +http://sourceforge.net/tracker/?group_id=59275&atid=490467 When reporting bugs, please include the version of the operating -system you're using and what locale you use. - -* Not all keystrokes are handled - - Certain keystrokes are not captured by the synergy server and, - therefore, are not transmitted to secondary screens. Which keys - depends on the primary screen's platform: - - Linux: - * ctrl+alt+del - * ctrl+alt+backspace (only if used by the X server) - * ctrl+alt+keypad_plus (only if used by the X server) - * ctrl+alt+keypad_minus (only if used by the X server) - - Windows NT family (except NT prior to service pack 3) - * ctrl+alt+del - * accessibility shortcuts (e.g. pressing shift 5 times for sticky keys) - - Windows 95 family (at NT prior to service pack 3) - * ctrl+alt+del - * ctrl+esc - * alt+[shift+]tab - * alt+[shift+]esc - * windows+E - * windows+[ctrl+]F - * windows+[shift+]M - * windows+R - * windows+F1 - * windows+tab - * windows+break - * accessibility shortcuts (e.g. press shift 5 times for sticky keys) - -* Non-ASCII keystrokes unsupported - - Only ASCII characters are decoded and transmitted by the server. - However, the clipboard does support Unicode. - -* Screen flashes (Windows) - - On Windows, the primary screen flashes when the cursor enters it. - This is a consequence of how synergy captures input. - -* Slow console text entry (Windows 95 family) - - Typing into a command prompt window on the Windows 95 family has - very slow visual feedback. - -* Event logs (Windows NT family) - - Synergy doesn't support event logs properly on the Windows NT family. - In particular, the event message is reported in the extra data portion - of the event instead of the normal message portion. - -* NoInteractiveServices not handled (Windows NT family) - - Synergy does not (cannot) work when the NoInteractiveServices registry - entry is set. Synergy doesn't not gracefully handle it being set. - -* Incorrectly hiding screen savers in certain situations (Windows) - - If turning off the screen saver on the primary screen requires a - password on Windows then all of the secondary screen screen savers - are deactivated when the password dialog appears. If the password - is not entered only the primary screen's screen saver is activated. - -* Not handling strange keyboards (X11) - - Synergy does not detect keyboards with unusual behavior and may - misbehave as a result. Unusual behavior includes keyboards that - don't report KeyRelease events and toggle keys that report KeyPress - when toggled on and KeyRelease when toggled off (instead of KeyPress - and KeyRelease for each physical press and release). - -* Not handling shift-lock (X11) - - Synergy doesn't handle shift-lock behavior (as opposed to caps-lock). - -* Large Motif clipboard items are truncated (X11) - - Synergy doesn't yet fully implement Motif's protocol for large - clipboard items. As a result it silently truncates those items. - Large depends on the X server but is probably around 256kB. - - Synergy also doesn't support by-name Motif clipboard items. It - will read no data at all for those items. - -* Automake isn't fully configured (Linux) - - The automake configuration isn't complete so synergy won't build - or fail to build properly on some (many) systems. +system you're using (on the server and relevant clients) and what +locales you use. diff --git a/FAQ b/FAQ index 0369b305..cb3a348c 100644 --- a/FAQ +++ b/FAQ @@ -1,10 +1,119 @@ Synergy Frequently Asked Questions ================================== -This will be added to as questions come in. +Questions +--------- +1. Why doesn't ctrl+alt+del work on secondary screens? +2. Can the server and client be on different operating systems? +3. What's the difference between synergy and x2x, x2vnc, etc? +4. What does "Cannot initialize hook library" mean? +5. What security/encryption does synergy provide? +6. What should I call my screens in the configuration? +7. Why do my CapsLock and NumLock keys act funny? +8. Can synergy share the display in addition to the mouse and keyboard? +9. Can synergy do drag and drop between computers? +10. Do AltGr or Mode-Switch work? + + +Answers +------- +1. Why doesn't ctrl+alt+del work on secondary screens? -1. ctrl+alt+del doesn't work on secondary screens. Synergy isn't able to capture ctrl+alt+del on PC compatible systems because it's handled completely differently than other keystrokes. If you need to use ctrl+alt+del you'll have to keep your other keyboards handy just for that. + Future versions may add support for or some equivalent to + ctrl+alt+del. + +2. Can the server and client be on different operating systems? + + Yes. The synergy network protocol is platform neutral so + synergy doesn't care what operating systems are running on + the server and clients. + +3. What's the difference between synergy and x2x, x2vnc, etc? + + Unlike x2x, synergy supports any number of computers and + it doesn't require X on Microsoft Windows platforms. It + also has more advanced clipboard support and synchronizes + screensavers. x2vnc is also limited to two computers, + requires the separate vnc package, and is really only + appropriate for using an X system to control a non-X system. + However, the right tool for the job is whatever tool works + best for you. + +4. What does "Cannot initialize hook library" mean? + + This error can occur on a synergy server running on a + Microsoft Windows operating system. It means that synergy + is already running or possibly was not shut down properly. + If it's running then first end the synergy task. If it's + not then try logging off and back on or rebooting then + starting synergy again. + +5. What security/encryption does synergy provide? + + None. Synergy currently does no encryption or authentication. + Given that, synergy should not be used on or over any untrusted + network, especially the Internet. It's generally fine for home + networks. Future versions will provide encryption and + authentication. + +6. What should I call my screens in the configuration? + + You can use any unique name in the configuration file for each + screen but it's easiest to use the hostname of the computer. + That's the computer name not including the domain. For example, + a computer with the fully qualified domain name `xyz.foo.com' has + the hostname `xyz'. There should also be an alias for `xyz' to + `xyz.foo.com'. If you don't use the computer's hostname, you + have to tell synergy the name of the screen using a command line + option, or the startup dialog on Windows. + +7. Why do my CapsLock and NumLock keys act funny? + + Some systems treat the Caps-Lock and Num-Lock keys differently + than all the others. Whereas most keys report going down when + physically pressed and going up when physically released, the + Caps-Lock and Num-Lock keys report going down when being + activated and going up when being deactivated. That is, when + you press and release, say, Caps-Lock to activate it, it only + reports going down, and when you press and release to deactivate + it, it only reports going up. This confuses synergy. + + You can solve the problem by changing your configuration file. + In the screens section, following each screen that has the + problem, add either or both of these lines as appropriate: + + halfDuplexCapsLock = true + halfDuplexNumLock = true + + Then restart synergy on the server. + +8. Can synergy share the display in addition to the mouse and keyboard? + + No. Synergy is a KM solution not a KVM (keyboard, video, mouse) + solution. However, future versions will probably support KVM. + Hopefully, this will make synergy suitable for managing large + numbers of headless servers. + +9. Can synergy do drag and drop between computers? + + No. That's a very cool idea and it'll be explored. However, it's + also clearly difficult and may take a long time to implement. + +10. Does AltGr/Mode-Switch work? + + Sort of. Synergy sends the character you're trying to create from + the server to the client. If the client can figure out how to + create that character then it will do so, synthesizing whatever + key events necessary. However, operating system differences can + cause unexpected problems. Pressing either Ctrl key plus the left + Alt key on Microsoft Windows means AltGr but on Unix that just + means Ctrl and Alt are down. If you have a synergy server running + on Windows and are having trouble with Ctrl+Alt_L = AltGr, try using + the right Alt key as AltGr instead. + + + diff --git a/INSTALL b/INSTALL index 40b1355a..8d531dd0 100644 --- a/INSTALL +++ b/INSTALL @@ -9,15 +9,18 @@ To build synergy from the sources you'll need the following: Windows: * VC++ 6.0 or up - Linux: - * gcc 2.95 (or up?) + Unix: + * gcc 2.95 (or up) * X11R4 or up headers and libraries +In this document, "Unix" means any of the following: Linux, Solaris, +Irix. + Configuring the build --------------------- -This step is only necessary when building on Linux. +This step is only necessary when building on Unix. To configure the build for your platform use the configure script: @@ -36,7 +39,7 @@ Windows: (Build | Set Active Configuration) to `All - Debug' or `All - Release' then build. Binaries are built into ./Debug or ./Release. -Linux: +Unix: Simply enter: make @@ -59,7 +62,7 @@ Windows: * synergys.exe * synrgyhk.dll -Linux: +Unix: make install will install the client and server into /usr/local/bin unless you @@ -126,8 +129,8 @@ See `Starting Automatically on Windows' below for configuring synergy to start automatically when the computer starts. -Configuring the Server on Linux -------------------------------- +Configuring the Server on Unix +------------------------------ The synergy server requires configuration. The configuration file is a plain text file broken into sections. Each section has the form: @@ -210,8 +213,8 @@ file if the user doesn't specify a path using the `--config' command line option. `synergys --help' reports those pathnames. -Running the Server on Linux ---------------------------- +Running the Server on Unix +-------------------------- Run the server on the computer that has the keyboard and mouse to be shared. You must have prepared a configuration file before @@ -232,12 +235,12 @@ file (either as a screen name or an alias) then you'll have to add a name in the configuration file. You can use `synergys --help' for a list of command line options. -See `Starting Automatically on Linux' below for running synergy +See `Starting Automatically on Unix' below for running synergy automatically when the X server starts. -Running the Client on Linux ---------------------------- +Running the Client on Unix +-------------------------- Run the client on all computers that aren't the server using the following command line: @@ -280,7 +283,7 @@ the problem. Here are typical problems and possible solutions: Once all the clients are running, try moving the mouse to each screen. Be sure to check all the configured links. -See `Starting Automatically on Linux' below for running synergy +See `Starting Automatically on Unix' below for running synergy automatically when the X server starts. @@ -316,51 +319,50 @@ Otherwise, click the `Install' button in the `When You Log In' box to have synergy automatically start when you log in. -Starting Automatically on Linux -------------------------------- +Starting Automatically on Unix +------------------------------ -Linux: - Synergy requires an X server. That means a server must be - running and synergy must be authorized to connect to that server. - It's best to have the display manager start synergy. You'll need - the necessary (probably root) permission to modify the display - manager configuration files. If you don't have that permission - you can start synergy after logging in via the .xsession file. +Synergy requires an X server. That means a server must be +running and synergy must be authorized to connect to that server. +It's best to have the display manager start synergy. You'll need +the necessary (probably root) permission to modify the display +manager configuration files. If you don't have that permission +you can start synergy after logging in via the .xsession file. - To have the display manager start synergy, edit the Xsetup script. - The location of this file depends on your installation. It might - be /etc/X11/xdm/Xsetup. Near the end of the file but before - anyplace the script calls exit, start the client with something - like: +To have the display manager start synergy, edit the Xsetup script. +The location of this file depends on your installation. It might +be /etc/X11/xdm/Xsetup. Near the end of the file but before +anyplace the script calls exit, start the client with something +like: - /usr/bin/killall synergyc - /usr/local/bin/synergyc [] + /usr/bin/killall synergyc + /usr/local/bin/synergyc [] - must not include `-f' or `--no-daemon'. Change the - paths as necessary. It's important to make sure no old copies - of synergy are running so they can't interfere with the new one. + must not include `-f' or `--no-daemon'. Change the +paths as necessary. It's important to make sure no old copies +of synergy are running so they can't interfere with the new one. - To start the server use something like: +To start the server use something like: - /usr/bin/killall synergys - /usr/local/bin/synergys [] --config + /usr/bin/killall synergys + /usr/local/bin/synergys [] --config - Again, must not include `-f' or `--no-daemon'. If - the configuration pathname is one of the default locations then - you don't need the `--config' option. +Again, must not include `-f' or `--no-daemon'. If +the configuration pathname is one of the default locations then +you don't need the `--config' option. - Note that some display managers (xdm and kdm, but not gdm) grab - the keyboard and do not release it until the user logs in, for - security reasons. This prevents a synergy server from sharing - the mouse and keyboard until the user logs in. It doesn't - prevent a synergy client from synthesizing mouse and keyboard - input, though. +Note that some display managers (xdm and kdm, but not gdm) grab +the keyboard and do not release it until the user logs in, for +security reasons. This prevents a synergy server from sharing +the mouse and keyboard until the user logs in. It doesn't +prevent a synergy client from synthesizing mouse and keyboard +input, though. Common Command Line Options --------------------------- -d, --debug use debugging level - --daemon run as a daemon (Linux) or background (Windows) + --daemon run as a daemon (Unix) or background (Windows) -f, --no-daemon run in the foreground -n, --name use instead of the hostname --restart automatically restart on failures diff --git a/README b/README index fb190422..cec0cd53 100644 --- a/README +++ b/README @@ -52,10 +52,13 @@ System Requirements * Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family); -* Linux: +* Unix: X Windows version 11 revision 4 or up with the XTEST extension (use `xdpyinfo | grep XTEST' to check for XTEST). +In this document, "Unix" means any of the following: Linux, Solaris, +Irix. + Installation ------------ @@ -74,7 +77,7 @@ The computer with that keyboard and mouse is called the "primary screen" and it runs the synergy server. All of the other computers are "secondary screens" and run the synergy client. The Windows NT family, starting with NT 4 with service pack 3, is the best choice -for the primary screen but Linux is good too. (This is based on the +for the primary screen but Unix is good too. (This is based on the known bugs; see BUGS for more details.) Second, you install the software. Choose the appropriate package @@ -111,8 +114,8 @@ Third, you configure and start the server. read the log messages to determine the problem then correct it and try `Test' again. - Linux - ----- + Unix + ---- Create a text file named synergy.conf with the following: section: screens @@ -165,8 +168,8 @@ Finally, start the clients. about to quit; read the log messages to determine the problem then correct it and try `Test' again. - Linux - ----- + Unix + ---- To start a client, enter the following: @@ -221,13 +224,13 @@ Tips and Tricks * Synergy's screen saver synchronization works best with xscreensaver under X windows. Synergy works better with xscreensaver if it is - using of the screen saver extensions. Prior to xscreensaver 4.0 + using one of the screen saver extensions. Prior to xscreensaver 4.0 you can use `-mit-extension', `-sgi-extension', or `-xidle-extension' command line options to enable an extension (assuming your server has the extension). Starting with 4.0 you must enable the corresponding option in your .xscreensaver file. -* Synergy automatically converts newlines in clipboard text (Linux +* Synergy automatically converts newlines in clipboard text (Unix expects \n to end each line while Windows expects \r\n). * Clients can be started and stopped at any time. When a screen is @@ -235,7 +238,7 @@ Tips and Tricks had moved all the way across it and jumped to the next screen. * A client's keyboard and mouse are fully functional while synergy is - running. You can use them in case synergy hangs. + running. You can use them in case synergy locks up. Bug Reports diff --git a/TODO b/TODO index b8a1efac..eeda47d3 100644 --- a/TODO +++ b/TODO @@ -31,8 +31,6 @@ Things to do to synergy, in no particular order: * MacOS 9 * MacOS 10 * BSD - * Irix - * Solaris * HP-UX * AIX From 62303391a80cf5b041bbf7e7847cba116c14be49 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 26 Dec 2002 18:40:22 +0000 Subject: [PATCH 405/807] More FAQs. --- FAQ | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/FAQ b/FAQ index cb3a348c..81dde7ba 100644 --- a/FAQ +++ b/FAQ @@ -13,6 +13,7 @@ Questions 8. Can synergy share the display in addition to the mouse and keyboard? 9. Can synergy do drag and drop between computers? 10. Do AltGr or Mode-Switch work? +11. Why isn't synergy ported to platform X? Answers @@ -115,5 +116,10 @@ Answers on Windows and are having trouble with Ctrl+Alt_L = AltGr, try using the right Alt key as AltGr instead. +11. Why isn't synergy ported to platform X? + Probably because the developers don't have access to platform X + and/or are unfamiliar with development on X. Also, synergy has + inherently non-portable aspects so there's a not insignificant + effort involved in porting. From f65921bc3fd593e536a3f85ce1411d86377c3d7a Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 4 Jan 2003 22:01:32 +0000 Subject: [PATCH 406/807] Refactored some platform dependent code into a new library, lib/arch. This should make porting easier. Will probably continue to refactor a little more, moving platform dependent event handling stuff into lib/platform. --- PORTING | 74 +- cmd/launcher/CAutoStart.cpp | 108 +- cmd/launcher/CAutoStart.h | 3 - cmd/launcher/LaunchUtil.cpp | 27 +- cmd/launcher/launcher.cpp | 134 ++- cmd/launcher/launcher.dsp | 4 +- cmd/launcher/launcher.rc | 4 +- cmd/synergyc/Makefile.am | 3 + cmd/synergyc/synergyc.cpp | 293 ++--- cmd/synergyc/synergyc.dsp | 8 +- cmd/synergys/Makefile.am | 3 + cmd/synergys/synergys.cpp | 347 +++--- cmd/synergys/synergys.dsp | 4 +- configure.in | 4 +- lib/Makefile.am | 2 + lib/arch/CArch.cpp | 576 +++++++++ lib/arch/CArch.h | 163 +++ lib/arch/CArchConsoleUnix.cpp | 54 + lib/arch/CArchConsoleUnix.h | 34 + lib/arch/CArchConsoleWindows.cpp | 107 ++ lib/arch/CArchConsoleWindows.h | 47 + lib/arch/CArchDaemonNone.cpp | 65 ++ lib/arch/CArchDaemonNone.h | 39 + lib/arch/CArchDaemonUnix.cpp | 81 ++ lib/arch/CArchDaemonUnix.h | 32 + .../CArchDaemonWindows.cpp} | 694 +++++------ lib/arch/CArchDaemonWindows.h | 130 +++ lib/arch/CArchFileUnix.cpp | 94 ++ lib/arch/CArchFileUnix.h | 35 + lib/arch/CArchFileWindows.cpp | 132 +++ lib/arch/CArchFileWindows.h | 35 + lib/arch/CArchImpl.cpp | 43 + lib/arch/CArchLogUnix.cpp | 73 ++ lib/arch/CArchLogUnix.h | 33 + lib/arch/CArchLogWindows.cpp | 84 ++ lib/arch/CArchLogWindows.h | 39 + lib/arch/CArchMiscWindows.cpp | 53 + lib/arch/CArchMiscWindows.h | 48 + lib/arch/CArchMultithreadPosix.cpp | 748 ++++++++++++ lib/arch/CArchMultithreadPosix.h | 93 ++ lib/arch/CArchMultithreadWindows.cpp | 648 +++++++++++ lib/arch/CArchMultithreadWindows.h | 99 ++ lib/arch/CArchNetworkBSD.cpp | 847 ++++++++++++++ lib/arch/CArchNetworkBSD.h | 95 ++ lib/arch/CArchNetworkWinsock.cpp | 838 ++++++++++++++ lib/arch/CArchNetworkWinsock.h | 91 ++ lib/arch/CArchSleepUnix.cpp | 88 ++ lib/arch/CArchSleepUnix.h | 31 + lib/arch/CArchSleepWindows.cpp | 54 + lib/arch/CArchSleepWindows.h | 31 + .../CPlatform.h => arch/CArchStringUnix.cpp} | 32 +- lib/arch/CArchStringUnix.h | 38 + lib/arch/CArchStringWindows.cpp | 38 + lib/arch/CArchStringWindows.h | 38 + lib/arch/CArchTimeUnix.cpp | 47 + lib/arch/CArchTimeUnix.h | 31 + lib/arch/CArchTimeWindows.cpp | 86 ++ lib/arch/CArchTimeWindows.h | 31 + lib/arch/CMultibyte.cpp | 40 + lib/arch/CMultibyteEmu.cpp | 81 ++ lib/arch/CMultibyteOS.cpp | 68 ++ lib/arch/IArchConsole.h | 57 + lib/arch/IArchDaemon.h | 104 ++ lib/arch/IArchFile.h | 59 + lib/arch/IArchLog.h | 49 + lib/arch/IArchMultithread.h | 80 ++ lib/arch/IArchNetwork.h | 105 ++ lib/arch/IArchSleep.h | 38 + lib/arch/IArchString.h | 61 + lib/arch/IArchTime.h | 35 + lib/arch/Makefile.am | 96 ++ lib/arch/XArch.cpp | 33 + lib/arch/XArch.h | 165 +++ .../CPlatform.cpp => arch/XArchImpl.h} | 16 +- lib/arch/XArchUnix.cpp | 33 + lib/arch/XArchUnix.h | 34 + lib/arch/XArchWindows.cpp | 126 ++ lib/arch/XArchWindows.h | 52 + lib/arch/arch.dsp | 269 +++++ lib/arch/vsnprintf.cpp | 36 + lib/base/CLog.cpp | 238 ++-- lib/base/CLog.h | 132 +-- lib/base/CStopwatch.cpp | 110 +- lib/base/CString.h | 82 +- lib/base/{CString.cpp => CStringUtil.cpp} | 15 +- lib/base/CStringUtil.h | 99 ++ lib/{io => base}/CUnicode.cpp | 89 +- lib/{io => base}/CUnicode.h | 1 - lib/base/ILogOutputter.h | 71 ++ lib/base/Makefile.am | 24 +- lib/base/XBase.cpp | 63 +- lib/base/XBase.h | 90 +- lib/base/base.dsp | 68 +- lib/client/CClient.cpp | 5 +- lib/client/CMSWindowsSecondaryScreen.cpp | 4 +- lib/client/Makefile.am | 2 + lib/client/client.dsp | 4 +- lib/{base => common}/BasicTypes.h | 0 lib/{base => common}/IInterface.h | 0 lib/common/Makefile.am | 45 + lib/{base => common}/Version.h | 4 + lib/common/common.dsp | 154 +++ lib/{base => common}/common.h | 0 lib/{base => common}/stdfstream.h | 0 lib/{base => common}/stdistream.h | 0 lib/{base => common}/stdlist.h | 0 lib/{base => common}/stdmap.h | 0 lib/{base => common}/stdostream.h | 0 lib/{base => common}/stdpost.h | 0 lib/{base => common}/stdpre.h | 0 lib/{base => common}/stdset.h | 0 lib/{base => common}/stdsstream.h | 0 lib/common/stdstring.h | 17 + lib/{base => common}/stdvector.h | 0 lib/http/CHTTPProtocol.h | 1 + lib/http/Makefile.am | 2 + lib/http/XHTTP.cpp | 1 + lib/http/http.dsp | 4 +- lib/io/Makefile.am | 4 +- lib/io/XIO.h | 16 +- lib/io/io.dsp | 12 +- lib/mt/CCondVar.cpp | 283 +---- lib/mt/CCondVar.h | 12 +- lib/mt/CMutex.cpp | 109 +- lib/mt/CMutex.h | 8 +- lib/mt/CThread.cpp | 159 +-- lib/mt/CThread.h | 71 +- lib/mt/CThreadRep.cpp | 794 ------------- lib/mt/CThreadRep.h | 168 --- lib/mt/CTimerThread.cpp | 3 +- lib/mt/Makefile.am | 4 +- lib/mt/XMT.h | 8 +- lib/mt/XThread.h | 24 +- lib/mt/mt.dsp | 12 +- lib/net/CNetwork.cpp | 1026 ----------------- lib/net/CNetwork.h | 272 ----- lib/net/CNetworkAddress.cpp | 138 +-- lib/net/CNetworkAddress.h | 30 +- lib/net/CTCPListenSocket.cpp | 62 +- lib/net/CTCPListenSocket.h | 4 +- lib/net/CTCPSocket.cpp | 280 +++-- lib/net/CTCPSocket.h | 9 +- lib/net/Makefile.am | 6 +- lib/net/XNetwork.cpp | 93 -- lib/net/XNetwork.h | 94 -- lib/net/XSocket.cpp | 109 +- lib/net/XSocket.h | 60 +- lib/net/net.dsp | 20 +- lib/platform/CMSWindowsScreen.cpp | 5 +- lib/platform/CUnixPlatform.cpp | 211 ---- lib/platform/CUnixPlatform.h | 50 - lib/platform/CWin32Platform.h | 130 --- lib/platform/CXWindowsClipboard.cpp | 9 +- lib/platform/IPlatform.h | 149 --- lib/platform/Makefile.am | 11 +- lib/platform/platform.dsp | 33 +- lib/platform/synrgyhk.dsp | 6 +- lib/server/CConfig.h | 1 + lib/server/CMSWindowsPrimaryScreen.cpp | 4 +- lib/server/CServer.cpp | 25 +- lib/server/CXWindowsPrimaryScreen.cpp | 5 +- lib/server/Makefile.am | 2 + lib/server/server.dsp | 4 +- lib/synergy/Makefile.am | 2 + lib/synergy/XScreen.h | 7 +- lib/synergy/XSynergy.cpp | 1 + lib/synergy/XSynergy.h | 7 +- lib/synergy/libsynergy.dsp | 8 +- synergy.dsw | 42 + 169 files changed, 9676 insertions(+), 5601 deletions(-) create mode 100644 lib/arch/CArch.cpp create mode 100644 lib/arch/CArch.h create mode 100644 lib/arch/CArchConsoleUnix.cpp create mode 100644 lib/arch/CArchConsoleUnix.h create mode 100644 lib/arch/CArchConsoleWindows.cpp create mode 100644 lib/arch/CArchConsoleWindows.h create mode 100644 lib/arch/CArchDaemonNone.cpp create mode 100644 lib/arch/CArchDaemonNone.h create mode 100644 lib/arch/CArchDaemonUnix.cpp create mode 100644 lib/arch/CArchDaemonUnix.h rename lib/{platform/CWin32Platform.cpp => arch/CArchDaemonWindows.cpp} (53%) create mode 100644 lib/arch/CArchDaemonWindows.h create mode 100644 lib/arch/CArchFileUnix.cpp create mode 100644 lib/arch/CArchFileUnix.h create mode 100644 lib/arch/CArchFileWindows.cpp create mode 100644 lib/arch/CArchFileWindows.h create mode 100644 lib/arch/CArchImpl.cpp create mode 100644 lib/arch/CArchLogUnix.cpp create mode 100644 lib/arch/CArchLogUnix.h create mode 100644 lib/arch/CArchLogWindows.cpp create mode 100644 lib/arch/CArchLogWindows.h create mode 100644 lib/arch/CArchMiscWindows.cpp create mode 100644 lib/arch/CArchMiscWindows.h create mode 100644 lib/arch/CArchMultithreadPosix.cpp create mode 100644 lib/arch/CArchMultithreadPosix.h create mode 100644 lib/arch/CArchMultithreadWindows.cpp create mode 100644 lib/arch/CArchMultithreadWindows.h create mode 100644 lib/arch/CArchNetworkBSD.cpp create mode 100644 lib/arch/CArchNetworkBSD.h create mode 100644 lib/arch/CArchNetworkWinsock.cpp create mode 100644 lib/arch/CArchNetworkWinsock.h create mode 100644 lib/arch/CArchSleepUnix.cpp create mode 100644 lib/arch/CArchSleepUnix.h create mode 100644 lib/arch/CArchSleepWindows.cpp create mode 100644 lib/arch/CArchSleepWindows.h rename lib/{platform/CPlatform.h => arch/CArchStringUnix.cpp} (67%) create mode 100644 lib/arch/CArchStringUnix.h create mode 100644 lib/arch/CArchStringWindows.cpp create mode 100644 lib/arch/CArchStringWindows.h create mode 100644 lib/arch/CArchTimeUnix.cpp create mode 100644 lib/arch/CArchTimeUnix.h create mode 100644 lib/arch/CArchTimeWindows.cpp create mode 100644 lib/arch/CArchTimeWindows.h create mode 100644 lib/arch/CMultibyte.cpp create mode 100644 lib/arch/CMultibyteEmu.cpp create mode 100644 lib/arch/CMultibyteOS.cpp create mode 100644 lib/arch/IArchConsole.h create mode 100644 lib/arch/IArchDaemon.h create mode 100644 lib/arch/IArchFile.h create mode 100644 lib/arch/IArchLog.h create mode 100644 lib/arch/IArchMultithread.h create mode 100644 lib/arch/IArchNetwork.h create mode 100644 lib/arch/IArchSleep.h create mode 100644 lib/arch/IArchString.h create mode 100644 lib/arch/IArchTime.h create mode 100644 lib/arch/Makefile.am create mode 100644 lib/arch/XArch.cpp create mode 100644 lib/arch/XArch.h rename lib/{platform/CPlatform.cpp => arch/XArchImpl.h} (79%) create mode 100644 lib/arch/XArchUnix.cpp create mode 100644 lib/arch/XArchUnix.h create mode 100644 lib/arch/XArchWindows.cpp create mode 100644 lib/arch/XArchWindows.h create mode 100644 lib/arch/arch.dsp create mode 100644 lib/arch/vsnprintf.cpp rename lib/base/{CString.cpp => CStringUtil.cpp} (94%) create mode 100644 lib/base/CStringUtil.h rename lib/{io => base}/CUnicode.cpp (92%) rename lib/{io => base}/CUnicode.h (99%) create mode 100644 lib/base/ILogOutputter.h rename lib/{base => common}/BasicTypes.h (100%) rename lib/{base => common}/IInterface.h (100%) create mode 100644 lib/common/Makefile.am rename lib/{base => common}/Version.h (91%) create mode 100644 lib/common/common.dsp rename lib/{base => common}/common.h (100%) rename lib/{base => common}/stdfstream.h (100%) rename lib/{base => common}/stdistream.h (100%) rename lib/{base => common}/stdlist.h (100%) rename lib/{base => common}/stdmap.h (100%) rename lib/{base => common}/stdostream.h (100%) rename lib/{base => common}/stdpost.h (100%) rename lib/{base => common}/stdpre.h (100%) rename lib/{base => common}/stdset.h (100%) rename lib/{base => common}/stdsstream.h (100%) create mode 100644 lib/common/stdstring.h rename lib/{base => common}/stdvector.h (100%) delete mode 100644 lib/mt/CThreadRep.cpp delete mode 100644 lib/mt/CThreadRep.h delete mode 100644 lib/net/CNetwork.cpp delete mode 100644 lib/net/CNetwork.h delete mode 100644 lib/net/XNetwork.cpp delete mode 100644 lib/net/XNetwork.h delete mode 100644 lib/platform/CUnixPlatform.cpp delete mode 100644 lib/platform/CUnixPlatform.h delete mode 100644 lib/platform/CWin32Platform.h delete mode 100644 lib/platform/IPlatform.h diff --git a/PORTING b/PORTING index 55fc63c5..95aad05d 100644 --- a/PORTING +++ b/PORTING @@ -10,16 +10,19 @@ The synergy source code organization is: . -- root makefiles, some standard documentation cmd -- program source code - synergy -- synergy client - synergyd -- synergy server + launcher -- synergy launcher for Windows + synergyc -- synergy client + synergys -- synergy server config -- stuff for autoconf/automake dist -- files for creating distributions rpm -- files for creating RPMs doc -- placeholder for documentation examples -- example files lib -- library source code + arch -- platform dependent utility library base -- simple utilities client -- synergy client library + common -- commonly needed header files http -- simple http tools io -- I/O mt -- multithreading @@ -130,7 +133,7 @@ following these guidelines. * Other headers in directory, sorted alphabetically * Headers for each library, sorted alphabetically per library Include headers from the library closest in the dependency graph - first, then the next farthest, etc. sort alphabetically within + first, then the next farthest, etc. Sort alphabetically within each library. * System headers @@ -181,12 +184,8 @@ following these guidelines. Inheriting implementation from multiple classes can have unpleasant consequences in C++ due to it's limited capabilities. Classes can inherit from any number of interface classes. An interface class - provides only pure virtual methods. - - Synergy breaks this rule in two places. First is that IInterface - implements the virtual destructor. However, it's just an empty - method so it doesn't really count. Second is MXErrno which provides - implementation for exceptions that use the global errno variable. + provides only pure virtual methods. Synergy breaks this rule in + IInterface which implements the virtual destructor for convenience. * No globals Avoid global variables. All global variables must be static, making @@ -272,18 +271,20 @@ following these guidelines. * Open brace for function is on a line by itself in first column * Close brace for function lines up with open brace * Always use braces on: if, else, for, while, do, switch - * `else {' goes on it's own line + * `else {' goes on its own line * Always explicitly test pointers against NULL e.g. `if (ptr == NULL)' not `if (ptr)' + * Always explicitly test integral values against 0 + e.g. `if (i == 0)' not `if (i)' * Put spaces around binary operators and after statements e.g. `if (a == b) {' not `if(a==b){' * C'tor initializers are one per line, indented one tab stop * Other indentation should follow existing practice - * Use Qt style comments for extraction by doxygen + * Use Qt style comments for extraction by doxygen (i.e. //! and /*!) * Mark incomplete or buggy code with `FIXME' - Other - * calls to log() should always be all on one line (even past column 80) + * calls to LOG() should always be all on one line (even past column 80) Class Relationships @@ -300,4 +301,51 @@ FIXME -- high level overview of class relationships Portability ----------- -FIXME -- information about porting to new platforms +Porting synergy to a new platform requires the following steps: + +- Setting up the build +- Adjusting lib/common/common.h +- Implementing lib/arch +- Implementing lib/platform +- Implementing primary and secondary screens for the platform +- Tweaking synergyc and synergys + +Setting up the build: + +The first phase is simply to create the files necessary to build the +other files. On Unix, synergy uses autoconf/automake which produces +a `configure' script that generates makefiles. On Windows, synergy +uses Visual C++ workspace and project files. If you're porting to +another Unix variant, you may need to adjust `configure.in', +`acinclude.m4', and Unix flavor dependent code in lib/arch. + +Adjusting lib/common/common.h: + +The lib/common/common.h header file is included directly or indirectly +by every other file. It prepares some platform dependent macros for +integer sizes and defines a macro for conveniently testing which +platform we're building on. That macro is named *_LIKE (e.g. UNIX_LIKE) +and has the value `1'. Exactly one *_LIKE macro must be defined by +common.h. + +Implementing lib/arch: + +Most platform dependent code lives in lib/arch. There are several +interface classes there and they must all be implemented for each +platform. See the interface header files for more information. + +Implementing lib/platform: + +Most of the remaining platform dependent code lives in lib/platform. +The code there implements platform dependent window, clipboard, and +screen saver handling. The following interfaces must be implemented: + +FIXME + +Implementing primary and secondary screens for the platform: + +FIXME + +Tweaking synergyc and synergys: + +FIXME diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp index f13345e7..13910634 100644 --- a/cmd/launcher/CAutoStart.cpp +++ b/cmd/launcher/CAutoStart.cpp @@ -12,8 +12,11 @@ * GNU General Public License for more details. */ -#include "CPlatform.h" #include "CLog.h" +#include "ILogOutputter.h" +#include "CArch.h" +#include "CStringUtil.h" +#include "XArch.h" #include "CAutoStart.h" #include "LaunchUtil.h" #include "resource.h" @@ -23,6 +26,37 @@ #define CLIENT_DAEMON_INFO "Shares this system's mouse and keyboard with others." #define SERVER_DAEMON_INFO "Shares this system's mouse and keyboard with others." +// +// CAutoStartOutputter +// +// This class detects a message above a certain level and saves it +// + +class CAutoStartOutputter : public ILogOutputter { +public: + CAutoStartOutputter(CString* msg) : m_msg(msg) { } + virtual ~CAutoStartOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } + +private: + CString* m_msg; +}; + +bool +CAutoStartOutputter::write(ELevel level, const char* message) +{ + if (level <= CLog::kERROR) { + *m_msg = message; + } + return false; +} + + // // CAutoStart // @@ -51,8 +85,7 @@ void CAutoStart::doModal() { // install our log outputter - CLog::Outputter oldOutputter = CLog::getOutputter(); - CLog::setOutputter(&CAutoStart::onLog); + CLOG->insert(new CAutoStartOutputter(&m_errorMessage)); // reset saved flag m_userConfigSaved = false; @@ -61,8 +94,8 @@ CAutoStart::doModal() DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_AUTOSTART), m_parent, dlgProc, (LPARAM)this); - // restore log outputter - CLog::setOutputter(oldOutputter); + // remove log outputter + CLOG->pop_front(); } bool @@ -74,19 +107,16 @@ CAutoStart::wasUserConfigSaved() const void CAutoStart::update() { - // need a platform object - CPlatform platform; - // get installation state - const bool installedSystem = platform.isDaemonInstalled( + const bool installedSystem = ARCH->isDaemonInstalled( m_name.c_str(), true); - const bool installedUser = platform.isDaemonInstalled( + const bool installedUser = ARCH->isDaemonInstalled( m_name.c_str(), false); // get user's permissions - const bool canInstallSystem = platform.canInstallDaemon( + const bool canInstallSystem = ARCH->canInstallDaemon( m_name.c_str(), true); - const bool canInstallUser = platform.canInstallDaemon( + const bool canInstallUser = ARCH->canInstallDaemon( m_name.c_str(), false); // update messages @@ -165,22 +195,25 @@ CAutoStart::onInstall(bool allUsers) m_errorMessage = ""; // install - CPlatform platform; - if (!platform.installDaemon(m_name.c_str(), + try { + ARCH->installDaemon(m_name.c_str(), m_isServer ? SERVER_DAEMON_INFO : CLIENT_DAEMON_INFO, - appPath.c_str(), m_cmdLine.c_str(), allUsers)) { + appPath.c_str(), m_cmdLine.c_str(), allUsers); + askOkay(m_hwnd, getString(IDS_INSTALL_TITLE), + getString(allUsers ? + IDS_INSTALLED_SYSTEM : + IDS_INSTALLED_USER)); + return true; + } + catch (XArchDaemon& e) { if (m_errorMessage.empty()) { - m_errorMessage = getString(IDS_INSTALL_GENERIC_ERROR); + m_errorMessage = CStringUtil::format( + getString(IDS_INSTALL_GENERIC_ERROR).c_str(), + e.what().c_str()); } showError(m_hwnd, m_errorMessage); return false; } - - askOkay(m_hwnd, getString(IDS_INSTALL_TITLE), - getString(allUsers ? - IDS_INSTALLED_SYSTEM : - IDS_INSTALLED_USER)); - return true; } bool @@ -190,30 +223,23 @@ CAutoStart::onUninstall(bool allUsers) m_errorMessage = ""; // uninstall - CPlatform platform; - if (platform.uninstallDaemon(m_name.c_str(), allUsers) != - IPlatform::kSuccess) { + try { + ARCH->uninstallDaemon(m_name.c_str(), allUsers); + askOkay(m_hwnd, getString(IDS_UNINSTALL_TITLE), + getString(allUsers ? + IDS_UNINSTALLED_SYSTEM : + IDS_UNINSTALLED_USER)); + return true; + } + catch (XArchDaemon& e) { if (m_errorMessage.empty()) { - m_errorMessage = getString(IDS_UNINSTALL_GENERIC_ERROR); + m_errorMessage = CStringUtil::format( + getString(IDS_UNINSTALL_GENERIC_ERROR).c_str(), + e.what().c_str()); } showError(m_hwnd, m_errorMessage); return false; } - - askOkay(m_hwnd, getString(IDS_UNINSTALL_TITLE), - getString(allUsers ? - IDS_UNINSTALLED_SYSTEM : - IDS_UNINSTALLED_USER)); - return true; -} - -bool -CAutoStart::onLog(int priority, const char* message) -{ - if (priority <= CLog::kERROR) { - s_singleton->m_errorMessage = message; - } - return true; } BOOL diff --git a/cmd/launcher/CAutoStart.h b/cmd/launcher/CAutoStart.h index 954801ab..e931530e 100644 --- a/cmd/launcher/CAutoStart.h +++ b/cmd/launcher/CAutoStart.h @@ -57,9 +57,6 @@ private: bool onInstall(bool allUsers); bool onUninstall(bool allUsers); - // log handling - static bool onLog(int priority, const char* message); - // message handling BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp index 57e31554..ccd35c07 100644 --- a/cmd/launcher/LaunchUtil.cpp +++ b/cmd/launcher/LaunchUtil.cpp @@ -13,8 +13,8 @@ */ #include "CConfig.h" -#include "CPlatform.h" #include "LaunchUtil.h" +#include "CArch.h" #include "resource.h" #include "stdfstream.h" @@ -107,10 +107,9 @@ CString getAppPath(const CString& appName) { // prepare path to app - CPlatform platform; char myPathname[MAX_PATH]; GetModuleFileName(s_instance, myPathname, MAX_PATH); - const char* myBasename = platform.getBasename(myPathname); + const char* myBasename = ARCH->getBasename(myPathname); CString appPath = CString(myPathname, myBasename - myPathname); appPath += appName; return appPath; @@ -136,24 +135,20 @@ loadConfig(const CString& pathname, CConfig& config) bool loadConfig(CConfig& config) { - CPlatform platform; - // load configuration bool configLoaded = false; - CString path = platform.getUserDirectory(); + CString path = ARCH->getUserDirectory(); if (!path.empty()) { - CPlatform platform; - // try loading the user's configuration - path = platform.addPathComponent(path, CONFIG_NAME); + path = ARCH->concatPath(path, CONFIG_NAME); if (loadConfig(path, config)) { configLoaded = true; } else { // try the system-wide config file - path = platform.getSystemDirectory(); + path = ARCH->getSystemDirectory(); if (!path.empty()) { - path = platform.addPathComponent(path, CONFIG_NAME); + path = ARCH->concatPath(path, CONFIG_NAME); if (loadConfig(path, config)) { configLoaded = true; } @@ -183,13 +178,11 @@ saveConfig(const CString& pathname, const CConfig& config) bool saveConfig(const CConfig& config, bool sysOnly) { - CPlatform platform; - // try saving the user's configuration if (!sysOnly) { - CString path = platform.getUserDirectory(); + CString path = ARCH->getUserDirectory(); if (!path.empty()) { - path = platform.addPathComponent(path, CONFIG_NAME); + path = ARCH->concatPath(path, CONFIG_NAME); if (saveConfig(path, config)) { return true; } @@ -198,9 +191,9 @@ saveConfig(const CConfig& config, bool sysOnly) // try the system-wide config file else { - CString path = platform.getSystemDirectory(); + CString path = ARCH->getSystemDirectory(); if (!path.empty()) { - path = platform.addPathComponent(path, CONFIG_NAME); + path = ARCH->concatPath(path, CONFIG_NAME); if (saveConfig(path, config)) { return true; } diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 603354fd..0b17b224 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -14,9 +14,9 @@ #include "CConfig.h" #include "ProtocolTypes.h" -#include "CPlatform.h" -#include "CNetwork.h" #include "CLog.h" +#include "CStringUtil.h" +#include "CArch.h" #include "Version.h" #include "stdvector.h" #include "resource.h" @@ -62,10 +62,26 @@ static const char* s_debugName[][2] = { }; static const int s_defaultDebug = 3; // INFO -static HWND s_mainWindow; -static CConfig s_config; -static CConfig s_oldConfig; -static CStringList s_screens; +// +// program arguments +// + +#define ARG CArgs::s_instance + +class CArgs { +public: + CArgs() { s_instance = this; } + ~CArgs() { s_instance = NULL; } + +public: + static CArgs* s_instance; + CConfig m_config; + CConfig m_oldConfig; + CStringList m_screens; +}; + +CArgs* CArgs::s_instance = NULL; + static BOOL CALLBACK @@ -117,7 +133,7 @@ static void enableSaveControls(HWND hwnd) { - enableItem(hwnd, IDC_MAIN_SAVE, s_config != s_oldConfig); + enableItem(hwnd, IDC_MAIN_SAVE, ARG->m_config != ARG->m_oldConfig); } static @@ -171,8 +187,8 @@ updateNeighbor(HWND hwnd, const CString& screen, EDirection direction) // add all screens to combo box if (!screen.empty()) { - for (CConfig::const_iterator index = s_config.begin(); - index != s_config.end(); ++index) { + for (CConfig::const_iterator index = ARG->m_config.begin(); + index != ARG->m_config.end(); ++index) { SendMessage(hwnd, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)index->c_str()); } @@ -184,7 +200,7 @@ updateNeighbor(HWND hwnd, const CString& screen, EDirection direction) // select neighbor in combo box LRESULT index = 0; if (!screen.empty()) { - const CString& neighbor = s_config.getNeighbor(screen, direction); + const CString& neighbor = ARG->m_config.getNeighbor(screen, direction); if (!neighbor.empty()) { index = SendMessage(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)neighbor.c_str()); @@ -205,7 +221,7 @@ updateNeighbors(HWND hwnd) HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); if (index != LB_ERR) { - screen = s_screens[index]; + screen = ARG->m_screens[index]; } // set neighbor combo boxes @@ -230,7 +246,7 @@ addScreen(HWND hwnd) if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), hwnd, addDlgProc, (LPARAM)&info) != 0) { // get current number of screens - UInt32 i = s_screens.size(); + UInt32 i = ARG->m_screens.size(); // add screen to list control HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); @@ -239,15 +255,15 @@ addScreen(HWND hwnd) SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); // add screen to screen list - s_screens.push_back(info.m_screen); + ARG->m_screens.push_back(info.m_screen); // add screen to config - s_config.addScreen(info.m_screen); + ARG->m_config.addScreen(info.m_screen); // add aliases to config for (CStringList::const_iterator index = info.m_aliases.begin(); index != info.m_aliases.end(); ++index) { - s_config.addAlias(info.m_screen, *index); + ARG->m_config.addAlias(info.m_screen, *index); } // update neighbors @@ -271,9 +287,9 @@ editScreen(HWND hwnd) // fill in screen info CScreenInfo info; - info.m_screen = s_screens[index]; - for (CConfig::all_const_iterator index = s_config.beginAll(); - index != s_config.endAll(); ++index) { + info.m_screen = ARG->m_screens[index]; + for (CConfig::all_const_iterator index = ARG->m_config.beginAll(); + index != ARG->m_config.endAll(); ++index) { if (CStringUtil::CaselessCmp::equal(index->second, info.m_screen) && !CStringUtil::CaselessCmp::equal(index->second, index->first)) { info.m_aliases.push_back(index->first); @@ -287,21 +303,21 @@ editScreen(HWND hwnd) if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), hwnd, addDlgProc, (LPARAM)&info) != 0) { // replace screen - s_screens[index] = info.m_screen; + ARG->m_screens[index] = info.m_screen; // remove old aliases for (CStringList::const_iterator index = oldInfo.m_aliases.begin(); index != oldInfo.m_aliases.end(); ++index) { - s_config.removeAlias(*index); + ARG->m_config.removeAlias(*index); } // replace name - s_config.renameScreen(oldInfo.m_screen, info.m_screen); + ARG->m_config.renameScreen(oldInfo.m_screen, info.m_screen); // add new aliases for (CStringList::const_iterator index = info.m_aliases.begin(); index != info.m_aliases.end(); ++index) { - s_config.addAlias(info.m_screen, *index); + ARG->m_config.addAlias(info.m_screen, *index); } // update list @@ -331,16 +347,16 @@ removeScreen(HWND hwnd) } // get screen name - CString name = s_screens[index]; + CString name = ARG->m_screens[index]; // remove screen from list control SendMessage(child, LB_DELETESTRING, index, 0); // remove screen from screen list - s_screens.erase(&s_screens[index]); + ARG->m_screens.erase(&ARG->m_screens[index]); // remove screen from config (this also removes aliases) - s_config.removeScreen(name); + ARG->m_config.removeScreen(name); // update neighbors updateNeighbors(hwnd); @@ -361,20 +377,20 @@ changeNeighbor(HWND hwnd, HWND combo, EDirection direction) } // get screen name - CString screen = s_screens[index]; + CString screen = ARG->m_screens[index]; // get selected neighbor index = SendMessage(combo, CB_GETCURSEL, 0, 0); // remove old connection - s_config.disconnect(screen, direction); + ARG->m_config.disconnect(screen, direction); // add new connection if (index != LB_ERR && index != 0) { LRESULT size = SendMessage(combo, CB_GETLBTEXTLEN, index, 0); char* neighbor = new char[size + 1]; SendMessage(combo, CB_GETLBTEXT, index, (LPARAM)neighbor); - s_config.connect(screen, direction, CString(neighbor)); + ARG->m_config.connect(screen, direction, CString(neighbor)); delete[] neighbor; } @@ -444,14 +460,14 @@ getCommandLine(HWND hwnd, bool testing) // get and verify screen name HWND child = getItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); CString name = getWindowText(child); - if (!s_config.isValidScreenName(name)) { + if (!ARG->m_config.isValidScreenName(name)) { showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SCREEN_NAME).c_str(), name.c_str())); SetFocus(child); return CString(); } - if (!isClient && !s_config.isScreen(name)) { + if (!isClient && !ARG->m_config.isScreen(name)) { showError(hwnd, CStringUtil::format( getString(IDS_UNKNOWN_SCREEN_NAME).c_str(), name.c_str())); @@ -490,7 +506,7 @@ getCommandLine(HWND hwnd, bool testing) // check server name child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); CString server = getWindowText(child); - if (!s_config.isValidScreenName(server)) { + if (!ARG->m_config.isValidScreenName(server)) { showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SERVER_NAME).c_str(), server.c_str())); @@ -649,15 +665,13 @@ static void initMainWindow(HWND hwnd) { - CPlatform platform; - // append version number to title CString titleFormat = getString(IDS_TITLE); setWindowText(hwnd, CStringUtil::format(titleFormat.c_str(), VERSION)); // load configuration - bool configLoaded = loadConfig(s_config); - s_oldConfig = s_config; + bool configLoaded = loadConfig(ARG->m_config); + ARG->m_oldConfig = ARG->m_config; enableSaveControls(hwnd); // choose client/server radio buttons @@ -673,9 +687,9 @@ initMainWindow(HWND hwnd) if (configLoaded) { int i = 1; child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); - for (CConfig::const_iterator index = s_config.begin(); - index != s_config.end(); ++i, ++index) { - s_screens.push_back(*index); + for (CConfig::const_iterator index = ARG->m_config.begin(); + index != ARG->m_config.end(); ++i, ++index) { + ARG->m_screens.push_back(*index); CString item = CStringUtil::print("%d. %s", i, index->c_str()); SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); } @@ -687,9 +701,9 @@ initMainWindow(HWND hwnd) child = getItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT); SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); - CNetwork::gethostname(buffer, sizeof(buffer)); + CString hostname = ARCH->getHostName(); child = getItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); - SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)hostname.c_str()); child = getItem(hwnd, IDC_MAIN_DEBUG); for (unsigned int i = 0; i < sizeof(s_debugName) / @@ -746,7 +760,7 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) tokenize(newAliases, getWindowText(child)); // name must be valid - if (!s_config.isValidScreenName(newName)) { + if (!ARG->m_config.isValidScreenName(newName)) { showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SCREEN_NAME).c_str(), newName.c_str())); @@ -756,7 +770,7 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) // aliases must be valid for (CStringList::const_iterator index = newAliases.begin(); index != newAliases.end(); ++index) { - if (!s_config.isValidScreenName(*index)) { + if (!ARG->m_config.isValidScreenName(*index)) { showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SCREEN_NAME).c_str(), index->c_str())); @@ -775,7 +789,7 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) // name must not exist in config but allow same name. also // allow name if it exists in the old alias list but not the // new one. - if (s_config.isScreen(newName) && + if (ARG->m_config.isScreen(newName) && !CStringUtil::CaselessCmp::equal(newName, info->m_screen) && !isNameInList(info->m_aliases, newName)) { showError(hwnd, CStringUtil::format( @@ -788,7 +802,7 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) // allow an alias to be the old name. for (CStringList::const_iterator index = newAliases.begin(); index != newAliases.end(); ++index) { - if (s_config.isScreen(*index) && + if (ARG->m_config.isScreen(*index) && !CStringUtil::CaselessCmp::equal(*index, info->m_screen) && !isNameInList(info->m_aliases, *index)) { showError(hwnd, CStringUtil::format( @@ -830,7 +844,7 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) switch (LOWORD(wParam)) { case IDCANCEL: // test for unsaved data - if (s_config != s_oldConfig) { + if (ARG->m_config != ARG->m_oldConfig) { if (!askVerify(hwnd, getString(IDS_UNSAVED_DATA_REALLY_QUIT))) { return 0; } @@ -846,14 +860,14 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) const bool testing = (LOWORD(wParam) == IDC_MAIN_TEST); // save data - if (s_config != s_oldConfig) { - if (!saveConfig(s_config, false)) { + if (ARG->m_config != ARG->m_oldConfig) { + if (!saveConfig(ARG->m_config, false)) { showError(hwnd, CStringUtil::format( getString(IDS_SAVE_FAILED).c_str(), getErrorString(GetLastError()).c_str())); return 0; } - s_oldConfig = s_config; + ARG->m_oldConfig = ARG->m_config; enableSaveControls(hwnd); } @@ -892,11 +906,11 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) if (!cmdLine.empty()) { // run dialog CAutoStart autoStart(hwnd, - isClientChecked(hwnd) ? NULL : &s_config, + isClientChecked(hwnd) ? NULL : &ARG->m_config, cmdLine); autoStart.doModal(); if (autoStart.wasUserConfigSaved()) { - s_oldConfig = s_config; + ARG->m_oldConfig = ARG->m_config; enableSaveControls(hwnd); } } @@ -904,13 +918,13 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } case IDC_MAIN_SAVE: - if (!saveConfig(s_config, false)) { + if (!saveConfig(ARG->m_config, false)) { showError(hwnd, CStringUtil::format( getString(IDS_SAVE_FAILED).c_str(), getErrorString(GetLastError()).c_str())); } else { - s_oldConfig = s_config; + ARG->m_oldConfig = ARG->m_config; enableSaveControls(hwnd); } return 0; @@ -981,10 +995,11 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) { - s_instance = instance; + CArch arch; + CLOG; + CArgs args; - // initialize network library - CNetwork::init(); + s_instance = instance; // register main window (dialog) class WNDCLASSEX classInfo; @@ -1009,13 +1024,14 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) RegisterClassEx(&classInfo); // create main window - s_mainWindow = CreateDialog(s_instance, MAKEINTRESOURCE(IDD_MAIN), 0, NULL); + HWND m_mainWindow = CreateDialog(s_instance, + MAKEINTRESOURCE(IDD_MAIN), 0, NULL); // prep window - initMainWindow(s_mainWindow); + initMainWindow(m_mainWindow); // show window - ShowWindow(s_mainWindow, nCmdShow); + ShowWindow(m_mainWindow, nCmdShow); // main loop MSG msg; @@ -1032,7 +1048,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) break; default: - if (!IsDialogMessage(s_mainWindow, &msg)) { + if (!IsDialogMessage(m_mainWindow, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp index f93739aa..51096677 100644 --- a/cmd/launcher/launcher.dsp +++ b/cmd/launcher/launcher.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -71,7 +71,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index af23a29a..6091c59f 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -241,8 +241,8 @@ BEGIN "Synergy is not configured to start automatically." IDS_INSTALL_LABEL "Install" IDS_UNINSTALL_LABEL "Uninstall" - IDS_INSTALL_GENERIC_ERROR "Install failed for an unknown reason." - IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed for an unknown reason." + IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}." + IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}." IDS_INSTALL_TITLE "Installed Auto-Start" IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 98ab2824..9af3e12c 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -38,6 +38,7 @@ synergyc_LDADD = \ $(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ $(X_LIBS) \ $(X_PRE_LIBS) \ -lXtst \ @@ -46,6 +47,8 @@ synergyc_LDADD = \ $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 495c0b6f..a6c03c2a 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -14,11 +14,9 @@ #include "CClient.h" #include "ISecondaryScreenFactory.h" -#include "CPlatform.h" #include "ProtocolTypes.h" #include "Version.h" #include "XScreen.h" -#include "CNetwork.h" #include "CNetworkAddress.h" #include "CTCPSocketFactory.h" #include "XSocket.h" @@ -28,12 +26,18 @@ #include "CThread.h" #include "XThread.h" #include "CLog.h" +#include "LogOutputters.h" #include "CString.h" +#include "CArch.h" +#include "CArchMiscWindows.h" #include +#define DAEMON_RUNNING(running_) #if WINDOWS_LIKE #include "CMSWindowsSecondaryScreen.h" #include "resource.h" +#undef DAEMON_RUNNING +#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #elif UNIX_LIKE #include "CXWindowsSecondaryScreen.h" #endif @@ -49,35 +53,33 @@ // program arguments // -static const char* pname = NULL; -static bool s_backend = false; -static bool s_restartable = true; -static bool s_daemon = true; -static bool s_camp = true; -static const char* s_logFilter = NULL; -static CString s_name; -static CNetworkAddress s_serverAddress; +#define ARG CArgs::s_instance +class CArgs { +public: + CArgs() : + m_pname(NULL), + m_backend(false), + m_restartable(true), + m_daemon(true), + m_camp(true), + m_logFilter(NULL) + { s_instance = this; } + ~CArgs() { s_instance = NULL; } -// -// logging thread safety -// +public: + static CArgs* s_instance; + const char* m_pname; + bool m_backend; + bool m_restartable; + bool m_daemon; + bool m_camp; + const char* m_logFilter; + CString m_name; + CNetworkAddress m_serverAddress; +}; -static CMutex* s_logMutex = NULL; - -static -void -logLock(bool lock) -{ - assert(s_logMutex != NULL); - - if (lock) { - s_logMutex->lock(); - } - else { - s_logMutex->unlock(); - } -} +CArgs* CArgs::s_instance = NULL; // @@ -113,27 +115,17 @@ static CClient* s_client = NULL; static int -realMain(CMutex* mutex) +realMain(void) { - // caller should have mutex locked on entry - - // initialize threading library - CThread::init(); - - // make logging thread safe - CMutex logMutex; - s_logMutex = &logMutex; - CLog::setLock(&logLock); - int result = kExitSuccess; do { bool opened = false; bool locked = true; try { // create client - s_client = new CClient(s_name); - s_client->camp(s_camp); - s_client->setAddress(s_serverAddress); + s_client = new CClient(ARG->m_name); + s_client->camp(ARG->m_camp); + s_client->setAddress(ARG->m_serverAddress); s_client->setScreenFactory(new CSecondaryScreenFactory); s_client->setSocketFactory(new CTCPSocketFactory); s_client->setStreamFilterFactory(NULL); @@ -144,9 +136,7 @@ realMain(CMutex* mutex) opened = true; // run client - if (mutex != NULL) { - mutex->unlock(); - } + DAEMON_RUNNING(true); locked = false; s_client->mainLoop(); locked = true; @@ -160,8 +150,8 @@ realMain(CMutex* mutex) // clean up #define FINALLY do { \ - if (!locked && mutex != NULL) { \ - mutex->lock(); \ + if (!locked) { \ + DAEMON_RUNNING(false); \ } \ if (s_client != NULL) { \ if (opened) { \ @@ -175,8 +165,8 @@ realMain(CMutex* mutex) } catch (XScreenUnavailable& e) { // wait before retrying if we're going to retry - if (s_restartable) { - CThread::sleep(e.getRetryTime()); + if (ARG->m_restartable) { + ARCH->sleep(e.getRetryTime()); } else { result = kExitFailed; @@ -189,8 +179,8 @@ realMain(CMutex* mutex) } catch (...) { // don't try to restart and fail - s_restartable = false; - result = kExitFailed; + ARG->m_restartable = false; + result = kExitFailed; FINALLY; } #undef FINALLY @@ -200,19 +190,10 @@ realMain(CMutex* mutex) } catch (XThread&) { // terminated - s_restartable = false; - result = kExitTerminated; + ARG->m_restartable = false; + result = kExitTerminated; } - catch (...) { - CLog::setLock(NULL); - s_logMutex = NULL; - throw; - } - } while (s_restartable); - - // clean up - CLog::setLock(NULL); - s_logMutex = NULL; + } while (ARG->m_restartable); return result; } @@ -233,7 +214,7 @@ version() LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n" "%s", - pname, + ARG->m_pname, kVersion, kProtocolMajorVersion, kProtocolMinorVersion, @@ -279,7 +260,7 @@ help() "\n" "Where log messages go depends on the platform and whether or not the\n" "client is running as a daemon.", - pname, kDefaultPort)); + ARG->m_pname, kDefaultPort)); } @@ -294,7 +275,7 @@ isArg(int argi, int argc, const char** argv, // match. check args left. if (argi + minRequiredParameters >= argc) { LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, - pname, argv[argi], pname)); + ARG->m_pname, argv[argi], ARG->m_pname)); bye(kExitArgs); } return true; @@ -308,61 +289,58 @@ static void parse(int argc, const char** argv) { - assert(pname != NULL); - assert(argv != NULL); - assert(argc >= 1); + assert(ARG->m_pname != NULL); + assert(argv != NULL); + assert(argc >= 1); // set defaults - char hostname[256]; - if (CNetwork::gethostname(hostname, sizeof(hostname)) != CNetwork::Error) { - s_name = hostname; - } + ARG->m_name = ARCH->getHostName(); // parse options int i; for (i = 1; i < argc; ++i) { if (isArg(i, argc, argv, "-d", "--debug", 1)) { // change logging level - s_logFilter = argv[++i]; + ARG->m_logFilter = argv[++i]; } else if (isArg(i, argc, argv, "-n", "--name", 1)) { // save screen name - s_name = argv[++i]; + ARG->m_name = argv[++i]; } else if (isArg(i, argc, argv, NULL, "--camp")) { // enable camping - s_camp = true; + ARG->m_camp = true; } else if (isArg(i, argc, argv, NULL, "--no-camp")) { // disable camping - s_camp = false; + ARG->m_camp = false; } else if (isArg(i, argc, argv, "-f", "--no-daemon")) { // not a daemon - s_daemon = false; + ARG->m_daemon = false; } else if (isArg(i, argc, argv, NULL, "--daemon")) { // daemonize - s_daemon = true; + ARG->m_daemon = true; } else if (isArg(i, argc, argv, "-1", "--no-restart")) { // don't try to restart - s_restartable = false; + ARG->m_restartable = false; } else if (isArg(i, argc, argv, NULL, "--restart")) { // try to restart - s_restartable = true; + ARG->m_restartable = true; } else if (isArg(i, argc, argv, "-z", NULL)) { - s_backend = true; + ARG->m_backend = true; } else if (isArg(i, argc, argv, "-h", "--help")) { @@ -383,7 +361,7 @@ parse(int argc, const char** argv) else if (argv[i][0] == '-') { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - pname, argv[i], pname)); + ARG->m_pname, argv[i], ARG->m_pname)); bye(kExitArgs); } @@ -396,49 +374,60 @@ parse(int argc, const char** argv) // exactly one non-option argument (server-address) if (i == argc) { LOG((CLOG_PRINT "%s: a server address or name is required" BYE, - pname, pname)); + ARG->m_pname, ARG->m_pname)); bye(kExitArgs); } if (i + 1 != argc) { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - pname, argv[i], pname)); + ARG->m_pname, argv[i], ARG->m_pname)); bye(kExitArgs); } // save server address try { - s_serverAddress = CNetworkAddress(argv[i], kDefaultPort); + ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); } catch (XSocketAddress& e) { LOG((CLOG_PRINT "%s: %s" BYE, - pname, e.what(), pname)); + ARG->m_pname, e.what(), ARG->m_pname)); bye(kExitFailed); } // increase default filter level for daemon. the user must // explicitly request another level for a daemon. - if (s_daemon && s_logFilter == NULL) { + if (ARG->m_daemon && ARG->m_logFilter == NULL) { #if WINDOWS_LIKE - if (CPlatform::isWindows95Family()) { + if (CArchMiscWindows::isWindows95Family()) { // windows 95 has no place for logging so avoid showing // the log console window. - s_logFilter = "FATAL"; + ARG->m_logFilter = "FATAL"; } else #endif { - s_logFilter = "NOTE"; + ARG->m_logFilter = "NOTE"; } } // set log filter - if (!CLog::setFilter(s_logFilter)) { + if (!CLOG->setFilter(ARG->m_logFilter)) { LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, - pname, s_logFilter, pname)); + ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); bye(kExitArgs); } } +static +void +useSystemLog() +{ + // redirect log messages + ILogOutputter* logger = new CSystemLogOutputter; + logger->open(DAEMON_NAME); + CLOG->insert(new CStopLogOutputter); + CLOG->insert(logger); +} + // // platform dependent entry points // @@ -449,24 +438,41 @@ parse(int argc, const char** argv) static bool s_hasImportantLogMessages = false; -static +// +// CMessageBoxOutputter +// +// This class writes severe log messages to a message box +// + +class CMessageBoxOutputter : public ILogOutputter { +public: + CMessageBoxOutputter() { } + virtual ~CMessageBoxOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } +}; + bool -logMessageBox(int priority, const char* msg) +CMessageBoxOutputter::write(ELevel level, const char* message) { // note any important messages the user may need to know about - if (priority <= CLog::kWARNING) { + if (level <= CLog::kWARNING) { s_hasImportantLogMessages = true; } // FATAL and PRINT messages get a dialog box if not running as // backend. if we're running as a backend the user will have // a chance to see the messages when we exit. - if (!s_backend && priority <= CLog::kFATAL) { - MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); - return true; + if (!ARG->m_backend && level <= CLog::kFATAL) { + MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING); + return false; } else { - return false; + return true; } } @@ -474,7 +480,7 @@ static void byeThrow(int x) { - throw CWin32Platform::CDaemonFailed(x); + CArchMiscWindows::daemonFailed(x); } static @@ -486,10 +492,9 @@ daemonStop(void) static int -daemonStartup(IPlatform* iplatform, int argc, const char** argv) +daemonStartup(int argc, const char** argv) { - // get platform pointer - CWin32Platform* platform = static_cast(iplatform); + useSystemLog(); // catch errors that would normally exit bye = &byeThrow; @@ -498,35 +503,35 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) parse(argc, argv); // cannot run as backend if running as a service - s_backend = false; + ARG->m_backend = false; // run as a service - return platform->runDaemon(realMain, daemonStop); + return CArchMiscWindows::runDaemon(realMain, daemonStop); } static int -daemonStartup95(IPlatform*, int, const char**) +daemonStartup95(int, const char**) { - return realMain(NULL); + useSystemLog(); + return realMain(); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { - CPlatform platform; + CArch arch; + CLOG; + CArgs args; // save instance CMSWindowsScreen::init(instance); // get program name - pname = platform.getBasename(__argv[0]); - - // initialize network library - CNetwork::init(); + ARG->m_pname = ARCH->getBasename(__argv[0]); // send PRINT and FATAL output to a message box - CLog::setOutputter(&logMessageBox); + CLOG->insert(new CMessageBoxOutputter); // windows NT family starts services using no command line options. // since i'm not sure how to tell the difference between that and @@ -534,10 +539,10 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // arguments and we're on NT then we're being invoked as a service. // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. - if (__argc <= 1 && !CWin32Platform::isWindows95Family()) { - int result = platform.daemonize(DAEMON_NAME, &daemonStartup); + if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + int result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); if (result == -1) { - LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); + LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); return kExitFailed; } return result; @@ -548,38 +553,36 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // daemonize if requested int result; - if (s_daemon) { - // redirect log messages - platform.installDaemonLogger(DAEMON_NAME); - + if (ARG->m_daemon) { // start as a daemon - if (CWin32Platform::isWindows95Family()) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup95); - if (result == -1) { - LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); + if (CArchMiscWindows::isWindows95Family()) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup95); + } + catch (XArchDaemon&) { + LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); result = kExitFailed; } } else { // cannot start a service from the command line so just // run normally (except with log messages redirected). - result = realMain(NULL); + useSystemLog(); + result = realMain(); } } else { // run - result = realMain(NULL); + result = realMain(); } - CNetwork::cleanup(); - // let user examine any messages if we're running as a backend // by putting up a dialog box before exiting. - if (s_backend && s_hasImportantLogMessages) { + if (ARG->m_backend && s_hasImportantLogMessages) { char msg[1024]; msg[0] = '\0'; LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); - MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); + MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); } return result; @@ -589,40 +592,40 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) static int -daemonStartup(IPlatform*, int, const char**) +daemonStartup(int, const char**) { - return realMain(NULL); + useSystemLog(); + return realMain(); } int main(int argc, char** argv) { - CPlatform platform; + CArch arch; + CLOG; + CArgs args; // get program name - pname = platform.getBasename(argv[0]); - - // initialize network library - CNetwork::init(); + ARG->m_pname = ARCH->getBasename(argv[0]); // parse command line parse(argc, const_cast(argv)); // daemonize if requested int result; - if (s_daemon) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup); - if (result == -1) { + if (ARG->m_daemon) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon&) { LOG((CLOG_CRIT "failed to daemonize")); - return kExitFailed; + result = kExitFailed; } } else { - result = realMain(NULL); + result = realMain(); } - CNetwork::cleanup(); - return result; } diff --git a/cmd/synergyc/synergyc.dsp b/cmd/synergyc/synergyc.dsp index ad4e1f1c..e99b0910 100644 --- a/cmd/synergyc/synergyc.dsp +++ b/cmd/synergyc/synergyc.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -54,7 +54,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../../Release/synergyc.exe" +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 !ELSEIF "$(CFG)" == "synergyc - Win32 Debug" @@ -70,7 +70,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -81,7 +81,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../../Debug/synergyc.exe" /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept !ENDIF diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index 9a200c51..8da55b4b 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -39,6 +39,7 @@ synergys_LDADD = \ $(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/arch/libarch.a \ $(X_LIBS) \ $(X_PRE_LIBS) \ -lXtst \ @@ -47,6 +48,8 @@ synergys_LDADD = \ $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 522f6710..edc09560 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -15,11 +15,9 @@ #include "CServer.h" #include "CConfig.h" #include "IPrimaryScreenFactory.h" -#include "CPlatform.h" #include "ProtocolTypes.h" #include "Version.h" #include "XScreen.h" -#include "CNetwork.h" #include "CTCPSocketFactory.h" #include "XSocket.h" #include "CLock.h" @@ -27,12 +25,18 @@ #include "CThread.h" #include "XThread.h" #include "CLog.h" +#include "LogOutputters.h" +#include "CArch.h" +#include "CArchMiscWindows.h" #include "stdfstream.h" #include +#define DAEMON_RUNNING(running_) #if WINDOWS_LIKE #include "CMSWindowsPrimaryScreen.h" #include "resource.h" +#undef DAEMON_RUNNING +#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #elif UNIX_LIKE #include "CXWindowsPrimaryScreen.h" #endif @@ -57,37 +61,35 @@ // program arguments // -static const char* pname = NULL; -static bool s_backend = false; -static bool s_restartable = true; -static bool s_daemon = true; -static const char* s_configFile = NULL; -static const char* s_logFilter = NULL; -static CString s_name; -static CNetworkAddress s_synergyAddress; -static CNetworkAddress s_httpAddress; -static CConfig s_config; +#define ARG CArgs::s_instance +class CArgs { +public: + CArgs() : + m_pname(NULL), + m_backend(false), + m_restartable(true), + m_daemon(true), + m_configFile(NULL), + m_logFilter(NULL) + { s_instance = this; } + ~CArgs() { s_instance = NULL; } -// -// logging thread safety -// +public: + static CArgs* s_instance; + const char* m_pname; + bool m_backend; + bool m_restartable; + bool m_daemon; + const char* m_configFile; + const char* m_logFilter; + CString m_name; + CNetworkAddress m_synergyAddress; + CNetworkAddress m_httpAddress; + CConfig m_config; +}; -static CMutex* s_logMutex = NULL; - -static -void -logLock(bool lock) -{ - assert(s_logMutex != NULL); - - if (lock) { - s_logMutex->lock(); - } - else { - s_logMutex->unlock(); - } -} +CArgs* CArgs::s_instance = NULL; // @@ -124,18 +126,8 @@ static CServer* s_server = NULL; static int -realMain(CMutex* mutex) +realMain(void) { - // caller should have mutex locked on entry - - // initialize threading library - CThread::init(); - - // make logging thread safe - CMutex logMutex; - s_logMutex = &logMutex; - CLog::setLock(&logLock); - int result = kExitSuccess; do { bool opened = false; @@ -143,28 +135,28 @@ realMain(CMutex* mutex) try { // if configuration has no screens then add this system // as the default - if (s_config.begin() == s_config.end()) { - s_config.addScreen(s_name); + if (ARG->m_config.begin() == ARG->m_config.end()) { + ARG->m_config.addScreen(ARG->m_name); } // set the contact address, if provided, in the config. // otherwise, if the config doesn't have an address, use // the default. - if (s_synergyAddress.isValid()) { - s_config.setSynergyAddress(s_synergyAddress); + if (ARG->m_synergyAddress.isValid()) { + ARG->m_config.setSynergyAddress(ARG->m_synergyAddress); } - else if (!s_config.getSynergyAddress().isValid()) { - s_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); + else if (!ARG->m_config.getSynergyAddress().isValid()) { + ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); } // set HTTP address if provided - if (s_httpAddress.isValid()) { - s_config.setHTTPAddress(s_httpAddress); + if (ARG->m_httpAddress.isValid()) { + ARG->m_config.setHTTPAddress(ARG->m_httpAddress); } // create server - s_server = new CServer(s_name); - s_server->setConfig(s_config); + s_server = new CServer(ARG->m_name); + s_server->setConfig(ARG->m_config); s_server->setScreenFactory(new CPrimaryScreenFactory); s_server->setSocketFactory(new CTCPSocketFactory); s_server->setStreamFilterFactory(NULL); @@ -174,18 +166,16 @@ realMain(CMutex* mutex) s_server->open(); opened = true; - // run server (unlocked) - if (mutex != NULL) { - mutex->unlock(); - } + // run server + DAEMON_RUNNING(true); locked = false; s_server->mainLoop(); locked = true; // clean up #define FINALLY do { \ - if (!locked && mutex != NULL) { \ - mutex->lock(); \ + if (!locked) { \ + DAEMON_RUNNING(false); \ } \ if (s_server != NULL) { \ if (opened) { \ @@ -199,8 +189,8 @@ realMain(CMutex* mutex) } catch (XScreenUnavailable& e) { // wait before retrying if we're going to retry - if (s_restartable) { - CThread::sleep(e.getRetryTime()); + if (ARG->m_restartable) { + ARCH->sleep(e.getRetryTime()); } else { result = kExitFailed; @@ -213,8 +203,8 @@ realMain(CMutex* mutex) } catch (...) { // don't try to restart and fail - s_restartable = false; - result = kExitFailed; + ARG->m_restartable = false; + result = kExitFailed; FINALLY; } #undef FINALLY @@ -224,19 +214,10 @@ realMain(CMutex* mutex) } catch (XThread&) { // terminated - s_restartable = false; - result = kExitTerminated; + ARG->m_restartable = false; + result = kExitTerminated; } - catch (...) { - CLog::setLock(NULL); - s_logMutex = NULL; - throw; - } - } while (s_restartable); - - // clean up - CLog::setLock(NULL); - s_logMutex = NULL; + } while (ARG->m_restartable); return result; } @@ -257,7 +238,7 @@ version() LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n" "%s", - pname, + ARG->m_pname, kVersion, kProtocolMajorVersion, kProtocolMinorVersion, @@ -287,8 +268,6 @@ help() #endif - CPlatform platform; - LOG((CLOG_PRINT "Usage: %s" " [--address
]" @@ -333,13 +312,13 @@ PLATFORM_EXTRA "\n" "Where log messages go depends on the platform and whether or not the\n" "server is running as a daemon.", - pname, + ARG->m_pname, kDefaultPort, - platform.addPathComponent( - platform.getUserDirectory(), + ARCH->concatPath( + ARCH->getUserDirectory(), USR_CONFIG_NAME).c_str(), - platform.addPathComponent( - platform.getSystemDirectory(), + ARCH->concatPath( + ARCH->getSystemDirectory(), SYS_CONFIG_NAME).c_str())); } @@ -354,7 +333,7 @@ isArg(int argi, int argc, const char** argv, // match. check args left. if (argi + minRequiredParameters >= argc) { LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, - pname, argv[argi], pname)); + ARG->m_pname, argv[argi], ARG->m_pname)); bye(kExitArgs); } return true; @@ -368,32 +347,30 @@ static void parse(int argc, const char** argv) { - assert(pname != NULL); - assert(argv != NULL); - assert(argc >= 1); + assert(ARG->m_pname != NULL); + assert(argv != NULL); + assert(argc >= 1); // set defaults - char hostname[256]; - if (CNetwork::gethostname(hostname, sizeof(hostname)) != CNetwork::Error) { - s_name = hostname; - } + ARG->m_name = ARCH->getHostName(); // parse options int i; for (i = 1; i < argc; ++i) { if (isArg(i, argc, argv, "-d", "--debug", 1)) { // change logging level - s_logFilter = argv[++i]; + ARG->m_logFilter = argv[++i]; } else if (isArg(i, argc, argv, "-a", "--address", 1)) { // save listen address try { - s_synergyAddress = CNetworkAddress(argv[i + 1], kDefaultPort); + ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], + kDefaultPort); } catch (XSocketAddress& e) { LOG((CLOG_PRINT "%s: %s" BYE, - pname, e.what(), pname)); + ARG->m_pname, e.what(), ARG->m_pname)); bye(kExitArgs); } ++i; @@ -402,11 +379,12 @@ parse(int argc, const char** argv) else if (isArg(i, argc, argv, NULL, "--http", 1)) { // save listen address try { - s_httpAddress = CNetworkAddress(argv[i + 1], kDefaultPort + 1); + ARG->m_httpAddress = CNetworkAddress(argv[i + 1], + kDefaultPort + 1); } catch (XSocketAddress& e) { LOG((CLOG_PRINT "%s: %s" BYE, - pname, e.what(), pname)); + ARG->m_pname, e.what(), ARG->m_pname)); bye(kExitArgs); } ++i; @@ -414,36 +392,36 @@ parse(int argc, const char** argv) else if (isArg(i, argc, argv, "-n", "--name", 1)) { // save screen name - s_name = argv[++i]; + ARG->m_name = argv[++i]; } else if (isArg(i, argc, argv, "-c", "--config", 1)) { // save configuration file path - s_configFile = argv[++i]; + ARG->m_configFile = argv[++i]; } else if (isArg(i, argc, argv, "-f", "--no-daemon")) { // not a daemon - s_daemon = false; + ARG->m_daemon = false; } else if (isArg(i, argc, argv, NULL, "--daemon")) { // daemonize - s_daemon = true; + ARG->m_daemon = true; } else if (isArg(i, argc, argv, "-1", "--no-restart")) { // don't try to restart - s_restartable = false; + ARG->m_restartable = false; } else if (isArg(i, argc, argv, NULL, "--restart")) { // try to restart - s_restartable = true; + ARG->m_restartable = true; } else if (isArg(i, argc, argv, "-z", NULL)) { - s_backend = true; + ARG->m_backend = true; } else if (isArg(i, argc, argv, "-h", "--help")) { @@ -464,7 +442,7 @@ parse(int argc, const char** argv) else if (argv[i][0] == '-') { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - pname, argv[i], pname)); + ARG->m_pname, argv[i], ARG->m_pname)); bye(kExitArgs); } @@ -477,30 +455,30 @@ parse(int argc, const char** argv) // no non-option arguments are allowed if (i != argc) { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - pname, argv[i], pname)); + ARG->m_pname, argv[i], ARG->m_pname)); bye(kExitArgs); } // increase default filter level for daemon. the user must // explicitly request another level for a daemon. - if (s_daemon && s_logFilter == NULL) { + if (ARG->m_daemon && ARG->m_logFilter == NULL) { #if WINDOWS_LIKE - if (CPlatform::isWindows95Family()) { + if (CArchMiscWindows::isWindows95Family()) { // windows 95 has no place for logging so avoid showing // the log console window. - s_logFilter = "FATAL"; + ARG->m_logFilter = "FATAL"; } else #endif { - s_logFilter = "NOTE"; + ARG->m_logFilter = "NOTE"; } } // set log filter - if (!CLog::setFilter(s_logFilter)) { + if (!CLOG->setFilter(ARG->m_logFilter)) { LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, - pname, s_logFilter, pname)); + ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); bye(kExitArgs); } } @@ -518,14 +496,14 @@ loadConfig(const char* pathname, bool require) if (!configStream) { throw XConfigRead("cannot open file"); } - configStream >> s_config; + configStream >> ARG->m_config; LOG((CLOG_DEBUG "configuration read successfully")); return true; } catch (XConfigRead& e) { if (require) { LOG((CLOG_PRINT "%s: cannot read configuration '%s': %s", - pname, pathname, e.what())); + ARG->m_pname, pathname, e.what())); bye(kExitConfig); } else { @@ -541,36 +519,46 @@ void loadConfig() { // load the config file, if specified - if (s_configFile != NULL) { + if (ARG->m_configFile != NULL) { // require the user specified file to load correctly - loadConfig(s_configFile, true); + loadConfig(ARG->m_configFile, true); } // load the default configuration if no explicit file given else { // get the user's home directory. use the effective user id // so a user can't get a setuid root program to load his file. - CPlatform platform; bool loaded = false; - CString path = platform.getUserDirectory(); + CString path = ARCH->getUserDirectory(); if (!path.empty()) { // complete path - path = platform.addPathComponent(path, USR_CONFIG_NAME); + path = ARCH->concatPath(path, USR_CONFIG_NAME); // now try loading the user's configuration loaded = loadConfig(path.c_str(), false); } if (!loaded) { // try the system-wide config file - path = platform.getSystemDirectory(); + path = ARCH->getSystemDirectory(); if (!path.empty()) { - path = platform.addPathComponent(path, SYS_CONFIG_NAME); + path = ARCH->concatPath(path, SYS_CONFIG_NAME); loadConfig(path.c_str(), false); } } } } +static +void +useSystemLog() +{ + // redirect log messages + ILogOutputter* logger = new CSystemLogOutputter; + logger->open(DAEMON_NAME); + CLOG->insert(new CStopLogOutputter); + CLOG->insert(logger); +} + // // platform dependent entry points // @@ -581,24 +569,41 @@ loadConfig() static bool s_hasImportantLogMessages = false; -static +// +// CMessageBoxOutputter +// +// This class writes severe log messages to a message box +// + +class CMessageBoxOutputter : public ILogOutputter { +public: + CMessageBoxOutputter() { } + virtual ~CMessageBoxOutputter() { } + + // ILogOutputter overrides + virtual void open(const char*) { } + virtual void close() { } + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const { return ""; } +}; + bool -logMessageBox(int priority, const char* msg) +CMessageBoxOutputter::write(ELevel level, const char* message) { // note any important messages the user may need to know about - if (priority <= CLog::kWARNING) { + if (level <= CLog::kWARNING) { s_hasImportantLogMessages = true; } // FATAL and PRINT messages get a dialog box if not running as // backend. if we're running as a backend the user will have // a chance to see the messages when we exit. - if (!s_backend && priority <= CLog::kFATAL) { - MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); - return true; + if (!ARG->m_backend && level <= CLog::kFATAL) { + MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING); + return false; } else { - return false; + return true; } } @@ -606,7 +611,7 @@ static void byeThrow(int x) { - throw CWin32Platform::CDaemonFailed(x); + CArchMiscWindows::daemonFailed(x); } static @@ -618,10 +623,9 @@ daemonStop(void) static int -daemonStartup(IPlatform* iplatform, int argc, const char** argv) +daemonStartup(int argc, const char** argv) { - // get platform pointer - CWin32Platform* platform = static_cast(iplatform); + useSystemLog(); // catch errors that would normally exit bye = &byeThrow; @@ -630,38 +634,38 @@ daemonStartup(IPlatform* iplatform, int argc, const char** argv) parse(argc, argv); // cannot run as backend if running as a service - s_backend = false; + ARG->m_backend = false; // load configuration loadConfig(); // run as a service - return platform->runDaemon(realMain, daemonStop); + return CArchMiscWindows::runDaemon(realMain, daemonStop); } static int -daemonStartup95(IPlatform*, int, const char**) +daemonStartup95(int, const char**) { - return realMain(NULL); + useSystemLog(); + return realMain(); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { - CPlatform platform; + CArch arch; + CLOG; + CArgs args; // save instance CMSWindowsScreen::init(instance); // get program name - pname = platform.getBasename(__argv[0]); - - // initialize network library - CNetwork::init(); + ARG->m_pname = ARCH->getBasename(__argv[0]); // send PRINT and FATAL output to a message box - CLog::setOutputter(&logMessageBox); + CLOG->insert(new CMessageBoxOutputter); // windows NT family starts services using no command line options. // since i'm not sure how to tell the difference between that and @@ -669,13 +673,14 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // arguments and we're on NT then we're being invoked as a service. // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. - if (__argc <= 1 && !CWin32Platform::isWindows95Family()) { - int result = platform.daemonize(DAEMON_NAME, &daemonStartup); - if (result == -1) { - LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); + if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon&) { + LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); return kExitFailed; } - return result; } // parse command line @@ -686,38 +691,36 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // daemonize if requested int result; - if (s_daemon) { - // redirect log messages - platform.installDaemonLogger(DAEMON_NAME); - + if (ARG->m_daemon) { // start as a daemon - if (CWin32Platform::isWindows95Family()) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup95); - if (result == -1) { - LOG((CLOG_CRIT "failed to start as a service" BYE, pname)); + if (CArchMiscWindows::isWindows95Family()) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup95); + } + catch (XArchDaemon&) { + LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); result = kExitFailed; } } else { // cannot start a service from the command line so just // run normally (except with log messages redirected). - result = realMain(NULL); + useSystemLog(); + result = realMain(); } } else { // run - result = realMain(NULL); + result = realMain(); } - CNetwork::cleanup(); - // let user examine any messages if we're running as a backend // by putting up a dialog box before exiting. - if (s_backend && s_hasImportantLogMessages) { + if (ARG->m_backend && s_hasImportantLogMessages) { char msg[1024]; msg[0] = '\0'; LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); - MessageBox(NULL, msg, pname, MB_OK | MB_ICONWARNING); + MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); } return result; @@ -727,21 +730,21 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) static int -daemonStartup(IPlatform*, int, const char**) +daemonStartup(int, const char**) { - return realMain(NULL); + useSystemLog(); + return realMain(); } int main(int argc, char** argv) { - CPlatform platform; + CArch arch; + CLOG; + CArgs args; // get program name - pname = platform.getBasename(argv[0]); - - // initialize network library - CNetwork::init(); + ARG->m_pname = ARCH->getBasename(argv[0]); // parse command line parse(argc, const_cast(argv)); @@ -751,19 +754,19 @@ main(int argc, char** argv) // daemonize if requested int result; - if (s_daemon) { - result = platform.daemonize(DAEMON_NAME, &daemonStartup); - if (result == -1) { + if (ARG->m_daemon) { + try { + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon&) { LOG((CLOG_CRIT "failed to daemonize")); - return kExitFailed; + result = kExitFailed; } } else { - result = realMain(NULL); + result = realMain(); } - CNetwork::cleanup(); - return result; } diff --git a/cmd/synergys/synergys.dsp b/cmd/synergys/synergys.dsp index c9853f5d..39accfa4 100644 --- a/cmd/synergys/synergys.dsp +++ b/cmd/synergys/synergys.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -70,7 +70,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/configure.in b/configure.in index 6064df92..bc3e6222 100644 --- a/configure.in +++ b/configure.in @@ -13,7 +13,7 @@ dnl GNU General Public License for more details. dnl Process this file with autoconf to produce a configure script. dnl initialize -AC_INIT(lib/base/common.h) +AC_INIT(lib/common/common.h) AC_CONFIG_AUX_DIR(config) dnl current version @@ -99,7 +99,9 @@ LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" AC_OUTPUT([ Makefile lib/Makefile +lib/arch/Makefile lib/base/Makefile +lib/common/Makefile lib/mt/Makefile lib/io/Makefile lib/http/Makefile diff --git a/lib/Makefile.am b/lib/Makefile.am index 5b23e06b..cababc3e 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -16,6 +16,8 @@ DEPTH = .. VDEPTH = ./$(VPATH)/$(DEPTH) SUBDIRS = \ + common \ + arch \ base \ mt \ io \ diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp new file mode 100644 index 00000000..e5e8f660 --- /dev/null +++ b/lib/arch/CArch.cpp @@ -0,0 +1,576 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "common.h" +#include "CArch.h" + +#undef ARCH_CONSOLE +#undef ARCH_DAEMON +#undef ARCH_FILE +#undef ARCH_LOG +#undef ARCH_MULTITHREAD +#undef ARCH_NETWORK +#undef ARCH_SLEEP +#undef ARCH_STRING +#undef ARCH_TIME + +// include appropriate architecture implementation +#if WINDOWS_LIKE +# include "CArchConsoleWindows.h" +# include "CArchDaemonWindows.h" +# include "CArchFileWindows.h" +# include "CArchLogWindows.h" +# include "CArchMultithreadWindows.h" +# include "CArchNetworkWinsock.h" +# include "CArchSleepWindows.h" +# include "CArchStringWindows.h" +# include "CArchTimeWindows.h" +#elif UNIX_LIKE +# include "CArchConsoleUnix.h" +# include "CArchDaemonUnix.h" +# include "CArchFileUnix.h" +# include "CArchLogUnix.h" +# if HAVE_PTHREAD +# include "CArchMultithreadPosix.h" +# endif +# include "CArchNetworkBSD.h" +# include "CArchSleepUnix.h" +# include "CArchStringUnix.h" +# include "CArchTimeUnix.h" +#endif + +#if !defined(ARCH_CONSOLE) +# error unsupported platform for console +#endif + +#if !defined(ARCH_DAEMON) +# error unsupported platform for daemon +#endif + +#if !defined(ARCH_FILE) +# error unsupported platform for file +#endif + +#if !defined(ARCH_LOG) +# error unsupported platform for logging +#endif + +#if !defined(ARCH_MULTITHREAD) +# error unsupported platform for multithreading +#endif + +#if !defined(ARCH_NETWORK) +# error unsupported platform for network +#endif + +#if !defined(ARCH_SLEEP) +# error unsupported platform for sleep +#endif + +#if !defined(ARCH_STRING) +# error unsupported platform for string +#endif + +#if !defined(ARCH_TIME) +# error unsupported platform for time +#endif + +// +// CArch +// + +CArch* CArch::s_instance = NULL; + +CArch::CArch(ARCH_ARGS) +{ + // only once instance of CArch + assert(s_instance == NULL); + s_instance = this; + + // create architecture implementation objects + m_mt = new ARCH_MULTITHREAD; + m_file = new ARCH_FILE; + m_log = new ARCH_LOG; + m_net = new ARCH_NETWORK; + m_sleep = new ARCH_SLEEP; + m_string = new ARCH_STRING; + m_time = new ARCH_TIME; + m_console = new ARCH_CONSOLE; + m_daemon = new ARCH_DAEMON; +} + +CArch::~CArch() +{ + // clean up + delete m_daemon; + delete m_console; + delete m_time; + delete m_string; + delete m_sleep; + delete m_net; + delete m_log; + delete m_file; + delete m_mt; + + // no instance + s_instance = NULL; +} + +CArch* +CArch::getInstance() +{ + return s_instance; +} + +void +CArch::openConsole(const char* title) +{ + m_console->openConsole(title); +} + +void +CArch::closeConsole() +{ + m_console->closeConsole(); +} + +void +CArch::writeConsole(const char* str) +{ + m_console->writeConsole(str); +} + +const char* +CArch::getNewlineForConsole() +{ + return m_console->getNewlineForConsole(); +} + +void +CArch::installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) +{ + m_daemon->installDaemon(name, description, pathname, commandLine, allUsers); +} + +void +CArch::uninstallDaemon(const char* name, bool allUsers) +{ + m_daemon->uninstallDaemon(name, allUsers); +} + +int +CArch::daemonize(const char* name, DaemonFunc func) +{ + return m_daemon->daemonize(name, func); +} + +bool +CArch::canInstallDaemon(const char* name, bool allUsers) +{ + return m_daemon->canInstallDaemon(name, allUsers); +} + +bool +CArch::isDaemonInstalled(const char* name, bool allUsers) +{ + return m_daemon->isDaemonInstalled(name, allUsers); +} + +const char* +CArch::getBasename(const char* pathname) +{ + return m_file->getBasename(pathname); +} + +std::string +CArch::getUserDirectory() +{ + return m_file->getUserDirectory(); +} + +std::string +CArch::getSystemDirectory() +{ + return m_file->getSystemDirectory(); +} + +std::string +CArch::concatPath(const std::string& prefix, const std::string& suffix) +{ + return m_file->concatPath(prefix, suffix); +} + +void +CArch::openLog(const char* name) +{ + m_log->openLog(name); +} + +void +CArch::closeLog() +{ + m_log->closeLog(); +} + +void +CArch::writeLog(ELevel level, const char* msg) +{ + m_log->writeLog(level, msg); +} + +CArchCond +CArch::newCondVar() +{ + return m_mt->newCondVar(); +} + +void +CArch::closeCondVar(CArchCond cond) +{ + m_mt->closeCondVar(cond); +} + +void +CArch::signalCondVar(CArchCond cond) +{ + m_mt->signalCondVar(cond); +} + +void +CArch::broadcastCondVar(CArchCond cond) +{ + m_mt->broadcastCondVar(cond); +} + +bool +CArch::waitCondVar(CArchCond cond, CArchMutex mutex, double timeout) +{ + return m_mt->waitCondVar(cond, mutex, timeout); +} + +CArchMutex +CArch::newMutex() +{ + return m_mt->newMutex(); +} + +void +CArch::closeMutex(CArchMutex mutex) +{ + m_mt->closeMutex(mutex); +} + +void +CArch::lockMutex(CArchMutex mutex) +{ + m_mt->lockMutex(mutex); +} + +void +CArch::unlockMutex(CArchMutex mutex) +{ + m_mt->unlockMutex(mutex); +} + +CArchThread +CArch::newThread(ThreadFunc func, void* data) +{ + return m_mt->newThread(func, data); +} + +CArchThread +CArch::newCurrentThread() +{ + return m_mt->newCurrentThread(); +} + +CArchThread +CArch::copyThread(CArchThread thread) +{ + return m_mt->copyThread(thread); +} + +void +CArch::closeThread(CArchThread thread) +{ + m_mt->closeThread(thread); +} + +void +CArch::cancelThread(CArchThread thread) +{ + m_mt->cancelThread(thread); +} + +void +CArch::setPriorityOfThread(CArchThread thread, int n) +{ + m_mt->setPriorityOfThread(thread, n); +} + +void +CArch::testCancelThread() +{ + m_mt->testCancelThread(); +} + +bool +CArch::wait(CArchThread thread, double timeout) +{ + return m_mt->wait(thread, timeout); +} + +bool +CArch::waitForEvent(double timeout) +{ + return m_mt->waitForEvent(timeout); +} + +bool +CArch::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return m_mt->isSameThread(thread1, thread2); +} + +bool +CArch::isExitedThread(CArchThread thread) +{ + return m_mt->isExitedThread(thread); +} + +void* +CArch::getResultOfThread(CArchThread thread) +{ + return m_mt->getResultOfThread(thread); +} + +IArchMultithread::ThreadID +CArch::getIDOfThread(CArchThread thread) +{ + return m_mt->getIDOfThread(thread); +} + +CArchSocket +CArch::newSocket(EAddressFamily family, ESocketType type) +{ + return m_net->newSocket(family, type); +} + +CArchSocket +CArch::copySocket(CArchSocket s) +{ + return m_net->copySocket(s); +} + +void +CArch::closeSocket(CArchSocket s) +{ + m_net->closeSocket(s); +} + +void +CArch::closeSocketForRead(CArchSocket s) +{ + m_net->closeSocketForRead(s); +} + +void +CArch::closeSocketForWrite(CArchSocket s) +{ + m_net->closeSocketForWrite(s); +} + +void +CArch::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + m_net->bindSocket(s, addr); +} + +void +CArch::listenOnSocket(CArchSocket s) +{ + m_net->listenOnSocket(s); +} + +CArchSocket +CArch::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + return m_net->acceptSocket(s, addr); +} + +void +CArch::connectSocket(CArchSocket s, CArchNetAddress name) +{ + m_net->connectSocket(s, name); +} + +int +CArch::pollSocket(CPollEntry pe[], int num, double timeout) +{ + return m_net->pollSocket(pe, num, timeout); +} + +size_t +CArch::readSocket(CArchSocket s, void* buf, size_t len) +{ + return m_net->readSocket(s, buf, len); +} + +size_t +CArch::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + return m_net->writeSocket(s, buf, len); +} + +void +CArch::throwErrorOnSocket(CArchSocket s) +{ + m_net->throwErrorOnSocket(s); +} + +bool +CArch::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + return m_net->setBlockingOnSocket(s, blocking); +} + +bool +CArch::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + return m_net->setNoDelayOnSocket(s, noDelay); +} + +std::string +CArch::getHostName() +{ + return m_net->getHostName(); +} + +CArchNetAddress +CArch::newAnyAddr(EAddressFamily family) +{ + return m_net->newAnyAddr(family); +} + +CArchNetAddress +CArch::copyAddr(CArchNetAddress addr) +{ + return m_net->copyAddr(addr); +} + +CArchNetAddress +CArch::nameToAddr(const std::string& name) +{ + return m_net->nameToAddr(name); +} + +void +CArch::closeAddr(CArchNetAddress addr) +{ + m_net->closeAddr(addr); +} + +std::string +CArch::addrToName(CArchNetAddress addr) +{ + return m_net->addrToName(addr); +} + +std::string +CArch::addrToString(CArchNetAddress addr) +{ + return m_net->addrToString(addr); +} + +IArchNetwork::EAddressFamily +CArch::getAddrFamily(CArchNetAddress addr) +{ + return m_net->getAddrFamily(addr); +} + +void +CArch::setAddrPort(CArchNetAddress addr, int port) +{ + m_net->setAddrPort(addr, port); +} + +int +CArch::getAddrPort(CArchNetAddress addr) +{ + return m_net->getAddrPort(addr); +} + +bool +CArch::isAnyAddr(CArchNetAddress addr) +{ + return m_net->isAnyAddr(addr); +} + +void +CArch::sleep(double timeout) +{ + m_sleep->sleep(timeout); +} + +int +CArch::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + return m_string->vsnprintf(str, size, fmt, ap); +} + +CArchMBState +CArch::newMBState() +{ + return m_string->newMBState(); +} + +void +CArch::closeMBState(CArchMBState state) +{ + m_string->closeMBState(state); +} + +void +CArch::initMBState(CArchMBState state) +{ + m_string->initMBState(state); +} + +bool +CArch::isInitMBState(CArchMBState state) +{ + return m_string->isInitMBState(state); +} + +int +CArch::convMBToWC(wchar_t* dst, const char* src, int n, CArchMBState state) +{ + return m_string->convMBToWC(dst, src, n, state); +} + +int +CArch::convWCToMB(char* dst, wchar_t src, CArchMBState state) +{ + return m_string->convWCToMB(dst, src, state); +} + +double +CArch::time() +{ + return m_time->time(); +} diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h new file mode 100644 index 00000000..e12e33af --- /dev/null +++ b/lib/arch/CArch.h @@ -0,0 +1,163 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCH_H +#define CARCH_H + +#include "IArchConsole.h" +#include "IArchDaemon.h" +#include "IArchFile.h" +#include "IArchLog.h" +#include "IArchMultithread.h" +#include "IArchNetwork.h" +#include "IArchSleep.h" +#include "IArchString.h" +#include "IArchTime.h" + +#define ARCH (CArch::getInstance()) + +#define ARCH_ARGS + +class CArch : public IArchConsole, + public IArchDaemon, + public IArchFile, + public IArchLog, + public IArchMultithread, + public IArchNetwork, + public IArchSleep, + public IArchString, + public IArchTime { +public: + CArch(ARCH_ARGS); + ~CArch(); + + // + // accessors + // + + static CArch* getInstance(); + + // IArchConsole overrides + virtual void openConsole(const char*); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); + + // IArchLog overrides + virtual void openLog(const char*); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual bool waitForEvent(double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + + // IArchSleep overrides + virtual void sleep(double timeout); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); + + // IArchTime overrides + virtual double time(); + +private: + static CArch* s_instance; + + IArchConsole* m_console; + IArchDaemon* m_daemon; + IArchFile* m_file; + IArchLog* m_log; + IArchMultithread* m_mt; + IArchNetwork* m_net; + IArchSleep* m_sleep; + IArchString* m_string; + IArchTime* m_time; +}; + +#endif diff --git a/lib/arch/CArchConsoleUnix.cpp b/lib/arch/CArchConsoleUnix.cpp new file mode 100644 index 00000000..79c4ae2c --- /dev/null +++ b/lib/arch/CArchConsoleUnix.cpp @@ -0,0 +1,54 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchConsoleUnix.h" +#include + +// +// CArchConsoleUnix +// + +CArchConsoleUnix::CArchConsoleUnix() +{ + // do nothing +} + +CArchConsoleUnix::~CArchConsoleUnix() +{ + // do nothing +} + +void +CArchConsoleUnix::openConsole(const char*) +{ + // do nothing +} + +void +CArchConsoleUnix::closeConsole() +{ + // do nothing +} + +void +CArchConsoleUnix::writeConsole(const char* str) +{ + fprintf(stderr, "%s", str); +} + +const char* +CArchConsoleUnix::getNewlineForConsole() +{ + return "\n"; +} diff --git a/lib/arch/CArchConsoleUnix.h b/lib/arch/CArchConsoleUnix.h new file mode 100644 index 00000000..5beb8799 --- /dev/null +++ b/lib/arch/CArchConsoleUnix.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHCONSOLEUNIX_H +#define CARCHCONSOLEUNIX_H + +#include "IArchConsole.h" + +#define ARCH_CONSOLE CArchConsoleUnix + +class CArchConsoleUnix : public IArchConsole { +public: + CArchConsoleUnix(); + virtual ~CArchConsoleUnix(); + + // IArchConsole overrides + virtual void openConsole(const char* title); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); +}; + +#endif diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp new file mode 100644 index 00000000..c7afb95c --- /dev/null +++ b/lib/arch/CArchConsoleWindows.cpp @@ -0,0 +1,107 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchConsoleWindows.h" +#include "CArch.h" +#include + +// +// CArchConsoleWindows +// + +DWORD CArchConsoleWindows::s_thread = 0; + +CArchConsoleWindows::CArchConsoleWindows() : + m_output(NULL) +{ + s_thread = GetCurrentThreadId(); + + m_mutex = ARCH->newMutex(); +} + +CArchConsoleWindows::~CArchConsoleWindows() +{ + ARCH->closeMutex(m_mutex); +} + +void +CArchConsoleWindows::openConsole(const char* title) +{ + ARCH->lockMutex(m_mutex); + if (m_output == NULL) { + if (AllocConsole()) { + // get console output handle + m_output = GetStdHandle(STD_ERROR_HANDLE); + + // set console title + if (title != NULL) { + SetConsoleTitle(title); + } + + // prep console. windows 95 and its ilk have braindead + // consoles that can't even resize independently of the + // buffer size. use a 25 line buffer for those systems. + OSVERSIONINFO osInfo; + COORD size = { 80, 1000 }; + osInfo.dwOSVersionInfoSize = sizeof(osInfo); + if (GetVersionEx(&osInfo) && + osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + size.Y = 25; + SetConsoleScreenBufferSize(m_output, size); + SetConsoleTextAttribute(m_output, + FOREGROUND_RED | + FOREGROUND_GREEN | + FOREGROUND_BLUE); + + // catch console signals + SetConsoleCtrlHandler(&CArchConsoleWindows::signalHandler, TRUE); + + // reopen stderr to point at console + freopen("con", "w", stderr); + } + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchConsoleWindows::closeConsole() +{ + ARCH->lockMutex(m_mutex); + if (m_output != NULL) { + if (FreeConsole()) { + m_output = NULL; + } + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchConsoleWindows::writeConsole(const char* str) +{ + fprintf(stderr, "%s", str); +} + +const char* +CArchConsoleWindows::getNewlineForConsole() +{ + return "\r\n"; +} + +BOOL WINAPI +CArchConsoleWindows::signalHandler(DWORD) +{ + // terminate cleanly and skip remaining handlers + PostThreadMessage(s_thread, WM_QUIT, 0, 0); + return TRUE; +} diff --git a/lib/arch/CArchConsoleWindows.h b/lib/arch/CArchConsoleWindows.h new file mode 100644 index 00000000..d93555b4 --- /dev/null +++ b/lib/arch/CArchConsoleWindows.h @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHCONSOLEWINDOWS_H +#define CARCHCONSOLEWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchConsole.h" +#include "IArchMultithread.h" +#include + +#define ARCH_CONSOLE CArchConsoleWindows + +class CArchConsoleWindows : public IArchConsole { +public: + CArchConsoleWindows(); + virtual ~CArchConsoleWindows(); + + // IArchConsole overrides + virtual void openConsole(const char* title); + virtual void closeConsole(); + virtual void writeConsole(const char*); + virtual const char* getNewlineForConsole(); + +private: + static BOOL WINAPI signalHandler(DWORD); + +private: + static DWORD s_thread; + + CArchMutex m_mutex; + HANDLE m_output; +}; + +#endif diff --git a/lib/arch/CArchDaemonNone.cpp b/lib/arch/CArchDaemonNone.cpp new file mode 100644 index 00000000..35a58236 --- /dev/null +++ b/lib/arch/CArchDaemonNone.cpp @@ -0,0 +1,65 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonNone.h" + +// +// CArchDaemonNone +// + +CArchDaemonNone::CArchDaemonNone() +{ + // do nothing +} + +CArchDaemonNone::~CArchDaemonNone() +{ + // do nothing +} + +void +CArchDaemonNone::installDaemon(const char*, + const char*, + const char*, + const char*, + bool) +{ + // do nothing +} + +void +CArchDaemonNone::uninstallDaemon(const char*, bool) +{ + // do nothing +} + +int +CArchDaemonNone::daemonize(const char* name, DaemonFunc func) +{ + // simply forward the call to func. obviously, this doesn't + // do any daemonizing. + return func(1, &name); +} + +bool +CArchDaemonNone::canInstallDaemon(const char*, bool) +{ + return false; +} + +bool +CArchDaemonNone::isDaemonInstalled(const char* name, bool allUsers) +{ + return false; +} diff --git a/lib/arch/CArchDaemonNone.h b/lib/arch/CArchDaemonNone.h new file mode 100644 index 00000000..8e017bdb --- /dev/null +++ b/lib/arch/CArchDaemonNone.h @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONNONE_H +#define CARCHDAEMONNONE_H + +#include "IArchDaemon.h" + +#define ARCH_DAEMON CArchDaemonNone + +class CArchDaemonNone : public IArchDaemon { +public: + CArchDaemonNone(); + virtual ~CArchDaemonNone(); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); +}; + +#endif diff --git a/lib/arch/CArchDaemonUnix.cpp b/lib/arch/CArchDaemonUnix.cpp new file mode 100644 index 00000000..748780db --- /dev/null +++ b/lib/arch/CArchDaemonUnix.cpp @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonUnix.h" +#include "XArchUnix.h" +#include +#include +#include +#include +#include + +// we derive from CArchDaemonNone +#include "CArchDaemonNone.cpp" + +// +// CArchDaemonUnix +// + +CArchDaemonUnix::CArchDaemonUnix() +{ + // do nothing +} + +CArchDaemonUnix::~CArchDaemonUnix() +{ + // do nothing +} + +int +CArchDaemonUnix::daemonize(const char* name, DaemonFunc func) +{ + // fork so shell thinks we're done and so we're not a process + // group leader + switch (fork()) { + case -1: + // failed + throw XArchDaemonFailed(new XArchEvalUnix(errno)); + + case 0: + // child + break; + + default: + // parent exits + exit(0); + } + + // become leader of a new session + setsid(); + + // chdir to root so we don't keep mounted filesystems points busy + chdir("/"); + + // mask off permissions for any but owner + umask(077); + + // close open files. we only expect stdin, stdout, stderr to be open. + close(0); + close(1); + close(2); + + // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use + // of standard I/O safely goes in the bit bucket. + open("/dev/null", O_RDONLY); + open("/dev/null", O_RDWR); + dup(1); + + // invoke function + return func(1, &name); +} diff --git a/lib/arch/CArchDaemonUnix.h b/lib/arch/CArchDaemonUnix.h new file mode 100644 index 00000000..b4c82daa --- /dev/null +++ b/lib/arch/CArchDaemonUnix.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONUNIX_H +#define CARCHDAEMONUNIX_H + +#include "CArchDaemonNone.h" + +#undef ARCH_DAEMON +#define ARCH_DAEMON CArchDaemonUnix + +class CArchDaemonUnix : public CArchDaemonNone { +public: + CArchDaemonUnix(); + virtual ~CArchDaemonUnix(); + + // IArchDaemon overrides + virtual int daemonize(const char* name, DaemonFunc func); +}; + +#endif diff --git a/lib/platform/CWin32Platform.cpp b/lib/arch/CArchDaemonWindows.cpp similarity index 53% rename from lib/platform/CWin32Platform.cpp rename to lib/arch/CArchDaemonWindows.cpp index fc1fa4ae..c63d48ea 100644 --- a/lib/platform/CWin32Platform.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -12,103 +12,80 @@ * GNU General Public License for more details. */ -#include "CWin32Platform.h" -#include "CLock.h" -#include "CThread.h" -#include "CLog.h" -#include "TMethodJob.h" +#include "CArchDaemonWindows.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include "XArchWindows.h" #include "stdvector.h" -#include -#include -#include // -// CWin32Platform +// CArchDaemonWindows // -HANDLE CWin32Platform::s_eventLog = NULL; -CWin32Platform* CWin32Platform::s_daemonPlatform = NULL; +CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL; -CWin32Platform::CWin32Platform() +CArchDaemonWindows::CArchDaemonWindows() { // do nothing } -CWin32Platform::~CWin32Platform() +CArchDaemonWindows::~CArchDaemonWindows() { // do nothing } -bool -CWin32Platform::isWindows95Family() +int +CArchDaemonWindows::runDaemon(RunFunc runFunc, StopFunc stopFunc) { - OSVERSIONINFO version; - version.dwOSVersionInfoSize = sizeof(version); - if (GetVersionEx(&version) == 0) { - LOG((CLOG_WARN "cannot determine OS: %d", GetLastError())); - return true; + assert(s_daemon != NULL); + + return s_daemon->doRunDaemon(runFunc, stopFunc); +} + +void +CArchDaemonWindows::daemonRunning(bool running) +{ + // if s_daemon is NULL we assume we're running on the windows + // 95 family and we just ignore this call so the caller doesn't + // have to go through the trouble of not calling it on the + // windows 95 family. + if (s_daemon != NULL) { + s_daemon->doDaemonRunning(running); } - return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); } void -CWin32Platform::setStatus(SERVICE_STATUS_HANDLE handle, DWORD state) +CArchDaemonWindows::daemonFailed(int result) { - setStatus(handle, state, 0, 0); + // if s_daemon is NULL we assume we're running on the windows + // 95 family and we just ignore this call so the caller doesn't + // have to go through the trouble of not calling it on the + // windows 95 family. + if (s_daemon != NULL) { + throw XArchDaemonRunFailed(result); + } } void -CWin32Platform::setStatus(SERVICE_STATUS_HANDLE handle, - DWORD state, DWORD step, DWORD waitHint) -{ - SERVICE_STATUS status; - status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | - SERVICE_INTERACTIVE_PROCESS; - status.dwCurrentState = state; - status.dwControlsAccepted = SERVICE_ACCEPT_STOP | - SERVICE_ACCEPT_PAUSE_CONTINUE | - SERVICE_ACCEPT_SHUTDOWN; - status.dwWin32ExitCode = NO_ERROR; - status.dwServiceSpecificExitCode = 0; - status.dwCheckPoint = step; - status.dwWaitHint = waitHint; - SetServiceStatus(handle, &status); -} - -void -CWin32Platform::setStatusError(SERVICE_STATUS_HANDLE handle, DWORD error) -{ - SERVICE_STATUS status; - status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | - SERVICE_INTERACTIVE_PROCESS; - status.dwCurrentState = SERVICE_STOPPED; - status.dwControlsAccepted = SERVICE_ACCEPT_STOP | - SERVICE_ACCEPT_PAUSE_CONTINUE | - SERVICE_ACCEPT_SHUTDOWN; - status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; - status.dwServiceSpecificExitCode = error; - status.dwCheckPoint = 0; - status.dwWaitHint = 0; - SetServiceStatus(handle, &status); -} - -bool -CWin32Platform::installDaemon(const char* name, const char* description, - const char* pathname, const char* commandLine, bool allUsers) +CArchDaemonWindows::installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) { // if not for all users then use the user's autostart registry. // key. if windows 95 family then use windows 95 services key. - if (!allUsers || isWindows95Family()) { + if (!allUsers || CArchMiscWindows::isWindows95Family()) { // open registry - HKEY key = isWindows95Family() ? open95ServicesKey() : - openUserStartupKey(); + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); if (key == NULL) { - LOG((CLOG_ERR "cannot open registry key", GetLastError())); - return false; + // can't open key + throw XArchDaemonInstallFailed(new XArchEvalWindows); } // construct entry - CString value; + std::string value; value += "\""; value += pathname; value += "\" "; @@ -119,8 +96,6 @@ CWin32Platform::installDaemon(const char* name, const char* description, // clean up closeKey(key); - - return true; } // windows NT family services @@ -128,11 +103,11 @@ CWin32Platform::installDaemon(const char* name, const char* description, // open service manager SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); if (mgr == NULL) { - LOG((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); - return false; + // can't open service manager + throw XArchDaemonInstallFailed(new XArchEvalWindows); } - // create the servie + // create the service SC_HANDLE service = CreateService(mgr, name, name, @@ -147,60 +122,69 @@ CWin32Platform::installDaemon(const char* name, const char* description, NULL, NULL, NULL); + if (service == NULL) { + // can't create service + // FIXME -- handle ERROR_SERVICE_EXISTS + DWORD err = GetLastError(); + CloseServiceHandle(mgr); + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } // done with service and manager - if (service != NULL) { - CloseServiceHandle(service); - CloseServiceHandle(mgr); - } - else { -// FIXME -- handle ERROR_SERVICE_EXISTS - - LOG((CLOG_ERR "CreateService failed with %d", GetLastError())); - CloseServiceHandle(mgr); - return false; - } + CloseServiceHandle(service); + CloseServiceHandle(mgr); // open the registry key for this service HKEY key = openNTServicesKey(); key = openKey(key, name); if (key == NULL) { // can't open key - uninstallDaemon(name, allUsers); - return false; + DWORD err = GetLastError(); + try { + uninstallDaemon(name, allUsers); + } + catch (...) { + // ignore + } + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); } // set the description - setValue(key, "Description", description); + setValue(key, _T("Description"), description); // set command line - key = openKey(key, "Parameters"); + key = openKey(key, _T("Parameters")); if (key == NULL) { // can't open key - uninstallDaemon(name, allUsers); - return false; + DWORD err = GetLastError(); + closeKey(key); + try { + uninstallDaemon(name, allUsers); + } + catch (...) { + // ignore + } + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); } - setValue(key, "CommandLine", commandLine); + setValue(key, _T("CommandLine"), commandLine); // done with registry closeKey(key); - - return true; } } -IPlatform::EResult -CWin32Platform::uninstallDaemon(const char* name, bool allUsers) +void +CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) { // if not for all users then use the user's autostart registry. // key. if windows 95 family then use windows 95 services key. - if (!allUsers || isWindows95Family()) { + if (!allUsers || CArchMiscWindows::isWindows95Family()) { // open registry - HKEY key = isWindows95Family() ? open95ServicesKey() : - openUserStartupKey(); + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); if (key == NULL) { - LOG((CLOG_ERR "cannot open registry key", GetLastError())); - return kAlready; + // can't open key. daemon is probably not installed. + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows); } // remove entry @@ -208,97 +192,90 @@ CWin32Platform::uninstallDaemon(const char* name, bool allUsers) // clean up closeKey(key); - - return kSuccess; } // windows NT family services else { - // remove parameters for this service + // remove parameters for this service. ignore failures. HKEY key = openNTServicesKey(); key = openKey(key, name); if (key != NULL) { - deleteKey(key, "Parameters"); + deleteKey(key, _T("Parameters")); closeKey(key); } // open service manager SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); if (mgr == NULL) { - LOG((CLOG_ERR "OpenSCManager failed with %d", GetLastError())); - return kFailed; + // can't open service manager + throw XArchDaemonUninstallFailed(new XArchEvalWindows); } // open the service. oddly, you must open a service to delete it. - EResult result; SC_HANDLE service = OpenService(mgr, name, DELETE); if (service == NULL) { - const DWORD e = GetLastError(); - LOG((CLOG_ERR "OpenService failed with %d", e)); - result = (e == ERROR_SERVICE_DOES_NOT_EXIST) ? kAlready : kFailed; + DWORD err = GetLastError(); + CloseServiceHandle(mgr); + if (err != ERROR_SERVICE_DOES_NOT_EXIST) { + throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); + } + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); } - else { - if (DeleteService(service) != 0) { - result = kSuccess; - } - else { - const DWORD e = GetLastError(); - switch (e) { - case ERROR_SERVICE_MARKED_FOR_DELETE: - result = kAlready; - break; + // delete the service + const bool okay = (DeleteService(service) == 0); + const DWORD err = GetLastError(); - default: - result = kFailed; - break; - } - } - CloseServiceHandle(service); - } - - // close the manager + // clean up + CloseServiceHandle(service); CloseServiceHandle(mgr); - return result; + // handle failure. ignore error if service isn't installed anymore. + if (!okay && isDaemonInstalled(name, allUsers)) { + if (err != ERROR_SERVICE_MARKED_FOR_DELETE) { + throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); + } + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); + } } } int -CWin32Platform::daemonize(const char* name, DaemonFunc func) +CArchDaemonWindows::daemonize(const char* name, DaemonFunc func) { assert(name != NULL); assert(func != NULL); // windows 95 family services - if (isWindows95Family()) { + if (CArchMiscWindows::isWindows95Family()) { typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD); // mark this process as a service so it's not killed when the // user logs off. HINSTANCE kernel = LoadLibrary("kernel32.dll"); if (kernel == NULL) { - LOG((CLOG_ERR "LoadLibrary failed with %d", GetLastError())); - return -1; + throw XArchDaemonFailed(new XArchEvalWindows); } RegisterServiceProcessT RegisterServiceProcess = reinterpret_cast( GetProcAddress(kernel, - _T("RegisterServiceProcess"))); + "RegisterServiceProcess")); if (RegisterServiceProcess == NULL) { - LOG((CLOG_ERR "can't lookup RegisterServiceProcess: %d", GetLastError())); + // missing RegisterServiceProcess function + DWORD err = GetLastError(); FreeLibrary(kernel); - return -1; + throw XArchDaemonFailed(new XArchEvalWindows(err)); } if (RegisterServiceProcess(NULL, 1) == 0) { - LOG((CLOG_ERR "RegisterServiceProcess failed with %d", GetLastError())); + // RegisterServiceProcess failed + DWORD err = GetLastError(); FreeLibrary(kernel); - return -1; + throw XArchDaemonFailed(new XArchEvalWindows(err)); } FreeLibrary(kernel); // now simply call the daemon function - return func(this, 1, &name); + return func(1, &name); } // windows NT family services @@ -309,46 +286,33 @@ CWin32Platform::daemonize(const char* name, DaemonFunc func) // construct the service entry SERVICE_TABLE_ENTRY entry[2]; entry[0].lpServiceName = const_cast(name); - entry[0].lpServiceProc = &CWin32Platform::serviceMainEntry; + entry[0].lpServiceProc = &CArchDaemonWindows::serviceMainEntry; entry[1].lpServiceName = NULL; entry[1].lpServiceProc = NULL; // hook us up to the service control manager. this won't return // (if successful) until the processes have terminated. - s_daemonPlatform = this; + s_daemon = this; if (StartServiceCtrlDispatcher(entry)) { - s_daemonPlatform = NULL; - return m_daemonResult; + // StartServiceCtrlDispatcher failed + s_daemon = NULL; + throw XArchDaemonFailed(new XArchEvalWindows); } - LOG((CLOG_ERR "StartServiceCtrlDispatcher failed with %d", GetLastError())); - s_daemonPlatform = NULL; - return -1; - } -} -void -CWin32Platform::installDaemonLogger(const char* name) -{ - if (!CWin32Platform::isWindows95Family()) { - // open event log and direct log messages to it - if (s_eventLog == NULL) { - s_eventLog = RegisterEventSource(NULL, name); - if (s_eventLog != NULL) { - CLog::setOutputter(&CWin32Platform::serviceLogger); - } - } + s_daemon = NULL; + return m_daemonResult; } } bool -CWin32Platform::canInstallDaemon(const char* name, bool allUsers) const +CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) { // if not for all users then use the user's autostart registry. // key. if windows 95 family then use windows 95 services key. - if (!allUsers || isWindows95Family()) { + if (!allUsers || CArchMiscWindows::isWindows95Family()) { // check if we can open the registry key - HKEY key = isWindows95Family() ? open95ServicesKey() : - openUserStartupKey(); + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); closeKey(key); return (key != NULL); } @@ -365,7 +329,7 @@ CWin32Platform::canInstallDaemon(const char* name, bool allUsers) const // check if we can open the registry key for this service HKEY key = openNTServicesKey(); key = openKey(key, name); - key = openKey(key, "Parameters"); + key = openKey(key, _T("Parameters")); closeKey(key); return (key != NULL); @@ -373,14 +337,14 @@ CWin32Platform::canInstallDaemon(const char* name, bool allUsers) const } bool -CWin32Platform::isDaemonInstalled(const char* name, bool allUsers) const +CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers) { // if not for all users then use the user's autostart registry. // key. if windows 95 family then use windows 95 services key. - if (!allUsers || isWindows95Family()) { + if (!allUsers || CArchMiscWindows::isWindows95Family()) { // check if we can open the registry key - HKEY key = isWindows95Family() ? open95ServicesKey() : - openUserStartupKey(); + HKEY key = CArchMiscWindows::isWindows95Family() ? + open95ServicesKey() : openUserStartupKey(); if (key == NULL) { return false; } @@ -399,9 +363,10 @@ CWin32Platform::isDaemonInstalled(const char* name, bool allUsers) const // check parameters for this service HKEY key = openNTServicesKey(); key = openKey(key, name); - key = openKey(key, "Parameters"); + key = openKey(key, _T("Parameters")); if (key != NULL) { - const bool installed = !readValueString(key, "CommandLine").empty(); + const bool installed = !readValueString(key, + _T("CommandLine")).empty(); closeKey(key); if (!installed) { return false; @@ -418,114 +383,17 @@ CWin32Platform::isDaemonInstalled(const char* name, bool allUsers) const SC_HANDLE service = OpenService(mgr, name, GENERIC_READ); // clean up - CloseServiceHandle(service); + if (service != NULL) { + CloseServiceHandle(service); + } CloseServiceHandle(mgr); return (service != NULL); } } -const char* -CWin32Platform::getBasename(const char* pathname) const -{ - if (pathname == NULL) { - return NULL; - } - - // check for last / - const char* basename = strrchr(pathname, '/'); - if (basename != NULL) { - ++basename; - } - else { - basename = pathname; - } - - // check for last backslash - const char* basename2 = strrchr(pathname, '\\'); - if (basename2 != NULL && basename2 > basename) { - basename = basename2 + 1; - } - - return basename; -} - -CString -CWin32Platform::getUserDirectory() const -{ - // try %HOMEPATH% - TCHAR dir[MAX_PATH]; - DWORD size = sizeof(dir) / sizeof(TCHAR); - DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size); - if (result != 0 && result <= size) { - // sanity check -- if dir doesn't appear to start with a - // drive letter and isn't a UNC name then don't use it - // FIXME -- allow UNC names - if (dir[0] != '\0' && (dir[1] == ':' || - ((dir[0] == '\\' || dir[0] == '/') && - (dir[1] == '\\' || dir[1] == '/')))) { - return dir; - } - } - - // get the location of the personal files. that's as close to - // a home directory as we're likely to find. - ITEMIDLIST* idl; - if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) { - TCHAR* path = NULL; - if (SHGetPathFromIDList(idl, dir)) { - DWORD attr = GetFileAttributes(dir); - if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) - path = dir; - } - - IMalloc* shalloc; - if (SUCCEEDED(SHGetMalloc(&shalloc))) { - shalloc->Free(idl); - shalloc->Release(); - } - - if (path != NULL) { - return path; - } - } - - // use root of C drive as a default - return "C:"; -} - -CString -CWin32Platform::getSystemDirectory() const -{ - // get windows directory - char dir[MAX_PATH]; - if (GetWindowsDirectory(dir, sizeof(dir)) != 0) { - return dir; - } - else { - // can't get it. use C:\ as a default. - return "C:"; - } -} - -CString -CWin32Platform::addPathComponent(const CString& prefix, - const CString& suffix) const -{ - CString path; - path.reserve(prefix.size() + 1 + suffix.size()); - path += prefix; - if (path.size() == 0 || - (path[path.size() - 1] != '\\' && - path[path.size() - 1] != '/')) { - path += '\\'; - } - path += suffix; - return path; -} - HKEY -CWin32Platform::openKey(HKEY key, const char* keyName) +CArchDaemonWindows::openKey(HKEY key, const TCHAR* keyName) { // ignore if parent is NULL if (key == NULL) { @@ -553,9 +421,9 @@ CWin32Platform::openKey(HKEY key, const char* keyName) } HKEY -CWin32Platform::openKey(HKEY key, const char** keyNames) +CArchDaemonWindows::openKey(HKEY key, const TCHAR** keyNames) { - for (UInt32 i = 0; key != NULL && keyNames[i] != NULL; ++i) { + for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) { // open next key key = openKey(key, keyNames[i]); } @@ -563,14 +431,14 @@ CWin32Platform::openKey(HKEY key, const char** keyNames) } void -CWin32Platform::closeKey(HKEY key) +CArchDaemonWindows::closeKey(HKEY key) { assert(key != NULL); RegCloseKey(key); } void -CWin32Platform::deleteKey(HKEY key, const char* name) +CArchDaemonWindows::deleteKey(HKEY key, const TCHAR* name) { assert(key != NULL); assert(name != NULL); @@ -578,7 +446,7 @@ CWin32Platform::deleteKey(HKEY key, const char* name) } void -CWin32Platform::deleteValue(HKEY key, const char* name) +CArchDaemonWindows::deleteValue(HKEY key, const TCHAR* name) { assert(key != NULL); assert(name != NULL); @@ -586,7 +454,8 @@ CWin32Platform::deleteValue(HKEY key, const char* name) } void -CWin32Platform::setValue(HKEY key, const char* name, const CString& value) +CArchDaemonWindows::setValue(HKEY key, + const TCHAR* name, const std::string& value) { assert(key != NULL); assert(name != NULL); @@ -595,15 +464,15 @@ CWin32Platform::setValue(HKEY key, const char* name, const CString& value) value.size() + 1); } -CString -CWin32Platform::readValueString(HKEY key, const char* name) +std::string +CArchDaemonWindows::readValueString(HKEY key, const TCHAR* name) { // get the size of the string DWORD type; DWORD size = 0; LONG result = RegQueryValueEx(key, name, 0, &type, NULL, &size); if (result != ERROR_SUCCESS || type != REG_SZ) { - return CString(); + return std::string(); } // allocate space @@ -614,17 +483,17 @@ CWin32Platform::readValueString(HKEY key, const char* name) reinterpret_cast(buffer), &size); if (result != ERROR_SUCCESS || type != REG_SZ) { delete[] buffer; - return CString(); + return std::string(); } // clean up and return value - CString value(buffer); + std::string value(buffer); delete[] buffer; return value; } HKEY -CWin32Platform::openNTServicesKey() +CArchDaemonWindows::openNTServicesKey() { static const char* s_keyNames[] = { _T("SYSTEM"), @@ -637,7 +506,7 @@ CWin32Platform::openNTServicesKey() } HKEY -CWin32Platform::open95ServicesKey() +CArchDaemonWindows::open95ServicesKey() { static const char* s_keyNames[] = { _T("Software"), @@ -652,7 +521,7 @@ CWin32Platform::open95ServicesKey() } HKEY -CWin32Platform::openUserStartupKey() +CArchDaemonWindows::openUserStartupKey() { static const char* s_keyNames[] = { _T("Software"), @@ -667,12 +536,14 @@ CWin32Platform::openUserStartupKey() } int -CWin32Platform::runDaemon(RunFunc run, StopFunc stop) +CArchDaemonWindows::doRunDaemon(RunFunc run, StopFunc stop) { // should only be called from DaemonFunc assert(m_serviceMutex != NULL); + assert(run != NULL); + assert(stop != NULL); - CLock lock(m_serviceMutex); + ARCH->lockMutex(m_serviceMutex); try { int result; m_stop = stop; @@ -680,14 +551,16 @@ CWin32Platform::runDaemon(RunFunc run, StopFunc stop) m_serviceRunning = false; for (;;) { // mark server as running - setStatus(m_statusHandle, SERVICE_RUNNING); + setStatus(SERVICE_RUNNING); // run callback in another thread m_serviceRunning = true; { - CThread thread(new TMethodJob(this, - &CWin32Platform::runDaemonThread, run)); - result = reinterpret_cast(thread.getResult()); + CArchThread thread = ARCH->newThread( + &CArchDaemonWindows::runDaemonThreadEntry, run); + ARCH->wait(thread, -1.0); + result = reinterpret_cast(ARCH->getResultOfThread(thread)); + ARCH->closeThread(thread); } m_serviceRunning = false; @@ -696,36 +569,37 @@ CWin32Platform::runDaemon(RunFunc run, StopFunc stop) // quit. if (m_serviceHandlerWaiting) { m_serviceHandlerWaiting = false; - m_serviceState->broadcast(); + ARCH->broadcastCondVar(m_serviceCondVar); } else { break; } // wait until we're told what to do next - while (*m_serviceState != SERVICE_RUNNING && - *m_serviceState != SERVICE_STOPPED) { - m_serviceState->wait(); + while (m_serviceState != SERVICE_RUNNING && + m_serviceState != SERVICE_STOPPED) { + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } // exit loop if we've been told to stop - if (*m_serviceState == SERVICE_STOPPED) { + if (m_serviceState == SERVICE_STOPPED) { break; } } // prevent daemonHandler from changing state - *m_serviceState = SERVICE_STOPPED; + m_serviceState = SERVICE_STOPPED; // tell service control that the service is stopped. // FIXME -- hopefully this will ensure that our handler won't // be called again but i can't find documentation that // verifies that. if it does it'll crash on the mutex that // we're about to destroy. - setStatus(m_statusHandle, *m_serviceState); + setStatus(m_serviceState); // clean up m_stop = NULL; + ARCH->unlockMutex(m_serviceMutex); return result; } @@ -733,58 +607,116 @@ CWin32Platform::runDaemon(RunFunc run, StopFunc stop) // FIXME -- report error // prevent serviceHandler from changing state - *m_serviceState = SERVICE_STOPPED; + m_serviceState = SERVICE_STOPPED; // set status - setStatusError(m_statusHandle, 0); + setStatusError(0); // wake up serviceHandler if it's waiting then wait for it if (m_serviceHandlerWaiting) { m_serviceHandlerWaiting = false; - m_serviceState->broadcast(); - m_serviceState->wait(); + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); // serviceHandler has exited by now } + ARCH->unlockMutex(m_serviceMutex); throw; } } void -CWin32Platform::runDaemonThread(void* vrun) +CArchDaemonWindows::doDaemonRunning(bool running) { - RunFunc run = reinterpret_cast(vrun); - CThread::exit(reinterpret_cast(run(m_serviceMutex))); + if (running) { + ARCH->unlockMutex(m_serviceMutex); + } + else { + ARCH->lockMutex(m_serviceMutex); + } +} + +void* +CArchDaemonWindows::runDaemonThread(RunFunc run) +{ + return reinterpret_cast(run()); +} + +void* +CArchDaemonWindows::runDaemonThreadEntry(void* vrun) +{ + assert(s_daemon != NULL); + + return s_daemon->runDaemonThread(reinterpret_cast(vrun)); } void -CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) +CArchDaemonWindows::setStatus(DWORD state) +{ + setStatus(state, 0, 0); +} + +void +CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint) +{ + assert(s_daemon != NULL); + + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS; + status.dwCurrentState = state; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = step; + status.dwWaitHint = waitHint; + SetServiceStatus(s_daemon->m_statusHandle, &status); +} + +void +CArchDaemonWindows::setStatusError(DWORD error) +{ + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | + SERVICE_INTERACTIVE_PROCESS; + status.dwCurrentState = SERVICE_STOPPED; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + status.dwServiceSpecificExitCode = error; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + SetServiceStatus(s_daemon->m_statusHandle, &status); +} + +void +CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) { typedef std::vector ArgList; - typedef std::vector Arguments; + typedef std::vector Arguments; const char** argv = const_cast(argvIn); - // open event log and direct log messages to it - installDaemonLogger(argv[0]); - // create synchronization objects - CThread::init(); - m_serviceMutex = new CMutex; - m_serviceState = new CCondVar(m_serviceMutex, SERVICE_RUNNING); + m_serviceMutex = ARCH->newMutex(); + m_serviceCondVar = ARCH->newCondVar(); + m_serviceState = SERVICE_RUNNING; // register our service handler functiom m_statusHandle = RegisterServiceCtrlHandler(argv[0], - &CWin32Platform::serviceHandlerEntry); + &CArchDaemonWindows::serviceHandlerEntry); if (m_statusHandle == NULL) { // cannot start as service m_daemonResult = -1; - delete m_serviceState; - delete m_serviceMutex; + ARCH->closeCondVar(m_serviceCondVar); + ARCH->closeMutex(m_serviceMutex); return; } // tell service control manager that we're starting - setStatus(m_statusHandle, SERVICE_START_PENDING, 0, 1000); + setStatus(SERVICE_START_PENDING, 0, 1000); // if no arguments supplied then try getting them from the registry. // the first argument doesn't count because it's the service name. @@ -792,28 +724,28 @@ CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) ArgList myArgv; if (argc <= 1) { // read command line - CString commandLine; + std::string commandLine; HKEY key = openNTServicesKey(); - key = openKey(key, argv[0]); - key = openKey(key, "Parameters"); + key = openKey(key, argvIn[0]); + key = openKey(key, _T("Parameters")); if (key != NULL) { - commandLine = readValueString(key, "CommandLine"); + commandLine = readValueString(key, _T("CommandLine")); } // if the command line isn't empty then parse and use it if (!commandLine.empty()) { // parse, honoring double quoted substrings - CString::size_type i = commandLine.find_first_not_of(" \t"); - while (i != CString::npos && i != commandLine.size()) { + std::string::size_type i = commandLine.find_first_not_of(" \t"); + while (i != std::string::npos && i != commandLine.size()) { // find end of string - CString::size_type e; + std::string::size_type e; if (commandLine[i] == '\"') { // quoted. find closing quote. ++i; e = commandLine.find("\"", i); // whitespace must follow closing quote - if (e == CString::npos || + if (e == std::string::npos || (e + 1 != commandLine.size() && commandLine[e + 1] != ' ' && commandLine[e + 1] != '\t')) { @@ -828,7 +760,7 @@ CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) else { // unquoted. find next whitespace. e = commandLine.find_first_of(" \t", i); - if (e == CString::npos) { + if (e == std::string::npos) { e = commandLine.size(); } @@ -845,7 +777,7 @@ CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) myArgv.push_back(argv[0]); // get pointers - for (UInt32 i = 0; i < args.size(); ++i) { + for (size_t i = 0; i < args.size(); ++i) { myArgv.push_back(args[i].c_str()); } @@ -857,93 +789,93 @@ CWin32Platform::serviceMain(DWORD argc, LPTSTR* argvIn) try { // invoke daemon function - m_daemonResult = m_daemonFunc(this, static_cast(argc), argv); + m_daemonResult = m_daemonFunc(static_cast(argc), argv); } - catch (CDaemonFailed& e) { - setStatusError(m_statusHandle, e.m_result); + catch (XArchDaemonRunFailed& e) { + setStatusError(e.m_result); m_daemonResult = -1; } catch (...) { - setStatusError(m_statusHandle, 1); + setStatusError(1); m_daemonResult = -1; } // clean up - delete m_serviceState; - delete m_serviceMutex; - - // FIXME -- close event log? + ARCH->closeCondVar(m_serviceCondVar); + ARCH->closeMutex(m_serviceMutex); } void WINAPI -CWin32Platform::serviceMainEntry(DWORD argc, LPTSTR* argv) +CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv) { - s_daemonPlatform->serviceMain(argc, argv); + s_daemon->serviceMain(argc, argv); } void -CWin32Platform::serviceHandler(DWORD ctrl) +CArchDaemonWindows::serviceHandler(DWORD ctrl) { - assert(m_serviceMutex != NULL); - assert(m_serviceState != NULL); + assert(m_serviceMutex != NULL); + assert(m_serviceCondVar != NULL); - CLock lock(m_serviceMutex); + ARCH->lockMutex(m_serviceMutex); // ignore request if service is already stopped - if (*m_serviceState == SERVICE_STOPPED) { - setStatus(m_statusHandle, *m_serviceState); + if (m_serviceState == SERVICE_STOPPED) { + setStatus(m_serviceState); + ARCH->unlockMutex(m_serviceMutex); return; } switch (ctrl) { case SERVICE_CONTROL_PAUSE: // update state - *m_serviceState = SERVICE_PAUSE_PENDING; - setStatus(m_statusHandle, *m_serviceState, 0, 1000); + m_serviceState = SERVICE_PAUSE_PENDING; + setStatus(m_serviceState, 0, 1000); // stop run callback if running and wait for it to finish if (m_serviceRunning) { m_serviceHandlerWaiting = true; m_stop(); - m_serviceState->wait(); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } // update state if service hasn't stopped while we were waiting - if (*m_serviceState != SERVICE_STOPPED) { - *m_serviceState = SERVICE_PAUSED; + if (m_serviceState != SERVICE_STOPPED) { + m_serviceState = SERVICE_PAUSED; } - m_serviceState->broadcast(); + ARCH->broadcastCondVar(m_serviceCondVar); break; case SERVICE_CONTROL_CONTINUE: // required status update - setStatus(m_statusHandle, *m_serviceState); + setStatus(m_serviceState); // update state but let main loop send RUNNING notification - *m_serviceState = SERVICE_RUNNING; - m_serviceState->broadcast(); + m_serviceState = SERVICE_RUNNING; + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->unlockMutex(m_serviceMutex); return; case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: // update state - *m_serviceState = SERVICE_STOP_PENDING; - setStatus(m_statusHandle, *m_serviceState, 0, 1000); + m_serviceState = SERVICE_STOP_PENDING; + setStatus(m_serviceState, 0, 1000); // stop run callback if running and wait for it to finish if (m_serviceRunning) { m_serviceHandlerWaiting = true; m_stop(); - m_serviceState->wait(); + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } // update state - *m_serviceState = SERVICE_STOPPED; - m_serviceState->broadcast(); + m_serviceState = SERVICE_STOPPED; + ARCH->broadcastCondVar(m_serviceCondVar); break; default: - LOG((CLOG_WARN "unknown service command: %d", ctrl)); + // unknown service command // fall through case SERVICE_CONTROL_INTERROGATE: @@ -951,51 +883,13 @@ CWin32Platform::serviceHandler(DWORD ctrl) } // send update - setStatus(m_statusHandle, *m_serviceState); + setStatus(m_serviceState); + + ARCH->unlockMutex(m_serviceMutex); } void WINAPI -CWin32Platform::serviceHandlerEntry(DWORD ctrl) +CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl) { - s_daemonPlatform->serviceHandler(ctrl); -} - -bool -CWin32Platform::serviceLogger(int priority, const char* msg) -{ - if (s_eventLog == NULL) { - return false; - } - - // convert priority - WORD type; - switch (priority) { - case CLog::kFATAL: - case CLog::kERROR: - type = EVENTLOG_ERROR_TYPE; - break; - - case CLog::kWARNING: - type = EVENTLOG_WARNING_TYPE; - break; - - default: - type = EVENTLOG_INFORMATION_TYPE; - break; - } - - // log it - // FIXME -- win32 wants to use a message table to look up event - // strings. log messages aren't organized that way so we'll - // just dump our string into the raw data section of the event - // so users can at least see the message. note that we use our - // priority as the event category. - ReportEvent(s_eventLog, type, static_cast(priority), - 0, // event ID - NULL, - 0, - strlen(msg + 1), // raw data size - NULL, - const_cast(msg));// raw data - return true; + s_daemon->serviceHandler(ctrl); } diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h new file mode 100644 index 00000000..aeb3916f --- /dev/null +++ b/lib/arch/CArchDaemonWindows.h @@ -0,0 +1,130 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHDAEMONWINDOWS_H +#define CARCHDAEMONWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchDaemon.h" +#include "IArchMultithread.h" +#include "stdstring.h" +#include +#include + +#define ARCH_DAEMON CArchDaemonWindows + +class CArchDaemonWindows : public IArchDaemon { +public: + typedef int (*RunFunc)(void); + typedef void (*StopFunc)(void); + + CArchDaemonWindows(); + virtual ~CArchDaemonWindows(); + + //! Run the daemon + /*! + When the client calls \c daemonize(), the \c DaemonFunc should call this + function after initialization and argument parsing to perform the + daemon processing. The \c runFunc should perform the daemon's + main loop, calling \c daemonRunning(true) when it enters the main loop + (i.e. after initialization) and \c daemonRunning(false) when it leaves + the main loop. The \c stopFunc function is called when the daemon + must exit the main loop and must cause \c runFunc to return. This + function returns what \c runFunc returns. \c runFunc should call + \c daemonFailed() if the daemon fails. + */ + static int runDaemon(RunFunc runFunc, StopFunc stopFunc); + + //! Indicate daemon is in main loop + /*! + The \c runFunc passed to \c runDaemon() should call this function + to indicate when it has entered (\c running is \c true) or exited + (\c running is \c false) the main loop. + */ + static void daemonRunning(bool running); + + //! Indicate failure of running daemon + /*! + The \c runFunc passed to \c runDaemon() should call this function + to indicate failure. \c result is returned by \c daemonize(). + */ + static void daemonFailed(int result); + + // IArchDaemon overrides + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers); + virtual void uninstallDaemon(const char* name, bool allUsers); + virtual int daemonize(const char* name, DaemonFunc func); + virtual bool canInstallDaemon(const char* name, bool allUsers); + virtual bool isDaemonInstalled(const char* name, bool allUsers); + +private: + static HKEY openKey(HKEY parent, const TCHAR*); + static HKEY openKey(HKEY parent, const TCHAR**); + static void closeKey(HKEY); + static void deleteKey(HKEY, const TCHAR* name); + static void deleteValue(HKEY, const TCHAR* name); + static void setValue(HKEY, const TCHAR* name, + const std::string& value); + static std::string readValueString(HKEY, const TCHAR* name); + static HKEY openNTServicesKey(); + static HKEY open95ServicesKey(); + static HKEY openUserStartupKey(); + + int doRunDaemon(RunFunc runFunc, StopFunc stopFunc); + void doDaemonRunning(bool running); + + static void setStatus(DWORD state); + static void setStatus(DWORD state, DWORD step, DWORD waitHint); + static void setStatusError(DWORD error); + + void* runDaemonThread(RunFunc); + static void* runDaemonThreadEntry(void*); + + void serviceMain(DWORD, LPTSTR*); + static void WINAPI serviceMainEntry(DWORD, LPTSTR*); + + void serviceHandler(DWORD ctrl); + static void WINAPI serviceHandlerEntry(DWORD ctrl); + +private: + class XArchDaemonRunFailed { + public: + XArchDaemonRunFailed(int result) : m_result(result) { } + + public: + int m_result; + }; + +private: + static CArchDaemonWindows* s_daemon; + + CArchMutex m_serviceMutex; + CArchCond m_serviceCondVar; + DWORD m_serviceState; + bool m_serviceHandlerWaiting; + bool m_serviceRunning; + StopFunc m_stop; + + DaemonFunc m_daemonFunc; + int m_daemonResult; + + SERVICE_STATUS_HANDLE m_statusHandle; +}; + +#endif diff --git a/lib/arch/CArchFileUnix.cpp b/lib/arch/CArchFileUnix.cpp new file mode 100644 index 00000000..a708cc8d --- /dev/null +++ b/lib/arch/CArchFileUnix.cpp @@ -0,0 +1,94 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchFileUnix.h" +#include +#include +#include +#include + +// +// CArchFileUnix +// + +CArchFileUnix::CArchFileUnix() +{ + // do nothing +} + +CArchFileUnix::~CArchFileUnix() +{ + // do nothing +} + +const char* +CArchFileUnix::getBasename(const char* pathname) +{ + if (pathname == NULL) { + return NULL; + } + + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + return basename + 1; + } + else { + return pathname; + } +} + +std::string +CArchFileUnix::getUserDirectory() +{ +#if HAVE_GETPWUID_R + struct passwd pwent; + struct passwd* pwentp; +#if defined(_SC_GETPW_R_SIZE_MAX) + long size = sysconf(_SC_GETPW_R_SIZE_MAX); +#else + long size = BUFSIZ; +#endif + char* buffer = new char[size]; + getpwuid_r(getuid(), &pwent, buffer, size, &pwentp); + delete[] buffer; +#else + struct passwd* pwentp = getpwuid(getuid()); +#endif + if (pwentp != NULL && pwentp->pw_dir != NULL) { + return pwentp->pw_dir; + } + else { + return std::string(); + } +} + +std::string +CArchFileUnix::getSystemDirectory() +{ + return "/etc"; +} + +std::string +CArchFileUnix::concatPath(const std::string& prefix, + const std::string& suffix) +{ + std::string path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + if (path.size() == 0 || path[path.size() - 1] != '/') { + path += '/'; + } + path += suffix; + return path; +} diff --git a/lib/arch/CArchFileUnix.h b/lib/arch/CArchFileUnix.h new file mode 100644 index 00000000..c6257555 --- /dev/null +++ b/lib/arch/CArchFileUnix.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHFILEUNIX_H +#define CARCHFILEUNIX_H + +#include "IArchFile.h" + +#define ARCH_FILE CArchFileUnix + +class CArchFileUnix : public IArchFile { +public: + CArchFileUnix(); + virtual ~CArchFileUnix(); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); +}; + +#endif diff --git a/lib/arch/CArchFileWindows.cpp b/lib/arch/CArchFileWindows.cpp new file mode 100644 index 00000000..7d9fd51b --- /dev/null +++ b/lib/arch/CArchFileWindows.cpp @@ -0,0 +1,132 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchFileWindows.h" +#include +#include +#include +#include + +// +// CArchFileWindows +// + +CArchFileWindows::CArchFileWindows() +{ + // do nothing +} + +CArchFileWindows::~CArchFileWindows() +{ + // do nothing +} + +const char* +CArchFileWindows::getBasename(const char* pathname) +{ + if (pathname == NULL) { + return NULL; + } + + // check for last slash + const char* basename = strrchr(pathname, '/'); + if (basename != NULL) { + ++basename; + } + else { + basename = pathname; + } + + // check for last backslash + const char* basename2 = strrchr(pathname, '\\'); + if (basename2 != NULL && basename2 > basename) { + basename = basename2 + 1; + } + + return basename; +} + +std::string +CArchFileWindows::getUserDirectory() +{ + // try %HOMEPATH% + TCHAR dir[MAX_PATH]; + DWORD size = sizeof(dir) / sizeof(TCHAR); + DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size); + if (result != 0 && result <= size) { + // sanity check -- if dir doesn't appear to start with a + // drive letter and isn't a UNC name then don't use it + // FIXME -- allow UNC names + if (dir[0] != '\0' && (dir[1] == ':' || + ((dir[0] == '\\' || dir[0] == '/') && + (dir[1] == '\\' || dir[1] == '/')))) { + return dir; + } + } + + // get the location of the personal files. that's as close to + // a home directory as we're likely to find. + ITEMIDLIST* idl; + if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) { + TCHAR* path = NULL; + if (SHGetPathFromIDList(idl, dir)) { + DWORD attr = GetFileAttributes(dir); + if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + path = dir; + } + + IMalloc* shalloc; + if (SUCCEEDED(SHGetMalloc(&shalloc))) { + shalloc->Free(idl); + shalloc->Release(); + } + + if (path != NULL) { + return path; + } + } + + // use root of C drive as a default + return "C:"; +} + +std::string +CArchFileWindows::getSystemDirectory() +{ + // get windows directory + char dir[MAX_PATH]; + if (GetWindowsDirectory(dir, sizeof(dir)) != 0) { + return dir; + } + else { + // can't get it. use C:\ as a default. + return "C:"; + } +} + +std::string +CArchFileWindows::concatPath(const std::string& prefix, + const std::string& suffix) +{ + std::string path; + path.reserve(prefix.size() + 1 + suffix.size()); + path += prefix; + if (path.size() == 0 || + (path[path.size() - 1] != '\\' && + path[path.size() - 1] != '/')) { + path += '\\'; + } + path += suffix; + return path; +} diff --git a/lib/arch/CArchFileWindows.h b/lib/arch/CArchFileWindows.h new file mode 100644 index 00000000..44263b38 --- /dev/null +++ b/lib/arch/CArchFileWindows.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHFILEWINDOWS_H +#define CARCHFILEWINDOWS_H + +#include "IArchFile.h" + +#define ARCH_FILE CArchFileWindows + +class CArchFileWindows : public IArchFile { +public: + CArchFileWindows(); + virtual ~CArchFileWindows(); + + // IArchFile overrides + virtual const char* getBasename(const char* pathname); + virtual std::string getUserDirectory(); + virtual std::string getSystemDirectory(); + virtual std::string concatPath(const std::string& prefix, + const std::string& suffix); +}; + +#endif diff --git a/lib/arch/CArchImpl.cpp b/lib/arch/CArchImpl.cpp new file mode 100644 index 00000000..c4017122 --- /dev/null +++ b/lib/arch/CArchImpl.cpp @@ -0,0 +1,43 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "common.h" + +// include appropriate architecture implementation +#if WINDOWS_LIKE +# include "CArchMiscWindows.cpp" +# include "CArchConsoleWindows.cpp" +# include "CArchDaemonWindows.cpp" +# include "CArchFileWindows.cpp" +# include "CArchLogWindows.cpp" +# include "CArchMultithreadWindows.cpp" +# include "CArchNetworkWinsock.cpp" +# include "CArchSleepWindows.cpp" +# include "CArchStringWindows.cpp" +# include "CArchTimeWindows.cpp" +# include "XArchWindows.cpp" +#elif UNIX_LIKE +# include "CArchConsoleUnix.cpp" +# include "CArchDaemonUnix.cpp" +# include "CArchFileUnix.cpp" +# include "CArchLogUnix.cpp" +# if HAVE_PTHREAD +# include "CArchMultithreadPosix.cpp" +# endif +# include "CArchNetworkBSD.cpp" +# include "CArchSleepUnix.cpp" +# include "CArchStringUnix.cpp" +# include "CArchTimeUnix.cpp" +# include "XArchUnix.cpp" +#endif diff --git a/lib/arch/CArchLogUnix.cpp b/lib/arch/CArchLogUnix.cpp new file mode 100644 index 00000000..c75696e3 --- /dev/null +++ b/lib/arch/CArchLogUnix.cpp @@ -0,0 +1,73 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchLogUnix.h" +#include + +// +// CArchLogUnix +// + +CArchLogUnix::CArchLogUnix() +{ + // do nothing +} + +CArchLogUnix::~CArchLogUnix() +{ + // do nothing +} + +void +CArchLogUnix::openLog(const char* name) +{ + openlog(name, 0, LOG_DAEMON); +} + +void +CArchLogUnix::closeLog() +{ + closelog(); +} + +void +CArchLogUnix::writeLog(ELevel level, const char* msg) +{ + // convert level + int priority; + switch (level) { + case kERROR: + priority = LOG_ERR; + break; + + case kWARNING: + priority = LOG_WARNING; + break; + + case kNOTE: + priority = LOG_NOTICE; + break; + + case kINFO: + priority = LOG_INFO; + break; + + default: + priority = LOG_DEBUG; + break; + } + + // log it + syslog(priority, "%s", msg); +} diff --git a/lib/arch/CArchLogUnix.h b/lib/arch/CArchLogUnix.h new file mode 100644 index 00000000..107d6891 --- /dev/null +++ b/lib/arch/CArchLogUnix.h @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHLOGUNIX_H +#define CARCHLOGUNIX_H + +#include "IArchLog.h" + +#define ARCH_LOG CArchLogUnix + +class CArchLogUnix : public IArchLog { +public: + CArchLogUnix(); + virtual ~CArchLogUnix(); + + // IArchLog overrides + virtual void openLog(const char* name); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); +}; + +#endif diff --git a/lib/arch/CArchLogWindows.cpp b/lib/arch/CArchLogWindows.cpp new file mode 100644 index 00000000..a1617da1 --- /dev/null +++ b/lib/arch/CArchLogWindows.cpp @@ -0,0 +1,84 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchLogWindows.h" +#include "CArchMiscWindows.h" +#include + +// +// CArchLogWindows +// + +CArchLogWindows::CArchLogWindows() +{ + // do nothing +} + +CArchLogWindows::~CArchLogWindows() +{ + // do nothing +} + +void +CArchLogWindows::openLog(const char* name) +{ + if (!CArchMiscWindows::isWindows95Family()) { + m_eventLog = RegisterEventSource(NULL, name); + } +} + +void +CArchLogWindows::closeLog() +{ + if (m_eventLog != NULL) { + DeregisterEventSource(m_eventLog); + m_eventLog = NULL; + } +} + +void +CArchLogWindows::writeLog(ELevel level, const char* msg) +{ + if (m_eventLog != NULL) { + // convert priority + WORD type; + switch (level) { + case kERROR: + type = EVENTLOG_ERROR_TYPE; + break; + + case kWARNING: + type = EVENTLOG_WARNING_TYPE; + break; + + default: + type = EVENTLOG_INFORMATION_TYPE; + break; + } + + // log it + // FIXME -- win32 wants to use a message table to look up event + // strings. log messages aren't organized that way so we'll + // just dump our string into the raw data section of the event + // so users can at least see the message. note that we use our + // level as the event category. + ReportEvent(m_eventLog, type, static_cast(level), + 0, // event ID + NULL, + 0, + strlen(msg + 1), // raw data size + NULL, + const_cast(msg));// raw data + } +} diff --git a/lib/arch/CArchLogWindows.h b/lib/arch/CArchLogWindows.h new file mode 100644 index 00000000..1c077643 --- /dev/null +++ b/lib/arch/CArchLogWindows.h @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHLOGWINDOWS_H +#define CARCHLOGWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchLog.h" +#include + +#define ARCH_LOG CArchLogWindows + +class CArchLogWindows : public IArchLog { +public: + CArchLogWindows(); + virtual ~CArchLogWindows(); + + // IArchLog overrides + virtual void openLog(const char* name); + virtual void closeLog(); + virtual void writeLog(ELevel, const char*); + +private: + HANDLE m_eventLog; +}; + +#endif diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp new file mode 100644 index 00000000..25511076 --- /dev/null +++ b/lib/arch/CArchMiscWindows.cpp @@ -0,0 +1,53 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define WIN32_LEAN_AND_MEAN + +#include "CArchMiscWindows.h" +#include "CArchDaemonWindows.h" +#include + +// +// CArchMiscWindows +// + +bool +CArchMiscWindows::isWindows95Family() +{ + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + // cannot determine OS; assume windows 95 family + return true; + } + return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); +} + +int +CArchMiscWindows::runDaemon(RunFunc runFunc, StopFunc stopFunc) +{ + return CArchDaemonWindows::runDaemon(runFunc, stopFunc); +} + +void +CArchMiscWindows::daemonRunning(bool running) +{ + CArchDaemonWindows::daemonRunning(running); +} + +void +CArchMiscWindows::daemonFailed(int result) +{ + CArchDaemonWindows::daemonFailed(result); +} diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h new file mode 100644 index 00000000..af1a2c78 --- /dev/null +++ b/lib/arch/CArchMiscWindows.h @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMISCWINDOWS_H +#define CARCHMISCWINDOWS_H + +class CArchMiscWindows { +public: + typedef int (*RunFunc)(void); + typedef void (*StopFunc)(void); + + //! Test if windows 95, et al. + /*! + Returns true iff the platform is win95/98/me. + */ + static bool isWindows95Family(); + + //! Run the daemon + /*! + Delegates to CArchDaemonWindows. + */ + static int runDaemon(RunFunc runFunc, StopFunc stopFunc); + + //! Indicate daemon is in main loop + /*! + Delegates to CArchDaemonWindows. + */ + static void daemonRunning(bool running); + + //! Indicate failure of running daemon + /*! + Delegates to CArchDaemonWindows. + */ + static void daemonFailed(int result); +}; + +#endif diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp new file mode 100644 index 00000000..304440b2 --- /dev/null +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -0,0 +1,748 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchMultithreadPosix.h" +#include "CArch.h" +#include "XArch.h" +#include +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#include + +#define SIGWAKEUP SIGUSR1 + +// +// CArchThreadImpl +// + +class CArchThreadImpl { +public: + CArchThreadImpl(); + +public: + int m_refCount; + pthread_t m_thread; + IArchMultithread::ThreadFunc m_func; + void* m_userData; + bool m_cancel; + bool m_cancelling; + bool m_exited; + void* m_result; +}; + +CArchThreadImpl::CArchThreadImpl() : + m_refCount(1), + m_func(NULL), + m_userData(NULL), + m_cancel(false), + m_cancelling(false), + m_exited(false), + m_result(NULL) +{ + // do nothing +} + + +// +// CArchMultithreadPosix +// + +CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL; + +CArchMultithreadPosix::CArchMultithreadPosix() : + m_newThreadCalled(false) +{ + assert(s_instance == NULL); + + s_instance = this; + + // create mutex for thread list + m_threadMutex = newMutex(); + + // create thread for calling (main) thread and add it to our + // list. no need to lock the mutex since we're the only thread. + m_mainThread = new CArchThreadImpl; + m_mainThread->m_thread = pthread_self(); + insert(m_mainThread); + + // install SIGWAKEUP handler. this causes SIGWAKEUP to interrupt + // system calls. we use that when cancelling a thread to force it + // to wake up immediately if it's blocked in a system call. we + // won't need this until another thread is created but it's fine + // to install it now. + struct sigaction act; + sigemptyset(&act.sa_mask); +# if defined(SA_INTERRUPT) + act.sa_flags = SA_INTERRUPT; +# else + act.sa_flags = 0; +# endif + act.sa_handler = &threadCancel; + sigaction(SIGWAKEUP, &act, NULL); + + // set desired signal dispositions. let SIGWAKEUP through but + // ignore SIGPIPE (we'll handle EPIPE). + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGWAKEUP); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + sigemptyset(&sigset); + sigaddset(&sigset, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); +} + +CArchMultithreadPosix::~CArchMultithreadPosix() +{ + assert(s_instance != NULL); + + closeMutex(m_threadMutex); + s_instance = NULL; +} + +CArchCond +CArchMultithreadPosix::newCondVar() +{ + CArchCondImpl* cond = new CArchCondImpl; + int status = pthread_cond_init(&cond->m_cond, NULL); + assert(status == 0); + return cond; +} + +void +CArchMultithreadPosix::closeCondVar(CArchCond cond) +{ + int status = pthread_cond_destroy(&cond->m_cond); + assert(status == 0); + delete cond; +} + +void +CArchMultithreadPosix::signalCondVar(CArchCond cond) +{ + int status = pthread_cond_signal(&cond->m_cond); + assert(status == 0); +} + +void +CArchMultithreadPosix::broadcastCondVar(CArchCond cond) +{ + int status = pthread_cond_broadcast(&cond->m_cond); + assert(status == 0); +} + +bool +CArchMultithreadPosix::waitCondVar(CArchCond cond, + CArchMutex mutex, double timeout) +{ + // get final time + struct timeval now; + gettimeofday(&now, NULL); + struct timespec finalTime; + finalTime.tv_sec = now.tv_sec; + finalTime.tv_nsec = now.tv_usec * 1000; + if (timeout >= 0.0) { + const long timeout_sec = (long)timeout; + const long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec)); + finalTime.tv_sec += timeout_sec; + finalTime.tv_nsec += timeout_nsec; + if (finalTime.tv_nsec >= 1000000000) { + finalTime.tv_nsec -= 1000000000; + finalTime.tv_sec += 1; + } + } + + // repeat until we reach the final time + int status; + for (;;) { + // get current time + gettimeofday(&now, NULL); + struct timespec endTime; + endTime.tv_sec = now.tv_sec; + endTime.tv_nsec = now.tv_usec * 1000; + + // done if past final timeout + if (timeout >= 0.0) { + if (endTime.tv_sec > finalTime.tv_sec || + (endTime.tv_sec == finalTime.tv_sec && + endTime.tv_nsec >= finalTime.tv_nsec)) { + status = ETIMEDOUT; + break; + } + } + + // compute the next timeout + endTime.tv_nsec += 50000000; + if (endTime.tv_nsec >= 1000000000) { + endTime.tv_nsec -= 1000000000; + endTime.tv_sec += 1; + } + + // don't wait past final timeout + if (timeout >= 0.0) { + if (endTime.tv_sec > finalTime.tv_sec || + (endTime.tv_sec == finalTime.tv_sec && + endTime.tv_nsec >= finalTime.tv_nsec)) { + endTime = finalTime; + } + } + + // see if we should cancel this thread + testCancelThread(); + + // wait + status = pthread_cond_timedwait(&cond->m_cond, + &mutex->m_mutex, &endTime); + + // check for cancel again + testCancelThread(); + + // check wait status + if (status != ETIMEDOUT && status != EINTR) { + break; + } + } + + switch (status) { + case 0: + // success + return true; + + case ETIMEDOUT: + return false; + + default: + assert(0 && "condition variable wait error"); + return false; + } +} + +CArchMutex +CArchMultithreadPosix::newMutex() +{ + CArchMutexImpl* mutex = new CArchMutexImpl; + int status = pthread_mutex_init(&mutex->m_mutex, NULL); + assert(status == 0); +/* + status = pthread_mutexattr_settype(&mutex->m_mutex, + PTHREAD_MUTEX_RECURSIVE); + assert(status == 0); +*/ + return mutex; +} + +void +CArchMultithreadPosix::closeMutex(CArchMutex mutex) +{ + int status = pthread_mutex_destroy(&mutex->m_mutex); + assert(status == 0); + delete mutex; +} + +void +CArchMultithreadPosix::lockMutex(CArchMutex mutex) +{ + int status = pthread_mutex_lock(&mutex->m_mutex); + + switch (status) { + case 0: + // success + return; + + case EDEADLK: + assert(0 && "lock already owned"); + break; + + case EAGAIN: + assert(0 && "too many recursive locks"); + break; + + default: + assert(0 && "unexpected error"); + break; + } +} + +void +CArchMultithreadPosix::unlockMutex(CArchMutex mutex) +{ + int status = pthread_mutex_unlock(&mutex->m_mutex); + + switch (status) { + case 0: + // success + return; + + case EPERM: + assert(0 && "thread doesn't own a lock"); + break; + + default: + assert(0 && "unexpected error"); + break; + } +} + +CArchThread +CArchMultithreadPosix::newThread(ThreadFunc func, void* data) +{ + assert(func != NULL); + + // initialize signal handler. we do this here instead of the + // constructor so we can avoid daemonizing (using fork()) + // when there are multiple threads. clients can safely + // use condition variables and mutexes before creating a + // new thread and they can safely use the only thread + // they have access to, the main thread, so they really + // can't tell the difference. + if (!m_newThreadCalled) { + m_newThreadCalled = true; + startSignalHandler(); + } + + lockMutex(m_threadMutex); + + // create thread impl for new thread + CArchThreadImpl* thread = new CArchThreadImpl; + thread->m_func = func; + thread->m_userData = data; + + // mask some signals in all threads except the main thread + sigset_t sigset, oldsigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); + + // create the thread. pthread_create() on RedHat 7.2 smp fails + // if passed a NULL attr so use a default attr. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&thread->m_thread, &attr, + &CArchMultithreadPosix::threadFunc, thread); + pthread_attr_destroy(&attr); + } + + // restore signals + pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); + + // check if thread was started + if (status != 0) { + // failed to start thread so clean up + delete thread; + thread = NULL; + } + else { + // add thread to list + insert(thread); + + // increment ref count to account for the thread itself + refThread(thread); + } + + // note that the child thread will wait until we release this mutex + unlockMutex(m_threadMutex); + + return thread; +} + +CArchThread +CArchMultithreadPosix::newCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = find(pthread_self()); + unlockMutex(m_threadMutex); + assert(thread != NULL); + return thread; +} + +void +CArchMultithreadPosix::closeThread(CArchThread thread) +{ + assert(thread != NULL); + + // decrement ref count and clean up thread if no more references + if (--thread->m_refCount == 0) { + // detach from thread (unless it's the main thread) + if (thread->m_func != NULL) { + pthread_detach(thread->m_thread); + } + + // remove thread from list + lockMutex(m_threadMutex); + assert(findNoRef(thread->m_thread) == thread); + erase(thread); + unlockMutex(m_threadMutex); + + // done with thread + delete thread; + } +} + +CArchThread +CArchMultithreadPosix::copyThread(CArchThread thread) +{ + refThread(thread); + return thread; +} + +void +CArchMultithreadPosix::cancelThread(CArchThread thread) +{ + assert(thread != NULL); + + // set cancel and wakeup flags if thread can be cancelled + bool wakeup = false; + lockMutex(m_threadMutex); + if (!thread->m_exited && !thread->m_cancelling) { + thread->m_cancel = true; + wakeup = true; + } + unlockMutex(m_threadMutex); + + // force thread to exit system calls if wakeup is true + if (wakeup) { + pthread_kill(thread->m_thread, SIGWAKEUP); + } +} + +void +CArchMultithreadPosix::setPriorityOfThread(CArchThread thread, int /*n*/) +{ + assert(thread != NULL); + + // FIXME +} + +void +CArchMultithreadPosix::testCancelThread() +{ + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(pthread_self()); + unlockMutex(m_threadMutex); + + // test cancel on thread + testCancelThreadImpl(thread); +} + +bool +CArchMultithreadPosix::wait(CArchThread target, double timeout) +{ + assert(target != NULL); + + lockMutex(m_threadMutex); + + // find current thread + CArchThreadImpl* self = findNoRef(pthread_self()); + + // ignore wait if trying to wait on ourself + if (target == self) { + unlockMutex(m_threadMutex); + return false; + } + + // ref the target so it can't go away while we're watching it + refThread(target); + + unlockMutex(m_threadMutex); + + try { + // do first test regardless of timeout + testCancelThreadImpl(self); + if (isExitedThread(target)) { + closeThread(target); + return true; + } + + // wait and repeat test if there's a timeout + if (timeout != 0.0) { + const double start = ARCH->time(); + do { + // wait a little + ARCH->sleep(0.05); + + // repeat test + testCancelThreadImpl(self); + if (isExitedThread(target)) { + closeThread(target); + return true; + } + + // repeat wait and test until timed out + } while (timeout < 0.0 || (ARCH->time() - start) <= timeout); + } + + closeThread(target); + return false; + } + catch (...) { + closeThread(target); + throw; + } +} + +bool +CArchMultithreadPosix::waitForEvent(double /*timeout*/) +{ + // not implemented +} + +bool +CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return (thread1 == thread2); +} + +bool +CArchMultithreadPosix::isExitedThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + bool exited = thread->m_exited; + unlockMutex(m_threadMutex); + return exited; +} + +void* +CArchMultithreadPosix::getResultOfThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* result = thread->m_result; + unlockMutex(m_threadMutex); + return result; +} + +IArchMultithread::ThreadID +CArchMultithreadPosix::getIDOfThread(CArchThread thread) +{ + return reinterpret_cast(reinterpret_cast(thread)); +} + +void +CArchMultithreadPosix::startSignalHandler() +{ + // set signal mask. the main thread blocks these signals and + // the signal handler thread will listen for them. + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); + + // fire up the INT and TERM signal handler thread. we could + // instead arrange to catch and handle these signals but + // we'd be unable to cancel the main thread since no pthread + // calls are allowed in a signal handler. + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + if (status == 0) { + status = pthread_create(&m_signalThread, &attr, + &CArchMultithreadPosix::threadSignalHandler, + m_mainThread); + pthread_attr_destroy(&attr); + } + if (status != 0) { + // can't create thread to wait for signal so don't block + // the signals. + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + } +} + +CArchThreadImpl* +CArchMultithreadPosix::find(pthread_t thread) +{ + CArchThreadImpl* impl = findNoRef(thread); + if (impl != NULL) { + refThread(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadPosix::findNoRef(pthread_t thread) +{ + // linear search + for (CThreadList::const_iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if ((*index)->m_thread == thread) { + return *index; + } + } + return NULL; +} + +void +CArchMultithreadPosix::insert(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // thread shouldn't already be on the list + assert(findNoRef(thread->m_thread) == NULL); + + // append to list + m_threadList.push_back(thread); +} + +void +CArchMultithreadPosix::erase(CArchThreadImpl* thread) +{ + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if (*index == thread) { + m_threadList.erase(index); + break; + } + } +} + +void +CArchMultithreadPosix::refThread(CArchThreadImpl* thread) +{ + assert(thread != NULL); + assert(findNoRef(thread->m_thread) != NULL); + ++thread->m_refCount; +} + +void +CArchMultithreadPosix::testCancelThreadImpl(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // update cancel state + lockMutex(m_threadMutex); + bool cancel = false; + if (thread->m_cancel && !thread->m_cancelling) { + thread->m_cancelling = true; + thread->m_cancel = false; + cancel = true; + } + unlockMutex(m_threadMutex); + + // unwind thread's stack if cancelling + if (cancel) { + throw XThreadCancel(); + } +} + +void* +CArchMultithreadPosix::threadFunc(void* vrep) +{ + // get the thread + CArchThreadImpl* thread = reinterpret_cast(vrep); + + // setup pthreads + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + // run thread + s_instance->doThreadFunc(thread); + + // terminate the thread + return NULL; +} + +void +CArchMultithreadPosix::doThreadFunc(CArchThread thread) +{ + // default priority is slightly below normal + setPriorityOfThread(thread, 1); + + // wait for parent to initialize this object + lockMutex(m_threadMutex); + unlockMutex(m_threadMutex); + + void* result = NULL; + try { + // go + result = (*thread->m_func)(thread->m_userData); + } + + catch (XThreadCancel&) { + // client called cancel() + } + catch (...) { + // note -- don't catch (...) to avoid masking bugs + lockMutex(m_threadMutex); + thread->m_exited = true; + unlockMutex(m_threadMutex); + closeThread(thread); + throw; + } + + // thread has exited + lockMutex(m_threadMutex); + thread->m_result = result; + thread->m_exited = true; + unlockMutex(m_threadMutex); + + // done with thread + closeThread(thread); +} + +void +CArchMultithreadPosix::threadCancel(int) +{ + // do nothing +} + +void* +CArchMultithreadPosix::threadSignalHandler(void* vrep) +{ + CArchThreadImpl* mainThread = reinterpret_cast(vrep); + + // detach + pthread_detach(pthread_self()); + + // add signal to mask + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGTERM); + + // also wait on SIGABRT. on linux (others?) this thread (process) + // will persist after all the other threads evaporate due to an + // assert unless we wait on SIGABRT. that means our resources (like + // the socket we're listening on) are not released and never will be + // until the lingering thread is killed. i don't know why sigwait() + // should protect the thread from being killed. note that sigwait() + // doesn't actually return if we receive SIGABRT and, for some + // reason, we don't have to block SIGABRT. + sigaddset(&sigset, SIGABRT); + + // we exit the loop via thread cancellation in sigwait() + for (;;) { + // wait +#if HAVE_POSIX_SIGWAIT + int signal; + sigwait(&sigset, &signal); +#else + sigwait(&sigset); +#endif + + // if we get here then the signal was raised. cancel the main + // thread so it can shut down cleanly. + ARCH->cancelThread(mainThread); + } +} diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h new file mode 100644 index 00000000..cc67a226 --- /dev/null +++ b/lib/arch/CArchMultithreadPosix.h @@ -0,0 +1,93 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMULTITHREADPOSIX_H +#define CARCHMULTITHREADPOSIX_H + +#include "IArchMultithread.h" +#include "stdlist.h" +#include + +#define ARCH_MULTITHREAD CArchMultithreadPosix + +class CArchCondImpl { +public: + pthread_cond_t m_cond; +}; + +class CArchMutexImpl { +public: + pthread_mutex_t m_mutex; +}; + +class CArchMultithreadPosix : public IArchMultithread { +public: + CArchMultithreadPosix(); + virtual ~CArchMultithreadPosix(); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual bool waitForEvent(double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + +private: + void startSignalHandler(); + + CArchThreadImpl* find(pthread_t thread); + CArchThreadImpl* findNoRef(pthread_t thread); + void insert(CArchThreadImpl* thread); + void erase(CArchThreadImpl* thread); + + void refThread(CArchThreadImpl* rep); + void testCancelThreadImpl(CArchThreadImpl* rep); + + void doThreadFunc(CArchThread thread); + static void* threadFunc(void* vrep); + static void threadCancel(int); + static void* threadSignalHandler(void* vrep); + +private: + typedef std::list CThreadList; + + static CArchMultithreadPosix* s_instance; + + bool m_newThreadCalled; + + CArchMutex m_threadMutex; + CArchThread m_mainThread; + CThreadList m_threadList; + + pthread_t m_signalThread; +}; + +#endif diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp new file mode 100644 index 00000000..3a498135 --- /dev/null +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -0,0 +1,648 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if !defined(_MT) +# error multithreading compile option is required +#endif + +#include "CArchMultithreadWindows.h" +#include "CArch.h" +#include "XArch.h" +#include + +// +// note -- implementation of condition variable taken from: +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html +// titled "Strategies for Implementing POSIX Condition Variables +// on Win32." it also provides an implementation that doesn't +// suffer from the incorrectness problem described in our +// corresponding header but it is slower, still unfair, and +// can cause busy waiting. +// + +// +// CArchThreadImpl +// + +class CArchThreadImpl { +public: + CArchThreadImpl(); + ~CArchThreadImpl(); + +public: + int m_refCount; + HANDLE m_thread; + DWORD m_id; + IArchMultithread::ThreadFunc m_func; + void* m_userData; + HANDLE m_cancel; + bool m_cancelling; + HANDLE m_exit; + void* m_result; +}; + +CArchThreadImpl::CArchThreadImpl() : + m_refCount(1), + m_thread(NULL), + m_id(0), + m_func(NULL), + m_userData(NULL), + m_cancelling(false), + m_result(NULL) +{ + m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); + m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +CArchThreadImpl::~CArchThreadImpl() +{ + CloseHandle(m_exit); + CloseHandle(m_cancel); +} + + +// +// CArchMultithreadWindows +// + +CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL; + +CArchMultithreadWindows::CArchMultithreadWindows() +{ + assert(s_instance == NULL); + s_instance = this; + + // create mutex for thread list + m_threadMutex = newMutex(); + + // create thread for calling (main) thread and add it to our + // list. no need to lock the mutex since we're the only thread. + CArchThreadImpl* mainThread = new CArchThreadImpl; + mainThread->m_thread = NULL; + mainThread->m_id = GetCurrentThreadId(); + insert(mainThread); +} + +CArchMultithreadWindows::~CArchMultithreadWindows() +{ + s_instance = NULL; +} + +HANDLE +CArchMultithreadWindows::getCancelEventForCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + return thread->m_cancel; +} + +CArchMultithreadWindows* +CArchMultithreadWindows::getInstance() +{ + return s_instance; +} + +CArchCond +CArchMultithreadWindows::newCondVar() +{ + CArchCondImpl* cond = new CArchCondImpl; + cond->m_events[CArchCondImpl::kSignal] = CreateEvent(NULL, + FALSE, FALSE, NULL); + cond->m_events[CArchCondImpl::kBroadcast] = CreateEvent(NULL, + TRUE, FALSE, NULL); + cond->m_waitCountMutex = newMutex(); + cond->m_waitCount = 0; + return cond; +} + +void +CArchMultithreadWindows::closeCondVar(CArchCond cond) +{ + CloseHandle(cond->m_events[CArchCondImpl::kSignal]); + CloseHandle(cond->m_events[CArchCondImpl::kBroadcast]); + closeMutex(cond->m_waitCountMutex); + delete cond; +} + +void +CArchMultithreadWindows::signalCondVar(CArchCond cond) +{ + // is anybody waiting? + lockMutex(cond->m_waitCountMutex); + const bool hasWaiter = (cond->m_waitCount > 0); + unlockMutex(cond->m_waitCountMutex); + + // wake one thread if anybody is waiting + if (hasWaiter) { + SetEvent(cond->m_events[CArchCondImpl::kSignal]); + } +} + +void +CArchMultithreadWindows::broadcastCondVar(CArchCond cond) +{ + // is anybody waiting? + lockMutex(cond->m_waitCountMutex); + const bool hasWaiter = (cond->m_waitCount > 0); + unlockMutex(cond->m_waitCountMutex); + + // wake all threads if anybody is waiting + if (hasWaiter) { + SetEvent(cond->m_events[CArchCondImpl::kBroadcast]); + } +} + +bool +CArchMultithreadWindows::waitCondVar(CArchCond cond, + CArchMutex mutex, double timeout) +{ + // prepare to wait + const DWORD winTimeout = (timeout < 0.0) ? INFINITE : + static_cast(1000.0 * timeout); + + // make a list of the condition variable events and the cancel event + // for the current thread. + HANDLE handles[3]; + handles[0] = cond->m_events[CArchCondImpl::kSignal]; + handles[1] = cond->m_events[CArchCondImpl::kBroadcast]; + handles[2] = getCancelEventForCurrentThread(); + + // update waiter count + lockMutex(cond->m_waitCountMutex); + ++cond->m_waitCount; + unlockMutex(cond->m_waitCountMutex); + + // release mutex. this should be atomic with the wait so that it's + // impossible for another thread to signal us between the unlock and + // the wait, which would lead to a lost signal on broadcasts. + // however, we're using a manual reset event for broadcasts which + // stays set until we reset it, so we don't lose the broadcast. + unlockMutex(mutex); + + // wait for a signal or broadcast + DWORD result = WaitForMultipleObjects(3, handles, FALSE, winTimeout); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 2 && + WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 2; + } + + // update the waiter count and check if we're the last waiter + lockMutex(cond->m_waitCountMutex); + --cond->m_waitCount; + const bool last = (result == WAIT_OBJECT_0 + 1 && cond->m_waitCount == 0); + unlockMutex(cond->m_waitCountMutex); + + // reset the broadcast event if we're the last waiter + if (last) { + ResetEvent(cond->m_events[CArchCondImpl::kBroadcast]); + } + + // reacquire the mutex + lockMutex(mutex); + + // cancel thread if necessary + if (result == WAIT_OBJECT_0 + 2) { + ARCH->testCancelThread(); + } + + // return success or failure + return (result == WAIT_OBJECT_0 + 0 || + result == WAIT_OBJECT_0 + 1); +} + +CArchMutex +CArchMultithreadWindows::newMutex() +{ + CArchMutexImpl* mutex = new CArchMutexImpl; + InitializeCriticalSection(&mutex->m_mutex); + return mutex; +} + +void +CArchMultithreadWindows::closeMutex(CArchMutex mutex) +{ + DeleteCriticalSection(&mutex->m_mutex); + delete mutex; +} + +void +CArchMultithreadWindows::lockMutex(CArchMutex mutex) +{ + EnterCriticalSection(&mutex->m_mutex); +} + +void +CArchMultithreadWindows::unlockMutex(CArchMutex mutex) +{ + LeaveCriticalSection(&mutex->m_mutex); +} + +CArchThread +CArchMultithreadWindows::newThread(ThreadFunc func, void* data) +{ + lockMutex(m_threadMutex); + + // create thread impl for new thread + CArchThreadImpl* thread = new CArchThreadImpl; + thread->m_func = func; + thread->m_userData = data; + + // create thread + unsigned int id; + thread->m_thread = reinterpret_cast(_beginthreadex(NULL, 0, + threadFunc, (void*)thread, 0, &id)); + thread->m_id = static_cast(id); + + // check if thread was started + if (thread->m_thread == 0) { + // failed to start thread so clean up + delete thread; + thread = NULL; + } + else { + // add thread to list + insert(thread); + + // increment ref count to account for the thread itself + refThread(thread); + } + + // note that the child thread will wait until we release this mutex + unlockMutex(m_threadMutex); + + return thread; +} + +CArchThread +CArchMultithreadWindows::newCurrentThread() +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = find(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + assert(thread != NULL); + return thread; +} + +void +CArchMultithreadWindows::closeThread(CArchThread thread) +{ + assert(thread != NULL); + + // decrement ref count and clean up thread if no more references + if (--thread->m_refCount == 0) { + // close the handle (main thread has a NULL handle) + if (thread->m_thread != NULL) { + CloseHandle(thread->m_thread); + } + + // remove thread from list + lockMutex(m_threadMutex); + assert(findNoRef(thread->m_id) == thread); + erase(thread); + unlockMutex(m_threadMutex); + + // done with thread + delete thread; + } +} + +CArchThread +CArchMultithreadWindows::copyThread(CArchThread thread) +{ + refThread(thread); + return thread; +} + +void +CArchMultithreadWindows::cancelThread(CArchThread thread) +{ + assert(thread != NULL); + + // set cancel flag + SetEvent(thread->m_cancel); +} + +void +CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n) +{ + assert(thread != NULL); + + DWORD pClass = NORMAL_PRIORITY_CLASS; + if (n < 0) { + switch (-n) { + case 1: n = THREAD_PRIORITY_ABOVE_NORMAL; break; + case 2: n = THREAD_PRIORITY_HIGHEST; break; + default: + pClass = HIGH_PRIORITY_CLASS; + switch (-n - 3) { + case 0: n = THREAD_PRIORITY_LOWEST; break; + case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; + case 2: n = THREAD_PRIORITY_NORMAL; break; + case 3: n = THREAD_PRIORITY_ABOVE_NORMAL; break; + default: n = THREAD_PRIORITY_HIGHEST; break; + } + break; + } + } + else { + switch (n) { + case 0: n = THREAD_PRIORITY_NORMAL; break; + case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; + case 2: n = THREAD_PRIORITY_LOWEST; break; + default: n = THREAD_PRIORITY_IDLE; break; + } + } + SetPriorityClass(thread->m_thread, pClass); + SetThreadPriority(thread->m_thread, n); +} + +void +CArchMultithreadWindows::testCancelThread() +{ + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + + // test cancel on thread + testCancelThreadImpl(thread); +} + +bool +CArchMultithreadWindows::wait(CArchThread target, double timeout) +{ + assert(target != NULL); + + lockMutex(m_threadMutex); + + // find current thread + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + + // ignore wait if trying to wait on ourself + if (target == self) { + unlockMutex(m_threadMutex); + return false; + } + + // ref the target so it can't go away while we're watching it + refThread(target); + + unlockMutex(m_threadMutex); + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for the target thread to + // terminate. + HANDLE handles[2]; + handles[0] = target->m_exit; + handles[1] = self->m_cancel; + DWORD result = WaitForMultipleObjects(2, handles, FALSE, t); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 1 && + WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 1; + } + + // release target + closeThread(target); + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 0: + // target thread terminated + return true; + + case WAIT_OBJECT_0 + 1: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + default: + // timeout or error + return false; + } +} + +bool +CArchMultithreadWindows::waitForEvent(double timeout) +{ + // check if messages are available first. if we don't do this then + // MsgWaitForMultipleObjects() will block even if the queue isn't + // empty if the messages in the queue were there before the last + // call to GetMessage()/PeekMessage(). + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + return true; + } + + // find current thread + lockMutex(m_threadMutex); + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + unlockMutex(m_threadMutex); + assert(self != NULL); + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for a message + HANDLE handles[1]; + handles[0] = self->m_cancel; + DWORD result = MsgWaitForMultipleObjects(1, handles, FALSE, t, QS_ALLINPUT); + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 1: + // message is available + return true; + + case WAIT_OBJECT_0 + 0: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + default: + // timeout or error + return false; + } +} + +bool +CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2) +{ + return (thread1 == thread2); +} + +bool +CArchMultithreadWindows::isExitedThread(CArchThread thread) +{ + // poll exit event + return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0); +} + +void* +CArchMultithreadWindows::getResultOfThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* result = thread->m_result; + unlockMutex(m_threadMutex); + return result; +} + +IArchMultithread::ThreadID +CArchMultithreadWindows::getIDOfThread(CArchThread thread) +{ + return static_cast(thread->m_id); +} + +CArchThreadImpl* +CArchMultithreadWindows::find(DWORD id) +{ + CArchThreadImpl* impl = findNoRef(id); + if (impl != NULL) { + refThread(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadWindows::findNoRef(DWORD id) +{ + // linear search + for (CThreadList::const_iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if ((*index)->m_id == id) { + return *index; + } + } + return NULL; +} + +void +CArchMultithreadWindows::insert(CArchThreadImpl* thread) +{ + assert(thread != NULL); + + // thread shouldn't already be on the list + assert(findNoRef(thread->m_id) == NULL); + + // append to list + m_threadList.push_back(thread); +} + +void +CArchMultithreadWindows::erase(CArchThreadImpl* thread) +{ + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + if (*index == thread) { + m_threadList.erase(index); + break; + } + } +} + +void +CArchMultithreadWindows::refThread(CArchThreadImpl* thread) +{ + assert(thread != NULL); + assert(findNoRef(thread->m_id) != NULL); + ++thread->m_refCount; +} + +void +CArchMultithreadWindows::testCancelThreadImpl(CArchThread thread) +{ + assert(thread != NULL); + + // poll cancel event. return if not set. + const DWORD result = WaitForSingleObject(thread->m_cancel, 0); + if (result != WAIT_OBJECT_0) { + return; + } + + // update cancel state + lockMutex(m_threadMutex); + bool cancel = !thread->m_cancelling; + thread->m_cancelling = true; + ResetEvent(thread->m_cancel); + unlockMutex(m_threadMutex); + + // unwind thread's stack if cancelling + if (cancel) { + throw XThreadCancel(); + } +} + +unsigned int __stdcall +CArchMultithreadWindows::threadFunc(void* vrep) +{ + // get the thread + CArchThreadImpl* thread = reinterpret_cast(vrep); + + // run thread + s_instance->doThreadFunc(thread); + + // terminate the thread + return 0; +} + +void +CArchMultithreadWindows::doThreadFunc(CArchThread thread) +{ + // default priority is slightly below normal + setPriorityOfThread(thread, 1); + + // wait for parent to initialize this object + lockMutex(m_threadMutex); + unlockMutex(m_threadMutex); + + void* result = NULL; + try { + // go + result = (*thread->m_func)(thread->m_userData); + } + + catch (XThreadCancel&) { + // client called cancel() + } + catch (...) { + // note -- don't catch (...) to avoid masking bugs + SetEvent(thread->m_exit); + closeThread(thread); + throw; + } + + // thread has exited + lockMutex(m_threadMutex); + thread->m_result = result; + unlockMutex(m_threadMutex); + SetEvent(thread->m_exit); + + // done with thread + closeThread(thread); +} diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h new file mode 100644 index 00000000..74b2d36b --- /dev/null +++ b/lib/arch/CArchMultithreadWindows.h @@ -0,0 +1,99 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHMULTITHREADWINDOWS_H +#define CARCHMULTITHREADWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchMultithread.h" +#include "stdlist.h" +#include + +#define ARCH_MULTITHREAD CArchMultithreadWindows + +class CArchCondImpl { +public: + enum { kSignal = 0, kBroadcast }; + + HANDLE m_events[2]; + mutable int m_waitCount; + CArchMutex m_waitCountMutex; +}; + +class CArchMutexImpl { +public: + CRITICAL_SECTION m_mutex; +}; + +class CArchMultithreadWindows : public IArchMultithread { +public: + CArchMultithreadWindows(); + virtual ~CArchMultithreadWindows(); + + // + // accessors + // + + HANDLE getCancelEventForCurrentThread(); + + static CArchMultithreadWindows* getInstance(); + + // IArchMultithread overrides + virtual CArchCond newCondVar(); + virtual void closeCondVar(CArchCond); + virtual void signalCondVar(CArchCond); + virtual void broadcastCondVar(CArchCond); + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout); + virtual CArchMutex newMutex(); + virtual void closeMutex(CArchMutex); + virtual void lockMutex(CArchMutex); + virtual void unlockMutex(CArchMutex); + virtual CArchThread newThread(ThreadFunc, void*); + virtual CArchThread newCurrentThread(); + virtual CArchThread copyThread(CArchThread); + virtual void closeThread(CArchThread); + virtual void cancelThread(CArchThread); + virtual void setPriorityOfThread(CArchThread, int n); + virtual void testCancelThread(); + virtual bool wait(CArchThread, double timeout); + virtual bool waitForEvent(double timeout); + virtual bool isSameThread(CArchThread, CArchThread); + virtual bool isExitedThread(CArchThread); + virtual void* getResultOfThread(CArchThread); + virtual ThreadID getIDOfThread(CArchThread); + +private: + CArchThreadImpl* find(DWORD id); + CArchThreadImpl* findNoRef(DWORD id); + void insert(CArchThreadImpl* thread); + void erase(CArchThreadImpl* thread); + + void refThread(CArchThreadImpl* rep); + void testCancelThreadImpl(CArchThreadImpl* rep); + + void doThreadFunc(CArchThread thread); + static unsigned int __stdcall threadFunc(void* vrep); + +private: + typedef std::list CThreadList; + + static CArchMultithreadWindows* s_instance; + + CArchMutex m_threadMutex; + + CThreadList m_threadList; +}; + +#endif diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp new file mode 100644 index 00000000..8e3f4706 --- /dev/null +++ b/lib/arch/CArchNetworkBSD.cpp @@ -0,0 +1,847 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchNetworkBSD.h" +#include "CArch.h" +#include "XArchUnix.h" +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif +#include +#include +#if !defined(TCP_NODELAY) +# include +#endif +#include +#include +#include + +#if HAVE_POLL +# include +#else +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TIME_H +# include +# endif +#endif + +static const int s_family[] = { + PF_INET +}; +static const int s_type[] = { + SOCK_DGRAM, + SOCK_STREAM +}; + +// +// CArchNetworkBSD +// + +CArchNetworkBSD::CArchNetworkBSD() +{ + // create mutex to make some calls thread safe + m_mutex = ARCH->newMutex(); +} + +CArchNetworkBSD::~CArchNetworkBSD() +{ + ARCH->closeMutex(m_mutex); +} + +CArchSocket +CArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type) +{ + // allocate socket object + CArchSocketImpl* newSocket = new CArchSocketImpl; + + // create socket + int fd = socket(s_family[family], s_type[type], 0); + if (fd == -1) { + throwError(errno); + } + + newSocket->m_fd = fd; + newSocket->m_connected = false; + newSocket->m_refCount = 1; + return newSocket; +} + +CArchSocket +CArchNetworkBSD::copySocket(CArchSocket s) +{ + assert(s != NULL); + + // ref the socket and return it + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + return s; +} + +void +CArchNetworkBSD::closeSocket(CArchSocket s) +{ + assert(s != NULL); + + // unref the socket and note if it should be released + ARCH->lockMutex(m_mutex); + const bool doClose = (--s->m_refCount == 0); + ARCH->unlockMutex(m_mutex); + + // close the socket if necessary + if (doClose) { + do { + if (close(s->m_fd) == -1) { + // close failed + int err = errno; + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + // restore the last ref and throw + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } + } while (false); + delete s; + } +} + +void +CArchNetworkBSD::closeSocketForRead(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown(s->m_fd, 0) == -1) { + if (errno != ENOTCONN) { + throwError(errno); + } + } +} + +void +CArchNetworkBSD::closeSocketForWrite(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown(s->m_fd, 1) == -1) { + if (errno != ENOTCONN) { + throwError(errno); + } + } +} + +void +CArchNetworkBSD::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + if (bind(s->m_fd, &addr->m_addr, addr->m_len) == -1) { + throwError(errno); + } +} + +void +CArchNetworkBSD::listenOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // hardcoding backlog + if (listen(s->m_fd, 3) == -1) { + throwError(errno); + } +} + +CArchSocket +CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + assert(s != NULL); + + // if user passed NULL in addr then use scratch space + CArchNetAddress dummy; + if (addr == NULL) { + addr = &dummy; + } + + // create new socket and address + CArchSocketImpl* newSocket = new CArchSocketImpl; + *addr = new CArchNetAddressImpl; + + // accept on socket + int fd; + do { + fd = accept(s->m_fd, &(*addr)->m_addr, &(*addr)->m_len); + if (fd == -1) { + int err = errno; + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + if (err == ECONNABORTED) { + // connection was aborted; try again + ARCH->testCancelThread(); + continue; + } + delete newSocket; + delete *addr; + *addr = NULL; + throwError(err); + } + } while (false); + + // initialize socket + newSocket->m_fd = fd; + newSocket->m_connected = true; + newSocket->m_refCount = 1; + + // discard address if not requested + if (addr == &dummy) { + ARCH->closeAddr(dummy); + } + + return newSocket; +} + +void +CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + do { + if (connect(s->m_fd, &addr->m_addr, addr->m_len) == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + if (errno == EISCONN) { + // already connected + break; + } + + if (errno == EAGAIN) { + // connecting + throw XArchNetworkConnecting(new XArchEvalUnix(errno)); + } + + throwError(errno); + } + } while (false); + + ARCH->lockMutex(m_mutex); + s->m_connected = true; + ARCH->unlockMutex(m_mutex); +} + +#if HAVE_POLL + +int +CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) +{ + assert(pe != NULL || num == 0); + + // return if nothing to do + if (num == 0) { + if (timeout > 0.0) { + ARCH->sleep(timeout); + } + return 0; + } + + // allocate space for translated query + struct pollfd* pfd = new struct pollfd[num]; + + // translate query + for (int i = 0; i < num; ++i) { + pfd[i].fd = (pe[i].m_socket == NULL) ? -1 : pe[i].m_socket->m_fd; + pfd[i].events = 0; + if ((pe[i].m_events & kPOLLIN) != 0) { + pfd[i].events |= POLLIN; + } + if ((pe[i].m_events & kPOLLOUT) != 0) { + pfd[i].events |= POLLOUT; + } + } + + // do the poll + int n; + do { + n = poll(pfd, num, static_cast(1000.0 * timeout)); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + delete[] pfd; + throwError(errno); + } + } while (false); + + // translate back + for (int i = 0; i < num; ++i) { + pe[i].m_revents = 0; + if ((pfd[i].revents & POLLIN) != 0) { + pe[i].m_revents |= kPOLLIN; + } + if ((pfd[i].revents & POLLOUT) != 0) { + pe[i].m_revents |= kPOLLOUT; + } + if ((pfd[i].revents & POLLERR) != 0) { + pe[i].m_revents |= kPOLLERR; + } + if ((pfd[i].revents & POLLNVAL) != 0) { + pe[i].m_revents |= kPOLLNVAL; + } + } + + // done with translated query + delete[] pfd; + + return n; +} + +#else + +void +CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) +{ + int i, n; + + do { + // prepare sets for select + n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; + + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } + + int fdi = pe[i].m_socket->m_fd; + if (pe[i].m_events & kPOLLIN) { + FD_SET(pe[i].m_socket->m_fd, &readSet); + readSetP = &readSet; + if (fdi > n) { + n = fdi; + } + } + if (pe[i].m_events & kPOLLOUT) { + FD_SET(pe[i].m_socket->m_fd, &writeSet); + writeSetP = &writeSet; + if (fdi > n) { + n = fdi; + } + } + if (true) { + FD_SET(pe[i].m_socket->m_fd, &errSet); + errSetP = &errSet; + if (fdi > n) { + n = fdi; + } + } + } + + // if there are no sockets then don't block forever + if (n == 0 && timeout < 0.0) { + timeout = 0.0; + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = static_cast(timeout); + timeout2.tv_usec = static_cast(1.0e+6 * + (timeout - timeout2.tv_sec)); + } + + // do the select + n = select((SELECT_TYPE_ARG1) n + 1, + SELECT_TYPE_ARG234 readSetP, + SELECT_TYPE_ARG234 writeSetP, + SELECT_TYPE_ARG234 errSetP, + SELECT_TYPE_ARG5 timeout2P); + + // handle results + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + n = 0; + for (i = 0; i < num; ++i) { + if (pe[i].m_socket != NULL) { + if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) { + pe[i].m_revents |= kPOLLIN; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) { + pe[i].m_revents |= kPOLLOUT; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) { + pe[i].m_revents |= kPOLLERR; + } + } + if (pe[i].m_revents != 0) { + ++n; + } + } + } while (false); + + return n; +} + +#endif + +size_t +CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) +{ + assert(s != NULL); + + ssize_t n; + do { + n = read(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + } while (false); + ARCH->testCancelThread(); + return n; +} + +size_t +CArchNetworkBSD::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + assert(s != NULL); + + ssize_t n; + do { + n = write(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(errno); + } + } while (false); + ARCH->testCancelThread(); + return n; +} + +void +CArchNetworkBSD::throwErrorOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // get the error from the socket layer + int err = 0; + socklen_t size = sizeof(err); + if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR, &err, &size) == -1) { + err = errno; + } + + // throw if there's an error + if (err != 0) { + throwError(err); + } +} + +bool +CArchNetworkBSD::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + assert(s != NULL); + + int mode = fcntl(s->m_fd, F_GETFL, 0); + if (mode == -1) { + throwError(errno); + } + bool old = ((mode & O_NDELAY) == 0); + if (blocking) { + mode &= ~O_NDELAY; + } + else { + mode |= O_NDELAY; + } + if (fcntl(s->m_fd, F_SETFL, mode) == -1) { + throwError(errno); + } + return old; +} + +bool +CArchNetworkBSD::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + assert(s != NULL); + + // get old state + int oflag; + socklen_t size = sizeof(oflag); + if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, &oflag, &size) == -1) { + throwError(errno); + } + + int flag = noDelay ? 1 : 0; + size = sizeof(flag); + if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, &flag, size) == -1) { + throwError(errno); + } + + return (oflag != 0); +} + +std::string +CArchNetworkBSD::getHostName() +{ + char name[256]; + if (gethostname(name, sizeof(name)) == -1) { + name[0] = '\0'; + } + else { + name[sizeof(name) - 1] = '\0'; + } + return name; +} + +CArchNetAddress +CArchNetworkBSD::newAnyAddr(EAddressFamily family) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // fill it in + switch (family) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_family = AF_INET; + ipAddr->sin_port = 0; + ipAddr->sin_addr.s_addr = INADDR_ANY; + addr->m_len = sizeof(struct sockaddr_in); + break; + } + + default: + delete addr; + assert(0 && "invalid family"); + } + + return addr; +} + +CArchNetAddress +CArchNetworkBSD::copyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + // allocate and copy address + return new CArchNetAddressImpl(*addr); +} + +CArchNetAddress +CArchNetworkBSD::nameToAddr(const std::string& name) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // try to convert assuming an IPv4 dot notation address + struct sockaddr_in inaddr; + memset(&inaddr, 0, sizeof(inaddr)); + if (inet_aton(name.c_str(), &inaddr.sin_addr) != 0) { + // it's a dot notation address + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = AF_INET; + inaddr.sin_port = 0; + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + else { + // mutexed address lookup (ugh) + ARCH->lockMutex(m_mutex); + struct hostent* info = gethostbyname(name.c_str()); + if (info == NULL) { + ARCH->unlockMutex(m_mutex); + delete addr; + throwNameError(h_errno); + } + + // copy over address (only IPv4 currently supported) + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = info->h_addrtype; + inaddr.sin_port = 0; + memcpy(&inaddr.sin_addr, info->h_addr_list[0], info->h_length); + memcpy(&addr->m_addr, &inaddr, addr->m_len); + + // done with static buffer + ARCH->unlockMutex(m_mutex); + } + + return addr; +} + +void +CArchNetworkBSD::closeAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + delete addr; +} + +std::string +CArchNetworkBSD::addrToName(CArchNetAddress addr) +{ + assert(addr != NULL); + + // mutexed name lookup (ugh) + ARCH->lockMutex(m_mutex); + struct hostent* info = gethostbyaddr(&addr->m_addr, addr->m_len, + addr->m_addr.sa_family); + if (info == NULL) { + ARCH->unlockMutex(m_mutex); + throwNameError(h_errno); + } + + // save (primary) name + std::string name = info->h_name; + + // done with static buffer + ARCH->unlockMutex(m_mutex); + + return name; +} + +std::string +CArchNetworkBSD::addrToString(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ARCH->lockMutex(m_mutex); + std::string s = inet_ntoa(ipAddr->sin_addr); + ARCH->unlockMutex(m_mutex); + return s; + } + + default: + assert(0 && "unknown address family"); + return ""; + } +} + +IArchNetwork::EAddressFamily +CArchNetworkBSD::getAddrFamily(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (addr->m_addr.sa_family) { + case AF_INET: + return kINET; + + default: + return kUNKNOWN; + } +} + +void +CArchNetworkBSD::setAddrPort(CArchNetAddress addr, int port) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_port = htons(port); + break; + } + + default: + assert(0 && "unknown address family"); + break; + } +} + +int +CArchNetworkBSD::getAddrPort(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return ntohs(ipAddr->sin_port); + } + + default: + assert(0 && "unknown address family"); + return 0; + } +} + +bool +CArchNetworkBSD::isAnyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return (ipAddr->sin_addr.s_addr == INADDR_ANY && + addr->m_len == sizeof(struct sockaddr_in)); + } + + default: + assert(0 && "unknown address family"); + return true; + } +} + +void +CArchNetworkBSD::throwError(int err) +{ + switch (err) { + case EAGAIN: + throw XArchNetworkWouldBlock(new XArchEvalUnix(err)); + + case EACCES: + case EPERM: + throw XArchNetworkAccess(new XArchEvalUnix(err)); + + case ENFILE: + case EMFILE: + case ENODEV: + case ENOBUFS: + case ENOMEM: + case ENOSR: + case ENETDOWN: + throw XArchNetworkResource(new XArchEvalUnix(err)); + + case EPROTOTYPE: + case EPROTONOSUPPORT: + case EAFNOSUPPORT: + case EPFNOSUPPORT: + case ESOCKTNOSUPPORT: + case EINVAL: + case ENOPROTOOPT: + case EOPNOTSUPP: + case ENOPKG: + case ESHUTDOWN: + throw XArchNetworkSupport(new XArchEvalUnix(err)); + + case EIO: + throw XArchNetworkIO(new XArchEvalUnix(err)); + + case EADDRNOTAVAIL: + throw XArchNetworkNoAddress(new XArchEvalUnix(err)); + + case EADDRINUSE: + throw XArchNetworkAddressInUse(new XArchEvalUnix(err)); + + case EHOSTUNREACH: + case ENETUNREACH: + throw XArchNetworkNoRoute(new XArchEvalUnix(err)); + + case ENOTCONN: + throw XArchNetworkNotConnected(new XArchEvalUnix(err)); + + case EPIPE: + case ECONNABORTED: + case ECONNRESET: + throw XArchNetworkDisconnected(new XArchEvalUnix(err)); + + case ECONNREFUSED: + throw XArchNetworkConnectionRefused(new XArchEvalUnix(err)); + + case EINPROGRESS: + case EALREADY: + throw XArchNetworkConnecting(new XArchEvalUnix(err)); + + case EHOSTDOWN: + case ETIMEDOUT: + throw XArchNetworkTimedOut(new XArchEvalUnix(err)); + + default: + throw XArchNetwork(new XArchEvalUnix(err)); + } +} + +void +CArchNetworkBSD::throwNameError(int err) +{ + static const char* s_msg[] = { + "The specified host is unknown", + "The requested name is valid but does not have an IP address", + "A non-recoverable name server error occurred", + "A temporary error occurred on an authoritative name server", + "An unknown name server error occurred" + }; + + switch (err) { + case HOST_NOT_FOUND: + throw XArchNetworkNameUnknown(s_msg[0]); + + case NO_DATA: + throw XArchNetworkNameNoAddress(s_msg[1]); + + case NO_RECOVERY: + throw XArchNetworkNameFailure(s_msg[2]); + + case TRY_AGAIN: + throw XArchNetworkNameUnavailable(s_msg[3]); + + default: + throw XArchNetworkName(s_msg[4]); + } +} diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h new file mode 100644 index 00000000..2d2eda76 --- /dev/null +++ b/lib/arch/CArchNetworkBSD.h @@ -0,0 +1,95 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHNETWORKBSD_H +#define CARCHNETWORKBSD_H + +#include "IArchNetwork.h" +#include "IArchMultithread.h" +#if HAVE_SYS_SOCKET_H +# include +#endif + +#if !defined(HAVE_SOCKLEN_T) +// Darwin is so unsure what to use for socklen_t it makes us choose +# if defined(__APPLE__) +# if !defined(_BSD_SOCKLEN_T_) +# define _BSD_SOCKLEN_T_ int +# endif +# else +typedef int socklen_t; +# endif +#endif + +#define ARCH_NETWORK CArchNetworkBSD + +class CArchSocketImpl { +public: + int m_fd; + bool m_connected; + int m_refCount; +}; + +class CArchNetAddressImpl { +public: + CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + +public: + struct sockaddr m_addr; + socklen_t m_len; +}; + +class CArchNetworkBSD : public IArchNetwork { +public: + CArchNetworkBSD(); + virtual ~CArchNetworkBSD(); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + +private: + void throwError(int); + void throwNameError(int); + +private: + CArchMutex m_mutex; +}; + +#endif diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp new file mode 100644 index 00000000..10e40188 --- /dev/null +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -0,0 +1,838 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include "CArchNetworkWinsock.h" +#include "CArch.h" +#include "XArchWindows.h" + +static const int s_family[] = { + PF_INET +}; +static const int s_type[] = { + SOCK_DGRAM, + SOCK_STREAM +}; + +static SOCKET (PASCAL FAR *accept_winsock)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen); +static int (PASCAL FAR *bind_winsock)(SOCKET s, const struct sockaddr FAR *addr, int namelen); +static int (PASCAL FAR *close_winsock)(SOCKET s); +static int (PASCAL FAR *connect_winsock)(SOCKET s, const struct sockaddr FAR *name, int namelen); +static int (PASCAL FAR *gethostname_winsock)(char FAR * name, int namelen); +static int (PASCAL FAR *getsockerror_winsock)(void); +static int (PASCAL FAR *getsockopt_winsock)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen); +static u_short (PASCAL FAR *htons_winsock)(u_short v); +static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in); +static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp); +static int (PASCAL FAR *ioctl_winsock)(SOCKET s, int cmd, void FAR *); +static int (PASCAL FAR *listen_winsock)(SOCKET s, int backlog); +static u_short (PASCAL FAR *ntohs_winsock)(u_short v); +static int (PASCAL FAR *recv_winsock)(SOCKET s, void FAR * buf, int len, int flags); +static int (PASCAL FAR *select_winsock)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); +static int (PASCAL FAR *send_winsock)(SOCKET s, const void FAR * buf, int len, int flags); +static int (PASCAL FAR *setsockopt_winsock)(SOCKET s, int level, int optname, const void FAR * optval, int optlen); +static int (PASCAL FAR *shutdown_winsock)(SOCKET s, int how); +static SOCKET (PASCAL FAR *socket_winsock)(int af, int type, int protocol); +static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type); +static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); +static int (PASCAL FAR *WSACleanup_winsock)(void); +static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR *); + +#undef FD_ISSET +#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) + +#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name) + +static HMODULE s_networkModule = NULL; + +static +FARPROC +netGetProcAddress(HMODULE module, LPCSTR name) +{ + FARPROC func = ::GetProcAddress(module, name); + if (!func) { + throw XArchNetworkSupport(""); + } + return func; +} + +// +// CArchNetworkWinsock +// + +CArchNetworkWinsock::CArchNetworkWinsock() +{ + static const char* s_library[] = { "ws2_32.dll", "wsock32.dll" }; + + assert(WSACleanup_winsock == NULL); + assert(s_networkModule == NULL); + + // try each winsock library + for (size_t i = 0; i < sizeof(s_library) / sizeof(s_library[0]); ++i) { + try { + init((HMODULE)::LoadLibrary(s_library[i])); + m_mutex = ARCH->newMutex(); + return; + } + catch (XArchNetwork&) { + // ignore + } + } + + // can't initialize any library + throw XArchNetworkSupport("Cannot load winsock library"); +} + +CArchNetworkWinsock::~CArchNetworkWinsock() +{ + if (s_networkModule != NULL) { + WSACleanup_winsock(); + ::FreeLibrary(s_networkModule); + + WSACleanup_winsock = NULL; + s_networkModule = NULL; + } + ARCH->closeMutex(m_mutex); +} + +void +CArchNetworkWinsock::init(HMODULE module) +{ + assert(module != NULL); + + // get startup function address + int (PASCAL FAR *startup)(WORD, LPWSADATA); + setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA)); + + // startup network library + WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); + WSADATA data; + int err = startup(version, &data); + if (data.wVersion != version) { + throw XArchNetworkSupport(new XArchEvalWinsock(err)); + } + if (err != 0) { + // some other initialization error + throwError(err); + } + + // get function addresses + setfunc(accept_winsock, accept, SOCKET (PASCAL FAR *)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen)); + setfunc(bind_winsock, bind, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *addr, int namelen)); + setfunc(close_winsock, closesocket, int (PASCAL FAR *)(SOCKET s)); + setfunc(connect_winsock, connect, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *name, int namelen)); + setfunc(gethostname_winsock, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); + setfunc(getsockerror_winsock, WSAGetLastError, int (PASCAL FAR *)(void)); + setfunc(getsockopt_winsock, getsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen)); + setfunc(htons_winsock, htons, u_short (PASCAL FAR *)(u_short v)); + setfunc(inet_ntoa_winsock, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(inet_addr_winsock, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(ioctl_winsock, ioctlsocket, int (PASCAL FAR *)(SOCKET s, int cmd, void FAR *)); + setfunc(listen_winsock, listen, int (PASCAL FAR *)(SOCKET s, int backlog)); + setfunc(ntohs_winsock, ntohs, u_short (PASCAL FAR *)(u_short v)); + setfunc(recv_winsock, recv, int (PASCAL FAR *)(SOCKET s, void FAR * buf, int len, int flags)); + setfunc(select_winsock, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); + setfunc(send_winsock, send, int (PASCAL FAR *)(SOCKET s, const void FAR * buf, int len, int flags)); + setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, const void FAR * optval, int optlen)); + setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(SOCKET s, int how)); + setfunc(socket_winsock, socket, SOCKET (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void)); + setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *)); + + s_networkModule = module; +} + +CArchSocket +CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type) +{ + // allocate socket object + CArchSocketImpl* socket = new CArchSocketImpl; + + // create socket + SOCKET fd = socket_winsock(s_family[family], s_type[type], 0); + if (fd == INVALID_SOCKET) { + throwError(getsockerror_winsock()); + } + + socket->m_socket = fd; + socket->m_connected = false; + socket->m_refCount = 1; + return socket; +} + +CArchSocket +CArchNetworkWinsock::copySocket(CArchSocket s) +{ + assert(s != NULL); + + // ref the socket and return it + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + return s; +} + +void +CArchNetworkWinsock::closeSocket(CArchSocket s) +{ + assert(s != NULL); + + // unref the socket and note if it should be released + ARCH->lockMutex(m_mutex); + const bool doClose = (--s->m_refCount == 0); + ARCH->unlockMutex(m_mutex); + + // close the socket if necessary + if (doClose) { + do { + if (close_winsock(s->m_socket) == SOCKET_ERROR) { + // close failed + int err = getsockerror_winsock(); + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + // restore the last ref and throw + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } + } while (false); + delete s; + } +} + +void +CArchNetworkWinsock::closeSocketForRead(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) { + if (getsockerror_winsock() != WSAENOTCONN) { + throwError(getsockerror_winsock()); + } + } +} + +void +CArchNetworkWinsock::closeSocketForWrite(CArchSocket s) +{ + assert(s != NULL); + + if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) { + if (getsockerror_winsock() != WSAENOTCONN) { + throwError(getsockerror_winsock()); + } + } +} + +void +CArchNetworkWinsock::bindSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + if (bind_winsock(s->m_socket, &addr->m_addr, addr->m_len) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } +} + +void +CArchNetworkWinsock::listenOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // hardcoding backlog + if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } +} + +CArchSocket +CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) +{ + assert(s != NULL); + + // if user passed NULL in addr then use scratch space + CArchNetAddress dummy; + if (addr == NULL) { + addr = &dummy; + } + + // create new socket and address + CArchSocketImpl* socket = new CArchSocketImpl; + *addr = new CArchNetAddressImpl; + + // accept on socket + SOCKET fd; + do { + fd = accept_winsock(s->m_socket, &(*addr)->m_addr, &(*addr)->m_len); + if (fd == INVALID_SOCKET) { + int err = getsockerror_winsock(); + if (err == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + if (err == WSAECONNABORTED) { + // connection was aborted; try again + ARCH->testCancelThread(); + continue; + } + delete socket; + delete *addr; + *addr = NULL; + throwError(err); + } + } while (false); + + // initialize socket + socket->m_socket = fd; + socket->m_connected = true; + socket->m_refCount = 1; + + // discard address if not requested + if (addr == &dummy) { + ARCH->closeAddr(dummy); + } + + return socket; +} + +void +CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr) +{ + assert(s != NULL); + assert(addr != NULL); + + do { + if (connect_winsock(s->m_socket, &addr->m_addr, + addr->m_len) == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + + if (getsockerror_winsock() == WSAEISCONN) { + // already connected + break; + } + + if (getsockerror_winsock() == WSAEWOULDBLOCK) { + // connecting + throw XArchNetworkConnecting(new XArchEvalWinsock( + getsockerror_winsock())); + } + + throwError(getsockerror_winsock()); + } + } while (false); + + ARCH->lockMutex(m_mutex); + s->m_connected = true; + ARCH->unlockMutex(m_mutex); +} + +int +CArchNetworkWinsock::pollSocket(CPollEntry pe[], int num, double timeout) +{ + int i, n; + + do { + // prepare sets for select + n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; + + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } + + if (pe[i].m_events & kPOLLIN) { + FD_SET(pe[i].m_socket->m_socket, &readSet); + readSetP = &readSet; + n = 1; + } + if (pe[i].m_events & kPOLLOUT) { + FD_SET(pe[i].m_socket->m_socket, &writeSet); + writeSetP = &writeSet; + n = 1; + } + if (true) { + FD_SET(pe[i].m_socket->m_socket, &errSet); + errSetP = &errSet; + n = 1; + } + } + + // if there are no sockets then don't block forever + if (n == 0 && timeout < 0.0) { + timeout = 0.0; + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = static_cast(timeout); + timeout2.tv_usec = static_cast(1.0e+6 * + (timeout - timeout2.tv_sec)); + } + + // do the select + n = select_winsock(0, readSetP, writeSetP, errSetP, timeout2P); + + // handle results + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + n = 0; + for (i = 0; i < num; ++i) { + if (pe[i].m_socket != NULL) { + if (FD_ISSET(pe[i].m_socket->m_socket, &readSet)) { + pe[i].m_revents |= kPOLLIN; + } + if (FD_ISSET(pe[i].m_socket->m_socket, &writeSet)) { + pe[i].m_revents |= kPOLLOUT; + } + if (FD_ISSET(pe[i].m_socket->m_socket, &errSet)) { + pe[i].m_revents |= kPOLLERR; + } + } + if (pe[i].m_revents != 0) { + ++n; + } + } + } while (false); + + return n; +} + +size_t +CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len) +{ + assert(s != NULL); + + int n; + do { + n = recv_winsock(s->m_socket, buf, len, 0); + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + } while (false); + ARCH->testCancelThread(); + return static_cast(n); +} + +size_t +CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len) +{ + assert(s != NULL); + + int n; + do { + n = send_winsock(s->m_socket, buf, len, 0); + if (n == SOCKET_ERROR) { + if (getsockerror_winsock() == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + continue; + } + throwError(getsockerror_winsock()); + } + } while (false); + ARCH->testCancelThread(); + return static_cast(n); +} + +void +CArchNetworkWinsock::throwErrorOnSocket(CArchSocket s) +{ + assert(s != NULL); + + // get the error from the socket layer + int err = 0; + int size = sizeof(err); + if (getsockopt_winsock(s->m_socket, SOL_SOCKET, + SO_ERROR, &err, &size) == SOCKET_ERROR) { + err = getsockerror_winsock(); + } + + // throw if there's an error + if (err != 0) { + throwError(err); + } +} + +bool +CArchNetworkWinsock::setBlockingOnSocket(CArchSocket s, bool blocking) +{ + assert(s != NULL); + + int flag = blocking ? 0 : 1; + if (ioctl_winsock(s->m_socket, FIONBIO, &flag) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + // FIXME -- can't get the current blocking state of socket? + return true; +} + +bool +CArchNetworkWinsock::setNoDelayOnSocket(CArchSocket s, bool noDelay) +{ + assert(s != NULL); + + // get old state + BOOL oflag; + int size = sizeof(oflag); + if (getsockopt_winsock(s->m_socket, IPPROTO_TCP, + TCP_NODELAY, &oflag, &size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + // set new state + BOOL flag = noDelay ? 1 : 0; + size = sizeof(flag); + if (setsockopt_winsock(s->m_socket, IPPROTO_TCP, + TCP_NODELAY, &flag, size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + return (oflag != 0); +} + +std::string +CArchNetworkWinsock::getHostName() +{ + char name[256]; + if (gethostname_winsock(name, sizeof(name)) == -1) { + name[0] = '\0'; + } + else { + name[sizeof(name) - 1] = '\0'; + } + return name; +} + +CArchNetAddress +CArchNetworkWinsock::newAnyAddr(EAddressFamily family) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // fill it in + switch (family) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_family = AF_INET; + ipAddr->sin_port = 0; + ipAddr->sin_addr.s_addr = INADDR_ANY; + addr->m_len = sizeof(struct sockaddr_in); + break; + } + + default: + delete addr; + assert(0 && "invalid family"); + } + + return addr; +} + +CArchNetAddress +CArchNetworkWinsock::copyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + // allocate and copy address + return new CArchNetAddressImpl(*addr); +} + +CArchNetAddress +CArchNetworkWinsock::nameToAddr(const std::string& name) +{ + // allocate address + CArchNetAddressImpl* addr = new CArchNetAddressImpl; + + // try to convert assuming an IPv4 dot notation address + struct sockaddr_in inaddr; + memset(&inaddr, 0, sizeof(inaddr)); + inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str()); + if (inaddr.sin_addr.s_addr != INADDR_NONE) { + // it's a dot notation address + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = AF_INET; + inaddr.sin_port = 0; + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + else { + // address lookup + struct hostent* info = gethostbyname_winsock(name.c_str()); + if (info == NULL) { + delete addr; + throwNameError(getsockerror_winsock()); + } + + // copy over address (only IPv4 currently supported) + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = info->h_addrtype; + inaddr.sin_port = 0; + memcpy(&inaddr.sin_addr, info->h_addr_list[0], info->h_length); + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } + + return addr; +} + +void +CArchNetworkWinsock::closeAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + delete addr; +} + +std::string +CArchNetworkWinsock::addrToName(CArchNetAddress addr) +{ + assert(addr != NULL); + + // name lookup + struct hostent* info = gethostbyaddr_winsock( + reinterpret_cast(&addr->m_addr), + addr->m_len, addr->m_addr.sa_family); + if (info == NULL) { + throwNameError(getsockerror_winsock()); + } + + // return (primary) name + return info->h_name; +} + +std::string +CArchNetworkWinsock::addrToString(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return inet_ntoa_winsock(ipAddr->sin_addr); + } + + default: + assert(0 && "unknown address family"); + return ""; + } +} + +IArchNetwork::EAddressFamily +CArchNetworkWinsock::getAddrFamily(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (addr->m_addr.sa_family) { + case AF_INET: + return kINET; + + default: + return kUNKNOWN; + } +} + +void +CArchNetworkWinsock::setAddrPort(CArchNetAddress addr, int port) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + ipAddr->sin_port = htons_winsock(static_cast(port)); + break; + } + + default: + assert(0 && "unknown address family"); + break; + } +} + +int +CArchNetworkWinsock::getAddrPort(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return ntohs_winsock(ipAddr->sin_port); + } + + default: + assert(0 && "unknown address family"); + return 0; + } +} + +bool +CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr) +{ + assert(addr != NULL); + + switch (getAddrFamily(addr)) { + case kINET: { + struct sockaddr_in* ipAddr = + reinterpret_cast(&addr->m_addr); + return (ipAddr->sin_addr.s_addr == INADDR_ANY && + addr->m_len == sizeof(struct sockaddr_in)); + } + + default: + assert(0 && "unknown address family"); + return true; + } +} + +void +CArchNetworkWinsock::throwError(int err) +{ + switch (err) { + case WSAEWOULDBLOCK: + throw XArchNetworkWouldBlock(new XArchEvalWinsock(err)); + + case WSAEACCES: + throw XArchNetworkAccess(new XArchEvalWinsock(err)); + + case WSAEMFILE: + case WSAENOBUFS: + case WSAENETDOWN: + throw XArchNetworkResource(new XArchEvalWinsock(err)); + + case WSAEPROTOTYPE: + case WSAEPROTONOSUPPORT: + case WSAEAFNOSUPPORT: + case WSAEPFNOSUPPORT: + case WSAESOCKTNOSUPPORT: + case WSAEINVAL: + case WSAENOPROTOOPT: + case WSAEOPNOTSUPP: + case WSAESHUTDOWN: + case WSANOTINITIALISED: + case WSAVERNOTSUPPORTED: + case WSASYSNOTREADY: + throw XArchNetworkSupport(new XArchEvalWinsock(err)); + + case WSAEADDRNOTAVAIL: + throw XArchNetworkNoAddress(new XArchEvalWinsock(err)); + + case WSAEADDRINUSE: + throw XArchNetworkAddressInUse(new XArchEvalWinsock(err)); + + case WSAEHOSTUNREACH: + case WSAENETUNREACH: + throw XArchNetworkNoRoute(new XArchEvalWinsock(err)); + + case WSAENOTCONN: + throw XArchNetworkNotConnected(new XArchEvalWinsock(err)); + + case WSAENETRESET: + case WSAEDISCON: + case WSAECONNABORTED: + case WSAECONNRESET: + throw XArchNetworkDisconnected(new XArchEvalWinsock(err)); + + case WSAECONNREFUSED: + throw XArchNetworkConnectionRefused(new XArchEvalWinsock(err)); + + case WSAEINPROGRESS: + case WSAEALREADY: + throw XArchNetworkConnecting(new XArchEvalWinsock(err)); + + case WSAEHOSTDOWN: + case WSAETIMEDOUT: + throw XArchNetworkTimedOut(new XArchEvalWinsock(err)); + + case WSAHOST_NOT_FOUND: + throw XArchNetworkNameUnknown(new XArchEvalWinsock(err)); + + case WSANO_DATA: + throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err)); + + case WSANO_RECOVERY: + throw XArchNetworkNameFailure(new XArchEvalWinsock(err)); + + case WSATRY_AGAIN: + throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err)); + + default: + throw XArchNetwork(new XArchEvalWinsock(err)); + } +} + +void +CArchNetworkWinsock::throwNameError(int err) +{ + switch (err) { + case WSAHOST_NOT_FOUND: + throw XArchNetworkNameUnknown(new XArchEvalWinsock(err)); + + case WSANO_DATA: + throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err)); + + case WSANO_RECOVERY: + throw XArchNetworkNameFailure(new XArchEvalWinsock(err)); + + case WSATRY_AGAIN: + throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err)); + + default: + throw XArchNetworkName(new XArchEvalWinsock(err)); + } +} diff --git a/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h new file mode 100644 index 00000000..d16b7f48 --- /dev/null +++ b/lib/arch/CArchNetworkWinsock.h @@ -0,0 +1,91 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHNETWORKWINSOCK_H +#define CARCHNETWORKWINSOCK_H + +#define WIN32_LEAN_AND_MEAN + +// declare no functions in winsock2 +#define INCL_WINSOCK_API_PROTOTYPES 0 +#define INCL_WINSOCK_API_TYPEDEFS 0 + +#include "IArchNetwork.h" +#include "IArchMultithread.h" +#include +#include + +#define ARCH_NETWORK CArchNetworkWinsock + +class CArchSocketImpl { +public: + SOCKET m_socket; + bool m_connected; + int m_refCount; +}; + +class CArchNetAddressImpl { +public: + CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + +public: + struct sockaddr m_addr; + int m_len; +}; + +class CArchNetworkWinsock : public IArchNetwork { +public: + CArchNetworkWinsock(); + virtual ~CArchNetworkWinsock(); + + // IArchNetwork overrides + virtual CArchSocket newSocket(EAddressFamily, ESocketType); + virtual CArchSocket copySocket(CArchSocket s); + virtual void closeSocket(CArchSocket s); + virtual void closeSocketForRead(CArchSocket s); + virtual void closeSocketForWrite(CArchSocket s); + virtual void bindSocket(CArchSocket s, CArchNetAddress addr); + virtual void listenOnSocket(CArchSocket s); + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); + virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual size_t readSocket(CArchSocket s, void* buf, size_t len); + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len); + virtual void throwErrorOnSocket(CArchSocket); + virtual bool setBlockingOnSocket(CArchSocket, bool blocking); + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual std::string getHostName(); + virtual CArchNetAddress newAnyAddr(EAddressFamily); + virtual CArchNetAddress copyAddr(CArchNetAddress); + virtual CArchNetAddress nameToAddr(const std::string&); + virtual void closeAddr(CArchNetAddress); + virtual std::string addrToName(CArchNetAddress); + virtual std::string addrToString(CArchNetAddress); + virtual EAddressFamily getAddrFamily(CArchNetAddress); + virtual void setAddrPort(CArchNetAddress, int port); + virtual int getAddrPort(CArchNetAddress); + virtual bool isAnyAddr(CArchNetAddress); + +private: + void init(HMODULE); + + void throwError(int); + void throwNameError(int); + +private: + CArchMutex m_mutex; +}; + +#endif diff --git a/lib/arch/CArchSleepUnix.cpp b/lib/arch/CArchSleepUnix.cpp new file mode 100644 index 00000000..1e5675f4 --- /dev/null +++ b/lib/arch/CArchSleepUnix.cpp @@ -0,0 +1,88 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchSleepUnix.h" +#include "CArch.h" +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +#if !HAVE_NANOSLEEP +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include +# endif +#endif + +// +// CArchSleepUnix +// + +CArchSleepUnix::CArchSleepUnix() +{ + // do nothing +} + +CArchSleepUnix::~CArchSleepUnix() +{ + // do nothing +} + +void +CArchSleepUnix::sleep(double timeout) +{ + ARCH->testCancelThread(); + if (timeout < 0.0) { + return; + } + +#if HAVE_NANOSLEEP + // prep timeout + struct timespec t; + t.tv_sec = (long)timeout; + t.tv_nsec = (long)(1.0e+9 * (timeout - (double)t.tv_sec)); + + // wait + while (nanosleep(&t, &t) < 0) + ARCH->testCancelThread(); +#else + /* emulate nanosleep() with select() */ + double startTime = time(); + double timeLeft = timeout; + while (timeLeft > 0.0) { + struct timeval timeout2; + timeout2.tv_sec = static_cast(timeLeft); + timeout2.tv_usec = static_cast(1.0e+6 * (timeLeft - + timeout2.tv_sec)); + select((SELECT_TYPE_ARG1) 0, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 &timeout2); + ARCH->testCancelThread(); + timeLeft = timeout - (time() - startTime); + } +#endif +} diff --git a/lib/arch/CArchSleepUnix.h b/lib/arch/CArchSleepUnix.h new file mode 100644 index 00000000..deb33629 --- /dev/null +++ b/lib/arch/CArchSleepUnix.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSLEEPUNIX_H +#define CARCHSLEEPUNIX_H + +#include "IArchSleep.h" + +#define ARCH_SLEEP CArchSleepUnix + +class CArchSleepUnix : public IArchSleep { +public: + CArchSleepUnix(); + virtual ~CArchSleepUnix(); + + // IArchSleep overrides + virtual void sleep(double timeout); +}; + +#endif diff --git a/lib/arch/CArchSleepWindows.cpp b/lib/arch/CArchSleepWindows.cpp new file mode 100644 index 00000000..9c0fe596 --- /dev/null +++ b/lib/arch/CArchSleepWindows.cpp @@ -0,0 +1,54 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchSleepWindows.h" +#include "CArch.h" +#include "CArchMultithreadWindows.h" + +// +// CArchSleepWindows +// + +CArchSleepWindows::CArchSleepWindows() +{ + // do nothing +} + +CArchSleepWindows::~CArchSleepWindows() +{ + // do nothing +} + +void +CArchSleepWindows::sleep(double timeout) +{ + ARCH->testCancelThread(); + if (timeout < 0.0) { + return; + } + + // get the cancel event from the current thread. this only + // works if we're using the windows multithread object but + // this is windows so that's pretty certain; we'll get a + // link error if we're not, though. + CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance(); + if (mt != NULL) { + HANDLE cancelEvent = mt->getCancelEventForCurrentThread(); + WaitForSingleObject(cancelEvent, (DWORD)(1000.0 * timeout)); + } + else { + Sleep((DWORD)(1000.0 * timeout)); + } + ARCH->testCancelThread(); +} diff --git a/lib/arch/CArchSleepWindows.h b/lib/arch/CArchSleepWindows.h new file mode 100644 index 00000000..a142d997 --- /dev/null +++ b/lib/arch/CArchSleepWindows.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSLEEPWINDOWS_H +#define CARCHSLEEPWINDOWS_H + +#include "IArchSleep.h" + +#define ARCH_SLEEP CArchSleepWindows + +class CArchSleepWindows : public IArchSleep { +public: + CArchSleepWindows(); + virtual ~CArchSleepWindows(); + + // IArchSleep overrides + virtual void sleep(double timeout); +}; + +#endif diff --git a/lib/platform/CPlatform.h b/lib/arch/CArchStringUnix.cpp similarity index 67% rename from lib/platform/CPlatform.h rename to lib/arch/CArchStringUnix.cpp index dcfb6b02..385bb0aa 100644 --- a/lib/platform/CPlatform.h +++ b/lib/arch/CArchStringUnix.cpp @@ -12,25 +12,23 @@ * GNU General Public License for more details. */ -#ifndef CPLATFORM_H -#define CPLATFORM_H +#include "CArchStringUnix.h" +#include -#include "common.h" +#include "CMultibyte.cpp" -#if WINDOWS_LIKE +// +// CArchStringUnix +// -#include "CWin32Platform.h" -typedef CWin32Platform CPlatform; +CArchStringUnix::CArchStringUnix() +{ + initMB(); +} -#elif UNIX_LIKE +CArchStringUnix::~CArchStringUnix() +{ + cleanMB(); +} -#include "CUnixPlatform.h" -typedef CUnixPlatform CPlatform; - -#else - -#error Unsupported platform - -#endif - -#endif +#include "vsnprintf.cpp" diff --git a/lib/arch/CArchStringUnix.h b/lib/arch/CArchStringUnix.h new file mode 100644 index 00000000..fcd114f9 --- /dev/null +++ b/lib/arch/CArchStringUnix.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSTRINGUNIX_H +#define CARCHSTRINGUNIX_H + +#include "IArchString.h" + +#define ARCH_STRING CArchStringUnix + +class CArchStringUnix : public IArchString { +public: + CArchStringUnix(); + virtual ~CArchStringUnix(); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); +}; + +#endif diff --git a/lib/arch/CArchStringWindows.cpp b/lib/arch/CArchStringWindows.cpp new file mode 100644 index 00000000..a0d8727d --- /dev/null +++ b/lib/arch/CArchStringWindows.cpp @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define WIN32_LEAN_AND_MEAN + +#include "CArchStringWindows.h" +#include + +#include "CMultibyte.cpp" + +// +// CArchStringWindows +// + +CArchStringWindows::CArchStringWindows() +{ + initMB(); +} + +CArchStringWindows::~CArchStringWindows() +{ + cleanMB(); +} + +#define HAVE_VSNPRINTF 1 +#define ARCH_VSNPRINTF _vsnprintf +#include "vsnprintf.cpp" diff --git a/lib/arch/CArchStringWindows.h b/lib/arch/CArchStringWindows.h new file mode 100644 index 00000000..54b77ca3 --- /dev/null +++ b/lib/arch/CArchStringWindows.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSTRINGWINDOWS_H +#define CARCHSTRINGWINDOWS_H + +#include "IArchString.h" + +#define ARCH_STRING CArchStringWindows + +class CArchStringWindows : public IArchString { +public: + CArchStringWindows(); + virtual ~CArchStringWindows(); + + // IArchString overrides + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap); + virtual CArchMBState newMBState(); + virtual void closeMBState(CArchMBState); + virtual void initMBState(CArchMBState); + virtual bool isInitMBState(CArchMBState); + virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); + virtual int convWCToMB(char*, wchar_t, CArchMBState); +}; + +#endif diff --git a/lib/arch/CArchTimeUnix.cpp b/lib/arch/CArchTimeUnix.cpp new file mode 100644 index 00000000..49506bad --- /dev/null +++ b/lib/arch/CArchTimeUnix.cpp @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchTimeUnix.h" +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +// +// CArchTimeUnix +// + +CArchTimeUnix::CArchTimeUnix() +{ + // do nothing +} + +CArchTimeUnix::~CArchTimeUnix() +{ + // do nothing +} + +double +CArchTimeUnix::time() +{ + struct timeval t; + gettimeofday(&t, NULL); + return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; +} diff --git a/lib/arch/CArchTimeUnix.h b/lib/arch/CArchTimeUnix.h new file mode 100644 index 00000000..c9b99999 --- /dev/null +++ b/lib/arch/CArchTimeUnix.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTIMEUNIX_H +#define CARCHTIMEUNIX_H + +#include "IArchTime.h" + +#define ARCH_TIME CArchTimeUnix + +class CArchTimeUnix : public IArchTime { +public: + CArchTimeUnix(); + virtual ~CArchTimeUnix(); + + // IArchTime overrides + virtual double time(); +}; + +#endif diff --git a/lib/arch/CArchTimeWindows.cpp b/lib/arch/CArchTimeWindows.cpp new file mode 100644 index 00000000..57aee290 --- /dev/null +++ b/lib/arch/CArchTimeWindows.cpp @@ -0,0 +1,86 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +// avoid getting a lot a crap from mmsystem.h that we don't need +#define MMNODRV // Installable driver support +#define MMNOSOUND // Sound support +#define MMNOWAVE // Waveform support +#define MMNOMIDI // MIDI support +#define MMNOAUX // Auxiliary audio support +#define MMNOMIXER // Mixer support +#define MMNOJOY // Joystick support +#define MMNOMCI // MCI support +#define MMNOMMIO // Multimedia file I/O support +#define MMNOMMSYSTEM // General MMSYSTEM functions + +#define WIN32_LEAN_AND_MEAN + +#include "CArchTimeWindows.h" +#include +#include + +typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void); + +static double s_freq = 0.0; +static HINSTANCE s_mmInstance = NULL; +static PTimeGetTime s_tgt = NULL; + + +// +// CArchTimeWindows +// + +CArchTimeWindows::CArchTimeWindows() +{ + assert(s_freq == 0.0 || s_mmInstance == NULL); + + LARGE_INTEGER freq; + if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) { + s_freq = 1.0 / static_cast(freq.QuadPart); + } + else { + // load winmm.dll and get timeGetTime + s_mmInstance = LoadLibrary("winmm"); + if (s_mmInstance != NULL) { + s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime"); + } + } +} + +CArchTimeWindows::~CArchTimeWindows() +{ + s_freq = 0.0; + if (s_mmInstance == NULL) { + FreeLibrary(reinterpret_cast(s_mmInstance)); + s_tgt = NULL; + s_mmInstance = NULL; + } +} + +double +CArchTimeWindows::time() +{ + // get time. we try three ways, in order of descending precision + if (s_freq != 0.0) { + LARGE_INTEGER c; + QueryPerformanceCounter(&c); + return s_freq * static_cast(c.QuadPart); + } + else if (s_tgt != NULL) { + return 0.001 * static_cast(s_tgt()); + } + else { + return 0.001 * static_cast(GetTickCount()); + } +} diff --git a/lib/arch/CArchTimeWindows.h b/lib/arch/CArchTimeWindows.h new file mode 100644 index 00000000..0672ad78 --- /dev/null +++ b/lib/arch/CArchTimeWindows.h @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTIMEWINDOWS_H +#define CARCHTIMEWINDOWS_H + +#include "IArchTime.h" + +#define ARCH_TIME CArchTimeWindows + +class CArchTimeWindows : public IArchTime { +public: + CArchTimeWindows(); + virtual ~CArchTimeWindows(); + + // IArchTime overrides + virtual double time(); +}; + +#endif diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp new file mode 100644 index 00000000..43b8b41f --- /dev/null +++ b/lib/arch/CMultibyte.cpp @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMULTIBYTE_H +#define CMULTIBYTE_H + +#include "common.h" + +#if (HAVE_MBSINIT && HAVE_MBRTOWC && HAVE_WCRTOMB) || WINDOWS_LIKE +#include "CMultibyteOS.cpp" +#else +#include "CMultibyteEmu.cpp" +#endif + +CArchMBState +ARCH_STRING::newMBState() +{ + CArchMBState state = new CArchMBStateImpl; + initMBState(state); + return state; +} + +void +ARCH_STRING::closeMBState(CArchMBState state) +{ + delete state; +} + +#endif diff --git a/lib/arch/CMultibyteEmu.cpp b/lib/arch/CMultibyteEmu.cpp new file mode 100644 index 00000000..80569fe5 --- /dev/null +++ b/lib/arch/CMultibyteEmu.cpp @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArch.h" +#include +#include + +class CArchMBStateImpl { +public: + mbstate_t m_mbstate; +}; + +// +// use C library non-reentrant multibyte conversion with mutex +// + +static CArchMutex s_mutex; + +static +void +initMB() +{ + s_mutex = ARCH->newMutex(); +} + +static +void +cleanMB() +{ + ARCH->closeMutex(s_mutex); +} + +void +ARCH_STRING::initMBState(CArchMBState state) +{ + memset(&state->m_mbstate, 0, sizeof(state->m_mbstate)); +} + +bool +ARCH_STRING::isInitMBState(CArchMBState state) +{ +#if !HAVE_MBSINIT + return (mbsinit(&state->m_mbstate) != 0); +#else + return true; +#endif +} + +int +ARCH_STRING::convMBToWC(wchar_t* dst, const char* src, int n, CArchMBState) +{ + wchar_t dummy; + ARCH->lockMutex(s_mutex); + int result = mbtowc(dst != NULL ? dst : &dummy, src, n); + ARCH->unlockMutex(s_mutex); + if (result < 0) + return -1; + else + return result; +} + +int +ARCH_STRING::convWCToMB(char* dst, wchar_t src, CArchMBState) +{ + char dummy[MB_LEN_MAX]; + ARCH->lockMutex(s_mutex); + int n = wctomb(dst != NULL ? dst : dummy, src); + ARCH->unlockMutex(s_mutex); + return n; +} diff --git a/lib/arch/CMultibyteOS.cpp b/lib/arch/CMultibyteOS.cpp new file mode 100644 index 00000000..49a85229 --- /dev/null +++ b/lib/arch/CMultibyteOS.cpp @@ -0,0 +1,68 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +class CArchMBStateImpl { +public: + mbstate_t m_mbstate; +}; + +// +// use C library reentrant multibyte conversion +// + +static +void +initMB() +{ + // do nothing +} + +static +void +cleanMB() +{ + // do nothing +} + +void +ARCH_STRING::initMBState(CArchMBState state) +{ + memset(&state->m_mbstate, 0, sizeof(state->m_mbstate)); +} + +bool +ARCH_STRING::isInitMBState(CArchMBState state) +{ + return (mbsinit(&state->m_mbstate) != 0); +} + +int +ARCH_STRING::convMBToWC(wchar_t* dst, const char* src, + int n, CArchMBState state) +{ + wchar_t dummy; + return static_cast(mbrtowc(dst != NULL ? dst : &dummy, + src, static_cast(n), &state->m_mbstate)); +} + +int +ARCH_STRING::convWCToMB(char* dst, wchar_t src, CArchMBState state) +{ + char dummy[MB_LEN_MAX]; + return static_cast(wcrtomb(dst != NULL ? dst : dummy, + src, &state->m_mbstate)); +} diff --git a/lib/arch/IArchConsole.h b/lib/arch/IArchConsole.h new file mode 100644 index 00000000..29c3f7bd --- /dev/null +++ b/lib/arch/IArchConsole.h @@ -0,0 +1,57 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHCONSOLE_H +#define IARCHCONSOLE_H + +#include "IInterface.h" + +class IArchConsole : public IInterface { +public: + //! @name manipulators + //@{ + + //! Open the console + /*! + Opens the console for writing. The console is opened automatically + on the first write so calling this method is optional. Uses \c title + for the console's title if appropriate for the architecture. Calling + this method on an already open console must have no effect. + */ + virtual void openConsole(const char* title) = 0; + + //! Close the console + /*! + Close the console. Calling this method on an already closed console + must have no effect. + */ + virtual void closeConsole() = 0; + + //! Write to the console + /*! + Writes the given string to the console, opening it if necessary. + */ + virtual void writeConsole(const char*) = 0; + + //! Returns the newline sequence for the console + /*! + Different consoles use different character sequences for newlines. + This method returns the appropriate newline sequence for the console. + */ + virtual const char* getNewlineForConsole() = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchDaemon.h b/lib/arch/IArchDaemon.h new file mode 100644 index 00000000..ce04eb43 --- /dev/null +++ b/lib/arch/IArchDaemon.h @@ -0,0 +1,104 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHDAEMON_H +#define IARCHDAEMON_H + +#include "IInterface.h" + +//! Interface for architecture dependent daemonizing +/*! +This interface defines the operations required by synergy for installing +uninstalling daeamons and daemonizing a process. Each architecture must +implement this interface. +*/ +class IArchDaemon : public IInterface { +public: + typedef int (*DaemonFunc)(int argc, const char** argv); + + //! @name manipulators + //@{ + + //! Install daemon + /*! + Install a daemon. \c name is the name of the daemon passed to the + system and \c description is a short human readable description of + the daemon. \c pathname is the path to the daemon executable. + \c commandLine should \b not include the name of program as the + first argument. If \c allUsers is true then the daemon will be + installed to start at boot time, otherwise it will be installed to + start when the current user logs in. Throws an \c XArchDaemon + exception on failure. + */ + virtual void installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + bool allUsers) = 0; + + //! Uninstall daemon + /*! + Uninstall a daemon. Throws an \c XArchDaemon on failure. + */ + virtual void uninstallDaemon(const char* name, bool allUsers) = 0; + + //! Daemonize the process + /*! + Daemonize. Throw XArchDaemonFailed on error. \c name is the name + of the daemon. Once daemonized, \c func is invoked and daemonize + returns when and what it does. + + Exactly what happens when daemonizing depends on the platform. +
    +
  • unix: + Detaches from terminal. \c func gets passed one argument, the + name passed to daemonize(). +
  • win32: + Becomes a service. Argument 0 is the name of the service + and the rest are the arguments passed to StartService(). + \c func is only called when the service is actually started. + \c func must call \c CArchMiscWindows::runDaemon() to finally + becoming a service. The \c runFunc function passed to \c runDaemon() + must call \c CArchMiscWindows::daemonRunning(true) when it + enters the main loop (i.e. after initialization) and + \c CArchMiscWindows::daemonRunning(false) when it leaves + the main loop. The \c stopFunc function passed to \c runDaemon() + is called when the daemon must exit the main loop and it must cause + \c runFunc to return. \c func should return what \c runDaemon() + returns. \c func or \c runFunc can call + \c CArchMiscWindows::daemonFailed() to indicate startup failure. +
+ */ + virtual int daemonize(const char* name, DaemonFunc func) = 0; + + //! Check if user has permission to install the daemon + /*! + Returns true iff the caller has permission to install or + uninstall the daemon. Note that even if this method returns + true it's possible that installing/uninstalling the service + may still fail. This method ignores whether or not the + service is already installed. + */ + virtual bool canInstallDaemon(const char* name, bool allUsers) = 0; + + //! Check if the daemon is installed + /*! + Returns true iff the daemon is installed. + */ + virtual bool isDaemonInstalled(const char* name, bool allUsers) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchFile.h b/lib/arch/IArchFile.h new file mode 100644 index 00000000..90b93649 --- /dev/null +++ b/lib/arch/IArchFile.h @@ -0,0 +1,59 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHFILE_H +#define IARCHFILE_H + +#include "IInterface.h" +#include "stdstring.h" + +class IArchFile : public IInterface { +public: + //! @name manipulators + //@{ + + //! Extract base name + /*! + Find the base name in the given \c pathname. + */ + virtual const char* getBasename(const char* pathname) = 0; + + //! Get user's home directory + /*! + Returns the user's home directory. Returns the empty string if + this cannot be determined. + */ + virtual std::string getUserDirectory() = 0; + + //! Get system directory + /*! + Returns the ussystem configuration file directory. + */ + virtual std::string getSystemDirectory() = 0; + + //! Concatenate path components + /*! + Concatenate pathname components with a directory separator + between them. This should not check if the resulting path + is longer than allowed by the system; we'll rely on the + system calls to tell us that. + */ + virtual std::string concatPath( + const std::string& prefix, + const std::string& suffix) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchLog.h b/lib/arch/IArchLog.h new file mode 100644 index 00000000..dc818ebb --- /dev/null +++ b/lib/arch/IArchLog.h @@ -0,0 +1,49 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHLOG_H +#define IARCHLOG_H + +#include "IInterface.h" + +class IArchLog : public IInterface { +public: + enum ELevel { kERROR, kWARNING, kNOTE, kINFO, kDEBUG }; + + //! @name manipulators + //@{ + + //! Open the log + /*! + Opens the log for writing. The log must be opened before being + written to. + */ + virtual void openLog(const char* name) = 0; + + //! Close the log + /*! + Close the log. + */ + virtual void closeLog() = 0; + + //! Write to the log + /*! + Writes the given string to the log with the given level. + */ + virtual void writeLog(ELevel, const char*) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h new file mode 100644 index 00000000..a3b2efd2 --- /dev/null +++ b/lib/arch/IArchMultithread.h @@ -0,0 +1,80 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHMULTITHREAD_H +#define IARCHMULTITHREAD_H + +#include "IInterface.h" + +class CArchCondImpl; +class CArchMutexImpl; +class CArchThreadImpl; +typedef CArchCondImpl* CArchCond; +typedef CArchMutexImpl* CArchMutex; +typedef CArchThreadImpl* CArchThread; + +//! Interface for architecture dependent multithreading +/*! +This interface defines the multithreading operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchMultithread : public IInterface { +public: + typedef void* (*ThreadFunc)(void*); + typedef unsigned int ThreadID; + + //! @name manipulators + //@{ + + // + // condition variable methods + // + + virtual CArchCond newCondVar() = 0; + virtual void closeCondVar(CArchCond) = 0; + virtual void signalCondVar(CArchCond) = 0; + virtual void broadcastCondVar(CArchCond) = 0; + virtual bool waitCondVar(CArchCond, CArchMutex, double timeout) = 0; + + // + // mutex methods + // + + virtual CArchMutex newMutex() = 0; + virtual void closeMutex(CArchMutex) = 0; + virtual void lockMutex(CArchMutex) = 0; + virtual void unlockMutex(CArchMutex) = 0; + + // + // thread methods + // + + virtual CArchThread newThread(ThreadFunc, void*) = 0; + virtual CArchThread newCurrentThread() = 0; + virtual CArchThread copyThread(CArchThread) = 0; + virtual void closeThread(CArchThread) = 0; + virtual void cancelThread(CArchThread) = 0; + virtual void setPriorityOfThread(CArchThread, int n) = 0; + virtual void testCancelThread() = 0; + virtual bool wait(CArchThread, double timeout) = 0; + virtual bool waitForEvent(double timeout) = 0; + virtual bool isSameThread(CArchThread, CArchThread) = 0; + virtual bool isExitedThread(CArchThread) = 0; + virtual void* getResultOfThread(CArchThread) = 0; + virtual ThreadID getIDOfThread(CArchThread) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h new file mode 100644 index 00000000..9bdacc71 --- /dev/null +++ b/lib/arch/IArchNetwork.h @@ -0,0 +1,105 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHNETWORK_H +#define IARCHNETWORK_H + +#include "IInterface.h" +#include "stdstring.h" + +class CArchSocketImpl; +class CArchNetAddressImpl; +typedef CArchSocketImpl* CArchSocket; +typedef CArchNetAddressImpl* CArchNetAddress; + +//! Interface for architecture dependent networking +/*! +This interface defines the networking operations required by +synergy. Each architecture must implement this interface. +*/ +class IArchNetwork : public IInterface { +public: + enum EAddressFamily { + kUNKNOWN, + kINET, + }; + + enum ESocketType { + kDGRAM, + kSTREAM + }; + + enum { + kPOLLIN = 1, + kPOLLOUT = 2, + kPOLLERR = 4, + kPOLLNVAL = 8 + }; + + class CPollEntry { + public: + CArchSocket m_socket; + unsigned short m_events; + unsigned short m_revents; + }; + + //! @name manipulators + //@{ + + virtual CArchSocket newSocket(EAddressFamily, ESocketType) = 0; + virtual CArchSocket copySocket(CArchSocket s) = 0; + virtual void closeSocket(CArchSocket s) = 0; + virtual void closeSocketForRead(CArchSocket s) = 0; + virtual void closeSocketForWrite(CArchSocket s) = 0; + virtual void bindSocket(CArchSocket s, CArchNetAddress addr) = 0; + virtual void listenOnSocket(CArchSocket s) = 0; + virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr) = 0; + virtual void connectSocket(CArchSocket s, CArchNetAddress name) = 0; + virtual int pollSocket(CPollEntry[], int num, double timeout) = 0; + virtual size_t readSocket(CArchSocket s, void* buf, size_t len) = 0; + virtual size_t writeSocket(CArchSocket s, + const void* buf, size_t len) = 0; + virtual void throwErrorOnSocket(CArchSocket) = 0; + + //! Set socket to (non-)blocking operation + /*! + Set socket to block or not block on accept, connect, poll, read and + write (i.e. calls that may take an arbitrary amount of time). + Returns the previous state. + */ + virtual bool setBlockingOnSocket(CArchSocket, bool blocking) = 0; + + //! Turn Nagle algorithm on or off on socket + /*! + Set socket to send messages immediately (true) or to collect small + messages into one packet (false). Returns the previous state. + */ + virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay) = 0; + + virtual std::string getHostName() = 0; + virtual CArchNetAddress newAnyAddr(EAddressFamily) = 0; + virtual CArchNetAddress copyAddr(CArchNetAddress) = 0; + virtual CArchNetAddress nameToAddr(const std::string&) = 0; + virtual void closeAddr(CArchNetAddress) = 0; + virtual std::string addrToName(CArchNetAddress) = 0; + virtual std::string addrToString(CArchNetAddress) = 0; + virtual EAddressFamily getAddrFamily(CArchNetAddress) = 0; + virtual void setAddrPort(CArchNetAddress, int port) = 0; + virtual int getAddrPort(CArchNetAddress) = 0; + virtual bool isAnyAddr(CArchNetAddress) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchSleep.h b/lib/arch/IArchSleep.h new file mode 100644 index 00000000..df6e88e8 --- /dev/null +++ b/lib/arch/IArchSleep.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHSLEEP_H +#define IARCHSLEEP_H + +#include "IInterface.h" + +class IArchSleep : public IInterface { +public: + //! @name manipulators + //@{ + + //! Sleep + /*! + Blocks the calling thread for \c timeout seconds. If + \c timeout < 0.0 then the call returns immediately. If \c timeout + == 0.0 then the calling thread yields the CPU. + + (cancellation point) + */ + virtual void sleep(double timeout) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchString.h b/lib/arch/IArchString.h new file mode 100644 index 00000000..d8117c30 --- /dev/null +++ b/lib/arch/IArchString.h @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHSTRING_H +#define IARCHSTRING_H + +#include "IInterface.h" +#include + +class CArchMBStateImpl; +typedef CArchMBStateImpl* CArchMBState; + +class IArchString : public IInterface { +public: + //! @name manipulators + //@{ + + //! printf() to limited size buffer with va_list + /*! + This method is equivalent to vsprintf() except it will not write + more than \c n bytes to the buffer, returning -1 if the output + was truncated and the number of bytes written not including the + trailing NUL otherwise. + */ + virtual int vsnprintf(char* str, + int size, const char* fmt, va_list ap) = 0; + + //! Create a new multibyte conversion state + virtual CArchMBState newMBState() = 0; + + //! Destroy a multibyte conversion state + virtual void closeMBState(CArchMBState) = 0; + + //! Initialize a multibyte conversion state + virtual void initMBState(CArchMBState) = 0; + + //! Test a multibyte conversion state + virtual bool isInitMBState(CArchMBState) = 0; + + //! Convert multibyte to wide character + virtual int convMBToWC(wchar_t*, + const char*, int, CArchMBState) = 0; + + //! Convert wide character to multibyte + virtual int convWCToMB(char*, wchar_t, CArchMBState) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchTime.h b/lib/arch/IArchTime.h new file mode 100644 index 00000000..b2b9a5cd --- /dev/null +++ b/lib/arch/IArchTime.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHTIME_H +#define IARCHTIME_H + +#include "IInterface.h" + +class IArchTime : public IInterface { +public: + //! @name manipulators + //@{ + + //! Get the current time + /*! + Returns the number of seconds since some arbitrary starting time. + This should return as high a precision as reasonable. + */ + virtual double time() = 0; + + //@} +}; + +#endif diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am new file mode 100644 index 00000000..e495adf8 --- /dev/null +++ b/lib/arch/Makefile.am @@ -0,0 +1,96 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + arch.dsp \ + CArchConsoleWindows.cpp \ + CArchDaemonWindows.cpp \ + CArchFileWindows.cpp \ + CArchLogWindows.cpp \ + CArchMiscWindows.cpp \ + CArchMultithreadWindows.cpp \ + CArchNetworkWinsock.cpp \ + CArchSleepWindows.cpp \ + CArchStringWindows.cpp \ + CArchTimeWindows.cpp \ + XArchWindows.cpp \ + CArchConsoleWindows.h \ + CArchDaemonWindows.h \ + CArchFileWindows.h \ + CArchLogWindows.h \ + CArchMiscWindows.h \ + CArchMultithreadWindows.h \ + CArchNetworkWinsock.h \ + CArchSleepWindows.h \ + CArchStringWindows.h \ + CArchTimeWindows.h \ + XArchWindows.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libarch.a +libarch_a_SOURCES = \ + CArch.cpp \ + CArchImpl.cpp \ + XArch.cpp \ + CArch.h \ + IArchConsole.h \ + IArchDaemon.h \ + IArchFile.h \ + IArchLog.h \ + IArchMultithread.h \ + IArchNetwork.h \ + IArchSleep.h \ + IArchString.h \ + IArchTime.h \ + XArch.h \ + XArchImpl.h \ + $(NULL) +EXTRA_libarch_a_SOURCES = \ + CArchConsoleUnix.cpp \ + CArchDaemonNone.cpp \ + CArchDaemonUnix.cpp \ + CArchFileUnix.cpp \ + CArchLogUnix.cpp \ + CArchMultithreadPosix.cpp \ + CArchNetworkBSD.cpp \ + CArchSleepUnix.cpp \ + CArchStringUnix.cpp \ + CArchTimeUnix.cpp \ + CMultibyte.cpp \ + CMultibyteOS.cpp \ + CMultibyteEmu.cpp \ + XArchUnix.cpp \ + vsnprintf.cpp \ + CArchConsoleUnix.h \ + CArchDaemonNone.h \ + CArchDaemonUnix.h \ + CArchFileUnix.h \ + CArchLogUnix.h \ + CArchMultithreadPosix.h \ + CArchNetworkBSD.h \ + CArchSleepUnix.h \ + CArchStringUnix.h \ + CArchTimeUnix.h \ + XArchUnix.h \ + $(NULL) +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + $(NULL) diff --git a/lib/arch/XArch.cpp b/lib/arch/XArch.cpp new file mode 100644 index 00000000..9dce5283 --- /dev/null +++ b/lib/arch/XArch.cpp @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArch.h" + +// +// XArch +// + +std::string +XArch::what() const throw() +{ + try { + if (m_what.empty() && m_eval != NULL) { + m_what = m_eval->eval(); + } + } + catch (...) { + // ignore + } + return m_what; +} diff --git a/lib/arch/XArch.h b/lib/arch/XArch.h new file mode 100644 index 00000000..131e07ed --- /dev/null +++ b/lib/arch/XArch.h @@ -0,0 +1,165 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCH_H +#define XARCH_H + +#include "stdstring.h" + +//! Generic thread exception +/*! +Exceptions derived from this class are used by the multithreading +library to perform stack unwinding when a thread terminates. These +exceptions must always be rethrown by clients when caught. +*/ +class XThread { }; + +//! Thread exception to cancel +/*! +Thrown to cancel a thread. Clients must not throw this type, but +must rethrow it if caught (by XThreadCancel, XThread, or ...). +*/ +class XThreadCancel : public XThread { }; + +/*! +\def RETHROW_XTHREAD +Convenience macro to rethrow an XThread exception but ignore other +exceptions. Put this in your catch (...) handler after necessary +cleanup but before leaving or returning from the handler. +*/ +#define RETHROW_XTHREAD \ + try { throw; } catch (XThread&) { throw; } catch (...) { } + +//! Lazy error message string evaluation +/*! +This class encapsulates platform dependent error string lookup. +Platforms subclass this type, taking an appropriate error code +type in the c'tor and overriding eval() to return the error +string for that error code. +*/ +class XArchEval { +public: + XArchEval() { } + virtual ~XArchEval() { } + + virtual XArchEval* clone() const throw() = 0; + + virtual std::string eval() const throw() = 0; +}; + +//! Generic exception architecture dependent library +class XArch { +public: + XArch(XArchEval* adoptedEvaluator) : m_eval(adoptedEvaluator) { } + XArch(const std::string& msg) : m_eval(NULL), m_what(msg) { } + XArch(const XArch& e) : m_eval(e.m_eval->clone()), m_what(e.m_what) { } + ~XArch() { delete m_eval; } + + std::string what() const throw(); + +private: + XArchEval* m_eval; + mutable std::string m_what; +}; + +// Macro to declare XArch derived types +#define XARCH_SUBCLASS(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_(XArchEval* adoptedEvaluator) : super_(adoptedEvaluator) { } \ + name_(const std::string& msg) : super_(msg) { } \ +} + +//! Generic network exception +/*! +Exceptions derived from this class are used by the networking +library to indicate various errors. +*/ +XARCH_SUBCLASS(XArchNetwork, XArch); + +//! Network insufficient permission +XARCH_SUBCLASS(XArchNetworkWouldBlock, XArchNetwork); + +//! Network insufficient permission +XARCH_SUBCLASS(XArchNetworkAccess, XArchNetwork); + +//! Network insufficient resources +XARCH_SUBCLASS(XArchNetworkResource, XArchNetwork); + +//! No support for requested network resource/service +XARCH_SUBCLASS(XArchNetworkSupport, XArchNetwork); + +//! Network I/O error +XARCH_SUBCLASS(XArchNetworkIO, XArchNetwork); + +//! Network address is unavailable or not local +XARCH_SUBCLASS(XArchNetworkNoAddress, XArchNetwork); + +//! Network address in use +XARCH_SUBCLASS(XArchNetworkAddressInUse, XArchNetwork); + +//! No route to address +XARCH_SUBCLASS(XArchNetworkNoRoute, XArchNetwork); + +//! Socket not connected +XARCH_SUBCLASS(XArchNetworkNotConnected, XArchNetwork); + +//! Remote end of socket has disconnected +XARCH_SUBCLASS(XArchNetworkDisconnected, XArchNetwork); + +//! Remote end of socket refused connection +XARCH_SUBCLASS(XArchNetworkConnectionRefused, XArchNetwork); + +//! Connection is in progress +XARCH_SUBCLASS(XArchNetworkConnecting, XArchNetwork); + +//! Remote end of socket is not responding +XARCH_SUBCLASS(XArchNetworkTimedOut, XArchNetwork); + +//! Generic network name lookup erros +XARCH_SUBCLASS(XArchNetworkName, XArchNetwork); + +//! The named host is unknown +XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName); + +//! The named host is known but has to address +XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName); + +//! Non-recoverable name server error +XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName); + +//! Temporary name server error +XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName); + +//! Generic daemon exception +/*! +Exceptions derived from this class are used by the daemon +library to indicate various errors. +*/ +XARCH_SUBCLASS(XArchDaemon, XArch); + +//! Could not daemonize +XARCH_SUBCLASS(XArchDaemonFailed, XArchDaemon); + +//! Could not install daemon +XARCH_SUBCLASS(XArchDaemonInstallFailed, XArchDaemon); + +//! Could not uninstall daemon +XARCH_SUBCLASS(XArchDaemonUninstallFailed, XArchDaemon); + +//! Attempted to uninstall a daemon that was not installed +XARCH_SUBCLASS(XArchDaemonUninstallNotInstalled, XArchDaemonUninstallFailed); + + +#endif diff --git a/lib/platform/CPlatform.cpp b/lib/arch/XArchImpl.h similarity index 79% rename from lib/platform/CPlatform.cpp rename to lib/arch/XArchImpl.h index 091cd650..28c79721 100644 --- a/lib/platform/CPlatform.cpp +++ b/lib/arch/XArchImpl.h @@ -12,18 +12,14 @@ * GNU General Public License for more details. */ -#include "common.h" +#ifndef XARCHIMPL_H +#define XARCHIMPL_H +// include appropriate architecture implementation #if WINDOWS_LIKE - -#include "CWin32Platform.cpp" - +# include "XArchWindows.h" #elif UNIX_LIKE - -#include "CUnixPlatform.cpp" - -#else - -#error Unsupported platform +# include "XArchUnix.h" +#endif #endif diff --git a/lib/arch/XArchUnix.cpp b/lib/arch/XArchUnix.cpp new file mode 100644 index 00000000..6f4047d5 --- /dev/null +++ b/lib/arch/XArchUnix.cpp @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArchUnix.h" +#include + +// +// XArchEvalUnix +// + +XArchEval* +XArchEvalUnix::clone() const throw() +{ + return new XArchEvalUnix(m_errno); +} + +std::string +XArchEvalUnix::eval() const throw() +{ + // FIXME -- not thread safe + return strerror(m_errno); +} diff --git a/lib/arch/XArchUnix.h b/lib/arch/XArchUnix.h new file mode 100644 index 00000000..19e0df4c --- /dev/null +++ b/lib/arch/XArchUnix.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCHUNIX_H +#define XARCHUNIX_H + +#include "XArch.h" + +//! Lazy error message string evaluation for unix +class XArchEvalUnix : public XArchEval { +public: + XArchEvalUnix(int err) : m_errno(err) { } + virtual ~XArchEvalUnix() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + int m_errno; +}; + +#endif diff --git a/lib/arch/XArchWindows.cpp b/lib/arch/XArchWindows.cpp new file mode 100644 index 00000000..a57bc71c --- /dev/null +++ b/lib/arch/XArchWindows.cpp @@ -0,0 +1,126 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "XArchWindows.h" + +// +// XArchEvalWindows +// + +XArchEval* +XArchEvalWindows::clone() const throw() +{ + return new XArchEvalWindows(m_errno); +} + +std::string +XArchEvalWindows::eval() const throw() +{ + char* cmsg; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + m_errno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&cmsg, + 0, + NULL) == 0) { + cmsg = NULL; + return "Unknown error"; + } + std::string smsg(cmsg); + LocalFree(cmsg); + return smsg; +} + + +// +// XArchEvalWinsock +// + +XArchEval* +XArchEvalWinsock::clone() const throw() +{ + return new XArchEvalWinsock(m_errno); +} + +std::string +XArchEvalWinsock::eval() const throw() +{ + // built-in windows function for looking up error message strings + // may not look up network error messages correctly. we'll have + // to do it ourself. + static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = { + /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, + /* 10009 */{WSAEBADF, "Bad file handle"}, + /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, + /* 10014 */{WSAEFAULT, "WSAEFAULT"}, + /* 10022 */{WSAEINVAL, "WSAEINVAL"}, + /* 10024 */{WSAEMFILE, "No more file descriptors available"}, + /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, + /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, + /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"}, + /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"}, + /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"}, + /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"}, + /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, + /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, + /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"}, + /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"}, + /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, + /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"}, + /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"}, + /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, + /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, + /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"}, + /* 10051 */{WSAENETUNREACH, "The network can't be reached from this hos at this time"}, + /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, + /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"}, + /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, + /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"}, + /* 10056 */{WSAEISCONN, "The socket is already connected"}, + /* 10057 */{WSAENOTCONN, "The socket is not connected"}, + /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, + /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"}, + /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, + /* 10061 */{WSAECONNREFUSED, "The attempt to connect was forcefully rejected"}, + /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, + /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"}, + /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, + /* 10065 */{WSAEHOSTUNREACH, "No route to host"}, + /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, + /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"}, + /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, + /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, + /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, + /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, + /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"}, + /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, + /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, + /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, + /* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"}, + /* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"}, + /* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"}, + /* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"}, + /* end */{0, NULL} + }; + + for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) { + if (s_netErrorCodes[i].m_code == m_errno) { + return s_netErrorCodes[i].m_msg; + } + } + return "Unknown error"; +} diff --git a/lib/arch/XArchWindows.h b/lib/arch/XArchWindows.h new file mode 100644 index 00000000..56c24007 --- /dev/null +++ b/lib/arch/XArchWindows.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef XARCHWINDOWS_H +#define XARCHWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "XArch.h" +#include + +//! Lazy error message string evaluation for windows +class XArchEvalWindows : public XArchEval { +public: + XArchEvalWindows() : m_errno(GetLastError()) { } + XArchEvalWindows(DWORD err) : m_errno(err) { } + virtual ~XArchEvalWindows() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + DWORD m_errno; +}; + +//! Lazy error message string evaluation for winsock +class XArchEvalWinsock : public XArchEval { +public: + XArchEvalWinsock(int err) : m_errno(err) { } + virtual ~XArchEvalWinsock() { } + + // XArchEval overrides + virtual XArchEval* clone() const throw(); + virtual std::string eval() const throw(); + +private: + int m_errno; +}; + +#endif diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp new file mode 100644 index 00000000..1af4c4b7 --- /dev/null +++ b/lib/arch/arch.dsp @@ -0,0 +1,269 @@ +# Microsoft Developer Studio Project File - Name="arch" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=arch - Win32 Debug +!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 "arch.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 "arch.mak" CFG="arch - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "arch - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "arch - Win32 Debug" (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)" == "arch - 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" +# 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 /W4 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "arch - 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" +# 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "arch - Win32 Release" +# Name "arch - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\CArch.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchImpl.cpp +# End Source File +# Begin Source File + +SOURCE=.\XArch.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\CArch.h +# End Source File +# Begin Source File + +SOURCE=.\CArchConsoleWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchDaemonWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchImpl.h +# End Source File +# Begin Source File + +SOURCE=.\CArchLogWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchMiscWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchMultithreadWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchNetworkWinsock.h +# End Source File +# Begin Source File + +SOURCE=.\CArchSleepWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchStringWindows.h +# End Source File +# Begin Source File + +SOURCE=.\CArchTimeWindows.h +# End Source File +# Begin Source File + +SOURCE=.\IArchConsole.h +# End Source File +# Begin Source File + +SOURCE=.\IArchLog.h +# End Source File +# Begin Source File + +SOURCE=.\IArchMultithread.h +# End Source File +# Begin Source File + +SOURCE=.\IArchNetwork.h +# End Source File +# Begin Source File + +SOURCE=.\IArchSleep.h +# End Source File +# Begin Source File + +SOURCE=.\IArchString.h +# End Source File +# Begin Source File + +SOURCE=.\IArchTime.h +# End Source File +# Begin Source File + +SOURCE=.\XArch.h +# End Source File +# Begin Source File + +SOURCE=.\XArchImpl.h +# End Source File +# Begin Source File + +SOURCE=.\XArchWindows.h +# End Source File +# End Group +# Begin Group "Included Files" + +# PROP Default_Filter "inc" +# Begin Source File + +SOURCE=.\CArchConsoleWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchDaemonWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchFileWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchLogWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchMiscWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchMultithreadWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchNetworkWinsock.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchSleepWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchStringWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CArchTimeWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyte.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyteEmu.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyteOS.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\vsnprintf.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\XArchWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/arch/vsnprintf.cpp b/lib/arch/vsnprintf.cpp new file mode 100644 index 00000000..4bd665d2 --- /dev/null +++ b/lib/arch/vsnprintf.cpp @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if HAVE_VSNPRINTF + +#if !defined(ARCH_VSNPRINTF) +# define ARCH_VSNPRINTF vsnprintf +#endif + +int +ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + int n = ::ARCH_VSNPRINTF(str, size, fmt, ap); + if (n > size) { + n = -1; + } + return n; +} + +#else // !HAVE_VSNPRINTF + +// FIXME +#error vsnprintf not implemented + +#endif // !HAVE_VSNPRINTF diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index 0cc48f08..84fe9994 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -14,15 +14,13 @@ #include "CLog.h" #include "CString.h" +#include "CStringUtil.h" +#include "LogOutputters.h" +#include "CArch.h" +#include "Version.h" #include -#include #include -#if WINDOWS_LIKE -#define WIN32_LEAN_AND_MEAN -#include -#endif - // names of priorities static const char* g_priority[] = { "FATAL", @@ -56,26 +54,64 @@ static const int g_prioritySuffixLength = 2; static const int g_priorityPad = g_maxPriorityLength + g_prioritySuffixLength; -// platform newline sequence -#if WINDOWS_LIKE -static const char* g_newline = "\r\n"; -#else -static const char* g_newline = "\n"; -#endif +// +// CLogLock +// +// Convenience object to lock/unlock a mutex. +// + +class CLogLock { +public: + CLogLock(CArchMutex mutex) : m_mutex(mutex) { ARCH->lockMutex(m_mutex); } + ~CLogLock() { ARCH->unlockMutex(m_mutex); } + +private: + CArchMutex m_mutex; +}; -// minimum length of a newline sequence -static const int g_newlineLength = 2; // // CLog // -CLog::Outputter CLog::s_outputter = NULL; -CLog::Lock CLog::s_lock = &CLog::dummyLock; -int CLog::s_maxPriority = -1; +CLog* CLog::s_log = NULL; + +CLog::CLog() +{ + assert(s_log == NULL); + + // create mutex for multithread safe operation + m_mutex = ARCH->newMutex(); + + // other initalization + m_maxPriority = g_defaultMaxPriority; + m_maxNewlineLength = 0; + insert(new CConsoleLogOutputter); +} + +CLog::~CLog() +{ + // clean up + for (COutputterList::iterator index = m_outputters.begin(); + index != m_outputters.end(); ++index) { + delete *index; + } + ARCH->closeMutex(m_mutex); + s_log = NULL; +} + +CLog* +CLog::getInstance() +{ + // note -- not thread safe; client must initialize log safely + if (s_log == NULL) { + s_log = new CLog; + } + return s_log; +} void -CLog::print(const char* fmt, ...) +CLog::print(const char* fmt, ...) const { // check if fmt begins with a priority argument int priority = 4; @@ -85,7 +121,7 @@ CLog::print(const char* fmt, ...) } // done if below priority threshold - if (priority > getMaxPriority()) { + if (priority > getFilter()) { return; } @@ -98,7 +134,7 @@ CLog::print(const char* fmt, ...) va_start(args, fmt); char* buffer = CStringUtil::vsprint(stack, sizeof(stack) / sizeof(stack[0]), - pad, g_newlineLength, fmt, args); + pad, m_maxNewlineLength, fmt, args); va_end(args); // output buffer @@ -110,7 +146,7 @@ CLog::print(const char* fmt, ...) } void -CLog::printt(const char* file, int line, const char* fmt, ...) +CLog::printt(const char* file, int line, const char* fmt, ...) const { // check if fmt begins with a priority argument int priority = 4; @@ -120,7 +156,7 @@ CLog::printt(const char* file, int line, const char* fmt, ...) } // done if below priority threshold - if (priority > getMaxPriority()) { + if (priority > getFilter()) { return; } @@ -136,7 +172,7 @@ CLog::printt(const char* file, int line, const char* fmt, ...) va_start(args, fmt); char* buffer = CStringUtil::vsprint(stack, sizeof(stack) / sizeof(stack[0]), - pad, g_newlineLength, fmt, args); + pad, m_maxNewlineLength, fmt, args); va_end(args); // print the prefix to the buffer. leave space for priority label. @@ -158,31 +194,34 @@ CLog::printt(const char* file, int line, const char* fmt, ...) } void -CLog::setOutputter(Outputter outputter) +CLog::insert(ILogOutputter* outputter) { - CHoldLock lock(s_lock); - s_outputter = outputter; -} + assert(outputter != NULL); + assert(outputter->getNewline() != NULL); -CLog::Outputter -CLog::getOutputter() -{ - CHoldLock lock(s_lock); - return s_outputter; + CLogLock lock(m_mutex); + m_outputters.push_front(outputter); + int newlineLength = strlen(outputter->getNewline()); + if (newlineLength > m_maxNewlineLength) { + m_maxNewlineLength = newlineLength; + } } void -CLog::setLock(Lock newLock) +CLog::remove(ILogOutputter* outputter) { - CHoldLock lock(s_lock); - s_lock = (newLock == NULL) ? dummyLock : newLock; + CLogLock lock(m_mutex); + m_outputters.remove(outputter); } -CLog::Lock -CLog::getLock() +void +CLog::pop_front() { - CHoldLock lock(s_lock); - return (s_lock == dummyLock) ? NULL : s_lock; + CLogLock lock(m_mutex); + if (!m_outputters.empty()) { + delete m_outputters.front(); + m_outputters.pop_front(); + } } bool @@ -203,38 +242,19 @@ CLog::setFilter(const char* maxPriority) void CLog::setFilter(int maxPriority) { - CHoldLock lock(s_lock); - s_maxPriority = maxPriority; + CLogLock lock(m_mutex); + m_maxPriority = maxPriority; } int -CLog::getFilter() +CLog::getFilter() const { - CHoldLock lock(s_lock); - return getMaxPriority(); + CLogLock lock(m_mutex); + return m_maxPriority; } void -CLog::dummyLock(bool) -{ - // do nothing -} - -int -CLog::getMaxPriority() -{ - CHoldLock lock(s_lock); - - if (s_maxPriority == -1) { - s_maxPriority = g_defaultMaxPriority; - setFilter(getenv("SYN_LOG_PRI")); - } - - return s_maxPriority; -} - -void -CLog::output(int priority, char* msg) +CLog::output(int priority, char* msg) const { assert(priority >= -1 && priority < g_numPriority); assert(msg != NULL); @@ -249,79 +269,23 @@ CLog::output(int priority, char* msg) msg[g_maxPriorityLength + 1] = ' '; } - // put a newline at the end - strcat(msg + g_priorityPad, g_newline); + // write to each outputter + CLogLock lock(m_mutex); + for (COutputterList::const_iterator index = m_outputters.begin(); + index != m_outputters.end(); ++index) { + // get outputter + ILogOutputter* outputter = *index; + + // put an appropriate newline at the end + strcat(msg + g_priorityPad, outputter->getNewline()); - // print it - CHoldLock lock(s_lock); - if (s_outputter == NULL || - !s_outputter(priority, msg + g_maxPriorityLength - n)) { -#if WINDOWS_LIKE - openConsole(); -#endif - fprintf(stderr, "%s", msg + g_maxPriorityLength - n); + // open the outputter + outputter->open(kApplication); + + // write message and break out of loop if it returns false + if (!outputter->write(static_cast(priority), + msg + g_maxPriorityLength - n)) { + break; + } } } - -#if WINDOWS_LIKE - -static DWORD s_thread = 0; - -static -BOOL WINAPI -CLogSignalHandler(DWORD) -{ - // terminate cleanly and skip remaining handlers - PostThreadMessage(s_thread, WM_QUIT, 0, 0); - return TRUE; -} - -void -CLog::openConsole() -{ - static bool s_hasConsole = false; - - // ignore if already created - if (s_hasConsole) - return; - - // remember the current thread. when we get a ctrl+break or the - // console is closed we'll post WM_QUIT to this thread to shutdown - // cleanly. - // note -- win95/98/me are broken and will not receive a signal - // when the console is closed nor during logoff or shutdown, - // see microsoft articles Q130717 and Q134284. we could work - // around this in a painful way using hooks and hidden windows - // (as apache does) but it's not worth it. the app will still - // quit, just not cleanly. users in-the-know can use ctrl+c. - s_thread = GetCurrentThreadId(); - - // open a console - if (!AllocConsole()) - return; - - // get the handle for error output - HANDLE herr = GetStdHandle(STD_ERROR_HANDLE); - - // prep console. windows 95 and its ilk have braindead - // consoles that can't even resize independently of the - // buffer size. use a 25 line buffer for those systems. - OSVERSIONINFO osInfo; - COORD size = { 80, 1000 }; - osInfo.dwOSVersionInfoSize = sizeof(osInfo); - if (GetVersionEx(&osInfo) && - osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) - size.Y = 25; - SetConsoleScreenBufferSize(herr, size); - SetConsoleTextAttribute(herr, - FOREGROUND_RED | - FOREGROUND_GREEN | - FOREGROUND_BLUE); - SetConsoleCtrlHandler(CLogSignalHandler, TRUE); - - // reopen stderr to point at console - freopen("con", "w", stderr); - s_hasConsole = true; -} - -#endif diff --git a/lib/base/CLog.h b/lib/base/CLog.h index 302e748a..d76f9140 100644 --- a/lib/base/CLog.h +++ b/lib/base/CLog.h @@ -16,8 +16,15 @@ #define CLOG_H #include "common.h" +#include "IArchMultithread.h" +#include "IInterface.h" +#include "stdlist.h" #include +#define CLOG (CLog::getInstance()) + +class ILogOutputter; + //! Logging facility /*! The logging class; all console output should go through this class. @@ -42,56 +49,51 @@ public: kDEBUG2 //!< For even more detailed debugging messages }; - //! Outputter function. - /*! - Type of outputter function. The outputter should write \c message, - which has the given \c priority, to a log and return true. Or it can - return false to let CLog use the default outputter. - */ - typedef bool (*Outputter)(int priority, const char* message); - - //! Locking function - /*! - Type of lock/unlock function. If \c lock is true then block other - threads that try to lock until this thread unlocks. If \c lock is - false then unlock and allow another (waiting) thread to lock. - */ - typedef void (*Lock)(bool lock); + ~CLog(); //! @name manipulators //@{ - //! Set the function used to write the log + //! Add an outputter to the head of the list /*! - Sets the function used to write to the log. The outputter function - is called with the formatted string to write and the priority level. - CLog will have already filtered messages below the current filter - priority. A NULL outputter means to use the default which is to print - to stderr. Note that the outputter should not call CLog methods but, - if it does, the current lock function must permit recursive locks. - */ - static void setOutputter(Outputter); + Inserts an outputter to the head of the outputter list. When the + logger writes a message, it goes to the outputter at the head of + the outputter list. If that outputter's \c write() method returns + true then it also goes to the next outputter, as so on until an + outputter returns false or there are no more outputters. Outputters + still in the outputter list when the log is destroyed will be + deleted. - //! Set the lock/unlock function - /*! - Set the lock/unlock function. Use setLock(NULL) to remove the - locking function. There is no default lock function; do not call - CLog from multiple threads unless a working lock function has been - installed. + By default, the logger has one outputter installed which writes to + the console. */ - static void setLock(Lock); + void insert(ILogOutputter* adopted); + + //! Remove an outputter from the list + /*! + Removes the first occurrence of the given outputter from the + outputter list. It does nothing if the outputter is not in the + list. The outputter is not deleted. + */ + void remove(ILogOutputter* orphaned); + + //! Remove the outputter from the head of the list + /*! + Removes and deletes the outputter at the head of the outputter list. + This does nothing if the outputter list is empty. + */ + void pop_front(); //! Set the minimum priority filter. /*! Set the filter. Messages below this priority are discarded. The default priority is 4 (INFO) (unless built without NDEBUG - in which case it's 5 (DEBUG)). The default can be overridden - by setting the SYN_LOG_PRI env var to "FATAL", "ERROR", etc. - setFilter(const char*) returns true if the priority \c name was - recognized; if \c name is NULL then it simply returns true. + in which case it's 5 (DEBUG)). setFilter(const char*) returns + true if the priority \c name was recognized; if \c name is NULL + then it simply returns true. */ - static bool setFilter(const char* name); - static void setFilter(int); + bool setFilter(const char* name); + void setFilter(int); //@} //! @name accessors @@ -101,52 +103,38 @@ public: /*! Print a log message using the printf-like \c format and arguments. */ - static void print(const char* format, ...); + void print(const char* format, ...) const; //! Print a log message /*! Print a log message using the printf-like \c format and arguments preceded by the filename and line number. */ - static void printt(const char* file, int line, - const char* format, ...); - - //! Get the function used to write the log - static Outputter getOutputter(); - - //! Get the lock/unlock function - /*! - Get the lock/unlock function. Note that the lock function is - used when retrieving the lock function. - */ - static Lock getLock(); + void printt(const char* file, int line, + const char* format, ...) const; //! Get the minimum priority level. - static int getFilter(); + int getFilter() const; + + //! Get the singleton instance of the log + static CLog* getInstance(); //@} private: - class CHoldLock { - public: - CHoldLock(Lock lock) : m_lock(lock) { m_lock(true); } - ~CHoldLock() { m_lock(false); } + CLog(); - private: - Lock m_lock; - }; - - static void dummyLock(bool); - static int getMaxPriority(); - static void output(int priority, char* msg); -#if WINDOWS_LIKE - static void openConsole(); -#endif + void output(int priority, char* msg) const; private: - static Outputter s_outputter; - static Lock s_lock; - static int s_maxPriority; + typedef std::list COutputterList; + + static CLog* s_log; + + CArchMutex m_mutex; + COutputterList m_outputters; + int m_maxNewlineLength; + int m_maxPriority; }; /*! @@ -193,12 +181,12 @@ which includes the filename and line number. #define LOGC(_a1, _a2) #define CLOG_TRACE #elif defined(NDEBUG) -#define LOG(_a1) CLog::print _a1 -#define LOGC(_a1, _a2) if (_a1) CLog::print _a2 +#define LOG(_a1) CLOG->print _a1 +#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2 #define CLOG_TRACE #else -#define LOG(_a1) CLog::printt _a1 -#define LOGC(_a1, _a2) if (_a1) CLog::printt _a2 +#define LOG(_a1) CLOG->printt _a1 +#define LOGC(_a1, _a2) if (_a1) CLOG->printt _a2 #define CLOG_TRACE __FILE__, __LINE__, #endif diff --git a/lib/base/CStopwatch.cpp b/lib/base/CStopwatch.cpp index 02b99d77..89edce2b 100644 --- a/lib/base/CStopwatch.cpp +++ b/lib/base/CStopwatch.cpp @@ -13,6 +13,7 @@ */ #include "CStopwatch.h" +#include "CArch.h" // // CStopwatch @@ -24,7 +25,7 @@ CStopwatch::CStopwatch(bool triggered) : m_stopped(triggered) { if (!triggered) { - m_mark = getClock(); + m_mark = ARCH->time(); } } @@ -42,7 +43,7 @@ CStopwatch::reset() return dt; } else { - const double t = getClock(); + const double t = ARCH->time(); const double dt = t - m_mark; m_mark = t; return dt; @@ -57,7 +58,7 @@ CStopwatch::stop() } // save the elapsed time - m_mark = getClock() - m_mark; + m_mark = ARCH->time() - m_mark; m_stopped = true; } @@ -70,7 +71,7 @@ CStopwatch::start() } // set the mark such that it reports the time elapsed at stop() - m_mark = getClock() - m_mark; + m_mark = ARCH->time() - m_mark; m_stopped = false; } @@ -93,7 +94,7 @@ CStopwatch::getTime() return m_mark; } else { - return getClock() - m_mark; + return ARCH->time() - m_mark; } } @@ -115,7 +116,7 @@ CStopwatch::getTime() const return m_mark; } else { - return getClock() - m_mark; + return ARCH->time() - m_mark; } } @@ -123,100 +124,3 @@ CStopwatch::operator double() const { return getTime(); } - -#if WINDOWS_LIKE - -// avoid getting a lot a crap from mmsystem.h that we don't need -#define MMNODRV // Installable driver support -#define MMNOSOUND // Sound support -#define MMNOWAVE // Waveform support -#define MMNOMIDI // MIDI support -#define MMNOAUX // Auxiliary audio support -#define MMNOMIXER // Mixer support -#define MMNOJOY // Joystick support -#define MMNOMCI // MCI support -#define MMNOMMIO // Multimedia file I/O support -#define MMNOMMSYSTEM // General MMSYSTEM functions - -#define WIN32_LEAN_AND_MEAN -#include -#include - -typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void); - -static double s_freq = 0.0; -static HINSTANCE s_mmInstance = NULL; -static PTimeGetTime s_tgt = NULL; - -// -// initialize local variables -// - -class CStopwatchInit { -public: - CStopwatchInit(); - ~CStopwatchInit(); -}; -static CStopwatchInit s_init; - -CStopwatchInit::CStopwatchInit() -{ - LARGE_INTEGER freq; - if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) { - s_freq = 1.0 / static_cast(freq.QuadPart); - } - else { - // load winmm.dll and get timeGetTime - s_mmInstance = LoadLibrary("winmm"); - if (s_mmInstance) { - s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime"); - } - } -} - -CStopwatchInit::~CStopwatchInit() -{ - if (s_mmInstance) { - FreeLibrary(reinterpret_cast(s_mmInstance)); - } -} - -double -CStopwatch::getClock() const -{ - // get time. we try three ways, in order of descending precision - if (s_freq != 0.0) { - LARGE_INTEGER c; - QueryPerformanceCounter(&c); - return s_freq * static_cast(c.QuadPart); - } - else if (s_tgt) { - return 0.001 * static_cast(s_tgt()); - } - else { - return 0.001 * static_cast(GetTickCount()); - } -} - -#elif UNIX_LIKE - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -double -CStopwatch::getClock() const -{ - struct timeval t; - gettimeofday(&t, NULL); - return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; -} - -#endif // UNIX_LIKE diff --git a/lib/base/CString.h b/lib/base/CString.h index 7add626f..e3eaa3b0 100644 --- a/lib/base/CString.h +++ b/lib/base/CString.h @@ -15,90 +15,10 @@ #ifndef CSTRING_H #define CSTRING_H -#include -#include "stdpre.h" -#include -#include "stdpost.h" +#include "stdstring.h" // use standard C++ string class for our string class typedef std::string CString; -//! String utilities -/*! -This class provides various functions for string manipulation. -*/ -class CStringUtil { -public: - //! Format positional arguments - /*! - Format a string using positional arguments. fmt has literal - characters and conversion specifications introduced by `\%': - - \c\%\% -- literal `\%' - - \c\%{n} -- positional element n, n a positive integer, {} are literal - - All arguments in the variable list are const char*. Positional - elements are indexed from 1. - */ - static CString format(const char* fmt, ...); - - //! Format positional arguments - /*! - Same as format() except takes va_list. - */ - static CString vformat(const char* fmt, va_list); - - //! Print a string using printf-style formatting - /*! - Equivalent to printf() except the result is returned as a CString. - */ - static CString print(const char* fmt, ...); - - //! Print a string using printf-style formatting - /*! - Same as print() except takes va_list. - */ - static CString vprint(const char* fmt, va_list); - - //! Print a string using printf-style formatting into a buffer - /*! - This is like print but print into a given buffer. If the resulting - string will not fit into \c buffer then a new buffer is allocated and - returned, otherwise \c buffer is returned. the caller must delete[] - the returned memory if is not \c buffer. - - \c prefix and \c suffix must be >= 0. Exactly \c prefix characters and - at least \c suffix characters are available in the buffer before - and after the printed string, respectively. \c bufferLength is the - length of buffer and should not be adjusted by the caller to - account for \c prefix or \c suffix. - */ - static char* vsprint(char* buffer, int bufferLength, - int prefix, int suffix, const char* fmt, va_list); - - //! Case-insensitive comparisons - /*! - This class provides case-insensitve comparison functions. - */ - class CaselessCmp { - public: - //! Same as less() - bool operator()(const CString& a, const CString& b) const; - - //! Returns true iff \c a is lexicographically less than \c b - static bool less(const CString& a, const CString& b); - - //! Returns true iff \c a is lexicographically equal to \c b - static bool equal(const CString& a, const CString& b); - - //! Returns true iff \c a is lexicographically less than \c b - static bool cmpLess(const CString::value_type& a, - const CString::value_type& b); - - //! Returns true iff \c a is lexicographically equal to \c b - static bool cmpEqual(const CString::value_type& a, - const CString::value_type& b); - }; -}; - #endif diff --git a/lib/base/CString.cpp b/lib/base/CStringUtil.cpp similarity index 94% rename from lib/base/CString.cpp rename to lib/base/CStringUtil.cpp index 13e78239..2e61c047 100644 --- a/lib/base/CString.cpp +++ b/lib/base/CStringUtil.cpp @@ -12,7 +12,8 @@ * GNU General Public License for more details. */ -#include "CString.h" +#include "CStringUtil.h" +#include "CArch.h" #include "common.h" #include "stdvector.h" #include @@ -20,12 +21,6 @@ #include #include -#if WINDOWS_LIKE -#define WIN32_LEAN_AND_MEAN -#include -#define vsnprintf _vsnprintf -#endif - // // CStringUtil // @@ -152,7 +147,8 @@ CStringUtil::vsprint(char* buffer, int len, // try writing to input buffer int n; if (buffer != NULL && len >= prefix + suffix) { - n = vsnprintf(buffer + prefix, len - (prefix + suffix), fmt, args); + n = ARCH->vsnprintf(buffer + prefix, + len - (prefix + suffix), fmt, args); if (n >= 0 && n <= len - (prefix + suffix)) return buffer; } @@ -163,7 +159,8 @@ CStringUtil::vsprint(char* buffer, int len, delete[] buffer; len *= 2; buffer = new char[len + (prefix + suffix)]; - n = vsnprintf(buffer + prefix, len - (prefix + suffix), fmt, args); + n = ARCH->vsnprintf(buffer + prefix, + len - (prefix + suffix), fmt, args); } while (n < 0 || n > len - (prefix + suffix)); return buffer; diff --git a/lib/base/CStringUtil.h b/lib/base/CStringUtil.h new file mode 100644 index 00000000..1ad5824c --- /dev/null +++ b/lib/base/CStringUtil.h @@ -0,0 +1,99 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTRINGUTIL_H +#define CSTRINGUTIL_H + +#include "CString.h" +#include + +//! String utilities +/*! +This class provides various functions for string manipulation. +*/ +class CStringUtil { +public: + //! Format positional arguments + /*! + Format a string using positional arguments. fmt has literal + characters and conversion specifications introduced by `\%': + - \c\%\% -- literal `\%' + - \c\%{n} -- positional element n, n a positive integer, {} are literal + + All arguments in the variable list are const char*. Positional + elements are indexed from 1. + */ + static CString format(const char* fmt, ...); + + //! Format positional arguments + /*! + Same as format() except takes va_list. + */ + static CString vformat(const char* fmt, va_list); + + //! Print a string using printf-style formatting + /*! + Equivalent to printf() except the result is returned as a CString. + */ + static CString print(const char* fmt, ...); + + //! Print a string using printf-style formatting + /*! + Same as print() except takes va_list. + */ + static CString vprint(const char* fmt, va_list); + + //! Print a string using printf-style formatting into a buffer + /*! + This is like print but print into a given buffer. If the resulting + string will not fit into \c buffer then a new buffer is allocated and + returned, otherwise \c buffer is returned. the caller must delete[] + the returned memory if is not \c buffer. + + \c prefix and \c suffix must be >= 0. Exactly \c prefix characters and + at least \c suffix characters are available in the buffer before + and after the printed string, respectively. \c bufferLength is the + length of buffer and should not be adjusted by the caller to + account for \c prefix or \c suffix. + */ + static char* vsprint(char* buffer, int bufferLength, + int prefix, int suffix, const char* fmt, va_list); + + //! Case-insensitive comparisons + /*! + This class provides case-insensitve comparison functions. + */ + class CaselessCmp { + public: + //! Same as less() + bool operator()(const CString& a, const CString& b) const; + + //! Returns true iff \c a is lexicographically less than \c b + static bool less(const CString& a, const CString& b); + + //! Returns true iff \c a is lexicographically equal to \c b + static bool equal(const CString& a, const CString& b); + + //! Returns true iff \c a is lexicographically less than \c b + static bool cmpLess(const CString::value_type& a, + const CString::value_type& b); + + //! Returns true iff \c a is lexicographically equal to \c b + static bool cmpEqual(const CString::value_type& a, + const CString::value_type& b); + }; +}; + +#endif + diff --git a/lib/io/CUnicode.cpp b/lib/base/CUnicode.cpp similarity index 92% rename from lib/io/CUnicode.cpp rename to lib/base/CUnicode.cpp index 040159f5..a0233d8d 100644 --- a/lib/io/CUnicode.cpp +++ b/lib/base/CUnicode.cpp @@ -13,7 +13,7 @@ */ #include "CUnicode.h" -#include +#include "CArch.h" #include // @@ -70,59 +70,6 @@ setError(bool* errors) } } -// -// multibyte conversion stuff when reentrant versions not available -// - -#if WINDOWS_LIKE -#define HAVE_MBSINIT 1 -#define HAVE_MBRTOWC 1 -#define HAVE_WCRTOMB 1 -#endif - -#if !HAVE_MBSINIT -static -int -mbsinit(const mbstate_t*) -{ - return 1; -} -#endif - -#if !HAVE_MBRTOWC -#include "CLock.h" -#include "CMutex.h" - -static CMutex s_mbrtowcMutex; - -static -size_t -mbrtowc(wchar_t* pwc, const char* s, size_t n, mbstate_t*) -{ - CLock lock(&s_mbrtowcMutex); - int result = mbtowc(pwc, s, n); - if (result < 0) - return (size_t)-1; - else - return result; -} -#endif - -#if !HAVE_WCRTOMB -#include "CLock.h" -#include "CMutex.h" - -static CMutex s_wcrtombMutex; - -static -size_t -wcrtomb(char* s, wchar_t wc, mbstate_t*) -{ - CLock lock(&s_wcrtombMutex); - return (size_t)wctomb(s, wc); -} -#endif - // // CUnicode @@ -274,14 +221,12 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) wchar_t* tmp = UTF8ToWideChar(src, size, errors); // get length of multibyte string - char mbc[MB_LEN_MAX]; - size_t mblen; - mbstate_t state; - memset(&state, 0, sizeof(state)); + int mblen; + CArchMBState state = ARCH->newMBState(); size_t len = 0; UInt32 n = size; for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { - mblen = wcrtomb(mbc, *scan, &state); + mblen = ARCH->convWCToMB(NULL, *scan, state); if (mblen == -1) { // unconvertable character setError(errors); @@ -293,11 +238,11 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) } // handle nul terminator - mblen = wcrtomb(mbc, L'\0', &state); + mblen = ARCH->convWCToMB(NULL, L'\0', state); if (mblen != -1) { len += mblen; } - assert(mbsinit(&state) != 0); + assert(ARCH->isInitMBState(state) != 0); // allocate multibyte string char* mbs = new char[len]; @@ -306,7 +251,7 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) char* dst = mbs; n = size; for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { - mblen = wcrtomb(dst, *scan, &state); + mblen = ARCH->convWCToMB(dst, *scan, state); if (mblen == -1) { // unconvertable character *dst++ = '?'; @@ -315,7 +260,7 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) dst += mblen; } } - mblen = wcrtomb(dst, L'\0', &state); + mblen = ARCH->convWCToMB(dst, L'\0', state); if (mblen != -1) { // don't include nul terminator dst += mblen - 1; @@ -325,6 +270,7 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) // clean up delete[] mbs; delete[] tmp; + ARCH->closeMBState(state); return text; } @@ -382,19 +328,18 @@ CUnicode::textToUTF8(const CString& src, bool* errors) // get length of multibyte string UInt32 n = src.size(); size_t len = 0; - mbstate_t state; - memset(&state, 0, sizeof(state)); + CArchMBState state = ARCH->newMBState(); for (const char* scan = src.c_str(); n > 0; ) { - size_t mblen = mbrtowc(NULL, scan, n, &state); + int mblen = ARCH->convMBToWC(NULL, scan, n, state); switch (mblen) { - case (size_t)-2: + case -2: // incomplete last character. convert to unknown character. setError(errors); len += 1; n = 0; break; - case (size_t)-1: + case -1: // invalid character. count one unknown character and // start at the next byte. setError(errors); @@ -417,7 +362,7 @@ CUnicode::textToUTF8(const CString& src, bool* errors) break; } } - memset(&state, 0, sizeof(state)); + ARCH->initMBState(state); // allocate wide character string wchar_t* wcs = new wchar_t[len]; @@ -426,15 +371,15 @@ CUnicode::textToUTF8(const CString& src, bool* errors) n = src.size(); wchar_t* dst = wcs; for (const char* scan = src.c_str(); n > 0; ++dst) { - size_t mblen = mbrtowc(dst, scan, n, &state); + int mblen = ARCH->convMBToWC(dst, scan, n, state); switch (mblen) { - case (size_t)-2: + case -2: // incomplete character. convert to unknown character. *dst = (wchar_t)0xfffd; n = 0; break; - case (size_t)-1: + case -1: // invalid character. count one unknown character and // start at the next byte. *dst = (wchar_t)0xfffd; diff --git a/lib/io/CUnicode.h b/lib/base/CUnicode.h similarity index 99% rename from lib/io/CUnicode.h rename to lib/base/CUnicode.h index 19816b17..85afbfd9 100644 --- a/lib/io/CUnicode.h +++ b/lib/base/CUnicode.h @@ -17,7 +17,6 @@ #include "CString.h" #include "BasicTypes.h" -#include //! Unicode utility functions /*! diff --git a/lib/base/ILogOutputter.h b/lib/base/ILogOutputter.h new file mode 100644 index 00000000..b4152c9e --- /dev/null +++ b/lib/base/ILogOutputter.h @@ -0,0 +1,71 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ILOGOUTPUTTER_H +#define ILOGOUTPUTTER_H + +#include "CLog.h" + +//! Outputter interface +/*! +Type of outputter interface. The logger performs all output through +outputters. ILogOutputter overrides must not call any log functions +directly or indirectly. +*/ +class ILogOutputter : public IInterface { +public: + typedef CLog::ELevel ELevel; + + //! @name manipulators + //@{ + + //! Open the outputter + /*! + Opens the outputter for writing. Calling this method on an + already open outputter must have no effect. + */ + virtual void open(const char* title) = 0; + + //! Close the outputter + /*! + Close the outputter. Calling this method on an already closed + outputter must have no effect. + */ + virtual void close() = 0; + + //! Write a message with level + /*! + Writes \c message, which has the given \c level, to a log. + If this method returns true then CLog will stop passing the + message to all outputters in the outputter chain, otherwise + it continues. Most implementations should return true. + */ + virtual bool write(ELevel level, const char* message) = 0; + + //@} + //! @name accessors + //@{ + + //! Returns the newline sequence for the outputter + /*! + Different outputters use different character sequences for newlines. + This method returns the appropriate newline sequence for this + outputter. + */ + virtual const char* getNewline() const = 0; + + //@} +}; + +#endif diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index 19ba6847..a89cf120 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -28,29 +28,23 @@ libbase_a_SOURCES = \ CFunctionJob.cpp \ CLog.cpp \ CStopwatch.cpp \ - CString.cpp \ + CStringUtil.cpp \ + CUnicode.cpp \ + LogOutputters.cpp \ XBase.cpp \ - BasicTypes.h \ CFunctionJob.h \ CLog.h \ CStopwatch.h \ CString.h \ - IInterface.h \ + CStringUtil.h \ + CUnicode.h \ IJob.h \ + ILogOutputter.h \ + LogOutputters.h \ TMethodJob.h \ - Version.h \ XBase.h \ - common.h \ - stdfstream.h \ - stdistream.h \ - stdlist.h \ - stdmap.h \ - stdostream.h \ - stdpost.h \ - stdpre.h \ - stdset.h \ - stdsstream.h \ - stdvector.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ $(NULL) diff --git a/lib/base/XBase.cpp b/lib/base/XBase.cpp index 057024b0..20d5f464 100644 --- a/lib/base/XBase.cpp +++ b/lib/base/XBase.cpp @@ -13,6 +13,7 @@ */ #include "XBase.h" +#include "CStringUtil.h" #include #include @@ -79,65 +80,3 @@ XBase::format(const char* /*id*/, const char* fmt, ...) const throw() return result; } - - -// -// MXErrno -// - -MXErrno::MXErrno() : -#if WINDOWS_LIKE - m_errno(GetLastError()), -#else - m_errno(errno), -#endif - m_string(NULL) -{ - // do nothing -} - -MXErrno::MXErrno(int err) : - m_errno(err), - m_string(NULL) -{ - // do nothing -} - -MXErrno::~MXErrno() -{ - if (m_string != NULL) { -#if WINDOWS_LIKE - LocalFree(m_string); -#endif - } -} - -int -MXErrno::getErrno() const -{ - return m_errno; -} - -const char* -MXErrno::getErrstr() const -{ -#if WINDOWS_LIKE - if (m_string != NULL) { - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM, - 0, - error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&m_string, - 0, - NULL) == 0) { - m_string = NULL; - return "unknown error"; - } - } - return m_string; -#else - return strerror(m_errno); -#endif -} diff --git a/lib/base/XBase.h b/lib/base/XBase.h index 72a871a4..fa93f94d 100644 --- a/lib/base/XBase.h +++ b/lib/base/XBase.h @@ -16,9 +16,11 @@ #define XBASE_H #include "CString.h" +/* #include "stdpre.h" #include #include "stdpost.h" +*/ //! Exception base class /*! @@ -52,33 +54,73 @@ private: mutable CString m_what; }; -//! Mix-in for handling \c errno /*! -This mix-in class for exception classes provides storage and query of -\c errno. On Windows, it uses GetLastError() instead of errno. +\def XBASE_SUBCLASS +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. getWhat() is not +declared. */ -class MXErrno { -public: - //! Save \c errno as the error code - MXErrno(); - //! Save \c err as the error code - MXErrno(int err); - virtual ~MXErrno(); +#define XBASE_SUBCLASS(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_() : super_() { } \ + name_(const CString& msg) : super_(msg) { } \ +} - //! @name accessors - //@{ +/*! +\def XBASE_SUBCLASS +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. getWhat() must be +implemented. +*/ +#define XBASE_SUBCLASS_WHAT(name_, super_) \ +class name_ : public super_ { \ +public: \ + name_() : super_() { } \ + name_(const CString& msg) : super_(msg) { } \ + \ +protected: \ + virtual CString getWhat() const throw(); \ +} - //! Get the error code - int getErrno() const; - - //! Get the human readable string for the error code - virtual const char* getErrstr() const; - - //@} - -private: - int m_errno; - mutable char* m_string; -}; +/*! +\def XBASE_SUBCLASS_FORMAT +Convenience macro to subclass from XBase (or a subclass of it), +providing the c'tor taking a const CString&. what() is overridden +to call getWhat() when first called; getWhat() can format the +error message and can call what() to get the message passed to the +c'tor. +*/ +#define XBASE_SUBCLASS_FORMAT(name_, super_) \ +class name_ : public super_ { \ +private: \ + enum EState { kFirst, kFormat, kDone }; \ + \ +public: \ + name_() : super_(), m_state(kDone) { } \ + name_(const CString& msg) : super_(msg), m_state(kFirst) { } \ + \ + virtual const char* what() const \ + { \ + if (m_state == kFirst) { \ + m_state = kFormat; \ + m_formatted = getWhat(); \ + m_state = kDone; \ + } \ + if (m_state == kDone) { \ + return m_formatted.c_str(); \ + } \ + else { \ + return super_::what(); \ + } \ + } \ + \ +protected: \ + virtual CString getWhat() const throw(); \ + \ +private: \ + mutable EState m_state; \ + mutable std::string m_formatted; \ +} #endif diff --git a/lib/base/base.dsp b/lib/base/base.dsp index 42a3ec67..a90dabc0 100644 --- a/lib/base/base.dsp +++ b/lib/base/base.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -99,7 +99,15 @@ SOURCE=.\CStopwatch.cpp # End Source File # Begin Source File -SOURCE=.\CString.cpp +SOURCE=.\CStringUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.cpp +# End Source File +# Begin Source File + +SOURCE=.\LogOutputters.cpp # End Source File # Begin Source File @@ -111,10 +119,6 @@ SOURCE=.\XBase.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\BasicTypes.h -# End Source File -# Begin Source File - SOURCE=.\CFunctionJob.h # End Source File # Begin Source File @@ -123,10 +127,6 @@ SOURCE=.\CLog.h # End Source File # Begin Source File -SOURCE=.\common.h -# End Source File -# Begin Source File - SOURCE=.\CStopwatch.h # End Source File # Begin Source File @@ -135,7 +135,11 @@ SOURCE=.\CString.h # End Source File # Begin Source File -SOURCE=.\IInterface.h +SOURCE=.\CStringUtil.h +# End Source File +# Begin Source File + +SOURCE=.\CUnicode.h # End Source File # Begin Source File @@ -143,43 +147,11 @@ SOURCE=.\IJob.h # End Source File # Begin Source File -SOURCE=.\stdfstream.h +SOURCE=.\ILogOutputter.h # End Source File # Begin Source File -SOURCE=.\stdistream.h -# End Source File -# Begin Source File - -SOURCE=.\stdlist.h -# End Source File -# Begin Source File - -SOURCE=.\stdmap.h -# End Source File -# Begin Source File - -SOURCE=.\stdostream.h -# End Source File -# Begin Source File - -SOURCE=.\stdpost.h -# End Source File -# Begin Source File - -SOURCE=.\stdpre.h -# End Source File -# Begin Source File - -SOURCE=.\stdset.h -# End Source File -# Begin Source File - -SOURCE=.\stdsstream.h -# End Source File -# Begin Source File - -SOURCE=.\stdvector.h +SOURCE=.\LogOutputters.h # End Source File # Begin Source File @@ -187,10 +159,6 @@ SOURCE=.\TMethodJob.h # End Source File # Begin Source File -SOURCE=.\Version.h -# End Source File -# Begin Source File - SOURCE=.\XBase.h # End Source File # End Group diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index d84ac270..932072e8 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -36,6 +36,7 @@ #include "CLog.h" #include "CStopwatch.h" #include "TMethodJob.h" +#include "CArch.h" // // CClient @@ -545,7 +546,7 @@ CClient::runServer() break; } catch (XSocketConnect& e) { - LOG((CLOG_DEBUG1 "failed to connect to server: %s", e.getErrstr())); + LOG((CLOG_DEBUG1 "failed to connect to server: %s", e.what())); // failed to connect. if not camping then rethrow. if (!m_camp) { @@ -553,7 +554,7 @@ CClient::runServer() } // we're camping. wait a bit before retrying - CThread::sleep(15.0); + ARCH->sleep(15.0); } } diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/client/CMSWindowsSecondaryScreen.cpp index 11536616..7f858323 100644 --- a/lib/client/CMSWindowsSecondaryScreen.cpp +++ b/lib/client/CMSWindowsSecondaryScreen.cpp @@ -14,10 +14,10 @@ #include "CMSWindowsSecondaryScreen.h" #include "CMSWindowsScreen.h" -#include "CPlatform.h" #include "XScreen.h" #include "CLock.h" #include "CLog.h" +#include "CArchMiscWindows.h" #include // these are only defined when WINVER >= 0x0500 @@ -34,7 +34,7 @@ CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( IScreenReceiver* receiver) : - m_is95Family(CPlatform::isWindows95Family()), + m_is95Family(CArchMiscWindows::isWindows95Family()), m_window(NULL), m_mask(0) { diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index 28673161..23c0e101 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -38,6 +38,8 @@ libclient_a_SOURCES = \ ISecondaryScreenFactory.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/client/client.dsp b/lib/client/client.dsp index 5880f495..a9b03625 100644 --- a/lib/client/client.dsp +++ b/lib/client/client.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/base/BasicTypes.h b/lib/common/BasicTypes.h similarity index 100% rename from lib/base/BasicTypes.h rename to lib/common/BasicTypes.h diff --git a/lib/base/IInterface.h b/lib/common/IInterface.h similarity index 100% rename from lib/base/IInterface.h rename to lib/common/IInterface.h diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am new file mode 100644 index 00000000..8a6abc21 --- /dev/null +++ b/lib/common/Makefile.am @@ -0,0 +1,45 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + common.dsp \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +noinst_LIBRARIES = libcommon.a +libcommon_a_SOURCES = \ + BasicTypes.h \ + IInterface.h \ + Version.h \ + common.h \ + stdfstream.h \ + stdistream.h \ + stdlist.h \ + stdmap.h \ + stdostream.h \ + stdpost.h \ + stdpre.h \ + stdset.h \ + stdsstream.h \ + stdstring.h \ + stdvector.h \ + $(NULL) +INCLUDES = \ + $(NULL) diff --git a/lib/base/Version.h b/lib/common/Version.h similarity index 91% rename from lib/base/Version.h rename to lib/common/Version.h index ebc57b63..882e8f7b 100644 --- a/lib/base/Version.h +++ b/lib/common/Version.h @@ -23,6 +23,7 @@ #endif // important strings +static const char* kApplication = "synergy"; static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; static const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; static const char* kWebsite = "http://synergy2.sourceforge.net/"; @@ -31,6 +32,9 @@ static const char* kWebsite = "http://synergy2.sourceforge.net/"; // a release version, odd implies development version. static const char* kVersion = VERSION; +// application version +static const char* kAppVersion = "synergy " VERSION; + // exit codes static const int kExitSuccess = 0; // successful completion static const int kExitFailed = 1; // general failure diff --git a/lib/common/common.dsp b/lib/common/common.dsp new file mode 100644 index 00000000..dd600c0a --- /dev/null +++ b/lib/common/common.dsp @@ -0,0 +1,154 @@ +# Microsoft Developer Studio Project File - Name="common" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=common - Win32 Debug +!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 "common.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 "common.mak" CFG="common - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "common - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "common - Win32 Debug" (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)" == "common - 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" +# 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 /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /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)" == "common - 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" +# 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 /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /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 "common - Win32 Release" +# Name "common - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\BasicTypes.h +# End Source File +# Begin Source File + +SOURCE=.\common.h +# End Source File +# Begin Source File + +SOURCE=.\IInterface.h +# End Source File +# Begin Source File + +SOURCE=.\stdfstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdistream.h +# End Source File +# Begin Source File + +SOURCE=.\stdlist.h +# End Source File +# Begin Source File + +SOURCE=.\stdmap.h +# End Source File +# Begin Source File + +SOURCE=.\stdostream.h +# End Source File +# Begin Source File + +SOURCE=.\stdpost.h +# End Source File +# Begin Source File + +SOURCE=.\stdpre.h +# End Source File +# Begin Source File + +SOURCE=.\stdset.h +# End Source File +# Begin Source File + +SOURCE=.\stdsstream.h +# End Source File +# Begin Source File + +SOURCE=.\stdstring.h +# End Source File +# Begin Source File + +SOURCE=.\stdvector.h +# End Source File +# Begin Source File + +SOURCE=.\Version.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/base/common.h b/lib/common/common.h similarity index 100% rename from lib/base/common.h rename to lib/common/common.h diff --git a/lib/base/stdfstream.h b/lib/common/stdfstream.h similarity index 100% rename from lib/base/stdfstream.h rename to lib/common/stdfstream.h diff --git a/lib/base/stdistream.h b/lib/common/stdistream.h similarity index 100% rename from lib/base/stdistream.h rename to lib/common/stdistream.h diff --git a/lib/base/stdlist.h b/lib/common/stdlist.h similarity index 100% rename from lib/base/stdlist.h rename to lib/common/stdlist.h diff --git a/lib/base/stdmap.h b/lib/common/stdmap.h similarity index 100% rename from lib/base/stdmap.h rename to lib/common/stdmap.h diff --git a/lib/base/stdostream.h b/lib/common/stdostream.h similarity index 100% rename from lib/base/stdostream.h rename to lib/common/stdostream.h diff --git a/lib/base/stdpost.h b/lib/common/stdpost.h similarity index 100% rename from lib/base/stdpost.h rename to lib/common/stdpost.h diff --git a/lib/base/stdpre.h b/lib/common/stdpre.h similarity index 100% rename from lib/base/stdpre.h rename to lib/common/stdpre.h diff --git a/lib/base/stdset.h b/lib/common/stdset.h similarity index 100% rename from lib/base/stdset.h rename to lib/common/stdset.h diff --git a/lib/base/stdsstream.h b/lib/common/stdsstream.h similarity index 100% rename from lib/base/stdsstream.h rename to lib/common/stdsstream.h diff --git a/lib/common/stdstring.h b/lib/common/stdstring.h new file mode 100644 index 00000000..3d83c03c --- /dev/null +++ b/lib/common/stdstring.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/base/stdvector.h b/lib/common/stdvector.h similarity index 100% rename from lib/base/stdvector.h rename to lib/common/stdvector.h diff --git a/lib/http/CHTTPProtocol.h b/lib/http/CHTTPProtocol.h index d4e06b43..8c866455 100644 --- a/lib/http/CHTTPProtocol.h +++ b/lib/http/CHTTPProtocol.h @@ -16,6 +16,7 @@ #define CHTTPPROTOCOL_H #include "CString.h" +#include "CStringUtil.h" #include "BasicTypes.h" #include "stdlist.h" #include "stdmap.h" diff --git a/lib/http/Makefile.am b/lib/http/Makefile.am index ba51677e..c9abe536 100644 --- a/lib/http/Makefile.am +++ b/lib/http/Makefile.am @@ -31,6 +31,8 @@ libhttp_a_SOURCES = \ XHTTP.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/http/XHTTP.cpp b/lib/http/XHTTP.cpp index 493a0383..9b146091 100644 --- a/lib/http/XHTTP.cpp +++ b/lib/http/XHTTP.cpp @@ -14,6 +14,7 @@ #include "XHTTP.h" #include "CHTTPProtocol.h" +#include "CStringUtil.h" #include "stdsstream.h" // diff --git a/lib/http/http.dsp b/lib/http/http.dsp index 5d3a6e7b..439b0616 100644 --- a/lib/http/http.dsp +++ b/lib/http/http.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /W4 /GX /O2 /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index 3ea4e3af..8bac4c8e 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -30,20 +30,20 @@ libio_a_SOURCES = \ CInputStreamFilter.cpp \ COutputStreamFilter.cpp \ CStreamBuffer.cpp \ - CUnicode.cpp \ XIO.cpp \ CBufferedInputStream.h \ CBufferedOutputStream.h \ CInputStreamFilter.h \ COutputStreamFilter.h \ CStreamBuffer.h \ - CUnicode.h \ IInputStream.h \ IOutputStream.h \ IStreamFilterFactory.h \ XIO.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ $(NULL) diff --git a/lib/io/XIO.h b/lib/io/XIO.h index 3cb1d944..31871bf5 100644 --- a/lib/io/XIO.h +++ b/lib/io/XIO.h @@ -18,33 +18,25 @@ #include "XBase.h" //! Generic I/O exception -class XIO : public XBase { }; +XBASE_SUBCLASS(XIO, XBase); //! I/O closing exception /*! Thrown if a stream cannot be closed. */ -class XIOClose : public XIO { }; +XBASE_SUBCLASS(XIOClose, XIO); //! I/O already closed exception /*! Thrown when attempting to close or perform I/O on an already closed. stream. */ -class XIOClosed : public XIO { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_WHAT(XIOClosed, XIO); //! I/O end of stream exception /*! Thrown when attempting to read beyond the end of a stream. */ -class XIOEndOfStream : public XIO { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_WHAT(XIOEndOfStream, XIO); #endif diff --git a/lib/io/io.dsp b/lib/io/io.dsp index 02159a1a..a6afae06 100644 --- a/lib/io/io.dsp +++ b/lib/io/io.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -107,10 +107,6 @@ SOURCE=.\CStreamBuffer.cpp # End Source File # Begin Source File -SOURCE=.\CUnicode.cpp -# End Source File -# Begin Source File - SOURCE=.\XIO.cpp # End Source File # End Group @@ -139,10 +135,6 @@ SOURCE=.\CStreamBuffer.h # End Source File # Begin Source File -SOURCE=.\CUnicode.h -# End Source File -# Begin Source File - SOURCE=.\IInputStream.h # End Source File # Begin Source File diff --git a/lib/mt/CCondVar.cpp b/lib/mt/CCondVar.cpp index ae618849..d13aedfd 100644 --- a/lib/mt/CCondVar.cpp +++ b/lib/mt/CCondVar.cpp @@ -14,6 +14,7 @@ #include "CCondVar.h" #include "CStopwatch.h" +#include "CArch.h" // // CCondVarBase @@ -21,17 +22,14 @@ CCondVarBase::CCondVarBase(CMutex* mutex) : m_mutex(mutex) -#if WINDOWS_LIKE - , m_waitCountMutex() -#endif { assert(m_mutex != NULL); - init(); + m_cond = ARCH->newCondVar(); } CCondVarBase::~CCondVarBase() { - fini(); + ARCH->closeCondVar(m_cond); } void @@ -46,67 +44,16 @@ CCondVarBase::unlock() const m_mutex->unlock(); } -bool -CCondVarBase::wait(double timeout) const -{ - CStopwatch timer(true); - return wait(timer, timeout); -} - -CMutex* -CCondVarBase::getMutex() const -{ - return m_mutex; -} - -#if HAVE_PTHREAD - -#include "CThread.h" -#include -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif -#include - -void -CCondVarBase::init() -{ - pthread_cond_t* cond = new pthread_cond_t; - int status = pthread_cond_init(cond, NULL); - assert(status == 0); - m_cond = reinterpret_cast(cond); -} - -void -CCondVarBase::fini() -{ - pthread_cond_t* cond = reinterpret_cast(m_cond); - int status = pthread_cond_destroy(cond); - assert(status == 0); - delete cond; -} - void CCondVarBase::signal() { - pthread_cond_t* cond = reinterpret_cast(m_cond); - int status = pthread_cond_signal(cond); - assert(status == 0); + ARCH->signalCondVar(m_cond); } void CCondVarBase::broadcast() { - pthread_cond_t* cond = reinterpret_cast(m_cond); - int status = pthread_cond_broadcast(cond); - assert(status == 0); + ARCH->broadcastCondVar(m_cond); } bool @@ -118,221 +65,17 @@ CCondVarBase::wait(CStopwatch& timer, double timeout) const if (timeout < 0.0) return false; } - - // get condition variable and mutex - pthread_cond_t* cond = reinterpret_cast(m_cond); - pthread_mutex_t* mutex = reinterpret_cast(m_mutex->m_mutex); - - // get final time - struct timeval now; - gettimeofday(&now, NULL); - struct timespec finalTime; - finalTime.tv_sec = now.tv_sec; - finalTime.tv_nsec = now.tv_usec * 1000; - if (timeout >= 0.0) { - const long timeout_sec = (long)timeout; - const long timeout_nsec = (long)(1000000000.0 * (timeout - timeout_sec)); - finalTime.tv_sec += timeout_sec; - finalTime.tv_nsec += timeout_nsec; - if (finalTime.tv_nsec >= 1000000000) { - finalTime.tv_nsec -= 1000000000; - finalTime.tv_sec += 1; - } - } - - // repeat until we reach the final time - int status; - for (;;) { - // compute the next timeout - gettimeofday(&now, NULL); - struct timespec endTime; - endTime.tv_sec = now.tv_sec; - endTime.tv_nsec = now.tv_usec * 1000 + 50000000; - if (endTime.tv_nsec >= 1000000000) { - endTime.tv_nsec -= 1000000000; - endTime.tv_sec += 1; - } - - // see if we should cancel this thread - CThread::testCancel(); - - // done if past final timeout - if (timeout >= 0.0) { - if (endTime.tv_sec > finalTime.tv_sec || - (endTime.tv_sec == finalTime.tv_sec && - endTime.tv_nsec >= finalTime.tv_nsec)) { - status = ETIMEDOUT; - break; - } - } - - // wait - status = pthread_cond_timedwait(cond, mutex, &endTime); - - // check for cancel again - CThread::testCancel(); - - // check wait status - if (status != ETIMEDOUT && status != EINTR) { - break; - } - } - - switch (status) { - case 0: - // success - return true; - - case ETIMEDOUT: - return false; - - default: - assert(0 && "condition variable wait error"); - return false; - } -} - -#endif // HAVE_PTHREAD - -#if WINDOWS_LIKE - -#include "CLock.h" -#include "CThreadRep.h" -#define WIN32_LEAN_AND_MEAN -#include - -// -// note -- implementation taken from -// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html -// titled "Strategies for Implementing POSIX Condition Variables -// on Win32." it also provides an implementation that doesn't -// suffer from the incorrectness problem described in our -// corresponding header but it is slower, still unfair, and -// can cause busy waiting. -// - -void -CCondVarBase::init() -{ - // prepare events - HANDLE* events = new HANDLE[2]; - events[kSignal] = CreateEvent(NULL, FALSE, FALSE, NULL); - events[kBroadcast] = CreateEvent(NULL, TRUE, FALSE, NULL); - - // prepare members - m_cond = reinterpret_cast(events); - m_waitCount = 0; -} - -void -CCondVarBase::fini() -{ - HANDLE* events = reinterpret_cast(m_cond); - CloseHandle(events[kSignal]); - CloseHandle(events[kBroadcast]); - delete[] events; -} - -void -CCondVarBase::signal() -{ - // is anybody waiting? - bool hasWaiter; - { - CLock lock(&m_waitCountMutex); - hasWaiter = (m_waitCount > 0); - } - - // wake one thread if anybody is waiting - if (hasWaiter) { - SetEvent(reinterpret_cast(m_cond)[kSignal]); - } -} - -void -CCondVarBase::broadcast() -{ - // is anybody waiting? - bool hasWaiter; - { - CLock lock(&m_waitCountMutex); - hasWaiter = (m_waitCount > 0); - } - - // wake all threads if anybody is waiting - if (hasWaiter) { - SetEvent(reinterpret_cast(m_cond)[kBroadcast]); - } + return wait(timeout); } bool -CCondVarBase::wait(CStopwatch& timer, double timeout) const +CCondVarBase::wait(double timeout) const { - // check timeout against timer - if (timeout >= 0.0) { - timeout -= timer.getTime(); - if (timeout < 0.0) { - return false; - } - } - - // prepare to wait - CThreadPtr currentRep = CThreadRep::getCurrentThreadRep(); - const DWORD winTimeout = (timeout < 0.0) ? INFINITE : - static_cast(1000.0 * timeout); - HANDLE* events = reinterpret_cast(m_cond); - HANDLE handles[3]; - handles[0] = events[kSignal]; - handles[1] = events[kBroadcast]; - handles[2] = currentRep->getCancelEvent(); - const DWORD n = currentRep->isCancellable() ? 3 : 2; - - // update waiter count - { - CLock lock(&m_waitCountMutex); - ++m_waitCount; - } - - // release mutex. this should be atomic with the wait so that it's - // impossible for another thread to signal us between the unlock and - // the wait, which would lead to a lost signal on broadcasts. - // however, we're using a manual reset event for broadcasts which - // stays set until we reset it, so we don't lose the broadcast. - m_mutex->unlock(); - - // wait for a signal or broadcast - DWORD result = WaitForMultipleObjects(n, handles, FALSE, winTimeout); - - // cancel takes priority - if (n == 3 && result != WAIT_OBJECT_0 + 2 && - WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) { - result = WAIT_OBJECT_0 + 2; - } - - // update the waiter count and check if we're the last waiter - bool last; - { - CLock lock(&m_waitCountMutex); - --m_waitCount; - last = (result == WAIT_OBJECT_0 + 1 && m_waitCount == 0); - } - - // reset the broadcast event if we're the last waiter - if (last) { - ResetEvent(events[kBroadcast]); - } - - // reacquire the mutex - m_mutex->lock(); - - // cancel thread if necessary - if (result == WAIT_OBJECT_0 + 2) { - currentRep->testCancel(); - } - - // return success or failure - return (result == WAIT_OBJECT_0 + 0 || - result == WAIT_OBJECT_0 + 1); + return ARCH->waitCondVar(m_cond, m_mutex->m_mutex, timeout); } -#endif // WINDOWS_LIKE +CMutex* +CCondVarBase::getMutex() const +{ + return m_mutex; +} diff --git a/lib/mt/CCondVar.h b/lib/mt/CCondVar.h index 5310e148..1b92ffd7 100644 --- a/lib/mt/CCondVar.h +++ b/lib/mt/CCondVar.h @@ -17,6 +17,7 @@ #include "CMutex.h" #include "BasicTypes.h" +#include "IArchMultithread.h" class CStopwatch; @@ -112,22 +113,13 @@ public: //@} private: - void init(); - void fini(); - // not implemented CCondVarBase(const CCondVarBase&); CCondVarBase& operator=(const CCondVarBase&); private: CMutex* m_mutex; - void* m_cond; - -#if WINDOWS_LIKE - enum { kSignal, kBroadcast }; - mutable UInt32 m_waitCount; - CMutex m_waitCountMutex; -#endif + CArchCond m_cond; }; //! Condition variable diff --git a/lib/mt/CMutex.cpp b/lib/mt/CMutex.cpp index 6e51ea7e..0d2bab3c 100644 --- a/lib/mt/CMutex.cpp +++ b/lib/mt/CMutex.cpp @@ -13,7 +13,7 @@ */ #include "CMutex.h" -#include "CLog.h" +#include "CArch.h" // // CMutex @@ -21,17 +21,17 @@ CMutex::CMutex() { - init(); + m_mutex = ARCH->newMutex(); } CMutex::CMutex(const CMutex&) { - init(); + m_mutex = ARCH->newMutex(); } CMutex::~CMutex() { - fini(); + ARCH->closeMutex(m_mutex); } CMutex& @@ -40,111 +40,14 @@ CMutex::operator=(const CMutex&) return *this; } -#if HAVE_PTHREAD - -#include -#include - -void -CMutex::init() -{ - pthread_mutex_t* mutex = new pthread_mutex_t; - int status = pthread_mutex_init(mutex, NULL); - assert(status == 0); -// status = pthread_mutexattr_settype(mutex, PTHREAD_MUTEX_RECURSIVE); -// assert(status == 0); - m_mutex = reinterpret_cast(mutex); -} - -void -CMutex::fini() -{ - pthread_mutex_t* mutex = reinterpret_cast(m_mutex); - int status = pthread_mutex_destroy(mutex); - LOGC(status != 0, (CLOG_ERR "pthread_mutex_destroy status %d", status)); - assert(status == 0); - delete mutex; -} - void CMutex::lock() const { - pthread_mutex_t* mutex = reinterpret_cast(m_mutex); - int status = pthread_mutex_lock(mutex); - - switch (status) { - case 0: - // success - return; - - case EDEADLK: - assert(0 && "lock already owned"); - break; - - case EAGAIN: - assert(0 && "too many recursive locks"); - break; - - default: - LOG((CLOG_ERR "pthread_mutex_lock status %d", status)); - assert(0 && "unexpected error"); - } + ARCH->lockMutex(m_mutex); } void CMutex::unlock() const { - pthread_mutex_t* mutex = reinterpret_cast(m_mutex); - int status = pthread_mutex_unlock(mutex); - - switch (status) { - case 0: - // success - return; - - case EPERM: - assert(0 && "thread doesn't own a lock"); - break; - - default: - LOG((CLOG_ERR "pthread_mutex_unlock status %d", status)); - assert(0 && "unexpected error"); - } + ARCH->unlockMutex(m_mutex); } - -#endif // HAVE_PTHREAD - -#if WINDOWS_LIKE - -#define WIN32_LEAN_AND_MEAN -#include - -void -CMutex::init() -{ - CRITICAL_SECTION* mutex = new CRITICAL_SECTION; - InitializeCriticalSection(mutex); - m_mutex = reinterpret_cast(mutex); -} - -void -CMutex::fini() -{ - CRITICAL_SECTION* mutex = reinterpret_cast(m_mutex); - DeleteCriticalSection(mutex); - delete mutex; -} - -void -CMutex::lock() const -{ - EnterCriticalSection(reinterpret_cast(m_mutex)); -} - -void -CMutex::unlock() const -{ - LeaveCriticalSection(reinterpret_cast(m_mutex)); -} - -#endif // WINDOWS_LIKE diff --git a/lib/mt/CMutex.h b/lib/mt/CMutex.h index 4ae6fa03..c11eeee3 100644 --- a/lib/mt/CMutex.h +++ b/lib/mt/CMutex.h @@ -15,6 +15,8 @@ #ifndef CMUTEX_H #define CMUTEX_H +#include "IArchMultithread.h" + //! Mutual exclusion /*! A non-recursive mutual exclusion object. Only one thread at a time can @@ -68,13 +70,9 @@ public: //@} -private: - void init(); - void fini(); - private: friend class CCondVarBase; - void* m_mutex; + CArchMutex m_mutex; }; #endif diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp index 5afd3122..27b0cedd 100644 --- a/lib/mt/CThread.cpp +++ b/lib/mt/CThread.cpp @@ -13,161 +13,166 @@ */ #include "CThread.h" -#include "CLock.h" -#include "CThreadRep.h" +#include "XMT.h" #include "XThread.h" #include "CLog.h" -#include "CStopwatch.h" +#include "IJob.h" +#include "CArch.h" // // CThread // -CThread::CThread(IJob* job, void* userData) +CThread::CThread(IJob* job) { - m_rep = new CThreadRep(job, userData); + m_thread = ARCH->newThread(&CThread::threadFunc, job); + if (m_thread == NULL) { + // couldn't create thread + delete job; + throw XMTThreadUnavailable(); + } } -CThread::CThread(const CThread& thread) : - m_rep(thread.m_rep) +CThread::CThread(const CThread& thread) { - m_rep->ref(); + m_thread = ARCH->copyThread(thread.m_thread); } -CThread::CThread(CThreadRep* rep) : - m_rep(rep) +CThread::CThread(CArchThread adoptedThread) { - // do nothing. rep should have already been Ref()'d. + m_thread = adoptedThread; } CThread::~CThread() { - m_rep->unref(); + ARCH->closeThread(m_thread); } CThread& CThread::operator=(const CThread& thread) { - if (thread.m_rep != m_rep) { - m_rep->unref(); - m_rep = thread.m_rep; - m_rep->ref(); - } + // copy given thread and release ours + CArchThread copy = ARCH->copyThread(thread.m_thread); + ARCH->closeThread(m_thread); + + // cut over + m_thread = copy; + return *this; } -void -CThread::init() -{ - CThreadRep::initThreads(); -} - -void -CThread::sleep(double timeout) -{ - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - if (timeout >= 0.0) { - currentRep->testCancel(); - currentRep->sleep(timeout); - } - currentRep->testCancel(); -} - void CThread::exit(void* result) { - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - LOG((CLOG_DEBUG1 "throw exit on thread %p", currentRep.operator->())); throw XThreadExit(result); } -bool -CThread::enableCancel(bool enable) -{ - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - return currentRep->enableCancel(enable); -} - void CThread::cancel() { - m_rep->cancel(); + ARCH->cancelThread(m_thread); } void CThread::setPriority(int n) { - m_rep->setPriority(n); + ARCH->setPriorityOfThread(m_thread, n); } CThread CThread::getCurrentThread() { - return CThread(CThreadRep::getCurrentThreadRep()); + return CThread(ARCH->newCurrentThread()); +} + +void +CThread::testCancel() +{ + ARCH->testCancelThread(); } bool CThread::wait(double timeout) const { - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - return currentRep->wait(m_rep, timeout); + return ARCH->wait(m_thread, timeout); } -#if WINDOWS_LIKE bool CThread::waitForEvent(double timeout) { - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - return currentRep->waitForEvent(timeout); -} -#endif - -void -CThread::testCancel() -{ - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - currentRep->testCancel(); + return ARCH->waitForEvent(timeout); } void* CThread::getResult() const { if (wait()) - return m_rep->getResult(); + return ARCH->getResultOfThread(m_thread); else return NULL; } -void* -CThread::getUserData() +IArchMultithread::ThreadID +CThread::getID() const { - return m_rep->getUserData(); + return ARCH->getIDOfThread(m_thread); } bool CThread::operator==(const CThread& thread) const { - return (m_rep == thread.m_rep); + return ARCH->isSameThread(m_thread, thread.m_thread); } bool CThread::operator!=(const CThread& thread) const { - return (m_rep != thread.m_rep); + return !ARCH->isSameThread(m_thread, thread.m_thread); } - -// -// CThreadMaskCancel -// - -CThreadMaskCancel::CThreadMaskCancel() : - m_old(CThread::enableCancel(false)) +void* +CThread::threadFunc(void* vjob) { - // do nothing -} + // get this thread's id for logging + IArchMultithread::ThreadID id; + { + CArchThread thread = ARCH->newCurrentThread(); + id = ARCH->getIDOfThread(thread); + ARCH->closeThread(thread); + } -CThreadMaskCancel::~CThreadMaskCancel() -{ - CThread::enableCancel(m_old); + // get job + IJob* job = reinterpret_cast(vjob); + + // run job + void* result = NULL; + try { + // go + LOG((CLOG_DEBUG1 "thread 0x%08x entry", id)); + job->run(); + LOG((CLOG_DEBUG1 "thread 0x%08x exit", id)); + } + + catch (XThreadCancel&) { + // client called cancel() + LOG((CLOG_DEBUG1 "caught cancel on thread 0x%08x", id)); + delete job; + throw; + } + catch (XThreadExit& e) { + // client called exit() + result = e.m_result; + LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x, result %p", id, result)); + } + catch (...) { + LOG((CLOG_DEBUG1 "exception on thread 0x%08x", id)); + delete job; + throw; + } + + // done with job + delete job; + + // return exit result + return result; } diff --git a/lib/mt/CThread.h b/lib/mt/CThread.h index f9dc9ac7..aaa5a097 100644 --- a/lib/mt/CThread.h +++ b/lib/mt/CThread.h @@ -16,9 +16,9 @@ #define CTHREAD_H #include "common.h" +#include "IArchMultithread.h" class IJob; -class CThreadRep; //! Thread handle /*! @@ -43,10 +43,9 @@ public: //! Run \c adoptedJob in a new thread /*! Create and start a new thread executing the \c adoptedJob. The - user data can be retrieved with getUserData(). The new thread - takes ownership of \c adoptedJob and will delete it. + new thread takes ownership of \c adoptedJob and will delete it. */ - CThread(IJob* adoptedJob, void* userData = 0); + CThread(IJob* adoptedJob); //! Duplicate a thread handle /*! @@ -74,24 +73,6 @@ public: */ CThread& operator=(const CThread&); - //! Initialize the thread library - /*! - Initialize the thread library. This \b must be called before - any other thread methods or creating a thread object. It is - harmless to call init() multiple times. - */ - static void init(); - - //! Sleep - /*! - Blocks the calling thread for \c timeout seconds. If - \c timeout < 0.0 then the call returns immediately. If \c timeout - == 0.0 then the calling thread yields the CPU. - - (cancellation point) - */ - static void sleep(double timeout); - //! Terminate the calling thread /*! Terminate the calling thread. This function does not return but @@ -107,15 +88,6 @@ public: */ static void exit(void*); - //! Enable or disable cancellation - /*! - Enable or disable cancellation. The default is enabled. This is not - a cancellation point so if you just enabled cancellation and want to - allow immediate cancellation you need to call testCancel(). - Returns the previous state. - */ - static bool enableCancel(bool); - //! Cancel thread /*! Cancel the thread. cancel() never waits for the thread to @@ -173,12 +145,6 @@ public: */ static void testCancel(); - //! Get the thread user data - /*! - Gets the user data passed to the c'tor that created this thread. - */ - void* getUserData(); - //! Wait for thread to terminate /*! Waits for the thread to terminate (by exit() or cancel() or @@ -192,7 +158,6 @@ public: */ bool wait(double timeout = -1.0) const; -#if WINDOWS_LIKE //! Wait for an event (win32) /*! Wait for the message queue to contain a message for up to \c timeout @@ -207,7 +172,6 @@ public: (cancellation point) */ static bool waitForEvent(double timeout = -1.0); -#endif //! Get the exit result /*! @@ -219,6 +183,15 @@ public: */ void* getResult() const; + //! Get the thread id + /*! + Returns an integer id for this thread. This id must not be used to + check if two CThread objects refer to the same thread. Use + operator==() for that. + */ + IArchMultithread::ThreadID + getID() const; + //! Compare thread handles /*! Returns true if two CThread objects refer to the same thread. @@ -234,24 +207,12 @@ public: //@} private: - CThread(CThreadRep*); + CThread(CArchThread); + + static void* threadFunc(void*); private: - CThreadRep* m_rep; -}; - -//! Disable cancellation utility -/*! -This class disables cancellation for the current thread in the c'tor -and enables it in the d'tor. -*/ -class CThreadMaskCancel { -public: - CThreadMaskCancel(); - ~CThreadMaskCancel(); - -private: - bool m_old; + CArchThread m_thread; }; #endif diff --git a/lib/mt/CThreadRep.cpp b/lib/mt/CThreadRep.cpp deleted file mode 100644 index cf1c8276..00000000 --- a/lib/mt/CThreadRep.cpp +++ /dev/null @@ -1,794 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CThreadRep.h" -#include "CLock.h" -#include "CMutex.h" -#include "CThread.h" -#include "XMT.h" -#include "XThread.h" -#include "CLog.h" -#include "IJob.h" - -#if HAVE_PTHREAD - -# include -# define SIGWAKEUP SIGUSR1 - -#elif WINDOWS_LIKE - -# if !defined(_MT) -# error multithreading compile option is required -# endif -# include - -#else - -#error unsupported platform for multithreading - -#endif - -// -// CThreadRep -// - -CMutex* CThreadRep::s_mutex = NULL; -CThreadRep* CThreadRep::s_head = NULL; -#if HAVE_PTHREAD -pthread_t CThreadRep::s_signalThread; -#endif - -CThreadRep::CThreadRep() : - m_prev(NULL), - m_next(NULL), - m_refCount(1), - m_job(NULL), - m_userData(NULL) -{ - // note -- s_mutex must be locked on entry - assert(s_mutex != NULL); - - // initialize stuff - init(); -#if HAVE_PTHREAD - // get main thread id - m_thread = pthread_self(); -#elif WINDOWS_LIKE - // get main thread id - m_thread = NULL; - m_id = GetCurrentThreadId(); -#endif - - // insert ourself into linked list - if (s_head != NULL) { - s_head->m_prev = this; - m_next = s_head; - } - s_head = this; -} - -CThreadRep::CThreadRep(IJob* job, void* userData) : - m_prev(NULL), - m_next(NULL), - m_refCount(2), // 1 for us, 1 for thread - m_job(job), - m_userData(userData) -{ - assert(m_job != NULL); - assert(s_mutex != NULL); - - // create a thread rep for the main thread if the current thread - // is unknown. note that this might cause multiple "main" threads - // if threads are created external to this library. - getCurrentThreadRep()->unref(); - - // initialize - init(); - - // hold mutex while we create the thread - CLock lock(s_mutex); - - // start the thread. throw if it doesn't start. -#if HAVE_PTHREAD - // mask some signals in all threads except the main thread - sigset_t sigset, oldsigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); - // pthread_create() RedHat 7.2 smp fails with a NULL attr. - pthread_attr_t attr; - int status = pthread_attr_init(&attr); - if (status == 0) { - status = pthread_create(&m_thread, &attr, threadFunc, (void*)this); - pthread_attr_destroy(&attr); - } - pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); - if (status != 0) { - throw XMTThreadUnavailable(); - } -#elif WINDOWS_LIKE - unsigned int id; - m_thread = reinterpret_cast(_beginthreadex(NULL, 0, - threadFunc, (void*)this, 0, &id)); - m_id = static_cast(id); - if (m_thread == 0) { - throw XMTThreadUnavailable(); - } -#endif - - // insert ourself into linked list - if (s_head != NULL) { - s_head->m_prev = this; - m_next = s_head; - } - s_head = this; - - // returning releases the locks, allowing the child thread to run -} - -CThreadRep::~CThreadRep() -{ - // note -- s_mutex must be locked on entry - - // remove ourself from linked list - if (m_prev != NULL) { - m_prev->m_next = m_next; - } - if (m_next != NULL) { - m_next->m_prev = m_prev; - } - if (s_head == this) { - s_head = m_next; - } - - // clean up - fini(); -} - -void -CThreadRep::initThreads() -{ - if (s_mutex == NULL) { - s_mutex = new CMutex; - -#if HAVE_PTHREAD - // install SIGWAKEUP handler - struct sigaction act; - sigemptyset(&act.sa_mask); -# if defined(SA_INTERRUPT) - act.sa_flags = SA_INTERRUPT; -# else - act.sa_flags = 0; -# endif - act.sa_handler = &threadCancel; - sigaction(SIGWAKEUP, &act, NULL); - - // set signal mask - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGWAKEUP); - pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); - sigemptyset(&sigset); - sigaddset(&sigset, SIGPIPE); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - pthread_sigmask(SIG_BLOCK, &sigset, NULL); - - // fire up the INT and TERM signal handler thread. we could - // instead arrange to catch and handle these signals but - // we'd be unable to cancel the main thread since no pthread - // calls are allowed in a signal handler. - pthread_attr_t attr; - int status = pthread_attr_init(&attr); - if (status == 0) { - status = pthread_create(&s_signalThread, &attr, - &CThreadRep::threadSignalHandler, - getCurrentThreadRep()); - pthread_attr_destroy(&attr); - } - if (status != 0) { - // can't create thread to wait for signal so don't block - // the signals. - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); - } -#endif // HAVE_PTHREAD - } -} - -void -CThreadRep::ref() -{ - CLock lock(s_mutex); - ++m_refCount; -} - -void -CThreadRep::unref() -{ - CLock lock(s_mutex); - if (--m_refCount == 0) { - delete this; - } -} - -bool -CThreadRep::enableCancel(bool enable) -{ - CLock lock(s_mutex); - const bool old = m_cancellable; - m_cancellable = enable; - return old; -} - -bool -CThreadRep::isCancellable() const -{ - CLock lock(s_mutex); - return (m_cancellable && !m_cancelling); -} - -void* -CThreadRep::getResult() const -{ - // no lock necessary since thread isn't running - return m_result; -} - -void* -CThreadRep::getUserData() const -{ - // no lock necessary because the value never changes - return m_userData; -} - -CThreadRep* -CThreadRep::getCurrentThreadRep() -{ - assert(s_mutex != NULL); - -#if HAVE_PTHREAD - const pthread_t thread = pthread_self(); -#elif WINDOWS_LIKE - const DWORD id = GetCurrentThreadId(); -#endif - - // lock list while we search - CLock lock(s_mutex); - - // search - CThreadRep* scan = s_head; - while (scan != NULL) { -#if HAVE_PTHREAD - if (scan->m_thread == thread) { - break; - } -#elif WINDOWS_LIKE - if (scan->m_id == id) { - break; - } -#endif - scan = scan->m_next; - } - - // create and use main thread rep if thread not found - if (scan == NULL) { - scan = new CThreadRep(); - } - - // ref for caller - ++scan->m_refCount; - - return scan; -} - -void -CThreadRep::doThreadFunc() -{ - // default priority is slightly below normal - setPriority(1); - - // wait for parent to initialize this object - { CLock lock(s_mutex); } - - void* result = NULL; - try { - // go - LOG((CLOG_DEBUG1 "thread %p entry", this)); - m_job->run(); - LOG((CLOG_DEBUG1 "thread %p exit", this)); - } - - catch (XThreadCancel&) { - // client called cancel() - LOG((CLOG_DEBUG1 "caught cancel on thread %p", this)); - } - - catch (XThreadExit& e) { - // client called exit() - result = e.m_result; - LOG((CLOG_DEBUG1 "caught exit on thread %p", this)); - } - catch (...) { - LOG((CLOG_DEBUG1 "exception on thread %p", this)); - // note -- don't catch (...) to avoid masking bugs - delete m_job; - throw; - } - - // done with job - delete m_job; - - // store exit result (no lock necessary because the result will - // not be accessed until m_exit is set) - m_result = result; -} - -#if HAVE_PTHREAD - -#include "CStopwatch.h" -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif -#if !HAVE_NANOSLEEP -# if HAVE_SYS_SELECT_H -# include -# endif -# if HAVE_SYS_TYPES_H -# include -# endif -# if HAVE_UNISTD_H -# include -# endif -#endif - -void -CThreadRep::init() -{ - m_result = NULL; - m_cancellable = true; - m_cancelling = false; - m_cancel = false; - m_exit = false; -} - -void -CThreadRep::fini() -{ - // main thread has NULL job - if (m_job != NULL) { - pthread_detach(m_thread); - } -} - -void -CThreadRep::sleep( - double timeout) -{ - if (timeout < 0.0) { - return; - } -#if HAVE_NANOSLEEP - struct timespec t; - t.tv_sec = (long)timeout; - t.tv_nsec = (long)(1000000000.0 * (timeout - (double)t.tv_sec)); - while (nanosleep(&t, &t) < 0) - testCancel(); -#else - /* emulate nanosleep() with select() */ - CStopwatch timer(true); - double timeLeft = timeout - timer.getTime(); - while (timeLeft > 0.0) { - struct timeval timeout2; - timeout2.tv_sec = static_cast(timeLeft); - timeout2.tv_usec = static_cast(1.0e+6 * (timeLeft - - timeout2.tv_sec)); - select((SELECT_TYPE_ARG1) 0, - SELECT_TYPE_ARG234 NULL, - SELECT_TYPE_ARG234 NULL, - SELECT_TYPE_ARG234 NULL, - SELECT_TYPE_ARG5 &timeout2); - testCancel(); - timeLeft = timeout - timer.getTime(); - } -#endif -} - -void -CThreadRep::cancel() -{ - CLock lock(s_mutex); - if (!m_exit && m_cancellable && !m_cancelling) { - m_cancel = true; - } - else { - return; - } - - // break out of system calls - LOG((CLOG_DEBUG1 "cancel thread %p", this)); - pthread_kill(m_thread, SIGWAKEUP); -} - -void -CThreadRep::testCancel() -{ - { - CLock lock(s_mutex); - - // done if not cancelled, not cancellable, or already cancelling - if (!m_cancel || !m_cancellable || m_cancelling) { - return; - } - - // update state for cancel - m_cancel = false; - m_cancelling = true; - } - - // start cancel - LOG((CLOG_DEBUG1 "throw cancel on thread %p", this)); - throw XThreadCancel(); -} - -bool -CThreadRep::wait(CThreadRep* target, double timeout) -{ - if (target == this) { - return false; - } - - testCancel(); - if (target->isExited()) { - return true; - } - - if (timeout != 0.0) { - CStopwatch timer; - do { - sleep(0.05); - testCancel(); - if (target->isExited()) { - return true; - } - } while (timeout < 0.0 || timer.getTime() <= timeout); - } - - return false; -} - -void -CThreadRep::setPriority(int) -{ - // FIXME -} - -bool -CThreadRep::isExited() const -{ - CLock lock(s_mutex); - return m_exit; -} - -void* -CThreadRep::threadFunc(void* arg) -{ - CThreadRep* rep = (CThreadRep*)arg; - - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); - pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); - - // run thread - rep->doThreadFunc(); - - { - // mark as terminated - CLock lock(s_mutex); - rep->m_exit = true; - } - - // unref thread - rep->unref(); - - // terminate the thread - return NULL; -} - -void -CThreadRep::threadCancel(int) -{ - // do nothing -} - -void* -CThreadRep::threadSignalHandler(void* vrep) -{ - CThreadRep* mainThreadRep = reinterpret_cast(vrep); - - // detach - pthread_detach(pthread_self()); - - // add signal to mask - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - - // also wait on SIGABRT. on linux (others?) this thread (process) - // will persist after all the other threads evaporate due to an - // assert unless we wait on SIGABRT. that means our resources (like - // the socket we're listening on) are not released and never will be - // until the lingering thread is killed. i don't know why sigwait() - // should protect the thread from being killed. note that sigwait() - // doesn't actually return if we receive SIGABRT and, for some - // reason, we don't have to block SIGABRT. - sigaddset(&sigset, SIGABRT); - - // we exit the loop via thread cancellation in sigwait() - for (;;) { - // wait -#if HAVE_POSIX_SIGWAIT - int signal; - sigwait(&sigset, &signal); -#else - sigwait(&sigset); -#endif - - // if we get here then the signal was raised. cancel the thread. - mainThreadRep->cancel(); - } -} - -#endif // HAVE_PTHREAD - -#if WINDOWS_LIKE - -void -CThreadRep::init() -{ - m_result = NULL; - m_cancellable = true; - m_cancelling = false; - m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); - m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); -} - -void -CThreadRep::fini() -{ - // destroy the events - CloseHandle(m_cancel); - CloseHandle(m_exit); - - // close the handle (main thread has a NULL handle) - if (m_thread != NULL) { - CloseHandle(m_thread); - } -} - -void -CThreadRep::sleep( - double timeout) -{ - if (isCancellable()) { - WaitForSingleObject(m_cancel, (DWORD)(1000.0 * timeout)); - } - else { - Sleep((DWORD)(1000.0 * timeout)); - } -} - -void -CThreadRep::cancel() -{ - LOG((CLOG_DEBUG1 "cancel thread %p", this)); - SetEvent(m_cancel); -} - -void -CThreadRep::testCancel() -{ - // poll cancel event. return if not set. - const DWORD result = WaitForSingleObject(getCancelEvent(), 0); - if (result != WAIT_OBJECT_0) { - return; - } - - { - // ignore if disabled or already cancelling - CLock lock(s_mutex); - if (!m_cancellable || m_cancelling) - return; - - // update state for cancel - m_cancelling = true; - ResetEvent(m_cancel); - } - - // start cancel - LOG((CLOG_DEBUG1 "throw cancel on thread %p", this)); - throw XThreadCancel(); -} - -bool -CThreadRep::wait(CThreadRep* target, double timeout) -{ - // get the current thread. if it's the same as the target thread - // then the thread is waiting on itself. - CThreadPtr currentRep(CThreadRep::getCurrentThreadRep()); - if (target == this) { - return false; - } - - // is cancellation enabled? - const DWORD n = (isCancellable() ? 2 : 1); - - // convert timeout - DWORD t; - if (timeout < 0.0) { - t = INFINITE; - } - else { - t = (DWORD)(1000.0 * timeout); - } - - // wait for this thread to be cancelled or for the target thread to - // terminate. - HANDLE handles[2]; - handles[0] = target->getExitEvent(); - handles[1] = m_cancel; - DWORD result = WaitForMultipleObjects(n, handles, FALSE, t); - - // cancel takes priority - if (n == 2 && result != WAIT_OBJECT_0 + 1 && - WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) { - result = WAIT_OBJECT_0 + 1; - } - - // handle result - switch (result) { - case WAIT_OBJECT_0 + 0: - // target thread terminated - return true; - - case WAIT_OBJECT_0 + 1: - // this thread was cancelled. does not return. - testCancel(); - - default: - // timeout or error - return false; - } -} - -bool -CThreadRep::waitForEvent(double timeout) -{ - // check if messages are available first. if we don't do this then - // MsgWaitForMultipleObjects() will block even if the queue isn't - // empty if the messages in the queue were there before the last - // call to GetMessage()/PeekMessage(). - if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { - return true; - } - - // is cancellation enabled? - const DWORD n = (isCancellable() ? 1 : 0); - - // convert timeout - DWORD t; - if (timeout < 0.0) { - t = INFINITE; - } - else { - t = (DWORD)(1000.0 * timeout); - } - - // wait for this thread to be cancelled or for the target thread to - // terminate. - HANDLE handles[1]; - handles[0] = m_cancel; - DWORD result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT); - - // handle result - switch (result) { - case WAIT_OBJECT_0 + 1: - // message is available - return true; - - case WAIT_OBJECT_0 + 0: - // this thread was cancelled. does not return. - testCancel(); - - default: - // timeout or error - return false; - } -} - -void -CThreadRep::setPriority(int n) -{ - DWORD pClass = NORMAL_PRIORITY_CLASS; - if (n < 0) { - switch (-n) { - case 1: n = THREAD_PRIORITY_ABOVE_NORMAL; break; - case 2: n = THREAD_PRIORITY_HIGHEST; break; - default: - pClass = HIGH_PRIORITY_CLASS; - switch (-n - 3) { - case 0: n = THREAD_PRIORITY_LOWEST; break; - case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; - case 2: n = THREAD_PRIORITY_NORMAL; break; - case 3: n = THREAD_PRIORITY_ABOVE_NORMAL; break; - default: n = THREAD_PRIORITY_HIGHEST; break; - } - break; - } - } - else { - switch (n) { - case 0: n = THREAD_PRIORITY_NORMAL; break; - case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; - case 2: n = THREAD_PRIORITY_LOWEST; break; - default: n = THREAD_PRIORITY_IDLE; break; - } - } - SetPriorityClass(m_thread, pClass); - SetThreadPriority(m_thread, n); -} - -HANDLE -CThreadRep::getExitEvent() const -{ - // no lock necessary because the value never changes - return m_exit; -} - -HANDLE -CThreadRep::getCancelEvent() const -{ - // no lock necessary because the value never changes - return m_cancel; -} - -unsigned int __stdcall -CThreadRep::threadFunc(void* arg) -{ - CThreadRep* rep = (CThreadRep*)arg; - - // run thread - rep->doThreadFunc(); - - // signal termination - SetEvent(rep->m_exit); - - // unref thread - rep->unref(); - - // terminate the thread - return 0; -} - -#endif // WINDOWS_LIKE diff --git a/lib/mt/CThreadRep.h b/lib/mt/CThreadRep.h deleted file mode 100644 index 13ea27be..00000000 --- a/lib/mt/CThreadRep.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CTHREADREP_H -#define CTHREADREP_H - -#include "common.h" - -#if HAVE_PTHREAD -#include -#elif WINDOWS_LIKE -#define WIN32_LEAN_AND_MEAN -#include -#endif - -class CMutex; -class IJob; - -//! Internal thread class; do not use directly -class CThreadRep { -public: - CThreadRep(IJob*, void* userData); - - // manipulators - - // initialize the thread library - static void initThreads(); - - // change ref count - void ref(); - void unref(); - - // the calling thread sleeps for t seconds. if t == 0.0 then - // the thread yields the CPU. - void sleep(double timeout); - - // cancel the thread - void cancel(); - - // set cancellation state - bool enableCancel(bool enable); - - // permanently disable further cancellation and start cancel cleanup - // if cancel has been called and cancellation hasn't been started yet. - void testCancel(); - - // wait for thread to exit or for current thread to cancel - bool wait(CThreadRep*, double timeout); - -#if WINDOWS_LIKE - // wait for a message on the queue - bool waitForEvent(double timeout); -#endif - - // set the priority - void setPriority(int n); - - // accessors - - // get the exit result for this thread. thread must be terminated. - void* getResult() const; - - // get the user data passed to the constructor - void* getUserData() const; - - // get the current cancellable state - bool isCancellable() const; - -#if HAVE_PTHREAD - bool isExited() const; -#elif WINDOWS_LIKE - HANDLE getExitEvent() const; - HANDLE getCancelEvent() const; -#endif - - // return the thread rep for the calling thread. the returned - // rep has been ref()'d. - static CThreadRep* getCurrentThreadRep(); - -protected: - virtual ~CThreadRep(); - -private: - // internal constructor - CThreadRep(); - - // initialization/cleanup - void init(); - void fini(); - - // thread rep lookup - static CThreadRep* find(); - - // thread functions -#if HAVE_PTHREAD - static void* threadFunc(void* arg); - static void threadCancel(int); - static void* threadSignalHandler(void*); -#elif WINDOWS_LIKE - static unsigned int __stdcall threadFunc(void* arg); -#endif - void doThreadFunc(); - - // not implemented - CThreadRep(const CThreadRep&); - CThreadRep& operator=(const CThreadRep&); - -private: - static CMutex* s_mutex; - static CThreadRep* s_head; - - CThreadRep* m_prev; - CThreadRep* m_next; - - int m_refCount; - IJob* m_job; - void* m_userData; - void* m_result; - bool m_cancellable; - bool m_cancelling; - -#if HAVE_PTHREAD - pthread_t m_thread; - bool m_exit; - bool m_cancel; - static pthread_t s_signalThread; -#endif - -#if WINDOWS_LIKE - HANDLE m_thread; - DWORD m_id; - HANDLE m_exit; - HANDLE m_cancel; -#endif -}; - -// -// CThreadPtr -- auto unref'ing pointer to thread rep -// - -class CThreadPtr { -public: - CThreadPtr(CThreadRep* rep) : m_rep(rep) { } - ~CThreadPtr() { m_rep->unref(); } - - CThreadRep* operator->() const { return m_rep; } - -private: - // not implemented - CThreadPtr(const CThreadPtr&); - CThreadPtr& operator=(const CThreadPtr&); - -private: - CThreadRep* m_rep; -}; - -#endif diff --git a/lib/mt/CTimerThread.cpp b/lib/mt/CTimerThread.cpp index 91355bc7..963b5799 100644 --- a/lib/mt/CTimerThread.cpp +++ b/lib/mt/CTimerThread.cpp @@ -16,6 +16,7 @@ #include "CThread.h" #include "TMethodJob.h" #include "CLog.h" +#include "CArch.h" // // CTimerThread @@ -50,7 +51,7 @@ void CTimerThread::timer(void*) { LOG((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); - CThread::sleep(m_timeout); + ARCH->sleep(m_timeout); LOG((CLOG_DEBUG1 "timeout")); m_callingThread->cancel(); } diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index baf32d7e..6eda91d6 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -29,18 +29,18 @@ libmt_a_SOURCES = \ CMutex.cpp \ CCondVar.cpp \ CThread.cpp \ - CThreadRep.cpp \ CTimerThread.cpp \ XMT.cpp \ CCondVar.h \ CLock.h \ CMutex.h \ CThread.h \ - CThreadRep.h \ CTimerThread.h \ XMT.h \ XThread.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ $(NULL) diff --git a/lib/mt/XMT.h b/lib/mt/XMT.h index 32ca9aaa..f66428ea 100644 --- a/lib/mt/XMT.h +++ b/lib/mt/XMT.h @@ -18,16 +18,12 @@ #include "XBase.h" //! Generic multithreading exception -class XMT : public XBase { }; +XBASE_SUBCLASS(XMT, XBase); //! Thread creation exception /*! Thrown when a thread cannot be created. */ -class XMTThreadUnavailable : public XMT { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_WHAT(XMTThreadUnavailable, XMT); #endif diff --git a/lib/mt/XThread.h b/lib/mt/XThread.h index b76c9933..30dff1da 100644 --- a/lib/mt/XThread.h +++ b/lib/mt/XThread.h @@ -15,13 +15,7 @@ #ifndef XTHREAD_H #define XTHREAD_H -//! Generic thread exception -/*! -Exceptions derived from this class are used by the multithreading -library to perform stack unwinding when a thread terminates. These -exceptions must always be rethrown by clients when caught. -*/ -class XThread { }; +#include "XArch.h" //! Thread exception to exit /*! @@ -39,20 +33,4 @@ public: void* m_result; }; -//! Thread exception to cancel -/*! -Thrown to cancel a thread. Clients must not throw this type, but -must rethrow it if caught (by XThreadCancel, XThread, or ...). -*/ -class XThreadCancel : public XThread { }; - -/*! -\def RETHROW_XTHREAD -Convenience macro to rethrow an XThread exception but ignore other -exceptions. Put this in your catch (...) handler after necessary -cleanup but before leaving or returning from the handler. -*/ -#define RETHROW_XTHREAD \ - try { throw; } catch (XThread&) { throw; } catch (...) { } - #endif diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp index 2cfdc7a1..f926fc1d 100644 --- a/lib/mt/mt.dsp +++ b/lib/mt/mt.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /W4 /GX /O2 /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -103,10 +103,6 @@ SOURCE=.\CThread.cpp # End Source File # Begin Source File -SOURCE=.\CThreadRep.cpp -# End Source File -# Begin Source File - SOURCE=.\CTimerThread.cpp # End Source File # Begin Source File @@ -135,10 +131,6 @@ SOURCE=.\CThread.h # End Source File # Begin Source File -SOURCE=.\CThreadRep.h -# End Source File -# Begin Source File - SOURCE=.\CTimerThread.h # End Source File # Begin Source File diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp deleted file mode 100644 index e473b933..00000000 --- a/lib/net/CNetwork.cpp +++ /dev/null @@ -1,1026 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CNetwork.h" -#include "XNetwork.h" -#include "CLog.h" -#include - -// -// CNetwork -// - -#if WINDOWS_LIKE - -static CNetwork::Socket (PASCAL FAR *accept_winsock)(CNetwork::Socket s, CNetwork::Address FAR *addr, CNetwork::AddressLength FAR *addrlen); -static int (PASCAL FAR *bind_winsock)(CNetwork::Socket s, const CNetwork::Address FAR *addr, CNetwork::AddressLength namelen); -static int (PASCAL FAR *close_winsock)(CNetwork::Socket s); -static int (PASCAL FAR *connect_winsock)(CNetwork::Socket s, const CNetwork::Address FAR *name, CNetwork::AddressLength namelen); -static int (PASCAL FAR *gethostname_winsock)(char FAR * name, int namelen); -static int (PASCAL FAR *getpeername_winsock)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); -static int (PASCAL FAR *getsockerror_winsock)(void); -static int (PASCAL FAR *getsockname_winsock)(CNetwork::Socket s, CNetwork::Address FAR *name, CNetwork::AddressLength FAR * namelen); -static int (PASCAL FAR *getsockopt_winsock)(CNetwork::Socket s, int level, int optname, void FAR * optval, CNetwork::AddressLength FAR *optlen); -static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in); -static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp); -static int (PASCAL FAR *ioctl_winsock)(CNetwork::Socket s, int cmd, void FAR *); -static int (PASCAL FAR *listen_winsock)(CNetwork::Socket s, int backlog); -static ssize_t (PASCAL FAR *recv_winsock)(CNetwork::Socket s, void FAR * buf, size_t len, int flags); -static ssize_t (PASCAL FAR *recvfrom_winsock)(CNetwork::Socket s, void FAR * buf, size_t len, int flags, CNetwork::Address FAR *from, CNetwork::AddressLength FAR * fromlen); -static int (PASCAL FAR *select_winsock)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); -static ssize_t (PASCAL FAR *send_winsock)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags); -static ssize_t (PASCAL FAR *sendto_winsock)(CNetwork::Socket s, const void FAR * buf, size_t len, int flags, const CNetwork::Address FAR *to, CNetwork::AddressLength tolen); -static int (PASCAL FAR *setsockopt_winsock)(CNetwork::Socket s, int level, int optname, const void FAR * optval, CNetwork::AddressLength optlen); -static int (PASCAL FAR *shutdown_winsock)(CNetwork::Socket s, int how); -static CNetwork::Socket (PASCAL FAR *socket_winsock)(int af, int type, int protocol); -static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type); -static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); -static struct servent FAR * (PASCAL FAR *getservbyport_winsock)(int port, const char FAR * proto); -static struct servent FAR * (PASCAL FAR *getservbyname_winsock)(const char FAR * name, const char FAR * proto); -static struct protoent FAR * (PASCAL FAR *getprotobynumber_winsock)(int proto); -static struct protoent FAR * (PASCAL FAR *getprotobyname_winsock)(const char FAR * name); -static int (PASCAL FAR *WSACleanup_winsock)(void); -static int (PASCAL FAR *WSAFDIsSet_winsock)(CNetwork::Socket, fd_set FAR *); - -const int CNetwork::Error = SOCKET_ERROR; -const CNetwork::Socket CNetwork::Null = INVALID_SOCKET; - -#undef FD_ISSET -#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) - -// have poll() pick up our select() pointer -#define select select_winsock - -static HMODULE s_networkModule = NULL; - -static -FARPROC -netGetProcAddress(HMODULE module, LPCSTR name) -{ - FARPROC func = ::GetProcAddress(module, name); - if (!func) { - throw XNetworkFunctionUnavailable(name); - } - return func; -} - -void -CNetwork::init() -{ - assert(WSACleanup_winsock == NULL); - assert(s_networkModule == NULL); - - // try winsock 2 - HMODULE module = (HMODULE)::LoadLibrary("ws2_32.dll"); - if (module == NULL) { - LOG((CLOG_NOTE "ws2_32.dll not found")); - } - else { - try { - init2(module); - return; - } - catch (XNetwork& e) { - LOG((CLOG_NOTE "ws2_32.dll error: %s", e.what())); - } - } - - // try winsock 1 - module = (HMODULE)::LoadLibrary("wsock32.dll"); - if (module == NULL) { - LOG((CLOG_NOTE "wsock32.dll not found")); - } - else { - try { - init2(module); - return; - } - catch (XNetwork& e) { - LOG((CLOG_NOTE "wsock32.dll error: %s", e.what())); - } - } - - // no networking - throw XNetworkUnavailable(); -} - -void -CNetwork::cleanup() -{ - if (s_networkModule != NULL) { - WSACleanup_winsock(); - ::FreeLibrary(s_networkModule); - - WSACleanup_winsock = NULL; - s_networkModule = NULL; - } -} - -UInt32 -CNetwork::swaphtonl(UInt32 v) -{ - static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; - if (s_endian.b[0] == 0x34) { - return ((v & 0xff000000lu) >> 24) | - ((v & 0x00ff0000lu) >> 8) | - ((v & 0x0000ff00lu) << 8) | - ((v & 0x000000fflu) << 24); - } - else { - return v; - } -} - -UInt16 -CNetwork::swaphtons(UInt16 v) -{ - static const union { UInt16 s; UInt8 b[2]; } s_endian = { 0x1234 }; - if (s_endian.b[0] == 0x34) { - return static_cast( ((v & 0xff00u) >> 8) | - ((v & 0x00ffu) << 8)); - } - else { - return v; - } -} - -UInt32 -CNetwork::swapntohl(UInt32 v) -{ - return swaphtonl(v); -} - -UInt16 -CNetwork::swapntohs(UInt16 v) -{ - return swaphtons(v); -} - -#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name) - -void -CNetwork::init2( - HMODULE module) -{ - assert(module != NULL); - - // get startup function address - int (PASCAL FAR *startup)(WORD, LPWSADATA); - setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA)); - - // startup network library - WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); - WSADATA data; - int err = startup(version, &data); - if (data.wVersion != version) { - throw XNetworkVersion(LOBYTE(data.wVersion), HIBYTE(data.wVersion)); - } - if (err != 0) { - throw XNetworkFailed(); - } - - // get function addresses - setfunc(accept_winsock, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); - setfunc(bind_winsock, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); - setfunc(close_winsock, closesocket, int (PASCAL FAR *)(Socket s)); - setfunc(connect_winsock, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); - setfunc(gethostname_winsock, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); - setfunc(getpeername_winsock, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockerror_winsock, WSAGetLastError, int (PASCAL FAR *)(void)); - setfunc(getsockname_winsock, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockopt_winsock, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); - setfunc(inet_addr_winsock, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); - setfunc(inet_ntoa_winsock, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); - setfunc(ioctl_winsock, ioctlsocket, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); - setfunc(listen_winsock, listen, int (PASCAL FAR *)(Socket s, int backlog)); - setfunc(recv_winsock, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); - setfunc(recvfrom_winsock, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); - setfunc(select_winsock, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout)); - setfunc(send_winsock, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); - setfunc(sendto_winsock, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); - setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); - setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(Socket s, int how)); - setfunc(socket_winsock, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); - setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); - setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(getservbyport_winsock, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); - setfunc(getservbyname_winsock, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); - setfunc(getprotobynumber_winsock, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); - setfunc(getprotobyname_winsock, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void)); - setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(CNetwork::Socket, fd_set FAR *)); - - s_networkModule = module; -} - -CNetwork::Socket -CNetwork::accept(Socket s, Address* addr, AddressLength* addrlen) -{ - return accept_winsock(s, addr, addrlen); -} - -int -CNetwork::bind(Socket s, const Address* addr, AddressLength namelen) -{ - return bind_winsock(s, addr, namelen); -} - -int -CNetwork::close(Socket s) -{ - return close_winsock(s); -} - -int -CNetwork::connect(Socket s, const Address* name, AddressLength namelen) -{ - return connect_winsock(s, name, namelen); -} - -int -CNetwork::gethostname(char* name, int namelen) -{ - return gethostname_winsock(name, namelen); -} - -int -CNetwork::getpeername(Socket s, Address* name, AddressLength* namelen) -{ - return getpeername_winsock(s, name, namelen); -} - -int -CNetwork::getsockerror(void) -{ - return getsockerror_winsock(); -} - -int -CNetwork::getsockname(Socket s, Address* name, AddressLength* namelen) -{ - return getsockname_winsock(s, name, namelen); -} - -int -CNetwork::getsockopt(Socket s, int level, int optname, void* optval, AddressLength* optlen) -{ - return getsockopt_winsock(s, level, optname, optval, optlen); -} - -int -CNetwork::inet_aton(const char* cp, InternetAddress* addr) -{ - assert(addr != NULL); - - // fake it with inet_addr, which is per-thread on winsock - unsigned long inetAddr = inet_addr_winsock(cp); - if (inetAddr == INADDR_NONE) { - return 0; - } - else { - addr->s_addr = inetAddr; - return 1; - } -} - -CString -CNetwork::inet_ntoa(struct in_addr in) -{ - // winsock returns strings per-thread - return CString(inet_ntoa_winsock(in)); -} - -int -CNetwork::ioctl(Socket s, int cmd, void* arg) -{ - return ioctl_winsock(s, cmd, arg); -} - -int -CNetwork::listen(Socket s, int backlog) -{ - return listen_winsock(s, backlog); -} - -ssize_t -CNetwork::read(Socket s, void* buf, size_t len) -{ - return recv_winsock(s, buf, len, 0); -} - -ssize_t -CNetwork::recv(Socket s, void* buf, size_t len, int flags) -{ - return recv_winsock(s, buf, len, flags); -} - -ssize_t -CNetwork::recvfrom(Socket s, void* buf, size_t len, int flags, Address* from, AddressLength* fromlen) -{ - return recvfrom_winsock(s, buf, len, flags, from, fromlen); -} - -ssize_t -CNetwork::send(Socket s, const void* buf, size_t len, int flags) -{ - return send_winsock(s, buf, len, flags); -} - -ssize_t -CNetwork::sendto(Socket s, const void* buf, size_t len, int flags, const Address* to, AddressLength tolen) -{ - return sendto_winsock(s, buf, len, flags, to, tolen); -} - -int -CNetwork::setsockopt(Socket s, int level, int optname, const void* optval, AddressLength optlen) -{ - return setsockopt_winsock(s, level, optname, optval, optlen); -} - -int -CNetwork::shutdown(Socket s, int how) -{ - return shutdown_winsock(s, how); -} - -CNetwork::Socket -CNetwork::socket(int af, int type, int protocol) -{ - return socket_winsock(af, type, protocol); -} - -ssize_t -CNetwork::write(Socket s, const void* buf, size_t len) -{ - return send_winsock(s, buf, len, 0); -} - -int -CNetwork::gethostbyaddr(CHostInfo* hostinfo, const char* addr, int len, int type) -{ - assert(hostinfo != NULL); - - // winsock returns structures per-thread - struct hostent FAR* info = gethostbyaddr_winsock(addr, len, type); - if (info == NULL) { - return getsockerror(); - } - else { - CHostInfo tmp(info); - hostinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::gethostbyname(CHostInfo* hostinfo, const char* name) -{ - assert(hostinfo != NULL); - - // winsock returns structures per-thread - struct hostent FAR* info = gethostbyname_winsock(name); - if (info == NULL) { - return getsockerror(); - } - else { - CHostInfo tmp(info); - hostinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getservbyport(CServiceInfo* servinfo, int port, const char* proto) -{ - assert(servinfo != NULL); - - // winsock returns structures per-thread - struct servent FAR* info = getservbyport_winsock(port, proto); - if (info == NULL) { - return getsockerror(); - } - else { - CServiceInfo tmp(info); - servinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getservbyname(CServiceInfo* servinfo, const char* name, const char* proto) -{ - assert(servinfo != NULL); - - // winsock returns structures per-thread - struct servent FAR* info = getservbyname_winsock(name, proto); - if (info == NULL) { - return getsockerror(); - } - else { - CServiceInfo tmp(info); - servinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getprotobynumber(CProtocolInfo* protoinfo, int proto) -{ - assert(protoinfo != NULL); - - // winsock returns structures per-thread - struct protoent FAR* info = getprotobynumber_winsock(proto); - if (info == NULL) { - return getsockerror(); - } - else { - CProtocolInfo tmp(info); - protoinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getprotobyname(CProtocolInfo* protoinfo, const char* name) -{ - assert(protoinfo != NULL); - - // winsock returns structures per-thread - struct protoent FAR* info = getprotobyname_winsock(name); - if (info == NULL) { - return getsockerror(); - } - else { - CProtocolInfo tmp(info); - protoinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::setblocking(Socket s, bool blocking) -{ - int flag = blocking ? 0 : 1; - return ioctl(s, FIONBIO, &flag); -} - -int -CNetwork::setnodelay(Socket s, bool nodelay) -{ - BOOL flag = nodelay ? 1 : 0; - return setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); -} - -#endif - -#if UNIX_LIKE - -#include "CMutex.h" -#include "CLock.h" - -#if HAVE_UNISTD_H -# include -#endif -#include -#include -#include -#if !defined(TCP_NODELAY) -# include -#endif - -const int CNetwork::Error = -1; -const CNetwork::Socket CNetwork::Null = -1; - -static CMutex* s_networkMutex = NULL; - -#define setfunc(var, name, type) var = (type)::name - -UInt32 -CNetwork::swaphtonl(UInt32 v) -{ - return htonl(v); -} - -UInt16 -CNetwork::swaphtons(UInt16 v) -{ - return htons(v); -} - -UInt32 -CNetwork::swapntohl(UInt32 v) -{ - return ntohl(v); -} - -UInt16 -CNetwork::swapntohs(UInt16 v) -{ - return ntohs(v); -} - -void -CNetwork::init() -{ - assert(s_networkMutex == NULL); - - try { - s_networkMutex = new CMutex; - } - catch (...) { - throw XNetworkFailed(); - } -} - -void -CNetwork::cleanup() -{ - delete s_networkMutex; - s_networkMutex = NULL; -} - -CNetwork::Socket -CNetwork::accept(Socket s, Address* addr, AddressLength* addrlen) -{ - return ::accept(s, addr, addrlen); -} - -int -CNetwork::bind(Socket s, const Address* addr, AddressLength namelen) -{ - return ::bind(s, addr, namelen); -} - -int -CNetwork::close(Socket s) -{ - return ::close(s); -} - -int -CNetwork::connect(Socket s, const Address* name, AddressLength namelen) -{ - return ::connect(s, name, namelen); -} - -int -CNetwork::gethostname(char* name, int namelen) -{ - return ::gethostname(name, namelen); -} - -int -CNetwork::getpeername(Socket s, Address* name, AddressLength* namelen) -{ - return ::getpeername(s, name, namelen); -} - -int -CNetwork::getsockerror(void) -{ - return errno; -} - -int -CNetwork::getsockname(Socket s, Address* name, AddressLength* namelen) -{ - return ::getsockname(s, name, namelen); -} - -int -CNetwork::getsockopt(Socket s, int level, int optname, void* optval, AddressLength* optlen) -{ - return ::getsockopt(s, level, optname, optval, optlen); -} - -int -CNetwork::inet_aton(const char* cp, InternetAddress* addr) -{ - return ::inet_aton(cp, addr); -} - -CString -CNetwork::inet_ntoa(struct in_addr in) -{ - // single threaded access to inet_ntoa functions - CLock lock(s_networkMutex); - return CString(::inet_ntoa(in)); -} - -int -CNetwork::ioctl(Socket s, int cmd, void* arg) -{ - return ::ioctl(s, cmd, arg); -} - -int -CNetwork::listen(Socket s, int backlog) -{ - return ::listen(s, backlog); -} - -ssize_t -CNetwork::read(Socket s, void* buf, size_t len) -{ - return ::read(s, buf, len); -} - -ssize_t -CNetwork::recv(Socket s, void* buf, size_t len, int flags) -{ - return ::recv(s, buf, len, flags); -} - -ssize_t -CNetwork::recvfrom(Socket s, void* buf, size_t len, int flags, Address* from, AddressLength* fromlen) -{ - return ::recvfrom(s, buf, len, flags, from, fromlen); -} - -ssize_t -CNetwork::send(Socket s, const void* buf, size_t len, int flags) -{ - return ::send(s, buf, len, flags); -} - -ssize_t -CNetwork::sendto(Socket s, const void* buf, size_t len, int flags, const Address* to, AddressLength tolen) -{ - return ::sendto(s, buf, len, flags, to, tolen); -} - -int -CNetwork::setsockopt(Socket s, int level, int optname, const void* optval, AddressLength optlen) -{ - return ::setsockopt(s, level, optname, optval, optlen); -} - -int -CNetwork::shutdown(Socket s, int how) -{ - return ::shutdown(s, how); -} - -CNetwork::Socket -CNetwork::socket(int af, int type, int protocol) -{ - return ::socket(af, type, protocol); -} - -ssize_t -CNetwork::write(Socket s, const void* buf, size_t len) -{ - return ::write(s, buf, len); -} - -int -CNetwork::gethostbyaddr(CHostInfo* hostinfo, const char* addr, int len, int type) -{ - assert(hostinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct hostent* info = ::gethostbyaddr(addr, len, type); - if (info == NULL) { - return h_errno; - } - else { - CHostInfo tmp(info); - hostinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::gethostbyname(CHostInfo* hostinfo, const char* name) -{ - assert(hostinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct hostent* info = ::gethostbyname(name); - if (info == NULL) { - return h_errno; - } - else { - CHostInfo tmp(info); - hostinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getservbyport(CServiceInfo* servinfo, int port, const char* proto) -{ - assert(servinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct servent* info = ::getservbyport(port, proto); - if (info == NULL) { - return -1; - } - else { - CServiceInfo tmp(info); - servinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getservbyname(CServiceInfo* servinfo, const char* name, const char* proto) -{ - assert(servinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct servent* info = ::getservbyname(name, proto); - if (info == NULL) { - return -1; - } - else { - CServiceInfo tmp(info); - servinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getprotobynumber(CProtocolInfo* protoinfo, int proto) -{ - assert(protoinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct protoent* info = ::getprotobynumber(proto); - if (info == NULL) { - return -1; - } - else { - CProtocolInfo tmp(info); - protoinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::getprotobyname(CProtocolInfo* protoinfo, const char* name) -{ - assert(protoinfo != NULL); - - // single threaded access to netdb functions - CLock lock(s_networkMutex); - struct protoent* info = ::getprotobyname(name); - if (info == NULL) { - return -1; - } - else { - CProtocolInfo tmp(info); - protoinfo->swap(tmp); - return 0; - } -} - -int -CNetwork::setblocking(CNetwork::Socket s, bool blocking) -{ - int mode = fcntl(s, F_GETFL, 0); - if (mode == -1) { - return -1; - } - if (blocking) { - mode &= ~O_NDELAY; - } - else { - mode |= O_NDELAY; - } - if (fcntl(s, F_SETFL, mode) < 0) { - return -1; - } - return 0; -} - -int -CNetwork::setnodelay(CNetwork::Socket s, bool nodelay) -{ - int flag = nodelay ? 1 : 0; - return setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); -} - -#endif - -#if HAVE_POLL - -int -CNetwork::poll(PollEntry fd[], int nfds, int timeout) -{ - return ::poll(fd, nfds, timeout); -} - -#else // !HAVE_POLL - -#if HAVE_SYS_SELECT_H && !WINDOWS_LIKE -# include -#endif -#if HAVE_SYS_TIME_H -# include -#endif -#if HAVE_SYS_TYPES_H -# include -#endif -#if HAVE_UNISTD_H -# include -#endif - -int -CNetwork::poll(PollEntry fd[], int nfds, int timeout) -{ - int i; - - // prepare sets for select - int n = 0; - fd_set readSet, writeSet, errSet; - fd_set* readSetP = NULL; - fd_set* writeSetP = NULL; - fd_set* errSetP = NULL; - FD_ZERO(&readSet); - FD_ZERO(&writeSet); - FD_ZERO(&errSet); - for (i = 0; i < nfds; ++i) { - int fdi = static_cast(fd[i].fd); - if (fd[i].events & kPOLLIN) { - FD_SET(fd[i].fd, &readSet); - readSetP = &readSet; - if (fdi > n) { - n = fdi; - } - } - if (fd[i].events & kPOLLOUT) { - FD_SET(fd[i].fd, &writeSet); - writeSetP = &writeSet; - if (fdi > n) { - n = fdi; - } - } - if (true) { - FD_SET(fd[i].fd, &errSet); - errSetP = &errSet; - if (fdi > n) { - n = fdi; - } - } - } - - // prepare timeout for select - struct timeval timeout2; - struct timeval* timeout2P; - if (timeout < 0) { - timeout2P = NULL; - } - else { - timeout2P = &timeout2; - timeout2.tv_sec = timeout / 1000; - timeout2.tv_usec = 1000 * (timeout % 1000); - } - - // do the select. note that winsock ignores the first argument. - n = select((SELECT_TYPE_ARG1) n + 1, - SELECT_TYPE_ARG234 readSetP, - SELECT_TYPE_ARG234 writeSetP, - SELECT_TYPE_ARG234 errSetP, - SELECT_TYPE_ARG5 timeout2P); - - // handle results - if (n == Error) { - return Error; - } - if (n == 0) { - return 0; - } - n = 0; - for (i = 0; i < nfds; ++i) { - fd[i].revents = 0; - if (FD_ISSET(fd[i].fd, &readSet)) { - fd[i].revents |= kPOLLIN; - } - if (FD_ISSET(fd[i].fd, &writeSet)) { - fd[i].revents |= kPOLLOUT; - } - if (FD_ISSET(fd[i].fd, &errSet)) { - fd[i].revents |= kPOLLERR; - } - if (fd[i].revents != 0) { - ++n; - } - } - return n; -} - -#endif // !HAVE_POLL - - -// -// CNetwork::CHostInfo -// - -CNetwork::CHostInfo::CHostInfo(const struct hostent* hent) -{ - assert(hent != NULL); - - m_name = hent->h_name; - m_addressType = hent->h_addrtype; - m_addressLength = hent->h_length; - for (char** scan = hent->h_aliases; *scan != NULL; ++scan) { - m_aliases.push_back(*scan); - } - - // concatenate addresses together - UInt32 n = 0; - for (char** scan = hent->h_addr_list; *scan != NULL; ++scan) { - ++n; - } - m_addressData.reserve(n); - for (char** scan = hent->h_addr_list; *scan != NULL; ++scan) { - m_addressData.append(*scan, m_addressLength); - } - - // set pointers into concatenated data - const char* data = m_addressData.data(); - for (char** scan = hent->h_addr_list; *scan != NULL; ++scan) { - m_addresses.push_back(data); - data += m_addressLength; - } -} - -void -CNetwork::CHostInfo::swap(CHostInfo& v) -{ - std::swap(m_name, v.m_name); - std::swap(m_aliases, v.m_aliases); - std::swap(m_addressType, v.m_addressType); - std::swap(m_addressLength, v.m_addressLength); - std::swap(m_addresses, v.m_addresses); - std::swap(m_addressData, v.m_addressData); -} - - -// -// CNetwork::CServiceInfo -// - -CNetwork::CServiceInfo::CServiceInfo(const struct servent* sent) -{ - assert(sent != NULL); - - m_name = sent->s_name; - m_port = sent->s_port; - m_protocol = sent->s_proto; - for (char** scan = sent->s_aliases; *scan != NULL; ++scan) { - m_aliases.push_back(*scan); - } -} - -void -CNetwork::CServiceInfo::swap(CServiceInfo& v) -{ - std::swap(m_name, v.m_name); - std::swap(m_aliases, v.m_aliases); - std::swap(m_port, v.m_port); - std::swap(m_protocol, v.m_protocol); -} - - -// -// CNetwork::CProtocolInfo -// - -CNetwork::CProtocolInfo::CProtocolInfo(const struct protoent* pent) -{ - assert(pent != NULL); - - m_name = pent->p_name; - m_protocol = pent->p_proto; - for (char** scan = pent->p_aliases; *scan != NULL; ++scan) { - m_aliases.push_back(*scan); - } -} - -void -CNetwork::CProtocolInfo::swap(CProtocolInfo& v) -{ - std::swap(m_name, v.m_name); - std::swap(m_aliases, v.m_aliases); - std::swap(m_protocol, v.m_protocol); -} diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h deleted file mode 100644 index 80873a55..00000000 --- a/lib/net/CNetwork.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CNETWORK_H -#define CNETWORK_H - -#include "BasicTypes.h" -#include "CString.h" -#include "stdvector.h" - -#if !defined(HAVE_SOCKLEN_T) -// Darwin is so unsure what to use for socklen_t it makes us choose -# if defined(__APPLE__) -# if !defined(_BSD_SOCKLEN_T_) -# define _BSD_SOCKLEN_T_ int -# endif -# else -typedef int socklen_t; -# endif -#endif - -#if WINDOWS_LIKE - // declare no functions in winsock2 -# define INCL_WINSOCK_API_PROTOTYPES 0 -# define INCL_WINSOCK_API_TYPEDEFS 0 -# include -typedef int ssize_t; -# define SELECT_TYPE_ARG1 int -# define SELECT_TYPE_ARG234 (fd_set *) -# define SELECT_TYPE_ARG5 (struct timeval *) -#endif - -#if UNIX_LIKE -# if HAVE_SYS_TYPES_H -# include -# endif -# if HAVE_SYS_SOCKET_H -# include -# endif -# if HAVE_POLL -# include -# endif -# include -# include -# include -#endif - -//! Networking functions -class CNetwork { -public: - // platform dependent types -#if WINDOWS_LIKE - typedef SOCKET Socket; - typedef struct sockaddr Address; - typedef int AddressLength; - typedef short AddressType; - typedef struct in_addr InternetAddress; -#elif UNIX_LIKE - typedef int Socket; - typedef struct sockaddr Address; - typedef socklen_t AddressLength; - typedef int AddressType; - typedef struct in_addr InternetAddress; -#endif - -#if !HAVE_POLL - class PollEntry { - public: - Socket fd; - short events; - short revents; - }; - enum { - kPOLLIN = 1, - kPOLLOUT = 2, - kPOLLERR = 4, - kPOLLNVAL = 8 - }; -#else - typedef struct pollfd PollEntry; - enum { - kPOLLIN = POLLIN, - kPOLLOUT = POLLOUT, - kPOLLERR = POLLERR, - kPOLLNVAL = POLLNVAL - }; -#endif - - //! Host name information - class CHostInfo { - public: - CHostInfo() { } - CHostInfo(const struct hostent*); - - void swap(CHostInfo&); - - public: - typedef std::vector AliasList; - typedef std::vector AddressList; - - CString m_name; - AliasList m_aliases; - AddressType m_addressType; - int m_addressLength; - AddressList m_addresses; - - private: - std::string m_addressData; - }; - - //! Network service information - class CServiceInfo { - public: - CServiceInfo() { } - CServiceInfo(const struct servent*); - - void swap(CServiceInfo&); - - public: - typedef std::vector AliasList; - - CString m_name; - AliasList m_aliases; - int m_port; - CString m_protocol; - }; - - //! Network protocol information - class CProtocolInfo { - public: - CProtocolInfo() { } - CProtocolInfo(const struct protoent*); - - void swap(CProtocolInfo&); - - public: - typedef std::vector AliasList; - - CString m_name; - AliasList m_aliases; - int m_protocol; - }; - - //! @name manipulators - //@{ - - //! Initialize network subsystem - /*! - This \b must be called before any other calls to the network subsystem. - */ - static void init(); - - //! Clean up network subsystem - /*! - This should be called when the network subsystem is no longer needed - and no longer in use. - */ - static void cleanup(); - - // byte swapping functions - //! Swap bytes to network order - static UInt32 swaphtonl(UInt32 hostlong); - //! Swap bytes to network order - static UInt16 swaphtons(UInt16 hostshort); - //! Swap bytes to host order - static UInt32 swapntohl(UInt32 netlong); - //! Swap bytes to host order - static UInt16 swapntohs(UInt16 netshort); - - //@} - //! @name constants - //@{ - - //! The error type - static const int Error; - //! The non-socket - static const Socket Null; - - // getsockerror() constants - enum { -#if WINDOWS_LIKE - kEADDRINUSE = WSAEADDRINUSE, - kECONNECTING = WSAEWOULDBLOCK, - kEINTR = WSAEINTR, -#elif UNIX_LIKE - kEADDRINUSE = EADDRINUSE, - kECONNECTING = EINPROGRESS, - kEINTR = EINTR, -#endif - kNone = 0 - }; - - // gethosterror() constants - enum { -#if WINDOWS_LIKE - kHOST_NOT_FOUND = WSAHOST_NOT_FOUND, - kNO_DATA = WSANO_DATA, - kNO_RECOVERY = WSANO_RECOVERY, - kTRY_AGAIN = WSATRY_AGAIN, -#elif UNIX_LIKE - kHOST_NOT_FOUND = HOST_NOT_FOUND, - kNO_DATA = NO_DATA, - kNO_RECOVERY = NO_RECOVERY, - kTRY_AGAIN = TRY_AGAIN, -#endif - kHOST_OK = 0 - }; - - //@} - - // socket interface (only available after init()) - - static Socket accept(Socket s, Address* addr, AddressLength* addrlen); - static int bind(Socket s, const Address* addr, AddressLength namelen); - static int close(Socket s); - static int connect(Socket s, const Address* name, AddressLength namelen); - static int gethostname(char* name, int namelen); - static int getpeername(Socket s, Address* name, AddressLength* namelen); - static int getsockerror(void); - static int getsockname(Socket s, Address* name, AddressLength* namelen); - static int getsockopt(Socket s, int level, int optname, void* optval, AddressLength* optlen); - static int inet_aton(const char* cp, InternetAddress* addr); - static CString inet_ntoa(struct in_addr in); - static int ioctl(Socket s, int cmd, void* ); - static int listen(Socket s, int backlog); - static int poll(PollEntry[], int nfds, int timeout); - static ssize_t read(Socket s, void* buf, size_t len); - static ssize_t recv(Socket s, void* buf, size_t len, int flags); - static ssize_t recvfrom(Socket s, void* buf, size_t len, int flags, Address* from, AddressLength* fromlen); - static ssize_t send(Socket s, const void* buf, size_t len, int flags); - static ssize_t sendto(Socket s, const void* buf, size_t len, int flags, const Address* to, AddressLength tolen); - static int setsockopt(Socket s, int level, int optname, const void* optval, AddressLength optlen); - static int shutdown(Socket s, int how); - static Socket socket(int af, int type, int protocol); - static ssize_t write(Socket s, const void* buf, size_t len); - static int gethostbyaddr(CHostInfo* hostinfo, const char* addr, int len, int type); - static int gethostbyname(CHostInfo* hostinfo, const char* name); - static int getservbyport(CServiceInfo* servinfo, int port, const char* proto); - static int getservbyname(CServiceInfo* servinfo, const char* name, const char* proto); - static int getprotobynumber(CProtocolInfo* protoinfo, int proto); - static int getprotobyname(CProtocolInfo* protoinfo, const char* name); - - // convenience functions (only available after init()) - - //! Set socket to (non-)blocking operation - static int setblocking(Socket s, bool blocking); - - //! Turn Nagle algorithm on or off on socket - /*! - Set socket to send messages immediately (true) or to collect small - messages into one packet (false). - */ - static int setnodelay(Socket s, bool nodelay); - -private: -#if WINDOWS_LIKE - static void init2(HMODULE); -#endif -}; - -#endif diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp index d626b31d..c60b96a5 100644 --- a/lib/net/CNetworkAddress.cpp +++ b/lib/net/CNetworkAddress.cpp @@ -14,53 +14,47 @@ #include "CNetworkAddress.h" #include "XSocket.h" +#include "CArch.h" +#include "XArch.h" #include -#include // // CNetworkAddress // CNetworkAddress::CNetworkAddress() : - m_port(0) + m_address(NULL) { // note -- make no calls to CNetwork socket interface here; // we're often called prior to CNetwork::init(). - - struct sockaddr_in* inetAddress = reinterpret_cast< - struct sockaddr_in*>(&m_address); - inetAddress->sin_family = AF_INET; - inetAddress->sin_port = CNetwork::swaphtons(m_port); - inetAddress->sin_addr.s_addr = INADDR_ANY; - memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } -CNetworkAddress::CNetworkAddress(UInt16 port) : - m_port(port) +CNetworkAddress::CNetworkAddress(int port) { if (port == 0) { - throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, m_port); + throw XSocketAddress(XSocketAddress::kBadPort, "", port); } - struct sockaddr_in* inetAddress = reinterpret_cast< - struct sockaddr_in*>(&m_address); - inetAddress->sin_family = AF_INET; - inetAddress->sin_port = CNetwork::swaphtons(m_port); - inetAddress->sin_addr.s_addr = INADDR_ANY; - memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); + m_address = ARCH->newAnyAddr(IArchNetwork::kINET); + ARCH->setAddrPort(m_address, port); } -CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : - m_hostname(hostname_), - m_port(port) +CNetworkAddress::CNetworkAddress(const CNetworkAddress& addr) : + m_address(ARCH->copyAddr(addr.m_address)), + m_hostname(addr.m_hostname) { - CString hostname(m_hostname); + // do nothing +} +CNetworkAddress::CNetworkAddress(const CString& hostname_, int port) : + m_hostname(hostname_) +{ if (port == 0) { - throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, m_port); + throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, port); } // check for port suffix + CString hostname(m_hostname); CString::size_type i = hostname.rfind(':'); if (i != CString::npos && i + 1 < hostname.size()) { // found a colon. see if it looks like an IPv6 address. @@ -92,81 +86,77 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, UInt16 port) : suffixPort <= 0 || suffixPort > 65535) { // bogus port throw XSocketAddress(XSocketAddress::kBadPort, - m_hostname, m_port); + m_hostname, port); } else { // good port - port = static_cast(suffixPort); + port = static_cast(suffixPort); hostname.erase(i); } } } // if hostname is empty then use wildcard address - struct sockaddr_in* inetAddress = reinterpret_cast< - struct sockaddr_in*>(&m_address); if (hostname.empty()) { - inetAddress->sin_family = AF_INET; - inetAddress->sin_port = CNetwork::swaphtons(port); - inetAddress->sin_addr.s_addr = INADDR_ANY; - memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); - return; + m_address = ARCH->newAnyAddr(IArchNetwork::kINET); + ARCH->setAddrPort(m_address, port); } - - // convert dot notation to address - if (CNetwork::inet_aton(hostname.c_str(), &inetAddress->sin_addr) != 0) { - inetAddress->sin_family = AF_INET; - inetAddress->sin_port = CNetwork::swaphtons(port); - memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); - return; + else { + // look up name + try { + m_address = ARCH->nameToAddr(hostname); + ARCH->setAddrPort(m_address, port); + } + catch (XArchNetworkNameUnknown&) { + throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); + } + catch (XArchNetworkNameNoAddress&) { + throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); + } + catch (XArchNetworkName&) { + throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); + } } - - // look up name - CNetwork::CHostInfo hostInfo; - switch (CNetwork::gethostbyname(&hostInfo, hostname.c_str())) { - case CNetwork::kHOST_OK: - break; - - case CNetwork::kHOST_NOT_FOUND: - throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); - - case CNetwork::kNO_DATA: - throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); - - case CNetwork::kNO_RECOVERY: - case CNetwork::kTRY_AGAIN: - default: - throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); - } - - inetAddress->sin_family = hostInfo.m_addressType; - inetAddress->sin_port = CNetwork::swaphtons(port); - memcpy(&inetAddress->sin_addr, hostInfo.m_addresses[0], - hostInfo.m_addressLength); - memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero)); } CNetworkAddress::~CNetworkAddress() { - // do nothing + if (m_address != NULL) { + ARCH->closeAddr(m_address); + } +} + +CNetworkAddress& +CNetworkAddress::operator=(const CNetworkAddress& addr) +{ + CArchNetAddress newAddr = NULL; + if (addr.m_address != NULL) { + newAddr = ARCH->copyAddr(addr.m_address); + } + if (m_address != NULL) { + ARCH->closeAddr(m_address); + } + m_hostname = addr.m_hostname; + m_address = newAddr; + return *this; } bool CNetworkAddress::isValid() const { - return (m_port != 0); + return (m_address != NULL); } -const CNetwork::Address* +const CArchNetAddress& CNetworkAddress::getAddress() const { - return &m_address; + return m_address; } - -CNetwork::AddressLength -CNetworkAddress::getAddressLength() const + +int +CNetworkAddress::getPort() const { - return sizeof(m_address); + return (m_address == NULL) ? 0 : ARCH->getAddrPort(m_address); } CString @@ -174,9 +164,3 @@ CNetworkAddress::getHostname() const { return m_hostname; } - -UInt16 -CNetworkAddress::getPort() const -{ - return m_port; -} diff --git a/lib/net/CNetworkAddress.h b/lib/net/CNetworkAddress.h index fe68dcf6..02898278 100644 --- a/lib/net/CNetworkAddress.h +++ b/lib/net/CNetworkAddress.h @@ -15,9 +15,9 @@ #ifndef CNETWORKADDRESS_H #define CNETWORKADDRESS_H -#include "CNetwork.h" #include "CString.h" #include "BasicTypes.h" +#include "IArchNetwork.h" //! Network address type /*! @@ -34,7 +34,7 @@ public: Construct the wildcard address with the given port. \c port must not be zero. */ - CNetworkAddress(UInt16 port); + CNetworkAddress(int port); /*! Construct the network address for the given \c hostname and \c port. @@ -44,10 +44,14 @@ public: that suffix is extracted and used as the port, overridding the port parameter. Neither the extracted port or \c port may be zero. */ - CNetworkAddress(const CString& hostname, UInt16 port); + CNetworkAddress(const CString& hostname, int port); + + CNetworkAddress(const CNetworkAddress&); ~CNetworkAddress(); + CNetworkAddress& operator=(const CNetworkAddress&); + //! @name accessors //@{ @@ -62,14 +66,14 @@ public: Returns the address in the platform's native network address structure. */ - const CNetwork::Address* getAddress() const; + const CArchNetAddress& getAddress() const; - //! Get address length + //! Get port /*! - Returns the length of the address in the platform's native network - address structure. + Returns the port passed to the c'tor as a suffix to the hostname, + if that existed, otherwise as passed directly to the c'tor. */ - CNetwork::AddressLength getAddressLength() const; + int getPort() const; //! Get hostname /*! @@ -77,19 +81,11 @@ public: */ CString getHostname() const; - //! Get port - /*! - Returns the port passed to the c'tor as a suffix to the hostname, - if that existed, otherwise as passed directly to the c'tor. - */ - UInt16 getPort() const; - //@} private: - CNetwork::Address m_address; + CArchNetAddress m_address; CString m_hostname; - UInt16 m_port; }; #endif diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index 83278166..1177516e 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -18,6 +18,8 @@ #include "XIO.h" #include "XSocket.h" #include "CThread.h" +#include "CArch.h" +#include "XArch.h" // // CTCPListenSocket @@ -25,16 +27,18 @@ CTCPListenSocket::CTCPListenSocket() { - m_fd = CNetwork::socket(PF_INET, SOCK_STREAM, 0); - if (m_fd == CNetwork::Null) { - throw XSocketCreate(); + try { + m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); + } + catch (XArchNetwork& e) { + throw XSocketCreate(e.what()); } } CTCPListenSocket::~CTCPListenSocket() { try { - close(); + ARCH->closeSocket(m_socket); } catch (...) { // ignore @@ -44,15 +48,15 @@ CTCPListenSocket::~CTCPListenSocket() void CTCPListenSocket::bind(const CNetworkAddress& addr) { - if (CNetwork::bind(m_fd, addr.getAddress(), - addr.getAddressLength()) == CNetwork::Error) { - if (CNetwork::getsockerror() == CNetwork::kEADDRINUSE) { - throw XSocketAddressInUse(); - } - throw XSocketBind(); + try { + ARCH->bindSocket(m_socket, addr.getAddress()); + ARCH->listenOnSocket(m_socket); } - if (CNetwork::listen(m_fd, 3) == CNetwork::Error) { - throw XSocketBind(); + catch (XArchNetworkAddressInUse& e) { + throw XSocketAddressInUse(e.what()); + } + catch (XArchNetwork& e) { + throw XSocketBind(e.what()); } } @@ -60,31 +64,35 @@ IDataSocket* CTCPListenSocket::accept() { // accept asynchronously so we can check for cancellation - CNetwork::PollEntry pfds[1]; - pfds[0].fd = m_fd; - pfds[0].events = CNetwork::kPOLLIN; + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; + pfds[0].m_events = IArchNetwork::kPOLLIN; for (;;) { - CThread::testCancel(); - const int status = CNetwork::poll(pfds, 1, 10); - if (status > 0 && (pfds[0].revents & CNetwork::kPOLLIN) != 0) { - CNetwork::Address addr; - CNetwork::AddressLength addrlen = sizeof(addr); - CNetwork::Socket fd = CNetwork::accept(m_fd, &addr, &addrlen); - if (fd != CNetwork::Null) { - return new CTCPSocket(fd); + ARCH->testCancelThread(); + try { + const int status = ARCH->pollSocket(pfds, 1, 0.01); + if (status > 0 && + (pfds[0].m_revents & IArchNetwork::kPOLLIN) != 0) { + return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); } } + catch (XArchNetwork&) { + // ignore and retry + } } } void CTCPListenSocket::close() { - if (m_fd == CNetwork::Null) { + if (m_socket == NULL) { throw XIOClosed(); } - if (CNetwork::close(m_fd) == CNetwork::Error) { - throw XSocketIOClose(); + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork& e) { + throw XSocketIOClose(e.what()); } - m_fd = CNetwork::Null; } diff --git a/lib/net/CTCPListenSocket.h b/lib/net/CTCPListenSocket.h index 15460f30..d5116379 100644 --- a/lib/net/CTCPListenSocket.h +++ b/lib/net/CTCPListenSocket.h @@ -16,7 +16,7 @@ #define CTCPLISTENSOCKET_H #include "IListenSocket.h" -#include "CNetwork.h" +#include "IArchNetwork.h" //! TCP listen socket /*! @@ -35,7 +35,7 @@ public: virtual IDataSocket* accept(); private: - CNetwork::Socket m_fd; + CArchSocket m_socket; }; #endif diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 0832904f..e74fc4f7 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -18,12 +18,12 @@ #include "CNetworkAddress.h" #include "XIO.h" #include "XSocket.h" -#include "CCondVar.h" #include "CLock.h" #include "CMutex.h" #include "CThread.h" -#include "CStopwatch.h" #include "TMethodJob.h" +#include "CArch.h" +#include "XArch.h" // // CTCPSocket @@ -31,17 +31,19 @@ CTCPSocket::CTCPSocket() { - m_fd = CNetwork::socket(PF_INET, SOCK_STREAM, 0); - if (m_fd == CNetwork::Null) { - throw XSocketCreate(); + try { + m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); + } + catch (XArchNetwork& e) { + throw XSocketCreate(e.what()); } init(); } -CTCPSocket::CTCPSocket(CNetwork::Socket fd) : - m_fd(fd) +CTCPSocket::CTCPSocket(CArchSocket socket) : + m_socket(socket) { - assert(m_fd != CNetwork::Null); + assert(m_socket != NULL); init(); @@ -59,7 +61,7 @@ CTCPSocket::~CTCPSocket() close(); } catch (...) { - // ignore failures + // ignore } // clean up @@ -71,12 +73,14 @@ CTCPSocket::~CTCPSocket() void CTCPSocket::bind(const CNetworkAddress& addr) { - if (CNetwork::bind(m_fd, addr.getAddress(), - addr.getAddressLength()) == CNetwork::Error) { - if (errno == CNetwork::kEADDRINUSE) { - throw XSocketAddressInUse(); - } - throw XSocketBind(); + try { + ARCH->bindSocket(m_socket, addr.getAddress()); + } + catch (XArchNetworkAddressInUse& e) { + throw XSocketAddressInUse(e.what()); + } + catch (XArchNetwork& e) { + throw XSocketBind(e.what()); } } @@ -96,12 +100,21 @@ CTCPSocket::close() } // cause ioThread to exit - { + if (m_socket != NULL) { CLock lock(m_mutex); - if (m_fd != CNetwork::Null) { - CNetwork::shutdown(m_fd, 2); - m_connected = kClosed; + try { + ARCH->closeSocketForRead(m_socket); } + catch (XArchNetwork&) { + // ignore + } + try { + ARCH->closeSocketForWrite(m_socket); + } + catch (XArchNetwork&) { + // ignore + } + m_connected = kClosed; } // wait for thread @@ -112,67 +125,71 @@ CTCPSocket::close() } // close socket - if (m_fd != CNetwork::Null) { - if (CNetwork::close(m_fd) == CNetwork::Error) { - throw XSocketIOClose(); + if (m_socket != NULL) { + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork& e) { + throw XSocketIOClose(e.what()); } - m_fd = CNetwork::Null; } } void CTCPSocket::connect(const CNetworkAddress& addr) { - // connect asynchronously so we can check for cancellation - CNetwork::setblocking(m_fd, false); - if (CNetwork::connect(m_fd, addr.getAddress(), - addr.getAddressLength()) == CNetwork::Error) { - // check for failure - if (CNetwork::getsockerror() != CNetwork::kECONNECTING) { - XSocketConnect e; - CNetwork::setblocking(m_fd, true); - throw e; + do { + // connect asynchronously so we can check for cancellation. + // we can't wrap setting and resetting the blocking flag in + // the c'tor/d'tor of a class (to make resetting automatic) + // because setBlockingOnSocket() can throw and it might be + // called while unwinding the stack due to a throw. + try { + ARCH->setBlockingOnSocket(m_socket, false); + ARCH->connectSocket(m_socket, addr.getAddress()); + ARCH->setBlockingOnSocket(m_socket, true); + + // connected + break; + } + catch (XArchNetworkConnecting&) { + // connection is in progress + ARCH->setBlockingOnSocket(m_socket, true); + } + catch (XArchNetwork& e) { + ARCH->setBlockingOnSocket(m_socket, true); + throw XSocketConnect(e.what()); } // wait for connection or failure - CNetwork::PollEntry pfds[1]; - pfds[0].fd = m_fd; - pfds[0].events = CNetwork::kPOLLOUT; + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; + pfds[0].m_events = IArchNetwork::kPOLLOUT; for (;;) { - CThread::testCancel(); - const int status = CNetwork::poll(pfds, 1, 10); - if (status > 0) { - if ((pfds[0].revents & (CNetwork::kPOLLERR | - CNetwork::kPOLLNVAL)) != 0) { - // connection failed - int error = 0; - CNetwork::AddressLength size = sizeof(error); - CNetwork::setblocking(m_fd, true); - CNetwork::getsockopt(m_fd, SOL_SOCKET, SO_ERROR, - reinterpret_cast(&error), &size); - throw XSocketConnect(error); - } - if ((pfds[0].revents & CNetwork::kPOLLOUT) != 0) { - int error; - CNetwork::AddressLength size = sizeof(error); - if (CNetwork::getsockopt(m_fd, SOL_SOCKET, SO_ERROR, - reinterpret_cast(&error), - &size) == CNetwork::Error || - error != 0) { + ARCH->testCancelThread(); + try { + const int status = ARCH->pollSocket(pfds, 1, 0.01); + if (status > 0) { + if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | + IArchNetwork::kPOLLNVAL)) != 0) { // connection failed - CNetwork::setblocking(m_fd, true); - throw XSocketConnect(error); + ARCH->throwErrorOnSocket(m_socket); } + if ((pfds[0].m_revents & IArchNetwork::kPOLLOUT) != 0) { + // connection may have failed or succeeded + ARCH->throwErrorOnSocket(m_socket); - // connected! - break; + // connected! + break; + } } } + catch (XArchNetwork& e) { + throw XSocketConnect(e.what()); + } } - } - - // back to blocking - CNetwork::setblocking(m_fd, true); + } while (false); // start servicing the socket m_connected = kReadWrite; @@ -208,7 +225,20 @@ CTCPSocket::init() // turn off Nagle algorithm. we send lots of very short messages // that should be sent without (much) delay. for example, the // mouse motion messages are much less useful if they're delayed. - CNetwork::setnodelay(m_fd, true); +// FIXME -- the client should do this + try { + ARCH->setNoDelayOnSocket(m_socket, true); + } + catch (XArchNetwork& e) { + try { + ARCH->closeSocket(m_socket); + m_socket = NULL; + } + catch (XArchNetwork&) { + // ignore + } + throw XSocketCreate(e.what()); + } } void @@ -244,85 +274,81 @@ CTCPSocket::ioCleanup() void CTCPSocket::ioService() { - assert(m_fd != CNetwork::Null); + assert(m_socket != NULL); // now service the connection - CNetwork::PollEntry pfds[1]; - pfds[0].fd = m_fd; + IArchNetwork::CPollEntry pfds[1]; + pfds[0].m_socket = m_socket; for (;;) { { // choose events to poll for CLock lock(m_mutex); - pfds[0].events = 0; + pfds[0].m_events = 0; if (m_connected == 0) { return; } if ((m_connected & kRead) != 0) { // still open for reading - pfds[0].events |= CNetwork::kPOLLIN; + pfds[0].m_events |= IArchNetwork::kPOLLIN; } if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) { // data queued for writing - pfds[0].events |= CNetwork::kPOLLOUT; + pfds[0].m_events |= IArchNetwork::kPOLLOUT; } } - // check for status - const int status = CNetwork::poll(pfds, 1, 10); + try { + // check for status + const int status = ARCH->pollSocket(pfds, 1, 0.01); - // transfer data and handle errors - if (status == 1) { - if ((pfds[0].revents & (CNetwork::kPOLLERR | - CNetwork::kPOLLNVAL)) != 0) { - // stream is no good anymore so bail - m_input->hangup(); - return; - } - - // read some data - if (pfds[0].revents & CNetwork::kPOLLIN) { - UInt8 buffer[4096]; - ssize_t n = CNetwork::read(m_fd, buffer, sizeof(buffer)); - if (n > 0) { + // transfer data and handle errors + if (status == 1) { + if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | + IArchNetwork::kPOLLNVAL)) != 0) { + // stream is no good anymore so bail CLock lock(m_mutex); - m_input->write(buffer, n); - } - else if (n == 0) { - // stream hungup m_input->hangup(); - m_connected &= ~kRead; + return; } - else { - // socket failed - if (CNetwork::getsockerror() != CNetwork::kEINTR) { - return; - } - } - } - - // write some data - if (pfds[0].revents & CNetwork::kPOLLOUT) { - CLock lock(m_mutex); - - // get amount of data to write - UInt32 n = m_output->getSize(); - - // write data - const void* buffer = m_output->peek(n); - ssize_t n2 = (UInt32)CNetwork::write(m_fd, buffer, n); - - // discard written data - if (n2 > 0) { - m_output->pop(n); - } - else if (n2 < 0) { - // socket failed - if (CNetwork::getsockerror() != CNetwork::kEINTR) { - return; + + // read some data + if (pfds[0].m_revents & IArchNetwork::kPOLLIN) { + UInt8 buffer[4096]; + size_t n = ARCH->readSocket(m_socket, + buffer, sizeof(buffer)); + CLock lock(m_mutex); + if (n > 0) { + m_input->write(buffer, n); + } + else { + // stream hungup + m_input->hangup(); + m_connected &= ~kRead; + } + } + + // write some data + if (pfds[0].m_revents & IArchNetwork::kPOLLOUT) { + CLock lock(m_mutex); + + // get amount of data to write + UInt32 n = m_output->getSize(); + + // write data + const void* buffer = m_output->peek(n); + size_t n2 = ARCH->writeSocket(m_socket, buffer, n); + + // discard written data + if (n2 > 0) { + m_output->pop(n2); } } } } + catch (XArchNetwork&) { + // socket has failed + return; + } } } @@ -330,14 +356,24 @@ void CTCPSocket::closeInput(void*) { // note -- m_mutex should already be locked - CNetwork::shutdown(m_fd, 0); - m_connected &= ~kRead; + try { + ARCH->closeSocketForRead(m_socket); + m_connected &= ~kRead; + } + catch (XArchNetwork&) { + // ignore + } } void CTCPSocket::closeOutput(void*) { // note -- m_mutex should already be locked - CNetwork::shutdown(m_fd, 1); - m_connected &= ~kWrite; + try { + ARCH->closeSocketForWrite(m_socket); + m_connected &= ~kWrite; + } + catch (XArchNetwork&) { + // ignore + } } diff --git a/lib/net/CTCPSocket.h b/lib/net/CTCPSocket.h index 8aafa19c..bfbade89 100644 --- a/lib/net/CTCPSocket.h +++ b/lib/net/CTCPSocket.h @@ -16,11 +16,10 @@ #define CTCPSOCKET_H #include "IDataSocket.h" -#include "CNetwork.h" +#include "BasicTypes.h" +#include "IArchNetwork.h" class CMutex; -template -class CCondVar; class CThread; class CBufferedInputStream; class CBufferedOutputStream; @@ -32,7 +31,7 @@ A data socket using TCP. class CTCPSocket : public IDataSocket { public: CTCPSocket(); - CTCPSocket(CNetwork::Socket); + CTCPSocket(CArchSocket); ~CTCPSocket(); // ISocket overrides @@ -55,7 +54,7 @@ private: private: enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 }; - CNetwork::Socket m_fd; + CArchSocket m_socket; CBufferedInputStream* m_input; CBufferedOutputStream* m_output; diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index 899fa938..efeb5198 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -25,14 +25,11 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libnet.a libnet_a_SOURCES = \ - CNetwork.cpp \ CNetworkAddress.cpp \ CTCPListenSocket.cpp \ CTCPSocket.cpp \ CTCPSocketFactory.cpp \ - XNetwork.cpp \ XSocket.cpp \ - CNetwork.h \ CNetworkAddress.h \ CTCPListenSocket.h \ CTCPSocket.h \ @@ -41,10 +38,11 @@ libnet_a_SOURCES = \ IListenSocket.h \ ISocket.h \ ISocketFactory.h \ - XNetwork.h \ XSocket.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/net/XNetwork.cpp b/lib/net/XNetwork.cpp deleted file mode 100644 index 61ba2555..00000000 --- a/lib/net/XNetwork.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "XNetwork.h" - -// -// XNetworkUnavailable -// - -CString -XNetworkUnavailable::getWhat() const throw() -{ - return format("XNetworkUnavailable", "network library is not available"); -} - - -// -// XNetworkFailed -// - -CString -XNetworkFailed::getWhat() const throw() -{ - return format("XNetworkFailed", "cannot initialize network library"); -} - - -// -// XNetworkVersion -// - -XNetworkVersion::XNetworkVersion(int major, int minor) throw() : - m_major(major), - m_minor(minor) -{ - // do nothing -} - -int -XNetworkVersion::getMajor() const throw() -{ - return m_major; -} - -int -XNetworkVersion::getMinor() const throw() -{ - return m_minor; -} - -CString -XNetworkVersion::getWhat() const throw() -{ - return format("XNetworkVersion", - "unsupported network version %{1}.%{2}", - CStringUtil::print("%d", m_major).c_str(), - CStringUtil::print("%d", m_minor).c_str()); -} - - -// -// XNetworkFunctionUnavailable -// - -XNetworkFunctionUnavailable::XNetworkFunctionUnavailable( - const char* name) throw() -{ - try { - m_name = name; - } - catch (...) { - // ignore - } -} - -CString -XNetworkFunctionUnavailable::getWhat() const throw() -{ - return format("XNetworkFunctionUnavailable", - "missing network function %{1}", - m_name.c_str()); -} diff --git a/lib/net/XNetwork.h b/lib/net/XNetwork.h deleted file mode 100644 index 1e9eee5b..00000000 --- a/lib/net/XNetwork.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef XNETWORK_H -#define XNETWORK_H - -#include "XBase.h" -#include "CString.h" - -//! Generic network exception -/*! -Network exceptions are thrown when initializing the network subsystem -and not during normal network use. -*/ -class XNetwork : public XBase { }; - -//! Network subsystem not available exception -/*! -Thrown when the network subsystem is unavailable, typically because -the necessary shared library is unavailable. -*/ -class XNetworkUnavailable : public XNetwork { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; - -//! Network subsystem failed exception -/*! -Thrown when the network subsystem cannot be initialized. -*/ -class XNetworkFailed : public XNetwork { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; - -//! Network subsystem vesion unsupported exception -/*! -Thrown when the network subsystem has a version incompatible with -what's expected. -*/ -class XNetworkVersion : public XNetwork { -public: - XNetworkVersion(int major, int minor) throw(); - - //! @name accessors - //@{ - - //! Get the network subsystem's major version - int getMajor() const throw(); - //! Get the network subsystem's minor version - int getMinor() const throw(); - - //@} - -protected: - // XBase overrides - virtual CString getWhat() const throw(); - -private: - int m_major; - int m_minor; -}; - -//! Network subsystem incomplete exception -/*! -Thrown when the network subsystem is missing an expected and required -function. -*/ -class XNetworkFunctionUnavailable : public XNetwork { -public: - XNetworkFunctionUnavailable(const char* name) throw(); - -protected: - // XBase overrides - virtual CString getWhat() const throw(); - -private: - CString m_name; -}; - -#endif diff --git a/lib/net/XSocket.cpp b/lib/net/XSocket.cpp index 8d372675..cf8b3ca6 100644 --- a/lib/net/XSocket.cpp +++ b/lib/net/XSocket.cpp @@ -13,14 +13,14 @@ */ #include "XSocket.h" -#include "CNetwork.h" +#include "CStringUtil.h" // // XSocketAddress // XSocketAddress::XSocketAddress(EError error, - const CString& hostname, UInt16 port) throw() : + const CString& hostname, int port) throw() : m_error(error), m_hostname(hostname), m_port(port) @@ -40,7 +40,7 @@ XSocketAddress::getHostname() const throw() return m_hostname; } -UInt16 +int XSocketAddress::getPort() const throw() { return m_port; @@ -67,97 +67,6 @@ XSocketAddress::getWhat() const throw() } -// -// XSocketErrno -// - -XSocketErrno::XSocketErrno() : - MXErrno(CNetwork::getsockerror()) -{ - // do nothing -} - -XSocketErrno::XSocketErrno(int err) : - MXErrno(err) -{ - // do nothing -} - -const char* -XSocketErrno::getErrstr() const -{ -#if WINDOWS_LIKE - // built-in windows function for looking up error message strings - // may not look up network error messages correctly. we'll have - // to do it ourself. - static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = { - /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, - /* 10009 */{WSAEBADF, "Bad file handle"}, - /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, - /* 10014 */{WSAEFAULT, "WSAEFAULT"}, - /* 10022 */{WSAEINVAL, "WSAEINVAL"}, - /* 10024 */{WSAEMFILE, "No more file descriptors available"}, - /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, - /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, - /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"}, - /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"}, - /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"}, - /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"}, - /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, - /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, - /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"}, - /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"}, - /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, - /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"}, - /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"}, - /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, - /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, - /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"}, - /* 10051 */{WSAENETUNREACH, "The network can't be reached from this hos at this time"}, - /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, - /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"}, - /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, - /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"}, - /* 10056 */{WSAEISCONN, "The socket is already connected"}, - /* 10057 */{WSAENOTCONN, "The socket is not connected"}, - /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, - /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"}, - /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, - /* 10061 */{WSAECONNREFUSED, "The attempt to connect was forcefully rejected"}, - /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, - /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"}, - /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, - /* 10065 */{WSAEHOSTUNREACH, "No route to host"}, - /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, - /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"}, - /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, - /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, - /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, - /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, - /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"}, - /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, - /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, - /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, - /* 11001 */{WSAHOST_NOT_FOUND, "Host not found"}, - /* 11002 */{WSATRY_AGAIN, "Host not found"}, - /* 11003 */{WSANO_RECOVERY, "Host name lookup error"}, - /* 11004 */{WSANO_DATA, "No data for host"}, - /* end */{0, NULL} - }; - - const int err = getErrno(); - for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) { - if (s_netErrorCodes[i].m_code == err) { - return s_netErrorCodes[i].m_msg; - } - } -#endif - - // not a network error code. fallback to system error message. - return MXErrno::getErrstr(); -} - - // // XSocketIOClose // @@ -165,8 +74,7 @@ XSocketErrno::getErrstr() const CString XSocketIOClose::getWhat() const throw() { - return format("XSocketIOClose", "close: %{1}", - getErrstr()); + return format("XSocketIOClose", "close: %{1}", what()); } @@ -177,8 +85,7 @@ XSocketIOClose::getWhat() const throw() CString XSocketBind::getWhat() const throw() { - return format("XSocketBind", "cannot bind address: %{1}", - getErrstr()); + return format("XSocketBind", "cannot bind address: %{1}", what()); } @@ -189,8 +96,7 @@ XSocketBind::getWhat() const throw() CString XSocketConnect::getWhat() const throw() { - return format("XSocketConnect", "cannot connect socket: %{1}", - getErrstr()); + return format("XSocketConnect", "cannot connect socket: %{1}", what()); } @@ -201,6 +107,5 @@ XSocketConnect::getWhat() const throw() CString XSocketCreate::getWhat() const throw() { - return format("XSocketCreate", "cannot create socket: %{1}", - getErrstr()); + return format("XSocketCreate", "cannot create socket: %{1}", what()); } diff --git a/lib/net/XSocket.h b/lib/net/XSocket.h index 131a3f1f..c3663033 100644 --- a/lib/net/XSocket.h +++ b/lib/net/XSocket.h @@ -21,7 +21,7 @@ #include "BasicTypes.h" //! Generic socket exception -class XSocket : public XBase { }; +XBASE_SUBCLASS(XSocket, XBase); //! Socket bad address exception /*! @@ -37,7 +37,7 @@ public: kBadPort //!< The port is invalid }; - XSocketAddress(EError, const CString& hostname, UInt16 port) throw(); + XSocketAddress(EError, const CString& hostname, int port) throw(); //! @name accessors //@{ @@ -47,7 +47,7 @@ public: //! Get the hostname CString getHostname() const throw(); //! Get the port - UInt16 getPort() const throw(); + int getPort() const throw(); //@} @@ -58,80 +58,38 @@ protected: private: EError m_error; CString m_hostname; - UInt16 m_port; -}; - -//! Generic socket exception using \c errno -class XSocketErrno : public MXErrno { -public: - XSocketErrno(); - XSocketErrno(int); - - // MXErrno overrides - virtual const char* getErrstr() const; + int m_port; }; //! I/O closing exception /*! Thrown if a stream cannot be closed. */ -class XSocketIOClose : public XIOClose, public XSocketErrno { -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_FORMAT(XSocketIOClose, XIOClose); //! Socket cannot bind address exception /*! Thrown when a socket cannot be bound to an address. */ -class XSocketBind : public XSocket, public XSocketErrno { -public: - XSocketBind() { } - XSocketBind(int e) : XSocketErrno(e) { } - -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_FORMAT(XSocketBind, XSocket); //! Socket address in use exception /*! Thrown when a socket cannot be bound to an address because the address is already in use. */ -class XSocketAddressInUse : public XSocketBind { -public: - XSocketAddressInUse() { } - XSocketAddressInUse(int e) : XSocketBind(e) { } -}; +XBASE_SUBCLASS(XSocketAddressInUse, XSocketBind); //! Cannot connect socket exception /*! Thrown when a socket cannot connect to a remote endpoint. */ -class XSocketConnect : public XSocket, public XSocketErrno { -public: - XSocketConnect() { } - XSocketConnect(int e) : XSocketErrno(e) { } - -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_FORMAT(XSocketConnect, XSocket); //! Cannot create socket exception /*! Thrown when a socket cannot be created (by the operating system). */ -class XSocketCreate : public XSocket, public XSocketErrno { -public: - XSocketCreate() { } - XSocketCreate(int e) : XSocketErrno(e) { } - -protected: - // XBase overrides - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_FORMAT(XSocketCreate, XSocket); #endif diff --git a/lib/net/net.dsp b/lib/net/net.dsp index fb7df902..2d038c50 100644 --- a/lib/net/net.dsp +++ b/lib/net/net.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -87,10 +87,6 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=.\CNetwork.cpp -# End Source File -# Begin Source File - SOURCE=.\CNetworkAddress.cpp # End Source File # Begin Source File @@ -107,10 +103,6 @@ SOURCE=.\CTCPSocketFactory.cpp # End Source File # Begin Source File -SOURCE=.\XNetwork.cpp -# End Source File -# Begin Source File - SOURCE=.\XSocket.cpp # End Source File # End Group @@ -119,10 +111,6 @@ SOURCE=.\XSocket.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\CNetwork.h -# End Source File -# Begin Source File - SOURCE=.\CNetworkAddress.h # End Source File # Begin Source File @@ -151,10 +139,6 @@ SOURCE=.\ISocketFactory.h # End Source File # Begin Source File -SOURCE=.\XNetwork.h -# End Source File -# Begin Source File - SOURCE=.\XSocket.h # End Source File # End Group diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 1b8e74c7..962b9047 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -15,7 +15,6 @@ #include "CMSWindowsScreen.h" #include "CMSWindowsClipboard.h" #include "CMSWindowsScreenSaver.h" -#include "CPlatform.h" #include "CClipboard.h" #include "IMSWindowsScreenEventHandler.h" #include "IScreenReceiver.h" @@ -24,6 +23,8 @@ #include "TMethodJob.h" #include "CLog.h" #include "CString.h" +#include "CStringUtil.h" +#include "CArchMiscWindows.h" #include // @@ -49,7 +50,7 @@ CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, m_class(NULL), m_icon(NULL), m_cursor(NULL), - m_is95Family(CPlatform::isWindows95Family()), + m_is95Family(CArchMiscWindows::isWindows95Family()), m_window(NULL), m_x(0), m_y(0), m_w(0), m_h(0), diff --git a/lib/platform/CUnixPlatform.cpp b/lib/platform/CUnixPlatform.cpp deleted file mode 100644 index 02848633..00000000 --- a/lib/platform/CUnixPlatform.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CUnixPlatform.h" -#include "CLog.h" -#include -#include -#include -#include -#include -#include -#include - - -// -// CUnixPlatform -// - -CUnixPlatform::CUnixPlatform() -{ - // do nothing -} - -CUnixPlatform::~CUnixPlatform() -{ - // do nothing -} - -bool -CUnixPlatform::installDaemon(const char*, const char*, - const char*, const char*, bool) -{ - // daemons don't require special installation - return true; -} - -CUnixPlatform::EResult -CUnixPlatform::uninstallDaemon(const char*, bool) -{ - // daemons don't require special installation - return kSuccess; -} - -int -CUnixPlatform::daemonize(const char* name, DaemonFunc func) -{ - // fork so shell thinks we're done and so we're not a process - // group leader - switch (fork()) { - case -1: - // failed - return -1; - - case 0: - // child - break; - - default: - // parent exits - exit(0); - } - - // become leader of a new session - setsid(); - - // chdir to root so we don't keep mounted filesystems points busy - chdir("/"); - - // mask off permissions for any but owner - umask(077); - - // close open files. we only expect stdin, stdout, stderr to be open. - close(0); - close(1); - close(2); - - // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use - // of standard I/O safely goes in the bit bucket. - open("/dev/null", O_RDONLY); - open("/dev/null", O_RDWR); - dup(1); - - // hook up logger - installDaemonLogger(name); - - // invoke function - return func(this, 1, &name); -} - -void -CUnixPlatform::installDaemonLogger(const char* name) -{ - openlog(name, 0, LOG_DAEMON); - CLog::setOutputter(&CUnixPlatform::deamonLogger); -} - -bool -CUnixPlatform::canInstallDaemon(const char*, bool) const -{ - return false; -} - -bool -CUnixPlatform::isDaemonInstalled(const char*, bool) const -{ - return false; -} - -const char* -CUnixPlatform::getBasename(const char* pathname) const -{ - if (pathname == NULL) { - return NULL; - } - - const char* basename = strrchr(pathname, '/'); - if (basename != NULL) { - return basename + 1; - } - else { - return pathname; - } -} - -CString -CUnixPlatform::getUserDirectory() const -{ -#if HAVE_GETPWUID_R - struct passwd pwent; - struct passwd* pwentp; -#if defined(_SC_GETPW_R_SIZE_MAX) - long size = sysconf(_SC_GETPW_R_SIZE_MAX); -#else - long size = BUFSIZ; -#endif - char* buffer = new char[size]; - getpwuid_r(getuid(), &pwent, buffer, size, &pwentp); - delete[] buffer; -#else - struct passwd* pwentp = getpwuid(getuid()); -#endif - if (pwentp != NULL && pwentp->pw_dir != NULL) { - return pwentp->pw_dir; - } - else { - return CString(); - } -} - -CString -CUnixPlatform::getSystemDirectory() const -{ - return "/etc"; -} - -CString -CUnixPlatform::addPathComponent(const CString& prefix, - const CString& suffix) const -{ - CString path; - path.reserve(prefix.size() + 1 + suffix.size()); - path += prefix; - if (path.size() == 0 || path[path.size() - 1] != '/') { - path += '/'; - } - path += suffix; - return path; -} - -bool -CUnixPlatform::deamonLogger(int priority, const char* msg) -{ - // convert priority - switch (priority) { - case CLog::kFATAL: - case CLog::kERROR: - priority = LOG_ERR; - break; - - case CLog::kWARNING: - priority = LOG_WARNING; - break; - - case CLog::kNOTE: - priority = LOG_NOTICE; - break; - - case CLog::kINFO: - priority = LOG_INFO; - break; - - default: - priority = LOG_DEBUG; - break; - } - - // log it - syslog(priority, "%s", msg); - return true; -} diff --git a/lib/platform/CUnixPlatform.h b/lib/platform/CUnixPlatform.h deleted file mode 100644 index 00e5ea23..00000000 --- a/lib/platform/CUnixPlatform.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CUNIXPLATFORM_H -#define CUNIXPLATFORM_H - -#include "IPlatform.h" - -//! Unix platform dependent functions -class CUnixPlatform : public IPlatform { -public: - CUnixPlatform(); - virtual ~CUnixPlatform(); - - // IPlatform overrides - virtual bool installDaemon(const char* name, - const char* description, - const char* pathname, - const char* commandLine, - bool allUsers); - virtual EResult uninstallDaemon(const char* name, bool allUsers); - virtual int daemonize(const char* name, DaemonFunc); - virtual void installDaemonLogger(const char* name); - virtual bool canInstallDaemon(const char* name, - bool allUsers) const; - virtual bool isDaemonInstalled(const char* name, - bool allUsers) const; - virtual const char* getBasename(const char* pathname) const; - virtual CString getUserDirectory() const; - virtual CString getSystemDirectory() const; - virtual CString addPathComponent( - const CString& prefix, - const CString& suffix) const; - -private: - static bool deamonLogger(int, const char*); -}; - -#endif diff --git a/lib/platform/CWin32Platform.h b/lib/platform/CWin32Platform.h deleted file mode 100644 index dadcb4f2..00000000 --- a/lib/platform/CWin32Platform.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CWIN32PLATFORM_H -#define CWIN32PLATFORM_H - -#include "IPlatform.h" -#include "CCondVar.h" -#include "CMutex.h" -#define WIN32_LEAN_AND_MEAN -#include - -//! Microsoft windows platform dependent functions -class CWin32Platform : public IPlatform { -public: - typedef int (*RunFunc)(CMutex*); - typedef void (*StopFunc)(void); - - CWin32Platform(); - virtual ~CWin32Platform(); - - //! Test if windows 95, et al. - /*! - Returns true iff the platform is win95/98/me. - */ - static bool isWindows95Family(); - - //! Utility for calling SetServiceStatus() - static void setStatus(SERVICE_STATUS_HANDLE, DWORD state); - //! Utility for calling SetServiceStatus() - static void setStatus(SERVICE_STATUS_HANDLE, - DWORD state, DWORD step, DWORD waitHint); - //! Utility for calling SetServiceStatus() - static void setStatusError(SERVICE_STATUS_HANDLE, DWORD error); - - //! Run service - /*! - Run a service. The RunFunc should unlock the passed in mutex - (which will be locked on entry) when not initializing or - shutting down (i.e. when running its loop). StopFunc should - cause the RunFunc() to return. Returns what RunFunc returns. - RunFunc should throw CDaemonFailed if the service fails. - */ - int runDaemon(RunFunc, StopFunc); - - //! Daemon failed exception - /*! - Thrown by RunFunc on service failure. Result is the error - code reported by the service. - */ - class CDaemonFailed { - public: - CDaemonFailed(int result) : m_result(result) { } - - public: - int m_result; - }; - - // IPlatform overrides - virtual bool installDaemon(const char* name, - const char* description, - const char* pathname, - const char* commandLine, - bool allUsers); - virtual EResult uninstallDaemon(const char* name, bool allUsers); - virtual int daemonize(const char* name, DaemonFunc); - virtual void installDaemonLogger(const char* name); - virtual bool canInstallDaemon(const char* name, - bool allUsers) const; - virtual bool isDaemonInstalled(const char* name, - bool allUsers) const; - virtual const char* getBasename(const char* pathname) const; - virtual CString getUserDirectory() const; - virtual CString getSystemDirectory() const; - virtual CString addPathComponent( - const CString& prefix, - const CString& suffix) const; - -private: - static HKEY openKey(HKEY parent, const char*); - static HKEY openKey(HKEY parent, const char**); - static void closeKey(HKEY); - static void deleteKey(HKEY, const char* name); - static void deleteValue(HKEY, const char* name); - static void setValue(HKEY, const char* name, - const CString& value); - static CString readValueString(HKEY, const char* name); - static HKEY openNTServicesKey(); - static HKEY open95ServicesKey(); - static HKEY openUserStartupKey(); - - void serviceMain(DWORD, LPTSTR*); - static void WINAPI serviceMainEntry(DWORD, LPTSTR*); - - void runDaemonThread(void*); - - void serviceHandler(DWORD ctrl); - static void WINAPI serviceHandlerEntry(DWORD ctrl); - - static bool serviceLogger(int, const char*); - -private: - DaemonFunc m_daemonFunc; - int m_daemonResult; - - SERVICE_STATUS_HANDLE m_statusHandle; - - CMutex* m_serviceMutex; - CCondVar* m_serviceState; - bool m_serviceHandlerWaiting; - bool m_serviceRunning; - StopFunc m_stop; - - static HANDLE s_eventLog; - - static CWin32Platform* s_daemonPlatform; -}; - -#endif diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 32ae8cde..6a9ced0f 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -20,6 +20,7 @@ #include "CThread.h" #include "CLog.h" #include "CStopwatch.h" +#include "CArch.h" #include "stdvector.h" #include #include @@ -751,16 +752,16 @@ CXWindowsClipboard::motifFillCache() } // see if atom is in target list - CMotifFormatMap::const_iterator index = + CMotifFormatMap::const_iterator index2 = motifFormats.find(converter->getAtom()); - if (index == motifFormats.end()) { + if (index2 == motifFormats.end()) { continue; } // get format const CMotifClipFormat* motifFormat = reinterpret_cast( - index->second.data()); + index2->second.data()); const Atom target = motifFormat->m_type; // get the data (finally) @@ -1283,7 +1284,7 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, } } else { - CThread::sleep(0.01); + ARCH->sleep(0.01); } } diff --git a/lib/platform/IPlatform.h b/lib/platform/IPlatform.h deleted file mode 100644 index 2962f4f2..00000000 --- a/lib/platform/IPlatform.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef IPLATFORM_H -#define IPLATFORM_H - -#include "IInterface.h" -#include "CString.h" - -//! Platform dependent functions interface -/*! -This interface provides methods to query platform specific data and -perform operations that are inherently non-portable. -*/ -class IPlatform : public IInterface { -public: - typedef int (*DaemonFunc)(IPlatform*, int argc, const char** argv); - - //! Uninstall result codes - enum EResult { - kSuccess, //!< Uninstall succeeded - kFailed, //!< Uninstall failed - kAlready //!< Not installed - }; - - //! @name manipulators - //@{ - - //! Install daemon - /*! - Install a daemon. \c name is the name of the daemon passed to the - system and \c description is a short human readable description of - the daemon. \c pathname is the path to the daemon executable. - \c commandLine should \b not include the name of program as the - first argument. If \c allUsers is true then the daemon will be - installed to start at boot time, otherwise it will be installed to - start when the current user logs in. - */ - // FIXME -- throw on error? will get better error messages that way. - virtual bool installDaemon(const char* name, - const char* description, - const char* pathname, - const char* commandLine, - bool allUsers) = 0; - - //! Uninstall daemon - /*! - Uninstall a daemon. - */ - virtual EResult uninstallDaemon(const char* name, bool allUsers) = 0; - - //! Daemonize the process - /*! - Daemonize. This should call installDaemonLogger(). Returns - true iff successful. \c name is the name of the daemon. - Once daemonized, \c func is invoked and daemonize returns when - and what it does. daemonize() returns -1 on error. - - Exactly what happens when daemonizing depends on the platform. -
    -
  • unix: - Detaches from terminal. \c func gets passed one argument, the - name passed to daemonize(). -
  • win32: - Becomes a service. Argument 0 is the name of the service - and the rest are the arguments passed to StartService(). - \c func is only called when the service is actually started. - \c func must behave like a proper ServiceMain() function; in - particular, it must call RegisterServiceCtrlHandler() and - SetServiceStatus(). -
- */ - virtual int daemonize(const char* name, DaemonFunc func) = 0; - - //! Install daemon logger - /*! - Directs CLog to send messages to the daemon log. Used when - messages should no longer go to the console. \c name is used - in the log to identify this process. - */ - virtual void installDaemonLogger(const char* name) = 0; - - //@} - //! @name accessors - //@{ - - //! Check if user has permission to install the daemon - /*! - Returns true iff the caller has permission to install or - uninstall the daemon. Note that even if this method returns - true it's possible that installing/uninstalling the service - may still fail. This method ignores whether or not the - service is already installed. - */ - virtual bool canInstallDaemon(const char* name, - bool allUsers) const = 0; - - //! Check if the daemon is installed - /*! - Returns true iff the daemon is installed. - */ - virtual bool isDaemonInstalled(const char* name, - bool allUsers) const = 0; - - //! Extract base name - /*! - Find the base name in the given \c pathname. - */ - virtual const char* getBasename(const char* pathname) const = 0; - - //! Get user's home directory - /*! - Returns the user's home directory. Returns the empty string if - this cannot be determined. - */ - virtual CString getUserDirectory() const = 0; - - //! Get system directory - /*! - Returns the ussystem configuration file directory. - */ - virtual CString getSystemDirectory() const = 0; - - //! Concatenate path components - /*! - Concatenate pathname components with a directory separator - between them. This should not check if the resulting path - is longer than allowed by the system; we'll rely on the - system calls to tell us that. - */ - virtual CString addPathComponent( - const CString& prefix, - const CString& suffix) const = 0; - - //@} -}; - -#endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 53511b5d..e6c6d6b6 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -26,7 +26,6 @@ EXTRA_DIST = \ CMSWindowsScreen.cpp \ CMSWindowsScreenSaver.cpp \ CSynergyHook.cpp \ - CWin32Platform.cpp \ CMSWindowsClipboard.h \ CMSWindowsClipboardAnyTextConverter.h \ CMSWindowsClipboardTextConverter.h \ @@ -34,7 +33,6 @@ EXTRA_DIST = \ CMSWindowsScreen.h \ CMSWindowsScreenSaver.h \ CSynergyHook.h \ - CWin32Platform.h \ IMSWindowsScreenEventHandler.h \ $(NULL) @@ -44,7 +42,6 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libplatform.a libplatform_a_SOURCES = \ - CPlatform.cpp \ CXWindowsClipboard.cpp \ CXWindowsClipboardTextConverter.cpp \ CXWindowsClipboardUCS2Converter.cpp \ @@ -52,8 +49,6 @@ libplatform_a_SOURCES = \ CXWindowsScreen.cpp \ CXWindowsScreenSaver.cpp \ CXWindowsUtil.cpp \ - CPlatform.h \ - CUnixPlatform.h \ CXWindowsClipboard.h \ CXWindowsClipboardTextConverter.h \ CXWindowsClipboardUCS2Converter.h \ @@ -61,12 +56,10 @@ libplatform_a_SOURCES = \ CXWindowsScreen.h \ CXWindowsScreenSaver.h \ CXWindowsUtil.h \ - IPlatform.h \ - $(NULL) -EXTRA_libplatform_a_SOURCES = \ - CUnixPlatform.cpp \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index b05d246e..300f12cb 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -109,10 +109,6 @@ SOURCE=.\CMSWindowsScreen.cpp SOURCE=.\CMSWindowsScreenSaver.cpp # End Source File -# Begin Source File - -SOURCE=.\CPlatform.cpp -# End Source File # End Group # Begin Group "Header Files" @@ -143,33 +139,8 @@ SOURCE=.\CMSWindowsScreenSaver.h # End Source File # Begin Source File -SOURCE=.\CPlatform.h -# End Source File -# Begin Source File - -SOURCE=.\CWin32Platform.h -# End Source File -# Begin Source File - SOURCE=.\IMSWindowsScreenEventHandler.h # End Source File -# Begin Source File - -SOURCE=.\IPlatform.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# Begin Group "Included Files" - -# PROP Default_Filter "inc" -# Begin Source File - -SOURCE=.\CWin32Platform.cpp -# PROP Exclude_From_Build 1 -# End Source File # End Group # End Target # End Project diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp index a7735c3e..cec6eaf9 100644 --- a/lib/platform/synrgyhk.dsp +++ b/lib/platform/synrgyhk.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -71,7 +71,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\synergy" /I "..\base" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -82,7 +82,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"" /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /nodefaultlib !ENDIF diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index 6fce3dda..1e29b8a7 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -18,6 +18,7 @@ #include "OptionTypes.h" #include "ProtocolTypes.h" #include "CNetworkAddress.h" +#include "CStringUtil.h" #include "XBase.h" #include "stdmap.h" #include "stdset.h" diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/server/CMSWindowsPrimaryScreen.cpp index 3e43ca51..56143f30 100644 --- a/lib/server/CMSWindowsPrimaryScreen.cpp +++ b/lib/server/CMSWindowsPrimaryScreen.cpp @@ -15,9 +15,9 @@ #include "CMSWindowsPrimaryScreen.h" #include "CMSWindowsScreen.h" #include "IPrimaryScreenReceiver.h" -#include "CPlatform.h" #include "XScreen.h" #include "CLog.h" +#include "CArchMiscWindows.h" #include // @@ -29,7 +29,7 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( IPrimaryScreenReceiver* primaryReceiver) : CPrimaryScreen(receiver), m_receiver(primaryReceiver), - m_is95Family(CPlatform::isWindows95Family()), + m_is95Family(CArchMiscWindows::isWindows95Family()), m_threadID(0), m_window(NULL), m_mark(0), diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index e08db389..0e101e9d 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -38,6 +38,7 @@ #include "CLog.h" #include "CStopwatch.h" #include "TMethodJob.h" +#include "CArch.h" // // CServer @@ -1074,7 +1075,7 @@ CServer::closeClients(const CConfig& config) // wait a moment to allow each client to close its connection // before we close it (to avoid having our socket enter TIME_WAIT). if (threads.size() > 0) { - CThread::sleep(1.0); + ARCH->sleep(1.0); } // cancel the old client threads @@ -1103,10 +1104,10 @@ CServer::startThread(IJob* job) // reap completed threads doReapThreads(m_threads); - // add new thread to list. use the job as user data for logging. - CThread thread(job, job); + // add new thread to list + CThread thread(job); m_threads.push_back(thread); - LOG((CLOG_DEBUG1 "started thread %p", thread.getUserData())); + LOG((CLOG_DEBUG1 "started thread 0x%08x", thread.getID())); return thread; } @@ -1152,13 +1153,13 @@ CServer::stopThreads(double timeout) CStopwatch timer(true); while (threads.size() > 0 && (timeout < 0.0 || timer.getTime() < timeout)) { doReapThreads(threads); - CThread::sleep(0.01); + ARCH->sleep(0.01); } // delete remaining threads for (CThreadList::iterator index = threads.begin(); index != threads.end(); ++index) { - LOG((CLOG_DEBUG1 "reaped running thread %p", index->getUserData())); + LOG((CLOG_DEBUG1 "reaped running thread 0x%08x", index->getID())); } LOG((CLOG_DEBUG1 "stopped threads")); @@ -1178,7 +1179,7 @@ CServer::doReapThreads(CThreadList& threads) index != threads.end(); ) { if (index->wait(0.0)) { // thread terminated - LOG((CLOG_DEBUG1 "reaped thread %p", index->getUserData())); + LOG((CLOG_DEBUG1 "reaped thread 0x%08x", index->getID())); index = threads.erase(index); } else { @@ -1211,7 +1212,7 @@ CServer::acceptClients(void*) break; } catch (XSocketAddressInUse& e) { - LOG((CLOG_WARN "bind failed: %s", e.getErrstr())); + LOG((CLOG_WARN "bind failed: %s", e.what())); // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { @@ -1220,7 +1221,7 @@ CServer::acceptClients(void*) } // wait a bit before retrying - CThread::sleep(5.0); + ARCH->sleep(5.0); } } @@ -1489,7 +1490,7 @@ CServer::acceptHTTPClients(void*) break; } catch (XSocketBind& e) { - LOG((CLOG_DEBUG1 "bind HTTP failed: %s", e.getErrstr())); + LOG((CLOG_DEBUG1 "bind HTTP failed: %s", e.what())); // give up if we've waited too long if (timer.getTime() >= m_bindTimeout) { @@ -1498,7 +1499,7 @@ CServer::acceptHTTPClients(void*) } // wait a bit before retrying - CThread::sleep(5.0); + ARCH->sleep(5.0); } } @@ -1550,7 +1551,7 @@ CServer::processHTTPRequest(void* vsocket) socket->getOutputStream()->flush(); // wait a moment to give the client a chance to hangup first - CThread::sleep(3.0); + ARCH->sleep(3.0); // clean up socket->close(); diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/server/CXWindowsPrimaryScreen.cpp index b544235b..763cf4ae 100644 --- a/lib/server/CXWindowsPrimaryScreen.cpp +++ b/lib/server/CXWindowsPrimaryScreen.cpp @@ -29,6 +29,7 @@ # define XK_XKB_KEYS # include #endif +#include "CArch.h" // // CXWindowsPrimaryScreen @@ -517,7 +518,7 @@ CXWindowsPrimaryScreen::showWindow() assert(result != GrabNotViewable); if (result != GrabSuccess) { LOG((CLOG_DEBUG2 "waiting to grab keyboard")); - CThread::sleep(0.05); + ARCH->sleep(0.05); if (timer.getTime() >= s_timeout) { LOG((CLOG_DEBUG2 "grab keyboard timed out")); XUnmapWindow(display, m_window); @@ -536,7 +537,7 @@ CXWindowsPrimaryScreen::showWindow() // back off to avoid grab deadlock XUngrabKeyboard(display, CurrentTime); LOG((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); - CThread::sleep(0.05); + ARCH->sleep(0.05); if (timer.getTime() >= s_timeout) { LOG((CLOG_DEBUG2 "grab pointer timed out")); XUnmapWindow(display, m_window); diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index e874aa37..5f4daf73 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -46,6 +46,8 @@ libserver_a_SOURCES = \ IPrimaryScreenFactory.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/server/server.dsp b/lib/server/server.dsp index fa732ab7..26134e1c 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /W4 /GX /O2 /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index f7e5d64c..c86a8b34 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -52,6 +52,8 @@ libsynergy_a_SOURCES = \ XSynergy.h \ $(NULL) INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ diff --git a/lib/synergy/XScreen.h b/lib/synergy/XScreen.h index 3bfa7eb2..9966e6cf 100644 --- a/lib/synergy/XScreen.h +++ b/lib/synergy/XScreen.h @@ -18,16 +18,13 @@ #include "XBase.h" //! Generic screen exception -class XScreen : public XBase { }; +XBASE_SUBCLASS(XScreen, XBase); //! Cannot open screen exception /*! Thrown when a screen cannot be opened or initialized. */ -class XScreenOpenFailure : public XScreen { -protected: - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_WHAT(XScreenOpenFailure, XScreen); //! Screen unavailable exception /*! diff --git a/lib/synergy/XSynergy.cpp b/lib/synergy/XSynergy.cpp index c50d542f..1e19945f 100644 --- a/lib/synergy/XSynergy.cpp +++ b/lib/synergy/XSynergy.cpp @@ -13,6 +13,7 @@ */ #include "XSynergy.h" +#include "CStringUtil.h" // // XBadClient diff --git a/lib/synergy/XSynergy.h b/lib/synergy/XSynergy.h index eb5b5167..23131194 100644 --- a/lib/synergy/XSynergy.h +++ b/lib/synergy/XSynergy.h @@ -18,16 +18,13 @@ #include "XBase.h" //! Generic synergy exception -class XSynergy : public XBase { }; +XBASE_SUBCLASS(XSynergy, XBase); //! Client error exception /*! Thrown when the client fails to follow the protocol. */ -class XBadClient : public XSynergy { -protected: - virtual CString getWhat() const throw(); -}; +XBASE_SUBCLASS_WHAT(XBadClient, XSynergy); //! Incompatible client exception /*! diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index b53b957d..3b121f79 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -4,7 +4,7 @@ # TARGTYPE "Win32 (x86) Static Library" 0x0104 -CFG=synergy - Win32 Debug +CFG=libsynergy - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE @@ -13,7 +13,7 @@ CFG=synergy - Win32 Debug !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 "libsynergy.mak" CFG="libsynergy - Win32 Debug" +!MESSAGE NMAKE /f "libsynergy.mak" CFG="libsynergy - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /W4 /GX /O2 /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/synergy.dsw b/synergy.dsw index baae1dce..a70a4d76 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -24,6 +24,18 @@ Package=<4> ############################################################################### +Project: "arch"=.\lib\arch\arch.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "base"=.\lib\base\base.dsp - Package Owner=<4> Package=<5> @@ -48,6 +60,18 @@ Package=<4> ############################################################################### +Project: "common"=.\lib\common\common.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "http"=.\lib\HTTP\http.dsp - Package Owner=<4> Package=<5> @@ -80,6 +104,12 @@ Package=<5> Package=<4> {{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency Begin Project Dependency Project_Dep_Name base End Project Dependency @@ -188,6 +218,12 @@ Package=<5> Package=<4> {{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency Begin Project Dependency Project_Dep_Name base End Project Dependency @@ -221,6 +257,12 @@ Package=<5> Package=<4> {{{ + Begin Project Dependency + Project_Dep_Name common + End Project Dependency + Begin Project Dependency + Project_Dep_Name arch + End Project Dependency Begin Project Dependency Project_Dep_Name base End Project Dependency From e9cc0b434e49bee180cfd609485d6164e06c90ce Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 5 Jan 2003 21:48:54 +0000 Subject: [PATCH 407/807] Moved CPrimaryScreen and CSecondaryScreen to the lib/synergy and the platform specific implementations to lib/platform. Added an lib/arch method to query the platform's native wide character encoding and changed CUnicode to use it. All platform dependent code is now in lib/arch, lib/platform, and the programs under cmd. Also added more documentation. --- PORTING | 57 +++++- cmd/synergyc/synergyc.cpp | 5 + cmd/synergys/synergys.cpp | 5 + doc/doxygen.cfg.in | 2 +- lib/arch/CArch.cpp | 8 + lib/arch/CArch.h | 21 ++ lib/arch/CArchConsoleUnix.h | 1 + lib/arch/CArchConsoleWindows.h | 1 + lib/arch/CArchDaemonNone.h | 7 + lib/arch/CArchDaemonUnix.h | 1 + lib/arch/CArchDaemonWindows.h | 1 + lib/arch/CArchFileUnix.h | 1 + lib/arch/CArchFileWindows.h | 1 + lib/arch/CArchLogUnix.h | 1 + lib/arch/CArchLogWindows.h | 1 + lib/arch/CArchMiscWindows.h | 1 + lib/arch/CArchMultithreadPosix.h | 1 + lib/arch/CArchMultithreadWindows.cpp | 2 +- lib/arch/CArchMultithreadWindows.h | 1 + lib/arch/CArchNetworkBSD.h | 1 + lib/arch/CArchNetworkWinsock.cpp | 4 +- lib/arch/CArchNetworkWinsock.h | 1 + lib/arch/CArchSleepUnix.h | 1 + lib/arch/CArchSleepWindows.h | 1 + lib/arch/CArchStringUnix.cpp | 6 + lib/arch/CArchStringUnix.h | 3 + lib/arch/CArchStringWindows.cpp | 6 + lib/arch/CArchStringWindows.h | 3 + lib/arch/CArchTimeUnix.h | 1 + lib/arch/CArchTimeWindows.h | 1 + lib/arch/IArchConsole.h | 5 + lib/arch/IArchFile.h | 5 + lib/arch/IArchLog.h | 17 +- lib/arch/IArchMultithread.h | 190 +++++++++++++++++- lib/arch/IArchNetwork.h | 189 ++++++++++++++++- lib/arch/IArchSleep.h | 5 + lib/arch/IArchString.h | 32 +++ lib/arch/IArchTime.h | 5 + lib/arch/Makefile.am | 1 - lib/arch/XArchImpl.h | 25 --- lib/arch/arch.dsp | 4 - lib/base/CLog.cpp | 7 +- lib/base/CUnicode.cpp | 54 +++-- lib/base/XBase.cpp | 13 -- lib/base/XBase.h | 9 +- lib/client/Makefile.am | 7 - lib/client/client.dsp | 20 -- lib/http/XHTTP.h | 1 + .../CMSWindowsPrimaryScreen.cpp | 0 .../CMSWindowsPrimaryScreen.h | 0 lib/platform/CMSWindowsScreen.h | 2 +- .../CMSWindowsSecondaryScreen.cpp | 0 .../CMSWindowsSecondaryScreen.h | 0 .../CXWindowsPrimaryScreen.cpp | 0 .../CXWindowsPrimaryScreen.h | 0 lib/platform/CXWindowsScreen.h | 8 +- .../CXWindowsSecondaryScreen.cpp | 0 .../CXWindowsSecondaryScreen.h | 0 lib/platform/Makefile.am | 8 + lib/platform/platform.dsp | 16 ++ lib/server/Makefile.am | 7 - lib/server/server.dsp | 20 -- lib/{server => synergy}/CPrimaryScreen.cpp | 0 lib/{server => synergy}/CPrimaryScreen.h | 0 lib/{client => synergy}/CSecondaryScreen.cpp | 0 lib/{client => synergy}/CSecondaryScreen.h | 0 .../IPrimaryScreenFactory.h | 0 .../ISecondaryScreenFactory.h | 0 lib/synergy/Makefile.am | 8 +- lib/synergy/libsynergy.dsp | 24 +++ 70 files changed, 672 insertions(+), 156 deletions(-) delete mode 100644 lib/arch/XArchImpl.h rename lib/{server => platform}/CMSWindowsPrimaryScreen.cpp (100%) rename lib/{server => platform}/CMSWindowsPrimaryScreen.h (100%) rename lib/{client => platform}/CMSWindowsSecondaryScreen.cpp (100%) rename lib/{client => platform}/CMSWindowsSecondaryScreen.h (100%) rename lib/{server => platform}/CXWindowsPrimaryScreen.cpp (100%) rename lib/{server => platform}/CXWindowsPrimaryScreen.h (100%) rename lib/{client => platform}/CXWindowsSecondaryScreen.cpp (100%) rename lib/{client => platform}/CXWindowsSecondaryScreen.h (100%) rename lib/{server => synergy}/CPrimaryScreen.cpp (100%) rename lib/{server => synergy}/CPrimaryScreen.h (100%) rename lib/{client => synergy}/CSecondaryScreen.cpp (100%) rename lib/{client => synergy}/CSecondaryScreen.h (100%) rename lib/{server => synergy}/IPrimaryScreenFactory.h (100%) rename lib/{client => synergy}/ISecondaryScreenFactory.h (100%) diff --git a/PORTING b/PORTING index 95aad05d..8a61f141 100644 --- a/PORTING +++ b/PORTING @@ -301,14 +301,26 @@ FIXME -- high level overview of class relationships Portability ----------- +Synergy is mostly platform independent code but necessarily has +platform dependent parts. The mundane platform dependent parts +come from the usual suspects: networking, multithreading, file +system, high resolution clocks, system logging, etc. Porting +these parts is relatively straightforward. + +Synergy also has more esoteric platform dependent code. The +functions for low-level event interception and insertion, +warping the cursor position, character to keyboard event +translation, clipboard manipulation, and screen saver control +are often obscure and poorly documented. Unfortunately, these +are exactly the functions synergy requires to do its magic. + Porting synergy to a new platform requires the following steps: - Setting up the build - Adjusting lib/common/common.h - Implementing lib/arch - Implementing lib/platform -- Implementing primary and secondary screens for the platform -- Tweaking synergyc and synergys +- Tweaks Setting up the build: @@ -330,22 +342,49 @@ common.h. Implementing lib/arch: -Most platform dependent code lives in lib/arch. There are several +Much platform dependent code lives in lib/arch. There are several interface classes there and they must all be implemented for each platform. See the interface header files for more information. +Platforms requiring special functions should create a class named +CArchMiscXXX where XXX is the platform name. The class should have +only static methods. Clients can include the appropriate header +file and make calls directly, surrounded by a suitable #ifdef/#endif. + Implementing lib/platform: Most of the remaining platform dependent code lives in lib/platform. The code there implements platform dependent window, clipboard, and -screen saver handling. The following interfaces must be implemented: +screen saver handling. If a platform is named XXX then the following +classes should be derived and implemented: -FIXME + * CXXXClipboard : IClipboard + Provides clipboard operations. Typically, this class will + have helper classes for converting between various clipboard + data formats. -Implementing primary and secondary screens for the platform: + * CXXXScreen : IScreen + Provide screen operations common to the server and clients. + The CXXXPrimaryScreen and CXXXSecondaryScreen classes use a + CXXXScreen. -FIXME + * CXXXPrimaryScreen : CPrimaryScreen, IScreenEventHandler + Provides server screen operations. -Tweaking synergyc and synergys: + * CXXXSecondaryScreen : CSecondaryScreen, IScreenEventHandler + Provides client screen operations. -FIXME + * CXXXScreenSaver : IScreenSaver + Provides screen saver operations. + +Tweaks: + +Finally, each platform typically requires various adjustments here +and there. In particular, synergyc.cpp and synergys.cpp usually +require platform dependent code for the main entry point, parsing +arguments, and reporting errors. Also, some platforms may benefit +from a graphical user interface front end. These are generally +not portable and synergy doesn't provide any infrastructure for +the code common to any platform, though it may do so someday. +There is, however, an implementation of a GUI front end for Windows +that serves as an example. diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index a6c03c2a..2d1f89a2 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -86,6 +86,11 @@ CArgs* CArgs::s_instance = NULL; // platform dependent factories // +//! Factory for creating secondary screens +/*! +Objects of this type create secondary screens appropriate for the +platform. +*/ class CSecondaryScreenFactory : public ISecondaryScreenFactory { public: CSecondaryScreenFactory() { } diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index edc09560..285a7e58 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -96,6 +96,11 @@ CArgs* CArgs::s_instance = NULL; // platform dependent factories // +//! Factory for creating primary screens +/*! +Objects of this type create primary screens appropriate for the +platform. +*/ class CPrimaryScreenFactory : public IPrimaryScreenFactory { public: CPrimaryScreenFactory() { } diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index 30ce8fa3..4abf52f9 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -679,7 +679,7 @@ ENABLE_PREPROCESSING = YES # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. -MACRO_EXPANSION = NO +MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index e5e8f660..6eb99e0d 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -130,6 +130,8 @@ CArch::~CArch() CArch* CArch::getInstance() { + assert(s_instance != NULL); + return s_instance; } @@ -569,6 +571,12 @@ CArch::convWCToMB(char* dst, wchar_t src, CArchMBState state) return m_string->convWCToMB(dst, src, state); } +IArchString::EWideCharEncoding +CArch::getWideCharEncoding() +{ + return m_string->getWideCharEncoding(); +} + double CArch::time() { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index e12e33af..e459658f 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -25,10 +25,24 @@ #include "IArchString.h" #include "IArchTime.h" +/*! +\def ARCH +This macro evaluates to the singleton CArch object. +*/ #define ARCH (CArch::getInstance()) #define ARCH_ARGS +//! Delegating mplementation of architecture dependent interfaces +/*! +This class is a centralized interface to all architecture dependent +interface implementations (except miscellaneous functions). It +instantiates an implementation of each interface and delegates calls +to each method to those implementations. Clients should use the +\c ARCH macro to access this object. Clients must also instantiate +exactly one of these objects before attempting to call any method, +typically at the beginning of \c main(). +*/ class CArch : public IArchConsole, public IArchDaemon, public IArchFile, @@ -46,6 +60,11 @@ public: // accessors // + //! Return the singleton instance + /*! + The client must have instantiated exactly once CArch object before + calling this function. + */ static CArch* getInstance(); // IArchConsole overrides @@ -142,6 +161,8 @@ public: virtual bool isInitMBState(CArchMBState); virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual EWideCharEncoding + getWideCharEncoding(); // IArchTime overrides virtual double time(); diff --git a/lib/arch/CArchConsoleUnix.h b/lib/arch/CArchConsoleUnix.h index 5beb8799..5e560fec 100644 --- a/lib/arch/CArchConsoleUnix.h +++ b/lib/arch/CArchConsoleUnix.h @@ -19,6 +19,7 @@ #define ARCH_CONSOLE CArchConsoleUnix +//! Unix implementation of IArchConsole class CArchConsoleUnix : public IArchConsole { public: CArchConsoleUnix(); diff --git a/lib/arch/CArchConsoleWindows.h b/lib/arch/CArchConsoleWindows.h index d93555b4..e51f6616 100644 --- a/lib/arch/CArchConsoleWindows.h +++ b/lib/arch/CArchConsoleWindows.h @@ -23,6 +23,7 @@ #define ARCH_CONSOLE CArchConsoleWindows +//! Win32 implementation of IArchConsole class CArchConsoleWindows : public IArchConsole { public: CArchConsoleWindows(); diff --git a/lib/arch/CArchDaemonNone.h b/lib/arch/CArchDaemonNone.h index 8e017bdb..5b540607 100644 --- a/lib/arch/CArchDaemonNone.h +++ b/lib/arch/CArchDaemonNone.h @@ -19,6 +19,13 @@ #define ARCH_DAEMON CArchDaemonNone +//! Dummy implementation of IArchDaemon +/*! +This class implements IArchDaemon for a platform that does not have +daemons. The install and uninstall functions do nothing, the query +functions return false, and \c daemonize() simply calls the passed +function and returns its result. +*/ class CArchDaemonNone : public IArchDaemon { public: CArchDaemonNone(); diff --git a/lib/arch/CArchDaemonUnix.h b/lib/arch/CArchDaemonUnix.h index b4c82daa..923004e1 100644 --- a/lib/arch/CArchDaemonUnix.h +++ b/lib/arch/CArchDaemonUnix.h @@ -20,6 +20,7 @@ #undef ARCH_DAEMON #define ARCH_DAEMON CArchDaemonUnix +//! Unix implementation of IArchDaemon class CArchDaemonUnix : public CArchDaemonNone { public: CArchDaemonUnix(); diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h index aeb3916f..0feef9ba 100644 --- a/lib/arch/CArchDaemonWindows.h +++ b/lib/arch/CArchDaemonWindows.h @@ -25,6 +25,7 @@ #define ARCH_DAEMON CArchDaemonWindows +//! Win32 implementation of IArchDaemon class CArchDaemonWindows : public IArchDaemon { public: typedef int (*RunFunc)(void); diff --git a/lib/arch/CArchFileUnix.h b/lib/arch/CArchFileUnix.h index c6257555..41d00e90 100644 --- a/lib/arch/CArchFileUnix.h +++ b/lib/arch/CArchFileUnix.h @@ -19,6 +19,7 @@ #define ARCH_FILE CArchFileUnix +//! Unix implementation of IArchFile class CArchFileUnix : public IArchFile { public: CArchFileUnix(); diff --git a/lib/arch/CArchFileWindows.h b/lib/arch/CArchFileWindows.h index 44263b38..617b1c40 100644 --- a/lib/arch/CArchFileWindows.h +++ b/lib/arch/CArchFileWindows.h @@ -19,6 +19,7 @@ #define ARCH_FILE CArchFileWindows +//! Win32 implementation of IArchFile class CArchFileWindows : public IArchFile { public: CArchFileWindows(); diff --git a/lib/arch/CArchLogUnix.h b/lib/arch/CArchLogUnix.h index 107d6891..b3717844 100644 --- a/lib/arch/CArchLogUnix.h +++ b/lib/arch/CArchLogUnix.h @@ -19,6 +19,7 @@ #define ARCH_LOG CArchLogUnix +//! Unix implementation of IArchLog class CArchLogUnix : public IArchLog { public: CArchLogUnix(); diff --git a/lib/arch/CArchLogWindows.h b/lib/arch/CArchLogWindows.h index 1c077643..d2385765 100644 --- a/lib/arch/CArchLogWindows.h +++ b/lib/arch/CArchLogWindows.h @@ -22,6 +22,7 @@ #define ARCH_LOG CArchLogWindows +//! Win32 implementation of IArchLog class CArchLogWindows : public IArchLog { public: CArchLogWindows(); diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index af1a2c78..cce90ea6 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -15,6 +15,7 @@ #ifndef CARCHMISCWINDOWS_H #define CARCHMISCWINDOWS_H +//! Miscellaneous win32 functions. class CArchMiscWindows { public: typedef int (*RunFunc)(void); diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h index cc67a226..59d14155 100644 --- a/lib/arch/CArchMultithreadPosix.h +++ b/lib/arch/CArchMultithreadPosix.h @@ -31,6 +31,7 @@ public: pthread_mutex_t m_mutex; }; +//! Posix implementation of IArchMultithread class CArchMultithreadPosix : public IArchMultithread { public: CArchMultithreadPosix(); diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index 3a498135..fdeff0fc 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -575,7 +575,7 @@ CArchMultithreadWindows::refThread(CArchThreadImpl* thread) } void -CArchMultithreadWindows::testCancelThreadImpl(CArchThread thread) +CArchMultithreadWindows::testCancelThreadImpl(CArchThreadImpl* thread) { assert(thread != NULL); diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h index 74b2d36b..f6940ca5 100644 --- a/lib/arch/CArchMultithreadWindows.h +++ b/lib/arch/CArchMultithreadWindows.h @@ -37,6 +37,7 @@ public: CRITICAL_SECTION m_mutex; }; +//! Win32 implementation of IArchMultithread class CArchMultithreadWindows : public IArchMultithread { public: CArchMultithreadWindows(); diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index 2d2eda76..23d971bc 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -50,6 +50,7 @@ public: socklen_t m_len; }; +//! Berkeley (BSD) sockets implementation of IArchNetwork class CArchNetworkBSD : public IArchNetwork { public: CArchNetworkBSD(); diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index 10e40188..1c10fb57 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -35,7 +35,7 @@ static int (PASCAL FAR *getsockopt_winsock)(SOCKET s, int level, int optname, vo static u_short (PASCAL FAR *htons_winsock)(u_short v); static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in); static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp); -static int (PASCAL FAR *ioctl_winsock)(SOCKET s, int cmd, void FAR *); +static int (PASCAL FAR *ioctl_winsock)(SOCKET s, int cmd, void FAR * data); static int (PASCAL FAR *listen_winsock)(SOCKET s, int backlog); static u_short (PASCAL FAR *ntohs_winsock)(u_short v); static int (PASCAL FAR *recv_winsock)(SOCKET s, void FAR * buf, int len, int flags); @@ -47,7 +47,7 @@ static SOCKET (PASCAL FAR *socket_winsock)(int af, int type, int protocol); static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type); static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); static int (PASCAL FAR *WSACleanup_winsock)(void); -static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR *); +static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR * fdset); #undef FD_ISSET #define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) diff --git a/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h index d16b7f48..58ce9cad 100644 --- a/lib/arch/CArchNetworkWinsock.h +++ b/lib/arch/CArchNetworkWinsock.h @@ -44,6 +44,7 @@ public: int m_len; }; +//! Win32 implementation of IArchNetwork class CArchNetworkWinsock : public IArchNetwork { public: CArchNetworkWinsock(); diff --git a/lib/arch/CArchSleepUnix.h b/lib/arch/CArchSleepUnix.h index deb33629..939ca401 100644 --- a/lib/arch/CArchSleepUnix.h +++ b/lib/arch/CArchSleepUnix.h @@ -19,6 +19,7 @@ #define ARCH_SLEEP CArchSleepUnix +//! Unix implementation of IArchSleep class CArchSleepUnix : public IArchSleep { public: CArchSleepUnix(); diff --git a/lib/arch/CArchSleepWindows.h b/lib/arch/CArchSleepWindows.h index a142d997..a5a5fa90 100644 --- a/lib/arch/CArchSleepWindows.h +++ b/lib/arch/CArchSleepWindows.h @@ -19,6 +19,7 @@ #define ARCH_SLEEP CArchSleepWindows +//! Win32 implementation of IArchSleep class CArchSleepWindows : public IArchSleep { public: CArchSleepWindows(); diff --git a/lib/arch/CArchStringUnix.cpp b/lib/arch/CArchStringUnix.cpp index 385bb0aa..32b2ad46 100644 --- a/lib/arch/CArchStringUnix.cpp +++ b/lib/arch/CArchStringUnix.cpp @@ -32,3 +32,9 @@ CArchStringUnix::~CArchStringUnix() } #include "vsnprintf.cpp" + +IArchString::EWideCharEncoding +CArchStringUnix::getWideCharEncoding() +{ + return kUCS4; +} diff --git a/lib/arch/CArchStringUnix.h b/lib/arch/CArchStringUnix.h index fcd114f9..b9fddcb3 100644 --- a/lib/arch/CArchStringUnix.h +++ b/lib/arch/CArchStringUnix.h @@ -19,6 +19,7 @@ #define ARCH_STRING CArchStringUnix +//! Unix implementation of IArchString class CArchStringUnix : public IArchString { public: CArchStringUnix(); @@ -33,6 +34,8 @@ public: virtual bool isInitMBState(CArchMBState); virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual EWideCharEncoding + getWideCharEncoding(); }; #endif diff --git a/lib/arch/CArchStringWindows.cpp b/lib/arch/CArchStringWindows.cpp index a0d8727d..4aefda2e 100644 --- a/lib/arch/CArchStringWindows.cpp +++ b/lib/arch/CArchStringWindows.cpp @@ -36,3 +36,9 @@ CArchStringWindows::~CArchStringWindows() #define HAVE_VSNPRINTF 1 #define ARCH_VSNPRINTF _vsnprintf #include "vsnprintf.cpp" + +IArchString::EWideCharEncoding +CArchStringWindows::getWideCharEncoding() +{ + return kUTF16; +} diff --git a/lib/arch/CArchStringWindows.h b/lib/arch/CArchStringWindows.h index 54b77ca3..c1303522 100644 --- a/lib/arch/CArchStringWindows.h +++ b/lib/arch/CArchStringWindows.h @@ -19,6 +19,7 @@ #define ARCH_STRING CArchStringWindows +//! Win32 implementation of IArchString class CArchStringWindows : public IArchString { public: CArchStringWindows(); @@ -33,6 +34,8 @@ public: virtual bool isInitMBState(CArchMBState); virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual EWideCharEncoding + getWideCharEncoding(); }; #endif diff --git a/lib/arch/CArchTimeUnix.h b/lib/arch/CArchTimeUnix.h index c9b99999..78c6ff6f 100644 --- a/lib/arch/CArchTimeUnix.h +++ b/lib/arch/CArchTimeUnix.h @@ -19,6 +19,7 @@ #define ARCH_TIME CArchTimeUnix +//! Generic Unix implementation of IArchTime class CArchTimeUnix : public IArchTime { public: CArchTimeUnix(); diff --git a/lib/arch/CArchTimeWindows.h b/lib/arch/CArchTimeWindows.h index 0672ad78..fb9b1e9f 100644 --- a/lib/arch/CArchTimeWindows.h +++ b/lib/arch/CArchTimeWindows.h @@ -19,6 +19,7 @@ #define ARCH_TIME CArchTimeWindows +//! Win32 implementation of IArchTime class CArchTimeWindows : public IArchTime { public: CArchTimeWindows(); diff --git a/lib/arch/IArchConsole.h b/lib/arch/IArchConsole.h index 29c3f7bd..c9da4100 100644 --- a/lib/arch/IArchConsole.h +++ b/lib/arch/IArchConsole.h @@ -17,6 +17,11 @@ #include "IInterface.h" +//! Interface for architecture dependent console output +/*! +This interface defines the console operations required by +synergy. Each architecture must implement this interface. +*/ class IArchConsole : public IInterface { public: //! @name manipulators diff --git a/lib/arch/IArchFile.h b/lib/arch/IArchFile.h index 90b93649..8594053d 100644 --- a/lib/arch/IArchFile.h +++ b/lib/arch/IArchFile.h @@ -18,6 +18,11 @@ #include "IInterface.h" #include "stdstring.h" +//! Interface for architecture dependent file system operations +/*! +This interface defines the file system operations required by +synergy. Each architecture must implement this interface. +*/ class IArchFile : public IInterface { public: //! @name manipulators diff --git a/lib/arch/IArchLog.h b/lib/arch/IArchLog.h index dc818ebb..0c259187 100644 --- a/lib/arch/IArchLog.h +++ b/lib/arch/IArchLog.h @@ -17,9 +17,24 @@ #include "IInterface.h" +//! Interface for architecture dependent logging +/*! +This interface defines the logging operations required by +synergy. Each architecture must implement this interface. +*/ class IArchLog : public IInterface { public: - enum ELevel { kERROR, kWARNING, kNOTE, kINFO, kDEBUG }; + //! Log levels + /*! + The logging priority levels in order of highest to lowest priority. + */ + enum ELevel { + kERROR, //!< For serious or fatal errors + kWARNING, //!< For minor errors and warnings + kNOTE, //!< For messages about notable events + kINFO, //!< For informational messages + kDEBUG //!< For debugging messages + }; //! @name manipulators //@{ diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index a3b2efd2..05abd6be 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -17,11 +17,47 @@ #include "IInterface.h" +/*! +\class CArchCondImpl +\brief Internal condition variable data. +An architecture dependent type holding the necessary data for a +condition variable. +*/ class CArchCondImpl; -class CArchMutexImpl; -class CArchThreadImpl; + +/*! +\var CArchCond +\brief Opaque condition variable type. +An opaque type representing a condition variable. +*/ typedef CArchCondImpl* CArchCond; + +/*! +\class CArchMutexImpl +\brief Internal mutex data. +An architecture dependent type holding the necessary data for a mutex. +*/ +class CArchMutexImpl; + +/*! +\var CArchMutex +\brief Opaque mutex type. +An opaque type representing a mutex. +*/ typedef CArchMutexImpl* CArchMutex; + +/*! +\class CArchThreadImpl +\brief Internal thread data. +An architecture dependent type holding the necessary data for a thread. +*/ +class CArchThreadImpl; + +/*! +\var CArchThread +\brief Opaque thread type. +An opaque type representing a thread. +*/ typedef CArchThreadImpl* CArchThread; //! Interface for architecture dependent multithreading @@ -31,7 +67,9 @@ synergy. Each architecture must implement this interface. */ class IArchMultithread : public IInterface { public: + //! Type of thread entry point typedef void* (*ThreadFunc)(void*); + //! Type of thread identifier typedef unsigned int ThreadID; //! @name manipulators @@ -41,38 +79,172 @@ public: // condition variable methods // + //! Create a condition variable + /*! + The condition variable is an opaque data type. + */ virtual CArchCond newCondVar() = 0; + + //! Destroy a condition variable virtual void closeCondVar(CArchCond) = 0; + + //! Signal a condition variable + /*! + Signalling a condition variable releases one waiting thread. + */ virtual void signalCondVar(CArchCond) = 0; + + //! Broadcast a condition variable + /*! + Broadcasting a condition variable releases all waiting threads. + */ virtual void broadcastCondVar(CArchCond) = 0; + + //! Wait on a condition variable + /*! + Waiting on a conditation variable for up to \c timeout seconds. + If \c timeout is < 0 then there is no timeout. The mutex must + be locked when this method is called. The mutex is unlocked + during the wait and locked again before returning. + + (Cancellation point) + */ virtual bool waitCondVar(CArchCond, CArchMutex, double timeout) = 0; // // mutex methods // + //! Create a non-recursive mutex + /*! + Creates a non-recursive mutex. A thread must not lock a + non-recursive mutex when it already holds a lock on that mutex. + If it does it will deadlock. The mutex is an opaque data type. + */ virtual CArchMutex newMutex() = 0; + + //! Destroy a mutex virtual void closeMutex(CArchMutex) = 0; + + //! Lock a mutex + /*! + (Cancellation point) + */ virtual void lockMutex(CArchMutex) = 0; + + //! Unlock a mutex virtual void unlockMutex(CArchMutex) = 0; // // thread methods // - virtual CArchThread newThread(ThreadFunc, void*) = 0; + //! Start a new thread + /*! + Creates and starts a new thread, using \c func as the entry point + and passing it \c userData. The thread is an opaque data type. + */ + virtual CArchThread newThread(ThreadFunc func, void* userData) = 0; + + //! Get a reference to the calling thread + /*! + Returns a thread representing the current (i.e. calling) thread. + */ virtual CArchThread newCurrentThread() = 0; - virtual CArchThread copyThread(CArchThread) = 0; + + //! Copy a thread object + /*! + Returns a reference to to thread referred to by \c thread. + */ + virtual CArchThread copyThread(CArchThread thread) = 0; + + //! Release a thread reference + /*! + Deletes the given thread object. This does not destroy the thread + the object referred to, even if there are no remaining references. + Use cancelThread() and waitThread() to stop a thread and wait for + it to exit. + */ virtual void closeThread(CArchThread) = 0; - virtual void cancelThread(CArchThread) = 0; + + //! Force a thread to exit + /*! + Causes \c thread to exit when it next calls a cancellation point. + A thread avoids cancellation as long as it nevers calls a + cancellation point. Once it begins the cancellation process it + must always let cancellation go to completion but may take as + long as necessary to clean up. + */ + virtual void cancelThread(CArchThread thread) = 0; + + //! Change thread priority + /*! + Changes the priority of \c thread by \c n. If \c n is positive + the thread has a lower priority and if negative a higher priority. + Some architectures may not support either or both directions. + */ virtual void setPriorityOfThread(CArchThread, int n) = 0; + + //! Cancellation point + /*! + This method does nothing but is a cancellation point. Clients + can make their own functions cancellation points by calling this + method at appropriate times. + */ virtual void testCancelThread() = 0; - virtual bool wait(CArchThread, double timeout) = 0; + + //! Wait for a thread to exit + /*! + Waits for up to \c timeout seconds for \c thread to exit (normally + or by cancellation). Waits forever if \c timeout < 0. Returns + true if the thread exited, false otherwise. Waiting on the current + thread returns immediately with false. + + (Cancellation point) + */ + virtual bool wait(CArchThread thread, double timeout) = 0; + + //! Wait for a user event + /*! + Waits for up to \c timeout seconds for a pending user event. + Returns true if an event occurred, false otherwise. + + This method is not required by all platforms. + + (Cancellation point) + */ virtual bool waitForEvent(double timeout) = 0; + + //! Compare threads + /*! + Returns true iff two thread objects refer to the same thread. + Note that comparing thread objects directly is meaningless. + */ virtual bool isSameThread(CArchThread, CArchThread) = 0; - virtual bool isExitedThread(CArchThread) = 0; - virtual void* getResultOfThread(CArchThread) = 0; - virtual ThreadID getIDOfThread(CArchThread) = 0; + + //! Test if thread exited + /*! + Returns true iff \c thread has exited. + */ + virtual bool isExitedThread(CArchThread thread) = 0; + + //! Returns the exit code of a thread + /*! + Waits indefinitely for \c thread to exit (if it hasn't yet) then + returns the thread's exit code. + + (Cancellation point) + */ + virtual void* getResultOfThread(CArchThread thread) = 0; + + //! Returns an ID for a thread + /*! + Returns some ID number for \c thread. This is for logging purposes. + All thread objects referring to the same thread return the same ID. + However, clients should us isSameThread() to compare thread objects + instead of comparing IDs. + */ + virtual ThreadID getIDOfThread(CArchThread thread) = 0; //@} }; diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h index 9bdacc71..1af8cd74 100644 --- a/lib/arch/IArchNetwork.h +++ b/lib/arch/IArchNetwork.h @@ -18,9 +18,33 @@ #include "IInterface.h" #include "stdstring.h" +/*! +\class CArchSocketImpl +\brief Internal socket data. +An architecture dependent type holding the necessary data for a socket. +*/ class CArchSocketImpl; -class CArchNetAddressImpl; + +/*! +\var CArchSocket +\brief Opaque socket type. +An opaque type representing a socket. +*/ typedef CArchSocketImpl* CArchSocket; + +/*! +\class CArchNetAddressImpl +\brief Internal network address data. +An architecture dependent type holding the necessary data for a network +address. +*/ +class CArchNetAddressImpl; + +/*! +\var CArchNetAddress +\brief Opaque network address type. +An opaque type representing a network address. +*/ typedef CArchNetAddressImpl* CArchNetAddress; //! Interface for architecture dependent networking @@ -30,47 +54,171 @@ synergy. Each architecture must implement this interface. */ class IArchNetwork : public IInterface { public: + //! Supported address families enum EAddressFamily { kUNKNOWN, kINET, }; + //! Supported socket types enum ESocketType { kDGRAM, kSTREAM }; + //! Events for \c poll() + /*! + Events for \c poll() are bitmasks and can be combined using the + bitwise operators. + */ enum { - kPOLLIN = 1, - kPOLLOUT = 2, - kPOLLERR = 4, - kPOLLNVAL = 8 + kPOLLIN = 1, //!< Socket is readable + kPOLLOUT = 2, //!< Socket is writable + kPOLLERR = 4, //!< The socket is in an error state + kPOLLNVAL = 8 //!< The socket is invalid }; + //! A socket query for \c poll() class CPollEntry { public: + //! The socket to query CArchSocket m_socket; + + //! The events to query for + /*! + The events to query for can be any combination of kPOLLIN and + kPOLLOUT. + */ unsigned short m_events; + + //! The result events unsigned short m_revents; }; //! @name manipulators //@{ + //! Create a new socket + /*! + The socket is an opaque data type. + */ virtual CArchSocket newSocket(EAddressFamily, ESocketType) = 0; + + //! Copy a socket object + /*! + Returns a reference to to socket referred to by \c s. + */ virtual CArchSocket copySocket(CArchSocket s) = 0; + + //! Release a socket reference + /*! + Deletes the given socket object. This does not destroy the socket + the object referred to until there are no remaining references for + the socket. + */ virtual void closeSocket(CArchSocket s) = 0; + + //! Close socket for further reads + /*! + Calling this disallows future reads on socket \c s. + */ virtual void closeSocketForRead(CArchSocket s) = 0; + + //! Close socket for further writes + /*! + Calling this disallows future writes on socket \c s. + */ virtual void closeSocketForWrite(CArchSocket s) = 0; + + //! Bind socket to address + /*! + Binds socket \c s to the address \c addr. + */ virtual void bindSocket(CArchSocket s, CArchNetAddress addr) = 0; + + //! Listen for connections on socket + /*! + Causes the socket \c s to begin listening for incoming connections. + */ virtual void listenOnSocket(CArchSocket s) = 0; + + //! Accept connection on socket + /*! + Accepts a connection on socket \c s, returning a new socket for the + connection and filling in \c addr with the address of the remote + end. \c addr may be NULL if the remote address isn't required. + The original socket \c s is unaffected and remains in the listening + state. The new socket shares most of the properties of \c s except + it's not in the listening state, it's connected, and is not + non-blocking even is \c s is. + + This call blocks if \c s is not non-blocking and there are no + pending connection requests. + + (Cancellation point) + */ virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr) = 0; - virtual void connectSocket(CArchSocket s, CArchNetAddress name) = 0; + + //! Connect socket + /*! + Connects the socket \c s to the remote address \c addr. This call + blocks if \c s is not non-blocking. If \c s is non-blocking then + the client can \c poll() for writability to detect a connection. + + (Cancellation point) + */ + virtual void connectSocket(CArchSocket s, CArchNetAddress addr) = 0; + + //! Check socket state + /*! + Tests the state of \c num sockets for readability and/or writability. + Waits up to \c timeout seconds for some socket to become readable + and/or writable (or indefinitely if \c timeout < 0). Returns the + number of sockets that were readable (if readability was being + queried) or writable (if writablility was being queried) and sets + the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL + are set in \c m_revents as appropriate. If a socket indicates + \c kPOLLERR then \c throwErrorOnSocket() can be used to determine + the type of error. + + (Cancellation point) + */ virtual int pollSocket(CPollEntry[], int num, double timeout) = 0; + + //! Read data from socket + /*! + Read up to \c len bytes from socket \c s in \c buf and return the + number of bytes read. The number of bytes can be less than \c len + if not enough data is available. Returns 0 if the remote end has + disconnected and there is no more queued received data. Blocks if + the socket is not non-blocking and there is no queued received data. + If non-blocking and there is no queued received data then throws + XArchNetworkWouldBlock. + + (Cancellation point) + */ virtual size_t readSocket(CArchSocket s, void* buf, size_t len) = 0; + + //! Write data from socket + /*! + Write up to \c len bytes to socket \c s from \c buf and return the + number of bytes written. The number of bytes can be less than + \c len if the remote end disconnected or the socket is non-blocking + and the internal buffers are full. If non-blocking and the internal + buffers are full before any data is written then throws + XArchNetworkWouldBlock. + + (Cancellation point) + */ virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len) = 0; - virtual void throwErrorOnSocket(CArchSocket) = 0; + + //! Check error on socket + /*! + If the socket \c s is in an error state then throws an appropriate + XArchNetwork exception. + */ + virtual void throwErrorOnSocket(CArchSocket s) = 0; //! Set socket to (non-)blocking operation /*! @@ -87,17 +235,42 @@ public: */ virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay) = 0; + //! Return local host's name virtual std::string getHostName() = 0; + + //! Create an "any" network address virtual CArchNetAddress newAnyAddr(EAddressFamily) = 0; + + //! Copy a network address virtual CArchNetAddress copyAddr(CArchNetAddress) = 0; + + //! Convert a name to a network address virtual CArchNetAddress nameToAddr(const std::string&) = 0; + + //! Destroy a network address virtual void closeAddr(CArchNetAddress) = 0; + + //! Convert an address to a host name virtual std::string addrToName(CArchNetAddress) = 0; + + //! Convert an address to a string virtual std::string addrToString(CArchNetAddress) = 0; + + //! Get an address's family virtual EAddressFamily getAddrFamily(CArchNetAddress) = 0; + + //! Set the port of an address virtual void setAddrPort(CArchNetAddress, int port) = 0; + + //! Get the port of an address virtual int getAddrPort(CArchNetAddress) = 0; - virtual bool isAnyAddr(CArchNetAddress) = 0; + + //! Test for the "any" address + /*! + Returns true if \c addr is the "any" address. \c newAnyAddr() + returns an "any" address. + */ + virtual bool isAnyAddr(CArchNetAddress addr) = 0; //@} }; diff --git a/lib/arch/IArchSleep.h b/lib/arch/IArchSleep.h index df6e88e8..95a2852c 100644 --- a/lib/arch/IArchSleep.h +++ b/lib/arch/IArchSleep.h @@ -17,6 +17,11 @@ #include "IInterface.h" +//! Interface for architecture dependent sleeping +/*! +This interface defines the sleep operations required by +synergy. Each architecture must implement this interface. +*/ class IArchSleep : public IInterface { public: //! @name manipulators diff --git a/lib/arch/IArchString.h b/lib/arch/IArchString.h index d8117c30..2d9a507d 100644 --- a/lib/arch/IArchString.h +++ b/lib/arch/IArchString.h @@ -18,11 +18,39 @@ #include "IInterface.h" #include +/*! +\class CArchMBStateImpl +\brief Internal multibyte conversion data. +An architecture dependent type holding the necessary data for a +multibyte to/from wide character conversion. +*/ class CArchMBStateImpl; + +/*! +\var CArchMBState +\brief Opaque multibyte conversion state type. +An opaque type representing multibyte conversion state. +*/ typedef CArchMBStateImpl* CArchMBState; +//! Interface for architecture dependent string operations +/*! +This interface defines the string operations required by +synergy. Each architecture must implement this interface. +*/ class IArchString : public IInterface { public: + //! Wide character encodings + /*! + The known wide character encodings + */ + enum EWideCharEncoding { + kUCS2, //!< The UCS-2 encoding + kUCS4, //!< The UCS-4 encoding + kUTF16, //!< The UTF-16 encoding + kUTF32 //!< The UTF-32 encoding + }; + //! @name manipulators //@{ @@ -55,6 +83,10 @@ public: //! Convert wide character to multibyte virtual int convWCToMB(char*, wchar_t, CArchMBState) = 0; + //! Return the architecture's native wide character encoding + virtual EWideCharEncoding + getWideCharEncoding() = 0; + //@} }; diff --git a/lib/arch/IArchTime.h b/lib/arch/IArchTime.h index b2b9a5cd..dade64bb 100644 --- a/lib/arch/IArchTime.h +++ b/lib/arch/IArchTime.h @@ -17,6 +17,11 @@ #include "IInterface.h" +//! Interface for architecture dependent time operations +/*! +This interface defines the time operations required by +synergy. Each architecture must implement this interface. +*/ class IArchTime : public IInterface { public: //! @name manipulators diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am index e495adf8..008e2169 100644 --- a/lib/arch/Makefile.am +++ b/lib/arch/Makefile.am @@ -61,7 +61,6 @@ libarch_a_SOURCES = \ IArchString.h \ IArchTime.h \ XArch.h \ - XArchImpl.h \ $(NULL) EXTRA_libarch_a_SOURCES = \ CArchConsoleUnix.cpp \ diff --git a/lib/arch/XArchImpl.h b/lib/arch/XArchImpl.h deleted file mode 100644 index 28c79721..00000000 --- a/lib/arch/XArchImpl.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef XARCHIMPL_H -#define XARCHIMPL_H - -// include appropriate architecture implementation -#if WINDOWS_LIKE -# include "XArchWindows.h" -#elif UNIX_LIKE -# include "XArchUnix.h" -#endif - -#endif diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp index 1af4c4b7..cb82663a 100644 --- a/lib/arch/arch.dsp +++ b/lib/arch/arch.dsp @@ -179,10 +179,6 @@ SOURCE=.\XArch.h # End Source File # Begin Source File -SOURCE=.\XArchImpl.h -# End Source File -# Begin Source File - SOURCE=.\XArchWindows.h # End Source File # End Group diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index 84fe9994..6b504993 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -54,12 +54,7 @@ static const int g_prioritySuffixLength = 2; static const int g_priorityPad = g_maxPriorityLength + g_prioritySuffixLength; -// -// CLogLock -// -// Convenience object to lock/unlock a mutex. -// - +//! Convenience object to lock/unlock a mutex class CLogLock { public: CLogLock(CArchMutex mutex) : m_mutex(mutex) { ARCH->lockMutex(m_mutex); } diff --git a/lib/base/CUnicode.cpp b/lib/base/CUnicode.cpp index a0233d8d..5cac46d6 100644 --- a/lib/base/CUnicode.cpp +++ b/lib/base/CUnicode.cpp @@ -414,13 +414,31 @@ wchar_t* CUnicode::UTF8ToWideChar(const CString& src, UInt32& size, bool* errors) { // convert to platform's wide character encoding -#if WINDOWS_LIKE - CString tmp = UTF8ToUTF16(src, errors); - size = tmp.size() >> 1; -#elif UNIX_LIKE - CString tmp = UTF8ToUCS4(src, errors); - size = tmp.size() >> 2; -#endif + CString tmp; + switch (ARCH->getWideCharEncoding()) { + case IArchString::kUCS2: + tmp = UTF8ToUCS2(src, errors); + size = tmp.size() >> 1; + break; + + case IArchString::kUCS4: + tmp = UTF8ToUCS4(src, errors); + size = tmp.size() >> 2; + break; + + case IArchString::kUTF16: + tmp = UTF8ToUTF16(src, errors); + size = tmp.size() >> 1; + break; + + case IArchString::kUTF32: + tmp = UTF8ToUTF32(src, errors); + size = tmp.size() >> 2; + break; + + default: + assert(0 && "unknown wide character encoding"); + } // copy to a wchar_t array wchar_t* dst = new wchar_t[size]; @@ -434,11 +452,23 @@ CUnicode::wideCharToUTF8(const wchar_t* src, UInt32 size, bool* errors) // convert from platform's wide character encoding. // note -- this must include a wide nul character (independent of // the CString's nul character). -#if WINDOWS_LIKE - return doUTF16ToUTF8(reinterpret_cast(src), size, errors); -#elif UNIX_LIKE - return doUCS4ToUTF8(reinterpret_cast(src), size, errors); -#endif + switch (ARCH->getWideCharEncoding()) { + case IArchString::kUCS2: + return doUCS2ToUTF8(reinterpret_cast(src), size, errors); + + case IArchString::kUCS4: + return doUCS4ToUTF8(reinterpret_cast(src), size, errors); + + case IArchString::kUTF16: + return doUTF16ToUTF8(reinterpret_cast(src), size, errors); + + case IArchString::kUTF32: + return doUTF32ToUTF8(reinterpret_cast(src), size, errors); + + default: + assert(0 && "unknown wide character encoding"); + return CString(); + } } CString diff --git a/lib/base/XBase.cpp b/lib/base/XBase.cpp index 20d5f464..022be306 100644 --- a/lib/base/XBase.cpp +++ b/lib/base/XBase.cpp @@ -17,30 +17,17 @@ #include #include -// win32 wants a const char* argument to std::exception c'tor -#if WINDOWS_LIKE -#include -#define STDEXCEPTARG "" -#endif - -// default to no argument -#ifndef STDEXCEPTARG -#define STDEXCEPTARG -#endif - // // XBase // XBase::XBase() : -// exception(STDEXCEPTARG), m_what() { // do nothing } XBase::XBase(const CString& msg) : -// exception(STDEXCEPTARG), m_what(msg) { // do nothing diff --git a/lib/base/XBase.h b/lib/base/XBase.h index fa93f94d..e9161d70 100644 --- a/lib/base/XBase.h +++ b/lib/base/XBase.h @@ -16,17 +16,12 @@ #define XBASE_H #include "CString.h" -/* -#include "stdpre.h" -#include -#include "stdpost.h" -*/ //! Exception base class /*! This is the base class of most exception types. */ -class XBase /*: public std::exception*/ { +class XBase { public: //! Use getWhat() as the result of what() XBase(); @@ -34,7 +29,7 @@ public: XBase(const CString& msg); virtual ~XBase(); - // std::exception overrides + //! Reason for exception virtual const char* what() const; protected: diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index 23c0e101..9d6ba463 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -17,8 +17,6 @@ VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ client.dsp \ - CMSWindowsSecondaryScreen.cpp \ - CMSWindowsSecondaryScreen.h \ $(NULL) MAINTAINERCLEANFILES = \ @@ -28,14 +26,9 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libclient.a libclient_a_SOURCES = \ CClient.cpp \ - CSecondaryScreen.cpp \ CServerProxy.cpp \ - CXWindowsSecondaryScreen.cpp \ CClient.h \ - CSecondaryScreen.h \ CServerProxy.h \ - CXWindowsSecondaryScreen.h \ - ISecondaryScreenFactory.h \ $(NULL) INCLUDES = \ -I$(VDEPTH)/lib/common \ diff --git a/lib/client/client.dsp b/lib/client/client.dsp index a9b03625..9be31758 100644 --- a/lib/client/client.dsp +++ b/lib/client/client.dsp @@ -91,14 +91,6 @@ SOURCE=.\CClient.cpp # End Source File # Begin Source File -SOURCE=.\CMSWindowsSecondaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CSecondaryScreen.cpp -# End Source File -# Begin Source File - SOURCE=.\CServerProxy.cpp # End Source File # End Group @@ -111,20 +103,8 @@ SOURCE=.\CClient.h # End Source File # Begin Source File -SOURCE=.\CMSWindowsSecondaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CSecondaryScreen.h -# End Source File -# Begin Source File - SOURCE=.\CServerProxy.h # End Source File -# Begin Source File - -SOURCE=.\ISecondaryScreenFactory.h -# End Source File # End Group # Begin Group "Resource Files" diff --git a/lib/http/XHTTP.h b/lib/http/XHTTP.h index e601600a..2bd6ed73 100644 --- a/lib/http/XHTTP.h +++ b/lib/http/XHTTP.h @@ -62,6 +62,7 @@ private: CString m_reason; }; +//! HTTP exception indicating an unsupported method class XHTTPAllow : public XHTTP { public: /*! diff --git a/lib/server/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp similarity index 100% rename from lib/server/CMSWindowsPrimaryScreen.cpp rename to lib/platform/CMSWindowsPrimaryScreen.cpp diff --git a/lib/server/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h similarity index 100% rename from lib/server/CMSWindowsPrimaryScreen.h rename to lib/platform/CMSWindowsPrimaryScreen.h diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 89565260..6183d50f 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -35,7 +35,7 @@ public: class IScreenReceiver; class IMSWindowsScreenEventHandler; -// Microsoft windows screen implementation +//! Implementation of IScreen for Microsoft Windows class CMSWindowsScreen : public IScreen { public: CMSWindowsScreen(IScreenReceiver*, IMSWindowsScreenEventHandler*); diff --git a/lib/client/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp similarity index 100% rename from lib/client/CMSWindowsSecondaryScreen.cpp rename to lib/platform/CMSWindowsSecondaryScreen.cpp diff --git a/lib/client/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h similarity index 100% rename from lib/client/CMSWindowsSecondaryScreen.h rename to lib/platform/CMSWindowsSecondaryScreen.h diff --git a/lib/server/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp similarity index 100% rename from lib/server/CXWindowsPrimaryScreen.cpp rename to lib/platform/CXWindowsPrimaryScreen.cpp diff --git a/lib/server/CXWindowsPrimaryScreen.h b/lib/platform/CXWindowsPrimaryScreen.h similarity index 100% rename from lib/server/CXWindowsPrimaryScreen.h rename to lib/platform/CXWindowsPrimaryScreen.h diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index f74b8389..a6451763 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -33,6 +33,11 @@ class IScreenReceiver; class CXWindowsClipboard; class CXWindowsScreenSaver; +/*! +\class CEvent +\brief User event data +An architecture dependent type holding user event data. +*/ // X11 event class CEvent { public: @@ -40,7 +45,7 @@ public: SInt32 m_result; }; -// X11 screen implementation +//! Implementation of IScreen for X11 class CXWindowsScreen : public IScreen { public: CXWindowsScreen(IScreenReceiver*, IScreenEventHandler*); @@ -279,6 +284,7 @@ private: static CXWindowsScreen* s_screen; }; +//! Convenience object to lock/unlock a CXWindowsScreen class CDisplayLock { public: CDisplayLock(const CXWindowsScreen*); diff --git a/lib/client/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp similarity index 100% rename from lib/client/CXWindowsSecondaryScreen.cpp rename to lib/platform/CXWindowsSecondaryScreen.cpp diff --git a/lib/client/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h similarity index 100% rename from lib/client/CXWindowsSecondaryScreen.h rename to lib/platform/CXWindowsSecondaryScreen.h diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index e6c6d6b6..ea082e15 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -23,15 +23,19 @@ EXTRA_DIST = \ CMSWindowsClipboardAnyTextConverter.cpp \ CMSWindowsClipboardTextConverter.cpp \ CMSWindowsClipboardUTF16Converter.cpp \ + CMSWindowsPrimaryScreen.cpp \ CMSWindowsScreen.cpp \ CMSWindowsScreenSaver.cpp \ + CMSWindowsSecondaryScreen.cpp \ CSynergyHook.cpp \ CMSWindowsClipboard.h \ CMSWindowsClipboardAnyTextConverter.h \ CMSWindowsClipboardTextConverter.h \ CMSWindowsClipboardUTF16Converter.h \ + CMSWindowsPrimaryScreen.h \ CMSWindowsScreen.h \ CMSWindowsScreenSaver.h \ + CMSWindowsSecondaryScreen.h \ CSynergyHook.h \ IMSWindowsScreenEventHandler.h \ $(NULL) @@ -46,15 +50,19 @@ libplatform_a_SOURCES = \ CXWindowsClipboardTextConverter.cpp \ CXWindowsClipboardUCS2Converter.cpp \ CXWindowsClipboardUTF8Converter.cpp \ + CXWindowsPrimaryScreen.cpp \ CXWindowsScreen.cpp \ CXWindowsScreenSaver.cpp \ + CXWindowsSecondaryScreen.cpp \ CXWindowsUtil.cpp \ CXWindowsClipboard.h \ CXWindowsClipboardTextConverter.h \ CXWindowsClipboardUCS2Converter.h \ CXWindowsClipboardUTF8Converter.h \ + CXWindowsPrimaryScreen.h \ CXWindowsScreen.h \ CXWindowsScreenSaver.h \ + CXWindowsSecondaryScreen.h \ CXWindowsUtil.h \ $(NULL) INCLUDES = \ diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index 300f12cb..ae599964 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -103,12 +103,20 @@ SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp # End Source File # Begin Source File +SOURCE=.\CMSWindowsPrimaryScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\CMSWindowsScreen.cpp # End Source File # Begin Source File SOURCE=.\CMSWindowsScreenSaver.cpp # End Source File +# Begin Source File + +SOURCE=.\CMSWindowsSecondaryScreen.cpp +# End Source File # End Group # Begin Group "Header Files" @@ -131,6 +139,10 @@ SOURCE=.\CMSWindowsClipboardUTF16Converter.h # End Source File # Begin Source File +SOURCE=.\CMSWindowsPrimaryScreen.h +# End Source File +# Begin Source File + SOURCE=.\CMSWindowsScreen.h # End Source File # Begin Source File @@ -139,6 +151,10 @@ SOURCE=.\CMSWindowsScreenSaver.h # End Source File # Begin Source File +SOURCE=.\CMSWindowsSecondaryScreen.h +# End Source File +# Begin Source File + SOURCE=.\IMSWindowsScreenEventHandler.h # End Source File # End Group diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index 5f4daf73..b1900797 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -17,8 +17,6 @@ VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ server.dsp \ - CMSWindowsPrimaryScreen.cpp \ - CMSWindowsPrimaryScreen.h \ $(NULL) MAINTAINERCLEANFILES = \ @@ -32,18 +30,13 @@ libserver_a_SOURCES = \ CConfig.cpp \ CHTTPServer.cpp \ CPrimaryClient.cpp \ - CPrimaryScreen.cpp \ CServer.cpp \ - CXWindowsPrimaryScreen.cpp \ CClientProxy.h \ CClientProxy1_0.h \ CConfig.h \ CHTTPServer.h \ CPrimaryClient.h \ - CPrimaryScreen.h \ CServer.h \ - CXWindowsPrimaryScreen.h \ - IPrimaryScreenFactory.h \ $(NULL) INCLUDES = \ -I$(VDEPTH)/lib/common \ diff --git a/lib/server/server.dsp b/lib/server/server.dsp index 26134e1c..29ee4b0a 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -103,18 +103,10 @@ SOURCE=.\CHTTPServer.cpp # End Source File # Begin Source File -SOURCE=.\CMSWindowsPrimaryScreen.cpp -# End Source File -# Begin Source File - SOURCE=.\CPrimaryClient.cpp # End Source File # Begin Source File -SOURCE=.\CPrimaryScreen.cpp -# End Source File -# Begin Source File - SOURCE=.\CServer.cpp # End Source File # End Group @@ -139,24 +131,12 @@ SOURCE=.\CHTTPServer.h # End Source File # Begin Source File -SOURCE=.\CMSWindowsPrimaryScreen.h -# End Source File -# Begin Source File - SOURCE=.\CPrimaryClient.h # End Source File # Begin Source File -SOURCE=.\CPrimaryScreen.h -# End Source File -# Begin Source File - SOURCE=.\CServer.h # End Source File -# Begin Source File - -SOURCE=.\IPrimaryScreenFactory.h -# End Source File # End Group # Begin Group "Resource Files" diff --git a/lib/server/CPrimaryScreen.cpp b/lib/synergy/CPrimaryScreen.cpp similarity index 100% rename from lib/server/CPrimaryScreen.cpp rename to lib/synergy/CPrimaryScreen.cpp diff --git a/lib/server/CPrimaryScreen.h b/lib/synergy/CPrimaryScreen.h similarity index 100% rename from lib/server/CPrimaryScreen.h rename to lib/synergy/CPrimaryScreen.h diff --git a/lib/client/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp similarity index 100% rename from lib/client/CSecondaryScreen.cpp rename to lib/synergy/CSecondaryScreen.cpp diff --git a/lib/client/CSecondaryScreen.h b/lib/synergy/CSecondaryScreen.h similarity index 100% rename from lib/client/CSecondaryScreen.h rename to lib/synergy/CSecondaryScreen.h diff --git a/lib/server/IPrimaryScreenFactory.h b/lib/synergy/IPrimaryScreenFactory.h similarity index 100% rename from lib/server/IPrimaryScreenFactory.h rename to lib/synergy/IPrimaryScreenFactory.h diff --git a/lib/client/ISecondaryScreenFactory.h b/lib/synergy/ISecondaryScreenFactory.h similarity index 100% rename from lib/client/ISecondaryScreenFactory.h rename to lib/synergy/ISecondaryScreenFactory.h diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index c86a8b34..4f1f1353 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -25,24 +25,30 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libsynergy.a libsynergy_a_SOURCES = \ + CClipboard.cpp \ CInputPacketStream.cpp \ COutputPacketStream.cpp \ + CPrimaryScreen.cpp \ CProtocolUtil.cpp \ - CClipboard.cpp \ + CSecondaryScreen.cpp \ XScreen.cpp \ XSynergy.cpp \ CClipboard.h \ CInputPacketStream.h \ COutputPacketStream.h \ + CPrimaryScreen.h \ CProtocolUtil.h \ + CSecondaryScreen.h \ ClipboardTypes.h \ IClient.h \ IClipboard.h \ + IPrimaryScreenFactory.h \ IPrimaryScreenReceiver.h \ IScreen.h \ IScreenEventHandler.h \ IScreenReceiver.h \ IScreenSaver.h \ + ISecondaryScreenFactory.h \ IServer.h \ KeyTypes.h \ MouseTypes.h \ diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 3b121f79..583271c6 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -99,10 +99,18 @@ SOURCE=.\COutputPacketStream.cpp # End Source File # Begin Source File +SOURCE=.\CPrimaryScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\CProtocolUtil.cpp # End Source File # Begin Source File +SOURCE=.\CSecondaryScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\XScreen.cpp # End Source File # Begin Source File @@ -131,10 +139,18 @@ SOURCE=.\COutputPacketStream.h # End Source File # Begin Source File +SOURCE=.\CPrimaryScreen.h +# End Source File +# Begin Source File + SOURCE=.\CProtocolUtil.h # End Source File # Begin Source File +SOURCE=.\CSecondaryScreen.h +# End Source File +# Begin Source File + SOURCE=.\IClient.h # End Source File # Begin Source File @@ -143,6 +159,10 @@ SOURCE=.\IClipboard.h # End Source File # Begin Source File +SOURCE=.\IPrimaryScreenFactory.h +# End Source File +# Begin Source File + SOURCE=.\IPrimaryScreenReceiver.h # End Source File # Begin Source File @@ -163,6 +183,10 @@ SOURCE=.\IScreenSaver.h # End Source File # Begin Source File +SOURCE=.\ISecondaryScreenFactory.h +# End Source File +# Begin Source File + SOURCE=.\IServer.h # End Source File # Begin Source File From f832bdaf1256adee0ed16c268c540fa65567cbad Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 5 Jan 2003 21:52:28 +0000 Subject: [PATCH 408/807] Added missing files. --- lib/base/LogOutputters.cpp | 158 +++++++++++++++++++++++++++++++++++++ lib/base/LogOutputters.h | 71 +++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 lib/base/LogOutputters.cpp create mode 100644 lib/base/LogOutputters.h diff --git a/lib/base/LogOutputters.cpp b/lib/base/LogOutputters.cpp new file mode 100644 index 00000000..0f0050ce --- /dev/null +++ b/lib/base/LogOutputters.cpp @@ -0,0 +1,158 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "LogOutputters.h" +#include "CArch.h" + +// +// CStopLogOutputter +// + +CStopLogOutputter::CStopLogOutputter() +{ + // do nothing +} + +CStopLogOutputter::~CStopLogOutputter() +{ + // do nothing +} + +void +CStopLogOutputter::open(const char*) +{ + // do nothing +} + +void +CStopLogOutputter::close() +{ + // do nothing +} + +bool +CStopLogOutputter::write(ELevel, const char*) +{ + return false; +} + +const char* +CStopLogOutputter::getNewline() const +{ + return ""; +} + + +// +// CConsoleLogOutputter +// + +CConsoleLogOutputter::CConsoleLogOutputter() +{ + // do nothing +} + +CConsoleLogOutputter::~CConsoleLogOutputter() +{ + // do nothing +} + +void +CConsoleLogOutputter::open(const char* title) +{ + ARCH->openConsole(title); +} + +void +CConsoleLogOutputter::close() +{ + ARCH->closeConsole(); +} + +bool +CConsoleLogOutputter::write(ELevel, const char* msg) +{ + ARCH->writeConsole(msg); + return true; +} + +const char* +CConsoleLogOutputter::getNewline() const +{ + return ARCH->getNewlineForConsole(); +} + + +// +// CSystemLogOutputter +// + +CSystemLogOutputter::CSystemLogOutputter() +{ + // do nothing +} + +CSystemLogOutputter::~CSystemLogOutputter() +{ + // do nothing +} + +void +CSystemLogOutputter::open(const char* title) +{ + ARCH->openLog(title); +} + +void +CSystemLogOutputter::close() +{ + ARCH->closeLog(); +} + +bool +CSystemLogOutputter::write(ELevel level, const char* msg) +{ + IArchLog::ELevel archLogLevel; + switch (level) { + case CLog::kFATAL: + case CLog::kERROR: + archLogLevel = IArchLog::kERROR; + break; + + case CLog::kWARNING: + archLogLevel = IArchLog::kWARNING; + break; + + case CLog::kNOTE: + archLogLevel = IArchLog::kNOTE; + break; + + case CLog::kINFO: + archLogLevel = IArchLog::kINFO; + break; + + default: + archLogLevel = IArchLog::kDEBUG; + break; + + }; + ARCH->writeLog(archLogLevel, msg); + return true; +} + +const char* +CSystemLogOutputter::getNewline() const +{ + return ""; +} diff --git a/lib/base/LogOutputters.h b/lib/base/LogOutputters.h new file mode 100644 index 00000000..74d18814 --- /dev/null +++ b/lib/base/LogOutputters.h @@ -0,0 +1,71 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LOGOUTPUTTERS_H +#define LOGOUTPUTTERS_H + +#include "ILogOutputter.h" + +//! Stop traversing log chain outputter +/*! +This outputter performs no output and returns false from \c write(), +causing the logger to stop traversing the outputter chain. Insert +this to prevent already inserted outputters from writing. +*/ +class CStopLogOutputter : public ILogOutputter { +public: + CStopLogOutputter(); + virtual ~CStopLogOutputter(); + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; +}; + +//! Write log to console +/*! +This outputter writes output to the console. The level for each +message is ignored. +*/ +class CConsoleLogOutputter : public ILogOutputter { +public: + CConsoleLogOutputter(); + virtual ~CConsoleLogOutputter(); + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; +}; + +//! Write log to system log +/*! +This outputter writes output to the system log. +*/ +class CSystemLogOutputter : public ILogOutputter { +public: + CSystemLogOutputter(); + virtual ~CSystemLogOutputter(); + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; +}; + +#endif From 1fd7ce14f3797be5d0416dce9f9f5a47ea15eeee Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 7 Jan 2003 21:11:54 +0000 Subject: [PATCH 409/807] Added low-level mouse hook to support mouse wheel on NT (>=SP3). Thanks to karsten for the patch used as a starting point. --- lib/platform/CSynergyHook.cpp | 149 ++++++++++++++++++++++++---------- 1 file changed, 106 insertions(+), 43 deletions(-) diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 2b9d1321..60590f3d 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -55,10 +55,11 @@ static HHOOK g_keyboard = NULL; static HHOOK g_mouse = NULL; static HHOOK g_cbt = NULL; static HHOOK g_getMessage = NULL; -static HANDLE g_keyHookThread = NULL; -static DWORD g_keyHookThreadID = 0; -static HANDLE g_keyHookEvent = NULL; +static HANDLE g_hookThreadLL = NULL; +static DWORD g_hookThreadIDLL = 0; +static HANDLE g_hookEventLL = NULL; static HHOOK g_keyboardLL = NULL; +static HHOOK g_mouseLL = NULL; static bool g_screenSaver = false; static bool g_relay = false; static UInt32 g_zoneSides = 0; @@ -162,7 +163,9 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) { // win2k and other systems supporting WM_MOUSEWHEEL in // the mouse hook are gratuitously different (and poorly - // documented). + // documented). if a low-level mouse hook is in place + // it should capture these events so we'll never see + // them. switch (g_wheelSupport) { case kWheelModern: { const MOUSEHOOKSTRUCT* info = @@ -342,12 +345,55 @@ keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_keyboardLL, code, wParam, lParam); } +// +// low-level mouse hook -- this allows us to capture and handle mouse +// wheel events on all windows NT platforms from NT SP3 and up. this +// is both simpler than using the mouse hook but also supports windows +// windows NT which does not report mouse wheel events. we need to +// keep the mouse hook handling of mouse wheel events because the +// windows 95 family doesn't support low-level hooks. +// + +static +LRESULT CALLBACK +mouseLLHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + if (g_relay) { + MSLLHOOKSTRUCT* info = reinterpret_cast(lParam); + + switch (wParam) { + case WM_MOUSEWHEEL: + // mouse wheel events are the same for entire NT family + // (>=SP3, prior versions have no low level hooks) for + // low-level mouse hook messages, unlike (regular) mouse + // hook messages which are gratuitously different on + // win2k and not sent at all for windows NT. + + // forward message to our window + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, + HIWORD(info->mouseData), 0); + + // discard event + return 1; + + default: + // all other events are passed through + break; + } + } + } + + return CallNextHookEx(g_mouseLL, code, wParam, lParam); +} + static DWORD WINAPI -getKeyboardLLProc(void*) +getLowLevelProc(void*) { - // thread proc for low-level keyboard hook. this does nothing but - // install the hook, process events, and uninstall the hook. + // thread proc for low-level keyboard/mouse hooks. this does + // nothing but install the hook, process events, and uninstall + // the hook. // force this thread to have a message queue MSG msg; @@ -360,13 +406,27 @@ getKeyboardLLProc(void*) 0); if (g_keyboardLL == NULL) { // indicate failure and exit - g_keyHookThreadID = 0; - SetEvent(g_keyHookEvent); + g_hookThreadIDLL = 0; + SetEvent(g_hookEventLL); + return 1; + } + + // install low-level mouse hook + g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL, + &mouseLLHook, + g_hinstance, + 0); + if (g_mouseLL == NULL) { + // indicate failure and exit + UnhookWindowsHookEx(g_keyboardLL); + g_keyboardLL = NULL; + g_hookThreadIDLL = 0; + SetEvent(g_hookEventLL); return 1; } // ready - SetEvent(g_keyHookEvent); + SetEvent(g_hookEventLL); // message loop bool done = false; @@ -387,7 +447,9 @@ getKeyboardLLProc(void*) } // uninstall hook + UnhookWindowsHookEx(g_mouseLL); UnhookWindowsHookEx(g_keyboardLL); + g_mouseLL = NULL; g_keyboardLL = NULL; return 0; @@ -397,10 +459,10 @@ getKeyboardLLProc(void*) static DWORD WINAPI -getKeyboardLLProc(void*) +getLowLevelProc(void*) { - g_keyHookThreadID = 0; - SetEvent(g_keyHookEvent); + g_hookThreadIDLL = 0; + SetEvent(g_hookEventLL); return 1; } @@ -506,10 +568,11 @@ init(DWORD threadID) g_mouse = NULL; g_cbt = NULL; g_getMessage = NULL; - g_keyHookThread = NULL; - g_keyHookThreadID = 0; - g_keyHookEvent = NULL; + g_hookThreadLL = NULL; + g_hookThreadIDLL = 0; + g_hookEventLL = NULL; g_keyboardLL = NULL; + g_mouseLL = NULL; g_screenSaver = false; } @@ -607,31 +670,31 @@ install() // ignore failure; we just won't get mouse wheel messages } - // install low-level keyboard hook, if possible. since this hook - // is called in the context of the installing thread and that + // install low-level keyboard/mouse hooks, if possible. since these + // hook are called in the context of the installing thread and that // thread must have a message loop but we don't want the caller's // message loop to do the work, we'll fire up a separate thread - // just for the hook. note that low-level keyboard hooks are only - // available on windows NT SP3 and above. - g_keyHookEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if (g_keyHookEvent != NULL) { - g_keyHookThread = CreateThread(NULL, 0, &getKeyboardLLProc, 0, - CREATE_SUSPENDED, &g_keyHookThreadID); - if (g_keyHookThread != NULL) { + // just for the hooks. note that low-level hooks are only available + // on windows NT SP3 and above. + g_hookEventLL = CreateEvent(NULL, TRUE, FALSE, NULL); + if (g_hookEventLL != NULL) { + g_hookThreadLL = CreateThread(NULL, 0, &getLowLevelProc, 0, + CREATE_SUSPENDED, &g_hookThreadIDLL); + if (g_hookThreadLL != NULL) { // start the thread and wait for it to initialize - ResumeThread(g_keyHookThread); - WaitForSingleObject(g_keyHookEvent, INFINITE); - ResetEvent(g_keyHookEvent); + ResumeThread(g_hookThreadLL); + WaitForSingleObject(g_hookEventLL, INFINITE); + ResetEvent(g_hookEventLL); - // the thread clears g_keyHookThreadID if it failed - if (g_keyHookThreadID == 0) { - CloseHandle(g_keyHookThread); - g_keyHookThread = NULL; + // the thread clears g_hookThreadIDLL if it failed + if (g_hookThreadIDLL == 0) { + CloseHandle(g_hookThreadLL); + g_hookThreadLL = NULL; } } - if (g_keyHookThread == NULL) { - CloseHandle(g_keyHookEvent); - g_keyHookEvent = NULL; + if (g_hookThreadLL == NULL) { + CloseHandle(g_hookEventLL); + g_hookEventLL = NULL; } } @@ -644,14 +707,14 @@ uninstall(void) assert(g_hinstance != NULL); // uninstall hooks - if (g_keyHookThread != NULL) { - PostThreadMessage(g_keyHookThreadID, WM_QUIT, 0, 0); - WaitForSingleObject(g_keyHookThread, INFINITE); - CloseHandle(g_keyHookEvent); - CloseHandle(g_keyHookThread); - g_keyHookEvent = NULL; - g_keyHookThread = NULL; - g_keyHookThreadID = 0; + if (g_hookThreadLL != NULL) { + PostThreadMessage(g_hookThreadIDLL, WM_QUIT, 0, 0); + WaitForSingleObject(g_hookThreadLL, INFINITE); + CloseHandle(g_hookEventLL); + CloseHandle(g_hookThreadLL); + g_hookEventLL = NULL; + g_hookThreadLL = NULL; + g_hookThreadIDLL = 0; } if (g_keyboard != NULL) { UnhookWindowsHookEx(g_keyboard); From 9a245498a6482563601cec629d884d5d68c640dd Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 7 Jan 2003 21:12:51 +0000 Subject: [PATCH 410/807] Attempts to improve forcing synergy window to foreground. These changes don't seem to improve the situation but don't seem to hurt either. --- lib/platform/CMSWindowsPrimaryScreen.cpp | 43 +++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 56143f30..a955f882 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -540,17 +540,52 @@ CMSWindowsPrimaryScreen::showWindow() m_lastForegroundWindow = GetForegroundWindow(); m_lastActiveThread = GetWindowThreadProcessId( m_lastForegroundWindow, NULL); + DWORD myThread = GetCurrentThreadId(); if (m_lastActiveThread != 0) { - DWORD myThread = GetCurrentThreadId(); - if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { - m_lastActiveWindow = GetActiveWindow(); - AttachThreadInput(myThread, m_lastActiveThread, FALSE); + if (myThread != m_lastActiveThread) { + if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { + m_lastActiveWindow = GetActiveWindow(); + AttachThreadInput(myThread, m_lastActiveThread, FALSE); + } } } // show our window ShowWindow(m_window, SW_SHOW); + // force our window to the foreground. this is necessary to + // capture input but is complicated by microsoft's misguided + // attempt to prevent applications from changing the + // foreground window. (the user should be in control of that + // under normal circumstances but there are exceptions; the + // good folks at microsoft, after abusing the previously + // available ability to switch foreground tasks in many of + // their apps, changed the behavior to prevent it. maybe + // it was easier than fixing the applications.) + // + // anyway, simply calling SetForegroundWindow() doesn't work + // unless there is no foreground window or we already are the + // foreground window. so we AttachThreadInput() to the + // foreground process then call SetForegroundWindow(); that + // makes Windows think the foreground process changed the + // foreground window which is allowed since the foreground + // is "voluntarily" yielding control. then we unattach the + // thread input and go about our business. + // + // unfortunately, this still doesn't work for console windows + // on the windows 95 family. if a console is the foreground + // app on the server when the user leaves the server screen + // then the keyboard will not be captured by synergy. + if (m_lastActiveThread != myThread) { + if (m_lastActiveThread != 0) { + AttachThreadInput(myThread, m_lastActiveThread, TRUE); + } + SetForegroundWindow(m_window); + if (m_lastActiveThread != 0) { + AttachThreadInput(myThread, m_lastActiveThread, FALSE); + } + } + // get keyboard input and capture mouse SetActiveWindow(m_window); SetFocus(m_window); From 84d75600a9b28a30a4da458a51d40a6ba204dfba Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 7 Jan 2003 21:47:27 +0000 Subject: [PATCH 411/807] Changed version number to 0.9.15. Added 0.9.15 log entries. --- ChangeLog | 338 +++++++++++++++++++++++++++++++++++++++++++ configure.in | 2 +- lib/common/Version.h | 2 +- 3 files changed, 340 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index e8025a37..18025a77 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,341 @@ +2003/01/07 21:12:51 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Attempts to improve forcing synergy window to foreground. These +changes don't seem to improve the situation but don't seem to +hurt either. + +---------- +2003/01/07 21:11:54 crs +lib/platform/CSynergyHook.cpp + +Added low-level mouse hook to support mouse wheel on NT (>=SP3). +Thanks to karsten for the patch used as a starting point. + +---------- +2003/01/05 21:52:28 crs +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h + +Added missing files. + +---------- +2003/01/05 21:48:54 crs +PORTING +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +doc/doxygen.cfg.in +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleUnix.h +lib/arch/CArchConsoleWindows.h +lib/arch/CArchDaemonNone.h +lib/arch/CArchDaemonUnix.h +lib/arch/CArchDaemonWindows.h +lib/arch/CArchFileUnix.h +lib/arch/CArchFileWindows.h +lib/arch/CArchLogUnix.h +lib/arch/CArchLogWindows.h +lib/arch/CArchMiscWindows.h +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/CArchNetworkBSD.h +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkWinsock.h +lib/arch/CArchSleepUnix.h +lib/arch/CArchSleepWindows.h +lib/arch/CArchStringUnix.cpp +lib/arch/CArchStringUnix.h +lib/arch/CArchStringWindows.cpp +lib/arch/CArchStringWindows.h +lib/arch/CArchTimeUnix.h +lib/arch/CArchTimeWindows.h +lib/arch/IArchConsole.h +lib/arch/IArchFile.h +lib/arch/IArchLog.h +lib/arch/IArchMultithread.h +lib/arch/IArchNetwork.h +lib/arch/IArchSleep.h +lib/arch/IArchString.h +lib/arch/IArchTime.h +lib/arch/Makefile.am +lib/arch/XArchImpl.h +lib/arch/arch.dsp +lib/base/CLog.cpp +lib/base/CUnicode.cpp +lib/base/XBase.cpp +lib/base/XBase.h +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/CMSWindowsSecondaryScreen.h +lib/client/CSecondaryScreen.cpp +lib/client/CSecondaryScreen.h +lib/client/CXWindowsSecondaryScreen.cpp +lib/client/CXWindowsSecondaryScreen.h +lib/client/ISecondaryScreenFactory.h +lib/client/Makefile.am +lib/client/client.dsp +lib/http/XHTTP.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/platform/Makefile.am +lib/platform/platform.dsp +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CMSWindowsPrimaryScreen.h +lib/server/CPrimaryScreen.cpp +lib/server/CPrimaryScreen.h +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/CXWindowsPrimaryScreen.h +lib/server/IPrimaryScreenFactory.h +lib/server/Makefile.am +lib/server/server.dsp +lib/synergy/CPrimaryScreen.cpp +lib/synergy/CPrimaryScreen.h +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h +lib/synergy/IPrimaryScreenFactory.h +lib/synergy/ISecondaryScreenFactory.h +lib/synergy/Makefile.am +lib/synergy/libsynergy.dsp + +Moved CPrimaryScreen and CSecondaryScreen to the lib/synergy +and the platform specific implementations to lib/platform. +Added an lib/arch method to query the platform's native wide +character encoding and changed CUnicode to use it. All +platform dependent code is now in lib/arch, lib/platform, +and the programs under cmd. Also added more documentation. + +---------- +2003/01/04 22:01:32 crs +PORTING +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/synergyc/Makefile.am +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.dsp +cmd/synergys/Makefile.am +cmd/synergys/synergys.cpp +cmd/synergys/synergys.dsp +configure.in +lib/Makefile.am +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleUnix.cpp +lib/arch/CArchConsoleUnix.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchConsoleWindows.h +lib/arch/CArchDaemonNone.cpp +lib/arch/CArchDaemonNone.h +lib/arch/CArchDaemonUnix.cpp +lib/arch/CArchDaemonUnix.h +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/CArchFileUnix.cpp +lib/arch/CArchFileUnix.h +lib/arch/CArchFileWindows.cpp +lib/arch/CArchFileWindows.h +lib/arch/CArchImpl.cpp +lib/arch/CArchLogUnix.cpp +lib/arch/CArchLogUnix.h +lib/arch/CArchLogWindows.cpp +lib/arch/CArchLogWindows.h +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkWinsock.h +lib/arch/CArchSleepUnix.cpp +lib/arch/CArchSleepUnix.h +lib/arch/CArchSleepWindows.cpp +lib/arch/CArchSleepWindows.h +lib/arch/CArchStringUnix.cpp +lib/arch/CArchStringUnix.h +lib/arch/CArchStringWindows.cpp +lib/arch/CArchStringWindows.h +lib/arch/CArchTimeUnix.cpp +lib/arch/CArchTimeUnix.h +lib/arch/CArchTimeWindows.cpp +lib/arch/CArchTimeWindows.h +lib/arch/CMultibyte.cpp +lib/arch/CMultibyteEmu.cpp +lib/arch/CMultibyteOS.cpp +lib/arch/IArchConsole.h +lib/arch/IArchDaemon.h +lib/arch/IArchFile.h +lib/arch/IArchLog.h +lib/arch/IArchMultithread.h +lib/arch/IArchNetwork.h +lib/arch/IArchSleep.h +lib/arch/IArchString.h +lib/arch/IArchTime.h +lib/arch/Makefile.am +lib/arch/XArch.cpp +lib/arch/XArch.h +lib/arch/XArchImpl.h +lib/arch/XArchUnix.cpp +lib/arch/XArchUnix.h +lib/arch/XArchWindows.cpp +lib/arch/XArchWindows.h +lib/arch/arch.dsp +lib/arch/vsnprintf.cpp +lib/base/BasicTypes.h +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/CStopwatch.cpp +lib/base/CString.cpp +lib/base/CString.h +lib/base/CStringUtil.cpp +lib/base/CStringUtil.h +lib/base/CUnicode.cpp +lib/base/CUnicode.h +lib/base/IInterface.h +lib/base/ILogOutputter.h +lib/base/Makefile.am +lib/base/Version.h +lib/base/XBase.cpp +lib/base/XBase.h +lib/base/base.dsp +lib/base/common.h +lib/base/stdfstream.h +lib/base/stdistream.h +lib/base/stdlist.h +lib/base/stdmap.h +lib/base/stdostream.h +lib/base/stdpost.h +lib/base/stdpre.h +lib/base/stdset.h +lib/base/stdsstream.h +lib/base/stdvector.h +lib/client/CClient.cpp +lib/client/CMSWindowsSecondaryScreen.cpp +lib/client/Makefile.am +lib/client/client.dsp +lib/common/BasicTypes.h +lib/common/IInterface.h +lib/common/Makefile.am +lib/common/Version.h +lib/common/common.dsp +lib/common/common.h +lib/common/stdfstream.h +lib/common/stdistream.h +lib/common/stdlist.h +lib/common/stdmap.h +lib/common/stdostream.h +lib/common/stdpost.h +lib/common/stdpre.h +lib/common/stdset.h +lib/common/stdsstream.h +lib/common/stdstring.h +lib/common/stdvector.h +lib/http/CHTTPProtocol.h +lib/http/Makefile.am +lib/http/XHTTP.cpp +lib/http/http.dsp +lib/io/CUnicode.cpp +lib/io/CUnicode.h +lib/io/Makefile.am +lib/io/XIO.cpp +lib/io/XIO.h +lib/io/io.dsp +lib/mt/CCondVar.cpp +lib/mt/CCondVar.h +lib/mt/CMutex.cpp +lib/mt/CMutex.h +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/mt/CThreadRep.cpp +lib/mt/CThreadRep.h +lib/mt/CTimerThread.cpp +lib/mt/Makefile.am +lib/mt/XMT.cpp +lib/mt/XMT.h +lib/mt/XThread.h +lib/mt/mt.dsp +lib/net/CNetwork.cpp +lib/net/CNetwork.h +lib/net/CNetworkAddress.cpp +lib/net/CNetworkAddress.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPListenSocket.h +lib/net/CTCPSocket.cpp +lib/net/CTCPSocket.h +lib/net/Makefile.am +lib/net/XNetwork.cpp +lib/net/XNetwork.h +lib/net/XSocket.cpp +lib/net/XSocket.h +lib/net/net.dsp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CPlatform.cpp +lib/platform/CPlatform.h +lib/platform/CUnixPlatform.cpp +lib/platform/CUnixPlatform.h +lib/platform/CWin32Platform.cpp +lib/platform/CWin32Platform.h +lib/platform/CXWindowsClipboard.cpp +lib/platform/IPlatform.h +lib/platform/Makefile.am +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CConfig.h +lib/server/CMSWindowsPrimaryScreen.cpp +lib/server/CServer.cpp +lib/server/CXWindowsPrimaryScreen.cpp +lib/server/Makefile.am +lib/server/server.dsp +lib/synergy/Makefile.am +lib/synergy/XScreen.cpp +lib/synergy/XScreen.h +lib/synergy/XSynergy.cpp +lib/synergy/XSynergy.h +lib/synergy/libsynergy.dsp +synergy.dsw + +Refactored some platform dependent code into a new library, +lib/arch. This should make porting easier. Will probably +continue to refactor a little more, moving platform dependent +event handling stuff into lib/platform. + +---------- +2002/12/26 18:40:22 crs +FAQ + +More FAQs. + +---------- +2002/12/25 23:49:42 crs +BUGS +FAQ +INSTALL +README +TODO + +Documentation update. + +---------- +2002/12/25 22:56:09 crs +lib/server/CMSWindowsPrimaryScreen.cpp + +Made synrgyhk.dll error messages less cryptic. + +---------- 2002/12/25 19:21:17 crs NEWS configure.in diff --git a/configure.in b/configure.in index bc3e6222..6b738b41 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=0 MINOR_VERSION=9 -RELEASE_VERSION=14 +RELEASE_VERSION=15 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 882e8f7b..8764eb36 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "0.9.14" +# define VERSION "0.9.15" #endif // important strings From f8240d97be6b43648159b90bed014efac3711e34 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 8 Jan 2003 21:36:10 +0000 Subject: [PATCH 412/807] Added a FAQ entry for client being rejected. User probably didn't start the server or told the client the wrong server host name. --- FAQ | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/FAQ b/FAQ index 81dde7ba..efdda31c 100644 --- a/FAQ +++ b/FAQ @@ -4,7 +4,7 @@ Synergy Frequently Asked Questions Questions --------- 1. Why doesn't ctrl+alt+del work on secondary screens? -2. Can the server and client be on different operating systems? +2. Can the server and client be using different operating systems? 3. What's the difference between synergy and x2x, x2vnc, etc? 4. What does "Cannot initialize hook library" mean? 5. What security/encryption does synergy provide? @@ -13,7 +13,8 @@ Questions 8. Can synergy share the display in addition to the mouse and keyboard? 9. Can synergy do drag and drop between computers? 10. Do AltGr or Mode-Switch work? -11. Why isn't synergy ported to platform X? +11. Why isn't synergy ported to platform XYZ? +12. My client can't connect. What's wrong? Answers @@ -27,7 +28,7 @@ Answers Future versions may add support for or some equivalent to ctrl+alt+del. -2. Can the server and client be on different operating systems? +2. Can the server and client be using different operating systems? Yes. The synergy network protocol is platform neutral so synergy doesn't care what operating systems are running on @@ -116,10 +117,21 @@ Answers on Windows and are having trouble with Ctrl+Alt_L = AltGr, try using the right Alt key as AltGr instead. -11. Why isn't synergy ported to platform X? +11. Why isn't synergy ported to platform XYZ? - Probably because the developers don't have access to platform X - and/or are unfamiliar with development on X. Also, synergy has + Probably because the developers don't have access to platform XYZ + and/or are unfamiliar with development on XYZ. Also, synergy has inherently non-portable aspects so there's a not insignificant effort involved in porting. +12. My client can't connect. What's wrong? + + A common mistake when starting the client is to give the wrong + server host name. The last synergyc command line option (Unix) + or the "Server Host Name" edit field (Windows) should be the + host name (or IP address) of the server *not* the client's host + name. If you get the error "connection failed: cannot connect + socket" followed by "the attempt to connect was forcefully + rejected" or "connection refused" then the server isn't started, + can't bind the address, or the client is connecting to the wrong + host name/address or port. From 0347bb16675b2e0e793df90ffacfcf879d9c0624 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 8 Jan 2003 21:36:13 +0000 Subject: [PATCH 413/807] Changed log level of two messages. Now won't spew about reading window properties and will report connection failure at DEBUG instead of DEBUG1. --- lib/client/CClient.cpp | 2 +- lib/platform/CXWindowsUtil.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 932072e8..6f690ab1 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -546,7 +546,7 @@ CClient::runServer() break; } catch (XSocketConnect& e) { - LOG((CLOG_DEBUG1 "failed to connect to server: %s", e.what())); + LOG((CLOG_DEBUG "failed to connect to server: %s", e.what())); // failed to connect. if not camping then rethrow. if (!m_camp) { diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 63cd1ca7..90d0b0aa 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -99,7 +99,7 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, } if (result == Success) { - LOG((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); + LOG((CLOG_DEBUG2 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); return true; } else { From 7872c301111880630c5310ba58e09f4fd91fcd35 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 8 Jan 2003 21:36:14 +0000 Subject: [PATCH 414/807] Portability fixes. Now builds on Linux 2.2 and 2.4 and solaris. Also builds on i386, alpha, G3/G4, and sparc. --- lib/arch/CArchMultithreadPosix.cpp | 14 ++++++++++++-- lib/arch/CArchMultithreadPosix.h | 1 + lib/arch/CArchNetworkBSD.cpp | 5 +++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 304440b2..20a2ad47 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -40,6 +40,7 @@ public: public: int m_refCount; + IArchMultithread::ThreadID m_id; pthread_t m_thread; IArchMultithread::ThreadFunc m_func; void* m_userData; @@ -51,6 +52,7 @@ public: CArchThreadImpl::CArchThreadImpl() : m_refCount(1), + m_id(0), m_func(NULL), m_userData(NULL), m_cancel(false), @@ -69,7 +71,8 @@ CArchThreadImpl::CArchThreadImpl() : CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL; CArchMultithreadPosix::CArchMultithreadPosix() : - m_newThreadCalled(false) + m_newThreadCalled(false), + m_nextID(0) { assert(s_instance == NULL); @@ -505,6 +508,7 @@ bool CArchMultithreadPosix::waitForEvent(double /*timeout*/) { // not implemented + return false; } bool @@ -534,7 +538,7 @@ CArchMultithreadPosix::getResultOfThread(CArchThread thread) IArchMultithread::ThreadID CArchMultithreadPosix::getIDOfThread(CArchThread thread) { - return reinterpret_cast(reinterpret_cast(thread)); + return thread->m_id; } void @@ -601,6 +605,12 @@ CArchMultithreadPosix::insert(CArchThreadImpl* thread) // thread shouldn't already be on the list assert(findNoRef(thread->m_thread) == NULL); + // set thread id. note that we don't worry about m_nextID + // wrapping back to 0 and duplicating thread ID's since the + // likelihood of synergy running that long is vanishingly + // small. + thread->m_id = ++m_nextID; + // append to list m_threadList.push_back(thread); } diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h index 59d14155..07e2f1d9 100644 --- a/lib/arch/CArchMultithreadPosix.h +++ b/lib/arch/CArchMultithreadPosix.h @@ -87,6 +87,7 @@ private: CArchMutex m_threadMutex; CArchThread m_mainThread; CThreadList m_threadList; + ThreadID m_nextID; pthread_t m_signalThread; }; diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 8e3f4706..474c68fc 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -641,8 +641,9 @@ CArchNetworkBSD::addrToName(CArchNetAddress addr) // mutexed name lookup (ugh) ARCH->lockMutex(m_mutex); - struct hostent* info = gethostbyaddr(&addr->m_addr, addr->m_len, - addr->m_addr.sa_family); + struct hostent* info = gethostbyaddr( + reinterpret_cast(&addr->m_addr), + addr->m_len, addr->m_addr.sa_family); if (info == NULL) { ARCH->unlockMutex(m_mutex); throwNameError(h_errno); From 02a0d61e559db01b441943727a733e58decca00d Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 8 Jan 2003 22:17:44 +0000 Subject: [PATCH 415/807] Added bit about configuring on Solaris, which requires some options to find the X11 includes and libraries. --- FAQ | 9 +++++++++ INSTALL | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/FAQ b/FAQ index efdda31c..8805d1d1 100644 --- a/FAQ +++ b/FAQ @@ -15,6 +15,7 @@ Questions 10. Do AltGr or Mode-Switch work? 11. Why isn't synergy ported to platform XYZ? 12. My client can't connect. What's wrong? +13. Linking fails on Solaris. What's wrong? Answers @@ -135,3 +136,11 @@ Answers rejected" or "connection refused" then the server isn't started, can't bind the address, or the client is connecting to the wrong host name/address or port. + +13. Linking fails on Solaris. What's wrong? + + Did you add `--x-includes=/usr/openwin/include + --x-libraries=/usr/openwin/lib' (without the linebreak) to the + `configure' command line? Solaris puts the X11 includes and + libraries in an unusual place and the above lets synergy find + them. diff --git a/INSTALL b/INSTALL index 8d531dd0..b90c6be9 100644 --- a/INSTALL +++ b/INSTALL @@ -30,6 +30,12 @@ For a list of options to configure use: ./configure --help +On Solaris you may need to use: + + ./configure --x-includes=/usr/openwin/include --x-libraries=/usr/openwin/lib + +so synergy can find the X11 includes and libraries. + Building -------- From 6c1344a0d8fc00adc51bef6ce6135661bd2ac5cc Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 9 Jan 2003 08:04:03 +0000 Subject: [PATCH 416/807] Updated ChangeLog. --- ChangeLog | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/ChangeLog b/ChangeLog index 18025a77..f1565960 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,44 @@ +2003/01/08 22:17:44 crs +//depot/project/synergy/FAQ +//depot/project/synergy/INSTALL + +Added bit about configuring on Solaris, which requires some +options to find the X11 includes and libraries. + +---------- +2003/01/08 21:36:14 crs +//depot/project/synergy/lib/arch/CArchMultithreadPosix.cpp +//depot/project/synergy/lib/arch/CArchMultithreadPosix.h +//depot/project/synergy/lib/arch/CArchNetworkBSD.cpp + +Portability fixes. Now builds on Linux 2.2 and 2.4 and solaris. +Also builds on i386, alpha, G3/G4, and sparc. + +---------- +2003/01/08 21:36:13 crs +//depot/project/synergy/lib/client/CClient.cpp +//depot/project/synergy/lib/platform/CXWindowsUtil.cpp + +Changed log level of two messages. Now won't spew about reading +window properties and will report connection failure at DEBUG +instead of DEBUG1. + +---------- +2003/01/08 21:36:10 crs +//depot/project/synergy/FAQ + +Added a FAQ entry for client being rejected. User probably didn't +start the server or told the client the wrong server host name. + +---------- +2003/01/07 21:47:27 crs +//depot/project/synergy/ChangeLog +//depot/project/synergy/configure.in +//depot/project/synergy/lib/common/Version.h + +Changed version number to 0.9.15. Added 0.9.15 log entries. + +---------- 2003/01/07 21:12:51 crs lib/platform/CMSWindowsPrimaryScreen.cpp From 1758ea6f9f5e3ceef70e59b4c28aaffd7acc38f0 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 11 Jan 2003 14:01:44 +0000 Subject: [PATCH 417/807] Attempt to fix problems with multimon windows. The mouse position reported by the synergy hook dll is in a space with 0,0 in the upper-left which is not necessarily the same as the virtual desktop space. So the windows primary screen now accounts for that. On the secondary screen, mouse_event() doesn't seem to accept negative coordinates even on the windows NT family, making monitors with negative coordinates inaccessible via absolute moves. So if the move will be to negative coordinates, use the windows 95 family fallback of absolute moving to 0,0 then relative moving to the final position. --- lib/platform/CMSWindowsPrimaryScreen.cpp | 9 ++++++++- lib/platform/CMSWindowsSecondaryScreen.cpp | 23 +++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index a955f882..990120d4 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -310,8 +310,15 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) m_y = static_cast(msg->lParam); if (!isActive()) { + // shift by origin of virtual screen. the synergy + // hook DLL does not account for + SInt32 w, h; + m_screen->getShape(x, y, w, h); + x += m_x; + y += m_y; + // motion on primary screen - m_receiver->onMouseMovePrimary(m_x, m_y); + m_receiver->onMouseMovePrimary(x, y); } else { // motion on secondary screen. warp mouse back to diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index 7f858323..e680b9ba 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -354,9 +354,30 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) static bool gotSendInput = false; static SendInput_t SendInput = NULL; + // motion is simple (i.e. it's on the primary monitor) if there + // is only one monitor. + bool simple = !m_screen->isMultimon(); + if (!simple) { + // also simple if motion is within the primary monitor + simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && + y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); + if (!simple && !m_is95Family) { + // also simple if not on windows 95 family since the + // NT family mouse_event() allows absolute moves to + // any monitor. + // + // note -- this is possibly untrue if the primary + // monitor isn't upper-left most so limit it to that + // situation. + SInt32 x0, y0, w, h; + m_screen->getShape(x0, y0, w, h); + simple = (x0 == 0 && y0 == 0); + } + } + // move the mouse directly to target position on NT family or if // not using multiple monitors. - if (!m_screen->isMultimon() || !m_is95Family) { + if (simple) { SInt32 x0, y0, w, h; m_screen->getShape(x0, y0, w, h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, From 4bf0836eae73b26245e36ea3f8ee00e51c925798 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 11 Jan 2003 15:16:41 +0000 Subject: [PATCH 418/807] Synergy no longer tries to suppress the screen saver once it starts. It was doing that already if started through synergy but not if started by something outside of synergy. In particular, if you use `xscreensaver-command --activate' synergy used to send fake mouse motion events every 5 seconds to deactivate it. That's unlikely to be what the user wanted, especially if the locking is enabled since it would force the password dialog to appear. As before, it's recommended that client screens not use locking because xscreensaver will not deactivate without getting a password even if we make the request through a programmatic interface. Presumably that's for security reasons but it makes life harder for synergy. --- lib/platform/CXWindowsScreenSaver.cpp | 41 +++++++++++++++++++++------ lib/platform/CXWindowsScreenSaver.h | 13 ++++++++- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index c712f826..e421cf09 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -35,7 +35,9 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( m_notify(None), m_xscreensaver(None), m_xscreensaverActive(false), - m_disabled(false) + m_disabled(false), + m_suppressDisable(false), + m_disableJobInstalled(false) { // screen saver disable callback m_disableJob = new TMethodJob(this, @@ -184,7 +186,7 @@ CXWindowsScreenSaver::enable() { // for xscreensaver m_disabled = false; - m_screen->removeTimer(m_disableJob); + updateDisableJob(); // for built-in X screen saver XSetScreenSaver(m_display, m_timeout, m_interval, @@ -197,7 +199,7 @@ CXWindowsScreenSaver::disable() // for xscreensaver. 5 seconds should be plenty often to // suppress the screen saver. m_disabled = true; - m_screen->addTimer(m_disableJob, 5.0); + updateDisableJob(); // use built-in X screen saver XGetScreenSaver(m_display, &m_timeout, &m_interval, @@ -211,9 +213,8 @@ void CXWindowsScreenSaver::activate() { // remove disable job timer - if (m_disabled) { - m_screen->removeTimer(m_disableJob); - } + m_suppressDisable = true; + updateDisableJob(); // try xscreensaver findXScreenSaver(); @@ -230,9 +231,8 @@ void CXWindowsScreenSaver::deactivate() { // reinstall disable job timer - if (m_disabled) { - m_screen->addTimer(m_disableJob, 5.0); - } + m_suppressDisable = false; + updateDisableJob(); // try xscreensaver findXScreenSaver(); @@ -346,6 +346,14 @@ CXWindowsScreenSaver::setXScreenSaverActive(bool activated) if (m_xscreensaverActive != activated) { LOG((CLOG_DEBUG "xscreensaver %s on window 0x%08x", activated ? "activated" : "deactivated", m_xscreensaver)); m_xscreensaverActive = activated; + + // if screen saver was activated forcefully (i.e. against + // our will) then just accept it. don't try to keep it + // from activating since that'll just pop up the password + // dialog if locking is enabled. + m_suppressDisable = activated; + updateDisableJob(); + sendNotify(activated); } } @@ -433,6 +441,21 @@ CXWindowsScreenSaver::addWatchXScreenSaver(Window window) } } +void +CXWindowsScreenSaver::updateDisableJob() +{ + assert(m_disableJob != NULL); + + if (m_disabled && !m_suppressDisable && !m_disableJobInstalled) { + m_disableJobInstalled = true; + m_screen->addTimer(m_disableJob, 5.0); + } + else if ((!m_disabled || m_suppressDisable) && m_disableJobInstalled) { + m_disableJobInstalled = false; + m_screen->removeTimer(m_disableJob); + } +} + void CXWindowsScreenSaver::disableCallback(void*) { diff --git a/lib/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h index dd1050cb..3285a1b7 100644 --- a/lib/platform/CXWindowsScreenSaver.h +++ b/lib/platform/CXWindowsScreenSaver.h @@ -97,6 +97,9 @@ private: // add window to the watch list void addWatchXScreenSaver(Window window); + // install/uninstall the job used to suppress the screensaver + void updateDisableJob(); + // called periodically to prevent the screen saver from starting void disableCallback(void*); @@ -140,9 +143,17 @@ private: int m_preferBlanking; int m_allowExposures; - // true iff the disabled job timer is installed + // true iff the client wants the screen saver suppressed bool m_disabled; + // true iff we're ignoring m_disabled. this is true, for example, + // when the client has called activate() and so presumably wants + // to activate the screen saver even if disabled. + bool m_suppressDisable; + + // true iff the disabled job timer is installed + bool m_disableJobInstalled; + // the job used to invoke disableCallback IJob* m_disableJob; }; From a14a462e22fba8f6d0323536882c035139d6f848 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 11 Jan 2003 21:06:21 +0000 Subject: [PATCH 419/807] Fixes to support FreeBSD and Darwin. --- acinclude.m4 | 11 +++++++++ configure.in | 2 +- lib/arch/CArchMultithreadPosix.cpp | 13 +++++++++++ lib/arch/CArchNetworkBSD.cpp | 10 +++++--- lib/arch/CArchNetworkBSD.h | 7 ------ lib/arch/CArchSleepUnix.cpp | 4 ++-- lib/arch/CMultibyteEmu.cpp | 37 ++++++++++++++++++++++++++++-- lib/common/Makefile.am | 13 ++++------- 8 files changed, 74 insertions(+), 23 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 8a57a75d..5b397620 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -480,6 +480,17 @@ if test "x$acx_pthread_ok" = xyes; then fi fi + # Detect pthread signal functions + AC_MSG_CHECKING([for pthread signal functions]) + AC_TRY_LINK([#include + #include ], + [pthread_kill(pthread_self(), SIGTERM);], + ok=yes, ok=unknown) + AC_MSG_RESULT(${ok}) + if test x"$ok" != xno; then + AC_DEFINE(HAVE_PTHREAD_SIGNAL,1,[Define if you have \`pthread_sigmask\' and \`pthread_kill\' functions.]) + fi + LIBS="$save_LIBS" CXXFLAGS="$save_CXXFLAGS" diff --git a/configure.in b/configure.in index 6b738b41..2ec6f592 100644 --- a/configure.in +++ b/configure.in @@ -48,7 +48,7 @@ ACX_CHECK_INET_ATON dnl checks for header files AC_HEADER_STDC -AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h]) +AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h wchar.h]) AC_CHECK_HEADERS([sys/socket.h sys/select.h]) AC_CHECK_HEADERS([istream ostream sstream]) AC_HEADER_TIME diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 20a2ad47..8fdd9dde 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -30,6 +30,17 @@ #define SIGWAKEUP SIGUSR1 +#if !HAVE_PTHREAD_SIGNAL + // boy, is this platform broken. forget about pthread signal + // handling and let signals through to every process. synergy + // will not terminate cleanly when it gets SIGTERM or SIGINT. +# define pthread_sigmask sigprocmask +# define pthread_kill(tid_, sig_) kill(0, (sig_)) +# define sigwait(set_, sig_) +# undef HAVE_POSIX_SIGWAIT +# define HAVE_POSIX_SIGWAIT 1 +#endif + // // CArchThreadImpl // @@ -318,7 +329,9 @@ CArchMultithreadPosix::newThread(ThreadFunc func, void* data) // can't tell the difference. if (!m_newThreadCalled) { m_newThreadCalled = true; +#if HAVE_PTHREAD_SIGNAL startSignalHandler(); +#endif } lockMutex(m_threadMutex); diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 474c68fc..c83ad827 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -327,7 +327,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) #else -void +int CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) { int i, n; @@ -765,8 +765,10 @@ CArchNetworkBSD::throwError(int err) case ENODEV: case ENOBUFS: case ENOMEM: - case ENOSR: case ENETDOWN: +#if defined(ENOSR) + case ENOSR: +#endif throw XArchNetworkResource(new XArchEvalUnix(err)); case EPROTOTYPE: @@ -777,8 +779,10 @@ CArchNetworkBSD::throwError(int err) case EINVAL: case ENOPROTOOPT: case EOPNOTSUPP: - case ENOPKG: case ESHUTDOWN: +#if defined(ENOPKG) + case ENOPKG: +#endif throw XArchNetworkSupport(new XArchEvalUnix(err)); case EIO: diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index 23d971bc..9718d71b 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -22,14 +22,7 @@ #endif #if !defined(HAVE_SOCKLEN_T) -// Darwin is so unsure what to use for socklen_t it makes us choose -# if defined(__APPLE__) -# if !defined(_BSD_SOCKLEN_T_) -# define _BSD_SOCKLEN_T_ int -# endif -# else typedef int socklen_t; -# endif #endif #define ARCH_NETWORK CArchNetworkBSD diff --git a/lib/arch/CArchSleepUnix.cpp b/lib/arch/CArchSleepUnix.cpp index 1e5675f4..35010721 100644 --- a/lib/arch/CArchSleepUnix.cpp +++ b/lib/arch/CArchSleepUnix.cpp @@ -69,7 +69,7 @@ CArchSleepUnix::sleep(double timeout) ARCH->testCancelThread(); #else /* emulate nanosleep() with select() */ - double startTime = time(); + double startTime = ARCH->time(); double timeLeft = timeout; while (timeLeft > 0.0) { struct timeval timeout2; @@ -82,7 +82,7 @@ CArchSleepUnix::sleep(double timeout) SELECT_TYPE_ARG234 NULL, SELECT_TYPE_ARG5 &timeout2); ARCH->testCancelThread(); - timeLeft = timeout - (time() - startTime); + timeLeft = timeout - (ARCH->time() - startTime); } #endif } diff --git a/lib/arch/CMultibyteEmu.cpp b/lib/arch/CMultibyteEmu.cpp index 80569fe5..0fd3087a 100644 --- a/lib/arch/CMultibyteEmu.cpp +++ b/lib/arch/CMultibyteEmu.cpp @@ -14,11 +14,42 @@ #include "CArch.h" #include -#include +#if HAVE_WCHAR_H +# include +#elif __APPLE__ + // wtf? Darwin puts mbtowc() et al. in stdlib +# include +#else + // platform apparently has no wchar_t support. provide dummy + // implementations. hopefully at least the C++ compiler has + // a built-in wchar_t type. +# undef HAVE_MBSINIT + +static inline +int +mbtowc(wchar_t* dst, const char* src, int n) +{ + *dst = static_cast(*src); + return 1; +} + +static inline +int +wctomb(char* dst, wchar_t src) +{ + *dst = static_cast(src); + return 1; +} + +#endif class CArchMBStateImpl { public: +#if HAVE_MBSINIT mbstate_t m_mbstate; +#else + int m_mbstate; // dummy data +#endif }; // @@ -50,7 +81,9 @@ ARCH_STRING::initMBState(CArchMBState state) bool ARCH_STRING::isInitMBState(CArchMBState state) { -#if !HAVE_MBSINIT + // if we're using this file mbsinit() probably doesn't exist + // but use it if it does +#if HAVE_MBSINIT return (mbsinit(&state->m_mbstate) != 0); #else return true; diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index 8a6abc21..4de7af6c 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -17,14 +17,6 @@ VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ common.dsp \ - $(NULL) - -MAINTAINERCLEANFILES = \ - Makefile.in \ - $(NULL) - -noinst_LIBRARIES = libcommon.a -libcommon_a_SOURCES = \ BasicTypes.h \ IInterface.h \ Version.h \ @@ -41,5 +33,10 @@ libcommon_a_SOURCES = \ stdstring.h \ stdvector.h \ $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + INCLUDES = \ $(NULL) From 780a6fd13dce0f636cffdc28515e500fc053a8a2 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 11 Jan 2003 21:07:14 +0000 Subject: [PATCH 420/807] Updated ChangeLog. --- ChangeLog | 70 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index f1565960..a16ee24c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,23 +1,71 @@ +2003/01/11 21:06:21 crs +acinclude.m4 +configure.in +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/CArchSleepUnix.cpp +lib/arch/CMultibyteEmu.cpp +lib/common/Makefile.am + +Fixes to support FreeBSD and Darwin. + +---------- +2003/01/11 15:16:41 crs +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h + +Synergy no longer tries to suppress the screen saver once it starts. +It was doing that already if started through synergy but not if +started by something outside of synergy. In particular, if you +use `xscreensaver-command --activate' synergy used to send fake +mouse motion events every 5 seconds to deactivate it. That's +unlikely to be what the user wanted, especially if the locking is +enabled since it would force the password dialog to appear. + +As before, it's recommended that client screens not use locking +because xscreensaver will not deactivate without getting a +password even if we make the request through a programmatic +interface. Presumably that's for security reasons but it makes +life harder for synergy. + +---------- +2003/01/11 14:01:44 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp + +Attempt to fix problems with multimon windows. The mouse position +reported by the synergy hook dll is in a space with 0,0 in the +upper-left which is not necessarily the same as the virtual desktop +space. So the windows primary screen now accounts for that. On +the secondary screen, mouse_event() doesn't seem to accept negative +coordinates even on the windows NT family, making monitors with +negative coordinates inaccessible via absolute moves. So if the +move will be to negative coordinates, use the windows 95 family +fallback of absolute moving to 0,0 then relative moving to the +final position. + +---------- 2003/01/08 22:17:44 crs -//depot/project/synergy/FAQ -//depot/project/synergy/INSTALL +FAQ +INSTALL Added bit about configuring on Solaris, which requires some options to find the X11 includes and libraries. ---------- 2003/01/08 21:36:14 crs -//depot/project/synergy/lib/arch/CArchMultithreadPosix.cpp -//depot/project/synergy/lib/arch/CArchMultithreadPosix.h -//depot/project/synergy/lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchNetworkBSD.cpp Portability fixes. Now builds on Linux 2.2 and 2.4 and solaris. Also builds on i386, alpha, G3/G4, and sparc. ---------- 2003/01/08 21:36:13 crs -//depot/project/synergy/lib/client/CClient.cpp -//depot/project/synergy/lib/platform/CXWindowsUtil.cpp +lib/client/CClient.cpp +lib/platform/CXWindowsUtil.cpp Changed log level of two messages. Now won't spew about reading window properties and will report connection failure at DEBUG @@ -25,16 +73,16 @@ instead of DEBUG1. ---------- 2003/01/08 21:36:10 crs -//depot/project/synergy/FAQ +FAQ Added a FAQ entry for client being rejected. User probably didn't start the server or told the client the wrong server host name. ---------- 2003/01/07 21:47:27 crs -//depot/project/synergy/ChangeLog -//depot/project/synergy/configure.in -//depot/project/synergy/lib/common/Version.h +ChangeLog +configure.in +lib/common/Version.h Changed version number to 0.9.15. Added 0.9.15 log entries. From b2e11d3d35c09b287168beaab9222473afafda6c Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 12 Jan 2003 16:08:45 +0000 Subject: [PATCH 421/807] Now catching and ignoring errors when writing to a socket in those cases where errors were not being caught, typically when responding to some other socket or protocol error. --- lib/server/CServer.cpp | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 0e101e9d..478be844 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -1289,7 +1289,12 @@ CServer::runClient(void* vsocket) catch (XDuplicateClient& e) { // client has duplicate name LOG((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); - CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); + try { + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); + } + catch (XSocket&) { + // ignore + } delete proxy; delete socket; return; @@ -1297,7 +1302,12 @@ CServer::runClient(void* vsocket) catch (XUnknownClient& e) { // client has unknown name LOG((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); - CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); + try { + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); + } + catch (XSocket&) { + // ignore + } delete proxy; delete socket; return; @@ -1324,7 +1334,12 @@ CServer::runClient(void* vsocket) catch (XBadClient&) { // client not behaving LOG((CLOG_WARN "protocol error from client \"%s\"", proxy->getName().c_str())); - CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); + try { + CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); + } + catch (XSocket& e) { + // ignore. client probably aborted the connection. + } } catch (XBase& e) { // misc error @@ -1434,13 +1449,23 @@ CServer::handshakeClient(IDataSocket* socket) catch (XIncompatibleClient& e) { // client is incompatible LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); - CProtocolUtil::writef(output, kMsgEIncompatible, + try { + CProtocolUtil::writef(output, kMsgEIncompatible, kProtocolMajorVersion, kProtocolMinorVersion); + } + catch (XSocket& e) { + // ignore + } } catch (XBadClient&) { // client not behaving LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); - CProtocolUtil::writef(output, kMsgEBad); + try { + CProtocolUtil::writef(output, kMsgEBad); + } + catch (XSocket& e) { + // ignore. client probably aborted the connection. + } } catch (XBase& e) { // misc error From 6c56f8a9fc2b79c9e4dfafc0fbd57bdd2130f230 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 12 Jan 2003 16:35:11 +0000 Subject: [PATCH 422/807] Removed unnecessary variable names. --- lib/server/CServer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 478be844..327475e3 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -1337,7 +1337,7 @@ CServer::runClient(void* vsocket) try { CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); } - catch (XSocket& e) { + catch (XSocket&) { // ignore. client probably aborted the connection. } } @@ -1453,7 +1453,7 @@ CServer::handshakeClient(IDataSocket* socket) CProtocolUtil::writef(output, kMsgEIncompatible, kProtocolMajorVersion, kProtocolMinorVersion); } - catch (XSocket& e) { + catch (XSocket&) { // ignore } } @@ -1463,7 +1463,7 @@ CServer::handshakeClient(IDataSocket* socket) try { CProtocolUtil::writef(output, kMsgEBad); } - catch (XSocket& e) { + catch (XSocket&) { // ignore. client probably aborted the connection. } } From 05f8f37888093c10e84f259f610d2b58eebc31d7 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 12 Jan 2003 16:35:54 +0000 Subject: [PATCH 423/807] Added test of using the client's own name as the server name with an appropriate error message. --- cmd/launcher/launcher.cpp | 12 ++++++++++++ cmd/launcher/launcher.rc | 1 + 2 files changed, 13 insertions(+) diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 0b17b224..7a4f4894 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -514,6 +514,18 @@ getCommandLine(HWND hwnd, bool testing) return CString(); } + // compare server name to local host. a common error + // is to provide the client's name for the server. we + // don't bother to check the addresses though that'd be + // more accurate. + if (CStringUtil::CaselessCmp::equal(ARCH->getHostName(), server)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SERVER_IS_CLIENT).c_str(), + server.c_str())); + SetFocus(child); + return CString(); + } + if (testing) { cmdLine += " --no-camp"; } diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 6091c59f..35974cc1 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -254,6 +254,7 @@ BEGIN IDS_UNINSTALLED_USER "Removed auto-start. Synergy will not automatically start each time you log in." IDS_INVALID_SERVER_NAME "Server name `%{1}' is invalid." IDS_TITLE "Synergy - Version %{1}" + IDS_SERVER_IS_CLIENT "Please enter the computer name of the synergy server, not\nthe name of this computer, in the Server Host Name field." END #endif // English (U.S.) resources From 0b67cdedf6e2a368ed56f59c8b5ab38ab0c1e740 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 12 Jan 2003 16:38:36 +0000 Subject: [PATCH 424/807] Added forgotten file for previous change. --- cmd/launcher/resource.h | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index f02d00ff..b5c78218 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -37,6 +37,7 @@ #define IDS_UNINSTALLED_USER 33 #define IDS_INVALID_SERVER_NAME 34 #define IDS_TITLE 35 +#define IDS_SERVER_IS_CLIENT 36 #define IDD_MAIN 101 #define IDD_ADD 102 #define IDD_WAIT 103 From f7e936faa96b61c65947e69146fc8feed4fdf42e Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 14 Jan 2003 19:46:17 +0000 Subject: [PATCH 425/807] Another try at fixing broken mouse behavior when a windows system has multiple monitors with 0,0 of the virtual desktop not at the upper-left. --- lib/platform/CMSWindowsPrimaryScreen.cpp | 7 ------- lib/platform/CMSWindowsSecondaryScreen.cpp | 2 ++ lib/platform/CSynergyHook.cpp | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 990120d4..0e036a04 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -310,13 +310,6 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) m_y = static_cast(msg->lParam); if (!isActive()) { - // shift by origin of virtual screen. the synergy - // hook DLL does not account for - SInt32 w, h; - m_screen->getShape(x, y, w, h); - x += m_x; - y += m_y; - // motion on primary screen m_receiver->onMouseMovePrimary(x, y); } diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index e680b9ba..35b4e5d0 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -357,6 +357,7 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) // motion is simple (i.e. it's on the primary monitor) if there // is only one monitor. bool simple = !m_screen->isMultimon(); +/* disable attempts to use simple motion with multiple monitors for now if (!simple) { // also simple if motion is within the primary monitor simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && @@ -374,6 +375,7 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) simple = (x0 == 0 && y0 == 0); } } +*/ // move the mouse directly to target position on NT family or if // not using multiple monitors. diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 60590f3d..b5824efb 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -209,20 +209,28 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) hideCursor(thread); } + // get position. it seems the positions are not + // in virtual desktop coordinates but in a similar + // space with 0,0 at the upper-left. translate + // into virtual desktop coordinates. + SInt32 x = (SInt32)info->pt.x + g_xScreen; + SInt32 y = (SInt32)info->pt.y + g_yScreen; + // relay the motion - SInt32 x = (SInt32)info->pt.x; - SInt32 y = (SInt32)info->pt.y; PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); } return 1; } } else { - // check for mouse inside jump zone + // check for mouse inside jump zone. it seems the positions + // are not in virtual desktop coordinates but in a similar + // space with 0,0 at the upper-left. translate into virtual + // desktop coordinates. bool inside = false; const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; - SInt32 x = (SInt32)info->pt.x; - SInt32 y = (SInt32)info->pt.y; + SInt32 x = (SInt32)info->pt.x + g_xScreen; + SInt32 y = (SInt32)info->pt.y + g_yScreen; if (!inside && (g_zoneSides & kLeftMask) != 0) { inside = (x < g_xScreen + g_zoneSize); } From a8bd4e7ff0f4c33d827541a5628e4011598055ab Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 14 Jan 2003 19:46:41 +0000 Subject: [PATCH 426/807] Moved log message into conditionals so it only appears when the conditions are true. --- lib/server/CServer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 327475e3..d72b33c2 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -700,18 +700,21 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) } else { // clamp to edge when locked - LOG((CLOG_DEBUG1 "clamp to \"%s\"", m_active->getName().c_str())); if (m_x < ax) { m_x = ax; + LOG((CLOG_DEBUG1 "clamp to left of \"%s\"", m_active->getName().c_str())); } else if (m_x > ax + aw - 1) { m_x = ax + aw - 1; + LOG((CLOG_DEBUG1 "clamp to right of \"%s\"", m_active->getName().c_str())); } if (m_y < ay) { m_y = ay; + LOG((CLOG_DEBUG1 "clamp to top of \"%s\"", m_active->getName().c_str())); } else if (m_y > ay + ah - 1) { m_y = ay + ah - 1; + LOG((CLOG_DEBUG1 "clamp to bottom of \"%s\"", m_active->getName().c_str())); } } From 24c0b3fd325e204cc3edbb656bb52c631bd7182a Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 16 Jan 2003 21:28:15 +0000 Subject: [PATCH 427/807] Fixed lookup of neighbor screens. The first problem was an old code in a conditional for moving left that blew an assert verifying that the mouse position was really on the screen if the neighbor screen wasn't connected. After that was fixed there was another problem when one screen linked to another which then linked (in the same direction) to itself. If the latter screen wasn't connected then it'd get into an infinite loop. --- lib/server/CServer.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index d72b33c2..9ae41237 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -841,30 +841,44 @@ CServer::getNeighbor(IClient* src, EDirection dir) const assert(src != NULL); + // get source screen name CString srcName = src->getName(); assert(!srcName.empty()); LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); - for (;;) { - // look up name of neighbor - const CString dstName(m_config.getNeighbor(srcName, dir)); - // if nothing in that direction then return NULL - if (dstName.empty()) { + // get first neighbor. if it's the source then the source jumps + // to itself and we return the source. + CString dstName(m_config.getNeighbor(srcName, dir)); + if (dstName == srcName) { + LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); + return src; + } + + // keep checking + for (;;) { + // if nothing in that direction then return NULL. if the + // destination is the source then we can make no more + // progress in this direction. since we haven't found a + // connected neighbor we return NULL. + if (dstName.empty() || dstName == srcName) { LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); return NULL; } // look up neighbor cell. if the screen is connected and - // ready then we can stop. otherwise we skip over an - // unconnected screen. + // ready then we can stop. CClientList::const_iterator index = m_clients.find(dstName); if (index != m_clients.end()) { LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); return index->second; } + // skip over unconnected screen LOG((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); srcName = dstName; + + // look up name of neighbor of skipped screen + dstName = m_config.getNeighbor(srcName, dir); } } @@ -897,7 +911,7 @@ CServer::getNeighbor(IClient* src, switch (srcSide) { case kLeft: x -= dx; - while (dst != NULL && dst != lastGoodScreen) { + while (dst != NULL) { lastGoodScreen = dst; lastGoodScreen->getShape(dx, dy, dw, dh); x += dw; From a3dcf9efdc1252638302fd456922340630ef0b60 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 16 Jan 2003 22:55:07 +0000 Subject: [PATCH 428/807] Fixed error in configure for Darwin. --- acinclude.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 5b397620..2ebdc27f 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -485,9 +485,9 @@ if test "x$acx_pthread_ok" = xyes; then AC_TRY_LINK([#include #include ], [pthread_kill(pthread_self(), SIGTERM);], - ok=yes, ok=unknown) + ok=yes, ok=no) AC_MSG_RESULT(${ok}) - if test x"$ok" != xno; then + if test x"$ok" = xyes; then AC_DEFINE(HAVE_PTHREAD_SIGNAL,1,[Define if you have \`pthread_sigmask\' and \`pthread_kill\' functions.]) fi From 75603c25f996e953c46a552b87e2751c4dfcb128 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 18 Jan 2003 10:49:13 +0000 Subject: [PATCH 429/807] Added a dist-pkg target to put the binary distribution files into a tar gzip file. This is to ease distribution of the binaries on systems without a packaging system supported by synergy (which currently supports only RPM). --- Makefile.am | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Makefile.am b/Makefile.am index 0ed794fe..edf9fec8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,26 @@ MAINTAINERCLEANFILES = \ doc/doxygen/html/* \ $(NULL) +PKG_FILES = \ + AUTHORS \ + BUGS \ + COPYING \ + ChangeLog \ + FAQ \ + HISTORY \ + INSTALL \ + NEWS \ + README \ + TODO \ + cmd/synergyc/synergyc \ + cmd/synergys/synergys \ + examples/synergy.conf \ + $(NULL) +PKG_PROG_FILES = \ + synergyc \ + synergys \ + $(NULL) + # build doxygen documentation doxygen: doxygen doc/doxygen.cfg @@ -68,3 +88,20 @@ dist-rpm: dist dist-zip: distdir zip -r $(distdir).zip $(distdir) -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) + +# build binary package. owner/group of packaged files will be +# owner/group of user running make. +PKGTOPDIR=/var/tmp/@PACKAGE@-@VERSION@ +dist-pkg: all + rm -rf $(PKGTOPDIR) + mkdir $(PKGTOPDIR) + mkdir $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + cp $(PKG_FILES) $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + (cd $(PKGTOPDIR)/@PACKAGE@-@VERSION@; \ + chmod 644 *; \ + chmod 755 $(PKG_PROG_FILES); \ + strip $(PKG_PROG_FILES) ) + type=`uname -s -m | tr '[A-Z] ' '[a-z].'`; \ + (cd $(PKGTOPDIR); tar cf - @PACKAGE@-@VERSION@ | \ + gzip - ) > @PACKAGE@-@VERSION@-1.$${type}.tar.gz && \ + rm -rf $(PKGTOPDIR) From 68a591210b35af05ce436338f9938a9350507133 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 18 Jan 2003 14:31:54 +0000 Subject: [PATCH 430/807] Was forcing modifier keys that have no effect on the keysym lookup to be up when synthesizing key events. Now leaving those modifiers in their current state. --- lib/platform/CXWindowsSecondaryScreen.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 996fb445..80a74d43 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -628,6 +628,16 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, required = clearBits(required, modifierBit); LOG((CLOG_DEBUG2 "desired = 0x%04x, current = 0x%04x", desired, m_mask)); + // some modifiers never have an effect on keysym lookup. leave + // those modifiers alone by copying their state from m_mask to + // desired. + desired = assignBits(desired, + ControlMask | + m_altMask | + m_metaMask | + m_superMask | + m_scrollLockMask, m_mask); + // add the key events required to get to the modifier state // necessary to generate an event yielding id. also save the // key events required to restore the state. if the key is From e86e552ac8558025dcbe0c68b9c953d93e842d94 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 18 Jan 2003 14:36:19 +0000 Subject: [PATCH 431/807] Fixed stupid errors introduced by last attempt to fix broken mouse behavior on multimonitor windows systems. Those errors broke synergy on all windows systems running as a server. Also added an attempt to reduce the occasional jump that can occur when switching screens when windows is the server. --- lib/arch/CArchSleepWindows.cpp | 3 +++ lib/platform/CMSWindowsPrimaryScreen.cpp | 19 ++++++++++++++++++- lib/platform/CSynergyHook.cpp | 6 ++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/arch/CArchSleepWindows.cpp b/lib/arch/CArchSleepWindows.cpp index 9c0fe596..f6c8bed8 100644 --- a/lib/arch/CArchSleepWindows.cpp +++ b/lib/arch/CArchSleepWindows.cpp @@ -46,6 +46,9 @@ CArchSleepWindows::sleep(double timeout) if (mt != NULL) { HANDLE cancelEvent = mt->getCancelEventForCurrentThread(); WaitForSingleObject(cancelEvent, (DWORD)(1000.0 * timeout)); + if (timeout == 0.0) { + Sleep(0); + } } else { Sleep((DWORD)(1000.0 * timeout)); diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 0e036a04..23476126 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -17,6 +17,7 @@ #include "IPrimaryScreenReceiver.h" #include "XScreen.h" #include "CLog.h" +#include "CArch.h" #include "CArchMiscWindows.h" #include @@ -311,7 +312,7 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) if (!isActive()) { // motion on primary screen - m_receiver->onMouseMovePrimary(x, y); + m_receiver->onMouseMovePrimary(m_x, m_y); } else { // motion on secondary screen. warp mouse back to @@ -641,6 +642,22 @@ CMSWindowsPrimaryScreen::warpCursorNoFlush(SInt32 x, SInt32 y) // between the previous message and the following message. SetCursorPos(x, y); + // yield the CPU. there's a race condition when warping: + // a hardware mouse event occurs + // the mouse hook is not called because that process doesn't have the CPU + // we send PRE_WARP, SetCursorPos(), send POST_WARP + // we process all of those events and update m_x, m_y + // we finish our time slice + // the hook is called + // the hook sends us a mouse event from the pre-warp position + // we get the CPU + // we compute a bogus warp + // we need the hook to process all mouse events that occur + // before we warp before we do the warp but i'm not sure how + // to guarantee that. yielding the CPU here may reduce the + // chance of undesired behavior. + ARCH->sleep(0.0); + // send an event that we can recognize after the mouse warp PostThreadMessage(m_threadID, SYNERGY_MSG_POST_WARP, 0, 0); } diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index b5824efb..23fe82fa 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -229,8 +229,8 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) // desktop coordinates. bool inside = false; const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; - SInt32 x = (SInt32)info->pt.x + g_xScreen; - SInt32 y = (SInt32)info->pt.y + g_yScreen; + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; if (!inside && (g_zoneSides & kLeftMask) != 0) { inside = (x < g_xScreen + g_zoneSize); } @@ -246,6 +246,8 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) // if inside then eat event and notify our window if (inside) { + x += g_xScreen; + y += g_yScreen; restoreCursor(); PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); return 1; From 784297af2463b334004356589eb89aca34728826 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 18 Jan 2003 16:21:36 +0000 Subject: [PATCH 432/807] Changed version number to 1.0.0. Documentation updates. --- ChangeLog | 77 ++++++++++++++++++++++++++++++++++++++++++++ INSTALL | 4 +-- NEWS | 16 +++++++++ TODO | 32 ++++++++++++------ configure.in | 6 ++-- lib/common/Version.h | 2 +- 6 files changed, 122 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index a16ee24c..80007917 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,80 @@ +2003/01/18 14:36:19 crs +lib/arch/CArchSleepWindows.cpp +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CSynergyHook.cpp + +Fixed stupid errors introduced by last attempt to fix broken +mouse behavior on multimonitor windows systems. Those errors +broke synergy on all windows systems running as a server. +Also added an attempt to reduce the occasional jump that can +occur when switching screens when windows is the server. + +---------- +2003/01/18 14:31:54 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +Was forcing modifier keys that have no effect on the keysym +lookup to be up when synthesizing key events. Now leaving +those modifiers in their current state. + +---------- +2003/01/18 10:49:13 crs +Makefile.am + +Added a dist-pkg target to put the binary distribution files into +a tar gzip file. This is to ease distribution of the binaries on +systems without a packaging system supported by synergy (which +currently supports only RPM). + +---------- +2003/01/16 21:28:15 crs +lib/server/CServer.cpp + +Fixed lookup of neighbor screens. The first problem was an old +code in a conditional for moving left that blew an assert verifying +that the mouse position was really on the screen if the neighbor +screen wasn't connected. + +After that was fixed there was another problem when one screen +linked to another which then linked (in the same direction) to +itself. If the latter screen wasn't connected then it'd get into +an infinite loop. + +---------- +2003/01/14 19:46:41 crs +lib/server/CServer.cpp + +Moved log message into conditionals so it only appears when the +conditions are true. + +---------- +2003/01/14 19:46:17 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CSynergyHook.cpp + +Another try at fixing broken mouse behavior when a windows system +has multiple monitors with 0,0 of the virtual desktop not at the +upper-left. + +---------- +2003/01/12 16:35:54 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added test of using the client's own name as the server name +with an appropriate error message. + +---------- +2003/01/12 16:08:45 crs +lib/server/CServer.cpp + +Now catching and ignoring errors when writing to a socket in those +cases where errors were not being caught, typically when responding +to some other socket or protocol error. + +---------- 2003/01/11 21:06:21 crs acinclude.m4 configure.in diff --git a/INSTALL b/INSTALL index b90c6be9..1f8f3188 100644 --- a/INSTALL +++ b/INSTALL @@ -13,8 +13,8 @@ To build synergy from the sources you'll need the following: * gcc 2.95 (or up) * X11R4 or up headers and libraries -In this document, "Unix" means any of the following: Linux, Solaris, -Irix. +In this document, "Unix" means any of the supported Unix or Unix-like +(e.g. Linux) operating systems. Configuring the build diff --git a/NEWS b/NEWS index 44e68298..e0d1d1a0 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,22 @@ Synergy News ============ +* Jan-20-2003 - Synergy 1.0.0 released + + Made following changes: + * Refactored to centralize platform dependent code + * Added support for mouse wheel on Windows NT (SP3 and up) + * Portability improvements + * Added more documentation + * Fixes for working with xscreensaver + * Fixes for circular screen links + + This release has been tested on Linux and Windows. It builds and + is believed to run on Solaris and FreeBSD. It is believed to + build and run on Irix and AIX. It builds but does not work on + MacOS X. + + * Dec-25-2002 - Synergy 0.9.14 released Made following changes: diff --git a/TODO b/TODO index eeda47d3..51016ffa 100644 --- a/TODO +++ b/TODO @@ -21,18 +21,11 @@ Things to do to synergy, in no particular order: Synergy currently supports only ASCII characters typed on the keyboard. It does, however, support Unicode clipboard text. -* Write man pages - -* Finish PORTING guide +* Write man/html pages * Port to other platforms - An incomplete list of desired platforms: - * MacOS 9 - * MacOS 10 - * BSD - * HP-UX - * AIX + Most desired is MacOS X. * Provide a nice win32 installer/uninstaller @@ -49,3 +42,24 @@ Things to do to synergy, in no particular order: is it should losslessly support any type it might be converted to. The type is converted to each platform's native type. For example, BMP for images on win32. + +* Keyboard shortcuts to jump from screen to screen + +Then there are major new features: + +* Provide a KVM mode + + In this mode synergy would share the monitor in addition to the + keyboard and mouse. + +* Add encryption and authentication + + Make synergy is safe to use on untrusted networks. + +* Support for limited drag and drop between systems + +* Support for (virtual) terminals on unix + + This would be useful in KVM mode to administer several remote + headless systems that you don't want running X just so synergy + can work. diff --git a/configure.in b/configure.in index 2ec6f592..d2a70bb8 100644 --- a/configure.in +++ b/configure.in @@ -17,9 +17,9 @@ AC_INIT(lib/common/common.h) AC_CONFIG_AUX_DIR(config) dnl current version -MAJOR_VERSION=0 -MINOR_VERSION=9 -RELEASE_VERSION=15 +MAJOR_VERSION=1 +MINOR_VERSION=0 +RELEASE_VERSION=0 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 8764eb36..71c2d078 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "0.9.15" +# define VERSION "1.0.0" #endif // important strings From a55119f096f22160de96c237982cde6910ef3d93 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 Jan 2003 08:36:43 +0000 Subject: [PATCH 433/807] Fixed running as a service on Windows NT family. --- cmd/synergyc/synergyc.cpp | 24 +++++++--------- cmd/synergys/synergys.cpp | 23 ++++++---------- lib/arch/CArchDaemonWindows.cpp | 41 ++++++++++++++++------------ lib/arch/CArchDaemonWindows.h | 16 +++++------ lib/arch/CArchLogWindows.cpp | 2 +- lib/arch/CArchMiscWindows.cpp | 4 +-- lib/arch/CArchMiscWindows.h | 3 +- lib/arch/CArchMultithreadWindows.cpp | 23 ++++++++++++++-- lib/arch/CArchMultithreadWindows.h | 1 + 9 files changed, 74 insertions(+), 63 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 2d1f89a2..7bf854be 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -145,6 +145,7 @@ realMain(void) locked = false; s_client->mainLoop(); locked = true; + DAEMON_RUNNING(false); // get client status if (s_client->wasRejected()) { @@ -157,6 +158,7 @@ realMain(void) #define FINALLY do { \ if (!locked) { \ DAEMON_RUNNING(false); \ + locked = true; \ } \ if (s_client != NULL) { \ if (opened) { \ @@ -488,13 +490,6 @@ byeThrow(int x) CArchMiscWindows::daemonFailed(x); } -static -void -daemonStop(void) -{ - s_client->exitMainLoop(); -} - static int daemonStartup(int argc, const char** argv) @@ -511,7 +506,7 @@ daemonStartup(int argc, const char** argv) ARG->m_backend = false; // run as a service - return CArchMiscWindows::runDaemon(realMain, daemonStop); + return CArchMiscWindows::runDaemon(realMain); } static @@ -545,12 +540,13 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - int result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); - if (result == -1) { - LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); return kExitFailed; } - return result; } // parse command line @@ -564,8 +560,8 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) try { result = ARCH->daemonize(DAEMON_NAME, &daemonStartup95); } - catch (XArchDaemon&) { - LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); result = kExitFailed; } } diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 285a7e58..c535aab7 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -175,12 +175,12 @@ realMain(void) DAEMON_RUNNING(true); locked = false; s_server->mainLoop(); - locked = true; // clean up #define FINALLY do { \ if (!locked) { \ DAEMON_RUNNING(false); \ + locked = true; \ } \ if (s_server != NULL) { \ if (opened) { \ @@ -209,7 +209,7 @@ realMain(void) catch (...) { // don't try to restart and fail ARG->m_restartable = false; - result = kExitFailed; + result = kExitFailed; FINALLY; } #undef FINALLY @@ -220,7 +220,7 @@ realMain(void) catch (XThread&) { // terminated ARG->m_restartable = false; - result = kExitTerminated; + result = kExitTerminated; } } while (ARG->m_restartable); @@ -619,13 +619,6 @@ byeThrow(int x) CArchMiscWindows::daemonFailed(x); } -static -void -daemonStop(void) -{ - s_server->exitMainLoop(); -} - static int daemonStartup(int argc, const char** argv) @@ -645,7 +638,7 @@ daemonStartup(int argc, const char** argv) loadConfig(); // run as a service - return CArchMiscWindows::runDaemon(realMain, daemonStop); + return CArchMiscWindows::runDaemon(realMain); } static @@ -682,8 +675,8 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) try { return ARCH->daemonize(DAEMON_NAME, &daemonStartup); } - catch (XArchDaemon&) { - LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); return kExitFailed; } } @@ -702,8 +695,8 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) try { result = ARCH->daemonize(DAEMON_NAME, &daemonStartup95); } - catch (XArchDaemon&) { - LOG((CLOG_CRIT "failed to start as a service" BYE, ARG->m_pname)); + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); result = kExitFailed; } } diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index c63d48ea..7633335e 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -24,7 +24,8 @@ CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL; -CArchDaemonWindows::CArchDaemonWindows() +CArchDaemonWindows::CArchDaemonWindows() : + m_daemonThread(NULL) { // do nothing } @@ -35,11 +36,11 @@ CArchDaemonWindows::~CArchDaemonWindows() } int -CArchDaemonWindows::runDaemon(RunFunc runFunc, StopFunc stopFunc) +CArchDaemonWindows::runDaemon(RunFunc runFunc) { assert(s_daemon != NULL); - return s_daemon->doRunDaemon(runFunc, stopFunc); + return s_daemon->doRunDaemon(runFunc); } void @@ -293,7 +294,7 @@ CArchDaemonWindows::daemonize(const char* name, DaemonFunc func) // hook us up to the service control manager. this won't return // (if successful) until the processes have terminated. s_daemon = this; - if (StartServiceCtrlDispatcher(entry)) { + if (StartServiceCtrlDispatcher(entry) == 0) { // StartServiceCtrlDispatcher failed s_daemon = NULL; throw XArchDaemonFailed(new XArchEvalWindows); @@ -536,7 +537,7 @@ CArchDaemonWindows::openUserStartupKey() } int -CArchDaemonWindows::doRunDaemon(RunFunc run, StopFunc stop) +CArchDaemonWindows::doRunDaemon(RunFunc run) { // should only be called from DaemonFunc assert(m_serviceMutex != NULL); @@ -546,7 +547,6 @@ CArchDaemonWindows::doRunDaemon(RunFunc run, StopFunc stop) ARCH->lockMutex(m_serviceMutex); try { int result; - m_stop = stop; m_serviceHandlerWaiting = false; m_serviceRunning = false; for (;;) { @@ -555,13 +555,11 @@ CArchDaemonWindows::doRunDaemon(RunFunc run, StopFunc stop) // run callback in another thread m_serviceRunning = true; - { - CArchThread thread = ARCH->newThread( + m_daemonThread = ARCH->newThread( &CArchDaemonWindows::runDaemonThreadEntry, run); - ARCH->wait(thread, -1.0); - result = reinterpret_cast(ARCH->getResultOfThread(thread)); - ARCH->closeThread(thread); - } + ARCH->wait(m_daemonThread, -1.0); + result = reinterpret_cast( + ARCH->getResultOfThread(m_daemonThread)); m_serviceRunning = false; // notify handler that the server stopped. if handler @@ -585,6 +583,10 @@ CArchDaemonWindows::doRunDaemon(RunFunc run, StopFunc stop) if (m_serviceState == SERVICE_STOPPED) { break; } + + // done with callback thread + ARCH->closeThread(m_daemonThread); + m_daemonThread = NULL; } // prevent daemonHandler from changing state @@ -598,7 +600,10 @@ CArchDaemonWindows::doRunDaemon(RunFunc run, StopFunc stop) setStatus(m_serviceState); // clean up - m_stop = NULL; + if (m_daemonThread != NULL) { + ARCH->closeThread(m_daemonThread); + m_daemonThread = NULL; + } ARCH->unlockMutex(m_serviceMutex); return result; @@ -716,7 +721,7 @@ CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) } // tell service control manager that we're starting - setStatus(SERVICE_START_PENDING, 0, 1000); + setStatus(SERVICE_START_PENDING, 0, 10000); // if no arguments supplied then try getting them from the registry. // the first argument doesn't count because it's the service name. @@ -830,12 +835,12 @@ CArchDaemonWindows::serviceHandler(DWORD ctrl) case SERVICE_CONTROL_PAUSE: // update state m_serviceState = SERVICE_PAUSE_PENDING; - setStatus(m_serviceState, 0, 1000); + setStatus(m_serviceState, 0, 5000); // stop run callback if running and wait for it to finish if (m_serviceRunning) { m_serviceHandlerWaiting = true; - m_stop(); + ARCH->cancelThread(m_daemonThread); ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } @@ -860,12 +865,12 @@ CArchDaemonWindows::serviceHandler(DWORD ctrl) case SERVICE_CONTROL_SHUTDOWN: // update state m_serviceState = SERVICE_STOP_PENDING; - setStatus(m_serviceState, 0, 1000); + setStatus(m_serviceState, 0, 5000); // stop run callback if running and wait for it to finish if (m_serviceRunning) { m_serviceHandlerWaiting = true; - m_stop(); + ARCH->cancelThread(m_daemonThread); ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h index 0feef9ba..9c291c9e 100644 --- a/lib/arch/CArchDaemonWindows.h +++ b/lib/arch/CArchDaemonWindows.h @@ -29,7 +29,6 @@ class CArchDaemonWindows : public IArchDaemon { public: typedef int (*RunFunc)(void); - typedef void (*StopFunc)(void); CArchDaemonWindows(); virtual ~CArchDaemonWindows(); @@ -41,12 +40,13 @@ public: daemon processing. The \c runFunc should perform the daemon's main loop, calling \c daemonRunning(true) when it enters the main loop (i.e. after initialization) and \c daemonRunning(false) when it leaves - the main loop. The \c stopFunc function is called when the daemon - must exit the main loop and must cause \c runFunc to return. This - function returns what \c runFunc returns. \c runFunc should call - \c daemonFailed() if the daemon fails. + the main loop. The \c runFunc is called in a new thread and when the + daemon must exit the main loop due to some external control the + thread is cancelled on behalf of the client. This function returns + what \c runFunc returns. \c runFunc should call \c daemonFailed() if + the daemon fails. */ - static int runDaemon(RunFunc runFunc, StopFunc stopFunc); + static int runDaemon(RunFunc runFunc); //! Indicate daemon is in main loop /*! @@ -87,7 +87,7 @@ private: static HKEY open95ServicesKey(); static HKEY openUserStartupKey(); - int doRunDaemon(RunFunc runFunc, StopFunc stopFunc); + int doRunDaemon(RunFunc runFunc); void doDaemonRunning(bool running); static void setStatus(DWORD state); @@ -120,8 +120,8 @@ private: DWORD m_serviceState; bool m_serviceHandlerWaiting; bool m_serviceRunning; - StopFunc m_stop; + CArchThread m_daemonThread; DaemonFunc m_daemonFunc; int m_daemonResult; diff --git a/lib/arch/CArchLogWindows.cpp b/lib/arch/CArchLogWindows.cpp index a1617da1..6c7ebbc4 100644 --- a/lib/arch/CArchLogWindows.cpp +++ b/lib/arch/CArchLogWindows.cpp @@ -77,7 +77,7 @@ CArchLogWindows::writeLog(ELevel level, const char* msg) 0, // event ID NULL, 0, - strlen(msg + 1), // raw data size + strlen(msg) + 1, // raw data size NULL, const_cast(msg));// raw data } diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index 25511076..40edd53e 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -35,9 +35,9 @@ CArchMiscWindows::isWindows95Family() } int -CArchMiscWindows::runDaemon(RunFunc runFunc, StopFunc stopFunc) +CArchMiscWindows::runDaemon(RunFunc runFunc) { - return CArchDaemonWindows::runDaemon(runFunc, stopFunc); + return CArchDaemonWindows::runDaemon(runFunc); } void diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index cce90ea6..2354f793 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -19,7 +19,6 @@ class CArchMiscWindows { public: typedef int (*RunFunc)(void); - typedef void (*StopFunc)(void); //! Test if windows 95, et al. /*! @@ -31,7 +30,7 @@ public: /*! Delegates to CArchDaemonWindows. */ - static int runDaemon(RunFunc runFunc, StopFunc stopFunc); + static int runDaemon(RunFunc runFunc); //! Indicate daemon is in main loop /*! diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index fdeff0fc..a44fe6b7 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -311,7 +311,7 @@ CArchMultithreadWindows::closeThread(CArchThread thread) // remove thread from list lockMutex(m_threadMutex); - assert(findNoRef(thread->m_id) == thread); + assert(findNoRefOrCreate(thread->m_id) == thread); erase(thread); unlockMutex(m_threadMutex); @@ -531,6 +531,23 @@ CArchMultithreadWindows::find(DWORD id) CArchThreadImpl* CArchMultithreadWindows::findNoRef(DWORD id) +{ + CArchThreadImpl* impl = findNoRefOrCreate(id); + if (impl == NULL) { + // create thread for calling thread which isn't in our list and + // add it to the list. this won't normally happen but it can if + // the system calls us under a new thread, like it does when we + // run as a service. + impl = new CArchThreadImpl; + impl->m_thread = NULL; + impl->m_id = GetCurrentThreadId(); + insert(impl); + } + return impl; +} + +CArchThreadImpl* +CArchMultithreadWindows::findNoRefOrCreate(DWORD id) { // linear search for (CThreadList::const_iterator index = m_threadList.begin(); @@ -548,7 +565,7 @@ CArchMultithreadWindows::insert(CArchThreadImpl* thread) assert(thread != NULL); // thread shouldn't already be on the list - assert(findNoRef(thread->m_id) == NULL); + assert(findNoRefOrCreate(thread->m_id) == NULL); // append to list m_threadList.push_back(thread); @@ -570,7 +587,7 @@ void CArchMultithreadWindows::refThread(CArchThreadImpl* thread) { assert(thread != NULL); - assert(findNoRef(thread->m_id) != NULL); + assert(findNoRefOrCreate(thread->m_id) != NULL); ++thread->m_refCount; } diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h index f6940ca5..e5003c1a 100644 --- a/lib/arch/CArchMultithreadWindows.h +++ b/lib/arch/CArchMultithreadWindows.h @@ -78,6 +78,7 @@ public: private: CArchThreadImpl* find(DWORD id); CArchThreadImpl* findNoRef(DWORD id); + CArchThreadImpl* findNoRefOrCreate(DWORD id); void insert(CArchThreadImpl* thread); void erase(CArchThreadImpl* thread); From 669f0b397486775a515911e7b548d2f96ba62cb3 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 Jan 2003 08:37:32 +0000 Subject: [PATCH 434/807] Changed version number to 1.0.1. --- NEWS | 6 ++++++ configure.in | 2 +- lib/common/Version.h | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index e0d1d1a0..24ee536b 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,12 @@ Synergy News ============ +* Jan-22-2003 - Synergy 1.0.1 released + + Made following changes: + * Fixed running as a service on Windows NT family + + * Jan-20-2003 - Synergy 1.0.0 released Made following changes: diff --git a/configure.in b/configure.in index d2a70bb8..15ff1af9 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=0 -RELEASE_VERSION=0 +RELEASE_VERSION=1 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 71c2d078..36a817ed 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.0" +# define VERSION "1.0.1" #endif // important strings From c4bdd235d08a3cf8d1b3d18d4d8707b128ec4f70 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 22 Jan 2003 08:38:20 +0000 Subject: [PATCH 435/807] Updates for version 1.0.1. --- ChangeLog | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ChangeLog b/ChangeLog index 80007917..c9a09a2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2003/01/22 08:37:32 crs +NEWS +configure.in +lib/common/Version.h + +Changed version number to 1.0.1. + +---------- +2003/01/22 08:36:43 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/CArchLogWindows.cpp +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h + +Fixed running as a service on Windows NT family. + +---------- 2003/01/18 14:36:19 crs lib/arch/CArchSleepWindows.cpp lib/platform/CMSWindowsPrimaryScreen.cpp From 188d89108bfbe806864784f3e17016dd84847dc1 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 25 Jan 2003 13:34:17 +0000 Subject: [PATCH 436/807] Added missing entry in a socket family table. This was a serious bug and should've failed on all platforms but just happened to work on linux and windows. --- lib/arch/CArchNetworkBSD.cpp | 1 + lib/arch/CArchNetworkWinsock.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index c83ad827..9b6eec6d 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -42,6 +42,7 @@ #endif static const int s_family[] = { + PF_UNSPEC, PF_INET }; static const int s_type[] = { diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index 1c10fb57..09b507ae 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -18,6 +18,7 @@ #include "XArchWindows.h" static const int s_family[] = { + PF_UNSPEC, PF_INET }; static const int s_type[] = { From 6b3e451b832fbe71221382a437b04059ac2241f0 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 25 Jan 2003 13:34:51 +0000 Subject: [PATCH 437/807] Added ability to set screen options from the windows launch dialog. --- cmd/launcher/launcher.cpp | 75 +++++++++++++++++++++++++++++++++++++-- cmd/launcher/launcher.rc | 27 +++++++++----- cmd/launcher/resource.h | 7 +++- lib/server/CConfig.cpp | 29 +++++++++++++-- lib/server/CConfig.h | 11 ++++-- 5 files changed, 132 insertions(+), 17 deletions(-) diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 7a4f4894..4b28794a 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -33,8 +33,9 @@ typedef std::vector CStringList; class CScreenInfo { public: - CString m_screen; - CStringList m_aliases; + CString m_screen; + CStringList m_aliases; + CConfig::CScreenOptions m_options; }; class CChildWaitInfo { @@ -266,6 +267,14 @@ addScreen(HWND hwnd) ARG->m_config.addAlias(info.m_screen, *index); } + // set options + ARG->m_config.removeOptions(info.m_screen); + for (CConfig::CScreenOptions::const_iterator + index = info.m_options.begin(); + index != info.m_options.end(); ++index) { + ARG->m_config.addOption(info.m_screen, index->first, index->second); + } + // update neighbors updateNeighbors(hwnd); enableScreensControls(hwnd); @@ -295,6 +304,11 @@ editScreen(HWND hwnd) info.m_aliases.push_back(index->first); } } + const CConfig::CScreenOptions* options = + ARG->m_config.getOptions(info.m_screen); + if (options != NULL) { + info.m_options = *options; + } // save current info CScreenInfo oldInfo = info; @@ -320,6 +334,14 @@ editScreen(HWND hwnd) ARG->m_config.addAlias(info.m_screen, *index); } + // set options + ARG->m_config.removeOptions(info.m_screen); + for (CConfig::CScreenOptions::const_iterator + index = info.m_options.begin(); + index != info.m_options.end(); ++index) { + ARG->m_config.addOption(info.m_screen, index->first, index->second); + } + // update list CString item = CStringUtil::print("%d. %s", index + 1, info.m_screen.c_str()); @@ -740,6 +762,18 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_INITDIALOG: { info = (CScreenInfo*)lParam; + // set title + CString title; + if (info->m_screen.empty()) { + title = getString(IDS_ADD_SCREEN); + } + else { + title = CStringUtil::format( + getString(IDS_EDIT_SCREEN).c_str(), + info->m_screen.c_str()); + } + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)title.c_str()); + // fill in screen name HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); SendMessage(child, WM_SETTEXT, 0, (LPARAM)info->m_screen.c_str()); @@ -756,6 +790,25 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); SendMessage(child, WM_SETTEXT, 0, (LPARAM)aliases.c_str()); + // set options + CConfig::CScreenOptions::const_iterator index; + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + index = info->m_options.find(kOptionHalfDuplexCapsLock); + if (index != info->m_options.end() && index->second != 0) { + SendMessage(child, BM_SETCHECK, BST_CHECKED, 0); + } + else { + SendMessage(child, BM_SETCHECK, BST_UNCHECKED, 0); + } + child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); + index = info->m_options.find(kOptionHalfDuplexNumLock); + if (index != info->m_options.end() && index->second != 0) { + SendMessage(child, BM_SETCHECK, BST_CHECKED, 0); + } + else { + SendMessage(child, BM_SETCHECK, BST_UNCHECKED, 0); + } + return TRUE; } @@ -824,10 +877,26 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } } - // save data + // save name data info->m_screen = newName; info->m_aliases = newAliases; + // save options + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + if (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED) { + info->m_options[kOptionHalfDuplexCapsLock] = 1; + } + else { + info->m_options.erase(kOptionHalfDuplexCapsLock); + } + child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); + if (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED) { + info->m_options[kOptionHalfDuplexNumLock] = 1; + } + else { + info->m_options.erase(kOptionHalfDuplexNumLock); + } + // success EndDialog(hwnd, 1); info = NULL; diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 35974cc1..b9840a49 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -106,18 +106,25 @@ BEGIN PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 END -IDD_ADD DIALOG DISCARDABLE 0, 0, 172, 95 +IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 170 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION CAPTION "Add Screen" FONT 8, "MS Sans Serif" BEGIN - LTEXT "Screen Name:",IDC_STATIC,7,9,46,8 - EDITTEXT IDC_ADD_SCREEN_NAME_EDIT,59,7,106,12,ES_AUTOHSCROLL - LTEXT "Aliases:",IDC_STATIC,7,25,25,8 - EDITTEXT IDC_ADD_ALIASES_EDIT,59,26,106,40,ES_MULTILINE | + LTEXT "&Screen Name:",IDC_STATIC,7,9,46,8 + EDITTEXT IDC_ADD_SCREEN_NAME_EDIT,79,7,106,12,ES_AUTOHSCROLL + LTEXT "&Aliases:",IDC_STATIC,7,25,25,8 + EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,40,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN - DEFPUSHBUTTON "OK",IDOK,59,74,50,14 - PUSHBUTTON "Cancel",IDCANCEL,115,74,50,14 + LTEXT "If your Caps Lock or Num Lock keys behave strangely on this client screen then try turning the half-duplex options on and reconnect the client.", + IDC_STATIC,13,82,165,25 + CONTROL "Half-duplex &Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,110,165,10 + CONTROL "Half-duplex &Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,122,165,10 + DEFPUSHBUTTON "OK",IDOK,79,149,50,14 + PUSHBUTTON "Cancel",IDCANCEL,135,149,50,14 + GROUPBOX "Options",IDC_STATIC,7,72,178,64 END IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54 @@ -170,9 +177,9 @@ BEGIN IDD_ADD, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 165 + RIGHTMARGIN, 185 TOPMARGIN, 7 - BOTTOMMARGIN, 88 + BOTTOMMARGIN, 163 END IDD_WAIT, DIALOG @@ -255,6 +262,8 @@ BEGIN IDS_INVALID_SERVER_NAME "Server name `%{1}' is invalid." IDS_TITLE "Synergy - Version %{1}" IDS_SERVER_IS_CLIENT "Please enter the computer name of the synergy server, not\nthe name of this computer, in the Server Host Name field." + IDS_ADD_SCREEN "Add Screen" + IDS_EDIT_SCREEN "Edit Screen %{1}" END #endif // English (U.S.) resources diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index b5c78218..9b7acf49 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -38,6 +38,8 @@ #define IDS_INVALID_SERVER_NAME 34 #define IDS_TITLE 35 #define IDS_SERVER_IS_CLIENT 36 +#define IDS_ADD_SCREEN 37 +#define IDS_EDIT_SCREEN 38 #define IDD_MAIN 101 #define IDD_ADD 102 #define IDD_WAIT 103 @@ -59,6 +61,7 @@ #define IDC_ADD_SCREEN_NAME_EDIT 1020 #define IDC_MAIN_SERVER_REMOVE_BUTTON 1020 #define IDC_ADD_ALIASES_EDIT 1021 +#define IDC_MAIN_SERVER_OPTIONS_BUTTON 1021 #define IDC_MAIN_SERVER_LEFT_COMBO 1022 #define IDC_MAIN_SERVER_RIGHT_COMBO 1023 #define IDC_MAIN_SERVER_TOP_COMBO 1024 @@ -74,6 +77,8 @@ #define IDC_AUTOSTART_INSTALL_SYSTEM 1034 #define IDC_MAIN_AUTOSTART 1035 #define IDC_MAIN_DEBUG 1036 +#define IDC_ADD_HD_CAPS_CHECK 1037 +#define IDC_ADD_HD_NUM_CHECK 1038 // Next default values for new objects // @@ -82,7 +87,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 106 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1037 +#define _APS_NEXT_CONTROL_VALUE 1038 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 66a5e94a..9ed619db 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -241,7 +241,7 @@ CConfig::setHTTPAddress(const CNetworkAddress& addr) } bool -CConfig::addOption(const CString& name, UInt32 option, SInt32 value) +CConfig::addOption(const CString& name, OptionID option, OptionValue value) { // find cell CCellMap::iterator index = m_map.find(name); @@ -255,7 +255,7 @@ CConfig::addOption(const CString& name, UInt32 option, SInt32 value) } bool -CConfig::removeOption(const CString& name, UInt32 option) +CConfig::removeOption(const CString& name, OptionID option) { // find cell CCellMap::iterator index = m_map.find(name); @@ -268,6 +268,20 @@ CConfig::removeOption(const CString& name, UInt32 option) return true; } +bool +CConfig::removeOptions(const CString& name) +{ + // find cell + CCellMap::iterator index = m_map.find(name); + if (index == m_map.end()) { + return false; + } + + // remove option + index->second.m_options.clear(); + return true; +} + bool CConfig::isValidScreenName(const CString& name) const { @@ -421,6 +435,17 @@ CConfig::operator==(const CConfig& x) const for (CCellMap::const_iterator index1 = m_map.begin(), index2 = x.m_map.begin(); index1 != m_map.end(); ++index1, ++index2) { + // compare names + if (!CStringUtil::CaselessCmp::equal(index1->first, index2->first)) { + return false; + } + + // compare options + if (index1->second.m_options != index2->second.m_options) { + return false; + } + + // compare neighbors for (UInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) { if (!CStringUtil::CaselessCmp::equal(index1->second.m_neighbor[i], index2->second.m_neighbor[i])) { diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index 1e29b8a7..a60b3abc 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -188,7 +188,7 @@ public: is a known screen. */ bool addOption(const CString& name, - UInt32 option, SInt32 value); + OptionID option, OptionValue value); //! Remove a screen option /*! @@ -196,7 +196,14 @@ public: nothing if the option doesn't exist on the screen. Returns true iff \c name is a known screen. */ - bool removeOption(const CString& name, UInt32 option); + bool removeOption(const CString& name, OptionID option); + + //! Remove a screen options + /*! + Removes all options and values from the named screen. Returns true + iff \c name is a known screen. + */ + bool removeOptions(const CString& name); //@} //! @name accessors From 06ed985f847876776fc800028f20c548ab6e49b5 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 25 Jan 2003 13:39:26 +0000 Subject: [PATCH 438/807] Changed version number to 1.0.2. --- NEWS | 7 +++++++ configure.in | 2 +- lib/common/Version.h | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 24ee536b..b13335b5 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,13 @@ Synergy News ============ +* Jan-25-2003 - Synergy 1.0.2 released + + Made following changes: + * Fixed out-of-bounds array lookup in the BSD and Windows network code + * Added ability to set screen options from Windows launch dialog + + * Jan-22-2003 - Synergy 1.0.1 released Made following changes: diff --git a/configure.in b/configure.in index 15ff1af9..bf1fef89 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=0 -RELEASE_VERSION=1 +RELEASE_VERSION=2 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 36a817ed..e2d3077e 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.1" +# define VERSION "1.0.2" #endif // important strings From eb65726ce76f26b8a9415a237d050141de2038ff Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 25 Jan 2003 13:40:34 +0000 Subject: [PATCH 439/807] Updates for version 1.0.2. --- ChangeLog | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/ChangeLog b/ChangeLog index c9a09a2c..c318605a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2003/01/25 13:39:26 crs +NEWS +configure.in +lib/common/Version.h + +Changed version number to 1.0.2. + +---------- +2003/01/25 13:34:51 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +lib/server/CConfig.cpp +lib/server/CConfig.h + +Added ability to set screen options from the windows launch dialog. + +---------- +2003/01/25 13:34:17 crs +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkWinsock.cpp + +Added missing entry in a socket family table. This was a serious +bug and should've failed on all platforms but just happened to +work on linux and windows. + +---------- 2003/01/22 08:37:32 crs NEWS configure.in From dedb48d24499c55c87ad0b86564a386e0ae8632a Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 29 Jan 2003 19:32:25 +0000 Subject: [PATCH 440/807] Applied patch from grmcdorman at users dot sourceforge dot net to support keymaps that have only uppercase letters, which is the case by default on the Sun X server (for US keyboards anyway). --- lib/platform/CXWindowsSecondaryScreen.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 80a74d43..1bacd828 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -28,6 +28,7 @@ # include # define XK_MISCELLANY # define XK_XKB_KEYS +# define XK_LATIN1 # include # if defined(HAVE_X11_EXTENSIONS_XTEST_H) # include @@ -1268,6 +1269,15 @@ CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask& mask) const // find the keycodes that generate the keysym KeyCodeIndex index = m_keycodeMap.find(keysym); + if (index == noKey() && keysym >= XK_a && keysym <= XK_z) { + // for an unfound lower case alpha, try the equivalent upper + // case (as some keymaps only include the upper case, notably + // Sun Solaris). + // Note that this depends on the keysyms for 'a' through 'z' + // and 'A' through 'Z' being contiguous. This is currently + // the case and extremely unlikely to change. + index = m_keycodeMap.find(keysym + XK_A - XK_a); + } if (index == noKey()) { // try backup keysym for certain keys (particularly the numpad // keys since most laptops don't have a separate numpad and the From 116005d64fc1e85b3490db554acb3248dbe4ad26 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 29 Jan 2003 22:16:40 +0000 Subject: [PATCH 441/807] To support keymaps with only upper (or lower) case keysyms we now use Xlib to convert an unmatched keysym to upper and lower case and use whichever, if any, is not the same as the original keysym. This supports case conversion in any language that Xlib supports it in. --- lib/platform/CXWindowsSecondaryScreen.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 1bacd828..801627e6 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -28,7 +28,6 @@ # include # define XK_MISCELLANY # define XK_XKB_KEYS -# define XK_LATIN1 # include # if defined(HAVE_X11_EXTENSIONS_XTEST_H) # include @@ -1269,14 +1268,15 @@ CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask& mask) const // find the keycodes that generate the keysym KeyCodeIndex index = m_keycodeMap.find(keysym); - if (index == noKey() && keysym >= XK_a && keysym <= XK_z) { - // for an unfound lower case alpha, try the equivalent upper - // case (as some keymaps only include the upper case, notably - // Sun Solaris). - // Note that this depends on the keysyms for 'a' through 'z' - // and 'A' through 'Z' being contiguous. This is currently - // the case and extremely unlikely to change. - index = m_keycodeMap.find(keysym + XK_A - XK_a); + if (index == noKey()) { + // try upper/lower case (as some keymaps only include the + // upper case, notably Sun Solaris). + KeySym lower, upper; + XConvertCase(keysym, &lower, &upper); + if (lower != keysym) + index = m_keycodeMap.find(lower); + else if (upper != keysym) + index = m_keycodeMap.find(upper); } if (index == noKey()) { // try backup keysym for certain keys (particularly the numpad From f0445295b54ae901a024a97be140fba04423c23e Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 Feb 2003 18:10:43 +0000 Subject: [PATCH 442/807] Added info about using SSH for authentication and encryption. --- FAQ | 23 +++++++++++++++++++++-- INSTALL | 36 ++++++++++++++++++++++++++++++++++++ README | 5 +++++ TODO | 3 ++- 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/FAQ b/FAQ index 8805d1d1..c007188b 100644 --- a/FAQ +++ b/FAQ @@ -57,12 +57,31 @@ Answers 5. What security/encryption does synergy provide? - None. Synergy currently does no encryption or authentication. + Synergy provides no built-in encryption or authentication. Given that, synergy should not be used on or over any untrusted network, especially the Internet. It's generally fine for home - networks. Future versions will provide encryption and + networks. Future versions may provide built-in encryption and authentication. + Strong encryption and authentication is available through SSH + (secure shell). Run the SSH daemon (i.e. server) on the same + computer that you run the synergy server. It requires no + special configuration to support synergy. On each synergy + client system, run SSH with port forwarding: + + ssh -f -N -L 24800::24800 + + where is the name of the SSH/synergy server. + Once ssh authenticates itself, start the synergy client + normally except use `localhost' or `127.0.0.1' as the server's + address. SSH will then encrypt all communication on behalf of + synergy. Authentication is handled by the SSH authentication. + + A free implementation of SSH for Linux and many Unix systems + called OpenSSH is available from http://www.openssh.com/. For + Windows there's a port of OpenSSH using Cygwin + (http://www.cygwin.com/). + 6. What should I call my screens in the configuration? You can use any unique name in the configuration file for each diff --git a/INSTALL b/INSTALL index 1f8f3188..1e2f25b9 100644 --- a/INSTALL +++ b/INSTALL @@ -365,6 +365,42 @@ prevent a synergy client from synthesizing mouse and keyboard input, though. +Network Security +---------------- + +Synergy has no built-in support for encryption or authentication. +The server accepts connections from any computer. The server and +clients send all data unencrypted which means the clipboard and +mouse and keyboard events (e.g. typed passwords) are easily +examined by anyone listening on the network. Therefore, do not +run synergy on untrusted networks except as follows. + +You can use SSH (secure shell) to provide strong authentication +and encryption to synergy without modifying either SSH or synergy. +On Linux and Unix a free implementation of SSH called OpenSSH is +available at http://www.openssh.com/. On Windows you can use the +Cygwin version of OpenSSH. + +First, install the SSH server (sshd) on the computer running the +synergy server. Next, install the SSH client (ssh) on each +synergy client computer. Start the SSH and synergy servers +normally. Then, for each client, start the SSH client with port +forwarding: + + ssh -f -N -L 24800::24800 + +where is the name or address of the SSH and +synergy server host. 24800 is the default synergy port; replace +it with whichever port you use if you don't use the default. Once +ssh authenticates with the server, start the synergy client as +usual except use `localhost' or `127.0.0.1' for the server +address. Synergy will then pass all communication through SSH +which encrypts it, passes it over the network, decrypts it, and +hands it back to synergy. Authentication is provided by SSH's +authentication. + + + Common Command Line Options --------------------------- -d, --debug use debugging level diff --git a/README b/README index cec0cd53..13d5d4d4 100644 --- a/README +++ b/README @@ -240,6 +240,11 @@ Tips and Tricks * A client's keyboard and mouse are fully functional while synergy is running. You can use them in case synergy locks up. +* Strong authentication and encryption is available by using SSH. See + the INSTALL file for more information. Synergy does not otherwise + provide secure communications and it should not be used on or over + untrusted networks. + Bug Reports ----------- diff --git a/TODO b/TODO index 51016ffa..b6f13e8e 100644 --- a/TODO +++ b/TODO @@ -54,7 +54,8 @@ Then there are major new features: * Add encryption and authentication - Make synergy is safe to use on untrusted networks. + Make synergy is safe to use on untrusted networks. Using synergy + through SSH can provide this until synergy has it built-in. * Support for limited drag and drop between systems From 154a474289a8e595f96fe7770ab33386b2a9c8a6 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 12 Feb 2003 19:38:39 +0000 Subject: [PATCH 443/807] Made sure every file includes common.h directly or indirectly. Also made sure common.h is included before any system headers. --- lib/arch/CArchMiscWindows.h | 2 ++ lib/arch/XArch.h | 1 + lib/base/CLog.h | 1 - lib/base/CString.h | 1 + lib/base/ILogOutputter.h | 1 + lib/mt/CCondVar.h | 1 - lib/mt/CLock.h | 2 ++ lib/mt/CThread.h | 1 - lib/mt/CTimerThread.h | 2 ++ 9 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index 2354f793..51433ba4 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -15,6 +15,8 @@ #ifndef CARCHMISCWINDOWS_H #define CARCHMISCWINDOWS_H +#include "common.h" + //! Miscellaneous win32 functions. class CArchMiscWindows { public: diff --git a/lib/arch/XArch.h b/lib/arch/XArch.h index 131e07ed..44c9394d 100644 --- a/lib/arch/XArch.h +++ b/lib/arch/XArch.h @@ -15,6 +15,7 @@ #ifndef XARCH_H #define XARCH_H +#include "common.h" #include "stdstring.h" //! Generic thread exception diff --git a/lib/base/CLog.h b/lib/base/CLog.h index d76f9140..88feea92 100644 --- a/lib/base/CLog.h +++ b/lib/base/CLog.h @@ -17,7 +17,6 @@ #include "common.h" #include "IArchMultithread.h" -#include "IInterface.h" #include "stdlist.h" #include diff --git a/lib/base/CString.h b/lib/base/CString.h index e3eaa3b0..bc905009 100644 --- a/lib/base/CString.h +++ b/lib/base/CString.h @@ -15,6 +15,7 @@ #ifndef CSTRING_H #define CSTRING_H +#include "common.h" #include "stdstring.h" // use standard C++ string class for our string class diff --git a/lib/base/ILogOutputter.h b/lib/base/ILogOutputter.h index b4152c9e..585598c2 100644 --- a/lib/base/ILogOutputter.h +++ b/lib/base/ILogOutputter.h @@ -15,6 +15,7 @@ #ifndef ILOGOUTPUTTER_H #define ILOGOUTPUTTER_H +#include "IInterface.h" #include "CLog.h" //! Outputter interface diff --git a/lib/mt/CCondVar.h b/lib/mt/CCondVar.h index 1b92ffd7..78fbfe90 100644 --- a/lib/mt/CCondVar.h +++ b/lib/mt/CCondVar.h @@ -17,7 +17,6 @@ #include "CMutex.h" #include "BasicTypes.h" -#include "IArchMultithread.h" class CStopwatch; diff --git a/lib/mt/CLock.h b/lib/mt/CLock.h index 33a14f68..f00e0cd1 100644 --- a/lib/mt/CLock.h +++ b/lib/mt/CLock.h @@ -15,6 +15,8 @@ #ifndef CLOCK_H #define CLOCK_H +#include "common.h" + class CMutex; class CCondVarBase; diff --git a/lib/mt/CThread.h b/lib/mt/CThread.h index aaa5a097..6592426b 100644 --- a/lib/mt/CThread.h +++ b/lib/mt/CThread.h @@ -15,7 +15,6 @@ #ifndef CTHREAD_H #define CTHREAD_H -#include "common.h" #include "IArchMultithread.h" class IJob; diff --git a/lib/mt/CTimerThread.h b/lib/mt/CTimerThread.h index 11a0da73..c0a43c51 100644 --- a/lib/mt/CTimerThread.h +++ b/lib/mt/CTimerThread.h @@ -15,6 +15,8 @@ #ifndef CTIMERTHREAD_H #define CTIMERTHREAD_H +#include "common.h" + class CThread; //! A timer thread From fefe45751774f1adeeb52b70c0e2fad8c859b991 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 12 Feb 2003 19:50:22 +0000 Subject: [PATCH 444/807] Added a simple implementation of vsnprintf for unix platforms without it using /dev/null, vfprintf(), and vsprintf(). --- lib/arch/vsnprintf.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/arch/vsnprintf.cpp b/lib/arch/vsnprintf.cpp index 4bd665d2..f06c0a30 100644 --- a/lib/arch/vsnprintf.cpp +++ b/lib/arch/vsnprintf.cpp @@ -28,7 +28,33 @@ ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) return n; } -#else // !HAVE_VSNPRINTF +#elif UNIX_LIKE // !HAVE_VSNPRINTF + +#include + +int +ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) +{ + static FILE* bitbucket = fopen("/dev/null", "w"); + if (bitbucket == NULL) { + // uh oh + if (size > 0) { + str[0] = '\0'; + } + return 0; + } + else { + // count the characters using the bitbucket + int n = vfprintf(bitbucket, fmt, ap); + if (n + 1 <= size) { + // it'll fit so print it into str + vsprintf(str, fmt, ap); + } + return n; + } +} + +#else // !HAVE_VSNPRINTF && !UNIX_LIKE // FIXME #error vsnprintf not implemented From c148bc7c9c9acfda8980c07563cb4f7cdbb015a0 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 12 Feb 2003 20:59:08 +0000 Subject: [PATCH 445/807] Fixed error in debug build on win32. --- lib/arch/CArchDaemonWindows.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index 7633335e..6f4f14e3 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -542,7 +542,6 @@ CArchDaemonWindows::doRunDaemon(RunFunc run) // should only be called from DaemonFunc assert(m_serviceMutex != NULL); assert(run != NULL); - assert(stop != NULL); ARCH->lockMutex(m_serviceMutex); try { From 57ba0cb66052d4989c28e5bd39f163974e8e68dc Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 12 Feb 2003 20:59:25 +0000 Subject: [PATCH 446/807] Fixed incorrect mouse button swapping on client screens. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 16 ++++++++++++++++ lib/platform/CXWindowsSecondaryScreen.cpp | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index 35b4e5d0..7e078763 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -637,6 +637,22 @@ static const UINT g_mapEF00[] = DWORD CMSWindowsSecondaryScreen::mapButton(ButtonID button, bool press) const { + // the system will swap the meaning of left/right for us if + // the user has configured a left-handed mouse but we don't + // want it to swap since we want the handedness of the + // server's mouse. so pre-swap for a left-handed mouse. + if (GetSystemMetrics(SM_SWAPBUTTON)) { + switch (button) { + case kButtonLeft: + button = kButtonRight; + break; + + case kButtonRight: + button = kButtonLeft; + break; + } + } + // map button id to button flag switch (button) { case kButtonLeft: diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 801627e6..1e848887 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -450,7 +450,7 @@ CXWindowsSecondaryScreen::mapButton(ButtonID id) const return 0; } else { - return static_cast(id); + return static_cast(m_buttons[id - 1]); } } From 3351a66f51d422776f74d0bc09ea8f8e27f329c0 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 Feb 2003 19:49:44 +0000 Subject: [PATCH 447/807] Fixed memory leaks. --- cmd/synergyc/synergyc.cpp | 7 +++++-- cmd/synergys/synergys.cpp | 7 +++++-- lib/arch/CArchMultithreadWindows.cpp | 9 +++++++++ lib/base/CUnicode.cpp | 1 + 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 7bf854be..a16d3407 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -540,13 +540,15 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + int result = kExitFailed; try { - return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); } catch (XArchDaemon& e) { LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - return kExitFailed; } + delete CLOG; + return result; } // parse command line @@ -586,6 +588,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); } + delete CLOG; return result; } diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index c535aab7..0e676d0a 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -672,13 +672,15 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + int result = kExitFailed; try { - return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); } catch (XArchDaemon& e) { LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - return kExitFailed; } + delete CLOG; + return result; } // parse command line @@ -721,6 +723,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); } + delete CLOG; return result; } diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index a44fe6b7..315a3979 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -97,6 +97,15 @@ CArchMultithreadWindows::CArchMultithreadWindows() CArchMultithreadWindows::~CArchMultithreadWindows() { s_instance = NULL; + + // clean up thread list + for (CThreadList::iterator index = m_threadList.begin(); + index != m_threadList.end(); ++index) { + delete *index; + } + + // done with mutex + delete m_threadMutex; } HANDLE diff --git a/lib/base/CUnicode.cpp b/lib/base/CUnicode.cpp index 5cac46d6..9d8fd8d2 100644 --- a/lib/base/CUnicode.cpp +++ b/lib/base/CUnicode.cpp @@ -406,6 +406,7 @@ CUnicode::textToUTF8(const CString& src, bool* errors) // clean up delete[] wcs; + ARCH->closeMBState(state); return utf8; } From 60fdb1f529d094acbd824a2a8b589c245e6d1bc5 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 Feb 2003 19:50:36 +0000 Subject: [PATCH 448/807] Changed heap to stack allocation in an oft-called function for data that's never used outside the function. --- lib/platform/CMSWindowsScreen.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 962b9047..d1471218 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -26,6 +26,8 @@ #include "CStringUtil.h" #include "CArchMiscWindows.h" #include +#include +#include // // add backwards compatible multihead support (and suppress bogus warning) @@ -726,10 +728,9 @@ CMSWindowsScreen::getDesktopName(HDESK desk) const else { DWORD size; GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); - TCHAR* name = new TCHAR[size / sizeof(TCHAR) + 1]; + TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR)); GetUserObjectInformation(desk, UOI_NAME, name, size, &size); CString result(name); - delete[] name; return result; } } @@ -737,7 +738,22 @@ CMSWindowsScreen::getDesktopName(HDESK desk) const bool CMSWindowsScreen::isCurrentDesktop(HDESK desk) const { - return CStringUtil::CaselessCmp::equal(getDesktopName(desk), m_deskName); + // don't allocate space for current desktop name on heap since + // we do this a lot and we never save the name. + TCHAR* name; + if (desk == NULL) { + name = _T(""); + } + else if (m_is95Family) { + name = _T("desktop"); + } + else { + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + name = (TCHAR*)alloca(size + sizeof(TCHAR)); + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + } + return (_tcsicmp(name, m_deskName.c_str()) == 0); } LRESULT CALLBACK From 9a7e452a3ebf257541c11a579976f1a80536b62b Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 Feb 2003 19:51:46 +0000 Subject: [PATCH 449/807] Commented out an unnecessary hook and added a compile time switch to disable grabbing of keyboard on win32 to facilitate debugging. --- lib/platform/CSynergyHook.cpp | 40 +++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 23fe82fa..a68aade9 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -16,6 +16,14 @@ #include "ProtocolTypes.h" #include +// +// debugging compile flag. when not zero the server doesn't grab +// the keyboard when the mouse leaves the server screen. this +// makes it possible to use the debugger (via the keyboard) when +// all user input would normally be caught by the hook procedures. +// +#define NO_GRAB_KEYBOARD 0 + // // extra mouse wheel stuff // @@ -258,6 +266,7 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_mouse, code, wParam, lParam); } +/* static LRESULT CALLBACK cbtHook(int code, WPARAM wParam, LPARAM lParam) @@ -270,6 +279,7 @@ cbtHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_cbt, code, wParam, lParam); } +*/ static LRESULT CALLBACK @@ -409,6 +419,7 @@ getLowLevelProc(void*) MSG msg; PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); +#if !NO_GRAB_KEYBOARD // install low-level keyboard hook g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, &keyboardLLHook, @@ -420,6 +431,10 @@ getLowLevelProc(void*) SetEvent(g_hookEventLL); return 1; } +#else + // keep compiler quiet + &keyboardLLHook; +#endif // install low-level mouse hook g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL, @@ -428,8 +443,10 @@ getLowLevelProc(void*) 0); if (g_mouseLL == NULL) { // indicate failure and exit - UnhookWindowsHookEx(g_keyboardLL); - g_keyboardLL = NULL; + if (g_keyboardLL != NULL) { + UnhookWindowsHookEx(g_keyboardLL); + g_keyboardLL = NULL; + } g_hookThreadIDLL = 0; SetEvent(g_hookEventLL); return 1; @@ -634,6 +651,7 @@ install() g_wheelSupport = getWheelSupport(); // install keyboard hook +#if !NO_GRAB_KEYBOARD g_keyboard = SetWindowsHookEx(WH_KEYBOARD, &keyboardHook, g_hinstance, @@ -642,6 +660,10 @@ install() g_threadID = NULL; return 0; } +#else + // keep compiler quiet + &keyboardHook; +#endif // install mouse hook g_mouse = SetWindowsHookEx(WH_MOUSE, @@ -650,12 +672,15 @@ install() 0); if (g_mouse == NULL) { // uninstall keyboard hook before failing - UnhookWindowsHookEx(g_keyboard); - g_keyboard = NULL; + if (g_keyboard != NULL) { + UnhookWindowsHookEx(g_keyboard); + g_keyboard = NULL; + } g_threadID = NULL; return 0; } +/* // install CBT hook g_cbt = SetWindowsHookEx(WH_CBT, &cbtHook, @@ -663,13 +688,16 @@ install() 0); if (g_cbt == NULL) { // uninstall keyboard and mouse hooks before failing - UnhookWindowsHookEx(g_keyboard); + if (g_keyboard != NULL) { + UnhookWindowsHookEx(g_keyboard); + g_keyboard = NULL; + } UnhookWindowsHookEx(g_mouse); - g_keyboard = NULL; g_mouse = NULL; g_threadID = NULL; return 0; } +*/ // install GetMessage hook (unless already installed) if (g_wheelSupport == kWheelOld && g_getMessage == NULL) { From 2fc8780285458123f4513822de6f0817499971da Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 Feb 2003 19:53:56 +0000 Subject: [PATCH 450/807] Added hack to heuristically detect bogus mouse motion caused by a race condition where the synergy server updates the mouse position but the synergy hook later receives a mouse update from before the position change (i.e. out of order). --- lib/platform/CMSWindowsPrimaryScreen.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 23476126..f8622970 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -321,8 +321,24 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) // back to center warpCursorNoFlush(m_xCenter, m_yCenter); - // send motion - m_receiver->onMouseMoveSecondary(x, y); + // examine the motion. if it's about the distance + // from the center of the screen to an edge then + // it's probably a bogus motion that we want to + // ignore (see warpCursorNoFlush() for a further + // description). + static SInt32 bogusZoneSize = 10; + SInt32 x0, y0, w0, h0; + m_screen->getShape(x0, y0, w0, h0); + if (-x + bogusZoneSize > m_xCenter - x0 || + x + bogusZoneSize > x0 + w0 - m_xCenter || + -y + bogusZoneSize > m_yCenter - y0 || + y + bogusZoneSize > y0 + h0 - m_yCenter) { + LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y)); + } + else { + // send motion + m_receiver->onMouseMoveSecondary(x, y); + } } } } @@ -655,7 +671,9 @@ CMSWindowsPrimaryScreen::warpCursorNoFlush(SInt32 x, SInt32 y) // we need the hook to process all mouse events that occur // before we warp before we do the warp but i'm not sure how // to guarantee that. yielding the CPU here may reduce the - // chance of undesired behavior. + // chance of undesired behavior. we'll also check for very + // large motions that look suspiciously like about half width + // or height of the screen. ARCH->sleep(0.0); // send an event that we can recognize after the mouse warp From dd339fe3757fb9e9aa33ba082ae4826bbe3ae2f8 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 Feb 2003 19:55:54 +0000 Subject: [PATCH 451/807] Changed win32 client side cursor warping to be all relative motion when not on the primary monitor. This should eliminate the flicker between virtual display 0,0 and the correct position. While this allows the user to confuse synergy by using the client's mouse, synergy recovers quickly and easily from any confusion. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 105 +++++---------------- 1 file changed, 25 insertions(+), 80 deletions(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index 7e078763..7783e885 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -350,35 +350,16 @@ CMSWindowsSecondaryScreen::hideWindow() void CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) { - typedef UINT (WINAPI *SendInput_t)(UINT, LPINPUT, int); - static bool gotSendInput = false; - static SendInput_t SendInput = NULL; - // motion is simple (i.e. it's on the primary monitor) if there // is only one monitor. bool simple = !m_screen->isMultimon(); -/* disable attempts to use simple motion with multiple monitors for now if (!simple) { // also simple if motion is within the primary monitor simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); - if (!simple && !m_is95Family) { - // also simple if not on windows 95 family since the - // NT family mouse_event() allows absolute moves to - // any monitor. - // - // note -- this is possibly untrue if the primary - // monitor isn't upper-left most so limit it to that - // situation. - SInt32 x0, y0, w, h; - m_screen->getShape(x0, y0, w, h); - simple = (x0 == 0 && y0 == 0); - } } -*/ - // move the mouse directly to target position on NT family or if - // not using multiple monitors. + // move the mouse directly to target position if motion is simple if (simple) { SInt32 x0, y0, w, h; m_screen->getShape(x0, y0, w, h); @@ -394,39 +375,29 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) // words, "by design." apparently the designers of windows 2000 // we're a little less lazy and did it right. // - // we use the microsoft recommendation (Q193003): set the absolute - // position on the primary monitor, disable mouse acceleration, - // relative move the mouse to the final location, restore mouse - // acceleration. to avoid one kind of race condition (the user - // clicking the mouse or pressing a key between the absolute and - // relative move) we'll use SendInput() which guarantees that the - // events are delivered uninterrupted. we cannot prevent changes - // to the mouse acceleration at inopportune times, though. if - // SendInput() is unavailable then use mouse_event(); SendInput() - // is not available on Windows 95 and NT 4.0 prior to SP3. + // microsoft recommends in Q193003 to absolute position the cursor + // somewhere on the primary monitor then relative move to the + // desired location. this doesn't work for us because when the + // user drags a scrollbar, a window, etc. it causes the dragged + // item to jump back a forth between the position on the primary + // monitor and the desired position. while it always ends up in + // the right place, the effect is disconcerting. // - // point-to-activate (x-mouse) doesn't seem to be bothered by the - // absolute/relative combination. a window over the absolute - // position (0,0) does *not* get activated (at least not on win2k) - // if the relative move puts the cursor elsewhere. similarly, the - // app under the final mouse position does *not* get deactivated - // by the absolute move to 0,0. + // instead we'll get the cursor's current position and do just a + // relative move from there to the desired position. relative + // moves are subject to cursor acceleration which we don't want. + // so we disable acceleration, do the relative move, then restore + // acceleration. there's a slight chance we'll end up in the + // wrong place if the user moves the cursor using this system's + // mouse while simultaneously moving the mouse on the server + // system. that defeats the purpose of synergy so we'll assume + // that won't happen. even if it does, the next mouse move will + // correct the position. else { - // lookup SendInput() function - if (!gotSendInput) { - gotSendInput = true; - HINSTANCE user32 = LoadLibrary("user32.dll"); - if (user32 != NULL) { - SendInput = reinterpret_cast( - GetProcAddress(user32, "SendInput")); - FreeLibrary(user32); - } - } - // save mouse speed & acceleration int oldSpeed[4]; bool accelChanged = - SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed,0) && + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); // use 1:1 motion @@ -437,38 +408,12 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); } - // send events - INPUT events[2]; - events[0].type = INPUT_MOUSE; - events[0].mi.dx = 0; - events[0].mi.dy = 0; - events[0].mi.mouseData = 0; - events[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; - events[0].mi.time = GetTickCount(); - events[0].mi.dwExtraInfo = 0; - events[1].type = INPUT_MOUSE; - events[1].mi.dx = x; - events[1].mi.dy = y; - events[1].mi.mouseData = 0; - events[1].mi.dwFlags = MOUSEEVENTF_MOVE; - events[1].mi.time = events[0].mi.time; - events[1].mi.dwExtraInfo = 0; - if (SendInput != NULL) { - SendInput(sizeof(events) / sizeof(events[0]), - events, sizeof(events[0])); - } - else { - mouse_event(events[0].mi.dwFlags, - events[0].mi.dx, - events[0].mi.dy, - events[0].mi.mouseData, - events[0].mi.dwExtraInfo); - mouse_event(events[1].mi.dwFlags, - events[1].mi.dx, - events[1].mi.dy, - events[1].mi.mouseData, - events[1].mi.dwExtraInfo); - } + // get current mouse position + POINT pos; + GetCursorPos(&pos); + + // move relative to mouse position + mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); // restore mouse speed & acceleration if (accelChanged) { From 6301af9d504e7605922048d15b4b5d5b6c765b82 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 Feb 2003 12:44:37 +0000 Subject: [PATCH 452/807] Changed version to 1.0.3. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index bf1fef89..e8c2dc5f 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=0 -RELEASE_VERSION=2 +RELEASE_VERSION=3 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index e2d3077e..958a894b 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.2" +# define VERSION "1.0.3" #endif // important strings From dce445a83a22d60582e00eae90b51b370c828139 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 Feb 2003 16:20:49 +0000 Subject: [PATCH 453/807] Updates for version 1.0.3. --- ChangeLog | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ NEWS | 12 ++++++ 2 files changed, 124 insertions(+) diff --git a/ChangeLog b/ChangeLog index c318605a..463d56e8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,115 @@ +2003/02/17 12:44:37 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.3. + +---------- +2003/02/16 19:55:54 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Changed win32 client side cursor warping to be all relative motion +when not on the primary monitor. This should eliminate the flicker +between virtual display 0,0 and the correct position. While this +allows the user to confuse synergy by using the client's mouse, +synergy recovers quickly and easily from any confusion. + +---------- +2003/02/16 19:53:56 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Added hack to heuristically detect bogus mouse motion caused by +a race condition where the synergy server updates the mouse +position but the synergy hook later receives a mouse update from +before the position change (i.e. out of order). + +---------- +2003/02/16 19:51:46 crs +lib/platform/CSynergyHook.cpp + +Commented out an unnecessary hook and added a compile time +switch to disable grabbing of keyboard on win32 to facilitate +debugging. + +---------- +2003/02/16 19:50:36 crs +lib/platform/CMSWindowsScreen.cpp + +Changed heap to stack allocation in an oft-called function for +data that's never used outside the function. + +---------- +2003/02/16 19:49:44 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/arch/CArchMultithreadWindows.cpp +lib/base/CUnicode.cpp + +Fixed memory leaks. + +---------- +2003/02/12 20:59:25 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +Fixed incorrect mouse button swapping on client screens. + +---------- +2003/02/12 20:59:08 crs +lib/arch/CArchDaemonWindows.cpp + +Fixed error in debug build on win32. + +---------- +2003/02/12 19:50:22 crs +lib/arch/vsnprintf.cpp + +Added a simple implementation of vsnprintf for unix platforms +without it using /dev/null, vfprintf(), and vsprintf(). + +---------- +2003/02/12 19:38:39 crs +lib/arch/CArchMiscWindows.h +lib/arch/XArch.h +lib/base/CLog.h +lib/base/CString.h +lib/base/ILogOutputter.h +lib/mt/CCondVar.h +lib/mt/CLock.h +lib/mt/CThread.h +lib/mt/CTimerThread.h + +Made sure every file includes common.h directly or indirectly. +Also made sure common.h is included before any system headers. + +---------- +2003/02/01 18:10:43 crs +FAQ +INSTALL +README +TODO + +Added info about using SSH for authentication and encryption. + +---------- +2003/01/29 22:16:40 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +To support keymaps with only upper (or lower) case keysyms we now +use Xlib to convert an unmatched keysym to upper and lower case and +use whichever, if any, is not the same as the original keysym. +This supports case conversion in any language that Xlib supports +it in. + +---------- +2003/01/29 19:32:25 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +Applied patch from grmcdorman at users dot sourceforge dot net to +support keymaps that have only uppercase letters, which is the case +by default on the Sun X server (for US keyboards anyway). + +---------- 2003/01/25 13:39:26 crs NEWS configure.in diff --git a/NEWS b/NEWS index b13335b5..613c4c18 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,18 @@ Synergy News ============ +* Feb-18-2003 - Synergy 1.0.3 released + + Made following changes: + * Support for X11 keymaps with only uppercase letters + * Fixed memory leaks + * Added documentation on using synergy with SSH + * Fixed unnecessary left-handed mouse button swapping + * Fixed debug build error on win32 + * Reduced frequency of large cursor jumps when leaving win32 server + * Changed cursor motion on win32 multimon to relative moves only + + * Jan-25-2003 - Synergy 1.0.2 released Made following changes: From 5f164375d4e18e225834d6c5907570b0743ad9a4 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Feb 2003 15:03:31 +0000 Subject: [PATCH 454/807] Changes to support remapping modifier keys on clients. --- lib/client/CServerProxy.cpp | 171 ++++++++++++++++++++++++++++++++++-- lib/client/CServerProxy.h | 7 ++ lib/server/CConfig.cpp | 80 ++++++++++++++++- lib/server/CConfig.h | 1 + lib/synergy/KeyTypes.h | 21 ++++- lib/synergy/OptionTypes.h | 11 ++- 6 files changed, 278 insertions(+), 13 deletions(-) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 22db4143..5b3bdf81 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -39,6 +39,10 @@ CServerProxy::CServerProxy(IClient* client, assert(m_client != NULL); assert(m_input != NULL); assert(m_output != NULL); + + // initialize modifier translation table + for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) + m_modifierTranslationTable[id] = id; } CServerProxy::~CServerProxy() @@ -313,6 +317,110 @@ CServerProxy::sendInfo(const CClientInfo& info) info.m_mx, info.m_my); } +KeyID +CServerProxy::translateKey(KeyID id) const +{ + static const KeyID s_translationTable[kKeyModifierIDLast][2] = { + { kKeyNone, kKeyNone }, + { kKeyShift_L, kKeyShift_R }, + { kKeyControl_L, kKeyControl_R }, + { kKeyAlt_L, kKeyAlt_R }, + { kKeyMeta_L, kKeyMeta_R }, + { kKeySuper_L, kKeySuper_R } + }; + + KeyModifierID id2 = kKeyModifierIDNull; + UInt32 side = 0; + switch (id) { + case kKeyShift_L: + id2 = kKeyModifierIDShift; + side = 0; + break; + + case kKeyShift_R: + id2 = kKeyModifierIDShift; + side = 1; + break; + + case kKeyControl_L: + id2 = kKeyModifierIDControl; + side = 0; + break; + + case kKeyControl_R: + id2 = kKeyModifierIDControl; + side = 1; + break; + + case kKeyAlt_L: + id2 = kKeyModifierIDAlt; + side = 0; + break; + + case kKeyAlt_R: + id2 = kKeyModifierIDAlt; + side = 1; + break; + + case kKeyMeta_L: + id2 = kKeyModifierIDMeta; + side = 0; + break; + + case kKeyMeta_R: + id2 = kKeyModifierIDMeta; + side = 1; + break; + + case kKeySuper_L: + id2 = kKeyModifierIDSuper; + side = 0; + break; + + case kKeySuper_R: + id2 = kKeyModifierIDSuper; + side = 1; + break; + } + + if (id2 != kKeyModifierIDNull) { + return s_translationTable[m_modifierTranslationTable[id2]][side]; + } + else { + return id; + } +} + +KeyModifierMask +CServerProxy::translateModifierMask(KeyModifierMask mask) const +{ + static const KeyModifierMask s_masks[kKeyModifierIDLast] = { + 0x0000, + KeyModifierShift, + KeyModifierControl, + KeyModifierAlt, + KeyModifierMeta, + KeyModifierSuper + }; + + KeyModifierMask newMask = 0; + if ((mask & KeyModifierShift) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDShift]]; + } + if ((mask & KeyModifierControl) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDControl]]; + } + if ((mask & KeyModifierAlt) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDAlt]]; + } + if ((mask & KeyModifierMeta) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDMeta]]; + } + if ((mask & KeyModifierSuper) != 0) { + newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]]; + } +} + void CServerProxy::enter() { @@ -397,9 +505,16 @@ CServerProxy::keyDown() CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, &id, &mask); LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask)); - // forward - getClient()->keyDown(static_cast(id), + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + + // forward + getClient()->keyDown(id2, mask2); } void @@ -414,10 +529,16 @@ CServerProxy::keyRepeat() kMsgDKeyRepeat + 4, &id, &mask, &count); LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count)); + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( + static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + // forward - getClient()->keyRepeat(static_cast(id), - static_cast(mask), - count); + getClient()->keyRepeat(id2, mask2, count); } void @@ -431,9 +552,16 @@ CServerProxy::keyUp() CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask); LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask)); - // forward - getClient()->keyUp(static_cast(id), + // translate + KeyID id2 = translateKey(static_cast(id)); + KeyModifierMask mask2 = translateModifierMask( static_cast(mask)); + if (id2 != static_cast(id) || + mask2 != static_cast(mask)) + LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + + // forward + getClient()->keyUp(id2, mask2); } void @@ -534,6 +662,10 @@ CServerProxy::resetOptions() // forward getClient()->resetOptions(); + + // reset modifier translation table + for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) + m_modifierTranslationTable[id] = id; } void @@ -546,6 +678,31 @@ CServerProxy::setOptions() // forward getClient()->setOptions(options); + + // update modifier table + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + KeyModifierID id = kKeyModifierIDNull; + if (options[i] == kOptionModifierMapForShift) { + id = kKeyModifierIDShift; + } + else if (options[i] == kOptionModifierMapForControl) { + id = kKeyModifierIDControl; + } + else if (options[i] == kOptionModifierMapForAlt) { + id = kKeyModifierIDAlt; + } + else if (options[i] == kOptionModifierMapForMeta) { + id = kKeyModifierIDMeta; + } + else if (options[i] == kOptionModifierMapForSuper) { + id = kKeyModifierIDSuper; + } + if (id != kKeyModifierIDNull) { + m_modifierTranslationTable[id] = + static_cast(options[i + 1]); + LOG((CLOG_DEBUG1 "modifier %d mapped to %d", id, m_modifierTranslationTable[id])); + } + } } void diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index a54ccdff..7d7eb2dd 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -16,6 +16,7 @@ #define CSERVERPROXY_H #include "IScreenReceiver.h" +#include "KeyTypes.h" #include "CMutex.h" class IClient; @@ -92,6 +93,10 @@ private: void sendInfo(const CClientInfo&); + // modifier key translation + KeyID translateKey(KeyID) const; + KeyModifierMask translateModifierMask(KeyModifierMask) const; + // message handlers void enter(); void leave(); @@ -123,6 +128,8 @@ private: SInt32 m_xMouse, m_yMouse; bool m_ignoreMouse; + + KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; }; #endif diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 9ed619db..5728bd05 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -13,7 +13,7 @@ */ #include "CConfig.h" -#include "ProtocolTypes.h" +#include "KeyTypes.h" #include "XSocket.h" #include "stdistream.h" #include "stdostream.h" @@ -514,6 +514,24 @@ CConfig::parseBoolean(const CString& arg) throw XConfigRead("invalid argument"); } +OptionValue +CConfig::parseModifierKey(const CString& arg) +{ + if (CStringUtil::CaselessCmp::equal(arg, "shift")) + return static_cast(kKeyModifierIDShift); + if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) + return static_cast(kKeyModifierIDControl); + if (CStringUtil::CaselessCmp::equal(arg, "alt")) + return static_cast(kKeyModifierIDAlt); + if (CStringUtil::CaselessCmp::equal(arg, "meta")) + return static_cast(kKeyModifierIDMeta); + if (CStringUtil::CaselessCmp::equal(arg, "super")) + return static_cast(kKeyModifierIDSuper); + if (CStringUtil::CaselessCmp::equal(arg, "none")) + return static_cast(kKeyModifierIDNull); + throw XConfigRead("invalid argument"); +} + const char* CConfig::getOptionName(OptionID id) { @@ -523,6 +541,21 @@ CConfig::getOptionName(OptionID id) if (id == kOptionHalfDuplexNumLock) { return "halfDuplexNumLock"; } + if (id == kOptionModifierMapForShift) { + return "shift"; + } + if (id == kOptionModifierMapForControl) { + return "ctrl"; + } + if (id == kOptionModifierMapForAlt) { + return "alt"; + } + if (id == kOptionModifierMapForMeta) { + return "meta"; + } + if (id == kOptionModifierMapForSuper) { + return "super"; + } return NULL; } @@ -533,6 +566,31 @@ CConfig::getOptionValue(OptionID id, OptionValue value) id == kOptionHalfDuplexNumLock) { return (value != 0) ? "true" : "false"; } + if (id == kOptionModifierMapForShift || + id == kOptionModifierMapForControl || + id == kOptionModifierMapForAlt || + id == kOptionModifierMapForMeta || + id == kOptionModifierMapForSuper) { + switch (value) { + case kKeyModifierIDShift: + return "shift"; + + case kKeyModifierIDControl: + return "ctrl"; + + case kKeyModifierIDAlt: + return "alt"; + + case kKeyModifierIDMeta: + return "meta"; + + case kKeyModifierIDSuper: + return "super"; + + default: + return "none"; + } + } return ""; } @@ -700,6 +758,26 @@ CConfig::readSectionScreens(std::istream& s) addOption(screen, kOptionHalfDuplexNumLock, parseBoolean(value)); } + else if (name == "shift") { + addOption(screen, kOptionModifierMapForShift, + parseModifierKey(value)); + } + else if (name == "ctrl") { + addOption(screen, kOptionModifierMapForControl, + parseModifierKey(value)); + } + else if (name == "alt") { + addOption(screen, kOptionModifierMapForAlt, + parseModifierKey(value)); + } + else if (name == "meta") { + addOption(screen, kOptionModifierMapForMeta, + parseModifierKey(value)); + } + else if (name == "super") { + addOption(screen, kOptionModifierMapForSuper, + parseModifierKey(value)); + } else { // unknown argument throw XConfigRead("unknown argument"); diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index a60b3abc..599e436e 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -293,6 +293,7 @@ public: private: static bool readLine(std::istream&, CString&); static bool parseBoolean(const CString&); + static OptionValue parseModifierKey(const CString&); static const char* getOptionName(OptionID); static const char* getOptionValue(OptionID, OptionValue); void readSection(std::istream&); diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 49b10c63..21028824 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -25,13 +25,19 @@ keys, function keys, modifier keys, etc). */ typedef UInt32 KeyID; -//! Modifier key ID +//! Modifier key mask /*! Type to hold a bitmask of key modifiers (e.g. shift keys). */ typedef UInt32 KeyModifierMask; -//! @name Modifier key identifiers +//! Modifier key ID +/*! +Type to hold the id of a key modifier (e.g. a shift key). +*/ +typedef UInt32 KeyModifierID; + +//! @name Modifier key masks //@{ static const KeyModifierMask KeyModifierShift = 0x0001; static const KeyModifierMask KeyModifierControl = 0x0002; @@ -44,6 +50,17 @@ static const KeyModifierMask KeyModifierNumLock = 0x2000; static const KeyModifierMask KeyModifierScrollLock = 0x4000; //@} +//! @name Modifier key identifiers +//@{ +static const KeyModifierID kKeyModifierIDNull = 0; +static const KeyModifierID kKeyModifierIDShift = 1; +static const KeyModifierID kKeyModifierIDControl = 2; +static const KeyModifierID kKeyModifierIDAlt = 3; +static const KeyModifierID kKeyModifierIDMeta = 4; +static const KeyModifierID kKeyModifierIDSuper = 5; +static const KeyModifierID kKeyModifierIDLast = 6; +//@} + //! @name Key identifiers //@{ // all identifiers except kKeyNone are equal to the corresponding diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index 3310ace0..31c72185 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -42,9 +42,14 @@ typedef std::vector COptionsList; //! @name Option identifiers //@{ -static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); -static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); -static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); +static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); +static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); +static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); +static const OptionID kOptionModifierMapForShift = OPTION_CODE("MMFS"); +static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC"); +static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); +static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); +static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); //@} #undef OPTION_CODE From 8685afd9f67595667b30136a540b0f8522aa2578 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Feb 2003 15:04:09 +0000 Subject: [PATCH 455/807] Changed version to 1.0.5. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index e8c2dc5f..5bfdd16d 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=0 -RELEASE_VERSION=3 +RELEASE_VERSION=5 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 958a894b..f1271e75 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.3" +# define VERSION "1.0.5" #endif // important strings From 366537dc22e348f529cbd3a064925398cebb875b Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Feb 2003 16:20:23 +0000 Subject: [PATCH 456/807] Added support for heartbeat global option. --- lib/client/CServerProxy.cpp | 31 +++++++++-- lib/client/CServerProxy.h | 1 + lib/server/CClientProxy.cpp | 6 +++ lib/server/CClientProxy.h | 10 ++++ lib/server/CClientProxy1_0.cpp | 40 ++++++++++---- lib/server/CClientProxy1_0.h | 4 +- lib/server/CConfig.cpp | 99 +++++++++++++++++++++++++--------- lib/server/CConfig.h | 7 +-- lib/server/CServer.cpp | 50 +++++++++++++---- lib/synergy/OptionTypes.h | 1 + lib/synergy/ProtocolTypes.h | 4 +- 11 files changed, 196 insertions(+), 57 deletions(-) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 5b3bdf81..ce0db115 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -34,7 +34,8 @@ CServerProxy::CServerProxy(IClient* client, m_client(client), m_input(adoptedInput), m_output(adoptedOutput), - m_seqNum(0) + m_seqNum(0), + m_heartRate(kHeartRate) { assert(m_client != NULL); assert(m_input != NULL); @@ -76,7 +77,7 @@ CServerProxy::mainLoop() // wait for a message LOG((CLOG_DEBUG2 "waiting for message")); UInt8 code[4]; - UInt32 n = getInputStream()->read(code, 4, kHeartRate); + UInt32 n = getInputStream()->read(code, 4, m_heartRate); // check if server hungup if (n == 0) { @@ -86,7 +87,7 @@ CServerProxy::mainLoop() // check for time out if (n == (UInt32)-1 || - (kHeartRate >= 0.0 && heartbeat.getTime() > kHeartRate)) { + (m_heartRate >= 0.0 && heartbeat.getTime() > m_heartRate)) { // send heartbeat CLock lock(&m_mutex); CProtocolUtil::writef(getOutputStream(), kMsgCNoop); @@ -663,9 +664,20 @@ CServerProxy::resetOptions() // forward getClient()->resetOptions(); + CLock lock(&m_mutex); + + // reset heart rate + m_heartRate = kHeartRate; + // reset modifier translation table - for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) + for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) { m_modifierTranslationTable[id] = id; + } + + // send heartbeat if necessary + if (m_heartRate >= 0.0) { + CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + } } void @@ -679,6 +691,8 @@ CServerProxy::setOptions() // forward getClient()->setOptions(options); + CLock lock(&m_mutex); + // update modifier table for (UInt32 i = 0, n = options.size(); i < n; i += 2) { KeyModifierID id = kKeyModifierIDNull; @@ -697,6 +711,15 @@ CServerProxy::setOptions() else if (options[i] == kOptionModifierMapForSuper) { id = kKeyModifierIDSuper; } + else if (options[i] == kOptionHeartbeat) { + // update heart rate + m_heartRate = 1.0e-3 * static_cast(options[i + 1]); + + // send heartbeat if necessary + if (m_heartRate >= 0.0) { + CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + } + } if (id != kKeyModifierIDNull) { m_modifierTranslationTable[id] = static_cast(options[i + 1]); diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index 7d7eb2dd..cbb58f1b 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -130,6 +130,7 @@ private: bool m_ignoreMouse; KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; + double m_heartRate; }; #endif diff --git a/lib/server/CClientProxy.cpp b/lib/server/CClientProxy.cpp index 938fe89d..55e940f7 100644 --- a/lib/server/CClientProxy.cpp +++ b/lib/server/CClientProxy.cpp @@ -59,3 +59,9 @@ CClientProxy::getName() const { return m_name; } + +const CMutex* +CClientProxy::getMutex() const +{ + return &m_mutex; +} diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index 40447a6b..43585001 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -16,6 +16,7 @@ #define CCLIENTPROXY_H #include "IClient.h" +#include "CMutex.h" #include "CString.h" class IInputStream; @@ -84,7 +85,16 @@ public: virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; +protected: + //! Get mutex + /*! + Returns the mutex for this object. Subclasses should use this + mutex to protect their data. + */ + const CMutex* getMutex() const; + private: + CMutex m_mutex; IServer* m_server; CString m_name; IInputStream* m_input; diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 6ce07a43..be0effca 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -31,7 +31,9 @@ CClientProxy1_0::CClientProxy1_0(IServer* server, const CString& name, IInputStream* input, IOutputStream* output) : - CClientProxy(server, name, input, output) + CClientProxy(server, name, input, output), + m_heartRate(kHeartRate), + m_heartDeath(kHeartRate * kHeartBeatsUntilDeath) { for (UInt32 i = 0; i < kClipboardEnd; ++i) { m_clipboardDirty[i] = true; @@ -81,7 +83,7 @@ CClientProxy1_0::mainLoop() // wait for a message UInt8 code[4]; - UInt32 n = getInputStream()->read(code, 4, kHeartRate); + UInt32 n = getInputStream()->read(code, 4, m_heartRate); CThread::testCancel(); // check if client hungup @@ -92,7 +94,7 @@ CClientProxy1_0::mainLoop() // check if client has stopped sending heartbeats if (n == (UInt32)-1) { - if (kHeartDeath >= 0.0 && heartTimer.getTime() > kHeartDeath) { + if (m_heartDeath >= 0.0 && heartTimer.getTime() > m_heartDeath) { LOG((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); return; } @@ -117,6 +119,7 @@ CClientProxy1_0::mainLoop() } else if (memcmp(code, kMsgCNoop, 4) == 0) { // discard no-ops + LOG((CLOG_DEBUG2 "no-op from", getName().c_str())); continue; } else if (memcmp(code, kMsgCClipboard, 4) == 0) { @@ -168,7 +171,7 @@ void CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) { // ignore if this clipboard is already clean - CLock lock(&m_mutex); + CLock lock(getMutex()); if (m_clipboardDirty[id]) { // this clipboard is now clean m_clipboardDirty[id] = false; @@ -185,14 +188,14 @@ CClientProxy1_0::grabClipboard(ClipboardID id) CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); // this clipboard is now dirty - CLock lock(&m_mutex); + CLock lock(getMutex()); m_clipboardDirty[id] = true; } void CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty) { - CLock lock(&m_mutex); + CLock lock(getMutex()); m_clipboardDirty[id] = dirty; } @@ -257,6 +260,11 @@ CClientProxy1_0::resetOptions() { LOG((CLOG_DEBUG1 "send reset options to \"%s\"", getName().c_str())); CProtocolUtil::writef(getOutputStream(), kMsgCResetOptions); + + // reset heart rate and death + CLock lock(getMutex()); + m_heartRate = kHeartRate; + m_heartDeath = kHeartRate * kHeartBeatsUntilDeath; } void @@ -264,19 +272,31 @@ CClientProxy1_0::setOptions(const COptionsList& options) { LOG((CLOG_DEBUG1 "send set options to \"%s\" size=%d", getName().c_str(), options.size())); CProtocolUtil::writef(getOutputStream(), kMsgDSetOptions, &options); + + // check options + CLock lock(getMutex()); + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionHeartbeat) { + m_heartRate = 1.0e-3 * static_cast(options[i + 1]); + if (m_heartRate <= 0.0) { + m_heartRate = -1.0; + } + m_heartDeath = m_heartRate * kHeartBeatsUntilDeath; + } + } } SInt32 CClientProxy1_0::getJumpZoneSize() const { - CLock lock(&m_mutex); + CLock lock(getMutex()); return m_info.m_zoneSize; } void CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { - CLock lock(&m_mutex); + CLock lock(getMutex()); x = m_info.m_x; y = m_info.m_y; w = m_info.m_w; @@ -292,7 +312,7 @@ CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const void CClientProxy1_0::getCursorCenter(SInt32& x, SInt32& y) const { - CLock lock(&m_mutex); + CLock lock(getMutex()); x = m_info.m_mx; y = m_info.m_my; } @@ -301,7 +321,7 @@ void CClientProxy1_0::recvInfo(bool notify) { { - CLock lock(&m_mutex); + CLock lock(getMutex()); // parse the message SInt16 x, y, w, h, zoneSize, mx, my; diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index ac94f0f5..6725e5be 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -17,7 +17,6 @@ #include "CClientProxy.h" #include "ProtocolTypes.h" -#include "CMutex.h" //! Proxy for client implementing protocol version 1.0 class CClientProxy1_0 : public CClientProxy { @@ -60,9 +59,10 @@ private: void recvGrabClipboard(); private: - CMutex m_mutex; CClientInfo m_info; bool m_clipboardDirty[kClipboardEnd]; + double m_heartRate; + double m_heartDeath; }; #endif diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 5728bd05..3359d9f1 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -17,6 +17,7 @@ #include "XSocket.h" #include "stdistream.h" #include "stdostream.h" +#include // // CConfig @@ -504,31 +505,57 @@ CConfig::readLine(std::istream& s, CString& line) return false; } -bool +OptionValue CConfig::parseBoolean(const CString& arg) { - if (CStringUtil::CaselessCmp::equal(arg, "true")) - return true; - if (CStringUtil::CaselessCmp::equal(arg, "false")) - return false; + if (CStringUtil::CaselessCmp::equal(arg, "true")) { + return static_cast(true); + } + if (CStringUtil::CaselessCmp::equal(arg, "false")) { + return static_cast(false); + } throw XConfigRead("invalid argument"); } +OptionValue +CConfig::parseInt(const CString& arg) +{ + const char* s = arg.c_str(); + char* end; + long tmp = strtol(s, &end, 10); + if (*end != '\0') { + // invalid characters + throw XConfigRead("invalid argument"); + } + OptionValue value = static_cast(tmp); + if (value != tmp) { + // out of range + throw XConfigRead("argument out of range"); + } + return value; +} + OptionValue CConfig::parseModifierKey(const CString& arg) { - if (CStringUtil::CaselessCmp::equal(arg, "shift")) + if (CStringUtil::CaselessCmp::equal(arg, "shift")) { return static_cast(kKeyModifierIDShift); - if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) + } + if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) { return static_cast(kKeyModifierIDControl); - if (CStringUtil::CaselessCmp::equal(arg, "alt")) + } + if (CStringUtil::CaselessCmp::equal(arg, "alt")) { return static_cast(kKeyModifierIDAlt); - if (CStringUtil::CaselessCmp::equal(arg, "meta")) + } + if (CStringUtil::CaselessCmp::equal(arg, "meta")) { return static_cast(kKeyModifierIDMeta); - if (CStringUtil::CaselessCmp::equal(arg, "super")) + } + if (CStringUtil::CaselessCmp::equal(arg, "super")) { return static_cast(kKeyModifierIDSuper); - if (CStringUtil::CaselessCmp::equal(arg, "none")) + } + if (CStringUtil::CaselessCmp::equal(arg, "none")) { return static_cast(kKeyModifierIDNull); + } throw XConfigRead("invalid argument"); } @@ -556,10 +583,13 @@ CConfig::getOptionName(OptionID id) if (id == kOptionModifierMapForSuper) { return "super"; } + if (id == kOptionHeartbeat) { + return "heartbeat"; + } return NULL; } -const char* +CString CConfig::getOptionValue(OptionID id, OptionValue value) { if (id == kOptionHalfDuplexCapsLock || @@ -591,6 +621,9 @@ CConfig::getOptionValue(OptionID id, OptionValue value) return "none"; } } + if (id == kOptionHeartbeat) { + return CStringUtil::print("%d", value); + } return ""; } @@ -599,7 +632,7 @@ void CConfig::readSection(std::istream& s) { static const char s_section[] = "section:"; - static const char s_network[] = "network"; + static const char s_options[] = "options"; static const char s_screens[] = "screens"; static const char s_links[] = "links"; static const char s_aliases[] = "aliases"; @@ -627,8 +660,8 @@ CConfig::readSection(std::istream& s) } // read section - if (name == s_network) { - readSectionNetwork(s); + if (name == s_options) { + readSectionOptions(s); } else if (name == s_screens) { readSectionScreens(s); @@ -645,7 +678,7 @@ CConfig::readSection(std::istream& s) } void -CConfig::readSectionNetwork(std::istream& s) +CConfig::readSectionOptions(std::istream& s) { CString line; CString name; @@ -693,6 +726,9 @@ CConfig::readSectionNetwork(std::istream& s) throw XConfigRead("invalid http argument"); } } + else if (name == "heartbeat") { + addOption("", kOptionHeartbeat, parseInt(value)); + } else { throw XConfigRead("unknown argument"); } @@ -931,15 +967,28 @@ operator>>(std::istream& s, CConfig& config) std::ostream& operator<<(std::ostream& s, const CConfig& config) { - // network section - s << "section: network" << std::endl; + // options section + s << "section: options" << std::endl; + const CConfig::CScreenOptions* options = config.getOptions(""); + if (options != NULL && options->size() > 0) { + for (CConfig::CScreenOptions::const_iterator + option = options->begin(); + option != options->end(); ++option) { + const char* name = CConfig::getOptionName(option->first); + CString value = CConfig::getOptionValue(option->first, + option->second); + if (name != NULL && !value.empty()) { + s << "\t" << name << " = " << value << std::endl; + } + } + } if (config.m_synergyAddress.isValid()) { - s << "\taddress=" << config.m_synergyAddress.getHostname().c_str() << - std::endl; + s << "\taddress = " << + config.m_synergyAddress.getHostname().c_str() << std::endl; } if (config.m_httpAddress.isValid()) { - s << "\thttp=" << config.m_httpAddress.getHostname().c_str() << - std::endl; + s << "\thttp = " << + config.m_httpAddress.getHostname().c_str() << std::endl; } s << "end" << std::endl; @@ -953,10 +1002,10 @@ operator<<(std::ostream& s, const CConfig& config) for (CConfig::CScreenOptions::const_iterator option = options->begin(); option != options->end(); ++option) { - const char* name = CConfig::getOptionName(option->first); - const char* value = CConfig::getOptionValue(option->first, + const char* name = CConfig::getOptionName(option->first); + CString value = CConfig::getOptionValue(option->first, option->second); - if (name != NULL && value != NULL) { + if (name != NULL && !value.empty()) { s << "\t\t" << name << " = " << value << std::endl; } } diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index 599e436e..a2fd31b3 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -292,12 +292,13 @@ public: private: static bool readLine(std::istream&, CString&); - static bool parseBoolean(const CString&); + static OptionValue parseBoolean(const CString&); + static OptionValue parseInt(const CString&); static OptionValue parseModifierKey(const CString&); static const char* getOptionName(OptionID); - static const char* getOptionValue(OptionID, OptionValue); + static CString getOptionValue(OptionID, OptionValue); void readSection(std::istream&); - void readSectionNetwork(std::istream&); + void readSectionOptions(std::istream&); void readSectionScreens(std::istream&); void readSectionLinks(std::istream&); void readSectionAliases(std::istream&); diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 9ae41237..cb636300 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -217,12 +217,29 @@ CServer::setConfig(const CConfig& config) CLock lock(&m_mutex); m_config = config; + // process global options + const CConfig::CScreenOptions* options = m_config.getOptions(""); + if (options != NULL && options->size() > 0) { +/* + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + const OptionID id = index->first; + const OptionValue value = index->second; + } +*/ + } + // tell primary screen about reconfiguration if (m_primaryClient != NULL) { m_primaryClient->reconfigure(getActivePrimarySides()); } - // FIXME -- tell all (connected) clients about current options + // tell all (connected) clients about current options + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + sendOptions(client); + } return true; } @@ -1622,20 +1639,31 @@ CServer::sendOptions(IClient* client) const { // note -- must be locked on entry - // look up options for client. we're done if there aren't any. + COptionsList optionsList; + + // look up options for client const CConfig::CScreenOptions* options = m_config.getOptions(client->getName()); - if (options == NULL || options->size() == 0) { - return; + if (options != NULL && options->size() > 0) { + // convert options to a more convenient form for sending + optionsList.reserve(2 * options->size()); + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + optionsList.push_back(index->first); + optionsList.push_back(static_cast(index->second)); + } } - // convert options to a more convenient form for sending - COptionsList optionsList; - optionsList.reserve(2 * options->size()); - for (CConfig::CScreenOptions::const_iterator index = options->begin(); - index != options->end(); ++index) { - optionsList.push_back(index->first); - optionsList.push_back(static_cast(index->second)); + // look up global options + options = m_config.getOptions(""); + if (options != NULL && options->size() > 0) { + // convert options to a more convenient form for sending + optionsList.reserve(optionsList.size() + 2 * options->size()); + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + optionsList.push_back(index->first); + optionsList.push_back(static_cast(index->second)); + } } // send the options diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index 31c72185..5a9ebf10 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -50,6 +50,7 @@ static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC"); static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); +static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); //@} #undef OPTION_CODE diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index 52201320..b37d1265 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -31,8 +31,8 @@ static const UInt32 kMaxHelloLength = 1024; // heartbeat. static const double kHeartRate = -1.0; -// time without a heartbeat that constitutes death -static const double kHeartDeath = 3.0 * kHeartRate; +// number of skipped heartbeats that constitutes death +static const double kHeartBeatsUntilDeath = 3.0; // direction constants enum EDirection { From aef50800e3ac0325aaaede29a43d71a6cdc69134 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Feb 2003 16:41:03 +0000 Subject: [PATCH 457/807] Added global options to CConfig (needed for heartbeat option). --- lib/server/CConfig.cpp | 74 ++++++++++++++++++++++++++++++++---------- lib/server/CConfig.h | 1 + 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 3359d9f1..b125206f 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -244,42 +244,69 @@ CConfig::setHTTPAddress(const CNetworkAddress& addr) bool CConfig::addOption(const CString& name, OptionID option, OptionValue value) { - // find cell - CCellMap::iterator index = m_map.find(name); - if (index == m_map.end()) { + // find options + CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + if (options == NULL) { return false; } // add option - index->second.m_options.insert(std::make_pair(option, value)); + options->insert(std::make_pair(option, value)); return true; } bool CConfig::removeOption(const CString& name, OptionID option) { - // find cell - CCellMap::iterator index = m_map.find(name); - if (index == m_map.end()) { + // find options + CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + if (options == NULL) { return false; } // remove option - index->second.m_options.erase(option); + options->erase(option); return true; } bool CConfig::removeOptions(const CString& name) { - // find cell - CCellMap::iterator index = m_map.find(name); - if (index == m_map.end()) { + // find options + CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } + } + if (options == NULL) { return false; } - // remove option - index->second.m_options.clear(); + // remove options + options->clear(); return true; } @@ -405,14 +432,20 @@ CConfig::getHTTPAddress() const const CConfig::CScreenOptions* CConfig::getOptions(const CString& name) const { - // find cell - CCellMap::const_iterator index = m_map.find(name); - if (index == m_map.end()) { - return NULL; + // find options + const CScreenOptions* options = NULL; + if (name.empty()) { + options = &m_globalOptions; + } + else { + CCellMap::const_iterator index = m_map.find(name); + if (index != m_map.end()) { + options = &index->second.m_options; + } } // return options - return &index->second.m_options; + return options; } bool @@ -433,6 +466,11 @@ CConfig::operator==(const CConfig& x) const return false; } + // compare global options + if (m_globalOptions != x.m_globalOptions) { + return false; + } + for (CCellMap::const_iterator index1 = m_map.begin(), index2 = x.m_map.begin(); index1 != m_map.end(); ++index1, ++index2) { diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index a2fd31b3..ea0fcd5e 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -308,6 +308,7 @@ private: CNameMap m_nameToCanonicalName; CNetworkAddress m_synergyAddress; CNetworkAddress m_httpAddress; + CScreenOptions m_globalOptions; }; //! Configuration stream read exception From 7bbd33d7870651575a5a46a8f669648c4dd4914a Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Feb 2003 21:53:25 +0000 Subject: [PATCH 458/807] Added support on X11 for a global option to delay switching screens when the mouse reaches a jump zone. --- lib/platform/CXWindowsPrimaryScreen.cpp | 12 ++ lib/platform/CXWindowsPrimaryScreen.h | 2 + lib/platform/CXWindowsScreen.cpp | 87 ++++++--- lib/platform/CXWindowsScreen.h | 12 +- lib/platform/CXWindowsSecondaryScreen.cpp | 6 + lib/platform/CXWindowsSecondaryScreen.h | 1 + lib/server/CConfig.cpp | 9 +- lib/server/CPrimaryClient.cpp | 6 + lib/server/CPrimaryClient.h | 7 + lib/server/CServer.cpp | 213 ++++++++++++++++------ lib/server/CServer.h | 10 + lib/synergy/CPrimaryScreen.h | 7 + lib/synergy/IPrimaryScreenReceiver.h | 6 + lib/synergy/IScreenEventHandler.h | 6 + lib/synergy/OptionTypes.h | 1 + 15 files changed, 306 insertions(+), 79 deletions(-) diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp index 763cf4ae..67611c39 100644 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -102,6 +102,12 @@ CXWindowsPrimaryScreen::setOptions(const COptionsList& options) } } +UInt32 +CXWindowsPrimaryScreen::addOneShotTimer(double timeout) +{ + return m_screen->addOneShotTimer(timeout); +} + KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const { @@ -387,6 +393,12 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) return false; } +void +CXWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id) +{ + m_receiver->onOneShotTimerExpired(id); +} + SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const { diff --git a/lib/platform/CXWindowsPrimaryScreen.h b/lib/platform/CXWindowsPrimaryScreen.h index c24464b8..5ac54356 100644 --- a/lib/platform/CXWindowsPrimaryScreen.h +++ b/lib/platform/CXWindowsPrimaryScreen.h @@ -40,6 +40,7 @@ public: virtual void warpCursor(SInt32 x, SInt32 y); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); + virtual UInt32 addOneShotTimer(double timeout); virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; virtual IScreen* getScreen() const; @@ -48,6 +49,7 @@ public: virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); virtual SInt32 getJumpZoneSize() const; protected: diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 40e8591d..cae5ff23 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -54,14 +54,13 @@ // CXWindowsScreen::CTimer // -CXWindowsScreen::CTimer::CTimer(IJob* job, double timeout) : +CXWindowsScreen::CTimer::CTimer(IJob* job, double startTime, double resetTime) : m_job(job), - m_timeout(timeout) + m_timeout(resetTime), + m_time(resetTime), + m_startTime(startTime) { - assert(m_job != NULL); assert(m_timeout > 0.0); - - reset(); } CXWindowsScreen::CTimer::~CTimer() @@ -72,19 +71,23 @@ CXWindowsScreen::CTimer::~CTimer() void CXWindowsScreen::CTimer::run() { - m_job->run(); + if (m_job != NULL) { + m_job->run(); + } } void CXWindowsScreen::CTimer::reset() { - m_time = m_timeout; + m_time = m_timeout; + m_startTime = 0.0; } CXWindowsScreen::CTimer::CTimer& CXWindowsScreen::CTimer::operator-=(double dt) { - m_time -= dt; + m_time -= dt - m_startTime; + m_startTime = 0.0; return *this; } @@ -118,7 +121,8 @@ CXWindowsScreen::CXWindowsScreen(IScreenReceiver* receiver, m_w(0), m_h(0), m_screensaver(NULL), m_screensaverNotify(false), - m_atomScreensaver(None) + m_atomScreensaver(None), + m_oneShotTimer(NULL) { assert(s_screen == NULL); assert(m_receiver != NULL); @@ -137,6 +141,7 @@ CXWindowsScreen::~CXWindowsScreen() assert(s_screen != NULL); assert(m_display == NULL); + delete m_oneShotTimer; s_screen = NULL; } @@ -145,7 +150,7 @@ CXWindowsScreen::addTimer(IJob* job, double timeout) { CLock lock(&m_timersMutex); removeTimerNoLock(job); - m_timers.push(CTimer(job, timeout)); + m_timers.push(CTimer(job, m_time.getTime(), timeout)); } void @@ -172,6 +177,15 @@ CXWindowsScreen::removeTimerNoLock(IJob* job) m_timers.swap(tmp); } +UInt32 +CXWindowsScreen::addOneShotTimer(double timeout) +{ + CLock lock(&m_timersMutex); + // FIXME -- support multiple one-shot timers + m_oneShotTimer = new CTimer(NULL, m_time.getTime(), timeout); + return 0; +} + void CXWindowsScreen::setWindow(Window window) { @@ -262,19 +276,27 @@ CXWindowsScreen::mainLoop() #endif while (!m_stop) { // compute timeout to next timer + double dtimeout; + { + CLock timersLock(&m_timersMutex); + dtimeout = (m_timers.empty() ? -1.0 : m_timers.top()); + if (m_oneShotTimer != NULL && + (dtimeout == -1.0 || *m_oneShotTimer < dtimeout)) { + dtimeout = *m_oneShotTimer; + } + } #if HAVE_POLL - int timeout = (m_timers.empty() ? -1 : - static_cast(1000.0 * m_timers.top())); + int timeout = static_cast(1000.0 * dtimeout); #else struct timeval timeout; struct timeval* timeoutPtr; - if (m_timers.empty()) { + if (dtimeout < 0.0) { timeoutPtr = NULL; } else { - timeout.tv_sec = static_cast(m_timers.top()); + timeout.tv_sec = static_cast(dtimeout); timeout.tv_usec = static_cast(1.0e+6 * - (m_timers.top() - timeout.tv_sec)); + (dtimeout - timeout.tv_sec)); timeoutPtr = &timeout; } @@ -697,6 +719,7 @@ CXWindowsScreen::createBlankCursor() bool CXWindowsScreen::processTimers() { + bool oneShot = false; std::vector jobs; { CLock lock(&m_timersMutex); @@ -705,10 +728,21 @@ CXWindowsScreen::processTimers() const double time = m_time.getTime(); // done if no timers have expired - if (m_timers.empty() || m_timers.top() > time) { + if ((m_oneShotTimer == NULL || *m_oneShotTimer > time) && + (m_timers.empty() || m_timers.top() > time)) { return false; } + // handle one shot timers + if (m_oneShotTimer != NULL) { + *m_oneShotTimer -= time; + if (*m_oneShotTimer <= 0.0) { + delete m_oneShotTimer; + m_oneShotTimer = NULL; + oneShot = true; + } + } + // subtract current time from all timers. note that this won't // change the order of elements in the priority queue (except // for floating point round off which we'll ignore). @@ -718,18 +752,27 @@ CXWindowsScreen::processTimers() } // process all timers at or below zero, saving the jobs - while (m_timers.top() <= 0.0) { - CTimer timer = m_timers.top(); - jobs.push_back(timer.getJob()); - timer.reset(); - m_timers.pop(); - m_timers.push(timer); + if (!m_timers.empty()) { + while (m_timers.top() <= 0.0) { + CTimer timer = m_timers.top(); + jobs.push_back(timer.getJob()); + timer.reset(); + m_timers.pop(); + m_timers.push(timer); + } } // reset the clock m_time.reset(); } + // now notify of the one shot timers + if (oneShot) { + m_mutex.unlock(); + m_eventHandler->onOneShotTimerExpired(0); + m_mutex.lock(); + } + // now run the jobs. note that if one of these jobs removes // a timer later in the jobs list and deletes that job pointer // then this will crash when it tries to run that job. diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index a6451763..83f10cfa 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -69,6 +69,14 @@ public: */ void removeTimer(IJob*); + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer (which will be passed to the receiver's + \c onTimerExpired()). + */ + UInt32 addOneShotTimer(double timeout); + //! Set window /*! Set the window (created by the subclass). This performs some @@ -216,7 +224,7 @@ private: // a timer priority queue element class CTimer { public: - CTimer(IJob* job, double timeout); + CTimer(IJob* job, double startTime, double resetTime); ~CTimer(); // manipulators @@ -242,6 +250,7 @@ private: IJob* m_job; double m_timeout; double m_time; + double m_startTime; }; private: @@ -278,6 +287,7 @@ private: CTimerPriorityQueue m_timers; CStopwatch m_time; CMutex m_timersMutex; + CTimer* m_oneShotTimer; // pointer to (singleton) screen. this is only needed by // ioErrorHandler(). diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 1e848887..31f1affc 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -263,6 +263,12 @@ CXWindowsSecondaryScreen::onEvent(CEvent* event) } } +void +CXWindowsSecondaryScreen::onOneShotTimerExpired(UInt32) +{ + // ignore +} + SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const { diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index ad2403c2..6711ee0f 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -51,6 +51,7 @@ public: virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); virtual SInt32 getJumpZoneSize() const; protected: diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index b125206f..09633313 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -624,6 +624,9 @@ CConfig::getOptionName(OptionID id) if (id == kOptionHeartbeat) { return "heartbeat"; } + if (id == kOptionScreenSwitchDelay) { + return "switchDelay"; + } return NULL; } @@ -659,7 +662,8 @@ CConfig::getOptionValue(OptionID id, OptionValue value) return "none"; } } - if (id == kOptionHeartbeat) { + if (id == kOptionHeartbeat || + id == kOptionScreenSwitchDelay) { return CStringUtil::print("%d", value); } @@ -767,6 +771,9 @@ CConfig::readSectionOptions(std::istream& s) else if (name == "heartbeat") { addOption("", kOptionHeartbeat, parseInt(value)); } + else if (name == "switchDelay") { + addOption("", kOptionScreenSwitchDelay, parseInt(value)); + } else { throw XConfigRead("unknown argument"); } diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index ca7d37d8..5cd66876 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -63,6 +63,12 @@ CPrimaryClient::reconfigure(UInt32 activeSides) m_screen->reconfigure(activeSides); } +UInt32 +CPrimaryClient::addOneShotTimer(double timeout) +{ + return m_screen->addOneShotTimer(timeout); +} + void CPrimaryClient::getClipboard(ClipboardID id, CString& data) const { diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 5cd0c259..d6c3f7c6 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -59,6 +59,13 @@ public: */ void reconfigure(UInt32 activeSides); + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer (which will be passed to \c onTimerExpired()). + */ + UInt32 addOneShotTimer(double timeout); + //@} //! @name accessors //@{ diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index cb636300..ce554f9b 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -59,7 +59,9 @@ CServer::CServer(const CString& serverName) : m_seqNum(0), m_activeSaver(NULL), m_httpServer(NULL), - m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests) + m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests), + m_switchWaitDelay(0.0), + m_switchWaitScreen(NULL) { // do nothing } @@ -220,13 +222,18 @@ CServer::setConfig(const CConfig& config) // process global options const CConfig::CScreenOptions* options = m_config.getOptions(""); if (options != NULL && options->size() > 0) { -/* for (CConfig::CScreenOptions::const_iterator index = options->begin(); index != options->end(); ++index) { const OptionID id = index->first; const OptionValue value = index->second; + if (id == kOptionScreenSwitchDelay) { + m_switchWaitDelay = 1.0e-3 * static_cast(value); + if (m_switchWaitDelay < 0.0) { + m_switchWaitDelay = 0.0; + } + clearSwitchWait(); + } } -*/ } // tell primary screen about reconfiguration @@ -496,6 +503,36 @@ CServer::onScreensaver(bool activated) } } +void +CServer::onOneShotTimerExpired(UInt32 id) +{ + CLock lock(&m_mutex); + + // ignore old timer or if there's no jump screen anymore + if (m_switchWaitScreen == NULL || id != m_switchWaitTimer) { + clearSwitchWait(); + return; + } + + // ignore if mouse is locked to screen + if (isLockedToScreenNoLock()) { + clearSwitchWait(); + return; + } + + // switch screen + switchScreen(m_switchWaitScreen, m_switchWaitX, m_switchWaitY, false); +} + +void +CServer::clearSwitchWait() +{ + if (m_switchWaitScreen != NULL) { + LOG((CLOG_DEBUG1 "cancel switch wait")); + m_switchWaitScreen = NULL; + } +} + void CServer::onKeyDown(KeyID id, KeyModifierMask mask) { @@ -582,11 +619,6 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) assert(m_primaryClient != NULL); assert(m_active == m_primaryClient); - // ignore if mouse is locked to screen - if (isLockedToScreenNoLock()) { - return false; - } - // get screen shape SInt32 ax, ay, aw, ah; m_active->getShape(ax, ay, aw, ah); @@ -616,6 +648,7 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) } else { // still on local screen + clearSwitchWait(); return false; } @@ -623,6 +656,27 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) // then ignore the move. IClient* newScreen = getNeighbor(m_active, dir, x, y); if (newScreen == NULL) { + clearSwitchWait(); + return false; + } + + // if waiting before a switch then prepare to switch later + if (m_switchWaitDelay > 0.0) { + if (m_switchWaitScreen == NULL || dir != m_switchWaitDir) { + m_switchWaitDir = dir; + m_switchWaitScreen = newScreen; + m_switchWaitX = x; + m_switchWaitY = y; + m_switchWaitTimer = m_primaryClient->addOneShotTimer( + m_switchWaitDelay); + LOG((CLOG_DEBUG1 "waiting to switch")); + } + return false; + } + + // ignore if mouse is locked to screen + if (isLockedToScreenNoLock()) { + LOG((CLOG_DEBUG1 "locked to screen")); return false; } @@ -667,72 +721,110 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) SInt32 ax, ay, aw, ah; m_active->getShape(ax, ay, aw, ah); + // find direction of neighbor + EDirection dir; + IClient* newScreen = NULL; + if (m_x < ax) { + dir = kLeft; + } + else if (m_x > ax + aw - 1) { + dir = kRight; + } + else if (m_y < ay) { + dir = kTop; + } + else if (m_y > ay + ah - 1) { + dir = kBottom; + } + else { + newScreen = m_active; + + // keep compiler quiet about unset variable + dir = kLeft; + } + // switch screens if the mouse is outside the screen and not // locked to the screen - IClient* newScreen = NULL; - if (!isLockedToScreenNoLock()) { - // find direction of neighbor - EDirection dir; - if (m_x < ax) { - dir = kLeft; - } - else if (m_x > ax + aw - 1) { - dir = kRight; - } - else if (m_y < ay) { - dir = kTop; - } - else if (m_y > ay + ah - 1) { - dir = kBottom; - } - else { - newScreen = m_active; - - // keep compiler quiet about unset variable - dir = kLeft; - } - - // get neighbor if we should switch + bool clamp = false; + if (newScreen == NULL) { + // get neighbor we should switch to + newScreen = getNeighbor(m_active, dir, m_x, m_y); + LOG((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); if (newScreen == NULL) { - LOG((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); - - // get new position or clamp to current screen - newScreen = getNeighbor(m_active, dir, m_x, m_y); - if (newScreen == NULL) { - LOG((CLOG_DEBUG1 "no neighbor; clamping")); - if (m_x < ax) { - m_x = ax; - } - else if (m_x > ax + aw - 1) { - m_x = ax + aw - 1; - } - if (m_y < ay) { - m_y = ay; - } - else if (m_y > ay + ah - 1) { - m_y = ay + ah - 1; - } + LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir))); + clamp = true; + } + else if (m_switchWaitDelay > 0.0) { + // wait to switch; prepare to switch later + if (m_switchWaitScreen == NULL || dir != m_switchWaitDir) { + m_switchWaitDir = dir; + m_switchWaitScreen = newScreen; + m_switchWaitX = m_x; + m_switchWaitY = m_y; + m_switchWaitTimer = m_primaryClient->addOneShotTimer( + m_switchWaitDelay); + LOG((CLOG_DEBUG1 "waiting to switch")); } + + // don't try to switch screen now + m_x = xOld + dx; + m_y = yOld + dy; + clamp = true; + } + else if (isLockedToScreenNoLock()) { + // clamp to edge when locked to screen + LOG((CLOG_DEBUG1 "locked to screen")); + clamp = true; } } else { - // clamp to edge when locked + // on same screen. if waiting and mouse is not on the border + // we're waiting on then stop waiting. + if (m_switchWaitScreen != NULL) { + bool clearWait; + SInt32 zoneSize = m_primaryClient->getJumpZoneSize(); + switch (m_switchWaitDir) { + case kLeft: + clearWait = (m_x >= ax + zoneSize); + break; + + case kRight: + clearWait = (m_x <= ax + aw - 1 - zoneSize); + break; + + case kTop: + clearWait = (m_y >= ay + zoneSize); + break; + + case kBottom: + clearWait = (m_y <= ay + ah - 1 + zoneSize); + break; + } + if (clearWait) { + clearSwitchWait(); + } + } + } + + // clamp mouse to edge + if (clamp) { if (m_x < ax) { m_x = ax; - LOG((CLOG_DEBUG1 "clamp to left of \"%s\"", m_active->getName().c_str())); + LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", m_active->getName().c_str())); } else if (m_x > ax + aw - 1) { m_x = ax + aw - 1; - LOG((CLOG_DEBUG1 "clamp to right of \"%s\"", m_active->getName().c_str())); + LOG((CLOG_DEBUG2 "clamp to right of \"%s\"", m_active->getName().c_str())); } if (m_y < ay) { m_y = ay; - LOG((CLOG_DEBUG1 "clamp to top of \"%s\"", m_active->getName().c_str())); + LOG((CLOG_DEBUG2 "clamp to top of \"%s\"", m_active->getName().c_str())); } else if (m_y > ay + ah - 1) { m_y = ay + ah - 1; - LOG((CLOG_DEBUG1 "clamp to bottom of \"%s\"", m_active->getName().c_str())); + LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", m_active->getName().c_str())); } + newScreen = NULL; } // warp cursor if on same screen @@ -801,6 +893,9 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y)); + // stop waiting to switch + clearSwitchWait(); + // record new position m_x = x; m_y = y; @@ -1099,6 +1194,11 @@ CServer::closeClients(const CConfig& config) // close that client assert(index2->second != m_primaryClient); index2->second->close(); + + // don't switch to it if we planned to + if (index2->second == m_switchWaitScreen) { + clearSwitchWait(); + } } else { ++index; @@ -1789,6 +1889,9 @@ CServer::removeConnection(const CString& name) // record new position (center of primary screen) m_primaryClient->getCursorCenter(m_x, m_y); + // stop waiting to switch if we were + clearSwitchWait(); + // don't notify active screen since it probably already disconnected LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 885d0e08..d4e93778 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -141,6 +141,7 @@ public: // IPrimaryScreenReceiver overrides virtual void onScreensaver(bool activated); + virtual void onOneShotTimerExpired(UInt32 id); virtual void onKeyDown(KeyID, KeyModifierMask); virtual void onKeyUp(KeyID, KeyModifierMask); virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -197,6 +198,9 @@ private: IClient* getNeighbor(IClient*, EDirection, SInt32& x, SInt32& y) const; + // reset switch wait state + void clearSwitchWait(); + // send screen options to \c client void sendOptions(IClient* client) const; @@ -320,6 +324,12 @@ private: CHTTPServer* m_httpServer; CCondVar m_httpAvailable; static const SInt32 s_httpMaxSimultaneousRequests; + + double m_switchWaitDelay; + EDirection m_switchWaitDir; + UInt32 m_switchWaitTimer; + IClient* m_switchWaitScreen; + SInt32 m_switchWaitX, m_switchWaitY; }; #endif diff --git a/lib/synergy/CPrimaryScreen.h b/lib/synergy/CPrimaryScreen.h index 2e953c25..0ae68e9d 100644 --- a/lib/synergy/CPrimaryScreen.h +++ b/lib/synergy/CPrimaryScreen.h @@ -129,6 +129,13 @@ public: */ virtual void setOptions(const COptionsList& options) = 0; + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer. + */ + virtual UInt32 addOneShotTimer(double timeout) = 0; + //@} //! @name accessors //@{ diff --git a/lib/synergy/IPrimaryScreenReceiver.h b/lib/synergy/IPrimaryScreenReceiver.h index 430b169a..ec36f263 100644 --- a/lib/synergy/IPrimaryScreenReceiver.h +++ b/lib/synergy/IPrimaryScreenReceiver.h @@ -34,6 +34,12 @@ public: */ virtual void onScreensaver(bool activated) = 0; + //! Notify of one-shot timer expiration + /*! + Called when a one-shot timer expires. + */ + virtual void onOneShotTimerExpired(UInt32 id) = 0; + // call to notify of events. onMouseMovePrimary() returns // true iff the mouse enters a jump zone and jumps. //! Notify of key press diff --git a/lib/synergy/IScreenEventHandler.h b/lib/synergy/IScreenEventHandler.h index 46c05d14..a68858ea 100644 --- a/lib/synergy/IScreenEventHandler.h +++ b/lib/synergy/IScreenEventHandler.h @@ -57,6 +57,12 @@ public: */ virtual bool onEvent(CEvent* event) = 0; + //! Notify of one-shot timer expiration + /*! + Called when a one-shot timer expires. + */ + virtual void onOneShotTimerExpired(UInt32 id) = 0; + //@} //! @name accessors //@{ diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index 5a9ebf10..ea7e594f 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -51,6 +51,7 @@ static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); +static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); //@} #undef OPTION_CODE From f411df65fbaefdde030bd73e235eca47bf403835 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 23 Feb 2003 19:29:08 +0000 Subject: [PATCH 459/807] Added support for a user option to require hitting the edge of a screen twice within a specified amount of time in order to switch screens. This can help prevent unintended switching. --- lib/server/CConfig.cpp | 24 ++- lib/server/CServer.cpp | 355 +++++++++++++++++++++--------------- lib/server/CServer.h | 28 ++- lib/synergy/OptionTypes.h | 1 + lib/synergy/ProtocolTypes.h | 5 +- 5 files changed, 262 insertions(+), 151 deletions(-) diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 09633313..b9024d6b 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -79,7 +79,7 @@ CConfig::renameScreen(const CString& oldName, // update connections for (index = m_map.begin(); index != m_map.end(); ++index) { - for (UInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) { + for (UInt32 i = 0; i < kNumDirections; ++i) { if (CStringUtil::CaselessCmp::equal(getCanonicalName( index->second.m_neighbor[i]), oldCanonical)) { index->second.m_neighbor[i] = newName; @@ -117,7 +117,7 @@ CConfig::removeScreen(const CString& name) // disconnect for (index = m_map.begin(); index != m_map.end(); ++index) { CCell& cell = index->second; - for (UInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) { + for (UInt32 i = 0; i < kNumDirections; ++i) { if (getCanonicalName(cell.m_neighbor[i]) == canonical) { cell.m_neighbor[i].erase(); } @@ -200,6 +200,8 @@ bool CConfig::connect(const CString& srcName, EDirection srcSide, const CString& dstName) { + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + // find source cell CCellMap::iterator index = m_map.find(getCanonicalName(srcName)); if (index == m_map.end()) { @@ -217,6 +219,8 @@ CConfig::connect(const CString& srcName, bool CConfig::disconnect(const CString& srcName, EDirection srcSide) { + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + // find source cell CCellMap::iterator index = m_map.find(srcName); if (index == m_map.end()) { @@ -406,6 +410,8 @@ CConfig::getCanonicalName(const CString& name) const CString CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const { + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + // find source cell CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); if (index == m_map.end()) { @@ -485,7 +491,7 @@ CConfig::operator==(const CConfig& x) const } // compare neighbors - for (UInt32 i = 0; i <= kLastDirection - kFirstDirection; ++i) { + for (UInt32 i = 0; i < kNumDirections; ++i) { if (!CStringUtil::CaselessCmp::equal(index1->second.m_neighbor[i], index2->second.m_neighbor[i])) { return false; @@ -516,6 +522,9 @@ const char* CConfig::dirName(EDirection dir) { static const char* s_name[] = { "left", "right", "top", "bottom" }; + + assert(dir >= kFirstDirection && dir <= kLastDirection); + return s_name[dir - kFirstDirection]; } @@ -627,6 +636,9 @@ CConfig::getOptionName(OptionID id) if (id == kOptionScreenSwitchDelay) { return "switchDelay"; } + if (id == kOptionScreenSwitchTwoTap) { + return "switchDoubleTap"; + } return NULL; } @@ -663,7 +675,8 @@ CConfig::getOptionValue(OptionID id, OptionValue value) } } if (id == kOptionHeartbeat || - id == kOptionScreenSwitchDelay) { + id == kOptionScreenSwitchDelay || + id == kOptionScreenSwitchTwoTap) { return CStringUtil::print("%d", value); } @@ -774,6 +787,9 @@ CConfig::readSectionOptions(std::istream& s) else if (name == "switchDelay") { addOption("", kOptionScreenSwitchDelay, parseInt(value)); } + else if (name == "switchDoubleTap") { + addOption("", kOptionScreenSwitchTwoTap, parseInt(value)); + } else { throw XConfigRead("unknown argument"); } diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index ce554f9b..37c43133 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -60,8 +60,13 @@ CServer::CServer(const CString& serverName) : m_activeSaver(NULL), m_httpServer(NULL), m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests), + m_switchDir(kNoDirection), + m_switchScreen(NULL), m_switchWaitDelay(0.0), - m_switchWaitScreen(NULL) + m_switchWaitEngaged(false), + m_switchTwoTapDelay(0.0), + m_switchTwoTapEngaged(false), + m_switchTwoTapArmed(false) { // do nothing } @@ -231,7 +236,14 @@ CServer::setConfig(const CConfig& config) if (m_switchWaitDelay < 0.0) { m_switchWaitDelay = 0.0; } - clearSwitchWait(); + m_switchWaitEngaged = false; + } + else if (id == kOptionScreenSwitchTwoTap) { + m_switchTwoTapDelay = 1.0e-3 * static_cast(value); + if (m_switchTwoTapDelay < 0.0) { + m_switchTwoTapDelay = 0.0; + } + m_switchTwoTapEngaged = false; } } } @@ -508,29 +520,20 @@ CServer::onOneShotTimerExpired(UInt32 id) { CLock lock(&m_mutex); - // ignore old timer or if there's no jump screen anymore - if (m_switchWaitScreen == NULL || id != m_switchWaitTimer) { - clearSwitchWait(); + // ignore if it's an old timer or if switch wait isn't engaged anymore + if (!m_switchWaitEngaged || id != m_switchWaitTimer) { return; } // ignore if mouse is locked to screen if (isLockedToScreenNoLock()) { - clearSwitchWait(); + LOG((CLOG_DEBUG1 "locked to screen")); + clearSwitchState(); return; } // switch screen - switchScreen(m_switchWaitScreen, m_switchWaitX, m_switchWaitY, false); -} - -void -CServer::clearSwitchWait() -{ - if (m_switchWaitScreen != NULL) { - LOG((CLOG_DEBUG1 "cancel switch wait")); - m_switchWaitScreen = NULL; - } + switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false); } void @@ -629,60 +632,37 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) if (x < ax + zoneSize) { x -= zoneSize; dir = kLeft; - LOG((CLOG_DEBUG1 "switch to left")); } else if (x >= ax + aw - zoneSize) { x += zoneSize; dir = kRight; - LOG((CLOG_DEBUG1 "switch to right")); } else if (y < ay + zoneSize) { y -= zoneSize; dir = kTop; - LOG((CLOG_DEBUG1 "switch to top")); } else if (y >= ay + ah - zoneSize) { y += zoneSize; dir = kBottom; - LOG((CLOG_DEBUG1 "switch to bottom")); } else { // still on local screen - clearSwitchWait(); + onNoSwitch(); return false; } - // get jump destination and, if no screen in jump direction, - // then ignore the move. + // get jump destination IClient* newScreen = getNeighbor(m_active, dir, x, y); - if (newScreen == NULL) { - clearSwitchWait(); + + // should we switch or not? + if (isSwitchOkay(newScreen, dir, x, y)) { + // switch screen + switchScreen(newScreen, x, y, false); + return true; + } + else { return false; } - - // if waiting before a switch then prepare to switch later - if (m_switchWaitDelay > 0.0) { - if (m_switchWaitScreen == NULL || dir != m_switchWaitDir) { - m_switchWaitDir = dir; - m_switchWaitScreen = newScreen; - m_switchWaitX = x; - m_switchWaitY = y; - m_switchWaitTimer = m_primaryClient->addOneShotTimer( - m_switchWaitDelay); - LOG((CLOG_DEBUG1 "waiting to switch")); - } - return false; - } - - // ignore if mouse is locked to screen - if (isLockedToScreenNoLock()) { - LOG((CLOG_DEBUG1 "locked to screen")); - return false; - } - - // switch screen - switchScreen(newScreen, x, y, false); - return true; } void @@ -721,93 +701,75 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) SInt32 ax, ay, aw, ah; m_active->getShape(ax, ay, aw, ah); - // find direction of neighbor - EDirection dir; - IClient* newScreen = NULL; - if (m_x < ax) { - dir = kLeft; - } - else if (m_x > ax + aw - 1) { - dir = kRight; - } - else if (m_y < ay) { - dir = kTop; - } - else if (m_y > ay + ah - 1) { - dir = kBottom; - } - else { - newScreen = m_active; + // find direction of neighbor and get the neighbor + IClient* newScreen; + do { + EDirection dir; + if (m_x < ax) { + dir = kLeft; + } + else if (m_x > ax + aw - 1) { + dir = kRight; + } + else if (m_y < ay) { + dir = kTop; + } + else if (m_y > ay + ah - 1) { + dir = kBottom; + } + else { + // we haven't left the screen + newScreen = m_active; - // keep compiler quiet about unset variable - dir = kLeft; - } + // if waiting and mouse is not on the border we're waiting + // on then stop waiting. also if it's not on the border + // then arm the double tap. + if (m_switchScreen != NULL) { + bool clearWait; + SInt32 zoneSize = m_primaryClient->getJumpZoneSize(); + switch (m_switchDir) { + case kLeft: + clearWait = (m_x >= ax + zoneSize); + break; - // switch screens if the mouse is outside the screen and not - // locked to the screen - bool clamp = false; - if (newScreen == NULL) { - // get neighbor we should switch to + case kRight: + clearWait = (m_x <= ax + aw - 1 - zoneSize); + break; + + case kTop: + clearWait = (m_y >= ay + zoneSize); + break; + + case kBottom: + clearWait = (m_y <= ay + ah - 1 + zoneSize); + break; + } + if (clearWait) { + onNoSwitch(); + } + } + + // skip rest of block + break; + } + + // try to switch screen. get the neighbor. newScreen = getNeighbor(m_active, dir, m_x, m_y); - LOG((CLOG_DEBUG1 "leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); - if (newScreen == NULL) { - LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir))); - clamp = true; - } - else if (m_switchWaitDelay > 0.0) { - // wait to switch; prepare to switch later - if (m_switchWaitScreen == NULL || dir != m_switchWaitDir) { - m_switchWaitDir = dir; - m_switchWaitScreen = newScreen; - m_switchWaitX = m_x; - m_switchWaitY = m_y; - m_switchWaitTimer = m_primaryClient->addOneShotTimer( - m_switchWaitDelay); - LOG((CLOG_DEBUG1 "waiting to switch")); - } - // don't try to switch screen now - m_x = xOld + dx; - m_y = yOld + dy; - clamp = true; - } - else if (isLockedToScreenNoLock()) { - // clamp to edge when locked to screen - LOG((CLOG_DEBUG1 "locked to screen")); - clamp = true; + // see if we should switch + if (!isSwitchOkay(newScreen, dir, m_x, m_y)) { + newScreen = m_active; } + } while (false); + + if (newScreen != m_active) { + // switch screens + switchScreen(newScreen, m_x, m_y, false); } else { - // on same screen. if waiting and mouse is not on the border - // we're waiting on then stop waiting. - if (m_switchWaitScreen != NULL) { - bool clearWait; - SInt32 zoneSize = m_primaryClient->getJumpZoneSize(); - switch (m_switchWaitDir) { - case kLeft: - clearWait = (m_x >= ax + zoneSize); - break; - - case kRight: - clearWait = (m_x <= ax + aw - 1 - zoneSize); - break; - - case kTop: - clearWait = (m_y >= ay + zoneSize); - break; - - case kBottom: - clearWait = (m_y <= ay + ah - 1 + zoneSize); - break; - } - if (clearWait) { - clearSwitchWait(); - } - } - } - - // clamp mouse to edge - if (clamp) { + // same screen. clamp mouse to edge. + m_x = xOld + dx; + m_y = yOld + dy; if (m_x < ax) { m_x = ax; LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", m_active->getName().c_str())); @@ -824,22 +786,13 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) m_y = ay + ah - 1; LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", m_active->getName().c_str())); } - newScreen = NULL; - } - // warp cursor if on same screen - if (newScreen == NULL || newScreen == m_active) { - // do nothing if mouse didn't move + // warp cursor if it moved. if (m_x != xOld || m_y != yOld) { LOG((CLOG_DEBUG2 "move on %s to %d,%d", m_active->getName().c_str(), m_x, m_y)); m_active->mouseMove(m_x, m_y); } } - - // otherwise screen screens - else { - switchScreen(newScreen, m_x, m_y, false); - } } void @@ -894,7 +847,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y)); // stop waiting to switch - clearSwitchWait(); + clearSwitchState(); // record new position m_x = x; @@ -1166,6 +1119,120 @@ CServer::getNeighbor(IClient* src, return dst; } +bool +CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) +{ + LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); + + // is there a neighbor? + if (newScreen == NULL) { + // there's no neighbor. we don't want to switch and we don't + // want to try to switch later. + LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir))); + clearSwitchState(); + return false; + } + + // should we switch or not? + bool preventSwitch = false; + bool allowSwitch = false; + + // note if the switch direction has changed. save the new + // direction and screen if so. + bool isNewDirection = (dir != m_switchDir); + if (isNewDirection || m_switchScreen == NULL) { + m_switchDir = dir; + m_switchScreen = newScreen; + } + + // is this a double tap and do we care? + if (!allowSwitch && m_switchTwoTapDelay > 0.0) { + if (isNewDirection || !m_switchTwoTapEngaged) { + // tapping a different or new edge. prepare for second tap. + preventSwitch = true; + m_switchTwoTapEngaged = true; + m_switchTwoTapArmed = false; + m_switchTwoTapTimer.reset(); + LOG((CLOG_DEBUG1 "waiting for second tap")); + } + else { + // second tap if we were armed. if soon enough then switch. + if (m_switchTwoTapArmed && + m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay) { + allowSwitch = true; + } + else { + // not fast enough. reset the clock. + preventSwitch = true; + m_switchTwoTapEngaged = true; + m_switchTwoTapArmed = false; + m_switchTwoTapTimer.reset(); + LOG((CLOG_DEBUG1 "waiting for second tap")); + } + } + } + + // if waiting before a switch then prepare to switch later + if (!allowSwitch && m_switchWaitDelay > 0.0) { + if (isNewDirection) { + m_switchWaitEngaged = true; + m_switchWaitX = x; + m_switchWaitY = y; + m_switchWaitTimer = m_primaryClient->addOneShotTimer( + m_switchWaitDelay); + LOG((CLOG_DEBUG1 "waiting to switch")); + } + preventSwitch = true; + } + + // ignore if mouse is locked to screen + if (!preventSwitch && isLockedToScreenNoLock()) { + LOG((CLOG_DEBUG1 "locked to screen")); + preventSwitch = true; + + // don't try to switch later. it's possible that we might + // not be locked to the screen when the wait delay expires + // and could switch then but we'll base the decision on + // when the user first attempts the switch. this also + // ensures that all switch tests are using the same + clearSwitchState(); + } + + return !preventSwitch; +} + +void +CServer::onNoSwitch() +{ + if (m_switchTwoTapEngaged) { + if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) { + // second tap took too long. disengage. + m_switchTwoTapEngaged = false; + m_switchTwoTapArmed = false; + } + else { + // we've moved away from the edge and there's still + // time to get back for a double tap. + m_switchTwoTapArmed = true; + } + } + + // once the mouse moves away from the edge we no longer want to + // switch after a delay. + m_switchWaitEngaged = false; +} + +void +CServer::clearSwitchState() +{ + if (m_switchScreen != NULL) { + m_switchDir = kNoDirection; + m_switchScreen = NULL; + m_switchWaitEngaged = false; + m_switchTwoTapEngaged = false; + } +} + void CServer::closeClients(const CConfig& config) { @@ -1196,8 +1263,8 @@ CServer::closeClients(const CConfig& config) index2->second->close(); // don't switch to it if we planned to - if (index2->second == m_switchWaitScreen) { - clearSwitchWait(); + if (index2->second == m_switchScreen) { + clearSwitchState(); } } else { @@ -1890,7 +1957,9 @@ CServer::removeConnection(const CString& name) m_primaryClient->getCursorCenter(m_x, m_y); // stop waiting to switch if we were - clearSwitchWait(); + if (active == m_switchScreen) { + clearSwitchState(); + } // don't notify active screen since it probably already disconnected LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); diff --git a/lib/server/CServer.h b/lib/server/CServer.h index d4e93778..11896830 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -22,6 +22,7 @@ #include "CCondVar.h" #include "CMutex.h" #include "CThread.h" +#include "CStopwatch.h" #include "stdlist.h" #include "stdmap.h" @@ -198,8 +199,18 @@ private: IClient* getNeighbor(IClient*, EDirection, SInt32& x, SInt32& y) const; + // test if a switch is permitted. this includes testing user + // options like switch delay and tracking any state required to + // implement them. returns true iff a switch is permitted. + bool isSwitchOkay(IClient* dst, EDirection, + SInt32 x, SInt32 y); + + // update switch state due to a mouse move that doesn't try to + // switch screens. + void onNoSwitch(); + // reset switch wait state - void clearSwitchWait(); + void clearSwitchState(); // send screen options to \c client void sendOptions(IClient* client) const; @@ -325,11 +336,22 @@ private: CCondVar m_httpAvailable; static const SInt32 s_httpMaxSimultaneousRequests; + // common state for screen switch tests. all tests are always + // trying to reach the same screen in the same direction. + EDirection m_switchDir; + IClient* m_switchScreen; + + // state for delayed screen switching double m_switchWaitDelay; - EDirection m_switchWaitDir; UInt32 m_switchWaitTimer; - IClient* m_switchWaitScreen; + bool m_switchWaitEngaged; SInt32 m_switchWaitX, m_switchWaitY; + + // state for double-tap screen switching + double m_switchTwoTapDelay; + CStopwatch m_switchTwoTapTimer; + bool m_switchTwoTapEngaged; + bool m_switchTwoTapArmed; }; #endif diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index ea7e594f..84a09f5c 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -52,6 +52,7 @@ static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); +static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); //@} #undef OPTION_CODE diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index b37d1265..8f9bd569 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -36,14 +36,17 @@ static const double kHeartBeatsUntilDeath = 3.0; // direction constants enum EDirection { + kNoDirection, kLeft, kRight, kTop, kBottom, kFirstDirection = kLeft, - kLastDirection = kBottom + kLastDirection = kBottom, + kNumDirections = kLastDirection - kFirstDirection + 1 }; enum EDirectionMask { + kNoDirMask = 0, kLeftMask = 1 << kLeft, kRightMask = 1 << kRight, kTopMask = 1 << kTop, From 1d17f865ea5df9264955e4b65a43ae87b38e3c19 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 12 Mar 2003 22:34:07 +0000 Subject: [PATCH 460/807] Added switch delay and double-tap options to win32 and added a tray icon to the client and server that gives status feedback to the user and allows the user to kill the app. --- cmd/launcher/CAdvancedOptions.cpp | 183 +++++++ cmd/launcher/CAdvancedOptions.h | 74 +++ cmd/launcher/CGlobalOptions.cpp | 211 +++++++ cmd/launcher/CGlobalOptions.h | 66 +++ cmd/launcher/LaunchUtil.cpp | 12 + cmd/launcher/LaunchUtil.h | 3 + cmd/launcher/Makefile.am | 4 + cmd/launcher/launcher.cpp | 199 ++++--- cmd/launcher/launcher.dsp | 16 + cmd/launcher/resource.h | 22 +- cmd/synergyc/CClientTaskBarReceiver.cpp | 163 ++++++ cmd/synergyc/CClientTaskBarReceiver.h | 110 ++++ .../CMSWindowsClientTaskBarReceiver.cpp | 280 ++++++++++ .../CMSWindowsClientTaskBarReceiver.h | 58 ++ .../CXWindowsClientTaskBarReceiver.cpp | 61 +++ cmd/synergyc/CXWindowsClientTaskBarReceiver.h | 37 ++ cmd/synergyc/Makefile.am | 24 +- cmd/synergyc/resource.h | 9 + cmd/synergyc/synergyc.cpp | 230 +++++--- cmd/synergyc/synergyc.dsp | 32 ++ cmd/synergyc/tb_error.ico | Bin 0 -> 318 bytes cmd/synergyc/tb_idle.ico | Bin 0 -> 318 bytes cmd/synergyc/tb_run.ico | Bin 0 -> 318 bytes cmd/synergyc/tb_wait.ico | Bin 0 -> 318 bytes .../CMSWindowsServerTaskBarReceiver.cpp | 300 ++++++++++ .../CMSWindowsServerTaskBarReceiver.h | 58 ++ cmd/synergys/CServerTaskBarReceiver.cpp | 171 ++++++ cmd/synergys/CServerTaskBarReceiver.h | 110 ++++ .../CXWindowsServerTaskBarReceiver.cpp | 61 +++ cmd/synergys/CXWindowsServerTaskBarReceiver.h | 37 ++ cmd/synergys/Makefile.am | 24 +- cmd/synergys/resource.h | 16 +- cmd/synergys/synergys.cpp | 250 ++++++--- cmd/synergys/synergys.dsp | 32 ++ cmd/synergys/tb_error.ico | Bin 0 -> 318 bytes cmd/synergys/tb_idle.ico | Bin 0 -> 318 bytes cmd/synergys/tb_run.ico | Bin 0 -> 318 bytes cmd/synergys/tb_wait.ico | Bin 0 -> 318 bytes lib/arch/CArch.cpp | 35 +- lib/arch/CArch.h | 14 +- lib/arch/CArchConsoleWindows.cpp | 10 +- lib/arch/CArchConsoleWindows.h | 2 +- lib/arch/CArchImpl.cpp | 2 + lib/arch/CArchMultithreadPosix.cpp | 6 +- lib/arch/CArchMultithreadPosix.h | 2 +- lib/arch/CArchMultithreadWindows.cpp | 84 +++ lib/arch/CArchMultithreadWindows.h | 2 +- lib/arch/CArchTaskBarWindows.cpp | 518 ++++++++++++++++++ lib/arch/CArchTaskBarWindows.h | 110 ++++ lib/arch/CArchTaskBarXWindows.cpp | 47 ++ lib/arch/CArchTaskBarXWindows.h | 34 ++ lib/arch/IArchMultithread.h | 23 +- lib/arch/IArchTaskBar.h | 63 +++ lib/arch/IArchTaskBarReceiver.h | 90 +++ lib/arch/Makefile.am | 6 + lib/arch/arch.dsp | 29 + lib/base/CJobList.cpp | 113 ++++ lib/base/CJobList.h | 72 +++ lib/base/LogOutputters.cpp | 23 + lib/base/LogOutputters.h | 18 + lib/base/Makefile.am | 2 + lib/base/base.dsp | 8 + lib/client/CClient.cpp | 85 ++- lib/client/CClient.h | 41 ++ lib/client/CServerProxy.cpp | 1 + lib/mt/CThread.cpp | 12 +- lib/mt/CThread.h | 22 +- lib/mt/CTimerThread.cpp | 10 +- lib/mt/CTimerThread.h | 7 +- lib/platform/CMSWindowsPrimaryScreen.cpp | 12 + lib/platform/CMSWindowsPrimaryScreen.h | 2 + lib/platform/CMSWindowsScreen.cpp | 69 ++- lib/platform/CMSWindowsScreen.h | 11 + lib/platform/CMSWindowsSecondaryScreen.cpp | 11 + lib/platform/CMSWindowsSecondaryScreen.h | 3 +- lib/platform/CSynergyHook.cpp | 6 + lib/platform/CXWindowsSecondaryScreen.cpp | 13 +- lib/platform/CXWindowsSecondaryScreen.h | 7 +- lib/server/CServer.cpp | 204 +++++-- lib/server/CServer.h | 55 ++ lib/synergy/CSecondaryScreen.cpp | 41 +- lib/synergy/CSecondaryScreen.h | 38 +- 82 files changed, 4420 insertions(+), 396 deletions(-) create mode 100644 cmd/launcher/CAdvancedOptions.cpp create mode 100644 cmd/launcher/CAdvancedOptions.h create mode 100644 cmd/launcher/CGlobalOptions.cpp create mode 100644 cmd/launcher/CGlobalOptions.h create mode 100644 cmd/synergyc/CClientTaskBarReceiver.cpp create mode 100644 cmd/synergyc/CClientTaskBarReceiver.h create mode 100644 cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp create mode 100644 cmd/synergyc/CMSWindowsClientTaskBarReceiver.h create mode 100644 cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp create mode 100644 cmd/synergyc/CXWindowsClientTaskBarReceiver.h create mode 100644 cmd/synergyc/tb_error.ico create mode 100644 cmd/synergyc/tb_idle.ico create mode 100644 cmd/synergyc/tb_run.ico create mode 100644 cmd/synergyc/tb_wait.ico create mode 100644 cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp create mode 100644 cmd/synergys/CMSWindowsServerTaskBarReceiver.h create mode 100644 cmd/synergys/CServerTaskBarReceiver.cpp create mode 100644 cmd/synergys/CServerTaskBarReceiver.h create mode 100644 cmd/synergys/CXWindowsServerTaskBarReceiver.cpp create mode 100644 cmd/synergys/CXWindowsServerTaskBarReceiver.h create mode 100644 cmd/synergys/tb_error.ico create mode 100644 cmd/synergys/tb_idle.ico create mode 100644 cmd/synergys/tb_run.ico create mode 100644 cmd/synergys/tb_wait.ico create mode 100644 lib/arch/CArchTaskBarWindows.cpp create mode 100644 lib/arch/CArchTaskBarWindows.h create mode 100644 lib/arch/CArchTaskBarXWindows.cpp create mode 100644 lib/arch/CArchTaskBarXWindows.h create mode 100644 lib/arch/IArchTaskBar.h create mode 100644 lib/arch/IArchTaskBarReceiver.h create mode 100644 lib/base/CJobList.cpp create mode 100644 lib/base/CJobList.h diff --git a/cmd/launcher/CAdvancedOptions.cpp b/cmd/launcher/CAdvancedOptions.cpp new file mode 100644 index 00000000..a6742ea1 --- /dev/null +++ b/cmd/launcher/CAdvancedOptions.cpp @@ -0,0 +1,183 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "ProtocolTypes.h" +#include "CStringUtil.h" +#include "CArch.h" +#include "CAdvancedOptions.h" +#include "LaunchUtil.h" +#include "resource.h" + +// +// CAdvancedOptions +// + +CAdvancedOptions* CAdvancedOptions::s_singleton = NULL; + +CAdvancedOptions::CAdvancedOptions(HWND parent, CConfig* config) : + m_parent(parent), + m_config(config), + m_isClient(false), + m_screenName(ARCH->getHostName()), + m_port(kDefaultPort) +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CAdvancedOptions::~CAdvancedOptions() +{ + s_singleton = NULL; +} + +void +CAdvancedOptions::doModal(bool isClient) +{ + // save state + m_isClient = isClient; + + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADVANCED_OPTIONS), + m_parent, dlgProc, (LPARAM)this); +} + +CString +CAdvancedOptions::getScreenName() const +{ + return m_screenName; +} + +int +CAdvancedOptions::getPort() const +{ + return m_port; +} + +CString +CAdvancedOptions::getCommandLine(bool isClient, const CString& serverName) const +{ + CString cmdLine; + + // screen name + if (!m_screenName.empty()) { + cmdLine += " --name "; + cmdLine += m_screenName; + } + + // port + char portString[20]; + sprintf(portString, "%d", m_port); + if (isClient) { + cmdLine += " "; + cmdLine += serverName; + cmdLine += ":"; + cmdLine += portString; + } + else { + cmdLine += " --address :"; + cmdLine += portString; + } + + return cmdLine; +} + +void +CAdvancedOptions::init(HWND hwnd) +{ + HWND child; + char buffer[20]; + sprintf(buffer, "%d", m_port); + child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); + + child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)m_screenName.c_str()); +} + +bool +CAdvancedOptions::save(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT); + CString name = getWindowText(child); + if (!m_config->isValidScreenName(name)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + name.c_str())); + SetFocus(child); + return false; + } + if (!m_isClient && !m_config->isScreen(name)) { + showError(hwnd, CStringUtil::format( + getString(IDS_UNKNOWN_SCREEN_NAME).c_str(), + name.c_str())); + SetFocus(child); + return false; + } + + // get and verify port + child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT); + CString portString = getWindowText(child); + int port = atoi(portString.c_str()); + if (port < 1 || port > 65535) { + CString defaultPortString = CStringUtil::print("%d", kDefaultPort); + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_PORT).c_str(), + portString.c_str(), + defaultPortString.c_str())); + SetFocus(child); + return false; + } + + // save state + m_screenName = name; + m_port = port; + + return true; +} + +BOOL +CAdvancedOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + init(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (save(hwnd)) { + EndDialog(hwnd, 0); + } + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CAdvancedOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CAdvancedOptions.h b/cmd/launcher/CAdvancedOptions.h new file mode 100644 index 00000000..f073f821 --- /dev/null +++ b/cmd/launcher/CAdvancedOptions.h @@ -0,0 +1,74 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CADVANCEDOPTIONS_H +#define CADVANCEDOPTIONS_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Advanced options dialog for Microsoft Windows launcher +class CAdvancedOptions { +public: + CAdvancedOptions(HWND parent, CConfig*); + ~CAdvancedOptions(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(bool isClient); + + //@} + //! @name accessors + //@{ + + //! Get the screen name + CString getScreenName() const; + + //! Get the port + int getPort() const; + + //! Convert options to command line string + CString getCommandLine(bool isClient, + const CString& serverName) const; + + //@} + +private: + void init(HWND hwnd); + bool save(HWND hwnd); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CAdvancedOptions* s_singleton; + + HWND m_parent; + CConfig* m_config; + bool m_isClient; + CString m_screenName; + int m_port; +}; + +#endif diff --git a/cmd/launcher/CGlobalOptions.cpp b/cmd/launcher/CGlobalOptions.cpp new file mode 100644 index 00000000..445b07a4 --- /dev/null +++ b/cmd/launcher/CGlobalOptions.cpp @@ -0,0 +1,211 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "ProtocolTypes.h" +#include "CStringUtil.h" +#include "CArch.h" +#include "CGlobalOptions.h" +#include "LaunchUtil.h" +#include "resource.h" + +static const int s_defaultDelay = 250; + +// +// CGlobalOptions +// + +CGlobalOptions* CGlobalOptions::s_singleton = NULL; + +CGlobalOptions::CGlobalOptions(HWND parent, CConfig* config) : + m_parent(parent), + m_config(config), + m_delayTime(s_defaultDelay), + m_twoTapTime(s_defaultDelay) +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CGlobalOptions::~CGlobalOptions() +{ + s_singleton = NULL; +} + +void +CGlobalOptions::doModal() +{ + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_GLOBAL_OPTIONS), + m_parent, dlgProc, (LPARAM)this); +} + +void +CGlobalOptions::init(HWND hwnd) +{ + HWND child; + char buffer[30]; + + // reset options + sprintf(buffer, "%d", m_delayTime); + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + setWindowText(child, buffer); + sprintf(buffer, "%d", m_twoTapTime); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + setWindowText(child, buffer); + + // get the global options + const CConfig::CScreenOptions* options = m_config->getOptions(""); + if (options != NULL) { + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + const OptionID id = index->first; + const OptionValue value = index->second; + if (id == kOptionScreenSwitchDelay) { + if (value > 0) { + sprintf(buffer, "%d", value); + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + setItemChecked(child, true); + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + setWindowText(child, buffer); + } + } + else if (id == kOptionScreenSwitchTwoTap) { + if (value > 0) { + sprintf(buffer, "%d", value); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + setItemChecked(child, true); + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + setWindowText(child, buffer); + } + } + } + } +} + +bool +CGlobalOptions::save(HWND hwnd) +{ + HWND child; + int newDelayTime = 0; + int newTwoTapTime = 0; + + // get requested options + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + if (isItemChecked(child)) { + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + newDelayTime = getTime(hwnd, child, true); + if (newDelayTime == 0) { + return false; + } + } + else { + child = getItem(hwnd, IDC_GLOBAL_DELAY_TIME); + newDelayTime = getTime(hwnd, child, false); + if (newDelayTime == 0) { + newDelayTime = s_defaultDelay; + } + } + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + if (isItemChecked(child)) { + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + newTwoTapTime = getTime(hwnd, child, true); + if (newTwoTapTime == 0) { + return false; + } + } + else { + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); + newTwoTapTime = getTime(hwnd, child, false); + if (newTwoTapTime == 0) { + newTwoTapTime = s_defaultDelay; + } + } + + // remove existing config options + m_config->removeOption("", kOptionScreenSwitchDelay); + m_config->removeOption("", kOptionScreenSwitchTwoTap); + + // add requested options + child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); + if (isItemChecked(child)) { + m_config->addOption("", kOptionScreenSwitchDelay, newDelayTime); + } + child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_CHECK); + if (isItemChecked(child)) { + m_config->addOption("", kOptionScreenSwitchTwoTap, newTwoTapTime); + } + + // save last values + m_delayTime = newDelayTime; + m_twoTapTime = newTwoTapTime; + + return true; +} + +int +CGlobalOptions::getTime(HWND hwnd, HWND child, bool reportError) +{ + CString valueString = getWindowText(child); + int value = atoi(valueString.c_str()); + if (value < 1) { + if (reportError) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_TIME).c_str(), + valueString.c_str())); + SetFocus(child); + } + return 0; + } + return value; +} + +BOOL +CGlobalOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + init(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (save(hwnd)) { + EndDialog(hwnd, 0); + } + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CGlobalOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CGlobalOptions.h b/cmd/launcher/CGlobalOptions.h new file mode 100644 index 00000000..520df187 --- /dev/null +++ b/cmd/launcher/CGlobalOptions.h @@ -0,0 +1,66 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CGLOBALOPTIONS_H +#define CGLOBALOPTIONS_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Global options dialog for Microsoft Windows launcher +class CGlobalOptions { +public: + CGlobalOptions(HWND parent, CConfig*); + ~CGlobalOptions(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + + //@} + +private: + void init(HWND hwnd); + bool save(HWND hwnd); + + int getTime(HWND hwnd, HWND child, bool reportError); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CGlobalOptions* s_singleton; + + HWND m_parent; + CConfig* m_config; + int m_delayTime; + int m_twoTapTime; +}; + +#endif diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp index ccd35c07..6a41f0c5 100644 --- a/cmd/launcher/LaunchUtil.cpp +++ b/cmd/launcher/LaunchUtil.cpp @@ -103,6 +103,18 @@ enableItem(HWND hwnd, int id, bool enabled) EnableWindow(GetDlgItem(hwnd, id), enabled); } +void +setItemChecked(HWND hwnd, bool checked) +{ + SendMessage(hwnd, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0); +} + +bool +isItemChecked(HWND hwnd) +{ + return (SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED); +} + CString getAppPath(const CString& appName) { diff --git a/cmd/launcher/LaunchUtil.h b/cmd/launcher/LaunchUtil.h index 414246e6..a7638a10 100644 --- a/cmd/launcher/LaunchUtil.h +++ b/cmd/launcher/LaunchUtil.h @@ -42,6 +42,9 @@ CString getWindowText(HWND hwnd); HWND getItem(HWND hwnd, int id); void enableItem(HWND hwnd, int id, bool enabled); +void setItemChecked(HWND, bool); +bool isItemChecked(HWND); + CString getAppPath(const CString& appName); bool loadConfig(CConfig& config); diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am index acf31abd..782d771a 100644 --- a/cmd/launcher/Makefile.am +++ b/cmd/launcher/Makefile.am @@ -16,8 +16,12 @@ DEPTH = ../.. VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ + CAdvancedOptions.cpp \ + CAdvancedOptions.h \ CAutoStart.cpp \ CAutoStart.h \ + CGlobalOptions.cpp \ + CGlobalOptions.h \ LaunchUtil.cpp \ LaunchUtil.h \ launcher.cpp \ diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 4b28794a..fe13e452 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -13,6 +13,8 @@ */ #include "CConfig.h" +#include "KeyTypes.h" +#include "OptionTypes.h" #include "ProtocolTypes.h" #include "CLog.h" #include "CStringUtil.h" @@ -24,6 +26,8 @@ // these must come after the above because it includes windows.h #include "LaunchUtil.h" #include "CAutoStart.h" +#include "CGlobalOptions.h" +#include "CAdvancedOptions.h" #define CONFIG_NAME "synergy.sgc" #define CLIENT_APP "synergyc.exe" @@ -47,10 +51,28 @@ public: HANDLE m_stop; }; -HINSTANCE s_instance = NULL; +struct CModifierInfo { +public: + int m_ctrlID; + const char* m_name; + KeyModifierID m_modifierID; + OptionID m_optionID; +}; -static const TCHAR* s_mainClass = TEXT("GoSynergy"); -static const TCHAR* s_layoutClass = TEXT("SynergyLayout"); +static const CModifierInfo s_modifiers[] = { + { IDC_ADD_MOD_SHIFT, "Shift", + kKeyModifierIDShift, kOptionModifierMapForShift }, + { IDC_ADD_MOD_CTRL, "Ctrl", + kKeyModifierIDControl, kOptionModifierMapForControl }, + { IDC_ADD_MOD_ALT, "Alt", + kKeyModifierIDAlt, kOptionModifierMapForAlt }, + { IDC_ADD_MOD_META, "Meta", + kKeyModifierIDMeta, kOptionModifierMapForMeta }, + { IDC_ADD_MOD_SUPER, "Super", + kKeyModifierIDSuper, kOptionModifierMapForSuper } +}; + +static const KeyModifierID baseModifier = kKeyModifierIDShift; static const char* s_debugName[][2] = { { TEXT("Error"), "ERROR" }, @@ -63,6 +85,14 @@ static const char* s_debugName[][2] = { }; static const int s_defaultDebug = 3; // INFO +HINSTANCE s_instance = NULL; + +static CGlobalOptions* s_globalOptions = NULL; +static CAdvancedOptions* s_advancedOptions = NULL; + +static const TCHAR* s_mainClass = TEXT("GoSynergy"); +static const TCHAR* s_layoutClass = TEXT("SynergyLayout"); + // // program arguments // @@ -127,7 +157,7 @@ bool isClientChecked(HWND hwnd) { HWND child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); - return (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED); + return isItemChecked(child); } static @@ -476,58 +506,20 @@ static CString getCommandLine(HWND hwnd, bool testing) { - // decide if client or server - const bool isClient = isClientChecked(hwnd); - - // get and verify screen name - HWND child = getItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); - CString name = getWindowText(child); - if (!ARG->m_config.isValidScreenName(name)) { - showError(hwnd, CStringUtil::format( - getString(IDS_INVALID_SCREEN_NAME).c_str(), - name.c_str())); - SetFocus(child); - return CString(); - } - if (!isClient && !ARG->m_config.isScreen(name)) { - showError(hwnd, CStringUtil::format( - getString(IDS_UNKNOWN_SCREEN_NAME).c_str(), - name.c_str())); - SetFocus(child); - return CString(); - } - - // get and verify port - child = getItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT); - CString portString = getWindowText(child); - UInt32 port = (UInt32)atoi(portString.c_str()); - if (port < 1 || port > 65535) { - CString defaultPortString = CStringUtil::print("%d", kDefaultPort); - showError(hwnd, CStringUtil::format( - getString(IDS_INVALID_PORT).c_str(), - portString.c_str(), - defaultPortString.c_str())); - SetFocus(child); - return CString(); - } - - // prepare command line CString cmdLine; - if (testing) { - // constant testing args - cmdLine += " -z --no-restart --no-daemon"; - // debug level testing arg - child = getItem(hwnd, IDC_MAIN_DEBUG); - cmdLine += " --debug "; - cmdLine += s_debugName[SendMessage(child, CB_GETCURSEL, 0, 0)][1]; + // add constant testing args + if (testing) { + cmdLine += " -z --no-restart --no-daemon"; } - cmdLine += " --name "; - cmdLine += name; + + // get the server name + CString server; + bool isClient = isClientChecked(hwnd); if (isClient) { // check server name - child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); - CString server = getWindowText(child); + HWND child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + server = getWindowText(child); if (!ARG->m_config.isValidScreenName(server)) { showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SERVER_NAME).c_str(), @@ -551,16 +543,19 @@ getCommandLine(HWND hwnd, bool testing) if (testing) { cmdLine += " --no-camp"; } - cmdLine += " "; - cmdLine += server; - cmdLine += ":"; - cmdLine += portString; } - else { - cmdLine += " --address :"; - cmdLine += portString; + + // debug level + if (testing) { + HWND child = getItem(hwnd, IDC_MAIN_DEBUG); + DWORD debug = SendMessage(child, CB_GETCURSEL, 0, 0); + cmdLine += " --debug "; + cmdLine += s_debugName[debug][1]; } + // add advanced options + cmdLine += s_advancedOptions->getCommandLine(isClient, server); + return cmdLine; } @@ -711,11 +706,9 @@ initMainWindow(HWND hwnd) // choose client/server radio buttons HWND child; child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); - SendMessage(child, BM_SETCHECK, !configLoaded ? - BST_CHECKED : BST_UNCHECKED, 0); + setItemChecked(child, !configLoaded); child = getItem(hwnd, IDC_MAIN_SERVER_RADIO); - SendMessage(child, BM_SETCHECK, configLoaded ? - BST_CHECKED : BST_UNCHECKED, 0); + setItemChecked(child, configLoaded); // if config is loaded then initialize server controls if (configLoaded) { @@ -729,16 +722,7 @@ initMainWindow(HWND hwnd) } } - // initialize other controls - char buffer[256]; - sprintf(buffer, "%d", kDefaultPort); - child = getItem(hwnd, IDC_MAIN_ADVANCED_PORT_EDIT); - SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); - - CString hostname = ARCH->getHostName(); - child = getItem(hwnd, IDC_MAIN_ADVANCED_NAME_EDIT); - SendMessage(child, WM_SETTEXT, 0, (LPARAM)hostname.c_str()); - + // debug level child = getItem(hwnd, IDC_MAIN_DEBUG); for (unsigned int i = 0; i < sizeof(s_debugName) / sizeof(s_debugName[0]); ++i) { @@ -794,19 +778,32 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) CConfig::CScreenOptions::const_iterator index; child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); index = info->m_options.find(kOptionHalfDuplexCapsLock); - if (index != info->m_options.end() && index->second != 0) { - SendMessage(child, BM_SETCHECK, BST_CHECKED, 0); - } - else { - SendMessage(child, BM_SETCHECK, BST_UNCHECKED, 0); - } + setItemChecked(child, (index != info->m_options.end() && + index->second != 0)); child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); index = info->m_options.find(kOptionHalfDuplexNumLock); - if (index != info->m_options.end() && index->second != 0) { - SendMessage(child, BM_SETCHECK, BST_CHECKED, 0); - } - else { - SendMessage(child, BM_SETCHECK, BST_UNCHECKED, 0); + setItemChecked(child, (index != info->m_options.end() && + index->second != 0)); + + // modifier options + for (UInt32 i = 0; i < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++i) { + child = getItem(hwnd, s_modifiers[i].m_ctrlID); + + // fill in options + for (UInt32 j = 0; j < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++j) { + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)s_modifiers[j].m_name); + } + + // choose current value + index = info->m_options.find(s_modifiers[i].m_optionID); + KeyModifierID id = s_modifiers[i].m_modifierID; + if (index != info->m_options.end()) { + id = index->second; + } + SendMessage(child, CB_SETCURSEL, id - baseModifier, 0); } return TRUE; @@ -883,20 +880,36 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) // save options child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); - if (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED) { + if (isItemChecked(child)) { info->m_options[kOptionHalfDuplexCapsLock] = 1; } else { info->m_options.erase(kOptionHalfDuplexCapsLock); } child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); - if (SendMessage(child, BM_GETCHECK, 0, 0) == BST_CHECKED) { + if (isItemChecked(child)) { info->m_options[kOptionHalfDuplexNumLock] = 1; } else { info->m_options.erase(kOptionHalfDuplexNumLock); } + // save modifier options + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + for (UInt32 i = 0; i < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++i) { + child = getItem(hwnd, s_modifiers[i].m_ctrlID); + KeyModifierID id = static_cast( + SendMessage(child, CB_GETCURSEL, 0, 0) + + baseModifier); + if (id != s_modifiers[i].m_modifierID) { + info->m_options[s_modifiers[i].m_optionID] = id; + } + else { + info->m_options.erase(s_modifiers[i].m_optionID); + } + } + // success EndDialog(hwnd, 1); info = NULL; @@ -1065,6 +1078,16 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) return 0; } break; + + case IDC_MAIN_OPTIONS: + s_globalOptions->doModal(); + enableSaveControls(hwnd); + break; + + case IDC_MAIN_ADVANCED: + s_advancedOptions->doModal(isClientChecked(hwnd)); + enableSaveControls(hwnd); + break; } default: @@ -1076,7 +1099,7 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) { - CArch arch; + CArch arch(instance); CLOG; CArgs args; @@ -1108,8 +1131,10 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) HWND m_mainWindow = CreateDialog(s_instance, MAKEINTRESOURCE(IDD_MAIN), 0, NULL); - // prep window + // prep windows initMainWindow(m_mainWindow); + s_globalOptions = new CGlobalOptions(m_mainWindow, &ARG->m_config); + s_advancedOptions = new CAdvancedOptions(m_mainWindow, &ARG->m_config); // show window ShowWindow(m_mainWindow, nCmdShow); diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp index 51096677..730a701c 100644 --- a/cmd/launcher/launcher.dsp +++ b/cmd/launcher/launcher.dsp @@ -95,10 +95,18 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CAdvancedOptions.cpp +# End Source File +# Begin Source File + SOURCE=.\CAutoStart.cpp # End Source File # Begin Source File +SOURCE=.\CGlobalOptions.cpp +# End Source File +# Begin Source File + SOURCE=.\launcher.cpp # End Source File # Begin Source File @@ -115,10 +123,18 @@ SOURCE=.\LaunchUtil.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CAdvancedOptions.h +# End Source File +# Begin Source File + SOURCE=.\CAutoStart.h # End Source File # Begin Source File +SOURCE=.\CGlobalOptions.h +# End Source File +# Begin Source File + SOURCE=.\LaunchUtil.h # End Source File # Begin Source File diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index 9b7acf49..6e1c1aba 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -40,11 +40,14 @@ #define IDS_SERVER_IS_CLIENT 36 #define IDS_ADD_SCREEN 37 #define IDS_EDIT_SCREEN 38 +#define IDS_INVALID_TIME 39 #define IDD_MAIN 101 #define IDD_ADD 102 #define IDD_WAIT 103 #define IDI_SYNERGY 104 #define IDD_AUTOSTART 105 +#define IDD_ADVANCED_OPTIONS 106 +#define IDD_GLOBAL_OPTIONS 107 #define IDC_MAIN_CLIENT_RADIO 1000 #define IDC_MAIN_SERVER_RADIO 1001 #define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1002 @@ -76,18 +79,31 @@ #define IDC_AUTOSTART_INSTALL_USER 1033 #define IDC_AUTOSTART_INSTALL_SYSTEM 1034 #define IDC_MAIN_AUTOSTART 1035 -#define IDC_MAIN_DEBUG 1036 +#define IDC_MAIN_OPTIONS 1036 #define IDC_ADD_HD_CAPS_CHECK 1037 +#define IDC_MAIN_ADVANCED 1037 #define IDC_ADD_HD_NUM_CHECK 1038 +#define IDC_ADVANCED_NAME_EDIT 1038 +#define IDC_ADVANCED_PORT_EDIT 1039 +#define IDC_MAIN_DEBUG 1040 +#define IDC_GLOBAL_DELAY_CHECK 1041 +#define IDC_GLOBAL_DELAY_TIME 1042 +#define IDC_GLOBAL_TWO_TAP_CHECK 1043 +#define IDC_ADD_MOD_SHIFT 1043 +#define IDC_GLOBAL_TWO_TAP_TIME 1044 +#define IDC_ADD_MOD_CTRL 1044 +#define IDC_ADD_MOD_ALT 1045 +#define IDC_ADD_MOD_META 1046 +#define IDC_ADD_MOD_SUPER 1047 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_RESOURCE_VALUE 108 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1038 +#define _APS_NEXT_CONTROL_VALUE 1044 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/cmd/synergyc/CClientTaskBarReceiver.cpp b/cmd/synergyc/CClientTaskBarReceiver.cpp new file mode 100644 index 00000000..9c60c5bf --- /dev/null +++ b/cmd/synergyc/CClientTaskBarReceiver.cpp @@ -0,0 +1,163 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientTaskBarReceiver.h" +#include "CClient.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CArch.h" + +// +// CClientTaskBarReceiver +// + +CClientTaskBarReceiver::CClientTaskBarReceiver() : + m_quit(NULL), + m_state(kNotRunning), + m_client(NULL) +{ + // create a job for getting notification when the client's + // status changes. + m_job = new TMethodJob(this, + &CClientTaskBarReceiver::statusChanged, NULL); +} + +CClientTaskBarReceiver::~CClientTaskBarReceiver() +{ + if (m_client != NULL) { + m_client->removeStatusJob(m_job); + } + delete m_job; + delete m_quit; +} + +void +CClientTaskBarReceiver::setClient(CClient* client) +{ + { + CLock lock(&m_mutex); + if (m_client != client) { + if (m_client != NULL) { + m_client->removeStatusJob(m_job); + } + m_client = client; + if (m_client != NULL) { + m_client->addStatusJob(m_job); + } + } + } + ARCH->updateReceiver(this); +} + +void +CClientTaskBarReceiver::setState(EState state) +{ + { + CLock lock(&m_mutex); + m_state = state; + } + ARCH->updateReceiver(this); +} + +void +CClientTaskBarReceiver::setQuitJob(IJob* job) +{ + CLock lock(&m_mutex); + delete m_quit; + m_quit = job; +} + +CClientTaskBarReceiver::EState +CClientTaskBarReceiver::getState() const +{ + return m_state; +} + +CClient* +CClientTaskBarReceiver::getClient() const +{ + return m_client; +} + +void +CClientTaskBarReceiver::lock() const +{ + m_mutex.lock(); +} + +void +CClientTaskBarReceiver::unlock() const +{ + m_mutex.unlock(); +} + +std::string +CClientTaskBarReceiver::getToolTip() const +{ + switch (m_state) { + case kNotRunning: + return "Synergy: Not running"; + + case kNotWorking: + return CString("Synergy: ") + m_errorMessage; + + case kNotConnected: + return "Synergy: Waiting for clients"; + + case kConnected: + return "Synergy: Connected"; + + default: + return ""; + } +} + +void +CClientTaskBarReceiver::quit() +{ + if (m_quit != NULL) { + m_quit->run(); + } +} + +void +CClientTaskBarReceiver::onStatusChanged() +{ + // do nothing +} + +void +CClientTaskBarReceiver::statusChanged(void*) +{ + // update our status + switch (m_client->getStatus(&m_errorMessage)) { + case CClient::kNotRunning: + setState(kNotRunning); + break; + + case CClient::kRunning: + setState(kConnected); + break; + + case CClient::kError: + setState(kNotWorking); + break; + + default: + break; + } + + // let subclasses have a go + onStatusChanged(); +} diff --git a/cmd/synergyc/CClientTaskBarReceiver.h b/cmd/synergyc/CClientTaskBarReceiver.h new file mode 100644 index 00000000..aa52b9d6 --- /dev/null +++ b/cmd/synergyc/CClientTaskBarReceiver.h @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTTASKBARRECEIVER_H +#define CCLIENTTASKBARRECEIVER_H + +#include "CMutex.h" +#include "CString.h" +#include "IArchTaskBarReceiver.h" + +class CClient; +class IJob; + +//! Implementation of IArchTaskBarReceiver for the synergy server +class CClientTaskBarReceiver : public IArchTaskBarReceiver { +public: + enum EState { + kNotRunning, + kNotWorking, + kNotConnected, + kConnected, + kMaxState + }; + + CClientTaskBarReceiver(); + virtual ~CClientTaskBarReceiver(); + + //! @name manipulators + //@{ + + //! Set server + /*! + Sets the server. The receiver will query state from this server. + */ + void setClient(CClient*); + + //! Set state + /*! + Sets the current server state. + */ + void setState(EState); + + //! Set the quit job that causes the server to quit + /*! + Set the job that causes the server to quit. + */ + void setQuitJob(IJob* adopted); + + //@} + //! @name accessors + //@{ + + //! Get state + /*! + Returns the current server state. The receiver is not locked + by this call; the caller must do the locking. + */ + EState getState() const; + + //! Get server + /*! + Returns the server set by \c setClient(). + */ + CClient* getClient() const; + + //@} + + // IArchTaskBarReceiver overrides + virtual void showStatus() = 0; + virtual void runMenu(int x, int y) = 0; + virtual void primaryAction() = 0; + virtual void lock() const; + virtual void unlock() const; + virtual const Icon getIcon() const = 0; + virtual std::string getToolTip() const; + +protected: + void quit(); + + //! Status change notification + /*! + Called when status changes. The default implementation does + nothing. + */ + virtual void onStatusChanged(); + +private: + void statusChanged(void*); + +private: + CMutex m_mutex; + IJob* m_quit; + EState m_state; + CClient* m_client; + IJob* m_job; + CString m_errorMessage; +}; + +#endif diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp new file mode 100644 index 00000000..f348a2a8 --- /dev/null +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -0,0 +1,280 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClientTaskBarReceiver.h" +#include "CClient.h" +#include "BasicTypes.h" +#include "CArch.h" +#include "CArchTaskBarWindows.h" +#include "resource.h" + +static const UINT g_stateToIconID[CMSWindowsClientTaskBarReceiver::kMaxState] = +{ + IDI_TASKBAR_NOT_RUNNING, + IDI_TASKBAR_NOT_WORKING, + IDI_TASKBAR_NOT_CONNECTED, + IDI_TASKBAR_CONNECTED +}; + +// +// CMSWindowsClientTaskBarReceiver +// + +CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver( + HINSTANCE appInstance) : + CClientTaskBarReceiver(), + m_appInstance(appInstance), + m_window(NULL) +{ + for (UInt32 i = 0; i < kMaxState; ++i) { + m_icon[i] = loadIcon(g_stateToIconID[i]); + } + m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR)); + + // don't create the window yet. we'll create it on demand. this + // has the side benefit of being created in the thread used for + // the task bar. that's good because it means the existence of + // the window won't prevent changing the main thread's desktop. + + // add ourself to the task bar + ARCH->addReceiver(this); +} + +CMSWindowsClientTaskBarReceiver::~CMSWindowsClientTaskBarReceiver() +{ + ARCH->removeReceiver(this); + for (UInt32 i = 0; i < kMaxState; ++i) { + deleteIcon(m_icon[i]); + } + DestroyMenu(m_menu); + destroyWindow(); +} + +void +CMSWindowsClientTaskBarReceiver::showStatus() +{ + // create the window + createWindow(); + + // lock self while getting status + lock(); + + // get the current status + std::string status = getToolTip(); + + // done getting status + unlock(); + + // update dialog + HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str()); + + if (!IsWindowVisible(m_window)) { + // position it by the mouse + POINT cursorPos; + GetCursorPos(&cursorPos); + RECT windowRect; + GetWindowRect(m_window, &windowRect); + int x = cursorPos.x; + int y = cursorPos.y; + int fw = GetSystemMetrics(SM_CXDLGFRAME); + int fh = GetSystemMetrics(SM_CYDLGFRAME); + int ww = windowRect.right - windowRect.left; + int wh = windowRect.bottom - windowRect.top; + int sw = GetSystemMetrics(SM_CXFULLSCREEN); + int sh = GetSystemMetrics(SM_CYFULLSCREEN); + if (fw < 1) { + fw = 1; + } + if (fh < 1) { + fh = 1; + } + if (x + ww - fw > sw) { + x -= ww - fw; + } + else { + x -= fw; + } + if (x < 0) { + x = 0; + } + if (y + wh - fh > sh) { + y -= wh - fh; + } + else { + y -= fh; + } + if (y < 0) { + y = 0; + } + SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh, + SWP_SHOWWINDOW); + } +} + +void +CMSWindowsClientTaskBarReceiver::runMenu(int x, int y) +{ + // do popup menu. we need a window to pass to TrackPopupMenu(). + // the SetForegroundWindow() and SendMessage() calls around + // TrackPopupMenu() are to get the menu to be dismissed when + // another window gets activated and are just one of those + // win32 weirdnesses. + createWindow(); + SetForegroundWindow(m_window); + HMENU menu = GetSubMenu(m_menu, 0); + SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE); + int n = TrackPopupMenu(menu, + TPM_NONOTIFY | + TPM_RETURNCMD | + TPM_LEFTBUTTON | + TPM_RIGHTBUTTON, + x, y, 0, m_window, NULL); + SendMessage(m_window, WM_NULL, 0, 0); + + // perform the requested operation + switch (n) { + case IDC_TASKBAR_STATUS: + showStatus(); + break; + + case IDC_TASKBAR_QUIT: + quit(); + break; + } +} + +void +CMSWindowsClientTaskBarReceiver::primaryAction() +{ + showStatus(); +} + +const IArchTaskBarReceiver::Icon +CMSWindowsClientTaskBarReceiver::getIcon() const +{ + return reinterpret_cast(m_icon[getState()]); +} + +void +CMSWindowsClientTaskBarReceiver::onStatusChanged() +{ + if (IsWindowVisible(m_window)) { + showStatus(); + } +} + +HICON +CMSWindowsClientTaskBarReceiver::loadIcon(UINT id) +{ + HANDLE icon = LoadImage(m_appInstance, + MAKEINTRESOURCE(id), + IMAGE_ICON, + 0, 0, + LR_DEFAULTCOLOR); + return reinterpret_cast(icon); +} + +void +CMSWindowsClientTaskBarReceiver::deleteIcon(HICON icon) +{ + if (icon != NULL) { + DestroyIcon(icon); + } +} + +void +CMSWindowsClientTaskBarReceiver::createWindow() +{ + // ignore if already created + if (m_window != NULL) { + return; + } + + // get the status dialog + m_window = CreateDialogParam(m_appInstance, + MAKEINTRESOURCE(IDD_TASKBAR_STATUS), + NULL, + &CMSWindowsClientTaskBarReceiver::staticDlgProc, + reinterpret_cast( + reinterpret_cast(this))); + + // window should appear on top of everything, including (especially) + // the task bar. + DWORD style = GetWindowLong(m_window, GWL_EXSTYLE); + style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; + SetWindowLong(m_window, GWL_EXSTYLE, style); + + // tell the task bar about this dialog + CArchTaskBarWindows::addDialog(m_window); +} + +void +CMSWindowsClientTaskBarReceiver::destroyWindow() +{ + if (m_window != NULL) { + CArchTaskBarWindows::removeDialog(m_window); + DestroyWindow(m_window); + m_window = NULL; + } +} + +BOOL +CMSWindowsClientTaskBarReceiver::dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM) +{ + switch (msg) { + case WM_INITDIALOG: + // use default focus + return TRUE; + + case WM_ACTIVATE: + // hide when another window is activated + if (LOWORD(wParam) == WA_INACTIVE) { + ShowWindow(hwnd, SW_HIDE); + } + break; + } + return FALSE; +} + +BOOL CALLBACK +CMSWindowsClientTaskBarReceiver::staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + // if msg is WM_INITDIALOG, extract the CMSWindowsClientTaskBarReceiver* + // and put it in the extra window data then forward the call. + CMSWindowsClientTaskBarReceiver* self = NULL; + if (msg == WM_INITDIALOG) { + self = reinterpret_cast( + reinterpret_cast(lParam)); + SetWindowLong(hwnd, GWL_USERDATA, lParam); + } + else { + // get the extra window data and forward the call + LONG data = GetWindowLong(hwnd, GWL_USERDATA); + if (data != 0) { + self = reinterpret_cast( + reinterpret_cast(data)); + } + } + + // forward the message + if (self != NULL) { + return self->dlgProc(hwnd, msg, wParam, lParam); + } + else { + return (msg == WM_INITDIALOG) ? TRUE : FALSE; + } +} diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h new file mode 100644 index 00000000..0fdb2cc5 --- /dev/null +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h @@ -0,0 +1,58 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIENTTASKBARRECEIVER_H +#define CMSWINDOWSCLIENTTASKBARRECEIVER_H + +#define WIN32_LEAN_AND_MEAN + +#include "CClientTaskBarReceiver.h" +#include + +//! Implementation of CClientTaskBarReceiver for Microsoft Windows +class CMSWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { +public: + CMSWindowsClientTaskBarReceiver(HINSTANCE); + virtual ~CMSWindowsClientTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + // CClientTaskBarReceiver overrides + virtual void onStatusChanged(); + +private: + HICON loadIcon(UINT); + void deleteIcon(HICON); + void createWindow(); + void destroyWindow(); + + BOOL dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + static BOOL CALLBACK + staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + +private: + HINSTANCE m_appInstance; + HWND m_window; + HMENU m_menu; + HICON m_icon[kMaxState]; +}; + +#endif diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp new file mode 100644 index 00000000..f60585ea --- /dev/null +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClientTaskBarReceiver.h" +#include "CArch.h" + +// +// CXWindowsClientTaskBarReceiver +// + +CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver() +{ + // add ourself to the task bar + ARCH->addReceiver(this); +} + +CXWindowsClientTaskBarReceiver::~CXWindowsClientTaskBarReceiver() +{ + ARCH->removeReceiver(this); +} + +void +CXWindowsClientTaskBarReceiver::showStatus() +{ + // do nothing +} + +void +CXWindowsClientTaskBarReceiver::runMenu(int, int) +{ + // do nothing +} + +void +CXWindowsClientTaskBarReceiver::primaryAction() +{ + // do nothing +} + +const IArchTaskBarReceiver::Icon +CXWindowsClientTaskBarReceiver::getIcon() const +{ + return NULL; +} + +void +CXWindowsClientTaskBarReceiver::onStatusChanged() +{ + // do nothing +} diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h new file mode 100644 index 00000000..e6028ce2 --- /dev/null +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h @@ -0,0 +1,37 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIENTTASKBARRECEIVER_H +#define CXWINDOWSCLIENTTASKBARRECEIVER_H + +#include "CClientTaskBarReceiver.h" + +//! Implementation of CClientTaskBarReceiver for X Windows +class CXWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { +public: + CXWindowsClientTaskBarReceiver(); + virtual ~CXWindowsClientTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + // CClientTaskBarReceiver overrides + virtual void onStatusChanged(); +}; + +#endif diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 9af3e12c..6b2510ce 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -15,19 +15,29 @@ NULL = DEPTH = ../.. VDEPTH = ./$(VPATH)/$(DEPTH) -EXTRA_DIST = \ - resource.h \ - synergyc.dsp \ - synergyc.ico \ - synergyc.rc \ +EXTRA_DIST = \ + CMSWindowsClientTaskBarReceiver.cpp \ + CMSWindowsClientTaskBarReceiver.h \ + resource.h \ + synergyc.dsp \ + synergyc.ico \ + synergyc.rc \ + tb_error.ico \ + tb_idle.ico \ + tb_run.ico \ + tb_wait.ico \ $(NULL) -MAINTAINERCLEANFILES = \ - Makefile.in \ +MAINTAINERCLEANFILES = \ + Makefile.in \ $(NULL) bin_PROGRAMS = synergyc synergyc_SOURCES = \ + CClientTaskBarReceiver.cpp \ + CClientTaskBarReceiver.h \ + CXWindowsClientTaskBarReceiver.cpp \ + CXWindowsClientTaskBarReceiver.h \ synergyc.cpp \ $(NULL) synergyc_LDADD = \ diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h index c5887fb2..d80c2389 100644 --- a/cmd/synergyc/resource.h +++ b/cmd/synergyc/resource.h @@ -4,6 +4,15 @@ // #define IDS_FAILED 1 #define IDI_SYNERGY 101 +#define IDI_TASKBAR_NOT_RUNNING 102 +#define IDI_TASKBAR_NOT_WORKING 103 +#define IDI_TASKBAR_NOT_CONNECTED 104 +#define IDI_TASKBAR_CONNECTED 105 +#define IDR_TASKBAR 107 +#define IDD_TASKBAR_STATUS 108 +#define IDC_TASKBAR_STATUS_STATUS 1000 +#define IDC_TASKBAR_QUIT 40003 +#define IDC_TASKBAR_STATUS 40004 // Next default values for new objects // diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index a16d3407..bc0125c4 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -25,6 +25,7 @@ #include "CMutex.h" #include "CThread.h" #include "XThread.h" +#include "CFunctionJob.h" #include "CLog.h" #include "LogOutputters.h" #include "CString.h" @@ -34,12 +35,15 @@ #define DAEMON_RUNNING(running_) #if WINDOWS_LIKE +#include "CMSWindowsScreen.h" #include "CMSWindowsSecondaryScreen.h" +#include "CMSWindowsClientTaskBarReceiver.h" #include "resource.h" #undef DAEMON_RUNNING #define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #elif UNIX_LIKE #include "CXWindowsSecondaryScreen.h" +#include "CXWindowsClientTaskBarReceiver.h" #endif // platform dependent name of a daemon @@ -112,11 +116,46 @@ CSecondaryScreenFactory::create(IScreenReceiver* receiver) } +//! CQuitJob +/*! +A job that cancels a given thread. +*/ +class CQuitJob : public IJob { +public: + CQuitJob(const CThread& thread); + ~CQuitJob(); + + // IJob overrides + virtual void run(); + +private: + CThread m_thread; +}; + +CQuitJob::CQuitJob(const CThread& thread) : + m_thread(thread) +{ + // do nothing +} + +CQuitJob::~CQuitJob() +{ + // do nothing +} + +void +CQuitJob::run() +{ + m_thread.cancel(); +} + + // // platform independent main // -static CClient* s_client = NULL; +static CClient* s_client = NULL; +static CClientTaskBarReceiver* s_taskBarReceiver = NULL; static int @@ -137,6 +176,7 @@ realMain(void) // open client try { + s_taskBarReceiver->setClient(s_client); s_client->open(); opened = true; @@ -160,11 +200,10 @@ realMain(void) DAEMON_RUNNING(false); \ locked = true; \ } \ - if (s_client != NULL) { \ - if (opened) { \ - s_client->close(); \ - } \ + if (opened) { \ + s_client->close(); \ } \ + s_taskBarReceiver->setClient(NULL); \ delete s_client; \ s_client = NULL; \ } while (false) @@ -205,6 +244,43 @@ realMain(void) return result; } +static +void +realMainEntry(void*) +{ + CThread::exit(reinterpret_cast(realMain())); +} + +static +int +runMainInThread(void) +{ + CThread appThread(new CFunctionJob(&realMainEntry)); + try { +#if WINDOWS_LIKE + MSG msg; + while (appThread.waitForEvent(-1.0) == CThread::kEvent) { + // check for a quit event + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + CThread::getCurrentThread().cancel(); + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +#else + appThread.wait(-1.0); +#endif + return reinterpret_cast(appThread.getResult()); + } + catch (XThread&) { + appThread.cancel(); + appThread.wait(-1.0); + throw; + } +} + // // command line parsing @@ -273,7 +349,7 @@ help() static bool -isArg(int argi, int argc, const char** argv, +isArg(int argi, int argc, const char* const* argv, const char* name1, const char* name2, int minRequiredParameters = 0) { @@ -294,7 +370,7 @@ isArg(int argi, int argc, const char** argv, static void -parse(int argc, const char** argv) +parse(int argc, const char* const* argv) { assert(ARG->m_pname != NULL); assert(argv != NULL); @@ -424,16 +500,6 @@ parse(int argc, const char** argv) } } -static -void -useSystemLog() -{ - // redirect log messages - ILogOutputter* logger = new CSystemLogOutputter; - logger->open(DAEMON_NAME); - CLOG->insert(new CStopLogOutputter); - CLOG->insert(logger); -} // // platform dependent entry points @@ -441,8 +507,6 @@ useSystemLog() #if WINDOWS_LIKE -#include "CMSWindowsScreen.h" - static bool s_hasImportantLogMessages = false; // @@ -494,7 +558,10 @@ static int daemonStartup(int argc, const char** argv) { - useSystemLog(); + CSystemLogger sysLogger(DAEMON_NAME); + + // have to cancel this thread to quit + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); // catch errors that would normally exit bye = &byeThrow; @@ -513,14 +580,62 @@ static int daemonStartup95(int, const char**) { - useSystemLog(); - return realMain(); + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); +} + +static +int +run(int argc, char** argv) +{ + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + + // parse command line + parse(argc, argv); + + // daemonize if requested + if (ARG->m_daemon) { + // start as a daemon + if (CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup95); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + else { + // cannot start a service from the command line so just + // run normally (except with log messages redirected). + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); + } + } + else { + // run + return runMainInThread(); + } } int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { - CArch arch; + CArch arch(instance); CLOG; CArgs args; @@ -533,52 +648,27 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // send PRINT and FATAL output to a message box CLOG->insert(new CMessageBoxOutputter); - // windows NT family starts services using no command line options. - // since i'm not sure how to tell the difference between that and - // a user providing no options we'll assume that if there are no - // arguments and we're on NT then we're being invoked as a service. - // users on NT can use `--daemon' or `--no-daemon' to force us out - // of the service code path. - if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - int result = kExitFailed; - try { - result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - } - delete CLOG; - return result; - } + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance); + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); - // parse command line - parse(__argc, const_cast(__argv)); - - // daemonize if requested int result; - if (ARG->m_daemon) { - // start as a daemon - if (CArchMiscWindows::isWindows95Family()) { - try { - result = ARCH->daemonize(DAEMON_NAME, &daemonStartup95); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - result = kExitFailed; - } - } - else { - // cannot start a service from the command line so just - // run normally (except with log messages redirected). - useSystemLog(); - result = realMain(); - } + try { + // run in foreground or as a daemon + result = run(__argc, __argv); } - else { - // run - result = realMain(); + catch (...) { + // note that we don't rethrow thread cancellation. we'll + // be exiting soon so it doesn't matter. what we'd like + // is for everything after this try/catch to be in a + // finally block. + result = kExitFailed; } + // done with task bar receiver + delete s_taskBarReceiver; + // let user examine any messages if we're running as a backend // by putting up a dialog box before exiting. if (ARG->m_backend && s_hasImportantLogMessages) { @@ -598,7 +688,7 @@ static int daemonStartup(int, const char**) { - useSystemLog(); + CSystemLogger sysLogger(DAEMON_NAME); return realMain(); } @@ -612,8 +702,13 @@ main(int argc, char** argv) // get program name ARG->m_pname = ARCH->getBasename(argv[0]); + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CXWindowsClientTaskBarReceiver; + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + // parse command line - parse(argc, const_cast(argv)); + parse(argc, argv); // daemonize if requested int result; @@ -630,6 +725,9 @@ main(int argc, char** argv) result = realMain(); } + // done with task bar receiver + delete s_taskBarReceiver; + return result; } diff --git a/cmd/synergyc/synergyc.dsp b/cmd/synergyc/synergyc.dsp index e99b0910..21842b62 100644 --- a/cmd/synergyc/synergyc.dsp +++ b/cmd/synergyc/synergyc.dsp @@ -94,6 +94,14 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CClientTaskBarReceiver.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClientTaskBarReceiver.cpp +# End Source File +# Begin Source File + SOURCE=.\synergyc.cpp # End Source File # Begin Source File @@ -106,6 +114,14 @@ SOURCE=.\synergyc.rc # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CClientTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClientTaskBarReceiver.h +# End Source File +# Begin Source File + SOURCE=.\resource.h # End Source File # End Group @@ -116,6 +132,22 @@ SOURCE=.\resource.h SOURCE=.\synergyc.ico # End Source File +# Begin Source File + +SOURCE=.\tb_error.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_idle.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_run.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_wait.ico +# End Source File # End Group # End Target # End Project diff --git a/cmd/synergyc/tb_error.ico b/cmd/synergyc/tb_error.ico new file mode 100644 index 0000000000000000000000000000000000000000..a304dc1815fa0ababb136f6f1ba13735d2fbb5a6 GIT binary patch literal 318 zcmaKmI}U?D3`8Fa1(GSKQd6eP(da0%M{>W753q;H0Z_=8bSPr%nek^=GIWY*nn*l4 zxTlLbnZ=G?3UTN^DWIg3pGj2`5%OH@N3!o(iHaPhoKX)@YIUo1XqN6xF32N$#zwk% rh~{<7+ME_FZfttD==auOF(2NnD_ToW$P?mr8eX*h4C50%ue=|=dPPd3 literal 0 HcmV?d00001 diff --git a/cmd/synergyc/tb_idle.ico b/cmd/synergyc/tb_idle.ico new file mode 100644 index 0000000000000000000000000000000000000000..3625257e4afe43f9ccdbbe3e6e2f541105871f3f GIT binary patch literal 318 zcmZurAr8YZ5OcLkQ&dtky23qgEVQs=fwR(&L?NM-@}RNK*GMJXU2NYux?F%xIgTSN z9vyHq70j804!syC2mhlP(7`-65vxQgMQRuOaj@@J2}-k!&TwR~h6-SLiTGZsnPlDg zL?YKMATu~=P8?F$VrI-~bI7i$=!5zVNDMQQ7gJ_kO3u&%zS>-Wn34tN}J+r=4z*gh`15R`a5?7jDz`v4(T BM~naf literal 0 HcmV?d00001 diff --git a/cmd/synergyc/tb_wait.ico b/cmd/synergyc/tb_wait.ico new file mode 100644 index 0000000000000000000000000000000000000000..ed56a5f0289dcd9ae1ca9d7b303ebb1b8dcedc97 GIT binary patch literal 318 zcmZ{fF%H8Z3`JiAQoA)%=ZqbZe1#k+TUJJn#Q}1N93eyZ^cxhZLu>p0Zy6f{4DxXt zaeEE$bQQcZ2?J3Kwec^?pdjZiVx`ntrG9X>;5addReceiver(this); +} + +CMSWindowsServerTaskBarReceiver::~CMSWindowsServerTaskBarReceiver() +{ + ARCH->removeReceiver(this); + for (UInt32 i = 0; i < kMaxState; ++i) { + deleteIcon(m_icon[i]); + } + DestroyMenu(m_menu); + destroyWindow(); +} + +void +CMSWindowsServerTaskBarReceiver::showStatus() +{ + // create the window + createWindow(); + + // lock self while getting status + lock(); + + // get the current status + std::string status = getToolTip(); + + // get the connect clients, if any + typedef std::vector CClientList; + CClientList clients; + CServer* server = getServer(); + if (server != NULL) { + server->getClients(clients); + } + + // done getting status + unlock(); + + // update dialog + HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str()); + child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS); + SendMessage(child, LB_RESETCONTENT, 0, 0); + for (CClientList::const_iterator index = clients.begin(); + index != clients.end(); ) { + const char* client = index->c_str(); + if (++index == clients.end()) { + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)client); + } + else { + SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)client); + } + } + + if (!IsWindowVisible(m_window)) { + // position it by the mouse + POINT cursorPos; + GetCursorPos(&cursorPos); + RECT windowRect; + GetWindowRect(m_window, &windowRect); + int x = cursorPos.x; + int y = cursorPos.y; + int fw = GetSystemMetrics(SM_CXDLGFRAME); + int fh = GetSystemMetrics(SM_CYDLGFRAME); + int ww = windowRect.right - windowRect.left; + int wh = windowRect.bottom - windowRect.top; + int sw = GetSystemMetrics(SM_CXFULLSCREEN); + int sh = GetSystemMetrics(SM_CYFULLSCREEN); + if (fw < 1) { + fw = 1; + } + if (fh < 1) { + fh = 1; + } + if (x + ww - fw > sw) { + x -= ww - fw; + } + else { + x -= fw; + } + if (x < 0) { + x = 0; + } + if (y + wh - fh > sh) { + y -= wh - fh; + } + else { + y -= fh; + } + if (y < 0) { + y = 0; + } + SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh, + SWP_SHOWWINDOW); + } +} + +void +CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) +{ + // do popup menu. we need a window to pass to TrackPopupMenu(). + // the SetForegroundWindow() and SendMessage() calls around + // TrackPopupMenu() are to get the menu to be dismissed when + // another window gets activated and are just one of those + // win32 weirdnesses. + createWindow(); + SetForegroundWindow(m_window); + HMENU menu = GetSubMenu(m_menu, 0); + SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE); + int n = TrackPopupMenu(menu, + TPM_NONOTIFY | + TPM_RETURNCMD | + TPM_LEFTBUTTON | + TPM_RIGHTBUTTON, + x, y, 0, m_window, NULL); + SendMessage(m_window, WM_NULL, 0, 0); + + // perform the requested operation + switch (n) { + case IDC_TASKBAR_STATUS: + showStatus(); + break; + + case IDC_TASKBAR_QUIT: + quit(); + break; + } +} + +void +CMSWindowsServerTaskBarReceiver::primaryAction() +{ + showStatus(); +} + +const IArchTaskBarReceiver::Icon +CMSWindowsServerTaskBarReceiver::getIcon() const +{ + return reinterpret_cast(m_icon[getState()]); +} + +void +CMSWindowsServerTaskBarReceiver::onStatusChanged() +{ + if (IsWindowVisible(m_window)) { + showStatus(); + } +} + +HICON +CMSWindowsServerTaskBarReceiver::loadIcon(UINT id) +{ + HANDLE icon = LoadImage(m_appInstance, + MAKEINTRESOURCE(id), + IMAGE_ICON, + 0, 0, + LR_DEFAULTCOLOR); + return reinterpret_cast(icon); +} + +void +CMSWindowsServerTaskBarReceiver::deleteIcon(HICON icon) +{ + if (icon != NULL) { + DestroyIcon(icon); + } +} + +void +CMSWindowsServerTaskBarReceiver::createWindow() +{ + // ignore if already created + if (m_window != NULL) { + return; + } + + // get the status dialog + m_window = CreateDialogParam(m_appInstance, + MAKEINTRESOURCE(IDD_TASKBAR_STATUS), + NULL, + &CMSWindowsServerTaskBarReceiver::staticDlgProc, + reinterpret_cast( + reinterpret_cast(this))); + + // window should appear on top of everything, including (especially) + // the task bar. + DWORD style = GetWindowLong(m_window, GWL_EXSTYLE); + style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; + SetWindowLong(m_window, GWL_EXSTYLE, style); + + // tell the task bar about this dialog + CArchTaskBarWindows::addDialog(m_window); +} + +void +CMSWindowsServerTaskBarReceiver::destroyWindow() +{ + if (m_window != NULL) { + CArchTaskBarWindows::removeDialog(m_window); + DestroyWindow(m_window); + m_window = NULL; + } +} + +BOOL +CMSWindowsServerTaskBarReceiver::dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM) +{ + switch (msg) { + case WM_INITDIALOG: + // use default focus + return TRUE; + + case WM_ACTIVATE: + // hide when another window is activated + if (LOWORD(wParam) == WA_INACTIVE) { + ShowWindow(hwnd, SW_HIDE); + } + break; + } + return FALSE; +} + +BOOL CALLBACK +CMSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + // if msg is WM_INITDIALOG, extract the CMSWindowsServerTaskBarReceiver* + // and put it in the extra window data then forward the call. + CMSWindowsServerTaskBarReceiver* self = NULL; + if (msg == WM_INITDIALOG) { + self = reinterpret_cast( + reinterpret_cast(lParam)); + SetWindowLong(hwnd, GWL_USERDATA, lParam); + } + else { + // get the extra window data and forward the call + LONG data = GetWindowLong(hwnd, GWL_USERDATA); + if (data != 0) { + self = reinterpret_cast( + reinterpret_cast(data)); + } + } + + // forward the message + if (self != NULL) { + return self->dlgProc(hwnd, msg, wParam, lParam); + } + else { + return (msg == WM_INITDIALOG) ? TRUE : FALSE; + } +} diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h new file mode 100644 index 00000000..372aad0b --- /dev/null +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h @@ -0,0 +1,58 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSSERVERTASKBARRECEIVER_H +#define CMSWINDOWSSERVERTASKBARRECEIVER_H + +#define WIN32_LEAN_AND_MEAN + +#include "CServerTaskBarReceiver.h" +#include + +//! Implementation of CServerTaskBarReceiver for Microsoft Windows +class CMSWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { +public: + CMSWindowsServerTaskBarReceiver(HINSTANCE); + virtual ~CMSWindowsServerTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + // CServerTaskBarReceiver overrides + virtual void onStatusChanged(); + +private: + HICON loadIcon(UINT); + void deleteIcon(HICON); + void createWindow(); + void destroyWindow(); + + BOOL dlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + static BOOL CALLBACK + staticDlgProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam); + +private: + HINSTANCE m_appInstance; + HWND m_window; + HMENU m_menu; + HICON m_icon[kMaxState]; +}; + +#endif diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp new file mode 100644 index 00000000..8349a13c --- /dev/null +++ b/cmd/synergys/CServerTaskBarReceiver.cpp @@ -0,0 +1,171 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CServerTaskBarReceiver.h" +#include "CServer.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CArch.h" + +// +// CServerTaskBarReceiver +// + +CServerTaskBarReceiver::CServerTaskBarReceiver() : + m_quit(NULL), + m_state(kNotRunning), + m_server(NULL) +{ + // create a job for getting notification when the server's + // status changes. + m_job = new TMethodJob(this, + &CServerTaskBarReceiver::statusChanged, NULL); +} + +CServerTaskBarReceiver::~CServerTaskBarReceiver() +{ + if (m_server != NULL) { + m_server->removeStatusJob(m_job); + } + delete m_job; + delete m_quit; +} + +void +CServerTaskBarReceiver::setServer(CServer* server) +{ + { + CLock lock(&m_mutex); + if (m_server != server) { + if (m_server != NULL) { + m_server->removeStatusJob(m_job); + } + m_server = server; + if (m_server != NULL) { + m_server->addStatusJob(m_job); + } + } + } + ARCH->updateReceiver(this); +} + +void +CServerTaskBarReceiver::setState(EState state) +{ + { + CLock lock(&m_mutex); + m_state = state; + } + ARCH->updateReceiver(this); +} + +void +CServerTaskBarReceiver::setQuitJob(IJob* job) +{ + CLock lock(&m_mutex); + delete m_quit; + m_quit = job; +} + +CServerTaskBarReceiver::EState +CServerTaskBarReceiver::getState() const +{ + return m_state; +} + +CServer* +CServerTaskBarReceiver::getServer() const +{ + return m_server; +} + +void +CServerTaskBarReceiver::lock() const +{ + m_mutex.lock(); +} + +void +CServerTaskBarReceiver::unlock() const +{ + m_mutex.unlock(); +} + +std::string +CServerTaskBarReceiver::getToolTip() const +{ + switch (m_state) { + case kNotRunning: + return "Synergy: Not running"; + + case kNotWorking: + return CString("Synergy: ") + m_errorMessage; + + case kNotConnected: + return "Synergy: Waiting for clients"; + + case kConnected: + return "Synergy: Connected"; + + default: + return ""; + } +} + +void +CServerTaskBarReceiver::quit() +{ + if (m_quit != NULL) { + m_quit->run(); + } +} + +void +CServerTaskBarReceiver::onStatusChanged() +{ + // do nothing +} + +void +CServerTaskBarReceiver::statusChanged(void*) +{ + // update our status + switch (m_server->getStatus(&m_errorMessage)) { + case CServer::kNotRunning: + setState(kNotRunning); + break; + + case CServer::kRunning: + if (m_server->getNumClients() > 1) + setState(kConnected); + else + setState(kNotConnected); + break; + + case CServer::kServerNameUnknown: + m_errorMessage = "Server name is not in configuration"; + setState(kNotWorking); + break; + + case CServer::kError: + setState(kNotWorking); + break; + + default: + break; + } + + // let subclasses have a go + onStatusChanged(); +} diff --git a/cmd/synergys/CServerTaskBarReceiver.h b/cmd/synergys/CServerTaskBarReceiver.h new file mode 100644 index 00000000..0591a097 --- /dev/null +++ b/cmd/synergys/CServerTaskBarReceiver.h @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSERVERTASKBARRECEIVER_H +#define CSERVERTASKBARRECEIVER_H + +#include "CMutex.h" +#include "CString.h" +#include "IArchTaskBarReceiver.h" + +class CServer; +class IJob; + +//! Implementation of IArchTaskBarReceiver for the synergy server +class CServerTaskBarReceiver : public IArchTaskBarReceiver { +public: + enum EState { + kNotRunning, + kNotWorking, + kNotConnected, + kConnected, + kMaxState + }; + + CServerTaskBarReceiver(); + virtual ~CServerTaskBarReceiver(); + + //! @name manipulators + //@{ + + //! Set server + /*! + Sets the server. The receiver will query state from this server. + */ + void setServer(CServer*); + + //! Set state + /*! + Sets the current server state. + */ + void setState(EState); + + //! Set the quit job that causes the server to quit + /*! + Set the job that causes the server to quit. + */ + void setQuitJob(IJob* adopted); + + //@} + //! @name accessors + //@{ + + //! Get state + /*! + Returns the current server state. The receiver is not locked + by this call; the caller must do the locking. + */ + EState getState() const; + + //! Get server + /*! + Returns the server set by \c setServer(). + */ + CServer* getServer() const; + + //@} + + // IArchTaskBarReceiver overrides + virtual void showStatus() = 0; + virtual void runMenu(int x, int y) = 0; + virtual void primaryAction() = 0; + virtual void lock() const; + virtual void unlock() const; + virtual const Icon getIcon() const = 0; + virtual std::string getToolTip() const; + +protected: + void quit(); + + //! Status change notification + /*! + Called when status changes. The default implementation does + nothing. + */ + virtual void onStatusChanged(); + +private: + void statusChanged(void*); + +private: + CMutex m_mutex; + IJob* m_quit; + EState m_state; + CServer* m_server; + IJob* m_job; + CString m_errorMessage; +}; + +#endif diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp new file mode 100644 index 00000000..20118732 --- /dev/null +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsServerTaskBarReceiver.h" +#include "CArch.h" + +// +// CXWindowsServerTaskBarReceiver +// + +CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver() +{ + // add ourself to the task bar + ARCH->addReceiver(this); +} + +CXWindowsServerTaskBarReceiver::~CXWindowsServerTaskBarReceiver() +{ + ARCH->removeReceiver(this); +} + +void +CXWindowsServerTaskBarReceiver::showStatus() +{ + // do nothing +} + +void +CXWindowsServerTaskBarReceiver::runMenu(int, int) +{ + // do nothing +} + +void +CXWindowsServerTaskBarReceiver::primaryAction() +{ + // do nothing +} + +const IArchTaskBarReceiver::Icon +CXWindowsServerTaskBarReceiver::getIcon() const +{ + return NULL; +} + +void +CXWindowsServerTaskBarReceiver::onStatusChanged() +{ + // do nothing +} diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.h b/cmd/synergys/CXWindowsServerTaskBarReceiver.h new file mode 100644 index 00000000..a05a4307 --- /dev/null +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.h @@ -0,0 +1,37 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSSERVERTASKBARRECEIVER_H +#define CXWINDOWSSERVERTASKBARRECEIVER_H + +#include "CServerTaskBarReceiver.h" + +//! Implementation of CServerTaskBarReceiver for X Windows +class CXWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { +public: + CXWindowsServerTaskBarReceiver(); + virtual ~CXWindowsServerTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; + +protected: + // CServerTaskBarReceiver overrides + virtual void onStatusChanged(); +}; + +#endif diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index 8da55b4b..b3384ce6 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -15,19 +15,29 @@ NULL = DEPTH = ../.. VDEPTH = ./$(VPATH)/$(DEPTH) -EXTRA_DIST = \ - resource.h \ - synergys.ico \ - synergys.dsp \ - synergys.rc \ +EXTRA_DIST = \ + CMSWindowsServerTaskBarReceiver.cpp \ + CMSWindowsServerTaskBarReceiver.h \ + resource.h \ + synergys.ico \ + synergys.dsp \ + synergys.rc \ + tb_error.ico \ + tb_idle.ico \ + tb_run.ico \ + tb_wait.ico \ $(NULL) -MAINTAINERCLEANFILES = \ - Makefile.in \ +MAINTAINERCLEANFILES = \ + Makefile.in \ $(NULL) bin_PROGRAMS = synergys synergys_SOURCES = \ + CServerTaskBarReceiver.cpp \ + CServerTaskBarReceiver.h \ + CXWindowsServerTaskBarReceiver.cpp \ + CXWindowsServerTaskBarReceiver.h \ synergys.cpp \ $(NULL) synergys_LDADD = \ diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index 6bfc7486..2e716a64 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -4,14 +4,24 @@ // #define IDS_FAILED 1 #define IDI_SYNERGY 101 +#define IDI_TASKBAR_NOT_RUNNING 102 +#define IDI_TASKBAR_NOT_WORKING 103 +#define IDI_TASKBAR_NOT_CONNECTED 104 +#define IDI_TASKBAR_CONNECTED 105 +#define IDR_TASKBAR 107 +#define IDD_TASKBAR_STATUS 108 +#define IDC_TASKBAR_STATUS_STATUS 1000 +#define IDC_TASKBAR_STATUS_CLIENTS 1001 +#define IDC_TASKBAR_QUIT 40003 +#define IDC_TASKBAR_STATUS 40004 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_RESOURCE_VALUE 109 +#define _APS_NEXT_COMMAND_VALUE 40005 +#define _APS_NEXT_CONTROL_VALUE 1003 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 0e676d0a..f2c4924e 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -24,6 +24,7 @@ #include "CMutex.h" #include "CThread.h" #include "XThread.h" +#include "CFunctionJob.h" #include "CLog.h" #include "LogOutputters.h" #include "CArch.h" @@ -33,12 +34,15 @@ #define DAEMON_RUNNING(running_) #if WINDOWS_LIKE +#include "CMSWindowsScreen.h" #include "CMSWindowsPrimaryScreen.h" +#include "CMSWindowsServerTaskBarReceiver.h" #include "resource.h" #undef DAEMON_RUNNING #define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #elif UNIX_LIKE #include "CXWindowsPrimaryScreen.h" +#include "CXWindowsServerTaskBarReceiver.h" #endif // platform dependent name of a daemon @@ -123,11 +127,46 @@ CPrimaryScreenFactory::create(IScreenReceiver* receiver, } +//! CQuitJob +/*! +A job that cancels a given thread. +*/ +class CQuitJob : public IJob { +public: + CQuitJob(const CThread& thread); + ~CQuitJob(); + + // IJob overrides + virtual void run(); + +private: + CThread m_thread; +}; + +CQuitJob::CQuitJob(const CThread& thread) : + m_thread(thread) +{ + // do nothing +} + +CQuitJob::~CQuitJob() +{ + // do nothing +} + +void +CQuitJob::run() +{ + m_thread.cancel(); +} + + // // platform independent main // -static CServer* s_server = NULL; +static CServer* s_server = NULL; +static CServerTaskBarReceiver* s_taskBarReceiver = NULL; static int @@ -168,6 +207,7 @@ realMain(void) // open server try { + s_taskBarReceiver->setServer(s_server); s_server->open(); opened = true; @@ -177,18 +217,17 @@ realMain(void) s_server->mainLoop(); // clean up -#define FINALLY do { \ - if (!locked) { \ - DAEMON_RUNNING(false); \ - locked = true; \ - } \ - if (s_server != NULL) { \ - if (opened) { \ - s_server->close(); \ - } \ - } \ - delete s_server; \ - s_server = NULL; \ +#define FINALLY do { \ + if (!locked) { \ + DAEMON_RUNNING(false); \ + locked = true; \ + } \ + if (opened) { \ + s_server->close(); \ + } \ + s_taskBarReceiver->setServer(NULL); \ + delete s_server; \ + s_server = NULL; \ } while (false) FINALLY; } @@ -227,6 +266,41 @@ realMain(void) return result; } +static +void +realMainEntry(void*) +{ + CThread::exit(reinterpret_cast(realMain())); +} + +static +int +runMainInThread(void) +{ + CThread appThread(new CFunctionJob(&realMainEntry)); + try { +#if WINDOWS_LIKE + MSG msg; + while (appThread.waitForEvent(-1.0) == CThread::kEvent) { + // check for a quit event + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + CThread::getCurrentThread().cancel(); + } + } + } +#else + appThread.wait(-1.0); +#endif + return reinterpret_cast(appThread.getResult()); + } + catch (XThread&) { + appThread.cancel(); + appThread.wait(-1.0); + throw; + } +} + // // command line parsing @@ -329,7 +403,7 @@ PLATFORM_EXTRA static bool -isArg(int argi, int argc, const char** argv, +isArg(int argi, int argc, const char* const* argv, const char* name1, const char* name2, int minRequiredParameters = 0) { @@ -350,7 +424,7 @@ isArg(int argi, int argc, const char** argv, static void -parse(int argc, const char** argv) +parse(int argc, const char* const* argv) { assert(ARG->m_pname != NULL); assert(argv != NULL); @@ -553,16 +627,6 @@ loadConfig() } } -static -void -useSystemLog() -{ - // redirect log messages - ILogOutputter* logger = new CSystemLogOutputter; - logger->open(DAEMON_NAME); - CLOG->insert(new CStopLogOutputter); - CLOG->insert(logger); -} // // platform dependent entry points @@ -570,8 +634,6 @@ useSystemLog() #if WINDOWS_LIKE -#include "CMSWindowsScreen.h" - static bool s_hasImportantLogMessages = false; // @@ -623,7 +685,10 @@ static int daemonStartup(int argc, const char** argv) { - useSystemLog(); + CSystemLogger sysLogger(DAEMON_NAME); + + // have to cancel this thread to quit + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); // catch errors that would normally exit bye = &byeThrow; @@ -645,14 +710,65 @@ static int daemonStartup95(int, const char**) { - useSystemLog(); - return realMain(); + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); +} + +static +int +run(int argc, char** argv) +{ + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + + // parse command line + parse(argc, argv); + + // load configuration + loadConfig(); + + // daemonize if requested + if (ARG->m_daemon) { + // start as a daemon + if (CArchMiscWindows::isWindows95Family()) { + try { + return ARCH->daemonize(DAEMON_NAME, &daemonStartup95); + } + catch (XArchDaemon& e) { + LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); + } + return kExitFailed; + } + else { + // cannot start a service from the command line so just + // run normally (except with log messages redirected). + CSystemLogger sysLogger(DAEMON_NAME); + return runMainInThread(); + } + } + else { + // run + return runMainInThread(); + } } int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { - CArch arch; + CArch arch(instance); CLOG; CArgs args; @@ -665,55 +781,27 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // send PRINT and FATAL output to a message box CLOG->insert(new CMessageBoxOutputter); - // windows NT family starts services using no command line options. - // since i'm not sure how to tell the difference between that and - // a user providing no options we'll assume that if there are no - // arguments and we're on NT then we're being invoked as a service. - // users on NT can use `--daemon' or `--no-daemon' to force us out - // of the service code path. - if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - int result = kExitFailed; - try { - result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - } - delete CLOG; - return result; - } + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance); + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); - // parse command line - parse(__argc, const_cast(__argv)); - - // load configuration - loadConfig(); - - // daemonize if requested int result; - if (ARG->m_daemon) { - // start as a daemon - if (CArchMiscWindows::isWindows95Family()) { - try { - result = ARCH->daemonize(DAEMON_NAME, &daemonStartup95); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - result = kExitFailed; - } - } - else { - // cannot start a service from the command line so just - // run normally (except with log messages redirected). - useSystemLog(); - result = realMain(); - } + try { + // run in foreground or as a daemon + result = run(__argc, __argv); } - else { - // run - result = realMain(); + catch (...) { + // note that we don't rethrow thread cancellation. we'll + // be exiting soon so it doesn't matter. what we'd like + // is for everything after this try/catch to be in a + // finally block. + result = kExitFailed; } + // done with task bar receiver + delete s_taskBarReceiver; + // let user examine any messages if we're running as a backend // by putting up a dialog box before exiting. if (ARG->m_backend && s_hasImportantLogMessages) { @@ -733,7 +821,7 @@ static int daemonStartup(int, const char**) { - useSystemLog(); + CSystemLogger sysLogger(DAEMON_NAME); return realMain(); } @@ -747,8 +835,13 @@ main(int argc, char** argv) // get program name ARG->m_pname = ARCH->getBasename(argv[0]); + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = new CXWindowsServerTaskBarReceiver; + s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); + // parse command line - parse(argc, const_cast(argv)); + parse(argc, argv); // load configuration loadConfig(); @@ -768,6 +861,9 @@ main(int argc, char** argv) result = realMain(); } + // done with task bar receiver + delete s_taskBarReceiver; + return result; } diff --git a/cmd/synergys/synergys.dsp b/cmd/synergys/synergys.dsp index 39accfa4..cbc3e626 100644 --- a/cmd/synergys/synergys.dsp +++ b/cmd/synergys/synergys.dsp @@ -94,6 +94,14 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CMSWindowsServerTaskBarReceiver.cpp +# End Source File +# Begin Source File + +SOURCE=.\CServerTaskBarReceiver.cpp +# End Source File +# Begin Source File + SOURCE=.\synergys.cpp # End Source File # Begin Source File @@ -106,6 +114,14 @@ SOURCE=.\synergys.rc # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CMSWindowsServerTaskBarReceiver.h +# End Source File +# Begin Source File + +SOURCE=.\CServerTaskBarReceiver.h +# End Source File +# Begin Source File + SOURCE=.\resource.h # End Source File # End Group @@ -116,6 +132,22 @@ SOURCE=.\resource.h SOURCE=.\synergys.ico # End Source File +# Begin Source File + +SOURCE=.\tb_error.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_idle.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_run.ico +# End Source File +# Begin Source File + +SOURCE=.\tb_wait.ico +# End Source File # End Group # End Target # End Project diff --git a/cmd/synergys/tb_error.ico b/cmd/synergys/tb_error.ico new file mode 100644 index 0000000000000000000000000000000000000000..a304dc1815fa0ababb136f6f1ba13735d2fbb5a6 GIT binary patch literal 318 zcmaKmI}U?D3`8Fa1(GSKQd6eP(da0%M{>W753q;H0Z_=8bSPr%nek^=GIWY*nn*l4 zxTlLbnZ=G?3UTN^DWIg3pGj2`5%OH@N3!o(iHaPhoKX)@YIUo1XqN6xF32N$#zwk% rh~{<7+ME_FZfttD==auOF(2NnD_ToW$P?mr8eX*h4C50%ue=|=dPPd3 literal 0 HcmV?d00001 diff --git a/cmd/synergys/tb_idle.ico b/cmd/synergys/tb_idle.ico new file mode 100644 index 0000000000000000000000000000000000000000..3625257e4afe43f9ccdbbe3e6e2f541105871f3f GIT binary patch literal 318 zcmZurAr8YZ5OcLkQ&dtky23qgEVQs=fwR(&L?NM-@}RNK*GMJXU2NYux?F%xIgTSN z9vyHq70j804!syC2mhlP(7`-65vxQgMQRuOaj@@J2}-k!&TwR~h6-SLiTGZsnPlDg zL?YKMATu~=P8?F$VrI-~bI7i$=!5zVNDMQQ7gJ_kO3u&%zS>-Wn34tN}J+r=4z*gh`15R`a5?7jDz`v4(T BM~naf literal 0 HcmV?d00001 diff --git a/cmd/synergys/tb_wait.ico b/cmd/synergys/tb_wait.ico new file mode 100644 index 0000000000000000000000000000000000000000..ed56a5f0289dcd9ae1ca9d7b303ebb1b8dcedc97 GIT binary patch literal 318 zcmZ{fF%H8Z3`JiAQoA)%=ZqbZe1#k+TUJJn#Q}1N93eyZ^cxhZLu>p0Zy6f{4DxXt zaeEE$bQQcZ2?J3Kwec^?pdjZiVx`ntrG9X>;5wait(thread, timeout); } -bool -CArch::waitForEvent(double timeout) +IArchMultithread::EWaitResult +CArch::waitForEvent(CArchThread thread, double timeout) { - return m_mt->waitForEvent(timeout); + return m_mt->waitForEvent(thread, timeout); } bool @@ -577,6 +586,24 @@ CArch::getWideCharEncoding() return m_string->getWideCharEncoding(); } +void +CArch::addReceiver(IArchTaskBarReceiver* receiver) +{ + m_taskbar->addReceiver(receiver); +} + +void +CArch::removeReceiver(IArchTaskBarReceiver* receiver) +{ + m_taskbar->removeReceiver(receiver); +} + +void +CArch::updateReceiver(IArchTaskBarReceiver* receiver) +{ + m_taskbar->updateReceiver(receiver); +} + double CArch::time() { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index e459658f..61315b63 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -23,6 +23,7 @@ #include "IArchNetwork.h" #include "IArchSleep.h" #include "IArchString.h" +#include "IArchTaskBar.h" #include "IArchTime.h" /*! @@ -31,7 +32,7 @@ This macro evaluates to the singleton CArch object. */ #define ARCH (CArch::getInstance()) -#define ARCH_ARGS +#define ARCH_ARGS void //! Delegating mplementation of architecture dependent interfaces /*! @@ -51,9 +52,10 @@ class CArch : public IArchConsole, public IArchNetwork, public IArchSleep, public IArchString, + public IArchTaskBar, public IArchTime { public: - CArch(ARCH_ARGS); + CArch(ARCH_ARGS* args = NULL); ~CArch(); // @@ -114,7 +116,7 @@ public: virtual void setPriorityOfThread(CArchThread, int n); virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); - virtual bool waitForEvent(double timeout); + virtual EWaitResult waitForEvent(CArchThread, double timeout); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); @@ -164,6 +166,11 @@ public: virtual EWideCharEncoding getWideCharEncoding(); + // IArchTaskBar + virtual void addReceiver(IArchTaskBarReceiver*); + virtual void removeReceiver(IArchTaskBarReceiver*); + virtual void updateReceiver(IArchTaskBarReceiver*); + // IArchTime overrides virtual double time(); @@ -178,6 +185,7 @@ private: IArchNetwork* m_net; IArchSleep* m_sleep; IArchString* m_string; + IArchTaskBar* m_taskbar; IArchTime* m_time; }; diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp index c7afb95c..a89a42f4 100644 --- a/lib/arch/CArchConsoleWindows.cpp +++ b/lib/arch/CArchConsoleWindows.cpp @@ -13,6 +13,7 @@ */ #include "CArchConsoleWindows.h" +#include "IArchMultithread.h" #include "CArch.h" #include @@ -20,12 +21,12 @@ // CArchConsoleWindows // -DWORD CArchConsoleWindows::s_thread = 0; +CArchThread CArchConsoleWindows::s_thread = 0; CArchConsoleWindows::CArchConsoleWindows() : m_output(NULL) { - s_thread = GetCurrentThreadId(); + s_thread = ARCH->newCurrentThread(); m_mutex = ARCH->newMutex(); } @@ -33,6 +34,7 @@ CArchConsoleWindows::CArchConsoleWindows() : CArchConsoleWindows::~CArchConsoleWindows() { ARCH->closeMutex(m_mutex); + ARCH->closeThread(s_thread); } void @@ -101,7 +103,7 @@ CArchConsoleWindows::getNewlineForConsole() BOOL WINAPI CArchConsoleWindows::signalHandler(DWORD) { - // terminate cleanly and skip remaining handlers - PostThreadMessage(s_thread, WM_QUIT, 0, 0); + // terminate thread and skip remaining handlers + ARCH->cancelThread(s_thread); return TRUE; } diff --git a/lib/arch/CArchConsoleWindows.h b/lib/arch/CArchConsoleWindows.h index e51f6616..bf9f0a32 100644 --- a/lib/arch/CArchConsoleWindows.h +++ b/lib/arch/CArchConsoleWindows.h @@ -39,7 +39,7 @@ private: static BOOL WINAPI signalHandler(DWORD); private: - static DWORD s_thread; + static CArchThread s_thread; CArchMutex m_mutex; HANDLE m_output; diff --git a/lib/arch/CArchImpl.cpp b/lib/arch/CArchImpl.cpp index c4017122..9e57d0a7 100644 --- a/lib/arch/CArchImpl.cpp +++ b/lib/arch/CArchImpl.cpp @@ -25,6 +25,7 @@ # include "CArchNetworkWinsock.cpp" # include "CArchSleepWindows.cpp" # include "CArchStringWindows.cpp" +# include "CArchTaskBarWindows.cpp" # include "CArchTimeWindows.cpp" # include "XArchWindows.cpp" #elif UNIX_LIKE @@ -38,6 +39,7 @@ # include "CArchNetworkBSD.cpp" # include "CArchSleepUnix.cpp" # include "CArchStringUnix.cpp" +# include "CArchTaskBarXWindows.cpp" # include "CArchTimeUnix.cpp" # include "XArchUnix.cpp" #endif diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 8fdd9dde..bef806bf 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -517,11 +517,11 @@ CArchMultithreadPosix::wait(CArchThread target, double timeout) } } -bool -CArchMultithreadPosix::waitForEvent(double /*timeout*/) +IArchMultithread::EWaitResult +CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/) { // not implemented - return false; + return kTimeout; } bool diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h index 07e2f1d9..d62e2d4d 100644 --- a/lib/arch/CArchMultithreadPosix.h +++ b/lib/arch/CArchMultithreadPosix.h @@ -55,7 +55,7 @@ public: virtual void setPriorityOfThread(CArchThread, int n); virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); - virtual bool waitForEvent(double timeout); + virtual EWaitResult waitForEvent(CArchThread, double timeout); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index 315a3979..c847e0ed 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -453,6 +453,89 @@ CArchMultithreadWindows::wait(CArchThread target, double timeout) } } +IArchMultithread::EWaitResult +CArchMultithreadWindows::waitForEvent(CArchThread target, double timeout) +{ + // find current thread. ref the target so it can't go away while + // we're watching it. + lockMutex(m_threadMutex); + CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); + assert(self != NULL); + if (target != NULL) { + refThread(target); + } + unlockMutex(m_threadMutex); + + // see if we've been cancelled before checking if any events + // are pending. + DWORD result = WaitForSingleObject(self->m_cancel, 0); + if (result == WAIT_OBJECT_0) { + if (target != NULL) { + closeThread(target); + } + testCancelThreadImpl(self); + } + + // check if messages are available first. if we don't do this then + // MsgWaitForMultipleObjects() will block even if the queue isn't + // empty if the messages in the queue were there before the last + // call to GetMessage()/PeekMessage(). + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + return kEvent; + } + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for this thread to be cancelled or for the target thread to + // terminate. + DWORD n = (target == NULL || target == self) ? 1 : 2; + HANDLE handles[2]; + handles[0] = self->m_cancel; + handles[1] = (n == 2) ? target->m_exit : NULL; + result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT); + + // cancel takes priority + if (result != WAIT_OBJECT_0 + 0 && + WaitForSingleObject(handles[0], 0) == WAIT_OBJECT_0) { + result = WAIT_OBJECT_0 + 0; + } + + // release target + if (target != NULL) { + closeThread(target); + } + + // handle result + switch (result) { + case WAIT_OBJECT_0 + 0: + // this thread was cancelled. does not return. + testCancelThreadImpl(self); + + case WAIT_OBJECT_0 + 1: + // target thread terminated + if (n == 2) { + return kExit; + } + // fall through + + case WAIT_OBJECT_0 + 2: + // message is available + return kEvent; + + default: + // timeout or error + return kTimeout; + } +} + +/* bool CArchMultithreadWindows::waitForEvent(double timeout) { @@ -499,6 +582,7 @@ CArchMultithreadWindows::waitForEvent(double timeout) return false; } } +*/ bool CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2) diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h index e5003c1a..2a8d5462 100644 --- a/lib/arch/CArchMultithreadWindows.h +++ b/lib/arch/CArchMultithreadWindows.h @@ -69,7 +69,7 @@ public: virtual void setPriorityOfThread(CArchThread, int n); virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); - virtual bool waitForEvent(double timeout); + virtual EWaitResult waitForEvent(CArchThread, double timeout); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); diff --git a/lib/arch/CArchTaskBarWindows.cpp b/lib/arch/CArchTaskBarWindows.cpp new file mode 100644 index 00000000..987ac6c0 --- /dev/null +++ b/lib/arch/CArchTaskBarWindows.cpp @@ -0,0 +1,518 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchTaskBarWindows.h" +#include "IArchTaskBarReceiver.h" +#include "CArch.h" +#include "XArch.h" +#include +#include + +static const UINT kAddReceiver = WM_USER + 10; +static const UINT kRemoveReceiver = WM_USER + 11; +static const UINT kUpdateReceiver = WM_USER + 12; +static const UINT kNotifyReceiver = WM_USER + 13; +static const UINT kFirstReceiverID = WM_USER + 14; + +// +// CArchTaskBarWindows +// + +CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL; +HINSTANCE CArchTaskBarWindows::s_appInstance = NULL; + +CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) : + m_nextID(kFirstReceiverID) +{ + // save the singleton instance + s_instance = this; + + // save app instance + s_appInstance = reinterpret_cast(appInstance); + + // we need a mutex + m_mutex = ARCH->newMutex(); + + // and a condition variable which uses the above mutex + m_ready = false; + m_condVar = ARCH->newCondVar(); + + // we're going to want to get a result from the thread we're + // about to create to know if it initialized successfully. + // so we lock the condition variable. + ARCH->lockMutex(m_mutex); + + // open a window and run an event loop in a separate thread. + // this has to happen in a separate thread because if we + // create a window on the current desktop with the current + // thread then the current thread won't be able to switch + // desktops if it needs to. + m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this); + + // wait for child thread + while (!m_ready) { + ARCH->waitCondVar(m_condVar, m_mutex, -1.0); + } + + // ready + ARCH->unlockMutex(m_mutex); +} + +CArchTaskBarWindows::~CArchTaskBarWindows() +{ + if (m_thread != NULL) { + ARCH->cancelThread(m_thread); + ARCH->wait(m_thread, -1.0); + ARCH->closeThread(m_thread); + } + ARCH->closeCondVar(m_condVar); + ARCH->closeMutex(m_mutex); + s_instance = NULL; +} + +void +CArchTaskBarWindows::addDialog(HWND hwnd) +{ + // add dialog to added dialogs list + ARCH->lockMutex(s_instance->m_mutex); + s_instance->m_addedDialogs.insert(std::make_pair(hwnd, true)); + ARCH->unlockMutex(s_instance->m_mutex); +} + +void +CArchTaskBarWindows::removeDialog(HWND hwnd) +{ + // mark dialog as removed + ARCH->lockMutex(s_instance->m_mutex); + CDialogs::iterator index = s_instance->m_dialogs.find(hwnd); + if (index != s_instance->m_dialogs.end()) { + index->second = false; + } + s_instance->m_addedDialogs.erase(hwnd); + ARCH->unlockMutex(s_instance->m_mutex); +} + +void +CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver) +{ + // ignore bogus receiver + if (receiver == NULL) { + return; + } + + // add receiver if necessary + CReceiverToInfoMap::iterator index = m_receivers.find(receiver); + if (index == m_receivers.end()) { + // add it, creating a new message ID for it + CReceiverInfo info; + info.m_id = getNextID(); + index = m_receivers.insert(std::make_pair(receiver, info)).first; + + // add ID to receiver mapping + m_idTable.insert(std::make_pair(info.m_id, index)); + } + + // add receiver + PostMessage(m_hwnd, kAddReceiver, index->second.m_id, 0); +} + +void +CArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver) +{ + // find receiver + CReceiverToInfoMap::iterator index = m_receivers.find(receiver); + if (index == m_receivers.end()) { + return; + } + + // remove icon. wait for this to finish before returning. + SendMessage(m_hwnd, kRemoveReceiver, index->second.m_id, 0); + + // recycle the ID + recycleID(index->second.m_id); + + // discard + m_idTable.erase(index->second.m_id); + m_receivers.erase(index); +} + +void +CArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver) +{ + // find receiver + CReceiverToInfoMap::const_iterator index = m_receivers.find(receiver); + if (index == m_receivers.end()) { + return; + } + + // update icon and tool tip + PostMessage(m_hwnd, kUpdateReceiver, index->second.m_id, 0); +} + +UINT +CArchTaskBarWindows::getNextID() +{ + if (m_oldIDs.empty()) { + return m_nextID++; + } + UINT id = m_oldIDs.back(); + m_oldIDs.pop_back(); + return id; +} + +void +CArchTaskBarWindows::recycleID(UINT id) +{ + m_oldIDs.push_back(id); +} + +void +CArchTaskBarWindows::addIcon(UINT id) +{ + ARCH->lockMutex(m_mutex); + CIDToReceiverMap::const_iterator index = m_idTable.find(id); + if (index != m_idTable.end()) { + modifyIconNoLock(index->second, NIM_ADD); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::removeIcon(UINT id) +{ + ARCH->lockMutex(m_mutex); + removeIconNoLock(id); + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::updateIcon(UINT id) +{ + ARCH->lockMutex(m_mutex); + CIDToReceiverMap::const_iterator index = m_idTable.find(id); + if (index != m_idTable.end()) { + modifyIconNoLock(index->second, NIM_MODIFY); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::addAllIcons() +{ + ARCH->lockMutex(m_mutex); + for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); + index != m_receivers.end(); ++index) { + modifyIconNoLock(index, NIM_ADD); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::removeAllIcons() +{ + ARCH->lockMutex(m_mutex); + for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); + index != m_receivers.end(); ++index) { + removeIconNoLock(index->second.m_id); + } + ARCH->unlockMutex(m_mutex); +} + +void +CArchTaskBarWindows::modifyIconNoLock( + CReceiverToInfoMap::const_iterator index, DWORD taskBarMessage) +{ + // get receiver + UINT id = index->second.m_id; + IArchTaskBarReceiver* receiver = index->first; + + // lock receiver so icon and tool tip are guaranteed to be consistent + receiver->lock(); + + // get icon data + HICON icon = reinterpret_cast( + const_cast(receiver->getIcon())); + + // get tool tip + std::string toolTip = receiver->getToolTip(); + + // done querying + receiver->unlock(); + + // prepare to add icon + NOTIFYICONDATA data; + data.cbSize = sizeof(NOTIFYICONDATA); + data.hWnd = m_hwnd; + data.uID = id; + data.uFlags = NIF_MESSAGE; + data.uCallbackMessage = kNotifyReceiver; + data.hIcon = icon; + if (icon != NULL) { + data.uFlags |= NIF_ICON; + } + if (!toolTip.empty()) { + strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip)); + data.szTip[sizeof(data.szTip) - 1] = '\0'; + data.uFlags |= NIF_TIP; + } + else { + data.szTip[0] = '\0'; + } + + // add icon + if (Shell_NotifyIcon(taskBarMessage, &data) == 0) { + // failed + } +} + +void +CArchTaskBarWindows::removeIconNoLock(UINT id) +{ + NOTIFYICONDATA data; + data.cbSize = sizeof(NOTIFYICONDATA); + data.hWnd = m_hwnd; + data.uID = id; + if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) { + // failed + } +} + +void +CArchTaskBarWindows::handleIconMessage( + IArchTaskBarReceiver* receiver, LPARAM lParam) +{ + // process message + switch (lParam) { + case WM_LBUTTONDOWN: + receiver->showStatus(); + break; + + case WM_LBUTTONDBLCLK: + receiver->primaryAction(); + break; + + case WM_RBUTTONUP: { + POINT p; + GetCursorPos(&p); + receiver->runMenu(p.x, p.y); + break; + } + + case WM_MOUSEMOVE: + // currently unused + break; + + default: + // unused + break; + } +} + +bool +CArchTaskBarWindows::processDialogs(MSG* msg) +{ + // only one thread can be in this method on any particular object + // at any given time. that's not a problem since only our event + // loop calls this method and there's just one of those. + + ARCH->lockMutex(m_mutex); + + // remove removed dialogs + m_dialogs.erase(false); + + // merge added dialogs into the dialog list + for (CDialogs::const_iterator index = m_addedDialogs.begin(); + index != m_addedDialogs.end(); ++index) { + m_dialogs.insert(std::make_pair(index->first, index->second)); + } + m_addedDialogs.clear(); + + ARCH->unlockMutex(m_mutex); + + // check message against all dialogs until one handles it. + // note that we don't hold a lock while checking because + // the message is processed and may make calls to this + // object. that's okay because addDialog() and + // removeDialog() don't change the map itself (just the + // values of some elements). + ARCH->lockMutex(m_mutex); + for (CDialogs::const_iterator index = m_dialogs.begin(); + index != m_dialogs.end(); ++index) { + if (index->second) { + ARCH->unlockMutex(m_mutex); + if (IsDialogMessage(index->first, msg)) { + return true; + } + ARCH->lockMutex(m_mutex); + } + } + ARCH->unlockMutex(m_mutex); + + return false; +} + +LRESULT +CArchTaskBarWindows::wndProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case kNotifyReceiver: { + // lookup receiver + CIDToReceiverMap::const_iterator index = m_idTable.find(wParam); + if (index != m_idTable.end()) { + IArchTaskBarReceiver* receiver = index->second->first; + handleIconMessage(receiver, lParam); + return 0; + } + break; + } + + case kAddReceiver: + addIcon(wParam); + break; + + case kRemoveReceiver: + removeIcon(wParam); + break; + + case kUpdateReceiver: + updateIcon(wParam); + break; + + default: + if (msg == m_taskBarRestart) { + // task bar was recreated so re-add our icons + addAllIcons(); + } + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +LRESULT CALLBACK +CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + // if msg is WM_NCCREATE, extract the CArchTaskBarWindows* and put + // it in the extra window data then forward the call. + CArchTaskBarWindows* self = NULL; + if (msg == WM_NCCREATE) { + CREATESTRUCT* createInfo; + createInfo = reinterpret_cast(lParam); + self = reinterpret_cast( + createInfo->lpCreateParams); + SetWindowLong(hwnd, 0, reinterpret_cast(self)); + } + else { + // get the extra window data and forward the call + LONG data = GetWindowLong(hwnd, 0); + if (data != 0) { + self = reinterpret_cast( + reinterpret_cast(data)); + } + } + + // forward the message + if (self != NULL) { + return self->wndProc(hwnd, msg, wParam, lParam); + } + else { + return DefWindowProc(hwnd, msg, wParam, lParam); + } +} + +void +CArchTaskBarWindows::threadMainLoop() +{ + // register the task bar restart message + m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_NOCLOSE; + classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); + classInfo.hInstance = s_appInstance; + classInfo.hIcon = NULL; + classInfo.hCursor = NULL; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = TEXT("SynergyTaskBar"); + classInfo.hIconSm = NULL; + ATOM windowClass = RegisterClassEx(&classInfo); + + // create window + m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, + reinterpret_cast(windowClass), + TEXT("Synergy Task Bar"), + WS_POPUP, + 0, 0, 1, 1, + NULL, + NULL, + s_appInstance, + reinterpret_cast(this)); + + // signal ready + ARCH->lockMutex(m_mutex); + m_ready = true; + ARCH->broadcastCondVar(m_condVar); + ARCH->unlockMutex(m_mutex); + + // handle failure + if (m_hwnd == NULL) { + UnregisterClass((LPCTSTR)windowClass, s_appInstance); + return; + } + + try { + // main loop + MSG msg; + for (;;) { + // wait for message + if (ARCH->waitForEvent(NULL, -1.0) != IArchMultithread::kEvent) { + continue; + } + + // peek for message and remove it. we don't GetMessage() + // because we should never block here, only in waitForEvent(). + if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + continue; + } + + // check message against dialogs + if (!processDialogs(&msg)) { + // process message + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + catch (XThread&) { + // clean up + removeAllIcons(); + DestroyWindow(m_hwnd); + UnregisterClass((LPCTSTR)windowClass, s_appInstance); + throw; + } +} + +void* +CArchTaskBarWindows::threadEntry(void* self) +{ + reinterpret_cast(self)->threadMainLoop(); + return NULL; +} diff --git a/lib/arch/CArchTaskBarWindows.h b/lib/arch/CArchTaskBarWindows.h new file mode 100644 index 00000000..67e9af17 --- /dev/null +++ b/lib/arch/CArchTaskBarWindows.h @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTASKBARWINDOWS_H +#define CARCHTASKBARWINDOWS_H + +#define WIN32_LEAN_AND_MEAN + +#include "IArchTaskBar.h" +#include "IArchMultithread.h" +#include "stdmap.h" +#include "stdvector.h" +#include + +#define ARCH_TASKBAR CArchTaskBarWindows + +//! Win32 implementation of IArchTaskBar +class CArchTaskBarWindows : public IArchTaskBar { +public: + CArchTaskBarWindows(void*); + virtual ~CArchTaskBarWindows(); + + //! Add a dialog window + /*! + Tell the task bar event loop about a dialog. Win32 annoyingly + requires messages destined for modeless dialog boxes to be + dispatched differently than other messages. + */ + static void addDialog(HWND); + + //! Remove a dialog window + /*! + Remove a dialog window added via \c addDialog(). + */ + static void removeDialog(HWND); + + // IArchTaskBar overrides + virtual void addReceiver(IArchTaskBarReceiver*); + virtual void removeReceiver(IArchTaskBarReceiver*); + virtual void updateReceiver(IArchTaskBarReceiver*); + +private: + class CReceiverInfo { + public: + UINT m_id; + }; + + typedef std::map CReceiverToInfoMap; + typedef std::map CIDToReceiverMap; + typedef std::vector CIDStack; + typedef std::map CDialogs; + + UINT getNextID(); + void recycleID(UINT); + + void addIcon(UINT); + void removeIcon(UINT); + void updateIcon(UINT); + void addAllIcons(); + void removeAllIcons(); + void modifyIconNoLock(CReceiverToInfoMap::const_iterator, + DWORD taskBarMessage); + void removeIconNoLock(UINT id); + void handleIconMessage(IArchTaskBarReceiver*, LPARAM); + + bool processDialogs(MSG*); + LRESULT wndProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK + staticWndProc(HWND, UINT, WPARAM, LPARAM); + void threadMainLoop(); + static void* threadEntry(void*); + +private: + static CArchTaskBarWindows* s_instance; + static HINSTANCE s_appInstance; + + // multithread data + CArchMutex m_mutex; + CArchCond m_condVar; + bool m_ready; + int m_result; + CArchThread m_thread; + + // child thread data + HWND m_hwnd; + UINT m_taskBarRestart; + + // shared data + CReceiverToInfoMap m_receivers; + CIDToReceiverMap m_idTable; + CIDStack m_oldIDs; + UINT m_nextID; + + // dialogs + CDialogs m_dialogs; + CDialogs m_addedDialogs; +}; + +#endif diff --git a/lib/arch/CArchTaskBarXWindows.cpp b/lib/arch/CArchTaskBarXWindows.cpp new file mode 100644 index 00000000..6934f271 --- /dev/null +++ b/lib/arch/CArchTaskBarXWindows.cpp @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchTaskBarXWindows.h" + +// +// CArchTaskBarXWindows +// + +CArchTaskBarXWindows::CArchTaskBarXWindows(void*) +{ + // do nothing +} + +CArchTaskBarXWindows::~CArchTaskBarXWindows() +{ + // do nothing +} + +void +CArchTaskBarXWindows::addReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} + +void +CArchTaskBarXWindows::removeReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} + +void +CArchTaskBarXWindows::updateReceiver(IArchTaskBarReceiver* /*receiver*/) +{ + // do nothing +} diff --git a/lib/arch/CArchTaskBarXWindows.h b/lib/arch/CArchTaskBarXWindows.h new file mode 100644 index 00000000..abf28012 --- /dev/null +++ b/lib/arch/CArchTaskBarXWindows.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHTASKBARXWINDOWS_H +#define CARCHTASKBARXWINDOWS_H + +#include "IArchTaskBar.h" + +#define ARCH_TASKBAR CArchTaskBarXWindows + +//! X11 implementation of IArchTaskBar +class CArchTaskBarXWindows : public IArchTaskBar { +public: + CArchTaskBarXWindows(void*); + virtual ~CArchTaskBarXWindows(); + + // IArchTaskBar overrides + virtual void addReceiver(IArchTaskBarReceiver*); + virtual void removeReceiver(IArchTaskBarReceiver*); + virtual void updateReceiver(IArchTaskBarReceiver*); +}; + +#endif diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index 05abd6be..ee376771 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -67,6 +67,13 @@ synergy. Each architecture must implement this interface. */ class IArchMultithread : public IInterface { public: + //! Result of waitForEvent() + enum EWaitResult { + kEvent, //!< An event is pending + kExit, //!< Thread exited + kTimeout //!< Wait timed out + }; + //! Type of thread entry point typedef void* (*ThreadFunc)(void*); //! Type of thread identifier @@ -102,10 +109,12 @@ public: //! Wait on a condition variable /*! - Waiting on a conditation variable for up to \c timeout seconds. + Wait on a conditation variable for up to \c timeout seconds. If \c timeout is < 0 then there is no timeout. The mutex must be locked when this method is called. The mutex is unlocked - during the wait and locked again before returning. + during the wait and locked again before returning. Returns + true if the condition variable was signalled and false on + timeout. (Cancellation point) */ @@ -206,14 +215,18 @@ public: //! Wait for a user event /*! - Waits for up to \c timeout seconds for a pending user event. - Returns true if an event occurred, false otherwise. + Waits for up to \c timeout seconds for a pending user event or + \c thread to exit (normally or by cancellation). Waits forever + if \c timeout < 0. Returns kEvent if an event occurred, kExit + if \c thread exited, or kTimeout if the timeout expired. If + \c thread is NULL then it doesn't wait for any thread to exit + and it will not return kExit. This method is not required by all platforms. (Cancellation point) */ - virtual bool waitForEvent(double timeout) = 0; + virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0; //! Compare threads /*! diff --git a/lib/arch/IArchTaskBar.h b/lib/arch/IArchTaskBar.h new file mode 100644 index 00000000..2cd20ded --- /dev/null +++ b/lib/arch/IArchTaskBar.h @@ -0,0 +1,63 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHTASKBAR_H +#define IARCHTASKBAR_H + +#include "IInterface.h" + +class IArchTaskBarReceiver; + +//! Interface for architecture dependent task bar control +/*! +This interface defines the task bar icon operations required +by synergy. Each architecture must implement this interface +though each operation can be a no-op. +*/ +class IArchTaskBar : public IInterface { +public: + // Event data is architecture dependent + typedef void* Event; + + //! @name manipulators + //@{ + + //! Add a receiver + /*! + Add a receiver object to be notified of user and application + events. This should be called before other methods. When + the receiver is added to the task bar, its icon appears on + the task bar. + */ + virtual void addReceiver(IArchTaskBarReceiver*) = 0; + + //! Remove a receiver + /*! + Remove a receiver object from the task bar. This removes the + icon from the task bar. + */ + virtual void removeReceiver(IArchTaskBarReceiver*) = 0; + + //! Update a receiver + /*! + Updates the display of the receiver on the task bar. This + should be called when the receiver appearance may have changed + (e.g. it's icon or tool tip has changed). + */ + virtual void updateReceiver(IArchTaskBarReceiver*) = 0; + + //@} +}; + +#endif diff --git a/lib/arch/IArchTaskBarReceiver.h b/lib/arch/IArchTaskBarReceiver.h new file mode 100644 index 00000000..917f2fbf --- /dev/null +++ b/lib/arch/IArchTaskBarReceiver.h @@ -0,0 +1,90 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHTASKBARRECEIVER_H +#define IARCHTASKBARRECEIVER_H + +#include "IInterface.h" +#include "stdstring.h" + +//! Interface for architecture dependent task bar event handling +/*! +This interface defines the task bar icon event handlers required +by synergy. Each architecture must implement this interface +though each operation can be a no-op. +*/ +class IArchTaskBarReceiver : public IInterface { +public: + // Icon data is architecture dependent + typedef void* Icon; + + //! @name manipulators + //@{ + + //! Show status window + /*! + Open a window displaying current status. This should return + immediately without waiting for the window to be closed. + */ + virtual void showStatus() = 0; + + //! Popup menu + /*! + Popup a menu of operations at or around \c x,y and perform the + chosen operation. + */ + virtual void runMenu(int x, int y) = 0; + + //! Perform primary action + /*! + Perform the primary (default) action. + */ + virtual void primaryAction() = 0; + + //@} + //! @name accessors + //@{ + + //! Lock receiver + /*! + Locks the receiver from changing state. The receiver should be + locked when querying it's state to ensure consistent results. + Each call to \c lock() must have a matching \c unlock() and + locks cannot be nested. + */ + virtual void lock() const = 0; + + //! Unlock receiver + virtual void unlock() const = 0; + + //! Get icon + /*! + Returns the icon to display in the task bar. The interface + to set the icon is left to subclasses. Getting and setting + the icon must be thread safe. + */ + virtual const Icon getIcon() const = 0; + + //! Get tooltip + /*! + Returns the tool tip to display in the task bar. The interface + to set the tooltip is left to sublclasses. Getting and setting + the icon must be thread safe. + */ + virtual std::string getToolTip() const = 0; + + //@} +}; + +#endif diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am index 008e2169..2b32473c 100644 --- a/lib/arch/Makefile.am +++ b/lib/arch/Makefile.am @@ -26,6 +26,7 @@ EXTRA_DIST = \ CArchNetworkWinsock.cpp \ CArchSleepWindows.cpp \ CArchStringWindows.cpp \ + CArchTaskBarWindows.cpp \ CArchTimeWindows.cpp \ XArchWindows.cpp \ CArchConsoleWindows.h \ @@ -37,6 +38,7 @@ EXTRA_DIST = \ CArchNetworkWinsock.h \ CArchSleepWindows.h \ CArchStringWindows.h \ + CArchTaskBarWindows.h \ CArchTimeWindows.h \ XArchWindows.h \ $(NULL) @@ -59,6 +61,8 @@ libarch_a_SOURCES = \ IArchNetwork.h \ IArchSleep.h \ IArchString.h \ + IArchTaskBar.h \ + IArchTaskBarReceiver.h \ IArchTime.h \ XArch.h \ $(NULL) @@ -72,6 +76,7 @@ EXTRA_libarch_a_SOURCES = \ CArchNetworkBSD.cpp \ CArchSleepUnix.cpp \ CArchStringUnix.cpp \ + CArchTaskBarXWindows.cpp \ CArchTimeUnix.cpp \ CMultibyte.cpp \ CMultibyteOS.cpp \ @@ -87,6 +92,7 @@ EXTRA_libarch_a_SOURCES = \ CArchNetworkBSD.h \ CArchSleepUnix.h \ CArchStringUnix.h \ + CArchTaskBarXWindows.h \ CArchTimeUnix.h \ XArchUnix.h \ $(NULL) diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp index cb82663a..126a11e3 100644 --- a/lib/arch/arch.dsp +++ b/lib/arch/arch.dsp @@ -115,6 +115,10 @@ SOURCE=.\CArchDaemonWindows.h # End Source File # Begin Source File +SOURCE=.\CArchFileWindows.h +# End Source File +# Begin Source File + SOURCE=.\CArchImpl.h # End Source File # Begin Source File @@ -143,6 +147,10 @@ SOURCE=.\CArchStringWindows.h # End Source File # Begin Source File +SOURCE=.\CArchTaskBarWindows.h +# End Source File +# Begin Source File + SOURCE=.\CArchTimeWindows.h # End Source File # Begin Source File @@ -151,6 +159,14 @@ SOURCE=.\IArchConsole.h # End Source File # Begin Source File +SOURCE=.\IArchDaemon.h +# End Source File +# Begin Source File + +SOURCE=.\IArchFile.h +# End Source File +# Begin Source File + SOURCE=.\IArchLog.h # End Source File # Begin Source File @@ -171,6 +187,14 @@ SOURCE=.\IArchString.h # End Source File # Begin Source File +SOURCE=.\IArchTaskBar.h +# End Source File +# Begin Source File + +SOURCE=.\IArchTaskBarReceiver.h +# End Source File +# Begin Source File + SOURCE=.\IArchTime.h # End Source File # Begin Source File @@ -232,6 +256,11 @@ SOURCE=.\CArchStringWindows.cpp # End Source File # Begin Source File +SOURCE=.\CArchTaskBarWindows.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + SOURCE=.\CArchTimeWindows.cpp # PROP Exclude_From_Build 1 # End Source File diff --git a/lib/base/CJobList.cpp b/lib/base/CJobList.cpp new file mode 100644 index 00000000..37358890 --- /dev/null +++ b/lib/base/CJobList.cpp @@ -0,0 +1,113 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CJobList.h" +#include "IJob.h" +#include "CArch.h" + +// +// CJobList +// + +CJobList::CJobList() +{ + m_mutex = ARCH->newMutex(); +} + +CJobList::~CJobList() +{ + ARCH->closeMutex(m_mutex); +} + +void +CJobList::addJob(IJob* job) +{ + // ignore bogus job + if (job == NULL) { + return; + } + + // make a temporary list with the job. later we'll splice this + // list into our jobs list. since splice() never throws we + // don't have to try/catch to unlock the mutex. + CJobs tmpList; + tmpList.push_front(job); + + // add job to list + ARCH->lockMutex(m_mutex); + m_jobs.splice(m_jobs.begin(), tmpList); + ARCH->unlockMutex(m_mutex); +} + +void +CJobList::removeJob(IJob* job) +{ + if (job != NULL) { + ARCH->lockMutex(m_mutex); + m_jobs.remove(job); + ARCH->unlockMutex(m_mutex); + } +} + +void +CJobList::runJobs() const +{ + // this is a little tricky. to allow any number of threads to + // traverse the list while jobs are added and removed while + // not holding the mutex when running a job (so other threads + // or the running job can add and remove jobs), we insert a + // new element into the list. this element has a NULL job and + // is a "safe place" while we traverse the list. the safe place + // is inserted at the start of the list (with the mutex locked) + // then, for each job, we lock the mutex and move the safe place + // past the next job, unlock the mutex, run the job and repeat + // until there are no more jobs. the safe place will not be + // removed by any other thread and is after every job that has + // been run and before every job that still needs to run. when + // all the jobs have been run we remove the safe place. + + // add the safe place + CJobs tmpList; + tmpList.push_front(NULL); + ARCH->lockMutex(m_mutex); + m_jobs.splice(m_jobs.begin(), tmpList); + CJobs::iterator safePlace = m_jobs.begin(); + + // find the next non-NULL job (NULL jobs are safe places) + CJobs::iterator next = safePlace; + while (next != m_jobs.end() && *next == NULL) + ++next; + while (next != m_jobs.end()) { + // found a job. run it without holding a lock. note the + // race condition here: we release the lock, allowing + // removeJob() to remove this job before we run the job. + // therefore the caller cannot safely destroy the job + // until all runJobs() complete. + IJob* job = *next; + ++next; + m_jobs.splice(next, m_jobs, safePlace); + ARCH->unlockMutex(m_mutex); + job->run(); + + // next real job + ARCH->lockMutex(m_mutex); + next = safePlace; + while (next != m_jobs.end() && *next == NULL) + ++next; + } + + // remove the safe place + m_jobs.erase(safePlace); + ARCH->unlockMutex(m_mutex); +} diff --git a/lib/base/CJobList.h b/lib/base/CJobList.h new file mode 100644 index 00000000..780d19da --- /dev/null +++ b/lib/base/CJobList.h @@ -0,0 +1,72 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CJOBLIST_H +#define CJOBLIST_H + +#include "IArchMultithread.h" +#include "stdlist.h" + +class IJob; + +class CJobList { +public: + CJobList(); + ~CJobList(); + + //! @name manipulators + //@{ + + //! Add a job + /*! + Add a job to the list. The client keeps ownership of the job. + Jobs can be safely added while \c runJobs() is executing. + */ + void addJob(IJob*); + + //! Remove a job + /*! + Remove a job from the list. The client keeps ownership of the job. + Jobs can be safely removed while \c runJobs() is executing. + */ + void removeJob(IJob*); + + //@} + //! @name accessors + //@{ + + //! Run all jobs + /*! + Run all jobs in the list. Any number of threads can call + \c runJobs() at once. Jobs can be added and removed while + \c runJobs() is executing in the same or another thread. + Any job added after \c runJobs() starts will not be run + by that call to runJobs(). Destroying a removed job + while \c runJobs() is executing is not safe unless the + removed completed before \c runJobs() started. + */ + void runJobs() const; + + //@} + +private: + typedef std::list CJobs; + typedef CJobs::iterator iterator; + + CArchMutex m_mutex; + mutable CJobs m_jobs; +}; + +#endif + diff --git a/lib/base/LogOutputters.cpp b/lib/base/LogOutputters.cpp index 0f0050ce..f8e0b65e 100644 --- a/lib/base/LogOutputters.cpp +++ b/lib/base/LogOutputters.cpp @@ -156,3 +156,26 @@ CSystemLogOutputter::getNewline() const { return ""; } + + +// +// CSystemLogger +// + +CSystemLogger::CSystemLogger(const char* title) +{ + // redirect log messages + m_syslog = new CSystemLogOutputter; + m_stop = new CStopLogOutputter; + m_syslog->open(title); + CLOG->insert(m_stop); + CLOG->insert(m_syslog); +} + +CSystemLogger::~CSystemLogger() +{ + CLOG->remove(m_syslog); + CLOG->remove(m_stop); + delete m_stop; + delete m_syslog; +} diff --git a/lib/base/LogOutputters.h b/lib/base/LogOutputters.h index 74d18814..21602438 100644 --- a/lib/base/LogOutputters.h +++ b/lib/base/LogOutputters.h @@ -68,4 +68,22 @@ public: virtual const char* getNewline() const; }; +//! Write log to system log only +/*! +Creating an object of this type inserts a CStopLogOutputter followed +by a CSystemLogOutputter into CLog. The destructor removes those +outputters. Add one of these to any scope that needs to write to +the system log (only) and restore the old outputters when exiting +the scope. +*/ +class CSystemLogger { +public: + CSystemLogger(const char* title); + ~CSystemLogger(); + +private: + ILogOutputter* m_syslog; + ILogOutputter* m_stop; +}; + #endif diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index a89cf120..f750ba7f 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -26,6 +26,7 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libbase.a libbase_a_SOURCES = \ CFunctionJob.cpp \ + CJobList.cpp \ CLog.cpp \ CStopwatch.cpp \ CStringUtil.cpp \ @@ -33,6 +34,7 @@ libbase_a_SOURCES = \ LogOutputters.cpp \ XBase.cpp \ CFunctionJob.h \ + CJobList.h \ CLog.h \ CStopwatch.h \ CString.h \ diff --git a/lib/base/base.dsp b/lib/base/base.dsp index a90dabc0..e0c96638 100644 --- a/lib/base/base.dsp +++ b/lib/base/base.dsp @@ -91,6 +91,10 @@ SOURCE=.\CFunctionJob.cpp # End Source File # Begin Source File +SOURCE=.\CJobList.cpp +# End Source File +# Begin Source File + SOURCE=.\CLog.cpp # End Source File # Begin Source File @@ -123,6 +127,10 @@ SOURCE=.\CFunctionJob.h # End Source File # Begin Source File +SOURCE=.\CJobList.h +# End Source File +# Begin Source File + SOURCE=.\CLog.h # End Source File # Begin Source File diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 6f690ab1..7ae42424 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -52,7 +52,8 @@ CClient::CClient(const CString& clientName) : m_streamFilterFactory(NULL), m_session(NULL), m_active(false), - m_rejected(true) + m_rejected(true), + m_status(kNotRunning) { // do nothing } @@ -108,15 +109,61 @@ CClient::exitMainLoop() m_screen->exitMainLoop(); } +void +CClient::addStatusJob(IJob* job) +{ + m_statusJobs.addJob(job); +} + +void +CClient::removeStatusJob(IJob* job) +{ + m_statusJobs.removeJob(job); +} + bool CClient::wasRejected() const { return m_rejected; } +CClient::EStatus +CClient::getStatus(CString* msg) const +{ + CLock lock(&m_mutex); + if (msg != NULL) { + *msg = m_statusMessage; + } + return m_status; +} + +void +CClient::runStatusJobs() const +{ + m_statusJobs.runJobs(); +} + +void +CClient::setStatus(EStatus status, const char* msg) +{ + { + CLock lock(&m_mutex); + m_status = status; + if (m_status == kError) { + m_statusMessage = (msg == NULL) ? "Error" : msg; + } + else { + m_statusMessage = (msg == NULL) ? "" : msg; + } + } + runStatusJobs(); +} + void CClient::onError() { + setStatus(kError); + // close down session but don't wait too long deleteSession(3.0); } @@ -172,9 +219,11 @@ CClient::open() try { LOG((CLOG_INFO "opening screen")); openSecondaryScreen(); + setStatus(kNotRunning); } - catch (XScreenOpenFailure&) { + catch (XScreenOpenFailure& e) { // can't open screen + setStatus(kError, e.what()); LOG((CLOG_INFO "failed to open screen")); throw; } @@ -195,6 +244,7 @@ CClient::mainLoop() } try { + setStatus(kNotRunning); LOG((CLOG_NOTE "starting client \"%s\"", m_name.c_str())); // start server interactions @@ -213,6 +263,7 @@ CClient::mainLoop() } catch (XMT& e) { LOG((CLOG_ERR "client error: %s", e.what())); + setStatus(kError, e.what()); // clean up deleteSession(); @@ -221,6 +272,7 @@ CClient::mainLoop() } catch (XBase& e) { LOG((CLOG_ERR "client error: %s", e.what())); + setStatus(kError, e.what()); // clean up deleteSession(); @@ -229,6 +281,8 @@ CClient::mainLoop() m_rejected = false; } catch (XThread&) { + setStatus(kNotRunning); + // clean up deleteSession(); LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); @@ -236,6 +290,7 @@ CClient::mainLoop() } catch (...) { LOG((CLOG_DEBUG "unknown client error")); + setStatus(kError); // clean up deleteSession(); @@ -529,11 +584,12 @@ CClient::runServer() { IDataSocket* socket = NULL; CServerProxy* proxy = NULL; + bool timedOut; try { for (;;) { try { // allow connect this much time to succeed - CTimerThread timer(15.0); + CTimerThread timer(15.0, &timedOut); // create socket and attempt to connect to server LOG((CLOG_DEBUG1 "connecting to server")); @@ -546,6 +602,7 @@ CClient::runServer() break; } catch (XSocketConnect& e) { + setStatus(kError, e.what()); LOG((CLOG_DEBUG "failed to connect to server: %s", e.what())); // failed to connect. if not camping then rethrow. @@ -565,31 +622,47 @@ CClient::runServer() m_server = proxy; } catch (XThread&) { - LOG((CLOG_ERR "connection timed out")); + if (timedOut) { + LOG((CLOG_ERR "connection timed out")); + setStatus(kError, "connection timed out"); + } + else { + // cancelled by some thread other than the timer + } delete socket; throw; } catch (XBase& e) { LOG((CLOG_ERR "connection failed: %s", e.what())); + setStatus(kError, e.what()); LOG((CLOG_DEBUG "disconnecting from server")); delete socket; return; } catch (...) { LOG((CLOG_ERR "connection failed: ")); + setStatus(kError); LOG((CLOG_DEBUG "disconnecting from server")); delete socket; return; } try { + // prepare for remote control + m_screen->remoteControl(); + // process messages bool rejected = true; if (proxy != NULL) { LOG((CLOG_DEBUG1 "communicating with server")); + setStatus(kRunning); rejected = !proxy->mainLoop(); + setStatus(kNotRunning); } + // prepare for local control + m_screen->localControl(); + // clean up CLock lock(&m_mutex); m_rejected = rejected; @@ -600,6 +673,8 @@ CClient::runServer() delete socket; } catch (...) { + setStatus(kNotRunning); + m_screen->localControl(); CLock lock(&m_mutex); m_rejected = false; m_server = NULL; @@ -664,9 +739,11 @@ CClient::handshakeServer(IDataSocket* socket) } catch (XIncompatibleClient& e) { LOG((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); + setStatus(kError, e.what()); } catch (XBase& e) { LOG((CLOG_WARN "error communicating with server: %s", e.what())); + setStatus(kError, e.what()); } catch (...) { // probably timed out diff --git a/lib/client/CClient.h b/lib/client/CClient.h index de0818a5..1cdcf3fa 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -20,6 +20,7 @@ #include "IClipboard.h" #include "CNetworkAddress.h" #include "CMutex.h" +#include "CJobList.h" class CSecondaryScreen; class CServerProxy; @@ -36,6 +37,13 @@ This class implements the top-level client algorithms for synergy. */ class CClient : public IScreenReceiver, public IClient { public: + enum EStatus { + kNotRunning, + kRunning, + kError, + kMaxStatus + }; + /*! This client will attempt to connect the server using \c clientName as its name. @@ -93,6 +101,22 @@ public: */ void exitMainLoop(); + //! Add a job to notify of status changes + /*! + The added job is run whenever the server's status changes in + certain externally visible ways. The client keeps ownership + of the job. + */ + void addStatusJob(IJob*); + + //! Remove a job to notify of status changes + /*! + Removes a previously added status notification job. A job can + remove itself when called but must not remove any other jobs. + The client keeps ownership of the job. + */ + void removeStatusJob(IJob*); + //@} //! @name accessors //@{ @@ -103,6 +127,12 @@ public: */ bool wasRejected() const; + //! Get the status + /*! + Returns the current status and status message. + */ + EStatus getStatus(CString* = NULL) const; + //@} // IScreenReceiver overrides @@ -140,6 +170,12 @@ public: virtual void getCursorCenter(SInt32& x, SInt32& y) const; private: + // notify status jobs of a change + void runStatusJobs() const; + + // set new status + void setStatus(EStatus, const char* msg = NULL); + // open/close the secondary screen void openSecondaryScreen(); void closeSecondaryScreen(); @@ -169,6 +205,11 @@ private: bool m_ownClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; CString m_dataClipboard[kClipboardEnd]; + + // the status change jobs and status + CJobList m_statusJobs; + EStatus m_status; + CString m_statusMessage; }; #endif diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index ce0db115..6f6690cc 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -420,6 +420,7 @@ CServerProxy::translateModifierMask(KeyModifierMask mask) const if ((mask & KeyModifierSuper) != 0) { newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]]; } + return newMask; } void diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp index 27b0cedd..fcf6832c 100644 --- a/lib/mt/CThread.cpp +++ b/lib/mt/CThread.cpp @@ -97,10 +97,16 @@ CThread::wait(double timeout) const return ARCH->wait(m_thread, timeout); } -bool -CThread::waitForEvent(double timeout) +CThread::EWaitResult +CThread::waitForEvent(double timeout) const { - return ARCH->waitForEvent(timeout); + // IArchMultithread EWaitResults map directly to our EWaitResults + static const EWaitResult s_map[] = { + kEvent, + kExit, + kTimeout + }; + return s_map[ARCH->waitForEvent(m_thread, timeout)]; } void* diff --git a/lib/mt/CThread.h b/lib/mt/CThread.h index 6592426b..73982612 100644 --- a/lib/mt/CThread.h +++ b/lib/mt/CThread.h @@ -39,6 +39,13 @@ documentation. // note -- do not derive from this class class CThread { public: + //! Result of waitForEvent() + enum EWaitResult { + kEvent, //!< An event is pending + kExit, //!< Thread exited + kTimeout //!< Wait timed out + }; + //! Run \c adoptedJob in a new thread /*! Create and start a new thread executing the \c adoptedJob. The @@ -159,18 +166,19 @@ public: //! Wait for an event (win32) /*! - Wait for the message queue to contain a message for up to \c timeout - seconds. This returns immediately if any message is available - (including messages that were already in the queue during the last - call to \c GetMessage() or \c PeekMessage() or waitForEvent(). - Returns true iff a message is available. This will wait forever - if \c timeout < 0.0. + Wait for the message queue to contain a message or for the thread + to exit for up to \c timeout seconds. This returns immediately if + any message is available (including messages that were already in + the queue during the last call to \c GetMessage() or + \c PeekMessage() or waitForEvent(). Returns kEvent if a message + is available, kExit if the thread exited, and kTimeout otherwise. + This will wait forever if \c timeout < 0.0. This method is available under win32 only. (cancellation point) */ - static bool waitForEvent(double timeout = -1.0); + EWaitResult waitForEvent(double timeout = -1.0) const; //! Get the exit result /*! diff --git a/lib/mt/CTimerThread.cpp b/lib/mt/CTimerThread.cpp index 963b5799..2ac63522 100644 --- a/lib/mt/CTimerThread.cpp +++ b/lib/mt/CTimerThread.cpp @@ -22,8 +22,13 @@ // CTimerThread // -CTimerThread::CTimerThread(double timeout) : m_timeout(timeout) +CTimerThread::CTimerThread(double timeout, bool* timedOut) : + m_timeout(timeout), + m_timedOut(timedOut) { + if (m_timedOut != NULL) { + *m_timedOut = false; + } if (m_timeout >= 0.0) { m_callingThread = new CThread(CThread::getCurrentThread()); m_timingThread = new CThread(new TMethodJob( @@ -53,5 +58,8 @@ CTimerThread::timer(void*) LOG((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); ARCH->sleep(m_timeout); LOG((CLOG_DEBUG1 "timeout")); + if (m_timedOut != NULL) { + *m_timedOut = true; + } m_callingThread->cancel(); } diff --git a/lib/mt/CTimerThread.h b/lib/mt/CTimerThread.h index c0a43c51..1a0a2b57 100644 --- a/lib/mt/CTimerThread.h +++ b/lib/mt/CTimerThread.h @@ -30,9 +30,11 @@ public: /*! Cancels the calling thread after \c timeout seconds unless destroyed before then. If \c timeout is less than zero then it never times - out and this is a no-op. + out and this is a no-op. If \c timedOutFlag is not NULL then it's + set to false in the c'tor and to true if the timeout exipires before + it's cancelled. */ - CTimerThread(double timeout); + CTimerThread(double timeout, bool* timedOutFlag = NULL); //! Cancel the timer thread ~CTimerThread(); @@ -45,6 +47,7 @@ private: private: double m_timeout; + bool* m_timedOut; CThread* m_callingThread; CThread* m_timingThread; }; diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index f8622970..cc3f5d9f 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -112,6 +112,12 @@ CMSWindowsPrimaryScreen::setOptions(const COptionsList& /*options*/) // no options } +UInt32 +CMSWindowsPrimaryScreen::addOneShotTimer(double timeout) +{ + return m_screen->addOneShotTimer(timeout); +} + KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { @@ -376,6 +382,12 @@ CMSWindowsPrimaryScreen::onEvent(CEvent* event) return false; } +void +CMSWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id) +{ + m_receiver->onOneShotTimerExpired(id); +} + SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const { diff --git a/lib/platform/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h index 91dc2b11..c274de40 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.h +++ b/lib/platform/CMSWindowsPrimaryScreen.h @@ -39,6 +39,7 @@ public: virtual void warpCursor(SInt32 x, SInt32 y); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); + virtual UInt32 addOneShotTimer(double timeout); virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; virtual IScreen* getScreen() const; @@ -47,6 +48,7 @@ public: virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); virtual SInt32 getJumpZoneSize() const; virtual void postCreateWindow(HWND); virtual void preDestroyWindow(HWND); diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index d1471218..188c070c 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -122,6 +122,11 @@ CMSWindowsScreen::closeDesktop() // remove timer if (m_timer != 0) { KillTimer(NULL, m_timer); + m_timer = 0; + } + if (m_oneShotTimer != 0) { + KillTimer(NULL, m_oneShotTimer); + m_oneShotTimer = 0; } // disconnect from desktop @@ -134,6 +139,18 @@ CMSWindowsScreen::closeDesktop() assert(m_desk == NULL); } +UInt32 +CMSWindowsScreen::addOneShotTimer(double timeout) +{ + // FIXME -- support multiple one-shot timers + if (m_oneShotTimer != 0) { + KillTimer(NULL, m_oneShotTimer); + } + m_oneShotTimer = SetTimer(NULL, 0, + static_cast(1000.0 * timeout), NULL); + return 0; +} + bool CMSWindowsScreen::isMultimon() const { @@ -205,8 +222,12 @@ CMSWindowsScreen::mainLoop() event.m_result = 0; for (;;) { // wait for an event in a cancellable way - CThread::waitForEvent(); - GetMessage(&event.m_msg, NULL, 0, 0); + if (CThread::getCurrentThread().waitForEvent(-1.0) != CThread::kEvent) { + continue; + } + if (!PeekMessage(&event.m_msg, NULL, 0, 0, PM_REMOVE)) { + continue; + } // handle quit message if (event.m_msg.message == WM_QUIT) { @@ -467,27 +488,35 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) } case WM_TIMER: - // if current desktop is not the input desktop then switch to it. - // windows 95 doesn't support multiple desktops so don't bother - // to check under it. - if (!m_is95Family) { - HDESK desk = openInputDesktop(); - if (desk != NULL) { - if (isCurrentDesktop(desk)) { - CloseDesktop(desk); - } - else if (!m_screensaver->isActive()) { - // don't switch desktops when the screensaver is - // active. we'd most likely switch to the - // screensaver desktop which would have the side - // effect of forcing the screensaver to stop. - switchDesktop(desk); - } - else { - CloseDesktop(desk); + if (msg->wParam == m_timer) { + // if current desktop is not the input desktop then switch to it. + // windows 95 doesn't support multiple desktops so don't bother + // to check under it. + if (!m_is95Family) { + HDESK desk = openInputDesktop(); + if (desk != NULL) { + if (isCurrentDesktop(desk)) { + CloseDesktop(desk); + } + else if (!m_screensaver->isActive()) { + // don't switch desktops when the screensaver is + // active. we'd most likely switch to the + // screensaver desktop which would have the side + // effect of forcing the screensaver to stop. + switchDesktop(desk); + } + else { + CloseDesktop(desk); + } } } } + else if (msg->wParam == m_oneShotTimer) { + // one shot timer expired + KillTimer(NULL, m_oneShotTimer); + m_oneShotTimer = 0; + m_eventHandler->onOneShotTimerExpired(0); + } return true; } diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 6183d50f..d11f5ef1 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -64,6 +64,14 @@ public: */ void closeDesktop(); + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer (which will be passed to the receiver's + \c onTimerExpired()). + */ + UInt32 addOneShotTimer(double timeout); + //@} //! @name accessors //@{ @@ -169,6 +177,9 @@ private: // the timer used to check for desktop switching UINT m_timer; + // the one shot timer + UINT m_oneShotTimer; + // the current desk and it's name HDESK m_desk; CString m_deskName; diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index 7783e885..a1e14d04 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -262,6 +262,12 @@ CMSWindowsSecondaryScreen::onEvent(CEvent* event) return false; } +void +CMSWindowsSecondaryScreen::onOneShotTimerExpired(UInt32) +{ + // ignore +} + SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const { @@ -272,6 +278,11 @@ void CMSWindowsSecondaryScreen::postCreateWindow(HWND window) { m_window = window; + + // update key state + updateKeys(); + + // hide cursor if this screen isn't active if (!isActive()) { showWindow(); } diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index 0681e236..acaa10b0 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -53,6 +53,7 @@ public: virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); + virtual void onOneShotTimerExpired(UInt32 id); virtual SInt32 getJumpZoneSize() const; virtual void postCreateWindow(HWND); virtual void preDestroyWindow(HWND); @@ -69,6 +70,7 @@ protected: virtual void hideWindow(); virtual void warpCursor(SInt32 x, SInt32 y); virtual void updateKeys(); + virtual void releaseKeys(); virtual void setToggleState(KeyModifierMask); virtual KeyModifierMask getToggleState() const; @@ -98,7 +100,6 @@ private: KeyModifierMask, EKeyAction) const; void doKeystrokes(const Keystrokes&, SInt32 count); - void releaseKeys(); void toggleKey(UINT virtualKey, KeyModifierMask mask); UINT virtualKeyToScanCode(UINT& virtualKey) const; bool isExtendedKey(UINT virtualKey) const; diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index a68aade9..8cd3271f 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -260,6 +260,12 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); return 1; } + else { + x += g_xScreen; + y += g_yScreen; + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); + return 0; + } } } diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 31f1affc..c398d6ac 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -356,7 +356,7 @@ CXWindowsSecondaryScreen::destroyWindow() CDisplayLock display(m_screen); if (display != NULL) { // release keys that are still pressed - releaseKeys(display); + doReleaseKeys(display); // no longer impervious to server grabs XTestGrabControl(display, False); @@ -901,7 +901,7 @@ CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const } void -CXWindowsSecondaryScreen::releaseKeys(Display* display) +CXWindowsSecondaryScreen::doReleaseKeys(Display* display) { assert(display != NULL); @@ -969,6 +969,15 @@ CXWindowsSecondaryScreen::updateKeys() updateModifiers(display); } +void +CXWindowsSecondaryScreen::releaseKeys() +{ + CDisplayLock display(m_screen); + if (display != NULL) { + doReleaseKeys(display); + } +} + void CXWindowsSecondaryScreen::updateModifiers(Display* display) { diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index 6711ee0f..96482f9f 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -67,6 +67,7 @@ protected: virtual void hideWindow(); virtual void warpCursor(SInt32 x, SInt32 y); virtual void updateKeys(); + virtual void releaseKeys(); virtual void setToggleState(KeyModifierMask); virtual KeyModifierMask getToggleState() const; @@ -97,14 +98,10 @@ private: unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, KeyModifierMask, EKeyAction) const; -/* - bool findKeyCode(KeyCode&, unsigned int&, - KeyID id, unsigned int) const; -*/ void doKeystrokes(const Keystrokes&, SInt32 count); unsigned int maskToX(KeyModifierMask) const; - void releaseKeys(Display*); + void doReleaseKeys(Display*); void updateKeycodeMap(Display* display); void updateModifiers(Display* display); void updateModifierMap(Display* display); diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 37c43133..65067b1a 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -66,7 +66,8 @@ CServer::CServer(const CString& serverName) : m_switchWaitEngaged(false), m_switchTwoTapDelay(0.0), m_switchTwoTapEngaged(false), - m_switchTwoTapArmed(false) + m_switchTwoTapArmed(false), + m_status(kNotRunning) { // do nothing } @@ -85,14 +86,17 @@ CServer::open() try { LOG((CLOG_INFO "opening screen")); openPrimaryScreen(); + setStatus(kNotRunning); } - catch (XScreen&) { + catch (XScreen& e) { // can't open screen + setStatus(kError, e.what()); LOG((CLOG_INFO "failed to open screen")); throw; } catch (XUnknownClient& e) { // can't open screen + setStatus(kServerNameUnknown); LOG((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); throw; } @@ -108,6 +112,7 @@ CServer::mainLoop() } try { + setStatus(kNotRunning); LOG((CLOG_NOTE "starting server")); // start listening for new clients @@ -138,11 +143,13 @@ CServer::mainLoop() stopThreads(); \ delete m_httpServer; \ m_httpServer = NULL; \ + runStatusJobs(); \ } while (false) FINALLY; } catch (XMT& e) { LOG((CLOG_ERR "server error: %s", e.what())); + setStatus(kError, e.what()); // clean up LOG((CLOG_NOTE "stopping server")); @@ -151,12 +158,15 @@ CServer::mainLoop() } catch (XBase& e) { LOG((CLOG_ERR "server error: %s", e.what())); + setStatus(kError, e.what()); // clean up LOG((CLOG_NOTE "stopping server")); FINALLY; } catch (XThread&) { + setStatus(kNotRunning); + // clean up LOG((CLOG_NOTE "stopping server")); FINALLY; @@ -164,6 +174,7 @@ CServer::mainLoop() } catch (...) { LOG((CLOG_DEBUG "unknown server error")); + setStatus(kError); // clean up LOG((CLOG_NOTE "stopping server")); @@ -260,6 +271,9 @@ CServer::setConfig(const CConfig& config) sendOptions(client); } + // notify of status + runStatusJobs(); + return true; } @@ -287,12 +301,52 @@ CServer::setStreamFilterFactory(IStreamFilterFactory* adopted) m_streamFilterFactory = adopted; } +void +CServer::addStatusJob(IJob* job) +{ + m_statusJobs.addJob(job); +} + +void +CServer::removeStatusJob(IJob* job) +{ + m_statusJobs.removeJob(job); +} + CString CServer::getPrimaryScreenName() const { return m_name; } +UInt32 +CServer::getNumClients() const +{ + CLock lock(&m_mutex); + return m_clients.size(); +} + +void +CServer::getClients(std::vector& list) const +{ + CLock lock(&m_mutex); + list.clear(); + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + list.push_back(index->first); + } +} + +CServer::EStatus +CServer::getStatus(CString* msg) const +{ + CLock lock(&m_mutex); + if (msg != NULL) { + *msg = m_statusMessage; + } + return m_status; +} + void CServer::getConfig(CConfig* config) const { @@ -302,6 +356,28 @@ CServer::getConfig(CConfig* config) const *config = m_config; } +void +CServer::runStatusJobs() const +{ + m_statusJobs.runJobs(); +} + +void +CServer::setStatus(EStatus status, const char* msg) +{ + { + CLock lock(&m_mutex); + m_status = status; + if (m_status == kError) { + m_statusMessage = (msg == NULL) ? "Error" : msg; + } + else { + m_statusMessage = (msg == NULL) ? "" : msg; + } + } + runStatusJobs(); +} + UInt32 CServer::getActivePrimarySides() const { @@ -325,6 +401,8 @@ CServer::getActivePrimarySides() const void CServer::onError() { + setStatus(kError); + // stop all running threads but don't wait too long since some // threads may be unable to proceed until this thread returns. stopThreads(3.0); @@ -743,6 +821,10 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) case kBottom: clearWait = (m_y <= ay + ah - 1 + zoneSize); break; + + default: + clearWait = false; + break; } if (clearWait) { onNoSwitch(); @@ -1174,7 +1256,7 @@ CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) // if waiting before a switch then prepare to switch later if (!allowSwitch && m_switchWaitDelay > 0.0) { - if (isNewDirection) { + if (isNewDirection || !m_switchWaitEngaged) { m_switchWaitEngaged = true; m_switchWaitX = x; m_switchWaitY = y; @@ -1413,6 +1495,7 @@ CServer::acceptClients(void*) break; } catch (XSocketAddressInUse& e) { + setStatus(kError, e.what()); LOG((CLOG_WARN "bind failed: %s", e.what())); // give up if we've waited too long @@ -1427,6 +1510,7 @@ CServer::acceptClients(void*) } // accept connections and begin processing them + setStatus(kRunning); LOG((CLOG_DEBUG1 "waiting for client connections")); for (;;) { // accept connection @@ -1439,16 +1523,15 @@ CServer::acceptClients(void*) startThread(new TMethodJob( this, &CServer::runClient, socket)); } - - // clean up - delete listen; } catch (XBase& e) { + setStatus(kError, e.what()); LOG((CLOG_ERR "cannot listen for clients: %s", e.what())); delete listen; exitMainLoopWithError(); } catch (...) { + setStatus(kNotRunning); delete listen; throw; } @@ -1923,71 +2006,84 @@ CServer::addConnection(IClient* client) LOG((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str())); - CLock lock(&m_mutex); + { + CLock lock(&m_mutex); - // name must be in our configuration - if (!m_config.isScreen(client->getName())) { - throw XUnknownClient(client->getName()); + // name must be in our configuration + if (!m_config.isScreen(client->getName())) { + throw XUnknownClient(client->getName()); + } + + // can only have one screen with a given name at any given time + if (m_clients.count(client->getName()) != 0) { + throw XDuplicateClient(client->getName()); + } + + // save screen info + m_clients.insert(std::make_pair(client->getName(), client)); + LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str())); } - - // can only have one screen with a given name at any given time - if (m_clients.count(client->getName()) != 0) { - throw XDuplicateClient(client->getName()); - } - - // save screen info - m_clients.insert(std::make_pair(client->getName(), client)); - LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str())); + runStatusJobs(); } void CServer::removeConnection(const CString& name) { LOG((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); - CLock lock(&m_mutex); + bool updateStatus; + { + CLock lock(&m_mutex); - // find client - CClientList::iterator index = m_clients.find(name); - assert(index != m_clients.end()); + // find client + CClientList::iterator index = m_clients.find(name); + assert(index != m_clients.end()); - // if this is active screen then we have to jump off of it - IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; - if (active == index->second && active != m_primaryClient) { - // record new position (center of primary screen) - m_primaryClient->getCursorCenter(m_x, m_y); + // if this is active screen then we have to jump off of it + IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; + if (active == index->second && active != m_primaryClient) { + // record new position (center of primary screen) + m_primaryClient->getCursorCenter(m_x, m_y); - // stop waiting to switch if we were - if (active == m_switchScreen) { - clearSwitchState(); + // stop waiting to switch if we were + if (active == m_switchScreen) { + clearSwitchState(); + } + + // don't notify active screen since it probably already + // disconnected. + LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); + + // cut over + m_active = m_primaryClient; + + // enter new screen (unless we already have because of the + // screen saver) + if (m_activeSaver == NULL) { + m_primaryClient->enter(m_x, m_y, m_seqNum, + m_primaryClient->getToggleMask(), false); + } } - // don't notify active screen since it probably already disconnected - LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); - - // cut over - m_active = m_primaryClient; - - // enter new screen (unless we already have because of the - // screen saver) - if (m_activeSaver == NULL) { - m_primaryClient->enter(m_x, m_y, m_seqNum, - m_primaryClient->getToggleMask(), false); + // if this screen had the cursor when the screen saver activated + // then we can't switch back to it when the screen saver + // deactivates. + if (m_activeSaver == index->second) { + m_activeSaver = NULL; } + + // done with client + delete index->second; + m_clients.erase(index); + + // remove any thread for this client + m_clientThreads.erase(name); + + updateStatus = (m_clients.size() <= 1); } - // if this screen had the cursor when the screen saver activated - // then we can't switch back to it when the screen saver - // deactivates. - if (m_activeSaver == index->second) { - m_activeSaver = NULL; + if (updateStatus) { + runStatusJobs(); } - - // done with client - delete index->second; - m_clients.erase(index); - - // remove any thread for this client - m_clientThreads.erase(name); } diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 11896830..8e644853 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -22,9 +22,11 @@ #include "CCondVar.h" #include "CMutex.h" #include "CThread.h" +#include "CJobList.h" #include "CStopwatch.h" #include "stdlist.h" #include "stdmap.h" +#include "stdvector.h" class CClientProxy; class CHTTPServer; @@ -42,6 +44,14 @@ This class implements the top-level server algorithms for synergy. */ class CServer : public IServer, public IPrimaryScreenReceiver { public: + enum EStatus { + kNotRunning, + kRunning, + kServerNameUnknown, + kError, + kMaxStatus + }; + /*! The server will look itself up in the configuration using \c serverName as its name. @@ -116,6 +126,22 @@ public: */ void setStreamFilterFactory(IStreamFilterFactory*); + //! Add a job to notify of status changes + /*! + The added job is run whenever the server's status changes in + certain externally visible ways. The client keeps ownership + of the job. + */ + void addStatusJob(IJob*); + + //! Remove a job to notify of status changes + /*! + Removes a previously added status notification job. A job can + remove itself when called but must not remove any other jobs. + The client keeps ownership of the job. + */ + void removeStatusJob(IJob*); + //@} //! @name accessors //@{ @@ -132,6 +158,24 @@ public: */ CString getPrimaryScreenName() const; + //! Get number of connected clients + /*! + Returns the number of connected clients, including the server itself. + */ + UInt32 getNumClients() const; + + //! Get the list of connected clients + /*! + Set the \c list to the names of the currently connected clients. + */ + void getClients(std::vector& list) const; + + //! Get the status + /*! + Returns the current status and status message. + */ + EStatus getStatus(CString* = NULL) const; + //@} // IServer overrides @@ -170,6 +214,12 @@ protected: private: typedef std::list CThreadList; + // notify status jobs of a change + void runStatusJobs() const; + + // set new status + void setStatus(EStatus, const char* msg = NULL); + // get the sides of the primary screen that have neighbors UInt32 getActivePrimarySides() const; @@ -352,6 +402,11 @@ private: CStopwatch m_switchTwoTapTimer; bool m_switchTwoTapEngaged; bool m_switchTwoTapArmed; + + // the status change jobs and status + CJobList m_statusJobs; + EStatus m_status; + CString m_statusMessage; }; #endif diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp index 554e91db..5df455a8 100644 --- a/lib/synergy/CSecondaryScreen.cpp +++ b/lib/synergy/CSecondaryScreen.cpp @@ -74,17 +74,6 @@ CSecondaryScreen::open() // create and prepare our window createWindow(); - // assume primary has all clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - grabClipboard(id); - } - - // update keyboard state - updateKeys(); - - // disable the screen saver - getScreen()->openScreensaver(false); - // subclass hook onPostOpen(); @@ -95,6 +84,30 @@ CSecondaryScreen::open() close(); throw; } +} + +void +CSecondaryScreen::close() +{ + onPreClose(); + destroyWindow(); + getScreen()->close(); + onPostClose(); +} + +void +CSecondaryScreen::remoteControl() +{ + // assume primary has all clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + grabClipboard(id); + } + + // update keyboard state + updateKeys(); + + // disable the screen saver + getScreen()->openScreensaver(false); // hide the cursor { @@ -105,13 +118,9 @@ CSecondaryScreen::open() } void -CSecondaryScreen::close() +CSecondaryScreen::localControl() { - onPreClose(); getScreen()->closeScreensaver(); - destroyWindow(); - getScreen()->close(); - onPostClose(); } void diff --git a/lib/synergy/CSecondaryScreen.h b/lib/synergy/CSecondaryScreen.h index bca229b8..3ffeba2b 100644 --- a/lib/synergy/CSecondaryScreen.h +++ b/lib/synergy/CSecondaryScreen.h @@ -41,11 +41,10 @@ public: //! Open screen /*! - Opens the screen. This includes initializing the screen, - hiding the cursor, and disabling the screen saver. It also causes - events to the reported to an IScreenReceiver (which is set through - some other interface). Calls close() before returning (rethrowing) - if it fails for any reason. + Opens the screen. It also causes events to the reported to an + IScreenReceiver (which is set through some other interface). + Calls close() before returning (rethrowing) if it fails for any + reason. */ void open(); @@ -64,12 +63,26 @@ public: */ void exitMainLoop(); + //! Prepare for remote control + /*! + Prepares the screen for remote control by the server. In + particular, it disables the screen saver. + */ + void remoteControl(); + + //! Release from remote control + /*! + Cleans up the screen from remote control by the server. In + particular, it enables the screen saver. It also synthesizes + key up events for any keys that are logically down; without + this the client will leave its keyboard in the wrong logical + state. + */ + void localControl(); + //! Close screen /*! - Closes the screen. This restores the screen saver, shows the cursor - and closes the screen. It also synthesizes key up events for any - keys that are logically down; without this the client will leave - its keyboard in the wrong logical state. + Closes the screen. */ void close(); @@ -332,6 +345,13 @@ protected: */ virtual void updateKeys() = 0; + //! Release keys + /*! + Synthesizes key release event for any key that our key state + says is down. + */ + virtual void releaseKeys() = 0; + //! Synchronize toggle key state /*! Toggle modifiers that don't match the given state so that they do. From a7bafcca2fc8409dbf29b0308bc69525131647ea Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 13 Mar 2003 19:20:55 +0000 Subject: [PATCH 461/807] Fixed double locking of mutex. --- lib/platform/CXWindowsScreen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index cae5ff23..ba91e46b 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -561,7 +561,8 @@ CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const y = my; } else { - getCursorCenter(x, y); + x = m_x + (m_w >> 1); + y = m_y + (m_h >> 1); } } From 635a2a7c5f8166ccf0e8c013fc7ed6352e37cde9 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 13 Mar 2003 20:24:45 +0000 Subject: [PATCH 462/807] Moved comment to more relevant location. --- lib/platform/CXWindowsScreenSaver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index e421cf09..089499bf 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -196,8 +196,7 @@ CXWindowsScreenSaver::enable() void CXWindowsScreenSaver::disable() { - // for xscreensaver. 5 seconds should be plenty often to - // suppress the screen saver. + // for xscreensaver m_disabled = true; updateDisableJob(); @@ -447,6 +446,7 @@ CXWindowsScreenSaver::updateDisableJob() assert(m_disableJob != NULL); if (m_disabled && !m_suppressDisable && !m_disableJobInstalled) { + // 5 seconds should be plenty often to suppress the screen saver m_disableJobInstalled = true; m_screen->addTimer(m_disableJob, 5.0); } From 95ddb95643640f73c99271a0e809e51efd7ddcdf Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 Mar 2003 17:40:25 +0000 Subject: [PATCH 463/807] Added resources missing from previous checkin. --- cmd/synergyc/synergyc.rc | 37 ++++++++++++++++++++++++++++++++++++- cmd/synergys/synergys.rc | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index 74ecee37..156651a4 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -55,8 +55,40 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_SYNERGY ICON DISCARDABLE "synergyc.ico" -#endif // English (U.S.) resources +IDI_TASKBAR_NOT_RUNNING ICON DISCARDABLE "tb_idle.ico" +IDI_TASKBAR_NOT_WORKING ICON DISCARDABLE "tb_error.ico" +IDI_TASKBAR_NOT_CONNECTED ICON DISCARDABLE "tb_wait.ico" +IDI_TASKBAR_CONNECTED ICON DISCARDABLE "tb_run.ico" + ///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_TASKBAR_STATUS DIALOG DISCARDABLE 0, 0, 145, 18 +STYLE DS_MODALFRAME | WS_POPUP +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_TASKBAR MENU DISCARDABLE +BEGIN + POPUP "Synergy" + BEGIN + MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM SEPARATOR + MENUITEM "Quit", IDC_TASKBAR_QUIT + END +END + ///////////////////////////////////////////////////////////////////////////// // @@ -68,6 +100,9 @@ BEGIN IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." END +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + #ifndef APSTUDIO_INVOKED diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index 61bb42f1..27e4484d 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -55,6 +55,43 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_SYNERGY ICON DISCARDABLE "synergys.ico" +IDI_TASKBAR_NOT_RUNNING ICON DISCARDABLE "tb_idle.ico" +IDI_TASKBAR_NOT_WORKING ICON DISCARDABLE "tb_error.ico" +IDI_TASKBAR_NOT_CONNECTED ICON DISCARDABLE "tb_wait.ico" +IDI_TASKBAR_CONNECTED ICON DISCARDABLE "tb_run.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_TASKBAR MENU DISCARDABLE +BEGIN + POPUP "Synergy" + BEGIN + MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM SEPARATOR + MENUITEM "Quit", IDC_TASKBAR_QUIT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_TASKBAR_STATUS DIALOG DISCARDABLE 0, 0, 145, 60 +STYLE DS_MODALFRAME | WS_POPUP +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LISTBOX IDC_TASKBAR_STATUS_CLIENTS,3,17,139,40,NOT LBS_NOTIFY | + LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_VSCROLL | + WS_TABSTOP +END + ///////////////////////////////////////////////////////////////////////////// // From de64342292640c61a0872ff6201d74dc7988c51a Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 Mar 2003 17:40:47 +0000 Subject: [PATCH 464/807] Minor hook fixes. --- lib/platform/CSynergyHook.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 8cd3271f..e0530137 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -294,7 +294,8 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam) if (code >= 0) { if (g_screenSaver) { MSG* msg = reinterpret_cast(lParam); - if (msg->message == WM_SYSCOMMAND && msg->wParam == SC_SCREENSAVE) { + if (msg->message == WM_SYSCOMMAND && + msg->wParam == SC_SCREENSAVE) { // broadcast screen saver started message PostThreadMessage(g_threadID, SYNERGY_MSG_SCREEN_SAVER, TRUE, 0); @@ -374,7 +375,7 @@ keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) // // low-level mouse hook -- this allows us to capture and handle mouse // wheel events on all windows NT platforms from NT SP3 and up. this -// is both simpler than using the mouse hook but also supports windows +// is both simpler than using the mouse hook and also supports windows // windows NT which does not report mouse wheel events. we need to // keep the mouse hook handling of mouse wheel events because the // windows 95 family doesn't support low-level hooks. @@ -561,8 +562,11 @@ DllMain(HINSTANCE instance, DWORD reason, LPVOID) } else if (reason == DLL_PROCESS_DETACH) { if (g_processID == GetCurrentProcessId()) { - if (g_keyboard != NULL || g_mouse != NULL || g_cbt != NULL) { + if (g_keyboard != NULL || + g_mouse != NULL || + g_getMessage != NULL) { uninstall(); + uninstallScreenSaver(); } g_processID = 0; g_hinstance = NULL; @@ -715,7 +719,7 @@ install() } // install low-level keyboard/mouse hooks, if possible. since these - // hook are called in the context of the installing thread and that + // hooks are called in the context of the installing thread and that // thread must have a message loop but we don't want the caller's // message loop to do the work, we'll fire up a separate thread // just for the hooks. note that low-level hooks are only available @@ -814,7 +818,7 @@ uninstallScreenSaver(void) assert(g_hinstance != NULL); // uninstall hook unless the mouse wheel hook is installed - if (g_getMessage != NULL && g_wheelSupport == kWheelNone) { + if (g_getMessage != NULL && g_wheelSupport != kWheelOld) { UnhookWindowsHookEx(g_getMessage); g_getMessage = NULL; } From 74e50877e96e663a7f4f7d984142d51fa462cc04 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 Mar 2003 17:40:56 +0000 Subject: [PATCH 465/807] Made releaseKeys() only synthesize key releases for those keys that synergy synthesized a press for, not keys that the user is physically pressing. --- lib/common/Makefile.am | 1 + lib/common/common.dsp | 4 + lib/common/stdbitset.h | 17 ++++ lib/platform/CMSWindowsSecondaryScreen.cpp | 90 ++++++++++++++-------- lib/platform/CMSWindowsSecondaryScreen.h | 5 +- lib/platform/CXWindowsSecondaryScreen.cpp | 43 +++++++---- lib/platform/CXWindowsSecondaryScreen.h | 14 ++-- 7 files changed, 120 insertions(+), 54 deletions(-) create mode 100644 lib/common/stdbitset.h diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index 4de7af6c..889af930 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -21,6 +21,7 @@ EXTRA_DIST = \ IInterface.h \ Version.h \ common.h \ + stdbitset.h \ stdfstream.h \ stdistream.h \ stdlist.h \ diff --git a/lib/common/common.dsp b/lib/common/common.dsp index dd600c0a..df6b77b4 100644 --- a/lib/common/common.dsp +++ b/lib/common/common.dsp @@ -103,6 +103,10 @@ SOURCE=.\IInterface.h # End Source File # Begin Source File +SOURCE=.\stdbitset.h +# End Source File +# Begin Source File + SOURCE=.\stdfstream.h # End Source File # Begin Source File diff --git a/lib/common/stdbitset.h b/lib/common/stdbitset.h new file mode 100644 index 00000000..529772ca --- /dev/null +++ b/lib/common/stdbitset.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index a1e14d04..f0cb0062 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -68,21 +68,25 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) doKeystrokes(keys, 1); // note that key is now down - m_keys[virtualKey] |= 0x80; + m_keys[virtualKey] |= 0x80; + m_fakeKeys[virtualKey] |= 0x80; switch (virtualKey) { case VK_LSHIFT: case VK_RSHIFT: - m_keys[VK_SHIFT] |= 0x80; + m_keys[VK_SHIFT] |= 0x80; + m_fakeKeys[VK_SHIFT] |= 0x80; break; case VK_LCONTROL: case VK_RCONTROL: - m_keys[VK_CONTROL] |= 0x80; + m_keys[VK_CONTROL] |= 0x80; + m_fakeKeys[VK_CONTROL] |= 0x80; break; case VK_LMENU: case VK_RMENU: - m_keys[VK_MENU] |= 0x80; + m_keys[VK_MENU] |= 0x80; + m_fakeKeys[VK_MENU] |= 0x80; break; } } @@ -128,41 +132,48 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) doKeystrokes(keys, 1); // note that key is now up - m_keys[virtualKey] &= ~0x80; + m_keys[virtualKey] &= ~0x80; + m_fakeKeys[virtualKey] &= ~0x80; switch (virtualKey) { case VK_LSHIFT: if ((m_keys[VK_RSHIFT] & 0x80) == 0) { - m_keys[VK_SHIFT] &= ~0x80; + m_keys[VK_SHIFT] &= ~0x80; + m_fakeKeys[VK_SHIFT] &= ~0x80; } break; case VK_RSHIFT: if ((m_keys[VK_LSHIFT] & 0x80) == 0) { - m_keys[VK_SHIFT] &= ~0x80; + m_keys[VK_SHIFT] &= ~0x80; + m_fakeKeys[VK_SHIFT] &= ~0x80; } break; case VK_LCONTROL: if ((m_keys[VK_RCONTROL] & 0x80) == 0) { - m_keys[VK_CONTROL] &= ~0x80; + m_keys[VK_CONTROL] &= ~0x80; + m_fakeKeys[VK_CONTROL] &= ~0x80; } break; case VK_RCONTROL: if ((m_keys[VK_LCONTROL] & 0x80) == 0) { - m_keys[VK_CONTROL] &= ~0x80; + m_keys[VK_CONTROL] &= ~0x80; + m_fakeKeys[VK_CONTROL] &= ~0x80; } break; case VK_LMENU: if ((m_keys[VK_RMENU] & 0x80) == 0) { - m_keys[VK_MENU] &= ~0x80; + m_keys[VK_MENU] &= ~0x80; + m_fakeKeys[VK_MENU] &= ~0x80; } break; case VK_RMENU: if ((m_keys[VK_LMENU] & 0x80) == 0) { - m_keys[VK_MENU] &= ~0x80; + m_keys[VK_MENU] &= ~0x80; + m_fakeKeys[VK_MENU] &= ~0x80; } break; } @@ -439,6 +450,7 @@ CMSWindowsSecondaryScreen::updateKeys() { // clear key state memset(m_keys, 0, sizeof(m_keys)); + memset(m_fakeKeys, 0, sizeof(m_keys)); // we only care about the modifier key states m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); @@ -457,6 +469,11 @@ CMSWindowsSecondaryScreen::updateKeys() m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); + // copy over lock states to m_fakeKeys + m_fakeKeys[VK_CAPITAL] = (m_keys[VK_CAPITAL] & 0x01); + m_fakeKeys[VK_NUMLOCK] = (m_keys[VK_NUMLOCK] & 0x01); + m_fakeKeys[VK_SCROLL] = (m_keys[VK_SCROLL] & 0x01); + // update active modifier mask m_mask = 0; if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) { @@ -1012,6 +1029,10 @@ CMSWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) void CMSWindowsSecondaryScreen::releaseKeys() { + // release keys that we've synthesized a press for and only those + // keys. we don't want to synthesize a release on a key the user + // is still physically pressing. + CLock lock(&m_mutex); m_screen->syncDesktop(); @@ -1020,42 +1041,42 @@ CMSWindowsSecondaryScreen::releaseKeys() // support them then they won't be set and the non-side-distinuishing // key will retain its state. if the platform does support them then // the non-side-distinguishing will be reset. - if ((m_keys[VK_LSHIFT] & 0x80) != 0) { + if ((m_fakeKeys[VK_LSHIFT] & 0x80) != 0) { sendKeyEvent(VK_LSHIFT, false); - m_keys[VK_SHIFT] = 0; - m_keys[VK_LSHIFT] = 0; + m_fakeKeys[VK_SHIFT] = 0; + m_fakeKeys[VK_LSHIFT] = 0; } - if ((m_keys[VK_RSHIFT] & 0x80) != 0) { + if ((m_fakeKeys[VK_RSHIFT] & 0x80) != 0) { sendKeyEvent(VK_RSHIFT, false); - m_keys[VK_SHIFT] = 0; - m_keys[VK_RSHIFT] = 0; + m_fakeKeys[VK_SHIFT] = 0; + m_fakeKeys[VK_RSHIFT] = 0; } - if ((m_keys[VK_LCONTROL] & 0x80) != 0) { + if ((m_fakeKeys[VK_LCONTROL] & 0x80) != 0) { sendKeyEvent(VK_LCONTROL, false); - m_keys[VK_CONTROL] = 0; - m_keys[VK_LCONTROL] = 0; + m_fakeKeys[VK_CONTROL] = 0; + m_fakeKeys[VK_LCONTROL] = 0; } - if ((m_keys[VK_RCONTROL] & 0x80) != 0) { + if ((m_fakeKeys[VK_RCONTROL] & 0x80) != 0) { sendKeyEvent(VK_RCONTROL, false); - m_keys[VK_CONTROL] = 0; - m_keys[VK_RCONTROL] = 0; + m_fakeKeys[VK_CONTROL] = 0; + m_fakeKeys[VK_RCONTROL] = 0; } - if ((m_keys[VK_LMENU] & 0x80) != 0) { + if ((m_fakeKeys[VK_LMENU] & 0x80) != 0) { sendKeyEvent(VK_LMENU, false); - m_keys[VK_MENU] = 0; - m_keys[VK_LMENU] = 0; + m_fakeKeys[VK_MENU] = 0; + m_fakeKeys[VK_LMENU] = 0; } - if ((m_keys[VK_RMENU] & 0x80) != 0) { + if ((m_fakeKeys[VK_RMENU] & 0x80) != 0) { sendKeyEvent(VK_RMENU, false); - m_keys[VK_MENU] = 0; - m_keys[VK_RMENU] = 0; + m_fakeKeys[VK_MENU] = 0; + m_fakeKeys[VK_RMENU] = 0; } // now check all the other keys - for (UInt32 i = 0; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { - if ((m_keys[i] & 0x80) != 0) { + for (UInt32 i = 0; i < sizeof(m_fakeKeys) / sizeof(m_fakeKeys[0]); ++i) { + if ((m_fakeKeys[i] & 0x80) != 0) { sendKeyEvent(i, false); - m_keys[i] = 0; + m_fakeKeys[i] = 0; } } } @@ -1068,8 +1089,9 @@ CMSWindowsSecondaryScreen::toggleKey(UINT virtualKey, KeyModifierMask mask) sendKeyEvent(virtualKey, false); // toggle shadow state - m_mask ^= mask; - m_keys[virtualKey & 0xff] ^= 0x01; + m_mask ^= mask; + m_keys[virtualKey & 0xff] ^= 0x01; + m_fakeKeys[virtualKey & 0xff] ^= 0x01; } UINT diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index acaa10b0..afa26a8c 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -115,9 +115,12 @@ private: // our window HWND m_window; - // virtual key states + // virtual key states as set by us or the user BYTE m_keys[256]; + // virtual key states as set by us + BYTE m_fakeKeys[256]; + // current active modifiers KeyModifierMask m_mask; }; diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index c398d6ac..19f1a6f8 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -111,7 +111,8 @@ CXWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) doKeystrokes(keys, 1); // note that key is now down - m_keys[keycode] = true; + m_keys[keycode] = true; + m_fakeKeys[keycode] = true; } void @@ -149,7 +150,8 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) doKeystrokes(keys, 1); // note that key is now up - m_keys[keycode] = false; + m_keys[keycode] = false; + m_fakeKeys[keycode] = false; } void @@ -251,10 +253,12 @@ CXWindowsSecondaryScreen::onEvent(CEvent* event) // handle event switch (xevent.type) { - case MappingNotify: + case MappingNotify: { // keyboard mapping changed - updateKeys(); + CDisplayLock display(m_screen); + doUpdateKeys(display); return true; + } case LeaveNotify: // mouse moved out of hider window somehow. hide the window. @@ -905,20 +909,19 @@ CXWindowsSecondaryScreen::doReleaseKeys(Display* display) { assert(display != NULL); - // key up for each key that's down + // key release for each key that we faked a press for for (UInt32 i = 0; i < 256; ++i) { - if (m_keys[i]) { + if (m_fakeKeys[i]) { XTestFakeKeyEvent(display, i, False, CurrentTime); - m_keys[i] = false; + m_fakeKeys[i] = false; + m_keys[i] = false; } } } void -CXWindowsSecondaryScreen::updateKeys() +CXWindowsSecondaryScreen::doUpdateKeys(Display* display) { - CDisplayLock display(m_screen); - // query the button mapping UInt32 numButtons = XGetPointerMapping(display, NULL, 0); unsigned char* tmpButtons = new unsigned char[numButtons]; @@ -947,6 +950,17 @@ CXWindowsSecondaryScreen::updateKeys() // clean up delete[] tmpButtons; + // update mappings and current modifiers + updateModifierMap(display); + updateKeycodeMap(display); + updateModifiers(display); +} + +void +CXWindowsSecondaryScreen::updateKeys() +{ + CDisplayLock display(m_screen); + // ask server which keys are pressed char keys[32]; XQueryKeymap(display, keys); @@ -963,10 +977,11 @@ CXWindowsSecondaryScreen::updateKeys() m_keys[j + 7] = ((keys[i] & 0x80) != 0); } - // update mappings and current modifiers - updateModifierMap(display); - updateKeycodeMap(display); - updateModifiers(display); + // we've fake pressed no keys + m_fakeKeys.reset(); + + // update mappings and current modifiers and mouse buttons + doUpdateKeys(display); } void diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index 96482f9f..18530130 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -17,6 +17,7 @@ #include "CSecondaryScreen.h" #include "IScreenEventHandler.h" +#include "stdbitset.h" #include "stdmap.h" #include "stdvector.h" #if defined(X_DISPLAY_MISSING) @@ -78,9 +79,6 @@ private: KeyCodeMask(); public: KeyCode m_keycode[4]; - // FIXME -- don't need masks - unsigned int m_keyMask[4]; - unsigned int m_keyMaskMask[4]; }; class Keystroke { public: @@ -101,6 +99,7 @@ private: void doKeystrokes(const Keystrokes&, SInt32 count); unsigned int maskToX(KeyModifierMask) const; + void doUpdateKeys(Display*); void doReleaseKeys(Display*); void updateKeycodeMap(Display* display); void updateModifiers(Display* display); @@ -125,8 +124,13 @@ private: bool m_numLockHalfDuplex; bool m_capsLockHalfDuplex; - // set entries indicate keys that are pressed. indexed by keycode. - bool m_keys[256]; + // set entries indicate keys that are pressed (by us or by the user). + // indexed by keycode. + std::bitset<256> m_keys; + + // set entries indicate keys that are synthetically pressed by us. + // this is normally the same as m_keys. + std::bitset<256> m_fakeKeys; // logical to physical button mapping. m_buttons[i] gives the // physical button for logical button i+1. From 9f984ad1a033ed5c01e3580c3b5fa06f215a23d5 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 Mar 2003 17:40:57 +0000 Subject: [PATCH 466/807] Fixed detection of screen saver shutdown on windows nt. --- lib/platform/CMSWindowsScreenSaver.cpp | 140 +++++++++++++++++-------- lib/platform/CMSWindowsScreenSaver.h | 4 +- 2 files changed, 99 insertions(+), 45 deletions(-) diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index df3322a7..e2d4ab3e 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -16,6 +16,9 @@ #include "CThread.h" #include "CLog.h" #include "TMethodJob.h" +#include "CArch.h" +#include +#include #if !defined(SPI_GETSCREENSAVERRUNNING) #define SPI_GETSCREENSAVERRUNNING 114 @@ -70,29 +73,35 @@ CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) // the system is idle or the user deliberately started // the screen saver. Sleep(250); - HWND hwnd = findScreenSaver(); - if (hwnd == NULL) { - // didn't start - LOG((CLOG_DEBUG "can't find screen saver window")); - return false; - } - // get the process - DWORD processID; - GetWindowThreadProcessId(hwnd, &processID); - HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID); - if (process == NULL) { - // didn't start - LOG((CLOG_DEBUG "can't open screen saver process")); - return false; - } - - // watch for the process to exit + // set parameters common to all screen saver handling m_threadID = GetCurrentThreadId(); m_msg = msg; m_wParam = wParam; m_lParam = lParam; - watchProcess(process); + + // we handle the screen saver differently for the windows + // 95 and nt families. + if (m_is95Family) { + // on windows 95 we wait for the screen saver process + // to terminate. get the process. + DWORD processID = findScreenSaver(); + HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID); + if (process == NULL) { + // didn't start + LOG((CLOG_DEBUG "can't open screen saver process")); + return false; + } + + // watch for the process to exit + watchProcess(process); + } + else { + // on the windows nt family we wait for the desktop to + // change until it's neither the Screen-Saver desktop + // nor a desktop we can't open (the login desktop). + watchDesktop(); + } return true; } @@ -240,39 +249,39 @@ CMSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg) return TRUE; } -HWND +DWORD CMSWindowsScreenSaver::findScreenSaver() { - if (!m_is95Family) { - // screen saver runs on a separate desktop - HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, MAXIMUM_ALLOWED); - if (desktop != NULL) { - // search - CFindScreenSaverInfo info; - info.m_desktop = desktop; - info.m_window = NULL; - EnumDesktopWindows(desktop, - &CMSWindowsScreenSaver::findScreenSaverFunc, - reinterpret_cast(&info)); + // try windows 95 way + HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL); - // done with desktop - CloseDesktop(desktop); - - // return window if found - if (info.m_window != NULL) { - return info.m_window; - } - } + // get process ID of process that owns the window, if found + if (hwnd != NULL) { + DWORD processID; + GetWindowThreadProcessId(hwnd, &processID); + return processID; } - // try windows 95 way - return FindWindow("WindowsScreenSaverClass", NULL); + // not found + return 0; +} + +void +CMSWindowsScreenSaver::watchDesktop() +{ + // stop watching previous process/desktop + unwatchProcess(); + + // watch desktop in another thread + LOG((CLOG_DEBUG "watching screen saver desktop")); + m_watch = new CThread(new TMethodJob(this, + &CMSWindowsScreenSaver::watchDesktopThread)); } void CMSWindowsScreenSaver::watchProcess(HANDLE process) { - // stop watching previous process + // stop watching previous process/desktop unwatchProcess(); // watch new process in another thread @@ -288,7 +297,7 @@ void CMSWindowsScreenSaver::unwatchProcess() { if (m_watch != NULL) { - LOG((CLOG_DEBUG "stopped watching screen saver process")); + LOG((CLOG_DEBUG "stopped watching screen saver process/desktop")); m_watch->cancel(); m_watch->wait(); delete m_watch; @@ -300,15 +309,58 @@ CMSWindowsScreenSaver::unwatchProcess() } } +void +CMSWindowsScreenSaver::watchDesktopThread(void*) +{ + DWORD reserved = 0; + TCHAR* name = NULL; + + for (;;) { + // wait a bit + ARCH->sleep(0.2); + + // get current desktop + HDESK desk = OpenInputDesktop(0, FALSE, GENERIC_READ); + if (desk == NULL) { + // can't open desktop so keep waiting + continue; + } + + // get current desktop name length + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + + // allocate more space for the name, if necessary + if (size > reserved) { + reserved = size; + name = (TCHAR*)alloca(reserved + sizeof(TCHAR)); + } + + // get current desktop name + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + + // compare name to screen saver desktop name + if (_tcsicmp(name, TEXT("Screen-saver")) == 0) { + // still the screen saver desktop so keep waiting + continue; + } + + // send screen saver deactivation message + PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); + return; + } +} + void CMSWindowsScreenSaver::watchProcessThread(void*) { for (;;) { CThread::testCancel(); if (WaitForSingleObject(m_process, 50) == WAIT_OBJECT_0) { - // process terminated. send screen saver deactivation - // message. + // process terminated LOG((CLOG_DEBUG "screen saver died")); + + // send screen saver deactivation message PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); return; } diff --git a/lib/platform/CMSWindowsScreenSaver.h b/lib/platform/CMSWindowsScreenSaver.h index f637035e..b4c5feac 100644 --- a/lib/platform/CMSWindowsScreenSaver.h +++ b/lib/platform/CMSWindowsScreenSaver.h @@ -58,9 +58,11 @@ private: static BOOL CALLBACK findScreenSaverFunc(HWND hwnd, LPARAM lParam); static BOOL CALLBACK killScreenSaverFunc(HWND hwnd, LPARAM lParam); - HWND findScreenSaver(); + DWORD findScreenSaver(); + void watchDesktop(); void watchProcess(HANDLE process); void unwatchProcess(); + void watchDesktopThread(void*); void watchProcessThread(void*); private: From 0fd70ee536b4c94ba0c0dbea9f1d08de1ad83461 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 Mar 2003 22:31:59 +0000 Subject: [PATCH 467/807] Added type casts to avoid warning. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index f0cb0062..9f335e54 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -470,9 +470,9 @@ CMSWindowsSecondaryScreen::updateKeys() m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); // copy over lock states to m_fakeKeys - m_fakeKeys[VK_CAPITAL] = (m_keys[VK_CAPITAL] & 0x01); - m_fakeKeys[VK_NUMLOCK] = (m_keys[VK_NUMLOCK] & 0x01); - m_fakeKeys[VK_SCROLL] = (m_keys[VK_SCROLL] & 0x01); + m_fakeKeys[VK_CAPITAL] = static_cast(m_keys[VK_CAPITAL] & 0x01); + m_fakeKeys[VK_NUMLOCK] = static_cast(m_keys[VK_NUMLOCK] & 0x01); + m_fakeKeys[VK_SCROLL] = static_cast(m_keys[VK_SCROLL] & 0x01); // update active modifier mask m_mask = 0; From a7e2141edef6550ea83ccc9b737e249c1702cf59 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 Mar 2003 22:32:01 +0000 Subject: [PATCH 468/807] Added a log message why the user is locked to the screen. --- lib/platform/CMSWindowsPrimaryScreen.cpp | 265 +++++++++++++++++++++++ lib/platform/CXWindowsPrimaryScreen.cpp | 47 ++-- lib/server/CServer.cpp | 9 +- lib/synergy/CPrimaryScreen.h | 2 + 4 files changed, 296 insertions(+), 27 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index cc3f5d9f..98785b08 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -21,6 +21,269 @@ #include "CArchMiscWindows.h" #include +// +// map virtual key id to a name +// + +static const char* g_vkToName[] = { + "vk 0x00", + "VK_LBUTTON", + "VK_RBUTTON", + "VK_CANCEL", + "VK_MBUTTON", + "vk 0x05", + "vk 0x06", + "vk 0x07", + "VK_BACK", + "VK_TAB", + "vk 0x0a", + "vk 0x0b", + "VK_CLEAR", + "VK_RETURN", + "vk 0x0e", + "vk 0x0f", + "VK_SHIFT", + "VK_CONTROL", + "VK_MENU", + "VK_PAUSE", + "VK_CAPITAL", + "VK_KANA", + "vk 0x16", + "VK_JUNJA", + "VK_FINAL", + "VK_KANJI", + "vk 0x1a", + "VK_ESCAPE", + "VK_CONVERT", + "VK_NONCONVERT", + "VK_ACCEPT", + "VK_MODECHANGE", + "VK_SPACE", + "VK_PRIOR", + "VK_NEXT", + "VK_END", + "VK_HOME", + "VK_LEFT", + "VK_UP", + "VK_RIGHT", + "VK_DOWN", + "VK_SELECT", + "VK_PRINT", + "VK_EXECUTE", + "VK_SNAPSHOT", + "VK_INSERT", + "VK_DELETE", + "VK_HELP", + "VK_0", + "VK_1", + "VK_2", + "VK_3", + "VK_4", + "VK_5", + "VK_6", + "VK_7", + "VK_8", + "VK_9", + "vk 0x3a", + "vk 0x3b", + "vk 0x3c", + "vk 0x3d", + "vk 0x3e", + "vk 0x3f", + "vk 0x40", + "VK_A", + "VK_B", + "VK_C", + "VK_D", + "VK_E", + "VK_F", + "VK_G", + "VK_H", + "VK_I", + "VK_J", + "VK_K", + "VK_L", + "VK_M", + "VK_N", + "VK_O", + "VK_P", + "VK_Q", + "VK_R", + "VK_S", + "VK_T", + "VK_U", + "VK_V", + "VK_W", + "VK_X", + "VK_Y", + "VK_Z", + "VK_LWIN", + "VK_RWIN", + "VK_APPS", + "vk 0x5e", + "vk 0x5f", + "VK_NUMPAD0", + "VK_NUMPAD1", + "VK_NUMPAD2", + "VK_NUMPAD3", + "VK_NUMPAD4", + "VK_NUMPAD5", + "VK_NUMPAD6", + "VK_NUMPAD7", + "VK_NUMPAD8", + "VK_NUMPAD9", + "VK_MULTIPLY", + "VK_ADD", + "VK_SEPARATOR", + "VK_SUBTRACT", + "VK_DECIMAL", + "VK_DIVIDE", + "VK_F1", + "VK_F2", + "VK_F3", + "VK_F4", + "VK_F5", + "VK_F6", + "VK_F7", + "VK_F8", + "VK_F9", + "VK_F10", + "VK_F11", + "VK_F12", + "VK_F13", + "VK_F14", + "VK_F15", + "VK_F16", + "VK_F17", + "VK_F18", + "VK_F19", + "VK_F20", + "VK_F21", + "VK_F22", + "VK_F23", + "VK_F24", + "vk 0x88", + "vk 0x89", + "vk 0x8a", + "vk 0x8b", + "vk 0x8c", + "vk 0x8d", + "vk 0x8e", + "vk 0x8f", + "VK_NUMLOCK", + "VK_SCROLL", + "vk 0x92", + "vk 0x93", + "vk 0x94", + "vk 0x95", + "vk 0x96", + "vk 0x97", + "vk 0x98", + "vk 0x99", + "vk 0x9a", + "vk 0x9b", + "vk 0x9c", + "vk 0x9d", + "vk 0x9e", + "vk 0x9f", + "VK_LSHIFT", + "VK_RSHIFT", + "VK_LCONTROL", + "VK_RCONTROL", + "VK_LMENU", + "VK_RMENU", + "vk 0xa6", + "vk 0xa7", + "vk 0xa8", + "vk 0xa9", + "vk 0xaa", + "vk 0xab", + "vk 0xac", + "vk 0xad", + "vk 0xae", + "vk 0xaf", + "vk 0xb0", + "vk 0xb1", + "vk 0xb2", + "vk 0xb3", + "vk 0xb4", + "vk 0xb5", + "vk 0xb6", + "vk 0xb7", + "vk 0xb8", + "vk 0xb9", + "vk 0xba", + "vk 0xbb", + "vk 0xbc", + "vk 0xbd", + "vk 0xbe", + "vk 0xbf", + "vk 0xc0", + "vk 0xc1", + "vk 0xc2", + "vk 0xc3", + "vk 0xc4", + "vk 0xc5", + "vk 0xc6", + "vk 0xc7", + "vk 0xc8", + "vk 0xc9", + "vk 0xca", + "vk 0xcb", + "vk 0xcc", + "vk 0xcd", + "vk 0xce", + "vk 0xcf", + "vk 0xd0", + "vk 0xd1", + "vk 0xd2", + "vk 0xd3", + "vk 0xd4", + "vk 0xd5", + "vk 0xd6", + "vk 0xd7", + "vk 0xd8", + "vk 0xd9", + "vk 0xda", + "vk 0xdb", + "vk 0xdc", + "vk 0xdd", + "vk 0xde", + "vk 0xdf", + "vk 0xe0", + "vk 0xe1", + "vk 0xe2", + "vk 0xe3", + "vk 0xe4", + "VK_PROCESSKEY", + "vk 0xe6", + "vk 0xe7", + "vk 0xe8", + "vk 0xe9", + "vk 0xea", + "vk 0xeb", + "vk 0xec", + "vk 0xed", + "vk 0xee", + "vk 0xef", + "vk 0xf0", + "vk 0xf1", + "vk 0xf2", + "vk 0xf3", + "vk 0xf4", + "vk 0xf5", + "VK_ATTN", + "VK_CRSEL", + "VK_EXSEL", + "VK_EREOF", + "VK_PLAY", + "VK_ZOOM", + "VK_NONAME", + "VK_PA1", + "VK_OEM_CLEAR", + "vk 0xff" +}; + // // CMSWindowsPrimaryScreen // @@ -157,6 +420,7 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const // use shadow keyboard state in m_keys for (UInt32 i = 0; i < 256; ++i) { if ((m_keys[i] & 0x80) != 0) { + LOG((CLOG_DEBUG "locked by \"%s\"", g_vkToName[i])); return true; } } @@ -166,6 +430,7 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const for (UInt32 b = 1, j = 0; j < 32; b <<= 1, ++j) { if ((s_mappedKeys[i] & b) != 0) { if (GetAsyncKeyState(i * 32 + j) < 0) { + LOG((CLOG_DEBUG "locked by \"%s\"", g_vkToName[i * 32 + j])); return true; } } diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp index 67611c39..69e91951 100644 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -150,6 +150,7 @@ CXWindowsPrimaryScreen::isLockedToScreen() const &xRoot, &yRoot, &xWindow, &yWindow, &state)) { if ((state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) != 0) { + LOG((CLOG_DEBUG "locked by mouse button")); return true; } } @@ -162,32 +163,32 @@ CXWindowsPrimaryScreen::isLockedToScreen() const // locked if any key is down for (unsigned int i = 0; i < sizeof(keyMap); ++i) { if (keyMap[i] != 0) { - // if any key is half-duplex then it'll be down when - // toggled on but shouldn't count as a reason to lock - // to the screen. - if (m_numLockHalfDuplex || m_capsLockHalfDuplex) { - for (unsigned int j = 0; j < 8; ++j) { - if ((keyMap[i] & (1 << j)) != 0) { - const KeyCode keycode = 8 * i + j; - const KeySym keysym = XKeycodeToKeysym(display, - keycode, 0); - if (m_numLockHalfDuplex && keysym == XK_Num_Lock) { - continue; - } - if (m_capsLockHalfDuplex && keysym == XK_Caps_Lock) { - continue; - } - - // non-half-duplex key down - return true; + for (unsigned int j = 0; j < 8; ++j) { + if ((keyMap[i] & (1 << j)) != 0) { + const KeyCode keycode = 8 * i + j; + const KeySym keysym = XKeycodeToKeysym(display, + keycode, 0); + // if any key is half-duplex then it'll be down when + // toggled on but shouldn't count as a reason to lock + // to the screen. + if (m_numLockHalfDuplex && keysym == XK_Num_Lock) { + continue; } + if (m_capsLockHalfDuplex && keysym == XK_Caps_Lock) { + continue; + } + + // non-half-duplex key down + char* name = XKeysymToString(keysym); + if (name == NULL) { + LOG((CLOG_DEBUG "locked by keycode %d", keycode)); + } + else { + LOG((CLOG_DEBUG "locked by \"%s\"", name)); + } + return true; } - - // only half-duplex keys down - continue; } - - return true; } } diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 65067b1a..2756f025 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -897,13 +897,14 @@ CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) bool CServer::isLockedToScreenNoLock() const { - // locked if primary says we're locked - if (m_primaryClient->isLockedToScreen()) { + // locked if scroll-lock is toggled on + if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) { + LOG((CLOG_DEBUG "locked by ScrollLock")); return true; } - // locked if scroll-lock is toggled on - if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) { + // locked if primary says we're locked + if (m_primaryClient->isLockedToScreen()) { return true; } diff --git a/lib/synergy/CPrimaryScreen.h b/lib/synergy/CPrimaryScreen.h index 0ae68e9d..c370d19b 100644 --- a/lib/synergy/CPrimaryScreen.h +++ b/lib/synergy/CPrimaryScreen.h @@ -175,6 +175,8 @@ public: any other reason that the user should not be allowed to switch screens. Active toggle keys (including the scroll lock key) should not be counted as reasons to lock to the screen. + If this method returns true it should log a message on why at + the CLOG_DEBUG level. */ virtual bool isLockedToScreen() const = 0; From a5633b1971be3f5dc47e2f38a383abbfa8748576 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 Mar 2003 22:32:10 +0000 Subject: [PATCH 469/807] Added options and advanced options dialogs which should've been part of an earlier checkin. Also now saving and restoring options that aren't in the configuration file to/from the registry. --- cmd/launcher/CAdvancedOptions.cpp | 46 +++++++++ cmd/launcher/CAdvancedOptions.h | 2 + cmd/launcher/LaunchUtil.cpp | 12 +++ cmd/launcher/LaunchUtil.h | 2 + cmd/launcher/launcher.cpp | 67 +++++++++++-- cmd/launcher/launcher.rc | 85 +++++++++++++--- cmd/launcher/resource.h | 5 +- lib/arch/CArchDaemonWindows.cpp | 160 ++++++------------------------ lib/arch/CArchDaemonWindows.h | 8 -- lib/arch/CArchMiscWindows.cpp | 143 ++++++++++++++++++++++++-- lib/arch/CArchMiscWindows.h | 35 +++++++ 11 files changed, 396 insertions(+), 169 deletions(-) diff --git a/cmd/launcher/CAdvancedOptions.cpp b/cmd/launcher/CAdvancedOptions.cpp index a6742ea1..6ffb37c4 100644 --- a/cmd/launcher/CAdvancedOptions.cpp +++ b/cmd/launcher/CAdvancedOptions.cpp @@ -16,6 +16,7 @@ #include "ProtocolTypes.h" #include "CStringUtil.h" #include "CArch.h" +#include "CArchMiscWindows.h" #include "CAdvancedOptions.h" #include "LaunchUtil.h" #include "resource.h" @@ -96,6 +97,28 @@ CAdvancedOptions::getCommandLine(bool isClient, const CString& serverName) const void CAdvancedOptions::init(HWND hwnd) { + // get values from registry + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + DWORD newPort = CArchMiscWindows::readValueInt(key, "port"); + CString newName = CArchMiscWindows::readValueString(key, "name"); + if (newPort != 0) { + m_port = static_cast(newPort); + } + if (!newName.empty()) { + m_screenName = newName; + } + CArchMiscWindows::closeKey(key); + } + + // now set GUI + doInit(hwnd); +} + +void +CAdvancedOptions::doInit(HWND hwnd) +{ + // set values in GUI HWND child; char buffer[20]; sprintf(buffer, "%d", m_port); @@ -144,9 +167,28 @@ CAdvancedOptions::save(HWND hwnd) m_screenName = name; m_port = port; + // save values to registry + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + CArchMiscWindows::setValue(key, "port", m_port); + CArchMiscWindows::setValue(key, "name", m_screenName); + CArchMiscWindows::closeKey(key); + } + return true; } +void +CAdvancedOptions::setDefaults(HWND hwnd) +{ + // restore defaults + m_screenName = ARCH->getHostName(); + m_port = kDefaultPort; + + // update GUI + doInit(hwnd); +} + BOOL CAdvancedOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) { @@ -166,6 +208,10 @@ CAdvancedOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) case IDCANCEL: EndDialog(hwnd, 0); return TRUE; + + case IDC_ADVANCED_DEFAULTS: + setDefaults(hwnd); + return TRUE; } break; diff --git a/cmd/launcher/CAdvancedOptions.h b/cmd/launcher/CAdvancedOptions.h index f073f821..e4106f44 100644 --- a/cmd/launcher/CAdvancedOptions.h +++ b/cmd/launcher/CAdvancedOptions.h @@ -55,7 +55,9 @@ public: private: void init(HWND hwnd); + void doInit(HWND hwnd); bool save(HWND hwnd); + void setDefaults(HWND hwnd); // message handling BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp index 6a41f0c5..46c8ef97 100644 --- a/cmd/launcher/LaunchUtil.cpp +++ b/cmd/launcher/LaunchUtil.cpp @@ -214,3 +214,15 @@ saveConfig(const CConfig& config, bool sysOnly) return false; } + +const TCHAR* const* +getSettingsPath() +{ + static const TCHAR* s_keyNames[] = { + TEXT("Software"), + TEXT("Synergy"), + TEXT("Synergy"), + NULL + }; + return s_keyNames; +} diff --git a/cmd/launcher/LaunchUtil.h b/cmd/launcher/LaunchUtil.h index a7638a10..48889264 100644 --- a/cmd/launcher/LaunchUtil.h +++ b/cmd/launcher/LaunchUtil.h @@ -50,4 +50,6 @@ CString getAppPath(const CString& appName); bool loadConfig(CConfig& config); bool saveConfig(const CConfig& config, bool sysOnly); +const TCHAR* const* getSettingsPath(); + #endif diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index fe13e452..587a3b84 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -19,6 +19,7 @@ #include "CLog.h" #include "CStringUtil.h" #include "CArch.h" +#include "CArchMiscWindows.h" #include "Version.h" #include "stdvector.h" #include "resource.h" @@ -703,12 +704,39 @@ initMainWindow(HWND hwnd) ARG->m_oldConfig = ARG->m_config; enableSaveControls(hwnd); + // get settings from registry + bool isServer = configLoaded; + int debugLevel = s_defaultDebug; + CString server; + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + if (isServer && CArchMiscWindows::hasValue(key, "isServer")) { + isServer = (CArchMiscWindows::readValueInt(key, "isServer") != 0); + } + if (CArchMiscWindows::hasValue(key, "debug")) { + debugLevel = static_cast( + CArchMiscWindows::readValueInt(key, "debug")); + if (debugLevel < 0) { + debugLevel = 0; + } + else if (debugLevel > CLog::kDEBUG2) { + debugLevel = CLog::kDEBUG2; + } + } + server = CArchMiscWindows::readValueString(key, "server"); + CArchMiscWindows::closeKey(key); + } + // choose client/server radio buttons HWND child; child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO); - setItemChecked(child, !configLoaded); + setItemChecked(child, !isServer); child = getItem(hwnd, IDC_MAIN_SERVER_RADIO); - setItemChecked(child, configLoaded); + setItemChecked(child, isServer); + + // set server name + child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + setWindowText(child, server); // if config is loaded then initialize server controls if (configLoaded) { @@ -728,13 +756,31 @@ initMainWindow(HWND hwnd) sizeof(s_debugName[0]); ++i) { SendMessage(child, CB_ADDSTRING, 0, (LPARAM)s_debugName[i][0]); } - SendMessage(child, CB_SETCURSEL, s_defaultDebug, 0); + SendMessage(child, CB_SETCURSEL, debugLevel, 0); // update neighbor combo boxes enableMainWindowControls(hwnd); updateNeighbors(hwnd); } +static +void +saveMainWindow(HWND hwnd) +{ + HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + if (key != NULL) { + HWND child; + child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); + CArchMiscWindows::setValue(key, "server", getWindowText(child)); + child = getItem(hwnd, IDC_MAIN_DEBUG); + CArchMiscWindows::setValue(key, "debug", + SendMessage(child, CB_GETCURSEL, 0, 0)); + CArchMiscWindows::setValue(key, "isServer", + isClientChecked(hwnd) ? 0 : 1); + CArchMiscWindows::closeKey(key); + } +} + static BOOL CALLBACK addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) @@ -1128,16 +1174,16 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) RegisterClassEx(&classInfo); // create main window - HWND m_mainWindow = CreateDialog(s_instance, + HWND mainWindow = CreateDialog(s_instance, MAKEINTRESOURCE(IDD_MAIN), 0, NULL); // prep windows - initMainWindow(m_mainWindow); - s_globalOptions = new CGlobalOptions(m_mainWindow, &ARG->m_config); - s_advancedOptions = new CAdvancedOptions(m_mainWindow, &ARG->m_config); + initMainWindow(mainWindow); + s_globalOptions = new CGlobalOptions(mainWindow, &ARG->m_config); + s_advancedOptions = new CAdvancedOptions(mainWindow, &ARG->m_config); // show window - ShowWindow(m_mainWindow, nCmdShow); + ShowWindow(mainWindow, nCmdShow); // main loop MSG msg; @@ -1154,7 +1200,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) break; default: - if (!IsDialogMessage(m_mainWindow, &msg)) { + if (!IsDialogMessage(mainWindow, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } @@ -1162,5 +1208,8 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) } } while (!done); + // save values to registry + saveMainWindow(mainWindow); + return msg.wParam; } diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index b9840a49..4bc02fad 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -62,7 +62,7 @@ BEGIN IDC_STATIC,7,7,286,19 GROUPBOX "",IDC_STATIC,7,29,286,31 GROUPBOX "",IDC_STATIC,7,67,286,103 - GROUPBOX "Advanced Options",IDC_STATIC,7,177,286,56 + GROUPBOX "Options",IDC_STATIC,7,177,286,56 CONTROL "&Client",IDC_MAIN_CLIENT_RADIO,"Button", BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,29,33,10 CONTROL "Server",IDC_MAIN_SERVER_RADIO,"Button", @@ -79,26 +79,23 @@ BEGIN PUSHBUTTON "&Remove",IDC_MAIN_SERVER_REMOVE_BUTTON,12,150,50,14 LTEXT "&Layout:",IDC_MAIN_SERVER_LAYOUT_LABEL,138,79,24,8 LTEXT "Left:",IDC_MAIN_SERVER_LEFT_LABEL,144,93,15,8 - COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,118,46, + COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,111,46, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP LTEXT "Right:",IDC_MAIN_SERVER_RIGHT_LABEL,144,109,20,8 - COMBOBOX IDC_MAIN_SERVER_RIGHT_COMBO,175,107,118,46, + COMBOBOX IDC_MAIN_SERVER_RIGHT_COMBO,175,107,112,46, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP LTEXT "Above:",IDC_MAIN_SERVER_TOP_LABEL,144,125,24,8 - COMBOBOX IDC_MAIN_SERVER_TOP_COMBO,175,123,118,46, + COMBOBOX IDC_MAIN_SERVER_TOP_COMBO,175,123,112,46, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP LTEXT "Below:",IDC_MAIN_SERVER_BOTTOM_LABEL,144,141,22,8 - COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,118,46, + COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,112,46, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Screen &Name:",IDC_STATIC,12,192,46,8 - EDITTEXT IDC_MAIN_ADVANCED_NAME_EDIT,64,190,106,12,ES_AUTOHSCROLL - LTEXT "&Port:",IDC_STATIC,194,192,16,8 - EDITTEXT IDC_MAIN_ADVANCED_PORT_EDIT,216,190,40,12,ES_AUTOHSCROLL | - ES_NUMBER - LTEXT "Automatic Startup:",IDC_STATIC,12,212,59,8 - PUSHBUTTON "Con&figure...",IDC_MAIN_AUTOSTART,78,210,50,14 - LTEXT "Test &Debug Level:",IDC_STATIC,151,212,60,8 - COMBOBOX IDC_MAIN_DEBUG,216,210,61,60,CBS_DROPDOWNLIST | + PUSHBUTTON "&Options...",IDC_MAIN_OPTIONS,12,191,50,14 + PUSHBUTTON "Adva&nced...",IDC_MAIN_ADVANCED,68,191,50,14 + LTEXT "Automatic Startup:",IDC_STATIC,138,193,59,8 + PUSHBUTTON "Con&figure...",IDC_MAIN_AUTOSTART,202,191,50,14 + LTEXT "Test &Debug Level:",IDC_STATIC,12,216,60,8 + COMBOBOX IDC_MAIN_DEBUG,78,213,61,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Sa&ve",IDC_MAIN_SAVE,75,241,50,14 DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,241,50,14 @@ -157,6 +154,50 @@ BEGIN IDC_STATIC,7,43,181,17 END +IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 148 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "It's easy to unintentionally switch screens when the pointer is near a screen's edge. Synergy can prevent switching until certain conditions are met to reduce unintentional switching.", + IDC_STATIC,7,7,191,26 + LTEXT "Synergy can wait to switch until the cursor has been at a screen's edge for some amount of time.", + IDC_STATIC,7,37,193,16 + CONTROL "Switch after waiting",IDC_GLOBAL_DELAY_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,59,77,10 + EDITTEXT IDC_GLOBAL_DELAY_TIME,112,58,45,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms",IDC_STATIC,159,60,10,8 + LTEXT "Synergy can switch only when the cursor hits a screen edge twice within some amount of time.", + IDC_STATIC,7,77,193,16 + CONTROL "Switch on double tap within",IDC_GLOBAL_TWO_TAP_CHECK, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,99,103,10 + EDITTEXT IDC_GLOBAL_TWO_TAP_TIME,112,98,45,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms",IDC_STATIC,159,100,10,8 + DEFPUSHBUTTON "OK",IDOK,94,127,50,14 + PUSHBUTTON "Cancel",IDCANCEL,150,127,50,14 +END + +IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 133 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Advanced Options" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Synergy normally uses this computer's name as its screen name. Enter another name here if you want to use a different screen name.", + IDC_STATIC,7,7,216,19 + LTEXT "Screen &Name:",IDC_STATIC,7,34,46,8 + EDITTEXT IDC_ADVANCED_NAME_EDIT,63,32,106,12,ES_AUTOHSCROLL + LTEXT "Synergy normally uses a particular network port number. Enter an alternative port here. (The server and all clients must use the same port number.)", + IDC_STATIC,7,56,216,26 + LTEXT "&Port:",IDC_STATIC,7,90,16,8 + EDITTEXT IDC_ADVANCED_PORT_EDIT,63,88,40,12,ES_AUTOHSCROLL | + ES_NUMBER + PUSHBUTTON "&Defaults",IDC_ADVANCED_DEFAULTS,7,112,50,14 + DEFPUSHBUTTON "OK",IDOK,118,112,50,14 + PUSHBUTTON "Cancel",IDCANCEL,173,112,50,14 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -197,6 +238,22 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 182 END + + IDD_GLOBAL_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 200 + TOPMARGIN, 7 + BOTTOMMARGIN, 141 + END + + IDD_ADVANCED_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 223 + TOPMARGIN, 7 + BOTTOMMARGIN, 126 + END END #endif // APSTUDIO_INVOKED diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index 6e1c1aba..c141e686 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -95,15 +95,16 @@ #define IDC_ADD_MOD_ALT 1045 #define IDC_ADD_MOD_META 1046 #define IDC_ADD_MOD_SUPER 1047 +#define IDC_ADVANCED_DEFAULTS 1049 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_RESOURCE_VALUE 110 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1044 +#define _APS_NEXT_CONTROL_VALUE 1050 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index 6f4f14e3..54dc5b40 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -93,10 +93,10 @@ CArchDaemonWindows::installDaemon(const char* name, value += commandLine; // install entry - setValue(key, name, value); + CArchMiscWindows::setValue(key, name, value); // clean up - closeKey(key); + CArchMiscWindows::closeKey(key); } // windows NT family services @@ -137,7 +137,7 @@ CArchDaemonWindows::installDaemon(const char* name, // open the registry key for this service HKEY key = openNTServicesKey(); - key = openKey(key, name); + key = CArchMiscWindows::openKey(key, name); if (key == NULL) { // can't open key DWORD err = GetLastError(); @@ -151,14 +151,14 @@ CArchDaemonWindows::installDaemon(const char* name, } // set the description - setValue(key, _T("Description"), description); + CArchMiscWindows::setValue(key, _T("Description"), description); // set command line - key = openKey(key, _T("Parameters")); + key = CArchMiscWindows::openKey(key, _T("Parameters")); if (key == NULL) { // can't open key DWORD err = GetLastError(); - closeKey(key); + CArchMiscWindows::closeKey(key); try { uninstallDaemon(name, allUsers); } @@ -167,10 +167,10 @@ CArchDaemonWindows::installDaemon(const char* name, } throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); } - setValue(key, _T("CommandLine"), commandLine); + CArchMiscWindows::setValue(key, _T("CommandLine"), commandLine); // done with registry - closeKey(key); + CArchMiscWindows::closeKey(key); } } @@ -189,20 +189,20 @@ CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) } // remove entry - deleteValue(key, name); + CArchMiscWindows::deleteValue(key, name); // clean up - closeKey(key); + CArchMiscWindows::closeKey(key); } // windows NT family services else { // remove parameters for this service. ignore failures. HKEY key = openNTServicesKey(); - key = openKey(key, name); + key = CArchMiscWindows::openKey(key, name); if (key != NULL) { - deleteKey(key, _T("Parameters")); - closeKey(key); + CArchMiscWindows::deleteKey(key, _T("Parameters")); + CArchMiscWindows::closeKey(key); } // open service manager @@ -314,7 +314,7 @@ CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) // check if we can open the registry key HKEY key = CArchMiscWindows::isWindows95Family() ? open95ServicesKey() : openUserStartupKey(); - closeKey(key); + CArchMiscWindows::closeKey(key); return (key != NULL); } @@ -329,9 +329,9 @@ CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) // check if we can open the registry key for this service HKEY key = openNTServicesKey(); - key = openKey(key, name); - key = openKey(key, _T("Parameters")); - closeKey(key); + key = CArchMiscWindows::openKey(key, name); + key = CArchMiscWindows::openKey(key, _T("Parameters")); + CArchMiscWindows::closeKey(key); return (key != NULL); } @@ -351,10 +351,11 @@ CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers) } // check for entry - const bool installed = !readValueString(key, name).empty(); + const bool installed = !CArchMiscWindows::readValueString(key, + name).empty(); // clean up - closeKey(key); + CArchMiscWindows::closeKey(key); return installed; } @@ -363,12 +364,12 @@ CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers) else { // check parameters for this service HKEY key = openNTServicesKey(); - key = openKey(key, name); - key = openKey(key, _T("Parameters")); + key = CArchMiscWindows::openKey(key, name); + key = CArchMiscWindows::openKey(key, _T("Parameters")); if (key != NULL) { - const bool installed = !readValueString(key, + const bool installed = !CArchMiscWindows::readValueString(key, _T("CommandLine")).empty(); - closeKey(key); + CArchMiscWindows::closeKey(key); if (!installed) { return false; } @@ -393,106 +394,6 @@ CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers) } } -HKEY -CArchDaemonWindows::openKey(HKEY key, const TCHAR* keyName) -{ - // ignore if parent is NULL - if (key == NULL) { - return NULL; - } - - // open next key - HKEY newKey; - LONG result = RegOpenKeyEx(key, keyName, 0, - KEY_WRITE | KEY_QUERY_VALUE, &newKey); - if (result != ERROR_SUCCESS) { - DWORD disp; - result = RegCreateKeyEx(key, keyName, 0, _T(""), - 0, KEY_WRITE | KEY_QUERY_VALUE, - NULL, &newKey, &disp); - } - if (result != ERROR_SUCCESS) { - RegCloseKey(key); - return NULL; - } - - // switch to new key - RegCloseKey(key); - return newKey; -} - -HKEY -CArchDaemonWindows::openKey(HKEY key, const TCHAR** keyNames) -{ - for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) { - // open next key - key = openKey(key, keyNames[i]); - } - return key; -} - -void -CArchDaemonWindows::closeKey(HKEY key) -{ - assert(key != NULL); - RegCloseKey(key); -} - -void -CArchDaemonWindows::deleteKey(HKEY key, const TCHAR* name) -{ - assert(key != NULL); - assert(name != NULL); - RegDeleteKey(key, name); -} - -void -CArchDaemonWindows::deleteValue(HKEY key, const TCHAR* name) -{ - assert(key != NULL); - assert(name != NULL); - RegDeleteValue(key, name); -} - -void -CArchDaemonWindows::setValue(HKEY key, - const TCHAR* name, const std::string& value) -{ - assert(key != NULL); - assert(name != NULL); - RegSetValueEx(key, name, 0, REG_SZ, - reinterpret_cast(value.c_str()), - value.size() + 1); -} - -std::string -CArchDaemonWindows::readValueString(HKEY key, const TCHAR* name) -{ - // get the size of the string - DWORD type; - DWORD size = 0; - LONG result = RegQueryValueEx(key, name, 0, &type, NULL, &size); - if (result != ERROR_SUCCESS || type != REG_SZ) { - return std::string(); - } - - // allocate space - char* buffer = new char[size]; - - // read it - result = RegQueryValueEx(key, name, 0, &type, - reinterpret_cast(buffer), &size); - if (result != ERROR_SUCCESS || type != REG_SZ) { - delete[] buffer; - return std::string(); - } - - // clean up and return value - std::string value(buffer); - delete[] buffer; - return value; -} - HKEY CArchDaemonWindows::openNTServicesKey() { @@ -503,7 +404,7 @@ CArchDaemonWindows::openNTServicesKey() NULL }; - return openKey(HKEY_LOCAL_MACHINE, s_keyNames); + return CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_keyNames); } HKEY @@ -518,7 +419,7 @@ CArchDaemonWindows::open95ServicesKey() NULL }; - return openKey(HKEY_LOCAL_MACHINE, s_keyNames); + return CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_keyNames); } HKEY @@ -533,7 +434,7 @@ CArchDaemonWindows::openUserStartupKey() NULL }; - return openKey(HKEY_CURRENT_USER, s_keyNames); + return CArchMiscWindows::openKey(HKEY_CURRENT_USER, s_keyNames); } int @@ -730,10 +631,11 @@ CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) // read command line std::string commandLine; HKEY key = openNTServicesKey(); - key = openKey(key, argvIn[0]); - key = openKey(key, _T("Parameters")); + key = CArchMiscWindows::openKey(key, argvIn[0]); + key = CArchMiscWindows::openKey(key, _T("Parameters")); if (key != NULL) { - commandLine = readValueString(key, _T("CommandLine")); + commandLine = CArchMiscWindows::readValueString(key, + _T("CommandLine")); } // if the command line isn't empty then parse and use it diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h index 9c291c9e..57fe74fe 100644 --- a/lib/arch/CArchDaemonWindows.h +++ b/lib/arch/CArchDaemonWindows.h @@ -75,14 +75,6 @@ public: virtual bool isDaemonInstalled(const char* name, bool allUsers); private: - static HKEY openKey(HKEY parent, const TCHAR*); - static HKEY openKey(HKEY parent, const TCHAR**); - static void closeKey(HKEY); - static void deleteKey(HKEY, const TCHAR* name); - static void deleteValue(HKEY, const TCHAR* name); - static void setValue(HKEY, const TCHAR* name, - const std::string& value); - static std::string readValueString(HKEY, const TCHAR* name); static HKEY openNTServicesKey(); static HKEY open95ServicesKey(); static HKEY openUserStartupKey(); diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index 40edd53e..cc91d025 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -12,11 +12,7 @@ * GNU General Public License for more details. */ -#define WIN32_LEAN_AND_MEAN - #include "CArchMiscWindows.h" -#include "CArchDaemonWindows.h" -#include // // CArchMiscWindows @@ -37,17 +33,150 @@ CArchMiscWindows::isWindows95Family() int CArchMiscWindows::runDaemon(RunFunc runFunc) { - return CArchDaemonWindows::runDaemon(runFunc); + return CArchMiscWindows::runDaemon(runFunc); } void CArchMiscWindows::daemonRunning(bool running) { - CArchDaemonWindows::daemonRunning(running); + CArchMiscWindows::daemonRunning(running); } void CArchMiscWindows::daemonFailed(int result) { - CArchDaemonWindows::daemonFailed(result); + CArchMiscWindows::daemonFailed(result); +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) +{ + // ignore if parent is NULL + if (key == NULL) { + return NULL; + } + + // open next key + HKEY newKey; + LONG result = RegOpenKeyEx(key, keyName, 0, + KEY_WRITE | KEY_QUERY_VALUE, &newKey); + if (result != ERROR_SUCCESS) { + DWORD disp; + result = RegCreateKeyEx(key, keyName, 0, TEXT(""), + 0, KEY_WRITE | KEY_QUERY_VALUE, + NULL, &newKey, &disp); + } + if (result != ERROR_SUCCESS) { + RegCloseKey(key); + return NULL; + } + + // switch to new key + RegCloseKey(key); + return newKey; +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames) +{ + for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) { + // open next key + key = openKey(key, keyNames[i]); + } + return key; +} + +void +CArchMiscWindows::closeKey(HKEY key) +{ + assert(key != NULL); + RegCloseKey(key); +} + +void +CArchMiscWindows::deleteKey(HKEY key, const TCHAR* name) +{ + assert(key != NULL); + assert(name != NULL); + RegDeleteKey(key, name); +} + +void +CArchMiscWindows::deleteValue(HKEY key, const TCHAR* name) +{ + assert(key != NULL); + assert(name != NULL); + RegDeleteValue(key, name); +} + +bool +CArchMiscWindows::hasValue(HKEY key, const TCHAR* name) +{ + DWORD type; + LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL); + return (result == ERROR_SUCCESS && + (type == REG_DWORD || type == REG_SZ)); +} + +void +CArchMiscWindows::setValue(HKEY key, + const TCHAR* name, const std::string& value) +{ + assert(key != NULL); + assert(name != NULL); + RegSetValueEx(key, name, 0, REG_SZ, + reinterpret_cast(value.c_str()), + value.size() + 1); +} + +void +CArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value) +{ + assert(key != NULL); + assert(name != NULL); + RegSetValueEx(key, name, 0, REG_DWORD, + reinterpret_cast(&value), + sizeof(DWORD)); +} + +std::string +CArchMiscWindows::readValueString(HKEY key, const TCHAR* name) +{ + // get the size of the string + DWORD type; + DWORD size = 0; + LONG result = RegQueryValueEx(key, name, 0, &type, NULL, &size); + if (result != ERROR_SUCCESS || type != REG_SZ) { + return std::string(); + } + + // allocate space + char* buffer = new char[size]; + + // read it + result = RegQueryValueEx(key, name, 0, &type, + reinterpret_cast(buffer), &size); + if (result != ERROR_SUCCESS || type != REG_SZ) { + delete[] buffer; + return std::string(); + } + + // clean up and return value + std::string value(buffer); + delete[] buffer; + return value; +} + +DWORD +CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name) +{ + DWORD type; + DWORD value; + DWORD size = sizeof(value); + LONG result = RegQueryValueEx(key, name, 0, &type, + reinterpret_cast(&value), &size); + if (result != ERROR_SUCCESS || type != REG_DWORD) { + return 0; + } + return value; } diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index 51433ba4..dc993362 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -15,7 +15,11 @@ #ifndef CARCHMISCWINDOWS_H #define CARCHMISCWINDOWS_H +#define WIN32_LEAN_AND_MEAN + #include "common.h" +#include "stdstring.h" +#include //! Miscellaneous win32 functions. class CArchMiscWindows { @@ -45,6 +49,37 @@ public: Delegates to CArchDaemonWindows. */ static void daemonFailed(int result); + + //! Open and return a registry key, closing the parent key + static HKEY openKey(HKEY parent, const TCHAR* child); + + //! Open and return a registry key, closing the parent key + static HKEY openKey(HKEY parent, const TCHAR* const* keyPath); + + //! Close a key + static void closeKey(HKEY); + + //! Delete a key (which should have no subkeys) + static void deleteKey(HKEY parent, const TCHAR* name); + + //! Delete a value + static void deleteValue(HKEY parent, const TCHAR* name); + + //! Test if a value exists + static bool hasValue(HKEY key, const TCHAR* name); + + //! Set a string value in the registry + static void setValue(HKEY key, const TCHAR* name, + const std::string& value); + + //! Set a DWORD value in the registry + static void setValue(HKEY key, const TCHAR* name, DWORD value); + + //! Read a string value from the registry + static std::string readValueString(HKEY, const TCHAR* name); + + //! Read a DWORD value from the registry + static DWORD readValueInt(HKEY, const TCHAR* name); }; #endif From 995c9547cf28d3113bbd9fa66867319b11293615 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Mar 2003 19:13:15 +0000 Subject: [PATCH 470/807] Fixed getWindowProperty(). It wasn't catching all failure cases correctly. --- lib/platform/CXWindowsUtil.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 90d0b0aa..399f2c27 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -35,7 +35,7 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, CXWindowsUtil::CErrorLock lock(display); // read the property - int result = Success; + bool okay = true; const long length = XMaxRequestSize(display); long offset = 0; unsigned long bytesLeft = 1; @@ -43,12 +43,13 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, // get more data unsigned long numItems; unsigned char* rawData; - result = XGetWindowProperty(display, window, property, + if (XGetWindowProperty(display, window, property, offset, length, False, AnyPropertyType, &actualType, &actualDatumSize, - &numItems, &bytesLeft, &rawData); - if (result != Success || actualType == None || actualDatumSize == 0) { + &numItems, &bytesLeft, &rawData) != Success || + actualType == None || actualDatumSize == 0) { // failed + okay = false; break; } @@ -98,12 +99,12 @@ CXWindowsUtil::getWindowProperty(Display* display, Window window, *format = static_cast(actualDatumSize); } - if (result == Success) { + if (okay) { LOG((CLOG_DEBUG2 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size())); return true; } else { - LOG((CLOG_DEBUG1 "can't read property %d on window 0x%08x", property, window)); + LOG((CLOG_DEBUG2 "can't read property %d on window 0x%08x", property, window)); return false; } } From 7684b35c4f511a9b67cf45fc0eb98e124fec9630 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Mar 2003 19:14:10 +0000 Subject: [PATCH 471/807] Fixed icons. --- cmd/synergyc/tb_idle.ico | Bin 318 -> 318 bytes cmd/synergyc/tb_run.ico | Bin 318 -> 318 bytes cmd/synergyc/tb_wait.ico | Bin 318 -> 318 bytes cmd/synergys/tb_idle.ico | Bin 318 -> 318 bytes cmd/synergys/tb_run.ico | Bin 318 -> 318 bytes cmd/synergys/tb_wait.ico | Bin 318 -> 318 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/cmd/synergyc/tb_idle.ico b/cmd/synergyc/tb_idle.ico index 3625257e4afe43f9ccdbbe3e6e2f541105871f3f..9ba599de07841a8899275e54b91383ebdba39fb1 100644 GIT binary patch delta 19 bcmdnTw2x`RX3iO$b52g!y>lkR#63*_R|g3{ delta 19 bcmdnTw2x`RX3hzmb52g!y>lk_#63*_R?`VW diff --git a/cmd/synergyc/tb_run.ico b/cmd/synergyc/tb_run.ico index 228555a90a474a73d95782a9d72dc4deefd2b409..86aa9f3ac989981768b950663e06469635252f8e 100644 GIT binary patch delta 19 bcmdnTw2x`RW=;+Uo-+))cW+>rxTgsKMqUS^ delta 19 bcmdnTw2x`RW=;kMo-+))cW>aHxTgsKMk)uT diff --git a/cmd/synergyc/tb_wait.ico b/cmd/synergyc/tb_wait.ico index ed56a5f0289dcd9ae1ca9d7b303ebb1b8dcedc97..3e2edd5f305fecd3883d8d44892dcf091156b8ee 100644 GIT binary patch delta 74 zcmdnTw2x`RJO>wt0tN<-KW9L+0hnemHsE03abYkvHUKgmjKP41*_eUZ*qGsr1499j N30BVx(l_y50ssiW6$}6X delta 74 zcmdnTw2x`RJcly}3K$qTzPNyB12D~CY{0<4a)!a!*nrvC;D9k0urM1lFdG{)xEx?8 P05ZYqnL+v{-b(-gqlkR#63*_R|g3{ delta 19 bcmdnTw2x`RX3hzmb52g!y>lk_#63*_R?`VW diff --git a/cmd/synergys/tb_run.ico b/cmd/synergys/tb_run.ico index 228555a90a474a73d95782a9d72dc4deefd2b409..86aa9f3ac989981768b950663e06469635252f8e 100644 GIT binary patch delta 19 bcmdnTw2x`RW=;+Uo-+))cW+>rxTgsKMqUS^ delta 19 bcmdnTw2x`RW=;kMo-+))cW>aHxTgsKMk)uT diff --git a/cmd/synergys/tb_wait.ico b/cmd/synergys/tb_wait.ico index ed56a5f0289dcd9ae1ca9d7b303ebb1b8dcedc97..e94db3c30602626ad420b8a3a37d1bc0750baff8 100644 GIT binary patch delta 19 bcmdnTw2x`RW=;+UmNN{-#s&-%_cQ?jJf;Q; delta 19 bcmdnTw2x`RW=;kMmNN{-#sV) From 95263289acb8b53cc5369b1ae93ef97c5b3dadc2 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Mar 2003 19:14:32 +0000 Subject: [PATCH 472/807] Fixed errors in merge causing infinite loops. --- lib/arch/CArchMiscWindows.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index cc91d025..f76b42f1 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -13,6 +13,7 @@ */ #include "CArchMiscWindows.h" +#include "CArchDaemonWindows.h" // // CArchMiscWindows @@ -33,19 +34,19 @@ CArchMiscWindows::isWindows95Family() int CArchMiscWindows::runDaemon(RunFunc runFunc) { - return CArchMiscWindows::runDaemon(runFunc); + return CArchDaemonWindows::runDaemon(runFunc); } void CArchMiscWindows::daemonRunning(bool running) { - CArchMiscWindows::daemonRunning(running); + CArchDaemonWindows::daemonRunning(running); } void CArchMiscWindows::daemonFailed(int result) { - CArchMiscWindows::daemonFailed(result); + CArchDaemonWindows::daemonFailed(result); } HKEY From 9dabd425a50ad348caf78f13fee5f74c6d94f188 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Mar 2003 19:16:37 +0000 Subject: [PATCH 473/807] Added check for the screen saver actually being active before entering the loop waiting for it to deactivate. The failure to check was causing the screen saver code to kick in when the screen saver timeout occurred, even if the screen saver wasn't enabled (because Windows still sends the screen saver activating message for no good reason when the screen saver is disabled). --- lib/platform/CMSWindowsScreenSaver.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index e2d4ab3e..14e0bac3 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -100,6 +100,15 @@ CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) // on the windows nt family we wait for the desktop to // change until it's neither the Screen-Saver desktop // nor a desktop we can't open (the login desktop). + // since windows will send the request-to-start-screen- + // saver message even when the screen saver is disabled + // we first check that the screen saver is indeed active + // before watching for it to stop. + if (!isActive()) { + LOG((CLOG_DEBUG "can't open screen saver desktop")); + return false; + } + watchDesktop(); } From a6cb8769ba42ecd183b522b3cd9349478bc51d6d Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 21 Mar 2003 19:34:08 +0000 Subject: [PATCH 474/807] Oops, included a windows only header in non-windows builds. --- cmd/synergyc/synergyc.cpp | 2 +- cmd/synergys/synergys.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index bc0125c4..095b62ea 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -30,13 +30,13 @@ #include "LogOutputters.h" #include "CString.h" #include "CArch.h" -#include "CArchMiscWindows.h" #include #define DAEMON_RUNNING(running_) #if WINDOWS_LIKE #include "CMSWindowsScreen.h" #include "CMSWindowsSecondaryScreen.h" +#include "CArchMiscWindows.h" #include "CMSWindowsClientTaskBarReceiver.h" #include "resource.h" #undef DAEMON_RUNNING diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index f2c4924e..3adc9f0d 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -28,7 +28,6 @@ #include "CLog.h" #include "LogOutputters.h" #include "CArch.h" -#include "CArchMiscWindows.h" #include "stdfstream.h" #include @@ -36,6 +35,7 @@ #if WINDOWS_LIKE #include "CMSWindowsScreen.h" #include "CMSWindowsPrimaryScreen.h" +#include "CArchMiscWindows.h" #include "CMSWindowsServerTaskBarReceiver.h" #include "resource.h" #undef DAEMON_RUNNING From 68ecf48a70edaac2f4a1df65f6d4d379877ecec0 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Mar 2003 11:49:13 +0000 Subject: [PATCH 475/807] Added key modifier and heartbeat options to GUI. --- cmd/launcher/CGlobalOptions.cpp | 51 ++++++++++++++++++++++++++++----- cmd/launcher/CGlobalOptions.h | 1 + cmd/launcher/launcher.rc | 41 ++++++++++++++++++++------ cmd/launcher/resource.h | 4 ++- 4 files changed, 80 insertions(+), 17 deletions(-) diff --git a/cmd/launcher/CGlobalOptions.cpp b/cmd/launcher/CGlobalOptions.cpp index 445b07a4..085a56d3 100644 --- a/cmd/launcher/CGlobalOptions.cpp +++ b/cmd/launcher/CGlobalOptions.cpp @@ -20,7 +20,8 @@ #include "LaunchUtil.h" #include "resource.h" -static const int s_defaultDelay = 250; +static const int s_defaultDelay = 250; +static const int s_defaultHeartbeat = 5000; // // CGlobalOptions @@ -32,7 +33,8 @@ CGlobalOptions::CGlobalOptions(HWND parent, CConfig* config) : m_parent(parent), m_config(config), m_delayTime(s_defaultDelay), - m_twoTapTime(s_defaultDelay) + m_twoTapTime(s_defaultDelay), + m_heartbeatTime(s_defaultHeartbeat) { assert(s_singleton == NULL); s_singleton = this; @@ -68,6 +70,11 @@ CGlobalOptions::init(HWND hwnd) setItemChecked(child, false); child = getItem(hwnd, IDC_GLOBAL_TWO_TAP_TIME); setWindowText(child, buffer); + sprintf(buffer, "%d", m_heartbeatTime); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + setWindowText(child, buffer); // get the global options const CConfig::CScreenOptions* options = m_config->getOptions(""); @@ -94,6 +101,15 @@ CGlobalOptions::init(HWND hwnd) setWindowText(child, buffer); } } + else if (id == kOptionHeartbeat) { + if (value > 0) { + sprintf(buffer, "%d", value); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + setItemChecked(child, true); + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + setWindowText(child, buffer); + } + } } } } @@ -102,8 +118,9 @@ bool CGlobalOptions::save(HWND hwnd) { HWND child; - int newDelayTime = 0; - int newTwoTapTime = 0; + int newDelayTime = 0; + int newTwoTapTime = 0; + int newHeartbeatTime = 0; // get requested options child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); @@ -136,10 +153,26 @@ CGlobalOptions::save(HWND hwnd) newTwoTapTime = s_defaultDelay; } } + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + if (isItemChecked(child)) { + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + newHeartbeatTime = getTime(hwnd, child, true); + if (newHeartbeatTime == 0) { + return false; + } + } + else { + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); + newHeartbeatTime = getTime(hwnd, child, false); + if (newHeartbeatTime == 0) { + newHeartbeatTime = s_defaultHeartbeat; + } + } // remove existing config options m_config->removeOption("", kOptionScreenSwitchDelay); m_config->removeOption("", kOptionScreenSwitchTwoTap); + m_config->removeOption("", kOptionHeartbeat); // add requested options child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); @@ -150,11 +183,15 @@ CGlobalOptions::save(HWND hwnd) if (isItemChecked(child)) { m_config->addOption("", kOptionScreenSwitchTwoTap, newTwoTapTime); } + child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_CHECK); + if (isItemChecked(child)) { + m_config->addOption("", kOptionHeartbeat, newHeartbeatTime); + } // save last values - m_delayTime = newDelayTime; - m_twoTapTime = newTwoTapTime; - + m_delayTime = newDelayTime; + m_twoTapTime = newTwoTapTime; + m_heartbeatTime = newHeartbeatTime; return true; } diff --git a/cmd/launcher/CGlobalOptions.h b/cmd/launcher/CGlobalOptions.h index 520df187..f04f1bae 100644 --- a/cmd/launcher/CGlobalOptions.h +++ b/cmd/launcher/CGlobalOptions.h @@ -61,6 +61,7 @@ private: CConfig* m_config; int m_delayTime; int m_twoTapTime; + int m_heartbeatTime; }; #endif diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 4bc02fad..67b3020e 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -103,7 +103,7 @@ BEGIN PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 END -IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 170 +IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 236 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION CAPTION "Add Screen" FONT 8, "MS Sans Serif" @@ -113,15 +113,31 @@ BEGIN LTEXT "&Aliases:",IDC_STATIC,7,25,25,8 EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,40,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + GROUPBOX "Options",IDC_STATIC,7,72,178,64 LTEXT "If your Caps Lock or Num Lock keys behave strangely on this client screen then try turning the half-duplex options on and reconnect the client.", IDC_STATIC,13,82,165,25 CONTROL "Half-duplex &Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,13,110,165,10 CONTROL "Half-duplex &Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,13,122,165,10 - DEFPUSHBUTTON "OK",IDOK,79,149,50,14 - PUSHBUTTON "Cancel",IDCANCEL,135,149,50,14 - GROUPBOX "Options",IDC_STATIC,7,72,178,64 + GROUPBOX "Modifiers",IDC_STATIC,7,139,178,65 + LTEXT "Shift",IDC_STATIC,13,155,15,8 + COMBOBOX IDC_ADD_MOD_SHIFT,37,152,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Ctrl",IDC_STATIC,13,170,11,8 + COMBOBOX IDC_ADD_MOD_CTRL,37,168,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Alt",IDC_STATIC,13,186,9,8 + COMBOBOX IDC_ADD_MOD_ALT,37,184,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Meta",IDC_STATIC,101,154,17,8 + COMBOBOX IDC_ADD_MOD_META,125,152,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Super",IDC_STATIC,101,170,20,8 + COMBOBOX IDC_ADD_MOD_SUPER,125,168,48,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,79,215,50,14 + PUSHBUTTON "Cancel",IDCANCEL,135,215,50,14 END IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54 @@ -154,7 +170,7 @@ BEGIN IDC_STATIC,7,43,181,17 END -IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 148 +IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 196 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Options" FONT 8, "MS Sans Serif" @@ -175,8 +191,15 @@ BEGIN EDITTEXT IDC_GLOBAL_TWO_TAP_TIME,112,98,45,12,ES_AUTOHSCROLL | ES_NUMBER LTEXT "ms",IDC_STATIC,159,100,10,8 - DEFPUSHBUTTON "OK",IDOK,94,127,50,14 - PUSHBUTTON "Cancel",IDCANCEL,150,127,50,14 + LTEXT "Synergy can periodically check that clients are still alive and connected. Use this only if synergy doesn't detect when clients disconnect.", + IDC_STATIC,7,122,193,24 + CONTROL "Check clients every",IDC_GLOBAL_HEARTBEAT_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,153,78,10 + EDITTEXT IDC_GLOBAL_HEARTBEAT_TIME,112,152,45,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "ms",IDC_STATIC,159,154,10,8 + DEFPUSHBUTTON "OK",IDOK,94,175,50,14 + PUSHBUTTON "Cancel",IDCANCEL,150,175,50,14 END IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 133 @@ -220,7 +243,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 185 TOPMARGIN, 7 - BOTTOMMARGIN, 163 + BOTTOMMARGIN, 229 END IDD_WAIT, DIALOG @@ -244,7 +267,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 200 TOPMARGIN, 7 - BOTTOMMARGIN, 141 + BOTTOMMARGIN, 189 END IDD_ADVANCED_OPTIONS, DIALOG diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index c141e686..9fbe46af 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -93,7 +93,9 @@ #define IDC_GLOBAL_TWO_TAP_TIME 1044 #define IDC_ADD_MOD_CTRL 1044 #define IDC_ADD_MOD_ALT 1045 +#define IDC_GLOBAL_HEARTBEAT_CHECK 1045 #define IDC_ADD_MOD_META 1046 +#define IDC_GLOBAL_HEARTBEAT_TIME 1046 #define IDC_ADD_MOD_SUPER 1047 #define IDC_ADVANCED_DEFAULTS 1049 @@ -104,7 +106,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 110 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1050 +#define _APS_NEXT_CONTROL_VALUE 1052 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif From 380369d331cec45d070cc89a3b4a8fabf1d4c74d Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 22 Mar 2003 11:49:23 +0000 Subject: [PATCH 476/807] Documentation updates. --- FAQ | 28 ++++++++++++- INSTALL | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++------ PORTING | 14 ++++--- README | 36 +++++++++------- TODO | 6 +-- 5 files changed, 171 insertions(+), 37 deletions(-) diff --git a/FAQ b/FAQ index c007188b..ac38f428 100644 --- a/FAQ +++ b/FAQ @@ -16,7 +16,8 @@ Questions 11. Why isn't synergy ported to platform XYZ? 12. My client can't connect. What's wrong? 13. Linking fails on Solaris. What's wrong? - +14. The screen saver never starts. Why not? +15. I can't switch screens anymore for no apparent reason. Why? Answers ------- @@ -163,3 +164,28 @@ Answers `configure' command line? Solaris puts the X11 includes and libraries in an unusual place and the above lets synergy find them. + +14. The screen saver never starts. Why not? + + If the synergy server is on X Windows then the screen saver will + not start while the mouse is on a client screen. This is a + consequence of how X Windows, synergy and xscreensaver work. + +15. I can't switch screens anymore for no apparent reason. Why? + + This sometimes happens but all the causes aren't yet known. One + known cause is if the synergy server in running on Windows + 95/98/Me and a 16-bit application is in the foreground when the + screen saver starts. Windows fails to notify synergy that the + screen saver has started in this situation (which is a bug in + Windows, not synergy) and synergy may intercept some keyboard + input and divert it to a client when the screen saver is running. + As a result, it's possible for the server system to believe a key + is pressed when it really isn't. Typically, it's the return key + and simply tapping it will allow synergy to switch screens again. + + If this problem happens to you, try tapping the enter key to see + if that solves the problem. If not, you can try running with + debug logging (--debug DEBUG) and synergy will report exactly why + it refuses to switch screens. If it claims a key is down then + try tapping that key and see if that solves the problem. diff --git a/INSTALL b/INSTALL index 1e2f25b9..31568c30 100644 --- a/INSTALL +++ b/INSTALL @@ -94,14 +94,18 @@ First configure the server. Click the `Server' radio button * Enter other names the server is known by * Click OK * Use `Add' to add your other computers + * Using a computer's name as its screen name is recommended * Use the controls under `Layout' to link screens together * Click (once) on the server's name in the `Screens' list * Choose the screen to the left of the server * Use `---' if there is no screen to the left of the server * Choose the screens to the right, above and below the server * Repeat the above steps for all the other screens - * Enter the server's screen name next to `Screen Name' - * This defaults to the computer's name (recommended) + * Use `Options...' to set desired options. + * If the server's screen name is not the server's computer name: + * Click `Advanced...' + * Enter the server's screen name next to `Screen Name' + * Click `OK' * Click `Test' The server will start and you'll see a console window with log messages @@ -118,19 +122,27 @@ client computer. Then configure the client: * Enter the server's computer name in `Server Host Name' * Do not use any of the server's screen names, unless one of those is also the computer name - * Enter the client's screen name next to `Screen Name' - * This defaults to the computer's name (recommended) - * This must be one of the names in the server's `Screens' list + * If the client's screen name is not the client's computer name: + * Click `Advanced...' + * Enter the client's screen name next to `Screen Name' + * Click `OK' * Click `Test' If all goes well, the client connects to the server successfully and the mouse and keyboard are shared. If an error occurs you'll get one -or more dialog boxes telling you what the errors are; read the errors +or more dialog boxes telling you what the errors are; read the errors to determine the problem then correct them and try `Test' again. When everything is working correctly, install the software on the other client computers (if any) and repeat the steps for configuring the client on each. +Once the clients and server are working you can stop the clients and +server by clicking the `Stop' button on each computer or by right +clicking on the tray icon (by the clock in the task bar) and choosing +`Quit'. Then click `Start' on the server computer then on each of +the clients. Synergy will start and the dialog window will close. +You can stop synergy or check on its status using the tray icon. + See `Starting Automatically on Windows' below for configuring synergy to start automatically when the computer starts. @@ -146,30 +158,75 @@ plain text file broken into sections. Each section has the form: end Comments are introduced by `#' and continue to the end of the line. -The file can have the following sections. +The file can have the following sections. The `screens' section must +appear before the `links' and `aliases' sections. * screens is a list of screen names, one name per line, each followed by a colon. Names are arbitrary strings but they must be unique. The hostname of each computer is recommended. There must be a screen name for the server and each client. + Each screen can specify a number of options. Options have the + form `name = value' and a listed one per line after the screen + name. Example: section: screens moe: larry: + halfDuplexCapsLock = true + halfDuplexNumLock = true curly: + meta = alt end This declares three screens named: moe, larry, and curly. + Screen `larry' has half-duplex caps lock and num lock keys + (see below) and screen `curly' converts the meta modifier key + to the alt key. + + Screen can have the following options: + + halfDuplexCapsLock = {true|false} + This computer has a caps lock key that doesn't report a + press and a release event when the user presses it but + instead reports a press event when it's turned on and a + release event when it's turned off. If caps lock acts + strangely on all screens then you may need this option + on the server screen. If it acts strangely on one + screen then that screen may need the option. + + halfDuplexNumLock = {true|false} + This is identical to halfDuplexCapsLock except it + applies to the num lock key. + + shift = {shift|ctrl|alt|meta|super|none} + ctrl = {shift|ctrl|alt|meta|super|none} + alt = {shift|ctrl|alt|meta|super|none} + meta = {shift|ctrl|alt|meta|super|none} + super = {shift|ctrl|alt|meta|super|none} + Map a modifier key pressed on the server's keyboard to + a different modifier on this client. This option only + has an effect on a client screen; it's accepted and + ignored on the server screen. + + You can map, say, the shift key to shift (the default), + ctrl, alt, meta, super or nothing. Normally, you + wouldn't remap shift or ctrl. You might, however, have + an X11 server with meta bound to the Alt keys. To use + this server effectively with a windows client, which + doesn't use meta but uses alt extensively, you'll want + the windows client to map meta to alt (using `meta = + alt'). * links is a list of screen names just like in the `screens' - section except each screen is followed by a list of links. - Each link has the form ` = '. - A link indicates which screen is adjacent in the given - direction. + section except each screen is followed by a list of links, + one per line. Each link has the form ` = + '. A link indicates which screen is adjacent in the + given direction. + Example: @@ -214,6 +271,47 @@ The file can have the following sections. connect as either name. Screen `curly' is also known as `shemp'. (Hey, it's just an example.) + * options + is a list of lines of the form `name = value'. These + set the global options. + + Example: + + section: options + heatbeat = 5000 + switchDelay = 500 + end + + You can use the following options: + + heartbeat = N + The server will expect each client to send a message no + less than every N milliseconds. If no message arrives + from a client within 3N seconds the server forces that + client to disconnect. + + If synergy fails to detect clients disconnecting while + the server is sleeping or vice versa, try using this + option. + + switchDelay = N + Synergy won't switch screens when the mouse reaches the + edge of a screen unless it stays on the edge for N + milliseconds. This helps prevent unintentional + switching when working near the edge of a screen. + + switchDoubleTap = N + Synergy won't switch screens when the mouse reaches the + edge of a screen unless it's moved away from the edge + and then back to the edge within N milliseconds. With + the option you have to quickly tap the edge twice to + switch. This helps prevent unintentional switching + when working near the edge of a screen. + + You can use both the switchDelay and switchDoubleTap options at + the same time. Synergy will switch when either requirement is + satisfied. + The synergy server will try certain pathnames to load the configuration file if the user doesn't specify a path using the `--config' command line option. `synergys --help' reports those pathnames. @@ -298,8 +396,8 @@ Starting Automatically on Windows When all the clients work you're ready to have synergy start automatically each time the system (re)starts. Click `Stop' on all -the clients then on the server'. Now click the `Configure' button by -the text `Automatic Startup'. The `Auto Start' dialog will pop up. +the clients then on the server'. Now click the `Configure...' button +by the text `Automatic Startup'. The `Auto Start' dialog will pop up. If an error occurs then correct the problem and click `Configure' again. diff --git a/PORTING b/PORTING index 8a61f141..3281822d 100644 --- a/PORTING +++ b/PORTING @@ -169,15 +169,16 @@ following these guidelines. is a macro for `for' in lib/base/common.h when building under Microsoft Visual C++ that works around the fact that that compiler doesn't follow the new scoping rules. Use the macro if your - compiler using the old scoping rules. + compiler uses the old scoping rules. * Standard C++ library The standard C++ library containers should always be used in favor of custom containers wherever reasonable. std::string is used throughout synergy but only as the CString typedef; always use - CString, never std::string. Synergy avoids using auto_ptr due to - some portability problems. Synergy makes limited use of standard - algorithms and streams but they can be freely used in new code. + CString, never std::string except in the arch library. Synergy + avoids using auto_ptr due to some portability problems. Synergy + makes limited use of standard algorithms and streams but they can + be freely used in new code. * Limited multiple inheritance Classes should inherit implementation from at most one superclass. @@ -207,7 +208,7 @@ following these guidelines. should be private, not public or protected. This makes it much easier to track the use of a member when reading code. Protected data is not allowed because `protected' is a synonym for `public - to my subclasses' and public data is Bad Thing. While it might + to my subclasses' and public data is a Bad Thing. While it might seem okay in this limited situation, the situation is not at all limited since an arbitrary number of classes can be derived, directly or indirectly, from the class and any of those classes @@ -219,6 +220,9 @@ following these guidelines. POD should have only public data members and non-copy constructors. It must not have any methods other than constructors, not even a destructor or assignment operators, nor protected or private data. + Note that this definition of POD is not the definition used in the + C++ standard, which limits the contained data types to types that + have no constructors, destructors, or methods. * Avoid using friend Avoid declaring friend functions or classes. They're sometimes diff --git a/README b/README index 13d5d4d4..2a068545 100644 --- a/README +++ b/README @@ -63,7 +63,8 @@ Irix. Installation ------------ -See INSTALL for detailed build and installation instructions. +See INSTALL for detailed build and installation instructions and for +more information on configuring synergy. Quick Start @@ -95,18 +96,22 @@ Third, you configure and start the server. * Click the `Server' radio button * Click `Add' to add the server to the `Screens' list - * Enter the name of server (the computer name is recommended) - * Enter other names the server is known by - * Click OK + * Enter the name of server (the computer name is recommended) + * Enter other names the server is known by + * Click OK * Use `Add' to add your other computers + * Using a computer's name as its screen name is recommended * Use the controls under `Layout' to link screens together - * Click (once) on the server's name in the `Screens' list - * Choose the screen to the left of the server + * Click (once) on the server's name in the `Screens' list + * Choose the screen to the left of the server * Use `---' if there is no screen to the left of the server - * Choose the screens to the right, above and below the server - * Repeat the above steps for all the other screens - * Enter the server's screen name next to `Screen Name' - * This defaults to the computer's name (recommended) + * Choose the screens to the right, above and below the server + * Repeat the above steps for all the other screens + * Use `Options...' to set desired options. + * If the server's screen name is not the server's computer name: + * Click `Advanced...' + * Enter the server's screen name next to `Screen Name' + * Click `OK' Now click `Test'. The server will start and you'll see a console window with log messages telling you about synergy's progress. If an error @@ -157,11 +162,12 @@ Finally, start the clients. * Click the `Client' radio button * Enter the server's computer name in `Server Host Name' - * Do not use any of the server's screen names, unless one of those - is also the computer name - * Enter the client's screen name next to `Screen Name' - * This defaults to the computer's name (recommended) - * This must be one of the names in the server's `Screens' list + * Do not use any of the server's screen names, unless one of those + is also the computer name + * If the client's screen name is not the client's computer name: + * Click `Advanced...' + * Enter the client's screen name next to `Screen Name' + * Click `OK' * Click `Test' If an error occurs you'll get a dialog box telling you synergy is diff --git a/TODO b/TODO index b6f13e8e..d0432fe5 100644 --- a/TODO +++ b/TODO @@ -12,9 +12,9 @@ Things to do to synergy, in no particular order: * Provide taskbar feedback - An icon indicating synergy's state in the taskbar would be nice. - It could also provide a means for viewing recent log messages, - stopping synergy, and forcing disconnection. + There's a tray icon on win32 for checking synergy's current status + and to quit synergy. It'd be nice to have something similar on + X11. * Support non-ASCII keyboards From 125e81c92e50e44d89ffdf143969c9c53ba6ebb2 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 25 Mar 2003 21:31:39 +0000 Subject: [PATCH 477/807] This should fix multimon support on win32. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 11 +++++++---- lib/platform/CSynergyHook.cpp | 18 ++++-------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index 9f335e54..16e7fe9b 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -383,11 +383,14 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) // move the mouse directly to target position if motion is simple if (simple) { - SInt32 x0, y0, w, h; - m_screen->getShape(x0, y0, w, h); + // when using absolute positioning with mouse_event(), + // the normalized device coordinates range over only + // the primary screen. + SInt32 w = GetSystemMetrics(SM_CXSCREEN); + SInt32 h = GetSystemMetrics(SM_CYSCREEN); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65535.99 * (x - x0)) / (w - 1)), - (DWORD)((65535.99 * (y - y0)) / (h - 1)), + (DWORD)((65536.0 * x) / w), + (DWORD)((65536.0 * y) / h), 0, 0); } diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index e0530137..079b8e30 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -217,12 +217,9 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) hideCursor(thread); } - // get position. it seems the positions are not - // in virtual desktop coordinates but in a similar - // space with 0,0 at the upper-left. translate - // into virtual desktop coordinates. - SInt32 x = (SInt32)info->pt.x + g_xScreen; - SInt32 y = (SInt32)info->pt.y + g_yScreen; + // get position + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; // relay the motion PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); @@ -231,10 +228,7 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) } } else { - // check for mouse inside jump zone. it seems the positions - // are not in virtual desktop coordinates but in a similar - // space with 0,0 at the upper-left. translate into virtual - // desktop coordinates. + // check for mouse inside jump zone bool inside = false; const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; SInt32 x = (SInt32)info->pt.x; @@ -254,15 +248,11 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) // if inside then eat event and notify our window if (inside) { - x += g_xScreen; - y += g_yScreen; restoreCursor(); PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); return 1; } else { - x += g_xScreen; - y += g_yScreen; PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); return 0; } From 5320f663ba2333873f878b5683e5e33930d96303 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 26 Mar 2003 21:03:58 +0000 Subject: [PATCH 478/807] Changed version to 1.0.6. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 5bfdd16d..663dce84 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=0 -RELEASE_VERSION=5 +RELEASE_VERSION=6 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index f1271e75..d39f2c54 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.5" +# define VERSION "1.0.6" #endif // important strings From 53c05e0163b87f1b955930a3c6eee30291e63d43 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 26 Mar 2003 21:19:10 +0000 Subject: [PATCH 479/807] Updates for version 1.0.6. --- ChangeLog | 319 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ NEWS | 13 +++ 2 files changed, 332 insertions(+) diff --git a/ChangeLog b/ChangeLog index 463d56e8..9b8621ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,322 @@ +2003/03/26 21:03:58 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.6. + +---------- +2003/03/25 21:31:39 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CSynergyHook.cpp + +This should fix multimon support on win32. + +---------- +2003/03/22 11:49:23 crs +FAQ +INSTALL +PORTING +README +TODO + +Documentation updates. + +---------- +2003/03/22 11:49:13 crs +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/CGlobalOptions.h +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added key modifier and heartbeat options to GUI. + +---------- +2003/03/21 19:34:08 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Oops, included a windows only header in non-windows builds. + +---------- +2003/03/21 19:16:37 crs +lib/platform/CMSWindowsScreenSaver.cpp + +Added check for the screen saver actually being active before +entering the loop waiting for it to deactivate. The failure +to check was causing the screen saver code to kick in when +the screen saver timeout occurred, even if the screen saver +wasn't enabled (because Windows still sends the screen saver +activating message for no good reason when the screen saver +is disabled). + +---------- +2003/03/21 19:14:32 crs +lib/arch/CArchMiscWindows.cpp + +Fixed errors in merge causing infinite loops. + +---------- +2003/03/21 19:14:10 crs +cmd/synergyc/tb_idle.ico +cmd/synergyc/tb_run.ico +cmd/synergyc/tb_wait.ico +cmd/synergys/tb_idle.ico +cmd/synergys/tb_run.ico +cmd/synergys/tb_wait.ico + +Fixed icons. + +---------- +2003/03/21 19:13:15 crs +lib/platform/CXWindowsUtil.cpp + +Fixed getWindowProperty(). It wasn't catching all failure +cases correctly. + +---------- +2003/03/17 22:32:10 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAdvancedOptions.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h + +Added options and advanced options dialogs which should've been +part of an earlier checkin. Also now saving and restoring +options that aren't in the configuration file to/from the +registry. + +---------- +2003/03/17 22:32:01 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.cpp +lib/server/CServer.cpp +lib/synergy/CPrimaryScreen.h + +Added a log message why the user is locked to the screen. + +---------- +2003/03/17 22:31:59 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Added type casts to avoid warning. + +---------- +2003/03/16 17:40:57 crs +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h + +Fixed detection of screen saver shutdown on windows nt. + +---------- +2003/03/16 17:40:56 crs +lib/common/Makefile.am +lib/common/common.dsp +lib/common/stdbitset.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Made releaseKeys() only synthesize key releases for those keys +that synergy synthesized a press for, not keys that the user +is physically pressing. + +---------- +2003/03/16 17:40:47 crs +lib/platform/CSynergyHook.cpp + +Minor hook fixes. + +---------- +2003/03/16 17:40:25 crs +cmd/synergyc/synergyc.rc +cmd/synergys/synergys.rc + +Added resources missing from previous checkin. + +---------- +2003/03/13 20:24:45 crs +lib/platform/CXWindowsScreenSaver.cpp + +Moved comment to more relevant location. + +---------- +2003/03/13 19:20:55 crs +lib/platform/CXWindowsScreen.cpp + +Fixed double locking of mutex. + +---------- +2003/03/12 22:34:07 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAdvancedOptions.h +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/CGlobalOptions.h +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/Makefile.am +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/resource.h +cmd/synergyc/CClientTaskBarReceiver.cpp +cmd/synergyc/CClientTaskBarReceiver.h +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CXWindowsClientTaskBarReceiver.h +cmd/synergyc/Makefile.am +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.dsp +cmd/synergyc/tb_error.ico +cmd/synergyc/tb_idle.ico +cmd/synergyc/tb_run.ico +cmd/synergyc/tb_wait.ico +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.h +cmd/synergys/CServerTaskBarReceiver.cpp +cmd/synergys/CServerTaskBarReceiver.h +cmd/synergys/CXWindowsServerTaskBarReceiver.cpp +cmd/synergys/CXWindowsServerTaskBarReceiver.h +cmd/synergys/Makefile.am +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.dsp +cmd/synergys/tb_error.ico +cmd/synergys/tb_idle.ico +cmd/synergys/tb_run.ico +cmd/synergys/tb_wait.ico +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchConsoleWindows.h +lib/arch/CArchImpl.cpp +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/CArchTaskBarWindows.cpp +lib/arch/CArchTaskBarWindows.h +lib/arch/CArchTaskBarXWindows.cpp +lib/arch/CArchTaskBarXWindows.h +lib/arch/IArchMultithread.h +lib/arch/IArchTaskBar.h +lib/arch/IArchTaskBarReceiver.h +lib/arch/Makefile.am +lib/arch/arch.dsp +lib/base/CJobList.cpp +lib/base/CJobList.h +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h +lib/base/Makefile.am +lib/base/base.dsp +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/mt/CTimerThread.cpp +lib/mt/CTimerThread.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h + +Added switch delay and double-tap options to win32 and added a +tray icon to the client and server that gives status feedback to +the user and allows the user to kill the app. + +---------- +2003/02/23 19:29:08 crs +lib/server/CConfig.cpp +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/OptionTypes.h +lib/synergy/ProtocolTypes.h + +Added support for a user option to require hitting the edge of a +screen twice within a specified amount of time in order to switch +screens. This can help prevent unintended switching. + +---------- +2003/02/22 21:53:25 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CConfig.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CPrimaryScreen.h +lib/synergy/IPrimaryScreenReceiver.h +lib/synergy/IScreenEventHandler.h +lib/synergy/OptionTypes.h + +Added support on X11 for a global option to delay switching screens +when the mouse reaches a jump zone. + +---------- +2003/02/22 16:41:03 crs +lib/server/CConfig.cpp +lib/server/CConfig.h + +Added global options to CConfig (needed for heartbeat option). + +---------- +2003/02/22 16:20:23 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CServer.cpp +lib/synergy/OptionTypes.h +lib/synergy/ProtocolTypes.h + +Added support for heartbeat global option. + +---------- +2003/02/22 15:04:09 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.5. + +---------- +2003/02/22 15:03:31 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/synergy/KeyTypes.h +lib/synergy/OptionTypes.h + +Changes to support remapping modifier keys on clients. + +---------- 2003/02/17 12:44:37 crs configure.in lib/common/Version.h diff --git a/NEWS b/NEWS index 613c4c18..b08b6d0f 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,19 @@ Synergy News ============ +* Mar-27-2003 - Synergy 1.0.6 released + + Made following changes: + * Added tray icon on win32 + * Fixed multi-monitor support on win32 + * Fixed win32 screen saver detection on NT/2k/XP + * Added per-screen options to remap modifier keys + * Added global options for restricting screen jumping + * Added global option for detecting unresponsive clients + * Added more logging for why screen jump won't happen + * Fixed problem sending the CLIPBOARD to motif/lesstif apps + * Win32 launcher now remembers non-config-file state + * Feb-18-2003 - Synergy 1.0.3 released Made following changes: From c2bd4ebd4cd99808e7d976a395d90be663f341f0 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 8 Apr 2003 19:26:35 +0000 Subject: [PATCH 480/807] Changed MS clipboard text converters to truncate the clipboard data at the first NUL. This fixes a bug when interoperating with some win32 programs. --- lib/platform/CMSWindowsClipboardTextConverter.cpp | 9 +++++---- lib/platform/CMSWindowsClipboardUTF16Converter.cpp | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/platform/CMSWindowsClipboardTextConverter.cpp b/lib/platform/CMSWindowsClipboardTextConverter.cpp index 7b11cdce..a735094a 100644 --- a/lib/platform/CMSWindowsClipboardTextConverter.cpp +++ b/lib/platform/CMSWindowsClipboardTextConverter.cpp @@ -45,10 +45,11 @@ CMSWindowsClipboardTextConverter::doFromIClipboard(const CString& data) const CString CMSWindowsClipboardTextConverter::doToIClipboard(const CString& data) const { - // convert and strip nul terminator - CString dst = CUnicode::textToUTF8(data); - if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { - dst.erase(dst.size() - 1); + // convert and truncate at first nul terminator + CString dst = CUnicode::textToUTF8(data); + CString::size_type n = dst.find('\0'); + if (n != CString::npos) { + dst.erase(n); } return dst; } diff --git a/lib/platform/CMSWindowsClipboardUTF16Converter.cpp b/lib/platform/CMSWindowsClipboardUTF16Converter.cpp index d0ec7ea3..81b85c60 100644 --- a/lib/platform/CMSWindowsClipboardUTF16Converter.cpp +++ b/lib/platform/CMSWindowsClipboardUTF16Converter.cpp @@ -46,9 +46,10 @@ CString CMSWindowsClipboardUTF16Converter::doToIClipboard(const CString& data) const { // convert and strip nul terminator - CString dst = CUnicode::UTF16ToUTF8(data); - if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { - dst.erase(dst.size() - 1); + CString dst = CUnicode::UTF16ToUTF8(data); + CString::size_type n = dst.find('\0'); + if (n != CString::npos) { + dst.erase(n); } return dst; } From 2d168319e480ecdd23bbb3dd3b291f7b81c0b216 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 Apr 2003 14:38:40 +0000 Subject: [PATCH 481/807] Changed version to 1.0.7. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 663dce84..5a8cecc9 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=0 -RELEASE_VERSION=6 +RELEASE_VERSION=7 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index d39f2c54..8b26dad2 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.6" +# define VERSION "1.0.7" #endif // important strings From ef59307c1615aab1e90a908e7a86ef78c6ea124c Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 Apr 2003 14:39:17 +0000 Subject: [PATCH 482/807] Added mention of tray icon to launcher start message box. --- cmd/launcher/launcher.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 67b3020e..03de9828 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -308,7 +308,7 @@ BEGIN IDS_SAVE_FAILED "Failed to save configuration: %{1}" IDS_STARTUP_FAILED "Failed to start synergy: %{1}" IDS_STARTED_TITLE "Started" - IDS_STARTED "Synergy was successfully started. Use the task manager to terminate it." + IDS_STARTED "Synergy was successfully started. Use the task manager or tray icon to terminate it." IDS_UNINSTALL_TITLE "Removed Auto-Start" END From 4521fe4990158586b004b28a217fbd5da30f12da Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 Apr 2003 14:59:53 +0000 Subject: [PATCH 483/807] Fixed several win32 bugs. First, synergy wasn't forwarding mouse events to other hook functions, which broke some tools like objectbar. Second, windows key processing was fixed. Previously pressing and release the key would only send a press event, locking the user onto the client window; also, the win32 server treated as a Meta modifier instead of a Super modifier, which broke any use of it as any kind of modifier key. Third, added hacks to support several key combinations on windows 95/98/me that are treated specially by windows, including Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any combination using the windows key like Win+E and Win+F but not Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which only happened when using a synergy server on windows) has been fixed; unfortunately the solution causes a lot of screen redraws for some reason. Finally, there's been a fix to clipboard handling that may or may not fix a problem where the clipboard would stop transferring between systems after a little while. I can't be sure if it fixes the problem because I can't reproduce the problem. --- lib/platform/CMSWindowsClipboard.cpp | 29 +++++ lib/platform/CMSWindowsClipboard.h | 6 + lib/platform/CMSWindowsPrimaryScreen.cpp | 146 +++++++++++++++++++++-- lib/platform/CMSWindowsPrimaryScreen.h | 1 + lib/platform/CMSWindowsScreen.cpp | 62 +++++----- lib/platform/CMSWindowsScreen.h | 2 +- lib/platform/CSynergyHook.cpp | 1 - 7 files changed, 201 insertions(+), 46 deletions(-) diff --git a/lib/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp index 32aad8e1..7df5d61f 100644 --- a/lib/platform/CMSWindowsClipboard.cpp +++ b/lib/platform/CMSWindowsClipboard.cpp @@ -21,6 +21,8 @@ // CMSWindowsClipboard // +UINT CMSWindowsClipboard::s_ownershipFormat = 0; + CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : m_window(window), m_time(0) @@ -40,11 +42,16 @@ CMSWindowsClipboard::empty() { LOG((CLOG_DEBUG "empty clipboard")); + // empty the clipboard (and take ownership) if (!EmptyClipboard()) { LOG((CLOG_DEBUG "failed to grab clipboard")); return false; } + // mark clipboard as being owned by synergy + HGLOBAL data = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 1); + SetClipboardData(getOwnershipFormat(), data); + return true; } @@ -159,3 +166,25 @@ CMSWindowsClipboard::clearConverters() } m_converters.clear(); } + +bool +CMSWindowsClipboard::isOwnedBySynergy() +{ + // create ownership format if we haven't yet + if (s_ownershipFormat == 0) { + s_ownershipFormat = RegisterClipboardFormat(TEXT("SynergyOwnership")); + } + return (IsClipboardFormatAvailable(getOwnershipFormat()) != 0); +} + +UINT +CMSWindowsClipboard::getOwnershipFormat() +{ + // create ownership format if we haven't yet + if (s_ownershipFormat == 0) { + s_ownershipFormat = RegisterClipboardFormat(TEXT("SynergyOwnership")); + } + + // return the format + return s_ownershipFormat; +} diff --git a/lib/platform/CMSWindowsClipboard.h b/lib/platform/CMSWindowsClipboard.h index a2a65b57..ce7ba724 100644 --- a/lib/platform/CMSWindowsClipboard.h +++ b/lib/platform/CMSWindowsClipboard.h @@ -28,6 +28,9 @@ public: CMSWindowsClipboard(HWND window); virtual ~CMSWindowsClipboard(); + //! Test if clipboard is owned by synergy + static bool isOwnedBySynergy(); + // IClipboard overrides virtual bool empty(); virtual void add(EFormat, const CString& data); @@ -44,12 +47,15 @@ private: HANDLE convertTextToWin32(const CString& data) const; CString convertTextFromWin32(HANDLE) const; + static UINT getOwnershipFormat(); + private: typedef std::vector ConverterList; HWND m_window; mutable Time m_time; ConverterList m_converters; + static UINT s_ownershipFormat; }; //! Clipboard format converter interface diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 98785b08..bee3ceb8 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -385,12 +385,45 @@ KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { KeyModifierMask mask = 0; - if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) - mask |= KeyModifierCapsLock; - if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) - mask |= KeyModifierNumLock; - if ((GetKeyState(VK_SCROLL) & 0x01) != 0) - mask |= KeyModifierScrollLock; + if (isActive()) { + // get key state + if ((m_keys[VK_CAPITAL] & 0x01) != 0) + mask |= KeyModifierCapsLock; + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + mask |= KeyModifierNumLock; + if ((m_keys[VK_SCROLL] & 0x01) != 0) + mask |= KeyModifierScrollLock; + } + else { + // show the window, but make it very small. we must do this + // because GetKeyState() reports the key state according to + // processed messages and until the window is visible the + // system won't update the state of the toggle keys reported + // by that function. unfortunately, this slows this method + // down significantly and, for some reason i don't understand, + // causes everything on the screen to redraw. + if (m_window != NULL) { + MoveWindow(m_window, 1, 1, 1, 1, FALSE); + const_cast(this)->showWindow(); + } + + // get key state + if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) + mask |= KeyModifierCapsLock; + if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) + mask |= KeyModifierNumLock; + if ((GetKeyState(VK_SCROLL) & 0x01) != 0) + mask |= KeyModifierScrollLock; + + // make the window hidden again and restore its size + if (m_window != NULL) { + const_cast(this)->hideWindow(); + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + MoveWindow(m_window, x, y, w, h, FALSE); + } + } + return mask; } @@ -459,6 +492,57 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) { assert(event != NULL); + const MSG* msg = &event->m_msg; + + // check if windows key is up but we think it's down. if so then + // synthesize a key release for it. we have to do this because + // if the user presses and releases a windows key without pressing + // any other key when its down then windows will eat the key + // release. if we don't detect that an synthesize the release + // then the user will be locked to the screen and the client won't + // take the usual windows key release action (which on windows is + // to show the start menu). + // + // we can use GetKeyState() to check the state of the windows keys + // because, event though the key release is not reported to us, + // the event is processed and the keyboard state updated by the + // system. since the key could go up at any time we'll check the + // state on every event. only check on windows 95 family since + // NT family reports the key release as usual. obviously we skip + // this if the event is for the windows key itself. + if (m_is95Family) { + if ((m_keys[VK_LWIN] & 0x80) != 0 && + (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 && + !(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_LWIN)) { + // compute appropriate parameters for fake event + WPARAM wParam = VK_LWIN; + LPARAM lParam = 0xc1000000; + lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); + + // process as if it were a key up + KeyModifierMask mask; + const KeyID key = mapKey(wParam, lParam, &mask); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x", key, mask)); + m_receiver->onKeyUp(key, mask); + updateKey(wParam, false); + } + if ((m_keys[VK_RWIN] & 0x80) != 0 && + (GetAsyncKeyState(VK_RWIN) & 0x8000) == 0 && + !(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_RWIN)) { + // compute appropriate parameters for fake event + WPARAM wParam = VK_RWIN; + LPARAM lParam = 0xc1000000; + lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); + + // process as if it were a key up + KeyModifierMask mask; + const KeyID key = mapKey(wParam, lParam, &mask); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x", key, mask)); + m_receiver->onKeyUp(key, mask); + updateKey(wParam, false); + } + } + // handle event const MSG* msg = &event->m_msg; switch (msg->message) { @@ -474,8 +558,9 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) if (key != kKeyNone) { if ((msg->lParam & 0x80000000) == 0) { // key press + const bool wasDown = ((msg->lParam & 0x40000000) != 0); const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); - if (repeat >= 2) { + if (repeat >= 2 || wasDown) { LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); m_receiver->onKeyRepeat(key, mask, repeat); } @@ -488,7 +573,20 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) updateKey(msg->wParam, true); } else { - // key release + // key release. if the key isn't down according to + // our table then we never got the key press event + // for it. if it's not a modifier key then we'll + // synthesize the press first. only do this on + // the windows 95 family, which eats certain special + // keys like alt+tab, ctrl+esc, etc. + if (m_is95Family && !isModifier(msg->wParam) && + (m_keys[msg->wParam] & 0x80) == 0) { + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x", key, mask)); + m_receiver->onKeyDown(key, mask); + updateKey(msg->wParam, true); + } + + // do key up LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask)); m_receiver->onKeyUp(key, mask); @@ -583,7 +681,9 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) if (!isActive()) { // motion on primary screen - m_receiver->onMouseMovePrimary(m_x, m_y); + if (x != 0 || y != 0) { + m_receiver->onMouseMovePrimary(m_x, m_y); + } } else { // motion on secondary screen. warp mouse back to @@ -1271,7 +1371,7 @@ CMSWindowsPrimaryScreen::mapKey( } if (((m_keys[VK_LWIN] | m_keys[VK_RWIN]) & 0x80) != 0) { - mask |= KeyModifierMeta; + mask |= KeyModifierSuper; } if ((m_keys[VK_CAPITAL] & 0x01) != 0) { mask |= KeyModifierCapsLock; @@ -1523,3 +1623,29 @@ CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press) } } } + +bool +CMSWindowsPrimaryScreen::isModifier(UINT vkCode) const + +{ + switch (vkCode) { + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + case VK_LWIN: + case VK_RWIN: + return true; + + default: + return false; + } +} diff --git a/lib/platform/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h index c274de40..f179a90c 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.h +++ b/lib/platform/CMSWindowsPrimaryScreen.h @@ -89,6 +89,7 @@ private: KeyModifierMask* maskOut); ButtonID mapButton(WPARAM button) const; void updateKey(UINT vkCode, bool press); + bool isModifier(UINT vkCode) const; private: IPrimaryScreenReceiver* m_receiver; diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 188c070c..1e848b1c 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -60,7 +60,7 @@ CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, m_threadID(0), m_lastThreadID(0), m_nextClipboardWindow(NULL), - m_clipboardOwner(NULL), + m_ownClipboard(false), m_timer(0), m_desk(NULL), m_deskName(), @@ -107,11 +107,9 @@ CMSWindowsScreen::openDesktop() } // poll input desktop to see if it changes (onPreDispatch() - // handles WM_TIMER) - m_timer = 0; - if (!m_is95Family) { - m_timer = SetTimer(NULL, 0, 200, NULL); - } + // handles WM_TIMER). this is also used for polling other + // stuff. + m_timer = SetTimer(NULL, 0, 200, NULL); return m_window; } @@ -321,13 +319,10 @@ CMSWindowsScreen::checkClipboards() // next reboot we do this double check. clipboard ownership // won't be reflected on other screens until we leave but at // least the clipboard itself will work. - HWND clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != clipboardOwner) { - m_clipboardOwner = clipboardOwner; - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } + if (m_ownClipboard && !CMSWindowsClipboard::isOwnedBySynergy()) { + m_ownClipboard = false; + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); } } @@ -510,6 +505,10 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) } } } + + // let client do timer related stuff. ignore the return + // value though since the event has been handled here. + m_eventHandler->onPreDispatch(event); } else if (msg->wParam == m_oneShotTimer) { // one shot timer expired @@ -517,9 +516,11 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) m_oneShotTimer = 0; m_eventHandler->onOneShotTimerExpired(0); } + return true; } + // let client handle the event return m_eventHandler->onPreDispatch(event); } @@ -560,14 +561,16 @@ CMSWindowsScreen::onEvent(CEvent* event) } // now notify client that somebody changed the clipboard (unless - // we're now the owner, in which case it's because we took - // ownership, or now it's owned by nobody, which will happen if - // we owned it and switched desktops because we destroy our - // window to do that). - m_clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); + // we're the owner). + if (!CMSWindowsClipboard::isOwnedBySynergy()) { + if (m_ownClipboard) { + m_ownClipboard = false; + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + else { + m_ownClipboard = true; } return true; @@ -631,8 +634,8 @@ CMSWindowsScreen::createBlankCursor() bool CMSWindowsScreen::switchDesktop(HDESK desk) { - // did we own the clipboard? - bool ownClipboard = (m_clipboardOwner == m_window && m_window != NULL); + // assume we don't own the clipboard until later + m_ownClipboard = false; // destroy old window if (m_window != NULL) { @@ -640,11 +643,6 @@ CMSWindowsScreen::switchDesktop(HDESK desk) ChangeClipboardChain(m_window, m_nextClipboardWindow); m_nextClipboardWindow = NULL; - // we no longer own the clipboard - if (ownClipboard) { - m_clipboardOwner = NULL; - } - // let client clean up before we destroy the window m_eventHandler->preDestroyWindow(m_window); @@ -712,12 +710,8 @@ CMSWindowsScreen::switchDesktop(HDESK desk) // install our clipboard snooper m_nextClipboardWindow = SetClipboardViewer(m_window); - // reassert clipboard ownership - if (ownClipboard) { - // FIXME -- take clipboard ownership, but we should also set - // the clipboard data. - } - m_clipboardOwner = GetClipboardOwner(); + // check if we own the clipboard + m_ownClipboard = CMSWindowsClipboard::isOwnedBySynergy(); // save new desktop m_desk = desk; diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index d11f5ef1..b7b97156 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -172,7 +172,7 @@ private: // clipboard stuff HWND m_nextClipboardWindow; - HWND m_clipboardOwner; + bool m_ownClipboard; // the timer used to check for desktop switching UINT m_timer; diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 079b8e30..885ec8d0 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -254,7 +254,6 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) } else { PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); - return 0; } } } From a4a08c3ce6856a248d381c1fd8c7b4a980472882 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 Apr 2003 17:13:27 +0000 Subject: [PATCH 484/807] Removed periodic call to XForceScreenSaver() to prevent the built-in screen saver from activating. It was unnecessary since the built-in screen saver is disabled as appropriate; this call was just to ensure that the screen saver wouldn't start if an external program reactivated the screen saver after synergy disabled it. It's possible that this was causing screen flicker under gnome, though i don't know why. It's also possible that periodically sending events to xscreensaver is causing the flicker but removing that code is more difficult because xscreensaver can't be disabled, only deactivated or killed. --- lib/platform/CXWindowsScreenSaver.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index 089499bf..c652c3d9 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -479,7 +479,4 @@ CXWindowsScreenSaver::disableCallback(void*) CXWindowsUtil::CErrorLock lock(m_display); XSendEvent(m_display, m_xscreensaver, False, 0, &event); } - - // force screen saver off and reset the timer - XForceScreenSaver(m_display, ScreenSaverReset); } From 019994548cc0c58fec9e1131410973f2d4db7744 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 Apr 2003 18:14:01 +0000 Subject: [PATCH 485/807] Fixed problem with type casting void* to int. --- cmd/synergyc/synergyc.cpp | 9 +++++---- cmd/synergys/synergys.cpp | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 095b62ea..4f72a6bd 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -246,16 +246,17 @@ realMain(void) static void -realMainEntry(void*) +realMainEntry(void* vresult) { - CThread::exit(reinterpret_cast(realMain())); + *reinterpret_cast(vresult) = realMain(); } static int runMainInThread(void) { - CThread appThread(new CFunctionJob(&realMainEntry)); + int result; + CThread appThread(new CFunctionJob(&realMainEntry, &result)); try { #if WINDOWS_LIKE MSG msg; @@ -272,7 +273,7 @@ runMainInThread(void) #else appThread.wait(-1.0); #endif - return reinterpret_cast(appThread.getResult()); + return result; } catch (XThread&) { appThread.cancel(); diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 3adc9f0d..d85c4c6b 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -268,16 +268,17 @@ realMain(void) static void -realMainEntry(void*) +realMainEntry(void* vresult) { - CThread::exit(reinterpret_cast(realMain())); + *reinterpret_cast(vresult) = realMain(); } static int runMainInThread(void) { - CThread appThread(new CFunctionJob(&realMainEntry)); + int result; + CThread appThread(new CFunctionJob(&realMainEntry, &result)); try { #if WINDOWS_LIKE MSG msg; @@ -292,7 +293,7 @@ runMainInThread(void) #else appThread.wait(-1.0); #endif - return reinterpret_cast(appThread.getResult()); + return result; } catch (XThread&) { appThread.cancel(); From 6a108ed2d51663cf48c92616de5484b4dd4f54a2 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 14 Apr 2003 22:15:56 +0000 Subject: [PATCH 486/807] Added workaround for apparent Xinerama bug when warping the pointer. This should allow synergy to be used on a system using Xinerama to create a single logical screen from multiple physical screens. --- configure.in | 2 ++ lib/platform/CXWindowsScreen.cpp | 51 ++++++++++++++++++++++++++++---- lib/platform/CXWindowsScreen.h | 1 + 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/configure.in b/configure.in index 5a8cecc9..92deeadb 100644 --- a/configure.in +++ b/configure.in @@ -57,6 +57,8 @@ AC_PATH_XTRA save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$X_CFLAGS $CPPFLAGS" AC_CHECK_HEADERS([X11/extensions/XTest.h]) + +AC_CHECK_LIB(Xinerama, XineramaQueryExtension, AC_CHECK_HEADERS([X11/extensions/Xinerama.h]) [X_LIBS="$X_LIBS -lXinerama"], , [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) CPPFLAGS="$save_CPPFLAGS" dnl checks for types diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index ba91e46b..81553d59 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -30,6 +30,12 @@ #else # include # include +# if HAVE_X11_EXTENSIONS_XINERAMA_H + // Xinerama.h may lack extern "C" for inclusion by C++ + extern "C" { +# include + } +# endif #endif #if UNIX_LIKE # if HAVE_POLL @@ -119,6 +125,7 @@ CXWindowsScreen::CXWindowsScreen(IScreenReceiver* receiver, m_window(None), m_x(0), m_y(0), m_w(0), m_h(0), + m_xCenter(0), m_yCenter(0), m_screensaver(NULL), m_screensaverNotify(false), m_atomScreensaver(None), @@ -561,8 +568,8 @@ CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const y = my; } else { - x = m_x + (m_w >> 1); - y = m_y + (m_h >> 1); + x = m_xCenter; + y = m_yCenter; } } @@ -570,20 +577,54 @@ void CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const { CLock lock(&m_mutex); - assert(m_display != NULL); - x = m_x + (m_w >> 1); - y = m_y + (m_h >> 1); + x = m_xCenter; + y = m_yCenter; } void CXWindowsScreen::updateScreenShape() { + // get shape of default screen m_x = 0; m_y = 0; m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); LOG((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + + // get center of default screen + m_xCenter = m_x + (m_w >> 1); + m_yCenter = m_y + (m_h >> 1); + +#if HAVE_X11_EXTENSIONS_XINERAMA_H + // get center of first Xinerama screen. Xinerama appears to have + // a bug when XWarpPointer() is used in combination with + // XGrabPointer(). in that case, the warp is successful but the + // next pointer motion warps the pointer again, apparently to + // constrain it to some unknown region, possibly the region from + // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over + // all physical screens. this warp only seems to happen if the + // pointer wasn't in that region before the XWarpPointer(). the + // second (unexpected) warp causes synergy to think the pointer + // has been moved when it hasn't. to work around the problem, + // we warp the pointer to the center of the first physical + // screen instead of the logical screen. + int eventBase, errorBase; + if (XineramaQueryExtension(m_display, &eventBase, &errorBase)) { + if (XineramaIsActive(m_display)) { + int numScreens; + XineramaScreenInfo* screens; + screens = XineramaQueryScreens(m_display, &numScreens); + if (screens != NULL) { + if (numScreens > 1) { + m_xCenter = screens[0].x_org + (screens[0].width >> 1); + m_yCenter = screens[0].y_org + (screens[0].height >> 1); + } + XFree(screens); + } + } + } +#endif } bool diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index 83f10cfa..ce37a004 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -271,6 +271,7 @@ private: SInt32 m_x, m_y; SInt32 m_w, m_h; + SInt32 m_xCenter, m_yCenter; // clipboards CXWindowsClipboard* m_clipboard[kClipboardEnd]; From aeb3f760d02af1b67abf855ddd03342c85b1b2c0 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 14 Apr 2003 22:16:21 +0000 Subject: [PATCH 487/807] Fixed incorrect initialization of an XMotionEvent. --- lib/platform/CXWindowsPrimaryScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp index 69e91951..a99cfea9 100644 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -598,7 +598,7 @@ CXWindowsPrimaryScreen::warpCursorNoFlush( eventBefore.xmotion.x_root = x; eventBefore.xmotion.y_root = y; eventBefore.xmotion.state = 0; - eventBefore.xmotion.is_hint = False; + eventBefore.xmotion.is_hint = NotifyNormal; eventBefore.xmotion.same_screen = True; XEvent eventAfter = eventBefore; XSendEvent(display, m_window, False, 0, &eventBefore); From 368d8cae39e1f4192d6921d15bcdc3e8b21a05f2 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Apr 2003 20:05:00 +0000 Subject: [PATCH 488/807] Now allowing screen names with underscores. --- lib/server/CConfig.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index b9024d6b..f75193a2 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -318,7 +318,7 @@ bool CConfig::isValidScreenName(const CString& name) const { // name is valid if matches validname - // name ::= [A-Za-z0-9] | [A-Za-z0-9][-A-Za-z0-9]*[A-Za-z0-9] + // name ::= [_A-Za-z0-9] | [_A-Za-z0-9][-_A-Za-z0-9]*[_A-Za-z0-9] // domain ::= . name // validname ::= name domain* @@ -337,13 +337,14 @@ CConfig::isValidScreenName(const CString& name) const } // check first and last characters - if (!isalnum(name[b]) || !isalnum(name[e - 1])) { + if (!(isalnum(name[b]) || name[b] == '_') || + !(isalnum(name[e - 1]) || name[e - 1] == '_')) { return false; } // check interior characters for (CString::size_type i = b; i < e; ++i) { - if (!isalnum(name[i]) && name[i] != '-') { + if (!isalnum(name[i]) && name[i] != '_' && name[i] != '-') { return false; } } From 56bafdb0e2ae547683a71c98eefd9d244c638657 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Apr 2003 20:59:14 +0000 Subject: [PATCH 489/807] Minor win32 fixes. --- cmd/synergyc/synergyc.cpp | 2 +- cmd/synergys/synergys.cpp | 2 +- lib/platform/CMSWindowsPrimaryScreen.cpp | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 4f72a6bd..e1aabab0 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -255,7 +255,7 @@ static int runMainInThread(void) { - int result; + int result = 0; CThread appThread(new CFunctionJob(&realMainEntry, &result)); try { #if WINDOWS_LIKE diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index d85c4c6b..8cf18f88 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -277,7 +277,7 @@ static int runMainInThread(void) { - int result; + int result = 0; CThread appThread(new CFunctionJob(&realMainEntry, &result)); try { #if WINDOWS_LIKE diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index bee3ceb8..db73e861 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -544,7 +544,6 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) } // handle event - const MSG* msg = &event->m_msg; switch (msg->message) { case SYNERGY_MSG_MARK: m_markReceived = msg->wParam; From 54cc95123bd6d08a11fb875028532656df3909fb Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Apr 2003 20:59:25 +0000 Subject: [PATCH 490/807] Win32 project configuration fixes. --- all.dsp | 4 +- cmd/exec.dsp | 63 +++++++++++++++++ lib/platform/makehook.dsp | 8 +-- lib/platform/synrgyhk.dsp | 8 +-- synergy.dsw | 143 +++++++++++++++++++++----------------- 5 files changed, 152 insertions(+), 74 deletions(-) create mode 100644 cmd/exec.dsp diff --git a/all.dsp b/all.dsp index e2ce73d3..2358823a 100644 --- a/all.dsp +++ b/all.dsp @@ -44,8 +44,8 @@ MTL=midl.exe # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "all___Win32_Debug" -# PROP BASE Intermediate_Dir "all___Win32_Debug" +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 diff --git a/cmd/exec.dsp b/cmd/exec.dsp new file mode 100644 index 00000000..99fc73b7 --- /dev/null +++ b/cmd/exec.dsp @@ -0,0 +1,63 @@ +# Microsoft Developer Studio Project File - Name="exec" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Generic Project" 0x010a + +CFG=exec - Win32 Debug +!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 "exec.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 "exec.mak" CFG="exec - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "exec - Win32 Release" (based on "Win32 (x86) Generic Project") +!MESSAGE "exec - Win32 Debug" (based on "Win32 (x86) Generic Project") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +MTL=midl.exe + +!IF "$(CFG)" == "exec - 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" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "exec - 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" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "exec - Win32 Release" +# Name "exec - Win32 Debug" +# End Target +# End Project diff --git a/lib/platform/makehook.dsp b/lib/platform/makehook.dsp index 507fa6e9..f14304a9 100644 --- a/lib/platform/makehook.dsp +++ b/lib/platform/makehook.dsp @@ -31,8 +31,8 @@ MTL=midl.exe # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "makehook___Win32_Release" -# PROP BASE Intermediate_Dir "makehook___Win32_Release" +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 @@ -44,8 +44,8 @@ MTL=midl.exe # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "makehook___Win32_Debug" -# PROP BASE Intermediate_Dir "makehook___Win32_Debug" +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp index cec6eaf9..54fcc6a7 100644 --- a/lib/platform/synrgyhk.dsp +++ b/lib/platform/synrgyhk.dsp @@ -33,8 +33,8 @@ RSC=rc.exe # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "synrgyhk___Win32_Release" -# PROP BASE Intermediate_Dir "synrgyhk___Win32_Release" +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 @@ -61,8 +61,8 @@ LINK32=link.exe # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "synrgyhk___Win32_Debug" -# PROP BASE Intermediate_Dir "synrgyhk___Win32_Debug" +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 diff --git a/synergy.dsw b/synergy.dsw index a70a4d76..7148711f 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -3,7 +3,70 @@ Microsoft Developer Studio Workspace File, Format Version 6.00 ############################################################################### -Project: "all"=.\all.dsp - Package Owner=<4> +Project: "all"=".\all.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name launcher + End Project Dependency +}}} + +############################################################################### + +Project: "arch"=".\lib\arch\arch.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "base"=".\lib\base\base.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "client"=".\lib\client\client.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "common"=".\lib\common\common.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "exec"=".\cmd\exec.dsp" - Package Owner=<4> Package=<5> {{{ @@ -17,14 +80,11 @@ Package=<4> Begin Project Dependency Project_Dep_Name synergys End Project Dependency - Begin Project Dependency - Project_Dep_Name launcher - End Project Dependency }}} ############################################################################### -Project: "arch"=.\lib\arch\arch.dsp - Package Owner=<4> +Project: "http"=".\lib\HTTP\http.dsp" - Package Owner=<4> Package=<5> {{{ @@ -36,7 +96,7 @@ Package=<4> ############################################################################### -Project: "base"=.\lib\base\base.dsp - Package Owner=<4> +Project: "io"=".\lib\io\io.dsp" - Package Owner=<4> Package=<5> {{{ @@ -48,55 +108,7 @@ Package=<4> ############################################################################### -Project: "client"=.\lib\client\client.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "common"=.\lib\common\common.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "http"=.\lib\HTTP\http.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "io"=.\lib\io\io.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "launcher"=.\cmd\launcher\launcher.dsp - Package Owner=<4> +Project: "launcher"=".\cmd\launcher\launcher.dsp" - Package Owner=<4> Package=<5> {{{ @@ -131,11 +143,14 @@ Package=<4> Begin Project Dependency Project_Dep_Name server End Project Dependency + Begin Project Dependency + Project_Dep_Name exec + End Project Dependency }}} ############################################################################### -Project: "libsynergy"=.\lib\synergy\libsynergy.dsp - Package Owner=<4> +Project: "libsynergy"=".\lib\synergy\libsynergy.dsp" - Package Owner=<4> Package=<5> {{{ @@ -147,7 +162,7 @@ Package=<4> ############################################################################### -Project: "makehook"=.\lib\platform\makehook.dsp - Package Owner=<4> +Project: "makehook"=".\lib\platform\makehook.dsp" - Package Owner=<4> Package=<5> {{{ @@ -162,7 +177,7 @@ Package=<4> ############################################################################### -Project: "mt"=.\lib\mt\mt.dsp - Package Owner=<4> +Project: "mt"=".\lib\mt\mt.dsp" - Package Owner=<4> Package=<5> {{{ @@ -174,7 +189,7 @@ Package=<4> ############################################################################### -Project: "net"=.\lib\net\net.dsp - Package Owner=<4> +Project: "net"=".\lib\net\net.dsp" - Package Owner=<4> Package=<5> {{{ @@ -186,7 +201,7 @@ Package=<4> ############################################################################### -Project: "platform"=.\lib\platform\platform.dsp - Package Owner=<4> +Project: "platform"=".\lib\platform\platform.dsp" - Package Owner=<4> Package=<5> {{{ @@ -198,7 +213,7 @@ Package=<4> ############################################################################### -Project: "server"=.\lib\server\server.dsp - Package Owner=<4> +Project: "server"=".\lib\server\server.dsp" - Package Owner=<4> Package=<5> {{{ @@ -210,7 +225,7 @@ Package=<4> ############################################################################### -Project: "synergyc"=.\cmd\synergyc\synergyc.dsp - Package Owner=<4> +Project: "synergyc"=".\cmd\synergyc\synergyc.dsp" - Package Owner=<4> Package=<5> {{{ @@ -249,7 +264,7 @@ Package=<4> ############################################################################### -Project: "synergys"=.\cmd\synergys\synergys.dsp - Package Owner=<4> +Project: "synergys"=".\cmd\synergys\synergys.dsp" - Package Owner=<4> Package=<5> {{{ @@ -294,7 +309,7 @@ Package=<4> ############################################################################### -Project: "synrgyhk"=.\lib\platform\synrgyhk.dsp - Package Owner=<4> +Project: "synrgyhk"=".\lib\platform\synrgyhk.dsp" - Package Owner=<4> Package=<5> {{{ From 7791b167bf0d5dd83b46bdbffdf61d2c20f59596 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 24 Apr 2003 20:10:13 +0000 Subject: [PATCH 491/807] Added exec.dsp to EXTRA_DIST. --- cmd/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/Makefile.am b/cmd/Makefile.am index c1953b4c..3d028996 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -22,6 +22,7 @@ SUBDIRS = \ $(NULL) EXTRA_DIST = \ + exec.dsp \ $(NULL) MAINTAINERCLEANFILES = \ From cf7ab3459d73674163130d0cdbe62671e509b02c Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 24 Apr 2003 20:11:38 +0000 Subject: [PATCH 492/807] Added KeySym <-> Unicode mappings. Changed code to use those mappings to better support Unicode key events. --- lib/platform/CXWindowsPrimaryScreen.cpp | 11 +- lib/platform/CXWindowsSecondaryScreen.cpp | 37 +- lib/platform/CXWindowsUtil.cpp | 834 ++++++++++++++++++++++ lib/platform/CXWindowsUtil.h | 24 + 4 files changed, 889 insertions(+), 17 deletions(-) diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp index a99cfea9..54cf8898 100644 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -720,10 +720,17 @@ CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const // MISCELLANY return static_cast(keysym - 0xff00 + 0xef00); - default: - // FIXME -- support unicode characters + default: { + // lookup character in table + UInt32 key = CXWindowsUtil::mapKeySymToUCS4(keysym); + if (key != 0x0000ffff) { + return static_cast(key); + } + + // unknown character return kKeyNone; } + } } ButtonID diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 19f1a6f8..7a67783c 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -1263,23 +1263,30 @@ CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask& mask) const { // convert id to keysym KeySym keysym = NoSymbol; - switch (id & 0xffffff00) { - case 0x0000: - // Latin-1 - keysym = static_cast(id); - break; + if ((id & 0xfffff000) == 0xe000) { + // special character + switch (id & 0x0000ff00) { + case 0xee00: + // ISO 9995 Function and Modifier Keys + if (id == kKeyLeftTab) { + keysym = XK_ISO_Left_Tab; + } + break; - case 0xee00: - // ISO 9995 Function and Modifier Keys - if (id == kKeyLeftTab) { - keysym = XK_ISO_Left_Tab; + case 0xef00: + // MISCELLANY + keysym = static_cast(id - 0xef00 + 0xff00); + break; } - break; - - case 0xef00: - // MISCELLANY - keysym = static_cast(id - 0xef00 + 0xff00); - break; + } + else if ((id >= 0x0020 && id <= 0x007e) || + (id >= 0x00a0 && id <= 0x00ff)) { + // Latin-1 maps directly + keysym = static_cast(id); + } + else { + // lookup keysym in table + keysym = CXWindowsUtil::mapUCS4ToKeySym(id); } // fail if unknown key diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 399f2c27..05415ddc 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -17,10 +17,802 @@ #include "CLog.h" #include +/* + * This table maps keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. + * + * Author: Markus G. Kuhn , + * University of Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven for preparing + * an initial draft of the mapping table. + * + * This software is in the public domain. Share and enjoy! + */ + +struct codepair { + KeySym keysym; + UInt32 ucs4; +} s_keymap[] = { +{ 0x01a1, 0x0104 }, /* Aogonek LATIN CAPITAL LETTER A WITH OGONEK */ +{ 0x01a2, 0x02d8 }, /* breve BREVE */ +{ 0x01a3, 0x0141 }, /* Lstroke LATIN CAPITAL LETTER L WITH STROKE */ +{ 0x01a5, 0x013d }, /* Lcaron LATIN CAPITAL LETTER L WITH CARON */ +{ 0x01a6, 0x015a }, /* Sacute LATIN CAPITAL LETTER S WITH ACUTE */ +{ 0x01a9, 0x0160 }, /* Scaron LATIN CAPITAL LETTER S WITH CARON */ +{ 0x01aa, 0x015e }, /* Scedilla LATIN CAPITAL LETTER S WITH CEDILLA */ +{ 0x01ab, 0x0164 }, /* Tcaron LATIN CAPITAL LETTER T WITH CARON */ +{ 0x01ac, 0x0179 }, /* Zacute LATIN CAPITAL LETTER Z WITH ACUTE */ +{ 0x01ae, 0x017d }, /* Zcaron LATIN CAPITAL LETTER Z WITH CARON */ +{ 0x01af, 0x017b }, /* Zabovedot LATIN CAPITAL LETTER Z WITH DOT ABOVE */ +{ 0x01b1, 0x0105 }, /* aogonek LATIN SMALL LETTER A WITH OGONEK */ +{ 0x01b2, 0x02db }, /* ogonek OGONEK */ +{ 0x01b3, 0x0142 }, /* lstroke LATIN SMALL LETTER L WITH STROKE */ +{ 0x01b5, 0x013e }, /* lcaron LATIN SMALL LETTER L WITH CARON */ +{ 0x01b6, 0x015b }, /* sacute LATIN SMALL LETTER S WITH ACUTE */ +{ 0x01b7, 0x02c7 }, /* caron CARON */ +{ 0x01b9, 0x0161 }, /* scaron LATIN SMALL LETTER S WITH CARON */ +{ 0x01ba, 0x015f }, /* scedilla LATIN SMALL LETTER S WITH CEDILLA */ +{ 0x01bb, 0x0165 }, /* tcaron LATIN SMALL LETTER T WITH CARON */ +{ 0x01bc, 0x017a }, /* zacute LATIN SMALL LETTER Z WITH ACUTE */ +{ 0x01bd, 0x02dd }, /* doubleacute DOUBLE ACUTE ACCENT */ +{ 0x01be, 0x017e }, /* zcaron LATIN SMALL LETTER Z WITH CARON */ +{ 0x01bf, 0x017c }, /* zabovedot LATIN SMALL LETTER Z WITH DOT ABOVE */ +{ 0x01c0, 0x0154 }, /* Racute LATIN CAPITAL LETTER R WITH ACUTE */ +{ 0x01c3, 0x0102 }, /* Abreve LATIN CAPITAL LETTER A WITH BREVE */ +{ 0x01c5, 0x0139 }, /* Lacute LATIN CAPITAL LETTER L WITH ACUTE */ +{ 0x01c6, 0x0106 }, /* Cacute LATIN CAPITAL LETTER C WITH ACUTE */ +{ 0x01c8, 0x010c }, /* Ccaron LATIN CAPITAL LETTER C WITH CARON */ +{ 0x01ca, 0x0118 }, /* Eogonek LATIN CAPITAL LETTER E WITH OGONEK */ +{ 0x01cc, 0x011a }, /* Ecaron LATIN CAPITAL LETTER E WITH CARON */ +{ 0x01cf, 0x010e }, /* Dcaron LATIN CAPITAL LETTER D WITH CARON */ +{ 0x01d0, 0x0110 }, /* Dstroke LATIN CAPITAL LETTER D WITH STROKE */ +{ 0x01d1, 0x0143 }, /* Nacute LATIN CAPITAL LETTER N WITH ACUTE */ +{ 0x01d2, 0x0147 }, /* Ncaron LATIN CAPITAL LETTER N WITH CARON */ +{ 0x01d5, 0x0150 }, /* Odoubleacute LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ +{ 0x01d8, 0x0158 }, /* Rcaron LATIN CAPITAL LETTER R WITH CARON */ +{ 0x01d9, 0x016e }, /* Uring LATIN CAPITAL LETTER U WITH RING ABOVE */ +{ 0x01db, 0x0170 }, /* Udoubleacute LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ +{ 0x01de, 0x0162 }, /* Tcedilla LATIN CAPITAL LETTER T WITH CEDILLA */ +{ 0x01e0, 0x0155 }, /* racute LATIN SMALL LETTER R WITH ACUTE */ +{ 0x01e3, 0x0103 }, /* abreve LATIN SMALL LETTER A WITH BREVE */ +{ 0x01e5, 0x013a }, /* lacute LATIN SMALL LETTER L WITH ACUTE */ +{ 0x01e6, 0x0107 }, /* cacute LATIN SMALL LETTER C WITH ACUTE */ +{ 0x01e8, 0x010d }, /* ccaron LATIN SMALL LETTER C WITH CARON */ +{ 0x01ea, 0x0119 }, /* eogonek LATIN SMALL LETTER E WITH OGONEK */ +{ 0x01ec, 0x011b }, /* ecaron LATIN SMALL LETTER E WITH CARON */ +{ 0x01ef, 0x010f }, /* dcaron LATIN SMALL LETTER D WITH CARON */ +{ 0x01f0, 0x0111 }, /* dstroke LATIN SMALL LETTER D WITH STROKE */ +{ 0x01f1, 0x0144 }, /* nacute LATIN SMALL LETTER N WITH ACUTE */ +{ 0x01f2, 0x0148 }, /* ncaron LATIN SMALL LETTER N WITH CARON */ +{ 0x01f5, 0x0151 }, /* odoubleacute LATIN SMALL LETTER O WITH DOUBLE ACUTE */ +{ 0x01f8, 0x0159 }, /* rcaron LATIN SMALL LETTER R WITH CARON */ +{ 0x01f9, 0x016f }, /* uring LATIN SMALL LETTER U WITH RING ABOVE */ +{ 0x01fb, 0x0171 }, /* udoubleacute LATIN SMALL LETTER U WITH DOUBLE ACUTE */ +{ 0x01fe, 0x0163 }, /* tcedilla LATIN SMALL LETTER T WITH CEDILLA */ +{ 0x01ff, 0x02d9 }, /* abovedot DOT ABOVE */ +{ 0x02a1, 0x0126 }, /* Hstroke LATIN CAPITAL LETTER H WITH STROKE */ +{ 0x02a6, 0x0124 }, /* Hcircumflex LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ +{ 0x02a9, 0x0130 }, /* Iabovedot LATIN CAPITAL LETTER I WITH DOT ABOVE */ +{ 0x02ab, 0x011e }, /* Gbreve LATIN CAPITAL LETTER G WITH BREVE */ +{ 0x02ac, 0x0134 }, /* Jcircumflex LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ +{ 0x02b1, 0x0127 }, /* hstroke LATIN SMALL LETTER H WITH STROKE */ +{ 0x02b6, 0x0125 }, /* hcircumflex LATIN SMALL LETTER H WITH CIRCUMFLEX */ +{ 0x02b9, 0x0131 }, /* idotless LATIN SMALL LETTER DOTLESS I */ +{ 0x02bb, 0x011f }, /* gbreve LATIN SMALL LETTER G WITH BREVE */ +{ 0x02bc, 0x0135 }, /* jcircumflex LATIN SMALL LETTER J WITH CIRCUMFLEX */ +{ 0x02c5, 0x010a }, /* Cabovedot LATIN CAPITAL LETTER C WITH DOT ABOVE */ +{ 0x02c6, 0x0108 }, /* Ccircumflex LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ +{ 0x02d5, 0x0120 }, /* Gabovedot LATIN CAPITAL LETTER G WITH DOT ABOVE */ +{ 0x02d8, 0x011c }, /* Gcircumflex LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ +{ 0x02dd, 0x016c }, /* Ubreve LATIN CAPITAL LETTER U WITH BREVE */ +{ 0x02de, 0x015c }, /* Scircumflex LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ +{ 0x02e5, 0x010b }, /* cabovedot LATIN SMALL LETTER C WITH DOT ABOVE */ +{ 0x02e6, 0x0109 }, /* ccircumflex LATIN SMALL LETTER C WITH CIRCUMFLEX */ +{ 0x02f5, 0x0121 }, /* gabovedot LATIN SMALL LETTER G WITH DOT ABOVE */ +{ 0x02f8, 0x011d }, /* gcircumflex LATIN SMALL LETTER G WITH CIRCUMFLEX */ +{ 0x02fd, 0x016d }, /* ubreve LATIN SMALL LETTER U WITH BREVE */ +{ 0x02fe, 0x015d }, /* scircumflex LATIN SMALL LETTER S WITH CIRCUMFLEX */ +{ 0x03a2, 0x0138 }, /* kra LATIN SMALL LETTER KRA */ +{ 0x03a3, 0x0156 }, /* Rcedilla LATIN CAPITAL LETTER R WITH CEDILLA */ +{ 0x03a5, 0x0128 }, /* Itilde LATIN CAPITAL LETTER I WITH TILDE */ +{ 0x03a6, 0x013b }, /* Lcedilla LATIN CAPITAL LETTER L WITH CEDILLA */ +{ 0x03aa, 0x0112 }, /* Emacron LATIN CAPITAL LETTER E WITH MACRON */ +{ 0x03ab, 0x0122 }, /* Gcedilla LATIN CAPITAL LETTER G WITH CEDILLA */ +{ 0x03ac, 0x0166 }, /* Tslash LATIN CAPITAL LETTER T WITH STROKE */ +{ 0x03b3, 0x0157 }, /* rcedilla LATIN SMALL LETTER R WITH CEDILLA */ +{ 0x03b5, 0x0129 }, /* itilde LATIN SMALL LETTER I WITH TILDE */ +{ 0x03b6, 0x013c }, /* lcedilla LATIN SMALL LETTER L WITH CEDILLA */ +{ 0x03ba, 0x0113 }, /* emacron LATIN SMALL LETTER E WITH MACRON */ +{ 0x03bb, 0x0123 }, /* gcedilla LATIN SMALL LETTER G WITH CEDILLA */ +{ 0x03bc, 0x0167 }, /* tslash LATIN SMALL LETTER T WITH STROKE */ +{ 0x03bd, 0x014a }, /* ENG LATIN CAPITAL LETTER ENG */ +{ 0x03bf, 0x014b }, /* eng LATIN SMALL LETTER ENG */ +{ 0x03c0, 0x0100 }, /* Amacron LATIN CAPITAL LETTER A WITH MACRON */ +{ 0x03c7, 0x012e }, /* Iogonek LATIN CAPITAL LETTER I WITH OGONEK */ +{ 0x03cc, 0x0116 }, /* Eabovedot LATIN CAPITAL LETTER E WITH DOT ABOVE */ +{ 0x03cf, 0x012a }, /* Imacron LATIN CAPITAL LETTER I WITH MACRON */ +{ 0x03d1, 0x0145 }, /* Ncedilla LATIN CAPITAL LETTER N WITH CEDILLA */ +{ 0x03d2, 0x014c }, /* Omacron LATIN CAPITAL LETTER O WITH MACRON */ +{ 0x03d3, 0x0136 }, /* Kcedilla LATIN CAPITAL LETTER K WITH CEDILLA */ +{ 0x03d9, 0x0172 }, /* Uogonek LATIN CAPITAL LETTER U WITH OGONEK */ +{ 0x03dd, 0x0168 }, /* Utilde LATIN CAPITAL LETTER U WITH TILDE */ +{ 0x03de, 0x016a }, /* Umacron LATIN CAPITAL LETTER U WITH MACRON */ +{ 0x03e0, 0x0101 }, /* amacron LATIN SMALL LETTER A WITH MACRON */ +{ 0x03e7, 0x012f }, /* iogonek LATIN SMALL LETTER I WITH OGONEK */ +{ 0x03ec, 0x0117 }, /* eabovedot LATIN SMALL LETTER E WITH DOT ABOVE */ +{ 0x03ef, 0x012b }, /* imacron LATIN SMALL LETTER I WITH MACRON */ +{ 0x03f1, 0x0146 }, /* ncedilla LATIN SMALL LETTER N WITH CEDILLA */ +{ 0x03f2, 0x014d }, /* omacron LATIN SMALL LETTER O WITH MACRON */ +{ 0x03f3, 0x0137 }, /* kcedilla LATIN SMALL LETTER K WITH CEDILLA */ +{ 0x03f9, 0x0173 }, /* uogonek LATIN SMALL LETTER U WITH OGONEK */ +{ 0x03fd, 0x0169 }, /* utilde LATIN SMALL LETTER U WITH TILDE */ +{ 0x03fe, 0x016b }, /* umacron LATIN SMALL LETTER U WITH MACRON */ +{ 0x047e, 0x203e }, /* overline OVERLINE */ +{ 0x04a1, 0x3002 }, /* kana_fullstop IDEOGRAPHIC FULL STOP */ +{ 0x04a2, 0x300c }, /* kana_openingbracket LEFT CORNER BRACKET */ +{ 0x04a3, 0x300d }, /* kana_closingbracket RIGHT CORNER BRACKET */ +{ 0x04a4, 0x3001 }, /* kana_comma IDEOGRAPHIC COMMA */ +{ 0x04a5, 0x30fb }, /* kana_conjunctive KATAKANA MIDDLE DOT */ +{ 0x04a6, 0x30f2 }, /* kana_WO KATAKANA LETTER WO */ +{ 0x04a7, 0x30a1 }, /* kana_a KATAKANA LETTER SMALL A */ +{ 0x04a8, 0x30a3 }, /* kana_i KATAKANA LETTER SMALL I */ +{ 0x04a9, 0x30a5 }, /* kana_u KATAKANA LETTER SMALL U */ +{ 0x04aa, 0x30a7 }, /* kana_e KATAKANA LETTER SMALL E */ +{ 0x04ab, 0x30a9 }, /* kana_o KATAKANA LETTER SMALL O */ +{ 0x04ac, 0x30e3 }, /* kana_ya KATAKANA LETTER SMALL YA */ +{ 0x04ad, 0x30e5 }, /* kana_yu KATAKANA LETTER SMALL YU */ +{ 0x04ae, 0x30e7 }, /* kana_yo KATAKANA LETTER SMALL YO */ +{ 0x04af, 0x30c3 }, /* kana_tsu KATAKANA LETTER SMALL TU */ +{ 0x04b0, 0x30fc }, /* prolongedsound KATAKANA-HIRAGANA PROLONGED SOUND MARK */ +{ 0x04b1, 0x30a2 }, /* kana_A KATAKANA LETTER A */ +{ 0x04b2, 0x30a4 }, /* kana_I KATAKANA LETTER I */ +{ 0x04b3, 0x30a6 }, /* kana_U KATAKANA LETTER U */ +{ 0x04b4, 0x30a8 }, /* kana_E KATAKANA LETTER E */ +{ 0x04b5, 0x30aa }, /* kana_O KATAKANA LETTER O */ +{ 0x04b6, 0x30ab }, /* kana_KA KATAKANA LETTER KA */ +{ 0x04b7, 0x30ad }, /* kana_KI KATAKANA LETTER KI */ +{ 0x04b8, 0x30af }, /* kana_KU KATAKANA LETTER KU */ +{ 0x04b9, 0x30b1 }, /* kana_KE KATAKANA LETTER KE */ +{ 0x04ba, 0x30b3 }, /* kana_KO KATAKANA LETTER KO */ +{ 0x04bb, 0x30b5 }, /* kana_SA KATAKANA LETTER SA */ +{ 0x04bc, 0x30b7 }, /* kana_SHI KATAKANA LETTER SI */ +{ 0x04bd, 0x30b9 }, /* kana_SU KATAKANA LETTER SU */ +{ 0x04be, 0x30bb }, /* kana_SE KATAKANA LETTER SE */ +{ 0x04bf, 0x30bd }, /* kana_SO KATAKANA LETTER SO */ +{ 0x04c0, 0x30bf }, /* kana_TA KATAKANA LETTER TA */ +{ 0x04c1, 0x30c1 }, /* kana_CHI KATAKANA LETTER TI */ +{ 0x04c2, 0x30c4 }, /* kana_TSU KATAKANA LETTER TU */ +{ 0x04c3, 0x30c6 }, /* kana_TE KATAKANA LETTER TE */ +{ 0x04c4, 0x30c8 }, /* kana_TO KATAKANA LETTER TO */ +{ 0x04c5, 0x30ca }, /* kana_NA KATAKANA LETTER NA */ +{ 0x04c6, 0x30cb }, /* kana_NI KATAKANA LETTER NI */ +{ 0x04c7, 0x30cc }, /* kana_NU KATAKANA LETTER NU */ +{ 0x04c8, 0x30cd }, /* kana_NE KATAKANA LETTER NE */ +{ 0x04c9, 0x30ce }, /* kana_NO KATAKANA LETTER NO */ +{ 0x04ca, 0x30cf }, /* kana_HA KATAKANA LETTER HA */ +{ 0x04cb, 0x30d2 }, /* kana_HI KATAKANA LETTER HI */ +{ 0x04cc, 0x30d5 }, /* kana_FU KATAKANA LETTER HU */ +{ 0x04cd, 0x30d8 }, /* kana_HE KATAKANA LETTER HE */ +{ 0x04ce, 0x30db }, /* kana_HO KATAKANA LETTER HO */ +{ 0x04cf, 0x30de }, /* kana_MA KATAKANA LETTER MA */ +{ 0x04d0, 0x30df }, /* kana_MI KATAKANA LETTER MI */ +{ 0x04d1, 0x30e0 }, /* kana_MU KATAKANA LETTER MU */ +{ 0x04d2, 0x30e1 }, /* kana_ME KATAKANA LETTER ME */ +{ 0x04d3, 0x30e2 }, /* kana_MO KATAKANA LETTER MO */ +{ 0x04d4, 0x30e4 }, /* kana_YA KATAKANA LETTER YA */ +{ 0x04d5, 0x30e6 }, /* kana_YU KATAKANA LETTER YU */ +{ 0x04d6, 0x30e8 }, /* kana_YO KATAKANA LETTER YO */ +{ 0x04d7, 0x30e9 }, /* kana_RA KATAKANA LETTER RA */ +{ 0x04d8, 0x30ea }, /* kana_RI KATAKANA LETTER RI */ +{ 0x04d9, 0x30eb }, /* kana_RU KATAKANA LETTER RU */ +{ 0x04da, 0x30ec }, /* kana_RE KATAKANA LETTER RE */ +{ 0x04db, 0x30ed }, /* kana_RO KATAKANA LETTER RO */ +{ 0x04dc, 0x30ef }, /* kana_WA KATAKANA LETTER WA */ +{ 0x04dd, 0x30f3 }, /* kana_N KATAKANA LETTER N */ +{ 0x04de, 0x309b }, /* voicedsound KATAKANA-HIRAGANA VOICED SOUND MARK */ +{ 0x04df, 0x309c }, /* semivoicedsound KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ +{ 0x05ac, 0x060c }, /* Arabic_comma ARABIC COMMA */ +{ 0x05bb, 0x061b }, /* Arabic_semicolon ARABIC SEMICOLON */ +{ 0x05bf, 0x061f }, /* Arabic_question_mark ARABIC QUESTION MARK */ +{ 0x05c1, 0x0621 }, /* Arabic_hamza ARABIC LETTER HAMZA */ +{ 0x05c2, 0x0622 }, /* Arabic_maddaonalef ARABIC LETTER ALEF WITH MADDA ABOVE */ +{ 0x05c3, 0x0623 }, /* Arabic_hamzaonalef ARABIC LETTER ALEF WITH HAMZA ABOVE */ +{ 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ARABIC LETTER WAW WITH HAMZA ABOVE */ +{ 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef ARABIC LETTER ALEF WITH HAMZA BELOW */ +{ 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ARABIC LETTER YEH WITH HAMZA ABOVE */ +{ 0x05c7, 0x0627 }, /* Arabic_alef ARABIC LETTER ALEF */ +{ 0x05c8, 0x0628 }, /* Arabic_beh ARABIC LETTER BEH */ +{ 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ARABIC LETTER TEH MARBUTA */ +{ 0x05ca, 0x062a }, /* Arabic_teh ARABIC LETTER TEH */ +{ 0x05cb, 0x062b }, /* Arabic_theh ARABIC LETTER THEH */ +{ 0x05cc, 0x062c }, /* Arabic_jeem ARABIC LETTER JEEM */ +{ 0x05cd, 0x062d }, /* Arabic_hah ARABIC LETTER HAH */ +{ 0x05ce, 0x062e }, /* Arabic_khah ARABIC LETTER KHAH */ +{ 0x05cf, 0x062f }, /* Arabic_dal ARABIC LETTER DAL */ +{ 0x05d0, 0x0630 }, /* Arabic_thal ARABIC LETTER THAL */ +{ 0x05d1, 0x0631 }, /* Arabic_ra ARABIC LETTER REH */ +{ 0x05d2, 0x0632 }, /* Arabic_zain ARABIC LETTER ZAIN */ +{ 0x05d3, 0x0633 }, /* Arabic_seen ARABIC LETTER SEEN */ +{ 0x05d4, 0x0634 }, /* Arabic_sheen ARABIC LETTER SHEEN */ +{ 0x05d5, 0x0635 }, /* Arabic_sad ARABIC LETTER SAD */ +{ 0x05d6, 0x0636 }, /* Arabic_dad ARABIC LETTER DAD */ +{ 0x05d7, 0x0637 }, /* Arabic_tah ARABIC LETTER TAH */ +{ 0x05d8, 0x0638 }, /* Arabic_zah ARABIC LETTER ZAH */ +{ 0x05d9, 0x0639 }, /* Arabic_ain ARABIC LETTER AIN */ +{ 0x05da, 0x063a }, /* Arabic_ghain ARABIC LETTER GHAIN */ +{ 0x05e0, 0x0640 }, /* Arabic_tatweel ARABIC TATWEEL */ +{ 0x05e1, 0x0641 }, /* Arabic_feh ARABIC LETTER FEH */ +{ 0x05e2, 0x0642 }, /* Arabic_qaf ARABIC LETTER QAF */ +{ 0x05e3, 0x0643 }, /* Arabic_kaf ARABIC LETTER KAF */ +{ 0x05e4, 0x0644 }, /* Arabic_lam ARABIC LETTER LAM */ +{ 0x05e5, 0x0645 }, /* Arabic_meem ARABIC LETTER MEEM */ +{ 0x05e6, 0x0646 }, /* Arabic_noon ARABIC LETTER NOON */ +{ 0x05e7, 0x0647 }, /* Arabic_ha ARABIC LETTER HEH */ +{ 0x05e8, 0x0648 }, /* Arabic_waw ARABIC LETTER WAW */ +{ 0x05e9, 0x0649 }, /* Arabic_alefmaksura ARABIC LETTER ALEF MAKSURA */ +{ 0x05ea, 0x064a }, /* Arabic_yeh ARABIC LETTER YEH */ +{ 0x05eb, 0x064b }, /* Arabic_fathatan ARABIC FATHATAN */ +{ 0x05ec, 0x064c }, /* Arabic_dammatan ARABIC DAMMATAN */ +{ 0x05ed, 0x064d }, /* Arabic_kasratan ARABIC KASRATAN */ +{ 0x05ee, 0x064e }, /* Arabic_fatha ARABIC FATHA */ +{ 0x05ef, 0x064f }, /* Arabic_damma ARABIC DAMMA */ +{ 0x05f0, 0x0650 }, /* Arabic_kasra ARABIC KASRA */ +{ 0x05f1, 0x0651 }, /* Arabic_shadda ARABIC SHADDA */ +{ 0x05f2, 0x0652 }, /* Arabic_sukun ARABIC SUKUN */ +{ 0x06a1, 0x0452 }, /* Serbian_dje CYRILLIC SMALL LETTER DJE */ +{ 0x06a2, 0x0453 }, /* Macedonia_gje CYRILLIC SMALL LETTER GJE */ +{ 0x06a3, 0x0451 }, /* Cyrillic_io CYRILLIC SMALL LETTER IO */ +{ 0x06a4, 0x0454 }, /* Ukrainian_ie CYRILLIC SMALL LETTER UKRAINIAN IE */ +{ 0x06a5, 0x0455 }, /* Macedonia_dse CYRILLIC SMALL LETTER DZE */ +{ 0x06a6, 0x0456 }, /* Ukrainian_i CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ +{ 0x06a7, 0x0457 }, /* Ukrainian_yi CYRILLIC SMALL LETTER YI */ +{ 0x06a8, 0x0458 }, /* Cyrillic_je CYRILLIC SMALL LETTER JE */ +{ 0x06a9, 0x0459 }, /* Cyrillic_lje CYRILLIC SMALL LETTER LJE */ +{ 0x06aa, 0x045a }, /* Cyrillic_nje CYRILLIC SMALL LETTER NJE */ +{ 0x06ab, 0x045b }, /* Serbian_tshe CYRILLIC SMALL LETTER TSHE */ +{ 0x06ac, 0x045c }, /* Macedonia_kje CYRILLIC SMALL LETTER KJE */ +{ 0x06ae, 0x045e }, /* Byelorussian_shortu CYRILLIC SMALL LETTER SHORT U */ +{ 0x06af, 0x045f }, /* Cyrillic_dzhe CYRILLIC SMALL LETTER DZHE */ +{ 0x06b0, 0x2116 }, /* numerosign NUMERO SIGN */ +{ 0x06b1, 0x0402 }, /* Serbian_DJE CYRILLIC CAPITAL LETTER DJE */ +{ 0x06b2, 0x0403 }, /* Macedonia_GJE CYRILLIC CAPITAL LETTER GJE */ +{ 0x06b3, 0x0401 }, /* Cyrillic_IO CYRILLIC CAPITAL LETTER IO */ +{ 0x06b4, 0x0404 }, /* Ukrainian_IE CYRILLIC CAPITAL LETTER UKRAINIAN IE */ +{ 0x06b5, 0x0405 }, /* Macedonia_DSE CYRILLIC CAPITAL LETTER DZE */ +{ 0x06b6, 0x0406 }, /* Ukrainian_I CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ +{ 0x06b7, 0x0407 }, /* Ukrainian_YI CYRILLIC CAPITAL LETTER YI */ +{ 0x06b8, 0x0408 }, /* Cyrillic_JE CYRILLIC CAPITAL LETTER JE */ +{ 0x06b9, 0x0409 }, /* Cyrillic_LJE CYRILLIC CAPITAL LETTER LJE */ +{ 0x06ba, 0x040a }, /* Cyrillic_NJE CYRILLIC CAPITAL LETTER NJE */ +{ 0x06bb, 0x040b }, /* Serbian_TSHE CYRILLIC CAPITAL LETTER TSHE */ +{ 0x06bc, 0x040c }, /* Macedonia_KJE CYRILLIC CAPITAL LETTER KJE */ +{ 0x06be, 0x040e }, /* Byelorussian_SHORTU CYRILLIC CAPITAL LETTER SHORT U */ +{ 0x06bf, 0x040f }, /* Cyrillic_DZHE CYRILLIC CAPITAL LETTER DZHE */ +{ 0x06c0, 0x044e }, /* Cyrillic_yu CYRILLIC SMALL LETTER YU */ +{ 0x06c1, 0x0430 }, /* Cyrillic_a CYRILLIC SMALL LETTER A */ +{ 0x06c2, 0x0431 }, /* Cyrillic_be CYRILLIC SMALL LETTER BE */ +{ 0x06c3, 0x0446 }, /* Cyrillic_tse CYRILLIC SMALL LETTER TSE */ +{ 0x06c4, 0x0434 }, /* Cyrillic_de CYRILLIC SMALL LETTER DE */ +{ 0x06c5, 0x0435 }, /* Cyrillic_ie CYRILLIC SMALL LETTER IE */ +{ 0x06c6, 0x0444 }, /* Cyrillic_ef CYRILLIC SMALL LETTER EF */ +{ 0x06c7, 0x0433 }, /* Cyrillic_ghe CYRILLIC SMALL LETTER GHE */ +{ 0x06c8, 0x0445 }, /* Cyrillic_ha CYRILLIC SMALL LETTER HA */ +{ 0x06c9, 0x0438 }, /* Cyrillic_i CYRILLIC SMALL LETTER I */ +{ 0x06ca, 0x0439 }, /* Cyrillic_shorti CYRILLIC SMALL LETTER SHORT I */ +{ 0x06cb, 0x043a }, /* Cyrillic_ka CYRILLIC SMALL LETTER KA */ +{ 0x06cc, 0x043b }, /* Cyrillic_el CYRILLIC SMALL LETTER EL */ +{ 0x06cd, 0x043c }, /* Cyrillic_em CYRILLIC SMALL LETTER EM */ +{ 0x06ce, 0x043d }, /* Cyrillic_en CYRILLIC SMALL LETTER EN */ +{ 0x06cf, 0x043e }, /* Cyrillic_o CYRILLIC SMALL LETTER O */ +{ 0x06d0, 0x043f }, /* Cyrillic_pe CYRILLIC SMALL LETTER PE */ +{ 0x06d1, 0x044f }, /* Cyrillic_ya CYRILLIC SMALL LETTER YA */ +{ 0x06d2, 0x0440 }, /* Cyrillic_er CYRILLIC SMALL LETTER ER */ +{ 0x06d3, 0x0441 }, /* Cyrillic_es CYRILLIC SMALL LETTER ES */ +{ 0x06d4, 0x0442 }, /* Cyrillic_te CYRILLIC SMALL LETTER TE */ +{ 0x06d5, 0x0443 }, /* Cyrillic_u CYRILLIC SMALL LETTER U */ +{ 0x06d6, 0x0436 }, /* Cyrillic_zhe CYRILLIC SMALL LETTER ZHE */ +{ 0x06d7, 0x0432 }, /* Cyrillic_ve CYRILLIC SMALL LETTER VE */ +{ 0x06d8, 0x044c }, /* Cyrillic_softsign CYRILLIC SMALL LETTER SOFT SIGN */ +{ 0x06d9, 0x044b }, /* Cyrillic_yeru CYRILLIC SMALL LETTER YERU */ +{ 0x06da, 0x0437 }, /* Cyrillic_ze CYRILLIC SMALL LETTER ZE */ +{ 0x06db, 0x0448 }, /* Cyrillic_sha CYRILLIC SMALL LETTER SHA */ +{ 0x06dc, 0x044d }, /* Cyrillic_e CYRILLIC SMALL LETTER E */ +{ 0x06dd, 0x0449 }, /* Cyrillic_shcha CYRILLIC SMALL LETTER SHCHA */ +{ 0x06de, 0x0447 }, /* Cyrillic_che CYRILLIC SMALL LETTER CHE */ +{ 0x06df, 0x044a }, /* Cyrillic_hardsign CYRILLIC SMALL LETTER HARD SIGN */ +{ 0x06e0, 0x042e }, /* Cyrillic_YU CYRILLIC CAPITAL LETTER YU */ +{ 0x06e1, 0x0410 }, /* Cyrillic_A CYRILLIC CAPITAL LETTER A */ +{ 0x06e2, 0x0411 }, /* Cyrillic_BE CYRILLIC CAPITAL LETTER BE */ +{ 0x06e3, 0x0426 }, /* Cyrillic_TSE CYRILLIC CAPITAL LETTER TSE */ +{ 0x06e4, 0x0414 }, /* Cyrillic_DE CYRILLIC CAPITAL LETTER DE */ +{ 0x06e5, 0x0415 }, /* Cyrillic_IE CYRILLIC CAPITAL LETTER IE */ +{ 0x06e6, 0x0424 }, /* Cyrillic_EF CYRILLIC CAPITAL LETTER EF */ +{ 0x06e7, 0x0413 }, /* Cyrillic_GHE CYRILLIC CAPITAL LETTER GHE */ +{ 0x06e8, 0x0425 }, /* Cyrillic_HA CYRILLIC CAPITAL LETTER HA */ +{ 0x06e9, 0x0418 }, /* Cyrillic_I CYRILLIC CAPITAL LETTER I */ +{ 0x06ea, 0x0419 }, /* Cyrillic_SHORTI CYRILLIC CAPITAL LETTER SHORT I */ +{ 0x06eb, 0x041a }, /* Cyrillic_KA CYRILLIC CAPITAL LETTER KA */ +{ 0x06ec, 0x041b }, /* Cyrillic_EL CYRILLIC CAPITAL LETTER EL */ +{ 0x06ed, 0x041c }, /* Cyrillic_EM CYRILLIC CAPITAL LETTER EM */ +{ 0x06ee, 0x041d }, /* Cyrillic_EN CYRILLIC CAPITAL LETTER EN */ +{ 0x06ef, 0x041e }, /* Cyrillic_O CYRILLIC CAPITAL LETTER O */ +{ 0x06f0, 0x041f }, /* Cyrillic_PE CYRILLIC CAPITAL LETTER PE */ +{ 0x06f1, 0x042f }, /* Cyrillic_YA CYRILLIC CAPITAL LETTER YA */ +{ 0x06f2, 0x0420 }, /* Cyrillic_ER CYRILLIC CAPITAL LETTER ER */ +{ 0x06f3, 0x0421 }, /* Cyrillic_ES CYRILLIC CAPITAL LETTER ES */ +{ 0x06f4, 0x0422 }, /* Cyrillic_TE CYRILLIC CAPITAL LETTER TE */ +{ 0x06f5, 0x0423 }, /* Cyrillic_U CYRILLIC CAPITAL LETTER U */ +{ 0x06f6, 0x0416 }, /* Cyrillic_ZHE CYRILLIC CAPITAL LETTER ZHE */ +{ 0x06f7, 0x0412 }, /* Cyrillic_VE CYRILLIC CAPITAL LETTER VE */ +{ 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN CYRILLIC CAPITAL LETTER SOFT SIGN */ +{ 0x06f9, 0x042b }, /* Cyrillic_YERU CYRILLIC CAPITAL LETTER YERU */ +{ 0x06fa, 0x0417 }, /* Cyrillic_ZE CYRILLIC CAPITAL LETTER ZE */ +{ 0x06fb, 0x0428 }, /* Cyrillic_SHA CYRILLIC CAPITAL LETTER SHA */ +{ 0x06fc, 0x042d }, /* Cyrillic_E CYRILLIC CAPITAL LETTER E */ +{ 0x06fd, 0x0429 }, /* Cyrillic_SHCHA CYRILLIC CAPITAL LETTER SHCHA */ +{ 0x06fe, 0x0427 }, /* Cyrillic_CHE CYRILLIC CAPITAL LETTER CHE */ +{ 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN CYRILLIC CAPITAL LETTER HARD SIGN */ +{ 0x07a1, 0x0386 }, /* Greek_ALPHAaccent GREEK CAPITAL LETTER ALPHA WITH TONOS */ +{ 0x07a2, 0x0388 }, /* Greek_EPSILONaccent GREEK CAPITAL LETTER EPSILON WITH TONOS */ +{ 0x07a3, 0x0389 }, /* Greek_ETAaccent GREEK CAPITAL LETTER ETA WITH TONOS */ +{ 0x07a4, 0x038a }, /* Greek_IOTAaccent GREEK CAPITAL LETTER IOTA WITH TONOS */ +{ 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ +{ 0x07a7, 0x038c }, /* Greek_OMICRONaccent GREEK CAPITAL LETTER OMICRON WITH TONOS */ +{ 0x07a8, 0x038e }, /* Greek_UPSILONaccent GREEK CAPITAL LETTER UPSILON WITH TONOS */ +{ 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ +{ 0x07ab, 0x038f }, /* Greek_OMEGAaccent GREEK CAPITAL LETTER OMEGA WITH TONOS */ +{ 0x07ae, 0x0385 }, /* Greek_accentdieresis GREEK DIALYTIKA TONOS */ +{ 0x07af, 0x2015 }, /* Greek_horizbar HORIZONTAL BAR */ +{ 0x07b1, 0x03ac }, /* Greek_alphaaccent GREEK SMALL LETTER ALPHA WITH TONOS */ +{ 0x07b2, 0x03ad }, /* Greek_epsilonaccent GREEK SMALL LETTER EPSILON WITH TONOS */ +{ 0x07b3, 0x03ae }, /* Greek_etaaccent GREEK SMALL LETTER ETA WITH TONOS */ +{ 0x07b4, 0x03af }, /* Greek_iotaaccent GREEK SMALL LETTER IOTA WITH TONOS */ +{ 0x07b5, 0x03ca }, /* Greek_iotadieresis GREEK SMALL LETTER IOTA WITH DIALYTIKA */ +{ 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ +{ 0x07b7, 0x03cc }, /* Greek_omicronaccent GREEK SMALL LETTER OMICRON WITH TONOS */ +{ 0x07b8, 0x03cd }, /* Greek_upsilonaccent GREEK SMALL LETTER UPSILON WITH TONOS */ +{ 0x07b9, 0x03cb }, /* Greek_upsilondieresis GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ +{ 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ +{ 0x07bb, 0x03ce }, /* Greek_omegaaccent GREEK SMALL LETTER OMEGA WITH TONOS */ +{ 0x07c1, 0x0391 }, /* Greek_ALPHA GREEK CAPITAL LETTER ALPHA */ +{ 0x07c2, 0x0392 }, /* Greek_BETA GREEK CAPITAL LETTER BETA */ +{ 0x07c3, 0x0393 }, /* Greek_GAMMA GREEK CAPITAL LETTER GAMMA */ +{ 0x07c4, 0x0394 }, /* Greek_DELTA GREEK CAPITAL LETTER DELTA */ +{ 0x07c5, 0x0395 }, /* Greek_EPSILON GREEK CAPITAL LETTER EPSILON */ +{ 0x07c6, 0x0396 }, /* Greek_ZETA GREEK CAPITAL LETTER ZETA */ +{ 0x07c7, 0x0397 }, /* Greek_ETA GREEK CAPITAL LETTER ETA */ +{ 0x07c8, 0x0398 }, /* Greek_THETA GREEK CAPITAL LETTER THETA */ +{ 0x07c9, 0x0399 }, /* Greek_IOTA GREEK CAPITAL LETTER IOTA */ +{ 0x07ca, 0x039a }, /* Greek_KAPPA GREEK CAPITAL LETTER KAPPA */ +{ 0x07cb, 0x039b }, /* Greek_LAMBDA GREEK CAPITAL LETTER LAMDA */ +{ 0x07cc, 0x039c }, /* Greek_MU GREEK CAPITAL LETTER MU */ +{ 0x07cd, 0x039d }, /* Greek_NU GREEK CAPITAL LETTER NU */ +{ 0x07ce, 0x039e }, /* Greek_XI GREEK CAPITAL LETTER XI */ +{ 0x07cf, 0x039f }, /* Greek_OMICRON GREEK CAPITAL LETTER OMICRON */ +{ 0x07d0, 0x03a0 }, /* Greek_PI GREEK CAPITAL LETTER PI */ +{ 0x07d1, 0x03a1 }, /* Greek_RHO GREEK CAPITAL LETTER RHO */ +{ 0x07d2, 0x03a3 }, /* Greek_SIGMA GREEK CAPITAL LETTER SIGMA */ +{ 0x07d4, 0x03a4 }, /* Greek_TAU GREEK CAPITAL LETTER TAU */ +{ 0x07d5, 0x03a5 }, /* Greek_UPSILON GREEK CAPITAL LETTER UPSILON */ +{ 0x07d6, 0x03a6 }, /* Greek_PHI GREEK CAPITAL LETTER PHI */ +{ 0x07d7, 0x03a7 }, /* Greek_CHI GREEK CAPITAL LETTER CHI */ +{ 0x07d8, 0x03a8 }, /* Greek_PSI GREEK CAPITAL LETTER PSI */ +{ 0x07d9, 0x03a9 }, /* Greek_OMEGA GREEK CAPITAL LETTER OMEGA */ +{ 0x07e1, 0x03b1 }, /* Greek_alpha GREEK SMALL LETTER ALPHA */ +{ 0x07e2, 0x03b2 }, /* Greek_beta GREEK SMALL LETTER BETA */ +{ 0x07e3, 0x03b3 }, /* Greek_gamma GREEK SMALL LETTER GAMMA */ +{ 0x07e4, 0x03b4 }, /* Greek_delta GREEK SMALL LETTER DELTA */ +{ 0x07e5, 0x03b5 }, /* Greek_epsilon GREEK SMALL LETTER EPSILON */ +{ 0x07e6, 0x03b6 }, /* Greek_zeta GREEK SMALL LETTER ZETA */ +{ 0x07e7, 0x03b7 }, /* Greek_eta GREEK SMALL LETTER ETA */ +{ 0x07e8, 0x03b8 }, /* Greek_theta GREEK SMALL LETTER THETA */ +{ 0x07e9, 0x03b9 }, /* Greek_iota GREEK SMALL LETTER IOTA */ +{ 0x07ea, 0x03ba }, /* Greek_kappa GREEK SMALL LETTER KAPPA */ +{ 0x07eb, 0x03bb }, /* Greek_lambda GREEK SMALL LETTER LAMDA */ +{ 0x07ec, 0x03bc }, /* Greek_mu GREEK SMALL LETTER MU */ +{ 0x07ed, 0x03bd }, /* Greek_nu GREEK SMALL LETTER NU */ +{ 0x07ee, 0x03be }, /* Greek_xi GREEK SMALL LETTER XI */ +{ 0x07ef, 0x03bf }, /* Greek_omicron GREEK SMALL LETTER OMICRON */ +{ 0x07f0, 0x03c0 }, /* Greek_pi GREEK SMALL LETTER PI */ +{ 0x07f1, 0x03c1 }, /* Greek_rho GREEK SMALL LETTER RHO */ +{ 0x07f2, 0x03c3 }, /* Greek_sigma GREEK SMALL LETTER SIGMA */ +{ 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma GREEK SMALL LETTER FINAL SIGMA */ +{ 0x07f4, 0x03c4 }, /* Greek_tau GREEK SMALL LETTER TAU */ +{ 0x07f5, 0x03c5 }, /* Greek_upsilon GREEK SMALL LETTER UPSILON */ +{ 0x07f6, 0x03c6 }, /* Greek_phi GREEK SMALL LETTER PHI */ +{ 0x07f7, 0x03c7 }, /* Greek_chi GREEK SMALL LETTER CHI */ +{ 0x07f8, 0x03c8 }, /* Greek_psi GREEK SMALL LETTER PSI */ +{ 0x07f9, 0x03c9 }, /* Greek_omega GREEK SMALL LETTER OMEGA */ +{ 0x08a1, 0x23b7 }, /* leftradical ??? */ +{ 0x08a2, 0x250c }, /* topleftradical BOX DRAWINGS LIGHT DOWN AND RIGHT */ +{ 0x08a3, 0x2500 }, /* horizconnector BOX DRAWINGS LIGHT HORIZONTAL */ +{ 0x08a4, 0x2320 }, /* topintegral TOP HALF INTEGRAL */ +{ 0x08a5, 0x2321 }, /* botintegral BOTTOM HALF INTEGRAL */ +{ 0x08a6, 0x2502 }, /* vertconnector BOX DRAWINGS LIGHT VERTICAL */ +{ 0x08a7, 0x23a1 }, /* topleftsqbracket ??? */ +{ 0x08a8, 0x23a3 }, /* botleftsqbracket ??? */ +{ 0x08a9, 0x23a4 }, /* toprightsqbracket ??? */ +{ 0x08aa, 0x23a6 }, /* botrightsqbracket ??? */ +{ 0x08ab, 0x239b }, /* topleftparens ??? */ +{ 0x08ac, 0x239d }, /* botleftparens ??? */ +{ 0x08ad, 0x239e }, /* toprightparens ??? */ +{ 0x08ae, 0x23a0 }, /* botrightparens ??? */ +{ 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ??? */ +{ 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ??? */ +{ 0x08bc, 0x2264 }, /* lessthanequal LESS-THAN OR EQUAL TO */ +{ 0x08bd, 0x2260 }, /* notequal NOT EQUAL TO */ +{ 0x08be, 0x2265 }, /* greaterthanequal GREATER-THAN OR EQUAL TO */ +{ 0x08bf, 0x222b }, /* integral INTEGRAL */ +{ 0x08c0, 0x2234 }, /* therefore THEREFORE */ +{ 0x08c1, 0x221d }, /* variation PROPORTIONAL TO */ +{ 0x08c2, 0x221e }, /* infinity INFINITY */ +{ 0x08c5, 0x2207 }, /* nabla NABLA */ +{ 0x08c8, 0x223c }, /* approximate TILDE OPERATOR */ +{ 0x08c9, 0x2243 }, /* similarequal ASYMPTOTICALLY EQUAL TO */ +{ 0x08cd, 0x21d4 }, /* ifonlyif LEFT RIGHT DOUBLE ARROW */ +{ 0x08ce, 0x21d2 }, /* implies RIGHTWARDS DOUBLE ARROW */ +{ 0x08cf, 0x2261 }, /* identical IDENTICAL TO */ +{ 0x08d6, 0x221a }, /* radical SQUARE ROOT */ +{ 0x08da, 0x2282 }, /* includedin SUBSET OF */ +{ 0x08db, 0x2283 }, /* includes SUPERSET OF */ +{ 0x08dc, 0x2229 }, /* intersection INTERSECTION */ +{ 0x08dd, 0x222a }, /* union UNION */ +{ 0x08de, 0x2227 }, /* logicaland LOGICAL AND */ +{ 0x08df, 0x2228 }, /* logicalor LOGICAL OR */ +{ 0x08ef, 0x2202 }, /* partialderivative PARTIAL DIFFERENTIAL */ +{ 0x08f6, 0x0192 }, /* function LATIN SMALL LETTER F WITH HOOK */ +{ 0x08fb, 0x2190 }, /* leftarrow LEFTWARDS ARROW */ +{ 0x08fc, 0x2191 }, /* uparrow UPWARDS ARROW */ +{ 0x08fd, 0x2192 }, /* rightarrow RIGHTWARDS ARROW */ +{ 0x08fe, 0x2193 }, /* downarrow DOWNWARDS ARROW */ +/* 0x09df blank ??? */ +{ 0x09e0, 0x25c6 }, /* soliddiamond BLACK DIAMOND */ +{ 0x09e1, 0x2592 }, /* checkerboard MEDIUM SHADE */ +{ 0x09e2, 0x2409 }, /* ht SYMBOL FOR HORIZONTAL TABULATION */ +{ 0x09e3, 0x240c }, /* ff SYMBOL FOR FORM FEED */ +{ 0x09e4, 0x240d }, /* cr SYMBOL FOR CARRIAGE RETURN */ +{ 0x09e5, 0x240a }, /* lf SYMBOL FOR LINE FEED */ +{ 0x09e8, 0x2424 }, /* nl SYMBOL FOR NEWLINE */ +{ 0x09e9, 0x240b }, /* vt SYMBOL FOR VERTICAL TABULATION */ +{ 0x09ea, 0x2518 }, /* lowrightcorner BOX DRAWINGS LIGHT UP AND LEFT */ +{ 0x09eb, 0x2510 }, /* uprightcorner BOX DRAWINGS LIGHT DOWN AND LEFT */ +{ 0x09ec, 0x250c }, /* upleftcorner BOX DRAWINGS LIGHT DOWN AND RIGHT */ +{ 0x09ed, 0x2514 }, /* lowleftcorner BOX DRAWINGS LIGHT UP AND RIGHT */ +{ 0x09ee, 0x253c }, /* crossinglines BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ +{ 0x09ef, 0x23ba }, /* horizlinescan1 HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ +{ 0x09f0, 0x23bb }, /* horizlinescan3 HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ +{ 0x09f1, 0x2500 }, /* horizlinescan5 BOX DRAWINGS LIGHT HORIZONTAL */ +{ 0x09f2, 0x23bc }, /* horizlinescan7 HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ +{ 0x09f3, 0x23bd }, /* horizlinescan9 HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ +{ 0x09f4, 0x251c }, /* leftt BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ +{ 0x09f5, 0x2524 }, /* rightt BOX DRAWINGS LIGHT VERTICAL AND LEFT */ +{ 0x09f6, 0x2534 }, /* bott BOX DRAWINGS LIGHT UP AND HORIZONTAL */ +{ 0x09f7, 0x252c }, /* topt BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ +{ 0x09f8, 0x2502 }, /* vertbar BOX DRAWINGS LIGHT VERTICAL */ +{ 0x0aa1, 0x2003 }, /* emspace EM SPACE */ +{ 0x0aa2, 0x2002 }, /* enspace EN SPACE */ +{ 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */ +{ 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */ +{ 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */ +{ 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */ +{ 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */ +{ 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */ +{ 0x0aa9, 0x2014 }, /* emdash EM DASH */ +{ 0x0aaa, 0x2013 }, /* endash EN DASH */ +/* 0x0aac signifblank ??? */ +{ 0x0aae, 0x2026 }, /* ellipsis HORIZONTAL ELLIPSIS */ +{ 0x0aaf, 0x2025 }, /* doubbaselinedot TWO DOT LEADER */ +{ 0x0ab0, 0x2153 }, /* onethird VULGAR FRACTION ONE THIRD */ +{ 0x0ab1, 0x2154 }, /* twothirds VULGAR FRACTION TWO THIRDS */ +{ 0x0ab2, 0x2155 }, /* onefifth VULGAR FRACTION ONE FIFTH */ +{ 0x0ab3, 0x2156 }, /* twofifths VULGAR FRACTION TWO FIFTHS */ +{ 0x0ab4, 0x2157 }, /* threefifths VULGAR FRACTION THREE FIFTHS */ +{ 0x0ab5, 0x2158 }, /* fourfifths VULGAR FRACTION FOUR FIFTHS */ +{ 0x0ab6, 0x2159 }, /* onesixth VULGAR FRACTION ONE SIXTH */ +{ 0x0ab7, 0x215a }, /* fivesixths VULGAR FRACTION FIVE SIXTHS */ +{ 0x0ab8, 0x2105 }, /* careof CARE OF */ +{ 0x0abb, 0x2012 }, /* figdash FIGURE DASH */ +{ 0x0abc, 0x2329 }, /* leftanglebracket LEFT-POINTING ANGLE BRACKET */ +/* 0x0abd decimalpoint ??? */ +{ 0x0abe, 0x232a }, /* rightanglebracket RIGHT-POINTING ANGLE BRACKET */ +/* 0x0abf marker ??? */ +{ 0x0ac3, 0x215b }, /* oneeighth VULGAR FRACTION ONE EIGHTH */ +{ 0x0ac4, 0x215c }, /* threeeighths VULGAR FRACTION THREE EIGHTHS */ +{ 0x0ac5, 0x215d }, /* fiveeighths VULGAR FRACTION FIVE EIGHTHS */ +{ 0x0ac6, 0x215e }, /* seveneighths VULGAR FRACTION SEVEN EIGHTHS */ +{ 0x0ac9, 0x2122 }, /* trademark TRADE MARK SIGN */ +{ 0x0aca, 0x2613 }, /* signaturemark SALTIRE */ +/* 0x0acb trademarkincircle ??? */ +{ 0x0acc, 0x25c1 }, /* leftopentriangle WHITE LEFT-POINTING TRIANGLE */ +{ 0x0acd, 0x25b7 }, /* rightopentriangle WHITE RIGHT-POINTING TRIANGLE */ +{ 0x0ace, 0x25cb }, /* emopencircle WHITE CIRCLE */ +{ 0x0acf, 0x25af }, /* emopenrectangle WHITE VERTICAL RECTANGLE */ +{ 0x0ad0, 0x2018 }, /* leftsinglequotemark LEFT SINGLE QUOTATION MARK */ +{ 0x0ad1, 0x2019 }, /* rightsinglequotemark RIGHT SINGLE QUOTATION MARK */ +{ 0x0ad2, 0x201c }, /* leftdoublequotemark LEFT DOUBLE QUOTATION MARK */ +{ 0x0ad3, 0x201d }, /* rightdoublequotemark RIGHT DOUBLE QUOTATION MARK */ +{ 0x0ad4, 0x211e }, /* prescription PRESCRIPTION TAKE */ +{ 0x0ad6, 0x2032 }, /* minutes PRIME */ +{ 0x0ad7, 0x2033 }, /* seconds DOUBLE PRIME */ +{ 0x0ad9, 0x271d }, /* latincross LATIN CROSS */ +/* 0x0ada hexagram ??? */ +{ 0x0adb, 0x25ac }, /* filledrectbullet BLACK RECTANGLE */ +{ 0x0adc, 0x25c0 }, /* filledlefttribullet BLACK LEFT-POINTING TRIANGLE */ +{ 0x0add, 0x25b6 }, /* filledrighttribullet BLACK RIGHT-POINTING TRIANGLE */ +{ 0x0ade, 0x25cf }, /* emfilledcircle BLACK CIRCLE */ +{ 0x0adf, 0x25ae }, /* emfilledrect BLACK VERTICAL RECTANGLE */ +{ 0x0ae0, 0x25e6 }, /* enopencircbullet WHITE BULLET */ +{ 0x0ae1, 0x25ab }, /* enopensquarebullet WHITE SMALL SQUARE */ +{ 0x0ae2, 0x25ad }, /* openrectbullet WHITE RECTANGLE */ +{ 0x0ae3, 0x25b3 }, /* opentribulletup WHITE UP-POINTING TRIANGLE */ +{ 0x0ae4, 0x25bd }, /* opentribulletdown WHITE DOWN-POINTING TRIANGLE */ +{ 0x0ae5, 0x2606 }, /* openstar WHITE STAR */ +{ 0x0ae6, 0x2022 }, /* enfilledcircbullet BULLET */ +{ 0x0ae7, 0x25aa }, /* enfilledsqbullet BLACK SMALL SQUARE */ +{ 0x0ae8, 0x25b2 }, /* filledtribulletup BLACK UP-POINTING TRIANGLE */ +{ 0x0ae9, 0x25bc }, /* filledtribulletdown BLACK DOWN-POINTING TRIANGLE */ +{ 0x0aea, 0x261c }, /* leftpointer WHITE LEFT POINTING INDEX */ +{ 0x0aeb, 0x261e }, /* rightpointer WHITE RIGHT POINTING INDEX */ +{ 0x0aec, 0x2663 }, /* club BLACK CLUB SUIT */ +{ 0x0aed, 0x2666 }, /* diamond BLACK DIAMOND SUIT */ +{ 0x0aee, 0x2665 }, /* heart BLACK HEART SUIT */ +{ 0x0af0, 0x2720 }, /* maltesecross MALTESE CROSS */ +{ 0x0af1, 0x2020 }, /* dagger DAGGER */ +{ 0x0af2, 0x2021 }, /* doubledagger DOUBLE DAGGER */ +{ 0x0af3, 0x2713 }, /* checkmark CHECK MARK */ +{ 0x0af4, 0x2717 }, /* ballotcross BALLOT X */ +{ 0x0af5, 0x266f }, /* musicalsharp MUSIC SHARP SIGN */ +{ 0x0af6, 0x266d }, /* musicalflat MUSIC FLAT SIGN */ +{ 0x0af7, 0x2642 }, /* malesymbol MALE SIGN */ +{ 0x0af8, 0x2640 }, /* femalesymbol FEMALE SIGN */ +{ 0x0af9, 0x260e }, /* telephone BLACK TELEPHONE */ +{ 0x0afa, 0x2315 }, /* telephonerecorder TELEPHONE RECORDER */ +{ 0x0afb, 0x2117 }, /* phonographcopyright SOUND RECORDING COPYRIGHT */ +{ 0x0afc, 0x2038 }, /* caret CARET */ +{ 0x0afd, 0x201a }, /* singlelowquotemark SINGLE LOW-9 QUOTATION MARK */ +{ 0x0afe, 0x201e }, /* doublelowquotemark DOUBLE LOW-9 QUOTATION MARK */ +/* 0x0aff cursor ??? */ +{ 0x0ba3, 0x003c }, /* leftcaret LESS-THAN SIGN */ +{ 0x0ba6, 0x003e }, /* rightcaret GREATER-THAN SIGN */ +{ 0x0ba8, 0x2228 }, /* downcaret LOGICAL OR */ +{ 0x0ba9, 0x2227 }, /* upcaret LOGICAL AND */ +{ 0x0bc0, 0x00af }, /* overbar MACRON */ +{ 0x0bc2, 0x22a5 }, /* downtack UP TACK */ +{ 0x0bc3, 0x2229 }, /* upshoe INTERSECTION */ +{ 0x0bc4, 0x230a }, /* downstile LEFT FLOOR */ +{ 0x0bc6, 0x005f }, /* underbar LOW LINE */ +{ 0x0bca, 0x2218 }, /* jot RING OPERATOR */ +{ 0x0bcc, 0x2395 }, /* quad APL FUNCTIONAL SYMBOL QUAD */ +{ 0x0bce, 0x22a4 }, /* uptack DOWN TACK */ +{ 0x0bcf, 0x25cb }, /* circle WHITE CIRCLE */ +{ 0x0bd3, 0x2308 }, /* upstile LEFT CEILING */ +{ 0x0bd6, 0x222a }, /* downshoe UNION */ +{ 0x0bd8, 0x2283 }, /* rightshoe SUPERSET OF */ +{ 0x0bda, 0x2282 }, /* leftshoe SUBSET OF */ +{ 0x0bdc, 0x22a2 }, /* lefttack RIGHT TACK */ +{ 0x0bfc, 0x22a3 }, /* righttack LEFT TACK */ +{ 0x0cdf, 0x2017 }, /* hebrew_doublelowline DOUBLE LOW LINE */ +{ 0x0ce0, 0x05d0 }, /* hebrew_aleph HEBREW LETTER ALEF */ +{ 0x0ce1, 0x05d1 }, /* hebrew_bet HEBREW LETTER BET */ +{ 0x0ce2, 0x05d2 }, /* hebrew_gimel HEBREW LETTER GIMEL */ +{ 0x0ce3, 0x05d3 }, /* hebrew_dalet HEBREW LETTER DALET */ +{ 0x0ce4, 0x05d4 }, /* hebrew_he HEBREW LETTER HE */ +{ 0x0ce5, 0x05d5 }, /* hebrew_waw HEBREW LETTER VAV */ +{ 0x0ce6, 0x05d6 }, /* hebrew_zain HEBREW LETTER ZAYIN */ +{ 0x0ce7, 0x05d7 }, /* hebrew_chet HEBREW LETTER HET */ +{ 0x0ce8, 0x05d8 }, /* hebrew_tet HEBREW LETTER TET */ +{ 0x0ce9, 0x05d9 }, /* hebrew_yod HEBREW LETTER YOD */ +{ 0x0cea, 0x05da }, /* hebrew_finalkaph HEBREW LETTER FINAL KAF */ +{ 0x0ceb, 0x05db }, /* hebrew_kaph HEBREW LETTER KAF */ +{ 0x0cec, 0x05dc }, /* hebrew_lamed HEBREW LETTER LAMED */ +{ 0x0ced, 0x05dd }, /* hebrew_finalmem HEBREW LETTER FINAL MEM */ +{ 0x0cee, 0x05de }, /* hebrew_mem HEBREW LETTER MEM */ +{ 0x0cef, 0x05df }, /* hebrew_finalnun HEBREW LETTER FINAL NUN */ +{ 0x0cf0, 0x05e0 }, /* hebrew_nun HEBREW LETTER NUN */ +{ 0x0cf1, 0x05e1 }, /* hebrew_samech HEBREW LETTER SAMEKH */ +{ 0x0cf2, 0x05e2 }, /* hebrew_ayin HEBREW LETTER AYIN */ +{ 0x0cf3, 0x05e3 }, /* hebrew_finalpe HEBREW LETTER FINAL PE */ +{ 0x0cf4, 0x05e4 }, /* hebrew_pe HEBREW LETTER PE */ +{ 0x0cf5, 0x05e5 }, /* hebrew_finalzade HEBREW LETTER FINAL TSADI */ +{ 0x0cf6, 0x05e6 }, /* hebrew_zade HEBREW LETTER TSADI */ +{ 0x0cf7, 0x05e7 }, /* hebrew_qoph HEBREW LETTER QOF */ +{ 0x0cf8, 0x05e8 }, /* hebrew_resh HEBREW LETTER RESH */ +{ 0x0cf9, 0x05e9 }, /* hebrew_shin HEBREW LETTER SHIN */ +{ 0x0cfa, 0x05ea }, /* hebrew_taw HEBREW LETTER TAV */ +{ 0x0da1, 0x0e01 }, /* Thai_kokai THAI CHARACTER KO KAI */ +{ 0x0da2, 0x0e02 }, /* Thai_khokhai THAI CHARACTER KHO KHAI */ +{ 0x0da3, 0x0e03 }, /* Thai_khokhuat THAI CHARACTER KHO KHUAT */ +{ 0x0da4, 0x0e04 }, /* Thai_khokhwai THAI CHARACTER KHO KHWAI */ +{ 0x0da5, 0x0e05 }, /* Thai_khokhon THAI CHARACTER KHO KHON */ +{ 0x0da6, 0x0e06 }, /* Thai_khorakhang THAI CHARACTER KHO RAKHANG */ +{ 0x0da7, 0x0e07 }, /* Thai_ngongu THAI CHARACTER NGO NGU */ +{ 0x0da8, 0x0e08 }, /* Thai_chochan THAI CHARACTER CHO CHAN */ +{ 0x0da9, 0x0e09 }, /* Thai_choching THAI CHARACTER CHO CHING */ +{ 0x0daa, 0x0e0a }, /* Thai_chochang THAI CHARACTER CHO CHANG */ +{ 0x0dab, 0x0e0b }, /* Thai_soso THAI CHARACTER SO SO */ +{ 0x0dac, 0x0e0c }, /* Thai_chochoe THAI CHARACTER CHO CHOE */ +{ 0x0dad, 0x0e0d }, /* Thai_yoying THAI CHARACTER YO YING */ +{ 0x0dae, 0x0e0e }, /* Thai_dochada THAI CHARACTER DO CHADA */ +{ 0x0daf, 0x0e0f }, /* Thai_topatak THAI CHARACTER TO PATAK */ +{ 0x0db0, 0x0e10 }, /* Thai_thothan THAI CHARACTER THO THAN */ +{ 0x0db1, 0x0e11 }, /* Thai_thonangmontho THAI CHARACTER THO NANGMONTHO */ +{ 0x0db2, 0x0e12 }, /* Thai_thophuthao THAI CHARACTER THO PHUTHAO */ +{ 0x0db3, 0x0e13 }, /* Thai_nonen THAI CHARACTER NO NEN */ +{ 0x0db4, 0x0e14 }, /* Thai_dodek THAI CHARACTER DO DEK */ +{ 0x0db5, 0x0e15 }, /* Thai_totao THAI CHARACTER TO TAO */ +{ 0x0db6, 0x0e16 }, /* Thai_thothung THAI CHARACTER THO THUNG */ +{ 0x0db7, 0x0e17 }, /* Thai_thothahan THAI CHARACTER THO THAHAN */ +{ 0x0db8, 0x0e18 }, /* Thai_thothong THAI CHARACTER THO THONG */ +{ 0x0db9, 0x0e19 }, /* Thai_nonu THAI CHARACTER NO NU */ +{ 0x0dba, 0x0e1a }, /* Thai_bobaimai THAI CHARACTER BO BAIMAI */ +{ 0x0dbb, 0x0e1b }, /* Thai_popla THAI CHARACTER PO PLA */ +{ 0x0dbc, 0x0e1c }, /* Thai_phophung THAI CHARACTER PHO PHUNG */ +{ 0x0dbd, 0x0e1d }, /* Thai_fofa THAI CHARACTER FO FA */ +{ 0x0dbe, 0x0e1e }, /* Thai_phophan THAI CHARACTER PHO PHAN */ +{ 0x0dbf, 0x0e1f }, /* Thai_fofan THAI CHARACTER FO FAN */ +{ 0x0dc0, 0x0e20 }, /* Thai_phosamphao THAI CHARACTER PHO SAMPHAO */ +{ 0x0dc1, 0x0e21 }, /* Thai_moma THAI CHARACTER MO MA */ +{ 0x0dc2, 0x0e22 }, /* Thai_yoyak THAI CHARACTER YO YAK */ +{ 0x0dc3, 0x0e23 }, /* Thai_rorua THAI CHARACTER RO RUA */ +{ 0x0dc4, 0x0e24 }, /* Thai_ru THAI CHARACTER RU */ +{ 0x0dc5, 0x0e25 }, /* Thai_loling THAI CHARACTER LO LING */ +{ 0x0dc6, 0x0e26 }, /* Thai_lu THAI CHARACTER LU */ +{ 0x0dc7, 0x0e27 }, /* Thai_wowaen THAI CHARACTER WO WAEN */ +{ 0x0dc8, 0x0e28 }, /* Thai_sosala THAI CHARACTER SO SALA */ +{ 0x0dc9, 0x0e29 }, /* Thai_sorusi THAI CHARACTER SO RUSI */ +{ 0x0dca, 0x0e2a }, /* Thai_sosua THAI CHARACTER SO SUA */ +{ 0x0dcb, 0x0e2b }, /* Thai_hohip THAI CHARACTER HO HIP */ +{ 0x0dcc, 0x0e2c }, /* Thai_lochula THAI CHARACTER LO CHULA */ +{ 0x0dcd, 0x0e2d }, /* Thai_oang THAI CHARACTER O ANG */ +{ 0x0dce, 0x0e2e }, /* Thai_honokhuk THAI CHARACTER HO NOKHUK */ +{ 0x0dcf, 0x0e2f }, /* Thai_paiyannoi THAI CHARACTER PAIYANNOI */ +{ 0x0dd0, 0x0e30 }, /* Thai_saraa THAI CHARACTER SARA A */ +{ 0x0dd1, 0x0e31 }, /* Thai_maihanakat THAI CHARACTER MAI HAN-AKAT */ +{ 0x0dd2, 0x0e32 }, /* Thai_saraaa THAI CHARACTER SARA AA */ +{ 0x0dd3, 0x0e33 }, /* Thai_saraam THAI CHARACTER SARA AM */ +{ 0x0dd4, 0x0e34 }, /* Thai_sarai THAI CHARACTER SARA I */ +{ 0x0dd5, 0x0e35 }, /* Thai_saraii THAI CHARACTER SARA II */ +{ 0x0dd6, 0x0e36 }, /* Thai_saraue THAI CHARACTER SARA UE */ +{ 0x0dd7, 0x0e37 }, /* Thai_sarauee THAI CHARACTER SARA UEE */ +{ 0x0dd8, 0x0e38 }, /* Thai_sarau THAI CHARACTER SARA U */ +{ 0x0dd9, 0x0e39 }, /* Thai_sarauu THAI CHARACTER SARA UU */ +{ 0x0dda, 0x0e3a }, /* Thai_phinthu THAI CHARACTER PHINTHU */ +/* 0x0dde Thai_maihanakat_maitho ??? */ +{ 0x0ddf, 0x0e3f }, /* Thai_baht THAI CURRENCY SYMBOL BAHT */ +{ 0x0de0, 0x0e40 }, /* Thai_sarae THAI CHARACTER SARA E */ +{ 0x0de1, 0x0e41 }, /* Thai_saraae THAI CHARACTER SARA AE */ +{ 0x0de2, 0x0e42 }, /* Thai_sarao THAI CHARACTER SARA O */ +{ 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan THAI CHARACTER SARA AI MAIMUAN */ +{ 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai THAI CHARACTER SARA AI MAIMALAI */ +{ 0x0de5, 0x0e45 }, /* Thai_lakkhangyao THAI CHARACTER LAKKHANGYAO */ +{ 0x0de6, 0x0e46 }, /* Thai_maiyamok THAI CHARACTER MAIYAMOK */ +{ 0x0de7, 0x0e47 }, /* Thai_maitaikhu THAI CHARACTER MAITAIKHU */ +{ 0x0de8, 0x0e48 }, /* Thai_maiek THAI CHARACTER MAI EK */ +{ 0x0de9, 0x0e49 }, /* Thai_maitho THAI CHARACTER MAI THO */ +{ 0x0dea, 0x0e4a }, /* Thai_maitri THAI CHARACTER MAI TRI */ +{ 0x0deb, 0x0e4b }, /* Thai_maichattawa THAI CHARACTER MAI CHATTAWA */ +{ 0x0dec, 0x0e4c }, /* Thai_thanthakhat THAI CHARACTER THANTHAKHAT */ +{ 0x0ded, 0x0e4d }, /* Thai_nikhahit THAI CHARACTER NIKHAHIT */ +{ 0x0df0, 0x0e50 }, /* Thai_leksun THAI DIGIT ZERO */ +{ 0x0df1, 0x0e51 }, /* Thai_leknung THAI DIGIT ONE */ +{ 0x0df2, 0x0e52 }, /* Thai_leksong THAI DIGIT TWO */ +{ 0x0df3, 0x0e53 }, /* Thai_leksam THAI DIGIT THREE */ +{ 0x0df4, 0x0e54 }, /* Thai_leksi THAI DIGIT FOUR */ +{ 0x0df5, 0x0e55 }, /* Thai_lekha THAI DIGIT FIVE */ +{ 0x0df6, 0x0e56 }, /* Thai_lekhok THAI DIGIT SIX */ +{ 0x0df7, 0x0e57 }, /* Thai_lekchet THAI DIGIT SEVEN */ +{ 0x0df8, 0x0e58 }, /* Thai_lekpaet THAI DIGIT EIGHT */ +{ 0x0df9, 0x0e59 }, /* Thai_lekkao THAI DIGIT NINE */ +{ 0x0ea1, 0x3131 }, /* Hangul_Kiyeog HANGUL LETTER KIYEOK */ +{ 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog HANGUL LETTER SSANGKIYEOK */ +{ 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios HANGUL LETTER KIYEOK-SIOS */ +{ 0x0ea4, 0x3134 }, /* Hangul_Nieun HANGUL LETTER NIEUN */ +{ 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj HANGUL LETTER NIEUN-CIEUC */ +{ 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh HANGUL LETTER NIEUN-HIEUH */ +{ 0x0ea7, 0x3137 }, /* Hangul_Dikeud HANGUL LETTER TIKEUT */ +{ 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud HANGUL LETTER SSANGTIKEUT */ +{ 0x0ea9, 0x3139 }, /* Hangul_Rieul HANGUL LETTER RIEUL */ +{ 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog HANGUL LETTER RIEUL-KIYEOK */ +{ 0x0eab, 0x313b }, /* Hangul_RieulMieum HANGUL LETTER RIEUL-MIEUM */ +{ 0x0eac, 0x313c }, /* Hangul_RieulPieub HANGUL LETTER RIEUL-PIEUP */ +{ 0x0ead, 0x313d }, /* Hangul_RieulSios HANGUL LETTER RIEUL-SIOS */ +{ 0x0eae, 0x313e }, /* Hangul_RieulTieut HANGUL LETTER RIEUL-THIEUTH */ +{ 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf HANGUL LETTER RIEUL-PHIEUPH */ +{ 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh HANGUL LETTER RIEUL-HIEUH */ +{ 0x0eb1, 0x3141 }, /* Hangul_Mieum HANGUL LETTER MIEUM */ +{ 0x0eb2, 0x3142 }, /* Hangul_Pieub HANGUL LETTER PIEUP */ +{ 0x0eb3, 0x3143 }, /* Hangul_SsangPieub HANGUL LETTER SSANGPIEUP */ +{ 0x0eb4, 0x3144 }, /* Hangul_PieubSios HANGUL LETTER PIEUP-SIOS */ +{ 0x0eb5, 0x3145 }, /* Hangul_Sios HANGUL LETTER SIOS */ +{ 0x0eb6, 0x3146 }, /* Hangul_SsangSios HANGUL LETTER SSANGSIOS */ +{ 0x0eb7, 0x3147 }, /* Hangul_Ieung HANGUL LETTER IEUNG */ +{ 0x0eb8, 0x3148 }, /* Hangul_Jieuj HANGUL LETTER CIEUC */ +{ 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj HANGUL LETTER SSANGCIEUC */ +{ 0x0eba, 0x314a }, /* Hangul_Cieuc HANGUL LETTER CHIEUCH */ +{ 0x0ebb, 0x314b }, /* Hangul_Khieuq HANGUL LETTER KHIEUKH */ +{ 0x0ebc, 0x314c }, /* Hangul_Tieut HANGUL LETTER THIEUTH */ +{ 0x0ebd, 0x314d }, /* Hangul_Phieuf HANGUL LETTER PHIEUPH */ +{ 0x0ebe, 0x314e }, /* Hangul_Hieuh HANGUL LETTER HIEUH */ +{ 0x0ebf, 0x314f }, /* Hangul_A HANGUL LETTER A */ +{ 0x0ec0, 0x3150 }, /* Hangul_AE HANGUL LETTER AE */ +{ 0x0ec1, 0x3151 }, /* Hangul_YA HANGUL LETTER YA */ +{ 0x0ec2, 0x3152 }, /* Hangul_YAE HANGUL LETTER YAE */ +{ 0x0ec3, 0x3153 }, /* Hangul_EO HANGUL LETTER EO */ +{ 0x0ec4, 0x3154 }, /* Hangul_E HANGUL LETTER E */ +{ 0x0ec5, 0x3155 }, /* Hangul_YEO HANGUL LETTER YEO */ +{ 0x0ec6, 0x3156 }, /* Hangul_YE HANGUL LETTER YE */ +{ 0x0ec7, 0x3157 }, /* Hangul_O HANGUL LETTER O */ +{ 0x0ec8, 0x3158 }, /* Hangul_WA HANGUL LETTER WA */ +{ 0x0ec9, 0x3159 }, /* Hangul_WAE HANGUL LETTER WAE */ +{ 0x0eca, 0x315a }, /* Hangul_OE HANGUL LETTER OE */ +{ 0x0ecb, 0x315b }, /* Hangul_YO HANGUL LETTER YO */ +{ 0x0ecc, 0x315c }, /* Hangul_U HANGUL LETTER U */ +{ 0x0ecd, 0x315d }, /* Hangul_WEO HANGUL LETTER WEO */ +{ 0x0ece, 0x315e }, /* Hangul_WE HANGUL LETTER WE */ +{ 0x0ecf, 0x315f }, /* Hangul_WI HANGUL LETTER WI */ +{ 0x0ed0, 0x3160 }, /* Hangul_YU HANGUL LETTER YU */ +{ 0x0ed1, 0x3161 }, /* Hangul_EU HANGUL LETTER EU */ +{ 0x0ed2, 0x3162 }, /* Hangul_YI HANGUL LETTER YI */ +{ 0x0ed3, 0x3163 }, /* Hangul_I HANGUL LETTER I */ +{ 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog HANGUL JONGSEONG KIYEOK */ +{ 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog HANGUL JONGSEONG SSANGKIYEOK */ +{ 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios HANGUL JONGSEONG KIYEOK-SIOS */ +{ 0x0ed7, 0x11ab }, /* Hangul_J_Nieun HANGUL JONGSEONG NIEUN */ +{ 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj HANGUL JONGSEONG NIEUN-CIEUC */ +{ 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh HANGUL JONGSEONG NIEUN-HIEUH */ +{ 0x0eda, 0x11ae }, /* Hangul_J_Dikeud HANGUL JONGSEONG TIKEUT */ +{ 0x0edb, 0x11af }, /* Hangul_J_Rieul HANGUL JONGSEONG RIEUL */ +{ 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog HANGUL JONGSEONG RIEUL-KIYEOK */ +{ 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum HANGUL JONGSEONG RIEUL-MIEUM */ +{ 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub HANGUL JONGSEONG RIEUL-PIEUP */ +{ 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios HANGUL JONGSEONG RIEUL-SIOS */ +{ 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut HANGUL JONGSEONG RIEUL-THIEUTH */ +{ 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf HANGUL JONGSEONG RIEUL-PHIEUPH */ +{ 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh HANGUL JONGSEONG RIEUL-HIEUH */ +{ 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum HANGUL JONGSEONG MIEUM */ +{ 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub HANGUL JONGSEONG PIEUP */ +{ 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios HANGUL JONGSEONG PIEUP-SIOS */ +{ 0x0ee6, 0x11ba }, /* Hangul_J_Sios HANGUL JONGSEONG SIOS */ +{ 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios HANGUL JONGSEONG SSANGSIOS */ +{ 0x0ee8, 0x11bc }, /* Hangul_J_Ieung HANGUL JONGSEONG IEUNG */ +{ 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj HANGUL JONGSEONG CIEUC */ +{ 0x0eea, 0x11be }, /* Hangul_J_Cieuc HANGUL JONGSEONG CHIEUCH */ +{ 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq HANGUL JONGSEONG KHIEUKH */ +{ 0x0eec, 0x11c0 }, /* Hangul_J_Tieut HANGUL JONGSEONG THIEUTH */ +{ 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf HANGUL JONGSEONG PHIEUPH */ +{ 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh HANGUL JONGSEONG HIEUH */ +{ 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh HANGUL LETTER RIEUL-YEORINHIEUH */ +{ 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum HANGUL LETTER KAPYEOUNMIEUM */ +{ 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub HANGUL LETTER KAPYEOUNPIEUP */ +{ 0x0ef2, 0x317f }, /* Hangul_PanSios HANGUL LETTER PANSIOS */ +{ 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung HANGUL LETTER YESIEUNG */ +{ 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf HANGUL LETTER KAPYEOUNPHIEUPH */ +{ 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh HANGUL LETTER YEORINHIEUH */ +{ 0x0ef6, 0x318d }, /* Hangul_AraeA HANGUL LETTER ARAEA */ +{ 0x0ef7, 0x318e }, /* Hangul_AraeAE HANGUL LETTER ARAEAE */ +{ 0x0ef8, 0x11eb }, /* Hangul_J_PanSios HANGUL JONGSEONG PANSIOS */ +{ 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung HANGUL JONGSEONG YESIEUNG */ +{ 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh HANGUL JONGSEONG YEORINHIEUH */ +{ 0x0eff, 0x20a9 }, /* Korean_Won WON SIGN */ +{ 0x13a4, 0x20ac }, /* Euro EURO SIGN */ +{ 0x13bc, 0x0152 }, /* OE LATIN CAPITAL LIGATURE OE */ +{ 0x13bd, 0x0153 }, /* oe LATIN SMALL LIGATURE OE */ +{ 0x13be, 0x0178 }, /* Ydiaeresis LATIN CAPITAL LETTER Y WITH DIAERESIS */ +{ 0x20ac, 0x20ac } /* EuroSign EURO SIGN */ +}; + + // // CXWindowsUtil // +CXWindowsUtil::CKeySymMap CXWindowsUtil::s_keySymToUCS4; +CXWindowsUtil::CUCS4Map CXWindowsUtil::s_UCS4ToKeySym; + bool CXWindowsUtil::getWindowProperty(Display* display, Window window, Atom property, CString* data, Atom* type, @@ -188,6 +980,34 @@ CXWindowsUtil::getCurrentTime(Display* display, Window window) return xevent.xproperty.time; } +UInt32 +CXWindowsUtil::mapKeySymToUCS4(KeySym k) +{ + initKeyMaps(); + + CKeySymMap::const_iterator index = s_keySymToUCS4.find(k); + if (index != s_keySymToUCS4.end()) { + return index->second; + } + else { + return 0x0000ffff; + } +} + +KeySym +CXWindowsUtil::mapUCS4ToKeySym(UInt32 c) +{ + initKeyMaps(); + + CUCS4Map::const_iterator index = s_UCS4ToKeySym.find(c); + if (index != s_UCS4ToKeySym.end()) { + return index->second; + } + else { + return NoSymbol; + } +} + Bool CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) { @@ -199,6 +1019,20 @@ CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) xevent->xproperty.state == PropertyNewValue) ? True : False; } +void +CXWindowsUtil::initKeyMaps() +{ + // note that keysyms 0x13a4 and 0x20ac both map to 0x20ac, which + // means ambiguity when converting unicode 0x20ac to a keysym. + // as written, the m_UCS4ToKeySym will map to XK_EuroSign. + if (s_keySymToUCS4.empty()) { + for (size_t i =0; i < sizeof(s_keymap) / sizeof(s_keymap[0]); ++i) { + s_keySymToUCS4[s_keymap[i].keysym] = s_keymap[i].ucs4; + s_UCS4ToKeySym[s_keymap[i].ucs4] = s_keymap[i].keysym; + } + } +} + // // CXWindowsUtil::CErrorLock diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h index 4faaafb8..7136b730 100644 --- a/lib/platform/CXWindowsUtil.h +++ b/lib/platform/CXWindowsUtil.h @@ -17,6 +17,7 @@ #include "CString.h" #include "BasicTypes.h" +#include "stdmap.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else @@ -55,6 +56,20 @@ public: */ static Time getCurrentTime(Display*, Window); + //! Convert KeySym to UCS-4 + /*! + Converts a KeySym to the equivalent UCS-4 character. Returns + 0x0000ffff if the KeySym cannot be mapped. + */ + static UInt32 mapKeySymToUCS4(KeySym); + + //! Convert UCS-4 to KeySym + /*! + Converts a UCS-4 character to the equivalent KeySym. Returns + NoSymbol (0) if the character cannot be mapped. + */ + static KeySym mapUCS4ToKeySym(UInt32); + //! X11 error handler /*! This class sets an X error handler in the c'tor and restores the @@ -112,6 +127,15 @@ private: static Bool propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg); + + static void initKeyMaps(); + +private: + typedef std::map CKeySymMap; + typedef std::map CUCS4Map; + + static CKeySymMap s_keySymToUCS4; + static CUCS4Map s_UCS4ToKeySym; }; #endif From 11f90022e0ca9b8c22bea1c316b5334e2c481ef1 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 27 Apr 2003 17:01:14 +0000 Subject: [PATCH 493/807] Checkpointing improved key handling. This change adds non-ASCII key handling to win32 on both client and server. It also changes the protocol and adds code to ensure every key pressed also gets released and that that doesn't get confused when the KeyID for the press is different from the KeyID of the release (or repeat). --- lib/client/CClient.cpp | 13 +-- lib/client/CClient.h | 7 +- lib/client/CServerProxy.cpp | 27 +++--- lib/platform/CMSWindowsPrimaryScreen.cpp | 78 +++++++++++------ lib/platform/CMSWindowsSecondaryScreen.cpp | 97 ++++++++++++++++++++-- lib/platform/CMSWindowsSecondaryScreen.h | 11 ++- lib/platform/CXWindowsPrimaryScreen.cpp | 21 +++-- lib/platform/CXWindowsSecondaryScreen.cpp | 80 +++++++++++++++++- lib/platform/CXWindowsSecondaryScreen.h | 11 ++- lib/server/CClientProxy.h | 7 +- lib/server/CClientProxy1_0.cpp | 13 +-- lib/server/CClientProxy1_0.h | 7 +- lib/server/CClientProxy1_1.cpp | 56 +++++++++++++ lib/server/CClientProxy1_1.h | 35 ++++++++ lib/server/CPrimaryClient.cpp | 6 +- lib/server/CPrimaryClient.h | 7 +- lib/server/CServer.cpp | 50 ++++++----- lib/server/CServer.h | 7 +- lib/server/Makefile.am | 1 + lib/synergy/CSecondaryScreen.h | 13 ++- lib/synergy/IClient.h | 13 ++- lib/synergy/IPrimaryScreenReceiver.h | 7 +- lib/synergy/KeyTypes.h | 9 +- lib/synergy/ProtocolTypes.h | 34 ++++++-- 24 files changed, 479 insertions(+), 131 deletions(-) create mode 100644 lib/server/CClientProxy1_1.cpp create mode 100644 lib/server/CClientProxy1_1.h diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 7ae42424..82f73cd7 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -365,21 +365,22 @@ CClient::setClipboardDirty(ClipboardID, bool) } void -CClient::keyDown(KeyID id, KeyModifierMask mask) +CClient::keyDown(KeyID id, KeyModifierMask mask, KeyButton button) { - m_screen->keyDown(id, mask); + m_screen->keyDown(id, mask, button); } void -CClient::keyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) +CClient::keyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) { - m_screen->keyRepeat(id, mask, count); + m_screen->keyRepeat(id, mask, count, button); } void -CClient::keyUp(KeyID id, KeyModifierMask mask) +CClient::keyUp(KeyID id, KeyModifierMask mask, KeyButton button) { - m_screen->keyUp(id, mask); + m_screen->keyUp(id, mask, button); } void diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 1cdcf3fa..61efddaa 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -152,9 +152,10 @@ public: virtual void setClipboard(ClipboardID, const CString&); virtual void grabClipboard(ClipboardID); virtual void setClipboardDirty(ClipboardID, bool dirty); - virtual void keyDown(KeyID, KeyModifierMask); - virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void keyUp(KeyID, KeyModifierMask); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 6f6690cc..3421bd60 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -503,9 +503,10 @@ CServerProxy::keyDown() flushCompressedMouse(); // parse - UInt16 id, mask; - CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, &id, &mask); - LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask)); + UInt16 id, mask, button; + CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, + &id, &mask, &button); + LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); // translate KeyID id2 = translateKey(static_cast(id)); @@ -516,7 +517,7 @@ CServerProxy::keyDown() LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); // forward - getClient()->keyDown(id2, mask2); + getClient()->keyDown(id2, mask2, button); } void @@ -526,10 +527,10 @@ CServerProxy::keyRepeat() flushCompressedMouse(); // parse - UInt16 id, mask, count; - CProtocolUtil::readf(getInputStream(), - kMsgDKeyRepeat + 4, &id, &mask, &count); - LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count)); + UInt16 id, mask, count, button; + CProtocolUtil::readf(getInputStream(), kMsgDKeyRepeat + 4, + &id, &mask, &count, &button); + LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button)); // translate KeyID id2 = translateKey(static_cast(id)); @@ -540,7 +541,7 @@ CServerProxy::keyRepeat() LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); // forward - getClient()->keyRepeat(id2, mask2, count); + getClient()->keyRepeat(id2, mask2, count, button); } void @@ -550,9 +551,9 @@ CServerProxy::keyUp() flushCompressedMouse(); // parse - UInt16 id, mask; - CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask); - LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask)); + UInt16 id, mask, button; + CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask, &button); + LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); // translate KeyID id2 = translateKey(static_cast(id)); @@ -563,7 +564,7 @@ CServerProxy::keyUp() LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); // forward - getClient()->keyUp(id2, mask2); + getClient()->keyUp(id2, mask2, button); } void diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index db73e861..bcb2d04c 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -521,9 +521,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) // process as if it were a key up KeyModifierMask mask; + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); const KeyID key = mapKey(wParam, lParam, &mask); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x", key, mask)); - m_receiver->onKeyUp(key, mask); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyUp(key, mask, button); updateKey(wParam, false); } if ((m_keys[VK_RWIN] & 0x80) != 0 && @@ -536,9 +538,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) // process as if it were a key up KeyModifierMask mask; + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); const KeyID key = mapKey(wParam, lParam, &mask); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x", key, mask)); - m_receiver->onKeyUp(key, mask); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyUp(key, mask, button); updateKey(wParam, false); } } @@ -554,18 +558,20 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) if (!ignore()) { KeyModifierMask mask; const KeyID key = mapKey(msg->wParam, msg->lParam, &mask); - if (key != kKeyNone) { + KeyButton button = static_cast( + (msg->lParam & 0x00ff0000u) >> 16); + if (key != kKeyNone && key != kKeyMultiKey) { if ((msg->lParam & 0x80000000) == 0) { // key press const bool wasDown = ((msg->lParam & 0x40000000) != 0); const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); if (repeat >= 2 || wasDown) { - LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); - m_receiver->onKeyRepeat(key, mask, repeat); + LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button)); + m_receiver->onKeyRepeat(key, mask, repeat, button); } else { - LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x", key, mask)); - m_receiver->onKeyDown(key, mask); + LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyDown(key, mask, button); } // update key state @@ -580,14 +586,14 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) // keys like alt+tab, ctrl+esc, etc. if (m_is95Family && !isModifier(msg->wParam) && (m_keys[msg->wParam] & 0x80) == 0) { - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x", key, mask)); - m_receiver->onKeyDown(key, mask); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyDown(key, mask, button); updateKey(msg->wParam, true); } // do key up - LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask)); - m_receiver->onKeyUp(key, mask); + LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyUp(key, mask, button); // update key state updateKey(msg->wParam, false); @@ -1408,11 +1414,6 @@ CMSWindowsPrimaryScreen::mapKey( return id; } - // check for dead keys - if (MapVirtualKey(vkCode, 2) >= 0x8000) { - return kKeyMultiKey; - } - // save the control state then clear it. ToAscii() maps ctrl+letter // to the corresponding control code and ctrl+backspace to delete. // we don't want that translation so we clear the control modifier @@ -1421,14 +1422,21 @@ CMSWindowsPrimaryScreen::mapKey( BYTE lControl = m_keys[VK_LCONTROL]; BYTE rControl = m_keys[VK_RCONTROL]; BYTE control = m_keys[VK_CONTROL]; + BYTE lMenu = m_keys[VK_LMENU]; + BYTE menu = m_keys[VK_MENU]; if ((mask & KeyModifierModeSwitch) == 0) { m_keys[VK_LCONTROL] = 0; m_keys[VK_RCONTROL] = 0; m_keys[VK_CONTROL] = 0; } + else { + m_keys[VK_LCONTROL] = 0x80; + m_keys[VK_CONTROL] = 0x80; + m_keys[VK_LMENU] = 0x80; + m_keys[VK_MENU] = 0x80; + } // convert to ascii - // FIXME -- support unicode WORD ascii; int result = ToAscii(vkCode, scanCode, m_keys, &ascii, 0); @@ -1436,23 +1444,45 @@ CMSWindowsPrimaryScreen::mapKey( m_keys[VK_LCONTROL] = lControl; m_keys[VK_RCONTROL] = rControl; m_keys[VK_CONTROL] = control; + m_keys[VK_LMENU] = lMenu; + m_keys[VK_MENU] = menu; - // if result is less than zero then it was a dead key. that key - // is remembered by the keyboard which we don't want. remove it - // by calling ToAscii() again with arbitrary arguments. + // if result is less than zero then it was a dead key. leave it + // there. if (result < 0) { - ToAscii(vkCode, scanCode, m_keys, &ascii, 0); return kKeyMultiKey; } // if result is 1 then the key was succesfully converted else if (result == 1) { + if (ascii >= 0x80) { + // character is not really ASCII. instead it's some + // character in the current ANSI code page. try to + // convert that to a Unicode character. if we fail + // then use the single byte character as is. + char src = static_cast(ascii & 0xff); + wchar_t unicode; + if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, + &src, 1, &unicode, 1) > 0) { + return static_cast(unicode); + } + } return static_cast(ascii & 0x00ff); } // if result is 2 then a previous dead key could not be composed. - // put the old dead key back. else if (result == 2) { + // if the two characters are the same and this is a key release + // then this event is the dead key being released. we put the + // dead key back in that case, otherwise we discard both key + // events because we can't compose the character. alternatively + // we could generate key events for both keys. + if (((ascii & 0xff00) >> 8) != (ascii & 0x00ff) || + (info & 0x80000000) == 0) { + // cannot compose key + return kKeyNone; + } + // get the scan code of the dead key and the shift state // required to generate it. vkCode = VkKeyScan(static_cast(ascii & 0x00ff)); diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index 16e7fe9b..25db82f3 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -49,7 +49,8 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() } void -CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) +CMSWindowsSecondaryScreen::keyDown(KeyID key, + KeyModifierMask mask, KeyButton button) { Keystrokes keys; UINT virtualKey; @@ -89,11 +90,14 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) m_fakeKeys[VK_MENU] |= 0x80; break; } + + // note which server key generated this key + m_serverKeyMap[button] = virtualKey; } void CMSWindowsSecondaryScreen::keyRepeat(KeyID key, - KeyModifierMask mask, SInt32 count) + KeyModifierMask mask, SInt32 count, KeyButton button) { Keystrokes keys; UINT virtualKey; @@ -101,6 +105,12 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key, CLock lock(&m_mutex); m_screen->syncDesktop(); + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + // get the sequence of keys to simulate key repeat and the final // modifier state. m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); @@ -108,12 +118,40 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key, return; } + // if we've seen this button (and we should have) then make sure + // we release the same key we pressed when we saw it. + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index != m_serverKeyMap.end() && virtualKey != index->second) { + // replace key up with previous keycode but leave key down + // alone so it uses the new keycode and store that keycode + // in the server key map. + for (Keystrokes::iterator index2 = keys.begin(); + index2 != keys.end(); ++index2) { + if (index2->m_virtualKey == index->second) { + index2->m_virtualKey = index->second; + break; + } + } + + // note that old key is now up + m_keys[index->second] = false; + m_fakeKeys[index->second] = false; + + // map server key to new key + index->second = virtualKey; + + // note that new key is now down + m_keys[index->second] = true; + m_fakeKeys[index->second] = true; + } + // generate key events doKeystrokes(keys, count); } void -CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) +CMSWindowsSecondaryScreen::keyUp(KeyID key, + KeyModifierMask mask, KeyButton button) { Keystrokes keys; UINT virtualKey; @@ -121,11 +159,41 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) CLock lock(&m_mutex); m_screen->syncDesktop(); + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + // get the sequence of keys to simulate key release and the final // modifier state. m_mask = mapKey(keys, virtualKey, key, mask, kRelease); + + // if there are no keys to generate then we should at least generate + // a key release for the key we pressed. if (keys.empty()) { - return; + Keystroke keystroke; + virtualKey = index->second; + keystroke.m_virtualKey = virtualKey; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + } + + // if we've seen this button (and we should have) then make sure + // we release the same key we pressed when we saw it. + if (index != m_serverKeyMap.end() && virtualKey != index->second) { + // replace key up with previous virtual key + for (Keystrokes::iterator index2 = keys.begin(); + index2 != keys.end(); ++index2) { + if (index2->m_virtualKey == virtualKey) { + index2->m_virtualKey = index->second; + break; + } + } + + // use old virtual key + virtualKey = index->second; } // generate key events @@ -177,6 +245,11 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) } break; } + + // remove server key from map + if (index != m_serverKeyMap.end()) { + m_serverKeyMap.erase(index); + } } void @@ -716,8 +789,20 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // if not in map then ask system to convert character if (virtualKey == 0) { // translate. return no keys if unknown key. - // FIXME -- handle unicode - TCHAR ascii = static_cast(id & 0x000000ff); + char ascii; + wchar_t unicode = static_cast(id & 0x0000ffff); + BOOL error; + if (WideCharToMultiByte(CP_THREAD_ACP, +#if defined(WC_NO_BEST_FIT_CHARS) + WC_NO_BEST_FIT_CHARS | +#endif + WC_COMPOSITECHECK | + WC_DEFAULTCHAR, + &unicode, 1, + &ascii, 1, NULL, &error) == 0 || error) { + LOG((CLOG_DEBUG2 "character %d not in code page", id)); + return m_mask; + } SHORT vk = VkKeyScan(ascii); if (vk == 0xffff) { LOG((CLOG_DEBUG2 "no virtual key for character %d", id)); diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index afa26a8c..e60327a4 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -38,9 +38,10 @@ public: virtual ~CMSWindowsSecondaryScreen(); // CSecondaryScreen overrides - virtual void keyDown(KeyID, KeyModifierMask); - virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void keyUp(KeyID, KeyModifierMask); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); @@ -83,6 +84,7 @@ private: bool m_repeat; }; typedef std::vector Keystrokes; + typedef std::map ServerKeyMap; // open/close desktop (for windows 95/98/me) bool openDesktop(); @@ -123,6 +125,9 @@ private: // current active modifiers KeyModifierMask m_mask; + + // map server key buttons to local virtual keys + ServerKeyMap m_serverKeyMap; }; #endif diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp index 54cf8898..b4469cb1 100644 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -241,12 +241,15 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) const KeyModifierMask mask = mapModifier(xevent.xkey.state); const KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { - m_receiver->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask, + static_cast(xevent.xkey.keycode)); if (key == kKeyCapsLock && m_capsLockHalfDuplex) { - m_receiver->onKeyUp(key, mask | KeyModifierCapsLock); + m_receiver->onKeyUp(key, mask | KeyModifierCapsLock, + static_cast(xevent.xkey.keycode)); } else if (key == kKeyNumLock && m_numLockHalfDuplex) { - m_receiver->onKeyUp(key, mask | KeyModifierNumLock); + m_receiver->onKeyUp(key, mask | KeyModifierNumLock, + static_cast(xevent.xkey.keycode)); } } } @@ -280,12 +283,15 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) // no press event follows so it's a plain release LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); if (key == kKeyCapsLock && m_capsLockHalfDuplex) { - m_receiver->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask, + static_cast(xevent.xkey.keycode)); } else if (key == kKeyNumLock && m_numLockHalfDuplex) { - m_receiver->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask, + static_cast(xevent.xkey.keycode)); } - m_receiver->onKeyUp(key, mask); + m_receiver->onKeyUp(key, mask, + static_cast(xevent.xkey.keycode)); } else { // found a press event following so it's a repeat. @@ -293,7 +299,8 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) // repeats but we'll just send a repeat of 1. // note that we discard the press event. LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - m_receiver->onKeyRepeat(key, mask, 1); + m_receiver->onKeyRepeat(key, mask, 1, + static_cast(xevent.xkey.keycode)); } } } diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 7a67783c..0fbbc223 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -95,7 +95,8 @@ CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() } void -CXWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) +CXWindowsSecondaryScreen::keyDown(KeyID key, + KeyModifierMask mask, KeyButton button) { Keystrokes keys; KeyCode keycode; @@ -113,15 +114,24 @@ CXWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) // note that key is now down m_keys[keycode] = true; m_fakeKeys[keycode] = true; + + // note which server key generated this key + m_serverKeyMap[button] = keycode; } void CXWindowsSecondaryScreen::keyRepeat(KeyID key, - KeyModifierMask mask, SInt32 count) + KeyModifierMask mask, SInt32 count, KeyButton button) { Keystrokes keys; KeyCode keycode; + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + // get the sequence of keys to simulate key repeat and the final // modifier state. m_mask = mapKey(keys, keycode, key, mask, kRepeat); @@ -129,21 +139,78 @@ CXWindowsSecondaryScreen::keyRepeat(KeyID key, return; } + // if we've seen this button (and we should have) then make sure + // we release the same key we pressed when we saw it. + if (index != m_serverKeyMap.end() && keycode != index->second) { + // replace key up with previous keycode but leave key down + // alone so it uses the new keycode and store that keycode + // in the server key map. + for (Keystrokes::iterator index2 = keys.begin(); + index2 != keys.end(); ++index2) { + if (index2->m_keycode == index->second) { + index2->m_keycode = index->second; + break; + } + } + + // note that old key is now up + m_keys[index->second] = false; + m_fakeKeys[index->second] = false; + + // map server key to new key + index->second = keycode; + + // note that new key is now down + m_keys[index->second] = true; + m_fakeKeys[index->second] = true; + } + // generate key events doKeystrokes(keys, count); } void -CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) +CXWindowsSecondaryScreen::keyUp(KeyID key, + KeyModifierMask mask, KeyButton button) { Keystrokes keys; KeyCode keycode; + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + // get the sequence of keys to simulate key release and the final // modifier state. m_mask = mapKey(keys, keycode, key, mask, kRelease); + + // if there are no keys to generate then we should at least generate + // a key release for the key we pressed. if (keys.empty()) { - return; + Keystroke keystroke; + keycode = index->second; + keystroke.m_keycode = keycode; + keystroke.m_press = False; + keystroke.m_repeat = false; + keys.push_back(keystroke); + } + + // if we've seen this button (and we should have) then make sure + // we release the same key we pressed when we saw it. + if (index != m_serverKeyMap.end() && keycode != index->second) { + // replace key up with previous keycode + for (Keystrokes::iterator index2 = keys.begin(); + index2 != keys.end(); ++index2) { + if (index2->m_keycode == keycode) { + index2->m_keycode = index->second; + break; + } + } + + // use old keycode + keycode = index->second; } // generate key events @@ -152,6 +219,11 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) // note that key is now up m_keys[keycode] = false; m_fakeKeys[keycode] = false; + + // remove server key from map + if (index != m_serverKeyMap.end()) { + m_serverKeyMap.erase(index); + } } void diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index 18530130..d8c6a88d 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -37,9 +37,10 @@ public: virtual ~CXWindowsSecondaryScreen(); // CSecondaryScreen overrides - virtual void keyDown(KeyID, KeyModifierMask); - virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void keyUp(KeyID, KeyModifierMask); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 x, SInt32 y); @@ -91,6 +92,7 @@ private: typedef std::map KeyCodeMap; typedef KeyCodeMap::const_iterator KeyCodeIndex; typedef std::map ModifierMap; + typedef std::map ServerKeyMap; unsigned int mapButton(ButtonID button) const; @@ -165,6 +167,9 @@ private: // maps keycodes to modifier indices ModifierMap m_keycodeToModifier; + + // map server key buttons to local keycodes + ServerKeyMap m_serverKeyMap; }; #endif diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index 43585001..2ab41c35 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -68,9 +68,10 @@ public: virtual void setClipboard(ClipboardID, const CString&) = 0; virtual void grabClipboard(ClipboardID) = 0; virtual void setClipboardDirty(ClipboardID, bool) = 0; - virtual void keyDown(KeyID, KeyModifierMask) = 0; - virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void keyUp(KeyID, KeyModifierMask) = 0; + virtual void keyDown(KeyID, KeyModifierMask, KeyButton) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton) = 0; + virtual void keyUp(KeyID, KeyModifierMask, KeyButton) = 0; virtual void mouseDown(ButtonID) = 0; virtual void mouseUp(ButtonID) = 0; virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index be0effca..7a9e734c 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -200,24 +200,25 @@ CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty) } void -CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask) +CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask, KeyButton) { LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown1_0, key, mask); } void -CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count) +CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton) { LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat1_0, key, mask, count); } void -CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask) +CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask, KeyButton) { LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp1_0, key, mask); } void diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 6725e5be..0a86e1a9 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -37,9 +37,10 @@ public: virtual void setClipboard(ClipboardID, const CString&); virtual void grabClipboard(ClipboardID); virtual void setClipboardDirty(ClipboardID, bool); - virtual void keyDown(KeyID, KeyModifierMask); - virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void keyUp(KeyID, KeyModifierMask); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); diff --git a/lib/server/CClientProxy1_1.cpp b/lib/server/CClientProxy1_1.cpp new file mode 100644 index 00000000..a09b27bd --- /dev/null +++ b/lib/server/CClientProxy1_1.cpp @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientProxy1_1.h" +#include "CProtocolUtil.h" +#include "CLog.h" +#include + +// +// CClientProxy1_1 +// + +CClientProxy1_1::CClientProxy1_1(IServer* server, const CString& name, + IInputStream* input, IOutputStream* output) : + CClientProxy1_0(server, name, input, output) +{ + // do nothing +} + +CClientProxy1_1::~CClientProxy1_1() +{ + // do nothing +} + +void +CClientProxy1_1::keyDown(KeyID key, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask, button); +} + +void +CClientProxy1_1::keyRepeat(KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d, button=0x%04x", getName().c_str(), key, mask, count, button)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count, button); +} + +void +CClientProxy1_1::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button)); + CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask, button); +} diff --git a/lib/server/CClientProxy1_1.h b/lib/server/CClientProxy1_1.h new file mode 100644 index 00000000..dbae783e --- /dev/null +++ b/lib/server/CClientProxy1_1.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTPROXY1_1_H +#define CCLIENTPROXY1_1_H + +#include "CClientProxy1_0.h" + +//! Proxy for client implementing protocol version 1.1 +class CClientProxy1_1 : public CClientProxy1_0 { +public: + CClientProxy1_1(IServer* server, const CString& name, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CClientProxy1_1(); + + // IClient overrides + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); +}; + +#endif diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 5cd66876..b425a68d 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -201,19 +201,19 @@ CPrimaryClient::setClipboardDirty(ClipboardID id, bool dirty) } void -CPrimaryClient::keyDown(KeyID, KeyModifierMask) +CPrimaryClient::keyDown(KeyID, KeyModifierMask, KeyButton) { // ignore } void -CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32) +CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton) { // ignore } void -CPrimaryClient::keyUp(KeyID, KeyModifierMask) +CPrimaryClient::keyUp(KeyID, KeyModifierMask, KeyButton) { // ignore } diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index d6c3f7c6..d0202cf1 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -107,9 +107,10 @@ public: virtual void setClipboard(ClipboardID, const CString&); virtual void grabClipboard(ClipboardID); virtual void setClipboardDirty(ClipboardID, bool); - virtual void keyDown(KeyID, KeyModifierMask); - virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); - virtual void keyUp(KeyID, KeyModifierMask); + virtual void keyDown(KeyID, KeyModifierMask, KeyButton); + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); + virtual void keyUp(KeyID, KeyModifierMask, KeyButton); virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 2756f025..fd687114 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -20,6 +20,7 @@ #include "COutputPacketStream.h" #include "CProtocolUtil.h" #include "CClientProxy1_0.h" +#include "CClientProxy1_1.h" #include "OptionTypes.h" #include "ProtocolTypes.h" #include "XScreen.h" @@ -615,9 +616,9 @@ CServer::onOneShotTimerExpired(UInt32 id) } void -CServer::onKeyDown(KeyID id, KeyModifierMask mask) +CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) { - LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x", id, mask)); + LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); CLock lock(&m_mutex); assert(m_active != NULL); @@ -627,13 +628,13 @@ CServer::onKeyDown(KeyID id, KeyModifierMask mask) } // relay - m_active->keyDown(id, mask); + m_active->keyDown(id, mask, button); } void -CServer::onKeyUp(KeyID id, KeyModifierMask mask) +CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button) { - LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x", id, mask)); + LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); CLock lock(&m_mutex); assert(m_active != NULL); @@ -643,13 +644,14 @@ CServer::onKeyUp(KeyID id, KeyModifierMask mask) } // relay - m_active->keyUp(id, mask); + m_active->keyUp(id, mask, button); } void -CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) +CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) { - LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); + LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); CLock lock(&m_mutex); assert(m_active != NULL); @@ -660,7 +662,7 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) } // relay - m_active->keyRepeat(id, mask, count); + m_active->keyRepeat(id, mask, count, button); } void @@ -1698,18 +1700,7 @@ CServer::handshakeClient(IDataSocket* socket) } // disallow invalid version numbers - if (major < 0 || minor < 0) { - throw XIncompatibleClient(major, minor); - } - - // disallow connection from test versions to release versions - if (major == 0 && kProtocolMajorVersion != 0) { - throw XIncompatibleClient(major, minor); - } - - // hangup (with error) if version isn't supported - if (major > kProtocolMajorVersion || - (major == kProtocolMajorVersion && minor > kProtocolMinorVersion)) { + if (major <= 0 || minor < 0) { throw XIncompatibleClient(major, minor); } @@ -1720,7 +1711,22 @@ CServer::handshakeClient(IDataSocket* socket) // create client proxy for highest version supported by the client LOG((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor)); - proxy = new CClientProxy1_0(this, name, input, output); + if (major == 1) { + switch (minor) { + case 0: + proxy = new CClientProxy1_0(this, name, input, output); + break; + + case 1: + proxy = new CClientProxy1_1(this, name, input, output); + break; + } + } + + // hangup (with error) if version isn't supported + if (proxy == NULL) { + throw XIncompatibleClient(major, minor); + } // negotiate // FIXME diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 8e644853..1e69f108 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -187,9 +187,10 @@ public: // IPrimaryScreenReceiver overrides virtual void onScreensaver(bool activated); virtual void onOneShotTimerExpired(UInt32 id); - virtual void onKeyDown(KeyID, KeyModifierMask); - virtual void onKeyUp(KeyID, KeyModifierMask); - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton); + virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton); + virtual void onKeyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton); virtual void onMouseDown(ButtonID); virtual void onMouseUp(ButtonID); virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index b1900797..ba6b8415 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -27,6 +27,7 @@ noinst_LIBRARIES = libserver.a libserver_a_SOURCES = \ CClientProxy.cpp \ CClientProxy1_0.cpp \ + CClientProxy1_1.cpp \ CConfig.cpp \ CHTTPServer.cpp \ CPrimaryClient.cpp \ diff --git a/lib/synergy/CSecondaryScreen.h b/lib/synergy/CSecondaryScreen.h index 3ffeba2b..239caf34 100644 --- a/lib/synergy/CSecondaryScreen.h +++ b/lib/synergy/CSecondaryScreen.h @@ -124,23 +124,28 @@ public: //! Notify of key press /*! Synthesize key events to generate a press of key \c id. If possible - match the given modifier mask. + match the given modifier mask. The KeyButton identifies the physical + key on the server that generated this key down. The client must + ensure that a key up or key repeat that uses the same KeyButton will + synthesize an up or repeat for the same client key synthesized by + keyDown(). */ - virtual void keyDown(KeyID id, KeyModifierMask) = 0; + virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0; //! Notify of key repeat /*! Synthesize key events to generate a press and release of key \c id \c count times. If possible match the given modifier mask. */ - virtual void keyRepeat(KeyID id, KeyModifierMask, SInt32 count) = 0; + virtual void keyRepeat(KeyID id, KeyModifierMask, + SInt32 count, KeyButton) = 0; //! Notify of key release /*! Synthesize key events to generate a release of key \c id. If possible match the given modifier mask. */ - virtual void keyUp(KeyID id, KeyModifierMask) = 0; + virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0; //! Notify of mouse press /*! diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index 18804bce..01ab4411 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -103,23 +103,28 @@ public: //! Notify of key press /*! Synthesize key events to generate a press of key \c id. If possible - match the given modifier mask. + match the given modifier mask. The KeyButton identifies the physical + key on the server that generated this key down. The client must + ensure that a key up or key repeat that uses the same KeyButton will + synthesize an up or repeat for the same client key synthesized by + keyDown(). */ - virtual void keyDown(KeyID id, KeyModifierMask) = 0; + virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0; //! Notify of key repeat /*! Synthesize key events to generate a press and release of key \c id \c count times. If possible match the given modifier mask. */ - virtual void keyRepeat(KeyID id, KeyModifierMask, SInt32 count) = 0; + virtual void keyRepeat(KeyID id, KeyModifierMask, + SInt32 count, KeyButton) = 0; //! Notify of key release /*! Synthesize key events to generate a release of key \c id. If possible match the given modifier mask. */ - virtual void keyUp(KeyID id, KeyModifierMask) = 0; + virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0; //! Notify of mouse press /*! diff --git a/lib/synergy/IPrimaryScreenReceiver.h b/lib/synergy/IPrimaryScreenReceiver.h index ec36f263..44b1beee 100644 --- a/lib/synergy/IPrimaryScreenReceiver.h +++ b/lib/synergy/IPrimaryScreenReceiver.h @@ -43,11 +43,12 @@ public: // call to notify of events. onMouseMovePrimary() returns // true iff the mouse enters a jump zone and jumps. //! Notify of key press - virtual void onKeyDown(KeyID, KeyModifierMask) = 0; + virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton) = 0; //! Notify of key release - virtual void onKeyUp(KeyID, KeyModifierMask) = 0; + virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton) = 0; //! Notify of key repeat - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void onKeyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton) = 0; //! Notify of mouse button press virtual void onMouseDown(ButtonID) = 0; //! Notify of mouse button release diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 21028824..9cef9c71 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -19,12 +19,19 @@ //! Key ID /*! -Type to hold a key identifier. The encoding is UTF-32, using +Type to hold a key symbol identifier. The encoding is UTF-32, using U+E000 through U+EFFF for the various control keys (e.g. arrow keys, function keys, modifier keys, etc). */ typedef UInt32 KeyID; +//! Key Code +/*! +Type to hold a physical key identifier. That is, it identifies a +physical key on the keyboard. +*/ +typedef UInt16 KeyButton; + //! Modifier key mask /*! Type to hold a bitmask of key modifiers (e.g. shift keys). diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index 8f9bd569..539ddaf6 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -18,8 +18,10 @@ #include "BasicTypes.h" // protocol version number +// 1.0: initial protocol +// 1.1: adds KeyCode to key press, release, and repeat static const SInt16 kProtocolMajorVersion = 1; -static const SInt16 kProtocolMinorVersion = 0; +static const SInt16 kProtocolMinorVersion = 1; // default contact port number static const UInt16 kDefaultPort = 24800; @@ -137,16 +139,34 @@ static const char kMsgCInfoAck[] = "CIAK"; // // key pressed: primary -> secondary -// $1 = KeyID, $2 = KeyModifierMask -static const char kMsgDKeyDown[] = "DKDN%2i%2i"; +// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton +// the KeyButton identifies the physical key on the primary used to +// generate this key. the secondary should note the KeyButton along +// with the physical key it uses to generate the key press. on +// release, the secondary can then use the primary's KeyButton to +// find its corresponding physical key and release it. this is +// necessary because the KeyID on release may not be the KeyID of +// the press. this can happen with combining (dead) keys or if +// the keyboard layouts are not identical and the user releases +// a modifier key before releasing the modified key. +static const char kMsgDKeyDown[] = "DKDN%2i%2i%2i"; + +// key pressed 1.0: same as above but without KeyButton +static const char kMsgDKeyDown1_0[] = "DKDN%2i%2i"; // key auto-repeat: primary -> secondary -// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats -static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i"; +// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton +static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i%2i"; + +// key auto-repeat 1.0: same as above but without KeyButton +static const char kMsgDKeyRepeat1_0[] = "DKRP%2i%2i%2i"; // key released: primary -> secondary -// $1 = KeyID, $2 = KeyModifierMask -static const char kMsgDKeyUp[] = "DKUP%2i%2i"; +// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton +static const char kMsgDKeyUp[] = "DKUP%2i%2i%2i"; + +// key released 1.0: same as above but without KeyButton +static const char kMsgDKeyUp1_0[] = "DKUP%2i%2i"; // mouse button pressed: primary -> secondary // $1 = ButtonID From 42ea6306f68b6c62678c0478fd41791d5ee5232d Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 27 Apr 2003 18:05:32 +0000 Subject: [PATCH 494/807] Fixes to previous checkpoint. Non-ascii keys seem to work correctly. Still not supporting key composition on X11. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 1 - lib/platform/CMSWindowsSecondaryScreen.h | 1 + lib/server/Makefile.am | 1 + lib/server/server.dsp | 8 ++++++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index 25db82f3..e7bd4e48 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -120,7 +120,6 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key, // if we've seen this button (and we should have) then make sure // we release the same key we pressed when we saw it. - ServerKeyMap::iterator index = m_serverKeyMap.find(button); if (index != m_serverKeyMap.end() && virtualKey != index->second) { // replace key up with previous keycode but leave key down // alone so it uses the new keycode and store that keycode diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index e60327a4..3bb9140b 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -25,6 +25,7 @@ #include "IMSWindowsScreenEventHandler.h" #include "CMutex.h" #include "CString.h" +#include "stdmap.h" #include "stdvector.h" class CMSWindowsScreen; diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index ba6b8415..a414bc90 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -34,6 +34,7 @@ libserver_a_SOURCES = \ CServer.cpp \ CClientProxy.h \ CClientProxy1_0.h \ + CClientProxy1_1.h \ CConfig.h \ CHTTPServer.h \ CPrimaryClient.h \ diff --git a/lib/server/server.dsp b/lib/server/server.dsp index 29ee4b0a..986fedf8 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -95,6 +95,10 @@ SOURCE=.\CClientProxy1_0.cpp # End Source File # Begin Source File +SOURCE=.\CClientProxy1_1.cpp +# End Source File +# Begin Source File + SOURCE=.\CConfig.cpp # End Source File # Begin Source File @@ -123,6 +127,10 @@ SOURCE=.\CClientProxy1_0.h # End Source File # Begin Source File +SOURCE=.\CClientProxy1_1.h +# End Source File +# Begin Source File + SOURCE=.\CConfig.h # End Source File # Begin Source File From 8d9134f93ab80f34256d33b1affa220c1aebf3c5 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 3 May 2003 12:37:03 +0000 Subject: [PATCH 495/807] Boosted priority of main synergy threads to be very high (highest realtime priority). After some testing it appears that anything less than this can starve synergy in some circumstances, preventing it from forwarding messages to clients. This is a rather risky change since synergy can now virtually take over a system if it behaves badly. This change only affects windows systems since lib/arch of other platforms don't yet attempt to boost priority. --- lib/arch/CArchMultithreadWindows.cpp | 67 +++++++++++++++++----------- lib/synergy/CPrimaryScreen.cpp | 2 +- lib/synergy/CSecondaryScreen.cpp | 2 +- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index c847e0ed..4934fc36 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -348,35 +348,52 @@ CArchMultithreadWindows::cancelThread(CArchThread thread) void CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n) { + struct CPriorityInfo { + public: + DWORD m_class; + int m_level; + }; + static const CPriorityInfo s_pClass[] = { + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_IDLE }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_IDLE }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, + { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL} + }; + static const size_t s_pMax = sizeof(s_pClass) / sizeof(s_pClass[0]) - 1; + static const size_t s_pBase = 8; // index of normal priority + assert(thread != NULL); - DWORD pClass = NORMAL_PRIORITY_CLASS; - if (n < 0) { - switch (-n) { - case 1: n = THREAD_PRIORITY_ABOVE_NORMAL; break; - case 2: n = THREAD_PRIORITY_HIGHEST; break; - default: - pClass = HIGH_PRIORITY_CLASS; - switch (-n - 3) { - case 0: n = THREAD_PRIORITY_LOWEST; break; - case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; - case 2: n = THREAD_PRIORITY_NORMAL; break; - case 3: n = THREAD_PRIORITY_ABOVE_NORMAL; break; - default: n = THREAD_PRIORITY_HIGHEST; break; - } - break; - } + size_t index = s_pBase - n; + if (index < 0) { + // lowest priority + index = 0; } - else { - switch (n) { - case 0: n = THREAD_PRIORITY_NORMAL; break; - case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break; - case 2: n = THREAD_PRIORITY_LOWEST; break; - default: n = THREAD_PRIORITY_IDLE; break; - } + else if (index > s_pMax) { + // highest priority + index = s_pMax; } - SetPriorityClass(thread->m_thread, pClass); - SetThreadPriority(thread->m_thread, n); + SetPriorityClass(thread->m_thread, s_pClass[index].m_class); + SetThreadPriority(thread->m_thread, s_pClass[index].m_level); } void diff --git a/lib/synergy/CPrimaryScreen.cpp b/lib/synergy/CPrimaryScreen.cpp index e2b743d7..79d0384e 100644 --- a/lib/synergy/CPrimaryScreen.cpp +++ b/lib/synergy/CPrimaryScreen.cpp @@ -40,7 +40,7 @@ void CPrimaryScreen::mainLoop() { // change our priority - CThread::getCurrentThread().setPriority(-3); + CThread::getCurrentThread().setPriority(-13); // run event loop try { diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp index 5df455a8..0f8bdbc2 100644 --- a/lib/synergy/CSecondaryScreen.cpp +++ b/lib/synergy/CSecondaryScreen.cpp @@ -38,7 +38,7 @@ void CSecondaryScreen::mainLoop() { // change our priority - CThread::getCurrentThread().setPriority(-7); + CThread::getCurrentThread().setPriority(-13); // run event loop try { From 75729cef46b673bc9e967d3b550e036a7d562415 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 3 May 2003 12:54:22 +0000 Subject: [PATCH 496/807] Fixed a few win32 keyboard/mouse problems. First, the mouse hook now captures non-client area mouse messages. Previously, these were ignored (because i forgot about them) and they caused all kinds of problems because they weren't forwarded. For example, clicking on a window border would cause the window to start resizing when the mouse came back to the server screen. Moving inside a title bar meant that the mouse wouldn't move on the client screen. Second, because non-client messages are now handled, the full screen transparent window is no longer necessary to capture input so it's never displayed. (The window is still necessary for clipboard ownership so it's still created.) No transparent window means no screen flashing. It also means we don't have to become the foreground and active window. This plays better with apps that minimize or restore when they're no longer the foreground application/active window. Third, fixed the low level keyboard hook to forward toggle key updates, which it was neglecting to do. Finally, keyboard and mouse input is always forwarded from the hook to the primary screen handler which then shadows the current key and mouse button state. If we're using low level hooks then this isn't really necessary and GetKeyState() always returns the right info but without low level hooks it means we can just use the shadow state. It also means we don't have to show our window in order to get the system's key state table up to date, fixing the screen flash when checking for the scroll lock state. --- lib/platform/CMSWindowsPrimaryScreen.cpp | 255 ++++--------- lib/platform/CMSWindowsPrimaryScreen.h | 4 +- lib/platform/CSynergyHook.cpp | 444 +++++++++++------------ lib/platform/CSynergyHook.h | 10 +- 4 files changed, 293 insertions(+), 420 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index bcb2d04c..bc5257ff 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -295,9 +295,9 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( m_receiver(primaryReceiver), m_is95Family(CArchMiscWindows::isWindows95Family()), m_threadID(0), - m_window(NULL), m_mark(0), - m_markReceived(0) + m_markReceived(0), + m_lowLevel(false) { assert(m_receiver != NULL); @@ -333,7 +333,6 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() { assert(m_hookLibrary != NULL); - assert(m_window == NULL); delete m_screen; FreeLibrary(m_hookLibrary); @@ -385,8 +384,8 @@ KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { KeyModifierMask mask = 0; - if (isActive()) { - // get key state + if (!m_lowLevel) { + // get key state from our shadow state if ((m_keys[VK_CAPITAL] & 0x01) != 0) mask |= KeyModifierCapsLock; if ((m_keys[VK_NUMLOCK] & 0x01) != 0) @@ -395,33 +394,13 @@ CMSWindowsPrimaryScreen::getToggleMask() const mask |= KeyModifierScrollLock; } else { - // show the window, but make it very small. we must do this - // because GetKeyState() reports the key state according to - // processed messages and until the window is visible the - // system won't update the state of the toggle keys reported - // by that function. unfortunately, this slows this method - // down significantly and, for some reason i don't understand, - // causes everything on the screen to redraw. - if (m_window != NULL) { - MoveWindow(m_window, 1, 1, 1, 1, FALSE); - const_cast(this)->showWindow(); - } - - // get key state + // get key state from the system when using low level hooks if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) mask |= KeyModifierCapsLock; if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) mask |= KeyModifierNumLock; if ((GetKeyState(VK_SCROLL) & 0x01) != 0) mask |= KeyModifierScrollLock; - - // make the window hidden again and restore its size - if (m_window != NULL) { - const_cast(this)->hideWindow(); - SInt32 x, y, w, h; - m_screen->getShape(x, y, w, h); - MoveWindow(m_window, x, y, w, h, FALSE); - } } return mask; @@ -444,12 +423,12 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const 0x7ffffe5f }; - // check each key. note that we cannot use GetKeyboardState() here - // since it reports the state of keys according to key messages - // that have been pulled off the queue. in general, we won't get - // these key messages because they're not for our window. if any - // key (or mouse button) is down then we're locked to the screen. - if (isActive()) { + // check each key. if we're capturing events at a low level we + // can query the keyboard state using GetKeyState(). if not we + // resort to using our shadow keyboard state since the system's + // shadow state won't be in sync (because our window is not + // getting keyboard events). + if (!m_lowLevel) { // use shadow keyboard state in m_keys for (UInt32 i = 0; i < 256; ++i) { if ((m_keys[i] & 0x80) != 0) { @@ -462,7 +441,7 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const for (UInt32 i = 0; i < 256 / 32; ++i) { for (UInt32 b = 1, j = 0; j < 32; b <<= 1, ++j) { if ((s_mappedKeys[i] & b) != 0) { - if (GetAsyncKeyState(i * 32 + j) < 0) { + if (GetKeyState(i * 32 + j) < 0) { LOG((CLOG_DEBUG "locked by \"%s\"", g_vkToName[i * 32 + j])); return true; } @@ -573,9 +552,6 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); m_receiver->onKeyDown(key, mask, button); } - - // update key state - updateKey(msg->wParam, true); } else { // key release. if the key isn't down according to @@ -594,51 +570,76 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) // do key up LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); m_receiver->onKeyUp(key, mask, button); - - // update key state - updateKey(msg->wParam, false); } } else { LOG((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", msg->wParam, msg->lParam)); } } + + // keep our shadow key state up to date + updateKey(msg->wParam, ((msg->lParam & 0x80000000) == 0)); + return true; - case SYNERGY_MSG_MOUSE_BUTTON: + case SYNERGY_MSG_MOUSE_BUTTON: { + static const int s_vkButton[] = { + 0, // kButtonNone + VK_LBUTTON, // kButtonLeft, etc. + VK_MBUTTON, + VK_RBUTTON + }; + + // get which button + bool pressed = false; + const ButtonID button = mapButton(msg->wParam); + // ignore message if posted prior to last mark change if (!ignore()) { - static const int s_vkButton[] = { - 0, // kButtonNone - VK_LBUTTON, // kButtonLeft, etc. - VK_MBUTTON, - VK_RBUTTON - }; - - const ButtonID button = mapButton(msg->wParam); switch (msg->wParam) { case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_NCLBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCMBUTTONDBLCLK: + case WM_NCRBUTTONDBLCLK: LOG((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { m_receiver->onMouseDown(button); m_keys[s_vkButton[button]] |= 0x80; } + pressed = true; break; case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONUP: + case WM_NCRBUTTONUP: LOG((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { m_receiver->onMouseUp(button); m_keys[s_vkButton[button]] &= ~0x80; } + pressed = false; break; } } + + // keep our shadow key state up to date + if (button != kButtonNone) { + updateKey(s_vkButton[button], pressed); + } + return true; + } case SYNERGY_MSG_MOUSE_WHEEL: // ignore message if posted prior to last mark change @@ -765,29 +766,25 @@ CMSWindowsPrimaryScreen::getJumpZoneSize() const } void -CMSWindowsPrimaryScreen::postCreateWindow(HWND window) +CMSWindowsPrimaryScreen::postCreateWindow(HWND) { - // save window - m_window = window; - // install hooks - m_install(); + switch (m_install()) { + case kHOOK_FAILED: + // FIXME -- can't install hook so we won't work; report error + m_lowLevel = false; + break; - // resize window - // note -- we use a fullscreen window to grab input. it should - // be possible to use a 1x1 window but i've run into problems - // with losing keyboard input (focus?) in that case. - // unfortunately, hiding the full screen window (when entering - // the screen) causes all other windows to redraw. - SInt32 x, y, w, h; - m_screen->getShape(x, y, w, h); - MoveWindow(m_window, x, y, w, h, FALSE); + case kHOOK_OKAY: + m_lowLevel = false; + break; - if (isActive()) { - // hide the cursor - showWindow(); + case kHOOK_OKAY_LL: + m_lowLevel = true; + break; } - else { + + if (!isActive()) { // watch jump zones m_setRelay(false); @@ -799,11 +796,6 @@ CMSWindowsPrimaryScreen::postCreateWindow(HWND window) void CMSWindowsPrimaryScreen::preDestroyWindow(HWND) { - // hide the window if it's visible - if (isActive()) { - hideWindow(); - } - // uninstall hooks m_uninstall(); } @@ -813,14 +805,11 @@ CMSWindowsPrimaryScreen::onPreMainLoop() { // must call mainLoop() from same thread as open() assert(m_threadID == GetCurrentThreadId()); - assert(m_window != NULL); } void CMSWindowsPrimaryScreen::onPreOpen() { - assert(m_window == NULL); - // initialize hook library m_threadID = GetCurrentThreadId(); if (m_init(m_threadID) == 0) { @@ -857,8 +846,6 @@ CMSWindowsPrimaryScreen::onPostClose() void CMSWindowsPrimaryScreen::onPreEnter() { - assert(m_window != NULL); - // enable ctrl+alt+del, alt+tab, etc if (m_is95Family) { DWORD dummy = 0; @@ -879,8 +866,6 @@ CMSWindowsPrimaryScreen::onPostEnter() void CMSWindowsPrimaryScreen::onPreLeave() { - assert(m_window != NULL); - // all messages prior to now are invalid nextMark(); } @@ -904,19 +889,10 @@ void CMSWindowsPrimaryScreen::createWindow() { // open the desktop and the window - m_window = m_screen->openDesktop(); - if (m_window == NULL) { + HWND window = m_screen->openDesktop(); + if (window == NULL) { throw XScreenOpenFailure(); } - - // note -- we use a fullscreen window to grab input. it should - // be possible to use a 1x1 window but i've run into problems - // with losing keyboard input (focus?) in that case. - // unfortunately, hiding the full screen window (when entering - // the scren causes all other windows to redraw). - SInt32 x, y, w, h; - m_screen->getShape(x, y, w, h); - MoveWindow(m_window, x, y, w, h, FALSE); } void @@ -924,104 +900,19 @@ CMSWindowsPrimaryScreen::destroyWindow() { // close the desktop and the window m_screen->closeDesktop(); - m_window = NULL; } bool CMSWindowsPrimaryScreen::showWindow() { - // remember the active window before we leave. GetActiveWindow() - // will only return the active window for the thread's queue (i.e. - // our app) but we need the globally active window. get that by - // attaching input to the foreground window's thread then calling - // GetActiveWindow() and then detaching our input. - m_lastActiveWindow = NULL; - m_lastForegroundWindow = GetForegroundWindow(); - m_lastActiveThread = GetWindowThreadProcessId( - m_lastForegroundWindow, NULL); - DWORD myThread = GetCurrentThreadId(); - if (m_lastActiveThread != 0) { - if (myThread != m_lastActiveThread) { - if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { - m_lastActiveWindow = GetActiveWindow(); - AttachThreadInput(myThread, m_lastActiveThread, FALSE); - } - } - } - - // show our window - ShowWindow(m_window, SW_SHOW); - - // force our window to the foreground. this is necessary to - // capture input but is complicated by microsoft's misguided - // attempt to prevent applications from changing the - // foreground window. (the user should be in control of that - // under normal circumstances but there are exceptions; the - // good folks at microsoft, after abusing the previously - // available ability to switch foreground tasks in many of - // their apps, changed the behavior to prevent it. maybe - // it was easier than fixing the applications.) - // - // anyway, simply calling SetForegroundWindow() doesn't work - // unless there is no foreground window or we already are the - // foreground window. so we AttachThreadInput() to the - // foreground process then call SetForegroundWindow(); that - // makes Windows think the foreground process changed the - // foreground window which is allowed since the foreground - // is "voluntarily" yielding control. then we unattach the - // thread input and go about our business. - // - // unfortunately, this still doesn't work for console windows - // on the windows 95 family. if a console is the foreground - // app on the server when the user leaves the server screen - // then the keyboard will not be captured by synergy. - if (m_lastActiveThread != myThread) { - if (m_lastActiveThread != 0) { - AttachThreadInput(myThread, m_lastActiveThread, TRUE); - } - SetForegroundWindow(m_window); - if (m_lastActiveThread != 0) { - AttachThreadInput(myThread, m_lastActiveThread, FALSE); - } - } - - // get keyboard input and capture mouse - SetActiveWindow(m_window); - SetFocus(m_window); - SetCapture(m_window); - + // do nothing. we don't need to show a window to capture input. return true; } void CMSWindowsPrimaryScreen::hideWindow() { - // restore the active window and hide our window. we can only set - // the active window for another thread if we first attach our input - // to that thread. - ReleaseCapture(); - if (m_lastActiveWindow != NULL) { - DWORD myThread = GetCurrentThreadId(); - if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { - // FIXME -- shouldn't raise window if X-Mouse is enabled - // but i have no idea how to do that or check if enabled. - SetActiveWindow(m_lastActiveWindow); - AttachThreadInput(myThread, m_lastActiveThread, FALSE); - } - } - - // hide the window. do not wait for it, though, since ShowWindow() - // waits for the event loop to process the show-window event, but - // that thread may need to lock the mutex that this thread has - // already locked. in particular, that deadlock will occur unless - // we use the asynchronous version of show window when a client - // disconnects: thread A will lock the mutex and enter the primary - // screen which warps the mouse and calls this method while thread B - // will handle the mouse warp event and call methods that try to - // lock the mutex. thread A owns the mutex and is waiting for the - // event loop, thread B owns the event loop and is waiting for the - // mutex causing deadlock. - ShowWindowAsync(m_window, SW_HIDE); + // do nothing. we don't need to show a window to capture input. } void @@ -1520,15 +1411,27 @@ CMSWindowsPrimaryScreen::mapButton(WPARAM button) const { switch (button) { case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCLBUTTONUP: return kButtonLeft; case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: case WM_MBUTTONUP: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONDBLCLK: + case WM_NCMBUTTONUP: return kButtonMiddle; case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONDBLCLK: + case WM_NCRBUTTONUP: return kButtonRight; default: diff --git a/lib/platform/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h index f179a90c..bb5c0eb0 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.h +++ b/lib/platform/CMSWindowsPrimaryScreen.h @@ -101,9 +101,6 @@ private: // the main loop's thread id DWORD m_threadID; - // our window - HWND m_window; - // used to discard queued messages that are no longer needed UInt32 m_mark; UInt32 m_markReceived; @@ -126,6 +123,7 @@ private: SetSidesFunc m_setSides; SetZoneFunc m_setZone; SetRelayFunc m_setRelay; + bool m_lowLevel; // stuff for restoring active window HWND m_lastForegroundWindow; diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 885ec8d0..1742f91c 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -61,7 +61,6 @@ static UINT g_wmMouseWheel = 0; static DWORD g_threadID = 0; static HHOOK g_keyboard = NULL; static HHOOK g_mouse = NULL; -static HHOOK g_cbt = NULL; static HHOOK g_getMessage = NULL; static HANDLE g_hookThreadLL = NULL; static DWORD g_hookThreadIDLL = 0; @@ -122,117 +121,92 @@ restoreCursor() } static -LRESULT CALLBACK -keyboardHook(int code, WPARAM wParam, LPARAM lParam) +bool +keyboardHookHandler(WPARAM wParam, LPARAM lParam) { - if (code >= 0) { - if (g_relay) { - // forward message to our window - PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, wParam, lParam); + // forward message to our window. do this whether or not we're + // forwarding events to clients because this'll keep our thread's + // key state table up to date. that's important for querying + // the scroll lock toggle state. + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, wParam, lParam); - // let certain keys pass through - switch (wParam) { - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - // pass event on. we want to let these through to - // the window proc because otherwise the keyboard - // lights may not stay synchronized. - break; + if (g_relay) { + // let certain keys pass through + switch (wParam) { + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // pass event on. we want to let these through to + // the window proc because otherwise the keyboard + // lights may not stay synchronized. + break; - default: - // discard event - return 1; - } + default: + // discard event + return true; } } - return CallNextHookEx(g_keyboard, code, wParam, lParam); + return false; } static -LRESULT CALLBACK -mouseHook(int code, WPARAM wParam, LPARAM lParam) +bool +mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 wheel) { - if (code >= 0) { + switch (wParam) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_NCLBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCMBUTTONDBLCLK: + case WM_NCRBUTTONDBLCLK: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONUP: + case WM_NCRBUTTONUP: + // always relay the event. eat it if relaying. + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_BUTTON, wParam, 0); + return g_relay; + + case WM_MOUSEWHEEL: if (g_relay) { - switch (wParam) { - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: - PostThreadMessage(g_threadID, - SYNERGY_MSG_MOUSE_BUTTON, wParam, 0); - return 1; + // relay event + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, wheel, 0); + } + return g_relay; - case WM_MOUSEWHEEL: - { - // win2k and other systems supporting WM_MOUSEWHEEL in - // the mouse hook are gratuitously different (and poorly - // documented). if a low-level mouse hook is in place - // it should capture these events so we'll never see - // them. - switch (g_wheelSupport) { - case kWheelModern: { - const MOUSEHOOKSTRUCT* info = - (const MOUSEHOOKSTRUCT*)lParam; - PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, - static_cast( - LOWORD(info->dwExtraInfo)), 0); - break; - } - - case kWheelWin2000: { - const MOUSEHOOKSTRUCTWin2000* info = - (const MOUSEHOOKSTRUCTWin2000*)lParam; - PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, - static_cast( - HIWORD(info->mouseData)), 0); - break; - } - - default: - break; - } - } - return 1; - - case WM_MOUSEMOVE: - { - const MOUSEHOOKSTRUCT* info = - (const MOUSEHOOKSTRUCT*)lParam; - - // we want the cursor to be hidden at all times so we - // hide the cursor on whatever window has it. but then - // we have to show the cursor whenever we leave that - // window (or at some later time before we stop relaying). - // so check the window with the cursor. if it's not the - // same window that had it before then show the cursor - // in the last window and hide it in this window. - DWORD thread = GetWindowThreadProcessId(info->hwnd, NULL); - if (thread != g_cursorThread) { - restoreCursor(); - hideCursor(thread); - } - - // get position - SInt32 x = (SInt32)info->pt.x; - SInt32 y = (SInt32)info->pt.y; - - // relay the motion - PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); - } - return 1; + case WM_NCMOUSEMOVE: + case WM_MOUSEMOVE: + if (g_relay) { + // we want the cursor to be hidden at all times so we + // hide the cursor on whatever window has it. but then + // we have to show the cursor whenever we leave that + // window (or at some later time before we stop relaying). + // so check the window with the cursor. if it's not the + // same window that had it before then show the cursor + // in the last window and hide it in this window. + DWORD thread = GetCurrentThreadId(); + if (thread != g_cursorThread) { + restoreCursor(); + hideCursor(thread); } + + // relay and eat event + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); + return true; } else { // check for mouse inside jump zone bool inside = false; - const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; - SInt32 x = (SInt32)info->pt.x; - SInt32 y = (SInt32)info->pt.y; if (!inside && (g_zoneSides & kLeftMask) != 0) { inside = (x < g_xScreen + g_zoneSize); } @@ -246,36 +220,71 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) inside = (y >= g_yScreen + g_hScreen - g_zoneSize); } - // if inside then eat event and notify our window - if (inside) { - restoreCursor(); - PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); - return 1; + // relay the event + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); + + // if inside then eat the event + return inside; + } + } + + // pass the event + return false; +} + +static +LRESULT CALLBACK +keyboardHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // handle the message + if (keyboardHookHandler(wParam, lParam)) { + return 1; + } + } + + return CallNextHookEx(g_keyboard, code, wParam, lParam); +} + +static +LRESULT CALLBACK +mouseHook(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + // decode message + const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + SInt32 w = 0; + if (wParam == WM_MOUSEWHEEL) { + // win2k and other systems supporting WM_MOUSEWHEEL in + // the mouse hook are gratuitously different (and poorly + // documented). if a low-level mouse hook is in place + // it should capture these events so we'll never see + // them. + switch (g_wheelSupport) { + case kWheelModern: + w = static_cast(LOWORD(info->dwExtraInfo)); + break; + + case kWheelWin2000: { + const MOUSEHOOKSTRUCTWin2000* info2k = + (const MOUSEHOOKSTRUCTWin2000*)lParam; + w = static_cast(HIWORD(info2k->mouseData)); + break; } - else { - PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); } } + + // handle the message + if (mouseHookHandler(wParam, x, y, w)) { + return 1; + } } return CallNextHookEx(g_mouse, code, wParam, lParam); } -/* -static -LRESULT CALLBACK -cbtHook(int code, WPARAM wParam, LPARAM lParam) -{ - if (code >= 0) { - if (g_relay) { - // do nothing for now. may add something later. - } - } - - return CallNextHookEx(g_cbt, code, wParam, lParam); -} -*/ - static LRESULT CALLBACK getMessageHook(int code, WPARAM wParam, LPARAM lParam) @@ -312,7 +321,7 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam) // // low-level keyboard hook -- this allows us to capture and handle // alt+tab, alt+esc, ctrl+esc, and windows key hot keys. on the down -// side, key repeats are not compressed for us. +// side, key repeats are not reported to us. // static @@ -320,41 +329,27 @@ LRESULT CALLBACK keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { - if (g_relay) { - KBDLLHOOKSTRUCT* info = reinterpret_cast(lParam); + // decode the message + KBDLLHOOKSTRUCT* info = reinterpret_cast(lParam); + WPARAM wParam = info->vkCode; + LPARAM lParam = 1; // repeat code + lParam |= (info->scanCode << 16); // scan code + if (info->flags & LLKHF_EXTENDED) { + lParam |= (1lu << 24); // extended key + } + if (info->flags & LLKHF_ALTDOWN) { + lParam |= (1lu << 29); // context code + } + if (info->flags & LLKHF_UP) { + lParam |= (1lu << 31); // transition + } + // FIXME -- bit 30 should be set if key was already down but + // we don't know that info. as a result we'll never generate + // key repeat events. - // let certain keys pass through - switch (info->vkCode) { - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - // pass event on. we want to let these through to - // the window proc because otherwise the keyboard - // lights may not stay synchronized. - break; - - default: - // construct lParam for WM_KEYDOWN, etc. - DWORD lParam = 1; // repeat code - lParam |= (info->scanCode << 16); // scan code - if (info->flags & LLKHF_EXTENDED) { - lParam |= (1lu << 24); // extended key - } - if (info->flags & LLKHF_ALTDOWN) { - lParam |= (1lu << 29); // context code - } - if (info->flags & LLKHF_UP) { - lParam |= (1lu << 31); // transition - } - // FIXME -- bit 30 should be set if key was already down - - // forward message to our window - PostThreadMessage(g_threadID, - SYNERGY_MSG_KEY, info->vkCode, lParam); - - // discard event - return 1; - } + // handle the message + if (keyboardHookHandler(wParam, lParam)) { + return 1; } } @@ -363,11 +358,7 @@ keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) // // low-level mouse hook -- this allows us to capture and handle mouse -// wheel events on all windows NT platforms from NT SP3 and up. this -// is both simpler than using the mouse hook and also supports windows -// windows NT which does not report mouse wheel events. we need to -// keep the mouse hook handling of mouse wheel events because the -// windows 95 family doesn't support low-level hooks. +// events very early. the earlier the better. // static @@ -375,28 +366,15 @@ LRESULT CALLBACK mouseLLHook(int code, WPARAM wParam, LPARAM lParam) { if (code >= 0) { - if (g_relay) { - MSLLHOOKSTRUCT* info = reinterpret_cast(lParam); + // decode the message + MSLLHOOKSTRUCT* info = reinterpret_cast(lParam); + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + SInt32 w = (SInt32)HIWORD(info->mouseData); - switch (wParam) { - case WM_MOUSEWHEEL: - // mouse wheel events are the same for entire NT family - // (>=SP3, prior versions have no low level hooks) for - // low-level mouse hook messages, unlike (regular) mouse - // hook messages which are gratuitously different on - // win2k and not sent at all for windows NT. - - // forward message to our window - PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, - HIWORD(info->mouseData), 0); - - // discard event - return 1; - - default: - // all other events are passed through - break; - } + // handle the message + if (mouseHookHandler(wParam, x, y, w)) { + return 1; } } @@ -592,7 +570,6 @@ init(DWORD threadID) g_threadID = 0; g_keyboard = NULL; g_mouse = NULL; - g_cbt = NULL; g_getMessage = NULL; g_hookThreadLL = NULL; g_hookThreadIDLL = 0; @@ -632,79 +609,28 @@ cleanup(void) return 1; } -int +EHookResult install() { assert(g_hinstance != NULL); assert(g_keyboard == NULL); assert(g_mouse == NULL); - assert(g_cbt == NULL); assert(g_getMessage == NULL || g_screenSaver); // must be initialized if (g_threadID == 0) { - return 0; + return kHOOK_FAILED; } // check for mouse wheel support g_wheelSupport = getWheelSupport(); - // install keyboard hook -#if !NO_GRAB_KEYBOARD - g_keyboard = SetWindowsHookEx(WH_KEYBOARD, - &keyboardHook, - g_hinstance, - 0); - if (g_keyboard == NULL) { - g_threadID = NULL; - return 0; - } -#else - // keep compiler quiet - &keyboardHook; -#endif - - // install mouse hook - g_mouse = SetWindowsHookEx(WH_MOUSE, - &mouseHook, - g_hinstance, - 0); - if (g_mouse == NULL) { - // uninstall keyboard hook before failing - if (g_keyboard != NULL) { - UnhookWindowsHookEx(g_keyboard); - g_keyboard = NULL; - } - g_threadID = NULL; - return 0; - } - -/* - // install CBT hook - g_cbt = SetWindowsHookEx(WH_CBT, - &cbtHook, - g_hinstance, - 0); - if (g_cbt == NULL) { - // uninstall keyboard and mouse hooks before failing - if (g_keyboard != NULL) { - UnhookWindowsHookEx(g_keyboard); - g_keyboard = NULL; - } - UnhookWindowsHookEx(g_mouse); - g_mouse = NULL; - g_threadID = NULL; - return 0; - } -*/ - // install GetMessage hook (unless already installed) if (g_wheelSupport == kWheelOld && g_getMessage == NULL) { g_getMessage = SetWindowsHookEx(WH_GETMESSAGE, &getMessageHook, g_hinstance, 0); - // ignore failure; we just won't get mouse wheel messages } // install low-level keyboard/mouse hooks, if possible. since these @@ -735,7 +661,47 @@ install() } } - return 1; + // install non-low-level hooks if the low-level hooks are not installed + if (g_hookThreadLL == NULL) { +#if !NO_GRAB_KEYBOARD + g_keyboard = SetWindowsHookEx(WH_KEYBOARD, + &keyboardHook, + g_hinstance, + 0); +#else + // keep compiler quiet + &keyboardHook; +#endif + g_mouse = SetWindowsHookEx(WH_MOUSE, + &mouseHook, + g_hinstance, + 0); + } + + // check for any failures. uninstall all hooks on failure. + if (g_hookThreadLL == NULL && +#if !NO_GRAB_KEYBOARD + (g_keyboard == NULL || g_mouse == NULL)) { +#else + (g_mouse == NULL)) { +#endif + if (g_keyboard != NULL) { + UnhookWindowsHookEx(g_keyboard); + g_keyboard = NULL; + } + if (g_mouse != NULL) { + UnhookWindowsHookEx(g_mouse); + g_mouse = NULL; + } + if (g_getMessage != NULL && !g_screenSaver) { + UnhookWindowsHookEx(g_getMessage); + g_getMessage = NULL; + } + g_threadID = NULL; + return kHOOK_FAILED; + } + + return (g_hookThreadLL == NULL) ? kHOOK_OKAY : kHOOK_OKAY_LL; } int @@ -755,20 +721,16 @@ uninstall(void) } if (g_keyboard != NULL) { UnhookWindowsHookEx(g_keyboard); + g_keyboard = NULL; } if (g_mouse != NULL) { UnhookWindowsHookEx(g_mouse); - } - if (g_cbt != NULL) { - UnhookWindowsHookEx(g_cbt); + g_mouse = NULL; } if (g_getMessage != NULL && !g_screenSaver) { UnhookWindowsHookEx(g_getMessage); g_getMessage = NULL; } - g_keyboard = NULL; - g_mouse = NULL; - g_cbt = NULL; g_wheelSupport = kWheelNone; // show the cursor @@ -837,6 +799,10 @@ setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize) void setRelay(int enable) { + if ((enable != 0) == g_relay) { + // no change + return; + } g_relay = (enable != 0); if (!g_relay) { restoreCursor(); diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h index 7f306665..03e72fa7 100644 --- a/lib/platform/CSynergyHook.h +++ b/lib/platform/CSynergyHook.h @@ -43,9 +43,15 @@ extern "C" { +enum EHookResult { + kHOOK_FAILED, + kHOOK_OKAY, + kHOOK_OKAY_LL +}; + typedef int (*InitFunc)(DWORD targetQueueThreadID); typedef int (*CleanupFunc)(void); -typedef int (*InstallFunc)(void); +typedef EHookResult (*InstallFunc)(void); typedef int (*UninstallFunc)(void); typedef int (*InstallScreenSaverFunc)(void); typedef int (*UninstallScreenSaverFunc)(void); @@ -55,7 +61,7 @@ typedef void (*SetRelayFunc)(int); CSYNERGYHOOK_API int init(DWORD); CSYNERGYHOOK_API int cleanup(void); -CSYNERGYHOOK_API int install(void); +CSYNERGYHOOK_API EHookResult install(void); CSYNERGYHOOK_API int uninstall(void); CSYNERGYHOOK_API int installScreenSaver(void); CSYNERGYHOOK_API int uninstallScreenSaver(void); From af110dbce2ef15f4a23f9789d8b0a02eb2ca6b30 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 3 May 2003 13:28:21 +0000 Subject: [PATCH 497/807] Now turning off auto-repeat when on an X11 client. This prevents the server from auto-repeating fake events, which is undesired since synergy will do the auto-repeating itself. This also disables auto-repeat on any keys locally configured on X11 to not auto-repeat. That's mainly to suppress auto-repeat on modifier keys, which auto-repeat on win32 but not X11. --- lib/platform/CXWindowsSecondaryScreen.cpp | 34 +++++++++++++++++++++++ lib/platform/CXWindowsSecondaryScreen.h | 4 +++ 2 files changed, 38 insertions(+) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 0fbbc223..598cc4ed 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -139,6 +139,11 @@ CXWindowsSecondaryScreen::keyRepeat(KeyID key, return; } + // if this keycode shouldn't auto-repeat then ignore + if ((m_keyControl.auto_repeats[keycode >> 3] & (1 << (keycode & 7))) == 0) { + return; + } + // if we've seen this button (and we should have) then make sure // we release the same key we pressed when we saw it. if (index != m_serverKeyMap.end() && keycode != index->second) { @@ -367,6 +372,10 @@ void CXWindowsSecondaryScreen::onPostOpen() { assert(m_window != None); + + // get the keyboard control state + CDisplayLock display(m_screen); + XGetKeyboardControl(display, &m_keyControl); } void @@ -375,10 +384,35 @@ CXWindowsSecondaryScreen::onPreEnter() assert(m_window != None); } +void +CXWindowsSecondaryScreen::onPostEnter() +{ + assert(m_window != None); + + // get the keyboard control state + CDisplayLock display(m_screen); + XGetKeyboardControl(display, &m_keyControl); + + // turn off auto-repeat. we do this so fake key press events don't + // cause the local server to generate their own auto-repeats of + // those keys. + XAutoRepeatOff(display); +} + void CXWindowsSecondaryScreen::onPreLeave() { assert(m_window != None); + + // restore the previous keyboard auto-repeat state. if the user + // changed the auto-repeat configuration while on the client then + // that state is lost. that's because we can't get notified by + // the X server when the auto-repeat configuration is changed so + // we can't track the desired configuration. + if (m_keyControl.global_auto_repeat == AutoRepeatModeOn) { + CDisplayLock display(m_screen); + XAutoRepeatOn(display); + } } void diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index d8c6a88d..ae0655d7 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -62,6 +62,7 @@ protected: virtual void onPreOpen(); virtual void onPostOpen(); virtual void onPreEnter(); + virtual void onPostEnter(); virtual void onPreLeave(); virtual void createWindow(); virtual void destroyWindow(); @@ -170,6 +171,9 @@ private: // map server key buttons to local keycodes ServerKeyMap m_serverKeyMap; + + // the keyboard control state the last time this screen was entered + XKeyboardState m_keyControl; }; #endif From 3fc40e1939631632ffc2870eae43b7e120efe332 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 3 May 2003 13:50:06 +0000 Subject: [PATCH 498/807] Now warping mouse to center of screen when leaving client screens. Some users requested this. Also, the hider window is mapped before warping the mouse so the active window shouldn't change if the focus policy is point-to-focus. Showing the window first can also reduce the likelihood of seeing the cursor briefly in its hidden position. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 18 +++++++++------- lib/platform/CMSWindowsSecondaryScreen.h | 2 +- lib/platform/CXWindowsSecondaryScreen.cpp | 25 +++++++++++++--------- lib/platform/CXWindowsSecondaryScreen.h | 2 +- lib/synergy/CSecondaryScreen.cpp | 6 ++++-- lib/synergy/CSecondaryScreen.h | 6 +++--- 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index e7bd4e48..f4be6dea 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -367,7 +367,9 @@ CMSWindowsSecondaryScreen::postCreateWindow(HWND window) // hide cursor if this screen isn't active if (!isActive()) { - showWindow(); + SInt32 x, y; + getScreen()->getCursorCenter(x, y); + showWindow(x, y); } } @@ -423,16 +425,16 @@ CMSWindowsSecondaryScreen::destroyWindow() } void -CMSWindowsSecondaryScreen::showWindow() +CMSWindowsSecondaryScreen::showWindow(SInt32 x, SInt32 y) { - // move hider window under the mouse (rather than moving the mouse - // somewhere else on the screen) - SInt32 x, y; - getCursorPos(x, y); + // move hider window under the given position MoveWindow(m_window, x, y, 1, 1, FALSE); - // raise and show the hider window. take activation. - ShowWindow(m_window, SW_SHOWNORMAL); + // raise and show the hider window + ShowWindow(m_window, SW_SHOWNA); + + // now warp the mouse + warpCursor(x, y); } void diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index 3bb9140b..3acff252 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -68,7 +68,7 @@ protected: virtual void onPreLeave(); virtual void createWindow(); virtual void destroyWindow(); - virtual void showWindow(); + virtual void showWindow(SInt32 x, SInt32 y); virtual void hideWindow(); virtual void warpCursor(SInt32 x, SInt32 y); virtual void updateKeys(); diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 598cc4ed..3f7ed996 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -488,18 +488,23 @@ CXWindowsSecondaryScreen::destroyWindow() } void -CXWindowsSecondaryScreen::showWindow() +CXWindowsSecondaryScreen::showWindow(SInt32 x, SInt32 y) { - // move hider window under the mouse (rather than moving the mouse - // somewhere else on the screen) - SInt32 x, y; - getCursorPos(x, y); - CDisplayLock display(m_screen); - XMoveWindow(display, m_window, x, y); + { + CDisplayLock display(m_screen); - // raise and show the hider window. take activation. - // FIXME -- take focus? - XMapRaised(display, m_window); + // move hider window under the given position + XMoveWindow(display, m_window, x, y); + + // raise and show the hider window. take activation. + // FIXME -- take focus? + XMapRaised(display, m_window); + } + + // now warp the mouse. we warp after showing the window so we're + // guaranteed to get the mouse leave event and to prevent the + // keyboard focus from changing under point-to-focus policies. + warpCursor(x, y); } void diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index ae0655d7..fd535c8c 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -66,7 +66,7 @@ protected: virtual void onPreLeave(); virtual void createWindow(); virtual void destroyWindow(); - virtual void showWindow(); + virtual void showWindow(SInt32 x, SInt32 y); virtual void hideWindow(); virtual void warpCursor(SInt32 x, SInt32 y); virtual void updateKeys(); diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp index 0f8bdbc2..24a9146f 100644 --- a/lib/synergy/CSecondaryScreen.cpp +++ b/lib/synergy/CSecondaryScreen.cpp @@ -173,8 +173,10 @@ CSecondaryScreen::leave() // restore toggle key state setToggleState(m_toggleKeys); - // hide mouse - showWindow(); + // warp and hide mouse + SInt32 x, y; + getScreen()->getCursorCenter(x, y); + showWindow(x, y); // subclass hook onPostLeave(); diff --git a/lib/synergy/CSecondaryScreen.h b/lib/synergy/CSecondaryScreen.h index 239caf34..e4dd8a9f 100644 --- a/lib/synergy/CSecondaryScreen.h +++ b/lib/synergy/CSecondaryScreen.h @@ -323,10 +323,10 @@ protected: //! Show window /*! Called when the user navigates off this secondary screen. It needn't - actually show the window created by createWindow() but it must hide - the cursor and clean up event synthesis. + actually show the window created by createWindow() but it must move + the cursor to x,y, hide it, and clean up event synthesis. */ - virtual void showWindow() = 0; + virtual void showWindow(SInt32 x, SInt32 y) = 0; //! Hide window /*! From 65de05e3ec464917e6dd30efcc3a43fbcb59598c Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 3 May 2003 13:57:52 +0000 Subject: [PATCH 499/807] Forgot to restore global auto-repeat configuration on exit. --- lib/platform/CXWindowsSecondaryScreen.cpp | 9 +++++++++ lib/platform/CXWindowsSecondaryScreen.h | 1 + 2 files changed, 10 insertions(+) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 3f7ed996..9eb93af8 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -378,6 +378,15 @@ CXWindowsSecondaryScreen::onPostOpen() XGetKeyboardControl(display, &m_keyControl); } +void +CXWindowsSecondaryScreen::onPreClose() +{ + if (m_keyControl.global_auto_repeat == AutoRepeatModeOn) { + CDisplayLock display(m_screen); + XAutoRepeatOn(display); + } +} + void CXWindowsSecondaryScreen::onPreEnter() { diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index fd535c8c..f44a12ed 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -61,6 +61,7 @@ protected: virtual void onPreMainLoop(); virtual void onPreOpen(); virtual void onPostOpen(); + virtual void onPreClose(); virtual void onPreEnter(); virtual void onPostEnter(); virtual void onPreLeave(); From ed439ec33c6bc23a5f0e82d50514e28e7a27ef6e Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 3 May 2003 14:38:36 +0000 Subject: [PATCH 500/807] Added global configuration option to disable screen saver synchronization. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 6 +- lib/platform/CXWindowsSecondaryScreen.cpp | 2 + lib/server/CConfig.cpp | 9 ++- lib/synergy/CSecondaryScreen.cpp | 93 ++++++++++++++++++++-- lib/synergy/CSecondaryScreen.h | 16 +++- lib/synergy/OptionTypes.h | 1 + 6 files changed, 112 insertions(+), 15 deletions(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index f4be6dea..35fd8c74 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -300,13 +300,13 @@ CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) void CMSWindowsSecondaryScreen::resetOptions() { - // no options + CSecondaryScreen::resetOptions(); } void -CMSWindowsSecondaryScreen::setOptions(const COptionsList& /*options*/) +CMSWindowsSecondaryScreen::setOptions(const COptionsList& options) { - // no options + CSecondaryScreen::setOptions(options); } IScreen* diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 9eb93af8..5f9c89ca 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -287,6 +287,7 @@ CXWindowsSecondaryScreen::resetOptions() { m_numLockHalfDuplex = false; m_capsLockHalfDuplex = false; + CSecondaryScreen::resetOptions(); } void @@ -302,6 +303,7 @@ CXWindowsSecondaryScreen::setOptions(const COptionsList& options) LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); } } + CSecondaryScreen::setOptions(options); } IScreen* diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index f75193a2..3a66103b 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -640,6 +640,9 @@ CConfig::getOptionName(OptionID id) if (id == kOptionScreenSwitchTwoTap) { return "switchDoubleTap"; } + if (id == kOptionScreenSaverSync) { + return "screenSaverSync"; + } return NULL; } @@ -647,7 +650,8 @@ CString CConfig::getOptionValue(OptionID id, OptionValue value) { if (id == kOptionHalfDuplexCapsLock || - id == kOptionHalfDuplexNumLock) { + id == kOptionHalfDuplexNumLock || + id == kOptionScreenSaverSync) { return (value != 0) ? "true" : "false"; } if (id == kOptionModifierMapForShift || @@ -791,6 +795,9 @@ CConfig::readSectionOptions(std::istream& s) else if (name == "switchDoubleTap") { addOption("", kOptionScreenSwitchTwoTap, parseInt(value)); } + else if (name == "screenSaverSync") { + addOption("", kOptionScreenSaverSync, parseBoolean(value)); + } else { throw XConfigRead("unknown argument"); } diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp index 24a9146f..941e468a 100644 --- a/lib/synergy/CSecondaryScreen.cpp +++ b/lib/synergy/CSecondaryScreen.cpp @@ -23,8 +23,10 @@ // CSecondaryScreen::CSecondaryScreen() : + m_remoteReady(false), m_active(false), - m_toggleKeys(0) + m_toggleKeys(0), + m_screenSaverSync(true) { // do nothing } @@ -106,14 +108,23 @@ CSecondaryScreen::remoteControl() // update keyboard state updateKeys(); - // disable the screen saver - getScreen()->openScreensaver(false); - - // hide the cursor + // now remote ready. fake being active for call to leave(). + bool screenSaverSync; { CLock lock(&m_mutex); - m_active = true; + m_remoteReady = true; + m_active = true; + + // copy screen saver synchronization state + screenSaverSync = m_screenSaverSync; } + + // disable the screen saver if synchronization is enabled + if (screenSaverSync) { + getScreen()->openScreensaver(false); + } + + // hide the cursor leave(); } @@ -121,6 +132,10 @@ void CSecondaryScreen::localControl() { getScreen()->closeScreensaver(); + + // not remote ready anymore + CLock lock(&m_mutex); + m_remoteReady = false; } void @@ -204,7 +219,71 @@ CSecondaryScreen::grabClipboard(ClipboardID id) void CSecondaryScreen::screensaver(bool activate) { - getScreen()->screensaver(activate); + // get screen saver synchronization flag + bool screenSaverSync; + { + CLock lock(&m_mutex); + screenSaverSync = m_screenSaverSync; + } + + // activate/deactivation screen saver iff synchronization enabled + if (screenSaverSync) { +LOG((CLOG_INFO "screensaver(%s)", activate ? "on" : "off")); + getScreen()->screensaver(activate); + } +else { +LOG((CLOG_INFO "screensaver(%s) ignored", activate ? "on" : "off")); +} +} + +void +CSecondaryScreen::resetOptions() +{ + // set screen saver synchronization flag and see if we need to + // update the screen saver synchronization. + bool screenSaverSyncOn; + { + CLock lock(&m_mutex); + screenSaverSyncOn = (!m_screenSaverSync && m_remoteReady); + m_screenSaverSync = true; + } + + // update screen saver synchronization + if (screenSaverSyncOn) { + getScreen()->openScreensaver(false); + } +} + +void +CSecondaryScreen::setOptions(const COptionsList& options) +{ + // update options + bool updateScreenSaverSync = false; + bool oldScreenSaverSync; + { + CLock lock(&m_mutex); + oldScreenSaverSync = m_screenSaverSync; + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionScreenSaverSync) { + updateScreenSaverSync = true; + m_screenSaverSync = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "screen saver synchronization %s", m_screenSaverSync ? "on" : "off")); + } + } + if (!m_remoteReady || oldScreenSaverSync == m_screenSaverSync) { + updateScreenSaverSync = false; + } + } + + // update screen saver synchronization + if (updateScreenSaverSync) { + if (oldScreenSaverSync) { + getScreen()->closeScreensaver(); + } + else { + getScreen()->openScreensaver(false); + } + } } bool diff --git a/lib/synergy/CSecondaryScreen.h b/lib/synergy/CSecondaryScreen.h index e4dd8a9f..f1790a83 100644 --- a/lib/synergy/CSecondaryScreen.h +++ b/lib/synergy/CSecondaryScreen.h @@ -177,16 +177,18 @@ public: //! Notify of options changes /*! - Reset all options to their default values. + Reset all options to their default values. Overrides should call + the superclass's method. */ - virtual void resetOptions() = 0; + virtual void resetOptions(); //! Notify of options changes /*! Set options to given values. Ignore unknown options and don't - modify our options that aren't given in \c options. + modify our options that aren't given in \c options. Overrides + should call the superclass's method. */ - virtual void setOptions(const COptionsList& options) = 0; + virtual void setOptions(const COptionsList& options); //@} //! @name accessors @@ -372,11 +374,17 @@ protected: private: CMutex m_mutex; + // true if ready for remote control + bool m_remoteReady; + // m_active is true if this screen has been entered bool m_active; // the toggle key state when this screen was last entered KeyModifierMask m_toggleKeys; + + // true if screen saver should be synchronized to server + bool m_screenSaverSync; }; #endif diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index 84a09f5c..d94cac37 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -53,6 +53,7 @@ static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); +static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); //@} #undef OPTION_CODE From ddfc05244b4cfb21867b3397a75a292c845c3914 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 3 May 2003 14:54:03 +0000 Subject: [PATCH 501/807] Removed accidental debugging code. --- lib/synergy/CSecondaryScreen.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp index 941e468a..c3df69bb 100644 --- a/lib/synergy/CSecondaryScreen.cpp +++ b/lib/synergy/CSecondaryScreen.cpp @@ -228,12 +228,8 @@ CSecondaryScreen::screensaver(bool activate) // activate/deactivation screen saver iff synchronization enabled if (screenSaverSync) { -LOG((CLOG_INFO "screensaver(%s)", activate ? "on" : "off")); getScreen()->screensaver(activate); } -else { -LOG((CLOG_INFO "screensaver(%s) ignored", activate ? "on" : "off")); -} } void From b840c61f6c20b15de048ce27c9f31e1528378447 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 3 May 2003 15:16:30 +0000 Subject: [PATCH 502/807] Added screen saver synchronization option to win32 launcher dialog. --- cmd/launcher/CGlobalOptions.cpp | 11 +++++++++++ cmd/launcher/launcher.rc | 12 ++++++++---- cmd/launcher/resource.h | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/cmd/launcher/CGlobalOptions.cpp b/cmd/launcher/CGlobalOptions.cpp index 085a56d3..8a44643f 100644 --- a/cmd/launcher/CGlobalOptions.cpp +++ b/cmd/launcher/CGlobalOptions.cpp @@ -75,6 +75,8 @@ CGlobalOptions::init(HWND hwnd) setItemChecked(child, false); child = getItem(hwnd, IDC_GLOBAL_HEARTBEAT_TIME); setWindowText(child, buffer); + child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); + setItemChecked(child, true); // get the global options const CConfig::CScreenOptions* options = m_config->getOptions(""); @@ -110,6 +112,10 @@ CGlobalOptions::init(HWND hwnd) setWindowText(child, buffer); } } + else if (id == kOptionScreenSaverSync) { + child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); + setItemChecked(child, (value != 0)); + } } } } @@ -173,6 +179,7 @@ CGlobalOptions::save(HWND hwnd) m_config->removeOption("", kOptionScreenSwitchDelay); m_config->removeOption("", kOptionScreenSwitchTwoTap); m_config->removeOption("", kOptionHeartbeat); + m_config->removeOption("", kOptionScreenSaverSync); // add requested options child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); @@ -187,6 +194,10 @@ CGlobalOptions::save(HWND hwnd) if (isItemChecked(child)) { m_config->addOption("", kOptionHeartbeat, newHeartbeatTime); } + child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); + if (!isItemChecked(child)) { + m_config->addOption("", kOptionScreenSaverSync, 0); + } // save last values m_delayTime = newDelayTime; diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 03de9828..4f127dea 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -170,7 +170,7 @@ BEGIN IDC_STATIC,7,43,181,17 END -IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 196 +IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 233 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Options" FONT 8, "MS Sans Serif" @@ -198,8 +198,12 @@ BEGIN EDITTEXT IDC_GLOBAL_HEARTBEAT_TIME,112,152,45,12,ES_AUTOHSCROLL | ES_NUMBER LTEXT "ms",IDC_STATIC,159,154,10,8 - DEFPUSHBUTTON "OK",IDOK,94,175,50,14 - PUSHBUTTON "Cancel",IDCANCEL,150,175,50,14 + LTEXT "Synergy can synchronize screen savers across all screens.", + IDC_STATIC,7,176,193,8 + CONTROL "Synchronize screen savers",IDC_GLOBAL_SCREENSAVER_SYNC, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,192,101,10 + DEFPUSHBUTTON "OK",IDOK,94,212,50,14 + PUSHBUTTON "Cancel",IDCANCEL,150,212,50,14 END IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 133 @@ -267,7 +271,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 200 TOPMARGIN, 7 - BOTTOMMARGIN, 189 + BOTTOMMARGIN, 226 END IDD_ADVANCED_OPTIONS, DIALOG diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index 9fbe46af..2885b2f0 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -97,6 +97,7 @@ #define IDC_ADD_MOD_META 1046 #define IDC_GLOBAL_HEARTBEAT_TIME 1046 #define IDC_ADD_MOD_SUPER 1047 +#define IDC_GLOBAL_SCREENSAVER_SYNC 1047 #define IDC_ADVANCED_DEFAULTS 1049 // Next default values for new objects From 0e58bab76c7472d53328bb2a4831a9f0db1c2abd Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 4 May 2003 21:40:42 +0000 Subject: [PATCH 503/807] Added support for 4th and 5th (non-mouse-wheel) buttons and "Internet" keyboard keys. --- configure.in | 1 + lib/platform/CMSWindowsPrimaryScreen.cpp | 219 +++++++++++---------- lib/platform/CMSWindowsPrimaryScreen.h | 5 +- lib/platform/CMSWindowsSecondaryScreen.cpp | 110 ++++++++++- lib/platform/CMSWindowsSecondaryScreen.h | 3 +- lib/platform/CSynergyHook.cpp | 31 ++- lib/platform/CXWindowsPrimaryScreen.cpp | 52 ++++- lib/platform/CXWindowsSecondaryScreen.cpp | 86 +++++++- lib/synergy/KeyTypes.h | 25 ++- lib/synergy/MouseTypes.h | 1 + 10 files changed, 401 insertions(+), 132 deletions(-) diff --git a/configure.in b/configure.in index 92deeadb..a5453acf 100644 --- a/configure.in +++ b/configure.in @@ -57,6 +57,7 @@ AC_PATH_XTRA save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$X_CFLAGS $CPPFLAGS" AC_CHECK_HEADERS([X11/extensions/XTest.h]) +AC_CHECK_HEADERS([X11/XF86keysym.h]) AC_CHECK_LIB(Xinerama, XineramaQueryExtension, AC_CHECK_HEADERS([X11/extensions/Xinerama.h]) [X_LIBS="$X_LIBS -lXinerama"], , [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) CPPFLAGS="$save_CPPFLAGS" diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index bc5257ff..92d3e832 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -21,16 +21,38 @@ #include "CArchMiscWindows.h" #include +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + // // map virtual key id to a name // +static const char* g_buttonToName[] = { + "button 0", + "Left Button", + "Middle Button", + "Right Button", + "X Button 1", + "X Button 2" +}; static const char* g_vkToName[] = { "vk 0x00", - "VK_LBUTTON", - "VK_RBUTTON", + "Left Button", + "Right Button", "VK_CANCEL", - "VK_MBUTTON", + "Middle Button", "vk 0x05", "vk 0x06", "vk 0x07", @@ -192,24 +214,24 @@ static const char* g_vkToName[] = { "VK_RCONTROL", "VK_LMENU", "VK_RMENU", - "vk 0xa6", - "vk 0xa7", - "vk 0xa8", - "vk 0xa9", - "vk 0xaa", - "vk 0xab", - "vk 0xac", - "vk 0xad", - "vk 0xae", - "vk 0xaf", - "vk 0xb0", - "vk 0xb1", - "vk 0xb2", - "vk 0xb3", - "vk 0xb4", - "vk 0xb5", - "vk 0xb6", - "vk 0xb7", + "VK_BROWSER_BACK", + "VK_BROWSER_FORWARD", + "VK_BROWSER_REFRESH", + "VK_BROWSER_STOP", + "VK_BROWSER_SEARCH", + "VK_BROWSER_FAVORITES", + "VK_BROWSER_HOME", + "VK_VOLUME_MUTE", + "VK_VOLUME_DOWN", + "VK_VOLUME_UP", + "VK_MEDIA_NEXT_TRACK", + "VK_MEDIA_PREV_TRACK", + "VK_MEDIA_STOP", + "VK_MEDIA_PLAY_PAUSE", + "VK_LAUNCH_MAIL", + "VK_LAUNCH_MEDIA_SELECT", + "VK_LAUNCH_APP1", + "VK_LAUNCH_APP2", "vk 0xb8", "vk 0xb9", "vk 0xba", @@ -384,24 +406,14 @@ KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { KeyModifierMask mask = 0; - if (!m_lowLevel) { - // get key state from our shadow state - if ((m_keys[VK_CAPITAL] & 0x01) != 0) - mask |= KeyModifierCapsLock; - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) - mask |= KeyModifierNumLock; - if ((m_keys[VK_SCROLL] & 0x01) != 0) - mask |= KeyModifierScrollLock; - } - else { - // get key state from the system when using low level hooks - if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) - mask |= KeyModifierCapsLock; - if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) - mask |= KeyModifierNumLock; - if ((GetKeyState(VK_SCROLL) & 0x01) != 0) - mask |= KeyModifierScrollLock; - } + + // get key state from our shadow state + if ((m_keys[VK_CAPITAL] & 0x01) != 0) + mask |= KeyModifierCapsLock; + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) + mask |= KeyModifierNumLock; + if ((m_keys[VK_SCROLL] & 0x01) != 0) + mask |= KeyModifierScrollLock; return mask; } @@ -409,44 +421,17 @@ CMSWindowsPrimaryScreen::getToggleMask() const bool CMSWindowsPrimaryScreen::isLockedToScreen() const { - // virtual key table. the table defines the virtual keys that are - // mapped to something (including mouse buttons, OEM and kanji keys - // but not unassigned or undefined keys). - static const UInt32 s_mappedKeys[] = { - 0xfbff331e, - 0x03ffffff, - 0x3ffffffe, - 0xffffffff, - 0x000300ff, - 0xfc000000, - 0xf8000001, - 0x7ffffe5f - }; - - // check each key. if we're capturing events at a low level we - // can query the keyboard state using GetKeyState(). if not we - // resort to using our shadow keyboard state since the system's - // shadow state won't be in sync (because our window is not - // getting keyboard events). - if (!m_lowLevel) { - // use shadow keyboard state in m_keys - for (UInt32 i = 0; i < 256; ++i) { - if ((m_keys[i] & 0x80) != 0) { - LOG((CLOG_DEBUG "locked by \"%s\"", g_vkToName[i])); - return true; - } + // use shadow keyboard state in m_keys and m_buttons + for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { + if ((m_buttons[i] & 0x80) != 0) { + LOG((CLOG_DEBUG "locked by \"%s\"", g_buttonToName[i])); + return true; } } - else { - for (UInt32 i = 0; i < 256 / 32; ++i) { - for (UInt32 b = 1, j = 0; j < 32; b <<= 1, ++j) { - if ((s_mappedKeys[i] & b) != 0) { - if (GetKeyState(i * 32 + j) < 0) { - LOG((CLOG_DEBUG "locked by \"%s\"", g_vkToName[i * 32 + j])); - return true; - } - } - } + for (UInt32 i = 0; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { + if ((m_keys[i] & 0x80) != 0) { + LOG((CLOG_DEBUG "locked by \"%s\"", g_vkToName[i])); + return true; } } @@ -583,16 +568,9 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) return true; case SYNERGY_MSG_MOUSE_BUTTON: { - static const int s_vkButton[] = { - 0, // kButtonNone - VK_LBUTTON, // kButtonLeft, etc. - VK_MBUTTON, - VK_RBUTTON - }; - // get which button bool pressed = false; - const ButtonID button = mapButton(msg->wParam); + const ButtonID button = mapButton(msg->wParam, msg->lParam); // ignore message if posted prior to last mark change if (!ignore()) { @@ -600,19 +578,22 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: + case WM_XBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: case WM_NCLBUTTONDOWN: case WM_NCMBUTTONDOWN: case WM_NCRBUTTONDOWN: + case WM_NCXBUTTONDOWN: case WM_NCLBUTTONDBLCLK: case WM_NCMBUTTONDBLCLK: case WM_NCRBUTTONDBLCLK: + case WM_NCXBUTTONDBLCLK: LOG((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { m_receiver->onMouseDown(button); - m_keys[s_vkButton[button]] |= 0x80; } pressed = true; break; @@ -620,13 +601,14 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: + case WM_XBUTTONUP: case WM_NCLBUTTONUP: case WM_NCMBUTTONUP: case WM_NCRBUTTONUP: + case WM_NCXBUTTONUP: LOG((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { m_receiver->onMouseUp(button); - m_keys[s_vkButton[button]] &= ~0x80; } pressed = false; break; @@ -634,8 +616,13 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) } // keep our shadow key state up to date - if (button != kButtonNone) { - updateKey(s_vkButton[button], pressed); + if (button >= kButtonLeft && button <= kButtonExtra0 + 1) { + if (pressed) { + m_buttons[button] |= 0x80; + } + else { + m_buttons[button] &= ~0x80; + } } return true; @@ -1139,24 +1126,24 @@ static const KeyID g_virtualKey[][2] = /* 0xa3 */ kKeyControl_R, kKeyControl_R, // VK_RCONTROL /* 0xa4 */ kKeyAlt_L, kKeyAlt_L, // VK_LMENU /* 0xa5 */ kKeyAlt_R, kKeyAlt_R, // VK_RMENU - /* 0xa6 */ kKeyNone, kKeyNone, // unassigned - /* 0xa7 */ kKeyNone, kKeyNone, // unassigned - /* 0xa8 */ kKeyNone, kKeyNone, // unassigned - /* 0xa9 */ kKeyNone, kKeyNone, // unassigned - /* 0xaa */ kKeyNone, kKeyNone, // unassigned - /* 0xab */ kKeyNone, kKeyNone, // unassigned - /* 0xac */ kKeyNone, kKeyNone, // unassigned - /* 0xad */ kKeyNone, kKeyNone, // unassigned - /* 0xae */ kKeyNone, kKeyNone, // unassigned - /* 0xaf */ kKeyNone, kKeyNone, // unassigned - /* 0xb0 */ kKeyNone, kKeyNone, // unassigned - /* 0xb1 */ kKeyNone, kKeyNone, // unassigned - /* 0xb2 */ kKeyNone, kKeyNone, // unassigned - /* 0xb3 */ kKeyNone, kKeyNone, // unassigned - /* 0xb4 */ kKeyNone, kKeyNone, // unassigned - /* 0xb5 */ kKeyNone, kKeyNone, // unassigned - /* 0xb6 */ kKeyNone, kKeyNone, // unassigned - /* 0xb7 */ kKeyNone, kKeyNone, // unassigned + /* 0xa6 */ kKeyNone, kKeyWWWBack, // VK_BROWSER_BACK + /* 0xa7 */ kKeyNone, kKeyWWWForward, // VK_BROWSER_FORWARD + /* 0xa8 */ kKeyNone, kKeyWWWRefresh, // VK_BROWSER_REFRESH + /* 0xa9 */ kKeyNone, kKeyWWWStop, // VK_BROWSER_STOP + /* 0xaa */ kKeyNone, kKeyWWWSearch, // VK_BROWSER_SEARCH + /* 0xab */ kKeyNone, kKeyWWWFavorites, // VK_BROWSER_FAVORITES + /* 0xac */ kKeyNone, kKeyWWWHome, // VK_BROWSER_HOME + /* 0xad */ kKeyNone, kKeyAudioMute, // VK_VOLUME_MUTE + /* 0xae */ kKeyNone, kKeyAudioDown, // VK_VOLUME_DOWN + /* 0xaf */ kKeyNone, kKeyAudioUp, // VK_VOLUME_UP + /* 0xb0 */ kKeyNone, kKeyAudioNext, // VK_MEDIA_NEXT_TRACK + /* 0xb1 */ kKeyNone, kKeyAudioPrev, // VK_MEDIA_PREV_TRACK + /* 0xb2 */ kKeyNone, kKeyAudioStop, // VK_MEDIA_STOP + /* 0xb3 */ kKeyNone, kKeyAudioPlay, // VK_MEDIA_PLAY_PAUSE + /* 0xb4 */ kKeyNone, kKeyAppMail, // VK_LAUNCH_MAIL + /* 0xb5 */ kKeyNone, kKeyAppMedia, // VK_LAUNCH_MEDIA_SELECT + /* 0xb6 */ kKeyNone, kKeyAppUser1, // VK_LAUNCH_APP1 + /* 0xb7 */ kKeyNone, kKeyAppUser2, // VK_LAUNCH_APP2 /* 0xb8 */ kKeyNone, kKeyNone, // unassigned /* 0xb9 */ kKeyNone, kKeyNone, // unassigned /* 0xba */ kKeyNone, kKeyNone, // OEM specific @@ -1407,9 +1394,9 @@ CMSWindowsPrimaryScreen::mapKey( } ButtonID -CMSWindowsPrimaryScreen::mapButton(WPARAM button) const +CMSWindowsPrimaryScreen::mapButton(WPARAM msg, LPARAM button) const { - switch (button) { + switch (msg) { case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: @@ -1434,6 +1421,21 @@ CMSWindowsPrimaryScreen::mapButton(WPARAM button) const case WM_NCRBUTTONUP: return kButtonRight; + case WM_XBUTTONDOWN: + case WM_XBUTTONDBLCLK: + case WM_XBUTTONUP: + case WM_NCXBUTTONDOWN: + case WM_NCXBUTTONDBLCLK: + case WM_NCXBUTTONUP: + switch (button) { + case XBUTTON1: + return kButtonExtra0 + 0; + + case XBUTTON2: + return kButtonExtra0 + 1; + } + return kButtonNone; + default: return kButtonNone; } @@ -1446,8 +1448,9 @@ CMSWindowsPrimaryScreen::updateKeys() // up-to-date results. i don't know why that is or why GetKeyState() // should give different results. - // clear key state + // clear key and button state memset(m_keys, 0, sizeof(m_keys)); + memset(m_buttons, 0, sizeof(m_buttons)); // we only care about the modifier key states. other keys and the // mouse buttons should be up. diff --git a/lib/platform/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h index bb5c0eb0..dfe40adf 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.h +++ b/lib/platform/CMSWindowsPrimaryScreen.h @@ -87,7 +87,7 @@ private: // key and button queries KeyID mapKey(WPARAM keycode, LPARAM info, KeyModifierMask* maskOut); - ButtonID mapButton(WPARAM button) const; + ButtonID mapButton(WPARAM msg, LPARAM button) const; void updateKey(UINT vkCode, bool press); bool isModifier(UINT vkCode) const; @@ -108,6 +108,9 @@ private: // map of key state BYTE m_keys[256]; + // map of button state + BYTE m_buttons[1 + kButtonExtra0 + 1]; + // last mouse position SInt32 m_x, m_y; diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index 35fd8c74..d6ad9c47 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -28,6 +28,42 @@ #define SPI_SETMOUSESPEED 113 #endif +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + +// multimedia keys +#if !defined(VK_BROWSER_BACK) +#define VK_BROWSER_BACK 0xA6 +#define VK_BROWSER_FORWARD 0xA7 +#define VK_BROWSER_REFRESH 0xA8 +#define VK_BROWSER_STOP 0xA9 +#define VK_BROWSER_SEARCH 0xAA +#define VK_BROWSER_FAVORITES 0xAB +#define VK_BROWSER_HOME 0xAC +#define VK_VOLUME_MUTE 0xAD +#define VK_VOLUME_DOWN 0xAE +#define VK_VOLUME_UP 0xAF +#define VK_MEDIA_NEXT_TRACK 0xB0 +#define VK_MEDIA_PREV_TRACK 0xB1 +#define VK_MEDIA_STOP 0xB2 +#define VK_MEDIA_PLAY_PAUSE 0xB3 +#define VK_LAUNCH_MAIL 0xB4 +#define VK_LAUNCH_MEDIA_SELECT 0xB5 +#define VK_LAUNCH_APP1 0xB6 +#define VK_LAUNCH_APP2 0xB7 +#endif + // // CMSWindowsSecondaryScreen // @@ -258,11 +294,12 @@ CMSWindowsSecondaryScreen::mouseDown(ButtonID button) m_screen->syncDesktop(); // map button id to button flag - DWORD flags = mapButton(button, true); + DWORD data; + DWORD flags = mapButton(button, true, &data); // send event if (flags != 0) { - mouse_event(flags, 0, 0, 0, 0); + mouse_event(flags, 0, 0, data, 0); } } @@ -273,11 +310,12 @@ CMSWindowsSecondaryScreen::mouseUp(ButtonID button) m_screen->syncDesktop(); // map button id to button flag - DWORD flags = mapButton(button, false); + DWORD data; + DWORD flags = mapButton(button, false, &data); // send event if (flags != 0) { - mouse_event(flags, 0, 0, 0, 0); + mouse_event(flags, 0, 0, data, 0); } } @@ -606,6 +644,48 @@ CMSWindowsSecondaryScreen::getToggleState() const // map special KeyID keys to virtual key codes. if the key is an // extended key then the entry is the virtual key code | 0x100. // unmapped keys have a 0 entry. +static const UINT g_mapE000[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, + /* 0xa4 */ 0, 0, VK_BROWSER_BACK|0x100, VK_BROWSER_FORWARD|0x100, + /* 0xa8 */ VK_BROWSER_REFRESH|0x100, VK_BROWSER_STOP|0x100, + /* 0xaa */ VK_BROWSER_SEARCH|0x100, VK_BROWSER_FAVORITES|0x100, + /* 0xac */ VK_BROWSER_HOME|0x100, VK_VOLUME_MUTE|0x100, + /* 0xae */ VK_VOLUME_DOWN|0x100, VK_VOLUME_UP|0x100, + /* 0xb0 */ VK_MEDIA_NEXT_TRACK|0x100, VK_MEDIA_PREV_TRACK|0x100, + /* 0xb2 */ VK_MEDIA_STOP|0x100, VK_MEDIA_PLAY_PAUSE|0x100, + /* 0xb4 */ VK_LAUNCH_MAIL|0x100, VK_LAUNCH_MEDIA_SELECT|0x100, + /* 0xb6 */ VK_LAUNCH_APP1|0x100, VK_LAUNCH_APP2|0x100, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; static const UINT g_mapEE00[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -685,8 +765,12 @@ static const UINT g_mapEF00[] = }; DWORD -CMSWindowsSecondaryScreen::mapButton(ButtonID button, bool press) const +CMSWindowsSecondaryScreen::mapButton(ButtonID button, + bool press, DWORD* inData) const { + DWORD dummy; + DWORD* data = (inData != NULL) ? inData : &dummy; + // the system will swap the meaning of left/right for us if // the user has configured a left-handed mouse but we don't // want it to swap since we want the handedness of the @@ -703,7 +787,8 @@ CMSWindowsSecondaryScreen::mapButton(ButtonID button, bool press) const } } - // map button id to button flag + // map button id to button flag and button data + *data = 0; switch (button) { case kButtonLeft: return press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; @@ -714,6 +799,14 @@ CMSWindowsSecondaryScreen::mapButton(ButtonID button, bool press) const case kButtonRight: return press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; + case kButtonExtra0 + 0: + *data = XBUTTON1; + return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; + + case kButtonExtra0 + 1: + *data = XBUTTON2; + return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; + default: return 0; } @@ -727,7 +820,10 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // check for special keys if ((id & 0xfffff000) == 0xe000) { - if ((id & 0xff00) == 0xee00) { + if ((id & 0xff00) == 0xe000) { + virtualKey = g_mapE000[id & 0xff]; + } + else if ((id & 0xff00) == 0xee00) { virtualKey = g_mapEE00[id & 0xff]; } else if ((id & 0xff00) == 0xef00) { diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index 3acff252..b975b46c 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -98,7 +98,8 @@ private: bool isMultimon() const; // key and button queries and operations - DWORD mapButton(ButtonID button, bool press) const; + DWORD mapButton(ButtonID button, + bool press, DWORD* data) const; KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, KeyModifierMask, EKeyAction) const; void doKeystrokes(const Keystrokes&, SInt32 count); diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 1742f91c..d5dcc1e5 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -45,6 +45,20 @@ typedef struct tagMOUSEHOOKSTRUCTWin2000 { #define SM_MOUSEWHEELPRESENT 75 #endif +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + // // globals @@ -152,35 +166,41 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) static bool -mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 wheel) +mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) { switch (wParam) { case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: + case WM_XBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: + case WM_XBUTTONUP: case WM_NCLBUTTONDOWN: case WM_NCMBUTTONDOWN: case WM_NCRBUTTONDOWN: + case WM_NCXBUTTONDOWN: case WM_NCLBUTTONDBLCLK: case WM_NCMBUTTONDBLCLK: case WM_NCRBUTTONDBLCLK: + case WM_NCXBUTTONDBLCLK: case WM_NCLBUTTONUP: case WM_NCMBUTTONUP: case WM_NCRBUTTONUP: + case WM_NCXBUTTONUP: // always relay the event. eat it if relaying. - PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_BUTTON, wParam, 0); + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_BUTTON, wParam, data); return g_relay; case WM_MOUSEWHEEL: if (g_relay) { // relay event - PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, wheel, 0); + PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, data, 0); } return g_relay; @@ -276,7 +296,10 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) } } - // handle the message + // handle the message. note that we don't handle X buttons + // here. that's okay because they're only supported on + // win2k and winxp and up and on those platforms we'll get + // get the mouse events through the low level hook. if (mouseHookHandler(wParam, x, y, w)) { return 1; } diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp index b4469cb1..857ad119 100644 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -699,6 +699,44 @@ CXWindowsPrimaryScreen::mapModifier(unsigned int state) const return mask; } +// map "Internet" keys to KeyIDs +static const KeySym g_map1008FF[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp, + /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, + /* 0x18 */ kKeyWWWHome, kKeyAppMail, 0, kKeyWWWSearch, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, kKeyWWWBack, kKeyWWWForward, + /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, 0, 0, 0, 0, + /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; + KeyID CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const { @@ -727,6 +765,10 @@ CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const // MISCELLANY return static_cast(keysym - 0xff00 + 0xef00); + case 0x1008ff00: + // "Internet" keys + return g_map1008FF[keysym & 0xff]; + default: { // lookup character in table UInt32 key = CXWindowsUtil::mapKeySymToUCS4(keysym); @@ -743,10 +785,18 @@ CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const ButtonID CXWindowsPrimaryScreen::mapButton(unsigned int button) const { - // FIXME -- should use button mapping? + // first three buttons map to 1, 2, 3 (kButtonLeft, Middle, Right) if (button >= 1 && button <= 3) { return static_cast(button); } + + // buttons 4 and 5 are ignored here. they're used for the wheel. + // buttons 6, 7, etc and up map to 4, 5, etc. + else if (button >= 6) { + return static_cast(button - 2); + } + + // unknown button else { return kButtonNone; } diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 5f9c89ca..78ccc57a 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -34,6 +34,9 @@ # else # error The XTest extension is required to build synergy # endif +# if defined(HAVE_X11_XF86KEYSYM_H) +# include +# endif #endif // @@ -263,7 +266,8 @@ void CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) { // choose button depending on rotation direction - const unsigned int xButton = mapButton((delta >= 0) ? 4 : 5); + const unsigned int xButton = mapButton(static_cast( + (delta >= 0) ? -1 : -2)); if (xButton == 0) { return; } @@ -573,17 +577,30 @@ CXWindowsSecondaryScreen::getToggleState() const unsigned int CXWindowsSecondaryScreen::mapButton(ButtonID id) const { + // map button -1 to button 4 (+wheel) + if (id == static_cast(-1)) { + id = 4; + } + + // map button -2 to button 5 (-wheel) + else if (id == static_cast(-2)) { + id = 5; + } + + // map buttons 4, 5, etc. to 6, 7, etc. to make room for buttons + // 4 and 5 used to simulate the mouse wheel. + else if (id >= 4) { + id += 2; + } + + // check button is in legal range if (id < 1 || id > m_buttons.size()) { // out of range return 0; } - else if (m_buttons[id - 1] == 0) { - // button not mapped - return 0; - } - else { - return static_cast(m_buttons[id - 1]); - } + + // map button + return static_cast(m_buttons[id - 1]); } KeyModifierMask @@ -1380,6 +1397,53 @@ CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) } } +// map special KeyID keys to KeySyms +#if defined(HAVE_X11_XF86KEYSYM_H) +static const KeySym g_mapE000[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, + /* 0xa4 */ 0, 0, + /* 0xa6 */ XF86XK_Back, XF86XK_Forward, + /* 0xa8 */ XF86XK_Refresh, XF86XK_Stop, + /* 0xaa */ XF86XK_Search, XF86XK_Favorites, + /* 0xac */ XF86XK_HomePage, XF86XK_AudioMute, + /* 0xae */ XF86XK_AudioLowerVolume, XF86XK_AudioRaiseVolume, + /* 0xb0 */ XF86XK_AudioNext, XF86XK_AudioPrev, + /* 0xb2 */ XF86XK_AudioStop, XF86XK_AudioPlay, + /* 0xb4 */ XF86XK_Mail, XF86XK_AudioMedia, + /* 0xb6 */ XF86XK_Launch0, XF86XK_Launch1, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + CXWindowsSecondaryScreen::KeyCodeIndex CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask& mask) const { @@ -1388,6 +1452,12 @@ CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask& mask) const if ((id & 0xfffff000) == 0xe000) { // special character switch (id & 0x0000ff00) { +#if defined(HAVE_X11_XF86KEYSYM_H) + case 0xe000: + keysym = g_mapE000[id & 0xff]; + break; +#endif + case 0xee00: // ISO 9995 Function and Modifier Keys if (id == kKeyLeftTab) { diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 9cef9c71..9af4dd0b 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -70,8 +70,8 @@ static const KeyModifierID kKeyModifierIDLast = 6; //! @name Key identifiers //@{ -// all identifiers except kKeyNone are equal to the corresponding -// X11 keysym - 0x1000. +// all identifiers except kKeyNone and those in 0xE000 to 0xE0FF +// inclusive are equal to the corresponding X11 keysym - 0x1000. // no key static const KeyID kKeyNone = 0x0000; @@ -211,6 +211,27 @@ static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */ // more function and modifier keys static const KeyID kKeyLeftTab = 0xEE20; + +// extended keys +static const KeyID kKeyWWWBack = 0xE0A6; +static const KeyID kKeyWWWForward = 0xE0A7; +static const KeyID kKeyWWWRefresh = 0xE0A8; +static const KeyID kKeyWWWStop = 0xE0A9; +static const KeyID kKeyWWWSearch = 0xE0AA; +static const KeyID kKeyWWWFavorites = 0xE0AB; +static const KeyID kKeyWWWHome = 0xE0AC; +static const KeyID kKeyAudioMute = 0xE0AD; +static const KeyID kKeyAudioDown = 0xE0AE; +static const KeyID kKeyAudioUp = 0xE0AF; +static const KeyID kKeyAudioNext = 0xE0B0; +static const KeyID kKeyAudioPrev = 0xE0B1; +static const KeyID kKeyAudioStop = 0xE0B2; +static const KeyID kKeyAudioPlay = 0xE0B3; +static const KeyID kKeyAppMail = 0xE0B4; +static const KeyID kKeyAppMedia = 0xE0B5; +static const KeyID kKeyAppUser1 = 0xE0B6; +static const KeyID kKeyAppUser2 = 0xE0B7; + //@} #endif diff --git a/lib/synergy/MouseTypes.h b/lib/synergy/MouseTypes.h index 6d68ee52..e4988553 100644 --- a/lib/synergy/MouseTypes.h +++ b/lib/synergy/MouseTypes.h @@ -29,6 +29,7 @@ static const ButtonID kButtonNone = 0; static const ButtonID kButtonLeft = 1; static const ButtonID kButtonMiddle = 2; static const ButtonID kButtonRight = 3; +static const ButtonID kButtonExtra0 = 4; //@} #endif From 923935060aa557c357ca04472b2755064f8ba516 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 8 May 2003 21:59:35 +0000 Subject: [PATCH 504/807] Fixed jumping to same client screen. It was broken by an earlier change (probably double tap). Jumping to the same server screen worked correctly. --- lib/server/CServer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index fd687114..f8f531b8 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -782,6 +782,7 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) m_active->getShape(ax, ay, aw, ah); // find direction of neighbor and get the neighbor + bool jump = true; IClient* newScreen; do { EDirection dir; @@ -800,6 +801,7 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) else { // we haven't left the screen newScreen = m_active; + jump = false; // if waiting and mouse is not on the border we're waiting // on then stop waiting. also if it's not on the border @@ -843,10 +845,11 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) // see if we should switch if (!isSwitchOkay(newScreen, dir, m_x, m_y)) { newScreen = m_active; + jump = false; } } while (false); - if (newScreen != m_active) { + if (jump) { // switch screens switchScreen(newScreen, m_x, m_y, false); } From a9c3d0be2fc6fd42d84a261eb7a07ffd593f5e89 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 10 May 2003 17:26:42 +0000 Subject: [PATCH 505/807] Updated documentation. --- INSTALL | 10 +++++++++- README | 14 +++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/INSTALL b/INSTALL index 31568c30..549273c9 100644 --- a/INSTALL +++ b/INSTALL @@ -95,13 +95,14 @@ First configure the server. Click the `Server' radio button * Click OK * Use `Add' to add your other computers * Using a computer's name as its screen name is recommended + * Choose desired screen options on the `Add' dialog * Use the controls under `Layout' to link screens together * Click (once) on the server's name in the `Screens' list * Choose the screen to the left of the server * Use `---' if there is no screen to the left of the server * Choose the screens to the right, above and below the server * Repeat the above steps for all the other screens - * Use `Options...' to set desired options. + * Use `Options...' to set desired options * If the server's screen name is not the server's computer name: * Click `Advanced...' * Enter the server's screen name next to `Screen Name' @@ -308,6 +309,13 @@ appear before the `links' and `aliases' sections. switch. This helps prevent unintentional switching when working near the edge of a screen. + screenSaverSync = {true|false} + If set to false then synergy won't synchronize screen + savers. Client screen savers will start according to + their individual configurations. The server screen + saver won't start if there is input, even if that input + is directed toward a client screen. + You can use both the switchDelay and switchDoubleTap options at the same time. Synergy will switch when either requirement is satisfied. diff --git a/README b/README index 2a068545..71a9c837 100644 --- a/README +++ b/README @@ -188,9 +188,12 @@ Finally, start the clients. Both the client and server should immediately report the connection or an error. If successful, you should now be able to move the mouse off the appropriate edge of your server's screen and have it -appear on the client's screen. Use the mouse and keyboard normally -except use the edge of the screens to jump to other screens. You -can also cut-and-paste across computers. Currently, only text +appear on the client's screen. If you're running the synery server +on Windows 95, 98, or Me then make sure the synergy log window is +not the active window; just click on another window, like synergy's +`Running Test...' window, if it is. Use the mouse and keyboard +normally except use the edge of the screens to jump to other screens. +You can also cut-and-paste across computers. Currently, only text transfers between computers. Start the remaining clients. Once the configuration is verified, see the instructions in INSTALL @@ -251,6 +254,11 @@ Tips and Tricks provide secure communications and it should not be used on or over untrusted networks. +* Synergy doesn't work if a 16-bit Windows application has the focus + on Windows 95/98/Me. This is due to limitations of Windows. One + commonly used 16-bit application is the command prompt (command.exe) + and this includes synergy's log window when running in test mode. + Bug Reports ----------- From 6f22c4d550736bb9fb6e10853543dc06f26aea58 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 10 May 2003 17:27:05 +0000 Subject: [PATCH 506/807] Changed version to 1.0.8. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index a5453acf..7e943610 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=0 -RELEASE_VERSION=7 +RELEASE_VERSION=8 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 8b26dad2..2f17175b 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.7" +# define VERSION "1.0.8" #endif // important strings From b87b1b1894698b75649abae12d4075b1ad90b126 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 10 May 2003 18:16:49 +0000 Subject: [PATCH 507/807] Updates for version 1.0.8. --- ChangeLog | 305 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ NEWS | 20 ++++ 2 files changed, 325 insertions(+) diff --git a/ChangeLog b/ChangeLog index 9b8621ad..1e1d2ba1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,308 @@ +2003/05/10 17:27:05 crs +configure.in +lib/common/Version.h + +Changed version to 1.0.8. + +---------- +2003/05/10 17:26:42 crs +INSTALL +README + +Updated documentation. + +---------- +2003/05/08 21:59:35 crs +lib/server/CServer.cpp + +Fixed jumping to same client screen. It was broken by an earlier +change (probably double tap). Jumping to the same server screen +worked correctly. + +---------- +2003/05/04 21:40:42 crs +configure.in +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/synergy/KeyTypes.h +lib/synergy/MouseTypes.h + +Added support for 4th and 5th (non-mouse-wheel) buttons and +"Internet" keyboard keys. + +---------- +2003/05/03 15:16:30 crs +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added screen saver synchronization option to win32 launcher dialog. + +---------- +2003/05/03 14:54:03 crs +lib/synergy/CSecondaryScreen.cpp + +Removed accidental debugging code. + +---------- +2003/05/03 14:38:36 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/server/CConfig.cpp +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h +lib/synergy/OptionTypes.h + +Added global configuration option to disable screen saver +synchronization. + +---------- +2003/05/03 13:57:52 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Forgot to restore global auto-repeat configuration on exit. + +---------- +2003/05/03 13:50:06 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/synergy/CSecondaryScreen.cpp +lib/synergy/CSecondaryScreen.h + +Now warping mouse to center of screen when leaving client screens. +Some users requested this. Also, the hider window is mapped before +warping the mouse so the active window shouldn't change if the focus +policy is point-to-focus. Showing the window first can also reduce +the likelihood of seeing the cursor briefly in its hidden position. + +---------- +2003/05/03 13:28:21 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Now turning off auto-repeat when on an X11 client. This prevents +the server from auto-repeating fake events, which is undesired +since synergy will do the auto-repeating itself. This also +disables auto-repeat on any keys locally configured on X11 to not +auto-repeat. That's mainly to suppress auto-repeat on modifier +keys, which auto-repeat on win32 but not X11. + +---------- +2003/05/03 12:54:22 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CSynergyHook.h + +Fixed a few win32 keyboard/mouse problems. First, the mouse hook +now captures non-client area mouse messages. Previously, these +were ignored (because i forgot about them) and they caused all +kinds of problems because they weren't forwarded. For example, +clicking on a window border would cause the window to start +resizing when the mouse came back to the server screen. Moving +inside a title bar meant that the mouse wouldn't move on the +client screen. + +Second, because non-client messages are now handled, the full +screen transparent window is no longer necessary to capture +input so it's never displayed. (The window is still necessary +for clipboard ownership so it's still created.) No transparent +window means no screen flashing. It also means we don't have to +become the foreground and active window. This plays better with +apps that minimize or restore when they're no longer the +foreground application/active window. + +Third, fixed the low level keyboard hook to forward toggle key +updates, which it was neglecting to do. + +Finally, keyboard and mouse input is always forwarded from the hook +to the primary screen handler which then shadows the current key +and mouse button state. If we're using low level hooks then this +isn't really necessary and GetKeyState() always returns the right +info but without low level hooks it means we can just use the +shadow state. It also means we don't have to show our window in +order to get the system's key state table up to date, fixing the +screen flash when checking for the scroll lock state. + +---------- +2003/05/03 12:37:03 crs +lib/arch/CArchMultithreadWindows.cpp +lib/synergy/CPrimaryScreen.cpp +lib/synergy/CSecondaryScreen.cpp + +Boosted priority of main synergy threads to be very high (highest +realtime priority). After some testing it appears that anything +less than this can starve synergy in some circumstances, preventing +it from forwarding messages to clients. This is a rather risky +change since synergy can now virtually take over a system if it +behaves badly. This change only affects windows systems since +lib/arch of other platforms don't yet attempt to boost priority. + +---------- +2003/04/27 18:05:32 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/server/Makefile.am +lib/server/server.dsp + +Fixes to previous checkpoint. Non-ascii keys seem to work correctly. +Still not supporting key composition on X11. + +---------- +2003/04/27 17:01:14 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_1.cpp +lib/server/CClientProxy1_1.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/Makefile.am +lib/synergy/CSecondaryScreen.h +lib/synergy/IClient.h +lib/synergy/IPrimaryScreenReceiver.h +lib/synergy/KeyTypes.h +lib/synergy/ProtocolTypes.h + +Checkpointing improved key handling. This change adds non-ASCII +key handling to win32 on both client and server. It also changes +the protocol and adds code to ensure every key pressed also gets +released and that that doesn't get confused when the KeyID for +the press is different from the KeyID of the release (or repeat). + +---------- +2003/04/24 20:11:38 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h + +Added KeySym <-> Unicode mappings. Changed code to use those +mappings to better support Unicode key events. + +---------- +2003/04/24 20:10:13 crs +cmd/Makefile.am + +Added exec.dsp to EXTRA_DIST. + +---------- +2003/04/16 20:59:25 crs +all.dsp +cmd/exec.dsp +lib/platform/makehook.dsp +lib/platform/synrgyhk.dsp +synergy.dsw + +Win32 project configuration fixes. + +---------- +2003/04/16 20:59:14 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/platform/CMSWindowsPrimaryScreen.cpp + +Minor win32 fixes. + +---------- +2003/04/16 20:05:00 crs +lib/server/CConfig.cpp + +Now allowing screen names with underscores. + +---------- +2003/04/14 22:16:21 crs +lib/platform/CXWindowsPrimaryScreen.cpp + +Fixed incorrect initialization of an XMotionEvent. + +---------- +2003/04/14 22:15:56 crs +configure.in +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h + +Added workaround for apparent Xinerama bug when warping the pointer. +This should allow synergy to be used on a system using Xinerama to +create a single logical screen from multiple physical screens. + +---------- +2003/04/13 18:14:01 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Fixed problem with type casting void* to int. + +---------- +2003/04/13 17:13:27 crs +lib/platform/CXWindowsScreenSaver.cpp + +Removed periodic call to XForceScreenSaver() to prevent the built-in +screen saver from activating. It was unnecessary since the built-in +screen saver is disabled as appropriate; this call was just to +ensure that the screen saver wouldn't start if an external program +reactivated the screen saver after synergy disabled it. + +It's possible that this was causing screen flicker under gnome, though +i don't know why. It's also possible that periodically sending events +to xscreensaver is causing the flicker but removing that code is more +difficult because xscreensaver can't be disabled, only deactivated or +killed. + +---------- +2003/04/13 14:59:53 crs +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsClipboard.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CSynergyHook.cpp + +Fixed several win32 bugs. First, synergy wasn't forwarding mouse +events to other hook functions, which broke some tools like objectbar. +Second, windows key processing was fixed. Previously pressing and +release the key would only send a press event, locking the user onto +the client window; also, the win32 server treated as a Meta modifier +instead of a Super modifier, which broke any use of it as any kind of +modifier key. Third, added hacks to support several key combinations +on windows 95/98/me that are treated specially by windows, including +Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any +combination using the windows key like Win+E and Win+F but not +Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which +only happened when using a synergy server on windows) has been fixed; +unfortunately the solution causes a lot of screen redraws for some +reason. Finally, there's been a fix to clipboard handling that may +or may not fix a problem where the clipboard would stop transferring +between systems after a little while. I can't be sure if it fixes +the problem because I can't reproduce the problem. + +---------- +2003/04/13 14:39:17 crs +cmd/launcher/launcher.rc + +Added mention of tray icon to launcher start message box. + +---------- 2003/03/26 21:03:58 crs configure.in lib/common/Version.h diff --git a/NEWS b/NEWS index b08b6d0f..e6be93f5 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,26 @@ Synergy News ============ +* May-10-2003 - Synergy 1.0.8 released + + Made following changes: + * Fixed hook forwarding (fixing interaction with objectbar) + * Fixed "Windows" key handling and added support Win+E, Win+F, etc + * Added win 95/98/me support for Alt+Tab, Alt+Esc, Ctrl+Esc + * Fixed scroll lock locking to server screen + * Fixed screen flashing on X11 and Windows + * Fixed compile problem on 64 bit systems + * Fixed Xinerama support + * Now allowing screen names that include underscores + * Improved non-ASCII key handling on Windows + * Fixed lagginess + * Fixed failure to capture all mouse input on Windows + * Fixed auto-repeat bugs on X11 + * Added option to disable screen saver synchronization + * Added support for 4th and 5th mouse buttons on Windows + * Added support for "Internet" and "Multimedia" keys + * Fixed jumping from client to itself (mouse wrapping) + * Mar-27-2003 - Synergy 1.0.6 released Made following changes: From 92ff58a5af272fdfa85ea1ab8746c78e7ac83c0f Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 17 May 2003 12:48:32 +0000 Subject: [PATCH 508/807] Added support for old versions of XF86keysym.h that are missing some expected #defines. --- lib/platform/CXWindowsSecondaryScreen.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 78ccc57a..fba55e7f 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -37,6 +37,12 @@ # if defined(HAVE_X11_XF86KEYSYM_H) # include # endif +# if !defined(XF86XK_Launch0) +# define XF86XK_Launch0 0x1008FF40 +# endif +# if !defined(XF86XK_Launch1) +# define XF86XK_Launch1 0x1008FF41 +# endif #endif // From 51919a50e6be74113174cc789ca95d93c33989de Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 17 May 2003 13:44:24 +0000 Subject: [PATCH 509/807] Added workaround for when XTest is unaware of Xinerama. When that's true, faking a mouse motion outside screen 0 is clamped onto screen 0. When the workaround is enabled, we use XWarpPointer() instead of an XTest fake motion. This isn't perfect but the only real fix requires patching XTest. --- lib/platform/CXWindowsSecondaryScreen.cpp | 53 +++++++++++++++++++++-- lib/platform/CXWindowsSecondaryScreen.h | 9 ++++ lib/server/CConfig.cpp | 10 ++++- lib/synergy/OptionTypes.h | 1 + 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index fba55e7f..8d599970 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -34,6 +34,12 @@ # else # error The XTest extension is required to build synergy # endif +# if HAVE_X11_EXTENSIONS_XINERAMA_H + // Xinerama.h may lack extern "C" for inclusion by C++ + extern "C" { +# include + } +# endif # if defined(HAVE_X11_XF86KEYSYM_H) # include # endif @@ -92,7 +98,8 @@ unsigned int assignBits(unsigned int src, CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) : CSecondaryScreen(), - m_window(None) + m_window(None), + m_xtestIsXineramaUnaware(true) { m_screen = new CXWindowsScreen(receiver, this); } @@ -295,8 +302,9 @@ CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) void CXWindowsSecondaryScreen::resetOptions() { - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; + m_xtestIsXineramaUnaware = true; CSecondaryScreen::resetOptions(); } @@ -312,6 +320,10 @@ CXWindowsSecondaryScreen::setOptions(const COptionsList& options) m_numLockHalfDuplex = (options[i + 1] != 0); LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); } + else if (options[i] == kOptionXTestXineramaUnaware) { + m_xtestIsXineramaUnaware = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false")); + } } CSecondaryScreen::setOptions(options); } @@ -388,6 +400,30 @@ CXWindowsSecondaryScreen::onPostOpen() // get the keyboard control state CDisplayLock display(m_screen); XGetKeyboardControl(display, &m_keyControl); + + // check if xinerama is enabled and, if so, get the first + // screen's dimensions. + m_xinerama = false; +#if HAVE_X11_EXTENSIONS_XINERAMA_H + int eventBase, errorBase; + if (XineramaQueryExtension(display, &eventBase, &errorBase)) { + if (XineramaIsActive(display)) { + int numScreens; + XineramaScreenInfo* screens; + screens = XineramaQueryScreens(display, &numScreens); + if (screens != NULL) { + if (numScreens > 1) { + m_xinerama = true; + m_xXinerama = screens[0].x_org; + m_yXinerama = screens[0].y_org; + m_wXinerama = screens[0].width; + m_hXinerama = screens[0].height; + } + XFree(screens); + } + } + } +#endif } void @@ -542,7 +578,16 @@ CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) { CDisplayLock display(m_screen); Display* pDisplay = display; - XTestFakeMotionEvent(display, DefaultScreen(pDisplay), x, y, CurrentTime); + + if (m_xinerama && m_xtestIsXineramaUnaware && + (x < m_xXinerama || x >= m_xXinerama + m_wXinerama || + y < m_yXinerama || y >= m_yXinerama + m_hXinerama)) { + XWarpPointer(display, None, None, 0, 0, 0, 0, x, y); + } + else { + XTestFakeMotionEvent(display, DefaultScreen(pDisplay), + x - m_xXinerama, y - m_yXinerama, CurrentTime); + } XSync(display, False); } diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index f44a12ed..b37f4327 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -175,6 +175,15 @@ private: // the keyboard control state the last time this screen was entered XKeyboardState m_keyControl; + + // stuff to workaround xtest being xinerama unaware. attempting + // to fake a mouse motion outside the first xinerama screen will + // be silently clamped to that screen. if xtest is buggy then + // use XWarpPointer instead. + bool m_xtestIsXineramaUnaware; + bool m_xinerama; + SInt32 m_xXinerama, m_yXinerama; + SInt32 m_wXinerama, m_hXinerama; }; #endif diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 3a66103b..89417b28 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -643,6 +643,9 @@ CConfig::getOptionName(OptionID id) if (id == kOptionScreenSaverSync) { return "screenSaverSync"; } + if (id == kOptionXTestXineramaUnaware) { + return "xtestIsXineramaUnaware"; + } return NULL; } @@ -651,7 +654,8 @@ CConfig::getOptionValue(OptionID id, OptionValue value) { if (id == kOptionHalfDuplexCapsLock || id == kOptionHalfDuplexNumLock || - id == kOptionScreenSaverSync) { + id == kOptionScreenSaverSync || + id == kOptionXTestXineramaUnaware) { return (value != 0) ? "true" : "false"; } if (id == kOptionModifierMapForShift || @@ -883,6 +887,10 @@ CConfig::readSectionScreens(std::istream& s) addOption(screen, kOptionModifierMapForSuper, parseModifierKey(value)); } + else if (name == "xtestIsXineramaUnaware") { + addOption(screen, kOptionXTestXineramaUnaware, + parseBoolean(value)); + } else { // unknown argument throw XConfigRead("unknown argument"); diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index d94cac37..aaefe7fa 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -54,6 +54,7 @@ static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); +static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); //@} #undef OPTION_CODE From 3fc39eab4e62cabec0ea5247a931eb207dcf70bc Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 17 May 2003 14:03:32 +0000 Subject: [PATCH 510/807] Fixed previous fix. Was trying to avoid using XWarpPointer() when warping on screen 0. That just doesn't work if screen 0 is not at 0,0. So now always use XWarpPointer() if there are multiple xinerama screens and the appropriate option is enabled. --- lib/platform/CXWindowsSecondaryScreen.cpp | 19 +++++-------------- lib/platform/CXWindowsSecondaryScreen.h | 8 +++----- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 8d599970..cb48ddd9 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -401,8 +401,7 @@ CXWindowsSecondaryScreen::onPostOpen() CDisplayLock display(m_screen); XGetKeyboardControl(display, &m_keyControl); - // check if xinerama is enabled and, if so, get the first - // screen's dimensions. + // check if xinerama is enabled and there is more than one screen m_xinerama = false; #if HAVE_X11_EXTENSIONS_XINERAMA_H int eventBase, errorBase; @@ -412,13 +411,7 @@ CXWindowsSecondaryScreen::onPostOpen() XineramaScreenInfo* screens; screens = XineramaQueryScreens(display, &numScreens); if (screens != NULL) { - if (numScreens > 1) { - m_xinerama = true; - m_xXinerama = screens[0].x_org; - m_yXinerama = screens[0].y_org; - m_wXinerama = screens[0].width; - m_hXinerama = screens[0].height; - } + m_xinerama = (numScreens > 1); XFree(screens); } } @@ -579,14 +572,12 @@ CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) CDisplayLock display(m_screen); Display* pDisplay = display; - if (m_xinerama && m_xtestIsXineramaUnaware && - (x < m_xXinerama || x >= m_xXinerama + m_wXinerama || - y < m_yXinerama || y >= m_yXinerama + m_hXinerama)) { - XWarpPointer(display, None, None, 0, 0, 0, 0, x, y); + if (m_xinerama && m_xtestIsXineramaUnaware) { + XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); } else { XTestFakeMotionEvent(display, DefaultScreen(pDisplay), - x - m_xXinerama, y - m_yXinerama, CurrentTime); + x, y, CurrentTime); } XSync(display, False); } diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index b37f4327..a1129ee5 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -177,13 +177,11 @@ private: XKeyboardState m_keyControl; // stuff to workaround xtest being xinerama unaware. attempting - // to fake a mouse motion outside the first xinerama screen will - // be silently clamped to that screen. if xtest is buggy then - // use XWarpPointer instead. + // to fake a mouse motion under xinerama may behave strangely, + // especially if screen 0 is not at 0,0 or if faking a motion on + // a screen other than screen 0. bool m_xtestIsXineramaUnaware; bool m_xinerama; - SInt32 m_xXinerama, m_yXinerama; - SInt32 m_wXinerama, m_hXinerama; }; #endif From 07ed8ebd1cdc90c388e89e5d4a15f77f1531d10a Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 17 May 2003 14:10:11 +0000 Subject: [PATCH 511/807] Added documentation for xtestIsXineramaUnaware option. --- INSTALL | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/INSTALL b/INSTALL index 549273c9..f33265ad 100644 --- a/INSTALL +++ b/INSTALL @@ -202,6 +202,17 @@ appear before the `links' and `aliases' sections. This is identical to halfDuplexCapsLock except it applies to the num lock key. + xtestIsXineramaUnaware = {true|false} + This option works around a bug in the XTest extension + when used in combination with Xinerama. It affects + X11 clients only. Not all versions of the XTest + extension are aware of the Xinerama extension. As a + result, they do not move the mouse correctly when + using multiple Xinerama screens. This option is + currently true by default. If you know your XTest + extension is Xinerama aware then set this option to + false. + shift = {shift|ctrl|alt|meta|super|none} ctrl = {shift|ctrl|alt|meta|super|none} alt = {shift|ctrl|alt|meta|super|none} From dc6652a21a91ff51d1fe892c12614d140e203680 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 17 May 2003 20:58:27 +0000 Subject: [PATCH 512/807] Fixed getting locked to screen after ctrl+alt+del. Also fixed cursor not being hidden on win32 server when on client screens (which happened when using low-level hooks). --- lib/platform/CMSWindowsPrimaryScreen.cpp | 57 +++++++++++++++++++-- lib/platform/CMSWindowsPrimaryScreen.h | 10 ++-- lib/platform/CMSWindowsScreen.cpp | 24 ++++++--- lib/platform/CMSWindowsScreen.h | 7 +++ lib/platform/CMSWindowsSecondaryScreen.cpp | 7 +++ lib/platform/CMSWindowsSecondaryScreen.h | 1 + lib/platform/CSynergyHook.cpp | 13 +++-- lib/platform/IMSWindowsScreenEventHandler.h | 7 +++ lib/synergy/CPrimaryScreen.cpp | 6 +-- 9 files changed, 111 insertions(+), 21 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 92d3e832..75f8c2f1 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -319,7 +319,8 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( m_threadID(0), m_mark(0), m_markReceived(0), - m_lowLevel(false) + m_lowLevel(false), + m_cursorThread(0) { assert(m_receiver != NULL); @@ -787,6 +788,13 @@ CMSWindowsPrimaryScreen::preDestroyWindow(HWND) m_uninstall(); } +void +CMSWindowsPrimaryScreen::onAccessibleDesktop() +{ + // get the current keyboard state + updateKeys(); +} + void CMSWindowsPrimaryScreen::onPreMainLoop() { @@ -833,6 +841,18 @@ CMSWindowsPrimaryScreen::onPostClose() void CMSWindowsPrimaryScreen::onPreEnter() { + // show cursor if we hid it + if (m_cursorThread != 0) { + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, TRUE); + } + ShowCursor(TRUE); + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, FALSE); + } + m_cursorThread = 0; + } + // enable ctrl+alt+del, alt+tab, etc if (m_is95Family) { DWORD dummy = 0; @@ -869,6 +889,19 @@ CMSWindowsPrimaryScreen::onPostLeave(bool success) DWORD dummy = 0; SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); } + + // hide the cursor if using low level hooks + if (m_lowLevel) { + HWND hwnd = GetForegroundWindow(); + m_cursorThread = GetWindowThreadProcessId(hwnd, NULL); + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, TRUE); + } + ShowCursor(FALSE); + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, FALSE); + } + } } } @@ -876,10 +909,13 @@ void CMSWindowsPrimaryScreen::createWindow() { // open the desktop and the window - HWND window = m_screen->openDesktop(); - if (window == NULL) { + m_window = m_screen->openDesktop(); + if (m_window == NULL) { throw XScreenOpenFailure(); } + + // we don't ever want our window to activate + EnableWindow(m_window, FALSE); } void @@ -892,14 +928,25 @@ CMSWindowsPrimaryScreen::destroyWindow() bool CMSWindowsPrimaryScreen::showWindow() { - // do nothing. we don't need to show a window to capture input. + // we don't need a window to capture input but we need a window + // to hide the cursor when using low-level hooks. do not try to + // take the activation; we want the currently active window to + // stay active. + if (m_lowLevel) { + SetWindowPos(m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1, + SWP_NOACTIVATE); + ShowWindow(m_window, SW_SHOWNA); + } return true; } void CMSWindowsPrimaryScreen::hideWindow() { - // do nothing. we don't need to show a window to capture input. + // hide our window + if (m_lowLevel) { + ShowWindow(m_window, SW_HIDE); + } } void diff --git a/lib/platform/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h index dfe40adf..a80ddbe7 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.h +++ b/lib/platform/CMSWindowsPrimaryScreen.h @@ -52,6 +52,7 @@ public: virtual SInt32 getJumpZoneSize() const; virtual void postCreateWindow(HWND); virtual void preDestroyWindow(HWND); + virtual void onAccessibleDesktop(); protected: // CPrimaryScreen overrides @@ -101,6 +102,9 @@ private: // the main loop's thread id DWORD m_threadID; + // my window + HWND m_window; + // used to discard queued messages that are no longer needed UInt32 m_mark; UInt32 m_markReceived; @@ -128,10 +132,8 @@ private: SetRelayFunc m_setRelay; bool m_lowLevel; - // stuff for restoring active window - HWND m_lastForegroundWindow; - HWND m_lastActiveWindow; - DWORD m_lastActiveThread; + // stuff for hiding the cursor + DWORD m_cursorThread; }; #endif diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 1e848b1c..d75257c5 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -68,7 +68,8 @@ CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, m_installScreensaver(NULL), m_uninstallScreensaver(NULL), m_screensaver(NULL), - m_screensaverNotify(false) + m_screensaverNotify(false), + m_inaccessibleDesktop(false) { assert(s_screen == NULL); assert(m_receiver != NULL); @@ -384,7 +385,7 @@ CMSWindowsScreen::syncDesktop() // of sucking up more and more CPU each time it's called (even if // the threads are already attached). since we only expect one // thread to call this more than once we can save just the last - // the attached thread. + // attached thread. DWORD threadID = GetCurrentThreadId(); if (threadID != m_lastThreadID && threadID != m_threadID) { m_lastThreadID = threadID; @@ -493,16 +494,27 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) if (isCurrentDesktop(desk)) { CloseDesktop(desk); } - else if (!m_screensaver->isActive()) { + else if (m_screensaver->isActive()) { // don't switch desktops when the screensaver is // active. we'd most likely switch to the // screensaver desktop which would have the side // effect of forcing the screensaver to stop. - switchDesktop(desk); - } - else { CloseDesktop(desk); } + else { + switchDesktop(desk); + } + + // if the desktop was inaccessible then notify the + // event handler of that. + if (m_inaccessibleDesktop) { + m_inaccessibleDesktop = false; + m_eventHandler->onAccessibleDesktop(); + } + } + else if (!m_inaccessibleDesktop) { + // the desktop has become inaccessible + m_inaccessibleDesktop = true; } } diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index b7b97156..f4af0cac 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -191,6 +191,13 @@ private: CMSWindowsScreenSaver* m_screensaver; bool m_screensaverNotify; + // true when the current desktop is inaccessible. while + // the desktop is inaccessible we won't receive user input + // and we'll lose track of the keyboard state. when the + // desktop becomes accessible again we'll notify the event + // handler of that. + bool m_inaccessibleDesktop; + static CMSWindowsScreen* s_screen; }; diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index d6ad9c47..aa3d9785 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -417,6 +417,13 @@ CMSWindowsSecondaryScreen::preDestroyWindow(HWND) // do nothing } +void +CMSWindowsSecondaryScreen::onAccessibleDesktop() +{ + // get the current keyboard state + updateKeys(); +} + void CMSWindowsSecondaryScreen::onPreMainLoop() { diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index b975b46c..906f28b9 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -59,6 +59,7 @@ public: virtual SInt32 getJumpZoneSize() const; virtual void postCreateWindow(HWND); virtual void preDestroyWindow(HWND); + virtual void onAccessibleDesktop(); protected: // CSecondaryScreen overrides diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index d5dcc1e5..904d46bd 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -111,10 +111,17 @@ void hideCursor(DWORD thread) { // we should be running the context of the window who's cursor - // we want to hide so we shouldn't have to attach thread input. - g_cursor = GetCursor(); + // we want to hide so we shouldn't have to attach thread input + // but we'll check to make sure. g_cursorThread = thread; - SetCursor(NULL); + if (g_cursorThread != 0) { + DWORD myThread = GetCurrentThreadId(); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, TRUE); + g_cursor = SetCursor(NULL); + if (myThread != g_cursorThread) + AttachThreadInput(myThread, g_cursorThread, FALSE); + } } static diff --git a/lib/platform/IMSWindowsScreenEventHandler.h b/lib/platform/IMSWindowsScreenEventHandler.h index 2ce8b90a..ee24ea8e 100644 --- a/lib/platform/IMSWindowsScreenEventHandler.h +++ b/lib/platform/IMSWindowsScreenEventHandler.h @@ -37,6 +37,13 @@ public: */ virtual void preDestroyWindow(HWND) = 0; + //! Notify of newly accessible desktop + /*! + This is called when the user switched from an inaccessible desktop + to an accessible desktop. + */ + virtual void onAccessibleDesktop() = 0; + //@} // IScreenEventHandler overrides diff --git a/lib/synergy/CPrimaryScreen.cpp b/lib/synergy/CPrimaryScreen.cpp index 79d0384e..19bbc8b2 100644 --- a/lib/synergy/CPrimaryScreen.cpp +++ b/lib/synergy/CPrimaryScreen.cpp @@ -172,12 +172,12 @@ CPrimaryScreen::leave() // get keyboard state as we leave updateKeys(); - // subclass hook - onPostLeave(true); - // warp mouse to center warpCursorToCenter(); + // subclass hook + onPostLeave(true); + // local client now active m_active = true; From 5484751997424aa4d1357ada6ee1543a96fad77d Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 17 May 2003 20:58:48 +0000 Subject: [PATCH 513/807] Changed version to 1.0.9. --- lib/common/Version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/Version.h b/lib/common/Version.h index 2f17175b..6e38f86b 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.8" +# define VERSION "1.0.9" #endif // important strings From c0cd5cfce020c0ae8174aaf382d59b2b8102d458 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 17 May 2003 20:59:57 +0000 Subject: [PATCH 514/807] Changed version to 1.0.9 in configure.in. --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 7e943610..15912330 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=0 -RELEASE_VERSION=8 +RELEASE_VERSION=9 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) From 01fab82edcdedcdcdc4dadd21cb97ab282761779 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 20 May 2003 19:14:24 +0000 Subject: [PATCH 515/807] Reduced maximum priority in debug build. --- lib/arch/CArchMultithreadWindows.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index 4934fc36..ba2fef6d 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -378,7 +378,12 @@ CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n) { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST }, { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL} }; +#if defined(_DEBUG) + // don't use really high priorities when debugging + static const size_t s_pMax = 13; +#else static const size_t s_pMax = sizeof(s_pClass) / sizeof(s_pClass[0]) - 1; +#endif static const size_t s_pBase = 8; // index of normal priority assert(thread != NULL); From bdecca0bccce0031976cae492e410d8d12ecc59b Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 20 May 2003 19:15:58 +0000 Subject: [PATCH 516/807] Attempt to improve key event synthesis. This change adds support for dead keys and attempts to choose the correct code page for the thread that will (probably) receive synthesized events. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 359 ++++++++++++++------- lib/platform/CMSWindowsSecondaryScreen.h | 12 + 2 files changed, 258 insertions(+), 113 deletions(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index aa3d9785..b7c61b57 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -747,7 +747,7 @@ static const UINT g_mapEF00[] = /* 0x64 */ 0, 0, 0, VK_APPS|0x100, /* 0x68 */ 0, 0, VK_HELP|0x100, VK_CANCEL|0x100, 0, 0, 0, 0, /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, VK_MODECHANGE|0x100, VK_NUMLOCK|0x100, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK|0x100, /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN|0x100, 0, 0, /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, @@ -867,94 +867,32 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, return m_mask; } - // get output mask. default output mask carries over the current - // toggle modifier states and includes desired shift, control, alt, - // meta, and super states. - KeyModifierMask outMask = (m_mask & - (KeyModifierCapsLock | - KeyModifierNumLock | - KeyModifierScrollLock)); - outMask |= (mask & - (KeyModifierShift | - KeyModifierControl | - KeyModifierAlt | - KeyModifierMeta | - KeyModifierSuper)); + // handle other special keys + if (virtualKey != 0) { + // compute the final desired modifier mask. special keys use + // the desired modifiers as given except we keep the caps lock, + // num lock, and scroll lock as is. + KeyModifierMask outMask = (m_mask & + (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)); + outMask |= (mask & + (KeyModifierShift | + KeyModifierControl | + KeyModifierAlt | + KeyModifierMeta | + KeyModifierSuper)); - // set control and alt if mode shift (AltGr) is requested - if ((mask & KeyModifierModeSwitch) != 0) { - outMask |= KeyModifierControl | KeyModifierAlt; - } + // strip out extended key flag + UINT virtualKey2 = (virtualKey & ~0x100); - // extract extended key flag - const bool isExtended = ((virtualKey & 0x100) != 0); - virtualKey &= ~0x100; - - // if not in map then ask system to convert character - if (virtualKey == 0) { - // translate. return no keys if unknown key. - char ascii; - wchar_t unicode = static_cast(id & 0x0000ffff); - BOOL error; - if (WideCharToMultiByte(CP_THREAD_ACP, -#if defined(WC_NO_BEST_FIT_CHARS) - WC_NO_BEST_FIT_CHARS | -#endif - WC_COMPOSITECHECK | - WC_DEFAULTCHAR, - &unicode, 1, - &ascii, 1, NULL, &error) == 0 || error) { - LOG((CLOG_DEBUG2 "character %d not in code page", id)); - return m_mask; - } - SHORT vk = VkKeyScan(ascii); - if (vk == 0xffff) { - LOG((CLOG_DEBUG2 "no virtual key for character %d", id)); - return m_mask; - } - - // use whatever shift state VkKeyScan says - // FIXME -- also for control and alt, but it's more difficult - // to determine if control and alt must be off or if it just - // doesn't matter. - outMask &= ~KeyModifierShift; - - // convert system modifier mask to our mask - if (HIBYTE(vk) & 1) { - outMask |= KeyModifierShift; - } - if (HIBYTE(vk) & 2) { - outMask |= KeyModifierControl; - } - if (HIBYTE(vk) & 4) { - outMask |= KeyModifierAlt; - } - - // handle combination of caps-lock and shift. if caps-lock is - // off locally then use shift as necessary. if caps-lock is on - // locally then it reverses the meaning of shift for keys that - // are subject to case conversion. - if ((outMask & KeyModifierCapsLock) != 0) { - if (tolower(ascii) != toupper(ascii)) { - LOG((CLOG_DEBUG2 "flip shift")); - outMask ^= KeyModifierShift; - } - } - - // get virtual key - virtualKey = LOBYTE(vk); - } - - // if in map then figure out correct modifier state - else { - // check numeric keypad. note that while KeyID distinguishes - // between the keypad movement keys (e.g. Home, left arrow), - // the virtual keys do not. however, the virtual keys do - // distinguish between keypad numbers and operators (e.g. - // add, multiply) and their main keyboard counterparts. - // therefore, we can ignore the num-lock state for movement - // virtual keys but not for numeric keys. - if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) { + // check numeric keypad. note that virtual keys do not distinguish + // between the keypad and non-keypad movement keys. however, the + // virtual keys do distinguish between keypad numbers and operators + // (e.g. add, multiply) and their main keyboard counterparts. + // therefore, we can ignore the num-lock state for movement virtual + // keys but not for numeric keys. + if (virtualKey2 >= VK_NUMPAD0 && virtualKey2 <= VK_DIVIDE) { // set required shift state based on current numlock state if ((outMask & KeyModifierNumLock) == 0) { if ((m_mask & KeyModifierNumLock) == 0) { @@ -968,13 +906,184 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, } } - // check for left tab - else if (id == kKeyLeftTab) { - outMask |= KeyModifierShift; + // check for left tab. that requires the shift key. + if (id == kKeyLeftTab) { + mask |= KeyModifierShift; + } + + // now generate the keystrokes and return the resulting modifier mask + LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d mask 0x%04x", id, virtualKey2, outMask)); + return mapToKeystrokes(keys, virtualKey, m_mask, outMask, action); + } + + // determine the thread that'll receive this event + // FIXME -- we can't be sure we'll get the right thread here + HWND targetWindow = GetForegroundWindow(); + DWORD targetThread = GetWindowThreadProcessId(targetWindow, NULL); + + // figure out the code page for the target thread. i'm just + // guessing here. get the target thread's keyboard layout, + // extract the language id from that, and choose the code page + // based on that language. + HKL hkl = GetKeyboardLayout(targetThread); + LANGID langID = static_cast(LOWORD(hkl)); + UINT codePage = getCodePageFromLangID(langID); + LOG((CLOG_DEBUG2 "using code page %d and language id 0x%04x for thread 0x%08x", codePage, langID, targetThread)); + + // regular characters are complicated by dead keys. it may not be + // possible to generate a desired character directly. we may need + // to generate a dead key first then some other character. the + // app receiving the events will compose these two characters into + // a single precomposed character. + // + // as best as i can tell this is the simplest way to convert a + // character into its uncomposed version. along the way we'll + // discover if the key cannot be handled at all. we convert + // from wide char to multibyte, then from multibyte to wide char + // forcing conversion to composite characters, then from wide + // char back to multibyte without making precomposed characters. + BOOL error; + char multiByte[2 * MB_LEN_MAX]; + wchar_t unicode[2]; + unicode[0] = static_cast(id & 0x0000ffff); + int nChars = WideCharToMultiByte(codePage, + WC_COMPOSITECHECK | WC_DEFAULTCHAR, + unicode, 1, + multiByte, sizeof(multiByte), + NULL, &error); + if (nChars == 0 || error) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); + return m_mask; + } + nChars = MultiByteToWideChar(codePage, + MB_COMPOSITE | MB_ERR_INVALID_CHARS, + multiByte, nChars, + unicode, 2); + if (nChars == 0) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x mb->wc mapping failed", id)); + return m_mask; + } + nChars = WideCharToMultiByte(codePage, + 0, + unicode, nChars, + multiByte, sizeof(multiByte), + NULL, &error); + if (nChars == 0 || error) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x wc->mb mapping failed", id)); + return m_mask; + } + + // we expect one or two characters in multiByte. if there are two + // then the *second* is a dead key. process the dead key if there. + // FIXME -- we assume each character is one byte here + if (nChars > 2) { + LOG((CLOG_DEBUG2 "multibyte characters not supported for character 0x%04x", id)); + return m_mask; + } + if (nChars == 2) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x needs dead key %u", id, (unsigned char)multiByte[1])); + mapCharacter(keys, multiByte[1], hkl, m_mask, mask, action); + } + + // process character + LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); + virtualKey = mapCharacter(keys, multiByte[0], hkl, m_mask, mask, action); + + // non-special key cannot modify the modifier mask + return m_mask; +} + +UINT +CMSWindowsSecondaryScreen::mapCharacter(Keystrokes& keys, + char c, HKL hkl, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, EKeyAction action) const +{ + // translate the character into its virtual key and its required + // modifier state. + SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); + + // get virtual key + UINT virtualKey = LOBYTE(virtualKeyAndModifierState); + + // get the required modifier state + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + + // compute the final desired modifier mask. this is the + // desired modifier mask except that the system might require + // that certain modifiers be up or down in order to generate + // the character. to start with, we know that we want to keep + // the caps lock, num lock, scroll lock modifiers as is. also, + // the system never requires the meta or super modifiers so we + // can set those however we like. + KeyModifierMask outMask = (currentMask & + (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)); + outMask |= (desiredMask & + (KeyModifierMeta | + KeyModifierSuper)); + + // win32 does not permit ctrl and alt used together to + // modify a character because ctrl and alt together mean + // AltGr. if the desired mask has both ctrl and alt then + // strip them both out. + if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + outMask &= ~(KeyModifierControl | KeyModifierAlt); + } + + // strip out the desired shift state. we're forced to use + // a particular shift state to generate the desired character. + outMask &= ~KeyModifierShift; + + // use the required modifiers. if AltGr is required then + // modifierState will indicate control and alt. + if ((modifierState & 1) != 0) { + outMask |= KeyModifierShift; + } + if ((modifierState & 2) != 0) { + outMask |= KeyModifierControl; + } + if ((modifierState & 4) != 0) { + outMask |= KeyModifierAlt; + } + + // handle combination of caps-lock and shift. if caps-lock is + // off locally then use shift as necessary. if caps-lock is on + // locally then it reverses the meaning of shift for keys that + // are subject to case conversion. + if ((outMask & KeyModifierCapsLock) != 0) { + // there doesn't seem to be a simple way to test if a + // character respects the caps lock key. for normal + // characters it's easy enough but CharLower() and + // CharUpper() don't map dead keys even though they + // do respect caps lock for some unfathomable reason. + // first check the easy way. if that doesn't work + // then see if it's a dead key. + unsigned char uc = static_cast(c); + if (CharLower((LPTSTR)uc) != CharUpper((LPTSTR)uc) || + (MapVirtualKey(virtualKey, 2) & 0x80000000lu) != 0) { + LOG((CLOG_DEBUG2 "flip shift")); + outMask ^= KeyModifierShift; } } - LOG((CLOG_DEBUG2 "KeyID %d to virtual key %d mask 0x%04x", id, virtualKey, outMask)); + // now generate the keystrokes. ignore the resulting modifier + // mask since it can't have changed (because we don't call this + // method for modifier keys). + LOG((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%04x", (unsigned char)c, virtualKey, outMask)); + mapToKeystrokes(keys, virtualKey, currentMask, outMask, action); + + return virtualKey; +} + +KeyModifierMask +CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, + UINT virtualKey, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, EKeyAction action) const +{ // a list of modifier key info class CModifierInfo { public: @@ -996,9 +1105,12 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, static const unsigned int s_numModifiers = sizeof(s_modifier) / sizeof(s_modifier[0]); + // strip out extended key flag + UINT virtualKey2 = (virtualKey & ~0x100); + // note if the key is a modifier unsigned int modifierIndex; - switch (virtualKey) { + switch (virtualKey2) { case VK_SHIFT: case VK_LSHIFT: case VK_RSHIFT: @@ -1040,18 +1152,17 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, } const bool isModifier = (modifierIndex != s_numModifiers); - // add the key events required to get to the modifier state - // necessary to generate an event yielding id. also save the - // key events required to restore the state. if the key is - // a modifier key then skip this because modifiers should not - // modify modifiers. + // add the key events required to get to the desired modifier state. + // also save the key events required to restore the current state. + // if the key is a modifier key then skip this because modifiers + // should not modify modifiers. Keystrokes undo; Keystroke keystroke; - if (outMask != m_mask && !isModifier) { + if (desiredMask != currentMask && !isModifier) { for (unsigned int i = 0; i < s_numModifiers; ++i) { KeyModifierMask bit = s_modifier[i].m_mask; - if ((outMask & bit) != (m_mask & bit)) { - if ((outMask & bit) != 0) { + if ((desiredMask & bit) != (currentMask & bit)) { + if ((desiredMask & bit) != 0) { // modifier is not active but should be. if the // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a @@ -1117,25 +1228,22 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // add the key event keystroke.m_virtualKey = virtualKey; - if (isExtended) { - keystroke.m_virtualKey |= 0x100; - } switch (action) { case kPress: - keystroke.m_press = true; - keystroke.m_repeat = false; + keystroke.m_press = true; + keystroke.m_repeat = false; keys.push_back(keystroke); break; case kRelease: - keystroke.m_press = false; - keystroke.m_repeat = false; + keystroke.m_press = false; + keystroke.m_repeat = false; keys.push_back(keystroke); break; case kRepeat: - keystroke.m_press = true; - keystroke.m_repeat = true; + keystroke.m_press = true; + keystroke.m_repeat = true; keys.push_back(keystroke); break; } @@ -1149,7 +1257,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // if the key is a modifier key then compute the modifier mask after // this key is pressed. - mask = m_mask; + KeyModifierMask mask = currentMask; if (isModifier && action != kRepeat) { // toggle keys modify the state on release. other keys set // the bit on press and clear the bit on release. @@ -1166,12 +1274,12 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // can't reset bit until all keys that set it are released. // scan those keys to see if any are pressed. bool down = false; - if (virtualKey != (modifier.m_virtualKey & 0xff) && + if (virtualKey2 != (modifier.m_virtualKey & 0xff) && (m_keys[modifier.m_virtualKey & 0xff] & 0x80) != 0) { down = true; } if (modifier.m_virtualKey2 != 0 && - virtualKey != (modifier.m_virtualKey2 & 0xff) && + virtualKey2 != (modifier.m_virtualKey2 & 0xff) && (m_keys[modifier.m_virtualKey2 & 0xff] & 0x80) != 0) { down = true; } @@ -1180,7 +1288,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, } } - LOG((CLOG_DEBUG2 "previous modifiers 0x%04x, final modifiers 0x%04x", m_mask, mask)); + LOG((CLOG_DEBUG2 "previous modifiers 0x%04x, final modifiers 0x%04x", currentMask, mask)); return mask; } @@ -1376,3 +1484,28 @@ CMSWindowsSecondaryScreen::sendKeyEvent(UINT virtualKey, bool press) static_cast(code), flags, 0); LOG((CLOG_DEBUG1 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : ""))); } + +UINT +CMSWindowsSecondaryScreen::getCodePageFromLangID(LANGID langid) const +{ + // construct a locale id from the language id + LCID lcid = MAKELCID(langid, SORT_DEFAULT); + + // get the ANSI code page for this locale + char data[6]; + if (GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, data, 6) == 0) { + // can't get code page + LOG((CLOG_DEBUG1 "can't find code page for langid 0x%04x", langid)); + return CP_ACP; + } + + // convert stringified code page into a number + UINT codePage = static_cast(atoi(data)); + if (codePage == 0) { + // parse failed + LOG((CLOG_DEBUG1 "can't parse code page %s for langid 0x%04x", data, langid)); + return CP_ACP; + } + + return codePage; +} diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index 906f28b9..7f97d3ce 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -103,6 +103,16 @@ private: bool press, DWORD* data) const; KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, KeyModifierMask, EKeyAction) const; + UINT mapCharacter(Keystrokes& keys, + char c, HKL hkl, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, + EKeyAction action) const; + KeyModifierMask mapToKeystrokes(Keystrokes& keys, + UINT virtualKey, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, + EKeyAction action) const; void doKeystrokes(const Keystrokes&, SInt32 count); void toggleKey(UINT virtualKey, KeyModifierMask mask); @@ -110,6 +120,8 @@ private: bool isExtendedKey(UINT virtualKey) const; void sendKeyEvent(UINT virtualKey, bool press); + UINT getCodePageFromLangID(LANGID) const; + private: CMutex m_mutex; CMSWindowsScreen* m_screen; From d577d457e3d15b5c556b9e9dcf82e110cf0d400d Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 21 May 2003 19:38:11 +0000 Subject: [PATCH 517/807] Made double tap require moving farther away from the tapped edge before arming. This should reduce spurious double taps. --- lib/server/CServer.cpp | 29 ++++++++++++++++++++++++----- lib/server/CServer.h | 3 ++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index f8f531b8..fa2487d4 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -68,6 +68,7 @@ CServer::CServer(const CString& serverName) : m_switchTwoTapDelay(0.0), m_switchTwoTapEngaged(false), m_switchTwoTapArmed(false), + m_switchTwoTapZone(3), m_status(kNotRunning) { // do nothing @@ -726,8 +727,16 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) dir = kBottom; } else { - // still on local screen - onNoSwitch(); + // still on local screen. check if we're inside the tap region. + SInt32 tapZone = (zoneSize < m_switchTwoTapZone) ? + m_switchTwoTapZone : zoneSize; + bool inTapZone = (x < ax + tapZone || + x >= ax + aw - tapZone || + y < ay + tapZone || + y >= ay + ah - tapZone); + + // failed to switch + onNoSwitch(inTapZone); return false; } @@ -831,7 +840,17 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) break; } if (clearWait) { - onNoSwitch(); + // still on local screen. check if we're inside the + // tap region. + SInt32 tapZone = (zoneSize < m_switchTwoTapZone) ? + m_switchTwoTapZone : zoneSize; + bool inTapZone = (m_x < ax + tapZone || + m_x >= ax + aw - tapZone || + m_y < ay + tapZone || + m_y >= ay + ah - tapZone); + + // failed to switch + onNoSwitch(inTapZone); } } @@ -1290,7 +1309,7 @@ CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) } void -CServer::onNoSwitch() +CServer::onNoSwitch(bool inTapZone) { if (m_switchTwoTapEngaged) { if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) { @@ -1298,7 +1317,7 @@ CServer::onNoSwitch() m_switchTwoTapEngaged = false; m_switchTwoTapArmed = false; } - else { + else if (!inTapZone) { // we've moved away from the edge and there's still // time to get back for a double tap. m_switchTwoTapArmed = true; diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 1e69f108..89a52bd6 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -258,7 +258,7 @@ private: // update switch state due to a mouse move that doesn't try to // switch screens. - void onNoSwitch(); + void onNoSwitch(bool inTapZone); // reset switch wait state void clearSwitchState(); @@ -403,6 +403,7 @@ private: CStopwatch m_switchTwoTapTimer; bool m_switchTwoTapEngaged; bool m_switchTwoTapArmed; + SInt32 m_switchTwoTapZone; // the status change jobs and status CJobList m_statusJobs; From 3bd3e7a17c41e49302ecd8edc63a570b1e71e2a1 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 21 May 2003 21:22:14 +0000 Subject: [PATCH 518/807] Fixed unsigned compare against zero. Changed win32 priority to maximum. --- lib/arch/CArchMultithreadWindows.cpp | 13 ++++++++----- lib/synergy/CPrimaryScreen.cpp | 2 +- lib/synergy/CSecondaryScreen.cpp | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index ba2fef6d..e9101515 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -388,14 +388,17 @@ CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n) assert(thread != NULL); - size_t index = s_pBase - n; - if (index < 0) { + size_t index; + if (s_pBase < n) { // lowest priority index = 0; } - else if (index > s_pMax) { - // highest priority - index = s_pMax; + else { + index = s_pBase - n; + if (index > s_pMax) { + // highest priority + index = s_pMax; + } } SetPriorityClass(thread->m_thread, s_pClass[index].m_class); SetThreadPriority(thread->m_thread, s_pClass[index].m_level); diff --git a/lib/synergy/CPrimaryScreen.cpp b/lib/synergy/CPrimaryScreen.cpp index 19bbc8b2..320733f0 100644 --- a/lib/synergy/CPrimaryScreen.cpp +++ b/lib/synergy/CPrimaryScreen.cpp @@ -40,7 +40,7 @@ void CPrimaryScreen::mainLoop() { // change our priority - CThread::getCurrentThread().setPriority(-13); + CThread::getCurrentThread().setPriority(-14); // run event loop try { diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp index c3df69bb..e7e72e70 100644 --- a/lib/synergy/CSecondaryScreen.cpp +++ b/lib/synergy/CSecondaryScreen.cpp @@ -40,7 +40,7 @@ void CSecondaryScreen::mainLoop() { // change our priority - CThread::getCurrentThread().setPriority(-13); + CThread::getCurrentThread().setPriority(-14); // run event loop try { From 3fc87e7f8773471a3a4abde6ed568e51a2ac5ba8 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 26 May 2003 09:46:52 +0000 Subject: [PATCH 519/807] Fixed loss of ctrl+alt+del key releases when the Winlogin desktop is accessible (was already fixed when inaccessible). This change also ignores press and release of virtual key 0, which should never happen but does according to one user. --- lib/platform/CMSWindowsPrimaryScreen.cpp | 34 ++++++++++++++++++++++++ lib/platform/CMSWindowsScreen.cpp | 2 ++ 2 files changed, 36 insertions(+) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 75f8c2f1..8734e540 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -1501,6 +1501,10 @@ CMSWindowsPrimaryScreen::updateKeys() // we only care about the modifier key states. other keys and the // mouse buttons should be up. + // sometimes these seem to be out of date so, to avoid getting + // locked to a screen unexpectedly, just assume the non-toggle + // modifier keys are up. +/* m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT) & 0x80); m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT) & 0x80); m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT) & 0x80); @@ -1513,6 +1517,7 @@ CMSWindowsPrimaryScreen::updateKeys() m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN) & 0x80); m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN) & 0x80); m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS) & 0x80); +*/ m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); @@ -1523,6 +1528,13 @@ CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press) { if (press) { switch (vkCode) { + case 0: + case VK_LBUTTON: + case VK_MBUTTON: + case VK_RBUTTON: + // ignore bogus key + break; + case VK_LSHIFT: case VK_RSHIFT: case VK_SHIFT: @@ -1558,9 +1570,31 @@ CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press) m_keys[vkCode] |= 0x80; break; } + + // special case: we detect ctrl+alt+del being pressed on some + // systems but we don't detect the release of those keys. so + // if ctrl, alt, and del are down then mark them up. + if ((m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0 && + (m_keys[VK_DELETE] & 0x80) != 0) { + m_keys[VK_LCONTROL] &= ~0x80; + m_keys[VK_RCONTROL] &= ~0x80; + m_keys[VK_CONTROL] &= ~0x80; + m_keys[VK_LMENU] &= ~0x80; + m_keys[VK_RMENU] &= ~0x80; + m_keys[VK_MENU] &= ~0x80; + m_keys[VK_DELETE] &= ~0x80; + } } else { switch (vkCode) { + case 0: + case VK_LBUTTON: + case VK_MBUTTON: + case VK_RBUTTON: + // ignore bogus key + break; + case VK_LSHIFT: case VK_RSHIFT: case VK_SHIFT: diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index d75257c5..5facb08b 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -508,6 +508,7 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) // if the desktop was inaccessible then notify the // event handler of that. if (m_inaccessibleDesktop) { + LOG((CLOG_INFO "desktop is now accessible")); m_inaccessibleDesktop = false; m_eventHandler->onAccessibleDesktop(); } @@ -515,6 +516,7 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) else if (!m_inaccessibleDesktop) { // the desktop has become inaccessible m_inaccessibleDesktop = true; + LOG((CLOG_INFO "desktop is now inaccessible")); } } From af24ae6db7ebce0a1573c98a224b6971bb5a16db Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 26 May 2003 09:49:38 +0000 Subject: [PATCH 520/807] No longer installing clibboard format for plain text on windows nt family because nt automatically converts to and from the unicode format. This may fix text encoding errors when synergy puts non-ascii text on the clipboard and other clients prefer CF_TEXT to CF_UNICODE (which they should not because synergy lists CF_UNICODE first). --- lib/platform/CMSWindowsClipboard.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp index 7df5d61f..1f3fd408 100644 --- a/lib/platform/CMSWindowsClipboard.cpp +++ b/lib/platform/CMSWindowsClipboard.cpp @@ -16,6 +16,7 @@ #include "CMSWindowsClipboardTextConverter.h" #include "CMSWindowsClipboardUTF16Converter.h" #include "CLog.h" +#include "CArchMiscWindows.h" // // CMSWindowsClipboard @@ -29,7 +30,11 @@ CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : { // add converters, most desired first m_converters.push_back(new CMSWindowsClipboardUTF16Converter); - m_converters.push_back(new CMSWindowsClipboardTextConverter); + if (CArchMiscWindows::isWindows95Family()) { + // windows nt family converts to/from unicode automatically. + // let it do so to avoid text encoding issues. + m_converters.push_back(new CMSWindowsClipboardTextConverter); + } } CMSWindowsClipboard::~CMSWindowsClipboard() From 5a65e36c992beda3882ebe0d68e758fa7c792257 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 26 May 2003 09:50:35 +0000 Subject: [PATCH 521/807] Added workaround for broken clipboard owners that report the type of TARGETS as TARGETS instead of ATOM. --- lib/platform/CXWindowsClipboard.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 6a9ced0f..de463546 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -488,12 +488,14 @@ CXWindowsClipboard::icccmFillCache() LOG((CLOG_DEBUG "ICCCM fill clipboard %d", m_id)); // see if we can get the list of available formats from the selection. - // if not then use a default list of formats. + // if not then use a default list of formats. note that some clipboard + // owners are broken and report TARGETS as the type of the TARGETS data + // instead of the correct type ATOM; allow either. const Atom atomTargets = m_atomTargets; Atom target; CString data; if (!icccmGetSelection(atomTargets, &target, &data) || - target != m_atomAtom) { + (target != m_atomAtom && target != m_atomTargets)) { LOG((CLOG_DEBUG1 "selection doesn't support TARGETS")); data = ""; From 7b58356fc744e0bba66a3ef019b833dfda4584c4 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Jun 2003 20:06:03 +0000 Subject: [PATCH 522/807] Added menu item on win32 tray icon to copy the last 1000 lines from the log to the clipboard. --- .../CMSWindowsClientTaskBarReceiver.cpp | 34 ++++++++++- .../CMSWindowsClientTaskBarReceiver.h | 7 ++- cmd/synergyc/resource.h | 1 + cmd/synergyc/synergyc.cpp | 10 +++- cmd/synergyc/synergyc.rc | 1 + .../CMSWindowsServerTaskBarReceiver.cpp | 34 ++++++++++- .../CMSWindowsServerTaskBarReceiver.h | 7 ++- cmd/synergys/resource.h | 3 +- cmd/synergys/synergys.cpp | 10 +++- cmd/synergys/synergys.rc | 1 + lib/base/CLog.cpp | 39 +++++++++++-- lib/base/CLog.h | 13 +++-- lib/base/LogOutputters.cpp | 57 +++++++++++++++++++ lib/base/LogOutputters.h | 39 +++++++++++++ lib/common/common.dsp | 4 ++ lib/common/stddeque.h | 17 ++++++ 16 files changed, 258 insertions(+), 19 deletions(-) create mode 100644 lib/common/stddeque.h diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp index f348a2a8..47561edd 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -14,6 +14,8 @@ #include "CMSWindowsClientTaskBarReceiver.h" #include "CClient.h" +#include "CMSWindowsClipboard.h" +#include "LogOutputters.h" #include "BasicTypes.h" #include "CArch.h" #include "CArchTaskBarWindows.h" @@ -32,10 +34,11 @@ static const UINT g_stateToIconID[CMSWindowsClientTaskBarReceiver::kMaxState] = // CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver( - HINSTANCE appInstance) : + HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : CClientTaskBarReceiver(), m_appInstance(appInstance), - m_window(NULL) + m_window(NULL), + m_logBuffer(logBuffer) { for (UInt32 i = 0; i < kMaxState; ++i) { m_icon[i] = loadIcon(g_stateToIconID[i]); @@ -149,6 +152,10 @@ CMSWindowsClientTaskBarReceiver::runMenu(int x, int y) showStatus(); break; + case IDC_TASKBAR_LOG: + copyLog(); + break; + case IDC_TASKBAR_QUIT: quit(); break; @@ -167,6 +174,29 @@ CMSWindowsClientTaskBarReceiver::getIcon() const return reinterpret_cast(m_icon[getState()]); } +void +CMSWindowsClientTaskBarReceiver::copyLog() const +{ + if (m_logBuffer != NULL) { + // collect log buffer + CString data; + for (CBufferedLogOutputter::const_iterator index = m_logBuffer->begin(); + index != m_logBuffer->end(); ++index) { + data += *index; + data += "\n"; + } + + // copy log to clipboard + if (!data.empty()) { + CMSWindowsClipboard clipboard(m_window); + clipboard.open(0); + clipboard.empty(); + clipboard.add(IClipboard::kText, data); + clipboard.close(); + } + } +} + void CMSWindowsClientTaskBarReceiver::onStatusChanged() { diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h index 0fdb2cc5..56a3f161 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h @@ -20,10 +20,12 @@ #include "CClientTaskBarReceiver.h" #include +class CBufferedLogOutputter; + //! Implementation of CClientTaskBarReceiver for Microsoft Windows class CMSWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { public: - CMSWindowsClientTaskBarReceiver(HINSTANCE); + CMSWindowsClientTaskBarReceiver(HINSTANCE, const CBufferedLogOutputter*); virtual ~CMSWindowsClientTaskBarReceiver(); // IArchTaskBarReceiver overrides @@ -33,6 +35,8 @@ public: virtual const Icon getIcon() const; protected: + void copyLog() const; + // CClientTaskBarReceiver overrides virtual void onStatusChanged(); @@ -53,6 +57,7 @@ private: HWND m_window; HMENU m_menu; HICON m_icon[kMaxState]; + const CBufferedLogOutputter* m_logBuffer; }; #endif diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h index d80c2389..1da78601 100644 --- a/cmd/synergyc/resource.h +++ b/cmd/synergyc/resource.h @@ -13,6 +13,7 @@ #define IDC_TASKBAR_STATUS_STATUS 1000 #define IDC_TASKBAR_QUIT 40003 #define IDC_TASKBAR_STATUS 40004 +#define IDC_TASKBAR_LOG 40005 // Next default values for new objects // diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index e1aabab0..7acd28bf 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -649,9 +649,14 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // send PRINT and FATAL output to a message box CLOG->insert(new CMessageBoxOutputter); + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + // make the task bar receiver. the user can control this app // through the task bar. - s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance); + s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance, + &logBuffer); s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); int result; @@ -670,6 +675,9 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // done with task bar receiver delete s_taskBarReceiver; + // done with log buffer + CLOG->remove(&logBuffer); + // let user examine any messages if we're running as a backend // by putting up a dialog box before exiting. if (ARG->m_backend && s_hasImportantLogMessages) { diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index 156651a4..9fd00206 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -84,6 +84,7 @@ BEGIN POPUP "Synergy" BEGIN MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG MENUITEM SEPARATOR MENUITEM "Quit", IDC_TASKBAR_QUIT END diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp index adcc132d..87f3d298 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -14,6 +14,8 @@ #include "CMSWindowsServerTaskBarReceiver.h" #include "CServer.h" +#include "CMSWindowsClipboard.h" +#include "LogOutputters.h" #include "BasicTypes.h" #include "CArch.h" #include "CArchTaskBarWindows.h" @@ -32,10 +34,11 @@ static const UINT g_stateToIconID[CMSWindowsServerTaskBarReceiver::kMaxState] = // CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver( - HINSTANCE appInstance) : + HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : CServerTaskBarReceiver(), m_appInstance(appInstance), - m_window(NULL) + m_window(NULL), + m_logBuffer(logBuffer) { for (UInt32 i = 0; i < kMaxState; ++i) { m_icon[i] = loadIcon(g_stateToIconID[i]); @@ -169,6 +172,10 @@ CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) showStatus(); break; + case IDC_TASKBAR_LOG: + copyLog(); + break; + case IDC_TASKBAR_QUIT: quit(); break; @@ -187,6 +194,29 @@ CMSWindowsServerTaskBarReceiver::getIcon() const return reinterpret_cast(m_icon[getState()]); } +void +CMSWindowsServerTaskBarReceiver::copyLog() const +{ + if (m_logBuffer != NULL) { + // collect log buffer + CString data; + for (CBufferedLogOutputter::const_iterator index = m_logBuffer->begin(); + index != m_logBuffer->end(); ++index) { + data += *index; + data += "\n"; + } + + // copy log to clipboard + if (!data.empty()) { + CMSWindowsClipboard clipboard(m_window); + clipboard.open(0); + clipboard.empty(); + clipboard.add(IClipboard::kText, data); + clipboard.close(); + } + } +} + void CMSWindowsServerTaskBarReceiver::onStatusChanged() { diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h index 372aad0b..d1c3dc65 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h @@ -20,10 +20,12 @@ #include "CServerTaskBarReceiver.h" #include +class CBufferedLogOutputter; + //! Implementation of CServerTaskBarReceiver for Microsoft Windows class CMSWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { public: - CMSWindowsServerTaskBarReceiver(HINSTANCE); + CMSWindowsServerTaskBarReceiver(HINSTANCE, const CBufferedLogOutputter*); virtual ~CMSWindowsServerTaskBarReceiver(); // IArchTaskBarReceiver overrides @@ -33,6 +35,8 @@ public: virtual const Icon getIcon() const; protected: + void copyLog() const; + // CServerTaskBarReceiver overrides virtual void onStatusChanged(); @@ -53,6 +57,7 @@ private: HWND m_window; HMENU m_menu; HICON m_icon[kMaxState]; + const CBufferedLogOutputter* m_logBuffer; }; #endif diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index 2e716a64..5ac24707 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -14,13 +14,14 @@ #define IDC_TASKBAR_STATUS_CLIENTS 1001 #define IDC_TASKBAR_QUIT 40003 #define IDC_TASKBAR_STATUS 40004 +#define IDC_TASKBAR_LOG 40005 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 109 -#define _APS_NEXT_COMMAND_VALUE 40005 +#define _APS_NEXT_COMMAND_VALUE 40006 #define _APS_NEXT_CONTROL_VALUE 1003 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 8cf18f88..e3fb74de 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -782,9 +782,14 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // send PRINT and FATAL output to a message box CLOG->insert(new CMessageBoxOutputter); + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + // make the task bar receiver. the user can control this app // through the task bar. - s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance); + s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance, + &logBuffer); s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); int result; @@ -803,6 +808,9 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // done with task bar receiver delete s_taskBarReceiver; + // done with log buffer + CLOG->remove(&logBuffer); + // let user examine any messages if we're running as a backend // by putting up a dialog box before exiting. if (ARG->m_backend && s_hasImportantLogMessages) { diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index 27e4484d..9f190754 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -70,6 +70,7 @@ BEGIN POPUP "Synergy" BEGIN MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG MENUITEM SEPARATOR MENUITEM "Quit", IDC_TASKBAR_QUIT END diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index 6b504993..4df3058b 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -91,6 +91,10 @@ CLog::~CLog() index != m_outputters.end(); ++index) { delete *index; } + for (COutputterList::iterator index = m_alwaysOutputters.begin(); + index != m_alwaysOutputters.end(); ++index) { + delete *index; + } ARCH->closeMutex(m_mutex); s_log = NULL; } @@ -189,13 +193,18 @@ CLog::printt(const char* file, int line, const char* fmt, ...) const } void -CLog::insert(ILogOutputter* outputter) +CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) { assert(outputter != NULL); assert(outputter->getNewline() != NULL); CLogLock lock(m_mutex); - m_outputters.push_front(outputter); + if (alwaysAtHead) { + m_alwaysOutputters.push_front(outputter); + } + else { + m_outputters.push_front(outputter); + } int newlineLength = strlen(outputter->getNewline()); if (newlineLength > m_maxNewlineLength) { m_maxNewlineLength = newlineLength; @@ -207,15 +216,17 @@ CLog::remove(ILogOutputter* outputter) { CLogLock lock(m_mutex); m_outputters.remove(outputter); + m_alwaysOutputters.remove(outputter); } void -CLog::pop_front() +CLog::pop_front(bool alwaysAtHead) { CLogLock lock(m_mutex); - if (!m_outputters.empty()) { - delete m_outputters.front(); - m_outputters.pop_front(); + COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters; + if (!list->empty()) { + delete list->front(); + list->pop_front(); } } @@ -266,6 +277,22 @@ CLog::output(int priority, char* msg) const // write to each outputter CLogLock lock(m_mutex); + for (COutputterList::const_iterator index = m_alwaysOutputters.begin(); + index != m_alwaysOutputters.end(); + ++index) { + // get outputter + ILogOutputter* outputter = *index; + + // put an appropriate newline at the end + strcat(msg + g_priorityPad, outputter->getNewline()); + + // open the outputter + outputter->open(kApplication); + + // write message + outputter->write(static_cast(priority), + msg + g_maxPriorityLength - n); + } for (COutputterList::const_iterator index = m_outputters.begin(); index != m_outputters.end(); ++index) { // get outputter diff --git a/lib/base/CLog.h b/lib/base/CLog.h index 88feea92..b25e0ba6 100644 --- a/lib/base/CLog.h +++ b/lib/base/CLog.h @@ -61,12 +61,15 @@ public: true then it also goes to the next outputter, as so on until an outputter returns false or there are no more outputters. Outputters still in the outputter list when the log is destroyed will be - deleted. + deleted. If \c alwaysAtHead is true then the outputter is always + called before all outputters with \c alwaysAtHead false and the + return value of the outputter is ignored. By default, the logger has one outputter installed which writes to the console. */ - void insert(ILogOutputter* adopted); + void insert(ILogOutputter* adopted, + bool alwaysAtHead = false); //! Remove an outputter from the list /*! @@ -79,9 +82,10 @@ public: //! Remove the outputter from the head of the list /*! Removes and deletes the outputter at the head of the outputter list. - This does nothing if the outputter list is empty. + This does nothing if the outputter list is empty. Only removes + outputters that were inserted with the matching \c alwaysAtHead. */ - void pop_front(); + void pop_front(bool alwaysAtHead = false); //! Set the minimum priority filter. /*! @@ -132,6 +136,7 @@ private: CArchMutex m_mutex; COutputterList m_outputters; + COutputterList m_alwaysOutputters; int m_maxNewlineLength; int m_maxPriority; }; diff --git a/lib/base/LogOutputters.cpp b/lib/base/LogOutputters.cpp index f8e0b65e..5f7a40ba 100644 --- a/lib/base/LogOutputters.cpp +++ b/lib/base/LogOutputters.cpp @@ -179,3 +179,60 @@ CSystemLogger::~CSystemLogger() delete m_stop; delete m_syslog; } + + +// +// CBufferedLogOutputter +// + +CBufferedLogOutputter::CBufferedLogOutputter(UInt32 maxBufferSize) : + m_maxBufferSize(maxBufferSize) +{ + // do nothing +} + +CBufferedLogOutputter::~CBufferedLogOutputter() +{ + // do nothing +} + +CBufferedLogOutputter::const_iterator +CBufferedLogOutputter::begin() const +{ + return m_buffer.begin(); +} + +CBufferedLogOutputter::const_iterator +CBufferedLogOutputter::end() const +{ + return m_buffer.end(); +} + +void +CBufferedLogOutputter::open(const char*) +{ + // do nothing +} + +void +CBufferedLogOutputter::close() +{ + // remove all elements from the buffer + m_buffer.clear(); +} + +bool +CBufferedLogOutputter::write(ELevel, const char* message) +{ + while (m_buffer.size() >= m_maxBufferSize) { + m_buffer.pop_front(); + } + m_buffer.push_back(CString(message)); + return true; +} + +const char* +CBufferedLogOutputter::getNewline() const +{ + return ""; +} diff --git a/lib/base/LogOutputters.h b/lib/base/LogOutputters.h index 21602438..64befe5f 100644 --- a/lib/base/LogOutputters.h +++ b/lib/base/LogOutputters.h @@ -15,7 +15,10 @@ #ifndef LOGOUTPUTTERS_H #define LOGOUTPUTTERS_H +#include "BasicTypes.h" #include "ILogOutputter.h" +#include "CString.h" +#include "stddeque.h" //! Stop traversing log chain outputter /*! @@ -86,4 +89,40 @@ private: ILogOutputter* m_stop; }; +//! Save log history +/*! +This outputter records the last N log messages. +*/ +class CBufferedLogOutputter : public ILogOutputter { +private: + typedef std::deque CBuffer; + +public: + typedef CBuffer::const_iterator const_iterator; + + CBufferedLogOutputter(UInt32 maxBufferSize); + virtual ~CBufferedLogOutputter(); + + //! @name accessors + //@{ + + //! Get start of buffer + const_iterator begin() const; + + //! Get end of buffer + const_iterator end() const; + + //@} + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; + +private: + UInt32 m_maxBufferSize; + CBuffer m_buffer; +}; + #endif diff --git a/lib/common/common.dsp b/lib/common/common.dsp index df6b77b4..38e1a686 100644 --- a/lib/common/common.dsp +++ b/lib/common/common.dsp @@ -107,6 +107,10 @@ SOURCE=.\stdbitset.h # End Source File # Begin Source File +SOURCE=.\stddeque.h +# End Source File +# Begin Source File + SOURCE=.\stdfstream.h # End Source File # Begin Source File diff --git a/lib/common/stddeque.h b/lib/common/stddeque.h new file mode 100644 index 00000000..06bdafd1 --- /dev/null +++ b/lib/common/stddeque.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stdpre.h" +#include +#include "stdpost.h" From c70ca5fbff4e2f1e48505faba4b32c2904987167 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Jun 2003 20:06:20 +0000 Subject: [PATCH 523/807] Fixed errors in log strings. --- lib/client/CServerProxy.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 3421bd60..d174e4cd 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -538,7 +538,7 @@ CServerProxy::keyRepeat() static_cast(mask)); if (id2 != static_cast(id) || mask2 != static_cast(mask)) - LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + LOG((CLOG_DEBUG1 "key repeat translated to id=%d, mask=0x%04x", id2, mask2)); // forward getClient()->keyRepeat(id2, mask2, count, button); @@ -561,7 +561,7 @@ CServerProxy::keyUp() static_cast(mask)); if (id2 != static_cast(id) || mask2 != static_cast(mask)) - LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + LOG((CLOG_DEBUG1 "key up translated to id=%d, mask=0x%04x", id2, mask2)); // forward getClient()->keyUp(id2, mask2, button); From 7464e99ba7dd4ddfa0c9a74c4ac04e539aeafade Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 2 Jun 2003 20:07:16 +0000 Subject: [PATCH 524/807] Fixed ctrl and alt keys on win32 clients. Was broken by a recent fix to character handling. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index b7c61b57..8b2658b1 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -1030,7 +1030,7 @@ CMSWindowsSecondaryScreen::mapCharacter(Keystrokes& keys, // strip them both out. if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) == (KeyModifierControl | KeyModifierAlt)) { - outMask &= ~(KeyModifierControl | KeyModifierAlt); + desiredMask &= ~(KeyModifierControl | KeyModifierAlt); } // strip out the desired shift state. we're forced to use @@ -1049,6 +1049,14 @@ CMSWindowsSecondaryScreen::mapCharacter(Keystrokes& keys, outMask |= KeyModifierAlt; } + // use desired modifiers + if ((desiredMask & KeyModifierControl) != 0) { + outMask |= KeyModifierControl; + } + if ((desiredMask & KeyModifierAlt) != 0) { + outMask |= KeyModifierAlt; + } + // handle combination of caps-lock and shift. if caps-lock is // off locally then use shift as necessary. if caps-lock is on // locally then it reverses the meaning of shift for keys that From f35a4541ee5c8e61c5435348251cf8c4ac362c19 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 8 Jun 2003 15:42:05 +0000 Subject: [PATCH 525/807] Added new file to Makefile. --- lib/common/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index 889af930..cfaafeb0 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -22,6 +22,7 @@ EXTRA_DIST = \ Version.h \ common.h \ stdbitset.h \ + stddeque.h \ stdfstream.h \ stdistream.h \ stdlist.h \ From 921526ab56436570c7216df0e1f051560a76396e Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 8 Jun 2003 16:31:52 +0000 Subject: [PATCH 526/807] More DEBUG2 level debugging of keyboard handling. --- lib/platform/CXWindowsSecondaryScreen.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index cb48ddd9..186065b5 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -682,6 +682,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // get the keysym we're trying to generate and possible keycodes KeySym keysym = keyIndex->first; const KeyCodeMask& entry = keyIndex->second; + LOG((CLOG_DEBUG2 "keysym is 0x%08x", keysym)); // we can choose any of the available keycode/modifier states to // generate our keysym. the most desireable is the one most @@ -701,6 +702,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // if mode switch is active then 2 and 3 are better than 0 and 1 if (getBits(desired, m_modeSwitchMask) != 0) { + LOG((CLOG_DEBUG2 "mode switch desired")); index[0] ^= 2; index[1] ^= 2; index[2] ^= 2; @@ -718,7 +720,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, if (adjustForNumLock(keysym)) { LOG((CLOG_DEBUG2 "num lock sensitive")); if (m_numLockMask != 0) { - LOG((CLOG_DEBUG2 "we have num lock")); + LOG((CLOG_DEBUG2 "we have a num lock")); if (getBits(desired, m_numLockMask) != 0) { LOG((CLOG_DEBUG2 "num lock desired, invert shift")); invertShift = true; @@ -728,7 +730,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, else if (adjustForCapsLock(keysym)) { LOG((CLOG_DEBUG2 "caps lock sensitive")); if (m_capsLockMask != 0) { - LOG((CLOG_DEBUG2 "we have caps lock")); + LOG((CLOG_DEBUG2 "we have a caps lock")); if (getBits(desired, m_capsLockMask) != 0) { LOG((CLOG_DEBUG2 "caps lock desired, invert shift")); invertShift = true; @@ -736,6 +738,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, } } if (desireShift != invertShift) { + LOG((CLOG_DEBUG2 "shift desired")); index[0] ^= 1; index[1] ^= 1; index[2] ^= 1; @@ -752,10 +755,12 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, bestIndex = index[bestIndex]; break; } + LOG((CLOG_DEBUG2 "skip index %d:%d because no mode-switch", bestIndex, index[bestIndex])); } } if (bestIndex == 4) { // no keycode/modifiers to generate the keysym + LOG((CLOG_DEBUG2 "no keycode for keysym")); return m_mask; } @@ -767,6 +772,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, ModifierMap::const_iterator modIndex = m_keycodeToModifier.find(keycode); unsigned int modifierBit = 0; if (modIndex != m_keycodeToModifier.end()) { + LOG((CLOG_DEBUG2 "keysym is modifier %d", modIndex->second)); modifierBit = (1 << modIndex->second); } From 784ab183aed65efe1a305fcdf357dfa16000b49e Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 8 Jun 2003 22:12:12 +0000 Subject: [PATCH 527/807] ctrl+alt+del emulation checkpoint. --- lib/platform/CMSWindowsPrimaryScreen.cpp | 24 +++++++-- lib/platform/CMSWindowsSecondaryScreen.cpp | 59 ++++++++++++++++++++++ lib/platform/CMSWindowsSecondaryScreen.h | 6 +++ lib/platform/CXWindowsPrimaryScreen.cpp | 26 +++++++++- lib/platform/CXWindowsSecondaryScreen.cpp | 11 ++++ 5 files changed, 119 insertions(+), 7 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 8734e540..232561ed 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -521,15 +521,29 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) case SYNERGY_MSG_KEY: // ignore message if posted prior to last mark change if (!ignore()) { + WPARAM wParam = msg->wParam; + LPARAM lParam = msg->lParam; + + // check for ctrl+alt+del emulation + if ((wParam == VK_PAUSE || wParam == VK_CANCEL) && + (m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0) { + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + wParam = VK_DELETE; + lParam &= 0xffff0000; + lParam |= 0x00000001; + } + + // process key normally KeyModifierMask mask; - const KeyID key = mapKey(msg->wParam, msg->lParam, &mask); + const KeyID key = mapKey(wParam, lParam, &mask); KeyButton button = static_cast( - (msg->lParam & 0x00ff0000u) >> 16); + (lParam & 0x00ff0000u) >> 16); if (key != kKeyNone && key != kKeyMultiKey) { - if ((msg->lParam & 0x80000000) == 0) { + if ((lParam & 0x80000000) == 0) { // key press - const bool wasDown = ((msg->lParam & 0x40000000) != 0); - const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); + const bool wasDown = ((lParam & 0x40000000) != 0); + const SInt32 repeat = (SInt32)(lParam & 0xffff); if (repeat >= 2 || wasDown) { LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button)); m_receiver->onKeyRepeat(key, mask, repeat, button); diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index 8b2658b1..e8dfe395 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -16,6 +16,8 @@ #include "CMSWindowsScreen.h" #include "XScreen.h" #include "CLock.h" +#include "CThread.h" +#include "CFunctionJob.h" #include "CLog.h" #include "CArchMiscWindows.h" #include @@ -94,6 +96,14 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key, CLock lock(&m_mutex); m_screen->syncDesktop(); + // check for ctrl+alt+del emulation + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + synthesizeCtrlAltDel(); + return; + } + // get the sequence of keys to simulate key press and the final // modifier state. m_mask = mapKey(keys, virtualKey, key, mask, kPress); @@ -1517,3 +1527,52 @@ CMSWindowsSecondaryScreen::getCodePageFromLangID(LANGID langid) const return codePage; } + +void +CMSWindowsSecondaryScreen::synthesizeCtrlAltDel() +{ + LOG((CLOG_DEBUG "emulating ctrl+alt+del")); + if (!m_is95Family) { + // to fake ctrl+alt+del on the NT family we broadcast a suitable + // hotkey to all windows on the winlogon desktop. however, we + // the current thread must be on that desktop to do the broadcast + // and we can't switch just any thread because some own windows + // or hooks. so start a new thread to do the real work. + CThread cad(new CFunctionJob( + &CMSWindowsSecondaryScreen::ctrlAltDelThread)); + cad.wait(); + } + else { + Keystrokes keys; + UINT virtualKey; + KeyID key = kKeyDelete; + KeyModifierMask mask = KeyModifierControl | KeyModifierAlt; + + // get the sequence of keys to simulate ctrl+alt+del + mapKey(keys, virtualKey, key, mask, kPress); + if (!keys.empty()) { + // generate key events + doKeystrokes(keys, 1); + } + } +} + +void +CMSWindowsSecondaryScreen::ctrlAltDelThread(void*) +{ + // get the Winlogon desktop at whatever privilege we can + HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED); + if (desk != NULL) { + if (SetThreadDesktop(desk)) { + PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, + MAKELPARAM(MOD_CONTROL | MOD_ALT, VK_DELETE)); + } + else { + LOG((CLOG_DEBUG "can't switch to Winlogon desk: %d", GetLastError())); + } + CloseDesktop(desk); + } + else { + LOG((CLOG_DEBUG "can't open Winlogon desk: %d", GetLastError())); + } +} diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index 7f97d3ce..f56c7c40 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -122,6 +122,12 @@ private: UINT getCodePageFromLangID(LANGID) const; + // generate a fake ctrl+alt+del + void synthesizeCtrlAltDel(); + + // thread that generates fake ctrl+alt+del + static void ctrlAltDelThread(void*); + private: CMutex m_mutex; CMSWindowsScreen* m_screen; diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp index 857ad119..b19b412a 100644 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -239,8 +239,18 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) { LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(&xevent.xkey); + KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { + // check for ctrl+alt+del emulation + if ((key == kKeyPause || key == kKeyCancel) && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + // pretend it's ctrl+alt+del + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + key = kKeyDelete; + } + + // handle key m_receiver->onKeyDown(key, mask, static_cast(xevent.xkey.keycode)); if (key == kKeyCapsLock && m_capsLockHalfDuplex) { @@ -258,7 +268,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) case KeyRelease: { const KeyModifierMask mask = mapModifier(xevent.xkey.state); - const KeyID key = mapKey(&xevent.xkey); + KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { // check if this is a key repeat by getting the next // KeyPress event that has the same key and time as @@ -279,6 +289,18 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) &CXWindowsPrimaryScreen::findKeyEvent, (XPointer)&filter) == True); } + + // check for ctrl+alt+del emulation + if ((key == kKeyPause || key == kKeyCancel) && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + // pretend it's ctrl+alt+del and ignore autorepeat + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + key = kKeyDelete; + hasPress = false; + } + + if (!hasPress) { // no press event follows so it's a plain release LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 186065b5..542f1528 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -117,6 +117,17 @@ CXWindowsSecondaryScreen::keyDown(KeyID key, Keystrokes keys; KeyCode keycode; + // check for ctrl+alt+del emulation + if ((mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt) && + key == XK_Up - 0xff00 + 0xef00) { + // just convert the key to Delete and synthesize the key + // normally. the X server/window manager will do the right + // thing (or, at least XFree86/KDE will). + LOG((CLOG_DEBUG "ctrl+alt+del emulation")); + key = XK_Delete - 0xff00u + 0xef00u; + } + // get the sequence of keys to simulate key press and the final // modifier state. m_mask = mapKey(keys, keycode, key, mask, kPress); From 5ca0e026ab2204ebd7474d2f61ae61dfeae85bb0 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 8 Jun 2003 22:20:01 +0000 Subject: [PATCH 528/807] Another ctrl+alt+del checkpoint. --- lib/platform/CXWindowsPrimaryScreen.cpp | 4 ++-- lib/platform/CXWindowsSecondaryScreen.cpp | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp index b19b412a..10ddc6eb 100644 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -242,7 +242,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) KeyID key = mapKey(&xevent.xkey); if (key != kKeyNone) { // check for ctrl+alt+del emulation - if ((key == kKeyPause || key == kKeyCancel) && + if ((key == kKeyPause || key == kKeyBreak) && (mask & (KeyModifierControl | KeyModifierAlt)) == (KeyModifierControl | KeyModifierAlt)) { // pretend it's ctrl+alt+del @@ -291,7 +291,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) } // check for ctrl+alt+del emulation - if ((key == kKeyPause || key == kKeyCancel) && + if ((key == kKeyPause || key == kKeyBreak) && (mask & (KeyModifierControl | KeyModifierAlt)) == (KeyModifierControl | KeyModifierAlt)) { // pretend it's ctrl+alt+del and ignore autorepeat diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 542f1528..53145883 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -118,14 +118,11 @@ CXWindowsSecondaryScreen::keyDown(KeyID key, KeyCode keycode; // check for ctrl+alt+del emulation - if ((mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt) && - key == XK_Up - 0xff00 + 0xef00) { - // just convert the key to Delete and synthesize the key - // normally. the X server/window manager will do the right - // thing (or, at least XFree86/KDE will). + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { LOG((CLOG_DEBUG "ctrl+alt+del emulation")); - key = XK_Delete - 0xff00u + 0xef00u; + // just pass the key through } // get the sequence of keys to simulate key press and the final @@ -214,6 +211,14 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, return; } + // check for ctrl+alt+del emulation + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + LOG((CLOG_DEBUG "ctrl+alt+del emulation")); + // just pass the key through + } + // get the sequence of keys to simulate key release and the final // modifier state. m_mask = mapKey(keys, keycode, key, mask, kRelease); From 92539f2ccc8b6c015ab9929033103cdf44c379d7 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 22 Jun 2003 15:01:44 +0000 Subject: [PATCH 529/807] Checkpoint for improving X11 client key handling. Should prevent unintentional Pointer_EnableKeys (i.e. generating NumLock press and release around a shift press). --- lib/platform/CXWindowsSecondaryScreen.cpp | 442 ++++++++++++---------- lib/platform/CXWindowsSecondaryScreen.h | 15 +- 2 files changed, 253 insertions(+), 204 deletions(-) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 53145883..b509ed47 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -700,65 +700,120 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, const KeyCodeMask& entry = keyIndex->second; LOG((CLOG_DEBUG2 "keysym is 0x%08x", keysym)); + // note if the key is a modifier + unsigned int modifierBit; + unsigned int modifierIndex = keySymToModifierIndex(keysym); + if (modifierIndex != static_cast(-1)) { + LOG((CLOG_DEBUG2 "keysym is modifier %d", modifierIndex)); + modifierBit = (1 << modifierIndex); + } + else { + modifierBit = 0; + } + + // if the key is a modifier and that modifier is already in the + // desired state then ignore the request since there's nothing + // to do. never ignore a toggle modifier on press or release, + // though. + if (modifierBit != 0) { + if (action == kRepeat) { + LOG((CLOG_DEBUG2 "ignore repeating modifier")); + return m_mask; + } + if (getBits(m_toggleModifierMask, modifierBit) == 0) { + if ((action == kPress && (m_mask & modifierBit) != 0) || + (action == kRelease && (m_mask & modifierBit) == 0)) { + LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", m_mask)); + return m_mask; + } + } + } + + // sensitive notes the modifier keys that affect the synthesized + // key event. these modifiers must be in the expected state to + // get the correct keysym and we'll only try to match these + // modifiers. + // + // the shift and mode switch keys can modify any keycode. num + // lock and caps lock only affect certain keysyms and if a + // keysym is affected by num lock it is not affected by caps + // lock. no other modifiers have any effect. + // + // requested notes the modifiers requested by the server and + // desired notes the modifier state we ultimately want to match. + // only the bits in desired indicated by sensitive are relevant. + // we assign the num lock and caps lock bits here if relevant. + // we'll assign shift and mode switch later. + unsigned int sensitive = ShiftMask | m_modeSwitchMask; + unsigned int requested = maskToX(mask); + unsigned int desired = 0; + if (adjustForNumLock(keysym)) { + sensitive |= m_numLockMask; + desired = assignBits(desired, m_numLockMask, requested); + } + else if (adjustForCapsLock(keysym)) { + sensitive |= m_capsLockMask; + desired = assignBits(desired, m_capsLockMask, requested); + } + + // we cannot be sensitive to the modifier we're pressing/releasing + sensitive = clearBits(sensitive, modifierBit); + // we can choose any of the available keycode/modifier states to // generate our keysym. the most desireable is the one most - // closely matching the input mask. determine the order we + // closely matching the current mask. determine the order we // should try modifier states, from best match to worst. this // doesn't concern itself with whether or not a given modifier // state has an associated keycode. we'll just skip those later - // if necessary. - - // default is none, shift, mode switch, shift + mode switch - unsigned int desired = maskToX(mask); + // if necessary. default is none, shift, mode switch, shift + + // mode switch. unsigned int index[4]; index[0] = 0; index[1] = 1; index[2] = 2; index[3] = 3; - // if mode switch is active then 2 and 3 are better than 0 and 1 - if (getBits(desired, m_modeSwitchMask) != 0) { - LOG((CLOG_DEBUG2 "mode switch desired")); - index[0] ^= 2; - index[1] ^= 2; - index[2] ^= 2; - index[3] ^= 2; - } - // if shift is active then 1 and 3 are better than 0 and 2. however, // if the key is affected by NumLock and NumLock is active then 1 and // 3 are better if shift is *not* down (because NumLock acts like // shift for those keysyms and shift cancels NumLock). similarly for - // keys affected by CapsLock. - bool desireShift = (getBits(desired, ShiftMask) != 0); + // keys affected by CapsLock. none of this is necessary if the key + // is itself shift. bool invertShift = false; - LOG((CLOG_DEBUG2 "desire shift: %s", desireShift ? "yes" : "no")); - if (adjustForNumLock(keysym)) { - LOG((CLOG_DEBUG2 "num lock sensitive")); - if (m_numLockMask != 0) { - LOG((CLOG_DEBUG2 "we have a num lock")); - if (getBits(desired, m_numLockMask) != 0) { - LOG((CLOG_DEBUG2 "num lock desired, invert shift")); + if (modifierBit != ShiftMask) { + bool desireShift = (getBits(m_mask, ShiftMask) != 0); + if ((sensitive & m_numLockMask) != 0) { + LOG((CLOG_DEBUG2 "num lock sensitive")); + if (getBits(m_mask, m_numLockMask) != 0) { + LOG((CLOG_DEBUG2 "num lock preferred, invert shift")); invertShift = true; } } - } - else if (adjustForCapsLock(keysym)) { - LOG((CLOG_DEBUG2 "caps lock sensitive")); - if (m_capsLockMask != 0) { - LOG((CLOG_DEBUG2 "we have a caps lock")); - if (getBits(desired, m_capsLockMask) != 0) { - LOG((CLOG_DEBUG2 "caps lock desired, invert shift")); + else if ((sensitive & m_capsLockMask) != 0) { + LOG((CLOG_DEBUG2 "caps lock sensitive")); + if (getBits(m_mask, m_capsLockMask) != 0) { + LOG((CLOG_DEBUG2 "caps lock preferred, invert shift")); invertShift = true; } } + if (desireShift != invertShift) { + LOG((CLOG_DEBUG2 "shift preferred")); + index[0] ^= 1; + index[1] ^= 1; + index[2] ^= 1; + index[3] ^= 1; + } } - if (desireShift != invertShift) { - LOG((CLOG_DEBUG2 "shift desired")); - index[0] ^= 1; - index[1] ^= 1; - index[2] ^= 1; - index[3] ^= 1; + + // if mode switch is active then 2 and 3 are better than 0 and 1, + // unless the key is itself mode switch. + if (modifierBit != m_modeSwitchMask && + getBits(m_mask, m_modeSwitchMask) != 0) { + LOG((CLOG_DEBUG2 "mode switch preferred")); + index[0] ^= 2; + index[1] ^= 2; + index[2] ^= 2; + index[3] ^= 2; } // find the first modifier state with a keycode we can generate. @@ -784,37 +839,13 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, keycode = entry.m_keycode[bestIndex]; LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode)); - // note if the key is a modifier - ModifierMap::const_iterator modIndex = m_keycodeToModifier.find(keycode); - unsigned int modifierBit = 0; - if (modIndex != m_keycodeToModifier.end()) { - LOG((CLOG_DEBUG2 "keysym is modifier %d", modIndex->second)); - modifierBit = (1 << modIndex->second); - } - - // if the key is a modifier and that modifier is already in the - // desired state then ignore the request since there's nothing - // to do. never ignore a toggle modifier on press or release, - // though. - if (modifierBit != 0) { - if (action == kRepeat) { - LOG((CLOG_DEBUG2 "ignore repeating modifier")); - return m_mask; - } - if (getBits(m_toggleModifierMask, modifierBit) == 0) { - if ((action == kPress && (m_mask & modifierBit) != 0) || - (action == kRelease && (m_mask & modifierBit) == 0)) { - LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", m_mask)); - return m_mask; - } - } - } + // FIXME -- can remove bits from sensitive if keycode doesn't have + // keysyms mapped to shift and/or mode switch. // bestIndex tells us if shift and mode switch should be on or off, // except if caps lock or num lock was down then we invert the sense // of bestIndex's lowest bit. // we must match both. - unsigned int required = ShiftMask | m_modeSwitchMask; if (((bestIndex & 1) == 0) != invertShift) { desired = clearBits(desired, ShiftMask); } @@ -828,28 +859,8 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, desired = setBits(desired, m_modeSwitchMask); } - // if the key is a modifier then remove it from the desired mask. - // we'll be matching the modifiers in the desired mask then adding - // a key press or release for the keysym. if we don't clear the - // modifier bit from the desired mask we'll end up dealing with - // that key twice, once while matching modifiers and once while - // handling the keysym. - // - // note that instead of clearing the bit, we make it identical to - // the same bit in m_mask, meaning it's already in the right state. - desired = assignBits(desired, modifierBit, m_mask); - required = clearBits(required, modifierBit); - LOG((CLOG_DEBUG2 "desired = 0x%04x, current = 0x%04x", desired, m_mask)); - - // some modifiers never have an effect on keysym lookup. leave - // those modifiers alone by copying their state from m_mask to - // desired. - desired = assignBits(desired, - ControlMask | - m_altMask | - m_metaMask | - m_superMask | - m_scrollLockMask, m_mask); + // we now know what modifiers we want + LOG((CLOG_DEBUG2 "modifiers: sensitive = 0x%04x, desired = 0x%04x, current = 0x%04x", sensitive, desired, m_mask)); // add the key events required to get to the modifier state // necessary to generate an event yielding id. also save the @@ -858,99 +869,100 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // modify modifiers. Keystrokes undo; Keystroke keystroke; - if (desired != m_mask) { - for (unsigned int i = 0; i < 8; ++i) { - unsigned int bit = (1 << i); - if (getBits(desired, bit) != getBits(m_mask, bit)) { - LOG((CLOG_DEBUG2 "fix modifier %d", i)); - // get the keycode we're using for this modifier. if - // there isn't one then bail if the modifier is required - // or ignore it if not required. - KeyCode modifierKey = m_modifierToKeycode[i]; - if (modifierKey == 0) { - LOG((CLOG_DEBUG2 "no key mapped to modifier 0x%04x", bit)); - if (getBits(required, bit) != 0) { - keys.clear(); - return m_mask; - } - else { - continue; - } - } + for (unsigned int i = 0; i < 8; ++i) { + // skip modifiers we don't care about + unsigned int bit = (1 << i); + if ((bit & sensitive) == 0) { + continue; + } - keystroke.m_keycode = modifierKey; - keystroke.m_repeat = false; - if (getBits(desired, bit)) { - // modifier is not active but should be. if the - // modifier is a toggle then toggle it on with a - // press/release, otherwise activate it with a - // press. use the first keycode for the modifier. - LOG((CLOG_DEBUG2 "modifier 0x%04x is not active", bit)); - if (getBits(m_toggleModifierMask, bit) != 0) { - LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); - if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || - (bit == m_numLockMask && m_numLockHalfDuplex)) { - keystroke.m_press = True; - keys.push_back(keystroke); - keystroke.m_press = False; - undo.push_back(keystroke); - } - else { - keystroke.m_press = True; - keys.push_back(keystroke); - keystroke.m_press = False; - keys.push_back(keystroke); - undo.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); - } - } - else { - keystroke.m_press = True; - keys.push_back(keystroke); - keystroke.m_press = False; - undo.push_back(keystroke); - } - } + // skip modifiers that are correct + if (getBits(desired, bit) == getBits(m_mask, bit)) { + continue; + } + LOG((CLOG_DEBUG2 "fix modifier %d", i)); + // get the keycode we're using for this modifier. if + // there isn't one then bail if the modifier is required + // or ignore it if not required. + KeyCode modifierKey = m_modifierToKeycode[i]; + if (modifierKey == 0) { + LOG((CLOG_DEBUG2 "no key mapped to modifier 0x%04x", bit)); + keys.clear(); + return m_mask; + } + + keystroke.m_keycode = modifierKey; + keystroke.m_repeat = false; + if (getBits(desired, bit)) { + // modifier is not active but should be. if the + // modifier is a toggle then toggle it on with a + // press/release, otherwise activate it with a + // press. use the first keycode for the modifier. + LOG((CLOG_DEBUG2 "modifier 0x%04x is not active but should be", bit)); + if (getBits(m_toggleModifierMask, bit) != 0) { + LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); + if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || + (bit == m_numLockMask && m_numLockHalfDuplex)) { + keystroke.m_press = True; + keys.push_back(keystroke); + keystroke.m_press = False; + undo.push_back(keystroke); + } else { - // modifier is active but should not be. if the - // modifier is a toggle then toggle it off with a - // press/release, otherwise deactivate it with a - // release. we must check each keycode for the - // modifier if not a toggle. - LOG((CLOG_DEBUG2 "modifier 0x%04x is active", bit)); - if (getBits(m_toggleModifierMask, bit) != 0) { - LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); - if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || - (bit == m_numLockMask && m_numLockHalfDuplex)) { - keystroke.m_press = False; - keys.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); - } - else { - keystroke.m_press = True; - keys.push_back(keystroke); - keystroke.m_press = False; - keys.push_back(keystroke); - undo.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); - } - } - else { - for (unsigned int j = 0; j < m_keysPerModifier; ++j) { - const KeyCode key = - m_modifierToKeycodes[i * m_keysPerModifier + j]; - if (key != 0 && m_keys[key]) { - keystroke.m_keycode = key; - keystroke.m_press = False; - keys.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); - } - } + keystroke.m_press = True; + keys.push_back(keystroke); + keystroke.m_press = False; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); + } + } + else { + keystroke.m_press = True; + keys.push_back(keystroke); + keystroke.m_press = False; + undo.push_back(keystroke); + } + } + + else { + // modifier is active but should not be. if the + // modifier is a toggle then toggle it off with a + // press/release, otherwise deactivate it with a + // release. we must check each keycode for the + // modifier if not a toggle. + LOG((CLOG_DEBUG2 "modifier 0x%04x is active", bit)); + if (getBits(m_toggleModifierMask, bit) != 0) { + LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); + if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || + (bit == m_numLockMask && m_numLockHalfDuplex)) { + keystroke.m_press = False; + keys.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); + } + else { + keystroke.m_press = True; + keys.push_back(keystroke); + keystroke.m_press = False; + keys.push_back(keystroke); + undo.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); + } + } + else { + for (unsigned int j = 0; j < m_keysPerModifier; ++j) { + const KeyCode key = + m_modifierToKeycodes[i * m_keysPerModifier + j]; + if (key != 0 && m_keys[key]) { + keystroke.m_keycode = key; + keystroke.m_press = False; + keys.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); } } } @@ -1016,7 +1028,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // scan those keys to see if any (except keycode) are pressed. bool down = false; for (unsigned int j = 0; !down && j < m_keysPerModifier; ++j) { - KeyCode modKeycode = m_modifierToKeycodes[modIndex->second * + KeyCode modKeycode = m_modifierToKeycodes[modifierIndex * m_keysPerModifier + j]; if (modKeycode != 0 && modKeycode != keycode) { down = m_keys[modKeycode]; @@ -1026,9 +1038,9 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, mask = clearBits(mask, modifierBit); } } + LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask)); } - LOG((CLOG_DEBUG2 "final mask: 0x%04x", mask)); return mask; } @@ -1307,26 +1319,6 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) XFree(keysyms); } -unsigned int -CXWindowsSecondaryScreen::indexToModifierMask(int index) const -{ - assert(index >= 0 && index <= 3); - - switch (index) { - case 0: - return 0; - - case 1: - return ShiftMask | LockMask; - - case 2: - return m_modeSwitchMask; - - case 3: - return ShiftMask | LockMask | m_modeSwitchMask; - } -} - void CXWindowsSecondaryScreen::updateModifierMap(Display* display) { @@ -1343,6 +1335,13 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display) m_numLockMask = 0; m_capsLockMask = 0; m_scrollLockMask = 0; + m_altIndex = static_cast(-1); + m_metaIndex = static_cast(-1); + m_superIndex = static_cast(-1); + m_modeSwitchIndex = static_cast(-1); + m_numLockIndex = static_cast(-1); + m_capsLockIndex = static_cast(-1); + m_scrollLockIndex = static_cast(-1); m_keysPerModifier = keymap->max_keypermod; m_modifierToKeycode.clear(); m_modifierToKeycode.resize(8); @@ -1368,9 +1367,6 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display) m_modifierToKeycode[i] = keycode; } - // save in keycode to modifier - m_keycodeToModifier.insert(std::make_pair(keycode, i)); - // save bit in all-modifiers mask m_modifierMask |= bit; @@ -1384,33 +1380,41 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display) switch (keysym) { case XK_Alt_L: case XK_Alt_R: + m_altIndex = i; m_altMask |= bit; break; case XK_Meta_L: case XK_Meta_R: + m_metaIndex = i; m_metaMask |= bit; break; case XK_Super_L: case XK_Super_R: + m_superIndex = i; m_superMask |= bit; break; case XK_Mode_switch: + m_modeSwitchIndex = i; m_modeSwitchMask |= bit; break; case XK_Num_Lock: + m_numLockIndex = i; m_numLockMask |= bit; break; case XK_Caps_Lock: + m_capsLockIndex = i; m_capsLockMask |= bit; break; case XK_Scroll_Lock: + m_scrollLockIndex = i; m_scrollLockMask |= bit; + break; } } } @@ -1418,6 +1422,46 @@ CXWindowsSecondaryScreen::updateModifierMap(Display* display) XFreeModifiermap(keymap); } +unsigned int +CXWindowsSecondaryScreen::keySymToModifierIndex(KeySym keysym) const +{ + switch (keysym) { + case XK_Shift_L: + case XK_Shift_R: + return 0; + + case XK_Control_L: + case XK_Control_R: + return 2; + + case XK_Alt_L: + case XK_Alt_R: + return m_altIndex; + + case XK_Meta_L: + case XK_Meta_R: + return m_metaIndex; + + case XK_Super_L: + case XK_Super_R: + return m_superIndex; + + case XK_Mode_switch: + return m_modeSwitchIndex; + + case XK_Num_Lock: + return m_numLockIndex; + + case XK_Caps_Lock: + return m_capsLockIndex; + + case XK_Scroll_Lock: + return m_scrollLockIndex; + } + + return static_cast(-1); +} + void CXWindowsSecondaryScreen::toggleKey(Display* display, KeySym keysym, unsigned int mask) diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index a1129ee5..94ebd60b 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -93,7 +93,6 @@ private: typedef std::vector KeyCodes; typedef std::map KeyCodeMap; typedef KeyCodeMap::const_iterator KeyCodeIndex; - typedef std::map ModifierMap; typedef std::map ServerKeyMap; unsigned int mapButton(ButtonID button) const; @@ -108,7 +107,7 @@ private: void updateKeycodeMap(Display* display); void updateModifiers(Display* display); void updateModifierMap(Display* display); - unsigned int indexToModifierMask(int index) const; + unsigned int keySymToModifierIndex(KeySym) const; void toggleKey(Display*, KeySym, unsigned int mask); static bool isToggleKeysym(KeySym); @@ -162,14 +161,20 @@ private: unsigned int m_capsLockMask; unsigned int m_scrollLockMask; + // modifier indices + unsigned int m_altIndex; + unsigned int m_metaIndex; + unsigned int m_superIndex; + unsigned int m_modeSwitchIndex; + unsigned int m_numLockIndex; + unsigned int m_capsLockIndex; + unsigned int m_scrollLockIndex; + // map X modifier key indices to the key codes bound to them unsigned int m_keysPerModifier; KeyCodes m_modifierToKeycode; KeyCodes m_modifierToKeycodes; - // maps keycodes to modifier indices - ModifierMap m_keycodeToModifier; - // map server key buttons to local keycodes ServerKeyMap m_serverKeyMap; From a6f21bff90231177a2a7bea2f40b64c8f0030cfe Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 22 Jun 2003 16:39:02 +0000 Subject: [PATCH 530/807] More fixes for X11 client keyboard handling. --- lib/platform/CXWindowsSecondaryScreen.cpp | 20 ++++++++++---------- lib/platform/CXWindowsSecondaryScreen.h | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index b509ed47..790d2a25 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -224,8 +224,10 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, m_mask = mapKey(keys, keycode, key, mask, kRelease); // if there are no keys to generate then we should at least generate - // a key release for the key we pressed. - if (keys.empty()) { + // a key release for the key we pressed. this does not apply to + // half-duplex modifiers. + if (keys.empty() &&!((key == kKeyCapsLock && m_capsLockHalfDuplex) || + (key == kKeyNumLock && m_numLockHalfDuplex))) { Keystroke keystroke; keycode = index->second; keystroke.m_keycode = keycode; @@ -686,9 +688,11 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, return m_mask; } - // convert the id to a keysym and adjust the mask if necessary - unsigned int outMask = m_mask; - KeyCodeIndex keyIndex = findKey(id, outMask); + // requested notes the modifiers requested by the server. + unsigned int requested = maskToX(mask); + + // convert the id to a keysym + KeyCodeIndex keyIndex = findKey(id, requested); if (keyIndex == noKey()) { // cannot convert id to keysym LOG((CLOG_DEBUG2 "no keysym for key")); @@ -739,13 +743,11 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // keysym is affected by num lock it is not affected by caps // lock. no other modifiers have any effect. // - // requested notes the modifiers requested by the server and // desired notes the modifier state we ultimately want to match. // only the bits in desired indicated by sensitive are relevant. // we assign the num lock and caps lock bits here if relevant. // we'll assign shift and mode switch later. unsigned int sensitive = ShiftMask | m_modeSwitchMask; - unsigned int requested = maskToX(mask); unsigned int desired = 0; if (adjustForNumLock(keysym)) { sensitive |= m_numLockMask; @@ -1553,7 +1555,7 @@ static const KeySym g_mapE000[] = #endif CXWindowsSecondaryScreen::KeyCodeIndex -CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask& mask) const +CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask mask) const { // convert id to keysym KeySym keysym = NoSymbol; @@ -1600,7 +1602,6 @@ CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask& mask) const // XK_ISO_Left_Tab sending events to secondary screens that do. if (keysym == XK_Tab && (mask & ShiftMask) != 0) { keysym = XK_ISO_Left_Tab; - mask &= ~ShiftMask; } // find the keycodes that generate the keysym @@ -1663,7 +1664,6 @@ CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask& mask) const case XK_ISO_Left_Tab: keysym = XK_Tab; - mask |= ShiftMask; break; default: diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index 94ebd60b..dd631a88 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -111,7 +111,7 @@ private: void toggleKey(Display*, KeySym, unsigned int mask); static bool isToggleKeysym(KeySym); - KeyCodeIndex findKey(KeyID keysym, KeyModifierMask& mask) const; + KeyCodeIndex findKey(KeyID keysym, KeyModifierMask mask) const; KeyCodeIndex noKey() const; bool adjustForNumLock(KeySym) const; bool adjustForCapsLock(KeySym) const; From 48965e23819a4b244a161776488a483a6b2426f9 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 22 Jun 2003 16:39:25 +0000 Subject: [PATCH 531/807] More fixes for X11 client keyboard handling. --- lib/client/CServerProxy.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index d174e4cd..435cdc55 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -404,7 +404,11 @@ CServerProxy::translateModifierMask(KeyModifierMask mask) const KeyModifierSuper }; - KeyModifierMask newMask = 0; + KeyModifierMask newMask = mask & ~(KeyModifierShift | + KeyModifierControl | + KeyModifierAlt | + KeyModifierMeta | + KeyModifierSuper); if ((mask & KeyModifierShift) != 0) { newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDShift]]; } From 1eab99d70c0b300757b5c187a6ead58c51344de7 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 22 Jun 2003 21:27:38 +0000 Subject: [PATCH 532/807] Added support for input methods. Only handling IMs that don't need a precompose area or status area. This includes IMs that do simple dead key composition. This only changes the server. The client still does not decompose a character it cannot generate directly into the keysyms to compose the character. --- lib/platform/CXWindowsPrimaryScreen.cpp | 116 +++++++++++++++++++++++- lib/platform/CXWindowsPrimaryScreen.h | 4 + 2 files changed, 116 insertions(+), 4 deletions(-) diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp index 10ddc6eb..e3538cef 100644 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -40,7 +40,9 @@ CXWindowsPrimaryScreen::CXWindowsPrimaryScreen( IPrimaryScreenReceiver* primaryReceiver) : CPrimaryScreen(receiver), m_receiver(primaryReceiver), - m_window(None) + m_window(None), + m_im(NULL), + m_ic(NULL) { m_screen = new CXWindowsScreen(receiver, this); } @@ -220,6 +222,11 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) assert(event != NULL); XEvent& xevent = event->m_event; + // let input methods try to handle event first + if (m_ic != NULL && XFilterEvent(&xevent, None)) { + return true; + } + // handle event switch (xevent.type) { case CreateNotify: @@ -455,18 +462,88 @@ CXWindowsPrimaryScreen::onPostOpen() // get cursor info m_screen->getCursorPos(m_x, m_y); m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // get the input method + CDisplayLock display(m_screen); + m_im = XOpenIM(display, NULL, NULL, NULL); + if (m_im == NULL) { + return; + } + + // find the appropriate style. synergy supports XIMPreeditNothing + // only at the moment. + XIMStyles* styles; + if (XGetIMValues(m_im, XNQueryInputStyle, &styles, NULL) != NULL || + styles == NULL) { + LOG((CLOG_WARN "cannot get IM styles")); + return; + } + XIMStyle style = 0; + for (unsigned short i = 0; i < styles->count_styles; ++i) { + style = styles->supported_styles[i]; + if ((style & XIMPreeditNothing) != 0) { + if ((style & (XIMStatusNothing | XIMStatusNone)) != 0) { + break; + } + } + } + XFree(styles); + if (style == 0) { + LOG((CLOG_WARN "no supported IM styles")); + return; + } + + // create an input context for the style and tell it about our window + m_ic = XCreateIC(m_im, XNInputStyle, style, XNClientWindow, m_window, NULL); + if (m_ic == NULL) { + LOG((CLOG_WARN "cannot create IC")); + return; + } + + // find out the events we must select for and do so + unsigned long mask; + if (XGetICValues(m_ic, XNFilterEvents, &mask, NULL) != NULL) { + LOG((CLOG_WARN "cannot get IC filter events")); + return; + } + XWindowAttributes attr; + XGetWindowAttributes(display, m_window, &attr); + XSelectInput(display, m_window, attr.your_event_mask | mask); +} + +void +CXWindowsPrimaryScreen::onPreClose() +{ + CDisplayLock display(m_screen); + if (m_ic != NULL) { + XDestroyIC(m_ic); + m_ic = NULL; + } + if (m_im != NULL) { + XCloseIM(m_im); + m_im = NULL; + } } void CXWindowsPrimaryScreen::onPreEnter() { assert(m_window != None); + + if (m_ic != NULL) { + XUnsetICFocus(m_ic); + } } void CXWindowsPrimaryScreen::onPreLeave() { assert(m_window != None); + + if (m_ic != NULL) { + XmbResetIC(m_ic); + XSetICFocus(m_ic); + } } void @@ -765,10 +842,41 @@ CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const CDisplayLock display(m_screen); // convert to a keysym - // FIXME -- we're not properly handling unicode KeySym keysym; - char dummy[1]; - XLookupString(event, dummy, 0, &keysym, NULL); + if (event->type == KeyPress && m_ic != NULL) { + // do multibyte lookup. can only call XmbLookupString with a + // key press event and a valid XIC so we checked those above. + char scratch[32]; + int n = sizeof(scratch) / sizeof(scratch[0]); + char* buffer = scratch; + int status; + n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status); + if (status == XBufferOverflow) { + // not enough space. grow buffer and try again. + buffer = new char[n]; + n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status); + delete[] buffer; + } + + // see what we got. since we don't care about the string + // we'll just look for a keysym. + switch (status) { + default: + case XLookupNone: + case XLookupChars: + keysym = 0; + break; + + case XLookupKeySym: + case XLookupBoth: + break; + } + } + else { + // plain old lookup + char dummy[1]; + XLookupString(event, dummy, 0, &keysym, NULL); + } // convert key switch (keysym & 0xffffff00) { diff --git a/lib/platform/CXWindowsPrimaryScreen.h b/lib/platform/CXWindowsPrimaryScreen.h index 5ac54356..828f73e6 100644 --- a/lib/platform/CXWindowsPrimaryScreen.h +++ b/lib/platform/CXWindowsPrimaryScreen.h @@ -57,6 +57,7 @@ protected: virtual void onPreMainLoop(); virtual void onPreOpen(); virtual void onPostOpen(); + virtual void onPreClose(); virtual void onPreEnter(); virtual void onPreLeave(); virtual void onEnterScreenSaver(); @@ -115,6 +116,9 @@ private: // position of center pixel of screen SInt32 m_xCenter, m_yCenter; + + XIM m_im; + XIC m_ic; }; #endif From 24fc257b3ccea4ea14c7cb47eb5e0a98caa3dcb1 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 1 Jul 2003 19:35:28 +0000 Subject: [PATCH 533/807] Rewrote key handling on X11 client. This should fix problems with applying the incorrect shift and mode switch modifiers to some keycodes, such as getting Pointer_EnableKeys when pressing shift with NumLock enabled. --- lib/platform/CXWindowsSecondaryScreen.cpp | 1547 +++++++++++++-------- lib/platform/CXWindowsSecondaryScreen.h | 140 +- 2 files changed, 1031 insertions(+), 656 deletions(-) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 790d2a25..734a58fc 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -28,6 +28,12 @@ # include # define XK_MISCELLANY # define XK_XKB_KEYS +# define XK_LATIN1 +# define XK_LATIN2 +# define XK_LATIN3 +# define XK_LATIN4 +# define XK_LATIN8 +# define XK_LATIN9 # include # if defined(HAVE_X11_EXTENSIONS_XTEST_H) # include @@ -96,12 +102,18 @@ unsigned int assignBits(unsigned int src, // CXWindowsSecondaryScreen // +CXWindowsSecondaryScreen::KeySymsMap + CXWindowsSecondaryScreen::s_decomposedKeySyms; + CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) : CSecondaryScreen(), m_window(None), m_xtestIsXineramaUnaware(true) { m_screen = new CXWindowsScreen(receiver, this); + + // make sure decomposed keysym table is prepared + getDecomposedKeySymTable(); } CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() @@ -210,6 +222,7 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, if (index == m_serverKeyMap.end()) { return; } + keycode = index->second; // check for ctrl+alt+del emulation if (key == kKeyDelete && @@ -219,37 +232,9 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, // just pass the key through } - // get the sequence of keys to simulate key release and the final - // modifier state. - m_mask = mapKey(keys, keycode, key, mask, kRelease); - - // if there are no keys to generate then we should at least generate - // a key release for the key we pressed. this does not apply to - // half-duplex modifiers. - if (keys.empty() &&!((key == kKeyCapsLock && m_capsLockHalfDuplex) || - (key == kKeyNumLock && m_numLockHalfDuplex))) { - Keystroke keystroke; - keycode = index->second; - keystroke.m_keycode = keycode; - keystroke.m_press = False; - keystroke.m_repeat = false; - keys.push_back(keystroke); - } - - // if we've seen this button (and we should have) then make sure - // we release the same key we pressed when we saw it. - if (index != m_serverKeyMap.end() && keycode != index->second) { - // replace key up with previous keycode - for (Keystrokes::iterator index2 = keys.begin(); - index2 != keys.end(); ++index2) { - if (index2->m_keycode == keycode) { - index2->m_keycode = index->second; - break; - } - } - - // use old keycode - keycode = index->second; + if (!((key == kKeyCapsLock && m_capsLockHalfDuplex) || + (key == kKeyNumLock && m_numLockHalfDuplex))) { + m_mask = mapKeyRelease(keys, keycode); } // generate key events @@ -606,15 +591,15 @@ CXWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) CDisplayLock display(m_screen); // toggle modifiers that don't match the desired state - unsigned int xMask = maskToX(mask); + ModifierMask xMask = maskToX(mask); if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) { - toggleKey(display, XK_Caps_Lock, m_capsLockMask); + toggleKey(display, m_capsLockKeysym, m_capsLockMask); } if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) { - toggleKey(display, XK_Num_Lock, m_numLockMask); + toggleKey(display, m_numLockKeysym, m_numLockMask); } if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { - toggleKey(display, XK_Scroll_Lock, m_scrollLockMask); + toggleKey(display, m_scrollLockKeysym, m_scrollLockMask); } } @@ -663,7 +648,7 @@ CXWindowsSecondaryScreen::mapButton(ButtonID id) const return static_cast(m_buttons[id - 1]); } -KeyModifierMask +CXWindowsSecondaryScreen::ModifierMask CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, KeyID id, KeyModifierMask mask, EKeyAction action) const { @@ -679,304 +664,322 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // keysym with that mask. we override the bits in the mask // that cannot be accomodated. - // note if the key is "half-duplex" + // ignore releases and repeats for half-duplex keys const bool isHalfDuplex = ((id == kKeyCapsLock && m_capsLockHalfDuplex) || (id == kKeyNumLock && m_numLockHalfDuplex)); - - // ignore releases and repeats for half-duplex keys if (isHalfDuplex && action != kPress) { return m_mask; } // requested notes the modifiers requested by the server. - unsigned int requested = maskToX(mask); + ModifierMask requested = maskToX(mask); - // convert the id to a keysym - KeyCodeIndex keyIndex = findKey(id, requested); - if (keyIndex == noKey()) { - // cannot convert id to keysym - LOG((CLOG_DEBUG2 "no keysym for key")); + // convert KeyID to a KeySym + KeySym keysym = keyIDToKeySym(id, requested); + if (keysym == NoSymbol) { + // unknown key + LOG((CLOG_DEBUG2 "no keysym for id 0x%08x", id)); return m_mask; } - // get the keysym we're trying to generate and possible keycodes - KeySym keysym = keyIndex->first; - const KeyCodeMask& entry = keyIndex->second; - LOG((CLOG_DEBUG2 "keysym is 0x%08x", keysym)); + // get the mapping for this keysym + KeySymIndex keyIndex = m_keysymMap.find(keysym); - // note if the key is a modifier - unsigned int modifierBit; - unsigned int modifierIndex = keySymToModifierIndex(keysym); - if (modifierIndex != static_cast(-1)) { - LOG((CLOG_DEBUG2 "keysym is modifier %d", modifierIndex)); - modifierBit = (1 << modifierIndex); - } - else { - modifierBit = 0; - } - - // if the key is a modifier and that modifier is already in the - // desired state then ignore the request since there's nothing - // to do. never ignore a toggle modifier on press or release, - // though. - if (modifierBit != 0) { - if (action == kRepeat) { - LOG((CLOG_DEBUG2 "ignore repeating modifier")); - return m_mask; + // if the mapping isn't found and keysym is caps lock sensitive + // then convert the case of the keysym and try again. + if (keyIndex == m_keysymMap.end()) { + KeySym lKey, uKey; + XConvertCase(keysym, &lKey, &uKey); + if (lKey != uKey) { + if (lKey == keysym) { + keyIndex = m_keysymMap.find(uKey); + } + else { + keyIndex = m_keysymMap.find(lKey); + } } - if (getBits(m_toggleModifierMask, modifierBit) == 0) { - if ((action == kPress && (m_mask & modifierBit) != 0) || - (action == kRelease && (m_mask & modifierBit) == 0)) { - LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", m_mask)); + } + + if (keyIndex != m_keysymMap.end()) { + // the keysym is mapped to some keycode. if it's a modifier + // and that modifier is already in the desired state then + // ignore the request since there's nothing to do. never + // ignore a toggle modifier on press or release, though. + const KeyMapping& keyMapping = keyIndex->second; + const ModifierMask modifierBit = keyMapping.m_modifierMask; + if (modifierBit != 0) { + if (action == kRepeat) { + LOG((CLOG_DEBUG2 "ignore repeating modifier")); return m_mask; } - } - } - - // sensitive notes the modifier keys that affect the synthesized - // key event. these modifiers must be in the expected state to - // get the correct keysym and we'll only try to match these - // modifiers. - // - // the shift and mode switch keys can modify any keycode. num - // lock and caps lock only affect certain keysyms and if a - // keysym is affected by num lock it is not affected by caps - // lock. no other modifiers have any effect. - // - // desired notes the modifier state we ultimately want to match. - // only the bits in desired indicated by sensitive are relevant. - // we assign the num lock and caps lock bits here if relevant. - // we'll assign shift and mode switch later. - unsigned int sensitive = ShiftMask | m_modeSwitchMask; - unsigned int desired = 0; - if (adjustForNumLock(keysym)) { - sensitive |= m_numLockMask; - desired = assignBits(desired, m_numLockMask, requested); - } - else if (adjustForCapsLock(keysym)) { - sensitive |= m_capsLockMask; - desired = assignBits(desired, m_capsLockMask, requested); - } - - // we cannot be sensitive to the modifier we're pressing/releasing - sensitive = clearBits(sensitive, modifierBit); - - // we can choose any of the available keycode/modifier states to - // generate our keysym. the most desireable is the one most - // closely matching the current mask. determine the order we - // should try modifier states, from best match to worst. this - // doesn't concern itself with whether or not a given modifier - // state has an associated keycode. we'll just skip those later - // if necessary. default is none, shift, mode switch, shift + - // mode switch. - unsigned int index[4]; - index[0] = 0; - index[1] = 1; - index[2] = 2; - index[3] = 3; - - // if shift is active then 1 and 3 are better than 0 and 2. however, - // if the key is affected by NumLock and NumLock is active then 1 and - // 3 are better if shift is *not* down (because NumLock acts like - // shift for those keysyms and shift cancels NumLock). similarly for - // keys affected by CapsLock. none of this is necessary if the key - // is itself shift. - bool invertShift = false; - if (modifierBit != ShiftMask) { - bool desireShift = (getBits(m_mask, ShiftMask) != 0); - if ((sensitive & m_numLockMask) != 0) { - LOG((CLOG_DEBUG2 "num lock sensitive")); - if (getBits(m_mask, m_numLockMask) != 0) { - LOG((CLOG_DEBUG2 "num lock preferred, invert shift")); - invertShift = true; + if (getBits(m_toggleModifierMask, modifierBit) == 0) { + if ((action == kPress && (m_mask & modifierBit) != 0) || + (action == kRelease && (m_mask & modifierBit) == 0)) { + LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", m_mask)); + return m_mask; + } } } - else if ((sensitive & m_capsLockMask) != 0) { - LOG((CLOG_DEBUG2 "caps lock sensitive")); - if (getBits(m_mask, m_capsLockMask) != 0) { - LOG((CLOG_DEBUG2 "caps lock preferred, invert shift")); - invertShift = true; - } - } - if (desireShift != invertShift) { - LOG((CLOG_DEBUG2 "shift preferred")); - index[0] ^= 1; - index[1] ^= 1; - index[2] ^= 1; - index[3] ^= 1; - } - } - // if mode switch is active then 2 and 3 are better than 0 and 1, - // unless the key is itself mode switch. - if (modifierBit != m_modeSwitchMask && - getBits(m_mask, m_modeSwitchMask) != 0) { - LOG((CLOG_DEBUG2 "mode switch preferred")); - index[0] ^= 2; - index[1] ^= 2; - index[2] ^= 2; - index[3] ^= 2; - } - - // find the first modifier state with a keycode we can generate. - // note that if m_modeSwitchMask is 0 then we can't generate - // m_keycode[2] and m_keycode[3]. - unsigned int bestIndex; - for (bestIndex = 0; bestIndex < 4; ++bestIndex) { - if (entry.m_keycode[index[bestIndex]] != 0) { - if (index[bestIndex] < 2 || m_modeSwitchMask != 0) { - bestIndex = index[bestIndex]; - break; - } - LOG((CLOG_DEBUG2 "skip index %d:%d because no mode-switch", bestIndex, index[bestIndex])); - } - } - if (bestIndex == 4) { - // no keycode/modifiers to generate the keysym - LOG((CLOG_DEBUG2 "no keycode for keysym")); - return m_mask; - } - - // get the keycode - keycode = entry.m_keycode[bestIndex]; - LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode)); - - // FIXME -- can remove bits from sensitive if keycode doesn't have - // keysyms mapped to shift and/or mode switch. - - // bestIndex tells us if shift and mode switch should be on or off, - // except if caps lock or num lock was down then we invert the sense - // of bestIndex's lowest bit. - // we must match both. - if (((bestIndex & 1) == 0) != invertShift) { - desired = clearBits(desired, ShiftMask); - } - else { - desired = setBits(desired, ShiftMask); - } - if ((bestIndex & 2) == 0) { - desired = clearBits(desired, m_modeSwitchMask); - } - else { - desired = setBits(desired, m_modeSwitchMask); - } - - // we now know what modifiers we want - LOG((CLOG_DEBUG2 "modifiers: sensitive = 0x%04x, desired = 0x%04x, current = 0x%04x", sensitive, desired, m_mask)); - - // add the key events required to get to the modifier state - // necessary to generate an event yielding id. also save the - // key events required to restore the state. if the key is - // a modifier key then skip this because modifiers should not - // modify modifiers. - Keystrokes undo; - Keystroke keystroke; - for (unsigned int i = 0; i < 8; ++i) { - // skip modifiers we don't care about - unsigned int bit = (1 << i); - if ((bit & sensitive) == 0) { - continue; - } - - // skip modifiers that are correct - if (getBits(desired, bit) == getBits(m_mask, bit)) { - continue; - } - - LOG((CLOG_DEBUG2 "fix modifier %d", i)); - // get the keycode we're using for this modifier. if - // there isn't one then bail if the modifier is required - // or ignore it if not required. - KeyCode modifierKey = m_modifierToKeycode[i]; - if (modifierKey == 0) { - LOG((CLOG_DEBUG2 "no key mapped to modifier 0x%04x", bit)); + // create the keystrokes for this keysym + ModifierMask mask; + if (!mapToKeystrokes(keys, keycode, mask, keyIndex, m_mask, action)) { + // failed to generate keystrokes keys.clear(); return m_mask; } + else { + // success + LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask)); + return mask; + } + } - keystroke.m_keycode = modifierKey; - keystroke.m_repeat = false; - if (getBits(desired, bit)) { - // modifier is not active but should be. if the - // modifier is a toggle then toggle it on with a - // press/release, otherwise activate it with a - // press. use the first keycode for the modifier. - LOG((CLOG_DEBUG2 "modifier 0x%04x is not active but should be", bit)); - if (getBits(m_toggleModifierMask, bit) != 0) { - LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); - if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || - (bit == m_numLockMask && m_numLockHalfDuplex)) { - keystroke.m_press = True; - keys.push_back(keystroke); - keystroke.m_press = False; - undo.push_back(keystroke); - } - else { - keystroke.m_press = True; - keys.push_back(keystroke); - keystroke.m_press = False; - keys.push_back(keystroke); - undo.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); + // we can't find the keysym mapped to any keycode. this doesn't + // necessarily mean we can't generate the keysym, though. if the + // keysym can be created by combining keysyms then we may still + // be okay. + KeySyms decomposition; + if (decomposeKeySym(keysym, decomposition)) { + LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms", keysym, decomposition.size())); + + // map each decomposed keysym to keystrokes. we want the mask + // and the keycode from the last keysym (which should be the + // only non-dead key). the dead keys are not sensitive to + // anything but shift and mode switch. + ModifierMask mask; + for (KeySyms::const_iterator i = decomposition.begin(); + i != decomposition.end();) { + // increment the iterator + KeySyms::const_iterator next = i; + ++next; + + // lookup the key + keysym = *i; + keyIndex = m_keysymMap.find(keysym); + if (keyIndex == m_keysymMap.end()) { + // missing a required keysym + LOG((CLOG_DEBUG2 "no keycode for decomposed keysym 0x%08x", keysym)); + keys.clear(); + return m_mask; + } + + // the keysym is mapped to some keycode + if (!mapToKeystrokes(keys, keycode, mask, + keyIndex, m_mask, action)) { + // failed to generate keystrokes + keys.clear(); + return m_mask; + } + + // on to the next keysym + i = next; + } + LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask)); + return mask; + } + + LOG((CLOG_DEBUG2 "no keycode for keysym")); + return m_mask; +} + +CXWindowsSecondaryScreen::ModifierMask +CXWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys, KeyCode keycode) const +{ + // add key release + Keystroke keystroke; + keystroke.m_keycode = keycode; + keystroke.m_press = False; + keystroke.m_repeat = false; + keys.push_back(keystroke); + + // if this is a modifier keycode then update the current modifier mask + KeyCodeToModifierMap::const_iterator i = m_keycodeToModifier.find(keycode); + if (i != m_keycodeToModifier.end()) { + ModifierMask bit = (1 << i->second); + if (getBits(m_toggleModifierMask, bit) != 0) { + return flipBits(m_mask, bit); + } + else { + // can't reset bit until all keys that set it are released. + // scan those keys to see if any (except keycode) are pressed. + KeyCodes::const_iterator j; + const KeyCodes& keycodes = m_modifierKeycodes[i->second]; + for (j = keycodes.begin(); j != keycodes.end(); ++j) { + KeyCode modKeycode = *j; + if (modKeycode != keycode && m_keys[modKeycode]) { + break; } } - else { - keystroke.m_press = True; - keys.push_back(keystroke); - keystroke.m_press = False; - undo.push_back(keystroke); + if (j == keycodes.end()) { + return clearBits(m_mask, bit); } } + } - else { - // modifier is active but should not be. if the - // modifier is a toggle then toggle it off with a - // press/release, otherwise deactivate it with a - // release. we must check each keycode for the - // modifier if not a toggle. - LOG((CLOG_DEBUG2 "modifier 0x%04x is active", bit)); - if (getBits(m_toggleModifierMask, bit) != 0) { - LOG((CLOG_DEBUG2 "modifier 0x%04x is a toggle", bit)); - if ((bit == m_capsLockMask && m_capsLockHalfDuplex) || - (bit == m_numLockMask && m_numLockHalfDuplex)) { - keystroke.m_press = False; - keys.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); - } - else { - keystroke.m_press = True; - keys.push_back(keystroke); - keystroke.m_press = False; - keys.push_back(keystroke); - undo.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); - } + return m_mask; +} + +unsigned int +CXWindowsSecondaryScreen::findBestKeyIndex(KeySymIndex keyIndex, + ModifierMask /*currentMask*/) const +{ + // there are up to 4 keycodes per keysym to choose from. the + // best choice is the one that requires the fewest adjustments + // to the modifier state. for example, the letter A normally + // requires shift + a. if shift isn't already down we'd have + // to synthesize a shift press before the a press. however, + // if A could also be created with some other keycode without + // shift then we'd prefer that when shift wasn't down. + // + // if the action is kRepeat or kRelease then we don't call this + // method since we just need to synthesize a key repeat/release + // on the same keycode that we pressed. + // XXX -- do this right + for (unsigned int i = 0; i < 4; ++i) { + if (keyIndex->second.m_keycode[i] != 0) { + return i; + } + } + + assert(0 && "no keycode found for keysym"); + return 0; +} + +bool +CXWindowsSecondaryScreen::isShiftInverted(KeySymIndex keyIndex, + ModifierMask currentMask) const +{ + // each keycode has up to 4 keysym associated with it, one each for: + // no modifiers, shift, mode switch, and shift and mode switch. if + // a keysym is modified by num lock and num lock is active then you + // get the shifted keysym when shift is not down and the unshifted + // keysym when it is. that is, num lock inverts the sense of the + // shift modifier when active. similarly for caps lock. this + // method returns true iff the sense of shift should be inverted + // for this key given a modifier state. + if (keyIndex->second.m_numLockSensitive) { + if (getBits(currentMask, m_numLockMask) != 0) { + return true; + } + } + + // if a keysym is num lock sensitive it is never caps lock + // sensitive, thus the else here. + else if (keyIndex->second.m_capsLockSensitive) { + if (getBits(currentMask, m_capsLockMask) != 0) { + return true; + } + } + + return false; +} + +CXWindowsSecondaryScreen::ModifierMask +CXWindowsSecondaryScreen::getModifierMask(KeySym keysym) const +{ + // find the keysym mapping. if it exists and there's a keycode + // for index 0 (the index we use for modifiers) then return the + // modifierMask, which might be 0. otherwise return 0. + KeySymIndex keyIndex = m_keysymMap.find(keysym); + if (keyIndex != m_keysymMap.end() && keyIndex->second.m_keycode[0] != 0) { + return keyIndex->second.m_modifierMask; + } + else { + return 0; + } +} + +bool +CXWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, + KeyCode& keycode, + ModifierMask& finalMask, + KeySymIndex keyIndex, + ModifierMask currentMask, + EKeyAction action) const +{ + // keyIndex must be valid + assert(keyIndex != m_keysymMap.end()); + + // get the keysym we're trying to generate and possible keycodes + const KeySym keysym = keyIndex->first; + const KeyMapping& mapping = keyIndex->second; + LOG((CLOG_DEBUG2 "keysym = 0x%08x", keysym)); + + // get the best keycode index for the keysym and modifiers. note + // that (bestIndex & 1) == 0 if the keycode is a shift modifier + // and (bestIndex & 2) == 0 if the keycode is a mode switch + // modifier. this is important later because we don't want + // adjustModifiers() to adjust a modifier if that's the key we're + // mapping. + unsigned int bestIndex = findBestKeyIndex(keyIndex, currentMask); + + // get the keycode + keycode = mapping.m_keycode[bestIndex]; + + // flip low bit of bestIndex if shift is inverted. if there's a + // keycode for this new index then use it. otherwise use the old + // keycode. you'd think we should fail if there isn't a keycode + // for the new index but some keymaps only include the upper case + // keysyms (notably those on Sun Solaris) so to handle the missing + // lower case keysyms we just use the old keycode. note that + // isShiftInverted() will always return false for a shift modifier. + if (isShiftInverted(keyIndex, currentMask)) { + LOG((CLOG_DEBUG2 "shift is inverted")); + bestIndex ^= 1; + if (mapping.m_keycode[bestIndex] != 0) { + keycode = mapping.m_keycode[bestIndex]; + } + } + LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode)); + + // compute desired mask. the desired mask is the one that matches + // bestIndex, except if the key being synthesized is a shift key + // where we desire what we already have or if it's the mode switch + // key where we only desire to adjust shift. also, if the keycode + // is not sensitive to shift then don't adjust it, otherwise + // something like shift+home would become just home. similiarly + // for mode switch. + ModifierMask desiredMask = currentMask; + if (keyIndex->second.m_modifierMask != m_shiftMask) { + if (keyIndex->second.m_shiftSensitive[bestIndex]) { + if ((bestIndex & 1) != 0) { + desiredMask = setBits(desiredMask, m_shiftMask); } else { - for (unsigned int j = 0; j < m_keysPerModifier; ++j) { - const KeyCode key = - m_modifierToKeycodes[i * m_keysPerModifier + j]; - if (key != 0 && m_keys[key]) { - keystroke.m_keycode = key; - keystroke.m_press = False; - keys.push_back(keystroke); - keystroke.m_press = True; - undo.push_back(keystroke); - } + desiredMask = clearBits(desiredMask, m_shiftMask); + } + } + if (keyIndex->second.m_modifierMask != m_modeSwitchMask) { + if (keyIndex->second.m_modeSwitchSensitive[bestIndex]) { + if ((bestIndex & 2) != 0) { + desiredMask = setBits(desiredMask, m_modeSwitchMask); + } + else { + desiredMask = clearBits(desiredMask, m_modeSwitchMask); } } } } + // adjust the modifiers to match the desired modifiers + Keystrokes undo; + ModifierMask tmpMask = currentMask; + if (!adjustModifiers(keys, undo, tmpMask, desiredMask)) { + LOG((CLOG_DEBUG2 "failed to adjust modifiers")); + return false; + } + // note if the press of a half-duplex key should be treated as a release - if (isHalfDuplex && getBits(m_mask, modifierBit) != 0) { + const bool isHalfDuplex = + ((keysym == m_capsLockKeysym && m_capsLockHalfDuplex) || + (keysym == m_numLockKeysym && m_numLockHalfDuplex)); + if (isHalfDuplex && getBits(currentMask, mapping.m_modifierMask) != 0) { action = kRelease; } // add the key event + Keystroke keystroke; keystroke.m_keycode = keycode; switch (action) { case kPress: @@ -1000,8 +1003,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, break; } - // add key events to restore the modifier state. apply events in - // the reverse order that they're stored in undo. + // put undo keystrokes at end of keystrokes in reverse order while (!undo.empty()) { keys.push_back(undo.back()); undo.pop_back(); @@ -1009,41 +1011,143 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // if the key is a modifier key then compute the modifier map after // this key is pressed or released. - mask = m_mask; - if (modifierBit != 0) { + finalMask = currentMask; + if (mapping.m_modifierMask != 0) { // can't be repeating if we've gotten here assert(action != kRepeat); // toggle keys modify the state on release. other keys set the // bit on press and clear the bit on release. if half-duplex // then toggle each time we get here. - if (getBits(m_toggleModifierMask, modifierBit) != 0) { - if (isHalfDuplex || action == kRelease) { - mask = flipBits(mask, modifierBit); + if (getBits(m_toggleModifierMask, mapping.m_modifierMask) != 0) { + if (isHalfDuplex) { + finalMask = flipBits(finalMask, mapping.m_modifierMask); } } else if (action == kPress) { - mask = setBits(mask, modifierBit); + finalMask = setBits(finalMask, mapping.m_modifierMask); } - else if (action == kRelease) { - // can't reset bit until all keys that set it are released. - // scan those keys to see if any (except keycode) are pressed. - bool down = false; - for (unsigned int j = 0; !down && j < m_keysPerModifier; ++j) { - KeyCode modKeycode = m_modifierToKeycodes[modifierIndex * - m_keysPerModifier + j]; - if (modKeycode != 0 && modKeycode != keycode) { - down = m_keys[modKeycode]; - } - } - if (!down) { - mask = clearBits(mask, modifierBit); - } - } - LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask)); } - return mask; + return true; +} + +bool +CXWindowsSecondaryScreen::adjustModifiers(Keystrokes& keys, + Keystrokes& undo, + ModifierMask& inOutMask, + ModifierMask desiredMask) const +{ + // get mode switch set correctly. do this before shift because + // mode switch may be sensitive to the shift modifier and will + // set/reset it as necessary. + const bool wantModeSwitch = ((desiredMask & m_modeSwitchMask) != 0); + const bool haveModeSwitch = ((inOutMask & m_modeSwitchMask) != 0); + if (wantModeSwitch != haveModeSwitch) { + LOG((CLOG_DEBUG2 "fix mode switch")); + + // adjust shift if necessary + KeySymIndex modeSwitchIndex = m_keysymMap.find(m_modeSwitchKeysym); + assert(modeSwitchIndex != m_keysymMap.end()); + if (modeSwitchIndex->second.m_shiftSensitive[0]) { + const bool wantShift = false; + const bool haveShift = ((inOutMask & m_shiftMask) != 0); + if (wantShift != haveShift) { + // add shift keystrokes + LOG((CLOG_DEBUG2 "fix shift for mode switch")); + if (!adjustModifier(keys, undo, m_shiftKeysym, wantShift)) { + return false; + } + inOutMask ^= m_shiftMask; + } + } + + // add mode switch keystrokes + if (!adjustModifier(keys, undo, m_modeSwitchKeysym, wantModeSwitch)) { + return false; + } + inOutMask ^= m_modeSwitchMask; + } + + // get shift set correctly + const bool wantShift = ((desiredMask & m_shiftMask) != 0); + const bool haveShift = ((inOutMask & m_shiftMask) != 0); + if (wantShift != haveShift) { + // add shift keystrokes + LOG((CLOG_DEBUG2 "fix shift")); + if (!adjustModifier(keys, undo, m_shiftKeysym, wantShift)) { + return false; + } + inOutMask ^= m_shiftMask; + } + + return true; +} + +bool +CXWindowsSecondaryScreen::adjustModifier(Keystrokes& keys, + Keystrokes& undo, KeySym keysym, bool desireActive) const +{ + // this method generates keystrokes to change a modifier into the + // desired state. under X11, we only expect to adjust the shift + // and mode switch states. other modifiers don't affect keysym + // generation, except num lock and caps lock and we don't change + // those but instead just invert the handling of the shift key. + // we don't check here if the modifier is already in the desired + // state; the caller should do that. + + // get the key mapping for keysym + KeySymIndex keyIndex = m_keysymMap.find(keysym); + if (keyIndex == m_keysymMap.end() || keyIndex->second.m_keycode[0] == 0) { + // no keycode for keysym or keycode is not a modifier + LOG((CLOG_DEBUG2 "no modifier for 0x%08x", keysym)); + return false; + } + + // this had better be a modifier + assert(keyIndex->second.m_modifierMask != 0); + + // we do not handle toggle modifiers here. they never need to be + // adjusted + assert((keyIndex->second.m_modifierMask & m_toggleModifierMask) == 0); + + // initialize keystroke + Keystroke keystroke; + keystroke.m_repeat = false; + + // releasing a modifier is quite different from pressing one. + // when we release a modifier we have to release every keycode that + // is assigned to the modifier since the modifier is active if any + // one of them is down. when we press a modifier we just have to + // press one of those keycodes. + if (desireActive) { + // press + keystroke.m_keycode = keyIndex->second.m_keycode[0]; + keystroke.m_press = True; + keys.push_back(keystroke); + keystroke.m_press = False; + undo.push_back(keystroke); + } + else { + // release + KeyCodeToModifierMap::const_iterator index = + m_keycodeToModifier.find(keyIndex->second.m_keycode[0]); + if (index != m_keycodeToModifier.end()) { + const KeyCodes& keycodes = m_modifierKeycodes[index->second]; + for (KeyCodes::const_iterator j = keycodes.begin(); + j != keycodes.end(); ++j) { + if (m_keys[*j]) { + keystroke.m_keycode = *j; + keystroke.m_press = False; + keys.push_back(keystroke); + keystroke.m_press = True; + undo.push_back(keystroke); + } + } + } + } + + return true; } void @@ -1076,6 +1180,8 @@ CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) } else { // send event + LOG((CLOG_DEBUG2 "keystrokes:")); + LOG((CLOG_DEBUG2 " %d %s", k->m_keycode, k->m_press ? "down" : "up")); XTestFakeKeyEvent(display, k->m_keycode, k->m_press, CurrentTime); // next key @@ -1087,15 +1193,15 @@ CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) XSync(display, False); } -unsigned int +CXWindowsSecondaryScreen::ModifierMask CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const { - unsigned int outMask = 0; + ModifierMask outMask = 0; if (inMask & KeyModifierShift) { - outMask |= ShiftMask; + outMask |= m_shiftMask; } if (inMask & KeyModifierControl) { - outMask |= ControlMask; + outMask |= m_ctrlMask; } if (inMask & KeyModifierAlt) { outMask |= m_altMask; @@ -1168,8 +1274,7 @@ CXWindowsSecondaryScreen::doUpdateKeys(Display* display) delete[] tmpButtons; // update mappings and current modifiers - updateModifierMap(display); - updateKeycodeMap(display); + updateKeysymMap(display); updateModifiers(display); } @@ -1211,43 +1316,11 @@ CXWindowsSecondaryScreen::releaseKeys() } void -CXWindowsSecondaryScreen::updateModifiers(Display* display) -{ - // query the pointer to get the keyboard state - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int state; - if (!XQueryPointer(display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &state)) { - state = 0; - } - - // update active modifier mask - m_mask = 0; - for (unsigned int i = 0; i < 8; ++i) { - const unsigned int bit = (1 << i); - if ((bit & m_toggleModifierMask) == 0) { - for (unsigned int j = 0; j < m_keysPerModifier; ++j) { - if (m_keys[m_modifierToKeycodes[i * m_keysPerModifier + j]]) - m_mask |= bit; - } - } - else if ((bit & state) != 0) { - // toggle is on - m_mask |= bit; - } - } -} - -void -CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) +CXWindowsSecondaryScreen::updateKeysymMap(Display* display) { // there are up to 4 keysyms per keycode static const unsigned int maxKeysyms = 4; - // table for counting 1 bits - static const int s_numBits[maxKeysyms] = { 0, 1, 1, 2 }; - // get the number of keycodes int minKeycode, maxKeycode; XDisplayKeycodes(display, &minKeycode, &maxKeycode); @@ -1265,220 +1338,265 @@ CXWindowsSecondaryScreen::updateKeycodeMap(Display* display) numKeysyms = maxKeysyms; } - // initialize - m_keycodeMap.clear(); + // get modifier map from server + XModifierKeymap* modifiers = XGetModifierMapping(display); - // insert keys + // determine shift and mode switch sensitivity. a keysym is shift + // or mode switch sensitive if its keycode is. a keycode is mode + // mode switch sensitive if it has keysyms for indices 2 or 3. + // it's shift sensitive if the keysym for index 1 (if any) is + // different from the keysym for index 0 and, if the keysym for + // for index 3 (if any) is different from the keysym for index 2. + // that is, if shift changes the generated keysym for the keycode. + std::vector usesShift(numKeycodes); + std::vector usesModeSwitch(numKeycodes); for (int i = 0; i < numKeycodes; ++i) { - // compute mask over all mapped keysyms. if a keycode has, say, - // no shifted keysym then we can ignore the shift state when - // synthesizing an event to generate it. - unsigned int globalMask = 0; - for (unsigned int j = 0; j < numKeysyms; ++j) { - const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; - if (keysym != NoSymbol) { - globalMask |= j; - } + // check mode switch first + if (numKeysyms > 2 && + keysyms[i * keysymsPerKeycode + 2] != NoSymbol || + keysyms[i * keysymsPerKeycode + 3] != NoSymbol) { + usesModeSwitch[i] = true; } - // map each keysym to it's keycode/modifier mask - for (unsigned int j = 0; j < numKeysyms; ++j) { - // get keysym - KeySym keysym = keysyms[i * keysymsPerKeycode + j]; + // check index 0 with index 1 keysyms + if (keysyms[i * keysymsPerKeycode + 0] != NoSymbol && + keysyms[i * keysymsPerKeycode + 1] != NoSymbol && + keysyms[i * keysymsPerKeycode + 1] != + keysyms[i * keysymsPerKeycode + 0]) { + usesShift[i] = true; + } - // get modifier mask required for this keysym. note that - // a keysym of NoSymbol means that a keysym using fewer - // modifiers would be generated using these modifiers. - // for example, given - // keycode 86 = KP_Add - // then we'll generate KP_Add regardless of the modifiers. - // we add an entry for that keysym for these modifiers. - unsigned int index = j; - if (keysym == NoSymbol && (index == 1 || index == 3)) { - // shift doesn't matter - index = index - 1; - keysym = keysyms[i * keysymsPerKeycode + index]; - } - if (keysym == NoSymbol && index == 2) { - // mode switch doesn't matter - index = 0; - keysym = keysyms[i * keysymsPerKeycode + index]; - } - if (keysym == NoSymbol && index == 0) { - // no symbols at all for this keycode - continue; - } - - // look it up, creating a new entry if necessary - KeyCodeMask& entry = m_keycodeMap[keysym]; - - // save keycode for keysym and modifiers - entry.m_keycode[j] = static_cast(minKeycode + i); + else if (numKeysyms >= 4 && + keysyms[i * keysymsPerKeycode + 2] != NoSymbol && + keysyms[i * keysymsPerKeycode + 3] != NoSymbol && + keysyms[i * keysymsPerKeycode + 3] != + keysyms[i * keysymsPerKeycode + 2]) { + usesShift[i] = true; } } - // clean up - XFree(keysyms); -} - -void -CXWindowsSecondaryScreen::updateModifierMap(Display* display) -{ - // get modifier map from server - XModifierKeymap* keymap = XGetModifierMapping(display); - // initialize - m_modifierMask = 0; - m_toggleModifierMask = 0; - m_altMask = 0; - m_metaMask = 0; - m_superMask = 0; - m_modeSwitchMask = 0; - m_numLockMask = 0; - m_capsLockMask = 0; - m_scrollLockMask = 0; - m_altIndex = static_cast(-1); - m_metaIndex = static_cast(-1); - m_superIndex = static_cast(-1); - m_modeSwitchIndex = static_cast(-1); - m_numLockIndex = static_cast(-1); - m_capsLockIndex = static_cast(-1); - m_scrollLockIndex = static_cast(-1); - m_keysPerModifier = keymap->max_keypermod; - m_modifierToKeycode.clear(); - m_modifierToKeycode.resize(8); - m_modifierToKeycodes.clear(); - m_modifierToKeycodes.resize(8 * m_keysPerModifier); + m_keysymMap.clear(); + int keysPerModifier = modifiers->max_keypermod; - // set keycodes and masks - for (unsigned int i = 0; i < 8; ++i) { - const unsigned int bit = (1 << i); - for (unsigned int j = 0; j < m_keysPerModifier; ++j) { - KeyCode keycode = keymap->modifiermap[i * m_keysPerModifier + j]; + // for each modifier keycode, get the index 0 keycode and add it to + // the keysym map. also collect all keycodes for each modifier. + m_keycodeToModifier.clear(); + for (ModifierIndex i = 0; i < 8; ++i) { + // start with no keycodes for this modifier + m_modifierKeycodes[i].clear(); - // save in modifier to keycode - m_modifierToKeycodes[i * m_keysPerModifier + j] = keycode; - - // no further interest in unmapped modifier + // add each keycode for modifier + for (unsigned int j = 0; j < keysPerModifier; ++j) { + // get keycode and ignore unset keycodes + KeyCode keycode = modifiers->modifiermap[i * keysPerModifier + j]; if (keycode == 0) { continue; } - // save keycode for modifier if we don't have one yet - if (m_modifierToKeycode[i] == 0) { - m_modifierToKeycode[i] = keycode; + // save keycode for modifier and modifier for keycode + m_modifierKeycodes[i].push_back(keycode); + m_keycodeToModifier[keycode] = i; + + // get keysym and get/create key mapping + const int keycodeIndex = keycode - minKeycode; + const KeySym keysym = keysyms[keycodeIndex * + keysymsPerKeycode + 0]; + KeyMapping& mapping = m_keysymMap[keysym]; + + // skip if we already have a keycode for this index + if (mapping.m_keycode[0] != 0) { + continue; } - // save bit in all-modifiers mask - m_modifierMask |= bit; + // fill in keysym info + mapping.m_keycode[0] = keycode; + mapping.m_shiftSensitive[0] = usesShift[keycodeIndex]; + mapping.m_modeSwitchSensitive[0] = usesModeSwitch[keycodeIndex]; + mapping.m_modifierMask = (1 << i); + mapping.m_capsLockSensitive = false; + mapping.m_numLockSensitive = false; + } + } - // modifier is a toggle if the keysym is a toggle modifier - const KeySym keysym = XKeycodeToKeysym(display, keycode, 0); - if (isToggleKeysym(keysym)) { - m_toggleModifierMask |= bit; + // create a convenient NoSymbol entry (if it doesn't exist yet). + // sometimes it's useful to handle NoSymbol like a normal keysym. + // remove any entry for NoSymbol. that keysym doesn't count. + { + KeyMapping& mapping = m_keysymMap[NoSymbol]; + for (unsigned int i = 0; i < numKeysyms; ++i) { + mapping.m_keycode[i] = 0; + mapping.m_shiftSensitive[i] = false; + mapping.m_modeSwitchSensitive[i] = false; + } + mapping.m_modifierMask = 0; + mapping.m_capsLockSensitive = false; + mapping.m_numLockSensitive = false; + } + + // add each keysym to the map, unless we've already inserted a key + // for that keysym index. + for (int i = 0; i < numKeycodes; ++i) { + for (unsigned int j = 0; j < numKeysyms; ++j) { + // lookup keysym + const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; + if (keysym == NoSymbol) { + continue; + } + KeyMapping& mapping = m_keysymMap[keysym]; + + // skip if we already have a keycode for this index + if (mapping.m_keycode[j] != 0) { + continue; } - // note mask for particular modifiers - switch (keysym) { - case XK_Alt_L: - case XK_Alt_R: - m_altIndex = i; - m_altMask |= bit; - break; + // fill in keysym info + if (mapping.m_keycode[0] == 0) { + mapping.m_modifierMask = 0; + } + mapping.m_keycode[j] = static_cast( + minKeycode + i); + mapping.m_shiftSensitive[j] = usesShift[i]; + mapping.m_modeSwitchSensitive[j] = usesModeSwitch[i]; + mapping.m_numLockSensitive = adjustForNumLock(keysym); + mapping.m_capsLockSensitive = adjustForCapsLock(keysym); + } + } - case XK_Meta_L: - case XK_Meta_R: - m_metaIndex = i; - m_metaMask |= bit; - break; + // choose the keysym to use for each modifier. if the modifier + // isn't mapped then use NoSymbol. if a modifier has both left + // and right versions then (arbitrarily) prefer the left. also + // collect the available modifier bits. + struct CModifierBitInfo { + public: + KeySym CXWindowsSecondaryScreen::*m_keysym; + KeySym m_left; + KeySym m_right; + }; + static const CModifierBitInfo s_modifierBitTable[] = { + { &CXWindowsSecondaryScreen::m_shiftKeysym, XK_Shift_L, XK_Shift_R }, + { &CXWindowsSecondaryScreen::m_ctrlKeysym, XK_Control_L, XK_Control_R }, + { &CXWindowsSecondaryScreen::m_altKeysym, XK_Alt_L, XK_Alt_R }, + { &CXWindowsSecondaryScreen::m_metaKeysym, XK_Meta_L, XK_Meta_R }, + { &CXWindowsSecondaryScreen::m_superKeysym, XK_Super_L, XK_Super_R }, + { &CXWindowsSecondaryScreen::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol }, + { &CXWindowsSecondaryScreen::m_numLockKeysym, XK_Num_Lock, NoSymbol }, + { &CXWindowsSecondaryScreen::m_capsLockKeysym, XK_Caps_Lock, NoSymbol }, + { &CXWindowsSecondaryScreen::m_scrollLockKeysym, XK_Scroll_Lock, NoSymbol } + }; + m_modifierMask = 0; + m_toggleModifierMask = 0; + for (size_t i = 0; i < sizeof(s_modifierBitTable) / + sizeof(s_modifierBitTable[0]); ++i) { + const CModifierBitInfo& info = s_modifierBitTable[i]; - case XK_Super_L: - case XK_Super_R: - m_superIndex = i; - m_superMask |= bit; - break; + // find available keysym + KeySymIndex keyIndex = m_keysymMap.find(info.m_left); + if (keyIndex == m_keysymMap.end() && info.m_right != NoSymbol) { + keyIndex = m_keysymMap.find(info.m_right); + } + if (keyIndex != m_keysymMap.end() && + keyIndex->second.m_modifierMask != 0) { + this->*(info.m_keysym) = keyIndex->first; + } + else { + this->*(info.m_keysym) = NoSymbol; + continue; + } - case XK_Mode_switch: - m_modeSwitchIndex = i; - m_modeSwitchMask |= bit; - break; + // add modifier bit + m_modifierMask |= keyIndex->second.m_modifierMask; + if (isToggleKeysym(this->*(info.m_keysym))) { + m_toggleModifierMask |= keyIndex->second.m_modifierMask; + } + } - case XK_Num_Lock: - m_numLockIndex = i; - m_numLockMask |= bit; - break; + // if there's no mode switch key mapped then remove all keycodes + // that depend on it and no keycode can be mode switch sensitive. + if (m_modeSwitchKeysym == NoSymbol) { + LOG((CLOG_DEBUG2 "no mode switch in keymap")); + for (KeySymMap::iterator i = m_keysymMap.begin(); + i != m_keysymMap.end(); ) { + i->second.m_keycode[2] = 0; + i->second.m_keycode[3] = 0; + i->second.m_modeSwitchSensitive[0] = false; + i->second.m_modeSwitchSensitive[1] = false; + i->second.m_modeSwitchSensitive[2] = false; + i->second.m_modeSwitchSensitive[3] = false; - case XK_Caps_Lock: - m_capsLockIndex = i; - m_capsLockMask |= bit; - break; - - case XK_Scroll_Lock: - m_scrollLockIndex = i; - m_scrollLockMask |= bit; - break; + // if this keysym no has no keycodes then remove it + // except for the NoSymbol keysym mapping. + if (i->second.m_keycode[0] == 0 && i->second.m_keycode[1] == 0) { + m_keysymMap.erase(i++); + } + else { + ++i; } } } - XFreeModifiermap(keymap); + // cache the bits for the modifier + m_shiftMask = getModifierMask(m_shiftKeysym); + m_ctrlMask = getModifierMask(m_ctrlKeysym); + m_altMask = getModifierMask(m_altKeysym); + m_metaMask = getModifierMask(m_metaKeysym); + m_superMask = getModifierMask(m_superKeysym); + m_capsLockMask = getModifierMask(m_capsLockKeysym); + m_numLockMask = getModifierMask(m_numLockKeysym); + m_modeSwitchMask = getModifierMask(m_modeSwitchKeysym); + m_scrollLockMask = getModifierMask(m_scrollLockKeysym); + + // clean up + XFree(keysyms); + XFreeModifiermap(modifiers); } -unsigned int -CXWindowsSecondaryScreen::keySymToModifierIndex(KeySym keysym) const +void +CXWindowsSecondaryScreen::updateModifiers(Display* display) { - switch (keysym) { - case XK_Shift_L: - case XK_Shift_R: - return 0; - - case XK_Control_L: - case XK_Control_R: - return 2; - - case XK_Alt_L: - case XK_Alt_R: - return m_altIndex; - - case XK_Meta_L: - case XK_Meta_R: - return m_metaIndex; - - case XK_Super_L: - case XK_Super_R: - return m_superIndex; - - case XK_Mode_switch: - return m_modeSwitchIndex; - - case XK_Num_Lock: - return m_numLockIndex; - - case XK_Caps_Lock: - return m_capsLockIndex; - - case XK_Scroll_Lock: - return m_scrollLockIndex; + // query the pointer to get the keyboard state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (!XQueryPointer(display, m_window, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + state = 0; } - return static_cast(-1); + // update active modifier mask + m_mask = 0; + for (ModifierIndex i = 0; i < 8; ++i) { + const ModifierMask bit = (1 << i); + if ((bit & m_toggleModifierMask) == 0) { + for (KeyCodes::const_iterator j = m_modifierKeycodes[i].begin(); + j != m_modifierKeycodes[i].end(); ++j) { + if (m_keys[*j]) { + m_mask |= bit; + break; + } + } + } + else if ((bit & state) != 0) { + // toggle is on + m_mask |= bit; + } + } } void CXWindowsSecondaryScreen::toggleKey(Display* display, - KeySym keysym, unsigned int mask) + KeySym keysym, ModifierMask mask) { - // lookup the keycode - KeyCodeMap::const_iterator index = m_keycodeMap.find(keysym); - if (index == m_keycodeMap.end()) { + // lookup the key mapping + KeySymIndex index = m_keysymMap.find(keysym); + if (index == m_keysymMap.end()) { return; } - // FIXME -- which keycode? KeyCode keycode = index->second.m_keycode[0]; // toggle the key - if ((keysym == XK_Caps_Lock && m_capsLockHalfDuplex) || - (keysym == XK_Num_Lock && m_numLockHalfDuplex)) { + if ((keysym == m_capsLockKeysym && m_capsLockHalfDuplex) || + (keysym == m_numLockKeysym && m_numLockHalfDuplex)) { // "half-duplex" toggle XTestFakeKeyEvent(display, keycode, (m_mask & mask) == 0, CurrentTime); } @@ -1554,8 +1672,8 @@ static const KeySym g_mapE000[] = }; #endif -CXWindowsSecondaryScreen::KeyCodeIndex -CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask mask) const +KeySym +CXWindowsSecondaryScreen::keyIDToKeySym(KeyID id, ModifierMask mask) const { // convert id to keysym KeySym keysym = NoSymbol; @@ -1564,8 +1682,7 @@ CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask mask) const switch (id & 0x0000ff00) { #if defined(HAVE_X11_XF86KEYSYM_H) case 0xe000: - keysym = g_mapE000[id & 0xff]; - break; + return g_mapE000[id & 0xff]; #endif case 0xee00: @@ -1584,16 +1701,16 @@ CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask mask) const else if ((id >= 0x0020 && id <= 0x007e) || (id >= 0x00a0 && id <= 0x00ff)) { // Latin-1 maps directly - keysym = static_cast(id); + return static_cast(id); } else { // lookup keysym in table - keysym = CXWindowsUtil::mapUCS4ToKeySym(id); + return CXWindowsUtil::mapUCS4ToKeySym(id); } // fail if unknown key if (keysym == NoSymbol) { - return m_keycodeMap.end(); + return keysym; } // if kKeyTab is requested with shift active then try XK_ISO_Left_Tab @@ -1604,93 +1721,92 @@ CXWindowsSecondaryScreen::findKey(KeyID id, KeyModifierMask mask) const keysym = XK_ISO_Left_Tab; } - // find the keycodes that generate the keysym - KeyCodeIndex index = m_keycodeMap.find(keysym); - if (index == noKey()) { - // try upper/lower case (as some keymaps only include the - // upper case, notably Sun Solaris). - KeySym lower, upper; - XConvertCase(keysym, &lower, &upper); - if (lower != keysym) - index = m_keycodeMap.find(lower); - else if (upper != keysym) - index = m_keycodeMap.find(upper); - } - if (index == noKey()) { - // try backup keysym for certain keys (particularly the numpad - // keys since most laptops don't have a separate numpad and the - // numpad overlaying the main keyboard may not have movement - // key bindings). - switch (keysym) { - case XK_KP_Home: - keysym = XK_Home; - break; + // some keysyms have emergency backups (particularly the numpad + // keys since most laptops don't have a separate numpad and the + // numpad overlaying the main keyboard may not have movement + // key bindings). figure out the emergency backup. + KeySym backupKeysym; + switch (keysym) { + case XK_KP_Home: + backupKeysym = XK_Home; + break; - case XK_KP_Left: - keysym = XK_Left; - break; + case XK_KP_Left: + backupKeysym = XK_Left; + break; - case XK_KP_Up: - keysym = XK_Up; - break; + case XK_KP_Up: + backupKeysym = XK_Up; + break; - case XK_KP_Right: - keysym = XK_Right; - break; + case XK_KP_Right: + backupKeysym = XK_Right; + break; - case XK_KP_Down: - keysym = XK_Down; - break; + case XK_KP_Down: + backupKeysym = XK_Down; + break; - case XK_KP_Prior: - keysym = XK_Prior; - break; + case XK_KP_Prior: + backupKeysym = XK_Prior; + break; - case XK_KP_Next: - keysym = XK_Next; - break; + case XK_KP_Next: + backupKeysym = XK_Next; + break; - case XK_KP_End: - keysym = XK_End; - break; + case XK_KP_End: + backupKeysym = XK_End; + break; - case XK_KP_Insert: - keysym = XK_Insert; - break; + case XK_KP_Insert: + backupKeysym = XK_Insert; + break; - case XK_KP_Delete: - keysym = XK_Delete; - break; + case XK_KP_Delete: + backupKeysym = XK_Delete; + break; - case XK_ISO_Left_Tab: - keysym = XK_Tab; - break; + case XK_ISO_Left_Tab: + backupKeysym = XK_Tab; + break; - default: - return index; - } - - index = m_keycodeMap.find(keysym); + default: + backupKeysym = keysym; + break; } - return index; + // see if the keysym is assigned to any keycode. if not and the + // backup keysym is then use the backup keysym. + if (backupKeysym != keysym && + m_keysymMap.find(keysym) == m_keysymMap.end() && + m_keysymMap.find(backupKeysym) != m_keysymMap.end()) { + keysym = backupKeysym; + } + + return keysym; } -CXWindowsSecondaryScreen::KeyCodeIndex -CXWindowsSecondaryScreen::noKey() const +bool +CXWindowsSecondaryScreen::decomposeKeySym(KeySym keysym, + KeySyms& decomposed) const { - return m_keycodeMap.end(); + // unfortunately, X11 doesn't appear to have any way of + // decomposing a keysym into its component keysyms. we'll + // use a lookup table for certain character sets. + const KeySymsMap& table = getDecomposedKeySymTable(); + KeySymsMap::const_iterator i = table.find(keysym); + if (i == table.end()) { + return false; + } + decomposed = i->second; + return true; } bool CXWindowsSecondaryScreen::adjustForNumLock(KeySym keysym) const { - if (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)) { - // it's NumLock sensitive - LOG((CLOG_DEBUG2 "keypad key: NumLock %s", ((m_mask & m_numLockMask) != 0) ? "active" : "inactive")); - return true; - } - return false; + return (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)); } bool @@ -1698,20 +1814,233 @@ CXWindowsSecondaryScreen::adjustForCapsLock(KeySym keysym) const { KeySym lKey, uKey; XConvertCase(keysym, &lKey, &uKey); - if (lKey != uKey) { - // it's CapsLock sensitive - LOG((CLOG_DEBUG2 "case convertible: CapsLock %s", ((m_mask & m_capsLockMask) != 0) ? "active" : "inactive")); - return true; + return (lKey != uKey); +} + +const CXWindowsSecondaryScreen::KeySymsMap& +CXWindowsSecondaryScreen::getDecomposedKeySymTable() +{ + static const KeySym s_rawTable[] = { + // Latin-1 (ISO 8859-1) + XK_Agrave, XK_dead_grave, XK_A, 0, + XK_Aacute, XK_dead_acute, XK_A, 0, + XK_Acircumflex, XK_dead_circumflex, XK_A, 0, + XK_Atilde, XK_dead_tilde, XK_A, 0, + XK_Adiaeresis, XK_dead_diaeresis, XK_A, 0, + XK_Aring, XK_dead_abovering, XK_A, 0, + XK_Ccedilla, XK_dead_cedilla, XK_C, 0, + XK_Egrave, XK_dead_grave, XK_E, 0, + XK_Eacute, XK_dead_acute, XK_E, 0, + XK_Ecircumflex, XK_dead_circumflex, XK_E, 0, + XK_Ediaeresis, XK_dead_diaeresis, XK_E, 0, + XK_Igrave, XK_dead_grave, XK_I, 0, + XK_Iacute, XK_dead_acute, XK_I, 0, + XK_Icircumflex, XK_dead_circumflex, XK_I, 0, + XK_Idiaeresis, XK_dead_diaeresis, XK_I, 0, + XK_Ntilde, XK_dead_tilde, XK_N, 0, + XK_Ograve, XK_dead_grave, XK_O, 0, + XK_Oacute, XK_dead_acute, XK_O, 0, + XK_Ocircumflex, XK_dead_circumflex, XK_O, 0, + XK_Otilde, XK_dead_tilde, XK_O, 0, + XK_Odiaeresis, XK_dead_diaeresis, XK_O, 0, + XK_Ugrave, XK_dead_grave, XK_U, 0, + XK_Uacute, XK_dead_acute, XK_U, 0, + XK_Ucircumflex, XK_dead_circumflex, XK_U, 0, + XK_Udiaeresis, XK_dead_diaeresis, XK_U, 0, + XK_Yacute, XK_dead_acute, XK_Y, 0, + XK_agrave, XK_dead_grave, XK_a, 0, + XK_aacute, XK_dead_acute, XK_a, 0, + XK_acircumflex, XK_dead_circumflex, XK_a, 0, + XK_atilde, XK_dead_tilde, XK_a, 0, + XK_adiaeresis, XK_dead_diaeresis, XK_a, 0, + XK_aring, XK_dead_abovering, XK_a, 0, + XK_ccedilla, XK_dead_cedilla, XK_c, 0, + XK_egrave, XK_dead_grave, XK_e, 0, + XK_eacute, XK_dead_acute, XK_e, 0, + XK_ecircumflex, XK_dead_circumflex, XK_e, 0, + XK_ediaeresis, XK_dead_diaeresis, XK_e, 0, + XK_igrave, XK_dead_grave, XK_i, 0, + XK_iacute, XK_dead_acute, XK_i, 0, + XK_icircumflex, XK_dead_circumflex, XK_i, 0, + XK_idiaeresis, XK_dead_diaeresis, XK_i, 0, + XK_ntilde, XK_dead_tilde, XK_n, 0, + XK_ograve, XK_dead_grave, XK_o, 0, + XK_oacute, XK_dead_acute, XK_o, 0, + XK_ocircumflex, XK_dead_circumflex, XK_o, 0, + XK_otilde, XK_dead_tilde, XK_o, 0, + XK_odiaeresis, XK_dead_diaeresis, XK_o, 0, + XK_ugrave, XK_dead_grave, XK_u, 0, + XK_uacute, XK_dead_acute, XK_u, 0, + XK_ucircumflex, XK_dead_circumflex, XK_u, 0, + XK_udiaeresis, XK_dead_diaeresis, XK_u, 0, + XK_yacute, XK_dead_acute, XK_y, 0, + XK_ydiaeresis, XK_dead_diaeresis, XK_y, 0, + + // Latin-2 (ISO 8859-2) + XK_Aogonek, XK_dead_ogonek, XK_A, 0, + XK_Lcaron, XK_dead_caron, XK_L, 0, + XK_Sacute, XK_dead_acute, XK_S, 0, + XK_Scaron, XK_dead_caron, XK_S, 0, + XK_Scedilla, XK_dead_cedilla, XK_S, 0, + XK_Tcaron, XK_dead_caron, XK_T, 0, + XK_Zacute, XK_dead_acute, XK_Z, 0, + XK_Zcaron, XK_dead_caron, XK_Z, 0, + XK_Zabovedot, XK_dead_abovedot, XK_Z, 0, + XK_aogonek, XK_dead_ogonek, XK_a, 0, + XK_lcaron, XK_dead_caron, XK_l, 0, + XK_sacute, XK_dead_acute, XK_s, 0, + XK_scaron, XK_dead_caron, XK_s, 0, + XK_scedilla, XK_dead_cedilla, XK_s, 0, + XK_tcaron, XK_dead_caron, XK_t, 0, + XK_zacute, XK_dead_acute, XK_z, 0, + XK_zcaron, XK_dead_caron, XK_z, 0, + XK_zabovedot, XK_dead_abovedot, XK_z, 0, + XK_Racute, XK_dead_acute, XK_R, 0, + XK_Abreve, XK_dead_breve, XK_A, 0, + XK_Lacute, XK_dead_acute, XK_L, 0, + XK_Cacute, XK_dead_acute, XK_C, 0, + XK_Ccaron, XK_dead_caron, XK_C, 0, + XK_Eogonek, XK_dead_ogonek, XK_E, 0, + XK_Ecaron, XK_dead_caron, XK_E, 0, + XK_Dcaron, XK_dead_caron, XK_D, 0, + XK_Nacute, XK_dead_acute, XK_N, 0, + XK_Ncaron, XK_dead_caron, XK_N, 0, + XK_Odoubleacute, XK_dead_doubleacute, XK_O, 0, + XK_Rcaron, XK_dead_caron, XK_R, 0, + XK_Uring, XK_dead_abovering, XK_U, 0, + XK_Udoubleacute, XK_dead_doubleacute, XK_U, 0, + XK_Tcedilla, XK_dead_cedilla, XK_T, 0, + XK_racute, XK_dead_acute, XK_r, 0, + XK_abreve, XK_dead_breve, XK_a, 0, + XK_lacute, XK_dead_acute, XK_l, 0, + XK_cacute, XK_dead_acute, XK_c, 0, + XK_ccaron, XK_dead_caron, XK_c, 0, + XK_eogonek, XK_dead_ogonek, XK_e, 0, + XK_ecaron, XK_dead_caron, XK_e, 0, + XK_dcaron, XK_dead_caron, XK_d, 0, + XK_nacute, XK_dead_acute, XK_n, 0, + XK_ncaron, XK_dead_caron, XK_n, 0, + XK_odoubleacute, XK_dead_doubleacute, XK_o, 0, + XK_rcaron, XK_dead_caron, XK_r, 0, + XK_uring, XK_dead_abovering, XK_u, 0, + XK_udoubleacute, XK_dead_doubleacute, XK_u, 0, + XK_tcedilla, XK_dead_cedilla, XK_t, 0, + + // Latin-3 (ISO 8859-3) + XK_Hcircumflex, XK_dead_circumflex, XK_H, 0, + XK_Iabovedot, XK_dead_abovedot, XK_I, 0, + XK_Gbreve, XK_dead_breve, XK_G, 0, + XK_Jcircumflex, XK_dead_circumflex, XK_J, 0, + XK_hcircumflex, XK_dead_circumflex, XK_h, 0, + XK_gbreve, XK_dead_breve, XK_g, 0, + XK_jcircumflex, XK_dead_circumflex, XK_j, 0, + XK_Cabovedot, XK_dead_abovedot, XK_C, 0, + XK_Ccircumflex, XK_dead_circumflex, XK_C, 0, + XK_Gabovedot, XK_dead_abovedot, XK_G, 0, + XK_Gcircumflex, XK_dead_circumflex, XK_G, 0, + XK_Ubreve, XK_dead_breve, XK_U, 0, + XK_Scircumflex, XK_dead_circumflex, XK_S, 0, + XK_cabovedot, XK_dead_abovedot, XK_c, 0, + XK_ccircumflex, XK_dead_circumflex, XK_c, 0, + XK_gabovedot, XK_dead_abovedot, XK_g, 0, + XK_gcircumflex, XK_dead_circumflex, XK_g, 0, + XK_ubreve, XK_dead_breve, XK_u, 0, + XK_scircumflex, XK_dead_circumflex, XK_s, 0, + + // Latin-4 (ISO 8859-4) + XK_scircumflex, XK_dead_circumflex, XK_s, 0, + XK_Rcedilla, XK_dead_cedilla, XK_R, 0, + XK_Itilde, XK_dead_tilde, XK_I, 0, + XK_Lcedilla, XK_dead_cedilla, XK_L, 0, + XK_Emacron, XK_dead_macron, XK_E, 0, + XK_Gcedilla, XK_dead_cedilla, XK_G, 0, + XK_rcedilla, XK_dead_cedilla, XK_r, 0, + XK_itilde, XK_dead_tilde, XK_i, 0, + XK_lcedilla, XK_dead_cedilla, XK_l, 0, + XK_emacron, XK_dead_macron, XK_e, 0, + XK_gcedilla, XK_dead_cedilla, XK_g, 0, + XK_Amacron, XK_dead_macron, XK_A, 0, + XK_Iogonek, XK_dead_ogonek, XK_I, 0, + XK_Eabovedot, XK_dead_abovedot, XK_E, 0, + XK_Imacron, XK_dead_macron, XK_I, 0, + XK_Ncedilla, XK_dead_cedilla, XK_N, 0, + XK_Omacron, XK_dead_macron, XK_O, 0, + XK_Kcedilla, XK_dead_cedilla, XK_K, 0, + XK_Uogonek, XK_dead_ogonek, XK_U, 0, + XK_Utilde, XK_dead_tilde, XK_U, 0, + XK_Umacron, XK_dead_macron, XK_U, 0, + XK_amacron, XK_dead_macron, XK_a, 0, + XK_iogonek, XK_dead_ogonek, XK_i, 0, + XK_eabovedot, XK_dead_abovedot, XK_e, 0, + XK_imacron, XK_dead_macron, XK_i, 0, + XK_ncedilla, XK_dead_cedilla, XK_n, 0, + XK_omacron, XK_dead_macron, XK_o, 0, + XK_kcedilla, XK_dead_cedilla, XK_k, 0, + XK_uogonek, XK_dead_ogonek, XK_u, 0, + XK_utilde, XK_dead_tilde, XK_u, 0, + XK_umacron, XK_dead_macron, XK_u, 0, + + // Latin-8 (ISO 8859-14) + XK_Babovedot, XK_dead_abovedot, XK_B, 0, + XK_babovedot, XK_dead_abovedot, XK_b, 0, + XK_Dabovedot, XK_dead_abovedot, XK_D, 0, + XK_Wgrave, XK_dead_grave, XK_W, 0, + XK_Wacute, XK_dead_acute, XK_W, 0, + XK_dabovedot, XK_dead_abovedot, XK_d, 0, + XK_Ygrave, XK_dead_grave, XK_Y, 0, + XK_Fabovedot, XK_dead_abovedot, XK_F, 0, + XK_fabovedot, XK_dead_abovedot, XK_f, 0, + XK_Mabovedot, XK_dead_abovedot, XK_M, 0, + XK_mabovedot, XK_dead_abovedot, XK_m, 0, + XK_Pabovedot, XK_dead_abovedot, XK_P, 0, + XK_wgrave, XK_dead_grave, XK_w, 0, + XK_pabovedot, XK_dead_abovedot, XK_p, 0, + XK_wacute, XK_dead_acute, XK_w, 0, + XK_Sabovedot, XK_dead_abovedot, XK_S, 0, + XK_ygrave, XK_dead_grave, XK_y, 0, + XK_Wdiaeresis, XK_dead_diaeresis, XK_W, 0, + XK_wdiaeresis, XK_dead_diaeresis, XK_w, 0, + XK_sabovedot, XK_dead_abovedot, XK_s, 0, + XK_Wcircumflex, XK_dead_circumflex, XK_W, 0, + XK_Tabovedot, XK_dead_abovedot, XK_T, 0, + XK_Ycircumflex, XK_dead_circumflex, XK_Y, 0, + XK_wcircumflex, XK_dead_circumflex, XK_w, 0, + XK_tabovedot, XK_dead_abovedot, XK_t, 0, + XK_ycircumflex, XK_dead_circumflex, XK_y, 0, + + // Latin-9 (ISO 8859-15) + XK_Ydiaeresis, XK_dead_diaeresis, XK_Y, 0, + + // end of table + 0 + }; + + // fill table if not yet initialized + if (s_decomposedKeySyms.empty()) { + const KeySym* scan = s_rawTable; + while (*scan != 0) { + // add an entry for this keysym + KeySyms& entry = s_decomposedKeySyms[*scan]; + + // add the decomposed keysyms for the keysym + while (*++scan != 0) { + entry.push_back(*scan); + } + + // skip end of entry marker + ++scan; + } } - return false; + + return s_decomposedKeySyms; } // -// CXWindowsSecondaryScreen::KeyCodeMask +// CXWindowsSecondaryScreen::KeyMapping // -CXWindowsSecondaryScreen::KeyCodeMask::KeyCodeMask() +CXWindowsSecondaryScreen::KeyMapping::KeyMapping() { m_keycode[0] = 0; m_keycode[1] = 0; diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index dd631a88..811aaa87 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -77,48 +77,87 @@ protected: private: enum EKeyAction { kPress, kRelease, kRepeat }; - class KeyCodeMask { - public: - KeyCodeMask(); - public: - KeyCode m_keycode[4]; - }; + typedef unsigned int ModifierIndex; + typedef unsigned int ModifierMask; class Keystroke { public: KeyCode m_keycode; Bool m_press; bool m_repeat; }; - typedef std::vector Keystrokes; + class KeyMapping { + public: + KeyMapping(); + + public: + // KeyCode to generate keysym and whether keycode[i] is + // sensitive to shift and mode switch. + KeyCode m_keycode[4]; + bool m_shiftSensitive[4]; + bool m_modeSwitchSensitive[4]; + + // the modifier mask of keysym or 0 if not a modifier + ModifierMask m_modifierMask; + + // whether keysym is sensitive to caps and num lock + bool m_numLockSensitive; + bool m_capsLockSensitive; + }; + typedef std::vector KeyCodes; - typedef std::map KeyCodeMap; - typedef KeyCodeMap::const_iterator KeyCodeIndex; + typedef std::map KeyCodeToModifierMap; + typedef std::map KeySymMap; + typedef KeySymMap::const_iterator KeySymIndex; + typedef std::vector Keystrokes; + typedef std::vector KeySyms; + typedef std::map KeySymsMap; typedef std::map ServerKeyMap; unsigned int mapButton(ButtonID button) const; - unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, + ModifierMask mapKey(Keystrokes&, KeyCode&, KeyID, KeyModifierMask, EKeyAction) const; + ModifierMask mapKeyRelease(Keystrokes&, KeyCode) const; + bool mapToKeystrokes(Keystrokes& keys, + KeyCode& keycode, + ModifierMask& finalMask, + KeySymIndex keyIndex, + ModifierMask currentMask, + EKeyAction action) const; + bool adjustModifiers(Keystrokes& keys, + Keystrokes& undo, + ModifierMask& inOutMask, + ModifierMask desiredMask) const; + bool adjustModifier(Keystrokes& keys, + Keystrokes& undo, + KeySym keysym, + bool desireActive) const; void doKeystrokes(const Keystrokes&, SInt32 count); - unsigned int maskToX(KeyModifierMask) const; + ModifierMask maskToX(KeyModifierMask) const; + + unsigned int findBestKeyIndex(KeySymIndex keyIndex, + ModifierMask currentMask) const; + bool isShiftInverted(KeySymIndex keyIndex, + ModifierMask currentMask) const; + ModifierMask getModifierMask(KeySym) const; void doUpdateKeys(Display*); void doReleaseKeys(Display*); - void updateKeycodeMap(Display* display); + void updateKeysymMap(Display* display); void updateModifiers(Display* display); - void updateModifierMap(Display* display); - unsigned int keySymToModifierIndex(KeySym) const; - void toggleKey(Display*, KeySym, unsigned int mask); + ModifierIndex keySymToModifierIndex(KeySym) const; + void toggleKey(Display*, KeySym, ModifierMask mask); static bool isToggleKeysym(KeySym); - KeyCodeIndex findKey(KeyID keysym, KeyModifierMask mask) const; - KeyCodeIndex noKey() const; + KeySym keyIDToKeySym(KeyID id, ModifierMask mask) const; bool adjustForNumLock(KeySym) const; bool adjustForCapsLock(KeySym) const; -private: - enum { kNONE, kSHIFT, kALTGR, kSHIFT_ALTGR }; + bool decomposeKeySym(KeySym keysym, + KeySyms& decomposed) const; + static const KeySymsMap& getDecomposedKeySymTable(); +private: CXWindowsScreen* m_screen; Window m_window; @@ -140,40 +179,44 @@ private: std::vector m_buttons; // current active modifiers (X key masks) - unsigned int m_mask; - - // maps key IDs to X keycodes and the X modifier key mask needed - // to generate the right keysym - KeyCodeMap m_keycodeMap; + ModifierMask m_mask; // the modifiers that have keys bound to them - unsigned int m_modifierMask; + ModifierMask m_modifierMask; // set bits indicate modifiers that toggle (e.g. caps-lock) - unsigned int m_toggleModifierMask; + ModifierMask m_toggleModifierMask; + + // keysym to keycode mapping + KeySymMap m_keysymMap; + + // modifier index to keycodes + KeyCodes m_modifierKeycodes[8]; + + // keycode to modifier index + KeyCodeToModifierMap m_keycodeToModifier; + + // modifier keysyms + KeySym m_shiftKeysym; + KeySym m_ctrlKeysym; + KeySym m_altKeysym; + KeySym m_metaKeysym; + KeySym m_superKeysym; + KeySym m_modeSwitchKeysym; + KeySym m_numLockKeysym; + KeySym m_capsLockKeysym; + KeySym m_scrollLockKeysym; // modifier masks - unsigned int m_altMask; - unsigned int m_metaMask; - unsigned int m_superMask; - unsigned int m_modeSwitchMask; - unsigned int m_numLockMask; - unsigned int m_capsLockMask; - unsigned int m_scrollLockMask; - - // modifier indices - unsigned int m_altIndex; - unsigned int m_metaIndex; - unsigned int m_superIndex; - unsigned int m_modeSwitchIndex; - unsigned int m_numLockIndex; - unsigned int m_capsLockIndex; - unsigned int m_scrollLockIndex; - - // map X modifier key indices to the key codes bound to them - unsigned int m_keysPerModifier; - KeyCodes m_modifierToKeycode; - KeyCodes m_modifierToKeycodes; + ModifierMask m_shiftMask; + ModifierMask m_ctrlMask; + ModifierMask m_altMask; + ModifierMask m_metaMask; + ModifierMask m_superMask; + ModifierMask m_modeSwitchMask; + ModifierMask m_numLockMask; + ModifierMask m_capsLockMask; + ModifierMask m_scrollLockMask; // map server key buttons to local keycodes ServerKeyMap m_serverKeyMap; @@ -187,6 +230,9 @@ private: // a screen other than screen 0. bool m_xtestIsXineramaUnaware; bool m_xinerama; + + // a table of keysym decompositions + static KeySymsMap s_decomposedKeySyms; }; #endif From 47b480c0bcdd140aa91d9895921a17a5120a0cb7 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 5 Jul 2003 14:47:41 +0000 Subject: [PATCH 534/807] Compress sequential MappingNotify events into one. --- lib/platform/CXWindowsScreen.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 81553d59..964fe739 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -635,6 +635,19 @@ CXWindowsScreen::onPreDispatch(CEvent* event) switch (xevent->type) { case MappingNotify: + { + CLock lock(&m_mutex); + if (XPending(m_display) > 0) { + XEvent tmpEvent; + XPeekEvent(m_display, &tmpEvent); + if (tmpEvent.type == MappingNotify) { + // discard this MappingNotify since another follows. + // we tend to get a bunch of these in a row. + return true; + } + } + } + // keyboard mapping changed XRefreshKeyboardMapping(&xevent->xmapping); From 8f9cc6e4764861c0100d03b9070f6242b07546d6 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 5 Jul 2003 14:49:08 +0000 Subject: [PATCH 535/807] Minor X11 keyboard code cleanup. Also now handling KeyPress with keycode == 0 generated by XFilterEvent() by using the keycode from the previous KeyPress. --- lib/platform/CXWindowsPrimaryScreen.cpp | 44 +++++++++++++++--- lib/platform/CXWindowsPrimaryScreen.h | 6 ++- lib/platform/CXWindowsSecondaryScreen.cpp | 54 ++++++++++++----------- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp index e3538cef..360bea85 100644 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ b/lib/platform/CXWindowsPrimaryScreen.cpp @@ -42,7 +42,8 @@ CXWindowsPrimaryScreen::CXWindowsPrimaryScreen( m_receiver(primaryReceiver), m_window(None), m_im(NULL), - m_ic(NULL) + m_ic(NULL), + m_lastKeycode(0) { m_screen = new CXWindowsScreen(receiver, this); } @@ -223,8 +224,28 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) XEvent& xevent = event->m_event; // let input methods try to handle event first - if (m_ic != NULL && XFilterEvent(&xevent, None)) { - return true; + if (m_ic != NULL) { + // XFilterEvent() may eat the event and generate a new KeyPress + // event with a keycode of 0 because there isn't an actual key + // associated with the keysym. but the KeyRelease may pass + // through XFilterEvent() and keep its keycode. this means + // there's a mismatch between KeyPress and KeyRelease keycodes. + // since we use the keycode on the client to detect when a key + // is released this won't do. so we remember the keycode on + // the most recent KeyPress (and clear it on a matching + // KeyRelease) so we have a keycode for a synthesized KeyPress. + if (xevent.type == KeyPress && xevent.xkey.keycode != 0) { + m_lastKeycode = xevent.xkey.keycode; + } + else if (xevent.type == KeyRelease && + xevent.xkey.keycode == m_lastKeycode) { + m_lastKeycode = 0; + } + + // now filter the event + if (XFilterEvent(&xevent, None)) { + return true; + } } // handle event @@ -257,16 +278,23 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) key = kKeyDelete; } + // get which button. see call to XFilterEvent() above + // for more info. + KeyCode keycode = xevent.xkey.keycode; + if (keycode == 0) { + keycode = m_lastKeycode; + } + // handle key m_receiver->onKeyDown(key, mask, - static_cast(xevent.xkey.keycode)); + static_cast(keycode)); if (key == kKeyCapsLock && m_capsLockHalfDuplex) { m_receiver->onKeyUp(key, mask | KeyModifierCapsLock, - static_cast(xevent.xkey.keycode)); + static_cast(keycode)); } else if (key == kKeyNumLock && m_numLockHalfDuplex) { m_receiver->onKeyUp(key, mask | KeyModifierNumLock, - static_cast(xevent.xkey.keycode)); + static_cast(keycode)); } } } @@ -509,6 +537,9 @@ CXWindowsPrimaryScreen::onPostOpen() XWindowAttributes attr; XGetWindowAttributes(display, m_window, &attr); XSelectInput(display, m_window, attr.your_event_mask | mask); + + // no previous keycode + m_lastKeycode = 0; } void @@ -523,6 +554,7 @@ CXWindowsPrimaryScreen::onPreClose() XCloseIM(m_im); m_im = NULL; } + m_lastKeycode = 0; } void diff --git a/lib/platform/CXWindowsPrimaryScreen.h b/lib/platform/CXWindowsPrimaryScreen.h index 828f73e6..2343ca07 100644 --- a/lib/platform/CXWindowsPrimaryScreen.h +++ b/lib/platform/CXWindowsPrimaryScreen.h @@ -117,8 +117,10 @@ private: // position of center pixel of screen SInt32 m_xCenter, m_yCenter; - XIM m_im; - XIC m_ic; + // input method stuff + XIM m_im; + XIC m_ic; + KeyCode m_lastKeycode; }; #endif diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 734a58fc..4609068e 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -126,9 +126,6 @@ void CXWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask, KeyButton button) { - Keystrokes keys; - KeyCode keycode; - // check for ctrl+alt+del emulation if (key == kKeyDelete && (mask & (KeyModifierControl | KeyModifierAlt)) == @@ -139,29 +136,30 @@ CXWindowsSecondaryScreen::keyDown(KeyID key, // get the sequence of keys to simulate key press and the final // modifier state. + Keystrokes keys; + KeyCode keycode; m_mask = mapKey(keys, keycode, key, mask, kPress); if (keys.empty()) { + // do nothing if there are no associated keys (i.e. lookup failed) return; } // generate key events doKeystrokes(keys, 1); - // note that key is now down - m_keys[keycode] = true; - m_fakeKeys[keycode] = true; - - // note which server key generated this key - m_serverKeyMap[button] = keycode; + // do not record button down if button is 0 (invalid) + if (button != 0) { + // note that key is now down + m_serverKeyMap[button] = keycode; + m_keys[keycode] = true; + m_fakeKeys[keycode] = true; + } } void CXWindowsSecondaryScreen::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button) { - Keystrokes keys; - KeyCode keycode; - // if we haven't seen this button go down then ignore it ServerKeyMap::iterator index = m_serverKeyMap.find(button); if (index == m_serverKeyMap.end()) { @@ -170,6 +168,8 @@ CXWindowsSecondaryScreen::keyRepeat(KeyID key, // get the sequence of keys to simulate key repeat and the final // modifier state. + Keystrokes keys; + KeyCode keycode; m_mask = mapKey(keys, keycode, key, mask, kRepeat); if (keys.empty()) { return; @@ -180,15 +180,20 @@ CXWindowsSecondaryScreen::keyRepeat(KeyID key, return; } - // if we've seen this button (and we should have) then make sure - // we release the same key we pressed when we saw it. - if (index != m_serverKeyMap.end() && keycode != index->second) { + // if the keycode for the auto-repeat is not the same as for the + // initial press then mark the initial key as released and the new + // key as pressed. this can happen when we auto-repeat after a + // dead key. for example, a dead accent followed by 'a' will + // generate an 'a with accent' followed by a repeating 'a'. the + // keycodes for the two keysyms might be different. + if (keycode != index->second) { // replace key up with previous keycode but leave key down // alone so it uses the new keycode and store that keycode - // in the server key map. + // in the server key map. the key up is the first keystroke + // with the keycode returned by mapKey(). for (Keystrokes::iterator index2 = keys.begin(); index2 != keys.end(); ++index2) { - if (index2->m_keycode == index->second) { + if (index2->m_keycode == keycode) { index2->m_keycode = index->second; break; } @@ -214,15 +219,12 @@ void CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) { - Keystrokes keys; - KeyCode keycode; - // if we haven't seen this button go down then ignore it ServerKeyMap::iterator index = m_serverKeyMap.find(button); if (index == m_serverKeyMap.end()) { return; } - keycode = index->second; + KeyCode keycode = index->second; // check for ctrl+alt+del emulation if (key == kKeyDelete && @@ -232,6 +234,9 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, // just pass the key through } + // get the sequence of keys to simulate key release and the final + // modifier state. + Keystrokes keys; if (!((key == kKeyCapsLock && m_capsLockHalfDuplex) || (key == kKeyNumLock && m_numLockHalfDuplex))) { m_mask = mapKeyRelease(keys, keycode); @@ -241,13 +246,9 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, doKeystrokes(keys, 1); // note that key is now up + m_serverKeyMap.erase(index); m_keys[keycode] = false; m_fakeKeys[keycode] = false; - - // remove server key from map - if (index != m_serverKeyMap.end()) { - m_serverKeyMap.erase(index); - } } void @@ -798,6 +799,7 @@ CXWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys, KeyCode keycode) const if (i != m_keycodeToModifier.end()) { ModifierMask bit = (1 << i->second); if (getBits(m_toggleModifierMask, bit) != 0) { + // toggle keys modify the state on release return flipBits(m_mask, bit); } else { From b9193ae1bbc3d684ffe0f89b50459f28686d7396 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 5 Jul 2003 17:04:06 +0000 Subject: [PATCH 536/807] Fix for new template syntax. --- lib/server/CConfig.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index ea0fcd5e..28e2c5b4 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -27,6 +27,7 @@ class CConfig; namespace std { +template <> struct iterator_traits { typedef CString value_type; typedef ptrdiff_t difference_type; From 28427a0e9bec633c21bdfff76cc727178ebbce7c Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 5 Jul 2003 17:04:26 +0000 Subject: [PATCH 537/807] Keyboard fixes on win32. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 309 ++++++++++----------- lib/platform/CMSWindowsSecondaryScreen.h | 12 + 2 files changed, 155 insertions(+), 166 deletions(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index e8dfe395..d39e775d 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -70,6 +70,19 @@ // CMSWindowsSecondaryScreen // +// a list of modifier key info +const CMSWindowsSecondaryScreen::CModifierInfo + CMSWindowsSecondaryScreen::s_modifier[] = { + { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, + { KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false }, + { KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false }, + // note -- no keys for KeyModifierMeta + { KeyModifierSuper, VK_LWIN | 0x100, VK_RWIN | 0x100, false }, + { KeyModifierCapsLock, VK_CAPITAL, 0, true }, + { KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true }, + { KeyModifierScrollLock,VK_SCROLL, 0, true } +}; + CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( IScreenReceiver* receiver) : m_is95Family(CArchMiscWindows::isWindows95Family()), @@ -90,9 +103,6 @@ void CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask, KeyButton button) { - Keystrokes keys; - UINT virtualKey; - CLock lock(&m_mutex); m_screen->syncDesktop(); @@ -106,48 +116,49 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key, // get the sequence of keys to simulate key press and the final // modifier state. + Keystrokes keys; + UINT virtualKey; m_mask = mapKey(keys, virtualKey, key, mask, kPress); if (keys.empty()) { + // do nothing if there are no associated keys (i.e. lookup failed) return; } // generate key events doKeystrokes(keys, 1); - // note that key is now down - m_keys[virtualKey] |= 0x80; - m_fakeKeys[virtualKey] |= 0x80; - switch (virtualKey) { - case VK_LSHIFT: - case VK_RSHIFT: - m_keys[VK_SHIFT] |= 0x80; - m_fakeKeys[VK_SHIFT] |= 0x80; - break; + // do not record button down if button is 0 (invalid) + if (button != 0) { + // note that key is now down + m_serverKeyMap[button] = virtualKey; + m_keys[virtualKey] |= 0x80; + m_fakeKeys[virtualKey] |= 0x80; + switch (virtualKey) { + case VK_LSHIFT: + case VK_RSHIFT: + m_keys[VK_SHIFT] |= 0x80; + m_fakeKeys[VK_SHIFT] |= 0x80; + break; - case VK_LCONTROL: - case VK_RCONTROL: - m_keys[VK_CONTROL] |= 0x80; - m_fakeKeys[VK_CONTROL] |= 0x80; - break; + case VK_LCONTROL: + case VK_RCONTROL: + m_keys[VK_CONTROL] |= 0x80; + m_fakeKeys[VK_CONTROL] |= 0x80; + break; - case VK_LMENU: - case VK_RMENU: - m_keys[VK_MENU] |= 0x80; - m_fakeKeys[VK_MENU] |= 0x80; - break; + case VK_LMENU: + case VK_RMENU: + m_keys[VK_MENU] |= 0x80; + m_fakeKeys[VK_MENU] |= 0x80; + break; + } } - - // note which server key generated this key - m_serverKeyMap[button] = virtualKey; } void CMSWindowsSecondaryScreen::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button) { - Keystrokes keys; - UINT virtualKey; - CLock lock(&m_mutex); m_screen->syncDesktop(); @@ -159,20 +170,26 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key, // get the sequence of keys to simulate key repeat and the final // modifier state. + Keystrokes keys; + UINT virtualKey; m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); if (keys.empty()) { return; } - // if we've seen this button (and we should have) then make sure - // we release the same key we pressed when we saw it. - if (index != m_serverKeyMap.end() && virtualKey != index->second) { + // if the keycode for the auto-repeat is not the same as for the + // initial press then mark the initial key as released and the new + // key as pressed. this can happen when we auto-repeat after a + // dead key. for example, a dead accent followed by 'a' will + // generate an 'a with accent' followed by a repeating 'a'. the + // keycodes for the two keysyms might be different. + if (virtualKey != index->second) { // replace key up with previous keycode but leave key down // alone so it uses the new keycode and store that keycode // in the server key map. for (Keystrokes::iterator index2 = keys.begin(); index2 != keys.end(); ++index2) { - if (index2->m_virtualKey == index->second) { + if (index2->m_virtualKey == virtualKey) { index2->m_virtualKey = index->second; break; } @@ -195,12 +212,8 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key, } void -CMSWindowsSecondaryScreen::keyUp(KeyID key, - KeyModifierMask mask, KeyButton button) +CMSWindowsSecondaryScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) { - Keystrokes keys; - UINT virtualKey; - CLock lock(&m_mutex); m_screen->syncDesktop(); @@ -209,42 +222,18 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, if (index == m_serverKeyMap.end()) { return; } + UINT virtualKey = index->second; // get the sequence of keys to simulate key release and the final // modifier state. - m_mask = mapKey(keys, virtualKey, key, mask, kRelease); - - // if there are no keys to generate then we should at least generate - // a key release for the key we pressed. - if (keys.empty()) { - Keystroke keystroke; - virtualKey = index->second; - keystroke.m_virtualKey = virtualKey; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - } - - // if we've seen this button (and we should have) then make sure - // we release the same key we pressed when we saw it. - if (index != m_serverKeyMap.end() && virtualKey != index->second) { - // replace key up with previous virtual key - for (Keystrokes::iterator index2 = keys.begin(); - index2 != keys.end(); ++index2) { - if (index2->m_virtualKey == virtualKey) { - index2->m_virtualKey = index->second; - break; - } - } - - // use old virtual key - virtualKey = index->second; - } + Keystrokes keys; + m_mask = mapKeyRelease(keys, virtualKey); // generate key events doKeystrokes(keys, 1); // note that key is now up + m_serverKeyMap.erase(index); m_keys[virtualKey] &= ~0x80; m_fakeKeys[virtualKey] &= ~0x80; switch (virtualKey) { @@ -290,11 +279,6 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, } break; } - - // remove server key from map - if (index != m_serverKeyMap.end()) { - m_serverKeyMap.erase(index); - } } void @@ -1003,6 +987,47 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, return m_mask; } +KeyModifierMask +CMSWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys, + UINT virtualKey) const +{ + // add key release + Keystroke keystroke; + keystroke.m_virtualKey = virtualKey; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + + // if this is a modifier keycode then update the current modifier mask + const CModifierInfo* modifier = getModifierInfo(virtualKey); + if (modifier != NULL) { + if (modifier->m_isToggle) { + // toggle keys modify the state on release + return (m_mask ^ modifier->m_mask); + } + else { + // can't reset bit until all keys that set it are released. + // scan those keys to see if any (except virtualKey) are + // pressed. + bool down = false; + if (virtualKey != (modifier->m_virtualKey & 0xff) && + (m_keys[modifier->m_virtualKey & 0xff] & 0x80) != 0) { + down = true; + } + if (modifier->m_virtualKey2 != 0 && + virtualKey != (modifier->m_virtualKey2 & 0xff) && + (m_keys[modifier->m_virtualKey2 & 0xff] & 0x80) != 0) { + down = true; + } + if (!down) { + return (m_mask & ~modifier->m_mask); + } + } + } + + return m_mask; +} + UINT CMSWindowsSecondaryScreen::mapCharacter(Keystrokes& keys, char c, HKL hkl, @@ -1102,73 +1127,7 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, KeyModifierMask currentMask, KeyModifierMask desiredMask, EKeyAction action) const { - // a list of modifier key info - class CModifierInfo { - public: - KeyModifierMask m_mask; - UINT m_virtualKey; - UINT m_virtualKey2; - bool m_isToggle; - }; - static const CModifierInfo s_modifier[] = { - { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, - { KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false }, - { KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false }, - // note -- no keys for KeyModifierMeta - { KeyModifierSuper, VK_LWIN | 0x100, VK_RWIN | 0x100, false }, - { KeyModifierCapsLock, VK_CAPITAL, 0, true }, - { KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true }, - { KeyModifierScrollLock,VK_SCROLL, 0, true } - }; - static const unsigned int s_numModifiers = - sizeof(s_modifier) / sizeof(s_modifier[0]); - - // strip out extended key flag - UINT virtualKey2 = (virtualKey & ~0x100); - - // note if the key is a modifier - unsigned int modifierIndex; - switch (virtualKey2) { - case VK_SHIFT: - case VK_LSHIFT: - case VK_RSHIFT: - modifierIndex = 0; - break; - - case VK_CONTROL: - case VK_LCONTROL: - case VK_RCONTROL: - modifierIndex = 1; - break; - - case VK_MENU: - case VK_LMENU: - case VK_RMENU: - modifierIndex = 2; - break; - - case VK_LWIN: - case VK_RWIN: - modifierIndex = 3; - break; - - case VK_CAPITAL: - modifierIndex = 4; - break; - - case VK_NUMLOCK: - modifierIndex = 5; - break; - - case VK_SCROLL: - modifierIndex = 6; - break; - - default: - modifierIndex = s_numModifiers; - break; - } - const bool isModifier = (modifierIndex != s_numModifiers); + const CModifierInfo* modifier = getModifierInfo(virtualKey); // add the key events required to get to the desired modifier state. // also save the key events required to restore the current state. @@ -1176,7 +1135,9 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, // should not modify modifiers. Keystrokes undo; Keystroke keystroke; - if (desiredMask != currentMask && !isModifier) { + if (desiredMask != currentMask && modifier == NULL) { + const unsigned int s_numModifiers = sizeof(s_modifier) / + sizeof(s_modifier[0]); for (unsigned int i = 0; i < s_numModifiers; ++i) { KeyModifierMask bit = s_modifier[i].m_mask; if ((desiredMask & bit) != (currentMask & bit)) { @@ -1274,35 +1235,12 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, } // if the key is a modifier key then compute the modifier mask after - // this key is pressed. + // this key is pressed. toggle keys modify the state on release. + // other keys set the modifier bit on press. KeyModifierMask mask = currentMask; - if (isModifier && action != kRepeat) { - // toggle keys modify the state on release. other keys set - // the bit on press and clear the bit on release. - const CModifierInfo& modifier = s_modifier[modifierIndex]; - if (modifier.m_isToggle) { - if (action == kRelease) { - mask ^= modifier.m_mask; - } - } - else if (action == kPress) { - mask |= modifier.m_mask; - } - else { - // can't reset bit until all keys that set it are released. - // scan those keys to see if any are pressed. - bool down = false; - if (virtualKey2 != (modifier.m_virtualKey & 0xff) && - (m_keys[modifier.m_virtualKey & 0xff] & 0x80) != 0) { - down = true; - } - if (modifier.m_virtualKey2 != 0 && - virtualKey2 != (modifier.m_virtualKey2 & 0xff) && - (m_keys[modifier.m_virtualKey2 & 0xff] & 0x80) != 0) { - down = true; - } - if (!down) - mask &= ~modifier.m_mask; + if (action == kPress) { + if (modifier != NULL && !modifier->m_isToggle) { + mask |= modifier->m_mask; } } @@ -1344,6 +1282,45 @@ CMSWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) } } +const CMSWindowsSecondaryScreen::CModifierInfo* +CMSWindowsSecondaryScreen::getModifierInfo(UINT virtualKey) const +{ + // note if the key is a modifier. strip out extended key flag from + // virtual key before lookup. + switch (virtualKey & ~0x100) { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + return s_modifier + 0; + + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + return s_modifier + 1; + + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + return s_modifier + 2; + + case VK_LWIN: + case VK_RWIN: + return s_modifier + 3; + + case VK_CAPITAL: + return s_modifier + 4; + + case VK_NUMLOCK: + return s_modifier + 5; + + case VK_SCROLL: + return s_modifier + 6; + + default: + return NULL; + } +} + void CMSWindowsSecondaryScreen::releaseKeys() { diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index f56c7c40..ed1bdf98 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -85,6 +85,13 @@ private: bool m_press; bool m_repeat; }; + class CModifierInfo { + public: + KeyModifierMask m_mask; + UINT m_virtualKey; + UINT m_virtualKey2; + bool m_isToggle; + }; typedef std::vector Keystrokes; typedef std::map ServerKeyMap; @@ -103,6 +110,7 @@ private: bool press, DWORD* data) const; KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, KeyModifierMask, EKeyAction) const; + KeyModifierMask mapKeyRelease(Keystrokes& keys, UINT virtualKey) const; UINT mapCharacter(Keystrokes& keys, char c, HKL hkl, KeyModifierMask currentMask, @@ -114,6 +122,7 @@ private: KeyModifierMask desiredMask, EKeyAction action) const; void doKeystrokes(const Keystrokes&, SInt32 count); + const CModifierInfo* getModifierInfo(UINT virtualKey) const; void toggleKey(UINT virtualKey, KeyModifierMask mask); UINT virtualKeyToScanCode(UINT& virtualKey) const; @@ -149,6 +158,9 @@ private: // map server key buttons to local virtual keys ServerKeyMap m_serverKeyMap; + + // modifier table + static const CModifierInfo s_modifier[]; }; #endif From 2e741b7d96bbf8189b4abcc74126e609e1aac745 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 5 Jul 2003 17:05:12 +0000 Subject: [PATCH 538/807] Fix to avoid warping mouse until client successfully connects to the server. --- lib/synergy/CSecondaryScreen.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp index e7e72e70..3bf40d46 100644 --- a/lib/synergy/CSecondaryScreen.cpp +++ b/lib/synergy/CSecondaryScreen.cpp @@ -73,8 +73,18 @@ CSecondaryScreen::open() // open the screen getScreen()->open(); - // create and prepare our window + // create and prepare our window. pretend we're active so + // we don't try to show our window until later. + { + CLock lock(&m_mutex); + assert(m_active == false); + m_active = true; + } createWindow(); + { + CLock lock(&m_mutex); + m_active = false; + } // subclass hook onPostOpen(); From c325b923ea80d2c16d2042aef9356643fd34af05 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 5 Jul 2003 17:06:18 +0000 Subject: [PATCH 539/807] Change version to 1.0.11. Skipping version 1.0.10 because there have been too many major changes since 1.0.8. A new experimental release will provide a stable starting point for testing. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 15912330..cf9ceae7 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=0 -RELEASE_VERSION=9 +RELEASE_VERSION=11 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 6e38f86b..772796cf 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.9" +# define VERSION "1.0.11" #endif // important strings From f27fd7b02130d14f4ed5a442dcf0ba3736f7ec81 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 8 Jul 2003 18:40:46 +0000 Subject: [PATCH 540/807] Changed windows server to release ctrl and alt keys when it's sending a key that requires AltGr. That's because AltGr *is* ctrl and alt but AltGr should be seen on clients as mode switch without the ctrl and alt. I can't think of a better way to do this other than to not send modifier keystrokes to the clients at all. --- lib/platform/CMSWindowsPrimaryScreen.cpp | 407 +++++++++++++++-------- lib/platform/CMSWindowsPrimaryScreen.h | 3 +- 2 files changed, 262 insertions(+), 148 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 232561ed..1eec8050 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -485,10 +485,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); // process as if it were a key up + bool altgr; KeyModifierMask mask; KeyButton button = static_cast( (lParam & 0x00ff0000u) >> 16); - const KeyID key = mapKey(wParam, lParam, &mask); + const KeyID key = mapKey(wParam, lParam, &mask, &altgr); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); m_receiver->onKeyUp(key, mask, button); updateKey(wParam, false); @@ -502,10 +503,11 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); // process as if it were a key up + bool altgr; KeyModifierMask mask; KeyButton button = static_cast( (lParam & 0x00ff0000u) >> 16); - const KeyID key = mapKey(wParam, lParam, &mask); + const KeyID key = mapKey(wParam, lParam, &mask, &altgr); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); m_receiver->onKeyUp(key, mask, button); updateKey(wParam, false); @@ -535,22 +537,111 @@ CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) } // process key normally + bool altgr; KeyModifierMask mask; - const KeyID key = mapKey(wParam, lParam, &mask); + const KeyID key = mapKey(wParam, lParam, &mask, &altgr); KeyButton button = static_cast( (lParam & 0x00ff0000u) >> 16); if (key != kKeyNone && key != kKeyMultiKey) { if ((lParam & 0x80000000) == 0) { // key press - const bool wasDown = ((lParam & 0x40000000) != 0); - const SInt32 repeat = (SInt32)(lParam & 0xffff); - if (repeat >= 2 || wasDown) { + + // if AltGr required for this key then make sure + // the ctrl and alt keys are *not* down on the + // client. windows simulates AltGr with ctrl and + // alt for some inexplicable reason and clients + // will get confused if they see mode switch and + // ctrl and alt. we'll also need to put ctrl and + // alt back the way there were after we simulate + // the key. + bool ctrlL = ((m_keys[VK_LCONTROL] & 0x80) != 0); + bool ctrlR = ((m_keys[VK_RCONTROL] & 0x80) != 0); + bool altL = ((m_keys[VK_LMENU] & 0x80) != 0); + bool altR = ((m_keys[VK_RMENU] & 0x80) != 0); + if (altgr) { + KeyID key; + KeyButton button; + KeyModifierMask mask2 = (mask & + ~(KeyModifierControl | + KeyModifierAlt | + KeyModifierModeSwitch)); + if (ctrlL) { + key = kKeyControl_L; + button = mapKeyToScanCode(VK_LCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + if (ctrlR) { + key = kKeyControl_R; + button = mapKeyToScanCode(VK_RCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + if (altL) { + key = kKeyAlt_L; + button = mapKeyToScanCode(VK_LMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + if (altR) { + key = kKeyAlt_R; + button = mapKeyToScanCode(VK_RMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyUp(key, mask2, button); + } + } + + // send key + const bool wasDown = ((lParam & 0x40000000) != 0); + SInt32 repeat = (SInt32)(lParam & 0xffff); + if (!wasDown) { + LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_receiver->onKeyDown(key, mask, button); + if (repeat > 0) { + --repeat; + } + } + if (repeat >= 1) { LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button)); m_receiver->onKeyRepeat(key, mask, repeat, button); } - else { - LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_receiver->onKeyDown(key, mask, button); + + // restore ctrl and alt state + if (altgr) { + KeyID key; + KeyButton button; + KeyModifierMask mask2 = (mask & + ~(KeyModifierControl | + KeyModifierAlt | + KeyModifierModeSwitch)); + if (ctrlL) { + key = kKeyControl_L; + button = mapKeyToScanCode(VK_LCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierControl; + } + if (ctrlR) { + key = kKeyControl_R; + button = mapKeyToScanCode(VK_RCONTROL, VK_CONTROL); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierControl; + } + if (altL) { + key = kKeyAlt_L; + button = mapKeyToScanCode(VK_LMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierAlt; + } + if (altR) { + key = kKeyAlt_R; + button = mapKeyToScanCode(VK_RMENU, VK_MENU); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_receiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierAlt; + } } } else { @@ -1283,7 +1374,8 @@ KeyID CMSWindowsPrimaryScreen::mapKey( WPARAM vkCode, LPARAM info, - KeyModifierMask* maskOut) + KeyModifierMask* maskOut, + bool* altgr) { // note: known microsoft bugs // Q72583 -- MapVirtualKey() maps keypad keys incorrectly @@ -1292,166 +1384,177 @@ CMSWindowsPrimaryScreen::mapKey( // SEPARATOR, MULTIPLY, SUBTRACT, ADD assert(maskOut != NULL); - - // map modifier key - KeyModifierMask mask = 0; - if (((m_keys[VK_LSHIFT] | - m_keys[VK_RSHIFT] | - m_keys[VK_SHIFT]) & 0x80) != 0) { - mask |= KeyModifierShift; - } - if (((m_keys[VK_LCONTROL] | - m_keys[VK_RCONTROL] | - m_keys[VK_CONTROL]) & 0x80) != 0) { - mask |= KeyModifierControl; - } - if ((m_keys[VK_RMENU] & 0x80) != 0) { - // right alt => AltGr on windows - mask |= KeyModifierModeSwitch; - } - else if (((m_keys[VK_LMENU] | - m_keys[VK_MENU]) & 0x80) != 0) { - mask |= KeyModifierAlt; - } - if (((m_keys[VK_LWIN] | - m_keys[VK_RWIN]) & 0x80) != 0) { - mask |= KeyModifierSuper; - } - if ((m_keys[VK_CAPITAL] & 0x01) != 0) { - mask |= KeyModifierCapsLock; - } - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { - mask |= KeyModifierNumLock; - } - if ((m_keys[VK_SCROLL] & 0x01) != 0) { - mask |= KeyModifierScrollLock; - } - // ctrl+alt => AltGr on windows -/* don't convert ctrl+alt to mode switch. if we do that then we can - * never send ctrl+alt+[key] from windows to some platform that - * doesn't treat ctrl+alt as mode switch (i.e. all other platforms). - * instead, let windows clients automatically treat ctrl+alt as - * AltGr and let other clients use ctrl+alt as is. the right alt - * key serves as a mode switch key. - if ((mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - mask |= KeyModifierModeSwitch; - mask &= ~(KeyModifierControl | KeyModifierAlt); - } -*/ - *maskOut = mask; - LOG((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask)); + assert(altgr != NULL); // get the scan code and the extended keyboard flag UINT scanCode = static_cast((info & 0x00ff0000u) >> 16); int extended = ((info & 0x01000000) == 0) ? 0 : 1; - LOG((CLOG_DEBUG1 "key vk=%d ext=%d scan=%d", vkCode, extended, scanCode)); + LOG((CLOG_DEBUG1 "key vk=%d info=0x%08x ext=%d scan=%d", vkCode, info, extended, scanCode)); // handle some keys via table lookup + char c = 0; KeyID id = g_virtualKey[vkCode][extended]; - if (id != kKeyNone) { - return id; - } + if (id == kKeyNone) { + // not in table - // save the control state then clear it. ToAscii() maps ctrl+letter - // to the corresponding control code and ctrl+backspace to delete. - // we don't want that translation so we clear the control modifier - // state. however, if we want to simulate AltGr (which is ctrl+alt) - // then we must not clear it. - BYTE lControl = m_keys[VK_LCONTROL]; - BYTE rControl = m_keys[VK_RCONTROL]; - BYTE control = m_keys[VK_CONTROL]; - BYTE lMenu = m_keys[VK_LMENU]; - BYTE menu = m_keys[VK_MENU]; - if ((mask & KeyModifierModeSwitch) == 0) { - m_keys[VK_LCONTROL] = 0; - m_keys[VK_RCONTROL] = 0; - m_keys[VK_CONTROL] = 0; - } - else { - m_keys[VK_LCONTROL] = 0x80; - m_keys[VK_CONTROL] = 0x80; - m_keys[VK_LMENU] = 0x80; - m_keys[VK_MENU] = 0x80; - } + // save the control state then clear it. ToAscii() maps ctrl+letter + // to the corresponding control code and ctrl+backspace to delete. + // we don't want that translation so we clear the control modifier + // state. however, if we want to simulate AltGr (which is ctrl+alt) + // then we must not clear it. + BYTE lControl = m_keys[VK_LCONTROL]; + BYTE rControl = m_keys[VK_RCONTROL]; + BYTE control = m_keys[VK_CONTROL]; + BYTE lMenu = m_keys[VK_LMENU]; + BYTE menu = m_keys[VK_MENU]; + if ((control & 0x80) == 0 || (menu & 0x80) == 0) { + m_keys[VK_LCONTROL] = 0; + m_keys[VK_RCONTROL] = 0; + m_keys[VK_CONTROL] = 0; + } + else { + m_keys[VK_LCONTROL] = 0x80; + m_keys[VK_CONTROL] = 0x80; + m_keys[VK_LMENU] = 0x80; + m_keys[VK_MENU] = 0x80; + } - // convert to ascii - WORD ascii; - int result = ToAscii(vkCode, scanCode, m_keys, &ascii, 0); + // convert to ascii + WORD ascii; + int result = ToAscii(vkCode, scanCode, m_keys, &ascii, 0); - // restore control state - m_keys[VK_LCONTROL] = lControl; - m_keys[VK_RCONTROL] = rControl; - m_keys[VK_CONTROL] = control; - m_keys[VK_LMENU] = lMenu; - m_keys[VK_MENU] = menu; + // restore control state + m_keys[VK_LCONTROL] = lControl; + m_keys[VK_RCONTROL] = rControl; + m_keys[VK_CONTROL] = control; + m_keys[VK_LMENU] = lMenu; + m_keys[VK_MENU] = menu; - // if result is less than zero then it was a dead key. leave it - // there. - if (result < 0) { - return kKeyMultiKey; - } + // if result is less than zero then it was a dead key. leave it + // there. + if (result < 0) { + id = kKeyMultiKey; + } - // if result is 1 then the key was succesfully converted - else if (result == 1) { - if (ascii >= 0x80) { - // character is not really ASCII. instead it's some - // character in the current ANSI code page. try to - // convert that to a Unicode character. if we fail - // then use the single byte character as is. - char src = static_cast(ascii & 0xff); - wchar_t unicode; - if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, - &src, 1, &unicode, 1) > 0) { - return static_cast(unicode); + // if result is 1 then the key was succesfully converted + else if (result == 1) { + c = static_cast(ascii & 0xff); + if (ascii >= 0x80) { + // character is not really ASCII. instead it's some + // character in the current ANSI code page. try to + // convert that to a Unicode character. if we fail + // then use the single byte character as is. + char src = c; + wchar_t unicode; + if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, + &src, 1, &unicode, 1) > 0) { + id = static_cast(unicode); + } + else { + id = static_cast(ascii & 0x00ff); + } + } + else { + id = static_cast(ascii & 0x00ff); } } - return static_cast(ascii & 0x00ff); + + // if result is 2 then a previous dead key could not be composed. + else if (result == 2) { + // if the two characters are the same and this is a key release + // then this event is the dead key being released. we put the + // dead key back in that case, otherwise we discard both key + // events because we can't compose the character. alternatively + // we could generate key events for both keys. + if (((ascii & 0xff00) >> 8) != (ascii & 0x00ff) || + (info & 0x80000000) == 0) { + // cannot compose key + return kKeyNone; + } + + // get the scan code of the dead key and the shift state + // required to generate it. + vkCode = VkKeyScan(static_cast(ascii & 0x00ff)); + + // set shift state required to generate key + BYTE keys[256]; + memset(keys, 0, sizeof(keys)); + if (vkCode & 0x0100) { + keys[VK_SHIFT] = 0x80; + } + if (vkCode & 0x0200) { + keys[VK_CONTROL] = 0x80; + } + if (vkCode & 0x0400) { + keys[VK_MENU] = 0x80; + } + + // strip shift state off of virtual key code + vkCode &= 0x00ff; + + // get the scan code for the key + scanCode = MapVirtualKey(vkCode, 0); + + // put it back + ToAscii(vkCode, scanCode, keys, &ascii, 0); + id = kKeyMultiKey; + } } - // if result is 2 then a previous dead key could not be composed. - else if (result == 2) { - // if the two characters are the same and this is a key release - // then this event is the dead key being released. we put the - // dead key back in that case, otherwise we discard both key - // events because we can't compose the character. alternatively - // we could generate key events for both keys. - if (((ascii & 0xff00) >> 8) != (ascii & 0x00ff) || - (info & 0x80000000) == 0) { - // cannot compose key - return kKeyNone; + // set mask + *altgr = false; + if (id != kKeyNone && id != kKeyMultiKey && c != 0) { + // note if key requires AltGr + SHORT virtualKeyAndModifierState = VkKeyScan(c); + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + if ((modifierState & 6) == 6) { + // key requires ctrl and alt == AltGr + *altgr = true; } - // get the scan code of the dead key and the shift state - // required to generate it. - vkCode = VkKeyScan(static_cast(ascii & 0x00ff)); - - // set shift state required to generate key - BYTE keys[256]; - memset(keys, 0, sizeof(keys)); - if (vkCode & 0x0100) { - keys[VK_SHIFT] = 0x80; + // map modifier key + KeyModifierMask mask = 0; + if (((m_keys[VK_LSHIFT] | + m_keys[VK_RSHIFT] | + m_keys[VK_SHIFT]) & 0x80) != 0) { + mask |= KeyModifierShift; } - if (vkCode & 0x0200) { - keys[VK_CONTROL] = 0x80; + if (*altgr) { + mask |= KeyModifierModeSwitch; } - if (vkCode & 0x0400) { - keys[VK_MENU] = 0x80; + else { + if (((m_keys[VK_LCONTROL] | + m_keys[VK_RCONTROL] | + m_keys[VK_CONTROL]) & 0x80) != 0) { + mask |= KeyModifierControl; + } + if (((m_keys[VK_LMENU] | + m_keys[VK_RMENU] | + m_keys[VK_MENU]) & 0x80) != 0) { + mask |= KeyModifierAlt; + } } - - // strip shift state off of virtual key code - vkCode &= 0x00ff; - - // get the scan code for the key - scanCode = MapVirtualKey(vkCode, 0); - - // put it back - ToAscii(vkCode, scanCode, keys, &ascii, 0); - return kKeyMultiKey; + if (((m_keys[VK_LWIN] | + m_keys[VK_RWIN]) & 0x80) != 0) { + mask |= KeyModifierSuper; + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { + mask |= KeyModifierCapsLock; + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { + mask |= KeyModifierNumLock; + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { + mask |= KeyModifierScrollLock; + } + *maskOut = mask; + } + else { + // don't care + *maskOut = 0; } - // cannot convert key - return kKeyNone; + return id; } ButtonID @@ -1679,3 +1782,13 @@ CMSWindowsPrimaryScreen::isModifier(UINT vkCode) const return false; } } + +KeyButton +CMSWindowsPrimaryScreen::mapKeyToScanCode(UINT vk1, UINT vk2) const +{ + KeyButton button = static_cast(MapVirtualKey(vk1, 0)); + if (button == 0) { + button = static_cast(MapVirtualKey(vk2, 0)); + } + return button; +} diff --git a/lib/platform/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h index a80ddbe7..c318d12c 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.h +++ b/lib/platform/CMSWindowsPrimaryScreen.h @@ -87,10 +87,11 @@ private: // key and button queries KeyID mapKey(WPARAM keycode, LPARAM info, - KeyModifierMask* maskOut); + KeyModifierMask* maskOut, bool* altgr); ButtonID mapButton(WPARAM msg, LPARAM button) const; void updateKey(UINT vkCode, bool press); bool isModifier(UINT vkCode) const; + KeyButton mapKeyToScanCode(UINT vk1, UINT vk2) const; private: IPrimaryScreenReceiver* m_receiver; From 476faea8abfe28c5ac221e19e3c5e8a24a894007 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 12 Jul 2003 17:57:31 +0000 Subject: [PATCH 541/807] Prevent INFO level log messages when client is repeatedly trying to connect. This prevents a log from filling up while the client can't connect for no useful reason. Also removed --camp option and cleaned up handling of client connection. Users must now use --restart instead of --camp. --- cmd/synergyc/synergyc.cpp | 13 ++--- lib/client/CClient.cpp | 85 +++++++++++++------------------ lib/client/CClient.h | 10 ---- lib/platform/CMSWindowsScreen.cpp | 10 ++-- lib/platform/CXWindowsScreen.cpp | 2 +- 5 files changed, 43 insertions(+), 77 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 7acd28bf..fd61bab7 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -66,7 +66,6 @@ public: m_backend(false), m_restartable(true), m_daemon(true), - m_camp(true), m_logFilter(NULL) { s_instance = this; } ~CArgs() { s_instance = NULL; } @@ -77,7 +76,6 @@ public: bool m_backend; bool m_restartable; bool m_daemon; - bool m_camp; const char* m_logFilter; CString m_name; CNetworkAddress m_serverAddress; @@ -168,7 +166,6 @@ realMain(void) try { // create client s_client = new CClient(ARG->m_name); - s_client->camp(ARG->m_camp); s_client->setAddress(ARG->m_serverAddress); s_client->setScreenFactory(new CSecondaryScreenFactory); s_client->setSocketFactory(new CTCPSocketFactory); @@ -212,6 +209,7 @@ realMain(void) catch (XScreenUnavailable& e) { // wait before retrying if we're going to retry if (ARG->m_restartable) { + LOG((CLOG_DEBUG "waiting %.0f seconds to retry", e.getRetryTime())); ARCH->sleep(e.getRetryTime()); } else { @@ -320,9 +318,6 @@ help() "\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" -"* --camp keep attempting to connect to the server until\n" -" successful.\n" -" --no-camp do not camp.\n" " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" @@ -394,13 +389,11 @@ parse(int argc, const char* const* argv) } else if (isArg(i, argc, argv, NULL, "--camp")) { - // enable camping - ARG->m_camp = true; + // ignore -- included for backwards compatibility } else if (isArg(i, argc, argv, NULL, "--no-camp")) { - // disable camping - ARG->m_camp = false; + // ignore -- included for backwards compatibility } else if (isArg(i, argc, argv, "-f", "--no-daemon")) { diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 82f73cd7..8ada1111 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -46,7 +46,6 @@ CClient::CClient(const CString& clientName) : m_name(clientName), m_screen(NULL), m_server(NULL), - m_camp(false), m_screenFactory(NULL), m_socketFactory(NULL), m_streamFilterFactory(NULL), @@ -65,13 +64,6 @@ CClient::~CClient() delete m_streamFilterFactory; } -void -CClient::camp(bool on) -{ - CLock lock(&m_mutex); - m_camp = on; -} - void CClient::setAddress(const CNetworkAddress& serverAddress) { @@ -217,14 +209,14 @@ CClient::open() { // open the screen try { - LOG((CLOG_INFO "opening screen")); + LOG((CLOG_DEBUG "opening screen")); openSecondaryScreen(); setStatus(kNotRunning); } catch (XScreenOpenFailure& e) { // can't open screen setStatus(kError, e.what()); - LOG((CLOG_INFO "failed to open screen")); + LOG((CLOG_DEBUG "failed to open screen")); throw; } } @@ -245,7 +237,7 @@ CClient::mainLoop() try { setStatus(kNotRunning); - LOG((CLOG_NOTE "starting client \"%s\"", m_name.c_str())); + LOG((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); // start server interactions { @@ -259,7 +251,7 @@ CClient::mainLoop() // clean up deleteSession(); - LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); } catch (XMT& e) { LOG((CLOG_ERR "client error: %s", e.what())); @@ -267,7 +259,7 @@ CClient::mainLoop() // clean up deleteSession(); - LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); throw; } catch (XBase& e) { @@ -276,7 +268,7 @@ CClient::mainLoop() // clean up deleteSession(); - LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); CLock lock(&m_mutex); m_rejected = false; } @@ -285,16 +277,16 @@ CClient::mainLoop() // clean up deleteSession(); - LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); throw; } catch (...) { - LOG((CLOG_DEBUG "unknown client error")); + LOG((CLOG_ERR "client error: ")); setStatus(kError); // clean up deleteSession(); - LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); throw; } } @@ -303,7 +295,7 @@ void CClient::close() { closeSecondaryScreen(); - LOG((CLOG_INFO "closed screen")); + LOG((CLOG_DEBUG "closed screen")); } void @@ -587,40 +579,21 @@ CClient::runServer() CServerProxy* proxy = NULL; bool timedOut; try { - for (;;) { - try { - // allow connect this much time to succeed - CTimerThread timer(15.0, &timedOut); + // allow connect and handshake this much time to succeed + CTimerThread timer(15.0, &timedOut); - // create socket and attempt to connect to server - LOG((CLOG_DEBUG1 "connecting to server")); - if (m_socketFactory != NULL) { - socket = m_socketFactory->create(); - } - assert(socket != NULL); - socket->connect(m_serverAddress); - LOG((CLOG_INFO "connected to server")); - break; - } - catch (XSocketConnect& e) { - setStatus(kError, e.what()); - LOG((CLOG_DEBUG "failed to connect to server: %s", e.what())); - - // failed to connect. if not camping then rethrow. - if (!m_camp) { - throw; - } - - // we're camping. wait a bit before retrying - ARCH->sleep(15.0); - } + // create socket and attempt to connect to server + LOG((CLOG_DEBUG1 "connecting to server")); + if (m_socketFactory != NULL) { + socket = m_socketFactory->create(); } + assert(socket != NULL); + socket->connect(m_serverAddress); // create proxy + LOG((CLOG_INFO "connected to server")); LOG((CLOG_DEBUG1 "negotiating with server")); proxy = handshakeServer(socket); - CLock lock(&m_mutex); - m_server = proxy; } catch (XThread&) { if (timedOut) { @@ -630,24 +603,37 @@ CClient::runServer() else { // cancelled by some thread other than the timer } + delete proxy; delete socket; throw; } + catch (XSocketConnect& e) { + LOG((CLOG_ERR "connection failed: %s", e.what())); + setStatus(kError, e.what()); + delete socket; + return; + } catch (XBase& e) { LOG((CLOG_ERR "connection failed: %s", e.what())); setStatus(kError, e.what()); - LOG((CLOG_DEBUG "disconnecting from server")); + LOG((CLOG_INFO "disconnecting from server")); delete socket; return; } catch (...) { LOG((CLOG_ERR "connection failed: ")); setStatus(kError); - LOG((CLOG_DEBUG "disconnecting from server")); + LOG((CLOG_INFO "disconnecting from server")); delete socket; return; } + // saver server proxy object + { + CLock lock(&m_mutex); + m_server = proxy; + } + try { // prepare for remote control m_screen->remoteControl(); @@ -709,9 +695,6 @@ CClient::handshakeServer(IDataSocket* socket) CServerProxy* proxy = NULL; try { - // give handshake some time - CTimerThread timer(30.0); - // wait for hello from server LOG((CLOG_DEBUG1 "wait for hello")); SInt16 major, minor; diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 61efddaa..5647e7e3 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -54,15 +54,6 @@ public: //! @name manipulators //@{ - //! Set camping state - /*! - Turns camping on or off. When camping the client will keep - trying to connect to the server until it succeeds. This - is useful if the client may start before the server. Do - not call this while in mainLoop(). - */ - void camp(bool on); - //! Set server address /*! Sets the server's address that the client should connect to. @@ -196,7 +187,6 @@ private: CSecondaryScreen* m_screen; IScreenReceiver* m_server; CNetworkAddress m_serverAddress; - bool m_camp; ISecondaryScreenFactory* m_screenFactory; ISocketFactory* m_socketFactory; IStreamFilterFactory* m_streamFilterFactory; diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 5facb08b..e42e91b1 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -441,7 +441,7 @@ CMSWindowsScreen::updateScreenShape() m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); - LOG((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); // check for multiple monitors m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || @@ -508,7 +508,7 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) // if the desktop was inaccessible then notify the // event handler of that. if (m_inaccessibleDesktop) { - LOG((CLOG_INFO "desktop is now accessible")); + LOG((CLOG_DEBUG "desktop is now accessible")); m_inaccessibleDesktop = false; m_eventHandler->onAccessibleDesktop(); } @@ -516,7 +516,7 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event) else if (!m_inaccessibleDesktop) { // the desktop has become inaccessible m_inaccessibleDesktop = true; - LOG((CLOG_INFO "desktop is now inaccessible")); + LOG((CLOG_DEBUG "desktop is now inaccessible")); } } @@ -674,7 +674,7 @@ CMSWindowsScreen::switchDesktop(HDESK desk) // if no new desktop then we're done if (desk == NULL) { - LOG((CLOG_INFO "disconnecting desktop")); + LOG((CLOG_DEBUG "disconnecting desktop")); return true; } @@ -730,7 +730,7 @@ CMSWindowsScreen::switchDesktop(HDESK desk) // save new desktop m_desk = desk; m_deskName = getDesktopName(m_desk); - LOG((CLOG_INFO "switched to desktop \"%s\"", m_deskName.c_str())); + LOG((CLOG_DEBUG "switched to desktop \"%s\"", m_deskName.c_str())); // let client prepare the window m_eventHandler->postCreateWindow(m_window); diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 964fe739..870da6dc 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -590,7 +590,7 @@ CXWindowsScreen::updateScreenShape() m_y = 0; m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); - LOG((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); // get center of default screen m_xCenter = m_x + (m_w >> 1); From faff28de44bb635fe1103216996de46625ca0e99 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 12 Jul 2003 18:13:36 +0000 Subject: [PATCH 542/807] Added ignoreNumLock boolean per-screen option. When true, NumLock is ignored on that client (it has no effect on the server). This is useful for keyboards that don't have separate number pads and the user often uses the client's keyboard directly, when turning on NumLock interferes with normal typing. --- lib/client/CServerProxy.cpp | 29 +++++++++++++++++++++++++++++ lib/client/CServerProxy.h | 1 + lib/server/CConfig.cpp | 10 +++++++++- lib/synergy/OptionTypes.h | 1 + 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 435cdc55..9b242e7d 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -445,6 +445,11 @@ CServerProxy::enter() m_seqNum = seqNum; } + // ignore num lock if so desired + if (m_ignoreNumLock) { + mask &= ~KeyModifierNumLock; + } + // forward getClient()->enter(x, y, seqNum, static_cast(mask), false); } @@ -520,6 +525,12 @@ CServerProxy::keyDown() mask2 != static_cast(mask)) LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + // ignore num lock if so desired + if (id2 == kKeyNumLock && m_ignoreNumLock) { + LOG((CLOG_DEBUG1 "ignoring num lock")); + return; + } + // forward getClient()->keyDown(id2, mask2, button); } @@ -544,6 +555,12 @@ CServerProxy::keyRepeat() mask2 != static_cast(mask)) LOG((CLOG_DEBUG1 "key repeat translated to id=%d, mask=0x%04x", id2, mask2)); + // ignore num lock if so desired + if (id2 == kKeyNumLock && m_ignoreNumLock) { + LOG((CLOG_DEBUG1 "ignoring num lock")); + return; + } + // forward getClient()->keyRepeat(id2, mask2, count, button); } @@ -567,6 +584,12 @@ CServerProxy::keyUp() mask2 != static_cast(mask)) LOG((CLOG_DEBUG1 "key up translated to id=%d, mask=0x%04x", id2, mask2)); + // ignore num lock if so desired + if (id2 == kKeyNumLock && m_ignoreNumLock) { + LOG((CLOG_DEBUG1 "ignoring num lock")); + return; + } + // forward getClient()->keyUp(id2, mask2, button); } @@ -684,6 +707,9 @@ CServerProxy::resetOptions() if (m_heartRate >= 0.0) { CProtocolUtil::writef(getOutputStream(), kMsgCNoop); } + + // don't ignore num lock + m_ignoreNumLock = false; } void @@ -726,6 +752,9 @@ CServerProxy::setOptions() CProtocolUtil::writef(getOutputStream(), kMsgCNoop); } } + else if (options[i] == kOptionIgnoreNumLock) { + m_ignoreNumLock = true; + } if (id != kKeyModifierIDNull) { m_modifierTranslationTable[id] = static_cast(options[i + 1]); diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index cbb58f1b..423cdf56 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -128,6 +128,7 @@ private: SInt32 m_xMouse, m_yMouse; bool m_ignoreMouse; + bool m_ignoreNumLock; KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; double m_heartRate; diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 89417b28..04a2dc72 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -646,6 +646,9 @@ CConfig::getOptionName(OptionID id) if (id == kOptionXTestXineramaUnaware) { return "xtestIsXineramaUnaware"; } + if (id == kOptionIgnoreNumLock) { + return "ignoreNumLock"; + } return NULL; } @@ -655,7 +658,8 @@ CConfig::getOptionValue(OptionID id, OptionValue value) if (id == kOptionHalfDuplexCapsLock || id == kOptionHalfDuplexNumLock || id == kOptionScreenSaverSync || - id == kOptionXTestXineramaUnaware) { + id == kOptionXTestXineramaUnaware || + id == kOptionIgnoreNumLock) { return (value != 0) ? "true" : "false"; } if (id == kOptionModifierMapForShift || @@ -891,6 +895,10 @@ CConfig::readSectionScreens(std::istream& s) addOption(screen, kOptionXTestXineramaUnaware, parseBoolean(value)); } + else if (name == "ignoreNumLock") { + addOption(screen, kOptionIgnoreNumLock, + parseBoolean(value)); + } else { // unknown argument throw XConfigRead("unknown argument"); diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index aaefe7fa..27e1f837 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -55,6 +55,7 @@ static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); +static const OptionID kOptionIgnoreNumLock = OPTION_CODE("IGNL"); //@} #undef OPTION_CODE From c22059b433460e31c10b5680c29d780dcea2a56a Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 12 Jul 2003 22:41:07 +0000 Subject: [PATCH 543/807] Win32 launcher changes for ignore NumLock option. --- cmd/launcher/launcher.cpp | 19 +++++++++++++------ cmd/launcher/launcher.rc | 40 +++++++++++++++++++++------------------ cmd/launcher/resource.h | 3 ++- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 587a3b84..12d9543b 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -540,14 +540,10 @@ getCommandLine(HWND hwnd, bool testing) SetFocus(child); return CString(); } - - if (testing) { - cmdLine += " --no-camp"; - } } - // debug level - if (testing) { + // debug level. always include this. + if (true) { HWND child = getItem(hwnd, IDC_MAIN_DEBUG); DWORD debug = SendMessage(child, CB_GETCURSEL, 0, 0); cmdLine += " --debug "; @@ -828,6 +824,10 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) index->second != 0)); child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); index = info->m_options.find(kOptionHalfDuplexNumLock); + setItemChecked(child, (index != info->m_options.end() && + index->second != 0)); + child = getItem(hwnd, IDC_ADD_IGNORE_NUM_LOCK); + index = info->m_options.find(kOptionIgnoreNumLock); setItemChecked(child, (index != info->m_options.end() && index->second != 0)); @@ -939,6 +939,13 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) else { info->m_options.erase(kOptionHalfDuplexNumLock); } + child = getItem(hwnd, IDC_ADD_IGNORE_NUM_LOCK); + if (isItemChecked(child)) { + info->m_options[kOptionIgnoreNumLock] = 1; + } + else { + info->m_options.erase(kOptionIgnoreNumLock); + } // save modifier options child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 4f127dea..b811362c 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -94,8 +94,8 @@ BEGIN PUSHBUTTON "Adva&nced...",IDC_MAIN_ADVANCED,68,191,50,14 LTEXT "Automatic Startup:",IDC_STATIC,138,193,59,8 PUSHBUTTON "Con&figure...",IDC_MAIN_AUTOSTART,202,191,50,14 - LTEXT "Test &Debug Level:",IDC_STATIC,12,216,60,8 - COMBOBOX IDC_MAIN_DEBUG,78,213,61,60,CBS_DROPDOWNLIST | + LTEXT "Lo&gging Level:",IDC_STATIC,12,216,48,8 + COMBOBOX IDC_MAIN_DEBUG,68,213,61,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Sa&ve",IDC_MAIN_SAVE,75,241,50,14 DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,241,50,14 @@ -103,7 +103,7 @@ BEGIN PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 END -IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 236 +IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 270 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION CAPTION "Add Screen" FONT 8, "MS Sans Serif" @@ -113,31 +113,35 @@ BEGIN LTEXT "&Aliases:",IDC_STATIC,7,25,25,8 EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,40,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN - GROUPBOX "Options",IDC_STATIC,7,72,178,64 + GROUPBOX "Options",IDC_STATIC,7,72,178,97 LTEXT "If your Caps Lock or Num Lock keys behave strangely on this client screen then try turning the half-duplex options on and reconnect the client.", IDC_STATIC,13,82,165,25 CONTROL "Half-duplex &Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,13,110,165,10 CONTROL "Half-duplex &Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,13,122,165,10 - GROUPBOX "Modifiers",IDC_STATIC,7,139,178,65 - LTEXT "Shift",IDC_STATIC,13,155,15,8 - COMBOBOX IDC_ADD_MOD_SHIFT,37,152,48,60,CBS_DROPDOWNLIST | + LTEXT "Use this to leave the client's Num Lock state alone.\nThis is primarily useful on laptops.", + IDC_STATIC,13,136,166,17 + CONTROL "&Ignore Num Lock",IDC_ADD_IGNORE_NUM_LOCK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,155,71,10 + GROUPBOX "Modifiers",IDC_STATIC,7,172,178,65 + LTEXT "Shift",IDC_STATIC,13,188,15,8 + COMBOBOX IDC_ADD_MOD_SHIFT,37,185,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Ctrl",IDC_STATIC,13,170,11,8 - COMBOBOX IDC_ADD_MOD_CTRL,37,168,48,60,CBS_DROPDOWNLIST | + LTEXT "Ctrl",IDC_STATIC,13,203,11,8 + COMBOBOX IDC_ADD_MOD_CTRL,37,201,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Alt",IDC_STATIC,13,186,9,8 - COMBOBOX IDC_ADD_MOD_ALT,37,184,48,60,CBS_DROPDOWNLIST | + LTEXT "Alt",IDC_STATIC,13,219,9,8 + COMBOBOX IDC_ADD_MOD_ALT,37,217,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Meta",IDC_STATIC,101,154,17,8 - COMBOBOX IDC_ADD_MOD_META,125,152,48,60,CBS_DROPDOWNLIST | + LTEXT "Meta",IDC_STATIC,101,187,17,8 + COMBOBOX IDC_ADD_MOD_META,125,185,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Super",IDC_STATIC,101,170,20,8 - COMBOBOX IDC_ADD_MOD_SUPER,125,168,48,60,CBS_DROPDOWNLIST | + LTEXT "Super",IDC_STATIC,101,203,20,8 + COMBOBOX IDC_ADD_MOD_SUPER,125,201,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - DEFPUSHBUTTON "OK",IDOK,79,215,50,14 - PUSHBUTTON "Cancel",IDCANCEL,135,215,50,14 + DEFPUSHBUTTON "OK",IDOK,79,249,50,14 + PUSHBUTTON "Cancel",IDCANCEL,135,249,50,14 END IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54 @@ -247,7 +251,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 185 TOPMARGIN, 7 - BOTTOMMARGIN, 229 + BOTTOMMARGIN, 263 END IDD_WAIT, DIALOG diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index 2885b2f0..d108d170 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -99,6 +99,7 @@ #define IDC_ADD_MOD_SUPER 1047 #define IDC_GLOBAL_SCREENSAVER_SYNC 1047 #define IDC_ADVANCED_DEFAULTS 1049 +#define IDC_ADD_IGNORE_NUM_LOCK 1052 // Next default values for new objects // @@ -107,7 +108,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 110 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1052 +#define _APS_NEXT_CONTROL_VALUE 1053 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif From 745c5421bed8485cc9a0c7d3b56031c1a021fd09 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 Jul 2003 16:57:08 +0000 Subject: [PATCH 544/807] Changed XSync() to XFlush() in X windows secondary screen. This doesn't appear to have any negative consequences and may prevent synergy from freezing when some X client (probably the window manager) grabs the server. --- lib/platform/CXWindowsSecondaryScreen.cpp | 18 ++++++++++++------ lib/platform/CXWindowsSecondaryScreen.h | 2 ++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 4609068e..33fa1359 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -251,6 +251,12 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, m_fakeKeys[keycode] = false; } +void +CXWindowsSecondaryScreen::flush(Display* display) const +{ + XFlush(display); +} + void CXWindowsSecondaryScreen::mouseDown(ButtonID button) { @@ -258,7 +264,7 @@ CXWindowsSecondaryScreen::mouseDown(ButtonID button) if (xButton != 0) { CDisplayLock display(m_screen); XTestFakeButtonEvent(display, xButton, True, CurrentTime); - XSync(display, False); + flush(display); } } @@ -269,7 +275,7 @@ CXWindowsSecondaryScreen::mouseUp(ButtonID button) if (xButton != 0) { CDisplayLock display(m_screen); XTestFakeButtonEvent(display, xButton, False, CurrentTime); - XSync(display, False); + flush(display); } } @@ -300,7 +306,7 @@ CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) XTestFakeButtonEvent(display, xButton, True, CurrentTime); XTestFakeButtonEvent(display, xButton, False, CurrentTime); } - XSync(display, False); + flush(display); } void @@ -526,7 +532,7 @@ CXWindowsSecondaryScreen::destroyWindow() XTestGrabControl(display, False); // update - XSync(display, False); + flush(display); } } @@ -583,7 +589,7 @@ CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) XTestFakeMotionEvent(display, DefaultScreen(pDisplay), x, y, CurrentTime); } - XSync(display, False); + flush(display); } void @@ -1192,7 +1198,7 @@ CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) } // update - XSync(display, False); + flush(display); } CXWindowsSecondaryScreen::ModifierMask diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index 811aaa87..c2de53ee 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -113,6 +113,8 @@ private: typedef std::map KeySymsMap; typedef std::map ServerKeyMap; + void flush(Display*) const; + unsigned int mapButton(ButtonID button) const; ModifierMask mapKey(Keystrokes&, KeyCode&, KeyID, From f6683d3cadf6b2f8348da5c65428be8ff1cd7676 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 Jul 2003 17:03:41 +0000 Subject: [PATCH 545/807] Forgot to remove --camp and --no-camp from brief usage message. --- cmd/synergyc/synergyc.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index fd61bab7..ce2dca07 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -309,7 +309,6 @@ help() { LOG((CLOG_PRINT "Usage: %s" -" [--camp|--no-camp]" " [--daemon|--no-daemon]" " [--debug ]" " [--name ]" From be07a171cc152ee256b67626a0faea94d4578eab Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 Jul 2003 18:14:35 +0000 Subject: [PATCH 546/807] Removed ignoreNumLock option. It doesn't really seem to be necessary. --- cmd/launcher/launcher.cpp | 11 ----------- cmd/launcher/launcher.rc | 36 ++++++++++++++++-------------------- cmd/launcher/resource.h | 3 +-- lib/client/CServerProxy.cpp | 29 ----------------------------- lib/client/CServerProxy.h | 1 - lib/server/CConfig.cpp | 10 +--------- lib/synergy/OptionTypes.h | 1 - 7 files changed, 18 insertions(+), 73 deletions(-) diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 12d9543b..0c4b4597 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -824,10 +824,6 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) index->second != 0)); child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); index = info->m_options.find(kOptionHalfDuplexNumLock); - setItemChecked(child, (index != info->m_options.end() && - index->second != 0)); - child = getItem(hwnd, IDC_ADD_IGNORE_NUM_LOCK); - index = info->m_options.find(kOptionIgnoreNumLock); setItemChecked(child, (index != info->m_options.end() && index->second != 0)); @@ -939,13 +935,6 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) else { info->m_options.erase(kOptionHalfDuplexNumLock); } - child = getItem(hwnd, IDC_ADD_IGNORE_NUM_LOCK); - if (isItemChecked(child)) { - info->m_options[kOptionIgnoreNumLock] = 1; - } - else { - info->m_options.erase(kOptionIgnoreNumLock); - } // save modifier options child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index b811362c..a8bf8c7b 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -103,7 +103,7 @@ BEGIN PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 END -IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 270 +IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 236 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION CAPTION "Add Screen" FONT 8, "MS Sans Serif" @@ -113,35 +113,31 @@ BEGIN LTEXT "&Aliases:",IDC_STATIC,7,25,25,8 EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,40,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN - GROUPBOX "Options",IDC_STATIC,7,72,178,97 + GROUPBOX "Options",IDC_STATIC,7,72,178,64 LTEXT "If your Caps Lock or Num Lock keys behave strangely on this client screen then try turning the half-duplex options on and reconnect the client.", IDC_STATIC,13,82,165,25 CONTROL "Half-duplex &Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,13,110,165,10 CONTROL "Half-duplex &Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,13,122,165,10 - LTEXT "Use this to leave the client's Num Lock state alone.\nThis is primarily useful on laptops.", - IDC_STATIC,13,136,166,17 - CONTROL "&Ignore Num Lock",IDC_ADD_IGNORE_NUM_LOCK,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,14,155,71,10 - GROUPBOX "Modifiers",IDC_STATIC,7,172,178,65 - LTEXT "Shift",IDC_STATIC,13,188,15,8 - COMBOBOX IDC_ADD_MOD_SHIFT,37,185,48,60,CBS_DROPDOWNLIST | + GROUPBOX "Modifiers",IDC_STATIC,7,139,178,65 + LTEXT "Shift",IDC_STATIC,13,155,15,8 + COMBOBOX IDC_ADD_MOD_SHIFT,37,152,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Ctrl",IDC_STATIC,13,203,11,8 - COMBOBOX IDC_ADD_MOD_CTRL,37,201,48,60,CBS_DROPDOWNLIST | + LTEXT "Ctrl",IDC_STATIC,13,170,11,8 + COMBOBOX IDC_ADD_MOD_CTRL,37,168,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Alt",IDC_STATIC,13,219,9,8 - COMBOBOX IDC_ADD_MOD_ALT,37,217,48,60,CBS_DROPDOWNLIST | + LTEXT "Alt",IDC_STATIC,13,186,9,8 + COMBOBOX IDC_ADD_MOD_ALT,37,184,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Meta",IDC_STATIC,101,187,17,8 - COMBOBOX IDC_ADD_MOD_META,125,185,48,60,CBS_DROPDOWNLIST | + LTEXT "Meta",IDC_STATIC,101,154,17,8 + COMBOBOX IDC_ADD_MOD_META,125,152,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Super",IDC_STATIC,101,203,20,8 - COMBOBOX IDC_ADD_MOD_SUPER,125,201,48,60,CBS_DROPDOWNLIST | + LTEXT "Super",IDC_STATIC,101,170,20,8 + COMBOBOX IDC_ADD_MOD_SUPER,125,168,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - DEFPUSHBUTTON "OK",IDOK,79,249,50,14 - PUSHBUTTON "Cancel",IDCANCEL,135,249,50,14 + DEFPUSHBUTTON "OK",IDOK,79,215,50,14 + PUSHBUTTON "Cancel",IDCANCEL,135,215,50,14 END IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54 @@ -251,7 +247,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 185 TOPMARGIN, 7 - BOTTOMMARGIN, 263 + BOTTOMMARGIN, 229 END IDD_WAIT, DIALOG diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index d108d170..2885b2f0 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -99,7 +99,6 @@ #define IDC_ADD_MOD_SUPER 1047 #define IDC_GLOBAL_SCREENSAVER_SYNC 1047 #define IDC_ADVANCED_DEFAULTS 1049 -#define IDC_ADD_IGNORE_NUM_LOCK 1052 // Next default values for new objects // @@ -108,7 +107,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 110 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1053 +#define _APS_NEXT_CONTROL_VALUE 1052 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 9b242e7d..435cdc55 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -445,11 +445,6 @@ CServerProxy::enter() m_seqNum = seqNum; } - // ignore num lock if so desired - if (m_ignoreNumLock) { - mask &= ~KeyModifierNumLock; - } - // forward getClient()->enter(x, y, seqNum, static_cast(mask), false); } @@ -525,12 +520,6 @@ CServerProxy::keyDown() mask2 != static_cast(mask)) LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); - // ignore num lock if so desired - if (id2 == kKeyNumLock && m_ignoreNumLock) { - LOG((CLOG_DEBUG1 "ignoring num lock")); - return; - } - // forward getClient()->keyDown(id2, mask2, button); } @@ -555,12 +544,6 @@ CServerProxy::keyRepeat() mask2 != static_cast(mask)) LOG((CLOG_DEBUG1 "key repeat translated to id=%d, mask=0x%04x", id2, mask2)); - // ignore num lock if so desired - if (id2 == kKeyNumLock && m_ignoreNumLock) { - LOG((CLOG_DEBUG1 "ignoring num lock")); - return; - } - // forward getClient()->keyRepeat(id2, mask2, count, button); } @@ -584,12 +567,6 @@ CServerProxy::keyUp() mask2 != static_cast(mask)) LOG((CLOG_DEBUG1 "key up translated to id=%d, mask=0x%04x", id2, mask2)); - // ignore num lock if so desired - if (id2 == kKeyNumLock && m_ignoreNumLock) { - LOG((CLOG_DEBUG1 "ignoring num lock")); - return; - } - // forward getClient()->keyUp(id2, mask2, button); } @@ -707,9 +684,6 @@ CServerProxy::resetOptions() if (m_heartRate >= 0.0) { CProtocolUtil::writef(getOutputStream(), kMsgCNoop); } - - // don't ignore num lock - m_ignoreNumLock = false; } void @@ -752,9 +726,6 @@ CServerProxy::setOptions() CProtocolUtil::writef(getOutputStream(), kMsgCNoop); } } - else if (options[i] == kOptionIgnoreNumLock) { - m_ignoreNumLock = true; - } if (id != kKeyModifierIDNull) { m_modifierTranslationTable[id] = static_cast(options[i + 1]); diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index 423cdf56..cbb58f1b 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -128,7 +128,6 @@ private: SInt32 m_xMouse, m_yMouse; bool m_ignoreMouse; - bool m_ignoreNumLock; KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; double m_heartRate; diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 04a2dc72..89417b28 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -646,9 +646,6 @@ CConfig::getOptionName(OptionID id) if (id == kOptionXTestXineramaUnaware) { return "xtestIsXineramaUnaware"; } - if (id == kOptionIgnoreNumLock) { - return "ignoreNumLock"; - } return NULL; } @@ -658,8 +655,7 @@ CConfig::getOptionValue(OptionID id, OptionValue value) if (id == kOptionHalfDuplexCapsLock || id == kOptionHalfDuplexNumLock || id == kOptionScreenSaverSync || - id == kOptionXTestXineramaUnaware || - id == kOptionIgnoreNumLock) { + id == kOptionXTestXineramaUnaware) { return (value != 0) ? "true" : "false"; } if (id == kOptionModifierMapForShift || @@ -895,10 +891,6 @@ CConfig::readSectionScreens(std::istream& s) addOption(screen, kOptionXTestXineramaUnaware, parseBoolean(value)); } - else if (name == "ignoreNumLock") { - addOption(screen, kOptionIgnoreNumLock, - parseBoolean(value)); - } else { // unknown argument throw XConfigRead("unknown argument"); diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index 27e1f837..aaefe7fa 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -55,7 +55,6 @@ static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); -static const OptionID kOptionIgnoreNumLock = OPTION_CODE("IGNL"); //@} #undef OPTION_CODE From 5488b77d613659843785dbdb3bca2e41f3016ba6 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 Jul 2003 20:42:11 +0000 Subject: [PATCH 547/807] Fixed handling of some keystrokes on win32. Pressing a dead key and then space should convert the dead key to a non-dead key but previous the key was discarded. Fixed that but VkKeyScan() fails in this case so added special case to fix that (assuming AltGr is required). VkKeyScan() can return the wrong result for characters that have more than one virtual key mapped to them. AltGr+9 (^) on the French layout has this problem. Now detecting that problem and using the current keyboard state to decide if AltGr is required. --- lib/platform/CMSWindowsPrimaryScreen.cpp | 40 +++++++++++++++++++----- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 1eec8050..5871f724 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -1144,7 +1144,7 @@ static const KeyID g_virtualKey[][2] = /* 0x1d */ kKeyNone, kKeyNone, // VK_NONCONVERT /* 0x1e */ kKeyNone, kKeyNone, // VK_ACCEPT /* 0x1f */ kKeyNone, kKeyNone, // VK_MODECHANGE - /* 0x20 */ 0x0020, kKeyNone, // VK_SPACE + /* 0x20 */ kKeyNone, kKeyNone, // VK_SPACE /* 0x21 */ kKeyKP_PageUp, kKeyPageUp, // VK_PRIOR /* 0x22 */ kKeyKP_PageDown, kKeyPageDown, // VK_NEXT /* 0x23 */ kKeyKP_End, kKeyEnd, // VK_END @@ -1421,7 +1421,8 @@ CMSWindowsPrimaryScreen::mapKey( // convert to ascii WORD ascii; - int result = ToAscii(vkCode, scanCode, m_keys, &ascii, 0); + int result = ToAscii(vkCode, scanCode, m_keys, &ascii, + ((menu & 0x80) == 0) ? 0 : 1); // restore control state m_keys[VK_LCONTROL] = lControl; @@ -1504,13 +1505,38 @@ CMSWindowsPrimaryScreen::mapKey( // set mask *altgr = false; if (id != kKeyNone && id != kKeyMultiKey && c != 0) { - // note if key requires AltGr - SHORT virtualKeyAndModifierState = VkKeyScan(c); - BYTE modifierState = HIBYTE(virtualKeyAndModifierState); - if ((modifierState & 6) == 6) { - // key requires ctrl and alt == AltGr + // note if key requires AltGr. VkKeyScan() can have a problem + // with some characters. there are two problems in particular. + // first, typing a dead key then pressing space will cause + // VkKeyScan() to return 0xffff. second, certain characters + // may map to multiple virtual keys and we might get the wrong + // one. if that happens then we might not get the right + // modifier mask. AltGr+9 on the french keyboard layout (^) + // has this problem. in the first case, we'll assume AltGr is + // required (only because it solves the problems we've seen + // so far). in the second, we'll use whatever the keyboard + // state says. + WORD virtualKeyAndModifierState = VkKeyScan(c); + if (virtualKeyAndModifierState == 0xffff) { + // there is no mapping. assume AltGr. + LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); *altgr = true; } + else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { + // we didn't get the key that was actually pressed + LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); + if ((m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0) { + *altgr = true; + } + } + else { + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + if ((modifierState & 6) == 6) { + // key requires ctrl and alt == AltGr + *altgr = true; + } + } // map modifier key KeyModifierMask mask = 0; From 9c4267ed115359e052e30665bcbf1b0bc46b1e56 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Jul 2003 21:40:57 +0000 Subject: [PATCH 548/807] Fixed handling of shift/ctrl/alt on special keys on win32 server. --- lib/platform/CMSWindowsPrimaryScreen.cpp | 72 +++++++++++------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp index 5871f724..d68612dc 100644 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ b/lib/platform/CMSWindowsPrimaryScreen.cpp @@ -1537,48 +1537,44 @@ CMSWindowsPrimaryScreen::mapKey( *altgr = true; } } + } - // map modifier key - KeyModifierMask mask = 0; - if (((m_keys[VK_LSHIFT] | - m_keys[VK_RSHIFT] | - m_keys[VK_SHIFT]) & 0x80) != 0) { - mask |= KeyModifierShift; - } - if (*altgr) { - mask |= KeyModifierModeSwitch; - } - else { - if (((m_keys[VK_LCONTROL] | - m_keys[VK_RCONTROL] | - m_keys[VK_CONTROL]) & 0x80) != 0) { - mask |= KeyModifierControl; - } - if (((m_keys[VK_LMENU] | - m_keys[VK_RMENU] | - m_keys[VK_MENU]) & 0x80) != 0) { - mask |= KeyModifierAlt; - } - } - if (((m_keys[VK_LWIN] | - m_keys[VK_RWIN]) & 0x80) != 0) { - mask |= KeyModifierSuper; - } - if ((m_keys[VK_CAPITAL] & 0x01) != 0) { - mask |= KeyModifierCapsLock; - } - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { - mask |= KeyModifierNumLock; - } - if ((m_keys[VK_SCROLL] & 0x01) != 0) { - mask |= KeyModifierScrollLock; - } - *maskOut = mask; + // map modifier key + KeyModifierMask mask = 0; + if (((m_keys[VK_LSHIFT] | + m_keys[VK_RSHIFT] | + m_keys[VK_SHIFT]) & 0x80) != 0) { + mask |= KeyModifierShift; + } + if (*altgr) { + mask |= KeyModifierModeSwitch; } else { - // don't care - *maskOut = 0; + if (((m_keys[VK_LCONTROL] | + m_keys[VK_RCONTROL] | + m_keys[VK_CONTROL]) & 0x80) != 0) { + mask |= KeyModifierControl; + } + if (((m_keys[VK_LMENU] | + m_keys[VK_RMENU] | + m_keys[VK_MENU]) & 0x80) != 0) { + mask |= KeyModifierAlt; + } } + if (((m_keys[VK_LWIN] | + m_keys[VK_RWIN]) & 0x80) != 0) { + mask |= KeyModifierSuper; + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { + mask |= KeyModifierCapsLock; + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { + mask |= KeyModifierNumLock; + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { + mask |= KeyModifierScrollLock; + } + *maskOut = mask; return id; } From b949e108a0af8c74e671554c1d01c5a5c88fef8f Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Jul 2003 21:41:09 +0000 Subject: [PATCH 549/807] Changed version to 1.1.1. --- configure.in | 4 ++-- lib/common/Version.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.in b/configure.in index cf9ceae7..3f1823a9 100644 --- a/configure.in +++ b/configure.in @@ -18,8 +18,8 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 -MINOR_VERSION=0 -RELEASE_VERSION=11 +MINOR_VERSION=1 +RELEASE_VERSION=1 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 772796cf..5155b0ff 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.0.11" +# define VERSION "1.1.1" #endif // important strings From 0325de2e1216156f6d22feb5c16c3e5c2f86f95c Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Jul 2003 22:38:43 +0000 Subject: [PATCH 550/807] Fixed handling of some non-ASCII but directly mapped characters on win32. The o, a, and u with diaeresis in the german keyboard mapping are examples. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index d39e775d..7895c382 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -936,6 +936,10 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // from wide char to multibyte, then from multibyte to wide char // forcing conversion to composite characters, then from wide // char back to multibyte without making precomposed characters. + // + // after the first conversion to multibyte we see if we can map + // the key. if so then we don't bother trying to decompose dead + // keys. BOOL error; char multiByte[2 * MB_LEN_MAX]; wchar_t unicode[2]; @@ -949,6 +953,11 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); return m_mask; } + virtualKey = mapCharacter(keys, multiByte[0], hkl, m_mask, mask, action); + if (virtualKey != static_cast(-1)) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); + return m_mask; + } nChars = MultiByteToWideChar(codePage, MB_COMPOSITE | MB_ERR_INVALID_CHARS, multiByte, nChars, @@ -1040,6 +1049,10 @@ CMSWindowsSecondaryScreen::mapCharacter(Keystrokes& keys, // get virtual key UINT virtualKey = LOBYTE(virtualKeyAndModifierState); + if (LOBYTE(virtualKeyAndModifierState) == 0xff) { + LOG((CLOG_DEBUG2 "cannot map character %d", static_cast(c))); + return static_cast(-1); + } // get the required modifier state BYTE modifierState = HIBYTE(virtualKeyAndModifierState); From 99792b13a5da0db8eb523d744031d906db2869a7 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Jul 2003 22:38:54 +0000 Subject: [PATCH 551/807] Changed version to 1.1.2. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 3f1823a9..45008f96 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=1 -RELEASE_VERSION=1 +RELEASE_VERSION=2 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 5155b0ff..cd97d184 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.1" +# define VERSION "1.1.2" #endif // important strings From e1985f52c90b307dc1e32fc4289d5ff2128b25e0 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 17 Jul 2003 21:16:58 +0000 Subject: [PATCH 552/807] Fixed handling of a dead key followed by space on win32 and X11. A dead key followed by space should convert the dead key to a regular character. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 54 ++++++++++++++++++---- lib/platform/CXWindowsSecondaryScreen.cpp | 13 ++++++ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index 7895c382..cc617337 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -127,8 +127,8 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key, // generate key events doKeystrokes(keys, 1); - // do not record button down if button is 0 (invalid) - if (button != 0) { + // do not record button down if button or virtual key is 0 (invalid) + if (button != 0 && virtualKey != 0) { // note that key is now down m_serverKeyMap[button] = virtualKey; m_keys[virtualKey] |= 0x80; @@ -956,21 +956,48 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, virtualKey = mapCharacter(keys, multiByte[0], hkl, m_mask, mask, action); if (virtualKey != static_cast(-1)) { LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); + if ((MapVirtualKey(virtualKey, 2) & 0x80000000u) != 0) { + // it looks like this character is a dead key but + // MapVirtualKey() will claim it's a dead key even if it's + // not (though i don't think it ever claims it's not when + // it is). we need a backup test to ensure that this is + // really a dead key. we could use ToAscii() for this but + // that keeps state and it's a hassle to restore that state. + // OemKeyScan() appears to do the trick. if the character + // cannot be generated with a single keystroke then it + // returns 0xffffffff. + if (OemKeyScan(multiByte[0]) != 0xffffffffu) { + // character mapped to a dead key but we want the + // character for real so send a space key afterwards. + LOG((CLOG_DEBUG2 "character mapped to dead key")); + Keystroke keystroke; + keystroke.m_virtualKey = VK_SPACE; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + + // ignore the release of this key since we already + // handled it in mapCharacter(). + virtualKey = 0; + } + } return m_mask; } nChars = MultiByteToWideChar(codePage, - MB_COMPOSITE | MB_ERR_INVALID_CHARS, - multiByte, nChars, - unicode, 2); + MB_COMPOSITE | MB_ERR_INVALID_CHARS, + multiByte, nChars, + unicode, 2); if (nChars == 0) { LOG((CLOG_DEBUG2 "KeyID 0x%08x mb->wc mapping failed", id)); return m_mask; } nChars = WideCharToMultiByte(codePage, - 0, - unicode, nChars, - multiByte, sizeof(multiByte), - NULL, &error); + 0, + unicode, nChars, + multiByte, sizeof(multiByte), + NULL, &error); if (nChars == 0 || error) { LOG((CLOG_DEBUG2 "KeyID 0x%08x wc->mb mapping failed", id)); return m_mask; @@ -1240,6 +1267,15 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, break; } + // if this is a dead key press then send a release immediately. + // the dead key may not be processed correctly if its release + // event comes after we release the modifiers. + if (action == kPress && + (MapVirtualKey(virtualKey, 2) & 0x80000000lu) != 0) { + keystroke.m_press = false; + keys.push_back(keystroke); + } + // add key events to restore the modifier state. apply events in // the reverse order that they're stored in undo. while (!undo.empty()) { diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index 33fa1359..b69369e9 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -1829,6 +1829,19 @@ const CXWindowsSecondaryScreen::KeySymsMap& CXWindowsSecondaryScreen::getDecomposedKeySymTable() { static const KeySym s_rawTable[] = { + // non-dead version of dead keys + XK_grave, XK_dead_grave, XK_space, 0, + XK_acute, XK_dead_acute, XK_space, 0, + XK_asciicircum, XK_dead_circumflex, XK_space, 0, + XK_asciitilde, XK_dead_tilde, XK_space, 0, + XK_cedilla, XK_dead_cedilla, XK_space, 0, + XK_ogonek, XK_dead_ogonek, XK_space, 0, + XK_caron, XK_dead_caron, XK_space, 0, + XK_abovedot, XK_dead_abovedot, XK_space, 0, + XK_doubleacute, XK_dead_doubleacute, XK_space, 0, + XK_breve, XK_dead_breve, XK_space, 0, + XK_macron, XK_dead_macron, XK_space, 0, + // Latin-1 (ISO 8859-1) XK_Agrave, XK_dead_grave, XK_A, 0, XK_Aacute, XK_dead_acute, XK_A, 0, From b59e105bc4f1aebab87a6ed06aba6ed9c02c2316 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 17 Jul 2003 21:17:08 +0000 Subject: [PATCH 553/807] Changed version to 1.1.3. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 45008f96..a8beba9e 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=1 -RELEASE_VERSION=2 +RELEASE_VERSION=3 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index cd97d184..9f1af7ae 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.2" +# define VERSION "1.1.3" #endif // important strings From 1030081f7f7cb7bf785aa830974490ffa048b46b Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 19 Jul 2003 22:12:54 +0000 Subject: [PATCH 554/807] Merged documentation updates in 1.0 into mainline. --- FAQ | 34 +++++++++++++++------------------- NEWS | 21 +++++++++++++++++++++ README | 29 +++++++++++++---------------- TODO | 33 ++++++++++++++------------------- 4 files changed, 63 insertions(+), 54 deletions(-) diff --git a/FAQ b/FAQ index ac38f428..0962f028 100644 --- a/FAQ +++ b/FAQ @@ -25,10 +25,10 @@ Answers Synergy isn't able to capture ctrl+alt+del on PC compatible systems because it's handled completely differently than - other keystrokes. If you need to use ctrl+alt+del you'll - have to keep your other keyboards handy just for that. - Future versions may add support for or some equivalent to - ctrl+alt+del. + other keystrokes. However, when the mouse is on a client + screen, pressing ctrl+alt+pause will simulate ctrl+alt+del + on the client. (A client running on Windows NT, 2000, or XP + must be running as a service for this to work.) 2. Can the server and client be using different operating systems? @@ -98,12 +98,12 @@ Answers Some systems treat the Caps-Lock and Num-Lock keys differently than all the others. Whereas most keys report going down when - physically pressed and going up when physically released, the - Caps-Lock and Num-Lock keys report going down when being - activated and going up when being deactivated. That is, when - you press and release, say, Caps-Lock to activate it, it only - reports going down, and when you press and release to deactivate - it, it only reports going up. This confuses synergy. + physically pressed and going up when physically released, on + these systems the Caps-Lock and Num-Lock keys report going down + when being activated and going up when being deactivated. That + is, when you press and release, say, Caps-Lock to activate it, it + only reports going down, and when you press and release to + deactivate it, it only reports going up. This confuses synergy. You can solve the problem by changing your configuration file. In the screens section, following each screen that has the @@ -128,15 +128,11 @@ Answers 10. Does AltGr/Mode-Switch work? - Sort of. Synergy sends the character you're trying to create from - the server to the client. If the client can figure out how to - create that character then it will do so, synthesizing whatever - key events necessary. However, operating system differences can - cause unexpected problems. Pressing either Ctrl key plus the left - Alt key on Microsoft Windows means AltGr but on Unix that just - means Ctrl and Alt are down. If you have a synergy server running - on Windows and are having trouble with Ctrl+Alt_L = AltGr, try using - the right Alt key as AltGr instead. + Yes, as of 1.0.12 synergy has full support for AltGr/Mode-switch. + That includes support for most (all?) European keyboard layouts. + All systems should be using the same keyboard layout, though, for + all characters to work. (Any character missing from a client's + layout cannot be generated by synergy.) 11. Why isn't synergy ported to platform XYZ? diff --git a/NEWS b/NEWS index e6be93f5..e542e28d 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,27 @@ Synergy News ============ +* Jul-20-2003 - Synergy 1.0.12 released + + This release finally completes support for non-ASCII characters, + fully supporting most (all?) European keyboard layouts including + dead key composition. This release includes changes from several + experimental versions (1.0.9, 1.0.11, 1.1.0, 1.1.1, 1.1.2, and + 1.1.3). + + Made following changes: + * Added non-ASCII support to win32 and X11 + * Added dead key support to win32 and X11 + * Fixed AltGr handling + * Added ctrl+alt+del simulation using ctrl+alt+pause + * Fixed loss of key event when user releases ctrl+alt+del + * Fixed incorrect synthesis of pointer-keys event on X11 + * Fixed Xinerama support + * Made some clipboard fixes on win32 and X11 + * Add tray icon menu item to copy log to clipboard + * Fixed mouse warping on unconnected client + * Stopped unconnected client from filling up event logs + * May-10-2003 - Synergy 1.0.8 released Made following changes: diff --git a/README b/README index 71a9c837..072c9f20 100644 --- a/README +++ b/README @@ -57,7 +57,10 @@ System Requirements (use `xdpyinfo | grep XTEST' to check for XTEST). In this document, "Unix" means any of the following: Linux, Solaris, -Irix. +Irix. Synergy may compile and run on other Unix variants, too. Patches +for other platforms are welcome (including patches that package binaries); +See the contact information available off of the synergy home page or use +the patch page on sourceforge. Installation @@ -77,9 +80,9 @@ The first step is to pick which keyboard and mouse you want to share. The computer with that keyboard and mouse is called the "primary screen" and it runs the synergy server. All of the other computers are "secondary screens" and run the synergy client. The Windows NT -family, starting with NT 4 with service pack 3, is the best choice -for the primary screen but Unix is good too. (This is based on the -known bugs; see BUGS for more details.) +family, starting with NT 4 with service pack 3, and Unix are the best +choices. The Windows version provides a convenient GUI for +configuration. Second, you install the software. Choose the appropriate package and install it. On Windows you should unzip the files into the @@ -205,18 +208,12 @@ your computers. Tips and Tricks --------------- * Be aware that not all keystrokes can be handled by synergy. In - particular, ctrl+alt+del is not handled. You cannot use synergy - to log into a Windows NT family system that requires the user to - press ctrl+alt+del to log on. You'll need to keep that computer's - keyboard handy in order to log on. - -* To work around the lack of ctrl+alt+del, you can configure Windows - 2000 and XP to not require ctrl+alt+del to log on using the System - control panel. If you're the only user of an NT system you might - want to enable auto-logon. In any case, you should keep each - computer's keyboard handy, perhaps under the desk or on top of the - computer itself. If the system supports USB you should also be able - to attach/detach a keyboard as necessary. + particular, ctrl+alt+del is not handled. However, synergy can + convert ctrl+alt+pause into ctrl+alt+del on the client side. + (Synergy must be installed as a service on the client for this to + work on the Windows NT family.) Some non-standard keys may not + work, especially "multimedia" buttons, though several are + correctly handled. * A screen can be its own neighbor. That allows a screen to "wrap". For example, if a configuration linked the left and right sides of diff --git a/TODO b/TODO index d0432fe5..78b3b80a 100644 --- a/TODO +++ b/TODO @@ -5,10 +5,10 @@ Things to do to synergy, in no particular order: * Provide GUI configuration - There's a GUI tool on win32 but no other platforms. It'd be nice - if the tool allowed users to drag screen icons around to set the - links between them, but this complicated because links aren't - necessarily symmetrical. + There's a GUI tool on win32 but no other platforms. Also, it'd be + nice if the tool allowed users to drag screen icons around to set + the links between them (though this assumes links are symmetrical + and synergy supports asymmetrical links). * Provide taskbar feedback @@ -16,18 +16,13 @@ Things to do to synergy, in no particular order: and to quit synergy. It'd be nice to have something similar on X11. -* Support non-ASCII keyboards - - Synergy currently supports only ASCII characters typed on the - keyboard. It does, however, support Unicode clipboard text. - -* Write man/html pages - * Port to other platforms Most desired is MacOS X. -* Provide a nice win32 installer/uninstaller +* Write man/html pages + +* Provide a win32 installer/uninstaller Synergy doesn't have any special needs so even just unzipping is satisfactory, but a proper installer would be nice. And, more @@ -43,8 +38,6 @@ Things to do to synergy, in no particular order: The type is converted to each platform's native type. For example, BMP for images on win32. -* Keyboard shortcuts to jump from screen to screen - Then there are major new features: * Provide a KVM mode @@ -52,11 +45,6 @@ Then there are major new features: In this mode synergy would share the monitor in addition to the keyboard and mouse. -* Add encryption and authentication - - Make synergy is safe to use on untrusted networks. Using synergy - through SSH can provide this until synergy has it built-in. - * Support for limited drag and drop between systems * Support for (virtual) terminals on unix @@ -64,3 +52,10 @@ Then there are major new features: This would be useful in KVM mode to administer several remote headless systems that you don't want running X just so synergy can work. + +* Configurable keys + + This includes shortcuts to jump to different screens, always + directing certain keystrokes to the same system, never sending + certain keystrokes to some systems, and remapping keys on the + server to other keys on the clients. From e725270c004357739f86ab66099523189b3dc8ad Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 21 Jul 2003 18:03:21 +0000 Subject: [PATCH 555/807] Updated ChangeLog. --- ChangeLog | 378 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 378 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1e1d2ba1..448ffaed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,381 @@ +2003/07/19 17:22:06 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergyc/synergyc.cpp +configure.in +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/common/Version.h +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CConfig.cpp +lib/synergy/OptionTypes.h + +Merge synergy 1.1 fixes into 1.0 branch. + +---------- +2003/07/19 17:19:26 crs + +Branched synergy 1.0 from 1.0.11. + +---------- +2003/07/17 21:16:58 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +Fixed handling of a dead key followed by space on win32 and X11. +A dead key followed by space should convert the dead key to a +regular character. + +---------- +2003/07/16 22:38:43 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Fixed handling of some non-ASCII but directly mapped characters +on win32. The o, a, and u with diaeresis in the german keyboard +mapping are examples. + +---------- +2003/07/16 21:40:57 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Fixed handling of shift/ctrl/alt on special keys on win32 server. + +---------- +2003/07/13 20:42:11 crs +lib/platform/CMSWindowsPrimaryScreen.cpp + +Fixed handling of some keystrokes on win32. Pressing a dead key +and then space should convert the dead key to a non-dead key but +previous the key was discarded. Fixed that but VkKeyScan() fails +in this case so added special case to fix that (assuming AltGr is +required). VkKeyScan() can return the wrong result for characters +that have more than one virtual key mapped to them. AltGr+9 (^) +on the French layout has this problem. Now detecting that problem +and using the current keyboard state to decide if AltGr is +required. + +---------- +2003/07/13 17:03:41 crs +cmd/synergyc/synergyc.cpp + +Forgot to remove --camp and --no-camp from brief usage message. + +---------- +2003/07/13 16:57:08 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Changed XSync() to XFlush() in X windows secondary screen. This +doesn't appear to have any negative consequences and may prevent +synergy from freezing when some X client (probably the window +manager) grabs the server. + +---------- +2003/07/12 17:57:31 crs +cmd/synergyc/synergyc.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CXWindowsScreen.cpp + +Prevent INFO level log messages when client is repeatedly trying +to connect. This prevents a log from filling up while the client +can't connect for no useful reason. Also removed --camp option +and cleaned up handling of client connection. Users must now use +--restart instead of --camp. + +---------- +2003/07/08 18:40:46 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h + +Changed windows server to release ctrl and alt keys when it's +sending a key that requires AltGr. That's because AltGr *is* +ctrl and alt but AltGr should be seen on clients as mode +switch without the ctrl and alt. I can't think of a better +way to do this other than to not send modifier keystrokes to +the clients at all. + +---------- +2003/07/05 17:06:18 crs +configure.in +lib/common/Version.h + +Change version to 1.0.11. Skipping version 1.0.10 because there +have been too many major changes since 1.0.8. A new experimental +release will provide a stable starting point for testing. + +---------- +2003/07/05 17:05:12 crs +lib/synergy/CSecondaryScreen.cpp + +Fix to avoid warping mouse until client successfully connects to +the server. + +---------- +2003/07/05 17:04:26 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h + +Keyboard fixes on win32. + +---------- +2003/07/05 17:04:06 crs +lib/server/CConfig.h + +Fix for new template syntax. + +---------- +2003/07/05 14:49:08 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h +lib/platform/CXWindowsSecondaryScreen.cpp + +Minor X11 keyboard code cleanup. Also now handling KeyPress with +keycode == 0 generated by XFilterEvent() by using the keycode from +the previous KeyPress. + +---------- +2003/07/05 14:47:41 crs +lib/platform/CXWindowsScreen.cpp + +Compress sequential MappingNotify events into one. + +---------- +2003/07/01 19:35:28 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Rewrote key handling on X11 client. This should fix problems +with applying the incorrect shift and mode switch modifiers to +some keycodes, such as getting Pointer_EnableKeys when pressing +shift with NumLock enabled. + +---------- +2003/06/22 21:27:38 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsPrimaryScreen.h + +Added support for input methods. Only handling IMs that don't +need a precompose area or status area. This includes IMs that +do simple dead key composition. This only changes the server. +The client still does not decompose a character it cannot +generate directly into the keysyms to compose the character. + +---------- +2003/06/22 16:39:25 crs +lib/client/CServerProxy.cpp + +More fixes for X11 client keyboard handling. + +---------- +2003/06/22 16:39:02 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +More fixes for X11 client keyboard handling. + +---------- +2003/06/22 15:01:44 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Checkpoint for improving X11 client key handling. Should prevent +unintentional Pointer_EnableKeys (i.e. generating NumLock press +and release around a shift press). + +---------- +2003/06/08 22:20:01 crs +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +Another ctrl+alt+del checkpoint. + +---------- +2003/06/08 22:12:12 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CXWindowsPrimaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.cpp + +ctrl+alt+del emulation checkpoint. + +---------- +2003/06/08 16:31:52 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +More DEBUG2 level debugging of keyboard handling. + +---------- +2003/06/08 15:42:05 crs +lib/common/Makefile.am + +Added new file to Makefile. + +---------- +2003/06/02 20:07:16 crs +lib/platform/CMSWindowsSecondaryScreen.cpp + +Fixed ctrl and alt keys on win32 clients. Was broken by a recent +fix to character handling. + +---------- +2003/06/02 20:06:20 crs +lib/client/CServerProxy.cpp + +Fixed errors in log strings. + +---------- +2003/06/02 20:06:03 crs +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.h +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h +lib/common/common.dsp +lib/common/stddeque.h + +Added menu item on win32 tray icon to copy the last 1000 lines from +the log to the clipboard. + +---------- +2003/05/26 09:50:35 crs +lib/platform/CXWindowsClipboard.cpp + +Added workaround for broken clipboard owners that report the +type of TARGETS as TARGETS instead of ATOM. + +---------- +2003/05/26 09:49:38 crs +lib/platform/CMSWindowsClipboard.cpp + +No longer installing clibboard format for plain text on windows nt +family because nt automatically converts to and from the unicode +format. This may fix text encoding errors when synergy puts +non-ascii text on the clipboard and other clients prefer CF_TEXT +to CF_UNICODE (which they should not because synergy lists +CF_UNICODE first). + +---------- +2003/05/26 09:46:52 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsScreen.cpp + +Fixed loss of ctrl+alt+del key releases when the Winlogin desktop +is accessible (was already fixed when inaccessible). This change +also ignores press and release of virtual key 0, which should never +happen but does according to one user. + +---------- +2003/05/21 21:22:14 crs +lib/arch/CArchMultithreadWindows.cpp +lib/synergy/CPrimaryScreen.cpp +lib/synergy/CSecondaryScreen.cpp + +Fixed unsigned compare against zero. Changed win32 priority to +maximum. + +---------- +2003/05/21 19:38:11 crs +lib/server/CServer.cpp +lib/server/CServer.h + +Made double tap require moving farther away from the tapped edge +before arming. This should reduce spurious double taps. + +---------- +2003/05/20 19:15:58 crs +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h + +Attempt to improve key event synthesis. This change adds support +for dead keys and attempts to choose the correct code page for the +thread that will (probably) receive synthesized events. + +---------- +2003/05/20 19:14:40 crs +lib/platform/CSynergyHook.cpp + +Minor thread ID compare fix. + +---------- +2003/05/20 19:14:24 crs +lib/arch/CArchMultithreadWindows.cpp + +Reduced maximum priority in debug build. + +---------- +2003/05/17 20:58:27 crs +lib/platform/CMSWindowsPrimaryScreen.cpp +lib/platform/CMSWindowsPrimaryScreen.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsSecondaryScreen.cpp +lib/platform/CMSWindowsSecondaryScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/IMSWindowsScreenEventHandler.h +lib/synergy/CPrimaryScreen.cpp + +Fixed getting locked to screen after ctrl+alt+del. Also fixed +cursor not being hidden on win32 server when on client screens +(which happened when using low-level hooks). + +---------- +2003/05/17 14:10:11 crs +INSTALL + +Added documentation for xtestIsXineramaUnaware option. + +---------- +2003/05/17 14:03:32 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h + +Fixed previous fix. Was trying to avoid using XWarpPointer() when +warping on screen 0. That just doesn't work if screen 0 is not at +0,0. So now always use XWarpPointer() if there are multiple +xinerama screens and the appropriate option is enabled. + +---------- +2003/05/17 13:44:24 crs +lib/platform/CXWindowsSecondaryScreen.cpp +lib/platform/CXWindowsSecondaryScreen.h +lib/server/CConfig.cpp +lib/synergy/OptionTypes.h + +Added workaround for when XTest is unaware of Xinerama. When that's +true, faking a mouse motion outside screen 0 is clamped onto screen 0. +When the workaround is enabled, we use XWarpPointer() instead of an +XTest fake motion. This isn't perfect but the only real fix requires +patching XTest. + +---------- +2003/05/17 12:48:32 crs +lib/platform/CXWindowsSecondaryScreen.cpp + +Added support for old versions of XF86keysym.h that are missing +some expected #defines. + +---------- 2003/05/10 17:27:05 crs configure.in lib/common/Version.h From 221628fd843291eb0b9c0aec7d4194ba2e6c5840 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 26 Jul 2003 13:41:41 +0000 Subject: [PATCH 556/807] Checkpoint refactoring. CSecondaryScreen now does the work common across platform secondary screens. X11 screen was compiled and tested but not the win23 screen. Will next change inheritance hierarchy. --- lib/platform/CMSWindowsSecondaryScreen.cpp | 846 +++++++------------- lib/platform/CMSWindowsSecondaryScreen.h | 67 +- lib/platform/CXWindowsSecondaryScreen.cpp | 836 +++++++------------ lib/platform/CXWindowsSecondaryScreen.h | 109 +-- lib/platform/IMSWindowsScreenEventHandler.h | 1 - lib/synergy/CSecondaryScreen.cpp | 428 +++++++++- lib/synergy/CSecondaryScreen.h | 130 ++- lib/synergy/IScreenEventHandler.h | 10 - 8 files changed, 1121 insertions(+), 1306 deletions(-) diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp index cc617337..c04cfcb0 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ b/lib/platform/CMSWindowsSecondaryScreen.cpp @@ -86,8 +86,7 @@ const CMSWindowsSecondaryScreen::CModifierInfo CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( IScreenReceiver* receiver) : m_is95Family(CArchMiscWindows::isWindows95Family()), - m_window(NULL), - m_mask(0) + m_window(NULL) { m_screen = new CMSWindowsScreen(receiver, this); } @@ -99,246 +98,64 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() delete m_screen; } -void -CMSWindowsSecondaryScreen::keyDown(KeyID key, - KeyModifierMask mask, KeyButton button) +CSecondaryScreen::SysKeyID +CMSWindowsSecondaryScreen::getUnhanded(SysKeyID sysKeyID) const { - CLock lock(&m_mutex); - m_screen->syncDesktop(); - - // check for ctrl+alt+del emulation - if (key == kKeyDelete && - (mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - synthesizeCtrlAltDel(); - return; - } - - // get the sequence of keys to simulate key press and the final - // modifier state. - Keystrokes keys; - UINT virtualKey; - m_mask = mapKey(keys, virtualKey, key, mask, kPress); - if (keys.empty()) { - // do nothing if there are no associated keys (i.e. lookup failed) - return; - } - - // generate key events - doKeystrokes(keys, 1); - - // do not record button down if button or virtual key is 0 (invalid) - if (button != 0 && virtualKey != 0) { - // note that key is now down - m_serverKeyMap[button] = virtualKey; - m_keys[virtualKey] |= 0x80; - m_fakeKeys[virtualKey] |= 0x80; - switch (virtualKey) { - case VK_LSHIFT: - case VK_RSHIFT: - m_keys[VK_SHIFT] |= 0x80; - m_fakeKeys[VK_SHIFT] |= 0x80; - break; - - case VK_LCONTROL: - case VK_RCONTROL: - m_keys[VK_CONTROL] |= 0x80; - m_fakeKeys[VK_CONTROL] |= 0x80; - break; - - case VK_LMENU: - case VK_RMENU: - m_keys[VK_MENU] |= 0x80; - m_fakeKeys[VK_MENU] |= 0x80; - break; - } - } -} - -void -CMSWindowsSecondaryScreen::keyRepeat(KeyID key, - KeyModifierMask mask, SInt32 count, KeyButton button) -{ - CLock lock(&m_mutex); - m_screen->syncDesktop(); - - // if we haven't seen this button go down then ignore it - ServerKeyMap::iterator index = m_serverKeyMap.find(button); - if (index == m_serverKeyMap.end()) { - return; - } - - // get the sequence of keys to simulate key repeat and the final - // modifier state. - Keystrokes keys; - UINT virtualKey; - m_mask = mapKey(keys, virtualKey, key, mask, kRepeat); - if (keys.empty()) { - return; - } - - // if the keycode for the auto-repeat is not the same as for the - // initial press then mark the initial key as released and the new - // key as pressed. this can happen when we auto-repeat after a - // dead key. for example, a dead accent followed by 'a' will - // generate an 'a with accent' followed by a repeating 'a'. the - // keycodes for the two keysyms might be different. - if (virtualKey != index->second) { - // replace key up with previous keycode but leave key down - // alone so it uses the new keycode and store that keycode - // in the server key map. - for (Keystrokes::iterator index2 = keys.begin(); - index2 != keys.end(); ++index2) { - if (index2->m_virtualKey == virtualKey) { - index2->m_virtualKey = index->second; - break; - } - } - - // note that old key is now up - m_keys[index->second] = false; - m_fakeKeys[index->second] = false; - - // map server key to new key - index->second = virtualKey; - - // note that new key is now down - m_keys[index->second] = true; - m_fakeKeys[index->second] = true; - } - - // generate key events - doKeystrokes(keys, count); -} - -void -CMSWindowsSecondaryScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) -{ - CLock lock(&m_mutex); - m_screen->syncDesktop(); - - // if we haven't seen this button go down then ignore it - ServerKeyMap::iterator index = m_serverKeyMap.find(button); - if (index == m_serverKeyMap.end()) { - return; - } - UINT virtualKey = index->second; - - // get the sequence of keys to simulate key release and the final - // modifier state. - Keystrokes keys; - m_mask = mapKeyRelease(keys, virtualKey); - - // generate key events - doKeystrokes(keys, 1); - - // note that key is now up - m_serverKeyMap.erase(index); - m_keys[virtualKey] &= ~0x80; - m_fakeKeys[virtualKey] &= ~0x80; - switch (virtualKey) { + switch (sysKeyID) { case VK_LSHIFT: - if ((m_keys[VK_RSHIFT] & 0x80) == 0) { - m_keys[VK_SHIFT] &= ~0x80; - m_fakeKeys[VK_SHIFT] &= ~0x80; - } - break; - case VK_RSHIFT: - if ((m_keys[VK_LSHIFT] & 0x80) == 0) { - m_keys[VK_SHIFT] &= ~0x80; - m_fakeKeys[VK_SHIFT] &= ~0x80; - } - break; + return VK_SHIFT; case VK_LCONTROL: - if ((m_keys[VK_RCONTROL] & 0x80) == 0) { - m_keys[VK_CONTROL] &= ~0x80; - m_fakeKeys[VK_CONTROL] &= ~0x80; - } - break; - case VK_RCONTROL: - if ((m_keys[VK_LCONTROL] & 0x80) == 0) { - m_keys[VK_CONTROL] &= ~0x80; - m_fakeKeys[VK_CONTROL] &= ~0x80; - } - break; + return VK_CONTROL; case VK_LMENU: - if ((m_keys[VK_RMENU] & 0x80) == 0) { - m_keys[VK_MENU] &= ~0x80; - m_fakeKeys[VK_MENU] &= ~0x80; - } - break; + case VK_RMENU: + return VK_MENU; + + default: + return 0; + } +} + +CSecondaryScreen::SysKeyID +CMSWindowsSecondaryScreen::getOtherHanded(SysKeyID sysKeyID) const +{ + switch (sysKeyID) { + case VK_LSHIFT: + return VK_RSHIFT; + + case VK_RSHIFT: + return VK_LSHIFT; + + case VK_LCONTROL: + return VK_RCONTROL; + + case VK_RCONTROL: + return VK_LCONTROL; + + case VK_LMENU: + return VK_RMENU; case VK_RMENU: - if ((m_keys[VK_LMENU] & 0x80) == 0) { - m_keys[VK_MENU] &= ~0x80; - m_fakeKeys[VK_MENU] &= ~0x80; - } - break; + return VK_LMENU; + + default: + return 0; } } -void -CMSWindowsSecondaryScreen::mouseDown(ButtonID button) +bool +CMSWindowsSecondaryScreen::isAutoRepeating(SysKeyID) const +{ + return true; +} + +void +CMSWindowsSecondaryScreen::sync() const { - CLock lock(&m_mutex); m_screen->syncDesktop(); - - // map button id to button flag - DWORD data; - DWORD flags = mapButton(button, true, &data); - - // send event - if (flags != 0) { - mouse_event(flags, 0, 0, data, 0); - } -} - -void -CMSWindowsSecondaryScreen::mouseUp(ButtonID button) -{ - CLock lock(&m_mutex); - m_screen->syncDesktop(); - - // map button id to button flag - DWORD data; - DWORD flags = mapButton(button, false, &data); - - // send event - if (flags != 0) { - mouse_event(flags, 0, 0, data, 0); - } -} - -void -CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) -{ - CLock lock(&m_mutex); - m_screen->syncDesktop(); - warpCursor(x, y); -} - -void -CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) -{ - CLock lock(&m_mutex); - m_screen->syncDesktop(); - mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); -} - -void -CMSWindowsSecondaryScreen::resetOptions() -{ - CSecondaryScreen::resetOptions(); -} - -void -CMSWindowsSecondaryScreen::setOptions(const COptionsList& options) -{ - CSecondaryScreen::setOptions(options); } IScreen* @@ -383,12 +200,6 @@ CMSWindowsSecondaryScreen::onOneShotTimerExpired(UInt32) // ignore } -SInt32 -CMSWindowsSecondaryScreen::getJumpZoneSize() const -{ - return 0; -} - void CMSWindowsSecondaryScreen::postCreateWindow(HWND window) { @@ -473,7 +284,7 @@ CMSWindowsSecondaryScreen::showWindow(SInt32 x, SInt32 y) ShowWindow(m_window, SW_SHOWNA); // now warp the mouse - warpCursor(x, y); + fakeMouseMove(x, y); } void @@ -482,164 +293,70 @@ CMSWindowsSecondaryScreen::hideWindow() ShowWindow(m_window, SW_HIDE); } -void -CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) +CSecondaryScreen::KeyState +CMSWindowsSecondaryScreen::getKeyState(UINT virtualKey) const { - // motion is simple (i.e. it's on the primary monitor) if there - // is only one monitor. - bool simple = !m_screen->isMultimon(); - if (!simple) { - // also simple if motion is within the primary monitor - simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && - y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); + BYTE sysState = static_cast(GetKeyState(virtualKey)); + KeyState state = 0; + if (sysState & 0x01u) { + state |= kToggled; } - - // move the mouse directly to target position if motion is simple - if (simple) { - // when using absolute positioning with mouse_event(), - // the normalized device coordinates range over only - // the primary screen. - SInt32 w = GetSystemMetrics(SM_CXSCREEN); - SInt32 h = GetSystemMetrics(SM_CYSCREEN); - mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65536.0 * x) / w), - (DWORD)((65536.0 * y) / h), - 0, 0); - } - - // windows 98 (and Me?) is broken. you cannot set the absolute - // position of the mouse except on the primary monitor but you - // can do relative moves onto any monitor. this is, in microsoft's - // words, "by design." apparently the designers of windows 2000 - // we're a little less lazy and did it right. - // - // microsoft recommends in Q193003 to absolute position the cursor - // somewhere on the primary monitor then relative move to the - // desired location. this doesn't work for us because when the - // user drags a scrollbar, a window, etc. it causes the dragged - // item to jump back a forth between the position on the primary - // monitor and the desired position. while it always ends up in - // the right place, the effect is disconcerting. - // - // instead we'll get the cursor's current position and do just a - // relative move from there to the desired position. relative - // moves are subject to cursor acceleration which we don't want. - // so we disable acceleration, do the relative move, then restore - // acceleration. there's a slight chance we'll end up in the - // wrong place if the user moves the cursor using this system's - // mouse while simultaneously moving the mouse on the server - // system. that defeats the purpose of synergy so we'll assume - // that won't happen. even if it does, the next mouse move will - // correct the position. - else { - // save mouse speed & acceleration - int oldSpeed[4]; - bool accelChanged = - SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && - SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); - - // use 1:1 motion - if (accelChanged) { - int newSpeed[4] = { 0, 0, 0, 1 }; - accelChanged = - SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || - SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); - } - - // get current mouse position - POINT pos; - GetCursorPos(&pos); - - // move relative to mouse position - mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); - - // restore mouse speed & acceleration - if (accelChanged) { - SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); - SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); - } + if (sysState & 0x80u) { + state |= kDown; } + return state; } void -CMSWindowsSecondaryScreen::updateKeys() +CMSWindowsSecondaryScreen::updateKeys(KeyState* keys) { - // clear key state - memset(m_keys, 0, sizeof(m_keys)); - memset(m_fakeKeys, 0, sizeof(m_keys)); - // we only care about the modifier key states - m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); - m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); - m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); - m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL)); - m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL)); - m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL)); - m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU)); - m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU)); - m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU)); - m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN)); - m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN)); - m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS)); - m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); - m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); - m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); - - // copy over lock states to m_fakeKeys - m_fakeKeys[VK_CAPITAL] = static_cast(m_keys[VK_CAPITAL] & 0x01); - m_fakeKeys[VK_NUMLOCK] = static_cast(m_keys[VK_NUMLOCK] & 0x01); - m_fakeKeys[VK_SCROLL] = static_cast(m_keys[VK_SCROLL] & 0x01); - - // update active modifier mask - m_mask = 0; - if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) { - m_mask |= KeyModifierShift; - } - if ((m_keys[VK_LCONTROL] & 0x80) != 0 || - (m_keys[VK_RCONTROL] & 0x80) != 0) { - m_mask |= KeyModifierControl; - } - if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) { - m_mask |= KeyModifierAlt; - } - // note -- no keys for KeyModifierMeta - if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) { - m_mask |= KeyModifierSuper; - } - if ((m_keys[VK_CAPITAL] & 0x01) != 0) { - m_mask |= KeyModifierCapsLock; - } - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { - m_mask |= KeyModifierNumLock; - } - if ((m_keys[VK_SCROLL] & 0x01) != 0) { - m_mask |= KeyModifierScrollLock; - } - // note -- do not save KeyModifierModeSwitch in m_mask - LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); -} - -void -CMSWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) -{ - // toggle modifiers that don't match the desired state - if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { - toggleKey(VK_CAPITAL, KeyModifierCapsLock); - } - if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { - toggleKey(VK_NUMLOCK | 0x100, KeyModifierNumLock); - } - if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { - toggleKey(VK_SCROLL, KeyModifierScrollLock); - } + keys[VK_LSHIFT] = getKeyState(VK_LSHIFT); + keys[VK_RSHIFT] = getKeyState(VK_RSHIFT); + keys[VK_SHIFT] = getKeyState(VK_SHIFT); + keys[VK_LCONTROL] = getKeyState(VK_LCONTROL); + keys[VK_RCONTROL] = getKeyState(VK_RCONTROL); + keys[VK_CONTROL] = getKeyState(VK_CONTROL); + keys[VK_LMENU] = getKeyState(VK_LMENU); + keys[VK_RMENU] = getKeyState(VK_RMENU); + keys[VK_MENU] = getKeyState(VK_MENU); + keys[VK_LWIN] = getKeyState(VK_LWIN); + keys[VK_RWIN] = getKeyState(VK_RWIN); + keys[VK_APPS] = getKeyState(VK_APPS); + keys[VK_CAPITAL] = getKeyState(VK_CAPITAL); + keys[VK_NUMLOCK] = getKeyState(VK_NUMLOCK); + keys[VK_SCROLL] = getKeyState(VK_SCROLL); } KeyModifierMask -CMSWindowsSecondaryScreen::getToggleState() const +CMSWindowsSecondaryScreen::getModifiers() const { - return (m_mask & (KeyModifierCapsLock | - KeyModifierNumLock | - KeyModifierScrollLock)); + // update active modifier mask + KeyModifierMask mask = 0; + if (isKeyDown(VK_LSHIFT) || isKeyDown(VK_RSHIFT)) { + mask |= KeyModifierShift; + } + if (isKeyDown(VK_LCONTROL) || isKeyDown(VK_RCONTROL)) { + mask |= KeyModifierControl; + } + if (isKeyDown(VK_LMENU) || isKeyDown(VK_RMENU)) { + mask |= KeyModifierAlt; + } + // note -- no keys for KeyModifierMeta + if (isKeyDown(VK_LWIN) || isKeyDown(VK_RWIN)) { + mask |= KeyModifierSuper; + } + if (isKeyToggled(VK_CAPITAL)) { + mask |= KeyModifierCapsLock; + } + if (isKeyToggled(VK_NUMLOCK)) { + mask |= KeyModifierNumLock; + } + if (isKeyToggled(VK_SCROLL)) { + mask |= KeyModifierScrollLock; + } + // note -- do not save KeyModifierModeSwitch in mask + return mask; } // map special KeyID keys to virtual key codes. if the key is an @@ -814,8 +531,10 @@ CMSWindowsSecondaryScreen::mapButton(ButtonID button, } KeyModifierMask -CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, - KeyID id, KeyModifierMask mask, EKeyAction action) const +CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, + SysKeyID& virtualKey, KeyID id, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, EKeyAction action) const { virtualKey = 0; @@ -832,7 +551,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, } if (virtualKey == 0) { LOG((CLOG_DEBUG2 "unknown special key")); - return m_mask; + return currentMask; } } @@ -851,14 +570,14 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // active window or fullscreen? BYTE scan = 0; - if ((mask & KeyModifierAlt) == 0) { + if ((desiredMask & KeyModifierAlt) == 0) { scan = 1; } // send event keybd_event(static_cast(virtualKey & 0xff), scan, flags, 0); } - return m_mask; + return currentMask; } // handle other special keys @@ -866,11 +585,11 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // compute the final desired modifier mask. special keys use // the desired modifiers as given except we keep the caps lock, // num lock, and scroll lock as is. - KeyModifierMask outMask = (m_mask & + KeyModifierMask outMask = (currentMask & (KeyModifierCapsLock | KeyModifierNumLock | KeyModifierScrollLock)); - outMask |= (mask & + outMask |= (desiredMask & (KeyModifierShift | KeyModifierControl | KeyModifierAlt | @@ -889,7 +608,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, if (virtualKey2 >= VK_NUMPAD0 && virtualKey2 <= VK_DIVIDE) { // set required shift state based on current numlock state if ((outMask & KeyModifierNumLock) == 0) { - if ((m_mask & KeyModifierNumLock) == 0) { + if ((currentMask & KeyModifierNumLock) == 0) { LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); outMask |= KeyModifierNumLock; } @@ -902,12 +621,13 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // check for left tab. that requires the shift key. if (id == kKeyLeftTab) { - mask |= KeyModifierShift; + // XXX -- this isn't used; outMask meant instead? + desiredMask |= KeyModifierShift; } // now generate the keystrokes and return the resulting modifier mask LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d mask 0x%04x", id, virtualKey2, outMask)); - return mapToKeystrokes(keys, virtualKey, m_mask, outMask, action); + return mapToKeystrokes(keys, virtualKey, currentMask, outMask, action); } // determine the thread that'll receive this event @@ -951,9 +671,10 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, NULL, &error); if (nChars == 0 || error) { LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); - return m_mask; + return currentMask; } - virtualKey = mapCharacter(keys, multiByte[0], hkl, m_mask, mask, action); + virtualKey = mapCharacter(keys, multiByte[0], + hkl, currentMask, desiredMask, action); if (virtualKey != static_cast(-1)) { LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); if ((MapVirtualKey(virtualKey, 2) & 0x80000000u) != 0) { @@ -971,11 +692,11 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // character for real so send a space key afterwards. LOG((CLOG_DEBUG2 "character mapped to dead key")); Keystroke keystroke; - keystroke.m_virtualKey = VK_SPACE; - keystroke.m_press = true; - keystroke.m_repeat = false; + keystroke.m_sysKeyID = VK_SPACE; + keystroke.m_press = true; + keystroke.m_repeat = false; keys.push_back(keystroke); - keystroke.m_press = false; + keystroke.m_press = false; keys.push_back(keystroke); // ignore the release of this key since we already @@ -983,7 +704,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, virtualKey = 0; } } - return m_mask; + return currentMask; } nChars = MultiByteToWideChar(codePage, MB_COMPOSITE | MB_ERR_INVALID_CHARS, @@ -991,7 +712,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, unicode, 2); if (nChars == 0) { LOG((CLOG_DEBUG2 "KeyID 0x%08x mb->wc mapping failed", id)); - return m_mask; + return currentMask; } nChars = WideCharToMultiByte(codePage, 0, @@ -1000,7 +721,7 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, NULL, &error); if (nChars == 0 || error) { LOG((CLOG_DEBUG2 "KeyID 0x%08x wc->mb mapping failed", id)); - return m_mask; + return currentMask; } // we expect one or two characters in multiByte. if there are two @@ -1008,60 +729,54 @@ CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, UINT& virtualKey, // FIXME -- we assume each character is one byte here if (nChars > 2) { LOG((CLOG_DEBUG2 "multibyte characters not supported for character 0x%04x", id)); - return m_mask; + return currentMask; } if (nChars == 2) { LOG((CLOG_DEBUG2 "KeyID 0x%08x needs dead key %u", id, (unsigned char)multiByte[1])); - mapCharacter(keys, multiByte[1], hkl, m_mask, mask, action); + mapCharacter(keys, multiByte[1], + hkl, currentMask, desiredMask, action); } // process character LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - virtualKey = mapCharacter(keys, multiByte[0], hkl, m_mask, mask, action); + virtualKey = mapCharacter(keys, multiByte[0], + hkl, currentMask, desiredMask, action); // non-special key cannot modify the modifier mask - return m_mask; + return currentMask; } KeyModifierMask -CMSWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys, - UINT virtualKey) const +CMSWindowsSecondaryScreen::getModifierKeyMask(SysKeyID virtualKey) const { - // add key release - Keystroke keystroke; - keystroke.m_virtualKey = virtualKey; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); + switch (virtualKey) { + case VK_CAPITAL: + return KeyModifierCapsLock; - // if this is a modifier keycode then update the current modifier mask + case VK_NUMLOCK: + return KeyModifierNumLock; + + case VK_SCROLL: + return KeyModifierScrollLock; + + default: + return 0; + } +} + +bool +CMSWindowsSecondaryScreen::isModifierActive(SysKeyID virtualKey) const +{ + // check if any virtual key for this modifier is down. return false + // for toggle modifiers. const CModifierInfo* modifier = getModifierInfo(virtualKey); - if (modifier != NULL) { - if (modifier->m_isToggle) { - // toggle keys modify the state on release - return (m_mask ^ modifier->m_mask); - } - else { - // can't reset bit until all keys that set it are released. - // scan those keys to see if any (except virtualKey) are - // pressed. - bool down = false; - if (virtualKey != (modifier->m_virtualKey & 0xff) && - (m_keys[modifier->m_virtualKey & 0xff] & 0x80) != 0) { - down = true; - } - if (modifier->m_virtualKey2 != 0 && - virtualKey != (modifier->m_virtualKey2 & 0xff) && - (m_keys[modifier->m_virtualKey2 & 0xff] & 0x80) != 0) { - down = true; - } - if (!down) { - return (m_mask & ~modifier->m_mask); - } + if (modifier != NULL && !modifier->m_isToggle) { + if (isKeyDown(modifier->m_virtualKey & 0xff) || + isKeyDown(modifier->m_virtualKey2 & 0xff)) { + return true; } } - - return m_mask; + return false; } UINT @@ -1186,9 +901,9 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, // modifier is a toggle then toggle it on with a // press/release, otherwise activate it with a // press. - keystroke.m_virtualKey = s_modifier[i].m_virtualKey; - keystroke.m_press = true; - keystroke.m_repeat = false; + keystroke.m_sysKeyID = s_modifier[i].m_virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; keys.push_back(keystroke); if (s_modifier[i].m_isToggle) { keystroke.m_press = false; @@ -1210,33 +925,33 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, // release. we must check each keycode for the // modifier if not a toggle. if (s_modifier[i].m_isToggle) { - keystroke.m_virtualKey = s_modifier[i].m_virtualKey; - keystroke.m_press = true; - keystroke.m_repeat = false; + keystroke.m_sysKeyID = s_modifier[i].m_virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = false; keys.push_back(keystroke); - keystroke.m_press = false; + keystroke.m_press = false; keys.push_back(keystroke); undo.push_back(keystroke); - keystroke.m_press = true; + keystroke.m_press = true; undo.push_back(keystroke); } else { UINT key = s_modifier[i].m_virtualKey; - if ((m_keys[key & 0xff] & 0x80) != 0) { - keystroke.m_virtualKey = key; - keystroke.m_press = false; - keystroke.m_repeat = false; + if (isKeyDown(key & 0xff)) { + keystroke.m_sysKeyID = key; + keystroke.m_press = false; + keystroke.m_repeat = false; keys.push_back(keystroke); - keystroke.m_press = true; + keystroke.m_press = true; undo.push_back(keystroke); } key = s_modifier[i].m_virtualKey2; - if (key != 0 && (m_keys[key & 0xff] & 0x80) != 0) { - keystroke.m_virtualKey = key; - keystroke.m_press = false; - keystroke.m_repeat = false; + if (isKeyDown(key & 0xff)) { + keystroke.m_sysKeyID = key; + keystroke.m_press = false; + keystroke.m_repeat = false; keys.push_back(keystroke); - keystroke.m_press = true; + keystroke.m_press = true; undo.push_back(keystroke); } } @@ -1246,7 +961,7 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, } // add the key event - keystroke.m_virtualKey = virtualKey; + keystroke.m_sysKeyID = virtualKey; switch (action) { case kPress: keystroke.m_press = true; @@ -1297,46 +1012,13 @@ CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, return mask; } -void -CMSWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) -{ - // do nothing if no keys or no repeats - if (count < 1 || keys.empty()) { - return; - } - - // generate key events - for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { - if (k->m_repeat) { - // repeat from here up to but not including the next key - // with m_repeat == false count times. - Keystrokes::const_iterator start = k; - for (; count > 0; --count) { - // send repeating events - for (k = start; k != keys.end() && k->m_repeat; ++k) { - sendKeyEvent(k->m_virtualKey, k->m_press); - } - } - - // note -- k is now on the first non-repeat key after the - // repeat keys, exactly where we'd like to continue from. - } - else { - // send event - sendKeyEvent(k->m_virtualKey, k->m_press); - - // next key - ++k; - } - } -} const CMSWindowsSecondaryScreen::CModifierInfo* CMSWindowsSecondaryScreen::getModifierInfo(UINT virtualKey) const { // note if the key is a modifier. strip out extended key flag from // virtual key before lookup. - switch (virtualKey & ~0x100) { + switch (virtualKey & 0xffu) { case VK_SHIFT: case VK_LSHIFT: case VK_RSHIFT: @@ -1370,72 +1052,22 @@ CMSWindowsSecondaryScreen::getModifierInfo(UINT virtualKey) const } } -void -CMSWindowsSecondaryScreen::releaseKeys() +CSecondaryScreen::SysKeyID +CMSWindowsSecondaryScreen::getToggleSysKey(KeyID keyID) const { - // release keys that we've synthesized a press for and only those - // keys. we don't want to synthesize a release on a key the user - // is still physically pressing. + switch (keyID) { + case kKeyNumLock: + return VK_NUMLOCK | 0x100; - CLock lock(&m_mutex); + case kKeyCapsLock: + return VK_CAPITAL; - m_screen->syncDesktop(); + case kKeyScrollLock: + return VK_SCROLL; - // release left/right modifier keys first. if the platform doesn't - // support them then they won't be set and the non-side-distinuishing - // key will retain its state. if the platform does support them then - // the non-side-distinguishing will be reset. - if ((m_fakeKeys[VK_LSHIFT] & 0x80) != 0) { - sendKeyEvent(VK_LSHIFT, false); - m_fakeKeys[VK_SHIFT] = 0; - m_fakeKeys[VK_LSHIFT] = 0; + default: + return 0; } - if ((m_fakeKeys[VK_RSHIFT] & 0x80) != 0) { - sendKeyEvent(VK_RSHIFT, false); - m_fakeKeys[VK_SHIFT] = 0; - m_fakeKeys[VK_RSHIFT] = 0; - } - if ((m_fakeKeys[VK_LCONTROL] & 0x80) != 0) { - sendKeyEvent(VK_LCONTROL, false); - m_fakeKeys[VK_CONTROL] = 0; - m_fakeKeys[VK_LCONTROL] = 0; - } - if ((m_fakeKeys[VK_RCONTROL] & 0x80) != 0) { - sendKeyEvent(VK_RCONTROL, false); - m_fakeKeys[VK_CONTROL] = 0; - m_fakeKeys[VK_RCONTROL] = 0; - } - if ((m_fakeKeys[VK_LMENU] & 0x80) != 0) { - sendKeyEvent(VK_LMENU, false); - m_fakeKeys[VK_MENU] = 0; - m_fakeKeys[VK_LMENU] = 0; - } - if ((m_fakeKeys[VK_RMENU] & 0x80) != 0) { - sendKeyEvent(VK_RMENU, false); - m_fakeKeys[VK_MENU] = 0; - m_fakeKeys[VK_RMENU] = 0; - } - - // now check all the other keys - for (UInt32 i = 0; i < sizeof(m_fakeKeys) / sizeof(m_fakeKeys[0]); ++i) { - if ((m_fakeKeys[i] & 0x80) != 0) { - sendKeyEvent(i, false); - m_fakeKeys[i] = 0; - } - } -} - -void -CMSWindowsSecondaryScreen::toggleKey(UINT virtualKey, KeyModifierMask mask) -{ - // send key events to simulate a press and release - sendKeyEvent(virtualKey, true); - sendKeyEvent(virtualKey, false); - - // toggle shadow state - m_mask ^= mask; - m_keys[virtualKey & 0xff] ^= 0x01; - m_fakeKeys[virtualKey & 0xff] ^= 0x01; } UINT @@ -1499,7 +1131,7 @@ CMSWindowsSecondaryScreen::isExtendedKey(UINT virtualKey) const } // check known virtual keys - switch (virtualKey & 0xff) { + switch (virtualKey & 0xffu) { case VK_NUMLOCK: case VK_RCONTROL: case VK_RMENU: @@ -1514,7 +1146,7 @@ CMSWindowsSecondaryScreen::isExtendedKey(UINT virtualKey) const } void -CMSWindowsSecondaryScreen::sendKeyEvent(UINT virtualKey, bool press) +CMSWindowsSecondaryScreen::fakeKeyEvent(UINT virtualKey, bool press) const { DWORD flags = 0; if (isExtendedKey(virtualKey)) { @@ -1526,7 +1158,104 @@ CMSWindowsSecondaryScreen::sendKeyEvent(UINT virtualKey, bool press) const UINT code = virtualKeyToScanCode(virtualKey); keybd_event(static_cast(virtualKey & 0xff), static_cast(code), flags, 0); - LOG((CLOG_DEBUG1 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : ""))); +} + +void +CMSWindowsSecondaryScreen::fakeMouseButton(ButtonID button, bool press) const +{ + // map button id to button flag + DWORD data; + DWORD flags = mapButton(button, press, &data); + + // send event + if (flags != 0) { + mouse_event(flags, 0, 0, data, 0); + } +} + +void +CMSWindowsSecondaryScreen::fakeMouseMove(SInt32 x, SInt32 y) const +{ + // motion is simple (i.e. it's on the primary monitor) if there + // is only one monitor. + bool simple = !m_screen->isMultimon(); + if (!simple) { + // also simple if motion is within the primary monitor + simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && + y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); + } + + // move the mouse directly to target position if motion is simple + if (simple) { + // when using absolute positioning with mouse_event(), + // the normalized device coordinates range over only + // the primary screen. + SInt32 w = GetSystemMetrics(SM_CXSCREEN); + SInt32 h = GetSystemMetrics(SM_CYSCREEN); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65536.0 * x) / w), + (DWORD)((65536.0 * y) / h), + 0, 0); + } + + // windows 98 (and Me?) is broken. you cannot set the absolute + // position of the mouse except on the primary monitor but you + // can do relative moves onto any monitor. this is, in microsoft's + // words, "by design." apparently the designers of windows 2000 + // we're a little less lazy and did it right. + // + // microsoft recommends in Q193003 to absolute position the cursor + // somewhere on the primary monitor then relative move to the + // desired location. this doesn't work for us because when the + // user drags a scrollbar, a window, etc. it causes the dragged + // item to jump back a forth between the position on the primary + // monitor and the desired position. while it always ends up in + // the right place, the effect is disconcerting. + // + // instead we'll get the cursor's current position and do just a + // relative move from there to the desired position. relative + // moves are subject to cursor acceleration which we don't want. + // so we disable acceleration, do the relative move, then restore + // acceleration. there's a slight chance we'll end up in the + // wrong place if the user moves the cursor using this system's + // mouse while simultaneously moving the mouse on the server + // system. that defeats the purpose of synergy so we'll assume + // that won't happen. even if it does, the next mouse move will + // correct the position. + else { + // save mouse speed & acceleration + int oldSpeed[4]; + bool accelChanged = + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && + SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); + + // use 1:1 motion + if (accelChanged) { + int newSpeed[4] = { 0, 0, 0, 1 }; + accelChanged = + SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || + SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); + } + + // get current mouse position + POINT pos; + GetCursorPos(&pos); + + // move relative to mouse position + mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); + + // restore mouse speed & acceleration + if (accelChanged) { + SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); + SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); + } + } +} + +void +CMSWindowsSecondaryScreen::fakeMouseWheel(SInt32 delta) +{ + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); } UINT @@ -1554,10 +1283,14 @@ CMSWindowsSecondaryScreen::getCodePageFromLangID(LANGID langid) const return codePage; } -void -CMSWindowsSecondaryScreen::synthesizeCtrlAltDel() +bool +CMSWindowsSecondaryScreen::synthesizeCtrlAltDel(EKeyAction action) { - LOG((CLOG_DEBUG "emulating ctrl+alt+del")); + // ignore except for key press + if (action != kPress) { + return true; + } + if (!m_is95Family) { // to fake ctrl+alt+del on the NT family we broadcast a suitable // hotkey to all windows on the winlogon desktop. however, we @@ -1581,6 +1314,7 @@ CMSWindowsSecondaryScreen::synthesizeCtrlAltDel() doKeystrokes(keys, 1); } } + return true; } void diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h index ed1bdf98..c402a9ba 100644 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ b/lib/platform/CMSWindowsSecondaryScreen.h @@ -25,8 +25,6 @@ #include "IMSWindowsScreenEventHandler.h" #include "CMutex.h" #include "CString.h" -#include "stdmap.h" -#include "stdvector.h" class CMSWindowsScreen; class IScreenReceiver; @@ -39,16 +37,6 @@ public: virtual ~CMSWindowsSecondaryScreen(); // CSecondaryScreen overrides - virtual void keyDown(KeyID, KeyModifierMask, KeyButton); - virtual void keyRepeat(KeyID, KeyModifierMask, - SInt32 count, KeyButton); - virtual void keyUp(KeyID, KeyModifierMask, KeyButton); - virtual void mouseDown(ButtonID); - virtual void mouseUp(ButtonID); - virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void mouseWheel(SInt32 delta); - virtual void resetOptions(); - virtual void setOptions(const COptionsList& options); virtual IScreen* getScreen() const; // IMSWindowsScreenEventHandler overrides @@ -56,7 +44,6 @@ public: virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); virtual void onOneShotTimerExpired(UInt32 id); - virtual SInt32 getJumpZoneSize() const; virtual void postCreateWindow(HWND); virtual void preDestroyWindow(HWND); virtual void onAccessibleDesktop(); @@ -71,20 +58,26 @@ protected: virtual void destroyWindow(); virtual void showWindow(SInt32 x, SInt32 y); virtual void hideWindow(); - virtual void warpCursor(SInt32 x, SInt32 y); - virtual void updateKeys(); - virtual void releaseKeys(); - virtual void setToggleState(KeyModifierMask); - virtual KeyModifierMask getToggleState() const; + virtual void updateKeys(KeyState* sysKeyStates); + virtual KeyModifierMask getModifiers() const; + + virtual SysKeyID getUnhanded(SysKeyID) const; + virtual SysKeyID getOtherHanded(SysKeyID) const; + virtual bool isAutoRepeating(SysKeyID) const; + virtual KeyModifierMask getModifierKeyMask(SysKeyID) const; + virtual bool isModifierActive(SysKeyID) const; + virtual SysKeyID getToggleSysKey(KeyID keyID) const; + virtual bool synthesizeCtrlAltDel(EKeyAction); + virtual void sync() const; + virtual KeyModifierMask + mapKey(Keystrokes&, SysKeyID& sysKeyID, KeyID, + KeyModifierMask, KeyModifierMask, EKeyAction) const; + virtual void fakeKeyEvent(SysKeyID, bool press) const; + virtual void fakeMouseButton(ButtonID, bool press) const; + virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseWheel(SInt32 delta) const; private: - enum EKeyAction { kPress, kRelease, kRepeat }; - class Keystroke { - public: - UINT m_virtualKey; - bool m_press; - bool m_repeat; - }; class CModifierInfo { public: KeyModifierMask m_mask; @@ -92,8 +85,6 @@ private: UINT m_virtualKey2; bool m_isToggle; }; - typedef std::vector Keystrokes; - typedef std::map ServerKeyMap; // open/close desktop (for windows 95/98/me) bool openDesktop(); @@ -108,9 +99,6 @@ private: // key and button queries and operations DWORD mapButton(ButtonID button, bool press, DWORD* data) const; - KeyModifierMask mapKey(Keystrokes&, UINT& virtualKey, KeyID, - KeyModifierMask, EKeyAction) const; - KeyModifierMask mapKeyRelease(Keystrokes& keys, UINT virtualKey) const; UINT mapCharacter(Keystrokes& keys, char c, HKL hkl, KeyModifierMask currentMask, @@ -121,19 +109,14 @@ private: KeyModifierMask currentMask, KeyModifierMask desiredMask, EKeyAction action) const; - void doKeystrokes(const Keystrokes&, SInt32 count); const CModifierInfo* getModifierInfo(UINT virtualKey) const; - void toggleKey(UINT virtualKey, KeyModifierMask mask); + KeyState getKeyState(UINT virtualKey) const; UINT virtualKeyToScanCode(UINT& virtualKey) const; bool isExtendedKey(UINT virtualKey) const; - void sendKeyEvent(UINT virtualKey, bool press); UINT getCodePageFromLangID(LANGID) const; - // generate a fake ctrl+alt+del - void synthesizeCtrlAltDel(); - // thread that generates fake ctrl+alt+del static void ctrlAltDelThread(void*); @@ -147,18 +130,6 @@ private: // our window HWND m_window; - // virtual key states as set by us or the user - BYTE m_keys[256]; - - // virtual key states as set by us - BYTE m_fakeKeys[256]; - - // current active modifiers - KeyModifierMask m_mask; - - // map server key buttons to local virtual keys - ServerKeyMap m_serverKeyMap; - // modifier table static const CModifierInfo s_modifier[]; }; diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp index b69369e9..11096b3a 100644 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ b/lib/platform/CXWindowsSecondaryScreen.cpp @@ -57,46 +57,6 @@ # endif #endif -// -// utility functions -// - -inline -static -unsigned int getBits(unsigned int src, unsigned int mask) -{ - return src & mask; -} - -inline -static -unsigned int setBits(unsigned int src, unsigned int mask) -{ - return src | mask; -} - -inline -static -unsigned int clearBits(unsigned int src, unsigned int mask) -{ - return src & ~mask; -} - -inline -static -unsigned int flipBits(unsigned int src, unsigned int mask) -{ - return src ^ mask; -} - -inline -static -unsigned int assignBits(unsigned int src, - unsigned int mask, unsigned int value) -{ - return setBits(clearBits(src, mask), clearBits(value, ~mask)); -} - // // CXWindowsSecondaryScreen @@ -122,220 +82,39 @@ CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() delete m_screen; } -void -CXWindowsSecondaryScreen::keyDown(KeyID key, - KeyModifierMask mask, KeyButton button) +bool +CXWindowsSecondaryScreen::isAutoRepeating(SysKeyID sysKeyID) const { - // check for ctrl+alt+del emulation - if (key == kKeyDelete && - (mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - LOG((CLOG_DEBUG "ctrl+alt+del emulation")); - // just pass the key through - } - - // get the sequence of keys to simulate key press and the final - // modifier state. - Keystrokes keys; - KeyCode keycode; - m_mask = mapKey(keys, keycode, key, mask, kPress); - if (keys.empty()) { - // do nothing if there are no associated keys (i.e. lookup failed) - return; - } - - // generate key events - doKeystrokes(keys, 1); - - // do not record button down if button is 0 (invalid) - if (button != 0) { - // note that key is now down - m_serverKeyMap[button] = keycode; - m_keys[keycode] = true; - m_fakeKeys[keycode] = true; - } + char bit = static_cast(1 << (sysKeyID & 7)); + return ((m_keyControl.auto_repeats[sysKeyID >> 3] & bit) != 0); } void -CXWindowsSecondaryScreen::keyRepeat(KeyID key, - KeyModifierMask mask, SInt32 count, KeyButton button) +CXWindowsSecondaryScreen::flush() { - // if we haven't seen this button go down then ignore it - ServerKeyMap::iterator index = m_serverKeyMap.find(button); - if (index == m_serverKeyMap.end()) { - return; - } - - // get the sequence of keys to simulate key repeat and the final - // modifier state. - Keystrokes keys; - KeyCode keycode; - m_mask = mapKey(keys, keycode, key, mask, kRepeat); - if (keys.empty()) { - return; - } - - // if this keycode shouldn't auto-repeat then ignore - if ((m_keyControl.auto_repeats[keycode >> 3] & (1 << (keycode & 7))) == 0) { - return; - } - - // if the keycode for the auto-repeat is not the same as for the - // initial press then mark the initial key as released and the new - // key as pressed. this can happen when we auto-repeat after a - // dead key. for example, a dead accent followed by 'a' will - // generate an 'a with accent' followed by a repeating 'a'. the - // keycodes for the two keysyms might be different. - if (keycode != index->second) { - // replace key up with previous keycode but leave key down - // alone so it uses the new keycode and store that keycode - // in the server key map. the key up is the first keystroke - // with the keycode returned by mapKey(). - for (Keystrokes::iterator index2 = keys.begin(); - index2 != keys.end(); ++index2) { - if (index2->m_keycode == keycode) { - index2->m_keycode = index->second; - break; - } - } - - // note that old key is now up - m_keys[index->second] = false; - m_fakeKeys[index->second] = false; - - // map server key to new key - index->second = keycode; - - // note that new key is now down - m_keys[index->second] = true; - m_fakeKeys[index->second] = true; - } - - // generate key events - doKeystrokes(keys, count); -} - -void -CXWindowsSecondaryScreen::keyUp(KeyID key, - KeyModifierMask mask, KeyButton button) -{ - // if we haven't seen this button go down then ignore it - ServerKeyMap::iterator index = m_serverKeyMap.find(button); - if (index == m_serverKeyMap.end()) { - return; - } - KeyCode keycode = index->second; - - // check for ctrl+alt+del emulation - if (key == kKeyDelete && - (mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - LOG((CLOG_DEBUG "ctrl+alt+del emulation")); - // just pass the key through - } - - // get the sequence of keys to simulate key release and the final - // modifier state. - Keystrokes keys; - if (!((key == kKeyCapsLock && m_capsLockHalfDuplex) || - (key == kKeyNumLock && m_numLockHalfDuplex))) { - m_mask = mapKeyRelease(keys, keycode); - } - - // generate key events - doKeystrokes(keys, 1); - - // note that key is now up - m_serverKeyMap.erase(index); - m_keys[keycode] = false; - m_fakeKeys[keycode] = false; -} - -void -CXWindowsSecondaryScreen::flush(Display* display) const -{ - XFlush(display); -} - -void -CXWindowsSecondaryScreen::mouseDown(ButtonID button) -{ - const unsigned int xButton = mapButton(button); - if (xButton != 0) { - CDisplayLock display(m_screen); - XTestFakeButtonEvent(display, xButton, True, CurrentTime); - flush(display); - } -} - -void -CXWindowsSecondaryScreen::mouseUp(ButtonID button) -{ - const unsigned int xButton = mapButton(button); - if (xButton != 0) { - CDisplayLock display(m_screen); - XTestFakeButtonEvent(display, xButton, False, CurrentTime); - flush(display); - } -} - -void -CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) -{ - warpCursor(x, y); -} - -void -CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) -{ - // choose button depending on rotation direction - const unsigned int xButton = mapButton(static_cast( - (delta >= 0) ? -1 : -2)); - if (xButton == 0) { - return; - } - - // now use absolute value of delta - if (delta < 0) { - delta = -delta; - } - - // send as many clicks as necessary CDisplayLock display(m_screen); - for (; delta >= 120; delta -= 120) { - XTestFakeButtonEvent(display, xButton, True, CurrentTime); - XTestFakeButtonEvent(display, xButton, False, CurrentTime); + if (display != NULL) { + XFlush(display); } - flush(display); } void CXWindowsSecondaryScreen::resetOptions() { - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; - m_xtestIsXineramaUnaware = true; CSecondaryScreen::resetOptions(); + m_xtestIsXineramaUnaware = true; } void CXWindowsSecondaryScreen::setOptions(const COptionsList& options) { + CSecondaryScreen::setOptions(options); for (UInt32 i = 0, n = options.size(); i < n; i += 2) { - if (options[i] == kOptionHalfDuplexCapsLock) { - m_capsLockHalfDuplex = (options[i + 1] != 0); - LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); - } - else if (options[i] == kOptionHalfDuplexNumLock) { - m_numLockHalfDuplex = (options[i + 1] != 0); - LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); - } - else if (options[i] == kOptionXTestXineramaUnaware) { + if (options[i] == kOptionXTestXineramaUnaware) { m_xtestIsXineramaUnaware = (options[i + 1] != 0); LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false")); } } - CSecondaryScreen::setOptions(options); } IScreen* @@ -384,12 +163,6 @@ CXWindowsSecondaryScreen::onOneShotTimerExpired(UInt32) // ignore } -SInt32 -CXWindowsSecondaryScreen::getJumpZoneSize() const -{ - return 0; -} - void CXWindowsSecondaryScreen::onPreMainLoop() { @@ -523,16 +296,13 @@ void CXWindowsSecondaryScreen::destroyWindow() { { + // release keys that are still pressed + releaseKeys(); + CDisplayLock display(m_screen); if (display != NULL) { - // release keys that are still pressed - doReleaseKeys(display); - // no longer impervious to server grabs XTestGrabControl(display, False); - - // update - flush(display); } } @@ -564,7 +334,7 @@ CXWindowsSecondaryScreen::showWindow(SInt32 x, SInt32 y) // now warp the mouse. we warp after showing the window so we're // guaranteed to get the mouse leave event and to prevent the // keyboard focus from changing under point-to-focus policies. - warpCursor(x, y); + fakeMouseMove(x, y); } void @@ -576,56 +346,6 @@ CXWindowsSecondaryScreen::hideWindow() XUnmapWindow(display, m_window); } -void -CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) -{ - CDisplayLock display(m_screen); - Display* pDisplay = display; - - if (m_xinerama && m_xtestIsXineramaUnaware) { - XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); - } - else { - XTestFakeMotionEvent(display, DefaultScreen(pDisplay), - x, y, CurrentTime); - } - flush(display); -} - -void -CXWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) -{ - CDisplayLock display(m_screen); - - // toggle modifiers that don't match the desired state - ModifierMask xMask = maskToX(mask); - if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) { - toggleKey(display, m_capsLockKeysym, m_capsLockMask); - } - if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) { - toggleKey(display, m_numLockKeysym, m_numLockMask); - } - if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { - toggleKey(display, m_scrollLockKeysym, m_scrollLockMask); - } -} - -KeyModifierMask -CXWindowsSecondaryScreen::getToggleState() const -{ - KeyModifierMask mask = 0; - if ((m_mask & m_capsLockMask) != 0) { - mask |= KeyModifierCapsLock; - } - if ((m_mask & m_numLockMask) != 0) { - mask |= KeyModifierNumLock; - } - if ((m_mask & m_scrollLockMask) != 0) { - mask |= KeyModifierScrollLock; - } - return mask; -} - unsigned int CXWindowsSecondaryScreen::mapButton(ButtonID id) const { @@ -655,9 +375,11 @@ CXWindowsSecondaryScreen::mapButton(ButtonID id) const return static_cast(m_buttons[id - 1]); } -CXWindowsSecondaryScreen::ModifierMask -CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, - KeyID id, KeyModifierMask mask, EKeyAction action) const +KeyModifierMask +CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, + SysKeyID& keycode, KeyID id, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, EKeyAction action) const { // note -- must have display locked on entry @@ -666,27 +388,23 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // generate the right keysym we need to set the modifier key // states appropriately. // - // the mask passed by the caller is the desired mask. however, - // there may not be a keycode mapping to generate the desired - // keysym with that mask. we override the bits in the mask - // that cannot be accomodated. + // desiredMask is the mask desired by the caller. however, there + // may not be a keycode mapping to generate the desired keysym + // with that mask. we override the bits in the mask that cannot + // be accomodated. // ignore releases and repeats for half-duplex keys - const bool isHalfDuplex = ((id == kKeyCapsLock && m_capsLockHalfDuplex) || - (id == kKeyNumLock && m_numLockHalfDuplex)); + const bool isHalfDuplex = isKeyHalfDuplex(id); if (isHalfDuplex && action != kPress) { - return m_mask; + return currentMask; } - // requested notes the modifiers requested by the server. - ModifierMask requested = maskToX(mask); - // convert KeyID to a KeySym - KeySym keysym = keyIDToKeySym(id, requested); + KeySym keysym = keyIDToKeySym(id, desiredMask); if (keysym == NoSymbol) { // unknown key LOG((CLOG_DEBUG2 "no keysym for id 0x%08x", id)); - return m_mask; + return currentMask; } // get the mapping for this keysym @@ -712,28 +430,29 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // and that modifier is already in the desired state then // ignore the request since there's nothing to do. never // ignore a toggle modifier on press or release, though. - const KeyMapping& keyMapping = keyIndex->second; - const ModifierMask modifierBit = keyMapping.m_modifierMask; + const KeyMapping& keyMapping = keyIndex->second; + const KeyModifierMask modifierBit = keyMapping.m_modifierMask; if (modifierBit != 0) { if (action == kRepeat) { LOG((CLOG_DEBUG2 "ignore repeating modifier")); - return m_mask; + return currentMask; } - if (getBits(m_toggleModifierMask, modifierBit) == 0) { - if ((action == kPress && (m_mask & modifierBit) != 0) || - (action == kRelease && (m_mask & modifierBit) == 0)) { - LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", m_mask)); - return m_mask; + if ((m_toggleModifierMask & modifierBit) == 0) { + if ((action == kPress && (currentMask & modifierBit) != 0) || + (action == kRelease && (currentMask & modifierBit) == 0)) { + LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", currentMask)); + return currentMask; } } } // create the keystrokes for this keysym - ModifierMask mask; - if (!mapToKeystrokes(keys, keycode, mask, keyIndex, m_mask, action)) { + KeyModifierMask mask; + if (!mapToKeystrokes(keys, keycode, mask, + keyIndex, currentMask, action, isHalfDuplex)) { // failed to generate keystrokes keys.clear(); - return m_mask; + return currentMask; } else { // success @@ -754,7 +473,7 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // and the keycode from the last keysym (which should be the // only non-dead key). the dead keys are not sensitive to // anything but shift and mode switch. - ModifierMask mask; + KeyModifierMask mask; for (KeySyms::const_iterator i = decomposition.begin(); i != decomposition.end();) { // increment the iterator @@ -768,15 +487,15 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, // missing a required keysym LOG((CLOG_DEBUG2 "no keycode for decomposed keysym 0x%08x", keysym)); keys.clear(); - return m_mask; + return currentMask; } // the keysym is mapped to some keycode if (!mapToKeystrokes(keys, keycode, mask, - keyIndex, m_mask, action)) { + keyIndex, currentMask, action, isHalfDuplex)) { // failed to generate keystrokes keys.clear(); - return m_mask; + return currentMask; } // on to the next keysym @@ -787,50 +506,41 @@ CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, KeyCode& keycode, } LOG((CLOG_DEBUG2 "no keycode for keysym")); - return m_mask; + return currentMask; } -CXWindowsSecondaryScreen::ModifierMask -CXWindowsSecondaryScreen::mapKeyRelease(Keystrokes& keys, KeyCode keycode) const +KeyModifierMask +CXWindowsSecondaryScreen::getModifierKeyMask(SysKeyID keycode) const { - // add key release - Keystroke keystroke; - keystroke.m_keycode = keycode; - keystroke.m_press = False; - keystroke.m_repeat = false; - keys.push_back(keystroke); - - // if this is a modifier keycode then update the current modifier mask KeyCodeToModifierMap::const_iterator i = m_keycodeToModifier.find(keycode); - if (i != m_keycodeToModifier.end()) { - ModifierMask bit = (1 << i->second); - if (getBits(m_toggleModifierMask, bit) != 0) { - // toggle keys modify the state on release - return flipBits(m_mask, bit); - } - else { - // can't reset bit until all keys that set it are released. - // scan those keys to see if any (except keycode) are pressed. - KeyCodes::const_iterator j; - const KeyCodes& keycodes = m_modifierKeycodes[i->second]; - for (j = keycodes.begin(); j != keycodes.end(); ++j) { - KeyCode modKeycode = *j; - if (modKeycode != keycode && m_keys[modKeycode]) { - break; - } - } - if (j == keycodes.end()) { - return clearBits(m_mask, bit); + if (i == m_keycodeToModifier.end()) { + return 0; + } + return m_modifierIndexToMask[i->second]; +} + +bool +CXWindowsSecondaryScreen::isModifierActive(SysKeyID keycode) const +{ + // check if any keycode for this modifier is down. return false + // for toggle modifiers. + KeyCodeToModifierMap::const_iterator i = m_keycodeToModifier.find(keycode); + if (i != m_keycodeToModifier.end() && + (m_modifierIndexToMask[i->second] & m_toggleModifierMask) != 0) { + const KeyCodes& keycodes = m_modifierKeycodes[i->second]; + for (KeyCodes::const_iterator j = keycodes.begin(); + j != keycodes.end(); ++j) { + if (isKeyDown(*j)) { + return true; } } } - - return m_mask; + return false; } unsigned int CXWindowsSecondaryScreen::findBestKeyIndex(KeySymIndex keyIndex, - ModifierMask /*currentMask*/) const + KeyModifierMask /*currentMask*/) const { // there are up to 4 keycodes per keysym to choose from. the // best choice is the one that requires the fewest adjustments @@ -856,7 +566,7 @@ CXWindowsSecondaryScreen::findBestKeyIndex(KeySymIndex keyIndex, bool CXWindowsSecondaryScreen::isShiftInverted(KeySymIndex keyIndex, - ModifierMask currentMask) const + KeyModifierMask currentMask) const { // each keycode has up to 4 keysym associated with it, one each for: // no modifiers, shift, mode switch, and shift and mode switch. if @@ -867,7 +577,7 @@ CXWindowsSecondaryScreen::isShiftInverted(KeySymIndex keyIndex, // method returns true iff the sense of shift should be inverted // for this key given a modifier state. if (keyIndex->second.m_numLockSensitive) { - if (getBits(currentMask, m_numLockMask) != 0) { + if ((currentMask & KeyModifierNumLock) != 0) { return true; } } @@ -875,7 +585,7 @@ CXWindowsSecondaryScreen::isShiftInverted(KeySymIndex keyIndex, // if a keysym is num lock sensitive it is never caps lock // sensitive, thus the else here. else if (keyIndex->second.m_capsLockSensitive) { - if (getBits(currentMask, m_capsLockMask) != 0) { + if ((currentMask & KeyModifierCapsLock) != 0) { return true; } } @@ -883,28 +593,14 @@ CXWindowsSecondaryScreen::isShiftInverted(KeySymIndex keyIndex, return false; } -CXWindowsSecondaryScreen::ModifierMask -CXWindowsSecondaryScreen::getModifierMask(KeySym keysym) const -{ - // find the keysym mapping. if it exists and there's a keycode - // for index 0 (the index we use for modifiers) then return the - // modifierMask, which might be 0. otherwise return 0. - KeySymIndex keyIndex = m_keysymMap.find(keysym); - if (keyIndex != m_keysymMap.end() && keyIndex->second.m_keycode[0] != 0) { - return keyIndex->second.m_modifierMask; - } - else { - return 0; - } -} - bool CXWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, - KeyCode& keycode, - ModifierMask& finalMask, + SysKeyID& keycode, + KeyModifierMask& finalMask, KeySymIndex keyIndex, - ModifierMask currentMask, - EKeyAction action) const + KeyModifierMask currentMask, + EKeyAction action, + bool isHalfDuplex) const { // keyIndex must be valid assert(keyIndex != m_keysymMap.end()); @@ -948,23 +644,23 @@ CXWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, // is not sensitive to shift then don't adjust it, otherwise // something like shift+home would become just home. similiarly // for mode switch. - ModifierMask desiredMask = currentMask; - if (keyIndex->second.m_modifierMask != m_shiftMask) { + KeyModifierMask desiredMask = currentMask; + if (keyIndex->second.m_modifierMask != KeyModifierShift) { if (keyIndex->second.m_shiftSensitive[bestIndex]) { if ((bestIndex & 1) != 0) { - desiredMask = setBits(desiredMask, m_shiftMask); + desiredMask |= KeyModifierShift; } else { - desiredMask = clearBits(desiredMask, m_shiftMask); + desiredMask &= ~KeyModifierShift; } } - if (keyIndex->second.m_modifierMask != m_modeSwitchMask) { + if (keyIndex->second.m_modifierMask != KeyModifierModeSwitch) { if (keyIndex->second.m_modeSwitchSensitive[bestIndex]) { if ((bestIndex & 2) != 0) { - desiredMask = setBits(desiredMask, m_modeSwitchMask); + desiredMask |= KeyModifierModeSwitch; } else { - desiredMask = clearBits(desiredMask, m_modeSwitchMask); + desiredMask &= ~KeyModifierModeSwitch; } } } @@ -972,41 +668,38 @@ CXWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, // adjust the modifiers to match the desired modifiers Keystrokes undo; - ModifierMask tmpMask = currentMask; + KeyModifierMask tmpMask = currentMask; if (!adjustModifiers(keys, undo, tmpMask, desiredMask)) { LOG((CLOG_DEBUG2 "failed to adjust modifiers")); return false; } // note if the press of a half-duplex key should be treated as a release - const bool isHalfDuplex = - ((keysym == m_capsLockKeysym && m_capsLockHalfDuplex) || - (keysym == m_numLockKeysym && m_numLockHalfDuplex)); - if (isHalfDuplex && getBits(currentMask, mapping.m_modifierMask) != 0) { + if (isHalfDuplex && (currentMask & mapping.m_modifierMask) != 0) { action = kRelease; } // add the key event Keystroke keystroke; - keystroke.m_keycode = keycode; + keystroke.m_sysKeyID = keycode; switch (action) { case kPress: - keystroke.m_press = True; + keystroke.m_press = true; keystroke.m_repeat = false; keys.push_back(keystroke); break; case kRelease: - keystroke.m_press = False; + keystroke.m_press = false; keystroke.m_repeat = false; keys.push_back(keystroke); break; case kRepeat: - keystroke.m_press = False; + keystroke.m_press = false; keystroke.m_repeat = true; keys.push_back(keystroke); - keystroke.m_press = True; + keystroke.m_press = true; keys.push_back(keystroke); break; } @@ -1027,13 +720,13 @@ CXWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, // toggle keys modify the state on release. other keys set the // bit on press and clear the bit on release. if half-duplex // then toggle each time we get here. - if (getBits(m_toggleModifierMask, mapping.m_modifierMask) != 0) { + if ((m_toggleModifierMask & mapping.m_modifierMask) != 0) { if (isHalfDuplex) { - finalMask = flipBits(finalMask, mapping.m_modifierMask); + finalMask ^= mapping.m_modifierMask; } } else if (action == kPress) { - finalMask = setBits(finalMask, mapping.m_modifierMask); + finalMask |= mapping.m_modifierMask; } } @@ -1043,14 +736,14 @@ CXWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, bool CXWindowsSecondaryScreen::adjustModifiers(Keystrokes& keys, Keystrokes& undo, - ModifierMask& inOutMask, - ModifierMask desiredMask) const + KeyModifierMask& inOutMask, + KeyModifierMask desiredMask) const { // get mode switch set correctly. do this before shift because // mode switch may be sensitive to the shift modifier and will // set/reset it as necessary. - const bool wantModeSwitch = ((desiredMask & m_modeSwitchMask) != 0); - const bool haveModeSwitch = ((inOutMask & m_modeSwitchMask) != 0); + const bool wantModeSwitch = ((desiredMask & KeyModifierModeSwitch) != 0); + const bool haveModeSwitch = ((inOutMask & KeyModifierModeSwitch) != 0); if (wantModeSwitch != haveModeSwitch) { LOG((CLOG_DEBUG2 "fix mode switch")); @@ -1059,14 +752,14 @@ CXWindowsSecondaryScreen::adjustModifiers(Keystrokes& keys, assert(modeSwitchIndex != m_keysymMap.end()); if (modeSwitchIndex->second.m_shiftSensitive[0]) { const bool wantShift = false; - const bool haveShift = ((inOutMask & m_shiftMask) != 0); + const bool haveShift = ((inOutMask & KeyModifierShift) != 0); if (wantShift != haveShift) { // add shift keystrokes LOG((CLOG_DEBUG2 "fix shift for mode switch")); if (!adjustModifier(keys, undo, m_shiftKeysym, wantShift)) { return false; } - inOutMask ^= m_shiftMask; + inOutMask ^= KeyModifierShift; } } @@ -1074,19 +767,19 @@ CXWindowsSecondaryScreen::adjustModifiers(Keystrokes& keys, if (!adjustModifier(keys, undo, m_modeSwitchKeysym, wantModeSwitch)) { return false; } - inOutMask ^= m_modeSwitchMask; + inOutMask ^= KeyModifierModeSwitch; } // get shift set correctly - const bool wantShift = ((desiredMask & m_shiftMask) != 0); - const bool haveShift = ((inOutMask & m_shiftMask) != 0); + const bool wantShift = ((desiredMask & KeyModifierShift) != 0); + const bool haveShift = ((inOutMask & KeyModifierShift) != 0); if (wantShift != haveShift) { // add shift keystrokes LOG((CLOG_DEBUG2 "fix shift")); if (!adjustModifier(keys, undo, m_shiftKeysym, wantShift)) { return false; } - inOutMask ^= m_shiftMask; + inOutMask ^= KeyModifierShift; } return true; @@ -1121,7 +814,7 @@ CXWindowsSecondaryScreen::adjustModifier(Keystrokes& keys, // initialize keystroke Keystroke keystroke; - keystroke.m_repeat = false; + keystroke.m_repeat = false; // releasing a modifier is quite different from pressing one. // when we release a modifier we have to release every keycode that @@ -1130,10 +823,10 @@ CXWindowsSecondaryScreen::adjustModifier(Keystrokes& keys, // press one of those keycodes. if (desireActive) { // press - keystroke.m_keycode = keyIndex->second.m_keycode[0]; - keystroke.m_press = True; + keystroke.m_sysKeyID = keyIndex->second.m_keycode[0]; + keystroke.m_press = true; keys.push_back(keystroke); - keystroke.m_press = False; + keystroke.m_press = false; undo.push_back(keystroke); } else { @@ -1144,11 +837,11 @@ CXWindowsSecondaryScreen::adjustModifier(Keystrokes& keys, const KeyCodes& keycodes = m_modifierKeycodes[index->second]; for (KeyCodes::const_iterator j = keycodes.begin(); j != keycodes.end(); ++j) { - if (m_keys[*j]) { - keystroke.m_keycode = *j; - keystroke.m_press = False; + if (isKeyDown(*j)) { + keystroke.m_sysKeyID = *j; + keystroke.m_press = false; keys.push_back(keystroke); - keystroke.m_press = True; + keystroke.m_press = true; undo.push_back(keystroke); } } @@ -1159,97 +852,64 @@ CXWindowsSecondaryScreen::adjustModifier(Keystrokes& keys, } void -CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) +CXWindowsSecondaryScreen::fakeKeyEvent(SysKeyID keycode, bool press) const { - // do nothing if no keys or no repeats - if (count < 1 || keys.empty()) { - return; - } - - // lock display CDisplayLock display(m_screen); - - // generate key events - for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { - if (k->m_repeat) { - // repeat from here up to but not including the next key - // with m_repeat == false count times. - Keystrokes::const_iterator start = k; - for (; count > 0; --count) { - // send repeating events - for (k = start; k != keys.end() && k->m_repeat; ++k) { - XTestFakeKeyEvent(display, - k->m_keycode, k->m_press, CurrentTime); - } - } - - // note -- k is now on the first non-repeat key after the - // repeat keys, exactly where we'd like to continue from. - } - else { - // send event - LOG((CLOG_DEBUG2 "keystrokes:")); - LOG((CLOG_DEBUG2 " %d %s", k->m_keycode, k->m_press ? "down" : "up")); - XTestFakeKeyEvent(display, k->m_keycode, k->m_press, CurrentTime); - - // next key - ++k; - } + if (display != NULL) { + XTestFakeKeyEvent(display, keycode, press ? True : False, CurrentTime); } - - // update - flush(display); -} - -CXWindowsSecondaryScreen::ModifierMask -CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const -{ - ModifierMask outMask = 0; - if (inMask & KeyModifierShift) { - outMask |= m_shiftMask; - } - if (inMask & KeyModifierControl) { - outMask |= m_ctrlMask; - } - if (inMask & KeyModifierAlt) { - outMask |= m_altMask; - } - if (inMask & KeyModifierMeta) { - outMask |= m_metaMask; - } - if (inMask & KeyModifierSuper) { - outMask |= m_superMask; - } - if (inMask & KeyModifierModeSwitch) { - outMask |= m_modeSwitchMask; - } - if (inMask & KeyModifierCapsLock) { - outMask |= m_capsLockMask; - } - if (inMask & KeyModifierNumLock) { - outMask |= m_numLockMask; - } - if (inMask & KeyModifierScrollLock) { - outMask |= m_scrollLockMask; - } - return outMask; } void -CXWindowsSecondaryScreen::doReleaseKeys(Display* display) +CXWindowsSecondaryScreen::fakeMouseButton(ButtonID button, bool press) const { - assert(display != NULL); - - // key release for each key that we faked a press for - for (UInt32 i = 0; i < 256; ++i) { - if (m_fakeKeys[i]) { - XTestFakeKeyEvent(display, i, False, CurrentTime); - m_fakeKeys[i] = false; - m_keys[i] = false; + const unsigned int xButton = mapButton(button); + if (xButton != 0) { + CDisplayLock display(m_screen); + if (display != NULL) { + XTestFakeButtonEvent(display, xButton, + press ? True : False, CurrentTime); } } } +void +CXWindowsSecondaryScreen::fakeMouseMove(SInt32 x, SInt32 y) const +{ + CDisplayLock display(m_screen); + if (m_xinerama && m_xtestIsXineramaUnaware) { + XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); + } + else { + Display* pDisplay = display; + XTestFakeMotionEvent(display, DefaultScreen(pDisplay), + x, y, CurrentTime); + } +} + +void +CXWindowsSecondaryScreen::fakeMouseWheel(SInt32 delta) const +{ + // choose button depending on rotation direction + const unsigned int xButton = mapButton(static_cast( + (delta >= 0) ? -1 : -2)); + if (xButton == 0) { + return; + } + + // now use absolute value of delta + if (delta < 0) { + delta = -delta; + } + + // send as many clicks as necessary + CDisplayLock display(m_screen); + for (; delta >= 120; delta -= 120) { + XTestFakeButtonEvent(display, xButton, True, CurrentTime); + XTestFakeButtonEvent(display, xButton, False, CurrentTime); + } +} + void CXWindowsSecondaryScreen::doUpdateKeys(Display* display) { @@ -1281,48 +941,35 @@ CXWindowsSecondaryScreen::doUpdateKeys(Display* display) // clean up delete[] tmpButtons; - // update mappings and current modifiers + // update mappings updateKeysymMap(display); - updateModifiers(display); } void -CXWindowsSecondaryScreen::updateKeys() +CXWindowsSecondaryScreen::updateKeys(KeyState* keys) { CDisplayLock display(m_screen); // ask server which keys are pressed - char keys[32]; - XQueryKeymap(display, keys); + char xkeys[32]; + XQueryKeymap(display, xkeys); // transfer to our state for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) { - m_keys[j + 0] = ((keys[i] & 0x01) != 0); - m_keys[j + 1] = ((keys[i] & 0x02) != 0); - m_keys[j + 2] = ((keys[i] & 0x04) != 0); - m_keys[j + 3] = ((keys[i] & 0x08) != 0); - m_keys[j + 4] = ((keys[i] & 0x10) != 0); - m_keys[j + 5] = ((keys[i] & 0x20) != 0); - m_keys[j + 6] = ((keys[i] & 0x40) != 0); - m_keys[j + 7] = ((keys[i] & 0x80) != 0); + keys[j + 0] = ((xkeys[i] & 0x01) != 0) ? kDown : 0; + keys[j + 1] = ((xkeys[i] & 0x02) != 0) ? kDown : 0; + keys[j + 2] = ((xkeys[i] & 0x04) != 0) ? kDown : 0; + keys[j + 3] = ((xkeys[i] & 0x08) != 0) ? kDown : 0; + keys[j + 4] = ((xkeys[i] & 0x10) != 0) ? kDown : 0; + keys[j + 5] = ((xkeys[i] & 0x20) != 0) ? kDown : 0; + keys[j + 6] = ((xkeys[i] & 0x40) != 0) ? kDown : 0; + keys[j + 7] = ((xkeys[i] & 0x80) != 0) ? kDown : 0; } - // we've fake pressed no keys - m_fakeKeys.reset(); - // update mappings and current modifiers and mouse buttons doUpdateKeys(display); } -void -CXWindowsSecondaryScreen::releaseKeys() -{ - CDisplayLock display(m_screen); - if (display != NULL) { - doReleaseKeys(display); - } -} - void CXWindowsSecondaryScreen::updateKeysymMap(Display* display) { @@ -1394,6 +1041,9 @@ CXWindowsSecondaryScreen::updateKeysymMap(Display* display) // start with no keycodes for this modifier m_modifierKeycodes[i].clear(); + // no mask for this modifier + m_modifierIndexToMask[i] = 0; + // add each keycode for modifier for (unsigned int j = 0; j < keysPerModifier; ++j) { // get keycode and ignore unset keycodes @@ -1417,11 +1067,14 @@ CXWindowsSecondaryScreen::updateKeysymMap(Display* display) continue; } + // save modifier mask + m_modifierIndexToMask[i] = mapToModifierMask(i, keysym); + // fill in keysym info mapping.m_keycode[0] = keycode; mapping.m_shiftSensitive[0] = usesShift[keycodeIndex]; mapping.m_modeSwitchSensitive[0] = usesModeSwitch[keycodeIndex]; - mapping.m_modifierMask = (1 << i); + mapping.m_modifierMask = m_modifierIndexToMask[i]; mapping.m_capsLockSensitive = false; mapping.m_numLockSensitive = false; } @@ -1543,25 +1196,16 @@ CXWindowsSecondaryScreen::updateKeysymMap(Display* display) } } - // cache the bits for the modifier - m_shiftMask = getModifierMask(m_shiftKeysym); - m_ctrlMask = getModifierMask(m_ctrlKeysym); - m_altMask = getModifierMask(m_altKeysym); - m_metaMask = getModifierMask(m_metaKeysym); - m_superMask = getModifierMask(m_superKeysym); - m_capsLockMask = getModifierMask(m_capsLockKeysym); - m_numLockMask = getModifierMask(m_numLockKeysym); - m_modeSwitchMask = getModifierMask(m_modeSwitchKeysym); - m_scrollLockMask = getModifierMask(m_scrollLockKeysym); - // clean up XFree(keysyms); XFreeModifiermap(modifiers); } -void -CXWindowsSecondaryScreen::updateModifiers(Display* display) +KeyModifierMask +CXWindowsSecondaryScreen::getModifiers() const { + CDisplayLock display(m_screen); + // query the pointer to get the keyboard state Window root, window; int xRoot, yRoot, xWindow, yWindow; @@ -1572,50 +1216,56 @@ CXWindowsSecondaryScreen::updateModifiers(Display* display) } // update active modifier mask - m_mask = 0; + KeyModifierMask mask = 0; for (ModifierIndex i = 0; i < 8; ++i) { - const ModifierMask bit = (1 << i); + const KeyModifierMask bit = m_modifierIndexToMask[i]; if ((bit & m_toggleModifierMask) == 0) { for (KeyCodes::const_iterator j = m_modifierKeycodes[i].begin(); j != m_modifierKeycodes[i].end(); ++j) { - if (m_keys[*j]) { - m_mask |= bit; +// XXX -- is this right? + if (isKeyDown(*j)) { + mask |= bit; break; } } } else if ((bit & state) != 0) { // toggle is on - m_mask |= bit; + mask |= bit; } } + + return mask; } -void -CXWindowsSecondaryScreen::toggleKey(Display* display, - KeySym keysym, ModifierMask mask) +CSecondaryScreen::SysKeyID +CXWindowsSecondaryScreen::getToggleSysKey(KeyID keyID) const { + // convert KeyID to KeySym + KeySym keysym; + switch (keyID) { + case kKeyNumLock: + keysym = m_numLockKeysym; + break; + + case kKeyCapsLock: + keysym = m_capsLockKeysym; + break; + + case kKeyScrollLock: + keysym = m_scrollLockKeysym; + break; + + default: + return 0; + } + // lookup the key mapping KeySymIndex index = m_keysymMap.find(keysym); if (index == m_keysymMap.end()) { - return; + return 0; } - KeyCode keycode = index->second.m_keycode[0]; - - // toggle the key - if ((keysym == m_capsLockKeysym && m_capsLockHalfDuplex) || - (keysym == m_numLockKeysym && m_numLockHalfDuplex)) { - // "half-duplex" toggle - XTestFakeKeyEvent(display, keycode, (m_mask & mask) == 0, CurrentTime); - } - else { - // normal toggle - XTestFakeKeyEvent(display, keycode, True, CurrentTime); - XTestFakeKeyEvent(display, keycode, False, CurrentTime); - } - - // toggle shadow state - m_mask ^= mask; + return index->second.m_keycode[0]; } bool @@ -1633,6 +1283,62 @@ CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) } } +KeyModifierMask +CXWindowsSecondaryScreen::mapToModifierMask( + ModifierIndex i, KeySym keysym) const +{ + // some modifier indices (0,1,2) are dedicated to particular uses, + // the rest depend on the keysyms bound. + switch (i) { + case 0: + return KeyModifierShift; + + case 1: + return KeyModifierCapsLock; + + case 2: + return KeyModifierControl; + + default: + switch (keysym) { + case XK_Shift_L: + case XK_Shift_R: + return KeyModifierShift; + + case XK_Control_L: + case XK_Control_R: + return KeyModifierControl; + + case XK_Alt_L: + case XK_Alt_R: + return KeyModifierAlt; + + case XK_Meta_L: + case XK_Meta_R: + return KeyModifierMeta; + + case XK_Super_L: + case XK_Super_R: + return KeyModifierSuper; + + case XK_Mode_switch: + return KeyModifierModeSwitch; + + case XK_Caps_Lock: + return KeyModifierCapsLock; + + case XK_Num_Lock: + return KeyModifierNumLock; + + case XK_Scroll_Lock: + return KeyModifierScrollLock; + + default: + return 0; + } + } +} + // map special KeyID keys to KeySyms #if defined(HAVE_X11_XF86KEYSYM_H) static const KeySym g_mapE000[] = @@ -1681,7 +1387,7 @@ static const KeySym g_mapE000[] = #endif KeySym -CXWindowsSecondaryScreen::keyIDToKeySym(KeyID id, ModifierMask mask) const +CXWindowsSecondaryScreen::keyIDToKeySym(KeyID id, KeyModifierMask mask) const { // convert id to keysym KeySym keysym = NoSymbol; @@ -1725,7 +1431,7 @@ CXWindowsSecondaryScreen::keyIDToKeySym(KeyID id, ModifierMask mask) const // instead. if that doesn't work, we'll fall back to XK_Tab with // shift active. this is to handle primary screens that don't map // XK_ISO_Left_Tab sending events to secondary screens that do. - if (keysym == XK_Tab && (mask & ShiftMask) != 0) { + if (keysym == XK_Tab && (mask & KeyModifierShift) != 0) { keysym = XK_ISO_Left_Tab; } @@ -2002,6 +1708,7 @@ CXWindowsSecondaryScreen::getDecomposedKeySymTable() XK_umacron, XK_dead_macron, XK_u, 0, // Latin-8 (ISO 8859-14) +#if defined(XK_Babovedot) XK_Babovedot, XK_dead_abovedot, XK_B, 0, XK_babovedot, XK_dead_abovedot, XK_b, 0, XK_Dabovedot, XK_dead_abovedot, XK_D, 0, @@ -2028,9 +1735,12 @@ CXWindowsSecondaryScreen::getDecomposedKeySymTable() XK_wcircumflex, XK_dead_circumflex, XK_w, 0, XK_tabovedot, XK_dead_abovedot, XK_t, 0, XK_ycircumflex, XK_dead_circumflex, XK_y, 0, +#endif // Latin-9 (ISO 8859-15) +#if defined(XK_Ydiaeresis) XK_Ydiaeresis, XK_dead_diaeresis, XK_Y, 0, +#endif // end of table 0 diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h index c2de53ee..2f22bcf7 100644 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ b/lib/platform/CXWindowsSecondaryScreen.h @@ -17,7 +17,6 @@ #include "CSecondaryScreen.h" #include "IScreenEventHandler.h" -#include "stdbitset.h" #include "stdmap.h" #include "stdvector.h" #if defined(X_DISPLAY_MISSING) @@ -37,14 +36,6 @@ public: virtual ~CXWindowsSecondaryScreen(); // CSecondaryScreen overrides - virtual void keyDown(KeyID, KeyModifierMask, KeyButton); - virtual void keyRepeat(KeyID, KeyModifierMask, - SInt32 count, KeyButton); - virtual void keyUp(KeyID, KeyModifierMask, KeyButton); - virtual void mouseDown(ButtonID); - virtual void mouseUp(ButtonID); - virtual void mouseMove(SInt32 x, SInt32 y); - virtual void mouseWheel(SInt32 delta); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); virtual IScreen* getScreen() const; @@ -54,7 +45,6 @@ public: virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); virtual void onOneShotTimerExpired(UInt32 id); - virtual SInt32 getJumpZoneSize() const; protected: // CSecondaryScreen overrides @@ -69,22 +59,24 @@ protected: virtual void destroyWindow(); virtual void showWindow(SInt32 x, SInt32 y); virtual void hideWindow(); - virtual void warpCursor(SInt32 x, SInt32 y); - virtual void updateKeys(); - virtual void releaseKeys(); - virtual void setToggleState(KeyModifierMask); - virtual KeyModifierMask getToggleState() const; + virtual void updateKeys(KeyState* sysKeyStates); + virtual KeyModifierMask getModifiers() const; + + virtual bool isAutoRepeating(SysKeyID) const; + virtual KeyModifierMask getModifierKeyMask(SysKeyID) const; + virtual bool isModifierActive(SysKeyID) const; + virtual SysKeyID getToggleSysKey(KeyID keyID) const; + virtual void flush(); + virtual KeyModifierMask + mapKey(Keystrokes&, SysKeyID& sysKeyID, KeyID, + KeyModifierMask, KeyModifierMask, EKeyAction) const; + virtual void fakeKeyEvent(SysKeyID, bool press) const; + virtual void fakeMouseButton(ButtonID, bool press) const; + virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseWheel(SInt32 delta) const; private: - enum EKeyAction { kPress, kRelease, kRepeat }; typedef unsigned int ModifierIndex; - typedef unsigned int ModifierMask; - class Keystroke { - public: - KeyCode m_keycode; - Bool m_press; - bool m_repeat; - }; class KeyMapping { public: KeyMapping(); @@ -97,7 +89,7 @@ private: bool m_modeSwitchSensitive[4]; // the modifier mask of keysym or 0 if not a modifier - ModifierMask m_modifierMask; + KeyModifierMask m_modifierMask; // whether keysym is sensitive to caps and num lock bool m_numLockSensitive; @@ -108,50 +100,40 @@ private: typedef std::map KeyCodeToModifierMap; typedef std::map KeySymMap; typedef KeySymMap::const_iterator KeySymIndex; - typedef std::vector Keystrokes; typedef std::vector KeySyms; typedef std::map KeySymsMap; - typedef std::map ServerKeyMap; - - void flush(Display*) const; unsigned int mapButton(ButtonID button) const; - ModifierMask mapKey(Keystrokes&, KeyCode&, KeyID, - KeyModifierMask, EKeyAction) const; - ModifierMask mapKeyRelease(Keystrokes&, KeyCode) const; bool mapToKeystrokes(Keystrokes& keys, - KeyCode& keycode, - ModifierMask& finalMask, + SysKeyID& keycode, + KeyModifierMask& finalMask, KeySymIndex keyIndex, - ModifierMask currentMask, - EKeyAction action) const; + KeyModifierMask currentMask, + EKeyAction action, + bool isHalfDuplex) const; bool adjustModifiers(Keystrokes& keys, Keystrokes& undo, - ModifierMask& inOutMask, - ModifierMask desiredMask) const; + KeyModifierMask& inOutMask, + KeyModifierMask desiredMask) const; bool adjustModifier(Keystrokes& keys, Keystrokes& undo, KeySym keysym, bool desireActive) const; - void doKeystrokes(const Keystrokes&, SInt32 count); - ModifierMask maskToX(KeyModifierMask) const; + KeyModifierMask mapToModifierMask(ModifierIndex, KeySym) const; unsigned int findBestKeyIndex(KeySymIndex keyIndex, - ModifierMask currentMask) const; + KeyModifierMask currentMask) const; bool isShiftInverted(KeySymIndex keyIndex, - ModifierMask currentMask) const; - ModifierMask getModifierMask(KeySym) const; + KeyModifierMask currentMask) const; void doUpdateKeys(Display*); - void doReleaseKeys(Display*); void updateKeysymMap(Display* display); void updateModifiers(Display* display); ModifierIndex keySymToModifierIndex(KeySym) const; - void toggleKey(Display*, KeySym, ModifierMask mask); static bool isToggleKeysym(KeySym); - KeySym keyIDToKeySym(KeyID id, ModifierMask mask) const; + KeySym keyIDToKeySym(KeyID id, KeyModifierMask mask) const; bool adjustForNumLock(KeySym) const; bool adjustForCapsLock(KeySym) const; @@ -163,31 +145,15 @@ private: CXWindowsScreen* m_screen; Window m_window; - // note toggle keys that toggles on up/down (false) or on - // transition (true) - bool m_numLockHalfDuplex; - bool m_capsLockHalfDuplex; - - // set entries indicate keys that are pressed (by us or by the user). - // indexed by keycode. - std::bitset<256> m_keys; - - // set entries indicate keys that are synthetically pressed by us. - // this is normally the same as m_keys. - std::bitset<256> m_fakeKeys; - // logical to physical button mapping. m_buttons[i] gives the // physical button for logical button i+1. std::vector m_buttons; - // current active modifiers (X key masks) - ModifierMask m_mask; - // the modifiers that have keys bound to them - ModifierMask m_modifierMask; + KeyModifierMask m_modifierMask; // set bits indicate modifiers that toggle (e.g. caps-lock) - ModifierMask m_toggleModifierMask; + KeyModifierMask m_toggleModifierMask; // keysym to keycode mapping KeySymMap m_keysymMap; @@ -195,6 +161,9 @@ private: // modifier index to keycodes KeyCodes m_modifierKeycodes[8]; + // modifier index to modifier mask + KeyModifierMask m_modifierIndexToMask[8]; + // keycode to modifier index KeyCodeToModifierMap m_keycodeToModifier; @@ -209,20 +178,6 @@ private: KeySym m_capsLockKeysym; KeySym m_scrollLockKeysym; - // modifier masks - ModifierMask m_shiftMask; - ModifierMask m_ctrlMask; - ModifierMask m_altMask; - ModifierMask m_metaMask; - ModifierMask m_superMask; - ModifierMask m_modeSwitchMask; - ModifierMask m_numLockMask; - ModifierMask m_capsLockMask; - ModifierMask m_scrollLockMask; - - // map server key buttons to local keycodes - ServerKeyMap m_serverKeyMap; - // the keyboard control state the last time this screen was entered XKeyboardState m_keyControl; diff --git a/lib/platform/IMSWindowsScreenEventHandler.h b/lib/platform/IMSWindowsScreenEventHandler.h index ee24ea8e..164f41a8 100644 --- a/lib/platform/IMSWindowsScreenEventHandler.h +++ b/lib/platform/IMSWindowsScreenEventHandler.h @@ -50,7 +50,6 @@ public: virtual void onScreensaver(bool activated) = 0; virtual bool onPreDispatch(const CEvent* event) = 0; virtual bool onEvent(CEvent* event) = 0; - virtual SInt32 getJumpZoneSize() const = 0; }; #endif diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp index 3bf40d46..6dfaed19 100644 --- a/lib/synergy/CSecondaryScreen.cpp +++ b/lib/synergy/CSecondaryScreen.cpp @@ -116,7 +116,10 @@ CSecondaryScreen::remoteControl() } // update keyboard state - updateKeys(); + { + CLock lock(&m_mutex); + updateKeys(); + } // now remote ready. fake being active for call to leave(). bool screenSaverSync; @@ -156,7 +159,7 @@ CSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) LOG((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); - getScreen()->syncDesktop(); + sync(); // now active m_active = true; @@ -167,14 +170,13 @@ CSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) // update our keyboard state to reflect the local state updateKeys(); - // remember toggle key state - m_toggleKeys = getToggleState(); - - // toggle modifiers that don't match the desired state + // toggle modifiers that don't match the desired state and + // remember previous toggle key state. + m_toggleKeys = m_mask; setToggleState(mask); // warp to requested location - warpCursor(x, y); + fakeMouseMove(x, y); // show mouse hideWindow(); @@ -190,7 +192,7 @@ CSecondaryScreen::leave() CLock lock(&m_mutex); assert(m_active == true); - getScreen()->syncDesktop(); + sync(); // subclass hook onPreLeave(); @@ -198,7 +200,7 @@ CSecondaryScreen::leave() // restore toggle key state setToggleState(m_toggleKeys); - // warp and hide mouse + // hide mouse SInt32 x, y; getScreen()->getCursorCenter(x, y); showWindow(x, y); @@ -242,16 +244,314 @@ CSecondaryScreen::screensaver(bool activate) } } +CSecondaryScreen::SysKeyID +CSecondaryScreen::getUnhanded(SysKeyID) const +{ + // no key represents both left and right sides of any key + return 0; +} + +CSecondaryScreen::SysKeyID +CSecondaryScreen::getOtherHanded(SysKeyID) const +{ + // no key represents both left and right sides of any key + return 0; +} + +bool +CSecondaryScreen::synthesizeCtrlAltDel(EKeyAction) +{ + // pass keys through unchanged + return false; +} + +void +CSecondaryScreen::sync() const +{ + // do nothing +} + +void +CSecondaryScreen::flush() +{ + // do nothing +} + +void +CSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) +{ + // do nothing if no keys or no repeats + if (count < 1 || keys.empty()) { + return; + } + + // generate key events + LOG((CLOG_DEBUG2 "keystrokes:")); + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { + if (k->m_repeat) { + // repeat from here up to but not including the next key + // with m_repeat == false count times. + Keystrokes::const_iterator start = k; + for (; count > 0; --count) { + // send repeating events + for (k = start; k != keys.end() && k->m_repeat; ++k) { + LOG((CLOG_DEBUG2 " %d %s repeat", k->m_sysKeyID, k->m_press ? "down" : "up")); + fakeKeyEvent(k->m_sysKeyID, k->m_press); + } + } + + // note -- k is now on the first non-repeat key after the + // repeat keys, exactly where we'd like to continue from. + } + else { + // send event + LOG((CLOG_DEBUG2 " %d %s", k->m_sysKeyID, k->m_press ? "down" : "up")); + fakeKeyEvent(k->m_sysKeyID, k->m_press); + + // next key + ++k; + } + } + + flush(); +} + +void +CSecondaryScreen::keyDown(KeyID key, + KeyModifierMask mask, KeyButton button) +{ + CLock lock(&m_mutex); + sync(); + + // check for ctrl+alt+del emulation + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + LOG((CLOG_DEBUG "emulating ctrl+alt+del press")); + if (synthesizeCtrlAltDel(kPress)) { + return; + } + } + + // get the sequence of keys to simulate key press and the final + // modifier state. + Keystrokes keys; + SysKeyID sysKeyID; + m_mask = mapKey(keys, sysKeyID, key, m_mask, mask, kPress); + if (keys.empty()) { + // do nothing if there are no associated keys (i.e. lookup failed) + return; + } + sysKeyID &= 0xffu; + + // generate key events + doKeystrokes(keys, 1); + + // do not record button down if button or system key is 0 (invalid) + if (button != 0 && sysKeyID != 0) { + // note that key is now down + SysKeyID unhandedSysKeyID = getUnhanded(sysKeyID); + m_serverKeyMap[button] = sysKeyID; + m_keys[sysKeyID] |= kDown; + m_fakeKeys[sysKeyID] |= kDown; + if (unhandedSysKeyID != 0) { + m_keys[unhandedSysKeyID] |= kDown; + m_fakeKeys[unhandedSysKeyID] |= kDown; + } + } +} + + +void +CSecondaryScreen::keyRepeat(KeyID key, + KeyModifierMask mask, SInt32 count, KeyButton button) +{ + CLock lock(&m_mutex); + sync(); + + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + + // get the sequence of keys to simulate key repeat and the final + // modifier state. + Keystrokes keys; + SysKeyID sysKeyID; + m_mask = mapKey(keys, sysKeyID, key, m_mask, mask, kRepeat); + if (keys.empty()) { + return; + } + sysKeyID &= 0xffu; + + // if this key shouldn't auto-repeat then ignore + if (!isAutoRepeating(sysKeyID)) { + return; + } + + // if the keycode for the auto-repeat is not the same as for the + // initial press then mark the initial key as released and the new + // key as pressed. this can happen when we auto-repeat after a + // dead key. for example, a dead accent followed by 'a' will + // generate an 'a with accent' followed by a repeating 'a'. the + // keycodes for the two keysyms might be different. + if (sysKeyID != index->second) { + // replace key up with previous key id but leave key down + // alone so it uses the new keycode and store that keycode + // in the server key map. + for (Keystrokes::iterator index2 = keys.begin(); + index2 != keys.end(); ++index2) { + if ((index2->m_sysKeyID & 0xffu) == sysKeyID) { + index2->m_sysKeyID = index->second; + break; + } + } + + // note that old key is now up + m_keys[index->second] &= ~kDown; + m_fakeKeys[index->second] &= ~kDown; + + // map server key to new key + index->second = sysKeyID; + + // note that new key is now down + m_keys[index->second] |= kDown; + m_fakeKeys[index->second] |= kDown; + } + + // generate key events + doKeystrokes(keys, count); +} + +void +CSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) +{ + CLock lock(&m_mutex); + sync(); + + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + SysKeyID sysKeyID = index->second; + + // check for ctrl+alt+del emulation + if (key == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + LOG((CLOG_DEBUG "emulating ctrl+alt+del release")); + if (synthesizeCtrlAltDel(kRelease)) { + return; + } + } + + // get the sequence of keys to simulate key release + Keystrokes keys; + Keystroke keystroke; + keystroke.m_sysKeyID = sysKeyID; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + + // generate key events + doKeystrokes(keys, 1); + + // note that key is now up + SysKeyID unhandedSysKeyID = getUnhanded(sysKeyID); + m_serverKeyMap.erase(index); + m_keys[sysKeyID] &= ~kDown; + m_fakeKeys[sysKeyID] &= ~kDown; + if (unhandedSysKeyID != 0) { + SysKeyID otherHandedSysKeyID = getOtherHanded(sysKeyID); + if ((m_keys[otherHandedSysKeyID] & kDown) == 0) { + m_keys[unhandedSysKeyID] &= ~kDown; + m_fakeKeys[unhandedSysKeyID] &= ~kDown; + } + } + + // get the new modifier state + mask = getModifierKeyMask(sysKeyID); + if (mask != 0) { + // key is a modifier key + if ((mask & (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock)) != 0) { + // modifier is a toggle + m_mask ^= mask; + } + else if (!isModifierActive(sysKeyID)) { + // all keys for this modifier are released + m_mask &= ~mask; + } + } +} + +void +CSecondaryScreen::mouseDown(ButtonID button) +{ + CLock lock(&m_mutex); + sync(); + fakeMouseButton(button, true); + flush(); +} + +void +CSecondaryScreen::mouseUp(ButtonID button) +{ + CLock lock(&m_mutex); + sync(); + fakeMouseButton(button, false); + flush(); +} + +void +CSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +{ + CLock lock(&m_mutex); + sync(); + fakeMouseMove(x, y); + flush(); +} + +void +CSecondaryScreen::mouseWheel(SInt32 delta) +{ + CLock lock(&m_mutex); + sync(); + fakeMouseWheel(delta); + flush(); +} + +void +CSecondaryScreen::setToggleState(KeyModifierMask mask) +{ + // toggle modifiers that don't match the desired state + KeyModifierMask different = (m_mask ^ mask); + if ((different & KeyModifierCapsLock) != 0) { + toggleKey(kKeyCapsLock, KeyModifierCapsLock); + } + if ((different & KeyModifierNumLock) != 0) { + toggleKey(kKeyNumLock, KeyModifierNumLock); + } + if ((different & KeyModifierScrollLock) != 0) { + toggleKey(kKeyScrollLock, KeyModifierScrollLock); + } +} + void CSecondaryScreen::resetOptions() { // set screen saver synchronization flag and see if we need to - // update the screen saver synchronization. + // update the screen saver synchronization. reset other options. bool screenSaverSyncOn; { CLock lock(&m_mutex); - screenSaverSyncOn = (!m_screenSaverSync && m_remoteReady); - m_screenSaverSync = true; + screenSaverSyncOn = (!m_screenSaverSync && m_remoteReady); + m_screenSaverSync = true; + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; } // update screen saver synchronization @@ -275,6 +575,14 @@ CSecondaryScreen::setOptions(const COptionsList& options) m_screenSaverSync = (options[i + 1] != 0); LOG((CLOG_DEBUG1 "screen saver synchronization %s", m_screenSaverSync ? "on" : "off")); } + else if (options[i] == kOptionHalfDuplexCapsLock) { + m_capsLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); + } + else if (options[i] == kOptionHalfDuplexNumLock) { + m_numLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); + } } if (!m_remoteReady || oldScreenSaverSync == m_screenSaverSync) { updateScreenSaverSync = false; @@ -306,17 +614,23 @@ CSecondaryScreen::getClipboard(ClipboardID id, getScreen()->getClipboard(id, clipboard); } +SInt32 +CSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + void CSecondaryScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { - getScreen()->syncDesktop(); + sync(); getScreen()->getShape(x, y, w, h); } void CSecondaryScreen::getCursorPos(SInt32& x, SInt32& y) const { - getScreen()->syncDesktop(); + sync(); getScreen()->getCursorPos(x, y); } @@ -379,3 +693,89 @@ CSecondaryScreen::onPostLeave() { // do nothing } + +void +CSecondaryScreen::updateKeys() +{ + sync(); + + // clear key state + memset(m_keys, 0, sizeof(m_keys)); + memset(m_fakeKeys, 0, sizeof(m_fakeKeys)); + + // let subclass set m_keys + updateKeys(m_keys); + + // get m_mask from subclass + m_mask = getModifiers(); + LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); +} + +void +CSecondaryScreen::releaseKeys() +{ + CLock lock(&m_mutex); + sync(); + + // release keys that we've synthesized a press for and only those + // keys. we don't want to synthesize a release on a key the user + // is still physically pressing. + for (UInt32 i = 1; i < 256; ++i) { + if ((m_fakeKeys[i] & kDown) != 0) { + fakeKeyEvent(i, false); + m_keys[i] &= ~kDown; + m_fakeKeys[i] &= ~kDown; + } + } + + flush(); +} + +void +CSecondaryScreen::toggleKey(KeyID keyID, KeyModifierMask mask) +{ + // get the system key ID for this toggle key ID + SysKeyID sysKeyID = getToggleSysKey(keyID); + if (sysKeyID == 0) { + return; + } + + // toggle the key + if (isKeyHalfDuplex(keyID)) { + // "half-duplex" toggle + fakeKeyEvent(sysKeyID, (m_mask & mask) == 0); + } + else { + // normal toggle + fakeKeyEvent(sysKeyID, true); + fakeKeyEvent(sysKeyID, false); + } + flush(); + + // toggle shadow state + m_mask ^= mask; + sysKeyID &= 0xffu; + m_keys[sysKeyID] ^= kToggled; + m_fakeKeys[sysKeyID] ^= kToggled; +} + +bool +CSecondaryScreen::isKeyDown(SysKeyID sysKeyID) const +{ + sysKeyID &= 0xffu; + return (sysKeyID != 0 && ((m_keys[sysKeyID] & kDown) != 0)); +} + +bool +CSecondaryScreen::isKeyToggled(SysKeyID sysKeyID) const +{ + sysKeyID &= 0xffu; + return (sysKeyID != 0 && ((m_keys[sysKeyID] & kToggled) != 0)); +} + +bool +CSecondaryScreen::isKeyHalfDuplex(KeyID keyID) const +{ + return ((keyID == kKeyCapsLock && m_capsLockHalfDuplex) || + (keyID == kKeyNumLock && m_numLockHalfDuplex)); +} diff --git a/lib/synergy/CSecondaryScreen.h b/lib/synergy/CSecondaryScreen.h index f1790a83..c69692b9 100644 --- a/lib/synergy/CSecondaryScreen.h +++ b/lib/synergy/CSecondaryScreen.h @@ -20,6 +20,8 @@ #include "MouseTypes.h" #include "OptionTypes.h" #include "CMutex.h" +#include "stdmap.h" +#include "stdvector.h" class IClipboard; class IScreen; @@ -130,41 +132,41 @@ public: synthesize an up or repeat for the same client key synthesized by keyDown(). */ - virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0; + void keyDown(KeyID id, KeyModifierMask, KeyButton); //! Notify of key repeat /*! Synthesize key events to generate a press and release of key \c id \c count times. If possible match the given modifier mask. */ - virtual void keyRepeat(KeyID id, KeyModifierMask, - SInt32 count, KeyButton) = 0; + void keyRepeat(KeyID id, KeyModifierMask, + SInt32 count, KeyButton); //! Notify of key release /*! Synthesize key events to generate a release of key \c id. If possible match the given modifier mask. */ - virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0; + void keyUp(KeyID id, KeyModifierMask, KeyButton); //! Notify of mouse press /*! Synthesize mouse events to generate a press of mouse button \c id. */ - virtual void mouseDown(ButtonID id) = 0; + void mouseDown(ButtonID id); //! Notify of mouse release /*! Synthesize mouse events to generate a release of mouse button \c id. */ - virtual void mouseUp(ButtonID id) = 0; + void mouseUp(ButtonID id); //! Notify of mouse motion /*! Synthesize mouse events to generate mouse motion to the absolute screen position \c xAbs,yAbs. */ - virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + void mouseMove(SInt32 xAbs, SInt32 yAbs); //! Notify of mouse wheel motion /*! @@ -173,7 +175,7 @@ public: motion towards the user. Each wheel click should generate a delta of +/-120. */ - virtual void mouseWheel(SInt32 delta) = 0; + void mouseWheel(SInt32 delta); //! Notify of options changes /*! @@ -212,7 +214,7 @@ public: Return the jump zone size, the size of the regions on the edges of the screen that cause the cursor to jump to another screen. */ - virtual SInt32 getJumpZoneSize() const = 0; + SInt32 getJumpZoneSize() const; //! Get screen shape /*! @@ -237,6 +239,26 @@ public: //@} protected: + typedef UInt8 KeyState; + typedef UInt32 SysKeyID; + enum EKeyState { kDown = 0x01, kToggled = 0x80 }; + enum EKeyAction { kPress, kRelease, kRepeat }; + class Keystroke { + public: + SysKeyID m_sysKeyID; + bool m_press; + bool m_repeat; + }; + typedef std::vector Keystrokes; + typedef std::map ServerKeyMap; + + void updateKeys(); + void releaseKeys(); + void doKeystrokes(const Keystrokes&, SInt32 count); + bool isKeyDown(SysKeyID) const; + bool isKeyToggled(SysKeyID) const; + bool isKeyHalfDuplex(KeyID) const; + //! Pre-mainLoop() hook /*! Called on entry to mainLoop(). Override to perform platform specific @@ -338,38 +360,54 @@ protected: */ virtual void hideWindow() = 0; + //! Synchronize key state + /*! + Save the current keyboard state. Normally a screen will save + the keyboard state in this method and use this shadow state, + available through isKeyDown() and getKeyState(), when + synthesizing events. + */ + virtual void updateKeys(KeyState* sysKeyStates) = 0; + + //! Get modifier key state + /*! + Return the current keyboard modifier state. + */ + virtual KeyModifierMask getModifiers() const = 0; + + //! Synchronize toggle key state + /*! + Toggles modifiers that don't match the given state so that they do. + */ + void setToggleState(KeyModifierMask); + + virtual SysKeyID getUnhanded(SysKeyID) const; + virtual SysKeyID getOtherHanded(SysKeyID) const; + virtual bool isAutoRepeating(SysKeyID) const = 0; + virtual KeyModifierMask getModifierKeyMask(SysKeyID) const = 0; + virtual bool isModifierActive(SysKeyID) const = 0; + virtual SysKeyID getToggleSysKey(KeyID keyID) const = 0; + virtual bool synthesizeCtrlAltDel(EKeyAction); + virtual void sync() const; + virtual void flush(); + + virtual KeyModifierMask + mapKey(Keystrokes&, SysKeyID& sysKeyID, KeyID, + KeyModifierMask currentMask, + KeyModifierMask desiredMask, EKeyAction) const = 0; + virtual void fakeKeyEvent(SysKeyID, bool press) const = 0; + virtual void fakeMouseButton(ButtonID, bool press) const = 0; + //! Warp cursor /*! Warp the cursor to the absolute coordinates \c x,y. */ - virtual void warpCursor(SInt32 x, SInt32 y) = 0; + virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; - //! Synchronize key state - /*! - Check the current keyboard state. Normally a screen will save - the keyboard state in this method and use this shadow state - when synthesizing events. - */ - virtual void updateKeys() = 0; + virtual void fakeMouseWheel(SInt32 delta) const = 0; - //! Release keys - /*! - Synthesizes key release event for any key that our key state - says is down. - */ - virtual void releaseKeys() = 0; - - //! Synchronize toggle key state - /*! - Toggle modifiers that don't match the given state so that they do. - */ - virtual void setToggleState(KeyModifierMask) = 0; - - //! Get the toggle key state - /*! - Returns the current state of the toggle keys. - */ - virtual KeyModifierMask getToggleState() const = 0; +private: + void toggleKey(KeyID, KeyModifierMask); private: CMutex m_mutex; @@ -380,11 +418,29 @@ private: // m_active is true if this screen has been entered bool m_active; + // true if screen saver should be synchronized to server + bool m_screenSaverSync; + + // map server key buttons to local system keys + ServerKeyMap m_serverKeyMap; + + // system key states as set by us or the user + KeyState m_keys[256]; + + // system key states as set by us + KeyState m_fakeKeys[256]; + + // current active modifiers +// XXX -- subclasses still have and use this + KeyModifierMask m_mask; + // the toggle key state when this screen was last entered KeyModifierMask m_toggleKeys; - // true if screen saver should be synchronized to server - bool m_screenSaverSync; + // note toggle keys that toggles on up/down (false) or on + // transition (true) + bool m_numLockHalfDuplex; + bool m_capsLockHalfDuplex; }; #endif diff --git a/lib/synergy/IScreenEventHandler.h b/lib/synergy/IScreenEventHandler.h index a68858ea..699d295c 100644 --- a/lib/synergy/IScreenEventHandler.h +++ b/lib/synergy/IScreenEventHandler.h @@ -63,16 +63,6 @@ public: */ virtual void onOneShotTimerExpired(UInt32 id) = 0; - //@} - //! @name accessors - //@{ - - //! Get jump zone size - /*! - Called to get the jump zone size. - */ - virtual SInt32 getJumpZoneSize() const = 0; - //@} }; From 345de4cd11427825ad4c246d82d97dd69a03d3c5 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 3 Aug 2003 21:24:45 +0000 Subject: [PATCH 557/807] Integrate fixes from 1.0 branch. --- lib/arch/CArchMultithreadWindows.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index e9101515..2a457a50 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -389,7 +389,7 @@ CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n) assert(thread != NULL); size_t index; - if (s_pBase < n) { + if (n > 0 && s_pBase < n) { // lowest priority index = 0; } @@ -400,7 +400,7 @@ CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n) index = s_pMax; } } - SetPriorityClass(thread->m_thread, s_pClass[index].m_class); + SetPriorityClass(GetCurrentProcess(), s_pClass[index].m_class); SetThreadPriority(thread->m_thread, s_pClass[index].m_level); } @@ -749,13 +749,13 @@ CArchMultithreadWindows::threadFunc(void* vrep) void CArchMultithreadWindows::doThreadFunc(CArchThread thread) { - // default priority is slightly below normal - setPriorityOfThread(thread, 1); - // wait for parent to initialize this object lockMutex(m_threadMutex); unlockMutex(m_threadMutex); + // default priority is slightly below normal + setPriorityOfThread(thread, 1); + void* result = NULL; try { // go From 2f9cdfd1b2dcd72b94e61cda4a7e09ac2591f05a Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 6 Aug 2003 21:09:25 +0000 Subject: [PATCH 558/807] Integrated fixes from 1.0 branch. --- cmd/launcher/CAdvancedOptions.cpp | 8 +++----- cmd/launcher/CAdvancedOptions.h | 2 +- cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp | 2 +- cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp | 2 +- lib/arch/CArchLogWindows.cpp | 4 ++-- lib/platform/CMSWindowsClipboard.cpp | 12 +++++++++++- lib/platform/CMSWindowsClipboard.h | 15 +++++++++++++++ 7 files changed, 34 insertions(+), 11 deletions(-) diff --git a/cmd/launcher/CAdvancedOptions.cpp b/cmd/launcher/CAdvancedOptions.cpp index 6ffb37c4..bd4009b3 100644 --- a/cmd/launcher/CAdvancedOptions.cpp +++ b/cmd/launcher/CAdvancedOptions.cpp @@ -36,6 +36,7 @@ CAdvancedOptions::CAdvancedOptions(HWND parent, CConfig* config) : { assert(s_singleton == NULL); s_singleton = this; + init(); } CAdvancedOptions::~CAdvancedOptions() @@ -95,7 +96,7 @@ CAdvancedOptions::getCommandLine(bool isClient, const CString& serverName) const } void -CAdvancedOptions::init(HWND hwnd) +CAdvancedOptions::init() { // get values from registry HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); @@ -110,9 +111,6 @@ CAdvancedOptions::init(HWND hwnd) } CArchMiscWindows::closeKey(key); } - - // now set GUI - doInit(hwnd); } void @@ -194,7 +192,7 @@ CAdvancedOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) { switch (message) { case WM_INITDIALOG: - init(hwnd); + doInit(hwnd); return TRUE; case WM_COMMAND: diff --git a/cmd/launcher/CAdvancedOptions.h b/cmd/launcher/CAdvancedOptions.h index e4106f44..c86bf38a 100644 --- a/cmd/launcher/CAdvancedOptions.h +++ b/cmd/launcher/CAdvancedOptions.h @@ -54,7 +54,7 @@ public: //@} private: - void init(HWND hwnd); + void init(); void doInit(HWND hwnd); bool save(HWND hwnd); void setDefaults(HWND hwnd); diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp index 47561edd..3bfd5816 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -190,7 +190,7 @@ CMSWindowsClientTaskBarReceiver::copyLog() const if (!data.empty()) { CMSWindowsClipboard clipboard(m_window); clipboard.open(0); - clipboard.empty(); + clipboard.emptyUnowned(); clipboard.add(IClipboard::kText, data); clipboard.close(); } diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp index 87f3d298..ca6525ef 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -210,7 +210,7 @@ CMSWindowsServerTaskBarReceiver::copyLog() const if (!data.empty()) { CMSWindowsClipboard clipboard(m_window); clipboard.open(0); - clipboard.empty(); + clipboard.emptyUnowned(); clipboard.add(IClipboard::kText, data); clipboard.close(); } diff --git a/lib/arch/CArchLogWindows.cpp b/lib/arch/CArchLogWindows.cpp index 6c7ebbc4..6d21d6ba 100644 --- a/lib/arch/CArchLogWindows.cpp +++ b/lib/arch/CArchLogWindows.cpp @@ -20,7 +20,7 @@ // CArchLogWindows // -CArchLogWindows::CArchLogWindows() +CArchLogWindows::CArchLogWindows() : m_eventLog(NULL) { // do nothing } @@ -33,7 +33,7 @@ CArchLogWindows::~CArchLogWindows() void CArchLogWindows::openLog(const char* name) { - if (!CArchMiscWindows::isWindows95Family()) { + if (m_eventLog == NULL && !CArchMiscWindows::isWindows95Family()) { m_eventLog = RegisterEventSource(NULL, name); } } diff --git a/lib/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp index 1f3fd408..5f4c7675 100644 --- a/lib/platform/CMSWindowsClipboard.cpp +++ b/lib/platform/CMSWindowsClipboard.cpp @@ -43,7 +43,7 @@ CMSWindowsClipboard::~CMSWindowsClipboard() } bool -CMSWindowsClipboard::empty() +CMSWindowsClipboard::emptyUnowned() { LOG((CLOG_DEBUG "empty clipboard")); @@ -53,6 +53,16 @@ CMSWindowsClipboard::empty() return false; } + return true; +} + +bool +CMSWindowsClipboard::empty() +{ + if (!emptyUnowned()) { + return false; + } + // mark clipboard as being owned by synergy HGLOBAL data = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 1); SetClipboardData(getOwnershipFormat(), data); diff --git a/lib/platform/CMSWindowsClipboard.h b/lib/platform/CMSWindowsClipboard.h index ce7ba724..e9b59fb9 100644 --- a/lib/platform/CMSWindowsClipboard.h +++ b/lib/platform/CMSWindowsClipboard.h @@ -28,6 +28,21 @@ public: CMSWindowsClipboard(HWND window); virtual ~CMSWindowsClipboard(); + //! Empty clipboard without ownership + /*! + Take ownership of the clipboard and clear all data from it. + This must be called between a successful open() and close(). + Return false if the clipboard ownership could not be taken; + the clipboard should not be emptied in this case. Unlike + empty(), isOwnedBySynergy() will return false when emptied + this way. This is useful when synergy wants to put data on + clipboard but pretend (to itself) that some other app did it. + When using empty(), synergy assumes the data came from the + server and doesn't need to be sent back. emptyUnowned() + makes synergy send the data to the server. + */ + bool emptyUnowned(); + //! Test if clipboard is owned by synergy static bool isOwnedBySynergy(); From 4fea7719f3166b2b674448ba89bb9959dfeff94f Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 2 Sep 2003 21:41:00 +0000 Subject: [PATCH 559/807] Merged changes from 1.0 branch. --- lib/arch/CArchMultithreadWindows.cpp | 3 -- lib/mt/CThread.cpp | 7 ++- lib/platform/CXWindowsClipboard.cpp | 69 ++++++++++++++++++++-------- 3 files changed, 57 insertions(+), 22 deletions(-) diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index 2a457a50..f156021f 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -753,9 +753,6 @@ CArchMultithreadWindows::doThreadFunc(CArchThread thread) lockMutex(m_threadMutex); unlockMutex(m_threadMutex); - // default priority is slightly below normal - setPriorityOfThread(thread, 1); - void* result = NULL; try { // go diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp index fcf6832c..b2463e8b 100644 --- a/lib/mt/CThread.cpp +++ b/lib/mt/CThread.cpp @@ -170,8 +170,13 @@ CThread::threadFunc(void* vjob) result = e.m_result; LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x, result %p", id, result)); } + catch (XBase& e) { + LOG((CLOG_ERR "exception on thread 0x%08x: %s", id, e.what())); + delete job; + throw; + } catch (...) { - LOG((CLOG_DEBUG1 "exception on thread 0x%08x", id)); + LOG((CLOG_ERR "exception on thread 0x%08x: ", id)); delete job; throw; } diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index de463546..0606c3ef 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -1049,27 +1049,60 @@ CXWindowsClipboard::sendReply(CReply* reply) LOG((CLOG_DEBUG1 "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property)); reply->m_replied = true; - // HACK -- work around apparent bug in lesstif, which doesn't - // wait around for the SelectionNotify then gets confused when - // it sees it the next time it requests the selection. if it - // looks like a lesstif requestor window then don't send the - // SelectionNotify. it looks like a lesstif requestor if: - // it has a _MOTIF_CLIP_LOCK_ACCESS_VALID property - // it does not have a GDK_SELECTION property - CString dummy; - if (m_id != kClipboardClipboard || - !CXWindowsUtil::getWindowProperty(m_display, + // dump every property on the requestor window to the debug2 + // log. we've seen what appears to be a bug in lesstif and + // knowing the properties may help design a workaround, if + // it becomes necessary. + if (CLOG->getFilter() >= CLog::kDEBUG2) { + CXWindowsUtil::CErrorLock lock(m_display); + int n; + Atom* props = XListProperties(m_display, reply->m_requestor, &n); + LOG((CLOG_DEBUG2 "properties of 0x%08x:", reply->m_requestor)); + for (int i = 0; i < n; ++i) { + Atom target; + CString data; + char* name = XGetAtomName(m_display, props[i]); + if (!CXWindowsUtil::getWindowProperty(m_display, reply->m_requestor, - m_atomMotifClipAccess, - &dummy, NULL, NULL, False) || - CXWindowsUtil::getWindowProperty(m_display, - reply->m_requestor, - m_atomGDKSelection, - &dummy, NULL, NULL, False)) { - sendNotify(reply->m_requestor, m_selection, + props[i], &data, &target, NULL, False)) { + LOG((CLOG_DEBUG2 " %s: ", name)); + } + else { + // if there are any non-ascii characters in string + // then print the binary data. + static const char* hex = "0123456789abcdef"; + for (CString::size_type j = 0; j < data.size(); ++j) { + if (data[j] < 32 || data[j] > 126) { + CString tmp; + tmp.reserve(data.size() * 3); + for (j = 0; j < data.size(); ++j) { + unsigned char v = (unsigned char)data[j]; + tmp += hex[v >> 16]; + tmp += hex[v & 15]; + tmp += ' '; + } + data = tmp; + break; + } + } + char* type = XGetAtomName(m_display, target); + LOG((CLOG_DEBUG2 " %s (%s): %s", name, type, data.c_str())); + if (type != NULL) { + XFree(type); + } + } + if (name != NULL) { + XFree(name); + } + } + if (props != NULL) { + XFree(props); + } + } + + sendNotify(reply->m_requestor, m_selection, reply->m_target, reply->m_property, reply->m_time); - } } // wait for delete notify From 47ca409ff987fe6af518234861aff0bf67363bc6 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 2 Sep 2003 22:05:47 +0000 Subject: [PATCH 560/807] Merged primary and secondary screens into one class. --- cmd/synergyc/synergyc.cpp | 33 +- cmd/synergys/synergys.cpp | 30 +- configure.in | 2 +- lib/arch/CArch.cpp | 5 + lib/arch/CArchConsoleWindows.cpp | 7 + lib/arch/CArchMiscWindows.cpp | 27 +- lib/arch/CArchMiscWindows.h | 3 + lib/arch/CArchMultithreadPosix.cpp | 10 +- lib/arch/IArchMultithread.h | 8 +- lib/base/CPriorityQueue.h | 125 ++ lib/base/Makefile.am | 1 + lib/client/CClient.cpp | 31 +- lib/client/CClient.h | 21 +- lib/platform/CMSWindowsDesktop.cpp | 75 + lib/platform/CMSWindowsDesktop.h | 52 + lib/platform/CMSWindowsKeyMapper.cpp | 1495 +++++++++++++ lib/platform/CMSWindowsKeyMapper.h | 142 ++ lib/platform/CMSWindowsPrimaryScreen.cpp | 1816 --------------- lib/platform/CMSWindowsPrimaryScreen.h | 140 -- lib/platform/CMSWindowsScreen.cpp | 1952 +++++++++++++---- lib/platform/CMSWindowsScreen.h | 249 ++- lib/platform/CMSWindowsSecondaryScreen.cpp | 1338 ----------- lib/platform/CMSWindowsSecondaryScreen.h | 137 -- lib/platform/CSynergyHook.cpp | 24 +- lib/platform/CSynergyHook.h | 9 +- lib/platform/CXWindowsKeyMapper.cpp | 910 ++++++++ lib/platform/CXWindowsKeyMapper.h | 153 ++ lib/platform/CXWindowsPrimaryScreen.cpp | 1044 --------- lib/platform/CXWindowsPrimaryScreen.h | 126 -- lib/platform/CXWindowsScreen.cpp | 1590 +++++++++++--- lib/platform/CXWindowsScreen.h | 294 ++- lib/platform/CXWindowsSecondaryScreen.cpp | 1780 --------------- lib/platform/CXWindowsSecondaryScreen.h | 195 -- lib/platform/CXWindowsUtil.cpp | 249 +++ lib/platform/CXWindowsUtil.h | 13 + lib/platform/IMSWindowsScreenEventHandler.h | 55 - lib/platform/Makefile.am | 15 +- lib/platform/platform.dsp | 24 +- lib/server/CPrimaryClient.cpp | 31 +- lib/server/CPrimaryClient.h | 11 +- lib/server/CServer.cpp | 16 +- lib/server/CServer.h | 15 +- lib/synergy/CPrimaryScreen.cpp | 281 --- lib/synergy/CPrimaryScreen.h | 349 --- lib/synergy/CScreen.cpp | 1001 +++++++++ lib/synergy/CScreen.h | 357 +++ lib/synergy/CSecondaryScreen.cpp | 781 ------- lib/synergy/CSecondaryScreen.h | 446 ---- lib/synergy/IKeyState.h | 156 ++ lib/synergy/{IScreen.h => IPlatformScreen.h} | 110 +- lib/synergy/IPrimaryScreen.h | 84 + lib/synergy/IScreenEventHandler.h | 69 - ...rimaryScreenFactory.h => IScreenFactory.h} | 20 +- lib/synergy/ISecondaryScreen.h | 82 + lib/synergy/ISecondaryScreenFactory.h | 38 - lib/synergy/Makefile.am | 15 +- lib/synergy/libsynergy.dsp | 30 +- 57 files changed, 8263 insertions(+), 9809 deletions(-) create mode 100644 lib/base/CPriorityQueue.h create mode 100644 lib/platform/CMSWindowsDesktop.cpp create mode 100644 lib/platform/CMSWindowsDesktop.h create mode 100644 lib/platform/CMSWindowsKeyMapper.cpp create mode 100644 lib/platform/CMSWindowsKeyMapper.h delete mode 100644 lib/platform/CMSWindowsPrimaryScreen.cpp delete mode 100644 lib/platform/CMSWindowsPrimaryScreen.h delete mode 100644 lib/platform/CMSWindowsSecondaryScreen.cpp delete mode 100644 lib/platform/CMSWindowsSecondaryScreen.h create mode 100644 lib/platform/CXWindowsKeyMapper.cpp create mode 100644 lib/platform/CXWindowsKeyMapper.h delete mode 100644 lib/platform/CXWindowsPrimaryScreen.cpp delete mode 100644 lib/platform/CXWindowsPrimaryScreen.h delete mode 100644 lib/platform/CXWindowsSecondaryScreen.cpp delete mode 100644 lib/platform/CXWindowsSecondaryScreen.h delete mode 100644 lib/platform/IMSWindowsScreenEventHandler.h delete mode 100644 lib/synergy/CPrimaryScreen.cpp delete mode 100644 lib/synergy/CPrimaryScreen.h create mode 100644 lib/synergy/CScreen.cpp create mode 100644 lib/synergy/CScreen.h delete mode 100644 lib/synergy/CSecondaryScreen.cpp delete mode 100644 lib/synergy/CSecondaryScreen.h create mode 100644 lib/synergy/IKeyState.h rename lib/synergy/{IScreen.h => IPlatformScreen.h} (58%) create mode 100644 lib/synergy/IPrimaryScreen.h delete mode 100644 lib/synergy/IScreenEventHandler.h rename lib/synergy/{IPrimaryScreenFactory.h => IScreenFactory.h} (64%) create mode 100644 lib/synergy/ISecondaryScreen.h delete mode 100644 lib/synergy/ISecondaryScreenFactory.h diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index ce2dca07..02f84c3d 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -13,7 +13,7 @@ */ #include "CClient.h" -#include "ISecondaryScreenFactory.h" +#include "IScreenFactory.h" #include "ProtocolTypes.h" #include "Version.h" #include "XScreen.h" @@ -35,14 +35,13 @@ #define DAEMON_RUNNING(running_) #if WINDOWS_LIKE #include "CMSWindowsScreen.h" -#include "CMSWindowsSecondaryScreen.h" #include "CArchMiscWindows.h" #include "CMSWindowsClientTaskBarReceiver.h" #include "resource.h" #undef DAEMON_RUNNING #define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #elif UNIX_LIKE -#include "CXWindowsSecondaryScreen.h" +#include "CXWindowsScreen.h" #include "CXWindowsClientTaskBarReceiver.h" #endif @@ -88,28 +87,28 @@ CArgs* CArgs::s_instance = NULL; // platform dependent factories // -//! Factory for creating secondary screens +//! Factory for creating screens /*! -Objects of this type create secondary screens appropriate for the -platform. +Objects of this type create screens appropriate for the platform. */ -class CSecondaryScreenFactory : public ISecondaryScreenFactory { +class CScreenFactory : public IScreenFactory { public: - CSecondaryScreenFactory() { } - virtual ~CSecondaryScreenFactory() { } + CScreenFactory() { } + virtual ~CScreenFactory() { } - // ISecondaryScreenFactory overrides - virtual CSecondaryScreen* - create(IScreenReceiver*); + // IScreenFactory overrides + virtual IPlatformScreen* + create(IScreenReceiver*, IPrimaryScreenReceiver*); }; -CSecondaryScreen* -CSecondaryScreenFactory::create(IScreenReceiver* receiver) +IPlatformScreen* +CScreenFactory::create(IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) { #if WINDOWS_LIKE - return new CMSWindowsSecondaryScreen(receiver); + return new CMSWindowsScreen(receiver, primaryReceiver); #elif UNIX_LIKE - return new CXWindowsSecondaryScreen(receiver); + return new CXWindowsScreen(receiver, primaryReceiver); #endif } @@ -167,7 +166,7 @@ realMain(void) // create client s_client = new CClient(ARG->m_name); s_client->setAddress(ARG->m_serverAddress); - s_client->setScreenFactory(new CSecondaryScreenFactory); + s_client->setScreenFactory(new CScreenFactory); s_client->setSocketFactory(new CTCPSocketFactory); s_client->setStreamFilterFactory(NULL); diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index e3fb74de..59b76fce 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -14,7 +14,7 @@ #include "CServer.h" #include "CConfig.h" -#include "IPrimaryScreenFactory.h" +#include "IScreenFactory.h" #include "ProtocolTypes.h" #include "Version.h" #include "XScreen.h" @@ -34,14 +34,13 @@ #define DAEMON_RUNNING(running_) #if WINDOWS_LIKE #include "CMSWindowsScreen.h" -#include "CMSWindowsPrimaryScreen.h" #include "CArchMiscWindows.h" #include "CMSWindowsServerTaskBarReceiver.h" #include "resource.h" #undef DAEMON_RUNNING #define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #elif UNIX_LIKE -#include "CXWindowsPrimaryScreen.h" +#include "CXWindowsScreen.h" #include "CXWindowsServerTaskBarReceiver.h" #endif @@ -100,29 +99,28 @@ CArgs* CArgs::s_instance = NULL; // platform dependent factories // -//! Factory for creating primary screens +//! Factory for creating screens /*! -Objects of this type create primary screens appropriate for the -platform. +Objects of this type create screens appropriate for the platform. */ -class CPrimaryScreenFactory : public IPrimaryScreenFactory { +class CScreenFactory : public IScreenFactory { public: - CPrimaryScreenFactory() { } - virtual ~CPrimaryScreenFactory() { } + CScreenFactory() { } + virtual ~CScreenFactory() { } - // IPrimaryScreenFactory overrides - virtual CPrimaryScreen* + // IScreenFactory overrides + virtual IPlatformScreen* create(IScreenReceiver*, IPrimaryScreenReceiver*); }; -CPrimaryScreen* -CPrimaryScreenFactory::create(IScreenReceiver* receiver, +IPlatformScreen* +CScreenFactory::create(IScreenReceiver* receiver, IPrimaryScreenReceiver* primaryReceiver) { #if WINDOWS_LIKE - return new CMSWindowsPrimaryScreen(receiver, primaryReceiver); + return new CMSWindowsScreen(receiver, primaryReceiver); #elif UNIX_LIKE - return new CXWindowsPrimaryScreen(receiver, primaryReceiver); + return new CXWindowsScreen(receiver, primaryReceiver); #endif } @@ -201,7 +199,7 @@ realMain(void) // create server s_server = new CServer(ARG->m_name); s_server->setConfig(ARG->m_config); - s_server->setScreenFactory(new CPrimaryScreenFactory); + s_server->setScreenFactory(new CScreenFactory); s_server->setSocketFactory(new CTCPSocketFactory); s_server->setStreamFilterFactory(NULL); diff --git a/configure.in b/configure.in index a8beba9e..cb0e2a6c 100644 --- a/configure.in +++ b/configure.in @@ -96,7 +96,7 @@ dnl checks for system services dnl adjust variables for X11 and pthreads -CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" +CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500" LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" AC_OUTPUT([ diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index b8322c33..30bfb38c 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -32,6 +32,7 @@ # include "CArchDaemonWindows.h" # include "CArchFileWindows.h" # include "CArchLogWindows.h" +# include "CArchMiscWindows.h" # include "CArchMultithreadWindows.h" # include "CArchNetworkWinsock.h" # include "CArchSleepWindows.h" @@ -116,6 +117,10 @@ CArch::CArch(ARCH_ARGS* args) m_console = new ARCH_CONSOLE; m_daemon = new ARCH_DAEMON; m_taskbar = new ARCH_TASKBAR(args); + +#if WINDOWS_LIKE + CArchMiscWindows::init(); +#endif } CArch::~CArch() diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp index a89a42f4..f479b378 100644 --- a/lib/arch/CArchConsoleWindows.cpp +++ b/lib/arch/CArchConsoleWindows.cpp @@ -29,6 +29,13 @@ CArchConsoleWindows::CArchConsoleWindows() : s_thread = ARCH->newCurrentThread(); m_mutex = ARCH->newMutex(); + + // dummy write to stderr to create locks in stdio from the main + // thread. if we open the console from another thread then we + // can deadlock in stdio when trying to write from a 3rd thread. + // writes to stderr without a console don't go anywhere so the + // user won't notice this. + fprintf(stderr, "\n"); } CArchConsoleWindows::~CArchConsoleWindows() diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index f76b42f1..1fb3d7bd 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -19,16 +19,31 @@ // CArchMiscWindows // +void +CArchMiscWindows::init() +{ + isWindows95Family(); +} + bool CArchMiscWindows::isWindows95Family() { - OSVERSIONINFO version; - version.dwOSVersionInfoSize = sizeof(version); - if (GetVersionEx(&version) == 0) { - // cannot determine OS; assume windows 95 family - return true; + static bool init = false; + static bool result = false; + + if (!init) { + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + // cannot determine OS; assume windows 95 family + result = true; + } + else { + result = (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); + } + init = true; } - return (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); + return result; } int diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index dc993362..766553ff 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -26,6 +26,9 @@ class CArchMiscWindows { public: typedef int (*RunFunc)(void); + //! Initialize + static void init(); + //! Test if windows 95, et al. /*! Returns true iff the platform is win95/98/me. diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index bef806bf..67e5fcca 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -252,14 +252,12 @@ CArchMultithreadPosix::waitCondVar(CArchCond cond, CArchMutex CArchMultithreadPosix::newMutex() { + pthread_mutexattr_t attr; + int status = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + assert(status == 0); CArchMutexImpl* mutex = new CArchMutexImpl; - int status = pthread_mutex_init(&mutex->m_mutex, NULL); + status = pthread_mutex_init(&mutex->m_mutex, &attr); assert(status == 0); -/* - status = pthread_mutexattr_settype(&mutex->m_mutex, - PTHREAD_MUTEX_RECURSIVE); - assert(status == 0); -*/ return mutex; } diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index ee376771..69d88694 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -124,11 +124,11 @@ public: // mutex methods // - //! Create a non-recursive mutex + //! Create a recursive mutex /*! - Creates a non-recursive mutex. A thread must not lock a - non-recursive mutex when it already holds a lock on that mutex. - If it does it will deadlock. The mutex is an opaque data type. + Creates a recursive mutex. A thread may lock a recursive mutex + when it already holds a lock on that mutex. The mutex is an + opaque data type. */ virtual CArchMutex newMutex() = 0; diff --git a/lib/base/CPriorityQueue.h b/lib/base/CPriorityQueue.h new file mode 100644 index 00000000..a03024b1 --- /dev/null +++ b/lib/base/CPriorityQueue.h @@ -0,0 +1,125 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CPRIORITYQUEUE_H +#define CPRIORITYQUEUE_H + +#include "stdvector.h" +#include +#include + +//! A priority queue with an iterator +/*! +This priority queue is the same as a standard priority queue except: +it sorts by std::greater, it has a forward iterator through the elements +(which can appear in any order), and its contents can be swapped. +*/ +template , + class Compare = std::greater > +class CPriorityQueue { +public: + typedef typename Container::value_type value_type; + typedef typename Container::size_type size_type; + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + typedef Container container_type; + + CPriorityQueue() { } + CPriorityQueue(Container& swappedIn) { swap(swappedIn); } + ~CPriorityQueue() { } + + //! @name manipulators + //@{ + + //! Add element + void push(const value_type& v) + { + c.push_back(v); + std::push_heap(c.begin(), c.end(), comp); + } + + //! Remove head element + void pop() + { + std::pop_heap(c.begin(), c.end(), comp); + c.pop_back(); + } + + //! Get start iterator + iterator begin() + { + return c.begin(); + } + + //! Get end iterator + iterator end() + { + return c.end(); + } + + //! Swap contents with another priority queue + void swap(CPriorityQueue& q) + { + c.swap(q.c); + } + + //! Swap contents with another container + void swap(Container& c2) + { + c.swap(c2); + std::make_heap(c.begin(), c.end(), comp); + } + + //@} + //! @name accessors + //@{ + + //! Returns true if there are no elements + bool empty() const + { + return c.empty(); + } + + //! Returns the number of elements + size_type size() const + { + return c.size(); + } + + //! Returns the head element + const value_type& top() const + { + return c.front(); + } + + //! Get start iterator + const_iterator begin() const + { + return c.begin(); + } + + //! Get end iterator + const_iterator end() const + { + return c.end(); + } + + //@} + +private: + Container c; + Compare comp; +}; + +#endif diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index f750ba7f..f9006ab2 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -36,6 +36,7 @@ libbase_a_SOURCES = \ CFunctionJob.h \ CJobList.h \ CLog.h \ + CPriorityQueue.h \ CStopwatch.h \ CString.h \ CStringUtil.h \ diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 8ada1111..1cc8a688 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -14,12 +14,13 @@ #include "CClient.h" #include "CServerProxy.h" -#include "ISecondaryScreenFactory.h" +#include "CScreen.h" +#include "IScreenFactory.h" #include "CClipboard.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" -#include "CSecondaryScreen.h" +#include "IPlatformScreen.h" #include "IServer.h" #include "ProtocolTypes.h" #include "XScreen.h" @@ -72,7 +73,7 @@ CClient::setAddress(const CNetworkAddress& serverAddress) } void -CClient::setScreenFactory(ISecondaryScreenFactory* adopted) +CClient::setScreenFactory(IScreenFactory* adopted) { CLock lock(&m_mutex); delete m_screenFactory; @@ -305,8 +306,9 @@ CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool) CLock lock(&m_mutex); m_active = true; } - - m_screen->enter(xAbs, yAbs, mask); + m_screen->mouseMove(xAbs, yAbs); + m_screen->enter(); + m_screen->setToggleState(mask); } bool @@ -464,7 +466,10 @@ CClient::openSecondaryScreen() // create screen LOG((CLOG_DEBUG1 "creating secondary screen")); if (m_screenFactory != NULL) { - m_screen = m_screenFactory->create(this); + IPlatformScreen* platformScreen = m_screenFactory->create(this, NULL); + if (platformScreen != NULL) { + m_screen = new CScreen(platformScreen, this); + } } if (m_screen == NULL) { throw XScreenOpenFailure(); @@ -634,9 +639,11 @@ CClient::runServer() m_server = proxy; } + bool enabled = false; try { - // prepare for remote control - m_screen->remoteControl(); + // enable the screen + m_screen->enable(); + enabled = true; // process messages bool rejected = true; @@ -647,8 +654,8 @@ CClient::runServer() setStatus(kNotRunning); } - // prepare for local control - m_screen->localControl(); + // disable the screen + m_screen->disable(); // clean up CLock lock(&m_mutex); @@ -661,7 +668,9 @@ CClient::runServer() } catch (...) { setStatus(kNotRunning); - m_screen->localControl(); + if (enabled) { + m_screen->disable(); + } CLock lock(&m_mutex); m_rejected = false; m_server = NULL; diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 5647e7e3..4a802a5a 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -22,12 +22,12 @@ #include "CMutex.h" #include "CJobList.h" -class CSecondaryScreen; +class CScreen; class CServerProxy; class CThread; class IDataSocket; class IScreenReceiver; -class ISecondaryScreenFactory; +class IScreenFactory; class ISocketFactory; class IStreamFilterFactory; @@ -60,13 +60,12 @@ public: */ void setAddress(const CNetworkAddress& serverAddress); - //! Set secondary screen factory + //! Set screen factory /*! - Sets the factory for creating secondary screens. This must be - set before calling open(). This object takes ownership of the - factory. + Sets the factory for creating screens. This must be set before + calling open(). This object takes ownership of the factory. */ - void setScreenFactory(ISecondaryScreenFactory*); + void setScreenFactory(IScreenFactory*); //! Set socket factory /*! @@ -184,12 +183,12 @@ private: private: CMutex m_mutex; CString m_name; - CSecondaryScreen* m_screen; + CScreen* m_screen; IScreenReceiver* m_server; CNetworkAddress m_serverAddress; - ISecondaryScreenFactory* m_screenFactory; - ISocketFactory* m_socketFactory; - IStreamFilterFactory* m_streamFilterFactory; + IScreenFactory* m_screenFactory; + ISocketFactory* m_socketFactory; + IStreamFilterFactory* m_streamFilterFactory; CThread* m_session; bool m_active; bool m_rejected; diff --git a/lib/platform/CMSWindowsDesktop.cpp b/lib/platform/CMSWindowsDesktop.cpp new file mode 100644 index 00000000..f62b6b1b --- /dev/null +++ b/lib/platform/CMSWindowsDesktop.cpp @@ -0,0 +1,75 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsDesktop.h" +#include "CLog.h" +#include "CArchMiscWindows.h" +#include + +// +// CMSWindowsDesktop +// + +HDESK +CMSWindowsDesktop::openInputDesktop() +{ + if (CArchMiscWindows::isWindows95Family()) { + // there's only one desktop on windows 95 et al. + return GetThreadDesktop(GetCurrentThreadId()); + } + else { + return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, + DESKTOP_CREATEWINDOW | + DESKTOP_HOOKCONTROL | + GENERIC_WRITE); + } +} + +void +CMSWindowsDesktop::closeDesktop(HDESK desk) +{ + // on 95/98/me we don't need to close the desktop returned by + // openInputDesktop(). + if (desk != NULL && !CArchMiscWindows::isWindows95Family()) { + CloseDesktop(desk); + } +} + +bool +CMSWindowsDesktop::setDesktop(HDESK desk) +{ + // 95/98/me doesn't support multiple desktops so just return + // true on those platforms. + return (CArchMiscWindows::isWindows95Family() || + SetThreadDesktop(desk) != 0); +} + +CString +CMSWindowsDesktop::getDesktopName(HDESK desk) +{ + if (desk == NULL) { + return CString(); + } + else if (CArchMiscWindows::isWindows95Family()) { + return "desktop"; + } + else { + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR)); + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + CString result(name); + return result; + } +} diff --git a/lib/platform/CMSWindowsDesktop.h b/lib/platform/CMSWindowsDesktop.h new file mode 100644 index 00000000..6f1d6afe --- /dev/null +++ b/lib/platform/CMSWindowsDesktop.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSDESKTOP_H +#define CMSWINDOWSDESKTOP_H + +#include "CString.h" +#include + +//! Encapsulate Microsoft Windows desktop +class CMSWindowsDesktop { +public: + //! Open the input desktop + /*! + Opens the input desktop. The caller must close the desktop. + */ + static HDESK openInputDesktop(); + + //! Close a desktop + /*! + Closes the given desktop. + */ + static void closeDesktop(HDESK); + + //! Change current desktop + /*! + Changes the calling thread's desktop, return true iff successful. + The call will fail if the calling thread has any windows or a hooks + on the current desktop. + */ + static bool setDesktop(HDESK); + + //! Get the desktop's name. + /*! + Returns the current desktop's name. Returns a constant string + on 95/98/Me. + */ + static CString getDesktopName(HDESK); +}; + +#endif diff --git a/lib/platform/CMSWindowsKeyMapper.cpp b/lib/platform/CMSWindowsKeyMapper.cpp new file mode 100644 index 00000000..4e4fc957 --- /dev/null +++ b/lib/platform/CMSWindowsKeyMapper.cpp @@ -0,0 +1,1495 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsKeyMapper.h" +#include "CLog.h" + +// multimedia keys +#if !defined(VK_BROWSER_BACK) +#define VK_BROWSER_BACK 0xA6 +#define VK_BROWSER_FORWARD 0xA7 +#define VK_BROWSER_REFRESH 0xA8 +#define VK_BROWSER_STOP 0xA9 +#define VK_BROWSER_SEARCH 0xAA +#define VK_BROWSER_FAVORITES 0xAB +#define VK_BROWSER_HOME 0xAC +#define VK_VOLUME_MUTE 0xAD +#define VK_VOLUME_DOWN 0xAE +#define VK_VOLUME_UP 0xAF +#define VK_MEDIA_NEXT_TRACK 0xB0 +#define VK_MEDIA_PREV_TRACK 0xB1 +#define VK_MEDIA_STOP 0xB2 +#define VK_MEDIA_PLAY_PAUSE 0xB3 +#define VK_LAUNCH_MAIL 0xB4 +#define VK_LAUNCH_MEDIA_SELECT 0xB5 +#define VK_LAUNCH_APP1 0xB6 +#define VK_LAUNCH_APP2 0xB7 +#endif + +// +// CMSWindowsKeyMapper +// + +// table of modifier keys +const CMSWindowsKeyMapper::CModifierKeys + CMSWindowsKeyMapper::s_modifiers[] = +{ + KeyModifierShift, { VK_LSHIFT, VK_RSHIFT }, + KeyModifierControl, { VK_LCONTROL, VK_RCONTROL | 0x100 }, + KeyModifierAlt, { VK_LMENU, VK_RMENU | 0x100 }, + KeyModifierSuper, { VK_LWIN | 0x100, VK_RWIN | 0x100 }, + KeyModifierCapsLock, { VK_CAPITAL, 0 }, + KeyModifierNumLock, { VK_NUMLOCK | 0x100, 0 }, + KeyModifierScrollLock, { VK_SCROLL, 0 } +}; + +const char* CMSWindowsKeyMapper::s_vkToName[] = +{ + "vk 0x00", + "Left Button", + "Right Button", + "VK_CANCEL", + "Middle Button", + "vk 0x05", + "vk 0x06", + "vk 0x07", + "VK_BACK", + "VK_TAB", + "vk 0x0a", + "vk 0x0b", + "VK_CLEAR", + "VK_RETURN", + "vk 0x0e", + "vk 0x0f", + "VK_SHIFT", + "VK_CONTROL", + "VK_MENU", + "VK_PAUSE", + "VK_CAPITAL", + "VK_KANA", + "vk 0x16", + "VK_JUNJA", + "VK_FINAL", + "VK_KANJI", + "vk 0x1a", + "VK_ESCAPE", + "VK_CONVERT", + "VK_NONCONVERT", + "VK_ACCEPT", + "VK_MODECHANGE", + "VK_SPACE", + "VK_PRIOR", + "VK_NEXT", + "VK_END", + "VK_HOME", + "VK_LEFT", + "VK_UP", + "VK_RIGHT", + "VK_DOWN", + "VK_SELECT", + "VK_PRINT", + "VK_EXECUTE", + "VK_SNAPSHOT", + "VK_INSERT", + "VK_DELETE", + "VK_HELP", + "VK_0", + "VK_1", + "VK_2", + "VK_3", + "VK_4", + "VK_5", + "VK_6", + "VK_7", + "VK_8", + "VK_9", + "vk 0x3a", + "vk 0x3b", + "vk 0x3c", + "vk 0x3d", + "vk 0x3e", + "vk 0x3f", + "vk 0x40", + "VK_A", + "VK_B", + "VK_C", + "VK_D", + "VK_E", + "VK_F", + "VK_G", + "VK_H", + "VK_I", + "VK_J", + "VK_K", + "VK_L", + "VK_M", + "VK_N", + "VK_O", + "VK_P", + "VK_Q", + "VK_R", + "VK_S", + "VK_T", + "VK_U", + "VK_V", + "VK_W", + "VK_X", + "VK_Y", + "VK_Z", + "VK_LWIN", + "VK_RWIN", + "VK_APPS", + "vk 0x5e", + "vk 0x5f", + "VK_NUMPAD0", + "VK_NUMPAD1", + "VK_NUMPAD2", + "VK_NUMPAD3", + "VK_NUMPAD4", + "VK_NUMPAD5", + "VK_NUMPAD6", + "VK_NUMPAD7", + "VK_NUMPAD8", + "VK_NUMPAD9", + "VK_MULTIPLY", + "VK_ADD", + "VK_SEPARATOR", + "VK_SUBTRACT", + "VK_DECIMAL", + "VK_DIVIDE", + "VK_F1", + "VK_F2", + "VK_F3", + "VK_F4", + "VK_F5", + "VK_F6", + "VK_F7", + "VK_F8", + "VK_F9", + "VK_F10", + "VK_F11", + "VK_F12", + "VK_F13", + "VK_F14", + "VK_F15", + "VK_F16", + "VK_F17", + "VK_F18", + "VK_F19", + "VK_F20", + "VK_F21", + "VK_F22", + "VK_F23", + "VK_F24", + "vk 0x88", + "vk 0x89", + "vk 0x8a", + "vk 0x8b", + "vk 0x8c", + "vk 0x8d", + "vk 0x8e", + "vk 0x8f", + "VK_NUMLOCK", + "VK_SCROLL", + "vk 0x92", + "vk 0x93", + "vk 0x94", + "vk 0x95", + "vk 0x96", + "vk 0x97", + "vk 0x98", + "vk 0x99", + "vk 0x9a", + "vk 0x9b", + "vk 0x9c", + "vk 0x9d", + "vk 0x9e", + "vk 0x9f", + "VK_LSHIFT", + "VK_RSHIFT", + "VK_LCONTROL", + "VK_RCONTROL", + "VK_LMENU", + "VK_RMENU", + "VK_BROWSER_BACK", + "VK_BROWSER_FORWARD", + "VK_BROWSER_REFRESH", + "VK_BROWSER_STOP", + "VK_BROWSER_SEARCH", + "VK_BROWSER_FAVORITES", + "VK_BROWSER_HOME", + "VK_VOLUME_MUTE", + "VK_VOLUME_DOWN", + "VK_VOLUME_UP", + "VK_MEDIA_NEXT_TRACK", + "VK_MEDIA_PREV_TRACK", + "VK_MEDIA_STOP", + "VK_MEDIA_PLAY_PAUSE", + "VK_LAUNCH_MAIL", + "VK_LAUNCH_MEDIA_SELECT", + "VK_LAUNCH_APP1", + "VK_LAUNCH_APP2", + "vk 0xb8", + "vk 0xb9", + "vk 0xba", + "vk 0xbb", + "vk 0xbc", + "vk 0xbd", + "vk 0xbe", + "vk 0xbf", + "vk 0xc0", + "vk 0xc1", + "vk 0xc2", + "vk 0xc3", + "vk 0xc4", + "vk 0xc5", + "vk 0xc6", + "vk 0xc7", + "vk 0xc8", + "vk 0xc9", + "vk 0xca", + "vk 0xcb", + "vk 0xcc", + "vk 0xcd", + "vk 0xce", + "vk 0xcf", + "vk 0xd0", + "vk 0xd1", + "vk 0xd2", + "vk 0xd3", + "vk 0xd4", + "vk 0xd5", + "vk 0xd6", + "vk 0xd7", + "vk 0xd8", + "vk 0xd9", + "vk 0xda", + "vk 0xdb", + "vk 0xdc", + "vk 0xdd", + "vk 0xde", + "vk 0xdf", + "vk 0xe0", + "vk 0xe1", + "vk 0xe2", + "vk 0xe3", + "vk 0xe4", + "VK_PROCESSKEY", + "vk 0xe6", + "vk 0xe7", + "vk 0xe8", + "vk 0xe9", + "vk 0xea", + "vk 0xeb", + "vk 0xec", + "vk 0xed", + "vk 0xee", + "vk 0xef", + "vk 0xf0", + "vk 0xf1", + "vk 0xf2", + "vk 0xf3", + "vk 0xf4", + "vk 0xf5", + "VK_ATTN", + "VK_CRSEL", + "VK_EXSEL", + "VK_EREOF", + "VK_PLAY", + "VK_ZOOM", + "VK_NONAME", + "VK_PA1", + "VK_OEM_CLEAR", + "vk 0xff" +}; + +// map virtual keys to synergy key enumeration +const KeyID CMSWindowsKeyMapper::s_virtualKey[][2] = +{ + /* 0x00 */ kKeyNone, kKeyNone, // reserved + /* 0x01 */ kKeyNone, kKeyNone, // VK_LBUTTON + /* 0x02 */ kKeyNone, kKeyNone, // VK_RBUTTON + /* 0x03 */ kKeyNone, kKeyBreak, // VK_CANCEL + /* 0x04 */ kKeyNone, kKeyNone, // VK_MBUTTON + /* 0x05 */ kKeyNone, kKeyNone, // undefined + /* 0x06 */ kKeyNone, kKeyNone, // undefined + /* 0x07 */ kKeyNone, kKeyNone, // undefined + /* 0x08 */ kKeyBackSpace, kKeyNone, // VK_BACK + /* 0x09 */ kKeyTab, kKeyNone, // VK_TAB + /* 0x0a */ kKeyNone, kKeyNone, // undefined + /* 0x0b */ kKeyNone, kKeyNone, // undefined + /* 0x0c */ kKeyClear, kKeyClear, // VK_CLEAR + /* 0x0d */ kKeyReturn, kKeyKP_Enter, // VK_RETURN + /* 0x0e */ kKeyNone, kKeyNone, // undefined + /* 0x0f */ kKeyNone, kKeyNone, // undefined + /* 0x10 */ kKeyShift_L, kKeyShift_R, // VK_SHIFT + /* 0x11 */ kKeyControl_L, kKeyControl_R, // VK_CONTROL + /* 0x12 */ kKeyAlt_L, kKeyAlt_R, // VK_MENU + /* 0x13 */ kKeyPause, kKeyNone, // VK_PAUSE + /* 0x14 */ kKeyCapsLock, kKeyNone, // VK_CAPITAL + /* 0x15 */ kKeyNone, kKeyNone, // VK_KANA + /* 0x16 */ kKeyNone, kKeyNone, // VK_HANGUL + /* 0x17 */ kKeyNone, kKeyNone, // VK_JUNJA + /* 0x18 */ kKeyNone, kKeyNone, // VK_FINAL + /* 0x19 */ kKeyNone, kKeyNone, // VK_KANJI + /* 0x1a */ kKeyNone, kKeyNone, // undefined + /* 0x1b */ kKeyEscape, kKeyNone, // VK_ESCAPE + /* 0x1c */ kKeyNone, kKeyNone, // VK_CONVERT + /* 0x1d */ kKeyNone, kKeyNone, // VK_NONCONVERT + /* 0x1e */ kKeyNone, kKeyNone, // VK_ACCEPT + /* 0x1f */ kKeyNone, kKeyNone, // VK_MODECHANGE + /* 0x20 */ kKeyNone, kKeyNone, // VK_SPACE + /* 0x21 */ kKeyKP_PageUp, kKeyPageUp, // VK_PRIOR + /* 0x22 */ kKeyKP_PageDown, kKeyPageDown, // VK_NEXT + /* 0x23 */ kKeyKP_End, kKeyEnd, // VK_END + /* 0x24 */ kKeyKP_Home, kKeyHome, // VK_HOME + /* 0x25 */ kKeyKP_Left, kKeyLeft, // VK_LEFT + /* 0x26 */ kKeyKP_Up, kKeyUp, // VK_UP + /* 0x27 */ kKeyKP_Right, kKeyRight, // VK_RIGHT + /* 0x28 */ kKeyKP_Down, kKeyDown, // VK_DOWN + /* 0x29 */ kKeySelect, kKeySelect, // VK_SELECT + /* 0x2a */ kKeyNone, kKeyNone, // VK_PRINT + /* 0x2b */ kKeyExecute, kKeyExecute, // VK_EXECUTE + /* 0x2c */ kKeyPrint, kKeyPrint, // VK_SNAPSHOT + /* 0x2d */ kKeyKP_Insert, kKeyInsert, // VK_INSERT + /* 0x2e */ kKeyKP_Delete, kKeyDelete, // VK_DELETE + /* 0x2f */ kKeyHelp, kKeyHelp, // VK_HELP + /* 0x30 */ kKeyNone, kKeyNone, // VK_0 + /* 0x31 */ kKeyNone, kKeyNone, // VK_1 + /* 0x32 */ kKeyNone, kKeyNone, // VK_2 + /* 0x33 */ kKeyNone, kKeyNone, // VK_3 + /* 0x34 */ kKeyNone, kKeyNone, // VK_4 + /* 0x35 */ kKeyNone, kKeyNone, // VK_5 + /* 0x36 */ kKeyNone, kKeyNone, // VK_6 + /* 0x37 */ kKeyNone, kKeyNone, // VK_7 + /* 0x38 */ kKeyNone, kKeyNone, // VK_8 + /* 0x39 */ kKeyNone, kKeyNone, // VK_9 + /* 0x3a */ kKeyNone, kKeyNone, // undefined + /* 0x3b */ kKeyNone, kKeyNone, // undefined + /* 0x3c */ kKeyNone, kKeyNone, // undefined + /* 0x3d */ kKeyNone, kKeyNone, // undefined + /* 0x3e */ kKeyNone, kKeyNone, // undefined + /* 0x3f */ kKeyNone, kKeyNone, // undefined + /* 0x40 */ kKeyNone, kKeyNone, // undefined + /* 0x41 */ kKeyNone, kKeyNone, // VK_A + /* 0x42 */ kKeyNone, kKeyNone, // VK_B + /* 0x43 */ kKeyNone, kKeyNone, // VK_C + /* 0x44 */ kKeyNone, kKeyNone, // VK_D + /* 0x45 */ kKeyNone, kKeyNone, // VK_E + /* 0x46 */ kKeyNone, kKeyNone, // VK_F + /* 0x47 */ kKeyNone, kKeyNone, // VK_G + /* 0x48 */ kKeyNone, kKeyNone, // VK_H + /* 0x49 */ kKeyNone, kKeyNone, // VK_I + /* 0x4a */ kKeyNone, kKeyNone, // VK_J + /* 0x4b */ kKeyNone, kKeyNone, // VK_K + /* 0x4c */ kKeyNone, kKeyNone, // VK_L + /* 0x4d */ kKeyNone, kKeyNone, // VK_M + /* 0x4e */ kKeyNone, kKeyNone, // VK_N + /* 0x4f */ kKeyNone, kKeyNone, // VK_O + /* 0x50 */ kKeyNone, kKeyNone, // VK_P + /* 0x51 */ kKeyNone, kKeyNone, // VK_Q + /* 0x52 */ kKeyNone, kKeyNone, // VK_R + /* 0x53 */ kKeyNone, kKeyNone, // VK_S + /* 0x54 */ kKeyNone, kKeyNone, // VK_T + /* 0x55 */ kKeyNone, kKeyNone, // VK_U + /* 0x56 */ kKeyNone, kKeyNone, // VK_V + /* 0x57 */ kKeyNone, kKeyNone, // VK_W + /* 0x58 */ kKeyNone, kKeyNone, // VK_X + /* 0x59 */ kKeyNone, kKeyNone, // VK_Y + /* 0x5a */ kKeyNone, kKeyNone, // VK_Z + /* 0x5b */ kKeyNone, kKeySuper_L, // VK_LWIN + /* 0x5c */ kKeyNone, kKeySuper_R, // VK_RWIN + /* 0x5d */ kKeyMenu, kKeyMenu, // VK_APPS + /* 0x5e */ kKeyNone, kKeyNone, // undefined + /* 0x5f */ kKeyNone, kKeyNone, // undefined + /* 0x60 */ kKeyKP_0, kKeyNone, // VK_NUMPAD0 + /* 0x61 */ kKeyKP_1, kKeyNone, // VK_NUMPAD1 + /* 0x62 */ kKeyKP_2, kKeyNone, // VK_NUMPAD2 + /* 0x63 */ kKeyKP_3, kKeyNone, // VK_NUMPAD3 + /* 0x64 */ kKeyKP_4, kKeyNone, // VK_NUMPAD4 + /* 0x65 */ kKeyKP_5, kKeyNone, // VK_NUMPAD5 + /* 0x66 */ kKeyKP_6, kKeyNone, // VK_NUMPAD6 + /* 0x67 */ kKeyKP_7, kKeyNone, // VK_NUMPAD7 + /* 0x68 */ kKeyKP_8, kKeyNone, // VK_NUMPAD8 + /* 0x69 */ kKeyKP_9, kKeyNone, // VK_NUMPAD9 + /* 0x6a */ kKeyKP_Multiply, kKeyNone, // VK_MULTIPLY + /* 0x6b */ kKeyKP_Add, kKeyNone, // VK_ADD + /* 0x6c */ kKeyKP_Separator,kKeyKP_Separator,// VK_SEPARATOR + /* 0x6d */ kKeyKP_Subtract, kKeyNone, // VK_SUBTRACT + /* 0x6e */ kKeyKP_Decimal, kKeyNone, // VK_DECIMAL + /* 0x6f */ kKeyNone, kKeyKP_Divide, // VK_DIVIDE + /* 0x70 */ kKeyF1, kKeyNone, // VK_F1 + /* 0x71 */ kKeyF2, kKeyNone, // VK_F2 + /* 0x72 */ kKeyF3, kKeyNone, // VK_F3 + /* 0x73 */ kKeyF4, kKeyNone, // VK_F4 + /* 0x74 */ kKeyF5, kKeyNone, // VK_F5 + /* 0x75 */ kKeyF6, kKeyNone, // VK_F6 + /* 0x76 */ kKeyF7, kKeyNone, // VK_F7 + /* 0x77 */ kKeyF8, kKeyNone, // VK_F8 + /* 0x78 */ kKeyF9, kKeyNone, // VK_F9 + /* 0x79 */ kKeyF10, kKeyNone, // VK_F10 + /* 0x7a */ kKeyF11, kKeyNone, // VK_F11 + /* 0x7b */ kKeyF12, kKeyNone, // VK_F12 + /* 0x7c */ kKeyF13, kKeyF13, // VK_F13 + /* 0x7d */ kKeyF14, kKeyF14, // VK_F14 + /* 0x7e */ kKeyF15, kKeyF15, // VK_F15 + /* 0x7f */ kKeyF16, kKeyF16, // VK_F16 + /* 0x80 */ kKeyF17, kKeyF17, // VK_F17 + /* 0x81 */ kKeyF18, kKeyF18, // VK_F18 + /* 0x82 */ kKeyF19, kKeyF19, // VK_F19 + /* 0x83 */ kKeyF20, kKeyF20, // VK_F20 + /* 0x84 */ kKeyF21, kKeyF21, // VK_F21 + /* 0x85 */ kKeyF22, kKeyF22, // VK_F22 + /* 0x86 */ kKeyF23, kKeyF23, // VK_F23 + /* 0x87 */ kKeyF24, kKeyF24, // VK_F24 + /* 0x88 */ kKeyNone, kKeyNone, // unassigned + /* 0x89 */ kKeyNone, kKeyNone, // unassigned + /* 0x8a */ kKeyNone, kKeyNone, // unassigned + /* 0x8b */ kKeyNone, kKeyNone, // unassigned + /* 0x8c */ kKeyNone, kKeyNone, // unassigned + /* 0x8d */ kKeyNone, kKeyNone, // unassigned + /* 0x8e */ kKeyNone, kKeyNone, // unassigned + /* 0x8f */ kKeyNone, kKeyNone, // unassigned + /* 0x90 */ kKeyNumLock, kKeyNumLock, // VK_NUMLOCK + /* 0x91 */ kKeyScrollLock, kKeyNone, // VK_SCROLL + /* 0x92 */ kKeyNone, kKeyNone, // unassigned + /* 0x93 */ kKeyNone, kKeyNone, // unassigned + /* 0x94 */ kKeyNone, kKeyNone, // unassigned + /* 0x95 */ kKeyNone, kKeyNone, // unassigned + /* 0x96 */ kKeyNone, kKeyNone, // unassigned + /* 0x97 */ kKeyNone, kKeyNone, // unassigned + /* 0x98 */ kKeyNone, kKeyNone, // unassigned + /* 0x99 */ kKeyNone, kKeyNone, // unassigned + /* 0x9a */ kKeyNone, kKeyNone, // unassigned + /* 0x9b */ kKeyNone, kKeyNone, // unassigned + /* 0x9c */ kKeyNone, kKeyNone, // unassigned + /* 0x9d */ kKeyNone, kKeyNone, // unassigned + /* 0x9e */ kKeyNone, kKeyNone, // unassigned + /* 0x9f */ kKeyNone, kKeyNone, // unassigned + /* 0xa0 */ kKeyShift_L, kKeyShift_L, // VK_LSHIFT + /* 0xa1 */ kKeyShift_R, kKeyShift_R, // VK_RSHIFT + /* 0xa2 */ kKeyControl_L, kKeyControl_L, // VK_LCONTROL + /* 0xa3 */ kKeyControl_R, kKeyControl_R, // VK_RCONTROL + /* 0xa4 */ kKeyAlt_L, kKeyAlt_L, // VK_LMENU + /* 0xa5 */ kKeyAlt_R, kKeyAlt_R, // VK_RMENU + /* 0xa6 */ kKeyNone, kKeyWWWBack, // VK_BROWSER_BACK + /* 0xa7 */ kKeyNone, kKeyWWWForward, // VK_BROWSER_FORWARD + /* 0xa8 */ kKeyNone, kKeyWWWRefresh, // VK_BROWSER_REFRESH + /* 0xa9 */ kKeyNone, kKeyWWWStop, // VK_BROWSER_STOP + /* 0xaa */ kKeyNone, kKeyWWWSearch, // VK_BROWSER_SEARCH + /* 0xab */ kKeyNone, kKeyWWWFavorites, // VK_BROWSER_FAVORITES + /* 0xac */ kKeyNone, kKeyWWWHome, // VK_BROWSER_HOME + /* 0xad */ kKeyNone, kKeyAudioMute, // VK_VOLUME_MUTE + /* 0xae */ kKeyNone, kKeyAudioDown, // VK_VOLUME_DOWN + /* 0xaf */ kKeyNone, kKeyAudioUp, // VK_VOLUME_UP + /* 0xb0 */ kKeyNone, kKeyAudioNext, // VK_MEDIA_NEXT_TRACK + /* 0xb1 */ kKeyNone, kKeyAudioPrev, // VK_MEDIA_PREV_TRACK + /* 0xb2 */ kKeyNone, kKeyAudioStop, // VK_MEDIA_STOP + /* 0xb3 */ kKeyNone, kKeyAudioPlay, // VK_MEDIA_PLAY_PAUSE + /* 0xb4 */ kKeyNone, kKeyAppMail, // VK_LAUNCH_MAIL + /* 0xb5 */ kKeyNone, kKeyAppMedia, // VK_LAUNCH_MEDIA_SELECT + /* 0xb6 */ kKeyNone, kKeyAppUser1, // VK_LAUNCH_APP1 + /* 0xb7 */ kKeyNone, kKeyAppUser2, // VK_LAUNCH_APP2 + /* 0xb8 */ kKeyNone, kKeyNone, // unassigned + /* 0xb9 */ kKeyNone, kKeyNone, // unassigned + /* 0xba */ kKeyNone, kKeyNone, // OEM specific + /* 0xbb */ kKeyNone, kKeyNone, // OEM specific + /* 0xbc */ kKeyNone, kKeyNone, // OEM specific + /* 0xbd */ kKeyNone, kKeyNone, // OEM specific + /* 0xbe */ kKeyNone, kKeyNone, // OEM specific + /* 0xbf */ kKeyNone, kKeyNone, // OEM specific + /* 0xc0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xc1 */ kKeyNone, kKeyNone, // unassigned + /* 0xc2 */ kKeyNone, kKeyNone, // unassigned + /* 0xc3 */ kKeyNone, kKeyNone, // unassigned + /* 0xc4 */ kKeyNone, kKeyNone, // unassigned + /* 0xc5 */ kKeyNone, kKeyNone, // unassigned + /* 0xc6 */ kKeyNone, kKeyNone, // unassigned + /* 0xc7 */ kKeyNone, kKeyNone, // unassigned + /* 0xc8 */ kKeyNone, kKeyNone, // unassigned + /* 0xc9 */ kKeyNone, kKeyNone, // unassigned + /* 0xca */ kKeyNone, kKeyNone, // unassigned + /* 0xcb */ kKeyNone, kKeyNone, // unassigned + /* 0xcc */ kKeyNone, kKeyNone, // unassigned + /* 0xcd */ kKeyNone, kKeyNone, // unassigned + /* 0xce */ kKeyNone, kKeyNone, // unassigned + /* 0xcf */ kKeyNone, kKeyNone, // unassigned + /* 0xd0 */ kKeyNone, kKeyNone, // unassigned + /* 0xd1 */ kKeyNone, kKeyNone, // unassigned + /* 0xd2 */ kKeyNone, kKeyNone, // unassigned + /* 0xd3 */ kKeyNone, kKeyNone, // unassigned + /* 0xd4 */ kKeyNone, kKeyNone, // unassigned + /* 0xd5 */ kKeyNone, kKeyNone, // unassigned + /* 0xd6 */ kKeyNone, kKeyNone, // unassigned + /* 0xd7 */ kKeyNone, kKeyNone, // unassigned + /* 0xd8 */ kKeyNone, kKeyNone, // unassigned + /* 0xd9 */ kKeyNone, kKeyNone, // unassigned + /* 0xda */ kKeyNone, kKeyNone, // unassigned + /* 0xdb */ kKeyNone, kKeyNone, // OEM specific + /* 0xdc */ kKeyNone, kKeyNone, // OEM specific + /* 0xdd */ kKeyNone, kKeyNone, // OEM specific + /* 0xde */ kKeyNone, kKeyNone, // OEM specific + /* 0xdf */ kKeyNone, kKeyNone, // OEM specific + /* 0xe0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe1 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe2 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe3 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe4 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe5 */ kKeyNone, kKeyNone, // unassigned + /* 0xe6 */ kKeyNone, kKeyNone, // OEM specific + /* 0xe7 */ kKeyNone, kKeyNone, // unassigned + /* 0xe8 */ kKeyNone, kKeyNone, // unassigned + /* 0xe9 */ kKeyNone, kKeyNone, // OEM specific + /* 0xea */ kKeyNone, kKeyNone, // OEM specific + /* 0xeb */ kKeyNone, kKeyNone, // OEM specific + /* 0xec */ kKeyNone, kKeyNone, // OEM specific + /* 0xed */ kKeyNone, kKeyNone, // OEM specific + /* 0xee */ kKeyNone, kKeyNone, // OEM specific + /* 0xef */ kKeyNone, kKeyNone, // OEM specific + /* 0xf0 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf1 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf2 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf3 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf4 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf5 */ kKeyNone, kKeyNone, // OEM specific + /* 0xf6 */ kKeyNone, kKeyNone, // VK_ATTN + /* 0xf7 */ kKeyNone, kKeyNone, // VK_CRSEL + /* 0xf8 */ kKeyNone, kKeyNone, // VK_EXSEL + /* 0xf9 */ kKeyNone, kKeyNone, // VK_EREOF + /* 0xfa */ kKeyNone, kKeyNone, // VK_PLAY + /* 0xfb */ kKeyNone, kKeyNone, // VK_ZOOM + /* 0xfc */ kKeyNone, kKeyNone, // reserved + /* 0xfd */ kKeyNone, kKeyNone, // VK_PA1 + /* 0xfe */ kKeyNone, kKeyNone, // VK_OEM_CLEAR + /* 0xff */ kKeyNone, kKeyNone // reserved +}; + +// map special KeyID keys to virtual key codes. if the key is an +// extended key then the entry is the virtual key code | 0x100. +// unmapped keys have a 0 entry. +const KeyButton CMSWindowsKeyMapper::s_mapE000[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, + /* 0xa4 */ 0, 0, VK_BROWSER_BACK|0x100, VK_BROWSER_FORWARD|0x100, + /* 0xa8 */ VK_BROWSER_REFRESH|0x100, VK_BROWSER_STOP|0x100, + /* 0xaa */ VK_BROWSER_SEARCH|0x100, VK_BROWSER_FAVORITES|0x100, + /* 0xac */ VK_BROWSER_HOME|0x100, VK_VOLUME_MUTE|0x100, + /* 0xae */ VK_VOLUME_DOWN|0x100, VK_VOLUME_UP|0x100, + /* 0xb0 */ VK_MEDIA_NEXT_TRACK|0x100, VK_MEDIA_PREV_TRACK|0x100, + /* 0xb2 */ VK_MEDIA_STOP|0x100, VK_MEDIA_PLAY_PAUSE|0x100, + /* 0xb4 */ VK_LAUNCH_MAIL|0x100, VK_LAUNCH_MEDIA_SELECT|0x100, + /* 0xb6 */ VK_LAUNCH_APP1|0x100, VK_LAUNCH_APP2|0x100, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +const KeyButton CMSWindowsKeyMapper::s_mapEE00[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ VK_TAB, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, + /* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0, + /* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ VK_HOME|0x100, VK_LEFT|0x100, VK_UP|0x100, VK_RIGHT|0x100, + /* 0x54 */ VK_DOWN|0x100, VK_PRIOR|0x100, VK_NEXT|0x100, VK_END|0x100, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ VK_SELECT|0x100, VK_SNAPSHOT|0x100, VK_EXECUTE|0x100, VK_INSERT|0x100, + /* 0x64 */ 0, 0, 0, VK_APPS|0x100, + /* 0x68 */ 0, 0, VK_HELP|0x100, VK_CANCEL|0x100, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK|0x100, + /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN|0x100, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, + /* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, + /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, + /* 0xac */ VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100, + /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, + /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, + /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, + /* 0xc0 */ VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, + /* 0xc8 */ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, + /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, + /* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, 0, + /* 0xe8 */ 0, VK_LMENU, VK_RMENU|0x100, VK_LWIN|0x100, + /* 0xec */ VK_RWIN|0x100, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100 +}; + +CMSWindowsKeyMapper::CMSWindowsKeyMapper() +{ + // do nothing +} + +CMSWindowsKeyMapper::~CMSWindowsKeyMapper() +{ + // do nothing +} + +void +CMSWindowsKeyMapper::update(IKeyState* keyState) +{ + static const size_t numModifiers = sizeof(s_modifiers) / + sizeof(s_modifiers[0]); + + // clear shadow state + memset(m_keys, 0, sizeof(m_keys)); + + // add modifiers + if (keyState != NULL) { + for (size_t i = 0; i < numModifiers; ++i) { + IKeyState::KeyButtons keys; + for (size_t j = 0; j < CModifierKeys::s_maxKeys; ++j) { + if (s_modifiers[i].m_keys[j] != 0) { + keys.push_back(s_modifiers[i].m_keys[j]); + } + } + keyState->addModifier(s_modifiers[i].m_mask, keys); + } + } + + // save current state of modifiers + for (size_t i = 0; i < numModifiers; ++i) { + for (size_t j = 0; j < CModifierKeys::s_maxKeys; ++j) { + if (s_modifiers[i].m_keys[j] != 0) { + SHORT s = GetKeyState(s_modifiers[i].m_keys[j]); + m_keys[s_modifiers[i].m_keys[j]] = static_cast(s); + if (keyState != NULL) { + if ((s & 0x01) != 0) { + keyState->setToggled(s_modifiers[i].m_mask); + } + if ((s & 0x80) != 0) { + keyState->setKeyDown(s_modifiers[i].m_keys[j]); + } + } + } + } + } +} + +void +CMSWindowsKeyMapper::updateKey(KeyButton key, bool pressed) +{ + if (pressed) { + switch (key) { + case 0: + case VK_LBUTTON: + case VK_MBUTTON: + case VK_RBUTTON: + // ignore bogus key + break; + + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + m_keys[key] |= 0x80; + m_keys[VK_SHIFT] |= 0x80; + break; + + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + m_keys[key] |= 0x80; + m_keys[VK_CONTROL] |= 0x80; + break; + + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + m_keys[key] |= 0x80; + m_keys[VK_MENU] |= 0x80; + break; + + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // toggle keys + m_keys[key] |= 0x80; + break; + + default: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + m_keys[key] |= 0x80; + break; + } + + // special case: we detect ctrl+alt+del being pressed on some + // systems but we don't detect the release of those keys. so + // if ctrl, alt, and del are down then mark them up. + if ((m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0 && + (m_keys[VK_DELETE] & 0x80) != 0) { + m_keys[VK_LCONTROL] &= ~0x80; + m_keys[VK_RCONTROL] &= ~0x80; + m_keys[VK_CONTROL] &= ~0x80; + m_keys[VK_LMENU] &= ~0x80; + m_keys[VK_RMENU] &= ~0x80; + m_keys[VK_MENU] &= ~0x80; + m_keys[VK_DELETE] &= ~0x80; + } + } + else { + switch (key) { + case 0: + case VK_LBUTTON: + case VK_MBUTTON: + case VK_RBUTTON: + // ignore bogus key + break; + + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + m_keys[key] &= ~0x80; + if (((m_keys[VK_LSHIFT] | m_keys[VK_RSHIFT]) & 0x80) == 0) { + m_keys[VK_SHIFT] &= ~0x80; + } + break; + + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + m_keys[key] &= ~0x80; + if (((m_keys[VK_LCONTROL] | m_keys[VK_RCONTROL]) & 0x80) == 0) { + m_keys[VK_CONTROL] &= ~0x80; + } + break; + + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + m_keys[key] &= ~0x80; + if (((m_keys[VK_LMENU] | m_keys[VK_RMENU]) & 0x80) == 0) { + m_keys[VK_MENU] &= ~0x80; + } + break; + + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + // toggle keys + m_keys[key] &= ~0x80; + m_keys[key] ^= 0x01; + break; + + default: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + m_keys[key] &= ~0x80; + break; + } + } +} + +KeyButton +CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, + const IKeyState& keyState, KeyID id, + KeyModifierMask, bool isAutoRepeat) const +{ + KeyButton virtualKey = 0; + + // check for special keys + if ((id & 0xfffff000u) == 0xe000u) { + if ((id & 0xff00u) == 0xe000u) { + virtualKey = s_mapE000[id & 0xffu]; + } + else if ((id & 0xff00) == 0xee00) { + virtualKey = s_mapEE00[id & 0xffu]; + } + else if ((id & 0xff00) == 0xef00) { + virtualKey = s_mapEF00[id & 0xffu]; + } + if (virtualKey == 0) { + LOG((CLOG_DEBUG2 "unknown special key")); + return virtualKey; + } + } + +/* XXX + // special handling of VK_SNAPSHOT + if ((virtualKey & 0xff) == VK_SNAPSHOT) { + // ignore key repeats on print screen + if (!isAutoRepeat) { + // get event flags + DWORD flags = 0; + if (isExtendedKey(virtualKey)) { + flags |= KEYEVENTF_EXTENDEDKEY; + } + if (action != kPress) { + flags |= KEYEVENTF_KEYUP; + } + + // active window or fullscreen? + BYTE scan = 0; + if ((mask & KeyModifierAlt) == 0) { + scan = 1; + } + + // send event + keybd_event(static_cast(virtualKey & 0xff), scan, flags, 0); + } + return 0; + } +*/ + + // handle other special keys + if (virtualKey != 0) { + // compute required modifiers + KeyModifierMask requiredMask = 0; + KeyModifierMask outMask = 0; + + // strip out extended key flag + UINT virtualKey2 = (virtualKey & 0xffu); + + // check numeric keypad. note that virtual keys do not distinguish + // between the keypad and non-keypad movement keys. however, the + // virtual keys do distinguish between keypad numbers and operators + // (e.g. add, multiply) and their main keyboard counterparts. + // therefore, we can ignore the num-lock state for movement virtual + // keys but not for numeric keys. + if (virtualKey2 >= VK_NUMPAD0 && virtualKey2 <= VK_DIVIDE) { + requiredMask |= KeyModifierNumLock; + if (!keyState.isModifierActive(KeyModifierNumLock)) { + LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); + outMask |= KeyModifierNumLock; + } + } + + // check for left tab. that requires the shift key. + if (id == kKeyLeftTab) { + requiredMask |= KeyModifierShift; + outMask |= KeyModifierShift; + } + + // now generate the keystrokes and return the resulting modifier mask + LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d mask 0x%04x", id, virtualKey2, outMask)); + return mapToKeystrokes(keys, keyState, virtualKey, + outMask, requiredMask, isAutoRepeat); + } + + // determine the thread that'll receive this event + // FIXME -- we can't be sure we'll get the right thread here + HWND targetWindow = GetForegroundWindow(); + DWORD targetThread = GetWindowThreadProcessId(targetWindow, NULL); + + // figure out the code page for the target thread. i'm just + // guessing here. get the target thread's keyboard layout, + // extract the language id from that, and choose the code page + // based on that language. + HKL hkl = GetKeyboardLayout(targetThread); + LANGID langID = static_cast(LOWORD(hkl)); + UINT codePage = getCodePageFromLangID(langID); + LOG((CLOG_DEBUG2 "using code page %d and language id 0x%04x for thread 0x%08x", codePage, langID, targetThread)); + + // regular characters are complicated by dead keys. it may not be + // possible to generate a desired character directly. we may need + // to generate a dead key first then some other character. the + // app receiving the events will compose these two characters into + // a single precomposed character. + // + // as best as i can tell this is the simplest way to convert a + // character into its uncomposed version. along the way we'll + // discover if the key cannot be handled at all. we convert + // from wide char to multibyte, then from multibyte to wide char + // forcing conversion to composite characters, then from wide + // char back to multibyte without making precomposed characters. + // + // after the first conversion to multibyte we see if we can map + // the key. if so then we don't bother trying to decompose dead + // keys. + BOOL error; + char multiByte[2 * MB_LEN_MAX]; + wchar_t unicode[2]; + unicode[0] = static_cast(id & 0x0000ffffu); + int nChars = WideCharToMultiByte(codePage, + WC_COMPOSITECHECK | WC_DEFAULTCHAR, + unicode, 1, + multiByte, sizeof(multiByte), + NULL, &error); + if (nChars == 0 || error) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); + return 0; + } + virtualKey = mapCharacter(keys, keyState, multiByte[0], hkl, isAutoRepeat); + if (virtualKey != 0) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); + if ((MapVirtualKey(virtualKey, 2) & 0x80000000u) != 0) { + // it looks like this character is a dead key but + // MapVirtualKey() will claim it's a dead key even if it's + // not (though i don't think it ever claims it's not when + // it is). we need a backup test to ensure that this is + // really a dead key. we could use ToAscii() for this but + // that keeps state and it's a hassle to restore that state. + // OemKeyScan() appears to do the trick. if the character + // cannot be generated with a single keystroke then it + // returns 0xffffffff. + if (OemKeyScan(multiByte[0]) != 0xffffffffu) { + // character mapped to a dead key but we want the + // character for real so send a space key afterwards. + LOG((CLOG_DEBUG2 "character mapped to dead key")); + IKeyState::Keystroke keystroke; + keystroke.m_key = VK_SPACE; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + + // ignore the release of this key since we already + // handled it. + virtualKey = 0; + } + } + return virtualKey; + } + nChars = MultiByteToWideChar(codePage, + MB_COMPOSITE | MB_ERR_INVALID_CHARS, + multiByte, nChars, + unicode, 2); + if (nChars == 0) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x mb->wc mapping failed", id)); + return 0; + } + nChars = WideCharToMultiByte(codePage, + 0, + unicode, nChars, + multiByte, sizeof(multiByte), + NULL, &error); + if (nChars == 0 || error) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x wc->mb mapping failed", id)); + return 0; + } + + // we expect one or two characters in multiByte. if there are two + // then the *second* is a dead key. process the dead key if there. + // FIXME -- we assume each character is one byte here + if (nChars > 2) { + LOG((CLOG_DEBUG2 "multibyte characters not supported for character 0x%04x", id)); + return 0; + } + if (nChars == 2) { + LOG((CLOG_DEBUG2 "KeyID 0x%08x needs dead key %u", id, (unsigned char)multiByte[1])); + mapCharacter(keys, keyState, multiByte[1], hkl, isAutoRepeat); + } + + // process character + LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); + virtualKey = mapCharacter(keys, keyState, multiByte[0], hkl, isAutoRepeat); + + return virtualKey; +} + +KeyID +CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM vkCode, LPARAM info, + KeyModifierMask* maskOut, bool* altgr) const +{ + // note: known microsoft bugs + // Q72583 -- MapVirtualKey() maps keypad keys incorrectly + // 95,98: num pad vk code -> invalid scan code + // 95,98,NT4: num pad scan code -> bad vk code except + // SEPARATOR, MULTIPLY, SUBTRACT, ADD + + // get the scan code and the extended keyboard flag + UINT scanCode = static_cast((info & 0x00ff0000u) >> 16); + int extended = ((info & 0x01000000) == 0) ? 0 : 1; + LOG((CLOG_DEBUG1 "key vk=%d info=0x%08x ext=%d scan=%d", vkCode, info, extended, scanCode)); + + // handle some keys via table lookup + char c = 0; + KeyID id = s_virtualKey[vkCode][extended]; +LOG((CLOG_NOTE "code=%d, info=0x%08x -> id=%d", vkCode, info, id)); + if (id == kKeyNone) { + // not in table + + // save the control state then clear it. ToAscii() maps ctrl+letter + // to the corresponding control code and ctrl+backspace to delete. + // we don't want that translation so we clear the control modifier + // state. however, if we want to simulate AltGr (which is ctrl+alt) + // then we must not clear it. + BYTE keys[256]; + memcpy(keys, m_keys, sizeof(keys)); + BYTE control = keys[VK_CONTROL]; + BYTE menu = keys[VK_MENU]; + if ((control & 0x80) == 0 || (menu & 0x80) == 0) { + keys[VK_LCONTROL] = 0; + keys[VK_RCONTROL] = 0; + keys[VK_CONTROL] = 0; + } + else { + keys[VK_LCONTROL] = 0x80; + keys[VK_CONTROL] = 0x80; + keys[VK_LMENU] = 0x80; + keys[VK_MENU] = 0x80; + } + + // convert to ascii + WORD ascii; + int result = ToAscii(vkCode, scanCode, keys, &ascii, + ((menu & 0x80) == 0) ? 0 : 1); + + // if result is less than zero then it was a dead key. leave it + // there. + if (result < 0) { + id = kKeyMultiKey; + } + + // if result is 1 then the key was succesfully converted + else if (result == 1) { + c = static_cast(ascii & 0xff); + if (ascii >= 0x80) { + // character is not really ASCII. instead it's some + // character in the current ANSI code page. try to + // convert that to a Unicode character. if we fail + // then use the single byte character as is. + char src = c; + wchar_t unicode; + if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, + &src, 1, &unicode, 1) > 0) { + id = static_cast(unicode); + } + else { + id = static_cast(ascii & 0x00ff); + } + } + else { + id = static_cast(ascii & 0x00ff); + } + } + + // if result is 2 then a previous dead key could not be composed. + else if (result == 2) { + // if the two characters are the same and this is a key release + // then this event is the dead key being released. we put the + // dead key back in that case, otherwise we discard both key + // events because we can't compose the character. alternatively + // we could generate key events for both keys. + if (((ascii & 0xff00) >> 8) != (ascii & 0x00ff) || + (info & 0x80000000) == 0) { + // cannot compose key + return kKeyNone; + } + + // get the scan code of the dead key and the shift state + // required to generate it. + vkCode = VkKeyScan(static_cast(ascii & 0x00ff)); + + // set shift state required to generate key + BYTE keys[256]; + memset(keys, 0, sizeof(keys)); + if (vkCode & 0x0100) { + keys[VK_SHIFT] = 0x80; + } + if (vkCode & 0x0200) { + keys[VK_CONTROL] = 0x80; + } + if (vkCode & 0x0400) { + keys[VK_MENU] = 0x80; + } + + // strip shift state off of virtual key code + vkCode &= 0x00ff; + + // get the scan code for the key + scanCode = MapVirtualKey(vkCode, 0); + + // put it back + ToAscii(vkCode, scanCode, keys, &ascii, 0); + id = kKeyMultiKey; + } + } + + // set mask + bool needAltGr = false; + if (id != kKeyNone && id != kKeyMultiKey && c != 0) { + // note if key requires AltGr. VkKeyScan() can have a problem + // with some characters. there are two problems in particular. + // first, typing a dead key then pressing space will cause + // VkKeyScan() to return 0xffff. second, certain characters + // may map to multiple virtual keys and we might get the wrong + // one. if that happens then we might not get the right + // modifier mask. AltGr+9 on the french keyboard layout (^) + // has this problem. in the first case, we'll assume AltGr is + // required (only because it solves the problems we've seen + // so far). in the second, we'll use whatever the keyboard + // state says. + WORD virtualKeyAndModifierState = VkKeyScan(c); + if (virtualKeyAndModifierState == 0xffff) { + // there is no mapping. assume AltGr. + LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); + needAltGr = true; + } + else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { + // we didn't get the key that was actually pressed + LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); + if ((m_keys[VK_CONTROL] & 0x80) != 0 && + (m_keys[VK_MENU] & 0x80) != 0) { + needAltGr = true; + } + } + else { + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + if ((modifierState & 6) == 6) { + // key requires ctrl and alt == AltGr + needAltGr = true; + } + } + } + if (altgr != NULL) { + *altgr = needAltGr; + } + + // map modifier key + // map modifier key + KeyModifierMask mask = 0; + if (((m_keys[VK_LSHIFT] | + m_keys[VK_RSHIFT] | + m_keys[VK_SHIFT]) & 0x80) != 0) { + mask |= KeyModifierShift; + } + if (needAltGr) { + mask |= KeyModifierModeSwitch; + } + else { + if (((m_keys[VK_LCONTROL] | + m_keys[VK_RCONTROL] | + m_keys[VK_CONTROL]) & 0x80) != 0) { + mask |= KeyModifierControl; + } + if (((m_keys[VK_LMENU] | + m_keys[VK_RMENU] | + m_keys[VK_MENU]) & 0x80) != 0) { + mask |= KeyModifierAlt; + } + } + if (((m_keys[VK_LWIN] | + m_keys[VK_RWIN]) & 0x80) != 0) { + mask |= KeyModifierSuper; + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { + mask |= KeyModifierCapsLock; + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { + mask |= KeyModifierNumLock; + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { + mask |= KeyModifierScrollLock; + } + if (maskOut != NULL) { + *maskOut = mask; + } + + return id; +} + +bool +CMSWindowsKeyMapper::isPressed(KeyButton key) const +{ + return ((m_keys[key & 0xffu] & 0x80) != 0); +} + +UINT +CMSWindowsKeyMapper::keyToScanCode(KeyButton* virtualKey) const +{ + // try mapping given virtual key + UINT code = MapVirtualKey((*virtualKey) & 0xffu, 0); + if (code != 0) { + return code; + } + + // no dice. if the virtual key distinguishes between left/right + // then try the one that doesn't distinguish sides. windows (or + // keyboard drivers) are inconsistent in their treatment of these + // virtual keys. the following behaviors have been observed: + // + // win2k (gateway desktop): + // MapVirtualKey(vk, 0): + // VK_SHIFT == VK_LSHIFT != VK_RSHIFT + // VK_CONTROL == VK_LCONTROL == VK_RCONTROL + // VK_MENU == VK_LMENU == VK_RMENU + // MapVirtualKey(sc, 3): + // VK_LSHIFT and VK_RSHIFT mapped independently + // VK_LCONTROL is mapped but not VK_RCONTROL + // VK_LMENU is mapped but not VK_RMENU + // + // win me (sony vaio laptop): + // MapVirtualKey(vk, 0): + // VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped + // VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped + // VK_MENU mapped; VK_LMENU, VK_RMENU not mapped + // MapVirtualKey(sc, 3): + // all scan codes unmapped (function apparently unimplemented) + switch ((*virtualKey) & 0xffu) { + case VK_LSHIFT: + case VK_RSHIFT: + *virtualKey = VK_SHIFT; + return MapVirtualKey(VK_SHIFT, 0); + + case VK_LCONTROL: + case VK_RCONTROL: + *virtualKey = VK_CONTROL; + return MapVirtualKey(VK_CONTROL, 0); + + case VK_LMENU: + case VK_RMENU: + *virtualKey = VK_MENU; + return MapVirtualKey(VK_MENU, 0); + + default: + return 0; + } +} + +bool +CMSWindowsKeyMapper::isExtendedKey(KeyButton virtualKey) const +{ + // see if we've already encoded the extended flag + if ((virtualKey & 0x100u) != 0) { + return true; + } + + // check known virtual keys + switch (virtualKey & 0xffu) { + case VK_NUMLOCK: + case VK_RCONTROL: + case VK_RMENU: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: + return true; + + default: + return false; + } +} + +const char* +CMSWindowsKeyMapper::getKeyName(KeyButton key) const +{ + return s_vkToName[key & 0xffu]; +} + +UINT +CMSWindowsKeyMapper::getCodePageFromLangID(LANGID langid) const +{ + // construct a locale id from the language id + LCID lcid = MAKELCID(langid, SORT_DEFAULT); + + // get the ANSI code page for this locale + char data[6]; + if (GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, data, 6) == 0) { + // can't get code page + LOG((CLOG_DEBUG1 "can't find code page for langid 0x%04x", langid)); + return CP_ACP; + } + + // convert stringified code page into a number + UINT codePage = static_cast(atoi(data)); + if (codePage == 0) { + // parse failed + LOG((CLOG_DEBUG1 "can't parse code page %s for langid 0x%04x", data, langid)); + return CP_ACP; + } + + return codePage; +} + +KeyButton +CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, + const IKeyState& keyState, char c, HKL hkl, + bool isAutoRepeat) const +{ + // translate the character into its virtual key and its required + // modifier state. + SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); + + // get virtual key + KeyButton virtualKey = LOBYTE(virtualKeyAndModifierState); + if (virtualKey == 0xffu) { + LOG((CLOG_DEBUG2 "cannot map character %d", static_cast(c))); + return 0; + } + + // get the required modifier state + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + + // see what modifiers are needed. we only check for shift and + // AltGr. we must always match the desired shift state but only + // the desired AltGr state if AltGr is required. AltGr is actually + // ctrl + alt so we can't require that ctrl and alt not be pressed + // otherwise users couldn't do, say, ctrl+z.f + KeyModifierMask desiredMask = 0; + KeyModifierMask requiredMask = KeyModifierShift; + if ((modifierState & 0x01u) == 1) { + desiredMask |= KeyModifierShift; + } + if ((modifierState & 0x06u) == 6) { + // add ctrl and alt, which must be matched + desiredMask |= KeyModifierControl | KeyModifierAlt; + requiredMask |= KeyModifierControl | KeyModifierAlt; + } + + // handle combination of caps-lock and shift. if caps-lock is + // off locally then use shift as necessary. if caps-lock is on + // locally then it reverses the meaning of shift for keys that + // are subject to case conversion. + if (keyState.isModifierActive(KeyModifierCapsLock)) { + // there doesn't seem to be a simple way to test if a + // character respects the caps lock key. for normal + // characters it's easy enough but CharLower() and + // CharUpper() don't map dead keys even though they + // do respect caps lock for some unfathomable reason. + // first check the easy way. if that doesn't work + // then see if it's a dead key. + unsigned char uc = static_cast(c); + if (CharLower((LPTSTR)uc) != CharUpper((LPTSTR)uc) || + (MapVirtualKey(virtualKey, 2) & 0x80000000lu) != 0) { + LOG((CLOG_DEBUG2 "flip shift")); + desiredMask ^= KeyModifierShift; + } + } + + // now generate the keystrokes. ignore the resulting modifier + // mask since it can't have changed (because we don't call this + // method for modifier keys). + LOG((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%08x", (unsigned char)c, virtualKey, desiredMask)); + mapToKeystrokes(keys, keyState, virtualKey, + desiredMask, requiredMask, isAutoRepeat); + + return virtualKey; +} + +KeyButton +CMSWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, + const IKeyState& keyState, KeyButton virtualKey, + KeyModifierMask desiredMask, KeyModifierMask requiredMask, + bool isAutoRepeat) const +{ + // adjust the modifiers to match the desired modifiers + IKeyState::Keystrokes undo; + if (!adjustModifiers(keys, undo, keyState, desiredMask, requiredMask)) { + LOG((CLOG_DEBUG2 "failed to adjust modifiers")); + return 0; + } + + // add the key event + IKeyState::Keystroke keystroke; + keystroke.m_key = virtualKey; + keystroke.m_press = true; + keystroke.m_repeat = isAutoRepeat; + keys.push_back(keystroke); + + // put undo keystrokes at end of keystrokes in reverse order + while (!undo.empty()) { + keys.push_back(undo.back()); + undo.pop_back(); + } + + return virtualKey; +} + +bool +CMSWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, + IKeyState::Keystrokes& undo, + const IKeyState& keyState, + KeyModifierMask desiredMask, + KeyModifierMask requiredMask) const +{ + // for each modifier in requiredMask make sure the current state + // of that modifier matches the bit in desiredMask. + for (KeyModifierMask mask = 1u; requiredMask != 0; mask <<= 1) { + if ((mask & requiredMask) != 0) { + bool active = ((desiredMask & mask) != 0); + if (!keyState.mapModifier(keys, undo, mask, active)) { + return false; + } + requiredMask ^= mask; + } + } + + return true; +} diff --git a/lib/platform/CMSWindowsKeyMapper.h b/lib/platform/CMSWindowsKeyMapper.h new file mode 100644 index 00000000..ae26dcdb --- /dev/null +++ b/lib/platform/CMSWindowsKeyMapper.h @@ -0,0 +1,142 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSKEYMAPPER_H +#define CMSWINDOWSKEYMAPPER_H + +#include "IKeyState.h" +#define WIN32_LEAN_AND_MEAN +#include + +//! Microsoft Windows key mapper +/*! +This class maps KeyIDs to keystrokes. +*/ +class CMSWindowsKeyMapper { +public: + CMSWindowsKeyMapper(); + ~CMSWindowsKeyMapper(); + + //! @name manipulators + //@{ + + //! Update key mapper + /*! + Updates the key mapper's internal tables according to the + current keyboard mapping and updates \c keyState. + */ + void update(IKeyState* keyState); + + //! Update shadow key state + /*! + Updates the shadow keyboard state. + */ + void updateKey(KeyButton key, bool pressed); + + //@} + //! @name accessors + //@{ + + //! Map key press/repeat to keystrokes + /*! + Converts a press/repeat of key \c id with the modifiers as given + in \c desiredMask into the keystrokes necessary to synthesize + that key event. Returns the platform specific code of the key + being pressed, or 0 if the key cannot be mapped or \c isAutoRepeat + is true and the key does not auto-repeat. + */ + KeyButton mapKey(IKeyState::Keystrokes&, + const IKeyState& keyState, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + + //! Map key event to a key + /*! + Converts a key event into a KeyID and the shadow modifier state + to a modifier mask. If \c altgr is non-NULL it's set to true if + the key requires AltGr and false otherwise. + */ + KeyID mapKeyFromEvent(WPARAM vkCode, LPARAM info, + KeyModifierMask* maskOut, bool* altgr) const; + + //! Test shadow key state + /*! + Returns true iff the shadow state indicates the key is pressed. + */ + bool isPressed(KeyButton key) const; + + //! Map key to a scan code + /*! + Returns the scan code for \c key and possibly adjusts \c key. + */ + UINT keyToScanCode(KeyButton* key) const; + + //! Check for extended key + /*! + Returns true iff \c key is an extended key + */ + bool isExtendedKey(KeyButton key) const; + + //! Get name of key + /*! + Return a string describing the given key. + */ + const char* getKeyName(KeyButton) const; + + //@} + +private: + // convert a language ID to a code page + UINT getCodePageFromLangID(LANGID langid) const; + + // map character \c c given keyboard layout \c hkl to the keystrokes + // to generate it. + KeyButton mapCharacter(IKeyState::Keystrokes& keys, + const IKeyState& keyState, char c, HKL hkl, + bool isAutoRepeat) const; + + // map \c virtualKey to the keystrokes to generate it, along with + // keystrokes to update and restore the modifier state. + KeyButton mapToKeystrokes(IKeyState::Keystrokes& keys, + const IKeyState& keyState, KeyButton virtualKey, + KeyModifierMask desiredMask, + KeyModifierMask requiredMask, + bool isAutoRepeat) const; + + // get keystrokes to get modifiers in a desired state + bool adjustModifiers(IKeyState::Keystrokes& keys, + IKeyState::Keystrokes& undo, + const IKeyState& keyState, + KeyModifierMask desiredMask, + KeyModifierMask requiredMask) const; + +private: + class CModifierKeys { + public: + enum { s_maxKeys = 2 }; + KeyModifierMask m_mask; + KeyButton m_keys[s_maxKeys]; + }; + + BYTE m_keys[256]; + + static const CModifierKeys s_modifiers[]; + static const char* s_vkToName[]; + static const KeyID s_virtualKey[][2]; + static const KeyButton s_mapE000[]; + static const KeyButton s_mapEE00[]; + static const KeyButton s_mapEF00[]; +}; + +#endif diff --git a/lib/platform/CMSWindowsPrimaryScreen.cpp b/lib/platform/CMSWindowsPrimaryScreen.cpp deleted file mode 100644 index d68612dc..00000000 --- a/lib/platform/CMSWindowsPrimaryScreen.cpp +++ /dev/null @@ -1,1816 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CMSWindowsPrimaryScreen.h" -#include "CMSWindowsScreen.h" -#include "IPrimaryScreenReceiver.h" -#include "XScreen.h" -#include "CLog.h" -#include "CArch.h" -#include "CArchMiscWindows.h" -#include - -// X button stuff -#if !defined(WM_XBUTTONDOWN) -#define WM_XBUTTONDOWN 0x020B -#define WM_XBUTTONUP 0x020C -#define WM_XBUTTONDBLCLK 0x020D -#define WM_NCXBUTTONDOWN 0x00AB -#define WM_NCXBUTTONUP 0x00AC -#define WM_NCXBUTTONDBLCLK 0x00AD -#define MOUSEEVENTF_XDOWN 0x0100 -#define MOUSEEVENTF_XUP 0x0200 -#define XBUTTON1 0x0001 -#define XBUTTON2 0x0002 -#endif - -// -// map virtual key id to a name -// - -static const char* g_buttonToName[] = { - "button 0", - "Left Button", - "Middle Button", - "Right Button", - "X Button 1", - "X Button 2" -}; -static const char* g_vkToName[] = { - "vk 0x00", - "Left Button", - "Right Button", - "VK_CANCEL", - "Middle Button", - "vk 0x05", - "vk 0x06", - "vk 0x07", - "VK_BACK", - "VK_TAB", - "vk 0x0a", - "vk 0x0b", - "VK_CLEAR", - "VK_RETURN", - "vk 0x0e", - "vk 0x0f", - "VK_SHIFT", - "VK_CONTROL", - "VK_MENU", - "VK_PAUSE", - "VK_CAPITAL", - "VK_KANA", - "vk 0x16", - "VK_JUNJA", - "VK_FINAL", - "VK_KANJI", - "vk 0x1a", - "VK_ESCAPE", - "VK_CONVERT", - "VK_NONCONVERT", - "VK_ACCEPT", - "VK_MODECHANGE", - "VK_SPACE", - "VK_PRIOR", - "VK_NEXT", - "VK_END", - "VK_HOME", - "VK_LEFT", - "VK_UP", - "VK_RIGHT", - "VK_DOWN", - "VK_SELECT", - "VK_PRINT", - "VK_EXECUTE", - "VK_SNAPSHOT", - "VK_INSERT", - "VK_DELETE", - "VK_HELP", - "VK_0", - "VK_1", - "VK_2", - "VK_3", - "VK_4", - "VK_5", - "VK_6", - "VK_7", - "VK_8", - "VK_9", - "vk 0x3a", - "vk 0x3b", - "vk 0x3c", - "vk 0x3d", - "vk 0x3e", - "vk 0x3f", - "vk 0x40", - "VK_A", - "VK_B", - "VK_C", - "VK_D", - "VK_E", - "VK_F", - "VK_G", - "VK_H", - "VK_I", - "VK_J", - "VK_K", - "VK_L", - "VK_M", - "VK_N", - "VK_O", - "VK_P", - "VK_Q", - "VK_R", - "VK_S", - "VK_T", - "VK_U", - "VK_V", - "VK_W", - "VK_X", - "VK_Y", - "VK_Z", - "VK_LWIN", - "VK_RWIN", - "VK_APPS", - "vk 0x5e", - "vk 0x5f", - "VK_NUMPAD0", - "VK_NUMPAD1", - "VK_NUMPAD2", - "VK_NUMPAD3", - "VK_NUMPAD4", - "VK_NUMPAD5", - "VK_NUMPAD6", - "VK_NUMPAD7", - "VK_NUMPAD8", - "VK_NUMPAD9", - "VK_MULTIPLY", - "VK_ADD", - "VK_SEPARATOR", - "VK_SUBTRACT", - "VK_DECIMAL", - "VK_DIVIDE", - "VK_F1", - "VK_F2", - "VK_F3", - "VK_F4", - "VK_F5", - "VK_F6", - "VK_F7", - "VK_F8", - "VK_F9", - "VK_F10", - "VK_F11", - "VK_F12", - "VK_F13", - "VK_F14", - "VK_F15", - "VK_F16", - "VK_F17", - "VK_F18", - "VK_F19", - "VK_F20", - "VK_F21", - "VK_F22", - "VK_F23", - "VK_F24", - "vk 0x88", - "vk 0x89", - "vk 0x8a", - "vk 0x8b", - "vk 0x8c", - "vk 0x8d", - "vk 0x8e", - "vk 0x8f", - "VK_NUMLOCK", - "VK_SCROLL", - "vk 0x92", - "vk 0x93", - "vk 0x94", - "vk 0x95", - "vk 0x96", - "vk 0x97", - "vk 0x98", - "vk 0x99", - "vk 0x9a", - "vk 0x9b", - "vk 0x9c", - "vk 0x9d", - "vk 0x9e", - "vk 0x9f", - "VK_LSHIFT", - "VK_RSHIFT", - "VK_LCONTROL", - "VK_RCONTROL", - "VK_LMENU", - "VK_RMENU", - "VK_BROWSER_BACK", - "VK_BROWSER_FORWARD", - "VK_BROWSER_REFRESH", - "VK_BROWSER_STOP", - "VK_BROWSER_SEARCH", - "VK_BROWSER_FAVORITES", - "VK_BROWSER_HOME", - "VK_VOLUME_MUTE", - "VK_VOLUME_DOWN", - "VK_VOLUME_UP", - "VK_MEDIA_NEXT_TRACK", - "VK_MEDIA_PREV_TRACK", - "VK_MEDIA_STOP", - "VK_MEDIA_PLAY_PAUSE", - "VK_LAUNCH_MAIL", - "VK_LAUNCH_MEDIA_SELECT", - "VK_LAUNCH_APP1", - "VK_LAUNCH_APP2", - "vk 0xb8", - "vk 0xb9", - "vk 0xba", - "vk 0xbb", - "vk 0xbc", - "vk 0xbd", - "vk 0xbe", - "vk 0xbf", - "vk 0xc0", - "vk 0xc1", - "vk 0xc2", - "vk 0xc3", - "vk 0xc4", - "vk 0xc5", - "vk 0xc6", - "vk 0xc7", - "vk 0xc8", - "vk 0xc9", - "vk 0xca", - "vk 0xcb", - "vk 0xcc", - "vk 0xcd", - "vk 0xce", - "vk 0xcf", - "vk 0xd0", - "vk 0xd1", - "vk 0xd2", - "vk 0xd3", - "vk 0xd4", - "vk 0xd5", - "vk 0xd6", - "vk 0xd7", - "vk 0xd8", - "vk 0xd9", - "vk 0xda", - "vk 0xdb", - "vk 0xdc", - "vk 0xdd", - "vk 0xde", - "vk 0xdf", - "vk 0xe0", - "vk 0xe1", - "vk 0xe2", - "vk 0xe3", - "vk 0xe4", - "VK_PROCESSKEY", - "vk 0xe6", - "vk 0xe7", - "vk 0xe8", - "vk 0xe9", - "vk 0xea", - "vk 0xeb", - "vk 0xec", - "vk 0xed", - "vk 0xee", - "vk 0xef", - "vk 0xf0", - "vk 0xf1", - "vk 0xf2", - "vk 0xf3", - "vk 0xf4", - "vk 0xf5", - "VK_ATTN", - "VK_CRSEL", - "VK_EXSEL", - "VK_EREOF", - "VK_PLAY", - "VK_ZOOM", - "VK_NONAME", - "VK_PA1", - "VK_OEM_CLEAR", - "vk 0xff" -}; - -// -// CMSWindowsPrimaryScreen -// - -CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( - IScreenReceiver* receiver, - IPrimaryScreenReceiver* primaryReceiver) : - CPrimaryScreen(receiver), - m_receiver(primaryReceiver), - m_is95Family(CArchMiscWindows::isWindows95Family()), - m_threadID(0), - m_mark(0), - m_markReceived(0), - m_lowLevel(false), - m_cursorThread(0) -{ - assert(m_receiver != NULL); - - // load the hook library - m_hookLibrary = LoadLibrary("synrgyhk"); - if (m_hookLibrary == NULL) { - LOG((CLOG_ERR "Failed to load hook library; synrgyhk.dll is missing")); - throw XScreenOpenFailure(); - } - m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides"); - m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone"); - m_setRelay = (SetRelayFunc)GetProcAddress(m_hookLibrary, "setRelay"); - m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install"); - m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall"); - m_init = (InitFunc)GetProcAddress(m_hookLibrary, "init"); - m_cleanup = (CleanupFunc)GetProcAddress(m_hookLibrary, "cleanup"); - if (m_setSides == NULL || - m_setZone == NULL || - m_setRelay == NULL || - m_install == NULL || - m_uninstall == NULL || - m_init == NULL || - m_cleanup == NULL) { - LOG((CLOG_ERR "Invalid hook library; use a newer synrgyhk.dll")); - FreeLibrary(m_hookLibrary); - throw XScreenOpenFailure(); - } - - // create screen - m_screen = new CMSWindowsScreen(receiver, this); -} - -CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() -{ - assert(m_hookLibrary != NULL); - - delete m_screen; - FreeLibrary(m_hookLibrary); -} - -void -CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) -{ - m_setSides(activeSides); -} - -void -CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) -{ - // warp mouse - warpCursorNoFlush(x, y); - - // remove all input events before and including warp - MSG msg; - while (PeekMessage(&msg, NULL, SYNERGY_MSG_INPUT_FIRST, - SYNERGY_MSG_INPUT_LAST, PM_REMOVE)) { - // do nothing - } - - // save position as last position - m_x = x; - m_y = y; -} - -void -CMSWindowsPrimaryScreen::resetOptions() -{ - // no options -} - -void -CMSWindowsPrimaryScreen::setOptions(const COptionsList& /*options*/) -{ - // no options -} - -UInt32 -CMSWindowsPrimaryScreen::addOneShotTimer(double timeout) -{ - return m_screen->addOneShotTimer(timeout); -} - -KeyModifierMask -CMSWindowsPrimaryScreen::getToggleMask() const -{ - KeyModifierMask mask = 0; - - // get key state from our shadow state - if ((m_keys[VK_CAPITAL] & 0x01) != 0) - mask |= KeyModifierCapsLock; - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) - mask |= KeyModifierNumLock; - if ((m_keys[VK_SCROLL] & 0x01) != 0) - mask |= KeyModifierScrollLock; - - return mask; -} - -bool -CMSWindowsPrimaryScreen::isLockedToScreen() const -{ - // use shadow keyboard state in m_keys and m_buttons - for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { - if ((m_buttons[i] & 0x80) != 0) { - LOG((CLOG_DEBUG "locked by \"%s\"", g_buttonToName[i])); - return true; - } - } - for (UInt32 i = 0; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { - if ((m_keys[i] & 0x80) != 0) { - LOG((CLOG_DEBUG "locked by \"%s\"", g_vkToName[i])); - return true; - } - } - - // not locked - return false; -} - -IScreen* -CMSWindowsPrimaryScreen::getScreen() const -{ - return m_screen; -} - -void -CMSWindowsPrimaryScreen::onScreensaver(bool activated) -{ - m_receiver->onScreensaver(activated); -} - -bool -CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) -{ - assert(event != NULL); - - const MSG* msg = &event->m_msg; - - // check if windows key is up but we think it's down. if so then - // synthesize a key release for it. we have to do this because - // if the user presses and releases a windows key without pressing - // any other key when its down then windows will eat the key - // release. if we don't detect that an synthesize the release - // then the user will be locked to the screen and the client won't - // take the usual windows key release action (which on windows is - // to show the start menu). - // - // we can use GetKeyState() to check the state of the windows keys - // because, event though the key release is not reported to us, - // the event is processed and the keyboard state updated by the - // system. since the key could go up at any time we'll check the - // state on every event. only check on windows 95 family since - // NT family reports the key release as usual. obviously we skip - // this if the event is for the windows key itself. - if (m_is95Family) { - if ((m_keys[VK_LWIN] & 0x80) != 0 && - (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 && - !(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_LWIN)) { - // compute appropriate parameters for fake event - WPARAM wParam = VK_LWIN; - LPARAM lParam = 0xc1000000; - lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); - - // process as if it were a key up - bool altgr; - KeyModifierMask mask; - KeyButton button = static_cast( - (lParam & 0x00ff0000u) >> 16); - const KeyID key = mapKey(wParam, lParam, &mask, &altgr); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_receiver->onKeyUp(key, mask, button); - updateKey(wParam, false); - } - if ((m_keys[VK_RWIN] & 0x80) != 0 && - (GetAsyncKeyState(VK_RWIN) & 0x8000) == 0 && - !(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_RWIN)) { - // compute appropriate parameters for fake event - WPARAM wParam = VK_RWIN; - LPARAM lParam = 0xc1000000; - lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); - - // process as if it were a key up - bool altgr; - KeyModifierMask mask; - KeyButton button = static_cast( - (lParam & 0x00ff0000u) >> 16); - const KeyID key = mapKey(wParam, lParam, &mask, &altgr); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_receiver->onKeyUp(key, mask, button); - updateKey(wParam, false); - } - } - - // handle event - switch (msg->message) { - case SYNERGY_MSG_MARK: - m_markReceived = msg->wParam; - return true; - - case SYNERGY_MSG_KEY: - // ignore message if posted prior to last mark change - if (!ignore()) { - WPARAM wParam = msg->wParam; - LPARAM lParam = msg->lParam; - - // check for ctrl+alt+del emulation - if ((wParam == VK_PAUSE || wParam == VK_CANCEL) && - (m_keys[VK_CONTROL] & 0x80) != 0 && - (m_keys[VK_MENU] & 0x80) != 0) { - LOG((CLOG_DEBUG "emulate ctrl+alt+del")); - wParam = VK_DELETE; - lParam &= 0xffff0000; - lParam |= 0x00000001; - } - - // process key normally - bool altgr; - KeyModifierMask mask; - const KeyID key = mapKey(wParam, lParam, &mask, &altgr); - KeyButton button = static_cast( - (lParam & 0x00ff0000u) >> 16); - if (key != kKeyNone && key != kKeyMultiKey) { - if ((lParam & 0x80000000) == 0) { - // key press - - // if AltGr required for this key then make sure - // the ctrl and alt keys are *not* down on the - // client. windows simulates AltGr with ctrl and - // alt for some inexplicable reason and clients - // will get confused if they see mode switch and - // ctrl and alt. we'll also need to put ctrl and - // alt back the way there were after we simulate - // the key. - bool ctrlL = ((m_keys[VK_LCONTROL] & 0x80) != 0); - bool ctrlR = ((m_keys[VK_RCONTROL] & 0x80) != 0); - bool altL = ((m_keys[VK_LMENU] & 0x80) != 0); - bool altR = ((m_keys[VK_RMENU] & 0x80) != 0); - if (altgr) { - KeyID key; - KeyButton button; - KeyModifierMask mask2 = (mask & - ~(KeyModifierControl | - KeyModifierAlt | - KeyModifierModeSwitch)); - if (ctrlL) { - key = kKeyControl_L; - button = mapKeyToScanCode(VK_LCONTROL, VK_CONTROL); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_receiver->onKeyUp(key, mask2, button); - } - if (ctrlR) { - key = kKeyControl_R; - button = mapKeyToScanCode(VK_RCONTROL, VK_CONTROL); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_receiver->onKeyUp(key, mask2, button); - } - if (altL) { - key = kKeyAlt_L; - button = mapKeyToScanCode(VK_LMENU, VK_MENU); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_receiver->onKeyUp(key, mask2, button); - } - if (altR) { - key = kKeyAlt_R; - button = mapKeyToScanCode(VK_RMENU, VK_MENU); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_receiver->onKeyUp(key, mask2, button); - } - } - - // send key - const bool wasDown = ((lParam & 0x40000000) != 0); - SInt32 repeat = (SInt32)(lParam & 0xffff); - if (!wasDown) { - LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_receiver->onKeyDown(key, mask, button); - if (repeat > 0) { - --repeat; - } - } - if (repeat >= 1) { - LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button)); - m_receiver->onKeyRepeat(key, mask, repeat, button); - } - - // restore ctrl and alt state - if (altgr) { - KeyID key; - KeyButton button; - KeyModifierMask mask2 = (mask & - ~(KeyModifierControl | - KeyModifierAlt | - KeyModifierModeSwitch)); - if (ctrlL) { - key = kKeyControl_L; - button = mapKeyToScanCode(VK_LCONTROL, VK_CONTROL); - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_receiver->onKeyDown(key, mask2, button); - mask2 |= KeyModifierControl; - } - if (ctrlR) { - key = kKeyControl_R; - button = mapKeyToScanCode(VK_RCONTROL, VK_CONTROL); - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_receiver->onKeyDown(key, mask2, button); - mask2 |= KeyModifierControl; - } - if (altL) { - key = kKeyAlt_L; - button = mapKeyToScanCode(VK_LMENU, VK_MENU); - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_receiver->onKeyDown(key, mask2, button); - mask2 |= KeyModifierAlt; - } - if (altR) { - key = kKeyAlt_R; - button = mapKeyToScanCode(VK_RMENU, VK_MENU); - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_receiver->onKeyDown(key, mask2, button); - mask2 |= KeyModifierAlt; - } - } - } - else { - // key release. if the key isn't down according to - // our table then we never got the key press event - // for it. if it's not a modifier key then we'll - // synthesize the press first. only do this on - // the windows 95 family, which eats certain special - // keys like alt+tab, ctrl+esc, etc. - if (m_is95Family && !isModifier(msg->wParam) && - (m_keys[msg->wParam] & 0x80) == 0) { - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_receiver->onKeyDown(key, mask, button); - updateKey(msg->wParam, true); - } - - // do key up - LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_receiver->onKeyUp(key, mask, button); - } - } - else { - LOG((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", msg->wParam, msg->lParam)); - } - } - - // keep our shadow key state up to date - updateKey(msg->wParam, ((msg->lParam & 0x80000000) == 0)); - - return true; - - case SYNERGY_MSG_MOUSE_BUTTON: { - // get which button - bool pressed = false; - const ButtonID button = mapButton(msg->wParam, msg->lParam); - - // ignore message if posted prior to last mark change - if (!ignore()) { - switch (msg->wParam) { - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_XBUTTONDOWN: - case WM_LBUTTONDBLCLK: - case WM_MBUTTONDBLCLK: - case WM_RBUTTONDBLCLK: - case WM_XBUTTONDBLCLK: - case WM_NCLBUTTONDOWN: - case WM_NCMBUTTONDOWN: - case WM_NCRBUTTONDOWN: - case WM_NCXBUTTONDOWN: - case WM_NCLBUTTONDBLCLK: - case WM_NCMBUTTONDBLCLK: - case WM_NCRBUTTONDBLCLK: - case WM_NCXBUTTONDBLCLK: - LOG((CLOG_DEBUG1 "event: button press button=%d", button)); - if (button != kButtonNone) { - m_receiver->onMouseDown(button); - } - pressed = true; - break; - - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: - case WM_XBUTTONUP: - case WM_NCLBUTTONUP: - case WM_NCMBUTTONUP: - case WM_NCRBUTTONUP: - case WM_NCXBUTTONUP: - LOG((CLOG_DEBUG1 "event: button release button=%d", button)); - if (button != kButtonNone) { - m_receiver->onMouseUp(button); - } - pressed = false; - break; - } - } - - // keep our shadow key state up to date - if (button >= kButtonLeft && button <= kButtonExtra0 + 1) { - if (pressed) { - m_buttons[button] |= 0x80; - } - else { - m_buttons[button] &= ~0x80; - } - } - - return true; - } - - case SYNERGY_MSG_MOUSE_WHEEL: - // ignore message if posted prior to last mark change - if (!ignore()) { - LOG((CLOG_DEBUG1 "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); - m_receiver->onMouseWheel(msg->wParam); - } - return true; - - case SYNERGY_MSG_PRE_WARP: - { - // save position to compute delta of next motion - m_x = static_cast(msg->wParam); - m_y = static_cast(msg->lParam); - - // we warped the mouse. discard events until we find the - // matching post warp event. see warpCursorNoFlush() for - // where the events are sent. we discard the matching - // post warp event and can be sure we've skipped the warp - // event. - MSG msg; - do { - GetMessage(&msg, NULL, SYNERGY_MSG_MOUSE_MOVE, - SYNERGY_MSG_POST_WARP); - } while (msg.message != SYNERGY_MSG_POST_WARP); - - return true; - } - - case SYNERGY_MSG_POST_WARP: - LOG((CLOG_WARN "unmatched post warp")); - return true; - - case SYNERGY_MSG_MOUSE_MOVE: - // ignore message if posted prior to last mark change - if (!ignore()) { - // compute motion delta (relative to the last known - // mouse position) - SInt32 x = static_cast(msg->wParam) - m_x; - SInt32 y = static_cast(msg->lParam) - m_y; - - // save position to compute delta of next motion - m_x = static_cast(msg->wParam); - m_y = static_cast(msg->lParam); - - if (!isActive()) { - // motion on primary screen - if (x != 0 || y != 0) { - m_receiver->onMouseMovePrimary(m_x, m_y); - } - } - else { - // motion on secondary screen. warp mouse back to - // center. - if (x != 0 || y != 0) { - // back to center - warpCursorNoFlush(m_xCenter, m_yCenter); - - // examine the motion. if it's about the distance - // from the center of the screen to an edge then - // it's probably a bogus motion that we want to - // ignore (see warpCursorNoFlush() for a further - // description). - static SInt32 bogusZoneSize = 10; - SInt32 x0, y0, w0, h0; - m_screen->getShape(x0, y0, w0, h0); - if (-x + bogusZoneSize > m_xCenter - x0 || - x + bogusZoneSize > x0 + w0 - m_xCenter || - -y + bogusZoneSize > m_yCenter - y0 || - y + bogusZoneSize > y0 + h0 - m_yCenter) { - LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y)); - } - else { - // send motion - m_receiver->onMouseMoveSecondary(x, y); - } - } - } - } - return true; - } - - return false; -} - -bool -CMSWindowsPrimaryScreen::onEvent(CEvent* event) -{ - assert(event != NULL); - - const MSG& msg = event->m_msg; - switch (msg.message) { - case WM_DISPLAYCHANGE: - // recompute center pixel of primary screen - m_screen->getCursorCenter(m_xCenter, m_yCenter); - - // warp mouse to center if active - if (isActive()) { - warpCursorToCenter(); - } - - // tell hook about resize if not active - else { - SInt32 x, y, w, h; - m_screen->getShape(x, y, w, h); - m_setZone(x, y, w, h, getJumpZoneSize()); - } - return true; - } - - return false; -} - -void -CMSWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id) -{ - m_receiver->onOneShotTimerExpired(id); -} - -SInt32 -CMSWindowsPrimaryScreen::getJumpZoneSize() const -{ - return 1; -} - -void -CMSWindowsPrimaryScreen::postCreateWindow(HWND) -{ - // install hooks - switch (m_install()) { - case kHOOK_FAILED: - // FIXME -- can't install hook so we won't work; report error - m_lowLevel = false; - break; - - case kHOOK_OKAY: - m_lowLevel = false; - break; - - case kHOOK_OKAY_LL: - m_lowLevel = true; - break; - } - - if (!isActive()) { - // watch jump zones - m_setRelay(false); - - // all messages prior to now are invalid - nextMark(); - } -} - -void -CMSWindowsPrimaryScreen::preDestroyWindow(HWND) -{ - // uninstall hooks - m_uninstall(); -} - -void -CMSWindowsPrimaryScreen::onAccessibleDesktop() -{ - // get the current keyboard state - updateKeys(); -} - -void -CMSWindowsPrimaryScreen::onPreMainLoop() -{ - // must call mainLoop() from same thread as open() - assert(m_threadID == GetCurrentThreadId()); -} - -void -CMSWindowsPrimaryScreen::onPreOpen() -{ - // initialize hook library - m_threadID = GetCurrentThreadId(); - if (m_init(m_threadID) == 0) { - LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?")); - throw XScreenOpenFailure(); - } -} - -void -CMSWindowsPrimaryScreen::onPostOpen() -{ - // get cursor info - m_screen->getCursorPos(m_x, m_y); - m_screen->getCursorCenter(m_xCenter, m_yCenter); - - // set jump zones - SInt32 x, y, w, h; - m_screen->getShape(x, y, w, h); - m_setZone(x, y, w, h, getJumpZoneSize()); - - // initialize marks - m_mark = 0; - m_markReceived = 0; - nextMark(); -} - -void -CMSWindowsPrimaryScreen::onPostClose() -{ - m_cleanup(); - m_threadID = 0; -} - -void -CMSWindowsPrimaryScreen::onPreEnter() -{ - // show cursor if we hid it - if (m_cursorThread != 0) { - if (m_threadID != m_cursorThread) { - AttachThreadInput(m_threadID, m_cursorThread, TRUE); - } - ShowCursor(TRUE); - if (m_threadID != m_cursorThread) { - AttachThreadInput(m_threadID, m_cursorThread, FALSE); - } - m_cursorThread = 0; - } - - // enable ctrl+alt+del, alt+tab, etc - if (m_is95Family) { - DWORD dummy = 0; - SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0); - } - - // watch jump zones - m_setRelay(false); -} - -void -CMSWindowsPrimaryScreen::onPostEnter() -{ - // all messages prior to now are invalid - nextMark(); -} - -void -CMSWindowsPrimaryScreen::onPreLeave() -{ - // all messages prior to now are invalid - nextMark(); -} - -void -CMSWindowsPrimaryScreen::onPostLeave(bool success) -{ - if (success) { - // relay all mouse and keyboard events - m_setRelay(true); - - // disable ctrl+alt+del, alt+tab, etc - if (m_is95Family) { - DWORD dummy = 0; - SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); - } - - // hide the cursor if using low level hooks - if (m_lowLevel) { - HWND hwnd = GetForegroundWindow(); - m_cursorThread = GetWindowThreadProcessId(hwnd, NULL); - if (m_threadID != m_cursorThread) { - AttachThreadInput(m_threadID, m_cursorThread, TRUE); - } - ShowCursor(FALSE); - if (m_threadID != m_cursorThread) { - AttachThreadInput(m_threadID, m_cursorThread, FALSE); - } - } - } -} - -void -CMSWindowsPrimaryScreen::createWindow() -{ - // open the desktop and the window - m_window = m_screen->openDesktop(); - if (m_window == NULL) { - throw XScreenOpenFailure(); - } - - // we don't ever want our window to activate - EnableWindow(m_window, FALSE); -} - -void -CMSWindowsPrimaryScreen::destroyWindow() -{ - // close the desktop and the window - m_screen->closeDesktop(); -} - -bool -CMSWindowsPrimaryScreen::showWindow() -{ - // we don't need a window to capture input but we need a window - // to hide the cursor when using low-level hooks. do not try to - // take the activation; we want the currently active window to - // stay active. - if (m_lowLevel) { - SetWindowPos(m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1, - SWP_NOACTIVATE); - ShowWindow(m_window, SW_SHOWNA); - } - return true; -} - -void -CMSWindowsPrimaryScreen::hideWindow() -{ - // hide our window - if (m_lowLevel) { - ShowWindow(m_window, SW_HIDE); - } -} - -void -CMSWindowsPrimaryScreen::warpCursorToCenter() -{ - warpCursor(m_xCenter, m_yCenter); -} - -void -CMSWindowsPrimaryScreen::warpCursorNoFlush(SInt32 x, SInt32 y) -{ - // send an event that we can recognize before the mouse warp - PostThreadMessage(m_threadID, SYNERGY_MSG_PRE_WARP, x, y); - - // warp mouse. hopefully this inserts a mouse motion event - // between the previous message and the following message. - SetCursorPos(x, y); - - // yield the CPU. there's a race condition when warping: - // a hardware mouse event occurs - // the mouse hook is not called because that process doesn't have the CPU - // we send PRE_WARP, SetCursorPos(), send POST_WARP - // we process all of those events and update m_x, m_y - // we finish our time slice - // the hook is called - // the hook sends us a mouse event from the pre-warp position - // we get the CPU - // we compute a bogus warp - // we need the hook to process all mouse events that occur - // before we warp before we do the warp but i'm not sure how - // to guarantee that. yielding the CPU here may reduce the - // chance of undesired behavior. we'll also check for very - // large motions that look suspiciously like about half width - // or height of the screen. - ARCH->sleep(0.0); - - // send an event that we can recognize after the mouse warp - PostThreadMessage(m_threadID, SYNERGY_MSG_POST_WARP, 0, 0); -} - -void -CMSWindowsPrimaryScreen::nextMark() -{ - // next mark - ++m_mark; - - // mark point in message queue where the mark was changed - PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); -} - -bool -CMSWindowsPrimaryScreen::ignore() const -{ - return (m_mark != m_markReceived); -} - -// map virtual keys to synergy key enumeration. use extended keyboard -// bit to distinguish some keys. -static const KeyID g_virtualKey[][2] = -{ - /* 0x00 */ kKeyNone, kKeyNone, // reserved - /* 0x01 */ kKeyNone, kKeyNone, // VK_LBUTTON - /* 0x02 */ kKeyNone, kKeyNone, // VK_RBUTTON - /* 0x03 */ kKeyNone, kKeyBreak, // VK_CANCEL - /* 0x04 */ kKeyNone, kKeyNone, // VK_MBUTTON - /* 0x05 */ kKeyNone, kKeyNone, // undefined - /* 0x06 */ kKeyNone, kKeyNone, // undefined - /* 0x07 */ kKeyNone, kKeyNone, // undefined - /* 0x08 */ kKeyBackSpace, kKeyNone, // VK_BACK - /* 0x09 */ kKeyTab, kKeyNone, // VK_TAB - /* 0x0a */ kKeyNone, kKeyNone, // undefined - /* 0x0b */ kKeyNone, kKeyNone, // undefined - /* 0x0c */ kKeyClear, kKeyClear, // VK_CLEAR - /* 0x0d */ kKeyReturn, kKeyKP_Enter, // VK_RETURN - /* 0x0e */ kKeyNone, kKeyNone, // undefined - /* 0x0f */ kKeyNone, kKeyNone, // undefined - /* 0x10 */ kKeyShift_L, kKeyShift_R, // VK_SHIFT - /* 0x11 */ kKeyControl_L, kKeyControl_R, // VK_CONTROL - /* 0x12 */ kKeyAlt_L, kKeyAlt_R, // VK_MENU - /* 0x13 */ kKeyPause, kKeyNone, // VK_PAUSE - /* 0x14 */ kKeyCapsLock, kKeyNone, // VK_CAPITAL - /* 0x15 */ kKeyNone, kKeyNone, // VK_KANA - /* 0x16 */ kKeyNone, kKeyNone, // VK_HANGUL - /* 0x17 */ kKeyNone, kKeyNone, // VK_JUNJA - /* 0x18 */ kKeyNone, kKeyNone, // VK_FINAL - /* 0x19 */ kKeyNone, kKeyNone, // VK_KANJI - /* 0x1a */ kKeyNone, kKeyNone, // undefined - /* 0x1b */ kKeyEscape, kKeyNone, // VK_ESCAPE - /* 0x1c */ kKeyNone, kKeyNone, // VK_CONVERT - /* 0x1d */ kKeyNone, kKeyNone, // VK_NONCONVERT - /* 0x1e */ kKeyNone, kKeyNone, // VK_ACCEPT - /* 0x1f */ kKeyNone, kKeyNone, // VK_MODECHANGE - /* 0x20 */ kKeyNone, kKeyNone, // VK_SPACE - /* 0x21 */ kKeyKP_PageUp, kKeyPageUp, // VK_PRIOR - /* 0x22 */ kKeyKP_PageDown, kKeyPageDown, // VK_NEXT - /* 0x23 */ kKeyKP_End, kKeyEnd, // VK_END - /* 0x24 */ kKeyKP_Home, kKeyHome, // VK_HOME - /* 0x25 */ kKeyKP_Left, kKeyLeft, // VK_LEFT - /* 0x26 */ kKeyKP_Up, kKeyUp, // VK_UP - /* 0x27 */ kKeyKP_Right, kKeyRight, // VK_RIGHT - /* 0x28 */ kKeyKP_Down, kKeyDown, // VK_DOWN - /* 0x29 */ kKeySelect, kKeySelect, // VK_SELECT - /* 0x2a */ kKeyNone, kKeyNone, // VK_PRINT - /* 0x2b */ kKeyExecute, kKeyExecute, // VK_EXECUTE - /* 0x2c */ kKeyPrint, kKeyPrint, // VK_SNAPSHOT - /* 0x2d */ kKeyKP_Insert, kKeyInsert, // VK_INSERT - /* 0x2e */ kKeyKP_Delete, kKeyDelete, // VK_DELETE - /* 0x2f */ kKeyHelp, kKeyHelp, // VK_HELP - /* 0x30 */ kKeyNone, kKeyNone, // VK_0 - /* 0x31 */ kKeyNone, kKeyNone, // VK_1 - /* 0x32 */ kKeyNone, kKeyNone, // VK_2 - /* 0x33 */ kKeyNone, kKeyNone, // VK_3 - /* 0x34 */ kKeyNone, kKeyNone, // VK_4 - /* 0x35 */ kKeyNone, kKeyNone, // VK_5 - /* 0x36 */ kKeyNone, kKeyNone, // VK_6 - /* 0x37 */ kKeyNone, kKeyNone, // VK_7 - /* 0x38 */ kKeyNone, kKeyNone, // VK_8 - /* 0x39 */ kKeyNone, kKeyNone, // VK_9 - /* 0x3a */ kKeyNone, kKeyNone, // undefined - /* 0x3b */ kKeyNone, kKeyNone, // undefined - /* 0x3c */ kKeyNone, kKeyNone, // undefined - /* 0x3d */ kKeyNone, kKeyNone, // undefined - /* 0x3e */ kKeyNone, kKeyNone, // undefined - /* 0x3f */ kKeyNone, kKeyNone, // undefined - /* 0x40 */ kKeyNone, kKeyNone, // undefined - /* 0x41 */ kKeyNone, kKeyNone, // VK_A - /* 0x42 */ kKeyNone, kKeyNone, // VK_B - /* 0x43 */ kKeyNone, kKeyNone, // VK_C - /* 0x44 */ kKeyNone, kKeyNone, // VK_D - /* 0x45 */ kKeyNone, kKeyNone, // VK_E - /* 0x46 */ kKeyNone, kKeyNone, // VK_F - /* 0x47 */ kKeyNone, kKeyNone, // VK_G - /* 0x48 */ kKeyNone, kKeyNone, // VK_H - /* 0x49 */ kKeyNone, kKeyNone, // VK_I - /* 0x4a */ kKeyNone, kKeyNone, // VK_J - /* 0x4b */ kKeyNone, kKeyNone, // VK_K - /* 0x4c */ kKeyNone, kKeyNone, // VK_L - /* 0x4d */ kKeyNone, kKeyNone, // VK_M - /* 0x4e */ kKeyNone, kKeyNone, // VK_N - /* 0x4f */ kKeyNone, kKeyNone, // VK_O - /* 0x50 */ kKeyNone, kKeyNone, // VK_P - /* 0x51 */ kKeyNone, kKeyNone, // VK_Q - /* 0x52 */ kKeyNone, kKeyNone, // VK_R - /* 0x53 */ kKeyNone, kKeyNone, // VK_S - /* 0x54 */ kKeyNone, kKeyNone, // VK_T - /* 0x55 */ kKeyNone, kKeyNone, // VK_U - /* 0x56 */ kKeyNone, kKeyNone, // VK_V - /* 0x57 */ kKeyNone, kKeyNone, // VK_W - /* 0x58 */ kKeyNone, kKeyNone, // VK_X - /* 0x59 */ kKeyNone, kKeyNone, // VK_Y - /* 0x5a */ kKeyNone, kKeyNone, // VK_Z - /* 0x5b */ kKeyNone, kKeySuper_L, // VK_LWIN - /* 0x5c */ kKeyNone, kKeySuper_R, // VK_RWIN - /* 0x5d */ kKeyMenu, kKeyMenu, // VK_APPS - /* 0x5e */ kKeyNone, kKeyNone, // undefined - /* 0x5f */ kKeyNone, kKeyNone, // undefined - /* 0x60 */ kKeyKP_0, kKeyNone, // VK_NUMPAD0 - /* 0x61 */ kKeyKP_1, kKeyNone, // VK_NUMPAD1 - /* 0x62 */ kKeyKP_2, kKeyNone, // VK_NUMPAD2 - /* 0x63 */ kKeyKP_3, kKeyNone, // VK_NUMPAD3 - /* 0x64 */ kKeyKP_4, kKeyNone, // VK_NUMPAD4 - /* 0x65 */ kKeyKP_5, kKeyNone, // VK_NUMPAD5 - /* 0x66 */ kKeyKP_6, kKeyNone, // VK_NUMPAD6 - /* 0x67 */ kKeyKP_7, kKeyNone, // VK_NUMPAD7 - /* 0x68 */ kKeyKP_8, kKeyNone, // VK_NUMPAD8 - /* 0x69 */ kKeyKP_9, kKeyNone, // VK_NUMPAD9 - /* 0x6a */ kKeyKP_Multiply, kKeyNone, // VK_MULTIPLY - /* 0x6b */ kKeyKP_Add, kKeyNone, // VK_ADD - /* 0x6c */ kKeyKP_Separator,kKeyKP_Separator,// VK_SEPARATOR - /* 0x6d */ kKeyKP_Subtract, kKeyNone, // VK_SUBTRACT - /* 0x6e */ kKeyKP_Decimal, kKeyNone, // VK_DECIMAL - /* 0x6f */ kKeyNone, kKeyKP_Divide, // VK_DIVIDE - /* 0x70 */ kKeyF1, kKeyNone, // VK_F1 - /* 0x71 */ kKeyF2, kKeyNone, // VK_F2 - /* 0x72 */ kKeyF3, kKeyNone, // VK_F3 - /* 0x73 */ kKeyF4, kKeyNone, // VK_F4 - /* 0x74 */ kKeyF5, kKeyNone, // VK_F5 - /* 0x75 */ kKeyF6, kKeyNone, // VK_F6 - /* 0x76 */ kKeyF7, kKeyNone, // VK_F7 - /* 0x77 */ kKeyF8, kKeyNone, // VK_F8 - /* 0x78 */ kKeyF9, kKeyNone, // VK_F9 - /* 0x79 */ kKeyF10, kKeyNone, // VK_F10 - /* 0x7a */ kKeyF11, kKeyNone, // VK_F11 - /* 0x7b */ kKeyF12, kKeyNone, // VK_F12 - /* 0x7c */ kKeyF13, kKeyF13, // VK_F13 - /* 0x7d */ kKeyF14, kKeyF14, // VK_F14 - /* 0x7e */ kKeyF15, kKeyF15, // VK_F15 - /* 0x7f */ kKeyF16, kKeyF16, // VK_F16 - /* 0x80 */ kKeyF17, kKeyF17, // VK_F17 - /* 0x81 */ kKeyF18, kKeyF18, // VK_F18 - /* 0x82 */ kKeyF19, kKeyF19, // VK_F19 - /* 0x83 */ kKeyF20, kKeyF20, // VK_F20 - /* 0x84 */ kKeyF21, kKeyF21, // VK_F21 - /* 0x85 */ kKeyF22, kKeyF22, // VK_F22 - /* 0x86 */ kKeyF23, kKeyF23, // VK_F23 - /* 0x87 */ kKeyF24, kKeyF24, // VK_F24 - /* 0x88 */ kKeyNone, kKeyNone, // unassigned - /* 0x89 */ kKeyNone, kKeyNone, // unassigned - /* 0x8a */ kKeyNone, kKeyNone, // unassigned - /* 0x8b */ kKeyNone, kKeyNone, // unassigned - /* 0x8c */ kKeyNone, kKeyNone, // unassigned - /* 0x8d */ kKeyNone, kKeyNone, // unassigned - /* 0x8e */ kKeyNone, kKeyNone, // unassigned - /* 0x8f */ kKeyNone, kKeyNone, // unassigned - /* 0x90 */ kKeyNumLock, kKeyNumLock, // VK_NUMLOCK - /* 0x91 */ kKeyScrollLock, kKeyNone, // VK_SCROLL - /* 0x92 */ kKeyNone, kKeyNone, // unassigned - /* 0x93 */ kKeyNone, kKeyNone, // unassigned - /* 0x94 */ kKeyNone, kKeyNone, // unassigned - /* 0x95 */ kKeyNone, kKeyNone, // unassigned - /* 0x96 */ kKeyNone, kKeyNone, // unassigned - /* 0x97 */ kKeyNone, kKeyNone, // unassigned - /* 0x98 */ kKeyNone, kKeyNone, // unassigned - /* 0x99 */ kKeyNone, kKeyNone, // unassigned - /* 0x9a */ kKeyNone, kKeyNone, // unassigned - /* 0x9b */ kKeyNone, kKeyNone, // unassigned - /* 0x9c */ kKeyNone, kKeyNone, // unassigned - /* 0x9d */ kKeyNone, kKeyNone, // unassigned - /* 0x9e */ kKeyNone, kKeyNone, // unassigned - /* 0x9f */ kKeyNone, kKeyNone, // unassigned - /* 0xa0 */ kKeyShift_L, kKeyShift_L, // VK_LSHIFT - /* 0xa1 */ kKeyShift_R, kKeyShift_R, // VK_RSHIFT - /* 0xa2 */ kKeyControl_L, kKeyControl_L, // VK_LCONTROL - /* 0xa3 */ kKeyControl_R, kKeyControl_R, // VK_RCONTROL - /* 0xa4 */ kKeyAlt_L, kKeyAlt_L, // VK_LMENU - /* 0xa5 */ kKeyAlt_R, kKeyAlt_R, // VK_RMENU - /* 0xa6 */ kKeyNone, kKeyWWWBack, // VK_BROWSER_BACK - /* 0xa7 */ kKeyNone, kKeyWWWForward, // VK_BROWSER_FORWARD - /* 0xa8 */ kKeyNone, kKeyWWWRefresh, // VK_BROWSER_REFRESH - /* 0xa9 */ kKeyNone, kKeyWWWStop, // VK_BROWSER_STOP - /* 0xaa */ kKeyNone, kKeyWWWSearch, // VK_BROWSER_SEARCH - /* 0xab */ kKeyNone, kKeyWWWFavorites, // VK_BROWSER_FAVORITES - /* 0xac */ kKeyNone, kKeyWWWHome, // VK_BROWSER_HOME - /* 0xad */ kKeyNone, kKeyAudioMute, // VK_VOLUME_MUTE - /* 0xae */ kKeyNone, kKeyAudioDown, // VK_VOLUME_DOWN - /* 0xaf */ kKeyNone, kKeyAudioUp, // VK_VOLUME_UP - /* 0xb0 */ kKeyNone, kKeyAudioNext, // VK_MEDIA_NEXT_TRACK - /* 0xb1 */ kKeyNone, kKeyAudioPrev, // VK_MEDIA_PREV_TRACK - /* 0xb2 */ kKeyNone, kKeyAudioStop, // VK_MEDIA_STOP - /* 0xb3 */ kKeyNone, kKeyAudioPlay, // VK_MEDIA_PLAY_PAUSE - /* 0xb4 */ kKeyNone, kKeyAppMail, // VK_LAUNCH_MAIL - /* 0xb5 */ kKeyNone, kKeyAppMedia, // VK_LAUNCH_MEDIA_SELECT - /* 0xb6 */ kKeyNone, kKeyAppUser1, // VK_LAUNCH_APP1 - /* 0xb7 */ kKeyNone, kKeyAppUser2, // VK_LAUNCH_APP2 - /* 0xb8 */ kKeyNone, kKeyNone, // unassigned - /* 0xb9 */ kKeyNone, kKeyNone, // unassigned - /* 0xba */ kKeyNone, kKeyNone, // OEM specific - /* 0xbb */ kKeyNone, kKeyNone, // OEM specific - /* 0xbc */ kKeyNone, kKeyNone, // OEM specific - /* 0xbd */ kKeyNone, kKeyNone, // OEM specific - /* 0xbe */ kKeyNone, kKeyNone, // OEM specific - /* 0xbf */ kKeyNone, kKeyNone, // OEM specific - /* 0xc0 */ kKeyNone, kKeyNone, // OEM specific - /* 0xc1 */ kKeyNone, kKeyNone, // unassigned - /* 0xc2 */ kKeyNone, kKeyNone, // unassigned - /* 0xc3 */ kKeyNone, kKeyNone, // unassigned - /* 0xc4 */ kKeyNone, kKeyNone, // unassigned - /* 0xc5 */ kKeyNone, kKeyNone, // unassigned - /* 0xc6 */ kKeyNone, kKeyNone, // unassigned - /* 0xc7 */ kKeyNone, kKeyNone, // unassigned - /* 0xc8 */ kKeyNone, kKeyNone, // unassigned - /* 0xc9 */ kKeyNone, kKeyNone, // unassigned - /* 0xca */ kKeyNone, kKeyNone, // unassigned - /* 0xcb */ kKeyNone, kKeyNone, // unassigned - /* 0xcc */ kKeyNone, kKeyNone, // unassigned - /* 0xcd */ kKeyNone, kKeyNone, // unassigned - /* 0xce */ kKeyNone, kKeyNone, // unassigned - /* 0xcf */ kKeyNone, kKeyNone, // unassigned - /* 0xd0 */ kKeyNone, kKeyNone, // unassigned - /* 0xd1 */ kKeyNone, kKeyNone, // unassigned - /* 0xd2 */ kKeyNone, kKeyNone, // unassigned - /* 0xd3 */ kKeyNone, kKeyNone, // unassigned - /* 0xd4 */ kKeyNone, kKeyNone, // unassigned - /* 0xd5 */ kKeyNone, kKeyNone, // unassigned - /* 0xd6 */ kKeyNone, kKeyNone, // unassigned - /* 0xd7 */ kKeyNone, kKeyNone, // unassigned - /* 0xd8 */ kKeyNone, kKeyNone, // unassigned - /* 0xd9 */ kKeyNone, kKeyNone, // unassigned - /* 0xda */ kKeyNone, kKeyNone, // unassigned - /* 0xdb */ kKeyNone, kKeyNone, // OEM specific - /* 0xdc */ kKeyNone, kKeyNone, // OEM specific - /* 0xdd */ kKeyNone, kKeyNone, // OEM specific - /* 0xde */ kKeyNone, kKeyNone, // OEM specific - /* 0xdf */ kKeyNone, kKeyNone, // OEM specific - /* 0xe0 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe1 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe2 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe3 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe4 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe5 */ kKeyNone, kKeyNone, // unassigned - /* 0xe6 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe7 */ kKeyNone, kKeyNone, // unassigned - /* 0xe8 */ kKeyNone, kKeyNone, // unassigned - /* 0xe9 */ kKeyNone, kKeyNone, // OEM specific - /* 0xea */ kKeyNone, kKeyNone, // OEM specific - /* 0xeb */ kKeyNone, kKeyNone, // OEM specific - /* 0xec */ kKeyNone, kKeyNone, // OEM specific - /* 0xed */ kKeyNone, kKeyNone, // OEM specific - /* 0xee */ kKeyNone, kKeyNone, // OEM specific - /* 0xef */ kKeyNone, kKeyNone, // OEM specific - /* 0xf0 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf1 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf2 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf3 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf4 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf5 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf6 */ kKeyNone, kKeyNone, // VK_ATTN - /* 0xf7 */ kKeyNone, kKeyNone, // VK_CRSEL - /* 0xf8 */ kKeyNone, kKeyNone, // VK_EXSEL - /* 0xf9 */ kKeyNone, kKeyNone, // VK_EREOF - /* 0xfa */ kKeyNone, kKeyNone, // VK_PLAY - /* 0xfb */ kKeyNone, kKeyNone, // VK_ZOOM - /* 0xfc */ kKeyNone, kKeyNone, // reserved - /* 0xfd */ kKeyNone, kKeyNone, // VK_PA1 - /* 0xfe */ kKeyNone, kKeyNone, // VK_OEM_CLEAR - /* 0xff */ kKeyNone, kKeyNone // reserved -}; - -KeyID -CMSWindowsPrimaryScreen::mapKey( - WPARAM vkCode, - LPARAM info, - KeyModifierMask* maskOut, - bool* altgr) -{ - // note: known microsoft bugs - // Q72583 -- MapVirtualKey() maps keypad keys incorrectly - // 95,98: num pad vk code -> invalid scan code - // 95,98,NT4: num pad scan code -> bad vk code except - // SEPARATOR, MULTIPLY, SUBTRACT, ADD - - assert(maskOut != NULL); - assert(altgr != NULL); - - // get the scan code and the extended keyboard flag - UINT scanCode = static_cast((info & 0x00ff0000u) >> 16); - int extended = ((info & 0x01000000) == 0) ? 0 : 1; - LOG((CLOG_DEBUG1 "key vk=%d info=0x%08x ext=%d scan=%d", vkCode, info, extended, scanCode)); - - // handle some keys via table lookup - char c = 0; - KeyID id = g_virtualKey[vkCode][extended]; - if (id == kKeyNone) { - // not in table - - // save the control state then clear it. ToAscii() maps ctrl+letter - // to the corresponding control code and ctrl+backspace to delete. - // we don't want that translation so we clear the control modifier - // state. however, if we want to simulate AltGr (which is ctrl+alt) - // then we must not clear it. - BYTE lControl = m_keys[VK_LCONTROL]; - BYTE rControl = m_keys[VK_RCONTROL]; - BYTE control = m_keys[VK_CONTROL]; - BYTE lMenu = m_keys[VK_LMENU]; - BYTE menu = m_keys[VK_MENU]; - if ((control & 0x80) == 0 || (menu & 0x80) == 0) { - m_keys[VK_LCONTROL] = 0; - m_keys[VK_RCONTROL] = 0; - m_keys[VK_CONTROL] = 0; - } - else { - m_keys[VK_LCONTROL] = 0x80; - m_keys[VK_CONTROL] = 0x80; - m_keys[VK_LMENU] = 0x80; - m_keys[VK_MENU] = 0x80; - } - - // convert to ascii - WORD ascii; - int result = ToAscii(vkCode, scanCode, m_keys, &ascii, - ((menu & 0x80) == 0) ? 0 : 1); - - // restore control state - m_keys[VK_LCONTROL] = lControl; - m_keys[VK_RCONTROL] = rControl; - m_keys[VK_CONTROL] = control; - m_keys[VK_LMENU] = lMenu; - m_keys[VK_MENU] = menu; - - // if result is less than zero then it was a dead key. leave it - // there. - if (result < 0) { - id = kKeyMultiKey; - } - - // if result is 1 then the key was succesfully converted - else if (result == 1) { - c = static_cast(ascii & 0xff); - if (ascii >= 0x80) { - // character is not really ASCII. instead it's some - // character in the current ANSI code page. try to - // convert that to a Unicode character. if we fail - // then use the single byte character as is. - char src = c; - wchar_t unicode; - if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, - &src, 1, &unicode, 1) > 0) { - id = static_cast(unicode); - } - else { - id = static_cast(ascii & 0x00ff); - } - } - else { - id = static_cast(ascii & 0x00ff); - } - } - - // if result is 2 then a previous dead key could not be composed. - else if (result == 2) { - // if the two characters are the same and this is a key release - // then this event is the dead key being released. we put the - // dead key back in that case, otherwise we discard both key - // events because we can't compose the character. alternatively - // we could generate key events for both keys. - if (((ascii & 0xff00) >> 8) != (ascii & 0x00ff) || - (info & 0x80000000) == 0) { - // cannot compose key - return kKeyNone; - } - - // get the scan code of the dead key and the shift state - // required to generate it. - vkCode = VkKeyScan(static_cast(ascii & 0x00ff)); - - // set shift state required to generate key - BYTE keys[256]; - memset(keys, 0, sizeof(keys)); - if (vkCode & 0x0100) { - keys[VK_SHIFT] = 0x80; - } - if (vkCode & 0x0200) { - keys[VK_CONTROL] = 0x80; - } - if (vkCode & 0x0400) { - keys[VK_MENU] = 0x80; - } - - // strip shift state off of virtual key code - vkCode &= 0x00ff; - - // get the scan code for the key - scanCode = MapVirtualKey(vkCode, 0); - - // put it back - ToAscii(vkCode, scanCode, keys, &ascii, 0); - id = kKeyMultiKey; - } - } - - // set mask - *altgr = false; - if (id != kKeyNone && id != kKeyMultiKey && c != 0) { - // note if key requires AltGr. VkKeyScan() can have a problem - // with some characters. there are two problems in particular. - // first, typing a dead key then pressing space will cause - // VkKeyScan() to return 0xffff. second, certain characters - // may map to multiple virtual keys and we might get the wrong - // one. if that happens then we might not get the right - // modifier mask. AltGr+9 on the french keyboard layout (^) - // has this problem. in the first case, we'll assume AltGr is - // required (only because it solves the problems we've seen - // so far). in the second, we'll use whatever the keyboard - // state says. - WORD virtualKeyAndModifierState = VkKeyScan(c); - if (virtualKeyAndModifierState == 0xffff) { - // there is no mapping. assume AltGr. - LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); - *altgr = true; - } - else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { - // we didn't get the key that was actually pressed - LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); - if ((m_keys[VK_CONTROL] & 0x80) != 0 && - (m_keys[VK_MENU] & 0x80) != 0) { - *altgr = true; - } - } - else { - BYTE modifierState = HIBYTE(virtualKeyAndModifierState); - if ((modifierState & 6) == 6) { - // key requires ctrl and alt == AltGr - *altgr = true; - } - } - } - - // map modifier key - KeyModifierMask mask = 0; - if (((m_keys[VK_LSHIFT] | - m_keys[VK_RSHIFT] | - m_keys[VK_SHIFT]) & 0x80) != 0) { - mask |= KeyModifierShift; - } - if (*altgr) { - mask |= KeyModifierModeSwitch; - } - else { - if (((m_keys[VK_LCONTROL] | - m_keys[VK_RCONTROL] | - m_keys[VK_CONTROL]) & 0x80) != 0) { - mask |= KeyModifierControl; - } - if (((m_keys[VK_LMENU] | - m_keys[VK_RMENU] | - m_keys[VK_MENU]) & 0x80) != 0) { - mask |= KeyModifierAlt; - } - } - if (((m_keys[VK_LWIN] | - m_keys[VK_RWIN]) & 0x80) != 0) { - mask |= KeyModifierSuper; - } - if ((m_keys[VK_CAPITAL] & 0x01) != 0) { - mask |= KeyModifierCapsLock; - } - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { - mask |= KeyModifierNumLock; - } - if ((m_keys[VK_SCROLL] & 0x01) != 0) { - mask |= KeyModifierScrollLock; - } - *maskOut = mask; - - return id; -} - -ButtonID -CMSWindowsPrimaryScreen::mapButton(WPARAM msg, LPARAM button) const -{ - switch (msg) { - case WM_LBUTTONDOWN: - case WM_LBUTTONDBLCLK: - case WM_LBUTTONUP: - case WM_NCLBUTTONDOWN: - case WM_NCLBUTTONDBLCLK: - case WM_NCLBUTTONUP: - return kButtonLeft; - - case WM_MBUTTONDOWN: - case WM_MBUTTONDBLCLK: - case WM_MBUTTONUP: - case WM_NCMBUTTONDOWN: - case WM_NCMBUTTONDBLCLK: - case WM_NCMBUTTONUP: - return kButtonMiddle; - - case WM_RBUTTONDOWN: - case WM_RBUTTONDBLCLK: - case WM_RBUTTONUP: - case WM_NCRBUTTONDOWN: - case WM_NCRBUTTONDBLCLK: - case WM_NCRBUTTONUP: - return kButtonRight; - - case WM_XBUTTONDOWN: - case WM_XBUTTONDBLCLK: - case WM_XBUTTONUP: - case WM_NCXBUTTONDOWN: - case WM_NCXBUTTONDBLCLK: - case WM_NCXBUTTONUP: - switch (button) { - case XBUTTON1: - return kButtonExtra0 + 0; - - case XBUTTON2: - return kButtonExtra0 + 1; - } - return kButtonNone; - - default: - return kButtonNone; - } -} - -void -CMSWindowsPrimaryScreen::updateKeys() -{ - // not using GetKeyboardState() because that doesn't seem to give - // up-to-date results. i don't know why that is or why GetKeyState() - // should give different results. - - // clear key and button state - memset(m_keys, 0, sizeof(m_keys)); - memset(m_buttons, 0, sizeof(m_buttons)); - - // we only care about the modifier key states. other keys and the - // mouse buttons should be up. - // sometimes these seem to be out of date so, to avoid getting - // locked to a screen unexpectedly, just assume the non-toggle - // modifier keys are up. -/* - m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT) & 0x80); - m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT) & 0x80); - m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT) & 0x80); - m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL) & 0x80); - m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL) & 0x80); - m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL) & 0x80); - m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU) & 0x80); - m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU) & 0x80); - m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU) & 0x80); - m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN) & 0x80); - m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN) & 0x80); - m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS) & 0x80); -*/ - m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); - m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); - m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); -} - -void -CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press) -{ - if (press) { - switch (vkCode) { - case 0: - case VK_LBUTTON: - case VK_MBUTTON: - case VK_RBUTTON: - // ignore bogus key - break; - - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: - m_keys[vkCode] |= 0x80; - m_keys[VK_SHIFT] |= 0x80; - break; - - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: - m_keys[vkCode] |= 0x80; - m_keys[VK_CONTROL] |= 0x80; - break; - - case VK_LMENU: - case VK_RMENU: - case VK_MENU: - m_keys[vkCode] |= 0x80; - m_keys[VK_MENU] |= 0x80; - break; - - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - // toggle keys - m_keys[vkCode] |= 0x80; - break; - - default: - case VK_LWIN: - case VK_RWIN: - case VK_APPS: - m_keys[vkCode] |= 0x80; - break; - } - - // special case: we detect ctrl+alt+del being pressed on some - // systems but we don't detect the release of those keys. so - // if ctrl, alt, and del are down then mark them up. - if ((m_keys[VK_CONTROL] & 0x80) != 0 && - (m_keys[VK_MENU] & 0x80) != 0 && - (m_keys[VK_DELETE] & 0x80) != 0) { - m_keys[VK_LCONTROL] &= ~0x80; - m_keys[VK_RCONTROL] &= ~0x80; - m_keys[VK_CONTROL] &= ~0x80; - m_keys[VK_LMENU] &= ~0x80; - m_keys[VK_RMENU] &= ~0x80; - m_keys[VK_MENU] &= ~0x80; - m_keys[VK_DELETE] &= ~0x80; - } - } - else { - switch (vkCode) { - case 0: - case VK_LBUTTON: - case VK_MBUTTON: - case VK_RBUTTON: - // ignore bogus key - break; - - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: - m_keys[vkCode] &= ~0x80; - if (((m_keys[VK_LSHIFT] | m_keys[VK_RSHIFT]) & 0x80) == 0) { - m_keys[VK_SHIFT] &= ~0x80; - } - break; - - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: - m_keys[vkCode] &= ~0x80; - if (((m_keys[VK_LCONTROL] | m_keys[VK_RCONTROL]) & 0x80) == 0) { - m_keys[VK_CONTROL] &= ~0x80; - } - break; - - case VK_LMENU: - case VK_RMENU: - case VK_MENU: - m_keys[vkCode] &= ~0x80; - if (((m_keys[VK_LMENU] | m_keys[VK_RMENU]) & 0x80) == 0) { - m_keys[VK_MENU] &= ~0x80; - } - break; - - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - // toggle keys - m_keys[vkCode] &= ~0x80; - m_keys[vkCode] ^= 0x01; - break; - - default: - case VK_LWIN: - case VK_RWIN: - case VK_APPS: - m_keys[vkCode] &= ~0x80; - break; - } - } -} - -bool -CMSWindowsPrimaryScreen::isModifier(UINT vkCode) const - -{ - switch (vkCode) { - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: - case VK_LMENU: - case VK_RMENU: - case VK_MENU: - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - case VK_LWIN: - case VK_RWIN: - return true; - - default: - return false; - } -} - -KeyButton -CMSWindowsPrimaryScreen::mapKeyToScanCode(UINT vk1, UINT vk2) const -{ - KeyButton button = static_cast(MapVirtualKey(vk1, 0)); - if (button == 0) { - button = static_cast(MapVirtualKey(vk2, 0)); - } - return button; -} diff --git a/lib/platform/CMSWindowsPrimaryScreen.h b/lib/platform/CMSWindowsPrimaryScreen.h deleted file mode 100644 index c318d12c..00000000 --- a/lib/platform/CMSWindowsPrimaryScreen.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CMSWINDOWSPRIMARYSCREEN_H -#define CMSWINDOWSPRIMARYSCREEN_H - -#include "CPrimaryScreen.h" -#include "IMSWindowsScreenEventHandler.h" -#include "CSynergyHook.h" -#include "MouseTypes.h" -#include "CString.h" - -class CMSWindowsScreen; -class IScreenReceiver; -class IPrimaryScreenReceiver; - -//! Microsoft windows primary screen implementation -class CMSWindowsPrimaryScreen : - public CPrimaryScreen, public IMSWindowsScreenEventHandler { -public: - typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM); - - CMSWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); - virtual ~CMSWindowsPrimaryScreen(); - - // CPrimaryScreen overrides - virtual void reconfigure(UInt32 activeSides); - virtual void warpCursor(SInt32 x, SInt32 y); - virtual void resetOptions(); - virtual void setOptions(const COptionsList& options); - virtual UInt32 addOneShotTimer(double timeout); - virtual KeyModifierMask getToggleMask() const; - virtual bool isLockedToScreen() const; - virtual IScreen* getScreen() const; - - // IMSWindowsScreenEventHandler overrides - virtual void onScreensaver(bool activated); - virtual bool onPreDispatch(const CEvent* event); - virtual bool onEvent(CEvent* event); - virtual void onOneShotTimerExpired(UInt32 id); - virtual SInt32 getJumpZoneSize() const; - virtual void postCreateWindow(HWND); - virtual void preDestroyWindow(HWND); - virtual void onAccessibleDesktop(); - -protected: - // CPrimaryScreen overrides - virtual void onPreMainLoop(); - virtual void onPreOpen(); - virtual void onPostOpen(); - virtual void onPostClose(); - virtual void onPreEnter(); - virtual void onPostEnter(); - virtual void onPreLeave(); - virtual void onPostLeave(bool); - - virtual void createWindow(); - virtual void destroyWindow(); - virtual bool showWindow(); - virtual void hideWindow(); - virtual void warpCursorToCenter(); - - virtual void updateKeys(); - -private: - void enterNoWarp(); - - // warp cursor without discarding queued events - void warpCursorNoFlush(SInt32 x, SInt32 y); - - // discard posted messages - void nextMark(); - - // test if event should be ignored - bool ignore() const; - - // key and button queries - KeyID mapKey(WPARAM keycode, LPARAM info, - KeyModifierMask* maskOut, bool* altgr); - ButtonID mapButton(WPARAM msg, LPARAM button) const; - void updateKey(UINT vkCode, bool press); - bool isModifier(UINT vkCode) const; - KeyButton mapKeyToScanCode(UINT vk1, UINT vk2) const; - -private: - IPrimaryScreenReceiver* m_receiver; - CMSWindowsScreen* m_screen; - - // true if windows 95/98/me - bool m_is95Family; - - // the main loop's thread id - DWORD m_threadID; - - // my window - HWND m_window; - - // used to discard queued messages that are no longer needed - UInt32 m_mark; - UInt32 m_markReceived; - - // map of key state - BYTE m_keys[256]; - - // map of button state - BYTE m_buttons[1 + kButtonExtra0 + 1]; - - // last mouse position - SInt32 m_x, m_y; - - // position of center pixel of screen - SInt32 m_xCenter, m_yCenter; - - // hook library stuff - HINSTANCE m_hookLibrary; - InitFunc m_init; - CleanupFunc m_cleanup; - InstallFunc m_install; - UninstallFunc m_uninstall; - SetSidesFunc m_setSides; - SetZoneFunc m_setZone; - SetRelayFunc m_setRelay; - bool m_lowLevel; - - // stuff for hiding the cursor - DWORD m_cursorThread; -}; - -#endif diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index e42e91b1..7a272c46 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -14,16 +14,20 @@ #include "CMSWindowsScreen.h" #include "CMSWindowsClipboard.h" +#include "CMSWindowsDesktop.h" #include "CMSWindowsScreenSaver.h" #include "CClipboard.h" -#include "IMSWindowsScreenEventHandler.h" #include "IScreenReceiver.h" +#include "IPrimaryScreenReceiver.h" +#include "XScreen.h" #include "CThread.h" #include "CLock.h" -#include "TMethodJob.h" +#include "CFunctionJob.h" #include "CLog.h" #include "CString.h" #include "CStringUtil.h" +#include "TMethodJob.h" +#include "CArch.h" #include "CArchMiscWindows.h" #include #include @@ -38,6 +42,50 @@ #include #pragma warning(pop) +// these are only defined when WINVER >= 0x0500 +#if !defined(SPI_GETMOUSESPEED) +#define SPI_GETMOUSESPEED 112 +#endif +#if !defined(SPI_SETMOUSESPEED) +#define SPI_SETMOUSESPEED 113 +#endif + +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + +// multimedia keys +#if !defined(VK_BROWSER_BACK) +#define VK_BROWSER_BACK 0xA6 +#define VK_BROWSER_FORWARD 0xA7 +#define VK_BROWSER_REFRESH 0xA8 +#define VK_BROWSER_STOP 0xA9 +#define VK_BROWSER_SEARCH 0xAA +#define VK_BROWSER_FAVORITES 0xAB +#define VK_BROWSER_HOME 0xAC +#define VK_VOLUME_MUTE 0xAD +#define VK_VOLUME_DOWN 0xAE +#define VK_VOLUME_UP 0xAF +#define VK_MEDIA_NEXT_TRACK 0xB0 +#define VK_MEDIA_PREV_TRACK 0xB1 +#define VK_MEDIA_STOP 0xB2 +#define VK_MEDIA_PLAY_PAUSE 0xB3 +#define VK_LAUNCH_MAIL 0xB4 +#define VK_LAUNCH_MEDIA_SELECT 0xB5 +#define VK_LAUNCH_APP1 0xB6 +#define VK_LAUNCH_APP2 0xB7 +#endif + // // CMSWindowsScreen // @@ -45,35 +93,41 @@ HINSTANCE CMSWindowsScreen::s_instance = NULL; CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; -CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, - IMSWindowsScreenEventHandler* eventHandler) : - m_receiver(receiver), - m_eventHandler(eventHandler), - m_class(NULL), - m_icon(NULL), - m_cursor(NULL), +CMSWindowsScreen::CMSWindowsScreen( + IScreenReceiver* receiver, + IPrimaryScreenReceiver* primaryReceiver) : + m_isPrimary(primaryReceiver != NULL), m_is95Family(CArchMiscWindows::isWindows95Family()), + m_receiver(receiver), + m_primaryReceiver(primaryReceiver), + m_isOnScreen(m_isPrimary), + m_class(0), + m_cursor(NULL), m_window(NULL), m_x(0), m_y(0), m_w(0), m_h(0), + m_xCenter(0), m_yCenter(0), m_multimon(false), + m_xCursor(0), m_yCursor(0), + m_mark(0), + m_markReceived(0), m_threadID(0), m_lastThreadID(0), - m_nextClipboardWindow(NULL), - m_ownClipboard(false), m_timer(0), - m_desk(NULL), - m_deskName(), - m_hookLibrary(NULL), - m_installScreensaver(NULL), - m_uninstallScreensaver(NULL), + m_oneShotTimer(0), m_screensaver(NULL), m_screensaverNotify(false), - m_inaccessibleDesktop(false) + m_nextClipboardWindow(NULL), + m_ownClipboard(false), + m_desk(NULL), + m_deskName(), + m_inaccessibleDesktop(false), + m_hookLibrary(NULL), + m_lowLevel(false), + m_keyState(NULL) { - assert(s_screen == NULL); - assert(m_receiver != NULL); - assert(m_eventHandler != NULL); + assert(s_screen == NULL); + assert(m_receiver != NULL); s_screen = this; @@ -96,66 +150,6 @@ CMSWindowsScreen::init(HINSTANCE instance) s_instance = instance; } -HWND -CMSWindowsScreen::openDesktop() -{ - // save thread id - m_threadID = GetCurrentThreadId(); - - // get the input desktop and switch to it - if (!switchDesktop(openInputDesktop())) { - return NULL; - } - - // poll input desktop to see if it changes (onPreDispatch() - // handles WM_TIMER). this is also used for polling other - // stuff. - m_timer = SetTimer(NULL, 0, 200, NULL); - - return m_window; -} - -void -CMSWindowsScreen::closeDesktop() -{ - // remove timer - if (m_timer != 0) { - KillTimer(NULL, m_timer); - m_timer = 0; - } - if (m_oneShotTimer != 0) { - KillTimer(NULL, m_oneShotTimer); - m_oneShotTimer = 0; - } - - // disconnect from desktop - switchDesktop(NULL); - - // clear thread id - m_threadID = 0; - - assert(m_window == NULL); - assert(m_desk == NULL); -} - -UInt32 -CMSWindowsScreen::addOneShotTimer(double timeout) -{ - // FIXME -- support multiple one-shot timers - if (m_oneShotTimer != 0) { - KillTimer(NULL, m_oneShotTimer); - } - m_oneShotTimer = SetTimer(NULL, 0, - static_cast(1000.0 * timeout), NULL); - return 0; -} - -bool -CMSWindowsScreen::isMultimon() const -{ - return m_multimon; -} - HINSTANCE CMSWindowsScreen::getInstance() { @@ -163,51 +157,178 @@ CMSWindowsScreen::getInstance() } void -CMSWindowsScreen::open() +CMSWindowsScreen::open(IKeyState* keyState) +{ + assert(s_instance != NULL); + assert(m_class == 0); + assert(m_hookLibrary == NULL); + + try { + // load the hook library + m_hookLibrary = LoadLibrary("synrgyhk"); + if (m_hookLibrary == NULL) { + LOG((CLOG_ERR "Failed to load hook library; synrgyhk.dll is missing")); + throw XScreenOpenFailure(); + } + m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides"); + m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone"); + m_setMode = (SetModeFunc)GetProcAddress(m_hookLibrary, "setMode"); + m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install"); + m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall"); + m_init = (InitFunc)GetProcAddress(m_hookLibrary, "init"); + m_cleanup = (CleanupFunc)GetProcAddress(m_hookLibrary, "cleanup"); + m_installScreensaver = + (InstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "installScreenSaver"); + m_uninstallScreensaver = + (UninstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "uninstallScreenSaver"); + if (m_setSides == NULL || + m_setZone == NULL || + m_setMode == NULL || + m_install == NULL || + m_uninstall == NULL || + m_init == NULL || + m_cleanup == NULL || + m_installScreensaver == NULL || + m_uninstallScreensaver == NULL) { + LOG((CLOG_ERR "Invalid hook library; use a newer synrgyhk.dll")); + throw XScreenOpenFailure(); + } + + // save thread id. this is mainly to ensure that mainLoop() + // is called by the same thread that called open(). these + // threads must be the same to get the right message queue. + m_threadID = GetCurrentThreadId(); + + // initialize hook library + if (m_isPrimary && m_init(m_threadID) == 0) { + LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?")); + throw XScreenOpenFailure(); + } + + // create the transparent cursor + m_cursor = createBlankCursor(); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = s_instance; + classInfo.hIcon = NULL; + classInfo.hCursor = m_cursor; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "Synergy"; + classInfo.hIconSm = NULL; + m_class = RegisterClassEx(&classInfo); + + // get screen shape + updateScreenShape(); + + // initialize the screen saver + m_screensaver = new CMSWindowsScreenSaver(); + + // initialize marks + m_mark = 0; + m_markReceived = 0; + } + catch (...) { + close(); + throw; + } + + // save the IKeyState + m_keyState = keyState; +} + +void +CMSWindowsScreen::close() { assert(s_instance != NULL); - assert(m_class == 0); - LOG((CLOG_DEBUG "opening display")); + // done with m_keyState + m_keyState = NULL; - // create the transparent cursor - createBlankCursor(); + // done with screen saver + delete m_screensaver; - // register a window class - WNDCLASSEX classInfo; - classInfo.cbSize = sizeof(classInfo); - classInfo.style = CS_DBLCLKS | CS_NOCLOSE; - classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; - classInfo.cbClsExtra = 0; - classInfo.cbWndExtra = 0; - classInfo.hInstance = s_instance; - classInfo.hIcon = NULL; - classInfo.hCursor = m_cursor; - classInfo.hbrBackground = NULL; - classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = "Synergy"; - classInfo.hIconSm = NULL; - m_class = RegisterClassEx(&classInfo); - - // get screen shape - updateScreenShape(); - - // initialize the screen saver - m_screensaver = new CMSWindowsScreenSaver(); - - // load the hook library and get the screen saver functions - m_hookLibrary = LoadLibrary("synrgyhk"); - if (m_hookLibrary != NULL) { - m_installScreensaver = (InstallScreenSaverFunc)GetProcAddress( - m_hookLibrary, "installScreenSaver"); - m_uninstallScreensaver = (UninstallScreenSaverFunc)GetProcAddress( - m_hookLibrary, "uninstallScreenSaver"); - if (m_installScreensaver == NULL || m_uninstallScreensaver == NULL) { - // disable if either install or uninstall is unavailable - m_installScreensaver = NULL; - m_uninstallScreensaver = NULL; - } + // unregister the window class + if (m_class != 0) { + UnregisterClass((LPCTSTR)m_class, s_instance); } + + // done with cursor + if (m_cursor != NULL) { + DestroyCursor(m_cursor); + } + + // done with hook library + if (m_hookLibrary != NULL) { + if (m_isPrimary) { + m_cleanup(); + } + FreeLibrary(m_hookLibrary); + } + + // reset state + m_screensaver = NULL; + m_cursor = NULL; + m_class = 0; + m_hookLibrary = NULL; + m_threadID = 0; +} + +void +CMSWindowsScreen::enable() +{ + assert(m_isOnScreen == m_isPrimary); + + if (m_isPrimary) { + // update shadow key state + m_keyMapper.update(NULL); + + // set jump zones + m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize()); + + // watch jump zones + m_setMode(kHOOK_WATCH_JUMP_ZONE); + } + + // create the window + if (!switchDesktop(CMSWindowsDesktop::openInputDesktop())) { + throw XScreenOpenFailure(); + } + + // poll input desktop to see if it changes. windows doesn't + // inform us when the desktop has changed but we need to + // open a new window when that happens so we poll. this is + // also used for polling other stuff. + m_timer = SetTimer(NULL, 0, 200, NULL); +} + +void +CMSWindowsScreen::disable() +{ + // remove timers + if (m_timer != 0) { + KillTimer(NULL, m_timer); + } + if (m_oneShotTimer != 0) { + KillTimer(NULL, m_oneShotTimer); + } + + // reset state + m_timer = 0; + m_oneShotTimer = 0; + + // done with window + switchDesktop(NULL); + assert(m_window == NULL); + assert(m_desk == NULL); } void @@ -217,20 +338,19 @@ CMSWindowsScreen::mainLoop() assert(m_threadID == GetCurrentThreadId()); // event loop - CEvent event; - event.m_result = 0; + MSG msg; for (;;) { // wait for an event in a cancellable way if (CThread::getCurrentThread().waitForEvent(-1.0) != CThread::kEvent) { continue; } - if (!PeekMessage(&event.m_msg, NULL, 0, 0, PM_REMOVE)) { + if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { continue; } // handle quit message - if (event.m_msg.message == WM_QUIT) { - if (event.m_msg.wParam == 0) { + if (msg.message == WM_QUIT) { + if (msg.wParam == 0) { // force termination CThread::getCurrentThread().cancel(); } @@ -241,9 +361,9 @@ CMSWindowsScreen::mainLoop() } // dispatch message - if (!onPreDispatch(&event)) { - TranslateMessage(&event.m_msg); - DispatchMessage(&event.m_msg); + if (!onPreDispatch(msg.hwnd, msg.message, msg.wParam, msg.lParam)) { + TranslateMessage(&msg); + DispatchMessage(&msg); } } } @@ -256,35 +376,82 @@ CMSWindowsScreen::exitMainLoop() } void -CMSWindowsScreen::close() +CMSWindowsScreen::enter() { - assert(s_instance != NULL); + if (m_isPrimary) { + // show the cursor + showCursor(true); + m_cursorThread = 0; - // done with hook library - if (m_hookLibrary != NULL) { - FreeLibrary(m_hookLibrary); - m_installScreensaver = NULL; - m_uninstallScreensaver = NULL; - m_hookLibrary = NULL; + // enable special key sequences on win95 family + enableSpecialKeys(true); + + // watch jump zones + m_setMode(kHOOK_WATCH_JUMP_ZONE); + + // all messages prior to now are invalid + nextMark(); + } + else { + // show the cursor + ShowWindow(m_window, SW_HIDE); } - // done with screen saver - delete m_screensaver; - m_screensaver = NULL; + // now on screen + m_isOnScreen = true; +} - // unregister the window class - if (m_class != 0) { - UnregisterClass((LPCTSTR)m_class, s_instance); - m_class = 0; +bool +CMSWindowsScreen::leave() +{ + if (m_isPrimary) { + // show window +/* XXX + if (m_lowLevel) { + SetWindowPos(m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1, + SWP_NOACTIVATE); + ShowWindow(m_window, SW_SHOWNA); + } +*/ + // update keys +/* XXX + m_keyMapper.update(NULL); +*/ + + // warp to center + warpCursor(m_xCenter, m_yCenter); + + // disable special key sequences on win95 family + enableSpecialKeys(false); + + // all messages prior to now are invalid + nextMark(); + + // watch jump zones + m_setMode(kHOOK_RELAY_EVENTS); + + // hide the cursor if using low level hooks + if (m_lowLevel) { + HWND hwnd = GetForegroundWindow(); + m_cursorThread = GetWindowThreadProcessId(hwnd, NULL); + showCursor(false); + } + } + else { + // move hider window under the cursor center + MoveWindow(m_window, m_xCenter, m_yCenter, 1, 1, FALSE); + + // raise and show the hider window + ShowWindow(m_window, SW_SHOWNA); + + // warp the mouse to the cursor center + fakeMouseMove(m_xCenter, m_yCenter); } - // delete resources - if (m_cursor != NULL) { - DestroyCursor(m_cursor); - m_cursor = NULL; - } + // now off screen + m_isOnScreen = false; - LOG((CLOG_DEBUG "closed display")); + return true; } bool @@ -321,6 +488,7 @@ CMSWindowsScreen::checkClipboards() // won't be reflected on other screens until we leave but at // least the clipboard itself will work. if (m_ownClipboard && !CMSWindowsClipboard::isOwnedBySynergy()) { + LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received")); m_ownClipboard = false; m_receiver->onGrabClipboard(kClipboardClipboard); m_receiver->onGrabClipboard(kClipboardSelection); @@ -334,9 +502,7 @@ CMSWindowsScreen::openScreensaver(bool notify) m_screensaverNotify = notify; if (m_screensaverNotify) { - if (m_installScreensaver != NULL) { - m_installScreensaver(); - } + m_installScreensaver(); } else { m_screensaver->disable(); @@ -348,9 +514,7 @@ CMSWindowsScreen::closeScreensaver() { if (m_screensaver != NULL) { if (m_screensaverNotify) { - if (m_uninstallScreensaver != NULL) { - m_uninstallScreensaver(); - } + m_uninstallScreensaver(); } else { m_screensaver->enable(); @@ -363,6 +527,7 @@ void CMSWindowsScreen::screensaver(bool activate) { assert(m_screensaver != NULL); + if (activate) { m_screensaver->activate(); } @@ -372,25 +537,29 @@ CMSWindowsScreen::screensaver(bool activate) } void -CMSWindowsScreen::syncDesktop() +CMSWindowsScreen::resetOptions() { - // change calling thread's desktop - if (!m_is95Family) { - if (SetThreadDesktop(m_desk) == 0) { -// LOG((CLOG_WARN "failed to set desktop: %d", GetLastError())); - } - } + // no options +} - // attach input queues if not already attached. this has a habit - // of sucking up more and more CPU each time it's called (even if - // the threads are already attached). since we only expect one - // thread to call this more than once we can save just the last - // attached thread. - DWORD threadID = GetCurrentThreadId(); - if (threadID != m_lastThreadID && threadID != m_threadID) { - m_lastThreadID = threadID; - AttachThreadInput(threadID, m_threadID, TRUE); - } +void +CMSWindowsScreen::setOptions(const COptionsList&) +{ + // no options +} + +void +CMSWindowsScreen::updateKeys() +{ + syncDesktop(); + m_keyMapper.update(m_keyState); + memset(m_buttons, 0, sizeof(m_buttons)); +} + +bool +CMSWindowsScreen::isPrimary() const +{ + return m_isPrimary; } bool @@ -402,8 +571,7 @@ CMSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const } void -CMSWindowsScreen::getShape(SInt32& x, SInt32& y, - SInt32& w, SInt32& h) const +CMSWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { assert(m_class != 0); @@ -417,20 +585,243 @@ void CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const { POINT pos; + syncDesktop(); if (GetCursorPos(&pos)) { x = pos.x; y = pos.y; } else { - getCursorCenter(x, y); + x = m_xCenter; + y = m_yCenter; } } void -CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +CMSWindowsScreen::reconfigure(UInt32 activeSides) { - x = GetSystemMetrics(SM_CXSCREEN) >> 1; - y = GetSystemMetrics(SM_CYSCREEN) >> 1; + assert(m_isPrimary); + + m_setSides(activeSides); +} + +void +CMSWindowsScreen::warpCursor(SInt32 x, SInt32 y) +{ + // warp mouse + warpCursorNoFlush(x, y); + + // remove all input events before and including warp + MSG msg; + while (PeekMessage(&msg, NULL, SYNERGY_MSG_INPUT_FIRST, + SYNERGY_MSG_INPUT_LAST, PM_REMOVE)) { + // do nothing + } + + // save position as last position + m_xCursor = x; + m_yCursor = y; +} + +UInt32 +CMSWindowsScreen::addOneShotTimer(double timeout) +{ + // FIXME -- support multiple one-shot timers + if (m_oneShotTimer != 0) { + KillTimer(NULL, m_oneShotTimer); + } + m_oneShotTimer = SetTimer(NULL, 0, + static_cast(1000.0 * timeout), NULL); + return 0; +} + +SInt32 +CMSWindowsScreen::getJumpZoneSize() const +{ + return 1; +} + +bool +CMSWindowsScreen::isAnyMouseButtonDown() const +{ + static const char* buttonToName[] = { + "button 0", + "Left Button", + "Middle Button", + "Right Button", + "X Button 1", + "X Button 2" + }; + + for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { + if ((m_buttons[i] & 0x80) != 0) { + LOG((CLOG_DEBUG "locked by \"%s\"", buttonToName[i])); + return true; + } + } + + return false; +} + +const char* +CMSWindowsScreen::getKeyName(KeyButton virtualKey) const +{ + return m_keyMapper.getKeyName(virtualKey); +} + +void +CMSWindowsScreen::fakeKeyEvent(KeyButton virtualKey, bool press) const +{ + DWORD flags = 0; + if (m_keyMapper.isExtendedKey(virtualKey)) { + flags |= KEYEVENTF_EXTENDEDKEY; + } + if (!press) { + flags |= KEYEVENTF_KEYUP; + } + const UINT code = m_keyMapper.keyToScanCode(&virtualKey); + syncDesktop(); + keybd_event(static_cast(virtualKey & 0xffu), + static_cast(code), flags, 0); +} + +bool +CMSWindowsScreen::fakeCtrlAltDel() const +{ + if (!m_is95Family) { + // to fake ctrl+alt+del on the NT family we broadcast a suitable + // hotkey to all windows on the winlogon desktop. however, we + // the current thread must be on that desktop to do the broadcast + // and we can't switch just any thread because some own windows + // or hooks. so start a new thread to do the real work. + CThread cad(new CFunctionJob(&CMSWindowsScreen::ctrlAltDelThread)); + cad.wait(); + } + else { + // get the sequence of keys to simulate ctrl+alt+del + IKeyState::Keystrokes keys; + KeyID key = kKeyDelete; + KeyModifierMask mask = KeyModifierControl | KeyModifierAlt; + if (mapKey(keys, *m_keyState, key, mask, false) == 0) { + keys.clear(); + } + + // do it + for (IKeyState::Keystrokes::const_iterator k = keys.begin(); + k != keys.end(); ++k) { + fakeKeyEvent(k->m_key, k->m_press); + } + } + return true; +} + +void +CMSWindowsScreen::fakeMouseButton(ButtonID id, bool press) const +{ + // map button id to button flag + DWORD data; + DWORD flags = mapButtonToEvent(id, press, &data); + + // send event + if (flags != 0) { + syncDesktop(); + mouse_event(flags, 0, 0, data, 0); + } +} + +void +CMSWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const +{ + // motion is simple (i.e. it's on the primary monitor) if there + // is only one monitor. + bool simple = !m_multimon; + if (!simple) { + // also simple if motion is within the primary monitor + simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && + y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); + } + + // move the mouse directly to target position if motion is simple + syncDesktop(); + if (simple) { + // when using absolute positioning with mouse_event(), + // the normalized device coordinates range over only + // the primary screen. + SInt32 w = GetSystemMetrics(SM_CXSCREEN); + SInt32 h = GetSystemMetrics(SM_CYSCREEN); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65536.0 * x) / w), + (DWORD)((65536.0 * y) / h), + 0, 0); + } + + // windows 98 (and Me?) is broken. you cannot set the absolute + // position of the mouse except on the primary monitor but you + // can do relative moves onto any monitor. this is, in microsoft's + // words, "by design." apparently the designers of windows 2000 + // we're a little less lazy and did it right. + // + // microsoft recommends in Q193003 to absolute position the cursor + // somewhere on the primary monitor then relative move to the + // desired location. this doesn't work for us because when the + // user drags a scrollbar, a window, etc. it causes the dragged + // item to jump back a forth between the position on the primary + // monitor and the desired position. while it always ends up in + // the right place, the effect is disconcerting. + // + // instead we'll get the cursor's current position and do just a + // relative move from there to the desired position. relative + // moves are subject to cursor acceleration which we don't want. + // so we disable acceleration, do the relative move, then restore + // acceleration. there's a slight chance we'll end up in the + // wrong place if the user moves the cursor using this system's + // mouse while simultaneously moving the mouse on the server + // system. that defeats the purpose of synergy so we'll assume + // that won't happen. even if it does, the next mouse move will + // correct the position. + else { + // save mouse speed & acceleration + int oldSpeed[4]; + bool accelChanged = + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && + SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); + + // use 1:1 motion + if (accelChanged) { + int newSpeed[4] = { 0, 0, 0, 1 }; + accelChanged = + SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || + SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); + } + + // get current mouse position + POINT pos; + GetCursorPos(&pos); + + // move relative to mouse position + mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); + + // restore mouse speed & acceleration + if (accelChanged) { + SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); + SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); + } + } +} + +void +CMSWindowsScreen::fakeMouseWheel(SInt32 delta) const +{ + syncDesktop(); + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); +} + +KeyButton +CMSWindowsScreen::mapKey(IKeyState::Keystrokes& keys, + const IKeyState& keyState, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + return m_keyMapper.mapKey(keys, keyState, id, desiredMask, isAutoRepeat); } void @@ -443,208 +834,15 @@ CMSWindowsScreen::updateScreenShape() m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + // get center for cursor + m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; + m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; + // check for multiple monitors m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || m_h != GetSystemMetrics(SM_CYSCREEN)); } -bool -CMSWindowsScreen::onPreDispatch(const CEvent* event) -{ - // handle event - const MSG* msg = &event->m_msg; - switch (msg->message) { - case SYNERGY_MSG_SCREEN_SAVER: - { - // activating or deactivating? - bool activate = (msg->wParam != 0); - - // ignore this message if there are any other screen saver - // messages already in the queue. this is important because - // our checkStarted() function has a deliberate delay, so it - // can't respond to events at full CPU speed and will fall - // behind if a lot of screen saver events are generated. - // that can easily happen because windows will continually - // send SC_SCREENSAVE until the screen saver starts, even if - // the screen saver is disabled! - MSG msg; - if (!PeekMessage(&msg, NULL, SYNERGY_MSG_SCREEN_SAVER, - SYNERGY_MSG_SCREEN_SAVER, PM_NOREMOVE)) { - if (activate) { - if (m_screensaver->checkStarted( - SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) { - m_eventHandler->onScreensaver(true); - } - } - else { - m_eventHandler->onScreensaver(false); - } - } - return true; - } - - case WM_TIMER: - if (msg->wParam == m_timer) { - // if current desktop is not the input desktop then switch to it. - // windows 95 doesn't support multiple desktops so don't bother - // to check under it. - if (!m_is95Family) { - HDESK desk = openInputDesktop(); - if (desk != NULL) { - if (isCurrentDesktop(desk)) { - CloseDesktop(desk); - } - else if (m_screensaver->isActive()) { - // don't switch desktops when the screensaver is - // active. we'd most likely switch to the - // screensaver desktop which would have the side - // effect of forcing the screensaver to stop. - CloseDesktop(desk); - } - else { - switchDesktop(desk); - } - - // if the desktop was inaccessible then notify the - // event handler of that. - if (m_inaccessibleDesktop) { - LOG((CLOG_DEBUG "desktop is now accessible")); - m_inaccessibleDesktop = false; - m_eventHandler->onAccessibleDesktop(); - } - } - else if (!m_inaccessibleDesktop) { - // the desktop has become inaccessible - m_inaccessibleDesktop = true; - LOG((CLOG_DEBUG "desktop is now inaccessible")); - } - } - - // let client do timer related stuff. ignore the return - // value though since the event has been handled here. - m_eventHandler->onPreDispatch(event); - } - else if (msg->wParam == m_oneShotTimer) { - // one shot timer expired - KillTimer(NULL, m_oneShotTimer); - m_oneShotTimer = 0; - m_eventHandler->onOneShotTimerExpired(0); - } - - return true; - } - - // let client handle the event - return m_eventHandler->onPreDispatch(event); -} - -bool -CMSWindowsScreen::onEvent(CEvent* event) -{ - assert(event != NULL); - - const MSG& msg = event->m_msg; - switch (msg.message) { - case WM_QUERYENDSESSION: - if (m_is95Family) { - event->m_result = TRUE; - return true; - } - break; - - case WM_ENDSESSION: - if (m_is95Family) { - if (msg.wParam == TRUE && msg.lParam == 0) { - exitMainLoop(); - } - return true; - } - break; - - case WM_PAINT: - ValidateRect(msg.hwnd, NULL); - return true; - - case WM_DRAWCLIPBOARD: - LOG((CLOG_DEBUG "clipboard was taken")); - - // first pass it on - if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, - msg.message, msg.wParam, msg.lParam); - } - - // now notify client that somebody changed the clipboard (unless - // we're the owner). - if (!CMSWindowsClipboard::isOwnedBySynergy()) { - if (m_ownClipboard) { - m_ownClipboard = false; - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - else { - m_ownClipboard = true; - } - return true; - - case WM_CHANGECBCHAIN: - if (m_nextClipboardWindow == (HWND)msg.wParam) { - m_nextClipboardWindow = (HWND)msg.lParam; - } - else if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, - msg.message, msg.wParam, msg.lParam); - } - return true; - - case WM_DISPLAYCHANGE: - { - // screen resolution may have changed. get old shape. - SInt32 xOld, yOld, wOld, hOld; - getShape(xOld, yOld, wOld, hOld); - - // update shape - updateScreenShape(); - - // collect new screen info - CClientInfo info; - getShape(info.m_x, info.m_y, info.m_w, info.m_h); - getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = m_eventHandler->getJumpZoneSize(); - - // do nothing if resolution hasn't changed - if (info.m_x != xOld || info.m_y != yOld || - info.m_w != wOld || info.m_h != hOld) { - // forward event - m_eventHandler->onEvent(event); - - // send new screen info - m_receiver->onInfoChanged(info); - } - - return true; - } - } - - return m_eventHandler->onEvent(event); -} - -void -CMSWindowsScreen::createBlankCursor() -{ - // create a transparent cursor - int cw = GetSystemMetrics(SM_CXCURSOR); - int ch = GetSystemMetrics(SM_CYCURSOR); - UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; - UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; - memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); - memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); - m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); - delete[] cursorXOR; - delete[] cursorAND; -} - bool CMSWindowsScreen::switchDesktop(HDESK desk) { @@ -657,17 +855,19 @@ CMSWindowsScreen::switchDesktop(HDESK desk) ChangeClipboardChain(m_window, m_nextClipboardWindow); m_nextClipboardWindow = NULL; - // let client clean up before we destroy the window - m_eventHandler->preDestroyWindow(m_window); + // uninstall hooks. we can't change the thread desktop + // with hooks installed. + if (m_isPrimary) { + m_uninstall(); + } - // now destroy window + // now destroy window. we can't change the thread desktop + // with a window. DestroyWindow(m_window); m_window = NULL; // done with desk - if (!m_is95Family) { - CloseDesktop(m_desk); - } + CMSWindowsDesktop::closeDesktop(m_desk); m_desk = NULL; m_deskName = ""; } @@ -680,18 +880,14 @@ CMSWindowsScreen::switchDesktop(HDESK desk) // uninstall screen saver hooks if (m_screensaverNotify) { - if (m_uninstallScreensaver != NULL) { - m_uninstallScreensaver(); - } + m_uninstallScreensaver(); } // set the desktop. can only do this when there are no windows // and hooks on the current desktop owned by this thread. - if (SetThreadDesktop(desk) == 0) { + if (!CMSWindowsDesktop::setDesktop(desk)) { LOG((CLOG_ERR "failed to set desktop: %d", GetLastError())); - if (!m_is95Family) { - CloseDesktop(desk); - } + CMSWindowsDesktop::closeDesktop(desk); return false; } @@ -708,16 +904,55 @@ CMSWindowsScreen::switchDesktop(HDESK desk) NULL); if (m_window == NULL) { LOG((CLOG_ERR "failed to create window: %d", GetLastError())); - if (!m_is95Family) { - CloseDesktop(desk); - } + CMSWindowsDesktop::closeDesktop(desk); return false; } // reinstall screen saver hooks if (m_screensaverNotify) { - if (m_installScreensaver != NULL) { - m_installScreensaver(); + m_installScreensaver(); + } + + if (m_isPrimary) { + // we don't ever want our window to activate + EnableWindow(m_window, FALSE); + + // install hooks + switch (m_install()) { + case kHOOK_FAILED: + // FIXME -- can't install hook so we won't work; report error + m_lowLevel = false; + break; + + case kHOOK_OKAY: + m_lowLevel = false; + break; + + case kHOOK_OKAY_LL: + m_lowLevel = true; + break; + } + + if (m_isOnScreen) { + // all messages prior to now are invalid + // FIXME -- is this necessary; couldn't we lose key releases? + nextMark(); + } + } + else { + // update key state + updateKeys(); + + // hide cursor if this screen isn't active + if (!m_isOnScreen) { + // move hider window under the cursor center + MoveWindow(m_window, m_xCenter, m_yCenter, 1, 1, FALSE); + + // raise and show the hider window + ShowWindow(m_window, SW_SHOWNA); + + // warp the mouse to the cursor center + fakeMouseMove(m_xCenter, m_yCenter); } } @@ -729,68 +964,892 @@ CMSWindowsScreen::switchDesktop(HDESK desk) // save new desktop m_desk = desk; - m_deskName = getDesktopName(m_desk); - LOG((CLOG_DEBUG "switched to desktop \"%s\"", m_deskName.c_str())); - - // let client prepare the window - m_eventHandler->postCreateWindow(m_window); + m_deskName = CMSWindowsDesktop::getDesktopName(desk); + LOG((CLOG_DEBUG "switched to desktop \"%s\" with window 0x%08x", m_deskName.c_str(), (UInt32)m_window)); return true; } -HDESK -CMSWindowsScreen::openInputDesktop() const +void +CMSWindowsScreen::syncDesktop() const { - if (m_is95Family) { - // there's only one desktop on windows 95 et al. - return GetThreadDesktop(GetCurrentThreadId()); + // change calling thread's desktop + if (!CMSWindowsDesktop::setDesktop(m_desk)) { +// LOG((CLOG_WARN "failed to set desktop: %d", GetLastError())); } - else { - return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, - DESKTOP_CREATEWINDOW | - DESKTOP_HOOKCONTROL | - GENERIC_WRITE); - } -} -CString -CMSWindowsScreen::getDesktopName(HDESK desk) const -{ - if (desk == NULL) { - return CString(); - } - else if (m_is95Family) { - return "desktop"; - } - else { - DWORD size; - GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); - TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR)); - GetUserObjectInformation(desk, UOI_NAME, name, size, &size); - CString result(name); - return result; + // attach input queues if not already attached. this has a habit + // of sucking up more and more CPU each time it's called (even if + // the threads are already attached). since we only expect one + // thread to call this more than once we can save just the last + // attached thread. + DWORD threadID = GetCurrentThreadId(); + if (threadID != m_lastThreadID && threadID != m_threadID) { + CMSWindowsScreen* self = const_cast(this); + self->m_lastThreadID = threadID; + AttachThreadInput(threadID, m_threadID, TRUE); } } bool -CMSWindowsScreen::isCurrentDesktop(HDESK desk) const +CMSWindowsScreen::onPreDispatch(HWND hwnd, + UINT message, WPARAM wParam, LPARAM lParam) { - // don't allocate space for current desktop name on heap since - // we do this a lot and we never save the name. - TCHAR* name; - if (desk == NULL) { - name = _T(""); + // handle event + switch (message) { + case SYNERGY_MSG_SCREEN_SAVER: + return onScreensaver(wParam != 0); + + case WM_TIMER: + return onTimer(static_cast(wParam)); } - else if (m_is95Family) { - name = _T("desktop"); + + if (m_isPrimary) { + return onPreDispatchPrimary(hwnd, message, wParam, lParam); + } + + return false; +} + +bool +CMSWindowsScreen::onPreDispatchPrimary(HWND, + UINT message, WPARAM wParam, LPARAM lParam) +{ + // check if windows key is up but we think it's down. if so then + // synthesize a key release for it. we have to do this because + // if the user presses and releases a windows key without pressing + // any other key while it's down then windows will eat the key + // release. if we don't detect that and synthesize the release + // then the user will be locked to the screen and the client won't + // take the usual windows key release action (which on windows is + // to show the start menu). + // + // we can use GetKeyState() to check the state of the windows keys + // because, event though the key release is not reported to us, + // the event is processed and the keyboard state updated by the + // system. since the key could go up at any time we'll check the + // state on every event. only check on windows 95 family since + // NT family reports the key release as usual. obviously we skip + // this if the event is for the windows key itself. + if (m_is95Family) { + if (m_keyMapper.isPressed(VK_LWIN) && + (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 && + !(message == SYNERGY_MSG_KEY && wParam == VK_LWIN)) { + // compute appropriate parameters for fake event + WPARAM wParam = VK_LWIN; + LPARAM lParam = 0xc1000000; + lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); + + // process as if it were a key up + KeyModifierMask mask; + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); + KeyID key = m_keyMapper.mapKeyFromEvent(wParam, + lParam, &mask, NULL); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_primaryReceiver->onKeyUp(key, mask, button); + m_keyMapper.updateKey(static_cast(wParam), false); + } + if (m_keyMapper.isPressed(VK_RWIN) && + (GetAsyncKeyState(VK_RWIN) & 0x8000) == 0 && + !(message == SYNERGY_MSG_KEY && wParam == VK_RWIN)) { + // compute appropriate parameters for fake event + WPARAM wParam = VK_RWIN; + LPARAM lParam = 0xc1000000; + lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); + + // process as if it were a key up + KeyModifierMask mask; + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); + KeyID key = m_keyMapper.mapKeyFromEvent(wParam, + lParam, &mask, NULL); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_primaryReceiver->onKeyUp(key, mask, button); + m_keyMapper.updateKey(static_cast(wParam), false); + } + } + + // handle event + switch (message) { + case SYNERGY_MSG_MARK: + return onMark(static_cast(wParam)); + + case SYNERGY_MSG_KEY: + return onKey(wParam, lParam); + + case SYNERGY_MSG_MOUSE_BUTTON: + return onMouseButton(wParam, lParam); + + case SYNERGY_MSG_MOUSE_MOVE: + return onMouseMove(static_cast(wParam), + static_cast(lParam)); + + case SYNERGY_MSG_MOUSE_WHEEL: + return onMouseWheel(static_cast(wParam)); + + case SYNERGY_MSG_PRE_WARP: + { + // save position to compute delta of next motion + m_xCursor = static_cast(wParam); + m_yCursor = static_cast(lParam); + + // we warped the mouse. discard events until we find the + // matching post warp event. see warpCursorNoFlush() for + // where the events are sent. we discard the matching + // post warp event and can be sure we've skipped the warp + // event. + MSG msg; + do { + GetMessage(&msg, NULL, SYNERGY_MSG_MOUSE_MOVE, + SYNERGY_MSG_POST_WARP); + } while (msg.message != SYNERGY_MSG_POST_WARP); + } + return true; + + case SYNERGY_MSG_POST_WARP: + LOG((CLOG_WARN "unmatched post warp")); + return true; + } + + return false; +} + +bool +CMSWindowsScreen::onEvent(HWND hwnd, + UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result) +{ + switch (message) { + case WM_QUERYENDSESSION: + if (m_is95Family) { + *result = TRUE; + return true; + } + break; + + case WM_ENDSESSION: + if (m_is95Family) { + if (wParam == TRUE && lParam == 0) { + exitMainLoop(); + } + return true; + } + break; + + case WM_PAINT: + ValidateRect(hwnd, NULL); + return true; + + case WM_DRAWCLIPBOARD: + LOG((CLOG_DEBUG "clipboard was taken")); + + // first pass on the message + if (m_nextClipboardWindow != NULL) { + SendMessage(m_nextClipboardWindow, message, wParam, lParam); + } + + // now handle the message + return onClipboardChange(); + + case WM_CHANGECBCHAIN: + if (m_nextClipboardWindow == (HWND)wParam) { + m_nextClipboardWindow = (HWND)lParam; + LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow)); + } + else if (m_nextClipboardWindow != NULL) { + LOG((CLOG_DEBUG "clipboard chain: forward: %d 0x%08x 0x%08x", message, wParam, lParam)); + SendMessage(m_nextClipboardWindow, message, wParam, lParam); + } + return true; + + case WM_DISPLAYCHANGE: + return onDisplayChange(); + + case WM_ACTIVATEAPP: + return onActivate(wParam != FALSE); + } + + return false; +} + +bool +CMSWindowsScreen::onMark(UInt32 mark) +{ + m_markReceived = mark; + return true; +} + +bool +CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) +{ + // ignore message if posted prior to last mark change + if (!ignore()) { + // check for ctrl+alt+del emulation + if ((wParam == VK_PAUSE || wParam == VK_CANCEL) && + (m_keyMapper.isPressed(VK_CONTROL) && + m_keyMapper.isPressed(VK_MENU))) { + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + wParam = VK_DELETE; + lParam &= 0xffff0000; + lParam |= 0x00000001; + } + + // process key normally + bool altgr; + KeyModifierMask mask; + const KeyID key = m_keyMapper.mapKeyFromEvent(wParam, + lParam, &mask, &altgr); + KeyButton button = static_cast( + (lParam & 0x00ff0000u) >> 16); + if (key != kKeyNone && key != kKeyMultiKey) { + if ((lParam & 0x80000000) == 0) { + // key press + + // if AltGr required for this key then make sure + // the ctrl and alt keys are *not* down on the + // client. windows simulates AltGr with ctrl and + // alt for some inexplicable reason and clients + // will get confused if they see mode switch and + // ctrl and alt. we'll also need to put ctrl and + // alt back the way they were after we simulate + // the key. + bool ctrlL = m_keyMapper.isPressed(VK_LCONTROL); + bool ctrlR = m_keyMapper.isPressed(VK_RCONTROL); + bool altL = m_keyMapper.isPressed(VK_LMENU); + bool altR = m_keyMapper.isPressed(VK_RMENU); + if (altgr) { + KeyID key; + KeyButton button; + UINT scanCode; + KeyModifierMask mask2 = (mask & + ~(KeyModifierControl | + KeyModifierAlt | + KeyModifierModeSwitch)); + if (ctrlL) { + key = kKeyControl_L; + button = VK_LCONTROL; + scanCode = m_keyMapper.keyToScanCode(&button); + button = static_cast(scanCode); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_primaryReceiver->onKeyUp(key, mask2, button); + } + if (ctrlR) { + key = kKeyControl_R; + button = VK_RCONTROL; + scanCode = m_keyMapper.keyToScanCode(&button); + button = static_cast(scanCode); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_primaryReceiver->onKeyUp(key, mask2, button); + } + if (altL) { + key = kKeyAlt_L; + button = VK_LMENU; + scanCode = m_keyMapper.keyToScanCode(&button); + button = static_cast(scanCode); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_primaryReceiver->onKeyUp(key, mask2, button); + } + if (altR) { + key = kKeyAlt_R; + button = VK_RMENU; + scanCode = m_keyMapper.keyToScanCode(&button); + button = static_cast(scanCode); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_primaryReceiver->onKeyUp(key, mask2, button); + } + } + + // send key + const bool wasDown = ((lParam & 0x40000000) != 0); + SInt32 repeat = (SInt32)(lParam & 0xffff); + if (!wasDown) { + LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_primaryReceiver->onKeyDown(key, mask, button); + if (repeat > 0) { + --repeat; + } + } + if (repeat >= 1) { + LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button)); + m_primaryReceiver->onKeyRepeat(key, mask, repeat, button); + } + + // restore ctrl and alt state + if (altgr) { + KeyID key; + KeyButton button; + UINT scanCode; + KeyModifierMask mask2 = (mask & + ~(KeyModifierControl | + KeyModifierAlt | + KeyModifierModeSwitch)); + if (ctrlL) { + key = kKeyControl_L; + button = VK_LCONTROL; + scanCode = m_keyMapper.keyToScanCode(&button); + button = static_cast(scanCode); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_primaryReceiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierControl; + } + if (ctrlR) { + key = kKeyControl_R; + button = VK_RCONTROL; + scanCode = m_keyMapper.keyToScanCode(&button); + button = static_cast(scanCode); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_primaryReceiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierControl; + } + if (altL) { + key = kKeyAlt_L; + button = VK_LMENU; + scanCode = m_keyMapper.keyToScanCode(&button); + button = static_cast(scanCode); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_primaryReceiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierAlt; + } + if (altR) { + key = kKeyAlt_R; + button = VK_RMENU; + scanCode = m_keyMapper.keyToScanCode(&button); + button = static_cast(scanCode); + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); + m_primaryReceiver->onKeyDown(key, mask2, button); + mask2 |= KeyModifierAlt; + } + } + } + else { + // key release. if the key isn't down according to + // our table then we never got the key press event + // for it. if it's not a modifier key then we'll + // synthesize the press first. only do this on + // the windows 95 family, which eats certain special + // keys like alt+tab, ctrl+esc, etc. + if (m_is95Family && !isModifier(wParam) && + m_keyMapper.isPressed(static_cast(wParam))) { + LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_primaryReceiver->onKeyDown(key, mask, button); + m_keyMapper.updateKey(static_cast(wParam), true); + } + + // do key up + LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + m_primaryReceiver->onKeyUp(key, mask, button); + } + } + else { + LOG((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", wParam, lParam)); + } + } + + // keep our shadow key state up to date + m_keyMapper.updateKey(static_cast(wParam), + ((lParam & 0x80000000) == 0)); + + return true; +} + +bool +CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) +{ + // get which button + bool pressed = false; + const ButtonID button = mapButtonFromEvent(wParam, lParam); + + // ignore message if posted prior to last mark change + if (!ignore()) { + switch (wParam) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_XBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: + case WM_NCLBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCXBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCMBUTTONDBLCLK: + case WM_NCRBUTTONDBLCLK: + case WM_NCXBUTTONDBLCLK: + LOG((CLOG_DEBUG1 "event: button press button=%d", button)); + if (button != kButtonNone) { + m_primaryReceiver->onMouseDown(button); + } + pressed = true; + break; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONUP: + case WM_NCRBUTTONUP: + case WM_NCXBUTTONUP: + LOG((CLOG_DEBUG1 "event: button release button=%d", button)); + if (button != kButtonNone) { + m_primaryReceiver->onMouseUp(button); + } + pressed = false; + break; + } + } + + // keep our shadow key state up to date + if (button >= kButtonLeft && button <= kButtonExtra0 + 1) { + if (pressed) { + m_buttons[button] |= 0x80; + } + else { + m_buttons[button] &= ~0x80; + } + } + + return true; +} + +bool +CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) +{ + // compute motion delta (relative to the last known + // mouse position) + SInt32 x = mx - m_xCursor; + SInt32 y = my - m_yCursor; + + // ignore if the mouse didn't move or if message posted prior + // to last mark change. + if (ignore() || (x == 0 && y == 0)) { + return true; + } + + // save position to compute delta of next motion + m_xCursor = mx; + m_yCursor = my; + + if (m_isOnScreen) { + // motion on primary screen + m_primaryReceiver->onMouseMovePrimary(m_xCursor, m_yCursor); } else { - DWORD size; - GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); - name = (TCHAR*)alloca(size + sizeof(TCHAR)); - GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + // motion on secondary screen. warp mouse back to + // center. + warpCursorNoFlush(m_xCenter, m_yCenter); + + // examine the motion. if it's about the distance + // from the center of the screen to an edge then + // it's probably a bogus motion that we want to + // ignore (see warpCursorNoFlush() for a further + // description). + static SInt32 bogusZoneSize = 10; + if (-x + bogusZoneSize > m_xCenter - m_x || + x + bogusZoneSize > m_x + m_w - m_xCenter || + -y + bogusZoneSize > m_yCenter - m_y || + y + bogusZoneSize > m_y + m_h - m_yCenter) { + LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y)); + } + else { + // send motion + m_primaryReceiver->onMouseMoveSecondary(x, y); + } + } + + return true; +} + +bool +CMSWindowsScreen::onMouseWheel(SInt32 delta) +{ + // ignore message if posted prior to last mark change + if (!ignore()) { + LOG((CLOG_DEBUG1 "event: button wheel delta=%d", delta)); + m_primaryReceiver->onMouseWheel(delta); + } + return true; +} + +bool +CMSWindowsScreen::onScreensaver(bool activated) +{ + // ignore this message if there are any other screen saver + // messages already in the queue. this is important because + // our checkStarted() function has a deliberate delay, so it + // can't respond to events at full CPU speed and will fall + // behind if a lot of screen saver events are generated. + // that can easily happen because windows will continually + // send SC_SCREENSAVE until the screen saver starts, even if + // the screen saver is disabled! + MSG msg; + if (PeekMessage(&msg, NULL, SYNERGY_MSG_SCREEN_SAVER, + SYNERGY_MSG_SCREEN_SAVER, PM_NOREMOVE)) { + return true; + } + + if (activated) { + if (m_screensaver->checkStarted(SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) { + m_primaryReceiver->onScreensaver(true); + } + } + else { + m_primaryReceiver->onScreensaver(false); + } + + return true; +} + +bool +CMSWindowsScreen::onTimer(UINT timerID) +{ + if (timerID == m_timer) { + // if current desktop is not the input desktop then switch to it. + HDESK desk = CMSWindowsDesktop::openInputDesktop(); + if (desk == m_desk || + CMSWindowsDesktop::getDesktopName(desk) == m_deskName || + m_screensaver->isActive()) { + // same desktop or screensaver is active. don't switch + // desktops when the screensaver is active. we'd most + // likely switch to the screensaver desktop which would + // have the side effect of forcing the screensaver to stop. + CMSWindowsDesktop::closeDesktop(desk); + } + else { + switchDesktop(desk); + } + + // if the desktop was inaccessible and isn't anymore then + // update our key state. + if (desk != NULL && m_inaccessibleDesktop) { + LOG((CLOG_DEBUG "desktop is now accessible")); + m_inaccessibleDesktop = false; + updateKeys(); + } + + // note if desktop was accessible but isn't anymore + else if (desk == NULL && !m_inaccessibleDesktop) { + m_inaccessibleDesktop = true; + LOG((CLOG_DEBUG "desktop is now inaccessible")); + } + } + + else if (timerID == m_oneShotTimer) { + // one shot timer expired + KillTimer(NULL, m_oneShotTimer); + m_oneShotTimer = 0; + m_primaryReceiver->onOneShotTimerExpired(0); + } + + return true; +} + +bool +CMSWindowsScreen::onDisplayChange() +{ + // screen resolution may have changed. save old shape. + SInt32 xOld = m_x, yOld = m_y, wOld = m_w, hOld = m_h; + + // update shape + updateScreenShape(); + + // do nothing if resolution hasn't changed + if (xOld != m_x || yOld != m_y || wOld != m_w || hOld != m_h) { + if (m_isPrimary) { + // warp mouse to center if off screen + if (!m_isOnScreen) { + warpCursor(m_xCenter, m_yCenter); + } + + // tell hook about resize if on screen + else { + m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize()); + } + } + + // collect new screen info + CClientInfo info; + info.m_x = m_x; + info.m_y = m_y; + info.m_w = m_w; + info.m_h = m_h; + info.m_zoneSize = getJumpZoneSize(); + getCursorPos(info.m_mx, info.m_my); + + // send new screen info + m_receiver->onInfoChanged(info); + } + + return true; +} + +bool +CMSWindowsScreen::onClipboardChange() +{ + // now notify client that somebody changed the clipboard (unless + // we're the owner). + if (!CMSWindowsClipboard::isOwnedBySynergy()) { + LOG((CLOG_DEBUG "clipboard changed: foreign owned")); + if (m_ownClipboard) { + LOG((CLOG_DEBUG "clipboard changed: lost ownership")); + m_ownClipboard = false; + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + else { + LOG((CLOG_DEBUG "clipboard changed: synergy owned")); + m_ownClipboard = true; + } + + return true; +} + +bool +CMSWindowsScreen::onActivate(bool activated) +{ + if (!m_isPrimary && activated) { + // some other app activated. hide the hider window. + ShowWindow(m_window, SW_HIDE); + } + + return false; +} + +void +CMSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) +{ + // send an event that we can recognize before the mouse warp + PostThreadMessage(m_threadID, SYNERGY_MSG_PRE_WARP, x, y); + + // warp mouse. hopefully this inserts a mouse motion event + // between the previous message and the following message. + SetCursorPos(x, y); + + // yield the CPU. there's a race condition when warping: + // a hardware mouse event occurs + // the mouse hook is not called because that process doesn't have the CPU + // we send PRE_WARP, SetCursorPos(), send POST_WARP + // we process all of those events and update m_x, m_y + // we finish our time slice + // the hook is called + // the hook sends us a mouse event from the pre-warp position + // we get the CPU + // we compute a bogus warp + // we need the hook to process all mouse events that occur + // before we warp before we do the warp but i'm not sure how + // to guarantee that. yielding the CPU here may reduce the + // chance of undesired behavior. we'll also check for very + // large motions that look suspiciously like about half width + // or height of the screen. + ARCH->sleep(0.0); + + // send an event that we can recognize after the mouse warp + PostThreadMessage(m_threadID, SYNERGY_MSG_POST_WARP, 0, 0); +} + +void +CMSWindowsScreen::nextMark() +{ + // next mark + ++m_mark; + + // mark point in message queue where the mark was changed + PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); +} + +bool +CMSWindowsScreen::ignore() const +{ + return (m_mark != m_markReceived); +} + +HCURSOR +CMSWindowsScreen::createBlankCursor() const +{ + // create a transparent cursor + int cw = GetSystemMetrics(SM_CXCURSOR); + int ch = GetSystemMetrics(SM_CYCURSOR); + UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; + UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; + memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); + memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); + HCURSOR c = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); + delete[] cursorXOR; + delete[] cursorAND; + return c; +} + +void +CMSWindowsScreen::showCursor(bool show) const +{ + if (m_cursorThread != 0) { + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, TRUE); + } + ShowCursor(show ? TRUE : FALSE); + if (m_threadID != m_cursorThread) { + AttachThreadInput(m_threadID, m_cursorThread, FALSE); + } + } +} + +void +CMSWindowsScreen::enableSpecialKeys(bool enable) const +{ + // enable/disable ctrl+alt+del, alt+tab, etc on win95 family. + // since the win95 family doesn't support low-level hooks, we + // use this undocumented feature to suppress normal handling + // of certain key combinations. + if (m_is95Family) { + DWORD dummy = 0; + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, + enable ? FALSE : TRUE, &dummy, 0); + } +} + +DWORD +CMSWindowsScreen::mapButtonToEvent(ButtonID button, + bool press, DWORD* inData) const +{ + DWORD dummy; + DWORD* data = (inData != NULL) ? inData : &dummy; + + // the system will swap the meaning of left/right for us if + // the user has configured a left-handed mouse but we don't + // want it to swap since we want the handedness of the + // server's mouse. so pre-swap for a left-handed mouse. + if (GetSystemMetrics(SM_SWAPBUTTON)) { + switch (button) { + case kButtonLeft: + button = kButtonRight; + break; + + case kButtonRight: + button = kButtonLeft; + break; + } + } + + // map button id to button flag and button data + *data = 0; + switch (button) { + case kButtonLeft: + return press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; + + case kButtonMiddle: + return press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; + + case kButtonRight: + return press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; + + case kButtonExtra0 + 0: + *data = XBUTTON1; + return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; + + case kButtonExtra0 + 1: + *data = XBUTTON2; + return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; + + default: + return 0; + } +} + +ButtonID +CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const +{ + switch (msg) { + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_LBUTTONUP: + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCLBUTTONUP: + return kButtonLeft; + + case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: + case WM_MBUTTONUP: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONDBLCLK: + case WM_NCMBUTTONUP: + return kButtonMiddle; + + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + case WM_RBUTTONUP: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONDBLCLK: + case WM_NCRBUTTONUP: + return kButtonRight; + + case WM_XBUTTONDOWN: + case WM_XBUTTONDBLCLK: + case WM_XBUTTONUP: + case WM_NCXBUTTONDOWN: + case WM_NCXBUTTONDBLCLK: + case WM_NCXBUTTONUP: + switch (button) { + case XBUTTON1: + return kButtonExtra0 + 0; + + case XBUTTON2: + return kButtonExtra0 + 1; + } + return kButtonNone; + + default: + return kButtonNone; + } +} + +bool +CMSWindowsScreen::isModifier(UINT vkCode) const +{ + switch (vkCode) { + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + case VK_LWIN: + case VK_RWIN: + return true; + + default: + return false; + } +} + +void +CMSWindowsScreen::ctrlAltDelThread(void*) +{ + // get the Winlogon desktop at whatever privilege we can + HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED); + if (desk != NULL) { + if (SetThreadDesktop(desk)) { + PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, + MAKELPARAM(MOD_CONTROL | MOD_ALT, VK_DELETE)); + } + else { + LOG((CLOG_DEBUG "can't switch to Winlogon desk: %d", GetLastError())); + } + CloseDesktop(desk); + } + else { + LOG((CLOG_DEBUG "can't open Winlogon desk: %d", GetLastError())); } - return (_tcsicmp(name, m_deskName.c_str()) == 0); } LRESULT CALLBACK @@ -798,17 +1857,10 @@ CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { assert(s_screen != NULL); - CEvent event; - event.m_msg.hwnd = hwnd; - event.m_msg.message = msg; - event.m_msg.wParam = wParam; - event.m_msg.lParam = lParam; - event.m_result = 0; + LRESULT result = 0; + if (!s_screen->onEvent(hwnd, msg, wParam, lParam, &result)) { + result = DefWindowProc(hwnd, msg, wParam, lParam); + } - if (s_screen->onEvent(&event)) { - return event.m_result; - } - else { - return DefWindowProc(hwnd, msg, wParam, lParam); - } + return result; } diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index f4af0cac..29be457b 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -15,7 +15,8 @@ #ifndef CMSWINDOWSSCREEN_H #define CMSWINDOWSSCREEN_H -#include "IScreen.h" +#include "IPlatformScreen.h" +#include "CMSWindowsKeyMapper.h" #include "CSynergyHook.h" #include "CMutex.h" #include "CString.h" @@ -23,22 +24,13 @@ #include class CMSWindowsScreenSaver; -class CThread; - -// Microsoft windows event -class CEvent { -public: - MSG m_msg; - LRESULT m_result; -}; - class IScreenReceiver; -class IMSWindowsScreenEventHandler; +class IPrimaryScreenReceiver; -//! Implementation of IScreen for Microsoft Windows -class CMSWindowsScreen : public IScreen { +//! Implementation of IPlatformScreen for Microsoft Windows +class CMSWindowsScreen : public IPlatformScreen { public: - CMSWindowsScreen(IScreenReceiver*, IMSWindowsScreenEventHandler*); + CMSWindowsScreen(IScreenReceiver*, IPrimaryScreenReceiver*); virtual ~CMSWindowsScreen(); //! @name manipulators @@ -51,37 +43,10 @@ public: */ static void init(HINSTANCE); - //! Open desktop - /*! - Open the desktop and create and return the window. Returns NULL - on failure. - */ - HWND openDesktop(); - - //! Close desktop - /*! - Close the window and desktop. - */ - void closeDesktop(); - - //! Install a one-shot timer - /*! - Installs a one-shot timer for \c timeout seconds and returns the - id of the timer (which will be passed to the receiver's - \c onTimerExpired()). - */ - UInt32 addOneShotTimer(double timeout); - //@} //! @name accessors //@{ - //! Test for multiple monitors - /*! - Returns true iff the system appears to have multiple monitors. - */ - bool isMultimon() const; - //! Get instance /*! Returns the application instance handle passed to init(). @@ -90,53 +55,116 @@ public: //@} - // IScreen overrides - // note -- this class expects the hook DLL to have been loaded - // and initialized before open() is called. - void open(); - void mainLoop(); - void exitMainLoop(); - void close(); - bool setClipboard(ClipboardID, const IClipboard*); - void checkClipboards(); - void openScreensaver(bool notify); - void closeScreensaver(); - void screensaver(bool activate); - void syncDesktop(); - bool getClipboard(ClipboardID, IClipboard*) const; - void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - void getCursorPos(SInt32&, SInt32&) const; - void getCursorCenter(SInt32&, SInt32&) const; + // IPlatformScreen overrides + virtual void open(IKeyState*); + virtual void close(); + virtual void enable(); + virtual void disable(); + virtual void mainLoop(); + virtual void exitMainLoop(); + virtual void enter(); + virtual bool leave(); + virtual bool setClipboard(ClipboardID, const IClipboard*); + virtual void checkClipboards(); + virtual void openScreensaver(bool notify); + virtual void closeScreensaver(); + virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual void updateKeys(); + virtual bool isPrimary() const; + virtual bool getClipboard(ClipboardID, IClipboard*) const; + virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; + virtual void getCursorPos(SInt32&, SInt32&) const; + + // IPrimaryScreen overrides + virtual void reconfigure(UInt32 activeSides); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual UInt32 addOneShotTimer(double timeout); + virtual SInt32 getJumpZoneSize() const; + virtual bool isAnyMouseButtonDown() const; + virtual const char* getKeyName(KeyButton) const; + + // ISecondaryScreen overrides + virtual void fakeKeyEvent(KeyButton id, bool press) const; + virtual bool fakeCtrlAltDel() const; + virtual void fakeMouseButton(ButtonID id, bool press) const; + virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseWheel(SInt32 delta) const; + virtual KeyButton mapKey(IKeyState::Keystrokes&, + const IKeyState& keyState, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; private: // update screen size cache void updateScreenShape(); - // internal pre-dispatch event processing - bool onPreDispatch(const CEvent* event); - - // internal (post-dispatch) event processing - bool onEvent(CEvent* event); - - // create the transparent cursor - void createBlankCursor(); - // switch to the given desktop. this destroys the window and unhooks // all hooks, switches the desktop, then creates the window and rehooks // all hooks (because you can't switch the thread's desktop if it has // any windows or hooks). bool switchDesktop(HDESK desk); - // get the input desktop. caller must CloseDesktop() the result. - // do not call under windows 95/98/me. - HDESK openInputDesktop() const; + // make sure we're on the expected desktop + void syncDesktop() const; - // get the desktop's name. do not call under windows 95/98/me. - CString getDesktopName(HDESK) const; + // handle message before it gets dispatched. returns true iff + // the message should not be dispatched. + bool onPreDispatch(HWND, UINT, WPARAM, LPARAM); - // returns true iff desk is the current desk. do not call under - // windows 95/98/me. - bool isCurrentDesktop(HDESK desk) const; + // handle message before it gets dispatched. returns true iff + // the message should not be dispatched. + bool onPreDispatchPrimary(HWND, UINT, WPARAM, LPARAM); + + // handle message. returns true iff handled and optionally sets + // \c *result (which defaults to 0). + bool onEvent(HWND, UINT, WPARAM, LPARAM, LRESULT* result); + + // message handlers + bool onMark(UInt32 mark); + bool onKey(WPARAM, LPARAM); + bool onMouseButton(WPARAM, LPARAM); + bool onMouseMove(SInt32 x, SInt32 y); + bool onMouseWheel(SInt32 delta); + bool onScreensaver(bool activated); + bool onTimer(UINT timerID); + bool onDisplayChange(); + bool onClipboardChange(); + bool onActivate(bool activated); + +// XXX + // warp cursor without discarding queued events + void warpCursorNoFlush(SInt32 x, SInt32 y); + + // discard posted messages + void nextMark(); + + // test if event should be ignored + bool ignore() const; +// XXX + + // create the transparent cursor + HCURSOR createBlankCursor() const; + + // show/hide the cursor + void showCursor(bool) const; + + // enable/disable special key combinations so we can catch/pass them + void enableSpecialKeys(bool) const; + + // map a button ID and action to a mouse event + DWORD mapButtonToEvent(ButtonID button, + bool press, DWORD* inData) const; + + // map a button event to a button ID + ButtonID mapButtonFromEvent(WPARAM msg, LPARAM button) const; + + // return true iff the given virtual key is a modifier + bool isModifier(UINT vkCode) const; + + // send ctrl+alt+del hotkey event + static void ctrlAltDelThread(void*); // our window proc static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); @@ -144,53 +172,63 @@ private: private: static HINSTANCE s_instance; - IScreenReceiver* m_receiver; - IMSWindowsScreenEventHandler* m_eventHandler; - - ATOM m_class; - HICON m_icon; - HCURSOR m_cursor; + // true if screen is being used as a primary screen, false otherwise + bool m_isPrimary; // true if windows 95/98/me bool m_is95Family; - // our window + // receivers + IScreenReceiver* m_receiver; + IPrimaryScreenReceiver* m_primaryReceiver; + + // true if mouse has entered the screen + bool m_isOnScreen; + + // our resources + ATOM m_class; + HCURSOR m_cursor; HWND m_window; - // screen shape + // screen shape stuff SInt32 m_x, m_y; SInt32 m_w, m_h; + SInt32 m_xCenter, m_yCenter; // true if system appears to have multiple monitors bool m_multimon; + // last mouse position + SInt32 m_xCursor, m_yCursor; + + // used to discard queued messages that are no longer needed + UInt32 m_mark; + UInt32 m_markReceived; + // the main loop's thread id DWORD m_threadID; // the thread id of the last attached thread DWORD m_lastThreadID; - // clipboard stuff - HWND m_nextClipboardWindow; - bool m_ownClipboard; - // the timer used to check for desktop switching UINT m_timer; // the one shot timer UINT m_oneShotTimer; + // screen saver stuff + CMSWindowsScreenSaver* m_screensaver; + bool m_screensaverNotify; + + // clipboard stuff + HWND m_nextClipboardWindow; + bool m_ownClipboard; + // the current desk and it's name HDESK m_desk; CString m_deskName; - // screen saver stuff - HINSTANCE m_hookLibrary; - InstallScreenSaverFunc m_installScreensaver; - UninstallScreenSaverFunc m_uninstallScreensaver; - CMSWindowsScreenSaver* m_screensaver; - bool m_screensaverNotify; - // true when the current desktop is inaccessible. while // the desktop is inaccessible we won't receive user input // and we'll lose track of the keyboard state. when the @@ -198,6 +236,29 @@ private: // handler of that. bool m_inaccessibleDesktop; + // hook library stuff + HINSTANCE m_hookLibrary; + InitFunc m_init; + CleanupFunc m_cleanup; + InstallFunc m_install; + UninstallFunc m_uninstall; + SetSidesFunc m_setSides; + SetZoneFunc m_setZone; + SetModeFunc m_setMode; + InstallScreenSaverFunc m_installScreensaver; + UninstallScreenSaverFunc m_uninstallScreensaver; + bool m_lowLevel; + + // keyboard stuff + IKeyState* m_keyState; + CMSWindowsKeyMapper m_keyMapper; + + // map of button state + BYTE m_buttons[1 + kButtonExtra0 + 1]; + + // stuff for hiding the cursor + DWORD m_cursorThread; + static CMSWindowsScreen* s_screen; }; diff --git a/lib/platform/CMSWindowsSecondaryScreen.cpp b/lib/platform/CMSWindowsSecondaryScreen.cpp deleted file mode 100644 index c04cfcb0..00000000 --- a/lib/platform/CMSWindowsSecondaryScreen.cpp +++ /dev/null @@ -1,1338 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CMSWindowsSecondaryScreen.h" -#include "CMSWindowsScreen.h" -#include "XScreen.h" -#include "CLock.h" -#include "CThread.h" -#include "CFunctionJob.h" -#include "CLog.h" -#include "CArchMiscWindows.h" -#include - -// these are only defined when WINVER >= 0x0500 -#if !defined(SPI_GETMOUSESPEED) -#define SPI_GETMOUSESPEED 112 -#endif -#if !defined(SPI_SETMOUSESPEED) -#define SPI_SETMOUSESPEED 113 -#endif - -// X button stuff -#if !defined(WM_XBUTTONDOWN) -#define WM_XBUTTONDOWN 0x020B -#define WM_XBUTTONUP 0x020C -#define WM_XBUTTONDBLCLK 0x020D -#define WM_NCXBUTTONDOWN 0x00AB -#define WM_NCXBUTTONUP 0x00AC -#define WM_NCXBUTTONDBLCLK 0x00AD -#define MOUSEEVENTF_XDOWN 0x0100 -#define MOUSEEVENTF_XUP 0x0200 -#define XBUTTON1 0x0001 -#define XBUTTON2 0x0002 -#endif - -// multimedia keys -#if !defined(VK_BROWSER_BACK) -#define VK_BROWSER_BACK 0xA6 -#define VK_BROWSER_FORWARD 0xA7 -#define VK_BROWSER_REFRESH 0xA8 -#define VK_BROWSER_STOP 0xA9 -#define VK_BROWSER_SEARCH 0xAA -#define VK_BROWSER_FAVORITES 0xAB -#define VK_BROWSER_HOME 0xAC -#define VK_VOLUME_MUTE 0xAD -#define VK_VOLUME_DOWN 0xAE -#define VK_VOLUME_UP 0xAF -#define VK_MEDIA_NEXT_TRACK 0xB0 -#define VK_MEDIA_PREV_TRACK 0xB1 -#define VK_MEDIA_STOP 0xB2 -#define VK_MEDIA_PLAY_PAUSE 0xB3 -#define VK_LAUNCH_MAIL 0xB4 -#define VK_LAUNCH_MEDIA_SELECT 0xB5 -#define VK_LAUNCH_APP1 0xB6 -#define VK_LAUNCH_APP2 0xB7 -#endif - -// -// CMSWindowsSecondaryScreen -// - -// a list of modifier key info -const CMSWindowsSecondaryScreen::CModifierInfo - CMSWindowsSecondaryScreen::s_modifier[] = { - { KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false }, - { KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false }, - { KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false }, - // note -- no keys for KeyModifierMeta - { KeyModifierSuper, VK_LWIN | 0x100, VK_RWIN | 0x100, false }, - { KeyModifierCapsLock, VK_CAPITAL, 0, true }, - { KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true }, - { KeyModifierScrollLock,VK_SCROLL, 0, true } -}; - -CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( - IScreenReceiver* receiver) : - m_is95Family(CArchMiscWindows::isWindows95Family()), - m_window(NULL) -{ - m_screen = new CMSWindowsScreen(receiver, this); -} - -CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() -{ - assert(m_window == NULL); - - delete m_screen; -} - -CSecondaryScreen::SysKeyID -CMSWindowsSecondaryScreen::getUnhanded(SysKeyID sysKeyID) const -{ - switch (sysKeyID) { - case VK_LSHIFT: - case VK_RSHIFT: - return VK_SHIFT; - - case VK_LCONTROL: - case VK_RCONTROL: - return VK_CONTROL; - - case VK_LMENU: - case VK_RMENU: - return VK_MENU; - - default: - return 0; - } -} - -CSecondaryScreen::SysKeyID -CMSWindowsSecondaryScreen::getOtherHanded(SysKeyID sysKeyID) const -{ - switch (sysKeyID) { - case VK_LSHIFT: - return VK_RSHIFT; - - case VK_RSHIFT: - return VK_LSHIFT; - - case VK_LCONTROL: - return VK_RCONTROL; - - case VK_RCONTROL: - return VK_LCONTROL; - - case VK_LMENU: - return VK_RMENU; - - case VK_RMENU: - return VK_LMENU; - - default: - return 0; - } -} - -bool -CMSWindowsSecondaryScreen::isAutoRepeating(SysKeyID) const -{ - return true; -} - -void -CMSWindowsSecondaryScreen::sync() const -{ - m_screen->syncDesktop(); -} - -IScreen* -CMSWindowsSecondaryScreen::getScreen() const -{ - return m_screen; -} - -void -CMSWindowsSecondaryScreen::onScreensaver(bool) -{ - // ignore -} - -bool -CMSWindowsSecondaryScreen::onPreDispatch(const CEvent*) -{ - return false; -} - -bool -CMSWindowsSecondaryScreen::onEvent(CEvent* event) -{ - assert(event != NULL); - - const MSG& msg = event->m_msg; - switch (msg.message) { - case WM_ACTIVATEAPP: - if (msg.wParam == FALSE) { - // some other app activated. hide the hider window. - ShowWindow(m_window, SW_HIDE); - } - break; - } - - return false; -} - -void -CMSWindowsSecondaryScreen::onOneShotTimerExpired(UInt32) -{ - // ignore -} - -void -CMSWindowsSecondaryScreen::postCreateWindow(HWND window) -{ - m_window = window; - - // update key state - updateKeys(); - - // hide cursor if this screen isn't active - if (!isActive()) { - SInt32 x, y; - getScreen()->getCursorCenter(x, y); - showWindow(x, y); - } -} - -void -CMSWindowsSecondaryScreen::preDestroyWindow(HWND) -{ - // do nothing -} - -void -CMSWindowsSecondaryScreen::onAccessibleDesktop() -{ - // get the current keyboard state - updateKeys(); -} - -void -CMSWindowsSecondaryScreen::onPreMainLoop() -{ - assert(m_window != NULL); -} - -void -CMSWindowsSecondaryScreen::onPreOpen() -{ - assert(m_window == NULL); -} - -void -CMSWindowsSecondaryScreen::onPreEnter() -{ - assert(m_window != NULL); -} - -void -CMSWindowsSecondaryScreen::onPreLeave() -{ - assert(m_window != NULL); -} - -void -CMSWindowsSecondaryScreen::createWindow() -{ - // open the desktop and the window - m_window = m_screen->openDesktop(); - if (m_window == NULL) { - throw XScreenOpenFailure(); - } -} - -void -CMSWindowsSecondaryScreen::destroyWindow() -{ - // release keys that are logically pressed - releaseKeys(); - - // close the desktop and the window - m_screen->closeDesktop(); - m_window = NULL; -} - -void -CMSWindowsSecondaryScreen::showWindow(SInt32 x, SInt32 y) -{ - // move hider window under the given position - MoveWindow(m_window, x, y, 1, 1, FALSE); - - // raise and show the hider window - ShowWindow(m_window, SW_SHOWNA); - - // now warp the mouse - fakeMouseMove(x, y); -} - -void -CMSWindowsSecondaryScreen::hideWindow() -{ - ShowWindow(m_window, SW_HIDE); -} - -CSecondaryScreen::KeyState -CMSWindowsSecondaryScreen::getKeyState(UINT virtualKey) const -{ - BYTE sysState = static_cast(GetKeyState(virtualKey)); - KeyState state = 0; - if (sysState & 0x01u) { - state |= kToggled; - } - if (sysState & 0x80u) { - state |= kDown; - } - return state; -} - -void -CMSWindowsSecondaryScreen::updateKeys(KeyState* keys) -{ - // we only care about the modifier key states - keys[VK_LSHIFT] = getKeyState(VK_LSHIFT); - keys[VK_RSHIFT] = getKeyState(VK_RSHIFT); - keys[VK_SHIFT] = getKeyState(VK_SHIFT); - keys[VK_LCONTROL] = getKeyState(VK_LCONTROL); - keys[VK_RCONTROL] = getKeyState(VK_RCONTROL); - keys[VK_CONTROL] = getKeyState(VK_CONTROL); - keys[VK_LMENU] = getKeyState(VK_LMENU); - keys[VK_RMENU] = getKeyState(VK_RMENU); - keys[VK_MENU] = getKeyState(VK_MENU); - keys[VK_LWIN] = getKeyState(VK_LWIN); - keys[VK_RWIN] = getKeyState(VK_RWIN); - keys[VK_APPS] = getKeyState(VK_APPS); - keys[VK_CAPITAL] = getKeyState(VK_CAPITAL); - keys[VK_NUMLOCK] = getKeyState(VK_NUMLOCK); - keys[VK_SCROLL] = getKeyState(VK_SCROLL); -} - -KeyModifierMask -CMSWindowsSecondaryScreen::getModifiers() const -{ - // update active modifier mask - KeyModifierMask mask = 0; - if (isKeyDown(VK_LSHIFT) || isKeyDown(VK_RSHIFT)) { - mask |= KeyModifierShift; - } - if (isKeyDown(VK_LCONTROL) || isKeyDown(VK_RCONTROL)) { - mask |= KeyModifierControl; - } - if (isKeyDown(VK_LMENU) || isKeyDown(VK_RMENU)) { - mask |= KeyModifierAlt; - } - // note -- no keys for KeyModifierMeta - if (isKeyDown(VK_LWIN) || isKeyDown(VK_RWIN)) { - mask |= KeyModifierSuper; - } - if (isKeyToggled(VK_CAPITAL)) { - mask |= KeyModifierCapsLock; - } - if (isKeyToggled(VK_NUMLOCK)) { - mask |= KeyModifierNumLock; - } - if (isKeyToggled(VK_SCROLL)) { - mask |= KeyModifierScrollLock; - } - // note -- do not save KeyModifierModeSwitch in mask - return mask; -} - -// map special KeyID keys to virtual key codes. if the key is an -// extended key then the entry is the virtual key code | 0x100. -// unmapped keys have a 0 entry. -static const UINT g_mapE000[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, - /* 0xa4 */ 0, 0, VK_BROWSER_BACK|0x100, VK_BROWSER_FORWARD|0x100, - /* 0xa8 */ VK_BROWSER_REFRESH|0x100, VK_BROWSER_STOP|0x100, - /* 0xaa */ VK_BROWSER_SEARCH|0x100, VK_BROWSER_FAVORITES|0x100, - /* 0xac */ VK_BROWSER_HOME|0x100, VK_VOLUME_MUTE|0x100, - /* 0xae */ VK_VOLUME_DOWN|0x100, VK_VOLUME_UP|0x100, - /* 0xb0 */ VK_MEDIA_NEXT_TRACK|0x100, VK_MEDIA_PREV_TRACK|0x100, - /* 0xb2 */ VK_MEDIA_STOP|0x100, VK_MEDIA_PLAY_PAUSE|0x100, - /* 0xb4 */ VK_LAUNCH_MAIL|0x100, VK_LAUNCH_MEDIA_SELECT|0x100, - /* 0xb6 */ VK_LAUNCH_APP1|0x100, VK_LAUNCH_APP2|0x100, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_mapEE00[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ VK_TAB, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const UINT g_mapEF00[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, - /* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0, - /* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ VK_HOME|0x100, VK_LEFT|0x100, VK_UP|0x100, VK_RIGHT|0x100, - /* 0x54 */ VK_DOWN|0x100, VK_PRIOR|0x100, VK_NEXT|0x100, VK_END|0x100, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ VK_SELECT|0x100, VK_SNAPSHOT|0x100, VK_EXECUTE|0x100, VK_INSERT|0x100, - /* 0x64 */ 0, 0, 0, VK_APPS|0x100, - /* 0x68 */ 0, 0, VK_HELP|0x100, VK_CANCEL|0x100, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK|0x100, - /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN|0x100, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, - /* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, - /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, - /* 0xac */ VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100, - /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, - /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, - /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, - /* 0xc0 */ VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, - /* 0xc8 */ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, - /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, - /* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, 0, - /* 0xe8 */ 0, VK_LMENU, VK_RMENU|0x100, VK_LWIN|0x100, - /* 0xec */ VK_RWIN|0x100, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100 -}; - -DWORD -CMSWindowsSecondaryScreen::mapButton(ButtonID button, - bool press, DWORD* inData) const -{ - DWORD dummy; - DWORD* data = (inData != NULL) ? inData : &dummy; - - // the system will swap the meaning of left/right for us if - // the user has configured a left-handed mouse but we don't - // want it to swap since we want the handedness of the - // server's mouse. so pre-swap for a left-handed mouse. - if (GetSystemMetrics(SM_SWAPBUTTON)) { - switch (button) { - case kButtonLeft: - button = kButtonRight; - break; - - case kButtonRight: - button = kButtonLeft; - break; - } - } - - // map button id to button flag and button data - *data = 0; - switch (button) { - case kButtonLeft: - return press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; - - case kButtonMiddle: - return press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; - - case kButtonRight: - return press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; - - case kButtonExtra0 + 0: - *data = XBUTTON1; - return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; - - case kButtonExtra0 + 1: - *data = XBUTTON2; - return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; - - default: - return 0; - } -} - -KeyModifierMask -CMSWindowsSecondaryScreen::mapKey(Keystrokes& keys, - SysKeyID& virtualKey, KeyID id, - KeyModifierMask currentMask, - KeyModifierMask desiredMask, EKeyAction action) const -{ - virtualKey = 0; - - // check for special keys - if ((id & 0xfffff000) == 0xe000) { - if ((id & 0xff00) == 0xe000) { - virtualKey = g_mapE000[id & 0xff]; - } - else if ((id & 0xff00) == 0xee00) { - virtualKey = g_mapEE00[id & 0xff]; - } - else if ((id & 0xff00) == 0xef00) { - virtualKey = g_mapEF00[id & 0xff]; - } - if (virtualKey == 0) { - LOG((CLOG_DEBUG2 "unknown special key")); - return currentMask; - } - } - - // special handling of VK_SNAPSHOT - if ((virtualKey & 0xff) == VK_SNAPSHOT) { - // ignore key repeats on print screen - if (action != kRepeat) { - // get event flags - DWORD flags = 0; - if (isExtendedKey(virtualKey)) { - flags |= KEYEVENTF_EXTENDEDKEY; - } - if (action != kPress) { - flags |= KEYEVENTF_KEYUP; - } - - // active window or fullscreen? - BYTE scan = 0; - if ((desiredMask & KeyModifierAlt) == 0) { - scan = 1; - } - - // send event - keybd_event(static_cast(virtualKey & 0xff), scan, flags, 0); - } - return currentMask; - } - - // handle other special keys - if (virtualKey != 0) { - // compute the final desired modifier mask. special keys use - // the desired modifiers as given except we keep the caps lock, - // num lock, and scroll lock as is. - KeyModifierMask outMask = (currentMask & - (KeyModifierCapsLock | - KeyModifierNumLock | - KeyModifierScrollLock)); - outMask |= (desiredMask & - (KeyModifierShift | - KeyModifierControl | - KeyModifierAlt | - KeyModifierMeta | - KeyModifierSuper)); - - // strip out extended key flag - UINT virtualKey2 = (virtualKey & ~0x100); - - // check numeric keypad. note that virtual keys do not distinguish - // between the keypad and non-keypad movement keys. however, the - // virtual keys do distinguish between keypad numbers and operators - // (e.g. add, multiply) and their main keyboard counterparts. - // therefore, we can ignore the num-lock state for movement virtual - // keys but not for numeric keys. - if (virtualKey2 >= VK_NUMPAD0 && virtualKey2 <= VK_DIVIDE) { - // set required shift state based on current numlock state - if ((outMask & KeyModifierNumLock) == 0) { - if ((currentMask & KeyModifierNumLock) == 0) { - LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); - outMask |= KeyModifierNumLock; - } - else { - LOG((CLOG_DEBUG2 "turn on shift for keypad key")); - outMask |= KeyModifierShift; - } - } - } - - // check for left tab. that requires the shift key. - if (id == kKeyLeftTab) { - // XXX -- this isn't used; outMask meant instead? - desiredMask |= KeyModifierShift; - } - - // now generate the keystrokes and return the resulting modifier mask - LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d mask 0x%04x", id, virtualKey2, outMask)); - return mapToKeystrokes(keys, virtualKey, currentMask, outMask, action); - } - - // determine the thread that'll receive this event - // FIXME -- we can't be sure we'll get the right thread here - HWND targetWindow = GetForegroundWindow(); - DWORD targetThread = GetWindowThreadProcessId(targetWindow, NULL); - - // figure out the code page for the target thread. i'm just - // guessing here. get the target thread's keyboard layout, - // extract the language id from that, and choose the code page - // based on that language. - HKL hkl = GetKeyboardLayout(targetThread); - LANGID langID = static_cast(LOWORD(hkl)); - UINT codePage = getCodePageFromLangID(langID); - LOG((CLOG_DEBUG2 "using code page %d and language id 0x%04x for thread 0x%08x", codePage, langID, targetThread)); - - // regular characters are complicated by dead keys. it may not be - // possible to generate a desired character directly. we may need - // to generate a dead key first then some other character. the - // app receiving the events will compose these two characters into - // a single precomposed character. - // - // as best as i can tell this is the simplest way to convert a - // character into its uncomposed version. along the way we'll - // discover if the key cannot be handled at all. we convert - // from wide char to multibyte, then from multibyte to wide char - // forcing conversion to composite characters, then from wide - // char back to multibyte without making precomposed characters. - // - // after the first conversion to multibyte we see if we can map - // the key. if so then we don't bother trying to decompose dead - // keys. - BOOL error; - char multiByte[2 * MB_LEN_MAX]; - wchar_t unicode[2]; - unicode[0] = static_cast(id & 0x0000ffff); - int nChars = WideCharToMultiByte(codePage, - WC_COMPOSITECHECK | WC_DEFAULTCHAR, - unicode, 1, - multiByte, sizeof(multiByte), - NULL, &error); - if (nChars == 0 || error) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); - return currentMask; - } - virtualKey = mapCharacter(keys, multiByte[0], - hkl, currentMask, desiredMask, action); - if (virtualKey != static_cast(-1)) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - if ((MapVirtualKey(virtualKey, 2) & 0x80000000u) != 0) { - // it looks like this character is a dead key but - // MapVirtualKey() will claim it's a dead key even if it's - // not (though i don't think it ever claims it's not when - // it is). we need a backup test to ensure that this is - // really a dead key. we could use ToAscii() for this but - // that keeps state and it's a hassle to restore that state. - // OemKeyScan() appears to do the trick. if the character - // cannot be generated with a single keystroke then it - // returns 0xffffffff. - if (OemKeyScan(multiByte[0]) != 0xffffffffu) { - // character mapped to a dead key but we want the - // character for real so send a space key afterwards. - LOG((CLOG_DEBUG2 "character mapped to dead key")); - Keystroke keystroke; - keystroke.m_sysKeyID = VK_SPACE; - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); - - // ignore the release of this key since we already - // handled it in mapCharacter(). - virtualKey = 0; - } - } - return currentMask; - } - nChars = MultiByteToWideChar(codePage, - MB_COMPOSITE | MB_ERR_INVALID_CHARS, - multiByte, nChars, - unicode, 2); - if (nChars == 0) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x mb->wc mapping failed", id)); - return currentMask; - } - nChars = WideCharToMultiByte(codePage, - 0, - unicode, nChars, - multiByte, sizeof(multiByte), - NULL, &error); - if (nChars == 0 || error) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x wc->mb mapping failed", id)); - return currentMask; - } - - // we expect one or two characters in multiByte. if there are two - // then the *second* is a dead key. process the dead key if there. - // FIXME -- we assume each character is one byte here - if (nChars > 2) { - LOG((CLOG_DEBUG2 "multibyte characters not supported for character 0x%04x", id)); - return currentMask; - } - if (nChars == 2) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x needs dead key %u", id, (unsigned char)multiByte[1])); - mapCharacter(keys, multiByte[1], - hkl, currentMask, desiredMask, action); - } - - // process character - LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - virtualKey = mapCharacter(keys, multiByte[0], - hkl, currentMask, desiredMask, action); - - // non-special key cannot modify the modifier mask - return currentMask; -} - -KeyModifierMask -CMSWindowsSecondaryScreen::getModifierKeyMask(SysKeyID virtualKey) const -{ - switch (virtualKey) { - case VK_CAPITAL: - return KeyModifierCapsLock; - - case VK_NUMLOCK: - return KeyModifierNumLock; - - case VK_SCROLL: - return KeyModifierScrollLock; - - default: - return 0; - } -} - -bool -CMSWindowsSecondaryScreen::isModifierActive(SysKeyID virtualKey) const -{ - // check if any virtual key for this modifier is down. return false - // for toggle modifiers. - const CModifierInfo* modifier = getModifierInfo(virtualKey); - if (modifier != NULL && !modifier->m_isToggle) { - if (isKeyDown(modifier->m_virtualKey & 0xff) || - isKeyDown(modifier->m_virtualKey2 & 0xff)) { - return true; - } - } - return false; -} - -UINT -CMSWindowsSecondaryScreen::mapCharacter(Keystrokes& keys, - char c, HKL hkl, - KeyModifierMask currentMask, - KeyModifierMask desiredMask, EKeyAction action) const -{ - // translate the character into its virtual key and its required - // modifier state. - SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); - - // get virtual key - UINT virtualKey = LOBYTE(virtualKeyAndModifierState); - if (LOBYTE(virtualKeyAndModifierState) == 0xff) { - LOG((CLOG_DEBUG2 "cannot map character %d", static_cast(c))); - return static_cast(-1); - } - - // get the required modifier state - BYTE modifierState = HIBYTE(virtualKeyAndModifierState); - - // compute the final desired modifier mask. this is the - // desired modifier mask except that the system might require - // that certain modifiers be up or down in order to generate - // the character. to start with, we know that we want to keep - // the caps lock, num lock, scroll lock modifiers as is. also, - // the system never requires the meta or super modifiers so we - // can set those however we like. - KeyModifierMask outMask = (currentMask & - (KeyModifierCapsLock | - KeyModifierNumLock | - KeyModifierScrollLock)); - outMask |= (desiredMask & - (KeyModifierMeta | - KeyModifierSuper)); - - // win32 does not permit ctrl and alt used together to - // modify a character because ctrl and alt together mean - // AltGr. if the desired mask has both ctrl and alt then - // strip them both out. - if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - desiredMask &= ~(KeyModifierControl | KeyModifierAlt); - } - - // strip out the desired shift state. we're forced to use - // a particular shift state to generate the desired character. - outMask &= ~KeyModifierShift; - - // use the required modifiers. if AltGr is required then - // modifierState will indicate control and alt. - if ((modifierState & 1) != 0) { - outMask |= KeyModifierShift; - } - if ((modifierState & 2) != 0) { - outMask |= KeyModifierControl; - } - if ((modifierState & 4) != 0) { - outMask |= KeyModifierAlt; - } - - // use desired modifiers - if ((desiredMask & KeyModifierControl) != 0) { - outMask |= KeyModifierControl; - } - if ((desiredMask & KeyModifierAlt) != 0) { - outMask |= KeyModifierAlt; - } - - // handle combination of caps-lock and shift. if caps-lock is - // off locally then use shift as necessary. if caps-lock is on - // locally then it reverses the meaning of shift for keys that - // are subject to case conversion. - if ((outMask & KeyModifierCapsLock) != 0) { - // there doesn't seem to be a simple way to test if a - // character respects the caps lock key. for normal - // characters it's easy enough but CharLower() and - // CharUpper() don't map dead keys even though they - // do respect caps lock for some unfathomable reason. - // first check the easy way. if that doesn't work - // then see if it's a dead key. - unsigned char uc = static_cast(c); - if (CharLower((LPTSTR)uc) != CharUpper((LPTSTR)uc) || - (MapVirtualKey(virtualKey, 2) & 0x80000000lu) != 0) { - LOG((CLOG_DEBUG2 "flip shift")); - outMask ^= KeyModifierShift; - } - } - - // now generate the keystrokes. ignore the resulting modifier - // mask since it can't have changed (because we don't call this - // method for modifier keys). - LOG((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%04x", (unsigned char)c, virtualKey, outMask)); - mapToKeystrokes(keys, virtualKey, currentMask, outMask, action); - - return virtualKey; -} - -KeyModifierMask -CMSWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, - UINT virtualKey, - KeyModifierMask currentMask, - KeyModifierMask desiredMask, EKeyAction action) const -{ - const CModifierInfo* modifier = getModifierInfo(virtualKey); - - // add the key events required to get to the desired modifier state. - // also save the key events required to restore the current state. - // if the key is a modifier key then skip this because modifiers - // should not modify modifiers. - Keystrokes undo; - Keystroke keystroke; - if (desiredMask != currentMask && modifier == NULL) { - const unsigned int s_numModifiers = sizeof(s_modifier) / - sizeof(s_modifier[0]); - for (unsigned int i = 0; i < s_numModifiers; ++i) { - KeyModifierMask bit = s_modifier[i].m_mask; - if ((desiredMask & bit) != (currentMask & bit)) { - if ((desiredMask & bit) != 0) { - // modifier is not active but should be. if the - // modifier is a toggle then toggle it on with a - // press/release, otherwise activate it with a - // press. - keystroke.m_sysKeyID = s_modifier[i].m_virtualKey; - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - if (s_modifier[i].m_isToggle) { - keystroke.m_press = false; - keys.push_back(keystroke); - undo.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - else { - keystroke.m_press = false; - undo.push_back(keystroke); - } - } - - else { - // modifier is active but should not be. if the - // modifier is a toggle then toggle it off with a - // press/release, otherwise deactivate it with a - // release. we must check each keycode for the - // modifier if not a toggle. - if (s_modifier[i].m_isToggle) { - keystroke.m_sysKeyID = s_modifier[i].m_virtualKey; - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); - undo.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - else { - UINT key = s_modifier[i].m_virtualKey; - if (isKeyDown(key & 0xff)) { - keystroke.m_sysKeyID = key; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - key = s_modifier[i].m_virtualKey2; - if (isKeyDown(key & 0xff)) { - keystroke.m_sysKeyID = key; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - } - } - } - } - } - - // add the key event - keystroke.m_sysKeyID = virtualKey; - switch (action) { - case kPress: - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - break; - - case kRelease: - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - break; - - case kRepeat: - keystroke.m_press = true; - keystroke.m_repeat = true; - keys.push_back(keystroke); - break; - } - - // if this is a dead key press then send a release immediately. - // the dead key may not be processed correctly if its release - // event comes after we release the modifiers. - if (action == kPress && - (MapVirtualKey(virtualKey, 2) & 0x80000000lu) != 0) { - keystroke.m_press = false; - keys.push_back(keystroke); - } - - // add key events to restore the modifier state. apply events in - // the reverse order that they're stored in undo. - while (!undo.empty()) { - keys.push_back(undo.back()); - undo.pop_back(); - } - - // if the key is a modifier key then compute the modifier mask after - // this key is pressed. toggle keys modify the state on release. - // other keys set the modifier bit on press. - KeyModifierMask mask = currentMask; - if (action == kPress) { - if (modifier != NULL && !modifier->m_isToggle) { - mask |= modifier->m_mask; - } - } - - LOG((CLOG_DEBUG2 "previous modifiers 0x%04x, final modifiers 0x%04x", currentMask, mask)); - return mask; -} - - -const CMSWindowsSecondaryScreen::CModifierInfo* -CMSWindowsSecondaryScreen::getModifierInfo(UINT virtualKey) const -{ - // note if the key is a modifier. strip out extended key flag from - // virtual key before lookup. - switch (virtualKey & 0xffu) { - case VK_SHIFT: - case VK_LSHIFT: - case VK_RSHIFT: - return s_modifier + 0; - - case VK_CONTROL: - case VK_LCONTROL: - case VK_RCONTROL: - return s_modifier + 1; - - case VK_MENU: - case VK_LMENU: - case VK_RMENU: - return s_modifier + 2; - - case VK_LWIN: - case VK_RWIN: - return s_modifier + 3; - - case VK_CAPITAL: - return s_modifier + 4; - - case VK_NUMLOCK: - return s_modifier + 5; - - case VK_SCROLL: - return s_modifier + 6; - - default: - return NULL; - } -} - -CSecondaryScreen::SysKeyID -CMSWindowsSecondaryScreen::getToggleSysKey(KeyID keyID) const -{ - switch (keyID) { - case kKeyNumLock: - return VK_NUMLOCK | 0x100; - - case kKeyCapsLock: - return VK_CAPITAL; - - case kKeyScrollLock: - return VK_SCROLL; - - default: - return 0; - } -} - -UINT -CMSWindowsSecondaryScreen::virtualKeyToScanCode(UINT& virtualKey) const -{ - // try mapping given virtual key - UINT code = MapVirtualKey(virtualKey & 0xff, 0); - if (code != 0) { - return code; - } - - // no dice. if the virtual key distinguishes between left/right - // then try the one that doesn't distinguish sides. windows (or - // keyboard drivers) are inconsistent in their treatment of these - // virtual keys. the following behaviors have been observed: - // - // win2k (gateway desktop): - // MapVirtualKey(vk, 0): - // VK_SHIFT == VK_LSHIFT != VK_RSHIFT - // VK_CONTROL == VK_LCONTROL == VK_RCONTROL - // VK_MENU == VK_LMENU == VK_RMENU - // MapVirtualKey(sc, 3): - // VK_LSHIFT and VK_RSHIFT mapped independently - // VK_LCONTROL is mapped but not VK_RCONTROL - // VK_LMENU is mapped but not VK_RMENU - // - // win me (sony vaio laptop): - // MapVirtualKey(vk, 0): - // VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped - // VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped - // VK_MENU mapped; VK_LMENU, VK_RMENU not mapped - // MapVirtualKey(sc, 3): - // all scan codes unmapped (function apparently unimplemented) - switch (virtualKey & 0xff) { - case VK_LSHIFT: - case VK_RSHIFT: - virtualKey = VK_SHIFT; - return MapVirtualKey(VK_SHIFT, 0); - - case VK_LCONTROL: - case VK_RCONTROL: - virtualKey = VK_CONTROL; - return MapVirtualKey(VK_CONTROL, 0); - - case VK_LMENU: - case VK_RMENU: - virtualKey = VK_MENU; - return MapVirtualKey(VK_MENU, 0); - - default: - return 0; - } -} - -bool -CMSWindowsSecondaryScreen::isExtendedKey(UINT virtualKey) const -{ - // see if we've already encoded the extended flag - if ((virtualKey & 0x100) != 0) { - return true; - } - - // check known virtual keys - switch (virtualKey & 0xffu) { - case VK_NUMLOCK: - case VK_RCONTROL: - case VK_RMENU: - case VK_LWIN: - case VK_RWIN: - case VK_APPS: - return true; - - default: - return false; - } -} - -void -CMSWindowsSecondaryScreen::fakeKeyEvent(UINT virtualKey, bool press) const -{ - DWORD flags = 0; - if (isExtendedKey(virtualKey)) { - flags |= KEYEVENTF_EXTENDEDKEY; - } - if (!press) { - flags |= KEYEVENTF_KEYUP; - } - const UINT code = virtualKeyToScanCode(virtualKey); - keybd_event(static_cast(virtualKey & 0xff), - static_cast(code), flags, 0); -} - -void -CMSWindowsSecondaryScreen::fakeMouseButton(ButtonID button, bool press) const -{ - // map button id to button flag - DWORD data; - DWORD flags = mapButton(button, press, &data); - - // send event - if (flags != 0) { - mouse_event(flags, 0, 0, data, 0); - } -} - -void -CMSWindowsSecondaryScreen::fakeMouseMove(SInt32 x, SInt32 y) const -{ - // motion is simple (i.e. it's on the primary monitor) if there - // is only one monitor. - bool simple = !m_screen->isMultimon(); - if (!simple) { - // also simple if motion is within the primary monitor - simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && - y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); - } - - // move the mouse directly to target position if motion is simple - if (simple) { - // when using absolute positioning with mouse_event(), - // the normalized device coordinates range over only - // the primary screen. - SInt32 w = GetSystemMetrics(SM_CXSCREEN); - SInt32 h = GetSystemMetrics(SM_CYSCREEN); - mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65536.0 * x) / w), - (DWORD)((65536.0 * y) / h), - 0, 0); - } - - // windows 98 (and Me?) is broken. you cannot set the absolute - // position of the mouse except on the primary monitor but you - // can do relative moves onto any monitor. this is, in microsoft's - // words, "by design." apparently the designers of windows 2000 - // we're a little less lazy and did it right. - // - // microsoft recommends in Q193003 to absolute position the cursor - // somewhere on the primary monitor then relative move to the - // desired location. this doesn't work for us because when the - // user drags a scrollbar, a window, etc. it causes the dragged - // item to jump back a forth between the position on the primary - // monitor and the desired position. while it always ends up in - // the right place, the effect is disconcerting. - // - // instead we'll get the cursor's current position and do just a - // relative move from there to the desired position. relative - // moves are subject to cursor acceleration which we don't want. - // so we disable acceleration, do the relative move, then restore - // acceleration. there's a slight chance we'll end up in the - // wrong place if the user moves the cursor using this system's - // mouse while simultaneously moving the mouse on the server - // system. that defeats the purpose of synergy so we'll assume - // that won't happen. even if it does, the next mouse move will - // correct the position. - else { - // save mouse speed & acceleration - int oldSpeed[4]; - bool accelChanged = - SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && - SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); - - // use 1:1 motion - if (accelChanged) { - int newSpeed[4] = { 0, 0, 0, 1 }; - accelChanged = - SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || - SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); - } - - // get current mouse position - POINT pos; - GetCursorPos(&pos); - - // move relative to mouse position - mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); - - // restore mouse speed & acceleration - if (accelChanged) { - SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); - SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); - } - } -} - -void -CMSWindowsSecondaryScreen::fakeMouseWheel(SInt32 delta) -{ - mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); -} - -UINT -CMSWindowsSecondaryScreen::getCodePageFromLangID(LANGID langid) const -{ - // construct a locale id from the language id - LCID lcid = MAKELCID(langid, SORT_DEFAULT); - - // get the ANSI code page for this locale - char data[6]; - if (GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, data, 6) == 0) { - // can't get code page - LOG((CLOG_DEBUG1 "can't find code page for langid 0x%04x", langid)); - return CP_ACP; - } - - // convert stringified code page into a number - UINT codePage = static_cast(atoi(data)); - if (codePage == 0) { - // parse failed - LOG((CLOG_DEBUG1 "can't parse code page %s for langid 0x%04x", data, langid)); - return CP_ACP; - } - - return codePage; -} - -bool -CMSWindowsSecondaryScreen::synthesizeCtrlAltDel(EKeyAction action) -{ - // ignore except for key press - if (action != kPress) { - return true; - } - - if (!m_is95Family) { - // to fake ctrl+alt+del on the NT family we broadcast a suitable - // hotkey to all windows on the winlogon desktop. however, we - // the current thread must be on that desktop to do the broadcast - // and we can't switch just any thread because some own windows - // or hooks. so start a new thread to do the real work. - CThread cad(new CFunctionJob( - &CMSWindowsSecondaryScreen::ctrlAltDelThread)); - cad.wait(); - } - else { - Keystrokes keys; - UINT virtualKey; - KeyID key = kKeyDelete; - KeyModifierMask mask = KeyModifierControl | KeyModifierAlt; - - // get the sequence of keys to simulate ctrl+alt+del - mapKey(keys, virtualKey, key, mask, kPress); - if (!keys.empty()) { - // generate key events - doKeystrokes(keys, 1); - } - } - return true; -} - -void -CMSWindowsSecondaryScreen::ctrlAltDelThread(void*) -{ - // get the Winlogon desktop at whatever privilege we can - HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED); - if (desk != NULL) { - if (SetThreadDesktop(desk)) { - PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, - MAKELPARAM(MOD_CONTROL | MOD_ALT, VK_DELETE)); - } - else { - LOG((CLOG_DEBUG "can't switch to Winlogon desk: %d", GetLastError())); - } - CloseDesktop(desk); - } - else { - LOG((CLOG_DEBUG "can't open Winlogon desk: %d", GetLastError())); - } -} diff --git a/lib/platform/CMSWindowsSecondaryScreen.h b/lib/platform/CMSWindowsSecondaryScreen.h deleted file mode 100644 index c402a9ba..00000000 --- a/lib/platform/CMSWindowsSecondaryScreen.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CMSWINDOWSSECONDARYSCREEN_H -#define CMSWINDOWSSECONDARYSCREEN_H - -// ensure that we get SendInput() -#if _WIN32_WINNT <= 0x400 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x401 -#endif - -#include "CSecondaryScreen.h" -#include "IMSWindowsScreenEventHandler.h" -#include "CMutex.h" -#include "CString.h" - -class CMSWindowsScreen; -class IScreenReceiver; - -//! Microsoft windows secondary screen implementation -class CMSWindowsSecondaryScreen : - public CSecondaryScreen, public IMSWindowsScreenEventHandler { -public: - CMSWindowsSecondaryScreen(IScreenReceiver*); - virtual ~CMSWindowsSecondaryScreen(); - - // CSecondaryScreen overrides - virtual IScreen* getScreen() const; - - // IMSWindowsScreenEventHandler overrides - virtual void onScreensaver(bool activated); - virtual bool onPreDispatch(const CEvent* event); - virtual bool onEvent(CEvent* event); - virtual void onOneShotTimerExpired(UInt32 id); - virtual void postCreateWindow(HWND); - virtual void preDestroyWindow(HWND); - virtual void onAccessibleDesktop(); - -protected: - // CSecondaryScreen overrides - virtual void onPreMainLoop(); - virtual void onPreOpen(); - virtual void onPreEnter(); - virtual void onPreLeave(); - virtual void createWindow(); - virtual void destroyWindow(); - virtual void showWindow(SInt32 x, SInt32 y); - virtual void hideWindow(); - virtual void updateKeys(KeyState* sysKeyStates); - virtual KeyModifierMask getModifiers() const; - - virtual SysKeyID getUnhanded(SysKeyID) const; - virtual SysKeyID getOtherHanded(SysKeyID) const; - virtual bool isAutoRepeating(SysKeyID) const; - virtual KeyModifierMask getModifierKeyMask(SysKeyID) const; - virtual bool isModifierActive(SysKeyID) const; - virtual SysKeyID getToggleSysKey(KeyID keyID) const; - virtual bool synthesizeCtrlAltDel(EKeyAction); - virtual void sync() const; - virtual KeyModifierMask - mapKey(Keystrokes&, SysKeyID& sysKeyID, KeyID, - KeyModifierMask, KeyModifierMask, EKeyAction) const; - virtual void fakeKeyEvent(SysKeyID, bool press) const; - virtual void fakeMouseButton(ButtonID, bool press) const; - virtual void fakeMouseMove(SInt32 x, SInt32 y) const; - virtual void fakeMouseWheel(SInt32 delta) const; - -private: - class CModifierInfo { - public: - KeyModifierMask m_mask; - UINT m_virtualKey; - UINT m_virtualKey2; - bool m_isToggle; - }; - - // open/close desktop (for windows 95/98/me) - bool openDesktop(); - void closeDesktop(); - - // make desk the thread desktop (for windows NT/2000/XP) - bool switchDesktop(HDESK desk); - - // returns true iff there appear to be multiple monitors - bool isMultimon() const; - - // key and button queries and operations - DWORD mapButton(ButtonID button, - bool press, DWORD* data) const; - UINT mapCharacter(Keystrokes& keys, - char c, HKL hkl, - KeyModifierMask currentMask, - KeyModifierMask desiredMask, - EKeyAction action) const; - KeyModifierMask mapToKeystrokes(Keystrokes& keys, - UINT virtualKey, - KeyModifierMask currentMask, - KeyModifierMask desiredMask, - EKeyAction action) const; - const CModifierInfo* getModifierInfo(UINT virtualKey) const; - - KeyState getKeyState(UINT virtualKey) const; - UINT virtualKeyToScanCode(UINT& virtualKey) const; - bool isExtendedKey(UINT virtualKey) const; - - UINT getCodePageFromLangID(LANGID) const; - - // thread that generates fake ctrl+alt+del - static void ctrlAltDelThread(void*); - -private: - CMutex m_mutex; - CMSWindowsScreen* m_screen; - - // true if windows 95/98/me - bool m_is95Family; - - // our window - HWND m_window; - - // modifier table - static const CModifierInfo s_modifier[]; -}; - -#endif diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 904d46bd..33022070 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -82,7 +82,7 @@ static HANDLE g_hookEventLL = NULL; static HHOOK g_keyboardLL = NULL; static HHOOK g_mouseLL = NULL; static bool g_screenSaver = false; -static bool g_relay = false; +static EHookMode g_mode = kHOOK_WATCH_JUMP_ZONE; static UInt32 g_zoneSides = 0; static SInt32 g_zoneSize = 0; static SInt32 g_xScreen = 0; @@ -151,7 +151,7 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) // the scroll lock toggle state. PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, wParam, lParam); - if (g_relay) { + if (g_mode == kHOOK_RELAY_EVENTS) { // let certain keys pass through switch (wParam) { case VK_CAPITAL: @@ -202,18 +202,18 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) case WM_NCXBUTTONUP: // always relay the event. eat it if relaying. PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_BUTTON, wParam, data); - return g_relay; + return (g_mode == kHOOK_RELAY_EVENTS); case WM_MOUSEWHEEL: - if (g_relay) { + if (g_mode == kHOOK_RELAY_EVENTS) { // relay event PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, data, 0); } - return g_relay; + return (g_mode == kHOOK_RELAY_EVENTS); case WM_NCMOUSEMOVE: case WM_MOUSEMOVE: - if (g_relay) { + if (g_mode == kHOOK_RELAY_EVENTS) { // we want the cursor to be hidden at all times so we // hide the cursor on whatever window has it. but then // we have to show the cursor whenever we leave that @@ -329,7 +329,7 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam) SYNERGY_MSG_SCREEN_SAVER, TRUE, 0); } } - if (g_relay) { + if (g_mode == kHOOK_RELAY_EVENTS) { MSG* msg = reinterpret_cast(lParam); if (msg->message == g_wmMouseWheel) { // post message to our window @@ -614,7 +614,7 @@ init(DWORD threadID) g_threadID = threadID; // set defaults - g_relay = false; + g_mode = kHOOK_WATCH_JUMP_ZONE; g_zoneSides = 0; g_zoneSize = 0; g_xScreen = 0; @@ -827,14 +827,14 @@ setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize) } void -setRelay(int enable) +setMode(EHookMode mode) { - if ((enable != 0) == g_relay) { + if (mode == g_mode) { // no change return; } - g_relay = (enable != 0); - if (!g_relay) { + g_mode = mode; + if (g_mode != kHOOK_RELAY_EVENTS) { restoreCursor(); } } diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h index 03e72fa7..6953fd70 100644 --- a/lib/platform/CSynergyHook.h +++ b/lib/platform/CSynergyHook.h @@ -49,6 +49,11 @@ enum EHookResult { kHOOK_OKAY_LL }; +enum EHookMode { + kHOOK_WATCH_JUMP_ZONE, + kHOOK_RELAY_EVENTS +}; + typedef int (*InitFunc)(DWORD targetQueueThreadID); typedef int (*CleanupFunc)(void); typedef EHookResult (*InstallFunc)(void); @@ -57,7 +62,7 @@ typedef int (*InstallScreenSaverFunc)(void); typedef int (*UninstallScreenSaverFunc)(void); typedef void (*SetSidesFunc)(UInt32); typedef void (*SetZoneFunc)(SInt32, SInt32, SInt32, SInt32, SInt32); -typedef void (*SetRelayFunc)(int); +typedef void (*SetModeFunc)(int); CSYNERGYHOOK_API int init(DWORD); CSYNERGYHOOK_API int cleanup(void); @@ -68,7 +73,7 @@ CSYNERGYHOOK_API int uninstallScreenSaver(void); CSYNERGYHOOK_API void setSides(UInt32 sides); CSYNERGYHOOK_API void setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize); -CSYNERGYHOOK_API void setRelay(int enable); // relay iff enable != 0 +CSYNERGYHOOK_API void setMode(EHookMode mode); } diff --git a/lib/platform/CXWindowsKeyMapper.cpp b/lib/platform/CXWindowsKeyMapper.cpp new file mode 100644 index 00000000..80ecbc37 --- /dev/null +++ b/lib/platform/CXWindowsKeyMapper.cpp @@ -0,0 +1,910 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsKeyMapper.h" +#include "CXWindowsUtil.h" +#include "CLog.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +# define XK_MISCELLANY +# define XK_XKB_KEYS +# include +# if defined(HAVE_X11_XF86KEYSYM_H) +# include +# endif +# if !defined(XF86XK_Launch0) +# define XF86XK_Launch0 0x1008FF40 +# endif +# if !defined(XF86XK_Launch1) +# define XF86XK_Launch1 0x1008FF41 +# endif +#endif + +// map special KeyID keys to KeySyms +#if defined(HAVE_X11_XF86KEYSYM_H) +static const KeySym g_mapE000[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, + /* 0xa4 */ 0, 0, + /* 0xa6 */ XF86XK_Back, XF86XK_Forward, + /* 0xa8 */ XF86XK_Refresh, XF86XK_Stop, + /* 0xaa */ XF86XK_Search, XF86XK_Favorites, + /* 0xac */ XF86XK_HomePage, XF86XK_AudioMute, + /* 0xae */ XF86XK_AudioLowerVolume, XF86XK_AudioRaiseVolume, + /* 0xb0 */ XF86XK_AudioNext, XF86XK_AudioPrev, + /* 0xb2 */ XF86XK_AudioStop, XF86XK_AudioPlay, + /* 0xb4 */ XF86XK_Mail, XF86XK_AudioMedia, + /* 0xb6 */ XF86XK_Launch0, XF86XK_Launch1, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + +CXWindowsKeyMapper::CXWindowsKeyMapper() +{ + // do nothing +} + +CXWindowsKeyMapper::~CXWindowsKeyMapper() +{ + // do nothing +} + +void +CXWindowsKeyMapper::update(Display* display, IKeyState* keyState) +{ + // query which keys are pressed + char keys[32]; + XQueryKeymap(display, keys); + + // query the pointer to get the keyboard state + Window root = DefaultRootWindow(display), window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (!XQueryPointer(display, root, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + state = 0; + } + + // update mappings + updateKeysymMap(display, keyState); + updateModifiers(); + + // transfer to our state + for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) { + if ((keys[i] & 0x01) != 0) + keyState->setKeyDown(j + 0); + if ((keys[i] & 0x02) != 0) + keyState->setKeyDown(j + 1); + if ((keys[i] & 0x04) != 0) + keyState->setKeyDown(j + 2); + if ((keys[i] & 0x08) != 0) + keyState->setKeyDown(j + 3); + if ((keys[i] & 0x10) != 0) + keyState->setKeyDown(j + 4); + if ((keys[i] & 0x20) != 0) + keyState->setKeyDown(j + 5); + if ((keys[i] & 0x40) != 0) + keyState->setKeyDown(j + 6); + if ((keys[i] & 0x80) != 0) + keyState->setKeyDown(j + 7); + } + + // set toggle modifier states + if ((state & LockMask) != 0) + keyState->setToggled(KeyModifierCapsLock); + if ((state & m_numLockMask) != 0) + keyState->setToggled(KeyModifierNumLock); + if ((state & m_scrollLockMask) != 0) + keyState->setToggled(KeyModifierScrollLock); +} + +KeyButton +CXWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, + const IKeyState& keyState, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + // the system translates key events into characters depending + // on the modifier key state at the time of the event. to + // generate the right keysym we need to set the modifier key + // states appropriately. + // + // desiredMask is the mask desired by the caller. however, there + // may not be a keycode mapping to generate the desired keysym + // with that mask. we override the bits in the mask that cannot + // be accomodated. + + // convert KeyID to a KeySym + KeySym keysym = keyIDToKeySym(id, desiredMask); + if (keysym == NoSymbol) { + // unknown key + return 0; + } + + // get the mapping for this keysym + KeySymIndex keyIndex = m_keysymMap.find(keysym); + + // if the mapping isn't found and keysym is caps lock sensitive + // then convert the case of the keysym and try again. + if (keyIndex == m_keysymMap.end()) { + KeySym lKey, uKey; + XConvertCase(keysym, &lKey, &uKey); + if (lKey != uKey) { + if (lKey == keysym) { + keyIndex = m_keysymMap.find(uKey); + } + else { + keyIndex = m_keysymMap.find(lKey); + } + } + } + + if (keyIndex != m_keysymMap.end()) { + // the keysym is mapped to some keycode. create the keystrokes + // for this keysym. + return mapToKeystrokes(keys, keyState, keyIndex, isAutoRepeat); + } + + // we can't find the keysym mapped to any keycode. this doesn't + // necessarily mean we can't generate the keysym, though. if the + // keysym can be created by combining keysyms then we may still + // be okay. + CXWindowsUtil::KeySyms decomposition; + if (!CXWindowsUtil::decomposeKeySym(keysym, decomposition)) { + return 0; + } + LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms", keysym, decomposition.size())); + + // map each decomposed keysym to keystrokes. we want the mask + // and the keycode from the last keysym (which should be the + // only non-dead key). the dead keys are not sensitive to + // anything but shift and mode switch. + KeyButton keycode = 0; + for (CXWindowsUtil::KeySyms::const_iterator i = decomposition.begin(); + i != decomposition.end(); ++i) { + // lookup the key + keysym = *i; + keyIndex = m_keysymMap.find(keysym); + if (keyIndex == m_keysymMap.end()) { + // missing a required keysym + return 0; + } + + // the keysym is mapped to some keycode + keycode = mapToKeystrokes(keys, keyState, keyIndex, isAutoRepeat); + if (keycode == 0) { + return 0; + } + } + + return keycode; +} + +KeyModifierMask +CXWindowsKeyMapper::mapModifier(unsigned int state) const +{ + KeyModifierMask mask = 0; + if (state & ShiftMask) + mask |= KeyModifierShift; + if (state & LockMask) + mask |= KeyModifierCapsLock; + if (state & ControlMask) + mask |= KeyModifierControl; + if (state & m_altMask) + mask |= KeyModifierAlt; + if (state & m_metaMask) + mask |= KeyModifierMeta; + if (state & m_superMask) + mask |= KeyModifierSuper; + if (state & m_modeSwitchMask) + mask |= KeyModifierModeSwitch; + if (state & m_numLockMask) + mask |= KeyModifierNumLock; + if (state & m_scrollLockMask) + mask |= KeyModifierScrollLock; + return mask; +} + +void +CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState) +{ + // there are up to 4 keysyms per keycode + static const unsigned int maxKeysyms = 4; + + // get the number of keycodes + int minKeycode, maxKeycode; + XDisplayKeycodes(display, &minKeycode, &maxKeycode); + const int numKeycodes = maxKeycode - minKeycode + 1; + + // get the keyboard mapping for all keys + int keysymsPerKeycode; + KeySym* keysyms = XGetKeyboardMapping(display, + minKeycode, numKeycodes, + &keysymsPerKeycode); + + // we only understand up to maxKeysyms keysyms per keycodes + unsigned int numKeysyms = keysymsPerKeycode; + if (numKeysyms > maxKeysyms) { + numKeysyms = maxKeysyms; + } + + // determine shift and mode switch sensitivity. a keysym is shift + // or mode switch sensitive if its keycode is. a keycode is mode + // mode switch sensitive if it has keysyms for indices 2 or 3. + // it's shift sensitive if the keysym for index 1 (if any) is + // different from the keysym for index 0 and, if the keysym for + // for index 3 (if any) is different from the keysym for index 2. + // that is, if shift changes the generated keysym for the keycode. + std::vector usesShift(numKeycodes); + std::vector usesModeSwitch(numKeycodes); + for (int i = 0; i < numKeycodes; ++i) { + // check mode switch first + if (numKeysyms > 2 && + keysyms[i * keysymsPerKeycode + 2] != NoSymbol || + keysyms[i * keysymsPerKeycode + 3] != NoSymbol) { + usesModeSwitch[i] = true; + } + + // check index 0 with index 1 keysyms + if (keysyms[i * keysymsPerKeycode + 0] != NoSymbol && + keysyms[i * keysymsPerKeycode + 1] != NoSymbol && + keysyms[i * keysymsPerKeycode + 1] != + keysyms[i * keysymsPerKeycode + 0]) { + usesShift[i] = true; + } + + else if (numKeysyms >= 4 && + keysyms[i * keysymsPerKeycode + 2] != NoSymbol && + keysyms[i * keysymsPerKeycode + 3] != NoSymbol && + keysyms[i * keysymsPerKeycode + 3] != + keysyms[i * keysymsPerKeycode + 2]) { + usesShift[i] = true; + } + } + + // get modifier map from server + XModifierKeymap* modifiers = XGetModifierMapping(display); + int keysPerModifier = modifiers->max_keypermod; + + // clear state + m_keysymMap.clear(); + m_modeSwitchKeysym = NoSymbol; + m_altMask = 0; + m_metaMask = 0; + m_superMask = 0; + m_modeSwitchMask = 0; + m_numLockMask = 0; + m_scrollLockMask = 0; + + // work around for my system, which reports this state bit when + // mode switch is down, instead of the appropriate modifier bit. + // should have no effect on other systems. -crs 9/02. + m_modeSwitchMask |= (1 << 13); + + // for each modifier keycode, get the index 0 keycode and add it to + // the keysym map. also collect all keycodes for each modifier. + for (unsigned int i = 0; i < 8; ++i) { + // no keycodes for this modifier yet + bool hasKeycode = false; + KeyModifierMask mask = 0; + IKeyState::KeyButtons modifierKeys; + + // add each keycode for modifier + for (unsigned int j = 0; j < keysPerModifier; ++j) { + // get keycode and ignore unset keycodes + KeyCode keycode = modifiers->modifiermap[i * keysPerModifier + j]; + if (keycode == 0) { + continue; + } + + // get keysym and get/create key mapping + const int keycodeIndex = keycode - minKeycode; + const KeySym keysym = keysyms[keycodeIndex * + keysymsPerKeycode + 0]; + + // get modifier mask if we haven't yet. this has the side + // effect of setting the m_*Mask members. + if (mask == 0) { + mask = mapToModifierMask(i, keysym); + if (mask == 0) { + continue; + } + } + + // save keycode for modifier + modifierKeys.push_back(keycode); + + // skip if we already have a keycode for this index + KeyMapping& mapping = m_keysymMap[keysym]; + if (mapping.m_keycode[0] != 0) { + continue; + } + + // fill in keysym info + mapping.m_keycode[0] = keycode; + mapping.m_shiftSensitive[0] = usesShift[keycodeIndex]; + mapping.m_modeSwitchSensitive[0] = usesModeSwitch[keycodeIndex]; + mapping.m_modifierMask = mask; + mapping.m_capsLockSensitive = false; + mapping.m_numLockSensitive = false; + } + + // tell keyState about this modifier + if (mask != 0 && keyState != NULL) { + keyState->addModifier(mask, modifierKeys); + } + } + + // create a convenient NoSymbol entry (if it doesn't exist yet). + // sometimes it's useful to handle NoSymbol like a normal keysym. + // remove any entry for NoSymbol. that keysym doesn't count. + { + KeyMapping& mapping = m_keysymMap[NoSymbol]; + for (unsigned int i = 0; i < numKeysyms; ++i) { + mapping.m_keycode[i] = 0; + mapping.m_shiftSensitive[i] = false; + mapping.m_modeSwitchSensitive[i] = false; + } + mapping.m_modifierMask = 0; + mapping.m_capsLockSensitive = false; + mapping.m_numLockSensitive = false; + } + + // add each keysym to the map, unless we've already inserted a key + // for that keysym index. + for (int i = 0; i < numKeycodes; ++i) { + for (unsigned int j = 0; j < numKeysyms; ++j) { + // lookup keysym + const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; + if (keysym == NoSymbol) { + continue; + } + KeyMapping& mapping = m_keysymMap[keysym]; + + // skip if we already have a keycode for this index + if (mapping.m_keycode[j] != 0) { + continue; + } + + // fill in keysym info + if (mapping.m_keycode[0] == 0) { + mapping.m_modifierMask = 0; + } + mapping.m_keycode[j] = static_cast( + minKeycode + i); + mapping.m_shiftSensitive[j] = usesShift[i]; + mapping.m_modeSwitchSensitive[j] = usesModeSwitch[i]; + mapping.m_numLockSensitive = isNumLockSensitive(keysym); + mapping.m_capsLockSensitive = isCapsLockSensitive(keysym); + } + } + + // clean up + XFreeModifiermap(modifiers); + XFree(keysyms); +} + +KeyModifierMask +CXWindowsKeyMapper::mapToModifierMask(unsigned int i, KeySym keysym) +{ + // some modifier indices (0,1,2) are dedicated to particular uses, + // the rest depend on the keysyms bound. + switch (i) { + case 0: + return KeyModifierShift; + + case 1: + return KeyModifierCapsLock; + + case 2: + return KeyModifierControl; + + default: + switch (keysym) { + case XK_Shift_L: + case XK_Shift_R: + return KeyModifierShift; + + case XK_Control_L: + case XK_Control_R: + return KeyModifierControl; + + case XK_Alt_L: + case XK_Alt_R: + m_altMask = (1 << i); + return KeyModifierAlt; + + case XK_Meta_L: + case XK_Meta_R: + m_metaMask = (1 << i); + return KeyModifierMeta; + + case XK_Super_L: + case XK_Super_R: + m_superMask = (1 << i); + return KeyModifierSuper; + + case XK_Mode_switch: + m_modeSwitchMask = (1 << i); + return KeyModifierModeSwitch; + + case XK_Caps_Lock: + return KeyModifierCapsLock; + + case XK_Num_Lock: + m_numLockMask = (1 << i); + return KeyModifierNumLock; + + case XK_Scroll_Lock: + m_scrollLockMask = (1 << i); + return KeyModifierScrollLock; + + default: + return 0; + } + } +} + +void +CXWindowsKeyMapper::updateModifiers() +{ + struct CModifierBitInfo { + public: + KeySym CXWindowsKeyMapper::*m_keysym; + KeySym m_left; + KeySym m_right; + }; + static const CModifierBitInfo s_modifierBitTable[] = { + { &CXWindowsKeyMapper::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol }, + }; + + // choose the keysym to use for some modifiers. if a modifier has + // both left and right versions then (arbitrarily) prefer the left. + for (size_t i = 0; i < sizeof(s_modifierBitTable) / + sizeof(s_modifierBitTable[0]); ++i) { + const CModifierBitInfo& info = s_modifierBitTable[i]; + + // find available keysym + KeySymIndex keyIndex = m_keysymMap.find(info.m_left); + if (keyIndex == m_keysymMap.end() && info.m_right != NoSymbol) { + keyIndex = m_keysymMap.find(info.m_right); + } + + // save modifier info + if (keyIndex != m_keysymMap.end() && + keyIndex->second.m_modifierMask != 0) { + this->*(info.m_keysym) = keyIndex->first; + } + } + + // if there's no mode switch key mapped then remove all keycodes + // that depend on it and no keycode can be mode switch sensitive. + if (m_modeSwitchKeysym == NoSymbol) { + LOG((CLOG_DEBUG2 "no mode switch in keymap")); + for (KeySymMap::iterator i = m_keysymMap.begin(); + i != m_keysymMap.end(); ) { + i->second.m_keycode[2] = 0; + i->second.m_keycode[3] = 0; + i->second.m_modeSwitchSensitive[0] = false; + i->second.m_modeSwitchSensitive[1] = false; + i->second.m_modeSwitchSensitive[2] = false; + i->second.m_modeSwitchSensitive[3] = false; + + // if this keysym no has no keycodes then remove it + // except for the NoSymbol keysym mapping. + if (i->second.m_keycode[0] == 0 && i->second.m_keycode[1] == 0) { + m_keysymMap.erase(i++); + } + else { + ++i; + } + } + } +} + +KeySym +CXWindowsKeyMapper::keyIDToKeySym(KeyID id, KeyModifierMask mask) const +{ + // convert id to keysym + KeySym keysym = NoSymbol; + if ((id & 0xfffff000) == 0xe000) { + // special character + switch (id & 0x0000ff00) { +#if defined(HAVE_X11_XF86KEYSYM_H) + case 0xe000: + return g_mapE000[id & 0xff]; +#endif + + case 0xee00: + // ISO 9995 Function and Modifier Keys + if (id == kKeyLeftTab) { + keysym = XK_ISO_Left_Tab; + } + break; + + case 0xef00: + // MISCELLANY + keysym = static_cast(id - 0xef00 + 0xff00); + break; + } + } + else if ((id >= 0x0020 && id <= 0x007e) || + (id >= 0x00a0 && id <= 0x00ff)) { + // Latin-1 maps directly + return static_cast(id); + } + else { + // lookup keysym in table + return CXWindowsUtil::mapUCS4ToKeySym(id); + } + + // fail if unknown key + if (keysym == NoSymbol) { + return keysym; + } + + // if kKeyTab is requested with shift active then try XK_ISO_Left_Tab + // instead. if that doesn't work, we'll fall back to XK_Tab with + // shift active. this is to handle primary screens that don't map + // XK_ISO_Left_Tab sending events to secondary screens that do. + if (keysym == XK_Tab && (mask & KeyModifierShift) != 0) { + keysym = XK_ISO_Left_Tab; + } + + // some keysyms have emergency backups (particularly the numpad + // keys since most laptops don't have a separate numpad and the + // numpad overlaying the main keyboard may not have movement + // key bindings). figure out the emergency backup. + KeySym backupKeysym; + switch (keysym) { + case XK_KP_Home: + backupKeysym = XK_Home; + break; + + case XK_KP_Left: + backupKeysym = XK_Left; + break; + + case XK_KP_Up: + backupKeysym = XK_Up; + break; + + case XK_KP_Right: + backupKeysym = XK_Right; + break; + + case XK_KP_Down: + backupKeysym = XK_Down; + break; + + case XK_KP_Prior: + backupKeysym = XK_Prior; + break; + + case XK_KP_Next: + backupKeysym = XK_Next; + break; + + case XK_KP_End: + backupKeysym = XK_End; + break; + + case XK_KP_Insert: + backupKeysym = XK_Insert; + break; + + case XK_KP_Delete: + backupKeysym = XK_Delete; + break; + + case XK_ISO_Left_Tab: + backupKeysym = XK_Tab; + break; + + default: + backupKeysym = keysym; + break; + } + + // see if the keysym is assigned to any keycode. if not and the + // backup keysym is then use the backup keysym. + if (backupKeysym != keysym && + m_keysymMap.find(keysym) == m_keysymMap.end() && + m_keysymMap.find(backupKeysym) != m_keysymMap.end()) { + keysym = backupKeysym; + } + + return keysym; +} + +KeyButton +CXWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, + const IKeyState& keyState, + KeySymIndex keyIndex, + bool isAutoRepeat) const +{ + // keyIndex must be valid + assert(keyIndex != m_keysymMap.end()); + + KeyModifierMask currentMask = keyState.getActiveModifiers(); + + // get the keysym we're trying to generate and possible keycodes + const KeySym keysym = keyIndex->first; + const KeyMapping& mapping = keyIndex->second; + LOG((CLOG_DEBUG2 "keysym = 0x%08x", keysym)); + + // get the best keycode index for the keysym and modifiers. note + // that (bestIndex & 1) == 0 if the keycode is a shift modifier + // and (bestIndex & 2) == 0 if the keycode is a mode switch + // modifier. this is important later because we don't want + // adjustModifiers() to adjust a modifier if that's the key we're + // mapping. + unsigned int bestIndex = findBestKeyIndex(keyIndex, currentMask); + + // get the keycode + KeyButton keycode = mapping.m_keycode[bestIndex]; + + // flip low bit of bestIndex if shift is inverted. if there's a + // keycode for this new index then use it. otherwise use the old + // keycode. you'd think we should fail if there isn't a keycode + // for the new index but some keymaps only include the upper case + // keysyms (notably those on Sun Solaris) so to handle the missing + // lower case keysyms we just use the old keycode. note that + // isShiftInverted() will always return false for a shift modifier. + if (isShiftInverted(keyIndex, currentMask)) { + LOG((CLOG_DEBUG2 "shift is inverted")); + bestIndex ^= 1; + if (mapping.m_keycode[bestIndex] != 0) { + keycode = mapping.m_keycode[bestIndex]; + } + } + LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode)); + + // if this for auto-repeat and this key does not auto-repeat + // then return 0. + if (isAutoRepeat && + (m_keyControl.auto_repeats[keycode >> 3] & + static_cast(1 << (keycode & 7))) == 0) { + return 0; + } + + // compute desired mask. the desired mask is the one that matches + // bestIndex, except if the key being synthesized is a shift key + // where we desire what we already have or if it's the mode switch + // key where we only desire to adjust shift. also, if the keycode + // is not sensitive to shift then don't adjust it, otherwise + // something like shift+home would become just home. similiarly + // for mode switch. + KeyModifierMask desiredMask = currentMask; + if (keyIndex->second.m_modifierMask != KeyModifierShift) { + if (keyIndex->second.m_shiftSensitive[bestIndex]) { + if ((bestIndex & 1) != 0) { + desiredMask |= KeyModifierShift; + } + else { + desiredMask &= ~KeyModifierShift; + } + } + if (keyIndex->second.m_modifierMask != KeyModifierModeSwitch) { + if (keyIndex->second.m_modeSwitchSensitive[bestIndex]) { + if ((bestIndex & 2) != 0) { + desiredMask |= KeyModifierModeSwitch; + } + else { + desiredMask &= ~KeyModifierModeSwitch; + } + } + } + } + + // adjust the modifiers to match the desired modifiers + IKeyState::Keystrokes undo; + if (!adjustModifiers(keys, undo, keyState, desiredMask)) { + LOG((CLOG_DEBUG2 "failed to adjust modifiers")); + return 0; + } + + // add the key event + IKeyState::Keystroke keystroke; + keystroke.m_key = keycode; + if (!isAutoRepeat) { + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + } + else { + keystroke.m_press = false; + keystroke.m_repeat = true; + keys.push_back(keystroke); + keystroke.m_press = true; + keys.push_back(keystroke); + } + + // put undo keystrokes at end of keystrokes in reverse order + while (!undo.empty()) { + keys.push_back(undo.back()); + undo.pop_back(); + } + + return keycode; +} + +unsigned int +CXWindowsKeyMapper::findBestKeyIndex(KeySymIndex keyIndex, + KeyModifierMask /*currentMask*/) const +{ + // there are up to 4 keycodes per keysym to choose from. the + // best choice is the one that requires the fewest adjustments + // to the modifier state. for example, the letter A normally + // requires shift + a. if shift isn't already down we'd have + // to synthesize a shift press before the a press. however, + // if A could also be created with some other keycode without + // shift then we'd prefer that when shift wasn't down. + // + // if the action is an auto-repeat then we don't call this + // method since we just need to synthesize a key repeat on the + // same keycode that we pressed. + // XXX -- do this right + for (unsigned int i = 0; i < 4; ++i) { + if (keyIndex->second.m_keycode[i] != 0) { + return i; + } + } + + assert(0 && "no keycode found for keysym"); + return 0; +} + +bool +CXWindowsKeyMapper::isShiftInverted(KeySymIndex keyIndex, + KeyModifierMask currentMask) const +{ + // each keycode has up to 4 keysym associated with it, one each for: + // no modifiers, shift, mode switch, and shift and mode switch. if + // a keysym is modified by num lock and num lock is active then you + // get the shifted keysym when shift is not down and the unshifted + // keysym when it is. that is, num lock inverts the sense of the + // shift modifier when active. similarly for caps lock. this + // method returns true iff the sense of shift should be inverted + // for this key given a modifier state. + if (keyIndex->second.m_numLockSensitive) { + if ((currentMask & KeyModifierNumLock) != 0) { + return true; + } + } + + // if a keysym is num lock sensitive it is never caps lock + // sensitive, thus the else here. + else if (keyIndex->second.m_capsLockSensitive) { + if ((currentMask & KeyModifierCapsLock) != 0) { + return true; + } + } + + return false; +} + +bool +CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, + IKeyState::Keystrokes& undo, + const IKeyState& keyState, + KeyModifierMask desiredMask) const +{ + KeyModifierMask currentMask = keyState.getActiveModifiers(); + + // get mode switch set correctly. do this before shift because + // mode switch may be sensitive to the shift modifier and will + // set/reset it as necessary. + const bool wantModeSwitch = ((desiredMask & KeyModifierModeSwitch) != 0); + const bool haveModeSwitch = ((currentMask & KeyModifierModeSwitch) != 0); + if (wantModeSwitch != haveModeSwitch) { + LOG((CLOG_DEBUG2 "fix mode switch")); + + // adjust shift if necessary + KeySymIndex modeSwitchIndex = m_keysymMap.find(m_modeSwitchKeysym); + assert(modeSwitchIndex != m_keysymMap.end()); + if (modeSwitchIndex->second.m_shiftSensitive[0]) { + const bool wantShift = false; + const bool haveShift = ((currentMask & KeyModifierShift) != 0); + if (wantShift != haveShift) { + // add shift keystrokes + LOG((CLOG_DEBUG2 "fix shift for mode switch")); + if (!keyState.mapModifier(keys, undo, + KeyModifierShift, wantShift)) { + return false; + } + currentMask ^= KeyModifierShift; + } + } + + // add mode switch keystrokes + if (!keyState.mapModifier(keys, undo, + KeyModifierModeSwitch, wantModeSwitch)) { + return false; + } + currentMask ^= KeyModifierModeSwitch; + } + + // get shift set correctly + const bool wantShift = ((desiredMask & KeyModifierShift) != 0); + const bool haveShift = ((currentMask & KeyModifierShift) != 0); + if (wantShift != haveShift) { + // add shift keystrokes + LOG((CLOG_DEBUG2 "fix shift")); + if (!keyState.mapModifier(keys, undo, KeyModifierShift, wantShift)) { + return false; + } + currentMask ^= KeyModifierShift; + } + + return true; +} + +bool +CXWindowsKeyMapper::isNumLockSensitive(KeySym keysym) const +{ + return (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)); +} + +bool +CXWindowsKeyMapper::isCapsLockSensitive(KeySym keysym) const +{ + KeySym lKey, uKey; + XConvertCase(keysym, &lKey, &uKey); + return (lKey != uKey); +} + + +// +// CXWindowsKeyMapper::KeyMapping +// + +CXWindowsKeyMapper::KeyMapping::KeyMapping() +{ + m_keycode[0] = 0; + m_keycode[1] = 0; + m_keycode[2] = 0; + m_keycode[3] = 0; +} diff --git a/lib/platform/CXWindowsKeyMapper.h b/lib/platform/CXWindowsKeyMapper.h new file mode 100644 index 00000000..0c707aa5 --- /dev/null +++ b/lib/platform/CXWindowsKeyMapper.h @@ -0,0 +1,153 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSKEYMAPPER_H +#define CXWINDOWSKEYMAPPER_H + +#include "IKeyState.h" +#include "stdmap.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +//! X Windows key mapper +/*! +This class maps KeyIDs to keystrokes. +*/ +class CXWindowsKeyMapper { +public: + CXWindowsKeyMapper(); + ~CXWindowsKeyMapper(); + + //! @name manipulators + //@{ + + //! Update key mapper + /*! + Updates the key mapper's internal tables according to the display's + current keyboard mapping and updates \c keyState. + */ + void update(Display*, IKeyState* keyState); + + //@} + //! @name accessors + //@{ + + //! Map key press/repeat to keystrokes + /*! + Converts a press/repeat of key \c id with the modifiers as given + in \c desiredMask into the keystrokes necessary to synthesize + that key event. Returns the platform specific code of the key + being pressed, or 0 if the key cannot be mapped or \c isAutoRepeat + is true and the key does not auto-repeat. + */ + KeyButton mapKey(IKeyState::Keystrokes&, + const IKeyState& keyState, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + + //! Convert X modifier mask to synergy mask + /*! + Returns the synergy modifier mask corresponding to the given X + modifier mask. + */ + KeyModifierMask mapModifier(unsigned int state) const; + + //@} + +private: + class KeyMapping { + public: + KeyMapping(); + + public: + // KeyCode to generate keysym and whether keycode[i] is + // sensitive to shift and mode switch. + KeyCode m_keycode[4]; + bool m_shiftSensitive[4]; + bool m_modeSwitchSensitive[4]; + + // the modifier mask of keysym or 0 if not a modifier + KeyModifierMask m_modifierMask; + + // whether keysym is sensitive to caps and num lock + bool m_numLockSensitive; + bool m_capsLockSensitive; + }; + typedef std::map KeySymMap; + typedef KeySymMap::const_iterator KeySymIndex; + + // save the current keyboard mapping and note the currently + // pressed keys in \c keyState. + void updateKeysymMap(Display* display, IKeyState* keyState); + + // note interesting modifier KeySyms + void updateModifiers(); + + // map a modifier index and its KeySym to a modifier mask. also + // save the modifier mask in one of m_*Mask. + KeyModifierMask mapToModifierMask(unsigned int, KeySym); + + // convert a KeyID to a KeySym + KeySym keyIDToKeySym(KeyID id, KeyModifierMask mask) const; + + // map a KeySym into the keystrokes to produce it + KeyButton mapToKeystrokes(IKeyState::Keystrokes& keys, + const IKeyState& keyState, + KeySymIndex keyIndex, + bool isAutoRepeat) const; + + // choose the best set of modifiers to generate the KeySym + unsigned int findBestKeyIndex(KeySymIndex keyIndex, + KeyModifierMask currentMask) const; + + // returns true if the sense of shift is inverted for KeySym + bool isShiftInverted(KeySymIndex keyIndex, + KeyModifierMask currentMask) const; + + // returns the keystrokes to adjust the modifiers into the desired + // state the keystrokes to get back to the current state. + bool adjustModifiers(IKeyState::Keystrokes& keys, + IKeyState::Keystrokes& undo, + const IKeyState& keyState, + KeyModifierMask desiredMask) const; + + // returns true if keysym is sensitive to the NumLock state + bool isNumLockSensitive(KeySym keysym) const; + + // returns true if keysym is sensitive to the CapsLock state + bool isCapsLockSensitive(KeySym keysym) const; + +private: + // keysym to keycode mapping + KeySymMap m_keysymMap; + + // the keyboard control state the last time this screen was entered + XKeyboardState m_keyControl; + + // modifier keysyms + KeySym m_modeSwitchKeysym; + + // modifier masks + unsigned int m_altMask; + unsigned int m_metaMask; + unsigned int m_superMask; + unsigned int m_modeSwitchMask; + unsigned int m_numLockMask; + unsigned int m_scrollLockMask; +}; + +#endif diff --git a/lib/platform/CXWindowsPrimaryScreen.cpp b/lib/platform/CXWindowsPrimaryScreen.cpp deleted file mode 100644 index 360bea85..00000000 --- a/lib/platform/CXWindowsPrimaryScreen.cpp +++ /dev/null @@ -1,1044 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CXWindowsPrimaryScreen.h" -#include "CXWindowsScreen.h" -#include "CXWindowsUtil.h" -#include "IPrimaryScreenReceiver.h" -#include "XScreen.h" -#include "CThread.h" -#include "CLog.h" -#include "CStopwatch.h" -#if defined(X_DISPLAY_MISSING) -# error X11 is required to build synergy -#else -# include -# include -# define XK_MISCELLANY -# define XK_XKB_KEYS -# include -#endif -#include "CArch.h" - -// -// CXWindowsPrimaryScreen -// - -CXWindowsPrimaryScreen::CXWindowsPrimaryScreen( - IScreenReceiver* receiver, - IPrimaryScreenReceiver* primaryReceiver) : - CPrimaryScreen(receiver), - m_receiver(primaryReceiver), - m_window(None), - m_im(NULL), - m_ic(NULL), - m_lastKeycode(0) -{ - m_screen = new CXWindowsScreen(receiver, this); -} - -CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() -{ - assert(m_window == None); - delete m_screen; -} - -void -CXWindowsPrimaryScreen::reconfigure(UInt32) -{ - // do nothing -} - -void -CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) -{ - CDisplayLock display(m_screen); - - // warp mouse - warpCursorNoFlush(display, x, y); - - // remove all input events before and including warp - XEvent event; - while (XCheckMaskEvent(display, PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask, - &event)) { - // do nothing - } - - // save position as last position - m_x = x; - m_y = y; -} - -void -CXWindowsPrimaryScreen::resetOptions() -{ - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; -} - -void -CXWindowsPrimaryScreen::setOptions(const COptionsList& options) -{ - for (UInt32 i = 0, n = options.size(); i < n; i += 2) { - if (options[i] == kOptionHalfDuplexCapsLock) { - m_capsLockHalfDuplex = (options[i + 1] != 0); - LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); - } - else if (options[i] == kOptionHalfDuplexNumLock) { - m_numLockHalfDuplex = (options[i + 1] != 0); - LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); - } - } -} - -UInt32 -CXWindowsPrimaryScreen::addOneShotTimer(double timeout) -{ - return m_screen->addOneShotTimer(timeout); -} - -KeyModifierMask -CXWindowsPrimaryScreen::getToggleMask() const -{ - CDisplayLock display(m_screen); - - // query the pointer to get the keyboard state - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int state; - if (!XQueryPointer(display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &state)) { - return 0; - } - - // convert to KeyModifierMask - KeyModifierMask mask = 0; - if (state & m_numLockMask) { - mask |= KeyModifierNumLock; - } - if (state & m_capsLockMask) { - mask |= KeyModifierCapsLock; - } - if (state & m_scrollLockMask) { - mask |= KeyModifierScrollLock; - } - - return mask; -} - -bool -CXWindowsPrimaryScreen::isLockedToScreen() const -{ - CDisplayLock display(m_screen); - - // query the pointer to get the button state - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int state; - if (XQueryPointer(display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &state)) { - if ((state & (Button1Mask | Button2Mask | Button3Mask | - Button4Mask | Button5Mask)) != 0) { - LOG((CLOG_DEBUG "locked by mouse button")); - return true; - } - } - - // get logical keyboard state - char keyMap[32]; - memset(keyMap, 0, sizeof(keyMap)); - XQueryKeymap(display, keyMap); - - // locked if any key is down - for (unsigned int i = 0; i < sizeof(keyMap); ++i) { - if (keyMap[i] != 0) { - for (unsigned int j = 0; j < 8; ++j) { - if ((keyMap[i] & (1 << j)) != 0) { - const KeyCode keycode = 8 * i + j; - const KeySym keysym = XKeycodeToKeysym(display, - keycode, 0); - // if any key is half-duplex then it'll be down when - // toggled on but shouldn't count as a reason to lock - // to the screen. - if (m_numLockHalfDuplex && keysym == XK_Num_Lock) { - continue; - } - if (m_capsLockHalfDuplex && keysym == XK_Caps_Lock) { - continue; - } - - // non-half-duplex key down - char* name = XKeysymToString(keysym); - if (name == NULL) { - LOG((CLOG_DEBUG "locked by keycode %d", keycode)); - } - else { - LOG((CLOG_DEBUG "locked by \"%s\"", name)); - } - return true; - } - } - } - } - - // not locked - return false; -} - -IScreen* -CXWindowsPrimaryScreen::getScreen() const -{ - return m_screen; -} - -void -CXWindowsPrimaryScreen::onScreensaver(bool activated) -{ - m_receiver->onScreensaver(activated); -} - -bool -CXWindowsPrimaryScreen::onPreDispatch(const CEvent*) -{ - return false; -} - -bool -CXWindowsPrimaryScreen::onEvent(CEvent* event) -{ - assert(event != NULL); - XEvent& xevent = event->m_event; - - // let input methods try to handle event first - if (m_ic != NULL) { - // XFilterEvent() may eat the event and generate a new KeyPress - // event with a keycode of 0 because there isn't an actual key - // associated with the keysym. but the KeyRelease may pass - // through XFilterEvent() and keep its keycode. this means - // there's a mismatch between KeyPress and KeyRelease keycodes. - // since we use the keycode on the client to detect when a key - // is released this won't do. so we remember the keycode on - // the most recent KeyPress (and clear it on a matching - // KeyRelease) so we have a keycode for a synthesized KeyPress. - if (xevent.type == KeyPress && xevent.xkey.keycode != 0) { - m_lastKeycode = xevent.xkey.keycode; - } - else if (xevent.type == KeyRelease && - xevent.xkey.keycode == m_lastKeycode) { - m_lastKeycode = 0; - } - - // now filter the event - if (XFilterEvent(&xevent, None)) { - return true; - } - } - - // handle event - switch (xevent.type) { - case CreateNotify: - { - // select events on new window - CDisplayLock display(m_screen); - selectEvents(display, xevent.xcreatewindow.window); - } - return true; - - case MappingNotify: - // keyboard mapping changed - updateKeys(); - return true; - - case KeyPress: - { - LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - KeyID key = mapKey(&xevent.xkey); - if (key != kKeyNone) { - // check for ctrl+alt+del emulation - if ((key == kKeyPause || key == kKeyBreak) && - (mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - // pretend it's ctrl+alt+del - LOG((CLOG_DEBUG "emulate ctrl+alt+del")); - key = kKeyDelete; - } - - // get which button. see call to XFilterEvent() above - // for more info. - KeyCode keycode = xevent.xkey.keycode; - if (keycode == 0) { - keycode = m_lastKeycode; - } - - // handle key - m_receiver->onKeyDown(key, mask, - static_cast(keycode)); - if (key == kKeyCapsLock && m_capsLockHalfDuplex) { - m_receiver->onKeyUp(key, mask | KeyModifierCapsLock, - static_cast(keycode)); - } - else if (key == kKeyNumLock && m_numLockHalfDuplex) { - m_receiver->onKeyUp(key, mask | KeyModifierNumLock, - static_cast(keycode)); - } - } - } - return true; - - case KeyRelease: - { - const KeyModifierMask mask = mapModifier(xevent.xkey.state); - KeyID key = mapKey(&xevent.xkey); - if (key != kKeyNone) { - // check if this is a key repeat by getting the next - // KeyPress event that has the same key and time as - // this release event, if any. first prepare the - // filter info. - CKeyEventInfo filter; - filter.m_event = KeyPress; - filter.m_window = xevent.xkey.window; - filter.m_time = xevent.xkey.time; - filter.m_keycode = xevent.xkey.keycode; - - // now check for event - bool hasPress; - { - XEvent xevent2; - CDisplayLock display(m_screen); - hasPress = (XCheckIfEvent(display, &xevent2, - &CXWindowsPrimaryScreen::findKeyEvent, - (XPointer)&filter) == True); - } - - // check for ctrl+alt+del emulation - if ((key == kKeyPause || key == kKeyBreak) && - (mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - // pretend it's ctrl+alt+del and ignore autorepeat - LOG((CLOG_DEBUG "emulate ctrl+alt+del")); - key = kKeyDelete; - hasPress = false; - } - - - if (!hasPress) { - // no press event follows so it's a plain release - LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - if (key == kKeyCapsLock && m_capsLockHalfDuplex) { - m_receiver->onKeyDown(key, mask, - static_cast(xevent.xkey.keycode)); - } - else if (key == kKeyNumLock && m_numLockHalfDuplex) { - m_receiver->onKeyDown(key, mask, - static_cast(xevent.xkey.keycode)); - } - m_receiver->onKeyUp(key, mask, - static_cast(xevent.xkey.keycode)); - } - else { - // found a press event following so it's a repeat. - // we could attempt to count the already queued - // repeats but we'll just send a repeat of 1. - // note that we discard the press event. - LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state)); - m_receiver->onKeyRepeat(key, mask, 1, - static_cast(xevent.xkey.keycode)); - } - } - } - return true; - - case ButtonPress: - { - LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNone) { - m_receiver->onMouseDown(button); - } - } - return true; - - case ButtonRelease: - { - LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); - const ButtonID button = mapButton(xevent.xbutton.button); - if (button != kButtonNone) { - m_receiver->onMouseUp(button); - } - else if (xevent.xbutton.button == 4) { - // wheel forward (away from user) - m_receiver->onMouseWheel(120); - } - else if (xevent.xbutton.button == 5) { - // wheel backward (toward user) - m_receiver->onMouseWheel(-120); - } - } - return true; - - case MotionNotify: - { - LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); - - // compute motion delta (relative to the last known - // mouse position) - SInt32 x = xevent.xmotion.x_root - m_x; - SInt32 y = xevent.xmotion.y_root - m_y; - - // save position to compute delta of next motion - m_x = xevent.xmotion.x_root; - m_y = xevent.xmotion.y_root; - - if (xevent.xmotion.send_event) { - // we warped the mouse. discard events until we - // find the matching sent event. see - // warpCursorNoFlush() for where the events are - // sent. we discard the matching sent event and - // can be sure we've skipped the warp event. - CDisplayLock display(m_screen); - do { - XMaskEvent(display, PointerMotionMask, &xevent); - } while (!xevent.xmotion.send_event); - } - else if (!isActive()) { - // motion on primary screen - m_receiver->onMouseMovePrimary(m_x, m_y); - } - else { - // motion on secondary screen. warp mouse back to - // center. - // - // my lombard (powerbook g3) running linux and - // using the adbmouse driver has two problems: - // first, the driver only sends motions of +/-2 - // pixels and, second, it seems to discard some - // physical input after a warp. the former isn't a - // big deal (we're just limited to every other - // pixel) but the latter is a PITA. to work around - // it we only warp when the mouse has moved more - // than s_size pixels from the center. - static const SInt32 s_size = 32; - if (xevent.xmotion.x_root - m_xCenter < -s_size || - xevent.xmotion.x_root - m_xCenter > s_size || - xevent.xmotion.y_root - m_yCenter < -s_size || - xevent.xmotion.y_root - m_yCenter > s_size) { - CDisplayLock display(m_screen); - warpCursorNoFlush(display, m_xCenter, m_yCenter); - } - - // send event if mouse moved. do this after warping - // back to center in case the motion takes us onto - // the primary screen. if we sent the event first - // in that case then the warp would happen after - // warping to the primary screen's enter position, - // effectively overriding it. - if (x != 0 || y != 0) { - m_receiver->onMouseMoveSecondary(x, y); - } - } - } - return true; - } - - return false; -} - -void -CXWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id) -{ - m_receiver->onOneShotTimerExpired(id); -} - -SInt32 -CXWindowsPrimaryScreen::getJumpZoneSize() const -{ - return 1; -} - -void -CXWindowsPrimaryScreen::onPreMainLoop() -{ - assert(m_window != None); -} - -void -CXWindowsPrimaryScreen::onPreOpen() -{ - assert(m_window == None); -} - -void -CXWindowsPrimaryScreen::onPostOpen() -{ - assert(m_window != None); - - // get cursor info - m_screen->getCursorPos(m_x, m_y); - m_screen->getCursorCenter(m_xCenter, m_yCenter); - - // get the input method - CDisplayLock display(m_screen); - m_im = XOpenIM(display, NULL, NULL, NULL); - if (m_im == NULL) { - return; - } - - // find the appropriate style. synergy supports XIMPreeditNothing - // only at the moment. - XIMStyles* styles; - if (XGetIMValues(m_im, XNQueryInputStyle, &styles, NULL) != NULL || - styles == NULL) { - LOG((CLOG_WARN "cannot get IM styles")); - return; - } - XIMStyle style = 0; - for (unsigned short i = 0; i < styles->count_styles; ++i) { - style = styles->supported_styles[i]; - if ((style & XIMPreeditNothing) != 0) { - if ((style & (XIMStatusNothing | XIMStatusNone)) != 0) { - break; - } - } - } - XFree(styles); - if (style == 0) { - LOG((CLOG_WARN "no supported IM styles")); - return; - } - - // create an input context for the style and tell it about our window - m_ic = XCreateIC(m_im, XNInputStyle, style, XNClientWindow, m_window, NULL); - if (m_ic == NULL) { - LOG((CLOG_WARN "cannot create IC")); - return; - } - - // find out the events we must select for and do so - unsigned long mask; - if (XGetICValues(m_ic, XNFilterEvents, &mask, NULL) != NULL) { - LOG((CLOG_WARN "cannot get IC filter events")); - return; - } - XWindowAttributes attr; - XGetWindowAttributes(display, m_window, &attr); - XSelectInput(display, m_window, attr.your_event_mask | mask); - - // no previous keycode - m_lastKeycode = 0; -} - -void -CXWindowsPrimaryScreen::onPreClose() -{ - CDisplayLock display(m_screen); - if (m_ic != NULL) { - XDestroyIC(m_ic); - m_ic = NULL; - } - if (m_im != NULL) { - XCloseIM(m_im); - m_im = NULL; - } - m_lastKeycode = 0; -} - -void -CXWindowsPrimaryScreen::onPreEnter() -{ - assert(m_window != None); - - if (m_ic != NULL) { - XUnsetICFocus(m_ic); - } -} - -void -CXWindowsPrimaryScreen::onPreLeave() -{ - assert(m_window != None); - - if (m_ic != NULL) { - XmbResetIC(m_ic); - XSetICFocus(m_ic); - } -} - -void -CXWindowsPrimaryScreen::onEnterScreenSaver() -{ - CDisplayLock display(m_screen); - - // set keyboard focus to root window. the screensaver should then - // pick up key events for when the user enters a password to unlock. - XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); -} - -void -CXWindowsPrimaryScreen::createWindow() -{ - assert(m_window == None); - - // get size of screen - SInt32 x, y, w, h; - m_screen->getShape(x, y, w, h); - - // grab window attributes. this window is used to capture user - // input when the user is focused on another client. don't let - // the window manager mess with it. - XSetWindowAttributes attr; - attr.event_mask = PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask | PropertyChangeMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = m_screen->getBlankCursor(); - - { - // create the grab window - CDisplayLock display(m_screen); - m_window = XCreateWindow(display, m_screen->getRoot(), - x, y, w, h, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - if (m_window == None) { - throw XScreenOpenFailure(); - } - LOG((CLOG_DEBUG "window is 0x%08x", m_window)); - - // start watching for events on other windows - selectEvents(display, m_screen->getRoot()); - } - - // tell generic screen about the window - m_screen->setWindow(m_window); -} - -void -CXWindowsPrimaryScreen::destroyWindow() -{ - // display can be NULL if the server unexpectedly disconnected - if (m_window != None) { - m_screen->setWindow(None); - CDisplayLock display(m_screen); - if (display != NULL) { - XDestroyWindow(display, m_window); - } - m_window = None; - } -} - -bool -CXWindowsPrimaryScreen::showWindow() -{ - assert(m_window != None); - - CDisplayLock display(m_screen); - - // raise and show the input window - XMapRaised(display, m_window); - - // grab the mouse and keyboard. keep trying until we get them. - // if we can't grab one after grabbing the other then ungrab - // and wait before retrying. give up after s_timeout seconds. - static const double s_timeout = 1.0; - int result; - CStopwatch timer; - do { - // keyboard first - do { - result = XGrabKeyboard(display, m_window, True, - GrabModeAsync, GrabModeAsync, CurrentTime); - assert(result != GrabNotViewable); - if (result != GrabSuccess) { - LOG((CLOG_DEBUG2 "waiting to grab keyboard")); - ARCH->sleep(0.05); - if (timer.getTime() >= s_timeout) { - LOG((CLOG_DEBUG2 "grab keyboard timed out")); - XUnmapWindow(display, m_window); - return false; - } - } - } while (result != GrabSuccess); - LOG((CLOG_DEBUG2 "grabbed keyboard")); - - // now the mouse - result = XGrabPointer(display, m_window, True, 0, - GrabModeAsync, GrabModeAsync, - m_window, None, CurrentTime); - assert(result != GrabNotViewable); - if (result != GrabSuccess) { - // back off to avoid grab deadlock - XUngrabKeyboard(display, CurrentTime); - LOG((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); - ARCH->sleep(0.05); - if (timer.getTime() >= s_timeout) { - LOG((CLOG_DEBUG2 "grab pointer timed out")); - XUnmapWindow(display, m_window); - return false; - } - } - } while (result != GrabSuccess); - LOG((CLOG_DEBUG1 "grabbed pointer and keyboard")); - - return true; -} - -void -CXWindowsPrimaryScreen::hideWindow() -{ - CDisplayLock display(m_screen); - - // unmap the grab window. this also ungrabs the mouse and keyboard. - XUnmapWindow(display, m_window); -} - -void -CXWindowsPrimaryScreen::warpCursorToCenter() -{ - warpCursor(m_xCenter, m_yCenter); -} - -void -CXWindowsPrimaryScreen::warpCursorNoFlush( - Display* display, SInt32 x, SInt32 y) -{ - assert(display != NULL); - assert(m_window != None); - - // send an event that we can recognize before the mouse warp - XEvent eventBefore; - eventBefore.type = MotionNotify; - eventBefore.xmotion.display = display; - eventBefore.xmotion.window = m_window; - eventBefore.xmotion.root = m_screen->getRoot(); - eventBefore.xmotion.subwindow = m_window; - eventBefore.xmotion.time = CurrentTime; - eventBefore.xmotion.x = x; - eventBefore.xmotion.y = y; - eventBefore.xmotion.x_root = x; - eventBefore.xmotion.y_root = y; - eventBefore.xmotion.state = 0; - eventBefore.xmotion.is_hint = NotifyNormal; - eventBefore.xmotion.same_screen = True; - XEvent eventAfter = eventBefore; - XSendEvent(display, m_window, False, 0, &eventBefore); - - // warp mouse - XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); - - // send an event that we can recognize after the mouse warp - XSendEvent(display, m_window, False, 0, &eventAfter); - XSync(display, False); - - LOG((CLOG_DEBUG2 "warped to %d,%d", x, y)); -} - -void -CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const -{ - // ignore errors while we adjust event masks. windows could be - // destroyed at any time after the XQueryTree() in doSelectEvents() - // so we must ignore BadWindow errors. - CXWindowsUtil::CErrorLock lock(display); - - // adjust event masks - doSelectEvents(display, w); -} - -void -CXWindowsPrimaryScreen::doSelectEvents(Display* display, Window w) const -{ - // we want to track the mouse everywhere on the display. to achieve - // that we select PointerMotionMask on every window. we also select - // SubstructureNotifyMask in order to get CreateNotify events so we - // select events on new windows too. - // - // note that this can break certain clients due a design flaw of X. - // X will deliver a PointerMotion event to the deepest window in the - // hierarchy that contains the pointer and has PointerMotionMask - // selected by *any* client. if another client doesn't select - // motion events in a subwindow so the parent window will get them - // then by selecting for motion events on the subwindow we break - // that client because the parent will no longer get the events. - - // FIXME -- should provide some workaround for event selection - // design flaw. perhaps only select for motion events on windows - // that already do or are top-level windows or don't propagate - // pointer events. or maybe an option to simply poll the mouse. - - // we don't want to adjust our grab window - if (w == m_window) { - return; - } - - // select events of interest. do this before querying the tree so - // we'll get notifications of children created after the XQueryTree() - // so we won't miss them. - XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask); - - // recurse on child windows - Window rw, pw, *cw; - unsigned int nc; - if (XQueryTree(display, w, &rw, &pw, &cw, &nc)) { - for (unsigned int i = 0; i < nc; ++i) { - doSelectEvents(display, cw[i]); - } - XFree(cw); - } -} - -KeyModifierMask -CXWindowsPrimaryScreen::mapModifier(unsigned int state) const -{ - KeyModifierMask mask = 0; - if (state & ShiftMask) - mask |= KeyModifierShift; - if (state & LockMask) - mask |= KeyModifierCapsLock; - if (state & ControlMask) - mask |= KeyModifierControl; - if (state & m_altMask) - mask |= KeyModifierAlt; - if (state & m_metaMask) - mask |= KeyModifierMeta; - if (state & m_superMask) - mask |= KeyModifierSuper; - if (state & m_modeSwitchMask) - mask |= KeyModifierModeSwitch; - if (state & m_numLockMask) - mask |= KeyModifierNumLock; - if (state & m_scrollLockMask) - mask |= KeyModifierScrollLock; - return mask; -} - -// map "Internet" keys to KeyIDs -static const KeySym g_map1008FF[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp, - /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, - /* 0x18 */ kKeyWWWHome, kKeyAppMail, 0, kKeyWWWSearch, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, kKeyWWWBack, kKeyWWWForward, - /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, 0, 0, 0, 0, - /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; - -KeyID -CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const -{ - CDisplayLock display(m_screen); - - // convert to a keysym - KeySym keysym; - if (event->type == KeyPress && m_ic != NULL) { - // do multibyte lookup. can only call XmbLookupString with a - // key press event and a valid XIC so we checked those above. - char scratch[32]; - int n = sizeof(scratch) / sizeof(scratch[0]); - char* buffer = scratch; - int status; - n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status); - if (status == XBufferOverflow) { - // not enough space. grow buffer and try again. - buffer = new char[n]; - n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status); - delete[] buffer; - } - - // see what we got. since we don't care about the string - // we'll just look for a keysym. - switch (status) { - default: - case XLookupNone: - case XLookupChars: - keysym = 0; - break; - - case XLookupKeySym: - case XLookupBoth: - break; - } - } - else { - // plain old lookup - char dummy[1]; - XLookupString(event, dummy, 0, &keysym, NULL); - } - - // convert key - switch (keysym & 0xffffff00) { - case 0x0000: - // Latin-1 - return static_cast(keysym); - - case 0xfe00: - // ISO 9995 Function and Modifier Keys - if (keysym == XK_ISO_Left_Tab) { - return kKeyLeftTab; - } - return kKeyNone; - - case 0xff00: - // MISCELLANY - return static_cast(keysym - 0xff00 + 0xef00); - - case 0x1008ff00: - // "Internet" keys - return g_map1008FF[keysym & 0xff]; - - default: { - // lookup character in table - UInt32 key = CXWindowsUtil::mapKeySymToUCS4(keysym); - if (key != 0x0000ffff) { - return static_cast(key); - } - - // unknown character - return kKeyNone; - } - } -} - -ButtonID -CXWindowsPrimaryScreen::mapButton(unsigned int button) const -{ - // first three buttons map to 1, 2, 3 (kButtonLeft, Middle, Right) - if (button >= 1 && button <= 3) { - return static_cast(button); - } - - // buttons 4 and 5 are ignored here. they're used for the wheel. - // buttons 6, 7, etc and up map to 4, 5, etc. - else if (button >= 6) { - return static_cast(button - 2); - } - - // unknown button - else { - return kButtonNone; - } -} - -void -CXWindowsPrimaryScreen::updateKeys() -{ - CDisplayLock display(m_screen); - - // get modifier map from server - XModifierKeymap* keymap = XGetModifierMapping(display); - - // initialize - m_altMask = 0; - m_metaMask = 0; - m_superMask = 0; - m_modeSwitchMask = 0; - m_numLockMask = 0; - m_capsLockMask = 0; - m_scrollLockMask = 0; - - // work around for my system, which reports this state bit when - // mode switch is down, instead of the appropriate modifier bit. - // should have no effect on other systems. -crs 9/02. - m_modeSwitchMask |= (1 << 13); - - // set keycodes and masks - for (unsigned int i = 0; i < 8; ++i) { - const unsigned int bit = (1 << i); - for (int j = 0; j < keymap->max_keypermod; ++j) { - KeyCode keycode = keymap->modifiermap[i * - keymap->max_keypermod + j]; - - // note mask for particular modifiers - const KeySym keysym = XKeycodeToKeysym(display, keycode, 0); - switch (keysym) { - case XK_Alt_L: - case XK_Alt_R: - m_altMask |= bit; - break; - - case XK_Meta_L: - case XK_Meta_R: - m_metaMask |= bit; - break; - - case XK_Super_L: - case XK_Super_R: - m_superMask |= bit; - break; - - case XK_Mode_switch: - m_modeSwitchMask |= bit; - break; - - case XK_Num_Lock: - m_numLockMask |= bit; - break; - - case XK_Caps_Lock: - m_capsLockMask |= bit; - break; - - case XK_Scroll_Lock: - m_scrollLockMask |= bit; - break; - } - } - } - - XFreeModifiermap(keymap); -} - -Bool -CXWindowsPrimaryScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) -{ - CKeyEventInfo* filter = reinterpret_cast(arg); - return (xevent->type == filter->m_event && - xevent->xkey.window == filter->m_window && - xevent->xkey.time == filter->m_time && - xevent->xkey.keycode == filter->m_keycode) ? True : False; -} diff --git a/lib/platform/CXWindowsPrimaryScreen.h b/lib/platform/CXWindowsPrimaryScreen.h deleted file mode 100644 index 2343ca07..00000000 --- a/lib/platform/CXWindowsPrimaryScreen.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CXWINDOWSPRIMARYSCREEN_H -#define CXWINDOWSPRIMARYSCREEN_H - -#include "CPrimaryScreen.h" -#include "IScreenEventHandler.h" -#include "MouseTypes.h" -#if defined(X_DISPLAY_MISSING) -# error X11 is required to build synergy -#else -# include -#endif - -class CXWindowsScreen; -class IScreenReceiver; -class IPrimaryScreenReceiver; - -//! X11 primary screen implementation -class CXWindowsPrimaryScreen : - public CPrimaryScreen, public IScreenEventHandler { -public: - CXWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); - virtual ~CXWindowsPrimaryScreen(); - - // CPrimaryScreen overrides - virtual void reconfigure(UInt32 activeSides); - virtual void warpCursor(SInt32 x, SInt32 y); - virtual void resetOptions(); - virtual void setOptions(const COptionsList& options); - virtual UInt32 addOneShotTimer(double timeout); - virtual KeyModifierMask getToggleMask() const; - virtual bool isLockedToScreen() const; - virtual IScreen* getScreen() const; - - // IScreenEventHandler overrides - virtual void onScreensaver(bool activated); - virtual bool onPreDispatch(const CEvent* event); - virtual bool onEvent(CEvent* event); - virtual void onOneShotTimerExpired(UInt32 id); - virtual SInt32 getJumpZoneSize() const; - -protected: - // CPrimaryScreen overrides - virtual void onPreMainLoop(); - virtual void onPreOpen(); - virtual void onPostOpen(); - virtual void onPreClose(); - virtual void onPreEnter(); - virtual void onPreLeave(); - virtual void onEnterScreenSaver(); - - virtual void createWindow(); - virtual void destroyWindow(); - virtual bool showWindow(); - virtual void hideWindow(); - virtual void warpCursorToCenter(); - - virtual void updateKeys(); - -private: - void warpCursorNoFlush(Display*, - SInt32 xAbsolute, SInt32 yAbsolute); - - void selectEvents(Display*, Window) const; - void doSelectEvents(Display*, Window) const; - - KeyModifierMask mapModifier(unsigned int state) const; - KeyID mapKey(XKeyEvent*) const; - ButtonID mapButton(unsigned int button) const; - - class CKeyEventInfo { - public: - int m_event; - Window m_window; - Time m_time; - KeyCode m_keycode; - }; - static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); - -private: - CXWindowsScreen* m_screen; - IPrimaryScreenReceiver* m_receiver; - - // our window - Window m_window; - - // note toggle keys that toggle on up/down (false) or on - // transition (true) - bool m_numLockHalfDuplex; - bool m_capsLockHalfDuplex; - - // modifier masks - unsigned int m_altMask; - unsigned int m_metaMask; - unsigned int m_superMask; - unsigned int m_modeSwitchMask; - unsigned int m_numLockMask; - unsigned int m_capsLockMask; - unsigned int m_scrollLockMask; - - // last mouse position - SInt32 m_x, m_y; - - // position of center pixel of screen - SInt32 m_xCenter, m_yCenter; - - // input method stuff - XIM m_im; - XIC m_ic; - KeyCode m_lastKeycode; -}; - -#endif diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 870da6dc..2dd1949d 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -17,12 +17,14 @@ #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CClipboard.h" -#include "IScreenEventHandler.h" #include "IScreenReceiver.h" +#include "IPrimaryScreenReceiver.h" #include "XScreen.h" #include "CLock.h" #include "CThread.h" #include "CLog.h" +#include "CStopwatch.h" +#include "CStringUtil.h" #include "IJob.h" #include #if defined(X_DISPLAY_MISSING) @@ -30,6 +32,13 @@ #else # include # include +# define XK_XKB_KEYS +# include +# if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# include +# else +# error The XTest extension is required to build synergy +# endif # if HAVE_X11_EXTENSIONS_XINERAMA_H // Xinerama.h may lack extern "C" for inclusion by C++ extern "C" { @@ -55,58 +64,45 @@ # endif # endif #endif +#include "CArch.h" -// -// CXWindowsScreen::CTimer -// - -CXWindowsScreen::CTimer::CTimer(IJob* job, double startTime, double resetTime) : - m_job(job), - m_timeout(resetTime), - m_time(resetTime), - m_startTime(startTime) +// map "Internet" keys to KeyIDs +static const KeySym g_map1008FF[] = { - assert(m_timeout > 0.0); -} - -CXWindowsScreen::CTimer::~CTimer() -{ - // do nothing -} - -void -CXWindowsScreen::CTimer::run() -{ - if (m_job != NULL) { - m_job->run(); - } -} - -void -CXWindowsScreen::CTimer::reset() -{ - m_time = m_timeout; - m_startTime = 0.0; -} - -CXWindowsScreen::CTimer::CTimer& -CXWindowsScreen::CTimer::operator-=(double dt) -{ - m_time -= dt - m_startTime; - m_startTime = 0.0; - return *this; -} - -CXWindowsScreen::CTimer::operator double() const -{ - return m_time; -} - -bool -CXWindowsScreen::CTimer::operator<(const CTimer& t) const -{ - return m_time < t.m_time; -} + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp, + /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, + /* 0x18 */ kKeyWWWHome, kKeyAppMail, 0, kKeyWWWSearch, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, kKeyWWWBack, kKeyWWWForward, + /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, 0, 0, 0, 0, + /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; // @@ -116,24 +112,32 @@ CXWindowsScreen::CTimer::operator<(const CTimer& t) const CXWindowsScreen* CXWindowsScreen::s_screen = NULL; CXWindowsScreen::CXWindowsScreen(IScreenReceiver* receiver, - IScreenEventHandler* eventHandler) : + IPrimaryScreenReceiver* primaryReceiver) : + m_isPrimary(primaryReceiver != NULL), m_display(NULL), m_root(None), - m_stop(false), - m_receiver(receiver), - m_eventHandler(eventHandler), m_window(None), + m_receiver(receiver), + m_primaryReceiver(primaryReceiver), + m_isOnScreen(m_isPrimary), m_x(0), m_y(0), m_w(0), m_h(0), + m_xCursor(0), m_yCursor(0), m_xCenter(0), m_yCenter(0), + m_keyState(NULL), + m_keyMapper(), + m_im(NULL), + m_ic(NULL), + m_lastKeycode(0), + m_atomQuit(None), m_screensaver(NULL), m_screensaverNotify(false), m_atomScreensaver(None), - m_oneShotTimer(NULL) + m_oneShotTimer(NULL), + m_xtestIsXineramaUnaware(true) { - assert(s_screen == NULL); - assert(m_receiver != NULL); - assert(m_eventHandler != NULL); + assert(s_screen == NULL); + assert(m_receiver != NULL); s_screen = this; @@ -153,115 +157,209 @@ CXWindowsScreen::~CXWindowsScreen() } void -CXWindowsScreen::addTimer(IJob* job, double timeout) +CXWindowsScreen::open(IKeyState* keyState) { - CLock lock(&m_timersMutex); - removeTimerNoLock(job); - m_timers.push(CTimer(job, m_time.getTime(), timeout)); -} + assert(m_display == NULL); -void -CXWindowsScreen::removeTimer(IJob* job) -{ - CLock lock(&m_timersMutex); - removeTimerNoLock(job); -} + try { + // set the X I/O error handler so we catch the display disconnecting + XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); -void -CXWindowsScreen::removeTimerNoLock(IJob* job) -{ - // do it the hard way. first collect all jobs that are not - // the removed job. - CTimerPriorityQueue::container_type tmp; - for (CTimerPriorityQueue::iterator index = m_timers.begin(); - index != m_timers.end(); ++index) { - if (index->getJob() != job) { - tmp.push_back(*index); + // get the DISPLAY + const char* display = getenv("DISPLAY"); + if (display == NULL) { + display = ":0.0"; } + + // open the display + LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); + m_display = XOpenDisplay(display); + if (m_display == NULL) { + throw XScreenUnavailable(60.0); + } + + // verify the availability of the XTest extension + if (!m_isPrimary) { + int majorOpcode, firstEvent, firstError; + if (!XQueryExtension(m_display, XTestExtensionName, + &majorOpcode, &firstEvent, &firstError)) { + LOG((CLOG_ERR "XTEST extension not available")); + throw XScreenOpenFailure(); + } + } + + // get root window + m_root = DefaultRootWindow(m_display); + + // get shape of default screen + m_x = 0; + m_y = 0; + m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); + m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + + // get center of default screen + m_xCenter = m_x + (m_w >> 1); + m_yCenter = m_y + (m_h >> 1); + + // check if xinerama is enabled and there is more than one screen. + // get center of first Xinerama screen. Xinerama appears to have + // a bug when XWarpPointer() is used in combination with + // XGrabPointer(). in that case, the warp is successful but the + // next pointer motion warps the pointer again, apparently to + // constrain it to some unknown region, possibly the region from + // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over + // all physical screens. this warp only seems to happen if the + // pointer wasn't in that region before the XWarpPointer(). the + // second (unexpected) warp causes synergy to think the pointer + // has been moved when it hasn't. to work around the problem, + // we warp the pointer to the center of the first physical + // screen instead of the logical screen. + m_xinerama = false; +#if HAVE_X11_EXTENSIONS_XINERAMA_H + int eventBase, errorBase; + if (XineramaQueryExtension(m_display, &eventBase, &errorBase) && + XineramaIsActive(m_display)) { + int numScreens; + XineramaScreenInfo* screens; + screens = XineramaQueryScreens(m_display, &numScreens); + if (screens != NULL) { + if (numScreens > 1) { + m_xinerama = true; + m_xCenter = screens[0].x_org + (screens[0].width >> 1); + m_yCenter = screens[0].y_org + (screens[0].height >> 1); + } + XFree(screens); + } + } +#endif + + // create the window + m_window = createWindow(); + if (m_window == None) { + throw XScreenOpenFailure(); + } + LOG((CLOG_DEBUG "window is 0x%08x", m_window)); + + if (m_isPrimary) { + // start watching for events on other windows + selectEvents(m_root); + + // prepare to use input methods + openIM(); + } + + // initialize the clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = new CXWindowsClipboard(m_display, m_window, id); + } + + // initialize the screen saver + m_atomScreensaver = XInternAtom(m_display, + "SYNERGY_SCREENSAVER", False); + m_screensaver = new CXWindowsScreenSaver(this, m_display); + } + catch (...) { + close(); + throw; } - // now swap in the new list - m_timers.swap(tmp); -} + // save the IKeyState + m_keyState = keyState; -UInt32 -CXWindowsScreen::addOneShotTimer(double timeout) -{ - CLock lock(&m_timersMutex); - // FIXME -- support multiple one-shot timers - m_oneShotTimer = new CTimer(NULL, m_time.getTime(), timeout); - return 0; + // we'll send ourself an event of this type to exit the main loop + m_atomQuit = XInternAtom(m_display, "SYNERGY_QUIT", False); + + if (!m_isPrimary) { + // become impervious to server grabs + XTestGrabControl(m_display, True); + } } void -CXWindowsScreen::setWindow(Window window) +CXWindowsScreen::close() { - CLock lock(&m_mutex); - assert(m_display != NULL); + // done with m_keyState + m_keyState = NULL; - // destroy the clipboards + // done with screen saver + delete m_screensaver; + + // destroy clipboards for (ClipboardID id = 0; id < kClipboardEnd; ++id) { delete m_clipboard[id]; m_clipboard[id] = NULL; } - // save the new window - m_window = window; - - // initialize the clipboards - if (m_window != None) { - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_clipboard[id] = new CXWindowsClipboard(m_display, m_window, id); - } + // done with input methods + if (m_ic != NULL) { + XDestroyIC(m_ic); + } + if (m_im != NULL) { + XCloseIM(m_im); } -} -Window -CXWindowsScreen::getRoot() const -{ - assert(m_display != NULL); - return m_root; -} + // done with window + if (m_window != None) { + XDestroyWindow(m_display, m_window); + } -Cursor -CXWindowsScreen::getBlankCursor() const -{ - return m_cursor; + // close the display + if (m_display != NULL) { + XCloseDisplay(m_display); + } + + // restore error handler + XSetIOErrorHandler(NULL); + + // reset state + m_atomQuit = None; + m_screensaver = NULL; + m_atomScreensaver = None; + m_ic = NULL; + m_im = NULL; + m_window = None; + m_root = None; + m_display = NULL; } void -CXWindowsScreen::open() +CXWindowsScreen::enable() { - assert(m_display == NULL); + if (!m_isPrimary) { + // get the keyboard control state + XKeyboardState keyControl; + XGetKeyboardControl(m_display, &keyControl); + m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn); - // set the X I/O error handler so we catch the display disconnecting - XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); + // move hider window under the cursor center + XMoveWindow(m_display, m_window, m_xCenter, m_yCenter); - // get the DISPLAY - const char* display = getenv("DISPLAY"); - if (display == NULL) { - display = ":0.0"; + // raise and show the window + // FIXME -- take focus? + XMapRaised(m_display, m_window); + + // warp the mouse to the cursor center + fakeMouseMove(m_xCenter, m_yCenter); + } +} + +void +CXWindowsScreen::disable() +{ + // release input context focus + if (m_ic != NULL) { + XUnsetICFocus(m_ic); } - // open the display - LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); - m_display = XOpenDisplay(display); - if (m_display == NULL) { - throw XScreenUnavailable(60.0); + // unmap the hider/grab window. this also ungrabs the mouse and + // keyboard if they're grabbed. + XUnmapWindow(m_display, m_window); + + // restore auto-repeat state + if (!m_isPrimary && m_autoRepeat) { + XAutoRepeatOn(m_display); } - - // get root window - m_root = DefaultRootWindow(m_display); - - // create the transparent cursor - createBlankCursor(); - - // get screen shape - updateScreenShape(); - - // initialize the screen saver - m_atomScreensaver = XInternAtom(m_display, "SYNERGY_SCREENSAVER", False); - m_screensaver = new CXWindowsScreenSaver(this, m_display); } void @@ -269,19 +367,13 @@ CXWindowsScreen::mainLoop() { // wait for an event in a cancellable way and don't lock the // display while we're waiting. - CEvent event; + XEvent event; m_mutex.lock(); + for (;;) { + #if UNIX_LIKE - // use poll() to wait for a message from the X server or for timeout. - // this is a good deal more efficient than polling and sleeping. -#if HAVE_POLL - struct pollfd pfds[1]; - pfds[0].fd = ConnectionNumber(m_display); - pfds[0].events = POLLIN; -#endif - while (!m_stop) { // compute timeout to next timer double dtimeout; { @@ -292,8 +384,14 @@ CXWindowsScreen::mainLoop() dtimeout = *m_oneShotTimer; } } + + // use poll() to wait for a message from the X server or for timeout. + // this is a good deal more efficient than polling and sleeping. #if HAVE_POLL - int timeout = static_cast(1000.0 * dtimeout); + struct pollfd pfds[1]; + pfds[0].fd = ConnectionNumber(m_display); + pfds[0].events = POLLIN; + int timeout = static_cast(1000.0 * dtimeout); #else struct timeval timeout; struct timeval* timeoutPtr; @@ -333,27 +431,10 @@ CXWindowsScreen::mainLoop() // process timers processTimers(); - // handle pending events - while (!m_stop && XPending(m_display) > 0) { - // get the event - XNextEvent(m_display, &event.m_event); - - // process the event. if unhandled then let the subclass - // have a go at it. - m_mutex.unlock(); - if (!onPreDispatch(&event)) { - m_eventHandler->onEvent(&event); - } - m_mutex.lock(); - } - } - #else // !UNIX_LIKE - // poll and sleep - while (!m_stop) { // poll for pending events and process timers - while (!m_stop && XPending(m_display) == 0) { + while (XPending(m_display) == 0) { // check timers if (processTimers()) { continue; @@ -365,75 +446,130 @@ CXWindowsScreen::mainLoop() m_mutex.lock(); } - // process events - while (!m_stop && XPending(m_display) > 0) { - // get the event - XNextEvent(m_display, &event.m_event); - - // process the event. if unhandled then let the subclass - // have a go at it. - m_mutex.unlock(); - if (!onPreDispatch(&event)) { - m_eventHandler->onEvent(&event); - } - m_mutex.lock(); - } - } - #endif // !UNIX_LIKE - m_mutex.unlock(); + // process events + while (XPending(m_display) > 0) { + // get the event + XNextEvent(m_display, &event); + if (isQuitEvent(&event)) { + return; + } + + // process the event + onEvent(&event); + } + } } void CXWindowsScreen::exitMainLoop() { - // m_stop should be a condition variable that we signal here - // but we can't wait on both the condition variable and the - // X connection so there's no point. however, we do need - // to wake up the X connection so send ourself some event. - CLock lock(&m_mutex); - m_stop = true; - - if (m_display != NULL && m_window != None) { + // send ourself a quit event. this will wake up the event loop + // and cause it to exit. + if (m_atomQuit != None) { XEvent event; event.xclient.type = ClientMessage; event.xclient.display = m_display; event.xclient.window = m_window; - event.xclient.message_type = XInternAtom(m_display, "ATOM", False); + event.xclient.message_type = m_atomQuit; event.xclient.format = 32; event.xclient.data.l[0] = 0; event.xclient.data.l[1] = 0; event.xclient.data.l[2] = 0; event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; - CXWindowsUtil::CErrorLock lock(m_display); + CLock lock(&m_mutex); + CXWindowsUtil::CErrorLock errorLock(m_display); XSendEvent(m_display, m_window, False, 0, &event); } } void -CXWindowsScreen::close() +CXWindowsScreen::enter() { CLock lock(&m_mutex); - // done with screen saver - delete m_screensaver; - m_screensaver = NULL; - - // destroy clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - delete m_clipboard[id]; - m_clipboard[id] = NULL; + // release input context focus + if (m_ic != NULL) { + XUnsetICFocus(m_ic); } - // close the display - if (m_display != NULL) { - XCloseDisplay(m_display); - m_display = NULL; - LOG((CLOG_DEBUG "closed display")); + // unmap the hider/grab window. this also ungrabs the mouse and + // keyboard if they're grabbed. + XUnmapWindow(m_display, m_window); + +/* maybe call this if entering for the screensaver + // set keyboard focus to root window. the screensaver should then + // pick up key events for when the user enters a password to unlock. + XSetInputFocus(m_display, PointerRoot, PointerRoot, CurrentTime); +*/ + + if (!m_isPrimary) { + // get the keyboard control state + XKeyboardState keyControl; + XGetKeyboardControl(m_display, &keyControl); + m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn); + + // turn off auto-repeat. we do this so fake key press events don't + // cause the local server to generate their own auto-repeats of + // those keys. + XAutoRepeatOff(m_display); } - XSetIOErrorHandler(NULL); + + // now on screen + m_isOnScreen = true; +} + +bool +CXWindowsScreen::leave() +{ + CLock lock(&m_mutex); + + if (!m_isPrimary) { + // restore the previous keyboard auto-repeat state. if the user + // changed the auto-repeat configuration while on the client then + // that state is lost. that's because we can't get notified by + // the X server when the auto-repeat configuration is changed so + // we can't track the desired configuration. + if (m_autoRepeat) { + XAutoRepeatOn(m_display); + } + + // move hider window under the cursor center + XMoveWindow(m_display, m_window, m_xCenter, m_yCenter); + } + + // raise and show the window + // FIXME -- take focus? + XMapRaised(m_display, m_window); + + // grab the mouse and keyboard, if primary and possible + if (m_isPrimary && !grabMouseAndKeyboard()) { + XUnmapWindow(m_display, m_window); + return false; + } + + // now warp the mouse. we warp after showing the window so we're + // guaranteed to get the mouse leave event and to prevent the + // keyboard focus from changing under point-to-focus policies. + if (m_isPrimary) { + warpCursor(m_xCenter, m_yCenter); + } + else { + fakeMouseMove(m_xCenter, m_yCenter); + } + + // set input context focus to our window + if (m_ic != NULL) { + XmbResetIC(m_ic); + XSetICFocus(m_ic); + } + + // now off screen + m_isOnScreen = false; + + return true; } bool @@ -515,9 +651,36 @@ CXWindowsScreen::screensaver(bool activate) } void -CXWindowsScreen::syncDesktop() +CXWindowsScreen::resetOptions() { - // do nothing; X doesn't suffer from this bogosity + m_xtestIsXineramaUnaware = true; +} + +void +CXWindowsScreen::setOptions(const COptionsList& options) +{ + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionXTestXineramaUnaware) { + m_xtestIsXineramaUnaware = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false")); + } + } +} + +void +CXWindowsScreen::updateKeys() +{ + CLock lock(&m_mutex); + + // update keyboard and mouse button mappings + m_keyMapper.update(m_display, m_keyState); + updateButtons(); +} + +bool +CXWindowsScreen::isPrimary() const +{ + return m_isPrimary; } bool @@ -544,9 +707,6 @@ CXWindowsScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const void CXWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { - CLock lock(&m_mutex); - assert(m_display != NULL); - x = m_x; y = m_y; w = m_w; @@ -562,7 +722,7 @@ CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const Window root, window; int mx, my, xWindow, yWindow; unsigned int mask; - if (XQueryPointer(m_display, getRoot(), &root, &window, + if (XQueryPointer(m_display, m_root, &root, &window, &mx, &my, &xWindow, &yWindow, &mask)) { x = mx; y = my; @@ -574,84 +734,368 @@ CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const } void -CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +CXWindowsScreen::reconfigure(UInt32) { - CLock lock(&m_mutex); - - x = m_xCenter; - y = m_yCenter; + // do nothing } void -CXWindowsScreen::updateScreenShape() +CXWindowsScreen::warpCursor(SInt32 x, SInt32 y) { - // get shape of default screen - m_x = 0; - m_y = 0; - m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); - m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); - LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + CLock lock(&m_mutex); - // get center of default screen - m_xCenter = m_x + (m_w >> 1); - m_yCenter = m_y + (m_h >> 1); + // warp mouse + warpCursorNoFlush(x, y); -#if HAVE_X11_EXTENSIONS_XINERAMA_H - // get center of first Xinerama screen. Xinerama appears to have - // a bug when XWarpPointer() is used in combination with - // XGrabPointer(). in that case, the warp is successful but the - // next pointer motion warps the pointer again, apparently to - // constrain it to some unknown region, possibly the region from - // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over - // all physical screens. this warp only seems to happen if the - // pointer wasn't in that region before the XWarpPointer(). the - // second (unexpected) warp causes synergy to think the pointer - // has been moved when it hasn't. to work around the problem, - // we warp the pointer to the center of the first physical - // screen instead of the logical screen. - int eventBase, errorBase; - if (XineramaQueryExtension(m_display, &eventBase, &errorBase)) { - if (XineramaIsActive(m_display)) { - int numScreens; - XineramaScreenInfo* screens; - screens = XineramaQueryScreens(m_display, &numScreens); - if (screens != NULL) { - if (numScreens > 1) { - m_xCenter = screens[0].x_org + (screens[0].width >> 1); - m_yCenter = screens[0].y_org + (screens[0].height >> 1); - } - XFree(screens); - } - } + // remove all input events before and including warp + XEvent event; + while (XCheckMaskEvent(m_display, PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask, + &event)) { + // do nothing } -#endif + + // save position as last position + m_xCursor = x; + m_yCursor = y; +} + +UInt32 +CXWindowsScreen::addOneShotTimer(double timeout) +{ + CLock lock(&m_timersMutex); + // FIXME -- support multiple one-shot timers + m_oneShotTimer = new CTimer(NULL, m_time.getTime(), timeout); + return 0; +} + +SInt32 +CXWindowsScreen::getJumpZoneSize() const +{ + return 1; } bool -CXWindowsScreen::onPreDispatch(CEvent* event) +CXWindowsScreen::isAnyMouseButtonDown() const { - assert(event != NULL); - XEvent* xevent = &event->m_event; + CLock lock(&m_mutex); + + // query the pointer to get the button state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (XQueryPointer(m_display, m_root, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + return ((state & (Button1Mask | Button2Mask | Button3Mask | + Button4Mask | Button5Mask)) != 0); + } + + return false; +} + +const char* +CXWindowsScreen::getKeyName(KeyButton keycode) const +{ + CLock lock(&m_mutex); + + KeySym keysym = XKeycodeToKeysym(m_display, keycode, 0); + char* name = XKeysymToString(keysym); + if (name != NULL) { + return name; + } + else { + static char buffer[20]; + return strcpy(buffer, + CStringUtil::print("keycode %d", keycode).c_str()); + } +} + +void +CXWindowsScreen::fakeKeyEvent(KeyButton keycode, bool press) const +{ + CLock lock(&m_mutex); + XTestFakeKeyEvent(m_display, keycode, press ? True : False, CurrentTime); + XFlush(m_display); +} + +bool +CXWindowsScreen::fakeCtrlAltDel() const +{ + // pass keys through unchanged + return false; +} + +void +CXWindowsScreen::fakeMouseButton(ButtonID button, bool press) const +{ + const unsigned int xButton = mapButtonToX(button); + if (xButton != 0) { + CLock lock(&m_mutex); + XTestFakeButtonEvent(m_display, xButton, + press ? True : False, CurrentTime); + XFlush(m_display); + } +} + +void +CXWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const +{ + CLock lock(&m_mutex); + if (m_xinerama && m_xtestIsXineramaUnaware) { + XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); + } + else { + XTestFakeMotionEvent(m_display, DefaultScreen(m_display), + x, y, CurrentTime); + } + XFlush(m_display); +} + +void +CXWindowsScreen::fakeMouseWheel(SInt32 delta) const +{ + // choose button depending on rotation direction + const unsigned int xButton = mapButtonToX(static_cast( + (delta >= 0) ? -1 : -2)); + if (xButton == 0) { + return; + } + + // now use absolute value of delta + if (delta < 0) { + delta = -delta; + } + + // send as many clicks as necessary + CLock lock(&m_mutex); + for (; delta >= 120; delta -= 120) { + XTestFakeButtonEvent(m_display, xButton, True, CurrentTime); + XTestFakeButtonEvent(m_display, xButton, False, CurrentTime); + } + XFlush(m_display); +} + +KeyButton +CXWindowsScreen::mapKey(IKeyState::Keystrokes& keys, + const IKeyState& keyState, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + return m_keyMapper.mapKey(keys, keyState, id, desiredMask, isAutoRepeat); +} + +bool +CXWindowsScreen::isQuitEvent(XEvent* event) const +{ + return (m_atomQuit != None && + event->type == ClientMessage && + event->xclient.window == m_window && + event->xclient.message_type == m_atomQuit); +} + +Window +CXWindowsScreen::createWindow() const +{ + // default window attributes. we don't want the window manager + // messing with our window and we don't want the cursor to be + // visible inside the window. + XSetWindowAttributes attr; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = createBlankCursor(); + + // adjust attributes and get size and shape + SInt32 x, y, w, h; + if (m_isPrimary) { + // grab window attributes. this window is used to capture user + // input when the user is focused on another client. it covers + // the whole screen. + attr.event_mask = PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask | PropertyChangeMask; + x = m_x; + y = m_y; + w = m_w; + h = m_h; + } + else { + // cursor hider window attributes. this window is used to hide the + // cursor when it's not on the screen. the window is hidden as soon + // as the cursor enters the screen or the display's real mouse is + // moved. we'll reposition the window as necessary so its + // position here doesn't matter. it only needs to be 1x1 because + // it only needs to contain the cursor's hotspot. + attr.event_mask = LeaveWindowMask; + x = 0; + y = 0; + w = 1; + h = 1; + } + + // create and return the window + return XCreateWindow(m_display, m_root, x, y, w, h, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); +} + +void +CXWindowsScreen::openIM() +{ + // open the input methods + XIM im = XOpenIM(m_display, NULL, NULL, NULL); + if (im == NULL) { + return; + } + + // find the appropriate style. synergy supports XIMPreeditNothing + // only at the moment. + XIMStyles* styles; + if (XGetIMValues(im, XNQueryInputStyle, &styles, NULL) != NULL || + styles == NULL) { + LOG((CLOG_WARN "cannot get IM styles")); + XCloseIM(im); + return; + } + XIMStyle style = 0; + for (unsigned short i = 0; i < styles->count_styles; ++i) { + style = styles->supported_styles[i]; + if ((style & XIMPreeditNothing) != 0) { + if ((style & (XIMStatusNothing | XIMStatusNone)) != 0) { + break; + } + } + } + XFree(styles); + if (style == 0) { + LOG((CLOG_WARN "no supported IM styles")); + XCloseIM(im); + return; + } + + // create an input context for the style and tell it about our window + XIC ic = XCreateIC(im, XNInputStyle, style, XNClientWindow, m_window, NULL); + if (ic == NULL) { + LOG((CLOG_WARN "cannot create IC")); + XCloseIM(im); + return; + } + + // find out the events we must select for and do so + unsigned long mask; + if (XGetICValues(ic, XNFilterEvents, &mask, NULL) != NULL) { + LOG((CLOG_WARN "cannot get IC filter events")); + XDestroyIC(ic); + XCloseIM(im); + return; + } + + // we have IM + m_im = im; + m_ic = ic; + m_lastKeycode = 0; + + // select events on our window that IM requires + XWindowAttributes attr; + XGetWindowAttributes(m_display, m_window, &attr); + XSelectInput(m_display, m_window, attr.your_event_mask | mask); +} + +void +CXWindowsScreen::addTimer(IJob* job, double timeout) +{ + CLock lock(&m_timersMutex); + removeTimerNoLock(job); + m_timers.push(CTimer(job, m_time.getTime(), timeout)); +} + +void +CXWindowsScreen::removeTimer(IJob* job) +{ + CLock lock(&m_timersMutex); + removeTimerNoLock(job); +} + +void +CXWindowsScreen::removeTimerNoLock(IJob* job) +{ + // do it the hard way. first collect all jobs that are not + // the removed job. + CTimerPriorityQueue::container_type tmp; + for (CTimerPriorityQueue::iterator index = m_timers.begin(); + index != m_timers.end(); ++index) { + if (index->getJob() != job) { + tmp.push_back(*index); + } + } + + // now swap in the new list + m_timers.swap(tmp); +} + +void +CXWindowsScreen::onEvent(XEvent* xevent) +{ + assert(xevent != NULL); + + // let input methods try to handle event first + if (m_ic != NULL) { + // XFilterEvent() may eat the event and generate a new KeyPress + // event with a keycode of 0 because there isn't an actual key + // associated with the keysym. but the KeyRelease may pass + // through XFilterEvent() and keep its keycode. this means + // there's a mismatch between KeyPress and KeyRelease keycodes. + // since we use the keycode on the client to detect when a key + // is released this won't do. so we remember the keycode on + // the most recent KeyPress (and clear it on a matching + // KeyRelease) so we have a keycode for a synthesized KeyPress. + if (xevent->type == KeyPress && xevent->xkey.keycode != 0) { + m_lastKeycode = xevent->xkey.keycode; + } + else if (xevent->type == KeyRelease && + xevent->xkey.keycode == m_lastKeycode) { + m_lastKeycode = 0; + } + + // now filter the event + if (XFilterEvent(xevent, None)) { + return; + } + } switch (xevent->type) { + case CreateNotify: + if (m_isPrimary) { + // select events on new window + selectEvents(xevent->xcreatewindow.window); + } + return; + case MappingNotify: - { - CLock lock(&m_mutex); - if (XPending(m_display) > 0) { - XEvent tmpEvent; - XPeekEvent(m_display, &tmpEvent); - if (tmpEvent.type == MappingNotify) { - // discard this MappingNotify since another follows. - // we tend to get a bunch of these in a row. - return true; - } + if (XPending(m_display) > 0) { + XEvent tmpEvent; + XPeekEvent(m_display, &tmpEvent); + if (tmpEvent.type == MappingNotify) { + // discard this MappingNotify since another follows. + // we tend to get a bunch of these in a row. + return; } } // keyboard mapping changed XRefreshKeyboardMapping(&xevent->xmapping); + m_keyState->updateKeys(); + break; - // pass event on + case LeaveNotify: + if (!m_isPrimary) { + // mouse moved out of hider window somehow. hide the window. + XUnmapWindow(m_display, m_window); + } break; case SelectionClear: @@ -664,7 +1108,7 @@ CXWindowsScreen::onPreDispatch(CEvent* event) LOG((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); m_clipboard[id]->lost(xevent->xselectionclear.time); m_receiver->onGrabClipboard(id); - return true; + return; } } break; @@ -675,12 +1119,11 @@ CXWindowsScreen::onPreDispatch(CEvent* event) // retrieval methods. we'll just delete the property // with the data (satisfying the usual ICCCM protocol). if (xevent->xselection.property != None) { - CLock lock(&m_mutex); XDeleteProperty(m_display, xevent->xselection.requestor, xevent->xselection.property); } - return true; + return; case SelectionRequest: { @@ -688,14 +1131,13 @@ CXWindowsScreen::onPreDispatch(CEvent* event) ClipboardID id = getClipboardID( xevent->xselectionrequest.selection); if (id != kClipboardEnd) { - CLock lock(&m_mutex); m_clipboard[id]->addRequest( xevent->xselectionrequest.owner, xevent->xselectionrequest.requestor, xevent->xselectionrequest.target, xevent->xselectionrequest.time, xevent->xselectionrequest.property); - return true; + return; } } break; @@ -706,16 +1148,17 @@ CXWindowsScreen::onPreDispatch(CEvent* event) processClipboardRequest(xevent->xproperty.window, xevent->xproperty.time, xevent->xproperty.atom); - return true; + return; } break; case ClientMessage: - if (xevent->xclient.message_type == m_atomScreensaver && + if (m_isPrimary && + xevent->xclient.message_type == m_atomScreensaver && xevent->xclient.format == 32) { // screen saver activation/deactivation event - m_eventHandler->onScreensaver(xevent->xclient.data.l[0] != 0); - return true; + m_primaryReceiver->onScreensaver(xevent->xclient.data.l[0] != 0); + return; } break; @@ -723,22 +1166,236 @@ CXWindowsScreen::onPreDispatch(CEvent* event) // looks like one of the windows that requested a clipboard // transfer has gone bye-bye. destroyClipboardRequest(xevent->xdestroywindow.window); + break; - // we don't know if the event was handled or not so continue + case KeyPress: + if (m_isPrimary) { + onKeyPress(xevent->xkey); + } + return; + + case KeyRelease: + if (m_isPrimary) { + onKeyRelease(xevent->xkey); + } + return; + + case ButtonPress: + if (m_isPrimary) { + onMousePress(xevent->xbutton); + } + return; + + case ButtonRelease: + if (m_isPrimary) { + onMouseRelease(xevent->xbutton); + } + return; + + case MotionNotify: + if (m_isPrimary) { + onMouseMove(xevent->xmotion); + } + return; + + default: break; } // let screen saver have a go - { - CLock lock(&m_mutex); - m_screensaver->onPreDispatch(xevent); - } - - return m_eventHandler->onPreDispatch(event); + m_screensaver->onPreDispatch(xevent); } void -CXWindowsScreen::createBlankCursor() +CXWindowsScreen::onKeyPress(XKeyEvent& xkey) +{ + LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xkey.keycode, xkey.state)); + const KeyModifierMask mask = m_keyMapper.mapModifier(xkey.state); + KeyID key = mapKeyFromX(&xkey); + if (key != kKeyNone) { + // check for ctrl+alt+del emulation + if ((key == kKeyPause || key == kKeyBreak) && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + // pretend it's ctrl+alt+del + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + key = kKeyDelete; + } + + // get which button. see call to XFilterEvent() in onEvent() + // for more info. + KeyButton keycode = static_cast(xkey.keycode); + if (keycode == 0) { + keycode = static_cast(m_lastKeycode); + } + + // handle key + m_primaryReceiver->onKeyDown(key, mask, keycode); + KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); + if (m_keyState->isHalfDuplex(keyMask)) { + m_primaryReceiver->onKeyUp(key, mask | keyMask, keycode); + } + } +} + +Bool +CXWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) +{ + CKeyEventInfo* filter = reinterpret_cast(arg); + return (xevent->type == filter->m_event && + xevent->xkey.window == filter->m_window && + xevent->xkey.time == filter->m_time && + xevent->xkey.keycode == filter->m_keycode) ? True : False; +} + +void +CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) +{ + const KeyModifierMask mask = m_keyMapper.mapModifier(xkey.state); + KeyID key = mapKeyFromX(&xkey); + if (key != kKeyNone) { + // check if this is a key repeat by getting the next + // KeyPress event that has the same key and time as + // this release event, if any. first prepare the + // filter info. + CKeyEventInfo filter; + filter.m_event = KeyPress; + filter.m_window = xkey.window; + filter.m_time = xkey.time; + filter.m_keycode = xkey.keycode; + + // now check for event + bool hasPress; + { + XEvent xevent2; + hasPress = (XCheckIfEvent(m_display, &xevent2, + &CXWindowsScreen::findKeyEvent, + (XPointer)&filter) == True); + } + + // check for ctrl+alt+del emulation + if ((key == kKeyPause || key == kKeyBreak) && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + // pretend it's ctrl+alt+del and ignore autorepeat + LOG((CLOG_DEBUG "emulate ctrl+alt+del")); + key = kKeyDelete; + hasPress = false; + } + + KeyButton keycode = static_cast(xkey.keycode); + if (!hasPress) { + // no press event follows so it's a plain release + LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", keycode, xkey.state)); + KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); + if (m_keyState->isHalfDuplex(keyMask)) { + m_primaryReceiver->onKeyDown(key, mask, keycode); + } + m_primaryReceiver->onKeyUp(key, mask, keycode); + } + else { + // found a press event following so it's a repeat. + // we could attempt to count the already queued + // repeats but we'll just send a repeat of 1. + // note that we discard the press event. + LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state)); + m_primaryReceiver->onKeyRepeat(key, mask, 1, keycode); + } + } +} + +void +CXWindowsScreen::onMousePress(const XButtonEvent& xbutton) +{ + LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button)); + const ButtonID button = mapButtonFromX(&xbutton); + if (button != kButtonNone) { + m_primaryReceiver->onMouseDown(button); + } +} + +void +CXWindowsScreen::onMouseRelease(const XButtonEvent& xbutton) +{ + LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button)); + const ButtonID button = mapButtonFromX(&xbutton); + if (button != kButtonNone) { + m_primaryReceiver->onMouseUp(button); + } + else if (xbutton.button == 4) { + // wheel forward (away from user) + m_primaryReceiver->onMouseWheel(120); + } + else if (xbutton.button == 5) { + // wheel backward (toward user) + m_primaryReceiver->onMouseWheel(-120); + } +} + +void +CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion) +{ + LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xmotion.x_root, xmotion.y_root)); + + // compute motion delta (relative to the last known + // mouse position) + SInt32 x = xmotion.x_root - m_xCursor; + SInt32 y = xmotion.y_root - m_yCursor; + + // save position to compute delta of next motion + m_xCursor = xmotion.x_root; + m_yCursor = xmotion.y_root; + + if (xmotion.send_event) { + // we warped the mouse. discard events until we + // find the matching sent event. see + // warpCursorNoFlush() for where the events are + // sent. we discard the matching sent event and + // can be sure we've skipped the warp event. + XEvent xevent; + do { + XMaskEvent(m_display, PointerMotionMask, &xevent); + } while (!xevent.xany.send_event); + } + else if (m_isOnScreen) { + // motion on primary screen + m_primaryReceiver->onMouseMovePrimary(m_xCursor, m_yCursor); + } + else { + // motion on secondary screen. warp mouse back to + // center. + // + // my lombard (powerbook g3) running linux and + // using the adbmouse driver has two problems: + // first, the driver only sends motions of +/-2 + // pixels and, second, it seems to discard some + // physical input after a warp. the former isn't a + // big deal (we're just limited to every other + // pixel) but the latter is a PITA. to work around + // it we only warp when the mouse has moved more + // than s_size pixels from the center. + static const SInt32 s_size = 32; + if (xmotion.x_root - m_xCenter < -s_size || + xmotion.x_root - m_xCenter > s_size || + xmotion.y_root - m_yCenter < -s_size || + xmotion.y_root - m_yCenter > s_size) { + warpCursorNoFlush(m_xCenter, m_yCenter); + } + + // send event if mouse moved. do this after warping + // back to center in case the motion takes us onto + // the primary screen. if we sent the event first + // in that case then the warp would happen after + // warping to the primary screen's enter position, + // effectively overriding it. + if (x != 0 || y != 0) { + m_primaryReceiver->onMouseMoveSecondary(x, y); + } + } +} + +Cursor +CXWindowsScreen::createBlankCursor() const { // this seems just a bit more complicated than really necessary @@ -763,12 +1420,14 @@ CXWindowsScreen::createBlankCursor() color.flags = DoRed | DoGreen | DoBlue; // make cursor from bitmap - m_cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, + Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, &color, &color, 0, 0); // don't need bitmap or the data anymore delete[] data; XFreePixmap(m_display, bitmap); + + return cursor; } bool @@ -824,7 +1483,7 @@ CXWindowsScreen::processTimers() // now notify of the one shot timers if (oneShot) { m_mutex.unlock(); - m_eventHandler->onOneShotTimerExpired(0); + m_primaryReceiver->onOneShotTimerExpired(0); m_mutex.lock(); } @@ -853,8 +1512,6 @@ void CXWindowsScreen::processClipboardRequest(Window requestor, Time time, Atom property) { - CLock lock(&m_mutex); - // check every clipboard until one returns success for (ClipboardID id = 0; id < kClipboardEnd; ++id) { if (m_clipboard[id] != NULL && @@ -867,8 +1524,6 @@ CXWindowsScreen::processClipboardRequest(Window requestor, void CXWindowsScreen::destroyClipboardRequest(Window requestor) { - CLock lock(&m_mutex); - // check every clipboard until one returns success for (ClipboardID id = 0; id < kClipboardEnd; ++id) { if (m_clipboard[id] != NULL && @@ -894,29 +1549,348 @@ CXWindowsScreen::ioErrorHandler(Display*) exit(17); } +void +CXWindowsScreen::selectEvents(Window w) const +{ + // ignore errors while we adjust event masks. windows could be + // destroyed at any time after the XQueryTree() in doSelectEvents() + // so we must ignore BadWindow errors. + CXWindowsUtil::CErrorLock lock(m_display); + + // adjust event masks + doSelectEvents(w); +} + +void +CXWindowsScreen::doSelectEvents(Window w) const +{ + // we want to track the mouse everywhere on the display. to achieve + // that we select PointerMotionMask on every window. we also select + // SubstructureNotifyMask in order to get CreateNotify events so we + // select events on new windows too. + // + // note that this can break certain clients due a design flaw of X. + // X will deliver a PointerMotion event to the deepest window in the + // hierarchy that contains the pointer and has PointerMotionMask + // selected by *any* client. if another client doesn't select + // motion events in a subwindow so the parent window will get them + // then by selecting for motion events on the subwindow we break + // that client because the parent will no longer get the events. + + // FIXME -- should provide some workaround for event selection + // design flaw. perhaps only select for motion events on windows + // that already do or are top-level windows or don't propagate + // pointer events. or maybe an option to simply poll the mouse. + + // we don't want to adjust our grab window + if (w == m_window) { + return; + } + + // select events of interest. do this before querying the tree so + // we'll get notifications of children created after the XQueryTree() + // so we won't miss them. + XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask); + + // recurse on child windows + Window rw, pw, *cw; + unsigned int nc; + if (XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) { + for (unsigned int i = 0; i < nc; ++i) { + doSelectEvents(cw[i]); + } + XFree(cw); + } +} + +KeyID +CXWindowsScreen::mapKeyFromX(XKeyEvent* event) const +{ + // convert to a keysym + KeySym keysym; + if (event->type == KeyPress && m_ic != NULL) { + // do multibyte lookup. can only call XmbLookupString with a + // key press event and a valid XIC so we checked those above. + char scratch[32]; + int n = sizeof(scratch) / sizeof(scratch[0]); + char* buffer = scratch; + int status; + n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status); + if (status == XBufferOverflow) { + // not enough space. grow buffer and try again. + buffer = new char[n]; + n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status); + delete[] buffer; + } + + // see what we got. since we don't care about the string + // we'll just look for a keysym. + switch (status) { + default: + case XLookupNone: + case XLookupChars: + keysym = 0; + break; + + case XLookupKeySym: + case XLookupBoth: + break; + } + } + else { + // plain old lookup + char dummy[1]; + XLookupString(event, dummy, 0, &keysym, NULL); + } + + // convert key + switch (keysym & 0xffffff00) { + case 0x0000: + // Latin-1 + return static_cast(keysym); + + case 0xfe00: + // ISO 9995 Function and Modifier Keys + if (keysym == XK_ISO_Left_Tab) { + return kKeyLeftTab; + } + return kKeyNone; + + case 0xff00: + // MISCELLANY + return static_cast(keysym - 0xff00 + 0xef00); + + case 0x1008ff00: + // "Internet" keys + return g_map1008FF[keysym & 0xff]; + + default: { + // lookup character in table + UInt32 key = CXWindowsUtil::mapKeySymToUCS4(keysym); + if (key != 0x0000ffff) { + return static_cast(key); + } + + // unknown character + return kKeyNone; + } + } +} + +ButtonID +CXWindowsScreen::mapButtonFromX(const XButtonEvent* event) const +{ + unsigned int button = event->button; + + // first three buttons map to 1, 2, 3 (kButtonLeft, Middle, Right) + if (button >= 1 && button <= 3) { + return static_cast(button); + } + + // buttons 4 and 5 are ignored here. they're used for the wheel. + // buttons 6, 7, etc and up map to 4, 5, etc. + else if (button >= 6) { + return static_cast(button - 2); + } + + // unknown button + else { + return kButtonNone; + } +} + +unsigned int +CXWindowsScreen::mapButtonToX(ButtonID id) const +{ + // map button -1 to button 4 (+wheel) + if (id == static_cast(-1)) { + id = 4; + } + + // map button -2 to button 5 (-wheel) + else if (id == static_cast(-2)) { + id = 5; + } + + // map buttons 4, 5, etc. to 6, 7, etc. to make room for buttons + // 4 and 5 used to simulate the mouse wheel. + else if (id >= 4) { + id += 2; + } + + // check button is in legal range + if (id < 1 || id > m_buttons.size()) { + // out of range + return 0; + } + + // map button + return static_cast(m_buttons[id - 1]); +} + +void +CXWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) +{ + assert(m_window != None); + + // send an event that we can recognize before the mouse warp + XEvent eventBefore; + eventBefore.type = MotionNotify; + eventBefore.xmotion.display = m_display; + eventBefore.xmotion.window = m_window; + eventBefore.xmotion.root = m_root; + eventBefore.xmotion.subwindow = m_window; + eventBefore.xmotion.time = CurrentTime; + eventBefore.xmotion.x = x; + eventBefore.xmotion.y = y; + eventBefore.xmotion.x_root = x; + eventBefore.xmotion.y_root = y; + eventBefore.xmotion.state = 0; + eventBefore.xmotion.is_hint = NotifyNormal; + eventBefore.xmotion.same_screen = True; + XEvent eventAfter = eventBefore; + XSendEvent(m_display, m_window, False, 0, &eventBefore); + + // warp mouse + XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); + + // send an event that we can recognize after the mouse warp + XSendEvent(m_display, m_window, False, 0, &eventAfter); + XSync(m_display, False); + + LOG((CLOG_DEBUG2 "warped to %d,%d", x, y)); +} + +// ------------------ + +void +CXWindowsScreen::updateButtons() +{ + // query the button mapping + UInt32 numButtons = XGetPointerMapping(m_display, NULL, 0); + unsigned char* tmpButtons = new unsigned char[numButtons]; + XGetPointerMapping(m_display, tmpButtons, numButtons); + + // find the largest logical button id + unsigned char maxButton = 0; + for (UInt32 i = 0; i < numButtons; ++i) { + if (tmpButtons[i] > maxButton) { + maxButton = tmpButtons[i]; + } + } + + // allocate button array + m_buttons.resize(maxButton); + + // fill in button array values. m_buttons[i] is the physical + // button number for logical button i+1. + for (UInt32 i = 0; i < numButtons; ++i) { + m_buttons[i] = 0; + } + for (UInt32 i = 0; i < numButtons; ++i) { + m_buttons[tmpButtons[i] - 1] = i + 1; + } + + // clean up + delete[] tmpButtons; +} + +bool +CXWindowsScreen::grabMouseAndKeyboard() +{ + // grab the mouse and keyboard. keep trying until we get them. + // if we can't grab one after grabbing the other then ungrab + // and wait before retrying. give up after s_timeout seconds. + static const double s_timeout = 1.0; + int result; + CStopwatch timer; + do { + // keyboard first + do { + result = XGrabKeyboard(m_display, m_window, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + LOG((CLOG_DEBUG2 "waiting to grab keyboard")); + ARCH->sleep(0.05); + if (timer.getTime() >= s_timeout) { + LOG((CLOG_DEBUG2 "grab keyboard timed out")); + return false; + } + } + } while (result != GrabSuccess); + LOG((CLOG_DEBUG2 "grabbed keyboard")); + + // now the mouse + result = XGrabPointer(m_display, m_window, True, 0, + GrabModeAsync, GrabModeAsync, + m_window, None, CurrentTime); + assert(result != GrabNotViewable); + if (result != GrabSuccess) { + // back off to avoid grab deadlock + XUngrabKeyboard(m_display, CurrentTime); + LOG((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer")); + ARCH->sleep(0.05); + if (timer.getTime() >= s_timeout) { + LOG((CLOG_DEBUG2 "grab pointer timed out")); + return false; + } + } + } while (result != GrabSuccess); + + LOG((CLOG_DEBUG1 "grabbed pointer and keyboard")); + return true; +} + // -// CDisplayLock +// CXWindowsScreen::CTimer // -CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : - m_mutex(&screen->m_mutex), - m_display(screen->m_display) +CXWindowsScreen::CTimer::CTimer(IJob* job, double startTime, double resetTime) : + m_job(job), + m_timeout(resetTime), + m_time(resetTime), + m_startTime(startTime) { - // note -- it's permitted for m_display to be NULL. that might - // happen if we couldn't connect to the display or if the - // display unexpectedly disconnected. the caller is expected - // to check for NULL as necessary. - - m_mutex->lock(); + assert(m_timeout > 0.0); } -CDisplayLock::~CDisplayLock() +CXWindowsScreen::CTimer::~CTimer() { - m_mutex->unlock(); + // do nothing } -CDisplayLock::operator Display*() const +void +CXWindowsScreen::CTimer::run() { - return m_display; + if (m_job != NULL) { + m_job->run(); + } +} + +void +CXWindowsScreen::CTimer::reset() +{ + m_time = m_timeout; + m_startTime = 0.0; +} + +CXWindowsScreen::CTimer::CTimer& +CXWindowsScreen::CTimer::operator-=(double dt) +{ + m_time -= dt - m_startTime; + m_startTime = 0.0; + return *this; +} + +CXWindowsScreen::CTimer::operator double() const +{ + return m_time; +} + +bool +CXWindowsScreen::CTimer::operator<(const CTimer& t) const +{ + return m_time < t.m_time; } diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index ce37a004..36d5be4b 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -15,40 +15,28 @@ #ifndef CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H -#include "IScreen.h" +#include "IPlatformScreen.h" +#include "CXWindowsKeyMapper.h" #include "CMutex.h" #include "CStopwatch.h" +#include "CPriorityQueue.h" #include "stdvector.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else # include #endif -#include -#include -class IJob; -class IScreenEventHandler; -class IScreenReceiver; class CXWindowsClipboard; class CXWindowsScreenSaver; +class IJob; +class IScreenReceiver; +class IPrimaryScreenReceiver; -/*! -\class CEvent -\brief User event data -An architecture dependent type holding user event data. -*/ -// X11 event -class CEvent { +//! Implementation of IPlatformScreen for X11 +class CXWindowsScreen : public IPlatformScreen { public: - XEvent m_event; - SInt32 m_result; -}; - -//! Implementation of IScreen for X11 -class CXWindowsScreen : public IScreen { -public: - CXWindowsScreen(IScreenReceiver*, IScreenEventHandler*); + CXWindowsScreen(IScreenReceiver*, IPrimaryScreenReceiver*); virtual ~CXWindowsScreen(); //! @name manipulators @@ -56,10 +44,10 @@ public: //! Add timer /*! - Add a job to invoke every timeout seconds. The job is - called with the display locked. If a job timeout expires twice - or more before the job can be called then the job is called - just once. The caller retains ownership of the job. + Add a job to invoke every timeout seconds. The job is called + with the display locked. If a job timeout expires twice or + more before the job can be called then the job is called just + once. The caller retains ownership of the job. */ void addTimer(IJob*, double timeout); @@ -69,64 +57,55 @@ public: */ void removeTimer(IJob*); - //! Install a one-shot timer - /*! - Installs a one-shot timer for \c timeout seconds and returns the - id of the timer (which will be passed to the receiver's - \c onTimerExpired()). - */ - UInt32 addOneShotTimer(double timeout); - - //! Set window - /*! - Set the window (created by the subclass). This performs some - initialization and saves the window in case it's needed later. - */ - void setWindow(Window); - - //@} - //! @name accessors - //@{ - - //! Get window - /*! - Returns the root window of the screen. - */ - Window getRoot() const; - - //! Get transparent cursor - /*! - Returns a cursor that is transparent everywhere. - */ - Cursor getBlankCursor() const; - //@} - // IScreen overrides - void open(); - void mainLoop(); - void exitMainLoop(); - void close(); - bool setClipboard(ClipboardID, const IClipboard*); - void checkClipboards(); - void openScreensaver(bool notify); - void closeScreensaver(); - void screensaver(bool activate); - void syncDesktop(); - bool getClipboard(ClipboardID, IClipboard*) const; - void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - void getCursorPos(SInt32&, SInt32&) const; - void getCursorCenter(SInt32&, SInt32&) const; + // IPlatformScreen overrides + virtual void open(IKeyState*); + virtual void close(); + virtual void enable(); + virtual void disable(); + virtual void mainLoop(); + virtual void exitMainLoop(); + virtual void enter(); + virtual bool leave(); + virtual bool setClipboard(ClipboardID, const IClipboard*); + virtual void checkClipboards(); + virtual void openScreensaver(bool notify); + virtual void closeScreensaver(); + virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual void updateKeys(); + virtual bool isPrimary() const; + virtual bool getClipboard(ClipboardID, IClipboard*) const; + virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; + virtual void getCursorPos(SInt32&, SInt32&) const; + + // IPrimaryScreen overrides + virtual void reconfigure(UInt32 activeSides); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual UInt32 addOneShotTimer(double timeout); + virtual SInt32 getJumpZoneSize() const; + virtual bool isAnyMouseButtonDown() const; + virtual const char* getKeyName(KeyButton) const; + + // ISecondaryScreen overrides + virtual void fakeKeyEvent(KeyButton id, bool press) const; + virtual bool fakeCtrlAltDel() const; + virtual void fakeMouseButton(ButtonID id, bool press) const; + virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseWheel(SInt32 delta) const; + virtual KeyButton mapKey(IKeyState::Keystrokes&, + const IKeyState& keyState, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; private: - // update screen size cache - void updateScreenShape(); - // process events before dispatching to receiver - bool onPreDispatch(CEvent* event); + void onEvent(XEvent* event); // create the transparent cursor - void createBlankCursor(); + Cursor createBlankCursor() const; // remove a timer without locking void removeTimerNoLock(IJob*); @@ -149,78 +128,6 @@ private: static int ioErrorHandler(Display*); private: - // a priority queue will direct access to the elements - template , - class Compare = std::greater > - class CPriorityQueue { - public: - typedef typename Container::value_type value_type; - typedef typename Container::size_type size_type; - typedef typename Container::iterator iterator; - typedef Container container_type; - - CPriorityQueue() { } - CPriorityQueue(Container& swappedIn); - ~CPriorityQueue() { } - - // manipulators - - void push(const value_type& v) - { - c.push_back(v); - std::push_heap(c.begin(), c.end(), comp); - } - - void pop() - { - std::pop_heap(c.begin(), c.end(), comp); - c.pop_back(); - } - - iterator begin() - { - return c.begin(); - } - - iterator end() - { - return c.end(); - } - - void swap(CPriorityQueue& q) - { - c.swap(q.c); - } - - void swap(Container& c2) - { - c.swap(c2); - std::make_heap(c.begin(), c.end(), comp); - } - - // accessors - - bool empty() const - { - return c.empty(); - } - - size_type size() const - { - return c.size(); - } - - const value_type& - top() const - { - return c.front(); - } - - private: - Container c; - Compare comp; - }; - // a timer priority queue element class CTimer { public: @@ -252,32 +159,80 @@ private: double m_time; double m_startTime; }; + class CKeyEventInfo { + public: + int m_event; + Window m_window; + Time m_time; + KeyCode m_keycode; + }; + + bool isQuitEvent(XEvent*) const; + + Window createWindow() const; + void openIM(); + + bool grabMouseAndKeyboard(); + void onKeyPress(XKeyEvent&); + void onKeyRelease(XKeyEvent&); + void onMousePress(const XButtonEvent&); + void onMouseRelease(const XButtonEvent&); + void onMouseMove(const XMotionEvent&); + + void selectEvents(Window) const; + void doSelectEvents(Window) const; + + KeyID mapKeyFromX(XKeyEvent*) const; + ButtonID mapButtonFromX(const XButtonEvent*) const; + unsigned int mapButtonToX(ButtonID id) const; + + void warpCursorNoFlush(SInt32 x, SInt32 y); + + void updateButtons(); + + static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: - friend class CDisplayLock; - typedef CPriorityQueue CTimerPriorityQueue; + // true if screen is being used as a primary screen, false otherwise + bool m_isPrimary; + // X is not thread safe CMutex m_mutex; Display* m_display; Window m_root; - bool m_stop; - - IScreenReceiver* m_receiver; - IScreenEventHandler* m_eventHandler; Window m_window; + IScreenReceiver* m_receiver; + IPrimaryScreenReceiver* m_primaryReceiver; + + // true if mouse has entered the screen + bool m_isOnScreen; + + // screen shape stuff SInt32 m_x, m_y; SInt32 m_w, m_h; SInt32 m_xCenter, m_yCenter; + // last mouse position + SInt32 m_xCursor, m_yCursor; + + // keyboard stuff + IKeyState* m_keyState; + CXWindowsKeyMapper m_keyMapper; + + // input method stuff + XIM m_im; + XIC m_ic; + KeyCode m_lastKeycode; + // clipboards CXWindowsClipboard* m_clipboard[kClipboardEnd]; - // the transparent cursor - Cursor m_cursor; + // the quit message + Atom m_atomQuit; // screen saver stuff CXWindowsScreenSaver* m_screensaver; @@ -290,22 +245,23 @@ private: CMutex m_timersMutex; CTimer* m_oneShotTimer; + // logical to physical button mapping. m_buttons[i] gives the + // physical button for logical button i+1. + std::vector m_buttons; + + // true if global auto-repeat was enabled before we turned it off + bool m_autoRepeat; + + // stuff to workaround xtest being xinerama unaware. attempting + // to fake a mouse motion under xinerama may behave strangely, + // especially if screen 0 is not at 0,0 or if faking a motion on + // a screen other than screen 0. + bool m_xtestIsXineramaUnaware; + bool m_xinerama; + // pointer to (singleton) screen. this is only needed by // ioErrorHandler(). static CXWindowsScreen* s_screen; }; -//! Convenience object to lock/unlock a CXWindowsScreen -class CDisplayLock { -public: - CDisplayLock(const CXWindowsScreen*); - ~CDisplayLock(); - - operator Display*() const; - -private: - const CMutex* m_mutex; - Display* m_display; -}; - #endif diff --git a/lib/platform/CXWindowsSecondaryScreen.cpp b/lib/platform/CXWindowsSecondaryScreen.cpp deleted file mode 100644 index 11096b3a..00000000 --- a/lib/platform/CXWindowsSecondaryScreen.cpp +++ /dev/null @@ -1,1780 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CXWindowsSecondaryScreen.h" -#include "CXWindowsClipboard.h" -#include "CXWindowsScreen.h" -#include "CXWindowsScreenSaver.h" -#include "CXWindowsUtil.h" -#include "IScreenReceiver.h" -#include "XScreen.h" -#include "CThread.h" -#include "CLog.h" -#if defined(X_DISPLAY_MISSING) -# error X11 is required to build synergy -#else -# include -# include -# define XK_MISCELLANY -# define XK_XKB_KEYS -# define XK_LATIN1 -# define XK_LATIN2 -# define XK_LATIN3 -# define XK_LATIN4 -# define XK_LATIN8 -# define XK_LATIN9 -# include -# if defined(HAVE_X11_EXTENSIONS_XTEST_H) -# include -# else -# error The XTest extension is required to build synergy -# endif -# if HAVE_X11_EXTENSIONS_XINERAMA_H - // Xinerama.h may lack extern "C" for inclusion by C++ - extern "C" { -# include - } -# endif -# if defined(HAVE_X11_XF86KEYSYM_H) -# include -# endif -# if !defined(XF86XK_Launch0) -# define XF86XK_Launch0 0x1008FF40 -# endif -# if !defined(XF86XK_Launch1) -# define XF86XK_Launch1 0x1008FF41 -# endif -#endif - - -// -// CXWindowsSecondaryScreen -// - -CXWindowsSecondaryScreen::KeySymsMap - CXWindowsSecondaryScreen::s_decomposedKeySyms; - -CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) : - CSecondaryScreen(), - m_window(None), - m_xtestIsXineramaUnaware(true) -{ - m_screen = new CXWindowsScreen(receiver, this); - - // make sure decomposed keysym table is prepared - getDecomposedKeySymTable(); -} - -CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() -{ - assert(m_window == None); - delete m_screen; -} - -bool -CXWindowsSecondaryScreen::isAutoRepeating(SysKeyID sysKeyID) const -{ - char bit = static_cast(1 << (sysKeyID & 7)); - return ((m_keyControl.auto_repeats[sysKeyID >> 3] & bit) != 0); -} - -void -CXWindowsSecondaryScreen::flush() -{ - CDisplayLock display(m_screen); - if (display != NULL) { - XFlush(display); - } -} - -void -CXWindowsSecondaryScreen::resetOptions() -{ - CSecondaryScreen::resetOptions(); - m_xtestIsXineramaUnaware = true; -} - -void -CXWindowsSecondaryScreen::setOptions(const COptionsList& options) -{ - CSecondaryScreen::setOptions(options); - for (UInt32 i = 0, n = options.size(); i < n; i += 2) { - if (options[i] == kOptionXTestXineramaUnaware) { - m_xtestIsXineramaUnaware = (options[i + 1] != 0); - LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false")); - } - } -} - -IScreen* -CXWindowsSecondaryScreen::getScreen() const -{ - return m_screen; -} - -void -CXWindowsSecondaryScreen::onScreensaver(bool) -{ - // ignore -} - -bool -CXWindowsSecondaryScreen::onPreDispatch(const CEvent*) -{ - return false; -} - -bool -CXWindowsSecondaryScreen::onEvent(CEvent* event) -{ - assert(event != NULL); - XEvent& xevent = event->m_event; - - // handle event - switch (xevent.type) { - case MappingNotify: { - // keyboard mapping changed - CDisplayLock display(m_screen); - doUpdateKeys(display); - return true; - } - - case LeaveNotify: - // mouse moved out of hider window somehow. hide the window. - hideWindow(); - return true; - } -} - -void -CXWindowsSecondaryScreen::onOneShotTimerExpired(UInt32) -{ - // ignore -} - -void -CXWindowsSecondaryScreen::onPreMainLoop() -{ - assert(m_window != None); -} - -void -CXWindowsSecondaryScreen::onPreOpen() -{ - assert(m_window == None); -} - -void -CXWindowsSecondaryScreen::onPostOpen() -{ - assert(m_window != None); - - // get the keyboard control state - CDisplayLock display(m_screen); - XGetKeyboardControl(display, &m_keyControl); - - // check if xinerama is enabled and there is more than one screen - m_xinerama = false; -#if HAVE_X11_EXTENSIONS_XINERAMA_H - int eventBase, errorBase; - if (XineramaQueryExtension(display, &eventBase, &errorBase)) { - if (XineramaIsActive(display)) { - int numScreens; - XineramaScreenInfo* screens; - screens = XineramaQueryScreens(display, &numScreens); - if (screens != NULL) { - m_xinerama = (numScreens > 1); - XFree(screens); - } - } - } -#endif -} - -void -CXWindowsSecondaryScreen::onPreClose() -{ - if (m_keyControl.global_auto_repeat == AutoRepeatModeOn) { - CDisplayLock display(m_screen); - XAutoRepeatOn(display); - } -} - -void -CXWindowsSecondaryScreen::onPreEnter() -{ - assert(m_window != None); -} - -void -CXWindowsSecondaryScreen::onPostEnter() -{ - assert(m_window != None); - - // get the keyboard control state - CDisplayLock display(m_screen); - XGetKeyboardControl(display, &m_keyControl); - - // turn off auto-repeat. we do this so fake key press events don't - // cause the local server to generate their own auto-repeats of - // those keys. - XAutoRepeatOff(display); -} - -void -CXWindowsSecondaryScreen::onPreLeave() -{ - assert(m_window != None); - - // restore the previous keyboard auto-repeat state. if the user - // changed the auto-repeat configuration while on the client then - // that state is lost. that's because we can't get notified by - // the X server when the auto-repeat configuration is changed so - // we can't track the desired configuration. - if (m_keyControl.global_auto_repeat == AutoRepeatModeOn) { - CDisplayLock display(m_screen); - XAutoRepeatOn(display); - } -} - -void -CXWindowsSecondaryScreen::createWindow() -{ - { - CDisplayLock display(m_screen); - - // verify the availability of the XTest extension - int majorOpcode, firstEvent, firstError; - if (!XQueryExtension(display, XTestExtensionName, - &majorOpcode, &firstEvent, &firstError)) { - LOG((CLOG_ERR "XTEST extension not available")); - throw XScreenOpenFailure(); - } - - // cursor hider window attributes. this window is used to hide the - // cursor when it's not on the screen. the window is hidden as soon - // as the cursor enters the screen or the display's real cursor is - // moved. - XSetWindowAttributes attr; - attr.event_mask = LeaveWindowMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = m_screen->getBlankCursor(); - - // create the cursor hider window - m_window = XCreateWindow(display, m_screen->getRoot(), - 0, 0, 1, 1, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - if (m_window == None) { - throw XScreenOpenFailure(); - } - LOG((CLOG_DEBUG "window is 0x%08x", m_window)); - - // become impervious to server grabs - XTestGrabControl(display, True); - } - - // tell generic screen about the window - m_screen->setWindow(m_window); -} - -void -CXWindowsSecondaryScreen::destroyWindow() -{ - { - // release keys that are still pressed - releaseKeys(); - - CDisplayLock display(m_screen); - if (display != NULL) { - // no longer impervious to server grabs - XTestGrabControl(display, False); - } - } - - // destroy window - if (m_window != None) { - m_screen->setWindow(None); - CDisplayLock display(m_screen); - if (display != NULL) { - XDestroyWindow(display, m_window); - } - m_window = None; - } -} - -void -CXWindowsSecondaryScreen::showWindow(SInt32 x, SInt32 y) -{ - { - CDisplayLock display(m_screen); - - // move hider window under the given position - XMoveWindow(display, m_window, x, y); - - // raise and show the hider window. take activation. - // FIXME -- take focus? - XMapRaised(display, m_window); - } - - // now warp the mouse. we warp after showing the window so we're - // guaranteed to get the mouse leave event and to prevent the - // keyboard focus from changing under point-to-focus policies. - fakeMouseMove(x, y); -} - -void -CXWindowsSecondaryScreen::hideWindow() -{ - assert(m_window != None); - - CDisplayLock display(m_screen); - XUnmapWindow(display, m_window); -} - -unsigned int -CXWindowsSecondaryScreen::mapButton(ButtonID id) const -{ - // map button -1 to button 4 (+wheel) - if (id == static_cast(-1)) { - id = 4; - } - - // map button -2 to button 5 (-wheel) - else if (id == static_cast(-2)) { - id = 5; - } - - // map buttons 4, 5, etc. to 6, 7, etc. to make room for buttons - // 4 and 5 used to simulate the mouse wheel. - else if (id >= 4) { - id += 2; - } - - // check button is in legal range - if (id < 1 || id > m_buttons.size()) { - // out of range - return 0; - } - - // map button - return static_cast(m_buttons[id - 1]); -} - -KeyModifierMask -CXWindowsSecondaryScreen::mapKey(Keystrokes& keys, - SysKeyID& keycode, KeyID id, - KeyModifierMask currentMask, - KeyModifierMask desiredMask, EKeyAction action) const -{ - // note -- must have display locked on entry - - // the system translates key events into characters depending - // on the modifier key state at the time of the event. to - // generate the right keysym we need to set the modifier key - // states appropriately. - // - // desiredMask is the mask desired by the caller. however, there - // may not be a keycode mapping to generate the desired keysym - // with that mask. we override the bits in the mask that cannot - // be accomodated. - - // ignore releases and repeats for half-duplex keys - const bool isHalfDuplex = isKeyHalfDuplex(id); - if (isHalfDuplex && action != kPress) { - return currentMask; - } - - // convert KeyID to a KeySym - KeySym keysym = keyIDToKeySym(id, desiredMask); - if (keysym == NoSymbol) { - // unknown key - LOG((CLOG_DEBUG2 "no keysym for id 0x%08x", id)); - return currentMask; - } - - // get the mapping for this keysym - KeySymIndex keyIndex = m_keysymMap.find(keysym); - - // if the mapping isn't found and keysym is caps lock sensitive - // then convert the case of the keysym and try again. - if (keyIndex == m_keysymMap.end()) { - KeySym lKey, uKey; - XConvertCase(keysym, &lKey, &uKey); - if (lKey != uKey) { - if (lKey == keysym) { - keyIndex = m_keysymMap.find(uKey); - } - else { - keyIndex = m_keysymMap.find(lKey); - } - } - } - - if (keyIndex != m_keysymMap.end()) { - // the keysym is mapped to some keycode. if it's a modifier - // and that modifier is already in the desired state then - // ignore the request since there's nothing to do. never - // ignore a toggle modifier on press or release, though. - const KeyMapping& keyMapping = keyIndex->second; - const KeyModifierMask modifierBit = keyMapping.m_modifierMask; - if (modifierBit != 0) { - if (action == kRepeat) { - LOG((CLOG_DEBUG2 "ignore repeating modifier")); - return currentMask; - } - if ((m_toggleModifierMask & modifierBit) == 0) { - if ((action == kPress && (currentMask & modifierBit) != 0) || - (action == kRelease && (currentMask & modifierBit) == 0)) { - LOG((CLOG_DEBUG2 "modifier in proper state: 0x%04x", currentMask)); - return currentMask; - } - } - } - - // create the keystrokes for this keysym - KeyModifierMask mask; - if (!mapToKeystrokes(keys, keycode, mask, - keyIndex, currentMask, action, isHalfDuplex)) { - // failed to generate keystrokes - keys.clear(); - return currentMask; - } - else { - // success - LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask)); - return mask; - } - } - - // we can't find the keysym mapped to any keycode. this doesn't - // necessarily mean we can't generate the keysym, though. if the - // keysym can be created by combining keysyms then we may still - // be okay. - KeySyms decomposition; - if (decomposeKeySym(keysym, decomposition)) { - LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms", keysym, decomposition.size())); - - // map each decomposed keysym to keystrokes. we want the mask - // and the keycode from the last keysym (which should be the - // only non-dead key). the dead keys are not sensitive to - // anything but shift and mode switch. - KeyModifierMask mask; - for (KeySyms::const_iterator i = decomposition.begin(); - i != decomposition.end();) { - // increment the iterator - KeySyms::const_iterator next = i; - ++next; - - // lookup the key - keysym = *i; - keyIndex = m_keysymMap.find(keysym); - if (keyIndex == m_keysymMap.end()) { - // missing a required keysym - LOG((CLOG_DEBUG2 "no keycode for decomposed keysym 0x%08x", keysym)); - keys.clear(); - return currentMask; - } - - // the keysym is mapped to some keycode - if (!mapToKeystrokes(keys, keycode, mask, - keyIndex, currentMask, action, isHalfDuplex)) { - // failed to generate keystrokes - keys.clear(); - return currentMask; - } - - // on to the next keysym - i = next; - } - LOG((CLOG_DEBUG2 "new mask: 0x%04x", mask)); - return mask; - } - - LOG((CLOG_DEBUG2 "no keycode for keysym")); - return currentMask; -} - -KeyModifierMask -CXWindowsSecondaryScreen::getModifierKeyMask(SysKeyID keycode) const -{ - KeyCodeToModifierMap::const_iterator i = m_keycodeToModifier.find(keycode); - if (i == m_keycodeToModifier.end()) { - return 0; - } - return m_modifierIndexToMask[i->second]; -} - -bool -CXWindowsSecondaryScreen::isModifierActive(SysKeyID keycode) const -{ - // check if any keycode for this modifier is down. return false - // for toggle modifiers. - KeyCodeToModifierMap::const_iterator i = m_keycodeToModifier.find(keycode); - if (i != m_keycodeToModifier.end() && - (m_modifierIndexToMask[i->second] & m_toggleModifierMask) != 0) { - const KeyCodes& keycodes = m_modifierKeycodes[i->second]; - for (KeyCodes::const_iterator j = keycodes.begin(); - j != keycodes.end(); ++j) { - if (isKeyDown(*j)) { - return true; - } - } - } - return false; -} - -unsigned int -CXWindowsSecondaryScreen::findBestKeyIndex(KeySymIndex keyIndex, - KeyModifierMask /*currentMask*/) const -{ - // there are up to 4 keycodes per keysym to choose from. the - // best choice is the one that requires the fewest adjustments - // to the modifier state. for example, the letter A normally - // requires shift + a. if shift isn't already down we'd have - // to synthesize a shift press before the a press. however, - // if A could also be created with some other keycode without - // shift then we'd prefer that when shift wasn't down. - // - // if the action is kRepeat or kRelease then we don't call this - // method since we just need to synthesize a key repeat/release - // on the same keycode that we pressed. - // XXX -- do this right - for (unsigned int i = 0; i < 4; ++i) { - if (keyIndex->second.m_keycode[i] != 0) { - return i; - } - } - - assert(0 && "no keycode found for keysym"); - return 0; -} - -bool -CXWindowsSecondaryScreen::isShiftInverted(KeySymIndex keyIndex, - KeyModifierMask currentMask) const -{ - // each keycode has up to 4 keysym associated with it, one each for: - // no modifiers, shift, mode switch, and shift and mode switch. if - // a keysym is modified by num lock and num lock is active then you - // get the shifted keysym when shift is not down and the unshifted - // keysym when it is. that is, num lock inverts the sense of the - // shift modifier when active. similarly for caps lock. this - // method returns true iff the sense of shift should be inverted - // for this key given a modifier state. - if (keyIndex->second.m_numLockSensitive) { - if ((currentMask & KeyModifierNumLock) != 0) { - return true; - } - } - - // if a keysym is num lock sensitive it is never caps lock - // sensitive, thus the else here. - else if (keyIndex->second.m_capsLockSensitive) { - if ((currentMask & KeyModifierCapsLock) != 0) { - return true; - } - } - - return false; -} - -bool -CXWindowsSecondaryScreen::mapToKeystrokes(Keystrokes& keys, - SysKeyID& keycode, - KeyModifierMask& finalMask, - KeySymIndex keyIndex, - KeyModifierMask currentMask, - EKeyAction action, - bool isHalfDuplex) const -{ - // keyIndex must be valid - assert(keyIndex != m_keysymMap.end()); - - // get the keysym we're trying to generate and possible keycodes - const KeySym keysym = keyIndex->first; - const KeyMapping& mapping = keyIndex->second; - LOG((CLOG_DEBUG2 "keysym = 0x%08x", keysym)); - - // get the best keycode index for the keysym and modifiers. note - // that (bestIndex & 1) == 0 if the keycode is a shift modifier - // and (bestIndex & 2) == 0 if the keycode is a mode switch - // modifier. this is important later because we don't want - // adjustModifiers() to adjust a modifier if that's the key we're - // mapping. - unsigned int bestIndex = findBestKeyIndex(keyIndex, currentMask); - - // get the keycode - keycode = mapping.m_keycode[bestIndex]; - - // flip low bit of bestIndex if shift is inverted. if there's a - // keycode for this new index then use it. otherwise use the old - // keycode. you'd think we should fail if there isn't a keycode - // for the new index but some keymaps only include the upper case - // keysyms (notably those on Sun Solaris) so to handle the missing - // lower case keysyms we just use the old keycode. note that - // isShiftInverted() will always return false for a shift modifier. - if (isShiftInverted(keyIndex, currentMask)) { - LOG((CLOG_DEBUG2 "shift is inverted")); - bestIndex ^= 1; - if (mapping.m_keycode[bestIndex] != 0) { - keycode = mapping.m_keycode[bestIndex]; - } - } - LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode)); - - // compute desired mask. the desired mask is the one that matches - // bestIndex, except if the key being synthesized is a shift key - // where we desire what we already have or if it's the mode switch - // key where we only desire to adjust shift. also, if the keycode - // is not sensitive to shift then don't adjust it, otherwise - // something like shift+home would become just home. similiarly - // for mode switch. - KeyModifierMask desiredMask = currentMask; - if (keyIndex->second.m_modifierMask != KeyModifierShift) { - if (keyIndex->second.m_shiftSensitive[bestIndex]) { - if ((bestIndex & 1) != 0) { - desiredMask |= KeyModifierShift; - } - else { - desiredMask &= ~KeyModifierShift; - } - } - if (keyIndex->second.m_modifierMask != KeyModifierModeSwitch) { - if (keyIndex->second.m_modeSwitchSensitive[bestIndex]) { - if ((bestIndex & 2) != 0) { - desiredMask |= KeyModifierModeSwitch; - } - else { - desiredMask &= ~KeyModifierModeSwitch; - } - } - } - } - - // adjust the modifiers to match the desired modifiers - Keystrokes undo; - KeyModifierMask tmpMask = currentMask; - if (!adjustModifiers(keys, undo, tmpMask, desiredMask)) { - LOG((CLOG_DEBUG2 "failed to adjust modifiers")); - return false; - } - - // note if the press of a half-duplex key should be treated as a release - if (isHalfDuplex && (currentMask & mapping.m_modifierMask) != 0) { - action = kRelease; - } - - // add the key event - Keystroke keystroke; - keystroke.m_sysKeyID = keycode; - switch (action) { - case kPress: - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - break; - - case kRelease: - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - break; - - case kRepeat: - keystroke.m_press = false; - keystroke.m_repeat = true; - keys.push_back(keystroke); - keystroke.m_press = true; - keys.push_back(keystroke); - break; - } - - // put undo keystrokes at end of keystrokes in reverse order - while (!undo.empty()) { - keys.push_back(undo.back()); - undo.pop_back(); - } - - // if the key is a modifier key then compute the modifier map after - // this key is pressed or released. - finalMask = currentMask; - if (mapping.m_modifierMask != 0) { - // can't be repeating if we've gotten here - assert(action != kRepeat); - - // toggle keys modify the state on release. other keys set the - // bit on press and clear the bit on release. if half-duplex - // then toggle each time we get here. - if ((m_toggleModifierMask & mapping.m_modifierMask) != 0) { - if (isHalfDuplex) { - finalMask ^= mapping.m_modifierMask; - } - } - else if (action == kPress) { - finalMask |= mapping.m_modifierMask; - } - } - - return true; -} - -bool -CXWindowsSecondaryScreen::adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask& inOutMask, - KeyModifierMask desiredMask) const -{ - // get mode switch set correctly. do this before shift because - // mode switch may be sensitive to the shift modifier and will - // set/reset it as necessary. - const bool wantModeSwitch = ((desiredMask & KeyModifierModeSwitch) != 0); - const bool haveModeSwitch = ((inOutMask & KeyModifierModeSwitch) != 0); - if (wantModeSwitch != haveModeSwitch) { - LOG((CLOG_DEBUG2 "fix mode switch")); - - // adjust shift if necessary - KeySymIndex modeSwitchIndex = m_keysymMap.find(m_modeSwitchKeysym); - assert(modeSwitchIndex != m_keysymMap.end()); - if (modeSwitchIndex->second.m_shiftSensitive[0]) { - const bool wantShift = false; - const bool haveShift = ((inOutMask & KeyModifierShift) != 0); - if (wantShift != haveShift) { - // add shift keystrokes - LOG((CLOG_DEBUG2 "fix shift for mode switch")); - if (!adjustModifier(keys, undo, m_shiftKeysym, wantShift)) { - return false; - } - inOutMask ^= KeyModifierShift; - } - } - - // add mode switch keystrokes - if (!adjustModifier(keys, undo, m_modeSwitchKeysym, wantModeSwitch)) { - return false; - } - inOutMask ^= KeyModifierModeSwitch; - } - - // get shift set correctly - const bool wantShift = ((desiredMask & KeyModifierShift) != 0); - const bool haveShift = ((inOutMask & KeyModifierShift) != 0); - if (wantShift != haveShift) { - // add shift keystrokes - LOG((CLOG_DEBUG2 "fix shift")); - if (!adjustModifier(keys, undo, m_shiftKeysym, wantShift)) { - return false; - } - inOutMask ^= KeyModifierShift; - } - - return true; -} - -bool -CXWindowsSecondaryScreen::adjustModifier(Keystrokes& keys, - Keystrokes& undo, KeySym keysym, bool desireActive) const -{ - // this method generates keystrokes to change a modifier into the - // desired state. under X11, we only expect to adjust the shift - // and mode switch states. other modifiers don't affect keysym - // generation, except num lock and caps lock and we don't change - // those but instead just invert the handling of the shift key. - // we don't check here if the modifier is already in the desired - // state; the caller should do that. - - // get the key mapping for keysym - KeySymIndex keyIndex = m_keysymMap.find(keysym); - if (keyIndex == m_keysymMap.end() || keyIndex->second.m_keycode[0] == 0) { - // no keycode for keysym or keycode is not a modifier - LOG((CLOG_DEBUG2 "no modifier for 0x%08x", keysym)); - return false; - } - - // this had better be a modifier - assert(keyIndex->second.m_modifierMask != 0); - - // we do not handle toggle modifiers here. they never need to be - // adjusted - assert((keyIndex->second.m_modifierMask & m_toggleModifierMask) == 0); - - // initialize keystroke - Keystroke keystroke; - keystroke.m_repeat = false; - - // releasing a modifier is quite different from pressing one. - // when we release a modifier we have to release every keycode that - // is assigned to the modifier since the modifier is active if any - // one of them is down. when we press a modifier we just have to - // press one of those keycodes. - if (desireActive) { - // press - keystroke.m_sysKeyID = keyIndex->second.m_keycode[0]; - keystroke.m_press = true; - keys.push_back(keystroke); - keystroke.m_press = false; - undo.push_back(keystroke); - } - else { - // release - KeyCodeToModifierMap::const_iterator index = - m_keycodeToModifier.find(keyIndex->second.m_keycode[0]); - if (index != m_keycodeToModifier.end()) { - const KeyCodes& keycodes = m_modifierKeycodes[index->second]; - for (KeyCodes::const_iterator j = keycodes.begin(); - j != keycodes.end(); ++j) { - if (isKeyDown(*j)) { - keystroke.m_sysKeyID = *j; - keystroke.m_press = false; - keys.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - } - } - } - - return true; -} - -void -CXWindowsSecondaryScreen::fakeKeyEvent(SysKeyID keycode, bool press) const -{ - CDisplayLock display(m_screen); - if (display != NULL) { - XTestFakeKeyEvent(display, keycode, press ? True : False, CurrentTime); - } -} - -void -CXWindowsSecondaryScreen::fakeMouseButton(ButtonID button, bool press) const -{ - const unsigned int xButton = mapButton(button); - if (xButton != 0) { - CDisplayLock display(m_screen); - if (display != NULL) { - XTestFakeButtonEvent(display, xButton, - press ? True : False, CurrentTime); - } - } -} - -void -CXWindowsSecondaryScreen::fakeMouseMove(SInt32 x, SInt32 y) const -{ - CDisplayLock display(m_screen); - if (m_xinerama && m_xtestIsXineramaUnaware) { - XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); - } - else { - Display* pDisplay = display; - XTestFakeMotionEvent(display, DefaultScreen(pDisplay), - x, y, CurrentTime); - } -} - -void -CXWindowsSecondaryScreen::fakeMouseWheel(SInt32 delta) const -{ - // choose button depending on rotation direction - const unsigned int xButton = mapButton(static_cast( - (delta >= 0) ? -1 : -2)); - if (xButton == 0) { - return; - } - - // now use absolute value of delta - if (delta < 0) { - delta = -delta; - } - - // send as many clicks as necessary - CDisplayLock display(m_screen); - for (; delta >= 120; delta -= 120) { - XTestFakeButtonEvent(display, xButton, True, CurrentTime); - XTestFakeButtonEvent(display, xButton, False, CurrentTime); - } -} - -void -CXWindowsSecondaryScreen::doUpdateKeys(Display* display) -{ - // query the button mapping - UInt32 numButtons = XGetPointerMapping(display, NULL, 0); - unsigned char* tmpButtons = new unsigned char[numButtons]; - XGetPointerMapping(display, tmpButtons, numButtons); - - // find the largest logical button id - unsigned char maxButton = 0; - for (UInt32 i = 0; i < numButtons; ++i) { - if (tmpButtons[i] > maxButton) { - maxButton = tmpButtons[i]; - } - } - - // allocate button array - m_buttons.resize(maxButton); - - // fill in button array values. m_buttons[i] is the physical - // button number for logical button i+1. - for (UInt32 i = 0; i < numButtons; ++i) { - m_buttons[i] = 0; - } - for (UInt32 i = 0; i < numButtons; ++i) { - m_buttons[tmpButtons[i] - 1] = i + 1; - } - - // clean up - delete[] tmpButtons; - - // update mappings - updateKeysymMap(display); -} - -void -CXWindowsSecondaryScreen::updateKeys(KeyState* keys) -{ - CDisplayLock display(m_screen); - - // ask server which keys are pressed - char xkeys[32]; - XQueryKeymap(display, xkeys); - - // transfer to our state - for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) { - keys[j + 0] = ((xkeys[i] & 0x01) != 0) ? kDown : 0; - keys[j + 1] = ((xkeys[i] & 0x02) != 0) ? kDown : 0; - keys[j + 2] = ((xkeys[i] & 0x04) != 0) ? kDown : 0; - keys[j + 3] = ((xkeys[i] & 0x08) != 0) ? kDown : 0; - keys[j + 4] = ((xkeys[i] & 0x10) != 0) ? kDown : 0; - keys[j + 5] = ((xkeys[i] & 0x20) != 0) ? kDown : 0; - keys[j + 6] = ((xkeys[i] & 0x40) != 0) ? kDown : 0; - keys[j + 7] = ((xkeys[i] & 0x80) != 0) ? kDown : 0; - } - - // update mappings and current modifiers and mouse buttons - doUpdateKeys(display); -} - -void -CXWindowsSecondaryScreen::updateKeysymMap(Display* display) -{ - // there are up to 4 keysyms per keycode - static const unsigned int maxKeysyms = 4; - - // get the number of keycodes - int minKeycode, maxKeycode; - XDisplayKeycodes(display, &minKeycode, &maxKeycode); - const int numKeycodes = maxKeycode - minKeycode + 1; - - // get the keyboard mapping for all keys - int keysymsPerKeycode; - KeySym* keysyms = XGetKeyboardMapping(display, - minKeycode, numKeycodes, - &keysymsPerKeycode); - - // we only understand up to maxKeysyms keysyms per keycodes - unsigned int numKeysyms = keysymsPerKeycode; - if (numKeysyms > maxKeysyms) { - numKeysyms = maxKeysyms; - } - - // get modifier map from server - XModifierKeymap* modifiers = XGetModifierMapping(display); - - // determine shift and mode switch sensitivity. a keysym is shift - // or mode switch sensitive if its keycode is. a keycode is mode - // mode switch sensitive if it has keysyms for indices 2 or 3. - // it's shift sensitive if the keysym for index 1 (if any) is - // different from the keysym for index 0 and, if the keysym for - // for index 3 (if any) is different from the keysym for index 2. - // that is, if shift changes the generated keysym for the keycode. - std::vector usesShift(numKeycodes); - std::vector usesModeSwitch(numKeycodes); - for (int i = 0; i < numKeycodes; ++i) { - // check mode switch first - if (numKeysyms > 2 && - keysyms[i * keysymsPerKeycode + 2] != NoSymbol || - keysyms[i * keysymsPerKeycode + 3] != NoSymbol) { - usesModeSwitch[i] = true; - } - - // check index 0 with index 1 keysyms - if (keysyms[i * keysymsPerKeycode + 0] != NoSymbol && - keysyms[i * keysymsPerKeycode + 1] != NoSymbol && - keysyms[i * keysymsPerKeycode + 1] != - keysyms[i * keysymsPerKeycode + 0]) { - usesShift[i] = true; - } - - else if (numKeysyms >= 4 && - keysyms[i * keysymsPerKeycode + 2] != NoSymbol && - keysyms[i * keysymsPerKeycode + 3] != NoSymbol && - keysyms[i * keysymsPerKeycode + 3] != - keysyms[i * keysymsPerKeycode + 2]) { - usesShift[i] = true; - } - } - - // initialize - m_keysymMap.clear(); - int keysPerModifier = modifiers->max_keypermod; - - // for each modifier keycode, get the index 0 keycode and add it to - // the keysym map. also collect all keycodes for each modifier. - m_keycodeToModifier.clear(); - for (ModifierIndex i = 0; i < 8; ++i) { - // start with no keycodes for this modifier - m_modifierKeycodes[i].clear(); - - // no mask for this modifier - m_modifierIndexToMask[i] = 0; - - // add each keycode for modifier - for (unsigned int j = 0; j < keysPerModifier; ++j) { - // get keycode and ignore unset keycodes - KeyCode keycode = modifiers->modifiermap[i * keysPerModifier + j]; - if (keycode == 0) { - continue; - } - - // save keycode for modifier and modifier for keycode - m_modifierKeycodes[i].push_back(keycode); - m_keycodeToModifier[keycode] = i; - - // get keysym and get/create key mapping - const int keycodeIndex = keycode - minKeycode; - const KeySym keysym = keysyms[keycodeIndex * - keysymsPerKeycode + 0]; - KeyMapping& mapping = m_keysymMap[keysym]; - - // skip if we already have a keycode for this index - if (mapping.m_keycode[0] != 0) { - continue; - } - - // save modifier mask - m_modifierIndexToMask[i] = mapToModifierMask(i, keysym); - - // fill in keysym info - mapping.m_keycode[0] = keycode; - mapping.m_shiftSensitive[0] = usesShift[keycodeIndex]; - mapping.m_modeSwitchSensitive[0] = usesModeSwitch[keycodeIndex]; - mapping.m_modifierMask = m_modifierIndexToMask[i]; - mapping.m_capsLockSensitive = false; - mapping.m_numLockSensitive = false; - } - } - - // create a convenient NoSymbol entry (if it doesn't exist yet). - // sometimes it's useful to handle NoSymbol like a normal keysym. - // remove any entry for NoSymbol. that keysym doesn't count. - { - KeyMapping& mapping = m_keysymMap[NoSymbol]; - for (unsigned int i = 0; i < numKeysyms; ++i) { - mapping.m_keycode[i] = 0; - mapping.m_shiftSensitive[i] = false; - mapping.m_modeSwitchSensitive[i] = false; - } - mapping.m_modifierMask = 0; - mapping.m_capsLockSensitive = false; - mapping.m_numLockSensitive = false; - } - - // add each keysym to the map, unless we've already inserted a key - // for that keysym index. - for (int i = 0; i < numKeycodes; ++i) { - for (unsigned int j = 0; j < numKeysyms; ++j) { - // lookup keysym - const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; - if (keysym == NoSymbol) { - continue; - } - KeyMapping& mapping = m_keysymMap[keysym]; - - // skip if we already have a keycode for this index - if (mapping.m_keycode[j] != 0) { - continue; - } - - // fill in keysym info - if (mapping.m_keycode[0] == 0) { - mapping.m_modifierMask = 0; - } - mapping.m_keycode[j] = static_cast( - minKeycode + i); - mapping.m_shiftSensitive[j] = usesShift[i]; - mapping.m_modeSwitchSensitive[j] = usesModeSwitch[i]; - mapping.m_numLockSensitive = adjustForNumLock(keysym); - mapping.m_capsLockSensitive = adjustForCapsLock(keysym); - } - } - - // choose the keysym to use for each modifier. if the modifier - // isn't mapped then use NoSymbol. if a modifier has both left - // and right versions then (arbitrarily) prefer the left. also - // collect the available modifier bits. - struct CModifierBitInfo { - public: - KeySym CXWindowsSecondaryScreen::*m_keysym; - KeySym m_left; - KeySym m_right; - }; - static const CModifierBitInfo s_modifierBitTable[] = { - { &CXWindowsSecondaryScreen::m_shiftKeysym, XK_Shift_L, XK_Shift_R }, - { &CXWindowsSecondaryScreen::m_ctrlKeysym, XK_Control_L, XK_Control_R }, - { &CXWindowsSecondaryScreen::m_altKeysym, XK_Alt_L, XK_Alt_R }, - { &CXWindowsSecondaryScreen::m_metaKeysym, XK_Meta_L, XK_Meta_R }, - { &CXWindowsSecondaryScreen::m_superKeysym, XK_Super_L, XK_Super_R }, - { &CXWindowsSecondaryScreen::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol }, - { &CXWindowsSecondaryScreen::m_numLockKeysym, XK_Num_Lock, NoSymbol }, - { &CXWindowsSecondaryScreen::m_capsLockKeysym, XK_Caps_Lock, NoSymbol }, - { &CXWindowsSecondaryScreen::m_scrollLockKeysym, XK_Scroll_Lock, NoSymbol } - }; - m_modifierMask = 0; - m_toggleModifierMask = 0; - for (size_t i = 0; i < sizeof(s_modifierBitTable) / - sizeof(s_modifierBitTable[0]); ++i) { - const CModifierBitInfo& info = s_modifierBitTable[i]; - - // find available keysym - KeySymIndex keyIndex = m_keysymMap.find(info.m_left); - if (keyIndex == m_keysymMap.end() && info.m_right != NoSymbol) { - keyIndex = m_keysymMap.find(info.m_right); - } - if (keyIndex != m_keysymMap.end() && - keyIndex->second.m_modifierMask != 0) { - this->*(info.m_keysym) = keyIndex->first; - } - else { - this->*(info.m_keysym) = NoSymbol; - continue; - } - - // add modifier bit - m_modifierMask |= keyIndex->second.m_modifierMask; - if (isToggleKeysym(this->*(info.m_keysym))) { - m_toggleModifierMask |= keyIndex->second.m_modifierMask; - } - } - - // if there's no mode switch key mapped then remove all keycodes - // that depend on it and no keycode can be mode switch sensitive. - if (m_modeSwitchKeysym == NoSymbol) { - LOG((CLOG_DEBUG2 "no mode switch in keymap")); - for (KeySymMap::iterator i = m_keysymMap.begin(); - i != m_keysymMap.end(); ) { - i->second.m_keycode[2] = 0; - i->second.m_keycode[3] = 0; - i->second.m_modeSwitchSensitive[0] = false; - i->second.m_modeSwitchSensitive[1] = false; - i->second.m_modeSwitchSensitive[2] = false; - i->second.m_modeSwitchSensitive[3] = false; - - // if this keysym no has no keycodes then remove it - // except for the NoSymbol keysym mapping. - if (i->second.m_keycode[0] == 0 && i->second.m_keycode[1] == 0) { - m_keysymMap.erase(i++); - } - else { - ++i; - } - } - } - - // clean up - XFree(keysyms); - XFreeModifiermap(modifiers); -} - -KeyModifierMask -CXWindowsSecondaryScreen::getModifiers() const -{ - CDisplayLock display(m_screen); - - // query the pointer to get the keyboard state - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int state; - if (!XQueryPointer(display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &state)) { - state = 0; - } - - // update active modifier mask - KeyModifierMask mask = 0; - for (ModifierIndex i = 0; i < 8; ++i) { - const KeyModifierMask bit = m_modifierIndexToMask[i]; - if ((bit & m_toggleModifierMask) == 0) { - for (KeyCodes::const_iterator j = m_modifierKeycodes[i].begin(); - j != m_modifierKeycodes[i].end(); ++j) { -// XXX -- is this right? - if (isKeyDown(*j)) { - mask |= bit; - break; - } - } - } - else if ((bit & state) != 0) { - // toggle is on - mask |= bit; - } - } - - return mask; -} - -CSecondaryScreen::SysKeyID -CXWindowsSecondaryScreen::getToggleSysKey(KeyID keyID) const -{ - // convert KeyID to KeySym - KeySym keysym; - switch (keyID) { - case kKeyNumLock: - keysym = m_numLockKeysym; - break; - - case kKeyCapsLock: - keysym = m_capsLockKeysym; - break; - - case kKeyScrollLock: - keysym = m_scrollLockKeysym; - break; - - default: - return 0; - } - - // lookup the key mapping - KeySymIndex index = m_keysymMap.find(keysym); - if (index == m_keysymMap.end()) { - return 0; - } - return index->second.m_keycode[0]; -} - -bool -CXWindowsSecondaryScreen::isToggleKeysym(KeySym key) -{ - switch (key) { - case XK_Caps_Lock: - case XK_Shift_Lock: - case XK_Num_Lock: - case XK_Scroll_Lock: - return true; - - default: - return false; - } -} - -KeyModifierMask -CXWindowsSecondaryScreen::mapToModifierMask( - ModifierIndex i, KeySym keysym) const -{ - // some modifier indices (0,1,2) are dedicated to particular uses, - // the rest depend on the keysyms bound. - switch (i) { - case 0: - return KeyModifierShift; - - case 1: - return KeyModifierCapsLock; - - case 2: - return KeyModifierControl; - - default: - switch (keysym) { - case XK_Shift_L: - case XK_Shift_R: - return KeyModifierShift; - - case XK_Control_L: - case XK_Control_R: - return KeyModifierControl; - - case XK_Alt_L: - case XK_Alt_R: - return KeyModifierAlt; - - case XK_Meta_L: - case XK_Meta_R: - return KeyModifierMeta; - - case XK_Super_L: - case XK_Super_R: - return KeyModifierSuper; - - case XK_Mode_switch: - return KeyModifierModeSwitch; - - case XK_Caps_Lock: - return KeyModifierCapsLock; - - case XK_Num_Lock: - return KeyModifierNumLock; - - case XK_Scroll_Lock: - return KeyModifierScrollLock; - - default: - return 0; - } - } -} - -// map special KeyID keys to KeySyms -#if defined(HAVE_X11_XF86KEYSYM_H) -static const KeySym g_mapE000[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, - /* 0xa4 */ 0, 0, - /* 0xa6 */ XF86XK_Back, XF86XK_Forward, - /* 0xa8 */ XF86XK_Refresh, XF86XK_Stop, - /* 0xaa */ XF86XK_Search, XF86XK_Favorites, - /* 0xac */ XF86XK_HomePage, XF86XK_AudioMute, - /* 0xae */ XF86XK_AudioLowerVolume, XF86XK_AudioRaiseVolume, - /* 0xb0 */ XF86XK_AudioNext, XF86XK_AudioPrev, - /* 0xb2 */ XF86XK_AudioStop, XF86XK_AudioPlay, - /* 0xb4 */ XF86XK_Mail, XF86XK_AudioMedia, - /* 0xb6 */ XF86XK_Launch0, XF86XK_Launch1, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -#endif - -KeySym -CXWindowsSecondaryScreen::keyIDToKeySym(KeyID id, KeyModifierMask mask) const -{ - // convert id to keysym - KeySym keysym = NoSymbol; - if ((id & 0xfffff000) == 0xe000) { - // special character - switch (id & 0x0000ff00) { -#if defined(HAVE_X11_XF86KEYSYM_H) - case 0xe000: - return g_mapE000[id & 0xff]; -#endif - - case 0xee00: - // ISO 9995 Function and Modifier Keys - if (id == kKeyLeftTab) { - keysym = XK_ISO_Left_Tab; - } - break; - - case 0xef00: - // MISCELLANY - keysym = static_cast(id - 0xef00 + 0xff00); - break; - } - } - else if ((id >= 0x0020 && id <= 0x007e) || - (id >= 0x00a0 && id <= 0x00ff)) { - // Latin-1 maps directly - return static_cast(id); - } - else { - // lookup keysym in table - return CXWindowsUtil::mapUCS4ToKeySym(id); - } - - // fail if unknown key - if (keysym == NoSymbol) { - return keysym; - } - - // if kKeyTab is requested with shift active then try XK_ISO_Left_Tab - // instead. if that doesn't work, we'll fall back to XK_Tab with - // shift active. this is to handle primary screens that don't map - // XK_ISO_Left_Tab sending events to secondary screens that do. - if (keysym == XK_Tab && (mask & KeyModifierShift) != 0) { - keysym = XK_ISO_Left_Tab; - } - - // some keysyms have emergency backups (particularly the numpad - // keys since most laptops don't have a separate numpad and the - // numpad overlaying the main keyboard may not have movement - // key bindings). figure out the emergency backup. - KeySym backupKeysym; - switch (keysym) { - case XK_KP_Home: - backupKeysym = XK_Home; - break; - - case XK_KP_Left: - backupKeysym = XK_Left; - break; - - case XK_KP_Up: - backupKeysym = XK_Up; - break; - - case XK_KP_Right: - backupKeysym = XK_Right; - break; - - case XK_KP_Down: - backupKeysym = XK_Down; - break; - - case XK_KP_Prior: - backupKeysym = XK_Prior; - break; - - case XK_KP_Next: - backupKeysym = XK_Next; - break; - - case XK_KP_End: - backupKeysym = XK_End; - break; - - case XK_KP_Insert: - backupKeysym = XK_Insert; - break; - - case XK_KP_Delete: - backupKeysym = XK_Delete; - break; - - case XK_ISO_Left_Tab: - backupKeysym = XK_Tab; - break; - - default: - backupKeysym = keysym; - break; - } - - // see if the keysym is assigned to any keycode. if not and the - // backup keysym is then use the backup keysym. - if (backupKeysym != keysym && - m_keysymMap.find(keysym) == m_keysymMap.end() && - m_keysymMap.find(backupKeysym) != m_keysymMap.end()) { - keysym = backupKeysym; - } - - return keysym; -} - -bool -CXWindowsSecondaryScreen::decomposeKeySym(KeySym keysym, - KeySyms& decomposed) const -{ - // unfortunately, X11 doesn't appear to have any way of - // decomposing a keysym into its component keysyms. we'll - // use a lookup table for certain character sets. - const KeySymsMap& table = getDecomposedKeySymTable(); - KeySymsMap::const_iterator i = table.find(keysym); - if (i == table.end()) { - return false; - } - decomposed = i->second; - return true; -} - -bool -CXWindowsSecondaryScreen::adjustForNumLock(KeySym keysym) const -{ - return (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)); -} - -bool -CXWindowsSecondaryScreen::adjustForCapsLock(KeySym keysym) const -{ - KeySym lKey, uKey; - XConvertCase(keysym, &lKey, &uKey); - return (lKey != uKey); -} - -const CXWindowsSecondaryScreen::KeySymsMap& -CXWindowsSecondaryScreen::getDecomposedKeySymTable() -{ - static const KeySym s_rawTable[] = { - // non-dead version of dead keys - XK_grave, XK_dead_grave, XK_space, 0, - XK_acute, XK_dead_acute, XK_space, 0, - XK_asciicircum, XK_dead_circumflex, XK_space, 0, - XK_asciitilde, XK_dead_tilde, XK_space, 0, - XK_cedilla, XK_dead_cedilla, XK_space, 0, - XK_ogonek, XK_dead_ogonek, XK_space, 0, - XK_caron, XK_dead_caron, XK_space, 0, - XK_abovedot, XK_dead_abovedot, XK_space, 0, - XK_doubleacute, XK_dead_doubleacute, XK_space, 0, - XK_breve, XK_dead_breve, XK_space, 0, - XK_macron, XK_dead_macron, XK_space, 0, - - // Latin-1 (ISO 8859-1) - XK_Agrave, XK_dead_grave, XK_A, 0, - XK_Aacute, XK_dead_acute, XK_A, 0, - XK_Acircumflex, XK_dead_circumflex, XK_A, 0, - XK_Atilde, XK_dead_tilde, XK_A, 0, - XK_Adiaeresis, XK_dead_diaeresis, XK_A, 0, - XK_Aring, XK_dead_abovering, XK_A, 0, - XK_Ccedilla, XK_dead_cedilla, XK_C, 0, - XK_Egrave, XK_dead_grave, XK_E, 0, - XK_Eacute, XK_dead_acute, XK_E, 0, - XK_Ecircumflex, XK_dead_circumflex, XK_E, 0, - XK_Ediaeresis, XK_dead_diaeresis, XK_E, 0, - XK_Igrave, XK_dead_grave, XK_I, 0, - XK_Iacute, XK_dead_acute, XK_I, 0, - XK_Icircumflex, XK_dead_circumflex, XK_I, 0, - XK_Idiaeresis, XK_dead_diaeresis, XK_I, 0, - XK_Ntilde, XK_dead_tilde, XK_N, 0, - XK_Ograve, XK_dead_grave, XK_O, 0, - XK_Oacute, XK_dead_acute, XK_O, 0, - XK_Ocircumflex, XK_dead_circumflex, XK_O, 0, - XK_Otilde, XK_dead_tilde, XK_O, 0, - XK_Odiaeresis, XK_dead_diaeresis, XK_O, 0, - XK_Ugrave, XK_dead_grave, XK_U, 0, - XK_Uacute, XK_dead_acute, XK_U, 0, - XK_Ucircumflex, XK_dead_circumflex, XK_U, 0, - XK_Udiaeresis, XK_dead_diaeresis, XK_U, 0, - XK_Yacute, XK_dead_acute, XK_Y, 0, - XK_agrave, XK_dead_grave, XK_a, 0, - XK_aacute, XK_dead_acute, XK_a, 0, - XK_acircumflex, XK_dead_circumflex, XK_a, 0, - XK_atilde, XK_dead_tilde, XK_a, 0, - XK_adiaeresis, XK_dead_diaeresis, XK_a, 0, - XK_aring, XK_dead_abovering, XK_a, 0, - XK_ccedilla, XK_dead_cedilla, XK_c, 0, - XK_egrave, XK_dead_grave, XK_e, 0, - XK_eacute, XK_dead_acute, XK_e, 0, - XK_ecircumflex, XK_dead_circumflex, XK_e, 0, - XK_ediaeresis, XK_dead_diaeresis, XK_e, 0, - XK_igrave, XK_dead_grave, XK_i, 0, - XK_iacute, XK_dead_acute, XK_i, 0, - XK_icircumflex, XK_dead_circumflex, XK_i, 0, - XK_idiaeresis, XK_dead_diaeresis, XK_i, 0, - XK_ntilde, XK_dead_tilde, XK_n, 0, - XK_ograve, XK_dead_grave, XK_o, 0, - XK_oacute, XK_dead_acute, XK_o, 0, - XK_ocircumflex, XK_dead_circumflex, XK_o, 0, - XK_otilde, XK_dead_tilde, XK_o, 0, - XK_odiaeresis, XK_dead_diaeresis, XK_o, 0, - XK_ugrave, XK_dead_grave, XK_u, 0, - XK_uacute, XK_dead_acute, XK_u, 0, - XK_ucircumflex, XK_dead_circumflex, XK_u, 0, - XK_udiaeresis, XK_dead_diaeresis, XK_u, 0, - XK_yacute, XK_dead_acute, XK_y, 0, - XK_ydiaeresis, XK_dead_diaeresis, XK_y, 0, - - // Latin-2 (ISO 8859-2) - XK_Aogonek, XK_dead_ogonek, XK_A, 0, - XK_Lcaron, XK_dead_caron, XK_L, 0, - XK_Sacute, XK_dead_acute, XK_S, 0, - XK_Scaron, XK_dead_caron, XK_S, 0, - XK_Scedilla, XK_dead_cedilla, XK_S, 0, - XK_Tcaron, XK_dead_caron, XK_T, 0, - XK_Zacute, XK_dead_acute, XK_Z, 0, - XK_Zcaron, XK_dead_caron, XK_Z, 0, - XK_Zabovedot, XK_dead_abovedot, XK_Z, 0, - XK_aogonek, XK_dead_ogonek, XK_a, 0, - XK_lcaron, XK_dead_caron, XK_l, 0, - XK_sacute, XK_dead_acute, XK_s, 0, - XK_scaron, XK_dead_caron, XK_s, 0, - XK_scedilla, XK_dead_cedilla, XK_s, 0, - XK_tcaron, XK_dead_caron, XK_t, 0, - XK_zacute, XK_dead_acute, XK_z, 0, - XK_zcaron, XK_dead_caron, XK_z, 0, - XK_zabovedot, XK_dead_abovedot, XK_z, 0, - XK_Racute, XK_dead_acute, XK_R, 0, - XK_Abreve, XK_dead_breve, XK_A, 0, - XK_Lacute, XK_dead_acute, XK_L, 0, - XK_Cacute, XK_dead_acute, XK_C, 0, - XK_Ccaron, XK_dead_caron, XK_C, 0, - XK_Eogonek, XK_dead_ogonek, XK_E, 0, - XK_Ecaron, XK_dead_caron, XK_E, 0, - XK_Dcaron, XK_dead_caron, XK_D, 0, - XK_Nacute, XK_dead_acute, XK_N, 0, - XK_Ncaron, XK_dead_caron, XK_N, 0, - XK_Odoubleacute, XK_dead_doubleacute, XK_O, 0, - XK_Rcaron, XK_dead_caron, XK_R, 0, - XK_Uring, XK_dead_abovering, XK_U, 0, - XK_Udoubleacute, XK_dead_doubleacute, XK_U, 0, - XK_Tcedilla, XK_dead_cedilla, XK_T, 0, - XK_racute, XK_dead_acute, XK_r, 0, - XK_abreve, XK_dead_breve, XK_a, 0, - XK_lacute, XK_dead_acute, XK_l, 0, - XK_cacute, XK_dead_acute, XK_c, 0, - XK_ccaron, XK_dead_caron, XK_c, 0, - XK_eogonek, XK_dead_ogonek, XK_e, 0, - XK_ecaron, XK_dead_caron, XK_e, 0, - XK_dcaron, XK_dead_caron, XK_d, 0, - XK_nacute, XK_dead_acute, XK_n, 0, - XK_ncaron, XK_dead_caron, XK_n, 0, - XK_odoubleacute, XK_dead_doubleacute, XK_o, 0, - XK_rcaron, XK_dead_caron, XK_r, 0, - XK_uring, XK_dead_abovering, XK_u, 0, - XK_udoubleacute, XK_dead_doubleacute, XK_u, 0, - XK_tcedilla, XK_dead_cedilla, XK_t, 0, - - // Latin-3 (ISO 8859-3) - XK_Hcircumflex, XK_dead_circumflex, XK_H, 0, - XK_Iabovedot, XK_dead_abovedot, XK_I, 0, - XK_Gbreve, XK_dead_breve, XK_G, 0, - XK_Jcircumflex, XK_dead_circumflex, XK_J, 0, - XK_hcircumflex, XK_dead_circumflex, XK_h, 0, - XK_gbreve, XK_dead_breve, XK_g, 0, - XK_jcircumflex, XK_dead_circumflex, XK_j, 0, - XK_Cabovedot, XK_dead_abovedot, XK_C, 0, - XK_Ccircumflex, XK_dead_circumflex, XK_C, 0, - XK_Gabovedot, XK_dead_abovedot, XK_G, 0, - XK_Gcircumflex, XK_dead_circumflex, XK_G, 0, - XK_Ubreve, XK_dead_breve, XK_U, 0, - XK_Scircumflex, XK_dead_circumflex, XK_S, 0, - XK_cabovedot, XK_dead_abovedot, XK_c, 0, - XK_ccircumflex, XK_dead_circumflex, XK_c, 0, - XK_gabovedot, XK_dead_abovedot, XK_g, 0, - XK_gcircumflex, XK_dead_circumflex, XK_g, 0, - XK_ubreve, XK_dead_breve, XK_u, 0, - XK_scircumflex, XK_dead_circumflex, XK_s, 0, - - // Latin-4 (ISO 8859-4) - XK_scircumflex, XK_dead_circumflex, XK_s, 0, - XK_Rcedilla, XK_dead_cedilla, XK_R, 0, - XK_Itilde, XK_dead_tilde, XK_I, 0, - XK_Lcedilla, XK_dead_cedilla, XK_L, 0, - XK_Emacron, XK_dead_macron, XK_E, 0, - XK_Gcedilla, XK_dead_cedilla, XK_G, 0, - XK_rcedilla, XK_dead_cedilla, XK_r, 0, - XK_itilde, XK_dead_tilde, XK_i, 0, - XK_lcedilla, XK_dead_cedilla, XK_l, 0, - XK_emacron, XK_dead_macron, XK_e, 0, - XK_gcedilla, XK_dead_cedilla, XK_g, 0, - XK_Amacron, XK_dead_macron, XK_A, 0, - XK_Iogonek, XK_dead_ogonek, XK_I, 0, - XK_Eabovedot, XK_dead_abovedot, XK_E, 0, - XK_Imacron, XK_dead_macron, XK_I, 0, - XK_Ncedilla, XK_dead_cedilla, XK_N, 0, - XK_Omacron, XK_dead_macron, XK_O, 0, - XK_Kcedilla, XK_dead_cedilla, XK_K, 0, - XK_Uogonek, XK_dead_ogonek, XK_U, 0, - XK_Utilde, XK_dead_tilde, XK_U, 0, - XK_Umacron, XK_dead_macron, XK_U, 0, - XK_amacron, XK_dead_macron, XK_a, 0, - XK_iogonek, XK_dead_ogonek, XK_i, 0, - XK_eabovedot, XK_dead_abovedot, XK_e, 0, - XK_imacron, XK_dead_macron, XK_i, 0, - XK_ncedilla, XK_dead_cedilla, XK_n, 0, - XK_omacron, XK_dead_macron, XK_o, 0, - XK_kcedilla, XK_dead_cedilla, XK_k, 0, - XK_uogonek, XK_dead_ogonek, XK_u, 0, - XK_utilde, XK_dead_tilde, XK_u, 0, - XK_umacron, XK_dead_macron, XK_u, 0, - - // Latin-8 (ISO 8859-14) -#if defined(XK_Babovedot) - XK_Babovedot, XK_dead_abovedot, XK_B, 0, - XK_babovedot, XK_dead_abovedot, XK_b, 0, - XK_Dabovedot, XK_dead_abovedot, XK_D, 0, - XK_Wgrave, XK_dead_grave, XK_W, 0, - XK_Wacute, XK_dead_acute, XK_W, 0, - XK_dabovedot, XK_dead_abovedot, XK_d, 0, - XK_Ygrave, XK_dead_grave, XK_Y, 0, - XK_Fabovedot, XK_dead_abovedot, XK_F, 0, - XK_fabovedot, XK_dead_abovedot, XK_f, 0, - XK_Mabovedot, XK_dead_abovedot, XK_M, 0, - XK_mabovedot, XK_dead_abovedot, XK_m, 0, - XK_Pabovedot, XK_dead_abovedot, XK_P, 0, - XK_wgrave, XK_dead_grave, XK_w, 0, - XK_pabovedot, XK_dead_abovedot, XK_p, 0, - XK_wacute, XK_dead_acute, XK_w, 0, - XK_Sabovedot, XK_dead_abovedot, XK_S, 0, - XK_ygrave, XK_dead_grave, XK_y, 0, - XK_Wdiaeresis, XK_dead_diaeresis, XK_W, 0, - XK_wdiaeresis, XK_dead_diaeresis, XK_w, 0, - XK_sabovedot, XK_dead_abovedot, XK_s, 0, - XK_Wcircumflex, XK_dead_circumflex, XK_W, 0, - XK_Tabovedot, XK_dead_abovedot, XK_T, 0, - XK_Ycircumflex, XK_dead_circumflex, XK_Y, 0, - XK_wcircumflex, XK_dead_circumflex, XK_w, 0, - XK_tabovedot, XK_dead_abovedot, XK_t, 0, - XK_ycircumflex, XK_dead_circumflex, XK_y, 0, -#endif - - // Latin-9 (ISO 8859-15) -#if defined(XK_Ydiaeresis) - XK_Ydiaeresis, XK_dead_diaeresis, XK_Y, 0, -#endif - - // end of table - 0 - }; - - // fill table if not yet initialized - if (s_decomposedKeySyms.empty()) { - const KeySym* scan = s_rawTable; - while (*scan != 0) { - // add an entry for this keysym - KeySyms& entry = s_decomposedKeySyms[*scan]; - - // add the decomposed keysyms for the keysym - while (*++scan != 0) { - entry.push_back(*scan); - } - - // skip end of entry marker - ++scan; - } - } - - return s_decomposedKeySyms; -} - - -// -// CXWindowsSecondaryScreen::KeyMapping -// - -CXWindowsSecondaryScreen::KeyMapping::KeyMapping() -{ - m_keycode[0] = 0; - m_keycode[1] = 0; - m_keycode[2] = 0; - m_keycode[3] = 0; -} diff --git a/lib/platform/CXWindowsSecondaryScreen.h b/lib/platform/CXWindowsSecondaryScreen.h deleted file mode 100644 index 2f22bcf7..00000000 --- a/lib/platform/CXWindowsSecondaryScreen.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CXWINDOWSSECONDARYSCREEN_H -#define CXWINDOWSSECONDARYSCREEN_H - -#include "CSecondaryScreen.h" -#include "IScreenEventHandler.h" -#include "stdmap.h" -#include "stdvector.h" -#if defined(X_DISPLAY_MISSING) -# error X11 is required to build synergy -#else -# include -#endif - -class CXWindowsScreen; -class IScreenReceiver; - -//! X11 secondary screen implementation -class CXWindowsSecondaryScreen : - public CSecondaryScreen, public IScreenEventHandler { -public: - CXWindowsSecondaryScreen(IScreenReceiver*); - virtual ~CXWindowsSecondaryScreen(); - - // CSecondaryScreen overrides - virtual void resetOptions(); - virtual void setOptions(const COptionsList& options); - virtual IScreen* getScreen() const; - - // IScreenEventHandler overrides - virtual void onScreensaver(bool activated); - virtual bool onPreDispatch(const CEvent* event); - virtual bool onEvent(CEvent* event); - virtual void onOneShotTimerExpired(UInt32 id); - -protected: - // CSecondaryScreen overrides - virtual void onPreMainLoop(); - virtual void onPreOpen(); - virtual void onPostOpen(); - virtual void onPreClose(); - virtual void onPreEnter(); - virtual void onPostEnter(); - virtual void onPreLeave(); - virtual void createWindow(); - virtual void destroyWindow(); - virtual void showWindow(SInt32 x, SInt32 y); - virtual void hideWindow(); - virtual void updateKeys(KeyState* sysKeyStates); - virtual KeyModifierMask getModifiers() const; - - virtual bool isAutoRepeating(SysKeyID) const; - virtual KeyModifierMask getModifierKeyMask(SysKeyID) const; - virtual bool isModifierActive(SysKeyID) const; - virtual SysKeyID getToggleSysKey(KeyID keyID) const; - virtual void flush(); - virtual KeyModifierMask - mapKey(Keystrokes&, SysKeyID& sysKeyID, KeyID, - KeyModifierMask, KeyModifierMask, EKeyAction) const; - virtual void fakeKeyEvent(SysKeyID, bool press) const; - virtual void fakeMouseButton(ButtonID, bool press) const; - virtual void fakeMouseMove(SInt32 x, SInt32 y) const; - virtual void fakeMouseWheel(SInt32 delta) const; - -private: - typedef unsigned int ModifierIndex; - class KeyMapping { - public: - KeyMapping(); - - public: - // KeyCode to generate keysym and whether keycode[i] is - // sensitive to shift and mode switch. - KeyCode m_keycode[4]; - bool m_shiftSensitive[4]; - bool m_modeSwitchSensitive[4]; - - // the modifier mask of keysym or 0 if not a modifier - KeyModifierMask m_modifierMask; - - // whether keysym is sensitive to caps and num lock - bool m_numLockSensitive; - bool m_capsLockSensitive; - }; - - typedef std::vector KeyCodes; - typedef std::map KeyCodeToModifierMap; - typedef std::map KeySymMap; - typedef KeySymMap::const_iterator KeySymIndex; - typedef std::vector KeySyms; - typedef std::map KeySymsMap; - - unsigned int mapButton(ButtonID button) const; - - bool mapToKeystrokes(Keystrokes& keys, - SysKeyID& keycode, - KeyModifierMask& finalMask, - KeySymIndex keyIndex, - KeyModifierMask currentMask, - EKeyAction action, - bool isHalfDuplex) const; - bool adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask& inOutMask, - KeyModifierMask desiredMask) const; - bool adjustModifier(Keystrokes& keys, - Keystrokes& undo, - KeySym keysym, - bool desireActive) const; - KeyModifierMask mapToModifierMask(ModifierIndex, KeySym) const; - - unsigned int findBestKeyIndex(KeySymIndex keyIndex, - KeyModifierMask currentMask) const; - bool isShiftInverted(KeySymIndex keyIndex, - KeyModifierMask currentMask) const; - - void doUpdateKeys(Display*); - void updateKeysymMap(Display* display); - void updateModifiers(Display* display); - ModifierIndex keySymToModifierIndex(KeySym) const; - static bool isToggleKeysym(KeySym); - - KeySym keyIDToKeySym(KeyID id, KeyModifierMask mask) const; - bool adjustForNumLock(KeySym) const; - bool adjustForCapsLock(KeySym) const; - - bool decomposeKeySym(KeySym keysym, - KeySyms& decomposed) const; - static const KeySymsMap& getDecomposedKeySymTable(); - -private: - CXWindowsScreen* m_screen; - Window m_window; - - // logical to physical button mapping. m_buttons[i] gives the - // physical button for logical button i+1. - std::vector m_buttons; - - // the modifiers that have keys bound to them - KeyModifierMask m_modifierMask; - - // set bits indicate modifiers that toggle (e.g. caps-lock) - KeyModifierMask m_toggleModifierMask; - - // keysym to keycode mapping - KeySymMap m_keysymMap; - - // modifier index to keycodes - KeyCodes m_modifierKeycodes[8]; - - // modifier index to modifier mask - KeyModifierMask m_modifierIndexToMask[8]; - - // keycode to modifier index - KeyCodeToModifierMap m_keycodeToModifier; - - // modifier keysyms - KeySym m_shiftKeysym; - KeySym m_ctrlKeysym; - KeySym m_altKeysym; - KeySym m_metaKeysym; - KeySym m_superKeysym; - KeySym m_modeSwitchKeysym; - KeySym m_numLockKeysym; - KeySym m_capsLockKeysym; - KeySym m_scrollLockKeysym; - - // the keyboard control state the last time this screen was entered - XKeyboardState m_keyControl; - - // stuff to workaround xtest being xinerama unaware. attempting - // to fake a mouse motion under xinerama may behave strangely, - // especially if screen 0 is not at 0,0 or if faking a motion on - // a screen other than screen 0. - bool m_xtestIsXineramaUnaware; - bool m_xinerama; - - // a table of keysym decompositions - static KeySymsMap s_decomposedKeySyms; -}; - -#endif diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 05415ddc..066085ab 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -16,6 +16,14 @@ #include "CThread.h" #include "CLog.h" #include +#define XK_XKB_KEYS +#define XK_LATIN1 +#define XK_LATIN2 +#define XK_LATIN3 +#define XK_LATIN4 +#define XK_LATIN8 +#define XK_LATIN9 +#include /* * This table maps keysym values into the corresponding ISO 10646 @@ -805,6 +813,218 @@ struct codepair { { 0x20ac, 0x20ac } /* EuroSign EURO SIGN */ }; +static const KeySym s_rawDecomposeTable[] = { + // non-dead version of dead keys + XK_grave, XK_dead_grave, XK_space, 0, + XK_acute, XK_dead_acute, XK_space, 0, + XK_asciicircum, XK_dead_circumflex, XK_space, 0, + XK_asciitilde, XK_dead_tilde, XK_space, 0, + XK_cedilla, XK_dead_cedilla, XK_space, 0, + XK_ogonek, XK_dead_ogonek, XK_space, 0, + XK_caron, XK_dead_caron, XK_space, 0, + XK_abovedot, XK_dead_abovedot, XK_space, 0, + XK_doubleacute, XK_dead_doubleacute, XK_space, 0, + XK_breve, XK_dead_breve, XK_space, 0, + XK_macron, XK_dead_macron, XK_space, 0, + + // Latin-1 (ISO 8859-1) + XK_Agrave, XK_dead_grave, XK_A, 0, + XK_Aacute, XK_dead_acute, XK_A, 0, + XK_Acircumflex, XK_dead_circumflex, XK_A, 0, + XK_Atilde, XK_dead_tilde, XK_A, 0, + XK_Adiaeresis, XK_dead_diaeresis, XK_A, 0, + XK_Aring, XK_dead_abovering, XK_A, 0, + XK_Ccedilla, XK_dead_cedilla, XK_C, 0, + XK_Egrave, XK_dead_grave, XK_E, 0, + XK_Eacute, XK_dead_acute, XK_E, 0, + XK_Ecircumflex, XK_dead_circumflex, XK_E, 0, + XK_Ediaeresis, XK_dead_diaeresis, XK_E, 0, + XK_Igrave, XK_dead_grave, XK_I, 0, + XK_Iacute, XK_dead_acute, XK_I, 0, + XK_Icircumflex, XK_dead_circumflex, XK_I, 0, + XK_Idiaeresis, XK_dead_diaeresis, XK_I, 0, + XK_Ntilde, XK_dead_tilde, XK_N, 0, + XK_Ograve, XK_dead_grave, XK_O, 0, + XK_Oacute, XK_dead_acute, XK_O, 0, + XK_Ocircumflex, XK_dead_circumflex, XK_O, 0, + XK_Otilde, XK_dead_tilde, XK_O, 0, + XK_Odiaeresis, XK_dead_diaeresis, XK_O, 0, + XK_Ugrave, XK_dead_grave, XK_U, 0, + XK_Uacute, XK_dead_acute, XK_U, 0, + XK_Ucircumflex, XK_dead_circumflex, XK_U, 0, + XK_Udiaeresis, XK_dead_diaeresis, XK_U, 0, + XK_Yacute, XK_dead_acute, XK_Y, 0, + XK_agrave, XK_dead_grave, XK_a, 0, + XK_aacute, XK_dead_acute, XK_a, 0, + XK_acircumflex, XK_dead_circumflex, XK_a, 0, + XK_atilde, XK_dead_tilde, XK_a, 0, + XK_adiaeresis, XK_dead_diaeresis, XK_a, 0, + XK_aring, XK_dead_abovering, XK_a, 0, + XK_ccedilla, XK_dead_cedilla, XK_c, 0, + XK_egrave, XK_dead_grave, XK_e, 0, + XK_eacute, XK_dead_acute, XK_e, 0, + XK_ecircumflex, XK_dead_circumflex, XK_e, 0, + XK_ediaeresis, XK_dead_diaeresis, XK_e, 0, + XK_igrave, XK_dead_grave, XK_i, 0, + XK_iacute, XK_dead_acute, XK_i, 0, + XK_icircumflex, XK_dead_circumflex, XK_i, 0, + XK_idiaeresis, XK_dead_diaeresis, XK_i, 0, + XK_ntilde, XK_dead_tilde, XK_n, 0, + XK_ograve, XK_dead_grave, XK_o, 0, + XK_oacute, XK_dead_acute, XK_o, 0, + XK_ocircumflex, XK_dead_circumflex, XK_o, 0, + XK_otilde, XK_dead_tilde, XK_o, 0, + XK_odiaeresis, XK_dead_diaeresis, XK_o, 0, + XK_ugrave, XK_dead_grave, XK_u, 0, + XK_uacute, XK_dead_acute, XK_u, 0, + XK_ucircumflex, XK_dead_circumflex, XK_u, 0, + XK_udiaeresis, XK_dead_diaeresis, XK_u, 0, + XK_yacute, XK_dead_acute, XK_y, 0, + XK_ydiaeresis, XK_dead_diaeresis, XK_y, 0, + + // Latin-2 (ISO 8859-2) + XK_Aogonek, XK_dead_ogonek, XK_A, 0, + XK_Lcaron, XK_dead_caron, XK_L, 0, + XK_Sacute, XK_dead_acute, XK_S, 0, + XK_Scaron, XK_dead_caron, XK_S, 0, + XK_Scedilla, XK_dead_cedilla, XK_S, 0, + XK_Tcaron, XK_dead_caron, XK_T, 0, + XK_Zacute, XK_dead_acute, XK_Z, 0, + XK_Zcaron, XK_dead_caron, XK_Z, 0, + XK_Zabovedot, XK_dead_abovedot, XK_Z, 0, + XK_aogonek, XK_dead_ogonek, XK_a, 0, + XK_lcaron, XK_dead_caron, XK_l, 0, + XK_sacute, XK_dead_acute, XK_s, 0, + XK_scaron, XK_dead_caron, XK_s, 0, + XK_scedilla, XK_dead_cedilla, XK_s, 0, + XK_tcaron, XK_dead_caron, XK_t, 0, + XK_zacute, XK_dead_acute, XK_z, 0, + XK_zcaron, XK_dead_caron, XK_z, 0, + XK_zabovedot, XK_dead_abovedot, XK_z, 0, + XK_Racute, XK_dead_acute, XK_R, 0, + XK_Abreve, XK_dead_breve, XK_A, 0, + XK_Lacute, XK_dead_acute, XK_L, 0, + XK_Cacute, XK_dead_acute, XK_C, 0, + XK_Ccaron, XK_dead_caron, XK_C, 0, + XK_Eogonek, XK_dead_ogonek, XK_E, 0, + XK_Ecaron, XK_dead_caron, XK_E, 0, + XK_Dcaron, XK_dead_caron, XK_D, 0, + XK_Nacute, XK_dead_acute, XK_N, 0, + XK_Ncaron, XK_dead_caron, XK_N, 0, + XK_Odoubleacute, XK_dead_doubleacute, XK_O, 0, + XK_Rcaron, XK_dead_caron, XK_R, 0, + XK_Uring, XK_dead_abovering, XK_U, 0, + XK_Udoubleacute, XK_dead_doubleacute, XK_U, 0, + XK_Tcedilla, XK_dead_cedilla, XK_T, 0, + XK_racute, XK_dead_acute, XK_r, 0, + XK_abreve, XK_dead_breve, XK_a, 0, + XK_lacute, XK_dead_acute, XK_l, 0, + XK_cacute, XK_dead_acute, XK_c, 0, + XK_ccaron, XK_dead_caron, XK_c, 0, + XK_eogonek, XK_dead_ogonek, XK_e, 0, + XK_ecaron, XK_dead_caron, XK_e, 0, + XK_dcaron, XK_dead_caron, XK_d, 0, + XK_nacute, XK_dead_acute, XK_n, 0, + XK_ncaron, XK_dead_caron, XK_n, 0, + XK_odoubleacute, XK_dead_doubleacute, XK_o, 0, + XK_rcaron, XK_dead_caron, XK_r, 0, + XK_uring, XK_dead_abovering, XK_u, 0, + XK_udoubleacute, XK_dead_doubleacute, XK_u, 0, + XK_tcedilla, XK_dead_cedilla, XK_t, 0, + + // Latin-3 (ISO 8859-3) + XK_Hcircumflex, XK_dead_circumflex, XK_H, 0, + XK_Iabovedot, XK_dead_abovedot, XK_I, 0, + XK_Gbreve, XK_dead_breve, XK_G, 0, + XK_Jcircumflex, XK_dead_circumflex, XK_J, 0, + XK_hcircumflex, XK_dead_circumflex, XK_h, 0, + XK_gbreve, XK_dead_breve, XK_g, 0, + XK_jcircumflex, XK_dead_circumflex, XK_j, 0, + XK_Cabovedot, XK_dead_abovedot, XK_C, 0, + XK_Ccircumflex, XK_dead_circumflex, XK_C, 0, + XK_Gabovedot, XK_dead_abovedot, XK_G, 0, + XK_Gcircumflex, XK_dead_circumflex, XK_G, 0, + XK_Ubreve, XK_dead_breve, XK_U, 0, + XK_Scircumflex, XK_dead_circumflex, XK_S, 0, + XK_cabovedot, XK_dead_abovedot, XK_c, 0, + XK_ccircumflex, XK_dead_circumflex, XK_c, 0, + XK_gabovedot, XK_dead_abovedot, XK_g, 0, + XK_gcircumflex, XK_dead_circumflex, XK_g, 0, + XK_ubreve, XK_dead_breve, XK_u, 0, + XK_scircumflex, XK_dead_circumflex, XK_s, 0, + + // Latin-4 (ISO 8859-4) + XK_scircumflex, XK_dead_circumflex, XK_s, 0, + XK_Rcedilla, XK_dead_cedilla, XK_R, 0, + XK_Itilde, XK_dead_tilde, XK_I, 0, + XK_Lcedilla, XK_dead_cedilla, XK_L, 0, + XK_Emacron, XK_dead_macron, XK_E, 0, + XK_Gcedilla, XK_dead_cedilla, XK_G, 0, + XK_rcedilla, XK_dead_cedilla, XK_r, 0, + XK_itilde, XK_dead_tilde, XK_i, 0, + XK_lcedilla, XK_dead_cedilla, XK_l, 0, + XK_emacron, XK_dead_macron, XK_e, 0, + XK_gcedilla, XK_dead_cedilla, XK_g, 0, + XK_Amacron, XK_dead_macron, XK_A, 0, + XK_Iogonek, XK_dead_ogonek, XK_I, 0, + XK_Eabovedot, XK_dead_abovedot, XK_E, 0, + XK_Imacron, XK_dead_macron, XK_I, 0, + XK_Ncedilla, XK_dead_cedilla, XK_N, 0, + XK_Omacron, XK_dead_macron, XK_O, 0, + XK_Kcedilla, XK_dead_cedilla, XK_K, 0, + XK_Uogonek, XK_dead_ogonek, XK_U, 0, + XK_Utilde, XK_dead_tilde, XK_U, 0, + XK_Umacron, XK_dead_macron, XK_U, 0, + XK_amacron, XK_dead_macron, XK_a, 0, + XK_iogonek, XK_dead_ogonek, XK_i, 0, + XK_eabovedot, XK_dead_abovedot, XK_e, 0, + XK_imacron, XK_dead_macron, XK_i, 0, + XK_ncedilla, XK_dead_cedilla, XK_n, 0, + XK_omacron, XK_dead_macron, XK_o, 0, + XK_kcedilla, XK_dead_cedilla, XK_k, 0, + XK_uogonek, XK_dead_ogonek, XK_u, 0, + XK_utilde, XK_dead_tilde, XK_u, 0, + XK_umacron, XK_dead_macron, XK_u, 0, + + // Latin-8 (ISO 8859-14) +#if defined(XK_Babovedot) + XK_Babovedot, XK_dead_abovedot, XK_B, 0, + XK_babovedot, XK_dead_abovedot, XK_b, 0, + XK_Dabovedot, XK_dead_abovedot, XK_D, 0, + XK_Wgrave, XK_dead_grave, XK_W, 0, + XK_Wacute, XK_dead_acute, XK_W, 0, + XK_dabovedot, XK_dead_abovedot, XK_d, 0, + XK_Ygrave, XK_dead_grave, XK_Y, 0, + XK_Fabovedot, XK_dead_abovedot, XK_F, 0, + XK_fabovedot, XK_dead_abovedot, XK_f, 0, + XK_Mabovedot, XK_dead_abovedot, XK_M, 0, + XK_mabovedot, XK_dead_abovedot, XK_m, 0, + XK_Pabovedot, XK_dead_abovedot, XK_P, 0, + XK_wgrave, XK_dead_grave, XK_w, 0, + XK_pabovedot, XK_dead_abovedot, XK_p, 0, + XK_wacute, XK_dead_acute, XK_w, 0, + XK_Sabovedot, XK_dead_abovedot, XK_S, 0, + XK_ygrave, XK_dead_grave, XK_y, 0, + XK_Wdiaeresis, XK_dead_diaeresis, XK_W, 0, + XK_wdiaeresis, XK_dead_diaeresis, XK_w, 0, + XK_sabovedot, XK_dead_abovedot, XK_s, 0, + XK_Wcircumflex, XK_dead_circumflex, XK_W, 0, + XK_Tabovedot, XK_dead_abovedot, XK_T, 0, + XK_Ycircumflex, XK_dead_circumflex, XK_Y, 0, + XK_wcircumflex, XK_dead_circumflex, XK_w, 0, + XK_tabovedot, XK_dead_abovedot, XK_t, 0, + XK_ycircumflex, XK_dead_circumflex, XK_y, 0, +#endif + + // Latin-9 (ISO 8859-15) +#if defined(XK_Ydiaeresis) + XK_Ydiaeresis, XK_dead_diaeresis, XK_Y, 0, +#endif + + // end of table + 0 +}; + // // CXWindowsUtil @@ -812,6 +1032,7 @@ struct codepair { CXWindowsUtil::CKeySymMap CXWindowsUtil::s_keySymToUCS4; CXWindowsUtil::CUCS4Map CXWindowsUtil::s_UCS4ToKeySym; +CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_decomposedKeySyms; bool CXWindowsUtil::getWindowProperty(Display* display, Window window, @@ -1008,6 +1229,21 @@ CXWindowsUtil::mapUCS4ToKeySym(UInt32 c) } } +bool +CXWindowsUtil::decomposeKeySym(KeySym keysym, KeySyms& decomposed) +{ + // unfortunately, X11 doesn't appear to have any way of + // decomposing a keysym into its component keysyms. we'll + // use a lookup table for certain character sets. + initKeyMaps(); + CKeySymsMap::const_iterator i = s_decomposedKeySyms.find(keysym); + if (i == s_decomposedKeySyms.end()) { + return false; + } + decomposed = i->second; + return true; +} + Bool CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) { @@ -1031,6 +1267,19 @@ CXWindowsUtil::initKeyMaps() s_UCS4ToKeySym[s_keymap[i].ucs4] = s_keymap[i].keysym; } } + + // fill decomposed key table if not filled yet + if (s_decomposedKeySyms.empty()) { + for (const KeySym* scan = s_rawDecomposeTable; *scan != 0; ++scan) { + // add an entry for this keysym + KeySyms& entry = s_decomposedKeySyms[*scan]; + + // add the decomposed keysyms for the keysym + while (*++scan != 0) { + entry.push_back(*scan); + } + } + } } diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h index 7136b730..179227eb 100644 --- a/lib/platform/CXWindowsUtil.h +++ b/lib/platform/CXWindowsUtil.h @@ -18,6 +18,7 @@ #include "CString.h" #include "BasicTypes.h" #include "stdmap.h" +#include "stdvector.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else @@ -27,6 +28,8 @@ //! X11 utility functions class CXWindowsUtil { public: + typedef std::vector KeySyms; + //! Get property /*! Gets property \c property on \c window. \b Appends the data to @@ -70,6 +73,14 @@ public: */ static KeySym mapUCS4ToKeySym(UInt32); + //! Decompose a KeySym + /*! + Decomposes \c keysym into its component keysyms. All but the last + decomposed KeySym are dead keys. Returns true iff the decomposition + was successful. + */ + static bool decomposeKeySym(KeySym keysym, KeySyms& decomposed); + //! X11 error handler /*! This class sets an X error handler in the c'tor and restores the @@ -133,9 +144,11 @@ private: private: typedef std::map CKeySymMap; typedef std::map CUCS4Map; + typedef std::map CKeySymsMap; static CKeySymMap s_keySymToUCS4; static CUCS4Map s_UCS4ToKeySym; + static CKeySymsMap s_decomposedKeySyms; }; #endif diff --git a/lib/platform/IMSWindowsScreenEventHandler.h b/lib/platform/IMSWindowsScreenEventHandler.h deleted file mode 100644 index 164f41a8..00000000 --- a/lib/platform/IMSWindowsScreenEventHandler.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef IMSWINDOWSSCREENEVENTHANDLER_H -#define IMSWINDOWSSCREENEVENTHANDLER_H - -#include "IScreenEventHandler.h" -#define WIN32_LEAN_AND_MEAN -#include - -//! MS Windows screen event handler interface -class IMSWindowsScreenEventHandler : public IScreenEventHandler { -public: - //! @name manipulators - //@{ - - //! Notify of window creation - /*! - This is called after the window is created. - */ - virtual void postCreateWindow(HWND) = 0; - - //! Notify of window destruction - /*! - This is called before the window is destroyed. - */ - virtual void preDestroyWindow(HWND) = 0; - - //! Notify of newly accessible desktop - /*! - This is called when the user switched from an inaccessible desktop - to an accessible desktop. - */ - virtual void onAccessibleDesktop() = 0; - - //@} - - // IScreenEventHandler overrides - virtual void onScreensaver(bool activated) = 0; - virtual bool onPreDispatch(const CEvent* event) = 0; - virtual bool onEvent(CEvent* event) = 0; -}; - -#endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index ea082e15..891271d3 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -23,21 +23,20 @@ EXTRA_DIST = \ CMSWindowsClipboardAnyTextConverter.cpp \ CMSWindowsClipboardTextConverter.cpp \ CMSWindowsClipboardUTF16Converter.cpp \ - CMSWindowsPrimaryScreen.cpp \ + CMSWindowsDesktop.cpp \ + CMSWindowsKeyMapper.cpp \ CMSWindowsScreen.cpp \ CMSWindowsScreenSaver.cpp \ - CMSWindowsSecondaryScreen.cpp \ CSynergyHook.cpp \ CMSWindowsClipboard.h \ CMSWindowsClipboardAnyTextConverter.h \ CMSWindowsClipboardTextConverter.h \ CMSWindowsClipboardUTF16Converter.h \ - CMSWindowsPrimaryScreen.h \ + CMSWindowsDesktop.h \ + CMSWindowsKeyMapper.h \ CMSWindowsScreen.h \ CMSWindowsScreenSaver.h \ - CMSWindowsSecondaryScreen.h \ CSynergyHook.h \ - IMSWindowsScreenEventHandler.h \ $(NULL) MAINTAINERCLEANFILES = \ @@ -50,19 +49,17 @@ libplatform_a_SOURCES = \ CXWindowsClipboardTextConverter.cpp \ CXWindowsClipboardUCS2Converter.cpp \ CXWindowsClipboardUTF8Converter.cpp \ - CXWindowsPrimaryScreen.cpp \ + CXWindowsKeyMapper.cpp \ CXWindowsScreen.cpp \ CXWindowsScreenSaver.cpp \ - CXWindowsSecondaryScreen.cpp \ CXWindowsUtil.cpp \ CXWindowsClipboard.h \ CXWindowsClipboardTextConverter.h \ CXWindowsClipboardUCS2Converter.h \ CXWindowsClipboardUTF8Converter.h \ - CXWindowsPrimaryScreen.h \ + CXWindowsKeyMapper.h \ CXWindowsScreen.h \ CXWindowsScreenSaver.h \ - CXWindowsSecondaryScreen.h \ CXWindowsUtil.h \ $(NULL) INCLUDES = \ diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index ae599964..1fad2189 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -103,7 +103,11 @@ SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp # End Source File # Begin Source File -SOURCE=.\CMSWindowsPrimaryScreen.cpp +SOURCE=.\CMSWindowsDesktop.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsKeyMapper.cpp # End Source File # Begin Source File @@ -113,10 +117,6 @@ SOURCE=.\CMSWindowsScreen.cpp SOURCE=.\CMSWindowsScreenSaver.cpp # End Source File -# Begin Source File - -SOURCE=.\CMSWindowsSecondaryScreen.cpp -# End Source File # End Group # Begin Group "Header Files" @@ -139,7 +139,11 @@ SOURCE=.\CMSWindowsClipboardUTF16Converter.h # End Source File # Begin Source File -SOURCE=.\CMSWindowsPrimaryScreen.h +SOURCE=.\CMSWindowsDesktop.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsKeyMapper.h # End Source File # Begin Source File @@ -149,14 +153,6 @@ SOURCE=.\CMSWindowsScreen.h SOURCE=.\CMSWindowsScreenSaver.h # End Source File -# Begin Source File - -SOURCE=.\CMSWindowsSecondaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\IMSWindowsScreenEventHandler.h -# End Source File # End Group # End Target # End Project diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index b425a68d..54a207f1 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -13,11 +13,11 @@ */ #include "CPrimaryClient.h" -#include "IPrimaryScreenFactory.h" +#include "CScreen.h" +#include "IScreenFactory.h" #include "IServer.h" #include "XScreen.h" #include "XSynergy.h" -#include "CPrimaryScreen.h" #include "CClipboard.h" #include "CLog.h" @@ -25,7 +25,7 @@ // CPrimaryClient // -CPrimaryClient::CPrimaryClient(IPrimaryScreenFactory* screenFactory, +CPrimaryClient::CPrimaryClient(IScreenFactory* screenFactory, IServer* server, IPrimaryScreenReceiver* receiver, const CString& name) : @@ -38,7 +38,11 @@ CPrimaryClient::CPrimaryClient(IPrimaryScreenFactory* screenFactory, // create screen LOG((CLOG_DEBUG1 "creating primary screen")); if (screenFactory != NULL) { - m_screen = screenFactory->create(this, receiver); + IPlatformScreen* platformScreen = + screenFactory->create(this, receiver); + if (platformScreen != NULL) { + m_screen = new CScreen(platformScreen, this); + } } if (m_screen == NULL) { throw XScreenOpenFailure(); @@ -86,7 +90,7 @@ CPrimaryClient::isLockedToScreen() const KeyModifierMask CPrimaryClient::getToggleMask() const { - return m_screen->getToggleMask(); + return m_screen->getActiveModifiers(); } void @@ -149,13 +153,28 @@ CPrimaryClient::close() m_screen->close(); } +void +CPrimaryClient::enable() +{ + m_screen->enable(); +} + +void +CPrimaryClient::disable() +{ + m_screen->disable(); +} + void CPrimaryClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask, bool screensaver) { // note -- we must not call any server methods except onError(). m_seqNum = seqNum; - m_screen->enter(xAbs, yAbs, screensaver); + if (!screensaver) { + m_screen->warpCursor(xAbs, yAbs); + } + m_screen->enter(); } bool diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index d0202cf1..823c7297 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -19,9 +19,9 @@ #include "IScreenReceiver.h" #include "ProtocolTypes.h" +class CScreen; class IClipboard; -class CPrimaryScreen; -class IPrimaryScreenFactory; +class IScreenFactory; class IPrimaryScreenReceiver; class IServer; @@ -38,7 +38,7 @@ public: \c factory. Throws XScreenOpenFailure or whatever the factory can throw if the screen cannot be created. */ - CPrimaryClient(IPrimaryScreenFactory* factory, IServer*, + CPrimaryClient(IScreenFactory* factory, IServer*, IPrimaryScreenReceiver*, const CString& name); ~CPrimaryClient(); @@ -96,6 +96,9 @@ public: virtual bool onGrabClipboard(ClipboardID); virtual void onClipboardChanged(ClipboardID, const CString&); +// XXX -- these go in IClient + virtual void enable(); + virtual void disable(); // IClient overrides virtual void open(); virtual void mainLoop(); @@ -127,7 +130,7 @@ public: private: IServer* m_server; - CPrimaryScreen* m_screen; + CScreen* m_screen; CString m_name; UInt32 m_seqNum; CClientInfo m_info; diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index fa2487d4..0710875d 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -15,7 +15,7 @@ #include "CServer.h" #include "CHTTPServer.h" #include "CPrimaryClient.h" -#include "IPrimaryScreenFactory.h" +#include "IScreenFactory.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" @@ -89,6 +89,7 @@ CServer::open() LOG((CLOG_INFO "opening screen")); openPrimaryScreen(); setStatus(kNotRunning); + m_primaryClient->enable(); } catch (XScreen& e) { // can't open screen @@ -212,6 +213,7 @@ void CServer::close() { if (m_primaryClient != NULL) { + m_primaryClient->disable(); closePrimaryScreen(); } LOG((CLOG_INFO "closed screen")); @@ -280,7 +282,7 @@ CServer::setConfig(const CConfig& config) } void -CServer::setScreenFactory(IPrimaryScreenFactory* adopted) +CServer::setScreenFactory(IScreenFactory* adopted) { CLock lock(&m_mutex); delete m_screenFactory; @@ -1601,7 +1603,7 @@ CServer::runClient(void* vsocket) try { CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); } - catch (XSocket&) { + catch (XIO&) { // ignore } delete proxy; @@ -1614,7 +1616,7 @@ CServer::runClient(void* vsocket) try { CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); } - catch (XSocket&) { + catch (XIO&) { // ignore } delete proxy; @@ -1646,7 +1648,7 @@ CServer::runClient(void* vsocket) try { CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); } - catch (XSocket&) { + catch (XIO&) { // ignore. client probably aborted the connection. } } @@ -1766,7 +1768,7 @@ CServer::handshakeClient(IDataSocket* socket) CProtocolUtil::writef(output, kMsgEIncompatible, kProtocolMajorVersion, kProtocolMinorVersion); } - catch (XSocket&) { + catch (XIO&) { // ignore } } @@ -1776,7 +1778,7 @@ CServer::handshakeClient(IDataSocket* socket) try { CProtocolUtil::writef(output, kMsgEBad); } - catch (XSocket&) { + catch (XIO&) { // ignore. client probably aborted the connection. } } diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 89a52bd6..92476365 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -33,7 +33,7 @@ class CHTTPServer; class CPrimaryClient; class IClient; class IDataSocket; -class IPrimaryScreenFactory; +class IScreenFactory; class IServerProtocol; class ISocketFactory; class IStreamFilterFactory; @@ -102,13 +102,12 @@ public: */ bool setConfig(const CConfig&); - //! Set primary screen factory + //! Set screen factory /*! - Sets the factory for creating primary screens. This must be - set before calling open(). This object takes ownership of the - factory. + Sets the factory for creating screens. This must be set before + calling open(). This object takes ownership of the factory. */ - void setScreenFactory(IPrimaryScreenFactory*); + void setScreenFactory(IScreenFactory*); //! Set socket factory /*! @@ -341,8 +340,8 @@ private: double m_bindTimeout; // factories - IPrimaryScreenFactory* m_screenFactory; - ISocketFactory* m_socketFactory; + IScreenFactory* m_screenFactory; + ISocketFactory* m_socketFactory; IStreamFilterFactory* m_streamFilterFactory; // running threads diff --git a/lib/synergy/CPrimaryScreen.cpp b/lib/synergy/CPrimaryScreen.cpp deleted file mode 100644 index 320733f0..00000000 --- a/lib/synergy/CPrimaryScreen.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CPrimaryScreen.h" -#include "IScreen.h" -#include "IScreenReceiver.h" -#include "ProtocolTypes.h" -#include "CLock.h" -#include "CThread.h" -#include "CLog.h" - -// -// CPrimaryScreen -// - -CPrimaryScreen::CPrimaryScreen(IScreenReceiver* receiver) : - m_receiver(receiver), - m_active(false) -{ - // do nothing -} - -CPrimaryScreen::~CPrimaryScreen() -{ - // do nothing -} - -void -CPrimaryScreen::mainLoop() -{ - // change our priority - CThread::getCurrentThread().setPriority(-14); - - // run event loop - try { - LOG((CLOG_DEBUG "entering event loop")); - onPreMainLoop(); - getScreen()->mainLoop(); - onPostMainLoop(); - LOG((CLOG_DEBUG "exiting event loop")); - } - catch (...) { - onPostMainLoop(); - LOG((CLOG_DEBUG "exiting event loop")); - throw; - } -} - -void -CPrimaryScreen::exitMainLoop() -{ - getScreen()->exitMainLoop(); -} - -void -CPrimaryScreen::open() -{ - CClientInfo info; - try { - // subclass hook - onPreOpen(); - - // open the screen - getScreen()->open(); - - // create and prepare our window - createWindow(); - - // collect screen info - getScreen()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); - getScreen()->getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getJumpZoneSize(); - - // update keyboard state - updateKeys(); - - // get notified of screen saver activation/deactivation - getScreen()->openScreensaver(true); - - // subclass hook - onPostOpen(); - - // reset options - resetOptions(); - } - catch (...) { - close(); - throw; - } - - // enter the screen - { - CLock lock(&m_mutex); - enterNoWarp(); - } - - // send screen info - m_receiver->onInfoChanged(info); -} - -void -CPrimaryScreen::close() -{ - onPreClose(); - getScreen()->closeScreensaver(); - destroyWindow(); - getScreen()->close(); - onPostClose(); -} - -void -CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver) -{ - LOG((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreensaver ? " for screen saver" : "")); - CLock lock(&m_mutex); - assert(m_active == true); - - if (!forScreensaver) { - warpCursor(x, y); - } - else { - onEnterScreensaver(); - } - enterNoWarp(); -} - -void -CPrimaryScreen::enterNoWarp() -{ - // note -- must be locked on entry - - // not active anymore - m_active = false; - - // subclass hook - onPreEnter(); - - // restore active window and hide our window - hideWindow(); - - // subclass hook - onPostEnter(); -} - -bool -CPrimaryScreen::leave() -{ - LOG((CLOG_INFO "leaving primary")); - CLock lock(&m_mutex); - assert(m_active == false); - - // subclass hook - onPreLeave(); - - // show our window - if (!showWindow()) { - onPostLeave(false); - return false; - } - - // get keyboard state as we leave - updateKeys(); - - // warp mouse to center - warpCursorToCenter(); - - // subclass hook - onPostLeave(true); - - // local client now active - m_active = true; - - // make sure our idea of clipboard ownership is correct - getScreen()->checkClipboards(); - - return true; -} - -void -CPrimaryScreen::setClipboard(ClipboardID id, - const IClipboard* clipboard) -{ - getScreen()->setClipboard(id, clipboard); -} - -void -CPrimaryScreen::grabClipboard(ClipboardID id) -{ - getScreen()->setClipboard(id, NULL); -} - -bool -CPrimaryScreen::isActive() const -{ - CLock lock(&m_mutex); - return m_active; -} - -void -CPrimaryScreen::getClipboard(ClipboardID id, - IClipboard* clipboard) const -{ - getScreen()->getClipboard(id, clipboard); -} - -void -CPrimaryScreen::onPreMainLoop() -{ - // do nothing -} - -void -CPrimaryScreen::onPostMainLoop() -{ - // do nothing -} - -void -CPrimaryScreen::onPreOpen() -{ - // do nothing -} - -void -CPrimaryScreen::onPostOpen() -{ - // do nothing -} - -void -CPrimaryScreen::onPreClose() -{ - // do nothing -} - -void -CPrimaryScreen::onPostClose() -{ - // do nothing -} - -void -CPrimaryScreen::onPreEnter() -{ - // do nothing -} - -void -CPrimaryScreen::onPostEnter() -{ - // do nothing -} - -void -CPrimaryScreen::onEnterScreensaver() -{ - // do nothing -} - -void -CPrimaryScreen::onPreLeave() -{ - // do nothing -} - -void -CPrimaryScreen::onPostLeave(bool) -{ - // do nothing -} diff --git a/lib/synergy/CPrimaryScreen.h b/lib/synergy/CPrimaryScreen.h deleted file mode 100644 index c370d19b..00000000 --- a/lib/synergy/CPrimaryScreen.h +++ /dev/null @@ -1,349 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CPRIMARYSCREEN_H -#define CPRIMARYSCREEN_H - -#include "ClipboardTypes.h" -#include "KeyTypes.h" -#include "OptionTypes.h" -#include "CMutex.h" - -class IClipboard; -class IScreen; -class IScreenReceiver; - -//! Generic server-side screen -/*! -This is a platform independent base class for primary screen -implementations. A primary screen is a server-side screen. -Each platform will derive a class from CPrimaryScreen to handle -platform dependent operations. -*/ -class CPrimaryScreen { -public: - CPrimaryScreen(IScreenReceiver*); - virtual ~CPrimaryScreen(); - - //! @name manipulators - //@{ - - //! Open screen - /*! - Opens the screen. This includes initializing the screen, opening - the screen saver, synchronizing keyboard state, and causing events - to be reported to an IPrimaryScreenReceiver (set through another - interface). Calls close() before returning (rethrowing) if it - fails for any reason. - */ - void open(); - - //! Run event loop - /*! - Run the screen's event loop. This returns when it detects - the application should terminate or when exitMainLoop() is called. - mainLoop() may only be called between open() and close(). - */ - void mainLoop(); - - //! Exit event loop - /*! - Force mainLoop() to return. This call can return before - mainLoop() does (i.e. asynchronously). - */ - void exitMainLoop(); - - //! Close screen - /*! - Closes the screen. This close the screen saver and the screen. - */ - void close(); - - //! Enter screen - /*! - Called when the user navigates to the primary screen. Warps - the cursor to the absolute coordinates \c x,y and unhides - it. If \c forScreensaver is true then we're entering because - the screen saver started and the cursor is not warped. - */ - void enter(SInt32 x, SInt32 y, bool forScreensaver); - - //! Leave screen - /*! - Called when the user navigates off the primary screen. Returns - true iff successful. - */ - bool leave(); - - //! Update configuration - /*! - This is called when the configuration has changed. \c activeSides - is a bitmask of EDirectionMask indicating which sides of the - primary screen are linked to clients. Override to handle the - possible change in jump zones. - */ - virtual void reconfigure(UInt32 activeSides) = 0; - - //! Warp cursor - /*! - Warp the cursor to the absolute coordinates \c x,y. Also - discard input events up to and including the warp before - returning. - */ - virtual void warpCursor(SInt32 x, SInt32 y) = 0; - - //! Set clipboard - /*! - Sets the system's clipboard contents. This is usually called - soon after an enter(). - */ - void setClipboard(ClipboardID, const IClipboard*); - - //! Grab clipboard - /*! - Grabs (i.e. take ownership of) the system clipboard. - */ - void grabClipboard(ClipboardID); - - //! Notify of options changes - /*! - Reset all options to their default values. - */ - virtual void resetOptions() = 0; - - //! Notify of options changes - /*! - Set options to given values. Ignore unknown options and don't - modify our options that aren't given in \c options. - */ - virtual void setOptions(const COptionsList& options) = 0; - - //! Install a one-shot timer - /*! - Installs a one-shot timer for \c timeout seconds and returns the - id of the timer. - */ - virtual UInt32 addOneShotTimer(double timeout) = 0; - - //@} - //! @name accessors - //@{ - - //! Test if active - /*! - Returns true iff the screen is active (i.e. the user has left - the screen). Note this is the reverse of a secdonary screen. - */ - bool isActive() const; - - //! Get clipboard - /*! - Saves the contents of the system clipboard indicated by \c id. - */ - void getClipboard(ClipboardID, IClipboard*) const; - - //! Get jump zone size - /*! - Return the jump zone size, the size of the regions on the edges of - the screen that cause the cursor to jump to another screen. - */ - virtual SInt32 getJumpZoneSize() const = 0; - - //! Get toggle key state - /*! - Return the primary screen's current toggle modifier key state. - The returned mask should have the corresponding bit set for - each toggle key that is active. For example, if caps lock is - on then the returned mask should have \c KeyModifierCapsLock set. - */ - virtual KeyModifierMask getToggleMask() const = 0; - - //! Get screen lock state - /*! - Return true if any key or button is being pressed or if there's - any other reason that the user should not be allowed to switch - screens. Active toggle keys (including the scroll lock key) - should not be counted as reasons to lock to the screen. - If this method returns true it should log a message on why at - the CLOG_DEBUG level. - */ - virtual bool isLockedToScreen() const = 0; - - //! Get screen - /*! - Return the platform dependent screen. - */ - virtual IScreen* getScreen() const = 0; - - //@} - -protected: - //! Pre-mainLoop() hook - /*! - Called on entry to mainLoop(). Override to perform platform specific - operations. Default does nothing. May throw. - */ - virtual void onPreMainLoop(); - - //! Post-mainLoop() hook - /*! - Called on exit from mainLoop(). Override to perform platform specific - operations. Default does nothing. May \b not throw. - */ - virtual void onPostMainLoop(); - - //! Pre-open() hook - /*! - Called on entry to open(). Override to perform platform specific - operations. Default does nothing. May throw. - */ - virtual void onPreOpen(); - - //! Post-open() hook - /*! - Called on exit from open() iff the open was successful. Default - does nothing. May throw. - */ - virtual void onPostOpen(); - - //! Pre-close() hook - /*! - Called on entry to close(). Override to perform platform specific - operations. Default does nothing. May \b not throw. - */ - virtual void onPreClose(); - - //! Post-close() hook - /*! - Called on exit from close(). Override to perform platform specific - operations. Default does nothing. May \b not throw. - */ - virtual void onPostClose(); - - //! Pre-enter() hook - /*! - Called from enter() after the cursor has been warped or, if - \c forScreensaver is true, onEnterScreensaver() was called. Override - to perform platform specific operations. Default does nothing. May - \b not throw. - */ - virtual void onPreEnter(); - - //! Post-enter() hook - /*! - Called on exit from enter(). Override to perform platform specific - operations. Default does nothing. May \b not throw. - */ - virtual void onPostEnter(); - - //! Pre-enter() for screen saver hook - /*! - Called on entry to enter() if the \c forScreensaver passed to it was - true. Override to perform platform specific operations. Default - does nothing. May \b not throw. - */ - virtual void onEnterScreensaver(); - - //! Pre-leave() hook - /*! - Called on entry to leave() after desktop synchronization. Override - to perform platform specific operations. Default does nothing. May - \b not throw. - */ - virtual void onPreLeave(); - - //! Post-leave() hook - /*! - Called on exit from leave(). \c success is the value returned by - showWindow(). Override to perform platform specific operations. - Default does nothing. May \b not throw. - */ - virtual void onPostLeave(bool success); - - //! Create window - /*! - Called to create the window. This window is generally used to - receive events, hide the cursor, and to capture keyboard and mouse - input. - */ - virtual void createWindow() = 0; - - //! Destroy window - /*! - Called to destroy the window created by createWindow(). - */ - virtual void destroyWindow() = 0; - - //! Show window - /*! - Called when the user navigates off the primary screen. Hide the - cursor and grab exclusive access to the input devices. Every call - to showWindow() has a matching call to hideWindow() which preceeds - it. Return true iff successful (in particular, iff the input - devices were grabbed). - - After a successful showWindow(), user input events and - screensaver activation/deactivation should be reported to an - IPrimaryScreenReceiver (set through another interface) until - hideWindow() is called. Report mouse motion to its - onMouseMoveSecondary(). User input should not be delivered to - any application except this one. - */ - virtual bool showWindow() = 0; - - //! Hide window - /*! - Called when the user navigates back to the primary screen. Show - the cursor and ungrab the input devices. - - After hideWindow(), user input events should be delivered normally - to other applications. Mouse motion over (at least) the jump zones - must be reported to an IPrimaryScreenReceiver's onMouseMovePrimary(). - */ - virtual void hideWindow() = 0; - - //! Warp cursor for relative motion - /*! - Prepare the cursor to report relative motion. When the user has - navigated to another screen, synergy requires the cursor motion - deltas, not the absolute coordinates. Typically this is done by - warping the cursor to the center of the primary screen and then - every time it moves compute the motion and warp back to the - center (but without reporting that warp as motion). This is - only called after a successful showWindow(). - */ - virtual void warpCursorToCenter() = 0; - - //! Synchronize key state - /*! - Check the current keyboard state. Normally a screen will save - the keyboard state in this method and use this shadow state - when handling user input and in methods like isLockedToScreen(). - */ - virtual void updateKeys() = 0; - -private: - void enterNoWarp(); - -private: - CMutex m_mutex; - - // object to notify of changes - IScreenReceiver* m_receiver; - - // m_active is true if this screen has been left - bool m_active; -}; - -#endif diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp new file mode 100644 index 00000000..ba3eb39b --- /dev/null +++ b/lib/synergy/CScreen.cpp @@ -0,0 +1,1001 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CScreen.h" +#include "IPlatformScreen.h" +#include "IScreenReceiver.h" +#include "ISecondaryScreen.h" +#include "ProtocolTypes.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" + +// +// CScreen +// + +CScreen::CScreen(IPlatformScreen* platformScreen, IScreenReceiver* receiver) : + m_screen(platformScreen), + m_receiver(receiver), + m_isPrimary(platformScreen->isPrimary()), + m_enabled(false), + m_entered(m_isPrimary), + m_toggleKeys(0), + m_screenSaverSync(true) +{ + // do nothing +} + +CScreen::~CScreen() +{ + delete m_screen; +} + +void +CScreen::open() +{ + CLock lock(&m_mutex); + + // open screen + m_screen->open(this); + + // reset options + resetOptions(); + + LOG((CLOG_DEBUG "opened display")); +} + +void +CScreen::close() +{ + CLock lock(&m_mutex); + assert(!m_enabled); + assert(m_entered == m_isPrimary); + + // close screen + m_screen->close(); + + LOG((CLOG_DEBUG "closed display")); +} + +void +CScreen::enable() +{ + CLock lock(&m_mutex); + assert(!m_enabled); + + m_screen->enable(); + if (m_isPrimary) { + enablePrimary(); + } + else { + enableSecondary(); + } + + // note activation + m_enabled = true; +} + +void +CScreen::disable() +{ + CLock lock(&m_mutex); + assert(m_enabled); + + m_screen->disable(); + if (m_isPrimary) { + disablePrimary(); + } + else { + disableSecondary(); + } + + // note deactivation + m_enabled = false; +} + +void +CScreen::mainLoop() +{ + // change our priority + CThread::getCurrentThread().setPriority(-14); + + // run event loop + try { + LOG((CLOG_DEBUG "entering event loop")); + m_screen->mainLoop(); + LOG((CLOG_DEBUG "exiting event loop")); + } + catch (...) { + LOG((CLOG_DEBUG "exiting event loop")); + throw; + } +} + +void +CScreen::exitMainLoop() +{ + m_screen->exitMainLoop(); +} + +void +CScreen::enter() +{ + CLock lock(&m_mutex); + assert(m_entered == false); + LOG((CLOG_INFO "entering screen")); + + // now on screen + m_entered = true; + + if (m_isPrimary) { + enterPrimary(); + } + else { + enterSecondary(); + } + m_screen->enter(); +} + +bool +CScreen::leave() +{ + CLock lock(&m_mutex); + assert(m_entered == true); + LOG((CLOG_INFO "leaving screen")); + + if (!m_screen->leave()) { + return false; + } + if (m_isPrimary) { + leavePrimary(); + } + else { + leaveSecondary(); + } + + // make sure our idea of clipboard ownership is correct + m_screen->checkClipboards(); + + // now not on screen + m_entered = false; + + return true; +} + +void +CScreen::reconfigure(UInt32 activeSides) +{ + assert(m_isPrimary); + m_screen->reconfigure(activeSides); +} + +void +CScreen::warpCursor(SInt32 x, SInt32 y) +{ + assert(m_isPrimary); + m_screen->warpCursor(x, y); +} + +void +CScreen::setClipboard(ClipboardID id, const IClipboard* clipboard) +{ + m_screen->setClipboard(id, clipboard); +} + +void +CScreen::grabClipboard(ClipboardID id) +{ + m_screen->setClipboard(id, NULL); +} + +void +CScreen::screensaver(bool activate) +{ + CLock lock(&m_mutex); + + if (!m_isPrimary) { + // activate/deactivation screen saver iff synchronization enabled + if (m_screenSaverSync) { + m_screen->screensaver(activate); + } + } +} + +void +CScreen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button) +{ + CLock lock(&m_mutex); + assert(!m_isPrimary); + + // check for ctrl+alt+del emulation + if (id == kKeyDelete && + (mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + LOG((CLOG_DEBUG "emulating ctrl+alt+del press")); + if (m_screen->fakeCtrlAltDel()) { + return; + } + } + + // get the sequence of keys to simulate key press and the final + // modifier state. + Keystrokes keys; + KeyButton key = m_screen->mapKey(keys, *this, id, mask, false); + if (key == 0) { + LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); + return; + } + if (keys.empty()) { + // do nothing if there are no associated keys + return; + } + + // generate key events + doKeystrokes(keys, 1); + + // note that key is down + updateKeyState(button, key, true); +} + +void +CScreen::keyRepeat(KeyID id, + KeyModifierMask mask, SInt32 count, KeyButton button) +{ + CLock lock(&m_mutex); + assert(!m_isPrimary); + + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + + // get the sequence of keys to simulate key repeat and the final + // modifier state. + Keystrokes keys; + KeyButton key = m_screen->mapKey(keys, *this, id, mask, true); + if (key == 0) { + LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); + return; + } + if (keys.empty()) { + // do nothing if there are no associated keys + return; + } + + // if the keycode for the auto-repeat is not the same as for the + // initial press then mark the initial key as released and the new + // key as pressed. this can happen when we auto-repeat after a + // dead key. for example, a dead accent followed by 'a' will + // generate an 'a with accent' followed by a repeating 'a'. the + // keycodes for the two keysyms might be different. + key &= 0xffu; + if (key != index->second) { + // replace key up with previous key id but leave key down + // alone so it uses the new keycode and store that keycode + // in the server key map. + for (Keystrokes::iterator index2 = keys.begin(); + index2 != keys.end(); ++index2) { + if ((index2->m_key & 0xffu) == key) { + index2->m_key = index->second; + break; + } + } + + // note that old key is now up + m_keys[index->second] &= ~kDown; + m_fakeKeys[index->second] &= ~kDown; + + // map server key to new key + index->second = key; + + // note that new key is now down + m_keys[index->second] |= kDown; + m_fakeKeys[index->second] |= kDown; + } + + // generate key events + doKeystrokes(keys, count); +} + +void +CScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) +{ + CLock lock(&m_mutex); + assert(!m_isPrimary); + + // if we haven't seen this button go down then ignore it + ServerKeyMap::iterator index = m_serverKeyMap.find(button); + if (index == m_serverKeyMap.end()) { + return; + } + KeyButton key = index->second; + + // get the sequence of keys to simulate key release + Keystrokes keys; + Keystroke keystroke; + keystroke.m_key = key; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + + // generate key events + doKeystrokes(keys, 1); + + // note that key is now up + updateKeyState(button, key, false); +} + +void +CScreen::mouseDown(ButtonID button) +{ + assert(!m_isPrimary); + m_screen->fakeMouseButton(button, true); +} + +void +CScreen::mouseUp(ButtonID button) +{ + assert(!m_isPrimary); + m_screen->fakeMouseButton(button, false); +} + +void +CScreen::mouseMove(SInt32 x, SInt32 y) +{ + assert(!m_isPrimary); + m_screen->fakeMouseMove(x, y); +} + +void +CScreen::mouseWheel(SInt32 delta) +{ + assert(!m_isPrimary); + m_screen->fakeMouseWheel(delta); +} + +void +CScreen::resetOptions() +{ + CLock lock(&m_mutex); + + // reset options + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; + + // if screen saver synchronization was off then turn it on since + // that's the default option state. + if (!m_screenSaverSync) { + m_screenSaverSync = true; + if (!m_isPrimary) { + m_screen->openScreensaver(false); + } + } + + // let screen handle its own options + m_screen->resetOptions(); +} + +void +CScreen::setOptions(const COptionsList& options) +{ + CLock lock(&m_mutex); + + // update options + bool oldScreenSaverSync = m_screenSaverSync; + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionScreenSaverSync) { + m_screenSaverSync = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "screen saver synchronization %s", m_screenSaverSync ? "on" : "off")); + } + else if (options[i] == kOptionHalfDuplexCapsLock) { + m_capsLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); + } + else if (options[i] == kOptionHalfDuplexNumLock) { + m_numLockHalfDuplex = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); + } + } + + // update screen saver synchronization + if (!m_isPrimary && oldScreenSaverSync != m_screenSaverSync) { + if (m_screenSaverSync) { + m_screen->openScreensaver(false); + } + else { + m_screen->closeScreensaver(); + } + } + + // let screen handle its own options + m_screen->setOptions(options); +} + +UInt32 +CScreen::addOneShotTimer(double timeout) +{ + return m_screen->addOneShotTimer(timeout); +} + +bool +CScreen::isOnScreen() const +{ + CLock lock(&m_mutex); + return m_entered; +} + +void +CScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const +{ + m_screen->getClipboard(id, clipboard); +} + +SInt32 +CScreen::getJumpZoneSize() const +{ + if (!m_isPrimary) { + return 0; + } + else { + return m_screen->getJumpZoneSize(); + } +} + +bool +CScreen::isLockedToScreen() const +{ + // check for pressed mouse buttons + if (m_screen->isAnyMouseButtonDown()) { + LOG((CLOG_DEBUG "locked by mouse button")); + return true; + } + + // we don't keep primary key state up to date so get the + // current state. + const_cast(this)->updateKeys(); + + // check for scroll lock toggled on + if (isModifierActive(KeyModifierScrollLock)) { + LOG((CLOG_DEBUG "locked by scroll lock")); + return true; + } + + // check for any pressed key + KeyButton key = isAnyKeyDown(); + if (key != 0) { + LOG((CLOG_DEBUG "locked by %s", m_screen->getKeyName(key))); + return true; + } + + // not locked + return false; +} + +void +CScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + m_screen->getShape(x, y, w, h); +} + +void +CScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + m_screen->getCursorPos(x, y); +} + +void +CScreen::updateKeys() +{ + CLock lock(&m_mutex); + + // clear key state + memset(m_keys, 0, sizeof(m_keys)); + memset(m_fakeKeys, 0, sizeof(m_fakeKeys)); + m_maskToKeys.clear(); + m_keyToMask.clear(); + + // let subclass set m_keys + m_screen->updateKeys(); + + // figure out active modifier mask + m_mask = getModifierMask(); + LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); +} + +void +CScreen::releaseKeys() +{ + CLock lock(&m_mutex); + + // release keys that we've synthesized a press for and only those + // keys. we don't want to synthesize a release on a key the user + // is still physically pressing. + for (KeyButton i = 1; i < 256; ++i) { + if ((m_fakeKeys[i] & kDown) != 0) { + fakeKeyEvent(i, false, false); + m_keys[i] &= ~kDown; + m_fakeKeys[i] &= ~kDown; + } + } +} + +void +CScreen::setKeyDown(KeyButton key) +{ + CLock lock(&m_mutex); + + m_keys[key & 0xffu] |= kDown; +} + +void +CScreen::setToggled(KeyModifierMask mask) +{ + CLock lock(&m_mutex); + + if (!isToggle(mask)) { + return; + } + MaskToKeys::const_iterator i = m_maskToKeys.find(mask); + if (i == m_maskToKeys.end()) { + return; + } + for (KeyButtons::const_iterator j = i->second.begin(); + j != i->second.end(); ++j) { + m_keys[(*j) & 0xffu] |= kToggled; + } +} + +void +CScreen::addModifier(KeyModifierMask mask, KeyButtons& keys) +{ + CLock lock(&m_mutex); + + // the modifier must have associated keys + if (keys.empty()) { + return; + } + + // the mask must not be zero + assert(mask != 0); + + // the mask must have exactly one high bit + assert((mask & (mask - 1)) == 0); + + // index mask by keycodes + for (KeyButtons::iterator j = keys.begin(); j != keys.end(); ++j) { + // key must be valid + assert(((*j) & 0xffu) != 0); + m_keyToMask[static_cast((*j) & 0xffu)] = mask; + } + + // index keys by mask + m_maskToKeys[mask].swap(keys); +} + +void +CScreen::setToggleState(KeyModifierMask mask) +{ + // toggle modifiers that don't match the desired state + KeyModifierMask different = (m_mask ^ mask); + if ((different & KeyModifierCapsLock) != 0) { + toggleKey(KeyModifierCapsLock); + } + if ((different & KeyModifierNumLock) != 0) { + toggleKey(KeyModifierNumLock); + } + if ((different & KeyModifierScrollLock) != 0) { + toggleKey(KeyModifierScrollLock); + } +} + +KeyButton +CScreen::isAnyKeyDown() const +{ + CLock lock(&m_mutex); + + for (UInt32 i = 1; i < 256; ++i) { + if ((m_keys[i] & kDown) != 0) { + return static_cast(i); + } + } + return 0; +} + +bool +CScreen::isKeyDown(KeyButton key) const +{ + CLock lock(&m_mutex); + + key &= 0xffu; + return (key != 0 && ((m_keys[key] & kDown) != 0)); +} + +bool +CScreen::isToggle(KeyModifierMask mask) const +{ + static const KeyModifierMask s_toggleMask = + KeyModifierCapsLock | KeyModifierNumLock | KeyModifierScrollLock; + return ((mask & s_toggleMask) != 0); +} + +bool +CScreen::isHalfDuplex(KeyModifierMask mask) const +{ + CLock lock(&m_mutex); + + return ((mask == KeyModifierCapsLock && m_capsLockHalfDuplex) || + (mask == KeyModifierNumLock && m_numLockHalfDuplex)); +} + +bool +CScreen::isModifierActive(KeyModifierMask mask) const +{ + CLock lock(&m_mutex); + + MaskToKeys::const_iterator i = m_maskToKeys.find(mask); + if (i == m_maskToKeys.end()) { + return false; + } + + KeyButtons::const_iterator j = i->second.begin(); + if (isToggle(mask)) { + // modifier is a toggle + if (isKeyToggled(*j)) { + return true; + } + } + else { + // modifier is not a toggle + for (; j != i->second.end(); ++j) { + if (isKeyDown(*j)) { + return true; + } + } + } + return false; +} + +KeyModifierMask +CScreen::getActiveModifiers() const +{ + CLock lock(&m_mutex); + if (m_isPrimary) { + // we don't keep primary key state up to date so get the + // current state. + const_cast(this)->updateKeys(); + } + return m_mask; +} + +bool +CScreen::mapModifier(Keystrokes& keys, Keystrokes& undo, + KeyModifierMask mask, bool desireActive) const +{ + CLock lock(&m_mutex); + + // look up modifier + MaskToKeys::const_iterator i = m_maskToKeys.find(mask); + if (i == m_maskToKeys.end()) { + return false; + } + + // ignore if already in desired state + if (isModifierActive(mask) == desireActive) { + return true; + } + + // initialize keystroke + Keystroke keystroke; + keystroke.m_repeat = false; + + // handle toggles + if (isToggle(mask)) { + keystroke.m_key = i->second.front(); + keystroke.m_press = true; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + keystroke.m_press = false; + undo.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + + else if (desireActive) { + // press + keystroke.m_key = i->second.front(); + keystroke.m_press = true; + keys.push_back(keystroke); + keystroke.m_press = false; + undo.push_back(keystroke); + } + + else { + // releasing a modifier is quite different from pressing one. + // when we release a modifier we have to release every keycode that + // is assigned to the modifier since the modifier is active if any + // one of them is down. when we press a modifier we just have to + // press one of those keycodes. + for (KeyButtons::const_iterator j = i->second.begin(); + j != i->second.end(); ++j) { + if (isKeyDown(*j)) { + keystroke.m_key = *j; + keystroke.m_press = false; + keys.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + } + } + + return true; +} + +KeyModifierMask +CScreen::getMaskForKey(KeyButton key) const +{ + CLock lock(&m_mutex); + KeyToMask::const_iterator i = m_keyToMask.find(key); + if (i == m_keyToMask.end()) { + return 0; + } + else { + return i->second; + } +} + +void +CScreen::enablePrimary() +{ + // get notified of screen saver activation/deactivation + m_screen->openScreensaver(true); + + // collect and send screen info + CClientInfo info; + m_screen->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + m_screen->getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getJumpZoneSize(); + m_receiver->onInfoChanged(info); +} + +void +CScreen::enableSecondary() +{ + // assume primary has all clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + grabClipboard(id); + } + + // disable the screen saver if synchronization is enabled + if (m_screenSaverSync) { + m_screen->openScreensaver(false); + } +} + +void +CScreen::disablePrimary() +{ + // done with screen saver + m_screen->closeScreensaver(); +} + +void +CScreen::disableSecondary() +{ + // done with screen saver + m_screen->closeScreensaver(); +} + +void +CScreen::enterPrimary() +{ + // do nothing +} + +void +CScreen::enterSecondary() +{ + // update our keyboard state to reflect the local state + updateKeys(); + + // remember toggle key state. we'll restore this when we leave. + m_toggleKeys = m_mask; +} + +void +CScreen::leavePrimary() +{ + // do nothing +} + +void +CScreen::leaveSecondary() +{ + // release any keys we think are still down + releaseKeys(); + + // restore toggle key state + setToggleState(m_toggleKeys); +} + +KeyModifierMask +CScreen::getModifierMask() const +{ + KeyModifierMask mask = 0; + if (isModifierActive(KeyModifierShift)) { + mask |= KeyModifierShift; + } + if (isModifierActive(KeyModifierControl)) { + mask |= KeyModifierControl; + } + if (isModifierActive(KeyModifierAlt)) { + mask |= KeyModifierAlt; + } + if (isModifierActive(KeyModifierMeta)) { + mask |= KeyModifierMeta; + } + if (isModifierActive(KeyModifierSuper)) { + mask |= KeyModifierSuper; + } + if (isModifierActive(KeyModifierModeSwitch)) { + mask |= KeyModifierModeSwitch; + } + if (isModifierActive(KeyModifierNumLock)) { + mask |= KeyModifierNumLock; + } + if (isModifierActive(KeyModifierCapsLock)) { + mask |= KeyModifierCapsLock; + } + if (isModifierActive(KeyModifierScrollLock)) { + mask |= KeyModifierScrollLock; + } + return mask; +} + +void +CScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) +{ + // do nothing if no keys or no repeats + if (count < 1 || keys.empty()) { + return; + } + + // generate key events + LOG((CLOG_DEBUG2 "keystrokes:")); + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { + if (k->m_repeat) { + // repeat from here up to but not including the next key + // with m_repeat == false count times. + Keystrokes::const_iterator start = k; + for (; count > 0; --count) { + // send repeating events + for (k = start; k != keys.end() && k->m_repeat; ++k) { + fakeKeyEvent(k->m_key, k->m_press, true); + } + } + + // note -- k is now on the first non-repeat key after the + // repeat keys, exactly where we'd like to continue from. + } + else { + // send event + fakeKeyEvent(k->m_key, k->m_press, false); + + // next key + ++k; + } + } +} + +void +CScreen::fakeKeyEvent(KeyButton key, bool press, bool repeat) const +{ + // half-duplex keys are special. we ignore releases and convert + // a press when the toggle is active to a release. + KeyModifierMask mask = getMaskForKey(key); + if (isHalfDuplex(mask)) { + if (repeat || !press) { + return; + } + if (isModifierActive(mask)) { + press = false; + } + } + + // send key event + LOG((CLOG_DEBUG2 " %d %s%s", key, press ? "down" : "up", repeat ? " repeat" : "")); + m_screen->fakeKeyEvent(key, press); +} + +void +CScreen::updateKeyState(KeyButton button, KeyButton key, bool press) +{ + // ignore bogus keys + key &= 0xffu; + if (button == 0 || key == 0) { + return; + } + + // update shadow state. shadow state doesn't change on auto-repeat. + if (press) { + // key is now down + m_serverKeyMap[button] = key; + m_keys[key] |= kDown; + m_fakeKeys[key] |= kDown; + } + else { + // key is now up + m_serverKeyMap.erase(button); + m_keys[key] &= ~kDown; + m_fakeKeys[key] &= ~kDown; + } + KeyModifierMask mask = getMaskForKey(key); + if (mask != 0) { + // key is a modifier + if (isToggle(mask)) { + // key is a toggle modifier + if (press) { + m_keys[key] ^= kToggled; + m_mask ^= mask; + + // if key is half duplex then don't report it as down + if (isHalfDuplex(mask)) { + m_keys[key] &= ~kDown; + m_fakeKeys[key] &= ~kDown; + } + } + } + else { + // key is a normal modifier + if (press) { + m_mask |= mask; + } + else if (!isModifierActive(mask)) { + // no key for modifier is down anymore + m_mask &= ~mask; + } + } + LOG((CLOG_DEBUG2 "new mask: 0x%04x", m_mask)); + } +} + +void +CScreen::toggleKey(KeyModifierMask mask) +{ + // get the system key ID for this toggle key ID + MaskToKeys::const_iterator i = m_maskToKeys.find(mask); + if (i == m_maskToKeys.end()) { + return; + } + KeyButton key = i->second.front(); + + // toggle the key + fakeKeyEvent(key, true, false); + fakeKeyEvent(key, false, false); + + // toggle shadow state + m_mask ^= mask; + key &= 0xffu; + m_keys[key] ^= kToggled; +} + +bool +CScreen::isKeyToggled(KeyButton key) const +{ + key &= 0xffu; + return (key != 0 && ((m_keys[key] & kToggled) != 0)); +} diff --git a/lib/synergy/CScreen.h b/lib/synergy/CScreen.h new file mode 100644 index 00000000..7a0f3d29 --- /dev/null +++ b/lib/synergy/CScreen.h @@ -0,0 +1,357 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSECONDARYSCREEN_H +#define CSECONDARYSCREEN_H + +#include "IKeyState.h" +#include "ClipboardTypes.h" +#include "MouseTypes.h" +#include "OptionTypes.h" +#include "CMutex.h" +#include "stdmap.h" + +class IClipboard; +class IPlatformScreen; +class IScreenReceiver; + +//! Platform independent screen +/*! +This is a platform independent screen. It can work as either a +primary or secondary screen. +*/ +class CScreen : public IKeyState { +public: + CScreen(IPlatformScreen* platformScreen, IScreenReceiver*); + virtual ~CScreen(); + + //! @name manipulators + //@{ + + //! Open screen + /*! + Opens the screen. + */ + void open(); + + //! Close screen + /*! + Closes the screen. + */ + void close(); + + //! Activate screen + /*! + Activate the screen, preparing it to report system and user events. + For a secondary screen it also means disabling the screen saver if + synchronizing it and preparing to synthesize events. + */ + void enable(); + + //! Deactivate screen + /*! + Undoes the operations in activate() and events are no longer + reported. It also releases keys that are logically pressed. + */ + void disable(); + + //! Run event loop + /*! + Run the screen's event loop. This returns when it detects + the application should terminate or when exitMainLoop() is called. + mainLoop() may only be called between open() and close(). + */ + void mainLoop(); + + //! Exit event loop + /*! + Force mainLoop() to return. This call can return before + mainLoop() does (i.e. asynchronously). + */ + void exitMainLoop(); + + //! Enter screen + /*! + Called when the user navigates to this screen. + */ + void enter(); + + //! Leave screen + /*! + Called when the user navigates off this screen. + */ + bool leave(); + + //! Update configuration + /*! + This is called when the configuration has changed. \c activeSides + is a bitmask of EDirectionMask indicating which sides of the + primary screen are linked to clients. + */ + void reconfigure(UInt32 activeSides); + + //! Warp cursor + /*! + Warps the cursor to the absolute coordinates \c x,y. Also + discards input events up to and including the warp before + returning. + */ + void warpCursor(SInt32 x, SInt32 y); + + //! Set clipboard + /*! + Sets the system's clipboard contents. This is usually called + soon after an enter(). + */ + void setClipboard(ClipboardID, const IClipboard*); + + //! Grab clipboard + /*! + Grabs (i.e. take ownership of) the system clipboard. + */ + void grabClipboard(ClipboardID); + + //! Activate/deactivate screen saver + /*! + Forcibly activates the screen saver if \c activate is true otherwise + forcibly deactivates it. + */ + void screensaver(bool activate); + + //! Notify of key press + /*! + Synthesize key events to generate a press of key \c id. If possible + match the given modifier mask. The KeyButton identifies the physical + key on the server that generated this key down. The client must + ensure that a key up or key repeat that uses the same KeyButton will + synthesize an up or repeat for the same client key synthesized by + keyDown(). + */ + void keyDown(KeyID id, KeyModifierMask, KeyButton); + + //! Notify of key repeat + /*! + Synthesize key events to generate a press and release of key \c id + \c count times. If possible match the given modifier mask. + */ + void keyRepeat(KeyID id, KeyModifierMask, + SInt32 count, KeyButton); + + //! Notify of key release + /*! + Synthesize key events to generate a release of key \c id. If possible + match the given modifier mask. + */ + void keyUp(KeyID id, KeyModifierMask, KeyButton); + + //! Notify of mouse press + /*! + Synthesize mouse events to generate a press of mouse button \c id. + */ + void mouseDown(ButtonID id); + + //! Notify of mouse release + /*! + Synthesize mouse events to generate a release of mouse button \c id. + */ + void mouseUp(ButtonID id); + + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion to the absolute + screen position \c xAbs,yAbs. + */ + void mouseMove(SInt32 xAbs, SInt32 yAbs); + + //! Notify of mouse wheel motion + /*! + Synthesize mouse events to generate mouse wheel motion of \c delta. + \c delta is positive for motion away from the user and negative for + motion towards the user. Each wheel click should generate a delta + of +/-120. + */ + void mouseWheel(SInt32 delta); + + //! Notify of options changes + /*! + Resets all options to their default values. + */ + void resetOptions(); + + //! Notify of options changes + /*! + Set options to given values. Ignores unknown options and doesn't + modify options that aren't given in \c options. + */ + void setOptions(const COptionsList& options); + + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer. + */ + UInt32 addOneShotTimer(double timeout); + + //@} + //! @name accessors + //@{ + + //! Test if cursor on screen + /*! + Returns true iff the cursor is on the screen. + */ + bool isOnScreen() const; + + //! Get clipboard + /*! + Saves the contents of the system clipboard indicated by \c id. + */ + void getClipboard(ClipboardID id, IClipboard*) const; + + //! Get jump zone size + /*! + Returns the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. + */ + SInt32 getJumpZoneSize() const; + + //! Get screen lock state + /*! + Returns true if there's any reason that the user should not be + allowed to leave the screen. Active toggle keys (excluding the + scroll lock key) are not be counted as reasons to lock to the + screen. If this method returns true it logs a message on why at + the CLOG_DEBUG level. + */ + bool isLockedToScreen() const; + + //! Get screen shape + /*! + Returns the position of the upper-left corner of the screen in \c x + and \c y and the size of the screen in \c width and \c height. + */ + void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + + //! Get cursor position + /*! + Returns the current position of the cursor in \c x,y. + */ + void getCursorPos(SInt32& x, SInt32& y) const; + + //@} + + // IKeyState overrides + virtual void updateKeys(); + virtual void releaseKeys(); + virtual void setKeyDown(KeyButton key); + virtual void setToggled(KeyModifierMask); + virtual void addModifier(KeyModifierMask, KeyButtons&); + virtual void setToggleState(KeyModifierMask); + virtual KeyButton isAnyKeyDown() const; + virtual bool isKeyDown(KeyButton) const; + virtual bool isToggle(KeyModifierMask) const; + virtual bool isHalfDuplex(KeyModifierMask) const; + virtual bool isModifierActive(KeyModifierMask) const; + virtual KeyModifierMask + getActiveModifiers() const; + virtual bool mapModifier(Keystrokes& keys, Keystrokes& undo, + KeyModifierMask mask, bool desireActive) const; + virtual KeyModifierMask + getMaskForKey(KeyButton) const; + +protected: + void enablePrimary(); + void enableSecondary(); + void disablePrimary(); + void disableSecondary(); + + void enterPrimary(); + void enterSecondary(); + void leavePrimary(); + void leaveSecondary(); + +private: + // Get the modifier mask for the current key state + KeyModifierMask getModifierMask() const; + + // Send fake keystrokes + void doKeystrokes(const Keystrokes&, SInt32 count); + + // Send a fake key event + void fakeKeyEvent(KeyButton, bool press, bool repeat) const; + + // Update the shadow state for a key + void updateKeyState(KeyButton button, + KeyButton key, bool press); + + // Toggle a modifier + void toggleKey(KeyModifierMask); + + // Test if a modifier is toggled + bool isKeyToggled(KeyButton) const; + +private: + typedef std::map ServerKeyMap; + typedef std::map MaskToKeys; + typedef std::map KeyToMask; + + CMutex m_mutex; + + // our platform dependent screen + IPlatformScreen* m_screen; + + // our screen receiver + IScreenReceiver* m_receiver; + + // true if screen is being used as a primary screen, false otherwise + bool m_isPrimary; + + // true if screen is enabled + bool m_enabled; + + // true if the cursor is on this screen + bool m_entered; + + // true if screen saver should be synchronized to server + bool m_screenSaverSync; + + // note toggle keys that toggles on up/down (false) or on + // transition (true) + bool m_numLockHalfDuplex; + bool m_capsLockHalfDuplex; + + // keyboard state + + // map server key buttons to local system keys + ServerKeyMap m_serverKeyMap; + + // system key states as set by us or the user + KeyState m_keys[256]; + + // system key states as set by us + KeyState m_fakeKeys[256]; + + // modifier info + MaskToKeys m_maskToKeys; + KeyToMask m_keyToMask; + + // current active modifiers + KeyModifierMask m_mask; + + // the toggle key state when this screen was last entered + KeyModifierMask m_toggleKeys; +}; + +#endif diff --git a/lib/synergy/CSecondaryScreen.cpp b/lib/synergy/CSecondaryScreen.cpp deleted file mode 100644 index 6dfaed19..00000000 --- a/lib/synergy/CSecondaryScreen.cpp +++ /dev/null @@ -1,781 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CSecondaryScreen.h" -#include "IScreen.h" -#include "CLock.h" -#include "CThread.h" -#include "CLog.h" - -// -// CSecondaryScreen -// - -CSecondaryScreen::CSecondaryScreen() : - m_remoteReady(false), - m_active(false), - m_toggleKeys(0), - m_screenSaverSync(true) -{ - // do nothing -} - -CSecondaryScreen::~CSecondaryScreen() -{ - // do nothing -} - -void -CSecondaryScreen::mainLoop() -{ - // change our priority - CThread::getCurrentThread().setPriority(-14); - - // run event loop - try { - LOG((CLOG_DEBUG "entering event loop")); - onPreMainLoop(); - getScreen()->mainLoop(); - onPostMainLoop(); - LOG((CLOG_DEBUG "exiting event loop")); - } - catch (...) { - onPostMainLoop(); - LOG((CLOG_DEBUG "exiting event loop")); - throw; - } -} - -void -CSecondaryScreen::exitMainLoop() -{ - getScreen()->exitMainLoop(); -} - -void -CSecondaryScreen::open() -{ - try { - // subclass hook - onPreOpen(); - - // open the screen - getScreen()->open(); - - // create and prepare our window. pretend we're active so - // we don't try to show our window until later. - { - CLock lock(&m_mutex); - assert(m_active == false); - m_active = true; - } - createWindow(); - { - CLock lock(&m_mutex); - m_active = false; - } - - // subclass hook - onPostOpen(); - - // reset options - resetOptions(); - } - catch (...) { - close(); - throw; - } -} - -void -CSecondaryScreen::close() -{ - onPreClose(); - destroyWindow(); - getScreen()->close(); - onPostClose(); -} - -void -CSecondaryScreen::remoteControl() -{ - // assume primary has all clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - grabClipboard(id); - } - - // update keyboard state - { - CLock lock(&m_mutex); - updateKeys(); - } - - // now remote ready. fake being active for call to leave(). - bool screenSaverSync; - { - CLock lock(&m_mutex); - m_remoteReady = true; - m_active = true; - - // copy screen saver synchronization state - screenSaverSync = m_screenSaverSync; - } - - // disable the screen saver if synchronization is enabled - if (screenSaverSync) { - getScreen()->openScreensaver(false); - } - - // hide the cursor - leave(); -} - -void -CSecondaryScreen::localControl() -{ - getScreen()->closeScreensaver(); - - // not remote ready anymore - CLock lock(&m_mutex); - m_remoteReady = false; -} - -void -CSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) -{ - CLock lock(&m_mutex); - assert(m_active == false); - - LOG((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); - - sync(); - - // now active - m_active = true; - - // subclass hook - onPreEnter(); - - // update our keyboard state to reflect the local state - updateKeys(); - - // toggle modifiers that don't match the desired state and - // remember previous toggle key state. - m_toggleKeys = m_mask; - setToggleState(mask); - - // warp to requested location - fakeMouseMove(x, y); - - // show mouse - hideWindow(); - - // subclass hook - onPostEnter(); -} - -void -CSecondaryScreen::leave() -{ - LOG((CLOG_INFO "leaving screen")); - CLock lock(&m_mutex); - assert(m_active == true); - - sync(); - - // subclass hook - onPreLeave(); - - // restore toggle key state - setToggleState(m_toggleKeys); - - // hide mouse - SInt32 x, y; - getScreen()->getCursorCenter(x, y); - showWindow(x, y); - - // subclass hook - onPostLeave(); - - // not active anymore - m_active = false; - - // make sure our idea of clipboard ownership is correct - getScreen()->checkClipboards(); -} - -void -CSecondaryScreen::setClipboard(ClipboardID id, - const IClipboard* clipboard) -{ - getScreen()->setClipboard(id, clipboard); -} - -void -CSecondaryScreen::grabClipboard(ClipboardID id) -{ - getScreen()->setClipboard(id, NULL); -} - -void -CSecondaryScreen::screensaver(bool activate) -{ - // get screen saver synchronization flag - bool screenSaverSync; - { - CLock lock(&m_mutex); - screenSaverSync = m_screenSaverSync; - } - - // activate/deactivation screen saver iff synchronization enabled - if (screenSaverSync) { - getScreen()->screensaver(activate); - } -} - -CSecondaryScreen::SysKeyID -CSecondaryScreen::getUnhanded(SysKeyID) const -{ - // no key represents both left and right sides of any key - return 0; -} - -CSecondaryScreen::SysKeyID -CSecondaryScreen::getOtherHanded(SysKeyID) const -{ - // no key represents both left and right sides of any key - return 0; -} - -bool -CSecondaryScreen::synthesizeCtrlAltDel(EKeyAction) -{ - // pass keys through unchanged - return false; -} - -void -CSecondaryScreen::sync() const -{ - // do nothing -} - -void -CSecondaryScreen::flush() -{ - // do nothing -} - -void -CSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) -{ - // do nothing if no keys or no repeats - if (count < 1 || keys.empty()) { - return; - } - - // generate key events - LOG((CLOG_DEBUG2 "keystrokes:")); - for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { - if (k->m_repeat) { - // repeat from here up to but not including the next key - // with m_repeat == false count times. - Keystrokes::const_iterator start = k; - for (; count > 0; --count) { - // send repeating events - for (k = start; k != keys.end() && k->m_repeat; ++k) { - LOG((CLOG_DEBUG2 " %d %s repeat", k->m_sysKeyID, k->m_press ? "down" : "up")); - fakeKeyEvent(k->m_sysKeyID, k->m_press); - } - } - - // note -- k is now on the first non-repeat key after the - // repeat keys, exactly where we'd like to continue from. - } - else { - // send event - LOG((CLOG_DEBUG2 " %d %s", k->m_sysKeyID, k->m_press ? "down" : "up")); - fakeKeyEvent(k->m_sysKeyID, k->m_press); - - // next key - ++k; - } - } - - flush(); -} - -void -CSecondaryScreen::keyDown(KeyID key, - KeyModifierMask mask, KeyButton button) -{ - CLock lock(&m_mutex); - sync(); - - // check for ctrl+alt+del emulation - if (key == kKeyDelete && - (mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - LOG((CLOG_DEBUG "emulating ctrl+alt+del press")); - if (synthesizeCtrlAltDel(kPress)) { - return; - } - } - - // get the sequence of keys to simulate key press and the final - // modifier state. - Keystrokes keys; - SysKeyID sysKeyID; - m_mask = mapKey(keys, sysKeyID, key, m_mask, mask, kPress); - if (keys.empty()) { - // do nothing if there are no associated keys (i.e. lookup failed) - return; - } - sysKeyID &= 0xffu; - - // generate key events - doKeystrokes(keys, 1); - - // do not record button down if button or system key is 0 (invalid) - if (button != 0 && sysKeyID != 0) { - // note that key is now down - SysKeyID unhandedSysKeyID = getUnhanded(sysKeyID); - m_serverKeyMap[button] = sysKeyID; - m_keys[sysKeyID] |= kDown; - m_fakeKeys[sysKeyID] |= kDown; - if (unhandedSysKeyID != 0) { - m_keys[unhandedSysKeyID] |= kDown; - m_fakeKeys[unhandedSysKeyID] |= kDown; - } - } -} - - -void -CSecondaryScreen::keyRepeat(KeyID key, - KeyModifierMask mask, SInt32 count, KeyButton button) -{ - CLock lock(&m_mutex); - sync(); - - // if we haven't seen this button go down then ignore it - ServerKeyMap::iterator index = m_serverKeyMap.find(button); - if (index == m_serverKeyMap.end()) { - return; - } - - // get the sequence of keys to simulate key repeat and the final - // modifier state. - Keystrokes keys; - SysKeyID sysKeyID; - m_mask = mapKey(keys, sysKeyID, key, m_mask, mask, kRepeat); - if (keys.empty()) { - return; - } - sysKeyID &= 0xffu; - - // if this key shouldn't auto-repeat then ignore - if (!isAutoRepeating(sysKeyID)) { - return; - } - - // if the keycode for the auto-repeat is not the same as for the - // initial press then mark the initial key as released and the new - // key as pressed. this can happen when we auto-repeat after a - // dead key. for example, a dead accent followed by 'a' will - // generate an 'a with accent' followed by a repeating 'a'. the - // keycodes for the two keysyms might be different. - if (sysKeyID != index->second) { - // replace key up with previous key id but leave key down - // alone so it uses the new keycode and store that keycode - // in the server key map. - for (Keystrokes::iterator index2 = keys.begin(); - index2 != keys.end(); ++index2) { - if ((index2->m_sysKeyID & 0xffu) == sysKeyID) { - index2->m_sysKeyID = index->second; - break; - } - } - - // note that old key is now up - m_keys[index->second] &= ~kDown; - m_fakeKeys[index->second] &= ~kDown; - - // map server key to new key - index->second = sysKeyID; - - // note that new key is now down - m_keys[index->second] |= kDown; - m_fakeKeys[index->second] |= kDown; - } - - // generate key events - doKeystrokes(keys, count); -} - -void -CSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) -{ - CLock lock(&m_mutex); - sync(); - - // if we haven't seen this button go down then ignore it - ServerKeyMap::iterator index = m_serverKeyMap.find(button); - if (index == m_serverKeyMap.end()) { - return; - } - SysKeyID sysKeyID = index->second; - - // check for ctrl+alt+del emulation - if (key == kKeyDelete && - (mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - LOG((CLOG_DEBUG "emulating ctrl+alt+del release")); - if (synthesizeCtrlAltDel(kRelease)) { - return; - } - } - - // get the sequence of keys to simulate key release - Keystrokes keys; - Keystroke keystroke; - keystroke.m_sysKeyID = sysKeyID; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - - // generate key events - doKeystrokes(keys, 1); - - // note that key is now up - SysKeyID unhandedSysKeyID = getUnhanded(sysKeyID); - m_serverKeyMap.erase(index); - m_keys[sysKeyID] &= ~kDown; - m_fakeKeys[sysKeyID] &= ~kDown; - if (unhandedSysKeyID != 0) { - SysKeyID otherHandedSysKeyID = getOtherHanded(sysKeyID); - if ((m_keys[otherHandedSysKeyID] & kDown) == 0) { - m_keys[unhandedSysKeyID] &= ~kDown; - m_fakeKeys[unhandedSysKeyID] &= ~kDown; - } - } - - // get the new modifier state - mask = getModifierKeyMask(sysKeyID); - if (mask != 0) { - // key is a modifier key - if ((mask & (KeyModifierCapsLock | - KeyModifierNumLock | - KeyModifierScrollLock)) != 0) { - // modifier is a toggle - m_mask ^= mask; - } - else if (!isModifierActive(sysKeyID)) { - // all keys for this modifier are released - m_mask &= ~mask; - } - } -} - -void -CSecondaryScreen::mouseDown(ButtonID button) -{ - CLock lock(&m_mutex); - sync(); - fakeMouseButton(button, true); - flush(); -} - -void -CSecondaryScreen::mouseUp(ButtonID button) -{ - CLock lock(&m_mutex); - sync(); - fakeMouseButton(button, false); - flush(); -} - -void -CSecondaryScreen::mouseMove(SInt32 x, SInt32 y) -{ - CLock lock(&m_mutex); - sync(); - fakeMouseMove(x, y); - flush(); -} - -void -CSecondaryScreen::mouseWheel(SInt32 delta) -{ - CLock lock(&m_mutex); - sync(); - fakeMouseWheel(delta); - flush(); -} - -void -CSecondaryScreen::setToggleState(KeyModifierMask mask) -{ - // toggle modifiers that don't match the desired state - KeyModifierMask different = (m_mask ^ mask); - if ((different & KeyModifierCapsLock) != 0) { - toggleKey(kKeyCapsLock, KeyModifierCapsLock); - } - if ((different & KeyModifierNumLock) != 0) { - toggleKey(kKeyNumLock, KeyModifierNumLock); - } - if ((different & KeyModifierScrollLock) != 0) { - toggleKey(kKeyScrollLock, KeyModifierScrollLock); - } -} - -void -CSecondaryScreen::resetOptions() -{ - // set screen saver synchronization flag and see if we need to - // update the screen saver synchronization. reset other options. - bool screenSaverSyncOn; - { - CLock lock(&m_mutex); - screenSaverSyncOn = (!m_screenSaverSync && m_remoteReady); - m_screenSaverSync = true; - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; - } - - // update screen saver synchronization - if (screenSaverSyncOn) { - getScreen()->openScreensaver(false); - } -} - -void -CSecondaryScreen::setOptions(const COptionsList& options) -{ - // update options - bool updateScreenSaverSync = false; - bool oldScreenSaverSync; - { - CLock lock(&m_mutex); - oldScreenSaverSync = m_screenSaverSync; - for (UInt32 i = 0, n = options.size(); i < n; i += 2) { - if (options[i] == kOptionScreenSaverSync) { - updateScreenSaverSync = true; - m_screenSaverSync = (options[i + 1] != 0); - LOG((CLOG_DEBUG1 "screen saver synchronization %s", m_screenSaverSync ? "on" : "off")); - } - else if (options[i] == kOptionHalfDuplexCapsLock) { - m_capsLockHalfDuplex = (options[i + 1] != 0); - LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); - } - else if (options[i] == kOptionHalfDuplexNumLock) { - m_numLockHalfDuplex = (options[i + 1] != 0); - LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); - } - } - if (!m_remoteReady || oldScreenSaverSync == m_screenSaverSync) { - updateScreenSaverSync = false; - } - } - - // update screen saver synchronization - if (updateScreenSaverSync) { - if (oldScreenSaverSync) { - getScreen()->closeScreensaver(); - } - else { - getScreen()->openScreensaver(false); - } - } -} - -bool -CSecondaryScreen::isActive() const -{ - CLock lock(&m_mutex); - return m_active; -} - -void -CSecondaryScreen::getClipboard(ClipboardID id, - IClipboard* clipboard) const -{ - getScreen()->getClipboard(id, clipboard); -} - -SInt32 -CSecondaryScreen::getJumpZoneSize() const -{ - return 0; -} - -void -CSecondaryScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - sync(); - getScreen()->getShape(x, y, w, h); -} - -void -CSecondaryScreen::getCursorPos(SInt32& x, SInt32& y) const -{ - sync(); - getScreen()->getCursorPos(x, y); -} - -void -CSecondaryScreen::onPreMainLoop() -{ - // do nothing -} - -void -CSecondaryScreen::onPostMainLoop() -{ - // do nothing -} - -void -CSecondaryScreen::onPreOpen() -{ - // do nothing -} - -void -CSecondaryScreen::onPostOpen() -{ - // do nothing -} - -void -CSecondaryScreen::onPreClose() -{ - // do nothing -} - -void -CSecondaryScreen::onPostClose() -{ - // do nothing -} - -void -CSecondaryScreen::onPreEnter() -{ - // do nothing -} - -void -CSecondaryScreen::onPostEnter() -{ - // do nothing -} - -void -CSecondaryScreen::onPreLeave() -{ - // do nothing -} - -void -CSecondaryScreen::onPostLeave() -{ - // do nothing -} - -void -CSecondaryScreen::updateKeys() -{ - sync(); - - // clear key state - memset(m_keys, 0, sizeof(m_keys)); - memset(m_fakeKeys, 0, sizeof(m_fakeKeys)); - - // let subclass set m_keys - updateKeys(m_keys); - - // get m_mask from subclass - m_mask = getModifiers(); - LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); -} - -void -CSecondaryScreen::releaseKeys() -{ - CLock lock(&m_mutex); - sync(); - - // release keys that we've synthesized a press for and only those - // keys. we don't want to synthesize a release on a key the user - // is still physically pressing. - for (UInt32 i = 1; i < 256; ++i) { - if ((m_fakeKeys[i] & kDown) != 0) { - fakeKeyEvent(i, false); - m_keys[i] &= ~kDown; - m_fakeKeys[i] &= ~kDown; - } - } - - flush(); -} - -void -CSecondaryScreen::toggleKey(KeyID keyID, KeyModifierMask mask) -{ - // get the system key ID for this toggle key ID - SysKeyID sysKeyID = getToggleSysKey(keyID); - if (sysKeyID == 0) { - return; - } - - // toggle the key - if (isKeyHalfDuplex(keyID)) { - // "half-duplex" toggle - fakeKeyEvent(sysKeyID, (m_mask & mask) == 0); - } - else { - // normal toggle - fakeKeyEvent(sysKeyID, true); - fakeKeyEvent(sysKeyID, false); - } - flush(); - - // toggle shadow state - m_mask ^= mask; - sysKeyID &= 0xffu; - m_keys[sysKeyID] ^= kToggled; - m_fakeKeys[sysKeyID] ^= kToggled; -} - -bool -CSecondaryScreen::isKeyDown(SysKeyID sysKeyID) const -{ - sysKeyID &= 0xffu; - return (sysKeyID != 0 && ((m_keys[sysKeyID] & kDown) != 0)); -} - -bool -CSecondaryScreen::isKeyToggled(SysKeyID sysKeyID) const -{ - sysKeyID &= 0xffu; - return (sysKeyID != 0 && ((m_keys[sysKeyID] & kToggled) != 0)); -} - -bool -CSecondaryScreen::isKeyHalfDuplex(KeyID keyID) const -{ - return ((keyID == kKeyCapsLock && m_capsLockHalfDuplex) || - (keyID == kKeyNumLock && m_numLockHalfDuplex)); -} diff --git a/lib/synergy/CSecondaryScreen.h b/lib/synergy/CSecondaryScreen.h deleted file mode 100644 index c69692b9..00000000 --- a/lib/synergy/CSecondaryScreen.h +++ /dev/null @@ -1,446 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CSECONDARYSCREEN_H -#define CSECONDARYSCREEN_H - -#include "ClipboardTypes.h" -#include "KeyTypes.h" -#include "MouseTypes.h" -#include "OptionTypes.h" -#include "CMutex.h" -#include "stdmap.h" -#include "stdvector.h" - -class IClipboard; -class IScreen; - -//! Generic client-side screen -/*! -This is a platform independent base class for secondary screen -implementations. A secondary screen is a client-side screen. -Each platform will derive a class from CSecondaryScreen to handle -platform dependent operations. -*/ -class CSecondaryScreen { -public: - CSecondaryScreen(); - virtual ~CSecondaryScreen(); - - //! @name manipulators - //@{ - - //! Open screen - /*! - Opens the screen. It also causes events to the reported to an - IScreenReceiver (which is set through some other interface). - Calls close() before returning (rethrowing) if it fails for any - reason. - */ - void open(); - - //! Run event loop - /*! - Run the screen's event loop. This returns when it detects - the application should terminate or when exitMainLoop() is called. - mainLoop() may only be called between open() and close(). - */ - void mainLoop(); - - //! Exit event loop - /*! - Force mainLoop() to return. This call can return before - mainLoop() does (i.e. asynchronously). - */ - void exitMainLoop(); - - //! Prepare for remote control - /*! - Prepares the screen for remote control by the server. In - particular, it disables the screen saver. - */ - void remoteControl(); - - //! Release from remote control - /*! - Cleans up the screen from remote control by the server. In - particular, it enables the screen saver. It also synthesizes - key up events for any keys that are logically down; without - this the client will leave its keyboard in the wrong logical - state. - */ - void localControl(); - - //! Close screen - /*! - Closes the screen. - */ - void close(); - - //! Enter screen - /*! - Called when the user navigates to this secondary screen. Warps - the cursor to the absolute coordinates \c x,y and unhides - it. Also prepares to synthesize input events. - */ - void enter(SInt32 x, SInt32 y, KeyModifierMask mask); - - //! Leave screen - /*! - Called when the user navigates off the secondary screen. Cleans - up input event synthesis and hides the cursor. - */ - void leave(); - - //! Set clipboard - /*! - Sets the system's clipboard contents. This is usually called - soon after an enter(). - */ - void setClipboard(ClipboardID, const IClipboard*); - - //! Grab clipboard - /*! - Grabs (i.e. take ownership of) the system clipboard. - */ - void grabClipboard(ClipboardID); - - //! Activate/deactivate screen saver - /*! - Forcibly activates the screen saver if \c activate is true otherwise - forcibly deactivates it. - */ - void screensaver(bool activate); - - //! Notify of key press - /*! - Synthesize key events to generate a press of key \c id. If possible - match the given modifier mask. The KeyButton identifies the physical - key on the server that generated this key down. The client must - ensure that a key up or key repeat that uses the same KeyButton will - synthesize an up or repeat for the same client key synthesized by - keyDown(). - */ - void keyDown(KeyID id, KeyModifierMask, KeyButton); - - //! Notify of key repeat - /*! - Synthesize key events to generate a press and release of key \c id - \c count times. If possible match the given modifier mask. - */ - void keyRepeat(KeyID id, KeyModifierMask, - SInt32 count, KeyButton); - - //! Notify of key release - /*! - Synthesize key events to generate a release of key \c id. If possible - match the given modifier mask. - */ - void keyUp(KeyID id, KeyModifierMask, KeyButton); - - //! Notify of mouse press - /*! - Synthesize mouse events to generate a press of mouse button \c id. - */ - void mouseDown(ButtonID id); - - //! Notify of mouse release - /*! - Synthesize mouse events to generate a release of mouse button \c id. - */ - void mouseUp(ButtonID id); - - //! Notify of mouse motion - /*! - Synthesize mouse events to generate mouse motion to the absolute - screen position \c xAbs,yAbs. - */ - void mouseMove(SInt32 xAbs, SInt32 yAbs); - - //! Notify of mouse wheel motion - /*! - Synthesize mouse events to generate mouse wheel motion of \c delta. - \c delta is positive for motion away from the user and negative for - motion towards the user. Each wheel click should generate a delta - of +/-120. - */ - void mouseWheel(SInt32 delta); - - //! Notify of options changes - /*! - Reset all options to their default values. Overrides should call - the superclass's method. - */ - virtual void resetOptions(); - - //! Notify of options changes - /*! - Set options to given values. Ignore unknown options and don't - modify our options that aren't given in \c options. Overrides - should call the superclass's method. - */ - virtual void setOptions(const COptionsList& options); - - //@} - //! @name accessors - //@{ - - //! Test if active - /*! - Returns true iff the screen is active (i.e. the user has entered - the screen). Note this is the reverse of a primary screen. - */ - bool isActive() const; - - //! Get clipboard - /*! - Saves the contents of the system clipboard indicated by \c id. - */ - void getClipboard(ClipboardID id, IClipboard*) const; - - //! Get jump zone size - /*! - Return the jump zone size, the size of the regions on the edges of - the screen that cause the cursor to jump to another screen. - */ - SInt32 getJumpZoneSize() const; - - //! Get screen shape - /*! - Return the position of the upper-left corner of the screen in \c x and - \c y and the size of the screen in \c width and \c height. - */ - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const; - - //! Get cursor position - /*! - Return the current position of the cursor in \c x,y. - */ - virtual void getCursorPos(SInt32& x, SInt32& y) const; - - //! Get screen - /*! - Return the platform dependent screen. - */ - virtual IScreen* getScreen() const = 0; - - //@} - -protected: - typedef UInt8 KeyState; - typedef UInt32 SysKeyID; - enum EKeyState { kDown = 0x01, kToggled = 0x80 }; - enum EKeyAction { kPress, kRelease, kRepeat }; - class Keystroke { - public: - SysKeyID m_sysKeyID; - bool m_press; - bool m_repeat; - }; - typedef std::vector Keystrokes; - typedef std::map ServerKeyMap; - - void updateKeys(); - void releaseKeys(); - void doKeystrokes(const Keystrokes&, SInt32 count); - bool isKeyDown(SysKeyID) const; - bool isKeyToggled(SysKeyID) const; - bool isKeyHalfDuplex(KeyID) const; - - //! Pre-mainLoop() hook - /*! - Called on entry to mainLoop(). Override to perform platform specific - operations. Default does nothing. May throw. - */ - virtual void onPreMainLoop(); - - //! Post-mainLoop() hook - /*! - Called on exit from mainLoop(). Override to perform platform specific - operations. Default does nothing. May \b not throw. - */ - virtual void onPostMainLoop(); - - //! Pre-open() hook - /*! - Called on entry to open(). Override to perform platform specific - operations. Default does nothing. May throw. - */ - virtual void onPreOpen(); - - //! Post-open() hook - /*! - Called on exit from open() iff the open was successful. Default - does nothing. May throw. - */ - virtual void onPostOpen(); - - //! Pre-close() hook - /*! - Called on entry to close(). Override to perform platform specific - operations. Default does nothing. May \b not throw. - */ - virtual void onPreClose(); - - //! Post-close() hook - /*! - Called on exit from close(). Override to perform platform specific - operations. Default does nothing. May \b not throw. - */ - virtual void onPostClose(); - - //! Pre-enter() hook - /*! - Called on entry to enter() after desktop synchronization. Override - to perform platform specific operations. Default does nothing. May - \b not throw. - */ - virtual void onPreEnter(); - - //! Post-enter() hook - /*! - Called on exit from enter(). Override to perform platform specific - operations. Default does nothing. May \b not throw. - */ - virtual void onPostEnter(); - - //! Pre-leave() hook - /*! - Called on entry to leave() after desktop synchronization. Override - to perform platform specific operations. Default does nothing. May - \b not throw. - */ - virtual void onPreLeave(); - - //! Post-leave() hook - /*! - Called on exit from leave(). Override to perform platform specific - operations. Default does nothing. May \b not throw. - */ - virtual void onPostLeave(); - - //! Create window - /*! - Called to create the window. This window is generally used to - receive events and hide the cursor. - */ - virtual void createWindow() = 0; - - //! Destroy window - /*! - Called to destroy the window created by createWindow(). - */ - virtual void destroyWindow() = 0; - - //! Show window - /*! - Called when the user navigates off this secondary screen. It needn't - actually show the window created by createWindow() but it must move - the cursor to x,y, hide it, and clean up event synthesis. - */ - virtual void showWindow(SInt32 x, SInt32 y) = 0; - - //! Hide window - /*! - Called when the user navigates to this secondary screen. It should - hide the window (if shown), show the cursor, and prepare to synthesize - input events. - */ - virtual void hideWindow() = 0; - - //! Synchronize key state - /*! - Save the current keyboard state. Normally a screen will save - the keyboard state in this method and use this shadow state, - available through isKeyDown() and getKeyState(), when - synthesizing events. - */ - virtual void updateKeys(KeyState* sysKeyStates) = 0; - - //! Get modifier key state - /*! - Return the current keyboard modifier state. - */ - virtual KeyModifierMask getModifiers() const = 0; - - //! Synchronize toggle key state - /*! - Toggles modifiers that don't match the given state so that they do. - */ - void setToggleState(KeyModifierMask); - - virtual SysKeyID getUnhanded(SysKeyID) const; - virtual SysKeyID getOtherHanded(SysKeyID) const; - virtual bool isAutoRepeating(SysKeyID) const = 0; - virtual KeyModifierMask getModifierKeyMask(SysKeyID) const = 0; - virtual bool isModifierActive(SysKeyID) const = 0; - virtual SysKeyID getToggleSysKey(KeyID keyID) const = 0; - virtual bool synthesizeCtrlAltDel(EKeyAction); - virtual void sync() const; - virtual void flush(); - - virtual KeyModifierMask - mapKey(Keystrokes&, SysKeyID& sysKeyID, KeyID, - KeyModifierMask currentMask, - KeyModifierMask desiredMask, EKeyAction) const = 0; - virtual void fakeKeyEvent(SysKeyID, bool press) const = 0; - virtual void fakeMouseButton(ButtonID, bool press) const = 0; - - //! Warp cursor - /*! - Warp the cursor to the absolute coordinates \c x,y. - */ - virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; - - virtual void fakeMouseWheel(SInt32 delta) const = 0; - -private: - void toggleKey(KeyID, KeyModifierMask); - -private: - CMutex m_mutex; - - // true if ready for remote control - bool m_remoteReady; - - // m_active is true if this screen has been entered - bool m_active; - - // true if screen saver should be synchronized to server - bool m_screenSaverSync; - - // map server key buttons to local system keys - ServerKeyMap m_serverKeyMap; - - // system key states as set by us or the user - KeyState m_keys[256]; - - // system key states as set by us - KeyState m_fakeKeys[256]; - - // current active modifiers -// XXX -- subclasses still have and use this - KeyModifierMask m_mask; - - // the toggle key state when this screen was last entered - KeyModifierMask m_toggleKeys; - - // note toggle keys that toggles on up/down (false) or on - // transition (true) - bool m_numLockHalfDuplex; - bool m_capsLockHalfDuplex; -}; - -#endif diff --git a/lib/synergy/IKeyState.h b/lib/synergy/IKeyState.h new file mode 100644 index 00000000..14a415c2 --- /dev/null +++ b/lib/synergy/IKeyState.h @@ -0,0 +1,156 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IKEYSTATE_H +#define IKEYSTATE_H + +#include "IInterface.h" +#include "KeyTypes.h" +#include "CString.h" +#include "stdvector.h" + +class IKeyState : public IInterface { +public: + class Keystroke { + public: + KeyButton m_key; + bool m_press; + bool m_repeat; + }; + + typedef std::vector Keystrokes; + typedef std::vector KeyButtons; + + //! @name manipulators + //@{ + + //! Update the key state + /*! + Causes the key state to get updated to reflect the physical keyboard + state and current keyboard mapping. + */ + virtual void updateKeys() = 0; + + //! Release fake pressed keys + /*! + Send fake key events to release keys that aren't physically pressed + but are logically pressed. + */ + virtual void releaseKeys() = 0; + + //! Mark key as being down + /*! + Sets the state of \c key to down. + */ + virtual void setKeyDown(KeyButton key) = 0; + + //! Mark modifier as being toggled on + /*! + Sets the state of the keys for the given (single) modifier to be + toggled on. + */ + virtual void setToggled(KeyModifierMask) = 0; + + //! Add keys for modifier + /*! + Sets the keys that are mapped to the given (single) modifier. For + example, if buttons 5 and 23 were mapped to KeyModifierShift (perhaps + as left and right shift keys) then the mask would be KeyModifierShift + and \c keys would contain 5 and 23. A modifier with no keys is + ignored. All keys must be valid (not zero). \c keys may be modified + by the call. + */ + virtual void addModifier(KeyModifierMask, KeyButtons& keys) = 0; + + //! Set toggle key state + /*! + Update the local toggle key state to match the given state. + */ + virtual void setToggleState(KeyModifierMask) = 0; + + //@} + //! @name accessors + //@{ + + //! Test if any key is down + /*! + If any key is down then returns one of those keys. Otherwise returns 0. + */ + virtual KeyButton isAnyKeyDown() const = 0; + + //! Test if key is pressed + /*! + Returns true iff the given key is down. Half-duplex toggles + should always return false. + */ + virtual bool isKeyDown(KeyButton) const = 0; + + //! Test if modifier is a toggle + /*! + Returns true iff the given (single) modifier is a toggle. + */ + virtual bool isToggle(KeyModifierMask) const = 0; + + //! Test if modifier is half-duplex + /*! + Returns true iff the given (single) modifier is a half-duplex + toggle key. + */ + virtual bool isHalfDuplex(KeyModifierMask) const = 0; + + //! Test if modifier is active + /*! + Returns true iff the given (single) modifier is currently active. + */ + virtual bool isModifierActive(KeyModifierMask) const = 0; + + //! Get the active modifiers + /*! + Returns the modifiers that are currently active. + */ + virtual KeyModifierMask + getActiveModifiers() const = 0; + + //! Get key events to change modifier state + /*! + Retrieves the key events necessary to activate (\c desireActive is true) + or deactivate (\c desireActive is false) the modifier given by \c mask + by pushing them onto the back of \c keys. \c mask must specify exactly + one modifier. \c undo receives the key events necessary to restore the + modifier's previous state. They're pushed onto \c undo in the reverse + order they should be executed. Returns true if the modifier can be + adjusted, false otherwise. + */ + virtual bool mapModifier(Keystrokes& keys, Keystrokes& undo, + KeyModifierMask mask, bool desireActive) const = 0; + + //! Get modifier mask for key + /*! + Returns the modifier mask for \c key. If \c key is not a modifier + key then returns 0. + */ + virtual KeyModifierMask + getMaskForKey(KeyButton) const = 0; + + //@} + +protected: + typedef UInt8 KeyState; + enum EKeyState { + kDown = 0x01, //!< Key is down + kToggled = 0x80 //!< Key is toggled on + }; +}; + +#endif diff --git a/lib/synergy/IScreen.h b/lib/synergy/IPlatformScreen.h similarity index 58% rename from lib/synergy/IScreen.h rename to lib/synergy/IPlatformScreen.h index 16769821..149c5526 100644 --- a/lib/synergy/IScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -12,21 +12,24 @@ * GNU General Public License for more details. */ -#ifndef ISCREEN_H -#define ISCREEN_H +#ifndef IPLATFORMSCREEN_H +#define IPLATFORMSCREEN_H -#include "IInterface.h" +#include "IPrimaryScreen.h" +#include "ISecondaryScreen.h" #include "ClipboardTypes.h" +#include "OptionTypes.h" class IClipboard; +class IKeyState; //! Screen interface /*! This interface defines the methods common to all platform dependent -screen implementations that are use by both primary and secondary +screen implementations that are used by both primary and secondary screens. */ -class IScreen : public IInterface { +class IPlatformScreen : public IPrimaryScreen, public ISecondaryScreen { public: //! @name manipulators //@{ @@ -37,7 +40,29 @@ public: if the screen cannot be opened but retrying later may succeed. Otherwise throw some other XScreenOpenFailure exception. */ - virtual void open() = 0; + virtual void open(IKeyState*) = 0; + + //! Close screen + /*! + Called to close the screen. close() should quietly ignore calls + that don't have a matching successful call to open(). + */ + virtual void close() = 0; + + //! Enable screen + /*! + Enable the screen, preparing it to report system and user events. + For a secondary screen it also means preparing to synthesize events + and hiding the cursor. + */ + virtual void enable() = 0; + + //! Disable screen + /*! + Undoes the operations in enable() and events should no longer + be reported. + */ + virtual void disable() = 0; //! Run event loop /*! @@ -54,12 +79,20 @@ public: */ virtual void exitMainLoop() = 0; - //! Close screen + //! Enter screen /*! - Called to close the screen. close() should quietly ignore calls - that don't have a matching successful call to open(). + Called when the user navigates to this screen. */ - virtual void close() = 0; + virtual void enter() = 0; + + //! Leave screen + /*! + Called when the user navigates off the screen. Returns true on + success, false on failure. A typical reason for failure is being + unable to install the keyboard and mouse snoopers on a primary + screen. Secondary screens should not fail. + */ + virtual bool leave() = 0; //! Set clipboard /*! @@ -84,6 +117,7 @@ public: it's closed. If \c notify is false then the screen saver is disabled on open and restored on close. */ +// XXX -- pass an interface pointer, not a notify flag virtual void openScreensaver(bool notify) = 0; //! Close screen saver @@ -101,20 +135,35 @@ public: */ virtual void screensaver(bool activate) = 0; - //! Attach to desktop + //! Notify of options changes /*! - Called to ensure that this thread is attached to the visible desktop. - This is mainly intended for microsoft windows which has an artificial - distinction between desktops where a thread cannot interact with the - visible desktop unless the thread is attached to that desktop. Since - it doesn't report when the visible desktop changes we must poll. + Reset all options to their default values. */ - virtual void syncDesktop() = 0; + virtual void resetOptions() = 0; + + //! Notify of options changes + /*! + Set options to given values. Ignore unknown options and don't + modify options that aren't given in \c options. + */ + virtual void setOptions(const COptionsList& options) = 0; + + //! Get keyboard state + /*! + Put the current keyboard state into the IKeyState passed to \c open(). + */ + virtual void updateKeys() = 0; //@} //! @name accessors //@{ + //! Test if is primary screen + /*! + Return true iff this screen is a primary screen. + */ + virtual bool isPrimary() const = 0; + //! Get clipboard /*! Save the contents of the clipboard indicated by \c id and return @@ -136,15 +185,26 @@ public: */ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; - //! Get cursor center position - /*! - Return the cursor center position which is where we park the - cursor to compute cursor motion deltas and should be far from - the edges of the screen, typically the center. - */ - virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; - //@} + + // IPrimaryScreen overrides + virtual void reconfigure(UInt32 activeSides) = 0; + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + virtual UInt32 addOneShotTimer(double timeout) = 0; + virtual SInt32 getJumpZoneSize() const = 0; + virtual bool isAnyMouseButtonDown() const = 0; + virtual const char* getKeyName(KeyButton) const = 0; + + // ISecondaryScreen overrides + virtual void fakeKeyEvent(KeyButton id, bool press) const = 0; + virtual bool fakeCtrlAltDel() const = 0; + virtual void fakeMouseButton(ButtonID id, bool press) const = 0; + virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + virtual void fakeMouseWheel(SInt32 delta) const = 0; + virtual KeyButton mapKey(IKeyState::Keystrokes&, + const IKeyState& keyState, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const = 0; }; #endif diff --git a/lib/synergy/IPrimaryScreen.h b/lib/synergy/IPrimaryScreen.h new file mode 100644 index 00000000..560436f3 --- /dev/null +++ b/lib/synergy/IPrimaryScreen.h @@ -0,0 +1,84 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IPRIMARYSCREEN_H +#define IPRIMARYSCREEN_H + +#include "IInterface.h" +#include "IKeyState.h" + +//! Primary screen interface +/*! +This interface defines the methods common to all platform dependent +primary screen implementations. +*/ +class IPrimaryScreen : public IInterface { +public: +// XXX -- may need an interface for sending events + //! @name manipulators + //@{ + + //! Update configuration + /*! + This is called when the configuration has changed. \c activeSides + is a bitmask of EDirectionMask indicating which sides of the + primary screen are linked to clients. Override to handle the + possible change in jump zones. + */ + virtual void reconfigure(UInt32 activeSides) = 0; + + //! Warp cursor + /*! + Warp the cursor to the absolute coordinates \c x,y. Also + discard input events up to and including the warp before + returning. + */ + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + + //! Install a one-shot timer + /*! + Installs a one-shot timer for \c timeout seconds and returns the + id of the timer. + */ +// XXX -- need to specify the receiver of the event. or we should +// pass a job. need a method to remove the timer? + virtual UInt32 addOneShotTimer(double timeout) = 0; + + //@} + //! @name accessors + //@{ + + //! Get jump zone size + /*! + Return the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. + */ + virtual SInt32 getJumpZoneSize() const = 0; + + //! Test if mouse is pressed + /*! + Return true if any mouse button is currently pressed. + */ + virtual bool isAnyMouseButtonDown() const = 0; + + //! Get name of key + /*! + Return a string describing the given key. + */ + virtual const char* getKeyName(KeyButton) const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/IScreenEventHandler.h b/lib/synergy/IScreenEventHandler.h deleted file mode 100644 index 699d295c..00000000 --- a/lib/synergy/IScreenEventHandler.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef ISCREENEVENTHANDLER_H -#define ISCREENEVENTHANDLER_H - -#include "IInterface.h" - -// the platform screen should define this -class CEvent; - -class IScreen; - -//! Screen event handler interface -/*! -This is the interface through which IScreen sends notification of events. -Each platform will derive two types from IScreenEventHandler, one -for handling events on the primary screen and one for the -secondary screen. The header file with the IScreen subclass for -each platform should define the CEvent type, which depends on the -type of native events for that platform. -*/ -class IScreenEventHandler : public IInterface { -public: - //! @name manipulators - //@{ - - //! Notify of screen saver change - /*! - Called when the screensaver is activated or deactivated. - */ - virtual void onScreensaver(bool activated) = 0; - - //! Event filtering - /*! - Called for each event before event translation and dispatch. Return - true to skip translation and dispatch. Subclasses should call the - superclass's version first and return true if it returns true. - */ - virtual bool onPreDispatch(const CEvent* event) = 0; - - //! Event handling - /*! - Called to handle an event. Iff the event was handled return true and - store the result, if any, in event->m_result, which defaults to zero. - */ - virtual bool onEvent(CEvent* event) = 0; - - //! Notify of one-shot timer expiration - /*! - Called when a one-shot timer expires. - */ - virtual void onOneShotTimerExpired(UInt32 id) = 0; - - //@} -}; - -#endif diff --git a/lib/synergy/IPrimaryScreenFactory.h b/lib/synergy/IScreenFactory.h similarity index 64% rename from lib/synergy/IPrimaryScreenFactory.h rename to lib/synergy/IScreenFactory.h index 630449a5..8a5ab410 100644 --- a/lib/synergy/IPrimaryScreenFactory.h +++ b/lib/synergy/IScreenFactory.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman + * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -12,27 +12,29 @@ * GNU General Public License for more details. */ -#ifndef IPRIMARYSCREENFACTORY_H -#define IPRIMARYSCREENFACTORY_H +#ifndef ISCREENFACTORY_H +#define ISCREENFACTORY_H #include "IInterface.h" -class CPrimaryScreen; class IPrimaryScreenReceiver; +class IPlatformScreen; class IScreenReceiver; //! Primary screen factory interface /*! -This interface provides factory methods to create primary screens. +This interface provides factory methods to create primary and +secondary screens. */ -class IPrimaryScreenFactory : public IInterface { +class IScreenFactory : public IInterface { public: //! Create screen /*! - Create and return a primary screen. The caller must delete the - returned object. + Create and return a screen. The caller must delete the returned + object. The screen is a primary screen iff the IPrimaryScreenReceiver + is not NULL. */ - virtual CPrimaryScreen* + virtual IPlatformScreen* create(IScreenReceiver*, IPrimaryScreenReceiver*) = 0; }; diff --git a/lib/synergy/ISecondaryScreen.h b/lib/synergy/ISecondaryScreen.h new file mode 100644 index 00000000..0a556d59 --- /dev/null +++ b/lib/synergy/ISecondaryScreen.h @@ -0,0 +1,82 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISECONDARYSCREEN_H +#define ISECONDARYSCREEN_H + +#include "IInterface.h" +#include "IKeyState.h" +#include "MouseTypes.h" + +//! Secondary screen interface +/*! +This interface defines the methods common to all platform dependent +secondary screen implementations. +*/ +class ISecondaryScreen : public IInterface { +public: + //! @name accessors + //@{ + + //! Fake key press/release + /*! + Synthesize a press or release of key \c id. + */ + virtual void fakeKeyEvent(KeyButton id, bool press) const = 0; + + //! Fake ctrl+alt+del + /*! + Synthesize a press of ctrl+alt+del. Return true if processing is + complete and false if normal key processing should continue. + */ + virtual bool fakeCtrlAltDel() const = 0; + + //! Fake mouse press/release + /*! + Synthesize a press or release of mouse button \c id. + */ + virtual void fakeMouseButton(ButtonID id, bool press) const = 0; + + //! Fake mouse move + /*! + Synthesize a mouse move to the absolute coordinates \c x,y. + */ + virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + + //! Fake mouse wheel + /*! + Synthesize a mouse wheel event of amount \c delta. + */ + virtual void fakeMouseWheel(SInt32 delta) const = 0; + + //! Map key press/repeat to keystrokes + /*! + Convert a press/repeat of key \c id with the modifiers as given + in \c desiredMask into the keystrokes necessary to synthesize + that key event. This may expand into multiple keys due to modifiers + that don't match the current modifier state from \c keyState, or to + needing to compose a character using dead key, or to other reasons. + Return the platform specific code of the key being pressed. If \c id + cannot be mapped or if \c isAutoRepeat is true and the key does not + auto-repeat then return 0. + */ + virtual KeyButton mapKey(IKeyState::Keystrokes&, + const IKeyState& keyState, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const = 0; + + //@} +}; + +#endif diff --git a/lib/synergy/ISecondaryScreenFactory.h b/lib/synergy/ISecondaryScreenFactory.h deleted file mode 100644 index 03667ec8..00000000 --- a/lib/synergy/ISecondaryScreenFactory.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef ISECONDARYSCREENFACTORY_H -#define ISECONDARYSCREENFACTORY_H - -#include "IInterface.h" - -class CSecondaryScreen; -class IScreenReceiver; - -//! Secondary screen factory interface -/*! -This interface provides factory methods to create secondary screens. -*/ -class ISecondaryScreenFactory : public IInterface { -public: - //! Create screen - /*! - Create and return a secondary screen. The caller must delete the - returned object. - */ - virtual CSecondaryScreen* - create(IScreenReceiver*) = 0; -}; - -#endif diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 4f1f1353..9923fa6e 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -28,27 +28,26 @@ libsynergy_a_SOURCES = \ CClipboard.cpp \ CInputPacketStream.cpp \ COutputPacketStream.cpp \ - CPrimaryScreen.cpp \ CProtocolUtil.cpp \ - CSecondaryScreen.cpp \ + CScreen.cpp \ XScreen.cpp \ XSynergy.cpp \ CClipboard.h \ CInputPacketStream.h \ COutputPacketStream.h \ - CPrimaryScreen.h \ CProtocolUtil.h \ - CSecondaryScreen.h \ + CScreen.h \ ClipboardTypes.h \ IClient.h \ IClipboard.h \ - IPrimaryScreenFactory.h \ + IKeyState.h \ + IPlatformScreen.h \ + IPrimaryScreen.h \ IPrimaryScreenReceiver.h \ - IScreen.h \ - IScreenEventHandler.h \ + IScreenFactory.h \ IScreenReceiver.h \ IScreenSaver.h \ - ISecondaryScreenFactory.h \ + ISecondaryScreen.h \ IServer.h \ KeyTypes.h \ MouseTypes.h \ diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 583271c6..928405ea 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -99,15 +99,11 @@ SOURCE=.\COutputPacketStream.cpp # End Source File # Begin Source File -SOURCE=.\CPrimaryScreen.cpp -# End Source File -# Begin Source File - SOURCE=.\CProtocolUtil.cpp # End Source File # Begin Source File -SOURCE=.\CSecondaryScreen.cpp +SOURCE=.\CScreen.cpp # End Source File # Begin Source File @@ -139,15 +135,11 @@ SOURCE=.\COutputPacketStream.h # End Source File # Begin Source File -SOURCE=.\CPrimaryScreen.h -# End Source File -# Begin Source File - SOURCE=.\CProtocolUtil.h # End Source File # Begin Source File -SOURCE=.\CSecondaryScreen.h +SOURCE=.\CScreen.h # End Source File # Begin Source File @@ -159,7 +151,15 @@ SOURCE=.\IClipboard.h # End Source File # Begin Source File -SOURCE=.\IPrimaryScreenFactory.h +SOURCE=.\IKeyState.h +# End Source File +# Begin Source File + +SOURCE=.\IPlatformScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IPrimaryScreen.h # End Source File # Begin Source File @@ -167,11 +167,7 @@ SOURCE=.\IPrimaryScreenReceiver.h # End Source File # Begin Source File -SOURCE=.\IScreen.h -# End Source File -# Begin Source File - -SOURCE=.\IScreenEventHandler.h +SOURCE=.\IScreenFactory.h # End Source File # Begin Source File @@ -183,7 +179,7 @@ SOURCE=.\IScreenSaver.h # End Source File # Begin Source File -SOURCE=.\ISecondaryScreenFactory.h +SOURCE=.\ISecondaryScreen.h # End Source File # Begin Source File From 809a347333f01217b9dfd32d61f44b615aa1bad6 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 2 Sep 2003 22:06:07 +0000 Subject: [PATCH 561/807] Removed heap allocation when polling sockets. --- lib/arch/CArchNetworkBSD.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 9b6eec6d..1db5df82 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -274,7 +274,8 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) } // allocate space for translated query - struct pollfd* pfd = new struct pollfd[num]; + struct pollfd* pfd = reinterpret_cast( + alloca(num * sizeof(struct pollfd))); // translate query for (int i = 0; i < num; ++i) { @@ -298,7 +299,6 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) ARCH->testCancelThread(); continue; } - delete[] pfd; throwError(errno); } } while (false); @@ -320,9 +320,6 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) } } - // done with translated query - delete[] pfd; - return n; } From 9311250c22708f19cc92c19069012cfec705b87e Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 3 Sep 2003 21:21:22 +0000 Subject: [PATCH 562/807] Fixed uses of X11 display without mutex held. --- lib/platform/CXWindowsScreen.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 2dd1949d..3de7bc06 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -326,6 +326,8 @@ CXWindowsScreen::close() void CXWindowsScreen::enable() { + CLock lock(&m_mutex); + if (!m_isPrimary) { // get the keyboard control state XKeyboardState keyControl; @@ -347,6 +349,8 @@ CXWindowsScreen::enable() void CXWindowsScreen::disable() { + CLock lock(&m_mutex); + // release input context focus if (m_ic != NULL) { XUnsetICFocus(m_ic); @@ -1761,8 +1765,6 @@ CXWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) LOG((CLOG_DEBUG2 "warped to %d,%d", x, y)); } -// ------------------ - void CXWindowsScreen::updateButtons() { From a237cbacdb038f3381d6afa6769b8e0f1e8bfe01 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 3 Sep 2003 21:22:25 +0000 Subject: [PATCH 563/807] Fixed suppression of auto-repeat for keys that don't auto-repeat. Had forgotten to query the auto-repeat per-key mask. --- lib/platform/CXWindowsKeyMapper.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/platform/CXWindowsKeyMapper.cpp b/lib/platform/CXWindowsKeyMapper.cpp index 80ecbc37..e1e4ab9f 100644 --- a/lib/platform/CXWindowsKeyMapper.cpp +++ b/lib/platform/CXWindowsKeyMapper.cpp @@ -98,6 +98,9 @@ CXWindowsKeyMapper::update(Display* display, IKeyState* keyState) char keys[32]; XQueryKeymap(display, keys); + // save the auto-repeat mask + XGetKeyboardControl(display, &m_keyControl); + // query the pointer to get the keyboard state Window root = DefaultRootWindow(display), window; int xRoot, yRoot, xWindow, yWindow; From 8395f698998378c343b9a093399f6959b28b02bb Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 3 Sep 2003 21:26:52 +0000 Subject: [PATCH 564/807] Now leaving client screen or entering server screen if necessary when disabling the screen. --- lib/synergy/CScreen.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index ba3eb39b..42c5a1d6 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -93,6 +93,12 @@ CScreen::disable() CLock lock(&m_mutex); assert(m_enabled); + if (!m_isPrimary && m_entered) { + leave(); + } + else if (m_isPrimary && !m_entered) { + enter(); + } m_screen->disable(); if (m_isPrimary) { disablePrimary(); From 33e359a3840762b4106566cac636bb09d93620c5 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 3 Sep 2003 21:49:57 +0000 Subject: [PATCH 565/807] Fixed failure to unlock mutex in CXWindowsScreen::mainLoop() when returning due to a quit event. --- lib/platform/CXWindowsScreen.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 3de7bc06..1dffd054 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -370,9 +370,14 @@ void CXWindowsScreen::mainLoop() { // wait for an event in a cancellable way and don't lock the - // display while we're waiting. + // display while we're waiting. we use CLock to ensure that + // we unlock on exit but we directly unlock/lock the mutex + // for certain sections when we mustn't hold the lock. it's + // very important that these sections not return (even by + // exception or cancellation) without first reestablishing + // the lock. XEvent event; - m_mutex.lock(); + CLock lock(&m_mutex); for (;;) { @@ -418,8 +423,8 @@ CXWindowsScreen::mainLoop() // wait for message from X server or for timeout. also check // if the thread has been cancelled. poll() should return -1 // with EINTR when the thread is cancelled. - m_mutex.unlock(); CThread::testCancel(); + m_mutex.unlock(); #if HAVE_POLL poll(pfds, 1, timeout); #else @@ -429,8 +434,8 @@ CXWindowsScreen::mainLoop() SELECT_TYPE_ARG234 NULL, SELECT_TYPE_ARG5 timeoutPtr); #endif - CThread::testCancel(); m_mutex.lock(); + CThread::testCancel(); // process timers processTimers(); @@ -445,9 +450,15 @@ CXWindowsScreen::mainLoop() } // wait - m_mutex.unlock(); - CThread::sleep(0.01); - m_mutex.lock(); + try { + m_mutex.unlock(); + CThread::sleep(0.01); + m_mutex.lock(); + } + catch (...) { + m_mutex.lock(); + throw; + } } #endif // !UNIX_LIKE From 80f399839866a55b54d64dfdbfac2bd880086452 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 3 Sep 2003 21:54:18 +0000 Subject: [PATCH 566/807] Changed name-to-address translation to only use IPv4 addresses and to only copy as much address as fits in sockaddr_in.sin_addr, in case hostent.h_length is wrong. --- lib/arch/CArchNetworkBSD.cpp | 13 ++++++++----- lib/arch/CArchNetworkWinsock.cpp | 13 ++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 1db5df82..9bd3c138 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -611,11 +611,14 @@ CArchNetworkBSD::nameToAddr(const std::string& name) } // copy over address (only IPv4 currently supported) - addr->m_len = sizeof(struct sockaddr_in); - inaddr.sin_family = info->h_addrtype; - inaddr.sin_port = 0; - memcpy(&inaddr.sin_addr, info->h_addr_list[0], info->h_length); - memcpy(&addr->m_addr, &inaddr, addr->m_len); + if (info->h_addrtype == AF_INET) { + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = info->h_addrtype; + inaddr.sin_port = 0; + memcpy(&inaddr.sin_addr, info->h_addr_list[0], + sizeof(inaddr.sin_addr)); + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } // done with static buffer ARCH->unlockMutex(m_mutex); diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index 09b507ae..d7b58e87 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -618,11 +618,14 @@ CArchNetworkWinsock::nameToAddr(const std::string& name) } // copy over address (only IPv4 currently supported) - addr->m_len = sizeof(struct sockaddr_in); - inaddr.sin_family = info->h_addrtype; - inaddr.sin_port = 0; - memcpy(&inaddr.sin_addr, info->h_addr_list[0], info->h_length); - memcpy(&addr->m_addr, &inaddr, addr->m_len); + if (info->h_addrtype == AF_INET) { + addr->m_len = sizeof(struct sockaddr_in); + inaddr.sin_family = info->h_addrtype; + inaddr.sin_port = 0; + memcpy(&inaddr.sin_addr, info->h_addr_list[0], + sizeof(inaddr.sin_addr)); + memcpy(&addr->m_addr, &inaddr, addr->m_len); + } } return addr; From 6d3c53671776be5888bc62379da733b5d87c2068 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 6 Sep 2003 23:17:41 +0000 Subject: [PATCH 567/807] Fixed potential failure to use synergy's keyboard layout when using low-level keyboard hooks, fixed handling of the global keyboard layout dead key buffer, fixed identification of dead keys, fixed synthesis of AltGr (now using right-alt instead of left-alt), now using VK_DECIMAL for Separator key, fixed bug where an unmappable key was treated as virtual key 0xff, and added support for shift-space (shift was being discarded). Also fixed failure to hide cursor when leaving primary screen and added support for handling PrintScreen key. --- lib/platform/CMSWindowsKeyMapper.cpp | 303 ++++++++++++++++++--------- lib/platform/CMSWindowsKeyMapper.h | 14 ++ lib/platform/CMSWindowsScreen.cpp | 13 +- lib/synergy/CScreen.cpp | 5 +- 4 files changed, 225 insertions(+), 110 deletions(-) diff --git a/lib/platform/CMSWindowsKeyMapper.cpp b/lib/platform/CMSWindowsKeyMapper.cpp index 4e4fc957..6e47aa95 100644 --- a/lib/platform/CMSWindowsKeyMapper.cpp +++ b/lib/platform/CMSWindowsKeyMapper.cpp @@ -41,7 +41,9 @@ // CMSWindowsKeyMapper // -// table of modifier keys +// table of modifier keys. note that VK_RMENU shows up under the Alt +// key and ModeSwitch. when simulating AltGr we need to use the right +// alt key so we use KeyModifierModeSwitch to get it. const CMSWindowsKeyMapper::CModifierKeys CMSWindowsKeyMapper::s_modifiers[] = { @@ -49,6 +51,7 @@ const CMSWindowsKeyMapper::CModifierKeys KeyModifierControl, { VK_LCONTROL, VK_RCONTROL | 0x100 }, KeyModifierAlt, { VK_LMENU, VK_RMENU | 0x100 }, KeyModifierSuper, { VK_LWIN | 0x100, VK_RWIN | 0x100 }, + KeyModifierModeSwitch, { VK_RMENU | 0x100, 0 }, KeyModifierCapsLock, { VK_CAPITAL, 0 }, KeyModifierNumLock, { VK_NUMLOCK | 0x100, 0 }, KeyModifierScrollLock, { VK_SCROLL, 0 } @@ -655,6 +658,9 @@ const KeyButton CMSWindowsKeyMapper::s_mapEE00[] = /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 }; +/* in g_mapEF00, 0xac is VK_DECIMAL not VK_SEPARATOR because win32 + * doesn't seem to use VK_SEPARATOR but instead maps VK_DECIMAL to + * the same meaning. */ const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -682,7 +688,7 @@ const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, - /* 0xac */ VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100, + /* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100, /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, @@ -698,7 +704,7 @@ const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100 }; -CMSWindowsKeyMapper::CMSWindowsKeyMapper() +CMSWindowsKeyMapper::CMSWindowsKeyMapper() : m_deadKey(0) { // do nothing } @@ -869,7 +875,7 @@ CMSWindowsKeyMapper::updateKey(KeyButton key, bool pressed) KeyButton CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, const IKeyState& keyState, KeyID id, - KeyModifierMask, bool isAutoRepeat) const + KeyModifierMask mask, bool isAutoRepeat) const { KeyButton virtualKey = 0; @@ -890,9 +896,8 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, } } -/* XXX // special handling of VK_SNAPSHOT - if ((virtualKey & 0xff) == VK_SNAPSHOT) { + if ((virtualKey & 0xffu) == VK_SNAPSHOT) { // ignore key repeats on print screen if (!isAutoRepeat) { // get event flags @@ -900,22 +905,20 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, if (isExtendedKey(virtualKey)) { flags |= KEYEVENTF_EXTENDEDKEY; } - if (action != kPress) { - flags |= KEYEVENTF_KEYUP; - } - // active window or fullscreen? + // active window (with alt) or fullscreen (without alt)? BYTE scan = 0; - if ((mask & KeyModifierAlt) == 0) { + if ((mask & KeyModifierAlt) != 0) { scan = 1; } - // send event - keybd_event(static_cast(virtualKey & 0xff), scan, flags, 0); + // send events + keybd_event(static_cast(virtualKey & 0xffu), scan, flags, 0); + flags |= KEYEVENTF_KEYUP; + keybd_event(static_cast(virtualKey & 0xffu), scan, flags, 0); } return 0; } -*/ // handle other special keys if (virtualKey != 0) { @@ -998,32 +1001,21 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, virtualKey = mapCharacter(keys, keyState, multiByte[0], hkl, isAutoRepeat); if (virtualKey != 0) { LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - if ((MapVirtualKey(virtualKey, 2) & 0x80000000u) != 0) { - // it looks like this character is a dead key but - // MapVirtualKey() will claim it's a dead key even if it's - // not (though i don't think it ever claims it's not when - // it is). we need a backup test to ensure that this is - // really a dead key. we could use ToAscii() for this but - // that keeps state and it's a hassle to restore that state. - // OemKeyScan() appears to do the trick. if the character - // cannot be generated with a single keystroke then it - // returns 0xffffffff. - if (OemKeyScan(multiByte[0]) != 0xffffffffu) { - // character mapped to a dead key but we want the - // character for real so send a space key afterwards. - LOG((CLOG_DEBUG2 "character mapped to dead key")); - IKeyState::Keystroke keystroke; - keystroke.m_key = VK_SPACE; - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); + if (isDeadChar(multiByte[0], hkl, false)) { + // character mapped to a dead key but we want the + // character for real so send a space key afterwards. + LOG((CLOG_DEBUG2 "character mapped to dead key")); + IKeyState::Keystroke keystroke; + keystroke.m_key = VK_SPACE; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); - // ignore the release of this key since we already - // handled it. - virtualKey = 0; - } + // ignore the release of this key since we already + // handled it. + virtualKey = 0; } return virtualKey; } @@ -1074,15 +1066,17 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM vkCode, LPARAM info, // 95,98,NT4: num pad scan code -> bad vk code except // SEPARATOR, MULTIPLY, SUBTRACT, ADD + HKL hkl = GetKeyboardLayout(0); + // get the scan code and the extended keyboard flag UINT scanCode = static_cast((info & 0x00ff0000u) >> 16); int extended = ((info & 0x01000000) == 0) ? 0 : 1; + bool press = ((info & 0x80000000) == 0); LOG((CLOG_DEBUG1 "key vk=%d info=0x%08x ext=%d scan=%d", vkCode, info, extended, scanCode)); // handle some keys via table lookup char c = 0; KeyID id = s_virtualKey[vkCode][extended]; -LOG((CLOG_NOTE "code=%d, info=0x%08x -> id=%d", vkCode, info, id)); if (id == kKeyNone) { // not in table @@ -1107,21 +1101,57 @@ LOG((CLOG_NOTE "code=%d, info=0x%08x -> id=%d", vkCode, info, id)); keys[VK_MENU] = 0x80; } - // convert to ascii - WORD ascii; - int result = ToAscii(vkCode, scanCode, keys, &ascii, - ((menu & 0x80) == 0) ? 0 : 1); + // get contents of keyboard layout buffer and clear out that + // buffer. we don't want anything placed there by some other + // app interfering and we need to put anything there back in + // place when we're done. + TCHAR oldDeadKey = getSavedDeadChar(hkl); - // if result is less than zero then it was a dead key. leave it - // there. + // put our previous dead key, if any, in the layout buffer + putBackDeadChar(m_deadKey, hkl, false); + m_deadKey = 0; + + // process key + WORD ascii; + bool isMenu = ((menu & 0x80) != 0); + int result = ToAsciiEx(vkCode, scanCode, keys, &ascii, + isMenu ? 1 : 0, hkl); + + // if result is less than zero then it was a dead key if (result < 0) { - id = kKeyMultiKey; + // save dead key if a key press. we catch the dead key + // release in the result == 2 case below. + if (press) { + m_deadKey = static_cast(ascii & 0xffu); + } } // if result is 1 then the key was succesfully converted else if (result == 1) { c = static_cast(ascii & 0xff); - if (ascii >= 0x80) { + } + + // if result is 2 and the two characters are the same and this + // is a key release then a dead key was released. save the + // dead key. if the two characters are the same and this is + // not a release then a dead key was pressed twice. send the + // dead key. + else if (result == 2) { + if (((ascii & 0xff00u) >> 8) == (ascii & 0x00ffu)) { + if (!press) { + m_deadKey = static_cast(ascii & 0xffu); + } + else { + putBackDeadChar(oldDeadKey, hkl, false); + result = toAscii(' ', hkl, false, &ascii); + c = static_cast((ascii >> 8) & 0xffu); + } + } + } + + // map character to key id + if (c != 0) { + if ((c & 0x80u) != 0) { // character is not really ASCII. instead it's some // character in the current ANSI code page. try to // convert that to a Unicode character. if we fail @@ -1133,54 +1163,22 @@ LOG((CLOG_NOTE "code=%d, info=0x%08x -> id=%d", vkCode, info, id)); id = static_cast(unicode); } else { - id = static_cast(ascii & 0x00ff); + id = static_cast(c) & 0xffu; } } else { - id = static_cast(ascii & 0x00ff); + id = static_cast(c) & 0xffu; } } - // if result is 2 then a previous dead key could not be composed. - else if (result == 2) { - // if the two characters are the same and this is a key release - // then this event is the dead key being released. we put the - // dead key back in that case, otherwise we discard both key - // events because we can't compose the character. alternatively - // we could generate key events for both keys. - if (((ascii & 0xff00) >> 8) != (ascii & 0x00ff) || - (info & 0x80000000) == 0) { - // cannot compose key - return kKeyNone; - } + // clear keyboard layout buffer. this removes any dead key we + // may have just put there. + toAscii(' ', hkl, false, NULL); - // get the scan code of the dead key and the shift state - // required to generate it. - vkCode = VkKeyScan(static_cast(ascii & 0x00ff)); - - // set shift state required to generate key - BYTE keys[256]; - memset(keys, 0, sizeof(keys)); - if (vkCode & 0x0100) { - keys[VK_SHIFT] = 0x80; - } - if (vkCode & 0x0200) { - keys[VK_CONTROL] = 0x80; - } - if (vkCode & 0x0400) { - keys[VK_MENU] = 0x80; - } - - // strip shift state off of virtual key code - vkCode &= 0x00ff; - - // get the scan code for the key - scanCode = MapVirtualKey(vkCode, 0); - - // put it back - ToAscii(vkCode, scanCode, keys, &ascii, 0); - id = kKeyMultiKey; - } + // restore keyboard layout buffer so a dead key inserted by + // another app doesn't disappear mysteriously (from its point + // of view). + putBackDeadChar(oldDeadKey, hkl, false); } // set mask @@ -1197,7 +1195,7 @@ LOG((CLOG_NOTE "code=%d, info=0x%08x -> id=%d", vkCode, info, id)); // required (only because it solves the problems we've seen // so far). in the second, we'll use whatever the keyboard // state says. - WORD virtualKeyAndModifierState = VkKeyScan(c); + WORD virtualKeyAndModifierState = VkKeyScanEx(c, hkl); if (virtualKeyAndModifierState == 0xffff) { // there is no mapping. assume AltGr. LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); @@ -1223,7 +1221,6 @@ LOG((CLOG_NOTE "code=%d, info=0x%08x -> id=%d", vkCode, info, id)); *altgr = needAltGr; } - // map modifier key // map modifier key KeyModifierMask mask = 0; if (((m_keys[VK_LSHIFT] | @@ -1276,7 +1273,8 @@ UINT CMSWindowsKeyMapper::keyToScanCode(KeyButton* virtualKey) const { // try mapping given virtual key - UINT code = MapVirtualKey((*virtualKey) & 0xffu, 0); + HKL hkl = GetKeyboardLayout(0); + UINT code = MapVirtualKeyEx((*virtualKey) & 0xffu, 0, hkl); if (code != 0) { return code; } @@ -1307,17 +1305,17 @@ CMSWindowsKeyMapper::keyToScanCode(KeyButton* virtualKey) const case VK_LSHIFT: case VK_RSHIFT: *virtualKey = VK_SHIFT; - return MapVirtualKey(VK_SHIFT, 0); + return MapVirtualKeyEx(VK_SHIFT, 0, hkl); case VK_LCONTROL: case VK_RCONTROL: *virtualKey = VK_CONTROL; - return MapVirtualKey(VK_CONTROL, 0); + return MapVirtualKeyEx(VK_CONTROL, 0, hkl); case VK_LMENU: case VK_RMENU: *virtualKey = VK_MENU; - return MapVirtualKey(VK_MENU, 0); + return MapVirtualKeyEx(VK_MENU, 0, hkl); default: return 0; @@ -1401,16 +1399,28 @@ CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, // AltGr. we must always match the desired shift state but only // the desired AltGr state if AltGr is required. AltGr is actually // ctrl + alt so we can't require that ctrl and alt not be pressed - // otherwise users couldn't do, say, ctrl+z.f + // otherwise users couldn't do, say, ctrl+z. + // + // the space character (ascii 32) is special in that it's unaffected + // by shift and should match the shift state from keyState. KeyModifierMask desiredMask = 0; KeyModifierMask requiredMask = KeyModifierShift; - if ((modifierState & 0x01u) == 1) { + if (c == 32) { + if (keyState.isModifierActive(KeyModifierShift)) { + desiredMask |= KeyModifierShift; + } + } + else if ((modifierState & 0x01u) == 1) { desiredMask |= KeyModifierShift; } if ((modifierState & 0x06u) == 6) { - // add ctrl and alt, which must be matched - desiredMask |= KeyModifierControl | KeyModifierAlt; - requiredMask |= KeyModifierControl | KeyModifierAlt; + // add ctrl and alt, which must be matched. match alt via + // mode-switch, which uses the right alt key rather than + // the left. windows doesn't care which alt key so long + // as ctrl is also down but some apps do their own mapping + // and they do care. Emacs and PuTTY, for example. + desiredMask |= KeyModifierControl | KeyModifierModeSwitch; + requiredMask |= KeyModifierControl | KeyModifierModeSwitch; } // handle combination of caps-lock and shift. if caps-lock is @@ -1427,7 +1437,7 @@ CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, // then see if it's a dead key. unsigned char uc = static_cast(c); if (CharLower((LPTSTR)uc) != CharUpper((LPTSTR)uc) || - (MapVirtualKey(virtualKey, 2) & 0x80000000lu) != 0) { + (MapVirtualKeyEx(virtualKey, 2, hkl) & 0x80000000lu) != 0) { LOG((CLOG_DEBUG2 "flip shift")); desiredMask ^= KeyModifierShift; } @@ -1453,6 +1463,7 @@ CMSWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, IKeyState::Keystrokes undo; if (!adjustModifiers(keys, undo, keyState, desiredMask, requiredMask)) { LOG((CLOG_DEBUG2 "failed to adjust modifiers")); + keys.clear(); return 0; } @@ -1493,3 +1504,93 @@ CMSWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, return true; } + +int +CMSWindowsKeyMapper::toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const +{ + // ignore bogus character + if (c == 0) { + return 0; + } + + // translate the character into its virtual key and its required + // modifier state. + SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); + + // get virtual key + BYTE virtualKey = LOBYTE(virtualKeyAndModifierState); + if (virtualKey == 0xffu) { + return 0; + } + + // get the required modifier state + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + + // set shift state required to generate key + BYTE keys[256]; + memset(keys, 0, sizeof(keys)); + if (modifierState & 0x01u) { + keys[VK_SHIFT] = 0x80u; + } + if (modifierState & 0x02u) { + keys[VK_CONTROL] = 0x80u; + } + if (modifierState & 0x04u) { + keys[VK_MENU] = 0x80u; + } + + // get the scan code for the key + UINT scanCode = MapVirtualKeyEx(virtualKey, 0, hkl); + + // discard characters if chars is NULL + WORD dummy; + if (chars == NULL) { + chars = &dummy; + } + + // put it back + return ToAsciiEx(virtualKey, scanCode, keys, chars, menu ? 1 : 0, hkl); +} + +bool +CMSWindowsKeyMapper::isDeadChar(TCHAR c, HKL hkl, bool menu) const +{ + // first clear out ToAsciiEx()'s internal buffer by sending it + // a space. + WORD ascii; + int old = toAscii(' ', hkl, 0, &ascii); + + // now pass the character of interest + WORD dummy; + bool isDead = (toAscii(c, hkl, menu, &dummy) < 0); + + // clear out internal buffer again + toAscii(' ', hkl, 0, &dummy); + + // put old dead key back if there was one + if (old == 2) { + toAscii(static_cast(ascii & 0xffu), hkl, menu, &dummy); + } + + return isDead; +} + +bool +CMSWindowsKeyMapper::putBackDeadChar(TCHAR c, HKL hkl, bool menu) const +{ + return (toAscii(c, hkl, menu, NULL) < 0); +} + +TCHAR +CMSWindowsKeyMapper::getSavedDeadChar(HKL hkl) const +{ + WORD old; + int nOld = toAscii(' ', hkl, false, &old); + if (nOld == 1 || nOld == 2) { + TCHAR c = static_cast(old & 0xffu); + if (nOld == 2 || isDeadChar(c, hkl, false)) { + return c; + } + } + return 0; +} diff --git a/lib/platform/CMSWindowsKeyMapper.h b/lib/platform/CMSWindowsKeyMapper.h index ae26dcdb..679e6fc7 100644 --- a/lib/platform/CMSWindowsKeyMapper.h +++ b/lib/platform/CMSWindowsKeyMapper.h @@ -121,6 +121,19 @@ private: KeyModifierMask desiredMask, KeyModifierMask requiredMask) const; + // pass character to ToAsciiEx(), returning what it returns + int toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const; + + // return true iff \c c is a dead character + bool isDeadChar(TCHAR c, HKL hkl, bool menu) const; + + // put back dead key into ToAscii() internal buffer. returns true + // iff the character was a dead key. + bool putBackDeadChar(TCHAR c, HKL hkl, bool menu) const; + + // get the dead key saved in the given keyboard layout, or 0 if none + TCHAR getSavedDeadChar(HKL hkl) const; + private: class CModifierKeys { public: @@ -130,6 +143,7 @@ private: }; BYTE m_keys[256]; + mutable TCHAR m_deadKey; static const CModifierKeys s_modifiers[]; static const char* s_vkToName[]; diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 7a272c46..f9e1a334 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -381,6 +381,7 @@ CMSWindowsScreen::enter() if (m_isPrimary) { // show the cursor showCursor(true); + ShowWindow(m_window, SW_HIDE); m_cursorThread = 0; // enable special key sequences on win95 family @@ -405,16 +406,18 @@ bool CMSWindowsScreen::leave() { if (m_isPrimary) { - // show window -/* XXX + // we don't need a window to capture input but we need a window + // to hide the cursor when using low-level hooks. also take the + // activation so we use our keyboard layout, not the layout of + // whatever window was active. if (m_lowLevel) { SetWindowPos(m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1, SWP_NOACTIVATE); - ShowWindow(m_window, SW_SHOWNA); + ShowWindow(m_window, SW_SHOW); } -*/ - // update keys + /* XXX + // update keys m_keyMapper.update(NULL); */ diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index 42c5a1d6..817c9dbf 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -239,12 +239,9 @@ CScreen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button) // modifier state. Keystrokes keys; KeyButton key = m_screen->mapKey(keys, *this, id, mask, false); - if (key == 0) { - LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); - return; - } if (keys.empty()) { // do nothing if there are no associated keys + LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); return; } From f4d7ea3d7bb4ffc8d59f76b836853bc66ce2b87e Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 7 Sep 2003 13:08:30 +0000 Subject: [PATCH 568/807] Fixed button mapping. Was returning the physical button instead of the logical button, but XTest wants the logical button. Apparently, the XTest implementation on my mac laptop has it backwards. --- lib/platform/CXWindowsScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 1dffd054..12b1d703 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -1740,7 +1740,7 @@ CXWindowsScreen::mapButtonToX(ButtonID id) const } // map button - return static_cast(m_buttons[id - 1]); + return static_cast(id); } void From 175843b4fb897626d35a606634dd5c64d3670344 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 8 Sep 2003 19:42:00 +0000 Subject: [PATCH 569/807] Changed server side of win32 to send ctrl+alt+ if the key doesn't map to anything via AltGr. This is an improvement over the previous code which would simply discard the key but it still behaves slightly differently than pressing ctrl+alt+ on the server: if that combination is a hotkey on the server then the hotkey is performed, even if the combination is also a valid AltGr combination; on the client, we'll get the AltGr combination in preference to the hotkey. --- lib/platform/CMSWindowsKeyMapper.cpp | 130 ++++++++++++++++----------- lib/platform/CMSWindowsKeyMapper.h | 6 ++ 2 files changed, 83 insertions(+), 53 deletions(-) diff --git a/lib/platform/CMSWindowsKeyMapper.cpp b/lib/platform/CMSWindowsKeyMapper.cpp index 6e47aa95..ac77cc95 100644 --- a/lib/platform/CMSWindowsKeyMapper.cpp +++ b/lib/platform/CMSWindowsKeyMapper.cpp @@ -1101,52 +1101,21 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM vkCode, LPARAM info, keys[VK_MENU] = 0x80; } - // get contents of keyboard layout buffer and clear out that - // buffer. we don't want anything placed there by some other - // app interfering and we need to put anything there back in - // place when we're done. - TCHAR oldDeadKey = getSavedDeadChar(hkl); - - // put our previous dead key, if any, in the layout buffer - putBackDeadChar(m_deadKey, hkl, false); - m_deadKey = 0; - - // process key - WORD ascii; + // map to a character bool isMenu = ((menu & 0x80) != 0); - int result = ToAsciiEx(vkCode, scanCode, keys, &ascii, - isMenu ? 1 : 0, hkl); + c = mapToCharacter(vkCode, scanCode, keys, press, isMenu, hkl); - // if result is less than zero then it was a dead key - if (result < 0) { - // save dead key if a key press. we catch the dead key - // release in the result == 2 case below. - if (press) { - m_deadKey = static_cast(ascii & 0xffu); - } - } - - // if result is 1 then the key was succesfully converted - else if (result == 1) { - c = static_cast(ascii & 0xff); - } - - // if result is 2 and the two characters are the same and this - // is a key release then a dead key was released. save the - // dead key. if the two characters are the same and this is - // not a release then a dead key was pressed twice. send the - // dead key. - else if (result == 2) { - if (((ascii & 0xff00u) >> 8) == (ascii & 0x00ffu)) { - if (!press) { - m_deadKey = static_cast(ascii & 0xffu); - } - else { - putBackDeadChar(oldDeadKey, hkl, false); - result = toAscii(' ', hkl, false, &ascii); - c = static_cast((ascii >> 8) & 0xffu); - } - } + // if mapping failed and ctrl and alt are pressed then try again + // with both not pressed. this handles the case where ctrl and + // alt are being used as individual modifiers rather than AltGr. + if (c == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) { + keys[VK_LCONTROL] = 0; + keys[VK_RCONTROL] = 0; + keys[VK_CONTROL] = 0; + keys[VK_LMENU] = 0; + keys[VK_RMENU] = 0; + keys[VK_MENU] = 0; + c = mapToCharacter(vkCode, scanCode, keys, press, isMenu, hkl); } // map character to key id @@ -1170,15 +1139,6 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM vkCode, LPARAM info, id = static_cast(c) & 0xffu; } } - - // clear keyboard layout buffer. this removes any dead key we - // may have just put there. - toAscii(' ', hkl, false, NULL); - - // restore keyboard layout buffer so a dead key inserted by - // another app doesn't disappear mysteriously (from its point - // of view). - putBackDeadChar(oldDeadKey, hkl, false); } // set mask @@ -1594,3 +1554,67 @@ CMSWindowsKeyMapper::getSavedDeadChar(HKL hkl) const } return 0; } + +char +CMSWindowsKeyMapper::mapToCharacter(UINT vkCode, UINT scanCode, + BYTE* keys, bool press, bool isMenu, HKL hkl) const +{ + // get contents of keyboard layout buffer and clear out that + // buffer. we don't want anything placed there by some other + // app interfering and we need to put anything there back in + // place when we're done. + TCHAR oldDeadKey = getSavedDeadChar(hkl); + + // put our previous dead key, if any, in the layout buffer + putBackDeadChar(m_deadKey, hkl, false); + m_deadKey = 0; + + // process key + WORD ascii; + int result = ToAsciiEx(vkCode, scanCode, keys, &ascii, + isMenu ? 1 : 0, hkl); + + // if result is less than zero then it was a dead key + char c = 0; + if (result < 0) { + // save dead key if a key press. we catch the dead key + // release in the result == 2 case below. + if (press) { + m_deadKey = static_cast(ascii & 0xffu); + } + } + + // if result is 1 then the key was succesfully converted + else if (result == 1) { + c = static_cast(ascii & 0xff); + } + + // if result is 2 and the two characters are the same and this + // is a key release then a dead key was released. save the + // dead key. if the two characters are the same and this is + // not a release then a dead key was pressed twice. send the + // dead key. + else if (result == 2) { + if (((ascii & 0xff00u) >> 8) == (ascii & 0x00ffu)) { + if (!press) { + m_deadKey = static_cast(ascii & 0xffu); + } + else { + putBackDeadChar(oldDeadKey, hkl, false); + result = toAscii(' ', hkl, false, &ascii); + c = static_cast((ascii >> 8) & 0xffu); + } + } + } + + // clear keyboard layout buffer. this removes any dead key we + // may have just put there. + toAscii(' ', hkl, false, NULL); + + // restore keyboard layout buffer so a dead key inserted by + // another app doesn't disappear mysteriously (from its point + // of view). + putBackDeadChar(oldDeadKey, hkl, false); + + return c; +} diff --git a/lib/platform/CMSWindowsKeyMapper.h b/lib/platform/CMSWindowsKeyMapper.h index 679e6fc7..5bcfcedf 100644 --- a/lib/platform/CMSWindowsKeyMapper.h +++ b/lib/platform/CMSWindowsKeyMapper.h @@ -134,6 +134,12 @@ private: // get the dead key saved in the given keyboard layout, or 0 if none TCHAR getSavedDeadChar(HKL hkl) const; + // map the given virtual key, scan code, and keyboard state to a + // character, if possible. this has the side effect of updating + // m_deadKey. + char mapToCharacter(UINT vkCode, UINT scanCode, + BYTE* keys, bool press, bool isMenu, HKL hkl) const; + private: class CModifierKeys { public: From fa215f1b13a10d676a6f41d78a778dafaae06bac Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Sep 2003 11:58:38 +0000 Subject: [PATCH 570/807] Merged changes from 1.0 branch, including Japanese keyboard support (thanks to Kazuhide Takahashi). --- lib/arch/CArchFileUnix.cpp | 3 +++ lib/platform/CMSWindowsKeyMapper.cpp | 4 ++-- lib/synergy/KeyTypes.h | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/arch/CArchFileUnix.cpp b/lib/arch/CArchFileUnix.cpp index a708cc8d..dc4cc3b8 100644 --- a/lib/arch/CArchFileUnix.cpp +++ b/lib/arch/CArchFileUnix.cpp @@ -56,6 +56,9 @@ CArchFileUnix::getUserDirectory() struct passwd* pwentp; #if defined(_SC_GETPW_R_SIZE_MAX) long size = sysconf(_SC_GETPW_R_SIZE_MAX); + if (size == -1) { + size = BUFSIZ; + } #else long size = BUFSIZ; #endif diff --git a/lib/platform/CMSWindowsKeyMapper.cpp b/lib/platform/CMSWindowsKeyMapper.cpp index ac77cc95..dbd981e2 100644 --- a/lib/platform/CMSWindowsKeyMapper.cpp +++ b/lib/platform/CMSWindowsKeyMapper.cpp @@ -345,7 +345,7 @@ const KeyID CMSWindowsKeyMapper::s_virtualKey[][2] = /* 0x16 */ kKeyNone, kKeyNone, // VK_HANGUL /* 0x17 */ kKeyNone, kKeyNone, // VK_JUNJA /* 0x18 */ kKeyNone, kKeyNone, // VK_FINAL - /* 0x19 */ kKeyNone, kKeyNone, // VK_KANJI + /* 0x19 */ kKeyZenkaku, kKeyNone, // VK_KANJI /* 0x1a */ kKeyNone, kKeyNone, // undefined /* 0x1b */ kKeyEscape, kKeyNone, // VK_ESCAPE /* 0x1c */ kKeyNone, kKeyNone, // VK_CONVERT @@ -668,7 +668,7 @@ const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = /* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0, /* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x28 */ 0, 0, VK_KANJI, 0, 0, 0, 0, 0, /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 9af4dd0b..687408be 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -86,6 +86,7 @@ static const KeyID kKeyPause = 0xEF13; /* Pause, hold */ static const KeyID kKeyScrollLock = 0xEF14; static const KeyID kKeySysReq = 0xEF15; static const KeyID kKeyEscape = 0xEF1B; +static const KeyID kKeyZenkaku = 0xEF2A; /* Zenkaku/Hankaku */ static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ // multi-key character composition From 4c7e5248969e481091d2d31f2997d72f8ce64868 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 24 Jan 2004 16:09:25 +0000 Subject: [PATCH 571/807] Checkpointing centralized event queue stuff. Currently have: an event queue and events, TCP sockets converted to use events, unix multithreading and network stuff converted, and an X Windows event queue subclass. --- lib/arch/CArch.cpp | 6 + lib/arch/CArch.h | 17 + lib/arch/CArchMultithreadPosix.cpp | 6 + lib/arch/CArchMultithreadPosix.h | 1 + lib/arch/CArchNetworkBSD.cpp | 19 +- lib/arch/IArchMultithread.h | 9 + lib/base/CEvent.cpp | 89 +++++ lib/base/CEvent.h | 100 ++++++ lib/base/CEventQueue.cpp | 381 +++++++++++++++++++++ lib/base/CEventQueue.h | 192 +++++++++++ lib/base/CFunctionEventJob.cpp | 40 +++ lib/base/CFunctionEventJob.h | 38 ++ lib/base/CLog.cpp | 22 +- lib/base/CPriorityQueue.h | 7 + lib/base/CSimpleEventQueue.cpp | 88 +++++ lib/base/CSimpleEventQueue.h | 60 ++++ lib/base/IEventJob.h | 32 ++ lib/base/IEventQueue.h | 142 ++++++++ lib/base/Makefile.am | 9 + lib/base/TMethodEventJob.h | 70 ++++ lib/io/CBufferedInputStream.cpp | 10 +- lib/io/CBufferedInputStream.h | 7 +- lib/io/CBufferedOutputStream.cpp | 11 +- lib/io/CBufferedOutputStream.h | 6 +- lib/mt/CThread.cpp | 6 + lib/mt/CThread.h | 9 + lib/net/CTCPListenSocket.cpp | 77 +++-- lib/net/CTCPListenSocket.h | 11 + lib/net/CTCPSocket.cpp | 495 ++++++++++++++++----------- lib/net/CTCPSocket.h | 46 ++- lib/net/IDataSocket.cpp | 55 +++ lib/net/IDataSocket.h | 50 +++ lib/net/IListenSocket.cpp | 27 ++ lib/net/IListenSocket.h | 22 +- lib/net/ISocket.cpp | 27 ++ lib/net/ISocket.h | 22 ++ lib/net/Makefile.am | 57 +-- lib/platform/CXWindowsEventQueue.cpp | 182 ++++++++++ lib/platform/CXWindowsEventQueue.h | 61 ++++ lib/platform/Makefile.am | 2 + 40 files changed, 2215 insertions(+), 296 deletions(-) create mode 100644 lib/base/CEvent.cpp create mode 100644 lib/base/CEvent.h create mode 100644 lib/base/CEventQueue.cpp create mode 100644 lib/base/CEventQueue.h create mode 100644 lib/base/CFunctionEventJob.cpp create mode 100644 lib/base/CFunctionEventJob.h create mode 100644 lib/base/CSimpleEventQueue.cpp create mode 100644 lib/base/CSimpleEventQueue.h create mode 100644 lib/base/IEventJob.h create mode 100644 lib/base/IEventQueue.h create mode 100644 lib/base/TMethodEventJob.h create mode 100644 lib/net/IDataSocket.cpp create mode 100644 lib/net/IListenSocket.cpp create mode 100644 lib/net/ISocket.cpp create mode 100644 lib/platform/CXWindowsEventQueue.cpp create mode 100644 lib/platform/CXWindowsEventQueue.h diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index 30bfb38c..d276c4ee 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -357,6 +357,12 @@ CArch::waitForEvent(CArchThread thread, double timeout) return m_mt->waitForEvent(thread, timeout); } +void +CArch::unblockThread(CArchThread thread) +{ + m_mt->unblockThread(thread); +} + bool CArch::isSameThread(CArchThread thread1, CArchThread thread2) { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index 61315b63..aad9fefb 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -117,6 +117,7 @@ public: virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); virtual EWaitResult waitForEvent(CArchThread, double timeout); + virtual void unblockThread(CArchThread thread); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); @@ -189,4 +190,20 @@ private: IArchTime* m_time; }; +//! Convenience object to lock/unlock an arch mutex +class CArchMutexLock { +public: + CArchMutexLock(CArchMutex mutex) : m_mutex(mutex) + { + ARCH->lockMutex(m_mutex); + } + ~CArchMutexLock() + { + ARCH->unlockMutex(m_mutex); + } + +private: + CArchMutex m_mutex; +}; + #endif diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 67e5fcca..707ef0fb 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -522,6 +522,12 @@ CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/) return kTimeout; } +void +CArchMultithreadPosix::unblockThread(CArchThread thread) +{ + pthread_kill(thread->m_thread, SIGWAKEUP); +} + bool CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2) { diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h index d62e2d4d..b0a1ca36 100644 --- a/lib/arch/CArchMultithreadPosix.h +++ b/lib/arch/CArchMultithreadPosix.h @@ -56,6 +56,7 @@ public: virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); virtual EWaitResult waitForEvent(CArchThread, double timeout); + virtual void unblockThread(CArchThread thread); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 9bd3c138..7de3db05 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -200,11 +200,6 @@ CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr) ARCH->testCancelThread(); continue; } - if (err == ECONNABORTED) { - // connection was aborted; try again - ARCH->testCancelThread(); - continue; - } delete newSocket; delete *addr; *addr = NULL; @@ -244,11 +239,6 @@ CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr) break; } - if (errno == EAGAIN) { - // connecting - throw XArchNetworkConnecting(new XArchEvalUnix(errno)); - } - throwError(errno); } } while (false); @@ -290,14 +280,15 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) } // do the poll + int t = (timeout < 0.0) ? -1 : static_cast(1000.0 * timeout); int n; do { - n = poll(pfd, num, static_cast(1000.0 * timeout)); + n = poll(pfd, num, t); if (n == -1) { if (errno == EINTR) { // interrupted system call ARCH->testCancelThread(); - continue; + return 0; } throwError(errno); } @@ -382,7 +373,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) // prepare timeout for select struct timeval timeout2; struct timeval* timeout2P; - if (timeout < 0) { + if (timeout < 0.0) { timeout2P = NULL; } else { @@ -404,7 +395,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) if (errno == EINTR) { // interrupted system call ARCH->testCancelThread(); - continue; + return 0; } throwError(errno); } diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index 69d88694..d8319213 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -228,6 +228,15 @@ public: */ virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0; + //! Unblock thread in system call + /*! + Cause a thread that's in a blocking system call to return. This + call may return before the thread is unblocked. If the thread is + not in a blocking system call, this call has no effect. This does + not cause a lockMutex() or waitCondVar() to return prematurely. + */ + virtual void unblockThread(CArchThread thread) = 0; + //! Compare threads /*! Returns true iff two thread objects refer to the same thread. diff --git a/lib/base/CEvent.cpp b/lib/base/CEvent.cpp new file mode 100644 index 00000000..36f4f674 --- /dev/null +++ b/lib/base/CEvent.cpp @@ -0,0 +1,89 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CEvent.h" + +// +// CEvent +// + +CEvent::Type CEvent::s_nextType = kLast; + +CEvent::CEvent() : + m_type(kUnknown), + m_target(NULL), + m_data(NULL) +{ + // do nothing +} + +CEvent::CEvent(Type type, void* target, void* data) : + m_type(type), + m_target(target), + m_data(data) +{ + // do nothing +} + +CEvent::Type +CEvent::getType() const +{ + return m_type; +} + +void* +CEvent::getTarget() const +{ + return m_target; +} + +void* +CEvent::getData() const +{ + return m_data; +} + +CEvent::Type +CEvent::registerType() +{ + // FIXME -- lock mutex (need a mutex) + return s_nextType++; +} + +CEvent::Type +CEvent::registerTypeOnce(Type& type) +{ + // FIXME -- lock mutex (need a mutex) + if (type == CEvent::kUnknown) { + type = s_nextType++; + } + return type; +} + +void +CEvent::deleteData(const CEvent& event) +{ + switch (event.getType()) { + case kUnknown: + case kQuit: + case kSystem: + case kTimer: + break; + + default: + // yes, really delete void* + delete event.getData(); + break; + } +} diff --git a/lib/base/CEvent.h b/lib/base/CEvent.h new file mode 100644 index 00000000..8257e603 --- /dev/null +++ b/lib/base/CEvent.h @@ -0,0 +1,100 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CEVENT_H +#define CEVENT_H + +#include "BasicTypes.h" + +//! Event +/*! +A \c CEvent holds an event type and a pointer to event data. +*/ +class CEvent { +public: + typedef UInt32 Type; + enum { + kUnknown, //!< The event type is unknown + kQuit, //!< The quit event + kSystem, //!< The data points to a system event type + kTimer, //!< The data points to timer info + kLast //!< Must be last + }; + + CEvent(); + + //! Create \c CEvent with data + /*! + The \p type must have been registered using \c registerType(). + The \p data must be POD (plain old data) which means it cannot + have a destructor or be composed of any types that do. \p target + is the intended recipient of the event. + */ + CEvent(Type type, void* target = NULL, void* data = NULL); + + //! @name manipulators + //@{ + + //@} + //! @name accessors + //@{ + + //! Get event type + /*! + Returns the event type. + */ + Type getType() const; + + //! Get the event target + /*! + Returns the event target. + */ + void* getTarget() const; + + //! Get the event data + /*! + Returns the event data. + */ + void* getData() const; + + //! Creates a new event type + /*! + Returns a unique event type id. + */ + static Type registerType(); + + //! Creates a new event type + /*! + If \p type contains \c kUnknown then it is set to a unique event + type id otherwise it is left alone. The final value of \p type + is returned. + */ + static Type registerTypeOnce(Type& type); + + //! Release event data + /*! + Deletes event data for the given event. + */ + static void deleteData(const CEvent&); + + //@} + +private: + Type m_type; + void* m_target; + void* m_data; + static Type s_nextType; +}; + +#endif diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp new file mode 100644 index 00000000..ce8c0058 --- /dev/null +++ b/lib/base/CEventQueue.cpp @@ -0,0 +1,381 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CEventQueue.h" +#include "IEventJob.h" +#include "CArch.h" + +// +// CEventQueue +// + +static int g_systemTarget = 0; +CEventQueue* CEventQueue::s_instance = NULL; + +CEventQueue::CEventQueue() +{ + assert(s_instance == NULL); + s_instance = this; + m_mutex = ARCH->newMutex(); +} + +CEventQueue::~CEventQueue() +{ + ARCH->closeMutex(m_mutex); + s_instance = NULL; +} + +void* +CEventQueue::getSystemTarget() +{ + // any unique arbitrary pointer will do + return &g_systemTarget; +} + +CEventQueue* +CEventQueue::getInstance() +{ + return s_instance; +} + +bool +CEventQueue::getEvent(CEvent& event, double timeout) +{ + // if no events are waiting then handle timers and then wait + if (doIsEmpty()) { + // handle timers first + if (hasTimerExpired(event)) { + return true; + } + + // get time until next timer expires. if there is a timer + // and it'll expire before the client's timeout then use + // that duration for our timeout instead. + double timerTimeout = getNextTimerTimeout(); + if (timerTimeout >= 0.0 && timerTimeout < timeout) { + timeout = timerTimeout; + } + + // wait for an event + waitForEvent(timeout); + } + + // if no events are pending then do the timers + if (doIsEmpty()) { + return hasTimerExpired(event); + } + + return doGetEvent(event); +} + +bool +CEventQueue::dispatchEvent(const CEvent& event) +{ + void* target = event.getTarget(); + IEventJob* job = getHandler(target); + if (job != NULL) { + job->run(event); + return true; + } + return false; +} + +void +CEventQueue::addEvent(const CEvent& event) +{ + // discard bogus event types + switch (event.getType()) { + case CEvent::kUnknown: + case CEvent::kSystem: + case CEvent::kTimer: + return; + + default: + break; + } + + // store the event's data locally + UInt32 eventID = saveEvent(event); + + // add it + if (!doAddEvent(eventID)) { + // failed to send event + removeEvent(eventID); + CEvent::deleteData(event); + } +} + +CEventQueueTimer* +CEventQueue::newTimer(double duration, void* target) +{ + assert(duration > 0.0); + + CEventQueueTimer* timer = doNewTimer(duration, false); + CArchMutexLock lock(m_mutex); + m_timers.insert(timer); + m_timerQueue.push(CTimer(timer, duration, target, false)); + return timer; +} + +CEventQueueTimer* +CEventQueue::newOneShotTimer(double duration, void* target) +{ + assert(duration > 0.0); + + CEventQueueTimer* timer = doNewTimer(duration, true); + CArchMutexLock lock(m_mutex); + m_timers.insert(timer); + m_timerQueue.push(CTimer(timer, duration, target, true)); + return timer; +} + +void +CEventQueue::deleteTimer(CEventQueueTimer* timer) +{ + { + CArchMutexLock lock(m_mutex); + for (CTimerQueue::iterator index = m_timerQueue.begin(); + index != m_timerQueue.end(); ++index) { + if (index->getTimer() == timer) { + m_timerQueue.erase(index); + break; + } + } + CTimers::iterator index = m_timers.find(timer); + if (index != m_timers.end()) { + m_timers.erase(index); + } + } + doDeleteTimer(timer); +} + +void +CEventQueue::adoptHandler(void* target, IEventJob* handler) +{ + CArchMutexLock lock(m_mutex); + IEventJob*& job = m_handlers[target]; + delete job; + job = handler; +} + +IEventJob* +CEventQueue::orphanHandler(void* target) +{ + CArchMutexLock lock(m_mutex); + CHandlerTable::iterator index = m_handlers.find(target); + if (index != m_handlers.end()) { + IEventJob* handler = index->second; + m_handlers.erase(index); + return handler; + } + else { + return NULL; + } +} + +bool +CEventQueue::isEmpty() const +{ + return (doIsEmpty() && getNextTimerTimeout() != 0.0); +} + +IEventJob* +CEventQueue::getHandler(void* target) const +{ + CArchMutexLock lock(m_mutex); + CHandlerTable::const_iterator index = m_handlers.find(target); + if (index != m_handlers.end()) { + return index->second; + } + else { + return NULL; + } +} + +UInt32 +CEventQueue::saveEvent(const CEvent& event) +{ + CArchMutexLock lock(m_mutex); + + // choose id + UInt32 id; + if (!m_oldEventIDs.empty()) { + // reuse an id + id = m_oldEventIDs.back(); + m_oldEventIDs.pop_back(); + } + else { + // make a new id + id = static_cast(m_oldEventIDs.size()); + } + + // save data + m_events[id] = event; + return id; +} + +CEvent +CEventQueue::removeEvent(UInt32 eventID) +{ + CArchMutexLock lock(m_mutex); + + // look up id + CEventTable::iterator index = m_events.find(eventID); + if (index == m_events.end()) { + return CEvent(); + } + + // get data + CEvent event = index->second; + m_events.erase(index); + + // save old id for reuse + m_oldEventIDs.push_back(eventID); + + return event; +} + +bool +CEventQueue::hasTimerExpired(CEvent& event) +{ + CArchMutexLock lock(m_mutex); + + // return true if there's a timer in the timer priority queue that + // has expired. if returning true then fill in event appropriately + // and reset and reinsert the timer. + if (m_timerQueue.empty()) { + return false; + } + + // get time elapsed since last check + const double time = m_time.getTime(); + m_time.reset(); + + // countdown elapsed time + for (CTimerQueue::iterator index = m_timerQueue.begin(); + index != m_timerQueue.end(); ++index) { + (*index) -= time; + } + + // done if no timers are expired + if (m_timerQueue.top() > 0.0) { + return false; + } + + // remove timer from queue + CTimer timer = m_timerQueue.top(); + m_timerQueue.pop(); + + // prepare event and reset the timer's clock + timer.fillEvent(m_timerEvent); + event = CEvent(CEvent::kTimer, timer.getTarget(), &m_timerEvent); + timer.reset(); + + // reinsert timer into queue if it's not a one-shot + if (!timer.isOneShot()) { + m_timerQueue.push(timer); + } + + return true; +} + +double +CEventQueue::getNextTimerTimeout() const +{ + CArchMutexLock lock(m_mutex); + + // return -1 if no timers, 0 if the top timer has expired, otherwise + // the time until the top timer in the timer priority queue will + // expire. + if (m_timerQueue.empty()) { + return -1.0; + } + if (m_timerQueue.top() <= 0.0) { + return 0.0; + } + return m_timerQueue.top(); +} + + +// +// CXWindowsScreen::CTimer +// + +CEventQueue::CTimer::CTimer(CEventQueueTimer* timer, + double timeout, void* target, bool oneShot) : + m_timer(timer), + m_timeout(timeout), + m_target(target), + m_oneShot(oneShot), + m_time(timeout) +{ + assert(m_timeout > 0.0); +} + +CEventQueue::CTimer::~CTimer() +{ + // do nothing +} + +void +CEventQueue::CTimer::reset() +{ + m_time = m_timeout; +} + +CEventQueue::CTimer::CTimer& +CEventQueue::CTimer::operator-=(double dt) +{ + m_time -= dt; + return *this; +} + +CEventQueue::CTimer::operator double() const +{ + return m_time; +} + +bool +CEventQueue::CTimer::isOneShot() const +{ + return m_oneShot; +} + +CEventQueueTimer* +CEventQueue::CTimer::getTimer() const +{ + return m_timer; +} + +void* +CEventQueue::CTimer::getTarget() const +{ + return m_target; +} + +void +CEventQueue::CTimer::fillEvent(CTimerEvent& event) const +{ + event.m_timer = m_timer; + event.m_count = 0; + if (m_time <= 0.0) { + event.m_count = static_cast((m_timeout - m_time) / m_timeout); + } +} + +bool +CEventQueue::CTimer::operator<(const CTimer& t) const +{ + return m_time < t.m_time; +} diff --git a/lib/base/CEventQueue.h b/lib/base/CEventQueue.h new file mode 100644 index 00000000..e6ac9ee2 --- /dev/null +++ b/lib/base/CEventQueue.h @@ -0,0 +1,192 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CEVENTQUEUE_H +#define CEVENTQUEUE_H + +#include "IEventQueue.h" +#include "CEvent.h" +#include "CPriorityQueue.h" +#include "CStopwatch.h" +#include "IArchMultithread.h" +#include "stdmap.h" +#include "stdset.h" + +//! Event queue +/*! +An event queue that implements the platform independent parts and +delegates the platform dependent parts to a subclass. +*/ +class CEventQueue : public IEventQueue { +public: + CEventQueue(); + virtual ~CEventQueue(); + + //! @name manipulators + //@{ + + //@} + //! @name accessors + //@{ + + //! Get the system event type target + /*! + Returns the target to use for dispatching \c CEvent::kSystem events. + */ + static void* getSystemTarget(); + + //! Get the singleton instance + /*! + Returns the singleton instance of the event queue + */ + static CEventQueue* getInstance(); + + //@} + + // IEventQueue overrides + virtual bool getEvent(CEvent& event, double timeout = -1.0); + virtual bool dispatchEvent(const CEvent& event); + virtual void addEvent(const CEvent& event); + virtual CEventQueueTimer* + newTimer(double duration, void* target = NULL); + virtual CEventQueueTimer* + newOneShotTimer(double duration, void* target = NULL); + virtual void deleteTimer(CEventQueueTimer*); + virtual void adoptHandler(void* target, IEventJob* dispatcher); + virtual IEventJob* orphanHandler(void* target); + virtual bool isEmpty() const; + virtual IEventJob* getHandler(void* target) const; + +protected: + //! @name manipulators + //@{ + + //! Get the data for a given id + /*! + Takes a saved event id, \p eventID, and returns a \c CEvent. The + event id becomes invalid after this call. The \p eventID must have + been passed to a successful call to \c doAddEvent() and not removed + since. + */ + CEvent removeEvent(UInt32 eventID); + + //! Block waiting for an event + /*! + Wait for an event in the system event queue for up to \p timeout + seconds. + */ + virtual void waitForEvent(double timeout) = 0; + + //! Get the next event + /*! + Remove the next system event (one should be pending) and convert it + to a \c CEvent. The event type should be either \c CEvent::kSystem + if the event was not added by \c doAddEvent() or a type returned by + \c CEvent::registerType() if it was (and not \c CEvent::kTimer). A + non-system event will normally be retrieved by \c removeEvent(), but + the implementation must be able to tell the difference between a + system event and one added by \c doAddEvent(). + */ + virtual bool doGetEvent(CEvent& event) = 0; + + //! Post an event + /*! + Add the given event to the end of the system queue. This is a user + event and \c doGetEvent() must be able to identify it as such. + This method must cause \c waitForEvent() to return at some future + time if it's blocked waiting on an event. + */ + virtual bool doAddEvent(UInt32 dataID) = 0; + + //@} + //! @name accessors + //@{ + + //! Check if system queue is empty + /*! + Return true iff the system queue is empty. + */ + virtual bool doIsEmpty() const = 0; + + //! Create a timer object + /*! + Create and return a timer object. The object is opaque and is + used only by the subclass but it must be a valid object (i.e. + not NULL). + */ + virtual CEventQueueTimer* + doNewTimer(double duration, bool oneShot) const = 0; + + //! Destroy a timer object + /*! + Destroy a timer object previously returned by \c doNewTimer(). + */ + virtual void doDeleteTimer(CEventQueueTimer*) const = 0; + + //@} + +private: + UInt32 saveEvent(const CEvent& event); + bool hasTimerExpired(CEvent& event); + double getNextTimerTimeout() const; + +private: + class CTimer { + public: + CTimer(CEventQueueTimer*, double timeout, void* target, bool oneShot); + ~CTimer(); + + void reset(); + + CTimer& operator-=(double); + + operator double() const; + + bool isOneShot() const; + CEventQueueTimer* + getTimer() const; + void* getTarget() const; + void fillEvent(CTimerEvent&) const; + + bool operator<(const CTimer&) const; + + private: + CEventQueueTimer* m_timer; + double m_timeout; + void* m_target; + bool m_oneShot; + double m_time; + }; + typedef std::set CTimers; + typedef CPriorityQueue CTimerQueue; + typedef std::map CEventTable; + typedef std::vector CEventIDList; + typedef std::map CHandlerTable; + + static CEventQueue* s_instance; + + CArchMutex m_mutex; + + CEventTable m_events; + CEventIDList m_oldEventIDs; + + CStopwatch m_time; + CTimers m_timers; + CTimerQueue m_timerQueue; + CTimerEvent m_timerEvent; + + CHandlerTable m_handlers; +}; + +#endif diff --git a/lib/base/CFunctionEventJob.cpp b/lib/base/CFunctionEventJob.cpp new file mode 100644 index 00000000..1a55aa00 --- /dev/null +++ b/lib/base/CFunctionEventJob.cpp @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CFunctionEventJob.h" + +// +// CFunctionJob +// + +CFunctionEventJob::CFunctionEventJob( + void (*func)(const CEvent&, void*), void* arg) : + m_func(func), + m_arg(arg) +{ + // do nothing +} + +CFunctionEventJob::~CFunctionEventJob() +{ + // do nothing +} + +void +CFunctionEventJob::run(const CEvent& event) +{ + if (m_func != NULL) { + m_func(event, m_arg); + } +} diff --git a/lib/base/CFunctionEventJob.h b/lib/base/CFunctionEventJob.h new file mode 100644 index 00000000..517b9c45 --- /dev/null +++ b/lib/base/CFunctionEventJob.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CFUNCTIONEVENTJOB_H +#define CFUNCTIONEVENTJOB_H + +#include "IEventJob.h" + +//! Use a function as an event job +/*! +An event job class that invokes a function. +*/ +class CFunctionEventJob : public IEventJob { +public: + //! run() invokes \c func(arg) + CFunctionEventJob(void (*func)(const CEvent&, void*), void* arg = NULL); + virtual ~CFunctionEventJob(); + + // IEventJob overrides + virtual void run(const CEvent&); + +private: + void (*m_func)(const CEvent&, void*); + void* m_arg; +}; + +#endif diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index 4df3058b..f99d90dd 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -54,16 +54,6 @@ static const int g_prioritySuffixLength = 2; static const int g_priorityPad = g_maxPriorityLength + g_prioritySuffixLength; -//! Convenience object to lock/unlock a mutex -class CLogLock { -public: - CLogLock(CArchMutex mutex) : m_mutex(mutex) { ARCH->lockMutex(m_mutex); } - ~CLogLock() { ARCH->unlockMutex(m_mutex); } - -private: - CArchMutex m_mutex; -}; - // // CLog @@ -198,7 +188,7 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) assert(outputter != NULL); assert(outputter->getNewline() != NULL); - CLogLock lock(m_mutex); + CArchMutexLock lock(m_mutex); if (alwaysAtHead) { m_alwaysOutputters.push_front(outputter); } @@ -214,7 +204,7 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) void CLog::remove(ILogOutputter* outputter) { - CLogLock lock(m_mutex); + CArchMutexLock lock(m_mutex); m_outputters.remove(outputter); m_alwaysOutputters.remove(outputter); } @@ -222,7 +212,7 @@ CLog::remove(ILogOutputter* outputter) void CLog::pop_front(bool alwaysAtHead) { - CLogLock lock(m_mutex); + CArchMutexLock lock(m_mutex); COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters; if (!list->empty()) { delete list->front(); @@ -248,14 +238,14 @@ CLog::setFilter(const char* maxPriority) void CLog::setFilter(int maxPriority) { - CLogLock lock(m_mutex); + CArchMutexLock lock(m_mutex); m_maxPriority = maxPriority; } int CLog::getFilter() const { - CLogLock lock(m_mutex); + CArchMutexLock lock(m_mutex); return m_maxPriority; } @@ -276,7 +266,7 @@ CLog::output(int priority, char* msg) const } // write to each outputter - CLogLock lock(m_mutex); + CArchMutexLock lock(m_mutex); for (COutputterList::const_iterator index = m_alwaysOutputters.begin(); index != m_alwaysOutputters.end(); ++index) { diff --git a/lib/base/CPriorityQueue.h b/lib/base/CPriorityQueue.h index a03024b1..c79c7e34 100644 --- a/lib/base/CPriorityQueue.h +++ b/lib/base/CPriorityQueue.h @@ -56,6 +56,13 @@ public: c.pop_back(); } + //! Erase element + void erase(iterator i) + { + c.erase(i); + std::make_heap(c.begin(), c.end(), comp); + } + //! Get start iterator iterator begin() { diff --git a/lib/base/CSimpleEventQueue.cpp b/lib/base/CSimpleEventQueue.cpp new file mode 100644 index 00000000..9fc7e616 --- /dev/null +++ b/lib/base/CSimpleEventQueue.cpp @@ -0,0 +1,88 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CSimpleEventQueue.h" +#include "CArch.h" + +class CEventQueueTimer { }; + +// +// CSimpleEventQueue +// + +CSimpleEventQueue::CSimpleEventQueue() +{ + m_queueMutex = ARCH->newMutex(); + m_queueReadyCond = ARCH->newCondVar(); + m_queueReady = false; +} + +CSimpleEventQueue::~CSimpleEventQueue() +{ + ARCH->closeCondVar(m_queueReadyCond); + ARCH->closeMutex(m_queueMutex); +} + +void +CSimpleEventQueue::waitForEvent(double timeout) +{ + CArchMutexLock lock(m_queueMutex); + while (!m_queueReady) { + ARCH->waitCondVar(m_queueReadyCond, m_queueMutex, -1.0); + } +} + +bool +CSimpleEventQueue::doGetEvent(CEvent& event) +{ + CArchMutexLock lock(m_queueMutex); + if (!m_queueReady) { + return false; + } + event = removeEvent(m_queue.back()); + m_queue.pop_back(); + m_queueReady = !m_queue.empty(); + return true; +} + +bool +CSimpleEventQueue::doAddEvent(UInt32 dataID) +{ + CArchMutexLock lock(m_queueMutex); + m_queue.push_front(dataID); + if (!m_queueReady) { + m_queueReady = true; + ARCH->broadcastCondVar(m_queueReadyCond); + } + return true; +} + +bool +CSimpleEventQueue::doIsEmpty() const +{ + CArchMutexLock lock(m_queueMutex); + return !m_queueReady; +} + +CEventQueueTimer* +CSimpleEventQueue::doNewTimer(double, bool) const +{ + return new CEventQueueTimer; +} + +void +CSimpleEventQueue::doDeleteTimer(CEventQueueTimer* timer) const +{ + delete timer; +} diff --git a/lib/base/CSimpleEventQueue.h b/lib/base/CSimpleEventQueue.h new file mode 100644 index 00000000..35ad4ecc --- /dev/null +++ b/lib/base/CSimpleEventQueue.h @@ -0,0 +1,60 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSIMPLEEVENTQUEUE_H +#define CSIMPLEEVENTQUEUE_H + +#include "CEventQueue.h" +#include "IArchMultithread.h" +#include "stddeque.h" + +//! Event queue for added events only +/*! +An event queue that provides no system events, just events added by +addEvent(). +*/ +class CSimpleEventQueue : public CEventQueue { +public: + CSimpleEventQueue(); + virtual ~CSimpleEventQueue(); + + //! @name manipulators + //@{ + + //@} + //! @name accessors + //@{ + + //@} + +protected: + // CEventQueue overrides + virtual void waitForEvent(double timeout); + virtual bool doGetEvent(CEvent& event); + virtual bool doAddEvent(UInt32 dataID); + virtual bool doIsEmpty() const; + virtual CEventQueueTimer* + doNewTimer(double duration, bool oneShot) const; + virtual void doDeleteTimer(CEventQueueTimer*) const; + +private: + typedef std::deque CEventDeque; + + CArchMutex m_queueMutex; + CArchCond m_queueReadyCond; + bool m_queueReady; + CEventDeque m_queue; +}; + +#endif diff --git a/lib/base/IEventJob.h b/lib/base/IEventJob.h new file mode 100644 index 00000000..01ef9a96 --- /dev/null +++ b/lib/base/IEventJob.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IEVENTJOB_H +#define IEVENTJOB_H + +#include "IInterface.h" + +class CEvent; + +//! Event handler interface +/*! +An event job is an interface for executing a event handler. +*/ +class IEventJob : public IInterface { +public: + //! Run the job + virtual void run(const CEvent&) = 0; +}; + +#endif diff --git a/lib/base/IEventQueue.h b/lib/base/IEventQueue.h new file mode 100644 index 00000000..aaaf9f33 --- /dev/null +++ b/lib/base/IEventQueue.h @@ -0,0 +1,142 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IEVENTQUEUE_H +#define IEVENTQUEUE_H + +#include "IInterface.h" +#include "BasicTypes.h" + +class CEvent; +class IEventJob; + +// Opaque type for timer info. This is defined by subclasses of +// IEventQueue. +class CEventQueueTimer; + +//! Event queue interface +/*! +An event queue provides a queue of CEvents. Clients can block waiting +on any event becoming available at the head of the queue and can place +new events at the end of the queue. Clients can also add and remove +timers which generate events periodically. +*/ +class IEventQueue : public IInterface { +public: + class CTimerEvent { + public: + CEventQueueTimer* m_timer; //!< The timer + UInt32 m_count; //!< Number of repeats + }; + + //! @name manipulators + //@{ + + //! Remove event from queue + /*! + Returns the next event on the queue into \p event. If no event is + available then blocks for up to \p timeout seconds, or forever if + \p timeout is negative. Returns true iff an event was available. + */ + virtual bool getEvent(CEvent& event, double timeout = -1.0) = 0; + + //! Dispatch an event + /*! + Looks up the dispatcher for the event's target and invokes it. + Returns true iff a dispatcher exists for the target. + */ + virtual bool dispatchEvent(const CEvent& event) = 0; + + //! Add event to queue + /*! + Adds \p event to the end of the queue. + */ + virtual void addEvent(const CEvent& event) = 0; + + //! Create a recurring timer + /*! + Creates and returns a timer. An event is returned after \p duration + seconds and the timer is reset to countdown again. When a timer event + is returned the data points to a \c CTimerEvent. The client must pass + the returned timer to \c deleteTimer() (whether or not the timer has + expired) to release the timer. The returned timer event uses the + given \p target. + + Events for a single timer don't accumulate in the queue, even if the + client reading events can't keep up. Instead, the \c m_count member + of the \c CTimerEvent indicates how many events for the timer would + have been put on the queue since the last event for the timer was + removed (or since the timer was added). + */ + virtual CEventQueueTimer* + newTimer(double duration, void* target = NULL) = 0; + + //! Create a one-shot timer + /*! + Creates and returns a one-shot timer. An event is returned when + the timer expires and the timer is removed from further handling. + When a timer event is returned the data points to a \c CTimerEvent. + The \m c_count member of the \c CTimerEvent is always 1. The client + must pass the returned timer to \c deleteTimer() (whether or not the + timer has expired) to release the timer. The returned timer event + uses the given \p target. + */ + virtual CEventQueueTimer* + newOneShotTimer(double duration, + void* target = NULL) = 0; + + //! Destroy a timer + /*! + Destroys a previously created timer. The timer is removed from the + queue and will not generate event, even if the timer has expired. + */ + virtual void deleteTimer(CEventQueueTimer*) = 0; + + //! Register an event handler + /*! + Registers an event handler for \p target. The \p handler is + adopted. Any existing handler for the target is deleted. + */ + virtual void adoptHandler(void* target, IEventJob* handler) = 0; + + //! Unregister an event handler + /*! + Unregisters an event handler for \p target and returns it. + Returns NULL if there was no such handler. The client becomes + responsible for deleting the returned handler. + */ + virtual IEventJob* orphanHandler(void* target) = 0; + + //@} + //! @name accessors + //@{ + + //! Test if queue is empty + /*! + Returns true iff the queue has no events in it, including timer + events. + */ + virtual bool isEmpty() const = 0; + + //! Get an event handler + /*! + Finds and returns the event handler for \p target, or NULL if + there is no such handler. + */ + virtual IEventJob* getHandler(void* target) const = 0; + + //@} +}; + +#endif diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index f9006ab2..db17a892 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -25,25 +25,34 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libbase.a libbase_a_SOURCES = \ + CEvent.cpp \ + CEventQueue.cpp \ + CFunctionEventJob.cpp \ CFunctionJob.cpp \ CJobList.cpp \ CLog.cpp \ + CSimpleEventQueue.cpp \ CStopwatch.cpp \ CStringUtil.cpp \ CUnicode.cpp \ LogOutputters.cpp \ XBase.cpp \ + CEvent.h \ + CEventQueue.h \ CFunctionJob.h \ CJobList.h \ CLog.h \ CPriorityQueue.h \ + CSimpleEventQueue.h \ CStopwatch.h \ CString.h \ CStringUtil.h \ CUnicode.h \ + IEventQueue.h \ IJob.h \ ILogOutputter.h \ LogOutputters.h \ + TMethodEventJob.h \ TMethodJob.h \ XBase.h \ $(NULL) diff --git a/lib/base/TMethodEventJob.h b/lib/base/TMethodEventJob.h new file mode 100644 index 00000000..15826be0 --- /dev/null +++ b/lib/base/TMethodEventJob.h @@ -0,0 +1,70 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMETHODEVENTJOB_H +#define CMETHODEVENTJOB_H + +#include "IEventJob.h" + +//! Use a member function as an event job +/*! +An event job class that invokes a member function. +*/ +template +class TMethodEventJob : public IEventJob { +public: + //! run(event) invokes \c object->method(event, arg) + TMethodEventJob(T* object, + void (T::*method)(const CEvent&, void*), + void* arg = NULL); + virtual ~TMethodEventJob(); + + // IJob overrides + virtual void run(const CEvent&); + +private: + T* m_object; + void (T::*m_method)(const CEvent&, void*); + void* m_arg; +}; + +template +inline +TMethodEventJob::TMethodEventJob(T* object, + void (T::*method)(const CEvent&, void*), void* arg) : + m_object(object), + m_method(method), + m_arg(arg) +{ + // do nothing +} + +template +inline +TMethodEventJob::~TMethodEventJob() +{ + // do nothing +} + +template +inline +void +TMethodEventJob::run(const CEvent& event) +{ + if (m_object != NULL) { + (m_object->*m_method)(event, m_arg); + } +} + +#endif diff --git a/lib/io/CBufferedInputStream.cpp b/lib/io/CBufferedInputStream.cpp index 27b7cb6f..87cf78a3 100644 --- a/lib/io/CBufferedInputStream.cpp +++ b/lib/io/CBufferedInputStream.cpp @@ -26,9 +26,10 @@ // CBufferedInputStream::CBufferedInputStream( - CMutex* mutex, IJob* adoptedCloseCB) : + CMutex* mutex, IJob* adoptedEmptyCB, IJob* adoptedCloseCB) : m_mutex(mutex), m_empty(mutex, true), + m_emptyCB(adoptedEmptyCB), m_closeCB(adoptedCloseCB), m_closed(false), m_hungup(false) @@ -39,6 +40,7 @@ CBufferedInputStream::CBufferedInputStream( CBufferedInputStream::~CBufferedInputStream() { delete m_closeCB; + delete m_emptyCB; } void @@ -64,6 +66,9 @@ CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout) if (m_closed) { throw XIOClosed(); } + if (n == 0) { + return n; + } // wait for data, hangup, or timeout CStopwatch timer(true); @@ -90,6 +95,9 @@ CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout) if (m_buffer.getSize() == 0) { m_empty = true; m_empty.broadcast(); + if (m_emptyCB != NULL) { + m_emptyCB->run(); + } } return n; } diff --git a/lib/io/CBufferedInputStream.h b/lib/io/CBufferedInputStream.h index 132d172b..01137563 100644 --- a/lib/io/CBufferedInputStream.h +++ b/lib/io/CBufferedInputStream.h @@ -36,8 +36,12 @@ public: The \c mutex must not be NULL and will be used to ensure thread safe access. If \c adoptedCloseCB is not NULL it will be called when close() is called, allowing the creator to detect the close. + If adoptedEmptyCB is not NULL, it will be called whenever the + buffer becomes empty (except it won't be called by the c'tor nor + when the buffer is closed). */ - CBufferedInputStream(CMutex* mutex, IJob* adoptedCloseCB); + CBufferedInputStream(CMutex* mutex, + IJob* adoptedEmptyCB, IJob* adoptedCloseCB); ~CBufferedInputStream(); //! @name manipulators @@ -87,6 +91,7 @@ public: private: CMutex* m_mutex; CCondVar m_empty; + IJob* m_emptyCB; IJob* m_closeCB; CStreamBuffer m_buffer; bool m_closed; diff --git a/lib/io/CBufferedOutputStream.cpp b/lib/io/CBufferedOutputStream.cpp index 3406232c..2b666d1f 100644 --- a/lib/io/CBufferedOutputStream.cpp +++ b/lib/io/CBufferedOutputStream.cpp @@ -24,8 +24,9 @@ // CBufferedOutputStream::CBufferedOutputStream( - CMutex* mutex, IJob* adoptedCloseCB) : + CMutex* mutex, IJob* adoptedFillCB, IJob* adoptedCloseCB) : m_mutex(mutex), + m_fillCB(adoptedFillCB), m_closeCB(adoptedCloseCB), m_empty(mutex, true), m_closed(false) @@ -36,6 +37,7 @@ CBufferedOutputStream::CBufferedOutputStream( CBufferedOutputStream::~CBufferedOutputStream() { delete m_closeCB; + delete m_fillCB; } const void* @@ -68,7 +70,6 @@ CBufferedOutputStream::close() } m_closed = true; - m_buffer.pop(m_buffer.getSize()); if (m_closeCB != NULL) { m_closeCB->run(); } @@ -82,7 +83,13 @@ CBufferedOutputStream::write(const void* buffer, UInt32 n) throw XIOClosed(); } + bool wasEmpty = (m_buffer.getSize() == 0); m_buffer.write(buffer, n); + if (wasEmpty && n > 0) { + if (m_fillCB != NULL) { + m_fillCB->run(); + } + } return n; } diff --git a/lib/io/CBufferedOutputStream.h b/lib/io/CBufferedOutputStream.h index 675aaac0..1853a161 100644 --- a/lib/io/CBufferedOutputStream.h +++ b/lib/io/CBufferedOutputStream.h @@ -36,8 +36,11 @@ public: The \c mutex must not be NULL and will be used to ensure thread safe access. If \c adoptedCloseCB is not NULL it will be called when close() is called, allowing the creator to detect the close. + If \c adoptedFillCB is not NULL, it will be called whenever the + buffer becomes non-empty. */ - CBufferedOutputStream(CMutex* mutex, IJob* adoptedCloseCB); + CBufferedOutputStream(CMutex* mutex, + IJob* adoptedFillCB, IJob* adoptedCloseCB); ~CBufferedOutputStream(); //! @name manipulators @@ -79,6 +82,7 @@ public: private: CMutex* m_mutex; + IJob* m_fillCB; IJob* m_closeCB; CCondVar m_empty; CStreamBuffer m_buffer; diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp index b2463e8b..cd88113d 100644 --- a/lib/mt/CThread.cpp +++ b/lib/mt/CThread.cpp @@ -109,6 +109,12 @@ CThread::waitForEvent(double timeout) const return s_map[ARCH->waitForEvent(m_thread, timeout)]; } +void +CThread::unblock() const +{ + ARCH->unblockThread(m_thread); +} + void* CThread::getResult() const { diff --git a/lib/mt/CThread.h b/lib/mt/CThread.h index 73982612..011f8611 100644 --- a/lib/mt/CThread.h +++ b/lib/mt/CThread.h @@ -180,6 +180,15 @@ public: */ EWaitResult waitForEvent(double timeout = -1.0) const; + //! Unblock thread in system call + /*! + Cause a thread that's in a blocking system call to return. This + call may return before the thread is unblocked. If the thread is + not in a blocking system call, this call has no effect. This does + not cause CMutex::lock() or CCondVar::wait() to return prematurely. + */ + void unblock() const; + //! Get the exit result /*! Returns the exit result. This does an implicit wait(). It returns diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index 1177516e..fb35f9d1 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -13,11 +13,16 @@ */ #include "CTCPListenSocket.h" -#include "CTCPSocket.h" #include "CNetworkAddress.h" -#include "XIO.h" +#include "CSocketMultiplexer.h" +#include "CTCPSocket.h" +#include "TSocketMultiplexerMethodJob.h" #include "XSocket.h" -#include "CThread.h" +#include "XIO.h" +#include "CEvent.h" +#include "CEventQueue.h" +#include "CLock.h" +#include "CMutex.h" #include "CArch.h" #include "XArch.h" @@ -25,8 +30,10 @@ // CTCPListenSocket // -CTCPListenSocket::CTCPListenSocket() +CTCPListenSocket::CTCPListenSocket() : + m_target(NULL) { + m_mutex = new CMutex; try { m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); } @@ -38,19 +45,28 @@ CTCPListenSocket::CTCPListenSocket() CTCPListenSocket::~CTCPListenSocket() { try { - ARCH->closeSocket(m_socket); + if (m_socket != NULL) { + CSocketMultiplexer::getInstance()->removeSocket(this); + ARCH->closeSocket(m_socket); + } } catch (...) { // ignore } + delete m_mutex; } void CTCPListenSocket::bind(const CNetworkAddress& addr) { try { + CLock lock(m_mutex); ARCH->bindSocket(m_socket, addr.getAddress()); ARCH->listenOnSocket(m_socket); + CSocketMultiplexer::getInstance()->addSocket(this, + new TSocketMultiplexerMethodJob( + this, &CTCPListenSocket::serviceListening, + m_socket, true, false)); } catch (XArchNetworkAddressInUse& e) { throw XSocketAddressInUse(e.what()); @@ -63,32 +79,27 @@ CTCPListenSocket::bind(const CNetworkAddress& addr) IDataSocket* CTCPListenSocket::accept() { - // accept asynchronously so we can check for cancellation - IArchNetwork::CPollEntry pfds[1]; - pfds[0].m_socket = m_socket; - pfds[0].m_events = IArchNetwork::kPOLLIN; - for (;;) { - ARCH->testCancelThread(); - try { - const int status = ARCH->pollSocket(pfds, 1, 0.01); - if (status > 0 && - (pfds[0].m_revents & IArchNetwork::kPOLLIN) != 0) { - return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); - } - } - catch (XArchNetwork&) { - // ignore and retry - } + try { + CSocketMultiplexer::getInstance()->addSocket(this, + new TSocketMultiplexerMethodJob( + this, &CTCPListenSocket::serviceListening, + m_socket, true, false)); + return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); + } + catch (XArchNetwork&) { + return NULL; } } void CTCPListenSocket::close() { + CLock lock(m_mutex); if (m_socket == NULL) { throw XIOClosed(); } try { + CSocketMultiplexer::getInstance()->removeSocket(this); ARCH->closeSocket(m_socket); m_socket = NULL; } @@ -96,3 +107,27 @@ CTCPListenSocket::close() throw XSocketIOClose(e.what()); } } + +void +CTCPListenSocket::setEventTarget(void* target) +{ + CLock lock(m_mutex); + m_target = target; +} + +ISocketMultiplexerJob* +CTCPListenSocket::serviceListening(ISocketMultiplexerJob* job, + bool read, bool, bool error) +{ + if (error) { + close(); + return NULL; + } + if (read) { + CEventQueue::getInstance()->addEvent( + CEvent(getConnectingEvent(), m_target, NULL)); + // stop polling on this socket until the client accepts + return NULL; + } + return job; +} diff --git a/lib/net/CTCPListenSocket.h b/lib/net/CTCPListenSocket.h index d5116379..8c582399 100644 --- a/lib/net/CTCPListenSocket.h +++ b/lib/net/CTCPListenSocket.h @@ -18,6 +18,9 @@ #include "IListenSocket.h" #include "IArchNetwork.h" +class CMutex; +class ISocketMultiplexerJob; + //! TCP listen socket /*! A listen socket using TCP. @@ -30,12 +33,20 @@ public: // ISocket overrides virtual void bind(const CNetworkAddress&); virtual void close(); + virtual void setEventTarget(void*); // IListenSocket overrides virtual IDataSocket* accept(); +private: + ISocketMultiplexerJob* + serviceListening(ISocketMultiplexerJob*, + bool, bool, bool); + private: CArchSocket m_socket; + CMutex* m_mutex; + void* m_target; }; #endif diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index e74fc4f7..7ab154b1 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -13,14 +13,16 @@ */ #include "CTCPSocket.h" +#include "CNetworkAddress.h" +#include "CSocketMultiplexer.h" +#include "TSocketMultiplexerMethodJob.h" #include "CBufferedInputStream.h" #include "CBufferedOutputStream.h" -#include "CNetworkAddress.h" -#include "XIO.h" #include "XSocket.h" +#include "XIO.h" #include "CLock.h" #include "CMutex.h" -#include "CThread.h" +#include "CEventQueue.h" #include "TMethodJob.h" #include "CArch.h" #include "XArch.h" @@ -45,20 +47,17 @@ CTCPSocket::CTCPSocket(CArchSocket socket) : { assert(m_socket != NULL); - init(); - // socket starts in connected state - m_connected = kReadWrite; - - // start handling socket - m_thread = new CThread(new TMethodJob( - this, &CTCPSocket::ioThread)); + init(); + setState(kReadWrite, true); } CTCPSocket::~CTCPSocket() { try { - close(); + if (m_socket != NULL) { + close(); + } } catch (...) { // ignore @@ -87,44 +86,28 @@ CTCPSocket::bind(const CNetworkAddress& addr) void CTCPSocket::close() { - // see if buffers should be flushed - bool doFlush = false; - { - CLock lock(m_mutex); - doFlush = (m_thread != NULL && (m_connected & kWrite) != 0); - } - // flush buffers - if (doFlush) { - m_output->flush(); - } + m_output->flush(); - // cause ioThread to exit - if (m_socket != NULL) { - CLock lock(m_mutex); - try { - ARCH->closeSocketForRead(m_socket); - } - catch (XArchNetwork&) { - // ignore - } - try { - ARCH->closeSocketForWrite(m_socket); - } - catch (XArchNetwork&) { - // ignore - } - m_connected = kClosed; - } + // now closed + setState(kClosed, true); - // wait for thread - if (m_thread != NULL) { - m_thread->wait(); - delete m_thread; - m_thread = NULL; + // close buffers + try { + m_input->close(); + } + catch (...) { + // ignore + } + try { + m_output->close(); + } + catch (...) { + // ignore } // close socket + CLock lock(m_mutex); if (m_socket != NULL) { try { ARCH->closeSocket(m_socket); @@ -136,65 +119,28 @@ CTCPSocket::close() } } +void +CTCPSocket::setEventTarget(void* target) +{ + CLock lock(m_mutex); + m_target = target; +} + void CTCPSocket::connect(const CNetworkAddress& addr) { - do { - // connect asynchronously so we can check for cancellation. - // we can't wrap setting and resetting the blocking flag in - // the c'tor/d'tor of a class (to make resetting automatic) - // because setBlockingOnSocket() can throw and it might be - // called while unwinding the stack due to a throw. - try { - ARCH->setBlockingOnSocket(m_socket, false); - ARCH->connectSocket(m_socket, addr.getAddress()); - ARCH->setBlockingOnSocket(m_socket, true); - - // connected - break; - } - catch (XArchNetworkConnecting&) { - // connection is in progress - ARCH->setBlockingOnSocket(m_socket, true); - } - catch (XArchNetwork& e) { - ARCH->setBlockingOnSocket(m_socket, true); - throw XSocketConnect(e.what()); - } - - // wait for connection or failure - IArchNetwork::CPollEntry pfds[1]; - pfds[0].m_socket = m_socket; - pfds[0].m_events = IArchNetwork::kPOLLOUT; - for (;;) { - ARCH->testCancelThread(); - try { - const int status = ARCH->pollSocket(pfds, 1, 0.01); - if (status > 0) { - if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | - IArchNetwork::kPOLLNVAL)) != 0) { - // connection failed - ARCH->throwErrorOnSocket(m_socket); - } - if ((pfds[0].m_revents & IArchNetwork::kPOLLOUT) != 0) { - // connection may have failed or succeeded - ARCH->throwErrorOnSocket(m_socket); - - // connected! - break; - } - } - } - catch (XArchNetwork& e) { - throw XSocketConnect(e.what()); - } - } - } while (false); - - // start servicing the socket - m_connected = kReadWrite; - m_thread = new CThread(new TMethodJob( - this, &CTCPSocket::ioThread)); + try { +// FIXME -- don't throw if in progress, just return that info + ARCH->connectSocket(m_socket, addr.getAddress()); + setState(kReadWrite, true); + } + catch (XArchNetworkConnecting&) { + // connection is in progress + setState(kConnecting, true); + } + catch (XArchNetwork& e) { + throw XSocketConnect(e.what()); + } } IInputStream* @@ -212,15 +158,24 @@ CTCPSocket::getOutputStream() void CTCPSocket::init() { - m_mutex = new CMutex; - m_thread = NULL; - m_connected = kClosed; - m_input = new CBufferedInputStream(m_mutex, + m_mutex = new CMutex; + m_input = new CBufferedInputStream(m_mutex, + new TMethodJob( + this, &CTCPSocket::emptyInput), new TMethodJob( this, &CTCPSocket::closeInput)); - m_output = new CBufferedOutputStream(m_mutex, + m_output = new CBufferedOutputStream(m_mutex, + new TMethodJob( + this, &CTCPSocket::fillOutput), new TMethodJob( this, &CTCPSocket::closeOutput)); + m_state = kUnconnected; + m_target = NULL; + m_job = NULL; + + // make socket non-blocking +// FIXME -- check for error + ARCH->setBlockingOnSocket(m_socket, false); // turn off Nagle algorithm. we send lots of very short messages // that should be sent without (much) delay. for example, the @@ -241,115 +196,101 @@ CTCPSocket::init() } } -void -CTCPSocket::ioThread(void*) +ISocketMultiplexerJob* +CTCPSocket::newMultiplexerJob(JobFunc func, bool readable, bool writable) { - try { - ioService(); - ioCleanup(); - } - catch (...) { - ioCleanup(); - throw; - } + return new TSocketMultiplexerMethodJob( + this, func, m_socket, readable, writable); } -void -CTCPSocket::ioCleanup() +ISocketMultiplexerJob* +CTCPSocket::setState(State state, bool setJob) { - try { - m_input->close(); + if (m_state == state || m_state == kClosed) { + return m_job; } - catch (...) { - // ignore - } - try { - m_output->close(); - } - catch (...) { - // ignore - } -} -void -CTCPSocket::ioService() -{ - assert(m_socket != NULL); + State oldState = m_state; + m_state = state; - // now service the connection - IArchNetwork::CPollEntry pfds[1]; - pfds[0].m_socket = m_socket; - for (;;) { - { - // choose events to poll for - CLock lock(m_mutex); - pfds[0].m_events = 0; - if (m_connected == 0) { - return; - } - if ((m_connected & kRead) != 0) { - // still open for reading - pfds[0].m_events |= IArchNetwork::kPOLLIN; - } - if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) { - // data queued for writing - pfds[0].m_events |= IArchNetwork::kPOLLOUT; - } + bool read = (m_input->getSize() > 0); + bool write = (m_output->getSize() > 0); + CEvent::Type eventType = 0; + m_job = NULL; + switch (m_state) { + case kUnconnected: + assert(0 && "cannot re-enter unconnected state"); + break; + + case kConnecting: + m_job = newMultiplexerJob(&CTCPSocket::serviceConnecting, false, true); + break; + + case kReadWrite: + if (oldState == kConnecting) { + eventType = IDataSocket::getConnectedEvent(); } + m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, write); + break; - try { - // check for status - const int status = ARCH->pollSocket(pfds, 1, 0.01); - - // transfer data and handle errors - if (status == 1) { - if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR | - IArchNetwork::kPOLLNVAL)) != 0) { - // stream is no good anymore so bail - CLock lock(m_mutex); - m_input->hangup(); - return; - } - - // read some data - if (pfds[0].m_revents & IArchNetwork::kPOLLIN) { - UInt8 buffer[4096]; - size_t n = ARCH->readSocket(m_socket, - buffer, sizeof(buffer)); - CLock lock(m_mutex); - if (n > 0) { - m_input->write(buffer, n); - } - else { - // stream hungup - m_input->hangup(); - m_connected &= ~kRead; - } - } - - // write some data - if (pfds[0].m_revents & IArchNetwork::kPOLLOUT) { - CLock lock(m_mutex); - - // get amount of data to write - UInt32 n = m_output->getSize(); - - // write data - const void* buffer = m_output->peek(n); - size_t n2 = ARCH->writeSocket(m_socket, buffer, n); - - // discard written data - if (n2 > 0) { - m_output->pop(n2); - } - } - } + case kReadOnly: + if (!write) { + eventType = IDataSocket::getShutdownOutputEvent(); } - catch (XArchNetwork&) { - // socket has failed - return; + if (oldState == kWriteOnly) { + goto shutdown; + } + m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, write); + break; + + case kWriteOnly: + if (!read) { + m_input->hangup(); + eventType = IDataSocket::getShutdownInputEvent(); + } + if (oldState == kReadOnly) { + goto shutdown; + } + m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, write); + break; + + case kShutdown: +shutdown: + if (!read && !write) { + eventType = ISocket::getDisconnectedEvent(); + m_state = kClosed; + } + else { + m_state = kShutdown; + } + break; + + case kClosed: + m_input->hangup(); + if (oldState == kConnecting) { + eventType = IDataSocket::getConnectionFailedEvent(); + } + else { + eventType = ISocket::getDisconnectedEvent(); + } + break; + } + + // notify + if (eventType != 0) { + sendEvent(eventType); + } + + // cut over to new job. multiplexer will delete the old job. + if (setJob) { + if (m_job == NULL) { + CSocketMultiplexer::getInstance()->removeSocket(this); + } + else { + CSocketMultiplexer::getInstance()->addSocket(this, m_job); } } + return m_job; } void @@ -358,7 +299,7 @@ CTCPSocket::closeInput(void*) // note -- m_mutex should already be locked try { ARCH->closeSocketForRead(m_socket); - m_connected &= ~kRead; + setState(kWriteOnly, true); } catch (XArchNetwork&) { // ignore @@ -370,10 +311,156 @@ CTCPSocket::closeOutput(void*) { // note -- m_mutex should already be locked try { - ARCH->closeSocketForWrite(m_socket); - m_connected &= ~kWrite; +// ARCH->closeSocketForWrite(m_socket); + setState(kReadOnly, true); } catch (XArchNetwork&) { // ignore } } + +void +CTCPSocket::emptyInput(void*) +{ + // note -- m_mutex should already be locked + bool write = (m_output->getSize() > 0); + if (m_state == kWriteOnly && !write) { + m_state = kShutdown; + } + if (m_state == kWriteOnly) { + m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, write); + CSocketMultiplexer::getInstance()->addSocket(this, m_job); + m_input->hangup(); + sendEvent(IDataSocket::getShutdownInputEvent()); + } + else if (m_state == kShutdown) { + m_job = NULL; + CSocketMultiplexer::getInstance()->removeSocket(this); + if (!write) { + sendEvent(ISocket::getDisconnectedEvent()); + m_state = kClosed; + } + } +} + +void +CTCPSocket::fillOutput(void*) +{ + // note -- m_mutex should already be locked + if (m_state == kReadWrite) { + m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, true); + CSocketMultiplexer::getInstance()->addSocket(this, m_job); + } + else if (m_state == kWriteOnly) { + m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, true); + CSocketMultiplexer::getInstance()->addSocket(this, m_job); + } +} + +ISocketMultiplexerJob* +CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job, + bool, bool write, bool error) +{ + CLock lock(m_mutex); + + if (write && !error) { + try { + // connection may have failed or succeeded + ARCH->throwErrorOnSocket(m_socket); + } + catch (XArchNetwork&) { + error = true; + } + } + + if (error) { + return setState(kClosed, false); + } + + if (write) { + return setState(kReadWrite, false); + } + + return job; +} + +ISocketMultiplexerJob* +CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, + bool read, bool write, bool error) +{ + CLock lock(m_mutex); + if (error) { + return setState(kClosed, false); + } + + if (write) { + // get amount of data to write + UInt32 n = m_output->getSize(); + + // write data + try { + const void* buffer = m_output->peek(n); + size_t n2 = ARCH->writeSocket(m_socket, buffer, n); + + // discard written data + if (n2 > 0) { + m_output->pop(n2); + } + } + catch (XArchNetworkDisconnected&) { + // stream hungup + return setState(kReadOnly, false); + } + } + + if (read) { + UInt8 buffer[4096]; + size_t n = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); + if (n > 0) { + // slurp up as much as possible + do { + m_input->write(buffer, n); + try { + n = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); + } + catch (XArchNetworkWouldBlock&) { + break; + } + } while (n > 0); + + // notify + sendEvent(IDataSocket::getInputEvent()); + } + else { + // stream hungup + return setState(kWriteOnly, false); + } + } + + if (write && m_output->getSize() == 0) { + if (m_state == kReadOnly) { + ARCH->closeSocketForWrite(m_socket); + sendEvent(IDataSocket::getShutdownOutputEvent()); + m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, + true, false); + job = m_job; + } + else if (m_state == kReadWrite || m_state == kReadOnly) { + m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, + true, false); + job = m_job; + } + else if (m_state == kWriteOnly) { + m_job = NULL; + job = m_job; + } + } + + return job; +} + +void +CTCPSocket::sendEvent(CEvent::Type type) +{ + CEventQueue::getInstance()->addEvent(CEvent(type, m_target, NULL)); +} diff --git a/lib/net/CTCPSocket.h b/lib/net/CTCPSocket.h index bfbade89..142ac4a6 100644 --- a/lib/net/CTCPSocket.h +++ b/lib/net/CTCPSocket.h @@ -16,6 +16,7 @@ #define CTCPSOCKET_H #include "IDataSocket.h" +#include "CEvent.h" #include "BasicTypes.h" #include "IArchNetwork.h" @@ -23,6 +24,7 @@ class CMutex; class CThread; class CBufferedInputStream; class CBufferedOutputStream; +class ISocketMultiplexerJob; //! TCP data socket /*! @@ -37,6 +39,7 @@ public: // ISocket overrides virtual void bind(const CNetworkAddress&); virtual void close(); + virtual void setEventTarget(void*); // IDataSocket overrides virtual void connect(const CNetworkAddress&); @@ -44,23 +47,52 @@ public: virtual IOutputStream* getOutputStream(); private: + enum State { + kUnconnected, + kConnecting, + kReadWrite, + kReadOnly, + kWriteOnly, + kShutdown, + kClosed + }; + void init(); - void ioThread(void*); - void ioCleanup(); - void ioService(); + + ISocketMultiplexerJob* + setState(State, bool setJob); + void closeInput(void*); void closeOutput(void*); + void emptyInput(void*); + void fillOutput(void*); + + ISocketMultiplexerJob* + serviceConnecting(ISocketMultiplexerJob*, + bool, bool, bool); + ISocketMultiplexerJob* + serviceConnected(ISocketMultiplexerJob*, + bool, bool, bool); + + typedef ISocketMultiplexerJob* (CTCPSocket::*JobFunc)( + ISocketMultiplexerJob*, + bool, bool, bool); + ISocketMultiplexerJob* + newMultiplexerJob(JobFunc, + bool readable, bool writable); + + void sendEvent(CEvent::Type); private: - enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 }; - CArchSocket m_socket; CBufferedInputStream* m_input; CBufferedOutputStream* m_output; CMutex* m_mutex; - CThread* m_thread; - UInt32 m_connected; + State m_state; + void* m_target; + + ISocketMultiplexerJob* m_job; }; #endif diff --git a/lib/net/IDataSocket.cpp b/lib/net/IDataSocket.cpp new file mode 100644 index 00000000..f6d577ab --- /dev/null +++ b/lib/net/IDataSocket.cpp @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "IDataSocket.h" + +// +// IDataSocket +// + +CEvent::Type IDataSocket::s_connectedEvent = CEvent::kUnknown; +CEvent::Type IDataSocket::s_failedEvent = CEvent::kUnknown; +CEvent::Type IDataSocket::s_inputEvent = CEvent::kUnknown; +CEvent::Type IDataSocket::s_shutdownInputEvent = CEvent::kUnknown; +CEvent::Type IDataSocket::s_shutdownOutputEvent = CEvent::kUnknown; + +CEvent::Type +IDataSocket::getConnectedEvent() +{ + return CEvent::registerTypeOnce(s_connectedEvent); +} + +CEvent::Type +IDataSocket::getConnectionFailedEvent() +{ + return CEvent::registerTypeOnce(s_failedEvent); +} + +CEvent::Type +IDataSocket::getInputEvent() +{ + return CEvent::registerTypeOnce(s_inputEvent); +} + +CEvent::Type +IDataSocket::getShutdownInputEvent() +{ + return CEvent::registerTypeOnce(s_shutdownInputEvent); +} + +CEvent::Type +IDataSocket::getShutdownOutputEvent() +{ + return CEvent::registerTypeOnce(s_shutdownOutputEvent); +} diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h index 0193e1d1..8fa2d6da 100644 --- a/lib/net/IDataSocket.h +++ b/lib/net/IDataSocket.h @@ -46,6 +46,7 @@ public: stream will shutdown the socket for reading. */ virtual IInputStream* getInputStream() = 0; + //! Get output stream /*! Returns the output stream for writing to the socket. Closing this @@ -53,11 +54,60 @@ public: */ virtual IOutputStream* getOutputStream() = 0; + //@} + //! @name accessors + //@{ + + //! Get connected event type + /*! + Returns the socket connected event type. A socket sends this + event when a remote connection has been established. + */ + static CEvent::Type getConnectedEvent(); + + //! Get connection failed event type + /*! + Returns the socket connection failed event type. A socket sends + this event when an attempt to connect to a remote port has failed. + */ + static CEvent::Type getConnectionFailedEvent(); + + //! Get input event type + /*! + Returns the socket input event type. A socket sends this + event when data is available to read from the input stream. + */ + static CEvent::Type getInputEvent(); + + //! Get shutdown input event type + /*! + Returns the socket shutdown input event type. A socket sends this + event when the remote side of the connection has shutdown for + writing and there is no more data to read from the socket. + */ + static CEvent::Type getShutdownInputEvent(); + + //! Get shutdown input event type + /*! + Returns the socket shutdown input event type. A socket sends this + event when the remote side of the connection has shutdown for + writing and there is no more data to read from the socket. + */ + static CEvent::Type getShutdownOutputEvent(); + //@} // ISocket overrides virtual void bind(const CNetworkAddress&) = 0; virtual void close() = 0; + virtual void setEventTarget(void*) = 0; + +private: + static CEvent::Type s_connectedEvent; + static CEvent::Type s_failedEvent; + static CEvent::Type s_inputEvent; + static CEvent::Type s_shutdownInputEvent; + static CEvent::Type s_shutdownOutputEvent; }; #endif diff --git a/lib/net/IListenSocket.cpp b/lib/net/IListenSocket.cpp new file mode 100644 index 00000000..9c9e704c --- /dev/null +++ b/lib/net/IListenSocket.cpp @@ -0,0 +1,27 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "IListenSocket.h" + +// +// IListenSocket +// + +CEvent::Type IListenSocket::s_connectingEvent = CEvent::kUnknown; + +CEvent::Type +IListenSocket::getConnectingEvent() +{ + return CEvent::registerTypeOnce(s_connectingEvent); +} diff --git a/lib/net/IListenSocket.h b/lib/net/IListenSocket.h index 7a7ddaae..36524750 100644 --- a/lib/net/IListenSocket.h +++ b/lib/net/IListenSocket.h @@ -31,18 +31,32 @@ public: //! Accept connection /*! - Wait for and accept a connection, returning a socket representing - the full-duplex data stream. - - (cancellation point) + Accept a connection, returning a socket representing the full-duplex + data stream. Returns NULL if no socket is waiting to be accepted. + This is only valid after a call to \c bind(). */ virtual IDataSocket* accept() = 0; + //@} + //! @name accessors + //@{ + + //! Get connecting event type + /*! + Returns the socket connecting event type. A socket sends this + event when a remote connection is waiting to be accepted. + */ + static CEvent::Type getConnectingEvent(); + //@} // ISocket overrides virtual void bind(const CNetworkAddress&) = 0; virtual void close() = 0; + virtual void setEventTarget(void*) = 0; + +private: + static CEvent::Type s_connectingEvent; }; #endif diff --git a/lib/net/ISocket.cpp b/lib/net/ISocket.cpp new file mode 100644 index 00000000..b1adbcc3 --- /dev/null +++ b/lib/net/ISocket.cpp @@ -0,0 +1,27 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ISocket.h" + +// +// ISocket +// + +CEvent::Type ISocket::s_disconnectedEvent = CEvent::kUnknown; + +CEvent::Type +ISocket::getDisconnectedEvent() +{ + return CEvent::registerTypeOnce(s_disconnectedEvent); +} diff --git a/lib/net/ISocket.h b/lib/net/ISocket.h index 623a25a7..79e55966 100644 --- a/lib/net/ISocket.h +++ b/lib/net/ISocket.h @@ -16,6 +16,7 @@ #define ISOCKET_H #include "IInterface.h" +#include "CEvent.h" class CNetworkAddress; @@ -40,7 +41,28 @@ public: */ virtual void close() = 0; + //! Set the socket's event target + /*! + Sets the target of any events sent by the socket. The default is NULL. + */ + virtual void setEventTarget(void*) = 0; + //@} + //! @name accessors + //@{ + + //! Get disconnected event type + /*! + Returns the socket disconnected event type. A socket sends this + event when the remote side of the socket has disconnected or + shutdown. + */ + static CEvent::Type getDisconnectedEvent(); + + //@} + +private: + static CEvent::Type s_disconnectedEvent; }; #endif diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index efeb5198..fc8dfe4a 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -15,35 +15,42 @@ NULL = DEPTH = ../.. VDEPTH = ./$(VPATH)/$(DEPTH) -EXTRA_DIST = \ - net.dsp \ +EXTRA_DIST = \ + net.dsp \ $(NULL) -MAINTAINERCLEANFILES = \ - Makefile.in \ +MAINTAINERCLEANFILES = \ + Makefile.in \ $(NULL) noinst_LIBRARIES = libnet.a -libnet_a_SOURCES = \ - CNetworkAddress.cpp \ - CTCPListenSocket.cpp \ - CTCPSocket.cpp \ - CTCPSocketFactory.cpp \ - XSocket.cpp \ - CNetworkAddress.h \ - CTCPListenSocket.h \ - CTCPSocket.h \ - CTCPSocketFactory.h \ - IDataSocket.h \ - IListenSocket.h \ - ISocket.h \ - ISocketFactory.h \ - XSocket.h \ +libnet_a_SOURCES = \ + CNetworkAddress.cpp \ + CSocketMultiplexer.cpp \ + CTCPListenSocket.cpp \ + CTCPSocket.cpp \ + CTCPSocketFactory.cpp \ + IDataSocket.cpp \ + IListenSocket.cpp \ + ISocket.cpp \ + XSocket.cpp \ + CNetworkAddress.h \ + CSocketMultiplexer.h \ + CTCPListenSocket.h \ + CTCPSocket.h \ + CTCPSocketFactory.h \ + IDataSocket.h \ + IListenSocket.h \ + ISocket.h \ + ISocketFactory.h \ + ISocketMultiplexerJob.h \ + TSocketMultiplexerMethodJob.h \ + XSocket.h \ $(NULL) -INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ - -I$(VDEPTH)/lib/mt \ - -I$(VDEPTH)/lib/io \ +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ + -I$(VDEPTH)/lib/base \ + -I$(VDEPTH)/lib/mt \ + -I$(VDEPTH)/lib/io \ $(NULL) diff --git a/lib/platform/CXWindowsEventQueue.cpp b/lib/platform/CXWindowsEventQueue.cpp new file mode 100644 index 00000000..9c51284d --- /dev/null +++ b/lib/platform/CXWindowsEventQueue.cpp @@ -0,0 +1,182 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsEventQueue.h" +#include "CEvent.h" +#include "CThread.h" +#if UNIX_LIKE +# if HAVE_POLL +# include +# else +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TIME_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include +# endif +# endif +#endif + +// +// CEventQueueTimer +// + +class CEventQueueTimer { }; + + +// +// CXWindowsEventQueue +// + +CXWindowsEventQueue::CXWindowsEventQueue(Display* display) : + m_display(display) +{ + m_userEvent = XInternAtom(m_display, "SYNERGY_USER_EVENT", False); + + XSetWindowAttributes attr; + m_window = XCreateWindow(m_display, DefaultRootWindow(m_display), + 0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent, + 0, &attr); +} + +CXWindowsEventQueue::~CXWindowsEventQueue() +{ + XDestroyWindow(m_display, m_window); +} + +void +CXWindowsEventQueue::processSystemEvent(CEvent& event) +{ + event = CEvent(CEvent::kSystem, getSystemTarget(), &m_event); +} + +void +CXWindowsEventQueue::processClientMessage(CEvent& event) +{ + assert(m_event.xany.type == ClientMessage); + + // handle user events specially + if (m_event.xclient.message_type == m_userEvent) { + // get event data + CEventData data = removeEventData(m_event.xclient.data.l[1]); + + // create event + event = CEvent(static_cast(m_event.xclient.data.l[0]), + data.first, data.second); + } + else { + processSystemEvent(event); + } +} + +void +CXWindowsEventQueue::waitForEvent(double dtimeout) +{ + // use poll() to wait for a message from the X server or for timeout. + // this is a good deal more efficient than polling and sleeping. +#if HAVE_POLL + struct pollfd pfds[1]; + pfds[0].fd = ConnectionNumber(m_display); + pfds[0].events = POLLIN; + int timeout = (dtimeout < 0.0) ? -1 : + static_cast(1000.0 * dtimeout); +#else + struct timeval timeout; + struct timeval* timeoutPtr; + if (dtimeout < 0.0) { + timeoutPtr = NULL; + } + else { + timeout.tv_sec = static_cast(dtimeout); + timeout.tv_usec = static_cast(1.0e+6 * + (dtimeout - timeout.tv_sec)); + timeoutPtr = &timeout; + } + + // initialize file descriptor sets + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(ConnectionNumber(m_display), &rfds); +#endif + + // wait for message from X server or for timeout. also check + // if the thread has been cancelled. poll() should return -1 + // with EINTR when the thread is cancelled. + CThread::testCancel(); +#if HAVE_POLL + poll(pfds, 1, timeout); +#else + select(ConnectionNumber(m_display) + 1, + SELECT_TYPE_ARG234 &rfds, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 timeoutPtr); +#endif + CThread::testCancel(); +} + +bool +CXWindowsEventQueue::doGetEvent(CEvent& event) +{ + // get next event + XNextEvent(m_display, &m_event); + + // process event + if (m_event.xany.type == ClientMessage) { + processClientMessage(event); + } + else { + processSystemEvent(event); + } + + return true; +} + +bool +CXWindowsEventQueue::doAddEvent(CEvent::Type type, UInt32 dataID) +{ + // send ourself a message + XEvent xevent; + xevent.xclient.type = ClientMessage; + xevent.xclient.window = m_window; + xevent.xclient.message_type = m_userEvent; + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = static_cast(type); + xevent.xclient.data.l[1] = static_cast(dataID); + return (XSendEvent(m_display, m_window, False, 0, &xevent) != 0); +} + +bool +CXWindowsEventQueue::doIsEmpty() const +{ + return (XPending(m_display) == 0); +} + +CEventQueueTimer* +CXWindowsEventQueue::doNewTimer(double, bool) const +{ + return new CEventQueueTimer(); +} + +void +CXWindowsEventQueue::doDeleteTimer(CEventQueueTimer* timer) const +{ + delete timer; +} diff --git a/lib/platform/CXWindowsEventQueue.h b/lib/platform/CXWindowsEventQueue.h new file mode 100644 index 00000000..39d9faef --- /dev/null +++ b/lib/platform/CXWindowsEventQueue.h @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSEVENTQUEUE_H +#define CXWINDOWSEVENTQUEUE_H + +#include "CEventQueue.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif + +//! Event queue for X11 +class CXWindowsEventQueue : public CEventQueue { +public: + CXWindowsEventQueue(Display*); + virtual ~CXWindowsEventQueue(); + + //! @name manipulators + //@{ + + //@} + //! @name accessors + //@{ + + //@} + +protected: + // CEventQueue overrides + virtual void waitForEvent(double timeout); + virtual bool doGetEvent(CEvent& event); + virtual bool doAddEvent(CEvent::Type type, UInt32 dataID); + virtual bool doIsEmpty() const; + virtual CEventQueueTimer* + doNewTimer(double duration, bool oneShot) const; + virtual void doDeleteTimer(CEventQueueTimer*) const; + +private: + void processSystemEvent(CEvent& event); + void processClientMessage(CEvent& event); + +private: + Display* m_display; + Window m_window; + Atom m_userEvent; + XEvent m_event; +}; + +#endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 891271d3..25629cc4 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -49,6 +49,7 @@ libplatform_a_SOURCES = \ CXWindowsClipboardTextConverter.cpp \ CXWindowsClipboardUCS2Converter.cpp \ CXWindowsClipboardUTF8Converter.cpp \ + CXWindowsEventQueue.cpp \ CXWindowsKeyMapper.cpp \ CXWindowsScreen.cpp \ CXWindowsScreenSaver.cpp \ @@ -57,6 +58,7 @@ libplatform_a_SOURCES = \ CXWindowsClipboardTextConverter.h \ CXWindowsClipboardUCS2Converter.h \ CXWindowsClipboardUTF8Converter.h \ + CXWindowsEventQueue.h \ CXWindowsKeyMapper.h \ CXWindowsScreen.h \ CXWindowsScreenSaver.h \ From 618aa7fedd874a3ed4fc894d6051f7d3ebca9b1d Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Feb 2004 20:56:52 +0000 Subject: [PATCH 572/807] Removed most HTTP stuff. It doesn't seem like the appropriate choice for server control. May later provide some other means for controlling the synergy server remotely. --- cmd/launcher/launcher.dsp | 4 +- cmd/synergys/Makefile.am | 2 - cmd/synergys/synergys.dsp | 4 +- configure.in | 1 - lib/Makefile.am | 1 - lib/http/CHTTPProtocol.cpp | 658 ------------------------------------- lib/http/CHTTPProtocol.h | 201 ----------- lib/http/Makefile.am | 39 --- lib/http/XHTTP.cpp | 135 -------- lib/http/XHTTP.h | 82 ----- lib/http/http.dsp | 110 ------- lib/server/server.dsp | 4 +- synergy.dsw | 18 - 13 files changed, 6 insertions(+), 1253 deletions(-) delete mode 100644 lib/http/CHTTPProtocol.cpp delete mode 100644 lib/http/CHTTPProtocol.h delete mode 100644 lib/http/Makefile.am delete mode 100644 lib/http/XHTTP.cpp delete mode 100644 lib/http/XHTTP.h delete mode 100644 lib/http/http.dsp diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp index 730a701c..afb8ed89 100644 --- a/cmd/launcher/launcher.dsp +++ b/cmd/launcher/launcher.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -71,7 +71,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index b3384ce6..f2c30e76 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -45,7 +45,6 @@ synergys_LDADD = \ $(DEPTH)/lib/platform/libplatform.a \ $(DEPTH)/lib/synergy/libsynergy.a \ $(DEPTH)/lib/net/libnet.a \ - $(DEPTH)/lib/http/libhttp.a \ $(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/base/libbase.a \ @@ -63,7 +62,6 @@ INCLUDES = \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ - -I$(VDEPTH)/lib/http \ -I$(VDEPTH)/lib/net \ -I$(VDEPTH)/lib/synergy \ -I$(VDEPTH)/lib/platform \ diff --git a/cmd/synergys/synergys.dsp b/cmd/synergys/synergys.dsp index cbc3e626..989ba8c5 100644 --- a/cmd/synergys/synergys.dsp +++ b/cmd/synergys/synergys.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -70,7 +70,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\http" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/configure.in b/configure.in index cb0e2a6c..999fb1b7 100644 --- a/configure.in +++ b/configure.in @@ -107,7 +107,6 @@ lib/base/Makefile lib/common/Makefile lib/mt/Makefile lib/io/Makefile -lib/http/Makefile lib/net/Makefile lib/synergy/Makefile lib/platform/Makefile diff --git a/lib/Makefile.am b/lib/Makefile.am index cababc3e..b0701a61 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -21,7 +21,6 @@ SUBDIRS = \ base \ mt \ io \ - http \ net \ synergy \ platform \ diff --git a/lib/http/CHTTPProtocol.cpp b/lib/http/CHTTPProtocol.cpp deleted file mode 100644 index ae385d6d..00000000 --- a/lib/http/CHTTPProtocol.cpp +++ /dev/null @@ -1,658 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CHTTPProtocol.h" -#include "XHTTP.h" -#include "IInputStream.h" -#include "IOutputStream.h" -#include "CLog.h" -#include "stdsstream.h" -#include -#include -#include - -// -// CHTTPRequest -// - -CHTTPRequest::CHTTPRequest() -{ - // do nothing -} - -CHTTPRequest::~CHTTPRequest() -{ - // do nothing -} - -void -CHTTPRequest::insertHeader(const CString& name, const CString& value) -{ - CHeaderMap::iterator index = m_headerByName.find(name); - if (index != m_headerByName.end()) { - index->second->second = value; - } - else { - CHeaderList::iterator pos = m_headers.insert( - m_headers.end(), std::make_pair(name, value)); - m_headerByName.insert(std::make_pair(name, pos)); - } -} - -void -CHTTPRequest::appendHeader(const CString& name, const CString& value) -{ - CHeaderMap::iterator index = m_headerByName.find(name); - if (index != m_headerByName.end()) { - index->second->second += ","; - index->second->second += value; - } - else { - CHeaderList::iterator pos = m_headers.insert( - m_headers.end(), std::make_pair(name, value)); - m_headerByName.insert(std::make_pair(name, pos)); - } -} - -void -CHTTPRequest::eraseHeader(const CString& name) -{ - CHeaderMap::iterator index = m_headerByName.find(name); - if (index != m_headerByName.end()) { - m_headers.erase(index->second); - } -} - -bool -CHTTPRequest::isHeader(const CString& name) const -{ - return (m_headerByName.find(name) != m_headerByName.end()); -} - -CString -CHTTPRequest::getHeader(const CString& name) const -{ - CHeaderMap::const_iterator index = m_headerByName.find(name); - if (index != m_headerByName.end()) { - return index->second->second; - } - else { - return CString(); - } -} - - -// -// CHTTPProtocol -// - -CHTTPRequest* -CHTTPProtocol::readRequest(IInputStream* stream, UInt32 maxSize) -{ - CString scratch; - - // note if we should limit the request size - const bool checkSize = (maxSize > 0); - - // parse request line by line - CHTTPRequest* request = new CHTTPRequest; - try { - CString line; - - // read request line. accept and discard leading empty lines. - do { - line = readLine(stream, scratch); - if (checkSize) { - if (line.size() + 2 > maxSize) { - throw XHTTP(413); - } - maxSize -= line.size() + 2; - } - } while (line.empty()); - - // parse request line: - { - std::istringstream s(line); - s.exceptions(std::ios::goodbit); - CString version; - s >> request->m_method >> request->m_uri >> version; - if (!s || request->m_uri.empty() || version.find("HTTP/") != 0) { - LOG((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); - throw XHTTP(400); - } - - // parse version - char dot; - s.str(version); - s.clear(); - s.ignore(5); - s >> request->m_majorVersion; - s.get(dot); - s >> request->m_minorVersion; - if (!s || dot != '.') { - LOG((CLOG_DEBUG1 "failed to parse HTTP request line: %s", line.c_str())); - throw XHTTP(400); - } - } - if (!isValidToken(request->m_method)) { - LOG((CLOG_DEBUG1 "invalid HTTP method: %s", line.c_str())); - throw XHTTP(400); - } - if (request->m_majorVersion < 1 || request->m_minorVersion < 0) { - LOG((CLOG_DEBUG1 "invalid HTTP version: %s", line.c_str())); - throw XHTTP(400); - } - - // parse headers - readHeaders(stream, request, false, scratch, - checkSize ? &maxSize : NULL); - - // HTTP/1.1 requests must have a Host header - if (request->m_majorVersion > 1 || - (request->m_majorVersion == 1 && request->m_minorVersion >= 1)) { - if (request->isHeader("Host") == 0) { - LOG((CLOG_DEBUG1 "Host header missing")); - throw XHTTP(400); - } - } - - // some methods may not have a body. ensure that the headers - // that indicate the body length do not exist for those methods - // and do exist for others. - if ((request->isHeader("Transfer-Encoding") || - request->isHeader("Content-Length")) == - (request->m_method == "GET" || - request->m_method == "HEAD")) { - LOG((CLOG_DEBUG1 "HTTP method (%s)/body mismatch", request->m_method.c_str())); - throw XHTTP(400); - } - - // prepare to read the body. the length of the body is - // determined using, in order: - // 1. Transfer-Encoding indicates a "chunked" transfer - // 2. Content-Length is present - // Content-Length is ignored for "chunked" transfers. - CString header; - if (!(header = request->getHeader("Transfer-Encoding")).empty()) { - // we only understand "chunked" encodings - if (!CStringUtil::CaselessCmp::equal(header, "chunked")) { - LOG((CLOG_DEBUG1 "unsupported Transfer-Encoding %s", header.c_str())); - throw XHTTP(501); - } - - // chunked encoding - UInt32 oldSize; - do { - oldSize = request->m_body.size(); - request->m_body += readChunk(stream, scratch, - checkSize ? &maxSize : NULL); - } while (request->m_body.size() != oldSize); - - // read footer - readHeaders(stream, request, true, scratch, - checkSize ? &maxSize : NULL); - - // remove "chunked" from Transfer-Encoding and set the - // Content-Length. - std::ostringstream s; - s << std::dec << request->m_body.size(); - request->eraseHeader("Transfer-Encoding"); - request->insertHeader("Content-Length", s.str()); - } - else if (!(header = request->getHeader("Content-Length")).empty()) { - // parse content-length - UInt32 length; - { - std::istringstream s(header); - s.exceptions(std::ios::goodbit); - s >> length; - if (!s) { - LOG((CLOG_DEBUG1 "cannot parse Content-Length", header.c_str())); - throw XHTTP(400); - } - } - - // check against expected size - if (checkSize && length > maxSize) { - throw XHTTP(413); - } - - // use content length - request->m_body = readBlock(stream, length, scratch); - if (request->m_body.size() != length) { - // length must match size of body - LOG((CLOG_DEBUG1 "Content-Length/actual length mismatch (%d vs %d)", length, request->m_body.size())); - throw XHTTP(400); - } - } - } - catch (...) { - delete request; - throw; - } - - return request; -} - -void -CHTTPProtocol::reply(IOutputStream* stream, CHTTPReply& reply) -{ - // suppress body for certain replies - bool hasBody = true; - if ((reply.m_status / 100) == 1 || - reply.m_status == 204 || - reply.m_status == 304) { - hasBody = false; - } - - // adjust headers - for (CHTTPReply::CHeaderList::iterator - index = reply.m_headers.begin(); - index != reply.m_headers.end(); ) { - const CString& header = index->first; - - // remove certain headers - if (CStringUtil::CaselessCmp::equal(header, "Content-Length") || - CStringUtil::CaselessCmp::equal(header, "Date") || - CStringUtil::CaselessCmp::equal(header, "Transfer-Encoding")) { - // FIXME -- Transfer-Encoding should be left as-is if - // not "chunked" and if the version is 1.1 or up. - index = reply.m_headers.erase(index); - } - - // keep as-is - else { - ++index; - } - } - - // write reply header - std::ostringstream s; - s << "HTTP/" << reply.m_majorVersion << "." << - reply.m_minorVersion << " " << - reply.m_status << " " << - reply.m_reason << "\r\n"; - - // get date - // FIXME -- should use C++ locale stuff but VC++ time_put is broken. - // FIXME -- double check that VC++ is broken - char date[30]; - { - const char* oldLocale = setlocale(LC_TIME, "C"); - time_t t = time(NULL); -#if HAVE_GMTIME_R - struct tm tm; - struct tm* tmp = &tm; - gmtime_r(&t, tmp); -#else - struct tm* tmp = gmtime(&t); -#endif - strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", tmp); - setlocale(LC_TIME, oldLocale); - } - - // write headers - s << "Date: " << date << "\r\n"; - for (CHTTPReply::CHeaderList::const_iterator - index = reply.m_headers.begin(); - index != reply.m_headers.end(); ++index) { - s << index->first << ": " << index->second << "\r\n"; - } - if (hasBody) { - s << "Content-Length: " << reply.m_body.size() << "\r\n"; - } - s << "Connection: close\r\n"; - - // write end of headers - s << "\r\n"; - - // write to stream - stream->write(s.str().data(), s.str().size()); - - // write body. replies to HEAD method never have a body (though - // they do have the Content-Length header). - if (hasBody && reply.m_method != "HEAD") { - stream->write(reply.m_body.data(), reply.m_body.size()); - } -} - -bool -CHTTPProtocol::parseFormData(const CHTTPRequest& request, CFormParts& parts) -{ - static const char formData[] = "multipart/form-data"; - static const char boundary[] = "boundary="; - static const char disposition[] = "Content-Disposition:"; - static const char nameAttr[] = "name="; - static const char quote[] = "\""; - - // find the Content-Type header - const CString contentType = request.getHeader("Content-Type"); - if (contentType.empty()) { - // missing required Content-Type header - return false; - } - - // parse type - CString::const_iterator index = std::search( - contentType.begin(), contentType.end(), - formData, formData + sizeof(formData) - 1, - CStringUtil::CaselessCmp::cmpEqual); - if (index == contentType.end()) { - // not form-data - return false; - } - index += sizeof(formData) - 1; - index = std::search(index, contentType.end(), - boundary, boundary + sizeof(boundary) - 1, - CStringUtil::CaselessCmp::cmpEqual); - if (index == contentType.end()) { - // no boundary - return false; - } - CString delimiter = contentType.c_str() + - (index - contentType.begin()) + - sizeof(boundary) - 1; - - // find first delimiter - const CString& body = request.m_body; - CString::size_type partIndex = body.find(delimiter); - if (partIndex == CString::npos) { - return false; - } - - // skip over it - partIndex += delimiter.size(); - - // prepend CRLF-- to delimiter - delimiter = "\r\n--" + delimiter; - - // parse parts until there are no more - for (;;) { - // is it the last part? - if (body.size() >= partIndex + 2 && - body[partIndex ] == '-' && - body[partIndex + 1] == '-') { - // found last part. ignore trailing data, if any. - return true; - } - - // find the end of this part - CString::size_type nextPart = body.find(delimiter, partIndex); - if (nextPart == CString::npos) { - // no terminator - return false; - } - - // find end of headers - CString::size_type endOfHeaders = body.find("\r\n\r\n", partIndex); - if (endOfHeaders == CString::npos || endOfHeaders > nextPart) { - // bad part - return false; - } - endOfHeaders += 2; - - // now find Content-Disposition - index = std::search(body.begin() + partIndex, - body.begin() + endOfHeaders, - disposition, - disposition + sizeof(disposition) - 1, - CStringUtil::CaselessCmp::cmpEqual); - if (index == contentType.begin() + endOfHeaders) { - // bad part - return false; - } - - // find the name in the Content-Disposition - CString::size_type endOfHeader = body.find("\r\n", - index - body.begin()); - if (endOfHeader >= endOfHeaders) { - // bad part - return false; - } - index = std::search(index, body.begin() + endOfHeader, - nameAttr, nameAttr + sizeof(nameAttr) - 1, - CStringUtil::CaselessCmp::cmpEqual); - if (index == body.begin() + endOfHeader) { - // no name - return false; - } - - // extract the name - CString name; - index += sizeof(nameAttr) - 1; - if (*index == quote[0]) { - // quoted name - ++index; - CString::size_type namePos = index - body.begin(); - index = std::search(index, body.begin() + endOfHeader, - quote, quote + 1, - CStringUtil::CaselessCmp::cmpEqual); - if (index == body.begin() + endOfHeader) { - // missing close quote - return false; - } - name = body.substr(namePos, index - body.begin() - namePos); - } - else { - // unquoted name - name = body.substr(index - body.begin(), - body.find_first_of(" \t\r\n")); - } - - // save part. add 2 to endOfHeaders to skip CRLF. - parts.insert(std::make_pair(name, body.substr(endOfHeaders + 2, - nextPart - (endOfHeaders + 2)))); - - // move to next part - partIndex = nextPart + delimiter.size(); - } - - // should've found the last delimiter inside the loop but we did not - return false; -} - -CString -CHTTPProtocol::readLine(IInputStream* stream, CString& tmpBuffer) -{ - // read up to and including a CRLF from stream, using whatever - // is in tmpBuffer as if it were at the head of the stream. - - for (;;) { - // scan tmpBuffer for CRLF - CString::size_type newline = tmpBuffer.find("\r\n"); - if (newline != CString::npos) { - // copy line without the CRLF - CString line = tmpBuffer.substr(0, newline); - - // discard line and CRLF from tmpBuffer - tmpBuffer.erase(0, newline + 2); - return line; - } - - // read more from stream - char buffer[4096]; - UInt32 n = stream->read(buffer, sizeof(buffer), -1.0); - if (n == 0) { - // stream is empty. return what's leftover. - CString line = tmpBuffer; - tmpBuffer.erase(); - return line; - } - - // append stream data - tmpBuffer.append(buffer, n); - } -} - -CString -CHTTPProtocol::readBlock(IInputStream* stream, - UInt32 numBytes, CString& tmpBuffer) -{ - CString data; - - // read numBytes from stream, using whatever is in tmpBuffer as - // if it were at the head of the stream. - if (tmpBuffer.size() > 0) { - // ignore stream if there's enough data in tmpBuffer - if (tmpBuffer.size() >= numBytes) { - data = tmpBuffer.substr(0, numBytes); - tmpBuffer.erase(0, numBytes); - return data; - } - - // move everything out of tmpBuffer into data - data = tmpBuffer; - tmpBuffer.erase(); - } - - // account for bytes read so far - assert(data.size() < numBytes); - numBytes -= data.size(); - - // read until we have all the requested data - while (numBytes > 0) { - // read max(4096, bytes_left) bytes into buffer - char buffer[4096]; - UInt32 n = sizeof(buffer); - if (n > numBytes) { - n = numBytes; - } - n = stream->read(buffer, n, -1.0); - - // if stream is empty then return what we've got so far - if (n == 0) { - break; - } - - // append stream data - data.append(buffer, n); - numBytes -= n; - } - - return data; -} - -CString -CHTTPProtocol::readChunk(IInputStream* stream, - CString& tmpBuffer, UInt32* maxSize) -{ - CString line; - - // get chunk header - line = readLine(stream, tmpBuffer); - - // parse chunk size - UInt32 size; - { - std::istringstream s(line); - s.exceptions(std::ios::goodbit); - s >> std::hex >> size; - if (!s) { - LOG((CLOG_DEBUG1 "cannot parse chunk size", line.c_str())); - throw XHTTP(400); - } - } - if (size == 0) { - return CString(); - } - - // check size - if (maxSize != NULL) { - if (line.size() + 2 + size + 2 > *maxSize) { - throw XHTTP(413); - } - maxSize -= line.size() + 2 + size + 2; - } - - // read size bytes - CString data = readBlock(stream, size, tmpBuffer); - if (data.size() != size) { - LOG((CLOG_DEBUG1 "expected/actual chunk size mismatch", size, data.size())); - throw XHTTP(400); - } - - // read an discard CRLF - line = readLine(stream, tmpBuffer); - if (!line.empty()) { - LOG((CLOG_DEBUG1 "missing CRLF after chunk")); - throw XHTTP(400); - } - - return data; -} - -void -CHTTPProtocol::readHeaders(IInputStream* stream, - CHTTPRequest* request, bool isFooter, - CString& tmpBuffer, UInt32* maxSize) -{ - // parse headers. done with headers when we get a blank line. - CString name; - CString line = readLine(stream, tmpBuffer); - while (!line.empty()) { - // check size - if (maxSize != NULL) { - if (line.size() + 2 > *maxSize) { - throw XHTTP(413); - } - *maxSize -= line.size() + 2; - } - - // if line starts with space or tab then append it to the - // previous header. if there is no previous header then - // throw. - if (line[0] == ' ' || line[0] == '\t') { - if (name.empty()) { - LOG((CLOG_DEBUG1 "first header is a continuation")); - throw XHTTP(400); - } - request->appendHeader(name, line); - } - - // line should have the form: :[] - else { - // parse - CString value; - std::istringstream s(line); - s.exceptions(std::ios::goodbit); - std::getline(s, name, ':'); - if (!s || !isValidToken(name)) { - LOG((CLOG_DEBUG1 "invalid header: %s", line.c_str())); - throw XHTTP(400); - } - std::getline(s, value); - - // check validity of name - if (isFooter) { - // FIXME -- only certain names are allowed in footers - // but which ones? - } - - request->appendHeader(name, value); - } - - // next header - line = readLine(stream, tmpBuffer); - } -} - -bool -CHTTPProtocol::isValidToken(const CString& token) -{ - return (token.find("()<>@,;:\\\"/[]?={} " - "\0\1\2\3\4\5\6\7" - "\10\11\12\13\14\15\16\17" - "\20\21\22\23\24\25\26\27" - "\30\31\32\33\34\35\36\37\177") == CString::npos); -} diff --git a/lib/http/CHTTPProtocol.h b/lib/http/CHTTPProtocol.h deleted file mode 100644 index 8c866455..00000000 --- a/lib/http/CHTTPProtocol.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CHTTPPROTOCOL_H -#define CHTTPPROTOCOL_H - -#include "CString.h" -#include "CStringUtil.h" -#include "BasicTypes.h" -#include "stdlist.h" -#include "stdmap.h" -#include "stdvector.h" - -class IInputStream; -class IOutputStream; - -//! HTTP request type -/*! -This class encapsulates an HTTP request. -*/ -class CHTTPRequest { -private: - typedef std::list > CHeaderList; -public: - //! Iterator on headers - /*! - An iterator on the headers. Each element is a std::pair; first is - the header name as a CString, second is the header value as a CString. - */ - typedef CHeaderList::const_iterator const_iterator; - - CHTTPRequest(); - ~CHTTPRequest(); - - //! @name manipulators - //@{ - - //! Insert header - /*! - Add a header by name replacing the existing header, if any. - Headers are sent in the order they're inserted. Replacing - a header does not change its original position in the order. - */ - void insertHeader(const CString& name, const CString& value); - - //! Append header - /*! - Append a header. Equivalent to insertHeader() if the header - doesn't exist, otherwise it appends a comma and the value to - the existing header. - */ - void appendHeader(const CString& name, const CString& value); - - //! Remove header - /*! - Remove a header by name. Does nothing if the header doesn't exist. - */ - void eraseHeader(const CString& name); - - //@} - //! @name accessors - //@{ - - //! Check header existence - /*! - Returns true iff the header exists. - */ - bool isHeader(const CString& name) const; - - //! Get header - /*! - Get a header by name. Returns the empty string if the header - doesn't exist. - */ - CString getHeader(const CString& name) const; - - // headers are iterated in the order they were added. - //! Get beginning header iterator - const_iterator begin() const { return m_headers.begin(); } - //! Get ending header iterator - const_iterator end() const { return m_headers.end(); } - - //@} - -public: - // note -- these members are public for convenience - //! The HTTP method - CString m_method; - //! The HTTP URI - CString m_uri; - //! The HTTP major version number - SInt32 m_majorVersion; - //! The HTTP minor version number - SInt32 m_minorVersion; - //! The HTTP body, after transfer decoding - CString m_body; - -private: - typedef std::map CHeaderMap; - - CHeaderList m_headers; - CHeaderMap m_headerByName; -}; - -//! HTTP reply type -/*! -This class encapsulates an HTTP reply. -*/ -class CHTTPReply { -public: - //! Header list - /*! - The type of the reply header list. Each pair is the header name - and value, respectively for first and second. - */ - typedef std::vector > CHeaderList; - - // note -- these members are public for convenience - //! The HTTP major version number - SInt32 m_majorVersion; - //! The HTTP minor version number - SInt32 m_minorVersion; - //! The HTTP status code - SInt32 m_status; - //! The HTTP reason phrase - CString m_reason; - //! The HTTP method - CString m_method; - //! The HTTP headers - CHeaderList m_headers; - //! The HTTP body - CString m_body; -}; - -//! HTTP protocol utilities -/*! -This class provides utility functions for HTTP. -*/ -class CHTTPProtocol { -public: - //! Multipart form parts - /*! - Each element is the contents of a multipart form part indexed by - it's name. - */ - typedef std::map CFormParts; - - //! Read HTTP request - /*! - Read and parse an HTTP request. The result is returned in a - CHTTPRequest which the client must delete. Throws an - XHTTP if there was a parse error. Throws an XIO exception - if there was a read error. If \c maxSize is greater than - zero and the request is larger than \c maxSize bytes then - throws XHTTP(413) (request entity too large). - */ - static CHTTPRequest* readRequest(IInputStream*, UInt32 maxSize = 0); - - //! Send HTTP response - /*! - Send an HTTP reply. The Content-Length and Date headers are set - automatically. - */ - static void reply(IOutputStream*, CHTTPReply&); - - //! Parse multipart form data - /*! - Parse a multipart/form-data body into its parts. Returns true - iff the entire body was correctly parsed. - */ - // FIXME -- name/value pairs insufficient to save part headers - static bool parseFormData(const CHTTPRequest&, - CFormParts& parts); - -private: - static CString readLine(IInputStream*, CString& tmpBuffer); - static CString readBlock(IInputStream*, - UInt32 numBytes, CString& tmpBuffer); - static CString readChunk(IInputStream*, CString& tmpBuffer, - UInt32* maxSize); - static void readHeaders(IInputStream*, - CHTTPRequest*, bool isFooter, - CString& tmpBuffer, - UInt32* maxSize); - - static bool isValidToken(const CString&); -}; - -#endif diff --git a/lib/http/Makefile.am b/lib/http/Makefile.am deleted file mode 100644 index c9abe536..00000000 --- a/lib/http/Makefile.am +++ /dev/null @@ -1,39 +0,0 @@ -# synergy -- mouse and keyboard sharing utility -# Copyright (C) 2002 Chris Schoeneman -# -# This package is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# found in the file COPYING that should have accompanied this file. -# -# This package is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -## Process this file with automake to produce Makefile.in -NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) - -EXTRA_DIST = \ - http.dsp \ - $(NULL) - -MAINTAINERCLEANFILES = \ - Makefile.in \ - $(NULL) - -noinst_LIBRARIES = libhttp.a -libhttp_a_SOURCES = \ - CHTTPProtocol.cpp \ - XHTTP.cpp \ - CHTTPProtocol.h \ - XHTTP.h \ - $(NULL) -INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ - -I$(VDEPTH)/lib/mt \ - -I$(VDEPTH)/lib/io \ - $(NULL) diff --git a/lib/http/XHTTP.cpp b/lib/http/XHTTP.cpp deleted file mode 100644 index 9b146091..00000000 --- a/lib/http/XHTTP.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "XHTTP.h" -#include "CHTTPProtocol.h" -#include "CStringUtil.h" -#include "stdsstream.h" - -// -// XHTTP -// - -XHTTP::XHTTP(SInt32 statusCode) : - XBase(), - m_status(statusCode), - m_reason(getReason(statusCode)) -{ - // do nothing -} - -XHTTP::XHTTP(SInt32 statusCode, const CString& reasonPhrase) : - XBase(), - m_status(statusCode), - m_reason(reasonPhrase) -{ - // do nothing -} - -XHTTP::~XHTTP() -{ - // do nothing -} - -SInt32 -XHTTP::getStatus() const -{ - return m_status; -} - -CString -XHTTP::getReason() const -{ - return m_reason; -} - -void -XHTTP::addHeaders(CHTTPReply&) const -{ - // do nothing -} - -CString -XHTTP::getWhat() const throw() -{ - const char* reason; - if (m_reason.empty()) { - reason = getReason(m_status); - } - else { - reason = m_reason.c_str(); - } - return format("XHTTP", "%{1} %{2}", - CStringUtil::print("%d", m_status).c_str(), - reason); -} - -const char* -XHTTP::getReason(SInt32 status) -{ - switch (status) { - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Moved Temporarily"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Time-out"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-URI Too Large"; - case 415: return "Unsupported Media Type"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Time-out"; - case 505: return "HTTP Version not supported"; - default: return ""; - } -} - - -// -// XHTTPAllow -// - -XHTTPAllow::XHTTPAllow(const CString& allowedMethods) : - XHTTP(405), - m_allowed(allowedMethods) -{ - // do nothing -} - -XHTTPAllow::~XHTTPAllow() -{ - // do nothing -} - -void -XHTTPAllow::addHeaders(CHTTPReply& reply) const -{ - reply.m_headers.push_back(std::make_pair(CString("Allow"), m_allowed)); -} diff --git a/lib/http/XHTTP.h b/lib/http/XHTTP.h deleted file mode 100644 index 2bd6ed73..00000000 --- a/lib/http/XHTTP.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef XHTTP_H -#define XHTTP_H - -#include "BasicTypes.h" -#include "XBase.h" - -class CHTTPReply; - -//! Generic HTTP exception -class XHTTP : public XBase { -public: - /*! - Use the HTTP \c statusCode as the failure reason. - */ - XHTTP(SInt32 statusCode); - /*! - Use the HTTP \c statusCode as the failure reason. Use \c reasonPhrase - as the human readable reason for the failure. - */ - XHTTP(SInt32 statusCode, const CString& reasonPhrase); - ~XHTTP(); - - //@{ - //! @name accessors - - //! Get the HTTP status code - SInt32 getStatus() const; - - //! Get the reason phrase - CString getReason() const; - - //! Modify reply for error - /*! - Override to modify an HTTP reply to further describe the error. - */ - virtual void addHeaders(CHTTPReply&) const; - - //@} - -protected: - virtual CString getWhat() const throw(); - -private: - static const char* getReason(SInt32 status); - -private: - SInt32 m_status; - CString m_reason; -}; - -//! HTTP exception indicating an unsupported method -class XHTTPAllow : public XHTTP { -public: - /*! - \c allowedMethods is added as an `Allow' header to a reply in - addHeaders(). - */ - XHTTPAllow(const CString& allowedMethods); - ~XHTTPAllow(); - - // XHTTP overrides - virtual void addHeaders(CHTTPReply&) const; - -private: - CString m_allowed; -}; - -#endif diff --git a/lib/http/http.dsp b/lib/http/http.dsp deleted file mode 100644 index 439b0616..00000000 --- a/lib/http/http.dsp +++ /dev/null @@ -1,110 +0,0 @@ -# Microsoft Developer Studio Project File - Name="http" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=http - Win32 Debug -!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 "http.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 "http.mak" CFG="http - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "http - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "http - Win32 Debug" (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)" == "http - 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" -# 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "http - 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" -# 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "http - Win32 Release" -# Name "http - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CHTTPProtocol.cpp -# End Source File -# Begin Source File - -SOURCE=.\XHTTP.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CHTTPProtocol.h -# End Source File -# Begin Source File - -SOURCE=.\XHTTP.h -# End Source File -# End Group -# End Target -# End Project diff --git a/lib/server/server.dsp b/lib/server/server.dsp index 986fedf8..536b33d7 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "Release" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "Debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\http" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/synergy.dsw b/synergy.dsw index 7148711f..63b402d9 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -84,18 +84,6 @@ Package=<4> ############################################################################### -Project: "http"=".\lib\HTTP\http.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - Project: "io"=".\lib\io\io.dsp" - Package Owner=<4> Package=<5> @@ -126,9 +114,6 @@ Package=<4> Project_Dep_Name base End Project Dependency Begin Project Dependency - Project_Dep_Name http - End Project Dependency - Begin Project Dependency Project_Dep_Name io End Project Dependency Begin Project Dependency @@ -282,9 +267,6 @@ Package=<4> Project_Dep_Name base End Project Dependency Begin Project Dependency - Project_Dep_Name http - End Project Dependency - Begin Project Dependency Project_Dep_Name io End Project Dependency Begin Project Dependency From 848aee7a3ac37e5ed7260470e8e7cdf48ae62752 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Feb 2004 21:09:22 +0000 Subject: [PATCH 573/807] Checkpoint. Code does not run. Still converting over to new event loop model. Streams, stream filters, and sockets are converted. Client proxies are almost converted. CServer is in progress. Removed all HTTP code. Haven't converted the necessary win32 arch stuff. --- cmd/synergys/synergys.cpp | 20 - lib/arch/CArch.cpp | 12 + lib/arch/CArch.h | 2 + lib/arch/CArchMultithreadPosix.cpp | 37 +- lib/arch/CArchMultithreadPosix.h | 4 + lib/arch/CArchNetworkBSD.cpp | 11 + lib/arch/IArchMultithread.h | 12 + lib/arch/XArch.h | 5 +- lib/base/CEventQueue.cpp | 123 ++- lib/base/CEventQueue.h | 47 +- .../IEventQueue.cpp} | 32 +- lib/base/IEventQueue.h | 72 +- lib/base/Makefile.am | 1 + lib/io/CBufferedInputStream.cpp | 140 --- lib/io/CBufferedInputStream.h | 101 --- lib/io/CBufferedOutputStream.cpp | 104 --- lib/io/CBufferedOutputStream.h | 92 -- lib/io/CInputStreamFilter.h | 52 -- lib/io/COutputStreamFilter.cpp | 39 - lib/io/COutputStreamFilter.h | 52 -- lib/io/CStreamFilter.cpp | 105 +++ lib/io/CStreamFilter.h | 62 ++ lib/io/IInputStream.h | 67 -- lib/io/IOutputStream.h | 58 -- lib/io/IStream.cpp | 55 ++ lib/io/IStream.h | 175 ++++ lib/io/IStreamFilterFactory.h | 22 +- lib/io/Makefile.am | 14 +- lib/io/XIO.cpp | 11 + lib/io/XIO.h | 6 + lib/net/CSocketMultiplexer.cpp | 304 +++++++ lib/net/CSocketMultiplexer.h | 95 +++ lib/net/CTCPListenSocket.cpp | 42 +- lib/net/CTCPListenSocket.h | 3 +- lib/net/CTCPSocket.cpp | 622 +++++++------- lib/net/CTCPSocket.h | 73 +- lib/net/IDataSocket.cpp | 25 +- lib/net/IDataSocket.h | 70 +- lib/net/IListenSocket.h | 2 +- lib/net/ISocket.h | 15 +- lib/net/ISocketMultiplexerJob.h | 75 ++ lib/net/TSocketMultiplexerMethodJob.h | 108 +++ lib/server/CClientProxy.cpp | 25 +- lib/server/CClientProxy.h | 22 +- lib/server/CClientProxy1_0.cpp | 316 ++++--- lib/server/CClientProxy1_0.h | 29 +- lib/server/CClientProxy1_1.cpp | 12 +- lib/server/CClientProxy1_1.h | 3 +- lib/server/CConfig.cpp | 27 - lib/server/CConfig.h | 10 - lib/server/CHTTPServer.cpp | 799 ------------------ lib/server/CHTTPServer.h | 142 ---- lib/server/CServer.cpp | 411 +++++---- lib/server/CServer.h | 27 +- lib/server/Makefile.am | 5 +- lib/synergy/CInputPacketStream.cpp | 181 ---- lib/synergy/CInputPacketStream.h | 51 -- lib/synergy/COutputPacketStream.cpp | 72 -- lib/synergy/COutputPacketStream.h | 36 - lib/synergy/CPacketStreamFilter.cpp | 221 +++++ lib/synergy/CPacketStreamFilter.h | 57 ++ lib/synergy/CProtocolUtil.cpp | 87 +- lib/synergy/CProtocolUtil.h | 17 +- lib/synergy/Makefile.am | 6 +- 64 files changed, 2581 insertions(+), 2942 deletions(-) rename lib/{io/CInputStreamFilter.cpp => base/IEventQueue.cpp} (52%) delete mode 100644 lib/io/CBufferedInputStream.cpp delete mode 100644 lib/io/CBufferedInputStream.h delete mode 100644 lib/io/CBufferedOutputStream.cpp delete mode 100644 lib/io/CBufferedOutputStream.h delete mode 100644 lib/io/CInputStreamFilter.h delete mode 100644 lib/io/COutputStreamFilter.cpp delete mode 100644 lib/io/COutputStreamFilter.h create mode 100644 lib/io/CStreamFilter.cpp create mode 100644 lib/io/CStreamFilter.h delete mode 100644 lib/io/IInputStream.h delete mode 100644 lib/io/IOutputStream.h create mode 100644 lib/io/IStream.cpp create mode 100644 lib/io/IStream.h create mode 100644 lib/net/CSocketMultiplexer.cpp create mode 100644 lib/net/CSocketMultiplexer.h create mode 100644 lib/net/ISocketMultiplexerJob.h create mode 100644 lib/net/TSocketMultiplexerMethodJob.h delete mode 100644 lib/server/CHTTPServer.cpp delete mode 100644 lib/server/CHTTPServer.h delete mode 100644 lib/synergy/CInputPacketStream.cpp delete mode 100644 lib/synergy/CInputPacketStream.h delete mode 100644 lib/synergy/COutputPacketStream.cpp delete mode 100644 lib/synergy/COutputPacketStream.h create mode 100644 lib/synergy/CPacketStreamFilter.cpp create mode 100644 lib/synergy/CPacketStreamFilter.h diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 59b76fce..cfa66e8a 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -88,7 +88,6 @@ public: const char* m_logFilter; CString m_name; CNetworkAddress m_synergyAddress; - CNetworkAddress m_httpAddress; CConfig m_config; }; @@ -191,11 +190,6 @@ realMain(void) ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); } - // set HTTP address if provided - if (ARG->m_httpAddress.isValid()) { - ARG->m_config.setHTTPAddress(ARG->m_httpAddress); - } - // create server s_server = new CServer(ARG->m_name); s_server->setConfig(ARG->m_config); @@ -454,20 +448,6 @@ parse(int argc, const char* const* argv) ++i; } - else if (isArg(i, argc, argv, NULL, "--http", 1)) { - // save listen address - try { - ARG->m_httpAddress = CNetworkAddress(argv[i + 1], - kDefaultPort + 1); - } - catch (XSocketAddress& e) { - LOG((CLOG_PRINT "%s: %s" BYE, - ARG->m_pname, e.what(), ARG->m_pname)); - bye(kExitArgs); - } - ++i; - } - else if (isArg(i, argc, argv, "-n", "--name", 1)) { // save screen name ARG->m_name = argv[++i]; diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index d276c4ee..4f95bc90 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -387,6 +387,18 @@ CArch::getIDOfThread(CArchThread thread) return m_mt->getIDOfThread(thread); } +void +CArch::setInterruptHandler(InterruptFunc func, void* userData) +{ + return m_mt->setInterruptHandler(func, userData); +} + +void +CArch::interrupt() +{ + return m_mt->interrupt(); +} + CArchSocket CArch::newSocket(EAddressFamily family, ESocketType type) { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index aad9fefb..9e80d174 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -122,6 +122,8 @@ public: virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread); + virtual void setInterruptHandler(InterruptFunc, void*); + virtual void interrupt(); // IArchNetwork overrides virtual CArchSocket newSocket(EAddressFamily, ESocketType); diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 707ef0fb..707f4dfd 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -83,7 +83,9 @@ CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL; CArchMultithreadPosix::CArchMultithreadPosix() : m_newThreadCalled(false), - m_nextID(0) + m_nextID(0), + m_signalFunc(NULL), + m_signalUserData(NULL) { assert(s_instance == NULL); @@ -558,6 +560,28 @@ CArchMultithreadPosix::getIDOfThread(CArchThread thread) return thread->m_id; } +void +CArchMultithreadPosix::setInterruptHandler(InterruptFunc func, void* userData) +{ + lockMutex(m_threadMutex); + m_signalFunc = func; + m_signalUserData = userData; + unlockMutex(m_threadMutex); +} + +void +CArchMultithreadPosix::interrupt() +{ + lockMutex(m_threadMutex); + if (m_signalFunc != NULL) { + m_signalFunc(m_signalUserData); + } + else { + ARCH->cancelThread(m_mainThread); + } + unlockMutex(m_threadMutex); +} + void CArchMultithreadPosix::startSignalHandler() { @@ -578,7 +602,7 @@ CArchMultithreadPosix::startSignalHandler() if (status == 0) { status = pthread_create(&m_signalThread, &attr, &CArchMultithreadPosix::threadSignalHandler, - m_mainThread); + NULL); pthread_attr_destroy(&attr); } if (status != 0) { @@ -735,10 +759,8 @@ CArchMultithreadPosix::threadCancel(int) } void* -CArchMultithreadPosix::threadSignalHandler(void* vrep) +CArchMultithreadPosix::threadSignalHandler(void*) { - CArchThreadImpl* mainThread = reinterpret_cast(vrep); - // detach pthread_detach(pthread_self()); @@ -768,8 +790,7 @@ CArchMultithreadPosix::threadSignalHandler(void* vrep) sigwait(&sigset); #endif - // if we get here then the signal was raised. cancel the main - // thread so it can shut down cleanly. - ARCH->cancelThread(mainThread); + // if we get here then the signal was raised + ARCH->interrupt(); } } diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h index b0a1ca36..4b87f0a0 100644 --- a/lib/arch/CArchMultithreadPosix.h +++ b/lib/arch/CArchMultithreadPosix.h @@ -61,6 +61,8 @@ public: virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread); + virtual void setInterruptHandler(InterruptFunc, void*); + virtual void interrupt(); private: void startSignalHandler(); @@ -91,6 +93,8 @@ private: ThreadID m_nextID; pthread_t m_signalThread; + InterruptFunc m_signalFunc; + void* m_signalUserData; }; #endif diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 7de3db05..bb8dae02 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -437,6 +437,10 @@ CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) ARCH->testCancelThread(); continue; } + else if (errno == EAGAIN) { + n = 0; + break; + } throwError(errno); } } while (false); @@ -458,6 +462,11 @@ CArchNetworkBSD::writeSocket(CArchSocket s, const void* buf, size_t len) ARCH->testCancelThread(); continue; } + else if (errno == EAGAIN) { + // no buffer space + n = 0; + break; + } throwError(errno); } } while (false); @@ -794,6 +803,8 @@ CArchNetworkBSD::throwError(int err) throw XArchNetworkNotConnected(new XArchEvalUnix(err)); case EPIPE: + throw XArchNetworkShutdown(new XArchEvalUnix(err)); + case ECONNABORTED: case ECONNRESET: throw XArchNetworkDisconnected(new XArchEvalUnix(err)); diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index d8319213..c06d4cfa 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -268,6 +268,18 @@ public: */ virtual ThreadID getIDOfThread(CArchThread thread) = 0; + //! Set the interrupt handler + /*! + Sets the function to call on receipt of an external interrupt. + By default and when \p func is NULL, the main thread is cancelled. + */ + typedef void (*InterruptFunc)(void*); + virtual void setInterruptHandler(InterruptFunc func, + void* userData) = 0; + + //! Invoke the interrupt handler + virtual void interrupt() = 0; + //@} }; diff --git a/lib/arch/XArch.h b/lib/arch/XArch.h index 44c9394d..027868ad 100644 --- a/lib/arch/XArch.h +++ b/lib/arch/XArch.h @@ -89,7 +89,7 @@ library to indicate various errors. */ XARCH_SUBCLASS(XArchNetwork, XArch); -//! Network insufficient permission +//! Operation would block XARCH_SUBCLASS(XArchNetworkWouldBlock, XArchNetwork); //! Network insufficient permission @@ -116,6 +116,9 @@ XARCH_SUBCLASS(XArchNetworkNoRoute, XArchNetwork); //! Socket not connected XARCH_SUBCLASS(XArchNetworkNotConnected, XArchNetwork); +//! Remote read end of socket has closed +XARCH_SUBCLASS(XArchNetworkShutdown, XArchNetwork); + //! Remote end of socket has disconnected XARCH_SUBCLASS(XArchNetworkDisconnected, XArchNetwork); diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp index ce8c0058..9c6d6ca4 100644 --- a/lib/base/CEventQueue.cpp +++ b/lib/base/CEventQueue.cpp @@ -16,37 +16,31 @@ #include "IEventJob.h" #include "CArch.h" +// interrupt handler. this just adds a quit event to the queue. +static +void +interrupt(void*) +{ + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + + // // CEventQueue // -static int g_systemTarget = 0; -CEventQueue* CEventQueue::s_instance = NULL; - CEventQueue::CEventQueue() { - assert(s_instance == NULL); - s_instance = this; - m_mutex = ARCH->newMutex(); + setInstance(this); + m_mutex = ARCH->newMutex(); + ARCH->setInterruptHandler(&interrupt, NULL); } CEventQueue::~CEventQueue() { + ARCH->setInterruptHandler(NULL, NULL); ARCH->closeMutex(m_mutex); - s_instance = NULL; -} - -void* -CEventQueue::getSystemTarget() -{ - // any unique arbitrary pointer will do - return &g_systemTarget; -} - -CEventQueue* -CEventQueue::getInstance() -{ - return s_instance; + setInstance(NULL); } bool @@ -83,7 +77,7 @@ bool CEventQueue::dispatchEvent(const CEvent& event) { void* target = event.getTarget(); - IEventJob* job = getHandler(target); + IEventJob* job = getHandler(event.getType(), target); if (job != NULL) { job->run(event); return true; @@ -164,16 +158,56 @@ void CEventQueue::adoptHandler(void* target, IEventJob* handler) { CArchMutexLock lock(m_mutex); - IEventJob*& job = m_handlers[target]; - delete job; - job = handler; + doAdoptHandler(CEvent::kUnknown, target, handler); +} + +void +CEventQueue::adoptHandler(CEvent::Type type, void* target, IEventJob* handler) +{ + assert(type != CEvent::kUnknown); + CArchMutexLock lock(m_mutex); + doAdoptHandler(type, target, handler); } IEventJob* CEventQueue::orphanHandler(void* target) { CArchMutexLock lock(m_mutex); - CHandlerTable::iterator index = m_handlers.find(target); + return doOrphanHandler(CEvent::kUnknown, target); +} + +IEventJob* +CEventQueue::orphanHandler(CEvent::Type type, void* target) +{ + assert(type != CEvent::kUnknown); + CArchMutexLock lock(m_mutex); + return doOrphanHandler(type, target); +} + +void +CEventQueue::removeHandler(void* target) +{ + delete orphanHandler(target); +} + +void +CEventQueue::removeHandler(CEvent::Type type, void* target) +{ + delete orphanHandler(type, target); +} + +void +CEventQueue::doAdoptHandler(CEvent::Type type, void* target, IEventJob* handler) +{ + IEventJob*& job = m_handlers[CTypeTarget(type, target)]; + delete job; + job = handler; +} + +IEventJob* +CEventQueue::doOrphanHandler(CEvent::Type type, void* target) +{ + CHandlerTable::iterator index = m_handlers.find(CTypeTarget(type, target)); if (index != m_handlers.end()) { IEventJob* handler = index->second; m_handlers.erase(index); @@ -191,16 +225,19 @@ CEventQueue::isEmpty() const } IEventJob* -CEventQueue::getHandler(void* target) const +CEventQueue::getHandler(CEvent::Type type, void* target) const { CArchMutexLock lock(m_mutex); - CHandlerTable::const_iterator index = m_handlers.find(target); + CHandlerTable::const_iterator index = + m_handlers.find(CTypeTarget(type, target)); if (index != m_handlers.end()) { return index->second; } - else { - return NULL; + index = m_handlers.find(CTypeTarget(CEvent::kUnknown, target)); + if (index != m_handlers.end()) { + return index->second; } + return NULL; } UInt32 @@ -217,7 +254,7 @@ CEventQueue::saveEvent(const CEvent& event) } else { // make a new id - id = static_cast(m_oldEventIDs.size()); + id = static_cast(m_events.size()); } // save data @@ -309,7 +346,31 @@ CEventQueue::getNextTimerTimeout() const // -// CXWindowsScreen::CTimer +// CEventQueue::CTypeTarget +// + +CEventQueue::CTypeTarget::CTypeTarget(CEvent::Type type, void* target) : + m_type(type), + m_target(target) +{ + // do nothing +} + +CEventQueue::CTypeTarget::~CTypeTarget() +{ + // do nothing +} + +bool +CEventQueue::CTypeTarget::operator<(const CTypeTarget& tt) const +{ + return (m_type < tt.m_type || + (m_type == tt.m_type && m_target < tt.m_target)); +} + + +// +// CEventQueue::CTimer // CEventQueue::CTimer::CTimer(CEventQueueTimer* timer, diff --git a/lib/base/CEventQueue.h b/lib/base/CEventQueue.h index e6ac9ee2..5443c394 100644 --- a/lib/base/CEventQueue.h +++ b/lib/base/CEventQueue.h @@ -33,27 +33,6 @@ public: CEventQueue(); virtual ~CEventQueue(); - //! @name manipulators - //@{ - - //@} - //! @name accessors - //@{ - - //! Get the system event type target - /*! - Returns the target to use for dispatching \c CEvent::kSystem events. - */ - static void* getSystemTarget(); - - //! Get the singleton instance - /*! - Returns the singleton instance of the event queue - */ - static CEventQueue* getInstance(); - - //@} - // IEventQueue overrides virtual bool getEvent(CEvent& event, double timeout = -1.0); virtual bool dispatchEvent(const CEvent& event); @@ -64,9 +43,14 @@ public: newOneShotTimer(double duration, void* target = NULL); virtual void deleteTimer(CEventQueueTimer*); virtual void adoptHandler(void* target, IEventJob* dispatcher); + virtual void adoptHandler(CEvent::Type type, + void* target, IEventJob* handler); virtual IEventJob* orphanHandler(void* target); + virtual IEventJob* orphanHandler(CEvent::Type type, void* target); + virtual void removeHandler(void* target); + virtual void removeHandler(CEvent::Type type, void* target); virtual bool isEmpty() const; - virtual IEventJob* getHandler(void* target) const; + virtual IEventJob* getHandler(CEvent::Type type, void* target) const; protected: //! @name manipulators @@ -137,11 +121,26 @@ protected: //@} private: + void doAdoptHandler(CEvent::Type type, + void* target, IEventJob* handler); + IEventJob* doOrphanHandler(CEvent::Type type, void* target); + UInt32 saveEvent(const CEvent& event); bool hasTimerExpired(CEvent& event); double getNextTimerTimeout() const; private: + class CTypeTarget { + public: + CTypeTarget(CEvent::Type type, void* target); + ~CTypeTarget(); + + bool operator<(const CTypeTarget&) const; + + private: + CEvent::Type m_type; + void* m_target; + }; class CTimer { public: CTimer(CEventQueueTimer*, double timeout, void* target, bool oneShot); @@ -172,9 +171,7 @@ private: typedef CPriorityQueue CTimerQueue; typedef std::map CEventTable; typedef std::vector CEventIDList; - typedef std::map CHandlerTable; - - static CEventQueue* s_instance; + typedef std::map CHandlerTable; CArchMutex m_mutex; diff --git a/lib/io/CInputStreamFilter.cpp b/lib/base/IEventQueue.cpp similarity index 52% rename from lib/io/CInputStreamFilter.cpp rename to lib/base/IEventQueue.cpp index 6d058b4a..a10621f2 100644 --- a/lib/io/CInputStreamFilter.cpp +++ b/lib/base/IEventQueue.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman + * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -12,28 +12,32 @@ * GNU General Public License for more details. */ -#include "CInputStreamFilter.h" +#include "IEventQueue.h" // -// CInputStreamFilter +// IEventQueue // -CInputStreamFilter::CInputStreamFilter(IInputStream* stream, bool adopted) : - m_stream(stream), - m_adopted(adopted) +static int g_systemTarget = 0; +IEventQueue* IEventQueue::s_instance = NULL; + +void* +IEventQueue::getSystemTarget() { - assert(m_stream != NULL); + // any unique arbitrary pointer will do + return &g_systemTarget; } -CInputStreamFilter::~CInputStreamFilter() +IEventQueue* +IEventQueue::getInstance() { - if (m_adopted) { - delete m_stream; - } + assert(s_instance != NULL); + return s_instance; } -IInputStream* -CInputStreamFilter::getStream() const +void +IEventQueue::setInstance(IEventQueue* instance) { - return m_stream; + assert(s_instance == NULL || instance == NULL); + s_instance = instance; } diff --git a/lib/base/IEventQueue.h b/lib/base/IEventQueue.h index aaaf9f33..0d144ebf 100644 --- a/lib/base/IEventQueue.h +++ b/lib/base/IEventQueue.h @@ -16,9 +16,10 @@ #define IEVENTQUEUE_H #include "IInterface.h" -#include "BasicTypes.h" +#include "CEvent.h" + +#define EVENTQUEUE IEventQueue::getInstance() -class CEvent; class IEventJob; // Opaque type for timer info. This is defined by subclasses of @@ -107,9 +108,21 @@ public: /*! Registers an event handler for \p target. The \p handler is adopted. Any existing handler for the target is deleted. + \c dispatchEvent() will invoke \p handler for any event for + \p target that doesn't have a type specific handler. */ virtual void adoptHandler(void* target, IEventJob* handler) = 0; + //! Register an event handler for an event type + /*! + Registers an event handler for \p type and \p target. The \p handler + is adopted. Any existing handler for the type,target pair is deleted. + \c dispatchEvent() will invoke \p handler for any event for \p target + of type \p type. + */ + virtual void adoptHandler(CEvent::Type type, + void* target, IEventJob* handler) = 0; + //! Unregister an event handler /*! Unregisters an event handler for \p target and returns it. @@ -118,6 +131,27 @@ public: */ virtual IEventJob* orphanHandler(void* target) = 0; + //! Unregister an event handler for an event type + /*! + Unregisters an event handler for the \p type, \p target pair and + returns it. Returns NULL if there was no such handler. The + client becomes responsible for deleting the returned handler. + */ + virtual IEventJob* orphanHandler(CEvent::Type type, void* target) = 0; + + //! Unregister an event handler + /*! + Unregisters an event handler for \p target and deletes it. + */ + virtual void removeHandler(void* target) = 0; + + //! Unregister an event handler for an event type + /*! + Unregisters an event handler for the \p type, \p target pair and + deletes it. + */ + virtual void removeHandler(CEvent::Type type, void* target) = 0; + //@} //! @name accessors //@{ @@ -131,12 +165,40 @@ public: //! Get an event handler /*! - Finds and returns the event handler for \p target, or NULL if - there is no such handler. + Finds and returns the event handler for the \p type, \p target pair. + If there is no such handler, returns the handler for \p target. If + that doesn't exist, returns NULL. */ - virtual IEventJob* getHandler(void* target) const = 0; + virtual IEventJob* getHandler(CEvent::Type type, void* target) const = 0; + + //! Get the system event type target + /*! + Returns the target to use for dispatching \c CEvent::kSystem events. + */ + static void* getSystemTarget(); + + //! Get the singleton instance + /*! + Returns the singleton instance of the event queue + */ + static IEventQueue* getInstance(); //@} + +protected: + //! @name manipulators + //@{ + + //! Set the singleton instance + /*! + Sets the singleton instance of the event queue + */ + static void setInstance(IEventQueue*); + + //@} + +private: + static IEventQueue* s_instance; }; #endif diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index db17a892..f146cb11 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -35,6 +35,7 @@ libbase_a_SOURCES = \ CStopwatch.cpp \ CStringUtil.cpp \ CUnicode.cpp \ + IEventQueue.cpp \ LogOutputters.cpp \ XBase.cpp \ CEvent.h \ diff --git a/lib/io/CBufferedInputStream.cpp b/lib/io/CBufferedInputStream.cpp deleted file mode 100644 index 87cf78a3..00000000 --- a/lib/io/CBufferedInputStream.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CBufferedInputStream.h" -#include "CLock.h" -#include "CMutex.h" -#include "CThread.h" -#include "CStopwatch.h" -#include "IJob.h" -#include "XIO.h" -#include - -// -// CBufferedInputStream -// - -CBufferedInputStream::CBufferedInputStream( - CMutex* mutex, IJob* adoptedEmptyCB, IJob* adoptedCloseCB) : - m_mutex(mutex), - m_empty(mutex, true), - m_emptyCB(adoptedEmptyCB), - m_closeCB(adoptedCloseCB), - m_closed(false), - m_hungup(false) -{ - assert(m_mutex != NULL); -} - -CBufferedInputStream::~CBufferedInputStream() -{ - delete m_closeCB; - delete m_emptyCB; -} - -void -CBufferedInputStream::write(const void* buffer, UInt32 n) -{ - if (!m_hungup && n > 0) { - m_buffer.write(buffer, n); - m_empty = (m_buffer.getSize() == 0); - m_empty.broadcast(); - } -} - -void -CBufferedInputStream::hangup() -{ - m_hungup = true; - m_empty.broadcast(); -} - -UInt32 -CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout) -{ - if (m_closed) { - throw XIOClosed(); - } - if (n == 0) { - return n; - } - - // wait for data, hangup, or timeout - CStopwatch timer(true); - while (!m_hungup && m_empty == true) { - if (!m_empty.wait(timer, timeout)) { - // timed out - return (UInt32)-1; - } - } - - // read data - const UInt32 count = m_buffer.getSize(); - if (n > count) { - n = count; - } - if (n > 0) { - if (buffer != NULL) { - memcpy(buffer, m_buffer.peek(n), n); - } - m_buffer.pop(n); - } - - // update empty state - if (m_buffer.getSize() == 0) { - m_empty = true; - m_empty.broadcast(); - if (m_emptyCB != NULL) { - m_emptyCB->run(); - } - } - return n; -} - -UInt32 -CBufferedInputStream::getSizeNoLock() const -{ - return m_buffer.getSize(); -} - -void -CBufferedInputStream::close() -{ - CLock lock(m_mutex); - if (m_closed) { - throw XIOClosed(); - } - - m_closed = true; - m_hungup = true; - m_buffer.pop(m_buffer.getSize()); - m_empty.broadcast(); - if (m_closeCB != NULL) { - m_closeCB->run(); - } -} - -UInt32 -CBufferedInputStream::read(void* buffer, UInt32 n, double timeout) -{ - CLock lock(m_mutex); - return readNoLock(buffer, n, timeout); -} - -UInt32 -CBufferedInputStream::getSize() const -{ - CLock lock(m_mutex); - return getSizeNoLock(); -} diff --git a/lib/io/CBufferedInputStream.h b/lib/io/CBufferedInputStream.h deleted file mode 100644 index 01137563..00000000 --- a/lib/io/CBufferedInputStream.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CBUFFEREDINPUTSTREAM_H -#define CBUFFEREDINPUTSTREAM_H - -#include "IInputStream.h" -#include "CStreamBuffer.h" -#include "CCondVar.h" - -class CMutex; -class IJob; - -//! Memory buffer input stream -/*! -This class provides an input stream that reads from a memory buffer. -It also provides a means for the owner to ensure thread safe access. -Typically, an owner object will make this object visible to clients -that need access to an IInputStream while using the CBufferedInputStream -methods to write data to the stream. -*/ -class CBufferedInputStream : public IInputStream { -public: - /*! - The \c mutex must not be NULL and will be used to ensure thread - safe access. If \c adoptedCloseCB is not NULL it will be called - when close() is called, allowing the creator to detect the close. - If adoptedEmptyCB is not NULL, it will be called whenever the - buffer becomes empty (except it won't be called by the c'tor nor - when the buffer is closed). - */ - CBufferedInputStream(CMutex* mutex, - IJob* adoptedEmptyCB, IJob* adoptedCloseCB); - ~CBufferedInputStream(); - - //! @name manipulators - //@{ - - //! Write data to stream - /*! - Write \c n bytes from \c buffer to the stream. The mutex must - be locked before calling this. - */ - void write(const void* buffer, UInt32 n); - - //! Hangup stream - /*! - Causes read() to always return immediately. If there is no - more data to read then it returns 0. Further writes are discarded. - The mutex must be locked before calling this. - */ - void hangup(); - - //! Read from stream - /*! - This is the same as read() but the mutex must be locked before - calling this. - */ - UInt32 readNoLock(void*, UInt32 n, double timeout); - - //@} - //! @name accessors - //@{ - - //! Get remaining size of stream - /*! - This is the same as getSize() but the mutex must be locked before - calling this. - */ - UInt32 getSizeNoLock() const; - - //@} - - // IInputStream overrides - // these all lock the mutex for their duration - virtual void close(); - virtual UInt32 read(void*, UInt32 n, double timeout); - virtual UInt32 getSize() const; - -private: - CMutex* m_mutex; - CCondVar m_empty; - IJob* m_emptyCB; - IJob* m_closeCB; - CStreamBuffer m_buffer; - bool m_closed; - bool m_hungup; -}; - -#endif diff --git a/lib/io/CBufferedOutputStream.cpp b/lib/io/CBufferedOutputStream.cpp deleted file mode 100644 index 2b666d1f..00000000 --- a/lib/io/CBufferedOutputStream.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CBufferedOutputStream.h" -#include "XIO.h" -#include "CLock.h" -#include "CMutex.h" -#include "CThread.h" -#include "IJob.h" - -// -// CBufferedOutputStream -// - -CBufferedOutputStream::CBufferedOutputStream( - CMutex* mutex, IJob* adoptedFillCB, IJob* adoptedCloseCB) : - m_mutex(mutex), - m_fillCB(adoptedFillCB), - m_closeCB(adoptedCloseCB), - m_empty(mutex, true), - m_closed(false) -{ - assert(m_mutex != NULL); -} - -CBufferedOutputStream::~CBufferedOutputStream() -{ - delete m_closeCB; - delete m_fillCB; -} - -const void* -CBufferedOutputStream::peek(UInt32 n) -{ - return m_buffer.peek(n); -} - -void -CBufferedOutputStream::pop(UInt32 n) -{ - m_buffer.pop(n); - if (m_buffer.getSize() == 0) { - m_empty.broadcast(); - } -} - -UInt32 -CBufferedOutputStream::getSize() const -{ - return m_buffer.getSize(); -} - -void -CBufferedOutputStream::close() -{ - CLock lock(m_mutex); - if (m_closed) { - throw XIOClosed(); - } - - m_closed = true; - if (m_closeCB != NULL) { - m_closeCB->run(); - } -} - -UInt32 -CBufferedOutputStream::write(const void* buffer, UInt32 n) -{ - CLock lock(m_mutex); - if (m_closed) { - throw XIOClosed(); - } - - bool wasEmpty = (m_buffer.getSize() == 0); - m_buffer.write(buffer, n); - if (wasEmpty && n > 0) { - if (m_fillCB != NULL) { - m_fillCB->run(); - } - } - return n; -} - -void -CBufferedOutputStream::flush() -{ - // wait until all data is written - CLock lock(m_mutex); - while (m_buffer.getSize() > 0) { - m_empty.wait(); - } -} diff --git a/lib/io/CBufferedOutputStream.h b/lib/io/CBufferedOutputStream.h deleted file mode 100644 index 1853a161..00000000 --- a/lib/io/CBufferedOutputStream.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CBUFFEREDOUTPUTSTREAM_H -#define CBUFFEREDOUTPUTSTREAM_H - -#include "IOutputStream.h" -#include "CStreamBuffer.h" -#include "CCondVar.h" - -class CMutex; -class IJob; - -//! Memory buffer output stream -/*! -This class provides an output stream that writes to a memory buffer. -It also provides a means for the owner to ensure thread safe access. -Typically, an owner object will make this object visible to clients -that need access to an IOutputStream while using the CBufferedOutputStream -methods to read the data written to the stream. -*/ -class CBufferedOutputStream : public IOutputStream { -public: - /*! - The \c mutex must not be NULL and will be used to ensure thread - safe access. If \c adoptedCloseCB is not NULL it will be called - when close() is called, allowing the creator to detect the close. - If \c adoptedFillCB is not NULL, it will be called whenever the - buffer becomes non-empty. - */ - CBufferedOutputStream(CMutex* mutex, - IJob* adoptedFillCB, IJob* adoptedCloseCB); - ~CBufferedOutputStream(); - - //! @name manipulators - //@{ - - //! Read data without removing from buffer - /*! - Returns a buffer of \c n bytes (which must be <= getSize()). The - caller must not modify the buffer nor delete it. The mutex must - be locked before calling this. - */ - const void* peek(UInt32 n); - - //! Discard data - /*! - Discards the next \c n bytes. If \c n >= getSize() then the buffer - is cleared. The mutex must be locked before calling this. - */ - void pop(UInt32 n); - - //@} - //! @name accessors - //@{ - - //! Get size of buffer - /*! - Returns the number of bytes in the buffer. The mutex must be locked - before calling this. - */ - UInt32 getSize() const; - - //@} - - // IOutputStream overrides - // these all lock the mutex for their duration - virtual void close(); - virtual UInt32 write(const void*, UInt32 n); - virtual void flush(); - -private: - CMutex* m_mutex; - IJob* m_fillCB; - IJob* m_closeCB; - CCondVar m_empty; - CStreamBuffer m_buffer; - bool m_closed; -}; - -#endif diff --git a/lib/io/CInputStreamFilter.h b/lib/io/CInputStreamFilter.h deleted file mode 100644 index 9274a810..00000000 --- a/lib/io/CInputStreamFilter.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CINPUTSTREAMFILTER_H -#define CINPUTSTREAMFILTER_H - -#include "IInputStream.h" - -//! A filtering input stream -/*! -This class wraps an input stream. Subclasses provide indirect access -to the stream, typically performing some filtering. -*/ -class CInputStreamFilter : public IInputStream { -public: - /*! - Create a wrapper around \c stream. Iff \c adoptStream is true then - this object takes ownership of the stream and will delete it in the - d'tor. - */ - CInputStreamFilter(IInputStream* stream, bool adoptStream = true); - ~CInputStreamFilter(); - - // IInputStream overrides - virtual void close() = 0; - virtual UInt32 read(void*, UInt32 n, double timeout) = 0; - virtual UInt32 getSize() const = 0; - -protected: - //! Get the stream - /*! - Returns the stream passed to the c'tor. - */ - IInputStream* getStream() const; - -private: - IInputStream* m_stream; - bool m_adopted; -}; - -#endif diff --git a/lib/io/COutputStreamFilter.cpp b/lib/io/COutputStreamFilter.cpp deleted file mode 100644 index d90465a7..00000000 --- a/lib/io/COutputStreamFilter.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "COutputStreamFilter.h" - -// -// COutputStreamFilter -// - -COutputStreamFilter::COutputStreamFilter(IOutputStream* stream, bool adopted) : - m_stream(stream), - m_adopted(adopted) -{ - assert(m_stream != NULL); -} - -COutputStreamFilter::~COutputStreamFilter() -{ - if (m_adopted) { - delete m_stream; - } -} - -IOutputStream* -COutputStreamFilter::getStream() const -{ - return m_stream; -} diff --git a/lib/io/COutputStreamFilter.h b/lib/io/COutputStreamFilter.h deleted file mode 100644 index 027e32dc..00000000 --- a/lib/io/COutputStreamFilter.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef COUTPUTSTREAMFILTER_H -#define COUTPUTSTREAMFILTER_H - -#include "IOutputStream.h" - -//! A filtering output stream -/*! -This class wraps an output stream. Subclasses provide indirect access -to the stream, typically performing some filtering. -*/ -class COutputStreamFilter : public IOutputStream { -public: - /*! - Create a wrapper around \c stream. Iff \c adoptStream is true then - this object takes ownership of the stream and will delete it in the - d'tor. - */ - COutputStreamFilter(IOutputStream* stream, bool adoptStream = true); - ~COutputStreamFilter(); - - // IOutputStream overrides - virtual void close() = 0; - virtual UInt32 write(const void*, UInt32 count) = 0; - virtual void flush() = 0; - -protected: - //! Get the stream - /*! - Returns the stream passed to the c'tor. - */ - IOutputStream* getStream() const; - -private: - IOutputStream* m_stream; - bool m_adopted; -}; - -#endif diff --git a/lib/io/CStreamFilter.cpp b/lib/io/CStreamFilter.cpp new file mode 100644 index 00000000..00c064a0 --- /dev/null +++ b/lib/io/CStreamFilter.cpp @@ -0,0 +1,105 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CStreamFilter.h" + +// +// CStreamFilter +// + +CStreamFilter::CStreamFilter(IStream* stream, bool adoptStream) : + m_stream(stream), + m_adopted(adoptStream) +{ + // do nothing +} + +CStreamFilter::~CStreamFilter() +{ + if (m_adopted) { + delete m_stream; + } +} + +void +CStreamFilter::close() +{ + getStream()->close(); +} + +UInt32 +CStreamFilter::read(void* buffer, UInt32 n) +{ + return getStream()->read(buffer, n); +} + +void +CStreamFilter::write(const void* buffer, UInt32 n) +{ + getStream()->write(buffer, n); +} + +void +CStreamFilter::flush() +{ + getStream()->flush(); +} + +void +CStreamFilter::shutdownInput() +{ + getStream()->shutdownInput(); +} + +void +CStreamFilter::shutdownOutput() +{ + getStream()->shutdownOutput(); +} + +void +CStreamFilter::setEventFilter(IEventJob* filter) +{ + getStream()->setEventFilter(filter); +} + +void* +CStreamFilter::getEventTarget() const +{ + return const_cast(reinterpret_cast(this)); +} + +bool +CStreamFilter::isReady() const +{ + return getStream()->isReady(); +} + +UInt32 +CStreamFilter::getSize() const +{ + return getStream()->getSize(); +} + +IEventJob* +CStreamFilter::getEventFilter() const +{ + return getStream()->getEventFilter(); +} + +IStream* +CStreamFilter::getStream() const +{ + return m_stream; +} diff --git a/lib/io/CStreamFilter.h b/lib/io/CStreamFilter.h new file mode 100644 index 00000000..5706cf4a --- /dev/null +++ b/lib/io/CStreamFilter.h @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSTREAMFILTER_H +#define CSTREAMFILTER_H + +#include "IStream.h" + +//! A stream filter +/*! +This class wraps a stream. Subclasses provide indirect access +to the wrapped stream, typically performing some filtering. +*/ +class CStreamFilter : public IStream { +public: + /*! + Create a wrapper around \c stream. Iff \c adoptStream is true then + this object takes ownership of the stream and will delete it in the + d'tor. + */ + CStreamFilter(IStream* stream, bool adoptStream = true); + ~CStreamFilter(); + + // IStream overrides + // These all just forward to the underlying stream. Override as + // necessary. + virtual void close(); + virtual UInt32 read(void* buffer, UInt32 n); + virtual void write(const void* buffer, UInt32 n); + virtual void flush(); + virtual void shutdownInput(); + virtual void shutdownOutput(); + virtual void setEventFilter(IEventJob* filter); + virtual void* getEventTarget() const; + virtual bool isReady() const; + virtual UInt32 getSize() const; + virtual IEventJob* getEventFilter() const; + +protected: + //! Get the stream + /*! + Returns the stream passed to the c'tor. + */ + IStream* getStream() const; + +private: + IStream* m_stream; + bool m_adopted; +}; + +#endif diff --git a/lib/io/IInputStream.h b/lib/io/IInputStream.h deleted file mode 100644 index a1b250de..00000000 --- a/lib/io/IInputStream.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef IINPUTSTREAM_H -#define IINPUTSTREAM_H - -#include "IInterface.h" -#include "BasicTypes.h" - -//! Input stream interface -/*! -Defines the interface for all input streams. -*/ -class IInputStream : public IInterface { -public: - //! @name manipulators - //@{ - - //! Close the stream - /*! - Closes the stream. Attempting to read() after close() throws - XIOClosed and getSize() always returns zero. - */ - virtual void close() = 0; - - //! Read from stream - /*! - Read up to \c n bytes into buffer, returning the number read. - Blocks for up to \c timeout seconds if no data is available but does - not wait if any data is available, even if less than \c n bytes. - If \c timeout < 0 then it blocks indefinitely until data is available. - If \c buffer is NULL then the data is discarded. Returns (UInt32)-1 if - it times out and 0 if no data is available and the other end of the - stream has hungup. - - (cancellation point) - */ - virtual UInt32 read(void* buffer, UInt32 n, double timeout) = 0; - - //@} - //! @name accessors - //@{ - - //! Get remaining size of stream - /*! - Returns a conservative estimate of the available bytes to read - (i.e. a number not greater than the actual number of bytes). - Some streams may not be able to determine this and will always - return zero. - */ - virtual UInt32 getSize() const = 0; - - //@} -}; - -#endif diff --git a/lib/io/IOutputStream.h b/lib/io/IOutputStream.h deleted file mode 100644 index 5540ce0a..00000000 --- a/lib/io/IOutputStream.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef IOUTPUTSTREAM_H -#define IOUTPUTSTREAM_H - -#include "IInterface.h" -#include "BasicTypes.h" - -//! Output stream interface -/*! -Defines the interface for all output streams. -*/ -class IOutputStream : public IInterface { -public: - //! @name manipulators - //@{ - - //! Close the stream - /*! - Closes the stream. Attempting to write() after close() throws - XIOClosed. - */ - virtual void close() = 0; - - //! Write to stream - /*! - Write \c n bytes from \c buffer to the stream. If this can't - complete immeditely it will block. If cancelled, an indeterminate - amount of data may have been written. - - (cancellation point) - */ - virtual UInt32 write(const void* buffer, UInt32 n) = 0; - - //! Flush the stream - /*! - Waits until all buffered data has been written to the stream. - - (cancellation point) - */ - virtual void flush() = 0; - - //@} -}; - -#endif diff --git a/lib/io/IStream.cpp b/lib/io/IStream.cpp new file mode 100644 index 00000000..42947190 --- /dev/null +++ b/lib/io/IStream.cpp @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "IStream.h" + +// +// IStream +// + +CEvent::Type IStream::s_inputReadyEvent = CEvent::kUnknown; +CEvent::Type IStream::s_outputFlushedEvent = CEvent::kUnknown; +CEvent::Type IStream::s_outputErrorEvent = CEvent::kUnknown; +CEvent::Type IStream::s_inputShutdownEvent = CEvent::kUnknown; +CEvent::Type IStream::s_outputShutdownEvent = CEvent::kUnknown; + +CEvent::Type +IStream::getInputReadyEvent() +{ + return CEvent::registerTypeOnce(s_inputReadyEvent); +} + +CEvent::Type +IStream::getOutputFlushedEvent() +{ + return CEvent::registerTypeOnce(s_outputFlushedEvent); +} + +CEvent::Type +IStream::getOutputErrorEvent() +{ + return CEvent::registerTypeOnce(s_outputErrorEvent); +} + +CEvent::Type +IStream::getInputShutdownEvent() +{ + return CEvent::registerTypeOnce(s_inputShutdownEvent); +} + +CEvent::Type +IStream::getOutputShutdownEvent() +{ + return CEvent::registerTypeOnce(s_outputShutdownEvent); +} diff --git a/lib/io/IStream.h b/lib/io/IStream.h new file mode 100644 index 00000000..1dc07570 --- /dev/null +++ b/lib/io/IStream.h @@ -0,0 +1,175 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISTREAM_H +#define ISTREAM_H + +#include "IInterface.h" +#include "CEvent.h" + +class IEventJob; + +//! Bidirectional stream interface +/*! +Defines the interface for all streams. +*/ +class IStream : public IInterface { +public: + //! @name manipulators + //@{ + + //! Close the stream + /*! + Closes the stream. Pending input data and buffered output data + are discarded. Use \c flush() before \c close() to send buffered + output data. Attempts to \c read() after a close return 0, + attempts to \c write() generate output error events, and attempts + to \c flush() return immediately. + */ + virtual void close() = 0; + + //! Read from stream + /*! + Read up to \p n bytes into \p buffer, returning the number read + (zero if no data is available or input is shutdown). \p buffer + may be NULL in which case the data is discarded. + */ + virtual UInt32 read(void* buffer, UInt32 n) = 0; + + //! Write to stream + /*! + Write \c n bytes from \c buffer to the stream. If this can't + complete immediately it will block. Data may be buffered in + order to return more quickly. A output error event is generated + when writing fails. + */ + virtual void write(const void* buffer, UInt32 n) = 0; + + //! Flush the stream + /*! + Waits until all buffered data has been written to the stream. + */ + virtual void flush() = 0; + + //! Shutdown input + /*! + Shutdown the input side of the stream. Any pending input data is + discarded and further reads immediately return 0. + */ + virtual void shutdownInput() = 0; + + //! Shutdown output + /*! + Shutdown the output side of the stream. Any buffered output data + is discarded and further writes generate output error events. Use + \c flush() before \c shutdownOutput() to send buffered output data. + */ + virtual void shutdownOutput() = 0; + + //! Set the event filter + /*! + If not NULL, the \p filter is passed any event that would've been + added to the queue. The filter can discard the event, modify it + and add it to the queue, and add other events. The default filter + is NULL. The caller retains ownership of the filter. + */ + virtual void setEventFilter(IEventJob* filter) = 0; + + //@} + + //@} + //! @name accessors + //@{ + + //! Get event target + /*! + Returns the event target for events generated by this stream. + */ + virtual void* getEventTarget() const = 0; + + //! Test if \c read() will succeed + /*! + Returns true iff an immediate \c read() will return data. This + may or may not be the same as \c getSize() > 0, depending on the + stream type. + */ + virtual bool isReady() const = 0; + + //! Get bytes available to read + /*! + Returns a conservative estimate of the available bytes to read + (i.e. a number not greater than the actual number of bytes). + Some streams may not be able to determine this and will always + return zero. + */ + virtual UInt32 getSize() const = 0; + + //! Get the event filter + /*! + Returns the current event filter. + */ + virtual IEventJob* getEventFilter() const = 0; + + //! Get input ready event type + /*! + Returns the input ready event type. A stream sends this event + when \c read() will return with data. + */ + static CEvent::Type getInputReadyEvent(); + + //! Get output flushed event type + /*! + Returns the output flushed event type. A stream sends this event + when the output buffer has been flushed. If there have been no + writes since the event was posted, calling \c shutdownOutput() or + \c close() will not discard any data and \c flush() will return + immediately. + */ + static CEvent::Type getOutputFlushedEvent(); + + //! Get output error event type + /*! + Returns the output error event type. A stream sends this event + when a write has failed. + */ + static CEvent::Type getOutputErrorEvent(); + + //! Get input shutdown event type + /*! + Returns the input shutdown event type. This is sent when the + input side of the stream has shutdown. When the input has + shutdown, no more data will ever be available to read. + */ + static CEvent::Type getInputShutdownEvent(); + + //! Get output shutdown event type + /*! + Returns the output shutdown event type. This is sent when the + output side of the stream has shutdown. When the output has + shutdown, no more data can ever be written to the stream. Any + attempt to do so will generate a output error event. + */ + static CEvent::Type getOutputShutdownEvent(); + + //@} + +private: + static CEvent::Type s_inputReadyEvent; + static CEvent::Type s_outputFlushedEvent; + static CEvent::Type s_outputErrorEvent; + static CEvent::Type s_inputShutdownEvent; + static CEvent::Type s_outputShutdownEvent; +}; + +#endif diff --git a/lib/io/IStreamFilterFactory.h b/lib/io/IStreamFilterFactory.h index 2f459ef5..6e6c86ef 100644 --- a/lib/io/IStreamFilterFactory.h +++ b/lib/io/IStreamFilterFactory.h @@ -17,10 +17,7 @@ #include "IInterface.h" -class CInputStreamFilter; -class COutputStreamFilter; -class IInputStream; -class IOutputStream; +class IStream; //! Stream filter factory interface /*! @@ -28,21 +25,12 @@ This interface provides factory methods to create stream filters. */ class IStreamFilterFactory : public IInterface { public: - //! Create input filter + //! Create filter /*! - Create and return an input stream filter. The caller must delete the - returned object. + Create and return a stream filter on \p stream. The caller must + delete the returned object. */ - virtual CInputStreamFilter* - createInput(IInputStream*, bool adoptStream) = 0; - - //! Create output filter - /*! - Create and return an output stream filter. The caller must delete the - returned object. - */ - virtual COutputStreamFilter* - createOutput(IOutputStream*, bool adoptStream) = 0; + virtual IStream* create(IStream* stream, bool adoptStream) = 0; }; #endif diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index 8bac4c8e..ad4f67fa 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -25,19 +25,13 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libio.a libio_a_SOURCES = \ - CBufferedInputStream.cpp \ - CBufferedOutputStream.cpp \ - CInputStreamFilter.cpp \ - COutputStreamFilter.cpp \ CStreamBuffer.cpp \ + CStreamFilter.cpp \ + IStream.cpp \ XIO.cpp \ - CBufferedInputStream.h \ - CBufferedOutputStream.h \ - CInputStreamFilter.h \ - COutputStreamFilter.h \ CStreamBuffer.h \ - IInputStream.h \ - IOutputStream.h \ + CStreamFilter.h \ + IStream.h \ IStreamFilterFactory.h \ XIO.h \ $(NULL) diff --git a/lib/io/XIO.cpp b/lib/io/XIO.cpp index 710c4190..b7101a4d 100644 --- a/lib/io/XIO.cpp +++ b/lib/io/XIO.cpp @@ -34,3 +34,14 @@ XIOEndOfStream::getWhat() const throw() { return format("XIOEndOfStream", "reached end of stream"); } + + +// +// XIOWouldBlock +// + +CString +XIOWouldBlock::getWhat() const throw() +{ + return format("XIOWouldBlock", "stream operation would block"); +} diff --git a/lib/io/XIO.h b/lib/io/XIO.h index 31871bf5..cc41ef40 100644 --- a/lib/io/XIO.h +++ b/lib/io/XIO.h @@ -39,4 +39,10 @@ Thrown when attempting to read beyond the end of a stream. */ XBASE_SUBCLASS_WHAT(XIOEndOfStream, XIO); +//! I/O would block exception +/*! +Thrown if an operation on a stream would block. +*/ +XBASE_SUBCLASS_WHAT(XIOWouldBlock, XIO); + #endif diff --git a/lib/net/CSocketMultiplexer.cpp b/lib/net/CSocketMultiplexer.cpp new file mode 100644 index 00000000..46191b3c --- /dev/null +++ b/lib/net/CSocketMultiplexer.cpp @@ -0,0 +1,304 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CSocketMultiplexer.h" +#include "ISocketMultiplexerJob.h" +#include "CCondVar.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" +#include "TMethodJob.h" +#include "CArch.h" +#include "XArch.h" +#include "stdvector.h" + +// +// CSocketMultiplexer +// + +CSocketMultiplexer* CSocketMultiplexer::s_instance = NULL; + +CSocketMultiplexer::CSocketMultiplexer() : + m_mutex(new CMutex), + m_pollable(new CCondVar(m_mutex, false)), + m_polling(new CCondVar(m_mutex, false)), + m_thread(NULL), + m_update(false) +{ + assert(s_instance == NULL); + + // this pointer just has to be unique and not NULL. it will + // never be dereferenced. it's used to identify cursor nodes + // in the jobs list. + m_cursorMark = reinterpret_cast(this); + + // start thread + m_thread = new CThread(new TMethodJob( + this, &CSocketMultiplexer::serviceThread)); + + s_instance = this; +} + +CSocketMultiplexer::~CSocketMultiplexer() +{ + m_thread->cancel(); + m_thread->wait(); + delete m_thread; + delete m_polling; + delete m_pollable; + delete m_mutex; + + // clean up jobs + for (CSocketJobMap::iterator i = m_socketJobMap.begin(); + i != m_socketJobMap.end(); ++i) { + delete *(i->second); + } + + s_instance = NULL; +} + +CSocketMultiplexer* +CSocketMultiplexer::getInstance() +{ + return s_instance; +} + +void +CSocketMultiplexer::addSocket(ISocket* socket, ISocketMultiplexerJob* job) +{ + assert(socket != NULL); + assert(job != NULL); + + CLock lock(m_mutex); + + // prevent service thread from restarting poll + *m_pollable = false; + + // break thread out of poll + m_thread->unblock(); + + // wait for poll to finish + while (*m_polling) { + m_polling->wait(); + } + + // insert/replace job + CSocketJobMap::iterator i = m_socketJobMap.find(socket); + if (i == m_socketJobMap.end()) { + // we *must* put the job at the end so the order of jobs in + // the list continue to match the order of jobs in pfds in + // serviceThread(). + CJobCursor j = m_socketJobs.insert(m_socketJobs.end(), job); + m_update = true; + m_socketJobMap.insert(std::make_pair(socket, j)); + } + else { + CJobCursor j = i->second; + if (*j != job) { + delete *j; + *j = job; + } + m_update = true; + } + + // there must be at least one socket so we can poll now + *m_pollable = true; + m_pollable->broadcast(); +} + +void +CSocketMultiplexer::removeSocket(ISocket* socket) +{ + assert(socket != NULL); + + CLock lock(m_mutex); + + // prevent service thread from restarting poll + *m_pollable = false; + + // break thread out of poll + m_thread->unblock(); + + // wait until thread finishes poll + while (*m_polling) { + m_polling->wait(); + } + + // remove job. rather than removing it from the map we put NULL + // in the list instead so the order of jobs in the list continues + // to match the order of jobs in pfds in serviceThread(). + CSocketJobMap::iterator i = m_socketJobMap.find(socket); + if (i != m_socketJobMap.end()) { + if (*(i->second) != NULL) { + delete *(i->second); + *(i->second) = NULL; + m_update = true; + } + } + + *m_pollable = true; + m_pollable->broadcast(); +} + +void +CSocketMultiplexer::serviceThread(void*) +{ + std::vector pfds; + IArchNetwork::CPollEntry pfd; + + // service the connections + for (;;) { + { + CLock lock(m_mutex); + + // wait until pollable + while (!*m_pollable) { + m_pollable->wait(); + } + + // now we're polling + *m_polling = true; + m_polling->broadcast(); + } + + // we're now the only thread that can access m_sockets and + // m_update because m_polling and m_pollable are both true. + // therefore, we don't need to hold the mutex. + + // collect poll entries + if (m_update) { + m_update = false; + pfds.clear(); + pfds.reserve(m_socketJobMap.size()); + + CJobCursor cursor = newCursor(); + CJobCursor jobCursor = nextCursor(cursor); + while (jobCursor != m_socketJobs.end()) { + ISocketMultiplexerJob* job = *jobCursor; + if (job != NULL) { + pfd.m_socket = job->getSocket(); + pfd.m_events = 0; + if (job->isReadable()) { + pfd.m_events |= IArchNetwork::kPOLLIN; + } + if (job->isWritable()) { + pfd.m_events |= IArchNetwork::kPOLLOUT; + } + pfds.push_back(pfd); + } + jobCursor = nextCursor(cursor); + } + deleteCursor(cursor); + } + + int status; + try { + // check for status + status = ARCH->pollSocket(&pfds[0], pfds.size(), -1); + } + catch (XArchNetwork&) { + // FIXME -- uh oh + status = 0; + } + + if (status != 0) { + // iterate over socket jobs, invoking each and saving the + // new job. + UInt32 i = 0; + CJobCursor cursor = newCursor(); + CJobCursor jobCursor = nextCursor(cursor); + while (i < pfds.size() && jobCursor != m_socketJobs.end()) { + if (*jobCursor != NULL) { + // get poll state + unsigned short revents = pfds[i].m_revents; + bool read = ((revents & IArchNetwork::kPOLLIN) != 0); + bool write = ((revents & IArchNetwork::kPOLLOUT) != 0); + bool error = ((revents & (IArchNetwork::kPOLLERR | + IArchNetwork::kPOLLNVAL)) != 0); + + // run job + ISocketMultiplexerJob* job = *jobCursor; + ISocketMultiplexerJob* newJob = job->run(read, write, error); + + // save job, if different + if (newJob != job) { + CLock lock(m_mutex); + delete job; + *jobCursor = newJob; + m_update = true; + } + ++i; + } + + // next job + jobCursor = nextCursor(cursor); + } + deleteCursor(cursor); + } + + // delete any removed socket jobs + CLock lock(m_mutex); + for (CSocketJobMap::iterator i = m_socketJobMap.begin(); + i != m_socketJobMap.end();) { + if (*(i->second) == NULL) { + m_socketJobMap.erase(i++); + m_update = true; + } + else { + ++i; + } + } + if (m_socketJobMap.empty()) { + *m_pollable = false; + } + + // done polling + *m_polling = false; + m_polling->broadcast(); + } +} + +CSocketMultiplexer::CJobCursor +CSocketMultiplexer::newCursor() +{ + CLock lock(m_mutex); + return m_socketJobs.insert(m_socketJobs.begin(), m_cursorMark); +} + +CSocketMultiplexer::CJobCursor +CSocketMultiplexer::nextCursor(CJobCursor cursor) +{ + CLock lock(m_mutex); + ISocketMultiplexerJob* job = NULL; + CJobCursor j = m_socketJobs.end(); + CJobCursor i = cursor; + while (++i != m_socketJobs.end()) { + if (*i != m_cursorMark) { + // found a real job (as opposed to a cursor) + j = i; + + // move our cursor just past the job + m_socketJobs.splice(++i, m_socketJobs, cursor); + break; + } + } + return j; +} + +void +CSocketMultiplexer::deleteCursor(CJobCursor cursor) +{ + CLock lock(m_mutex); + m_socketJobs.erase(cursor); +} diff --git a/lib/net/CSocketMultiplexer.h b/lib/net/CSocketMultiplexer.h new file mode 100644 index 00000000..8abd57ba --- /dev/null +++ b/lib/net/CSocketMultiplexer.h @@ -0,0 +1,95 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSOCKETMULTIPLEXER_H +#define CSOCKETMULTIPLEXER_H + +#include "IArchNetwork.h" +#include "stdlist.h" +#include "stdmap.h" + +template +class CCondVar; +class CMutex; +class CThread; +class ISocket; +class ISocketMultiplexerJob; + +//! Socket multiplexer +/*! +A socket multiplexer services multiple sockets simultaneously. +*/ +class CSocketMultiplexer { +public: + CSocketMultiplexer(); + ~CSocketMultiplexer(); + + //! @name manipulators + //@{ + + void addSocket(ISocket*, ISocketMultiplexerJob*); + + void removeSocket(ISocket*); + + //@} + //! @name accessors + //@{ + + // maybe belongs on ISocketMultiplexer + static CSocketMultiplexer* + getInstance(); + + //@} + +private: + // list of jobs. we use a list so we can safely iterate over it + // while other threads modify it. + typedef std::list CSocketJobs; + typedef CSocketJobs::iterator CJobCursor; + typedef std::map CSocketJobMap; + + // service sockets. the service thread will only access m_sockets + // and m_update while m_pollable and m_polling are true. all other + // threads must only modify these when m_pollable and m_polling are + // false. only the service thread sets m_polling. + void serviceThread(void*); + + // create, iterate, and destroy a cursor. a cursor is used to + // safely iterate through the job list while other threads modify + // the list. it works by inserting a dummy item in the list and + // moving that item through the list. the dummy item will never + // be removed by other edits so an iterator pointing at the item + // remains valid until we remove the dummy item in deleteCursor(). + // nextCursor() finds the next non-dummy item, moves our dummy + // item just past it, and returns an iterator for the non-dummy + // item. all cursor calls lock the mutex for their duration. + CJobCursor newCursor(); + CJobCursor nextCursor(CJobCursor); + void deleteCursor(CJobCursor); + +private: + CMutex* m_mutex; + CCondVar* m_pollable; + CCondVar* m_polling; + CThread* m_thread; + bool m_update; + + CSocketJobs m_socketJobs; + CSocketJobMap m_socketJobMap; + ISocketMultiplexerJob* m_cursorMark; + + static CSocketMultiplexer* s_instance; +}; + +#endif diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index fb35f9d1..49695446 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -30,8 +30,7 @@ // CTCPListenSocket // -CTCPListenSocket::CTCPListenSocket() : - m_target(NULL) +CTCPListenSocket::CTCPListenSocket() { m_mutex = new CMutex; try { @@ -76,21 +75,6 @@ CTCPListenSocket::bind(const CNetworkAddress& addr) } } -IDataSocket* -CTCPListenSocket::accept() -{ - try { - CSocketMultiplexer::getInstance()->addSocket(this, - new TSocketMultiplexerMethodJob( - this, &CTCPListenSocket::serviceListening, - m_socket, true, false)); - return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); - } - catch (XArchNetwork&) { - return NULL; - } -} - void CTCPListenSocket::close() { @@ -108,11 +92,25 @@ CTCPListenSocket::close() } } -void -CTCPListenSocket::setEventTarget(void* target) +void* +CTCPListenSocket::getEventTarget() const { - CLock lock(m_mutex); - m_target = target; + return const_cast(reinterpret_cast(this)); +} + +IDataSocket* +CTCPListenSocket::accept() +{ + try { + CSocketMultiplexer::getInstance()->addSocket(this, + new TSocketMultiplexerMethodJob( + this, &CTCPListenSocket::serviceListening, + m_socket, true, false)); + return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); + } + catch (XArchNetwork&) { + return NULL; + } } ISocketMultiplexerJob* @@ -125,7 +123,7 @@ CTCPListenSocket::serviceListening(ISocketMultiplexerJob* job, } if (read) { CEventQueue::getInstance()->addEvent( - CEvent(getConnectingEvent(), m_target, NULL)); + CEvent(getConnectingEvent(), this, NULL)); // stop polling on this socket until the client accepts return NULL; } diff --git a/lib/net/CTCPListenSocket.h b/lib/net/CTCPListenSocket.h index 8c582399..5321b8db 100644 --- a/lib/net/CTCPListenSocket.h +++ b/lib/net/CTCPListenSocket.h @@ -33,7 +33,7 @@ public: // ISocket overrides virtual void bind(const CNetworkAddress&); virtual void close(); - virtual void setEventTarget(void*); + virtual void* getEventTarget() const; // IListenSocket overrides virtual IDataSocket* accept(); @@ -46,7 +46,6 @@ private: private: CArchSocket m_socket; CMutex* m_mutex; - void* m_target; }; #endif diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 7ab154b1..cdfed02e 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -16,14 +16,10 @@ #include "CNetworkAddress.h" #include "CSocketMultiplexer.h" #include "TSocketMultiplexerMethodJob.h" -#include "CBufferedInputStream.h" -#include "CBufferedOutputStream.h" #include "XSocket.h" -#include "XIO.h" #include "CLock.h" -#include "CMutex.h" #include "CEventQueue.h" -#include "TMethodJob.h" +#include "IEventJob.h" #include "CArch.h" #include "XArch.h" @@ -31,7 +27,10 @@ // CTCPSocket // -CTCPSocket::CTCPSocket() +CTCPSocket::CTCPSocket() : + m_mutex(), + m_flushed(&m_mutex, true), + m_eventFilter(NULL) { try { m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); @@ -39,34 +38,32 @@ CTCPSocket::CTCPSocket() catch (XArchNetwork& e) { throw XSocketCreate(e.what()); } + init(); } CTCPSocket::CTCPSocket(CArchSocket socket) : - m_socket(socket) + m_mutex(), + m_socket(socket), + m_flushed(&m_mutex, true), + m_eventFilter(NULL) { assert(m_socket != NULL); // socket starts in connected state init(); - setState(kReadWrite, true); + onConnected(); + setJob(newJob()); } CTCPSocket::~CTCPSocket() { try { - if (m_socket != NULL) { - close(); - } + close(); } catch (...) { // ignore } - - // clean up - delete m_input; - delete m_output; - delete m_mutex; } void @@ -86,92 +83,202 @@ CTCPSocket::bind(const CNetworkAddress& addr) void CTCPSocket::close() { - // flush buffers - m_output->flush(); + CLock lock(&m_mutex); - // now closed - setState(kClosed, true); + // clear buffers and enter disconnected state + if (m_connected) { + sendSocketEvent(getDisconnectedEvent()); + } + onDisconnected(); - // close buffers - try { - m_input->close(); - } - catch (...) { - // ignore - } - try { - m_output->close(); - } - catch (...) { - // ignore - } + // remove ourself from the multiplexer + setJob(NULL); - // close socket - CLock lock(m_mutex); + // close the socket if (m_socket != NULL) { + CArchSocket socket = m_socket; + m_socket = NULL; try { - ARCH->closeSocket(m_socket); - m_socket = NULL; + ARCH->closeSocket(socket); } catch (XArchNetwork& e) { - throw XSocketIOClose(e.what()); + // FIXME -- just discard this for now + //throw XSocketIOClose(e.what()); } } } -void -CTCPSocket::setEventTarget(void* target) +void* +CTCPSocket::getEventTarget() const { - CLock lock(m_mutex); - m_target = target; + return const_cast(reinterpret_cast(this)); +} + +UInt32 +CTCPSocket::read(void* buffer, UInt32 n) +{ + // copy data directly from our input buffer + CLock lock(&m_mutex); + UInt32 size = m_inputBuffer.getSize(); + if (n > size) { + n = size; + } + if (buffer != NULL) { + memcpy(buffer, m_inputBuffer.peek(n), n); + } + m_inputBuffer.pop(n); + + // if no more data and we cannot read or write then send disconnected + if (n > 0 && !m_readable && !m_writable) { + sendSocketEvent(getDisconnectedEvent()); + } + + return n; +} + +void +CTCPSocket::write(const void* buffer, UInt32 n) +{ + CLock lock(&m_mutex); + + // must not have shutdown output + if (!m_writable) { + sendStreamEvent(getOutputErrorEvent()); + return; + } + + // ignore empty writes + if (n == 0) { + return; + } + + // copy data to the output buffer + bool wasEmpty = (m_outputBuffer.getSize() == 0); + m_outputBuffer.write(buffer, n); + + // there's data to write + m_flushed = false; + + // make sure we're waiting to write + if (wasEmpty) { + setJob(newJob()); + } +} + +void +CTCPSocket::flush() +{ + CLock lock(&m_mutex); + while (m_flushed == false) { + m_flushed.wait(); + } +} + +void +CTCPSocket::shutdownInput() +{ + CLock lock(&m_mutex); + + // shutdown socket for reading + try { + ARCH->closeSocketForRead(m_socket); + } + catch (XArchNetwork&) { + // ignore + } + + // shutdown buffer for reading + if (m_readable) { + sendStreamEvent(getInputShutdownEvent()); + onInputShutdown(); + setJob(newJob()); + } +} + +void +CTCPSocket::shutdownOutput() +{ + CLock lock(&m_mutex); + + // shutdown socket for writing + try { + ARCH->closeSocketForWrite(m_socket); + } + catch (XArchNetwork&) { + // ignore + } + + // shutdown buffer for writing + if (m_writable) { + sendStreamEvent(getOutputShutdownEvent()); + onOutputShutdown(); + setJob(newJob()); + } +} + +void +CTCPSocket::setEventFilter(IEventJob* filter) +{ + CLock lock(&m_mutex); + m_eventFilter = filter; +} + +bool +CTCPSocket::isReady() const +{ + CLock lock(&m_mutex); + return (m_inputBuffer.getSize() > 0); +} + +UInt32 +CTCPSocket::getSize() const +{ + CLock lock(&m_mutex); + return m_inputBuffer.getSize(); +} + +IEventJob* +CTCPSocket::getEventFilter() const +{ + CLock lock(&m_mutex); + return m_eventFilter; } void CTCPSocket::connect(const CNetworkAddress& addr) { + CLock lock(&m_mutex); + + // fail on attempts to reconnect + if (m_socket == NULL || m_connected) { + sendSocketEvent(getConnectionFailedEvent()); + return; + } + try { // FIXME -- don't throw if in progress, just return that info ARCH->connectSocket(m_socket, addr.getAddress()); - setState(kReadWrite, true); + sendSocketEvent(getConnectedEvent()); + onConnected(); + setJob(newJob()); } catch (XArchNetworkConnecting&) { // connection is in progress - setState(kConnecting, true); + m_writable = true; + setJob(newJob()); } catch (XArchNetwork& e) { throw XSocketConnect(e.what()); } } -IInputStream* -CTCPSocket::getInputStream() -{ - return m_input; -} - -IOutputStream* -CTCPSocket::getOutputStream() -{ - return m_output; -} - void CTCPSocket::init() { - m_mutex = new CMutex; - m_input = new CBufferedInputStream(m_mutex, - new TMethodJob( - this, &CTCPSocket::emptyInput), - new TMethodJob( - this, &CTCPSocket::closeInput)); - m_output = new CBufferedOutputStream(m_mutex, - new TMethodJob( - this, &CTCPSocket::fillOutput), - new TMethodJob( - this, &CTCPSocket::closeOutput)); - m_state = kUnconnected; - m_target = NULL; - m_job = NULL; + // default state + m_connected = false; + m_readable = false; + m_writable = false; // make socket non-blocking // FIXME -- check for error @@ -196,172 +303,97 @@ CTCPSocket::init() } } -ISocketMultiplexerJob* -CTCPSocket::newMultiplexerJob(JobFunc func, bool readable, bool writable) -{ - return new TSocketMultiplexerMethodJob( - this, func, m_socket, readable, writable); -} - -ISocketMultiplexerJob* -CTCPSocket::setState(State state, bool setJob) -{ - if (m_state == state || m_state == kClosed) { - return m_job; - } - - State oldState = m_state; - m_state = state; - - bool read = (m_input->getSize() > 0); - bool write = (m_output->getSize() > 0); - CEvent::Type eventType = 0; - m_job = NULL; - switch (m_state) { - case kUnconnected: - assert(0 && "cannot re-enter unconnected state"); - break; - - case kConnecting: - m_job = newMultiplexerJob(&CTCPSocket::serviceConnecting, false, true); - break; - - case kReadWrite: - if (oldState == kConnecting) { - eventType = IDataSocket::getConnectedEvent(); - } - m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, write); - break; - - case kReadOnly: - if (!write) { - eventType = IDataSocket::getShutdownOutputEvent(); - } - if (oldState == kWriteOnly) { - goto shutdown; - } - m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, write); - break; - - case kWriteOnly: - if (!read) { - m_input->hangup(); - eventType = IDataSocket::getShutdownInputEvent(); - } - if (oldState == kReadOnly) { - goto shutdown; - } - m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, write); - break; - - case kShutdown: -shutdown: - if (!read && !write) { - eventType = ISocket::getDisconnectedEvent(); - m_state = kClosed; - } - else { - m_state = kShutdown; - } - break; - - case kClosed: - m_input->hangup(); - if (oldState == kConnecting) { - eventType = IDataSocket::getConnectionFailedEvent(); - } - else { - eventType = ISocket::getDisconnectedEvent(); - } - break; - } - - // notify - if (eventType != 0) { - sendEvent(eventType); - } - - // cut over to new job. multiplexer will delete the old job. - if (setJob) { - if (m_job == NULL) { - CSocketMultiplexer::getInstance()->removeSocket(this); - } - else { - CSocketMultiplexer::getInstance()->addSocket(this, m_job); - } - } - return m_job; -} - void -CTCPSocket::closeInput(void*) +CTCPSocket::setJob(ISocketMultiplexerJob* job) { - // note -- m_mutex should already be locked - try { - ARCH->closeSocketForRead(m_socket); - setState(kWriteOnly, true); - } - catch (XArchNetwork&) { - // ignore - } -} - -void -CTCPSocket::closeOutput(void*) -{ - // note -- m_mutex should already be locked - try { -// ARCH->closeSocketForWrite(m_socket); - setState(kReadOnly, true); - } - catch (XArchNetwork&) { - // ignore - } -} - -void -CTCPSocket::emptyInput(void*) -{ - // note -- m_mutex should already be locked - bool write = (m_output->getSize() > 0); - if (m_state == kWriteOnly && !write) { - m_state = kShutdown; - } - if (m_state == kWriteOnly) { - m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, write); - CSocketMultiplexer::getInstance()->addSocket(this, m_job); - m_input->hangup(); - sendEvent(IDataSocket::getShutdownInputEvent()); - } - else if (m_state == kShutdown) { - m_job = NULL; + // multiplexer will delete the old job + if (job == NULL) { CSocketMultiplexer::getInstance()->removeSocket(this); - if (!write) { - sendEvent(ISocket::getDisconnectedEvent()); - m_state = kClosed; - } + } + else { + CSocketMultiplexer::getInstance()->addSocket(this, job); + } +} + +ISocketMultiplexerJob* +CTCPSocket::newJob() +{ + // note -- must have m_mutex locked on entry + + if (m_socket == NULL || !(m_readable || m_writable)) { + return NULL; + } + else if (!m_connected) { + assert(!m_readable); + return new TSocketMultiplexerMethodJob( + this, &CTCPSocket::serviceConnecting, + m_socket, m_readable, m_writable); + } + else { + return new TSocketMultiplexerMethodJob( + this, &CTCPSocket::serviceConnected, + m_socket, m_readable, + m_writable && (m_outputBuffer.getSize() > 0)); } } void -CTCPSocket::fillOutput(void*) +CTCPSocket::sendSocketEvent(CEvent::Type type) { - // note -- m_mutex should already be locked - if (m_state == kReadWrite) { - m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, true); - CSocketMultiplexer::getInstance()->addSocket(this, m_job); + EVENTQUEUE->addEvent(CEvent(type, this, NULL)); +} + +void +CTCPSocket::sendStreamEvent(CEvent::Type type) +{ + if (m_eventFilter != NULL) { + m_eventFilter->run(CEvent(type, this, NULL)); } - else if (m_state == kWriteOnly) { - m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, true); - CSocketMultiplexer::getInstance()->addSocket(this, m_job); + else { + EVENTQUEUE->addEvent(CEvent(type, this, NULL)); } } +void +CTCPSocket::onConnected() +{ + m_connected = true; + m_readable = true; + m_writable = true; +} + +void +CTCPSocket::onInputShutdown() +{ + m_inputBuffer.pop(m_inputBuffer.getSize()); + m_readable = false; +} + +void +CTCPSocket::onOutputShutdown() +{ + m_outputBuffer.pop(m_outputBuffer.getSize()); + m_writable = false; + + // we're now flushed + m_flushed = true; + m_flushed.broadcast(); +} + +void +CTCPSocket::onDisconnected() +{ + // disconnected + onInputShutdown(); + onOutputShutdown(); + m_connected = false; +} + ISocketMultiplexerJob* CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job, bool, bool write, bool error) { - CLock lock(m_mutex); + CLock lock(&m_mutex); if (write && !error) { try { @@ -374,11 +406,15 @@ CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job, } if (error) { - return setState(kClosed, false); + sendSocketEvent(getConnectionFailedEvent()); + onDisconnected(); + return newJob(); } if (write) { - return setState(kReadWrite, false); + sendSocketEvent(getConnectedEvent()); + onConnected(); + return newJob(); } return job; @@ -388,79 +424,99 @@ ISocketMultiplexerJob* CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, bool read, bool write, bool error) { - CLock lock(m_mutex); + CLock lock(&m_mutex); + if (error) { - return setState(kClosed, false); + sendSocketEvent(getDisconnectedEvent()); + onDisconnected(); + return newJob(); } - if (write) { - // get amount of data to write - UInt32 n = m_output->getSize(); + bool needNewJob = false; - // write data + if (write) { try { - const void* buffer = m_output->peek(n); - size_t n2 = ARCH->writeSocket(m_socket, buffer, n); + // write data + UInt32 n = m_outputBuffer.getSize(); + const void* buffer = m_outputBuffer.peek(n); + n = (UInt32)ARCH->writeSocket(m_socket, buffer, n); // discard written data - if (n2 > 0) { - m_output->pop(n2); + if (n > 0) { + m_outputBuffer.pop(n); + if (m_outputBuffer.getSize() == 0) { + sendStreamEvent(getOutputFlushedEvent()); + m_flushed = true; + m_flushed.broadcast(); + needNewJob = true; + } + } + } + catch (XArchNetworkShutdown&) { + // remote read end of stream hungup. our output side + // has therefore shutdown. + onOutputShutdown(); + sendStreamEvent(getOutputShutdownEvent()); + if (!m_readable && m_inputBuffer.getSize() == 0) { + sendSocketEvent(getDisconnectedEvent()); + } + needNewJob = true; + } + catch (XArchNetworkDisconnected&) { + // stream hungup + onDisconnected(); + sendSocketEvent(getDisconnectedEvent()); + needNewJob = true; + } + catch (XArchNetwork&) { + // other write error + onDisconnected(); + sendStreamEvent(getOutputErrorEvent()); + sendSocketEvent(getDisconnectedEvent()); + needNewJob = true; + } + } + + if (read && m_readable) { + try { + UInt8 buffer[4096]; + size_t n = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); + if (n > 0) { + bool wasEmpty = (m_inputBuffer.getSize() == 0); + + // slurp up as much as possible + do { + m_inputBuffer.write(buffer, n); + n = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); + } while (n > 0); + + // send input ready if input buffer was empty + if (wasEmpty) { + sendStreamEvent(getInputReadyEvent()); + } + } + else { + // remote write end of stream hungup. our input side + // has therefore shutdown but don't flush our buffer + // since there's still data to be read. + sendStreamEvent(getInputShutdownEvent()); + if (!m_writable && m_inputBuffer.getSize() == 0) { + sendSocketEvent(getDisconnectedEvent()); + } + m_readable = false; + needNewJob = true; } } catch (XArchNetworkDisconnected&) { // stream hungup - return setState(kReadOnly, false); + sendSocketEvent(getDisconnectedEvent()); + onDisconnected(); + needNewJob = true; + } + catch (XArchNetwork&) { + // ignore other read error } } - if (read) { - UInt8 buffer[4096]; - size_t n = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); - if (n > 0) { - // slurp up as much as possible - do { - m_input->write(buffer, n); - try { - n = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); - } - catch (XArchNetworkWouldBlock&) { - break; - } - } while (n > 0); - - // notify - sendEvent(IDataSocket::getInputEvent()); - } - else { - // stream hungup - return setState(kWriteOnly, false); - } - } - - if (write && m_output->getSize() == 0) { - if (m_state == kReadOnly) { - ARCH->closeSocketForWrite(m_socket); - sendEvent(IDataSocket::getShutdownOutputEvent()); - m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, - true, false); - job = m_job; - } - else if (m_state == kReadWrite || m_state == kReadOnly) { - m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, - true, false); - job = m_job; - } - else if (m_state == kWriteOnly) { - m_job = NULL; - job = m_job; - } - } - - return job; -} - -void -CTCPSocket::sendEvent(CEvent::Type type) -{ - CEventQueue::getInstance()->addEvent(CEvent(type, m_target, NULL)); + return needNewJob ? newJob() : job; } diff --git a/lib/net/CTCPSocket.h b/lib/net/CTCPSocket.h index 142ac4a6..0335dce1 100644 --- a/lib/net/CTCPSocket.h +++ b/lib/net/CTCPSocket.h @@ -16,14 +16,13 @@ #define CTCPSOCKET_H #include "IDataSocket.h" -#include "CEvent.h" -#include "BasicTypes.h" +#include "CStreamBuffer.h" +#include "CCondVar.h" +#include "CMutex.h" #include "IArchNetwork.h" class CMutex; class CThread; -class CBufferedInputStream; -class CBufferedOutputStream; class ISocketMultiplexerJob; //! TCP data socket @@ -39,33 +38,34 @@ public: // ISocket overrides virtual void bind(const CNetworkAddress&); virtual void close(); - virtual void setEventTarget(void*); + virtual void* getEventTarget() const; + + // IStream overrides + virtual UInt32 read(void* buffer, UInt32 n); + virtual void write(const void* buffer, UInt32 n); + virtual void flush(); + virtual void shutdownInput(); + virtual void shutdownOutput(); + virtual void setEventFilter(IEventJob* filter); + virtual bool isReady() const; + virtual UInt32 getSize() const; + virtual IEventJob* getEventFilter() const; // IDataSocket overrides virtual void connect(const CNetworkAddress&); - virtual IInputStream* getInputStream(); - virtual IOutputStream* getOutputStream(); private: - enum State { - kUnconnected, - kConnecting, - kReadWrite, - kReadOnly, - kWriteOnly, - kShutdown, - kClosed - }; - void init(); - ISocketMultiplexerJob* - setState(State, bool setJob); + void setJob(ISocketMultiplexerJob*); + ISocketMultiplexerJob* newJob(); + void sendSocketEvent(CEvent::Type); + void sendStreamEvent(CEvent::Type); - void closeInput(void*); - void closeOutput(void*); - void emptyInput(void*); - void fillOutput(void*); + void onConnected(); + void onInputShutdown(); + void onOutputShutdown(); + void onDisconnected(); ISocketMultiplexerJob* serviceConnecting(ISocketMultiplexerJob*, @@ -74,25 +74,16 @@ private: serviceConnected(ISocketMultiplexerJob*, bool, bool, bool); - typedef ISocketMultiplexerJob* (CTCPSocket::*JobFunc)( - ISocketMultiplexerJob*, - bool, bool, bool); - ISocketMultiplexerJob* - newMultiplexerJob(JobFunc, - bool readable, bool writable); - - void sendEvent(CEvent::Type); - private: - CArchSocket m_socket; - CBufferedInputStream* m_input; - CBufferedOutputStream* m_output; - - CMutex* m_mutex; - State m_state; - void* m_target; - - ISocketMultiplexerJob* m_job; + CMutex m_mutex; + CArchSocket m_socket; + CStreamBuffer m_inputBuffer; + CStreamBuffer m_outputBuffer; + CCondVar m_flushed; + bool m_connected; + bool m_readable; + bool m_writable; + IEventJob* m_eventFilter; }; #endif diff --git a/lib/net/IDataSocket.cpp b/lib/net/IDataSocket.cpp index f6d577ab..b3f6a5e1 100644 --- a/lib/net/IDataSocket.cpp +++ b/lib/net/IDataSocket.cpp @@ -18,11 +18,8 @@ // IDataSocket // -CEvent::Type IDataSocket::s_connectedEvent = CEvent::kUnknown; -CEvent::Type IDataSocket::s_failedEvent = CEvent::kUnknown; -CEvent::Type IDataSocket::s_inputEvent = CEvent::kUnknown; -CEvent::Type IDataSocket::s_shutdownInputEvent = CEvent::kUnknown; -CEvent::Type IDataSocket::s_shutdownOutputEvent = CEvent::kUnknown; +CEvent::Type IDataSocket::s_connectedEvent = CEvent::kUnknown; +CEvent::Type IDataSocket::s_failedEvent = CEvent::kUnknown; CEvent::Type IDataSocket::getConnectedEvent() @@ -35,21 +32,3 @@ IDataSocket::getConnectionFailedEvent() { return CEvent::registerTypeOnce(s_failedEvent); } - -CEvent::Type -IDataSocket::getInputEvent() -{ - return CEvent::registerTypeOnce(s_inputEvent); -} - -CEvent::Type -IDataSocket::getShutdownInputEvent() -{ - return CEvent::registerTypeOnce(s_shutdownInputEvent); -} - -CEvent::Type -IDataSocket::getShutdownOutputEvent() -{ - return CEvent::registerTypeOnce(s_shutdownOutputEvent); -} diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h index 8fa2d6da..fd071669 100644 --- a/lib/net/IDataSocket.h +++ b/lib/net/IDataSocket.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman + * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -16,44 +16,27 @@ #define IDATASOCKET_H #include "ISocket.h" - -class IInputStream; -class IOutputStream; +#include "IStream.h" //! Data stream socket interface /*! This interface defines the methods common to all network sockets that represent a full-duplex data stream. */ -class IDataSocket : public ISocket { +class IDataSocket : public ISocket, public IStream { public: //! @name manipulators //@{ //! Connect socket /*! - Attempt to connect to a remote endpoint. This waits until the - connection is established or fails. If it fails it throws an - XSocketConnect exception. - - (cancellation point) + Attempt to connect to a remote endpoint. This returns immediately + and sends a connected event when successful or a connection failed + event when it fails. The stream acts as if shutdown for input and + output until the stream connects. */ virtual void connect(const CNetworkAddress&) = 0; - //! Get input stream - /*! - Returns the input stream for reading from the socket. Closing this - stream will shutdown the socket for reading. - */ - virtual IInputStream* getInputStream() = 0; - - //! Get output stream - /*! - Returns the output stream for writing to the socket. Closing this - stream will shutdown the socket for writing. - */ - virtual IOutputStream* getOutputStream() = 0; - //@} //! @name accessors //@{ @@ -72,42 +55,27 @@ public: */ static CEvent::Type getConnectionFailedEvent(); - //! Get input event type - /*! - Returns the socket input event type. A socket sends this - event when data is available to read from the input stream. - */ - static CEvent::Type getInputEvent(); - - //! Get shutdown input event type - /*! - Returns the socket shutdown input event type. A socket sends this - event when the remote side of the connection has shutdown for - writing and there is no more data to read from the socket. - */ - static CEvent::Type getShutdownInputEvent(); - - //! Get shutdown input event type - /*! - Returns the socket shutdown input event type. A socket sends this - event when the remote side of the connection has shutdown for - writing and there is no more data to read from the socket. - */ - static CEvent::Type getShutdownOutputEvent(); - //@} // ISocket overrides virtual void bind(const CNetworkAddress&) = 0; virtual void close() = 0; - virtual void setEventTarget(void*) = 0; + virtual void* getEventTarget() const = 0; + + // IStream overrides + virtual UInt32 read(void* buffer, UInt32 n) = 0; + virtual void write(const void* buffer, UInt32 n) = 0; + virtual void flush() = 0; + virtual void shutdownInput() = 0; + virtual void shutdownOutput() = 0; + virtual void setEventFilter(IEventJob* filter) = 0; + virtual bool isReady() const = 0; + virtual UInt32 getSize() const = 0; + virtual IEventJob* getEventFilter() const = 0; private: static CEvent::Type s_connectedEvent; static CEvent::Type s_failedEvent; - static CEvent::Type s_inputEvent; - static CEvent::Type s_shutdownInputEvent; - static CEvent::Type s_shutdownOutputEvent; }; #endif diff --git a/lib/net/IListenSocket.h b/lib/net/IListenSocket.h index 36524750..894234f4 100644 --- a/lib/net/IListenSocket.h +++ b/lib/net/IListenSocket.h @@ -53,7 +53,7 @@ public: // ISocket overrides virtual void bind(const CNetworkAddress&) = 0; virtual void close() = 0; - virtual void setEventTarget(void*) = 0; + virtual void* getEventTarget() const = 0; private: static CEvent::Type s_connectingEvent; diff --git a/lib/net/ISocket.h b/lib/net/ISocket.h index 79e55966..4452e023 100644 --- a/lib/net/ISocket.h +++ b/lib/net/ISocket.h @@ -23,6 +23,7 @@ class CNetworkAddress; //! Generic socket interface /*! This interface defines the methods common to all network sockets. +Generated events use \c this as the target. */ class ISocket : public IInterface { public: @@ -41,21 +42,21 @@ public: */ virtual void close() = 0; - //! Set the socket's event target - /*! - Sets the target of any events sent by the socket. The default is NULL. - */ - virtual void setEventTarget(void*) = 0; - //@} //! @name accessors //@{ + //! Get event target + /*! + Returns the event target for events generated by this socket. + */ + virtual void* getEventTarget() const = 0; + //! Get disconnected event type /*! Returns the socket disconnected event type. A socket sends this event when the remote side of the socket has disconnected or - shutdown. + shutdown both input and output. */ static CEvent::Type getDisconnectedEvent(); diff --git a/lib/net/ISocketMultiplexerJob.h b/lib/net/ISocketMultiplexerJob.h new file mode 100644 index 00000000..ac722326 --- /dev/null +++ b/lib/net/ISocketMultiplexerJob.h @@ -0,0 +1,75 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISOCKETMULTIPLEXERJOB_H +#define ISOCKETMULTIPLEXERJOB_H + +#include "IArchNetwork.h" +#include "IInterface.h" + +//! Socket multiplexer job +/*! +A socket multiplexer job handles events on a socket. +*/ +class ISocketMultiplexerJob : public IInterface { +public: + //! @name manipulators + //@{ + + //! Handle socket event + /*! + Called by a socket multiplexer when the socket becomes readable, + writable, or has an error. It should return itself if the same + job can continue to service events, a new job if the socket must + be serviced differently, or NULL if the socket should no longer + be serviced. The socket is readable if \p readable is true, + writable if \p writable is true, and in error if \p error is + true. + + This call must not attempt to directly change the job for this + socket by calling \c addSocket() or \c removeSocket() on the + multiplexer. It must instead return the new job. It can, + however, add or remove jobs for other sockets. + */ + virtual ISocketMultiplexerJob* + run(bool readable, bool writable, bool error) = 0; + + //@} + //! @name accessors + //@{ + + //! Get the socket + /*! + Return the socket to multiplex + */ + virtual CArchSocket getSocket() const = 0; + + //! Check for interest in readability + /*! + Return true if the job is interested in being run if the socket + becomes readable. + */ + virtual bool isReadable() const = 0; + + //! Check for interest in writability + /*! + Return true if the job is interested in being run if the socket + becomes writable. + */ + virtual bool isWritable() const = 0; + + //@} +}; + +#endif diff --git a/lib/net/TSocketMultiplexerMethodJob.h b/lib/net/TSocketMultiplexerMethodJob.h new file mode 100644 index 00000000..992885c4 --- /dev/null +++ b/lib/net/TSocketMultiplexerMethodJob.h @@ -0,0 +1,108 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef TSOCKERMULTIPLEXERMETHODJOB_H +#define TSOCKERMULTIPLEXERMETHODJOB_H + +#include "ISocketMultiplexerJob.h" +#include "CArch.h" + +//! Use a method as a socket multiplexer job +/*! +A socket multiplexer job class that invokes a member function. +*/ +template +class TSocketMultiplexerMethodJob : public ISocketMultiplexerJob { +public: + typedef ISocketMultiplexerJob* + (T::*Method)(ISocketMultiplexerJob*, bool, bool, bool); + + //! run() invokes \c object->method(arg) + TSocketMultiplexerMethodJob(T* object, Method method, + CArchSocket socket, bool readable, bool writeable); + virtual ~TSocketMultiplexerMethodJob(); + + // IJob overrides + virtual ISocketMultiplexerJob* + run(bool readable, bool writable, bool error); + virtual CArchSocket getSocket() const; + virtual bool isReadable() const; + virtual bool isWritable() const; + +private: + T* m_object; + Method m_method; + CArchSocket m_socket; + bool m_readable; + bool m_writable; + void* m_arg; +}; + +template +inline +TSocketMultiplexerMethodJob::TSocketMultiplexerMethodJob(T* object, + Method method, CArchSocket socket, + bool readable, bool writable) : + m_object(object), + m_method(method), + m_socket(ARCH->copySocket(socket)), + m_readable(readable), + m_writable(writable) +{ + // do nothing +} + +template +inline +TSocketMultiplexerMethodJob::~TSocketMultiplexerMethodJob() +{ + ARCH->closeSocket(m_socket); +} + +template +inline +ISocketMultiplexerJob* +TSocketMultiplexerMethodJob::run(bool read, bool write, bool error) +{ + if (m_object != NULL) { + return (m_object->*m_method)(this, read, write, error); + } + return NULL; +} + +template +inline +CArchSocket +TSocketMultiplexerMethodJob::getSocket() const +{ + return m_socket; +} + +template +inline +bool +TSocketMultiplexerMethodJob::isReadable() const +{ + return m_readable; +} + +template +inline +bool +TSocketMultiplexerMethodJob::isWritable() const +{ + return m_writable; +} + +#endif diff --git a/lib/server/CClientProxy.cpp b/lib/server/CClientProxy.cpp index 55e940f7..0df174c9 100644 --- a/lib/server/CClientProxy.cpp +++ b/lib/server/CClientProxy.cpp @@ -13,27 +13,24 @@ */ #include "CClientProxy.h" -#include "IInputStream.h" -#include "IOutputStream.h" +#include "IStream.h" // // CClientProxy // -CClientProxy::CClientProxy(IServer* server, const CString& name, - IInputStream* input, IOutputStream* output) : +CClientProxy::CClientProxy(IServer* server, + const CString& name, IStream* stream) : m_server(server), m_name(name), - m_input(input), - m_output(output) + m_stream(stream) { // do nothing } CClientProxy::~CClientProxy() { - delete m_output; - delete m_input; + delete m_stream; } IServer* @@ -42,16 +39,10 @@ CClientProxy::getServer() const return m_server; } -IInputStream* -CClientProxy::getInputStream() const +IStream* +CClientProxy::getStream() const { - return m_input; -} - -IOutputStream* -CClientProxy::getOutputStream() const -{ - return m_output; + return m_stream; } CString diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index 2ab41c35..751442ae 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -19,8 +19,7 @@ #include "CMutex.h" #include "CString.h" -class IInputStream; -class IOutputStream; +class IStream; class IServer; //! Generic proxy for client @@ -29,9 +28,7 @@ public: /*! \c name is the name of the client. */ - CClientProxy(IServer* server, const CString& name, - IInputStream* adoptedInput, - IOutputStream* adoptedOutput); + CClientProxy(IServer* server, const CString& name, IStream* adoptedStream); ~CClientProxy(); //! @name accessors @@ -43,17 +40,11 @@ public: */ IServer* getServer() const; - //! Get input stream + //! Get stream /*! - Returns the input stream passed to the c'tor. + Returns the stream passed to the c'tor. */ - IInputStream* getInputStream() const; - - //! Get output stream - /*! - Returns the output stream passed to the c'tor. - */ - IOutputStream* getOutputStream() const; + IStream* getStream() const; //@} @@ -98,8 +89,7 @@ private: CMutex m_mutex; IServer* m_server; CString m_name; - IInputStream* m_input; - IOutputStream* m_output; + IStream* m_stream; }; #endif diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 7a9e734c..ccf5b357 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -17,46 +17,189 @@ #include "CClipboard.h" #include "CProtocolUtil.h" #include "XSynergy.h" -#include "IInputStream.h" -#include "IOutputStream.h" +#include "IStream.h" #include "CLock.h" -#include "CThread.h" #include "CLog.h" -#include "CStopwatch.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" #include // // CClientProxy1_0 // -CClientProxy1_0::CClientProxy1_0(IServer* server, const CString& name, - IInputStream* input, IOutputStream* output) : - CClientProxy(server, name, input, output), - m_heartRate(kHeartRate), - m_heartDeath(kHeartRate * kHeartBeatsUntilDeath) +CClientProxy1_0::CClientProxy1_0(IServer* server, + const CString& name, IStream* stream) : + CClientProxy(server, name, stream), + m_heartbeatAlarm(kHeartRate * kHeartBeatsUntilDeath), + m_heartbeatTimer(NULL) { for (UInt32 i = 0; i < kClipboardEnd; ++i) { m_clipboardDirty[i] = true; } + + // install event handlers + EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(), + stream->getEventTarget(), + new TMethodEventJob(this, + &CClientProxy1_0::handleData, NULL)); + EVENTQUEUE->adoptHandler(IStream::getOutputErrorEvent(), + stream->getEventTarget(), + new TMethodEventJob(this, + &CClientProxy1_0::handleWriteError, NULL)); + EVENTQUEUE->adoptHandler(IStream::getInputShutdownEvent(), + stream->getEventTarget(), + new TMethodEventJob(this, + &CClientProxy1_0::handleDisconnect, NULL)); + EVENTQUEUE->adoptHandler(IStream::getOutputShutdownEvent(), + stream->getEventTarget(), + new TMethodEventJob(this, + &CClientProxy1_0::handleWriteError, NULL)); + EVENTQUEUE->adoptHandler(CEvent::kTimer, this, + new TMethodEventJob(this, + &CClientProxy1_0::handleFlatline, NULL)); + + // FIXME -- open() replacement must install initial heartbeat timer } CClientProxy1_0::~CClientProxy1_0() { - // do nothing + removeHandlers(); } +void +CClientProxy1_0::disconnect() +{ + CLock lock(getMutex()); + removeHandlers(); + // FIXME -- send disconnect event (server should be listening for this) + getStream()->flush(); + getStream()->close(); +} + +void +CClientProxy1_0::removeHandlers() +{ + // uninstall event handlers + EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(), + getStream()->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getOutputErrorEvent(), + getStream()->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getInputShutdownEvent(), + getStream()->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getOutputShutdownEvent(), + getStream()->getEventTarget()); + EVENTQUEUE->removeHandler(CEvent::kTimer, this); + + // remove timer + removeHeartbeatTimer(); +} + +void +CClientProxy1_0::addHeartbeatTimer() +{ + CLock lock(getMutex()); + if (m_heartbeatAlarm > 0.0) { + m_heartbeatTimer = EVENTQUEUE->newOneShotTimer(m_heartbeatAlarm, this); + } +} + +void +CClientProxy1_0::removeHeartbeatTimer() +{ + CLock lock(getMutex()); + if (m_heartbeatTimer != NULL) { + EVENTQUEUE->deleteTimer(m_heartbeatTimer); + m_heartbeatTimer = NULL; + } +} + +void +CClientProxy1_0::handleData(const CEvent&, void*) +{ + // handle messages until there are no more. first read message code. + UInt8 code[4]; + UInt32 n = getStream()->read(code, 4); + while (n != 0) { + // verify we got an entire code + if (n != 4) { + LOG((CLOG_ERR "incomplete message from \"%s\": %d bytes", getName().c_str(), n)); + disconnect(); + return; + } + + // parse message + LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + if (!parseMessage(code)) { + LOG((CLOG_ERR "invalid message from client \"%s\"", getName().c_str())); + disconnect(); + return; + } + + // next message + n = getStream()->read(code, 4); + } + + // restart heartbeat timer + removeHeartbeatTimer(); + addHeartbeatTimer(); +} + +bool +CClientProxy1_0::parseMessage(const UInt8* code) +{ + if (memcmp(code, kMsgDInfo, 4) == 0) { + return recvInfo(true); + } + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // discard no-ops + LOG((CLOG_DEBUG2 "no-op from", getName().c_str())); + return true; + } + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + return recvGrabClipboard(); + } + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + return recvClipboard(); + } + return false; +} + +void +CClientProxy1_0::handleDisconnect(const CEvent&, void*) +{ + LOG((CLOG_NOTE "client \"%s\" disconnected", getName().c_str())); + disconnect(); +} + +void +CClientProxy1_0::handleWriteError(const CEvent&, void*) +{ + LOG((CLOG_ERR "error writing to client \"%s\"", getName().c_str())); + disconnect(); +} + +void +CClientProxy1_0::handleFlatline(const CEvent&, void*) +{ + // didn't get a heartbeat fast enough. assume client is dead. + LOG((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); + disconnect(); +} + +// FIXME -- replace this void CClientProxy1_0::open() { // send request LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgQInfo); - getOutputStream()->flush(); + CProtocolUtil::writef(getStream(), kMsgQInfo); + getStream()->flush(); // wait for and verify reply UInt8 code[4]; for (;;) { - UInt32 n = getInputStream()->read(code, 4, -1.0); + UInt32 n = getStream()->read(code, 4); if (n == 4) { if (memcmp(code, kMsgCNoop, 4) == 0) { // discard heartbeats @@ -73,79 +216,15 @@ CClientProxy1_0::open() recvInfo(false); } -void -CClientProxy1_0::mainLoop() -{ - // handle messages until the client hangs up or stops sending heartbeats - CStopwatch heartTimer; - for (;;) { - CThread::testCancel(); - - // wait for a message - UInt8 code[4]; - UInt32 n = getInputStream()->read(code, 4, m_heartRate); - CThread::testCancel(); - - // check if client hungup - if (n == 0) { - LOG((CLOG_NOTE "client \"%s\" disconnected", getName().c_str())); - return; - } - - // check if client has stopped sending heartbeats - if (n == (UInt32)-1) { - if (m_heartDeath >= 0.0 && heartTimer.getTime() > m_heartDeath) { - LOG((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); - return; - } - continue; - } - - // got a message so reset heartbeat monitor - heartTimer.reset(); - - // verify we got an entire code - if (n != 4) { - LOG((CLOG_ERR "incomplete message from \"%s\": %d bytes", getName().c_str(), n)); - - // client sent an incomplete message - throw XBadClient(); - } - - // parse message - LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); - if (memcmp(code, kMsgDInfo, 4) == 0) { - recvInfo(true); - } - else if (memcmp(code, kMsgCNoop, 4) == 0) { - // discard no-ops - LOG((CLOG_DEBUG2 "no-op from", getName().c_str())); - continue; - } - else if (memcmp(code, kMsgCClipboard, 4) == 0) { - recvGrabClipboard(); - } - else if (memcmp(code, kMsgDClipboard, 4) == 0) { - recvClipboard(); - } - // note -- more message handlers go here - else { - LOG((CLOG_ERR "invalid message from client \"%s\"", getName().c_str())); - - // unknown message - throw XBadClient(); - } - } -} - +// FIXME -- replace this void CClientProxy1_0::close() { LOG((CLOG_DEBUG1 "send close to \"%s\"", getName().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCClose); + CProtocolUtil::writef(getStream(), kMsgCClose); // force the close to be sent before we return - getOutputStream()->flush(); + getStream()->flush(); } void @@ -153,7 +232,7 @@ CClientProxy1_0::enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, bool) { LOG((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getName().c_str(), xAbs, yAbs, seqNum, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgCEnter, + CProtocolUtil::writef(getStream(), kMsgCEnter, xAbs, yAbs, seqNum, mask); } @@ -161,7 +240,7 @@ bool CClientProxy1_0::leave() { LOG((CLOG_DEBUG1 "send leave to \"%s\"", getName().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCLeave); + CProtocolUtil::writef(getStream(), kMsgCLeave); // we can never prevent the user from leaving return true; @@ -177,7 +256,7 @@ CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) m_clipboardDirty[id] = false; LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); - CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); + CProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data); } } @@ -185,7 +264,7 @@ void CClientProxy1_0::grabClipboard(ClipboardID id) { LOG((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getName().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); + CProtocolUtil::writef(getStream(), kMsgCClipboard, id, 0); // this clipboard is now dirty CLock lock(getMutex()); @@ -203,7 +282,7 @@ void CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask, KeyButton) { LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown1_0, key, mask); + CProtocolUtil::writef(getStream(), kMsgDKeyDown1_0, key, mask); } void @@ -211,78 +290,81 @@ CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton) { LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat1_0, key, mask, count); + CProtocolUtil::writef(getStream(), kMsgDKeyRepeat1_0, key, mask, count); } void CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask, KeyButton) { LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp1_0, key, mask); + CProtocolUtil::writef(getStream(), kMsgDKeyUp1_0, key, mask); } void CClientProxy1_0::mouseDown(ButtonID button) { LOG((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getName().c_str(), button)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button); + CProtocolUtil::writef(getStream(), kMsgDMouseDown, button); } void CClientProxy1_0::mouseUp(ButtonID button) { LOG((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getName().c_str(), button)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button); + CProtocolUtil::writef(getStream(), kMsgDMouseUp, button); } void CClientProxy1_0::mouseMove(SInt32 xAbs, SInt32 yAbs) { LOG((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getName().c_str(), xAbs, yAbs)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs); + CProtocolUtil::writef(getStream(), kMsgDMouseMove, xAbs, yAbs); } void CClientProxy1_0::mouseWheel(SInt32 delta) { LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), delta)); - CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta); + CProtocolUtil::writef(getStream(), kMsgDMouseWheel, delta); } void CClientProxy1_0::screensaver(bool on) { LOG((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getName().c_str(), on ? 1 : 0)); - CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); + CProtocolUtil::writef(getStream(), kMsgCScreenSaver, on ? 1 : 0); } void CClientProxy1_0::resetOptions() { LOG((CLOG_DEBUG1 "send reset options to \"%s\"", getName().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCResetOptions); + CProtocolUtil::writef(getStream(), kMsgCResetOptions); // reset heart rate and death CLock lock(getMutex()); - m_heartRate = kHeartRate; - m_heartDeath = kHeartRate * kHeartBeatsUntilDeath; + m_heartbeatAlarm = kHeartRate * kHeartBeatsUntilDeath; + removeHeartbeatTimer(); + addHeartbeatTimer(); } void CClientProxy1_0::setOptions(const COptionsList& options) { LOG((CLOG_DEBUG1 "send set options to \"%s\" size=%d", getName().c_str(), options.size())); - CProtocolUtil::writef(getOutputStream(), kMsgDSetOptions, &options); + CProtocolUtil::writef(getStream(), kMsgDSetOptions, &options); // check options CLock lock(getMutex()); for (UInt32 i = 0, n = options.size(); i < n; i += 2) { if (options[i] == kOptionHeartbeat) { - m_heartRate = 1.0e-3 * static_cast(options[i + 1]); - if (m_heartRate <= 0.0) { - m_heartRate = -1.0; + double rate = 1.0e-3 * static_cast(options[i + 1]); + if (rate <= 0.0) { + rate = -1.0; } - m_heartDeath = m_heartRate * kHeartBeatsUntilDeath; + m_heartbeatAlarm = rate * kHeartBeatsUntilDeath; + removeHeartbeatTimer(); + addHeartbeatTimer(); } } } @@ -318,7 +400,7 @@ CClientProxy1_0::getCursorCenter(SInt32& x, SInt32& y) const y = m_info.m_my; } -void +bool CClientProxy1_0::recvInfo(bool notify) { { @@ -326,16 +408,18 @@ CClientProxy1_0::recvInfo(bool notify) // parse the message SInt16 x, y, w, h, zoneSize, mx, my; - CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, - &x, &y, &w, &h, &zoneSize, &mx, &my); + if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4, + &x, &y, &w, &h, &zoneSize, &mx, &my)) { + return false; + } LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my)); // validate if (w <= 0 || h <= 0 || zoneSize < 0) { - throw XBadClient(); + return false; } if (mx < x || my < y || mx >= x + w || my >= y + h) { - throw XBadClient(); + return false; } // save @@ -355,44 +439,52 @@ CClientProxy1_0::recvInfo(bool notify) // acknowledge receipt LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCInfoAck); + CProtocolUtil::writef(getStream(), kMsgCInfoAck); + return true; } -void +bool CClientProxy1_0::recvClipboard() { // parse message ClipboardID id; UInt32 seqNum; CString data; - CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); + if (!CProtocolUtil::readf(getStream(), + kMsgDClipboard + 4, &id, &seqNum, &data)) { + return false; + } LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, data.size())); // validate if (id >= kClipboardEnd) { - throw XBadClient(); + return false; } // send update. this calls us back to reset our clipboard dirty flag // so don't hold a lock during the call. getServer()->onClipboardChanged(id, seqNum, data); + return true; } -void +bool CClientProxy1_0::recvGrabClipboard() { // parse message ClipboardID id; UInt32 seqNum; - CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + if (!CProtocolUtil::readf(getStream(), kMsgCClipboard + 4, &id, &seqNum)) { + return false; + } LOG((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getName().c_str(), id, seqNum)); // validate if (id >= kClipboardEnd) { - throw XBadClient(); + return false; } // send update. this calls us back to reset our clipboard dirty flag // so don't hold a lock during the call. getServer()->onGrabClipboard(getName(), id, seqNum); + return true; } diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 0a86e1a9..175f3c37 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -18,12 +18,14 @@ #include "CClientProxy.h" #include "ProtocolTypes.h" +class CEvent; +class CEventQueueTimer; + //! Proxy for client implementing protocol version 1.0 class CClientProxy1_0 : public CClientProxy { public: CClientProxy1_0(IServer* server, const CString& name, - IInputStream* adoptedInput, - IOutputStream* adoptedOutput); + IStream* adoptedStream); ~CClientProxy1_0(); // IClient overrides @@ -54,16 +56,29 @@ public: virtual void getCursorPos(SInt32& x, SInt32& y) const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; +protected: + virtual bool parseMessage(const UInt8* code); + private: - void recvInfo(bool notify); - void recvClipboard(); - void recvGrabClipboard(); + void disconnect(); + void removeHandlers(); + void addHeartbeatTimer(); + void removeHeartbeatTimer(); + + void handleData(const CEvent&, void*); + void handleDisconnect(const CEvent&, void*); + void handleWriteError(const CEvent&, void*); + void handleFlatline(const CEvent&, void*); + + bool recvInfo(bool notify); + bool recvClipboard(); + bool recvGrabClipboard(); private: CClientInfo m_info; bool m_clipboardDirty[kClipboardEnd]; - double m_heartRate; - double m_heartDeath; + double m_heartbeatAlarm; + CEventQueueTimer* m_heartbeatTimer; }; #endif diff --git a/lib/server/CClientProxy1_1.cpp b/lib/server/CClientProxy1_1.cpp index a09b27bd..b5d2cf21 100644 --- a/lib/server/CClientProxy1_1.cpp +++ b/lib/server/CClientProxy1_1.cpp @@ -21,9 +21,9 @@ // CClientProxy1_1 // -CClientProxy1_1::CClientProxy1_1(IServer* server, const CString& name, - IInputStream* input, IOutputStream* output) : - CClientProxy1_0(server, name, input, output) +CClientProxy1_1::CClientProxy1_1(IServer* server, + const CString& name, IStream* stream) : + CClientProxy1_0(server, name, stream) { // do nothing } @@ -37,7 +37,7 @@ void CClientProxy1_1::keyDown(KeyID key, KeyModifierMask mask, KeyButton button) { LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask, button); + CProtocolUtil::writef(getStream(), kMsgDKeyDown, key, mask, button); } void @@ -45,12 +45,12 @@ CClientProxy1_1::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button) { LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d, button=0x%04x", getName().c_str(), key, mask, count, button)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask, count, button); + CProtocolUtil::writef(getStream(), kMsgDKeyRepeat, key, mask, count, button); } void CClientProxy1_1::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) { LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button)); - CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask, button); + CProtocolUtil::writef(getStream(), kMsgDKeyUp, key, mask, button); } diff --git a/lib/server/CClientProxy1_1.h b/lib/server/CClientProxy1_1.h index dbae783e..5e21f21b 100644 --- a/lib/server/CClientProxy1_1.h +++ b/lib/server/CClientProxy1_1.h @@ -21,8 +21,7 @@ class CClientProxy1_1 : public CClientProxy1_0 { public: CClientProxy1_1(IServer* server, const CString& name, - IInputStream* adoptedInput, - IOutputStream* adoptedOutput); + IStream* adoptedStream); ~CClientProxy1_1(); // IClient overrides diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 89417b28..de20f312 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -239,12 +239,6 @@ CConfig::setSynergyAddress(const CNetworkAddress& addr) m_synergyAddress = addr; } -void -CConfig::setHTTPAddress(const CNetworkAddress& addr) -{ - m_httpAddress = addr; -} - bool CConfig::addOption(const CString& name, OptionID option, OptionValue value) { @@ -430,12 +424,6 @@ CConfig::getSynergyAddress() const return m_synergyAddress; } -const CNetworkAddress& -CConfig::getHTTPAddress() const -{ - return m_httpAddress; -} - const CConfig::CScreenOptions* CConfig::getOptions(const CString& name) const { @@ -462,9 +450,6 @@ CConfig::operator==(const CConfig& x) const if (m_synergyAddress != x.m_synergyAddress) { return false; } - if (m_httpAddress != x.m_httpAddress) { - return false; - } */ if (m_map.size() != x.m_map.size()) { return false; @@ -782,14 +767,6 @@ CConfig::readSectionOptions(std::istream& s) throw XConfigRead("invalid address argument"); } } - else if (name == "http") { - try { - m_httpAddress = CNetworkAddress(value, kDefaultPort + 1); - } - catch (XSocketAddress&) { - throw XConfigRead("invalid http argument"); - } - } else if (name == "heartbeat") { addOption("", kOptionHeartbeat, parseInt(value)); } @@ -1063,10 +1040,6 @@ operator<<(std::ostream& s, const CConfig& config) s << "\taddress = " << config.m_synergyAddress.getHostname().c_str() << std::endl; } - if (config.m_httpAddress.isValid()) { - s << "\thttp = " << - config.m_httpAddress.getHostname().c_str() << std::endl; - } s << "end" << std::endl; // screens section diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index 28e2c5b4..a2a9eed5 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -175,13 +175,6 @@ public: */ void setSynergyAddress(const CNetworkAddress&); - //! Set HTTP server address - /*! - Set the HTTP listen addresses. There is no default address so - this must be called to run an HTTP server using this configuration. - */ - void setHTTPAddress(const CNetworkAddress&); - //! Add a screen option /*! Adds an option and its value to the named screen. Replaces the @@ -255,8 +248,6 @@ public: //! Get the server address const CNetworkAddress& getSynergyAddress() const; - //! Get the HTTP server address - const CNetworkAddress& getHTTPAddress() const; //! Get the screen options /*! @@ -308,7 +299,6 @@ private: CCellMap m_map; CNameMap m_nameToCanonicalName; CNetworkAddress m_synergyAddress; - CNetworkAddress m_httpAddress; CScreenOptions m_globalOptions; }; diff --git a/lib/server/CHTTPServer.cpp b/lib/server/CHTTPServer.cpp deleted file mode 100644 index d3582ac4..00000000 --- a/lib/server/CHTTPServer.cpp +++ /dev/null @@ -1,799 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CHTTPServer.h" -#include "CConfig.h" -#include "CHTTPProtocol.h" -#include "CServer.h" -#include "XHTTP.h" -#include "IDataSocket.h" -#include "XThread.h" -#include "CLog.h" -#include "stdset.h" -#include "stdsstream.h" - -// -// CHTTPServer -// - -// maximum size of an HTTP request. this should be large enough to -// handle any reasonable request but small enough to prevent a -// malicious client from causing us to use too much memory. -const UInt32 CHTTPServer::s_maxRequestSize = 32768; - -CHTTPServer::CHTTPServer( - CServer* server) : - m_server(server) -{ - // do nothing -} - -CHTTPServer::~CHTTPServer() -{ - // do nothing -} - -void -CHTTPServer::processRequest(IDataSocket* socket) -{ - assert(socket != NULL); - - CHTTPRequest* request = NULL; - try { - // parse request - request = CHTTPProtocol::readRequest( - socket->getInputStream(), s_maxRequestSize); - if (request == NULL) { - throw XHTTP(400); - } - - // if absolute uri then strip off scheme and host - if (request->m_uri[0] != '/') { - CString::size_type n = request->m_uri.find('/'); - if (n == CString::npos) { - throw XHTTP(404); - } - request->m_uri = request->m_uri.substr(n); - } - - // prepare reply - CHTTPReply reply; - reply.m_majorVersion = request->m_majorVersion; - reply.m_minorVersion = request->m_minorVersion; - reply.m_status = 200; - reply.m_reason = "OK"; - reply.m_method = request->m_method; - - // process - doProcessRequest(*request, reply); - - // send reply - CHTTPProtocol::reply(socket->getOutputStream(), reply); - LOG((CLOG_INFO "HTTP reply %d for %s %s", reply.m_status, request->m_method.c_str(), request->m_uri.c_str())); - - // clean up - delete request; - } - catch (XHTTP& e) { - LOG((CLOG_WARN "returning HTTP error %d %s for %s", e.getStatus(), e.getReason().c_str(), (request != NULL) ? request->m_uri.c_str() : "")); - - // clean up - delete request; - - // return error - CHTTPReply reply; - reply.m_majorVersion = 1; - reply.m_minorVersion = 0; - reply.m_status = e.getStatus(); - reply.m_reason = e.getReason(); - reply.m_method = "GET"; -// FIXME -- use a nicer error page - reply.m_headers.push_back(std::make_pair(CString("Content-Type"), - CString("text/plain"))); - reply.m_body = e.getReason(); - e.addHeaders(reply); - CHTTPProtocol::reply(socket->getOutputStream(), reply); - } - catch (...) { - // ignore other exceptions - RETHROW_XTHREAD - } -} - -void -CHTTPServer::doProcessRequest(CHTTPRequest& request, CHTTPReply& reply) -{ - // switch based on uri - if (request.m_uri == "/editmap") { - if (request.m_method == "GET" || request.m_method == "HEAD") { - doProcessGetEditMap(request, reply); - reply.m_headers.push_back(std::make_pair( - CString("Content-Type"), - CString("text/html"))); - } - else if (request.m_method == "POST") { - doProcessPostEditMap(request, reply); - reply.m_headers.push_back(std::make_pair( - CString("Content-Type"), - CString("text/html"))); - } - else { - throw XHTTPAllow("GET, HEAD, POST"); - } - } - else { - // unknown page - throw XHTTP(404); - } -} - -void -CHTTPServer::doProcessGetEditMap(CHTTPRequest& /*request*/, CHTTPReply& reply) -{ - static const char* s_editMapProlog1 = - "\r\n" - "\r\n" - " Synergy -- Edit Screens\r\n" - "\r\n" - "\r\n" - "
\r\n" - " \r\n"; - static const char* s_editMapEpilog = - " \r\n" - " \r\n" - "
\r\n" - "
\r\n" - "\r\n" - "\r\n"; - static const char* s_editMapTableProlog = - "
" - " \r\n"; - static const char* s_editMapTableEpilog = - "
" - "
\r\n"; - static const char* s_editMapRowProlog = - "\r\n"; - static const char* s_editMapRowEpilog = - "\r\n"; - static const char* s_editMapScreenDummy = - ""; - static const char* s_editMapScreenPrimary = - ""; - static const char* s_editMapScreenEnd = - "\r\n"; - - std::ostringstream s; - - // convert screen map into a temporary screen map - CScreenArray screens; - { - CConfig config; - m_server->getConfig(&config); - screens.convertFrom(config); - // FIXME -- note to user if config couldn't be exactly represented - } - - // insert blank columns and rows around array (to allow the user - // to insert new screens) - screens.insertColumn(0); - screens.insertColumn(screens.getWidth()); - screens.insertRow(0); - screens.insertRow(screens.getHeight()); - - // get array size - const SInt32 w = screens.getWidth(); - const SInt32 h = screens.getHeight(); - - // construct reply - reply.m_body += s_editMapProlog1; - s << w << "x" << h; - reply.m_body += s.str(); - reply.m_body += s_editMapProlog2; - - // add screen map for editing - const CString primaryName = m_server->getPrimaryScreenName(); - reply.m_body += s_editMapTableProlog; - for (SInt32 y = 0; y < h; ++y) { - reply.m_body += s_editMapRowProlog; - for (SInt32 x = 0; x < w; ++x) { - s.str(""); - if (!screens.isAllowed(x, y) && screens.get(x, y) != primaryName) { - s << s_editMapScreenDummy; - } - else { - if (!screens.isSet(x, y)) { - s << s_editMapScreenDead; - } - else if (screens.get(x, y) == primaryName) { - s << s_editMapScreenPrimary; - } - else { - s << s_editMapScreenLive; - } - s << screens.get(x, y) << - s_editMapScreenLiveDead1 << - "n" << x << "x" << y << - s_editMapScreenLiveDead2; - } - s << s_editMapScreenEnd; - reply.m_body += s.str(); - } - reply.m_body += s_editMapRowEpilog; - } - reply.m_body += s_editMapTableEpilog; - - reply.m_body += s_editMapEpilog; -} - -void -CHTTPServer::doProcessPostEditMap(CHTTPRequest& request, CHTTPReply& reply) -{ - typedef std::vector ScreenArray; - typedef std::set ScreenSet; - - // parse the result - CHTTPProtocol::CFormParts parts; - if (!CHTTPProtocol::parseFormData(request, parts)) { - LOG((CLOG_WARN "editmap: cannot parse form data")); - throw XHTTP(400); - } - - try { - std::ostringstream s; - - // convert post data into a temporary screen map. also check - // that no screen name is invalid or used more than once. - SInt32 w, h; - CHTTPProtocol::CFormParts::iterator index = parts.find("size"); - if (index == parts.end() || - !parseXY(index->second, w, h) || - w <= 0 || h <= 0) { - LOG((CLOG_WARN "editmap: cannot parse size or size is invalid")); - throw XHTTP(400); - } - ScreenSet screenNames; - CScreenArray screens; - screens.resize(w, h); - for (SInt32 y = 0; y < h; ++y) { - for (SInt32 x = 0; x < w; ++x) { - // find part - s.str(""); - s << "n" << x << "x" << y; - index = parts.find(s.str()); - if (index == parts.end()) { - // FIXME -- screen is missing. error? - continue; - } - - // skip blank names - const CString& name = index->second; - if (name.empty()) { - continue; - } - - // check name. name must be legal and must not have - // already been seen. - if (screenNames.count(name)) { - // FIXME -- better error message - LOG((CLOG_WARN "editmap: duplicate name %s", name.c_str())); - throw XHTTP(400); - } - // FIXME -- check that name is legal - - // save name. if we've already seen the name then - // report an error. - screens.set(x, y, name); - screenNames.insert(name); - } - } - - // if new map is invalid then return error. map is invalid if: - // there are no screens, or - // the screens are not 4-connected. - if (screenNames.empty()) { - // no screens - // FIXME -- need better no screens - LOG((CLOG_WARN "editmap: no screens")); - throw XHTTP(400); - } - if (!screens.isValid()) { - // FIXME -- need better unconnected screens error - LOG((CLOG_WARN "editmap: unconnected screens")); - throw XHTTP(400); - } - - // convert temporary screen map into a regular map - CConfig config; - m_server->getConfig(&config); - screens.convertTo(config); - - // set new screen map on server - m_server->setConfig(config); - - // now reply with current map - doProcessGetEditMap(request, reply); - } - catch (XHTTP&) { - // FIXME -- construct a more meaningful error? - throw; - } -} - -bool -CHTTPServer::parseXY(const CString& xy, SInt32& x, SInt32& y) -{ - std::istringstream s(xy); - char delimiter; - s >> x; - s.get(delimiter); - s >> y; - return (!!s && delimiter == 'x'); -} - - -// -// CHTTPServer::CScreenArray -// - -CHTTPServer::CScreenArray::CScreenArray() : - m_w(0), - m_h(0) -{ - // do nothing -} - -CHTTPServer::CScreenArray::~CScreenArray() -{ - // do nothing -} - -void -CHTTPServer::CScreenArray::resize(SInt32 w, SInt32 h) -{ - m_screens.clear(); - m_screens.resize(w * h); - m_w = w; - m_h = h; -} - -void -CHTTPServer::CScreenArray::insertRow(SInt32 i) -{ - assert(i >= 0 && i <= m_h); - - CNames newScreens; - newScreens.resize(m_w * (m_h + 1)); - - for (SInt32 y = 0; y < i; ++y) { - for (SInt32 x = 0; x < m_w; ++x) { - newScreens[x + y * m_w] = m_screens[x + y * m_w]; - } - } - for (SInt32 y = i; y < m_h; ++y) { - for (SInt32 x = 0; x < m_w; ++x) { - newScreens[x + (y + 1) * m_w] = m_screens[x + y * m_w]; - } - } - - m_screens.swap(newScreens); - ++m_h; -} - -void -CHTTPServer::CScreenArray::insertColumn(SInt32 i) -{ - assert(i >= 0 && i <= m_w); - - CNames newScreens; - newScreens.resize((m_w + 1) * m_h); - - for (SInt32 y = 0; y < m_h; ++y) { - for (SInt32 x = 0; x < i; ++x) { - newScreens[x + y * (m_w + 1)] = m_screens[x + y * m_w]; - } - for (SInt32 x = i; x < m_w; ++x) { - newScreens[(x + 1) + y * (m_w + 1)] = m_screens[x + y * m_w]; - } - } - - m_screens.swap(newScreens); - ++m_w; -} - -void -CHTTPServer::CScreenArray::eraseRow(SInt32 i) -{ - assert(i >= 0 && i < m_h); - - CNames newScreens; - newScreens.resize(m_w * (m_h - 1)); - - for (SInt32 y = 0; y < i; ++y) { - for (SInt32 x = 0; x < m_w; ++x) { - newScreens[x + y * m_w] = m_screens[x + y * m_w]; - } - } - for (SInt32 y = i + 1; y < m_h; ++y) { - for (SInt32 x = 0; x < m_w; ++x) { - newScreens[x + (y - 1) * m_w] = m_screens[x + y * m_w]; - } - } - - m_screens.swap(newScreens); - --m_h; -} - -void -CHTTPServer::CScreenArray::eraseColumn(SInt32 i) -{ - assert(i >= 0 && i < m_w); - - CNames newScreens; - newScreens.resize((m_w - 1) * m_h); - - for (SInt32 y = 0; y < m_h; ++y) { - for (SInt32 x = 0; x < m_w; ++x) { - newScreens[x + y * (m_w - 1)] = m_screens[x + y * m_w]; - } - for (SInt32 x = i + 1; x < m_w; ++x) { - newScreens[(x - 1) + y * (m_w - 1)] = m_screens[x + y * m_w]; - } - } - - m_screens.swap(newScreens); - --m_w; -} - -void -CHTTPServer::CScreenArray::rotateRows(SInt32 i) -{ - // nothing to do if no rows - if (m_h == 0) { - return; - } - - // convert to canonical form - if (i < 0) { - i = m_h - ((-i) % m_h); - } - else { - i %= m_h; - } - if (i == 0 || i == m_h) { - return; - } - - while (i > 0) { - // rotate one row - for (SInt32 x = 0; x < m_w; ++x) { - CString tmp = m_screens[x]; - for (SInt32 y = 1; y < m_h; ++y) { - m_screens[x + (y - 1) * m_w] = m_screens[x + y * m_w]; - } - m_screens[x + (m_h - 1) * m_w] = tmp; - } - } -} - -void -CHTTPServer::CScreenArray::rotateColumns(SInt32 i) -{ - // nothing to do if no columns - if (m_h == 0) { - return; - } - - // convert to canonical form - if (i < 0) { - i = m_w - ((-i) % m_w); - } - else { - i %= m_w; - } - if (i == 0 || i == m_w) { - return; - } - - while (i > 0) { - // rotate one column - for (SInt32 y = 0; y < m_h; ++y) { - CString tmp = m_screens[0 + y * m_w]; - for (SInt32 x = 1; x < m_w; ++x) { - m_screens[x - 1 + y * m_w] = m_screens[x + y * m_w]; - } - m_screens[m_w - 1 + y * m_w] = tmp; - } - } -} - -void -CHTTPServer::CScreenArray::remove(SInt32 x, SInt32 y) -{ - set(x, y, CString()); -} - -void -CHTTPServer::CScreenArray::set(SInt32 x, SInt32 y, const CString& name) -{ - assert(x >= 0 && x < m_w); - assert(y >= 0 && y < m_h); - - m_screens[x + y * m_w] = name; -} - -bool -CHTTPServer::CScreenArray::isAllowed(SInt32 x, SInt32 y) const -{ - assert(x >= 0 && x < m_w); - assert(y >= 0 && y < m_h); - - if (x > 0 && !m_screens[(x - 1) + y * m_w].empty()) { - return true; - } - if (x < m_w - 1 && !m_screens[(x + 1) + y * m_w].empty()) { - return true; - } - if (y > 0 && !m_screens[x + (y - 1) * m_w].empty()) { - return true; - } - if (y < m_h - 1 && !m_screens[x + (y + 1) * m_w].empty()) { - return true; - } - return false; -} - -bool -CHTTPServer::CScreenArray::isSet(SInt32 x, SInt32 y) const -{ - assert(x >= 0 && x < m_w); - assert(y >= 0 && y < m_h); - - return !m_screens[x + y * m_w].empty(); -} - -CString -CHTTPServer::CScreenArray::get(SInt32 x, SInt32 y) const -{ - assert(x >= 0 && x < m_w); - assert(y >= 0 && y < m_h); - - return m_screens[x + y * m_w]; -} - -bool -CHTTPServer::CScreenArray::find(const CString& name, - SInt32& xOut, SInt32& yOut) const -{ - for (SInt32 y = 0; y < m_h; ++y) { - for (SInt32 x = 0; x < m_w; ++x) { - if (m_screens[x + y * m_w] == name) { - xOut = x; - yOut = y; - return true; - } - } - } - return false; -} - -bool -CHTTPServer::CScreenArray::isValid() const -{ - SInt32 count = 0, isolated = 0; - for (SInt32 y = 0; y < m_h; ++y) { - for (SInt32 x = 0; x < m_w; ++x) { - if (isSet(x, y)) { - ++count; - if (!isAllowed(x, y)) { - ++isolated; - } - } - } - } - return (count <= 1 || isolated == 0); -} - -bool -CHTTPServer::CScreenArray::convertFrom(const CConfig& config) -{ - typedef std::set ScreenSet; - - // insert the first screen - CConfig::const_iterator index = config.begin(); - if (index == config.end()) { - // no screens - resize(0, 0); - return true; - } - CString name = *index; - resize(1, 1); - set(0, 0, name); - - // flood fill state - CNames screenStack; - ScreenSet doneSet; - - // put all but the first screen on the stack - // note -- if all screens are 4-connected then we can skip this - while (++index != config.end()) { - screenStack.push_back(*index); - } - - // put the first screen on the stack last so we process it first - screenStack.push_back(name); - - // perform a flood fill using the stack as the seeds - while (!screenStack.empty()) { - // get next screen from stack - CString name = screenStack.back(); - screenStack.pop_back(); - - // skip screen if we've seen it before - if (doneSet.count(name) > 0) { - continue; - } - - // add this screen to doneSet so we don't process it again - doneSet.insert(name); - - // find the screen. if it's not found then not all of the - // screens are 4-connected. discard disconnected screens. - SInt32 x, y; - if (!find(name, x, y)) { - continue; - } - - // insert the screen's neighbors - // FIXME -- handle edge wrapping - CString neighbor; - neighbor = config.getNeighbor(name, kLeft); - if (!neighbor.empty() && doneSet.count(neighbor) == 0) { - // insert left neighbor, adding a column if necessary - if (x == 0 || get(x - 1, y) != neighbor) { - ++x; - insertColumn(x - 1); - set(x - 1, y, neighbor); - } - screenStack.push_back(neighbor); - } - neighbor = config.getNeighbor(name, kRight); - if (!neighbor.empty() && doneSet.count(neighbor) == 0) { - // insert right neighbor, adding a column if necessary - if (x == m_w - 1 || get(x + 1, y) != neighbor) { - insertColumn(x + 1); - set(x + 1, y, neighbor); - } - screenStack.push_back(neighbor); - } - neighbor = config.getNeighbor(name, kTop); - if (!neighbor.empty() && doneSet.count(neighbor) == 0) { - // insert top neighbor, adding a row if necessary - if (y == 0 || get(x, y - 1) != neighbor) { - ++y; - insertRow(y - 1); - set(x, y - 1, neighbor); - } - screenStack.push_back(neighbor); - } - neighbor = config.getNeighbor(name, kBottom); - if (!neighbor.empty() && doneSet.count(neighbor) == 0) { - // insert bottom neighbor, adding a row if necessary - if (y == m_h - 1 || get(x, y + 1) != neighbor) { - insertRow(y + 1); - set(x, y + 1, neighbor); - } - screenStack.push_back(neighbor); - } - } - - // check symmetry - // FIXME -- handle edge wrapping - for (index = config.begin(); index != config.end(); ++index) { - const CString& name = *index; - SInt32 x, y; - if (!find(name, x, y)) { - return false; - } - - CString neighbor; - neighbor = config.getNeighbor(name, kLeft); - if ((x == 0 && !neighbor.empty()) || - (x > 0 && get(x - 1, y) != neighbor)) { - return false; - } - - neighbor = config.getNeighbor(name, kRight); - if ((x == m_w - 1 && !neighbor.empty()) || - (x < m_w - 1 && get(x + 1, y) != neighbor)) { - return false; - } - - neighbor = config.getNeighbor(name, kTop); - if ((y == 0 && !neighbor.empty()) || - (y > 0 && get(x, y - 1) != neighbor)) { - return false; - } - - neighbor = config.getNeighbor(name, kBottom); - if ((y == m_h - 1 && !neighbor.empty()) || - (y < m_h - 1 && get(x, y + 1) != neighbor)) { - return false; - } - } - - return true; -} - -void -CHTTPServer::CScreenArray::convertTo(CConfig& config) const -{ - config.removeAllScreens(); - - // add screens and find smallest box containing all screens - SInt32 x0 = m_w, x1 = 0, y0 = m_h, y1 = 0; - for (SInt32 y = 0; y < m_h; ++y) { - for (SInt32 x = 0; x < m_w; ++x) { - if (isSet(x, y)) { - config.addScreen(get(x, y)); - if (x < x0) { - x0 = x; - } - if (x > x1) { - x1 = x; - } - if (y < y0) { - y0 = y; - } - if (y > y1) { - y1 = y; - } - } - - } - } - - // make connections between screens - // FIXME -- add support for wrapping - // FIXME -- mark topmost and leftmost screens - for (SInt32 y = 0; y < m_h; ++y) { - for (SInt32 x = 0; x < m_w; ++x) { - if (!isSet(x, y)) { - continue; - } - if (x > x0 && isSet(x - 1, y)) { - config.connect(get(x, y), kLeft, get(x - 1, y)); - } - if (x < x1 && isSet(x + 1, y)) { - config.connect(get(x, y), kRight, get(x + 1, y)); - } - if (y > y0 && isSet(x, y - 1)) { - config.connect(get(x, y), kTop, get(x, y - 1)); - } - if (y < y1 && isSet(x, y + 1)) { - config.connect(get(x, y), kBottom, get(x, y + 1)); - } - } - } -} diff --git a/lib/server/CHTTPServer.h b/lib/server/CHTTPServer.h deleted file mode 100644 index 18f80440..00000000 --- a/lib/server/CHTTPServer.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CHTTPSERVER_H -#define CHTTPSERVER_H - -#include "CString.h" -#include "BasicTypes.h" -#include "stdvector.h" - -class CServer; -class CConfig; -class CHTTPRequest; -class CHTTPReply; -class IDataSocket; - -//! Simple HTTP server -/*! -This class implements a simple HTTP server for interacting with the -synergy server. -*/ -class CHTTPServer { -public: - CHTTPServer(CServer*); - virtual ~CHTTPServer(); - - //! @name manipulators - //@{ - - //! Process HTTP request - /*! - Synchronously processes an HTTP request on the given socket. - */ - void processRequest(IDataSocket*); - - //@} - -protected: - //! Process HTTP request - /*! - Processes a successfully read HTTP request. The reply is partially - filled in (version, method, status (200) and reason (OK)). This - method checks the URI and handles the request, filling in the rest - of the reply. If the request cannot be satisfied it throws an - appropriate XHTTP exception. - */ - virtual void doProcessRequest(CHTTPRequest&, CHTTPReply&); - - //! Process request for map - virtual void doProcessGetEditMap(CHTTPRequest&, CHTTPReply&); - - //! Process request for changing map - virtual void doProcessPostEditMap(CHTTPRequest&, CHTTPReply&); - - //! Parse coordinate string - static bool parseXY(const CString&, SInt32& x, SInt32& y); - - //! Screen map helper - /*! - This class represents the screen map as a resizable array. It's - used to handle map requests. - */ - class CScreenArray { - public: - CScreenArray(); - ~CScreenArray(); - - // resize the array. this also clears all the elements. - void resize(SInt32 w, SInt32 h); - - // insert/remove a row/column. all elements in a new row/column - // are unset. - void insertRow(SInt32 insertedBeforeRow); - void insertColumn(SInt32 insertedBeforeColumn); - void eraseRow(SInt32 row); - void eraseColumn(SInt32 column); - - // rotate rows or columns - void rotateRows(SInt32 rowsDown); - void rotateColumns(SInt32 columnsDown); - - // remove/set a screen name. setting an empty name is the - // same as removing a name. names are not checked for - // validity. - void remove(SInt32 x, SInt32 y); - void set(SInt32 x, SInt32 y, const CString&); - - // convert a CConfig to a CScreenArray. returns true iff - // all connections are symmetric and therefore exactly - // representable by a CScreenArray. - bool convertFrom(const CConfig&); - - // accessors - - // get the array size - SInt32 getWidth() const { return m_w; } - SInt32 getHeight() const { return m_h; } - - // returns true iff the cell has a 4-connected neighbor - bool isAllowed(SInt32 x, SInt32 y) const; - - // returns true iff the cell has a (non-empty) name - bool isSet(SInt32 x, SInt32 y) const; - - // get a screen name - CString get(SInt32 x, SInt32 y) const; - - // find a screen by name. returns true iff found. - bool find(const CString&, SInt32& x, SInt32& y) const; - - // return true iff the overall array is valid. that means - // just zero or one screen or all screens are 4-connected - // to other screens. - bool isValid() const; - - // convert this to a CConfig - void convertTo(CConfig&) const; - - private: - typedef std::vector CNames; - - SInt32 m_w, m_h; - CNames m_screens; - }; - -private: - CServer* m_server; - static const UInt32 s_maxRequestSize; -}; - -#endif diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 0710875d..47361435 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -13,7 +13,6 @@ */ #include "CServer.h" -#include "CHTTPServer.h" #include "CPrimaryClient.h" #include "IScreenFactory.h" #include "CInputPacketStream.h" @@ -45,8 +44,6 @@ // CServer // -const SInt32 CServer::s_httpMaxSimultaneousRequests = 3; - CServer::CServer(const CString& serverName) : m_name(serverName), m_error(false), @@ -59,8 +56,6 @@ CServer::CServer(const CString& serverName) : m_primaryClient(NULL), m_seqNum(0), m_activeSaver(NULL), - m_httpServer(NULL), - m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests), m_switchDir(kNoDirection), m_switchScreen(NULL), m_switchWaitDelay(0.0), @@ -118,18 +113,15 @@ CServer::mainLoop() setStatus(kNotRunning); LOG((CLOG_NOTE "starting server")); +// FIXME -- started here + createClientListener(); +// FIXME -- finished here + // start listening for new clients m_acceptClientThread = new CThread(startThread( new TMethodJob(this, &CServer::acceptClients))); - // start listening for HTTP requests - if (m_config.getHTTPAddress().isValid()) { - m_httpServer = new CHTTPServer(this); - startThread(new TMethodJob(this, - &CServer::acceptHTTPClients)); - } - // handle events m_primaryClient->mainLoop(); @@ -144,8 +136,6 @@ CServer::mainLoop() // bucket. #define FINALLY do { \ stopThreads(); \ - delete m_httpServer; \ - m_httpServer = NULL; \ runStatusJobs(); \ } while (false) FINALLY; @@ -360,6 +350,16 @@ CServer::getConfig(CConfig* config) const *config = m_config; } +CString +CServer::getCanonicalName(const CString& name) const +{ + CLock lock(&m_mutex); + if (m_config.isScreen(name)) { + return m_config.getCanonicalName(name); + } + return name; +} + void CServer::runStatusJobs() const { @@ -411,11 +411,6 @@ CServer::onError() // threads may be unable to proceed until this thread returns. stopThreads(3.0); - // done with the HTTP server - CLock lock(&m_mutex); - delete m_httpServer; - m_httpServer = NULL; - // note -- we do not attempt to close down the primary screen } @@ -1673,23 +1668,6 @@ CServer::handshakeClient(IDataSocket* socket) { LOG((CLOG_DEBUG1 "negotiating with new client")); - // get the input and output streams - IInputStream* input = socket->getInputStream(); - IOutputStream* output = socket->getOutputStream(); - bool own = false; - - // attach filters - if (m_streamFilterFactory != NULL) { - input = m_streamFilterFactory->createInput(input, own); - output = m_streamFilterFactory->createOutput(output, own); - own = true; - } - - // attach the packetizing filters - input = new CInputPacketStream(input, own); - output = new COutputPacketStream(output, own); - own = true; - CClientProxy* proxy = NULL; CString name(""); try { @@ -1810,111 +1788,6 @@ CServer::handshakeClient(IDataSocket* socket) return NULL; } -void -CServer::acceptHTTPClients(void*) -{ - LOG((CLOG_DEBUG1 "starting to wait for HTTP clients")); - - IListenSocket* listen = NULL; - try { - // create socket listener - listen = new CTCPListenSocket; - - // bind to the desired port. keep retrying if we can't bind - // the address immediately. - CStopwatch timer; - for (;;) { - try { - LOG((CLOG_DEBUG1 "binding HTTP listen socket")); - listen->bind(m_config.getHTTPAddress()); - break; - } - catch (XSocketBind& e) { - LOG((CLOG_DEBUG1 "bind HTTP failed: %s", e.what())); - - // give up if we've waited too long - if (timer.getTime() >= m_bindTimeout) { - LOG((CLOG_DEBUG1 "waited too long to bind HTTP, giving up")); - throw; - } - - // wait a bit before retrying - ARCH->sleep(5.0); - } - } - - // accept connections and begin processing them - LOG((CLOG_DEBUG1 "waiting for HTTP connections")); - for (;;) { - // limit the number of HTTP requests being handled at once - { - CLock lock(&m_httpAvailable); - while (m_httpAvailable == 0) { - m_httpAvailable.wait(); - } - assert(m_httpAvailable > 0); - m_httpAvailable = m_httpAvailable - 1; - } - - // accept connection - CThread::testCancel(); - IDataSocket* socket = listen->accept(); - LOG((CLOG_NOTE "accepted HTTP connection")); - CThread::testCancel(); - - // handle HTTP request - startThread(new TMethodJob( - this, &CServer::processHTTPRequest, socket)); - } - - // clean up - delete listen; - } - catch (XBase& e) { - LOG((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); - delete listen; - exitMainLoopWithError(); - } - catch (...) { - delete listen; - throw; - } -} - -void -CServer::processHTTPRequest(void* vsocket) -{ - IDataSocket* socket = reinterpret_cast(vsocket); - try { - // process the request and force delivery - m_httpServer->processRequest(socket); - socket->getOutputStream()->flush(); - - // wait a moment to give the client a chance to hangup first - ARCH->sleep(3.0); - - // clean up - socket->close(); - delete socket; - - // increment available HTTP handlers - { - CLock lock(&m_httpAvailable); - m_httpAvailable = m_httpAvailable + 1; - m_httpAvailable.signal(); - } - } - catch (...) { - delete socket; - { - CLock lock(&m_httpAvailable); - m_httpAvailable = m_httpAvailable + 1; - m_httpAvailable.signal(); - } - throw; - } -} - void CServer::sendOptions(IClient* client) const { @@ -2141,3 +2014,259 @@ CServer::CClipboardInfo::CClipboardInfo() : { // do nothing } + +// --- transitional --- + + +void +CServer::clientConnecting(const CEvent&, void*) +{ + // accept client connection + IDataSocket* socket = m_listen->accept(); + if (socket == NULL) { + return; + } + + LOG((CLOG_NOTE "accepted client connection")); + + // filter socket's streams then wrap the result in a new socket + IInputStream* input = socket->getInputStream(); + IOutputStream* output = socket->getOutputStream(); + bool own = false; + if (m_streamFilterFactory != NULL) { + input = m_streamFilterFactory->createInput(input, own); + output = m_streamFilterFactory->createOutput(output, own); + own = true; + } + input = new CInputPacketStream(input, own); + output = new COutputPacketStream(output, own); + socket = new CDataSocket(socket, + new CInputOutputStream(input, output, true)); + +// FIXME -- may want to move these event handlers (but not the +// handshake timer?) and the handshake into a new proxy object. +// we save this proxy as a provisional connection. it calls +// back to us (or maybe sends an event) to notify of failure or +// success. failure is socket failure or protocol error. +// success returns a new proxy of the appropriate version. we +// need to verify the validity of the client's name then remove +// the provisional connection and install a true connection. +// if we keep the timer then when it expires we just remove and +// delete the provisional connection. if we install a true +// connection then we remove the timer. + CProvisionalClient* client; + try { + client = new CProvisionalClient(this, socket); + } + catch (XBase&) { + delete socket; + // handle error + return; + } + + // start timer for handshake + CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(30.0, client); + + // add client to client map + m_provisional.insert(client, timer); +} + +void +CServer::recvClientHello(const CEvent&, void* vsocket) +{ + IDataSocket* socket = reinterpret_cast(vsocket); + + LOG((CLOG_DEBUG1 "parsing hello reply")); + + CClientProxy* proxy = NULL; + CString name(""); + try { + // limit the maximum length of the hello + UInt32 n = socket->getInputStream()->getSize(); + if (n > kMaxHelloLength) { + LOG((CLOG_DEBUG1 "hello reply too long")); + throw XBadClient(); + } + + // parse the reply to hello + SInt16 major, minor; + CProtocolUtil::readf(socket->getInputStream(), kMsgHelloBack, + &major, &minor, &name); + + // disallow invalid version numbers + if (major <= 0 || minor < 0) { + throw XIncompatibleClient(major, minor); + } + + // convert name to canonical form (if any) +// FIXME -- need lock? + if (m_config.isScreen(name)) { + name = m_config.getCanonicalName(name); + } + + // create client proxy for highest version supported by the client + LOG((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor)); + if (major == 1) { + switch (minor) { + case 0: +// FIXME -- should pass socket, not input and output + proxy = new CClientProxy1_0(this, name, input, output); + break; + + case 1: +// FIXME -- should pass socket, not input and output + proxy = new CClientProxy1_1(this, name, input, output); + break; + } + } + + // hangup (with error) if version isn't supported + if (proxy == NULL) { + throw XIncompatibleClient(major, minor); + } + + // the proxy now owns the socket + socket = NULL; + + // add provisional client connection. this also checks if + // the client's name is okay (i.e. in the map and not in use). + addProvisionalClient(proxy); + + // negotiate + // FIXME + + // request the client's info and install a handler for it. note + // that the handshake timer is still going. + EVENTQUEUE->adoptHandler(IInputStream::getInputReadyEvent(), + socket->getInputStream(), + new TMethodEventJob(this, + &CServer::recvClientInfo, socket)); + LOG((CLOG_DEBUG1 "request info for client \"%s\"", name.c_str())); +// FIXME -- maybe should send this request some other way. +// could have the proxy itself handle the input. that makes sense +// but will have to check if that'll work. for one thing, the proxy +// will have to inform this object of any errors. it takes this +// object as a parameter so it probably can do that. +/* + proxy->open(); +*/ + return; + } + catch (XDuplicateClient& e) { + // client has duplicate name + LOG((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); + CProtocolUtil::writefNoError(proxy->getOutputStream(), kMsgEBusy); + } + catch (XUnknownClient& e) { + // client has unknown name + LOG((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); + CProtocolUtil::writefNoError(proxy->getOutputStream(), kMsgEUnknown); + } + catch (XIncompatibleClient& e) { + // client is incompatible + LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); + CProtocolUtil::writefNoError(socket->getOutputStream(), + kMsgEIncompatible, + kProtocolMajorVersion, kProtocolMinorVersion); + } + catch (XBadClient&) { + // client not behaving + LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); + CProtocolUtil::writefNoError(socket->getOutputStream(), kMsgEBad); + } + catch (XIO&) { + // client not behaving + LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); + CProtocolUtil::writefNoError(socket->getOutputStream(), kMsgEBad); + } + catch (XBase& e) { + // misc error + LOG((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); + } + +// FIXME -- really clean up, including event handlers and timer. +// should have a method for this. + delete proxy; + delete socket; +} + +void +CServer::recvClientInfo(const CEvent&, void* vsocket) +{ + // FIXME -- get client's info and proxy becomes first class citizen. + // adopt new input handler aimed at clientInput. remove timer. +} + +void +CServer::clientTimeout(const CEvent&, void* vsocket) +{ + // FIXME -- client failed to connect fast enough. if client is + // already first class then just ignore this (that should never + // happen though because timer events are synthesized in the + // getEvent() so there can't be a race, so don't fuss over + // checking). +} + +void +CServer::clientInput(const CEvent&, void* vsocket) +{ + // FIXME -- client has sent a message +} + +void +CServer::clientDisconnected(const CEvent&, void* vsocket) +{ + // FIXME -- handle disconnection of client +} + +void +CServer::createClientListener() +{ + LOG((CLOG_DEBUG1 "creating socket to listen for clients")); + + assert(m_socketFactory != NULL); + IListenSocket* listen = NULL; + try { + // create socket + listen = m_socketFactory->createListen(); + + // setup event handler + EVENTQUEUE->adoptHandler(IListenSocket::getConnectingEvent(), listen, + new TMethodEventJob(this, + &CServer::clientConnecting)); + + // bind listen address + LOG((CLOG_DEBUG1 "binding listen socket")); + listen->bind(m_config.getSynergyAddress()); + } + catch (XSocketAddressInUse& e) { + if (listen != NULL) { + EVENTQUEUE->removeHandler( + IListenSocket::getConnectingEvent(), listen); + delete listen; + } + setStatus(kError, e.what()); + LOG((CLOG_WARN "bind failed: %s", e.what())); + // FIXME -- throw retry in X seconds object. the caller should + // catch this and optionally wait X seconds and try again. + // alternatively we could install a timer and retry automatically. + throw; + } + catch (...) { + if (listen != NULL) { + EVENTQUEUE->removeHandler( + IListenSocket::getConnectingEvent(), listen); + delete listen; + } +/* FIXME -- set some status and log error + setStatus(kNotRunning); + setStatus(kError, e.what()); + LOG((CLOG_ERR "cannot listen for clients: %s", e.what())); +*/ + throw; + } + + m_listen = listen; + setStatus(kRunning); + LOG((CLOG_DEBUG1 "waiting for client connections")); +} diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 92476365..f7a9d735 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -29,7 +29,6 @@ #include "stdvector.h" class CClientProxy; -class CHTTPServer; class CPrimaryClient; class IClient; class IDataSocket; @@ -151,6 +150,12 @@ public: */ void getConfig(CConfig*) const; + //! Get canonical screen name + /*! + Returns the canonical version of a screen name. + */ + CString getCanonicalName(const CString& name) const; + //! Get name /*! Returns the server's name passed to the c'tor @@ -295,12 +300,6 @@ private: void runClient(void*); CClientProxy* handshakeClient(IDataSocket*); - // thread method to accept incoming HTTP connections - void acceptHTTPClients(void*); - - // thread method to process HTTP requests - void processHTTPRequest(void*); - // connection list maintenance void addConnection(IClient*); void removeConnection(const CString& name); @@ -381,11 +380,6 @@ private: IClient* m_activeSaver; SInt32 m_xSaver, m_ySaver; - // HTTP request processing stuff - CHTTPServer* m_httpServer; - CCondVar m_httpAvailable; - static const SInt32 s_httpMaxSimultaneousRequests; - // common state for screen switch tests. all tests are always // trying to reach the same screen in the same direction. EDirection m_switchDir; @@ -408,6 +402,15 @@ private: CJobList m_statusJobs; EStatus m_status; CString m_statusMessage; + +//--- +/* + IListenSocket* m_listen; + + typedef std::map CProvisionalClients; + CProvisionalClients m_provisional; +*/ }; #endif diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index a414bc90..cab17f22 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -29,15 +29,15 @@ libserver_a_SOURCES = \ CClientProxy1_0.cpp \ CClientProxy1_1.cpp \ CConfig.cpp \ - CHTTPServer.cpp \ CPrimaryClient.cpp \ + CProvisionalClient.cpp \ CServer.cpp \ CClientProxy.h \ CClientProxy1_0.h \ CClientProxy1_1.h \ CConfig.h \ - CHTTPServer.h \ CPrimaryClient.h \ + CProvisionalClient.h \ CServer.h \ $(NULL) INCLUDES = \ @@ -46,7 +46,6 @@ INCLUDES = \ -I$(VDEPTH)/lib/base \ -I$(VDEPTH)/lib/mt \ -I$(VDEPTH)/lib/io \ - -I$(VDEPTH)/lib/http \ -I$(VDEPTH)/lib/net \ -I$(VDEPTH)/lib/synergy \ -I$(VDEPTH)/lib/platform \ diff --git a/lib/synergy/CInputPacketStream.cpp b/lib/synergy/CInputPacketStream.cpp deleted file mode 100644 index 8b0096fc..00000000 --- a/lib/synergy/CInputPacketStream.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CInputPacketStream.h" -#include "CLock.h" -#include "CStopwatch.h" - -// -// CInputPacketStream -// - -CInputPacketStream::CInputPacketStream(IInputStream* stream, bool adopt) : - CInputStreamFilter(stream, adopt), - m_mutex(), - m_size(0), - m_buffer(&m_mutex, NULL) -{ - // do nothing -} - -CInputPacketStream::~CInputPacketStream() -{ - // do nothing -} - -void -CInputPacketStream::close() -{ - getStream()->close(); -} - -UInt32 -CInputPacketStream::read(void* buffer, UInt32 n, double timeout) -{ - CLock lock(&m_mutex); - - // wait for entire message to be read. return if stream - // hungup or timeout. - switch (waitForFullMessage(timeout)) { - case kData: - break; - - case kHungup: - return 0; - - case kTimedout: - return (UInt32)-1; - } - - // limit number of bytes to read to the number of bytes left in the - // current message. - if (n > m_size) { - n = m_size; - } - - // now read from our buffer - n = m_buffer.readNoLock(buffer, n, -1.0); - assert(n <= m_size); - m_size -= n; - - return n; -} - -UInt32 -CInputPacketStream::getSize() const -{ - CLock lock(&m_mutex); - return getSizeNoLock(); -} - -UInt32 -CInputPacketStream::getSizeNoLock() const -{ - CStopwatch timer(true); - while (!hasFullMessage() && getStream()->getSize() > 0) { - // read more data - if (getMoreMessage(-1.0) != kData) { - // stream hungup - return 0; - } - } - - return m_size; -} - -CInputPacketStream::EResult -CInputPacketStream::waitForFullMessage(double timeout) const -{ - CStopwatch timer(true); - while (!hasFullMessage()) { - // compute remaining timeout - double t = timeout - timer.getTime(); - if (timeout >= 0.0 && t <= 0.0) { - // timeout - return kTimedout; - } - - // read more data - switch (getMoreMessage(t)) { - case kData: - break; - - case kHungup: - // stream hungup - return kHungup; - - case kTimedout: - // stream timed out - return kTimedout; - } - } - - return kData; -} - -CInputPacketStream::EResult -CInputPacketStream::getMoreMessage(double timeout) const -{ - // read more data - char buffer[4096]; - UInt32 n = getStream()->read(buffer, sizeof(buffer), timeout); - - // return if stream timed out - if (n == (UInt32)-1) { - return kTimedout; - } - - // return if stream hungup - if (n == 0) { - m_buffer.hangup(); - return kHungup; - } - - // append to our buffer - m_buffer.write(buffer, n); - - return kData; -} - -bool -CInputPacketStream::hasFullMessage() const -{ - // get payload length if we don't have it yet - if (m_size == 0) { - // check if length field has been read yet - if (m_buffer.getSizeNoLock() < 4) { - // not enough data for payload length - return false; - } - - // save payload length - UInt8 buffer[4]; - UInt32 n = m_buffer.readNoLock(buffer, sizeof(buffer), -1.0); - assert(n == 4); - m_size = ((UInt32)buffer[0] << 24) | - ((UInt32)buffer[1] << 16) | - ((UInt32)buffer[2] << 8) | - (UInt32)buffer[3]; - - // if payload length is zero then discard null message - if (m_size == 0) { - return false; - } - } - assert(m_size > 0); - - // we have the full message when we have at least m_size bytes in - // the buffer - return (m_buffer.getSizeNoLock() >= m_size); -} diff --git a/lib/synergy/CInputPacketStream.h b/lib/synergy/CInputPacketStream.h deleted file mode 100644 index a4122f33..00000000 --- a/lib/synergy/CInputPacketStream.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CINPUTPACKETSTREAM_H -#define CINPUTPACKETSTREAM_H - -#include "CInputStreamFilter.h" -#include "CBufferedInputStream.h" -#include "CMutex.h" - -//! Packetizing input stream filter -/*! -Filters an input stream to extract packet by packet. -*/ -class CInputPacketStream : public CInputStreamFilter { -public: - CInputPacketStream(IInputStream*, bool adoptStream = true); - ~CInputPacketStream(); - - // IInputStream overrides - virtual void close(); - virtual UInt32 read(void*, UInt32 maxCount, double timeout); - virtual UInt32 getSize() const; - -private: - enum EResult { kData, kHungup, kTimedout }; - - UInt32 getSizeNoLock() const; - EResult waitForFullMessage(double timeout) const; - EResult getMoreMessage(double timeout) const; - bool hasFullMessage() const; - -private: - CMutex m_mutex; - mutable UInt32 m_size; - mutable CBufferedInputStream m_buffer; -}; - -#endif - diff --git a/lib/synergy/COutputPacketStream.cpp b/lib/synergy/COutputPacketStream.cpp deleted file mode 100644 index 7fbf1ad2..00000000 --- a/lib/synergy/COutputPacketStream.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "COutputPacketStream.h" - -// -// COuputPacketStream -// - -COutputPacketStream::COutputPacketStream(IOutputStream* stream, bool adopt) : - COutputStreamFilter(stream, adopt) -{ - // do nothing -} - -COutputPacketStream::~COutputPacketStream() -{ - // do nothing -} - -void -COutputPacketStream::close() -{ - getStream()->close(); -} - -UInt32 -COutputPacketStream::write(const void* buffer, UInt32 count) -{ - // write the length of the payload - UInt8 length[4]; - length[0] = (UInt8)((count >> 24) & 0xff); - length[1] = (UInt8)((count >> 16) & 0xff); - length[2] = (UInt8)((count >> 8) & 0xff); - length[3] = (UInt8)( count & 0xff); - UInt32 count2 = sizeof(length); - const UInt8* cbuffer = length; - while (count2 > 0) { - UInt32 n = getStream()->write(cbuffer, count2); - cbuffer += n; - count2 -= n; - } - - // write the payload - count2 = count; - cbuffer = reinterpret_cast(buffer); - while (count2 > 0) { - UInt32 n = getStream()->write(cbuffer, count2); - cbuffer += n; - count2 -= n; - } - - return count; -} - -void -COutputPacketStream::flush() -{ - getStream()->flush(); -} - diff --git a/lib/synergy/COutputPacketStream.h b/lib/synergy/COutputPacketStream.h deleted file mode 100644 index 7e25d47d..00000000 --- a/lib/synergy/COutputPacketStream.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef COUTPUTPACKETSTREAM_H -#define COUTPUTPACKETSTREAM_H - -#include "COutputStreamFilter.h" - -//! Packetizing output stream filter -/*! -Filters an output stream to create packets that include message -boundaries. Each write() is considered a single packet. -*/ -class COutputPacketStream : public COutputStreamFilter { -public: - COutputPacketStream(IOutputStream*, bool adoptStream = true); - ~COutputPacketStream(); - - // IOutputStream overrides - virtual void close(); - virtual UInt32 write(const void*, UInt32 count); - virtual void flush(); -}; - -#endif diff --git a/lib/synergy/CPacketStreamFilter.cpp b/lib/synergy/CPacketStreamFilter.cpp new file mode 100644 index 00000000..0ce5e7c3 --- /dev/null +++ b/lib/synergy/CPacketStreamFilter.cpp @@ -0,0 +1,221 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CPacketStreamFilter.h" +#include "IEventQueue.h" +#include "CLock.h" +#include "TMethodEventJob.h" + +// +// CPacketStreamFilter +// + +CPacketStreamFilter::CPacketStreamFilter(IStream* stream, bool adoptStream) : + CStreamFilter(stream, adoptStream), + m_size(0), + m_eventFilter(NULL), + m_inputShutdown(false) +{ + // install event filter + getStream()->setEventFilter(new TMethodEventJob( + this, &CPacketStreamFilter::filterEvent, NULL)); +} + +CPacketStreamFilter::~CPacketStreamFilter() +{ + delete getStream()->getEventFilter(); +} + +void +CPacketStreamFilter::close() +{ + CLock lock(&m_mutex); + m_size = 0; + m_buffer.pop(m_buffer.getSize()); + CStreamFilter::close(); +} + +UInt32 +CPacketStreamFilter::read(void* buffer, UInt32 n) +{ + if (n == 0) { + return 0; + } + + CLock lock(&m_mutex); + + // if not enough data yet then give up + if (!isReadyNoLock()) { + return 0; + } + + // read no more than what's left in the buffered packet + if (n > m_size) { + n = m_size; + } + + // read it + if (buffer != NULL) { + memcpy(buffer, m_buffer.peek(n), n); + } + m_buffer.pop(n); + m_size -= n; + + // get next packet's size if we've finished with this packet and + // there's enough data to do so. + readPacketSize(); + + if (m_inputShutdown && m_size == 0) { + sendEvent(CEvent(getInputShutdownEvent(), getEventTarget(), NULL)); + } + + return n; +} + +void +CPacketStreamFilter::write(const void* buffer, UInt32 count) +{ + // write the length of the payload + UInt8 length[4]; + length[0] = (UInt8)((count >> 24) & 0xff); + length[1] = (UInt8)((count >> 16) & 0xff); + length[2] = (UInt8)((count >> 8) & 0xff); + length[3] = (UInt8)( count & 0xff); + getStream()->write(length, sizeof(length)); + + // write the payload + getStream()->write(buffer, count); +} + +void +CPacketStreamFilter::shutdownInput() +{ + CLock lock(&m_mutex); + m_size = 0; + m_buffer.pop(m_buffer.getSize()); + CStreamFilter::shutdownInput(); +} + +void +CPacketStreamFilter::setEventFilter(IEventJob* filter) +{ + CLock lock(&m_mutex); + m_eventFilter = filter; +} + +bool +CPacketStreamFilter::isReady() const +{ + CLock lock(&m_mutex); + return isReadyNoLock(); +} + +UInt32 +CPacketStreamFilter::getSize() const +{ + CLock lock(&m_mutex); + return isReadyNoLock() ? m_size : 0; +} + +IEventJob* +CPacketStreamFilter::getEventFilter() const +{ + CLock lock(&m_mutex); + return m_eventFilter; +} + +bool +CPacketStreamFilter::isReadyNoLock() const +{ + return (m_size != 0 && m_buffer.getSize() >= m_size); +} + +void +CPacketStreamFilter::readPacketSize() +{ + // note -- m_mutex must be locked on entry + + if (m_size == 0 && m_buffer.getSize() >= 4) { + UInt8 buffer[4]; + memcpy(buffer, m_buffer.peek(sizeof(buffer)), sizeof(buffer)); + m_buffer.pop(sizeof(buffer)); + m_size = ((UInt32)buffer[0] << 24) | + ((UInt32)buffer[1] << 16) | + ((UInt32)buffer[2] << 8) | + (UInt32)buffer[3]; + } +} + +void +CPacketStreamFilter::readMore() +{ + // note -- m_mutex must be locked on entry + + // note if we have whole packet + bool wasReady = isReadyNoLock(); + + // read more data + char buffer[4096]; + UInt32 n = getStream()->read(buffer, sizeof(buffer)); + while (n > 0) { + m_buffer.write(buffer, n); + n = getStream()->read(buffer, sizeof(buffer)); + } + + // if we don't yet have the next packet size then get it, + // if possible. + readPacketSize(); + + // note if we now have a whole packet + bool isReady = isReadyNoLock(); + + // if we weren't ready before but now we are then send a + // input ready event apparently from the filtered stream. + if (wasReady != isReady) { + sendEvent(CEvent(getInputReadyEvent(), getEventTarget(), NULL)); + } +} + +void +CPacketStreamFilter::sendEvent(const CEvent& event) +{ + if (m_eventFilter != NULL) { + m_eventFilter->run(event); + } + else { + EVENTQUEUE->addEvent(event); + } +} + +void +CPacketStreamFilter::filterEvent(const CEvent& event, void*) +{ + CLock lock(&m_mutex); + + if (event.getType() == getInputReadyEvent()) { + readMore(); + return; + } + else if (event.getType() == getInputShutdownEvent()) { + // discard this if we have buffered data + m_inputShutdown = true; + if (m_size == 0) { + sendEvent(CEvent(getInputShutdownEvent(), getEventTarget(), NULL)); + } + return; + } + + // pass event + sendEvent(event); +} diff --git a/lib/synergy/CPacketStreamFilter.h b/lib/synergy/CPacketStreamFilter.h new file mode 100644 index 00000000..3156f40d --- /dev/null +++ b/lib/synergy/CPacketStreamFilter.h @@ -0,0 +1,57 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CPACKETSTREAMFILTER_H +#define CPACKETSTREAMFILTER_H + +#include "CStreamFilter.h" +#include "CStreamBuffer.h" +#include "CMutex.h" + +//! Packetizing stream filter +/*! +Filters a stream to read and write packets. +*/ +class CPacketStreamFilter : public CStreamFilter { +public: + CPacketStreamFilter(IStream* stream, bool adoptStream = true); + ~CPacketStreamFilter(); + + // IStream overrides + virtual void close(); + virtual UInt32 read(void* buffer, UInt32 n); + virtual void write(const void* buffer, UInt32 n); + virtual void shutdownInput(); + virtual void setEventFilter(IEventJob* filter); + virtual bool isReady() const; + virtual UInt32 getSize() const; + virtual IEventJob* getEventFilter() const; + +private: + bool isReadyNoLock() const; + void readPacketSize(); + + void readMore(); + void sendEvent(const CEvent&); + void filterEvent(const CEvent&, void*); + +private: + CMutex m_mutex; + UInt32 m_size; + CStreamBuffer m_buffer; + IEventJob* m_eventFilter; + bool m_inputShutdown; +}; + +#endif diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp index 7ef9de6a..d85f840a 100644 --- a/lib/synergy/CProtocolUtil.cpp +++ b/lib/synergy/CProtocolUtil.cpp @@ -13,8 +13,7 @@ */ #include "CProtocolUtil.h" -#include "IInputStream.h" -#include "IOutputStream.h" +#include "IStream.h" #include "CLog.h" #include "stdvector.h" #include @@ -25,51 +24,77 @@ // void -CProtocolUtil::writef(IOutputStream* stream, const char* fmt, ...) +CProtocolUtil::writef(IStream* stream, const char* fmt, ...) { assert(stream != NULL); assert(fmt != NULL); LOG((CLOG_DEBUG2 "writef(%s)", fmt)); va_list args; - - // determine total size to write va_start(args, fmt); - UInt32 count = getLength(fmt, args); + UInt32 size = getLength(fmt, args); va_end(args); - - // done if nothing to write - if (count == 0) { - return; - } - - // fill buffer - UInt8* buffer = new UInt8[count]; va_start(args, fmt); - writef(buffer, fmt, args); + vwritef(stream, fmt, size, args); va_end(args); - - // write buffer - UInt8* scan = buffer; - while (count > 0) { - const UInt32 n = stream->write(scan, count); - LOG((CLOG_DEBUG2 "wrote %d of %d bytes", n, count)); - count -= n; - scan += n; - } - - delete[] buffer; } -void -CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) +bool +CProtocolUtil::readf(IStream* stream, const char* fmt, ...) { assert(stream != NULL); assert(fmt != NULL); LOG((CLOG_DEBUG2 "readf(%s)", fmt)); + bool result; va_list args; va_start(args, fmt); + try { + vreadf(stream, fmt, args); + result = true; + } + catch (XIO&) { + result = false; + } + va_end(args); + return result; +} + +void +CProtocolUtil::vwritef(IStream* stream, + const char* fmt, UInt32 size, va_list args) +{ + assert(stream != NULL); + assert(fmt != NULL); + + // done if nothing to write + if (size == 0) { + return; + } + + // fill buffer + // FIXME -- can we use alloca? + UInt8* buffer = new UInt8[size]; + writef(buffer, fmt, args); + + try { + // write buffer + stream->write(buffer, size); + LOG((CLOG_DEBUG2 "wrote %d bytes", size)); + + delete[] buffer; + } + catch (XBase&) { + delete[] buffer; + throw; + } +} + +bool +CProtocolUtil::vreadf(IStream* stream, const char* fmt, va_list args) +{ + assert(stream != NULL); + assert(fmt != NULL); // begin scanning while (*fmt) { @@ -242,8 +267,6 @@ CProtocolUtil::readf(IInputStream* stream, const char* fmt, ...) ++fmt; } } - - va_end(args); } UInt32 @@ -485,7 +508,7 @@ CProtocolUtil::eatLength(const char** pfmt) } void -CProtocolUtil::read(IInputStream* stream, void* vbuffer, UInt32 count) +CProtocolUtil::read(IStream* stream, void* vbuffer, UInt32 count) { assert(stream != NULL); assert(vbuffer != NULL); @@ -493,7 +516,7 @@ CProtocolUtil::read(IInputStream* stream, void* vbuffer, UInt32 count) UInt8* buffer = reinterpret_cast(vbuffer); while (count > 0) { // read more - UInt32 n = stream->read(buffer, count, -1.0); + UInt32 n = stream->read(buffer, count); // bail if stream has hungup if (n == 0) { diff --git a/lib/synergy/CProtocolUtil.h b/lib/synergy/CProtocolUtil.h index 1e0db39f..2ea1609f 100644 --- a/lib/synergy/CProtocolUtil.h +++ b/lib/synergy/CProtocolUtil.h @@ -19,8 +19,7 @@ #include "XIO.h" #include -class IInputStream; -class IOutputStream; +class IStream; //! Synergy protocol utilities /*! @@ -47,13 +46,14 @@ public: - \%s -- converts CString* to stream of bytes - \%S -- converts integer N and const UInt8* to stream of N bytes */ - static void writef(IOutputStream*, + static void writef(IStream*, const char* fmt, ...); //! Read formatted data /*! Read formatted binary data from a buffer. This performs the - reverse operation of writef(). + reverse operation of writef(). Returns true if the entire + format was successfully parsed, false otherwise. Format specifiers are: - \%\% -- read (and discard) a literal `\%' @@ -65,14 +65,19 @@ public: - \%4I -- reads NBO 4 byte integers; arg is std::vector* - \%s -- reads bytes; argument must be a CString*, \b not a char* */ - static void readf(IInputStream*, + static bool readf(IStream*, const char* fmt, ...); private: + static void vwritef(IStream*, + const char* fmt, UInt32 size, va_list); + static bool vreadf(IStream*, + const char* fmt, va_list); + static UInt32 getLength(const char* fmt, va_list); static void writef(void*, const char* fmt, va_list); static UInt32 eatLength(const char** fmt); - static void read(IInputStream*, void*, UInt32); + static void read(IStream*, void*, UInt32); }; //! Mismatched read exception diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 9923fa6e..1203ae2f 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -26,15 +26,13 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libsynergy.a libsynergy_a_SOURCES = \ CClipboard.cpp \ - CInputPacketStream.cpp \ - COutputPacketStream.cpp \ + CPacketStreamFilter.cpp \ CProtocolUtil.cpp \ CScreen.cpp \ XScreen.cpp \ XSynergy.cpp \ CClipboard.h \ - CInputPacketStream.h \ - COutputPacketStream.h \ + CPacketStreamFilter.h \ CProtocolUtil.h \ CScreen.h \ ClipboardTypes.h \ From 3bcdf139a797cc735f3073d2a75564282a0afc2e Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 8 Feb 2004 16:51:45 +0000 Subject: [PATCH 574/807] No longer sending incorrect disconnect events in read() and removed redundant sending of disconnect event in close(). --- lib/net/CTCPSocket.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index cdfed02e..fd592203 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -129,8 +129,9 @@ CTCPSocket::read(void* buffer, UInt32 n) m_inputBuffer.pop(n); // if no more data and we cannot read or write then send disconnected - if (n > 0 && !m_readable && !m_writable) { + if (n > 0 && m_inputBuffer.getSize() == 0 && !m_readable && !m_writable) { sendSocketEvent(getDisconnectedEvent()); + m_connected = false; } return n; @@ -459,6 +460,7 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, sendStreamEvent(getOutputShutdownEvent()); if (!m_readable && m_inputBuffer.getSize() == 0) { sendSocketEvent(getDisconnectedEvent()); + m_connected = false; } needNewJob = true; } @@ -502,6 +504,7 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, sendStreamEvent(getInputShutdownEvent()); if (!m_writable && m_inputBuffer.getSize() == 0) { sendSocketEvent(getDisconnectedEvent()); + m_connected = false; } m_readable = false; needNewJob = true; From c44c18bfdcef64a789bd446f2e2cec859bd136f0 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 8 Feb 2004 17:07:11 +0000 Subject: [PATCH 575/807] Refactored event queue. The event queue is now separated from the buffer that holds the events and generates system events. This allows us to switch in/out a platform specific event handler as necessary without losing our timers and handlers. --- lib/base/CEventQueue.cpp | 94 ++++++++++++++++++++++------------ lib/base/CEventQueue.h | 72 ++------------------------ lib/base/CSimpleEventQueue.cpp | 88 ------------------------------- lib/base/CSimpleEventQueue.h | 60 ---------------------- lib/base/IEventQueue.h | 10 +++- lib/base/Makefile.am | 79 ++++++++++++++-------------- 6 files changed, 114 insertions(+), 289 deletions(-) delete mode 100644 lib/base/CSimpleEventQueue.cpp delete mode 100644 lib/base/CSimpleEventQueue.h diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp index 9c6d6ca4..8528b0ac 100644 --- a/lib/base/CEventQueue.cpp +++ b/lib/base/CEventQueue.cpp @@ -13,6 +13,7 @@ */ #include "CEventQueue.h" +#include "CSimpleEventQueueBuffer.h" #include "IEventJob.h" #include "CArch.h" @@ -34,20 +35,42 @@ CEventQueue::CEventQueue() setInstance(this); m_mutex = ARCH->newMutex(); ARCH->setInterruptHandler(&interrupt, NULL); + m_buffer = new CSimpleEventQueueBuffer; } CEventQueue::~CEventQueue() { + delete m_buffer; ARCH->setInterruptHandler(NULL, NULL); ARCH->closeMutex(m_mutex); setInstance(NULL); } +void +CEventQueue::adoptBuffer(IEventQueueBuffer* buffer) +{ + CArchMutexLock lock(m_mutex); + + // discard old buffer and old events + delete m_buffer; + for (CEventTable::iterator i = m_events.begin(); i != m_events.end(); ++i) { + CEvent::deleteData(i->second); + } + m_events.clear(); + m_oldEventIDs.clear(); + + // use new buffer + m_buffer = buffer; + if (m_buffer == NULL) { + m_buffer = new CSimpleEventQueueBuffer; + } +} + bool CEventQueue::getEvent(CEvent& event, double timeout) { // if no events are waiting then handle timers and then wait - if (doIsEmpty()) { + if (m_buffer->isEmpty()) { // handle timers first if (hasTimerExpired(event)) { return true; @@ -62,15 +85,30 @@ CEventQueue::getEvent(CEvent& event, double timeout) } // wait for an event - waitForEvent(timeout); + m_buffer->waitForEvent(timeout); } // if no events are pending then do the timers - if (doIsEmpty()) { + if (m_buffer->isEmpty()) { return hasTimerExpired(event); } - return doGetEvent(event); + UInt32 dataID; + IEventQueueBuffer::Type type = m_buffer->getEvent(event, dataID); + switch (type) { + case IEventQueueBuffer::kNone: + return false; + + case IEventQueueBuffer::kSystem: + return true; + + case IEventQueueBuffer::kUser: + { + CArchMutexLock lock(m_mutex); + event = removeEvent(dataID); + return true; + } + } } bool @@ -99,11 +137,13 @@ CEventQueue::addEvent(const CEvent& event) break; } + CArchMutexLock lock(m_mutex); + // store the event's data locally UInt32 eventID = saveEvent(event); // add it - if (!doAddEvent(eventID)) { + if (!m_buffer->addEvent(eventID)) { // failed to send event removeEvent(eventID); CEvent::deleteData(event); @@ -115,7 +155,7 @@ CEventQueue::newTimer(double duration, void* target) { assert(duration > 0.0); - CEventQueueTimer* timer = doNewTimer(duration, false); + CEventQueueTimer* timer = m_buffer->newTimer(duration, false); CArchMutexLock lock(m_mutex); m_timers.insert(timer); m_timerQueue.push(CTimer(timer, duration, target, false)); @@ -127,7 +167,7 @@ CEventQueue::newOneShotTimer(double duration, void* target) { assert(duration > 0.0); - CEventQueueTimer* timer = doNewTimer(duration, true); + CEventQueueTimer* timer = m_buffer->newTimer(duration, true); CArchMutexLock lock(m_mutex); m_timers.insert(timer); m_timerQueue.push(CTimer(timer, duration, target, true)); @@ -137,27 +177,24 @@ CEventQueue::newOneShotTimer(double duration, void* target) void CEventQueue::deleteTimer(CEventQueueTimer* timer) { - { - CArchMutexLock lock(m_mutex); - for (CTimerQueue::iterator index = m_timerQueue.begin(); - index != m_timerQueue.end(); ++index) { - if (index->getTimer() == timer) { - m_timerQueue.erase(index); - break; - } - } - CTimers::iterator index = m_timers.find(timer); - if (index != m_timers.end()) { - m_timers.erase(index); + CArchMutexLock lock(m_mutex); + for (CTimerQueue::iterator index = m_timerQueue.begin(); + index != m_timerQueue.end(); ++index) { + if (index->getTimer() == timer) { + m_timerQueue.erase(index); + break; } } - doDeleteTimer(timer); + CTimers::iterator index = m_timers.find(timer); + if (index != m_timers.end()) { + m_timers.erase(index); + } + m_buffer->deleteTimer(timer); } void CEventQueue::adoptHandler(void* target, IEventJob* handler) { - CArchMutexLock lock(m_mutex); doAdoptHandler(CEvent::kUnknown, target, handler); } @@ -165,14 +202,12 @@ void CEventQueue::adoptHandler(CEvent::Type type, void* target, IEventJob* handler) { assert(type != CEvent::kUnknown); - CArchMutexLock lock(m_mutex); doAdoptHandler(type, target, handler); } IEventJob* CEventQueue::orphanHandler(void* target) { - CArchMutexLock lock(m_mutex); return doOrphanHandler(CEvent::kUnknown, target); } @@ -180,7 +215,6 @@ IEventJob* CEventQueue::orphanHandler(CEvent::Type type, void* target) { assert(type != CEvent::kUnknown); - CArchMutexLock lock(m_mutex); return doOrphanHandler(type, target); } @@ -199,6 +233,7 @@ CEventQueue::removeHandler(CEvent::Type type, void* target) void CEventQueue::doAdoptHandler(CEvent::Type type, void* target, IEventJob* handler) { + CArchMutexLock lock(m_mutex); IEventJob*& job = m_handlers[CTypeTarget(type, target)]; delete job; job = handler; @@ -207,6 +242,7 @@ CEventQueue::doAdoptHandler(CEvent::Type type, void* target, IEventJob* handler) IEventJob* CEventQueue::doOrphanHandler(CEvent::Type type, void* target) { + CArchMutexLock lock(m_mutex); CHandlerTable::iterator index = m_handlers.find(CTypeTarget(type, target)); if (index != m_handlers.end()) { IEventJob* handler = index->second; @@ -221,7 +257,7 @@ CEventQueue::doOrphanHandler(CEvent::Type type, void* target) bool CEventQueue::isEmpty() const { - return (doIsEmpty() && getNextTimerTimeout() != 0.0); + return (m_buffer->isEmpty() && getNextTimerTimeout() != 0.0); } IEventJob* @@ -243,8 +279,6 @@ CEventQueue::getHandler(CEvent::Type type, void* target) const UInt32 CEventQueue::saveEvent(const CEvent& event) { - CArchMutexLock lock(m_mutex); - // choose id UInt32 id; if (!m_oldEventIDs.empty()) { @@ -265,8 +299,6 @@ CEventQueue::saveEvent(const CEvent& event) CEvent CEventQueue::removeEvent(UInt32 eventID) { - CArchMutexLock lock(m_mutex); - // look up id CEventTable::iterator index = m_events.find(eventID); if (index == m_events.end()) { @@ -286,8 +318,6 @@ CEventQueue::removeEvent(UInt32 eventID) bool CEventQueue::hasTimerExpired(CEvent& event) { - CArchMutexLock lock(m_mutex); - // return true if there's a timer in the timer priority queue that // has expired. if returning true then fill in event appropriately // and reset and reinsert the timer. @@ -330,8 +360,6 @@ CEventQueue::hasTimerExpired(CEvent& event) double CEventQueue::getNextTimerTimeout() const { - CArchMutexLock lock(m_mutex); - // return -1 if no timers, 0 if the top timer has expired, otherwise // the time until the top timer in the timer priority queue will // expire. diff --git a/lib/base/CEventQueue.h b/lib/base/CEventQueue.h index 5443c394..514612bb 100644 --- a/lib/base/CEventQueue.h +++ b/lib/base/CEventQueue.h @@ -34,6 +34,7 @@ public: virtual ~CEventQueue(); // IEventQueue overrides + virtual void adoptBuffer(IEventQueueBuffer*); virtual bool getEvent(CEvent& event, double timeout = -1.0); virtual bool dispatchEvent(const CEvent& event); virtual void addEvent(const CEvent& event); @@ -52,80 +53,13 @@ public: virtual bool isEmpty() const; virtual IEventJob* getHandler(CEvent::Type type, void* target) const; -protected: - //! @name manipulators - //@{ - - //! Get the data for a given id - /*! - Takes a saved event id, \p eventID, and returns a \c CEvent. The - event id becomes invalid after this call. The \p eventID must have - been passed to a successful call to \c doAddEvent() and not removed - since. - */ - CEvent removeEvent(UInt32 eventID); - - //! Block waiting for an event - /*! - Wait for an event in the system event queue for up to \p timeout - seconds. - */ - virtual void waitForEvent(double timeout) = 0; - - //! Get the next event - /*! - Remove the next system event (one should be pending) and convert it - to a \c CEvent. The event type should be either \c CEvent::kSystem - if the event was not added by \c doAddEvent() or a type returned by - \c CEvent::registerType() if it was (and not \c CEvent::kTimer). A - non-system event will normally be retrieved by \c removeEvent(), but - the implementation must be able to tell the difference between a - system event and one added by \c doAddEvent(). - */ - virtual bool doGetEvent(CEvent& event) = 0; - - //! Post an event - /*! - Add the given event to the end of the system queue. This is a user - event and \c doGetEvent() must be able to identify it as such. - This method must cause \c waitForEvent() to return at some future - time if it's blocked waiting on an event. - */ - virtual bool doAddEvent(UInt32 dataID) = 0; - - //@} - //! @name accessors - //@{ - - //! Check if system queue is empty - /*! - Return true iff the system queue is empty. - */ - virtual bool doIsEmpty() const = 0; - - //! Create a timer object - /*! - Create and return a timer object. The object is opaque and is - used only by the subclass but it must be a valid object (i.e. - not NULL). - */ - virtual CEventQueueTimer* - doNewTimer(double duration, bool oneShot) const = 0; - - //! Destroy a timer object - /*! - Destroy a timer object previously returned by \c doNewTimer(). - */ - virtual void doDeleteTimer(CEventQueueTimer*) const = 0; - - //@} - private: void doAdoptHandler(CEvent::Type type, void* target, IEventJob* handler); IEventJob* doOrphanHandler(CEvent::Type type, void* target); UInt32 saveEvent(const CEvent& event); + CEvent removeEvent(UInt32 eventID); bool hasTimerExpired(CEvent& event); double getNextTimerTimeout() const; @@ -175,6 +109,8 @@ private: CArchMutex m_mutex; + IEventQueueBuffer* m_buffer; + CEventTable m_events; CEventIDList m_oldEventIDs; diff --git a/lib/base/CSimpleEventQueue.cpp b/lib/base/CSimpleEventQueue.cpp deleted file mode 100644 index 9fc7e616..00000000 --- a/lib/base/CSimpleEventQueue.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2004 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CSimpleEventQueue.h" -#include "CArch.h" - -class CEventQueueTimer { }; - -// -// CSimpleEventQueue -// - -CSimpleEventQueue::CSimpleEventQueue() -{ - m_queueMutex = ARCH->newMutex(); - m_queueReadyCond = ARCH->newCondVar(); - m_queueReady = false; -} - -CSimpleEventQueue::~CSimpleEventQueue() -{ - ARCH->closeCondVar(m_queueReadyCond); - ARCH->closeMutex(m_queueMutex); -} - -void -CSimpleEventQueue::waitForEvent(double timeout) -{ - CArchMutexLock lock(m_queueMutex); - while (!m_queueReady) { - ARCH->waitCondVar(m_queueReadyCond, m_queueMutex, -1.0); - } -} - -bool -CSimpleEventQueue::doGetEvent(CEvent& event) -{ - CArchMutexLock lock(m_queueMutex); - if (!m_queueReady) { - return false; - } - event = removeEvent(m_queue.back()); - m_queue.pop_back(); - m_queueReady = !m_queue.empty(); - return true; -} - -bool -CSimpleEventQueue::doAddEvent(UInt32 dataID) -{ - CArchMutexLock lock(m_queueMutex); - m_queue.push_front(dataID); - if (!m_queueReady) { - m_queueReady = true; - ARCH->broadcastCondVar(m_queueReadyCond); - } - return true; -} - -bool -CSimpleEventQueue::doIsEmpty() const -{ - CArchMutexLock lock(m_queueMutex); - return !m_queueReady; -} - -CEventQueueTimer* -CSimpleEventQueue::doNewTimer(double, bool) const -{ - return new CEventQueueTimer; -} - -void -CSimpleEventQueue::doDeleteTimer(CEventQueueTimer* timer) const -{ - delete timer; -} diff --git a/lib/base/CSimpleEventQueue.h b/lib/base/CSimpleEventQueue.h deleted file mode 100644 index 35ad4ecc..00000000 --- a/lib/base/CSimpleEventQueue.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2004 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CSIMPLEEVENTQUEUE_H -#define CSIMPLEEVENTQUEUE_H - -#include "CEventQueue.h" -#include "IArchMultithread.h" -#include "stddeque.h" - -//! Event queue for added events only -/*! -An event queue that provides no system events, just events added by -addEvent(). -*/ -class CSimpleEventQueue : public CEventQueue { -public: - CSimpleEventQueue(); - virtual ~CSimpleEventQueue(); - - //! @name manipulators - //@{ - - //@} - //! @name accessors - //@{ - - //@} - -protected: - // CEventQueue overrides - virtual void waitForEvent(double timeout); - virtual bool doGetEvent(CEvent& event); - virtual bool doAddEvent(UInt32 dataID); - virtual bool doIsEmpty() const; - virtual CEventQueueTimer* - doNewTimer(double duration, bool oneShot) const; - virtual void doDeleteTimer(CEventQueueTimer*) const; - -private: - typedef std::deque CEventDeque; - - CArchMutex m_queueMutex; - CArchCond m_queueReadyCond; - bool m_queueReady; - CEventDeque m_queue; -}; - -#endif diff --git a/lib/base/IEventQueue.h b/lib/base/IEventQueue.h index 0d144ebf..6e649456 100644 --- a/lib/base/IEventQueue.h +++ b/lib/base/IEventQueue.h @@ -21,9 +21,10 @@ #define EVENTQUEUE IEventQueue::getInstance() class IEventJob; +class IEventQueueBuffer; // Opaque type for timer info. This is defined by subclasses of -// IEventQueue. +// IEventQueueBuffer. class CEventQueueTimer; //! Event queue interface @@ -44,6 +45,13 @@ public: //! @name manipulators //@{ + //! Set the buffer + /*! + Replace the current event queue buffer. Any queued events are + discarded. The queue takes ownership of the buffer. + */ + virtual void adoptBuffer(IEventQueueBuffer*) = 0; + //! Remove event from queue /*! Returns the next event on the queue into \p event. If no event is diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index f146cb11..9eb2a526 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -15,49 +15,50 @@ NULL = DEPTH = ../.. VDEPTH = ./$(VPATH)/$(DEPTH) -EXTRA_DIST = \ - base.dsp \ +EXTRA_DIST = \ + base.dsp \ $(NULL) -MAINTAINERCLEANFILES = \ - Makefile.in \ +MAINTAINERCLEANFILES = \ + Makefile.in \ $(NULL) noinst_LIBRARIES = libbase.a -libbase_a_SOURCES = \ - CEvent.cpp \ - CEventQueue.cpp \ - CFunctionEventJob.cpp \ - CFunctionJob.cpp \ - CJobList.cpp \ - CLog.cpp \ - CSimpleEventQueue.cpp \ - CStopwatch.cpp \ - CStringUtil.cpp \ - CUnicode.cpp \ - IEventQueue.cpp \ - LogOutputters.cpp \ - XBase.cpp \ - CEvent.h \ - CEventQueue.h \ - CFunctionJob.h \ - CJobList.h \ - CLog.h \ - CPriorityQueue.h \ - CSimpleEventQueue.h \ - CStopwatch.h \ - CString.h \ - CStringUtil.h \ - CUnicode.h \ - IEventQueue.h \ - IJob.h \ - ILogOutputter.h \ - LogOutputters.h \ - TMethodEventJob.h \ - TMethodJob.h \ - XBase.h \ +libbase_a_SOURCES = \ + CEvent.cpp \ + CEventQueue.cpp \ + CFunctionEventJob.cpp \ + CFunctionJob.cpp \ + CJobList.cpp \ + CLog.cpp \ + CSimpleEventQueueBuffer.cpp \ + CStopwatch.cpp \ + CStringUtil.cpp \ + CUnicode.cpp \ + IEventQueue.cpp \ + LogOutputters.cpp \ + XBase.cpp \ + CEvent.h \ + CEventQueue.h \ + CFunctionJob.h \ + CJobList.h \ + CLog.h \ + CPriorityQueue.h \ + CSimpleEventQueueBuffer.h \ + CStopwatch.h \ + CString.h \ + CStringUtil.h \ + CUnicode.h \ + IEventQueue.h \ + IEventQueueBuffer.h \ + IJob.h \ + ILogOutputter.h \ + LogOutputters.h \ + TMethodEventJob.h \ + TMethodJob.h \ + XBase.h \ $(NULL) -INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ +INCLUDES = \ + -I$(VDEPTH)/lib/common \ + -I$(VDEPTH)/lib/arch \ $(NULL) From 1861f21fb522d791f3e38059d86fb67962742af9 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Feb 2004 14:04:36 +0000 Subject: [PATCH 576/807] Checkpoint. synergys now works. Still need to do lib/client and synergyc. --- cmd/synergys/CServerTaskBarReceiver.cpp | 140 +- cmd/synergys/CServerTaskBarReceiver.h | 76 +- .../CXWindowsServerTaskBarReceiver.cpp | 6 - cmd/synergys/CXWindowsServerTaskBarReceiver.h | 4 - cmd/synergys/synergys.cpp | 449 ++- lib/arch/CArch.cpp | 6 + lib/arch/CArch.h | 1 + lib/arch/CArchMultithreadPosix.cpp | 1 + lib/arch/CArchNetworkBSD.cpp | 8 + lib/arch/CArchNetworkBSD.h | 1 + lib/arch/IArchNetwork.h | 3 + lib/arch/vsnprintf.cpp | 1 - lib/base/CEvent.cpp | 22 +- lib/base/CEvent.h | 49 +- lib/base/CEventQueue.cpp | 115 +- lib/base/CEventQueue.h | 21 +- lib/base/CSimpleEventQueueBuffer.cpp | 97 + lib/base/CSimpleEventQueueBuffer.h | 49 + lib/base/CStringUtil.cpp | 4 +- lib/base/IEventQueue.h | 34 +- lib/base/IEventQueueBuffer.h | 94 + lib/io/IStream.cpp | 15 +- lib/net/CNetworkAddress.cpp | 12 + lib/net/CNetworkAddress.h | 12 + lib/net/CSocketMultiplexer.cpp | 5 +- lib/net/CTCPListenSocket.cpp | 5 +- lib/net/CTCPSocket.cpp | 20 +- lib/net/IDataSocket.cpp | 6 +- lib/net/IListenSocket.cpp | 3 +- lib/net/ISocket.cpp | 3 +- ...ueue.cpp => CXWindowsEventQueueBuffer.cpp} | 86 +- ...entQueue.h => CXWindowsEventQueueBuffer.h} | 40 +- lib/platform/CXWindowsScreen.cpp | 807 ++--- lib/platform/CXWindowsScreen.h | 112 +- lib/platform/CXWindowsScreenSaver.cpp | 140 +- lib/platform/CXWindowsScreenSaver.h | 58 +- lib/platform/Makefile.am | 4 +- lib/server/CClientListener.cpp | 189 ++ lib/server/CClientListener.h | 76 + lib/server/CClientProxy.cpp | 39 +- lib/server/CClientProxy.h | 60 +- lib/server/CClientProxy1_0.cpp | 238 +- lib/server/CClientProxy1_0.h | 35 +- lib/server/CClientProxy1_1.cpp | 5 +- lib/server/CClientProxy1_1.h | 3 +- lib/server/CClientProxyUnknown.cpp | 277 ++ lib/server/CClientProxyUnknown.h | 83 + lib/server/CConfig.cpp | 2 - lib/server/CPrimaryClient.cpp | 148 +- lib/server/CPrimaryClient.h | 67 +- lib/server/CServer.cpp | 2755 +++++++---------- lib/server/CServer.h | 376 +-- lib/server/Makefile.am | 6 +- lib/synergy/CProtocolUtil.cpp | 1 - lib/synergy/CScreen.cpp | 156 +- lib/synergy/CScreen.h | 87 +- lib/synergy/IClient.h | 68 +- lib/synergy/IPlatformScreen.cpp | 149 + lib/synergy/IPlatformScreen.h | 159 +- lib/synergy/IPrimaryScreen.h | 18 +- lib/synergy/IPrimaryScreenReceiver.h | 73 - lib/synergy/IScreen.cpp | 52 + lib/synergy/IScreen.h | 106 + lib/synergy/IScreenFactory.h | 41 - lib/synergy/IScreenReceiver.h | 65 - lib/synergy/IScreenSaver.h | 1 + lib/synergy/IServer.h | 75 - lib/synergy/Makefile.am | 7 +- 68 files changed, 3812 insertions(+), 4114 deletions(-) create mode 100644 lib/base/CSimpleEventQueueBuffer.cpp create mode 100644 lib/base/CSimpleEventQueueBuffer.h create mode 100644 lib/base/IEventQueueBuffer.h rename lib/platform/{CXWindowsEventQueue.cpp => CXWindowsEventQueueBuffer.cpp} (62%) rename lib/platform/{CXWindowsEventQueue.h => CXWindowsEventQueueBuffer.h} (55%) create mode 100644 lib/server/CClientListener.cpp create mode 100644 lib/server/CClientListener.h create mode 100644 lib/server/CClientProxyUnknown.cpp create mode 100644 lib/server/CClientProxyUnknown.h create mode 100644 lib/synergy/IPlatformScreen.cpp delete mode 100644 lib/synergy/IPrimaryScreenReceiver.h create mode 100644 lib/synergy/IScreen.cpp create mode 100644 lib/synergy/IScreen.h delete mode 100644 lib/synergy/IScreenFactory.h delete mode 100644 lib/synergy/IScreenReceiver.h delete mode 100644 lib/synergy/IServer.h diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp index 8349a13c..93b182f4 100644 --- a/cmd/synergys/CServerTaskBarReceiver.cpp +++ b/cmd/synergys/CServerTaskBarReceiver.cpp @@ -14,8 +14,8 @@ #include "CServerTaskBarReceiver.h" #include "CServer.h" +#include "CEventQueue.h" #include "CLock.h" -#include "TMethodJob.h" #include "CArch.h" // @@ -23,71 +23,80 @@ // CServerTaskBarReceiver::CServerTaskBarReceiver() : - m_quit(NULL), - m_state(kNotRunning), - m_server(NULL) + m_state(kNotRunning) { - // create a job for getting notification when the server's - // status changes. - m_job = new TMethodJob(this, - &CServerTaskBarReceiver::statusChanged, NULL); + // do nothing } CServerTaskBarReceiver::~CServerTaskBarReceiver() { - if (m_server != NULL) { - m_server->removeStatusJob(m_job); - } - delete m_job; - delete m_quit; + // do nothing } +#include "CLog.h" void -CServerTaskBarReceiver::setServer(CServer* server) +CServerTaskBarReceiver::updateStatus(CServer* server, const CString& errorMsg) { { + // update our status CLock lock(&m_mutex); - if (m_server != server) { - if (m_server != NULL) { - m_server->removeStatusJob(m_job); + m_errorMessage = errorMsg; + if (server == NULL) { + if (m_errorMessage.empty()) { + m_state = kNotRunning; } - m_server = server; - if (m_server != NULL) { - m_server->addStatusJob(m_job); + else { + m_state = kNotWorking; + } + } + else { + m_clients.clear(); + server->getClients(m_clients); + if (m_clients.size() <= 1) { + m_state = kNotConnected; + } + else { + m_state = kConnected; } } - } - ARCH->updateReceiver(this); -} -void -CServerTaskBarReceiver::setState(EState state) -{ - { - CLock lock(&m_mutex); - m_state = state; + // let subclasses have a go + onStatusChanged(server); +LOG((CLOG_INFO "### status: %s", getToolTip().c_str())); } - ARCH->updateReceiver(this); -} -void -CServerTaskBarReceiver::setQuitJob(IJob* job) -{ - CLock lock(&m_mutex); - delete m_quit; - m_quit = job; + // tell task bar + ARCH->updateReceiver(this); } CServerTaskBarReceiver::EState -CServerTaskBarReceiver::getState() const +CServerTaskBarReceiver::getStatus() const { return m_state; } -CServer* -CServerTaskBarReceiver::getServer() const +const CString& +CServerTaskBarReceiver::getErrorMessage() const { - return m_server; + return m_errorMessage; +} + +const CServerTaskBarReceiver::CClients& +CServerTaskBarReceiver::getClients() const +{ + return m_clients; +} + +void +CServerTaskBarReceiver::quit() +{ + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + +void +CServerTaskBarReceiver::onStatusChanged(CServer*) +{ + // do nothing } void @@ -110,7 +119,7 @@ CServerTaskBarReceiver::getToolTip() const return "Synergy: Not running"; case kNotWorking: - return CString("Synergy: ") + m_errorMessage; + return std::string("Synergy: ") + m_errorMessage; case kNotConnected: return "Synergy: Waiting for clients"; @@ -122,50 +131,3 @@ CServerTaskBarReceiver::getToolTip() const return ""; } } - -void -CServerTaskBarReceiver::quit() -{ - if (m_quit != NULL) { - m_quit->run(); - } -} - -void -CServerTaskBarReceiver::onStatusChanged() -{ - // do nothing -} - -void -CServerTaskBarReceiver::statusChanged(void*) -{ - // update our status - switch (m_server->getStatus(&m_errorMessage)) { - case CServer::kNotRunning: - setState(kNotRunning); - break; - - case CServer::kRunning: - if (m_server->getNumClients() > 1) - setState(kConnected); - else - setState(kNotConnected); - break; - - case CServer::kServerNameUnknown: - m_errorMessage = "Server name is not in configuration"; - setState(kNotWorking); - break; - - case CServer::kError: - setState(kNotWorking); - break; - - default: - break; - } - - // let subclasses have a go - onStatusChanged(); -} diff --git a/cmd/synergys/CServerTaskBarReceiver.h b/cmd/synergys/CServerTaskBarReceiver.h index 0591a097..372d73f2 100644 --- a/cmd/synergys/CServerTaskBarReceiver.h +++ b/cmd/synergys/CServerTaskBarReceiver.h @@ -18,61 +18,24 @@ #include "CMutex.h" #include "CString.h" #include "IArchTaskBarReceiver.h" +#include "stdvector.h" class CServer; -class IJob; //! Implementation of IArchTaskBarReceiver for the synergy server class CServerTaskBarReceiver : public IArchTaskBarReceiver { public: - enum EState { - kNotRunning, - kNotWorking, - kNotConnected, - kConnected, - kMaxState - }; - CServerTaskBarReceiver(); virtual ~CServerTaskBarReceiver(); //! @name manipulators //@{ - //! Set server + //! Update status /*! - Sets the server. The receiver will query state from this server. + Determine the status and query required information from the server. */ - void setServer(CServer*); - - //! Set state - /*! - Sets the current server state. - */ - void setState(EState); - - //! Set the quit job that causes the server to quit - /*! - Set the job that causes the server to quit. - */ - void setQuitJob(IJob* adopted); - - //@} - //! @name accessors - //@{ - - //! Get state - /*! - Returns the current server state. The receiver is not locked - by this call; the caller must do the locking. - */ - EState getState() const; - - //! Get server - /*! - Returns the server set by \c setServer(). - */ - CServer* getServer() const; + void updateStatus(CServer*, const CString& errorMsg); //@} @@ -86,6 +49,28 @@ public: virtual std::string getToolTip() const; protected: + typedef std::vector CClients; + enum EState { + kNotRunning, + kNotWorking, + kNotConnected, + kConnected, + kMaxState + }; + + //! Get status + EState getStatus() const; + + //! Get error message + const CString& getErrorMessage() const; + + //! Get connected clients + const CClients& getClients() const; + + //! Quit app + /*! + Causes the application to quit gracefully + */ void quit(); //! Status change notification @@ -93,18 +78,13 @@ protected: Called when status changes. The default implementation does nothing. */ - virtual void onStatusChanged(); - -private: - void statusChanged(void*); + virtual void onStatusChanged(CServer* server); private: CMutex m_mutex; - IJob* m_quit; EState m_state; - CServer* m_server; - IJob* m_job; CString m_errorMessage; + CClients m_clients; }; #endif diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp index 20118732..b3e683a2 100644 --- a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp @@ -53,9 +53,3 @@ CXWindowsServerTaskBarReceiver::getIcon() const { return NULL; } - -void -CXWindowsServerTaskBarReceiver::onStatusChanged() -{ - // do nothing -} diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.h b/cmd/synergys/CXWindowsServerTaskBarReceiver.h index a05a4307..ad114145 100644 --- a/cmd/synergys/CXWindowsServerTaskBarReceiver.h +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.h @@ -28,10 +28,6 @@ public: virtual void runMenu(int x, int y); virtual void primaryAction(); virtual const Icon getIcon() const; - -protected: - // CServerTaskBarReceiver overrides - virtual void onStatusChanged(); }; #endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index cfa66e8a..ad5ecde7 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -12,22 +12,25 @@ * GNU General Public License for more details. */ -#include "CServer.h" +#include "CClientListener.h" +#include "CClientProxy.h" #include "CConfig.h" -#include "IScreenFactory.h" +#include "CPrimaryClient.h" +#include "CServer.h" +#include "CScreen.h" #include "ProtocolTypes.h" #include "Version.h" #include "XScreen.h" +#include "CSocketMultiplexer.h" #include "CTCPSocketFactory.h" #include "XSocket.h" -#include "CLock.h" -#include "CMutex.h" #include "CThread.h" -#include "XThread.h" -#include "CFunctionJob.h" +#include "CEventQueue.h" +#include "CFunctionEventJob.h" #include "CLog.h" #include "LogOutputters.h" #include "CArch.h" +#include "XArch.h" #include "stdfstream.h" #include @@ -98,166 +101,301 @@ CArgs* CArgs::s_instance = NULL; // platform dependent factories // -//! Factory for creating screens -/*! -Objects of this type create screens appropriate for the platform. -*/ -class CScreenFactory : public IScreenFactory { -public: - CScreenFactory() { } - virtual ~CScreenFactory() { } - - // IScreenFactory overrides - virtual IPlatformScreen* - create(IScreenReceiver*, IPrimaryScreenReceiver*); -}; - -IPlatformScreen* -CScreenFactory::create(IScreenReceiver* receiver, - IPrimaryScreenReceiver* primaryReceiver) +static +CScreen* +createScreen() { #if WINDOWS_LIKE - return new CMSWindowsScreen(receiver, primaryReceiver); + return new CScreen(new CMSWindowsScreen(true)); #elif UNIX_LIKE - return new CXWindowsScreen(receiver, primaryReceiver); + return new CScreen(new CXWindowsScreen(true)); #endif } -//! CQuitJob -/*! -A job that cancels a given thread. -*/ -class CQuitJob : public IJob { -public: - CQuitJob(const CThread& thread); - ~CQuitJob(); - - // IJob overrides - virtual void run(); - -private: - CThread m_thread; -}; - -CQuitJob::CQuitJob(const CThread& thread) : - m_thread(thread) -{ - // do nothing -} - -CQuitJob::~CQuitJob() -{ - // do nothing -} - -void -CQuitJob::run() -{ - m_thread.cancel(); -} - - // // platform independent main // static CServer* s_server = NULL; +static CPrimaryClient* s_primaryClient = NULL; +static CClientListener* s_listener = NULL; static CServerTaskBarReceiver* s_taskBarReceiver = NULL; static -int -realMain(void) +void +updateStatus() { - int result = kExitSuccess; - do { - bool opened = false; - bool locked = true; - try { - // if configuration has no screens then add this system - // as the default - if (ARG->m_config.begin() == ARG->m_config.end()) { - ARG->m_config.addScreen(ARG->m_name); - } - - // set the contact address, if provided, in the config. - // otherwise, if the config doesn't have an address, use - // the default. - if (ARG->m_synergyAddress.isValid()) { - ARG->m_config.setSynergyAddress(ARG->m_synergyAddress); - } - else if (!ARG->m_config.getSynergyAddress().isValid()) { - ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); - } - - // create server - s_server = new CServer(ARG->m_name); - s_server->setConfig(ARG->m_config); - s_server->setScreenFactory(new CScreenFactory); - s_server->setSocketFactory(new CTCPSocketFactory); - s_server->setStreamFilterFactory(NULL); - - // open server - try { - s_taskBarReceiver->setServer(s_server); - s_server->open(); - opened = true; - - // run server - DAEMON_RUNNING(true); - locked = false; - s_server->mainLoop(); - - // clean up -#define FINALLY do { \ - if (!locked) { \ - DAEMON_RUNNING(false); \ - locked = true; \ - } \ - if (opened) { \ - s_server->close(); \ - } \ - s_taskBarReceiver->setServer(NULL); \ - delete s_server; \ - s_server = NULL; \ - } while (false) - FINALLY; - } - catch (XScreenUnavailable& e) { - // wait before retrying if we're going to retry - if (ARG->m_restartable) { - ARCH->sleep(e.getRetryTime()); - } - else { - result = kExitFailed; - } - FINALLY; - } - catch (XThread&) { - FINALLY; - throw; - } - catch (...) { - // don't try to restart and fail - ARG->m_restartable = false; - result = kExitFailed; - FINALLY; - } -#undef FINALLY - } - catch (XBase& e) { - LOG((CLOG_CRIT "failed: %s", e.what())); - } - catch (XThread&) { - // terminated - ARG->m_restartable = false; - result = kExitTerminated; - } - } while (ARG->m_restartable); - - return result; + s_taskBarReceiver->updateStatus(s_server, ""); } +static +void +updateStatus(const CString& msg) +{ + s_taskBarReceiver->updateStatus(s_server, msg); +} + +static +void +handleClientConnected(const CEvent&, void* vlistener) +{ + CClientListener* listener = reinterpret_cast(vlistener); + CClientProxy* client = listener->getNextClient(); + if (client != NULL) { + s_server->adoptClient(client); + updateStatus(); + } +} + +static +CClientListener* +openClientListener(const CNetworkAddress& address) +{ + CClientListener* listen = + new CClientListener(address, new CTCPSocketFactory, NULL); + EVENTQUEUE->adoptHandler(CClientListener::getConnectedEvent(), listen, + new CFunctionEventJob( + &handleClientConnected, listen)); + return listen; +} + +static +void +closeClientListener(CClientListener* listen) +{ + if (listen != NULL) { + EVENTQUEUE->removeHandler(CClientListener::getConnectedEvent(), listen); + delete listen; + } +} + +static +void +handleScreenError(const CEvent&, void*) +{ + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + +static +CPrimaryClient* +openPrimaryClient(const CString& name) +{ + LOG((CLOG_DEBUG1 "creating primary screen")); + CScreen* screen = createScreen(); + CPrimaryClient* primaryClient = new CPrimaryClient(name, screen); + EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), + primaryClient->getEventTarget(), + new CFunctionEventJob( + &handleScreenError)); + return primaryClient; +} + +static +void +closePrimaryClient(CPrimaryClient* primaryClient) +{ + if (primaryClient != NULL) { + EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), + primaryClient->getEventTarget()); + delete primaryClient; + } +} + +static +void +handleNoClients(const CEvent&, void*) +{ + updateStatus(); +} + +static +void +handleClientsDisconnected(const CEvent&, void*) +{ + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + +static +CServer* +openServer(const CConfig& config, CPrimaryClient* primaryClient) +{ + CServer* server = new CServer(config, primaryClient); + EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server, + new CFunctionEventJob(handleNoClients)); + return server; +} + +static +void +closeServer(CServer* server) +{ + if (server == NULL) { + return; + } + + // tell all clients to disconnect + server->disconnect(); + + // wait for clients to disconnect for up to timeout seconds + double timeout = 3.0; + CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, + new CFunctionEventJob(handleClientsDisconnected)); + EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server, + new CFunctionEventJob(handleClientsDisconnected)); + CEvent event; + EVENTQUEUE->getEvent(event); + while (event.getType() != CEvent::kQuit) { + EVENTQUEUE->dispatchEvent(event); + CEvent::deleteData(event); + EVENTQUEUE->getEvent(event); + } + EVENTQUEUE->removeHandler(CEvent::kTimer, timer); + EVENTQUEUE->deleteTimer(timer); + EVENTQUEUE->removeHandler(CServer::getDisconnectedEvent(), server); + + // done with server + delete server; +} + +static bool startServer(); + +static +void +retryStartHandler(const CEvent&, void* vtimer) +{ + // discard old timer + CEventQueueTimer* timer = reinterpret_cast(vtimer); + EVENTQUEUE->deleteTimer(timer); + EVENTQUEUE->removeHandler(CEvent::kTimer, NULL); + + // try starting the server again + LOG((CLOG_DEBUG1 "retry starting server")); + startServer(); +} + +static +bool +startServer() +{ + double retryTime; + CPrimaryClient* primaryClient = NULL; + CClientListener* listener = NULL; + try { + CString name = ARG->m_config.getCanonicalName(ARG->m_name); + primaryClient = openPrimaryClient(name); + listener = openClientListener(ARG->m_config.getSynergyAddress()); + s_server = openServer(ARG->m_config, primaryClient); + s_primaryClient = primaryClient; + s_listener = listener; + updateStatus(); + LOG((CLOG_NOTE "started server")); + return true; + } + catch (XScreenUnavailable& e) { + LOG((CLOG_WARN "cannot open primary screen: %s", e.what())); + closeClientListener(listener); + closePrimaryClient(primaryClient); + updateStatus(CString("cannot open primary screen: ") + e.what()); + retryTime = e.getRetryTime(); + } + catch (XSocketAddressInUse& e) { + LOG((CLOG_WARN "cannot listen for clients: %s", e.what())); + closeClientListener(listener); + closePrimaryClient(primaryClient); + updateStatus(CString("cannot listen for clients: ") + e.what()); + retryTime = 10.0; + } + catch (XScreenOpenFailure& e) { + LOG((CLOG_CRIT "cannot open primary screen: %s", e.what())); + closeClientListener(listener); + closePrimaryClient(primaryClient); + return false; + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed to start server: %s", e.what())); + closeClientListener(listener); + closePrimaryClient(primaryClient); + return false; + } + + if (ARG->m_restartable) { + // install a timer and handler to retry later + LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); + CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, + new CFunctionEventJob(&retryStartHandler, timer)); + return true; + } + else { + // don't try again + return false; + } +} + +static +int +realMain() +{ + // if configuration has no screens then add this system + // as the default + if (ARG->m_config.begin() == ARG->m_config.end()) { + ARG->m_config.addScreen(ARG->m_name); + } + + // set the contact address, if provided, in the config. + // otherwise, if the config doesn't have an address, use + // the default. + if (ARG->m_synergyAddress.isValid()) { + ARG->m_config.setSynergyAddress(ARG->m_synergyAddress); + } + else if (!ARG->m_config.getSynergyAddress().isValid()) { + ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); + } + + // canonicalize the primary screen name + CString primaryName = ARG->m_config.getCanonicalName(ARG->m_name); + if (primaryName.empty()) { + LOG((CLOG_CRIT "unknown screen name `%s'", ARG->m_name.c_str())); + return kExitFailed; + } + + // start the server. if this return false then we've failed and + // we shouldn't retry. + LOG((CLOG_DEBUG1 "starting server")); + if (!startServer()) { + return kExitFailed; + } + + // run event loop. if startServer() failed we're supposed to retry + // later. the timer installed by startServer() will take care of + // that. + DAEMON_RUNNING(true); + CEvent event; + EVENTQUEUE->getEvent(event); + while (event.getType() != CEvent::kQuit) { + EVENTQUEUE->dispatchEvent(event); + CEvent::deleteData(event); + EVENTQUEUE->getEvent(event); + } + DAEMON_RUNNING(false); + + // close down + LOG((CLOG_DEBUG1 "stopping server")); + closeClientListener(s_listener); + closeServer(s_server); + closePrimaryClient(s_primaryClient); + s_server = NULL; + s_listener = NULL; + s_primaryClient = NULL; + updateStatus(); + LOG((CLOG_NOTE "stopped server")); + + return kExitSuccess; +} + +/* XXX static void realMainEntry(void* vresult) @@ -293,7 +431,7 @@ runMainInThread(void) throw; } } - +*/ // // command line parsing @@ -666,9 +804,6 @@ daemonStartup(int argc, const char** argv) { CSystemLogger sysLogger(DAEMON_NAME); - // have to cancel this thread to quit - s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); - // catch errors that would normally exit bye = &byeThrow; @@ -768,7 +903,6 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // through the task bar. s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance, &logBuffer); - s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); int result; try { @@ -817,15 +951,20 @@ main(int argc, char** argv) { CArch arch; CLOG; - CArgs args; + + // go really fast + CThread::getCurrentThread().setPriority(-14); + + CSocketMultiplexer multiplexer; + CEventQueue eventQueue; // get program name + CArgs args; ARG->m_pname = ARCH->getBasename(argv[0]); // make the task bar receiver. the user can control this app // through the task bar. s_taskBarReceiver = new CXWindowsServerTaskBarReceiver; - s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); // parse command line parse(argc, argv); diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index 4f95bc90..d07bb57e 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -555,6 +555,12 @@ CArch::isAnyAddr(CArchNetAddress addr) return m_net->isAnyAddr(addr); } +bool +CArch::isEqualAddr(CArchNetAddress a, CArchNetAddress b) +{ + return m_net->isEqualAddr(a, b); +} + void CArch::sleep(double timeout) { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index 9e80d174..18c1cd08 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -153,6 +153,7 @@ public: virtual void setAddrPort(CArchNetAddress, int port); virtual int getAddrPort(CArchNetAddress); virtual bool isAnyAddr(CArchNetAddress); + virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress); // IArchSleep overrides virtual void sleep(double timeout); diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 707f4dfd..1d5680d6 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -575,6 +575,7 @@ CArchMultithreadPosix::interrupt() lockMutex(m_threadMutex); if (m_signalFunc != NULL) { m_signalFunc(m_signalUserData); + pthread_kill(m_mainThread->m_thread, SIGWAKEUP); } else { ARCH->cancelThread(m_mainThread); diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index bb8dae02..36daf926 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #if HAVE_POLL # include @@ -750,6 +751,13 @@ CArchNetworkBSD::isAnyAddr(CArchNetAddress addr) } } +bool +CArchNetworkBSD::isEqualAddr(CArchNetAddress a, CArchNetAddress b) +{ + return (a->m_len == b->m_len && + memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0); +} + void CArchNetworkBSD::throwError(int err) { diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index 9718d71b..18f942b8 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -77,6 +77,7 @@ public: virtual void setAddrPort(CArchNetAddress, int port); virtual int getAddrPort(CArchNetAddress); virtual bool isAnyAddr(CArchNetAddress); + virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress); private: void throwError(int); diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h index 1af8cd74..21758800 100644 --- a/lib/arch/IArchNetwork.h +++ b/lib/arch/IArchNetwork.h @@ -265,6 +265,9 @@ public: //! Get the port of an address virtual int getAddrPort(CArchNetAddress) = 0; + //! Test addresses for equality + virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress) = 0; + //! Test for the "any" address /*! Returns true if \c addr is the "any" address. \c newAnyAddr() diff --git a/lib/arch/vsnprintf.cpp b/lib/arch/vsnprintf.cpp index f06c0a30..62ccd771 100644 --- a/lib/arch/vsnprintf.cpp +++ b/lib/arch/vsnprintf.cpp @@ -56,7 +56,6 @@ ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) #else // !HAVE_VSNPRINTF && !UNIX_LIKE -// FIXME #error vsnprintf not implemented #endif // !HAVE_VSNPRINTF diff --git a/lib/base/CEvent.cpp b/lib/base/CEvent.cpp index 36f4f674..67451d6a 100644 --- a/lib/base/CEvent.cpp +++ b/lib/base/CEvent.cpp @@ -13,13 +13,12 @@ */ #include "CEvent.h" +#include "CEventQueue.h" // // CEvent // -CEvent::Type CEvent::s_nextType = kLast; - CEvent::CEvent() : m_type(kUnknown), m_target(NULL), @@ -55,20 +54,21 @@ CEvent::getData() const } CEvent::Type -CEvent::registerType() +CEvent::registerType(const char* name) { - // FIXME -- lock mutex (need a mutex) - return s_nextType++; + return EVENTQUEUE->registerType(name); } CEvent::Type -CEvent::registerTypeOnce(Type& type) +CEvent::registerTypeOnce(Type& type, const char* name) { - // FIXME -- lock mutex (need a mutex) - if (type == CEvent::kUnknown) { - type = s_nextType++; - } - return type; + return EVENTQUEUE->registerTypeOnce(type, name); +} + +const char* +CEvent::getTypeName(Type type) +{ + return EVENTQUEUE->getTypeName(type); } void diff --git a/lib/base/CEvent.h b/lib/base/CEvent.h index 8257e603..a2c44735 100644 --- a/lib/base/CEvent.h +++ b/lib/base/CEvent.h @@ -16,6 +16,7 @@ #define CEVENT_H #include "BasicTypes.h" +#include "stdmap.h" //! Event /*! @@ -46,6 +47,33 @@ public: //! @name manipulators //@{ + //! Creates a new event type + /*! + Returns a unique event type id. + */ + static Type registerType(const char* name); + + //! Creates a new event type + /*! + If \p type contains \c kUnknown then it is set to a unique event + type id otherwise it is left alone. The final value of \p type + is returned. + */ + static Type registerTypeOnce(Type& type, const char* name); + + //! Get name for event + /*! + Returns the name for the event \p type. This is primarily for + debugging. + */ + static const char* getTypeName(Type type); + + //! Release event data + /*! + Deletes event data for the given event. + */ + static void deleteData(const CEvent&); + //@} //! @name accessors //@{ @@ -68,33 +96,12 @@ public: */ void* getData() const; - //! Creates a new event type - /*! - Returns a unique event type id. - */ - static Type registerType(); - - //! Creates a new event type - /*! - If \p type contains \c kUnknown then it is set to a unique event - type id otherwise it is left alone. The final value of \p type - is returned. - */ - static Type registerTypeOnce(Type& type); - - //! Release event data - /*! - Deletes event data for the given event. - */ - static void deleteData(const CEvent&); - //@} private: Type m_type; void* m_target; void* m_data; - static Type s_nextType; }; #endif diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp index 8528b0ac..879a97ce 100644 --- a/lib/base/CEventQueue.cpp +++ b/lib/base/CEventQueue.cpp @@ -13,7 +13,9 @@ */ #include "CEventQueue.h" +#include "CLog.h" #include "CSimpleEventQueueBuffer.h" +#include "CStopwatch.h" #include "IEventJob.h" #include "CArch.h" @@ -30,7 +32,8 @@ interrupt(void*) // CEventQueue // -CEventQueue::CEventQueue() +CEventQueue::CEventQueue() : + m_nextType(CEvent::kLast) { setInstance(this); m_mutex = ARCH->newMutex(); @@ -46,6 +49,54 @@ CEventQueue::~CEventQueue() setInstance(NULL); } +CEvent::Type +CEventQueue::registerType(const char* name) +{ + CArchMutexLock lock(m_mutex); + m_typeMap.insert(std::make_pair(m_nextType, name)); + LOG((CLOG_DEBUG1 "registered event type %s as %d", name, m_nextType)); + return m_nextType++; +} + +CEvent::Type +CEventQueue::registerTypeOnce(CEvent::Type& type, const char* name) +{ + CArchMutexLock lock(m_mutex); + if (type == CEvent::kUnknown) { + m_typeMap.insert(std::make_pair(m_nextType, name)); + LOG((CLOG_DEBUG1 "registered event type %s as %d", name, m_nextType)); + type = m_nextType++; + } + return type; +} + +const char* +CEventQueue::getTypeName(CEvent::Type type) +{ + switch (type) { + case CEvent::kUnknown: + return "nil"; + + case CEvent::kQuit: + return "quit"; + + case CEvent::kSystem: + return "system"; + + case CEvent::kTimer: + return "timer"; + + default: + CTypeMap::const_iterator i = m_typeMap.find(type); + if (i == m_typeMap.end()) { + return ""; + } + else { + return i->second; + } + } +} + void CEventQueue::adoptBuffer(IEventQueueBuffer* buffer) { @@ -69,45 +120,55 @@ CEventQueue::adoptBuffer(IEventQueueBuffer* buffer) bool CEventQueue::getEvent(CEvent& event, double timeout) { + CStopwatch timer(true); +retry: // if no events are waiting then handle timers and then wait - if (m_buffer->isEmpty()) { + while (m_buffer->isEmpty()) { // handle timers first if (hasTimerExpired(event)) { return true; } + // get time remaining in timeout + double timeLeft = timeout - timer.getTime(); + if (timeout >= 0.0 && timeLeft <= 0.0) { + return false; + } + // get time until next timer expires. if there is a timer // and it'll expire before the client's timeout then use // that duration for our timeout instead. double timerTimeout = getNextTimerTimeout(); - if (timerTimeout >= 0.0 && timerTimeout < timeout) { - timeout = timerTimeout; + if (timeout < 0.0 || (timerTimeout >= 0.0 && timerTimeout < timeLeft)) { + timeLeft = timerTimeout; } // wait for an event - m_buffer->waitForEvent(timeout); - } - - // if no events are pending then do the timers - if (m_buffer->isEmpty()) { - return hasTimerExpired(event); + m_buffer->waitForEvent(timeLeft); } + // get the event UInt32 dataID; IEventQueueBuffer::Type type = m_buffer->getEvent(event, dataID); switch (type) { case IEventQueueBuffer::kNone: + if (timeout < 0.0 || timeout <= timer.getTime()) { + // don't want to fail if client isn't expecting that + // so if getEvent() fails with an infinite timeout + // then just try getting another event. + goto retry; + } return false; case IEventQueueBuffer::kSystem: return true; case IEventQueueBuffer::kUser: - { - CArchMutexLock lock(m_mutex); - event = removeEvent(dataID); - return true; - } + { + CArchMutexLock lock(m_mutex); + event = removeEvent(dataID); + return true; + } } } @@ -156,9 +217,16 @@ CEventQueue::newTimer(double duration, void* target) assert(duration > 0.0); CEventQueueTimer* timer = m_buffer->newTimer(duration, false); + if (target == NULL) { + target = timer; + } CArchMutexLock lock(m_mutex); m_timers.insert(timer); - m_timerQueue.push(CTimer(timer, duration, target, false)); + // initial duration is requested duration plus whatever's on + // the clock currently because the latter will be subtracted + // the next time we check for timers. + m_timerQueue.push(CTimer(timer, duration, + duration + m_time.getTime(), target, false)); return timer; } @@ -168,9 +236,16 @@ CEventQueue::newOneShotTimer(double duration, void* target) assert(duration > 0.0); CEventQueueTimer* timer = m_buffer->newTimer(duration, true); + if (target == NULL) { + target = timer; + } CArchMutexLock lock(m_mutex); m_timers.insert(timer); - m_timerQueue.push(CTimer(timer, duration, target, true)); + // initial duration is requested duration plus whatever's on + // the clock currently because the latter will be subtracted + // the next time we check for timers. + m_timerQueue.push(CTimer(timer, duration, + duration + m_time.getTime(), target, true)); return timer; } @@ -401,13 +476,13 @@ CEventQueue::CTypeTarget::operator<(const CTypeTarget& tt) const // CEventQueue::CTimer // -CEventQueue::CTimer::CTimer(CEventQueueTimer* timer, - double timeout, void* target, bool oneShot) : +CEventQueue::CTimer::CTimer(CEventQueueTimer* timer, double timeout, + double initialTime, void* target, bool oneShot) : m_timer(timer), m_timeout(timeout), m_target(target), m_oneShot(oneShot), - m_time(timeout) + m_time(initialTime) { assert(m_timeout > 0.0); } diff --git a/lib/base/CEventQueue.h b/lib/base/CEventQueue.h index 514612bb..b61eb1e3 100644 --- a/lib/base/CEventQueue.h +++ b/lib/base/CEventQueue.h @@ -39,9 +39,9 @@ public: virtual bool dispatchEvent(const CEvent& event); virtual void addEvent(const CEvent& event); virtual CEventQueueTimer* - newTimer(double duration, void* target = NULL); + newTimer(double duration, void* target); virtual CEventQueueTimer* - newOneShotTimer(double duration, void* target = NULL); + newOneShotTimer(double duration, void* target); virtual void deleteTimer(CEventQueueTimer*); virtual void adoptHandler(void* target, IEventJob* dispatcher); virtual void adoptHandler(CEvent::Type type, @@ -50,8 +50,13 @@ public: virtual IEventJob* orphanHandler(CEvent::Type type, void* target); virtual void removeHandler(void* target); virtual void removeHandler(CEvent::Type type, void* target); + virtual CEvent::Type + registerType(const char* name); + virtual CEvent::Type + registerTypeOnce(CEvent::Type& type, const char* name); virtual bool isEmpty() const; virtual IEventJob* getHandler(CEvent::Type type, void* target) const; + virtual const char* getTypeName(CEvent::Type type); private: void doAdoptHandler(CEvent::Type type, @@ -77,7 +82,8 @@ private: }; class CTimer { public: - CTimer(CEventQueueTimer*, double timeout, void* target, bool oneShot); + CTimer(CEventQueueTimer*, double timeout, double initialTime, + void* target, bool oneShot); ~CTimer(); void reset(); @@ -106,19 +112,28 @@ private: typedef std::map CEventTable; typedef std::vector CEventIDList; typedef std::map CHandlerTable; + typedef std::map CTypeMap; CArchMutex m_mutex; + // registered events + CEvent::Type m_nextType; + CTypeMap m_typeMap; + + // buffer of events IEventQueueBuffer* m_buffer; + // saved events CEventTable m_events; CEventIDList m_oldEventIDs; + // timers CStopwatch m_time; CTimers m_timers; CTimerQueue m_timerQueue; CTimerEvent m_timerEvent; + // event handlers CHandlerTable m_handlers; }; diff --git a/lib/base/CSimpleEventQueueBuffer.cpp b/lib/base/CSimpleEventQueueBuffer.cpp new file mode 100644 index 00000000..f01d2722 --- /dev/null +++ b/lib/base/CSimpleEventQueueBuffer.cpp @@ -0,0 +1,97 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CSimpleEventQueueBuffer.h" +#include "CStopwatch.h" +#include "CArch.h" + +class CEventQueueTimer { }; + +// +// CSimpleEventQueueBuffer +// + +CSimpleEventQueueBuffer::CSimpleEventQueueBuffer() +{ + m_queueMutex = ARCH->newMutex(); + m_queueReadyCond = ARCH->newCondVar(); + m_queueReady = false; +} + +CSimpleEventQueueBuffer::~CSimpleEventQueueBuffer() +{ + ARCH->closeCondVar(m_queueReadyCond); + ARCH->closeMutex(m_queueMutex); +} + +void +CSimpleEventQueueBuffer::waitForEvent(double timeout) +{ + CArchMutexLock lock(m_queueMutex); + CStopwatch timer(true); + while (!m_queueReady) { + double timeLeft = timeout; + if (timeLeft >= 0.0) { + timeLeft -= timer.getTime(); + if (timeLeft < 0.0) { + return; + } + } + ARCH->waitCondVar(m_queueReadyCond, m_queueMutex, timeLeft); + } +} + +IEventQueueBuffer::Type +CSimpleEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) +{ + CArchMutexLock lock(m_queueMutex); + if (!m_queueReady) { + return kNone; + } + dataID = m_queue.back(); + m_queue.pop_back(); + m_queueReady = !m_queue.empty(); + return kUser; +} + +bool +CSimpleEventQueueBuffer::addEvent(UInt32 dataID) +{ + CArchMutexLock lock(m_queueMutex); + m_queue.push_front(dataID); + if (!m_queueReady) { + m_queueReady = true; + ARCH->broadcastCondVar(m_queueReadyCond); + } + return true; +} + +bool +CSimpleEventQueueBuffer::isEmpty() const +{ + CArchMutexLock lock(m_queueMutex); + return !m_queueReady; +} + +CEventQueueTimer* +CSimpleEventQueueBuffer::newTimer(double, bool) const +{ + return new CEventQueueTimer; +} + +void +CSimpleEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const +{ + delete timer; +} diff --git a/lib/base/CSimpleEventQueueBuffer.h b/lib/base/CSimpleEventQueueBuffer.h new file mode 100644 index 00000000..c395fabd --- /dev/null +++ b/lib/base/CSimpleEventQueueBuffer.h @@ -0,0 +1,49 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSIMPLEEVENTQUEUEBUFFER_H +#define CSIMPLEEVENTQUEUEBUFFER_H + +#include "IEventQueueBuffer.h" +#include "IArchMultithread.h" +#include "stddeque.h" + +//! In-memory event queue buffer +/*! +An event queue buffer provides a queue of events for an IEventQueue. +*/ +class CSimpleEventQueueBuffer : public IEventQueueBuffer { +public: + CSimpleEventQueueBuffer(); + ~CSimpleEventQueueBuffer(); + + // IEventQueueBuffer overrides + virtual void waitForEvent(double timeout); + virtual Type getEvent(CEvent& event, UInt32& dataID); + virtual bool addEvent(UInt32 dataID); + virtual bool isEmpty() const; + virtual CEventQueueTimer* + newTimer(double duration, bool oneShot) const; + virtual void deleteTimer(CEventQueueTimer*) const; + +private: + typedef std::deque CEventDeque; + + CArchMutex m_queueMutex; + CArchCond m_queueReadyCond; + bool m_queueReady; + CEventDeque m_queue; +}; + +#endif diff --git a/lib/base/CStringUtil.cpp b/lib/base/CStringUtil.cpp index 2e61c047..08dc61c8 100644 --- a/lib/base/CStringUtil.cpp +++ b/lib/base/CStringUtil.cpp @@ -176,7 +176,7 @@ CStringUtil::CaselessCmp::cmpEqual( const CString::value_type& a, const CString::value_type& b) { - // FIXME -- use std::tolower but not in all versions of libstdc++ have it + // should use std::tolower but not in all versions of libstdc++ have it return tolower(a) == tolower(b); } @@ -185,7 +185,7 @@ CStringUtil::CaselessCmp::cmpLess( const CString::value_type& a, const CString::value_type& b) { - // FIXME -- use std::tolower but not in all versions of libstdc++ have it + // should use std::tolower but not in all versions of libstdc++ have it return tolower(a) < tolower(b); } diff --git a/lib/base/IEventQueue.h b/lib/base/IEventQueue.h index 6e649456..765d4143 100644 --- a/lib/base/IEventQueue.h +++ b/lib/base/IEventQueue.h @@ -80,7 +80,8 @@ public: is returned the data points to a \c CTimerEvent. The client must pass the returned timer to \c deleteTimer() (whether or not the timer has expired) to release the timer. The returned timer event uses the - given \p target. + given \p target. If \p target is NULL it uses the returned timer as + the target. Events for a single timer don't accumulate in the queue, even if the client reading events can't keep up. Instead, the \c m_count member @@ -89,7 +90,7 @@ public: removed (or since the timer was added). */ virtual CEventQueueTimer* - newTimer(double duration, void* target = NULL) = 0; + newTimer(double duration, void* target) = 0; //! Create a one-shot timer /*! @@ -99,11 +100,12 @@ public: The \m c_count member of the \c CTimerEvent is always 1. The client must pass the returned timer to \c deleteTimer() (whether or not the timer has expired) to release the timer. The returned timer event - uses the given \p target. + uses the given \p target. If \p target is NULL it uses the returned + timer as the target. */ virtual CEventQueueTimer* newOneShotTimer(double duration, - void* target = NULL) = 0; + void* target) = 0; //! Destroy a timer /*! @@ -160,6 +162,23 @@ public: */ virtual void removeHandler(CEvent::Type type, void* target) = 0; + //! Creates a new event type + /*! + Returns a unique event type id. + */ + virtual CEvent::Type + registerType(const char* name) = 0; + + //! Creates a new event type + /*! + If \p type contains \c kUnknown then it is set to a unique event + type id otherwise it is left alone. The final value of \p type + is returned. + */ + virtual CEvent::Type + registerTypeOnce(CEvent::Type& type, + const char* name) = 0; + //@} //! @name accessors //@{ @@ -179,6 +198,13 @@ public: */ virtual IEventJob* getHandler(CEvent::Type type, void* target) const = 0; + //! Get name for event + /*! + Returns the name for the event \p type. This is primarily for + debugging. + */ + virtual const char* getTypeName(CEvent::Type type) = 0; + //! Get the system event type target /*! Returns the target to use for dispatching \c CEvent::kSystem events. diff --git a/lib/base/IEventQueueBuffer.h b/lib/base/IEventQueueBuffer.h new file mode 100644 index 00000000..1aff51a6 --- /dev/null +++ b/lib/base/IEventQueueBuffer.h @@ -0,0 +1,94 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IEVENTQUEUEBUFFER_H +#define IEVENTQUEUEBUFFER_H + +#include "IInterface.h" +#include "BasicTypes.h" + +class CEvent; +class CEventQueueTimer; + +//! Event queue buffer interface +/*! +An event queue buffer provides a queue of events for an IEventQueue. +*/ +class IEventQueueBuffer : public IInterface { +public: + enum Type { + kNone, //!< No event is available + kSystem, //!< Event is a system event + kUser //!< Event is a user event + }; + + //! @name manipulators + //@{ + + //! Block waiting for an event + /*! + Wait for an event in the event queue buffer for up to \p timeout + seconds. + */ + virtual void waitForEvent(double timeout) = 0; + + //! Get the next event + /*! + Get the next event from the buffer. Return kNone if no event is + available. If a system event is next, return kSystem and fill in + event. The event data in a system event can point to a static + buffer (because CEvent::deleteData() will not attempt to delete + data in a kSystem event). Otherwise, return kUser and fill in + \p dataID with the value passed to \c addEvent(). + */ + virtual Type getEvent(CEvent& event, UInt32& dataID) = 0; + + //! Post an event + /*! + Add the given event to the end of the queue buffer. This is a user + event and \c getEvent() must be able to identify it as such and + return \p dataID. This method must cause \c waitForEvent() to + return at some future time if it's blocked waiting on an event. + */ + virtual bool addEvent(UInt32 dataID) = 0; + + //@} + //! @name accessors + //@{ + + //! Check if event queue buffer is empty + /*! + Return true iff the event queue buffer is empty. + */ + virtual bool isEmpty() const = 0; + + //! Create a timer object + /*! + Create and return a timer object. The object is opaque and is + used only by the buffer but it must be a valid object (i.e. + not NULL). + */ + virtual CEventQueueTimer* + newTimer(double duration, bool oneShot) const = 0; + + //! Destroy a timer object + /*! + Destroy a timer object previously returned by \c newTimer(). + */ + virtual void deleteTimer(CEventQueueTimer*) const = 0; + + //@} +}; + +#endif diff --git a/lib/io/IStream.cpp b/lib/io/IStream.cpp index 42947190..aec65b61 100644 --- a/lib/io/IStream.cpp +++ b/lib/io/IStream.cpp @@ -27,29 +27,34 @@ CEvent::Type IStream::s_outputShutdownEvent = CEvent::kUnknown; CEvent::Type IStream::getInputReadyEvent() { - return CEvent::registerTypeOnce(s_inputReadyEvent); + return CEvent::registerTypeOnce(s_inputReadyEvent, + "IStream::inputReady"); } CEvent::Type IStream::getOutputFlushedEvent() { - return CEvent::registerTypeOnce(s_outputFlushedEvent); + return CEvent::registerTypeOnce(s_outputFlushedEvent, + "IStream::outputFlushed"); } CEvent::Type IStream::getOutputErrorEvent() { - return CEvent::registerTypeOnce(s_outputErrorEvent); + return CEvent::registerTypeOnce(s_outputErrorEvent, + "IStream::outputError"); } CEvent::Type IStream::getInputShutdownEvent() { - return CEvent::registerTypeOnce(s_inputShutdownEvent); + return CEvent::registerTypeOnce(s_inputShutdownEvent, + "IStream::inputShutdown"); } CEvent::Type IStream::getOutputShutdownEvent() { - return CEvent::registerTypeOnce(s_outputShutdownEvent); + return CEvent::registerTypeOnce(s_outputShutdownEvent, + "IStream::outputShutdown"); } diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp index c60b96a5..3b2e3032 100644 --- a/lib/net/CNetworkAddress.cpp +++ b/lib/net/CNetworkAddress.cpp @@ -141,6 +141,18 @@ CNetworkAddress::operator=(const CNetworkAddress& addr) return *this; } +bool +CNetworkAddress::operator==(const CNetworkAddress& addr) const +{ + return ARCH->isEqualAddr(m_address, addr.m_address); +} + +bool +CNetworkAddress::operator!=(const CNetworkAddress& addr) const +{ + return !operator==(addr); +} + bool CNetworkAddress::isValid() const { diff --git a/lib/net/CNetworkAddress.h b/lib/net/CNetworkAddress.h index 02898278..f1e66bab 100644 --- a/lib/net/CNetworkAddress.h +++ b/lib/net/CNetworkAddress.h @@ -55,6 +55,18 @@ public: //! @name accessors //@{ + //! Check address equality + /*! + Returns true if this address is equal to \p address. + */ + bool operator==(const CNetworkAddress&) const; + + //! Check address inequality + /*! + Returns true if this address is not equal to \p address. + */ + bool operator!=(const CNetworkAddress&) const; + //! Check address validity /*! Returns true if this is not the invalid address. diff --git a/lib/net/CSocketMultiplexer.cpp b/lib/net/CSocketMultiplexer.cpp index 46191b3c..2ed2dc35 100644 --- a/lib/net/CSocketMultiplexer.cpp +++ b/lib/net/CSocketMultiplexer.cpp @@ -18,6 +18,7 @@ #include "CLock.h" #include "CMutex.h" #include "CThread.h" +#include "CLog.h" #include "TMethodJob.h" #include "CArch.h" #include "XArch.h" @@ -207,8 +208,8 @@ CSocketMultiplexer::serviceThread(void*) // check for status status = ARCH->pollSocket(&pfds[0], pfds.size(), -1); } - catch (XArchNetwork&) { - // FIXME -- uh oh + catch (XArchNetwork& e) { + LOG((CLOG_WARN "error in socket multiplexer: %s", e.what().c_str())); status = 0; } diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index 49695446..bca77cd4 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -62,6 +62,7 @@ CTCPListenSocket::bind(const CNetworkAddress& addr) CLock lock(m_mutex); ARCH->bindSocket(m_socket, addr.getAddress()); ARCH->listenOnSocket(m_socket); + ARCH->setBlockingOnSocket(m_socket, false); CSocketMultiplexer::getInstance()->addSocket(this, new TSocketMultiplexerMethodJob( this, &CTCPListenSocket::serviceListening, @@ -102,11 +103,13 @@ IDataSocket* CTCPListenSocket::accept() { try { + IDataSocket* socket = + new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); CSocketMultiplexer::getInstance()->addSocket(this, new TSocketMultiplexerMethodJob( this, &CTCPListenSocket::serviceListening, m_socket, true, false)); - return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); + return socket; } catch (XArchNetwork&) { return NULL; diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index fd592203..0b7b6ddf 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -19,6 +19,7 @@ #include "XSocket.h" #include "CLock.h" #include "CEventQueue.h" +#include "CLog.h" #include "IEventJob.h" #include "CArch.h" #include "XArch.h" @@ -102,8 +103,8 @@ CTCPSocket::close() ARCH->closeSocket(socket); } catch (XArchNetwork& e) { - // FIXME -- just discard this for now - //throw XSocketIOClose(e.what()); + // ignore, there's not much we can do + LOG((CLOG_WARN "error closing socket: %s", e.what().c_str())); } } } @@ -257,7 +258,6 @@ CTCPSocket::connect(const CNetworkAddress& addr) } try { -// FIXME -- don't throw if in progress, just return that info ARCH->connectSocket(m_socket, addr.getAddress()); sendSocketEvent(getConnectedEvent()); onConnected(); @@ -281,15 +281,13 @@ CTCPSocket::init() m_readable = false; m_writable = false; - // make socket non-blocking -// FIXME -- check for error - ARCH->setBlockingOnSocket(m_socket, false); - - // turn off Nagle algorithm. we send lots of very short messages - // that should be sent without (much) delay. for example, the - // mouse motion messages are much less useful if they're delayed. -// FIXME -- the client should do this try { + // make socket non-blocking + ARCH->setBlockingOnSocket(m_socket, false); + + // turn off Nagle algorithm. we send lots of very short messages + // that should be sent without (much) delay. for example, the + // mouse motion messages are much less useful if they're delayed. ARCH->setNoDelayOnSocket(m_socket, true); } catch (XArchNetwork& e) { diff --git a/lib/net/IDataSocket.cpp b/lib/net/IDataSocket.cpp index b3f6a5e1..ff10f4ec 100644 --- a/lib/net/IDataSocket.cpp +++ b/lib/net/IDataSocket.cpp @@ -24,11 +24,13 @@ CEvent::Type IDataSocket::s_failedEvent = CEvent::kUnknown; CEvent::Type IDataSocket::getConnectedEvent() { - return CEvent::registerTypeOnce(s_connectedEvent); + return CEvent::registerTypeOnce(s_connectedEvent, + "IDataSocket::connected"); } CEvent::Type IDataSocket::getConnectionFailedEvent() { - return CEvent::registerTypeOnce(s_failedEvent); + return CEvent::registerTypeOnce(s_failedEvent, + "IDataSocket::failed"); } diff --git a/lib/net/IListenSocket.cpp b/lib/net/IListenSocket.cpp index 9c9e704c..20dbc9a4 100644 --- a/lib/net/IListenSocket.cpp +++ b/lib/net/IListenSocket.cpp @@ -23,5 +23,6 @@ CEvent::Type IListenSocket::s_connectingEvent = CEvent::kUnknown; CEvent::Type IListenSocket::getConnectingEvent() { - return CEvent::registerTypeOnce(s_connectingEvent); + return CEvent::registerTypeOnce(s_connectingEvent, + "IListenSocket::connecting"); } diff --git a/lib/net/ISocket.cpp b/lib/net/ISocket.cpp index b1adbcc3..a9aaf1ac 100644 --- a/lib/net/ISocket.cpp +++ b/lib/net/ISocket.cpp @@ -23,5 +23,6 @@ CEvent::Type ISocket::s_disconnectedEvent = CEvent::kUnknown; CEvent::Type ISocket::getDisconnectedEvent() { - return CEvent::registerTypeOnce(s_disconnectedEvent); + return CEvent::registerTypeOnce(s_disconnectedEvent, + "ISocket::disconnected"); } diff --git a/lib/platform/CXWindowsEventQueue.cpp b/lib/platform/CXWindowsEventQueueBuffer.cpp similarity index 62% rename from lib/platform/CXWindowsEventQueue.cpp rename to lib/platform/CXWindowsEventQueueBuffer.cpp index 9c51284d..7592bc89 100644 --- a/lib/platform/CXWindowsEventQueue.cpp +++ b/lib/platform/CXWindowsEventQueueBuffer.cpp @@ -12,9 +12,10 @@ * GNU General Public License for more details. */ -#include "CXWindowsEventQueue.h" -#include "CEvent.h" +#include "CXWindowsEventQueueBuffer.h" #include "CThread.h" +#include "CEvent.h" +#include "IEventQueue.h" #if UNIX_LIKE # if HAVE_POLL # include @@ -42,52 +43,27 @@ class CEventQueueTimer { }; // -// CXWindowsEventQueue +// CXWindowsEventQueueBuffer // -CXWindowsEventQueue::CXWindowsEventQueue(Display* display) : - m_display(display) +CXWindowsEventQueueBuffer::CXWindowsEventQueueBuffer( + Display* display, Window window) : + m_display(display), + m_window(window) { + assert(m_display != NULL); + assert(m_window != None); + m_userEvent = XInternAtom(m_display, "SYNERGY_USER_EVENT", False); - - XSetWindowAttributes attr; - m_window = XCreateWindow(m_display, DefaultRootWindow(m_display), - 0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent, - 0, &attr); } -CXWindowsEventQueue::~CXWindowsEventQueue() +CXWindowsEventQueueBuffer::~CXWindowsEventQueueBuffer() { - XDestroyWindow(m_display, m_window); + // do nothing } void -CXWindowsEventQueue::processSystemEvent(CEvent& event) -{ - event = CEvent(CEvent::kSystem, getSystemTarget(), &m_event); -} - -void -CXWindowsEventQueue::processClientMessage(CEvent& event) -{ - assert(m_event.xany.type == ClientMessage); - - // handle user events specially - if (m_event.xclient.message_type == m_userEvent) { - // get event data - CEventData data = removeEventData(m_event.xclient.data.l[1]); - - // create event - event = CEvent(static_cast(m_event.xclient.data.l[0]), - data.first, data.second); - } - else { - processSystemEvent(event); - } -} - -void -CXWindowsEventQueue::waitForEvent(double dtimeout) +CXWindowsEventQueueBuffer::waitForEvent(double dtimeout) { // use poll() to wait for a message from the X server or for timeout. // this is a good deal more efficient than polling and sleeping. @@ -132,25 +108,27 @@ CXWindowsEventQueue::waitForEvent(double dtimeout) CThread::testCancel(); } -bool -CXWindowsEventQueue::doGetEvent(CEvent& event) +IEventQueueBuffer::Type +CXWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) { // get next event XNextEvent(m_display, &m_event); // process event - if (m_event.xany.type == ClientMessage) { - processClientMessage(event); + if (m_event.xany.type == ClientMessage && + m_event.xclient.message_type == m_userEvent) { + dataID = static_cast(m_event.xclient.data.l[0]); + return kUser; } else { - processSystemEvent(event); + event = CEvent(CEvent::kSystem, + IEventQueue::getSystemTarget(), &m_event); + return kSystem; } - - return true; } bool -CXWindowsEventQueue::doAddEvent(CEvent::Type type, UInt32 dataID) +CXWindowsEventQueueBuffer::addEvent(UInt32 dataID) { // send ourself a message XEvent xevent; @@ -158,25 +136,29 @@ CXWindowsEventQueue::doAddEvent(CEvent::Type type, UInt32 dataID) xevent.xclient.window = m_window; xevent.xclient.message_type = m_userEvent; xevent.xclient.format = 32; - xevent.xclient.data.l[0] = static_cast(type); - xevent.xclient.data.l[1] = static_cast(dataID); - return (XSendEvent(m_display, m_window, False, 0, &xevent) != 0); + xevent.xclient.data.l[0] = static_cast(dataID); + if (XSendEvent(m_display, m_window, False, 0, &xevent) == 0) { + return false; + } + + // force waitForEvent() to return + XFlush(m_display); } bool -CXWindowsEventQueue::doIsEmpty() const +CXWindowsEventQueueBuffer::isEmpty() const { return (XPending(m_display) == 0); } CEventQueueTimer* -CXWindowsEventQueue::doNewTimer(double, bool) const +CXWindowsEventQueueBuffer::newTimer(double, bool) const { return new CEventQueueTimer(); } void -CXWindowsEventQueue::doDeleteTimer(CEventQueueTimer* timer) const +CXWindowsEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const { delete timer; } diff --git a/lib/platform/CXWindowsEventQueue.h b/lib/platform/CXWindowsEventQueueBuffer.h similarity index 55% rename from lib/platform/CXWindowsEventQueue.h rename to lib/platform/CXWindowsEventQueueBuffer.h index 39d9faef..8b3d4312 100644 --- a/lib/platform/CXWindowsEventQueue.h +++ b/lib/platform/CXWindowsEventQueueBuffer.h @@ -12,44 +12,30 @@ * GNU General Public License for more details. */ -#ifndef CXWINDOWSEVENTQUEUE_H -#define CXWINDOWSEVENTQUEUE_H +#ifndef CXWINDOWSEVENTQUEUEBUFFER_H +#define CXWINDOWSEVENTQUEUEBUFFER_H -#include "CEventQueue.h" +#include "IEventQueueBuffer.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else # include #endif -//! Event queue for X11 -class CXWindowsEventQueue : public CEventQueue { +//! Event queue buffer for X11 +class CXWindowsEventQueueBuffer : public IEventQueueBuffer { public: - CXWindowsEventQueue(Display*); - virtual ~CXWindowsEventQueue(); + CXWindowsEventQueueBuffer(Display*, Window); + virtual ~CXWindowsEventQueueBuffer(); - //! @name manipulators - //@{ - - //@} - //! @name accessors - //@{ - - //@} - -protected: - // CEventQueue overrides + // IEventQueueBuffer overrides virtual void waitForEvent(double timeout); - virtual bool doGetEvent(CEvent& event); - virtual bool doAddEvent(CEvent::Type type, UInt32 dataID); - virtual bool doIsEmpty() const; + virtual Type getEvent(CEvent& event, UInt32& dataID); + virtual bool addEvent(UInt32 dataID); + virtual bool isEmpty() const; virtual CEventQueueTimer* - doNewTimer(double duration, bool oneShot) const; - virtual void doDeleteTimer(CEventQueueTimer*) const; - -private: - void processSystemEvent(CEvent& event); - void processClientMessage(CEvent& event); + newTimer(double duration, bool oneShot) const; + virtual void deleteTimer(CEventQueueTimer*) const; private: Display* m_display; diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 12b1d703..c7e5f1cc 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -14,18 +14,16 @@ #include "CXWindowsScreen.h" #include "CXWindowsClipboard.h" +#include "CXWindowsEventQueueBuffer.h" #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CClipboard.h" -#include "IScreenReceiver.h" -#include "IPrimaryScreenReceiver.h" #include "XScreen.h" -#include "CLock.h" -#include "CThread.h" #include "CLog.h" #include "CStopwatch.h" #include "CStringUtil.h" -#include "IJob.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" #include #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy @@ -109,16 +107,24 @@ static const KeySym g_map1008FF[] = // CXWindowsScreen // +// NOTE -- the X display is shared among several objects but is owned +// by the CXWindowsScreen. Xlib is not reentrant so we must ensure +// that no two objects can simultaneously call Xlib with the display. +// this is easy since we only make X11 calls from the main thread. +// we must also ensure that these objects do not use the display in +// their destructors or, if they do, we can tell them not to. This +// is to handle unexpected disconnection of the X display, when any +// call on the display is invalid. In that situation we discard the +// display and the X11 event queue buffer, ignore any calls that try +// to use the display, and wait to be destroyed. + CXWindowsScreen* CXWindowsScreen::s_screen = NULL; -CXWindowsScreen::CXWindowsScreen(IScreenReceiver* receiver, - IPrimaryScreenReceiver* primaryReceiver) : - m_isPrimary(primaryReceiver != NULL), +CXWindowsScreen::CXWindowsScreen(bool isPrimary) : + m_isPrimary(isPrimary), m_display(NULL), m_root(None), m_window(None), - m_receiver(receiver), - m_primaryReceiver(primaryReceiver), m_isOnScreen(m_isPrimary), m_x(0), m_y(0), m_w(0), m_h(0), @@ -129,205 +135,99 @@ CXWindowsScreen::CXWindowsScreen(IScreenReceiver* receiver, m_im(NULL), m_ic(NULL), m_lastKeycode(0), - m_atomQuit(None), + m_sequenceNumber(0), m_screensaver(NULL), m_screensaverNotify(false), - m_atomScreensaver(None), - m_oneShotTimer(NULL), m_xtestIsXineramaUnaware(true) { - assert(s_screen == NULL); - assert(m_receiver != NULL); + assert(s_screen == NULL); s_screen = this; - // no clipboards to start with - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_clipboard[id] = NULL; + // set the X I/O error handler so we catch the display disconnecting + XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); + + try { + m_display = openDisplay(); + m_root = DefaultRootWindow(m_display); + saveShape(); + m_window = openWindow(); + m_screensaver = new CXWindowsScreenSaver(m_display, + m_window, getEventTarget()); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_xinerama ? "(xinerama)" : "")); + LOG((CLOG_DEBUG "window is 0x%08x", m_window)); } + catch (...) { + if (m_display != NULL) { + XCloseDisplay(m_display); + } + throw; + } + + // primary/secondary screen only initialization + if (m_isPrimary) { + // start watching for events on other windows + selectEvents(m_root); + + // prepare to use input methods + openIM(); + } + else { + // become impervious to server grabs + XTestGrabControl(m_display, True); + } + + // initialize the clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = new CXWindowsClipboard(m_display, m_window, id); + } + + // install event handlers + EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(), + new TMethodEventJob(this, + &CXWindowsScreen::handleSystemEvent)); + + // install the platform event queue + EVENTQUEUE->adoptBuffer(new CXWindowsEventQueueBuffer(m_display, m_window)); } CXWindowsScreen::~CXWindowsScreen() { assert(s_screen != NULL); - assert(m_display == NULL); + assert(m_display != NULL); + + EVENTQUEUE->adoptBuffer(NULL); + EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget()); + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + delete m_clipboard[id]; + } + delete m_screensaver; + m_screensaver = NULL; + if (m_display != NULL) { + // FIXME -- is it safe to clean up the IC and IM without a display? + if (m_ic != NULL) { + XDestroyIC(m_ic); + } + if (m_im != NULL) { + XCloseIM(m_im); + } + XDestroyWindow(m_display, m_window); + XCloseDisplay(m_display); + } + XSetIOErrorHandler(NULL); - delete m_oneShotTimer; s_screen = NULL; } void -CXWindowsScreen::open(IKeyState* keyState) +CXWindowsScreen::setKeyState(IKeyState* keyState) { - assert(m_display == NULL); - - try { - // set the X I/O error handler so we catch the display disconnecting - XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); - - // get the DISPLAY - const char* display = getenv("DISPLAY"); - if (display == NULL) { - display = ":0.0"; - } - - // open the display - LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); - m_display = XOpenDisplay(display); - if (m_display == NULL) { - throw XScreenUnavailable(60.0); - } - - // verify the availability of the XTest extension - if (!m_isPrimary) { - int majorOpcode, firstEvent, firstError; - if (!XQueryExtension(m_display, XTestExtensionName, - &majorOpcode, &firstEvent, &firstError)) { - LOG((CLOG_ERR "XTEST extension not available")); - throw XScreenOpenFailure(); - } - } - - // get root window - m_root = DefaultRootWindow(m_display); - - // get shape of default screen - m_x = 0; - m_y = 0; - m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); - m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); - LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); - - // get center of default screen - m_xCenter = m_x + (m_w >> 1); - m_yCenter = m_y + (m_h >> 1); - - // check if xinerama is enabled and there is more than one screen. - // get center of first Xinerama screen. Xinerama appears to have - // a bug when XWarpPointer() is used in combination with - // XGrabPointer(). in that case, the warp is successful but the - // next pointer motion warps the pointer again, apparently to - // constrain it to some unknown region, possibly the region from - // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over - // all physical screens. this warp only seems to happen if the - // pointer wasn't in that region before the XWarpPointer(). the - // second (unexpected) warp causes synergy to think the pointer - // has been moved when it hasn't. to work around the problem, - // we warp the pointer to the center of the first physical - // screen instead of the logical screen. - m_xinerama = false; -#if HAVE_X11_EXTENSIONS_XINERAMA_H - int eventBase, errorBase; - if (XineramaQueryExtension(m_display, &eventBase, &errorBase) && - XineramaIsActive(m_display)) { - int numScreens; - XineramaScreenInfo* screens; - screens = XineramaQueryScreens(m_display, &numScreens); - if (screens != NULL) { - if (numScreens > 1) { - m_xinerama = true; - m_xCenter = screens[0].x_org + (screens[0].width >> 1); - m_yCenter = screens[0].y_org + (screens[0].height >> 1); - } - XFree(screens); - } - } -#endif - - // create the window - m_window = createWindow(); - if (m_window == None) { - throw XScreenOpenFailure(); - } - LOG((CLOG_DEBUG "window is 0x%08x", m_window)); - - if (m_isPrimary) { - // start watching for events on other windows - selectEvents(m_root); - - // prepare to use input methods - openIM(); - } - - // initialize the clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_clipboard[id] = new CXWindowsClipboard(m_display, m_window, id); - } - - // initialize the screen saver - m_atomScreensaver = XInternAtom(m_display, - "SYNERGY_SCREENSAVER", False); - m_screensaver = new CXWindowsScreenSaver(this, m_display); - } - catch (...) { - close(); - throw; - } - - // save the IKeyState m_keyState = keyState; - - // we'll send ourself an event of this type to exit the main loop - m_atomQuit = XInternAtom(m_display, "SYNERGY_QUIT", False); - - if (!m_isPrimary) { - // become impervious to server grabs - XTestGrabControl(m_display, True); - } -} - -void -CXWindowsScreen::close() -{ - // done with m_keyState - m_keyState = NULL; - - // done with screen saver - delete m_screensaver; - - // destroy clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - delete m_clipboard[id]; - m_clipboard[id] = NULL; - } - - // done with input methods - if (m_ic != NULL) { - XDestroyIC(m_ic); - } - if (m_im != NULL) { - XCloseIM(m_im); - } - - // done with window - if (m_window != None) { - XDestroyWindow(m_display, m_window); - } - - // close the display - if (m_display != NULL) { - XCloseDisplay(m_display); - } - - // restore error handler - XSetIOErrorHandler(NULL); - - // reset state - m_atomQuit = None; - m_screensaver = NULL; - m_atomScreensaver = None; - m_ic = NULL; - m_im = NULL; - m_window = None; - m_root = None; - m_display = NULL; } void CXWindowsScreen::enable() { - CLock lock(&m_mutex); - if (!m_isPrimary) { // get the keyboard control state XKeyboardState keyControl; @@ -349,8 +249,6 @@ CXWindowsScreen::enable() void CXWindowsScreen::disable() { - CLock lock(&m_mutex); - // release input context focus if (m_ic != NULL) { XUnsetICFocus(m_ic); @@ -366,145 +264,9 @@ CXWindowsScreen::disable() } } -void -CXWindowsScreen::mainLoop() -{ - // wait for an event in a cancellable way and don't lock the - // display while we're waiting. we use CLock to ensure that - // we unlock on exit but we directly unlock/lock the mutex - // for certain sections when we mustn't hold the lock. it's - // very important that these sections not return (even by - // exception or cancellation) without first reestablishing - // the lock. - XEvent event; - CLock lock(&m_mutex); - - for (;;) { - -#if UNIX_LIKE - - // compute timeout to next timer - double dtimeout; - { - CLock timersLock(&m_timersMutex); - dtimeout = (m_timers.empty() ? -1.0 : m_timers.top()); - if (m_oneShotTimer != NULL && - (dtimeout == -1.0 || *m_oneShotTimer < dtimeout)) { - dtimeout = *m_oneShotTimer; - } - } - - // use poll() to wait for a message from the X server or for timeout. - // this is a good deal more efficient than polling and sleeping. -#if HAVE_POLL - struct pollfd pfds[1]; - pfds[0].fd = ConnectionNumber(m_display); - pfds[0].events = POLLIN; - int timeout = static_cast(1000.0 * dtimeout); -#else - struct timeval timeout; - struct timeval* timeoutPtr; - if (dtimeout < 0.0) { - timeoutPtr = NULL; - } - else { - timeout.tv_sec = static_cast(dtimeout); - timeout.tv_usec = static_cast(1.0e+6 * - (dtimeout - timeout.tv_sec)); - timeoutPtr = &timeout; - } - - // initialize file descriptor sets - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(ConnectionNumber(m_display), &rfds); -#endif - - // wait for message from X server or for timeout. also check - // if the thread has been cancelled. poll() should return -1 - // with EINTR when the thread is cancelled. - CThread::testCancel(); - m_mutex.unlock(); -#if HAVE_POLL - poll(pfds, 1, timeout); -#else - select(ConnectionNumber(m_display) + 1, - SELECT_TYPE_ARG234 &rfds, - SELECT_TYPE_ARG234 NULL, - SELECT_TYPE_ARG234 NULL, - SELECT_TYPE_ARG5 timeoutPtr); -#endif - m_mutex.lock(); - CThread::testCancel(); - - // process timers - processTimers(); - -#else // !UNIX_LIKE - - // poll for pending events and process timers - while (XPending(m_display) == 0) { - // check timers - if (processTimers()) { - continue; - } - - // wait - try { - m_mutex.unlock(); - CThread::sleep(0.01); - m_mutex.lock(); - } - catch (...) { - m_mutex.lock(); - throw; - } - } - -#endif // !UNIX_LIKE - - // process events - while (XPending(m_display) > 0) { - // get the event - XNextEvent(m_display, &event); - if (isQuitEvent(&event)) { - return; - } - - // process the event - onEvent(&event); - } - } -} - -void -CXWindowsScreen::exitMainLoop() -{ - // send ourself a quit event. this will wake up the event loop - // and cause it to exit. - if (m_atomQuit != None) { - XEvent event; - event.xclient.type = ClientMessage; - event.xclient.display = m_display; - event.xclient.window = m_window; - event.xclient.message_type = m_atomQuit; - event.xclient.format = 32; - event.xclient.data.l[0] = 0; - event.xclient.data.l[1] = 0; - event.xclient.data.l[2] = 0; - event.xclient.data.l[3] = 0; - event.xclient.data.l[4] = 0; - CLock lock(&m_mutex); - CXWindowsUtil::CErrorLock errorLock(m_display); - XSendEvent(m_display, m_window, False, 0, &event); - } -} - void CXWindowsScreen::enter() { - CLock lock(&m_mutex); - // release input context focus if (m_ic != NULL) { XUnsetICFocus(m_ic); @@ -539,8 +301,6 @@ CXWindowsScreen::enter() bool CXWindowsScreen::leave() { - CLock lock(&m_mutex); - if (!m_isPrimary) { // restore the previous keyboard auto-repeat state. if the user // changed the auto-repeat configuration while on the client then @@ -590,8 +350,6 @@ CXWindowsScreen::leave() bool CXWindowsScreen::setClipboard(ClipboardID id, const IClipboard* clipboard) { - CLock lock(&m_mutex); - // fail if we don't have the requested clipboard if (m_clipboard[id] == NULL) { return false; @@ -625,14 +383,8 @@ CXWindowsScreen::checkClipboards() void CXWindowsScreen::openScreensaver(bool notify) { - CLock lock(&m_mutex); - assert(m_screensaver != NULL); - m_screensaverNotify = notify; - if (m_screensaverNotify) { - m_screensaver->setNotify(m_window); - } - else { + if (!m_screensaverNotify) { m_screensaver->disable(); } } @@ -640,23 +392,14 @@ CXWindowsScreen::openScreensaver(bool notify) void CXWindowsScreen::closeScreensaver() { - CLock lock(&m_mutex); - if (m_screensaver != NULL) { - if (m_screensaverNotify) { - m_screensaver->setNotify(None); - } - else { - m_screensaver->enable(); - } + if (!m_screensaverNotify) { + m_screensaver->enable(); } } void CXWindowsScreen::screensaver(bool activate) { - CLock lock(&m_mutex); - assert(m_screensaver != NULL); - if (activate) { m_screensaver->activate(); } @@ -685,27 +428,34 @@ CXWindowsScreen::setOptions(const COptionsList& options) void CXWindowsScreen::updateKeys() { - CLock lock(&m_mutex); - // update keyboard and mouse button mappings m_keyMapper.update(m_display, m_keyState); updateButtons(); } +void +CXWindowsScreen::setSequenceNumber(UInt32 seqNum) +{ + m_sequenceNumber = seqNum; +} + bool CXWindowsScreen::isPrimary() const { return m_isPrimary; } +void* +CXWindowsScreen::getEventTarget() const +{ + return const_cast(this); +} + bool CXWindowsScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const { assert(clipboard != NULL); - // block others from using the display while we get the clipboard - CLock lock(&m_mutex); - // fail if we don't have the requested clipboard if (m_clipboard[id] == NULL) { return false; @@ -731,9 +481,6 @@ CXWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const void CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const { - CLock lock(&m_mutex); - assert(m_display != NULL); - Window root, window; int mx, my, xWindow, yWindow; unsigned int mask; @@ -757,8 +504,6 @@ CXWindowsScreen::reconfigure(UInt32) void CXWindowsScreen::warpCursor(SInt32 x, SInt32 y) { - CLock lock(&m_mutex); - // warp mouse warpCursorNoFlush(x, y); @@ -777,15 +522,6 @@ CXWindowsScreen::warpCursor(SInt32 x, SInt32 y) m_yCursor = y; } -UInt32 -CXWindowsScreen::addOneShotTimer(double timeout) -{ - CLock lock(&m_timersMutex); - // FIXME -- support multiple one-shot timers - m_oneShotTimer = new CTimer(NULL, m_time.getTime(), timeout); - return 0; -} - SInt32 CXWindowsScreen::getJumpZoneSize() const { @@ -795,8 +531,6 @@ CXWindowsScreen::getJumpZoneSize() const bool CXWindowsScreen::isAnyMouseButtonDown() const { - CLock lock(&m_mutex); - // query the pointer to get the button state Window root, window; int xRoot, yRoot, xWindow, yWindow; @@ -810,11 +544,16 @@ CXWindowsScreen::isAnyMouseButtonDown() const return false; } +void +CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + x = m_xCenter; + y = m_yCenter; +} + const char* CXWindowsScreen::getKeyName(KeyButton keycode) const { - CLock lock(&m_mutex); - KeySym keysym = XKeycodeToKeysym(m_display, keycode, 0); char* name = XKeysymToString(keysym); if (name != NULL) { @@ -830,7 +569,6 @@ CXWindowsScreen::getKeyName(KeyButton keycode) const void CXWindowsScreen::fakeKeyEvent(KeyButton keycode, bool press) const { - CLock lock(&m_mutex); XTestFakeKeyEvent(m_display, keycode, press ? True : False, CurrentTime); XFlush(m_display); } @@ -847,7 +585,6 @@ CXWindowsScreen::fakeMouseButton(ButtonID button, bool press) const { const unsigned int xButton = mapButtonToX(button); if (xButton != 0) { - CLock lock(&m_mutex); XTestFakeButtonEvent(m_display, xButton, press ? True : False, CurrentTime); XFlush(m_display); @@ -857,7 +594,6 @@ CXWindowsScreen::fakeMouseButton(ButtonID button, bool press) const void CXWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const { - CLock lock(&m_mutex); if (m_xinerama && m_xtestIsXineramaUnaware) { XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); } @@ -884,7 +620,6 @@ CXWindowsScreen::fakeMouseWheel(SInt32 delta) const } // send as many clicks as necessary - CLock lock(&m_mutex); for (; delta >= 120; delta -= 120) { XTestFakeButtonEvent(m_display, xButton, True, CurrentTime); XTestFakeButtonEvent(m_display, xButton, False, CurrentTime); @@ -901,17 +636,84 @@ CXWindowsScreen::mapKey(IKeyState::Keystrokes& keys, return m_keyMapper.mapKey(keys, keyState, id, desiredMask, isAutoRepeat); } -bool -CXWindowsScreen::isQuitEvent(XEvent* event) const +Display* +CXWindowsScreen::openDisplay() const { - return (m_atomQuit != None && - event->type == ClientMessage && - event->xclient.window == m_window && - event->xclient.message_type == m_atomQuit); + // get the DISPLAY + const char* displayName = getenv("DISPLAY"); + if (displayName == NULL) { + displayName = ":0.0"; + } + + // open the display + LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", displayName)); + Display* display = XOpenDisplay(displayName); + if (display == NULL) { + throw XScreenUnavailable(60.0); + } + + // verify the availability of the XTest extension + if (!m_isPrimary) { + int majorOpcode, firstEvent, firstError; + if (!XQueryExtension(display, XTestExtensionName, + &majorOpcode, &firstEvent, &firstError)) { + LOG((CLOG_ERR "XTEST extension not available")); + XCloseDisplay(display); + throw XScreenOpenFailure(); + } + } + + return display; +} + +void +CXWindowsScreen::saveShape() +{ + // get shape of default screen + m_x = 0; + m_y = 0; + m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); + m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); + + // get center of default screen + m_xCenter = m_x + (m_w >> 1); + m_yCenter = m_y + (m_h >> 1); + + // check if xinerama is enabled and there is more than one screen. + // get center of first Xinerama screen. Xinerama appears to have + // a bug when XWarpPointer() is used in combination with + // XGrabPointer(). in that case, the warp is successful but the + // next pointer motion warps the pointer again, apparently to + // constrain it to some unknown region, possibly the region from + // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over + // all physical screens. this warp only seems to happen if the + // pointer wasn't in that region before the XWarpPointer(). the + // second (unexpected) warp causes synergy to think the pointer + // has been moved when it hasn't. to work around the problem, + // we warp the pointer to the center of the first physical + // screen instead of the logical screen. + m_xinerama = false; +#if HAVE_X11_EXTENSIONS_XINERAMA_H + int eventBase, errorBase; + if (XineramaQueryExtension(m_display, &eventBase, &errorBase) && + XineramaIsActive(m_display)) { + int numScreens; + XineramaScreenInfo* screens; + screens = XineramaQueryScreens(m_display, &numScreens); + if (screens != NULL) { + if (numScreens > 1) { + m_xinerama = true; + m_xCenter = screens[0].x_org + (screens[0].width >> 1); + m_yCenter = screens[0].y_org + (screens[0].height >> 1); + } + XFree(screens); + } + } +#endif } Window -CXWindowsScreen::createWindow() const +CXWindowsScreen::openWindow() const { // default window attributes. we don't want the window manager // messing with our window and we don't want the cursor to be @@ -951,11 +753,15 @@ CXWindowsScreen::createWindow() const } // create and return the window - return XCreateWindow(m_display, m_root, x, y, w, h, 0, 0, + Window window = XCreateWindow(m_display, m_root, x, y, w, h, 0, 0, InputOnly, CopyFromParent, CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, &attr); + if (window == None) { + throw XScreenOpenFailure(); + } + return window; } void @@ -1021,40 +827,24 @@ CXWindowsScreen::openIM() } void -CXWindowsScreen::addTimer(IJob* job, double timeout) +CXWindowsScreen::sendEvent(CEvent::Type type, void* data) { - CLock lock(&m_timersMutex); - removeTimerNoLock(job); - m_timers.push(CTimer(job, m_time.getTime(), timeout)); + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data)); } void -CXWindowsScreen::removeTimer(IJob* job) +CXWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) { - CLock lock(&m_timersMutex); - removeTimerNoLock(job); + CClipboardInfo* info = new CClipboardInfo; + info->m_id = id; + info->m_sequenceNumber = m_sequenceNumber; + sendEvent(type, info); } void -CXWindowsScreen::removeTimerNoLock(IJob* job) -{ - // do it the hard way. first collect all jobs that are not - // the removed job. - CTimerPriorityQueue::container_type tmp; - for (CTimerPriorityQueue::iterator index = m_timers.begin(); - index != m_timers.end(); ++index) { - if (index->getJob() != job) { - tmp.push_back(*index); - } - } - - // now swap in the new list - m_timers.swap(tmp); -} - -void -CXWindowsScreen::onEvent(XEvent* xevent) +CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) { + XEvent* xevent = reinterpret_cast(event.getData()); assert(xevent != NULL); // let input methods try to handle event first @@ -1082,13 +872,20 @@ CXWindowsScreen::onEvent(XEvent* xevent) } } + // let screen saver have a go + if (m_screensaver->handleXEvent(xevent)) { + // screen saver handled it + return; + } + + // handle the event ourself switch (xevent->type) { case CreateNotify: if (m_isPrimary) { // select events on new window selectEvents(xevent->xcreatewindow.window); } - return; + break; case MappingNotify: if (XPending(m_display) > 0) { @@ -1122,7 +919,7 @@ CXWindowsScreen::onEvent(XEvent* xevent) if (id != kClipboardEnd) { LOG((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); m_clipboard[id]->lost(xevent->xselectionclear.time); - m_receiver->onGrabClipboard(id); + sendClipboardEvent(getClipboardGrabbedEvent(), id); return; } } @@ -1138,7 +935,7 @@ CXWindowsScreen::onEvent(XEvent* xevent) xevent->xselection.requestor, xevent->xselection.property); } - return; + break; case SelectionRequest: { @@ -1163,17 +960,6 @@ CXWindowsScreen::onEvent(XEvent* xevent) processClipboardRequest(xevent->xproperty.window, xevent->xproperty.time, xevent->xproperty.atom); - return; - } - break; - - case ClientMessage: - if (m_isPrimary && - xevent->xclient.message_type == m_atomScreensaver && - xevent->xclient.format == 32) { - // screen saver activation/deactivation event - m_primaryReceiver->onScreensaver(xevent->xclient.data.l[0] != 0); - return; } break; @@ -1216,9 +1002,6 @@ CXWindowsScreen::onEvent(XEvent* xevent) default: break; } - - // let screen saver have a go - m_screensaver->onPreDispatch(xevent); } void @@ -1245,10 +1028,11 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey) } // handle key - m_primaryReceiver->onKeyDown(key, mask, keycode); + sendEvent(getKeyDownEvent(), new CKeyInfo(key, mask, keycode, 1)); KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); if (m_keyState->isHalfDuplex(keyMask)) { - m_primaryReceiver->onKeyUp(key, mask | keyMask, keycode); + sendEvent(getKeyUpEvent(), + new CKeyInfo(key, mask | keyMask, keycode, 1)); } } } @@ -1304,9 +1088,10 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", keycode, xkey.state)); KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); if (m_keyState->isHalfDuplex(keyMask)) { - m_primaryReceiver->onKeyDown(key, mask, keycode); + sendEvent(getKeyDownEvent(), + new CKeyInfo(key, mask, keycode, 1)); } - m_primaryReceiver->onKeyUp(key, mask, keycode); + sendEvent(getKeyUpEvent(), new CKeyInfo(key, mask, keycode, 1)); } else { // found a press event following so it's a repeat. @@ -1314,7 +1099,7 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) // repeats but we'll just send a repeat of 1. // note that we discard the press event. LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state)); - m_primaryReceiver->onKeyRepeat(key, mask, 1, keycode); + sendEvent(getKeyRepeatEvent(), new CKeyInfo(key, mask, keycode, 1)); } } } @@ -1325,7 +1110,7 @@ CXWindowsScreen::onMousePress(const XButtonEvent& xbutton) LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button)); const ButtonID button = mapButtonFromX(&xbutton); if (button != kButtonNone) { - m_primaryReceiver->onMouseDown(button); + sendEvent(getButtonDownEvent(), new CButtonInfo(button)); } } @@ -1335,15 +1120,15 @@ CXWindowsScreen::onMouseRelease(const XButtonEvent& xbutton) LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button)); const ButtonID button = mapButtonFromX(&xbutton); if (button != kButtonNone) { - m_primaryReceiver->onMouseUp(button); + sendEvent(getButtonUpEvent(), new CButtonInfo(button)); } else if (xbutton.button == 4) { // wheel forward (away from user) - m_primaryReceiver->onMouseWheel(120); + sendEvent(getWheelEvent(), new CWheelInfo(120)); } else if (xbutton.button == 5) { // wheel backward (toward user) - m_primaryReceiver->onMouseWheel(-120); + sendEvent(getWheelEvent(), new CWheelInfo(-120)); } } @@ -1374,7 +1159,8 @@ CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion) } else if (m_isOnScreen) { // motion on primary screen - m_primaryReceiver->onMouseMovePrimary(m_xCursor, m_yCursor); + sendEvent(getMotionOnPrimaryEvent(), + new CMotionInfo(m_xCursor, m_yCursor)); } else { // motion on secondary screen. warp mouse back to @@ -1404,7 +1190,7 @@ CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion) // warping to the primary screen's enter position, // effectively overriding it. if (x != 0 || y != 0) { - m_primaryReceiver->onMouseMoveSecondary(x, y); + sendEvent(getMotionOnSecondaryEvent(), new CMotionInfo(x, y)); } } } @@ -1445,72 +1231,6 @@ CXWindowsScreen::createBlankCursor() const return cursor; } -bool -CXWindowsScreen::processTimers() -{ - bool oneShot = false; - std::vector jobs; - { - CLock lock(&m_timersMutex); - - // get current time - const double time = m_time.getTime(); - - // done if no timers have expired - if ((m_oneShotTimer == NULL || *m_oneShotTimer > time) && - (m_timers.empty() || m_timers.top() > time)) { - return false; - } - - // handle one shot timers - if (m_oneShotTimer != NULL) { - *m_oneShotTimer -= time; - if (*m_oneShotTimer <= 0.0) { - delete m_oneShotTimer; - m_oneShotTimer = NULL; - oneShot = true; - } - } - - // subtract current time from all timers. note that this won't - // change the order of elements in the priority queue (except - // for floating point round off which we'll ignore). - for (CTimerPriorityQueue::iterator index = m_timers.begin(); - index != m_timers.end(); ++index) { - (*index) -= time; - } - - // process all timers at or below zero, saving the jobs - if (!m_timers.empty()) { - while (m_timers.top() <= 0.0) { - CTimer timer = m_timers.top(); - jobs.push_back(timer.getJob()); - timer.reset(); - m_timers.pop(); - m_timers.push(timer); - } - } - - // reset the clock - m_time.reset(); - } - - // now notify of the one shot timers - if (oneShot) { - m_mutex.unlock(); - m_primaryReceiver->onOneShotTimerExpired(0); - m_mutex.lock(); - } - - // now run the jobs. note that if one of these jobs removes - // a timer later in the jobs list and deletes that job pointer - // then this will crash when it tries to run that job. - for (std::vector::iterator index = jobs.begin(); - index != jobs.end(); ++index) { - (*index)->run(); - } -} - ClipboardID CXWindowsScreen::getClipboardID(Atom selection) const { @@ -1548,20 +1268,38 @@ CXWindowsScreen::destroyClipboardRequest(Window requestor) } } +void +CXWindowsScreen::onError() +{ + // prevent further access to the X display + EVENTQUEUE->adoptBuffer(NULL); + m_screensaver->destroy(); + m_screensaver = NULL; + m_display = NULL; + + // notify of failure + sendEvent(getErrorEvent(), NULL); + + // FIXME -- should ensure that we ignore operations that involve + // m_display from now on. however, Xlib will simply exit the + // application in response to the X I/O error so there's no + // point in trying to really handle the error. if we did want + // to handle the error, it'd probably be easiest to delegate to + // one of two objects. one object would take the implementation + // from this class. the other object would be stub methods that + // don't use X11. on error, we'd switch to the latter. +} + int CXWindowsScreen::ioErrorHandler(Display*) { // the display has disconnected, probably because X is shutting - // down. X forces us to exit at this point. that's arguably - // a flaw in X but, realistically, it's difficult to gracefully - // handle not having a Display* anymore. we'll simply log the - // error, notify the subclass (which must not use the display - // so we set it to NULL), and exit. - LOG((CLOG_WARN "X display has unexpectedly disconnected")); - s_screen->m_display = NULL; - s_screen->m_receiver->onError(); - LOG((CLOG_CRIT "quiting due to X display disconnection")); - exit(17); + // down. X forces us to exit at this point which is annoying. + // we'll pretend as if we won't exit so we try to make sure we + // don't access the display anymore. + LOG((CLOG_CRIT "X display has unexpectedly disconnected")); + s_screen->onError(); + return 0; } void @@ -1854,56 +1592,3 @@ CXWindowsScreen::grabMouseAndKeyboard() LOG((CLOG_DEBUG1 "grabbed pointer and keyboard")); return true; } - - -// -// CXWindowsScreen::CTimer -// - -CXWindowsScreen::CTimer::CTimer(IJob* job, double startTime, double resetTime) : - m_job(job), - m_timeout(resetTime), - m_time(resetTime), - m_startTime(startTime) -{ - assert(m_timeout > 0.0); -} - -CXWindowsScreen::CTimer::~CTimer() -{ - // do nothing -} - -void -CXWindowsScreen::CTimer::run() -{ - if (m_job != NULL) { - m_job->run(); - } -} - -void -CXWindowsScreen::CTimer::reset() -{ - m_time = m_timeout; - m_startTime = 0.0; -} - -CXWindowsScreen::CTimer::CTimer& -CXWindowsScreen::CTimer::operator-=(double dt) -{ - m_time -= dt - m_startTime; - m_startTime = 0.0; - return *this; -} - -CXWindowsScreen::CTimer::operator double() const -{ - return m_time; -} - -bool -CXWindowsScreen::CTimer::operator<(const CTimer& t) const -{ - return m_time < t.m_time; -} diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index 36d5be4b..65ecfcee 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -17,9 +17,6 @@ #include "IPlatformScreen.h" #include "CXWindowsKeyMapper.h" -#include "CMutex.h" -#include "CStopwatch.h" -#include "CPriorityQueue.h" #include "stdvector.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy @@ -29,43 +26,22 @@ class CXWindowsClipboard; class CXWindowsScreenSaver; -class IJob; -class IScreenReceiver; -class IPrimaryScreenReceiver; //! Implementation of IPlatformScreen for X11 class CXWindowsScreen : public IPlatformScreen { public: - CXWindowsScreen(IScreenReceiver*, IPrimaryScreenReceiver*); + CXWindowsScreen(bool isPrimary); virtual ~CXWindowsScreen(); //! @name manipulators //@{ - //! Add timer - /*! - Add a job to invoke every timeout seconds. The job is called - with the display locked. If a job timeout expires twice or - more before the job can be called then the job is called just - once. The caller retains ownership of the job. - */ - void addTimer(IJob*, double timeout); - - //! Remove timer - /*! - Remove a job. The caller retains ownership of the job. - */ - void removeTimer(IJob*); - //@} // IPlatformScreen overrides - virtual void open(IKeyState*); - virtual void close(); + virtual void setKeyState(IKeyState*); virtual void enable(); virtual void disable(); - virtual void mainLoop(); - virtual void exitMainLoop(); virtual void enter(); virtual bool leave(); virtual bool setClipboard(ClipboardID, const IClipboard*); @@ -76,17 +52,22 @@ public: virtual void resetOptions(); virtual void setOptions(const COptionsList& options); virtual void updateKeys(); + virtual void setSequenceNumber(UInt32); virtual bool isPrimary() const; - virtual bool getClipboard(ClipboardID, IClipboard*) const; - virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - virtual void getCursorPos(SInt32&, SInt32&) const; + + // IScreen overrides + virtual void* getEventTarget() const; + virtual bool getClipboard(ClipboardID id, IClipboard*) const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); - virtual UInt32 addOneShotTimer(double timeout); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; virtual const char* getKeyName(KeyButton) const; // ISecondaryScreen overrides @@ -101,18 +82,16 @@ public: bool isAutoRepeat) const; private: - // process events before dispatching to receiver - void onEvent(XEvent* event); + // event sending + void sendEvent(CEvent::Type, void* = NULL); + void sendClipboardEvent(CEvent::Type, ClipboardID); + + // event handling + void handleSystemEvent(const CEvent&, void*); // create the transparent cursor Cursor createBlankCursor() const; - // remove a timer without locking - void removeTimerNoLock(IJob*); - - // process timers - bool processTimers(); - // determine the clipboard from the X selection. returns // kClipboardEnd if no such clipboard. ClipboardID getClipboardID(Atom selection) const; @@ -125,40 +104,10 @@ private: void destroyClipboardRequest(Window window); // X I/O error handler + void onError(); static int ioErrorHandler(Display*); private: - // a timer priority queue element - class CTimer { - public: - CTimer(IJob* job, double startTime, double resetTime); - ~CTimer(); - - // manipulators - - void run(); - - void reset(); - - CTimer& operator-=(double); - - // accessors - - IJob* getJob() const - { - return m_job; - } - - operator double() const; - - bool operator<(const CTimer&) const; - - private: - IJob* m_job; - double m_timeout; - double m_time; - double m_startTime; - }; class CKeyEventInfo { public: int m_event; @@ -167,9 +116,9 @@ private: KeyCode m_keycode; }; - bool isQuitEvent(XEvent*) const; - - Window createWindow() const; + Display* openDisplay() const; + void saveShape(); + Window openWindow() const; void openIM(); bool grabMouseAndKeyboard(); @@ -193,21 +142,13 @@ private: static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: - typedef CPriorityQueue CTimerPriorityQueue; - // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; - // X is not thread safe - CMutex m_mutex; - Display* m_display; Window m_root; Window m_window; - IScreenReceiver* m_receiver; - IPrimaryScreenReceiver* m_primaryReceiver; - // true if mouse has entered the screen bool m_isOnScreen; @@ -230,20 +171,11 @@ private: // clipboards CXWindowsClipboard* m_clipboard[kClipboardEnd]; - - // the quit message - Atom m_atomQuit; + UInt32 m_sequenceNumber; // screen saver stuff CXWindowsScreenSaver* m_screensaver; bool m_screensaverNotify; - Atom m_atomScreensaver; - - // timers, the stopwatch used to time, and a mutex for the timers - CTimerPriorityQueue m_timers; - CStopwatch m_time; - CMutex m_timersMutex; - CTimer* m_oneShotTimer; // logical to physical button mapping. m_buttons[i] gives the // physical button for logical button i+1. diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index c652c3d9..a40ae7e4 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -13,10 +13,12 @@ */ #include "CXWindowsScreenSaver.h" -#include "CXWindowsScreen.h" #include "CXWindowsUtil.h" +#include "IPlatformScreen.h" #include "CLog.h" -#include "TMethodJob.h" +#include "CEvent.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" #include #if defined(HAVE_X11_EXTENSIONS_XTEST_H) # include @@ -29,20 +31,16 @@ // CXWindowsScreenSaver::CXWindowsScreenSaver( - CXWindowsScreen* screen, Display* display) : - m_screen(screen), + Display* display, Window window, void* eventTarget) : m_display(display), - m_notify(None), + m_xscreensaverSink(window), + m_eventTarget(eventTarget), m_xscreensaver(None), m_xscreensaverActive(false), m_disabled(false), m_suppressDisable(false), - m_disableJobInstalled(false) + m_disableTimer(NULL) { - // screen saver disable callback - m_disableJob = new TMethodJob(this, - &CXWindowsScreenSaver::disableCallback); - // get atoms m_atomScreenSaver = XInternAtom(m_display, "SCREENSAVER", False); @@ -52,24 +50,6 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( "ACTIVATE", False); m_atomScreenSaverDeactivate = XInternAtom(m_display, "DEACTIVATE", False); - m_atomSynergyScreenSaver = XInternAtom(m_display, - "SYNERGY_SCREENSAVER", False); - - // create dummy window to receive xscreensaver responses. this - // shouldn't be necessary (we should be able to send responses - // to None) but it doesn't hurt. - XSetWindowAttributes attr; - attr.event_mask = 0;//PropertyChangeMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - m_xscreensaverSink = XCreateWindow(m_display, - DefaultRootWindow(m_display), - 0, 0, 1, 1, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect, - &attr); - LOG((CLOG_DEBUG "xscreensaver sink window is 0x%08x", m_xscreensaverSink)); // watch top-level windows for changes { @@ -94,28 +74,39 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( // get the built-in settings XGetScreenSaver(m_display, &m_timeout, &m_interval, &m_preferBlanking, &m_allowExposures); + + // install disable timer event handler + EVENTQUEUE->adoptHandler(CEvent::kTimer, this, + new TMethodEventJob(this, + &CXWindowsScreenSaver::handleDisableTimer)); } CXWindowsScreenSaver::~CXWindowsScreenSaver() { - // clear watch list - clearWatchForXScreenSaver(); - - // stop watching root for events - CXWindowsUtil::CErrorLock lock(m_display); - Window root = DefaultRootWindow(m_display); - XSelectInput(m_display, root, m_rootEventMask); - - // destroy dummy sink window - XDestroyWindow(m_display, m_xscreensaverSink); - // done with disable job - m_screen->removeTimer(m_disableJob); - delete m_disableJob; + if (m_disableTimer != NULL) { + EVENTQUEUE->deleteTimer(m_disableTimer); + } + EVENTQUEUE->removeHandler(CEvent::kTimer, this); + + if (m_display != NULL) { + XSetScreenSaver(m_display, m_timeout, m_interval, + m_preferBlanking, m_allowExposures); + clearWatchForXScreenSaver(); + CXWindowsUtil::CErrorLock lock(m_display); + XSelectInput(m_display, DefaultRootWindow(m_display), m_rootEventMask); + } +} + +void +CXWindowsScreenSaver::destroy() +{ + m_display = NULL; + delete this; } bool -CXWindowsScreenSaver::onPreDispatch(const XEvent* xevent) +CXWindowsScreenSaver::handleXEvent(const XEvent* xevent) { switch (xevent->type) { case CreateNotify: @@ -175,18 +166,12 @@ CXWindowsScreenSaver::onPreDispatch(const XEvent* xevent) return false; } -void -CXWindowsScreenSaver::setNotify(Window notify) -{ - m_notify = notify; -} - void CXWindowsScreenSaver::enable() { // for xscreensaver m_disabled = false; - updateDisableJob(); + updateDisableTimer(); // for built-in X screen saver XSetScreenSaver(m_display, m_timeout, m_interval, @@ -198,7 +183,7 @@ CXWindowsScreenSaver::disable() { // for xscreensaver m_disabled = true; - updateDisableJob(); + updateDisableTimer(); // use built-in X screen saver XGetScreenSaver(m_display, &m_timeout, &m_interval, @@ -213,7 +198,7 @@ CXWindowsScreenSaver::activate() { // remove disable job timer m_suppressDisable = true; - updateDisableJob(); + updateDisableTimer(); // try xscreensaver findXScreenSaver(); @@ -231,7 +216,7 @@ CXWindowsScreenSaver::deactivate() { // reinstall disable job timer m_suppressDisable = false; - updateDisableJob(); + updateDisableTimer(); // try xscreensaver findXScreenSaver(); @@ -256,27 +241,6 @@ CXWindowsScreenSaver::isActive() const return false; } -void -CXWindowsScreenSaver::sendNotify(bool activated) -{ - if (m_notify != None) { - XEvent event; - event.xclient.type = ClientMessage; - event.xclient.display = m_display; - event.xclient.window = m_notify; - event.xclient.message_type = m_atomSynergyScreenSaver; - event.xclient.format = 32; - event.xclient.data.l[0] = activated ? 1 : 0; - event.xclient.data.l[1] = 0; - event.xclient.data.l[2] = 0; - event.xclient.data.l[3] = 0; - event.xclient.data.l[4] = 0; - - CXWindowsUtil::CErrorLock lock(m_display); - XSendEvent(m_display, m_notify, False, 0, &event); - } -} - bool CXWindowsScreenSaver::findXScreenSaver() { @@ -351,9 +315,18 @@ CXWindowsScreenSaver::setXScreenSaverActive(bool activated) // from activating since that'll just pop up the password // dialog if locking is enabled. m_suppressDisable = activated; - updateDisableJob(); + updateDisableTimer(); - sendNotify(activated); + if (activated) { + EVENTQUEUE->addEvent(CEvent( + IPlatformScreen::getScreensaverActivatedEvent(), + m_eventTarget)); + } + else { + EVENTQUEUE->addEvent(CEvent( + IPlatformScreen::getScreensaverDeactivatedEvent(), + m_eventTarget)); + } } } @@ -441,23 +414,20 @@ CXWindowsScreenSaver::addWatchXScreenSaver(Window window) } void -CXWindowsScreenSaver::updateDisableJob() +CXWindowsScreenSaver::updateDisableTimer() { - assert(m_disableJob != NULL); - - if (m_disabled && !m_suppressDisable && !m_disableJobInstalled) { + if (m_disabled && !m_suppressDisable && m_disableTimer == NULL) { // 5 seconds should be plenty often to suppress the screen saver - m_disableJobInstalled = true; - m_screen->addTimer(m_disableJob, 5.0); + m_disableTimer = EVENTQUEUE->newTimer(5.0, this); } - else if ((!m_disabled || m_suppressDisable) && m_disableJobInstalled) { - m_disableJobInstalled = false; - m_screen->removeTimer(m_disableJob); + else if ((!m_disabled || m_suppressDisable) && m_disableTimer != NULL) { + EVENTQUEUE->deleteTimer(m_disableTimer); + m_disableTimer = NULL; } } void -CXWindowsScreenSaver::disableCallback(void*) +CXWindowsScreenSaver::handleDisableTimer(const CEvent&, void*) { // send fake mouse motion directly to xscreensaver if (m_xscreensaver != None) { diff --git a/lib/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h index 3285a1b7..b956a84b 100644 --- a/lib/platform/CXWindowsScreenSaver.h +++ b/lib/platform/CXWindowsScreenSaver.h @@ -23,16 +23,13 @@ # include #endif -class IJob; -class CXWindowsScreen; +class CEvent; +class CEventQueueTimer; //! X11 screen saver implementation class CXWindowsScreenSaver : public IScreenSaver { public: - // note -- the caller must ensure that Display* passed to c'tor isn't - // being used in another call to Xlib when calling any method on this - // object (including during the c'tor and d'tor). - CXWindowsScreenSaver(CXWindowsScreen*, Display*); + CXWindowsScreenSaver(Display*, Window, void* eventTarget); virtual ~CXWindowsScreenSaver(); //! @name manipulators @@ -40,22 +37,17 @@ public: //! Event filtering /*! - Called for each event before event translation and dispatch. Return - true to skip translation and dispatch. Subclasses should call the - superclass's version first and return true if it returns true. + Should be called for each system event before event translation and + dispatch. Returns true to skip translation and dispatch. */ - bool onPreDispatch(const XEvent*); + bool handleXEvent(const XEvent*); - //! Set notify target + //! Destroy without the display /*! - Tells this object to send a ClientMessage to the given window - when the screen saver activates or deactivates. Only one window - can be notified at a time. The message type is the "SCREENSAVER" - atom, the format is 32, and the data.l[0] member is non-zero - if activated, zero if deactivated. Pass None to disable - notification. + Tells this object to delete itself without using the X11 display. + It may leak some resources as a result. */ - void setNotify(Window); + void destroy(); //@} @@ -67,9 +59,6 @@ public: virtual bool isActive() const; private: - // send a notification - void sendNotify(bool activated); - // find and set the running xscreensaver's window. returns true iff // found. bool findXScreenSaver(); @@ -98,25 +87,22 @@ private: void addWatchXScreenSaver(Window window); // install/uninstall the job used to suppress the screensaver - void updateDisableJob(); + void updateDisableTimer(); // called periodically to prevent the screen saver from starting - void disableCallback(void*); + void handleDisableTimer(const CEvent&, void*); private: typedef std::map CWatchList; - // the event loop object - CXWindowsScreen* m_screen; - // the X display Display* m_display; - // old event mask on root window - long m_rootEventMask; + // window to receive xscreensaver repsonses + Window m_xscreensaverSink; - // window to notify on screen saver activation/deactivation - Window m_notify; + // the target for the events we generate + void* m_eventTarget; // xscreensaver's window Window m_xscreensaver; @@ -124,8 +110,8 @@ private: // xscreensaver activation state bool m_xscreensaverActive; - // dummy window to receive xscreensaver repsonses - Window m_xscreensaverSink; + // old event mask on root window + long m_rootEventMask; // potential xscreensaver windows being watched CWatchList m_watchWindows; @@ -135,7 +121,6 @@ private: Atom m_atomScreenSaverVersion; Atom m_atomScreenSaverActivate; Atom m_atomScreenSaverDeactivate; - Atom m_atomSynergyScreenSaver; // built-in screen saver settings int m_timeout; @@ -151,11 +136,8 @@ private: // to activate the screen saver even if disabled. bool m_suppressDisable; - // true iff the disabled job timer is installed - bool m_disableJobInstalled; - - // the job used to invoke disableCallback - IJob* m_disableJob; + // the disable timer (NULL if not installed) + CEventQueueTimer* m_disableTimer; }; #endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 25629cc4..ec33a35a 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -49,7 +49,7 @@ libplatform_a_SOURCES = \ CXWindowsClipboardTextConverter.cpp \ CXWindowsClipboardUCS2Converter.cpp \ CXWindowsClipboardUTF8Converter.cpp \ - CXWindowsEventQueue.cpp \ + CXWindowsEventQueueBuffer.cpp \ CXWindowsKeyMapper.cpp \ CXWindowsScreen.cpp \ CXWindowsScreenSaver.cpp \ @@ -58,7 +58,7 @@ libplatform_a_SOURCES = \ CXWindowsClipboardTextConverter.h \ CXWindowsClipboardUCS2Converter.h \ CXWindowsClipboardUTF8Converter.h \ - CXWindowsEventQueue.h \ + CXWindowsEventQueueBuffer.h \ CXWindowsKeyMapper.h \ CXWindowsScreen.h \ CXWindowsScreenSaver.h \ diff --git a/lib/server/CClientListener.cpp b/lib/server/CClientListener.cpp new file mode 100644 index 00000000..9a811a36 --- /dev/null +++ b/lib/server/CClientListener.cpp @@ -0,0 +1,189 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientListener.h" +#include "CClientProxy.h" +#include "CClientProxyUnknown.h" +#include "CPacketStreamFilter.h" +#include "IStreamFilterFactory.h" +#include "IDataSocket.h" +#include "IListenSocket.h" +#include "ISocketFactory.h" +#include "XSocket.h" +#include "CLog.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" + +// +// CClientListener +// + +CEvent::Type CClientListener::s_connectedEvent = CEvent::kUnknown; + +CClientListener::CClientListener(const CNetworkAddress& address, + ISocketFactory* socketFactory, + IStreamFilterFactory* streamFilterFactory) : + m_socketFactory(socketFactory), + m_streamFilterFactory(streamFilterFactory) +{ + assert(m_socketFactory != NULL); + + try { + // create listen socket + m_listen = m_socketFactory->createListen(); + + // bind listen address + LOG((CLOG_DEBUG1 "binding listen socket")); + m_listen->bind(address); + } + catch (XSocketAddressInUse& e) { + delete m_listen; + delete m_socketFactory; + delete m_streamFilterFactory; + throw; + } + catch (XBase& e) { + delete m_listen; + delete m_socketFactory; + delete m_streamFilterFactory; + throw; + } + LOG((CLOG_DEBUG1 "listening for clients")); + + // setup event handler + EVENTQUEUE->adoptHandler(IListenSocket::getConnectingEvent(), m_listen, + new TMethodEventJob(this, + &CClientListener::handleClientConnecting)); +} + +CClientListener::~CClientListener() +{ + LOG((CLOG_DEBUG1 "stop listening for clients")); + + // discard already connected clients + for (CNewClients::iterator index = m_newClients.begin(); + index != m_newClients.end(); ++index) { + CClientProxyUnknown* client = *index; + EVENTQUEUE->removeHandler(client); + delete client; + } + + // discard waiting clients + CClientProxy* client = getNextClient(); + while (client != NULL) { + delete client; + client = getNextClient(); + } + + EVENTQUEUE->removeHandler(IListenSocket::getConnectingEvent(), m_listen); + delete m_listen; + delete m_socketFactory; + delete m_streamFilterFactory; +} + +CClientProxy* +CClientListener::getNextClient() +{ + CClientProxy* client = NULL; + if (!m_waitingClients.empty()) { + client = m_waitingClients.front(); + m_waitingClients.pop_front(); + EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); + } + return client; +} + +CEvent::Type +CClientListener::getConnectedEvent() +{ + return CEvent::registerTypeOnce(s_connectedEvent, + "CClientListener::connected"); +} + +void +CClientListener::handleClientConnecting(const CEvent&, void*) +{ + // accept client connection + IStream* stream = m_listen->accept(); + if (stream == NULL) { + return; + } + LOG((CLOG_NOTE "accepted client connection")); + + // filter socket messages, including a packetizing filter + if (m_streamFilterFactory != NULL) { + stream = m_streamFilterFactory->create(stream, true); + } + stream = new CPacketStreamFilter(stream, true); + + // create proxy for unknown client + CClientProxyUnknown* client = new CClientProxyUnknown(stream, 30.0); + m_newClients.insert(client); + + // watch for events from unknown client + EVENTQUEUE->adoptHandler(CClientProxyUnknown::getSuccessEvent(), client, + new TMethodEventJob(this, + &CClientListener::handleUnknownClient, client)); + EVENTQUEUE->adoptHandler(CClientProxyUnknown::getFailureEvent(), client, + new TMethodEventJob(this, + &CClientListener::handleUnknownClient, client)); +} + +void +CClientListener::handleUnknownClient(const CEvent&, void* vclient) +{ + CClientProxyUnknown* unknownClient = + reinterpret_cast(vclient); + + // we should have the client in our new client list + assert(m_newClients.count(unknownClient) == 1); + + // get the real client proxy and install it + CClientProxy* client = unknownClient->orphanClientProxy(); + if (client != NULL) { + // handshake was successful + m_waitingClients.push_back(client); + EVENTQUEUE->addEvent(CEvent(getConnectedEvent(), this)); + + // watch for client to disconnect while it's in our queue + EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client, + new TMethodEventJob(this, + &CClientListener::handleClientDisconnected, + client)); + } + + // now finished with unknown client + EVENTQUEUE->removeHandler(CClientProxyUnknown::getSuccessEvent(), client); + EVENTQUEUE->removeHandler(CClientProxyUnknown::getFailureEvent(), client); + m_newClients.erase(unknownClient); + delete unknownClient; +} + +void +CClientListener::handleClientDisconnected(const CEvent&, void* vclient) +{ + CClientProxy* client = reinterpret_cast(vclient); + + // find client in waiting clients queue + for (CWaitingClients::iterator i = m_waitingClients.begin(), + n = m_waitingClients.end(); i != n; ++i) { + if (*i == client) { + m_waitingClients.erase(i); + EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), + client); + delete client; + break; + } + } +} diff --git a/lib/server/CClientListener.h b/lib/server/CClientListener.h new file mode 100644 index 00000000..e5302e69 --- /dev/null +++ b/lib/server/CClientListener.h @@ -0,0 +1,76 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTLISTENER_H +#define CCLIENTLISTENER_H + +#include "CConfig.h" +#include "CEvent.h" +#include "stddeque.h" +#include "stdset.h" + +class CClientProxy; +class CClientProxyUnknown; +class CNetworkAddress; +class IListenSocket; +class ISocketFactory; +class IStreamFilterFactory; + +class CClientListener { +public: + // The factories are adopted. + CClientListener(const CNetworkAddress&, + ISocketFactory*, IStreamFilterFactory*); + ~CClientListener(); + + //! @name accessors + //@{ + + //! Get next connected client + /*! + Returns the next connected client and removes it from the internal + list. The client is responsible for deleting the returned client. + Returns NULL if no clients are available. + */ + CClientProxy* getNextClient(); + + //! Get connected event type + /*! + Returns the connected event type. This is sent whenever a + a client connects. + */ + static CEvent::Type getConnectedEvent(); + + //@} + +private: + // client connection event handlers + void handleClientConnecting(const CEvent&, void*); + void handleUnknownClient(const CEvent&, void*); + void handleClientDisconnected(const CEvent&, void*); + +private: + typedef std::set CNewClients; + typedef std::deque CWaitingClients; + + IListenSocket* m_listen; + ISocketFactory* m_socketFactory; + IStreamFilterFactory* m_streamFilterFactory; + CNewClients m_newClients; + CWaitingClients m_waitingClients; + + static CEvent::Type s_connectedEvent; +}; + +#endif diff --git a/lib/server/CClientProxy.cpp b/lib/server/CClientProxy.cpp index 0df174c9..e5dc02c0 100644 --- a/lib/server/CClientProxy.cpp +++ b/lib/server/CClientProxy.cpp @@ -13,15 +13,18 @@ */ #include "CClientProxy.h" +#include "CProtocolUtil.h" #include "IStream.h" +#include "CLog.h" // // CClientProxy // -CClientProxy::CClientProxy(IServer* server, - const CString& name, IStream* stream) : - m_server(server), +CEvent::Type CClientProxy::s_readyEvent = CEvent::kUnknown; +CEvent::Type CClientProxy::s_disconnectedEvent = CEvent::kUnknown; + +CClientProxy::CClientProxy(const CString& name, IStream* stream) : m_name(name), m_stream(stream) { @@ -33,10 +36,14 @@ CClientProxy::~CClientProxy() delete m_stream; } -IServer* -CClientProxy::getServer() const +void +CClientProxy::close(const char* msg) { - return m_server; + LOG((CLOG_DEBUG1 "send close \"%s\" to \"%s\"", msg, getName().c_str())); + CProtocolUtil::writef(getStream(), msg); + + // force the close to be sent before we return + getStream()->flush(); } IStream* @@ -51,8 +58,22 @@ CClientProxy::getName() const return m_name; } -const CMutex* -CClientProxy::getMutex() const +CEvent::Type +CClientProxy::getReadyEvent() { - return &m_mutex; + return CEvent::registerTypeOnce(s_readyEvent, + "CClientProxy::ready"); +} + +CEvent::Type +CClientProxy::getDisconnectedEvent() +{ + return CEvent::registerTypeOnce(s_disconnectedEvent, + "CClientProxy::disconnected"); +} + +void* +CClientProxy::getEventTarget() const +{ + return static_cast(const_cast(this)); } diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index 751442ae..aad57f6a 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -16,11 +16,10 @@ #define CCLIENTPROXY_H #include "IClient.h" -#include "CMutex.h" +#include "CEvent.h" #include "CString.h" class IStream; -class IServer; //! Generic proxy for client class CClientProxy : public IClient { @@ -28,17 +27,21 @@ public: /*! \c name is the name of the client. */ - CClientProxy(IServer* server, const CString& name, IStream* adoptedStream); + CClientProxy(const CString& name, IStream* adoptedStream); ~CClientProxy(); - //! @name accessors + //! @name manipulators //@{ - //! Get server + //! Disconnect /*! - Returns the server passed to the c'tor. + Ask the client to disconnect, using \p msg as the reason. */ - IServer* getServer() const; + void close(const char* msg); + + //@} + //! @name accessors + //@{ //! Get stream /*! @@ -46,12 +49,31 @@ public: */ IStream* getStream() const; + //! Get ready event type + /*! + Returns the ready event type. This is sent when the client has + completed the initial handshake. Until it is sent, the client is + not fully connected. + */ + static CEvent::Type getReadyEvent(); + + //! Get disconnect event type + /*! + Returns the disconnect event type. This is sent when the client + disconnects or is disconnected. The target is getEventTarget(). + */ + static CEvent::Type getDisconnectedEvent(); + //@} + // IScreen + virtual void* getEventTarget() const; + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + // IClient overrides - virtual void open() = 0; - virtual void mainLoop() = 0; - virtual void close() = 0; virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, bool forScreensaver) = 0; @@ -71,25 +93,13 @@ public: virtual void resetOptions() = 0; virtual void setOptions(const COptionsList& options) = 0; virtual CString getName() const; - virtual SInt32 getJumpZoneSize() const = 0; - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const = 0; - virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; - virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; - -protected: - //! Get mutex - /*! - Returns the mutex for this object. Subclasses should use this - mutex to protect their data. - */ - const CMutex* getMutex() const; private: - CMutex m_mutex; - IServer* m_server; CString m_name; IStream* m_stream; + + static CEvent::Type s_readyEvent; + static CEvent::Type s_disconnectedEvent; }; #endif diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index ccf5b357..505eacfa 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -13,12 +13,9 @@ */ #include "CClientProxy1_0.h" -#include "CServer.h" -#include "CClipboard.h" #include "CProtocolUtil.h" #include "XSynergy.h" #include "IStream.h" -#include "CLock.h" #include "CLog.h" #include "IEventQueue.h" #include "TMethodEventJob.h" @@ -28,16 +25,12 @@ // CClientProxy1_0 // -CClientProxy1_0::CClientProxy1_0(IServer* server, - const CString& name, IStream* stream) : - CClientProxy(server, name, stream), +CClientProxy1_0::CClientProxy1_0(const CString& name, IStream* stream) : + CClientProxy(name, stream), m_heartbeatAlarm(kHeartRate * kHeartBeatsUntilDeath), - m_heartbeatTimer(NULL) + m_heartbeatTimer(NULL), + m_parser(&CClientProxy1_0::parseHandshakeMessage) { - for (UInt32 i = 0; i < kClipboardEnd; ++i) { - m_clipboardDirty[i] = true; - } - // install event handlers EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(), stream->getEventTarget(), @@ -59,7 +52,8 @@ CClientProxy1_0::CClientProxy1_0(IServer* server, new TMethodEventJob(this, &CClientProxy1_0::handleFlatline, NULL)); - // FIXME -- open() replacement must install initial heartbeat timer + LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str())); + CProtocolUtil::writef(getStream(), kMsgQInfo); } CClientProxy1_0::~CClientProxy1_0() @@ -70,11 +64,9 @@ CClientProxy1_0::~CClientProxy1_0() void CClientProxy1_0::disconnect() { - CLock lock(getMutex()); removeHandlers(); - // FIXME -- send disconnect event (server should be listening for this) - getStream()->flush(); getStream()->close(); + EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), getEventTarget())); } void @@ -98,7 +90,6 @@ CClientProxy1_0::removeHandlers() void CClientProxy1_0::addHeartbeatTimer() { - CLock lock(getMutex()); if (m_heartbeatAlarm > 0.0) { m_heartbeatTimer = EVENTQUEUE->newOneShotTimer(m_heartbeatAlarm, this); } @@ -107,7 +98,6 @@ CClientProxy1_0::addHeartbeatTimer() void CClientProxy1_0::removeHeartbeatTimer() { - CLock lock(getMutex()); if (m_heartbeatTimer != NULL) { EVENTQUEUE->deleteTimer(m_heartbeatTimer); m_heartbeatTimer = NULL; @@ -130,7 +120,7 @@ CClientProxy1_0::handleData(const CEvent&, void*) // parse message LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); - if (!parseMessage(code)) { + if (!(this->*m_parser)(code)) { LOG((CLOG_ERR "invalid message from client \"%s\"", getName().c_str())); disconnect(); return; @@ -145,11 +135,35 @@ CClientProxy1_0::handleData(const CEvent&, void*) addHeartbeatTimer(); } +bool +CClientProxy1_0::parseHandshakeMessage(const UInt8* code) +{ + if (memcmp(code, kMsgCNoop, 4) == 0) { + // discard no-ops + LOG((CLOG_DEBUG2 "no-op from", getName().c_str())); + return true; + } + else if (memcmp(code, kMsgDInfo, 4) == 0) { + // future messages get parsed by parseMessage + m_parser = &CClientProxy1_0::parseMessage; + if (recvInfo()) { + EVENTQUEUE->addEvent(CEvent(getReadyEvent(), getEventTarget())); + return true; + } + } + return false; +} + bool CClientProxy1_0::parseMessage(const UInt8* code) { if (memcmp(code, kMsgDInfo, 4) == 0) { - return recvInfo(true); + if (recvInfo()) { + EVENTQUEUE->addEvent( + CEvent(getShapeChangedEvent(), getEventTarget())); + return true; + } + return false; } else if (memcmp(code, kMsgCNoop, 4) == 0) { // discard no-ops @@ -168,14 +182,14 @@ CClientProxy1_0::parseMessage(const UInt8* code) void CClientProxy1_0::handleDisconnect(const CEvent&, void*) { - LOG((CLOG_NOTE "client \"%s\" disconnected", getName().c_str())); + LOG((CLOG_NOTE "client \"%s\" has disconnected", getName().c_str())); disconnect(); } void CClientProxy1_0::handleWriteError(const CEvent&, void*) { - LOG((CLOG_ERR "error writing to client \"%s\"", getName().c_str())); + LOG((CLOG_WARN "error writing to client \"%s\"", getName().c_str())); disconnect(); } @@ -187,44 +201,28 @@ CClientProxy1_0::handleFlatline(const CEvent&, void*) disconnect(); } -// FIXME -- replace this -void -CClientProxy1_0::open() +bool +CClientProxy1_0::getClipboard(ClipboardID id, IClipboard* clipboard) const { - // send request - LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str())); - CProtocolUtil::writef(getStream(), kMsgQInfo); - getStream()->flush(); - - // wait for and verify reply - UInt8 code[4]; - for (;;) { - UInt32 n = getStream()->read(code, 4); - if (n == 4) { - if (memcmp(code, kMsgCNoop, 4) == 0) { - // discard heartbeats - continue; - } - if (memcmp(code, kMsgDInfo, 4) == 0) { - break; - } - } - throw XBadClient(); - } - - // handle reply - recvInfo(false); + CClipboard::copy(clipboard, &m_clipboard[id].m_clipboard); + return true; } -// FIXME -- replace this void -CClientProxy1_0::close() +CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { - LOG((CLOG_DEBUG1 "send close to \"%s\"", getName().c_str())); - CProtocolUtil::writef(getStream(), kMsgCClose); + x = m_info.m_x; + y = m_info.m_y; + w = m_info.m_w; + h = m_info.m_h; +} - // force the close to be sent before we return - getStream()->flush(); +void +CClientProxy1_0::getCursorPos(SInt32& x, SInt32& y) const +{ + assert(0 && "shouldn't be called"); + x = m_info.m_mx; + y = m_info.m_my; } void @@ -250,10 +248,9 @@ void CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) { // ignore if this clipboard is already clean - CLock lock(getMutex()); - if (m_clipboardDirty[id]) { + if (m_clipboard[id].m_dirty) { // this clipboard is now clean - m_clipboardDirty[id] = false; + m_clipboard[id].m_dirty = false; LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); CProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data); @@ -267,15 +264,13 @@ CClientProxy1_0::grabClipboard(ClipboardID id) CProtocolUtil::writef(getStream(), kMsgCClipboard, id, 0); // this clipboard is now dirty - CLock lock(getMutex()); - m_clipboardDirty[id] = true; + m_clipboard[id].m_dirty = true; } void CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty) { - CLock lock(getMutex()); - m_clipboardDirty[id] = dirty; + m_clipboard[id].m_dirty = dirty; } void @@ -342,7 +337,6 @@ CClientProxy1_0::resetOptions() CProtocolUtil::writef(getStream(), kMsgCResetOptions); // reset heart rate and death - CLock lock(getMutex()); m_heartbeatAlarm = kHeartRate * kHeartBeatsUntilDeath; removeHeartbeatTimer(); addHeartbeatTimer(); @@ -355,7 +349,6 @@ CClientProxy1_0::setOptions(const COptionsList& options) CProtocolUtil::writef(getStream(), kMsgDSetOptions, &options); // check options - CLock lock(getMutex()); for (UInt32 i = 0, n = options.size(); i < n; i += 2) { if (options[i] == kOptionHeartbeat) { double rate = 1.0e-3 * static_cast(options[i + 1]); @@ -369,73 +362,33 @@ CClientProxy1_0::setOptions(const COptionsList& options) } } -SInt32 -CClientProxy1_0::getJumpZoneSize() const -{ - CLock lock(getMutex()); - return m_info.m_zoneSize; -} - -void -CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - CLock lock(getMutex()); - x = m_info.m_x; - y = m_info.m_y; - w = m_info.m_w; - h = m_info.m_h; -} - -void -CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const -{ - assert(0 && "shouldn't be called"); -} - -void -CClientProxy1_0::getCursorCenter(SInt32& x, SInt32& y) const -{ - CLock lock(getMutex()); - x = m_info.m_mx; - y = m_info.m_my; -} - bool -CClientProxy1_0::recvInfo(bool notify) +CClientProxy1_0::recvInfo() { - { - CLock lock(getMutex()); + // parse the message + SInt16 x, y, w, h, zoneSize, mx, my; + if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4, + &x, &y, &w, &h, &zoneSize, &mx, &my)) { + return false; + } + LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my)); - // parse the message - SInt16 x, y, w, h, zoneSize, mx, my; - if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4, - &x, &y, &w, &h, &zoneSize, &mx, &my)) { - return false; - } - LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my)); - - // validate - if (w <= 0 || h <= 0 || zoneSize < 0) { - return false; - } - if (mx < x || my < y || mx >= x + w || my >= y + h) { - return false; - } - - // save - m_info.m_x = x; - m_info.m_y = y; - m_info.m_w = w; - m_info.m_h = h; - m_info.m_zoneSize = zoneSize; - m_info.m_mx = mx; - m_info.m_my = my; + // validate + if (w <= 0 || h <= 0 || zoneSize < 0) { + return false; + } + if (mx < x || my < y || mx >= x + w || my >= y + h) { + return false; } - // tell server of change - if (notify) { - getServer()->onInfoChanged(getName(), m_info); - } + // save + m_info.m_x = x; + m_info.m_y = y; + m_info.m_w = w; + m_info.m_h = h; + m_info.m_zoneSize = zoneSize; + m_info.m_mx = mx; + m_info.m_my = my; // acknowledge receipt LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); @@ -461,9 +414,17 @@ CClientProxy1_0::recvClipboard() return false; } - // send update. this calls us back to reset our clipboard dirty flag - // so don't hold a lock during the call. - getServer()->onClipboardChanged(id, seqNum, data); + // save clipboard + m_clipboard[id].m_clipboard.unmarshall(data, 0); + m_clipboard[id].m_sequenceNumber = seqNum; + + // notify + CClipboardInfo* info = new CClipboardInfo; + info->m_id = id; + info->m_sequenceNumber = seqNum; + EVENTQUEUE->addEvent(CEvent(getClipboardChangedEvent(), + getEventTarget(), info)); + return true; } @@ -483,8 +444,25 @@ CClientProxy1_0::recvGrabClipboard() return false; } - // send update. this calls us back to reset our clipboard dirty flag - // so don't hold a lock during the call. - getServer()->onGrabClipboard(getName(), id, seqNum); + // notify + CClipboardInfo* info = new CClipboardInfo; + info->m_id = id; + info->m_sequenceNumber = seqNum; + EVENTQUEUE->addEvent(CEvent(getClipboardGrabbedEvent(), + getEventTarget(), info)); + return true; } + + +// +// CClientProxy1_0::CClientClipboard +// + +CClientProxy1_0::CClientClipboard::CClientClipboard() : + m_clipboard(), + m_sequenceNumber(0), + m_dirty(true) +{ + // do nothing +} diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 175f3c37..26584e97 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -16,6 +16,7 @@ #define CCLIENTPROXY1_0_H #include "CClientProxy.h" +#include "CClipboard.h" #include "ProtocolTypes.h" class CEvent; @@ -24,14 +25,16 @@ class CEventQueueTimer; //! Proxy for client implementing protocol version 1.0 class CClientProxy1_0 : public CClientProxy { public: - CClientProxy1_0(IServer* server, const CString& name, - IStream* adoptedStream); + CClientProxy1_0(const CString& name, IStream* adoptedStream); ~CClientProxy1_0(); + // IScreen + virtual bool getClipboard(ClipboardID id, IClipboard*) const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + // IClient overrides - virtual void open(); - virtual void mainLoop(); - virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, bool forScreensaver); @@ -50,13 +53,9 @@ public: virtual void screensaver(bool activate); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); - virtual SInt32 getJumpZoneSize() const; - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const; - virtual void getCursorPos(SInt32& x, SInt32& y) const; - virtual void getCursorCenter(SInt32& x, SInt32& y) const; protected: + virtual bool parseHandshakeMessage(const UInt8* code); virtual bool parseMessage(const UInt8* code); private: @@ -70,15 +69,27 @@ private: void handleWriteError(const CEvent&, void*); void handleFlatline(const CEvent&, void*); - bool recvInfo(bool notify); + bool recvInfo(); bool recvClipboard(); bool recvGrabClipboard(); private: + typedef bool (CClientProxy1_0::*MessageParser)(const UInt8*); + struct CClientClipboard { + public: + CClientClipboard(); + + public: + CClipboard m_clipboard; + UInt32 m_sequenceNumber; + bool m_dirty; + }; + CClientInfo m_info; - bool m_clipboardDirty[kClipboardEnd]; + CClientClipboard m_clipboard[kClipboardEnd]; double m_heartbeatAlarm; CEventQueueTimer* m_heartbeatTimer; + MessageParser m_parser; }; #endif diff --git a/lib/server/CClientProxy1_1.cpp b/lib/server/CClientProxy1_1.cpp index b5d2cf21..ccce9305 100644 --- a/lib/server/CClientProxy1_1.cpp +++ b/lib/server/CClientProxy1_1.cpp @@ -21,9 +21,8 @@ // CClientProxy1_1 // -CClientProxy1_1::CClientProxy1_1(IServer* server, - const CString& name, IStream* stream) : - CClientProxy1_0(server, name, stream) +CClientProxy1_1::CClientProxy1_1(const CString& name, IStream* stream) : + CClientProxy1_0(name, stream) { // do nothing } diff --git a/lib/server/CClientProxy1_1.h b/lib/server/CClientProxy1_1.h index 5e21f21b..08764829 100644 --- a/lib/server/CClientProxy1_1.h +++ b/lib/server/CClientProxy1_1.h @@ -20,8 +20,7 @@ //! Proxy for client implementing protocol version 1.1 class CClientProxy1_1 : public CClientProxy1_0 { public: - CClientProxy1_1(IServer* server, const CString& name, - IStream* adoptedStream); + CClientProxy1_1(const CString& name, IStream* adoptedStream); ~CClientProxy1_1(); // IClient overrides diff --git a/lib/server/CClientProxyUnknown.cpp b/lib/server/CClientProxyUnknown.cpp new file mode 100644 index 00000000..4c45ba07 --- /dev/null +++ b/lib/server/CClientProxyUnknown.cpp @@ -0,0 +1,277 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientProxyUnknown.h" +#include "CClientProxy1_0.h" +#include "CClientProxy1_1.h" +#include "ProtocolTypes.h" +#include "CProtocolUtil.h" +#include "XSynergy.h" +#include "IStream.h" +#include "XIO.h" +#include "CLog.h" +#include "CString.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" + +// +// CClientProxyUnknown +// + +CEvent::Type CClientProxyUnknown::s_successEvent = CEvent::kUnknown; +CEvent::Type CClientProxyUnknown::s_failureEvent = CEvent::kUnknown; + +CClientProxyUnknown::CClientProxyUnknown(IStream* stream, double timeout) : + m_stream(stream), + m_proxy(NULL), + m_ready(false) +{ + EVENTQUEUE->adoptHandler(CEvent::kTimer, this, + new TMethodEventJob(this, + &CClientProxyUnknown::handleTimeout, NULL)); + m_timer = EVENTQUEUE->newOneShotTimer(timeout, this); + addStreamHandlers(); + + LOG((CLOG_DEBUG1 "saying hello")); + CProtocolUtil::writef(m_stream, kMsgHello, + kProtocolMajorVersion, + kProtocolMinorVersion); +} + +CClientProxyUnknown::~CClientProxyUnknown() +{ + removeHandlers(); + removeTimer(); + delete m_stream; + delete m_proxy; +} + +CClientProxy* +CClientProxyUnknown::orphanClientProxy() +{ + if (m_ready) { + removeHandlers(); + CClientProxy* proxy = m_proxy; + m_proxy = NULL; + return proxy; + } + else { + return NULL; + } +} + +CEvent::Type +CClientProxyUnknown::getSuccessEvent() +{ + return CEvent::registerTypeOnce(s_successEvent, + "CClientProxy::success"); +} + +CEvent::Type +CClientProxyUnknown::getFailureEvent() +{ + return CEvent::registerTypeOnce(s_failureEvent, + "CClientProxy::failure"); +} + +void +CClientProxyUnknown::sendSuccess() +{ + m_ready = true; + removeTimer(); + EVENTQUEUE->addEvent(CEvent(getSuccessEvent(), this)); +} + +void +CClientProxyUnknown::sendFailure() +{ + delete m_proxy; + m_proxy = NULL; + m_ready = false; + removeHandlers(); + removeTimer(); + EVENTQUEUE->addEvent(CEvent(getFailureEvent(), this)); +} + +void +CClientProxyUnknown::addStreamHandlers() +{ + assert(m_stream != NULL); + + EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClientProxyUnknown::handleData)); + EVENTQUEUE->adoptHandler(IStream::getOutputErrorEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClientProxyUnknown::handleWriteError)); + EVENTQUEUE->adoptHandler(IStream::getInputShutdownEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClientProxyUnknown::handleDisconnect)); + EVENTQUEUE->adoptHandler(IStream::getOutputShutdownEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClientProxyUnknown::handleWriteError)); +} + +void +CClientProxyUnknown::addProxyHandlers() +{ + assert(m_proxy != NULL); + + EVENTQUEUE->adoptHandler(CClientProxy::getReadyEvent(), + m_proxy, + new TMethodEventJob(this, + &CClientProxyUnknown::handleReady)); + EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), + m_proxy, + new TMethodEventJob(this, + &CClientProxyUnknown::handleDisconnect)); +} + +void +CClientProxyUnknown::removeHandlers() +{ + if (m_stream != NULL) { + EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getOutputErrorEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getInputShutdownEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getOutputShutdownEvent(), + m_stream->getEventTarget()); + } + if (m_proxy != NULL) { + EVENTQUEUE->removeHandler(CClientProxy::getReadyEvent(), + m_proxy); + EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), + m_proxy); + } +} + +void +CClientProxyUnknown::removeTimer() +{ + if (m_timer != NULL) { + EVENTQUEUE->deleteTimer(m_timer); + EVENTQUEUE->removeHandler(CEvent::kTimer, this); + m_timer = NULL; + } +} + +void +CClientProxyUnknown::handleData(const CEvent&, void*) +{ + LOG((CLOG_DEBUG1 "parsing hello reply")); + + CString name(""); + try { + // limit the maximum length of the hello + UInt32 n = m_stream->getSize(); + if (n > kMaxHelloLength) { + LOG((CLOG_DEBUG1 "hello reply too long")); + throw XBadClient(); + } + + // parse the reply to hello + SInt16 major, minor; + if (!CProtocolUtil::readf(m_stream, kMsgHelloBack, + &major, &minor, &name)) { + throw XBadClient(); + } + + // disallow invalid version numbers + if (major <= 0 || minor < 0) { + throw XIncompatibleClient(major, minor); + } + + // remove stream event handlers. the proxy we're about to create + // may install its own handlers and we don't want to accidentally + // remove those later. + removeHandlers(); + + // create client proxy for highest version supported by the client + if (major == 1) { + switch (minor) { + case 0: + m_proxy = new CClientProxy1_0(name, m_stream); + break; + + case 1: + m_proxy = new CClientProxy1_1(name, m_stream); + break; + } + } + + // hangup (with error) if version isn't supported + if (m_proxy == NULL) { + throw XIncompatibleClient(major, minor); + } + + // the proxy is created and now proxy now owns the stream + LOG((CLOG_DEBUG1 "created proxy for client \"%s\" version %d.%d", name.c_str(), major, minor)); + m_stream = NULL; + + // wait until the proxy signals that it's ready or has disconnected + addProxyHandlers(); + return; + } + catch (XIncompatibleClient& e) { + // client is incompatible + LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); + CProtocolUtil::writef(m_stream, + kMsgEIncompatible, + kProtocolMajorVersion, kProtocolMinorVersion); + } + catch (XBadClient&) { + // client not behaving + LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); + CProtocolUtil::writef(m_stream, kMsgEBad); + } + catch (XBase& e) { + // misc error + LOG((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); + } + sendFailure(); +} + +void +CClientProxyUnknown::handleWriteError(const CEvent&, void*) +{ + LOG((CLOG_NOTE "error communicating with new client")); + sendFailure(); +} + +void +CClientProxyUnknown::handleTimeout(const CEvent&, void*) +{ + LOG((CLOG_NOTE "new client is unresponsive")); + sendFailure(); +} + +void +CClientProxyUnknown::handleDisconnect(const CEvent&, void*) +{ + LOG((CLOG_NOTE "new client disconnected")); + sendFailure(); +} + +void +CClientProxyUnknown::handleReady(const CEvent&, void*) +{ + sendSuccess(); +} diff --git a/lib/server/CClientProxyUnknown.h b/lib/server/CClientProxyUnknown.h new file mode 100644 index 00000000..140a1aee --- /dev/null +++ b/lib/server/CClientProxyUnknown.h @@ -0,0 +1,83 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTPROXYUNKNOWN_H +#define CCLIENTPROXYUNKNOWN_H + +#include "CEvent.h" + +class CClientProxy; +class CEventQueueTimer; +class IStream; + +class CClientProxyUnknown { +public: + CClientProxyUnknown(IStream* stream, double timeout); + ~CClientProxyUnknown(); + + //! @name manipulators + //@{ + + //! Get the client proxy + /*! + Returns the client proxy created after a successful handshake + (i.e. when this object sends a success event). Returns NULL + if the handshake is unsuccessful or incomplete. + */ + CClientProxy* orphanClientProxy(); + + //@} + //! @name accessors + //@{ + + //! Get success event type + /*! + Returns the success event type. This is sent when the client has + correctly responded to the hello message. The target is this. + */ + static CEvent::Type getSuccessEvent(); + + //! Get failure event type + /*! + Returns the failure event type. This is sent when a client fails + to correctly respond to the hello message. The target is this. + */ + static CEvent::Type getFailureEvent(); + + //@} + +private: + void sendSuccess(); + void sendFailure(); + void addStreamHandlers(); + void addProxyHandlers(); + void removeHandlers(); + void removeTimer(); + void handleData(const CEvent&, void*); + void handleWriteError(const CEvent&, void*); + void handleTimeout(const CEvent&, void*); + void handleDisconnect(const CEvent&, void*); + void handleReady(const CEvent&, void*); + +private: + IStream* m_stream; + CEventQueueTimer* m_timer; + CClientProxy* m_proxy; + bool m_ready; + + static CEvent::Type s_successEvent; + static CEvent::Type s_failureEvent; +}; + +#endif diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index de20f312..0b40a666 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -446,11 +446,9 @@ CConfig::getOptions(const CString& name) const bool CConfig::operator==(const CConfig& x) const { -/* FIXME -- no compare available for CNetworkAddress if (m_synergyAddress != x.m_synergyAddress) { return false; } -*/ if (m_map.size() != x.m_map.size()) { return false; } diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 54a207f1..5281b092 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -14,10 +14,6 @@ #include "CPrimaryClient.h" #include "CScreen.h" -#include "IScreenFactory.h" -#include "IServer.h" -#include "XScreen.h" -#include "XSynergy.h" #include "CClipboard.h" #include "CLog.h" @@ -25,66 +21,31 @@ // CPrimaryClient // -CPrimaryClient::CPrimaryClient(IScreenFactory* screenFactory, - IServer* server, - IPrimaryScreenReceiver* receiver, - const CString& name) : - m_server(server), +CPrimaryClient::CPrimaryClient(const CString& name, CScreen* screen) : m_name(name), - m_seqNum(0) + m_screen(screen) { - assert(m_server != NULL); - - // create screen - LOG((CLOG_DEBUG1 "creating primary screen")); - if (screenFactory != NULL) { - IPlatformScreen* platformScreen = - screenFactory->create(this, receiver); - if (platformScreen != NULL) { - m_screen = new CScreen(platformScreen, this); - } - } - if (m_screen == NULL) { - throw XScreenOpenFailure(); + // all clipboards are clean + for (UInt32 i = 0; i < kClipboardEnd; ++i) { + m_clipboardDirty[i] = false; } } CPrimaryClient::~CPrimaryClient() { - LOG((CLOG_DEBUG1 "destroying primary screen")); delete m_screen; } -void -CPrimaryClient::exitMainLoop() -{ - m_screen->exitMainLoop(); -} - void CPrimaryClient::reconfigure(UInt32 activeSides) { m_screen->reconfigure(activeSides); } -UInt32 -CPrimaryClient::addOneShotTimer(double timeout) -{ - return m_screen->addOneShotTimer(timeout); -} - void -CPrimaryClient::getClipboard(ClipboardID id, CString& data) const +CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const { - CClipboard clipboard; - m_screen->getClipboard(id, &clipboard); - data = clipboard.marshall(); -} - -bool -CPrimaryClient::isLockedToScreen() const -{ - return m_screen->isLockedToScreen(); + m_screen->getCursorCenter(x, y); } KeyModifierMask @@ -93,64 +54,41 @@ CPrimaryClient::getToggleMask() const return m_screen->getActiveModifiers(); } -void -CPrimaryClient::onError() +bool +CPrimaryClient::isLockedToScreen() const { - // forward to server - m_server->onError(); + return m_screen->isLockedToScreen(); } -void -CPrimaryClient::onInfoChanged(const CClientInfo& info) +void* +CPrimaryClient::getEventTarget() const { - m_info = info; - try { - m_server->onInfoChanged(getName(), m_info); - } - catch (XBadClient&) { - // ignore - } + return m_screen->getEventTarget(); } bool -CPrimaryClient::onGrabClipboard(ClipboardID id) +CPrimaryClient::getClipboard(ClipboardID id, IClipboard* clipboard) const { - try { - return m_server->onGrabClipboard(getName(), id, m_seqNum); - } - catch (XBadClient&) { - return false; - } + return m_screen->getClipboard(id, clipboard); +} + +SInt32 +CPrimaryClient::getJumpZoneSize() const +{ + return m_screen->getJumpZoneSize(); } void -CPrimaryClient::onClipboardChanged(ClipboardID id, const CString& data) +CPrimaryClient::getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const { - m_server->onClipboardChanged(id, m_seqNum, data); + m_screen->getShape(x, y, width, height); } void -CPrimaryClient::open() +CPrimaryClient::getCursorPos(SInt32& x, SInt32& y) const { - // all clipboards are clean - for (UInt32 i = 0; i < kClipboardEnd; ++i) { - m_clipboardDirty[i] = false; - } - - // now open the screen - m_screen->open(); -} - -void -CPrimaryClient::mainLoop() -{ - m_screen->mainLoop(); -} - -void -CPrimaryClient::close() -{ - m_screen->close(); + m_screen->getCursorPos(x, y); } void @@ -169,8 +107,7 @@ void CPrimaryClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask, bool screensaver) { - // note -- we must not call any server methods except onError(). - m_seqNum = seqNum; + m_screen->setSequenceNumber(seqNum); if (!screensaver) { m_screen->warpCursor(xAbs, yAbs); } @@ -180,15 +117,12 @@ CPrimaryClient::enter(SInt32 xAbs, SInt32 yAbs, bool CPrimaryClient::leave() { - // note -- we must not call any server methods except onError(). return m_screen->leave(); } void CPrimaryClient::setClipboard(ClipboardID id, const CString& data) { - // note -- we must not call any server methods except onError(). - // ignore if this clipboard is already clean if (m_clipboardDirty[id]) { // this clipboard is now clean @@ -284,31 +218,3 @@ CPrimaryClient::getName() const { return m_name; } - -SInt32 -CPrimaryClient::getJumpZoneSize() const -{ - return m_info.m_zoneSize; -} - -void -CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - x = m_info.m_x; - y = m_info.m_y; - w = m_info.m_w; - h = m_info.m_h; -} - -void -CPrimaryClient::getCursorPos(SInt32&, SInt32&) const -{ - assert(0 && "shouldn't be called"); -} - -void -CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const -{ - x = m_info.m_mx; - y = m_info.m_my; -} diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 823c7297..4d80f86a 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -16,14 +16,9 @@ #define CPRIMARYCLIENT_H #include "IClient.h" -#include "IScreenReceiver.h" #include "ProtocolTypes.h" class CScreen; -class IClipboard; -class IScreenFactory; -class IPrimaryScreenReceiver; -class IServer; //! Primary screen as pseudo-client /*! @@ -31,50 +26,34 @@ The primary screen does not have a client associated with it. This class provides a pseudo-client to allow the primary screen to be treated as if it was on a client. */ -class CPrimaryClient : public IScreenReceiver, public IClient { +class CPrimaryClient : public IClient { public: /*! - \c name is the name of the server. The caller retains ownership of - \c factory. Throws XScreenOpenFailure or whatever the factory can - throw if the screen cannot be created. + \c name is the name of the server. \p screen is adopted. */ - CPrimaryClient(IScreenFactory* factory, IServer*, - IPrimaryScreenReceiver*, const CString& name); + CPrimaryClient(const CString& name, CScreen* screen); ~CPrimaryClient(); //! @name manipulators //@{ - //! Exit event loop - /*! - Force mainLoop() to return. This call can return before - mainLoop() does (i.e. asynchronously). This may only be - called between a successful open() and close(). - */ - void exitMainLoop(); - //! Update configuration /*! Handles reconfiguration of jump zones. */ void reconfigure(UInt32 activeSides); - //! Install a one-shot timer - /*! - Installs a one-shot timer for \c timeout seconds and returns the - id of the timer (which will be passed to \c onTimerExpired()). - */ - UInt32 addOneShotTimer(double timeout); - //@} //! @name accessors //@{ - //! Get clipboard + //! Get cursor center position /*! - Save the marshalled contents of the clipboard indicated by \c id. + Return the cursor center position which is where we park the + cursor to compute cursor motion deltas and should be far from + the edges of the screen, typically the center. */ - void getClipboard(ClipboardID, CString&) const; + void getCursorCenter(SInt32& x, SInt32& y) const; //! Get toggle key state /*! @@ -90,19 +69,19 @@ public: //@} - // IScreenReceiver overrides - virtual void onError(); - virtual void onInfoChanged(const CClientInfo&); - virtual bool onGrabClipboard(ClipboardID); - virtual void onClipboardChanged(ClipboardID, const CString&); - -// XXX -- these go in IClient + // FIXME -- these probably belong on IScreen virtual void enable(); virtual void disable(); + + // IScreen overrides + virtual void* getEventTarget() const; + virtual bool getClipboard(ClipboardID id, IClipboard*) const; + virtual SInt32 getJumpZoneSize() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + // IClient overrides - virtual void open(); - virtual void mainLoop(); - virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, bool forScreensaver); @@ -122,18 +101,10 @@ public: virtual void resetOptions(); virtual void setOptions(const COptionsList& options); virtual CString getName() const; - virtual SInt32 getJumpZoneSize() const; - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const; - virtual void getCursorPos(SInt32& x, SInt32& y) const; - virtual void getCursorCenter(SInt32& x, SInt32& y) const; private: - IServer* m_server; - CScreen* m_screen; CString m_name; - UInt32 m_seqNum; - CClientInfo m_info; + CScreen* m_screen; bool m_clipboardDirty[kClipboardEnd]; }; diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 47361435..cc2282f5 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -13,212 +13,168 @@ */ #include "CServer.h" +#include "CClientProxy.h" +#include "CClientProxyUnknown.h" #include "CPrimaryClient.h" -#include "IScreenFactory.h" -#include "CInputPacketStream.h" -#include "COutputPacketStream.h" -#include "CProtocolUtil.h" -#include "CClientProxy1_0.h" -#include "CClientProxy1_1.h" +#include "CPacketStreamFilter.h" +#include "IPlatformScreen.h" #include "OptionTypes.h" #include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" -#include "CTCPListenSocket.h" #include "IDataSocket.h" -#include "ISocketFactory.h" +#include "IListenSocket.h" #include "XSocket.h" -#include "IStreamFilterFactory.h" -#include "CLock.h" -#include "CThread.h" -#include "CTimerThread.h" -#include "XMT.h" -#include "XThread.h" -#include "CFunctionJob.h" +#include "IEventQueue.h" #include "CLog.h" -#include "CStopwatch.h" -#include "TMethodJob.h" +#include "TMethodEventJob.h" #include "CArch.h" // // CServer // -CServer::CServer(const CString& serverName) : - m_name(serverName), - m_error(false), - m_bindTimeout(5.0 * 60.0), - m_screenFactory(NULL), - m_socketFactory(NULL), - m_streamFilterFactory(NULL), - m_acceptClientThread(NULL), - m_active(NULL), - m_primaryClient(NULL), +CEvent::Type CServer::s_errorEvent = CEvent::kUnknown; +CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; + +CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : + m_active(primaryClient), + m_primaryClient(primaryClient), m_seqNum(0), + m_config(config), m_activeSaver(NULL), m_switchDir(kNoDirection), m_switchScreen(NULL), m_switchWaitDelay(0.0), - m_switchWaitEngaged(false), + m_switchWaitTimer(NULL), m_switchTwoTapDelay(0.0), m_switchTwoTapEngaged(false), m_switchTwoTapArmed(false), - m_switchTwoTapZone(3), - m_status(kNotRunning) + m_switchTwoTapZone(3) { - // do nothing + // must have a primary client and it must have a canonical name + assert(m_primaryClient != NULL); + assert(m_config.isScreen(primaryClient->getName())); + + CString primaryName = getName(primaryClient); + + // clear clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + CClipboardInfo& clipboard = m_clipboards[id]; + clipboard.m_clipboardOwner = primaryName; + clipboard.m_clipboardSeqNum = m_seqNum; + if (clipboard.m_clipboard.open(0)) { + clipboard.m_clipboard.empty(); + clipboard.m_clipboard.close(); + } + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + } + + // install event handlers + EVENTQUEUE->adoptHandler(CEvent::kTimer, this, + new TMethodEventJob(this, + &CServer::handleSwitchWaitTimeout)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyDownEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleKeyDownEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyUpEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleKeyUpEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyRepeatEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleKeyRepeatEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonDownEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleButtonDownEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonUpEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleButtonUpEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getMotionOnPrimaryEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleMotionPrimaryEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getMotionOnSecondaryEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleMotionSecondaryEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getWheelEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleWheelEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getScreensaverActivatedEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleScreensaverActivatedEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getScreensaverDeactivatedEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleScreensaverDeactivatedEvent)); + + // add connection + addClient(m_primaryClient); + + // tell it about the active sides + m_primaryClient->reconfigure(getActivePrimarySides()); + + // tell primary client about its options + sendOptions(m_primaryClient); + + m_primaryClient->enable(); } CServer::~CServer() { - delete m_screenFactory; - delete m_socketFactory; - delete m_streamFilterFactory; -} + // remove event handlers and timers + EVENTQUEUE->removeHandler(IPlatformScreen::getKeyDownEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getKeyUpEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getKeyRepeatEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getButtonDownEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getButtonUpEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnPrimaryEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnSecondaryEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getWheelEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getScreensaverActivatedEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getScreensaverDeactivatedEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(CEvent::kTimer, this); + stopSwitch(); -void -CServer::open() -{ - // open the screen - try { - LOG((CLOG_INFO "opening screen")); - openPrimaryScreen(); - setStatus(kNotRunning); - m_primaryClient->enable(); - } - catch (XScreen& e) { - // can't open screen - setStatus(kError, e.what()); - LOG((CLOG_INFO "failed to open screen")); - throw; - } - catch (XUnknownClient& e) { - // can't open screen - setStatus(kServerNameUnknown); - LOG((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str())); - throw; - } -} - -void -CServer::mainLoop() -{ - // check preconditions - { - CLock lock(&m_mutex); - assert(m_primaryClient != NULL); + // force immediate disconnection of secondary clients + disconnect(); + for (COldClients::iterator index = m_oldClients.begin(); + index != m_oldClients.begin(); ++index) { + IClient* client = index->first; + EVENTQUEUE->deleteTimer(index->second); + EVENTQUEUE->removeHandler(CEvent::kTimer, client); + EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); + delete client; } - try { - setStatus(kNotRunning); - LOG((CLOG_NOTE "starting server")); - -// FIXME -- started here - createClientListener(); -// FIXME -- finished here - - // start listening for new clients - m_acceptClientThread = new CThread(startThread( - new TMethodJob(this, - &CServer::acceptClients))); - - // handle events - m_primaryClient->mainLoop(); - - // clean up - LOG((CLOG_NOTE "stopping server")); - - // use a macro to write the stuff that should go into a finally - // block so we can repeat it easily. stroustrup's view that - // "resource acquistion is initialization" is a better solution - // than a finally block is parochial. they both have their - // place. adding finally to C++ would've been a drop in a big - // bucket. -#define FINALLY do { \ - stopThreads(); \ - runStatusJobs(); \ - } while (false) - FINALLY; - } - catch (XMT& e) { - LOG((CLOG_ERR "server error: %s", e.what())); - setStatus(kError, e.what()); - - // clean up - LOG((CLOG_NOTE "stopping server")); - FINALLY; - throw; - } - catch (XBase& e) { - LOG((CLOG_ERR "server error: %s", e.what())); - setStatus(kError, e.what()); - - // clean up - LOG((CLOG_NOTE "stopping server")); - FINALLY; - } - catch (XThread&) { - setStatus(kNotRunning); - - // clean up - LOG((CLOG_NOTE "stopping server")); - FINALLY; - throw; - } - catch (...) { - LOG((CLOG_DEBUG "unknown server error")); - setStatus(kError); - - // clean up - LOG((CLOG_NOTE "stopping server")); - FINALLY; - throw; - } -#undef FINALLY - - // throw if there was an error - if (m_error) { - LOG((CLOG_DEBUG "forwarding child thread exception")); - throw XServerRethrow(); - } -} - -void -CServer::exitMainLoop() -{ - m_primaryClient->exitMainLoop(); -} - -void -CServer::exitMainLoopWithError() -{ - { - CLock lock(&m_mutex); - m_error = true; - } - exitMainLoop(); -} - -void -CServer::close() -{ - if (m_primaryClient != NULL) { - m_primaryClient->disable(); - closePrimaryScreen(); - } - LOG((CLOG_INFO "closed screen")); + // disconnect primary client + removeClient(m_primaryClient); } bool CServer::setConfig(const CConfig& config) { // refuse configuration if it doesn't include the primary screen - { - CLock lock(&m_mutex); - if (m_primaryClient != NULL && - !config.isScreen(m_primaryClient->getName())) { - return false; - } + if (m_primaryClient != NULL && + !config.isScreen(m_primaryClient->getName())) { + return false; } // close clients that are connected but being dropped from the @@ -226,32 +182,8 @@ CServer::setConfig(const CConfig& config) closeClients(config); // cut over - CLock lock(&m_mutex); m_config = config; - - // process global options - const CConfig::CScreenOptions* options = m_config.getOptions(""); - if (options != NULL && options->size() > 0) { - for (CConfig::CScreenOptions::const_iterator index = options->begin(); - index != options->end(); ++index) { - const OptionID id = index->first; - const OptionValue value = index->second; - if (id == kOptionScreenSwitchDelay) { - m_switchWaitDelay = 1.0e-3 * static_cast(value); - if (m_switchWaitDelay < 0.0) { - m_switchWaitDelay = 0.0; - } - m_switchWaitEngaged = false; - } - else if (id == kOptionScreenSwitchTwoTap) { - m_switchTwoTapDelay = 1.0e-3 * static_cast(value); - if (m_switchTwoTapDelay < 0.0) { - m_switchTwoTapDelay = 0.0; - } - m_switchTwoTapEngaged = false; - } - } - } + processOptions(); // tell primary screen about reconfiguration if (m_primaryClient != NULL) { @@ -265,65 +197,66 @@ CServer::setConfig(const CConfig& config) sendOptions(client); } - // notify of status - runStatusJobs(); - return true; } void -CServer::setScreenFactory(IScreenFactory* adopted) +CServer::adoptClient(IClient* client) { - CLock lock(&m_mutex); - delete m_screenFactory; - m_screenFactory = adopted; + assert(client != NULL); + + // name must be in our configuration + if (!m_config.isScreen(client->getName())) { + LOG((CLOG_WARN "a client with name \"%s\" is not in the map", client->getName().c_str())); + closeClient(client, kMsgEUnknown); + return; + } + + // add client to client list + if (!addClient(client)) { + // can only have one screen with a given name at any given time + LOG((CLOG_WARN "a client with name \"%s\" is already connected", getName(client).c_str())); + closeClient(client, kMsgEBusy); + return; + } + LOG((CLOG_NOTE "client \"%s\" has connected", getName(client).c_str())); + + // watch for client disconnection + EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client, + new TMethodEventJob(this, + &CServer::handleClientDisconnected, client)); + + // send configuration options to client + sendOptions(client); + + // activate screen saver on new client if active on the primary screen + if (m_activeSaver != NULL) { + client->screensaver(true); + } } void -CServer::setSocketFactory(ISocketFactory* adopted) +CServer::disconnect() { - CLock lock(&m_mutex); - delete m_socketFactory; - m_socketFactory = adopted; -} - -void -CServer::setStreamFilterFactory(IStreamFilterFactory* adopted) -{ - CLock lock(&m_mutex); - delete m_streamFilterFactory; - m_streamFilterFactory = adopted; -} - -void -CServer::addStatusJob(IJob* job) -{ - m_statusJobs.addJob(job); -} - -void -CServer::removeStatusJob(IJob* job) -{ - m_statusJobs.removeJob(job); -} - -CString -CServer::getPrimaryScreenName() const -{ - return m_name; + // close all secondary clients + if (m_clients.size() > 1 || !m_oldClients.empty()) { + CConfig emptyConfig; + closeClients(emptyConfig); + } + else { + EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this)); + } } UInt32 CServer::getNumClients() const { - CLock lock(&m_mutex); return m_clients.size(); } void CServer::getClients(std::vector& list) const { - CLock lock(&m_mutex); list.clear(); for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { @@ -331,582 +264,18 @@ CServer::getClients(std::vector& list) const } } -CServer::EStatus -CServer::getStatus(CString* msg) const +CEvent::Type +CServer::getErrorEvent() { - CLock lock(&m_mutex); - if (msg != NULL) { - *msg = m_statusMessage; - } - return m_status; + return CEvent::registerTypeOnce(s_errorEvent, + "CServer::error"); } -void -CServer::getConfig(CConfig* config) const +CEvent::Type +CServer::getDisconnectedEvent() { - assert(config != NULL); - - CLock lock(&m_mutex); - *config = m_config; -} - -CString -CServer::getCanonicalName(const CString& name) const -{ - CLock lock(&m_mutex); - if (m_config.isScreen(name)) { - return m_config.getCanonicalName(name); - } - return name; -} - -void -CServer::runStatusJobs() const -{ - m_statusJobs.runJobs(); -} - -void -CServer::setStatus(EStatus status, const char* msg) -{ - { - CLock lock(&m_mutex); - m_status = status; - if (m_status == kError) { - m_statusMessage = (msg == NULL) ? "Error" : msg; - } - else { - m_statusMessage = (msg == NULL) ? "" : msg; - } - } - runStatusJobs(); -} - -UInt32 -CServer::getActivePrimarySides() const -{ - // note -- m_mutex must be locked on entry - UInt32 sides = 0; - if (!m_config.getNeighbor(getPrimaryScreenName(), kLeft).empty()) { - sides |= kLeftMask; - } - if (!m_config.getNeighbor(getPrimaryScreenName(), kRight).empty()) { - sides |= kRightMask; - } - if (!m_config.getNeighbor(getPrimaryScreenName(), kTop).empty()) { - sides |= kTopMask; - } - if (!m_config.getNeighbor(getPrimaryScreenName(), kBottom).empty()) { - sides |= kBottomMask; - } - return sides; -} - -void -CServer::onError() -{ - setStatus(kError); - - // stop all running threads but don't wait too long since some - // threads may be unable to proceed until this thread returns. - stopThreads(3.0); - - // note -- we do not attempt to close down the primary screen -} - -void -CServer::onInfoChanged(const CString& name, const CClientInfo& info) -{ - CLock lock(&m_mutex); - - // look up client - CClientList::iterator index = m_clients.find(name); - if (index == m_clients.end()) { - throw XBadClient(); - } - IClient* client = index->second; - assert(client != NULL); - - // update the remote mouse coordinates - if (client == m_active) { - m_x = info.m_mx; - m_y = info.m_my; - } - LOG((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", name.c_str(), info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); - - // handle resolution change to primary screen - if (client == m_primaryClient) { - if (client == m_active) { - onMouseMovePrimaryNoLock(m_x, m_y); - } - else { - onMouseMoveSecondaryNoLock(0, 0); - } - } -} - -bool -CServer::onGrabClipboard(const CString& name, ClipboardID id, UInt32 seqNum) -{ - CLock lock(&m_mutex); - - // screen must be connected - CClientList::iterator grabber = m_clients.find(name); - if (grabber == m_clients.end()) { - throw XBadClient(); - } - - // ignore grab if sequence number is old. always allow primary - // screen to grab. - CClipboardInfo& clipboard = m_clipboards[id]; - if (name != m_primaryClient->getName() && - seqNum < clipboard.m_clipboardSeqNum) { - LOG((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", name.c_str(), id)); - return false; - } - - // mark screen as owning clipboard - LOG((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", name.c_str(), id, clipboard.m_clipboardOwner.c_str())); - clipboard.m_clipboardOwner = name; - clipboard.m_clipboardSeqNum = seqNum; - - // clear the clipboard data (since it's not known at this point) - if (clipboard.m_clipboard.open(0)) { - clipboard.m_clipboard.empty(); - clipboard.m_clipboard.close(); - } - clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); - - // tell all other screens to take ownership of clipboard. tell the - // grabber that it's clipboard isn't dirty. - for (CClientList::iterator index = m_clients.begin(); - index != m_clients.end(); ++index) { - IClient* client = index->second; - if (index == grabber) { - client->setClipboardDirty(id, false); - } - else { - client->grabClipboard(id); - } - } - - return true; -} - -void -CServer::onClipboardChanged(ClipboardID id, UInt32 seqNum, const CString& data) -{ - CLock lock(&m_mutex); - onClipboardChangedNoLock(id, seqNum, data); -} - -void -CServer::onClipboardChangedNoLock(ClipboardID id, - UInt32 seqNum, const CString& data) -{ - CClipboardInfo& clipboard = m_clipboards[id]; - - // ignore update if sequence number is old - if (seqNum < clipboard.m_clipboardSeqNum) { - LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", clipboard.m_clipboardOwner.c_str(), id)); - return; - } - - // ignore if data hasn't changed - if (data == clipboard.m_clipboardData) { - LOG((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id)); - return; - } - - // unmarshall into our clipboard buffer - LOG((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); - clipboard.m_clipboardData = data; - clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); - - // tell all clients except the sender that the clipboard is dirty - CClientList::const_iterator sender = - m_clients.find(clipboard.m_clipboardOwner); - for (CClientList::const_iterator index = m_clients.begin(); - index != m_clients.end(); ++index) { - IClient* client = index->second; - client->setClipboardDirty(id, index != sender); - } - - // send the new clipboard to the active screen - m_active->setClipboard(id, m_clipboards[id].m_clipboardData); -} - -void -CServer::onScreensaver(bool activated) -{ - LOG((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); - CLock lock(&m_mutex); - - if (activated) { - // save current screen and position - m_activeSaver = m_active; - m_xSaver = m_x; - m_ySaver = m_y; - - // jump to primary screen - if (m_active != m_primaryClient) { - switchScreen(m_primaryClient, 0, 0, true); - } - } - else { - // jump back to previous screen and position. we must check - // that the position is still valid since the screen may have - // changed resolutions while the screen saver was running. - if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { - // check position - IClient* screen = m_activeSaver; - SInt32 x, y, w, h; - screen->getShape(x, y, w, h); - SInt32 zoneSize = screen->getJumpZoneSize(); - if (m_xSaver < x + zoneSize) { - m_xSaver = x + zoneSize; - } - else if (m_xSaver >= x + w - zoneSize) { - m_xSaver = x + w - zoneSize - 1; - } - if (m_ySaver < y + zoneSize) { - m_ySaver = y + zoneSize; - } - else if (m_ySaver >= y + h - zoneSize) { - m_ySaver = y + h - zoneSize - 1; - } - - // jump - switchScreen(screen, m_xSaver, m_ySaver, false); - } - - // reset state - m_activeSaver = NULL; - } - - // send message to all clients - for (CClientList::const_iterator index = m_clients.begin(); - index != m_clients.end(); ++index) { - IClient* client = index->second; - client->screensaver(activated); - } -} - -void -CServer::onOneShotTimerExpired(UInt32 id) -{ - CLock lock(&m_mutex); - - // ignore if it's an old timer or if switch wait isn't engaged anymore - if (!m_switchWaitEngaged || id != m_switchWaitTimer) { - return; - } - - // ignore if mouse is locked to screen - if (isLockedToScreenNoLock()) { - LOG((CLOG_DEBUG1 "locked to screen")); - clearSwitchState(); - return; - } - - // switch screen - switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false); -} - -void -CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) -{ - LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); - CLock lock(&m_mutex); - assert(m_active != NULL); - - // handle command keys - if (onCommandKey(id, mask, true)) { - return; - } - - // relay - m_active->keyDown(id, mask, button); -} - -void -CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button) -{ - LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); - CLock lock(&m_mutex); - assert(m_active != NULL); - - // handle command keys - if (onCommandKey(id, mask, false)) { - return; - } - - // relay - m_active->keyUp(id, mask, button); -} - -void -CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, - SInt32 count, KeyButton button) -{ - LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); - CLock lock(&m_mutex); - assert(m_active != NULL); - - // handle command keys - if (onCommandKey(id, mask, false)) { - onCommandKey(id, mask, true); - return; - } - - // relay - m_active->keyRepeat(id, mask, count, button); -} - -void -CServer::onMouseDown(ButtonID id) -{ - LOG((CLOG_DEBUG1 "onMouseDown id=%d", id)); - CLock lock(&m_mutex); - assert(m_active != NULL); - - // relay - m_active->mouseDown(id); -} - -void -CServer::onMouseUp(ButtonID id) -{ - LOG((CLOG_DEBUG1 "onMouseUp id=%d", id)); - CLock lock(&m_mutex); - assert(m_active != NULL); - - // relay - m_active->mouseUp(id); -} - -bool -CServer::onMouseMovePrimary(SInt32 x, SInt32 y) -{ - LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); - CLock lock(&m_mutex); - return onMouseMovePrimaryNoLock(x, y); -} - -bool -CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) -{ - // mouse move on primary (server's) screen - assert(m_primaryClient != NULL); - assert(m_active == m_primaryClient); - - // get screen shape - SInt32 ax, ay, aw, ah; - m_active->getShape(ax, ay, aw, ah); - SInt32 zoneSize = m_active->getJumpZoneSize(); - - // see if we should change screens - EDirection dir; - if (x < ax + zoneSize) { - x -= zoneSize; - dir = kLeft; - } - else if (x >= ax + aw - zoneSize) { - x += zoneSize; - dir = kRight; - } - else if (y < ay + zoneSize) { - y -= zoneSize; - dir = kTop; - } - else if (y >= ay + ah - zoneSize) { - y += zoneSize; - dir = kBottom; - } - else { - // still on local screen. check if we're inside the tap region. - SInt32 tapZone = (zoneSize < m_switchTwoTapZone) ? - m_switchTwoTapZone : zoneSize; - bool inTapZone = (x < ax + tapZone || - x >= ax + aw - tapZone || - y < ay + tapZone || - y >= ay + ah - tapZone); - - // failed to switch - onNoSwitch(inTapZone); - return false; - } - - // get jump destination - IClient* newScreen = getNeighbor(m_active, dir, x, y); - - // should we switch or not? - if (isSwitchOkay(newScreen, dir, x, y)) { - // switch screen - switchScreen(newScreen, x, y, false); - return true; - } - else { - return false; - } -} - -void -CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) -{ - LOG((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); - CLock lock(&m_mutex); - onMouseMoveSecondaryNoLock(dx, dy); -} - -void -CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) -{ - // mouse move on secondary (client's) screen - assert(m_active != NULL); - if (m_active == m_primaryClient) { - // we're actually on the primary screen. this can happen - // when the primary screen begins processing a mouse move - // for a secondary screen, then the active (secondary) - // screen disconnects causing us to jump to the primary - // screen, and finally the primary screen finishes - // processing the mouse move, still thinking it's for - // a secondary screen. we just ignore the motion. - return; - } - - // save old position - const SInt32 xOld = m_x; - const SInt32 yOld = m_y; - - // accumulate motion - m_x += dx; - m_y += dy; - - // get screen shape - SInt32 ax, ay, aw, ah; - m_active->getShape(ax, ay, aw, ah); - - // find direction of neighbor and get the neighbor - bool jump = true; - IClient* newScreen; - do { - EDirection dir; - if (m_x < ax) { - dir = kLeft; - } - else if (m_x > ax + aw - 1) { - dir = kRight; - } - else if (m_y < ay) { - dir = kTop; - } - else if (m_y > ay + ah - 1) { - dir = kBottom; - } - else { - // we haven't left the screen - newScreen = m_active; - jump = false; - - // if waiting and mouse is not on the border we're waiting - // on then stop waiting. also if it's not on the border - // then arm the double tap. - if (m_switchScreen != NULL) { - bool clearWait; - SInt32 zoneSize = m_primaryClient->getJumpZoneSize(); - switch (m_switchDir) { - case kLeft: - clearWait = (m_x >= ax + zoneSize); - break; - - case kRight: - clearWait = (m_x <= ax + aw - 1 - zoneSize); - break; - - case kTop: - clearWait = (m_y >= ay + zoneSize); - break; - - case kBottom: - clearWait = (m_y <= ay + ah - 1 + zoneSize); - break; - - default: - clearWait = false; - break; - } - if (clearWait) { - // still on local screen. check if we're inside the - // tap region. - SInt32 tapZone = (zoneSize < m_switchTwoTapZone) ? - m_switchTwoTapZone : zoneSize; - bool inTapZone = (m_x < ax + tapZone || - m_x >= ax + aw - tapZone || - m_y < ay + tapZone || - m_y >= ay + ah - tapZone); - - // failed to switch - onNoSwitch(inTapZone); - } - } - - // skip rest of block - break; - } - - // try to switch screen. get the neighbor. - newScreen = getNeighbor(m_active, dir, m_x, m_y); - - // see if we should switch - if (!isSwitchOkay(newScreen, dir, m_x, m_y)) { - newScreen = m_active; - jump = false; - } - } while (false); - - if (jump) { - // switch screens - switchScreen(newScreen, m_x, m_y, false); - } - else { - // same screen. clamp mouse to edge. - m_x = xOld + dx; - m_y = yOld + dy; - if (m_x < ax) { - m_x = ax; - LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", m_active->getName().c_str())); - } - else if (m_x > ax + aw - 1) { - m_x = ax + aw - 1; - LOG((CLOG_DEBUG2 "clamp to right of \"%s\"", m_active->getName().c_str())); - } - if (m_y < ay) { - m_y = ay; - LOG((CLOG_DEBUG2 "clamp to top of \"%s\"", m_active->getName().c_str())); - } - else if (m_y > ay + ah - 1) { - m_y = ay + ah - 1; - LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", m_active->getName().c_str())); - } - - // warp cursor if it moved. - if (m_x != xOld || m_y != yOld) { - LOG((CLOG_DEBUG2 "move on %s to %d,%d", m_active->getName().c_str(), m_x, m_y)); - m_active->mouseMove(m_x, m_y); - } - } -} - -void -CServer::onMouseWheel(SInt32 delta) -{ - LOG((CLOG_DEBUG1 "onMouseWheel %+d", delta)); - CLock lock(&m_mutex); - assert(m_active != NULL); - - // relay - m_active->mouseWheel(delta); + return CEvent::registerTypeOnce(s_disconnectedEvent, + "CServer::disconnected"); } bool @@ -915,8 +284,34 @@ CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) return false; } +CString +CServer::getName(const IClient* client) const +{ + return m_config.getCanonicalName(client->getName()); +} + +UInt32 +CServer::getActivePrimarySides() const +{ + CString primaryName = getName(m_primaryClient); + UInt32 sides = 0; + if (!m_config.getNeighbor(primaryName, kLeft).empty()) { + sides |= kLeftMask; + } + if (!m_config.getNeighbor(primaryName, kRight).empty()) { + sides |= kRightMask; + } + if (!m_config.getNeighbor(primaryName, kTop).empty()) { + sides |= kTopMask; + } + if (!m_config.getNeighbor(primaryName, kBottom).empty()) { + sides |= kBottomMask; + } + return sides; +} + bool -CServer::isLockedToScreenNoLock() const +CServer::isLockedToScreen() const { // locked if scroll-lock is toggled on if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) { @@ -933,11 +328,20 @@ CServer::isLockedToScreenNoLock() const return false; } +SInt32 +CServer::getJumpZoneSize(IClient* client) const +{ + if (client == m_primaryClient) { + return m_primaryClient->getJumpZoneSize(); + } + else { + return 0; + } +} + void CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) { - // note -- must be locked on entry - assert(dst != NULL); #ifndef NDEBUG { @@ -948,10 +352,10 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) #endif assert(m_active != NULL); - LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y)); + LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", getName(m_active).c_str(), getName(dst).c_str(), x, y)); // stop waiting to switch - clearSwitchState(); + stopSwitch(); // record new position m_x = x; @@ -973,11 +377,9 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) if (m_active == m_primaryClient) { for (ClipboardID id = 0; id < kClipboardEnd; ++id) { CClipboardInfo& clipboard = m_clipboards[id]; - if (clipboard.m_clipboardOwner == m_primaryClient->getName()) { - CString clipboardData; - m_primaryClient->getClipboard(id, clipboardData); - onClipboardChangedNoLock(id, - clipboard.m_clipboardSeqNum, clipboardData); + if (clipboard.m_clipboardOwner == getName(m_primaryClient)) { + onClipboardChanged(m_primaryClient, + id, clipboard.m_clipboardSeqNum); } } } @@ -1011,7 +413,7 @@ CServer::getNeighbor(IClient* src, EDirection dir) const assert(src != NULL); // get source screen name - CString srcName = src->getName(); + CString srcName = getName(src); assert(!srcName.empty()); LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); @@ -1087,7 +489,7 @@ CServer::getNeighbor(IClient* src, if (x >= 0) { break; } - LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); @@ -1103,7 +505,7 @@ CServer::getNeighbor(IClient* src, if (x < dw) { break; } - LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); @@ -1119,7 +521,7 @@ CServer::getNeighbor(IClient* src, if (y >= 0) { break; } - LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); @@ -1135,7 +537,7 @@ CServer::getNeighbor(IClient* src, if (y < sh) { break; } - LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str())); + LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); dst = getNeighbor(lastGoodScreen, srcSide); } assert(lastGoodScreen != NULL); @@ -1152,30 +554,30 @@ CServer::getNeighbor(IClient* src, // a neighbor (i.e. an asymmetrical side) then we don't need to // move inwards because that side can't provoke a jump. if (dst == m_primaryClient) { - const CString dstName(dst->getName()); + const CString dstName(getName(dst)); switch (srcSide) { case kLeft: if (!m_config.getNeighbor(dstName, kRight).empty() && - x > dx + dw - 1 - dst->getJumpZoneSize()) - x = dx + dw - 1 - dst->getJumpZoneSize(); + x > dx + dw - 1 - getJumpZoneSize(dst)) + x = dx + dw - 1 - getJumpZoneSize(dst); break; case kRight: if (!m_config.getNeighbor(dstName, kLeft).empty() && - x < dx + dst->getJumpZoneSize()) - x = dx + dst->getJumpZoneSize(); + x < dx + getJumpZoneSize(dst)) + x = dx + getJumpZoneSize(dst); break; case kTop: if (!m_config.getNeighbor(dstName, kBottom).empty() && - y > dy + dh - 1 - dst->getJumpZoneSize()) - y = dy + dh - 1 - dst->getJumpZoneSize(); + y > dy + dh - 1 - getJumpZoneSize(dst)) + y = dy + dh - 1 - getJumpZoneSize(dst); break; case kBottom: if (!m_config.getNeighbor(dstName, kTop).empty() && - y < dy + dst->getJumpZoneSize()) - y = dy + dst->getJumpZoneSize(); + y < dy + getJumpZoneSize(dst)) + y = dy + getJumpZoneSize(dst); break; } } @@ -1226,14 +628,14 @@ CServer::getNeighbor(IClient* src, bool CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) { - LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir))); + LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", getName(m_active).c_str(), CConfig::dirName(dir))); // is there a neighbor? if (newScreen == NULL) { // there's no neighbor. we don't want to switch and we don't // want to try to switch later. LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir))); - clearSwitchState(); + stopSwitch(); return false; } @@ -1251,554 +653,145 @@ CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) // is this a double tap and do we care? if (!allowSwitch && m_switchTwoTapDelay > 0.0) { - if (isNewDirection || !m_switchTwoTapEngaged) { - // tapping a different or new edge. prepare for second tap. - preventSwitch = true; - m_switchTwoTapEngaged = true; - m_switchTwoTapArmed = false; - m_switchTwoTapTimer.reset(); - LOG((CLOG_DEBUG1 "waiting for second tap")); + if (isNewDirection || + !isSwitchTwoTapStarted() || !shouldSwitchTwoTap()) { + // tapping a different or new edge or second tap not + // fast enough. prepare for second tap. + preventSwitch = true; + startSwitchTwoTap(); } else { - // second tap if we were armed. if soon enough then switch. - if (m_switchTwoTapArmed && - m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay) { - allowSwitch = true; - } - else { - // not fast enough. reset the clock. - preventSwitch = true; - m_switchTwoTapEngaged = true; - m_switchTwoTapArmed = false; - m_switchTwoTapTimer.reset(); - LOG((CLOG_DEBUG1 "waiting for second tap")); - } + // got second tap + allowSwitch = true; } } // if waiting before a switch then prepare to switch later if (!allowSwitch && m_switchWaitDelay > 0.0) { - if (isNewDirection || !m_switchWaitEngaged) { - m_switchWaitEngaged = true; - m_switchWaitX = x; - m_switchWaitY = y; - m_switchWaitTimer = m_primaryClient->addOneShotTimer( - m_switchWaitDelay); - LOG((CLOG_DEBUG1 "waiting to switch")); + if (isNewDirection || !isSwitchWaitStarted()) { + startSwitchWait(x, y); } preventSwitch = true; } - // ignore if mouse is locked to screen - if (!preventSwitch && isLockedToScreenNoLock()) { + // ignore if mouse is locked to screen and don't try to switch later + if (!preventSwitch && isLockedToScreen()) { LOG((CLOG_DEBUG1 "locked to screen")); preventSwitch = true; - - // don't try to switch later. it's possible that we might - // not be locked to the screen when the wait delay expires - // and could switch then but we'll base the decision on - // when the user first attempts the switch. this also - // ensures that all switch tests are using the same - clearSwitchState(); + stopSwitch(); } return !preventSwitch; } void -CServer::onNoSwitch(bool inTapZone) +CServer::noSwitch(SInt32 x, SInt32 y) +{ + armSwitchTwoTap(x, y); + stopSwitchWait(); +} + +void +CServer::stopSwitch() +{ + if (m_switchScreen != NULL) { + m_switchScreen = NULL; + m_switchDir = kNoDirection; + stopSwitchTwoTap(); + stopSwitchWait(); + } +} + +void +CServer::startSwitchTwoTap() +{ + m_switchTwoTapEngaged = true; + m_switchTwoTapArmed = false; + m_switchTwoTapTimer.reset(); + LOG((CLOG_DEBUG1 "waiting for second tap")); +} + +void +CServer::armSwitchTwoTap(SInt32 x, SInt32 y) { if (m_switchTwoTapEngaged) { if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) { // second tap took too long. disengage. - m_switchTwoTapEngaged = false; - m_switchTwoTapArmed = false; + stopSwitchTwoTap(); } - else if (!inTapZone) { - // we've moved away from the edge and there's still - // time to get back for a double tap. - m_switchTwoTapArmed = true; - } - } - - // once the mouse moves away from the edge we no longer want to - // switch after a delay. - m_switchWaitEngaged = false; -} - -void -CServer::clearSwitchState() -{ - if (m_switchScreen != NULL) { - m_switchDir = kNoDirection; - m_switchScreen = NULL; - m_switchWaitEngaged = false; - m_switchTwoTapEngaged = false; - } -} - -void -CServer::closeClients(const CConfig& config) -{ - CThreadList threads; - { - CLock lock(&m_mutex); - - // get the set of clients that are connected but are being - // dropped from the configuration (or who's canonical name - // is changing) and tell them to disconnect. note that - // since m_clientThreads doesn't include a thread for the - // primary client we will not close it. - for (CClientThreadList::iterator - index = m_clientThreads.begin(); - index != m_clientThreads.end(); ) { - const CString& name = index->first; - if (!config.isCanonicalName(name)) { - // lookup IClient with name - CClientList::const_iterator index2 = m_clients.find(name); - assert(index2 != m_clients.end()); - - // save the thread and remove it from m_clientThreads - threads.push_back(index->second); - m_clientThreads.erase(index++); - - // close that client - assert(index2->second != m_primaryClient); - index2->second->close(); - - // don't switch to it if we planned to - if (index2->second == m_switchScreen) { - clearSwitchState(); - } + else if (!m_switchTwoTapArmed) { + // still time for a double tap. see if we left the tap + // zone and, if so, arm the two tap. + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + SInt32 tapZone = m_primaryClient->getJumpZoneSize(); + if (tapZone < m_switchTwoTapZone) { + tapZone = m_switchTwoTapZone; } - else { - ++index; + if (x >= ax + tapZone && x < ax + aw - tapZone && + y >= ay + tapZone && y < ay + ah - tapZone) { + m_switchTwoTapArmed = true; } } } - - // wait a moment to allow each client to close its connection - // before we close it (to avoid having our socket enter TIME_WAIT). - if (threads.size() > 0) { - ARCH->sleep(1.0); - } - - // cancel the old client threads - for (CThreadList::iterator index = threads.begin(); - index != threads.end(); ++index) { - index->cancel(); - } - - // wait for old client threads to terminate. we must not hold - // the lock while we do this so those threads can finish any - // calls to this object. - for (CThreadList::iterator index = threads.begin(); - index != threads.end(); ++index) { - index->wait(); - } - - // clean up thread list - reapThreads(); -} - -CThread -CServer::startThread(IJob* job) -{ - CLock lock(&m_mutex); - - // reap completed threads - doReapThreads(m_threads); - - // add new thread to list - CThread thread(job); - m_threads.push_back(thread); - LOG((CLOG_DEBUG1 "started thread 0x%08x", thread.getID())); - return thread; } void -CServer::stopThreads(double timeout) +CServer::stopSwitchTwoTap() { - LOG((CLOG_DEBUG1 "stopping threads")); + m_switchTwoTapEngaged = false; + m_switchTwoTapArmed = false; +} - // cancel the accept client thread to prevent more clients from - // connecting while we're shutting down. - CThread* acceptClientThread; - { - CLock lock(&m_mutex); - acceptClientThread = m_acceptClientThread; - m_acceptClientThread = NULL; - } - if (acceptClientThread != NULL) { - acceptClientThread->cancel(); - acceptClientThread->wait(timeout); - delete acceptClientThread; - } +bool +CServer::isSwitchTwoTapStarted() const +{ + return m_switchTwoTapEngaged; +} - // close all clients (except the primary) - { - CConfig emptyConfig; - closeClients(emptyConfig); - } - - // swap thread list so nobody can mess with it - CThreadList threads; - { - CLock lock(&m_mutex); - threads.swap(m_threads); - } - - // cancel every thread - for (CThreadList::iterator index = threads.begin(); - index != threads.end(); ++index) { - index->cancel(); - } - - // now wait for the threads - CStopwatch timer(true); - while (threads.size() > 0 && (timeout < 0.0 || timer.getTime() < timeout)) { - doReapThreads(threads); - ARCH->sleep(0.01); - } - - // delete remaining threads - for (CThreadList::iterator index = threads.begin(); - index != threads.end(); ++index) { - LOG((CLOG_DEBUG1 "reaped running thread 0x%08x", index->getID())); - } - - LOG((CLOG_DEBUG1 "stopped threads")); +bool +CServer::shouldSwitchTwoTap() const +{ + // this is the second tap if two-tap is armed and this tap + // came fast enough + return (m_switchTwoTapArmed && + m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay); } void -CServer::reapThreads() +CServer::startSwitchWait(SInt32 x, SInt32 y) { - CLock lock(&m_mutex); - doReapThreads(m_threads); + stopSwitchWait(); + m_switchWaitX = x; + m_switchWaitY = y; + m_switchWaitTimer = EVENTQUEUE->newOneShotTimer(m_switchWaitDelay, this); + LOG((CLOG_DEBUG1 "waiting to switch")); } void -CServer::doReapThreads(CThreadList& threads) +CServer::stopSwitchWait() { - for (CThreadList::iterator index = threads.begin(); - index != threads.end(); ) { - if (index->wait(0.0)) { - // thread terminated - LOG((CLOG_DEBUG1 "reaped thread 0x%08x", index->getID())); - index = threads.erase(index); - } - else { - // thread is running - ++index; - } + if (m_switchWaitTimer != NULL) { + EVENTQUEUE->deleteTimer(m_switchWaitTimer); + m_switchWaitTimer = NULL; } } -void -CServer::acceptClients(void*) +bool +CServer::isSwitchWaitStarted() const { - LOG((CLOG_DEBUG1 "starting to wait for clients")); - - IListenSocket* listen = NULL; - try { - // create socket listener - if (m_socketFactory != NULL) { - listen = m_socketFactory->createListen(); - } - assert(listen != NULL); - - // bind to the desired port. keep retrying if we can't bind - // the address immediately. - CStopwatch timer; - for (;;) { - try { - LOG((CLOG_DEBUG1 "binding listen socket")); - listen->bind(m_config.getSynergyAddress()); - break; - } - catch (XSocketAddressInUse& e) { - setStatus(kError, e.what()); - LOG((CLOG_WARN "bind failed: %s", e.what())); - - // give up if we've waited too long - if (timer.getTime() >= m_bindTimeout) { - LOG((CLOG_ERR "waited too long to bind, giving up")); - throw; - } - - // wait a bit before retrying - ARCH->sleep(5.0); - } - } - - // accept connections and begin processing them - setStatus(kRunning); - LOG((CLOG_DEBUG1 "waiting for client connections")); - for (;;) { - // accept connection - CThread::testCancel(); - IDataSocket* socket = listen->accept(); - LOG((CLOG_NOTE "accepted client connection")); - CThread::testCancel(); - - // start handshake thread - startThread(new TMethodJob( - this, &CServer::runClient, socket)); - } - } - catch (XBase& e) { - setStatus(kError, e.what()); - LOG((CLOG_ERR "cannot listen for clients: %s", e.what())); - delete listen; - exitMainLoopWithError(); - } - catch (...) { - setStatus(kNotRunning); - delete listen; - throw; - } -} - -void -CServer::runClient(void* vsocket) -{ - // get the socket pointer from the argument - assert(vsocket != NULL); - IDataSocket* socket = reinterpret_cast(vsocket); - - // create proxy - CClientProxy* proxy = NULL; - try { - proxy = handshakeClient(socket); - if (proxy == NULL) { - delete socket; - return; - } - } - catch (...) { - delete socket; - throw; - } - - // add the connection - try { - addConnection(proxy); - - // save this client's thread - CLock lock(&m_mutex); - m_clientThreads.insert(std::make_pair(proxy->getName(), - CThread::getCurrentThread())); - - // send configuration options - sendOptions(proxy); - } - catch (XDuplicateClient& e) { - // client has duplicate name - LOG((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); - try { - CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); - } - catch (XIO&) { - // ignore - } - delete proxy; - delete socket; - return; - } - catch (XUnknownClient& e) { - // client has unknown name - LOG((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); - try { - CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); - } - catch (XIO&) { - // ignore - } - delete proxy; - delete socket; - return; - } - catch (...) { - delete proxy; - delete socket; - throw; - } - - // activate screen saver on new client if active on the primary screen - { - CLock lock(&m_mutex); - if (m_activeSaver != NULL) { - proxy->screensaver(true); - } - } - - // handle client messages - try { - LOG((CLOG_NOTE "client \"%s\" has connected", proxy->getName().c_str())); - proxy->mainLoop(); - } - catch (XBadClient&) { - // client not behaving - LOG((CLOG_WARN "protocol error from client \"%s\"", proxy->getName().c_str())); - try { - CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad); - } - catch (XIO&) { - // ignore. client probably aborted the connection. - } - } - catch (XBase& e) { - // misc error - LOG((CLOG_WARN "error communicating with client \"%s\": %s", proxy->getName().c_str(), e.what())); - } - catch (...) { - // mainLoop() was probably cancelled - removeConnection(proxy->getName()); - delete socket; - throw; - } - - // clean up - removeConnection(proxy->getName()); - delete socket; -} - -CClientProxy* -CServer::handshakeClient(IDataSocket* socket) -{ - LOG((CLOG_DEBUG1 "negotiating with new client")); - - CClientProxy* proxy = NULL; - CString name(""); - try { - // give the client a limited time to complete the handshake - CTimerThread timer(30.0); - - // say hello - LOG((CLOG_DEBUG1 "saying hello")); - CProtocolUtil::writef(output, kMsgHello, - kProtocolMajorVersion, - kProtocolMinorVersion); - output->flush(); - - // wait for the reply - LOG((CLOG_DEBUG1 "waiting for hello reply")); - UInt32 n = input->getSize(); - - // limit the maximum length of the hello - if (n > kMaxHelloLength) { - throw XBadClient(); - } - - // get and parse the reply to hello - SInt16 major, minor; - try { - LOG((CLOG_DEBUG1 "parsing hello reply")); - CProtocolUtil::readf(input, kMsgHelloBack, - &major, &minor, &name); - } - catch (XIO&) { - throw XBadClient(); - } - - // disallow invalid version numbers - if (major <= 0 || minor < 0) { - throw XIncompatibleClient(major, minor); - } - - // convert name to canonical form (if any) - if (m_config.isScreen(name)) { - name = m_config.getCanonicalName(name); - } - - // create client proxy for highest version supported by the client - LOG((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor)); - if (major == 1) { - switch (minor) { - case 0: - proxy = new CClientProxy1_0(this, name, input, output); - break; - - case 1: - proxy = new CClientProxy1_1(this, name, input, output); - break; - } - } - - // hangup (with error) if version isn't supported - if (proxy == NULL) { - throw XIncompatibleClient(major, minor); - } - - // negotiate - // FIXME - - // ask and wait for the client's info - LOG((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str())); - proxy->open(); - - return proxy; - } - catch (XIncompatibleClient& e) { - // client is incompatible - LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); - try { - CProtocolUtil::writef(output, kMsgEIncompatible, - kProtocolMajorVersion, kProtocolMinorVersion); - } - catch (XIO&) { - // ignore - } - } - catch (XBadClient&) { - // client not behaving - LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); - try { - CProtocolUtil::writef(output, kMsgEBad); - } - catch (XIO&) { - // ignore. client probably aborted the connection. - } - } - catch (XBase& e) { - // misc error - LOG((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); - } - catch (...) { - // probably timed out - if (proxy != NULL) { - delete proxy; - } - else if (own) { - delete input; - delete output; - } - throw; - } - - // failed - if (proxy != NULL) { - delete proxy; - } - else if (own) { - delete input; - delete output; - } - - return NULL; + return (m_switchWaitTimer != NULL); } void CServer::sendOptions(IClient* client) const { - // note -- must be locked on entry - COptionsList optionsList; // look up options for client const CConfig::CScreenOptions* options = - m_config.getOptions(client->getName()); - if (options != NULL && options->size() > 0) { + m_config.getOptions(getName(client)); + if (options != NULL) { // convert options to a more convenient form for sending optionsList.reserve(2 * options->size()); for (CConfig::CScreenOptions::const_iterator index = options->begin(); @@ -1810,7 +803,7 @@ CServer::sendOptions(IClient* client) const // look up global options options = m_config.getOptions(""); - if (options != NULL && options->size() > 0) { + if (options != NULL) { // convert options to a more convenient form for sending optionsList.reserve(optionsList.size() + 2 * options->size()); for (CConfig::CScreenOptions::const_iterator index = options->begin(); @@ -1825,180 +818,762 @@ CServer::sendOptions(IClient* client) const } void -CServer::openPrimaryScreen() +CServer::processOptions() { - assert(m_primaryClient == NULL); - - // reset sequence number - m_seqNum = 0; - - // canonicalize the primary screen name - CString primaryName = m_config.getCanonicalName(getPrimaryScreenName()); - if (primaryName.empty()) { - throw XUnknownClient(getPrimaryScreenName()); + const CConfig::CScreenOptions* options = m_config.getOptions(""); + if (options == NULL) { + return; } - // clear clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - CClipboardInfo& clipboard = m_clipboards[id]; - clipboard.m_clipboardOwner = primaryName; - clipboard.m_clipboardSeqNum = m_seqNum; - if (clipboard.m_clipboard.open(0)) { - clipboard.m_clipboard.empty(); - clipboard.m_clipboard.close(); + for (CConfig::CScreenOptions::const_iterator index = options->begin(); + index != options->end(); ++index) { + const OptionID id = index->first; + const OptionValue value = index->second; + if (id == kOptionScreenSwitchDelay) { + m_switchWaitDelay = 1.0e-3 * static_cast(value); + if (m_switchWaitDelay < 0.0) { + m_switchWaitDelay = 0.0; + } + stopSwitchWait(); } - clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + else if (id == kOptionScreenSwitchTwoTap) { + m_switchTwoTapDelay = 1.0e-3 * static_cast(value); + if (m_switchTwoTapDelay < 0.0) { + m_switchTwoTapDelay = 0.0; + } + stopSwitchTwoTap(); + } + } +} + +void +CServer::handleShapeChanged(const CEvent&, void* vclient) +{ + // ignore events from unknown clients + IClient* client = reinterpret_cast(vclient); + if (m_clientSet.count(client) == 0) { + return; } - try { - // create the primary client - m_primaryClient = new CPrimaryClient(m_screenFactory, - this, this, primaryName); - - // add connection - addConnection(m_primaryClient); - m_active = m_primaryClient; - - // open the screen - LOG((CLOG_DEBUG1 "opening primary screen")); - m_primaryClient->open(); - - // tell it about the active sides - m_primaryClient->reconfigure(getActivePrimarySides()); - - // tell primary client about its options - sendOptions(m_primaryClient); + // update the mouse coordinates + if (client == m_active) { + client->getCursorPos(m_x, m_y); } - catch (...) { - // if m_active is NULL then we haven't added the connection - // for the primary client so we don't try to remove it. - if (m_active != NULL) { - removeConnection(primaryName); + LOG((CLOG_INFO "screen \"%s\" shape changed", getName(client).c_str())); + + // handle resolution change to primary screen + if (client == m_primaryClient) { + if (client == m_active) { + onMouseMovePrimary(m_x, m_y); } else { - delete m_primaryClient; + onMouseMoveSecondary(0, 0); } - m_active = NULL; - m_primaryClient = NULL; - throw; } } void -CServer::closePrimaryScreen() +CServer::handleClipboardGrabbed(const CEvent& event, void* vclient) { + // ignore events from unknown clients + IClient* grabber = reinterpret_cast(vclient); + if (m_clientSet.count(grabber) == 0) { + return; + } + const IScreen::CClipboardInfo* info = + reinterpret_cast(event.getData()); + + // ignore grab if sequence number is old. always allow primary + // screen to grab. + CClipboardInfo& clipboard = m_clipboards[info->m_id]; + if (grabber != m_primaryClient && + info->m_sequenceNumber < clipboard.m_clipboardSeqNum) { + LOG((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", getName(grabber).c_str(), info->m_id)); + return; + } + + // mark screen as owning clipboard + LOG((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", getName(grabber).c_str(), info->m_id, clipboard.m_clipboardOwner.c_str())); + clipboard.m_clipboardOwner = getName(grabber); + clipboard.m_clipboardSeqNum = info->m_sequenceNumber; + + // clear the clipboard data (since it's not known at this point) + if (clipboard.m_clipboard.open(0)) { + clipboard.m_clipboard.empty(); + clipboard.m_clipboard.close(); + } + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + + // tell all other screens to take ownership of clipboard. tell the + // grabber that it's clipboard isn't dirty. + for (CClientList::iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + if (client == grabber) { + client->setClipboardDirty(info->m_id, false); + } + else { + client->grabClipboard(info->m_id); + } + } +} + +void +CServer::handleClipboardChanged(const CEvent& event, void* vclient) +{ + // ignore events from unknown clients + IClient* sender = reinterpret_cast(vclient); + if (m_clientSet.count(sender) == 0) { + return; + } + const IScreen::CClipboardInfo* info = + reinterpret_cast(event.getData()); + onClipboardChanged(sender, info->m_id, info->m_sequenceNumber); +} + +void +CServer::handleKeyDownEvent(const CEvent& event, void*) +{ + IPlatformScreen::CKeyInfo* info = + reinterpret_cast(event.getData()); + onKeyDown(info->m_key, info->m_mask, info->m_button); +} + +void +CServer::handleKeyUpEvent(const CEvent& event, void*) +{ + IPlatformScreen::CKeyInfo* info = + reinterpret_cast(event.getData()); + onKeyUp(info->m_key, info->m_mask, info->m_button); +} + +void +CServer::handleKeyRepeatEvent(const CEvent& event, void*) +{ + IPlatformScreen::CKeyInfo* info = + reinterpret_cast(event.getData()); + onKeyRepeat(info->m_key, info->m_mask, info->m_count, info->m_button); +} + +void +CServer::handleButtonDownEvent(const CEvent& event, void*) +{ + IPlatformScreen::CButtonInfo* info = + reinterpret_cast(event.getData()); + onMouseDown(info->m_button); +} + +void +CServer::handleButtonUpEvent(const CEvent& event, void*) +{ + IPlatformScreen::CButtonInfo* info = + reinterpret_cast(event.getData()); + onMouseUp(info->m_button); +} + +void +CServer::handleMotionPrimaryEvent(const CEvent& event, void*) +{ + IPlatformScreen::CMotionInfo* info = + reinterpret_cast(event.getData()); + onMouseMovePrimary(info->m_x, info->m_y); +} + +void +CServer::handleMotionSecondaryEvent(const CEvent& event, void*) +{ + IPlatformScreen::CMotionInfo* info = + reinterpret_cast(event.getData()); + onMouseMoveSecondary(info->m_x, info->m_y); +} + +void +CServer::handleWheelEvent(const CEvent& event, void*) +{ + IPlatformScreen::CWheelInfo* info = + reinterpret_cast(event.getData()); + onMouseWheel(info->m_wheel); +} + +void +CServer::handleScreensaverActivatedEvent(const CEvent& event, void*) +{ + onScreensaver(true); +} + +void +CServer::handleScreensaverDeactivatedEvent(const CEvent& event, void*) +{ + onScreensaver(false); +} + +void +CServer::handleSwitchWaitTimeout(const CEvent&, void*) +{ + // ignore if mouse is locked to screen + if (isLockedToScreen()) { + LOG((CLOG_DEBUG1 "locked to screen")); + stopSwitch(); + return; + } + + // switch screen + switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false); +} + +void +CServer::handleClientDisconnected(const CEvent&, void* vclient) +{ + // client has disconnected. it might be an old client or an + // active client. we don't care so just handle it both ways. + IClient* client = reinterpret_cast(vclient); + removeActiveClient(client); + removeOldClient(client); + delete client; +} + +void +CServer::handleClientCloseTimeout(const CEvent&, void* vclient) +{ + // client took too long to disconnect. just dump it. + IClient* client = reinterpret_cast(vclient); + LOG((CLOG_NOTE "forced disconnection of client \"%s\"", getName(client).c_str())); + removeOldClient(client); + delete client; +} + +void +CServer::onClipboardChanged(IClient* sender, ClipboardID id, UInt32 seqNum) +{ + CClipboardInfo& clipboard = m_clipboards[id]; + + // ignore update if sequence number is old + if (seqNum < clipboard.m_clipboardSeqNum) { + LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", clipboard.m_clipboardOwner.c_str(), id)); + return; + } + + // should be the expected client + assert(sender == m_clients.find(clipboard.m_clipboardOwner)->second); + + // get data + sender->getClipboard(id, &clipboard.m_clipboard); + + // ignore if data hasn't changed + CString data = clipboard.m_clipboard.marshall(); + if (data == clipboard.m_clipboardData) { + LOG((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id)); + return; + } + + // got new data + LOG((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + clipboard.m_clipboardData = data; + + // tell all clients except the sender that the clipboard is dirty + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + client->setClipboardDirty(id, client != sender); + } + + // send the new clipboard to the active screen + m_active->setClipboard(id, clipboard.m_clipboardData); +} + +void +CServer::onScreensaver(bool activated) +{ + LOG((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); + + if (activated) { + // save current screen and position + m_activeSaver = m_active; + m_xSaver = m_x; + m_ySaver = m_y; + + // jump to primary screen + if (m_active != m_primaryClient) { + switchScreen(m_primaryClient, 0, 0, true); + } + } + else { + // jump back to previous screen and position. we must check + // that the position is still valid since the screen may have + // changed resolutions while the screen saver was running. + if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { + // check position + IClient* screen = m_activeSaver; + SInt32 x, y, w, h; + screen->getShape(x, y, w, h); + SInt32 zoneSize = getJumpZoneSize(screen); + if (m_xSaver < x + zoneSize) { + m_xSaver = x + zoneSize; + } + else if (m_xSaver >= x + w - zoneSize) { + m_xSaver = x + w - zoneSize - 1; + } + if (m_ySaver < y + zoneSize) { + m_ySaver = y + zoneSize; + } + else if (m_ySaver >= y + h - zoneSize) { + m_ySaver = y + h - zoneSize - 1; + } + + // jump + switchScreen(screen, m_xSaver, m_ySaver, false); + } + + // reset state + m_activeSaver = NULL; + } + + // send message to all clients + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + client->screensaver(activated); + } +} + +void +CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, true)) { + return; + } + + // relay + m_active->keyDown(id, mask, button); +} + +void +CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button) +{ + LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, false)) { + return; + } + + // relay + m_active->keyUp(id, mask, button); +} + +void +CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); + assert(m_active != NULL); + + // handle command keys + if (onCommandKey(id, mask, false)) { + onCommandKey(id, mask, true); + return; + } + + // relay + m_active->keyRepeat(id, mask, count, button); +} + +void +CServer::onMouseDown(ButtonID id) +{ + LOG((CLOG_DEBUG1 "onMouseDown id=%d", id)); + assert(m_active != NULL); + + // relay + m_active->mouseDown(id); +} + +void +CServer::onMouseUp(ButtonID id) +{ + LOG((CLOG_DEBUG1 "onMouseUp id=%d", id)); + assert(m_active != NULL); + + // relay + m_active->mouseUp(id); +} + +bool +CServer::onMouseMovePrimary(SInt32 x, SInt32 y) +{ + LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); + + // mouse move on primary (server's) screen assert(m_primaryClient != NULL); + assert(m_active == m_primaryClient); - // close the primary screen - try { - LOG((CLOG_DEBUG1 "closing primary screen")); - m_primaryClient->close(); + // save position + m_x = x; + m_y = y; + + // get screen shape + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + SInt32 zoneSize = getJumpZoneSize(m_active); + + // see if we should change screens + EDirection dir; + if (x < ax + zoneSize) { + x -= zoneSize; + dir = kLeft; } - catch (...) { - // ignore + else if (x >= ax + aw - zoneSize) { + x += zoneSize; + dir = kRight; + } + else if (y < ay + zoneSize) { + y -= zoneSize; + dir = kTop; + } + else if (y >= ay + ah - zoneSize) { + y += zoneSize; + dir = kBottom; + } + else { + // still on local screen + noSwitch(x, y); + return false; } - // remove connection - removeConnection(m_primaryClient->getName()); - m_primaryClient = NULL; + // get jump destination + IClient* newScreen = getNeighbor(m_active, dir, x, y); + + // should we switch or not? + if (isSwitchOkay(newScreen, dir, x, y)) { + // switch screen + switchScreen(newScreen, x, y, false); + return true; + } + else { + return false; + } } void -CServer::addConnection(IClient* client) +CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) { - assert(client != NULL); + LOG((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); - LOG((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str())); - - { - CLock lock(&m_mutex); - - // name must be in our configuration - if (!m_config.isScreen(client->getName())) { - throw XUnknownClient(client->getName()); - } - - // can only have one screen with a given name at any given time - if (m_clients.count(client->getName()) != 0) { - throw XDuplicateClient(client->getName()); - } - - // save screen info - m_clients.insert(std::make_pair(client->getName(), client)); - LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str())); + // mouse move on secondary (client's) screen + assert(m_active != NULL); + if (m_active == m_primaryClient) { + // we're actually on the primary screen. this can happen + // when the primary screen begins processing a mouse move + // for a secondary screen, then the active (secondary) + // screen disconnects causing us to jump to the primary + // screen, and finally the primary screen finishes + // processing the mouse move, still thinking it's for + // a secondary screen. we just ignore the motion. + return; + } + + // save old position + const SInt32 xOld = m_x; + const SInt32 yOld = m_y; + + // accumulate motion + m_x += dx; + m_y += dy; + + // get screen shape + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + + // find direction of neighbor and get the neighbor + bool jump = true; + IClient* newScreen; + do { + EDirection dir; + if (m_x < ax) { + dir = kLeft; + } + else if (m_x > ax + aw - 1) { + dir = kRight; + } + else if (m_y < ay) { + dir = kTop; + } + else if (m_y > ay + ah - 1) { + dir = kBottom; + } + else { + // we haven't left the screen + newScreen = m_active; + jump = false; + + // if waiting and mouse is not on the border we're waiting + // on then stop waiting. also if it's not on the border + // then arm the double tap. + if (m_switchScreen != NULL) { + bool clearWait; + SInt32 zoneSize = m_primaryClient->getJumpZoneSize(); + switch (m_switchDir) { + case kLeft: + clearWait = (m_x >= ax + zoneSize); + break; + + case kRight: + clearWait = (m_x <= ax + aw - 1 - zoneSize); + break; + + case kTop: + clearWait = (m_y >= ay + zoneSize); + break; + + case kBottom: + clearWait = (m_y <= ay + ah - 1 + zoneSize); + break; + + default: + clearWait = false; + break; + } + if (clearWait) { + // still on local screen + noSwitch(m_x, m_y); + } + } + + // skip rest of block + break; + } + + // try to switch screen. get the neighbor. + newScreen = getNeighbor(m_active, dir, m_x, m_y); + + // see if we should switch + if (!isSwitchOkay(newScreen, dir, m_x, m_y)) { + newScreen = m_active; + jump = false; + } + } while (false); + + if (jump) { + // switch screens + switchScreen(newScreen, m_x, m_y, false); + } + else { + // same screen. clamp mouse to edge. + m_x = xOld + dx; + m_y = yOld + dy; + if (m_x < ax) { + m_x = ax; + LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", getName(m_active).c_str())); + } + else if (m_x > ax + aw - 1) { + m_x = ax + aw - 1; + LOG((CLOG_DEBUG2 "clamp to right of \"%s\"", getName(m_active).c_str())); + } + if (m_y < ay) { + m_y = ay; + LOG((CLOG_DEBUG2 "clamp to top of \"%s\"", getName(m_active).c_str())); + } + else if (m_y > ay + ah - 1) { + m_y = ay + ah - 1; + LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", getName(m_active).c_str())); + } + + // warp cursor if it moved. + if (m_x != xOld || m_y != yOld) { + LOG((CLOG_DEBUG2 "move on %s to %d,%d", getName(m_active).c_str(), m_x, m_y)); + m_active->mouseMove(m_x, m_y); + } } - runStatusJobs(); } void -CServer::removeConnection(const CString& name) +CServer::onMouseWheel(SInt32 delta) { - LOG((CLOG_DEBUG "removing connection \"%s\"", name.c_str())); - bool updateStatus; - { - CLock lock(&m_mutex); + LOG((CLOG_DEBUG1 "onMouseWheel %+d", delta)); + assert(m_active != NULL); - // find client - CClientList::iterator index = m_clients.find(name); - assert(index != m_clients.end()); + // relay + m_active->mouseWheel(delta); +} - // if this is active screen then we have to jump off of it - IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; - if (active == index->second && active != m_primaryClient) { - // record new position (center of primary screen) - m_primaryClient->getCursorCenter(m_x, m_y); - - // stop waiting to switch if we were - if (active == m_switchScreen) { - clearSwitchState(); - } - - // don't notify active screen since it probably already - // disconnected. - LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); - - // cut over - m_active = m_primaryClient; - - // enter new screen (unless we already have because of the - // screen saver) - if (m_activeSaver == NULL) { - m_primaryClient->enter(m_x, m_y, m_seqNum, - m_primaryClient->getToggleMask(), false); - } - } - - // if this screen had the cursor when the screen saver activated - // then we can't switch back to it when the screen saver - // deactivates. - if (m_activeSaver == index->second) { - m_activeSaver = NULL; - } - - // done with client - delete index->second; - m_clients.erase(index); - - // remove any thread for this client - m_clientThreads.erase(name); - - updateStatus = (m_clients.size() <= 1); +bool +CServer::addClient(IClient* client) +{ + CString name = getName(client); + if (m_clients.count(name) != 0) { + return false; } - if (updateStatus) { - runStatusJobs(); + // add event handlers + EVENTQUEUE->adoptHandler(IScreen::getShapeChangedEvent(), + client->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleShapeChanged, client)); + EVENTQUEUE->adoptHandler(IScreen::getClipboardGrabbedEvent(), + client->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleClipboardGrabbed, client)); + EVENTQUEUE->adoptHandler(IScreen::getClipboardChangedEvent(), + client->getEventTarget(), + new TMethodEventJob(this, + &CServer::handleClipboardChanged, client)); + + // add to list + m_clientSet.insert(client); + m_clients.insert(std::make_pair(name, client)); + return true; +} + +bool +CServer::removeClient(IClient* client) +{ + // return false if not in list + CClientList::iterator i = m_clients.find(getName(client)); + if (i == m_clients.end()) { + return false; + } + + // remove event handlers + EVENTQUEUE->removeHandler(IScreen::getShapeChangedEvent(), + client->getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(), + client->getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getClipboardChangedEvent(), + client->getEventTarget()); + + // remove from list + m_clients.erase(i); + m_clientSet.erase(client); + return true; +} + +void +CServer::closeClient(IClient* client, const char* msg) +{ + assert(client != m_primaryClient); + assert(msg != NULL); + + // send message to client. this message should cause the client + // to disconnect. we add this client to the closed client list + // and install a timer to remove the client if it doesn't respond + // quickly enough. we also remove the client from the active + // client list since we're not going to listen to it anymore. + // note that this method also works on clients that are not in + // the m_clients list. adoptClient() may call us with such a + // client. + LOG((CLOG_NOTE "disconnecting client \"%s\"", getName(client).c_str())); + + // send message + // FIXME -- avoid type cast (kinda hard, though) + ((CClientProxy*)client)->close(msg); + + // install timer. wait timeout seconds for client to close. + double timeout = 5.0; + CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(5.0, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, + new TMethodEventJob(this, + &CServer::handleClientCloseTimeout, client)); + + // move client to closing list + removeClient(client); + m_oldClients.insert(std::make_pair(client, timer)); + + // if this client is the active screen then we have to + // jump off of it + forceLeaveClient(client); +} + +void +CServer::closeClients(const CConfig& config) +{ + // collect the clients that are connected but are being dropped + // from the configuration (or who's canonical name is changing). + typedef std::set CRemovedClients; + CRemovedClients removed; + for (CClientList::iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + if (!config.isCanonicalName(index->first)) { + removed.insert(index->second); + } + } + + // don't close the primary client + removed.erase(m_primaryClient); + + // now close them. we collect the list then close in two steps + // because closeClient() modifies the collection we iterate over. + for (CRemovedClients::iterator index = removed.begin(); + index != removed.end(); ++index) { + closeClient(*index, kMsgCClose); } } - -// -// CServer::CClipboardInfo -// - -CString -CServer::XServerRethrow::getWhat() const throw() +void +CServer::removeActiveClient(IClient* client) { - return format("XServerRethrow", "child thread failed"); + if (removeClient(client)) { + forceLeaveClient(client); + EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); + if (m_clients.size() == 1 && m_oldClients.empty()) { + EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this)); + } + } +} + +void +CServer::removeOldClient(IClient* client) +{ + COldClients::iterator i = m_oldClients.find(client); + if (i != m_oldClients.end()) { + EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); + EVENTQUEUE->removeHandler(CEvent::kTimer, i->second); + EVENTQUEUE->deleteTimer(i->second); + m_oldClients.erase(i); + if (m_clients.size() == 1 && m_oldClients.empty()) { + EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this)); + } + } +} + +void +CServer::forceLeaveClient(IClient* client) +{ + IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; + if (active == client) { + // record new position (center of primary screen) + m_primaryClient->getCursorCenter(m_x, m_y); + + // stop waiting to switch to this client + if (active == m_switchScreen) { + stopSwitch(); + } + + // don't notify active screen since it has probably already + // disconnected. + LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", getName(active).c_str(), getName(m_primaryClient).c_str(), m_x, m_y)); + + // cut over + m_active = m_primaryClient; + + // enter new screen (unless we already have because of the + // screen saver) + if (m_activeSaver == NULL) { + m_primaryClient->enter(m_x, m_y, m_seqNum, + m_primaryClient->getToggleMask(), false); + } + } + + // if this screen had the cursor when the screen saver activated + // then we can't switch back to it when the screen saver + // deactivates. + if (m_activeSaver == client) { + m_activeSaver = NULL; + } } @@ -2014,259 +1589,3 @@ CServer::CClipboardInfo::CClipboardInfo() : { // do nothing } - -// --- transitional --- - - -void -CServer::clientConnecting(const CEvent&, void*) -{ - // accept client connection - IDataSocket* socket = m_listen->accept(); - if (socket == NULL) { - return; - } - - LOG((CLOG_NOTE "accepted client connection")); - - // filter socket's streams then wrap the result in a new socket - IInputStream* input = socket->getInputStream(); - IOutputStream* output = socket->getOutputStream(); - bool own = false; - if (m_streamFilterFactory != NULL) { - input = m_streamFilterFactory->createInput(input, own); - output = m_streamFilterFactory->createOutput(output, own); - own = true; - } - input = new CInputPacketStream(input, own); - output = new COutputPacketStream(output, own); - socket = new CDataSocket(socket, - new CInputOutputStream(input, output, true)); - -// FIXME -- may want to move these event handlers (but not the -// handshake timer?) and the handshake into a new proxy object. -// we save this proxy as a provisional connection. it calls -// back to us (or maybe sends an event) to notify of failure or -// success. failure is socket failure or protocol error. -// success returns a new proxy of the appropriate version. we -// need to verify the validity of the client's name then remove -// the provisional connection and install a true connection. -// if we keep the timer then when it expires we just remove and -// delete the provisional connection. if we install a true -// connection then we remove the timer. - CProvisionalClient* client; - try { - client = new CProvisionalClient(this, socket); - } - catch (XBase&) { - delete socket; - // handle error - return; - } - - // start timer for handshake - CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(30.0, client); - - // add client to client map - m_provisional.insert(client, timer); -} - -void -CServer::recvClientHello(const CEvent&, void* vsocket) -{ - IDataSocket* socket = reinterpret_cast(vsocket); - - LOG((CLOG_DEBUG1 "parsing hello reply")); - - CClientProxy* proxy = NULL; - CString name(""); - try { - // limit the maximum length of the hello - UInt32 n = socket->getInputStream()->getSize(); - if (n > kMaxHelloLength) { - LOG((CLOG_DEBUG1 "hello reply too long")); - throw XBadClient(); - } - - // parse the reply to hello - SInt16 major, minor; - CProtocolUtil::readf(socket->getInputStream(), kMsgHelloBack, - &major, &minor, &name); - - // disallow invalid version numbers - if (major <= 0 || minor < 0) { - throw XIncompatibleClient(major, minor); - } - - // convert name to canonical form (if any) -// FIXME -- need lock? - if (m_config.isScreen(name)) { - name = m_config.getCanonicalName(name); - } - - // create client proxy for highest version supported by the client - LOG((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor)); - if (major == 1) { - switch (minor) { - case 0: -// FIXME -- should pass socket, not input and output - proxy = new CClientProxy1_0(this, name, input, output); - break; - - case 1: -// FIXME -- should pass socket, not input and output - proxy = new CClientProxy1_1(this, name, input, output); - break; - } - } - - // hangup (with error) if version isn't supported - if (proxy == NULL) { - throw XIncompatibleClient(major, minor); - } - - // the proxy now owns the socket - socket = NULL; - - // add provisional client connection. this also checks if - // the client's name is okay (i.e. in the map and not in use). - addProvisionalClient(proxy); - - // negotiate - // FIXME - - // request the client's info and install a handler for it. note - // that the handshake timer is still going. - EVENTQUEUE->adoptHandler(IInputStream::getInputReadyEvent(), - socket->getInputStream(), - new TMethodEventJob(this, - &CServer::recvClientInfo, socket)); - LOG((CLOG_DEBUG1 "request info for client \"%s\"", name.c_str())); -// FIXME -- maybe should send this request some other way. -// could have the proxy itself handle the input. that makes sense -// but will have to check if that'll work. for one thing, the proxy -// will have to inform this object of any errors. it takes this -// object as a parameter so it probably can do that. -/* - proxy->open(); -*/ - return; - } - catch (XDuplicateClient& e) { - // client has duplicate name - LOG((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); - CProtocolUtil::writefNoError(proxy->getOutputStream(), kMsgEBusy); - } - catch (XUnknownClient& e) { - // client has unknown name - LOG((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); - CProtocolUtil::writefNoError(proxy->getOutputStream(), kMsgEUnknown); - } - catch (XIncompatibleClient& e) { - // client is incompatible - LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor())); - CProtocolUtil::writefNoError(socket->getOutputStream(), - kMsgEIncompatible, - kProtocolMajorVersion, kProtocolMinorVersion); - } - catch (XBadClient&) { - // client not behaving - LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); - CProtocolUtil::writefNoError(socket->getOutputStream(), kMsgEBad); - } - catch (XIO&) { - // client not behaving - LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str())); - CProtocolUtil::writefNoError(socket->getOutputStream(), kMsgEBad); - } - catch (XBase& e) { - // misc error - LOG((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what())); - } - -// FIXME -- really clean up, including event handlers and timer. -// should have a method for this. - delete proxy; - delete socket; -} - -void -CServer::recvClientInfo(const CEvent&, void* vsocket) -{ - // FIXME -- get client's info and proxy becomes first class citizen. - // adopt new input handler aimed at clientInput. remove timer. -} - -void -CServer::clientTimeout(const CEvent&, void* vsocket) -{ - // FIXME -- client failed to connect fast enough. if client is - // already first class then just ignore this (that should never - // happen though because timer events are synthesized in the - // getEvent() so there can't be a race, so don't fuss over - // checking). -} - -void -CServer::clientInput(const CEvent&, void* vsocket) -{ - // FIXME -- client has sent a message -} - -void -CServer::clientDisconnected(const CEvent&, void* vsocket) -{ - // FIXME -- handle disconnection of client -} - -void -CServer::createClientListener() -{ - LOG((CLOG_DEBUG1 "creating socket to listen for clients")); - - assert(m_socketFactory != NULL); - IListenSocket* listen = NULL; - try { - // create socket - listen = m_socketFactory->createListen(); - - // setup event handler - EVENTQUEUE->adoptHandler(IListenSocket::getConnectingEvent(), listen, - new TMethodEventJob(this, - &CServer::clientConnecting)); - - // bind listen address - LOG((CLOG_DEBUG1 "binding listen socket")); - listen->bind(m_config.getSynergyAddress()); - } - catch (XSocketAddressInUse& e) { - if (listen != NULL) { - EVENTQUEUE->removeHandler( - IListenSocket::getConnectingEvent(), listen); - delete listen; - } - setStatus(kError, e.what()); - LOG((CLOG_WARN "bind failed: %s", e.what())); - // FIXME -- throw retry in X seconds object. the caller should - // catch this and optionally wait X seconds and try again. - // alternatively we could install a timer and retry automatically. - throw; - } - catch (...) { - if (listen != NULL) { - EVENTQUEUE->removeHandler( - IListenSocket::getConnectingEvent(), listen); - delete listen; - } -/* FIXME -- set some status and log error - setStatus(kNotRunning); - setStatus(kError, e.what()); - LOG((CLOG_ERR "cannot listen for clients: %s", e.what())); -*/ - throw; - } - - m_listen = listen; - setStatus(kRunning); - LOG((CLOG_DEBUG1 "waiting for client connections")); -} diff --git a/lib/server/CServer.h b/lib/server/CServer.h index f7a9d735..874449e1 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -15,84 +15,40 @@ #ifndef CSERVER_H #define CSERVER_H -#include "IServer.h" -#include "IPrimaryScreenReceiver.h" #include "CConfig.h" #include "CClipboard.h" -#include "CCondVar.h" -#include "CMutex.h" -#include "CThread.h" -#include "CJobList.h" +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CEvent.h" #include "CStopwatch.h" -#include "stdlist.h" #include "stdmap.h" +#include "stdset.h" #include "stdvector.h" class CClientProxy; +class CClientProxyUnknown; +class CEventQueueTimer; class CPrimaryClient; class IClient; -class IDataSocket; -class IScreenFactory; -class IServerProtocol; -class ISocketFactory; -class IStreamFilterFactory; //! Synergy server /*! This class implements the top-level server algorithms for synergy. */ -class CServer : public IServer, public IPrimaryScreenReceiver { +class CServer { public: - enum EStatus { - kNotRunning, - kRunning, - kServerNameUnknown, - kError, - kMaxStatus - }; - /*! - The server will look itself up in the configuration using \c serverName - as its name. + Start the server with the configuration \p config and the primary + client (local screen) \p primaryClient. The client retains + ownership of \p primaryClient. */ - CServer(const CString& serverName); + CServer(const CConfig& config, CPrimaryClient* primaryClient); ~CServer(); //! @name manipulators //@{ - //! Open server - /*! - Open the server. Throws XScreenUnavailable if the server's - screen cannot be opened but might be available after some time. - Otherwise throws some other exception if the server's screen or - the server cannot be opened and retrying won't help. - */ - void open(); - - //! Server main loop - /*! - Run server's event loop and return when exitMainLoop() is called. - This must be called between a successful open() and close(). - - (cancellation point) - */ - void mainLoop(); - - //! Exit event loop - /*! - Force mainLoop() to return. This call can return before - mainLoop() does (i.e. asynchronously). This may only be - called between a successful open() and close(). - */ - void exitMainLoop(); - - //! Close server - /*! - Close the server. - */ - void close(); - //! Set configuration /*! Change the server's configuration. Returns true iff the new @@ -101,67 +57,26 @@ public: */ bool setConfig(const CConfig&); - //! Set screen factory + //! Add a client /*! - Sets the factory for creating screens. This must be set before - calling open(). This object takes ownership of the factory. + Adds \p client to the server. The client is adopted and will be + destroyed when the client disconnects or is disconnected. */ - void setScreenFactory(IScreenFactory*); + void adoptClient(IClient* client); - //! Set socket factory + //! Disconnect clients /*! - Sets the factory used to create a socket to connect to the server. - This must be set before calling mainLoop(). This object takes - ownership of the factory. + Disconnect clients. This tells them to disconnect but does not wait + for them to actually do so. The server sends the disconnected event + when they're all disconnected (or immediately if none are connected). + The caller can also just destroy this object to force the disconnection. */ - void setSocketFactory(ISocketFactory*); - - //! Set stream filter factory - /*! - Sets the factory used to filter the socket streams used to - communicate with the server. This object takes ownership - of the factory. - */ - void setStreamFilterFactory(IStreamFilterFactory*); - - //! Add a job to notify of status changes - /*! - The added job is run whenever the server's status changes in - certain externally visible ways. The client keeps ownership - of the job. - */ - void addStatusJob(IJob*); - - //! Remove a job to notify of status changes - /*! - Removes a previously added status notification job. A job can - remove itself when called but must not remove any other jobs. - The client keeps ownership of the job. - */ - void removeStatusJob(IJob*); + void disconnect(); //@} //! @name accessors //@{ - //! Get configuration - /*! - Returns the current configuration. - */ - void getConfig(CConfig*) const; - - //! Get canonical screen name - /*! - Returns the canonical version of a screen name. - */ - CString getCanonicalName(const CString& name) const; - - //! Get name - /*! - Returns the server's name passed to the c'tor - */ - CString getPrimaryScreenName() const; - //! Get number of connected clients /*! Returns the number of connected clients, including the server itself. @@ -174,33 +89,22 @@ public: */ void getClients(std::vector& list) const; - //! Get the status + //! Get error event type /*! - Returns the current status and status message. + Returns the error event type. This is sent when the server fails + for some reason. */ - EStatus getStatus(CString* = NULL) const; + static CEvent::Type getErrorEvent(); + + //! Get disconnected event type + /*! + Returns the disconnected event type. This is sent when all the + clients have disconnected. + */ + static CEvent::Type getDisconnectedEvent(); //@} - // IServer overrides - virtual void onError(); - virtual void onInfoChanged(const CString&, const CClientInfo&); - virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); - virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); - - // IPrimaryScreenReceiver overrides - virtual void onScreensaver(bool activated); - virtual void onOneShotTimerExpired(UInt32 id); - virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton); - virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton); - virtual void onKeyRepeat(KeyID, KeyModifierMask, - SInt32 count, KeyButton); - virtual void onMouseDown(ButtonID); - virtual void onMouseUp(ButtonID); - virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); - virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); - virtual void onMouseWheel(SInt32 delta); - protected: //! Handle special keys /*! @@ -208,36 +112,18 @@ protected: */ bool onCommandKey(KeyID, KeyModifierMask, bool down); - //! Exit event loop and note an error condition - /*! - Force mainLoop() to return by throwing an exception. This call - can return before mainLoop() does (i.e. asynchronously). This - may only be called between a successful open() and close(). - */ - void exitMainLoopWithError(); - private: - typedef std::list CThreadList; - - // notify status jobs of a change - void runStatusJobs() const; - - // set new status - void setStatus(EStatus, const char* msg = NULL); + // get canonical name of client + CString getName(const IClient*) const; // get the sides of the primary screen that have neighbors UInt32 getActivePrimarySides() const; - // handle mouse motion - bool onMouseMovePrimaryNoLock(SInt32 x, SInt32 y); - void onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy); - - // set the clipboard - void onClipboardChangedNoLock(ClipboardID, - UInt32 seqNum, const CString& data); - // returns true iff mouse should be locked to the current screen - bool isLockedToScreenNoLock() const; + bool isLockedToScreen() const; + + // returns the jump zone of the client + SInt32 getJumpZoneSize(IClient*) const; // change the active screen void switchScreen(IClient*, @@ -260,57 +146,98 @@ private: bool isSwitchOkay(IClient* dst, EDirection, SInt32 x, SInt32 y); - // update switch state due to a mouse move that doesn't try to - // switch screens. - void onNoSwitch(bool inTapZone); + // update switch state due to a mouse move at \p x, \p y that + // doesn't switch screens. + void noSwitch(SInt32 x, SInt32 y); - // reset switch wait state - void clearSwitchState(); + // stop switch timers + void stopSwitch(); + + // start two tap switch timer + void startSwitchTwoTap(); + + // arm the two tap switch timer if \p x, \p y is outside the tap zone + void armSwitchTwoTap(SInt32 x, SInt32 y); + + // stop the two tap switch timer + void stopSwitchTwoTap(); + + // returns true iff the two tap switch timer is started + bool isSwitchTwoTapStarted() const; + + // returns true iff should switch because of two tap + bool shouldSwitchTwoTap() const; + + // start delay switch timer + void startSwitchWait(SInt32 x, SInt32 y); + + // stop delay switch timer + void stopSwitchWait(); + + // returns true iff the delay switch timer is started + bool isSwitchWaitStarted() const; // send screen options to \c client void sendOptions(IClient* client) const; - // open/close the primary screen - void openPrimaryScreen(); - void closePrimaryScreen(); + // process options from configuration + void processOptions(); - // update the clipboard if owned by the primary screen - void updatePrimaryClipboard(ClipboardID); + // event handlers + void handleShapeChanged(const CEvent&, void*); + void handleClipboardGrabbed(const CEvent&, void*); + void handleClipboardChanged(const CEvent&, void*); + void handleKeyDownEvent(const CEvent&, void*); + void handleKeyUpEvent(const CEvent&, void*); + void handleKeyRepeatEvent(const CEvent&, void*); + void handleButtonDownEvent(const CEvent&, void*); + void handleButtonUpEvent(const CEvent&, void*); + void handleMotionPrimaryEvent(const CEvent&, void*); + void handleMotionSecondaryEvent(const CEvent&, void*); + void handleWheelEvent(const CEvent&, void*); + void handleScreensaverActivatedEvent(const CEvent&, void*); + void handleScreensaverDeactivatedEvent(const CEvent&, void*); + void handleSwitchWaitTimeout(const CEvent&, void*); + void handleClientDisconnected(const CEvent&, void*); + void handleClientCloseTimeout(const CEvent&, void*); - // close all clients that are *not* in config, not including the - // primary client. + // event processing + void onClipboardChanged(IClient* sender, + ClipboardID id, UInt32 seqNum); + void onScreensaver(bool activated); + void onKeyDown(KeyID, KeyModifierMask, KeyButton); + void onKeyUp(KeyID, KeyModifierMask, KeyButton); + void onKeyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton); + void onMouseDown(ButtonID); + void onMouseUp(ButtonID); + bool onMouseMovePrimary(SInt32 x, SInt32 y); + void onMouseMoveSecondary(SInt32 dx, SInt32 dy); + void onMouseWheel(SInt32 delta); + + // add client to list and attach event handlers for client + bool addClient(IClient*); + + // remove client from list and detach event handlers for client + bool removeClient(IClient*); + + // close a client + void closeClient(IClient*, const char* msg); + + // close clients not in \p config void closeClients(const CConfig& config); - // start a thread, adding it to the list of threads - CThread startThread(IJob* adopted); + // close all clients whether they've completed the handshake or not, + // except the primary client + void closeAllClients(); - // cancel running threads, waiting at most timeout seconds for - // them to finish. - void stopThreads(double timeout = -1.0); + // remove clients from internal state + void removeActiveClient(IClient*); + void removeOldClient(IClient*); - // reap threads, clearing finished threads from the thread list. - // doReapThreads does the work on the given thread list. - void reapThreads(); - void doReapThreads(CThreadList&); - - // thread method to accept incoming client connections - void acceptClients(void*); - - // thread method to do client interaction - void runClient(void*); - CClientProxy* handshakeClient(IDataSocket*); - - // connection list maintenance - void addConnection(IClient*); - void removeConnection(const CString& name); + // force the cursor off of \p client + void forceLeaveClient(IClient* client); private: - class XServerRethrow : public XBase { - protected: - // XBase overrides - virtual CString getWhat() const throw(); - }; - class CClipboardInfo { public: CClipboardInfo(); @@ -322,52 +249,27 @@ private: UInt32 m_clipboardSeqNum; }; - CMutex m_mutex; - - // the name of the primary screen - CString m_name; - - // true if we should exit the main loop by throwing an exception. - // this is used to propagate an exception from one of our threads - // to the mainLoop() thread. but, since we can't make a copy of - // the original exception, we return an arbitrary, unique - // exception type. the caller of mainLoop() cannot catch this - // exception except through XBase or .... - bool m_error; - - // how long to wait to bind our socket until we give up - double m_bindTimeout; - - // factories - IScreenFactory* m_screenFactory; - ISocketFactory* m_socketFactory; - IStreamFilterFactory* m_streamFilterFactory; - - // running threads - CThreadList m_threads; - CThread* m_acceptClientThread; - - // the screens - typedef std::map CClientList; - typedef std::map CClientThreadList; - - // all clients indexed by name - CClientList m_clients; - - // run thread of all secondary screen clients. does not include the - // primary screen's run thread. - CClientThreadList m_clientThreads; - // the primary screen client CPrimaryClient* m_primaryClient; + // all clients (including the primary client) indexed by name + typedef std::map CClientList; + typedef std::set CClientSet; + CClientList m_clients; + CClientSet m_clientSet; + + // all old connections that we're waiting to hangup + typedef std::map COldClients; + COldClients m_oldClients; + // the client with focus IClient* m_active; // the sequence number of enter messages UInt32 m_seqNum; - // current mouse position (in absolute secondary screen coordinates) + // current mouse position (in absolute screen coordinates) on + // whichever screen is active SInt32 m_x, m_y; // current configuration @@ -387,8 +289,7 @@ private: // state for delayed screen switching double m_switchWaitDelay; - UInt32 m_switchWaitTimer; - bool m_switchWaitEngaged; + CEventQueueTimer* m_switchWaitTimer; SInt32 m_switchWaitX, m_switchWaitY; // state for double-tap screen switching @@ -398,19 +299,8 @@ private: bool m_switchTwoTapArmed; SInt32 m_switchTwoTapZone; - // the status change jobs and status - CJobList m_statusJobs; - EStatus m_status; - CString m_statusMessage; - -//--- -/* - IListenSocket* m_listen; - - typedef std::map CProvisionalClients; - CProvisionalClients m_provisional; -*/ + static CEvent::Type s_errorEvent; + static CEvent::Type s_disconnectedEvent; }; #endif diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index cab17f22..6dfd8a6f 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -25,19 +25,21 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libserver.a libserver_a_SOURCES = \ + CClientListener.cpp \ CClientProxy.cpp \ CClientProxy1_0.cpp \ CClientProxy1_1.cpp \ + CClientProxyUnknown.cpp \ CConfig.cpp \ CPrimaryClient.cpp \ - CProvisionalClient.cpp \ CServer.cpp \ + CClientListener.h \ CClientProxy.h \ CClientProxy1_0.h \ CClientProxy1_1.h \ + CClientProxyUnknown.h \ CConfig.h \ CPrimaryClient.h \ - CProvisionalClient.h \ CServer.h \ $(NULL) INCLUDES = \ diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp index d85f840a..ecaf4f39 100644 --- a/lib/synergy/CProtocolUtil.cpp +++ b/lib/synergy/CProtocolUtil.cpp @@ -73,7 +73,6 @@ CProtocolUtil::vwritef(IStream* stream, } // fill buffer - // FIXME -- can we use alloca? UInt8* buffer = new UInt8[size]; writef(buffer, fmt, args); diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index 817c9dbf..9bbd4a82 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -14,41 +14,26 @@ #include "CScreen.h" #include "IPlatformScreen.h" -#include "IScreenReceiver.h" -#include "ISecondaryScreen.h" #include "ProtocolTypes.h" -#include "CLock.h" -#include "CThread.h" #include "CLog.h" +#include "IEventQueue.h" // // CScreen // -CScreen::CScreen(IPlatformScreen* platformScreen, IScreenReceiver* receiver) : +CScreen::CScreen(IPlatformScreen* platformScreen) : m_screen(platformScreen), - m_receiver(receiver), m_isPrimary(platformScreen->isPrimary()), m_enabled(false), m_entered(m_isPrimary), m_toggleKeys(0), m_screenSaverSync(true) { - // do nothing -} - -CScreen::~CScreen() -{ - delete m_screen; -} - -void -CScreen::open() -{ - CLock lock(&m_mutex); + assert(m_screen != NULL); // open screen - m_screen->open(this); + m_screen->setKeyState(this); // reset options resetOptions(); @@ -56,23 +41,20 @@ CScreen::open() LOG((CLOG_DEBUG "opened display")); } -void -CScreen::close() +CScreen::~CScreen() { - CLock lock(&m_mutex); + if (m_enabled) { + disable(); + } assert(!m_enabled); assert(m_entered == m_isPrimary); - - // close screen - m_screen->close(); - + delete m_screen; LOG((CLOG_DEBUG "closed display")); } void CScreen::enable() { - CLock lock(&m_mutex); assert(!m_enabled); m_screen->enable(); @@ -90,7 +72,6 @@ CScreen::enable() void CScreen::disable() { - CLock lock(&m_mutex); assert(m_enabled); if (!m_isPrimary && m_entered) { @@ -111,34 +92,9 @@ CScreen::disable() m_enabled = false; } -void -CScreen::mainLoop() -{ - // change our priority - CThread::getCurrentThread().setPriority(-14); - - // run event loop - try { - LOG((CLOG_DEBUG "entering event loop")); - m_screen->mainLoop(); - LOG((CLOG_DEBUG "exiting event loop")); - } - catch (...) { - LOG((CLOG_DEBUG "exiting event loop")); - throw; - } -} - -void -CScreen::exitMainLoop() -{ - m_screen->exitMainLoop(); -} - void CScreen::enter() { - CLock lock(&m_mutex); assert(m_entered == false); LOG((CLOG_INFO "entering screen")); @@ -157,7 +113,6 @@ CScreen::enter() bool CScreen::leave() { - CLock lock(&m_mutex); assert(m_entered == true); LOG((CLOG_INFO "leaving screen")); @@ -209,8 +164,6 @@ CScreen::grabClipboard(ClipboardID id) void CScreen::screensaver(bool activate) { - CLock lock(&m_mutex); - if (!m_isPrimary) { // activate/deactivation screen saver iff synchronization enabled if (m_screenSaverSync) { @@ -222,7 +175,6 @@ CScreen::screensaver(bool activate) void CScreen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button) { - CLock lock(&m_mutex); assert(!m_isPrimary); // check for ctrl+alt+del emulation @@ -256,7 +208,6 @@ void CScreen::keyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button) { - CLock lock(&m_mutex); assert(!m_isPrimary); // if we haven't seen this button go down then ignore it @@ -316,7 +267,6 @@ CScreen::keyRepeat(KeyID id, void CScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) { - CLock lock(&m_mutex); assert(!m_isPrimary); // if we haven't seen this button go down then ignore it @@ -372,8 +322,6 @@ CScreen::mouseWheel(SInt32 delta) void CScreen::resetOptions() { - CLock lock(&m_mutex); - // reset options m_numLockHalfDuplex = false; m_capsLockHalfDuplex = false; @@ -394,8 +342,6 @@ CScreen::resetOptions() void CScreen::setOptions(const COptionsList& options) { - CLock lock(&m_mutex); - // update options bool oldScreenSaverSync = m_screenSaverSync; for (UInt32 i = 0, n = options.size(); i < n; i += 2) { @@ -427,37 +373,18 @@ CScreen::setOptions(const COptionsList& options) m_screen->setOptions(options); } -UInt32 -CScreen::addOneShotTimer(double timeout) +void +CScreen::setSequenceNumber(UInt32 seqNum) { - return m_screen->addOneShotTimer(timeout); + return m_screen->setSequenceNumber(seqNum); } bool CScreen::isOnScreen() const { - CLock lock(&m_mutex); return m_entered; } -void -CScreen::getClipboard(ClipboardID id, - IClipboard* clipboard) const -{ - m_screen->getClipboard(id, clipboard); -} - -SInt32 -CScreen::getJumpZoneSize() const -{ - if (!m_isPrimary) { - return 0; - } - else { - return m_screen->getJumpZoneSize(); - } -} - bool CScreen::isLockedToScreen() const { @@ -488,6 +415,35 @@ CScreen::isLockedToScreen() const return false; } +SInt32 +CScreen::getJumpZoneSize() const +{ + if (!m_isPrimary) { + return 0; + } + else { + return m_screen->getJumpZoneSize(); + } +} + +void +CScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + m_screen->getCursorCenter(x, y); +} + +void* +CScreen::getEventTarget() const +{ + return m_screen; +} + +bool +CScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const +{ + return m_screen->getClipboard(id, clipboard); +} + void CScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { @@ -503,8 +459,6 @@ CScreen::getCursorPos(SInt32& x, SInt32& y) const void CScreen::updateKeys() { - CLock lock(&m_mutex); - // clear key state memset(m_keys, 0, sizeof(m_keys)); memset(m_fakeKeys, 0, sizeof(m_fakeKeys)); @@ -522,8 +476,6 @@ CScreen::updateKeys() void CScreen::releaseKeys() { - CLock lock(&m_mutex); - // release keys that we've synthesized a press for and only those // keys. we don't want to synthesize a release on a key the user // is still physically pressing. @@ -539,16 +491,12 @@ CScreen::releaseKeys() void CScreen::setKeyDown(KeyButton key) { - CLock lock(&m_mutex); - m_keys[key & 0xffu] |= kDown; } void CScreen::setToggled(KeyModifierMask mask) { - CLock lock(&m_mutex); - if (!isToggle(mask)) { return; } @@ -565,8 +513,6 @@ CScreen::setToggled(KeyModifierMask mask) void CScreen::addModifier(KeyModifierMask mask, KeyButtons& keys) { - CLock lock(&m_mutex); - // the modifier must have associated keys if (keys.empty()) { return; @@ -608,8 +554,6 @@ CScreen::setToggleState(KeyModifierMask mask) KeyButton CScreen::isAnyKeyDown() const { - CLock lock(&m_mutex); - for (UInt32 i = 1; i < 256; ++i) { if ((m_keys[i] & kDown) != 0) { return static_cast(i); @@ -621,8 +565,6 @@ CScreen::isAnyKeyDown() const bool CScreen::isKeyDown(KeyButton key) const { - CLock lock(&m_mutex); - key &= 0xffu; return (key != 0 && ((m_keys[key] & kDown) != 0)); } @@ -638,8 +580,6 @@ CScreen::isToggle(KeyModifierMask mask) const bool CScreen::isHalfDuplex(KeyModifierMask mask) const { - CLock lock(&m_mutex); - return ((mask == KeyModifierCapsLock && m_capsLockHalfDuplex) || (mask == KeyModifierNumLock && m_numLockHalfDuplex)); } @@ -647,8 +587,6 @@ CScreen::isHalfDuplex(KeyModifierMask mask) const bool CScreen::isModifierActive(KeyModifierMask mask) const { - CLock lock(&m_mutex); - MaskToKeys::const_iterator i = m_maskToKeys.find(mask); if (i == m_maskToKeys.end()) { return false; @@ -675,7 +613,6 @@ CScreen::isModifierActive(KeyModifierMask mask) const KeyModifierMask CScreen::getActiveModifiers() const { - CLock lock(&m_mutex); if (m_isPrimary) { // we don't keep primary key state up to date so get the // current state. @@ -688,8 +625,6 @@ bool CScreen::mapModifier(Keystrokes& keys, Keystrokes& undo, KeyModifierMask mask, bool desireActive) const { - CLock lock(&m_mutex); - // look up modifier MaskToKeys::const_iterator i = m_maskToKeys.find(mask); if (i == m_maskToKeys.end()) { @@ -751,7 +686,6 @@ CScreen::mapModifier(Keystrokes& keys, Keystrokes& undo, KeyModifierMask CScreen::getMaskForKey(KeyButton key) const { - CLock lock(&m_mutex); KeyToMask::const_iterator i = m_keyToMask.find(key); if (i == m_keyToMask.end()) { return 0; @@ -767,12 +701,8 @@ CScreen::enablePrimary() // get notified of screen saver activation/deactivation m_screen->openScreensaver(true); - // collect and send screen info - CClientInfo info; - m_screen->getShape(info.m_x, info.m_y, info.m_w, info.m_h); - m_screen->getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getJumpZoneSize(); - m_receiver->onInfoChanged(info); + // claim screen changed size + EVENTQUEUE->addEvent(CEvent(getShapeChangedEvent(), getEventTarget())); } void diff --git a/lib/synergy/CScreen.h b/lib/synergy/CScreen.h index 7a0f3d29..a20c021e 100644 --- a/lib/synergy/CScreen.h +++ b/lib/synergy/CScreen.h @@ -12,45 +12,32 @@ * GNU General Public License for more details. */ -#ifndef CSECONDARYSCREEN_H -#define CSECONDARYSCREEN_H +#ifndef CSCREEN_H +#define CSCREEN_H #include "IKeyState.h" +#include "IScreen.h" #include "ClipboardTypes.h" #include "MouseTypes.h" #include "OptionTypes.h" -#include "CMutex.h" #include "stdmap.h" class IClipboard; class IPlatformScreen; -class IScreenReceiver; //! Platform independent screen /*! This is a platform independent screen. It can work as either a primary or secondary screen. */ -class CScreen : public IKeyState { +class CScreen : public IScreen, public IKeyState { public: - CScreen(IPlatformScreen* platformScreen, IScreenReceiver*); + CScreen(IPlatformScreen* platformScreen); virtual ~CScreen(); //! @name manipulators //@{ - //! Open screen - /*! - Opens the screen. - */ - void open(); - - //! Close screen - /*! - Closes the screen. - */ - void close(); - //! Activate screen /*! Activate the screen, preparing it to report system and user events. @@ -66,21 +53,6 @@ public: */ void disable(); - //! Run event loop - /*! - Run the screen's event loop. This returns when it detects - the application should terminate or when exitMainLoop() is called. - mainLoop() may only be called between open() and close(). - */ - void mainLoop(); - - //! Exit event loop - /*! - Force mainLoop() to return. This call can return before - mainLoop() does (i.e. asynchronously). - */ - void exitMainLoop(); - //! Enter screen /*! Called when the user navigates to this screen. @@ -196,12 +168,11 @@ public: */ void setOptions(const COptionsList& options); - //! Install a one-shot timer + //! Set clipboard sequence number /*! - Installs a one-shot timer for \c timeout seconds and returns the - id of the timer. + Sets the sequence number to use in subsequent clipboard events. */ - UInt32 addOneShotTimer(double timeout); + void setSequenceNumber(UInt32); //@} //! @name accessors @@ -213,19 +184,6 @@ public: */ bool isOnScreen() const; - //! Get clipboard - /*! - Saves the contents of the system clipboard indicated by \c id. - */ - void getClipboard(ClipboardID id, IClipboard*) const; - - //! Get jump zone size - /*! - Returns the jump zone size, the size of the regions on the edges of - the screen that cause the cursor to jump to another screen. - */ - SInt32 getJumpZoneSize() const; - //! Get screen lock state /*! Returns true if there's any reason that the user should not be @@ -236,22 +194,30 @@ public: */ bool isLockedToScreen() const; - //! Get screen shape + //! Get jump zone size /*! - Returns the position of the upper-left corner of the screen in \c x - and \c y and the size of the screen in \c width and \c height. + Return the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. */ - void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const; + SInt32 getJumpZoneSize() const; - //! Get cursor position + //! Get cursor center position /*! - Returns the current position of the cursor in \c x,y. + Return the cursor center position which is where we park the + cursor to compute cursor motion deltas and should be far from + the edges of the screen, typically the center. */ - void getCursorPos(SInt32& x, SInt32& y) const; + void getCursorCenter(SInt32& x, SInt32& y) const; //@} + // IScreen overrides + virtual void* getEventTarget() const; + virtual bool getClipboard(ClipboardID id, IClipboard*) const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + // IKeyState overrides virtual void updateKeys(); virtual void releaseKeys(); @@ -307,14 +273,9 @@ private: typedef std::map MaskToKeys; typedef std::map KeyToMask; - CMutex m_mutex; - // our platform dependent screen IPlatformScreen* m_screen; - // our screen receiver - IScreenReceiver* m_receiver; - // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index 01ab4411..3e7c8b08 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -15,7 +15,7 @@ #ifndef ICLIENT_H #define ICLIENT_H -#include "IInterface.h" +#include "IScreen.h" #include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" @@ -27,43 +27,18 @@ This interface defines the methods necessary for the server to communicate with a client. */ -class IClient : public IInterface { +class IClient : public IScreen { public: //! @name manipulators //@{ - //! Open client - /*! - Open the client. Throw if the client cannot be opened. If the - screen cannot be opened but retrying later may succeed then throw - XScreenUnavailable. - */ - virtual void open() = 0; - - //! Client main loop - /*! - Run client's event loop. This method is typically called in a - separate thread and is exited by cancelling the thread. This - must be called between a successful open() and close(). - - (cancellation point) - */ - virtual void mainLoop() = 0; - - //! Close client - /*! - Close the client. - */ - virtual void close() = 0; - //! Enter screen /*! - Enter the screen. The cursor should be warped to \c xAbs,yAbs. - The client should record seqNum for future reporting of - clipboard changes. \c mask is the expected toggle button state - and the client should update its state to match. \c forScreensaver - is true iff the screen is being entered because the screen saver is - starting. + Enter the screen. The cursor should be warped to \p xAbs,yAbs. + \p mask is the expected toggle button state and the client should + update its state to match. \p forScreensaver is true iff the + screen is being entered because the screen saver is starting. + Subsequent clipboard events should report \p seqNum. */ virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, @@ -180,35 +155,14 @@ public: */ virtual CString getName() const = 0; - //! Get jump zone size - /*! - Called to get the jump zone size. - */ - virtual SInt32 getJumpZoneSize() const = 0; + //@} - //! Get screen shape - /*! - Return the position of the upper-left corner of the screen in \c x and - \c y and the size of the screen in \c width and \c height. - */ + // IScreen overrides + virtual void* getEventTarget() const = 0; + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; - - //! Get cursor position - /*! - Return the current position of the cursor in \c x and \c y. - */ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; - - //! Get cursor center position - /*! - Return the cursor center position which is where we park the - cursor to compute cursor motion deltas and should be far from - the edges of the screen, typically the center. - */ - virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; - - //@} }; #endif diff --git a/lib/synergy/IPlatformScreen.cpp b/lib/synergy/IPlatformScreen.cpp new file mode 100644 index 00000000..78d1e436 --- /dev/null +++ b/lib/synergy/IPlatformScreen.cpp @@ -0,0 +1,149 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "IPlatformScreen.h" + +// +// IPlatformScreen +// + +CEvent::Type IPlatformScreen::s_keyDownEvent = CEvent::kUnknown; +CEvent::Type IPlatformScreen::s_keyUpEvent = CEvent::kUnknown; +CEvent::Type IPlatformScreen::s_keyRepeatEvent = CEvent::kUnknown; +CEvent::Type IPlatformScreen::s_buttonDownEvent = CEvent::kUnknown; +CEvent::Type IPlatformScreen::s_buttonUpEvent = CEvent::kUnknown; +CEvent::Type IPlatformScreen::s_motionPrimaryEvent = CEvent::kUnknown; +CEvent::Type IPlatformScreen::s_motionSecondaryEvent = CEvent::kUnknown; +CEvent::Type IPlatformScreen::s_wheelEvent = CEvent::kUnknown; +CEvent::Type IPlatformScreen::s_ssActivatedEvent = CEvent::kUnknown; +CEvent::Type IPlatformScreen::s_ssDeactivatedEvent = CEvent::kUnknown; + +CEvent::Type +IPlatformScreen::getKeyDownEvent() +{ + return CEvent::registerTypeOnce(s_keyDownEvent, + "IPlatformScreen::keyDown"); +} + +CEvent::Type +IPlatformScreen::getKeyUpEvent() +{ + return CEvent::registerTypeOnce(s_keyUpEvent, + "IPlatformScreen::keyUp"); +} + +CEvent::Type +IPlatformScreen::getKeyRepeatEvent() +{ + return CEvent::registerTypeOnce(s_keyRepeatEvent, + "IPlatformScreen::keyRepeat"); +} + +CEvent::Type +IPlatformScreen::getButtonDownEvent() +{ + return CEvent::registerTypeOnce(s_buttonDownEvent, + "IPlatformScreen::buttonDown"); +} + +CEvent::Type +IPlatformScreen::getButtonUpEvent() +{ + return CEvent::registerTypeOnce(s_buttonUpEvent, + "IPlatformScreen::buttonUp"); +} + +CEvent::Type +IPlatformScreen::getMotionOnPrimaryEvent() +{ + return CEvent::registerTypeOnce(s_motionPrimaryEvent, + "IPlatformScreen::motionPrimary"); +} + +CEvent::Type +IPlatformScreen::getMotionOnSecondaryEvent() +{ + return CEvent::registerTypeOnce(s_motionSecondaryEvent, + "IPlatformScreen::motionSecondary"); +} + +CEvent::Type +IPlatformScreen::getWheelEvent() +{ + return CEvent::registerTypeOnce(s_wheelEvent, + "IPlatformScreen::wheel"); +} + +CEvent::Type +IPlatformScreen::getScreensaverActivatedEvent() +{ + return CEvent::registerTypeOnce(s_ssActivatedEvent, + "IPlatformScreen::screensaverActivated"); +} + +CEvent::Type +IPlatformScreen::getScreensaverDeactivatedEvent() +{ + return CEvent::registerTypeOnce(s_ssDeactivatedEvent, + "IPlatformScreen::screensaverDeactivated"); +} + + +// +// IPlatformScreen::CKeyInfo +// + +IPlatformScreen::CKeyInfo::CKeyInfo(KeyID id, + KeyModifierMask mask, KeyButton button, SInt32 count) : + m_key(id), + m_mask(mask), + m_button(button), + m_count(count) +{ + // do nothing +} + + +// +// IPlatformScreen::CButtonInfo +// + +IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) : + m_button(id) +{ + // do nothing +} + + +// +// IPlatformScreen::CMotionInfo +// + +IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) : + m_x(x), + m_y(y) +{ + // do nothing +} + + +// +// IPlatformScreen::CWheelInfo +// + +IPlatformScreen::CWheelInfo::CWheelInfo(SInt32 wheel) : + m_wheel(wheel) +{ + // do nothing +} diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index 149c5526..456d58d9 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -15,10 +15,12 @@ #ifndef IPLATFORMSCREEN_H #define IPLATFORMSCREEN_H +#include "IScreen.h" #include "IPrimaryScreen.h" #include "ISecondaryScreen.h" #include "ClipboardTypes.h" #include "OptionTypes.h" +#include "CEvent.h" class IClipboard; class IKeyState; @@ -27,27 +29,60 @@ class IKeyState; /*! This interface defines the methods common to all platform dependent screen implementations that are used by both primary and secondary -screens. +screens. A platform screen is expected to post the events defined +in \c IScreen when appropriate. It should also post events defined +in \c IPlatformScreen if acting as the primary screen. The target +on the events should be the value returned by \c getEventTarget(). */ -class IPlatformScreen : public IPrimaryScreen, public ISecondaryScreen { +class IPlatformScreen : public IScreen, + public IPrimaryScreen, public ISecondaryScreen { public: + //! Key event data + class CKeyInfo { + public: + CKeyInfo(KeyID, KeyModifierMask, KeyButton, SInt32 count); + + public: + KeyID m_key; + KeyModifierMask m_mask; + KeyButton m_button; + SInt32 m_count; + }; + //! Button event data + class CButtonInfo { + public: + CButtonInfo(ButtonID); + + public: + ButtonID m_button; + }; + //! Motion event data + class CMotionInfo { + public: + CMotionInfo(SInt32 x, SInt32 y); + + public: + SInt32 m_x; + SInt32 m_y; + }; + //! Wheel motion event data + class CWheelInfo { + public: + CWheelInfo(SInt32); + + public: + SInt32 m_wheel; + }; + //! @name manipulators //@{ - //! Open screen + //! Set the key state /*! - Called to open and initialize the screen. Throw XScreenUnavailable - if the screen cannot be opened but retrying later may succeed. - Otherwise throw some other XScreenOpenFailure exception. + Sets the key state object. This object tracks keyboard state and + the screen is expected to keep it up to date. */ - virtual void open(IKeyState*) = 0; - - //! Close screen - /*! - Called to close the screen. close() should quietly ignore calls - that don't have a matching successful call to open(). - */ - virtual void close() = 0; + virtual void setKeyState(IKeyState*) = 0; //! Enable screen /*! @@ -64,21 +99,6 @@ public: */ virtual void disable() = 0; - //! Run event loop - /*! - Run the event loop and return when exitMainLoop() is called. - This must be called between a successful open() and close(). - */ - virtual void mainLoop() = 0; - - //! Exit event loop - /*! - Force mainLoop() to return. This call can return before - mainLoop() does (i.e. asynchronously). This may only be - called between a successful open() and close(). - */ - virtual void exitMainLoop() = 0; - //! Enter screen /*! Called when the user navigates to this screen. @@ -102,22 +122,19 @@ public: //! Check clipboard owner /*! - Check ownership of all clipboards and notify an IScreenReceiver (set - through some other interface) if any changed. This is used as a - backup in case the system doesn't reliably report clipboard ownership - changes. + Check ownership of all clipboards and post grab events for any that + have changed. This is used as a backup in case the system doesn't + reliably report clipboard ownership changes. */ virtual void checkClipboards() = 0; //! Open screen saver /*! Open the screen saver. If \c notify is true then this object must - call an IScreenEventHandler's (set through some other interface) - onScreenSaver() when the screensaver activates or deactivates until - it's closed. If \c notify is false then the screen saver is - disabled on open and restored on close. + send events when the screen saver activates or deactivates until + \c closeScreensaver() is called. If \c notify is false then the + screen saver is disabled and restored on \c closeScreensaver(). */ -// XXX -- pass an interface pointer, not a notify flag virtual void openScreensaver(bool notify) = 0; //! Close screen saver @@ -154,6 +171,12 @@ public: */ virtual void updateKeys() = 0; + //! Set clipboard sequence number + /*! + Sets the sequence number to use in subsequent clipboard events. + */ + virtual void setSequenceNumber(UInt32) = 0; + //@} //! @name accessors //@{ @@ -164,35 +187,49 @@ public: */ virtual bool isPrimary() const = 0; - //! Get clipboard + //! Get key down event type. Event data is CKeyInfo*, count == 1. + static CEvent::Type getKeyDownEvent(); + //! Get key up event type. Event data is CKeyInfo*, count == 1. + static CEvent::Type getKeyUpEvent(); + //! Get key repeat event type. Event data is CKeyInfo*. + static CEvent::Type getKeyRepeatEvent(); + //! Get button down event type. Event data is CButtonInfo*. + static CEvent::Type getButtonDownEvent(); + //! Get button up event type. Event data is CButtonInfo*. + static CEvent::Type getButtonUpEvent(); + //! Get mouse motion on the primary screen event type /*! - Save the contents of the clipboard indicated by \c id and return - true iff successful. + Event data is CMotionInfo* and the values are an absolute position. */ - virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; - - //! Get screen shape + static CEvent::Type getMotionOnPrimaryEvent(); + //! Get mouse motion on a secondary screen event type /*! - Return the position of the upper-left corner of the screen in \c x and - \c y and the size of the screen in \c w (width) and \c h (height). + Event data is CMotionInfo* and the values are motion deltas not + absolute coordinates. */ - virtual void getShape(SInt32& x, SInt32& y, - SInt32& w, SInt32& h) const = 0; - - //! Get cursor position - /*! - Return the current position of the cursor in \c x and \c y. - */ - virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + static CEvent::Type getMotionOnSecondaryEvent(); + //! Get mouse wheel event type. Event data is CWheelInfo*. + static CEvent::Type getWheelEvent(); + //! Get screensaver activated event type + static CEvent::Type getScreensaverActivatedEvent(); + //! Get screensaver deactivated event type + static CEvent::Type getScreensaverDeactivatedEvent(); //@} + // IScreen overrides + virtual void* getEventTarget() const = 0; + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides) = 0; virtual void warpCursor(SInt32 x, SInt32 y) = 0; - virtual UInt32 addOneShotTimer(double timeout) = 0; virtual SInt32 getJumpZoneSize() const = 0; virtual bool isAnyMouseButtonDown() const = 0; + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; virtual const char* getKeyName(KeyButton) const = 0; // ISecondaryScreen overrides @@ -205,6 +242,18 @@ public: const IKeyState& keyState, KeyID id, KeyModifierMask desiredMask, bool isAutoRepeat) const = 0; + +private: + static CEvent::Type s_keyDownEvent; + static CEvent::Type s_keyUpEvent; + static CEvent::Type s_keyRepeatEvent; + static CEvent::Type s_buttonDownEvent; + static CEvent::Type s_buttonUpEvent; + static CEvent::Type s_motionPrimaryEvent; + static CEvent::Type s_motionSecondaryEvent; + static CEvent::Type s_wheelEvent; + static CEvent::Type s_ssActivatedEvent; + static CEvent::Type s_ssDeactivatedEvent; }; #endif diff --git a/lib/synergy/IPrimaryScreen.h b/lib/synergy/IPrimaryScreen.h index 560436f3..fb3f6ccb 100644 --- a/lib/synergy/IPrimaryScreen.h +++ b/lib/synergy/IPrimaryScreen.h @@ -25,7 +25,6 @@ primary screen implementations. */ class IPrimaryScreen : public IInterface { public: -// XXX -- may need an interface for sending events //! @name manipulators //@{ @@ -46,15 +45,6 @@ public: */ virtual void warpCursor(SInt32 x, SInt32 y) = 0; - //! Install a one-shot timer - /*! - Installs a one-shot timer for \c timeout seconds and returns the - id of the timer. - */ -// XXX -- need to specify the receiver of the event. or we should -// pass a job. need a method to remove the timer? - virtual UInt32 addOneShotTimer(double timeout) = 0; - //@} //! @name accessors //@{ @@ -72,6 +62,14 @@ public: */ virtual bool isAnyMouseButtonDown() const = 0; + //! Get cursor center position + /*! + Return the cursor center position which is where we park the + cursor to compute cursor motion deltas and should be far from + the edges of the screen, typically the center. + */ + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + //! Get name of key /*! Return a string describing the given key. diff --git a/lib/synergy/IPrimaryScreenReceiver.h b/lib/synergy/IPrimaryScreenReceiver.h deleted file mode 100644 index 44b1beee..00000000 --- a/lib/synergy/IPrimaryScreenReceiver.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef IPRIMARYSCREENRECEIVER_H -#define IPRIMARYSCREENRECEIVER_H - -#include "IInterface.h" -#include "KeyTypes.h" -#include "MouseTypes.h" - -//! Primary screen event receiver interface -/*! -The interface for receiving notification of events on the primary -screen. The server implements this interface to handle user input. -Platform dependent primary screen implementation will need to take -an IPrimaryScreenReceiver* and notify it of events. -*/ -class IPrimaryScreenReceiver : public IInterface { -public: - //! Notify of screen saver change - /*! - Called when the screensaver is activated or deactivated. - */ - virtual void onScreensaver(bool activated) = 0; - - //! Notify of one-shot timer expiration - /*! - Called when a one-shot timer expires. - */ - virtual void onOneShotTimerExpired(UInt32 id) = 0; - - // call to notify of events. onMouseMovePrimary() returns - // true iff the mouse enters a jump zone and jumps. - //! Notify of key press - virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton) = 0; - //! Notify of key release - virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton) = 0; - //! Notify of key repeat - virtual void onKeyRepeat(KeyID, KeyModifierMask, - SInt32 count, KeyButton) = 0; - //! Notify of mouse button press - virtual void onMouseDown(ButtonID) = 0; - //! Notify of mouse button release - virtual void onMouseUp(ButtonID) = 0; - //! Notify of mouse motion - /*! - Called when the mouse has moved while on the primary screen. \c x - and \c y are the absolute screen position of the mouse. Return - true iff the mouse enters a jump zone and jumps. - */ - virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; - //! Notify of mouse motion - /*! - Called when the mouse has moved while on the secondary screen. - \c dx and \c dy are the relative motion from the last position. - */ - virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; - //! Notify of mouse wheen motion - virtual void onMouseWheel(SInt32 delta) = 0; -}; - -#endif diff --git a/lib/synergy/IScreen.cpp b/lib/synergy/IScreen.cpp new file mode 100644 index 00000000..1c55dc5c --- /dev/null +++ b/lib/synergy/IScreen.cpp @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "IScreen.h" + +// +// IScreen +// + +CEvent::Type IScreen::s_errorEvent = CEvent::kUnknown; +CEvent::Type IScreen::s_shapeChangedEvent = CEvent::kUnknown; +CEvent::Type IScreen::s_clipboardGrabbedEvent = CEvent::kUnknown; +CEvent::Type IScreen::s_clipboardChangedEvent = CEvent::kUnknown; + +CEvent::Type +IScreen::getErrorEvent() +{ + return CEvent::registerTypeOnce(s_errorEvent, + "IScreen::error"); +} + +CEvent::Type +IScreen::getShapeChangedEvent() +{ + return CEvent::registerTypeOnce(s_shapeChangedEvent, + "IScreen::shapeChanged"); +} + +CEvent::Type +IScreen::getClipboardGrabbedEvent() +{ + return CEvent::registerTypeOnce(s_clipboardGrabbedEvent, + "IScreen::clipboardGrabbed"); +} + +CEvent::Type +IScreen::getClipboardChangedEvent() +{ + return CEvent::registerTypeOnce(s_clipboardChangedEvent, + "IScreen::clipboardChanged"); +} diff --git a/lib/synergy/IScreen.h b/lib/synergy/IScreen.h new file mode 100644 index 00000000..7af925cf --- /dev/null +++ b/lib/synergy/IScreen.h @@ -0,0 +1,106 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISCREEN_H +#define ISCREEN_H + +#include "IInterface.h" +#include "ClipboardTypes.h" +#include "CEvent.h" + +class IClipboard; + +//! Screen interface +/*! +This interface defines the methods common to all screens. +*/ +class IScreen : public IInterface { +public: + struct CClipboardInfo { + public: + ClipboardID m_id; + UInt32 m_sequenceNumber; + }; + + //! @name accessors + //@{ + + //! Get event target + /*! + Returns the target used for events created by this object. + */ + virtual void* getEventTarget() const = 0; + + //! Get clipboard + /*! + Save the contents of the clipboard indicated by \c id and return + true iff successful. + */ + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; + + //! Get screen shape + /*! + Return the position of the upper-left corner of the screen in \c x and + \c y and the size of the screen in \c width and \c height. + */ + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + + //! Get cursor position + /*! + Return the current position of the cursor in \c x and \c y. + */ + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + //! Get error event type + /*! + Returns the error event type. This is sent whenever the screen has + failed for some reason (e.g. the X Windows server died). + */ + static CEvent::Type getErrorEvent(); + + //! Get shape changed event type + /*! + Returns the shape changed event type. This is sent whenever the + screen's shape changes, the cursor center moves, or the jump zone + size changes. + */ + static CEvent::Type getShapeChangedEvent(); + + //! Get clipboard grabbed event type + /*! + Returns the clipboard grabbed event type. This is sent whenever the + clipboard is grabbed by some other application so we don't own it + anymore. The data is a pointer to a CClipboardInfo. + */ + static CEvent::Type getClipboardGrabbedEvent(); + + //! Get clipboard changed event type + /*! + Returns the clipboard changed event type. This is sent whenever the + contents of the clipboard has changed. The data is a pointer to a + CClipboardInfo. + */ + static CEvent::Type getClipboardChangedEvent(); + + //@} + +private: + static CEvent::Type s_errorEvent; + static CEvent::Type s_shapeChangedEvent; + static CEvent::Type s_clipboardGrabbedEvent; + static CEvent::Type s_clipboardChangedEvent; +}; + +#endif diff --git a/lib/synergy/IScreenFactory.h b/lib/synergy/IScreenFactory.h deleted file mode 100644 index 8a5ab410..00000000 --- a/lib/synergy/IScreenFactory.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2003 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef ISCREENFACTORY_H -#define ISCREENFACTORY_H - -#include "IInterface.h" - -class IPrimaryScreenReceiver; -class IPlatformScreen; -class IScreenReceiver; - -//! Primary screen factory interface -/*! -This interface provides factory methods to create primary and -secondary screens. -*/ -class IScreenFactory : public IInterface { -public: - //! Create screen - /*! - Create and return a screen. The caller must delete the returned - object. The screen is a primary screen iff the IPrimaryScreenReceiver - is not NULL. - */ - virtual IPlatformScreen* - create(IScreenReceiver*, IPrimaryScreenReceiver*) = 0; -}; - -#endif diff --git a/lib/synergy/IScreenReceiver.h b/lib/synergy/IScreenReceiver.h deleted file mode 100644 index 0dd55075..00000000 --- a/lib/synergy/IScreenReceiver.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef ISCREENRECEIVER_H -#define ISCREENRECEIVER_H - -#include "IInterface.h" -#include "ClipboardTypes.h" -#include "ProtocolTypes.h" -#include "CString.h" - -//! Screen event receiver interface -/*! -This interface defines the methods common to most types that receive -events for changes to a screen. Note that the methods in this -interface are similar to the methods in IServer but have different -parameters. This interface is suitable for client-side types. -*/ -class IScreenReceiver : public IInterface { -public: - //! Notify of error - /*! - Called when the screen is unexpectedly closing. This implies that - the screen is no longer usable and that the program should close - the screen and probably terminate. - */ - virtual void onError() = 0; - - //! Notify of client screen change - /*! - Called when the client's info has changed. For example, when the - screen resolution has changed. - */ - virtual void onInfoChanged(const CClientInfo&) = 0; - - //! Notify of clipboad grab - /*! - Called when the clipboard was grabbed by another program and, - therefore, we no longer own it. Returns true if the grab was - honored, false otherwise. - */ - virtual bool onGrabClipboard(ClipboardID) = 0; - - //! Notify of new clipboard data - /*! - Called when the data on the clipboard has changed because some - other program has changed it. \c data will have marshalled - clipboard data. - */ - virtual void onClipboardChanged(ClipboardID, - const CString& data) = 0; -}; - -#endif diff --git a/lib/synergy/IScreenSaver.h b/lib/synergy/IScreenSaver.h index f2da8114..9076b309 100644 --- a/lib/synergy/IScreenSaver.h +++ b/lib/synergy/IScreenSaver.h @@ -16,6 +16,7 @@ #define ISCREENSAVER_H #include "IInterface.h" +#include "CEvent.h" //! Screen saver interface /*! diff --git a/lib/synergy/IServer.h b/lib/synergy/IServer.h deleted file mode 100644 index 42b7a675..00000000 --- a/lib/synergy/IServer.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef ISERVER_H -#define ISERVER_H - -#include "IInterface.h" -#include "ClipboardTypes.h" -#include "CString.h" - -class CClientInfo; - -//! Server interface -/*! -This interface defines the methods necessary for clients to -communicate with the server. Note that the methods in this -interface are similar to the methods in IScreenReceiver but -include extra parameters. This interface is suitable for -server-side client proxies. Client-side objects should use -the IScreenReceiver interface since the extra parameters are -meaningless on the client-side. -*/ -class IServer : public IInterface { -public: - //! @name manipulators - //@{ - - //! Notify of error - /*! - Called when the screen is unexpectedly closing. This implies that - the screen is no longer usable and that the program should close - the screen and probably terminate. - */ - virtual void onError() = 0; - - //! Notify of client screen change - /*! - Called when the client's info has changed. - */ - virtual void onInfoChanged(const CString& clientName, - const CClientInfo&) = 0; - - //! Notify of clipboad grab - /*! - Called when the clipboard was grabbed by another program and, - therefore, we no longer own it. Returns true if the grab was - honored, false otherwise. - */ - virtual bool onGrabClipboard(const CString& clientName, - ClipboardID, UInt32 seqNum) = 0; - - //! Notify of new clipboard data - /*! - Called when the data on the clipboard has changed because some - other program has changed it. \c data has the marshalled clipboard - data. - */ - virtual void onClipboardChanged(ClipboardID, - UInt32 seqNum, const CString& data) = 0; - - //@} -}; - -#endif diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 1203ae2f..ed4763fa 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -29,6 +29,8 @@ libsynergy_a_SOURCES = \ CPacketStreamFilter.cpp \ CProtocolUtil.cpp \ CScreen.cpp \ + IPlatformScreen.cpp \ + IScreen.cpp \ XScreen.cpp \ XSynergy.cpp \ CClipboard.h \ @@ -41,12 +43,9 @@ libsynergy_a_SOURCES = \ IKeyState.h \ IPlatformScreen.h \ IPrimaryScreen.h \ - IPrimaryScreenReceiver.h \ - IScreenFactory.h \ - IScreenReceiver.h \ + IScreen.h \ IScreenSaver.h \ ISecondaryScreen.h \ - IServer.h \ KeyTypes.h \ MouseTypes.h \ OptionTypes.h \ From 901a76df0d7ec1e945aa229cff75c9dc2face2db Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 14 Feb 2004 16:30:27 +0000 Subject: [PATCH 577/807] Minor cleanup. --- cmd/synergys/CServerTaskBarReceiver.cpp | 2 -- cmd/synergys/synergys.cpp | 48 ++++++++++++++++++++----- lib/server/CPrimaryClient.cpp | 2 +- lib/server/CPrimaryClient.h | 4 +-- 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp index 93b182f4..fb634d50 100644 --- a/cmd/synergys/CServerTaskBarReceiver.cpp +++ b/cmd/synergys/CServerTaskBarReceiver.cpp @@ -33,7 +33,6 @@ CServerTaskBarReceiver::~CServerTaskBarReceiver() // do nothing } -#include "CLog.h" void CServerTaskBarReceiver::updateStatus(CServer* server, const CString& errorMsg) { @@ -62,7 +61,6 @@ CServerTaskBarReceiver::updateStatus(CServer* server, const CString& errorMsg) // let subclasses have a go onStatusChanged(server); -LOG((CLOG_INFO "### status: %s", getToolTip().c_str())); } // tell task bar diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index ad5ecde7..5956d011 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -118,6 +118,7 @@ createScreen() // static CServer* s_server = NULL; +static CScreen* s_serverScreen = NULL; static CPrimaryClient* s_primaryClient = NULL; static CClientListener* s_listener = NULL; static CServerTaskBarReceiver* s_taskBarReceiver = NULL; @@ -170,6 +171,20 @@ closeClientListener(CClientListener* listen) } } +static +CScreen* +openServerScreen() +{ + return createScreen(); +} + +static +void +closeServerScreen(CScreen* screen) +{ + delete screen; +} + static void handleScreenError(const CEvent&, void*) @@ -179,10 +194,9 @@ handleScreenError(const CEvent&, void*) static CPrimaryClient* -openPrimaryClient(const CString& name) +openPrimaryClient(const CString& name, CScreen* screen) { LOG((CLOG_DEBUG1 "creating primary screen")); - CScreen* screen = createScreen(); CPrimaryClient* primaryClient = new CPrimaryClient(name, screen); EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), primaryClient->getEventTarget(), @@ -280,13 +294,16 @@ bool startServer() { double retryTime; + CScreen* serverScreen = NULL; CPrimaryClient* primaryClient = NULL; CClientListener* listener = NULL; try { CString name = ARG->m_config.getCanonicalName(ARG->m_name); - primaryClient = openPrimaryClient(name); + serverScreen = openServerScreen(); + primaryClient = openPrimaryClient(name, serverScreen); listener = openClientListener(ARG->m_config.getSynergyAddress()); s_server = openServer(ARG->m_config, primaryClient); + s_serverScreen = serverScreen; s_primaryClient = primaryClient; s_listener = listener; updateStatus(); @@ -297,6 +314,7 @@ startServer() LOG((CLOG_WARN "cannot open primary screen: %s", e.what())); closeClientListener(listener); closePrimaryClient(primaryClient); + closeServerScreen(serverScreen); updateStatus(CString("cannot open primary screen: ") + e.what()); retryTime = e.getRetryTime(); } @@ -304,6 +322,7 @@ startServer() LOG((CLOG_WARN "cannot listen for clients: %s", e.what())); closeClientListener(listener); closePrimaryClient(primaryClient); + closeServerScreen(serverScreen); updateStatus(CString("cannot listen for clients: ") + e.what()); retryTime = 10.0; } @@ -311,12 +330,14 @@ startServer() LOG((CLOG_CRIT "cannot open primary screen: %s", e.what())); closeClientListener(listener); closePrimaryClient(primaryClient); + closeServerScreen(serverScreen); return false; } catch (XBase& e) { LOG((CLOG_CRIT "failed to start server: %s", e.what())); closeClientListener(listener); closePrimaryClient(primaryClient); + closeServerScreen(serverScreen); return false; } @@ -334,6 +355,20 @@ startServer() } } +static +void +stopServer() +{ + closeClientListener(s_listener); + closeServer(s_server); + closePrimaryClient(s_primaryClient); + closeServerScreen(s_serverScreen); + s_server = NULL; + s_listener = NULL; + s_primaryClient = NULL; + s_serverScreen = NULL; +} + static int realMain() @@ -383,12 +418,7 @@ realMain() // close down LOG((CLOG_DEBUG1 "stopping server")); - closeClientListener(s_listener); - closeServer(s_server); - closePrimaryClient(s_primaryClient); - s_server = NULL; - s_listener = NULL; - s_primaryClient = NULL; + stopServer(); updateStatus(); LOG((CLOG_NOTE "stopped server")); diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 5281b092..5a4de474 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -33,7 +33,7 @@ CPrimaryClient::CPrimaryClient(const CString& name, CScreen* screen) : CPrimaryClient::~CPrimaryClient() { - delete m_screen; + // do nothing } void diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 4d80f86a..61eec5b6 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -24,12 +24,12 @@ class CScreen; /*! The primary screen does not have a client associated with it. This class provides a pseudo-client to allow the primary screen to be -treated as if it was on a client. +treated as if it was a client. */ class CPrimaryClient : public IClient { public: /*! - \c name is the name of the server. \p screen is adopted. + \c name is the name of the server and \p screen is primary screen. */ CPrimaryClient(const CString& name, CScreen* screen); ~CPrimaryClient(); From 48908242d24e82530afb8d5ba8c2db422d305b24 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 15 Feb 2004 17:32:11 +0000 Subject: [PATCH 578/807] Checkpoint. Conversion to event driven system complete for Unix. Still need to convert win32 platform specific files. --- cmd/synergyc/CClientTaskBarReceiver.cpp | 128 +-- cmd/synergyc/CClientTaskBarReceiver.h | 74 +- .../CXWindowsClientTaskBarReceiver.cpp | 6 - cmd/synergyc/CXWindowsClientTaskBarReceiver.h | 4 - cmd/synergyc/synergyc.cpp | 433 +++++--- cmd/synergys/CServerTaskBarReceiver.cpp | 2 +- cmd/synergys/synergys.cpp | 70 +- lib/arch/CArchNetworkBSD.cpp | 7 + lib/arch/XArch.h | 8 +- lib/base/CEvent.cpp | 3 +- lib/base/CEvent.h | 9 +- lib/client/CClient.cpp | 934 ++++++++---------- lib/client/CClient.h | 204 ++-- lib/client/CServerProxy.cpp | 501 ++++------ lib/client/CServerProxy.h | 82 +- lib/io/CStreamFilter.cpp | 2 +- lib/net/CNetworkAddress.cpp | 3 + lib/net/CTCPListenSocket.cpp | 6 +- lib/net/CTCPSocket.cpp | 175 ++-- lib/net/CTCPSocket.h | 1 + lib/net/IDataSocket.h | 7 + lib/net/XSocket.cpp | 2 + lib/net/XSocket.h | 1 + lib/platform/CXWindowsScreen.cpp | 29 +- lib/platform/CXWindowsScreen.h | 2 +- lib/server/CClientProxy.h | 2 +- lib/server/CClientProxy1_0.cpp | 30 +- lib/server/CClientProxy1_0.h | 2 +- lib/server/CConfig.cpp | 5 +- lib/server/CPrimaryClient.cpp | 20 +- lib/server/CPrimaryClient.h | 10 +- lib/server/CServer.cpp | 21 +- lib/synergy/CClipboard.cpp | 116 +-- lib/synergy/CClipboard.h | 23 - lib/synergy/CPacketStreamFilter.cpp | 4 +- lib/synergy/IClient.h | 2 +- lib/synergy/IClipboard.cpp | 155 +++ lib/synergy/IClipboard.h | 38 + lib/synergy/IPlatformScreen.cpp | 42 +- lib/synergy/IPlatformScreen.h | 8 +- lib/synergy/Makefile.am | 1 + lib/synergy/ProtocolTypes.h | 16 +- 42 files changed, 1543 insertions(+), 1645 deletions(-) create mode 100644 lib/synergy/IClipboard.cpp diff --git a/cmd/synergyc/CClientTaskBarReceiver.cpp b/cmd/synergyc/CClientTaskBarReceiver.cpp index 9c60c5bf..e1cbcfac 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CClientTaskBarReceiver.cpp @@ -15,7 +15,7 @@ #include "CClientTaskBarReceiver.h" #include "CClient.h" #include "CLock.h" -#include "TMethodJob.h" +#include "IEventQueue.h" #include "CArch.h" // @@ -23,71 +23,73 @@ // CClientTaskBarReceiver::CClientTaskBarReceiver() : - m_quit(NULL), - m_state(kNotRunning), - m_client(NULL) + m_state(kNotRunning) { - // create a job for getting notification when the client's - // status changes. - m_job = new TMethodJob(this, - &CClientTaskBarReceiver::statusChanged, NULL); + // do nothing } CClientTaskBarReceiver::~CClientTaskBarReceiver() { - if (m_client != NULL) { - m_client->removeStatusJob(m_job); - } - delete m_job; - delete m_quit; + // do nothing } void -CClientTaskBarReceiver::setClient(CClient* client) +CClientTaskBarReceiver::updateStatus(CClient* client, const CString& errorMsg) { { + // update our status CLock lock(&m_mutex); - if (m_client != client) { - if (m_client != NULL) { - m_client->removeStatusJob(m_job); + m_errorMessage = errorMsg; + if (client == NULL) { + if (m_errorMessage.empty()) { + m_state = kNotRunning; } - m_client = client; - if (m_client != NULL) { - m_client->addStatusJob(m_job); + else { + m_state = kNotWorking; + } + } + else { + if (client->isConnected()) { + m_state = kConnected; + } + else if (client->isConnecting()) { + m_state = kConnecting; + } + else { + m_state = kNotConnected; } } - } - ARCH->updateReceiver(this); -} -void -CClientTaskBarReceiver::setState(EState state) -{ - { - CLock lock(&m_mutex); - m_state = state; + // let subclasses have a go + onStatusChanged(client); } - ARCH->updateReceiver(this); -} -void -CClientTaskBarReceiver::setQuitJob(IJob* job) -{ - CLock lock(&m_mutex); - delete m_quit; - m_quit = job; + // tell task bar + ARCH->updateReceiver(this); } CClientTaskBarReceiver::EState -CClientTaskBarReceiver::getState() const +CClientTaskBarReceiver::getStatus() const { return m_state; } -CClient* -CClientTaskBarReceiver::getClient() const +const CString& +CClientTaskBarReceiver::getErrorMessage() const { - return m_client; + return m_errorMessage; +} + +void +CClientTaskBarReceiver::quit() +{ + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + +void +CClientTaskBarReceiver::onStatusChanged(CClient*) +{ + // do nothing } void @@ -113,7 +115,10 @@ CClientTaskBarReceiver::getToolTip() const return CString("Synergy: ") + m_errorMessage; case kNotConnected: - return "Synergy: Waiting for clients"; + return CString("Synergy: Not connected: ") + m_errorMessage; + + case kConnecting: + return "Synergy: Connecting..."; case kConnected: return "Synergy: Connected"; @@ -122,42 +127,3 @@ CClientTaskBarReceiver::getToolTip() const return ""; } } - -void -CClientTaskBarReceiver::quit() -{ - if (m_quit != NULL) { - m_quit->run(); - } -} - -void -CClientTaskBarReceiver::onStatusChanged() -{ - // do nothing -} - -void -CClientTaskBarReceiver::statusChanged(void*) -{ - // update our status - switch (m_client->getStatus(&m_errorMessage)) { - case CClient::kNotRunning: - setState(kNotRunning); - break; - - case CClient::kRunning: - setState(kConnected); - break; - - case CClient::kError: - setState(kNotWorking); - break; - - default: - break; - } - - // let subclasses have a go - onStatusChanged(); -} diff --git a/cmd/synergyc/CClientTaskBarReceiver.h b/cmd/synergyc/CClientTaskBarReceiver.h index aa52b9d6..ab9c371c 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.h +++ b/cmd/synergyc/CClientTaskBarReceiver.h @@ -20,59 +20,21 @@ #include "IArchTaskBarReceiver.h" class CClient; -class IJob; //! Implementation of IArchTaskBarReceiver for the synergy server class CClientTaskBarReceiver : public IArchTaskBarReceiver { public: - enum EState { - kNotRunning, - kNotWorking, - kNotConnected, - kConnected, - kMaxState - }; - CClientTaskBarReceiver(); virtual ~CClientTaskBarReceiver(); //! @name manipulators //@{ - //! Set server + //! Update status /*! - Sets the server. The receiver will query state from this server. + Determine the status and query required information from the client. */ - void setClient(CClient*); - - //! Set state - /*! - Sets the current server state. - */ - void setState(EState); - - //! Set the quit job that causes the server to quit - /*! - Set the job that causes the server to quit. - */ - void setQuitJob(IJob* adopted); - - //@} - //! @name accessors - //@{ - - //! Get state - /*! - Returns the current server state. The receiver is not locked - by this call; the caller must do the locking. - */ - EState getState() const; - - //! Get server - /*! - Returns the server set by \c setClient(). - */ - CClient* getClient() const; + void updateStatus(CClient*, const CString& errorMsg); //@} @@ -86,24 +48,36 @@ public: virtual std::string getToolTip() const; protected: + enum EState { + kNotRunning, + kNotWorking, + kNotConnected, + kConnecting, + kConnected, + kMaxState + }; + + //! Get status + EState getStatus() const; + + //! Get error message + const CString& getErrorMessage() const; + + //! Quit app + /*! + Causes the application to quit gracefully + */ void quit(); //! Status change notification /*! - Called when status changes. The default implementation does - nothing. + Called when status changes. The default implementation does nothing. */ - virtual void onStatusChanged(); - -private: - void statusChanged(void*); + virtual void onStatusChanged(CClient* client); private: CMutex m_mutex; - IJob* m_quit; EState m_state; - CClient* m_client; - IJob* m_job; CString m_errorMessage; }; diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp index f60585ea..13fac23f 100644 --- a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp @@ -53,9 +53,3 @@ CXWindowsClientTaskBarReceiver::getIcon() const { return NULL; } - -void -CXWindowsClientTaskBarReceiver::onStatusChanged() -{ - // do nothing -} diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h index e6028ce2..fab3a649 100644 --- a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h @@ -28,10 +28,6 @@ public: virtual void runMenu(int x, int y); virtual void primaryAction(); virtual const Icon getIcon() const; - -protected: - // CClientTaskBarReceiver overrides - virtual void onStatusChanged(); }; #endif diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 02f84c3d..1515acac 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -13,23 +13,22 @@ */ #include "CClient.h" -#include "IScreenFactory.h" +#include "CScreen.h" #include "ProtocolTypes.h" #include "Version.h" #include "XScreen.h" #include "CNetworkAddress.h" +#include "CSocketMultiplexer.h" #include "CTCPSocketFactory.h" #include "XSocket.h" -#include "CCondVar.h" -#include "CLock.h" -#include "CMutex.h" #include "CThread.h" -#include "XThread.h" -#include "CFunctionJob.h" +#include "CEventQueue.h" +#include "CFunctionEventJob.h" #include "CLog.h" -#include "LogOutputters.h" #include "CString.h" +#include "LogOutputters.h" #include "CArch.h" +#include "XArch.h" #include #define DAEMON_RUNNING(running_) @@ -87,160 +86,290 @@ CArgs* CArgs::s_instance = NULL; // platform dependent factories // -//! Factory for creating screens -/*! -Objects of this type create screens appropriate for the platform. -*/ -class CScreenFactory : public IScreenFactory { -public: - CScreenFactory() { } - virtual ~CScreenFactory() { } - - // IScreenFactory overrides - virtual IPlatformScreen* - create(IScreenReceiver*, IPrimaryScreenReceiver*); -}; - -IPlatformScreen* -CScreenFactory::create(IScreenReceiver* receiver, - IPrimaryScreenReceiver* primaryReceiver) +static +CScreen* +createScreen() { #if WINDOWS_LIKE - return new CMSWindowsScreen(receiver, primaryReceiver); + return new CScreen(new CMSWindowsScreen(false)); #elif UNIX_LIKE - return new CXWindowsScreen(receiver, primaryReceiver); + return new CScreen(new CXWindowsScreen(false)); #endif } -//! CQuitJob -/*! -A job that cancels a given thread. -*/ -class CQuitJob : public IJob { -public: - CQuitJob(const CThread& thread); - ~CQuitJob(); - - // IJob overrides - virtual void run(); - -private: - CThread m_thread; -}; - -CQuitJob::CQuitJob(const CThread& thread) : - m_thread(thread) -{ - // do nothing -} - -CQuitJob::~CQuitJob() -{ - // do nothing -} - -void -CQuitJob::run() -{ - m_thread.cancel(); -} - - // // platform independent main // -static CClient* s_client = NULL; +static CClient* s_client = NULL; +static CScreen* s_clientScreen = NULL; static CClientTaskBarReceiver* s_taskBarReceiver = NULL; +static double s_retryTime = 0.0; + +static +void +updateStatus() +{ + s_taskBarReceiver->updateStatus(s_client, ""); +} + +static +void +updateStatus(const CString& msg) +{ + s_taskBarReceiver->updateStatus(s_client, msg); +} + +static +void +resetRestartTimeout() +{ + s_retryTime = 0.0; +} + +static +double +nextRestartTimeout() +{ + // choose next restart timeout. we start with rapid retries + // then slow down. + if (s_retryTime < 1.0) { + s_retryTime = 1.0; + } + else if (s_retryTime < 3.0) { + s_retryTime = 3.0; + } + else if (s_retryTime < 5.0) { + s_retryTime = 5.0; + } + else if (s_retryTime < 15.0) { + s_retryTime = 15.0; + } + else if (s_retryTime < 30.0) { + s_retryTime = 30.0; + } + else { + s_retryTime = 60.0; + } + return s_retryTime; +} + +static +void +handleScreenError(const CEvent&, void*) +{ + LOG((CLOG_CRIT "error on screen")); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + +static +CScreen* +openClientScreen() +{ + CScreen* screen = createScreen(); + EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), + screen->getEventTarget(), + new CFunctionEventJob( + &handleScreenError)); + return screen; +} + +static +void +closeClientScreen(CScreen* screen) +{ + if (screen != NULL) { + EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), + screen->getEventTarget()); + delete screen; + } +} + +static +void +handleClientRestart(const CEvent&, void* vtimer) +{ + // discard old timer + CEventQueueTimer* timer = reinterpret_cast(vtimer); + EVENTQUEUE->deleteTimer(timer); + EVENTQUEUE->removeHandler(CEvent::kTimer, NULL); + + // reconnect + s_client->connect(); + updateStatus(); +} + +static +void +scheduleClientRestart(double retryTime) +{ + // install a timer and handler to retry later + LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); + CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, + new CFunctionEventJob(&handleClientRestart, timer)); +} + +static +void +handleClientConnected(const CEvent&, void*) +{ + LOG((CLOG_DEBUG "connected to server")); + resetRestartTimeout(); + updateStatus(); +} + +static +void +handleClientFailed(const CEvent& e, void*) +{ + CClient::CFailInfo* info = + reinterpret_cast(e.getData()); + + updateStatus(CString("Failed to connect to server: ") + info->m_what); + if (!ARG->m_restartable || !info->m_retry) { + LOG((CLOG_ERR "failed to connect to server: %s", info->m_what)); + } + else { + LOG((CLOG_WARN "failed to connect to server: %s", info->m_what)); + scheduleClientRestart(nextRestartTimeout()); + } +} + +static +void +handleClientDisconnected(const CEvent&, void*) +{ + LOG((CLOG_NOTE "disconnected from server")); + if (!ARG->m_restartable) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + else { + s_client->connect(); + } + updateStatus(); +} + +static +CClient* +openClient(const CString& name, const CNetworkAddress& address, CScreen* screen) +{ + CClient* client = new CClient(name, address, + new CTCPSocketFactory, NULL, screen); + EVENTQUEUE->adoptHandler(CClient::getConnectedEvent(), + client->getEventTarget(), + new CFunctionEventJob(handleClientConnected)); + EVENTQUEUE->adoptHandler(CClient::getConnectionFailedEvent(), + client->getEventTarget(), + new CFunctionEventJob(handleClientFailed)); + EVENTQUEUE->adoptHandler(CClient::getDisconnectedEvent(), + client->getEventTarget(), + new CFunctionEventJob(handleClientDisconnected)); + return client; +} + +static +void +closeClient(CClient* client) +{ + if (client == NULL) { + return; + } + + EVENTQUEUE->removeHandler(CClient::getConnectedEvent(), client); + EVENTQUEUE->removeHandler(CClient::getConnectionFailedEvent(), client); + EVENTQUEUE->removeHandler(CClient::getDisconnectedEvent(), client); + delete client; +} + +static +bool +startClient() +{ + double retryTime; + CScreen* clientScreen = NULL; + try { + clientScreen = openClientScreen(); + s_client = openClient(ARG->m_name, + ARG->m_serverAddress, clientScreen); + s_clientScreen = clientScreen; + LOG((CLOG_NOTE "started client")); + s_client->connect(); + updateStatus(); + return true; + } + catch (XScreenUnavailable& e) { + LOG((CLOG_WARN "cannot open secondary screen: %s", e.what())); + closeClientScreen(clientScreen); + updateStatus(CString("Cannot open secondary screen: ") + e.what()); + retryTime = e.getRetryTime(); + } + catch (XScreenOpenFailure& e) { + LOG((CLOG_CRIT "cannot open secondary screen: %s", e.what())); + closeClientScreen(clientScreen); + return false; + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed to start client: %s", e.what())); + closeClientScreen(clientScreen); + return false; + } + + if (ARG->m_restartable) { + scheduleClientRestart(retryTime); + return true; + } + else { + // don't try again + return false; + } +} + +static +void +stopClient() +{ + closeClient(s_client); + closeClientScreen(s_clientScreen); + s_client = NULL; + s_clientScreen = NULL; +} static int -realMain(void) +realMain() { - int result = kExitSuccess; - do { - bool opened = false; - bool locked = true; - try { - // create client - s_client = new CClient(ARG->m_name); - s_client->setAddress(ARG->m_serverAddress); - s_client->setScreenFactory(new CScreenFactory); - s_client->setSocketFactory(new CTCPSocketFactory); - s_client->setStreamFilterFactory(NULL); + // start the client. if this return false then we've failed and + // we shouldn't retry. + LOG((CLOG_DEBUG1 "starting client")); + if (!startClient()) { + return kExitFailed; + } - // open client - try { - s_taskBarReceiver->setClient(s_client); - s_client->open(); - opened = true; + // run event loop. if startClient() failed we're supposed to retry + // later. the timer installed by startClient() will take care of + // that. + DAEMON_RUNNING(true); + CEvent event; + EVENTQUEUE->getEvent(event); + while (event.getType() != CEvent::kQuit) { + EVENTQUEUE->dispatchEvent(event); + CEvent::deleteData(event); + EVENTQUEUE->getEvent(event); + } + DAEMON_RUNNING(false); - // run client - DAEMON_RUNNING(true); - locked = false; - s_client->mainLoop(); - locked = true; - DAEMON_RUNNING(false); + // close down + LOG((CLOG_DEBUG1 "stopping client")); + stopClient(); + updateStatus(); + LOG((CLOG_NOTE "stopped client")); - // get client status - if (s_client->wasRejected()) { - // try again later. we don't want to bother - // the server very often if it doesn't want us. - throw XScreenUnavailable(60.0); - } - - // clean up -#define FINALLY do { \ - if (!locked) { \ - DAEMON_RUNNING(false); \ - locked = true; \ - } \ - if (opened) { \ - s_client->close(); \ - } \ - s_taskBarReceiver->setClient(NULL); \ - delete s_client; \ - s_client = NULL; \ - } while (false) - FINALLY; - } - catch (XScreenUnavailable& e) { - // wait before retrying if we're going to retry - if (ARG->m_restartable) { - LOG((CLOG_DEBUG "waiting %.0f seconds to retry", e.getRetryTime())); - ARCH->sleep(e.getRetryTime()); - } - else { - result = kExitFailed; - } - FINALLY; - } - catch (XThread&) { - FINALLY; - throw; - } - catch (...) { - // don't try to restart and fail - ARG->m_restartable = false; - result = kExitFailed; - FINALLY; - } -#undef FINALLY - } - catch (XBase& e) { - LOG((CLOG_CRIT "failed: %s", e.what())); - } - catch (XThread&) { - // terminated - ARG->m_restartable = false; - result = kExitTerminated; - } - } while (ARG->m_restartable); - - return result; + return kExitSuccess; } +/* static void realMainEntry(void* vresult) @@ -278,6 +407,7 @@ runMainInThread(void) throw; } } +*/ // @@ -292,14 +422,12 @@ static void version() { - LOG((CLOG_PRINT -"%s %s, protocol version %d.%d\n" -"%s", - ARG->m_pname, - kVersion, - kProtocolMajorVersion, - kProtocolMinorVersion, - kCopyright)); + LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n%s", + ARG->m_pname, + kVersion, + kProtocolMajorVersion, + kProtocolMinorVersion, + kCopyright)); } static @@ -697,15 +825,20 @@ main(int argc, char** argv) { CArch arch; CLOG; - CArgs args; + + // go really fast + CThread::getCurrentThread().setPriority(-14); + + CSocketMultiplexer multiplexer; + CEventQueue eventQueue; // get program name + CArgs args; ARG->m_pname = ARCH->getBasename(argv[0]); // make the task bar receiver. the user can control this app // through the task bar. s_taskBarReceiver = new CXWindowsClientTaskBarReceiver; - s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); // parse command line parse(argc, argv); diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp index fb634d50..b3858827 100644 --- a/cmd/synergys/CServerTaskBarReceiver.cpp +++ b/cmd/synergys/CServerTaskBarReceiver.cpp @@ -14,8 +14,8 @@ #include "CServerTaskBarReceiver.h" #include "CServer.h" -#include "CEventQueue.h" #include "CLock.h" +#include "IEventQueue.h" #include "CArch.h" // diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 5956d011..98d1402d 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -171,25 +171,35 @@ closeClientListener(CClientListener* listen) } } +static +void +handleScreenError(const CEvent&, void*) +{ + LOG((CLOG_CRIT "error on screen")); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + static CScreen* openServerScreen() { - return createScreen(); + CScreen* screen = createScreen(); + EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), + screen->getEventTarget(), + new CFunctionEventJob( + &handleScreenError)); + return screen; } static void closeServerScreen(CScreen* screen) { - delete screen; -} - -static -void -handleScreenError(const CEvent&, void*) -{ - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + if (screen != NULL) { + EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), + screen->getEventTarget()); + delete screen; + } } static @@ -197,23 +207,14 @@ CPrimaryClient* openPrimaryClient(const CString& name, CScreen* screen) { LOG((CLOG_DEBUG1 "creating primary screen")); - CPrimaryClient* primaryClient = new CPrimaryClient(name, screen); - EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), - primaryClient->getEventTarget(), - new CFunctionEventJob( - &handleScreenError)); - return primaryClient; + return new CPrimaryClient(name, screen); } static void closePrimaryClient(CPrimaryClient* primaryClient) { - if (primaryClient != NULL) { - EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), - primaryClient->getEventTarget()); - delete primaryClient; - } + delete primaryClient; } static @@ -711,7 +712,7 @@ parse(int argc, const char* const* argv) static bool -loadConfig(const char* pathname, bool require) +loadConfig(const char* pathname) { assert(pathname != NULL); @@ -727,15 +728,8 @@ loadConfig(const char* pathname, bool require) return true; } catch (XConfigRead& e) { - if (require) { - LOG((CLOG_PRINT "%s: cannot read configuration '%s': %s", - ARG->m_pname, pathname, e.what())); - bye(kExitConfig); - } - else { - LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", + LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", pathname, e.what())); - } } return false; } @@ -744,34 +738,38 @@ static void loadConfig() { + bool loaded = false; + // load the config file, if specified if (ARG->m_configFile != NULL) { - // require the user specified file to load correctly - loadConfig(ARG->m_configFile, true); + loaded = loadConfig(ARG->m_configFile); } // load the default configuration if no explicit file given else { - // get the user's home directory. use the effective user id - // so a user can't get a setuid root program to load his file. - bool loaded = false; + // get the user's home directory CString path = ARCH->getUserDirectory(); if (!path.empty()) { // complete path path = ARCH->concatPath(path, USR_CONFIG_NAME); // now try loading the user's configuration - loaded = loadConfig(path.c_str(), false); + loaded = loadConfig(path.c_str()); } if (!loaded) { // try the system-wide config file path = ARCH->getSystemDirectory(); if (!path.empty()) { path = ARCH->concatPath(path, SYS_CONFIG_NAME); - loadConfig(path.c_str(), false); + loaded = loadConfig(path.c_str()); } } } + + if (!loaded) { + LOG((CLOG_PRINT "%s: no configuration available", ARG->m_pname)); + bye(kExitConfig); + } } diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 36daf926..29f71586 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -620,6 +620,13 @@ CArchNetworkBSD::nameToAddr(const std::string& name) sizeof(inaddr.sin_addr)); memcpy(&addr->m_addr, &inaddr, addr->m_len); } + else { + ARCH->unlockMutex(m_mutex); + delete addr; + throw XArchNetworkNameUnsupported( + "The requested name is valid but " + "does not have a supported address family"); + } // done with static buffer ARCH->unlockMutex(m_mutex); diff --git a/lib/arch/XArch.h b/lib/arch/XArch.h index 027868ad..45b7a0e8 100644 --- a/lib/arch/XArch.h +++ b/lib/arch/XArch.h @@ -64,7 +64,8 @@ class XArch { public: XArch(XArchEval* adoptedEvaluator) : m_eval(adoptedEvaluator) { } XArch(const std::string& msg) : m_eval(NULL), m_what(msg) { } - XArch(const XArch& e) : m_eval(e.m_eval->clone()), m_what(e.m_what) { } + XArch(const XArch& e) : m_eval(e.m_eval != NULL ? e.m_eval->clone() : NULL), + m_what(e.m_what) { } ~XArch() { delete m_eval; } std::string what() const throw(); @@ -137,7 +138,7 @@ XARCH_SUBCLASS(XArchNetworkName, XArchNetwork); //! The named host is unknown XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName); -//! The named host is known but has to address +//! The named host is known but has no address XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName); //! Non-recoverable name server error @@ -146,6 +147,9 @@ XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName); //! Temporary name server error XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName); +//! The named host is known but no supported address +XARCH_SUBCLASS(XArchNetworkNameUnsupported, XArchNetworkName); + //! Generic daemon exception /*! Exceptions derived from this class are used by the daemon diff --git a/lib/base/CEvent.cpp b/lib/base/CEvent.cpp index 67451d6a..5d828989 100644 --- a/lib/base/CEvent.cpp +++ b/lib/base/CEvent.cpp @@ -82,8 +82,7 @@ CEvent::deleteData(const CEvent& event) break; default: - // yes, really delete void* - delete event.getData(); + free(event.getData()); break; } } diff --git a/lib/base/CEvent.h b/lib/base/CEvent.h index a2c44735..272369ba 100644 --- a/lib/base/CEvent.h +++ b/lib/base/CEvent.h @@ -38,9 +38,10 @@ public: //! Create \c CEvent with data /*! The \p type must have been registered using \c registerType(). - The \p data must be POD (plain old data) which means it cannot - have a destructor or be composed of any types that do. \p target - is the intended recipient of the event. + The \p data must be POD (plain old data) allocated by malloc(), + which means it cannot have a constructor, destructor or be + composed of any types that do. \p target is the intended + recipient of the event. */ CEvent(Type type, void* target = NULL, void* data = NULL); @@ -70,7 +71,7 @@ public: //! Release event data /*! - Deletes event data for the given event. + Deletes event data for the given event (using free()). */ static void deleteData(const CEvent&); diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 1cc8a688..da227d2a 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -15,297 +15,163 @@ #include "CClient.h" #include "CServerProxy.h" #include "CScreen.h" -#include "IScreenFactory.h" #include "CClipboard.h" -#include "CInputPacketStream.h" -#include "COutputPacketStream.h" +#include "CPacketStreamFilter.h" #include "CProtocolUtil.h" -#include "IPlatformScreen.h" -#include "IServer.h" #include "ProtocolTypes.h" -#include "XScreen.h" #include "XSynergy.h" #include "IDataSocket.h" #include "ISocketFactory.h" -#include "XSocket.h" #include "IStreamFilterFactory.h" -#include "CLock.h" -#include "CThread.h" -#include "CTimerThread.h" -#include "XMT.h" -#include "XThread.h" #include "CLog.h" -#include "CStopwatch.h" -#include "TMethodJob.h" -#include "CArch.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" // // CClient // -CClient::CClient(const CString& clientName) : - m_name(clientName), - m_screen(NULL), +CEvent::Type CClient::s_connectedEvent = CEvent::kUnknown; +CEvent::Type CClient::s_connectionFailedEvent = CEvent::kUnknown; +CEvent::Type CClient::s_disconnectedEvent = CEvent::kUnknown; + +CClient::CClient(const CString& name, const CNetworkAddress& address, + ISocketFactory* socketFactory, + IStreamFilterFactory* streamFilterFactory, + CScreen* screen) : + m_name(name), + m_serverAddress(address), + m_socketFactory(socketFactory), + m_streamFilterFactory(streamFilterFactory), + m_screen(screen), + m_stream(NULL), + m_timer(NULL), m_server(NULL), - m_screenFactory(NULL), - m_socketFactory(NULL), - m_streamFilterFactory(NULL), - m_session(NULL), - m_active(false), - m_rejected(true), - m_status(kNotRunning) + + m_active(false) { + assert(m_socketFactory != NULL); + assert(m_screen != NULL); + // do nothing } CClient::~CClient() { - delete m_screenFactory; + cleanupTimer(); + cleanupScreen(); + cleanupConnecting(); + cleanupConnection(); delete m_socketFactory; delete m_streamFilterFactory; } void -CClient::setAddress(const CNetworkAddress& serverAddress) +CClient::connect() { - CLock lock(&m_mutex); - m_serverAddress = serverAddress; -} - -void -CClient::setScreenFactory(IScreenFactory* adopted) -{ - CLock lock(&m_mutex); - delete m_screenFactory; - m_screenFactory = adopted; -} - -void -CClient::setSocketFactory(ISocketFactory* adopted) -{ - CLock lock(&m_mutex); - delete m_socketFactory; - m_socketFactory = adopted; -} - -void -CClient::setStreamFilterFactory(IStreamFilterFactory* adopted) -{ - CLock lock(&m_mutex); - delete m_streamFilterFactory; - m_streamFilterFactory = adopted; -} - -void -CClient::exitMainLoop() -{ - m_screen->exitMainLoop(); -} - -void -CClient::addStatusJob(IJob* job) -{ - m_statusJobs.addJob(job); -} - -void -CClient::removeStatusJob(IJob* job) -{ - m_statusJobs.removeJob(job); -} - -bool -CClient::wasRejected() const -{ - return m_rejected; -} - -CClient::EStatus -CClient::getStatus(CString* msg) const -{ - CLock lock(&m_mutex); - if (msg != NULL) { - *msg = m_statusMessage; - } - return m_status; -} - -void -CClient::runStatusJobs() const -{ - m_statusJobs.runJobs(); -} - -void -CClient::setStatus(EStatus status, const char* msg) -{ - { - CLock lock(&m_mutex); - m_status = status; - if (m_status == kError) { - m_statusMessage = (msg == NULL) ? "Error" : msg; - } - else { - m_statusMessage = (msg == NULL) ? "" : msg; - } - } - runStatusJobs(); -} - -void -CClient::onError() -{ - setStatus(kError); - - // close down session but don't wait too long - deleteSession(3.0); -} - -void -CClient::onInfoChanged(const CClientInfo& info) -{ - LOG((CLOG_DEBUG "resolution changed")); - - CLock lock(&m_mutex); - if (m_server != NULL) { - m_server->onInfoChanged(info); - } -} - -bool -CClient::onGrabClipboard(ClipboardID id) -{ - CLock lock(&m_mutex); - if (m_server == NULL) { - // m_server can be NULL if the screen calls this method - // before we've gotten around to connecting to the server. - // we simply ignore the clipboard change in that case. - return false; - } - - // grab ownership - m_server->onGrabClipboard(id); - - // we now own the clipboard and it has not been sent to the server - m_ownClipboard[id] = true; - m_timeClipboard[id] = 0; - - // if we're not the active screen then send the clipboard now, - // otherwise we'll wait until we leave. - if (!m_active) { - sendClipboard(id); - } - - return true; -} - -void -CClient::onClipboardChanged(ClipboardID, const CString&) -{ - // ignore -- we'll check the clipboard when we leave -} - -void -CClient::open() -{ - // open the screen - try { - LOG((CLOG_DEBUG "opening screen")); - openSecondaryScreen(); - setStatus(kNotRunning); - } - catch (XScreenOpenFailure& e) { - // can't open screen - setStatus(kError, e.what()); - LOG((CLOG_DEBUG "failed to open screen")); - throw; - } -} - -void -CClient::mainLoop() -{ - { - CLock lock(&m_mutex); - - // check preconditions - assert(m_screen != NULL); - assert(m_server == NULL); - - // connection starts as unsuccessful - m_rejected = true; + if (m_stream != NULL) { + return; } try { - setStatus(kNotRunning); - LOG((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); + IDataSocket* socket = m_socketFactory->create(); - // start server interactions - { - CLock lock(&m_mutex); - m_session = new CThread(new TMethodJob( - this, &CClient::runSession)); + // filter socket messages, including a packetizing filter + m_stream = socket; + if (m_streamFilterFactory != NULL) { + m_stream = m_streamFilterFactory->create(m_stream, true); } + m_stream = new CPacketStreamFilter(m_stream, true); - // handle events - m_screen->mainLoop(); - - // clean up - deleteSession(); - LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); - } - catch (XMT& e) { - LOG((CLOG_ERR "client error: %s", e.what())); - setStatus(kError, e.what()); - - // clean up - deleteSession(); - LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); - throw; + // connect + LOG((CLOG_DEBUG1 "connecting to server")); + setupConnecting(); + setupTimer(); + socket->connect(m_serverAddress); } catch (XBase& e) { - LOG((CLOG_ERR "client error: %s", e.what())); - setStatus(kError, e.what()); - - // clean up - deleteSession(); - LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); - CLock lock(&m_mutex); - m_rejected = false; - } - catch (XThread&) { - setStatus(kNotRunning); - - // clean up - deleteSession(); - LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); - throw; - } - catch (...) { - LOG((CLOG_ERR "client error: ")); - setStatus(kError); - - // clean up - deleteSession(); - LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); - throw; + delete m_stream; + m_stream = NULL; + sendConnectionFailedEvent(e.what()); + return; } } void -CClient::close() +CClient::disconnect(const char* msg) { - closeSecondaryScreen(); - LOG((CLOG_DEBUG "closed screen")); + cleanupTimer(); + cleanupScreen(); + cleanupConnection(); + if (msg != NULL) { + sendConnectionFailedEvent(msg); + } + else { + sendEvent(getDisconnectedEvent(), NULL); + } +} + +bool +CClient::isConnected() const +{ + return (m_server != NULL); +} + +bool +CClient::isConnecting() const +{ + return (m_timer != NULL); +} + +CEvent::Type +CClient::getConnectedEvent() +{ + return CEvent::registerTypeOnce(s_connectedEvent, + "CClient::connected"); +} + +CEvent::Type +CClient::getConnectionFailedEvent() +{ + return CEvent::registerTypeOnce(s_connectionFailedEvent, + "CClient::failed"); +} + +CEvent::Type +CClient::getDisconnectedEvent() +{ + return CEvent::registerTypeOnce(s_disconnectedEvent, + "CClient::disconnected"); +} + +void* +CClient::getEventTarget() const +{ + return m_screen->getEventTarget(); +} + +bool +CClient::getClipboard(ClipboardID id, IClipboard* clipboard) const +{ + return m_screen->getClipboard(id, clipboard); +} + +void +CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + m_screen->getShape(x, y, w, h); +} + +void +CClient::getCursorPos(SInt32& x, SInt32& y) const +{ + m_screen->getCursorPos(x, y); } void CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool) { - { - CLock lock(&m_mutex); - m_active = true; - } + m_active = true; m_screen->mouseMove(xAbs, yAbs); m_screen->enter(); m_screen->setToggleState(mask); @@ -316,7 +182,6 @@ CClient::leave() { m_screen->leave(); - CLock lock(&m_mutex); m_active = false; // send clipboards that we own and that have changed @@ -330,26 +195,16 @@ CClient::leave() } void -CClient::setClipboard(ClipboardID id, const CString& data) +CClient::setClipboard(ClipboardID id, const IClipboard* clipboard) { - // unmarshall - CClipboard clipboard; - clipboard.unmarshall(data, 0); - - // set screen's clipboard - m_screen->setClipboard(id, &clipboard); + m_screen->setClipboard(id, clipboard); } void CClient::grabClipboard(ClipboardID id) { - // we no longer own the clipboard - { - CLock lock(&m_mutex); - m_ownClipboard[id] = false; - } - m_screen->grabClipboard(id); + m_ownClipboard[id] = false; } void @@ -425,89 +280,6 @@ CClient::getName() const return m_name; } -SInt32 -CClient::getJumpZoneSize() const -{ - return m_screen->getJumpZoneSize(); -} - -void -CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - m_screen->getShape(x, y, w, h); -} - -void -CClient::getCursorPos(SInt32& x, SInt32& y) const -{ - m_screen->getCursorPos(x, y); -} - -void -CClient::getCursorCenter(SInt32&, SInt32&) const -{ - assert(0 && "shouldn't be called"); -} - -void -CClient::openSecondaryScreen() -{ - assert(m_screen == NULL); - - // not active - m_active = false; - - // reset clipboard state - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_ownClipboard[id] = false; - m_timeClipboard[id] = 0; - } - - // create screen - LOG((CLOG_DEBUG1 "creating secondary screen")); - if (m_screenFactory != NULL) { - IPlatformScreen* platformScreen = m_screenFactory->create(this, NULL); - if (platformScreen != NULL) { - m_screen = new CScreen(platformScreen, this); - } - } - if (m_screen == NULL) { - throw XScreenOpenFailure(); - } - - // open screen - try { - LOG((CLOG_DEBUG1 "opening secondary screen")); - m_screen->open(); - } - catch (...) { - LOG((CLOG_DEBUG1 "destroying secondary screen")); - delete m_screen; - m_screen = NULL; - throw; - } -} - -void -CClient::closeSecondaryScreen() -{ - // close the secondary screen - try { - if (m_screen != NULL) { - LOG((CLOG_DEBUG1 "closing secondary screen")); - m_screen->close(); - } - } - catch (...) { - // ignore - } - - // clean up - LOG((CLOG_DEBUG1 "destroying secondary screen")); - delete m_screen; - m_screen = NULL; -} - void CClient::sendClipboard(ClipboardID id) { @@ -537,227 +309,305 @@ CClient::sendClipboard(ClipboardID id) // save and send data if different if (data != m_dataClipboard[id]) { m_dataClipboard[id] = data; - m_server->onClipboardChanged(id, data); + m_server->onClipboardChanged(id, &clipboard); } } } void -CClient::runSession(void*) +CClient::sendEvent(CEvent::Type type, void* data) { - try { - LOG((CLOG_DEBUG "starting server proxy")); - runServer(); - m_screen->exitMainLoop(); - LOG((CLOG_DEBUG "stopping server proxy")); - } - catch (...) { - m_screen->exitMainLoop(); - LOG((CLOG_DEBUG "stopping server proxy")); - throw; + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data)); +} + +void +CClient::sendConnectionFailedEvent(const char* msg) +{ + CFailInfo* info = (CFailInfo*)malloc(sizeof(CFailInfo) + strlen(msg)); + info->m_retry = true; + strcpy(info->m_what, msg); + sendEvent(getConnectionFailedEvent(), info); +} + +void +CClient::setupConnecting() +{ + assert(m_stream != NULL); + + EVENTQUEUE->adoptHandler(IDataSocket::getConnectedEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleConnected)); + EVENTQUEUE->adoptHandler(IDataSocket::getConnectionFailedEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleConnectionFailed)); +} + +void +CClient::setupConnection() +{ + assert(m_stream != NULL); + + EVENTQUEUE->adoptHandler(ISocket::getDisconnectedEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleDisconnected)); + EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleHello)); + EVENTQUEUE->adoptHandler(IStream::getOutputErrorEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleOutputError)); + EVENTQUEUE->adoptHandler(IStream::getInputShutdownEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleDisconnected)); + EVENTQUEUE->adoptHandler(IStream::getOutputShutdownEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleDisconnected)); +} + +void +CClient::setupScreen() +{ + assert(m_server == NULL); + + m_ready = false; + m_server = new CServerProxy(this, m_stream); + EVENTQUEUE->adoptHandler(IScreen::getShapeChangedEvent(), + getEventTarget(), + new TMethodEventJob(this, + &CClient::handleShapeChanged)); + EVENTQUEUE->adoptHandler(IScreen::getClipboardGrabbedEvent(), + getEventTarget(), + new TMethodEventJob(this, + &CClient::handleClipboardGrabbed)); + EVENTQUEUE->adoptHandler(IScreen::getClipboardChangedEvent(), + getEventTarget(), + new TMethodEventJob(this, + &CClient::handleClipboardChanged)); + EVENTQUEUE->adoptHandler(CServerProxy::getHandshakeCompleteEvent(), + m_server, + new TMethodEventJob(this, + &CClient::handleHandshakeComplete)); +} + +void +CClient::setupTimer() +{ + assert(m_timer == NULL); + + m_timer = EVENTQUEUE->newOneShotTimer(15.0, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer, + new TMethodEventJob(this, + &CClient::handleConnectTimeout)); +} + +void +CClient::cleanupConnecting() +{ + if (m_stream != NULL) { + EVENTQUEUE->removeHandler(IDataSocket::getConnectedEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IDataSocket::getConnectionFailedEvent(), + m_stream->getEventTarget()); } } void -CClient::deleteSession(double timeout) +CClient::cleanupConnection() { - // get session thread object - CThread* thread; - { - CLock lock(&m_mutex); - thread = m_session; - m_session = NULL; - } - - // shut it down - if (thread != NULL) { - thread->cancel(); - thread->wait(timeout); - delete thread; + if (m_stream != NULL) { + EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getOutputErrorEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getInputShutdownEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getOutputShutdownEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(ISocket::getDisconnectedEvent(), + m_stream->getEventTarget()); + delete m_stream; + m_stream = NULL; } } void -CClient::runServer() +CClient::cleanupScreen() { - IDataSocket* socket = NULL; - CServerProxy* proxy = NULL; - bool timedOut; - try { - // allow connect and handshake this much time to succeed - CTimerThread timer(15.0, &timedOut); - - // create socket and attempt to connect to server - LOG((CLOG_DEBUG1 "connecting to server")); - if (m_socketFactory != NULL) { - socket = m_socketFactory->create(); - } - assert(socket != NULL); - socket->connect(m_serverAddress); - - // create proxy - LOG((CLOG_INFO "connected to server")); - LOG((CLOG_DEBUG1 "negotiating with server")); - proxy = handshakeServer(socket); - } - catch (XThread&) { - if (timedOut) { - LOG((CLOG_ERR "connection timed out")); - setStatus(kError, "connection timed out"); + if (m_server != NULL) { + if (!m_ready) { + EVENTQUEUE->removeHandler(CServerProxy::getHandshakeCompleteEvent(), + m_server); } else { - // cancelled by some thread other than the timer - } - delete proxy; - delete socket; - throw; - } - catch (XSocketConnect& e) { - LOG((CLOG_ERR "connection failed: %s", e.what())); - setStatus(kError, e.what()); - delete socket; - return; - } - catch (XBase& e) { - LOG((CLOG_ERR "connection failed: %s", e.what())); - setStatus(kError, e.what()); - LOG((CLOG_INFO "disconnecting from server")); - delete socket; - return; - } - catch (...) { - LOG((CLOG_ERR "connection failed: ")); - setStatus(kError); - LOG((CLOG_INFO "disconnecting from server")); - delete socket; - return; - } - - // saver server proxy object - { - CLock lock(&m_mutex); - m_server = proxy; - } - - bool enabled = false; - try { - // enable the screen - m_screen->enable(); - enabled = true; - - // process messages - bool rejected = true; - if (proxy != NULL) { - LOG((CLOG_DEBUG1 "communicating with server")); - setStatus(kRunning); - rejected = !proxy->mainLoop(); - setStatus(kNotRunning); - } - - // disable the screen - m_screen->disable(); - - // clean up - CLock lock(&m_mutex); - m_rejected = rejected; - m_server = NULL; - delete proxy; - LOG((CLOG_DEBUG "disconnecting from server")); - socket->close(); - delete socket; - } - catch (...) { - setStatus(kNotRunning); - if (enabled) { m_screen->disable(); + m_ready = false; } - CLock lock(&m_mutex); - m_rejected = false; - m_server = NULL; - delete proxy; - LOG((CLOG_DEBUG "disconnecting from server")); - socket->close(); - delete socket; - throw; + EVENTQUEUE->removeHandler(IScreen::getShapeChangedEvent(), + getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(), + getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getClipboardChangedEvent(), + getEventTarget()); + delete m_server; + m_server = NULL; } } -CServerProxy* -CClient::handshakeServer(IDataSocket* socket) +void +CClient::cleanupTimer() { - // get the input and output streams - IInputStream* input = socket->getInputStream(); - IOutputStream* output = socket->getOutputStream(); - bool own = false; - - // attach filters - if (m_streamFilterFactory != NULL) { - input = m_streamFilterFactory->createInput(input, own); - output = m_streamFilterFactory->createOutput(output, own); - own = true; + if (m_timer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer); + EVENTQUEUE->deleteTimer(m_timer); + m_timer = NULL; + } +} + +void +CClient::handleConnected(const CEvent&, void*) +{ + LOG((CLOG_DEBUG1 "connected; wait for hello")); + cleanupConnecting(); + setupConnection(); + + // reset clipboard state + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_ownClipboard[id] = false; + m_timeClipboard[id] = 0; + } +} + +void +CClient::handleConnectionFailed(const CEvent& event, void*) +{ + IDataSocket::CConnectionFailedInfo* info = + reinterpret_cast(event.getData()); + + cleanupTimer(); + cleanupConnecting(); + delete m_stream; + m_stream = NULL; + LOG((CLOG_DEBUG1 "connection failed")); + sendConnectionFailedEvent(info->m_what); +} + +void +CClient::handleConnectTimeout(const CEvent&, void*) +{ + cleanupTimer(); + cleanupConnecting(); + delete m_stream; + m_stream = NULL; + LOG((CLOG_DEBUG1 "connection timed out")); + sendConnectionFailedEvent("Timed out"); +} + +void +CClient::handleOutputError(const CEvent&, void*) +{ + cleanupTimer(); + cleanupScreen(); + cleanupConnection(); + LOG((CLOG_WARN "error sending to server")); + sendEvent(getDisconnectedEvent(), NULL); +} + +void +CClient::handleDisconnected(const CEvent&, void*) +{ + cleanupTimer(); + cleanupScreen(); + cleanupConnection(); + LOG((CLOG_DEBUG1 "disconnected")); + sendEvent(getDisconnectedEvent(), NULL); +} + +void +CClient::handleHandshakeComplete(const CEvent&, void*) +{ + m_ready = true; + EVENTQUEUE->removeHandler(CServerProxy::getHandshakeCompleteEvent(), + m_server); + sendEvent(getConnectedEvent(), NULL); + m_screen->enable(); +} + +void +CClient::handleShapeChanged(const CEvent&, void*) +{ + LOG((CLOG_DEBUG "resolution changed")); + m_server->onInfoChanged(); +} + +void +CClient::handleClipboardGrabbed(const CEvent& event, void*) +{ + const IScreen::CClipboardInfo* info = + reinterpret_cast(event.getData()); + + // grab ownership + m_server->onGrabClipboard(info->m_id); + + // we now own the clipboard and it has not been sent to the server + m_ownClipboard[info->m_id] = true; + m_timeClipboard[info->m_id] = 0; + + // if we're not the active screen then send the clipboard now, + // otherwise we'll wait until we leave. + if (!m_active) { + sendClipboard(info->m_id); + } +} + +void +CClient::handleClipboardChanged(const CEvent&, void*) +{ + // ignore -- we'll check the clipboard when we leave +} + +void +CClient::handleHello(const CEvent&, void*) +{ + SInt16 major, minor; + if (!CProtocolUtil::readf(m_stream, kMsgHello, &major, &minor)) { + sendConnectionFailedEvent("Protocol error from server"); + cleanupTimer(); + cleanupConnection(); + return; + } + + // check versions + LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); + if (major < kProtocolMajorVersion || + (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) { + sendConnectionFailedEvent(XIncompatibleClient(major, minor).what()); + cleanupTimer(); + cleanupConnection(); + return; + } + + // say hello back + LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); + CProtocolUtil::writef(m_stream, kMsgHelloBack, + kProtocolMajorVersion, + kProtocolMinorVersion, &m_name); + + // now connected but waiting to complete handshake + setupScreen(); + cleanupTimer(); + + // make sure we process any remaining messages later. we won't + // receive another event for already pending messages so we fake + // one. + if (m_stream->isReady()) { + EVENTQUEUE->addEvent(CEvent(IStream::getInputReadyEvent(), + m_stream->getEventTarget())); } - - // attach the packetizing filters - input = new CInputPacketStream(input, own); - output = new COutputPacketStream(output, own); - own = true; - - CServerProxy* proxy = NULL; - try { - // wait for hello from server - LOG((CLOG_DEBUG1 "wait for hello")); - SInt16 major, minor; - CProtocolUtil::readf(input, kMsgHello, &major, &minor); - - // check versions - LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); - if (major < kProtocolMajorVersion || - (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) { - throw XIncompatibleClient(major, minor); - } - - // say hello back - LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); - CProtocolUtil::writef(output, kMsgHelloBack, - kProtocolMajorVersion, - kProtocolMinorVersion, &m_name); - - // create server proxy - proxy = new CServerProxy(this, input, output); - - // negotiate - // FIXME - - return proxy; - } - catch (XIncompatibleClient& e) { - LOG((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); - setStatus(kError, e.what()); - } - catch (XBase& e) { - LOG((CLOG_WARN "error communicating with server: %s", e.what())); - setStatus(kError, e.what()); - } - catch (...) { - // probably timed out - if (proxy != NULL) { - delete proxy; - } - else if (own) { - delete input; - delete output; - } - throw; - } - - // failed - if (proxy != NULL) { - delete proxy; - } - else if (own) { - delete input; - delete output; - } - - return NULL; } diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 4a802a5a..3595a719 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -15,133 +15,113 @@ #ifndef CCLIENT_H #define CCLIENT_H -#include "IScreenReceiver.h" #include "IClient.h" #include "IClipboard.h" #include "CNetworkAddress.h" -#include "CMutex.h" -#include "CJobList.h" +class CEventQueueTimer; class CScreen; class CServerProxy; -class CThread; class IDataSocket; -class IScreenReceiver; -class IScreenFactory; class ISocketFactory; +class IStream; class IStreamFilterFactory; //! Synergy client /*! This class implements the top-level client algorithms for synergy. */ -class CClient : public IScreenReceiver, public IClient { +class CClient : public IClient { public: - enum EStatus { - kNotRunning, - kRunning, - kError, - kMaxStatus + class CFailInfo { + public: + bool m_retry; + char m_what[1]; }; /*! - This client will attempt to connect the server using \c clientName - as its name. + This client will attempt to connect to the server using \p name + as its name and \p address as the server's address and \p factory + to create the socket. \p screen is the local screen. */ - CClient(const CString& clientName); + CClient(const CString& name, const CNetworkAddress& address, + ISocketFactory* socketFactory, + IStreamFilterFactory* streamFilterFactory, + CScreen* screen); ~CClient(); //! @name manipulators //@{ - //! Set server address + //! Connect to server /*! - Sets the server's address that the client should connect to. + Starts an attempt to connect to the server. This is ignored if + the client is trying to connect or is already connected. */ - void setAddress(const CNetworkAddress& serverAddress); + void connect(); - //! Set screen factory + //! Disconnect /*! - Sets the factory for creating screens. This must be set before - calling open(). This object takes ownership of the factory. + Disconnects from the server with an optional error message. */ - void setScreenFactory(IScreenFactory*); - - //! Set socket factory - /*! - Sets the factory used to create a socket to connect to the server. - This must be set before calling mainLoop(). This object takes - ownership of the factory. - */ - void setSocketFactory(ISocketFactory*); - - //! Set stream filter factory - /*! - Sets the factory used to filter the socket streams used to - communicate with the server. This object takes ownership - of the factory. - */ - void setStreamFilterFactory(IStreamFilterFactory*); - - //! Exit event loop - /*! - Force mainLoop() to return. This call can return before - mainLoop() does (i.e. asynchronously). This may only be - called between a successful open() and close(). - */ - void exitMainLoop(); - - //! Add a job to notify of status changes - /*! - The added job is run whenever the server's status changes in - certain externally visible ways. The client keeps ownership - of the job. - */ - void addStatusJob(IJob*); - - //! Remove a job to notify of status changes - /*! - Removes a previously added status notification job. A job can - remove itself when called but must not remove any other jobs. - The client keeps ownership of the job. - */ - void removeStatusJob(IJob*); + void disconnect(const char* msg); //@} //! @name accessors //@{ - //! + //! Test if connected /*! - Returns true if the server rejected our connection. + Returns true iff the client is successfully connected to the server. */ - bool wasRejected() const; + bool isConnected() const; - //! Get the status + //! Test if connecting /*! - Returns the current status and status message. + Returns true iff the client is currently attempting to connect to + the server. */ - EStatus getStatus(CString* = NULL) const; + bool isConnecting() const; + + //! Get connected event type + /*! + Returns the connected event type. This is sent when the client has + successfully connected to the server. + */ + static CEvent::Type getConnectedEvent(); + + //! Get connection failed event type + /*! + Returns the connection failed event type. This is sent when the + server fails for some reason. The event data is a CFailInfo*. + */ + static CEvent::Type getConnectionFailedEvent(); + + //! Get disconnected event type + /*! + Returns the disconnected event type. This is sent when the client + has disconnected from the server (and only after having successfully + connected). + */ + static CEvent::Type getDisconnectedEvent(); //@} - // IScreenReceiver overrides - virtual void onError(); - virtual void onInfoChanged(const CClientInfo&); - virtual bool onGrabClipboard(ClipboardID); - virtual void onClipboardChanged(ClipboardID, const CString&); + // IScreen overrides + virtual void* getEventTarget() const; + virtual bool getClipboard(ClipboardID id, IClipboard*) const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; // IClient overrides - virtual void open(); - virtual void mainLoop(); - virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, bool forScreensaver); virtual bool leave(); - virtual void setClipboard(ClipboardID, const CString&); + virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); - virtual void setClipboardDirty(ClipboardID, bool dirty); + virtual void setClipboardDirty(ClipboardID, bool); virtual void keyDown(KeyID, KeyModifierMask, KeyButton); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count, KeyButton); @@ -154,52 +134,48 @@ public: virtual void resetOptions(); virtual void setOptions(const COptionsList& options); virtual CString getName() const; - virtual SInt32 getJumpZoneSize() const; - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const; - virtual void getCursorPos(SInt32& x, SInt32& y) const; - virtual void getCursorCenter(SInt32& x, SInt32& y) const; private: - // notify status jobs of a change - void runStatusJobs() const; - - // set new status - void setStatus(EStatus, const char* msg = NULL); - - // open/close the secondary screen - void openSecondaryScreen(); - void closeSecondaryScreen(); - - // send the clipboard to the server void sendClipboard(ClipboardID); - - // handle server messaging - void runSession(void*); - void deleteSession(double timeout = -1.0); - void runServer(); - CServerProxy* handshakeServer(IDataSocket*); + void sendEvent(CEvent::Type, void*); + void sendConnectionFailedEvent(const char* msg); + void setupConnecting(); + void setupConnection(); + void setupScreen(); + void setupTimer(); + void cleanupConnecting(); + void cleanupConnection(); + void cleanupScreen(); + void cleanupTimer(); + void handleConnected(const CEvent&, void*); + void handleConnectionFailed(const CEvent&, void*); + void handleConnectTimeout(const CEvent&, void*); + void handleOutputError(const CEvent&, void*); + void handleDisconnected(const CEvent&, void*); + void handleHandshakeComplete(const CEvent&, void*); + void handleShapeChanged(const CEvent&, void*); + void handleClipboardGrabbed(const CEvent&, void*); + void handleClipboardChanged(const CEvent&, void*); + void handleHello(const CEvent&, void*); private: - CMutex m_mutex; - CString m_name; - CScreen* m_screen; - IScreenReceiver* m_server; - CNetworkAddress m_serverAddress; - IScreenFactory* m_screenFactory; - ISocketFactory* m_socketFactory; + CString m_name; + CNetworkAddress m_serverAddress; + ISocketFactory* m_socketFactory; IStreamFilterFactory* m_streamFilterFactory; - CThread* m_session; - bool m_active; - bool m_rejected; + CScreen* m_screen; + IStream* m_stream; + CEventQueueTimer* m_timer; + CServerProxy* m_server; + bool m_ready; + bool m_active; bool m_ownClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; CString m_dataClipboard[kClipboardEnd]; - // the status change jobs and status - CJobList m_statusJobs; - EStatus m_status; - CString m_statusMessage; + static CEvent::Type s_connectedEvent; + static CEvent::Type s_connectionFailedEvent; + static CEvent::Type s_disconnectedEvent; }; #endif diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 435cdc55..2935e195 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -13,15 +13,15 @@ */ #include "CServerProxy.h" +#include "CClient.h" +#include "CClipboard.h" #include "CProtocolUtil.h" -#include "IClient.h" #include "OptionTypes.h" #include "ProtocolTypes.h" -#include "IInputStream.h" -#include "IOutputStream.h" -#include "CLock.h" +#include "IStream.h" #include "CLog.h" -#include "CStopwatch.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" #include "XBase.h" #include @@ -29,293 +29,243 @@ // CServerProxy // -CServerProxy::CServerProxy(IClient* client, - IInputStream* adoptedInput, IOutputStream* adoptedOutput) : +CEvent::Type CServerProxy::s_handshakeCompleteEvent = + CEvent::kUnknown; + +CServerProxy::CServerProxy(CClient* client, IStream* stream) : m_client(client), - m_input(adoptedInput), - m_output(adoptedOutput), + m_stream(stream), + m_timer(NULL), m_seqNum(0), - m_heartRate(kHeartRate) + m_compressMouse(false), + m_ignoreMouse(false), + m_heartRate(0.0) { assert(m_client != NULL); - assert(m_input != NULL); - assert(m_output != NULL); + assert(m_stream != NULL); // initialize modifier translation table for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) m_modifierTranslationTable[id] = id; + + // handle data on stream + EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CServerProxy::handleMessage)); + + // send heartbeat + installHeartBeat(kHeartRate); } CServerProxy::~CServerProxy() { - delete m_input; - delete m_output; + installHeartBeat(-1.0); } -bool -CServerProxy::mainLoop() +CEvent::Type +CServerProxy::getHandshakeCompleteEvent() { - bool failedToConnect = false; - try { - // no compressed mouse motion yet - m_compressMouse = false; + return CEvent::registerTypeOnce(s_handshakeCompleteEvent, + "CServerProxy::handshakeComplete"); +} - // not ignoring mouse motions - m_ignoreMouse = false; +void +CServerProxy::installHeartBeat(double heartRate) +{ + if (m_timer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer); + EVENTQUEUE->deleteTimer(m_timer); + } + m_heartRate = heartRate; + if (m_heartRate > 0.0) { + m_timer = EVENTQUEUE->newTimer(m_heartRate, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer, + new TMethodEventJob(this, + &CServerProxy::handleHeartBeat)); + } +} - // reset sequence number - m_seqNum = 0; +void +CServerProxy::handleMessage(const CEvent&, void*) +{ + while (m_stream->isReady()) { + // read next code + UInt8 code[4]; + UInt32 n = m_stream->read(code, sizeof(code)); + if (n == 0) { + break; + } + if (n != 4) { + // client sent an incomplete message + LOG((CLOG_ERR "incomplete message from server")); + m_client->disconnect("incomplete message from server"); + return; + } - // handle messages from server - CStopwatch heartbeat; - for (;;) { - // if no input is pending then flush compressed mouse motion - if (getInputStream()->getSize() == 0) { - flushCompressedMouse(); - } + // parse message + LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + if (memcmp(code, kMsgDMouseMove, 4) == 0) { + mouseMove(); + } - // wait for a message - LOG((CLOG_DEBUG2 "waiting for message")); - UInt8 code[4]; - UInt32 n = getInputStream()->read(code, 4, m_heartRate); + else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { + mouseWheel(); + } - // check if server hungup - if (n == 0) { - LOG((CLOG_NOTE "server disconnected")); - break; - } + else if (memcmp(code, kMsgDKeyDown, 4) == 0) { + keyDown(); + } - // check for time out - if (n == (UInt32)-1 || - (m_heartRate >= 0.0 && heartbeat.getTime() > m_heartRate)) { - // send heartbeat - CLock lock(&m_mutex); - CProtocolUtil::writef(getOutputStream(), kMsgCNoop); - heartbeat.reset(); - if (n == (UInt32)-1) { - // no message to process - continue; - } - } + else if (memcmp(code, kMsgDKeyUp, 4) == 0) { + keyUp(); + } - // verify we got an entire code - if (n != 4) { - // client sent an incomplete message - LOG((CLOG_ERR "incomplete message from server")); - break; - } + else if (memcmp(code, kMsgDMouseDown, 4) == 0) { + mouseDown(); + } - // parse message - LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); - if (memcmp(code, kMsgDMouseMove, 4) == 0) { - mouseMove(); - } + else if (memcmp(code, kMsgDMouseUp, 4) == 0) { + mouseUp(); + } - else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { - mouseWheel(); - } + else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { + keyRepeat(); + } - else if (memcmp(code, kMsgDKeyDown, 4) == 0) { - keyDown(); - } + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + } - else if (memcmp(code, kMsgDKeyUp, 4) == 0) { - keyUp(); - } + else if (memcmp(code, kMsgCEnter, 4) == 0) { + enter(); + } - else if (memcmp(code, kMsgDMouseDown, 4) == 0) { - mouseDown(); - } + else if (memcmp(code, kMsgCLeave, 4) == 0) { + leave(); + } - else if (memcmp(code, kMsgDMouseUp, 4) == 0) { - mouseUp(); - } + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + grabClipboard(); + } - else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { - keyRepeat(); - } + else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { + screensaver(); + } - else if (memcmp(code, kMsgCNoop, 4) == 0) { - // accept and discard no-op - } + else if (memcmp(code, kMsgQInfo, 4) == 0) { + queryInfo(); + } - else if (memcmp(code, kMsgCEnter, 4) == 0) { - enter(); - } + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + infoAcknowledgment(); + } - else if (memcmp(code, kMsgCLeave, 4) == 0) { - leave(); - } + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + setClipboard(); + } - else if (memcmp(code, kMsgCClipboard, 4) == 0) { - grabClipboard(); - } + else if (memcmp(code, kMsgCResetOptions, 4) == 0) { + resetOptions(); + } - else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { - screensaver(); - } + else if (memcmp(code, kMsgDSetOptions, 4) == 0) { + setOptions(); + } - else if (memcmp(code, kMsgQInfo, 4) == 0) { - queryInfo(); - } + else if (memcmp(code, kMsgCClose, 4) == 0) { + // server wants us to hangup + LOG((CLOG_DEBUG1 "recv close")); + m_client->disconnect(NULL); + break; + } - else if (memcmp(code, kMsgCInfoAck, 4) == 0) { - infoAcknowledgment(); - } + else if (memcmp(code, kMsgEIncompatible, 4) == 0) { + SInt32 major, minor; + CProtocolUtil::readf(m_stream, + kMsgEIncompatible + 4, &major, &minor); + LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); + m_client->disconnect("server has incompatible version"); + return; + } - else if (memcmp(code, kMsgDClipboard, 4) == 0) { - setClipboard(); - } + else if (memcmp(code, kMsgEBusy, 4) == 0) { + LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str())); + m_client->disconnect("server already has a connected client with our name"); + return; + } - else if (memcmp(code, kMsgCResetOptions, 4) == 0) { - resetOptions(); - } + else if (memcmp(code, kMsgEUnknown, 4) == 0) { + LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str())); + m_client->disconnect("server refused client with our name"); + return; + } - else if (memcmp(code, kMsgDSetOptions, 4) == 0) { - setOptions(); - } + else if (memcmp(code, kMsgEBad, 4) == 0) { + LOG((CLOG_ERR "server disconnected due to a protocol error")); + m_client->disconnect("server reported a protocol error"); + return; + } - else if (memcmp(code, kMsgCClose, 4) == 0) { - // server wants us to hangup - LOG((CLOG_DEBUG1 "recv close")); - break; - } - - else if (memcmp(code, kMsgEIncompatible, 4) == 0) { - SInt32 major, minor; - CProtocolUtil::readf(getInputStream(), - kMsgEIncompatible + 4, &major, &minor); - LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); - failedToConnect = true; - break; - } - - else if (memcmp(code, kMsgEBusy, 4) == 0) { - LOG((CLOG_ERR "server already has a connected client with name \"%s\"", getName().c_str())); - failedToConnect = true; - break; - } - - else if (memcmp(code, kMsgEUnknown, 4) == 0) { - LOG((CLOG_ERR "server refused client with name \"%s\"", getName().c_str())); - failedToConnect = true; - break; - } - - else if (memcmp(code, kMsgEBad, 4) == 0) { - LOG((CLOG_ERR "server disconnected due to a protocol error")); - failedToConnect = true; - break; - } - - else { - // unknown message - LOG((CLOG_ERR "unknown message from server")); - LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3])); - failedToConnect = true; - break; - } + else { + // unknown message + LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3])); + m_client->disconnect("unknown message from server"); + return; } } - catch (XBase& e) { - LOG((CLOG_ERR "error: %s", e.what())); - } - catch (...) { - throw; - } - - return !failedToConnect; -} - -IClient* -CServerProxy::getClient() const -{ - return m_client; -} - -CString -CServerProxy::getName() const -{ - return m_client->getName(); -} - -IInputStream* -CServerProxy::getInputStream() const -{ - return m_input; -} - -IOutputStream* -CServerProxy::getOutputStream() const -{ - return m_output; + flushCompressedMouse(); } void -CServerProxy::onError() +CServerProxy::handleHeartBeat(const CEvent&, void*) { - // ignore + CProtocolUtil::writef(m_stream, kMsgCNoop); } void -CServerProxy::onInfoChanged(const CClientInfo& info) +CServerProxy::onInfoChanged() { // ignore mouse motion until we receive acknowledgment of our info // change message. - CLock lock(&m_mutex); m_ignoreMouse = true; // send info update - sendInfo(info); + queryInfo(); } bool CServerProxy::onGrabClipboard(ClipboardID id) { LOG((CLOG_DEBUG1 "sending clipboard %d changed", id)); - CLock lock(&m_mutex); - CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, m_seqNum); + CProtocolUtil::writef(m_stream, kMsgCClipboard, id, m_seqNum); return true; } void -CServerProxy::onClipboardChanged(ClipboardID id, const CString& data) +CServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard) { - CLock lock(&m_mutex); + CString data = IClipboard::marshall(clipboard); LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); - CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, m_seqNum, &data); + CProtocolUtil::writef(m_stream, kMsgDClipboard, id, m_seqNum, &data); } void CServerProxy::flushCompressedMouse() { - bool send = false; - SInt32 x = 0, y = 0; - { - CLock lock(&m_mutex); - if (m_compressMouse) { - m_compressMouse = false; - x = m_xMouse; - y = m_yMouse; - send = true; - } - } - - if (send) { - getClient()->mouseMove(x, y); + if (m_compressMouse) { + m_compressMouse = false; + m_client->mouseMove(m_xMouse, m_yMouse); } } void CServerProxy::sendInfo(const CClientInfo& info) { - // note -- m_mutex should be locked on entry - LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); - CProtocolUtil::writef(getOutputStream(), kMsgDInfo, + LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d", info.m_x, info.m_y, info.m_w, info.m_h)); + CProtocolUtil::writef(m_stream, kMsgDInfo, info.m_x, info.m_y, - info.m_w, info.m_h, - info.m_zoneSize, - info.m_mx, info.m_my); + info.m_w, info.m_h, 0, 0, 0); } KeyID @@ -434,19 +384,15 @@ CServerProxy::enter() SInt16 x, y; UInt16 mask; UInt32 seqNum; - CProtocolUtil::readf(getInputStream(), - kMsgCEnter + 4, &x, &y, &seqNum, &mask); + CProtocolUtil::readf(m_stream, kMsgCEnter + 4, &x, &y, &seqNum, &mask); LOG((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask)); // discard old compressed mouse motion, if any - { - CLock lock(&m_mutex); - m_compressMouse = false; - m_seqNum = seqNum; - } + m_compressMouse = false; + m_seqNum = seqNum; // forward - getClient()->enter(x, y, seqNum, static_cast(mask), false); + m_client->enter(x, y, seqNum, static_cast(mask), false); } void @@ -459,7 +405,7 @@ CServerProxy::leave() flushCompressedMouse(); // forward - getClient()->leave(); + m_client->leave(); } void @@ -469,8 +415,7 @@ CServerProxy::setClipboard() ClipboardID id; UInt32 seqNum; CString data; - CProtocolUtil::readf(getInputStream(), - kMsgDClipboard + 4, &id, &seqNum, &data); + CProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &data); LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); // validate @@ -479,7 +424,9 @@ CServerProxy::setClipboard() } // forward - getClient()->setClipboard(id, data); + CClipboard clipboard; + clipboard.unmarshall(data, 0); + m_client->setClipboard(id, &clipboard); } void @@ -488,7 +435,7 @@ CServerProxy::grabClipboard() // parse ClipboardID id; UInt32 seqNum; - CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + CProtocolUtil::readf(m_stream, kMsgCClipboard + 4, &id, &seqNum); LOG((CLOG_DEBUG "recv grab clipboard %d", id)); // validate @@ -497,7 +444,7 @@ CServerProxy::grabClipboard() } // forward - getClient()->grabClipboard(id); + m_client->grabClipboard(id); } void @@ -508,8 +455,7 @@ CServerProxy::keyDown() // parse UInt16 id, mask, button; - CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, - &id, &mask, &button); + CProtocolUtil::readf(m_stream, kMsgDKeyDown + 4, &id, &mask, &button); LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); // translate @@ -521,7 +467,7 @@ CServerProxy::keyDown() LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); // forward - getClient()->keyDown(id2, mask2, button); + m_client->keyDown(id2, mask2, button); } void @@ -532,7 +478,7 @@ CServerProxy::keyRepeat() // parse UInt16 id, mask, count, button; - CProtocolUtil::readf(getInputStream(), kMsgDKeyRepeat + 4, + CProtocolUtil::readf(m_stream, kMsgDKeyRepeat + 4, &id, &mask, &count, &button); LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button)); @@ -545,7 +491,7 @@ CServerProxy::keyRepeat() LOG((CLOG_DEBUG1 "key repeat translated to id=%d, mask=0x%04x", id2, mask2)); // forward - getClient()->keyRepeat(id2, mask2, count, button); + m_client->keyRepeat(id2, mask2, count, button); } void @@ -556,7 +502,7 @@ CServerProxy::keyUp() // parse UInt16 id, mask, button; - CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask, &button); + CProtocolUtil::readf(m_stream, kMsgDKeyUp + 4, &id, &mask, &button); LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); // translate @@ -568,7 +514,7 @@ CServerProxy::keyUp() LOG((CLOG_DEBUG1 "key up translated to id=%d, mask=0x%04x", id2, mask2)); // forward - getClient()->keyUp(id2, mask2, button); + m_client->keyUp(id2, mask2, button); } void @@ -579,11 +525,11 @@ CServerProxy::mouseDown() // parse SInt8 id; - CProtocolUtil::readf(getInputStream(), kMsgDMouseDown + 4, &id); + CProtocolUtil::readf(m_stream, kMsgDMouseDown + 4, &id); LOG((CLOG_DEBUG1 "recv mouse down id=%d", id)); // forward - getClient()->mouseDown(static_cast(id)); + m_client->mouseDown(static_cast(id)); } void @@ -594,11 +540,11 @@ CServerProxy::mouseUp() // parse SInt8 id; - CProtocolUtil::readf(getInputStream(), kMsgDMouseUp + 4, &id); + CProtocolUtil::readf(m_stream, kMsgDMouseUp + 4, &id); LOG((CLOG_DEBUG1 "recv mouse up id=%d", id)); // forward - getClient()->mouseUp(static_cast(id)); + m_client->mouseUp(static_cast(id)); } void @@ -607,30 +553,27 @@ CServerProxy::mouseMove() // parse bool ignore; SInt16 x, y; - CProtocolUtil::readf(getInputStream(), kMsgDMouseMove + 4, &x, &y); + CProtocolUtil::readf(m_stream, kMsgDMouseMove + 4, &x, &y); - { - // note if we should ignore the move - CLock lock(&m_mutex); - ignore = m_ignoreMouse; + // note if we should ignore the move + ignore = m_ignoreMouse; - // compress mouse motion events if more input follows - if (!ignore && !m_compressMouse && getInputStream()->getSize() > 0) { - m_compressMouse = true; - } + // compress mouse motion events if more input follows + if (!ignore && !m_compressMouse && m_stream->isReady()) { + m_compressMouse = true; + } - // if compressing then ignore the motion but record it - if (m_compressMouse) { - ignore = true; - m_xMouse = x; - m_yMouse = y; - } + // if compressing then ignore the motion but record it + if (m_compressMouse) { + ignore = true; + m_xMouse = x; + m_yMouse = y; } LOG((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); // forward if (!ignore) { - getClient()->mouseMove(x, y); + m_client->mouseMove(x, y); } } @@ -642,11 +585,11 @@ CServerProxy::mouseWheel() // parse SInt16 delta; - CProtocolUtil::readf(getInputStream(), kMsgDMouseWheel + 4, &delta); + CProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &delta); LOG((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); // forward - getClient()->mouseWheel(delta); + m_client->mouseWheel(delta); } void @@ -654,11 +597,11 @@ CServerProxy::screensaver() { // parse SInt8 on; - CProtocolUtil::readf(getInputStream(), kMsgCScreenSaver + 4, &on); + CProtocolUtil::readf(m_stream, kMsgCScreenSaver + 4, &on); LOG((CLOG_DEBUG1 "recv screen saver on=%d", on)); // forward - getClient()->screensaver(on != 0); + m_client->screensaver(on != 0); } void @@ -668,22 +611,18 @@ CServerProxy::resetOptions() LOG((CLOG_DEBUG1 "recv reset options")); // forward - getClient()->resetOptions(); + m_client->resetOptions(); - CLock lock(&m_mutex); - - // reset heart rate - m_heartRate = kHeartRate; + // reset heart rate and send heartbeat if necessary + installHeartBeat(kHeartRate); + if (m_heartRate >= 0.0) { + CProtocolUtil::writef(m_stream, kMsgCNoop); + } // reset modifier translation table for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) { m_modifierTranslationTable[id] = id; } - - // send heartbeat if necessary - if (m_heartRate >= 0.0) { - CProtocolUtil::writef(getOutputStream(), kMsgCNoop); - } } void @@ -691,13 +630,11 @@ CServerProxy::setOptions() { // parse COptionsList options; - CProtocolUtil::readf(getInputStream(), kMsgDSetOptions + 4, &options); + CProtocolUtil::readf(m_stream, kMsgDSetOptions + 4, &options); LOG((CLOG_DEBUG1 "recv set options size=%d", options.size())); // forward - getClient()->setOptions(options); - - CLock lock(&m_mutex); + m_client->setOptions(options); // update modifier table for (UInt32 i = 0, n = options.size(); i < n; i += 2) { @@ -718,12 +655,10 @@ CServerProxy::setOptions() id = kKeyModifierIDSuper; } else if (options[i] == kOptionHeartbeat) { - // update heart rate - m_heartRate = 1.0e-3 * static_cast(options[i + 1]); - - // send heartbeat if necessary + // update heart rate and send heartbeat if necessary + installHeartBeat(1.0e-3 * static_cast(options[i + 1])); if (m_heartRate >= 0.0) { - CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + CProtocolUtil::writef(m_stream, kMsgCNoop); } } if (id != kKeyModifierIDNull) { @@ -737,24 +672,14 @@ CServerProxy::setOptions() void CServerProxy::queryInfo() { - // get current info CClientInfo info; - getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); - getClient()->getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getClient()->getJumpZoneSize(); - - // send it - CLock lock(&m_mutex); + m_client->getShape(info.m_x, info.m_y, info.m_w, info.m_h); sendInfo(info); } void CServerProxy::infoAcknowledgment() { - // parse LOG((CLOG_DEBUG1 "recv info acknowledgment")); - - // now allow mouse motion - CLock lock(&m_mutex); m_ignoreMouse = false; } diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index cbb58f1b..f54cbe60 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -15,88 +15,66 @@ #ifndef CSERVERPROXY_H #define CSERVERPROXY_H -#include "IScreenReceiver.h" +#include "ClipboardTypes.h" #include "KeyTypes.h" -#include "CMutex.h" +#include "CEvent.h" -class IClient; -class IInputStream; -class IOutputStream; +class CClient; +class CClientInfo; +class CEventQueueTimer; +class IClipboard; +class IStream; //! Proxy for server /*! This class acts a proxy for the server, converting calls into messages to the server and messages from the server to calls on the client. */ -class CServerProxy : public IScreenReceiver { +class CServerProxy { public: - /*! \c adoptedInput is the stream from the server and - \c adoptedOutput is the stream to the server. This object - takes ownership of both and destroys them in the d'tor. - Messages from the server are converted to calls on \c client. + /*! + Process messages from the server on \p stream and forward to + \p client. */ - CServerProxy(IClient* client, - IInputStream* adoptedInput, - IOutputStream* adoptedOutput); + CServerProxy(CClient* client, IStream* stream); ~CServerProxy(); //! @name manipulators //@{ - //! Run event loop - /*! - Run the event loop and return when the server disconnects or - requests the client to disconnect. Return true iff the server - didn't reject our connection. - - (cancellation point) - */ - bool mainLoop(); + virtual void onInfoChanged(); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const IClipboard*); //@} //! @name accessors //@{ - //! Get client + //! Get handshake complete event type /*! - Returns the client passed to the c'tor. + Returns the handshake complete event type. This is sent when the + client has completed the handshake with the server. */ - IClient* getClient() const; - - //! Get input stream - /*! - Return the input stream passed to the c'tor. - */ - IInputStream* getInputStream() const; - - //! Get output stream - /*! - Return the output stream passed to the c'tor. - */ - IOutputStream* getOutputStream() const; + static CEvent::Type getHandshakeCompleteEvent(); //@} - // IScreenReceiver overrides - virtual void onError(); - virtual void onInfoChanged(const CClientInfo&); - virtual bool onGrabClipboard(ClipboardID); - virtual void onClipboardChanged(ClipboardID, const CString& data); - private: - - // get the client name (from the client) - CString getName() const; - // if compressing mouse motion then send the last motion now void flushCompressedMouse(); void sendInfo(const CClientInfo&); + void installHeartBeat(double); + // modifier key translation KeyID translateKey(KeyID) const; KeyModifierMask translateModifierMask(KeyModifierMask) const; + // event handlers + void handleMessage(const CEvent&, void*); + void handleHeartBeat(const CEvent&, void*); + // message handlers void enter(); void leave(); @@ -116,11 +94,9 @@ private: void infoAcknowledgment(); private: - CMutex m_mutex; - - IClient* m_client; - IInputStream* m_input; - IOutputStream* m_output; + CClient* m_client; + IStream* m_stream; + CEventQueueTimer* m_timer; UInt32 m_seqNum; @@ -131,6 +107,8 @@ private: KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; double m_heartRate; + + static CEvent::Type s_handshakeCompleteEvent; }; #endif diff --git a/lib/io/CStreamFilter.cpp b/lib/io/CStreamFilter.cpp index 00c064a0..d5a2a350 100644 --- a/lib/io/CStreamFilter.cpp +++ b/lib/io/CStreamFilter.cpp @@ -77,7 +77,7 @@ CStreamFilter::setEventFilter(IEventJob* filter) void* CStreamFilter::getEventTarget() const { - return const_cast(reinterpret_cast(this)); + return getStream()->getEventTarget(); } bool diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp index 3b2e3032..758f76dc 100644 --- a/lib/net/CNetworkAddress.cpp +++ b/lib/net/CNetworkAddress.cpp @@ -113,6 +113,9 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, int port) : catch (XArchNetworkNameNoAddress&) { throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); } + catch (XArchNetworkNameUnsupported&) { + throw XSocketAddress(XSocketAddress::kUnsupported, hostname, port); + } catch (XArchNetworkName&) { throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); } diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index bca77cd4..3f503d11 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -19,10 +19,9 @@ #include "TSocketMultiplexerMethodJob.h" #include "XSocket.h" #include "XIO.h" -#include "CEvent.h" -#include "CEventQueue.h" #include "CLock.h" #include "CMutex.h" +#include "IEventQueue.h" #include "CArch.h" #include "XArch.h" @@ -125,8 +124,7 @@ CTCPListenSocket::serviceListening(ISocketMultiplexerJob* job, return NULL; } if (read) { - CEventQueue::getInstance()->addEvent( - CEvent(getConnectingEvent(), this, NULL)); + EVENTQUEUE->addEvent(CEvent(getConnectingEvent(), this, NULL)); // stop polling on this socket until the client accepts return NULL; } diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 0b7b6ddf..78dc540a 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -18,11 +18,12 @@ #include "TSocketMultiplexerMethodJob.h" #include "XSocket.h" #include "CLock.h" -#include "CEventQueue.h" #include "CLog.h" +#include "IEventQueue.h" #include "IEventJob.h" #include "CArch.h" #include "XArch.h" +#include // // CTCPSocket @@ -84,6 +85,9 @@ CTCPSocket::bind(const CNetworkAddress& addr) void CTCPSocket::close() { + // remove ourself from the multiplexer + setJob(NULL); + CLock lock(&m_mutex); // clear buffers and enter disconnected state @@ -92,9 +96,6 @@ CTCPSocket::close() } onDisconnected(); - // remove ourself from the multiplexer - setJob(NULL); - // close the socket if (m_socket != NULL) { CArchSocket socket = m_socket; @@ -141,26 +142,29 @@ CTCPSocket::read(void* buffer, UInt32 n) void CTCPSocket::write(const void* buffer, UInt32 n) { - CLock lock(&m_mutex); + bool wasEmpty; + { + CLock lock(&m_mutex); - // must not have shutdown output - if (!m_writable) { - sendStreamEvent(getOutputErrorEvent()); - return; + // must not have shutdown output + if (!m_writable) { + sendStreamEvent(getOutputErrorEvent()); + return; + } + + // ignore empty writes + if (n == 0) { + return; + } + + // copy data to the output buffer + wasEmpty = (m_outputBuffer.getSize() == 0); + m_outputBuffer.write(buffer, n); + + // there's data to write + m_flushed = false; } - // ignore empty writes - if (n == 0) { - return; - } - - // copy data to the output buffer - bool wasEmpty = (m_outputBuffer.getSize() == 0); - m_outputBuffer.write(buffer, n); - - // there's data to write - m_flushed = false; - // make sure we're waiting to write if (wasEmpty) { setJob(newJob()); @@ -179,20 +183,26 @@ CTCPSocket::flush() void CTCPSocket::shutdownInput() { - CLock lock(&m_mutex); + bool useNewJob = false; + { + CLock lock(&m_mutex); - // shutdown socket for reading - try { - ARCH->closeSocketForRead(m_socket); - } - catch (XArchNetwork&) { - // ignore - } + // shutdown socket for reading + try { + ARCH->closeSocketForRead(m_socket); + } + catch (XArchNetwork&) { + // ignore + } - // shutdown buffer for reading - if (m_readable) { - sendStreamEvent(getInputShutdownEvent()); - onInputShutdown(); + // shutdown buffer for reading + if (m_readable) { + sendStreamEvent(getInputShutdownEvent()); + onInputShutdown(); + useNewJob = true; + } + } + if (useNewJob) { setJob(newJob()); } } @@ -200,20 +210,26 @@ CTCPSocket::shutdownInput() void CTCPSocket::shutdownOutput() { - CLock lock(&m_mutex); + bool useNewJob = false; + { + CLock lock(&m_mutex); - // shutdown socket for writing - try { - ARCH->closeSocketForWrite(m_socket); - } - catch (XArchNetwork&) { - // ignore - } + // shutdown socket for writing + try { + ARCH->closeSocketForWrite(m_socket); + } + catch (XArchNetwork&) { + // ignore + } - // shutdown buffer for writing - if (m_writable) { - sendStreamEvent(getOutputShutdownEvent()); - onOutputShutdown(); + // shutdown buffer for writing + if (m_writable) { + sendStreamEvent(getOutputShutdownEvent()); + onOutputShutdown(); + useNewJob = true; + } + } + if (useNewJob) { setJob(newJob()); } } @@ -249,28 +265,29 @@ CTCPSocket::getEventFilter() const void CTCPSocket::connect(const CNetworkAddress& addr) { - CLock lock(&m_mutex); + { + CLock lock(&m_mutex); - // fail on attempts to reconnect - if (m_socket == NULL || m_connected) { - sendSocketEvent(getConnectionFailedEvent()); - return; - } + // fail on attempts to reconnect + if (m_socket == NULL || m_connected) { + sendConnectionFailedEvent("busy"); + return; + } - try { - ARCH->connectSocket(m_socket, addr.getAddress()); - sendSocketEvent(getConnectedEvent()); - onConnected(); - setJob(newJob()); - } - catch (XArchNetworkConnecting&) { - // connection is in progress - m_writable = true; - setJob(newJob()); - } - catch (XArchNetwork& e) { - throw XSocketConnect(e.what()); + try { + ARCH->connectSocket(m_socket, addr.getAddress()); + sendSocketEvent(getConnectedEvent()); + onConnected(); + } + catch (XArchNetworkConnecting&) { + // connection is in progress + m_writable = true; + } + catch (XArchNetwork& e) { + throw XSocketConnect(e.what()); + } } + setJob(newJob()); } void @@ -339,17 +356,27 @@ CTCPSocket::newJob() void CTCPSocket::sendSocketEvent(CEvent::Type type) { - EVENTQUEUE->addEvent(CEvent(type, this, NULL)); + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL)); +} + +void +CTCPSocket::sendConnectionFailedEvent(const char* msg) +{ + CConnectionFailedInfo* info = (CConnectionFailedInfo*)malloc( + sizeof(CConnectionFailedInfo) + strlen(msg)); + strcpy(info->m_what, msg); + EVENTQUEUE->addEvent(CEvent(getConnectionFailedEvent(), + getEventTarget(), info)); } void CTCPSocket::sendStreamEvent(CEvent::Type type) { if (m_eventFilter != NULL) { - m_eventFilter->run(CEvent(type, this, NULL)); + m_eventFilter->run(CEvent(type, getEventTarget(), NULL)); } else { - EVENTQUEUE->addEvent(CEvent(type, this, NULL)); + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL)); } } @@ -394,22 +421,18 @@ CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job, { CLock lock(&m_mutex); - if (write && !error) { + if (error) { try { // connection may have failed or succeeded ARCH->throwErrorOnSocket(m_socket); } - catch (XArchNetwork&) { - error = true; + catch (XArchNetwork& e) { + sendConnectionFailedEvent(e.what().c_str()); + onDisconnected(); + return newJob(); } } - if (error) { - sendSocketEvent(getConnectionFailedEvent()); - onDisconnected(); - return newJob(); - } - if (write) { sendSocketEvent(getConnectedEvent()); onConnected(); diff --git a/lib/net/CTCPSocket.h b/lib/net/CTCPSocket.h index 0335dce1..8b60d5b6 100644 --- a/lib/net/CTCPSocket.h +++ b/lib/net/CTCPSocket.h @@ -60,6 +60,7 @@ private: void setJob(ISocketMultiplexerJob*); ISocketMultiplexerJob* newJob(); void sendSocketEvent(CEvent::Type); + void sendConnectionFailedEvent(const char*); void sendStreamEvent(CEvent::Type); void onConnected(); diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h index fd071669..bc0f2d3f 100644 --- a/lib/net/IDataSocket.h +++ b/lib/net/IDataSocket.h @@ -25,6 +25,12 @@ represent a full-duplex data stream. */ class IDataSocket : public ISocket, public IStream { public: + class CConnectionFailedInfo { + public: + // pointer to a string describing the failure + char m_what[1]; + }; + //! @name manipulators //@{ @@ -52,6 +58,7 @@ public: /*! Returns the socket connection failed event type. A socket sends this event when an attempt to connect to a remote port has failed. + The data is a pointer to a CConnectionFailedInfo. */ static CEvent::Type getConnectionFailedEvent(); diff --git a/lib/net/XSocket.cpp b/lib/net/XSocket.cpp index cf8b3ca6..8471538a 100644 --- a/lib/net/XSocket.cpp +++ b/lib/net/XSocket.cpp @@ -53,12 +53,14 @@ XSocketAddress::getWhat() const throw() "XSocketAddressUnknown", "XSocketAddressNotFound", "XSocketAddressNoAddress", + "XSocketAddressUnsupported", "XSocketAddressBadPort" }; static const char* s_errorMsg[] = { "unknown error for: %{1}:%{2}", "address not found for: %{1}", "no address for: %{1}", + "unsupported address for: %{1}", "invalid port" // m_port may not be set to the bad port }; return format(s_errorID[m_error], s_errorMsg[m_error], diff --git a/lib/net/XSocket.h b/lib/net/XSocket.h index c3663033..f84285b9 100644 --- a/lib/net/XSocket.h +++ b/lib/net/XSocket.h @@ -34,6 +34,7 @@ public: kUnknown, //!< Unknown error kNotFound, //!< The hostname is unknown kNoAddress, //!< The hostname is valid but has no IP address + kUnsupported, //!< The hostname is valid but has no supported address kBadPort //!< The port is invalid }; diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index c7e5f1cc..3b4f6104 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -835,7 +835,7 @@ CXWindowsScreen::sendEvent(CEvent::Type type, void* data) void CXWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) { - CClipboardInfo* info = new CClipboardInfo; + CClipboardInfo* info = (CClipboardInfo*)malloc(sizeof(CClipboardInfo)); info->m_id = id; info->m_sequenceNumber = m_sequenceNumber; sendEvent(type, info); @@ -1028,11 +1028,11 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey) } // handle key - sendEvent(getKeyDownEvent(), new CKeyInfo(key, mask, keycode, 1)); + sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask, keycode, 1)); KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); if (m_keyState->isHalfDuplex(keyMask)) { sendEvent(getKeyUpEvent(), - new CKeyInfo(key, mask | keyMask, keycode, 1)); + CKeyInfo::alloc(key, mask | keyMask, keycode, 1)); } } } @@ -1040,7 +1040,7 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey) Bool CXWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) { - CKeyEventInfo* filter = reinterpret_cast(arg); + CKeyEventFilter* filter = reinterpret_cast(arg); return (xevent->type == filter->m_event && xevent->xkey.window == filter->m_window && xevent->xkey.time == filter->m_time && @@ -1057,7 +1057,7 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) // KeyPress event that has the same key and time as // this release event, if any. first prepare the // filter info. - CKeyEventInfo filter; + CKeyEventFilter filter; filter.m_event = KeyPress; filter.m_window = xkey.window; filter.m_time = xkey.time; @@ -1089,9 +1089,9 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); if (m_keyState->isHalfDuplex(keyMask)) { sendEvent(getKeyDownEvent(), - new CKeyInfo(key, mask, keycode, 1)); + CKeyInfo::alloc(key, mask, keycode, 1)); } - sendEvent(getKeyUpEvent(), new CKeyInfo(key, mask, keycode, 1)); + sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, keycode, 1)); } else { // found a press event following so it's a repeat. @@ -1099,7 +1099,8 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) // repeats but we'll just send a repeat of 1. // note that we discard the press event. LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state)); - sendEvent(getKeyRepeatEvent(), new CKeyInfo(key, mask, keycode, 1)); + sendEvent(getKeyRepeatEvent(), + CKeyInfo::alloc(key, mask, keycode, 1)); } } } @@ -1110,7 +1111,7 @@ CXWindowsScreen::onMousePress(const XButtonEvent& xbutton) LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button)); const ButtonID button = mapButtonFromX(&xbutton); if (button != kButtonNone) { - sendEvent(getButtonDownEvent(), new CButtonInfo(button)); + sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); } } @@ -1120,15 +1121,15 @@ CXWindowsScreen::onMouseRelease(const XButtonEvent& xbutton) LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button)); const ButtonID button = mapButtonFromX(&xbutton); if (button != kButtonNone) { - sendEvent(getButtonUpEvent(), new CButtonInfo(button)); + sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); } else if (xbutton.button == 4) { // wheel forward (away from user) - sendEvent(getWheelEvent(), new CWheelInfo(120)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(120)); } else if (xbutton.button == 5) { // wheel backward (toward user) - sendEvent(getWheelEvent(), new CWheelInfo(-120)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(-120)); } } @@ -1160,7 +1161,7 @@ CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion) else if (m_isOnScreen) { // motion on primary screen sendEvent(getMotionOnPrimaryEvent(), - new CMotionInfo(m_xCursor, m_yCursor)); + CMotionInfo::alloc(m_xCursor, m_yCursor)); } else { // motion on secondary screen. warp mouse back to @@ -1190,7 +1191,7 @@ CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion) // warping to the primary screen's enter position, // effectively overriding it. if (x != 0 || y != 0) { - sendEvent(getMotionOnSecondaryEvent(), new CMotionInfo(x, y)); + sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y)); } } } diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index 65ecfcee..b56c693d 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -108,7 +108,7 @@ private: static int ioErrorHandler(Display*); private: - class CKeyEventInfo { + class CKeyEventFilter { public: int m_event; Window m_window; diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index aad57f6a..4a95ebc5 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -78,7 +78,7 @@ public: UInt32 seqNum, KeyModifierMask mask, bool forScreensaver) = 0; virtual bool leave() = 0; - virtual void setClipboard(ClipboardID, const CString&) = 0; + virtual void setClipboard(ClipboardID, const IClipboard*) = 0; virtual void grabClipboard(ClipboardID) = 0; virtual void setClipboardDirty(ClipboardID, bool) = 0; virtual void keyDown(KeyID, KeyModifierMask, KeyButton) = 0; diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 505eacfa..617e12a2 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -218,11 +218,9 @@ CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const } void -CClientProxy1_0::getCursorPos(SInt32& x, SInt32& y) const +CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const { assert(0 && "shouldn't be called"); - x = m_info.m_mx; - y = m_info.m_my; } void @@ -245,13 +243,15 @@ CClientProxy1_0::leave() } void -CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) +CClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard) { // ignore if this clipboard is already clean if (m_clipboard[id].m_dirty) { // this clipboard is now clean m_clipboard[id].m_dirty = false; + CClipboard::copy(&m_clipboard[id].m_clipboard, clipboard); + CString data = m_clipboard[id].m_clipboard.marshall(); LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); CProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data); } @@ -366,29 +366,23 @@ bool CClientProxy1_0::recvInfo() { // parse the message - SInt16 x, y, w, h, zoneSize, mx, my; + SInt16 x, y, w, h, dummy1, dummy2, dummy3; if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4, - &x, &y, &w, &h, &zoneSize, &mx, &my)) { + &x, &y, &w, &h, &dummy1, &dummy2, &dummy3)) { return false; } - LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my)); + LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d", getName().c_str(), x, y, w, h)); // validate - if (w <= 0 || h <= 0 || zoneSize < 0) { - return false; - } - if (mx < x || my < y || mx >= x + w || my >= y + h) { + if (w <= 0 || h <= 0) { return false; } // save - m_info.m_x = x; - m_info.m_y = y; - m_info.m_w = w; - m_info.m_h = h; - m_info.m_zoneSize = zoneSize; - m_info.m_mx = mx; - m_info.m_my = my; + m_info.m_x = x; + m_info.m_y = y; + m_info.m_w = w; + m_info.m_h = h; // acknowledge receipt LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 26584e97..503dcba9 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -39,7 +39,7 @@ public: UInt32 seqNum, KeyModifierMask mask, bool forScreensaver); virtual bool leave(); - virtual void setClipboard(ClipboardID, const CString&); + virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); virtual void setClipboardDirty(ClipboardID, bool); virtual void keyDown(KeyID, KeyModifierMask, KeyButton); diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 0b40a666..a4893cc8 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -761,8 +761,9 @@ CConfig::readSectionOptions(std::istream& s) try { m_synergyAddress = CNetworkAddress(value, kDefaultPort); } - catch (XSocketAddress&) { - throw XConfigRead("invalid address argument"); + catch (XSocketAddress& e) { + throw XConfigRead(CString("invalid address argument: ") + + e.what()); } } else if (name == "heartbeat") { diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 5a4de474..9da849bd 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -42,6 +42,12 @@ CPrimaryClient::reconfigure(UInt32 activeSides) m_screen->reconfigure(activeSides); } +SInt32 +CPrimaryClient::getJumpZoneSize() const +{ + return m_screen->getJumpZoneSize(); +} + void CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const { @@ -72,12 +78,6 @@ CPrimaryClient::getClipboard(ClipboardID id, IClipboard* clipboard) const return m_screen->getClipboard(id, clipboard); } -SInt32 -CPrimaryClient::getJumpZoneSize() const -{ - return m_screen->getJumpZoneSize(); -} - void CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const @@ -121,19 +121,15 @@ CPrimaryClient::leave() } void -CPrimaryClient::setClipboard(ClipboardID id, const CString& data) +CPrimaryClient::setClipboard(ClipboardID id, const IClipboard* clipboard) { // ignore if this clipboard is already clean if (m_clipboardDirty[id]) { // this clipboard is now clean m_clipboardDirty[id] = false; - // unmarshall data - CClipboard clipboard; - clipboard.unmarshall(data, 0); - // set clipboard - m_screen->setClipboard(id, &clipboard); + m_screen->setClipboard(id, clipboard); } } diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 61eec5b6..985fc4a3 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -47,6 +47,13 @@ public: //! @name accessors //@{ + //! Get jump zone size + /*! + Return the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. + */ + SInt32 getJumpZoneSize() const; + //! Get cursor center position /*! Return the cursor center position which is where we park the @@ -76,7 +83,6 @@ public: // IScreen overrides virtual void* getEventTarget() const; virtual bool getClipboard(ClipboardID id, IClipboard*) const; - virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; @@ -86,7 +92,7 @@ public: UInt32 seqNum, KeyModifierMask mask, bool forScreensaver); virtual bool leave(); - virtual void setClipboard(ClipboardID, const CString&); + virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); virtual void setClipboardDirty(ClipboardID, bool); virtual void keyDown(KeyID, KeyModifierMask, KeyButton); diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index cc2282f5..654ef383 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -16,7 +16,6 @@ #include "CClientProxy.h" #include "CClientProxyUnknown.h" #include "CPrimaryClient.h" -#include "CPacketStreamFilter.h" #include "IPlatformScreen.h" #include "OptionTypes.h" #include "ProtocolTypes.h" @@ -205,6 +204,11 @@ CServer::adoptClient(IClient* client) { assert(client != NULL); + // watch for client disconnection + EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client, + new TMethodEventJob(this, + &CServer::handleClientDisconnected, client)); + // name must be in our configuration if (!m_config.isScreen(client->getName())) { LOG((CLOG_WARN "a client with name \"%s\" is not in the map", client->getName().c_str())); @@ -221,11 +225,6 @@ CServer::adoptClient(IClient* client) } LOG((CLOG_NOTE "client \"%s\" has connected", getName(client).c_str())); - // watch for client disconnection - EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client, - new TMethodEventJob(this, - &CServer::handleClientDisconnected, client)); - // send configuration options to client sendOptions(client); @@ -287,7 +286,11 @@ CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) CString CServer::getName(const IClient* client) const { - return m_config.getCanonicalName(client->getName()); + CString name = m_config.getCanonicalName(client->getName()); + if (name.empty()) { + name = client->getName(); + } + return name; } UInt32 @@ -397,7 +400,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) // send the clipboard data to new active screen for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_active->setClipboard(id, m_clipboards[id].m_clipboardData); + m_active->setClipboard(id, &m_clipboards[id].m_clipboard); } } else { @@ -1078,7 +1081,7 @@ CServer::onClipboardChanged(IClient* sender, ClipboardID id, UInt32 seqNum) } // send the new clipboard to the active screen - m_active->setClipboard(id, clipboard.m_clipboardData); + m_active->setClipboard(id, &clipboard.m_clipboard); } void diff --git a/lib/synergy/CClipboard.cpp b/lib/synergy/CClipboard.cpp index d82448ac..d0388a01 100644 --- a/lib/synergy/CClipboard.cpp +++ b/lib/synergy/CClipboard.cpp @@ -101,126 +101,14 @@ CClipboard::get(EFormat format) const return m_data[format]; } -bool -CClipboard::copy(IClipboard* dst, const IClipboard* src) -{ - assert(dst != NULL); - assert(src != NULL); - - return copy(dst, src, src->getTime()); -} - -bool -CClipboard::copy(IClipboard* dst, const IClipboard* src, Time time) -{ - assert(dst != NULL); - assert(src != NULL); - - bool success = false; - if (src->open(time)) { - if (dst->open(time)) { - if (dst->empty()) { - for (SInt32 format = 0; - format != IClipboard::kNumFormats; ++format) { - IClipboard::EFormat eFormat = (IClipboard::EFormat)format; - if (src->has(eFormat)) { - dst->add(eFormat, src->get(eFormat)); - } - } - success = true; - } - dst->close(); - } - src->close(); - } - - return success; -} - void CClipboard::unmarshall(const CString& data, Time time) { - const char* index = data.data(); - - // clear existing data - open(time); - empty(); - - // read the number of formats - const UInt32 numFormats = readUInt32(index); - index += 4; - - // read each format - for (UInt32 i = 0; i < numFormats; ++i) { - // get the format id - UInt32 format = readUInt32(index); - index += 4; - - // get the size of the format data - UInt32 size = readUInt32(index); - index += 4; - - // save the data if it's a known format. if either the client - // or server supports more clipboard formats than the other - // then one of them will get a format >= kNumFormats here. - if (format < static_cast(IClipboard::kNumFormats)) { - m_added[format] = true; - m_data[format] = CString(index, size); - } - index += size; - } - - // done - close(); + IClipboard::unmarshall(this, data, time); } CString CClipboard::marshall() const { - CString data; - - // compute size of marshalled data - UInt32 size = 4; - UInt32 numFormats = 0; - UInt32 format; - for (format = 0; format != IClipboard::kNumFormats; ++format) { - if (m_added[format]) { - ++numFormats; - size += 4 + 4 + m_data[format].size(); - } - } - - // allocate space - data.reserve(size); - - // marshall the data - writeUInt32(&data, numFormats); - for (format = 0; format != IClipboard::kNumFormats; ++format) { - if (m_added[format]) { - writeUInt32(&data, format); - writeUInt32(&data, m_data[format].size()); - data += m_data[format]; - } - } - - return data; -} - -UInt32 -CClipboard::readUInt32(const char* buf) const -{ - const unsigned char* ubuf = reinterpret_cast(buf); - return (static_cast(ubuf[0]) << 24) | - (static_cast(ubuf[1]) << 16) | - (static_cast(ubuf[2]) << 8) | - static_cast(ubuf[3]); -} - -void -CClipboard::writeUInt32(CString* buf, UInt32 v) const -{ - *buf += static_cast((v >> 24) & 0xff); - *buf += static_cast((v >> 16) & 0xff); - *buf += static_cast((v >> 8) & 0xff); - *buf += static_cast( v & 0xff); + return IClipboard::marshall(this); } diff --git a/lib/synergy/CClipboard.h b/lib/synergy/CClipboard.h index 55d12938..f8d10aff 100644 --- a/lib/synergy/CClipboard.h +++ b/lib/synergy/CClipboard.h @@ -47,25 +47,6 @@ public: */ CString marshall() const; - //! Copy clipboard - /*! - Transfers all the data in one clipboard to another. The - clipboards can be of any concrete clipboard type (and - they don't have to be the same type). This also sets - the destination clipboard's timestamp to source clipboard's - timestamp. Returns true iff the copy succeeded. - */ - static bool copy(IClipboard* dst, const IClipboard* src); - - //! Copy clipboard - /*! - Transfers all the data in one clipboard to another. The - clipboards can be of any concrete clipboard type (and they - don't have to be the same type). This also sets the - timestamp to \c time. Returns true iff the copy succeeded. - */ - static bool copy(IClipboard* dst, const IClipboard* src, Time); - //@} // IClipboard overrides @@ -77,10 +58,6 @@ public: virtual bool has(EFormat) const; virtual CString get(EFormat) const; -private: - UInt32 readUInt32(const char*) const; - void writeUInt32(CString*, UInt32) const; - private: mutable bool m_open; mutable Time m_time; diff --git a/lib/synergy/CPacketStreamFilter.cpp b/lib/synergy/CPacketStreamFilter.cpp index 0ce5e7c3..d59d2fa1 100644 --- a/lib/synergy/CPacketStreamFilter.cpp +++ b/lib/synergy/CPacketStreamFilter.cpp @@ -34,7 +34,9 @@ CPacketStreamFilter::CPacketStreamFilter(IStream* stream, bool adoptStream) : CPacketStreamFilter::~CPacketStreamFilter() { - delete getStream()->getEventFilter(); + IEventJob* job = getStream()->getEventFilter(); + getStream()->setEventFilter(NULL); + delete job; } void diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index 3e7c8b08..b4ef2eca 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -58,7 +58,7 @@ public: already known to be up to date then this may do nothing. \c data has marshalled clipboard data. */ - virtual void setClipboard(ClipboardID, const CString& data) = 0; + virtual void setClipboard(ClipboardID, const IClipboard*) = 0; //! Grab clipboard /*! diff --git a/lib/synergy/IClipboard.cpp b/lib/synergy/IClipboard.cpp new file mode 100644 index 00000000..7a055612 --- /dev/null +++ b/lib/synergy/IClipboard.cpp @@ -0,0 +1,155 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "IClipboard.h" +#include "stdvector.h" + +// +// IClipboard +// + +void +IClipboard::unmarshall(IClipboard* clipboard, const CString& data, Time time) +{ + assert(clipboard != NULL); + + const char* index = data.data(); + + // clear existing data + clipboard->open(time); + clipboard->empty(); + + // read the number of formats + const UInt32 numFormats = readUInt32(index); + index += 4; + + // read each format + for (UInt32 i = 0; i < numFormats; ++i) { + // get the format id + IClipboard::EFormat format = + static_cast(readUInt32(index)); + index += 4; + + // get the size of the format data + UInt32 size = readUInt32(index); + index += 4; + + // save the data if it's a known format. if either the client + // or server supports more clipboard formats than the other + // then one of them will get a format >= kNumFormats here. + if (format add(format, CString(index, size)); + } + index += size; + } + + // done + clipboard->close(); +} + +CString +IClipboard::marshall(const IClipboard* clipboard) +{ + assert(clipboard != NULL); + + CString data; + + std::vector formatData; + formatData.resize(IClipboard::kNumFormats); + // FIXME -- use current time + clipboard->open(0); + + // compute size of marshalled data + UInt32 size = 4; + UInt32 numFormats = 0; + for (UInt32 format = 0; format != IClipboard::kNumFormats; ++format) { + if (clipboard->has(static_cast(format))) { + ++numFormats; + formatData[format] = + clipboard->get(static_cast(format)); + size += 4 + 4 + formatData[format].size(); + } + } + + // allocate space + data.reserve(size); + + // marshall the data + writeUInt32(&data, numFormats); + for (UInt32 format = 0; format != IClipboard::kNumFormats; ++format) { + if (clipboard->has(static_cast(format))) { + writeUInt32(&data, format); + writeUInt32(&data, formatData[format].size()); + data += formatData[format]; + } + } + clipboard->close(); + + return data; +} + +bool +IClipboard::copy(IClipboard* dst, const IClipboard* src) +{ + assert(dst != NULL); + assert(src != NULL); + + return copy(dst, src, src->getTime()); +} + +bool +IClipboard::copy(IClipboard* dst, const IClipboard* src, Time time) +{ + assert(dst != NULL); + assert(src != NULL); + + bool success = false; + if (src->open(time)) { + if (dst->open(time)) { + if (dst->empty()) { + for (SInt32 format = 0; + format != IClipboard::kNumFormats; ++format) { + IClipboard::EFormat eFormat = (IClipboard::EFormat)format; + if (src->has(eFormat)) { + dst->add(eFormat, src->get(eFormat)); + } + } + success = true; + } + dst->close(); + } + src->close(); + } + + return success; +} + +UInt32 +IClipboard::readUInt32(const char* buf) +{ + const unsigned char* ubuf = reinterpret_cast(buf); + return (static_cast(ubuf[0]) << 24) | + (static_cast(ubuf[1]) << 16) | + (static_cast(ubuf[2]) << 8) | + static_cast(ubuf[3]); +} + +void +IClipboard::writeUInt32(CString* buf, UInt32 v) +{ + *buf += static_cast((v >> 24) & 0xff); + *buf += static_cast((v >> 16) & 0xff); + *buf += static_cast((v >> 8) & 0xff); + *buf += static_cast( v & 0xff); +} diff --git a/lib/synergy/IClipboard.h b/lib/synergy/IClipboard.h index 25defae6..c0bfbeb4 100644 --- a/lib/synergy/IClipboard.h +++ b/lib/synergy/IClipboard.h @@ -111,7 +111,45 @@ public: */ virtual CString get(EFormat) const = 0; + //! Marshall clipboard data + /*! + Merge \p clipboard's data into a single buffer that can be later + unmarshalled to restore the clipboard and return the buffer. + */ + static CString marshall(const IClipboard* clipboard); + + //! Unmarshall clipboard data + /*! + Extract marshalled clipboard data and store it in \p clipboard. + Sets the clipboard time to \c time. + */ + static void unmarshall(IClipboard* clipboard, + const CString& data, Time time); + + //! Copy clipboard + /*! + Transfers all the data in one clipboard to another. The + clipboards can be of any concrete clipboard type (and + they don't have to be the same type). This also sets + the destination clipboard's timestamp to source clipboard's + timestamp. Returns true iff the copy succeeded. + */ + static bool copy(IClipboard* dst, const IClipboard* src); + + //! Copy clipboard + /*! + Transfers all the data in one clipboard to another. The + clipboards can be of any concrete clipboard type (and they + don't have to be the same type). This also sets the + timestamp to \c time. Returns true iff the copy succeeded. + */ + static bool copy(IClipboard* dst, const IClipboard* src, Time); + //@} + +private: + static UInt32 readUInt32(const char*); + static void writeUInt32(CString*, UInt32); }; #endif diff --git a/lib/synergy/IPlatformScreen.cpp b/lib/synergy/IPlatformScreen.cpp index 78d1e436..a9a37418 100644 --- a/lib/synergy/IPlatformScreen.cpp +++ b/lib/synergy/IPlatformScreen.cpp @@ -104,14 +104,16 @@ IPlatformScreen::getScreensaverDeactivatedEvent() // IPlatformScreen::CKeyInfo // -IPlatformScreen::CKeyInfo::CKeyInfo(KeyID id, - KeyModifierMask mask, KeyButton button, SInt32 count) : - m_key(id), - m_mask(mask), - m_button(button), - m_count(count) +IPlatformScreen::CKeyInfo* +IPlatformScreen::CKeyInfo::alloc(KeyID id, + KeyModifierMask mask, KeyButton button, SInt32 count) { - // do nothing + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); + info->m_key = id; + info->m_mask = mask; + info->m_button = button; + info->m_count = count; + return info; } @@ -119,10 +121,12 @@ IPlatformScreen::CKeyInfo::CKeyInfo(KeyID id, // IPlatformScreen::CButtonInfo // -IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) : - m_button(id) +IPlatformScreen::CButtonInfo* +IPlatformScreen::CButtonInfo::alloc(ButtonID id) { - // do nothing + CButtonInfo* info = (CButtonInfo*)malloc(sizeof(CButtonInfo)); + info->m_button = id; + return info; } @@ -130,11 +134,13 @@ IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) : // IPlatformScreen::CMotionInfo // -IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) : - m_x(x), - m_y(y) +IPlatformScreen::CMotionInfo* +IPlatformScreen::CMotionInfo::alloc(SInt32 x, SInt32 y) { - // do nothing + CMotionInfo* info = (CMotionInfo*)malloc(sizeof(CMotionInfo)); + info->m_x = x; + info->m_y = y; + return info; } @@ -142,8 +148,10 @@ IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) : // IPlatformScreen::CWheelInfo // -IPlatformScreen::CWheelInfo::CWheelInfo(SInt32 wheel) : - m_wheel(wheel) +IPlatformScreen::CWheelInfo* +IPlatformScreen::CWheelInfo::alloc(SInt32 wheel) { - // do nothing + CWheelInfo* info = (CWheelInfo*)malloc(sizeof(CWheelInfo)); + info->m_wheel = wheel; + return info; } diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index 456d58d9..bae81e86 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -40,7 +40,7 @@ public: //! Key event data class CKeyInfo { public: - CKeyInfo(KeyID, KeyModifierMask, KeyButton, SInt32 count); + static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count); public: KeyID m_key; @@ -51,7 +51,7 @@ public: //! Button event data class CButtonInfo { public: - CButtonInfo(ButtonID); + static CButtonInfo* alloc(ButtonID); public: ButtonID m_button; @@ -59,7 +59,7 @@ public: //! Motion event data class CMotionInfo { public: - CMotionInfo(SInt32 x, SInt32 y); + static CMotionInfo* alloc(SInt32 x, SInt32 y); public: SInt32 m_x; @@ -68,7 +68,7 @@ public: //! Wheel motion event data class CWheelInfo { public: - CWheelInfo(SInt32); + static CWheelInfo* alloc(SInt32); public: SInt32 m_wheel; diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index ed4763fa..e34363d7 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -29,6 +29,7 @@ libsynergy_a_SOURCES = \ CPacketStreamFilter.cpp \ CProtocolUtil.cpp \ CScreen.cpp \ + IClipboard.cpp \ IPlatformScreen.cpp \ IScreen.cpp \ XScreen.cpp \ diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index 539ddaf6..c1162c2d 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -267,19 +267,11 @@ public: */ SInt32 m_w, m_h; - //! Jump zone size - /*! - This is the size of the jump zone. The cursor jumps to the adjacent - screen when it comes within this many pixels of the edge of the screen. - */ - SInt32 m_zoneSize; + //! Obsolete (jump zone size) + SInt32 obsolete1; - //! Mouse position - /*! - The position of the cursor. This is not kept up-to-date so it's - only meaningful when receiving an update. - */ - SInt32 m_mx, m_my; + //! Obsolete (mouse position) + SInt32 obsolete2, obsolete3; }; #endif From 612a2054e6a5ce79373da047dae13c28d1c29aaa Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 15 Feb 2004 18:12:35 +0000 Subject: [PATCH 579/807] Updated Makefiles and win32 projects and removed dead classes. --- lib/base/CFunctionEventJob.cpp | 2 +- lib/base/CJobList.cpp | 113 -------------------- lib/base/CJobList.h | 72 ------------- lib/base/Makefile.am | 4 +- lib/base/base.dsp | 56 +++++++++- lib/io/io.dsp | 44 ++------ lib/mt/CTimerThread.cpp | 65 ----------- lib/mt/CTimerThread.h | 56 ---------- lib/mt/Makefile.am | 4 +- lib/mt/mt.dsp | 8 -- lib/net/net.dsp | 32 ++++++ lib/platform/CMSWindowsEventQueueBuffer.cpp | 0 lib/platform/CMSWindowsEventQueueBuffer.h | 0 lib/platform/Makefile.am | 2 + lib/platform/platform.dsp | 8 ++ lib/server/server.dsp | 16 ++- lib/synergy/libsynergy.dsp | 38 +++---- 17 files changed, 135 insertions(+), 385 deletions(-) delete mode 100644 lib/base/CJobList.cpp delete mode 100644 lib/base/CJobList.h delete mode 100644 lib/mt/CTimerThread.cpp delete mode 100644 lib/mt/CTimerThread.h create mode 100644 lib/platform/CMSWindowsEventQueueBuffer.cpp create mode 100644 lib/platform/CMSWindowsEventQueueBuffer.h diff --git a/lib/base/CFunctionEventJob.cpp b/lib/base/CFunctionEventJob.cpp index 1a55aa00..5afdc988 100644 --- a/lib/base/CFunctionEventJob.cpp +++ b/lib/base/CFunctionEventJob.cpp @@ -15,7 +15,7 @@ #include "CFunctionEventJob.h" // -// CFunctionJob +// CFunctionEventJob // CFunctionEventJob::CFunctionEventJob( diff --git a/lib/base/CJobList.cpp b/lib/base/CJobList.cpp deleted file mode 100644 index 37358890..00000000 --- a/lib/base/CJobList.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CJobList.h" -#include "IJob.h" -#include "CArch.h" - -// -// CJobList -// - -CJobList::CJobList() -{ - m_mutex = ARCH->newMutex(); -} - -CJobList::~CJobList() -{ - ARCH->closeMutex(m_mutex); -} - -void -CJobList::addJob(IJob* job) -{ - // ignore bogus job - if (job == NULL) { - return; - } - - // make a temporary list with the job. later we'll splice this - // list into our jobs list. since splice() never throws we - // don't have to try/catch to unlock the mutex. - CJobs tmpList; - tmpList.push_front(job); - - // add job to list - ARCH->lockMutex(m_mutex); - m_jobs.splice(m_jobs.begin(), tmpList); - ARCH->unlockMutex(m_mutex); -} - -void -CJobList::removeJob(IJob* job) -{ - if (job != NULL) { - ARCH->lockMutex(m_mutex); - m_jobs.remove(job); - ARCH->unlockMutex(m_mutex); - } -} - -void -CJobList::runJobs() const -{ - // this is a little tricky. to allow any number of threads to - // traverse the list while jobs are added and removed while - // not holding the mutex when running a job (so other threads - // or the running job can add and remove jobs), we insert a - // new element into the list. this element has a NULL job and - // is a "safe place" while we traverse the list. the safe place - // is inserted at the start of the list (with the mutex locked) - // then, for each job, we lock the mutex and move the safe place - // past the next job, unlock the mutex, run the job and repeat - // until there are no more jobs. the safe place will not be - // removed by any other thread and is after every job that has - // been run and before every job that still needs to run. when - // all the jobs have been run we remove the safe place. - - // add the safe place - CJobs tmpList; - tmpList.push_front(NULL); - ARCH->lockMutex(m_mutex); - m_jobs.splice(m_jobs.begin(), tmpList); - CJobs::iterator safePlace = m_jobs.begin(); - - // find the next non-NULL job (NULL jobs are safe places) - CJobs::iterator next = safePlace; - while (next != m_jobs.end() && *next == NULL) - ++next; - while (next != m_jobs.end()) { - // found a job. run it without holding a lock. note the - // race condition here: we release the lock, allowing - // removeJob() to remove this job before we run the job. - // therefore the caller cannot safely destroy the job - // until all runJobs() complete. - IJob* job = *next; - ++next; - m_jobs.splice(next, m_jobs, safePlace); - ARCH->unlockMutex(m_mutex); - job->run(); - - // next real job - ARCH->lockMutex(m_mutex); - next = safePlace; - while (next != m_jobs.end() && *next == NULL) - ++next; - } - - // remove the safe place - m_jobs.erase(safePlace); - ARCH->unlockMutex(m_mutex); -} diff --git a/lib/base/CJobList.h b/lib/base/CJobList.h deleted file mode 100644 index 780d19da..00000000 --- a/lib/base/CJobList.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CJOBLIST_H -#define CJOBLIST_H - -#include "IArchMultithread.h" -#include "stdlist.h" - -class IJob; - -class CJobList { -public: - CJobList(); - ~CJobList(); - - //! @name manipulators - //@{ - - //! Add a job - /*! - Add a job to the list. The client keeps ownership of the job. - Jobs can be safely added while \c runJobs() is executing. - */ - void addJob(IJob*); - - //! Remove a job - /*! - Remove a job from the list. The client keeps ownership of the job. - Jobs can be safely removed while \c runJobs() is executing. - */ - void removeJob(IJob*); - - //@} - //! @name accessors - //@{ - - //! Run all jobs - /*! - Run all jobs in the list. Any number of threads can call - \c runJobs() at once. Jobs can be added and removed while - \c runJobs() is executing in the same or another thread. - Any job added after \c runJobs() starts will not be run - by that call to runJobs(). Destroying a removed job - while \c runJobs() is executing is not safe unless the - removed completed before \c runJobs() started. - */ - void runJobs() const; - - //@} - -private: - typedef std::list CJobs; - typedef CJobs::iterator iterator; - - CArchMutex m_mutex; - mutable CJobs m_jobs; -}; - -#endif - diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index 9eb2a526..6ac0f716 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -29,7 +29,6 @@ libbase_a_SOURCES = \ CEventQueue.cpp \ CFunctionEventJob.cpp \ CFunctionJob.cpp \ - CJobList.cpp \ CLog.cpp \ CSimpleEventQueueBuffer.cpp \ CStopwatch.cpp \ @@ -40,8 +39,8 @@ libbase_a_SOURCES = \ XBase.cpp \ CEvent.h \ CEventQueue.h \ + CFunctionEventJob.h \ CFunctionJob.h \ - CJobList.h \ CLog.h \ CPriorityQueue.h \ CSimpleEventQueueBuffer.h \ @@ -49,6 +48,7 @@ libbase_a_SOURCES = \ CString.h \ CStringUtil.h \ CUnicode.h \ + IEventJob.h \ IEventQueue.h \ IEventQueueBuffer.h \ IJob.h \ diff --git a/lib/base/base.dsp b/lib/base/base.dsp index e0c96638..a20e4fb4 100644 --- a/lib/base/base.dsp +++ b/lib/base/base.dsp @@ -87,15 +87,27 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CEvent.cpp +# End Source File +# Begin Source File + +SOURCE=.\CEventQueue.cpp +# End Source File +# Begin Source File + +SOURCE=.\CFunctionEventJob.cpp +# End Source File +# Begin Source File + SOURCE=.\CFunctionJob.cpp # End Source File # Begin Source File -SOURCE=.\CJobList.cpp +SOURCE=.\CLog.cpp # End Source File # Begin Source File -SOURCE=.\CLog.cpp +SOURCE=.\CSimpleEventQueueBuffer.cpp # End Source File # Begin Source File @@ -111,6 +123,10 @@ SOURCE=.\CUnicode.cpp # End Source File # Begin Source File +SOURCE=.\IEventQueue.cpp +# End Source File +# Begin Source File + SOURCE=.\LogOutputters.cpp # End Source File # Begin Source File @@ -123,15 +139,31 @@ SOURCE=.\XBase.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CEvent.h +# End Source File +# Begin Source File + +SOURCE=.\CEventQueue.h +# End Source File +# Begin Source File + +SOURCE=.\CFunctionEventJob.h +# End Source File +# Begin Source File + SOURCE=.\CFunctionJob.h # End Source File # Begin Source File -SOURCE=.\CJobList.h +SOURCE=.\CLog.h # End Source File # Begin Source File -SOURCE=.\CLog.h +SOURCE=.\CPriorityQueue.h +# End Source File +# Begin Source File + +SOURCE=.\CSimpleEventQueueBuffer.h # End Source File # Begin Source File @@ -151,6 +183,18 @@ SOURCE=.\CUnicode.h # End Source File # Begin Source File +SOURCE=.\IEventJob.h +# End Source File +# Begin Source File + +SOURCE=.\IEventQueue.h +# End Source File +# Begin Source File + +SOURCE=.\IEventQueueBuffer.h +# End Source File +# Begin Source File + SOURCE=.\IJob.h # End Source File # Begin Source File @@ -163,6 +207,10 @@ SOURCE=.\LogOutputters.h # End Source File # Begin Source File +SOURCE=.\TMethodEventJob.h +# End Source File +# Begin Source File + SOURCE=.\TMethodJob.h # End Source File # Begin Source File diff --git a/lib/io/io.dsp b/lib/io/io.dsp index a6afae06..c1346d05 100644 --- a/lib/io/io.dsp +++ b/lib/io/io.dsp @@ -87,26 +87,18 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File -SOURCE=.\CBufferedInputStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\CBufferedOutputStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\CInputStreamFilter.cpp -# End Source File -# Begin Source File - -SOURCE=.\COutputStreamFilter.cpp -# End Source File -# Begin Source File - SOURCE=.\CStreamBuffer.cpp # End Source File # Begin Source File +SOURCE=.\CStreamFilter.cpp +# End Source File +# Begin Source File + +SOURCE=.\IStream.cpp +# End Source File +# Begin Source File + SOURCE=.\XIO.cpp # End Source File # End Group @@ -115,31 +107,15 @@ SOURCE=.\XIO.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\CBufferedInputStream.h -# End Source File -# Begin Source File - -SOURCE=.\CBufferedOutputStream.h -# End Source File -# Begin Source File - -SOURCE=.\CInputStreamFilter.h -# End Source File -# Begin Source File - -SOURCE=.\COutputStreamFilter.h -# End Source File -# Begin Source File - SOURCE=.\CStreamBuffer.h # End Source File # Begin Source File -SOURCE=.\IInputStream.h +SOURCE=.\CStreamFilter.h # End Source File # Begin Source File -SOURCE=.\IOutputStream.h +SOURCE=.\IStream.h # End Source File # Begin Source File diff --git a/lib/mt/CTimerThread.cpp b/lib/mt/CTimerThread.cpp deleted file mode 100644 index 2ac63522..00000000 --- a/lib/mt/CTimerThread.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CTimerThread.h" -#include "CThread.h" -#include "TMethodJob.h" -#include "CLog.h" -#include "CArch.h" - -// -// CTimerThread -// - -CTimerThread::CTimerThread(double timeout, bool* timedOut) : - m_timeout(timeout), - m_timedOut(timedOut) -{ - if (m_timedOut != NULL) { - *m_timedOut = false; - } - if (m_timeout >= 0.0) { - m_callingThread = new CThread(CThread::getCurrentThread()); - m_timingThread = new CThread(new TMethodJob( - this, &CTimerThread::timer)); - } - else { - m_callingThread = NULL; - m_timingThread = NULL; - } -} - -CTimerThread::~CTimerThread() -{ - if (m_timingThread != NULL) { - LOG((CLOG_DEBUG1 "cancelling timeout")); - m_timingThread->cancel(); - m_timingThread->wait(); - LOG((CLOG_DEBUG1 "cancelled timeout")); - delete m_timingThread; - delete m_callingThread; - } -} - -void -CTimerThread::timer(void*) -{ - LOG((CLOG_DEBUG1 "timeout in %f seconds", m_timeout)); - ARCH->sleep(m_timeout); - LOG((CLOG_DEBUG1 "timeout")); - if (m_timedOut != NULL) { - *m_timedOut = true; - } - m_callingThread->cancel(); -} diff --git a/lib/mt/CTimerThread.h b/lib/mt/CTimerThread.h deleted file mode 100644 index 1a0a2b57..00000000 --- a/lib/mt/CTimerThread.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CTIMERTHREAD_H -#define CTIMERTHREAD_H - -#include "common.h" - -class CThread; - -//! A timer thread -/*! -An object of this class cancels the thread that called the c'tor unless -the object is destroyed before a given timeout. -*/ -class CTimerThread { -public: - //! Cancel calling thread after \c timeout seconds - /*! - Cancels the calling thread after \c timeout seconds unless destroyed - before then. If \c timeout is less than zero then it never times - out and this is a no-op. If \c timedOutFlag is not NULL then it's - set to false in the c'tor and to true if the timeout exipires before - it's cancelled. - */ - CTimerThread(double timeout, bool* timedOutFlag = NULL); - //! Cancel the timer thread - ~CTimerThread(); - -private: - void timer(void*); - - // not implemented - CTimerThread(const CTimerThread&); - CTimerThread& operator=(const CTimerThread&); - -private: - double m_timeout; - bool* m_timedOut; - CThread* m_callingThread; - CThread* m_timingThread; -}; - -#endif - diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index 6eda91d6..1d0dc5ff 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -25,17 +25,15 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libmt.a libmt_a_SOURCES = \ + CCondVar.cpp \ CLock.cpp \ CMutex.cpp \ - CCondVar.cpp \ CThread.cpp \ - CTimerThread.cpp \ XMT.cpp \ CCondVar.h \ CLock.h \ CMutex.h \ CThread.h \ - CTimerThread.h \ XMT.h \ XThread.h \ $(NULL) diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp index f926fc1d..fd5b18fd 100644 --- a/lib/mt/mt.dsp +++ b/lib/mt/mt.dsp @@ -103,10 +103,6 @@ SOURCE=.\CThread.cpp # End Source File # Begin Source File -SOURCE=.\CTimerThread.cpp -# End Source File -# Begin Source File - SOURCE=.\XMT.cpp # End Source File # End Group @@ -131,10 +127,6 @@ SOURCE=.\CThread.h # End Source File # Begin Source File -SOURCE=.\CTimerThread.h -# End Source File -# Begin Source File - SOURCE=.\XMT.h # End Source File # Begin Source File diff --git a/lib/net/net.dsp b/lib/net/net.dsp index 2d038c50..42314fc2 100644 --- a/lib/net/net.dsp +++ b/lib/net/net.dsp @@ -91,6 +91,10 @@ SOURCE=.\CNetworkAddress.cpp # End Source File # Begin Source File +SOURCE=.\CSocketMultiplexer.cpp +# End Source File +# Begin Source File + SOURCE=.\CTCPListenSocket.cpp # End Source File # Begin Source File @@ -103,6 +107,18 @@ SOURCE=.\CTCPSocketFactory.cpp # End Source File # Begin Source File +SOURCE=.\IDataSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\IListenSocket.cpp +# End Source File +# Begin Source File + +SOURCE=.\ISocket.cpp +# End Source File +# Begin Source File + SOURCE=.\XSocket.cpp # End Source File # End Group @@ -115,6 +131,10 @@ SOURCE=.\CNetworkAddress.h # End Source File # Begin Source File +SOURCE=.\CSocketMultiplexer.h +# End Source File +# Begin Source File + SOURCE=.\CTCPListenSocket.h # End Source File # Begin Source File @@ -127,6 +147,10 @@ SOURCE=.\CTCPSocketFactory.h # End Source File # Begin Source File +SOURCE=.\IDataSocket.h +# End Source File +# Begin Source File + SOURCE=.\IListenSocket.h # End Source File # Begin Source File @@ -139,6 +163,14 @@ SOURCE=.\ISocketFactory.h # End Source File # Begin Source File +SOURCE=.\ISocketMultiplexerJob.h +# End Source File +# Begin Source File + +SOURCE=.\TSocketMultiplexerMethodJob.h +# End Source File +# Begin Source File + SOURCE=.\XSocket.h # End Source File # End Group diff --git a/lib/platform/CMSWindowsEventQueueBuffer.cpp b/lib/platform/CMSWindowsEventQueueBuffer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/lib/platform/CMSWindowsEventQueueBuffer.h b/lib/platform/CMSWindowsEventQueueBuffer.h new file mode 100644 index 00000000..e69de29b diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index ec33a35a..1aabff53 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -24,6 +24,7 @@ EXTRA_DIST = \ CMSWindowsClipboardTextConverter.cpp \ CMSWindowsClipboardUTF16Converter.cpp \ CMSWindowsDesktop.cpp \ + CMSWindowsEventQueueBuffer.cpp \ CMSWindowsKeyMapper.cpp \ CMSWindowsScreen.cpp \ CMSWindowsScreenSaver.cpp \ @@ -33,6 +34,7 @@ EXTRA_DIST = \ CMSWindowsClipboardTextConverter.h \ CMSWindowsClipboardUTF16Converter.h \ CMSWindowsDesktop.h \ + CMSWindowsEventQueueBuffer.h \ CMSWindowsKeyMapper.h \ CMSWindowsScreen.h \ CMSWindowsScreenSaver.h \ diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index 1fad2189..4dc638dd 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -107,6 +107,10 @@ SOURCE=.\CMSWindowsDesktop.cpp # End Source File # Begin Source File +SOURCE=.\CMSWindowsEventQueueBuffer.cpp +# End Source File +# Begin Source File + SOURCE=.\CMSWindowsKeyMapper.cpp # End Source File # Begin Source File @@ -143,6 +147,10 @@ SOURCE=.\CMSWindowsDesktop.h # End Source File # Begin Source File +SOURCE=.\CMSWindowsEventQueueBuffer.h +# End Source File +# Begin Source File + SOURCE=.\CMSWindowsKeyMapper.h # End Source File # Begin Source File diff --git a/lib/server/server.dsp b/lib/server/server.dsp index 536b33d7..1aa729c5 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -87,6 +87,10 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CClientListener.cpp +# End Source File +# Begin Source File + SOURCE=.\CClientProxy.cpp # End Source File # Begin Source File @@ -99,11 +103,11 @@ SOURCE=.\CClientProxy1_1.cpp # End Source File # Begin Source File -SOURCE=.\CConfig.cpp +SOURCE=.\CClientProxyUnknown.cpp # End Source File # Begin Source File -SOURCE=.\CHTTPServer.cpp +SOURCE=.\CConfig.cpp # End Source File # Begin Source File @@ -119,6 +123,10 @@ SOURCE=.\CServer.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CClientListener.h +# End Source File +# Begin Source File + SOURCE=.\CClientProxy.h # End Source File # Begin Source File @@ -131,11 +139,11 @@ SOURCE=.\CClientProxy1_1.h # End Source File # Begin Source File -SOURCE=.\CConfig.h +SOURCE=.\CClientProxyUnknown.h # End Source File # Begin Source File -SOURCE=.\CHTTPServer.h +SOURCE=.\CConfig.h # End Source File # Begin Source File diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 928405ea..72deabcd 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -91,11 +91,7 @@ SOURCE=.\CClipboard.cpp # End Source File # Begin Source File -SOURCE=.\CInputPacketStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\COutputPacketStream.cpp +SOURCE=.\CPacketStreamFilter.cpp # End Source File # Begin Source File @@ -107,6 +103,18 @@ SOURCE=.\CScreen.cpp # End Source File # Begin Source File +SOURCE=.\IClipboard.cpp +# End Source File +# Begin Source File + +SOURCE=.\IPlatformScreen.cpp +# End Source File +# Begin Source File + +SOURCE=.\IScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\XScreen.cpp # End Source File # Begin Source File @@ -123,15 +131,11 @@ SOURCE=.\CClipboard.h # End Source File # Begin Source File -SOURCE=.\CInputPacketStream.h -# End Source File -# Begin Source File - SOURCE=.\ClipboardTypes.h # End Source File # Begin Source File -SOURCE=.\COutputPacketStream.h +SOURCE=.\CPacketStreamFilter.h # End Source File # Begin Source File @@ -163,15 +167,7 @@ SOURCE=.\IPrimaryScreen.h # End Source File # Begin Source File -SOURCE=.\IPrimaryScreenReceiver.h -# End Source File -# Begin Source File - -SOURCE=.\IScreenFactory.h -# End Source File -# Begin Source File - -SOURCE=.\IScreenReceiver.h +SOURCE=.\IScreen.h # End Source File # Begin Source File @@ -183,10 +179,6 @@ SOURCE=.\ISecondaryScreen.h # End Source File # Begin Source File -SOURCE=.\IServer.h -# End Source File -# Begin Source File - SOURCE=.\KeyTypes.h # End Source File # Begin Source File From 54acf38d8241532836e8ba8059e5fd878df2a516 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 28 Feb 2004 12:19:49 +0000 Subject: [PATCH 580/807] Merged Win32 updates. Added full warnings on g++. Fixed bug in client when handling server rejection. --- acinclude.m4 | 30 + cmd/launcher/LaunchUtil.cpp | 24 +- cmd/launcher/launcher.rc | 5 +- cmd/launcher/resource.h | 1 + .../CMSWindowsClientTaskBarReceiver.cpp | 15 +- .../CMSWindowsClientTaskBarReceiver.h | 1 + .../CXWindowsClientTaskBarReceiver.cpp | 3 +- cmd/synergyc/CXWindowsClientTaskBarReceiver.h | 4 +- cmd/synergyc/Makefile.am | 1 + cmd/synergyc/resource.h | 2 + cmd/synergyc/synergyc.cpp | 332 ++-- cmd/synergyc/synergyc.rc | 2 + .../CMSWindowsServerTaskBarReceiver.cpp | 23 +- .../CMSWindowsServerTaskBarReceiver.h | 1 + .../CXWindowsServerTaskBarReceiver.cpp | 3 +- cmd/synergys/CXWindowsServerTaskBarReceiver.h | 4 +- cmd/synergys/Makefile.am | 1 + cmd/synergys/resource.h | 2 + cmd/synergys/synergys.cpp | 361 ++--- cmd/synergys/synergys.rc | 2 + configure.in | 3 + lib/arch/CArch.cpp | 22 +- lib/arch/CArch.h | 3 +- lib/arch/CArchConsoleWindows.cpp | 4 +- lib/arch/CArchDaemonWindows.cpp | 235 ++- lib/arch/CArchDaemonWindows.h | 18 +- lib/arch/CArchMiscWindows.cpp | 33 + lib/arch/CArchMiscWindows.h | 25 + lib/arch/CArchMultithreadPosix.cpp | 27 +- lib/arch/CArchMultithreadPosix.h | 15 +- lib/arch/CArchMultithreadWindows.cpp | 194 +-- lib/arch/CArchMultithreadWindows.h | 15 +- lib/arch/CArchNetworkBSD.cpp | 14 +- lib/arch/CArchNetworkBSD.h | 1 + lib/arch/CArchNetworkWinsock.cpp | 357 +++-- lib/arch/CArchNetworkWinsock.h | 10 +- lib/arch/CArchTaskBarWindows.cpp | 217 +-- lib/arch/CArchTaskBarWindows.h | 11 +- lib/arch/IArchMultithread.h | 31 - lib/arch/IArchNetwork.h | 14 +- lib/arch/arch.dsp | 4 - lib/base/CEventQueue.cpp | 6 +- lib/base/CPriorityQueue.h | 4 + lib/base/CSimpleEventQueueBuffer.cpp | 2 +- lib/client/CServerProxy.cpp | 290 ++-- lib/client/CServerProxy.h | 17 +- lib/common/Makefile.am | 7 +- lib/common/Version.h | 12 +- lib/common/common.dsp | 4 + lib/mt/CThread.cpp | 24 +- lib/mt/CThread.h | 39 +- lib/net/CSocketMultiplexer.cpp | 9 +- lib/net/IDataSocket.cpp | 15 + lib/net/IDataSocket.h | 8 +- lib/platform/CMSWindowsDesktop.h | 6 + lib/platform/CMSWindowsEventQueueBuffer.cpp | 129 ++ lib/platform/CMSWindowsEventQueueBuffer.h | 44 + lib/platform/CMSWindowsKeyMapper.cpp | 2 +- lib/platform/CMSWindowsScreen.cpp | 1412 +++++++++-------- lib/platform/CMSWindowsScreen.h | 122 +- lib/platform/CSynergyHook.cpp | 207 +-- lib/platform/CSynergyHook.h | 2 + lib/platform/CXWindowsClipboard.cpp | 2 +- lib/platform/CXWindowsEventQueueBuffer.cpp | 3 +- lib/platform/CXWindowsKeyMapper.cpp | 5 +- lib/platform/CXWindowsScreen.cpp | 2 +- lib/platform/Makefile.am | 2 + lib/platform/platform.dsp | 8 + lib/server/CClientListener.cpp | 4 +- lib/server/CServer.cpp | 48 +- lib/synergy/CProtocolUtil.cpp | 2 +- lib/synergy/CProtocolUtil.h | 2 +- lib/synergy/CScreen.cpp | 6 +- lib/synergy/IScreen.h | 3 +- 74 files changed, 2333 insertions(+), 2185 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 2ebdc27f..62009432 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -513,3 +513,33 @@ else $2 fi ])dnl ACX_PTHREAD + +dnl enable maximum compiler warnings +dnl we only know how to do this for g++ +AC_DEFUN([ACX_CXX_WARNINGS], [ + AC_MSG_CHECKING([for C++ compiler warning flags]) + if test "$GXX" = "yes"; then + acx_cxx_warnings="-Wall" + fi + if test -n "$acx_cxx_warnings"; then + CXXFLAGS="$CXXFLAGS $acx_cxx_warnings" + else + acx_cxx_warnings="unknown" + fi + AC_MSG_RESULT($acx_cxx_warnings) +])dnl ACX_CXX_WARNINGS + +dnl enable compiler warnings are errors +dnl we only know how to do this for g++ +AC_DEFUN([ACX_CXX_WARNINGS_ARE_ERRORS], [ + AC_MSG_CHECKING([for C++ compiler warning are errors flags]) + if test "$GXX" = "yes"; then + acx_cxx_warnings_are_errors="-Werror" + fi + if test -n "$acx_cxx_warnings_are_errors"; then + CXXFLAGS="$CXXFLAGS $acx_cxx_warnings_are_errors" + else + acx_cxx_warnings_are_errors="unknown" + fi + AC_MSG_RESULT($acx_cxx_warnings_are_errors) +])dnl ACX_CXX_WARNINGS_ARE_ERRORS diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp index 46c8ef97..240fd1d9 100644 --- a/cmd/launcher/LaunchUtil.cpp +++ b/cmd/launcher/LaunchUtil.cpp @@ -14,6 +14,7 @@ #include "CConfig.h" #include "LaunchUtil.h" +#include "CMSWindowsUtil.h" #include "CArch.h" #include "resource.h" #include "stdfstream.h" @@ -23,32 +24,13 @@ CString getString(DWORD id) { - char buffer[1024]; - buffer[0] = '\0'; - LoadString(s_instance, id, buffer, sizeof(buffer) / sizeof(buffer[0])); - return buffer; + return CMSWindowsUtil::getString(s_instance, id); } CString getErrorString(DWORD error) { - char* buffer; - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM, - 0, - error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&buffer, - 0, - NULL) == 0) { - return getString(IDS_ERROR); - } - else { - CString result(buffer); - LocalFree(buffer); - return result; - } + return CMSWindowsUtil::getErrorString(s_instance, error, IDS_ERROR); } void diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index a8bf8c7b..0a5ceb01 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -332,8 +332,8 @@ BEGIN "Synergy is not configured to start automatically." IDS_INSTALL_LABEL "Install" IDS_UNINSTALL_LABEL "Uninstall" - IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}." - IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}." + IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}" + IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}" IDS_INSTALL_TITLE "Installed Auto-Start" IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." @@ -348,6 +348,7 @@ BEGIN IDS_SERVER_IS_CLIENT "Please enter the computer name of the synergy server, not\nthe name of this computer, in the Server Host Name field." IDS_ADD_SCREEN "Add Screen" IDS_EDIT_SCREEN "Edit Screen %{1}" + IDS_ERROR_CODE "Error code: %{1}" END #endif // English (U.S.) resources diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index 2885b2f0..3ef98665 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -41,6 +41,7 @@ #define IDS_ADD_SCREEN 37 #define IDS_EDIT_SCREEN 38 #define IDS_INVALID_TIME 39 +#define IDS_ERROR_CODE 39 #define IDD_MAIN 101 #define IDD_ADD 102 #define IDD_WAIT 103 diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp index 3bfd5816..c53ea1c8 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -21,18 +21,19 @@ #include "CArchTaskBarWindows.h" #include "resource.h" -static const UINT g_stateToIconID[CMSWindowsClientTaskBarReceiver::kMaxState] = +// +// CMSWindowsClientTaskBarReceiver +// + +const UINT CMSWindowsClientTaskBarReceiver::s_stateToIconID[kMaxState] = { IDI_TASKBAR_NOT_RUNNING, IDI_TASKBAR_NOT_WORKING, IDI_TASKBAR_NOT_CONNECTED, + IDI_TASKBAR_NOT_CONNECTED, IDI_TASKBAR_CONNECTED }; -// -// CMSWindowsClientTaskBarReceiver -// - CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver( HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : CClientTaskBarReceiver(), @@ -41,7 +42,7 @@ CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver( m_logBuffer(logBuffer) { for (UInt32 i = 0; i < kMaxState; ++i) { - m_icon[i] = loadIcon(g_stateToIconID[i]); + m_icon[i] = loadIcon(s_stateToIconID[i]); } m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR)); @@ -171,7 +172,7 @@ CMSWindowsClientTaskBarReceiver::primaryAction() const IArchTaskBarReceiver::Icon CMSWindowsClientTaskBarReceiver::getIcon() const { - return reinterpret_cast(m_icon[getState()]); + return reinterpret_cast(m_icon[getStatus()]); } void diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h index 56a3f161..72d33be5 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h @@ -58,6 +58,7 @@ private: HMENU m_menu; HICON m_icon[kMaxState]; const CBufferedLogOutputter* m_logBuffer; + static const UINT s_stateToIconID[]; }; #endif diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp index 13fac23f..681f9be5 100644 --- a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp @@ -19,7 +19,8 @@ // CXWindowsClientTaskBarReceiver // -CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver() +CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver( + const CBufferedLogOutputter*) { // add ourself to the task bar ARCH->addReceiver(this); diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h index fab3a649..fa9da471 100644 --- a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h @@ -17,10 +17,12 @@ #include "CClientTaskBarReceiver.h" +class CBufferedLogOutputter; + //! Implementation of CClientTaskBarReceiver for X Windows class CXWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { public: - CXWindowsClientTaskBarReceiver(); + CXWindowsClientTaskBarReceiver(const CBufferedLogOutputter*); virtual ~CXWindowsClientTaskBarReceiver(); // IArchTaskBarReceiver overrides diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 6b2510ce..4f53508b 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -48,6 +48,7 @@ synergyc_LDADD = \ $(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/common/libcommon.a \ $(DEPTH)/lib/arch/libarch.a \ $(X_LIBS) \ $(X_PRE_LIBS) \ diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h index 1da78601..37f085d6 100644 --- a/cmd/synergyc/resource.h +++ b/cmd/synergyc/resource.h @@ -3,6 +3,8 @@ // Used by synergyc.rc // #define IDS_FAILED 1 +#define IDS_INIT_FAILED 2 +#define IDS_UNCAUGHT_EXCEPTION 3 #define IDI_SYNERGY 101 #define IDI_TASKBAR_NOT_RUNNING 102 #define IDI_TASKBAR_NOT_WORKING 103 diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 1515acac..7ca94409 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -26,6 +26,7 @@ #include "CFunctionEventJob.h" #include "CLog.h" #include "CString.h" +#include "CStringUtil.h" #include "LogOutputters.h" #include "CArch.h" #include "XArch.h" @@ -33,8 +34,9 @@ #define DAEMON_RUNNING(running_) #if WINDOWS_LIKE -#include "CMSWindowsScreen.h" #include "CArchMiscWindows.h" +#include "CMSWindowsScreen.h" +#include "CMSWindowsUtil.h" #include "CMSWindowsClientTaskBarReceiver.h" #include "resource.h" #undef DAEMON_RUNNING @@ -51,6 +53,9 @@ #define DAEMON_NAME "synergyc" #endif +typedef int (*StartupFunc)(int, char**); +static void parse(int argc, const char* const* argv); + // // program arguments // @@ -64,7 +69,8 @@ public: m_backend(false), m_restartable(true), m_daemon(true), - m_logFilter(NULL) + m_logFilter(NULL), + m_serverAddress(NULL) { s_instance = this; } ~CArgs() { s_instance = NULL; } @@ -76,7 +82,7 @@ public: bool m_daemon; const char* m_logFilter; CString m_name; - CNetworkAddress m_serverAddress; + CNetworkAddress* m_serverAddress; }; CArgs* CArgs::s_instance = NULL; @@ -97,6 +103,18 @@ createScreen() #endif } +static +CClientTaskBarReceiver* +createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) +{ +#if WINDOWS_LIKE + return new CMSWindowsClientTaskBarReceiver( + CMSWindowsScreen::getInstance(), logBuffer); +#elif UNIX_LIKE + return new CXWindowsClientTaskBarReceiver(logBuffer); +#endif +} + // // platform independent main @@ -292,7 +310,7 @@ startClient() try { clientScreen = openClientScreen(); s_client = openClient(ARG->m_name, - ARG->m_serverAddress, clientScreen); + *ARG->m_serverAddress, clientScreen); s_clientScreen = clientScreen; LOG((CLOG_NOTE "started client")); s_client->connect(); @@ -338,7 +356,7 @@ stopClient() static int -realMain() +mainLoop() { // start the client. if this return false then we've failed and // we shouldn't retry. @@ -350,8 +368,8 @@ realMain() // run event loop. if startClient() failed we're supposed to retry // later. the timer installed by startClient() will take care of // that. - DAEMON_RUNNING(true); CEvent event; + DAEMON_RUNNING(true); EVENTQUEUE->getEvent(event); while (event.getType() != CEvent::kQuit) { EVENTQUEUE->dispatchEvent(event); @@ -369,45 +387,65 @@ realMain() return kExitSuccess; } -/* static -void -realMainEntry(void* vresult) +int +daemonMainLoop(int, const char**) { - *reinterpret_cast(vresult) = realMain(); + CSystemLogger sysLogger(DAEMON_NAME); + return mainLoop(); } static int -runMainInThread(void) +standardStartup(int argc, char** argv) { - int result = 0; - CThread appThread(new CFunctionJob(&realMainEntry, &result)); - try { -#if WINDOWS_LIKE - MSG msg; - while (appThread.waitForEvent(-1.0) == CThread::kEvent) { - // check for a quit event - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) { - CThread::getCurrentThread().cancel(); - } - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } -#else - appThread.wait(-1.0); -#endif - return result; + // parse command line + parse(argc, argv); + + // daemonize if requested + if (ARG->m_daemon) { + return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop); } - catch (XThread&) { - appThread.cancel(); - appThread.wait(-1.0); - throw; + else { + return mainLoop(); } } -*/ + +static +int +run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) +{ + // general initialization + CSocketMultiplexer multiplexer; + CEventQueue eventQueue; + ARG->m_serverAddress = new CNetworkAddress; + ARG->m_pname = ARCH->getBasename(argv[0]); + + // install caller's output filter + if (outputter != NULL) { + CLOG->insert(outputter); + } + + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = createTaskBarReceiver(&logBuffer); + + // run + int result = startup(argc, argv); + + // done with task bar receiver + delete s_taskBarReceiver; + + // done with log buffer + CLOG->remove(&logBuffer); + + delete ARG->m_serverAddress; + return result; +} // @@ -588,7 +626,7 @@ parse(int argc, const char* const* argv) // save server address try { - ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); + *ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); } catch (XSocketAddress& e) { LOG((CLOG_PRINT "%s: %s" BYE, @@ -676,192 +714,106 @@ byeThrow(int x) static int -daemonStartup(int argc, const char** argv) +daemonNTMainLoop(int argc, const char** argv) { - CSystemLogger sysLogger(DAEMON_NAME); - - // have to cancel this thread to quit - s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); - - // catch errors that would normally exit - bye = &byeThrow; - - // parse command line parse(argc, argv); - - // cannot run as backend if running as a service ARG->m_backend = false; - - // run as a service - return CArchMiscWindows::runDaemon(realMain); + return CArchMiscWindows::runDaemon(mainLoop); } static int -daemonStartup95(int, const char**) +daemonNTStartup(int, char**) { CSystemLogger sysLogger(DAEMON_NAME); - return runMainInThread(); + bye = &byeThrow; + return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop); } static -int -run(int argc, char** argv) +void +showError(HINSTANCE instance, const char* title, UINT id, const char* arg) { - // windows NT family starts services using no command line options. - // since i'm not sure how to tell the difference between that and - // a user providing no options we'll assume that if there are no - // arguments and we're on NT then we're being invoked as a service. - // users on NT can use `--daemon' or `--no-daemon' to force us out - // of the service code path. - if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - try { - return ARCH->daemonize(DAEMON_NAME, &daemonStartup); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - } - return kExitFailed; - } - - // parse command line - parse(argc, argv); - - // daemonize if requested - if (ARG->m_daemon) { - // start as a daemon - if (CArchMiscWindows::isWindows95Family()) { - try { - return ARCH->daemonize(DAEMON_NAME, &daemonStartup95); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - } - return kExitFailed; - } - else { - // cannot start a service from the command line so just - // run normally (except with log messages redirected). - CSystemLogger sysLogger(DAEMON_NAME); - return runMainInThread(); - } - } - else { - // run - return runMainInThread(); - } + CString fmt = CMSWindowsUtil::getString(instance, id); + CString msg = CStringUtil::format(fmt.c_str(), arg); + MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { - CArch arch(instance); - CLOG; - CArgs args; - - // save instance - CMSWindowsScreen::init(instance); - - // get program name - ARG->m_pname = ARCH->getBasename(__argv[0]); - - // send PRINT and FATAL output to a message box - CLOG->insert(new CMessageBoxOutputter); - - // save log messages - CBufferedLogOutputter logBuffer(1000); - CLOG->insert(&logBuffer, true); - - // make the task bar receiver. the user can control this app - // through the task bar. - s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance, - &logBuffer); - s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); - - int result; try { - // run in foreground or as a daemon - result = run(__argc, __argv); + CArch arch(instance); + CMSWindowsScreen::init(instance); + CLOG; +// FIXME +// CThread::getCurrentThread().setPriority(-14); + CArgs args; + + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + StartupFunc startup = &standardStartup; + if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + startup = &daemonNTStartup; + } + + // send PRINT and FATAL output to a message box + int result = run(__argc, __argv, new CMessageBoxOutputter, startup); + + // let user examine any messages if we're running as a backend + // by putting up a dialog box before exiting. + if (args.m_backend && s_hasImportantLogMessages) { + showError(instance, args.m_pname, IDS_FAILED, ""); + } + + delete CLOG; + return result; + } + catch (XBase& e) { + showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what()); + throw; + } + catch (XArch& e) { + showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str()); + return kExitFailed; } catch (...) { - // note that we don't rethrow thread cancellation. we'll - // be exiting soon so it doesn't matter. what we'd like - // is for everything after this try/catch to be in a - // finally block. - result = kExitFailed; + showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, ""); + throw; } - - // done with task bar receiver - delete s_taskBarReceiver; - - // done with log buffer - CLOG->remove(&logBuffer); - - // let user examine any messages if we're running as a backend - // by putting up a dialog box before exiting. - if (ARG->m_backend && s_hasImportantLogMessages) { - char msg[1024]; - msg[0] = '\0'; - LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); - MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); - } - - delete CLOG; - return result; } #elif UNIX_LIKE -static -int -daemonStartup(int, const char**) -{ - CSystemLogger sysLogger(DAEMON_NAME); - return realMain(); -} - int main(int argc, char** argv) { - CArch arch; - CLOG; - - // go really fast - CThread::getCurrentThread().setPriority(-14); - - CSocketMultiplexer multiplexer; - CEventQueue eventQueue; - - // get program name CArgs args; - ARG->m_pname = ARCH->getBasename(argv[0]); - - // make the task bar receiver. the user can control this app - // through the task bar. - s_taskBarReceiver = new CXWindowsClientTaskBarReceiver; - - // parse command line - parse(argc, argv); - - // daemonize if requested - int result; - if (ARG->m_daemon) { - try { - result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); - } - catch (XArchDaemon&) { - LOG((CLOG_CRIT "failed to daemonize")); - result = kExitFailed; - } + try { + int result; + CArch arch; + CLOG; + CArgs args; + result = run(argc, argv, NULL, &standardStartup); + delete CLOG; + return result; } - else { - result = realMain(); + catch (XBase& e) { + LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what())); + throw; + } + catch (XArch& e) { + LOG((CLOG_CRIT "Initialization failed: %s" BYE, e.what().c_str())); + return kExitFailed; + } + catch (...) { + LOG((CLOG_CRIT "Uncaught exception: \n")); + throw; } - - // done with task bar receiver - delete s_taskBarReceiver; - - return result; } #else diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index 9fd00206..596ce309 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -99,6 +99,8 @@ END STRINGTABLE DISCARDABLE BEGIN IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." + IDS_INIT_FAILED "Synergy failed to initialize: %{1}" + IDS_UNCAUGHT_EXCEPTION "Uncaught exception: %{1}" END #endif // English (U.S.) resources diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp index ca6525ef..5f5e5a24 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -21,7 +21,11 @@ #include "CArchTaskBarWindows.h" #include "resource.h" -static const UINT g_stateToIconID[CMSWindowsServerTaskBarReceiver::kMaxState] = +// +// CMSWindowsServerTaskBarReceiver +// + +const UINT CMSWindowsServerTaskBarReceiver::s_stateToIconID[kMaxState] = { IDI_TASKBAR_NOT_RUNNING, IDI_TASKBAR_NOT_WORKING, @@ -29,10 +33,6 @@ static const UINT g_stateToIconID[CMSWindowsServerTaskBarReceiver::kMaxState] = IDI_TASKBAR_CONNECTED }; -// -// CMSWindowsServerTaskBarReceiver -// - CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver( HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : CServerTaskBarReceiver(), @@ -41,7 +41,7 @@ CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver( m_logBuffer(logBuffer) { for (UInt32 i = 0; i < kMaxState; ++i) { - m_icon[i] = loadIcon(g_stateToIconID[i]); + m_icon[i] = loadIcon(s_stateToIconID[i]); } m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR)); @@ -77,12 +77,7 @@ CMSWindowsServerTaskBarReceiver::showStatus() std::string status = getToolTip(); // get the connect clients, if any - typedef std::vector CClientList; - CClientList clients; - CServer* server = getServer(); - if (server != NULL) { - server->getClients(clients); - } + const CClients& clients = getClients(); // done getting status unlock(); @@ -92,7 +87,7 @@ CMSWindowsServerTaskBarReceiver::showStatus() SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str()); child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS); SendMessage(child, LB_RESETCONTENT, 0, 0); - for (CClientList::const_iterator index = clients.begin(); + for (CClients::const_iterator index = clients.begin(); index != clients.end(); ) { const char* client = index->c_str(); if (++index == clients.end()) { @@ -191,7 +186,7 @@ CMSWindowsServerTaskBarReceiver::primaryAction() const IArchTaskBarReceiver::Icon CMSWindowsServerTaskBarReceiver::getIcon() const { - return reinterpret_cast(m_icon[getState()]); + return reinterpret_cast(m_icon[getStatus()]); } void diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h index d1c3dc65..ab679077 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h @@ -58,6 +58,7 @@ private: HMENU m_menu; HICON m_icon[kMaxState]; const CBufferedLogOutputter* m_logBuffer; + static const UINT s_stateToIconID[]; }; #endif diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp index b3e683a2..861d2f8c 100644 --- a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp @@ -19,7 +19,8 @@ // CXWindowsServerTaskBarReceiver // -CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver() +CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver( + const CBufferedLogOutputter*) { // add ourself to the task bar ARCH->addReceiver(this); diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.h b/cmd/synergys/CXWindowsServerTaskBarReceiver.h index ad114145..73234123 100644 --- a/cmd/synergys/CXWindowsServerTaskBarReceiver.h +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.h @@ -17,10 +17,12 @@ #include "CServerTaskBarReceiver.h" +class CBufferedLogOutputter; + //! Implementation of CServerTaskBarReceiver for X Windows class CXWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { public: - CXWindowsServerTaskBarReceiver(); + CXWindowsServerTaskBarReceiver(const CBufferedLogOutputter*); virtual ~CXWindowsServerTaskBarReceiver(); // IArchTaskBarReceiver overrides diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index f2c30e76..5ef69af9 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -48,6 +48,7 @@ synergys_LDADD = \ $(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/common/libcommon.a \ $(DEPTH)/lib/arch/libarch.a \ $(X_LIBS) \ $(X_PRE_LIBS) \ diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index 5ac24707..c52355cf 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -3,6 +3,8 @@ // Used by synergys.rc // #define IDS_FAILED 1 +#define IDS_INIT_FAILED 2 +#define IDS_UNCAUGHT_EXCEPTION 3 #define IDI_SYNERGY 101 #define IDI_TASKBAR_NOT_RUNNING 102 #define IDI_TASKBAR_NOT_WORKING 103 diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 98d1402d..d1900fea 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -28,6 +28,8 @@ #include "CEventQueue.h" #include "CFunctionEventJob.h" #include "CLog.h" +#include "CString.h" +#include "CStringUtil.h" #include "LogOutputters.h" #include "CArch.h" #include "XArch.h" @@ -36,8 +38,9 @@ #define DAEMON_RUNNING(running_) #if WINDOWS_LIKE -#include "CMSWindowsScreen.h" #include "CArchMiscWindows.h" +#include "CMSWindowsScreen.h" +#include "CMSWindowsUtil.h" #include "CMSWindowsServerTaskBarReceiver.h" #include "resource.h" #undef DAEMON_RUNNING @@ -63,6 +66,10 @@ #define SYS_CONFIG_NAME "synergy.conf" #endif +typedef int (*StartupFunc)(int, char**); +static void parse(int argc, const char* const* argv); +static void loadConfig(); + // // program arguments // @@ -90,8 +97,8 @@ public: const char* m_configFile; const char* m_logFilter; CString m_name; - CNetworkAddress m_synergyAddress; - CConfig m_config; + CNetworkAddress* m_synergyAddress; + CConfig* m_config; }; CArgs* CArgs::s_instance = NULL; @@ -112,6 +119,18 @@ createScreen() #endif } +static +CServerTaskBarReceiver* +createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) +{ +#if WINDOWS_LIKE + return new CMSWindowsServerTaskBarReceiver( + CMSWindowsScreen::getInstance(), logBuffer); +#elif UNIX_LIKE + return new CXWindowsServerTaskBarReceiver(logBuffer); +#endif +} + // // platform independent main @@ -299,11 +318,11 @@ startServer() CPrimaryClient* primaryClient = NULL; CClientListener* listener = NULL; try { - CString name = ARG->m_config.getCanonicalName(ARG->m_name); + CString name = ARG->m_config->getCanonicalName(ARG->m_name); serverScreen = openServerScreen(); primaryClient = openPrimaryClient(name, serverScreen); - listener = openClientListener(ARG->m_config.getSynergyAddress()); - s_server = openServer(ARG->m_config, primaryClient); + listener = openClientListener(ARG->m_config->getSynergyAddress()); + s_server = openServer(*ARG->m_config, primaryClient); s_serverScreen = serverScreen; s_primaryClient = primaryClient; s_listener = listener; @@ -372,26 +391,26 @@ stopServer() static int -realMain() +mainLoop() { // if configuration has no screens then add this system // as the default - if (ARG->m_config.begin() == ARG->m_config.end()) { - ARG->m_config.addScreen(ARG->m_name); + if (ARG->m_config->begin() == ARG->m_config->end()) { + ARG->m_config->addScreen(ARG->m_name); } // set the contact address, if provided, in the config. // otherwise, if the config doesn't have an address, use // the default. - if (ARG->m_synergyAddress.isValid()) { - ARG->m_config.setSynergyAddress(ARG->m_synergyAddress); + if (ARG->m_synergyAddress->isValid()) { + ARG->m_config->setSynergyAddress(*ARG->m_synergyAddress); } - else if (!ARG->m_config.getSynergyAddress().isValid()) { - ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); + else if (!ARG->m_config->getSynergyAddress().isValid()) { + ARG->m_config->setSynergyAddress(CNetworkAddress(kDefaultPort)); } // canonicalize the primary screen name - CString primaryName = ARG->m_config.getCanonicalName(ARG->m_name); + CString primaryName = ARG->m_config->getCanonicalName(ARG->m_name); if (primaryName.empty()) { LOG((CLOG_CRIT "unknown screen name `%s'", ARG->m_name.c_str())); return kExitFailed; @@ -407,8 +426,8 @@ realMain() // run event loop. if startServer() failed we're supposed to retry // later. the timer installed by startServer() will take care of // that. - DAEMON_RUNNING(true); CEvent event; + DAEMON_RUNNING(true); EVENTQUEUE->getEvent(event); while (event.getType() != CEvent::kQuit) { EVENTQUEUE->dispatchEvent(event); @@ -426,43 +445,71 @@ realMain() return kExitSuccess; } -/* XXX static -void -realMainEntry(void* vresult) +int +daemonMainLoop(int, const char**) { - *reinterpret_cast(vresult) = realMain(); + CSystemLogger sysLogger(DAEMON_NAME); + return mainLoop(); } static int -runMainInThread(void) +standardStartup(int argc, char** argv) { - int result = 0; - CThread appThread(new CFunctionJob(&realMainEntry, &result)); - try { -#if WINDOWS_LIKE - MSG msg; - while (appThread.waitForEvent(-1.0) == CThread::kEvent) { - // check for a quit event - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) { - CThread::getCurrentThread().cancel(); - } - } - } -#else - appThread.wait(-1.0); -#endif - return result; + // parse command line + parse(argc, argv); + + // load configuration + loadConfig(); + + // daemonize if requested + if (ARG->m_daemon) { + return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop); } - catch (XThread&) { - appThread.cancel(); - appThread.wait(-1.0); - throw; + else { + return mainLoop(); } } -*/ + +static +int +run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) +{ + // general initialization + CSocketMultiplexer multiplexer; + CEventQueue eventQueue; + ARG->m_synergyAddress = new CNetworkAddress; + ARG->m_config = new CConfig; + ARG->m_pname = ARCH->getBasename(argv[0]); + + // install caller's output filter + if (outputter != NULL) { + CLOG->insert(outputter); + } + + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = createTaskBarReceiver(&logBuffer); + + // run + int result = startup(argc, argv); + + // done with task bar receiver + delete s_taskBarReceiver; + + // done with log buffer + CLOG->remove(&logBuffer); + + delete ARG->m_config; + delete ARG->m_synergyAddress; + return result; +} + // // command line parsing @@ -606,7 +653,7 @@ parse(int argc, const char* const* argv) else if (isArg(i, argc, argv, "-a", "--address", 1)) { // save listen address try { - ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], + *ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], kDefaultPort); } catch (XSocketAddress& e) { @@ -723,7 +770,7 @@ loadConfig(const char* pathname) if (!configStream) { throw XConfigRead("cannot open file"); } - configStream >> ARG->m_config; + configStream >> *ARG->m_config; LOG((CLOG_DEBUG "configuration read successfully")); return true; } @@ -828,197 +875,107 @@ byeThrow(int x) static int -daemonStartup(int argc, const char** argv) +daemonNTMainLoop(int argc, const char** argv) { - CSystemLogger sysLogger(DAEMON_NAME); - - // catch errors that would normally exit - bye = &byeThrow; - - // parse command line parse(argc, argv); - - // cannot run as backend if running as a service ARG->m_backend = false; - - // load configuration loadConfig(); - - // run as a service - return CArchMiscWindows::runDaemon(realMain); + return CArchMiscWindows::runDaemon(mainLoop); } static int -daemonStartup95(int, const char**) +daemonNTStartup(int, char**) { CSystemLogger sysLogger(DAEMON_NAME); - return runMainInThread(); + bye = &byeThrow; + return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop); } static -int -run(int argc, char** argv) +void +showError(HINSTANCE instance, const char* title, UINT id, const char* arg) { - // windows NT family starts services using no command line options. - // since i'm not sure how to tell the difference between that and - // a user providing no options we'll assume that if there are no - // arguments and we're on NT then we're being invoked as a service. - // users on NT can use `--daemon' or `--no-daemon' to force us out - // of the service code path. - if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - try { - return ARCH->daemonize(DAEMON_NAME, &daemonStartup); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - } - return kExitFailed; - } - - // parse command line - parse(argc, argv); - - // load configuration - loadConfig(); - - // daemonize if requested - if (ARG->m_daemon) { - // start as a daemon - if (CArchMiscWindows::isWindows95Family()) { - try { - return ARCH->daemonize(DAEMON_NAME, &daemonStartup95); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - } - return kExitFailed; - } - else { - // cannot start a service from the command line so just - // run normally (except with log messages redirected). - CSystemLogger sysLogger(DAEMON_NAME); - return runMainInThread(); - } - } - else { - // run - return runMainInThread(); - } + CString fmt = CMSWindowsUtil::getString(instance, id); + CString msg = CStringUtil::format(fmt.c_str(), arg); + MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { - CArch arch(instance); - CLOG; - CArgs args; - - // save instance - CMSWindowsScreen::init(instance); - - // get program name - ARG->m_pname = ARCH->getBasename(__argv[0]); - - // send PRINT and FATAL output to a message box - CLOG->insert(new CMessageBoxOutputter); - - // save log messages - CBufferedLogOutputter logBuffer(1000); - CLOG->insert(&logBuffer, true); - - // make the task bar receiver. the user can control this app - // through the task bar. - s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance, - &logBuffer); - - int result; try { - // run in foreground or as a daemon - result = run(__argc, __argv); + CArch arch(instance); + CMSWindowsScreen::init(instance); + CLOG; +// FIXME +// CThread::getCurrentThread().setPriority(-14); + CArgs args; + + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + StartupFunc startup = &standardStartup; + if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + startup = &daemonNTStartup; + } + + // send PRINT and FATAL output to a message box + int result = run(__argc, __argv, new CMessageBoxOutputter, startup); + + // let user examine any messages if we're running as a backend + // by putting up a dialog box before exiting. + if (args.m_backend && s_hasImportantLogMessages) { + showError(instance, args.m_pname, IDS_FAILED, ""); + } + + delete CLOG; + return result; + } + catch (XBase& e) { + showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what()); + throw; + } + catch (XArch& e) { + showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str()); + return kExitFailed; } catch (...) { - // note that we don't rethrow thread cancellation. we'll - // be exiting soon so it doesn't matter. what we'd like - // is for everything after this try/catch to be in a - // finally block. - result = kExitFailed; + showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, ""); + throw; } - - // done with task bar receiver - delete s_taskBarReceiver; - - // done with log buffer - CLOG->remove(&logBuffer); - - // let user examine any messages if we're running as a backend - // by putting up a dialog box before exiting. - if (ARG->m_backend && s_hasImportantLogMessages) { - char msg[1024]; - msg[0] = '\0'; - LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); - MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); - } - - delete CLOG; - return result; } #elif UNIX_LIKE -static -int -daemonStartup(int, const char**) -{ - CSystemLogger sysLogger(DAEMON_NAME); - return realMain(); -} - int main(int argc, char** argv) { - CArch arch; - CLOG; - - // go really fast - CThread::getCurrentThread().setPriority(-14); - - CSocketMultiplexer multiplexer; - CEventQueue eventQueue; - - // get program name CArgs args; - ARG->m_pname = ARCH->getBasename(argv[0]); - - // make the task bar receiver. the user can control this app - // through the task bar. - s_taskBarReceiver = new CXWindowsServerTaskBarReceiver; - - // parse command line - parse(argc, argv); - - // load configuration - loadConfig(); - - // daemonize if requested - int result; - if (ARG->m_daemon) { - try { - result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); - } - catch (XArchDaemon&) { - LOG((CLOG_CRIT "failed to daemonize")); - result = kExitFailed; - } + try { + int result; + CArch arch; + CLOG; + CArgs args; + result = run(argc, argv, NULL, &standardStartup); + delete CLOG; + return result; } - else { - result = realMain(); + catch (XBase& e) { + LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what())); + throw; + } + catch (XArch& e) { + LOG((CLOG_CRIT "Initialization failed: %s" BYE, e.what().c_str())); + return kExitFailed; + } + catch (...) { + LOG((CLOG_CRIT "Uncaught exception: \n")); + throw; } - - // done with task bar receiver - delete s_taskBarReceiver; - - return result; } #else diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index 9f190754..d528c4dd 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -102,6 +102,8 @@ END STRINGTABLE DISCARDABLE BEGIN IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." + IDS_INIT_FAILED "Synergy failed to initialize: %{1}" + IDS_UNCAUGHT_EXCEPTION "Uncaught exception: %{1}" END #endif // English (U.S.) resources diff --git a/configure.in b/configure.in index 999fb1b7..5bde3874 100644 --- a/configure.in +++ b/configure.in @@ -94,6 +94,9 @@ dnl use AC_REPLACE_FUNCS() for stuff in string.h dnl checks for system services +dnl enable maximum compiler warnings and warnings are errors. +ACX_CXX_WARNINGS +ACX_CXX_WARNINGS_ARE_ERRORS dnl adjust variables for X11 and pthreads CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500" diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index d07bb57e..d8640960 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -351,18 +351,6 @@ CArch::wait(CArchThread thread, double timeout) return m_mt->wait(thread, timeout); } -IArchMultithread::EWaitResult -CArch::waitForEvent(CArchThread thread, double timeout) -{ - return m_mt->waitForEvent(thread, timeout); -} - -void -CArch::unblockThread(CArchThread thread) -{ - m_mt->unblockThread(thread); -} - bool CArch::isSameThread(CArchThread thread1, CArchThread thread2) { @@ -390,13 +378,13 @@ CArch::getIDOfThread(CArchThread thread) void CArch::setInterruptHandler(InterruptFunc func, void* userData) { - return m_mt->setInterruptHandler(func, userData); + m_mt->setInterruptHandler(func, userData); } void CArch::interrupt() { - return m_mt->interrupt(); + m_mt->interrupt(); } CArchSocket @@ -459,6 +447,12 @@ CArch::pollSocket(CPollEntry pe[], int num, double timeout) return m_net->pollSocket(pe, num, timeout); } +void +CArch::unblockPollSocket(CArchThread thread) +{ + m_net->unblockPollSocket(thread); +} + size_t CArch::readSocket(CArchSocket s, void* buf, size_t len) { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index 18c1cd08..f82c27cf 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -116,8 +116,6 @@ public: virtual void setPriorityOfThread(CArchThread, int n); virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); - virtual EWaitResult waitForEvent(CArchThread, double timeout); - virtual void unblockThread(CArchThread thread); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); @@ -136,6 +134,7 @@ public: virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); virtual void connectSocket(CArchSocket s, CArchNetAddress name); virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual void unblockPollSocket(CArchThread thread); virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len); diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp index f479b378..2cdedc71 100644 --- a/lib/arch/CArchConsoleWindows.cpp +++ b/lib/arch/CArchConsoleWindows.cpp @@ -110,7 +110,7 @@ CArchConsoleWindows::getNewlineForConsole() BOOL WINAPI CArchConsoleWindows::signalHandler(DWORD) { - // terminate thread and skip remaining handlers - ARCH->cancelThread(s_thread); + // terminate app and skip remaining handlers + ARCH->interrupt(); return TRUE; } diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index 54dc5b40..edca9385 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -24,10 +24,9 @@ CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL; -CArchDaemonWindows::CArchDaemonWindows() : - m_daemonThread(NULL) +CArchDaemonWindows::CArchDaemonWindows() { - // do nothing + m_quitMessage = RegisterWindowMessage("SynergyDaemonExit"); } CArchDaemonWindows::~CArchDaemonWindows() @@ -55,6 +54,17 @@ CArchDaemonWindows::daemonRunning(bool running) } } +UINT +CArchDaemonWindows::getDaemonQuitMessage() +{ + if (s_daemon != NULL) { + return s_daemon->doGetDaemonQuitMessage(); + } + else { + return 0; + } +} + void CArchDaemonWindows::daemonFailed(int result) { @@ -437,122 +447,89 @@ CArchDaemonWindows::openUserStartupKey() return CArchMiscWindows::openKey(HKEY_CURRENT_USER, s_keyNames); } +bool +CArchDaemonWindows::isRunState(DWORD state) +{ + switch (state) { + case SERVICE_START_PENDING: + case SERVICE_CONTINUE_PENDING: + case SERVICE_RUNNING: + return true; + + default: + return false; + } +} + int CArchDaemonWindows::doRunDaemon(RunFunc run) { // should only be called from DaemonFunc assert(m_serviceMutex != NULL); - assert(run != NULL); + assert(run != NULL); + // create message queue for this thread + MSG dummy; + PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE); + + int result = 0; ARCH->lockMutex(m_serviceMutex); - try { - int result; - m_serviceHandlerWaiting = false; - m_serviceRunning = false; - for (;;) { - // mark server as running - setStatus(SERVICE_RUNNING); - - // run callback in another thread - m_serviceRunning = true; - m_daemonThread = ARCH->newThread( - &CArchDaemonWindows::runDaemonThreadEntry, run); - ARCH->wait(m_daemonThread, -1.0); - result = reinterpret_cast( - ARCH->getResultOfThread(m_daemonThread)); - m_serviceRunning = false; - - // notify handler that the server stopped. if handler - // isn't waiting then we stopped unexpectedly and we - // quit. - if (m_serviceHandlerWaiting) { - m_serviceHandlerWaiting = false; - ARCH->broadcastCondVar(m_serviceCondVar); - } - else { - break; - } - - // wait until we're told what to do next - while (m_serviceState != SERVICE_RUNNING && - m_serviceState != SERVICE_STOPPED) { - ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); - } - - // exit loop if we've been told to stop - if (m_serviceState == SERVICE_STOPPED) { - break; - } - - // done with callback thread - ARCH->closeThread(m_daemonThread); - m_daemonThread = NULL; - } - - // prevent daemonHandler from changing state - m_serviceState = SERVICE_STOPPED; - - // tell service control that the service is stopped. - // FIXME -- hopefully this will ensure that our handler won't - // be called again but i can't find documentation that - // verifies that. if it does it'll crash on the mutex that - // we're about to destroy. - setStatus(m_serviceState); - - // clean up - if (m_daemonThread != NULL) { - ARCH->closeThread(m_daemonThread); - m_daemonThread = NULL; - } - ARCH->unlockMutex(m_serviceMutex); - - return result; - } - catch (...) { - // FIXME -- report error - - // prevent serviceHandler from changing state - m_serviceState = SERVICE_STOPPED; - - // set status - setStatusError(0); - - // wake up serviceHandler if it's waiting then wait for it - if (m_serviceHandlerWaiting) { - m_serviceHandlerWaiting = false; - ARCH->broadcastCondVar(m_serviceCondVar); + m_daemonThreadID = GetCurrentThreadId(); + while (m_serviceState != SERVICE_STOPPED) { + // wait until we're told to start + while (!isRunState(m_serviceState) && + m_serviceState != SERVICE_STOP_PENDING) { ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); - // serviceHandler has exited by now } - ARCH->unlockMutex(m_serviceMutex); - throw; + // run unless told to stop + if (m_serviceState != SERVICE_STOP_PENDING) { + ARCH->unlockMutex(m_serviceMutex); + try { + result = run(); + } + catch (...) { + ARCH->lockMutex(m_serviceMutex); + setStatusError(0); + m_serviceState = SERVICE_STOPPED; + setStatus(m_serviceState); + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->unlockMutex(m_serviceMutex); + throw; + } + ARCH->lockMutex(m_serviceMutex); + } + + // notify of new state + if (m_serviceState == SERVICE_PAUSE_PENDING) { + m_serviceState = SERVICE_PAUSED; + } + else { + m_serviceState = SERVICE_STOPPED; + } + setStatus(m_serviceState); + ARCH->broadcastCondVar(m_serviceCondVar); } + ARCH->unlockMutex(m_serviceMutex); + return result; } void CArchDaemonWindows::doDaemonRunning(bool running) { + ARCH->lockMutex(m_serviceMutex); if (running) { - ARCH->unlockMutex(m_serviceMutex); - } - else { - ARCH->lockMutex(m_serviceMutex); + m_serviceState = SERVICE_RUNNING; + setStatus(m_serviceState); + ARCH->broadcastCondVar(m_serviceCondVar); } + ARCH->unlockMutex(m_serviceMutex); } -void* -CArchDaemonWindows::runDaemonThread(RunFunc run) +UINT +CArchDaemonWindows::doGetDaemonQuitMessage() { - return reinterpret_cast(run()); -} - -void* -CArchDaemonWindows::runDaemonThreadEntry(void* vrun) -{ - assert(s_daemon != NULL); - - return s_daemon->runDaemonThread(reinterpret_cast(vrun)); + return m_quitMessage; } void @@ -583,6 +560,8 @@ CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint) void CArchDaemonWindows::setStatusError(DWORD error) { + assert(s_daemon != NULL); + SERVICE_STATUS status; status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; @@ -605,11 +584,10 @@ CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) const char** argv = const_cast(argvIn); // create synchronization objects - m_serviceMutex = ARCH->newMutex(); - m_serviceCondVar = ARCH->newCondVar(); - m_serviceState = SERVICE_RUNNING; - - // register our service handler functiom + m_serviceMutex = ARCH->newMutex(); + m_serviceCondVar = ARCH->newCondVar(); + + // register our service handler function m_statusHandle = RegisterServiceCtrlHandler(argv[0], &CArchDaemonWindows::serviceHandlerEntry); if (m_statusHandle == NULL) { @@ -621,7 +599,8 @@ CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) } // tell service control manager that we're starting - setStatus(SERVICE_START_PENDING, 0, 10000); + m_serviceState = SERVICE_START_PENDING; + setStatus(m_serviceState, 0, 10000); // if no arguments supplied then try getting them from the registry. // the first argument doesn't count because it's the service name. @@ -726,58 +705,40 @@ CArchDaemonWindows::serviceHandler(DWORD ctrl) ARCH->lockMutex(m_serviceMutex); // ignore request if service is already stopped - if (m_serviceState == SERVICE_STOPPED) { - setStatus(m_serviceState); + if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) { + if (s_daemon != NULL) { + setStatus(m_serviceState); + } ARCH->unlockMutex(m_serviceMutex); return; } switch (ctrl) { case SERVICE_CONTROL_PAUSE: - // update state m_serviceState = SERVICE_PAUSE_PENDING; setStatus(m_serviceState, 0, 5000); - - // stop run callback if running and wait for it to finish - if (m_serviceRunning) { - m_serviceHandlerWaiting = true; - ARCH->cancelThread(m_daemonThread); + PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0); + while (isRunState(m_serviceState)) { ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } - - // update state if service hasn't stopped while we were waiting - if (m_serviceState != SERVICE_STOPPED) { - m_serviceState = SERVICE_PAUSED; - } - ARCH->broadcastCondVar(m_serviceCondVar); break; case SERVICE_CONTROL_CONTINUE: - // required status update - setStatus(m_serviceState); - - // update state but let main loop send RUNNING notification - m_serviceState = SERVICE_RUNNING; + // FIXME -- maybe should flush quit messages from queue + m_serviceState = SERVICE_CONTINUE_PENDING; + setStatus(m_serviceState, 0, 5000); ARCH->broadcastCondVar(m_serviceCondVar); - ARCH->unlockMutex(m_serviceMutex); - return; + break; case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: - // update state m_serviceState = SERVICE_STOP_PENDING; setStatus(m_serviceState, 0, 5000); - - // stop run callback if running and wait for it to finish - if (m_serviceRunning) { - m_serviceHandlerWaiting = true; - ARCH->cancelThread(m_daemonThread); + PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0); + ARCH->broadcastCondVar(m_serviceCondVar); + while (isRunState(m_serviceState)) { ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } - - // update state - m_serviceState = SERVICE_STOPPED; - ARCH->broadcastCondVar(m_serviceCondVar); break; default: @@ -785,12 +746,10 @@ CArchDaemonWindows::serviceHandler(DWORD ctrl) // fall through case SERVICE_CONTROL_INTERROGATE: + setStatus(m_serviceState); break; } - // send update - setStatus(m_serviceState); - ARCH->unlockMutex(m_serviceMutex); } diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h index 57fe74fe..4fa31bc4 100644 --- a/lib/arch/CArchDaemonWindows.h +++ b/lib/arch/CArchDaemonWindows.h @@ -42,7 +42,7 @@ public: (i.e. after initialization) and \c daemonRunning(false) when it leaves the main loop. The \c runFunc is called in a new thread and when the daemon must exit the main loop due to some external control the - thread is cancelled on behalf of the client. This function returns + getDaemonQuitMessage() is posted to the thread. This function returns what \c runFunc returns. \c runFunc should call \c daemonFailed() if the daemon fails. */ @@ -63,6 +63,14 @@ public: */ static void daemonFailed(int result); + //! Get daemon quit message + /*! + The windows NT daemon tells daemon thread to exit by posting this + message to it. The thread must, of course, have a message queue + for this to work. + */ + static UINT getDaemonQuitMessage(); + // IArchDaemon overrides virtual void installDaemon(const char* name, const char* description, @@ -81,13 +89,13 @@ private: int doRunDaemon(RunFunc runFunc); void doDaemonRunning(bool running); + UINT doGetDaemonQuitMessage(); static void setStatus(DWORD state); static void setStatus(DWORD state, DWORD step, DWORD waitHint); static void setStatusError(DWORD error); - void* runDaemonThread(RunFunc); - static void* runDaemonThreadEntry(void*); + static bool isRunState(DWORD state); void serviceMain(DWORD, LPTSTR*); static void WINAPI serviceMainEntry(DWORD, LPTSTR*); @@ -113,11 +121,13 @@ private: bool m_serviceHandlerWaiting; bool m_serviceRunning; - CArchThread m_daemonThread; + DWORD m_daemonThreadID; DaemonFunc m_daemonFunc; int m_daemonResult; SERVICE_STATUS_HANDLE m_statusHandle; + + UINT m_quitMessage; }; #endif diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index 1fb3d7bd..d2b0842c 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -19,9 +19,12 @@ // CArchMiscWindows // +CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL; + void CArchMiscWindows::init() { + s_dialogs = new CDialogs; isWindows95Family(); } @@ -64,6 +67,12 @@ CArchMiscWindows::daemonFailed(int result) CArchDaemonWindows::daemonFailed(result); } +UINT +CArchMiscWindows::getDaemonQuitMessage() +{ + return CArchDaemonWindows::getDaemonQuitMessage(); +} + HKEY CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) { @@ -196,3 +205,27 @@ CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name) } return value; } + +void +CArchMiscWindows::addDialog(HWND hwnd) +{ + s_dialogs->insert(hwnd); +} + +void +CArchMiscWindows::removeDialog(HWND hwnd) +{ + s_dialogs->erase(hwnd); +} + +bool +CArchMiscWindows::processDialog(MSG* msg) +{ + for (CDialogs::const_iterator index = s_dialogs->begin(); + index != s_dialogs->end(); ++index) { + if (IsDialogMessage(*index, msg)) { + return true; + } + } + return false; +} diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index 766553ff..749e52f5 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -19,6 +19,7 @@ #include "common.h" #include "stdstring.h" +#include "stdset.h" #include //! Miscellaneous win32 functions. @@ -53,6 +54,12 @@ public: */ static void daemonFailed(int result); + //! Get daemon quit message + /*! + Delegates to CArchDaemonWindows. + */ + static UINT getDaemonQuitMessage(); + //! Open and return a registry key, closing the parent key static HKEY openKey(HKEY parent, const TCHAR* child); @@ -83,6 +90,24 @@ public: //! Read a DWORD value from the registry static DWORD readValueInt(HKEY, const TCHAR* name); + + //! Add a dialog + static void addDialog(HWND); + + //! Remove a dialog + static void removeDialog(HWND); + + //! Process dialog message + /*! + Checks if the message is destined for a dialog. If so the message + is passed to the dialog and returns true, otherwise returns false. + */ + static bool processDialog(MSG*); + +private: + typedef std::set CDialogs; + + static CDialogs* s_dialogs; }; #endif diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 1d5680d6..403cc82b 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -134,6 +134,18 @@ CArchMultithreadPosix::~CArchMultithreadPosix() s_instance = NULL; } +void +CArchMultithreadPosix::unblockThread(CArchThread thread) +{ + pthread_kill(thread->m_thread, SIGWAKEUP); +} + +CArchMultithreadPosix* +CArchMultithreadPosix::getInstance() +{ + return s_instance; +} + CArchCond CArchMultithreadPosix::newCondVar() { @@ -517,19 +529,6 @@ CArchMultithreadPosix::wait(CArchThread target, double timeout) } } -IArchMultithread::EWaitResult -CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/) -{ - // not implemented - return kTimeout; -} - -void -CArchMultithreadPosix::unblockThread(CArchThread thread) -{ - pthread_kill(thread->m_thread, SIGWAKEUP); -} - bool CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2) { @@ -575,7 +574,7 @@ CArchMultithreadPosix::interrupt() lockMutex(m_threadMutex); if (m_signalFunc != NULL) { m_signalFunc(m_signalUserData); - pthread_kill(m_mainThread->m_thread, SIGWAKEUP); + unblockThread(m_mainThread); } else { ARCH->cancelThread(m_mainThread); diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h index 4b87f0a0..3921c8e9 100644 --- a/lib/arch/CArchMultithreadPosix.h +++ b/lib/arch/CArchMultithreadPosix.h @@ -37,6 +37,19 @@ public: CArchMultithreadPosix(); virtual ~CArchMultithreadPosix(); + //! @name manipulators + //@{ + + void unblockThread(CArchThread thread); + + //@} + //! @name accessors + //@{ + + static CArchMultithreadPosix* getInstance(); + + //@} + // IArchMultithread overrides virtual CArchCond newCondVar(); virtual void closeCondVar(CArchCond); @@ -55,8 +68,6 @@ public: virtual void setPriorityOfThread(CArchThread, int n); virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); - virtual EWaitResult waitForEvent(CArchThread, double timeout); - virtual void unblockThread(CArchThread thread); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index f156021f..384722c4 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -50,6 +50,7 @@ public: bool m_cancelling; HANDLE m_exit; void* m_result; + void* m_networkData; }; CArchThreadImpl::CArchThreadImpl() : @@ -59,7 +60,8 @@ CArchThreadImpl::CArchThreadImpl() : m_func(NULL), m_userData(NULL), m_cancelling(false), - m_result(NULL) + m_result(NULL), + m_networkData(NULL) { m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -78,7 +80,9 @@ CArchThreadImpl::~CArchThreadImpl() CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL; -CArchMultithreadWindows::CArchMultithreadWindows() +CArchMultithreadWindows::CArchMultithreadWindows() : + m_signalFunc(NULL), + m_signalUserData(NULL) { assert(s_instance == NULL); s_instance = this; @@ -88,10 +92,10 @@ CArchMultithreadWindows::CArchMultithreadWindows() // create thread for calling (main) thread and add it to our // list. no need to lock the mutex since we're the only thread. - CArchThreadImpl* mainThread = new CArchThreadImpl; - mainThread->m_thread = NULL; - mainThread->m_id = GetCurrentThreadId(); - insert(mainThread); + m_mainThread = new CArchThreadImpl; + m_mainThread->m_thread = NULL; + m_mainThread->m_id = GetCurrentThreadId(); + insert(m_mainThread); } CArchMultithreadWindows::~CArchMultithreadWindows() @@ -108,6 +112,24 @@ CArchMultithreadWindows::~CArchMultithreadWindows() delete m_threadMutex; } +void +CArchMultithreadWindows::setNetworkDataForCurrentThread(void* data) +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(GetCurrentThreadId()); + thread->m_networkData = data; + unlockMutex(m_threadMutex); +} + +void* +CArchMultithreadWindows::getNetworkDataForThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* data = thread->m_networkData; + unlockMutex(m_threadMutex); + return data; +} + HANDLE CArchMultithreadWindows::getCancelEventForCurrentThread() { @@ -183,7 +205,7 @@ CArchMultithreadWindows::waitCondVar(CArchCond cond, // make a list of the condition variable events and the cancel event // for the current thread. - HANDLE handles[3]; + HANDLE handles[4]; handles[0] = cond->m_events[CArchCondImpl::kSignal]; handles[1] = cond->m_events[CArchCondImpl::kBroadcast]; handles[2] = getCancelEventForCurrentThread(); @@ -446,8 +468,8 @@ CArchMultithreadWindows::wait(CArchThread target, double timeout) t = (DWORD)(1000.0 * timeout); } - // wait for this thread to be cancelled or for the target thread to - // terminate. + // wait for this thread to be cancelled or woken up or for the + // target thread to terminate. HANDLE handles[2]; handles[0] = target->m_exit; handles[1] = self->m_cancel; @@ -478,137 +500,6 @@ CArchMultithreadWindows::wait(CArchThread target, double timeout) } } -IArchMultithread::EWaitResult -CArchMultithreadWindows::waitForEvent(CArchThread target, double timeout) -{ - // find current thread. ref the target so it can't go away while - // we're watching it. - lockMutex(m_threadMutex); - CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); - assert(self != NULL); - if (target != NULL) { - refThread(target); - } - unlockMutex(m_threadMutex); - - // see if we've been cancelled before checking if any events - // are pending. - DWORD result = WaitForSingleObject(self->m_cancel, 0); - if (result == WAIT_OBJECT_0) { - if (target != NULL) { - closeThread(target); - } - testCancelThreadImpl(self); - } - - // check if messages are available first. if we don't do this then - // MsgWaitForMultipleObjects() will block even if the queue isn't - // empty if the messages in the queue were there before the last - // call to GetMessage()/PeekMessage(). - if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { - return kEvent; - } - - // convert timeout - DWORD t; - if (timeout < 0.0) { - t = INFINITE; - } - else { - t = (DWORD)(1000.0 * timeout); - } - - // wait for this thread to be cancelled or for the target thread to - // terminate. - DWORD n = (target == NULL || target == self) ? 1 : 2; - HANDLE handles[2]; - handles[0] = self->m_cancel; - handles[1] = (n == 2) ? target->m_exit : NULL; - result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT); - - // cancel takes priority - if (result != WAIT_OBJECT_0 + 0 && - WaitForSingleObject(handles[0], 0) == WAIT_OBJECT_0) { - result = WAIT_OBJECT_0 + 0; - } - - // release target - if (target != NULL) { - closeThread(target); - } - - // handle result - switch (result) { - case WAIT_OBJECT_0 + 0: - // this thread was cancelled. does not return. - testCancelThreadImpl(self); - - case WAIT_OBJECT_0 + 1: - // target thread terminated - if (n == 2) { - return kExit; - } - // fall through - - case WAIT_OBJECT_0 + 2: - // message is available - return kEvent; - - default: - // timeout or error - return kTimeout; - } -} - -/* -bool -CArchMultithreadWindows::waitForEvent(double timeout) -{ - // check if messages are available first. if we don't do this then - // MsgWaitForMultipleObjects() will block even if the queue isn't - // empty if the messages in the queue were there before the last - // call to GetMessage()/PeekMessage(). - if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { - return true; - } - - // find current thread - lockMutex(m_threadMutex); - CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); - unlockMutex(m_threadMutex); - assert(self != NULL); - - // convert timeout - DWORD t; - if (timeout < 0.0) { - t = INFINITE; - } - else { - t = (DWORD)(1000.0 * timeout); - } - - // wait for this thread to be cancelled or for a message - HANDLE handles[1]; - handles[0] = self->m_cancel; - DWORD result = MsgWaitForMultipleObjects(1, handles, FALSE, t, QS_ALLINPUT); - - // handle result - switch (result) { - case WAIT_OBJECT_0 + 1: - // message is available - return true; - - case WAIT_OBJECT_0 + 0: - // this thread was cancelled. does not return. - testCancelThreadImpl(self); - - default: - // timeout or error - return false; - } -} -*/ - bool CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2) { @@ -637,6 +528,29 @@ CArchMultithreadWindows::getIDOfThread(CArchThread thread) return static_cast(thread->m_id); } +void +CArchMultithreadWindows::setInterruptHandler(InterruptFunc func, void* userData) +{ + lockMutex(m_threadMutex); + m_signalFunc = func; + m_signalUserData = userData; + unlockMutex(m_threadMutex); +} + +void +CArchMultithreadWindows::interrupt() +{ + lockMutex(m_threadMutex); + if (m_signalFunc != NULL) { + m_signalFunc(m_signalUserData); + ARCH->unblockPollSocket(m_mainThread); + } + else { + ARCH->cancelThread(m_mainThread); + } + unlockMutex(m_threadMutex); +} + CArchThreadImpl* CArchMultithreadWindows::find(DWORD id) { diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h index 2a8d5462..aecd4173 100644 --- a/lib/arch/CArchMultithreadWindows.h +++ b/lib/arch/CArchMultithreadWindows.h @@ -43,12 +43,20 @@ public: CArchMultithreadWindows(); virtual ~CArchMultithreadWindows(); + // + // manipulators + // + + void setNetworkDataForCurrentThread(void*); + // // accessors // HANDLE getCancelEventForCurrentThread(); + void* getNetworkDataForThread(CArchThread); + static CArchMultithreadWindows* getInstance(); // IArchMultithread overrides @@ -69,11 +77,12 @@ public: virtual void setPriorityOfThread(CArchThread, int n); virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); - virtual EWaitResult waitForEvent(CArchThread, double timeout); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread); + virtual void setInterruptHandler(InterruptFunc, void*); + virtual void interrupt(); private: CArchThreadImpl* find(DWORD id); @@ -96,6 +105,10 @@ private: CArchMutex m_threadMutex; CThreadList m_threadList; + CArchThread m_mainThread; + + InterruptFunc m_signalFunc; + void* m_signalUserData; }; #endif diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 29f71586..487a2eb6 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -424,6 +424,12 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) #endif +void +CArchNetworkBSD::unblockPollSocket(CArchThread thread) +{ + CArchMultithreadPosix::getInstance()->unblockThread(thread); +} + size_t CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) { @@ -435,8 +441,8 @@ CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) if (n == -1) { if (errno == EINTR) { // interrupted system call - ARCH->testCancelThread(); - continue; + n = 0; + break; } else if (errno == EAGAIN) { n = 0; @@ -460,8 +466,8 @@ CArchNetworkBSD::writeSocket(CArchSocket s, const void* buf, size_t len) if (n == -1) { if (errno == EINTR) { // interrupted system call - ARCH->testCancelThread(); - continue; + n = 0; + break; } else if (errno == EAGAIN) { // no buffer space diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index 18f942b8..b4ed7097 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -60,6 +60,7 @@ public: virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); virtual void connectSocket(CArchSocket s, CArchNetAddress name); virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual void unblockPollSocket(CArchThread thread); virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len); diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index d7b58e87..547546bb 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -15,7 +15,9 @@ #include "CArchNetworkWinsock.h" #include "CArch.h" +#include "IArchMultithread.h" #include "XArchWindows.h" +#include static const int s_family[] = { PF_UNSPEC, @@ -49,6 +51,13 @@ static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); static int (PASCAL FAR *WSACleanup_winsock)(void); static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR * fdset); +static WSAEVENT (PASCAL FAR *WSACreateEvent_winsock)(void); +static BOOL (PASCAL FAR *WSACloseEvent_winsock)(WSAEVENT); +static BOOL (PASCAL FAR *WSASetEvent_winsock)(WSAEVENT); +static BOOL (PASCAL FAR *WSAResetEvent_winsock)(WSAEVENT); +static int (PASCAL FAR *WSAEventSelect_winsock)(SOCKET, WSAEVENT, long); +static DWORD (PASCAL FAR *WSAWaitForMultipleEvents_winsock)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL); +static int (PASCAL FAR *WSAEnumNetworkEvents_winsock)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS); #undef FD_ISSET #define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) @@ -68,13 +77,23 @@ netGetProcAddress(HMODULE module, LPCSTR name) return func; } +CArchNetAddressImpl* +CArchNetAddressImpl::alloc(size_t size) +{ + size_t totalSize = size + ADDR_HDR_SIZE; + CArchNetAddressImpl* addr = (CArchNetAddressImpl*)malloc(totalSize); + addr->m_len = size; + return addr; +} + + // // CArchNetworkWinsock // CArchNetworkWinsock::CArchNetworkWinsock() { - static const char* s_library[] = { "ws2_32.dll", "wsock32.dll" }; + static const char* s_library[] = { "ws2_32.dll" }; assert(WSACleanup_winsock == NULL); assert(s_networkModule == NULL); @@ -110,14 +129,16 @@ CArchNetworkWinsock::~CArchNetworkWinsock() void CArchNetworkWinsock::init(HMODULE module) { - assert(module != NULL); + if (module == NULL) { + throw XArchNetworkSupport(""); + } // get startup function address int (PASCAL FAR *startup)(WORD, LPWSADATA); setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA)); // startup network library - WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); + WORD version = MAKEWORD(2 /*major*/, 0 /*minor*/); WSADATA data; int err = startup(version, &data); if (data.wVersion != version) { @@ -152,6 +173,13 @@ CArchNetworkWinsock::init(HMODULE module) setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void)); setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *)); + setfunc(WSACreateEvent_winsock, WSACreateEvent, WSAEVENT (PASCAL FAR *)(void)); + setfunc(WSACloseEvent_winsock, WSACloseEvent, BOOL (PASCAL FAR *)(WSAEVENT)); + setfunc(WSASetEvent_winsock, WSASetEvent, BOOL (PASCAL FAR *)(WSAEVENT)); + setfunc(WSAResetEvent_winsock, WSAResetEvent, BOOL (PASCAL FAR *)(WSAEVENT)); + setfunc(WSAEventSelect_winsock, WSAEventSelect, int (PASCAL FAR *)(SOCKET, WSAEVENT, long)); + setfunc(WSAWaitForMultipleEvents_winsock, WSAWaitForMultipleEvents, DWORD (PASCAL FAR *)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL)); + setfunc(WSAEnumNetworkEvents_winsock, WSAEnumNetworkEvents, int (PASCAL FAR *)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS)); s_networkModule = module; } @@ -171,6 +199,8 @@ CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type) socket->m_socket = fd; socket->m_connected = false; socket->m_refCount = 1; + socket->m_event = WSACreateEvent_winsock(); + socket->m_pollWrite = false; return socket; } @@ -202,7 +232,7 @@ CArchNetworkWinsock::closeSocket(CArchSocket s) if (close_winsock(s->m_socket) == SOCKET_ERROR) { // close failed int err = getsockerror_winsock(); - if (err == EINTR) { + if (err == WSAEINTR) { // interrupted system call ARCH->testCancelThread(); continue; @@ -215,6 +245,7 @@ CArchNetworkWinsock::closeSocket(CArchSocket s) throwError(err); } } while (false); + WSACloseEvent_winsock(s->m_event); delete s; } } @@ -270,35 +301,24 @@ CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) { assert(s != NULL); - // if user passed NULL in addr then use scratch space - CArchNetAddress dummy; - if (addr == NULL) { - addr = &dummy; - } - - // create new socket and address + // create new socket and temporary address CArchSocketImpl* socket = new CArchSocketImpl; - *addr = new CArchNetAddressImpl; + CArchNetAddress tmp = CArchNetAddressImpl::alloc(sizeof(struct sockaddr)); // accept on socket SOCKET fd; do { - fd = accept_winsock(s->m_socket, &(*addr)->m_addr, &(*addr)->m_len); + fd = accept_winsock(s->m_socket, &tmp->m_addr, &tmp->m_len); if (fd == INVALID_SOCKET) { int err = getsockerror_winsock(); - if (err == EINTR) { + delete socket; + free(tmp); + *addr = NULL; + if (err == WSAEINTR) { // interrupted system call ARCH->testCancelThread(); - continue; + return NULL; } - if (err == WSAECONNABORTED) { - // connection was aborted; try again - ARCH->testCancelThread(); - continue; - } - delete socket; - delete *addr; - *addr = NULL; throwError(err); } } while (false); @@ -307,12 +327,15 @@ CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) socket->m_socket = fd; socket->m_connected = true; socket->m_refCount = 1; + socket->m_event = WSACreateEvent_winsock(); + socket->m_pollWrite = true; - // discard address if not requested - if (addr == &dummy) { - ARCH->closeAddr(dummy); + // copy address if requested + if (addr != NULL) { + *addr = ARCH->copyAddr(tmp); } + free(tmp); return socket; } @@ -325,7 +348,7 @@ CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr) do { if (connect_winsock(s->m_socket, &addr->m_addr, addr->m_len) == SOCKET_ERROR) { - if (getsockerror_winsock() == EINTR) { + if (getsockerror_winsock() == WSAEINTR) { // interrupted system call ARCH->testCancelThread(); continue; @@ -354,95 +377,162 @@ CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr) int CArchNetworkWinsock::pollSocket(CPollEntry pe[], int num, double timeout) { - int i, n; + int i; + DWORD n; - do { - // prepare sets for select - n = 0; - fd_set readSet, writeSet, errSet; - fd_set* readSetP = NULL; - fd_set* writeSetP = NULL; - fd_set* errSetP = NULL; - FD_ZERO(&readSet); - FD_ZERO(&writeSet); - FD_ZERO(&errSet); - for (i = 0; i < num; ++i) { - // reset return flags - pe[i].m_revents = 0; + // prepare sockets and wait list + bool canWrite = false; + WSAEVENT* events = (WSAEVENT*)alloca((num + 1) * sizeof(WSAEVENT)); + for (i = 0, n = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; - // set invalid flag if socket is bogus then go to next socket - if (pe[i].m_socket == NULL) { - pe[i].m_revents |= kPOLLNVAL; - continue; - } + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } - if (pe[i].m_events & kPOLLIN) { - FD_SET(pe[i].m_socket->m_socket, &readSet); - readSetP = &readSet; - n = 1; - } - if (pe[i].m_events & kPOLLOUT) { - FD_SET(pe[i].m_socket->m_socket, &writeSet); - writeSetP = &writeSet; - n = 1; - } - if (true) { - FD_SET(pe[i].m_socket->m_socket, &errSet); - errSetP = &errSet; - n = 1; + // select desired events + long socketEvents = 0; + if ((pe[i].m_events & kPOLLIN) != 0) { + socketEvents |= FD_READ | FD_ACCEPT | FD_CLOSE; + } + if ((pe[i].m_events & kPOLLOUT) != 0) { + socketEvents |= FD_WRITE | FD_CONNECT | FD_CLOSE; + + // if m_pollWrite is false then we assume the socket is + // writable. winsock doesn't signal writability except + // when the state changes from unwritable. + if (!pe[i].m_socket->m_pollWrite) { + canWrite = true; + pe[i].m_revents |= kPOLLOUT; } } - // if there are no sockets then don't block forever - if (n == 0 && timeout < 0.0) { - timeout = 0.0; + // if no events then ignore socket + if (socketEvents == 0) { + continue; } - // prepare timeout for select - struct timeval timeout2; - struct timeval* timeout2P; - if (timeout < 0) { - timeout2P = NULL; + // select socket for desired events + WSAEventSelect_winsock(pe[i].m_socket->m_socket, + pe[i].m_socket->m_event, socketEvents); + + // add socket event to wait list + events[n++] = pe[i].m_socket->m_event; + } + + // if no sockets then return immediately + if (n == 0) { + return 0; + } + + // add the unblock event + CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance(); + CArchThread thread = mt->newCurrentThread(); + WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread); + if (unblockEvent == NULL) { + unblockEvent = new WSAEVENT; + *unblockEvent = WSACreateEvent_winsock(); + mt->setNetworkDataForCurrentThread(unblockEvent); + } + events[n++] = *unblockEvent; + + // prepare timeout + DWORD t = (timeout < 0.0) ? INFINITE : (DWORD)(1000.0 * timeout); + if (canWrite) { + // if we know we can write then don't block + t = 0; + } + + // wait + DWORD result = WSAWaitForMultipleEvents_winsock(n, events, FALSE, t, FALSE); + + // reset the unblock event + WSAResetEvent_winsock(*unblockEvent); + + // handle results + if (result == WSA_WAIT_FAILED) { + if (getsockerror_winsock() == WSAEINTR) { + // interrupted system call + ARCH->testCancelThread(); + return 0; } - else { - timeout2P = &timeout2; - timeout2.tv_sec = static_cast(timeout); - timeout2.tv_usec = static_cast(1.0e+6 * - (timeout - timeout2.tv_sec)); + throwError(getsockerror_winsock()); + } + if (result == WSA_WAIT_TIMEOUT && !canWrite) { + return 0; + } + if (result == WSA_WAIT_EVENT_0 + n - 1) { + // the unblock event was signalled + return 0; + } + for (i = 0, n = 0; i < num; ++i) { + // skip events we didn't check + if (pe[i].m_socket == NULL || + (pe[i].m_events & (kPOLLIN | kPOLLOUT)) == 0) { + continue; } - // do the select - n = select_winsock(0, readSetP, writeSetP, errSetP, timeout2P); + // get events + WSANETWORKEVENTS info; + if (WSAEnumNetworkEvents_winsock(pe[i].m_socket->m_socket, + pe[i].m_socket->m_event, &info) == SOCKET_ERROR) { + continue; + } + if ((info.lNetworkEvents & FD_READ) != 0) { + pe[i].m_revents |= kPOLLIN; + } + if ((info.lNetworkEvents & FD_ACCEPT) != 0) { + pe[i].m_revents |= kPOLLIN; + } + if ((info.lNetworkEvents & FD_WRITE) != 0) { + pe[i].m_revents |= kPOLLOUT; - // handle results - if (n == SOCKET_ERROR) { - if (getsockerror_winsock() == EINTR) { - // interrupted system call - ARCH->testCancelThread(); - continue; + // socket is now writable so don't bothing polling for + // writable until it becomes unwritable. + pe[i].m_socket->m_pollWrite = false; + } + if ((info.lNetworkEvents & FD_CONNECT) != 0) { + if (info.iErrorCode[FD_CONNECT_BIT] != 0) { + pe[i].m_revents |= kPOLLERR; + } + else { + pe[i].m_revents |= kPOLLOUT; + pe[i].m_socket->m_pollWrite = false; } - throwError(getsockerror_winsock()); } - n = 0; - for (i = 0; i < num; ++i) { - if (pe[i].m_socket != NULL) { - if (FD_ISSET(pe[i].m_socket->m_socket, &readSet)) { + if ((info.lNetworkEvents & FD_CLOSE) != 0) { + if (info.iErrorCode[FD_CLOSE_BIT] != 0) { + pe[i].m_revents |= kPOLLERR; + } + else { + if ((pe[i].m_events & kPOLLIN) != 0) { pe[i].m_revents |= kPOLLIN; } - if (FD_ISSET(pe[i].m_socket->m_socket, &writeSet)) { + if ((pe[i].m_events & kPOLLOUT) != 0) { pe[i].m_revents |= kPOLLOUT; } - if (FD_ISSET(pe[i].m_socket->m_socket, &errSet)) { - pe[i].m_revents |= kPOLLERR; - } - } - if (pe[i].m_revents != 0) { - ++n; } } - } while (false); + if (pe[i].m_revents != 0) { + ++n; + } + } - return n; + return (int)n; +} + +void +CArchNetworkWinsock::unblockPollSocket(CArchThread thread) +{ + // set the unblock event + CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance(); + WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread); + if (unblockEvent != NULL) { + WSASetEvent_winsock(*unblockEvent); + } } size_t @@ -454,10 +544,14 @@ CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len) do { n = recv_winsock(s->m_socket, buf, len, 0); if (n == SOCKET_ERROR) { - if (getsockerror_winsock() == EINTR) { + if (getsockerror_winsock() == WSAEINTR) { // interrupted system call - ARCH->testCancelThread(); - continue; + n = 0; + break; + } + else if (getsockerror_winsock() == WSAEWOULDBLOCK) { + n = 0; + break; } throwError(getsockerror_winsock()); } @@ -475,10 +569,15 @@ CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len) do { n = send_winsock(s->m_socket, buf, len, 0); if (n == SOCKET_ERROR) { - if (getsockerror_winsock() == EINTR) { + if (getsockerror_winsock() == WSAEINTR) { // interrupted system call - ARCH->testCancelThread(); - continue; + n = 0; + break; + } + else if (getsockerror_winsock() == WSAEWOULDBLOCK) { + s->m_pollWrite = true; + n = 0; + break; } throwError(getsockerror_winsock()); } @@ -559,26 +658,20 @@ CArchNetworkWinsock::getHostName() CArchNetAddress CArchNetworkWinsock::newAnyAddr(EAddressFamily family) { - // allocate address - CArchNetAddressImpl* addr = new CArchNetAddressImpl; - - // fill it in + CArchNetAddressImpl* addr = NULL; switch (family) { case kINET: { - struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in)); + struct sockaddr_in* ipAddr = TYPED_ADDR(struct sockaddr_in, addr); ipAddr->sin_family = AF_INET; ipAddr->sin_port = 0; ipAddr->sin_addr.s_addr = INADDR_ANY; - addr->m_len = sizeof(struct sockaddr_in); break; } default: - delete addr; assert(0 && "invalid family"); } - return addr; } @@ -587,26 +680,27 @@ CArchNetworkWinsock::copyAddr(CArchNetAddress addr) { assert(addr != NULL); - // allocate and copy address - return new CArchNetAddressImpl(*addr); + CArchNetAddressImpl* copy = CArchNetAddressImpl::alloc(addr->m_len); + memcpy(TYPED_ADDR(void, copy), TYPED_ADDR(void, addr), addr->m_len); + return copy; } CArchNetAddress CArchNetworkWinsock::nameToAddr(const std::string& name) { // allocate address - CArchNetAddressImpl* addr = new CArchNetAddressImpl; + CArchNetAddressImpl* addr = NULL; // try to convert assuming an IPv4 dot notation address struct sockaddr_in inaddr; memset(&inaddr, 0, sizeof(inaddr)); + inaddr.sin_family = AF_INET; + inaddr.sin_port = 0; inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str()); if (inaddr.sin_addr.s_addr != INADDR_NONE) { // it's a dot notation address - addr->m_len = sizeof(struct sockaddr_in); - inaddr.sin_family = AF_INET; - inaddr.sin_port = 0; - memcpy(&addr->m_addr, &inaddr, addr->m_len); + addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in)); + memcpy(TYPED_ADDR(void, addr), &inaddr, addr->m_len); } else { @@ -616,16 +710,8 @@ CArchNetworkWinsock::nameToAddr(const std::string& name) delete addr; throwNameError(getsockerror_winsock()); } - - // copy over address (only IPv4 currently supported) - if (info->h_addrtype == AF_INET) { - addr->m_len = sizeof(struct sockaddr_in); - inaddr.sin_family = info->h_addrtype; - inaddr.sin_port = 0; - memcpy(&inaddr.sin_addr, info->h_addr_list[0], - sizeof(inaddr.sin_addr)); - memcpy(&addr->m_addr, &inaddr, addr->m_len); - } + addr = CArchNetAddressImpl::alloc(info->h_length); + memcpy(TYPED_ADDR(void, addr), info->h_addr_list[0], info->h_length); } return addr; @@ -636,7 +722,7 @@ CArchNetworkWinsock::closeAddr(CArchNetAddress addr) { assert(addr != NULL); - delete addr; + free(addr); } std::string @@ -734,8 +820,8 @@ CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr) case kINET: { struct sockaddr_in* ipAddr = reinterpret_cast(&addr->m_addr); - return (ipAddr->sin_addr.s_addr == INADDR_ANY && - addr->m_len == sizeof(struct sockaddr_in)); + return (addr->m_len == sizeof(struct sockaddr_in) && + ipAddr->sin_addr.s_addr == INADDR_ANY); } default: @@ -744,6 +830,13 @@ CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr) } } +bool +CArchNetworkWinsock::isEqualAddr(CArchNetAddress a, CArchNetAddress b) +{ + return (a == b || (a->m_len == b->m_len && + memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0)); +} + void CArchNetworkWinsock::throwError(int err) { @@ -786,8 +879,10 @@ CArchNetworkWinsock::throwError(int err) case WSAENOTCONN: throw XArchNetworkNotConnected(new XArchEvalWinsock(err)); - case WSAENETRESET: case WSAEDISCON: + throw XArchNetworkShutdown(new XArchEvalWinsock(err)); + + case WSAENETRESET: case WSAECONNABORTED: case WSAECONNRESET: throw XArchNetworkDisconnected(new XArchEvalWinsock(err)); diff --git a/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h index 58ce9cad..fd11dba9 100644 --- a/lib/arch/CArchNetworkWinsock.h +++ b/lib/arch/CArchNetworkWinsock.h @@ -33,16 +33,20 @@ public: SOCKET m_socket; bool m_connected; int m_refCount; + WSAEVENT m_event; + bool m_pollWrite; }; class CArchNetAddressImpl { public: - CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + static CArchNetAddressImpl* alloc(size_t); public: - struct sockaddr m_addr; int m_len; + struct sockaddr m_addr; }; +#define ADDR_HDR_SIZE offsetof(CArchNetAddressImpl, m_addr) +#define TYPED_ADDR(type_, addr_) (reinterpret_cast(&addr_->m_addr)) //! Win32 implementation of IArchNetwork class CArchNetworkWinsock : public IArchNetwork { @@ -61,6 +65,7 @@ public: virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); virtual void connectSocket(CArchSocket s, CArchNetAddress name); virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual void unblockPollSocket(CArchThread thread); virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len); @@ -78,6 +83,7 @@ public: virtual void setAddrPort(CArchNetAddress, int port); virtual int getAddrPort(CArchNetAddress); virtual bool isAnyAddr(CArchNetAddress); + virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress); private: void init(HMODULE); diff --git a/lib/arch/CArchTaskBarWindows.cpp b/lib/arch/CArchTaskBarWindows.cpp index 987ac6c0..fccba001 100644 --- a/lib/arch/CArchTaskBarWindows.cpp +++ b/lib/arch/CArchTaskBarWindows.cpp @@ -41,71 +41,67 @@ CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) : // save app instance s_appInstance = reinterpret_cast(appInstance); - // we need a mutex - m_mutex = ARCH->newMutex(); + // register the task bar restart message + m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); - // and a condition variable which uses the above mutex - m_ready = false; - m_condVar = ARCH->newCondVar(); + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_NOCLOSE; + classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); + classInfo.hInstance = s_appInstance; + classInfo.hIcon = NULL; + classInfo.hCursor = NULL; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = TEXT("SynergyTaskBar"); + classInfo.hIconSm = NULL; + m_windowClass = RegisterClassEx(&classInfo); - // we're going to want to get a result from the thread we're - // about to create to know if it initialized successfully. - // so we lock the condition variable. - ARCH->lockMutex(m_mutex); - - // open a window and run an event loop in a separate thread. - // this has to happen in a separate thread because if we - // create a window on the current desktop with the current - // thread then the current thread won't be able to switch - // desktops if it needs to. - m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this); - - // wait for child thread - while (!m_ready) { - ARCH->waitCondVar(m_condVar, m_mutex, -1.0); - } - - // ready - ARCH->unlockMutex(m_mutex); + // create window + m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, + reinterpret_cast(m_windowClass), + TEXT("Synergy Task Bar"), + WS_POPUP, + 0, 0, 1, 1, + NULL, + NULL, + s_appInstance, + reinterpret_cast(this)); } CArchTaskBarWindows::~CArchTaskBarWindows() { - if (m_thread != NULL) { - ARCH->cancelThread(m_thread); - ARCH->wait(m_thread, -1.0); - ARCH->closeThread(m_thread); + if (m_hwnd != NULL) { + removeAllIcons(); + DestroyWindow(m_hwnd); } - ARCH->closeCondVar(m_condVar); - ARCH->closeMutex(m_mutex); + UnregisterClass((LPCTSTR)m_windowClass, s_appInstance); + s_instance = NULL; } void CArchTaskBarWindows::addDialog(HWND hwnd) { - // add dialog to added dialogs list - ARCH->lockMutex(s_instance->m_mutex); - s_instance->m_addedDialogs.insert(std::make_pair(hwnd, true)); - ARCH->unlockMutex(s_instance->m_mutex); + CArchMiscWindows::addDialog(hwnd); } void CArchTaskBarWindows::removeDialog(HWND hwnd) { - // mark dialog as removed - ARCH->lockMutex(s_instance->m_mutex); - CDialogs::iterator index = s_instance->m_dialogs.find(hwnd); - if (index != s_instance->m_dialogs.end()) { - index->second = false; - } - s_instance->m_addedDialogs.erase(hwnd); - ARCH->unlockMutex(s_instance->m_mutex); + CArchMiscWindows::removeDialog(hwnd); } void CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver) { + if (m_hwnd == NULL) { + return; + } + // ignore bogus receiver if (receiver == NULL) { return; @@ -180,53 +176,43 @@ CArchTaskBarWindows::recycleID(UINT id) void CArchTaskBarWindows::addIcon(UINT id) { - ARCH->lockMutex(m_mutex); CIDToReceiverMap::const_iterator index = m_idTable.find(id); if (index != m_idTable.end()) { modifyIconNoLock(index->second, NIM_ADD); } - ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::removeIcon(UINT id) { - ARCH->lockMutex(m_mutex); removeIconNoLock(id); - ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::updateIcon(UINT id) { - ARCH->lockMutex(m_mutex); CIDToReceiverMap::const_iterator index = m_idTable.find(id); if (index != m_idTable.end()) { modifyIconNoLock(index->second, NIM_MODIFY); } - ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::addAllIcons() { - ARCH->lockMutex(m_mutex); for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); index != m_receivers.end(); ++index) { modifyIconNoLock(index, NIM_ADD); } - ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::removeAllIcons() { - ARCH->lockMutex(m_mutex); for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); index != m_receivers.end(); ++index) { removeIconNoLock(index->second.m_id); } - ARCH->unlockMutex(m_mutex); } void @@ -319,49 +305,6 @@ CArchTaskBarWindows::handleIconMessage( } } -bool -CArchTaskBarWindows::processDialogs(MSG* msg) -{ - // only one thread can be in this method on any particular object - // at any given time. that's not a problem since only our event - // loop calls this method and there's just one of those. - - ARCH->lockMutex(m_mutex); - - // remove removed dialogs - m_dialogs.erase(false); - - // merge added dialogs into the dialog list - for (CDialogs::const_iterator index = m_addedDialogs.begin(); - index != m_addedDialogs.end(); ++index) { - m_dialogs.insert(std::make_pair(index->first, index->second)); - } - m_addedDialogs.clear(); - - ARCH->unlockMutex(m_mutex); - - // check message against all dialogs until one handles it. - // note that we don't hold a lock while checking because - // the message is processed and may make calls to this - // object. that's okay because addDialog() and - // removeDialog() don't change the map itself (just the - // values of some elements). - ARCH->lockMutex(m_mutex); - for (CDialogs::const_iterator index = m_dialogs.begin(); - index != m_dialogs.end(); ++index) { - if (index->second) { - ARCH->unlockMutex(m_mutex); - if (IsDialogMessage(index->first, msg)) { - return true; - } - ARCH->lockMutex(m_mutex); - } - } - ARCH->unlockMutex(m_mutex); - - return false; -} - LRESULT CArchTaskBarWindows::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -432,87 +375,3 @@ CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg, return DefWindowProc(hwnd, msg, wParam, lParam); } } - -void -CArchTaskBarWindows::threadMainLoop() -{ - // register the task bar restart message - m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); - - // register a window class - WNDCLASSEX classInfo; - classInfo.cbSize = sizeof(classInfo); - classInfo.style = CS_NOCLOSE; - classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; - classInfo.cbClsExtra = 0; - classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); - classInfo.hInstance = s_appInstance; - classInfo.hIcon = NULL; - classInfo.hCursor = NULL; - classInfo.hbrBackground = NULL; - classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = TEXT("SynergyTaskBar"); - classInfo.hIconSm = NULL; - ATOM windowClass = RegisterClassEx(&classInfo); - - // create window - m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, - reinterpret_cast(windowClass), - TEXT("Synergy Task Bar"), - WS_POPUP, - 0, 0, 1, 1, - NULL, - NULL, - s_appInstance, - reinterpret_cast(this)); - - // signal ready - ARCH->lockMutex(m_mutex); - m_ready = true; - ARCH->broadcastCondVar(m_condVar); - ARCH->unlockMutex(m_mutex); - - // handle failure - if (m_hwnd == NULL) { - UnregisterClass((LPCTSTR)windowClass, s_appInstance); - return; - } - - try { - // main loop - MSG msg; - for (;;) { - // wait for message - if (ARCH->waitForEvent(NULL, -1.0) != IArchMultithread::kEvent) { - continue; - } - - // peek for message and remove it. we don't GetMessage() - // because we should never block here, only in waitForEvent(). - if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - continue; - } - - // check message against dialogs - if (!processDialogs(&msg)) { - // process message - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - } - catch (XThread&) { - // clean up - removeAllIcons(); - DestroyWindow(m_hwnd); - UnregisterClass((LPCTSTR)windowClass, s_appInstance); - throw; - } -} - -void* -CArchTaskBarWindows::threadEntry(void* self) -{ - reinterpret_cast(self)->threadMainLoop(); - return NULL; -} diff --git a/lib/arch/CArchTaskBarWindows.h b/lib/arch/CArchTaskBarWindows.h index 67e9af17..5877637c 100644 --- a/lib/arch/CArchTaskBarWindows.h +++ b/lib/arch/CArchTaskBarWindows.h @@ -18,7 +18,6 @@ #define WIN32_LEAN_AND_MEAN #include "IArchTaskBar.h" -#include "IArchMultithread.h" #include "stdmap.h" #include "stdvector.h" #include @@ -78,21 +77,13 @@ private: LRESULT wndProc(HWND, UINT, WPARAM, LPARAM); static LRESULT CALLBACK staticWndProc(HWND, UINT, WPARAM, LPARAM); - void threadMainLoop(); - static void* threadEntry(void*); private: static CArchTaskBarWindows* s_instance; static HINSTANCE s_appInstance; - // multithread data - CArchMutex m_mutex; - CArchCond m_condVar; - bool m_ready; - int m_result; - CArchThread m_thread; - // child thread data + ATOM m_windowClass; HWND m_hwnd; UINT m_taskBarRestart; diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index c06d4cfa..477f42a7 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -67,13 +67,6 @@ synergy. Each architecture must implement this interface. */ class IArchMultithread : public IInterface { public: - //! Result of waitForEvent() - enum EWaitResult { - kEvent, //!< An event is pending - kExit, //!< Thread exited - kTimeout //!< Wait timed out - }; - //! Type of thread entry point typedef void* (*ThreadFunc)(void*); //! Type of thread identifier @@ -213,30 +206,6 @@ public: */ virtual bool wait(CArchThread thread, double timeout) = 0; - //! Wait for a user event - /*! - Waits for up to \c timeout seconds for a pending user event or - \c thread to exit (normally or by cancellation). Waits forever - if \c timeout < 0. Returns kEvent if an event occurred, kExit - if \c thread exited, or kTimeout if the timeout expired. If - \c thread is NULL then it doesn't wait for any thread to exit - and it will not return kExit. - - This method is not required by all platforms. - - (Cancellation point) - */ - virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0; - - //! Unblock thread in system call - /*! - Cause a thread that's in a blocking system call to return. This - call may return before the thread is unblocked. If the thread is - not in a blocking system call, this call has no effect. This does - not cause a lockMutex() or waitCondVar() to return prematurely. - */ - virtual void unblockThread(CArchThread thread) = 0; - //! Compare threads /*! Returns true iff two thread objects refer to the same thread. diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h index 21758800..245748ec 100644 --- a/lib/arch/IArchNetwork.h +++ b/lib/arch/IArchNetwork.h @@ -18,6 +18,9 @@ #include "IInterface.h" #include "stdstring.h" +class CArchThreadImpl; +typedef CArchThreadImpl* CArchThread; + /*! \class CArchSocketImpl \brief Internal socket data. @@ -179,12 +182,21 @@ public: the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL are set in \c m_revents as appropriate. If a socket indicates \c kPOLLERR then \c throwErrorOnSocket() can be used to determine - the type of error. + the type of error. Returns 0 immediately regardless of the \c timeout + if no valid sockets are selected for testing. (Cancellation point) */ virtual int pollSocket(CPollEntry[], int num, double timeout) = 0; + //! Unblock thread in pollSocket() + /*! + Cause a thread that's in a pollSocket() call to return. This + call may return before the thread is unblocked. If the thread is + not in a pollSocket() call this call has no effect. + */ + virtual void unblockPollSocket(CArchThread thread) = 0; + //! Read data from socket /*! Read up to \c len bytes from socket \c s in \c buf and return the diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp index 126a11e3..72fcc909 100644 --- a/lib/arch/arch.dsp +++ b/lib/arch/arch.dsp @@ -119,10 +119,6 @@ SOURCE=.\CArchFileWindows.h # End Source File # Begin Source File -SOURCE=.\CArchImpl.h -# End Source File -# Begin Source File - SOURCE=.\CArchLogWindows.h # End Source File # Begin Source File diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp index 879a97ce..d3ff27bc 100644 --- a/lib/base/CEventQueue.cpp +++ b/lib/base/CEventQueue.cpp @@ -169,6 +169,10 @@ retry: event = removeEvent(dataID); return true; } + + default: + assert(0 && "invalid event type"); + return false; } } @@ -498,7 +502,7 @@ CEventQueue::CTimer::reset() m_time = m_timeout; } -CEventQueue::CTimer::CTimer& +CEventQueue::CTimer& CEventQueue::CTimer::operator-=(double dt) { m_time -= dt; diff --git a/lib/base/CPriorityQueue.h b/lib/base/CPriorityQueue.h index c79c7e34..8a7fd7e7 100644 --- a/lib/base/CPriorityQueue.h +++ b/lib/base/CPriorityQueue.h @@ -26,7 +26,11 @@ it sorts by std::greater, it has a forward iterator through the elements (which can appear in any order), and its contents can be swapped. */ template , +#if WINDOWS_LIKE + class Compare = std::greater > +#else class Compare = std::greater > +#endif class CPriorityQueue { public: typedef typename Container::value_type value_type; diff --git a/lib/base/CSimpleEventQueueBuffer.cpp b/lib/base/CSimpleEventQueueBuffer.cpp index f01d2722..8f2dbd14 100644 --- a/lib/base/CSimpleEventQueueBuffer.cpp +++ b/lib/base/CSimpleEventQueueBuffer.cpp @@ -53,7 +53,7 @@ CSimpleEventQueueBuffer::waitForEvent(double timeout) } IEventQueueBuffer::Type -CSimpleEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) +CSimpleEventQueueBuffer::getEvent(CEvent&, UInt32& dataID) { CArchMutexLock lock(m_queueMutex); if (!m_queueReady) { diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 2935e195..88ae2c9d 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -39,7 +39,8 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : m_seqNum(0), m_compressMouse(false), m_ignoreMouse(false), - m_heartRate(0.0) + m_heartRate(0.0), + m_parser(&CServerProxy::parseHandshakeMessage) { assert(m_client != NULL); assert(m_stream != NULL); @@ -52,7 +53,7 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(), m_stream->getEventTarget(), new TMethodEventJob(this, - &CServerProxy::handleMessage)); + &CServerProxy::handleData)); // send heartbeat installHeartBeat(kHeartRate); @@ -61,6 +62,8 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : CServerProxy::~CServerProxy() { installHeartBeat(-1.0); + EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(), + m_stream->getEventTarget()); } CEvent::Type @@ -87,136 +90,193 @@ CServerProxy::installHeartBeat(double heartRate) } void -CServerProxy::handleMessage(const CEvent&, void*) +CServerProxy::handleData(const CEvent&, void*) { - while (m_stream->isReady()) { - // read next code - UInt8 code[4]; - UInt32 n = m_stream->read(code, sizeof(code)); - if (n == 0) { - break; - } + // handle messages until there are no more. first read message code. + UInt8 code[4]; + UInt32 n = m_stream->read(code, 4); + while (n != 0) { + // verify we got an entire code if (n != 4) { - // client sent an incomplete message - LOG((CLOG_ERR "incomplete message from server")); + LOG((CLOG_ERR "incomplete message from server: %d bytes", n)); m_client->disconnect("incomplete message from server"); return; } // parse message LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); - if (memcmp(code, kMsgDMouseMove, 4) == 0) { - mouseMove(); - } - - else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { - mouseWheel(); - } - - else if (memcmp(code, kMsgDKeyDown, 4) == 0) { - keyDown(); - } - - else if (memcmp(code, kMsgDKeyUp, 4) == 0) { - keyUp(); - } - - else if (memcmp(code, kMsgDMouseDown, 4) == 0) { - mouseDown(); - } - - else if (memcmp(code, kMsgDMouseUp, 4) == 0) { - mouseUp(); - } - - else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { - keyRepeat(); - } - - else if (memcmp(code, kMsgCNoop, 4) == 0) { - // accept and discard no-op - } - - else if (memcmp(code, kMsgCEnter, 4) == 0) { - enter(); - } - - else if (memcmp(code, kMsgCLeave, 4) == 0) { - leave(); - } - - else if (memcmp(code, kMsgCClipboard, 4) == 0) { - grabClipboard(); - } - - else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { - screensaver(); - } - - else if (memcmp(code, kMsgQInfo, 4) == 0) { - queryInfo(); - } - - else if (memcmp(code, kMsgCInfoAck, 4) == 0) { - infoAcknowledgment(); - } - - else if (memcmp(code, kMsgDClipboard, 4) == 0) { - setClipboard(); - } - - else if (memcmp(code, kMsgCResetOptions, 4) == 0) { - resetOptions(); - } - - else if (memcmp(code, kMsgDSetOptions, 4) == 0) { - setOptions(); - } - - else if (memcmp(code, kMsgCClose, 4) == 0) { - // server wants us to hangup - LOG((CLOG_DEBUG1 "recv close")); - m_client->disconnect(NULL); + switch ((this->*m_parser)(code)) { + case kOkay: break; - } - else if (memcmp(code, kMsgEIncompatible, 4) == 0) { - SInt32 major, minor; - CProtocolUtil::readf(m_stream, - kMsgEIncompatible + 4, &major, &minor); - LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); - m_client->disconnect("server has incompatible version"); + case kUnknown: + LOG((CLOG_ERR "invalid message from server")); + m_client->disconnect("invalid message from server"); + return; + + case kDisconnect: return; } - else if (memcmp(code, kMsgEBusy, 4) == 0) { - LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str())); - m_client->disconnect("server already has a connected client with our name"); - return; - } - - else if (memcmp(code, kMsgEUnknown, 4) == 0) { - LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str())); - m_client->disconnect("server refused client with our name"); - return; - } - - else if (memcmp(code, kMsgEBad, 4) == 0) { - LOG((CLOG_ERR "server disconnected due to a protocol error")); - m_client->disconnect("server reported a protocol error"); - return; - } - - else { - // unknown message - LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3])); - m_client->disconnect("unknown message from server"); - return; - } + // next message + n = m_stream->read(code, 4); } + flushCompressedMouse(); } +CServerProxy::EResult +CServerProxy::parseHandshakeMessage(const UInt8* code) +{ + if (memcmp(code, kMsgQInfo, 4) == 0) { + queryInfo(); + } + + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + infoAcknowledgment(); + } + + else if (memcmp(code, kMsgCResetOptions, 4) == 0) { + resetOptions(); + + // handshake is complete + m_parser = &CServerProxy::parseMessage; + EVENTQUEUE->addEvent(CEvent(getHandshakeCompleteEvent(), this)); + } + + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + } + + else if (memcmp(code, kMsgCClose, 4) == 0) { + // server wants us to hangup + LOG((CLOG_DEBUG1 "recv close")); + m_client->disconnect(NULL); + return kDisconnect; + } + + else if (memcmp(code, kMsgEIncompatible, 4) == 0) { + SInt32 major, minor; + CProtocolUtil::readf(m_stream, + kMsgEIncompatible + 4, &major, &minor); + LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); + m_client->disconnect("server has incompatible version"); + return kDisconnect; + } + + else if (memcmp(code, kMsgEBusy, 4) == 0) { + LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str())); + m_client->disconnect("server already has a connected client with our name"); + return kDisconnect; + } + + else if (memcmp(code, kMsgEUnknown, 4) == 0) { + LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str())); + m_client->disconnect("server refused client with our name"); + return kDisconnect; + } + + else if (memcmp(code, kMsgEBad, 4) == 0) { + LOG((CLOG_ERR "server disconnected due to a protocol error")); + m_client->disconnect("server reported a protocol error"); + return kDisconnect; + } + else { + return kUnknown; + } + + return kOkay; +} + +CServerProxy::EResult +CServerProxy::parseMessage(const UInt8* code) +{ + if (memcmp(code, kMsgDMouseMove, 4) == 0) { + mouseMove(); + } + + else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { + mouseWheel(); + } + + else if (memcmp(code, kMsgDKeyDown, 4) == 0) { + keyDown(); + } + + else if (memcmp(code, kMsgDKeyUp, 4) == 0) { + keyUp(); + } + + else if (memcmp(code, kMsgDMouseDown, 4) == 0) { + mouseDown(); + } + + else if (memcmp(code, kMsgDMouseUp, 4) == 0) { + mouseUp(); + } + + else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { + keyRepeat(); + } + + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + } + + else if (memcmp(code, kMsgCEnter, 4) == 0) { + enter(); + } + + else if (memcmp(code, kMsgCLeave, 4) == 0) { + leave(); + } + + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + grabClipboard(); + } + + else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { + screensaver(); + } + + else if (memcmp(code, kMsgQInfo, 4) == 0) { + queryInfo(); + } + + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + infoAcknowledgment(); + } + + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + setClipboard(); + } + + else if (memcmp(code, kMsgCResetOptions, 4) == 0) { + resetOptions(); + } + + else if (memcmp(code, kMsgDSetOptions, 4) == 0) { + setOptions(); + } + + else if (memcmp(code, kMsgCClose, 4) == 0) { + // server wants us to hangup + LOG((CLOG_DEBUG1 "recv close")); + m_client->disconnect(NULL); + return kDisconnect; + } + else if (memcmp(code, kMsgEBad, 4) == 0) { + LOG((CLOG_ERR "server disconnected due to a protocol error")); + m_client->disconnect("server reported a protocol error"); + return kDisconnect; + } + else { + return kUnknown; + } + + return kOkay; +} + void CServerProxy::handleHeartBeat(const CEvent&, void*) { diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index f54cbe60..3a06784e 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -42,9 +42,9 @@ public: //! @name manipulators //@{ - virtual void onInfoChanged(); - virtual bool onGrabClipboard(ClipboardID); - virtual void onClipboardChanged(ClipboardID, const IClipboard*); + void onInfoChanged(); + bool onGrabClipboard(ClipboardID); + void onClipboardChanged(ClipboardID, const IClipboard*); //@} //! @name accessors @@ -59,6 +59,11 @@ public: //@} +protected: + enum EResult { kOkay, kUnknown, kDisconnect }; + EResult parseHandshakeMessage(const UInt8* code); + EResult parseMessage(const UInt8* code); + private: // if compressing mouse motion then send the last motion now void flushCompressedMouse(); @@ -72,7 +77,7 @@ private: KeyModifierMask translateModifierMask(KeyModifierMask) const; // event handlers - void handleMessage(const CEvent&, void*); + void handleData(const CEvent&, void*); void handleHeartBeat(const CEvent&, void*); // message handlers @@ -94,6 +99,8 @@ private: void infoAcknowledgment(); private: + typedef EResult (CServerProxy::*MessageParser)(const UInt8*); + CClient* m_client; IStream* m_stream; CEventQueueTimer* m_timer; @@ -108,6 +115,8 @@ private: KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; double m_heartRate; + MessageParser m_parser; + static CEvent::Type s_handshakeCompleteEvent; }; diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index cfaafeb0..34aaec8f 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -19,7 +19,6 @@ EXTRA_DIST = \ common.dsp \ BasicTypes.h \ IInterface.h \ - Version.h \ common.h \ stdbitset.h \ stddeque.h \ @@ -40,5 +39,11 @@ MAINTAINERCLEANFILES = \ Makefile.in \ $(NULL) +noinst_LIBRARIES = libcommon.a +libcommon_a_SOURCES = \ + Version.cpp \ + Version.h \ + $(NULL) + INCLUDES = \ $(NULL) diff --git a/lib/common/Version.h b/lib/common/Version.h index 9f1af7ae..26049a6d 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -23,17 +23,17 @@ #endif // important strings -static const char* kApplication = "synergy"; -static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; -static const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; -static const char* kWebsite = "http://synergy2.sourceforge.net/"; +extern const char* kApplication; +extern const char* kCopyright; +extern const char* kContact; +extern const char* kWebsite; // build version. follows linux kernel style: an even minor number implies // a release version, odd implies development version. -static const char* kVersion = VERSION; +extern const char* kVersion; // application version -static const char* kAppVersion = "synergy " VERSION; +extern const char* kAppVersion; // exit codes static const int kExitSuccess = 0; // successful completion diff --git a/lib/common/common.dsp b/lib/common/common.dsp index 38e1a686..a95905d2 100644 --- a/lib/common/common.dsp +++ b/lib/common/common.dsp @@ -85,6 +85,10 @@ LIB32=link.exe -lib # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\Version.cpp +# End Source File # End Group # Begin Group "Header Files" diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp index cd88113d..195b7b67 100644 --- a/lib/mt/CThread.cpp +++ b/lib/mt/CThread.cpp @@ -79,6 +79,12 @@ CThread::setPriority(int n) ARCH->setPriorityOfThread(m_thread, n); } +void +CThread::unblockPollSocket() +{ + ARCH->unblockPollSocket(m_thread); +} + CThread CThread::getCurrentThread() { @@ -97,24 +103,6 @@ CThread::wait(double timeout) const return ARCH->wait(m_thread, timeout); } -CThread::EWaitResult -CThread::waitForEvent(double timeout) const -{ - // IArchMultithread EWaitResults map directly to our EWaitResults - static const EWaitResult s_map[] = { - kEvent, - kExit, - kTimeout - }; - return s_map[ARCH->waitForEvent(m_thread, timeout)]; -} - -void -CThread::unblock() const -{ - ARCH->unblockThread(m_thread); -} - void* CThread::getResult() const { diff --git a/lib/mt/CThread.h b/lib/mt/CThread.h index 011f8611..896d3778 100644 --- a/lib/mt/CThread.h +++ b/lib/mt/CThread.h @@ -39,13 +39,6 @@ documentation. // note -- do not derive from this class class CThread { public: - //! Result of waitForEvent() - enum EWaitResult { - kEvent, //!< An event is pending - kExit, //!< Thread exited - kTimeout //!< Wait timed out - }; - //! Run \c adoptedJob in a new thread /*! Create and start a new thread executing the \c adoptedJob. The @@ -130,6 +123,13 @@ public: */ void setPriority(int n); + //! Force pollSocket() to return + /*! + Forces a currently blocked pollSocket() in the thread to return + immediately. + */ + void unblockPollSocket(); + //@} //! @name accessors //@{ @@ -164,31 +164,6 @@ public: */ bool wait(double timeout = -1.0) const; - //! Wait for an event (win32) - /*! - Wait for the message queue to contain a message or for the thread - to exit for up to \c timeout seconds. This returns immediately if - any message is available (including messages that were already in - the queue during the last call to \c GetMessage() or - \c PeekMessage() or waitForEvent(). Returns kEvent if a message - is available, kExit if the thread exited, and kTimeout otherwise. - This will wait forever if \c timeout < 0.0. - - This method is available under win32 only. - - (cancellation point) - */ - EWaitResult waitForEvent(double timeout = -1.0) const; - - //! Unblock thread in system call - /*! - Cause a thread that's in a blocking system call to return. This - call may return before the thread is unblocked. If the thread is - not in a blocking system call, this call has no effect. This does - not cause CMutex::lock() or CCondVar::wait() to return prematurely. - */ - void unblock() const; - //! Get the exit result /*! Returns the exit result. This does an implicit wait(). It returns diff --git a/lib/net/CSocketMultiplexer.cpp b/lib/net/CSocketMultiplexer.cpp index 2ed2dc35..1a9e11b6 100644 --- a/lib/net/CSocketMultiplexer.cpp +++ b/lib/net/CSocketMultiplexer.cpp @@ -54,6 +54,7 @@ CSocketMultiplexer::CSocketMultiplexer() : CSocketMultiplexer::~CSocketMultiplexer() { m_thread->cancel(); + m_thread->unblockPollSocket(); m_thread->wait(); delete m_thread; delete m_polling; @@ -87,7 +88,7 @@ CSocketMultiplexer::addSocket(ISocket* socket, ISocketMultiplexerJob* job) *m_pollable = false; // break thread out of poll - m_thread->unblock(); + m_thread->unblockPollSocket(); // wait for poll to finish while (*m_polling) { @@ -129,7 +130,7 @@ CSocketMultiplexer::removeSocket(ISocket* socket) *m_pollable = false; // break thread out of poll - m_thread->unblock(); + m_thread->unblockPollSocket(); // wait until thread finishes poll while (*m_polling) { @@ -160,11 +161,12 @@ CSocketMultiplexer::serviceThread(void*) // service the connections for (;;) { + CThread::testCancel(); { CLock lock(m_mutex); // wait until pollable - while (!*m_pollable) { + while (!(bool)*m_pollable) { m_pollable->wait(); } @@ -281,7 +283,6 @@ CSocketMultiplexer::CJobCursor CSocketMultiplexer::nextCursor(CJobCursor cursor) { CLock lock(m_mutex); - ISocketMultiplexerJob* job = NULL; CJobCursor j = m_socketJobs.end(); CJobCursor i = cursor; while (++i != m_socketJobs.end()) { diff --git a/lib/net/IDataSocket.cpp b/lib/net/IDataSocket.cpp index ff10f4ec..dc4b08ed 100644 --- a/lib/net/IDataSocket.cpp +++ b/lib/net/IDataSocket.cpp @@ -34,3 +34,18 @@ IDataSocket::getConnectionFailedEvent() return CEvent::registerTypeOnce(s_failedEvent, "IDataSocket::failed"); } + +void +IDataSocket::close() +{ + // this is here to work around a VC++6 bug. see the header file. + assert(0 && "bad call"); +} + +void* +IDataSocket::getEventTarget() const +{ + // this is here to work around a VC++6 bug. see the header file. + assert(0 && "bad call"); + return NULL; +} diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h index bc0f2d3f..85b85480 100644 --- a/lib/net/IDataSocket.h +++ b/lib/net/IDataSocket.h @@ -65,9 +65,13 @@ public: //@} // ISocket overrides + // close() and getEventTarget() aren't pure to work around a bug + // in VC++6. it claims the methods are unused locals and warns + // that it's removing them. it's presumably tickled by inheriting + // methods with identical signatures from both superclasses. virtual void bind(const CNetworkAddress&) = 0; - virtual void close() = 0; - virtual void* getEventTarget() const = 0; + virtual void close(); + virtual void* getEventTarget() const; // IStream overrides virtual UInt32 read(void* buffer, UInt32 n) = 0; diff --git a/lib/platform/CMSWindowsDesktop.h b/lib/platform/CMSWindowsDesktop.h index 6f1d6afe..908f5c87 100644 --- a/lib/platform/CMSWindowsDesktop.h +++ b/lib/platform/CMSWindowsDesktop.h @@ -21,6 +21,12 @@ //! Encapsulate Microsoft Windows desktop class CMSWindowsDesktop { public: + //! Open the desktop + /*! + Opens the desktop named \p name. The caller must close the desktop. + */ + static HDESK openDesktop(const CString& name); + //! Open the input desktop /*! Opens the input desktop. The caller must close the desktop. diff --git a/lib/platform/CMSWindowsEventQueueBuffer.cpp b/lib/platform/CMSWindowsEventQueueBuffer.cpp index e69de29b..99366e1c 100644 --- a/lib/platform/CMSWindowsEventQueueBuffer.cpp +++ b/lib/platform/CMSWindowsEventQueueBuffer.cpp @@ -0,0 +1,129 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsEventQueueBuffer.h" +#include "CThread.h" +#include "IEventQueue.h" +#include "CArchMiscWindows.h" + +// +// CEventQueueTimer +// + +class CEventQueueTimer { }; + + +// +// CMSWindowsEventQueueBuffer +// + +CMSWindowsEventQueueBuffer::CMSWindowsEventQueueBuffer() +{ + // remember thread. we'll be posting messages to it. + m_thread = GetCurrentThreadId(); + + // create a message type for custom events + m_userEvent = RegisterWindowMessage("SYNERGY_USER_EVENT"); + + // get message type for daemon quit + m_daemonQuit = CArchMiscWindows::getDaemonQuitMessage(); + + // make sure this thread has a message queue + MSG dummy; + PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); +} + +CMSWindowsEventQueueBuffer::~CMSWindowsEventQueueBuffer() +{ + // do nothing +} + +void +CMSWindowsEventQueueBuffer::waitForEvent(double timeout) +{ + // check if messages are available first. if we don't do this then + // MsgWaitForMultipleObjects() will block even if the queue isn't + // empty if the messages in the queue were there before the last + // call to GetMessage()/PeekMessage(). + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + return; + } + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for a message. we cannot be interrupted by thread + // cancellation but that's okay because we're run in the main + // thread and we never cancel that thread. + HANDLE dummy[1]; + MsgWaitForMultipleObjects(0, dummy, FALSE, t, QS_ALLINPUT); +} + +IEventQueueBuffer::Type +CMSWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) +{ + // BOOL. yeah, right. + BOOL result = GetMessage(&m_event, NULL, 0, 0); + if (result == -1) { + return kNone; + } + else if (result == 0) { + event = CEvent(CEvent::kQuit); + return kSystem; + } + else if (m_event.message == m_daemonQuit) { + event = CEvent(CEvent::kQuit); + return kSystem; + } + else if (m_event.message == m_userEvent) { + dataID = static_cast(m_event.wParam); + return kUser; + } + else { + event = CEvent(CEvent::kSystem, + IEventQueue::getSystemTarget(), &m_event); + return kSystem; + } +} + +bool +CMSWindowsEventQueueBuffer::addEvent(UInt32 dataID) +{ + return (PostThreadMessage(m_thread, m_userEvent, + static_cast(dataID), 0) != 0); +} + +bool +CMSWindowsEventQueueBuffer::isEmpty() const +{ + return (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0); +} + +CEventQueueTimer* +CMSWindowsEventQueueBuffer::newTimer(double, bool) const +{ + return new CEventQueueTimer; +} + +void +CMSWindowsEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const +{ + delete timer; +} diff --git a/lib/platform/CMSWindowsEventQueueBuffer.h b/lib/platform/CMSWindowsEventQueueBuffer.h index e69de29b..28d8a2f6 100644 --- a/lib/platform/CMSWindowsEventQueueBuffer.h +++ b/lib/platform/CMSWindowsEventQueueBuffer.h @@ -0,0 +1,44 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSEVENTQUEUEBUFFER_H +#define CMSWINDOWSEVENTQUEUEBUFFER_H + +#include "IEventQueueBuffer.h" +#define WIN32_LEAN_AND_MEAN +#include + +//! Event queue buffer for Win32 +class CMSWindowsEventQueueBuffer : public IEventQueueBuffer { +public: + CMSWindowsEventQueueBuffer(); + virtual ~CMSWindowsEventQueueBuffer(); + + // IEventQueueBuffer overrides + virtual void waitForEvent(double timeout); + virtual Type getEvent(CEvent& event, UInt32& dataID); + virtual bool addEvent(UInt32 dataID); + virtual bool isEmpty() const; + virtual CEventQueueTimer* + newTimer(double duration, bool oneShot) const; + virtual void deleteTimer(CEventQueueTimer*) const; + +private: + DWORD m_thread; + UINT m_userEvent; + MSG m_event; + UINT m_daemonQuit; +}; + +#endif diff --git a/lib/platform/CMSWindowsKeyMapper.cpp b/lib/platform/CMSWindowsKeyMapper.cpp index dbd981e2..de2435a2 100644 --- a/lib/platform/CMSWindowsKeyMapper.cpp +++ b/lib/platform/CMSWindowsKeyMapper.cpp @@ -741,7 +741,7 @@ CMSWindowsKeyMapper::update(IKeyState* keyState) for (size_t j = 0; j < CModifierKeys::s_maxKeys; ++j) { if (s_modifiers[i].m_keys[j] != 0) { SHORT s = GetKeyState(s_modifiers[i].m_keys[j]); - m_keys[s_modifiers[i].m_keys[j]] = static_cast(s); + m_keys[s_modifiers[i].m_keys[j] & 0xffu] = static_cast(s); if (keyState != NULL) { if ((s & 0x01) != 0) { keyState->setToggled(s_modifiers[i].m_mask); diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index f9e1a334..21c06a23 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -15,17 +15,18 @@ #include "CMSWindowsScreen.h" #include "CMSWindowsClipboard.h" #include "CMSWindowsDesktop.h" +#include "CMSWindowsEventQueueBuffer.h" #include "CMSWindowsScreenSaver.h" #include "CClipboard.h" -#include "IScreenReceiver.h" -#include "IPrimaryScreenReceiver.h" #include "XScreen.h" -#include "CThread.h" #include "CLock.h" +#include "CThread.h" #include "CFunctionJob.h" #include "CLog.h" #include "CString.h" #include "CStringUtil.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" #include "TMethodJob.h" #include "CArch.h" #include "CArchMiscWindows.h" @@ -33,6 +34,25 @@ #include #include +// ; +#define SYNERGY_MSG_SWITCH SYNERGY_HOOK_LAST_MSG + 1 +// ; +#define SYNERGY_MSG_ENTER SYNERGY_HOOK_LAST_MSG + 2 +// ; +#define SYNERGY_MSG_LEAVE SYNERGY_HOOK_LAST_MSG + 3 +// wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code +#define SYNERGY_MSG_FAKE_KEY SYNERGY_HOOK_LAST_MSG + 4 + // flags, XBUTTON id +#define SYNERGY_MSG_FAKE_BUTTON SYNERGY_HOOK_LAST_MSG + 5 +// x; y +#define SYNERGY_MSG_FAKE_MOVE SYNERGY_HOOK_LAST_MSG + 6 +// delta; +#define SYNERGY_MSG_FAKE_WHEEL SYNERGY_HOOK_LAST_MSG + 7 +// POINT*; +#define SYNERGY_MSG_CURSOR_POS SYNERGY_HOOK_LAST_MSG + 8 +// IKeyState*; +#define SYNERGY_MSG_SYNC_KEYS SYNERGY_HOOK_LAST_MSG + 9 + // // add backwards compatible multihead support (and suppress bogus warning) // @@ -91,15 +111,11 @@ // HINSTANCE CMSWindowsScreen::s_instance = NULL; -CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; +CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; -CMSWindowsScreen::CMSWindowsScreen( - IScreenReceiver* receiver, - IPrimaryScreenReceiver* primaryReceiver) : - m_isPrimary(primaryReceiver != NULL), +CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : + m_isPrimary(isPrimary), m_is95Family(CArchMiscWindows::isWindows95Family()), - m_receiver(receiver), - m_primaryReceiver(primaryReceiver), m_isOnScreen(m_isPrimary), m_class(0), m_cursor(NULL), @@ -109,44 +125,87 @@ CMSWindowsScreen::CMSWindowsScreen( m_xCenter(0), m_yCenter(0), m_multimon(false), m_xCursor(0), m_yCursor(0), + m_sequenceNumber(0), m_mark(0), m_markReceived(0), - m_threadID(0), - m_lastThreadID(0), - m_timer(0), - m_oneShotTimer(0), + m_timer(NULL), m_screensaver(NULL), m_screensaverNotify(false), m_nextClipboardWindow(NULL), m_ownClipboard(false), - m_desk(NULL), - m_deskName(), - m_inaccessibleDesktop(false), + m_activeDesk(NULL), + m_activeDeskName(), m_hookLibrary(NULL), - m_lowLevel(false), - m_keyState(NULL) + m_keyState(NULL), + m_mutex(), + m_deskReady(&m_mutex, false) { + assert(s_instance != NULL); assert(s_screen == NULL); - assert(m_receiver != NULL); s_screen = this; + try { + m_hookLibrary = openHookLibrary("synrgyhk"); + m_cursor = createBlankCursor(); + m_class = createWindowClass(); + m_deskClass = createDeskWindowClass(m_isPrimary); + updateScreenShape(); + m_window = createWindow(m_class, "Synergy"); + m_screensaver = new CMSWindowsScreenSaver(); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "")); + LOG((CLOG_DEBUG "window is 0x%08x", m_window)); + } + catch (...) { + delete m_screensaver; + destroyWindow(m_window); + destroyClass(m_deskClass); + destroyClass(m_class); + destroyCursor(m_cursor); + closeHookLibrary(m_hookLibrary); + m_screensaver = NULL; + m_class = 0; + m_cursor = NULL; + m_hookLibrary = NULL; + s_screen = NULL; + throw; + } - // make sure this thread has a message queue - MSG dummy; - PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + + // install event handlers + EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(), + new TMethodEventJob(this, + &CMSWindowsScreen::handleSystemEvent)); + + // install the platform event queue + EVENTQUEUE->adoptBuffer(new CMSWindowsEventQueueBuffer); } CMSWindowsScreen::~CMSWindowsScreen() { assert(s_screen != NULL); - assert(m_class == 0); + disable(); + EVENTQUEUE->adoptBuffer(NULL); + EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget()); + removeDesks(); + ChangeClipboardChain(m_window, m_nextClipboardWindow); + delete m_screensaver; + destroyWindow(m_window); + destroyClass(m_deskClass); + destroyClass(m_class); + destroyCursor(m_cursor); + closeHookLibrary(m_hookLibrary); s_screen = NULL; } void CMSWindowsScreen::init(HINSTANCE instance) { + assert(s_instance == NULL); + assert(instance != NULL); + s_instance = instance; } @@ -157,129 +216,11 @@ CMSWindowsScreen::getInstance() } void -CMSWindowsScreen::open(IKeyState* keyState) +CMSWindowsScreen::setKeyState(IKeyState* keyState) { - assert(s_instance != NULL); - assert(m_class == 0); - assert(m_hookLibrary == NULL); - - try { - // load the hook library - m_hookLibrary = LoadLibrary("synrgyhk"); - if (m_hookLibrary == NULL) { - LOG((CLOG_ERR "Failed to load hook library; synrgyhk.dll is missing")); - throw XScreenOpenFailure(); - } - m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides"); - m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone"); - m_setMode = (SetModeFunc)GetProcAddress(m_hookLibrary, "setMode"); - m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install"); - m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall"); - m_init = (InitFunc)GetProcAddress(m_hookLibrary, "init"); - m_cleanup = (CleanupFunc)GetProcAddress(m_hookLibrary, "cleanup"); - m_installScreensaver = - (InstallScreenSaverFunc)GetProcAddress( - m_hookLibrary, "installScreenSaver"); - m_uninstallScreensaver = - (UninstallScreenSaverFunc)GetProcAddress( - m_hookLibrary, "uninstallScreenSaver"); - if (m_setSides == NULL || - m_setZone == NULL || - m_setMode == NULL || - m_install == NULL || - m_uninstall == NULL || - m_init == NULL || - m_cleanup == NULL || - m_installScreensaver == NULL || - m_uninstallScreensaver == NULL) { - LOG((CLOG_ERR "Invalid hook library; use a newer synrgyhk.dll")); - throw XScreenOpenFailure(); - } - - // save thread id. this is mainly to ensure that mainLoop() - // is called by the same thread that called open(). these - // threads must be the same to get the right message queue. - m_threadID = GetCurrentThreadId(); - - // initialize hook library - if (m_isPrimary && m_init(m_threadID) == 0) { - LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?")); - throw XScreenOpenFailure(); - } - - // create the transparent cursor - m_cursor = createBlankCursor(); - - // register a window class - WNDCLASSEX classInfo; - classInfo.cbSize = sizeof(classInfo); - classInfo.style = CS_DBLCLKS | CS_NOCLOSE; - classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; - classInfo.cbClsExtra = 0; - classInfo.cbWndExtra = 0; - classInfo.hInstance = s_instance; - classInfo.hIcon = NULL; - classInfo.hCursor = m_cursor; - classInfo.hbrBackground = NULL; - classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = "Synergy"; - classInfo.hIconSm = NULL; - m_class = RegisterClassEx(&classInfo); - - // get screen shape - updateScreenShape(); - - // initialize the screen saver - m_screensaver = new CMSWindowsScreenSaver(); - - // initialize marks - m_mark = 0; - m_markReceived = 0; - } - catch (...) { - close(); - throw; - } - - // save the IKeyState m_keyState = keyState; -} - -void -CMSWindowsScreen::close() -{ - assert(s_instance != NULL); - - // done with m_keyState - m_keyState = NULL; - - // done with screen saver - delete m_screensaver; - - // unregister the window class - if (m_class != 0) { - UnregisterClass((LPCTSTR)m_class, s_instance); - } - - // done with cursor - if (m_cursor != NULL) { - DestroyCursor(m_cursor); - } - - // done with hook library - if (m_hookLibrary != NULL) { - if (m_isPrimary) { - m_cleanup(); - } - FreeLibrary(m_hookLibrary); - } - - // reset state - m_screensaver = NULL; - m_cursor = NULL; - m_class = 0; - m_hookLibrary = NULL; - m_threadID = 0; + m_keyMapper.update(m_keyState); + memset(m_buttons, 0, sizeof(m_buttons)); } void @@ -298,92 +239,40 @@ CMSWindowsScreen::enable() m_setMode(kHOOK_WATCH_JUMP_ZONE); } - // create the window - if (!switchDesktop(CMSWindowsDesktop::openInputDesktop())) { - throw XScreenOpenFailure(); - } + // set the active desk and (re)install the hooks + checkDesk(); - // poll input desktop to see if it changes. windows doesn't - // inform us when the desktop has changed but we need to - // open a new window when that happens so we poll. this is - // also used for polling other stuff. - m_timer = SetTimer(NULL, 0, 200, NULL); + // install the desk timer. this timer periodically checks + // which desk is active and reinstalls the hooks as necessary. + // we wouldn't need this if windows notified us of a desktop + // change but as far as i can tell it doesn't. + m_timer = EVENTQUEUE->newTimer(0.2, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer, + new TMethodEventJob( + this, &CMSWindowsScreen::handleCheckDesk)); } void CMSWindowsScreen::disable() { - // remove timers - if (m_timer != 0) { - KillTimer(NULL, m_timer); - } - if (m_oneShotTimer != 0) { - KillTimer(NULL, m_oneShotTimer); + // remove timer + if (m_timer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer); + EVENTQUEUE->deleteTimer(m_timer); + m_timer = NULL; } - // reset state - m_timer = 0; - m_oneShotTimer = 0; - - // done with window - switchDesktop(NULL); - assert(m_window == NULL); - assert(m_desk == NULL); -} - -void -CMSWindowsScreen::mainLoop() -{ - // must call mainLoop() from same thread as openDesktop() - assert(m_threadID == GetCurrentThreadId()); - - // event loop - MSG msg; - for (;;) { - // wait for an event in a cancellable way - if (CThread::getCurrentThread().waitForEvent(-1.0) != CThread::kEvent) { - continue; - } - if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - continue; - } - - // handle quit message - if (msg.message == WM_QUIT) { - if (msg.wParam == 0) { - // force termination - CThread::getCurrentThread().cancel(); - } - else { - // just exit the main loop - break; - } - } - - // dispatch message - if (!onPreDispatch(msg.hwnd, msg.message, msg.wParam, msg.lParam)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + // disable hooks + if (m_isPrimary) { + m_setMode(kHOOK_DISABLE); } } -void -CMSWindowsScreen::exitMainLoop() -{ - // close down cleanly - PostThreadMessage(m_threadID, WM_QUIT, 1, 0); -} - void CMSWindowsScreen::enter() { + sendDeskMessage(SYNERGY_MSG_ENTER, 0, 0); if (m_isPrimary) { - // show the cursor - showCursor(true); - ShowWindow(m_window, SW_HIDE); - m_cursorThread = 0; - // enable special key sequences on win95 family enableSpecialKeys(true); @@ -393,10 +282,6 @@ CMSWindowsScreen::enter() // all messages prior to now are invalid nextMark(); } - else { - // show the cursor - ShowWindow(m_window, SW_HIDE); - } // now on screen m_isOnScreen = true; @@ -405,17 +290,9 @@ CMSWindowsScreen::enter() bool CMSWindowsScreen::leave() { - if (m_isPrimary) { - // we don't need a window to capture input but we need a window - // to hide the cursor when using low-level hooks. also take the - // activation so we use our keyboard layout, not the layout of - // whatever window was active. - if (m_lowLevel) { - SetWindowPos(m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1, - SWP_NOACTIVATE); - ShowWindow(m_window, SW_SHOW); - } + sendDeskMessage(SYNERGY_MSG_LEAVE, 0, 0); + if (m_isPrimary) { /* XXX // update keys m_keyMapper.update(NULL); @@ -430,25 +307,8 @@ CMSWindowsScreen::leave() // all messages prior to now are invalid nextMark(); - // watch jump zones + // capture events m_setMode(kHOOK_RELAY_EVENTS); - - // hide the cursor if using low level hooks - if (m_lowLevel) { - HWND hwnd = GetForegroundWindow(); - m_cursorThread = GetWindowThreadProcessId(hwnd, NULL); - showCursor(false); - } - } - else { - // move hider window under the cursor center - MoveWindow(m_window, m_xCenter, m_yCenter, 1, 1, FALSE); - - // raise and show the hider window - ShowWindow(m_window, SW_SHOWNA); - - // warp the mouse to the cursor center - fakeMouseMove(m_xCenter, m_yCenter); } // now off screen @@ -493,8 +353,8 @@ CMSWindowsScreen::checkClipboards() if (m_ownClipboard && !CMSWindowsClipboard::isOwnedBySynergy()) { LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received")); m_ownClipboard = false; - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); } } @@ -554,17 +414,28 @@ CMSWindowsScreen::setOptions(const COptionsList&) void CMSWindowsScreen::updateKeys() { - syncDesktop(); - m_keyMapper.update(m_keyState); + sendDeskMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); memset(m_buttons, 0, sizeof(m_buttons)); } +void +CMSWindowsScreen::setSequenceNumber(UInt32 seqNum) +{ + m_sequenceNumber = seqNum; +} + bool CMSWindowsScreen::isPrimary() const { return m_isPrimary; } +void* +CMSWindowsScreen::getEventTarget() const +{ + return const_cast(this); +} + bool CMSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const { @@ -588,15 +459,10 @@ void CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const { POINT pos; - syncDesktop(); - if (GetCursorPos(&pos)) { - x = pos.x; - y = pos.y; - } - else { - x = m_xCenter; - y = m_yCenter; - } + sendDeskMessage(SYNERGY_MSG_CURSOR_POS, + reinterpret_cast(&pos), 0); + x = pos.x; + y = pos.y; } void @@ -604,6 +470,7 @@ CMSWindowsScreen::reconfigure(UInt32 activeSides) { assert(m_isPrimary); + LOG((CLOG_DEBUG "active sides: %x", activeSides)); m_setSides(activeSides); } @@ -625,18 +492,6 @@ CMSWindowsScreen::warpCursor(SInt32 x, SInt32 y) m_yCursor = y; } -UInt32 -CMSWindowsScreen::addOneShotTimer(double timeout) -{ - // FIXME -- support multiple one-shot timers - if (m_oneShotTimer != 0) { - KillTimer(NULL, m_oneShotTimer); - } - m_oneShotTimer = SetTimer(NULL, 0, - static_cast(1000.0 * timeout), NULL); - return 0; -} - SInt32 CMSWindowsScreen::getJumpZoneSize() const { @@ -665,6 +520,13 @@ CMSWindowsScreen::isAnyMouseButtonDown() const return false; } +void +CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + x = m_xCenter; + y = m_yCenter; +} + const char* CMSWindowsScreen::getKeyName(KeyButton virtualKey) const { @@ -682,9 +544,9 @@ CMSWindowsScreen::fakeKeyEvent(KeyButton virtualKey, bool press) const flags |= KEYEVENTF_KEYUP; } const UINT code = m_keyMapper.keyToScanCode(&virtualKey); - syncDesktop(); - keybd_event(static_cast(virtualKey & 0xffu), - static_cast(code), flags, 0); + sendDeskMessage(SYNERGY_MSG_FAKE_KEY, flags, + MAKEWORD(static_cast(code), + static_cast(virtualKey & 0xffu))); } bool @@ -692,8 +554,8 @@ CMSWindowsScreen::fakeCtrlAltDel() const { if (!m_is95Family) { // to fake ctrl+alt+del on the NT family we broadcast a suitable - // hotkey to all windows on the winlogon desktop. however, we - // the current thread must be on that desktop to do the broadcast + // hotkey to all windows on the winlogon desktop. however, the + // current thread must be on that desktop to do the broadcast // and we can't switch just any thread because some own windows // or hooks. so start a new thread to do the real work. CThread cad(new CFunctionJob(&CMSWindowsScreen::ctrlAltDelThread)); @@ -720,102 +582,23 @@ CMSWindowsScreen::fakeCtrlAltDel() const void CMSWindowsScreen::fakeMouseButton(ButtonID id, bool press) const { - // map button id to button flag DWORD data; DWORD flags = mapButtonToEvent(id, press, &data); - - // send event - if (flags != 0) { - syncDesktop(); - mouse_event(flags, 0, 0, data, 0); - } + sendDeskMessage(SYNERGY_MSG_FAKE_BUTTON, flags, data); } void CMSWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const { - // motion is simple (i.e. it's on the primary monitor) if there - // is only one monitor. - bool simple = !m_multimon; - if (!simple) { - // also simple if motion is within the primary monitor - simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && - y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); - } - - // move the mouse directly to target position if motion is simple - syncDesktop(); - if (simple) { - // when using absolute positioning with mouse_event(), - // the normalized device coordinates range over only - // the primary screen. - SInt32 w = GetSystemMetrics(SM_CXSCREEN); - SInt32 h = GetSystemMetrics(SM_CYSCREEN); - mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65536.0 * x) / w), - (DWORD)((65536.0 * y) / h), - 0, 0); - } - - // windows 98 (and Me?) is broken. you cannot set the absolute - // position of the mouse except on the primary monitor but you - // can do relative moves onto any monitor. this is, in microsoft's - // words, "by design." apparently the designers of windows 2000 - // we're a little less lazy and did it right. - // - // microsoft recommends in Q193003 to absolute position the cursor - // somewhere on the primary monitor then relative move to the - // desired location. this doesn't work for us because when the - // user drags a scrollbar, a window, etc. it causes the dragged - // item to jump back a forth between the position on the primary - // monitor and the desired position. while it always ends up in - // the right place, the effect is disconcerting. - // - // instead we'll get the cursor's current position and do just a - // relative move from there to the desired position. relative - // moves are subject to cursor acceleration which we don't want. - // so we disable acceleration, do the relative move, then restore - // acceleration. there's a slight chance we'll end up in the - // wrong place if the user moves the cursor using this system's - // mouse while simultaneously moving the mouse on the server - // system. that defeats the purpose of synergy so we'll assume - // that won't happen. even if it does, the next mouse move will - // correct the position. - else { - // save mouse speed & acceleration - int oldSpeed[4]; - bool accelChanged = - SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && - SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); - - // use 1:1 motion - if (accelChanged) { - int newSpeed[4] = { 0, 0, 0, 1 }; - accelChanged = - SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || - SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); - } - - // get current mouse position - POINT pos; - GetCursorPos(&pos); - - // move relative to mouse position - mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); - - // restore mouse speed & acceleration - if (accelChanged) { - SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); - SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); - } - } + sendDeskMessage(SYNERGY_MSG_FAKE_MOVE, + static_cast(x), + static_cast(y)); } void CMSWindowsScreen::fakeMouseWheel(SInt32 delta) const { - syncDesktop(); - mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); + sendDeskMessage(SYNERGY_MSG_FAKE_WHEEL, delta, 0); } KeyButton @@ -827,171 +610,190 @@ CMSWindowsScreen::mapKey(IKeyState::Keystrokes& keys, return m_keyMapper.mapKey(keys, keyState, id, desiredMask, isAutoRepeat); } -void -CMSWindowsScreen::updateScreenShape() +HINSTANCE +CMSWindowsScreen::openHookLibrary(const char* name) { - // get shape - m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); - m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); - m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); - m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); - LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + // load the hook library + HINSTANCE hookLibrary = LoadLibrary(name); + if (hookLibrary == NULL) { + LOG((CLOG_ERR "Failed to load hook library; %s.dll is missing", name)); + throw XScreenOpenFailure(); + } - // get center for cursor - m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; - m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; + // look up functions + m_setSides = (SetSidesFunc)GetProcAddress(hookLibrary, "setSides"); + m_setZone = (SetZoneFunc)GetProcAddress(hookLibrary, "setZone"); + m_setMode = (SetModeFunc)GetProcAddress(hookLibrary, "setMode"); + m_install = (InstallFunc)GetProcAddress(hookLibrary, "install"); + m_uninstall = (UninstallFunc)GetProcAddress(hookLibrary, "uninstall"); + m_init = (InitFunc)GetProcAddress(hookLibrary, "init"); + m_cleanup = (CleanupFunc)GetProcAddress(hookLibrary, "cleanup"); + m_installScreensaver = + (InstallScreenSaverFunc)GetProcAddress( + hookLibrary, "installScreenSaver"); + m_uninstallScreensaver = + (UninstallScreenSaverFunc)GetProcAddress( + hookLibrary, "uninstallScreenSaver"); + if (m_setSides == NULL || + m_setZone == NULL || + m_setMode == NULL || + m_install == NULL || + m_uninstall == NULL || + m_init == NULL || + m_cleanup == NULL || + m_installScreensaver == NULL || + m_uninstallScreensaver == NULL) { + LOG((CLOG_ERR "Invalid hook library; use a newer %s.dll", name)); + throw XScreenOpenFailure(); + } - // check for multiple monitors - m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || - m_h != GetSystemMetrics(SM_CYSCREEN)); + // initialize hook library + if (m_init(GetCurrentThreadId()) == 0) { + LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?")); + throw XScreenOpenFailure(); + } + + return hookLibrary; } -bool -CMSWindowsScreen::switchDesktop(HDESK desk) +void +CMSWindowsScreen::closeHookLibrary(HINSTANCE hookLibrary) const { - // assume we don't own the clipboard until later - m_ownClipboard = false; - - // destroy old window - if (m_window != NULL) { - // first remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // uninstall hooks. we can't change the thread desktop - // with hooks installed. - if (m_isPrimary) { - m_uninstall(); - } - - // now destroy window. we can't change the thread desktop - // with a window. - DestroyWindow(m_window); - m_window = NULL; - - // done with desk - CMSWindowsDesktop::closeDesktop(m_desk); - m_desk = NULL; - m_deskName = ""; + if (hookLibrary != NULL) { + m_cleanup(); + FreeLibrary(hookLibrary); } +} - // if no new desktop then we're done - if (desk == NULL) { - LOG((CLOG_DEBUG "disconnecting desktop")); - return true; +HCURSOR +CMSWindowsScreen::createBlankCursor() const +{ + // create a transparent cursor + int cw = GetSystemMetrics(SM_CXCURSOR); + int ch = GetSystemMetrics(SM_CYCURSOR); + UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; + UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; + memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); + memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); + HCURSOR c = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); + delete[] cursorXOR; + delete[] cursorAND; + return c; +} + +void +CMSWindowsScreen::destroyCursor(HCURSOR cursor) const +{ + if (cursor != NULL) { + DestroyCursor(cursor); } +} - // uninstall screen saver hooks - if (m_screensaverNotify) { - m_uninstallScreensaver(); +ATOM +CMSWindowsScreen::createWindowClass() const +{ + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = s_instance; + classInfo.hIcon = NULL; + classInfo.hCursor = NULL; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "Synergy"; + classInfo.hIconSm = NULL; + return RegisterClassEx(&classInfo); +} + +ATOM +CMSWindowsScreen::createDeskWindowClass(bool isPrimary) const +{ + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = isPrimary ? + &CMSWindowsScreen::primaryDeskProc : + &CMSWindowsScreen::secondaryDeskProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = s_instance; + classInfo.hIcon = NULL; + classInfo.hCursor = m_cursor; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "SynergyDesk"; + classInfo.hIconSm = NULL; + return RegisterClassEx(&classInfo); +} + +void +CMSWindowsScreen::destroyClass(ATOM windowClass) const +{ + if (windowClass != 0) { + UnregisterClass((LPCTSTR)windowClass, s_instance); } +} - // set the desktop. can only do this when there are no windows - // and hooks on the current desktop owned by this thread. - if (!CMSWindowsDesktop::setDesktop(desk)) { - LOG((CLOG_ERR "failed to set desktop: %d", GetLastError())); - CMSWindowsDesktop::closeDesktop(desk); - return false; - } - - // create the window - m_window = CreateWindowEx(WS_EX_TOPMOST | +HWND +CMSWindowsScreen::createWindow(ATOM windowClass, const char* name) const +{ + HWND window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - (LPCTSTR)m_class, - "Synergy", + (LPCTSTR)windowClass, + name, WS_POPUP, 0, 0, 1, 1, NULL, NULL, - getInstance(), + s_instance, NULL); - if (m_window == NULL) { + if (window == NULL) { LOG((CLOG_ERR "failed to create window: %d", GetLastError())); - CMSWindowsDesktop::closeDesktop(desk); - return false; + throw XScreenOpenFailure(); } - - // reinstall screen saver hooks - if (m_screensaverNotify) { - m_installScreensaver(); - } - - if (m_isPrimary) { - // we don't ever want our window to activate - EnableWindow(m_window, FALSE); - - // install hooks - switch (m_install()) { - case kHOOK_FAILED: - // FIXME -- can't install hook so we won't work; report error - m_lowLevel = false; - break; - - case kHOOK_OKAY: - m_lowLevel = false; - break; - - case kHOOK_OKAY_LL: - m_lowLevel = true; - break; - } - - if (m_isOnScreen) { - // all messages prior to now are invalid - // FIXME -- is this necessary; couldn't we lose key releases? - nextMark(); - } - } - else { - // update key state - updateKeys(); - - // hide cursor if this screen isn't active - if (!m_isOnScreen) { - // move hider window under the cursor center - MoveWindow(m_window, m_xCenter, m_yCenter, 1, 1, FALSE); - - // raise and show the hider window - ShowWindow(m_window, SW_SHOWNA); - - // warp the mouse to the cursor center - fakeMouseMove(m_xCenter, m_yCenter); - } - } - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - // check if we own the clipboard - m_ownClipboard = CMSWindowsClipboard::isOwnedBySynergy(); - - // save new desktop - m_desk = desk; - m_deskName = CMSWindowsDesktop::getDesktopName(desk); - LOG((CLOG_DEBUG "switched to desktop \"%s\" with window 0x%08x", m_deskName.c_str(), (UInt32)m_window)); - - return true; + return window; } void -CMSWindowsScreen::syncDesktop() const +CMSWindowsScreen::destroyWindow(HWND hwnd) const { - // change calling thread's desktop - if (!CMSWindowsDesktop::setDesktop(m_desk)) { -// LOG((CLOG_WARN "failed to set desktop: %d", GetLastError())); + if (hwnd != NULL) { + DestroyWindow(hwnd); } +} - // attach input queues if not already attached. this has a habit - // of sucking up more and more CPU each time it's called (even if - // the threads are already attached). since we only expect one - // thread to call this more than once we can save just the last - // attached thread. - DWORD threadID = GetCurrentThreadId(); - if (threadID != m_lastThreadID && threadID != m_threadID) { - CMSWindowsScreen* self = const_cast(this); - self->m_lastThreadID = threadID; - AttachThreadInput(threadID, m_threadID, TRUE); +void +CMSWindowsScreen::sendEvent(CEvent::Type type, void* data) +{ + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data)); +} + +void +CMSWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) +{ + CClipboardInfo* info = (CClipboardInfo*)malloc(sizeof(CClipboardInfo)); + info->m_id = id; + info->m_sequenceNumber = m_sequenceNumber; + sendEvent(type, info); +} + +void +CMSWindowsScreen::handleSystemEvent(const CEvent& event, void*) +{ + MSG* msg = reinterpret_cast(event.getData()); + assert(msg != NULL); + + if (CArchMiscWindows::processDialog(msg)) { + return; } + if (onPreDispatch(msg->hwnd, msg->message, msg->wParam, msg->lParam)) { + return; + } + TranslateMessage(msg); + DispatchMessage(msg); } bool @@ -1002,9 +804,6 @@ CMSWindowsScreen::onPreDispatch(HWND hwnd, switch (message) { case SYNERGY_MSG_SCREEN_SAVER: return onScreensaver(wParam != 0); - - case WM_TIMER: - return onTimer(static_cast(wParam)); } if (m_isPrimary) { @@ -1050,7 +849,7 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, KeyID key = m_keyMapper.mapKeyFromEvent(wParam, lParam, &mask, NULL); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_primaryReceiver->onKeyUp(key, mask, button); + sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); m_keyMapper.updateKey(static_cast(wParam), false); } if (m_keyMapper.isPressed(VK_RWIN) && @@ -1068,7 +867,7 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, KeyID key = m_keyMapper.mapKeyFromEvent(wParam, lParam, &mask, NULL); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_primaryReceiver->onKeyUp(key, mask, button); + sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); m_keyMapper.updateKey(static_cast(wParam), false); } } @@ -1119,10 +918,10 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, } bool -CMSWindowsScreen::onEvent(HWND hwnd, - UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result) +CMSWindowsScreen::onEvent(HWND, UINT msg, + WPARAM wParam, LPARAM lParam, LRESULT* result) { - switch (message) { + switch (msg) { case WM_QUERYENDSESSION: if (m_is95Family) { *result = TRUE; @@ -1133,22 +932,18 @@ CMSWindowsScreen::onEvent(HWND hwnd, case WM_ENDSESSION: if (m_is95Family) { if (wParam == TRUE && lParam == 0) { - exitMainLoop(); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } return true; } break; - case WM_PAINT: - ValidateRect(hwnd, NULL); - return true; - case WM_DRAWCLIPBOARD: LOG((CLOG_DEBUG "clipboard was taken")); // first pass on the message if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, message, wParam, lParam); + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); } // now handle the message @@ -1160,16 +955,13 @@ CMSWindowsScreen::onEvent(HWND hwnd, LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow)); } else if (m_nextClipboardWindow != NULL) { - LOG((CLOG_DEBUG "clipboard chain: forward: %d 0x%08x 0x%08x", message, wParam, lParam)); - SendMessage(m_nextClipboardWindow, message, wParam, lParam); + LOG((CLOG_DEBUG "clipboard chain: forward: %d 0x%08x 0x%08x", msg, wParam, lParam)); + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); } return true; case WM_DISPLAYCHANGE: return onDisplayChange(); - - case WM_ACTIVATEAPP: - return onActivate(wParam != FALSE); } return false; @@ -1234,7 +1026,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyUp(key, mask2, button); + sendEvent(getKeyUpEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); } if (ctrlR) { key = kKeyControl_R; @@ -1242,7 +1035,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyUp(key, mask2, button); + sendEvent(getKeyUpEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); } if (altL) { key = kKeyAlt_L; @@ -1250,7 +1044,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyUp(key, mask2, button); + sendEvent(getKeyUpEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); } if (altR) { key = kKeyAlt_R; @@ -1258,7 +1053,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyUp(key, mask2, button); + sendEvent(getKeyUpEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); } } @@ -1267,14 +1063,16 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) SInt32 repeat = (SInt32)(lParam & 0xffff); if (!wasDown) { LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_primaryReceiver->onKeyDown(key, mask, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask, button, 1)); if (repeat > 0) { --repeat; } } if (repeat >= 1) { LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button)); - m_primaryReceiver->onKeyRepeat(key, mask, repeat, button); + sendEvent(getKeyRepeatEvent(), + CKeyInfo::alloc(key, mask, button, repeat)); } // restore ctrl and alt state @@ -1292,7 +1090,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyDown(key, mask2, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); mask2 |= KeyModifierControl; } if (ctrlR) { @@ -1301,7 +1100,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyDown(key, mask2, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); mask2 |= KeyModifierControl; } if (altL) { @@ -1310,7 +1110,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyDown(key, mask2, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); mask2 |= KeyModifierAlt; } if (altR) { @@ -1319,7 +1120,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyDown(key, mask2, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); mask2 |= KeyModifierAlt; } } @@ -1334,13 +1136,15 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) if (m_is95Family && !isModifier(wParam) && m_keyMapper.isPressed(static_cast(wParam))) { LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_primaryReceiver->onKeyDown(key, mask, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask, button, 1)); m_keyMapper.updateKey(static_cast(wParam), true); } // do key up LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_primaryReceiver->onKeyUp(key, mask, button); + sendEvent(getKeyUpEvent(), + CKeyInfo::alloc(key, mask, button, 1)); } } else { @@ -1383,7 +1187,7 @@ CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) case WM_NCXBUTTONDBLCLK: LOG((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { - m_primaryReceiver->onMouseDown(button); + sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); } pressed = true; break; @@ -1398,7 +1202,7 @@ CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) case WM_NCXBUTTONUP: LOG((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { - m_primaryReceiver->onMouseUp(button); + sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); } pressed = false; break; @@ -1438,7 +1242,8 @@ CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) if (m_isOnScreen) { // motion on primary screen - m_primaryReceiver->onMouseMovePrimary(m_xCursor, m_yCursor); + sendEvent(getMotionOnPrimaryEvent(), + CMotionInfo::alloc(m_xCursor, m_yCursor)); } else { // motion on secondary screen. warp mouse back to @@ -1459,7 +1264,7 @@ CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) } else { // send motion - m_primaryReceiver->onMouseMoveSecondary(x, y); + sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y)); } } @@ -1472,7 +1277,7 @@ CMSWindowsScreen::onMouseWheel(SInt32 delta) // ignore message if posted prior to last mark change if (!ignore()) { LOG((CLOG_DEBUG1 "event: button wheel delta=%d", delta)); - m_primaryReceiver->onMouseWheel(delta); + sendEvent(getWheelEvent(), CWheelInfo::alloc(delta)); } return true; } @@ -1496,55 +1301,11 @@ CMSWindowsScreen::onScreensaver(bool activated) if (activated) { if (m_screensaver->checkStarted(SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) { - m_primaryReceiver->onScreensaver(true); + sendEvent(getScreensaverActivatedEvent()); } } else { - m_primaryReceiver->onScreensaver(false); - } - - return true; -} - -bool -CMSWindowsScreen::onTimer(UINT timerID) -{ - if (timerID == m_timer) { - // if current desktop is not the input desktop then switch to it. - HDESK desk = CMSWindowsDesktop::openInputDesktop(); - if (desk == m_desk || - CMSWindowsDesktop::getDesktopName(desk) == m_deskName || - m_screensaver->isActive()) { - // same desktop or screensaver is active. don't switch - // desktops when the screensaver is active. we'd most - // likely switch to the screensaver desktop which would - // have the side effect of forcing the screensaver to stop. - CMSWindowsDesktop::closeDesktop(desk); - } - else { - switchDesktop(desk); - } - - // if the desktop was inaccessible and isn't anymore then - // update our key state. - if (desk != NULL && m_inaccessibleDesktop) { - LOG((CLOG_DEBUG "desktop is now accessible")); - m_inaccessibleDesktop = false; - updateKeys(); - } - - // note if desktop was accessible but isn't anymore - else if (desk == NULL && !m_inaccessibleDesktop) { - m_inaccessibleDesktop = true; - LOG((CLOG_DEBUG "desktop is now inaccessible")); - } - } - - else if (timerID == m_oneShotTimer) { - // one shot timer expired - KillTimer(NULL, m_oneShotTimer); - m_oneShotTimer = 0; - m_primaryReceiver->onOneShotTimerExpired(0); + sendEvent(getScreensaverDeactivatedEvent()); } return true; @@ -1573,17 +1334,8 @@ CMSWindowsScreen::onDisplayChange() } } - // collect new screen info - CClientInfo info; - info.m_x = m_x; - info.m_y = m_y; - info.m_w = m_w; - info.m_h = m_h; - info.m_zoneSize = getJumpZoneSize(); - getCursorPos(info.m_mx, info.m_my); - // send new screen info - m_receiver->onInfoChanged(info); + sendEvent(getShapeChangedEvent()); } return true; @@ -1599,8 +1351,8 @@ CMSWindowsScreen::onClipboardChange() if (m_ownClipboard) { LOG((CLOG_DEBUG "clipboard changed: lost ownership")); m_ownClipboard = false; - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); } } else { @@ -1611,22 +1363,11 @@ CMSWindowsScreen::onClipboardChange() return true; } -bool -CMSWindowsScreen::onActivate(bool activated) -{ - if (!m_isPrimary && activated) { - // some other app activated. hide the hider window. - ShowWindow(m_window, SW_HIDE); - } - - return false; -} - void CMSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) { // send an event that we can recognize before the mouse warp - PostThreadMessage(m_threadID, SYNERGY_MSG_PRE_WARP, x, y); + PostThreadMessage(GetCurrentThreadId(), SYNERGY_MSG_PRE_WARP, x, y); // warp mouse. hopefully this inserts a mouse motion event // between the previous message and the following message. @@ -1651,7 +1392,7 @@ CMSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) ARCH->sleep(0.0); // send an event that we can recognize after the mouse warp - PostThreadMessage(m_threadID, SYNERGY_MSG_POST_WARP, 0, 0); + PostThreadMessage(GetCurrentThreadId(), SYNERGY_MSG_POST_WARP, 0, 0); } void @@ -1661,7 +1402,7 @@ CMSWindowsScreen::nextMark() ++m_mark; // mark point in message queue where the mark was changed - PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); + PostThreadMessage(GetCurrentThreadId(), SYNERGY_MSG_MARK, m_mark, 0); } bool @@ -1670,34 +1411,23 @@ CMSWindowsScreen::ignore() const return (m_mark != m_markReceived); } -HCURSOR -CMSWindowsScreen::createBlankCursor() const -{ - // create a transparent cursor - int cw = GetSystemMetrics(SM_CXCURSOR); - int ch = GetSystemMetrics(SM_CYCURSOR); - UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; - UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; - memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); - memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); - HCURSOR c = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); - delete[] cursorXOR; - delete[] cursorAND; - return c; -} - void -CMSWindowsScreen::showCursor(bool show) const +CMSWindowsScreen::updateScreenShape() { - if (m_cursorThread != 0) { - if (m_threadID != m_cursorThread) { - AttachThreadInput(m_threadID, m_cursorThread, TRUE); - } - ShowCursor(show ? TRUE : FALSE); - if (m_threadID != m_cursorThread) { - AttachThreadInput(m_threadID, m_cursorThread, FALSE); - } - } + // get shape + m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); + m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); + m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); + + // get center for cursor + m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; + m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; + + // check for multiple monitors + m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || + m_h != GetSystemMetrics(SM_CYSCREEN)); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "")); } void @@ -1867,3 +1597,417 @@ CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return result; } + +LRESULT CALLBACK +CMSWindowsScreen::primaryDeskProc( + HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + // FIXME + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +LRESULT CALLBACK +CMSWindowsScreen::secondaryDeskProc( + HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_MOUSEMOVE: + case WM_ACTIVATE: + case WM_ACTIVATEAPP: + // hide window + ShowWindow(hwnd, SW_HIDE); + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void +CMSWindowsScreen::deskMouseMove(SInt32 x, SInt32 y) const +{ + // motion is simple (i.e. it's on the primary monitor) if there + // is only one monitor. + bool simple = !m_multimon; + if (!simple) { + // also simple if motion is within the primary monitor + simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && + y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); + } + + // move the mouse directly to target position if motion is simple + if (simple) { + // when using absolute positioning with mouse_event(), + // the normalized device coordinates range over only + // the primary screen. + SInt32 w = GetSystemMetrics(SM_CXSCREEN); + SInt32 h = GetSystemMetrics(SM_CYSCREEN); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65536.0 * x) / w), + (DWORD)((65536.0 * y) / h), + 0, 0); + } + + // windows 98 (and Me?) is broken. you cannot set the absolute + // position of the mouse except on the primary monitor but you + // can do relative moves onto any monitor. this is, in microsoft's + // words, "by design." apparently the designers of windows 2000 + // we're a little less lazy and did it right. + // + // microsoft recommends in Q193003 to absolute position the cursor + // somewhere on the primary monitor then relative move to the + // desired location. this doesn't work for us because when the + // user drags a scrollbar, a window, etc. it causes the dragged + // item to jump back and forth between the position on the primary + // monitor and the desired position. while it always ends up in + // the right place, the effect is disconcerting. + // + // instead we'll get the cursor's current position and do just a + // relative move from there to the desired position. relative + // moves are subject to cursor acceleration which we don't want. + // so we disable acceleration, do the relative move, then restore + // acceleration. there's a slight chance we'll end up in the + // wrong place if the user moves the cursor using this system's + // mouse while simultaneously moving the mouse on the server + // system. that defeats the purpose of synergy so we'll assume + // that won't happen. even if it does, the next mouse move will + // correct the position. + else { + // save mouse speed & acceleration + int oldSpeed[4]; + bool accelChanged = + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && + SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); + + // use 1:1 motion + if (accelChanged) { + int newSpeed[4] = { 0, 0, 0, 1 }; + accelChanged = + SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || + SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); + } + + // move relative to mouse position + POINT pos; + GetCursorPos(&pos); + mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); + + // restore mouse speed & acceleration + if (accelChanged) { + SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); + SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); + } + } +} + +void +CMSWindowsScreen::deskEnter(CDesk* desk, DWORD& cursorThreadID) +{ + if (m_isPrimary) { + if (desk->m_lowLevel) { + if (cursorThreadID != 0) { + AttachThreadInput(desk->m_threadID, cursorThreadID, TRUE); + ShowCursor(TRUE); + AttachThreadInput(desk->m_threadID, cursorThreadID, FALSE); + cursorThreadID = 0; + } + } + } + ShowWindow(desk->m_window, SW_HIDE); +} + +void +CMSWindowsScreen::deskLeave(CDesk* desk, DWORD& cursorThreadID) +{ + if (m_isPrimary) { + // we don't need a window to capture input but we need a window + // to hide the cursor when using low-level hooks. also take the + // activation so we use our keyboard layout, not the layout of + // whatever window was active. + if (desk->m_lowLevel) { + SetWindowPos(desk->m_window, HWND_TOPMOST, + m_xCenter, m_yCenter, 1, 1, SWP_NOACTIVATE); + if (cursorThreadID == 0) { + HWND hwnd = GetForegroundWindow(); + cursorThreadID = GetWindowThreadProcessId(hwnd, NULL); + if (cursorThreadID != 0) { + AttachThreadInput(desk->m_threadID, cursorThreadID, TRUE); + ShowCursor(FALSE); + AttachThreadInput(desk->m_threadID, cursorThreadID, FALSE); + } + } + } + } + else { + // move hider window under the cursor center + MoveWindow(desk->m_window, m_xCenter, m_yCenter, 1, 1, FALSE); + + // raise and show the hider window + ShowWindow(desk->m_window, SW_SHOWNA); + + // warp the mouse to the cursor center + deskMouseMove(m_xCenter, m_yCenter); + } +} + +void +CMSWindowsScreen::deskThread(void* vdesk) +{ + MSG msg; + + // id of thread that had cursor when we were last told to hide it + DWORD cursorThreadID = 0; + + // use given desktop for this thread + CDesk* desk = reinterpret_cast(vdesk); + desk->m_threadID = GetCurrentThreadId(); + desk->m_window = NULL; + if (desk->m_desk != NULL && SetThreadDesktop(desk->m_desk) != 0) { + // create a message queue + PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE); + + // create a window. we use this window to hide the cursor. + try { + desk->m_window = createWindow(m_deskClass, "SynergyDesk"); + LOG((CLOG_DEBUG "desk %s window is 0x%08x", desk->m_name.c_str(), desk->m_window)); + } + catch (...) { + // ignore + LOG((CLOG_DEBUG "can't create desk window for %s", desk->m_name.c_str())); + } + + // a window on the primary screen should never activate + if (m_isPrimary && desk->m_window != NULL) { + EnableWindow(desk->m_window, FALSE); + } + } + + // tell main thread that we're ready + { + CLock lock(&m_mutex); + m_deskReady = true; + m_deskReady.broadcast(); + } + + while (GetMessage(&msg, NULL, 0, 0)) { + switch (msg.message) { + default: + TranslateMessage(&msg); + DispatchMessage(&msg); + continue; + + case SYNERGY_MSG_SWITCH: + if (m_isPrimary) { + m_uninstall(); + if (m_screensaverNotify) { + m_uninstallScreensaver(); + m_installScreensaver(); + } + switch (m_install()) { + case kHOOK_FAILED: + // we won't work on this desk + desk->m_lowLevel = false; + break; + + case kHOOK_OKAY: + desk->m_lowLevel = false; + break; + + case kHOOK_OKAY_LL: + desk->m_lowLevel = true; + break; + } + } + break; + + case SYNERGY_MSG_ENTER: + deskEnter(desk, cursorThreadID); + break; + + case SYNERGY_MSG_LEAVE: + deskLeave(desk, cursorThreadID); + break; + + case SYNERGY_MSG_FAKE_KEY: + keybd_event(HIBYTE(msg.lParam), LOBYTE(msg.lParam), msg.wParam, 0); + break; + + case SYNERGY_MSG_FAKE_BUTTON: + if (msg.wParam != 0) { + mouse_event(msg.wParam, 0, 0, msg.lParam, 0); + } + break; + + case SYNERGY_MSG_FAKE_MOVE: + deskMouseMove(static_cast(msg.wParam), + static_cast(msg.lParam)); + break; + + case SYNERGY_MSG_FAKE_WHEEL: + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, msg.wParam, 0); + break; + + case SYNERGY_MSG_CURSOR_POS: { + POINT* pos = reinterpret_cast(msg.wParam); + if (!GetCursorPos(pos)) { + pos->x = m_xCenter; + pos->y = m_yCenter; + } + break; + } + + case SYNERGY_MSG_SYNC_KEYS: + m_keyMapper.update(m_keyState); + break; + } + + // notify that message was processed + CLock lock(&m_mutex); + m_deskReady = true; + m_deskReady.broadcast(); + } + + // clean up + if (desk->m_window != NULL) { + DestroyWindow(desk->m_window); + } + if (desk->m_desk != NULL) { + CMSWindowsDesktop::closeDesktop(desk->m_desk); + } +} + +CMSWindowsScreen::CDesk* +CMSWindowsScreen::addDesk(const CString& name, HDESK hdesk) +{ + CDesk* desk = new CDesk; + desk->m_name = name; + desk->m_desk = hdesk; + desk->m_targetID = GetCurrentThreadId(); + desk->m_thread = new CThread(new TMethodJob( + this, &CMSWindowsScreen::deskThread, desk)); + waitForDesk(); + m_desks.insert(std::make_pair(name, desk)); + return desk; +} + +void +CMSWindowsScreen::removeDesks() +{ + for (CDesks::iterator index = m_desks.begin(); + index != m_desks.end(); ++index) { + CDesk* desk = index->second; + PostThreadMessage(desk->m_threadID, WM_QUIT, 0, 0); + desk->m_thread->wait(); + delete desk->m_thread; + delete desk; + } + m_desks.clear(); +} + +void +CMSWindowsScreen::checkDesk() +{ + // get current desktop. if we already know about it then return. + CDesk* desk; + HDESK hdesk = CMSWindowsDesktop::openInputDesktop(); + CString name = CMSWindowsDesktop::getDesktopName(hdesk); + CDesks::const_iterator index = m_desks.find(name); + if (index == m_desks.end()) { + desk = addDesk(name, hdesk); + // hold on to hdesk until thread exits so the desk can't + // be removed by the system + } + else { + CMSWindowsDesktop::closeDesktop(hdesk); + desk = index->second; + } + + // if active desktop changed then tell the old and new desk threads + // about the change. don't switch desktops when the screensaver is + // active becaue we'd most likely switch to the screensaver desktop + // which would have the side effect of forcing the screensaver to + // stop. + // FIXME -- really not switch if screensaver is active? + if (name != m_activeDeskName && !m_screensaver->isActive()) { + // show cursor on previous desk + if (!m_isOnScreen) { + sendDeskMessage(SYNERGY_MSG_ENTER, 0, 0); + } + + // check for desk accessibility change. we don't get events + // from an inaccessible desktop so when we switch from an + // inaccessible desktop to an accessible one we have to + // update the keyboard state. + LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str())); + bool isAccessible = isDeskAccessible(desk); + if (isDeskAccessible(m_activeDesk) != isAccessible) { + if (isAccessible) { + LOG((CLOG_DEBUG "desktop is now accessible")); + updateKeys(); + } + else { + LOG((CLOG_DEBUG "desktop is now inaccessible")); + } + } + + // switch desk + m_activeDesk = desk; + m_activeDeskName = name; + sendDeskMessage(SYNERGY_MSG_SWITCH, 0, 0); + + // hide cursor on new desk + if (!m_isOnScreen) { + sendDeskMessage(SYNERGY_MSG_LEAVE, 0, 0); + } + } +} + +// FIXME -- may want some of following when we switch desks. calling +// nextMark() for isPrimary may lead to loss of events. updateKeys() +// is to catch any key events lost between switching and detecting the +// switch. neither are strictly necessary. +/* + if (m_isPrimary) { + if (m_isOnScreen) { + // all messages prior to now are invalid + // FIXME -- is this necessary; couldn't we lose key releases? + nextMark(); + } + } + else { + // update key state + updateKeys(); + } +*/ + +bool +CMSWindowsScreen::isDeskAccessible(const CDesk* desk) const +{ + return (desk != NULL && desk->m_desk != NULL); +} + +void +CMSWindowsScreen::sendDeskMessage(UINT msg, WPARAM wParam, LPARAM lParam) const +{ + if (m_activeDesk != NULL && m_activeDesk->m_window != NULL) { + PostThreadMessage(m_activeDesk->m_threadID, msg, wParam, lParam); + waitForDesk(); + } +} + +void +CMSWindowsScreen::waitForDesk() const +{ + CMSWindowsScreen* self = const_cast(this); + + CLock lock(&m_mutex); + while (!(bool)m_deskReady) { + m_deskReady.wait(); + } + self->m_deskReady = false; +} + +void +CMSWindowsScreen::handleCheckDesk(const CEvent&, void*) +{ + checkDesk(); +} diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 29be457b..ddae1f02 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -18,19 +18,20 @@ #include "IPlatformScreen.h" #include "CMSWindowsKeyMapper.h" #include "CSynergyHook.h" +#include "CCondVar.h" #include "CMutex.h" #include "CString.h" #define WIN32_LEAN_AND_MEAN #include +class CEventQueueTimer; class CMSWindowsScreenSaver; -class IScreenReceiver; -class IPrimaryScreenReceiver; +class CThread; //! Implementation of IPlatformScreen for Microsoft Windows class CMSWindowsScreen : public IPlatformScreen { public: - CMSWindowsScreen(IScreenReceiver*, IPrimaryScreenReceiver*); + CMSWindowsScreen(bool isPrimary); virtual ~CMSWindowsScreen(); //! @name manipulators @@ -56,12 +57,9 @@ public: //@} // IPlatformScreen overrides - virtual void open(IKeyState*); - virtual void close(); + virtual void setKeyState(IKeyState*); virtual void enable(); virtual void disable(); - virtual void mainLoop(); - virtual void exitMainLoop(); virtual void enter(); virtual bool leave(); virtual bool setClipboard(ClipboardID, const IClipboard*); @@ -72,17 +70,22 @@ public: virtual void resetOptions(); virtual void setOptions(const COptionsList& options); virtual void updateKeys(); + virtual void setSequenceNumber(UInt32); virtual bool isPrimary() const; - virtual bool getClipboard(ClipboardID, IClipboard*) const; - virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - virtual void getCursorPos(SInt32&, SInt32&) const; + + // IScreen overrides + virtual void* getEventTarget() const; + virtual bool getClipboard(ClipboardID id, IClipboard*) const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); - virtual UInt32 addOneShotTimer(double timeout); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; virtual const char* getKeyName(KeyButton) const; // ISecondaryScreen overrides @@ -97,17 +100,31 @@ public: bool isAutoRepeat) const; private: - // update screen size cache - void updateScreenShape(); + class CDesk { + public: + CString m_name; + CThread* m_thread; + DWORD m_threadID; + DWORD m_targetID; + HDESK m_desk; + HWND m_window; + bool m_lowLevel; + }; + typedef std::map CDesks; - // switch to the given desktop. this destroys the window and unhooks - // all hooks, switches the desktop, then creates the window and rehooks - // all hooks (because you can't switch the thread's desktop if it has - // any windows or hooks). - bool switchDesktop(HDESK desk); - - // make sure we're on the expected desktop - void syncDesktop() const; +// FIXME -- comment + HINSTANCE openHookLibrary(const char* name); + void closeHookLibrary(HINSTANCE hookLibrary) const; + HCURSOR createBlankCursor() const; + void destroyCursor(HCURSOR cursor) const; + ATOM createWindowClass() const; + ATOM createDeskWindowClass(bool isPrimary) const; + void destroyClass(ATOM windowClass) const; + HWND createWindow(ATOM windowClass, const char* name) const; + void destroyWindow(HWND) const; + void sendEvent(CEvent::Type type, void* = NULL); + void sendClipboardEvent(CEvent::Type type, ClipboardID id); + void handleSystemEvent(const CEvent& event, void*); // handle message before it gets dispatched. returns true iff // the message should not be dispatched. @@ -128,10 +145,8 @@ private: bool onMouseMove(SInt32 x, SInt32 y); bool onMouseWheel(SInt32 delta); bool onScreensaver(bool activated); - bool onTimer(UINT timerID); bool onDisplayChange(); bool onClipboardChange(); - bool onActivate(bool activated); // XXX // warp cursor without discarding queued events @@ -144,11 +159,8 @@ private: bool ignore() const; // XXX - // create the transparent cursor - HCURSOR createBlankCursor() const; - - // show/hide the cursor - void showCursor(bool) const; + // update screen size cache + void updateScreenShape(); // enable/disable special key combinations so we can catch/pass them void enableSpecialKeys(bool) const; @@ -169,6 +181,22 @@ private: // our window proc static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); + // our desk window procs + static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM); + + void deskMouseMove(SInt32 x, SInt32 y) const; + void deskEnter(CDesk* desk, DWORD& cursorThreadID); + void deskLeave(CDesk* desk, DWORD& cursorThreadID); + void deskThread(void* vdesk); + CDesk* addDesk(const CString& name, HDESK hdesk); + void removeDesks(); + void checkDesk(); + bool isDeskAccessible(const CDesk* desk) const; + void sendDeskMessage(UINT, WPARAM, LPARAM) const; + void waitForDesk() const; + void handleCheckDesk(const CEvent& event, void*); + private: static HINSTANCE s_instance; @@ -178,17 +206,13 @@ private: // true if windows 95/98/me bool m_is95Family; - // receivers - IScreenReceiver* m_receiver; - IPrimaryScreenReceiver* m_primaryReceiver; - // true if mouse has entered the screen bool m_isOnScreen; // our resources ATOM m_class; + ATOM m_deskClass; HCURSOR m_cursor; - HWND m_window; // screen shape stuff SInt32 m_x, m_y; @@ -201,6 +225,8 @@ private: // last mouse position SInt32 m_xCursor, m_yCursor; + UInt32 m_sequenceNumber; + // used to discard queued messages that are no longer needed UInt32 m_mark; UInt32 m_markReceived; @@ -208,33 +234,27 @@ private: // the main loop's thread id DWORD m_threadID; - // the thread id of the last attached thread - DWORD m_lastThreadID; - // the timer used to check for desktop switching - UINT m_timer; - - // the one shot timer - UINT m_oneShotTimer; + CEventQueueTimer* m_timer; // screen saver stuff CMSWindowsScreenSaver* m_screensaver; bool m_screensaverNotify; - // clipboard stuff + // clipboard stuff. our window is used mainly as a clipboard + // owner and as a link in the clipboard viewer chain. + HWND m_window; HWND m_nextClipboardWindow; bool m_ownClipboard; // the current desk and it's name - HDESK m_desk; - CString m_deskName; + CDesk* m_activeDesk; + CString m_activeDeskName; - // true when the current desktop is inaccessible. while - // the desktop is inaccessible we won't receive user input - // and we'll lose track of the keyboard state. when the - // desktop becomes accessible again we'll notify the event - // handler of that. - bool m_inaccessibleDesktop; + // one desk per desktop and a cond var to communicate with it + CMutex m_mutex; + CCondVar m_deskReady; + CDesks m_desks; // hook library stuff HINSTANCE m_hookLibrary; @@ -247,7 +267,6 @@ private: SetModeFunc m_setMode; InstallScreenSaverFunc m_installScreensaver; UninstallScreenSaverFunc m_uninstallScreensaver; - bool m_lowLevel; // keyboard stuff IKeyState* m_keyState; @@ -256,9 +275,6 @@ private: // map of button state BYTE m_buttons[1 + kButtonExtra0 + 1]; - // stuff for hiding the cursor - DWORD m_cursorThread; - static CMSWindowsScreen* s_screen; }; diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 33022070..4b58ac8d 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -76,13 +76,10 @@ static DWORD g_threadID = 0; static HHOOK g_keyboard = NULL; static HHOOK g_mouse = NULL; static HHOOK g_getMessage = NULL; -static HANDLE g_hookThreadLL = NULL; -static DWORD g_hookThreadIDLL = 0; -static HANDLE g_hookEventLL = NULL; static HHOOK g_keyboardLL = NULL; static HHOOK g_mouseLL = NULL; static bool g_screenSaver = false; -static EHookMode g_mode = kHOOK_WATCH_JUMP_ZONE; +static EHookMode g_mode = kHOOK_DISABLE; static UInt32 g_zoneSides = 0; static SInt32 g_zoneSize = 0; static SInt32 g_xScreen = 0; @@ -141,6 +138,7 @@ restoreCursor() g_cursorThread = 0; } +#if !NO_GRAB_KEYBOARD static bool keyboardHookHandler(WPARAM wParam, LPARAM lParam) @@ -170,6 +168,7 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) return false; } +#endif static bool @@ -231,7 +230,7 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); return true; } - else { + else if (g_mode == kHOOK_WATCH_JUMP_ZONE) { // check for mouse inside jump zone bool inside = false; if (!inside && (g_zoneSides & kLeftMask) != 0) { @@ -259,6 +258,7 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) return false; } +#if !NO_GRAB_KEYBOARD static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) @@ -272,6 +272,7 @@ keyboardHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_keyboard, code, wParam, lParam); } +#endif static LRESULT CALLBACK @@ -354,6 +355,7 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam) // side, key repeats are not reported to us. // +#if !NO_GRAB_KEYBOARD static LRESULT CALLBACK keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) @@ -385,6 +387,7 @@ keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_keyboardLL, code, wParam, lParam); } +#endif // // low-level mouse hook -- this allows us to capture and handle mouse @@ -411,92 +414,6 @@ mouseLLHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_mouseLL, code, wParam, lParam); } -static -DWORD WINAPI -getLowLevelProc(void*) -{ - // thread proc for low-level keyboard/mouse hooks. this does - // nothing but install the hook, process events, and uninstall - // the hook. - - // force this thread to have a message queue - MSG msg; - PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); - -#if !NO_GRAB_KEYBOARD - // install low-level keyboard hook - g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, - &keyboardLLHook, - g_hinstance, - 0); - if (g_keyboardLL == NULL) { - // indicate failure and exit - g_hookThreadIDLL = 0; - SetEvent(g_hookEventLL); - return 1; - } -#else - // keep compiler quiet - &keyboardLLHook; -#endif - - // install low-level mouse hook - g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL, - &mouseLLHook, - g_hinstance, - 0); - if (g_mouseLL == NULL) { - // indicate failure and exit - if (g_keyboardLL != NULL) { - UnhookWindowsHookEx(g_keyboardLL); - g_keyboardLL = NULL; - } - g_hookThreadIDLL = 0; - SetEvent(g_hookEventLL); - return 1; - } - - // ready - SetEvent(g_hookEventLL); - - // message loop - bool done = false; - while (!done) { - switch (GetMessage(&msg, NULL, 0, 0)) { - case -1: - break; - - case 0: - done = true; - break; - - default: - TranslateMessage(&msg); - DispatchMessage(&msg); - break; - } - } - - // uninstall hook - UnhookWindowsHookEx(g_mouseLL); - UnhookWindowsHookEx(g_keyboardLL); - g_mouseLL = NULL; - g_keyboardLL = NULL; - - return 0; -} - -#else // (_WIN32_WINNT < 0x0400) - -static -DWORD WINAPI -getLowLevelProc(void*) -{ - g_hookThreadIDLL = 0; - SetEvent(g_hookEventLL); - return 1; -} - #endif static @@ -559,12 +476,8 @@ DllMain(HINSTANCE instance, DWORD reason, LPVOID) } else if (reason == DLL_PROCESS_DETACH) { if (g_processID == GetCurrentProcessId()) { - if (g_keyboard != NULL || - g_mouse != NULL || - g_getMessage != NULL) { - uninstall(); - uninstallScreenSaver(); - } + uninstall(); + uninstallScreenSaver(); g_processID = 0; g_hinstance = NULL; } @@ -601,9 +514,6 @@ init(DWORD threadID) g_keyboard = NULL; g_mouse = NULL; g_getMessage = NULL; - g_hookThreadLL = NULL; - g_hookThreadIDLL = 0; - g_hookEventLL = NULL; g_keyboardLL = NULL; g_mouseLL = NULL; g_screenSaver = false; @@ -614,7 +524,7 @@ init(DWORD threadID) g_threadID = threadID; // set defaults - g_mode = kHOOK_WATCH_JUMP_ZONE; + g_mode = kHOOK_DISABLE; g_zoneSides = 0; g_zoneSize = 0; g_xScreen = 0; @@ -663,75 +573,51 @@ install() 0); } - // install low-level keyboard/mouse hooks, if possible. since these - // hooks are called in the context of the installing thread and that - // thread must have a message loop but we don't want the caller's - // message loop to do the work, we'll fire up a separate thread - // just for the hooks. note that low-level hooks are only available - // on windows NT SP3 and above. - g_hookEventLL = CreateEvent(NULL, TRUE, FALSE, NULL); - if (g_hookEventLL != NULL) { - g_hookThreadLL = CreateThread(NULL, 0, &getLowLevelProc, 0, - CREATE_SUSPENDED, &g_hookThreadIDLL); - if (g_hookThreadLL != NULL) { - // start the thread and wait for it to initialize - ResumeThread(g_hookThreadLL); - WaitForSingleObject(g_hookEventLL, INFINITE); - ResetEvent(g_hookEventLL); - - // the thread clears g_hookThreadIDLL if it failed - if (g_hookThreadIDLL == 0) { - CloseHandle(g_hookThreadLL); - g_hookThreadLL = NULL; - } - } - if (g_hookThreadLL == NULL) { - CloseHandle(g_hookEventLL); - g_hookEventLL = NULL; - } - } - - // install non-low-level hooks if the low-level hooks are not installed - if (g_hookThreadLL == NULL) { + // install keyboard hook #if !NO_GRAB_KEYBOARD +#if (_WIN32_WINNT >= 0x0400) + g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, + &keyboardLLHook, + g_hinstance, + 0); +#endif + if (g_keyboardLL == NULL) { g_keyboard = SetWindowsHookEx(WH_KEYBOARD, &keyboardHook, g_hinstance, 0); -#else - // keep compiler quiet - &keyboardHook; + } #endif + + // install mouse hook +#if (_WIN32_WINNT >= 0x0400) + g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL, + &mouseLLHook, + g_hinstance, + 0); +#endif + if (g_mouseLL == NULL) { g_mouse = SetWindowsHookEx(WH_MOUSE, &mouseHook, g_hinstance, 0); } - // check for any failures. uninstall all hooks on failure. - if (g_hookThreadLL == NULL && + // check that we got all the hooks we wanted + if ((g_getMessage == NULL && g_wheelSupport == kWheelOld) || #if !NO_GRAB_KEYBOARD - (g_keyboard == NULL || g_mouse == NULL)) { -#else - (g_mouse == NULL)) { + (g_keyboardLL == NULL && g_keyboard == NULL) || #endif - if (g_keyboard != NULL) { - UnhookWindowsHookEx(g_keyboard); - g_keyboard = NULL; - } - if (g_mouse != NULL) { - UnhookWindowsHookEx(g_mouse); - g_mouse = NULL; - } - if (g_getMessage != NULL && !g_screenSaver) { - UnhookWindowsHookEx(g_getMessage); - g_getMessage = NULL; - } - g_threadID = NULL; + (g_mouseLL == NULL && g_mouse == NULL)) { + uninstall(); return kHOOK_FAILED; } - return (g_hookThreadLL == NULL) ? kHOOK_OKAY : kHOOK_OKAY_LL; + if (g_keyboardLL != NULL || g_mouseLL != NULL) { + return kHOOK_OKAY_LL; + } + + return kHOOK_OKAY; } int @@ -740,14 +626,13 @@ uninstall(void) assert(g_hinstance != NULL); // uninstall hooks - if (g_hookThreadLL != NULL) { - PostThreadMessage(g_hookThreadIDLL, WM_QUIT, 0, 0); - WaitForSingleObject(g_hookThreadLL, INFINITE); - CloseHandle(g_hookEventLL); - CloseHandle(g_hookThreadLL); - g_hookEventLL = NULL; - g_hookThreadLL = NULL; - g_hookThreadIDLL = 0; + if (g_keyboardLL != NULL) { + UnhookWindowsHookEx(g_keyboardLL); + g_keyboardLL = NULL; + } + if (g_mouseLL != NULL) { + UnhookWindowsHookEx(g_mouseLL); + g_mouseLL = NULL; } if (g_keyboard != NULL) { UnhookWindowsHookEx(g_keyboard); diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h index 6953fd70..a9ab2b0c 100644 --- a/lib/platform/CSynergyHook.h +++ b/lib/platform/CSynergyHook.h @@ -40,6 +40,7 @@ #define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; #define SYNERGY_MSG_INPUT_FIRST SYNERGY_MSG_KEY #define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP +#define SYNERGY_HOOK_LAST_MSG SYNERGY_MSG_SCREEN_SAVER extern "C" { @@ -50,6 +51,7 @@ enum EHookResult { }; enum EHookMode { + kHOOK_DISABLE, kHOOK_WATCH_JUMP_ZONE, kHOOK_RELAY_EVENTS }; diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 0606c3ef..52140624 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -740,7 +740,7 @@ CXWindowsClipboard::motifFillCache() // save it motifFormats.insert(std::make_pair(motifFormat->m_type, data)); } - const UInt32 numMotifFormats = motifFormats.size(); + //const UInt32 numMotifFormats = motifFormats.size(); // try each converter in order (because they're in order of // preference). diff --git a/lib/platform/CXWindowsEventQueueBuffer.cpp b/lib/platform/CXWindowsEventQueueBuffer.cpp index 7592bc89..ea450e17 100644 --- a/lib/platform/CXWindowsEventQueueBuffer.cpp +++ b/lib/platform/CXWindowsEventQueueBuffer.cpp @@ -143,6 +143,7 @@ CXWindowsEventQueueBuffer::addEvent(UInt32 dataID) // force waitForEvent() to return XFlush(m_display); + return true; } bool @@ -154,7 +155,7 @@ CXWindowsEventQueueBuffer::isEmpty() const CEventQueueTimer* CXWindowsEventQueueBuffer::newTimer(double, bool) const { - return new CEventQueueTimer(); + return new CEventQueueTimer; } void diff --git a/lib/platform/CXWindowsKeyMapper.cpp b/lib/platform/CXWindowsKeyMapper.cpp index e1e4ab9f..948f1e26 100644 --- a/lib/platform/CXWindowsKeyMapper.cpp +++ b/lib/platform/CXWindowsKeyMapper.cpp @@ -308,8 +308,8 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState) } // get modifier map from server - XModifierKeymap* modifiers = XGetModifierMapping(display); - int keysPerModifier = modifiers->max_keypermod; + XModifierKeymap* modifiers = XGetModifierMapping(display); + unsigned int keysPerModifier = modifiers->max_keypermod; // clear state m_keysymMap.clear(); @@ -330,7 +330,6 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState) // the keysym map. also collect all keycodes for each modifier. for (unsigned int i = 0; i < 8; ++i) { // no keycodes for this modifier yet - bool hasKeycode = false; KeyModifierMask mask = 0; IKeyState::KeyButtons modifierKeys; diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 3b4f6104..8415a39d 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -128,8 +128,8 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) : m_isOnScreen(m_isPrimary), m_x(0), m_y(0), m_w(0), m_h(0), - m_xCursor(0), m_yCursor(0), m_xCenter(0), m_yCenter(0), + m_xCursor(0), m_yCursor(0), m_keyState(NULL), m_keyMapper(), m_im(NULL), diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 1aabff53..daef7e63 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -28,6 +28,7 @@ EXTRA_DIST = \ CMSWindowsKeyMapper.cpp \ CMSWindowsScreen.cpp \ CMSWindowsScreenSaver.cpp \ + CMSWindowsUtil.cpp \ CSynergyHook.cpp \ CMSWindowsClipboard.h \ CMSWindowsClipboardAnyTextConverter.h \ @@ -38,6 +39,7 @@ EXTRA_DIST = \ CMSWindowsKeyMapper.h \ CMSWindowsScreen.h \ CMSWindowsScreenSaver.h \ + CMSWindowsUtil.h \ CSynergyHook.h \ $(NULL) diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index 4dc638dd..123e513b 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -121,6 +121,10 @@ SOURCE=.\CMSWindowsScreen.cpp SOURCE=.\CMSWindowsScreenSaver.cpp # End Source File +# Begin Source File + +SOURCE=.\CMSWindowsUtil.cpp +# End Source File # End Group # Begin Group "Header Files" @@ -161,6 +165,10 @@ SOURCE=.\CMSWindowsScreen.h SOURCE=.\CMSWindowsScreenSaver.h # End Source File +# Begin Source File + +SOURCE=.\CMSWindowsUtil.h +# End Source File # End Group # End Target # End Project diff --git a/lib/server/CClientListener.cpp b/lib/server/CClientListener.cpp index 9a811a36..45f3a713 100644 --- a/lib/server/CClientListener.cpp +++ b/lib/server/CClientListener.cpp @@ -47,13 +47,13 @@ CClientListener::CClientListener(const CNetworkAddress& address, LOG((CLOG_DEBUG1 "binding listen socket")); m_listen->bind(address); } - catch (XSocketAddressInUse& e) { + catch (XSocketAddressInUse&) { delete m_listen; delete m_socketFactory; delete m_streamFilterFactory; throw; } - catch (XBase& e) { + catch (XBase&) { delete m_listen; delete m_socketFactory; delete m_streamFilterFactory; diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 654ef383..d129ab96 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -37,8 +37,8 @@ CEvent::Type CServer::s_errorEvent = CEvent::kUnknown; CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : - m_active(primaryClient), m_primaryClient(primaryClient), + m_active(primaryClient), m_seqNum(0), m_config(config), m_activeSaver(NULL), @@ -117,9 +117,6 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : // add connection addClient(m_primaryClient); - // tell it about the active sides - m_primaryClient->reconfigure(getActivePrimarySides()); - // tell primary client about its options sendOptions(m_primaryClient); @@ -171,8 +168,7 @@ bool CServer::setConfig(const CConfig& config) { // refuse configuration if it doesn't include the primary screen - if (m_primaryClient != NULL && - !config.isScreen(m_primaryClient->getName())) { + if (!config.isScreen(m_primaryClient->getName())) { return false; } @@ -185,9 +181,7 @@ CServer::setConfig(const CConfig& config) processOptions(); // tell primary screen about reconfiguration - if (m_primaryClient != NULL) { - m_primaryClient->reconfigure(getActivePrimarySides()); - } + m_primaryClient->reconfigure(getActivePrimarySides()); // tell all (connected) clients about current options for (CClientList::const_iterator index = m_clients.begin(); @@ -296,18 +290,17 @@ CServer::getName(const IClient* client) const UInt32 CServer::getActivePrimarySides() const { - CString primaryName = getName(m_primaryClient); UInt32 sides = 0; - if (!m_config.getNeighbor(primaryName, kLeft).empty()) { + if (getNeighbor(m_primaryClient, kLeft) != NULL) { sides |= kLeftMask; } - if (!m_config.getNeighbor(primaryName, kRight).empty()) { + if (getNeighbor(m_primaryClient, kRight) != NULL) { sides |= kRightMask; } - if (!m_config.getNeighbor(primaryName, kTop).empty()) { + if (getNeighbor(m_primaryClient, kTop) != NULL) { sides |= kTopMask; } - if (!m_config.getNeighbor(primaryName, kBottom).empty()) { + if (getNeighbor(m_primaryClient, kBottom) != NULL) { sides |= kBottomMask; } return sides; @@ -546,6 +539,10 @@ CServer::getNeighbor(IClient* src, assert(lastGoodScreen != NULL); y += dy; break; + + case kNoDirection: + assert(0 && "bad direction"); + return NULL; } // save destination screen @@ -582,6 +579,10 @@ CServer::getNeighbor(IClient* src, y < dy + getJumpZoneSize(dst)) y = dy + getJumpZoneSize(dst); break; + + case kNoDirection: + assert(0 && "bad direction"); + return NULL; } } @@ -623,6 +624,10 @@ CServer::getNeighbor(IClient* src, } x += dx; break; + + case kNoDirection: + assert(0 && "bad direction"); + return NULL; } return dst; @@ -999,13 +1004,13 @@ CServer::handleWheelEvent(const CEvent& event, void*) } void -CServer::handleScreensaverActivatedEvent(const CEvent& event, void*) +CServer::handleScreensaverActivatedEvent(const CEvent&, void*) { onScreensaver(true); } void -CServer::handleScreensaverDeactivatedEvent(const CEvent& event, void*) +CServer::handleScreensaverDeactivatedEvent(const CEvent&, void*) { onScreensaver(false); } @@ -1212,7 +1217,6 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y) LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); // mouse move on primary (server's) screen - assert(m_primaryClient != NULL); assert(m_active == m_primaryClient); // save position @@ -1429,6 +1433,10 @@ CServer::addClient(IClient* client) // add to list m_clientSet.insert(client); m_clients.insert(std::make_pair(name, client)); + + // tell primary client about the active sides + m_primaryClient->reconfigure(getActivePrimarySides()); + return true; } @@ -1452,6 +1460,7 @@ CServer::removeClient(IClient* client) // remove from list m_clients.erase(i); m_clientSet.erase(client); + return true; } @@ -1477,7 +1486,7 @@ CServer::closeClient(IClient* client, const char* msg) // install timer. wait timeout seconds for client to close. double timeout = 5.0; - CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(5.0, NULL); + CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL); EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, new TMethodEventJob(this, &CServer::handleClientCloseTimeout, client)); @@ -1577,6 +1586,9 @@ CServer::forceLeaveClient(IClient* client) if (m_activeSaver == client) { m_activeSaver = NULL; } + + // tell primary client about the active sides + m_primaryClient->reconfigure(getActivePrimarySides()); } diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp index ecaf4f39..de7351b9 100644 --- a/lib/synergy/CProtocolUtil.cpp +++ b/lib/synergy/CProtocolUtil.cpp @@ -89,7 +89,7 @@ CProtocolUtil::vwritef(IStream* stream, } } -bool +void CProtocolUtil::vreadf(IStream* stream, const char* fmt, va_list args) { assert(stream != NULL); diff --git a/lib/synergy/CProtocolUtil.h b/lib/synergy/CProtocolUtil.h index 2ea1609f..d4019b0b 100644 --- a/lib/synergy/CProtocolUtil.h +++ b/lib/synergy/CProtocolUtil.h @@ -71,7 +71,7 @@ public: private: static void vwritef(IStream*, const char* fmt, UInt32 size, va_list); - static bool vreadf(IStream*, + static void vreadf(IStream*, const char* fmt, va_list); static UInt32 getLength(const char* fmt, va_list); diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index 9bbd4a82..a4164f55 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -27,8 +27,8 @@ CScreen::CScreen(IPlatformScreen* platformScreen) : m_isPrimary(platformScreen->isPrimary()), m_enabled(false), m_entered(m_isPrimary), - m_toggleKeys(0), - m_screenSaverSync(true) + m_screenSaverSync(true), + m_toggleKeys(0) { assert(m_screen != NULL); @@ -376,7 +376,7 @@ CScreen::setOptions(const COptionsList& options) void CScreen::setSequenceNumber(UInt32 seqNum) { - return m_screen->setSequenceNumber(seqNum); + m_screen->setSequenceNumber(seqNum); } bool diff --git a/lib/synergy/IScreen.h b/lib/synergy/IScreen.h index 7af925cf..2f1b4450 100644 --- a/lib/synergy/IScreen.h +++ b/lib/synergy/IScreen.h @@ -73,8 +73,7 @@ public: //! Get shape changed event type /*! Returns the shape changed event type. This is sent whenever the - screen's shape changes, the cursor center moves, or the jump zone - size changes. + screen's shape changes. */ static CEvent::Type getShapeChangedEvent(); From 63db68aee5f3ea6cedd2fef7b052c32e025523db Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 28 Feb 2004 12:24:47 +0000 Subject: [PATCH 581/807] Added missing file. --- lib/common/Version.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 lib/common/Version.cpp diff --git a/lib/common/Version.cpp b/lib/common/Version.cpp new file mode 100644 index 00000000..2347e1df --- /dev/null +++ b/lib/common/Version.cpp @@ -0,0 +1,22 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "Version.h" + +const char* kApplication = "synergy"; +const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; +const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; +const char* kWebsite = "http://synergy2.sourceforge.net/"; +const char* kVersion = VERSION; +const char* kAppVersion = "synergy " VERSION; From 1a9ddb9110451a85b6efce6f5432d07d11026f8a Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 28 Feb 2004 12:30:52 +0000 Subject: [PATCH 582/807] Added missing files. --- lib/platform/CMSWindowsUtil.cpp | 75 +++++++++++++++++++++++++++++++++ lib/platform/CMSWindowsUtil.h | 38 +++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 lib/platform/CMSWindowsUtil.cpp create mode 100644 lib/platform/CMSWindowsUtil.h diff --git a/lib/platform/CMSWindowsUtil.cpp b/lib/platform/CMSWindowsUtil.cpp new file mode 100644 index 00000000..4b3e3f4c --- /dev/null +++ b/lib/platform/CMSWindowsUtil.cpp @@ -0,0 +1,75 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsUtil.h" +#include "CStringUtil.h" +#include + +// +// CMSWindowsUtil +// + +CString +CMSWindowsUtil::getString(HINSTANCE instance, DWORD id) +{ + char buffer[1024]; + int size = static_cast(sizeof(buffer) / sizeof(buffer[0])); + char* msg = buffer; + + // load string + int n = LoadString(instance, id, msg, size); + msg[n] = '\0'; + if (n < size) { + return msg; + } + + // not enough buffer space. keep trying larger buffers until + // we get the whole string. + msg = NULL; + do { + size <<= 1; + delete[] msg; + char* msg = new char[size]; + n = LoadString(instance, id, msg, size); + } while (n == size); + msg[n] = '\0'; + + CString result(msg); + delete[] msg; + return result; +} + +CString +CMSWindowsUtil::getErrorString(HINSTANCE hinstance, DWORD error, DWORD id) +{ + char* buffer; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&buffer, + 0, + NULL) == 0) { + CString errorString = CStringUtil::print("%d", error); + return CStringUtil::format(getString(hinstance, id).c_str(), + errorString.c_str()); + } + else { + CString result(buffer); + LocalFree(buffer); + return result; + } +} diff --git a/lib/platform/CMSWindowsUtil.h b/lib/platform/CMSWindowsUtil.h new file mode 100644 index 00000000..5c4d14f5 --- /dev/null +++ b/lib/platform/CMSWindowsUtil.h @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSUTIL_H +#define CMSWINDOWSUTIL_H + +#include "CString.h" +#define WINDOWS_LEAN_AND_MEAN +#include + +class CMSWindowsUtil { +public: + //! Get message string + /*! + Gets a string for \p id from the string table of \p instance. + */ + static CString getString(HINSTANCE instance, DWORD id); + + //! Get error string + /*! + Gets a system error message for \p error. If the error cannot be + found return the string for \p id, replacing ${1} with \p error. + */ + static CString getErrorString(HINSTANCE, DWORD error, DWORD id); +}; + +#endif From 3dea8f8c5db980b1e576ff417cc20bcd547b5c0d Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 28 Feb 2004 16:00:54 +0000 Subject: [PATCH 583/807] Now using first set options message as end of handshake. --- lib/client/CServerProxy.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 88ae2c9d..c72dc12f 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -136,14 +136,18 @@ CServerProxy::parseHandshakeMessage(const UInt8* code) infoAcknowledgment(); } - else if (memcmp(code, kMsgCResetOptions, 4) == 0) { - resetOptions(); + else if (memcmp(code, kMsgDSetOptions, 4) == 0) { + setOptions(); // handshake is complete m_parser = &CServerProxy::parseMessage; EVENTQUEUE->addEvent(CEvent(getHandshakeCompleteEvent(), this)); } + else if (memcmp(code, kMsgCResetOptions, 4) == 0) { + resetOptions(); + } + else if (memcmp(code, kMsgCNoop, 4) == 0) { // accept and discard no-op } From 9f7e909361c4bde75b35235637319e4b8a2a2ee1 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 28 Feb 2004 16:06:00 +0000 Subject: [PATCH 584/807] Fixed incorrect accumulation of newlines in log. --- lib/base/CLog.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index f99d90dd..1cc007da 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -265,6 +265,9 @@ CLog::output(int priority, char* msg) const msg[g_maxPriorityLength + 1] = ' '; } + // find end of message + char* end = msg + g_priorityPad + strlen(msg + g_priorityPad); + // write to each outputter CArchMutexLock lock(m_mutex); for (COutputterList::const_iterator index = m_alwaysOutputters.begin(); @@ -274,7 +277,7 @@ CLog::output(int priority, char* msg) const ILogOutputter* outputter = *index; // put an appropriate newline at the end - strcat(msg + g_priorityPad, outputter->getNewline()); + strcpy(end, outputter->getNewline()); // open the outputter outputter->open(kApplication); @@ -289,7 +292,7 @@ CLog::output(int priority, char* msg) const ILogOutputter* outputter = *index; // put an appropriate newline at the end - strcat(msg + g_priorityPad, outputter->getNewline()); + strcpy(end, outputter->getNewline()); // open the outputter outputter->open(kApplication); From 82dffeb49850e197e4e6ce21850b532768e0ecae Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 28 Feb 2004 17:49:29 +0000 Subject: [PATCH 585/807] Generalized signal handling. Now handling SIGHUP in addition to SIGINT and SIGTERM. Setup SIGHUP to reload the server's configuration. --- cmd/synergyc/synergyc.cpp | 9 +++- cmd/synergys/synergys.cpp | 77 +++++++++++++++++++------- lib/arch/CArch.cpp | 8 +-- lib/arch/CArch.h | 4 +- lib/arch/CArchConsoleWindows.cpp | 17 ++++-- lib/arch/CArchMultithreadPosix.cpp | 80 ++++++++++++++++------------ lib/arch/CArchMultithreadPosix.h | 8 +-- lib/arch/CArchMultithreadWindows.cpp | 25 +++++---- lib/arch/CArchMultithreadWindows.h | 8 +-- lib/arch/IArchMultithread.h | 25 +++++++-- lib/base/CEventQueue.cpp | 10 ++-- 11 files changed, 183 insertions(+), 88 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 7ca94409..165c246c 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -358,6 +358,13 @@ static int mainLoop() { + // create socket multiplexer. this must happen after daemonization + // on unix because threads evaporate across a fork(). + CSocketMultiplexer multiplexer; + + // create the event queue + CEventQueue eventQueue; + // start the client. if this return false then we've failed and // we shouldn't retry. LOG((CLOG_DEBUG1 "starting client")); @@ -416,8 +423,6 @@ int run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) { // general initialization - CSocketMultiplexer multiplexer; - CEventQueue eventQueue; ARG->m_serverAddress = new CNetworkAddress; ARG->m_pname = ARCH->getBasename(argv[0]); diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index d1900fea..2f57aca7 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -68,6 +68,7 @@ typedef int (*StartupFunc)(int, char**); static void parse(int argc, const char* const* argv); +static bool loadConfig(const CString& pathname); static void loadConfig(); // @@ -83,7 +84,7 @@ public: m_backend(false), m_restartable(true), m_daemon(true), - m_configFile(NULL), + m_configFile(), m_logFilter(NULL) { s_instance = this; } ~CArgs() { s_instance = NULL; } @@ -94,7 +95,7 @@ public: bool m_backend; bool m_restartable; bool m_daemon; - const char* m_configFile; + CString m_configFile; const char* m_logFilter; CString m_name; CNetworkAddress* m_synergyAddress; @@ -136,11 +137,12 @@ createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) // platform independent main // -static CServer* s_server = NULL; -static CScreen* s_serverScreen = NULL; -static CPrimaryClient* s_primaryClient = NULL; -static CClientListener* s_listener = NULL; -static CServerTaskBarReceiver* s_taskBarReceiver = NULL; +static CServer* s_server = NULL; +static CScreen* s_serverScreen = NULL; +static CPrimaryClient* s_primaryClient = NULL; +static CClientListener* s_listener = NULL; +static CServerTaskBarReceiver* s_taskBarReceiver = NULL; +static CEvent::Type s_reloadConfigEvent = CEvent::kUnknown; static void @@ -389,10 +391,38 @@ stopServer() s_serverScreen = NULL; } +static +void +reloadSignalHandler(CArch::ESignal, void*) +{ + EVENTQUEUE->addEvent(CEvent(s_reloadConfigEvent, + IEventQueue::getSystemTarget())); +} + +static +void +reloadConfig(const CEvent&, void*) +{ + LOG((CLOG_DEBUG "reload configuration")); + if (loadConfig(ARG->m_configFile)) { + if (s_server != NULL) { + s_server->setConfig(*ARG->m_config); + } + LOG((CLOG_NOTE "reloaded configuration")); + } +} + static int mainLoop() { + // create socket multiplexer. this must happen after daemonization + // on unix because threads evaporate across a fork(). + CSocketMultiplexer multiplexer; + + // create the event queue + CEventQueue eventQueue; + // if configuration has no screens then add this system // as the default if (ARG->m_config->begin() == ARG->m_config->end()) { @@ -423,6 +453,13 @@ mainLoop() return kExitFailed; } + // handle hangup signal by reloading the server's configuration + CEvent::registerTypeOnce(s_reloadConfigEvent, "reloadConfig"); + ARCH->setSignalHandler(CArch::kHANGUP, &reloadSignalHandler, NULL); + EVENTQUEUE->adoptHandler(s_reloadConfigEvent, + IEventQueue::getSystemTarget(), + new CFunctionEventJob(&reloadConfig)); + // run event loop. if startServer() failed we're supposed to retry // later. the timer installed by startServer() will take care of // that. @@ -438,6 +475,8 @@ mainLoop() // close down LOG((CLOG_DEBUG1 "stopping server")); + EVENTQUEUE->removeHandler(s_reloadConfigEvent, + IEventQueue::getSystemTarget()); stopServer(); updateStatus(); LOG((CLOG_NOTE "stopped server")); @@ -477,8 +516,6 @@ int run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) { // general initialization - CSocketMultiplexer multiplexer; - CEventQueue eventQueue; ARG->m_synergyAddress = new CNetworkAddress; ARG->m_config = new CConfig; ARG->m_pname = ARCH->getBasename(argv[0]); @@ -759,14 +796,12 @@ parse(int argc, const char* const* argv) static bool -loadConfig(const char* pathname) +loadConfig(const CString& pathname) { - assert(pathname != NULL); - try { // load configuration - LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname)); - std::ifstream configStream(pathname); + LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str())); + std::ifstream configStream(pathname.c_str()); if (!configStream) { throw XConfigRead("cannot open file"); } @@ -776,7 +811,7 @@ loadConfig(const char* pathname) } catch (XConfigRead& e) { LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", - pathname, e.what())); + pathname.c_str(), e.what())); } return false; } @@ -788,7 +823,7 @@ loadConfig() bool loaded = false; // load the config file, if specified - if (ARG->m_configFile != NULL) { + if (!ARG->m_configFile.empty()) { loaded = loadConfig(ARG->m_configFile); } @@ -801,14 +836,20 @@ loadConfig() path = ARCH->concatPath(path, USR_CONFIG_NAME); // now try loading the user's configuration - loaded = loadConfig(path.c_str()); + if (loadConfig(path)) { + loaded = true; + ARG->m_configFile = path; + } } if (!loaded) { // try the system-wide config file path = ARCH->getSystemDirectory(); if (!path.empty()) { path = ARCH->concatPath(path, SYS_CONFIG_NAME); - loaded = loadConfig(path.c_str()); + if (loadConfig(path)) { + loaded = true; + ARG->m_configFile = path; + } } } } diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index d8640960..98b18279 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -376,15 +376,15 @@ CArch::getIDOfThread(CArchThread thread) } void -CArch::setInterruptHandler(InterruptFunc func, void* userData) +CArch::setSignalHandler(ESignal signal, SignalFunc func, void* userData) { - m_mt->setInterruptHandler(func, userData); + m_mt->setSignalHandler(signal, func, userData); } void -CArch::interrupt() +CArch::raiseSignal(ESignal signal) { - m_mt->interrupt(); + m_mt->raiseSignal(signal); } CArchSocket diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index f82c27cf..8152d669 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -120,8 +120,8 @@ public: virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread); - virtual void setInterruptHandler(InterruptFunc, void*); - virtual void interrupt(); + virtual void setSignalHandler(ESignal, SignalFunc, void*); + virtual void raiseSignal(ESignal); // IArchNetwork overrides virtual CArchSocket newSocket(EAddressFamily, ESocketType); diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp index 2cdedc71..6796a454 100644 --- a/lib/arch/CArchConsoleWindows.cpp +++ b/lib/arch/CArchConsoleWindows.cpp @@ -108,9 +108,20 @@ CArchConsoleWindows::getNewlineForConsole() } BOOL WINAPI -CArchConsoleWindows::signalHandler(DWORD) +CArchConsoleWindows::signalHandler(DWORD ctrlType) { // terminate app and skip remaining handlers - ARCH->interrupt(); - return TRUE; + switch (ctrlType) { + case CTRL_C_EVENT: + ARCH->raiseSignal(CArch::kINTERRUPT); + return TRUE; + + case CTRL_BREAK_EVENT: + ARCH->raiseSignal(CArch::kTERMINATE); + return TRUE; + + default: + ARCH->raiseSignal(CArch::kINTERRUPT); + return TRUE; + } } diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 403cc82b..b87a9b58 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -41,6 +41,16 @@ # define HAVE_POSIX_SIGWAIT 1 #endif +static +void +setSignalSet(sigset_t* sigset) +{ + sigemptyset(sigset); + sigaddset(sigset, SIGHUP); + sigaddset(sigset, SIGINT); + sigaddset(sigset, SIGTERM); +} + // // CArchThreadImpl // @@ -83,14 +93,18 @@ CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL; CArchMultithreadPosix::CArchMultithreadPosix() : m_newThreadCalled(false), - m_nextID(0), - m_signalFunc(NULL), - m_signalUserData(NULL) + m_nextID(0) { assert(s_instance == NULL); s_instance = this; + // no signal handlers + for (size_t i = 0; i < kNUM_SIGNALS; ++i) { + m_signalFunc[i] = NULL; + m_signalUserData[i] = NULL; + } + // create mutex for thread list m_threadMutex = newMutex(); @@ -353,13 +367,6 @@ CArchMultithreadPosix::newThread(ThreadFunc func, void* data) thread->m_func = func; thread->m_userData = data; - // mask some signals in all threads except the main thread - sigset_t sigset, oldsigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); - // create the thread. pthread_create() on RedHat 7.2 smp fails // if passed a NULL attr so use a default attr. pthread_attr_t attr; @@ -370,9 +377,6 @@ CArchMultithreadPosix::newThread(ThreadFunc func, void* data) pthread_attr_destroy(&attr); } - // restore signals - pthread_sigmask(SIG_SETMASK, &oldsigset, NULL); - // check if thread was started if (status != 0) { // failed to start thread so clean up @@ -560,23 +564,24 @@ CArchMultithreadPosix::getIDOfThread(CArchThread thread) } void -CArchMultithreadPosix::setInterruptHandler(InterruptFunc func, void* userData) +CArchMultithreadPosix::setSignalHandler( + ESignal signal, SignalFunc func, void* userData) { lockMutex(m_threadMutex); - m_signalFunc = func; - m_signalUserData = userData; + m_signalFunc[signal] = func; + m_signalUserData[signal] = userData; unlockMutex(m_threadMutex); } void -CArchMultithreadPosix::interrupt() +CArchMultithreadPosix::raiseSignal(ESignal signal) { lockMutex(m_threadMutex); - if (m_signalFunc != NULL) { - m_signalFunc(m_signalUserData); + if (m_signalFunc[signal] != NULL) { + m_signalFunc[signal](signal, m_signalUserData[signal]); unblockThread(m_mainThread); } - else { + else if (signal == kINTERRUPT || signal == kTERMINATE) { ARCH->cancelThread(m_mainThread); } unlockMutex(m_threadMutex); @@ -587,11 +592,9 @@ CArchMultithreadPosix::startSignalHandler() { // set signal mask. the main thread blocks these signals and // the signal handler thread will listen for them. - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - pthread_sigmask(SIG_BLOCK, &sigset, NULL); + sigset_t sigset, oldsigset; + setSignalSet(&sigset); + pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset); // fire up the INT and TERM signal handler thread. we could // instead arrange to catch and handle these signals but @@ -608,10 +611,7 @@ CArchMultithreadPosix::startSignalHandler() if (status != 0) { // can't create thread to wait for signal so don't block // the signals. - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); - pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + pthread_sigmask(SIG_UNBLOCK, &oldsigset, NULL); } } @@ -766,9 +766,7 @@ CArchMultithreadPosix::threadSignalHandler(void*) // add signal to mask sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGTERM); + setSignalSet(&sigset); // also wait on SIGABRT. on linux (others?) this thread (process) // will persist after all the other threads evaporate due to an @@ -791,6 +789,22 @@ CArchMultithreadPosix::threadSignalHandler(void*) #endif // if we get here then the signal was raised - ARCH->interrupt(); + switch (signal) { + case SIGINT: + ARCH->raiseSignal(kINTERRUPT); + break; + + case SIGTERM: + ARCH->raiseSignal(kTERMINATE); + break; + + case SIGHUP: + ARCH->raiseSignal(kHANGUP); + break; + + default: + // ignore + break; + } } } diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h index 3921c8e9..2ac6183b 100644 --- a/lib/arch/CArchMultithreadPosix.h +++ b/lib/arch/CArchMultithreadPosix.h @@ -72,8 +72,8 @@ public: virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread); - virtual void setInterruptHandler(InterruptFunc, void*); - virtual void interrupt(); + virtual void setSignalHandler(ESignal, SignalFunc, void*); + virtual void raiseSignal(ESignal); private: void startSignalHandler(); @@ -104,8 +104,8 @@ private: ThreadID m_nextID; pthread_t m_signalThread; - InterruptFunc m_signalFunc; - void* m_signalUserData; + SignalFunc m_signalFunc[kNUM_SIGNALS]; + void* m_signalUserData[kNUM_SIGNALS]; }; #endif diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index 384722c4..91ec38d4 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -80,13 +80,17 @@ CArchThreadImpl::~CArchThreadImpl() CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL; -CArchMultithreadWindows::CArchMultithreadWindows() : - m_signalFunc(NULL), - m_signalUserData(NULL) +CArchMultithreadWindows::CArchMultithreadWindows() { assert(s_instance == NULL); s_instance = this; + // no signal handlers + for (size_t i = 0; i < kNUM_SIGNALS; ++i) { + m_signalFunc[i] = NULL; + m_signalUserData[i] = NULL; + } + // create mutex for thread list m_threadMutex = newMutex(); @@ -529,23 +533,24 @@ CArchMultithreadWindows::getIDOfThread(CArchThread thread) } void -CArchMultithreadWindows::setInterruptHandler(InterruptFunc func, void* userData) +CArchMultithreadWindows::setSignalHandler( + ESignal signal, SignalFunc func, void* userData) { lockMutex(m_threadMutex); - m_signalFunc = func; - m_signalUserData = userData; + m_signalFunc[signal] = func; + m_signalUserData[signal] = userData; unlockMutex(m_threadMutex); } void -CArchMultithreadWindows::interrupt() +CArchMultithreadWindows::raiseSignal(ESignal signal) { lockMutex(m_threadMutex); - if (m_signalFunc != NULL) { - m_signalFunc(m_signalUserData); + if (m_signalFunc[signal] != NULL) { + m_signalFunc[signal](signal, m_signalUserData[signal]); ARCH->unblockPollSocket(m_mainThread); } - else { + else if (signal == kINTERRUPT || signal == kTERMINATE) { ARCH->cancelThread(m_mainThread); } unlockMutex(m_threadMutex); diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h index aecd4173..44a8d184 100644 --- a/lib/arch/CArchMultithreadWindows.h +++ b/lib/arch/CArchMultithreadWindows.h @@ -81,8 +81,8 @@ public: virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread); - virtual void setInterruptHandler(InterruptFunc, void*); - virtual void interrupt(); + virtual void setSignalHandler(ESignal, SignalFunc, void*); + virtual void raiseSignal(ESignal); private: CArchThreadImpl* find(DWORD id); @@ -107,8 +107,8 @@ private: CThreadList m_threadList; CArchThread m_mainThread; - InterruptFunc m_signalFunc; - void* m_signalUserData; + SignalFunc m_signalFunc[kNUM_SIGNALS]; + void* m_signalUserData[kNUM_SIGNALS]; }; #endif diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index 477f42a7..d5eaa550 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -71,6 +71,19 @@ public: typedef void* (*ThreadFunc)(void*); //! Type of thread identifier typedef unsigned int ThreadID; + //! Types of signals + /*! + Not all platforms support all signals. Unsupported signals are + ignored. + */ + enum ESignal { + kINTERRUPT, //!< Interrupt (e.g. Ctrl+C) + kTERMINATE, //!< Terminate (e.g. Ctrl+Break) + kHANGUP, //!< Hangup (SIGHUP) + kNUM_SIGNALS + }; + //! Type of signal handler function + typedef void (*SignalFunc)(ESignal, void* userData); //! @name manipulators //@{ @@ -242,12 +255,16 @@ public: Sets the function to call on receipt of an external interrupt. By default and when \p func is NULL, the main thread is cancelled. */ - typedef void (*InterruptFunc)(void*); - virtual void setInterruptHandler(InterruptFunc func, + virtual void setSignalHandler(ESignal, SignalFunc func, void* userData) = 0; - //! Invoke the interrupt handler - virtual void interrupt() = 0; + //! Invoke the signal handler + /*! + Invokes the signal handler for \p signal, if any. If no handler + cancels the main thread for \c kINTERRUPT and \c kTERMINATE and + ignores the call otherwise. + */ + virtual void raiseSignal(ESignal signal) = 0; //@} }; diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp index d3ff27bc..8e309ce3 100644 --- a/lib/base/CEventQueue.cpp +++ b/lib/base/CEventQueue.cpp @@ -1,4 +1,4 @@ -/* +;/* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2004 Chris Schoeneman * @@ -22,7 +22,7 @@ // interrupt handler. this just adds a quit event to the queue. static void -interrupt(void*) +interrupt(CArch::ESignal, void*) { EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } @@ -37,14 +37,16 @@ CEventQueue::CEventQueue() : { setInstance(this); m_mutex = ARCH->newMutex(); - ARCH->setInterruptHandler(&interrupt, NULL); + ARCH->setSignalHandler(CArch::kINTERRUPT, &interrupt, NULL); + ARCH->setSignalHandler(CArch::kTERMINATE, &interrupt, NULL); m_buffer = new CSimpleEventQueueBuffer; } CEventQueue::~CEventQueue() { delete m_buffer; - ARCH->setInterruptHandler(NULL, NULL); + ARCH->setSignalHandler(CArch::kINTERRUPT, NULL, NULL); + ARCH->setSignalHandler(CArch::kTERMINATE, NULL, NULL); ARCH->closeMutex(m_mutex); setInstance(NULL); } From 75cafe65a644f5cc91967475763b0f311531b1cf Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 28 Feb 2004 17:51:55 +0000 Subject: [PATCH 586/807] Enabled running at high priority on windows. --- cmd/synergyc/synergyc.cpp | 3 +-- cmd/synergys/synergys.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 165c246c..2f5b945b 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -751,8 +751,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) CArch arch(instance); CMSWindowsScreen::init(instance); CLOG; -// FIXME -// CThread::getCurrentThread().setPriority(-14); + CThread::getCurrentThread().setPriority(-14); CArgs args; // windows NT family starts services using no command line options. diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 2f57aca7..a7b70cd6 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -949,8 +949,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) CArch arch(instance); CMSWindowsScreen::init(instance); CLOG; -// FIXME -// CThread::getCurrentThread().setPriority(-14); + CThread::getCurrentThread().setPriority(-14); CArgs args; // windows NT family starts services using no command line options. From d6ec331b09966bce12be289b770747a66819cb78 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 29 Feb 2004 16:11:17 +0000 Subject: [PATCH 587/807] Made all arch sockets non-blocking. --- lib/arch/CArch.cpp | 10 +- lib/arch/CArch.h | 3 +- lib/arch/CArchNetworkBSD.cpp | 179 ++++++++++++----------------- lib/arch/CArchNetworkBSD.h | 5 +- lib/arch/CArchNetworkWinsock.cpp | 187 ++++++++++++------------------- lib/arch/CArchNetworkWinsock.h | 6 +- lib/arch/IArchNetwork.h | 45 ++------ lib/arch/XArch.h | 7 +- lib/net/CTCPListenSocket.cpp | 5 +- lib/net/CTCPSocket.cpp | 18 ++- 10 files changed, 179 insertions(+), 286 deletions(-) diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index 98b18279..88f2eda4 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -435,10 +435,10 @@ CArch::acceptSocket(CArchSocket s, CArchNetAddress* addr) return m_net->acceptSocket(s, addr); } -void +bool CArch::connectSocket(CArchSocket s, CArchNetAddress name) { - m_net->connectSocket(s, name); + return m_net->connectSocket(s, name); } int @@ -471,12 +471,6 @@ CArch::throwErrorOnSocket(CArchSocket s) m_net->throwErrorOnSocket(s); } -bool -CArch::setBlockingOnSocket(CArchSocket s, bool blocking) -{ - return m_net->setBlockingOnSocket(s, blocking); -} - bool CArch::setNoDelayOnSocket(CArchSocket s, bool noDelay) { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index 8152d669..7e70bd84 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -132,14 +132,13 @@ public: virtual void bindSocket(CArchSocket s, CArchNetAddress addr); virtual void listenOnSocket(CArchSocket s); virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); - virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual bool connectSocket(CArchSocket s, CArchNetAddress name); virtual int pollSocket(CPollEntry[], int num, double timeout); virtual void unblockPollSocket(CArchThread thread); virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len); virtual void throwErrorOnSocket(CArchSocket); - virtual bool setBlockingOnSocket(CArchSocket, bool blocking); virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); virtual std::string getHostName(); virtual CArchNetAddress newAnyAddr(EAddressFamily); diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 487a2eb6..e7f64a43 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -69,18 +69,23 @@ CArchNetworkBSD::~CArchNetworkBSD() CArchSocket CArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type) { - // allocate socket object - CArchSocketImpl* newSocket = new CArchSocketImpl; - // create socket int fd = socket(s_family[family], s_type[type], 0); if (fd == -1) { throwError(errno); } + try { + setBlockingOnSocket(fd, false); + } + catch (...) { + close(fd); + throw; + } - newSocket->m_fd = fd; - newSocket->m_connected = false; - newSocket->m_refCount = 1; + // allocate socket object + CArchSocketImpl* newSocket = new CArchSocketImpl; + newSocket->m_fd = fd; + newSocket->m_refCount = 1; return newSocket; } @@ -108,23 +113,14 @@ CArchNetworkBSD::closeSocket(CArchSocket s) // close the socket if necessary if (doClose) { - do { - if (close(s->m_fd) == -1) { - // close failed - int err = errno; - if (err == EINTR) { - // interrupted system call - ARCH->testCancelThread(); - continue; - } - - // restore the last ref and throw - ARCH->lockMutex(m_mutex); - ++s->m_refCount; - ARCH->unlockMutex(m_mutex); - throwError(err); - } - } while (false); + if (close(s->m_fd) == -1) { + // close failed. restore the last ref and throw. + int err = errno; + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } delete s; } } @@ -191,27 +187,32 @@ CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr) *addr = new CArchNetAddressImpl; // accept on socket - int fd; - do { - fd = accept(s->m_fd, &(*addr)->m_addr, &(*addr)->m_len); - if (fd == -1) { - int err = errno; - if (err == EINTR) { - // interrupted system call - ARCH->testCancelThread(); - continue; - } - delete newSocket; - delete *addr; - *addr = NULL; - throwError(err); + int fd = accept(s->m_fd, &(*addr)->m_addr, &(*addr)->m_len); + if (fd == -1) { + int err = errno; + delete newSocket; + delete *addr; + *addr = NULL; + if (err == EAGAIN) { + return NULL; } - } while (false); + throwError(err); + } + + try { + setBlockingOnSocket(fd, false); + } + catch (...) { + close(fd); + delete newSocket; + delete *addr; + *addr = NULL; + throw; + } // initialize socket - newSocket->m_fd = fd; - newSocket->m_connected = true; - newSocket->m_refCount = 1; + newSocket->m_fd = fd; + newSocket->m_refCount = 1; // discard address if not requested if (addr == &dummy) { @@ -221,32 +222,22 @@ CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr) return newSocket; } -void +bool CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr) { assert(s != NULL); assert(addr != NULL); - do { - if (connect(s->m_fd, &addr->m_addr, addr->m_len) == -1) { - if (errno == EINTR) { - // interrupted system call - ARCH->testCancelThread(); - continue; - } - - if (errno == EISCONN) { - // already connected - break; - } - - throwError(errno); + if (connect(s->m_fd, &addr->m_addr, addr->m_len) == -1) { + if (errno == EISCONN) { + return true; } - } while (false); - - ARCH->lockMutex(m_mutex); - s->m_connected = true; - ARCH->unlockMutex(m_mutex); + if (errno == EINPROGRESS) { + return false; + } + throwError(errno); + } + return true; } #if HAVE_POLL @@ -435,23 +426,13 @@ CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) { assert(s != NULL); - ssize_t n; - do { - n = read(s->m_fd, buf, len); - if (n == -1) { - if (errno == EINTR) { - // interrupted system call - n = 0; - break; - } - else if (errno == EAGAIN) { - n = 0; - break; - } - throwError(errno); + ssize_t n = read(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR || errno == EAGAIN) { + return 0; } - } while (false); - ARCH->testCancelThread(); + throwError(errno); + } return n; } @@ -460,24 +441,13 @@ CArchNetworkBSD::writeSocket(CArchSocket s, const void* buf, size_t len) { assert(s != NULL); - ssize_t n; - do { - n = write(s->m_fd, buf, len); - if (n == -1) { - if (errno == EINTR) { - // interrupted system call - n = 0; - break; - } - else if (errno == EAGAIN) { - // no buffer space - n = 0; - break; - } - throwError(errno); + ssize_t n = write(s->m_fd, buf, len); + if (n == -1) { + if (errno == EINTR || errno == EAGAIN) { + return 0; } - } while (false); - ARCH->testCancelThread(); + throwError(errno); + } return n; } @@ -499,26 +469,24 @@ CArchNetworkBSD::throwErrorOnSocket(CArchSocket s) } } -bool -CArchNetworkBSD::setBlockingOnSocket(CArchSocket s, bool blocking) +void +CArchNetworkBSD::setBlockingOnSocket(int fd, bool blocking) { - assert(s != NULL); + assert(fd != -1); - int mode = fcntl(s->m_fd, F_GETFL, 0); + int mode = fcntl(fd, F_GETFL, 0); if (mode == -1) { throwError(errno); } - bool old = ((mode & O_NDELAY) == 0); if (blocking) { mode &= ~O_NDELAY; } else { mode |= O_NDELAY; } - if (fcntl(s->m_fd, F_SETFL, mode) == -1) { + if (fcntl(fd, F_SETFL, mode) == -1) { throwError(errno); } - return old; } bool @@ -775,8 +743,9 @@ void CArchNetworkBSD::throwError(int err) { switch (err) { - case EAGAIN: - throw XArchNetworkWouldBlock(new XArchEvalUnix(err)); + case EINTR: + ARCH->testCancelThread(); + throw XArchNetworkInterrupted(new XArchEvalUnix(err)); case EACCES: case EPERM: @@ -833,10 +802,6 @@ CArchNetworkBSD::throwError(int err) case ECONNREFUSED: throw XArchNetworkConnectionRefused(new XArchEvalUnix(err)); - case EINPROGRESS: - case EALREADY: - throw XArchNetworkConnecting(new XArchEvalUnix(err)); - case EHOSTDOWN: case ETIMEDOUT: throw XArchNetworkTimedOut(new XArchEvalUnix(err)); diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index b4ed7097..06f7f507 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -30,7 +30,6 @@ typedef int socklen_t; class CArchSocketImpl { public: int m_fd; - bool m_connected; int m_refCount; }; @@ -58,14 +57,13 @@ public: virtual void bindSocket(CArchSocket s, CArchNetAddress addr); virtual void listenOnSocket(CArchSocket s); virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); - virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual bool connectSocket(CArchSocket s, CArchNetAddress name); virtual int pollSocket(CPollEntry[], int num, double timeout); virtual void unblockPollSocket(CArchThread thread); virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len); virtual void throwErrorOnSocket(CArchSocket); - virtual bool setBlockingOnSocket(CArchSocket, bool blocking); virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); virtual std::string getHostName(); virtual CArchNetAddress newAnyAddr(EAddressFamily); @@ -81,6 +79,7 @@ public: virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress); private: + void setBlockingOnSocket(int fd, bool blocking); void throwError(int); void throwNameError(int); diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index 547546bb..0f69c554 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -187,20 +187,25 @@ CArchNetworkWinsock::init(HMODULE module) CArchSocket CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type) { - // allocate socket object - CArchSocketImpl* socket = new CArchSocketImpl; - // create socket SOCKET fd = socket_winsock(s_family[family], s_type[type], 0); if (fd == INVALID_SOCKET) { throwError(getsockerror_winsock()); } + try { + setBlockingOnSocket(fd, false); + } + catch (...) { + close(fd); + throw; + } - socket->m_socket = fd; - socket->m_connected = false; - socket->m_refCount = 1; - socket->m_event = WSACreateEvent_winsock(); - socket->m_pollWrite = false; + // allocate socket object + CArchSocketImpl* socket = new CArchSocketImpl; + socket->m_socket = fd; + socket->m_refCount = 1; + socket->m_event = WSACreateEvent_winsock(); + socket->m_pollWrite = false; return socket; } @@ -228,23 +233,14 @@ CArchNetworkWinsock::closeSocket(CArchSocket s) // close the socket if necessary if (doClose) { - do { - if (close_winsock(s->m_socket) == SOCKET_ERROR) { - // close failed - int err = getsockerror_winsock(); - if (err == WSAEINTR) { - // interrupted system call - ARCH->testCancelThread(); - continue; - } - - // restore the last ref and throw - ARCH->lockMutex(m_mutex); - ++s->m_refCount; - ARCH->unlockMutex(m_mutex); - throwError(err); - } - } while (false); + if (close_winsock(s->m_socket) == SOCKET_ERROR) { + // close failed. restore the last ref and throw. + int err = getsockerror_winsock(); + ARCH->lockMutex(m_mutex); + ++s->m_refCount; + ARCH->unlockMutex(m_mutex); + throwError(err); + } WSACloseEvent_winsock(s->m_event); delete s; } @@ -306,26 +302,31 @@ CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) CArchNetAddress tmp = CArchNetAddressImpl::alloc(sizeof(struct sockaddr)); // accept on socket - SOCKET fd; - do { - fd = accept_winsock(s->m_socket, &tmp->m_addr, &tmp->m_len); - if (fd == INVALID_SOCKET) { - int err = getsockerror_winsock(); - delete socket; - free(tmp); - *addr = NULL; - if (err == WSAEINTR) { - // interrupted system call - ARCH->testCancelThread(); - return NULL; - } - throwError(err); + SOCKET fd = accept_winsock(s->m_socket, &tmp->m_addr, &tmp->m_len); + if (fd == INVALID_SOCKET) { + int err = getsockerror_winsock(); + delete socket; + free(tmp); + *addr = NULL; + if (err == WSAEWOULDBLOCK) { + return NULL; } - } while (false); + throwError(err); + } + + try { + setBlockingOnSocket(fd, false); + } + catch (...) { + close(fd); + delete socket; + free(tmp); + *addr = NULL; + throw; + } // initialize socket socket->m_socket = fd; - socket->m_connected = true; socket->m_refCount = 1; socket->m_event = WSACreateEvent_winsock(); socket->m_pollWrite = true; @@ -339,39 +340,23 @@ CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) return socket; } -void +bool CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr) { assert(s != NULL); assert(addr != NULL); - do { - if (connect_winsock(s->m_socket, &addr->m_addr, - addr->m_len) == SOCKET_ERROR) { - if (getsockerror_winsock() == WSAEINTR) { - // interrupted system call - ARCH->testCancelThread(); - continue; - } - - if (getsockerror_winsock() == WSAEISCONN) { - // already connected - break; - } - - if (getsockerror_winsock() == WSAEWOULDBLOCK) { - // connecting - throw XArchNetworkConnecting(new XArchEvalWinsock( - getsockerror_winsock())); - } - - throwError(getsockerror_winsock()); + if (connect_winsock(s->m_socket, &addr->m_addr, + addr->m_len) == SOCKET_ERROR) { + if (getsockerror_winsock() == WSAEISCONN) { + return true; } - } while (false); - - ARCH->lockMutex(m_mutex); - s->m_connected = true; - ARCH->unlockMutex(m_mutex); + if (getsockerror_winsock() == WSAEWOULDBLOCK) { + return false; + } + throwError(getsockerror_winsock()); + } + return true; } int @@ -540,23 +525,14 @@ CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len) { assert(s != NULL); - int n; - do { - n = recv_winsock(s->m_socket, buf, len, 0); - if (n == SOCKET_ERROR) { - if (getsockerror_winsock() == WSAEINTR) { - // interrupted system call - n = 0; - break; - } - else if (getsockerror_winsock() == WSAEWOULDBLOCK) { - n = 0; - break; - } - throwError(getsockerror_winsock()); + int n = recv_winsock(s->m_socket, buf, len, 0); + if (n == SOCKET_ERROR) { + int err = getsockerror_winsock(); + if (err == WSAEINTR || err == WSAEWOULDBLOCK) { + return 0; } - } while (false); - ARCH->testCancelThread(); + throwError(err); + } return static_cast(n); } @@ -565,24 +541,18 @@ CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len) { assert(s != NULL); - int n; - do { - n = send_winsock(s->m_socket, buf, len, 0); - if (n == SOCKET_ERROR) { - if (getsockerror_winsock() == WSAEINTR) { - // interrupted system call - n = 0; - break; - } - else if (getsockerror_winsock() == WSAEWOULDBLOCK) { - s->m_pollWrite = true; - n = 0; - break; - } - throwError(getsockerror_winsock()); + int n = send_winsock(s->m_socket, buf, len, 0); + if (n == SOCKET_ERROR) { + int err = getsockerror_winsock(); + if (err == WSAEINTR) { + return 0; } - } while (false); - ARCH->testCancelThread(); + if (err == WSAEWOULDBLOCK) { + s->m_pollWrite = true; + return 0; + } + throwError(err); + } return static_cast(n); } @@ -605,17 +575,15 @@ CArchNetworkWinsock::throwErrorOnSocket(CArchSocket s) } } -bool -CArchNetworkWinsock::setBlockingOnSocket(CArchSocket s, bool blocking) +void +CArchNetworkWinsock::setBlockingOnSocket(SOCKET s, bool blocking) { assert(s != NULL); int flag = blocking ? 0 : 1; - if (ioctl_winsock(s->m_socket, FIONBIO, &flag) == SOCKET_ERROR) { + if (ioctl_winsock(s, FIONBIO, &flag) == SOCKET_ERROR) { throwError(getsockerror_winsock()); } - // FIXME -- can't get the current blocking state of socket? - return true; } bool @@ -841,9 +809,6 @@ void CArchNetworkWinsock::throwError(int err) { switch (err) { - case WSAEWOULDBLOCK: - throw XArchNetworkWouldBlock(new XArchEvalWinsock(err)); - case WSAEACCES: throw XArchNetworkAccess(new XArchEvalWinsock(err)); @@ -890,10 +855,6 @@ CArchNetworkWinsock::throwError(int err) case WSAECONNREFUSED: throw XArchNetworkConnectionRefused(new XArchEvalWinsock(err)); - case WSAEINPROGRESS: - case WSAEALREADY: - throw XArchNetworkConnecting(new XArchEvalWinsock(err)); - case WSAEHOSTDOWN: case WSAETIMEDOUT: throw XArchNetworkTimedOut(new XArchEvalWinsock(err)); diff --git a/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h index fd11dba9..8124019c 100644 --- a/lib/arch/CArchNetworkWinsock.h +++ b/lib/arch/CArchNetworkWinsock.h @@ -31,7 +31,6 @@ class CArchSocketImpl { public: SOCKET m_socket; - bool m_connected; int m_refCount; WSAEVENT m_event; bool m_pollWrite; @@ -63,14 +62,13 @@ public: virtual void bindSocket(CArchSocket s, CArchNetAddress addr); virtual void listenOnSocket(CArchSocket s); virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); - virtual void connectSocket(CArchSocket s, CArchNetAddress name); + virtual bool connectSocket(CArchSocket s, CArchNetAddress name); virtual int pollSocket(CPollEntry[], int num, double timeout); virtual void unblockPollSocket(CArchThread thread); virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len); virtual void throwErrorOnSocket(CArchSocket); - virtual bool setBlockingOnSocket(CArchSocket, bool blocking); virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); virtual std::string getHostName(); virtual CArchNetAddress newAnyAddr(EAddressFamily); @@ -88,6 +86,8 @@ public: private: void init(HMODULE); + void setBlockingOnSocket(SOCKET, bool blocking); + void throwError(int); void throwNameError(int); diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h index 245748ec..2c821afb 100644 --- a/lib/arch/IArchNetwork.h +++ b/lib/arch/IArchNetwork.h @@ -152,25 +152,21 @@ public: end. \c addr may be NULL if the remote address isn't required. The original socket \c s is unaffected and remains in the listening state. The new socket shares most of the properties of \c s except - it's not in the listening state, it's connected, and is not - non-blocking even is \c s is. - - This call blocks if \c s is not non-blocking and there are no - pending connection requests. - - (Cancellation point) + it's not in the listening state and it's connected. Returns NULL + if there are no pending connection requests. */ virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr) = 0; //! Connect socket /*! - Connects the socket \c s to the remote address \c addr. This call - blocks if \c s is not non-blocking. If \c s is non-blocking then - the client can \c poll() for writability to detect a connection. - - (Cancellation point) + Connects the socket \c s to the remote address \c addr. Returns + true if the connection succeed immediately, false if the connection + is in progress, and throws if the connection failed immediately. + If it returns false, \c pollSocket() can be used to wait on the + socket for writing to detect when the connection finally succeeds + or fails. */ - virtual void connectSocket(CArchSocket s, CArchNetAddress addr) = 0; + virtual bool connectSocket(CArchSocket s, CArchNetAddress addr) = 0; //! Check socket state /*! @@ -202,12 +198,7 @@ public: Read up to \c len bytes from socket \c s in \c buf and return the number of bytes read. The number of bytes can be less than \c len if not enough data is available. Returns 0 if the remote end has - disconnected and there is no more queued received data. Blocks if - the socket is not non-blocking and there is no queued received data. - If non-blocking and there is no queued received data then throws - XArchNetworkWouldBlock. - - (Cancellation point) + disconnected and/or there is no more queued received data. */ virtual size_t readSocket(CArchSocket s, void* buf, size_t len) = 0; @@ -215,12 +206,8 @@ public: /*! Write up to \c len bytes to socket \c s from \c buf and return the number of bytes written. The number of bytes can be less than - \c len if the remote end disconnected or the socket is non-blocking - and the internal buffers are full. If non-blocking and the internal - buffers are full before any data is written then throws - XArchNetworkWouldBlock. - - (Cancellation point) + \c len if the remote end disconnected or the internal buffers fill + up. */ virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len) = 0; @@ -232,14 +219,6 @@ public: */ virtual void throwErrorOnSocket(CArchSocket s) = 0; - //! Set socket to (non-)blocking operation - /*! - Set socket to block or not block on accept, connect, poll, read and - write (i.e. calls that may take an arbitrary amount of time). - Returns the previous state. - */ - virtual bool setBlockingOnSocket(CArchSocket, bool blocking) = 0; - //! Turn Nagle algorithm on or off on socket /*! Set socket to send messages immediately (true) or to collect small diff --git a/lib/arch/XArch.h b/lib/arch/XArch.h index 45b7a0e8..75083649 100644 --- a/lib/arch/XArch.h +++ b/lib/arch/XArch.h @@ -90,8 +90,8 @@ library to indicate various errors. */ XARCH_SUBCLASS(XArchNetwork, XArch); -//! Operation would block -XARCH_SUBCLASS(XArchNetworkWouldBlock, XArchNetwork); +//! Operation was interrupted +XARCH_SUBCLASS(XArchNetworkInterrupted, XArchNetwork); //! Network insufficient permission XARCH_SUBCLASS(XArchNetworkAccess, XArchNetwork); @@ -126,9 +126,6 @@ XARCH_SUBCLASS(XArchNetworkDisconnected, XArchNetwork); //! Remote end of socket refused connection XARCH_SUBCLASS(XArchNetworkConnectionRefused, XArchNetwork); -//! Connection is in progress -XARCH_SUBCLASS(XArchNetworkConnecting, XArchNetwork); - //! Remote end of socket is not responding XARCH_SUBCLASS(XArchNetworkTimedOut, XArchNetwork); diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index 3f503d11..296fa42c 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -61,7 +61,6 @@ CTCPListenSocket::bind(const CNetworkAddress& addr) CLock lock(m_mutex); ARCH->bindSocket(m_socket, addr.getAddress()); ARCH->listenOnSocket(m_socket); - ARCH->setBlockingOnSocket(m_socket, false); CSocketMultiplexer::getInstance()->addSocket(this, new TSocketMultiplexerMethodJob( this, &CTCPListenSocket::serviceListening, @@ -104,10 +103,12 @@ CTCPListenSocket::accept() try { IDataSocket* socket = new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); - CSocketMultiplexer::getInstance()->addSocket(this, + if (socket != NULL) { + CSocketMultiplexer::getInstance()->addSocket(this, new TSocketMultiplexerMethodJob( this, &CTCPListenSocket::serviceListening, m_socket, true, false)); + } return socket; } catch (XArchNetwork&) { diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 78dc540a..7c9b12ce 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -275,13 +275,14 @@ CTCPSocket::connect(const CNetworkAddress& addr) } try { - ARCH->connectSocket(m_socket, addr.getAddress()); - sendSocketEvent(getConnectedEvent()); - onConnected(); - } - catch (XArchNetworkConnecting&) { - // connection is in progress - m_writable = true; + if (ARCH->connectSocket(m_socket, addr.getAddress())) { + sendSocketEvent(getConnectedEvent()); + onConnected(); + } + else { + // connection is in progress + m_writable = true; + } } catch (XArchNetwork& e) { throw XSocketConnect(e.what()); @@ -299,9 +300,6 @@ CTCPSocket::init() m_writable = false; try { - // make socket non-blocking - ARCH->setBlockingOnSocket(m_socket, false); - // turn off Nagle algorithm. we send lots of very short messages // that should be sent without (much) delay. for example, the // mouse motion messages are much less useful if they're delayed. From 1ccb92b888be10e6931eae96ebd04f359548fb15 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 29 Feb 2004 16:48:22 +0000 Subject: [PATCH 588/807] Fixed BSD unblockPollSocket(). Was signaling to break out of poll() but there was a race condition where the thread trying to unblock poll() could send the signal before the polling thread had entered poll(). Now using a pipe and polling on that and the client's sockets, and just writing a byte into the pipe to unblock poll. This persists until the next call to poll() so we might force poll() to return once unnecessarily but that's not a problem. This change makes the BSD code similar to the winsock code, which uses a winsock event instead of a pipe. --- lib/arch/CArchMultithreadPosix.cpp | 22 ++- lib/arch/CArchMultithreadPosix.h | 4 +- lib/arch/CArchNetworkBSD.cpp | 285 ++++++++++++++++++----------- lib/arch/CArchNetworkBSD.h | 2 + 4 files changed, 203 insertions(+), 110 deletions(-) diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index b87a9b58..ea292412 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -69,6 +69,7 @@ public: bool m_cancelling; bool m_exited; void* m_result; + void* m_networkData; }; CArchThreadImpl::CArchThreadImpl() : @@ -79,7 +80,8 @@ CArchThreadImpl::CArchThreadImpl() : m_cancel(false), m_cancelling(false), m_exited(false), - m_result(NULL) + m_result(NULL), + m_networkData(NULL) { // do nothing } @@ -149,9 +151,21 @@ CArchMultithreadPosix::~CArchMultithreadPosix() } void -CArchMultithreadPosix::unblockThread(CArchThread thread) +CArchMultithreadPosix::setNetworkDataForCurrentThread(void* data) { - pthread_kill(thread->m_thread, SIGWAKEUP); + lockMutex(m_threadMutex); + CArchThreadImpl* thread = find(pthread_self()); + thread->m_networkData = data; + unlockMutex(m_threadMutex); +} + +void* +CArchMultithreadPosix::getNetworkDataForThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* data = thread->m_networkData; + unlockMutex(m_threadMutex); + return data; } CArchMultithreadPosix* @@ -579,7 +593,7 @@ CArchMultithreadPosix::raiseSignal(ESignal signal) lockMutex(m_threadMutex); if (m_signalFunc[signal] != NULL) { m_signalFunc[signal](signal, m_signalUserData[signal]); - unblockThread(m_mainThread); + pthread_kill(m_mainThread->m_thread, SIGWAKEUP); } else if (signal == kINTERRUPT || signal == kTERMINATE) { ARCH->cancelThread(m_mainThread); diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h index 2ac6183b..4e587cfc 100644 --- a/lib/arch/CArchMultithreadPosix.h +++ b/lib/arch/CArchMultithreadPosix.h @@ -40,12 +40,14 @@ public: //! @name manipulators //@{ - void unblockThread(CArchThread thread); + void setNetworkDataForCurrentThread(void*); //@} //! @name accessors //@{ + void* getNetworkDataForThread(CArchThread); + static CArchMultithreadPosix* getInstance(); //@} diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index e7f64a43..44b1a39d 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -257,7 +257,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) // allocate space for translated query struct pollfd* pfd = reinterpret_cast( - alloca(num * sizeof(struct pollfd))); + alloca((1 + num) * sizeof(struct pollfd))); // translate query for (int i = 0; i < num; ++i) { @@ -270,21 +270,43 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) pfd[i].events |= POLLOUT; } } + int n = num; + + // add the unblock pipe + const int* unblockPipe = getUnblockPipe(); + if (unblockPipe != NULL) { + pfd[n].fd = unblockPipe[0]; + pfd[n].events = POLLIN; + ++n; + } + + // prepare timeout + int t = (timeout < 0.0) ? -1 : static_cast(1000.0 * timeout); // do the poll - int t = (timeout < 0.0) ? -1 : static_cast(1000.0 * timeout); - int n; - do { - n = poll(pfd, num, t); - if (n == -1) { - if (errno == EINTR) { - // interrupted system call - ARCH->testCancelThread(); - return 0; - } - throwError(errno); + n = poll(pfd, n, t); + + // reset the unblock pipe + if (unblockPipe != NULL && (pfd[num].revents & POLLIN) != 0) { + // the unblock event was signalled. flush the pipe. + char dummy[100]; + do { + read(unblockPipe[0], dummy, sizeof(dummy)); + } while (errno != EAGAIN); + + // don't count this unblock pipe in return value + --n; + } + + // handle results + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + return 0; } - } while (false); + throwError(errno); + } // translate back for (int i = 0; i < num; ++i) { @@ -313,102 +335,119 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) { int i, n; - do { - // prepare sets for select - n = 0; - fd_set readSet, writeSet, errSet; - fd_set* readSetP = NULL; - fd_set* writeSetP = NULL; - fd_set* errSetP = NULL; - FD_ZERO(&readSet); - FD_ZERO(&writeSet); - FD_ZERO(&errSet); - for (i = 0; i < num; ++i) { - // reset return flags - pe[i].m_revents = 0; + // prepare sets for select + n = 0; + fd_set readSet, writeSet, errSet; + fd_set* readSetP = NULL; + fd_set* writeSetP = NULL; + fd_set* errSetP = NULL; + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&errSet); + for (i = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; - // set invalid flag if socket is bogus then go to next socket - if (pe[i].m_socket == NULL) { - pe[i].m_revents |= kPOLLNVAL; - continue; - } - - int fdi = pe[i].m_socket->m_fd; - if (pe[i].m_events & kPOLLIN) { - FD_SET(pe[i].m_socket->m_fd, &readSet); - readSetP = &readSet; - if (fdi > n) { - n = fdi; - } - } - if (pe[i].m_events & kPOLLOUT) { - FD_SET(pe[i].m_socket->m_fd, &writeSet); - writeSetP = &writeSet; - if (fdi > n) { - n = fdi; - } - } - if (true) { - FD_SET(pe[i].m_socket->m_fd, &errSet); - errSetP = &errSet; - if (fdi > n) { - n = fdi; - } - } + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; } - // if there are no sockets then don't block forever - if (n == 0 && timeout < 0.0) { - timeout = 0.0; - } - - // prepare timeout for select - struct timeval timeout2; - struct timeval* timeout2P; - if (timeout < 0.0) { - timeout2P = NULL; - } - else { - timeout2P = &timeout2; - timeout2.tv_sec = static_cast(timeout); - timeout2.tv_usec = static_cast(1.0e+6 * - (timeout - timeout2.tv_sec)); - } - - // do the select - n = select((SELECT_TYPE_ARG1) n + 1, - SELECT_TYPE_ARG234 readSetP, - SELECT_TYPE_ARG234 writeSetP, - SELECT_TYPE_ARG234 errSetP, - SELECT_TYPE_ARG5 timeout2P); - - // handle results - if (n == -1) { - if (errno == EINTR) { - // interrupted system call - ARCH->testCancelThread(); - return 0; - } - throwError(errno); - } - n = 0; - for (i = 0; i < num; ++i) { - if (pe[i].m_socket != NULL) { - if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) { - pe[i].m_revents |= kPOLLIN; - } - if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) { - pe[i].m_revents |= kPOLLOUT; - } - if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) { - pe[i].m_revents |= kPOLLERR; - } - } - if (pe[i].m_revents != 0) { - ++n; + int fdi = pe[i].m_socket->m_fd; + if (pe[i].m_events & kPOLLIN) { + FD_SET(pe[i].m_socket->m_fd, &readSet); + readSetP = &readSet; + if (fdi > n) { + n = fdi; } } - } while (false); + if (pe[i].m_events & kPOLLOUT) { + FD_SET(pe[i].m_socket->m_fd, &writeSet); + writeSetP = &writeSet; + if (fdi > n) { + n = fdi; + } + } + if (true) { + FD_SET(pe[i].m_socket->m_fd, &errSet); + errSetP = &errSet; + if (fdi > n) { + n = fdi; + } + } + } + + // add the unblock pipe + const int* unblockPipe = getUnblockPipe(); + if (unblockPipe != NULL) { + FD_SET(unblockPipe[0], &readSet); + readSetP = &readSet; + if (unblockPipe[0] > n) { + n = unblockPipe[0]; + } + } + + // if there are no sockets then don't block forever + if (n == 0 && timeout < 0.0) { + timeout = 0.0; + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0.0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = static_cast(timeout); + timeout2.tv_usec = static_cast(1.0e+6 * + (timeout - timeout2.tv_sec)); + } + + // do the select + n = select((SELECT_TYPE_ARG1) n + 1, + SELECT_TYPE_ARG234 readSetP, + SELECT_TYPE_ARG234 writeSetP, + SELECT_TYPE_ARG234 errSetP, + SELECT_TYPE_ARG5 timeout2P); + + // reset the unblock pipe + if (unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) { + // the unblock event was signalled. flush the pipe. + char dummy[100]; + do { + read(unblockPipe[0], dummy, sizeof(dummy)); + } while (errno != EAGAIN); + } + + // handle results + if (n == -1) { + if (errno == EINTR) { + // interrupted system call + ARCH->testCancelThread(); + return 0; + } + throwError(errno); + } + n = 0; + for (i = 0; i < num; ++i) { + if (pe[i].m_socket != NULL) { + if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) { + pe[i].m_revents |= kPOLLIN; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) { + pe[i].m_revents |= kPOLLOUT; + } + if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) { + pe[i].m_revents |= kPOLLERR; + } + } + if (pe[i].m_revents != 0) { + ++n; + } + } return n; } @@ -418,7 +457,11 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) void CArchNetworkBSD::unblockPollSocket(CArchThread thread) { - CArchMultithreadPosix::getInstance()->unblockThread(thread); + const int* unblockPipe = getUnblockPipeForThread(thread); + if (unblockPipe != NULL) { + char dummy = 0; + write(unblockPipe[1], &dummy, 1); + } } size_t @@ -739,6 +782,38 @@ CArchNetworkBSD::isEqualAddr(CArchNetAddress a, CArchNetAddress b) memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0); } +const int* +CArchNetworkBSD::getUnblockPipe() +{ + CArchMultithreadPosix* mt = CArchMultithreadPosix::getInstance(); + return getUnblockPipeForThread(mt->newCurrentThread()); +} + +const int* +CArchNetworkBSD::getUnblockPipeForThread(CArchThread thread) +{ + CArchMultithreadPosix* mt = CArchMultithreadPosix::getInstance(); + int* unblockPipe = (int*)mt->getNetworkDataForThread(thread); + if (unblockPipe == NULL) { + unblockPipe = new int[2]; + if (pipe(unblockPipe) != -1) { + try { + setBlockingOnSocket(unblockPipe[0], false); + mt->setNetworkDataForCurrentThread(unblockPipe); + } + catch (...) { + delete[] unblockPipe; + unblockPipe = NULL; + } + } + else { + delete[] unblockPipe; + unblockPipe = NULL; + } + } + return unblockPipe; +} + void CArchNetworkBSD::throwError(int err) { diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index 06f7f507..2aaf75d7 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -79,6 +79,8 @@ public: virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress); private: + const int* getUnblockPipe(); + const int* getUnblockPipeForThread(CArchThread); void setBlockingOnSocket(int fd, bool blocking); void throwError(int); void throwNameError(int); From c3135b1b1c09b2bc61122ed2b2cdf690bb06bf7f Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 29 Feb 2004 17:28:51 +0000 Subject: [PATCH 589/807] Moved clipboard changed event to CClientProxy because only it and CServer use it. CServerProxy instead makes a direct call to CClient, like it does for most other messages. --- lib/client/CClient.cpp | 12 ------------ lib/client/CClient.h | 1 - lib/server/CClientProxy.cpp | 12 ++++++++++-- lib/server/CClientProxy.h | 9 +++++++++ lib/server/CServer.cpp | 4 ++-- lib/synergy/IScreen.cpp | 8 -------- lib/synergy/IScreen.h | 9 --------- 7 files changed, 21 insertions(+), 34 deletions(-) diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index da227d2a..2d5ee1f1 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -386,10 +386,6 @@ CClient::setupScreen() getEventTarget(), new TMethodEventJob(this, &CClient::handleClipboardGrabbed)); - EVENTQUEUE->adoptHandler(IScreen::getClipboardChangedEvent(), - getEventTarget(), - new TMethodEventJob(this, - &CClient::handleClipboardChanged)); EVENTQUEUE->adoptHandler(CServerProxy::getHandshakeCompleteEvent(), m_server, new TMethodEventJob(this, @@ -453,8 +449,6 @@ CClient::cleanupScreen() getEventTarget()); EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(), getEventTarget()); - EVENTQUEUE->removeHandler(IScreen::getClipboardChangedEvent(), - getEventTarget()); delete m_server; m_server = NULL; } @@ -566,12 +560,6 @@ CClient::handleClipboardGrabbed(const CEvent& event, void*) } } -void -CClient::handleClipboardChanged(const CEvent&, void*) -{ - // ignore -- we'll check the clipboard when we leave -} - void CClient::handleHello(const CEvent&, void*) { diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 3595a719..70386413 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -155,7 +155,6 @@ private: void handleHandshakeComplete(const CEvent&, void*); void handleShapeChanged(const CEvent&, void*); void handleClipboardGrabbed(const CEvent&, void*); - void handleClipboardChanged(const CEvent&, void*); void handleHello(const CEvent&, void*); private: diff --git a/lib/server/CClientProxy.cpp b/lib/server/CClientProxy.cpp index e5dc02c0..99772cd9 100644 --- a/lib/server/CClientProxy.cpp +++ b/lib/server/CClientProxy.cpp @@ -21,8 +21,9 @@ // CClientProxy // -CEvent::Type CClientProxy::s_readyEvent = CEvent::kUnknown; -CEvent::Type CClientProxy::s_disconnectedEvent = CEvent::kUnknown; +CEvent::Type CClientProxy::s_readyEvent = CEvent::kUnknown; +CEvent::Type CClientProxy::s_disconnectedEvent = CEvent::kUnknown; +CEvent::Type CClientProxy::s_clipboardChangedEvent= CEvent::kUnknown; CClientProxy::CClientProxy(const CString& name, IStream* stream) : m_name(name), @@ -72,6 +73,13 @@ CClientProxy::getDisconnectedEvent() "CClientProxy::disconnected"); } +CEvent::Type +CClientProxy::getClipboardChangedEvent() +{ + return CEvent::registerTypeOnce(s_clipboardChangedEvent, + "CClientProxy::clipboardChanged"); +} + void* CClientProxy::getEventTarget() const { diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index 4a95ebc5..9453a203 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -64,6 +64,14 @@ public: */ static CEvent::Type getDisconnectedEvent(); + //! Get clipboard changed event type + /*! + Returns the clipboard changed event type. This is sent whenever the + contents of the clipboard has changed. The data is a pointer to a + IScreen::CClipboardInfo. + */ + static CEvent::Type getClipboardChangedEvent(); + //@} // IScreen @@ -100,6 +108,7 @@ private: static CEvent::Type s_readyEvent; static CEvent::Type s_disconnectedEvent; + static CEvent::Type s_clipboardChangedEvent; }; #endif diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index d129ab96..56432af9 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -1425,7 +1425,7 @@ CServer::addClient(IClient* client) client->getEventTarget(), new TMethodEventJob(this, &CServer::handleClipboardGrabbed, client)); - EVENTQUEUE->adoptHandler(IScreen::getClipboardChangedEvent(), + EVENTQUEUE->adoptHandler(CClientProxy::getClipboardChangedEvent(), client->getEventTarget(), new TMethodEventJob(this, &CServer::handleClipboardChanged, client)); @@ -1454,7 +1454,7 @@ CServer::removeClient(IClient* client) client->getEventTarget()); EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(), client->getEventTarget()); - EVENTQUEUE->removeHandler(IScreen::getClipboardChangedEvent(), + EVENTQUEUE->removeHandler(CClientProxy::getClipboardChangedEvent(), client->getEventTarget()); // remove from list diff --git a/lib/synergy/IScreen.cpp b/lib/synergy/IScreen.cpp index 1c55dc5c..fadbe8b3 100644 --- a/lib/synergy/IScreen.cpp +++ b/lib/synergy/IScreen.cpp @@ -21,7 +21,6 @@ CEvent::Type IScreen::s_errorEvent = CEvent::kUnknown; CEvent::Type IScreen::s_shapeChangedEvent = CEvent::kUnknown; CEvent::Type IScreen::s_clipboardGrabbedEvent = CEvent::kUnknown; -CEvent::Type IScreen::s_clipboardChangedEvent = CEvent::kUnknown; CEvent::Type IScreen::getErrorEvent() @@ -43,10 +42,3 @@ IScreen::getClipboardGrabbedEvent() return CEvent::registerTypeOnce(s_clipboardGrabbedEvent, "IScreen::clipboardGrabbed"); } - -CEvent::Type -IScreen::getClipboardChangedEvent() -{ - return CEvent::registerTypeOnce(s_clipboardChangedEvent, - "IScreen::clipboardChanged"); -} diff --git a/lib/synergy/IScreen.h b/lib/synergy/IScreen.h index 2f1b4450..a36f8fb1 100644 --- a/lib/synergy/IScreen.h +++ b/lib/synergy/IScreen.h @@ -85,21 +85,12 @@ public: */ static CEvent::Type getClipboardGrabbedEvent(); - //! Get clipboard changed event type - /*! - Returns the clipboard changed event type. This is sent whenever the - contents of the clipboard has changed. The data is a pointer to a - CClipboardInfo. - */ - static CEvent::Type getClipboardChangedEvent(); - //@} private: static CEvent::Type s_errorEvent; static CEvent::Type s_shapeChangedEvent; static CEvent::Type s_clipboardGrabbedEvent; - static CEvent::Type s_clipboardChangedEvent; }; #endif From b8606412d696b75dc6ac4fe6317c40dddb843301 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 29 Feb 2004 17:29:01 +0000 Subject: [PATCH 590/807] Switched to doxygen comments. --- lib/arch/CArchMultithreadWindows.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h index 44a8d184..d009c842 100644 --- a/lib/arch/CArchMultithreadWindows.h +++ b/lib/arch/CArchMultithreadWindows.h @@ -43,15 +43,14 @@ public: CArchMultithreadWindows(); virtual ~CArchMultithreadWindows(); - // - // manipulators - // + //! @name manipulators + //@{ void setNetworkDataForCurrentThread(void*); - // - // accessors - // + //@} + //! @name accessors + //@{ HANDLE getCancelEventForCurrentThread(); @@ -59,6 +58,8 @@ public: static CArchMultithreadWindows* getInstance(); + //@} + // IArchMultithread overrides virtual CArchCond newCondVar(); virtual void closeCondVar(CArchCond); From 8f083a2e65cbaf1f3cd15a6ab81d5486b2d522f3 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 29 Feb 2004 17:36:32 +0000 Subject: [PATCH 591/807] Fixed comment. --- lib/arch/IArchMultithread.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index d5eaa550..55db7af9 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -142,9 +142,6 @@ public: virtual void closeMutex(CArchMutex) = 0; //! Lock a mutex - /*! - (Cancellation point) - */ virtual void lockMutex(CArchMutex) = 0; //! Unlock a mutex @@ -205,6 +202,8 @@ public: This method does nothing but is a cancellation point. Clients can make their own functions cancellation points by calling this method at appropriate times. + + (Cancellation point) */ virtual void testCancelThread() = 0; From 77dd4c81c4385cb774214dfbcec79ccf28128f30 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 29 Feb 2004 21:31:24 +0000 Subject: [PATCH 592/807] Added reload configuration menu item to win32 task bar. --- cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp | 8 ++++++++ cmd/synergys/resource.h | 3 ++- cmd/synergys/synergys.cpp | 13 +++++++++---- cmd/synergys/synergys.rc | 1 + 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp index 5f5e5a24..3a79cb27 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -15,12 +15,15 @@ #include "CMSWindowsServerTaskBarReceiver.h" #include "CServer.h" #include "CMSWindowsClipboard.h" +#include "IEventQueue.h" #include "LogOutputters.h" #include "BasicTypes.h" #include "CArch.h" #include "CArchTaskBarWindows.h" #include "resource.h" +extern CEvent::Type getReloadConfigEvent(); + // // CMSWindowsServerTaskBarReceiver // @@ -171,6 +174,11 @@ CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) copyLog(); break; + case IDC_RELOAD_CONFIG: + EVENTQUEUE->addEvent(CEvent(getReloadConfigEvent(), + IEventQueue::getSystemTarget())); + break; + case IDC_TASKBAR_QUIT: quit(); break; diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index c52355cf..57fd8c35 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -17,13 +17,14 @@ #define IDC_TASKBAR_QUIT 40003 #define IDC_TASKBAR_STATUS 40004 #define IDC_TASKBAR_LOG 40005 +#define IDC_RELOAD_CONFIG 40006 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 109 -#define _APS_NEXT_COMMAND_VALUE 40006 +#define _APS_NEXT_COMMAND_VALUE 40007 #define _APS_NEXT_CONTROL_VALUE 1003 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index a7b70cd6..c1370197 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -144,6 +144,12 @@ static CClientListener* s_listener = NULL; static CServerTaskBarReceiver* s_taskBarReceiver = NULL; static CEvent::Type s_reloadConfigEvent = CEvent::kUnknown; +CEvent::Type +getReloadConfigEvent() +{ + return CEvent::registerTypeOnce(s_reloadConfigEvent, "reloadConfig"); +} + static void updateStatus() @@ -395,7 +401,7 @@ static void reloadSignalHandler(CArch::ESignal, void*) { - EVENTQUEUE->addEvent(CEvent(s_reloadConfigEvent, + EVENTQUEUE->addEvent(CEvent(getReloadConfigEvent(), IEventQueue::getSystemTarget())); } @@ -454,9 +460,8 @@ mainLoop() } // handle hangup signal by reloading the server's configuration - CEvent::registerTypeOnce(s_reloadConfigEvent, "reloadConfig"); ARCH->setSignalHandler(CArch::kHANGUP, &reloadSignalHandler, NULL); - EVENTQUEUE->adoptHandler(s_reloadConfigEvent, + EVENTQUEUE->adoptHandler(getReloadConfigEvent(), IEventQueue::getSystemTarget(), new CFunctionEventJob(&reloadConfig)); @@ -475,7 +480,7 @@ mainLoop() // close down LOG((CLOG_DEBUG1 "stopping server")); - EVENTQUEUE->removeHandler(s_reloadConfigEvent, + EVENTQUEUE->removeHandler(getReloadConfigEvent(), IEventQueue::getSystemTarget()); stopServer(); updateStatus(); diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index d528c4dd..41e40115 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -71,6 +71,7 @@ BEGIN BEGIN MENUITEM "Show Status", IDC_TASKBAR_STATUS MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG + MENUITEM "Reload Configuration", IDC_RELOAD_CONFIG MENUITEM SEPARATOR MENUITEM "Quit", IDC_TASKBAR_QUIT END From 4ac756fc282e702d591206ee5ac915c702f23859 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 29 Feb 2004 21:32:00 +0000 Subject: [PATCH 593/807] Fixed cursor hiding on win32. Still fails occassionally. --- lib/platform/CMSWindowsScreen.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 21c06a23..1329e4c3 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -1612,11 +1612,23 @@ CMSWindowsScreen::secondaryDeskProc( { switch (msg) { case WM_MOUSEMOVE: - case WM_ACTIVATE: - case WM_ACTIVATEAPP: // hide window ShowWindow(hwnd, SW_HIDE); break; + + case WM_ACTIVATE: + // hide window + if (LOWORD(wParam) == WA_INACTIVE) { + ShowWindow(hwnd, SW_HIDE); + } + break; + + case WM_ACTIVATEAPP: + // hide window + if (!(BOOL)wParam) { + ShowWindow(hwnd, SW_HIDE); + } + break; } return DefWindowProc(hwnd, msg, wParam, lParam); @@ -1726,6 +1738,7 @@ CMSWindowsScreen::deskLeave(CDesk* desk, DWORD& cursorThreadID) if (desk->m_lowLevel) { SetWindowPos(desk->m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1, SWP_NOACTIVATE); + ShowWindow(desk->m_window, SW_SHOW); if (cursorThreadID == 0) { HWND hwnd = GetForegroundWindow(); cursorThreadID = GetWindowThreadProcessId(hwnd, NULL); From ffa966747be2e2f0d111886238907f42da9832c0 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 29 Feb 2004 21:33:20 +0000 Subject: [PATCH 594/807] Fixed handling of winsock connect event. Was always immediately indicating socket had connected. --- lib/arch/CArchNetworkWinsock.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index 0f69c554..484bb8ff 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -196,7 +196,7 @@ CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type) setBlockingOnSocket(fd, false); } catch (...) { - close(fd); + close_winsock(fd); throw; } @@ -205,7 +205,7 @@ CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type) socket->m_socket = fd; socket->m_refCount = 1; socket->m_event = WSACreateEvent_winsock(); - socket->m_pollWrite = false; + socket->m_pollWrite = true; return socket; } @@ -318,7 +318,7 @@ CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) setBlockingOnSocket(fd, false); } catch (...) { - close(fd); + close_winsock(fd); delete socket; free(tmp); *addr = NULL; From 5593573182d7b490f4c11576f898e34d6d0bde7a Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 29 Feb 2004 21:34:30 +0000 Subject: [PATCH 595/807] Fixed processing of events. Was waking up on a sent (rather than posted) message but then blocking in GetMessage() which handles the sent message directly. No longer blocking on sent messages. --- lib/platform/CMSWindowsEventQueueBuffer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/platform/CMSWindowsEventQueueBuffer.cpp b/lib/platform/CMSWindowsEventQueueBuffer.cpp index 99366e1c..2f126095 100644 --- a/lib/platform/CMSWindowsEventQueueBuffer.cpp +++ b/lib/platform/CMSWindowsEventQueueBuffer.cpp @@ -56,7 +56,7 @@ CMSWindowsEventQueueBuffer::waitForEvent(double timeout) // MsgWaitForMultipleObjects() will block even if the queue isn't // empty if the messages in the queue were there before the last // call to GetMessage()/PeekMessage(). - if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + if (HIWORD(GetQueueStatus(QS_ALLEVENTS)) != 0) { return; } @@ -73,7 +73,7 @@ CMSWindowsEventQueueBuffer::waitForEvent(double timeout) // cancellation but that's okay because we're run in the main // thread and we never cancel that thread. HANDLE dummy[1]; - MsgWaitForMultipleObjects(0, dummy, FALSE, t, QS_ALLINPUT); + MsgWaitForMultipleObjects(0, dummy, FALSE, t, QS_ALLEVENTS); } IEventQueueBuffer::Type @@ -113,7 +113,7 @@ CMSWindowsEventQueueBuffer::addEvent(UInt32 dataID) bool CMSWindowsEventQueueBuffer::isEmpty() const { - return (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0); + return (HIWORD(GetQueueStatus(QS_ALLEVENTS)) == 0); } CEventQueueTimer* From a27c6ad2c649d6bef53071110632bd277a263b25 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 6 Mar 2004 16:20:08 +0000 Subject: [PATCH 596/807] Server now disables jump zones when scroll lock is active. --- lib/platform/CXWindowsScreen.cpp | 3 ++- lib/server/CServer.cpp | 32 ++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 8415a39d..de18c1d0 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -1344,7 +1344,8 @@ CXWindowsScreen::doSelectEvents(Window w) const // select events of interest. do this before querying the tree so // we'll get notifications of children created after the XQueryTree() // so we won't miss them. - XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask); + XSelectInput(m_display, w, KeyPressMask | KeyReleaseMask | + PointerMotionMask | SubstructureNotifyMask); // recurse on child windows Window rw, pw, *cw; diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 56432af9..5ee5b946 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -272,8 +272,11 @@ CServer::getDisconnectedEvent() } bool -CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) +CServer::onCommandKey(KeyID id, KeyModifierMask /*mask*/, bool /*down*/) { + if (id == kKeyScrollLock) { + m_primaryClient->reconfigure(getActivePrimarySides()); + } return false; } @@ -291,17 +294,19 @@ UInt32 CServer::getActivePrimarySides() const { UInt32 sides = 0; - if (getNeighbor(m_primaryClient, kLeft) != NULL) { - sides |= kLeftMask; - } - if (getNeighbor(m_primaryClient, kRight) != NULL) { - sides |= kRightMask; - } - if (getNeighbor(m_primaryClient, kTop) != NULL) { - sides |= kTopMask; - } - if (getNeighbor(m_primaryClient, kBottom) != NULL) { - sides |= kBottomMask; + if (!isLockedToScreen()) { + if (getNeighbor(m_primaryClient, kLeft) != NULL) { + sides |= kLeftMask; + } + if (getNeighbor(m_primaryClient, kRight) != NULL) { + sides |= kRightMask; + } + if (getNeighbor(m_primaryClient, kTop) != NULL) { + sides |= kTopMask; + } + if (getNeighbor(m_primaryClient, kBottom) != NULL) { + sides |= kBottomMask; + } } return sides; } @@ -1148,6 +1153,7 @@ void CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) { LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); +LOG((CLOG_INFO "onKeyDown: id=%d mask=0x%04x button=0x%04x", id, mask, button)); assert(m_active != NULL); // handle command keys @@ -1163,6 +1169,7 @@ void CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button) { LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); +LOG((CLOG_INFO "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); assert(m_active != NULL); // handle command keys @@ -1179,6 +1186,7 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button) { LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); +LOG((CLOG_INFO "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); assert(m_active != NULL); // handle command keys From f06823264349512d20b9716000dc38d3da281ab3 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 8 Mar 2004 20:45:53 +0000 Subject: [PATCH 597/807] Typecasting fix to compile on old solaris. --- lib/arch/CArchNetworkBSD.cpp | 9 ++++++--- lib/arch/CArchNetworkBSD.h | 5 +++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 44b1a39d..bfdcf20f 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -502,7 +502,8 @@ CArchNetworkBSD::throwErrorOnSocket(CArchSocket s) // get the error from the socket layer int err = 0; socklen_t size = sizeof(err); - if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR, &err, &size) == -1) { + if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR, + (optval_t*)&err, &size) == -1) { err = errno; } @@ -540,13 +541,15 @@ CArchNetworkBSD::setNoDelayOnSocket(CArchSocket s, bool noDelay) // get old state int oflag; socklen_t size = sizeof(oflag); - if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, &oflag, &size) == -1) { + if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, + (optval_t*)&oflag, &size) == -1) { throwError(errno); } int flag = noDelay ? 1 : 0; size = sizeof(flag); - if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, &flag, size) == -1) { + if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, + (optval_t*)&flag, size) == -1) { throwError(errno); } diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index 2aaf75d7..b656e14a 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -25,6 +25,11 @@ typedef int socklen_t; #endif +// old systems may use char* for [gs]etsockopt()'s optval argument. +// this should be void on modern systems but char is forwards +// compatible so we always use it. +typedef char optval_t; + #define ARCH_NETWORK CArchNetworkBSD class CArchSocketImpl { From 9e995bedbf62b07fe0a31cd2b2cdeaa7988daba9 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 8 Mar 2004 20:53:32 +0000 Subject: [PATCH 598/807] Win32 fixes. Fixed slightly off cursor positioning when using absolute mouse_event(). Improved keyboard handling: now using keyboard layout of last foreground window when leaving server so users can meaningfully choose the locale, moved dead key handling into hook library so there should be no more race conditions involving the keyboard dead key buffer, simplified keyboard and cursor handling by using a full screen transparent window when not using low level hooks, fixed error in restoring buffered dead key when checking for dead keys. This hopefully fixes all known keyboard bugs on win32. --- lib/platform/CMSWindowsKeyMapper.cpp | 156 ++---------- lib/platform/CMSWindowsKeyMapper.h | 23 +- lib/platform/CMSWindowsScreen.cpp | 89 ++++--- lib/platform/CMSWindowsScreen.h | 7 +- lib/platform/CSynergyHook.cpp | 339 +++++++++++++++++++++------ lib/server/CServer.cpp | 16 +- 6 files changed, 353 insertions(+), 277 deletions(-) diff --git a/lib/platform/CMSWindowsKeyMapper.cpp b/lib/platform/CMSWindowsKeyMapper.cpp index de2435a2..8cc2641c 100644 --- a/lib/platform/CMSWindowsKeyMapper.cpp +++ b/lib/platform/CMSWindowsKeyMapper.cpp @@ -706,7 +706,7 @@ const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = CMSWindowsKeyMapper::CMSWindowsKeyMapper() : m_deadKey(0) { - // do nothing + m_keyLayout = GetKeyboardLayout(0); } CMSWindowsKeyMapper::~CMSWindowsKeyMapper() @@ -872,6 +872,12 @@ CMSWindowsKeyMapper::updateKey(KeyButton key, bool pressed) } } +void +CMSWindowsKeyMapper::setKeyLayout(HKL keyLayout) +{ + m_keyLayout = keyLayout; +} + KeyButton CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, const IKeyState& keyState, KeyID id, @@ -1057,8 +1063,8 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, } KeyID -CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM vkCode, LPARAM info, - KeyModifierMask* maskOut, bool* altgr) const +CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM charAndVirtKey, + LPARAM info, KeyModifierMask* maskOut, bool* altgr) const { // note: known microsoft bugs // Q72583 -- MapVirtualKey() maps keypad keys incorrectly @@ -1066,59 +1072,18 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM vkCode, LPARAM info, // 95,98,NT4: num pad scan code -> bad vk code except // SEPARATOR, MULTIPLY, SUBTRACT, ADD - HKL hkl = GetKeyboardLayout(0); + char c = (char)((charAndVirtKey & 0xff00u) >> 8); + UINT vkCode = (charAndVirtKey & 0xffu); // get the scan code and the extended keyboard flag UINT scanCode = static_cast((info & 0x00ff0000u) >> 16); int extended = ((info & 0x01000000) == 0) ? 0 : 1; - bool press = ((info & 0x80000000) == 0); LOG((CLOG_DEBUG1 "key vk=%d info=0x%08x ext=%d scan=%d", vkCode, info, extended, scanCode)); // handle some keys via table lookup - char c = 0; KeyID id = s_virtualKey[vkCode][extended]; if (id == kKeyNone) { - // not in table - - // save the control state then clear it. ToAscii() maps ctrl+letter - // to the corresponding control code and ctrl+backspace to delete. - // we don't want that translation so we clear the control modifier - // state. however, if we want to simulate AltGr (which is ctrl+alt) - // then we must not clear it. - BYTE keys[256]; - memcpy(keys, m_keys, sizeof(keys)); - BYTE control = keys[VK_CONTROL]; - BYTE menu = keys[VK_MENU]; - if ((control & 0x80) == 0 || (menu & 0x80) == 0) { - keys[VK_LCONTROL] = 0; - keys[VK_RCONTROL] = 0; - keys[VK_CONTROL] = 0; - } - else { - keys[VK_LCONTROL] = 0x80; - keys[VK_CONTROL] = 0x80; - keys[VK_LMENU] = 0x80; - keys[VK_MENU] = 0x80; - } - - // map to a character - bool isMenu = ((menu & 0x80) != 0); - c = mapToCharacter(vkCode, scanCode, keys, press, isMenu, hkl); - - // if mapping failed and ctrl and alt are pressed then try again - // with both not pressed. this handles the case where ctrl and - // alt are being used as individual modifiers rather than AltGr. - if (c == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) { - keys[VK_LCONTROL] = 0; - keys[VK_RCONTROL] = 0; - keys[VK_CONTROL] = 0; - keys[VK_LMENU] = 0; - keys[VK_RMENU] = 0; - keys[VK_MENU] = 0; - c = mapToCharacter(vkCode, scanCode, keys, press, isMenu, hkl); - } - - // map character to key id + // not in table; map character to key id if (c != 0) { if ((c & 0x80u) != 0) { // character is not really ASCII. instead it's some @@ -1155,7 +1120,7 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM vkCode, LPARAM info, // required (only because it solves the problems we've seen // so far). in the second, we'll use whatever the keyboard // state says. - WORD virtualKeyAndModifierState = VkKeyScanEx(c, hkl); + WORD virtualKeyAndModifierState = VkKeyScanEx(c, m_keyLayout); if (virtualKeyAndModifierState == 0xffff) { // there is no mapping. assume AltGr. LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); @@ -1233,8 +1198,7 @@ UINT CMSWindowsKeyMapper::keyToScanCode(KeyButton* virtualKey) const { // try mapping given virtual key - HKL hkl = GetKeyboardLayout(0); - UINT code = MapVirtualKeyEx((*virtualKey) & 0xffu, 0, hkl); + UINT code = MapVirtualKeyEx((*virtualKey) & 0xffu, 0, m_keyLayout); if (code != 0) { return code; } @@ -1265,17 +1229,17 @@ CMSWindowsKeyMapper::keyToScanCode(KeyButton* virtualKey) const case VK_LSHIFT: case VK_RSHIFT: *virtualKey = VK_SHIFT; - return MapVirtualKeyEx(VK_SHIFT, 0, hkl); + return MapVirtualKeyEx(VK_SHIFT, 0, m_keyLayout); case VK_LCONTROL: case VK_RCONTROL: *virtualKey = VK_CONTROL; - return MapVirtualKeyEx(VK_CONTROL, 0, hkl); + return MapVirtualKeyEx(VK_CONTROL, 0, m_keyLayout); case VK_LMENU: case VK_RMENU: *virtualKey = VK_MENU; - return MapVirtualKeyEx(VK_MENU, 0, hkl); + return MapVirtualKeyEx(VK_MENU, 0, m_keyLayout); default: return 0; @@ -1528,93 +1492,9 @@ CMSWindowsKeyMapper::isDeadChar(TCHAR c, HKL hkl, bool menu) const toAscii(' ', hkl, 0, &dummy); // put old dead key back if there was one - if (old == 2) { + if (old == 1 && ascii != ' ') { toAscii(static_cast(ascii & 0xffu), hkl, menu, &dummy); } return isDead; } - -bool -CMSWindowsKeyMapper::putBackDeadChar(TCHAR c, HKL hkl, bool menu) const -{ - return (toAscii(c, hkl, menu, NULL) < 0); -} - -TCHAR -CMSWindowsKeyMapper::getSavedDeadChar(HKL hkl) const -{ - WORD old; - int nOld = toAscii(' ', hkl, false, &old); - if (nOld == 1 || nOld == 2) { - TCHAR c = static_cast(old & 0xffu); - if (nOld == 2 || isDeadChar(c, hkl, false)) { - return c; - } - } - return 0; -} - -char -CMSWindowsKeyMapper::mapToCharacter(UINT vkCode, UINT scanCode, - BYTE* keys, bool press, bool isMenu, HKL hkl) const -{ - // get contents of keyboard layout buffer and clear out that - // buffer. we don't want anything placed there by some other - // app interfering and we need to put anything there back in - // place when we're done. - TCHAR oldDeadKey = getSavedDeadChar(hkl); - - // put our previous dead key, if any, in the layout buffer - putBackDeadChar(m_deadKey, hkl, false); - m_deadKey = 0; - - // process key - WORD ascii; - int result = ToAsciiEx(vkCode, scanCode, keys, &ascii, - isMenu ? 1 : 0, hkl); - - // if result is less than zero then it was a dead key - char c = 0; - if (result < 0) { - // save dead key if a key press. we catch the dead key - // release in the result == 2 case below. - if (press) { - m_deadKey = static_cast(ascii & 0xffu); - } - } - - // if result is 1 then the key was succesfully converted - else if (result == 1) { - c = static_cast(ascii & 0xff); - } - - // if result is 2 and the two characters are the same and this - // is a key release then a dead key was released. save the - // dead key. if the two characters are the same and this is - // not a release then a dead key was pressed twice. send the - // dead key. - else if (result == 2) { - if (((ascii & 0xff00u) >> 8) == (ascii & 0x00ffu)) { - if (!press) { - m_deadKey = static_cast(ascii & 0xffu); - } - else { - putBackDeadChar(oldDeadKey, hkl, false); - result = toAscii(' ', hkl, false, &ascii); - c = static_cast((ascii >> 8) & 0xffu); - } - } - } - - // clear keyboard layout buffer. this removes any dead key we - // may have just put there. - toAscii(' ', hkl, false, NULL); - - // restore keyboard layout buffer so a dead key inserted by - // another app doesn't disappear mysteriously (from its point - // of view). - putBackDeadChar(oldDeadKey, hkl, false); - - return c; -} diff --git a/lib/platform/CMSWindowsKeyMapper.h b/lib/platform/CMSWindowsKeyMapper.h index 5bcfcedf..7292e675 100644 --- a/lib/platform/CMSWindowsKeyMapper.h +++ b/lib/platform/CMSWindowsKeyMapper.h @@ -44,6 +44,13 @@ public: */ void updateKey(KeyButton key, bool pressed); + //! Set the active keyboard layout + /*! + Uses \p keyLayout when finding scan codes via \c keyToScanCode() + and mapping keys in \c mapKeyFromEvent(). + */ + void setKeyLayout(HKL keyLayout); + //@} //! @name accessors //@{ @@ -67,7 +74,7 @@ public: to a modifier mask. If \c altgr is non-NULL it's set to true if the key requires AltGr and false otherwise. */ - KeyID mapKeyFromEvent(WPARAM vkCode, LPARAM info, + KeyID mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, KeyModifierMask* maskOut, bool* altgr) const; //! Test shadow key state @@ -127,19 +134,6 @@ private: // return true iff \c c is a dead character bool isDeadChar(TCHAR c, HKL hkl, bool menu) const; - // put back dead key into ToAscii() internal buffer. returns true - // iff the character was a dead key. - bool putBackDeadChar(TCHAR c, HKL hkl, bool menu) const; - - // get the dead key saved in the given keyboard layout, or 0 if none - TCHAR getSavedDeadChar(HKL hkl) const; - - // map the given virtual key, scan code, and keyboard state to a - // character, if possible. this has the side effect of updating - // m_deadKey. - char mapToCharacter(UINT vkCode, UINT scanCode, - BYTE* keys, bool press, bool isMenu, HKL hkl) const; - private: class CModifierKeys { public: @@ -150,6 +144,7 @@ private: BYTE m_keys[256]; mutable TCHAR m_deadKey; + HKL m_keyLayout; static const CModifierKeys s_modifiers[]; static const char* s_vkToName[]; diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 1329e4c3..ae15ffb8 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -128,6 +128,7 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : m_sequenceNumber(0), m_mark(0), m_markReceived(0), + m_keyLayout(NULL), m_timer(NULL), m_screensaver(NULL), m_screensaverNotify(false), @@ -290,7 +291,17 @@ CMSWindowsScreen::enter() bool CMSWindowsScreen::leave() { - sendDeskMessage(SYNERGY_MSG_LEAVE, 0, 0); + // get keyboard layout of foreground window. we'll use this + // keyboard layout for translating keys sent to clients. + HWND window = GetForegroundWindow(); + DWORD thread = GetWindowThreadProcessId(window, NULL); + m_keyLayout = GetKeyboardLayout(thread); + + // tell the key mapper about the keyboard layout + m_keyMapper.setKeyLayout(m_keyLayout); + + // tell desk that we're leaving and tell it the keyboard layout + sendDeskMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0); if (m_isPrimary) { /* XXX @@ -543,7 +554,7 @@ CMSWindowsScreen::fakeKeyEvent(KeyButton virtualKey, bool press) const if (!press) { flags |= KEYEVENTF_KEYUP; } - const UINT code = m_keyMapper.keyToScanCode(&virtualKey); + UINT code = m_keyMapper.keyToScanCode(&virtualKey); sendDeskMessage(SYNERGY_MSG_FAKE_KEY, flags, MAKEWORD(static_cast(code), static_cast(virtualKey & 0xffu))); @@ -977,6 +988,9 @@ CMSWindowsScreen::onMark(UInt32 mark) bool CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) { + WPARAM charAndVirtKey = wParam; + wParam &= 0xffu; + // ignore message if posted prior to last mark change if (!ignore()) { // check for ctrl+alt+del emulation @@ -992,8 +1006,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) // process key normally bool altgr; KeyModifierMask mask; - const KeyID key = m_keyMapper.mapKeyFromEvent(wParam, - lParam, &mask, &altgr); + const KeyID key = m_keyMapper.mapKeyFromEvent( + charAndVirtKey, lParam, &mask, &altgr); KeyButton button = static_cast( (lParam & 0x00ff0000u) >> 16); if (key != kKeyNone && key != kKeyMultiKey) { @@ -1654,8 +1668,8 @@ CMSWindowsScreen::deskMouseMove(SInt32 x, SInt32 y) const SInt32 w = GetSystemMetrics(SM_CXSCREEN); SInt32 h = GetSystemMetrics(SM_CYSCREEN); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65536.0 * x) / w), - (DWORD)((65536.0 * y) / h), + (DWORD)((65535.0f * x) / (w - 1) + 0.5f), + (DWORD)((65535.0f * y) / (h - 1) + 0.5f), 0, 0); } @@ -1712,43 +1726,45 @@ CMSWindowsScreen::deskMouseMove(SInt32 x, SInt32 y) const } void -CMSWindowsScreen::deskEnter(CDesk* desk, DWORD& cursorThreadID) +CMSWindowsScreen::deskEnter(CDesk* desk) { if (m_isPrimary) { - if (desk->m_lowLevel) { - if (cursorThreadID != 0) { - AttachThreadInput(desk->m_threadID, cursorThreadID, TRUE); - ShowCursor(TRUE); - AttachThreadInput(desk->m_threadID, cursorThreadID, FALSE); - cursorThreadID = 0; - } - } + ShowCursor(TRUE); } ShowWindow(desk->m_window, SW_HIDE); } void -CMSWindowsScreen::deskLeave(CDesk* desk, DWORD& cursorThreadID) +CMSWindowsScreen::deskLeave(CDesk* desk, HKL keyLayout) { if (m_isPrimary) { - // we don't need a window to capture input but we need a window - // to hide the cursor when using low-level hooks. also take the - // activation so we use our keyboard layout, not the layout of - // whatever window was active. + // map a window to hide the cursor and to use whatever keyboard + // layout we choose rather than the keyboard layout of the last + // active window. + int x, y, w, h; if (desk->m_lowLevel) { - SetWindowPos(desk->m_window, HWND_TOPMOST, - m_xCenter, m_yCenter, 1, 1, SWP_NOACTIVATE); - ShowWindow(desk->m_window, SW_SHOW); - if (cursorThreadID == 0) { - HWND hwnd = GetForegroundWindow(); - cursorThreadID = GetWindowThreadProcessId(hwnd, NULL); - if (cursorThreadID != 0) { - AttachThreadInput(desk->m_threadID, cursorThreadID, TRUE); - ShowCursor(FALSE); - AttachThreadInput(desk->m_threadID, cursorThreadID, FALSE); - } - } + // with a low level hook the cursor will never budge so + // just a 1x1 window is sufficient. + x = m_xCenter; + y = m_yCenter; + w = 1; + h = 1; } + else { + // with regular hooks the cursor will jitter as it's moved + // by the user then back to the center by us. to be sure + // we never lose it, cover all the monitors with the window. + x = m_x; + y = m_y; + w = m_w; + h = m_h; + } + SetWindowPos(desk->m_window, HWND_TOPMOST, x, y, w, h, SWP_NOACTIVATE); + ShowWindow(desk->m_window, SW_SHOW); + ShowCursor(FALSE); + + // switch to requested keyboard layout + ActivateKeyboardLayout(keyLayout, 0); } else { // move hider window under the cursor center @@ -1767,9 +1783,6 @@ CMSWindowsScreen::deskThread(void* vdesk) { MSG msg; - // id of thread that had cursor when we were last told to hide it - DWORD cursorThreadID = 0; - // use given desktop for this thread CDesk* desk = reinterpret_cast(vdesk); desk->m_threadID = GetCurrentThreadId(); @@ -1833,11 +1846,11 @@ CMSWindowsScreen::deskThread(void* vdesk) break; case SYNERGY_MSG_ENTER: - deskEnter(desk, cursorThreadID); + deskEnter(desk); break; case SYNERGY_MSG_LEAVE: - deskLeave(desk, cursorThreadID); + deskLeave(desk, (HKL)msg.wParam); break; case SYNERGY_MSG_FAKE_KEY: @@ -1969,7 +1982,7 @@ CMSWindowsScreen::checkDesk() // hide cursor on new desk if (!m_isOnScreen) { - sendDeskMessage(SYNERGY_MSG_LEAVE, 0, 0); + sendDeskMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0); } } } diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index ddae1f02..15c8dfc9 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -186,8 +186,8 @@ private: static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM); void deskMouseMove(SInt32 x, SInt32 y) const; - void deskEnter(CDesk* desk, DWORD& cursorThreadID); - void deskLeave(CDesk* desk, DWORD& cursorThreadID); + void deskEnter(CDesk* desk); + void deskLeave(CDesk* desk, HKL keyLayout); void deskThread(void* vdesk); CDesk* addDesk(const CString& name, HDESK hdesk); void removeDesks(); @@ -234,6 +234,9 @@ private: // the main loop's thread id DWORD m_threadID; + // the keyboard layout to use when off primary screen + HKL m_keyLayout; + // the timer used to check for desktop switching CEventQueueTimer* m_timer; diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 4b58ac8d..f7003c4a 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -24,6 +24,12 @@ // #define NO_GRAB_KEYBOARD 0 +// +// debugging compile flag. when not zero the server will not +// install low level hooks. +// +#define NO_LOWLEVEL_HOOKS 0 + // // extra mouse wheel stuff // @@ -86,8 +92,11 @@ static SInt32 g_xScreen = 0; static SInt32 g_yScreen = 0; static SInt32 g_wScreen = 0; static SInt32 g_hScreen = 0; -static HCURSOR g_cursor = NULL; -static DWORD g_cursorThread = 0; +static WPARAM g_deadVirtKey = 0; +static LPARAM g_deadLParam = 0; +static BYTE g_deadKeyState[256] = { 0 }; +static DWORD g_hookThread = 0; +static DWORD g_attachedThread = 0; #pragma data_seg() @@ -105,49 +114,217 @@ extern "C" int _fltused=0; static void -hideCursor(DWORD thread) +attachThreadToForeground() { - // we should be running the context of the window who's cursor - // we want to hide so we shouldn't have to attach thread input - // but we'll check to make sure. - g_cursorThread = thread; - if (g_cursorThread != 0) { - DWORD myThread = GetCurrentThreadId(); - if (myThread != g_cursorThread) - AttachThreadInput(myThread, g_cursorThread, TRUE); - g_cursor = SetCursor(NULL); - if (myThread != g_cursorThread) - AttachThreadInput(myThread, g_cursorThread, FALSE); + // only attach threads if using low level hooks. a low level hook + // runs in the thread that installed the hook but we have to make + // changes that require being attached to the target thread (which + // should be the foreground window). a regular hook runs in the + // thread that just removed the event from its queue so we're + // already in the right thread. + if (g_hookThread != 0) { + HWND window = GetForegroundWindow(); + DWORD threadID = GetWindowThreadProcessId(window, NULL); + // skip if no change + if (g_attachedThread != threadID) { + // detach from previous thread + if (g_attachedThread != 0 && g_attachedThread != g_hookThread) { + AttachThreadInput(g_hookThread, g_attachedThread, FALSE); + } + // attach to new thread + g_attachedThread = threadID; + if (g_attachedThread != 0 && g_attachedThread != g_hookThread) { + AttachThreadInput(g_hookThread, g_attachedThread, TRUE); + } + } } } static void -restoreCursor() +detachThread() { - // restore the show cursor in the window we hid it last - if (g_cursor != NULL && g_cursorThread != 0) { - DWORD myThread = GetCurrentThreadId(); - if (myThread != g_cursorThread) - AttachThreadInput(myThread, g_cursorThread, TRUE); - SetCursor(g_cursor); - if (myThread != g_cursorThread) - AttachThreadInput(myThread, g_cursorThread, FALSE); + if (g_attachedThread != 0) { + AttachThreadInput(g_hookThread, g_attachedThread, FALSE); + g_attachedThread = 0; } - g_cursor = NULL; - g_cursorThread = 0; } #if !NO_GRAB_KEYBOARD +static +WPARAM +makeKeyMsg(UINT virtKey, char c) +{ + return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), 0); +} + +static +void +keyboardGetState(BYTE keys[256]) +{ + if (g_hookThread != 0) { + GetKeyboardState(keys); + } + else { + SHORT key; + for (int i = 0; i < 256; ++i) { + key = GetAsyncKeyState(i); + keys[i] = (BYTE)((key < 0) ? 0x80u : 0); + } + key = GetKeyState(VK_CAPITAL); + keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1)); + } +} + static bool keyboardHookHandler(WPARAM wParam, LPARAM lParam) { + attachThreadToForeground(); + + // check for dead keys. we don't forward those to our window. + // instead we'll leave the key in the keyboard layout (a buffer + // internal to the system) for translation when the next key is + // pressed. + UINT c = MapVirtualKey(wParam, 2); + if ((c & 0x80000000u) != 0) { + if ((lParam & 0x80000000u) == 0) { + if (g_deadVirtKey == 0) { + // dead key press, no dead key in the buffer + g_deadVirtKey = wParam; + g_deadLParam = lParam; + keyboardGetState(g_deadKeyState); + return false; + } + // second dead key press in a row so let it pass + } + else { + // dead key release + return false; + } + } + + // convert key to a character. this combines a saved dead key, + // if any, with this key. however, the dead key must remain in + // the keyboard layout for the application receiving this event + // so it can also convert the key to a character. we only do + // this on a key press. + WPARAM charAndVirtKey = (wParam & 0xffu); + if (c != 0) { + // we need the keyboard state for ToAscii() + BYTE keys[256]; + keyboardGetState(keys); + + // ToAscii() maps ctrl+letter to the corresponding control code + // and ctrl+backspace to delete. we don't want those translations + // so clear the control modifier state. however, if we want to + // simulate AltGr (which is ctrl+alt) then we must not clear it. + BYTE control = keys[VK_CONTROL]; + BYTE menu = keys[VK_MENU]; + if ((control & 0x80) == 0 || (menu & 0x80) == 0) { + keys[VK_LCONTROL] = 0; + keys[VK_RCONTROL] = 0; + keys[VK_CONTROL] = 0; + } + else { + keys[VK_LCONTROL] = 0x80; + keys[VK_CONTROL] = 0x80; + keys[VK_LMENU] = 0x80; + keys[VK_MENU] = 0x80; + } + + // ToAscii() needs to know if a menu is active for some reason. + // we don't know and there doesn't appear to be any way to find + // out. so we'll just assume a menu is active if the menu key + // is down. + // XXX -- figure out some way to check if a menu is active + UINT flags = 0; + if ((menu & 0x80) != 0) + flags |= 1; + + // map the key event to a character. this has the side + // effect of removing the dead key from the system's keyboard + // layout buffer. + WORD c = 0; + UINT scanCode = ((lParam & 0x00ff0000u) >> 16); + int n = ToAscii(wParam, scanCode, keys, &c, flags); + + // if mapping failed and ctrl and alt are pressed then try again + // with both not pressed. this handles the case where ctrl and + // alt are being used as individual modifiers rather than AltGr. + // we have to put the dead key back first, if there was one. + if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) { + if (g_deadVirtKey != 0) { + ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16, + g_deadKeyState, &c, flags); + } + keys[VK_LCONTROL] = 0; + keys[VK_RCONTROL] = 0; + keys[VK_CONTROL] = 0; + keys[VK_LMENU] = 0; + keys[VK_RMENU] = 0; + keys[VK_MENU] = 0; + n = ToAscii(wParam, scanCode, keys, &c, flags); + } + + switch (n) { + default: + // key is a dead key; we're not expecting this since we + // bailed out above for any dead key. + g_deadVirtKey = wParam; + g_deadLParam = lParam; + break; + + case 0: + // key doesn't map to a character. this can happen if + // non-character keys are pressed after a dead key. + break; + + case 1: + // key maps to a character composed with dead key + charAndVirtKey = makeKeyMsg(wParam, (char)LOBYTE(c)); + break; + + case 2: { + // previous dead key not composed. send a fake key press + // and release for the dead key to our window. + WPARAM deadCharAndVirtKey = + makeKeyMsg(g_deadVirtKey, (char)LOBYTE(c)); + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, + deadCharAndVirtKey, g_deadLParam & 0x7fffffffu); + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, + deadCharAndVirtKey, g_deadLParam | 0x80000000u); + + // use uncomposed character + charAndVirtKey = makeKeyMsg(wParam, (char)HIBYTE(c)); + break; + } + } + + // put back the dead key, if any, for the application to use + if (g_deadVirtKey != 0) { + ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16, + g_deadKeyState, &c, flags); + } + + // clear out old dead key state + g_deadVirtKey = 0; + g_deadLParam = 0; + } + // forward message to our window. do this whether or not we're // forwarding events to clients because this'll keep our thread's // key state table up to date. that's important for querying // the scroll lock toggle state. - PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, wParam, lParam); + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, charAndVirtKey, lParam); + + // send fake key release if the user just pressed two dead keys + // in a row, otherwise we'll lose the release because we always + // return from the top of this function for all dead key releases. + if ((c & 0x80000000u) != 0) { + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, + charAndVirtKey, lParam | 0x80000000u); + } if (g_mode == kHOOK_RELAY_EVENTS) { // let certain keys pass through @@ -160,8 +337,21 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) // lights may not stay synchronized. break; + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + case VK_HANGUL: + // always pass the shift modifiers + break; + default: - // discard event + // discard return true; } } @@ -174,6 +364,8 @@ static bool mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) { + attachThreadToForeground(); + switch (wParam) { case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: @@ -213,19 +405,6 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) case WM_NCMOUSEMOVE: case WM_MOUSEMOVE: if (g_mode == kHOOK_RELAY_EVENTS) { - // we want the cursor to be hidden at all times so we - // hide the cursor on whatever window has it. but then - // we have to show the cursor whenever we leave that - // window (or at some later time before we stop relaying). - // so check the window with the cursor. if it's not the - // same window that had it before then show the cursor - // in the last window and hide it in this window. - DWORD thread = GetCurrentThreadId(); - if (thread != g_cursorThread) { - restoreCursor(); - hideCursor(thread); - } - // relay and eat event PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); return true; @@ -347,7 +526,7 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_getMessage, code, wParam, lParam); } -#if (_WIN32_WINNT >= 0x0400) +#if (_WIN32_WINNT >= 0x0400) && !NO_LOWLEVEL_HOOKS // // low-level keyboard hook -- this allows us to capture and handle @@ -524,15 +703,13 @@ init(DWORD threadID) g_threadID = threadID; // set defaults - g_mode = kHOOK_DISABLE; - g_zoneSides = 0; - g_zoneSize = 0; - g_xScreen = 0; - g_yScreen = 0; - g_wScreen = 0; - g_hScreen = 0; - g_cursor = NULL; - g_cursorThread = 0; + g_mode = kHOOK_DISABLE; + g_zoneSides = 0; + g_zoneSize = 0; + g_xScreen = 0; + g_yScreen = 0; + g_wScreen = 0; + g_hScreen = 0; return 1; } @@ -562,6 +739,10 @@ install() return kHOOK_FAILED; } + // discard old dead keys + g_deadVirtKey = 0; + g_deadLParam = 0; + // check for mouse wheel support g_wheelSupport = getWheelSupport(); @@ -573,35 +754,43 @@ install() 0); } - // install keyboard hook -#if !NO_GRAB_KEYBOARD -#if (_WIN32_WINNT >= 0x0400) - g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, - &keyboardLLHook, - g_hinstance, - 0); -#endif - if (g_keyboardLL == NULL) { - g_keyboard = SetWindowsHookEx(WH_KEYBOARD, - &keyboardHook, - g_hinstance, - 0); - } -#endif - - // install mouse hook -#if (_WIN32_WINNT >= 0x0400) + // install low-level hooks. we require that they both get installed. +#if (_WIN32_WINNT >= 0x0400) && !NO_LOWLEVEL_HOOKS g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL, &mouseLLHook, g_hinstance, 0); +#if !NO_GRAB_KEYBOARD + g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, + &keyboardLLHook, + g_hinstance, + 0); + if (g_mouseLL == NULL || g_keyboardLL == NULL) { + if (g_keyboardLL != NULL) { + UnhookWindowsHookEx(g_keyboardLL); + g_keyboardLL = NULL; + } + if (g_mouseLL != NULL) { + UnhookWindowsHookEx(g_mouseLL); + g_mouseLL = NULL; + } + } #endif +#endif + + // install regular hooks if (g_mouseLL == NULL) { g_mouse = SetWindowsHookEx(WH_MOUSE, &mouseHook, g_hinstance, 0); } + if (g_keyboardLL == NULL) { + g_keyboard = SetWindowsHookEx(WH_KEYBOARD, + &keyboardHook, + g_hinstance, + 0); + } // check that we got all the hooks we wanted if ((g_getMessage == NULL && g_wheelSupport == kWheelOld) || @@ -614,6 +803,7 @@ install() } if (g_keyboardLL != NULL || g_mouseLL != NULL) { + g_hookThread = GetCurrentThreadId(); return kHOOK_OKAY_LL; } @@ -625,6 +815,13 @@ uninstall(void) { assert(g_hinstance != NULL); + // discard old dead keys + g_deadVirtKey = 0; + g_deadLParam = 0; + + // detach from thread + detachThread(); + // uninstall hooks if (g_keyboardLL != NULL) { UnhookWindowsHookEx(g_keyboardLL); @@ -648,9 +845,6 @@ uninstall(void) } g_wheelSupport = kWheelNone; - // show the cursor - restoreCursor(); - return 1; } @@ -719,9 +913,6 @@ setMode(EHookMode mode) return; } g_mode = mode; - if (g_mode != kHOOK_RELAY_EVENTS) { - restoreCursor(); - } } } diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 5ee5b946..b870b968 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -1153,7 +1153,6 @@ void CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) { LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); -LOG((CLOG_INFO "onKeyDown: id=%d mask=0x%04x button=0x%04x", id, mask, button)); assert(m_active != NULL); // handle command keys @@ -1169,7 +1168,6 @@ void CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button) { LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); -LOG((CLOG_INFO "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); assert(m_active != NULL); // handle command keys @@ -1186,7 +1184,6 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button) { LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); -LOG((CLOG_INFO "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); assert(m_active != NULL); // handle command keys @@ -1225,7 +1222,10 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y) LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); // mouse move on primary (server's) screen - assert(m_active == m_primaryClient); + if (m_active != m_primaryClient) { + // stale event -- we're actually on a secondary screen + return false; + } // save position m_x = x; @@ -1282,13 +1282,7 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) // mouse move on secondary (client's) screen assert(m_active != NULL); if (m_active == m_primaryClient) { - // we're actually on the primary screen. this can happen - // when the primary screen begins processing a mouse move - // for a secondary screen, then the active (secondary) - // screen disconnects causing us to jump to the primary - // screen, and finally the primary screen finishes - // processing the mouse move, still thinking it's for - // a secondary screen. we just ignore the motion. + // stale event -- we're actually on the primary screen return; } From 3920c63af6401782de3b25120df66d27db04c8c8 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 8 Mar 2004 21:18:36 +0000 Subject: [PATCH 599/807] Removed dependency on X11/XF86keysym.h. There are several versions of that file in existance, not all of which have all the symbols we require and none of which provide any convenient means of telling what groups of symbols they define. --- configure.in | 1 - lib/platform/CXWindowsKeyMapper.cpp | 74 +++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/configure.in b/configure.in index 5bde3874..8dbe4e78 100644 --- a/configure.in +++ b/configure.in @@ -57,7 +57,6 @@ AC_PATH_XTRA save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$X_CFLAGS $CPPFLAGS" AC_CHECK_HEADERS([X11/extensions/XTest.h]) -AC_CHECK_HEADERS([X11/XF86keysym.h]) AC_CHECK_LIB(Xinerama, XineramaQueryExtension, AC_CHECK_HEADERS([X11/extensions/Xinerama.h]) [X_LIBS="$X_LIBS -lXinerama"], , [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) CPPFLAGS="$save_CPPFLAGS" diff --git a/lib/platform/CXWindowsKeyMapper.cpp b/lib/platform/CXWindowsKeyMapper.cpp index 948f1e26..35526a05 100644 --- a/lib/platform/CXWindowsKeyMapper.cpp +++ b/lib/platform/CXWindowsKeyMapper.cpp @@ -23,15 +23,71 @@ # define XK_MISCELLANY # define XK_XKB_KEYS # include -# if defined(HAVE_X11_XF86KEYSYM_H) -# include -# endif -# if !defined(XF86XK_Launch0) -# define XF86XK_Launch0 0x1008FF40 -# endif -# if !defined(XF86XK_Launch1) -# define XF86XK_Launch1 0x1008FF41 -# endif +// these should be in XF86keysym.h but there are several versions of +// that file floating around and not all have all symbols and none of +// them provide any form of versioning so we just define 'em here. +#define XF86XK_Standby 0x1008FF10 +#define XF86XK_AudioLowerVolume 0x1008FF11 +#define XF86XK_AudioMute 0x1008FF12 +#define XF86XK_AudioRaiseVolume 0x1008FF13 +#define XF86XK_AudioPlay 0x1008FF14 +#define XF86XK_AudioStop 0x1008FF15 +#define XF86XK_AudioPrev 0x1008FF16 +#define XF86XK_AudioNext 0x1008FF17 +#define XF86XK_HomePage 0x1008FF18 +#define XF86XK_Mail 0x1008FF19 +#define XF86XK_Start 0x1008FF1A +#define XF86XK_Search 0x1008FF1B +#define XF86XK_AudioRecord 0x1008FF1C +#define XF86XK_Calculator 0x1008FF1D +#define XF86XK_Memo 0x1008FF1E +#define XF86XK_ToDoList 0x1008FF1F +#define XF86XK_Calendar 0x1008FF20 +#define XF86XK_PowerDown 0x1008FF21 +#define XF86XK_ContrastAdjust 0x1008FF22 +#define XF86XK_RockerUp 0x1008FF23 +#define XF86XK_RockerDown 0x1008FF24 +#define XF86XK_RockerEnter 0x1008FF25 +#define XF86XK_Back 0x1008FF26 +#define XF86XK_Forward 0x1008FF27 +#define XF86XK_Stop 0x1008FF28 +#define XF86XK_Refresh 0x1008FF29 +#define XF86XK_PowerOff 0x1008FF2A +#define XF86XK_WakeUp 0x1008FF2B +#define XF86XK_Eject 0x1008FF2C +#define XF86XK_ScreenSaver 0x1008FF2D +#define XF86XK_WWW 0x1008FF2E +#define XF86XK_Sleep 0x1008FF2F +#define XF86XK_Favorites 0x1008FF30 +#define XF86XK_AudioPause 0x1008FF31 +#define XF86XK_AudioMedia 0x1008FF32 +#define XF86XK_MyComputer 0x1008FF33 +#define XF86XK_VendorHome 0x1008FF34 +#define XF86XK_LightBulb 0x1008FF35 +#define XF86XK_Shop 0x1008FF36 +#define XF86XK_History 0x1008FF37 +#define XF86XK_OpenURL 0x1008FF38 +#define XF86XK_AddFavorite 0x1008FF39 +#define XF86XK_HotLinks 0x1008FF3A +#define XF86XK_BrightnessAdjust 0x1008FF3B +#define XF86XK_Finance 0x1008FF3C +#define XF86XK_Community 0x1008FF3D +#define XF86XK_Launch0 0x1008FF40 +#define XF86XK_Launch1 0x1008FF41 +#define XF86XK_Launch2 0x1008FF42 +#define XF86XK_Launch3 0x1008FF43 +#define XF86XK_Launch4 0x1008FF44 +#define XF86XK_Launch5 0x1008FF45 +#define XF86XK_Launch6 0x1008FF46 +#define XF86XK_Launch7 0x1008FF47 +#define XF86XK_Launch8 0x1008FF48 +#define XF86XK_Launch9 0x1008FF49 +#define XF86XK_LaunchA 0x1008FF4A +#define XF86XK_LaunchB 0x1008FF4B +#define XF86XK_LaunchC 0x1008FF4C +#define XF86XK_LaunchD 0x1008FF4D +#define XF86XK_LaunchE 0x1008FF4E +#define XF86XK_LaunchF 0x1008FF4F #endif // map special KeyID keys to KeySyms From 6c7039490d20426b47d37d56200d087fc7b711ac Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 10 Mar 2004 20:35:03 +0000 Subject: [PATCH 600/807] Added check for inet_aton and a simple implementation for platforms that are missing it. --- acinclude.m4 | 1 + lib/arch/CArchNetworkBSD.cpp | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/acinclude.m4 b/acinclude.m4 index 62009432..6492e615 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -243,6 +243,7 @@ AC_DEFUN([ACX_CHECK_INET_ATON], [ LIBS="$save_LIBS" AC_MSG_RESULT($acx_inet_aton_ok) if test x"$acx_inet_aton_ok" = xyes; then + AC_DEFINE(HAVE_INET_ATON,1,[Define if you have the \`inet_aton\' function.]) break; fi INET_ATON_LIBS="" diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index bfdcf20f..fa7f7ebe 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -51,6 +51,29 @@ static const int s_type[] = { SOCK_STREAM }; +#if !HAVE_INET_ATON +// parse dotted quad addresses. we don't bother with the weird BSD'ism +// of handling octal and hex and partial forms. +static +in_addr_t +inet_aton(const char* cp, struct in_addr* inp) +{ + unsigned int a, b, c, d; + if (sscanf(cp, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) { + return 0; + } + if (a >= 256 || b >= 256 || c >= 256 || d >= 256) { + return 0; + } + unsigned char* incp = (unsigned char*)inp; + incp[0] = (unsigned char)(a & 0xffu); + incp[1] = (unsigned char)(b & 0xffu); + incp[2] = (unsigned char)(c & 0xffu); + incp[3] = (unsigned char)(d & 0xffu); + return inp->s_addr; +} +#endif + // // CArchNetworkBSD // From a1c807ba674056624b3178c9281e08f3b999401a Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 10 Mar 2004 22:03:01 +0000 Subject: [PATCH 601/807] Added support for DPMS in X11 screen saver. DPMS is the extension that allows you to power down the display. Previously, synergy would not power on the display if DPMS was enabled and activated and xscreensaver was not running. It also wouldn't disable DPMS so the display would power down normally on a synergy client if there was no input activity. --- configure.in | 2 +- lib/platform/CXWindowsScreenSaver.cpp | 129 ++++++++++++++++++++++++-- lib/platform/CXWindowsScreenSaver.h | 16 ++++ 3 files changed, 140 insertions(+), 7 deletions(-) diff --git a/configure.in b/configure.in index 8dbe4e78..e8a9906d 100644 --- a/configure.in +++ b/configure.in @@ -57,8 +57,8 @@ AC_PATH_XTRA save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$X_CFLAGS $CPPFLAGS" AC_CHECK_HEADERS([X11/extensions/XTest.h]) - AC_CHECK_LIB(Xinerama, XineramaQueryExtension, AC_CHECK_HEADERS([X11/extensions/Xinerama.h]) [X_LIBS="$X_LIBS -lXinerama"], , [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) +AC_CHECK_LIB(Xext, DPMSQueryExtension, AC_CHECK_HEADERS([X11/extensions/dpms.h]) [X_LIBS="$X_LIBS -lXext"], , [$X_LIBS -lX11 $X_EXTRA_LIBS]) CPPFLAGS="$save_CPPFLAGS" dnl checks for types diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index a40ae7e4..44c7c0b8 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -25,6 +25,11 @@ #else # error The XTest extension is required to build synergy #endif +#if defined(HAVE_X11_EXTENSIONS_DPMS_H) +extern "C" { +# include +} +#endif // // CXWindowsScreenSaver @@ -37,6 +42,7 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( m_eventTarget(eventTarget), m_xscreensaver(None), m_xscreensaverActive(false), + m_dpms(false), m_disabled(false), m_suppressDisable(false), m_disableTimer(NULL) @@ -51,6 +57,18 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( m_atomScreenSaverDeactivate = XInternAtom(m_display, "DEACTIVATE", False); + // check for DPMS extension. this is an alternative screen saver + // that powers down the display. +#if defined(HAVE_X11_EXTENSIONS_DPMS_H) + int eventBase, errorBase; + if (DPMSQueryExtension(m_display, &eventBase, &errorBase)) { + if (DPMSCapable(m_display)) { + // we have DPMS + m_dpms = true; + } + } +#endif + // watch top-level windows for changes { bool error = false; @@ -66,15 +84,18 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( } } + // get the built-in settings + XGetScreenSaver(m_display, &m_timeout, &m_interval, + &m_preferBlanking, &m_allowExposures); + + // get the DPMS settings + m_dpmsEnabled = isDPMSEnabled(); + // get the xscreensaver window, if any if (!findXScreenSaver()) { setXScreenSaver(None); } - // get the built-in settings - XGetScreenSaver(m_display, &m_timeout, &m_interval, - &m_preferBlanking, &m_allowExposures); - // install disable timer event handler EVENTQUEUE->adoptHandler(CEvent::kTimer, this, new TMethodEventJob(this, @@ -90,6 +111,7 @@ CXWindowsScreenSaver::~CXWindowsScreenSaver() EVENTQUEUE->removeHandler(CEvent::kTimer, this); if (m_display != NULL) { + enableDPMS(m_dpmsEnabled); XSetScreenSaver(m_display, m_timeout, m_interval, m_preferBlanking, m_allowExposures); clearWatchForXScreenSaver(); @@ -176,6 +198,9 @@ CXWindowsScreenSaver::enable() // for built-in X screen saver XSetScreenSaver(m_display, m_timeout, m_interval, m_preferBlanking, m_allowExposures); + + // for DPMS + enableDPMS(m_dpmsEnabled); } void @@ -190,6 +215,11 @@ CXWindowsScreenSaver::disable() &m_preferBlanking, &m_allowExposures); XSetScreenSaver(m_display, 0, m_interval, m_preferBlanking, m_allowExposures); + + // for DPMS + m_dpmsEnabled = isDPMSEnabled(); + enableDPMS(false); + // FIXME -- now deactivate? } @@ -200,6 +230,9 @@ CXWindowsScreenSaver::activate() m_suppressDisable = true; updateDisableTimer(); + // enable DPMS if it was enabled + enableDPMS(m_dpmsEnabled); + // try xscreensaver findXScreenSaver(); if (m_xscreensaver != None) { @@ -207,8 +240,13 @@ CXWindowsScreenSaver::activate() return; } - // use built-in X screen saver - XForceScreenSaver(m_display, ScreenSaverActive); + // try built-in X screen saver + if (m_timeout != 0) { + XForceScreenSaver(m_display, ScreenSaverActive); + } + + // try DPMS + activateDPMS(true); } void @@ -218,6 +256,14 @@ CXWindowsScreenSaver::deactivate() m_suppressDisable = false; updateDisableTimer(); + // try DPMS + activateDPMS(false); + + // disable DPMS if screen saver is disabled + if (m_disabled) { + enableDPMS(false); + } + // try xscreensaver findXScreenSaver(); if (m_xscreensaver != None) { @@ -237,6 +283,11 @@ CXWindowsScreenSaver::isActive() const return m_xscreensaverActive; } + // check DPMS + if (isDPMSActivated()) { + return true; + } + // can't check built-in X screen saver activity return false; } @@ -282,6 +333,9 @@ CXWindowsScreenSaver::setXScreenSaver(Window window) XWindowAttributes attr; XGetWindowAttributes(m_display, m_xscreensaver, &attr); setXScreenSaverActive(!error && attr.map_state != IsUnmapped); + + // save current DPMS state; xscreensaver may have changed it. + m_dpmsEnabled = isDPMSEnabled(); } else { // screen saver can't be active if it doesn't exist @@ -450,3 +504,66 @@ CXWindowsScreenSaver::handleDisableTimer(const CEvent&, void*) XSendEvent(m_display, m_xscreensaver, False, 0, &event); } } + +void +CXWindowsScreenSaver::activateDPMS(bool activate) +{ +#if defined(HAVE_X11_EXTENSIONS_DPMS_H) + if (m_dpms) { + // DPMSForceLevel will generate a BadMatch if DPMS is disabled + CXWindowsUtil::CErrorLock lock(m_display); + DPMSForceLevel(m_display, activate ? DPMSModeStandby : DPMSModeOn); + } +#endif +} + +void +CXWindowsScreenSaver::enableDPMS(bool enable) +{ +#if defined(HAVE_X11_EXTENSIONS_DPMS_H) + if (m_dpms) { + if (enable) { + DPMSEnable(m_display); + } + else { + DPMSDisable(m_display); + } + } +#endif +} + +bool +CXWindowsScreenSaver::isDPMSEnabled() const +{ +#if defined(HAVE_X11_EXTENSIONS_DPMS_H) + if (m_dpms) { + CARD16 level; + BOOL state; + DPMSInfo(m_display, &level, &state); + return (state != False); + } + else { + return false; + } +#else + return false; +#endif +} + +bool +CXWindowsScreenSaver::isDPMSActivated() const +{ +#if defined(HAVE_X11_EXTENSIONS_DPMS_H) + if (m_dpms) { + CARD16 level; + BOOL state; + DPMSInfo(m_display, &level, &state); + return (level != DPMSModeOn); + } + else { + return false; + } +#else + return false; +#endif +} diff --git a/lib/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h index b956a84b..20c2237d 100644 --- a/lib/platform/CXWindowsScreenSaver.h +++ b/lib/platform/CXWindowsScreenSaver.h @@ -92,6 +92,18 @@ private: // called periodically to prevent the screen saver from starting void handleDisableTimer(const CEvent&, void*); + // force DPMS to activate or deactivate + void activateDPMS(bool activate); + + // enable/disable DPMS screen saver + void enableDPMS(bool); + + // check if DPMS is enabled + bool isDPMSEnabled() const; + + // check if DPMS is activate + bool isDPMSActivated() const; + private: typedef std::map CWatchList; @@ -128,6 +140,10 @@ private: int m_preferBlanking; int m_allowExposures; + // DPMS screen saver settings + bool m_dpms; + bool m_dpmsEnabled; + // true iff the client wants the screen saver suppressed bool m_disabled; From a6e858a2081a8ba7cc49a96c94baf3c2d3fa27e7 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 13 Mar 2004 17:13:55 +0000 Subject: [PATCH 602/807] Added win32 support for power management. --- cmd/synergyc/resource.h | 12 +-- cmd/synergyc/synergyc.cpp | 54 +++++++++-- cmd/synergys/synergys.cpp | 2 +- lib/arch/CArchMiscWindows.cpp | 127 +++++++++++++++++++++++-- lib/arch/CArchMiscWindows.h | 44 +++++++++ lib/platform/CMSWindowsScreen.cpp | 103 ++++++++++++++------ lib/platform/CMSWindowsScreen.h | 14 ++- lib/platform/CMSWindowsScreenSaver.cpp | 99 ++++++++++++++++++- lib/platform/CMSWindowsScreenSaver.h | 6 ++ 9 files changed, 401 insertions(+), 60 deletions(-) diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h index 37f085d6..1a5455a1 100644 --- a/cmd/synergyc/resource.h +++ b/cmd/synergyc/resource.h @@ -13,17 +13,17 @@ #define IDR_TASKBAR 107 #define IDD_TASKBAR_STATUS 108 #define IDC_TASKBAR_STATUS_STATUS 1000 -#define IDC_TASKBAR_QUIT 40003 -#define IDC_TASKBAR_STATUS 40004 -#define IDC_TASKBAR_LOG 40005 +#define IDC_TASKBAR_QUIT 40001 +#define IDC_TASKBAR_STATUS 40002 +#define IDC_TASKBAR_LOG 40003 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_RESOURCE_VALUE 109 +#define _APS_NEXT_COMMAND_VALUE 40004 +#define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 2f5b945b..9e7be5ab 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -24,6 +24,7 @@ #include "CThread.h" #include "CEventQueue.h" #include "CFunctionEventJob.h" +#include "CFunctionJob.h" #include "CLog.h" #include "CString.h" #include "CStringUtil.h" @@ -54,7 +55,12 @@ #endif typedef int (*StartupFunc)(int, char**); +static bool startClient(); static void parse(int argc, const char* const* argv); +#if WINDOWS_LIKE +static void handleSystemSuspend(void*); +static void handleSystemResume(void*); +#endif // // program arguments @@ -97,7 +103,9 @@ CScreen* createScreen() { #if WINDOWS_LIKE - return new CScreen(new CMSWindowsScreen(false)); + return new CScreen(new CMSWindowsScreen(false, + new CFunctionJob(&handleSystemSuspend), + new CFunctionJob(&handleSystemResume))); #elif UNIX_LIKE return new CScreen(new CXWindowsScreen(false)); #endif @@ -124,6 +132,7 @@ static CClient* s_client = NULL; static CScreen* s_clientScreen = NULL; static CClientTaskBarReceiver* s_taskBarReceiver = NULL; static double s_retryTime = 0.0; +static bool s_suspened = false; static void @@ -181,6 +190,26 @@ handleScreenError(const CEvent&, void*) EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } +#if WINDOWS_LIKE +static +void +handleSystemSuspend(void*) +{ + LOG((CLOG_NOTE "system suspending")); + s_suspened = true; + s_client->disconnect(NULL); +} + +static +void +handleSystemResume(void*) +{ + LOG((CLOG_NOTE "system resuming")); + s_suspened = false; + startClient(); +} +#endif + static CScreen* openClientScreen() @@ -211,11 +240,12 @@ handleClientRestart(const CEvent&, void* vtimer) // discard old timer CEventQueueTimer* timer = reinterpret_cast(vtimer); EVENTQUEUE->deleteTimer(timer); - EVENTQUEUE->removeHandler(CEvent::kTimer, NULL); + EVENTQUEUE->removeHandler(CEvent::kTimer, timer); // reconnect - s_client->connect(); - updateStatus(); + if (!s_suspened) { + startClient(); + } } static @@ -251,7 +281,9 @@ handleClientFailed(const CEvent& e, void*) } else { LOG((CLOG_WARN "failed to connect to server: %s", info->m_what)); - scheduleClientRestart(nextRestartTimeout()); + if (!s_suspened) { + scheduleClientRestart(nextRestartTimeout()); + } } } @@ -263,7 +295,7 @@ handleClientDisconnected(const CEvent&, void*) if (!ARG->m_restartable) { EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } - else { + else if (!s_suspened) { s_client->connect(); } updateStatus(); @@ -308,11 +340,13 @@ startClient() double retryTime; CScreen* clientScreen = NULL; try { - clientScreen = openClientScreen(); - s_client = openClient(ARG->m_name, + if (s_clientScreen == NULL) { + clientScreen = openClientScreen(); + s_client = openClient(ARG->m_name, *ARG->m_serverAddress, clientScreen); - s_clientScreen = clientScreen; - LOG((CLOG_NOTE "started client")); + s_clientScreen = clientScreen; + LOG((CLOG_NOTE "started client")); + } s_client->connect(); updateStatus(); return true; diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index c1370197..b1cdaed5 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -114,7 +114,7 @@ CScreen* createScreen() { #if WINDOWS_LIKE - return new CScreen(new CMSWindowsScreen(true)); + return new CScreen(new CMSWindowsScreen(true, NULL, NULL)); #elif UNIX_LIKE return new CScreen(new CXWindowsScreen(true)); #endif diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index d2b0842c..6f4e9ca2 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -15,11 +15,24 @@ #include "CArchMiscWindows.h" #include "CArchDaemonWindows.h" +#ifndef ES_SYSTEM_REQUIRED +#define ES_SYSTEM_REQUIRED ((DWORD)0x00000001) +#endif +#ifndef ES_DISPLAY_REQUIRED +#define ES_DISPLAY_REQUIRED ((DWORD)0x00000002) +#endif +#ifndef ES_CONTINUOUS +#define ES_CONTINUOUS ((DWORD)0x80000000) +#endif +typedef DWORD EXECUTION_STATE; + // // CArchMiscWindows // -CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL; +CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL; +DWORD CArchMiscWindows::s_busyState = 0; +CArchMiscWindows::STES_t CArchMiscWindows::s_stes = NULL; void CArchMiscWindows::init() @@ -143,6 +156,29 @@ CArchMiscWindows::hasValue(HKEY key, const TCHAR* name) (type == REG_DWORD || type == REG_SZ)); } +CArchMiscWindows::EValueType +CArchMiscWindows::typeOfValue(HKEY key, const TCHAR* name) +{ + DWORD type; + LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL); + if (result != ERROR_SUCCESS) { + return kNO_VALUE; + } + switch (type) { + case REG_DWORD: + return kUINT; + + case REG_SZ: + return kSTRING; + + case REG_BINARY: + return kBINARY; + + default: + return kUNKNOWN; + } +} + void CArchMiscWindows::setValue(HKEY key, const TCHAR* name, const std::string& value) @@ -164,14 +200,25 @@ CArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value) sizeof(DWORD)); } +void +CArchMiscWindows::setValueBinary(HKEY key, + const TCHAR* name, const std::string& value) +{ + assert(key != NULL); + assert(name != NULL); + RegSetValueEx(key, name, 0, REG_BINARY, + reinterpret_cast(value.data()), + value.size()); +} + std::string -CArchMiscWindows::readValueString(HKEY key, const TCHAR* name) +CArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type) { // get the size of the string - DWORD type; + DWORD actualType; DWORD size = 0; - LONG result = RegQueryValueEx(key, name, 0, &type, NULL, &size); - if (result != ERROR_SUCCESS || type != REG_SZ) { + LONG result = RegQueryValueEx(key, name, 0, &actualType, NULL, &size); + if (result != ERROR_SUCCESS || actualType != type) { return std::string(); } @@ -179,19 +226,31 @@ CArchMiscWindows::readValueString(HKEY key, const TCHAR* name) char* buffer = new char[size]; // read it - result = RegQueryValueEx(key, name, 0, &type, + result = RegQueryValueEx(key, name, 0, &actualType, reinterpret_cast(buffer), &size); - if (result != ERROR_SUCCESS || type != REG_SZ) { + if (result != ERROR_SUCCESS || actualType != type) { delete[] buffer; return std::string(); } // clean up and return value - std::string value(buffer); + std::string value(buffer, size); delete[] buffer; return value; } +std::string +CArchMiscWindows::readValueString(HKEY key, const TCHAR* name) +{ + return readBinaryOrString(key, name, REG_SZ); +} + +std::string +CArchMiscWindows::readValueBinary(HKEY key, const TCHAR* name) +{ + return readBinaryOrString(key, name, REG_BINARY); +} + DWORD CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name) { @@ -229,3 +288,55 @@ CArchMiscWindows::processDialog(MSG* msg) } return false; } + +void +CArchMiscWindows::addBusyState(DWORD busyModes) +{ + s_busyState |= busyModes; + setThreadExecutionState(s_busyState); +} + +void +CArchMiscWindows::removeBusyState(DWORD busyModes) +{ + s_busyState &= ~busyModes; + setThreadExecutionState(s_busyState); +} + +void +CArchMiscWindows::setThreadExecutionState(DWORD busyModes) +{ + // look up function dynamically so we work on older systems + if (s_stes == NULL) { + HINSTANCE kernel = LoadLibrary("kernel32.dll"); + if (kernel != NULL) { + s_stes = reinterpret_cast(GetProcAddress(kernel, + "SetThreadExecutionState")); + } + if (s_stes == NULL) { + s_stes = &CArchMiscWindows::dummySetThreadExecutionState; + } + } + + // convert to STES form + EXECUTION_STATE state = 0; + if ((busyModes & kSYSTEM) != 0) { + state |= ES_SYSTEM_REQUIRED; + } + if ((busyModes & kDISPLAY) != 0) { + state |= ES_DISPLAY_REQUIRED; + } + if (state != 0) { + state |= ES_CONTINUOUS; + } + + // do it + s_stes(state); +} + +DWORD +CArchMiscWindows::dummySetThreadExecutionState(DWORD) +{ + // do nothing + return 0; +} diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index 749e52f5..e55b6dd2 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -25,6 +25,19 @@ //! Miscellaneous win32 functions. class CArchMiscWindows { public: + enum EValueType { + kUNKNOWN, + kNO_VALUE, + kUINT, + kSTRING, + kBINARY + }; + enum EBusyModes { + kIDLE = 0x0000, + kSYSTEM = 0x0001, + kDISPLAY = 0x0002 + }; + typedef int (*RunFunc)(void); //! Initialize @@ -78,6 +91,9 @@ public: //! Test if a value exists static bool hasValue(HKEY key, const TCHAR* name); + //! Get type of value + static EValueType typeOfValue(HKEY key, const TCHAR* name); + //! Set a string value in the registry static void setValue(HKEY key, const TCHAR* name, const std::string& value); @@ -85,12 +101,22 @@ public: //! Set a DWORD value in the registry static void setValue(HKEY key, const TCHAR* name, DWORD value); + //! Set a BINARY value in the registry + /*! + Sets the \p name value of \p key to \p value.data(). + */ + static void setValueBinary(HKEY key, const TCHAR* name, + const std::string& value); + //! Read a string value from the registry static std::string readValueString(HKEY, const TCHAR* name); //! Read a DWORD value from the registry static DWORD readValueInt(HKEY, const TCHAR* name); + //! Read a BINARY value from the registry + static std::string readValueBinary(HKEY, const TCHAR* name); + //! Add a dialog static void addDialog(HWND); @@ -104,10 +130,28 @@ public: */ static bool processDialog(MSG*); + //! Disable power saving + static void addBusyState(DWORD busyModes); + + //! Enable power saving + static void removeBusyState(DWORD busyModes); + +private: + //! Read a string value from the registry + static std::string readBinaryOrString(HKEY, const TCHAR* name, DWORD type); + + //! Set thread busy state + static void setThreadExecutionState(DWORD); + + static DWORD WINAPI dummySetThreadExecutionState(DWORD); + private: typedef std::set CDialogs; + typedef DWORD (WINAPI *STES_t)(DWORD); static CDialogs* s_dialogs; + static DWORD s_busyState; + static STES_t s_stes; }; #endif diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index ae15ffb8..4bc2aae7 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -113,7 +113,8 @@ HINSTANCE CMSWindowsScreen::s_instance = NULL; CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; -CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : +CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, + IJob* suspend, IJob* resume) : m_isPrimary(isPrimary), m_is95Family(CArchMiscWindows::isWindows95Family()), m_isOnScreen(m_isPrimary), @@ -137,16 +138,29 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : m_activeDesk(NULL), m_activeDeskName(), m_hookLibrary(NULL), + m_init(NULL), + m_cleanup(NULL), + m_install(NULL), + m_uninstall(NULL), + m_setSides(NULL), + m_setZone(NULL), + m_setMode(NULL), + m_installScreensaver(NULL), + m_uninstallScreensaver(NULL), m_keyState(NULL), m_mutex(), - m_deskReady(&m_mutex, false) + m_deskReady(&m_mutex, false), + m_suspend(suspend), + m_resume(resume) { assert(s_instance != NULL); assert(s_screen == NULL); s_screen = this; try { - m_hookLibrary = openHookLibrary("synrgyhk"); + if (m_isPrimary) { + m_hookLibrary = openHookLibrary("synrgyhk"); + } m_cursor = createBlankCursor(); m_class = createWindowClass(); m_deskClass = createDeskWindowClass(m_isPrimary); @@ -171,9 +185,6 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : throw; } - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - // install event handlers EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(), new TMethodEventJob(this, @@ -190,14 +201,14 @@ CMSWindowsScreen::~CMSWindowsScreen() disable(); EVENTQUEUE->adoptBuffer(NULL); EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget()); - removeDesks(); - ChangeClipboardChain(m_window, m_nextClipboardWindow); delete m_screensaver; destroyWindow(m_window); destroyClass(m_deskClass); destroyClass(m_class); destroyCursor(m_cursor); closeHookLibrary(m_hookLibrary); + delete m_suspend; + delete m_resume; s_screen = NULL; } @@ -229,6 +240,9 @@ CMSWindowsScreen::enable() { assert(m_isOnScreen == m_isPrimary); + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + if (m_isPrimary) { // update shadow key state m_keyMapper.update(NULL); @@ -239,6 +253,12 @@ CMSWindowsScreen::enable() // watch jump zones m_setMode(kHOOK_WATCH_JUMP_ZONE); } + else { + // prevent the system from entering power saving modes. if + // it did we'd be forced to disconnect from the server and + // the server would not be able to wake us up. + CArchMiscWindows::addBusyState(CArchMiscWindows::kSYSTEM); + } // set the active desk and (re)install the hooks checkDesk(); @@ -263,10 +283,27 @@ CMSWindowsScreen::disable() m_timer = NULL; } - // disable hooks if (m_isPrimary) { + // disable hooks m_setMode(kHOOK_DISABLE); + + // enable special key sequences on win95 family + enableSpecialKeys(true); } + else { + // allow the system to enter power saving mode + CArchMiscWindows::removeBusyState(CArchMiscWindows::kSYSTEM | + CArchMiscWindows::kDISPLAY); + } + + // destroy desks + removeDesks(); + + // stop snooping the clipboard + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + m_isOnScreen = m_isPrimary; } void @@ -973,6 +1010,25 @@ CMSWindowsScreen::onEvent(HWND, UINT msg, case WM_DISPLAYCHANGE: return onDisplayChange(); + + case WM_POWERBROADCAST: + switch (wParam) { + case PBT_APMRESUMEAUTOMATIC: + case PBT_APMRESUMECRITICAL: + case PBT_APMRESUMESUSPEND: + if (m_resume != NULL) { + m_resume->run(); + } + break; + + case PBT_APMSUSPEND: + if (m_suspend != NULL) { + m_suspend->run(); + } + break; + } + *result = TRUE; + return true; } return false; @@ -1316,10 +1372,16 @@ CMSWindowsScreen::onScreensaver(bool activated) if (activated) { if (m_screensaver->checkStarted(SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) { sendEvent(getScreensaverActivatedEvent()); + + // enable display power down + CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY); } } else { sendEvent(getScreensaverDeactivatedEvent()); + + // disable display power down + CArchMiscWindows::addBusyState(CArchMiscWindows::kDISPLAY); } return true; @@ -1616,7 +1678,6 @@ LRESULT CALLBACK CMSWindowsScreen::primaryDeskProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - // FIXME return DefWindowProc(hwnd, msg, wParam, lParam); } @@ -1893,6 +1954,7 @@ CMSWindowsScreen::deskThread(void* vdesk) } // clean up + deskEnter(desk); if (desk->m_window != NULL) { DestroyWindow(desk->m_window); } @@ -1927,6 +1989,8 @@ CMSWindowsScreen::removeDesks() delete desk; } m_desks.clear(); + m_activeDesk = NULL; + m_activeDeskName = ""; } void @@ -1952,7 +2016,6 @@ CMSWindowsScreen::checkDesk() // active becaue we'd most likely switch to the screensaver desktop // which would have the side effect of forcing the screensaver to // stop. - // FIXME -- really not switch if screensaver is active? if (name != m_activeDeskName && !m_screensaver->isActive()) { // show cursor on previous desk if (!m_isOnScreen) { @@ -1987,24 +2050,6 @@ CMSWindowsScreen::checkDesk() } } -// FIXME -- may want some of following when we switch desks. calling -// nextMark() for isPrimary may lead to loss of events. updateKeys() -// is to catch any key events lost between switching and detecting the -// switch. neither are strictly necessary. -/* - if (m_isPrimary) { - if (m_isOnScreen) { - // all messages prior to now are invalid - // FIXME -- is this necessary; couldn't we lose key releases? - nextMark(); - } - } - else { - // update key state - updateKeys(); - } -*/ - bool CMSWindowsScreen::isDeskAccessible(const CDesk* desk) const { diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 15c8dfc9..5f10a45d 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -31,7 +31,7 @@ class CThread; //! Implementation of IPlatformScreen for Microsoft Windows class CMSWindowsScreen : public IPlatformScreen { public: - CMSWindowsScreen(bool isPrimary); + CMSWindowsScreen(bool isPrimary, IJob* suspend, IJob* resume); virtual ~CMSWindowsScreen(); //! @name manipulators @@ -112,7 +112,7 @@ private: }; typedef std::map CDesks; -// FIXME -- comment + // initialization and shutdown operations HINSTANCE openHookLibrary(const char* name); void closeHookLibrary(HINSTANCE hookLibrary) const; HCURSOR createBlankCursor() const; @@ -122,8 +122,12 @@ private: void destroyClass(ATOM windowClass) const; HWND createWindow(ATOM windowClass, const char* name) const; void destroyWindow(HWND) const; + + // convenience function to send events void sendEvent(CEvent::Type type, void* = NULL); void sendClipboardEvent(CEvent::Type type, ClipboardID id); + + // system event handler (does DispatchMessage) void handleSystemEvent(const CEvent& event, void*); // handle message before it gets dispatched. returns true iff @@ -148,7 +152,6 @@ private: bool onDisplayChange(); bool onClipboardChange(); -// XXX // warp cursor without discarding queued events void warpCursorNoFlush(SInt32 x, SInt32 y); @@ -157,7 +160,6 @@ private: // test if event should be ignored bool ignore() const; -// XXX // update screen size cache void updateScreenShape(); @@ -278,6 +280,10 @@ private: // map of button state BYTE m_buttons[1 + kButtonExtra0 + 1]; + // suspend/resume callbacks + IJob* m_suspend; + IJob* m_resume; + static CMSWindowsScreen* s_screen; }; diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index 14e0bac3..89090009 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -13,10 +13,12 @@ */ #include "CMSWindowsScreenSaver.h" +#include "CMSWindowsScreen.h" #include "CThread.h" #include "CLog.h" #include "TMethodJob.h" #include "CArch.h" +#include "CArchMiscWindows.h" #include #include @@ -24,11 +26,21 @@ #define SPI_GETSCREENSAVERRUNNING 114 #endif +static const TCHAR* g_isSecureNT = "ScreenSaverIsSecure"; +static const TCHAR* g_isSecure9x = "ScreenSaverUsePassword"; +static const TCHAR* const g_pathScreenSaverIsSecure[] = { + "Control Panel", + "Desktop", + NULL +}; + // // CMSWindowsScreenSaver // CMSWindowsScreenSaver::CMSWindowsScreenSaver() : + m_wasSecure(false), + m_wasSecureAnInt(false), m_process(NULL), m_threadID(0), m_watch(NULL) @@ -119,6 +131,14 @@ void CMSWindowsScreenSaver::enable() { SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, m_wasEnabled, 0, 0); + + // restore password protection + if (m_wasSecure) { + setSecure(true, m_wasSecureAnInt); + } + + // restore display power down + CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY); } void @@ -126,6 +146,15 @@ CMSWindowsScreenSaver::disable() { SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, 0); + + // disable password protected screensaver + m_wasSecure = isSecure(&m_wasSecureAnInt); + if (m_wasSecure) { + setSecure(false, m_wasSecureAnInt); + } + + // disable display power down + CArchMiscWindows::addBusyState(CArchMiscWindows::kDISPLAY); } void @@ -141,6 +170,9 @@ CMSWindowsScreenSaver::activate() // no foreground window. pretend we got the event instead. DefWindowProc(NULL, WM_SYSCOMMAND, SC_SCREENSAVE, 0); } + + // restore power save when screen saver activates + CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY); } } @@ -179,6 +211,9 @@ CMSWindowsScreenSaver::deactivate() !m_wasEnabled, 0, SPIF_SENDWININICHANGE); SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, m_wasEnabled, 0, SPIF_SENDWININICHANGE); + + // disable display power down + CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY); } bool @@ -252,8 +287,11 @@ BOOL CALLBACK CMSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg) { if (IsWindowVisible(hwnd)) { - PostMessage(hwnd, WM_CLOSE, 0, 0); - *reinterpret_cast(arg) = true; + HINSTANCE instance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE); + if (instance != CMSWindowsScreen::getInstance()) { + PostMessage(hwnd, WM_CLOSE, 0, 0); + *reinterpret_cast(arg) = true; + } } return TRUE; } @@ -375,3 +413,60 @@ CMSWindowsScreenSaver::watchProcessThread(void*) } } } + +void +CMSWindowsScreenSaver::setSecure(bool secure, bool saveSecureAsInt) +{ + HKEY hkey = + CArchMiscWindows::openKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure); + if (hkey == NULL) { + return; + } + + const TCHAR* isSecure = m_is95Family ? g_isSecure9x : g_isSecureNT; + if (saveSecureAsInt) { + CArchMiscWindows::setValue(hkey, isSecure, secure ? 1 : 0); + } + else { + CArchMiscWindows::setValue(hkey, isSecure, secure ? "1" : "0"); + } + + CArchMiscWindows::closeKey(hkey); +} + +bool +CMSWindowsScreenSaver::isSecure(bool* wasSecureFlagAnInt) const +{ + // get the password protection setting key + HKEY hkey = + CArchMiscWindows::openKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure); + if (hkey == NULL) { + return false; + } + + // get the value. the value may be an int or a string, depending + // on the version of windows. + bool result; + const TCHAR* isSecure = m_is95Family ? g_isSecure9x : g_isSecureNT; + switch (CArchMiscWindows::typeOfValue(hkey, isSecure)) { + default: + result = false; + + case CArchMiscWindows::kUINT: { + DWORD value = + CArchMiscWindows::readValueInt(hkey, isSecure); + *wasSecureFlagAnInt = true; + result = (value != 0); + } + + case CArchMiscWindows::kSTRING: { + std::string value = + CArchMiscWindows::readValueString(hkey, isSecure); + *wasSecureFlagAnInt = false; + result = (value != "0"); + } + } + + CArchMiscWindows::closeKey(hkey); + return result; +} diff --git a/lib/platform/CMSWindowsScreenSaver.h b/lib/platform/CMSWindowsScreenSaver.h index b4c5feac..7cc7cdc9 100644 --- a/lib/platform/CMSWindowsScreenSaver.h +++ b/lib/platform/CMSWindowsScreenSaver.h @@ -16,6 +16,7 @@ #define CMSWINDOWSSCREENSAVER_H #include "IScreenSaver.h" +#include "CString.h" #define WIN32_LEAN_AND_MEAN #include @@ -65,11 +66,16 @@ private: void watchDesktopThread(void*); void watchProcessThread(void*); + void setSecure(bool secure, bool saveSecureAsInt); + bool isSecure(bool* wasSecureAnInt) const; + private: bool m_is95Family; bool m_is95; bool m_isNT; BOOL m_wasEnabled; + bool m_wasSecure; + bool m_wasSecureAnInt; HANDLE m_process; CThread* m_watch; From 8c1af9d53f1e728169cf886fc59b817fd95903fe Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 13 Mar 2004 17:14:32 +0000 Subject: [PATCH 603/807] Fixed win32 taskbar icon event handling. Wasn't responding to messages sent via SendMessage (rather than PostMessage). --- lib/platform/CMSWindowsEventQueueBuffer.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/platform/CMSWindowsEventQueueBuffer.cpp b/lib/platform/CMSWindowsEventQueueBuffer.cpp index 2f126095..69dee930 100644 --- a/lib/platform/CMSWindowsEventQueueBuffer.cpp +++ b/lib/platform/CMSWindowsEventQueueBuffer.cpp @@ -56,7 +56,7 @@ CMSWindowsEventQueueBuffer::waitForEvent(double timeout) // MsgWaitForMultipleObjects() will block even if the queue isn't // empty if the messages in the queue were there before the last // call to GetMessage()/PeekMessage(). - if (HIWORD(GetQueueStatus(QS_ALLEVENTS)) != 0) { + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { return; } @@ -73,12 +73,21 @@ CMSWindowsEventQueueBuffer::waitForEvent(double timeout) // cancellation but that's okay because we're run in the main // thread and we never cancel that thread. HANDLE dummy[1]; - MsgWaitForMultipleObjects(0, dummy, FALSE, t, QS_ALLEVENTS); + MsgWaitForMultipleObjects(0, dummy, FALSE, t, QS_ALLINPUT); } IEventQueueBuffer::Type CMSWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) { + // peek at messages first. waiting for QS_ALLINPUT will return + // if a message has been sent to our window but GetMessage will + // dispatch that message behind our backs and block. PeekMessage + // will also dispatch behind our backs but won't block. + if (!PeekMessage(&m_event, NULL, 0, 0, PM_NOREMOVE) && + !PeekMessage(&m_event, (HWND)-1, 0, 0, PM_NOREMOVE)) { + return kNone; + } + // BOOL. yeah, right. BOOL result = GetMessage(&m_event, NULL, 0, 0); if (result == -1) { @@ -113,7 +122,7 @@ CMSWindowsEventQueueBuffer::addEvent(UInt32 dataID) bool CMSWindowsEventQueueBuffer::isEmpty() const { - return (HIWORD(GetQueueStatus(QS_ALLEVENTS)) == 0); + return (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0); } CEventQueueTimer* From 12fa99aee0a2f802281af82a8eb7842905c46c8c Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 13 Mar 2004 17:16:24 +0000 Subject: [PATCH 604/807] Fixed handling of handshake complete. Was posting an event for it but making direct calls for other messages from the server. This could cause messages to be handled out of order. Now making a direct call for handshake complete. --- lib/client/CClient.cpp | 28 +++++++++------------------- lib/client/CClient.h | 7 ++++++- lib/client/CServerProxy.cpp | 12 +----------- lib/client/CServerProxy.h | 13 ------------- 4 files changed, 16 insertions(+), 44 deletions(-) diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 2d5ee1f1..35fefe1e 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -111,6 +111,14 @@ CClient::disconnect(const char* msg) } } +void +CClient::handshakeComplete() +{ + m_ready = true; + m_screen->enable(); + sendEvent(getConnectedEvent(), NULL); +} + bool CClient::isConnected() const { @@ -386,10 +394,6 @@ CClient::setupScreen() getEventTarget(), new TMethodEventJob(this, &CClient::handleClipboardGrabbed)); - EVENTQUEUE->adoptHandler(CServerProxy::getHandshakeCompleteEvent(), - m_server, - new TMethodEventJob(this, - &CClient::handleHandshakeComplete)); } void @@ -437,11 +441,7 @@ void CClient::cleanupScreen() { if (m_server != NULL) { - if (!m_ready) { - EVENTQUEUE->removeHandler(CServerProxy::getHandshakeCompleteEvent(), - m_server); - } - else { + if (m_ready) { m_screen->disable(); m_ready = false; } @@ -523,16 +523,6 @@ CClient::handleDisconnected(const CEvent&, void*) sendEvent(getDisconnectedEvent(), NULL); } -void -CClient::handleHandshakeComplete(const CEvent&, void*) -{ - m_ready = true; - EVENTQUEUE->removeHandler(CServerProxy::getHandshakeCompleteEvent(), - m_server); - sendEvent(getConnectedEvent(), NULL); - m_screen->enable(); -} - void CClient::handleShapeChanged(const CEvent&, void*) { diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 70386413..9c49d3cd 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -66,6 +66,12 @@ public: */ void disconnect(const char* msg); + //! Notify of handshake complete + /*! + Notifies the client that the connection handshake has completed. + */ + void handshakeComplete(); + //@} //! @name accessors //@{ @@ -152,7 +158,6 @@ private: void handleConnectTimeout(const CEvent&, void*); void handleOutputError(const CEvent&, void*); void handleDisconnected(const CEvent&, void*); - void handleHandshakeComplete(const CEvent&, void*); void handleShapeChanged(const CEvent&, void*); void handleClipboardGrabbed(const CEvent&, void*); void handleHello(const CEvent&, void*); diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index c72dc12f..b6568365 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -29,9 +29,6 @@ // CServerProxy // -CEvent::Type CServerProxy::s_handshakeCompleteEvent = - CEvent::kUnknown; - CServerProxy::CServerProxy(CClient* client, IStream* stream) : m_client(client), m_stream(stream), @@ -66,13 +63,6 @@ CServerProxy::~CServerProxy() m_stream->getEventTarget()); } -CEvent::Type -CServerProxy::getHandshakeCompleteEvent() -{ - return CEvent::registerTypeOnce(s_handshakeCompleteEvent, - "CServerProxy::handshakeComplete"); -} - void CServerProxy::installHeartBeat(double heartRate) { @@ -141,7 +131,7 @@ CServerProxy::parseHandshakeMessage(const UInt8* code) // handshake is complete m_parser = &CServerProxy::parseMessage; - EVENTQUEUE->addEvent(CEvent(getHandshakeCompleteEvent(), this)); + m_client->handshakeComplete(); } else if (memcmp(code, kMsgCResetOptions, 4) == 0) { diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index 3a06784e..61e5474b 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -47,17 +47,6 @@ public: void onClipboardChanged(ClipboardID, const IClipboard*); //@} - //! @name accessors - //@{ - - //! Get handshake complete event type - /*! - Returns the handshake complete event type. This is sent when the - client has completed the handshake with the server. - */ - static CEvent::Type getHandshakeCompleteEvent(); - - //@} protected: enum EResult { kOkay, kUnknown, kDisconnect }; @@ -116,8 +105,6 @@ private: double m_heartRate; MessageParser m_parser; - - static CEvent::Type s_handshakeCompleteEvent; }; #endif From 83713c6235403f86d25d023332d7d1e5862b8ec9 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 13 Mar 2004 18:58:20 +0000 Subject: [PATCH 605/807] Fixed error in previous submit. --- lib/platform/CMSWindowsScreen.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 5f10a45d..cd7727bf 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -27,6 +27,7 @@ class CEventQueueTimer; class CMSWindowsScreenSaver; class CThread; +class IJob; //! Implementation of IPlatformScreen for Microsoft Windows class CMSWindowsScreen : public IPlatformScreen { From 03dc45972baca4d1cfb7913bce1f7072363350a5 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 13 Mar 2004 19:01:27 +0000 Subject: [PATCH 606/807] Improved handling of active window on win32. Synergy no longer takes activation so the previously active window doesn't pop to the top of the window stack when it regains activation. One drawback of this is that the mouse cursor isn't shown when a window (other than synergy's) is activated. However, synergy does detect mouse motion as before and shows the cursor when it sees any. --- lib/platform/CMSWindowsScreen.cpp | 53 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 4bc2aae7..14409450 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -1685,25 +1685,22 @@ LRESULT CALLBACK CMSWindowsScreen::secondaryDeskProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + // would like to detect any local user input and hide the hider + // window but for now we just detect mouse motion. + bool hide = false; switch (msg) { case WM_MOUSEMOVE: - // hide window - ShowWindow(hwnd, SW_HIDE); - break; - - case WM_ACTIVATE: - // hide window - if (LOWORD(wParam) == WA_INACTIVE) { - ShowWindow(hwnd, SW_HIDE); + if (LOWORD(lParam) != 0 || HIWORD(lParam) != 0) { + hide = true; } break; + } - case WM_ACTIVATEAPP: - // hide window - if (!(BOOL)wParam) { - ShowWindow(hwnd, SW_HIDE); - } - break; + if (hide && IsWindowVisible(hwnd)) { + ReleaseCapture(); + SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | + SWP_NOACTIVATE | SWP_HIDEWINDOW); } return DefWindowProc(hwnd, msg, wParam, lParam); @@ -1789,15 +1786,19 @@ CMSWindowsScreen::deskMouseMove(SInt32 x, SInt32 y) const void CMSWindowsScreen::deskEnter(CDesk* desk) { - if (m_isPrimary) { - ShowCursor(TRUE); + if (!m_isPrimary) { + ReleaseCapture(); } - ShowWindow(desk->m_window, SW_HIDE); + ShowCursor(TRUE); + SetWindowPos(desk->m_window, HWND_BOTTOM, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | + SWP_NOACTIVATE | SWP_HIDEWINDOW); } void CMSWindowsScreen::deskLeave(CDesk* desk, HKL keyLayout) { + ShowCursor(FALSE); if (m_isPrimary) { // map a window to hide the cursor and to use whatever keyboard // layout we choose rather than the keyboard layout of the last @@ -1820,19 +1821,23 @@ CMSWindowsScreen::deskLeave(CDesk* desk, HKL keyLayout) w = m_w; h = m_h; } - SetWindowPos(desk->m_window, HWND_TOPMOST, x, y, w, h, SWP_NOACTIVATE); - ShowWindow(desk->m_window, SW_SHOW); - ShowCursor(FALSE); + SetWindowPos(desk->m_window, HWND_TOPMOST, x, y, w, h, + SWP_NOACTIVATE | SWP_SHOWWINDOW); // switch to requested keyboard layout ActivateKeyboardLayout(keyLayout, 0); } else { - // move hider window under the cursor center - MoveWindow(desk->m_window, m_xCenter, m_yCenter, 1, 1, FALSE); + // move hider window under the cursor center, raise, and show it + SetWindowPos(desk->m_window, HWND_TOPMOST, + m_xCenter, m_yCenter, 1, 1, + SWP_NOACTIVATE | SWP_SHOWWINDOW); - // raise and show the hider window - ShowWindow(desk->m_window, SW_SHOWNA); + // watch for mouse motion. if we see any then we hide the + // hider window so the user can use the physically attached + // mouse if desired. we'd rather not capture the mouse but + // we aren't notified when the mouse leaves our window. + SetCapture(desk->m_window); // warp the mouse to the cursor center deskMouseMove(m_xCenter, m_yCenter); From 6f19fcfe392a7b0a9e1067e641573786a50110cf Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 14 Mar 2004 17:50:37 +0000 Subject: [PATCH 607/807] Fixed doxygen formatting error. --- lib/io/IStream.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/io/IStream.h b/lib/io/IStream.h index 1dc07570..37d8514f 100644 --- a/lib/io/IStream.h +++ b/lib/io/IStream.h @@ -86,8 +86,6 @@ public: */ virtual void setEventFilter(IEventJob* filter) = 0; - //@} - //@} //! @name accessors //@{ From 20ba10bfa89159baa2f69d785c896d8e2aa5cf7a Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 14 Mar 2004 17:55:53 +0000 Subject: [PATCH 608/807] Changed how key state is tracked on X11. Now updating key state on every key press and release so we don't have to updateKeys() in isLockedToScreen(). However, if any key appears to be down we still call updateKeys() to double check that it's really down. If not we note the spurious lock and don't lock to the screen. --- lib/platform/CXWindowsKeyMapper.cpp | 16 +-- lib/platform/CXWindowsScreen.cpp | 96 ++++++++++------- lib/platform/CXWindowsScreen.h | 3 +- lib/synergy/CScreen.cpp | 45 ++++---- lib/synergy/CScreen.h | 9 +- lib/synergy/IKeyState.h | 4 +- lib/synergy/IPlatformScreen.cpp | 157 ---------------------------- lib/synergy/IPlatformScreen.h | 112 ++++++-------------- lib/synergy/IPrimaryScreen.cpp | 157 ++++++++++++++++++++++++++++ lib/synergy/IPrimaryScreen.h | 91 +++++++++++++++- lib/synergy/Makefile.am | 2 +- 11 files changed, 382 insertions(+), 310 deletions(-) delete mode 100644 lib/synergy/IPlatformScreen.cpp create mode 100644 lib/synergy/IPrimaryScreen.cpp diff --git a/lib/platform/CXWindowsKeyMapper.cpp b/lib/platform/CXWindowsKeyMapper.cpp index 35526a05..5ff68ebd 100644 --- a/lib/platform/CXWindowsKeyMapper.cpp +++ b/lib/platform/CXWindowsKeyMapper.cpp @@ -173,21 +173,21 @@ CXWindowsKeyMapper::update(Display* display, IKeyState* keyState) // transfer to our state for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) { if ((keys[i] & 0x01) != 0) - keyState->setKeyDown(j + 0); + keyState->setKeyDown(j + 0, true); if ((keys[i] & 0x02) != 0) - keyState->setKeyDown(j + 1); + keyState->setKeyDown(j + 1, true); if ((keys[i] & 0x04) != 0) - keyState->setKeyDown(j + 2); + keyState->setKeyDown(j + 2, true); if ((keys[i] & 0x08) != 0) - keyState->setKeyDown(j + 3); + keyState->setKeyDown(j + 3, true); if ((keys[i] & 0x10) != 0) - keyState->setKeyDown(j + 4); + keyState->setKeyDown(j + 4, true); if ((keys[i] & 0x20) != 0) - keyState->setKeyDown(j + 5); + keyState->setKeyDown(j + 5, true); if ((keys[i] & 0x40) != 0) - keyState->setKeyDown(j + 6); + keyState->setKeyDown(j + 6, true); if ((keys[i] & 0x80) != 0) - keyState->setKeyDown(j + 7); + keyState->setKeyDown(j + 7, true); } // set toggle modifier states diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index de18c1d0..da9a705c 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -184,8 +184,8 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) : // install event handlers EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(), - new TMethodEventJob(this, - &CXWindowsScreen::handleSystemEvent)); + new TMethodEventJob(this, + &IPlatformScreen::handleSystemEvent)); // install the platform event queue EVENTQUEUE->adoptBuffer(new CXWindowsEventQueueBuffer(m_display, m_window)); @@ -244,6 +244,9 @@ CXWindowsScreen::enable() // warp the mouse to the cursor center fakeMouseMove(m_xCenter, m_yCenter); } + else { + m_keyState->updateKeys(); + } } void @@ -544,6 +547,21 @@ CXWindowsScreen::isAnyMouseButtonDown() const return false; } +KeyModifierMask +CXWindowsScreen::getActiveModifiers() const +{ + // query the pointer to get the modifier state + Window root, window; + int xRoot, yRoot, xWindow, yWindow; + unsigned int state; + if (XQueryPointer(m_display, m_root, &root, &window, + &xRoot, &yRoot, &xWindow, &yWindow, &state)) { + return m_keyMapper.mapModifier(state); + } + + return 0; +} + void CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const { @@ -841,12 +859,49 @@ CXWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) sendEvent(type, info); } +Bool +CXWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) +{ + CKeyEventFilter* filter = reinterpret_cast(arg); + return (xevent->type == filter->m_event && + xevent->xkey.window == filter->m_window && + xevent->xkey.time == filter->m_time && + xevent->xkey.keycode == filter->m_keycode) ? True : False; +} + void CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) { XEvent* xevent = reinterpret_cast(event.getData()); assert(xevent != NULL); + // update key state + bool isRepeat = false; + if (m_isPrimary) { + if (xevent->type == KeyRelease) { + // check if this is a key repeat by getting the next + // KeyPress event that has the same key and time as + // this release event, if any. first prepare the + // filter info. + CKeyEventFilter filter; + filter.m_event = KeyPress; + filter.m_window = xevent->xkey.window; + filter.m_time = xevent->xkey.time; + filter.m_keycode = xevent->xkey.keycode; + XEvent xevent2; + isRepeat = (XCheckIfEvent(m_display, &xevent2, + &CXWindowsScreen::findKeyEvent, + (XPointer)&filter) == True); + } + + if (xevent->type == KeyPress || xevent->type == KeyRelease) { + if (!isRepeat) { + m_keyState->setKeyDown(xevent->xkey.keycode, + xevent->type == KeyPress); + } + } + } + // let input methods try to handle event first if (m_ic != NULL) { // XFilterEvent() may eat the event and generate a new KeyPress @@ -977,7 +1032,7 @@ CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) case KeyRelease: if (m_isPrimary) { - onKeyRelease(xevent->xkey); + onKeyRelease(xevent->xkey, isRepeat); } return; @@ -1037,41 +1092,12 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey) } } -Bool -CXWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) -{ - CKeyEventFilter* filter = reinterpret_cast(arg); - return (xevent->type == filter->m_event && - xevent->xkey.window == filter->m_window && - xevent->xkey.time == filter->m_time && - xevent->xkey.keycode == filter->m_keycode) ? True : False; -} - void -CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) +CXWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat) { const KeyModifierMask mask = m_keyMapper.mapModifier(xkey.state); KeyID key = mapKeyFromX(&xkey); if (key != kKeyNone) { - // check if this is a key repeat by getting the next - // KeyPress event that has the same key and time as - // this release event, if any. first prepare the - // filter info. - CKeyEventFilter filter; - filter.m_event = KeyPress; - filter.m_window = xkey.window; - filter.m_time = xkey.time; - filter.m_keycode = xkey.keycode; - - // now check for event - bool hasPress; - { - XEvent xevent2; - hasPress = (XCheckIfEvent(m_display, &xevent2, - &CXWindowsScreen::findKeyEvent, - (XPointer)&filter) == True); - } - // check for ctrl+alt+del emulation if ((key == kKeyPause || key == kKeyBreak) && (mask & (KeyModifierControl | KeyModifierAlt)) == @@ -1079,11 +1105,11 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) // pretend it's ctrl+alt+del and ignore autorepeat LOG((CLOG_DEBUG "emulate ctrl+alt+del")); key = kKeyDelete; - hasPress = false; + isRepeat = false; } KeyButton keycode = static_cast(xkey.keycode); - if (!hasPress) { + if (!isRepeat) { // no press event follows so it's a plain release LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", keycode, xkey.state)); KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index b56c693d..7298190f 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -67,6 +67,7 @@ public: virtual void warpCursor(SInt32 x, SInt32 y); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; + virtual KeyModifierMask getActiveModifiers() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; virtual const char* getKeyName(KeyButton) const; @@ -123,7 +124,7 @@ private: bool grabMouseAndKeyboard(); void onKeyPress(XKeyEvent&); - void onKeyRelease(XKeyEvent&); + void onKeyRelease(XKeyEvent&, bool isRepeat); void onMousePress(const XButtonEvent&); void onMouseRelease(const XButtonEvent&); void onMouseMove(const XMotionEvent&); diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index a4164f55..dde44703 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -394,21 +394,22 @@ CScreen::isLockedToScreen() const return true; } - // we don't keep primary key state up to date so get the - // current state. - const_cast(this)->updateKeys(); - - // check for scroll lock toggled on - if (isModifierActive(KeyModifierScrollLock)) { - LOG((CLOG_DEBUG "locked by scroll lock")); - return true; - } - // check for any pressed key KeyButton key = isAnyKeyDown(); if (key != 0) { - LOG((CLOG_DEBUG "locked by %s", m_screen->getKeyName(key))); - return true; + // double check current state of the keys. this shouldn't + // be necessary but we don't seem to get some key release + // events sometimes. this is an emergency backup so the + // client doesn't get stuck on the screen. + const_cast(this)->updateKeys(); + KeyButton key2 = isAnyKeyDown(); + if (key2 != 0) { + LOG((CLOG_DEBUG "locked by %s", m_screen->getKeyName(key2))); + return true; + } + else { + LOG((CLOG_DEBUG "spuriously locked by %s", m_screen->getKeyName(key))); + } } // not locked @@ -476,6 +477,7 @@ CScreen::updateKeys() void CScreen::releaseKeys() { +LOG((CLOG_INFO "releaseKeys")); // FIXME // release keys that we've synthesized a press for and only those // keys. we don't want to synthesize a release on a key the user // is still physically pressing. @@ -489,9 +491,16 @@ CScreen::releaseKeys() } void -CScreen::setKeyDown(KeyButton key) +CScreen::setKeyDown(KeyButton key, bool down) { - m_keys[key & 0xffu] |= kDown; + if (!isHalfDuplex(getMaskForKey(key))) { + if (down) { + m_keys[key & 0xffu] |= kDown; + } + else { + m_keys[key & 0xffu] &= ~kDown; + } + } } void @@ -614,11 +623,11 @@ KeyModifierMask CScreen::getActiveModifiers() const { if (m_isPrimary) { - // we don't keep primary key state up to date so get the - // current state. - const_cast(this)->updateKeys(); + return m_screen->getActiveModifiers(); + } + else { + return m_mask; } - return m_mask; } bool diff --git a/lib/synergy/CScreen.h b/lib/synergy/CScreen.h index a20c021e..524bb0bd 100644 --- a/lib/synergy/CScreen.h +++ b/lib/synergy/CScreen.h @@ -187,10 +187,9 @@ public: //! Get screen lock state /*! Returns true if there's any reason that the user should not be - allowed to leave the screen. Active toggle keys (excluding the - scroll lock key) are not be counted as reasons to lock to the - screen. If this method returns true it logs a message on why at - the CLOG_DEBUG level. + allowed to leave the screen (usually because a button or key is + pressed). If this method returns true it logs a message as to + why at the CLOG_DEBUG level. */ bool isLockedToScreen() const; @@ -221,7 +220,7 @@ public: // IKeyState overrides virtual void updateKeys(); virtual void releaseKeys(); - virtual void setKeyDown(KeyButton key); + virtual void setKeyDown(KeyButton key, bool); virtual void setToggled(KeyModifierMask); virtual void addModifier(KeyModifierMask, KeyButtons&); virtual void setToggleState(KeyModifierMask); diff --git a/lib/synergy/IKeyState.h b/lib/synergy/IKeyState.h index 14a415c2..f8dec884 100644 --- a/lib/synergy/IKeyState.h +++ b/lib/synergy/IKeyState.h @@ -51,9 +51,9 @@ public: //! Mark key as being down /*! - Sets the state of \c key to down. + Sets the state of \c key to down or up. */ - virtual void setKeyDown(KeyButton key) = 0; + virtual void setKeyDown(KeyButton key, bool down) = 0; //! Mark modifier as being toggled on /*! diff --git a/lib/synergy/IPlatformScreen.cpp b/lib/synergy/IPlatformScreen.cpp deleted file mode 100644 index a9a37418..00000000 --- a/lib/synergy/IPlatformScreen.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2004 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "IPlatformScreen.h" - -// -// IPlatformScreen -// - -CEvent::Type IPlatformScreen::s_keyDownEvent = CEvent::kUnknown; -CEvent::Type IPlatformScreen::s_keyUpEvent = CEvent::kUnknown; -CEvent::Type IPlatformScreen::s_keyRepeatEvent = CEvent::kUnknown; -CEvent::Type IPlatformScreen::s_buttonDownEvent = CEvent::kUnknown; -CEvent::Type IPlatformScreen::s_buttonUpEvent = CEvent::kUnknown; -CEvent::Type IPlatformScreen::s_motionPrimaryEvent = CEvent::kUnknown; -CEvent::Type IPlatformScreen::s_motionSecondaryEvent = CEvent::kUnknown; -CEvent::Type IPlatformScreen::s_wheelEvent = CEvent::kUnknown; -CEvent::Type IPlatformScreen::s_ssActivatedEvent = CEvent::kUnknown; -CEvent::Type IPlatformScreen::s_ssDeactivatedEvent = CEvent::kUnknown; - -CEvent::Type -IPlatformScreen::getKeyDownEvent() -{ - return CEvent::registerTypeOnce(s_keyDownEvent, - "IPlatformScreen::keyDown"); -} - -CEvent::Type -IPlatformScreen::getKeyUpEvent() -{ - return CEvent::registerTypeOnce(s_keyUpEvent, - "IPlatformScreen::keyUp"); -} - -CEvent::Type -IPlatformScreen::getKeyRepeatEvent() -{ - return CEvent::registerTypeOnce(s_keyRepeatEvent, - "IPlatformScreen::keyRepeat"); -} - -CEvent::Type -IPlatformScreen::getButtonDownEvent() -{ - return CEvent::registerTypeOnce(s_buttonDownEvent, - "IPlatformScreen::buttonDown"); -} - -CEvent::Type -IPlatformScreen::getButtonUpEvent() -{ - return CEvent::registerTypeOnce(s_buttonUpEvent, - "IPlatformScreen::buttonUp"); -} - -CEvent::Type -IPlatformScreen::getMotionOnPrimaryEvent() -{ - return CEvent::registerTypeOnce(s_motionPrimaryEvent, - "IPlatformScreen::motionPrimary"); -} - -CEvent::Type -IPlatformScreen::getMotionOnSecondaryEvent() -{ - return CEvent::registerTypeOnce(s_motionSecondaryEvent, - "IPlatformScreen::motionSecondary"); -} - -CEvent::Type -IPlatformScreen::getWheelEvent() -{ - return CEvent::registerTypeOnce(s_wheelEvent, - "IPlatformScreen::wheel"); -} - -CEvent::Type -IPlatformScreen::getScreensaverActivatedEvent() -{ - return CEvent::registerTypeOnce(s_ssActivatedEvent, - "IPlatformScreen::screensaverActivated"); -} - -CEvent::Type -IPlatformScreen::getScreensaverDeactivatedEvent() -{ - return CEvent::registerTypeOnce(s_ssDeactivatedEvent, - "IPlatformScreen::screensaverDeactivated"); -} - - -// -// IPlatformScreen::CKeyInfo -// - -IPlatformScreen::CKeyInfo* -IPlatformScreen::CKeyInfo::alloc(KeyID id, - KeyModifierMask mask, KeyButton button, SInt32 count) -{ - CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); - info->m_key = id; - info->m_mask = mask; - info->m_button = button; - info->m_count = count; - return info; -} - - -// -// IPlatformScreen::CButtonInfo -// - -IPlatformScreen::CButtonInfo* -IPlatformScreen::CButtonInfo::alloc(ButtonID id) -{ - CButtonInfo* info = (CButtonInfo*)malloc(sizeof(CButtonInfo)); - info->m_button = id; - return info; -} - - -// -// IPlatformScreen::CMotionInfo -// - -IPlatformScreen::CMotionInfo* -IPlatformScreen::CMotionInfo::alloc(SInt32 x, SInt32 y) -{ - CMotionInfo* info = (CMotionInfo*)malloc(sizeof(CMotionInfo)); - info->m_x = x; - info->m_y = y; - return info; -} - - -// -// IPlatformScreen::CWheelInfo -// - -IPlatformScreen::CWheelInfo* -IPlatformScreen::CWheelInfo::alloc(SInt32 wheel) -{ - CWheelInfo* info = (CWheelInfo*)malloc(sizeof(CWheelInfo)); - info->m_wheel = wheel; - return info; -} diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index bae81e86..379ef03d 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -20,7 +20,6 @@ #include "ISecondaryScreen.h" #include "ClipboardTypes.h" #include "OptionTypes.h" -#include "CEvent.h" class IClipboard; class IKeyState; @@ -29,51 +28,11 @@ class IKeyState; /*! This interface defines the methods common to all platform dependent screen implementations that are used by both primary and secondary -screens. A platform screen is expected to post the events defined -in \c IScreen when appropriate. It should also post events defined -in \c IPlatformScreen if acting as the primary screen. The target -on the events should be the value returned by \c getEventTarget(). +screens. */ class IPlatformScreen : public IScreen, public IPrimaryScreen, public ISecondaryScreen { public: - //! Key event data - class CKeyInfo { - public: - static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count); - - public: - KeyID m_key; - KeyModifierMask m_mask; - KeyButton m_button; - SInt32 m_count; - }; - //! Button event data - class CButtonInfo { - public: - static CButtonInfo* alloc(ButtonID); - - public: - ButtonID m_button; - }; - //! Motion event data - class CMotionInfo { - public: - static CMotionInfo* alloc(SInt32 x, SInt32 y); - - public: - SInt32 m_x; - SInt32 m_y; - }; - //! Wheel motion event data - class CWheelInfo { - public: - static CWheelInfo* alloc(SInt32); - - public: - SInt32 m_wheel; - }; - //! @name manipulators //@{ @@ -167,7 +126,8 @@ public: //! Get keyboard state /*! - Put the current keyboard state into the IKeyState passed to \c open(). + Put the current keyboard state into the IKeyState passed to + \c setKeyState(). */ virtual void updateKeys() = 0; @@ -187,34 +147,6 @@ public: */ virtual bool isPrimary() const = 0; - //! Get key down event type. Event data is CKeyInfo*, count == 1. - static CEvent::Type getKeyDownEvent(); - //! Get key up event type. Event data is CKeyInfo*, count == 1. - static CEvent::Type getKeyUpEvent(); - //! Get key repeat event type. Event data is CKeyInfo*. - static CEvent::Type getKeyRepeatEvent(); - //! Get button down event type. Event data is CButtonInfo*. - static CEvent::Type getButtonDownEvent(); - //! Get button up event type. Event data is CButtonInfo*. - static CEvent::Type getButtonUpEvent(); - //! Get mouse motion on the primary screen event type - /*! - Event data is CMotionInfo* and the values are an absolute position. - */ - static CEvent::Type getMotionOnPrimaryEvent(); - //! Get mouse motion on a secondary screen event type - /*! - Event data is CMotionInfo* and the values are motion deltas not - absolute coordinates. - */ - static CEvent::Type getMotionOnSecondaryEvent(); - //! Get mouse wheel event type. Event data is CWheelInfo*. - static CEvent::Type getWheelEvent(); - //! Get screensaver activated event type - static CEvent::Type getScreensaverActivatedEvent(); - //! Get screensaver deactivated event type - static CEvent::Type getScreensaverDeactivatedEvent(); - //@} // IScreen overrides @@ -229,6 +161,7 @@ public: virtual void warpCursor(SInt32 x, SInt32 y) = 0; virtual SInt32 getJumpZoneSize() const = 0; virtual bool isAnyMouseButtonDown() const = 0; + virtual KeyModifierMask getActiveModifiers() const = 0; virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; virtual const char* getKeyName(KeyButton) const = 0; @@ -243,17 +176,32 @@ public: KeyModifierMask desiredMask, bool isAutoRepeat) const = 0; -private: - static CEvent::Type s_keyDownEvent; - static CEvent::Type s_keyUpEvent; - static CEvent::Type s_keyRepeatEvent; - static CEvent::Type s_buttonDownEvent; - static CEvent::Type s_buttonUpEvent; - static CEvent::Type s_motionPrimaryEvent; - static CEvent::Type s_motionSecondaryEvent; - static CEvent::Type s_wheelEvent; - static CEvent::Type s_ssActivatedEvent; - static CEvent::Type s_ssDeactivatedEvent; +protected: + //! Handle system event + /*! + A platform screen is expected to install a handler for system + events in its c'tor like so: + \code + EVENTQUEUE->adoptHandler(CEvent::kSystem, + IEventQueue::getSystemTarget(), + new TMethodEventJob(this, + &IPlatformScreen::handleSystemEvent)); + \endcode + It should remove the handler in its d'tor. Override the + \c handleSystemEvent() method to process system events. + It should post the events \c IScreen as appropriate. + + A primary screen has further responsibilities. It should post + the events in \c IPrimaryScreen as appropriate. It should also + call \c setKeyDown() on the \c IKeyState passed to \c setKeyState() + whenever a key is pressed or released (but not for key repeats). + And it should call \c updateKeys() on the \c IKeyState if necessary + when the keyboard mapping changes. + + The target of all events should be the value returned by + \c getEventTarget(). + */ + virtual void handleSystemEvent(const CEvent& event, void*) = 0; }; #endif diff --git a/lib/synergy/IPrimaryScreen.cpp b/lib/synergy/IPrimaryScreen.cpp new file mode 100644 index 00000000..9a201001 --- /dev/null +++ b/lib/synergy/IPrimaryScreen.cpp @@ -0,0 +1,157 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "IPrimaryScreen.h" + +// +// IPrimaryScreen +// + +CEvent::Type IPrimaryScreen::s_keyDownEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_keyUpEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_keyRepeatEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_buttonDownEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_buttonUpEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_motionPrimaryEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_motionSecondaryEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_wheelEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_ssActivatedEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_ssDeactivatedEvent = CEvent::kUnknown; + +CEvent::Type +IPrimaryScreen::getKeyDownEvent() +{ + return CEvent::registerTypeOnce(s_keyDownEvent, + "IPrimaryScreen::keyDown"); +} + +CEvent::Type +IPrimaryScreen::getKeyUpEvent() +{ + return CEvent::registerTypeOnce(s_keyUpEvent, + "IPrimaryScreen::keyUp"); +} + +CEvent::Type +IPrimaryScreen::getKeyRepeatEvent() +{ + return CEvent::registerTypeOnce(s_keyRepeatEvent, + "IPrimaryScreen::keyRepeat"); +} + +CEvent::Type +IPrimaryScreen::getButtonDownEvent() +{ + return CEvent::registerTypeOnce(s_buttonDownEvent, + "IPrimaryScreen::buttonDown"); +} + +CEvent::Type +IPrimaryScreen::getButtonUpEvent() +{ + return CEvent::registerTypeOnce(s_buttonUpEvent, + "IPrimaryScreen::buttonUp"); +} + +CEvent::Type +IPrimaryScreen::getMotionOnPrimaryEvent() +{ + return CEvent::registerTypeOnce(s_motionPrimaryEvent, + "IPrimaryScreen::motionPrimary"); +} + +CEvent::Type +IPrimaryScreen::getMotionOnSecondaryEvent() +{ + return CEvent::registerTypeOnce(s_motionSecondaryEvent, + "IPrimaryScreen::motionSecondary"); +} + +CEvent::Type +IPrimaryScreen::getWheelEvent() +{ + return CEvent::registerTypeOnce(s_wheelEvent, + "IPrimaryScreen::wheel"); +} + +CEvent::Type +IPrimaryScreen::getScreensaverActivatedEvent() +{ + return CEvent::registerTypeOnce(s_ssActivatedEvent, + "IPrimaryScreen::screensaverActivated"); +} + +CEvent::Type +IPrimaryScreen::getScreensaverDeactivatedEvent() +{ + return CEvent::registerTypeOnce(s_ssDeactivatedEvent, + "IPrimaryScreen::screensaverDeactivated"); +} + + +// +// IPrimaryScreen::CKeyInfo +// + +IPrimaryScreen::CKeyInfo* +IPrimaryScreen::CKeyInfo::alloc(KeyID id, + KeyModifierMask mask, KeyButton button, SInt32 count) +{ + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); + info->m_key = id; + info->m_mask = mask; + info->m_button = button; + info->m_count = count; + return info; +} + + +// +// IPrimaryScreen::CButtonInfo +// + +IPrimaryScreen::CButtonInfo* +IPrimaryScreen::CButtonInfo::alloc(ButtonID id) +{ + CButtonInfo* info = (CButtonInfo*)malloc(sizeof(CButtonInfo)); + info->m_button = id; + return info; +} + + +// +// IPrimaryScreen::CMotionInfo +// + +IPrimaryScreen::CMotionInfo* +IPrimaryScreen::CMotionInfo::alloc(SInt32 x, SInt32 y) +{ + CMotionInfo* info = (CMotionInfo*)malloc(sizeof(CMotionInfo)); + info->m_x = x; + info->m_y = y; + return info; +} + + +// +// IPrimaryScreen::CWheelInfo +// + +IPrimaryScreen::CWheelInfo* +IPrimaryScreen::CWheelInfo::alloc(SInt32 wheel) +{ + CWheelInfo* info = (CWheelInfo*)malloc(sizeof(CWheelInfo)); + info->m_wheel = wheel; + return info; +} diff --git a/lib/synergy/IPrimaryScreen.h b/lib/synergy/IPrimaryScreen.h index fb3f6ccb..9b88caf8 100644 --- a/lib/synergy/IPrimaryScreen.h +++ b/lib/synergy/IPrimaryScreen.h @@ -17,6 +17,8 @@ #include "IInterface.h" #include "IKeyState.h" +#include "CEvent.h" +#include "MouseTypes.h" //! Primary screen interface /*! @@ -25,6 +27,43 @@ primary screen implementations. */ class IPrimaryScreen : public IInterface { public: + //! Key event data + class CKeyInfo { + public: + static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count); + + public: + KeyID m_key; + KeyModifierMask m_mask; + KeyButton m_button; + SInt32 m_count; + }; + //! Button event data + class CButtonInfo { + public: + static CButtonInfo* alloc(ButtonID); + + public: + ButtonID m_button; + }; + //! Motion event data + class CMotionInfo { + public: + static CMotionInfo* alloc(SInt32 x, SInt32 y); + + public: + SInt32 m_x; + SInt32 m_y; + }; + //! Wheel motion event data + class CWheelInfo { + public: + static CWheelInfo* alloc(SInt32); + + public: + SInt32 m_wheel; + }; + //! @name manipulators //@{ @@ -58,10 +97,20 @@ public: //! Test if mouse is pressed /*! - Return true if any mouse button is currently pressed. + Return true if any mouse button is currently pressed. Ideally, + "current" means up to the last processed event but it can mean + the current physical mouse button state. */ virtual bool isAnyMouseButtonDown() const = 0; + //! Get current modifier key state + /*! + Returns the current modifier key state. Ideally, "current" means + up to the lat processed event but it can mean the current physical + modifier key state. + */ + virtual KeyModifierMask getActiveModifiers() const = 0; + //! Get cursor center position /*! Return the cursor center position which is where we park the @@ -76,7 +125,47 @@ public: */ virtual const char* getKeyName(KeyButton) const = 0; + //! Get key down event type. Event data is CKeyInfo*, count == 1. + static CEvent::Type getKeyDownEvent(); + //! Get key up event type. Event data is CKeyInfo*, count == 1. + static CEvent::Type getKeyUpEvent(); + //! Get key repeat event type. Event data is CKeyInfo*. + static CEvent::Type getKeyRepeatEvent(); + //! Get button down event type. Event data is CButtonInfo*. + static CEvent::Type getButtonDownEvent(); + //! Get button up event type. Event data is CButtonInfo*. + static CEvent::Type getButtonUpEvent(); + //! Get mouse motion on the primary screen event type + /*! + Event data is CMotionInfo* and the values are an absolute position. + */ + static CEvent::Type getMotionOnPrimaryEvent(); + //! Get mouse motion on a secondary screen event type + /*! + Event data is CMotionInfo* and the values are motion deltas not + absolute coordinates. + */ + static CEvent::Type getMotionOnSecondaryEvent(); + //! Get mouse wheel event type. Event data is CWheelInfo*. + static CEvent::Type getWheelEvent(); + //! Get screensaver activated event type + static CEvent::Type getScreensaverActivatedEvent(); + //! Get screensaver deactivated event type + static CEvent::Type getScreensaverDeactivatedEvent(); + //@} + +private: + static CEvent::Type s_keyDownEvent; + static CEvent::Type s_keyUpEvent; + static CEvent::Type s_keyRepeatEvent; + static CEvent::Type s_buttonDownEvent; + static CEvent::Type s_buttonUpEvent; + static CEvent::Type s_motionPrimaryEvent; + static CEvent::Type s_motionSecondaryEvent; + static CEvent::Type s_wheelEvent; + static CEvent::Type s_ssActivatedEvent; + static CEvent::Type s_ssDeactivatedEvent; }; #endif diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index e34363d7..60a304ca 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -30,7 +30,7 @@ libsynergy_a_SOURCES = \ CProtocolUtil.cpp \ CScreen.cpp \ IClipboard.cpp \ - IPlatformScreen.cpp \ + IPrimaryScreen.cpp \ IScreen.cpp \ XScreen.cpp \ XSynergy.cpp \ From 19559d4b4e1489bdc076b129029c7feb78694862 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 17 Mar 2004 20:59:25 +0000 Subject: [PATCH 609/807] Updated keyboard handling on win32. Still needs some work to avoid shadowing key state in multiple places. Also got locked to screen and reported key appeared to be wrong. --- lib/platform/CMSWindowsKeyMapper.cpp | 615 ++++++++++++++------------- lib/platform/CMSWindowsKeyMapper.h | 65 ++- lib/platform/CMSWindowsScreen.cpp | 204 ++++----- lib/platform/CMSWindowsScreen.h | 5 + lib/synergy/CScreen.cpp | 28 +- lib/synergy/CScreen.h | 4 +- lib/synergy/IKeyState.h | 2 +- lib/synergy/libsynergy.dsp | 2 +- 8 files changed, 464 insertions(+), 461 deletions(-) diff --git a/lib/platform/CMSWindowsKeyMapper.cpp b/lib/platform/CMSWindowsKeyMapper.cpp index 8cc2641c..b07da1fd 100644 --- a/lib/platform/CMSWindowsKeyMapper.cpp +++ b/lib/platform/CMSWindowsKeyMapper.cpp @@ -14,6 +14,7 @@ #include "CMSWindowsKeyMapper.h" #include "CLog.h" +#include "CStringUtil.h" // multimedia keys #if !defined(VK_BROWSER_BACK) @@ -41,22 +42,6 @@ // CMSWindowsKeyMapper // -// table of modifier keys. note that VK_RMENU shows up under the Alt -// key and ModeSwitch. when simulating AltGr we need to use the right -// alt key so we use KeyModifierModeSwitch to get it. -const CMSWindowsKeyMapper::CModifierKeys - CMSWindowsKeyMapper::s_modifiers[] = -{ - KeyModifierShift, { VK_LSHIFT, VK_RSHIFT }, - KeyModifierControl, { VK_LCONTROL, VK_RCONTROL | 0x100 }, - KeyModifierAlt, { VK_LMENU, VK_RMENU | 0x100 }, - KeyModifierSuper, { VK_LWIN | 0x100, VK_RWIN | 0x100 }, - KeyModifierModeSwitch, { VK_RMENU | 0x100, 0 }, - KeyModifierCapsLock, { VK_CAPITAL, 0 }, - KeyModifierNumLock, { VK_NUMLOCK | 0x100, 0 }, - KeyModifierScrollLock, { VK_SCROLL, 0 } -}; - const char* CMSWindowsKeyMapper::s_vkToName[] = { "vk 0x00", @@ -578,10 +563,8 @@ const KeyID CMSWindowsKeyMapper::s_virtualKey[][2] = /* 0xff */ kKeyNone, kKeyNone // reserved }; -// map special KeyID keys to virtual key codes. if the key is an -// extended key then the entry is the virtual key code | 0x100. -// unmapped keys have a 0 entry. -const KeyButton CMSWindowsKeyMapper::s_mapE000[] = +// map special KeyID keys to virtual key codes +const UINT CMSWindowsKeyMapper::s_mapE000[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -604,15 +587,15 @@ const KeyButton CMSWindowsKeyMapper::s_mapE000[] = /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */ 0, 0, 0, 0, - /* 0xa4 */ 0, 0, VK_BROWSER_BACK|0x100, VK_BROWSER_FORWARD|0x100, - /* 0xa8 */ VK_BROWSER_REFRESH|0x100, VK_BROWSER_STOP|0x100, - /* 0xaa */ VK_BROWSER_SEARCH|0x100, VK_BROWSER_FAVORITES|0x100, - /* 0xac */ VK_BROWSER_HOME|0x100, VK_VOLUME_MUTE|0x100, - /* 0xae */ VK_VOLUME_DOWN|0x100, VK_VOLUME_UP|0x100, - /* 0xb0 */ VK_MEDIA_NEXT_TRACK|0x100, VK_MEDIA_PREV_TRACK|0x100, - /* 0xb2 */ VK_MEDIA_STOP|0x100, VK_MEDIA_PLAY_PAUSE|0x100, - /* 0xb4 */ VK_LAUNCH_MAIL|0x100, VK_LAUNCH_MEDIA_SELECT|0x100, - /* 0xb6 */ VK_LAUNCH_APP1|0x100, VK_LAUNCH_APP2|0x100, + /* 0xa4 */ 0, 0, VK_BROWSER_BACK, VK_BROWSER_FORWARD, + /* 0xa8 */ VK_BROWSER_REFRESH, VK_BROWSER_STOP, + /* 0xaa */ VK_BROWSER_SEARCH, VK_BROWSER_FAVORITES, + /* 0xac */ VK_BROWSER_HOME, VK_VOLUME_MUTE, + /* 0xae */ VK_VOLUME_DOWN, VK_VOLUME_UP, + /* 0xb0 */ VK_MEDIA_NEXT_TRACK, VK_MEDIA_PREV_TRACK, + /* 0xb2 */ VK_MEDIA_STOP, VK_MEDIA_PLAY_PAUSE, + /* 0xb4 */ VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, + /* 0xb6 */ VK_LAUNCH_APP1, VK_LAUNCH_APP2, /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -623,7 +606,7 @@ const KeyButton CMSWindowsKeyMapper::s_mapE000[] = /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 }; -const KeyButton CMSWindowsKeyMapper::s_mapEE00[] = +const UINT CMSWindowsKeyMapper::s_mapEE00[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -661,7 +644,7 @@ const KeyButton CMSWindowsKeyMapper::s_mapEE00[] = /* in g_mapEF00, 0xac is VK_DECIMAL not VK_SEPARATOR because win32 * doesn't seem to use VK_SEPARATOR but instead maps VK_DECIMAL to * the same meaning. */ -const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = +const UINT CMSWindowsKeyMapper::s_mapEF00[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, @@ -673,22 +656,22 @@ const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ VK_HOME|0x100, VK_LEFT|0x100, VK_UP|0x100, VK_RIGHT|0x100, - /* 0x54 */ VK_DOWN|0x100, VK_PRIOR|0x100, VK_NEXT|0x100, VK_END|0x100, + /* 0x50 */ VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, + /* 0x54 */ VK_DOWN, VK_PRIOR, VK_NEXT, VK_END, /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ VK_SELECT|0x100, VK_SNAPSHOT|0x100, VK_EXECUTE|0x100, VK_INSERT|0x100, - /* 0x64 */ 0, 0, 0, VK_APPS|0x100, - /* 0x68 */ 0, 0, VK_HELP|0x100, VK_CANCEL|0x100, 0, 0, 0, 0, + /* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT, + /* 0x64 */ 0, 0, 0, VK_APPS, + /* 0x68 */ 0, 0, VK_HELP, VK_CANCEL, 0, 0, 0, 0, /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK|0x100, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK, /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN|0x100, 0, 0, + /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN, 0, 0, /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, /* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, - /* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100, + /* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE, /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, @@ -697,11 +680,11 @@ const KeyButton CMSWindowsKeyMapper::s_mapEF00[] = /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, - /* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, 0, - /* 0xe8 */ 0, VK_LMENU, VK_RMENU|0x100, VK_LWIN|0x100, - /* 0xec */ VK_RWIN|0x100, 0, 0, 0, + /* 0xe4 */ VK_RCONTROL, VK_CAPITAL, 0, 0, + /* 0xe8 */ 0, VK_LMENU, VK_RMENU, VK_LWIN, + /* 0xec */ VK_RWIN, 0, 0, 0, /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100 + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE }; CMSWindowsKeyMapper::CMSWindowsKeyMapper() : m_deadKey(0) @@ -717,156 +700,169 @@ CMSWindowsKeyMapper::~CMSWindowsKeyMapper() void CMSWindowsKeyMapper::update(IKeyState* keyState) { - static const size_t numModifiers = sizeof(s_modifiers) / - sizeof(s_modifiers[0]); - // clear shadow state memset(m_keys, 0, sizeof(m_keys)); - // add modifiers + // clear scan code to/from virtual key mapping + memset(m_scanCodeToVirtKey, 0, sizeof(m_scanCodeToVirtKey)); + memset(m_virtKeyToScanCode, 0, sizeof(m_virtKeyToScanCode)); + + // add modifiers. note that VK_RMENU shows up under the Alt key + // and ModeSwitch. when simulating AltGr we need to use the right + // alt key so we use KeyModifierModeSwitch to get it. if (keyState != NULL) { - for (size_t i = 0; i < numModifiers; ++i) { - IKeyState::KeyButtons keys; - for (size_t j = 0; j < CModifierKeys::s_maxKeys; ++j) { - if (s_modifiers[i].m_keys[j] != 0) { - keys.push_back(s_modifiers[i].m_keys[j]); - } - } - keyState->addModifier(s_modifiers[i].m_mask, keys); - } + IKeyState::KeyButtons keys; + keys.push_back((KeyButton)MapVirtualKey(VK_LSHIFT, 0)); + keys.push_back((KeyButton)MapVirtualKey(VK_RSHIFT, 0)); + keyState->addModifier(KeyModifierShift, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_LCONTROL, 0)); + keys.push_back((KeyButton)(MapVirtualKey(VK_RCONTROL, 0) | 0x100)); + keyState->addModifier(KeyModifierControl, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_LMENU, 0)); + keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100)); + keyState->addModifier(KeyModifierAlt, keys); + keys.clear(); + keys.push_back((KeyButton)(MapVirtualKey(VK_LWIN, 0) | 0x100)); + keys.push_back((KeyButton)(MapVirtualKey(VK_RWIN, 0) | 0x100)); + keyState->addModifier(KeyModifierSuper, keys); + keys.clear(); + keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100)); + keyState->addModifier(KeyModifierModeSwitch, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_CAPITAL, 0)); + keyState->addModifier(KeyModifierCapsLock, keys); + keys.clear(); + keys.push_back((KeyButton)(MapVirtualKey(VK_NUMLOCK, 0) | 0x100)); + keyState->addModifier(KeyModifierNumLock, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_SCROLL, 0)); + keyState->addModifier(KeyModifierScrollLock, keys); + keys.clear(); } - // save current state of modifiers - for (size_t i = 0; i < numModifiers; ++i) { - for (size_t j = 0; j < CModifierKeys::s_maxKeys; ++j) { - if (s_modifiers[i].m_keys[j] != 0) { - SHORT s = GetKeyState(s_modifiers[i].m_keys[j]); - m_keys[s_modifiers[i].m_keys[j] & 0xffu] = static_cast(s); +/* FIXME -- potential problem here on win me + // win me (sony vaio laptop): + // MapVirtualKey(vk, 0): + // VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped + // VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped + // VK_MENU mapped; VK_LMENU, VK_RMENU not mapped + // MapVirtualKey(sc, 3): + // all scan codes unmapped (function apparently unimplemented) +*/ + BYTE keys[256]; + GetKeyboardState(keys); + for (UINT i = 1; i < 256; ++i) { + // skip certain virtual keys (the ones for the mouse buttons) + if (i < VK_BACK && i != VK_CANCEL) { + continue; + } + + // map to a scancode and back to a virtual key + UINT scancode = MapVirtualKey(i, 0); + UINT virtKey = MapVirtualKey(scancode, 3); + if (scancode == 0 || virtKey == 0) { + // the VK_PAUSE virtual key doesn't map properly + if (i == VK_PAUSE) { + // i hope this works on all keyboards + scancode = 0x45; + virtKey = i; + } + else { + continue; + } + } + + // we need some adjustments due to inadequacies in the API. + // if the mapped virtual key doesn't match the starting + // point then there's a really good chance that that virtual + // key is mapped to an extended key. however, this is not + // the case for modifiers that don't distinguish between left + // and right. also VK_NUMLOCK gets mapped to a non-extended + // key but it should be. + if (virtKey != i || i == VK_NUMLOCK) { + if (i != VK_SHIFT && i != VK_CONTROL && i != VK_MENU) { + scancode |= 0x100; + } + } + + // okay, now we have the scan code for the virtual key. + // save the key state. + m_scanCodeToVirtKey[scancode] = i; + m_virtKeyToScanCode[i] = (KeyButton)scancode; + m_keys[scancode] = (BYTE)(keys[i] & 0x80); + if (keyState != NULL) { + keyState->setKeyDown((KeyButton)scancode, (keys[i] & 0x80) != 0); + } + // toggle state applies to all keys but we only want it for + // the modifier keys with corresponding lights. + if ((keys[i] & 0x01) != 0) { + switch (i) { + case VK_CAPITAL: + m_keys[scancode] |= 0x01; if (keyState != NULL) { - if ((s & 0x01) != 0) { - keyState->setToggled(s_modifiers[i].m_mask); - } - if ((s & 0x80) != 0) { - keyState->setKeyDown(s_modifiers[i].m_keys[j]); - } + keyState->setToggled(KeyModifierCapsLock); } + break; + + case VK_NUMLOCK: + m_keys[scancode] |= 0x01; + if (keyState != NULL) { + keyState->setToggled(KeyModifierNumLock); + } + break; + + case VK_SCROLL: + m_keys[scancode] |= 0x01; + if (keyState != NULL) { + keyState->setToggled(KeyModifierScrollLock); + } + break; } } } } void -CMSWindowsKeyMapper::updateKey(KeyButton key, bool pressed) +CMSWindowsKeyMapper::updateKey(LPARAM eventLParam) { + bool pressed = ((eventLParam & 0x80000000u) == 0); + UINT scanCode = ((eventLParam & 0x01ff0000u) >> 16); + UINT virtKey = m_scanCodeToVirtKey[scanCode]; + if (virtKey == 0) { + // unmapped key + return; + } + if (pressed) { - switch (key) { - case 0: - case VK_LBUTTON: - case VK_MBUTTON: - case VK_RBUTTON: - // ignore bogus key - break; - - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: - m_keys[key] |= 0x80; - m_keys[VK_SHIFT] |= 0x80; - break; - - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: - m_keys[key] |= 0x80; - m_keys[VK_CONTROL] |= 0x80; - break; - - case VK_LMENU: - case VK_RMENU: - case VK_MENU: - m_keys[key] |= 0x80; - m_keys[VK_MENU] |= 0x80; - break; - - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - // toggle keys - m_keys[key] |= 0x80; - break; - - default: - case VK_LWIN: - case VK_RWIN: - case VK_APPS: - m_keys[key] |= 0x80; - break; - } + m_keys[scanCode] |= 0x80; // special case: we detect ctrl+alt+del being pressed on some // systems but we don't detect the release of those keys. so // if ctrl, alt, and del are down then mark them up. - if ((m_keys[VK_CONTROL] & 0x80) != 0 && - (m_keys[VK_MENU] & 0x80) != 0 && - (m_keys[VK_DELETE] & 0x80) != 0) { - m_keys[VK_LCONTROL] &= ~0x80; - m_keys[VK_RCONTROL] &= ~0x80; - m_keys[VK_CONTROL] &= ~0x80; - m_keys[VK_LMENU] &= ~0x80; - m_keys[VK_RMENU] &= ~0x80; - m_keys[VK_MENU] &= ~0x80; - m_keys[VK_DELETE] &= ~0x80; + if (isPressed(VK_CONTROL) && + isPressed(VK_MENU) && + isPressed(VK_DELETE)) { + m_keys[m_virtKeyToScanCode[VK_LCONTROL]] &= ~0x80; + m_keys[m_virtKeyToScanCode[VK_RCONTROL]] &= ~0x80; + m_keys[m_virtKeyToScanCode[VK_LMENU]] &= ~0x80; + m_keys[m_virtKeyToScanCode[VK_RMENU]] &= ~0x80; + m_keys[m_virtKeyToScanCode[VK_DELETE]] &= ~0x80; } } else { - switch (key) { - case 0: - case VK_LBUTTON: - case VK_MBUTTON: - case VK_RBUTTON: - // ignore bogus key - break; - - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: - m_keys[key] &= ~0x80; - if (((m_keys[VK_LSHIFT] | m_keys[VK_RSHIFT]) & 0x80) == 0) { - m_keys[VK_SHIFT] &= ~0x80; - } - break; - - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: - m_keys[key] &= ~0x80; - if (((m_keys[VK_LCONTROL] | m_keys[VK_RCONTROL]) & 0x80) == 0) { - m_keys[VK_CONTROL] &= ~0x80; - } - break; - - case VK_LMENU: - case VK_RMENU: - case VK_MENU: - m_keys[key] &= ~0x80; - if (((m_keys[VK_LMENU] | m_keys[VK_RMENU]) & 0x80) == 0) { - m_keys[VK_MENU] &= ~0x80; - } - break; + m_keys[scanCode] &= ~0x80; + // handle toggle keys + switch (virtKey) { case VK_CAPITAL: case VK_NUMLOCK: case VK_SCROLL: - // toggle keys - m_keys[key] &= ~0x80; - m_keys[key] ^= 0x01; + m_keys[scanCode] ^= 0x01; break; default: - case VK_LWIN: - case VK_RWIN: - case VK_APPS: - m_keys[key] &= ~0x80; break; } } @@ -883,7 +879,7 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, const IKeyState& keyState, KeyID id, KeyModifierMask mask, bool isAutoRepeat) const { - KeyButton virtualKey = 0; + UINT virtualKey = 0; // check for special keys if ((id & 0xfffff000u) == 0xe000u) { @@ -898,20 +894,14 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, } if (virtualKey == 0) { LOG((CLOG_DEBUG2 "unknown special key")); - return virtualKey; + return 0; } } // special handling of VK_SNAPSHOT - if ((virtualKey & 0xffu) == VK_SNAPSHOT) { + if (virtualKey == VK_SNAPSHOT) { // ignore key repeats on print screen if (!isAutoRepeat) { - // get event flags - DWORD flags = 0; - if (isExtendedKey(virtualKey)) { - flags |= KEYEVENTF_EXTENDEDKEY; - } - // active window (with alt) or fullscreen (without alt)? BYTE scan = 0; if ((mask & KeyModifierAlt) != 0) { @@ -919,9 +909,8 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, } // send events - keybd_event(static_cast(virtualKey & 0xffu), scan, flags, 0); - flags |= KEYEVENTF_KEYUP; - keybd_event(static_cast(virtualKey & 0xffu), scan, flags, 0); + keybd_event(VK_SNAPSHOT, scan, 0, 0); + keybd_event(VK_SNAPSHOT, scan, KEYEVENTF_KEYUP, 0); } return 0; } @@ -932,16 +921,13 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, KeyModifierMask requiredMask = 0; KeyModifierMask outMask = 0; - // strip out extended key flag - UINT virtualKey2 = (virtualKey & 0xffu); - // check numeric keypad. note that virtual keys do not distinguish // between the keypad and non-keypad movement keys. however, the // virtual keys do distinguish between keypad numbers and operators // (e.g. add, multiply) and their main keyboard counterparts. // therefore, we can ignore the num-lock state for movement virtual // keys but not for numeric keys. - if (virtualKey2 >= VK_NUMPAD0 && virtualKey2 <= VK_DIVIDE) { + if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) { requiredMask |= KeyModifierNumLock; if (!keyState.isModifierActive(KeyModifierNumLock)) { LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); @@ -956,8 +942,9 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, } // now generate the keystrokes and return the resulting modifier mask - LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d mask 0x%04x", id, virtualKey2, outMask)); - return mapToKeystrokes(keys, keyState, virtualKey, + KeyButton scanCode = m_virtKeyToScanCode[virtualKey]; + LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d scan code 0x%04x mask 0x%04x", id, virtualKey, scanCode, outMask)); + return mapToKeystrokes(keys, keyState, scanCode, outMask, requiredMask, isAutoRepeat); } @@ -1004,15 +991,16 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); return 0; } - virtualKey = mapCharacter(keys, keyState, multiByte[0], hkl, isAutoRepeat); - if (virtualKey != 0) { + KeyButton button = mapCharacter(keys, keyState, + multiByte[0], hkl, isAutoRepeat); + if (button != 0) { LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); if (isDeadChar(multiByte[0], hkl, false)) { // character mapped to a dead key but we want the // character for real so send a space key afterwards. LOG((CLOG_DEBUG2 "character mapped to dead key")); IKeyState::Keystroke keystroke; - keystroke.m_key = VK_SPACE; + keystroke.m_key = m_virtKeyToScanCode[VK_SPACE]; keystroke.m_press = true; keystroke.m_repeat = false; keys.push_back(keystroke); @@ -1021,9 +1009,9 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, // ignore the release of this key since we already // handled it. - virtualKey = 0; + button = 0; } - return virtualKey; + return button; } nChars = MultiByteToWideChar(codePage, MB_COMPOSITE | MB_ERR_INVALID_CHARS, @@ -1057,9 +1045,7 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, // process character LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - virtualKey = mapCharacter(keys, keyState, multiByte[0], hkl, isAutoRepeat); - - return virtualKey; + return mapCharacter(keys, keyState, multiByte[0], hkl, isAutoRepeat); } KeyID @@ -1129,8 +1115,7 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM charAndVirtKey, else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { // we didn't get the key that was actually pressed LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); - if ((m_keys[VK_CONTROL] & 0x80) != 0 && - (m_keys[VK_MENU] & 0x80) != 0) { + if (isPressed(VK_CONTROL) && isPressed(VK_MENU)) { needAltGr = true; } } @@ -1147,121 +1132,31 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM charAndVirtKey, } // map modifier key - KeyModifierMask mask = 0; - if (((m_keys[VK_LSHIFT] | - m_keys[VK_RSHIFT] | - m_keys[VK_SHIFT]) & 0x80) != 0) { - mask |= KeyModifierShift; - } - if (needAltGr) { - mask |= KeyModifierModeSwitch; - } - else { - if (((m_keys[VK_LCONTROL] | - m_keys[VK_RCONTROL] | - m_keys[VK_CONTROL]) & 0x80) != 0) { - mask |= KeyModifierControl; - } - if (((m_keys[VK_LMENU] | - m_keys[VK_RMENU] | - m_keys[VK_MENU]) & 0x80) != 0) { - mask |= KeyModifierAlt; - } - } - if (((m_keys[VK_LWIN] | - m_keys[VK_RWIN]) & 0x80) != 0) { - mask |= KeyModifierSuper; - } - if ((m_keys[VK_CAPITAL] & 0x01) != 0) { - mask |= KeyModifierCapsLock; - } - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { - mask |= KeyModifierNumLock; - } - if ((m_keys[VK_SCROLL] & 0x01) != 0) { - mask |= KeyModifierScrollLock; - } if (maskOut != NULL) { - *maskOut = mask; + *maskOut = getShadowModifiers(needAltGr); } return id; } bool -CMSWindowsKeyMapper::isPressed(KeyButton key) const +CMSWindowsKeyMapper::isModifier(UINT virtKey) const { - return ((m_keys[key & 0xffu] & 0x80) != 0); -} - -UINT -CMSWindowsKeyMapper::keyToScanCode(KeyButton* virtualKey) const -{ - // try mapping given virtual key - UINT code = MapVirtualKeyEx((*virtualKey) & 0xffu, 0, m_keyLayout); - if (code != 0) { - return code; - } - - // no dice. if the virtual key distinguishes between left/right - // then try the one that doesn't distinguish sides. windows (or - // keyboard drivers) are inconsistent in their treatment of these - // virtual keys. the following behaviors have been observed: - // - // win2k (gateway desktop): - // MapVirtualKey(vk, 0): - // VK_SHIFT == VK_LSHIFT != VK_RSHIFT - // VK_CONTROL == VK_LCONTROL == VK_RCONTROL - // VK_MENU == VK_LMENU == VK_RMENU - // MapVirtualKey(sc, 3): - // VK_LSHIFT and VK_RSHIFT mapped independently - // VK_LCONTROL is mapped but not VK_RCONTROL - // VK_LMENU is mapped but not VK_RMENU - // - // win me (sony vaio laptop): - // MapVirtualKey(vk, 0): - // VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped - // VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped - // VK_MENU mapped; VK_LMENU, VK_RMENU not mapped - // MapVirtualKey(sc, 3): - // all scan codes unmapped (function apparently unimplemented) - switch ((*virtualKey) & 0xffu) { + switch (virtKey) { case VK_LSHIFT: case VK_RSHIFT: - *virtualKey = VK_SHIFT; - return MapVirtualKeyEx(VK_SHIFT, 0, m_keyLayout); - + case VK_SHIFT: case VK_LCONTROL: case VK_RCONTROL: - *virtualKey = VK_CONTROL; - return MapVirtualKeyEx(VK_CONTROL, 0, m_keyLayout); - + case VK_CONTROL: case VK_LMENU: case VK_RMENU: - *virtualKey = VK_MENU; - return MapVirtualKeyEx(VK_MENU, 0, m_keyLayout); - - default: - return 0; - } -} - -bool -CMSWindowsKeyMapper::isExtendedKey(KeyButton virtualKey) const -{ - // see if we've already encoded the extended flag - if ((virtualKey & 0x100u) != 0) { - return true; - } - - // check known virtual keys - switch (virtualKey & 0xffu) { + case VK_MENU: + case VK_CAPITAL: case VK_NUMLOCK: - case VK_RCONTROL: - case VK_RMENU: + case VK_SCROLL: case VK_LWIN: case VK_RWIN: - case VK_APPS: return true; default: @@ -1269,10 +1164,134 @@ CMSWindowsKeyMapper::isExtendedKey(KeyButton virtualKey) const } } +bool +CMSWindowsKeyMapper::isPressed(UINT virtKey) const +{ + switch (virtKey) { + case VK_SHIFT: + return ((m_keys[m_virtKeyToScanCode[VK_LSHIFT]] & 0x80) != 0 || + (m_keys[m_virtKeyToScanCode[VK_RSHIFT]] & 0x80) != 0); + + case VK_CONTROL: + return ((m_keys[m_virtKeyToScanCode[VK_LCONTROL]] & 0x80) != 0 || + (m_keys[m_virtKeyToScanCode[VK_RCONTROL]] & 0x80) != 0); + + case VK_MENU: + return ((m_keys[m_virtKeyToScanCode[VK_LMENU]] & 0x80) != 0 || + (m_keys[m_virtKeyToScanCode[VK_RMENU]] & 0x80) != 0); + + default: + return ((m_keys[m_virtKeyToScanCode[virtKey & 0xffu]] & 0x80) != 0); + } +} + +bool +CMSWindowsKeyMapper::isToggled(UINT virtKey) const +{ + return ((m_keys[m_virtKeyToScanCode[virtKey & 0xffu]] & 0x01) != 0); +} + +UINT +CMSWindowsKeyMapper::buttonToVirtualKey(KeyButton button) const +{ + return m_scanCodeToVirtKey[button & 0x1ffu]; +} + +KeyButton +CMSWindowsKeyMapper::virtualKeyToButton(UINT virtKey) const +{ + return m_virtKeyToScanCode[virtKey & 0xffu]; +} + +bool +CMSWindowsKeyMapper::isExtendedKey(KeyButton button) const +{ + return ((button & 0x100u) != 0); +} + +KeyModifierMask +CMSWindowsKeyMapper::getActiveModifiers() const +{ + KeyModifierMask mask = 0; + if (GetKeyState(VK_SHIFT) < 0 || + GetKeyState(VK_LSHIFT) < 0 || + GetKeyState(VK_RSHIFT) < 0) { + mask |= KeyModifierShift; + } + if (GetKeyState(VK_CONTROL) < 0 || + GetKeyState(VK_LCONTROL) < 0 || + GetKeyState(VK_RCONTROL) < 0) { + mask |= KeyModifierControl; + } + if (GetKeyState(VK_MENU) < 0 || + GetKeyState(VK_LMENU) < 0 || + GetKeyState(VK_RMENU) < 0) { + mask |= KeyModifierAlt; + } + if (GetKeyState(VK_LWIN) < 0 || + GetKeyState(VK_RWIN) < 0) { + mask |= KeyModifierSuper; + } + if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) { + mask |= KeyModifierCapsLock; + } + if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) { + mask |= KeyModifierNumLock; + } + if ((GetKeyState(VK_SCROLL) & 0x01) != 0) { + mask |= KeyModifierScrollLock; + } + return mask; +} + +KeyModifierMask +CMSWindowsKeyMapper::getShadowModifiers(bool needAltGr) const +{ + KeyModifierMask mask = 0; + if (isPressed(VK_SHIFT)) { + mask |= KeyModifierShift; + } + if (needAltGr) { + mask |= KeyModifierModeSwitch; + } + else { + if (isPressed(VK_CONTROL)) { + mask |= KeyModifierControl; + } + if (isPressed(VK_MENU)) { + mask |= KeyModifierAlt; + } + } + if (isPressed(VK_LWIN) || isPressed(VK_RWIN)) { + mask |= KeyModifierSuper; + } + if (isToggled(VK_CAPITAL)) { + mask |= KeyModifierCapsLock; + } + if (isToggled(VK_NUMLOCK)) { + mask |= KeyModifierNumLock; + } + if (isToggled(VK_SCROLL)) { + mask |= KeyModifierScrollLock; + } + return mask; +} + const char* CMSWindowsKeyMapper::getKeyName(KeyButton key) const { - return s_vkToName[key & 0xffu]; + char keyName[100]; + CMSWindowsKeyMapper* self = const_cast(this); + if (GetKeyNameText((key & 0x01ffu) << 16, keyName, sizeof(keyName)) != 0) { + self->m_keyName = keyName; + } + else if (m_scanCodeToVirtKey[key] != 0) { + self->m_keyName = s_vkToName[m_scanCodeToVirtKey[key]]; + } + else { + self->m_keyName = CStringUtil::print("scan code 0x%03x", key & 0x01ffu); + } + return m_keyName.c_str(); } UINT @@ -1310,7 +1329,7 @@ CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); // get virtual key - KeyButton virtualKey = LOBYTE(virtualKeyAndModifierState); + UINT virtualKey = LOBYTE(virtualKeyAndModifierState); if (virtualKey == 0xffu) { LOG((CLOG_DEBUG2 "cannot map character %d", static_cast(c))); return 0; @@ -1370,16 +1389,15 @@ CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, // now generate the keystrokes. ignore the resulting modifier // mask since it can't have changed (because we don't call this // method for modifier keys). - LOG((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%08x", (unsigned char)c, virtualKey, desiredMask)); - mapToKeystrokes(keys, keyState, virtualKey, + KeyButton scanCode = m_virtKeyToScanCode[virtualKey]; + LOG((CLOG_DEBUG2 "character %d to virtual key %d scan code 0x%04x mask 0x%08x", (unsigned char)c, virtualKey, scanCode, desiredMask)); + return mapToKeystrokes(keys, keyState, scanCode, desiredMask, requiredMask, isAutoRepeat); - - return virtualKey; } KeyButton CMSWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, - const IKeyState& keyState, KeyButton virtualKey, + const IKeyState& keyState, KeyButton button, KeyModifierMask desiredMask, KeyModifierMask requiredMask, bool isAutoRepeat) const { @@ -1393,7 +1411,7 @@ CMSWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, // add the key event IKeyState::Keystroke keystroke; - keystroke.m_key = virtualKey; + keystroke.m_key = button; keystroke.m_press = true; keystroke.m_repeat = isAutoRepeat; keys.push_back(keystroke); @@ -1404,7 +1422,7 @@ CMSWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, undo.pop_back(); } - return virtualKey; + return button; } bool @@ -1425,7 +1443,6 @@ CMSWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, requiredMask ^= mask; } } - return true; } diff --git a/lib/platform/CMSWindowsKeyMapper.h b/lib/platform/CMSWindowsKeyMapper.h index 7292e675..883c5c60 100644 --- a/lib/platform/CMSWindowsKeyMapper.h +++ b/lib/platform/CMSWindowsKeyMapper.h @@ -16,6 +16,7 @@ #define CMSWINDOWSKEYMAPPER_H #include "IKeyState.h" +#include "CString.h" #define WIN32_LEAN_AND_MEAN #include @@ -42,7 +43,7 @@ public: /*! Updates the shadow keyboard state. */ - void updateKey(KeyButton key, bool pressed); + void updateKey(LPARAM eventLParam); //! Set the active keyboard layout /*! @@ -77,23 +78,41 @@ public: KeyID mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, KeyModifierMask* maskOut, bool* altgr) const; + //! Check if virtual key is a modifier + /*! + Returns true iff \p virtKey refers to a modifier key. + */ + bool isModifier(UINT virtKey) const; + //! Test shadow key state /*! Returns true iff the shadow state indicates the key is pressed. */ - bool isPressed(KeyButton key) const; + bool isPressed(UINT virtKey) const; - //! Map key to a scan code + //! Map button to a virtual key /*! - Returns the scan code for \c key and possibly adjusts \c key. + Returns the virtual key for \c button. */ - UINT keyToScanCode(KeyButton* key) const; + UINT buttonToVirtualKey(KeyButton button) const; + + //! Map virtual key to a button + /*! + Returns the button for virtual key \c virtKey. + */ + KeyButton virtualKeyToButton(UINT virtKey) const; //! Check for extended key /*! Returns true iff \c key is an extended key */ - bool isExtendedKey(KeyButton key) const; + bool isExtendedKey(KeyButton button) const; + + //! Get current modifier key state + /*! + Returns the current modifier key state. + */ + KeyModifierMask getActiveModifiers() const; //! Get name of key /*! @@ -116,7 +135,7 @@ private: // map \c virtualKey to the keystrokes to generate it, along with // keystrokes to update and restore the modifier state. KeyButton mapToKeystrokes(IKeyState::Keystrokes& keys, - const IKeyState& keyState, KeyButton virtualKey, + const IKeyState& keyState, KeyButton button, KeyModifierMask desiredMask, KeyModifierMask requiredMask, bool isAutoRepeat) const; @@ -128,6 +147,18 @@ private: KeyModifierMask desiredMask, KeyModifierMask requiredMask) const; + //! Test shadow key toggle state + /*! + Returns true iff the shadow state indicates the key is toggled on. + */ + bool isToggled(UINT virtKey) const; + + //! Get shadow modifier key state + /*! + Returns the shadow modifier key state. + */ + KeyModifierMask getShadowModifiers(bool needAltGr) const; + // pass character to ToAsciiEx(), returning what it returns int toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const; @@ -142,16 +173,24 @@ private: KeyButton m_keys[s_maxKeys]; }; - BYTE m_keys[256]; + // map of key state for each scan code. this would be 8 bits + // except windows reuses some scan codes for "extended" keys + // we actually need 9 bits. an example is the left and right + // alt keys; they share the same scan code but the right key + // is "extended". + BYTE m_keys[512]; + UINT m_scanCodeToVirtKey[512]; + KeyButton m_virtKeyToScanCode[256]; mutable TCHAR m_deadKey; HKL m_keyLayout; + CString m_keyName; static const CModifierKeys s_modifiers[]; - static const char* s_vkToName[]; - static const KeyID s_virtualKey[][2]; - static const KeyButton s_mapE000[]; - static const KeyButton s_mapEE00[]; - static const KeyButton s_mapEF00[]; + static const char* s_vkToName[]; + static const KeyID s_virtualKey[][2]; + static const UINT s_mapE000[]; + static const UINT s_mapEE00[]; + static const UINT s_mapEF00[]; }; #endif diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 14409450..34ba1865 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -84,28 +84,6 @@ #define XBUTTON2 0x0002 #endif -// multimedia keys -#if !defined(VK_BROWSER_BACK) -#define VK_BROWSER_BACK 0xA6 -#define VK_BROWSER_FORWARD 0xA7 -#define VK_BROWSER_REFRESH 0xA8 -#define VK_BROWSER_STOP 0xA9 -#define VK_BROWSER_SEARCH 0xAA -#define VK_BROWSER_FAVORITES 0xAB -#define VK_BROWSER_HOME 0xAC -#define VK_VOLUME_MUTE 0xAD -#define VK_VOLUME_DOWN 0xAE -#define VK_VOLUME_UP 0xAF -#define VK_MEDIA_NEXT_TRACK 0xB0 -#define VK_MEDIA_PREV_TRACK 0xB1 -#define VK_MEDIA_STOP 0xB2 -#define VK_MEDIA_PLAY_PAUSE 0xB3 -#define VK_LAUNCH_MAIL 0xB4 -#define VK_LAUNCH_MEDIA_SELECT 0xB5 -#define VK_LAUNCH_APP1 0xB6 -#define VK_LAUNCH_APP2 0xB7 -#endif - // // CMSWindowsScreen // @@ -231,8 +209,7 @@ void CMSWindowsScreen::setKeyState(IKeyState* keyState) { m_keyState = keyState; - m_keyMapper.update(m_keyState); - memset(m_buttons, 0, sizeof(m_buttons)); + updateKeys(); } void @@ -336,6 +313,7 @@ CMSWindowsScreen::leave() // tell the key mapper about the keyboard layout m_keyMapper.setKeyLayout(m_keyLayout); + sendDeskMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); // tell desk that we're leaving and tell it the keyboard layout sendDeskMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0); @@ -464,6 +442,7 @@ CMSWindowsScreen::updateKeys() { sendDeskMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); memset(m_buttons, 0, sizeof(m_buttons)); + // FIXME -- get the button state } void @@ -568,6 +547,12 @@ CMSWindowsScreen::isAnyMouseButtonDown() const return false; } +KeyModifierMask +CMSWindowsScreen::getActiveModifiers() const +{ + return m_keyMapper.getActiveModifiers(); +} + void CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const { @@ -582,19 +567,19 @@ CMSWindowsScreen::getKeyName(KeyButton virtualKey) const } void -CMSWindowsScreen::fakeKeyEvent(KeyButton virtualKey, bool press) const +CMSWindowsScreen::fakeKeyEvent(KeyButton id, bool press) const { DWORD flags = 0; - if (m_keyMapper.isExtendedKey(virtualKey)) { + if (m_keyMapper.isExtendedKey(id)) { flags |= KEYEVENTF_EXTENDEDKEY; } if (!press) { flags |= KEYEVENTF_KEYUP; } - UINT code = m_keyMapper.keyToScanCode(&virtualKey); + UINT vk = m_keyMapper.buttonToVirtualKey(id); sendDeskMessage(SYNERGY_MSG_FAKE_KEY, flags, - MAKEWORD(static_cast(code), - static_cast(virtualKey & 0xffu))); + MAKEWORD(static_cast(id & 0xffu), + static_cast(vk & 0xffu))); } bool @@ -870,9 +855,8 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, // if the user presses and releases a windows key without pressing // any other key while it's down then windows will eat the key // release. if we don't detect that and synthesize the release - // then the user will be locked to the screen and the client won't - // take the usual windows key release action (which on windows is - // to show the start menu). + // then the cclient won't take the usual windows key release action + // (which on windows is to show the start menu). // // we can use GetKeyState() to check the state of the windows keys // because, event though the key release is not reported to us, @@ -881,42 +865,12 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, // state on every event. only check on windows 95 family since // NT family reports the key release as usual. obviously we skip // this if the event is for the windows key itself. - if (m_is95Family) { - if (m_keyMapper.isPressed(VK_LWIN) && - (GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 && - !(message == SYNERGY_MSG_KEY && wParam == VK_LWIN)) { - // compute appropriate parameters for fake event - WPARAM wParam = VK_LWIN; - LPARAM lParam = 0xc1000000; - lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); - - // process as if it were a key up - KeyModifierMask mask; - KeyButton button = static_cast( - (lParam & 0x00ff0000u) >> 16); - KeyID key = m_keyMapper.mapKeyFromEvent(wParam, - lParam, &mask, NULL); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); - m_keyMapper.updateKey(static_cast(wParam), false); + if (m_is95Family && message != SYNERGY_MSG_KEY) { + if (wParam != VK_LWIN) { + fixKey(VK_LWIN); } - if (m_keyMapper.isPressed(VK_RWIN) && - (GetAsyncKeyState(VK_RWIN) & 0x8000) == 0 && - !(message == SYNERGY_MSG_KEY && wParam == VK_RWIN)) { - // compute appropriate parameters for fake event - WPARAM wParam = VK_RWIN; - LPARAM lParam = 0xc1000000; - lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24)); - - // process as if it were a key up - KeyModifierMask mask; - KeyButton button = static_cast( - (lParam & 0x00ff0000u) >> 16); - KeyID key = m_keyMapper.mapKeyFromEvent(wParam, - lParam, &mask, NULL); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); - m_keyMapper.updateKey(static_cast(wParam), false); + if (wParam != VK_RWIN) { + fixKey(VK_RWIN); } } @@ -1047,6 +1001,16 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) WPARAM charAndVirtKey = wParam; wParam &= 0xffu; + // update key state. ignore key repeats. + if ((lParam & 0xc0000000u) == 0x00000000) { + KeyButton scancode = (KeyButton)((lParam & 0x01ff0000) >> 16); + m_keyState->setKeyDown(scancode, true); + } + else if ((lParam & 0xc0000000u) == 0xc0000000) { + KeyButton scancode = (KeyButton)((lParam & 0x01ff0000) >> 16); + m_keyState->setKeyDown(scancode, false); + } + // ignore message if posted prior to last mark change if (!ignore()) { // check for ctrl+alt+del emulation @@ -1054,9 +1018,11 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) (m_keyMapper.isPressed(VK_CONTROL) && m_keyMapper.isPressed(VK_MENU))) { LOG((CLOG_DEBUG "emulate ctrl+alt+del")); - wParam = VK_DELETE; - lParam &= 0xffff0000; - lParam |= 0x00000001; + wParam = VK_DELETE; + lParam &= 0xfffe0000; + lParam |= m_keyMapper.virtualKeyToButton(wParam) << 16; + lParam |= 0x00000001; + charAndVirtKey = wParam; } // process key normally @@ -1065,7 +1031,7 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) const KeyID key = m_keyMapper.mapKeyFromEvent( charAndVirtKey, lParam, &mask, &altgr); KeyButton button = static_cast( - (lParam & 0x00ff0000u) >> 16); + (lParam & 0x01ff0000u) >> 16); if (key != kKeyNone && key != kKeyMultiKey) { if ((lParam & 0x80000000) == 0) { // key press @@ -1085,43 +1051,34 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) if (altgr) { KeyID key; KeyButton button; - UINT scanCode; KeyModifierMask mask2 = (mask & ~(KeyModifierControl | KeyModifierAlt | KeyModifierModeSwitch)); if (ctrlL) { - key = kKeyControl_L; - button = VK_LCONTROL; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + key = kKeyControl_L; + button = m_keyMapper.virtualKeyToButton(VK_LCONTROL); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask2, button, 1)); } if (ctrlR) { - key = kKeyControl_R; - button = VK_RCONTROL; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + key = kKeyControl_R; + button = m_keyMapper.virtualKeyToButton(VK_RCONTROL); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask2, button, 1)); } if (altL) { - key = kKeyAlt_L; - button = VK_LMENU; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + key = kKeyAlt_L; + button = m_keyMapper.virtualKeyToButton(VK_LMENU); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask2, button, 1)); } if (altR) { - key = kKeyAlt_R; - button = VK_RMENU; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + key = kKeyAlt_R; + button = m_keyMapper.virtualKeyToButton(VK_RMENU); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask2, button, 1)); @@ -1149,16 +1106,13 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) if (altgr) { KeyID key; KeyButton button; - UINT scanCode; KeyModifierMask mask2 = (mask & ~(KeyModifierControl | KeyModifierAlt | KeyModifierModeSwitch)); if (ctrlL) { key = kKeyControl_L; - button = VK_LCONTROL; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + button = m_keyMapper.virtualKeyToButton(VK_LCONTROL); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask2, button, 1)); @@ -1166,9 +1120,7 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) } if (ctrlR) { key = kKeyControl_R; - button = VK_RCONTROL; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + button = m_keyMapper.virtualKeyToButton(VK_RCONTROL); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask2, button, 1)); @@ -1176,9 +1128,7 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) } if (altL) { key = kKeyAlt_L; - button = VK_LMENU; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + button = m_keyMapper.virtualKeyToButton(VK_LMENU); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask2, button, 1)); @@ -1186,9 +1136,7 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) } if (altR) { key = kKeyAlt_R; - button = VK_RMENU; - scanCode = m_keyMapper.keyToScanCode(&button); - button = static_cast(scanCode); + button = m_keyMapper.virtualKeyToButton(VK_RMENU); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask2, button, 1)); @@ -1203,12 +1151,13 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) // synthesize the press first. only do this on // the windows 95 family, which eats certain special // keys like alt+tab, ctrl+esc, etc. - if (m_is95Family && !isModifier(wParam) && - m_keyMapper.isPressed(static_cast(wParam))) { + if (m_is95Family && + !m_keyMapper.isModifier(wParam) && + m_keyMapper.isPressed(wParam)) { LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask, button, 1)); - m_keyMapper.updateKey(static_cast(wParam), true); + m_keyMapper.updateKey(lParam & 0x3fffffffu); } // do key up @@ -1223,8 +1172,7 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) } // keep our shadow key state up to date - m_keyMapper.updateKey(static_cast(wParam), - ((lParam & 0x80000000) == 0)); + m_keyMapper.updateKey(lParam); return true; } @@ -1520,6 +1468,25 @@ CMSWindowsScreen::enableSpecialKeys(bool enable) const } } +void +CMSWindowsScreen::fixKey(UINT virtualKey) +{ + if (m_keyMapper.isPressed(virtualKey) && + (GetAsyncKeyState(virtualKey) & 0x8000) == 0) { + // compute appropriate parameters for fake event + KeyButton button = m_keyMapper.virtualKeyToButton(virtualKey); + LPARAM lParam = 0xc0000000 | ((LPARAM)button << 16); + + // process as if it were a key up + KeyModifierMask mask; + KeyID key = m_keyMapper.mapKeyFromEvent(virtualKey, + lParam, &mask, NULL); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); + m_keyMapper.updateKey(lParam); + } +} + DWORD CMSWindowsScreen::mapButtonToEvent(ButtonID button, bool press, DWORD* inData) const @@ -1616,31 +1583,6 @@ CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const } } -bool -CMSWindowsScreen::isModifier(UINT vkCode) const -{ - switch (vkCode) { - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: - case VK_LMENU: - case VK_RMENU: - case VK_MENU: - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - case VK_LWIN: - case VK_RWIN: - return true; - - default: - return false; - } -} - void CMSWindowsScreen::ctrlAltDelThread(void*) { diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index cd7727bf..f9e0b9b7 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -86,6 +86,7 @@ public: virtual void warpCursor(SInt32 x, SInt32 y); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; + virtual KeyModifierMask getActiveModifiers() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; virtual const char* getKeyName(KeyButton) const; @@ -168,6 +169,10 @@ private: // enable/disable special key combinations so we can catch/pass them void enableSpecialKeys(bool) const; + // send fake key up if shadow state says virtualKey is down but + // system says it isn't. + void fixKey(UINT virtualKey); + // map a button ID and action to a mouse event DWORD mapButtonToEvent(ButtonID button, bool press, DWORD* inData) const; diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index dde44703..ada14d8f 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -235,14 +235,14 @@ CScreen::keyRepeat(KeyID id, // dead key. for example, a dead accent followed by 'a' will // generate an 'a with accent' followed by a repeating 'a'. the // keycodes for the two keysyms might be different. - key &= 0xffu; + key &= 0x1ffu; if (key != index->second) { // replace key up with previous key id but leave key down // alone so it uses the new keycode and store that keycode // in the server key map. for (Keystrokes::iterator index2 = keys.begin(); index2 != keys.end(); ++index2) { - if ((index2->m_key & 0xffu) == key) { + if ((index2->m_key & 0x1ffu) == key) { index2->m_key = index->second; break; } @@ -477,11 +477,10 @@ CScreen::updateKeys() void CScreen::releaseKeys() { -LOG((CLOG_INFO "releaseKeys")); // FIXME // release keys that we've synthesized a press for and only those // keys. we don't want to synthesize a release on a key the user // is still physically pressing. - for (KeyButton i = 1; i < 256; ++i) { + for (KeyButton i = 1; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { if ((m_fakeKeys[i] & kDown) != 0) { fakeKeyEvent(i, false, false); m_keys[i] &= ~kDown; @@ -495,10 +494,10 @@ CScreen::setKeyDown(KeyButton key, bool down) { if (!isHalfDuplex(getMaskForKey(key))) { if (down) { - m_keys[key & 0xffu] |= kDown; + m_keys[key & 0x1ffu] |= kDown; } else { - m_keys[key & 0xffu] &= ~kDown; + m_keys[key & 0x1ffu] &= ~kDown; } } } @@ -515,7 +514,7 @@ CScreen::setToggled(KeyModifierMask mask) } for (KeyButtons::const_iterator j = i->second.begin(); j != i->second.end(); ++j) { - m_keys[(*j) & 0xffu] |= kToggled; + m_keys[(*j) & 0x1ffu] |= kToggled; } } @@ -536,8 +535,9 @@ CScreen::addModifier(KeyModifierMask mask, KeyButtons& keys) // index mask by keycodes for (KeyButtons::iterator j = keys.begin(); j != keys.end(); ++j) { // key must be valid - assert(((*j) & 0xffu) != 0); - m_keyToMask[static_cast((*j) & 0xffu)] = mask; + if (((*j) & 0x1ffu) != 0) { + m_keyToMask[static_cast((*j) & 0x1ffu)] = mask; + } } // index keys by mask @@ -563,7 +563,7 @@ CScreen::setToggleState(KeyModifierMask mask) KeyButton CScreen::isAnyKeyDown() const { - for (UInt32 i = 1; i < 256; ++i) { + for (UInt32 i = 1; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { if ((m_keys[i] & kDown) != 0) { return static_cast(i); } @@ -574,7 +574,7 @@ CScreen::isAnyKeyDown() const bool CScreen::isKeyDown(KeyButton key) const { - key &= 0xffu; + key &= 0x1ffu; return (key != 0 && ((m_keys[key] & kDown) != 0)); } @@ -867,7 +867,7 @@ void CScreen::updateKeyState(KeyButton button, KeyButton key, bool press) { // ignore bogus keys - key &= 0xffu; + key &= 0x1ffu; if (button == 0 || key == 0) { return; } @@ -931,13 +931,13 @@ CScreen::toggleKey(KeyModifierMask mask) // toggle shadow state m_mask ^= mask; - key &= 0xffu; + key &= 0x1ffu; m_keys[key] ^= kToggled; } bool CScreen::isKeyToggled(KeyButton key) const { - key &= 0xffu; + key &= 0x1ffu; return (key != 0 && ((m_keys[key] & kToggled) != 0)); } diff --git a/lib/synergy/CScreen.h b/lib/synergy/CScreen.h index 524bb0bd..ce2e43d3 100644 --- a/lib/synergy/CScreen.h +++ b/lib/synergy/CScreen.h @@ -298,10 +298,10 @@ private: ServerKeyMap m_serverKeyMap; // system key states as set by us or the user - KeyState m_keys[256]; + KeyState m_keys[512]; // system key states as set by us - KeyState m_fakeKeys[256]; + KeyState m_fakeKeys[512]; // modifier info MaskToKeys m_maskToKeys; diff --git a/lib/synergy/IKeyState.h b/lib/synergy/IKeyState.h index f8dec884..d54d29f1 100644 --- a/lib/synergy/IKeyState.h +++ b/lib/synergy/IKeyState.h @@ -68,7 +68,7 @@ public: example, if buttons 5 and 23 were mapped to KeyModifierShift (perhaps as left and right shift keys) then the mask would be KeyModifierShift and \c keys would contain 5 and 23. A modifier with no keys is - ignored. All keys must be valid (not zero). \c keys may be modified + ignored. Keys that are zero are ignored. \c keys may be modified by the call. */ virtual void addModifier(KeyModifierMask, KeyButtons& keys) = 0; diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 72deabcd..a0f80876 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -107,7 +107,7 @@ SOURCE=.\IClipboard.cpp # End Source File # Begin Source File -SOURCE=.\IPlatformScreen.cpp +SOURCE=.\IPrimaryScreen.cpp # End Source File # Begin Source File From 8d99fd25113c696fed96e2b34a6f503f084cda2e Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 21 Mar 2004 20:01:41 +0000 Subject: [PATCH 610/807] Checkpoint. Converted X11 to new keyboard state tracking design. This new design is simpler. For keyboard support, clients need only implement 4 virtual methods on a class derived from CKeyState and one trivial method in the class derived from CPlatformScreen, which is now the superclass of platform screens instead of IPlatformScreen. Keyboard methods have been removed from IPlatformScreen, IPrimaryScreen and ISecondaryScreen. Also, all keyboard state tracking is now in exactly one place (the CKeyState subclass) rather than in CScreen, the platform screen, and the key mapper. Still need to convert Win32. --- lib/client/CClient.cpp | 3 +- ...owsKeyMapper.cpp => CXWindowsKeyState.cpp} | 191 +++--- ...WindowsKeyMapper.h => CXWindowsKeyState.h} | 78 ++- lib/platform/CXWindowsScreen.cpp | 102 +--- lib/platform/CXWindowsScreen.h | 59 +- lib/platform/Makefile.am | 4 +- lib/server/CPrimaryClient.cpp | 4 +- lib/synergy/CKeyState.cpp | 532 +++++++++++++++++ lib/synergy/CKeyState.h | 185 ++++++ lib/synergy/CPlatformScreen.cpp | 82 +++ lib/synergy/CPlatformScreen.h | 99 +++ lib/synergy/CScreen.cpp | 563 ++---------------- lib/synergy/CScreen.h | 83 +-- lib/synergy/IKeyState.cpp | 61 ++ lib/synergy/IKeyState.h | 133 ++--- lib/synergy/IPlatformScreen.h | 48 +- lib/synergy/IPrimaryScreen.cpp | 41 -- lib/synergy/IPrimaryScreen.h | 37 +- lib/synergy/ISecondaryScreen.h | 23 - lib/synergy/Makefile.am | 5 + 20 files changed, 1306 insertions(+), 1027 deletions(-) rename lib/platform/{CXWindowsKeyMapper.cpp => CXWindowsKeyState.cpp} (89%) rename lib/platform/{CXWindowsKeyMapper.h => CXWindowsKeyState.h} (68%) create mode 100644 lib/synergy/CKeyState.cpp create mode 100644 lib/synergy/CKeyState.h create mode 100644 lib/synergy/CPlatformScreen.cpp create mode 100644 lib/synergy/CPlatformScreen.h create mode 100644 lib/synergy/IKeyState.cpp diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 35fefe1e..f1f4ae22 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -181,8 +181,7 @@ CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool) { m_active = true; m_screen->mouseMove(xAbs, yAbs); - m_screen->enter(); - m_screen->setToggleState(mask); + m_screen->enter(mask); } bool diff --git a/lib/platform/CXWindowsKeyMapper.cpp b/lib/platform/CXWindowsKeyState.cpp similarity index 89% rename from lib/platform/CXWindowsKeyMapper.cpp rename to lib/platform/CXWindowsKeyState.cpp index 5ff68ebd..0397ce3c 100644 --- a/lib/platform/CXWindowsKeyMapper.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -12,9 +12,10 @@ * GNU General Public License for more details. */ -#include "CXWindowsKeyMapper.h" +#include "CXWindowsKeyState.h" #include "CXWindowsUtil.h" #include "CLog.h" +#include "CStringUtil.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else @@ -137,73 +138,119 @@ static const KeySym g_mapE000[] = }; #endif -CXWindowsKeyMapper::CXWindowsKeyMapper() +CXWindowsKeyState::CXWindowsKeyState(Display* display) : + m_display(display) { // do nothing } -CXWindowsKeyMapper::~CXWindowsKeyMapper() +CXWindowsKeyState::~CXWindowsKeyState() { // do nothing } +KeyModifierMask +CXWindowsKeyState::mapModifiersFromX(unsigned int state) const +{ + KeyModifierMask mask = 0; + if (state & ShiftMask) + mask |= KeyModifierShift; + if (state & LockMask) + mask |= KeyModifierCapsLock; + if (state & ControlMask) + mask |= KeyModifierControl; + if (state & m_altMask) + mask |= KeyModifierAlt; + if (state & m_metaMask) + mask |= KeyModifierMeta; + if (state & m_superMask) + mask |= KeyModifierSuper; + if (state & m_modeSwitchMask) + mask |= KeyModifierModeSwitch; + if (state & m_numLockMask) + mask |= KeyModifierNumLock; + if (state & m_scrollLockMask) + mask |= KeyModifierScrollLock; + return mask; +} + +const char* +CXWindowsKeyState::getKeyName(KeyButton keycode) const +{ + KeySym keysym = XKeycodeToKeysym(m_display, keycode, 0); + char* name = XKeysymToString(keysym); + if (name != NULL) { + return name; + } + else { + static char buffer[20]; + return strcpy(buffer, + CStringUtil::print("keycode %d", keycode).c_str()); + } +} + void -CXWindowsKeyMapper::update(Display* display, IKeyState* keyState) +CXWindowsKeyState::doUpdateKeys() { // query which keys are pressed char keys[32]; - XQueryKeymap(display, keys); + XQueryKeymap(m_display, keys); // save the auto-repeat mask - XGetKeyboardControl(display, &m_keyControl); + XGetKeyboardControl(m_display, &m_keyControl); // query the pointer to get the keyboard state - Window root = DefaultRootWindow(display), window; + Window root = DefaultRootWindow(m_display), window; int xRoot, yRoot, xWindow, yWindow; unsigned int state; - if (!XQueryPointer(display, root, &root, &window, + if (!XQueryPointer(m_display, root, &root, &window, &xRoot, &yRoot, &xWindow, &yWindow, &state)) { state = 0; } // update mappings - updateKeysymMap(display, keyState); + updateKeysymMap(); updateModifiers(); // transfer to our state for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) { if ((keys[i] & 0x01) != 0) - keyState->setKeyDown(j + 0, true); + setKeyDown(j + 0, true); if ((keys[i] & 0x02) != 0) - keyState->setKeyDown(j + 1, true); + setKeyDown(j + 1, true); if ((keys[i] & 0x04) != 0) - keyState->setKeyDown(j + 2, true); + setKeyDown(j + 2, true); if ((keys[i] & 0x08) != 0) - keyState->setKeyDown(j + 3, true); + setKeyDown(j + 3, true); if ((keys[i] & 0x10) != 0) - keyState->setKeyDown(j + 4, true); + setKeyDown(j + 4, true); if ((keys[i] & 0x20) != 0) - keyState->setKeyDown(j + 5, true); + setKeyDown(j + 5, true); if ((keys[i] & 0x40) != 0) - keyState->setKeyDown(j + 6, true); + setKeyDown(j + 6, true); if ((keys[i] & 0x80) != 0) - keyState->setKeyDown(j + 7, true); + setKeyDown(j + 7, true); } // set toggle modifier states if ((state & LockMask) != 0) - keyState->setToggled(KeyModifierCapsLock); + setToggled(KeyModifierCapsLock); if ((state & m_numLockMask) != 0) - keyState->setToggled(KeyModifierNumLock); + setToggled(KeyModifierNumLock); if ((state & m_scrollLockMask) != 0) - keyState->setToggled(KeyModifierScrollLock); + setToggled(KeyModifierScrollLock); +} + +void +CXWindowsKeyState::doFakeKeyEvent(KeyButton keycode, bool press, bool) +{ + XTestFakeKeyEvent(m_display, keycode, press ? True : False, CurrentTime); + XFlush(m_display); } KeyButton -CXWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, - const IKeyState& keyState, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const +CXWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, + KeyModifierMask desiredMask, bool isAutoRepeat) const { // the system translates key events into characters depending // on the modifier key state at the time of the event. to @@ -243,7 +290,7 @@ CXWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, if (keyIndex != m_keysymMap.end()) { // the keysym is mapped to some keycode. create the keystrokes // for this keysym. - return mapToKeystrokes(keys, keyState, keyIndex, isAutoRepeat); + return mapToKeystrokes(keys, keyIndex, isAutoRepeat); } // we can't find the keysym mapped to any keycode. this doesn't @@ -272,7 +319,7 @@ CXWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, } // the keysym is mapped to some keycode - keycode = mapToKeystrokes(keys, keyState, keyIndex, isAutoRepeat); + keycode = mapToKeystrokes(keys, keyIndex, isAutoRepeat); if (keycode == 0) { return 0; } @@ -281,45 +328,20 @@ CXWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, return keycode; } -KeyModifierMask -CXWindowsKeyMapper::mapModifier(unsigned int state) const -{ - KeyModifierMask mask = 0; - if (state & ShiftMask) - mask |= KeyModifierShift; - if (state & LockMask) - mask |= KeyModifierCapsLock; - if (state & ControlMask) - mask |= KeyModifierControl; - if (state & m_altMask) - mask |= KeyModifierAlt; - if (state & m_metaMask) - mask |= KeyModifierMeta; - if (state & m_superMask) - mask |= KeyModifierSuper; - if (state & m_modeSwitchMask) - mask |= KeyModifierModeSwitch; - if (state & m_numLockMask) - mask |= KeyModifierNumLock; - if (state & m_scrollLockMask) - mask |= KeyModifierScrollLock; - return mask; -} - void -CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState) +CXWindowsKeyState::updateKeysymMap() { // there are up to 4 keysyms per keycode static const unsigned int maxKeysyms = 4; // get the number of keycodes int minKeycode, maxKeycode; - XDisplayKeycodes(display, &minKeycode, &maxKeycode); + XDisplayKeycodes(m_display, &minKeycode, &maxKeycode); const int numKeycodes = maxKeycode - minKeycode + 1; // get the keyboard mapping for all keys int keysymsPerKeycode; - KeySym* keysyms = XGetKeyboardMapping(display, + KeySym* keysyms = XGetKeyboardMapping(m_display, minKeycode, numKeycodes, &keysymsPerKeycode); @@ -364,7 +386,7 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState) } // get modifier map from server - XModifierKeymap* modifiers = XGetModifierMapping(display); + XModifierKeymap* modifiers = XGetModifierMapping(m_display); unsigned int keysPerModifier = modifiers->max_keypermod; // clear state @@ -387,7 +409,7 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState) for (unsigned int i = 0; i < 8; ++i) { // no keycodes for this modifier yet KeyModifierMask mask = 0; - IKeyState::KeyButtons modifierKeys; + KeyButtons modifierKeys; // add each keycode for modifier for (unsigned int j = 0; j < keysPerModifier; ++j) { @@ -429,9 +451,9 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState) mapping.m_numLockSensitive = false; } - // tell keyState about this modifier - if (mask != 0 && keyState != NULL) { - keyState->addModifier(mask, modifierKeys); + // note this modifier + if (mask != 0) { + addModifier(mask, modifierKeys); } } @@ -485,7 +507,7 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState) } KeyModifierMask -CXWindowsKeyMapper::mapToModifierMask(unsigned int i, KeySym keysym) +CXWindowsKeyState::mapToModifierMask(unsigned int i, KeySym keysym) { // some modifier indices (0,1,2) are dedicated to particular uses, // the rest depend on the keysyms bound. @@ -546,16 +568,16 @@ CXWindowsKeyMapper::mapToModifierMask(unsigned int i, KeySym keysym) } void -CXWindowsKeyMapper::updateModifiers() +CXWindowsKeyState::updateModifiers() { struct CModifierBitInfo { public: - KeySym CXWindowsKeyMapper::*m_keysym; + KeySym CXWindowsKeyState::*m_keysym; KeySym m_left; KeySym m_right; }; static const CModifierBitInfo s_modifierBitTable[] = { - { &CXWindowsKeyMapper::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol }, + { &CXWindowsKeyState::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol }, }; // choose the keysym to use for some modifiers. if a modifier has @@ -603,7 +625,7 @@ CXWindowsKeyMapper::updateModifiers() } KeySym -CXWindowsKeyMapper::keyIDToKeySym(KeyID id, KeyModifierMask mask) const +CXWindowsKeyState::keyIDToKeySym(KeyID id, KeyModifierMask mask) const { // convert id to keysym KeySym keysym = NoSymbol; @@ -718,15 +740,13 @@ CXWindowsKeyMapper::keyIDToKeySym(KeyID id, KeyModifierMask mask) const } KeyButton -CXWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, - const IKeyState& keyState, - KeySymIndex keyIndex, - bool isAutoRepeat) const +CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys, + KeySymIndex keyIndex, bool isAutoRepeat) const { // keyIndex must be valid assert(keyIndex != m_keysymMap.end()); - KeyModifierMask currentMask = keyState.getActiveModifiers(); + KeyModifierMask currentMask = getActiveModifiers(); // get the keysym we're trying to generate and possible keycodes const KeySym keysym = keyIndex->first; @@ -798,14 +818,14 @@ CXWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, } // adjust the modifiers to match the desired modifiers - IKeyState::Keystrokes undo; - if (!adjustModifiers(keys, undo, keyState, desiredMask)) { + Keystrokes undo; + if (!adjustModifiers(keys, undo, desiredMask)) { LOG((CLOG_DEBUG2 "failed to adjust modifiers")); return 0; } // add the key event - IKeyState::Keystroke keystroke; + Keystroke keystroke; keystroke.m_key = keycode; if (!isAutoRepeat) { keystroke.m_press = true; @@ -830,7 +850,7 @@ CXWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, } unsigned int -CXWindowsKeyMapper::findBestKeyIndex(KeySymIndex keyIndex, +CXWindowsKeyState::findBestKeyIndex(KeySymIndex keyIndex, KeyModifierMask /*currentMask*/) const { // there are up to 4 keycodes per keysym to choose from. the @@ -856,7 +876,7 @@ CXWindowsKeyMapper::findBestKeyIndex(KeySymIndex keyIndex, } bool -CXWindowsKeyMapper::isShiftInverted(KeySymIndex keyIndex, +CXWindowsKeyState::isShiftInverted(KeySymIndex keyIndex, KeyModifierMask currentMask) const { // each keycode has up to 4 keysym associated with it, one each for: @@ -885,12 +905,11 @@ CXWindowsKeyMapper::isShiftInverted(KeySymIndex keyIndex, } bool -CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, - IKeyState::Keystrokes& undo, - const IKeyState& keyState, +CXWindowsKeyState::adjustModifiers(Keystrokes& keys, + Keystrokes& undo, KeyModifierMask desiredMask) const { - KeyModifierMask currentMask = keyState.getActiveModifiers(); + KeyModifierMask currentMask = getActiveModifiers(); // get mode switch set correctly. do this before shift because // mode switch may be sensitive to the shift modifier and will @@ -909,8 +928,7 @@ CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, if (wantShift != haveShift) { // add shift keystrokes LOG((CLOG_DEBUG2 "fix shift for mode switch")); - if (!keyState.mapModifier(keys, undo, - KeyModifierShift, wantShift)) { + if (!mapModifier(keys, undo, KeyModifierShift, wantShift)) { return false; } currentMask ^= KeyModifierShift; @@ -918,8 +936,7 @@ CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, } // add mode switch keystrokes - if (!keyState.mapModifier(keys, undo, - KeyModifierModeSwitch, wantModeSwitch)) { + if (!mapModifier(keys, undo, KeyModifierModeSwitch, wantModeSwitch)) { return false; } currentMask ^= KeyModifierModeSwitch; @@ -931,7 +948,7 @@ CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, if (wantShift != haveShift) { // add shift keystrokes LOG((CLOG_DEBUG2 "fix shift")); - if (!keyState.mapModifier(keys, undo, KeyModifierShift, wantShift)) { + if (!mapModifier(keys, undo, KeyModifierShift, wantShift)) { return false; } currentMask ^= KeyModifierShift; @@ -941,13 +958,13 @@ CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, } bool -CXWindowsKeyMapper::isNumLockSensitive(KeySym keysym) const +CXWindowsKeyState::isNumLockSensitive(KeySym keysym) const { return (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)); } bool -CXWindowsKeyMapper::isCapsLockSensitive(KeySym keysym) const +CXWindowsKeyState::isCapsLockSensitive(KeySym keysym) const { KeySym lKey, uKey; XConvertCase(keysym, &lKey, &uKey); @@ -956,10 +973,10 @@ CXWindowsKeyMapper::isCapsLockSensitive(KeySym keysym) const // -// CXWindowsKeyMapper::KeyMapping +// CXWindowsKeyState::KeyMapping // -CXWindowsKeyMapper::KeyMapping::KeyMapping() +CXWindowsKeyState::KeyMapping::KeyMapping() { m_keycode[0] = 0; m_keycode[1] = 0; diff --git a/lib/platform/CXWindowsKeyMapper.h b/lib/platform/CXWindowsKeyState.h similarity index 68% rename from lib/platform/CXWindowsKeyMapper.h rename to lib/platform/CXWindowsKeyState.h index 0c707aa5..98458228 100644 --- a/lib/platform/CXWindowsKeyMapper.h +++ b/lib/platform/CXWindowsKeyState.h @@ -12,62 +12,55 @@ * GNU General Public License for more details. */ -#ifndef CXWINDOWSKEYMAPPER_H -#define CXWINDOWSKEYMAPPER_H +#ifndef CXWINDOWSKEYSTATE_H +#define CXWINDOWSKEYSTATE_H -#include "IKeyState.h" +#include "CKeyState.h" #include "stdmap.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else # include +# if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# include +# else +# error The XTest extension is required to build synergy +# endif #endif -//! X Windows key mapper +//! X Windows key state /*! -This class maps KeyIDs to keystrokes. +A key state for X Windows. */ -class CXWindowsKeyMapper { +class CXWindowsKeyState : public CKeyState { public: - CXWindowsKeyMapper(); - ~CXWindowsKeyMapper(); + CXWindowsKeyState(Display*); + ~CXWindowsKeyState(); - //! @name manipulators - //@{ - - //! Update key mapper - /*! - Updates the key mapper's internal tables according to the display's - current keyboard mapping and updates \c keyState. - */ - void update(Display*, IKeyState* keyState); - - //@} //! @name accessors //@{ - //! Map key press/repeat to keystrokes - /*! - Converts a press/repeat of key \c id with the modifiers as given - in \c desiredMask into the keystrokes necessary to synthesize - that key event. Returns the platform specific code of the key - being pressed, or 0 if the key cannot be mapped or \c isAutoRepeat - is true and the key does not auto-repeat. - */ - KeyButton mapKey(IKeyState::Keystrokes&, - const IKeyState& keyState, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const; - //! Convert X modifier mask to synergy mask /*! - Returns the synergy modifier mask corresponding to the given X - modifier mask. + Returns the synergy modifier mask corresponding to the X modifier + mask in \p state. */ - KeyModifierMask mapModifier(unsigned int state) const; + KeyModifierMask mapModifiersFromX(unsigned int state) const; //@} + // IKeyState overrides + virtual const char* getKeyName(KeyButton) const; + +protected: + // IKeyState overrides + virtual void doUpdateKeys(); + virtual void doFakeKeyEvent(KeyButton button, + bool press, bool isAutoRepeat); + virtual KeyButton mapKey(Keystrokes& keys, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + private: class KeyMapping { public: @@ -90,9 +83,8 @@ private: typedef std::map KeySymMap; typedef KeySymMap::const_iterator KeySymIndex; - // save the current keyboard mapping and note the currently - // pressed keys in \c keyState. - void updateKeysymMap(Display* display, IKeyState* keyState); + // save the current keyboard mapping and note the modifiers + void updateKeysymMap(); // note interesting modifier KeySyms void updateModifiers(); @@ -105,8 +97,7 @@ private: KeySym keyIDToKeySym(KeyID id, KeyModifierMask mask) const; // map a KeySym into the keystrokes to produce it - KeyButton mapToKeystrokes(IKeyState::Keystrokes& keys, - const IKeyState& keyState, + KeyButton mapToKeystrokes(Keystrokes& keys, KeySymIndex keyIndex, bool isAutoRepeat) const; @@ -120,9 +111,8 @@ private: // returns the keystrokes to adjust the modifiers into the desired // state the keystrokes to get back to the current state. - bool adjustModifiers(IKeyState::Keystrokes& keys, - IKeyState::Keystrokes& undo, - const IKeyState& keyState, + bool adjustModifiers(Keystrokes& keys, + Keystrokes& undo, KeyModifierMask desiredMask) const; // returns true if keysym is sensitive to the NumLock state @@ -132,6 +122,8 @@ private: bool isCapsLockSensitive(KeySym keysym) const; private: + Display* m_display; + // keysym to keycode mapping KeySymMap m_keysymMap; diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index da9a705c..2531ba8e 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -15,6 +15,7 @@ #include "CXWindowsScreen.h" #include "CXWindowsClipboard.h" #include "CXWindowsEventQueueBuffer.h" +#include "CXWindowsKeyState.h" #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CClipboard.h" @@ -131,7 +132,6 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) : m_xCenter(0), m_yCenter(0), m_xCursor(0), m_yCursor(0), m_keyState(NULL), - m_keyMapper(), m_im(NULL), m_ic(NULL), m_lastKeycode(0), @@ -154,6 +154,7 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) : m_window = openWindow(); m_screensaver = new CXWindowsScreenSaver(m_display, m_window, getEventTarget()); + m_keyState = new CXWindowsKeyState(m_display); LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_xinerama ? "(xinerama)" : "")); LOG((CLOG_DEBUG "window is 0x%08x", m_window)); } @@ -201,7 +202,9 @@ CXWindowsScreen::~CXWindowsScreen() for (ClipboardID id = 0; id < kClipboardEnd; ++id) { delete m_clipboard[id]; } + delete m_keyState; delete m_screensaver; + m_keyState = NULL; m_screensaver = NULL; if (m_display != NULL) { // FIXME -- is it safe to clean up the IC and IM without a display? @@ -219,12 +222,6 @@ CXWindowsScreen::~CXWindowsScreen() s_screen = NULL; } -void -CXWindowsScreen::setKeyState(IKeyState* keyState) -{ - m_keyState = keyState; -} - void CXWindowsScreen::enable() { @@ -319,7 +316,6 @@ CXWindowsScreen::leave() } // raise and show the window - // FIXME -- take focus? XMapRaised(m_display, m_window); // grab the mouse and keyboard, if primary and possible @@ -328,6 +324,9 @@ CXWindowsScreen::leave() return false; } + // take focus + XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime); + // now warp the mouse. we warp after showing the window so we're // guaranteed to get the mouse leave event and to prevent the // keyboard focus from changing under point-to-focus policies. @@ -428,14 +427,6 @@ CXWindowsScreen::setOptions(const COptionsList& options) } } -void -CXWindowsScreen::updateKeys() -{ - // update keyboard and mouse button mappings - m_keyMapper.update(m_display, m_keyState); - updateButtons(); -} - void CXWindowsScreen::setSequenceNumber(UInt32 seqNum) { @@ -547,21 +538,6 @@ CXWindowsScreen::isAnyMouseButtonDown() const return false; } -KeyModifierMask -CXWindowsScreen::getActiveModifiers() const -{ - // query the pointer to get the modifier state - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int state; - if (XQueryPointer(m_display, m_root, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &state)) { - return m_keyMapper.mapModifier(state); - } - - return 0; -} - void CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const { @@ -569,28 +545,6 @@ CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const y = m_yCenter; } -const char* -CXWindowsScreen::getKeyName(KeyButton keycode) const -{ - KeySym keysym = XKeycodeToKeysym(m_display, keycode, 0); - char* name = XKeysymToString(keysym); - if (name != NULL) { - return name; - } - else { - static char buffer[20]; - return strcpy(buffer, - CStringUtil::print("keycode %d", keycode).c_str()); - } -} - -void -CXWindowsScreen::fakeKeyEvent(KeyButton keycode, bool press) const -{ - XTestFakeKeyEvent(m_display, keycode, press ? True : False, CurrentTime); - XFlush(m_display); -} - bool CXWindowsScreen::fakeCtrlAltDel() const { @@ -645,15 +599,6 @@ CXWindowsScreen::fakeMouseWheel(SInt32 delta) const XFlush(m_display); } -KeyButton -CXWindowsScreen::mapKey(IKeyState::Keystrokes& keys, - const IKeyState& keyState, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const -{ - return m_keyMapper.mapKey(keys, keyState, id, desiredMask, isAutoRepeat); -} - Display* CXWindowsScreen::openDisplay() const { @@ -788,6 +733,7 @@ CXWindowsScreen::openIM() // open the input methods XIM im = XOpenIM(m_display, NULL, NULL, NULL); if (im == NULL) { + LOG((CLOG_INFO "no support for IM")); return; } @@ -811,7 +757,7 @@ CXWindowsScreen::openIM() } XFree(styles); if (style == 0) { - LOG((CLOG_WARN "no supported IM styles")); + LOG((CLOG_INFO "no supported IM styles")); XCloseIM(im); return; } @@ -859,6 +805,12 @@ CXWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) sendEvent(type, info); } +IKeyState* +CXWindowsScreen::getKeyState() const +{ + return m_keyState; +} + Bool CXWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) { @@ -1063,7 +1015,7 @@ void CXWindowsScreen::onKeyPress(XKeyEvent& xkey) { LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xkey.keycode, xkey.state)); - const KeyModifierMask mask = m_keyMapper.mapModifier(xkey.state); + const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state); KeyID key = mapKeyFromX(&xkey); if (key != kKeyNone) { // check for ctrl+alt+del emulation @@ -1083,19 +1035,15 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey) } // handle key - sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask, keycode, 1)); - KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); - if (m_keyState->isHalfDuplex(keyMask)) { - sendEvent(getKeyUpEvent(), - CKeyInfo::alloc(key, mask | keyMask, keycode, 1)); - } + m_keyState->sendKeyEvent(getEventTarget(), + true, false, key, mask, 1, keycode); } } void CXWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat) { - const KeyModifierMask mask = m_keyMapper.mapModifier(xkey.state); + const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state); KeyID key = mapKeyFromX(&xkey); if (key != kKeyNone) { // check for ctrl+alt+del emulation @@ -1112,12 +1060,8 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat) if (!isRepeat) { // no press event follows so it's a plain release LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", keycode, xkey.state)); - KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); - if (m_keyState->isHalfDuplex(keyMask)) { - sendEvent(getKeyDownEvent(), - CKeyInfo::alloc(key, mask, keycode, 1)); - } - sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, keycode, 1)); + m_keyState->sendKeyEvent(getEventTarget(), + false, false, key, mask, 1, keycode); } else { // found a press event following so it's a repeat. @@ -1125,8 +1069,8 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat) // repeats but we'll just send a repeat of 1. // note that we discard the press event. LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state)); - sendEvent(getKeyRepeatEvent(), - CKeyInfo::alloc(key, mask, keycode, 1)); + m_keyState->sendKeyEvent(getEventTarget(), + false, true, key, mask, 1, keycode); } } } diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index 7298190f..41b42a75 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -15,8 +15,7 @@ #ifndef CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H -#include "IPlatformScreen.h" -#include "CXWindowsKeyMapper.h" +#include "CPlatformScreen.h" #include "stdvector.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy @@ -25,10 +24,11 @@ #endif class CXWindowsClipboard; +class CXWindowsKeyState; class CXWindowsScreenSaver; //! Implementation of IPlatformScreen for X11 -class CXWindowsScreen : public IPlatformScreen { +class CXWindowsScreen : public CPlatformScreen { public: CXWindowsScreen(bool isPrimary); virtual ~CXWindowsScreen(); @@ -38,23 +38,6 @@ public: //@} - // IPlatformScreen overrides - virtual void setKeyState(IKeyState*); - virtual void enable(); - virtual void disable(); - virtual void enter(); - virtual bool leave(); - virtual bool setClipboard(ClipboardID, const IClipboard*); - virtual void checkClipboards(); - virtual void openScreensaver(bool notify); - virtual void closeScreensaver(); - virtual void screensaver(bool activate); - virtual void resetOptions(); - virtual void setOptions(const COptionsList& options); - virtual void updateKeys(); - virtual void setSequenceNumber(UInt32); - virtual bool isPrimary() const; - // IScreen overrides virtual void* getEventTarget() const; virtual bool getClipboard(ClipboardID id, IClipboard*) const; @@ -67,29 +50,40 @@ public: virtual void warpCursor(SInt32 x, SInt32 y); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; - virtual KeyModifierMask getActiveModifiers() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; - virtual const char* getKeyName(KeyButton) const; // ISecondaryScreen overrides - virtual void fakeKeyEvent(KeyButton id, bool press) const; virtual bool fakeCtrlAltDel() const; virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; virtual void fakeMouseWheel(SInt32 delta) const; - virtual KeyButton mapKey(IKeyState::Keystrokes&, - const IKeyState& keyState, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const; + + // IPlatformScreen overrides + virtual void enable(); + virtual void disable(); + virtual void enter(); + virtual bool leave(); + virtual bool setClipboard(ClipboardID, const IClipboard*); + virtual void checkClipboards(); + virtual void openScreensaver(bool notify); + virtual void closeScreensaver(); + virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual void setSequenceNumber(UInt32); + virtual bool isPrimary() const; + +protected: + // IPlatformScreen overrides + virtual void handleSystemEvent(const CEvent&, void*); + virtual void updateButtons(); + virtual IKeyState* getKeyState() const; private: // event sending void sendEvent(CEvent::Type, void* = NULL); void sendClipboardEvent(CEvent::Type, ClipboardID); - // event handling - void handleSystemEvent(const CEvent&, void*); - // create the transparent cursor Cursor createBlankCursor() const; @@ -138,8 +132,6 @@ private: void warpCursorNoFlush(SInt32 x, SInt32 y); - void updateButtons(); - static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: @@ -162,8 +154,7 @@ private: SInt32 m_xCursor, m_yCursor; // keyboard stuff - IKeyState* m_keyState; - CXWindowsKeyMapper m_keyMapper; + CXWindowsKeyState* m_keyState; // input method stuff XIM m_im; diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index daef7e63..9245adee 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -54,7 +54,7 @@ libplatform_a_SOURCES = \ CXWindowsClipboardUCS2Converter.cpp \ CXWindowsClipboardUTF8Converter.cpp \ CXWindowsEventQueueBuffer.cpp \ - CXWindowsKeyMapper.cpp \ + CXWindowsKeyState.cpp \ CXWindowsScreen.cpp \ CXWindowsScreenSaver.cpp \ CXWindowsUtil.cpp \ @@ -63,7 +63,7 @@ libplatform_a_SOURCES = \ CXWindowsClipboardUCS2Converter.h \ CXWindowsClipboardUTF8Converter.h \ CXWindowsEventQueueBuffer.h \ - CXWindowsKeyMapper.h \ + CXWindowsKeyState.h \ CXWindowsScreen.h \ CXWindowsScreenSaver.h \ CXWindowsUtil.h \ diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 9da849bd..a8e21c3c 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -105,13 +105,13 @@ CPrimaryClient::disable() void CPrimaryClient::enter(SInt32 xAbs, SInt32 yAbs, - UInt32 seqNum, KeyModifierMask, bool screensaver) + UInt32 seqNum, KeyModifierMask mask, bool screensaver) { m_screen->setSequenceNumber(seqNum); if (!screensaver) { m_screen->warpCursor(xAbs, yAbs); } - m_screen->enter(); + m_screen->enter(mask); } bool diff --git a/lib/synergy/CKeyState.cpp b/lib/synergy/CKeyState.cpp new file mode 100644 index 00000000..9cd80de5 --- /dev/null +++ b/lib/synergy/CKeyState.cpp @@ -0,0 +1,532 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CKeyState.h" +#include "IEventQueue.h" +#include "CLog.h" +#include + +// +// CKeyState +// + +CKeyState::CKeyState() : + m_halfDuplex(0), + m_mask(0) +{ + memset(&m_keys, 0, sizeof(m_keys)); + memset(&m_serverKeyMap, 0, sizeof(m_serverKeyMap)); + memset(&m_keyToMask, 0, sizeof(m_keyToMask)); +} + +CKeyState::~CKeyState() +{ + // do nothing +} + +void +CKeyState::setKeyDown(KeyButton button, bool down) +{ + button &= kButtonMask; + if (button != 0) { + if (down) { + m_keys[button] |= kDown; + } + else { + m_keys[button] &= ~kDown; + } + } +} + +void +CKeyState::setToggled(KeyModifierMask modifier) +{ + if (isToggle(modifier)) { + const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(modifier)]; + for (KeyButtons::const_iterator j = buttons.begin(); + j != buttons.end(); ++j) { + m_keys[(*j) & kButtonMask] |= kToggled; + } + } +} + +void +CKeyState::sendKeyEvent( + void* target, bool press, bool isAutoRepeat, + KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + if (isHalfDuplex(m_keyToMask[button])) { + if (isAutoRepeat) { + // ignore auto-repeat on half-duplex keys + } + else { + EVENTQUEUE->addEvent(CEvent(getKeyDownEvent(), target, + CKeyInfo::alloc(key, mask, button, 1))); + EVENTQUEUE->addEvent(CEvent(getKeyUpEvent(), target, + CKeyInfo::alloc(key, mask, button, 1))); + } + } + else { + if (isAutoRepeat) { + EVENTQUEUE->addEvent(CEvent(getKeyRepeatEvent(), target, + CKeyInfo::alloc(key, mask, button, count))); + } + else if (press) { + EVENTQUEUE->addEvent(CEvent(getKeyDownEvent(), target, + CKeyInfo::alloc(key, mask, button, 1))); + } + else { + EVENTQUEUE->addEvent(CEvent(getKeyUpEvent(), target, + CKeyInfo::alloc(key, mask, button, 1))); + } + } +} + +void +CKeyState::updateKeys() +{ + static const KeyModifierMask s_masks[] = { + KeyModifierShift, + KeyModifierControl, + KeyModifierAlt, + KeyModifierMeta, + KeyModifierSuper, + KeyModifierModeSwitch, + KeyModifierCapsLock, + KeyModifierNumLock, + KeyModifierScrollLock + }; + + // reset our state + memset(&m_keys, 0, sizeof(m_keys)); + memset(&m_serverKeyMap, 0, sizeof(m_serverKeyMap)); + memset(&m_keyToMask, 0, sizeof(m_keyToMask)); + for (UInt32 i = 0; i < sizeof(m_maskToKeys)/sizeof(m_maskToKeys[0]); ++i) { + m_maskToKeys[i].clear(); + } + + // let subclass set the state + doUpdateKeys(); + + // figure out the active modifiers + m_mask = 0; + for (UInt32 i = 0; i < sizeof(s_masks) / sizeof(s_masks[0]); ++i) { + if (isModifierActive(s_masks[i])) { + m_mask |= s_masks[i]; + } + } + LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); +} + +void +CKeyState::setHalfDuplexMask(KeyModifierMask mask) +{ + m_halfDuplex = mask & (KeyModifierCapsLock | + KeyModifierNumLock | + KeyModifierScrollLock); +} + +void +CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) +{ + // get the sequence of keys to simulate key press and the final + // modifier state. + Keystrokes keys; + KeyButton localID = (mapKey(keys, id, mask, false) & kButtonMask); + if (keys.empty()) { + // do nothing if there are no associated keys + LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); + return; + } + + // generate key events + fakeKeyEvents(keys, 1); + + // note that key is down + updateKeyState(button & kButtonMask, localID, true); +} + +void +CKeyState::fakeKeyRepeat( + KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + button &= kButtonMask; + + // if we haven't seen this button go down then ignore it + KeyButton oldLocalID = m_serverKeyMap[button]; + if (oldLocalID == 0) { + return; + } + + // get the sequence of keys to simulate key repeat and the final + // modifier state. + Keystrokes keys; + KeyButton localID = (mapKey(keys, id, mask, true) & kButtonMask); + if (localID == 0) { + LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); + return; + } + if (keys.empty()) { + // do nothing if there are no associated keys + return; + } + + // if the keycode for the auto-repeat is not the same as for the + // initial press then mark the initial key as released and the new + // key as pressed. this can happen when we auto-repeat after a + // dead key. for example, a dead accent followed by 'a' will + // generate an 'a with accent' followed by a repeating 'a'. the + // keycodes for the two keysyms might be different. + if (localID != oldLocalID) { + // replace key up with previous key id but leave key down + // alone so it uses the new keycode. + for (Keystrokes::iterator index = keys.begin(); + index != keys.end(); ++index) { + if (index->m_key == localID) { + index->m_key = oldLocalID; + break; + } + } + + // note that old key is now up + m_keys[oldLocalID] &= ~kDown; + + // map server key to new key + m_serverKeyMap[button] = localID; + + // note that new key is now down + m_keys[localID] |= kDown; + } + + // generate key events + fakeKeyEvents(keys, count); +} + +void +CKeyState::fakeKeyUp(KeyButton button) +{ + // if we haven't seen this button go down then ignore it + KeyButton localID = m_serverKeyMap[button & kButtonMask]; + if (localID == 0) { + return; + } + + // get the sequence of keys to simulate key release + Keystrokes keys; + Keystroke keystroke; + keystroke.m_key = localID; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + + // generate key events + fakeKeyEvents(keys, 1); + + // note that key is now up + updateKeyState(button, localID, false); +} + +void +CKeyState::fakeToggle(KeyModifierMask modifier) +{ + const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(modifier)]; + if (buttons.empty() || !isToggle(modifier)) { + return; + } + KeyButton button = buttons[0]; + + // get the sequence of keys to simulate key toggle + Keystrokes keys; + Keystroke keystroke; + keystroke.m_key = button; + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + + // generate key events + fakeKeyEvents(keys, 1); + + // note the toggle + m_keys[button] ^= kToggled; + m_mask ^= modifier; +} + +bool +CKeyState::isKeyDown(KeyButton button) const +{ + return ((m_keys[button & kButtonMask] & kDown) != 0); +} + +KeyModifierMask +CKeyState::getActiveModifiers() const +{ + return m_mask; +} + +void +CKeyState::addModifier(KeyModifierMask modifier, const KeyButtons& buttons) +{ + // the mask must not be zero + assert(modifier != 0); + + // the mask must have exactly one high bit + assert((modifier & (modifier - 1)) == 0); + + for (KeyButtons::const_iterator j = buttons.begin(); + j != buttons.end(); ++j) { + KeyButton button = static_cast(((*j) & kButtonMask)); + if (button != 0) { + m_keyToMask[button] = modifier; + } + } + + // index keys by mask + m_maskToKeys[getIndexForModifier(modifier)] = buttons; +} + +bool +CKeyState::mapModifier(Keystrokes& keys, Keystrokes& undo, + KeyModifierMask mask, bool desireActive) const +{ + // look up modifier + const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)]; + if (buttons.empty()) { + return false; + } + + // ignore if already in desired state + if (isModifierActive(mask) == desireActive) { + return true; + } + + // initialize keystroke + Keystroke keystroke; + keystroke.m_repeat = false; + + // handle toggles + if (isToggle(mask)) { + keystroke.m_key = buttons[0]; + keystroke.m_press = true; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + keystroke.m_press = false; + undo.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + + else if (desireActive) { + // press + keystroke.m_key = buttons[0]; + keystroke.m_press = true; + keys.push_back(keystroke); + keystroke.m_press = false; + undo.push_back(keystroke); + } + + else { + // releasing a modifier is quite different from pressing one. + // when we release a modifier we have to release every keycode that + // is assigned to the modifier since the modifier is active if any + // one of them is down. when we press a modifier we just have to + // press one of those keycodes. + for (KeyButtons::const_iterator j = buttons.begin(); + j != buttons.end(); ++j) { + if (isKeyDown(*j)) { + keystroke.m_key = *j; + keystroke.m_press = false; + keys.push_back(keystroke); + keystroke.m_press = true; + undo.push_back(keystroke); + } + } + } + + return true; +} + +bool +CKeyState::isToggle(KeyModifierMask mask) const +{ + return (mask == KeyModifierCapsLock || + mask == KeyModifierNumLock || + mask == KeyModifierScrollLock); +} + +bool +CKeyState::isHalfDuplex(KeyModifierMask mask) const +{ + return ((mask & m_halfDuplex) != 0); +} + +bool +CKeyState::isModifierActive(KeyModifierMask mask) const +{ + const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)]; + KeyButtons::const_iterator j = buttons.begin(); + if (isToggle(mask)) { + // modifier is a toggle + if ((m_keys[*j] & kToggled) != 0) { + return true; + } + } + else { + // modifier is not a toggle + for (; j != buttons.end(); ++j) { + if ((m_keys[*j] & kDown) != 0) { + return true; + } + } + } + return false; +} + +UInt32 +CKeyState::getIndexForModifier(KeyModifierMask mask) const +{ + switch (mask) { + case KeyModifierShift: + return 0; + + case KeyModifierControl: + return 1; + + case KeyModifierAlt: + return 2; + + case KeyModifierMeta: + return 3; + + case KeyModifierSuper: + return 4; + + case KeyModifierModeSwitch: + return 5; + + case KeyModifierCapsLock: + return 6; + + case KeyModifierNumLock: + return 7; + + case KeyModifierScrollLock: + return 8; + + default: + assert(0 && "invalid modifier mask"); + return 0; + } +} + +void +CKeyState::fakeKeyEvents(const Keystrokes& keys, UInt32 count) +{ + // do nothing if no keys or no repeats + if (count == 0 || keys.empty()) { + return; + } + + // generate key events + LOG((CLOG_DEBUG2 "keystrokes:")); + for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { + if (k->m_repeat) { + // repeat from here up to but not including the next key + // with m_repeat == false count times. + Keystrokes::const_iterator start = k; + while (count-- > 0) { + // send repeating events + for (k = start; k != keys.end() && k->m_repeat; ++k) { + fakeKeyEvent(k->m_key, k->m_press, true); + } + } + + // note -- k is now on the first non-repeat key after the + // repeat keys, exactly where we'd like to continue from. + } + else { + // send event + fakeKeyEvent(k->m_key, k->m_press, false); + + // next key + ++k; + } + } +} + +void +CKeyState::fakeKeyEvent(KeyButton button, bool press, bool isAutoRepeat) +{ + // half-duplex keys are special. we ignore releases and convert + // a press when the toggle is active to a release. + KeyModifierMask mask = m_keyToMask[button]; + if (isHalfDuplex(mask)) { + if (isAutoRepeat || !press) { + return; + } + if (isModifierActive(mask)) { + press = false; + } + } + + // send key event + LOG((CLOG_DEBUG2 " %d %s%s", button, press ? "down" : "up", isAutoRepeat ? " repeat" : "")); + doFakeKeyEvent(button, press, isAutoRepeat); +} + +void +CKeyState::updateKeyState(KeyButton serverID, KeyButton localID, bool press) +{ + // ignore bogus keys + if (serverID == 0 || localID == 0) { + return; + } + + // update key state. state doesn't change when auto-repeating. + if (press) { + m_serverKeyMap[serverID] = localID; + m_keys[localID] |= kDown; + } + else { + m_serverKeyMap[serverID] = 0; + m_keys[localID] &= ~kDown; + } + + // update modifier state + KeyModifierMask mask = m_keyToMask[localID]; + if (mask != 0) { + if (isToggle(mask)) { + m_keys[localID] ^= kToggled; + m_mask ^= mask; + + // never report half-duplex keys as down + if (isHalfDuplex(mask)) { + m_keys[localID] &= ~kDown; + } + } + else { + if (press) { + m_mask |= mask; + } + else if (!isModifierActive(mask)) { + m_mask &= ~mask; + } + } + LOG((CLOG_DEBUG2 "new mask: 0x%04x", m_mask)); + } +} diff --git a/lib/synergy/CKeyState.h b/lib/synergy/CKeyState.h new file mode 100644 index 00000000..a53cba3f --- /dev/null +++ b/lib/synergy/CKeyState.h @@ -0,0 +1,185 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CKEYSTATE_H +#define CKEYSTATE_H + +#include "IKeyState.h" +#include "stdvector.h" + +//! Core key state +/*! +This class provides key state services. Subclasses must implement a few +platform specific methods. +*/ +class CKeyState : public IKeyState { +public: + CKeyState(); + virtual ~CKeyState(); + + //! @name manipulators + //@{ + + //! Mark key as being down + /*! + Sets the state of \p button to down or up. + */ + void setKeyDown(KeyButton button, bool down); + + //! Mark modifier as being toggled on + /*! + Sets the state of the keys for the given (single) \p modifier to be + toggled on. + */ + void setToggled(KeyModifierMask modifier); + + //! Post a key event + /*! + Posts a key event. This may adjust the event or post additional + events in some circumstances. + */ + void sendKeyEvent(void* target, + bool press, bool isAutoRepeat, + KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton button); + + //@} + //! @name accessors + //@{ + + //@} + + // IKeyState overrides + virtual void updateKeys(); + virtual void setHalfDuplexMask(KeyModifierMask); + virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button); + virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button); + virtual void fakeKeyUp(KeyButton button); + virtual void fakeToggle(KeyModifierMask modifier); + virtual bool isKeyDown(KeyButton) const; + virtual KeyModifierMask + getActiveModifiers() const; + virtual const char* getKeyName(KeyButton) const = 0; + +protected: + class Keystroke { + public: + KeyButton m_key; + bool m_press; + bool m_repeat; + }; + typedef std::vector Keystrokes; + typedef std::vector KeyButtons; + + //! @name protocted manipulators + //@{ + + //! Add keys for modifier + /*! + Sets the buttons that are mapped to the given (single) \p modifier. For + example, if buttons 5 and 23 were mapped to KeyModifierShift (perhaps + as left and right shift keys) then the mask would be KeyModifierShift + and \c buttons would contain 5 and 23. A modifier with no keys is + ignored. Buttons that are zero are ignored. + */ + void addModifier(KeyModifierMask modifier, + const KeyButtons& buttons); + + //! Get key events to change modifier state + /*! + Retrieves the key events necessary to activate (\c desireActive is true) + or deactivate (\c desireActive is false) the modifier given by \c mask + by pushing them onto the back of \c keys. \c mask must specify exactly + one modifier. \c undo receives the key events necessary to restore the + modifier's previous state. They're pushed onto \c undo in the reverse + order they should be executed. Returns true if the modifier can be + adjusted, false otherwise. + */ + bool mapModifier(Keystrokes& keys, Keystrokes& undo, + KeyModifierMask mask, bool desireActive) const; + + //! Update the key state + /*! + Update the key state to reflect the physical keyboard state and + current keyboard mapping. This must call \c setKeyDown, \c setToggled, + and \c addModifier to set the current state. + */ + virtual void doUpdateKeys() = 0; + + //! Fake a key event + /*! + Synthesize a key event for \p button. If \p press is true then + synthesize a key press and, if false, a key release. If + \p isAutoRepeat is true then the event is an auto-repeat. + */ + virtual void doFakeKeyEvent(KeyButton button, + bool press, bool isAutoRepeat) = 0; + + //! Map key press/repeat to keystrokes + /*! + Converts a press/repeat of key \p id with the modifiers as given + in \p desiredMask into the keystrokes necessary to synthesize + that key event. Returns the platform specific code of the key + being pressed, or 0 if the key cannot be mapped or \p isAutoRepeat + is true and the key does not auto-repeat. + */ + virtual KeyButton mapKey(Keystrokes& keys, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const = 0; + + //@} + +private: + bool isHalfDuplex(KeyModifierMask) const; + bool isToggle(KeyModifierMask) const; + bool isModifierActive(KeyModifierMask) const; + UInt32 getIndexForModifier(KeyModifierMask) const; + void fakeKeyEvents(const Keystrokes&, UInt32 count); + void fakeKeyEvent(KeyButton, bool press, bool isAutoRepeat); + void updateKeyState(KeyButton serverID, + KeyButton localID, bool press); + +private: + enum { + kNumModifiers = 9, + kButtonMask = kNumButtons - 1 + }; + typedef UInt8 KeyState; + enum EKeyState { + kDown = 0x01, //!< Key is down + kToggled = 0x02 //!< Key is toggled on + }; + + // modifiers that are half-duplex + KeyModifierMask m_halfDuplex; + + // current modifier state + KeyModifierMask m_mask; + + // current keyboard state + KeyState m_keys[kNumButtons]; + + // map from server button ID to local button ID for pressed keys + KeyButton m_serverKeyMap[kNumButtons]; + + // map button to the modifier mask it represents + KeyModifierMask m_keyToMask[kNumButtons]; + + // map modifier to buttons with that modifier + KeyButtons m_maskToKeys[kNumModifiers]; +}; + +#endif diff --git a/lib/synergy/CPlatformScreen.cpp b/lib/synergy/CPlatformScreen.cpp new file mode 100644 index 00000000..95e0fe77 --- /dev/null +++ b/lib/synergy/CPlatformScreen.cpp @@ -0,0 +1,82 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CPlatformScreen.h" + +CPlatformScreen::CPlatformScreen() +{ + // do nothing +} + +CPlatformScreen::~CPlatformScreen() +{ + // do nothing +} + +void +CPlatformScreen::updateKeys() +{ + getKeyState()->updateKeys(); + updateButtons(); +} + +void +CPlatformScreen::setHalfDuplexMask(KeyModifierMask mask) +{ + getKeyState()->setHalfDuplexMask(mask); +} + +void +CPlatformScreen::fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button) +{ + getKeyState()->fakeKeyDown(id, mask, button); +} + +void +CPlatformScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + getKeyState()->fakeKeyRepeat(id, mask, count, button); +} + +void +CPlatformScreen::fakeKeyUp(KeyButton button) +{ + getKeyState()->fakeKeyUp(button); +} + +void +CPlatformScreen::fakeToggle(KeyModifierMask modifier) +{ + getKeyState()->fakeToggle(modifier); +} + +bool +CPlatformScreen::isKeyDown(KeyButton button) const +{ + return getKeyState()->isKeyDown(button); +} + +KeyModifierMask +CPlatformScreen::getActiveModifiers() const +{ + return getKeyState()->getActiveModifiers(); +} + +const char* +CPlatformScreen::getKeyName(KeyButton button) const +{ + return getKeyState()->getKeyName(button); +} diff --git a/lib/synergy/CPlatformScreen.h b/lib/synergy/CPlatformScreen.h new file mode 100644 index 00000000..68071fbb --- /dev/null +++ b/lib/synergy/CPlatformScreen.h @@ -0,0 +1,99 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CPLATFORMSCREEN_H +#define CPLATFORMSCREEN_H + +#include "IPlatformScreen.h" + +//! Base screen implementation +/*! +This screen implementation is the superclass of all other screen +implementations. It implements a handful of methods and requires +subclasses to implement the rest. +*/ +class CPlatformScreen : public IPlatformScreen { +public: + CPlatformScreen(); + virtual ~CPlatformScreen(); + + // IScreen overrides + virtual void* getEventTarget() const = 0; + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + // IPrimaryScreen overrides + virtual void reconfigure(UInt32 activeSides) = 0; + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + virtual SInt32 getJumpZoneSize() const = 0; + virtual bool isAnyMouseButtonDown() const = 0; + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; + + // ISecondaryScreen overrides + virtual bool fakeCtrlAltDel() const = 0; + virtual void fakeMouseButton(ButtonID id, bool press) const = 0; + virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + virtual void fakeMouseWheel(SInt32 delta) const = 0; + + // IKeyState overrides + virtual void updateKeys(); + virtual void setHalfDuplexMask(KeyModifierMask); + virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button); + virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button); + virtual void fakeKeyUp(KeyButton button); + virtual void fakeToggle(KeyModifierMask modifier); + virtual bool isKeyDown(KeyButton) const; + virtual KeyModifierMask + getActiveModifiers() const; + virtual const char* getKeyName(KeyButton) const; + + // IPlatformScreen overrides + virtual void enable() = 0; + virtual void disable() = 0; + virtual void enter() = 0; + virtual bool leave() = 0; + virtual bool setClipboard(ClipboardID, const IClipboard*) = 0; + virtual void checkClipboards() = 0; + virtual void openScreensaver(bool notify) = 0; + virtual void closeScreensaver() = 0; + virtual void screensaver(bool activate) = 0; + virtual void resetOptions() = 0; + virtual void setOptions(const COptionsList& options) = 0; + virtual void setSequenceNumber(UInt32) = 0; + virtual bool isPrimary() const = 0; + +protected: + //! Update mouse buttons + /*! + Subclasses must implement this method to update their internal mouse + button mapping and, if desired, state tracking. + */ + virtual void updateButtons() = 0; + + //! Get the key state + /*! + Subclasses must implement this method to return the platform specific + key state object that each subclass must have. + */ + virtual IKeyState* getKeyState() const = 0; + + // IPlatformScreen overrides + virtual void handleSystemEvent(const CEvent& event, void*) = 0; +}; + +#endif diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index ada14d8f..9c4594da 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -32,9 +32,6 @@ CScreen::CScreen(IPlatformScreen* platformScreen) : { assert(m_screen != NULL); - // open screen - m_screen->setKeyState(this); - // reset options resetOptions(); @@ -78,7 +75,7 @@ CScreen::disable() leave(); } else if (m_isPrimary && !m_entered) { - enter(); + enter(0); } m_screen->disable(); if (m_isPrimary) { @@ -93,7 +90,7 @@ CScreen::disable() } void -CScreen::enter() +CScreen::enter(KeyModifierMask toggleMask) { assert(m_entered == false); LOG((CLOG_INFO "entering screen")); @@ -105,7 +102,7 @@ CScreen::enter() enterPrimary(); } else { - enterSecondary(); + enterSecondary(toggleMask); } m_screen->enter(); } @@ -186,22 +183,7 @@ CScreen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button) return; } } - - // get the sequence of keys to simulate key press and the final - // modifier state. - Keystrokes keys; - KeyButton key = m_screen->mapKey(keys, *this, id, mask, false); - if (keys.empty()) { - // do nothing if there are no associated keys - LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); - return; - } - - // generate key events - doKeystrokes(keys, 1); - - // note that key is down - updateKeyState(button, key, true); + m_screen->fakeKeyDown(id, mask, button); } void @@ -209,86 +191,14 @@ CScreen::keyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button) { assert(!m_isPrimary); - - // if we haven't seen this button go down then ignore it - ServerKeyMap::iterator index = m_serverKeyMap.find(button); - if (index == m_serverKeyMap.end()) { - return; - } - - // get the sequence of keys to simulate key repeat and the final - // modifier state. - Keystrokes keys; - KeyButton key = m_screen->mapKey(keys, *this, id, mask, true); - if (key == 0) { - LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); - return; - } - if (keys.empty()) { - // do nothing if there are no associated keys - return; - } - - // if the keycode for the auto-repeat is not the same as for the - // initial press then mark the initial key as released and the new - // key as pressed. this can happen when we auto-repeat after a - // dead key. for example, a dead accent followed by 'a' will - // generate an 'a with accent' followed by a repeating 'a'. the - // keycodes for the two keysyms might be different. - key &= 0x1ffu; - if (key != index->second) { - // replace key up with previous key id but leave key down - // alone so it uses the new keycode and store that keycode - // in the server key map. - for (Keystrokes::iterator index2 = keys.begin(); - index2 != keys.end(); ++index2) { - if ((index2->m_key & 0x1ffu) == key) { - index2->m_key = index->second; - break; - } - } - - // note that old key is now up - m_keys[index->second] &= ~kDown; - m_fakeKeys[index->second] &= ~kDown; - - // map server key to new key - index->second = key; - - // note that new key is now down - m_keys[index->second] |= kDown; - m_fakeKeys[index->second] |= kDown; - } - - // generate key events - doKeystrokes(keys, count); + m_screen->fakeKeyRepeat(id, mask, count, button); } void CScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) { assert(!m_isPrimary); - - // if we haven't seen this button go down then ignore it - ServerKeyMap::iterator index = m_serverKeyMap.find(button); - if (index == m_serverKeyMap.end()) { - return; - } - KeyButton key = index->second; - - // get the sequence of keys to simulate key release - Keystrokes keys; - Keystroke keystroke; - keystroke.m_key = key; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - - // generate key events - doKeystrokes(keys, 1); - - // note that key is now up - updateKeyState(button, key, false); + m_screen->fakeKeyUp(button); } void @@ -323,8 +233,7 @@ void CScreen::resetOptions() { // reset options - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; + m_halfDuplex = 0; // if screen saver synchronization was off then turn it on since // that's the default option state. @@ -350,12 +259,24 @@ CScreen::setOptions(const COptionsList& options) LOG((CLOG_DEBUG1 "screen saver synchronization %s", m_screenSaverSync ? "on" : "off")); } else if (options[i] == kOptionHalfDuplexCapsLock) { - m_capsLockHalfDuplex = (options[i + 1] != 0); - LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off")); + if (options[i + 1] != 0) { + m_halfDuplex |= KeyModifierCapsLock; + } + else { + m_halfDuplex &= ~KeyModifierCapsLock; + } + m_screen->setHalfDuplexMask(m_halfDuplex); + LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", ((m_halfDuplex & KeyModifierCapsLock) != 0) ? "on" : "off")); } else if (options[i] == kOptionHalfDuplexNumLock) { - m_numLockHalfDuplex = (options[i + 1] != 0); - LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off")); + if (options[i + 1] != 0) { + m_halfDuplex |= KeyModifierNumLock; + } + else { + m_halfDuplex &= ~KeyModifierNumLock; + } + m_screen->setHalfDuplexMask(m_halfDuplex); + LOG((CLOG_DEBUG1 "half-duplex num-lock %s", ((m_halfDuplex & KeyModifierNumLock) != 0) ? "on" : "off")); } } @@ -401,7 +322,7 @@ CScreen::isLockedToScreen() const // be necessary but we don't seem to get some key release // events sometimes. this is an emergency backup so the // client doesn't get stuck on the screen. - const_cast(this)->updateKeys(); + m_screen->updateKeys(); KeyButton key2 = isAnyKeyDown(); if (key2 != 0) { LOG((CLOG_DEBUG "locked by %s", m_screen->getKeyName(key2))); @@ -433,6 +354,12 @@ CScreen::getCursorCenter(SInt32& x, SInt32& y) const m_screen->getCursorCenter(x, y); } +KeyModifierMask +CScreen::getActiveModifiers() const +{ + return m_screen->getActiveModifiers(); +} + void* CScreen::getEventTarget() const { @@ -457,253 +384,6 @@ CScreen::getCursorPos(SInt32& x, SInt32& y) const m_screen->getCursorPos(x, y); } -void -CScreen::updateKeys() -{ - // clear key state - memset(m_keys, 0, sizeof(m_keys)); - memset(m_fakeKeys, 0, sizeof(m_fakeKeys)); - m_maskToKeys.clear(); - m_keyToMask.clear(); - - // let subclass set m_keys - m_screen->updateKeys(); - - // figure out active modifier mask - m_mask = getModifierMask(); - LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); -} - -void -CScreen::releaseKeys() -{ - // release keys that we've synthesized a press for and only those - // keys. we don't want to synthesize a release on a key the user - // is still physically pressing. - for (KeyButton i = 1; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { - if ((m_fakeKeys[i] & kDown) != 0) { - fakeKeyEvent(i, false, false); - m_keys[i] &= ~kDown; - m_fakeKeys[i] &= ~kDown; - } - } -} - -void -CScreen::setKeyDown(KeyButton key, bool down) -{ - if (!isHalfDuplex(getMaskForKey(key))) { - if (down) { - m_keys[key & 0x1ffu] |= kDown; - } - else { - m_keys[key & 0x1ffu] &= ~kDown; - } - } -} - -void -CScreen::setToggled(KeyModifierMask mask) -{ - if (!isToggle(mask)) { - return; - } - MaskToKeys::const_iterator i = m_maskToKeys.find(mask); - if (i == m_maskToKeys.end()) { - return; - } - for (KeyButtons::const_iterator j = i->second.begin(); - j != i->second.end(); ++j) { - m_keys[(*j) & 0x1ffu] |= kToggled; - } -} - -void -CScreen::addModifier(KeyModifierMask mask, KeyButtons& keys) -{ - // the modifier must have associated keys - if (keys.empty()) { - return; - } - - // the mask must not be zero - assert(mask != 0); - - // the mask must have exactly one high bit - assert((mask & (mask - 1)) == 0); - - // index mask by keycodes - for (KeyButtons::iterator j = keys.begin(); j != keys.end(); ++j) { - // key must be valid - if (((*j) & 0x1ffu) != 0) { - m_keyToMask[static_cast((*j) & 0x1ffu)] = mask; - } - } - - // index keys by mask - m_maskToKeys[mask].swap(keys); -} - -void -CScreen::setToggleState(KeyModifierMask mask) -{ - // toggle modifiers that don't match the desired state - KeyModifierMask different = (m_mask ^ mask); - if ((different & KeyModifierCapsLock) != 0) { - toggleKey(KeyModifierCapsLock); - } - if ((different & KeyModifierNumLock) != 0) { - toggleKey(KeyModifierNumLock); - } - if ((different & KeyModifierScrollLock) != 0) { - toggleKey(KeyModifierScrollLock); - } -} - -KeyButton -CScreen::isAnyKeyDown() const -{ - for (UInt32 i = 1; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) { - if ((m_keys[i] & kDown) != 0) { - return static_cast(i); - } - } - return 0; -} - -bool -CScreen::isKeyDown(KeyButton key) const -{ - key &= 0x1ffu; - return (key != 0 && ((m_keys[key] & kDown) != 0)); -} - -bool -CScreen::isToggle(KeyModifierMask mask) const -{ - static const KeyModifierMask s_toggleMask = - KeyModifierCapsLock | KeyModifierNumLock | KeyModifierScrollLock; - return ((mask & s_toggleMask) != 0); -} - -bool -CScreen::isHalfDuplex(KeyModifierMask mask) const -{ - return ((mask == KeyModifierCapsLock && m_capsLockHalfDuplex) || - (mask == KeyModifierNumLock && m_numLockHalfDuplex)); -} - -bool -CScreen::isModifierActive(KeyModifierMask mask) const -{ - MaskToKeys::const_iterator i = m_maskToKeys.find(mask); - if (i == m_maskToKeys.end()) { - return false; - } - - KeyButtons::const_iterator j = i->second.begin(); - if (isToggle(mask)) { - // modifier is a toggle - if (isKeyToggled(*j)) { - return true; - } - } - else { - // modifier is not a toggle - for (; j != i->second.end(); ++j) { - if (isKeyDown(*j)) { - return true; - } - } - } - return false; -} - -KeyModifierMask -CScreen::getActiveModifiers() const -{ - if (m_isPrimary) { - return m_screen->getActiveModifiers(); - } - else { - return m_mask; - } -} - -bool -CScreen::mapModifier(Keystrokes& keys, Keystrokes& undo, - KeyModifierMask mask, bool desireActive) const -{ - // look up modifier - MaskToKeys::const_iterator i = m_maskToKeys.find(mask); - if (i == m_maskToKeys.end()) { - return false; - } - - // ignore if already in desired state - if (isModifierActive(mask) == desireActive) { - return true; - } - - // initialize keystroke - Keystroke keystroke; - keystroke.m_repeat = false; - - // handle toggles - if (isToggle(mask)) { - keystroke.m_key = i->second.front(); - keystroke.m_press = true; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); - keystroke.m_press = false; - undo.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - - else if (desireActive) { - // press - keystroke.m_key = i->second.front(); - keystroke.m_press = true; - keys.push_back(keystroke); - keystroke.m_press = false; - undo.push_back(keystroke); - } - - else { - // releasing a modifier is quite different from pressing one. - // when we release a modifier we have to release every keycode that - // is assigned to the modifier since the modifier is active if any - // one of them is down. when we press a modifier we just have to - // press one of those keycodes. - for (KeyButtons::const_iterator j = i->second.begin(); - j != i->second.end(); ++j) { - if (isKeyDown(*j)) { - keystroke.m_key = *j; - keystroke.m_press = false; - keys.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - } - } - - return true; -} - -KeyModifierMask -CScreen::getMaskForKey(KeyButton key) const -{ - KeyToMask::const_iterator i = m_keyToMask.find(key); - if (i == m_keyToMask.end()) { - return 0; - } - else { - return i->second; - } -} - void CScreen::enablePrimary() { @@ -749,13 +429,16 @@ CScreen::enterPrimary() } void -CScreen::enterSecondary() +CScreen::enterSecondary(KeyModifierMask toggleMask) { // update our keyboard state to reflect the local state - updateKeys(); + m_screen->updateKeys(); // remember toggle key state. we'll restore this when we leave. - m_toggleKeys = m_mask; + m_toggleKeys = getActiveModifiers(); + + // restore toggle key state + setToggleState(toggleMask); } void @@ -774,170 +457,42 @@ CScreen::leaveSecondary() setToggleState(m_toggleKeys); } -KeyModifierMask -CScreen::getModifierMask() const -{ - KeyModifierMask mask = 0; - if (isModifierActive(KeyModifierShift)) { - mask |= KeyModifierShift; - } - if (isModifierActive(KeyModifierControl)) { - mask |= KeyModifierControl; - } - if (isModifierActive(KeyModifierAlt)) { - mask |= KeyModifierAlt; - } - if (isModifierActive(KeyModifierMeta)) { - mask |= KeyModifierMeta; - } - if (isModifierActive(KeyModifierSuper)) { - mask |= KeyModifierSuper; - } - if (isModifierActive(KeyModifierModeSwitch)) { - mask |= KeyModifierModeSwitch; - } - if (isModifierActive(KeyModifierNumLock)) { - mask |= KeyModifierNumLock; - } - if (isModifierActive(KeyModifierCapsLock)) { - mask |= KeyModifierCapsLock; - } - if (isModifierActive(KeyModifierScrollLock)) { - mask |= KeyModifierScrollLock; - } - return mask; -} - void -CScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) +CScreen::releaseKeys() { - // do nothing if no keys or no repeats - if (count < 1 || keys.empty()) { - return; - } - - // generate key events - LOG((CLOG_DEBUG2 "keystrokes:")); - for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { - if (k->m_repeat) { - // repeat from here up to but not including the next key - // with m_repeat == false count times. - Keystrokes::const_iterator start = k; - for (; count > 0; --count) { - // send repeating events - for (k = start; k != keys.end() && k->m_repeat; ++k) { - fakeKeyEvent(k->m_key, k->m_press, true); - } - } - - // note -- k is now on the first non-repeat key after the - // repeat keys, exactly where we'd like to continue from. - } - else { - // send event - fakeKeyEvent(k->m_key, k->m_press, false); - - // next key - ++k; + // release keys that we've synthesized a press for and only those + // keys. we don't want to synthesize a release on a key the user + // is still physically pressing. + for (KeyButton i = 1; i < IKeyState::kNumButtons; ++i) { + if (m_screen->isKeyDown(i)) { + m_screen->fakeKeyUp(i); } } } void -CScreen::fakeKeyEvent(KeyButton key, bool press, bool repeat) const +CScreen::setToggleState(KeyModifierMask mask) { - // half-duplex keys are special. we ignore releases and convert - // a press when the toggle is active to a release. - KeyModifierMask mask = getMaskForKey(key); - if (isHalfDuplex(mask)) { - if (repeat || !press) { - return; - } - if (isModifierActive(mask)) { - press = false; - } + // toggle modifiers that don't match the desired state + KeyModifierMask different = (m_screen->getActiveModifiers() ^ mask); + if ((different & KeyModifierCapsLock) != 0) { + m_screen->fakeToggle(KeyModifierCapsLock); } - - // send key event - LOG((CLOG_DEBUG2 " %d %s%s", key, press ? "down" : "up", repeat ? " repeat" : "")); - m_screen->fakeKeyEvent(key, press); -} - -void -CScreen::updateKeyState(KeyButton button, KeyButton key, bool press) -{ - // ignore bogus keys - key &= 0x1ffu; - if (button == 0 || key == 0) { - return; + if ((different & KeyModifierNumLock) != 0) { + m_screen->fakeToggle(KeyModifierNumLock); } - - // update shadow state. shadow state doesn't change on auto-repeat. - if (press) { - // key is now down - m_serverKeyMap[button] = key; - m_keys[key] |= kDown; - m_fakeKeys[key] |= kDown; - } - else { - // key is now up - m_serverKeyMap.erase(button); - m_keys[key] &= ~kDown; - m_fakeKeys[key] &= ~kDown; - } - KeyModifierMask mask = getMaskForKey(key); - if (mask != 0) { - // key is a modifier - if (isToggle(mask)) { - // key is a toggle modifier - if (press) { - m_keys[key] ^= kToggled; - m_mask ^= mask; - - // if key is half duplex then don't report it as down - if (isHalfDuplex(mask)) { - m_keys[key] &= ~kDown; - m_fakeKeys[key] &= ~kDown; - } - } - } - else { - // key is a normal modifier - if (press) { - m_mask |= mask; - } - else if (!isModifierActive(mask)) { - // no key for modifier is down anymore - m_mask &= ~mask; - } - } - LOG((CLOG_DEBUG2 "new mask: 0x%04x", m_mask)); + if ((different & KeyModifierScrollLock) != 0) { + m_screen->fakeToggle(KeyModifierScrollLock); } } -void -CScreen::toggleKey(KeyModifierMask mask) +KeyButton +CScreen::isAnyKeyDown() const { - // get the system key ID for this toggle key ID - MaskToKeys::const_iterator i = m_maskToKeys.find(mask); - if (i == m_maskToKeys.end()) { - return; + for (KeyButton i = 1; i < IKeyState::kNumButtons; ++i) { + if (m_screen->isKeyDown(i)) { + return i; + } } - KeyButton key = i->second.front(); - - // toggle the key - fakeKeyEvent(key, true, false); - fakeKeyEvent(key, false, false); - - // toggle shadow state - m_mask ^= mask; - key &= 0x1ffu; - m_keys[key] ^= kToggled; -} - -bool -CScreen::isKeyToggled(KeyButton key) const -{ - key &= 0x1ffu; - return (key != 0 && ((m_keys[key] & kToggled) != 0)); + return 0; } diff --git a/lib/synergy/CScreen.h b/lib/synergy/CScreen.h index ce2e43d3..ae605d9b 100644 --- a/lib/synergy/CScreen.h +++ b/lib/synergy/CScreen.h @@ -15,12 +15,11 @@ #ifndef CSCREEN_H #define CSCREEN_H -#include "IKeyState.h" #include "IScreen.h" #include "ClipboardTypes.h" +#include "KeyTypes.h" #include "MouseTypes.h" #include "OptionTypes.h" -#include "stdmap.h" class IClipboard; class IPlatformScreen; @@ -30,7 +29,7 @@ class IPlatformScreen; This is a platform independent screen. It can work as either a primary or secondary screen. */ -class CScreen : public IScreen, public IKeyState { +class CScreen : public IScreen { public: CScreen(IPlatformScreen* platformScreen); virtual ~CScreen(); @@ -55,9 +54,10 @@ public: //! Enter screen /*! - Called when the user navigates to this screen. + Called when the user navigates to this screen. \p toggleMask has the + toggle keys that should be turned on on the secondary screen. */ - void enter(); + void enter(KeyModifierMask toggleMask); //! Leave screen /*! @@ -208,6 +208,12 @@ public: */ void getCursorCenter(SInt32& x, SInt32& y) const; + //! Get the active modifiers + /*! + Returns the modifiers that are currently active. + */ + KeyModifierMask getActiveModifiers() const; + //@} // IScreen overrides @@ -217,25 +223,6 @@ public: SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; - // IKeyState overrides - virtual void updateKeys(); - virtual void releaseKeys(); - virtual void setKeyDown(KeyButton key, bool); - virtual void setToggled(KeyModifierMask); - virtual void addModifier(KeyModifierMask, KeyButtons&); - virtual void setToggleState(KeyModifierMask); - virtual KeyButton isAnyKeyDown() const; - virtual bool isKeyDown(KeyButton) const; - virtual bool isToggle(KeyModifierMask) const; - virtual bool isHalfDuplex(KeyModifierMask) const; - virtual bool isModifierActive(KeyModifierMask) const; - virtual KeyModifierMask - getActiveModifiers() const; - virtual bool mapModifier(Keystrokes& keys, Keystrokes& undo, - KeyModifierMask mask, bool desireActive) const; - virtual KeyModifierMask - getMaskForKey(KeyButton) const; - protected: void enablePrimary(); void enableSecondary(); @@ -243,35 +230,16 @@ protected: void disableSecondary(); void enterPrimary(); - void enterSecondary(); + void enterSecondary(KeyModifierMask toggleMask); void leavePrimary(); void leaveSecondary(); private: - // Get the modifier mask for the current key state - KeyModifierMask getModifierMask() const; - - // Send fake keystrokes - void doKeystrokes(const Keystrokes&, SInt32 count); - - // Send a fake key event - void fakeKeyEvent(KeyButton, bool press, bool repeat) const; - - // Update the shadow state for a key - void updateKeyState(KeyButton button, - KeyButton key, bool press); - - // Toggle a modifier - void toggleKey(KeyModifierMask); - - // Test if a modifier is toggled - bool isKeyToggled(KeyButton) const; + void releaseKeys(); + void setToggleState(KeyModifierMask); + KeyButton isAnyKeyDown() const; private: - typedef std::map ServerKeyMap; - typedef std::map MaskToKeys; - typedef std::map KeyToMask; - // our platform dependent screen IPlatformScreen* m_screen; @@ -289,26 +257,7 @@ private: // note toggle keys that toggles on up/down (false) or on // transition (true) - bool m_numLockHalfDuplex; - bool m_capsLockHalfDuplex; - - // keyboard state - - // map server key buttons to local system keys - ServerKeyMap m_serverKeyMap; - - // system key states as set by us or the user - KeyState m_keys[512]; - - // system key states as set by us - KeyState m_fakeKeys[512]; - - // modifier info - MaskToKeys m_maskToKeys; - KeyToMask m_keyToMask; - - // current active modifiers - KeyModifierMask m_mask; + KeyModifierMask m_halfDuplex; // the toggle key state when this screen was last entered KeyModifierMask m_toggleKeys; diff --git a/lib/synergy/IKeyState.cpp b/lib/synergy/IKeyState.cpp new file mode 100644 index 00000000..bcc1ea2a --- /dev/null +++ b/lib/synergy/IKeyState.cpp @@ -0,0 +1,61 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "IKeyState.h" + +// +// IKeyState +// + +CEvent::Type IKeyState::s_keyDownEvent = CEvent::kUnknown; +CEvent::Type IKeyState::s_keyUpEvent = CEvent::kUnknown; +CEvent::Type IKeyState::s_keyRepeatEvent = CEvent::kUnknown; + +CEvent::Type +IKeyState::getKeyDownEvent() +{ + return CEvent::registerTypeOnce(s_keyDownEvent, + "IKeyState::keyDown"); +} + +CEvent::Type +IKeyState::getKeyUpEvent() +{ + return CEvent::registerTypeOnce(s_keyUpEvent, + "IKeyState::keyUp"); +} + +CEvent::Type +IKeyState::getKeyRepeatEvent() +{ + return CEvent::registerTypeOnce(s_keyRepeatEvent, + "IKeyState::keyRepeat"); +} + + +// +// IKeyState::CKeyInfo +// + +IKeyState::CKeyInfo* +IKeyState::CKeyInfo::alloc(KeyID id, + KeyModifierMask mask, KeyButton button, SInt32 count) +{ + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); + info->m_key = id; + info->m_mask = mask; + info->m_button = button; + info->m_count = count; + return info; +} diff --git a/lib/synergy/IKeyState.h b/lib/synergy/IKeyState.h index d54d29f1..7d0b8f63 100644 --- a/lib/synergy/IKeyState.h +++ b/lib/synergy/IKeyState.h @@ -17,20 +17,30 @@ #include "IInterface.h" #include "KeyTypes.h" -#include "CString.h" -#include "stdvector.h" +#include "CEvent.h" +//! Key state interface +/*! +This interface provides access to set and query the keyboard state and +to synthesize key events. +*/ class IKeyState : public IInterface { public: - class Keystroke { - public: - KeyButton m_key; - bool m_press; - bool m_repeat; + enum { + kNumButtons = 0x200 }; - typedef std::vector Keystrokes; - typedef std::vector KeyButtons; + //! Key event data + class CKeyInfo { + public: + static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count); + + public: + KeyID m_key; + KeyModifierMask m_mask; + KeyButton m_button; + SInt32 m_count; + }; //! @name manipulators //@{ @@ -42,79 +52,52 @@ public: */ virtual void updateKeys() = 0; - //! Release fake pressed keys + //! Set half-duplex mask /*! - Send fake key events to release keys that aren't physically pressed - but are logically pressed. + Sets which modifier toggle keys are half-duplex. A half-duplex + toggle key doesn't report a key release when toggled on and + doesn't report a key press when toggled off. */ - virtual void releaseKeys() = 0; + virtual void setHalfDuplexMask(KeyModifierMask) = 0; - //! Mark key as being down + //! Fake a key press /*! - Sets the state of \c key to down or up. + Synthesizes a key press event and updates the key state. */ - virtual void setKeyDown(KeyButton key, bool down) = 0; + virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button) = 0; - //! Mark modifier as being toggled on + //! Fake a key repeat /*! - Sets the state of the keys for the given (single) modifier to be - toggled on. + Synthesizes a key repeat event and updates the key state. */ - virtual void setToggled(KeyModifierMask) = 0; + virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) = 0; - //! Add keys for modifier + //! Fake a key release /*! - Sets the keys that are mapped to the given (single) modifier. For - example, if buttons 5 and 23 were mapped to KeyModifierShift (perhaps - as left and right shift keys) then the mask would be KeyModifierShift - and \c keys would contain 5 and 23. A modifier with no keys is - ignored. Keys that are zero are ignored. \c keys may be modified - by the call. + Synthesizes a key release event and updates the key state. */ - virtual void addModifier(KeyModifierMask, KeyButtons& keys) = 0; + virtual void fakeKeyUp(KeyButton button) = 0; - //! Set toggle key state + //! Fake a modifier toggle /*! - Update the local toggle key state to match the given state. + Synthesizes key press/release events to toggle the given \p modifier + and updates the key state. */ - virtual void setToggleState(KeyModifierMask) = 0; + virtual void fakeToggle(KeyModifierMask modifier) = 0; //@} //! @name accessors //@{ - //! Test if any key is down - /*! - If any key is down then returns one of those keys. Otherwise returns 0. - */ - virtual KeyButton isAnyKeyDown() const = 0; - //! Test if key is pressed /*! Returns true iff the given key is down. Half-duplex toggles - should always return false. + always return false. */ virtual bool isKeyDown(KeyButton) const = 0; - //! Test if modifier is a toggle - /*! - Returns true iff the given (single) modifier is a toggle. - */ - virtual bool isToggle(KeyModifierMask) const = 0; - - //! Test if modifier is half-duplex - /*! - Returns true iff the given (single) modifier is a half-duplex - toggle key. - */ - virtual bool isHalfDuplex(KeyModifierMask) const = 0; - - //! Test if modifier is active - /*! - Returns true iff the given (single) modifier is currently active. - */ - virtual bool isModifierActive(KeyModifierMask) const = 0; - //! Get the active modifiers /*! Returns the modifiers that are currently active. @@ -122,35 +105,25 @@ public: virtual KeyModifierMask getActiveModifiers() const = 0; - //! Get key events to change modifier state + //! Get name of key /*! - Retrieves the key events necessary to activate (\c desireActive is true) - or deactivate (\c desireActive is false) the modifier given by \c mask - by pushing them onto the back of \c keys. \c mask must specify exactly - one modifier. \c undo receives the key events necessary to restore the - modifier's previous state. They're pushed onto \c undo in the reverse - order they should be executed. Returns true if the modifier can be - adjusted, false otherwise. + Return a string describing the given key. */ - virtual bool mapModifier(Keystrokes& keys, Keystrokes& undo, - KeyModifierMask mask, bool desireActive) const = 0; + virtual const char* getKeyName(KeyButton) const = 0; - //! Get modifier mask for key - /*! - Returns the modifier mask for \c key. If \c key is not a modifier - key then returns 0. - */ - virtual KeyModifierMask - getMaskForKey(KeyButton) const = 0; + //! Get key down event type. Event data is CKeyInfo*, count == 1. + static CEvent::Type getKeyDownEvent(); + //! Get key up event type. Event data is CKeyInfo*, count == 1. + static CEvent::Type getKeyUpEvent(); + //! Get key repeat event type. Event data is CKeyInfo*. + static CEvent::Type getKeyRepeatEvent(); //@} -protected: - typedef UInt8 KeyState; - enum EKeyState { - kDown = 0x01, //!< Key is down - kToggled = 0x80 //!< Key is toggled on - }; +private: + static CEvent::Type s_keyDownEvent; + static CEvent::Type s_keyUpEvent; + static CEvent::Type s_keyRepeatEvent; }; #endif diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index 379ef03d..b97f3453 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -18,11 +18,11 @@ #include "IScreen.h" #include "IPrimaryScreen.h" #include "ISecondaryScreen.h" +#include "IKeyState.h" #include "ClipboardTypes.h" #include "OptionTypes.h" class IClipboard; -class IKeyState; //! Screen interface /*! @@ -31,18 +31,12 @@ screen implementations that are used by both primary and secondary screens. */ class IPlatformScreen : public IScreen, - public IPrimaryScreen, public ISecondaryScreen { + public IPrimaryScreen, public ISecondaryScreen, + public IKeyState { public: //! @name manipulators //@{ - //! Set the key state - /*! - Sets the key state object. This object tracks keyboard state and - the screen is expected to keep it up to date. - */ - virtual void setKeyState(IKeyState*) = 0; - //! Enable screen /*! Enable the screen, preparing it to report system and user events. @@ -124,13 +118,6 @@ public: */ virtual void setOptions(const COptionsList& options) = 0; - //! Get keyboard state - /*! - Put the current keyboard state into the IKeyState passed to - \c setKeyState(). - */ - virtual void updateKeys() = 0; - //! Set clipboard sequence number /*! Sets the sequence number to use in subsequent clipboard events. @@ -161,20 +148,27 @@ public: virtual void warpCursor(SInt32 x, SInt32 y) = 0; virtual SInt32 getJumpZoneSize() const = 0; virtual bool isAnyMouseButtonDown() const = 0; - virtual KeyModifierMask getActiveModifiers() const = 0; virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; - virtual const char* getKeyName(KeyButton) const = 0; // ISecondaryScreen overrides - virtual void fakeKeyEvent(KeyButton id, bool press) const = 0; virtual bool fakeCtrlAltDel() const = 0; virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; virtual void fakeMouseWheel(SInt32 delta) const = 0; - virtual KeyButton mapKey(IKeyState::Keystrokes&, - const IKeyState& keyState, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const = 0; + + // IKeyState overrides + virtual void updateKeys() = 0; + virtual void setHalfDuplexMask(KeyModifierMask) = 0; + virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button) = 0; + virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) = 0; + virtual void fakeKeyUp(KeyButton button) = 0; + virtual void fakeToggle(KeyModifierMask modifier) = 0; + virtual bool isKeyDown(KeyButton) const = 0; + virtual KeyModifierMask + getActiveModifiers() const = 0; + virtual const char* getKeyName(KeyButton) const = 0; protected: //! Handle system event @@ -193,10 +187,10 @@ protected: A primary screen has further responsibilities. It should post the events in \c IPrimaryScreen as appropriate. It should also - call \c setKeyDown() on the \c IKeyState passed to \c setKeyState() - whenever a key is pressed or released (but not for key repeats). - And it should call \c updateKeys() on the \c IKeyState if necessary - when the keyboard mapping changes. + call \c setKeyDown() on its \c CKeyState whenever a key is pressed + or released (but not for key repeats). And it should call + \c updateKeys() on its \c CKeyState if necessary when the keyboard + mapping changes. The target of all events should be the value returned by \c getEventTarget(). diff --git a/lib/synergy/IPrimaryScreen.cpp b/lib/synergy/IPrimaryScreen.cpp index 9a201001..62d3c4b9 100644 --- a/lib/synergy/IPrimaryScreen.cpp +++ b/lib/synergy/IPrimaryScreen.cpp @@ -18,9 +18,6 @@ // IPrimaryScreen // -CEvent::Type IPrimaryScreen::s_keyDownEvent = CEvent::kUnknown; -CEvent::Type IPrimaryScreen::s_keyUpEvent = CEvent::kUnknown; -CEvent::Type IPrimaryScreen::s_keyRepeatEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_buttonDownEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_buttonUpEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_motionPrimaryEvent = CEvent::kUnknown; @@ -29,27 +26,6 @@ CEvent::Type IPrimaryScreen::s_wheelEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_ssActivatedEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_ssDeactivatedEvent = CEvent::kUnknown; -CEvent::Type -IPrimaryScreen::getKeyDownEvent() -{ - return CEvent::registerTypeOnce(s_keyDownEvent, - "IPrimaryScreen::keyDown"); -} - -CEvent::Type -IPrimaryScreen::getKeyUpEvent() -{ - return CEvent::registerTypeOnce(s_keyUpEvent, - "IPrimaryScreen::keyUp"); -} - -CEvent::Type -IPrimaryScreen::getKeyRepeatEvent() -{ - return CEvent::registerTypeOnce(s_keyRepeatEvent, - "IPrimaryScreen::keyRepeat"); -} - CEvent::Type IPrimaryScreen::getButtonDownEvent() { @@ -100,23 +76,6 @@ IPrimaryScreen::getScreensaverDeactivatedEvent() } -// -// IPrimaryScreen::CKeyInfo -// - -IPrimaryScreen::CKeyInfo* -IPrimaryScreen::CKeyInfo::alloc(KeyID id, - KeyModifierMask mask, KeyButton button, SInt32 count) -{ - CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); - info->m_key = id; - info->m_mask = mask; - info->m_button = button; - info->m_count = count; - return info; -} - - // // IPrimaryScreen::CButtonInfo // diff --git a/lib/synergy/IPrimaryScreen.h b/lib/synergy/IPrimaryScreen.h index 9b88caf8..6718a17b 100644 --- a/lib/synergy/IPrimaryScreen.h +++ b/lib/synergy/IPrimaryScreen.h @@ -16,9 +16,8 @@ #define IPRIMARYSCREEN_H #include "IInterface.h" -#include "IKeyState.h" -#include "CEvent.h" #include "MouseTypes.h" +#include "CEvent.h" //! Primary screen interface /*! @@ -27,17 +26,6 @@ primary screen implementations. */ class IPrimaryScreen : public IInterface { public: - //! Key event data - class CKeyInfo { - public: - static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count); - - public: - KeyID m_key; - KeyModifierMask m_mask; - KeyButton m_button; - SInt32 m_count; - }; //! Button event data class CButtonInfo { public: @@ -103,14 +91,6 @@ public: */ virtual bool isAnyMouseButtonDown() const = 0; - //! Get current modifier key state - /*! - Returns the current modifier key state. Ideally, "current" means - up to the lat processed event but it can mean the current physical - modifier key state. - */ - virtual KeyModifierMask getActiveModifiers() const = 0; - //! Get cursor center position /*! Return the cursor center position which is where we park the @@ -119,18 +99,6 @@ public: */ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; - //! Get name of key - /*! - Return a string describing the given key. - */ - virtual const char* getKeyName(KeyButton) const = 0; - - //! Get key down event type. Event data is CKeyInfo*, count == 1. - static CEvent::Type getKeyDownEvent(); - //! Get key up event type. Event data is CKeyInfo*, count == 1. - static CEvent::Type getKeyUpEvent(); - //! Get key repeat event type. Event data is CKeyInfo*. - static CEvent::Type getKeyRepeatEvent(); //! Get button down event type. Event data is CButtonInfo*. static CEvent::Type getButtonDownEvent(); //! Get button up event type. Event data is CButtonInfo*. @@ -156,9 +124,6 @@ public: //@} private: - static CEvent::Type s_keyDownEvent; - static CEvent::Type s_keyUpEvent; - static CEvent::Type s_keyRepeatEvent; static CEvent::Type s_buttonDownEvent; static CEvent::Type s_buttonUpEvent; static CEvent::Type s_motionPrimaryEvent; diff --git a/lib/synergy/ISecondaryScreen.h b/lib/synergy/ISecondaryScreen.h index 0a556d59..65ac7708 100644 --- a/lib/synergy/ISecondaryScreen.h +++ b/lib/synergy/ISecondaryScreen.h @@ -16,7 +16,6 @@ #define ISECONDARYSCREEN_H #include "IInterface.h" -#include "IKeyState.h" #include "MouseTypes.h" //! Secondary screen interface @@ -29,12 +28,6 @@ public: //! @name accessors //@{ - //! Fake key press/release - /*! - Synthesize a press or release of key \c id. - */ - virtual void fakeKeyEvent(KeyButton id, bool press) const = 0; - //! Fake ctrl+alt+del /*! Synthesize a press of ctrl+alt+del. Return true if processing is @@ -60,22 +53,6 @@ public: */ virtual void fakeMouseWheel(SInt32 delta) const = 0; - //! Map key press/repeat to keystrokes - /*! - Convert a press/repeat of key \c id with the modifiers as given - in \c desiredMask into the keystrokes necessary to synthesize - that key event. This may expand into multiple keys due to modifiers - that don't match the current modifier state from \c keyState, or to - needing to compose a character using dead key, or to other reasons. - Return the platform specific code of the key being pressed. If \c id - cannot be mapped or if \c isAutoRepeat is true and the key does not - auto-repeat then return 0. - */ - virtual KeyButton mapKey(IKeyState::Keystrokes&, - const IKeyState& keyState, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const = 0; - //@} }; diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 60a304ca..27fe0c63 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -26,16 +26,21 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libsynergy.a libsynergy_a_SOURCES = \ CClipboard.cpp \ + CKeyState.cpp \ CPacketStreamFilter.cpp \ + CPlatformScreen.cpp \ CProtocolUtil.cpp \ CScreen.cpp \ IClipboard.cpp \ + IKeyState.cpp \ IPrimaryScreen.cpp \ IScreen.cpp \ XScreen.cpp \ XSynergy.cpp \ CClipboard.h \ + CKeyState.h \ CPacketStreamFilter.h \ + CPlatformScreen.h \ CProtocolUtil.h \ CScreen.h \ ClipboardTypes.h \ From ab11ebea014949a513c841d90c8954f63185e1fc Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 26 Mar 2004 20:59:21 +0000 Subject: [PATCH 611/807] Fixed handling of reading strings from the registry. This was broken when support for binary data was added. The terminating NUL was included in the string as a character (that's in addition to the terminating NUL added by std::string). --- lib/arch/CArchMiscWindows.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index 6f4e9ca2..b4010040 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -222,6 +222,11 @@ CArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type) return std::string(); } + // if zero size then return empty string + if (size == 0) { + return std::string(); + } + // allocate space char* buffer = new char[size]; @@ -234,6 +239,10 @@ CArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type) } // clean up and return value + if (type == REG_SZ && buffer[size - 1] == '\0') { + // don't include terminating nul; std::string will add one. + --size; + } std::string value(buffer, size); delete[] buffer; return value; From e2a31e8b66c5a98cdf78249342a70247b965c52e Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 26 Mar 2004 20:59:26 +0000 Subject: [PATCH 612/807] Converted win32 to new keyboard state tracking design. Also changed locking to screen so that keys no longer count (only mouse buttons and scroll lock toggled on). This is to deal with the unreliability of key event reporting which can leave us locked to a screen with no key physically pressed. The result of this is that clients get key repeats and releases without the corresponding key press. CKeyState handles this by discarding repeat/release events on keys it hasn't seen go down. Also made a few other minor fixes to win32 keyboard handling. --- lib/platform/CMSWindowsDesks.cpp | 865 ++++++++++++++ lib/platform/CMSWindowsDesks.h | 252 ++++ lib/platform/CMSWindowsDesktop.cpp | 75 -- lib/platform/CMSWindowsDesktop.h | 58 - lib/platform/CMSWindowsKeyMapper.h | 196 ---- ...wsKeyMapper.cpp => CMSWindowsKeyState.cpp} | 867 +++++++------- lib/platform/CMSWindowsKeyState.h | 130 +++ lib/platform/CMSWindowsScreen.cpp | 1009 ++--------------- lib/platform/CMSWindowsScreen.h | 125 +- lib/platform/CSynergyHook.cpp | 8 +- lib/platform/CXWindowsKeyState.cpp | 9 +- lib/platform/CXWindowsKeyState.h | 1 + lib/platform/CXWindowsScreen.cpp | 12 +- lib/platform/CXWindowsScreen.h | 1 - lib/platform/platform.dsp | 8 +- lib/server/CServer.cpp | 16 +- lib/server/CServer.h | 6 + lib/synergy/CKeyState.cpp | 26 +- lib/synergy/CKeyState.h | 11 +- lib/synergy/CPlatformScreen.cpp | 6 + lib/synergy/CPlatformScreen.h | 2 +- lib/synergy/CScreen.cpp | 16 +- lib/synergy/IKeyState.h | 7 + lib/synergy/IPlatformScreen.h | 2 +- lib/synergy/ISecondaryScreen.h | 7 - lib/synergy/libsynergy.dsp | 20 + 26 files changed, 1974 insertions(+), 1761 deletions(-) create mode 100644 lib/platform/CMSWindowsDesks.cpp create mode 100644 lib/platform/CMSWindowsDesks.h delete mode 100644 lib/platform/CMSWindowsDesktop.cpp delete mode 100644 lib/platform/CMSWindowsDesktop.h delete mode 100644 lib/platform/CMSWindowsKeyMapper.h rename lib/platform/{CMSWindowsKeyMapper.cpp => CMSWindowsKeyState.cpp} (72%) create mode 100644 lib/platform/CMSWindowsKeyState.h diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp new file mode 100644 index 00000000..0e2b7734 --- /dev/null +++ b/lib/platform/CMSWindowsDesks.cpp @@ -0,0 +1,865 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsDesks.h" +#include "CMSWindowsDesktop.h" +#include "CMSWindowsScreen.h" +#include "IScreenSaver.h" +#include "XScreen.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" +#include "IEventQueue.h" +#include "IJob.h" +#include "TMethodEventJob.h" +#include "TMethodJob.h" +#include "CArchMiscWindows.h" +#include + +// these are only defined when WINVER >= 0x0500 +#if !defined(SPI_GETMOUSESPEED) +#define SPI_GETMOUSESPEED 112 +#endif +#if !defined(SPI_SETMOUSESPEED) +#define SPI_SETMOUSESPEED 113 +#endif + +// X button stuff +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define WM_NCXBUTTONDOWN 0x00AB +#define WM_NCXBUTTONUP 0x00AC +#define WM_NCXBUTTONDBLCLK 0x00AD +#define MOUSEEVENTF_XDOWN 0x0100 +#define MOUSEEVENTF_XUP 0x0200 +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif +#if !defined(VK_XBUTTON1) +#define VK_XBUTTON1 0x05 +#define VK_XBUTTON2 0x06 +#endif + +// ; +#define SYNERGY_MSG_SWITCH SYNERGY_HOOK_LAST_MSG + 1 +// ; +#define SYNERGY_MSG_ENTER SYNERGY_HOOK_LAST_MSG + 2 +// ; +#define SYNERGY_MSG_LEAVE SYNERGY_HOOK_LAST_MSG + 3 +// wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code +#define SYNERGY_MSG_FAKE_KEY SYNERGY_HOOK_LAST_MSG + 4 + // flags, XBUTTON id +#define SYNERGY_MSG_FAKE_BUTTON SYNERGY_HOOK_LAST_MSG + 5 +// x; y +#define SYNERGY_MSG_FAKE_MOVE SYNERGY_HOOK_LAST_MSG + 6 +// delta; +#define SYNERGY_MSG_FAKE_WHEEL SYNERGY_HOOK_LAST_MSG + 7 +// POINT*; +#define SYNERGY_MSG_CURSOR_POS SYNERGY_HOOK_LAST_MSG + 8 +// IKeyState*; +#define SYNERGY_MSG_SYNC_KEYS SYNERGY_HOOK_LAST_MSG + 9 +// install; +#define SYNERGY_MSG_SCREENSAVER SYNERGY_HOOK_LAST_MSG + 10 + +// +// CMSWindowsDesks +// + +CMSWindowsDesks::CMSWindowsDesks( + bool isPrimary, HINSTANCE hookLibrary, + const IScreenSaver* screensaver, IJob* updateKeys) : + m_isPrimary(isPrimary), + m_is95Family(CArchMiscWindows::isWindows95Family()), + m_isOnScreen(m_isPrimary), + m_x(0), m_y(0), + m_w(0), m_h(0), + m_xCenter(0), m_yCenter(0), + m_multimon(false), + m_timer(NULL), + m_screensaver(screensaver), + m_screensaverNotify(false), + m_activeDesk(NULL), + m_activeDeskName(), + m_mutex(), + m_deskReady(&m_mutex, false), + m_updateKeys(updateKeys) +{ + queryHookLibrary(hookLibrary); + m_cursor = createBlankCursor(); + m_deskClass = createDeskWindowClass(m_isPrimary); + m_keyLayout = GetKeyboardLayout(GetCurrentThreadId()); +} + +CMSWindowsDesks::~CMSWindowsDesks() +{ + disable(); + destroyClass(m_deskClass); + destroyCursor(m_cursor); + delete m_updateKeys; +} + +void +CMSWindowsDesks::enable() +{ + // set the active desk and (re)install the hooks + checkDesk(); + + // install the desk timer. this timer periodically checks + // which desk is active and reinstalls the hooks as necessary. + // we wouldn't need this if windows notified us of a desktop + // change but as far as i can tell it doesn't. + m_timer = EVENTQUEUE->newTimer(0.2, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer, + new TMethodEventJob( + this, &CMSWindowsDesks::handleCheckDesk)); + + updateKeys(); +} + +void +CMSWindowsDesks::disable() +{ + // remove timer + if (m_timer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer); + EVENTQUEUE->deleteTimer(m_timer); + m_timer = NULL; + } + + // destroy desks + removeDesks(); + + m_isOnScreen = m_isPrimary; +} + +void +CMSWindowsDesks::enter() +{ + sendMessage(SYNERGY_MSG_ENTER, 0, 0); +} + +void +CMSWindowsDesks::leave(HKL keyLayout) +{ + sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)keyLayout, 0); +} + +void +CMSWindowsDesks::updateKeys() +{ + sendMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); +} + +void +CMSWindowsDesks::setShape(SInt32 x, SInt32 y, + SInt32 width, SInt32 height, + SInt32 xCenter, SInt32 yCenter, bool isMultimon) +{ + m_x = x; + m_y = y; + m_w = width; + m_h = height; + m_xCenter = xCenter; + m_yCenter = yCenter; + m_multimon = isMultimon; +} + +void +CMSWindowsDesks::installScreensaverHooks(bool install) +{ + if (m_isPrimary && m_screensaverNotify != install) { + m_screensaverNotify = install; + sendMessage(SYNERGY_MSG_SCREENSAVER, install, 0); + } +} + +void +CMSWindowsDesks::getCursorPos(SInt32& x, SInt32& y) const +{ + POINT pos; + sendMessage(SYNERGY_MSG_CURSOR_POS, reinterpret_cast(&pos), 0); + x = pos.x; + y = pos.y; +} + +void +CMSWindowsDesks::fakeKeyEvent( + KeyButton button, UINT virtualKey, + bool press, bool /*isAutoRepeat*/) const +{ + DWORD flags = 0; + if (((button & 0x100u) != 0)) { + flags |= KEYEVENTF_EXTENDEDKEY; + } + if (!press) { + flags |= KEYEVENTF_KEYUP; + } + sendMessage(SYNERGY_MSG_FAKE_KEY, flags, + MAKEWORD(static_cast(button & 0xffu), + static_cast(virtualKey & 0xffu))); +} + +void +CMSWindowsDesks::fakeMouseButton(ButtonID button, bool press) const +{ + // the system will swap the meaning of left/right for us if + // the user has configured a left-handed mouse but we don't + // want it to swap since we want the handedness of the + // server's mouse. so pre-swap for a left-handed mouse. + if (GetSystemMetrics(SM_SWAPBUTTON)) { + switch (button) { + case kButtonLeft: + button = kButtonRight; + break; + + case kButtonRight: + button = kButtonLeft; + break; + } + } + + // map button id to button flag and button data + DWORD data = 0; + DWORD flags; + switch (button) { + case kButtonLeft: + flags = press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; + break; + + case kButtonMiddle: + flags = press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; + break; + + case kButtonRight: + flags = press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; + break; + + case kButtonExtra0 + 0: + data = XBUTTON1; + flags = press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; + break; + + case kButtonExtra0 + 1: + data = XBUTTON2; + flags = press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; + break; + + default: + return; + } + + // do it + sendMessage(SYNERGY_MSG_FAKE_BUTTON, flags, data); +} + +void +CMSWindowsDesks::fakeMouseMove(SInt32 x, SInt32 y) const +{ + sendMessage(SYNERGY_MSG_FAKE_MOVE, + static_cast(x), + static_cast(y)); +} + +void +CMSWindowsDesks::fakeMouseWheel(SInt32 delta) const +{ + sendMessage(SYNERGY_MSG_FAKE_WHEEL, delta, 0); +} + +void +CMSWindowsDesks::sendMessage(UINT msg, WPARAM wParam, LPARAM lParam) const +{ + if (m_activeDesk != NULL && m_activeDesk->m_window != NULL) { + PostThreadMessage(m_activeDesk->m_threadID, msg, wParam, lParam); + waitForDesk(); + } +} + +void +CMSWindowsDesks::queryHookLibrary(HINSTANCE hookLibrary) +{ + // look up functions + if (m_isPrimary) { + m_install = (InstallFunc)GetProcAddress(hookLibrary, "install"); + m_uninstall = (UninstallFunc)GetProcAddress(hookLibrary, "uninstall"); + m_installScreensaver = + (InstallScreenSaverFunc)GetProcAddress( + hookLibrary, "installScreenSaver"); + m_uninstallScreensaver = + (UninstallScreenSaverFunc)GetProcAddress( + hookLibrary, "uninstallScreenSaver"); + if (m_install == NULL || + m_uninstall == NULL || + m_installScreensaver == NULL || + m_uninstallScreensaver == NULL) { + LOG((CLOG_ERR "Invalid hook library")); + throw XScreenOpenFailure(); + } + } + else { + m_install = NULL; + m_uninstall = NULL; + m_installScreensaver = NULL; + m_uninstallScreensaver = NULL; + } +} + +HCURSOR +CMSWindowsDesks::createBlankCursor() const +{ + // create a transparent cursor + int cw = GetSystemMetrics(SM_CXCURSOR); + int ch = GetSystemMetrics(SM_CYCURSOR); + UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; + UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; + memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); + memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); + HCURSOR c = CreateCursor(CMSWindowsScreen::getInstance(), + 0, 0, cw, ch, cursorAND, cursorXOR); + delete[] cursorXOR; + delete[] cursorAND; + return c; +} + +void +CMSWindowsDesks::destroyCursor(HCURSOR cursor) const +{ + if (cursor != NULL) { + DestroyCursor(cursor); + } +} + +ATOM +CMSWindowsDesks::createDeskWindowClass(bool isPrimary) const +{ + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = isPrimary ? + &CMSWindowsDesks::primaryDeskProc : + &CMSWindowsDesks::secondaryDeskProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = CMSWindowsScreen::getInstance(); + classInfo.hIcon = NULL; + classInfo.hCursor = m_cursor; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "SynergyDesk"; + classInfo.hIconSm = NULL; + return RegisterClassEx(&classInfo); +} + +void +CMSWindowsDesks::destroyClass(ATOM windowClass) const +{ + if (windowClass != 0) { + UnregisterClass((LPCTSTR)windowClass, CMSWindowsScreen::getInstance()); + } +} + +HWND +CMSWindowsDesks::createWindow(ATOM windowClass, const char* name) const +{ + HWND window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | + WS_EX_TOOLWINDOW, + (LPCTSTR)windowClass, + name, + WS_POPUP, + 0, 0, 1, 1, + NULL, NULL, + CMSWindowsScreen::getInstance(), + NULL); + if (window == NULL) { + LOG((CLOG_ERR "failed to create window: %d", GetLastError())); + throw XScreenOpenFailure(); + } + return window; +} + +void +CMSWindowsDesks::destroyWindow(HWND hwnd) const +{ + if (hwnd != NULL) { + DestroyWindow(hwnd); + } +} + +LRESULT CALLBACK +CMSWindowsDesks::primaryDeskProc( + HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +LRESULT CALLBACK +CMSWindowsDesks::secondaryDeskProc( + HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + // would like to detect any local user input and hide the hider + // window but for now we just detect mouse motion. + bool hide = false; + switch (msg) { + case WM_MOUSEMOVE: + if (LOWORD(lParam) != 0 || HIWORD(lParam) != 0) { + hide = true; + } + break; + } + + if (hide && IsWindowVisible(hwnd)) { + ReleaseCapture(); + SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | + SWP_NOACTIVATE | SWP_HIDEWINDOW); + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void +CMSWindowsDesks::deskMouseMove(SInt32 x, SInt32 y) const +{ + // motion is simple (i.e. it's on the primary monitor) if there + // is only one monitor. + bool simple = !m_multimon; + if (!simple) { + // also simple if motion is within the primary monitor + simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && + y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); + } + + // move the mouse directly to target position if motion is simple + if (simple) { + // when using absolute positioning with mouse_event(), + // the normalized device coordinates range over only + // the primary screen. + SInt32 w = GetSystemMetrics(SM_CXSCREEN); + SInt32 h = GetSystemMetrics(SM_CYSCREEN); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65535.0f * x) / (w - 1) + 0.5f), + (DWORD)((65535.0f * y) / (h - 1) + 0.5f), + 0, 0); + } + + // windows 98 (and Me?) is broken. you cannot set the absolute + // position of the mouse except on the primary monitor but you + // can do relative moves onto any monitor. this is, in microsoft's + // words, "by design." apparently the designers of windows 2000 + // we're a little less lazy and did it right. + // + // microsoft recommends in Q193003 to absolute position the cursor + // somewhere on the primary monitor then relative move to the + // desired location. this doesn't work for us because when the + // user drags a scrollbar, a window, etc. it causes the dragged + // item to jump back and forth between the position on the primary + // monitor and the desired position. while it always ends up in + // the right place, the effect is disconcerting. + // + // instead we'll get the cursor's current position and do just a + // relative move from there to the desired position. relative + // moves are subject to cursor acceleration which we don't want. + // so we disable acceleration, do the relative move, then restore + // acceleration. there's a slight chance we'll end up in the + // wrong place if the user moves the cursor using this system's + // mouse while simultaneously moving the mouse on the server + // system. that defeats the purpose of synergy so we'll assume + // that won't happen. even if it does, the next mouse move will + // correct the position. + else { + // save mouse speed & acceleration + int oldSpeed[4]; + bool accelChanged = + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && + SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); + + // use 1:1 motion + if (accelChanged) { + int newSpeed[4] = { 0, 0, 0, 1 }; + accelChanged = + SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || + SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); + } + + // move relative to mouse position + POINT pos; + GetCursorPos(&pos); + mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); + + // restore mouse speed & acceleration + if (accelChanged) { + SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); + SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); + } + } +} + +void +CMSWindowsDesks::deskEnter(CDesk* desk) +{ + if (!m_isPrimary) { + ReleaseCapture(); + } + ShowCursor(TRUE); + SetWindowPos(desk->m_window, HWND_BOTTOM, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | + SWP_NOACTIVATE | SWP_HIDEWINDOW); +} + +void +CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout) +{ + ShowCursor(FALSE); + if (m_isPrimary) { + // update key state + m_updateKeys->run(); + + // map a window to hide the cursor and to use whatever keyboard + // layout we choose rather than the keyboard layout of the last + // active window. + int x, y, w, h; + if (desk->m_lowLevel) { + // with a low level hook the cursor will never budge so + // just a 1x1 window is sufficient. + x = m_xCenter; + y = m_yCenter; + w = 1; + h = 1; + } + else { + // with regular hooks the cursor will jitter as it's moved + // by the user then back to the center by us. to be sure + // we never lose it, cover all the monitors with the window. + x = m_x; + y = m_y; + w = m_w; + h = m_h; + } + SetWindowPos(desk->m_window, HWND_TOPMOST, x, y, w, h, + SWP_NOACTIVATE | SWP_SHOWWINDOW); + + // switch to requested keyboard layout + ActivateKeyboardLayout(keyLayout, 0); + } + else { + // move hider window under the cursor center, raise, and show it + SetWindowPos(desk->m_window, HWND_TOPMOST, + m_xCenter, m_yCenter, 1, 1, + SWP_NOACTIVATE | SWP_SHOWWINDOW); + + // watch for mouse motion. if we see any then we hide the + // hider window so the user can use the physically attached + // mouse if desired. we'd rather not capture the mouse but + // we aren't notified when the mouse leaves our window. + SetCapture(desk->m_window); + + // warp the mouse to the cursor center + deskMouseMove(m_xCenter, m_yCenter); + } +} + +void +CMSWindowsDesks::deskThread(void* vdesk) +{ + MSG msg; + + // use given desktop for this thread + CDesk* desk = reinterpret_cast(vdesk); + desk->m_threadID = GetCurrentThreadId(); + desk->m_window = NULL; + if (desk->m_desk != NULL && SetThreadDesktop(desk->m_desk) != 0) { + // create a message queue + PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE); + + // create a window. we use this window to hide the cursor. + try { + desk->m_window = createWindow(m_deskClass, "SynergyDesk"); + LOG((CLOG_DEBUG "desk %s window is 0x%08x", desk->m_name.c_str(), desk->m_window)); + } + catch (...) { + // ignore + LOG((CLOG_DEBUG "can't create desk window for %s", desk->m_name.c_str())); + } + + // a window on the primary screen should never activate + if (m_isPrimary && desk->m_window != NULL) { + EnableWindow(desk->m_window, FALSE); + } + } + + // tell main thread that we're ready + { + CLock lock(&m_mutex); + m_deskReady = true; + m_deskReady.broadcast(); + } + + while (GetMessage(&msg, NULL, 0, 0)) { + switch (msg.message) { + default: + TranslateMessage(&msg); + DispatchMessage(&msg); + continue; + + case SYNERGY_MSG_SWITCH: + if (m_isPrimary) { + m_uninstall(); + if (m_screensaverNotify) { + m_uninstallScreensaver(); + m_installScreensaver(); + } + switch (m_install()) { + case kHOOK_FAILED: + // we won't work on this desk + desk->m_lowLevel = false; + break; + + case kHOOK_OKAY: + desk->m_lowLevel = false; + break; + + case kHOOK_OKAY_LL: + desk->m_lowLevel = true; + break; + } + } + break; + + case SYNERGY_MSG_ENTER: + m_isOnScreen = true; + deskEnter(desk); + break; + + case SYNERGY_MSG_LEAVE: + m_isOnScreen = false; + m_keyLayout = (HKL)msg.wParam; + deskLeave(desk, m_keyLayout); + break; + + case SYNERGY_MSG_FAKE_KEY: + keybd_event(HIBYTE(msg.lParam), LOBYTE(msg.lParam), msg.wParam, 0); + break; + + case SYNERGY_MSG_FAKE_BUTTON: + if (msg.wParam != 0) { + mouse_event(msg.wParam, 0, 0, msg.lParam, 0); + } + break; + + case SYNERGY_MSG_FAKE_MOVE: + deskMouseMove(static_cast(msg.wParam), + static_cast(msg.lParam)); + break; + + case SYNERGY_MSG_FAKE_WHEEL: + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, msg.wParam, 0); + break; + + case SYNERGY_MSG_CURSOR_POS: { + POINT* pos = reinterpret_cast(msg.wParam); + if (!GetCursorPos(pos)) { + pos->x = m_xCenter; + pos->y = m_yCenter; + } + break; + } + + case SYNERGY_MSG_SYNC_KEYS: + m_updateKeys->run(); + break; + + case SYNERGY_MSG_SCREENSAVER: + if (msg.wParam != 0) { + m_installScreensaver(); + } + else { + m_uninstallScreensaver(); + } + break; + } + + // notify that message was processed + CLock lock(&m_mutex); + m_deskReady = true; + m_deskReady.broadcast(); + } + + // clean up + deskEnter(desk); + if (desk->m_window != NULL) { + DestroyWindow(desk->m_window); + } + if (desk->m_desk != NULL) { + closeDesktop(desk->m_desk); + } +} + +CMSWindowsDesks::CDesk* +CMSWindowsDesks::addDesk(const CString& name, HDESK hdesk) +{ + CDesk* desk = new CDesk; + desk->m_name = name; + desk->m_desk = hdesk; + desk->m_targetID = GetCurrentThreadId(); + desk->m_thread = new CThread(new TMethodJob( + this, &CMSWindowsDesks::deskThread, desk)); + waitForDesk(); + m_desks.insert(std::make_pair(name, desk)); + return desk; +} + +void +CMSWindowsDesks::removeDesks() +{ + for (CDesks::iterator index = m_desks.begin(); + index != m_desks.end(); ++index) { + CDesk* desk = index->second; + PostThreadMessage(desk->m_threadID, WM_QUIT, 0, 0); + desk->m_thread->wait(); + delete desk->m_thread; + delete desk; + } + m_desks.clear(); + m_activeDesk = NULL; + m_activeDeskName = ""; +} + +void +CMSWindowsDesks::checkDesk() +{ + // get current desktop. if we already know about it then return. + CDesk* desk; + HDESK hdesk = openInputDesktop(); + CString name = getDesktopName(hdesk); + CDesks::const_iterator index = m_desks.find(name); + if (index == m_desks.end()) { + desk = addDesk(name, hdesk); + // hold on to hdesk until thread exits so the desk can't + // be removed by the system + } + else { + closeDesktop(hdesk); + desk = index->second; + } + + // if active desktop changed then tell the old and new desk threads + // about the change. don't switch desktops when the screensaver is + // active becaue we'd most likely switch to the screensaver desktop + // which would have the side effect of forcing the screensaver to + // stop. + if (name != m_activeDeskName && !m_screensaver->isActive()) { + // show cursor on previous desk + bool wasOnScreen = m_isOnScreen; + if (!wasOnScreen) { + sendMessage(SYNERGY_MSG_ENTER, 0, 0); + } + + // check for desk accessibility change. we don't get events + // from an inaccessible desktop so when we switch from an + // inaccessible desktop to an accessible one we have to + // update the keyboard state. + LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str())); + bool isAccessible = isDeskAccessible(desk); + if (isDeskAccessible(m_activeDesk) != isAccessible) { + if (isAccessible) { + LOG((CLOG_DEBUG "desktop is now accessible")); + sendMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); + } + else { + LOG((CLOG_DEBUG "desktop is now inaccessible")); + } + } + + // switch desk + m_activeDesk = desk; + m_activeDeskName = name; + sendMessage(SYNERGY_MSG_SWITCH, 0, 0); + + // hide cursor on new desk + if (!wasOnScreen) { + sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0); + } + } +} + +bool +CMSWindowsDesks::isDeskAccessible(const CDesk* desk) const +{ + return (desk != NULL && desk->m_desk != NULL); +} + +void +CMSWindowsDesks::waitForDesk() const +{ + CMSWindowsDesks* self = const_cast(this); + + CLock lock(&m_mutex); + while (!(bool)m_deskReady) { + m_deskReady.wait(); + } + self->m_deskReady = false; +} + +void +CMSWindowsDesks::handleCheckDesk(const CEvent&, void*) +{ + checkDesk(); +} + +HDESK +CMSWindowsDesks::openInputDesktop() +{ + if (m_is95Family) { + // there's only one desktop on windows 95 et al. + return GetThreadDesktop(GetCurrentThreadId()); + } + else { + return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, + DESKTOP_CREATEWINDOW | + DESKTOP_HOOKCONTROL | + GENERIC_WRITE); + } +} + +void +CMSWindowsDesks::closeDesktop(HDESK desk) +{ + // on 95/98/me we don't need to close the desktop returned by + // openInputDesktop(). + if (desk != NULL && !m_is95Family) { + CloseDesktop(desk); + } +} + +CString +CMSWindowsDesks::getDesktopName(HDESK desk) +{ + if (desk == NULL) { + return CString(); + } + else if (m_is95Family) { + return "desktop"; + } + else { + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR)); + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + CString result(name); + return result; + } +} diff --git a/lib/platform/CMSWindowsDesks.h b/lib/platform/CMSWindowsDesks.h new file mode 100644 index 00000000..774fad5d --- /dev/null +++ b/lib/platform/CMSWindowsDesks.h @@ -0,0 +1,252 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSDESKS_H +#define CMSWINDOWSDESKS_H + +#include "CSynergyHook.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CCondVar.h" +#include "CMutex.h" +#include "CString.h" +#include "stdmap.h" +#define WIN32_LEAN_AND_MEAN +#include + +class CEvent; +class CEventQueueTimer; +class CThread; +class IJob; +class IScreenSaver; + +//! Microsoft Windows desk handling +/*! +Desks in Microsoft Windows are only remotely like desktops on X11 +systems. A desk is another virtual surface for windows but desks +impose serious restrictions: a thread can interact with only one +desk at a time, you can't switch desks if the thread has any hooks +installed or owns any windows, windows cannot exist on multiple +desks at once, etc. Basically, they're useless except for running +the login window or the screensaver, which is what they're used +for. Synergy must deal with them mainly because of the login +window and screensaver but users can create their own desks and +synergy should work on those too. + +This class encapsulates all the desk nastiness. Clients of this +object don't have to know anything about desks. +*/ +class CMSWindowsDesks { +public: + //! Constructor + /*! + \p isPrimary is true iff the desk is for a primary screen. + \p screensaver points to a screensaver object and it's used + only to check if the screensaver is active. The \p updateKeys + job is adopted and is called when the key state should be + updated in a thread attached to the current desk. + \p hookLibrary must be a handle to the hook library. + */ + CMSWindowsDesks(bool isPrimary, HINSTANCE hookLibrary, + const IScreenSaver* screensaver, IJob* updateKeys); + ~CMSWindowsDesks(); + + //! @name manipulators + //@{ + + //! Enable desk tracking + /*! + Enables desk tracking. While enabled, this object checks to see + if the desk has changed and ensures that the hooks are installed + on the new desk. \c setShape should be called at least once + before calling \c enable. + */ + void enable(); + + //! Disable desk tracking + /*! + Disables desk tracking. \sa enable. + */ + void disable(); + + //! Notify of entering a desk + /*! + Prepares a desk for when the cursor enters it. + */ + void enter(); + + //! Notify of leaving a desk + /*! + Prepares a desk for when the cursor leaves it. + */ + void leave(HKL keyLayout); + + //! Update the key state + /*! + Causes the key state to get updated to reflect the physical keyboard + state and current keyboard mapping. + */ + void updateKeys(); + + //! Tell desk about new size + /*! + This tells the desks that the display size has changed. + */ + void setShape(SInt32 x, SInt32 y, + SInt32 width, SInt32 height, + SInt32 xCenter, SInt32 yCenter, bool isMultimon); + + //! Install/uninstall screensaver hooks + /*! + If \p install is true then the screensaver hooks are installed and, + if desk tracking is enabled, updated whenever the desk changes. If + \p install is false then the screensaver hooks are uninstalled. + */ + void installScreensaverHooks(bool install); + + //@} + //! @name accessors + //@{ + + //! Get cursor position + /*! + Return the current position of the cursor in \c x and \c y. + */ + void getCursorPos(SInt32& x, SInt32& y) const; + + //! Fake key press/release + /*! + Synthesize a press or release of key \c button. + */ + void fakeKeyEvent(KeyButton button, UINT virtualKey, + bool press, bool isAutoRepeat) const; + + //! Fake mouse press/release + /*! + Synthesize a press or release of mouse button \c id. + */ + void fakeMouseButton(ButtonID id, bool press) const; + + //! Fake mouse move + /*! + Synthesize a mouse move to the absolute coordinates \c x,y. + */ + void fakeMouseMove(SInt32 x, SInt32 y) const; + + //! Fake mouse wheel + /*! + Synthesize a mouse wheel event of amount \c delta. + */ + void fakeMouseWheel(SInt32 delta) const; + + //@} + +private: + class CDesk { + public: + CString m_name; + CThread* m_thread; + DWORD m_threadID; + DWORD m_targetID; + HDESK m_desk; + HWND m_window; + bool m_lowLevel; + }; + typedef std::map CDesks; + + // initialization and shutdown operations + void queryHookLibrary(HINSTANCE hookLibrary); + HCURSOR createBlankCursor() const; + void destroyCursor(HCURSOR cursor) const; + ATOM createDeskWindowClass(bool isPrimary) const; + void destroyClass(ATOM windowClass) const; + HWND createWindow(ATOM windowClass, const char* name) const; + void destroyWindow(HWND) const; + + // message handlers + void deskMouseMove(SInt32 x, SInt32 y) const; + void deskEnter(CDesk* desk); + void deskLeave(CDesk* desk, HKL keyLayout); + void deskThread(void* vdesk); + + // desk switch checking and handling + CDesk* addDesk(const CString& name, HDESK hdesk); + void removeDesks(); + void checkDesk(); + bool isDeskAccessible(const CDesk* desk) const; + void handleCheckDesk(const CEvent& event, void*); + + // communication with desk threads + void waitForDesk() const; + void sendMessage(UINT, WPARAM, LPARAM) const; + + // desk API wrappers + HDESK openInputDesktop(); + void closeDesktop(HDESK); + CString getDesktopName(HDESK); + + // our desk window procs + static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM); + +private: + // true if screen is being used as a primary screen, false otherwise + bool m_isPrimary; + + // true if windows 95/98/me + bool m_is95Family; + + // true if mouse has entered the screen + bool m_isOnScreen; + + // our resources + ATOM m_deskClass; + HCURSOR m_cursor; + + // screen shape stuff + SInt32 m_x, m_y; + SInt32 m_w, m_h; + SInt32 m_xCenter, m_yCenter; + + // true if system appears to have multiple monitors + bool m_multimon; + + // the timer used to check for desktop switching + CEventQueueTimer* m_timer; + + // screen saver stuff + const IScreenSaver* m_screensaver; + bool m_screensaverNotify; + + // the current desk and it's name + CDesk* m_activeDesk; + CString m_activeDeskName; + + // one desk per desktop and a cond var to communicate with it + CMutex m_mutex; + CCondVar m_deskReady; + CDesks m_desks; + + // hook library stuff + InstallFunc m_install; + UninstallFunc m_uninstall; + InstallScreenSaverFunc m_installScreensaver; + UninstallScreenSaverFunc m_uninstallScreensaver; + + // keyboard stuff + IJob* m_updateKeys; + HKL m_keyLayout; +}; + +#endif diff --git a/lib/platform/CMSWindowsDesktop.cpp b/lib/platform/CMSWindowsDesktop.cpp deleted file mode 100644 index f62b6b1b..00000000 --- a/lib/platform/CMSWindowsDesktop.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CMSWindowsDesktop.h" -#include "CLog.h" -#include "CArchMiscWindows.h" -#include - -// -// CMSWindowsDesktop -// - -HDESK -CMSWindowsDesktop::openInputDesktop() -{ - if (CArchMiscWindows::isWindows95Family()) { - // there's only one desktop on windows 95 et al. - return GetThreadDesktop(GetCurrentThreadId()); - } - else { - return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, - DESKTOP_CREATEWINDOW | - DESKTOP_HOOKCONTROL | - GENERIC_WRITE); - } -} - -void -CMSWindowsDesktop::closeDesktop(HDESK desk) -{ - // on 95/98/me we don't need to close the desktop returned by - // openInputDesktop(). - if (desk != NULL && !CArchMiscWindows::isWindows95Family()) { - CloseDesktop(desk); - } -} - -bool -CMSWindowsDesktop::setDesktop(HDESK desk) -{ - // 95/98/me doesn't support multiple desktops so just return - // true on those platforms. - return (CArchMiscWindows::isWindows95Family() || - SetThreadDesktop(desk) != 0); -} - -CString -CMSWindowsDesktop::getDesktopName(HDESK desk) -{ - if (desk == NULL) { - return CString(); - } - else if (CArchMiscWindows::isWindows95Family()) { - return "desktop"; - } - else { - DWORD size; - GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); - TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR)); - GetUserObjectInformation(desk, UOI_NAME, name, size, &size); - CString result(name); - return result; - } -} diff --git a/lib/platform/CMSWindowsDesktop.h b/lib/platform/CMSWindowsDesktop.h deleted file mode 100644 index 908f5c87..00000000 --- a/lib/platform/CMSWindowsDesktop.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CMSWINDOWSDESKTOP_H -#define CMSWINDOWSDESKTOP_H - -#include "CString.h" -#include - -//! Encapsulate Microsoft Windows desktop -class CMSWindowsDesktop { -public: - //! Open the desktop - /*! - Opens the desktop named \p name. The caller must close the desktop. - */ - static HDESK openDesktop(const CString& name); - - //! Open the input desktop - /*! - Opens the input desktop. The caller must close the desktop. - */ - static HDESK openInputDesktop(); - - //! Close a desktop - /*! - Closes the given desktop. - */ - static void closeDesktop(HDESK); - - //! Change current desktop - /*! - Changes the calling thread's desktop, return true iff successful. - The call will fail if the calling thread has any windows or a hooks - on the current desktop. - */ - static bool setDesktop(HDESK); - - //! Get the desktop's name. - /*! - Returns the current desktop's name. Returns a constant string - on 95/98/Me. - */ - static CString getDesktopName(HDESK); -}; - -#endif diff --git a/lib/platform/CMSWindowsKeyMapper.h b/lib/platform/CMSWindowsKeyMapper.h deleted file mode 100644 index 883c5c60..00000000 --- a/lib/platform/CMSWindowsKeyMapper.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2003 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef CMSWINDOWSKEYMAPPER_H -#define CMSWINDOWSKEYMAPPER_H - -#include "IKeyState.h" -#include "CString.h" -#define WIN32_LEAN_AND_MEAN -#include - -//! Microsoft Windows key mapper -/*! -This class maps KeyIDs to keystrokes. -*/ -class CMSWindowsKeyMapper { -public: - CMSWindowsKeyMapper(); - ~CMSWindowsKeyMapper(); - - //! @name manipulators - //@{ - - //! Update key mapper - /*! - Updates the key mapper's internal tables according to the - current keyboard mapping and updates \c keyState. - */ - void update(IKeyState* keyState); - - //! Update shadow key state - /*! - Updates the shadow keyboard state. - */ - void updateKey(LPARAM eventLParam); - - //! Set the active keyboard layout - /*! - Uses \p keyLayout when finding scan codes via \c keyToScanCode() - and mapping keys in \c mapKeyFromEvent(). - */ - void setKeyLayout(HKL keyLayout); - - //@} - //! @name accessors - //@{ - - //! Map key press/repeat to keystrokes - /*! - Converts a press/repeat of key \c id with the modifiers as given - in \c desiredMask into the keystrokes necessary to synthesize - that key event. Returns the platform specific code of the key - being pressed, or 0 if the key cannot be mapped or \c isAutoRepeat - is true and the key does not auto-repeat. - */ - KeyButton mapKey(IKeyState::Keystrokes&, - const IKeyState& keyState, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const; - - //! Map key event to a key - /*! - Converts a key event into a KeyID and the shadow modifier state - to a modifier mask. If \c altgr is non-NULL it's set to true if - the key requires AltGr and false otherwise. - */ - KeyID mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, - KeyModifierMask* maskOut, bool* altgr) const; - - //! Check if virtual key is a modifier - /*! - Returns true iff \p virtKey refers to a modifier key. - */ - bool isModifier(UINT virtKey) const; - - //! Test shadow key state - /*! - Returns true iff the shadow state indicates the key is pressed. - */ - bool isPressed(UINT virtKey) const; - - //! Map button to a virtual key - /*! - Returns the virtual key for \c button. - */ - UINT buttonToVirtualKey(KeyButton button) const; - - //! Map virtual key to a button - /*! - Returns the button for virtual key \c virtKey. - */ - KeyButton virtualKeyToButton(UINT virtKey) const; - - //! Check for extended key - /*! - Returns true iff \c key is an extended key - */ - bool isExtendedKey(KeyButton button) const; - - //! Get current modifier key state - /*! - Returns the current modifier key state. - */ - KeyModifierMask getActiveModifiers() const; - - //! Get name of key - /*! - Return a string describing the given key. - */ - const char* getKeyName(KeyButton) const; - - //@} - -private: - // convert a language ID to a code page - UINT getCodePageFromLangID(LANGID langid) const; - - // map character \c c given keyboard layout \c hkl to the keystrokes - // to generate it. - KeyButton mapCharacter(IKeyState::Keystrokes& keys, - const IKeyState& keyState, char c, HKL hkl, - bool isAutoRepeat) const; - - // map \c virtualKey to the keystrokes to generate it, along with - // keystrokes to update and restore the modifier state. - KeyButton mapToKeystrokes(IKeyState::Keystrokes& keys, - const IKeyState& keyState, KeyButton button, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask, - bool isAutoRepeat) const; - - // get keystrokes to get modifiers in a desired state - bool adjustModifiers(IKeyState::Keystrokes& keys, - IKeyState::Keystrokes& undo, - const IKeyState& keyState, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask) const; - - //! Test shadow key toggle state - /*! - Returns true iff the shadow state indicates the key is toggled on. - */ - bool isToggled(UINT virtKey) const; - - //! Get shadow modifier key state - /*! - Returns the shadow modifier key state. - */ - KeyModifierMask getShadowModifiers(bool needAltGr) const; - - // pass character to ToAsciiEx(), returning what it returns - int toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const; - - // return true iff \c c is a dead character - bool isDeadChar(TCHAR c, HKL hkl, bool menu) const; - -private: - class CModifierKeys { - public: - enum { s_maxKeys = 2 }; - KeyModifierMask m_mask; - KeyButton m_keys[s_maxKeys]; - }; - - // map of key state for each scan code. this would be 8 bits - // except windows reuses some scan codes for "extended" keys - // we actually need 9 bits. an example is the left and right - // alt keys; they share the same scan code but the right key - // is "extended". - BYTE m_keys[512]; - UINT m_scanCodeToVirtKey[512]; - KeyButton m_virtKeyToScanCode[256]; - mutable TCHAR m_deadKey; - HKL m_keyLayout; - CString m_keyName; - - static const CModifierKeys s_modifiers[]; - static const char* s_vkToName[]; - static const KeyID s_virtualKey[][2]; - static const UINT s_mapE000[]; - static const UINT s_mapEE00[]; - static const UINT s_mapEF00[]; -}; - -#endif diff --git a/lib/platform/CMSWindowsKeyMapper.cpp b/lib/platform/CMSWindowsKeyState.cpp similarity index 72% rename from lib/platform/CMSWindowsKeyMapper.cpp rename to lib/platform/CMSWindowsKeyState.cpp index b07da1fd..1d9554d7 100644 --- a/lib/platform/CMSWindowsKeyMapper.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -12,9 +12,19 @@ * GNU General Public License for more details. */ -#include "CMSWindowsKeyMapper.h" +#include "CMSWindowsKeyState.h" +#include "CMSWindowsDesks.h" +#include "CThread.h" +#include "CFunctionJob.h" #include "CLog.h" #include "CStringUtil.h" +#include "CArchMiscWindows.h" + +// extended mouse buttons +#if !defined(VK_XBUTTON1) +#define VK_XBUTTON1 0x05 +#define VK_XBUTTON2 0x06 +#endif // multimedia keys #if !defined(VK_BROWSER_BACK) @@ -39,10 +49,10 @@ #endif // -// CMSWindowsKeyMapper +// CMSWindowsKeyState // -const char* CMSWindowsKeyMapper::s_vkToName[] = +const char* CMSWindowsKeyState::s_vkToName[] = { "vk 0x00", "Left Button", @@ -303,7 +313,7 @@ const char* CMSWindowsKeyMapper::s_vkToName[] = }; // map virtual keys to synergy key enumeration -const KeyID CMSWindowsKeyMapper::s_virtualKey[][2] = +const KeyID CMSWindowsKeyState::s_virtualKey[][2] = { /* 0x00 */ kKeyNone, kKeyNone, // reserved /* 0x01 */ kKeyNone, kKeyNone, // VK_LBUTTON @@ -564,7 +574,7 @@ const KeyID CMSWindowsKeyMapper::s_virtualKey[][2] = }; // map special KeyID keys to virtual key codes -const UINT CMSWindowsKeyMapper::s_mapE000[] = +const UINT CMSWindowsKeyState::s_mapE000[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -606,7 +616,7 @@ const UINT CMSWindowsKeyMapper::s_mapE000[] = /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 }; -const UINT CMSWindowsKeyMapper::s_mapEE00[] = +const UINT CMSWindowsKeyState::s_mapEE00[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -644,7 +654,7 @@ const UINT CMSWindowsKeyMapper::s_mapEE00[] = /* in g_mapEF00, 0xac is VK_DECIMAL not VK_SEPARATOR because win32 * doesn't seem to use VK_SEPARATOR but instead maps VK_DECIMAL to * the same meaning. */ -const UINT CMSWindowsKeyMapper::s_mapEF00[] = +const UINT CMSWindowsKeyState::s_mapEF00[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, @@ -687,60 +697,405 @@ const UINT CMSWindowsKeyMapper::s_mapEF00[] = /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE }; -CMSWindowsKeyMapper::CMSWindowsKeyMapper() : m_deadKey(0) +CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks) : + m_is95Family(CArchMiscWindows::isWindows95Family()), + m_desks(desks), + m_keyLayout(GetKeyboardLayout(0)) { - m_keyLayout = GetKeyboardLayout(0); } -CMSWindowsKeyMapper::~CMSWindowsKeyMapper() +CMSWindowsKeyState::~CMSWindowsKeyState() { // do nothing } void -CMSWindowsKeyMapper::update(IKeyState* keyState) +CMSWindowsKeyState::setKeyLayout(HKL keyLayout) { - // clear shadow state - memset(m_keys, 0, sizeof(m_keys)); + m_keyLayout = keyLayout; +} +void +CMSWindowsKeyState::fixKey(void* target, UINT virtualKey) +{ + // check if virtualKey is up but we think it's down. if so then + // synthesize a key release for it. + // + // we use GetAsyncKeyState() to check the state of the keys even + // though we might not be in sync with that yet. + KeyButton button = m_virtKeyToScanCode[virtualKey]; + if (isKeyDown(button) && (GetAsyncKeyState(virtualKey) & 0x8000) == 0) { + // compute appropriate parameters for fake event + LPARAM lParam = 0xc0000000 | ((LPARAM)button << 16); + + // process as if it were a key up + KeyModifierMask mask; + KeyID key = mapKeyFromEvent(virtualKey, lParam, &mask); + LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); + CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button); + CKeyState::setKeyDown(button, false); + } +} + +KeyID +CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey, + LPARAM info, KeyModifierMask* maskOut) const +{ + // note: known microsoft bugs + // Q72583 -- MapVirtualKey() maps keypad keys incorrectly + // 95,98: num pad vk code -> invalid scan code + // 95,98,NT4: num pad scan code -> bad vk code except + // SEPARATOR, MULTIPLY, SUBTRACT, ADD + + // extract character and virtual key + char c = (char)((charAndVirtKey & 0xff00u) >> 8); + UINT vkCode = (charAndVirtKey & 0xffu); + + // handle some keys via table lookup + int extended = ((info >> 24) & 1); + KeyID id = s_virtualKey[vkCode][extended]; + + // check if not in table; map character to key id + if (id == kKeyNone && c != 0) { + if ((c & 0x80u) == 0) { + // ASCII + id = static_cast(c) & 0xffu; + } + else { + // character is not really ASCII. instead it's some + // character in the current ANSI code page. try to + // convert that to a Unicode character. if we fail + // then use the single byte character as is. + char src = c; + wchar_t unicode; + if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, + &src, 1, &unicode, 1) > 0) { + id = static_cast(unicode); + } + else { + id = static_cast(c) & 0xffu; + } + } + } + + // set mask + KeyModifierMask activeMask = getActiveModifiers(); + bool needAltGr = false; + if (id != kKeyNone && c != 0) { + // note if key requires AltGr. VkKeyScan() can have a problem + // with some characters. there are two problems in particular. + // first, typing a dead key then pressing space will cause + // VkKeyScan() to return 0xffff. second, certain characters + // may map to multiple virtual keys and we might get the wrong + // one. if that happens then we might not get the right + // modifier mask. AltGr+9 on the french keyboard layout (^) + // has this problem. in the first case, we'll assume AltGr is + // required (only because it solves the problems we've seen + // so far). in the second, we'll use whatever the keyboard + // state says. + WORD virtualKeyAndModifierState = VkKeyScanEx(c, m_keyLayout); + if (virtualKeyAndModifierState == 0xffff) { + // there is no mapping. assume AltGr. + LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); + needAltGr = true; + } + else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { + // we didn't get the key that was actually pressed + LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); + if ((activeMask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + needAltGr = true; + } + } + else { + BYTE modifierState = HIBYTE(virtualKeyAndModifierState); + if ((modifierState & 6) == 6) { + // key requires ctrl and alt == AltGr + needAltGr = true; + } + } + } + + // map modifier key + if (maskOut != NULL) { + if (needAltGr) { + activeMask |= KeyModifierModeSwitch; + activeMask &= ~(KeyModifierControl | KeyModifierAlt); + } + else { + activeMask &= ~KeyModifierModeSwitch; + } + *maskOut = activeMask; + } + + return id; +} + +KeyButton +CMSWindowsKeyState::virtualKeyToButton(UINT virtualKey) const +{ + return m_virtKeyToScanCode[virtualKey & 0xffu]; +} + +void +CMSWindowsKeyState::setKeyDown(KeyButton button, bool down) +{ + CKeyState::setKeyDown(button, down); + + // special case: we detect ctrl+alt+del being pressed on some + // systems but we don't detect the release of those keys. so + // if ctrl, alt, and del are down then mark them up. + if (down && isKeyDown(m_virtKeyToScanCode[VK_DELETE])) { + KeyModifierMask mask = getActiveModifiers(); + if ((mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)) { + CKeyState::setKeyDown(m_virtKeyToScanCode[VK_LCONTROL], false); + CKeyState::setKeyDown(m_virtKeyToScanCode[VK_RCONTROL], false); + CKeyState::setKeyDown(m_virtKeyToScanCode[VK_LMENU], false); + CKeyState::setKeyDown(m_virtKeyToScanCode[VK_RMENU], false); + CKeyState::setKeyDown(m_virtKeyToScanCode[VK_DELETE], false); + } + } +} + +void +CMSWindowsKeyState::sendKeyEvent(void* target, + bool press, bool isAutoRepeat, + KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + if (press || isAutoRepeat) { + // if AltGr required for this key then make sure + // the ctrl and alt keys are *not* down on the + // client. windows simulates AltGr with ctrl and + // alt for some inexplicable reason and clients + // will get confused if they see mode switch and + // ctrl and alt. we'll also need to put ctrl and + // alt back the way they were after we simulate + // the key. + bool ctrlL = isKeyDown(m_virtKeyToScanCode[VK_LCONTROL]); + bool ctrlR = isKeyDown(m_virtKeyToScanCode[VK_RCONTROL]); + bool altL = isKeyDown(m_virtKeyToScanCode[VK_LMENU]); + bool altR = isKeyDown(m_virtKeyToScanCode[VK_RMENU]); + if ((mask & KeyModifierModeSwitch) != 0) { + KeyModifierMask mask2 = (mask & + ~(KeyModifierControl | + KeyModifierAlt | + KeyModifierModeSwitch)); + if (ctrlL) { + CKeyState::sendKeyEvent(target, false, false, + kKeyControl_L, mask2, 1, + m_virtKeyToScanCode[VK_LCONTROL]); + } + if (ctrlR) { + CKeyState::sendKeyEvent(target, false, false, + kKeyControl_R, mask2, 1, + m_virtKeyToScanCode[VK_RCONTROL]); + } + if (altL) { + CKeyState::sendKeyEvent(target, false, false, + kKeyAlt_L, mask2, 1, + m_virtKeyToScanCode[VK_LMENU]); + } + if (altR) { + CKeyState::sendKeyEvent(target, false, false, + kKeyAlt_R, mask2, 1, + m_virtKeyToScanCode[VK_RMENU]); + } + } + + // send key + if (press) { + CKeyState::sendKeyEvent(target, true, false, + key, mask, 1, button); + if (count > 0) { + --count; + } + } + if (count >= 1) { + CKeyState::sendKeyEvent(target, true, true, + key, mask, count, button); + } + + // restore ctrl and alt state + if ((mask & KeyModifierModeSwitch) != 0) { + KeyModifierMask mask2 = (mask & + ~(KeyModifierControl | + KeyModifierAlt | + KeyModifierModeSwitch)); + if (ctrlL) { + CKeyState::sendKeyEvent(target, true, false, + kKeyControl_L, mask2, 1, + m_virtKeyToScanCode[VK_LCONTROL]); + mask2 |= KeyModifierControl; + } + if (ctrlR) { + CKeyState::sendKeyEvent(target, true, false, + kKeyControl_R, mask2, 1, + m_virtKeyToScanCode[VK_RCONTROL]); + mask2 |= KeyModifierControl; + } + if (altL) { + CKeyState::sendKeyEvent(target, true, false, + kKeyAlt_L, mask2, 1, + m_virtKeyToScanCode[VK_LMENU]); + mask2 |= KeyModifierAlt; + } + if (altR) { + CKeyState::sendKeyEvent(target, true, false, + kKeyAlt_R, mask2, 1, + m_virtKeyToScanCode[VK_RMENU]); + mask2 |= KeyModifierAlt; + } + } + } + else { + // key release. if the key isn't down according to + // our table then we never got the key press event + // for it. if it's not a modifier key then we'll + // synthesize the press first. only do this on + // the windows 95 family, which eats certain special + // keys like alt+tab, ctrl+esc, etc. + if (m_is95Family && isKeyDown(button)) { + switch (m_scanCodeToVirtKey[button]) { + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + case VK_LWIN: + case VK_RWIN: + break; + + default: + CKeyState::sendKeyEvent(target, + true, false, key, mask, 1, button); + break; + } + } + + // do key up + CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button); + } +} + +bool +CMSWindowsKeyState::fakeCtrlAltDel() +{ + if (!m_is95Family) { + // to fake ctrl+alt+del on the NT family we broadcast a suitable + // hotkey to all windows on the winlogon desktop. however, the + // current thread must be on that desktop to do the broadcast + // and we can't switch just any thread because some own windows + // or hooks. so start a new thread to do the real work. + CThread cad(new CFunctionJob(&CMSWindowsKeyState::ctrlAltDelThread)); + cad.wait(); + } + else { + // simulate ctrl+alt+del + fakeKeyDown(kKeyDelete, KeyModifierControl | KeyModifierAlt, + m_virtKeyToScanCode[VK_DELETE]); + } + return true; +} + +void +CMSWindowsKeyState::ctrlAltDelThread(void*) +{ + // get the Winlogon desktop at whatever privilege we can + HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED); + if (desk != NULL) { + if (SetThreadDesktop(desk)) { + PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, + MAKELPARAM(MOD_CONTROL | MOD_ALT, VK_DELETE)); + } + else { + LOG((CLOG_DEBUG "can't switch to Winlogon desk: %d", GetLastError())); + } + CloseDesktop(desk); + } + else { + LOG((CLOG_DEBUG "can't open Winlogon desk: %d", GetLastError())); + } +} + +const char* +CMSWindowsKeyState::getKeyName(KeyButton button) const +{ + char keyName[100]; + char keyName2[100]; + CMSWindowsKeyState* self = const_cast(this); + if (GetKeyNameText(button << 16, keyName, sizeof(keyName)) != 0) { + // get the extended name of the key if button is not extended + // or vice versa. if the names are different then report both. + button ^= 0x100u; + if (GetKeyNameText(button << 16, keyName2, sizeof(keyName2)) != 0 && + strcmp(keyName, keyName2) != 0) { + self->m_keyName = CStringUtil::print("%s or %s", keyName, keyName2); + } + else { + self->m_keyName = keyName; + } + } + else if (m_scanCodeToVirtKey[button] != 0) { + self->m_keyName = s_vkToName[m_scanCodeToVirtKey[button]]; + } + else { + self->m_keyName = CStringUtil::print("scan code 0x%03x", button); + } + return m_keyName.c_str(); +} + +void +CMSWindowsKeyState::doUpdateKeys() +{ // clear scan code to/from virtual key mapping memset(m_scanCodeToVirtKey, 0, sizeof(m_scanCodeToVirtKey)); memset(m_virtKeyToScanCode, 0, sizeof(m_virtKeyToScanCode)); - // add modifiers. note that VK_RMENU shows up under the Alt key - // and ModeSwitch. when simulating AltGr we need to use the right - // alt key so we use KeyModifierModeSwitch to get it. - if (keyState != NULL) { - IKeyState::KeyButtons keys; - keys.push_back((KeyButton)MapVirtualKey(VK_LSHIFT, 0)); - keys.push_back((KeyButton)MapVirtualKey(VK_RSHIFT, 0)); - keyState->addModifier(KeyModifierShift, keys); - keys.clear(); - keys.push_back((KeyButton)MapVirtualKey(VK_LCONTROL, 0)); - keys.push_back((KeyButton)(MapVirtualKey(VK_RCONTROL, 0) | 0x100)); - keyState->addModifier(KeyModifierControl, keys); - keys.clear(); - keys.push_back((KeyButton)MapVirtualKey(VK_LMENU, 0)); - keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100)); - keyState->addModifier(KeyModifierAlt, keys); - keys.clear(); - keys.push_back((KeyButton)(MapVirtualKey(VK_LWIN, 0) | 0x100)); - keys.push_back((KeyButton)(MapVirtualKey(VK_RWIN, 0) | 0x100)); - keyState->addModifier(KeyModifierSuper, keys); - keys.clear(); - keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100)); - keyState->addModifier(KeyModifierModeSwitch, keys); - keys.clear(); - keys.push_back((KeyButton)MapVirtualKey(VK_CAPITAL, 0)); - keyState->addModifier(KeyModifierCapsLock, keys); - keys.clear(); - keys.push_back((KeyButton)(MapVirtualKey(VK_NUMLOCK, 0) | 0x100)); - keyState->addModifier(KeyModifierNumLock, keys); - keys.clear(); - keys.push_back((KeyButton)MapVirtualKey(VK_SCROLL, 0)); - keyState->addModifier(KeyModifierScrollLock, keys); - keys.clear(); - } + // add modifiers. note that ModeSwitch is mapped to VK_RMENU and + // that it's mapped *before* the Alt modifier. we must map it so + // KeyModifierModeSwitch mask can be converted to keystrokes. it + // must be mapped before the Alt modifier so that the Alt modifier + // takes precedence when mapping keystrokes to modifier masks. + // + // we have to explicitly set the extended key flag for some + // modifiers because the win32 API is inadequate. + KeyButtons keys; + keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100)); + addModifier(KeyModifierModeSwitch, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_LSHIFT, 0)); + keys.push_back((KeyButton)(MapVirtualKey(VK_RSHIFT, 0) | 0x100)); + addModifier(KeyModifierShift, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_LCONTROL, 0)); + keys.push_back((KeyButton)(MapVirtualKey(VK_RCONTROL, 0) | 0x100)); + addModifier(KeyModifierControl, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_LMENU, 0)); + keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100)); + addModifier(KeyModifierAlt, keys); + keys.clear(); + keys.push_back((KeyButton)(MapVirtualKey(VK_LWIN, 0) | 0x100)); + keys.push_back((KeyButton)(MapVirtualKey(VK_RWIN, 0) | 0x100)); + addModifier(KeyModifierSuper, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_CAPITAL, 0)); + addModifier(KeyModifierCapsLock, keys); + keys.clear(); + keys.push_back((KeyButton)(MapVirtualKey(VK_NUMLOCK, 0) | 0x100)); + addModifier(KeyModifierNumLock, keys); + keys.clear(); + keys.push_back((KeyButton)MapVirtualKey(VK_SCROLL, 0)); + addModifier(KeyModifierScrollLock, keys); /* FIXME -- potential problem here on win me // win me (sony vaio laptop): @@ -751,18 +1106,30 @@ CMSWindowsKeyMapper::update(IKeyState* keyState) // MapVirtualKey(sc, 3): // all scan codes unmapped (function apparently unimplemented) */ - BYTE keys[256]; - GetKeyboardState(keys); + BYTE keyState[256]; + GetKeyboardState(keyState); for (UINT i = 1; i < 256; ++i) { - // skip certain virtual keys (the ones for the mouse buttons) - if (i < VK_BACK && i != VK_CANCEL) { + // skip mouse button virtual keys + switch (i) { + case VK_LBUTTON: + case VK_RBUTTON: + case VK_MBUTTON: + case VK_XBUTTON1: + case VK_XBUTTON2: continue; + + default: + break; } // map to a scancode and back to a virtual key UINT scancode = MapVirtualKey(i, 0); UINT virtKey = MapVirtualKey(scancode, 3); - if (scancode == 0 || virtKey == 0) { + if (virtKey == 0) { + // assume MapVirtualKey(xxx, 3) is unimplemented + virtKey = i; + } + else if (scancode == 0) { // the VK_PAUSE virtual key doesn't map properly if (i == VK_PAUSE) { // i hope this works on all keyboards @@ -775,6 +1142,25 @@ CMSWindowsKeyMapper::update(IKeyState* keyState) } // we need some adjustments due to inadequacies in the API. + // the API provides no means to query the keyboard by scan + // code that i can see. so we're doing it by virtual key. + // but a single virtual key can map to multiple physical + // keys. for example, VK_HOME maps to NumPad 7 and to the + // (extended key) Home key. this means we can never tell + // which of the two keys is pressed. + // + // this is a problem if a key is down when this method is + // called. if the extended key is down we'll record the + // non-extended key as being down. when the extended key + // goes up, we'll record that correctly and leave the + // non-extended key as being down. to deal with that we + // always re-check the keyboard state if we think we're + // locked to a screen because a key is down. the re-check + // should clear it up. + // + // the win32 functions that take scan codes are: + + // // if the mapped virtual key doesn't match the starting // point then there's a really good chance that that virtual // key is mapped to an extended key. however, this is not @@ -788,36 +1174,28 @@ CMSWindowsKeyMapper::update(IKeyState* keyState) } // okay, now we have the scan code for the virtual key. - // save the key state. m_scanCodeToVirtKey[scancode] = i; m_virtKeyToScanCode[i] = (KeyButton)scancode; - m_keys[scancode] = (BYTE)(keys[i] & 0x80); - if (keyState != NULL) { - keyState->setKeyDown((KeyButton)scancode, (keys[i] & 0x80) != 0); + + // save the key state + if ((keyState[i] & 0x80) != 0) { + setKeyDown((KeyButton)scancode, true); } + // toggle state applies to all keys but we only want it for // the modifier keys with corresponding lights. - if ((keys[i] & 0x01) != 0) { + if ((keyState[i] & 0x01) != 0) { switch (i) { case VK_CAPITAL: - m_keys[scancode] |= 0x01; - if (keyState != NULL) { - keyState->setToggled(KeyModifierCapsLock); - } + setToggled(KeyModifierCapsLock); break; case VK_NUMLOCK: - m_keys[scancode] |= 0x01; - if (keyState != NULL) { - keyState->setToggled(KeyModifierNumLock); - } + setToggled(KeyModifierNumLock); break; case VK_SCROLL: - m_keys[scancode] |= 0x01; - if (keyState != NULL) { - keyState->setToggled(KeyModifierScrollLock); - } + setToggled(KeyModifierScrollLock); break; } } @@ -825,58 +1203,15 @@ CMSWindowsKeyMapper::update(IKeyState* keyState) } void -CMSWindowsKeyMapper::updateKey(LPARAM eventLParam) +CMSWindowsKeyState::doFakeKeyEvent(KeyButton button, + bool press, bool isAutoRepeat) { - bool pressed = ((eventLParam & 0x80000000u) == 0); - UINT scanCode = ((eventLParam & 0x01ff0000u) >> 16); - UINT virtKey = m_scanCodeToVirtKey[scanCode]; - if (virtKey == 0) { - // unmapped key - return; - } - - if (pressed) { - m_keys[scanCode] |= 0x80; - - // special case: we detect ctrl+alt+del being pressed on some - // systems but we don't detect the release of those keys. so - // if ctrl, alt, and del are down then mark them up. - if (isPressed(VK_CONTROL) && - isPressed(VK_MENU) && - isPressed(VK_DELETE)) { - m_keys[m_virtKeyToScanCode[VK_LCONTROL]] &= ~0x80; - m_keys[m_virtKeyToScanCode[VK_RCONTROL]] &= ~0x80; - m_keys[m_virtKeyToScanCode[VK_LMENU]] &= ~0x80; - m_keys[m_virtKeyToScanCode[VK_RMENU]] &= ~0x80; - m_keys[m_virtKeyToScanCode[VK_DELETE]] &= ~0x80; - } - } - else { - m_keys[scanCode] &= ~0x80; - - // handle toggle keys - switch (virtKey) { - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - m_keys[scanCode] ^= 0x01; - break; - - default: - break; - } - } -} - -void -CMSWindowsKeyMapper::setKeyLayout(HKL keyLayout) -{ - m_keyLayout = keyLayout; + UINT vk = m_scanCodeToVirtKey[button]; + m_desks->fakeKeyEvent(button, vk, press, isAutoRepeat); } KeyButton -CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, - const IKeyState& keyState, KeyID id, +CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, KeyModifierMask mask, bool isAutoRepeat) const { UINT virtualKey = 0; @@ -929,7 +1264,7 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, // keys but not for numeric keys. if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) { requiredMask |= KeyModifierNumLock; - if (!keyState.isModifierActive(KeyModifierNumLock)) { + if ((getActiveModifiers() & KeyModifierNumLock) != 0) { LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); outMask |= KeyModifierNumLock; } @@ -944,7 +1279,7 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, // now generate the keystrokes and return the resulting modifier mask KeyButton scanCode = m_virtKeyToScanCode[virtualKey]; LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d scan code 0x%04x mask 0x%04x", id, virtualKey, scanCode, outMask)); - return mapToKeystrokes(keys, keyState, scanCode, + return mapToKeystrokes(keys, scanCode, outMask, requiredMask, isAutoRepeat); } @@ -991,15 +1326,14 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); return 0; } - KeyButton button = mapCharacter(keys, keyState, - multiByte[0], hkl, isAutoRepeat); + KeyButton button = mapCharacter(keys, multiByte[0], hkl, isAutoRepeat); if (button != 0) { LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); if (isDeadChar(multiByte[0], hkl, false)) { // character mapped to a dead key but we want the // character for real so send a space key afterwards. LOG((CLOG_DEBUG2 "character mapped to dead key")); - IKeyState::Keystroke keystroke; + Keystroke keystroke; keystroke.m_key = m_virtKeyToScanCode[VK_SPACE]; keystroke.m_press = true; keystroke.m_repeat = false; @@ -1040,262 +1374,16 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys, } if (nChars == 2) { LOG((CLOG_DEBUG2 "KeyID 0x%08x needs dead key %u", id, (unsigned char)multiByte[1])); - mapCharacter(keys, keyState, multiByte[1], hkl, isAutoRepeat); + mapCharacter(keys, multiByte[1], hkl, isAutoRepeat); } // process character LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - return mapCharacter(keys, keyState, multiByte[0], hkl, isAutoRepeat); -} - -KeyID -CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM charAndVirtKey, - LPARAM info, KeyModifierMask* maskOut, bool* altgr) const -{ - // note: known microsoft bugs - // Q72583 -- MapVirtualKey() maps keypad keys incorrectly - // 95,98: num pad vk code -> invalid scan code - // 95,98,NT4: num pad scan code -> bad vk code except - // SEPARATOR, MULTIPLY, SUBTRACT, ADD - - char c = (char)((charAndVirtKey & 0xff00u) >> 8); - UINT vkCode = (charAndVirtKey & 0xffu); - - // get the scan code and the extended keyboard flag - UINT scanCode = static_cast((info & 0x00ff0000u) >> 16); - int extended = ((info & 0x01000000) == 0) ? 0 : 1; - LOG((CLOG_DEBUG1 "key vk=%d info=0x%08x ext=%d scan=%d", vkCode, info, extended, scanCode)); - - // handle some keys via table lookup - KeyID id = s_virtualKey[vkCode][extended]; - if (id == kKeyNone) { - // not in table; map character to key id - if (c != 0) { - if ((c & 0x80u) != 0) { - // character is not really ASCII. instead it's some - // character in the current ANSI code page. try to - // convert that to a Unicode character. if we fail - // then use the single byte character as is. - char src = c; - wchar_t unicode; - if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, - &src, 1, &unicode, 1) > 0) { - id = static_cast(unicode); - } - else { - id = static_cast(c) & 0xffu; - } - } - else { - id = static_cast(c) & 0xffu; - } - } - } - - // set mask - bool needAltGr = false; - if (id != kKeyNone && id != kKeyMultiKey && c != 0) { - // note if key requires AltGr. VkKeyScan() can have a problem - // with some characters. there are two problems in particular. - // first, typing a dead key then pressing space will cause - // VkKeyScan() to return 0xffff. second, certain characters - // may map to multiple virtual keys and we might get the wrong - // one. if that happens then we might not get the right - // modifier mask. AltGr+9 on the french keyboard layout (^) - // has this problem. in the first case, we'll assume AltGr is - // required (only because it solves the problems we've seen - // so far). in the second, we'll use whatever the keyboard - // state says. - WORD virtualKeyAndModifierState = VkKeyScanEx(c, m_keyLayout); - if (virtualKeyAndModifierState == 0xffff) { - // there is no mapping. assume AltGr. - LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); - needAltGr = true; - } - else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { - // we didn't get the key that was actually pressed - LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); - if (isPressed(VK_CONTROL) && isPressed(VK_MENU)) { - needAltGr = true; - } - } - else { - BYTE modifierState = HIBYTE(virtualKeyAndModifierState); - if ((modifierState & 6) == 6) { - // key requires ctrl and alt == AltGr - needAltGr = true; - } - } - } - if (altgr != NULL) { - *altgr = needAltGr; - } - - // map modifier key - if (maskOut != NULL) { - *maskOut = getShadowModifiers(needAltGr); - } - - return id; -} - -bool -CMSWindowsKeyMapper::isModifier(UINT virtKey) const -{ - switch (virtKey) { - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: - case VK_LMENU: - case VK_RMENU: - case VK_MENU: - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - case VK_LWIN: - case VK_RWIN: - return true; - - default: - return false; - } -} - -bool -CMSWindowsKeyMapper::isPressed(UINT virtKey) const -{ - switch (virtKey) { - case VK_SHIFT: - return ((m_keys[m_virtKeyToScanCode[VK_LSHIFT]] & 0x80) != 0 || - (m_keys[m_virtKeyToScanCode[VK_RSHIFT]] & 0x80) != 0); - - case VK_CONTROL: - return ((m_keys[m_virtKeyToScanCode[VK_LCONTROL]] & 0x80) != 0 || - (m_keys[m_virtKeyToScanCode[VK_RCONTROL]] & 0x80) != 0); - - case VK_MENU: - return ((m_keys[m_virtKeyToScanCode[VK_LMENU]] & 0x80) != 0 || - (m_keys[m_virtKeyToScanCode[VK_RMENU]] & 0x80) != 0); - - default: - return ((m_keys[m_virtKeyToScanCode[virtKey & 0xffu]] & 0x80) != 0); - } -} - -bool -CMSWindowsKeyMapper::isToggled(UINT virtKey) const -{ - return ((m_keys[m_virtKeyToScanCode[virtKey & 0xffu]] & 0x01) != 0); + return mapCharacter(keys, multiByte[0], hkl, isAutoRepeat); } UINT -CMSWindowsKeyMapper::buttonToVirtualKey(KeyButton button) const -{ - return m_scanCodeToVirtKey[button & 0x1ffu]; -} - -KeyButton -CMSWindowsKeyMapper::virtualKeyToButton(UINT virtKey) const -{ - return m_virtKeyToScanCode[virtKey & 0xffu]; -} - -bool -CMSWindowsKeyMapper::isExtendedKey(KeyButton button) const -{ - return ((button & 0x100u) != 0); -} - -KeyModifierMask -CMSWindowsKeyMapper::getActiveModifiers() const -{ - KeyModifierMask mask = 0; - if (GetKeyState(VK_SHIFT) < 0 || - GetKeyState(VK_LSHIFT) < 0 || - GetKeyState(VK_RSHIFT) < 0) { - mask |= KeyModifierShift; - } - if (GetKeyState(VK_CONTROL) < 0 || - GetKeyState(VK_LCONTROL) < 0 || - GetKeyState(VK_RCONTROL) < 0) { - mask |= KeyModifierControl; - } - if (GetKeyState(VK_MENU) < 0 || - GetKeyState(VK_LMENU) < 0 || - GetKeyState(VK_RMENU) < 0) { - mask |= KeyModifierAlt; - } - if (GetKeyState(VK_LWIN) < 0 || - GetKeyState(VK_RWIN) < 0) { - mask |= KeyModifierSuper; - } - if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) { - mask |= KeyModifierCapsLock; - } - if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) { - mask |= KeyModifierNumLock; - } - if ((GetKeyState(VK_SCROLL) & 0x01) != 0) { - mask |= KeyModifierScrollLock; - } - return mask; -} - -KeyModifierMask -CMSWindowsKeyMapper::getShadowModifiers(bool needAltGr) const -{ - KeyModifierMask mask = 0; - if (isPressed(VK_SHIFT)) { - mask |= KeyModifierShift; - } - if (needAltGr) { - mask |= KeyModifierModeSwitch; - } - else { - if (isPressed(VK_CONTROL)) { - mask |= KeyModifierControl; - } - if (isPressed(VK_MENU)) { - mask |= KeyModifierAlt; - } - } - if (isPressed(VK_LWIN) || isPressed(VK_RWIN)) { - mask |= KeyModifierSuper; - } - if (isToggled(VK_CAPITAL)) { - mask |= KeyModifierCapsLock; - } - if (isToggled(VK_NUMLOCK)) { - mask |= KeyModifierNumLock; - } - if (isToggled(VK_SCROLL)) { - mask |= KeyModifierScrollLock; - } - return mask; -} - -const char* -CMSWindowsKeyMapper::getKeyName(KeyButton key) const -{ - char keyName[100]; - CMSWindowsKeyMapper* self = const_cast(this); - if (GetKeyNameText((key & 0x01ffu) << 16, keyName, sizeof(keyName)) != 0) { - self->m_keyName = keyName; - } - else if (m_scanCodeToVirtKey[key] != 0) { - self->m_keyName = s_vkToName[m_scanCodeToVirtKey[key]]; - } - else { - self->m_keyName = CStringUtil::print("scan code 0x%03x", key & 0x01ffu); - } - return m_keyName.c_str(); -} - -UINT -CMSWindowsKeyMapper::getCodePageFromLangID(LANGID langid) const +CMSWindowsKeyState::getCodePageFromLangID(LANGID langid) const { // construct a locale id from the language id LCID lcid = MAKELCID(langid, SORT_DEFAULT); @@ -1320,10 +1408,11 @@ CMSWindowsKeyMapper::getCodePageFromLangID(LANGID langid) const } KeyButton -CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, - const IKeyState& keyState, char c, HKL hkl, - bool isAutoRepeat) const +CMSWindowsKeyState::mapCharacter(Keystrokes& keys, + char c, HKL hkl, bool isAutoRepeat) const { + KeyModifierMask activeMask = getActiveModifiers(); + // translate the character into its virtual key and its required // modifier state. SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); @@ -1345,13 +1434,11 @@ CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, // otherwise users couldn't do, say, ctrl+z. // // the space character (ascii 32) is special in that it's unaffected - // by shift and should match the shift state from keyState. + // by shift and should match our stored shift state. KeyModifierMask desiredMask = 0; KeyModifierMask requiredMask = KeyModifierShift; if (c == 32) { - if (keyState.isModifierActive(KeyModifierShift)) { - desiredMask |= KeyModifierShift; - } + desiredMask |= (activeMask & KeyModifierShift); } else if ((modifierState & 0x01u) == 1) { desiredMask |= KeyModifierShift; @@ -1370,7 +1457,7 @@ CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, // off locally then use shift as necessary. if caps-lock is on // locally then it reverses the meaning of shift for keys that // are subject to case conversion. - if (keyState.isModifierActive(KeyModifierCapsLock)) { + if ((activeMask & KeyModifierCapsLock) != 0) { // there doesn't seem to be a simple way to test if a // character respects the caps lock key. for normal // characters it's easy enough but CharLower() and @@ -1391,26 +1478,25 @@ CMSWindowsKeyMapper::mapCharacter(IKeyState::Keystrokes& keys, // method for modifier keys). KeyButton scanCode = m_virtKeyToScanCode[virtualKey]; LOG((CLOG_DEBUG2 "character %d to virtual key %d scan code 0x%04x mask 0x%08x", (unsigned char)c, virtualKey, scanCode, desiredMask)); - return mapToKeystrokes(keys, keyState, scanCode, + return mapToKeystrokes(keys, scanCode, desiredMask, requiredMask, isAutoRepeat); } KeyButton -CMSWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, - const IKeyState& keyState, KeyButton button, +CMSWindowsKeyState::mapToKeystrokes(Keystrokes& keys, KeyButton button, KeyModifierMask desiredMask, KeyModifierMask requiredMask, bool isAutoRepeat) const { // adjust the modifiers to match the desired modifiers - IKeyState::Keystrokes undo; - if (!adjustModifiers(keys, undo, keyState, desiredMask, requiredMask)) { + Keystrokes undo; + if (!adjustModifiers(keys, undo, desiredMask, requiredMask)) { LOG((CLOG_DEBUG2 "failed to adjust modifiers")); keys.clear(); return 0; } // add the key event - IKeyState::Keystroke keystroke; + Keystroke keystroke; keystroke.m_key = button; keystroke.m_press = true; keystroke.m_repeat = isAutoRepeat; @@ -1426,9 +1512,8 @@ CMSWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys, } bool -CMSWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, - IKeyState::Keystrokes& undo, - const IKeyState& keyState, +CMSWindowsKeyState::adjustModifiers(Keystrokes& keys, + Keystrokes& undo, KeyModifierMask desiredMask, KeyModifierMask requiredMask) const { @@ -1437,7 +1522,7 @@ CMSWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, for (KeyModifierMask mask = 1u; requiredMask != 0; mask <<= 1) { if ((mask & requiredMask) != 0) { bool active = ((desiredMask & mask) != 0); - if (!keyState.mapModifier(keys, undo, mask, active)) { + if (!mapModifier(keys, undo, mask, active)) { return false; } requiredMask ^= mask; @@ -1447,7 +1532,7 @@ CMSWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys, } int -CMSWindowsKeyMapper::toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const +CMSWindowsKeyState::toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const { // ignore bogus character if (c == 0) { @@ -1494,7 +1579,7 @@ CMSWindowsKeyMapper::toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const } bool -CMSWindowsKeyMapper::isDeadChar(TCHAR c, HKL hkl, bool menu) const +CMSWindowsKeyState::isDeadChar(TCHAR c, HKL hkl, bool menu) const { // first clear out ToAsciiEx()'s internal buffer by sending it // a space. diff --git a/lib/platform/CMSWindowsKeyState.h b/lib/platform/CMSWindowsKeyState.h new file mode 100644 index 00000000..cc888103 --- /dev/null +++ b/lib/platform/CMSWindowsKeyState.h @@ -0,0 +1,130 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSKEYSTATE_H +#define CMSWINDOWSKEYSTATE_H + +#include "CKeyState.h" +#include "CString.h" +#define WIN32_LEAN_AND_MEAN +#include + +class CMSWindowsDesks; + +//! Microsoft Windows key mapper +/*! +This class maps KeyIDs to keystrokes. +*/ +class CMSWindowsKeyState : public CKeyState { +public: + CMSWindowsKeyState(CMSWindowsDesks* desks); + virtual ~CMSWindowsKeyState(); + + //! @name accessors + //@{ + + //! Set the active keyboard layout + /*! + Uses \p keyLayout when querying the keyboard. + */ + void setKeyLayout(HKL keyLayout); + + //! Check the named virtual key for release + /*! + If \p virtualKey isn't really pressed but we think it is then + update our state and post a key release event to \p eventTarget. + */ + void fixKey(void* eventTarget, UINT virtualKey); + + //! Map key event to a key + /*! + Converts a key event into a KeyID and the shadow modifier state + to a modifier mask. + */ + KeyID mapKeyFromEvent(WPARAM charAndVirtKey, + LPARAM info, KeyModifierMask* maskOut) const; + + //! Map a virtual key to a button + /*! + Returns the button for the \p virtualKey. + */ + KeyButton virtualKeyToButton(UINT virtualKey) const; + + //@} + + // IKeyState overrides + virtual void setKeyDown(KeyButton button, bool down); + virtual void sendKeyEvent(void* target, + bool press, bool isAutoRepeat, + KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton button); + virtual bool fakeCtrlAltDel(); + virtual const char* getKeyName(KeyButton) const; + +protected: + // IKeyState overrides + virtual void doUpdateKeys(); + virtual void doFakeKeyEvent(KeyButton button, + bool press, bool isAutoRepeat); + virtual KeyButton mapKey(Keystrokes& keys, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + +private: + // send ctrl+alt+del hotkey event on NT family + static void ctrlAltDelThread(void*); + + // convert a language ID to a code page + UINT getCodePageFromLangID(LANGID langid) const; + + // map character \c c given keyboard layout \c hkl to the keystrokes + // to generate it. + KeyButton mapCharacter(Keystrokes& keys, + char c, HKL hkl, bool isAutoRepeat) const; + + // map \c virtualKey to the keystrokes to generate it, along with + // keystrokes to update and restore the modifier state. + KeyButton mapToKeystrokes(Keystrokes& keys, KeyButton button, + KeyModifierMask desiredMask, + KeyModifierMask requiredMask, + bool isAutoRepeat) const; + + // get keystrokes to get modifiers in a desired state + bool adjustModifiers(Keystrokes& keys, + Keystrokes& undo, + KeyModifierMask desiredMask, + KeyModifierMask requiredMask) const; + + // pass character to ToAsciiEx(), returning what it returns + int toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const; + + // return true iff \c c is a dead character + bool isDeadChar(TCHAR c, HKL hkl, bool menu) const; + +private: + bool m_is95Family; + CMSWindowsDesks* m_desks; + HKL m_keyLayout; + CString m_keyName; + UINT m_scanCodeToVirtKey[512]; + KeyButton m_virtKeyToScanCode[256]; + + static const char* s_vkToName[]; + static const KeyID s_virtualKey[][2]; + static const UINT s_mapE000[]; + static const UINT s_mapEE00[]; + static const UINT s_mapEF00[]; +}; + +#endif diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 34ba1865..e6213f6b 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -14,8 +14,9 @@ #include "CMSWindowsScreen.h" #include "CMSWindowsClipboard.h" -#include "CMSWindowsDesktop.h" +#include "CMSWindowsDesks.h" #include "CMSWindowsEventQueueBuffer.h" +#include "CMSWindowsKeyState.h" #include "CMSWindowsScreenSaver.h" #include "CClipboard.h" #include "XScreen.h" @@ -30,28 +31,7 @@ #include "TMethodJob.h" #include "CArch.h" #include "CArchMiscWindows.h" -#include -#include -#include - -// ; -#define SYNERGY_MSG_SWITCH SYNERGY_HOOK_LAST_MSG + 1 -// ; -#define SYNERGY_MSG_ENTER SYNERGY_HOOK_LAST_MSG + 2 -// ; -#define SYNERGY_MSG_LEAVE SYNERGY_HOOK_LAST_MSG + 3 -// wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code -#define SYNERGY_MSG_FAKE_KEY SYNERGY_HOOK_LAST_MSG + 4 - // flags, XBUTTON id -#define SYNERGY_MSG_FAKE_BUTTON SYNERGY_HOOK_LAST_MSG + 5 -// x; y -#define SYNERGY_MSG_FAKE_MOVE SYNERGY_HOOK_LAST_MSG + 6 -// delta; -#define SYNERGY_MSG_FAKE_WHEEL SYNERGY_HOOK_LAST_MSG + 7 -// POINT*; -#define SYNERGY_MSG_CURSOR_POS SYNERGY_HOOK_LAST_MSG + 8 -// IKeyState*; -#define SYNERGY_MSG_SYNC_KEYS SYNERGY_HOOK_LAST_MSG + 9 +#include // // add backwards compatible multihead support (and suppress bogus warning) @@ -62,14 +42,6 @@ #include #pragma warning(pop) -// these are only defined when WINVER >= 0x0500 -#if !defined(SPI_GETMOUSESPEED) -#define SPI_GETMOUSESPEED 112 -#endif -#if !defined(SPI_SETMOUSESPEED) -#define SPI_SETMOUSESPEED 113 -#endif - // X button stuff #if !defined(WM_XBUTTONDOWN) #define WM_XBUTTONDOWN 0x020B @@ -83,6 +55,10 @@ #define XBUTTON1 0x0001 #define XBUTTON2 0x0002 #endif +#if !defined(VK_XBUTTON1) +#define VK_XBUTTON1 0x05 +#define VK_XBUTTON2 0x06 +#endif // // CMSWindowsScreen @@ -97,7 +73,6 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_is95Family(CArchMiscWindows::isWindows95Family()), m_isOnScreen(m_isPrimary), m_class(0), - m_cursor(NULL), m_window(NULL), m_x(0), m_y(0), m_w(0), m_h(0), @@ -108,26 +83,18 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_mark(0), m_markReceived(0), m_keyLayout(NULL), - m_timer(NULL), m_screensaver(NULL), m_screensaverNotify(false), m_nextClipboardWindow(NULL), m_ownClipboard(false), - m_activeDesk(NULL), - m_activeDeskName(), + m_desks(NULL), m_hookLibrary(NULL), m_init(NULL), m_cleanup(NULL), - m_install(NULL), - m_uninstall(NULL), m_setSides(NULL), m_setZone(NULL), m_setMode(NULL), - m_installScreensaver(NULL), - m_uninstallScreensaver(NULL), m_keyState(NULL), - m_mutex(), - m_deskReady(&m_mutex, false), m_suspend(suspend), m_resume(resume) { @@ -139,27 +106,28 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, if (m_isPrimary) { m_hookLibrary = openHookLibrary("synrgyhk"); } - m_cursor = createBlankCursor(); - m_class = createWindowClass(); - m_deskClass = createDeskWindowClass(m_isPrimary); - updateScreenShape(); - m_window = createWindow(m_class, "Synergy"); m_screensaver = new CMSWindowsScreenSaver(); + m_desks = new CMSWindowsDesks(m_isPrimary, + m_hookLibrary, m_screensaver, + new TMethodJob(this, + &CMSWindowsScreen::updateKeysCB)); + m_keyState = new CMSWindowsKeyState(m_desks); + updateScreenShape(); + m_class = createWindowClass(); + m_window = createWindow(m_class, "Synergy"); LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "")); LOG((CLOG_DEBUG "window is 0x%08x", m_window)); } catch (...) { + delete m_keyState; + delete m_desks; delete m_screensaver; destroyWindow(m_window); - destroyClass(m_deskClass); destroyClass(m_class); - destroyCursor(m_cursor); closeHookLibrary(m_hookLibrary); - m_screensaver = NULL; - m_class = 0; - m_cursor = NULL; - m_hookLibrary = NULL; - s_screen = NULL; + delete m_suspend; + delete m_resume; + s_screen = NULL; throw; } @@ -179,11 +147,11 @@ CMSWindowsScreen::~CMSWindowsScreen() disable(); EVENTQUEUE->adoptBuffer(NULL); EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget()); + delete m_keyState; + delete m_desks; delete m_screensaver; destroyWindow(m_window); - destroyClass(m_deskClass); destroyClass(m_class); - destroyCursor(m_cursor); closeHookLibrary(m_hookLibrary); delete m_suspend; delete m_resume; @@ -205,13 +173,6 @@ CMSWindowsScreen::getInstance() return s_instance; } -void -CMSWindowsScreen::setKeyState(IKeyState* keyState) -{ - m_keyState = keyState; - updateKeys(); -} - void CMSWindowsScreen::enable() { @@ -220,10 +181,10 @@ CMSWindowsScreen::enable() // install our clipboard snooper m_nextClipboardWindow = SetClipboardViewer(m_window); - if (m_isPrimary) { - // update shadow key state - m_keyMapper.update(NULL); + // track the active desk and (re)install the hooks + m_desks->enable(); + if (m_isPrimary) { // set jump zones m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize()); @@ -236,29 +197,13 @@ CMSWindowsScreen::enable() // the server would not be able to wake us up. CArchMiscWindows::addBusyState(CArchMiscWindows::kSYSTEM); } - - // set the active desk and (re)install the hooks - checkDesk(); - - // install the desk timer. this timer periodically checks - // which desk is active and reinstalls the hooks as necessary. - // we wouldn't need this if windows notified us of a desktop - // change but as far as i can tell it doesn't. - m_timer = EVENTQUEUE->newTimer(0.2, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer, - new TMethodEventJob( - this, &CMSWindowsScreen::handleCheckDesk)); } void CMSWindowsScreen::disable() { - // remove timer - if (m_timer != NULL) { - EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer); - EVENTQUEUE->deleteTimer(m_timer); - m_timer = NULL; - } + // stop tracking the active desk + m_desks->disable(); if (m_isPrimary) { // disable hooks @@ -273,9 +218,6 @@ CMSWindowsScreen::disable() CArchMiscWindows::kDISPLAY); } - // destroy desks - removeDesks(); - // stop snooping the clipboard ChangeClipboardChain(m_window, m_nextClipboardWindow); m_nextClipboardWindow = NULL; @@ -286,7 +228,7 @@ CMSWindowsScreen::disable() void CMSWindowsScreen::enter() { - sendDeskMessage(SYNERGY_MSG_ENTER, 0, 0); + m_desks->enter(); if (m_isPrimary) { // enable special key sequences on win95 family enableSpecialKeys(true); @@ -312,18 +254,12 @@ CMSWindowsScreen::leave() m_keyLayout = GetKeyboardLayout(thread); // tell the key mapper about the keyboard layout - m_keyMapper.setKeyLayout(m_keyLayout); - sendDeskMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); + m_keyState->setKeyLayout(m_keyLayout); // tell desk that we're leaving and tell it the keyboard layout - sendDeskMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0); + m_desks->leave(m_keyLayout); if (m_isPrimary) { -/* XXX - // update keys - m_keyMapper.update(NULL); -*/ - // warp to center warpCursor(m_xCenter, m_yCenter); @@ -391,7 +327,7 @@ CMSWindowsScreen::openScreensaver(bool notify) m_screensaverNotify = notify; if (m_screensaverNotify) { - m_installScreensaver(); + m_desks->installScreensaverHooks(true); } else { m_screensaver->disable(); @@ -403,7 +339,7 @@ CMSWindowsScreen::closeScreensaver() { if (m_screensaver != NULL) { if (m_screensaverNotify) { - m_uninstallScreensaver(); + m_desks->installScreensaverHooks(false); } else { m_screensaver->enable(); @@ -437,14 +373,6 @@ CMSWindowsScreen::setOptions(const COptionsList&) // no options } -void -CMSWindowsScreen::updateKeys() -{ - sendDeskMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); - memset(m_buttons, 0, sizeof(m_buttons)); - // FIXME -- get the button state -} - void CMSWindowsScreen::setSequenceNumber(UInt32 seqNum) { @@ -485,11 +413,7 @@ CMSWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const void CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const { - POINT pos; - sendDeskMessage(SYNERGY_MSG_CURSOR_POS, - reinterpret_cast(&pos), 0); - x = pos.x; - y = pos.y; + m_desks->getCursorPos(x, y); } void @@ -529,7 +453,7 @@ bool CMSWindowsScreen::isAnyMouseButtonDown() const { static const char* buttonToName[] = { - "button 0", + "", "Left Button", "Middle Button", "Right Button", @@ -537,8 +461,8 @@ CMSWindowsScreen::isAnyMouseButtonDown() const "X Button 2" }; - for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { - if ((m_buttons[i] & 0x80) != 0) { + for (UInt32 i = 1; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { + if (m_buttons[i]) { LOG((CLOG_DEBUG "locked by \"%s\"", buttonToName[i])); return true; } @@ -547,12 +471,6 @@ CMSWindowsScreen::isAnyMouseButtonDown() const return false; } -KeyModifierMask -CMSWindowsScreen::getActiveModifiers() const -{ - return m_keyMapper.getActiveModifiers(); -} - void CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const { @@ -560,87 +478,28 @@ CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const y = m_yCenter; } -const char* -CMSWindowsScreen::getKeyName(KeyButton virtualKey) const -{ - return m_keyMapper.getKeyName(virtualKey); -} - -void -CMSWindowsScreen::fakeKeyEvent(KeyButton id, bool press) const -{ - DWORD flags = 0; - if (m_keyMapper.isExtendedKey(id)) { - flags |= KEYEVENTF_EXTENDEDKEY; - } - if (!press) { - flags |= KEYEVENTF_KEYUP; - } - UINT vk = m_keyMapper.buttonToVirtualKey(id); - sendDeskMessage(SYNERGY_MSG_FAKE_KEY, flags, - MAKEWORD(static_cast(id & 0xffu), - static_cast(vk & 0xffu))); -} - -bool -CMSWindowsScreen::fakeCtrlAltDel() const -{ - if (!m_is95Family) { - // to fake ctrl+alt+del on the NT family we broadcast a suitable - // hotkey to all windows on the winlogon desktop. however, the - // current thread must be on that desktop to do the broadcast - // and we can't switch just any thread because some own windows - // or hooks. so start a new thread to do the real work. - CThread cad(new CFunctionJob(&CMSWindowsScreen::ctrlAltDelThread)); - cad.wait(); - } - else { - // get the sequence of keys to simulate ctrl+alt+del - IKeyState::Keystrokes keys; - KeyID key = kKeyDelete; - KeyModifierMask mask = KeyModifierControl | KeyModifierAlt; - if (mapKey(keys, *m_keyState, key, mask, false) == 0) { - keys.clear(); - } - - // do it - for (IKeyState::Keystrokes::const_iterator k = keys.begin(); - k != keys.end(); ++k) { - fakeKeyEvent(k->m_key, k->m_press); - } - } - return true; -} - void CMSWindowsScreen::fakeMouseButton(ButtonID id, bool press) const { - DWORD data; - DWORD flags = mapButtonToEvent(id, press, &data); - sendDeskMessage(SYNERGY_MSG_FAKE_BUTTON, flags, data); + m_desks->fakeMouseButton(id, press); } void CMSWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const { - sendDeskMessage(SYNERGY_MSG_FAKE_MOVE, - static_cast(x), - static_cast(y)); + m_desks->fakeMouseMove(x, y); } void CMSWindowsScreen::fakeMouseWheel(SInt32 delta) const { - sendDeskMessage(SYNERGY_MSG_FAKE_WHEEL, delta, 0); + m_desks->fakeMouseWheel(delta); } -KeyButton -CMSWindowsScreen::mapKey(IKeyState::Keystrokes& keys, - const IKeyState& keyState, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const +void +CMSWindowsScreen::updateKeys() { - return m_keyMapper.mapKey(keys, keyState, id, desiredMask, isAutoRepeat); + m_desks->updateKeys(); } HINSTANCE @@ -657,25 +516,13 @@ CMSWindowsScreen::openHookLibrary(const char* name) m_setSides = (SetSidesFunc)GetProcAddress(hookLibrary, "setSides"); m_setZone = (SetZoneFunc)GetProcAddress(hookLibrary, "setZone"); m_setMode = (SetModeFunc)GetProcAddress(hookLibrary, "setMode"); - m_install = (InstallFunc)GetProcAddress(hookLibrary, "install"); - m_uninstall = (UninstallFunc)GetProcAddress(hookLibrary, "uninstall"); m_init = (InitFunc)GetProcAddress(hookLibrary, "init"); m_cleanup = (CleanupFunc)GetProcAddress(hookLibrary, "cleanup"); - m_installScreensaver = - (InstallScreenSaverFunc)GetProcAddress( - hookLibrary, "installScreenSaver"); - m_uninstallScreensaver = - (UninstallScreenSaverFunc)GetProcAddress( - hookLibrary, "uninstallScreenSaver"); if (m_setSides == NULL || m_setZone == NULL || m_setMode == NULL || - m_install == NULL || - m_uninstall == NULL || m_init == NULL || - m_cleanup == NULL || - m_installScreensaver == NULL || - m_uninstallScreensaver == NULL) { + m_cleanup == NULL) { LOG((CLOG_ERR "Invalid hook library; use a newer %s.dll", name)); throw XScreenOpenFailure(); } @@ -741,27 +588,6 @@ CMSWindowsScreen::createWindowClass() const return RegisterClassEx(&classInfo); } -ATOM -CMSWindowsScreen::createDeskWindowClass(bool isPrimary) const -{ - WNDCLASSEX classInfo; - classInfo.cbSize = sizeof(classInfo); - classInfo.style = CS_DBLCLKS | CS_NOCLOSE; - classInfo.lpfnWndProc = isPrimary ? - &CMSWindowsScreen::primaryDeskProc : - &CMSWindowsScreen::secondaryDeskProc; - classInfo.cbClsExtra = 0; - classInfo.cbWndExtra = 0; - classInfo.hInstance = s_instance; - classInfo.hIcon = NULL; - classInfo.hCursor = m_cursor; - classInfo.hbrBackground = NULL; - classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = "SynergyDesk"; - classInfo.hIconSm = NULL; - return RegisterClassEx(&classInfo); -} - void CMSWindowsScreen::destroyClass(ATOM windowClass) const { @@ -829,6 +655,23 @@ CMSWindowsScreen::handleSystemEvent(const CEvent& event, void*) DispatchMessage(msg); } +void +CMSWindowsScreen::updateButtons() +{ + m_buttons[kButtonNone] = false; + m_buttons[kButtonLeft] = (GetKeyState(VK_LBUTTON) < 0); + m_buttons[kButtonMiddle] = (GetKeyState(VK_MBUTTON) < 0); + m_buttons[kButtonRight] = (GetKeyState(VK_RBUTTON) < 0); + m_buttons[kButtonExtra0 + 0] = (GetKeyState(VK_XBUTTON1) < 0); + m_buttons[kButtonExtra0 + 1] = (GetKeyState(VK_XBUTTON2) < 0); +} + +IKeyState* +CMSWindowsScreen::getKeyState() const +{ + return m_keyState; +} + bool CMSWindowsScreen::onPreDispatch(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) @@ -850,27 +693,24 @@ bool CMSWindowsScreen::onPreDispatchPrimary(HWND, UINT message, WPARAM wParam, LPARAM lParam) { - // check if windows key is up but we think it's down. if so then - // synthesize a key release for it. we have to do this because - // if the user presses and releases a windows key without pressing - // any other key while it's down then windows will eat the key + // fake a key release for the windows keys if we think they're + // down but they're really up. we have to do this because if the + // user presses and releases a windows key without pressing any + // other key while it's down then the system will eat the key // release. if we don't detect that and synthesize the release - // then the cclient won't take the usual windows key release action + // then the client won't take the usual windows key release action // (which on windows is to show the start menu). // - // we can use GetKeyState() to check the state of the windows keys - // because, event though the key release is not reported to us, - // the event is processed and the keyboard state updated by the - // system. since the key could go up at any time we'll check the - // state on every event. only check on windows 95 family since - // NT family reports the key release as usual. obviously we skip - // this if the event is for the windows key itself. + // since the key could go up at any time we'll check the state on + // every event. only check on the windows 95 family since the NT + // family reports the key release as usual. obviously we skip + // this if the event is for a windows key itself. if (m_is95Family && message != SYNERGY_MSG_KEY) { if (wParam != VK_LWIN) { - fixKey(VK_LWIN); + m_keyState->fixKey(getEventTarget(), VK_LWIN); } if (wParam != VK_RWIN) { - fixKey(VK_RWIN); + m_keyState->fixKey(getEventTarget(), VK_RWIN); } } @@ -998,182 +838,48 @@ CMSWindowsScreen::onMark(UInt32 mark) bool CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) { - WPARAM charAndVirtKey = wParam; - wParam &= 0xffu; + LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, lParam)); // update key state. ignore key repeats. + KeyButton button = (KeyButton)((lParam & 0x01ff0000) >> 16); if ((lParam & 0xc0000000u) == 0x00000000) { - KeyButton scancode = (KeyButton)((lParam & 0x01ff0000) >> 16); - m_keyState->setKeyDown(scancode, true); + m_keyState->setKeyDown(button, true); } - else if ((lParam & 0xc0000000u) == 0xc0000000) { - KeyButton scancode = (KeyButton)((lParam & 0x01ff0000) >> 16); - m_keyState->setKeyDown(scancode, false); + else if ((lParam & 0x80000000u) == 0x80000000) { + m_keyState->setKeyDown(button, false); } // ignore message if posted prior to last mark change if (!ignore()) { // check for ctrl+alt+del emulation - if ((wParam == VK_PAUSE || wParam == VK_CANCEL) && - (m_keyMapper.isPressed(VK_CONTROL) && - m_keyMapper.isPressed(VK_MENU))) { + UINT virtKey = (wParam & 0xffu); + if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) && + (m_keyState->getActiveModifiers() & + (KeyModifierControl | KeyModifierAlt)) != 0) { LOG((CLOG_DEBUG "emulate ctrl+alt+del")); - wParam = VK_DELETE; - lParam &= 0xfffe0000; - lParam |= m_keyMapper.virtualKeyToButton(wParam) << 16; - lParam |= 0x00000001; - charAndVirtKey = wParam; + // switch wParam and lParam to be as if VK_DELETE was + // pressed or released + wParam = VK_DELETE; + lParam &= 0xfe000000; + lParam |= m_keyState->virtualKeyToButton(wParam) << 16; + lParam |= 0x00000001; } - // process key normally - bool altgr; + // process key KeyModifierMask mask; - const KeyID key = m_keyMapper.mapKeyFromEvent( - charAndVirtKey, lParam, &mask, &altgr); - KeyButton button = static_cast( - (lParam & 0x01ff0000u) >> 16); - if (key != kKeyNone && key != kKeyMultiKey) { - if ((lParam & 0x80000000) == 0) { - // key press - - // if AltGr required for this key then make sure - // the ctrl and alt keys are *not* down on the - // client. windows simulates AltGr with ctrl and - // alt for some inexplicable reason and clients - // will get confused if they see mode switch and - // ctrl and alt. we'll also need to put ctrl and - // alt back the way they were after we simulate - // the key. - bool ctrlL = m_keyMapper.isPressed(VK_LCONTROL); - bool ctrlR = m_keyMapper.isPressed(VK_RCONTROL); - bool altL = m_keyMapper.isPressed(VK_LMENU); - bool altR = m_keyMapper.isPressed(VK_RMENU); - if (altgr) { - KeyID key; - KeyButton button; - KeyModifierMask mask2 = (mask & - ~(KeyModifierControl | - KeyModifierAlt | - KeyModifierModeSwitch)); - if (ctrlL) { - key = kKeyControl_L; - button = m_keyMapper.virtualKeyToButton(VK_LCONTROL); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - sendEvent(getKeyUpEvent(), - CKeyInfo::alloc(key, mask2, button, 1)); - } - if (ctrlR) { - key = kKeyControl_R; - button = m_keyMapper.virtualKeyToButton(VK_RCONTROL); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - sendEvent(getKeyUpEvent(), - CKeyInfo::alloc(key, mask2, button, 1)); - } - if (altL) { - key = kKeyAlt_L; - button = m_keyMapper.virtualKeyToButton(VK_LMENU); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - sendEvent(getKeyUpEvent(), - CKeyInfo::alloc(key, mask2, button, 1)); - } - if (altR) { - key = kKeyAlt_R; - button = m_keyMapper.virtualKeyToButton(VK_RMENU); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - sendEvent(getKeyUpEvent(), - CKeyInfo::alloc(key, mask2, button, 1)); - } - } - - // send key - const bool wasDown = ((lParam & 0x40000000) != 0); - SInt32 repeat = (SInt32)(lParam & 0xffff); - if (!wasDown) { - LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); - sendEvent(getKeyDownEvent(), - CKeyInfo::alloc(key, mask, button, 1)); - if (repeat > 0) { - --repeat; - } - } - if (repeat >= 1) { - LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button)); - sendEvent(getKeyRepeatEvent(), - CKeyInfo::alloc(key, mask, button, repeat)); - } - - // restore ctrl and alt state - if (altgr) { - KeyID key; - KeyButton button; - KeyModifierMask mask2 = (mask & - ~(KeyModifierControl | - KeyModifierAlt | - KeyModifierModeSwitch)); - if (ctrlL) { - key = kKeyControl_L; - button = m_keyMapper.virtualKeyToButton(VK_LCONTROL); - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - sendEvent(getKeyDownEvent(), - CKeyInfo::alloc(key, mask2, button, 1)); - mask2 |= KeyModifierControl; - } - if (ctrlR) { - key = kKeyControl_R; - button = m_keyMapper.virtualKeyToButton(VK_RCONTROL); - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - sendEvent(getKeyDownEvent(), - CKeyInfo::alloc(key, mask2, button, 1)); - mask2 |= KeyModifierControl; - } - if (altL) { - key = kKeyAlt_L; - button = m_keyMapper.virtualKeyToButton(VK_LMENU); - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - sendEvent(getKeyDownEvent(), - CKeyInfo::alloc(key, mask2, button, 1)); - mask2 |= KeyModifierAlt; - } - if (altR) { - key = kKeyAlt_R; - button = m_keyMapper.virtualKeyToButton(VK_RMENU); - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - sendEvent(getKeyDownEvent(), - CKeyInfo::alloc(key, mask2, button, 1)); - mask2 |= KeyModifierAlt; - } - } - } - else { - // key release. if the key isn't down according to - // our table then we never got the key press event - // for it. if it's not a modifier key then we'll - // synthesize the press first. only do this on - // the windows 95 family, which eats certain special - // keys like alt+tab, ctrl+esc, etc. - if (m_is95Family && - !m_keyMapper.isModifier(wParam) && - m_keyMapper.isPressed(wParam)) { - LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); - sendEvent(getKeyDownEvent(), - CKeyInfo::alloc(key, mask, button, 1)); - m_keyMapper.updateKey(lParam & 0x3fffffffu); - } - - // do key up - LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - sendEvent(getKeyUpEvent(), - CKeyInfo::alloc(key, mask, button, 1)); - } + KeyID key = m_keyState->mapKeyFromEvent(wParam, lParam, &mask); + button = static_cast((lParam & 0x01ff0000u) >> 16); + if (key != kKeyNone) { + m_keyState->sendKeyEvent(getEventTarget(), + ((lParam & 0x80000000) == 0), + ((lParam & 0x40000000) == 1), + key, mask, (SInt32)(lParam & 0xffff), button); } else { - LOG((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", wParam, lParam)); + LOG((CLOG_DEBUG2 "event: cannot map key")); } } - // keep our shadow key state up to date - m_keyMapper.updateKey(lParam); - return true; } @@ -1184,6 +890,16 @@ CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) bool pressed = false; const ButtonID button = mapButtonFromEvent(wParam, lParam); + // keep our shadow key state up to date + if (button >= kButtonLeft && button <= kButtonExtra0 + 1) { + if (pressed) { + m_buttons[button] = true; + } + else { + m_buttons[button] = false; + } + } + // ignore message if posted prior to last mark change if (!ignore()) { switch (wParam) { @@ -1227,16 +943,6 @@ CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) } } - // keep our shadow key state up to date - if (button >= kButtonLeft && button <= kButtonExtra0 + 1) { - if (pressed) { - m_buttons[button] |= 0x80; - } - else { - m_buttons[button] &= ~0x80; - } - } - return true; } @@ -1452,6 +1158,9 @@ CMSWindowsScreen::updateScreenShape() m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || m_h != GetSystemMetrics(SM_CYSCREEN)); LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "")); + + // tell the desks + m_desks->setShape(m_x, m_y, m_w, m_h, m_xCenter, m_yCenter, m_multimon); } void @@ -1468,73 +1177,6 @@ CMSWindowsScreen::enableSpecialKeys(bool enable) const } } -void -CMSWindowsScreen::fixKey(UINT virtualKey) -{ - if (m_keyMapper.isPressed(virtualKey) && - (GetAsyncKeyState(virtualKey) & 0x8000) == 0) { - // compute appropriate parameters for fake event - KeyButton button = m_keyMapper.virtualKeyToButton(virtualKey); - LPARAM lParam = 0xc0000000 | ((LPARAM)button << 16); - - // process as if it were a key up - KeyModifierMask mask; - KeyID key = m_keyMapper.mapKeyFromEvent(virtualKey, - lParam, &mask, NULL); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); - m_keyMapper.updateKey(lParam); - } -} - -DWORD -CMSWindowsScreen::mapButtonToEvent(ButtonID button, - bool press, DWORD* inData) const -{ - DWORD dummy; - DWORD* data = (inData != NULL) ? inData : &dummy; - - // the system will swap the meaning of left/right for us if - // the user has configured a left-handed mouse but we don't - // want it to swap since we want the handedness of the - // server's mouse. so pre-swap for a left-handed mouse. - if (GetSystemMetrics(SM_SWAPBUTTON)) { - switch (button) { - case kButtonLeft: - button = kButtonRight; - break; - - case kButtonRight: - button = kButtonLeft; - break; - } - } - - // map button id to button flag and button data - *data = 0; - switch (button) { - case kButtonLeft: - return press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; - - case kButtonMiddle: - return press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; - - case kButtonRight: - return press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; - - case kButtonExtra0 + 0: - *data = XBUTTON1; - return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; - - case kButtonExtra0 + 1: - *data = XBUTTON2; - return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP; - - default: - return 0; - } -} - ButtonID CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const { @@ -1584,23 +1226,10 @@ CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const } void -CMSWindowsScreen::ctrlAltDelThread(void*) +CMSWindowsScreen::updateKeysCB(void*) { - // get the Winlogon desktop at whatever privilege we can - HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED); - if (desk != NULL) { - if (SetThreadDesktop(desk)) { - PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, - MAKELPARAM(MOD_CONTROL | MOD_ALT, VK_DELETE)); - } - else { - LOG((CLOG_DEBUG "can't switch to Winlogon desk: %d", GetLastError())); - } - CloseDesktop(desk); - } - else { - LOG((CLOG_DEBUG "can't open Winlogon desk: %d", GetLastError())); - } + m_keyState->updateKeys(); + updateButtons(); } LRESULT CALLBACK @@ -1615,417 +1244,3 @@ CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return result; } - -LRESULT CALLBACK -CMSWindowsScreen::primaryDeskProc( - HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -LRESULT CALLBACK -CMSWindowsScreen::secondaryDeskProc( - HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - // would like to detect any local user input and hide the hider - // window but for now we just detect mouse motion. - bool hide = false; - switch (msg) { - case WM_MOUSEMOVE: - if (LOWORD(lParam) != 0 || HIWORD(lParam) != 0) { - hide = true; - } - break; - } - - if (hide && IsWindowVisible(hwnd)) { - ReleaseCapture(); - SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | - SWP_NOACTIVATE | SWP_HIDEWINDOW); - } - - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -void -CMSWindowsScreen::deskMouseMove(SInt32 x, SInt32 y) const -{ - // motion is simple (i.e. it's on the primary monitor) if there - // is only one monitor. - bool simple = !m_multimon; - if (!simple) { - // also simple if motion is within the primary monitor - simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && - y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); - } - - // move the mouse directly to target position if motion is simple - if (simple) { - // when using absolute positioning with mouse_event(), - // the normalized device coordinates range over only - // the primary screen. - SInt32 w = GetSystemMetrics(SM_CXSCREEN); - SInt32 h = GetSystemMetrics(SM_CYSCREEN); - mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65535.0f * x) / (w - 1) + 0.5f), - (DWORD)((65535.0f * y) / (h - 1) + 0.5f), - 0, 0); - } - - // windows 98 (and Me?) is broken. you cannot set the absolute - // position of the mouse except on the primary monitor but you - // can do relative moves onto any monitor. this is, in microsoft's - // words, "by design." apparently the designers of windows 2000 - // we're a little less lazy and did it right. - // - // microsoft recommends in Q193003 to absolute position the cursor - // somewhere on the primary monitor then relative move to the - // desired location. this doesn't work for us because when the - // user drags a scrollbar, a window, etc. it causes the dragged - // item to jump back and forth between the position on the primary - // monitor and the desired position. while it always ends up in - // the right place, the effect is disconcerting. - // - // instead we'll get the cursor's current position and do just a - // relative move from there to the desired position. relative - // moves are subject to cursor acceleration which we don't want. - // so we disable acceleration, do the relative move, then restore - // acceleration. there's a slight chance we'll end up in the - // wrong place if the user moves the cursor using this system's - // mouse while simultaneously moving the mouse on the server - // system. that defeats the purpose of synergy so we'll assume - // that won't happen. even if it does, the next mouse move will - // correct the position. - else { - // save mouse speed & acceleration - int oldSpeed[4]; - bool accelChanged = - SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && - SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); - - // use 1:1 motion - if (accelChanged) { - int newSpeed[4] = { 0, 0, 0, 1 }; - accelChanged = - SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || - SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); - } - - // move relative to mouse position - POINT pos; - GetCursorPos(&pos); - mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); - - // restore mouse speed & acceleration - if (accelChanged) { - SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); - SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); - } - } -} - -void -CMSWindowsScreen::deskEnter(CDesk* desk) -{ - if (!m_isPrimary) { - ReleaseCapture(); - } - ShowCursor(TRUE); - SetWindowPos(desk->m_window, HWND_BOTTOM, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | - SWP_NOACTIVATE | SWP_HIDEWINDOW); -} - -void -CMSWindowsScreen::deskLeave(CDesk* desk, HKL keyLayout) -{ - ShowCursor(FALSE); - if (m_isPrimary) { - // map a window to hide the cursor and to use whatever keyboard - // layout we choose rather than the keyboard layout of the last - // active window. - int x, y, w, h; - if (desk->m_lowLevel) { - // with a low level hook the cursor will never budge so - // just a 1x1 window is sufficient. - x = m_xCenter; - y = m_yCenter; - w = 1; - h = 1; - } - else { - // with regular hooks the cursor will jitter as it's moved - // by the user then back to the center by us. to be sure - // we never lose it, cover all the monitors with the window. - x = m_x; - y = m_y; - w = m_w; - h = m_h; - } - SetWindowPos(desk->m_window, HWND_TOPMOST, x, y, w, h, - SWP_NOACTIVATE | SWP_SHOWWINDOW); - - // switch to requested keyboard layout - ActivateKeyboardLayout(keyLayout, 0); - } - else { - // move hider window under the cursor center, raise, and show it - SetWindowPos(desk->m_window, HWND_TOPMOST, - m_xCenter, m_yCenter, 1, 1, - SWP_NOACTIVATE | SWP_SHOWWINDOW); - - // watch for mouse motion. if we see any then we hide the - // hider window so the user can use the physically attached - // mouse if desired. we'd rather not capture the mouse but - // we aren't notified when the mouse leaves our window. - SetCapture(desk->m_window); - - // warp the mouse to the cursor center - deskMouseMove(m_xCenter, m_yCenter); - } -} - -void -CMSWindowsScreen::deskThread(void* vdesk) -{ - MSG msg; - - // use given desktop for this thread - CDesk* desk = reinterpret_cast(vdesk); - desk->m_threadID = GetCurrentThreadId(); - desk->m_window = NULL; - if (desk->m_desk != NULL && SetThreadDesktop(desk->m_desk) != 0) { - // create a message queue - PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE); - - // create a window. we use this window to hide the cursor. - try { - desk->m_window = createWindow(m_deskClass, "SynergyDesk"); - LOG((CLOG_DEBUG "desk %s window is 0x%08x", desk->m_name.c_str(), desk->m_window)); - } - catch (...) { - // ignore - LOG((CLOG_DEBUG "can't create desk window for %s", desk->m_name.c_str())); - } - - // a window on the primary screen should never activate - if (m_isPrimary && desk->m_window != NULL) { - EnableWindow(desk->m_window, FALSE); - } - } - - // tell main thread that we're ready - { - CLock lock(&m_mutex); - m_deskReady = true; - m_deskReady.broadcast(); - } - - while (GetMessage(&msg, NULL, 0, 0)) { - switch (msg.message) { - default: - TranslateMessage(&msg); - DispatchMessage(&msg); - continue; - - case SYNERGY_MSG_SWITCH: - if (m_isPrimary) { - m_uninstall(); - if (m_screensaverNotify) { - m_uninstallScreensaver(); - m_installScreensaver(); - } - switch (m_install()) { - case kHOOK_FAILED: - // we won't work on this desk - desk->m_lowLevel = false; - break; - - case kHOOK_OKAY: - desk->m_lowLevel = false; - break; - - case kHOOK_OKAY_LL: - desk->m_lowLevel = true; - break; - } - } - break; - - case SYNERGY_MSG_ENTER: - deskEnter(desk); - break; - - case SYNERGY_MSG_LEAVE: - deskLeave(desk, (HKL)msg.wParam); - break; - - case SYNERGY_MSG_FAKE_KEY: - keybd_event(HIBYTE(msg.lParam), LOBYTE(msg.lParam), msg.wParam, 0); - break; - - case SYNERGY_MSG_FAKE_BUTTON: - if (msg.wParam != 0) { - mouse_event(msg.wParam, 0, 0, msg.lParam, 0); - } - break; - - case SYNERGY_MSG_FAKE_MOVE: - deskMouseMove(static_cast(msg.wParam), - static_cast(msg.lParam)); - break; - - case SYNERGY_MSG_FAKE_WHEEL: - mouse_event(MOUSEEVENTF_WHEEL, 0, 0, msg.wParam, 0); - break; - - case SYNERGY_MSG_CURSOR_POS: { - POINT* pos = reinterpret_cast(msg.wParam); - if (!GetCursorPos(pos)) { - pos->x = m_xCenter; - pos->y = m_yCenter; - } - break; - } - - case SYNERGY_MSG_SYNC_KEYS: - m_keyMapper.update(m_keyState); - break; - } - - // notify that message was processed - CLock lock(&m_mutex); - m_deskReady = true; - m_deskReady.broadcast(); - } - - // clean up - deskEnter(desk); - if (desk->m_window != NULL) { - DestroyWindow(desk->m_window); - } - if (desk->m_desk != NULL) { - CMSWindowsDesktop::closeDesktop(desk->m_desk); - } -} - -CMSWindowsScreen::CDesk* -CMSWindowsScreen::addDesk(const CString& name, HDESK hdesk) -{ - CDesk* desk = new CDesk; - desk->m_name = name; - desk->m_desk = hdesk; - desk->m_targetID = GetCurrentThreadId(); - desk->m_thread = new CThread(new TMethodJob( - this, &CMSWindowsScreen::deskThread, desk)); - waitForDesk(); - m_desks.insert(std::make_pair(name, desk)); - return desk; -} - -void -CMSWindowsScreen::removeDesks() -{ - for (CDesks::iterator index = m_desks.begin(); - index != m_desks.end(); ++index) { - CDesk* desk = index->second; - PostThreadMessage(desk->m_threadID, WM_QUIT, 0, 0); - desk->m_thread->wait(); - delete desk->m_thread; - delete desk; - } - m_desks.clear(); - m_activeDesk = NULL; - m_activeDeskName = ""; -} - -void -CMSWindowsScreen::checkDesk() -{ - // get current desktop. if we already know about it then return. - CDesk* desk; - HDESK hdesk = CMSWindowsDesktop::openInputDesktop(); - CString name = CMSWindowsDesktop::getDesktopName(hdesk); - CDesks::const_iterator index = m_desks.find(name); - if (index == m_desks.end()) { - desk = addDesk(name, hdesk); - // hold on to hdesk until thread exits so the desk can't - // be removed by the system - } - else { - CMSWindowsDesktop::closeDesktop(hdesk); - desk = index->second; - } - - // if active desktop changed then tell the old and new desk threads - // about the change. don't switch desktops when the screensaver is - // active becaue we'd most likely switch to the screensaver desktop - // which would have the side effect of forcing the screensaver to - // stop. - if (name != m_activeDeskName && !m_screensaver->isActive()) { - // show cursor on previous desk - if (!m_isOnScreen) { - sendDeskMessage(SYNERGY_MSG_ENTER, 0, 0); - } - - // check for desk accessibility change. we don't get events - // from an inaccessible desktop so when we switch from an - // inaccessible desktop to an accessible one we have to - // update the keyboard state. - LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str())); - bool isAccessible = isDeskAccessible(desk); - if (isDeskAccessible(m_activeDesk) != isAccessible) { - if (isAccessible) { - LOG((CLOG_DEBUG "desktop is now accessible")); - updateKeys(); - } - else { - LOG((CLOG_DEBUG "desktop is now inaccessible")); - } - } - - // switch desk - m_activeDesk = desk; - m_activeDeskName = name; - sendDeskMessage(SYNERGY_MSG_SWITCH, 0, 0); - - // hide cursor on new desk - if (!m_isOnScreen) { - sendDeskMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0); - } - } -} - -bool -CMSWindowsScreen::isDeskAccessible(const CDesk* desk) const -{ - return (desk != NULL && desk->m_desk != NULL); -} - -void -CMSWindowsScreen::sendDeskMessage(UINT msg, WPARAM wParam, LPARAM lParam) const -{ - if (m_activeDesk != NULL && m_activeDesk->m_window != NULL) { - PostThreadMessage(m_activeDesk->m_threadID, msg, wParam, lParam); - waitForDesk(); - } -} - -void -CMSWindowsScreen::waitForDesk() const -{ - CMSWindowsScreen* self = const_cast(this); - - CLock lock(&m_mutex); - while (!(bool)m_deskReady) { - m_deskReady.wait(); - } - self->m_deskReady = false; -} - -void -CMSWindowsScreen::handleCheckDesk(const CEvent&, void*) -{ - checkDesk(); -} diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index f9e0b9b7..8a43c833 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -15,8 +15,7 @@ #ifndef CMSWINDOWSSCREEN_H #define CMSWINDOWSSCREEN_H -#include "IPlatformScreen.h" -#include "CMSWindowsKeyMapper.h" +#include "CPlatformScreen.h" #include "CSynergyHook.h" #include "CCondVar.h" #include "CMutex.h" @@ -24,13 +23,14 @@ #define WIN32_LEAN_AND_MEAN #include -class CEventQueueTimer; +class CMSWindowsDesks; +class CMSWindowsKeyState; class CMSWindowsScreenSaver; class CThread; class IJob; //! Implementation of IPlatformScreen for Microsoft Windows -class CMSWindowsScreen : public IPlatformScreen { +class CMSWindowsScreen : public CPlatformScreen { public: CMSWindowsScreen(bool isPrimary, IJob* suspend, IJob* resume); virtual ~CMSWindowsScreen(); @@ -57,23 +57,6 @@ public: //@} - // IPlatformScreen overrides - virtual void setKeyState(IKeyState*); - virtual void enable(); - virtual void disable(); - virtual void enter(); - virtual bool leave(); - virtual bool setClipboard(ClipboardID, const IClipboard*); - virtual void checkClipboards(); - virtual void openScreensaver(bool notify); - virtual void closeScreensaver(); - virtual void screensaver(bool activate); - virtual void resetOptions(); - virtual void setOptions(const COptionsList& options); - virtual void updateKeys(); - virtual void setSequenceNumber(UInt32); - virtual bool isPrimary() const; - // IScreen overrides virtual void* getEventTarget() const; virtual bool getClipboard(ClipboardID id, IClipboard*) const; @@ -86,34 +69,38 @@ public: virtual void warpCursor(SInt32 x, SInt32 y); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; - virtual KeyModifierMask getActiveModifiers() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; - virtual const char* getKeyName(KeyButton) const; // ISecondaryScreen overrides - virtual void fakeKeyEvent(KeyButton id, bool press) const; - virtual bool fakeCtrlAltDel() const; virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; virtual void fakeMouseWheel(SInt32 delta) const; - virtual KeyButton mapKey(IKeyState::Keystrokes&, - const IKeyState& keyState, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const; + + // IKeyState overrides + virtual void updateKeys(); + + // IPlatformScreen overrides + virtual void enable(); + virtual void disable(); + virtual void enter(); + virtual bool leave(); + virtual bool setClipboard(ClipboardID, const IClipboard*); + virtual void checkClipboards(); + virtual void openScreensaver(bool notify); + virtual void closeScreensaver(); + virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual void setSequenceNumber(UInt32); + virtual bool isPrimary() const; + +protected: + // IPlatformScreen overrides + virtual void handleSystemEvent(const CEvent&, void*); + virtual void updateButtons(); + virtual IKeyState* getKeyState() const; private: - class CDesk { - public: - CString m_name; - CThread* m_thread; - DWORD m_threadID; - DWORD m_targetID; - HDESK m_desk; - HWND m_window; - bool m_lowLevel; - }; - typedef std::map CDesks; - // initialization and shutdown operations HINSTANCE openHookLibrary(const char* name); void closeHookLibrary(HINSTANCE hookLibrary) const; @@ -129,9 +116,6 @@ private: void sendEvent(CEvent::Type type, void* = NULL); void sendClipboardEvent(CEvent::Type type, ClipboardID id); - // system event handler (does DispatchMessage) - void handleSystemEvent(const CEvent& event, void*); - // handle message before it gets dispatched. returns true iff // the message should not be dispatched. bool onPreDispatch(HWND, UINT, WPARAM, LPARAM); @@ -169,42 +153,15 @@ private: // enable/disable special key combinations so we can catch/pass them void enableSpecialKeys(bool) const; - // send fake key up if shadow state says virtualKey is down but - // system says it isn't. - void fixKey(UINT virtualKey); - - // map a button ID and action to a mouse event - DWORD mapButtonToEvent(ButtonID button, - bool press, DWORD* inData) const; - // map a button event to a button ID ButtonID mapButtonFromEvent(WPARAM msg, LPARAM button) const; - // return true iff the given virtual key is a modifier - bool isModifier(UINT vkCode) const; - - // send ctrl+alt+del hotkey event - static void ctrlAltDelThread(void*); + // job to update the key state + void updateKeysCB(void*); // our window proc static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); - // our desk window procs - static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM); - static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM); - - void deskMouseMove(SInt32 x, SInt32 y) const; - void deskEnter(CDesk* desk); - void deskLeave(CDesk* desk, HKL keyLayout); - void deskThread(void* vdesk); - CDesk* addDesk(const CString& name, HDESK hdesk); - void removeDesks(); - void checkDesk(); - bool isDeskAccessible(const CDesk* desk) const; - void sendDeskMessage(UINT, WPARAM, LPARAM) const; - void waitForDesk() const; - void handleCheckDesk(const CEvent& event, void*); - private: static HINSTANCE s_instance; @@ -219,8 +176,6 @@ private: // our resources ATOM m_class; - ATOM m_deskClass; - HCURSOR m_cursor; // screen shape stuff SInt32 m_x, m_y; @@ -245,9 +200,6 @@ private: // the keyboard layout to use when off primary screen HKL m_keyLayout; - // the timer used to check for desktop switching - CEventQueueTimer* m_timer; - // screen saver stuff CMSWindowsScreenSaver* m_screensaver; bool m_screensaverNotify; @@ -258,33 +210,22 @@ private: HWND m_nextClipboardWindow; bool m_ownClipboard; - // the current desk and it's name - CDesk* m_activeDesk; - CString m_activeDeskName; - // one desk per desktop and a cond var to communicate with it - CMutex m_mutex; - CCondVar m_deskReady; - CDesks m_desks; + CMSWindowsDesks* m_desks; // hook library stuff HINSTANCE m_hookLibrary; InitFunc m_init; CleanupFunc m_cleanup; - InstallFunc m_install; - UninstallFunc m_uninstall; SetSidesFunc m_setSides; SetZoneFunc m_setZone; SetModeFunc m_setMode; - InstallScreenSaverFunc m_installScreensaver; - UninstallScreenSaverFunc m_uninstallScreensaver; // keyboard stuff - IKeyState* m_keyState; - CMSWindowsKeyMapper m_keyMapper; + CMSWindowsKeyState* m_keyState; // map of button state - BYTE m_buttons[1 + kButtonExtra0 + 1]; + bool m_buttons[1 + kButtonExtra0 + 1]; // suspend/resume callbacks IJob* m_suspend; diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index f7003c4a..955dc2ae 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -219,8 +219,8 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) // and ctrl+backspace to delete. we don't want those translations // so clear the control modifier state. however, if we want to // simulate AltGr (which is ctrl+alt) then we must not clear it. - BYTE control = keys[VK_CONTROL]; - BYTE menu = keys[VK_MENU]; + UINT control = keys[VK_CONTROL] | keys[VK_LCONTROL] | keys[VK_RCONTROL]; + UINT menu = keys[VK_MENU] | keys[VK_LMENU] | keys[VK_RMENU]; if ((control & 0x80) == 0 || (menu & 0x80) == 0) { keys[VK_LCONTROL] = 0; keys[VK_RCONTROL] = 0; @@ -228,8 +228,10 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) } else { keys[VK_LCONTROL] = 0x80; + keys[VK_RCONTROL] = 0x80; keys[VK_CONTROL] = 0x80; keys[VK_LMENU] = 0x80; + keys[VK_RMENU] = 0x80; keys[VK_MENU] = 0x80; } @@ -237,7 +239,7 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) // we don't know and there doesn't appear to be any way to find // out. so we'll just assume a menu is active if the menu key // is down. - // XXX -- figure out some way to check if a menu is active + // FIXME -- figure out some way to check if a menu is active UINT flags = 0; if ((menu & 0x80) != 0) flags |= 1; diff --git a/lib/platform/CXWindowsKeyState.cpp b/lib/platform/CXWindowsKeyState.cpp index 0397ce3c..27b23d9a 100644 --- a/lib/platform/CXWindowsKeyState.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -174,6 +174,13 @@ CXWindowsKeyState::mapModifiersFromX(unsigned int state) const return mask; } +bool +CXWindowsKeyState::fakeCtrlAltDel() +{ + // pass keys through unchanged + return false; +} + const char* CXWindowsKeyState::getKeyName(KeyButton keycode) const { @@ -864,7 +871,7 @@ CXWindowsKeyState::findBestKeyIndex(KeySymIndex keyIndex, // if the action is an auto-repeat then we don't call this // method since we just need to synthesize a key repeat on the // same keycode that we pressed. - // XXX -- do this right + // FIXME -- do this right for (unsigned int i = 0; i < 4; ++i) { if (keyIndex->second.m_keycode[i] != 0) { return i; diff --git a/lib/platform/CXWindowsKeyState.h b/lib/platform/CXWindowsKeyState.h index 98458228..36abeeb2 100644 --- a/lib/platform/CXWindowsKeyState.h +++ b/lib/platform/CXWindowsKeyState.h @@ -50,6 +50,7 @@ public: //@} // IKeyState overrides + virtual bool fakeCtrlAltDel(); virtual const char* getKeyName(KeyButton) const; protected: diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 2531ba8e..fe368d13 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -241,9 +241,8 @@ CXWindowsScreen::enable() // warp the mouse to the cursor center fakeMouseMove(m_xCenter, m_yCenter); } - else { - m_keyState->updateKeys(); - } + + updateKeys(); } void @@ -545,13 +544,6 @@ CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const y = m_yCenter; } -bool -CXWindowsScreen::fakeCtrlAltDel() const -{ - // pass keys through unchanged - return false; -} - void CXWindowsScreen::fakeMouseButton(ButtonID button, bool press) const { diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index 41b42a75..c7be5bb4 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -53,7 +53,6 @@ public: virtual void getCursorCenter(SInt32& x, SInt32& y) const; // ISecondaryScreen overrides - virtual bool fakeCtrlAltDel() const; virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; virtual void fakeMouseWheel(SInt32 delta) const; diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index 123e513b..a5036971 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -103,7 +103,7 @@ SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp # End Source File # Begin Source File -SOURCE=.\CMSWindowsDesktop.cpp +SOURCE=.\CMSWindowsDesks.cpp # End Source File # Begin Source File @@ -111,7 +111,7 @@ SOURCE=.\CMSWindowsEventQueueBuffer.cpp # End Source File # Begin Source File -SOURCE=.\CMSWindowsKeyMapper.cpp +SOURCE=.\CMSWindowsKeyState.cpp # End Source File # Begin Source File @@ -147,7 +147,7 @@ SOURCE=.\CMSWindowsClipboardUTF16Converter.h # End Source File # Begin Source File -SOURCE=.\CMSWindowsDesktop.h +SOURCE=.\CMSWindowsDesks.h # End Source File # Begin Source File @@ -155,7 +155,7 @@ SOURCE=.\CMSWindowsEventQueueBuffer.h # End Source File # Begin Source File -SOURCE=.\CMSWindowsKeyMapper.h +SOURCE=.\CMSWindowsKeyState.h # End Source File # Begin Source File diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index b870b968..7aa040c1 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -294,7 +294,7 @@ UInt32 CServer::getActivePrimarySides() const { UInt32 sides = 0; - if (!isLockedToScreen()) { + if (!isLockedToScreenServer()) { if (getNeighbor(m_primaryClient, kLeft) != NULL) { sides |= kLeftMask; } @@ -312,7 +312,7 @@ CServer::getActivePrimarySides() const } bool -CServer::isLockedToScreen() const +CServer::isLockedToScreenServer() const { // locked if scroll-lock is toggled on if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) { @@ -320,6 +320,18 @@ CServer::isLockedToScreen() const return true; } + // not locked + return false; +} + +bool +CServer::isLockedToScreen() const +{ + // locked if we say we're locked + if (isLockedToScreenServer()) { + return true; + } + // locked if primary says we're locked if (m_primaryClient->isLockedToScreen()) { return true; diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 874449e1..286eab0e 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -120,6 +120,12 @@ private: UInt32 getActivePrimarySides() const; // returns true iff mouse should be locked to the current screen + // according to this object only, ignoring what the primary client + // says. + bool isLockedToScreenServer() const; + + // returns true iff mouse should be locked to the current screen + // according to this object or the primary client. bool isLockedToScreen() const; // returns the jump zone of the client diff --git a/lib/synergy/CKeyState.cpp b/lib/synergy/CKeyState.cpp index 9cd80de5..5dffa9d0 100644 --- a/lib/synergy/CKeyState.cpp +++ b/lib/synergy/CKeyState.cpp @@ -39,14 +39,7 @@ void CKeyState::setKeyDown(KeyButton button, bool down) { button &= kButtonMask; - if (button != 0) { - if (down) { - m_keys[button] |= kDown; - } - else { - m_keys[button] &= ~kDown; - } - } + updateKeyState(button, button, down); } void @@ -144,7 +137,8 @@ CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) // get the sequence of keys to simulate key press and the final // modifier state. Keystrokes keys; - KeyButton localID = (mapKey(keys, id, mask, false) & kButtonMask); + KeyButton localID = + (KeyButton)(mapKey(keys, id, mask, false) & kButtonMask); if (keys.empty()) { // do nothing if there are no associated keys LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); @@ -155,7 +149,7 @@ CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) fakeKeyEvents(keys, 1); // note that key is down - updateKeyState(button & kButtonMask, localID, true); + updateKeyState((KeyButton)(button & kButtonMask), localID, true); } void @@ -174,7 +168,7 @@ CKeyState::fakeKeyRepeat( // get the sequence of keys to simulate key repeat and the final // modifier state. Keystrokes keys; - KeyButton localID = (mapKey(keys, id, mask, true) & kButtonMask); + KeyButton localID = (KeyButton)(mapKey(keys, id, mask, true) & kButtonMask); if (localID == 0) { LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); return; @@ -511,12 +505,16 @@ CKeyState::updateKeyState(KeyButton serverID, KeyButton localID, bool press) KeyModifierMask mask = m_keyToMask[localID]; if (mask != 0) { if (isToggle(mask)) { - m_keys[localID] ^= kToggled; - m_mask ^= mask; - // never report half-duplex keys as down if (isHalfDuplex(mask)) { m_keys[localID] &= ~kDown; + press = true; + } + + // toggle on the press + if (press) { + m_keys[localID] ^= kToggled; + m_mask ^= mask; } } else { diff --git a/lib/synergy/CKeyState.h b/lib/synergy/CKeyState.h index a53cba3f..0ed89f2d 100644 --- a/lib/synergy/CKeyState.h +++ b/lib/synergy/CKeyState.h @@ -33,9 +33,10 @@ public: //! Mark key as being down /*! - Sets the state of \p button to down or up. + Sets the state of \p button to down or up. If this is overridden + it must forward to the superclass. */ - void setKeyDown(KeyButton button, bool down); + virtual void setKeyDown(KeyButton button, bool down); //! Mark modifier as being toggled on /*! @@ -47,9 +48,10 @@ public: //! Post a key event /*! Posts a key event. This may adjust the event or post additional - events in some circumstances. + events in some circumstances. If this is overridden it must forward + to the superclass. */ - void sendKeyEvent(void* target, + virtual void sendKeyEvent(void* target, bool press, bool isAutoRepeat, KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button); @@ -69,6 +71,7 @@ public: SInt32 count, KeyButton button); virtual void fakeKeyUp(KeyButton button); virtual void fakeToggle(KeyModifierMask modifier); + virtual bool fakeCtrlAltDel() = 0; virtual bool isKeyDown(KeyButton) const; virtual KeyModifierMask getActiveModifiers() const; diff --git a/lib/synergy/CPlatformScreen.cpp b/lib/synergy/CPlatformScreen.cpp index 95e0fe77..ea0760a5 100644 --- a/lib/synergy/CPlatformScreen.cpp +++ b/lib/synergy/CPlatformScreen.cpp @@ -63,6 +63,12 @@ CPlatformScreen::fakeToggle(KeyModifierMask modifier) getKeyState()->fakeToggle(modifier); } +bool +CPlatformScreen::fakeCtrlAltDel() +{ + return getKeyState()->fakeCtrlAltDel(); +} + bool CPlatformScreen::isKeyDown(KeyButton button) const { diff --git a/lib/synergy/CPlatformScreen.h b/lib/synergy/CPlatformScreen.h index 68071fbb..b5fcde01 100644 --- a/lib/synergy/CPlatformScreen.h +++ b/lib/synergy/CPlatformScreen.h @@ -43,7 +43,6 @@ public: virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; // ISecondaryScreen overrides - virtual bool fakeCtrlAltDel() const = 0; virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; virtual void fakeMouseWheel(SInt32 delta) const = 0; @@ -57,6 +56,7 @@ public: SInt32 count, KeyButton button); virtual void fakeKeyUp(KeyButton button); virtual void fakeToggle(KeyModifierMask modifier); + virtual bool fakeCtrlAltDel(); virtual bool isKeyDown(KeyButton) const; virtual KeyModifierMask getActiveModifiers() const; diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index 9c4594da..787e72ba 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -98,13 +98,13 @@ CScreen::enter(KeyModifierMask toggleMask) // now on screen m_entered = true; + m_screen->enter(); if (m_isPrimary) { enterPrimary(); } else { enterSecondary(toggleMask); } - m_screen->enter(); } bool @@ -315,6 +315,16 @@ CScreen::isLockedToScreen() const return true; } +// note -- we don't lock to the screen if a key is down. key +// reporting is simply not reliable enough to trust. the effect +// of switching screens with a key down is that the client will +// receive key repeats and key releases for keys that it hasn't +// see go down. that's okay because CKeyState will ignore those +// events. the user might be surprised that any modifier keys +// held while crossing to another screen don't apply on the +// target screen. if that ends up being a problem we can try +// to synthesize a key press for those modifiers on entry. +/* // check for any pressed key KeyButton key = isAnyKeyDown(); if (key != 0) { @@ -332,6 +342,7 @@ CScreen::isLockedToScreen() const LOG((CLOG_DEBUG "spuriously locked by %s", m_screen->getKeyName(key))); } } +*/ // not locked return false; @@ -431,9 +442,6 @@ CScreen::enterPrimary() void CScreen::enterSecondary(KeyModifierMask toggleMask) { - // update our keyboard state to reflect the local state - m_screen->updateKeys(); - // remember toggle key state. we'll restore this when we leave. m_toggleKeys = getActiveModifiers(); diff --git a/lib/synergy/IKeyState.h b/lib/synergy/IKeyState.h index 7d0b8f63..04e7be8e 100644 --- a/lib/synergy/IKeyState.h +++ b/lib/synergy/IKeyState.h @@ -87,6 +87,13 @@ public: */ virtual void fakeToggle(KeyModifierMask modifier) = 0; + //! Fake ctrl+alt+del + /*! + Synthesize a press of ctrl+alt+del. Return true if processing is + complete and false if normal key processing should continue. + */ + virtual bool fakeCtrlAltDel() = 0; + //@} //! @name accessors //@{ diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index b97f3453..13bd0a31 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -151,7 +151,6 @@ public: virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; // ISecondaryScreen overrides - virtual bool fakeCtrlAltDel() const = 0; virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; virtual void fakeMouseWheel(SInt32 delta) const = 0; @@ -165,6 +164,7 @@ public: SInt32 count, KeyButton button) = 0; virtual void fakeKeyUp(KeyButton button) = 0; virtual void fakeToggle(KeyModifierMask modifier) = 0; + virtual bool fakeCtrlAltDel() = 0; virtual bool isKeyDown(KeyButton) const = 0; virtual KeyModifierMask getActiveModifiers() const = 0; diff --git a/lib/synergy/ISecondaryScreen.h b/lib/synergy/ISecondaryScreen.h index 65ac7708..1311e7af 100644 --- a/lib/synergy/ISecondaryScreen.h +++ b/lib/synergy/ISecondaryScreen.h @@ -28,13 +28,6 @@ public: //! @name accessors //@{ - //! Fake ctrl+alt+del - /*! - Synthesize a press of ctrl+alt+del. Return true if processing is - complete and false if normal key processing should continue. - */ - virtual bool fakeCtrlAltDel() const = 0; - //! Fake mouse press/release /*! Synthesize a press or release of mouse button \c id. diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index a0f80876..f0fd4cc9 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -91,10 +91,18 @@ SOURCE=.\CClipboard.cpp # End Source File # Begin Source File +SOURCE=.\CKeyState.cpp +# End Source File +# Begin Source File + SOURCE=.\CPacketStreamFilter.cpp # End Source File # Begin Source File +SOURCE=.\CPlatformScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\CProtocolUtil.cpp # End Source File # Begin Source File @@ -107,6 +115,10 @@ SOURCE=.\IClipboard.cpp # End Source File # Begin Source File +SOURCE=.\IKeyState.cpp +# End Source File +# Begin Source File + SOURCE=.\IPrimaryScreen.cpp # End Source File # Begin Source File @@ -131,6 +143,10 @@ SOURCE=.\CClipboard.h # End Source File # Begin Source File +SOURCE=.\CKeyState.h +# End Source File +# Begin Source File + SOURCE=.\ClipboardTypes.h # End Source File # Begin Source File @@ -139,6 +155,10 @@ SOURCE=.\CPacketStreamFilter.h # End Source File # Begin Source File +SOURCE=.\CPlatformScreen.h +# End Source File +# Begin Source File + SOURCE=.\CProtocolUtil.h # End Source File # Begin Source File From d2f4cbf0026f35613385659decb5cc3f8ee4bed9 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Mar 2004 14:05:31 +0000 Subject: [PATCH 613/807] Fixed bugs in installing per-user startup programs on windows 95 family. --- cmd/launcher/CAutoStart.cpp | 7 ++++++- cmd/launcher/launcher.rc | 8 +++++--- cmd/launcher/resource.h | 1 + lib/arch/CArchDaemonWindows.cpp | 8 ++++---- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp index 13910634..66fb3fd0 100644 --- a/cmd/launcher/CAutoStart.cpp +++ b/cmd/launcher/CAutoStart.cpp @@ -122,7 +122,12 @@ CAutoStart::update() // update messages CString msg, label; if (canInstallSystem) { - msg = getString(IDS_AUTOSTART_PERMISSION_SYSTEM); + if (canInstallUser) { + msg = getString(IDS_AUTOSTART_PERMISSION_ALL); + } + else { + msg = getString(IDS_AUTOSTART_PERMISSION_SYSTEM); + } } else if (canInstallUser) { msg = getString(IDS_AUTOSTART_PERMISSION_USER); diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 0a5ceb01..9d4b6ca5 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -319,7 +319,7 @@ END STRINGTABLE DISCARDABLE BEGIN IDS_AUTOSTART_PERMISSION_SYSTEM - "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself." + "You have sufficient access rights to install and uninstall Auto Start for all users." IDS_AUTOSTART_PERMISSION_USER "You have sufficient access rights to install and uninstall Auto Start for just yourself." IDS_AUTOSTART_PERMISSION_NONE @@ -335,8 +335,8 @@ BEGIN IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}" IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}" IDS_INSTALL_TITLE "Installed Auto-Start" - IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." - IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." + IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will automatically start each time you start your computer." + IDS_INSTALLED_USER "Installed auto-start. Synergy will automatically start each time you log in." END STRINGTABLE DISCARDABLE @@ -349,6 +349,8 @@ BEGIN IDS_ADD_SCREEN "Add Screen" IDS_EDIT_SCREEN "Edit Screen %{1}" IDS_ERROR_CODE "Error code: %{1}" + IDS_AUTOSTART_PERMISSION_ALL + "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself." END #endif // English (U.S.) resources diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index 3ef98665..3044c2d5 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -42,6 +42,7 @@ #define IDS_EDIT_SCREEN 38 #define IDS_INVALID_TIME 39 #define IDS_ERROR_CODE 39 +#define IDS_AUTOSTART_PERMISSION_ALL 40 #define IDD_MAIN 101 #define IDD_ADD 102 #define IDD_WAIT 103 diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index edca9385..d702cb99 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -88,7 +88,7 @@ CArchDaemonWindows::installDaemon(const char* name, // key. if windows 95 family then use windows 95 services key. if (!allUsers || CArchMiscWindows::isWindows95Family()) { // open registry - HKEY key = CArchMiscWindows::isWindows95Family() ? + HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? open95ServicesKey() : openUserStartupKey(); if (key == NULL) { // can't open key @@ -191,7 +191,7 @@ CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) // key. if windows 95 family then use windows 95 services key. if (!allUsers || CArchMiscWindows::isWindows95Family()) { // open registry - HKEY key = CArchMiscWindows::isWindows95Family() ? + HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? open95ServicesKey() : openUserStartupKey(); if (key == NULL) { // can't open key. daemon is probably not installed. @@ -322,7 +322,7 @@ CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) // key. if windows 95 family then use windows 95 services key. if (!allUsers || CArchMiscWindows::isWindows95Family()) { // check if we can open the registry key - HKEY key = CArchMiscWindows::isWindows95Family() ? + HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? open95ServicesKey() : openUserStartupKey(); CArchMiscWindows::closeKey(key); return (key != NULL); @@ -354,7 +354,7 @@ CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers) // key. if windows 95 family then use windows 95 services key. if (!allUsers || CArchMiscWindows::isWindows95Family()) { // check if we can open the registry key - HKEY key = CArchMiscWindows::isWindows95Family() ? + HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? open95ServicesKey() : openUserStartupKey(); if (key == NULL) { return false; From bf7643faab0274ceb4c4cd8eb5c3bef449d4468f Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Mar 2004 14:05:52 +0000 Subject: [PATCH 614/807] Changed debug logging of key IDs to use hex. --- lib/client/CServerProxy.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index b6568365..37c44f10 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -510,7 +510,7 @@ CServerProxy::keyDown() // parse UInt16 id, mask, button; CProtocolUtil::readf(m_stream, kMsgDKeyDown + 4, &id, &mask, &button); - LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); + LOG((CLOG_DEBUG1 "recv key down id=0x%08x, mask=0x%04x, button=0x%04x", id, mask, button)); // translate KeyID id2 = translateKey(static_cast(id)); @@ -518,7 +518,7 @@ CServerProxy::keyDown() static_cast(mask)); if (id2 != static_cast(id) || mask2 != static_cast(mask)) - LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); + LOG((CLOG_DEBUG1 "key down translated to id=0x%08x, mask=0x%04x", id2, mask2)); // forward m_client->keyDown(id2, mask2, button); @@ -534,7 +534,7 @@ CServerProxy::keyRepeat() UInt16 id, mask, count, button; CProtocolUtil::readf(m_stream, kMsgDKeyRepeat + 4, &id, &mask, &count, &button); - LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button)); + LOG((CLOG_DEBUG1 "recv key repeat id=0x%08x, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button)); // translate KeyID id2 = translateKey(static_cast(id)); @@ -542,7 +542,7 @@ CServerProxy::keyRepeat() static_cast(mask)); if (id2 != static_cast(id) || mask2 != static_cast(mask)) - LOG((CLOG_DEBUG1 "key repeat translated to id=%d, mask=0x%04x", id2, mask2)); + LOG((CLOG_DEBUG1 "key repeat translated to id=0x%08x, mask=0x%04x", id2, mask2)); // forward m_client->keyRepeat(id2, mask2, count, button); @@ -557,7 +557,7 @@ CServerProxy::keyUp() // parse UInt16 id, mask, button; CProtocolUtil::readf(m_stream, kMsgDKeyUp + 4, &id, &mask, &button); - LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); + LOG((CLOG_DEBUG1 "recv key up id=0x%08x, mask=0x%04x, button=0x%04x", id, mask, button)); // translate KeyID id2 = translateKey(static_cast(id)); @@ -565,7 +565,7 @@ CServerProxy::keyUp() static_cast(mask)); if (id2 != static_cast(id) || mask2 != static_cast(mask)) - LOG((CLOG_DEBUG1 "key up translated to id=%d, mask=0x%04x", id2, mask2)); + LOG((CLOG_DEBUG1 "key up translated to id=0x%08x, mask=0x%04x", id2, mask2)); // forward m_client->keyUp(id2, mask2, button); From 0f7fa2dfd99b193f3945334ee87b0a1ab95bc406 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Mar 2004 14:06:40 +0000 Subject: [PATCH 615/807] Fixed windows 95 family screen saver stuff. --- lib/platform/CMSWindowsScreenSaver.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index 89090009..6a821281 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -27,7 +27,7 @@ #endif static const TCHAR* g_isSecureNT = "ScreenSaverIsSecure"; -static const TCHAR* g_isSecure9x = "ScreenSaverUsePassword"; +static const TCHAR* g_isSecure9x = "ScreenSaveUsePassword"; static const TCHAR* const g_pathScreenSaverIsSecure[] = { "Control Panel", "Desktop", @@ -162,6 +162,7 @@ CMSWindowsScreenSaver::activate() { // don't activate if already active if (!isActive()) { + // activate HWND hwnd = GetForegroundWindow(); if (hwnd != NULL) { PostMessage(hwnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0); @@ -451,12 +452,14 @@ CMSWindowsScreenSaver::isSecure(bool* wasSecureFlagAnInt) const switch (CArchMiscWindows::typeOfValue(hkey, isSecure)) { default: result = false; + break; case CArchMiscWindows::kUINT: { DWORD value = CArchMiscWindows::readValueInt(hkey, isSecure); *wasSecureFlagAnInt = true; result = (value != 0); + break; } case CArchMiscWindows::kSTRING: { @@ -464,6 +467,7 @@ CMSWindowsScreenSaver::isSecure(bool* wasSecureFlagAnInt) const CArchMiscWindows::readValueString(hkey, isSecure); *wasSecureFlagAnInt = false; result = (value != "0"); + break; } } From e50146119f5f69843b5bf7da32f8a86ba2d9c113 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Mar 2004 14:07:37 +0000 Subject: [PATCH 616/807] Updated makefile to reflect renaming of platform files for win32. --- lib/platform/Makefile.am | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 9245adee..54e70b29 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -23,9 +23,9 @@ EXTRA_DIST = \ CMSWindowsClipboardAnyTextConverter.cpp \ CMSWindowsClipboardTextConverter.cpp \ CMSWindowsClipboardUTF16Converter.cpp \ - CMSWindowsDesktop.cpp \ + CMSWindowsDesks.cpp \ CMSWindowsEventQueueBuffer.cpp \ - CMSWindowsKeyMapper.cpp \ + CMSWindowsKeyState.cpp \ CMSWindowsScreen.cpp \ CMSWindowsScreenSaver.cpp \ CMSWindowsUtil.cpp \ @@ -34,9 +34,9 @@ EXTRA_DIST = \ CMSWindowsClipboardAnyTextConverter.h \ CMSWindowsClipboardTextConverter.h \ CMSWindowsClipboardUTF16Converter.h \ - CMSWindowsDesktop.h \ + CMSWindowsDesks.h \ CMSWindowsEventQueueBuffer.h \ - CMSWindowsKeyMapper.h \ + CMSWindowsKeyState.h \ CMSWindowsScreen.h \ CMSWindowsScreenSaver.h \ CMSWindowsUtil.h \ From 97046541b9d9667e874b90f90bd1ae1a5d0a2989 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Mar 2004 14:07:58 +0000 Subject: [PATCH 617/807] Fixed keyboard handling on windows 95 family. --- lib/platform/CMSWindowsDesks.cpp | 39 ++- lib/platform/CMSWindowsKeyState.cpp | 423 +++++++++++++++++----------- lib/platform/CMSWindowsKeyState.h | 9 +- lib/platform/CMSWindowsScreen.cpp | 178 ++++++++++-- lib/platform/CMSWindowsScreen.h | 14 + lib/platform/CSynergyHook.cpp | 57 +++- lib/platform/CSynergyHook.h | 3 +- lib/synergy/CKeyState.h | 5 +- 8 files changed, 519 insertions(+), 209 deletions(-) diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index 0e2b7734..28fcca22 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -13,7 +13,6 @@ */ #include "CMSWindowsDesks.h" -#include "CMSWindowsDesktop.h" #include "CMSWindowsScreen.h" #include "IScreenSaver.h" #include "XScreen.h" @@ -200,6 +199,27 @@ CMSWindowsDesks::fakeKeyEvent( KeyButton button, UINT virtualKey, bool press, bool /*isAutoRepeat*/) const { + // win 95 family doesn't understand handed modifier virtual keys + if (m_is95Family) { + switch (virtualKey) { + case VK_LSHIFT: + case VK_RSHIFT: + virtualKey = VK_SHIFT; + break; + + case VK_LCONTROL: + case VK_RCONTROL: + virtualKey = VK_CONTROL; + break; + + case VK_LMENU: + case VK_RMENU: + virtualKey = VK_MENU; + break; + } + } + + // synthesize event DWORD flags = 0; if (((button & 0x100u) != 0)) { flags |= KEYEVENTF_EXTENDEDKEY; @@ -552,6 +572,14 @@ CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout) SetWindowPos(desk->m_window, HWND_TOPMOST, x, y, w, h, SWP_NOACTIVATE | SWP_SHOWWINDOW); + // if not using low-level hooks we have to also activate the + // window to ensure we don't lose keyboard focus. + // FIXME -- see if this can be avoided. if so then always + // disable the window (see handling of SYNERGY_MSG_SWITCH). + if (!desk->m_lowLevel) { + SetActiveWindow(desk->m_window); + } + // switch to requested keyboard layout ActivateKeyboardLayout(keyLayout, 0); } @@ -594,11 +622,6 @@ CMSWindowsDesks::deskThread(void* vdesk) // ignore LOG((CLOG_DEBUG "can't create desk window for %s", desk->m_name.c_str())); } - - // a window on the primary screen should never activate - if (m_isPrimary && desk->m_window != NULL) { - EnableWindow(desk->m_window, FALSE); - } } // tell main thread that we're ready @@ -636,6 +659,10 @@ CMSWindowsDesks::deskThread(void* vdesk) desk->m_lowLevel = true; break; } + + // a window on the primary screen with low-level hooks + // should never activate. + EnableWindow(desk->m_window, desk->m_lowLevel ? FALSE : TRUE); } break; diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index 1d9554d7..427323f9 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -573,7 +573,8 @@ const KeyID CMSWindowsKeyState::s_virtualKey[][2] = /* 0xff */ kKeyNone, kKeyNone // reserved }; -// map special KeyID keys to virtual key codes +// map special KeyID keys to virtual key codes plus whether or not +// the key maps to an extended scan code const UINT CMSWindowsKeyState::s_mapE000[] = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -597,15 +598,15 @@ const UINT CMSWindowsKeyState::s_mapE000[] = /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */ 0, 0, 0, 0, - /* 0xa4 */ 0, 0, VK_BROWSER_BACK, VK_BROWSER_FORWARD, - /* 0xa8 */ VK_BROWSER_REFRESH, VK_BROWSER_STOP, - /* 0xaa */ VK_BROWSER_SEARCH, VK_BROWSER_FAVORITES, - /* 0xac */ VK_BROWSER_HOME, VK_VOLUME_MUTE, - /* 0xae */ VK_VOLUME_DOWN, VK_VOLUME_UP, - /* 0xb0 */ VK_MEDIA_NEXT_TRACK, VK_MEDIA_PREV_TRACK, - /* 0xb2 */ VK_MEDIA_STOP, VK_MEDIA_PLAY_PAUSE, - /* 0xb4 */ VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, - /* 0xb6 */ VK_LAUNCH_APP1, VK_LAUNCH_APP2, + /* 0xa4 */ 0, 0, VK_BROWSER_BACK | 0x100u, VK_BROWSER_FORWARD | 0x100u, + /* 0xa8 */ VK_BROWSER_REFRESH | 0x100u, VK_BROWSER_STOP | 0x100u, + /* 0xaa */ VK_BROWSER_SEARCH | 0x100u, VK_BROWSER_FAVORITES | 0x100u, + /* 0xac */ VK_BROWSER_HOME | 0x100u, VK_VOLUME_MUTE | 0x100u, + /* 0xae */ VK_VOLUME_DOWN | 0x100u, VK_VOLUME_UP | 0x100u, + /* 0xb0 */ VK_MEDIA_NEXT_TRACK | 0x100u, VK_MEDIA_PREV_TRACK | 0x100u, + /* 0xb2 */ VK_MEDIA_STOP | 0x100u, VK_MEDIA_PLAY_PAUSE | 0x100u, + /* 0xb4 */ VK_LAUNCH_MAIL | 0x100u, VK_LAUNCH_MEDIA_SELECT | 0x100u, + /* 0xb6 */ VK_LAUNCH_APP1 | 0x100u, VK_LAUNCH_APP2 | 0x100u, /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -666,35 +667,41 @@ const UINT CMSWindowsKeyState::s_mapEF00[] = /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, - /* 0x54 */ VK_DOWN, VK_PRIOR, VK_NEXT, VK_END, + /* 0x50 */ VK_HOME | 0x100u, VK_LEFT | 0x100u, + /* 0x52 */ VK_UP | 0x100u, VK_RIGHT | 0x100u, + /* 0x54 */ VK_DOWN | 0x100u, VK_PRIOR | 0x100u, + /* 0x56 */ VK_NEXT | 0x100u, VK_END | 0x100u, /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT, - /* 0x64 */ 0, 0, 0, VK_APPS, - /* 0x68 */ 0, 0, VK_HELP, VK_CANCEL, 0, 0, 0, 0, + /* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT | 0x100u, + /* 0x64 */ 0, 0, 0, VK_APPS | 0x100u, + /* 0x68 */ 0, 0, VK_HELP, VK_CANCEL | 0x100u, 0, 0, 0, 0, /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK | 0x100u, /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN, 0, 0, + /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN | 0x100u, 0, 0, /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, /* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, - /* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE, + /* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE | 0x100u, /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, /* 0xc0 */ VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, - /* 0xc8 */ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, - /* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0, + /* 0xc8 */ VK_F11, VK_F12, VK_F13 | 0x100u, VK_F14 | 0x100u, + /* 0xcc */ VK_F15 | 0x100u, VK_F16 | 0x100u, + /* 0xce */ VK_F17 | 0x100u, VK_F18 | 0x100u, + /* 0xd0 */ VK_F19 | 0x100u, VK_F20 | 0x100u, + /* 0xd2 */ VK_F21 | 0x100u, VK_F22 | 0x100u, + /* 0xd4 */ VK_F23 | 0x100u, VK_F24 | 0x100u, 0, 0, /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, - /* 0xe4 */ VK_RCONTROL, VK_CAPITAL, 0, 0, - /* 0xe8 */ 0, VK_LMENU, VK_RMENU, VK_LWIN, - /* 0xec */ VK_RWIN, 0, 0, 0, + /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT | 0x100u, VK_LCONTROL, + /* 0xe4 */ VK_RCONTROL | 0x100u, VK_CAPITAL, 0, 0, + /* 0xe8 */ 0, VK_LMENU, VK_RMENU | 0x100u, VK_LWIN | 0x100u, + /* 0xec */ VK_RWIN | 0x100u, 0, 0, 0, /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE | 0x100u }; CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks) : @@ -733,7 +740,7 @@ CMSWindowsKeyState::fixKey(void* target, UINT virtualKey) KeyID key = mapKeyFromEvent(virtualKey, lParam, &mask); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button); - CKeyState::setKeyDown(button, false); + setKeyDown(button, false); } } @@ -741,6 +748,7 @@ KeyID CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, KeyModifierMask* maskOut) const { +// FIXME -- look into this // note: known microsoft bugs // Q72583 -- MapVirtualKey() maps keypad keys incorrectly // 95,98: num pad vk code -> invalid scan code @@ -837,27 +845,6 @@ CMSWindowsKeyState::virtualKeyToButton(UINT virtualKey) const return m_virtKeyToScanCode[virtualKey & 0xffu]; } -void -CMSWindowsKeyState::setKeyDown(KeyButton button, bool down) -{ - CKeyState::setKeyDown(button, down); - - // special case: we detect ctrl+alt+del being pressed on some - // systems but we don't detect the release of those keys. so - // if ctrl, alt, and del are down then mark them up. - if (down && isKeyDown(m_virtKeyToScanCode[VK_DELETE])) { - KeyModifierMask mask = getActiveModifiers(); - if ((mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - CKeyState::setKeyDown(m_virtKeyToScanCode[VK_LCONTROL], false); - CKeyState::setKeyDown(m_virtKeyToScanCode[VK_RCONTROL], false); - CKeyState::setKeyDown(m_virtKeyToScanCode[VK_LMENU], false); - CKeyState::setKeyDown(m_virtKeyToScanCode[VK_RMENU], false); - CKeyState::setKeyDown(m_virtKeyToScanCode[VK_DELETE], false); - } - } -} - void CMSWindowsKeyState::sendKeyEvent(void* target, bool press, bool isAutoRepeat, @@ -865,7 +852,7 @@ CMSWindowsKeyState::sendKeyEvent(void* target, SInt32 count, KeyButton button) { if (press || isAutoRepeat) { - // if AltGr required for this key then make sure + // if AltGr is required for this key then make sure // the ctrl and alt keys are *not* down on the // client. windows simulates AltGr with ctrl and // alt for some inexplicable reason and clients @@ -950,37 +937,6 @@ CMSWindowsKeyState::sendKeyEvent(void* target, } } else { - // key release. if the key isn't down according to - // our table then we never got the key press event - // for it. if it's not a modifier key then we'll - // synthesize the press first. only do this on - // the windows 95 family, which eats certain special - // keys like alt+tab, ctrl+esc, etc. - if (m_is95Family && isKeyDown(button)) { - switch (m_scanCodeToVirtKey[button]) { - case VK_LSHIFT: - case VK_RSHIFT: - case VK_SHIFT: - case VK_LCONTROL: - case VK_RCONTROL: - case VK_CONTROL: - case VK_LMENU: - case VK_RMENU: - case VK_MENU: - case VK_CAPITAL: - case VK_NUMLOCK: - case VK_SCROLL: - case VK_LWIN: - case VK_RWIN: - break; - - default: - CKeyState::sendKeyEvent(target, - true, false, key, mask, 1, button); - break; - } - } - // do key up CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button); } @@ -1065,47 +1021,35 @@ CMSWindowsKeyState::doUpdateKeys() // KeyModifierModeSwitch mask can be converted to keystrokes. it // must be mapped before the Alt modifier so that the Alt modifier // takes precedence when mapping keystrokes to modifier masks. - // - // we have to explicitly set the extended key flag for some - // modifiers because the win32 API is inadequate. KeyButtons keys; - keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100)); + keys.push_back(mapVirtKeyToButton(VK_RMENU)); addModifier(KeyModifierModeSwitch, keys); keys.clear(); - keys.push_back((KeyButton)MapVirtualKey(VK_LSHIFT, 0)); - keys.push_back((KeyButton)(MapVirtualKey(VK_RSHIFT, 0) | 0x100)); + keys.push_back(mapVirtKeyToButton(VK_LSHIFT)); + keys.push_back(mapVirtKeyToButton(VK_RSHIFT)); addModifier(KeyModifierShift, keys); keys.clear(); - keys.push_back((KeyButton)MapVirtualKey(VK_LCONTROL, 0)); - keys.push_back((KeyButton)(MapVirtualKey(VK_RCONTROL, 0) | 0x100)); + keys.push_back(mapVirtKeyToButton(VK_LCONTROL)); + keys.push_back(mapVirtKeyToButton(VK_RCONTROL)); addModifier(KeyModifierControl, keys); keys.clear(); - keys.push_back((KeyButton)MapVirtualKey(VK_LMENU, 0)); - keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100)); + keys.push_back(mapVirtKeyToButton(VK_LMENU)); + keys.push_back(mapVirtKeyToButton(VK_RMENU)); addModifier(KeyModifierAlt, keys); keys.clear(); - keys.push_back((KeyButton)(MapVirtualKey(VK_LWIN, 0) | 0x100)); - keys.push_back((KeyButton)(MapVirtualKey(VK_RWIN, 0) | 0x100)); + keys.push_back(mapVirtKeyToButton(VK_LWIN)); + keys.push_back(mapVirtKeyToButton(VK_RWIN)); addModifier(KeyModifierSuper, keys); keys.clear(); - keys.push_back((KeyButton)MapVirtualKey(VK_CAPITAL, 0)); + keys.push_back(mapVirtKeyToButton(VK_CAPITAL)); addModifier(KeyModifierCapsLock, keys); keys.clear(); - keys.push_back((KeyButton)(MapVirtualKey(VK_NUMLOCK, 0) | 0x100)); + keys.push_back(mapVirtKeyToButton(VK_NUMLOCK)); addModifier(KeyModifierNumLock, keys); keys.clear(); - keys.push_back((KeyButton)MapVirtualKey(VK_SCROLL, 0)); + keys.push_back(mapVirtKeyToButton(VK_SCROLL)); addModifier(KeyModifierScrollLock, keys); -/* FIXME -- potential problem here on win me - // win me (sony vaio laptop): - // MapVirtualKey(vk, 0): - // VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped - // VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped - // VK_MENU mapped; VK_LMENU, VK_RMENU not mapped - // MapVirtualKey(sc, 3): - // all scan codes unmapped (function apparently unimplemented) -*/ BYTE keyState[256]; GetKeyboardState(keyState); for (UINT i = 1; i < 256; ++i) { @@ -1123,63 +1067,20 @@ CMSWindowsKeyState::doUpdateKeys() } // map to a scancode and back to a virtual key - UINT scancode = MapVirtualKey(i, 0); - UINT virtKey = MapVirtualKey(scancode, 3); - if (virtKey == 0) { - // assume MapVirtualKey(xxx, 3) is unimplemented - virtKey = i; - } - else if (scancode == 0) { - // the VK_PAUSE virtual key doesn't map properly - if (i == VK_PAUSE) { - // i hope this works on all keyboards - scancode = 0x45; - virtKey = i; - } - else { - continue; - } - } - - // we need some adjustments due to inadequacies in the API. - // the API provides no means to query the keyboard by scan - // code that i can see. so we're doing it by virtual key. - // but a single virtual key can map to multiple physical - // keys. for example, VK_HOME maps to NumPad 7 and to the - // (extended key) Home key. this means we can never tell - // which of the two keys is pressed. - // - // this is a problem if a key is down when this method is - // called. if the extended key is down we'll record the - // non-extended key as being down. when the extended key - // goes up, we'll record that correctly and leave the - // non-extended key as being down. to deal with that we - // always re-check the keyboard state if we think we're - // locked to a screen because a key is down. the re-check - // should clear it up. - // - // the win32 functions that take scan codes are: - - // - // if the mapped virtual key doesn't match the starting - // point then there's a really good chance that that virtual - // key is mapped to an extended key. however, this is not - // the case for modifiers that don't distinguish between left - // and right. also VK_NUMLOCK gets mapped to a non-extended - // key but it should be. - if (virtKey != i || i == VK_NUMLOCK) { - if (i != VK_SHIFT && i != VK_CONTROL && i != VK_MENU) { - scancode |= 0x100; - } + KeyButton button2; + KeyButton button = mapVirtKeyToButton(i, button2); + if (button == 0) { + continue; } // okay, now we have the scan code for the virtual key. - m_scanCodeToVirtKey[scancode] = i; - m_virtKeyToScanCode[i] = (KeyButton)scancode; + m_scanCodeToVirtKey[button] = i; + m_scanCodeToVirtKey[button2] = i; + m_virtKeyToScanCode[i] = button; // save the key state if ((keyState[i] & 0x80) != 0) { - setKeyDown((KeyButton)scancode, true); + setKeyDown(button, true); } // toggle state applies to all keys but we only want it for @@ -1214,27 +1115,27 @@ KeyButton CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, KeyModifierMask mask, bool isAutoRepeat) const { - UINT virtualKey = 0; + UINT extVirtualKey = 0; // check for special keys if ((id & 0xfffff000u) == 0xe000u) { if ((id & 0xff00u) == 0xe000u) { - virtualKey = s_mapE000[id & 0xffu]; + extVirtualKey = s_mapE000[id & 0xffu]; } else if ((id & 0xff00) == 0xee00) { - virtualKey = s_mapEE00[id & 0xffu]; + extVirtualKey = s_mapEE00[id & 0xffu]; } else if ((id & 0xff00) == 0xef00) { - virtualKey = s_mapEF00[id & 0xffu]; + extVirtualKey = s_mapEF00[id & 0xffu]; } - if (virtualKey == 0) { + if (extVirtualKey == 0) { LOG((CLOG_DEBUG2 "unknown special key")); return 0; } } // special handling of VK_SNAPSHOT - if (virtualKey == VK_SNAPSHOT) { + if (extVirtualKey == VK_SNAPSHOT) { // ignore key repeats on print screen if (!isAutoRepeat) { // active window (with alt) or fullscreen (without alt)? @@ -1251,7 +1152,7 @@ CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, } // handle other special keys - if (virtualKey != 0) { + if (extVirtualKey != 0) { // compute required modifiers KeyModifierMask requiredMask = 0; KeyModifierMask outMask = 0; @@ -1262,6 +1163,7 @@ CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, // (e.g. add, multiply) and their main keyboard counterparts. // therefore, we can ignore the num-lock state for movement virtual // keys but not for numeric keys. + UINT virtualKey = (extVirtualKey & 0xffu); if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) { requiredMask |= KeyModifierNumLock; if ((getActiveModifiers() & KeyModifierNumLock) != 0) { @@ -1277,9 +1179,12 @@ CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, } // now generate the keystrokes and return the resulting modifier mask - KeyButton scanCode = m_virtKeyToScanCode[virtualKey]; - LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d scan code 0x%04x mask 0x%04x", id, virtualKey, scanCode, outMask)); - return mapToKeystrokes(keys, scanCode, + KeyButton button = m_virtKeyToScanCode[virtualKey]; + if ((extVirtualKey & 0x100u) != 0) { + button |= 0x100u; + } + LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d scan code 0x%03x mask 0x%04x", id, virtualKey, button, outMask)); + return mapToKeystrokes(keys, button, outMask, requiredMask, isAutoRepeat); } @@ -1407,6 +1312,196 @@ CMSWindowsKeyState::getCodePageFromLangID(LANGID langid) const return codePage; } +KeyButton +CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey, + KeyButton& extended) const +{ + // this method does what MapVirtualKey(virtualKey, 0) should do. + // we have to explicitly set the extended key flag for some + // modifiers because the win32 API is inadequate. we also find + // the unextended and the extended scancodes for those virtual + // keys that have both except for VK_SHIFT, VK_CONTROL, and VK_MENU. + // + // the windows 95 family doesn't map the side distinguishing virtual + // keys. but we know that VK_CONTROL maps to VK_LCONTROL and + // that VK_RCONTROL is the same scan code | 0x100. similarly for + // VK_MENU. but VK_RSHIFT cannot be determined that way so we + // search for it. + extended = 0; + KeyButton button; + if (m_is95Family) { + UINT scancode; + switch (virtualKey) { + case VK_LSHIFT: + button = (KeyButton)MapVirtualKey(VK_SHIFT, 0); + break; + + case VK_RSHIFT: + // we have to search + scancode = MapVirtualKey(VK_SHIFT, 0); + for (UINT i = 1; i < 256; ++i) { + if (i != scancode && MapVirtualKey(i, 1) == VK_SHIFT) { + return (KeyButton)(i); + } + } + return 0; + + case VK_LCONTROL: + case VK_RCONTROL: + button = (KeyButton)MapVirtualKey(VK_CONTROL, 0); + break; + + case VK_LMENU: + case VK_RMENU: + button = (KeyButton)MapVirtualKey(VK_MENU, 0); + break; + + case VK_PAUSE: + // mapped to 0. i hope this works on all keyboards. + button = (KeyButton)0x45u; + break; + + case VK_DIVIDE: + // mapped to 0. i hope this works on all keyboards. + button = (KeyButton)0x35u; + break; + + default: + button = (KeyButton)MapVirtualKey(virtualKey, 0); + break; + } + } + else { + switch (virtualKey) { + case VK_PAUSE: + // mapped to 0. i hope this works on all keyboards. + button = (KeyButton)0x45u; + break; + + default: + button = (KeyButton)MapVirtualKey(virtualKey, 0); + break; + } + } + + // map extended keys + switch (virtualKey) { + case VK_RETURN: // Return/numpad Enter + case VK_PRIOR: // numpad PageUp/PageUp + case VK_NEXT: // numpad PageDown/PageDown + case VK_END: // numpad End/End + case VK_HOME: // numpad Home/Home + case VK_LEFT: // numpad Left/Left + case VK_UP: // numpad Up/Up + case VK_RIGHT: // numpad Right/Right + case VK_DOWN: // numpad Down/Down + case VK_INSERT: // numpad Insert/Insert + case VK_DELETE: // numpad Delete/Delete +// case VK_SELECT: +// case VK_EXECUTE: +// case VK_HELP: + extended = (KeyButton)(button | 0x100u); + break; + } + + // see if the win32 API can help us determine an extended key. + // if the remapped virtual key doesn't match the starting + // point then there's a really good chance that that virtual + // key is mapped to an extended key. however, this is not + // the case for modifiers that don't distinguish between left + // and right. + UINT virtualKey2 = MapVirtualKey(button, 3); + if (virtualKey2 != 0 && virtualKey2 != virtualKey) { + switch (virtualKey) { + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + break; + + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: + case VK_MULTIPLY: + case VK_ADD: + case VK_SEPARATOR: + case VK_SUBTRACT: + case VK_DECIMAL: + break; + + default: + button |= 0x100u; + extended = 0; + break; + } + return button; + } + + // note other extended keys that the win32 API won't help us with. + // on the windows 95 family this is the only way to find extended + // keys since MapVirtualKey(N, 3) is unimplemented. + switch (virtualKey) { + case VK_CANCEL: + case VK_LWIN: + case VK_RWIN: + case VK_APPS: +// case VK_SEPARATOR: + case VK_DIVIDE: + case VK_F13: + case VK_F14: + case VK_F15: + case VK_F16: + case VK_F17: + case VK_F18: + case VK_F19: + case VK_F20: + case VK_F21: + case VK_F22: + case VK_F23: + case VK_F24: + case VK_NUMLOCK: + case VK_RSHIFT: + case VK_RCONTROL: + case VK_RMENU: + case VK_BROWSER_BACK: + case VK_BROWSER_FORWARD: + case VK_BROWSER_REFRESH: + case VK_BROWSER_STOP: + case VK_BROWSER_SEARCH: + case VK_BROWSER_FAVORITES: + case VK_BROWSER_HOME: + case VK_VOLUME_MUTE: + case VK_VOLUME_DOWN: + case VK_VOLUME_UP: + case VK_MEDIA_NEXT_TRACK: + case VK_MEDIA_PREV_TRACK: + case VK_MEDIA_STOP: + case VK_MEDIA_PLAY_PAUSE: + case VK_LAUNCH_MAIL: + case VK_LAUNCH_MEDIA_SELECT: + case VK_LAUNCH_APP1: + case VK_LAUNCH_APP2: + button |= 0x100u; + extended = 0; + break; + } + + return button; +} + +KeyButton +CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey) const +{ + KeyButton dummy; + return mapVirtKeyToButton(virtualKey, dummy); +} + KeyButton CMSWindowsKeyState::mapCharacter(Keystrokes& keys, char c, HKL hkl, bool isAutoRepeat) const diff --git a/lib/platform/CMSWindowsKeyState.h b/lib/platform/CMSWindowsKeyState.h index cc888103..d4c7e1e0 100644 --- a/lib/platform/CMSWindowsKeyState.h +++ b/lib/platform/CMSWindowsKeyState.h @@ -64,7 +64,6 @@ public: //@} // IKeyState overrides - virtual void setKeyDown(KeyButton button, bool down); virtual void sendKeyEvent(void* target, bool press, bool isAutoRepeat, KeyID key, KeyModifierMask mask, @@ -88,6 +87,14 @@ private: // convert a language ID to a code page UINT getCodePageFromLangID(LANGID langid) const; + // map a virtual key to a button. this tries to deal with the + // broken win32 API as best it can. + KeyButton mapVirtKeyToButton(UINT virtualKey, + KeyButton& extended) const; + + // same as above and discard extended + KeyButton mapVirtKeyToButton(UINT virtualKey) const; + // map character \c c given keyboard layout \c hkl to the keystrokes // to generate it. KeyButton mapCharacter(Keystrokes& keys, diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index e6213f6b..8f627928 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -83,6 +83,7 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_mark(0), m_markReceived(0), m_keyLayout(NULL), + m_fixTimer(NULL), m_screensaver(NULL), m_screensaverNotify(false), m_nextClipboardWindow(NULL), @@ -218,6 +219,13 @@ CMSWindowsScreen::disable() CArchMiscWindows::kDISPLAY); } + // uninstall fix key timer + if (m_fixTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); + EVENTQUEUE->deleteTimer(m_fixTimer); + m_fixTimer = NULL; + } + // stop snooping the clipboard ChangeClipboardChain(m_window, m_nextClipboardWindow); m_nextClipboardWindow = NULL; @@ -680,6 +688,10 @@ CMSWindowsScreen::onPreDispatch(HWND hwnd, switch (message) { case SYNERGY_MSG_SCREEN_SAVER: return onScreensaver(wParam != 0); + + case SYNERGY_MSG_DEBUG: + LOG((CLOG_INFO "hook: 0x%08x 0x%08x", wParam, lParam)); + return true; } if (m_isPrimary) { @@ -693,27 +705,6 @@ bool CMSWindowsScreen::onPreDispatchPrimary(HWND, UINT message, WPARAM wParam, LPARAM lParam) { - // fake a key release for the windows keys if we think they're - // down but they're really up. we have to do this because if the - // user presses and releases a windows key without pressing any - // other key while it's down then the system will eat the key - // release. if we don't detect that and synthesize the release - // then the client won't take the usual windows key release action - // (which on windows is to show the start menu). - // - // since the key could go up at any time we'll check the state on - // every event. only check on the windows 95 family since the NT - // family reports the key release as usual. obviously we skip - // this if the event is for a windows key itself. - if (m_is95Family && message != SYNERGY_MSG_KEY) { - if (wParam != VK_LWIN) { - m_keyState->fixKey(getEventTarget(), VK_LWIN); - } - if (wParam != VK_RWIN) { - m_keyState->fixKey(getEventTarget(), VK_RWIN); - } - } - // handle event switch (message) { case SYNERGY_MSG_MARK: @@ -840,22 +831,67 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) { LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, lParam)); - // update key state. ignore key repeats. + // fix up key state + fixKeys(); + + // get key info KeyButton button = (KeyButton)((lParam & 0x01ff0000) >> 16); - if ((lParam & 0xc0000000u) == 0x00000000) { + bool down = ((lParam & 0xc0000000u) == 0x00000000u); + bool up = ((lParam & 0x80000000u) == 0x80000000u); + bool wasDown = isKeyDown(button); + + // the windows keys are a royal pain on the windows 95 family. + // the system eats the key up events if and only if the windows + // key wasn't combined with another key, i.e. it was tapped. + // fixKeys() and scheduleFixKeys() are all about synthesizing + // the missing key up. but even windows itself gets a little + // confused and sets bit 30 in lParam if you tap the windows + // key twice. that bit means the key was previously down and + // that makes some sense since the up event was missing. + // anyway, on the windows 95 family we forget about windows + // key repeats and treat anything that's not a key down as a + // key up. + if (m_is95Family && + ((wParam & 0xffu) == VK_LWIN || (wParam & 0xffu) == VK_RWIN)) { + down = !up; + } + + // update key state. ignore key repeats. + if (down) { m_keyState->setKeyDown(button, true); } - else if ((lParam & 0x80000000u) == 0x80000000) { + else if (up) { m_keyState->setKeyDown(button, false); } + // schedule a timer if we need to fix keys later + scheduleFixKeys(); + + // special case: we detect ctrl+alt+del being pressed on some + // systems but we don't detect the release of those keys. so + // if ctrl, alt, and del are down then mark them up. + KeyModifierMask mask = getActiveModifiers(); + bool ctrlAlt = ((mask & (KeyModifierControl | KeyModifierAlt)) == + (KeyModifierControl | KeyModifierAlt)); + if (down && ctrlAlt && + isKeyDown(m_keyState->virtualKeyToButton(VK_DELETE))) { + m_keyState->setKeyDown( + m_keyState->virtualKeyToButton(VK_LCONTROL), false); + m_keyState->setKeyDown( + m_keyState->virtualKeyToButton(VK_RCONTROL), false); + m_keyState->setKeyDown( + m_keyState->virtualKeyToButton(VK_LMENU), false); + m_keyState->setKeyDown( + m_keyState->virtualKeyToButton(VK_RMENU), false); + m_keyState->setKeyDown( + m_keyState->virtualKeyToButton(VK_DELETE), false); + } + // ignore message if posted prior to last mark change if (!ignore()) { // check for ctrl+alt+del emulation UINT virtKey = (wParam & 0xffu); - if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) && - (m_keyState->getActiveModifiers() & - (KeyModifierControl | KeyModifierAlt)) != 0) { + if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) && ctrlAlt) { LOG((CLOG_DEBUG "emulate ctrl+alt+del")); // switch wParam and lParam to be as if VK_DELETE was // pressed or released @@ -870,9 +906,41 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) KeyID key = m_keyState->mapKeyFromEvent(wParam, lParam, &mask); button = static_cast((lParam & 0x01ff0000u) >> 16); if (key != kKeyNone) { + // fix up key. if the key isn't down according to + // our table then we never got the key press event + // for it. if it's not a modifier key then we'll + // synthesize the press first. only do this on + // the windows 95 family, which eats certain special + // keys like alt+tab, ctrl+esc, etc. + if (m_is95Family && !wasDown && up) { + switch (virtKey) { + case VK_LSHIFT: + case VK_RSHIFT: + case VK_SHIFT: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + case VK_LMENU: + case VK_RMENU: + case VK_MENU: + case VK_CAPITAL: + case VK_NUMLOCK: + case VK_SCROLL: + case VK_LWIN: + case VK_RWIN: + break; + + default: + m_keyState->sendKeyEvent(getEventTarget(), + true, false, key, mask, 1, button); + break; + } + } + + // do it m_keyState->sendKeyEvent(getEventTarget(), - ((lParam & 0x80000000) == 0), - ((lParam & 0x40000000) == 1), + ((lParam & 0x80000000u) == 0), + ((lParam & 0x40000000u) == 1), key, mask, (SInt32)(lParam & 0xffff), button); } else { @@ -1225,6 +1293,58 @@ CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const } } +void +CMSWindowsScreen::fixKeys() +{ + // fake key releases for the windows keys if we think they're + // down but they're really up. we have to do this because if the + // user presses and releases a windows key without pressing any + // other key while it's down then the system will eat the key + // release. if we don't detect that and synthesize the release + // then the client won't take the usual windows key release action + // (which on windows is to show the start menu). + // + // only check on the windows 95 family since the NT family reports + // the key releases as usual. + if (m_is95Family) { + m_keyState->fixKey(getEventTarget(), VK_LWIN); + m_keyState->fixKey(getEventTarget(), VK_RWIN); + + // check if we need the fix timer anymore + scheduleFixKeys(); + } +} + +void +CMSWindowsScreen::scheduleFixKeys() +{ + if (m_is95Family) { + // see if any keys that need fixing are down + bool fix = + (m_keyState->isKeyDown(m_keyState->virtualKeyToButton(VK_LWIN)) || + m_keyState->isKeyDown(m_keyState->virtualKeyToButton(VK_RWIN))); + + // start or stop fix timer + if (fix && m_fixTimer == NULL) { + m_fixTimer = EVENTQUEUE->newTimer(0.1, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer, + new TMethodEventJob( + this, &CMSWindowsScreen::handleFixKeys)); + } + else if (!fix && m_fixTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); + EVENTQUEUE->deleteTimer(m_fixTimer); + m_fixTimer = NULL; + } + } +} + +void +CMSWindowsScreen::handleFixKeys(const CEvent&, void*) +{ + fixKeys(); +} + void CMSWindowsScreen::updateKeysCB(void*) { diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 8a43c833..e9e51a06 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -23,6 +23,7 @@ #define WIN32_LEAN_AND_MEAN #include +class CEventQueueTimer; class CMSWindowsDesks; class CMSWindowsKeyState; class CMSWindowsScreenSaver; @@ -156,6 +157,16 @@ private: // map a button event to a button ID ButtonID mapButtonFromEvent(WPARAM msg, LPARAM button) const; + // fix the key state, synthesizing fake key releases for keys + // that aren't down anymore. + void fixKeys(); + + // (un)schedule a later call to fixKeys + void scheduleFixKeys(); + + // event handler to fix the key state + void handleFixKeys(const CEvent&, void*); + // job to update the key state void updateKeysCB(void*); @@ -200,6 +211,9 @@ private: // the keyboard layout to use when off primary screen HKL m_keyLayout; + // the timer used to check for fixing key state + CEventQueueTimer* m_fixTimer; + // screen saver stuff CMSWindowsScreenSaver* m_screensaver; bool m_screensaverNotify; diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 955dc2ae..0fb0b79c 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -94,6 +94,7 @@ static SInt32 g_wScreen = 0; static SInt32 g_hScreen = 0; static WPARAM g_deadVirtKey = 0; static LPARAM g_deadLParam = 0; +static WPARAM g_oldDeadVirtKey = 0; static BYTE g_deadKeyState[256] = { 0 }; static DWORD g_hookThread = 0; static DWORD g_attachedThread = 0; @@ -185,21 +186,44 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) // check for dead keys. we don't forward those to our window. // instead we'll leave the key in the keyboard layout (a buffer // internal to the system) for translation when the next key is - // pressed. + // pressed. note that some systems set bit 31 to indicate a + // dead key and others bit 15. nice. UINT c = MapVirtualKey(wParam, 2); - if ((c & 0x80000000u) != 0) { + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | 0x00000000, c); + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | (c << 8) | 0x01000000, lParam); + if ((c & 0x80008000u) != 0) { if ((lParam & 0x80000000u) == 0) { if (g_deadVirtKey == 0) { // dead key press, no dead key in the buffer g_deadVirtKey = wParam; g_deadLParam = lParam; keyboardGetState(g_deadKeyState); + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | 0x02000000, lParam); return false; } // second dead key press in a row so let it pass + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | 0x03000000, lParam); + } + else if (wParam == g_oldDeadVirtKey) { + // dead key release for second dead key in a row. discard + // because we've already handled it. also take it out of + // the keyboard buffer. + g_oldDeadVirtKey = 0; + WORD c; + UINT scanCode = ((lParam & 0x00ff0000u) >> 16); + ToAscii(wParam, scanCode, g_deadKeyState, &c, 0); + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | 0x09000000, lParam); + return true; } else { // dead key release + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | 0x04000000, lParam); return false; } } @@ -256,6 +280,8 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) // alt are being used as individual modifiers rather than AltGr. // we have to put the dead key back first, if there was one. if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) { + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | 0x05000000, lParam); if (g_deadVirtKey != 0) { ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16, g_deadKeyState, &c, flags); @@ -269,6 +295,9 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) n = ToAscii(wParam, scanCode, keys, &c, flags); } + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | (c << 8) | ((n & 0xff) << 16) | 0x06000000, + lParam); switch (n) { default: // key is a dead key; we're not expecting this since we @@ -307,6 +336,9 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) if (g_deadVirtKey != 0) { ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16, g_deadKeyState, &c, flags); + for (int i = 0; i < 256; ++i) { + g_deadKeyState[i] = 0; + } } // clear out old dead key state @@ -318,12 +350,17 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) // forwarding events to clients because this'll keep our thread's // key state table up to date. that's important for querying // the scroll lock toggle state. + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + charAndVirtKey | 0x07000000, lParam); PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, charAndVirtKey, lParam); // send fake key release if the user just pressed two dead keys // in a row, otherwise we'll lose the release because we always // return from the top of this function for all dead key releases. - if ((c & 0x80000000u) != 0) { + if ((c & 0x80008000u) != 0) { + g_oldDeadVirtKey = wParam; + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | 0x08000000, lParam); PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, charAndVirtKey, lParam | 0x80000000u); } @@ -342,6 +379,14 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) case VK_SHIFT: case VK_LSHIFT: case VK_RSHIFT: + // pass the shift modifiers. if we don't do this + // we may not get the right dead key when caps lock + // is on. for example, on the french layout (with + // english keycaps) on caps lock then press shift + [ + // and q. instead of an A with ^ above it you get an + // A with dots above it. + break; + case VK_CONTROL: case VK_LCONTROL: case VK_RCONTROL: @@ -349,8 +394,8 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) case VK_LMENU: case VK_RMENU: case VK_HANGUL: - // always pass the shift modifiers - break; + // discard the control and alt modifiers + return true; default: // discard @@ -787,12 +832,14 @@ install() g_hinstance, 0); } +#if !NO_GRAB_KEYBOARD if (g_keyboardLL == NULL) { g_keyboard = SetWindowsHookEx(WH_KEYBOARD, &keyboardHook, g_hinstance, 0); } +#endif // check that we got all the hooks we wanted if ((g_getMessage == NULL && g_wheelSupport == kWheelOld) || diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h index a9ab2b0c..045d96e7 100644 --- a/lib/platform/CSynergyHook.h +++ b/lib/platform/CSynergyHook.h @@ -38,9 +38,10 @@ #define SYNERGY_MSG_POST_WARP WM_APP + 0x0016 // ; #define SYNERGY_MSG_PRE_WARP WM_APP + 0x0017 // x; y #define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; +#define SYNERGY_MSG_DEBUG WM_APP + 0x0019 // data, data #define SYNERGY_MSG_INPUT_FIRST SYNERGY_MSG_KEY #define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP -#define SYNERGY_HOOK_LAST_MSG SYNERGY_MSG_SCREEN_SAVER +#define SYNERGY_HOOK_LAST_MSG SYNERGY_MSG_DEBUG extern "C" { diff --git a/lib/synergy/CKeyState.h b/lib/synergy/CKeyState.h index 0ed89f2d..00c28eb4 100644 --- a/lib/synergy/CKeyState.h +++ b/lib/synergy/CKeyState.h @@ -33,10 +33,9 @@ public: //! Mark key as being down /*! - Sets the state of \p button to down or up. If this is overridden - it must forward to the superclass. + Sets the state of \p button to down or up. */ - virtual void setKeyDown(KeyButton button, bool down); + void setKeyDown(KeyButton button, bool down); //! Mark modifier as being toggled on /*! From cfb082f306af29dba5831abdfd27d3cef3e640db Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Mar 2004 14:53:01 +0000 Subject: [PATCH 618/807] Added ISO_Level3_Shift as a synonym for Mode_switch. I've no idea if this will work as hoped but I've seen documentation that XFree 4.3 uses ISO_Level3_Shift rather than Mode_switch. --- lib/platform/CXWindowsKeyState.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/platform/CXWindowsKeyState.cpp b/lib/platform/CXWindowsKeyState.cpp index 27b23d9a..b4efddcb 100644 --- a/lib/platform/CXWindowsKeyState.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -554,6 +554,7 @@ CXWindowsKeyState::mapToModifierMask(unsigned int i, KeySym keysym) return KeyModifierSuper; case XK_Mode_switch: + case XK_ISO_Level3_Shift: m_modeSwitchMask = (1 << i); return KeyModifierModeSwitch; @@ -585,6 +586,7 @@ CXWindowsKeyState::updateModifiers() }; static const CModifierBitInfo s_modifierBitTable[] = { { &CXWindowsKeyState::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol }, + { &CXWindowsKeyState::m_modeSwitchKeysym, XK_ISO_Level3_Shift, NoSymbol }, }; // choose the keysym to use for some modifiers. if a modifier has From 29633720898ffc07ab8f76223ba1c5bf18c0c089 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Mar 2004 20:00:00 +0000 Subject: [PATCH 619/807] Added win32 installer project. --- dist/Makefile.am | 1 + dist/nullsoft/Makefile.am | 27 +++++++ dist/nullsoft/dosify.c | 99 +++++++++++++++++++++++ dist/nullsoft/installer.dsp | 124 +++++++++++++++++++++++++++++ dist/nullsoft/installer.mak | 28 +++++++ dist/nullsoft/synergy.nsi | 152 ++++++++++++++++++++++++++++++++++++ dist/rpm/Makefile.am | 1 + synergy.dsw | 12 +++ 8 files changed, 444 insertions(+) create mode 100644 dist/nullsoft/Makefile.am create mode 100644 dist/nullsoft/dosify.c create mode 100644 dist/nullsoft/installer.dsp create mode 100755 dist/nullsoft/installer.mak create mode 100644 dist/nullsoft/synergy.nsi diff --git a/dist/Makefile.am b/dist/Makefile.am index bf906cbf..b23a284e 100644 --- a/dist/Makefile.am +++ b/dist/Makefile.am @@ -17,6 +17,7 @@ VDEPTH = ./$(VPATH)/$(DEPTH) SUBDIRS = \ rpm \ + nullsoft \ $(NULL) EXTRA_DIST = \ diff --git a/dist/nullsoft/Makefile.am b/dist/nullsoft/Makefile.am new file mode 100644 index 00000000..b45cc611 --- /dev/null +++ b/dist/nullsoft/Makefile.am @@ -0,0 +1,27 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = +DEPTH = ../.. +VDEPTH = ./$(VPATH)/$(DEPTH) + +EXTRA_DIST = \ + installer.dsp \ + installer.mak \ + synergy.nsi \ + dosify.c \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) diff --git a/dist/nullsoft/dosify.c b/dist/nullsoft/dosify.c new file mode 100644 index 00000000..95d0caee --- /dev/null +++ b/dist/nullsoft/dosify.c @@ -0,0 +1,99 @@ +#include +#include +#include + +static +char* +concatPath(const char* dir, const char* name, const char* ext) +{ + size_t nDir = (dir != NULL) ? strlen(dir) : 0; + size_t nPath = nDir + 1 + strlen(name) + strlen(ext?ext:"") + 1; + char* path = malloc(nPath); + + /* directory */ + if (nDir > 0 && strcmp(dir, ".") != 0) { + strcpy(path, dir); + if (path[nDir - 1] != '\\' && path[nDir - 1] != '/') { + strcat(path, "\\"); + } + } + else { + strcpy(path, ""); + } + + + /* name */ + strcat(path, name); + + /* extension */ + if (ext != NULL && strrchr(name, '.') == NULL) { + strcat(path, ext); + } + + return path; +} + +static +int +dosify(const char* srcdir, const char* dstdir, const char* name) +{ + FILE* dFile, *sFile; + char* dName, *sName; + + sName = concatPath(srcdir, name, NULL); + dName = concatPath(dstdir, name, ".txt"); + + sFile = fopen(sName, "rb"); + if (sFile == NULL) { + fprintf(stderr, "Can't open \"%s\" for reading\n", sName); + return 0; + } + else { + dFile = fopen(dName, "w"); + if (dFile == NULL) { + fclose(sFile); + fprintf(stderr, "Can't open \"%s\" for writing\n", dName); + return 0; + } + else { + char buffer[1024]; + while (!ferror(dFile) && + fgets(buffer, sizeof(buffer), sFile) != NULL) { + fprintf(dFile, "%s", buffer); + } + if (ferror(sFile) || ferror(dFile)) { + fprintf(stderr, + "Error copying \"%s\" to \"%s\"\n", sName, dName); + fclose(dFile); + fclose(sFile); + _unlink(dName); + return 0; + } + } + } + + fclose(dFile); + fclose(sFile); + free(dName); + free(sName); + return 1; +} + +#include +int +main(int argc, char** argv) +{ + int i; + + if (argc < 3) { + fprintf(stderr, "usage: %s [files]\n", argv[0]); + return 1; + } + + for (i = 3; i < argc; ++i) { + if (!dosify(argv[1], argv[2], argv[i])) + return 1; + } + + return 0; +} diff --git a/dist/nullsoft/installer.dsp b/dist/nullsoft/installer.dsp new file mode 100644 index 00000000..c101f53f --- /dev/null +++ b/dist/nullsoft/installer.dsp @@ -0,0 +1,124 @@ +# Microsoft Developer Studio Project File - Name="installer" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=installer - Win32 Debug +!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 "installer.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 "installer.mak" CFG="installer - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "installer - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "installer - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "installer - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "installer___Win32_Release" +# PROP BASE Intermediate_Dir "installer___Win32_Release" +# PROP BASE Cmd_Line "NMAKE /f installer.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "installer.exe" +# PROP BASE Bsc_Name "installer.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "." +# PROP Cmd_Line "nmake /nologo /f "installer.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\..\SynergyInstaller.exe" +# PROP Bsc_Name "" +# PROP Target_Dir "" +NSIS=makensis + +!ELSEIF "$(CFG)" == "installer - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "installer___Win32_Debug" +# PROP BASE Intermediate_Dir "installer___Win32_Debug" +# PROP BASE Cmd_Line "NMAKE /f installer.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "installer.exe" +# PROP BASE Bsc_Name "installer.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "." +# PROP Cmd_Line "echo "Can only build installer for Release configuration"" +# PROP Rebuild_Opt "" +# PROP Target_File "dummy.exe" +# PROP Bsc_Name "" +# PROP Target_Dir "" +NSIS=makensis + +!ENDIF + +# Begin Target + +# Name "installer - Win32 Release" +# Name "installer - Win32 Debug" + +!IF "$(CFG)" == "installer - Win32 Release" + +!ELSEIF "$(CFG)" == "installer - Win32 Debug" + +!ENDIF + +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\dosify.c +# PROP Intermediate_Dir "." +# End Source File +# Begin Source File + +SOURCE=.\installer.mak + +!IF "$(CFG)" == "installer - Win32 Release" + +# PROP Intermediate_Dir "." + +!ELSEIF "$(CFG)" == "installer - Win32 Debug" + +# PROP Intermediate_Dir "." + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\synergy.nsi +# PROP Intermediate_Dir "." +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/dist/nullsoft/installer.mak b/dist/nullsoft/installer.mak new file mode 100755 index 00000000..30d4d205 --- /dev/null +++ b/dist/nullsoft/installer.mak @@ -0,0 +1,28 @@ +NULL= +DEPTH=..\.. + +NSIS="D:\Program Files\NSIS\makensis" + +DOCS = \ + AUTHORS \ + BUGS \ + COPYING \ + ChangeLog \ + FAQ \ + HISTORY \ + INSTALL \ + NEWS \ + README \ + TODO \ + $(NULL) + +default: dosifydocs installer + +installer: + $(NSIS) synergy.nsi + +dosifydocs: dosify.exe + .\dosify.exe $(DEPTH) . $(DOCS) + +dosify.exe: dosify.c + $(CC) /nologo /Yd /Zi /MLd /Fe$@ $** diff --git a/dist/nullsoft/synergy.nsi b/dist/nullsoft/synergy.nsi new file mode 100644 index 00000000..00c5885f --- /dev/null +++ b/dist/nullsoft/synergy.nsi @@ -0,0 +1,152 @@ +; Synergy.nsi +; +; This script is based on example1.nsi, but it remember the directory, +; has uninstall support and (optionally) installs start menu shortcuts. +; +; It will install makensisw.exe into a directory that the user selects, + +;-------------------------------- + +; Path to root of tree +!define DEPTH "..\.." + +; The name of the installer +Name "Synergy" + +; The file to write +OutFile "${DEPTH}/SynergyInstaller.exe" + +; The default installation directory +InstallDir $PROGRAMFILES\Synergy + +; Registry key to check for directory (so if you install again, it will +; overwrite the old one automatically) +InstallDirRegKey HKLM "Software\Synergy" "Install_Dir" + +;-------------------------------- + +; Pages + +Page license +Page components +Page directory +Page instfiles + +UninstPage uninstConfirm +UninstPage instfiles + +;-------------------------------- + +; Text +ComponentText "This this install Synergy on your computer. Select the optional components you want to install." +DirText "Choose a directory to install Synergy to:" +UninstallText "This will uninstall Synergy from your computer." +LicenseText "Synergy is distributed under the GNU GPL:" +LicenseData "COPYING.txt" + +;-------------------------------- + +; The stuff to install +Section "Synergy (required)" + + SectionIn RO + + ; Set output path to the installation directory. + SetOutPath $INSTDIR + + ; Put files there + File "${DEPTH}\Release\synergy.exe" + File "${DEPTH}\Release\synergyc.exe" + File "${DEPTH}\Release\synergys.exe" + File "${DEPTH}\Release\*.dll" + File AUTHORS.txt + File BUGS.txt + File COPYING.txt + File ChangeLog.txt + File FAQ.txt + File HISTORY.txt + File INSTALL.txt + File NEWS.txt + File README.txt + File TODO.txt + + ; Write the installation path into the registry + WriteRegStr HKLM SOFTWARE\Synergy "Install_Dir" "$INSTDIR" + + ; Write the uninstall keys for Windows + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Synergy" "DisplayName" "Synergy" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Synergy" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Synergy" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Synergy" "NoRepair" 1 + WriteUninstaller "uninstall.exe" + +SectionEnd + +; Optional section (can be disabled by the user) +Section "Start Menu Shortcuts" + + CreateDirectory "$SMPROGRAMS\Synergy" + CreateShortCut "$SMPROGRAMS\Synergy\Synergy.lnk" "$INSTDIR\synergy.exe" "" "$INSTDIR\synergy.exe" 0 + CreateShortCut "$SMPROGRAMS\Synergy\README.lnk" "$INSTDIR\README.txt" + CreateShortCut "$SMPROGRAMS\Synergy\NEWS.lnk" "$INSTDIR\NEWS.txt" + CreateShortCut "$SMPROGRAMS\Synergy\FAQ.lnk" "$INSTDIR\FAQ.txt" + CreateShortCut "$SMPROGRAMS\Synergy\Synergy Folder.lnk" "$INSTDIR" + CreateShortCut "$SMPROGRAMS\Synergy\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 + +SectionEnd + +;-------------------------------- + +; Uninstaller + +Section "Uninstall" + + ; Remove autorun registry keys for synergy + DeleteRegKey HKLM "SYSTEM\CurrentControlSet\Services\Synergy Server" + DeleteRegKey HKLM "SYSTEM\CurrentControlSet\Services\Synergy Client" + DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\RunServices" "Synergy Server" + DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\RunServices" "Synergy Client" + DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "Synergy Server" + DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "Synergy Client" + + ; not all keys will have existed, so errors WILL have happened + ClearErrors + + ; Remove registry keys + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Synergy" + DeleteRegKey HKLM SOFTWARE\Synergy + + ClearErrors + + ; First try to remove files that might be locked (if synergy is running) + Delete /REBOOTOK $INSTDIR\synergy.exe + Delete /REBOOTOK $INSTDIR\synergyc.exe + Delete /REBOOTOK $INSTDIR\synergys.exe + Delete /REBOOTOK $INSTDIR\synrgyhk.dll + + ; Remove files and directory + Delete $INSTDIR\*.* + RMDir $INSTDIR + + ; Remove shortcuts, if any + Delete "$SMPROGRAMS\Synergy\*.*" + + ; Remove directories used + RMDir "$SMPROGRAMS\Synergy" + RMDir "$INSTDIR" + + IfRebootFlag 0 EndOfAll + MessageBox MB_OKCANCEL "Uninstaller needs to reboot to finish cleaning up. reboot now?" IDCANCEL NoReboot + ClearErrors + Reboot + IfErrors 0 EndOfAll + MessageBox MB_OK "Uninstaller could not reboot. Please reboot manually. Thank you." + Abort "Uninstaller could not reboot. Please reboot manually. Thank you." + NoReboot: + DetailPrint "" + DetailPrint "Uninstaller could not reboot. Please reboot manually. Thank you." + DetailPrint "" + SetDetailsView show + EndOfAll: + +SectionEnd diff --git a/dist/rpm/Makefile.am b/dist/rpm/Makefile.am index 1f0c046b..e19d9eec 100644 --- a/dist/rpm/Makefile.am +++ b/dist/rpm/Makefile.am @@ -16,6 +16,7 @@ DEPTH = ../.. VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ + synergy.spec.in \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/synergy.dsw b/synergy.dsw index 63b402d9..225c09ed 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -84,6 +84,18 @@ Package=<4> ############################################################################### +Project: "installer"=".\DIST\NULLSOFT\installer.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + Project: "io"=".\lib\io\io.dsp" - Package Owner=<4> Package=<5> From 8d0a2fa04384b47470b68c8ad59315c1a1df6381 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Mar 2004 20:00:30 +0000 Subject: [PATCH 620/807] Forgot file in previous submit. --- configure.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/configure.in b/configure.in index e8a9906d..c8f14e34 100644 --- a/configure.in +++ b/configure.in @@ -41,6 +41,9 @@ ac_ext=cpp dnl check compiler ACX_CHECK_CXX +dnl macros we should do testing with +CXXFLAGS="$CXXFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500" + dnl checks for libraries ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) ACX_CHECK_NANOSLEEP @@ -98,7 +101,7 @@ ACX_CXX_WARNINGS ACX_CXX_WARNINGS_ARE_ERRORS dnl adjust variables for X11 and pthreads -CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500" +CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" AC_OUTPUT([ @@ -119,6 +122,7 @@ cmd/launcher/Makefile cmd/synergyc/Makefile cmd/synergys/Makefile dist/Makefile +dist/nullsoft/Makefile dist/rpm/Makefile dist/rpm/synergy.spec doc/doxygen.cfg From 3c292422083b7e6c85ca7112c4036d0375cabe28 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Mar 2004 20:01:16 +0000 Subject: [PATCH 621/807] Updates for Version 1.1.3. --- AUTHORS | 4 +- COPYING | 49 +-- ChangeLog | 830 ++++++++++++++++++++++++++++++++++++++++++++++++++ FAQ | 34 +-- INSTALL | 20 +- NEWS | 24 ++ PORTING | 22 +- README | 14 +- TODO | 10 +- nodist/readme | 427 -------------------------- 10 files changed, 932 insertions(+), 502 deletions(-) delete mode 100644 nodist/readme diff --git a/AUTHORS b/AUTHORS index a7ebc105..22f10267 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,6 @@ Synergy Authors =============== -Chris Schoeneman +Chris Schoeneman -- creator, maintainer, X11, Win32 +Ryan Breen -- Mac OS X port +Guido Poschta -- Windows installer diff --git a/COPYING b/COPYING index 895e5896..43bb4f3e 100644 --- a/COPYING +++ b/COPYING @@ -2,14 +2,14 @@ Synergy is copyright (C) 2002 Chris Schoeneman. Synergy is distributed under the following license. GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -59,7 +59,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - GNU GENERAL PUBLIC LICENSE + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains @@ -258,26 +258,29 @@ make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE +IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE +COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM +"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR +IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED +TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY +WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED +ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, +SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF +THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT +LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. - END OF TERMS AND CONDITIONS + END OF TERMS AND CONDITIONS diff --git a/ChangeLog b/ChangeLog index 448ffaed..4e06cb70 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,833 @@ +2004/03/28 14:53:01 crs +lib/platform/CXWindowsKeyState.cpp + +Added ISO_Level3_Shift as a synonym for Mode_switch. I've no idea +if this will work as hoped but I've seen documentation that XFree +4.3 uses ISO_Level3_Shift rather than Mode_switch. + +---------- +2004/03/28 14:07:58 crs +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CSynergyHook.h +lib/synergy/CKeyState.h + +Fixed keyboard handling on windows 95 family. + +---------- +2004/03/28 14:07:37 crs +lib/platform/Makefile.am + +Updated makefile to reflect renaming of platform files for win32. + +---------- +2004/03/28 14:06:40 crs +lib/platform/CMSWindowsScreenSaver.cpp + +Fixed windows 95 family screen saver stuff. + +---------- +2004/03/28 14:05:52 crs +lib/client/CServerProxy.cpp + +Changed debug logging of key IDs to use hex. + +---------- +2004/03/28 14:05:31 crs +cmd/launcher/CAutoStart.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +lib/arch/CArchDaemonWindows.cpp + +Fixed bugs in installing per-user startup programs on windows 95 +family. + +---------- +2004/03/26 20:59:26 crs +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h +lib/platform/CMSWindowsDesktop.cpp +lib/platform/CMSWindowsDesktop.h +lib/platform/CMSWindowsKeyMapper.cpp +lib/platform/CMSWindowsKeyMapper.h +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/platform.dsp +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.cpp +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.h +lib/synergy/ISecondaryScreen.h +lib/synergy/libsynergy.dsp + +Converted win32 to new keyboard state tracking design. Also +changed locking to screen so that keys no longer count (only +mouse buttons and scroll lock toggled on). This is to deal +with the unreliability of key event reporting which can leave +us locked to a screen with no key physically pressed. The +result of this is that clients get key repeats and releases +without the corresponding key press. CKeyState handles this +by discarding repeat/release events on keys it hasn't seen go +down. Also made a few other minor fixes to win32 keyboard +handling. + +---------- +2004/03/26 20:59:21 crs +lib/arch/CArchMiscWindows.cpp + +Fixed handling of reading strings from the registry. This was +broken when support for binary data was added. The terminating +NUL was included in the string as a character (that's in addition +to the terminating NUL added by std::string). + +---------- +2004/03/21 20:01:41 crs +lib/client/CClient.cpp +lib/platform/CXWindowsKeyMapper.cpp +lib/platform/CXWindowsKeyMapper.h +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/Makefile.am +lib/server/CPrimaryClient.cpp +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.cpp +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IKeyState.cpp +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h +lib/synergy/ISecondaryScreen.h +lib/synergy/Makefile.am + +Checkpoint. Converted X11 to new keyboard state tracking design. +This new design is simpler. For keyboard support, clients need only +implement 4 virtual methods on a class derived from CKeyState and +one trivial method in the class derived from CPlatformScreen, which +is now the superclass of platform screens instead of IPlatformScreen. +Keyboard methods have been removed from IPlatformScreen, IPrimaryScreen +and ISecondaryScreen. Also, all keyboard state tracking is now in +exactly one place (the CKeyState subclass) rather than in CScreen, +the platform screen, and the key mapper. Still need to convert Win32. + +---------- +2004/03/17 20:59:25 crs +lib/platform/CMSWindowsKeyMapper.cpp +lib/platform/CMSWindowsKeyMapper.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IKeyState.h +lib/synergy/libsynergy.dsp + +Updated keyboard handling on win32. Still needs some work to +avoid shadowing key state in multiple places. Also got locked +to screen and reported key appeared to be wrong. + +---------- +2004/03/14 17:55:53 crs +lib/platform/CXWindowsKeyMapper.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.cpp +lib/synergy/IPlatformScreen.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h +lib/synergy/Makefile.am + +Changed how key state is tracked on X11. Now updating key state +on every key press and release so we don't have to updateKeys() +in isLockedToScreen(). However, if any key appears to be down +we still call updateKeys() to double check that it's really down. +If not we note the spurious lock and don't lock to the screen. + +---------- +2004/03/14 17:50:37 crs +lib/io/IStream.h + +Fixed doxygen formatting error. + +---------- +2004/03/13 19:01:27 crs +lib/platform/CMSWindowsScreen.cpp + +Improved handling of active window on win32. Synergy no longer +takes activation so the previously active window doesn't pop to +the top of the window stack when it regains activation. One +drawback of this is that the mouse cursor isn't shown when +a window (other than synergy's) is activated. However, synergy +does detect mouse motion as before and shows the cursor when it +sees any. + +---------- +2004/03/13 18:58:20 crs +lib/platform/CMSWindowsScreen.h + +Fixed error in previous submit. + +---------- +2004/03/13 17:16:24 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h + +Fixed handling of handshake complete. Was posting an event for it +but making direct calls for other messages from the server. This +could cause messages to be handled out of order. Now making a +direct call for handshake complete. + +---------- +2004/03/13 17:14:32 crs +lib/platform/CMSWindowsEventQueueBuffer.cpp + +Fixed win32 taskbar icon event handling. Wasn't responding to +messages sent via SendMessage (rather than PostMessage). + +---------- +2004/03/13 17:13:55 crs +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h + +Added win32 support for power management. + +---------- +2004/03/10 22:03:01 crs +configure.in +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h + +Added support for DPMS in X11 screen saver. DPMS is the extension +that allows you to power down the display. Previously, synergy +would not power on the display if DPMS was enabled and activated +and xscreensaver was not running. It also wouldn't disable DPMS +so the display would power down normally on a synergy client if +there was no input activity. + +---------- +2004/03/10 20:35:03 crs +acinclude.m4 +lib/arch/CArchNetworkBSD.cpp + +Added check for inet_aton and a simple implementation for platforms +that are missing it. + +---------- +2004/03/08 21:18:36 crs +configure.in +lib/platform/CXWindowsKeyMapper.cpp + +Removed dependency on X11/XF86keysym.h. There are several versions +of that file in existance, not all of which have all the symbols we +require and none of which provide any convenient means of telling +what groups of symbols they define. + +---------- +2004/03/08 20:53:32 crs +lib/platform/CMSWindowsKeyMapper.cpp +lib/platform/CMSWindowsKeyMapper.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CSynergyHook.cpp +lib/server/CServer.cpp + +Win32 fixes. Fixed slightly off cursor positioning when using +absolute mouse_event(). Improved keyboard handling: now using +keyboard layout of last foreground window when leaving server +so users can meaningfully choose the locale, moved dead key +handling into hook library so there should be no more race +conditions involving the keyboard dead key buffer, simplified +keyboard and cursor handling by using a full screen transparent +window when not using low level hooks, fixed error in restoring +buffered dead key when checking for dead keys. This hopefully +fixes all known keyboard bugs on win32. + +---------- +2004/03/08 20:45:53 crs +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h + +Typecasting fix to compile on old solaris. + +---------- +2004/03/06 16:20:08 crs +lib/platform/CXWindowsScreen.cpp +lib/server/CServer.cpp + +Server now disables jump zones when scroll lock is active. + +---------- +2004/02/29 21:34:30 crs +lib/platform/CMSWindowsEventQueueBuffer.cpp + +Fixed processing of events. Was waking up on a sent (rather than +posted) message but then blocking in GetMessage() which handles +the sent message directly. No longer blocking on sent messages. + +---------- +2004/02/29 21:33:20 crs +lib/arch/CArchNetworkWinsock.cpp + +Fixed handling of winsock connect event. Was always immediately +indicating socket had connected. + +---------- +2004/02/29 21:32:00 crs +lib/platform/CMSWindowsScreen.cpp + +Fixed cursor hiding on win32. Still fails occassionally. + +---------- +2004/02/29 21:31:24 crs +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc + +Added reload configuration menu item to win32 task bar. + +---------- +2004/02/29 17:36:32 crs +lib/arch/IArchMultithread.h + +Fixed comment. + +---------- +2004/02/29 17:29:01 crs +lib/arch/CArchMultithreadWindows.h + +Switched to doxygen comments. + +---------- +2004/02/29 17:28:51 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CServer.cpp +lib/synergy/IScreen.cpp +lib/synergy/IScreen.h + +Moved clipboard changed event to CClientProxy because only it +and CServer use it. CServerProxy instead makes a direct call +to CClient, like it does for most other messages. + +---------- +2004/02/29 16:48:22 crs +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h + +Fixed BSD unblockPollSocket(). Was signaling to break out of +poll() but there was a race condition where the thread trying +to unblock poll() could send the signal before the polling +thread had entered poll(). Now using a pipe and polling on +that and the client's sockets, and just writing a byte into +the pipe to unblock poll. This persists until the next call +to poll() so we might force poll() to return once unnecessarily +but that's not a problem. This change makes the BSD code +similar to the winsock code, which uses a winsock event instead +of a pipe. + +---------- +2004/02/29 16:11:17 crs +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkWinsock.h +lib/arch/IArchNetwork.h +lib/arch/XArch.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPSocket.cpp + +Made all arch sockets non-blocking. + +---------- +2004/02/28 17:51:55 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Enabled running at high priority on windows. + +---------- +2004/02/28 17:49:29 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/IArchMultithread.h +lib/base/CEventQueue.cpp + +Generalized signal handling. Now handling SIGHUP in addition +to SIGINT and SIGTERM. Setup SIGHUP to reload the server's +configuration. + +---------- +2004/02/28 16:06:00 crs +lib/base/CLog.cpp + +Fixed incorrect accumulation of newlines in log. + +---------- +2004/02/28 16:00:54 crs +lib/client/CServerProxy.cpp + +Now using first set options message as end of handshake. + +---------- +2004/02/28 12:30:52 crs +lib/platform/CMSWindowsUtil.cpp +lib/platform/CMSWindowsUtil.h + +Added missing files. + +---------- +2004/02/28 12:24:47 crs +lib/common/Version.cpp + +Added missing file. + +---------- +2004/02/28 12:19:49 crs +acinclude.m4 +cmd/launcher/LaunchUtil.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CXWindowsClientTaskBarReceiver.h +cmd/synergyc/Makefile.am +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.h +cmd/synergys/CXWindowsServerTaskBarReceiver.cpp +cmd/synergys/CXWindowsServerTaskBarReceiver.h +cmd/synergys/Makefile.am +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc +configure.in +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchMultithreadWindows.h +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkWinsock.h +lib/arch/CArchTaskBarWindows.cpp +lib/arch/CArchTaskBarWindows.h +lib/arch/IArchMultithread.h +lib/arch/IArchNetwork.h +lib/arch/arch.dsp +lib/base/CEventQueue.cpp +lib/base/CPriorityQueue.h +lib/base/CSimpleEventQueueBuffer.cpp +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/common/Makefile.am +lib/common/Version.h +lib/common/common.dsp +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/net/CSocketMultiplexer.cpp +lib/net/IDataSocket.cpp +lib/net/IDataSocket.h +lib/platform/CMSWindowsDesktop.h +lib/platform/CMSWindowsEventQueueBuffer.cpp +lib/platform/CMSWindowsEventQueueBuffer.h +lib/platform/CMSWindowsKeyMapper.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CSynergyHook.h +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsEventQueueBuffer.cpp +lib/platform/CXWindowsKeyMapper.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/Makefile.am +lib/platform/platform.dsp +lib/server/CClientListener.cpp +lib/server/CServer.cpp +lib/synergy/CProtocolUtil.cpp +lib/synergy/CProtocolUtil.h +lib/synergy/CScreen.cpp +lib/synergy/IScreen.h + +Merged Win32 updates. Added full warnings on g++. Fixed bug in +client when handling server rejection. + +---------- +2004/02/15 18:12:35 crs +lib/base/CFunctionEventJob.cpp +lib/base/CJobList.cpp +lib/base/CJobList.h +lib/base/Makefile.am +lib/base/base.dsp +lib/io/io.dsp +lib/mt/CTimerThread.cpp +lib/mt/CTimerThread.h +lib/mt/Makefile.am +lib/mt/mt.dsp +lib/net/net.dsp +lib/platform/CMSWindowsEventQueueBuffer.cpp +lib/platform/CMSWindowsEventQueueBuffer.h +lib/platform/Makefile.am +lib/platform/platform.dsp +lib/server/server.dsp +lib/synergy/libsynergy.dsp + +Updated Makefiles and win32 projects and removed dead classes. + +---------- +2004/02/15 17:32:11 crs +cmd/synergyc/CClientTaskBarReceiver.cpp +cmd/synergyc/CClientTaskBarReceiver.h +cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp +cmd/synergyc/CXWindowsClientTaskBarReceiver.h +cmd/synergyc/synergyc.cpp +cmd/synergys/CServerTaskBarReceiver.cpp +cmd/synergys/synergys.cpp +lib/arch/CArchNetworkBSD.cpp +lib/arch/XArch.h +lib/base/CEvent.cpp +lib/base/CEvent.h +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/io/CStreamFilter.cpp +lib/net/CNetworkAddress.cpp +lib/net/CTCPListenSocket.cpp +lib/net/CTCPSocket.cpp +lib/net/CTCPSocket.h +lib/net/IDataSocket.h +lib/net/XSocket.cpp +lib/net/XSocket.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CConfig.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/synergy/CClipboard.cpp +lib/synergy/CClipboard.h +lib/synergy/CPacketStreamFilter.cpp +lib/synergy/IClient.h +lib/synergy/IClipboard.cpp +lib/synergy/IClipboard.h +lib/synergy/IPlatformScreen.cpp +lib/synergy/IPlatformScreen.h +lib/synergy/Makefile.am +lib/synergy/ProtocolTypes.h + +Checkpoint. Conversion to event driven system complete for Unix. +Still need to convert win32 platform specific files. + +---------- +2004/02/14 16:30:27 crs +cmd/synergys/CServerTaskBarReceiver.cpp +cmd/synergys/synergys.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h + +Minor cleanup. + +---------- +2004/02/14 14:04:36 crs +cmd/synergys/CServerTaskBarReceiver.cpp +cmd/synergys/CServerTaskBarReceiver.h +cmd/synergys/CXWindowsServerTaskBarReceiver.cpp +cmd/synergys/CXWindowsServerTaskBarReceiver.h +cmd/synergys/Makefile.am +cmd/synergys/synergys.cpp +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/IArchNetwork.h +lib/arch/vsnprintf.cpp +lib/base/CEvent.cpp +lib/base/CEvent.h +lib/base/CEventQueue.cpp +lib/base/CEventQueue.h +lib/base/CSimpleEventQueueBuffer.cpp +lib/base/CSimpleEventQueueBuffer.h +lib/base/CStringUtil.cpp +lib/base/IEventQueue.h +lib/base/IEventQueueBuffer.h +lib/io/IStream.cpp +lib/net/CNetworkAddress.cpp +lib/net/CNetworkAddress.h +lib/net/CSocketMultiplexer.cpp +lib/net/CTCPListenSocket.cpp +lib/net/CTCPSocket.cpp +lib/net/IDataSocket.cpp +lib/net/IListenSocket.cpp +lib/net/ISocket.cpp +lib/platform/CXWindowsEventQueue.cpp +lib/platform/CXWindowsEventQueue.h +lib/platform/CXWindowsEventQueueBuffer.cpp +lib/platform/CXWindowsEventQueueBuffer.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h +lib/platform/Makefile.am +lib/server/CClientListener.cpp +lib/server/CClientListener.h +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_1.cpp +lib/server/CClientProxy1_1.h +lib/server/CClientProxyUnknown.cpp +lib/server/CClientProxyUnknown.h +lib/server/CConfig.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/Makefile.am +lib/synergy/CProtocolUtil.cpp +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IClient.h +lib/synergy/IPlatformScreen.cpp +lib/synergy/IPlatformScreen.h +lib/synergy/IPrimaryScreen.h +lib/synergy/IPrimaryScreenReceiver.h +lib/synergy/IScreen.cpp +lib/synergy/IScreen.h +lib/synergy/IScreenFactory.h +lib/synergy/IScreenReceiver.h +lib/synergy/IScreenSaver.h +lib/synergy/IServer.h +lib/synergy/Makefile.am + +Checkpoint. synergys now works. Still need to do lib/client and +synergyc. + +---------- +2004/02/08 17:07:11 crs +lib/base/CEventQueue.cpp +lib/base/CEventQueue.h +lib/base/CSimpleEventQueue.cpp +lib/base/CSimpleEventQueue.h +lib/base/IEventQueue.h +lib/base/Makefile.am + +Refactored event queue. The event queue is now separated from the +buffer that holds the events and generates system events. This +allows us to switch in/out a platform specific event handler as +necessary without losing our timers and handlers. + +---------- +2004/02/08 16:51:45 crs +lib/net/CTCPSocket.cpp + +No longer sending incorrect disconnect events in read() and +removed redundant sending of disconnect event in close(). + +---------- +2004/02/01 21:09:22 crs +cmd/synergys/synergys.cpp +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchNetworkBSD.cpp +lib/arch/IArchMultithread.h +lib/arch/XArch.h +lib/base/CEventQueue.cpp +lib/base/CEventQueue.h +lib/base/IEventQueue.cpp +lib/base/IEventQueue.h +lib/base/Makefile.am +lib/io/CBufferedInputStream.cpp +lib/io/CBufferedInputStream.h +lib/io/CBufferedOutputStream.cpp +lib/io/CBufferedOutputStream.h +lib/io/CInputStreamFilter.cpp +lib/io/CInputStreamFilter.h +lib/io/COutputStreamFilter.cpp +lib/io/COutputStreamFilter.h +lib/io/CStreamFilter.cpp +lib/io/CStreamFilter.h +lib/io/IInputStream.h +lib/io/IOutputStream.h +lib/io/IStream.cpp +lib/io/IStream.h +lib/io/IStreamFilterFactory.h +lib/io/Makefile.am +lib/io/XIO.cpp +lib/io/XIO.h +lib/net/CSocketMultiplexer.cpp +lib/net/CSocketMultiplexer.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPListenSocket.h +lib/net/CTCPSocket.cpp +lib/net/CTCPSocket.h +lib/net/IDataSocket.cpp +lib/net/IDataSocket.h +lib/net/IListenSocket.h +lib/net/ISocket.h +lib/net/ISocketMultiplexerJob.h +lib/net/Makefile.am +lib/net/TSocketMultiplexerMethodJob.h +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_1.cpp +lib/server/CClientProxy1_1.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CHTTPServer.cpp +lib/server/CHTTPServer.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/Makefile.am +lib/synergy/CInputPacketStream.cpp +lib/synergy/CInputPacketStream.h +lib/synergy/COutputPacketStream.cpp +lib/synergy/COutputPacketStream.h +lib/synergy/CPacketStreamFilter.cpp +lib/synergy/CPacketStreamFilter.h +lib/synergy/CProtocolUtil.cpp +lib/synergy/CProtocolUtil.h +lib/synergy/Makefile.am + +Checkpoint. Code does not run. Still converting over to new +event loop model. Streams, stream filters, and sockets are +converted. Client proxies are almost converted. CServer is +in progress. Removed all HTTP code. Haven't converted the +necessary win32 arch stuff. + +---------- +2004/02/01 20:56:52 crs +cmd/launcher/launcher.dsp +cmd/synergys/Makefile.am +cmd/synergys/synergys.dsp +configure.in +lib/Makefile.am +lib/http/CHTTPProtocol.cpp +lib/http/CHTTPProtocol.h +lib/http/Makefile.am +lib/http/XHTTP.cpp +lib/http/XHTTP.h +lib/http/http.dsp +lib/server/server.dsp +synergy.dsw + +Removed most HTTP stuff. It doesn't seem like the appropriate +choice for server control. May later provide some other means +for controlling the synergy server remotely. + +---------- +2004/01/24 16:09:25 crs +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchMultithreadPosix.cpp +lib/arch/CArchMultithreadPosix.h +lib/arch/CArchNetworkBSD.cpp +lib/arch/IArchMultithread.h +lib/base/CEvent.cpp +lib/base/CEvent.h +lib/base/CEventQueue.cpp +lib/base/CEventQueue.h +lib/base/CFunctionEventJob.cpp +lib/base/CFunctionEventJob.h +lib/base/CLog.cpp +lib/base/CPriorityQueue.h +lib/base/CSimpleEventQueue.cpp +lib/base/CSimpleEventQueue.h +lib/base/IEventJob.h +lib/base/IEventQueue.h +lib/base/Makefile.am +lib/base/TMethodEventJob.h +lib/io/CBufferedInputStream.cpp +lib/io/CBufferedInputStream.h +lib/io/CBufferedOutputStream.cpp +lib/io/CBufferedOutputStream.h +lib/mt/CThread.cpp +lib/mt/CThread.h +lib/net/CTCPListenSocket.cpp +lib/net/CTCPListenSocket.h +lib/net/CTCPSocket.cpp +lib/net/CTCPSocket.h +lib/net/IDataSocket.cpp +lib/net/IDataSocket.h +lib/net/IListenSocket.cpp +lib/net/IListenSocket.h +lib/net/ISocket.cpp +lib/net/ISocket.h +lib/net/Makefile.am +lib/platform/CXWindowsEventQueue.cpp +lib/platform/CXWindowsEventQueue.h +lib/platform/Makefile.am + +Checkpointing centralized event queue stuff. Currently have: +an event queue and events, TCP sockets converted to use events, +unix multithreading and network stuff converted, and an X Windows +event queue subclass. + +---------- 2003/07/19 17:22:06 crs cmd/launcher/launcher.cpp cmd/launcher/launcher.rc diff --git a/FAQ b/FAQ index 0962f028..a238c6d2 100644 --- a/FAQ +++ b/FAQ @@ -12,12 +12,13 @@ Questions 7. Why do my CapsLock and NumLock keys act funny? 8. Can synergy share the display in addition to the mouse and keyboard? 9. Can synergy do drag and drop between computers? -10. Do AltGr or Mode-Switch work? +10. Do AltGr or Mode-Switch or ISO_Level3_Shift work? 11. Why isn't synergy ported to platform XYZ? 12. My client can't connect. What's wrong? 13. Linking fails on Solaris. What's wrong? 14. The screen saver never starts. Why not? 15. I can't switch screens anymore for no apparent reason. Why? +16. I get the error 'Xlib: No protocol specified'. Why? Answers ------- @@ -126,13 +127,14 @@ Answers No. That's a very cool idea and it'll be explored. However, it's also clearly difficult and may take a long time to implement. -10. Does AltGr/Mode-Switch work? +10. Does AltGr/Mode-Switch/ISO_Level3_Shift work? Yes, as of 1.0.12 synergy has full support for AltGr/Mode-switch. That includes support for most (all?) European keyboard layouts. All systems should be using the same keyboard layout, though, for all characters to work. (Any character missing from a client's - layout cannot be generated by synergy.) + layout cannot be generated by synergy.) There is experimental + support for ISO_Level3_Shift in 1.1.3. 11. Why isn't synergy ported to platform XYZ? @@ -169,19 +171,13 @@ Answers 15. I can't switch screens anymore for no apparent reason. Why? - This sometimes happens but all the causes aren't yet known. One - known cause is if the synergy server in running on Windows - 95/98/Me and a 16-bit application is in the foreground when the - screen saver starts. Windows fails to notify synergy that the - screen saver has started in this situation (which is a bug in - Windows, not synergy) and synergy may intercept some keyboard - input and divert it to a client when the screen saver is running. - As a result, it's possible for the server system to believe a key - is pressed when it really isn't. Typically, it's the return key - and simply tapping it will allow synergy to switch screens again. - - If this problem happens to you, try tapping the enter key to see - if that solves the problem. If not, you can try running with - debug logging (--debug DEBUG) and synergy will report exactly why - it refuses to switch screens. If it claims a key is down then - try tapping that key and see if that solves the problem. + This should not happen with 1.1.3 and up. Earlier versions of + synergy would not allow switching screens when a key was down and + sometimes it would believe a key was down when it was not. + +16. I get the error 'Xlib: No protocol specified'. Why? + + You're running synergy without authorization to connect to the + X display. Typically the reason is running synergy as root when + logged in as non-root. Just run synergy as the same user that's + logged in. diff --git a/INSTALL b/INSTALL index f33265ad..c2caed75 100644 --- a/INSTALL +++ b/INSTALL @@ -58,10 +58,14 @@ Installing ---------- Windows: - There is no support for creating an installer for synergy or installing - the files in a particular location. Instead, just copy the following - files from the Debug or Release directory to a directory you choose - (perhaps under the Program Files directory): + You'll need NSIS, the Nullsoft Scriptable Install System, available + from http://nsis.sourceforge.net/. Build `All - Release' then build + 'Installer - Release'. This creates SynergyInstaller.exe in the top + level directory. Run this to install. + + Alternatively, you can simply copy the following files from the Debug + or Release directory to a directory you choose (perhaps under the + Program Files directory): * synergy.exe * synergyc.exe @@ -90,7 +94,7 @@ First configure the server. Click the `Server' radio button * Click the `Server' radio button * Click `Add' to add the server to the `Screens' list - * Enter the name of server (the computer name is recommended) + * Enter the name of server (the computer's name is recommended) * Enter other names the server is known by * Click OK * Use `Add' to add your other computers @@ -403,6 +407,12 @@ the problem. Here are typical problems and possible solutions: program using that port (24800) and restart the server. +If you get the error "Xlib: No protocol specified" you're probably +running synergy as root while logged in as another user. X11 may +prevent this for security reasons. Either run synergy as the same +user that's logged in or (not recommended) use 'xhost +' to allow +anyone to connect to the display. + Once all the clients are running, try moving the mouse to each screen. Be sure to check all the configured links. diff --git a/NEWS b/NEWS index e542e28d..944a0a2a 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,30 @@ Synergy News ============ +* Mar-28-2004 - Synergy 1.1.3 released + + Made following changes: + * Major code refactoring; reduced use of threads, added event queue + * Removed unused HTTP support code + * No longer interfering with mouse when scroll lock is toggled on + * Fixed minor mispositioning of mouse on win32 + * Unix portability fixes + * Added support for power management + * Improved keyboard handling and bug fixes + * Fixed dead key handling + + Note: the tray icon on windows is known to not work correctly when + running the synergy server on Windows 95/95/Me. + +* Aug-24-2003 - Synergy 1.0.14 released + + Made following changes: + * Fixed bugs in setting win32 process/thread priority + * Fixed resource leak in opening win32 system log + * Fixed win32 launcher not getting non-default advanced options + * Synergy log copied to clipboard now transferred to other screens + * Hack to work around lesstif clipboard removed (fixes pasting on X) + * Jul-20-2003 - Synergy 1.0.12 released This release finally completes support for non-ASCII characters, diff --git a/PORTING b/PORTING index 3281822d..a771799d 100644 --- a/PORTING +++ b/PORTING @@ -15,6 +15,7 @@ cmd -- program source code synergys -- synergy server config -- stuff for autoconf/automake dist -- files for creating distributions + nullsoft -- files for creating Nullsoft NSIS installer (Windows) rpm -- files for creating RPMs doc -- placeholder for documentation examples -- example files @@ -23,7 +24,6 @@ lib -- library source code base -- simple utilities client -- synergy client library common -- commonly needed header files - http -- simple http tools io -- I/O mt -- multithreading net -- networking @@ -358,8 +358,8 @@ file and make calls directly, surrounded by a suitable #ifdef/#endif. Implementing lib/platform: Most of the remaining platform dependent code lives in lib/platform. -The code there implements platform dependent window, clipboard, and -screen saver handling. If a platform is named XXX then the following +The code there implements platform dependent window, clipboard, keyboard +and screen saver handling. If a platform is named XXX then the following classes should be derived and implemented: * CXXXClipboard : IClipboard @@ -367,16 +367,16 @@ classes should be derived and implemented: have helper classes for converting between various clipboard data formats. - * CXXXScreen : IScreen - Provide screen operations common to the server and clients. - The CXXXPrimaryScreen and CXXXSecondaryScreen classes use a - CXXXScreen. + * CXXXEventQueueBuffer : IEventQueueBuffer + Provides operations for waiting for, posting and retrieving events. + Also provides operations for creating and deleting timers. - * CXXXPrimaryScreen : CPrimaryScreen, IScreenEventHandler - Provides server screen operations. + * CXXXKeyState : CKeyState + Provides operations for synthesizing key events and for mapping a + key ID to a sequence of events to generate that key. - * CXXXSecondaryScreen : CSecondaryScreen, IScreenEventHandler - Provides client screen operations. + * CXXXScreen : IScreen, IPrimaryScreen, ISecondaryScreen, IPlatformScreen + Provides screen operations. * CXXXScreenSaver : IScreenSaver Provides screen saver operations. diff --git a/README b/README index 072c9f20..5cdfc938 100644 --- a/README +++ b/README @@ -48,7 +48,7 @@ System Requirements mouse, TCP/IP networking; -* Microsoft Windows 95, Windows 98, Windows Me (the Windows 96 family); +* Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family); * Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family); @@ -85,11 +85,9 @@ choices. The Windows version provides a convenient GUI for configuration. Second, you install the software. Choose the appropriate package -and install it. On Windows you should unzip the files into the -`Program Files' directory; all the files will be put into a new -directory named `Synergy' under `Program Files'. You must install -the software on all the computers that will share the mouse and -keyboard. +and install it. On Windows you should run SynergyInstaller. You +must install the software on all the computers that will share the +mouse and keyboard. Third, you configure and start the server. Windows @@ -99,7 +97,7 @@ Third, you configure and start the server. * Click the `Server' radio button * Click `Add' to add the server to the `Screens' list - * Enter the name of server (the computer name is recommended) + * Enter the name of server (the computer's name is recommended) * Enter other names the server is known by * Click OK * Use `Add' to add your other computers @@ -220,8 +218,6 @@ Tips and Tricks a screen to itself then moving off the left of the screen would put the mouse at the right of the screen and vice versa. -* You cannot switch screens when a key or mouse button is pressed. - * You cannot switch screens when the scroll lock it toggled on. Use this to prevent unintentional switching. diff --git a/TODO b/TODO index 78b3b80a..0b1fc88d 100644 --- a/TODO +++ b/TODO @@ -18,16 +18,12 @@ Things to do to synergy, in no particular order: * Port to other platforms - Most desired is MacOS X. + Most desired is MacOS X. Ryan Breen has a partial port to Mac OS X. + Only the client is ported and there is no clipboard or screen saver + support. It's available from http://www.ryanbreen.com/synergy. * Write man/html pages -* Provide a win32 installer/uninstaller - - Synergy doesn't have any special needs so even just unzipping is - satisfactory, but a proper installer would be nice. And, more - importantly, it should provide an uninstaller. - * Add more clipboard formats Synergy currently supports only text on the clipboard. It should diff --git a/nodist/readme b/nodist/readme deleted file mode 100644 index 847c914f..00000000 --- a/nodist/readme +++ /dev/null @@ -1,427 +0,0 @@ -synergy -------- -synergy: [noun] a mutually advantageous conjunction of distinct elements - -synergy lets you easily share a single mouse and keyboard between -multiple computers, each with its own display, using software only. -redirecting the mouse and keyboard is as simple as moving the mouse -off the edge of your screen. synergy merges the clipboards of all -the systems into one, allowing cut-and-paste between systems. it -also synchronizes screensavers so they all start and stop together -and, if screen locking is enabled, only one screen requires a -password to unlock them all. - - -system requirements -------------------- -all systems: - keyboard - mouse - TCP/IP networking - -Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family): - ??? MB RAM - -Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family): - ??? MB RAM - -Linux, Unix: - ??? MB RAM - X Windows, revision 4 or up with the XTEST extension - use `xdpyinfo | grep XTEST' to check - - -manifest --------- - linux windows - ----- ------- - README README this file - synergy synergy.exe the synergy client - synergyd synergyd.exe the synergy server - synrgyhk.dll the synergy hook dll - synergy.conf synergy.conf sample configuration file - - -running synergy ---------------- -synergy is simple to configure. the server uses a configuration file -and command line options while the client uses only command line -options. it's recommended that both the client and server be run in -the foreground until the configuration is verified to work. - -step 1: create a configuration file - edit the sample configuration file. there are two sections you - must fill in and a third optional section. you should delete - the existing lines inside the sections. - - in the "screens" section, add a line for each computer you'll - be using (server and clients). put the hostname of the computer - followed by a colon (with no space in between). the computers - can be listed in any order. - - in the "links" section you define how screens are connected. - each screen is listed as in the "screens" section except - following each screen is a list of links to other screens in - the form " = " where is "left", - "right", "up", or "down" and is a screen listed in - the "screens" section. - - as an example, if we have "left=foo" under the "bar" screen - then screen "foo" is on the left of screen "bar". the user - will be able to move the mouse off the left edge of "foo" and - will appear on the opposite (right) edge of "bar". note that - it's entirely possible to have one-way (asymmetric) links and - screens with only links into them. the latter should be - avoided since there's no way to move the mouse off those - screens. - - in the "aliases" section you can list other names for each - screen. this is especially useful for dealing with fully - qualified domain names versus simple hostnames. - -step 2: start the server - the server is the system with the mouse and keyboard to be - shared. each platform has its own tradeoffs when running as - the server. see the release notes below for more information. - - run the synergy server on the server system using the following - command line: - - synergyd -f --config - - replacing with the path to the configuration - file. you can use `synergyd --help' for a list of command line - options. - -step 3: start the clients - on each client system start the synergy client using the - following command line: - - synergy -f --debug INFO --no-camp - - replacing with the hostname or address of the - server system. - - the client should quickly report `connected to server'. if it - does not but doesn't print an error and exit immeditately then - it's trying to connect to the server but cannot. it will time - out in 30 seconds and exit (use ctrl+c to exit earlier). you - should check that the server is running and try again. - - otherwise, if the client doesn't connect it should print an - error describing the problem. here are typical problems and - possible solutions: - - failed to open screen: - check permission to open the X display; - check that the DISPLAY environment variable is set. - already connected: - check that synergy isn't already running. - refused client: - add client to the server's configuration file. - connection failed: - check server-hostname; - the server cannot open the desired port, stop the - program using that port (24800) and restart the - server. - -step 4: verify the configuration - once the clients are connected, use the mouse to check that - the screens are properly linked. moving the mouse off the - edge of a screen with a link should cause it to appear on - the opposite edge of the linked-to screen. - - -using synergy -------------- -using synergy is very easy. once clients have connected to the -server all you do to redirect keyboard and mouse input to a screen -(i.e. switch screens) is move the mouse cursor off the edge of the -screen you're on. which edges go to which screens depends on the -configuration. - -clients can be connected and disconnected at any time. until a -client is connected, switching to it works as if you switched to -it then moved all the way across it in the same direction and -switched to the next screen. this repeats until you reach a -connected screen. if there is no connected screen in that -direction then the mouse will not leave the starting screen. - -disconnecting a client while the mouse is on it causes the mouse -to instantly jump to the center of the server screen. - -the clipboard is automatically transferred between screens. if -you copy on one screen you just switch to another screen and paste. -note that X Windows has two major clipboards: the primary -selection and the clipboard. synergy supports both. however, -Microsoft Windows only supports the clipboard. the Windows -clipboard is transferred to both the X primary selection and the -clipboard. whichever X clipboard was changed last becomes the -Windows clipboard. end-of-line sequences (LF on linux and unix, -CRLF on Windows) are automatically converted as necessary. the -clipboards are transferred using Unicode; if your platforms and -applications understand Unicode then you should be able to cut -and paste any Unicode character. - -synergy synchronizes screensavers. the screensavers on client -screens are disabled when they connect to the server. when the -primary screen's screensaver starts, the screensaver on each -secondary screen starts too. all the secondary screensavers are -stopped when the primary screensaver stops. moving the mouse or -pressing a key will stop the primary screensaver, regardless of -which screen the mouse was on when the screensavers started. if -the primary screensaver requires a password to unlock then the -user is prevented from switching to the secondary screens until -the primary screen is unlocked. - - -installing as a daemon/service ------------------------------- -synergy can run in the foreground or as a daemon/service. it's -recommended that you run it in the foreground until you've sorted -out your configuration. - -on the Windows NT family you cannot run a service directly. -instead you install the service then run or stop it via the -Services control panel. on the Windows 95 family, you can use -the `--daemon' command line option to start synergy as a service -or you can install the service and restart your computer. - -in the text below, except where noted, synergy refers to the -client and/or the server. - -windows: - to install synergy just run one of the following: - - synergy --install [other command line options] - synergyd --install [other command line options] - - the client/server is installed as a service and the command - line is saved and used when starting the service. the system - will expect to find the program wherever it was when you used - the --install option so make sure it's not on a network share - from another system because the network share will not be - available at boot time. synergyd will also try to load - synrgyhk.dll so that should be in the same directory as - synergyd.exe. - - note that when installing the client you must provide the - server hostname argument. to change the arguments you must - first uninstall then reinstall. - - you must also install the configuration file along with the - server. it's recommended that you put it in the windows - directory (e.g. C:\WINNT) and call it "synergy.sgc". the - server will automatically find this file. however, you can - also use the --config command line option and specify an - *absolute* path to the file. remember that this file must be - accessible when the system starts up, before network shares - are mapped. - - to uninstall use: - - synergy --uninstall - synergyd --uninstall - -linux, unix: - before starting synergy as a daemon you should understand that - synergy requires an X server that it can connect to. synergy - can start before the X server does and will repeatly attempt to - connect to the X server until it succeeds. however, if the - server requires authorization then it's unlikely that synergy - will ever succeed. so, in general, synergy should be (re)started - by the X display manager. - - some display managers (xdm and kdm, but not gdm) grab the - keyboard and do not release it until the user logs in, also - for security reasons. this prevents a synergy server from - sharing the mouse and keyboard until the user logs in but - it doesn't prevent a synergy client from synthesizing mouse - and keyboard input. - - you should modify xdm's Xsetup script to start the synergy - client or server. for example, somewhere near the bottom of - Xsetup (but someplace before anywhere the script calls exit) - you might add: - /usr/bin/killall synergy - /usr/sbin/synergy 192.168.1.101 - this assumes synergy is installed in /usr/sbin. these lines - make sure any already running synergy is terminated and starts - a fresh copy. it's important to kill old copies so that you - don't end up with multiple synergy instances fighting each - other or, at the very least, using up system resources. - - to start the synergy server you might use: - /usr/bin/killall synergyd - /usr/sbin/synergyd --config /root/synergy.conf - assuming synergyd is installed in /usr/sbin. if you've put - the configuration data in /etc/synergy.conf then you don't - need the --config option. - - another option is to put the synergy startup in .Xsession in - your home directory. that allows users without root access to - start synergy when they login. in this case synergy will not - be running while on the login screen. - - -common command line options ---------------------------- - -d, --debug use debugging level - --daemon run as a daemon (linux,unix) or background (windows) - -f, --no-daemon run in the foreground - -n, --name use instead of the hostname - --restart automatically restart on failures - -1, --no-restart do not restart on failure - -h, --help print help and exit - --version print version information and exit - --install install as a service (windows) - --uninstall uninstall service (windows) - -debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, -INFO, DEBUG, DEBUG1, and DEBUG2. only messages at or above the given -level are logged. messages are logged to a terminal window when -running in the foreground. unix logs messages to syslog when running -as a daemon. the Windows NT family logs messages to the event log -when running as a service. the Windows 95 family shows FATAL log -messages in a message box and others in a terminal window when running -as a service. - -the `--name' option lets the client or server use a name other than -its hostname for its screen. this name is used when checking the -configuration. - -neither the client nor server will automatically restart if an error -occurs that is sure to happen every time. for example, the server -will exit immediately if it can't find itself in the screen -configuration. on X11 both the client and server will also terminate -if the connection to the X server is lost. since xdm will normally -restart the server and then synergy this is the correct behavior. - - -server command line options ---------------------------- - -a, --address
listen for connections on the given address - -c, --config read configuration from - -
has one of the following forms: - - : - : - is a hostname or address of a network interface on the -server system. is a port number from 1 to 65535. -defaults to the system's hostname and defaults to 24800. - - -client command line options ---------------------------- - --camp retry connection to server until successful - --no-camp try connection to server only once -
address of server - -see the "server command line options" for a description of
-but note that there is no default though there is a -default . - - -release notes -------------- -synergy does not yet fully capture all possible input or have full -control over the mouse and keyboard on all platforms. each platform -has its own limitations and these limitations may influence your -choice for the server. - -the following lists enumerate the limitations of each platform. a -key (combination) that cannot be captured is not detected by synergy. -a key (combination) that cannot be blocked will be passed through to -the server system even when the mouse is on a client system. if a -key cannot be captured then it also cannot be blocked. - -windows 95 family, windows NT prior to service pack 3: - * cannot capture: - * ctrl+alt+del - * ctrl+esc - * alt+[shift+]tab - * alt+[shift+]esc - * windows+E - * windows+[ctrl+]F - * windows+[shift+]M - * windows+R - * windows+F1 - * windows+tab - * windows+break - * accessibility shortcuts (e.g. press shift 5 times for sticky keys) - * the individual keys are captured but the dialogs still appear - * cannot synthesize: - * accessibility shortcuts - -windows NT family (except NT prior to SP3): - * cannot block: - * ctrl+alt+del - * accessibility shortcuts (e.g. press shift 5 times for sticky keys) - * the individual keys are captured but the dialogs still appear - * cannot synthesize: - * accessibility shortcuts - -linux, unix: - * cannot capture: - * ctrl+alt+del - * ctrl+alt+backspace (only if used by the X server) - * ctrl+alt+keypad_plus (only if used by the X server) - * ctrl+alt+keypad_minus (only if used by the X server) - * keyboard/mouse grabs prevent switching screens for their duration - * some display managers grab the keyboard until login - -currently, the windows NT family (except NT prior to SP3) makes the -best server. - - -known bugs ----------- -all: - * non-ASCII keyboard characters are not supported - -windows: - * screen flashes when entering the screen - * synergy may interfere with desktop switcher programs. however, - synergy understands and handles multiple desktops. - * there should be a control panel - * there should be a taskbar icon - -windows 95 family: - * typing into a console window can be slow - -windows NT family: - * the event viewer reports a message lookup error for synergy logs. - however, the full synergy message is in the extra data portion of - the event dialog. - * does not gracefully handle NoInteractiveServices being enabled - -linux: - * some keyboards have toggle keys that toggle on on key press and - toggle off on the key release after the next key press. synergy - doesn't handle these properly. - * shift-lock (as opposed to caps-lock) is not supported - * large (~256k) motif clipboard items are not copied properly - - -tips and tricks ---------------- -* a screen can be its own neighbor. that allows a screen to "wrap". - for example, if a configuration linked the left and right sides of - a screen to itself then moving off the left of the screen would put - the mouse at the right of the screen and vice versa. - -* you cannot switch screens when a key or mouse button is pressed. - -* you cannot switch screens when the scroll lock it toggled on. use - this to prevent unintentional switching. - -* turn off mouse driven virtual desktop switching on X windows. it - will interfere with synergy. use keyboard shortcuts instead. - -* synergy's screensaver synchronization works best with xscreensaver - under X windows. synergy works better with xscreensaver if it is - using of the screensaver extensions. prior to xscreensaver 4.0 - you can use `-mit-extension', `-sgi-extension', or `-xidle-extension' - command line options to enable an extension. starting with 4.0 - you must enable the corresponding option in your .xscreensaver file. From 82552698f7554bd0b8f5c5ec306e6ac972033f84 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 28 Mar 2004 20:52:49 +0000 Subject: [PATCH 622/807] Fixed bug in starting (when not testing) synergy from launcher on windows NT. It was trying to start it as a service. --- cmd/launcher/launcher.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 0c4b4597..1b89e9fb 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -514,6 +514,11 @@ getCommandLine(HWND hwnd, bool testing) cmdLine += " -z --no-restart --no-daemon"; } + // can't start as service on NT + else if (!CArchMiscWindows::isWindows95Family()) { + cmdLine += " --no-daemon"; + } + // get the server name CString server; bool isClient = isClientChecked(hwnd); From ceb654246c4570174ea8f5d34bbe64fad85948c3 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Mar 2004 18:54:56 +0000 Subject: [PATCH 623/807] Changed version to 1.1.4. This time changing the version before making any other changes. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index c8f14e34..efcdea5b 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=1 -RELEASE_VERSION=3 +RELEASE_VERSION=4 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 26049a6d..0a5d2e35 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.3" +# define VERSION "1.1.4" #endif // important strings From 610518104b9189fcd5a16ca77a78874c6d99f2d8 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 30 Mar 2004 18:55:58 +0000 Subject: [PATCH 624/807] Fixed crash bug in CKeyState. Would deference bogus pointer in isModifierActive if there's an unmapped toggle modifier. --- lib/synergy/CKeyState.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/synergy/CKeyState.cpp b/lib/synergy/CKeyState.cpp index 5dffa9d0..89e2001c 100644 --- a/lib/synergy/CKeyState.cpp +++ b/lib/synergy/CKeyState.cpp @@ -374,19 +374,21 @@ CKeyState::isModifierActive(KeyModifierMask mask) const { const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)]; KeyButtons::const_iterator j = buttons.begin(); - if (isToggle(mask)) { - // modifier is a toggle - if ((m_keys[*j] & kToggled) != 0) { - return true; - } - } - else { - // modifier is not a toggle - for (; j != buttons.end(); ++j) { - if ((m_keys[*j] & kDown) != 0) { + if (j != buttons.end()) { + if (isToggle(mask)) { + // modifier is a toggle + if ((m_keys[*j] & kToggled) != 0) { return true; } } + else { + // modifier is not a toggle + for (; j != buttons.end(); ++j) { + if ((m_keys[*j] & kDown) != 0) { + return true; + } + } + } } return false; } From 48054531904f7b41cd05363c77b0208834354fae Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Mar 2004 22:12:53 +0000 Subject: [PATCH 625/807] Fixed failure to initialize double tap and wait to switch timeouts. --- lib/server/CServer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 7aa040c1..79a13bb9 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -117,6 +117,9 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : // add connection addClient(m_primaryClient); + // process options locally + processOptions(); + // tell primary client about its options sendOptions(m_primaryClient); From 4ace26f19b3edf1ab31792f722f3db4af911a0ef Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Mar 2004 22:14:01 +0000 Subject: [PATCH 626/807] Make screen drop down lists longer in the launcher. They're now long enough for the scroll bar to show up properly (with the thumb) and they have enough space for 6 screens without needing the scroll bar. --- cmd/launcher/launcher.rc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 9d4b6ca5..82d60df4 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -79,16 +79,16 @@ BEGIN PUSHBUTTON "&Remove",IDC_MAIN_SERVER_REMOVE_BUTTON,12,150,50,14 LTEXT "&Layout:",IDC_MAIN_SERVER_LAYOUT_LABEL,138,79,24,8 LTEXT "Left:",IDC_MAIN_SERVER_LEFT_LABEL,144,93,15,8 - COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,111,46, + COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,111,75, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP LTEXT "Right:",IDC_MAIN_SERVER_RIGHT_LABEL,144,109,20,8 - COMBOBOX IDC_MAIN_SERVER_RIGHT_COMBO,175,107,112,46, + COMBOBOX IDC_MAIN_SERVER_RIGHT_COMBO,175,107,112,75, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP LTEXT "Above:",IDC_MAIN_SERVER_TOP_LABEL,144,125,24,8 - COMBOBOX IDC_MAIN_SERVER_TOP_COMBO,175,123,112,46, + COMBOBOX IDC_MAIN_SERVER_TOP_COMBO,175,123,112,75, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP LTEXT "Below:",IDC_MAIN_SERVER_BOTTOM_LABEL,144,141,22,8 - COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,112,46, + COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,112,75, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "&Options...",IDC_MAIN_OPTIONS,12,191,50,14 PUSHBUTTON "Adva&nced...",IDC_MAIN_ADVANCED,68,191,50,14 From 4576b30c378b2a0d6167ba1630866922285552b4 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Mar 2004 22:14:15 +0000 Subject: [PATCH 627/807] Fixed lookup of hosts by name on win32. --- lib/arch/CArchNetworkWinsock.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index 484bb8ff..bbe8cdfe 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -675,11 +675,21 @@ CArchNetworkWinsock::nameToAddr(const std::string& name) // address lookup struct hostent* info = gethostbyname_winsock(name.c_str()); if (info == NULL) { - delete addr; throwNameError(getsockerror_winsock()); } - addr = CArchNetAddressImpl::alloc(info->h_length); - memcpy(TYPED_ADDR(void, addr), info->h_addr_list[0], info->h_length); + + // copy over address (only IPv4 currently supported) + if (info->h_addrtype == AF_INET) { + addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in)); + memcpy(&inaddr.sin_addr, info->h_addr_list[0], + sizeof(inaddr.sin_addr)); + memcpy(TYPED_ADDR(void, addr), &inaddr, addr->m_len); + } + else { + throw XArchNetworkNameUnsupported( + "The requested name is valid but " + "does not have a supported address family"); + } } return addr; From d1a60e848ef8a3614f6c7f4448327cecfada20b6 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Mar 2004 22:15:13 +0000 Subject: [PATCH 628/807] Reverted task bar code to 1.0.15 version. That used a window in its own thread for handling messages. It seems to fix most of the task bar bugs but there's still an hourglass cursor on NT when using the popup menu. --- lib/arch/CArchTaskBarWindows.cpp | 186 +++++++++++++++++++++++++------ lib/arch/CArchTaskBarWindows.h | 11 +- lib/arch/IArchTaskBar.h | 3 - 3 files changed, 160 insertions(+), 40 deletions(-) diff --git a/lib/arch/CArchTaskBarWindows.cpp b/lib/arch/CArchTaskBarWindows.cpp index fccba001..a31745d9 100644 --- a/lib/arch/CArchTaskBarWindows.cpp +++ b/lib/arch/CArchTaskBarWindows.cpp @@ -41,45 +41,43 @@ CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) : // save app instance s_appInstance = reinterpret_cast(appInstance); - // register the task bar restart message - m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); + // we need a mutex + m_mutex = ARCH->newMutex(); - // register a window class - WNDCLASSEX classInfo; - classInfo.cbSize = sizeof(classInfo); - classInfo.style = CS_NOCLOSE; - classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; - classInfo.cbClsExtra = 0; - classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); - classInfo.hInstance = s_appInstance; - classInfo.hIcon = NULL; - classInfo.hCursor = NULL; - classInfo.hbrBackground = NULL; - classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = TEXT("SynergyTaskBar"); - classInfo.hIconSm = NULL; - m_windowClass = RegisterClassEx(&classInfo); + // and a condition variable which uses the above mutex + m_ready = false; + m_condVar = ARCH->newCondVar(); - // create window - m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, - reinterpret_cast(m_windowClass), - TEXT("Synergy Task Bar"), - WS_POPUP, - 0, 0, 1, 1, - NULL, - NULL, - s_appInstance, - reinterpret_cast(this)); + // we're going to want to get a result from the thread we're + // about to create to know if it initialized successfully. + // so we lock the condition variable. + ARCH->lockMutex(m_mutex); + + // open a window and run an event loop in a separate thread. + // this has to happen in a separate thread because if we + // create a window on the current desktop with the current + // thread then the current thread won't be able to switch + // desktops if it needs to. + m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this); + + // wait for child thread + while (!m_ready) { + ARCH->waitCondVar(m_condVar, m_mutex, -1.0); + } + + // ready + ARCH->unlockMutex(m_mutex); } CArchTaskBarWindows::~CArchTaskBarWindows() { - if (m_hwnd != NULL) { - removeAllIcons(); - DestroyWindow(m_hwnd); + if (m_thread != NULL) { + PostMessage(m_hwnd, WM_QUIT, 0, 0); + ARCH->wait(m_thread, -1.0); + ARCH->closeThread(m_thread); } - UnregisterClass((LPCTSTR)m_windowClass, s_appInstance); - + ARCH->closeCondVar(m_condVar); + ARCH->closeMutex(m_mutex); s_instance = NULL; } @@ -98,10 +96,6 @@ CArchTaskBarWindows::removeDialog(HWND hwnd) void CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver) { - if (m_hwnd == NULL) { - return; - } - // ignore bogus receiver if (receiver == NULL) { return; @@ -176,43 +170,53 @@ CArchTaskBarWindows::recycleID(UINT id) void CArchTaskBarWindows::addIcon(UINT id) { + ARCH->lockMutex(m_mutex); CIDToReceiverMap::const_iterator index = m_idTable.find(id); if (index != m_idTable.end()) { modifyIconNoLock(index->second, NIM_ADD); } + ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::removeIcon(UINT id) { + ARCH->lockMutex(m_mutex); removeIconNoLock(id); + ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::updateIcon(UINT id) { + ARCH->lockMutex(m_mutex); CIDToReceiverMap::const_iterator index = m_idTable.find(id); if (index != m_idTable.end()) { modifyIconNoLock(index->second, NIM_MODIFY); } + ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::addAllIcons() { + ARCH->lockMutex(m_mutex); for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); index != m_receivers.end(); ++index) { modifyIconNoLock(index, NIM_ADD); } + ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::removeAllIcons() { + ARCH->lockMutex(m_mutex); for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); index != m_receivers.end(); ++index) { removeIconNoLock(index->second.m_id); } + ARCH->unlockMutex(m_mutex); } void @@ -305,6 +309,49 @@ CArchTaskBarWindows::handleIconMessage( } } +bool +CArchTaskBarWindows::processDialogs(MSG* msg) +{ + // only one thread can be in this method on any particular object + // at any given time. that's not a problem since only our event + // loop calls this method and there's just one of those. + + ARCH->lockMutex(m_mutex); + + // remove removed dialogs + m_dialogs.erase(false); + + // merge added dialogs into the dialog list + for (CDialogs::const_iterator index = m_addedDialogs.begin(); + index != m_addedDialogs.end(); ++index) { + m_dialogs.insert(std::make_pair(index->first, index->second)); + } + m_addedDialogs.clear(); + + ARCH->unlockMutex(m_mutex); + + // check message against all dialogs until one handles it. + // note that we don't hold a lock while checking because + // the message is processed and may make calls to this + // object. that's okay because addDialog() and + // removeDialog() don't change the map itself (just the + // values of some elements). + ARCH->lockMutex(m_mutex); + for (CDialogs::const_iterator index = m_dialogs.begin(); + index != m_dialogs.end(); ++index) { + if (index->second) { + ARCH->unlockMutex(m_mutex); + if (IsDialogMessage(index->first, msg)) { + return true; + } + ARCH->lockMutex(m_mutex); + } + } + ARCH->unlockMutex(m_mutex); + + return false; +} + LRESULT CArchTaskBarWindows::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -375,3 +422,70 @@ CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg, return DefWindowProc(hwnd, msg, wParam, lParam); } } + +void +CArchTaskBarWindows::threadMainLoop() +{ + // register the task bar restart message + m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_NOCLOSE; + classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); + classInfo.hInstance = s_appInstance; + classInfo.hIcon = NULL; + classInfo.hCursor = NULL; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = TEXT("SynergyTaskBar"); + classInfo.hIconSm = NULL; + ATOM windowClass = RegisterClassEx(&classInfo); + + // create window + m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, + reinterpret_cast(windowClass), + TEXT("Synergy Task Bar"), + WS_POPUP, + 0, 0, 1, 1, + NULL, + NULL, + s_appInstance, + reinterpret_cast(this)); + + // signal ready + ARCH->lockMutex(m_mutex); + m_ready = true; + ARCH->broadcastCondVar(m_condVar); + ARCH->unlockMutex(m_mutex); + + // handle failure + if (m_hwnd == NULL) { + UnregisterClass((LPCTSTR)windowClass, s_appInstance); + return; + } + + // main loop + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + if (!processDialogs(&msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + // clean up + removeAllIcons(); + DestroyWindow(m_hwnd); + UnregisterClass((LPCTSTR)windowClass, s_appInstance); +} + +void* +CArchTaskBarWindows::threadEntry(void* self) +{ + reinterpret_cast(self)->threadMainLoop(); + return NULL; +} diff --git a/lib/arch/CArchTaskBarWindows.h b/lib/arch/CArchTaskBarWindows.h index 5877637c..67e9af17 100644 --- a/lib/arch/CArchTaskBarWindows.h +++ b/lib/arch/CArchTaskBarWindows.h @@ -18,6 +18,7 @@ #define WIN32_LEAN_AND_MEAN #include "IArchTaskBar.h" +#include "IArchMultithread.h" #include "stdmap.h" #include "stdvector.h" #include @@ -77,13 +78,21 @@ private: LRESULT wndProc(HWND, UINT, WPARAM, LPARAM); static LRESULT CALLBACK staticWndProc(HWND, UINT, WPARAM, LPARAM); + void threadMainLoop(); + static void* threadEntry(void*); private: static CArchTaskBarWindows* s_instance; static HINSTANCE s_appInstance; + // multithread data + CArchMutex m_mutex; + CArchCond m_condVar; + bool m_ready; + int m_result; + CArchThread m_thread; + // child thread data - ATOM m_windowClass; HWND m_hwnd; UINT m_taskBarRestart; diff --git a/lib/arch/IArchTaskBar.h b/lib/arch/IArchTaskBar.h index 2cd20ded..e9471566 100644 --- a/lib/arch/IArchTaskBar.h +++ b/lib/arch/IArchTaskBar.h @@ -27,9 +27,6 @@ though each operation can be a no-op. */ class IArchTaskBar : public IInterface { public: - // Event data is architecture dependent - typedef void* Event; - //! @name manipulators //@{ From bf61301d4ca14c69ec35e9b0c4e6bd62a1ff1a42 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Mar 2004 22:20:27 +0000 Subject: [PATCH 629/807] Updated NEWS and ChangeLog for 1.1.4. --- ChangeLog | 47 +++++++++++++++++++++++++++++++++++++++++++++++ NEWS | 8 ++++++++ 2 files changed, 55 insertions(+) diff --git a/ChangeLog b/ChangeLog index 4e06cb70..03f063af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,50 @@ +2004/03/31 22:15:13 crs +lib/arch/CArchTaskBarWindows.cpp +lib/arch/CArchTaskBarWindows.h +lib/arch/IArchTaskBar.h + +Reverted task bar code to 1.0.15 version. That used a window in +its own thread for handling messages. It seems to fix most of +the task bar bugs but there's still an hourglass cursor on NT +when using the popup menu. + +---------- +2004/03/31 22:14:15 crs +lib/arch/CArchNetworkWinsock.cpp + +Fixed lookup of hosts by name on win32. + +---------- +2004/03/31 22:14:01 crs +cmd/launcher/launcher.rc + +Make screen drop down lists longer in the launcher. They're now +long enough for the scroll bar to show up properly (with the +thumb) and they have enough space for 6 screens without needing +the scroll bar. + +---------- +2004/03/31 22:12:53 crs +lib/server/CServer.cpp + +Fixed failure to initialize double tap and wait to switch timeouts. + +---------- +2004/03/30 18:55:58 crs +lib/synergy/CKeyState.cpp + +Fixed crash bug in CKeyState. Would deference bogus pointer in +isModifierActive if there's an unmapped toggle modifier. + +---------- +2004/03/30 18:54:56 crs +configure.in +lib/common/Version.h + +Changed version to 1.1.4. This time changing the version before +making any other changes. + +---------- 2004/03/28 14:53:01 crs lib/platform/CXWindowsKeyState.cpp diff --git a/NEWS b/NEWS index 944a0a2a..69ad6968 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,14 @@ Synergy News ============ +* Mar-31-2004 - Synergy 1.1.4 released + + Made following changes: + * Fixed lookup of hosts by name of win32 + * Reverted tray icon code to 1.0.15 version; seems to fix the bugs + * Fixed crash when caps, num, or scroll lock not in key map on X11 + * Fixed double tap and wait to switch features + * Mar-28-2004 - Synergy 1.1.3 released Made following changes: From 398ea44c20beab3c013bd8e82eddc26d514aeae4 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 31 Mar 2004 22:30:49 +0000 Subject: [PATCH 630/807] Minor win32 installer tweaks. --- dist/nullsoft/synergy.nsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/nullsoft/synergy.nsi b/dist/nullsoft/synergy.nsi index 00c5885f..2351176a 100644 --- a/dist/nullsoft/synergy.nsi +++ b/dist/nullsoft/synergy.nsi @@ -27,8 +27,8 @@ InstallDirRegKey HKLM "Software\Synergy" "Install_Dir" ; Pages -Page license Page components +Page license Page directory Page instfiles @@ -38,7 +38,7 @@ UninstPage instfiles ;-------------------------------- ; Text -ComponentText "This this install Synergy on your computer. Select the optional components you want to install." +ComponentText "This will install Synergy on your computer. Select the optional components you want to install." DirText "Choose a directory to install Synergy to:" UninstallText "This will uninstall Synergy from your computer." LicenseText "Synergy is distributed under the GNU GPL:" From ef908bf0bcd34a08dfdf259df809593d35944f6f Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 4 Apr 2004 12:12:30 +0000 Subject: [PATCH 631/807] Changed version to 1.1.5. --- configure.in | 24 ++++++++++++------------ lib/common/Version.h | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/configure.in b/configure.in index efcdea5b..ea209543 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=1 -RELEASE_VERSION=4 +RELEASE_VERSION=5 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) @@ -106,17 +106,6 @@ LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" AC_OUTPUT([ Makefile -lib/Makefile -lib/arch/Makefile -lib/base/Makefile -lib/common/Makefile -lib/mt/Makefile -lib/io/Makefile -lib/net/Makefile -lib/synergy/Makefile -lib/platform/Makefile -lib/client/Makefile -lib/server/Makefile cmd/Makefile cmd/launcher/Makefile cmd/synergyc/Makefile @@ -126,4 +115,15 @@ dist/nullsoft/Makefile dist/rpm/Makefile dist/rpm/synergy.spec doc/doxygen.cfg +lib/Makefile +lib/arch/Makefile +lib/base/Makefile +lib/client/Makefile +lib/common/Makefile +lib/io/Makefile +lib/mt/Makefile +lib/net/Makefile +lib/platform/Makefile +lib/server/Makefile +lib/synergy/Makefile ]) diff --git a/lib/common/Version.h b/lib/common/Version.h index 0a5d2e35..2e2adcaa 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.4" +# define VERSION "1.1.5" #endif // important strings From 083607345aa91b4e281ce7a40c0f7ed94cf64a71 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 4 Apr 2004 12:12:32 +0000 Subject: [PATCH 632/807] Removed DEPTH, VDEPTH, and VPATH from makefiles. --- Makefile.am | 2 -- cmd/Makefile.am | 2 -- cmd/launcher/Makefile.am | 2 -- cmd/synergyc/Makefile.am | 52 +++++++++++++++++++-------------------- cmd/synergys/Makefile.am | 52 +++++++++++++++++++-------------------- dist/Makefile.am | 2 -- dist/nullsoft/Makefile.am | 2 -- dist/rpm/Makefile.am | 2 -- lib/Makefile.am | 2 -- lib/arch/Makefile.am | 4 +-- lib/base/Makefile.am | 6 ++--- lib/client/Makefile.am | 18 ++++++-------- lib/common/Makefile.am | 2 -- lib/io/Makefile.am | 10 +++----- lib/mt/Makefile.am | 10 +++----- lib/net/Makefile.am | 12 ++++----- lib/platform/Makefile.am | 14 +++++------ lib/server/Makefile.am | 18 ++++++-------- lib/synergy/Makefile.am | 14 +++++------ 19 files changed, 94 insertions(+), 132 deletions(-) diff --git a/Makefile.am b/Makefile.am index edf9fec8..d3e210ac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = . -VDEPTH = ./$(VPATH) SUBDIRS = \ lib \ diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 3d028996..2472175e 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = .. -VDEPTH = ./$(VPATH)/$(DEPTH) SUBDIRS = \ launcher \ diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am index 782d771a..deb8c719 100644 --- a/cmd/launcher/Makefile.am +++ b/cmd/launcher/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ CAdvancedOptions.cpp \ diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 4f53508b..950a0072 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ CMSWindowsClientTaskBarReceiver.cpp \ @@ -40,31 +38,31 @@ synergyc_SOURCES = \ CXWindowsClientTaskBarReceiver.h \ synergyc.cpp \ $(NULL) -synergyc_LDADD = \ - $(DEPTH)/lib/client/libclient.a \ - $(DEPTH)/lib/platform/libplatform.a \ - $(DEPTH)/lib/synergy/libsynergy.a \ - $(DEPTH)/lib/net/libnet.a \ - $(DEPTH)/lib/io/libio.a \ - $(DEPTH)/lib/mt/libmt.a \ - $(DEPTH)/lib/base/libbase.a \ - $(DEPTH)/lib/common/libcommon.a \ - $(DEPTH)/lib/arch/libarch.a \ - $(X_LIBS) \ - $(X_PRE_LIBS) \ - -lXtst \ - -lXext \ - -lX11 \ - $(X_EXTRA_LIBS) \ +synergyc_LDADD = \ + $(top_builddir)/lib/client/libclient.a \ + $(top_builddir)/lib/platform/libplatform.a \ + $(top_builddir)/lib/synergy/libsynergy.a \ + $(top_builddir)/lib/net/libnet.a \ + $(top_builddir)/lib/io/libio.a \ + $(top_builddir)/lib/mt/libmt.a \ + $(top_builddir)/lib/base/libbase.a \ + $(top_builddir)/lib/common/libcommon.a \ + $(top_builddir)/lib/arch/libarch.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ - -I$(VDEPTH)/lib/mt \ - -I$(VDEPTH)/lib/io \ - -I$(VDEPTH)/lib/net \ - -I$(VDEPTH)/lib/synergy \ - -I$(VDEPTH)/lib/platform \ - -I$(VDEPTH)/lib/client \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ + -I$(top_srcdir)/lib/base \ + -I$(top_srcdir)/lib/mt \ + -I$(top_srcdir)/lib/io \ + -I$(top_srcdir)/lib/net \ + -I$(top_srcdir)/lib/synergy \ + -I$(top_srcdir)/lib/platform \ + -I$(top_srcdir)/lib/client \ $(NULL) diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index 5ef69af9..1a45c95f 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ CMSWindowsServerTaskBarReceiver.cpp \ @@ -40,31 +38,31 @@ synergys_SOURCES = \ CXWindowsServerTaskBarReceiver.h \ synergys.cpp \ $(NULL) -synergys_LDADD = \ - $(DEPTH)/lib/server/libserver.a \ - $(DEPTH)/lib/platform/libplatform.a \ - $(DEPTH)/lib/synergy/libsynergy.a \ - $(DEPTH)/lib/net/libnet.a \ - $(DEPTH)/lib/io/libio.a \ - $(DEPTH)/lib/mt/libmt.a \ - $(DEPTH)/lib/base/libbase.a \ - $(DEPTH)/lib/common/libcommon.a \ - $(DEPTH)/lib/arch/libarch.a \ - $(X_LIBS) \ - $(X_PRE_LIBS) \ - -lXtst \ - -lXext \ - -lX11 \ - $(X_EXTRA_LIBS) \ +synergys_LDADD = \ + $(top_builddir)/lib/server/libserver.a \ + $(top_builddir)/lib/platform/libplatform.a \ + $(top_builddir)/lib/synergy/libsynergy.a \ + $(top_builddir)/lib/net/libnet.a \ + $(top_builddir)/lib/io/libio.a \ + $(top_builddir)/lib/mt/libmt.a \ + $(top_builddir)/lib/base/libbase.a \ + $(top_builddir)/lib/common/libcommon.a \ + $(top_builddir)/lib/arch/libarch.a \ + $(X_LIBS) \ + $(X_PRE_LIBS) \ + -lXtst \ + -lXext \ + -lX11 \ + $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ - -I$(VDEPTH)/lib/mt \ - -I$(VDEPTH)/lib/io \ - -I$(VDEPTH)/lib/net \ - -I$(VDEPTH)/lib/synergy \ - -I$(VDEPTH)/lib/platform \ - -I$(VDEPTH)/lib/server \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ + -I$(top_srcdir)/lib/base \ + -I$(top_srcdir)/lib/mt \ + -I$(top_srcdir)/lib/io \ + -I$(top_srcdir)/lib/net \ + -I$(top_srcdir)/lib/synergy \ + -I$(top_srcdir)/lib/platform \ + -I$(top_srcdir)/lib/server \ $(NULL) diff --git a/dist/Makefile.am b/dist/Makefile.am index b23a284e..1af99c18 100644 --- a/dist/Makefile.am +++ b/dist/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = .. -VDEPTH = ./$(VPATH)/$(DEPTH) SUBDIRS = \ rpm \ diff --git a/dist/nullsoft/Makefile.am b/dist/nullsoft/Makefile.am index b45cc611..5630da82 100644 --- a/dist/nullsoft/Makefile.am +++ b/dist/nullsoft/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ installer.dsp \ diff --git a/dist/rpm/Makefile.am b/dist/rpm/Makefile.am index e19d9eec..0e86d9ba 100644 --- a/dist/rpm/Makefile.am +++ b/dist/rpm/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ synergy.spec.in \ diff --git a/lib/Makefile.am b/lib/Makefile.am index b0701a61..2a57133c 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = .. -VDEPTH = ./$(VPATH)/$(DEPTH) SUBDIRS = \ common \ diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am index 2b32473c..47888ade 100644 --- a/lib/arch/Makefile.am +++ b/lib/arch/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ arch.dsp \ @@ -97,5 +95,5 @@ EXTRA_libarch_a_SOURCES = \ XArchUnix.h \ $(NULL) INCLUDES = \ - -I$(VDEPTH)/lib/common \ + -I$(top_srcdir)/lib/common \ $(NULL) diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index 6ac0f716..87f48162 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ base.dsp \ @@ -59,6 +57,6 @@ libbase_a_SOURCES = \ XBase.h \ $(NULL) INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ $(NULL) diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index 9d6ba463..67b2b80e 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ client.dsp \ @@ -31,12 +29,12 @@ libclient_a_SOURCES = \ CServerProxy.h \ $(NULL) INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ - -I$(VDEPTH)/lib/mt \ - -I$(VDEPTH)/lib/io \ - -I$(VDEPTH)/lib/net \ - -I$(VDEPTH)/lib/synergy \ - -I$(VDEPTH)/lib/platform \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ + -I$(top_srcdir)/lib/base \ + -I$(top_srcdir)/lib/mt \ + -I$(top_srcdir)/lib/io \ + -I$(top_srcdir)/lib/net \ + -I$(top_srcdir)/lib/synergy \ + -I$(top_srcdir)/lib/platform \ $(NULL) diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index 34aaec8f..092fef3e 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ common.dsp \ diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index ad4f67fa..d7388187 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ io.dsp \ @@ -36,8 +34,8 @@ libio_a_SOURCES = \ XIO.h \ $(NULL) INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ - -I$(VDEPTH)/lib/mt \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ + -I$(top_srcdir)/lib/base \ + -I$(top_srcdir)/lib/mt \ $(NULL) diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index 1d0dc5ff..553cf6a3 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ mt.dsp \ @@ -37,8 +35,8 @@ libmt_a_SOURCES = \ XMT.h \ XThread.h \ $(NULL) -INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ +INCLUDES = \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ + -I$(top_srcdir)/lib/base \ $(NULL) diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index fc8dfe4a..aab44c0d 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ net.dsp \ @@ -48,9 +46,9 @@ libnet_a_SOURCES = \ XSocket.h \ $(NULL) INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ - -I$(VDEPTH)/lib/mt \ - -I$(VDEPTH)/lib/io \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ + -I$(top_srcdir)/lib/base \ + -I$(top_srcdir)/lib/mt \ + -I$(top_srcdir)/lib/io \ $(NULL) diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 54e70b29..9ac9c323 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ makehook.dsp \ @@ -69,10 +67,10 @@ libplatform_a_SOURCES = \ CXWindowsUtil.h \ $(NULL) INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ - -I$(VDEPTH)/lib/mt \ - -I$(VDEPTH)/lib/io \ - -I$(VDEPTH)/lib/synergy \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ + -I$(top_srcdir)/lib/base \ + -I$(top_srcdir)/lib/mt \ + -I$(top_srcdir)/lib/io \ + -I$(top_srcdir)/lib/synergy \ $(NULL) diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index 6dfd8a6f..7ffb76b4 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ server.dsp \ @@ -43,12 +41,12 @@ libserver_a_SOURCES = \ CServer.h \ $(NULL) INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ - -I$(VDEPTH)/lib/mt \ - -I$(VDEPTH)/lib/io \ - -I$(VDEPTH)/lib/net \ - -I$(VDEPTH)/lib/synergy \ - -I$(VDEPTH)/lib/platform \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ + -I$(top_srcdir)/lib/base \ + -I$(top_srcdir)/lib/mt \ + -I$(top_srcdir)/lib/io \ + -I$(top_srcdir)/lib/net \ + -I$(top_srcdir)/lib/synergy \ + -I$(top_srcdir)/lib/platform \ $(NULL) diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 27fe0c63..0265490c 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -12,8 +12,6 @@ ## Process this file with automake to produce Makefile.in NULL = -DEPTH = ../.. -VDEPTH = ./$(VPATH)/$(DEPTH) EXTRA_DIST = \ libsynergy.dsp \ @@ -60,10 +58,10 @@ libsynergy_a_SOURCES = \ XSynergy.h \ $(NULL) INCLUDES = \ - -I$(VDEPTH)/lib/common \ - -I$(VDEPTH)/lib/arch \ - -I$(VDEPTH)/lib/base \ - -I$(VDEPTH)/lib/mt \ - -I$(VDEPTH)/lib/io \ - -I$(VDEPTH)/lib/net \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ + -I$(top_srcdir)/lib/base \ + -I$(top_srcdir)/lib/mt \ + -I$(top_srcdir)/lib/io \ + -I$(top_srcdir)/lib/net \ $(NULL) From 6d6ebf792632d80488618ce391c9e21f08832b22 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 5 Apr 2004 21:08:49 +0000 Subject: [PATCH 633/807] Made hook debug logging print at DEBUG1 rather than INFO level. --- lib/platform/CMSWindowsScreen.cpp | 2 +- lib/platform/CMSWindowsScreen.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 8f627928..392093ff 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -690,7 +690,7 @@ CMSWindowsScreen::onPreDispatch(HWND hwnd, return onScreensaver(wParam != 0); case SYNERGY_MSG_DEBUG: - LOG((CLOG_INFO "hook: 0x%08x 0x%08x", wParam, lParam)); + LOG((CLOG_DEBUG1 "hook: 0x%08x 0x%08x", wParam, lParam)); return true; } diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index e9e51a06..b0695f76 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -199,6 +199,7 @@ private: // last mouse position SInt32 m_xCursor, m_yCursor; + // last clipboard UInt32 m_sequenceNumber; // used to discard queued messages that are no longer needed From 3db9facb6c8721d4971a61a0d936688df082ddfc Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 5 Apr 2004 21:10:06 +0000 Subject: [PATCH 634/807] Added workaround for win32 low-level mouse hook position weirdness. The low-level hook can report mouse positions outside the boundaries of the screen and bogus retrograde motion. This messes up switch on double tap. This change attempts to detect and suppress the bogus events. --- lib/platform/CSynergyHook.cpp | 44 ++++++++++++++++++++----- lib/server/CServer.cpp | 60 +++++++++++++++++++++++++++++++---- lib/server/CServer.h | 7 ++++ 3 files changed, 96 insertions(+), 15 deletions(-) diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 0fb0b79c..a4aeb098 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -457,6 +457,33 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) return true; } else if (g_mode == kHOOK_WATCH_JUMP_ZONE) { + // low level hooks can report bogus mouse positions that are + // outside of the screen. jeez. naturally we end up getting + // fake motion in the other direction to get the position back + // on the screen, which plays havoc with switch on double tap. + // CServer deals with that. we'll clamp positions onto the + // screen. also, if we discard events for positions outside + // of the screen then the mouse appears to get a bit jerky + // near the edge. we can either accept that or pass the bogus + // events. we'll try passing the events. + bool bogus = false; + if (x < g_xScreen) { + x = g_xScreen; + bogus = true; + } + else if (x >= g_xScreen + g_wScreen) { + x = g_xScreen + g_wScreen - 1; + bogus = true; + } + if (y < g_yScreen) { + y = g_yScreen; + bogus = true; + } + else if (y >= g_yScreen + g_hScreen) { + y = g_yScreen + g_hScreen - 1; + bogus = true; + } + // check for mouse inside jump zone bool inside = false; if (!inside && (g_zoneSides & kLeftMask) != 0) { @@ -475,8 +502,8 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) // relay the event PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); - // if inside then eat the event - return inside; + // if inside and not bogus then eat the event + return inside && !bogus; } } @@ -518,13 +545,13 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) // them. switch (g_wheelSupport) { case kWheelModern: - w = static_cast(LOWORD(info->dwExtraInfo)); + w = static_cast(LOWORD(info->dwExtraInfo)); break; case kWheelWin2000: { const MOUSEHOOKSTRUCTWin2000* info2k = (const MOUSEHOOKSTRUCTWin2000*)lParam; - w = static_cast(HIWORD(info2k->mouseData)); + w = static_cast(HIWORD(info2k->mouseData)); break; } } @@ -561,7 +588,8 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam) if (msg->message == g_wmMouseWheel) { // post message to our window PostThreadMessage(g_threadID, - SYNERGY_MSG_MOUSE_WHEEL, msg->wParam, 0); + SYNERGY_MSG_MOUSE_WHEEL, + static_cast(msg->wParam & 0xffffu), 0); // zero out the delta in the message so it's (hopefully) // ignored @@ -627,9 +655,9 @@ mouseLLHook(int code, WPARAM wParam, LPARAM lParam) if (code >= 0) { // decode the message MSLLHOOKSTRUCT* info = reinterpret_cast(lParam); - SInt32 x = (SInt32)info->pt.x; - SInt32 y = (SInt32)info->pt.y; - SInt32 w = (SInt32)HIWORD(info->mouseData); + SInt32 x = static_cast(info->pt.x); + SInt32 y = static_cast(info->pt.y); + SInt32 w = static_cast(HIWORD(info->mouseData)); // handle the message if (mouseHookHandler(wParam, x, y, w)) { diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 79a13bb9..912272b6 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -40,6 +40,10 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_primaryClient(primaryClient), m_active(primaryClient), m_seqNum(0), + m_xDelta(0), + m_yDelta(0), + m_xDelta2(0), + m_yDelta2(0), m_config(config), m_activeSaver(NULL), m_switchDir(kNoDirection), @@ -374,8 +378,12 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) stopSwitch(); // record new position - m_x = x; - m_y = y; + m_x = x; + m_y = y; + m_xDelta = 0; + m_yDelta = 0; + m_xDelta2 = 0; + m_yDelta2 = 0; // wrapping means leaving the active screen and entering it again. // since that's a waste of time we skip that and just warp the @@ -758,7 +766,29 @@ CServer::armSwitchTwoTap(SInt32 x, SInt32 y) } if (x >= ax + tapZone && x < ax + aw - tapZone && y >= ay + tapZone && y < ay + ah - tapZone) { - m_switchTwoTapArmed = true; + // win32 can generate bogus mouse events that appear to + // move in the opposite direction that the mouse actually + // moved. try to ignore that crap here. + switch (m_switchDir) { + case kLeft: + m_switchTwoTapArmed = (m_xDelta > 0 && m_xDelta2 > 0); + break; + + case kRight: + m_switchTwoTapArmed = (m_xDelta < 0 && m_xDelta2 < 0); + break; + + case kTop: + m_switchTwoTapArmed = (m_yDelta > 0 && m_yDelta2 > 0); + break; + + case kBottom: + m_switchTwoTapArmed = (m_yDelta < 0 && m_yDelta2 < 0); + break; + + default: + break; + } } } } @@ -1242,9 +1272,17 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y) return false; } + // save last delta + m_xDelta2 = m_xDelta; + m_yDelta2 = m_yDelta; + + // save current delta + m_xDelta = x - m_x; + m_yDelta = y - m_y; + // save position - m_x = x; - m_y = y; + m_x = x; + m_y = y; // get screen shape SInt32 ax, ay, aw, ah; @@ -1305,9 +1343,17 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) const SInt32 xOld = m_x; const SInt32 yOld = m_y; + // save last delta + m_xDelta2 = m_xDelta; + m_yDelta2 = m_yDelta; + + // save current delta + m_xDelta = dx; + m_yDelta = dy; + // accumulate motion - m_x += dx; - m_y += dy; + m_x += dx; + m_y += dy; // get screen shape SInt32 ax, ay, aw, ah; diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 286eab0e..0e28eee7 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -278,6 +278,13 @@ private: // whichever screen is active SInt32 m_x, m_y; + // last mouse deltas. this is needed to smooth out double tap + // on win32 which reports bogus mouse motion at the edge of + // the screen when using low level hooks, synthesizing motion + // in the opposite direction the mouse actually moved. + SInt32 m_xDelta, m_yDelta; + SInt32 m_xDelta2, m_yDelta2; + // current configuration CConfig m_config; From bede6352c14bfc17f5941b56e791cf8c641427a8 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 5 Apr 2004 21:23:44 +0000 Subject: [PATCH 635/807] Fixed bug in handling rejection of screen with name that's already in use. The client was being correctly rejected but the already connected client was being forcefully disconnected too because the client to disconnect was found by looking up the client by name. We now instead look up the client by IClient*. --- lib/server/CServer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 912272b6..27a8651a 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -1507,8 +1507,8 @@ bool CServer::removeClient(IClient* client) { // return false if not in list - CClientList::iterator i = m_clients.find(getName(client)); - if (i == m_clients.end()) { + CClientSet::iterator i = m_clientSet.find(client); + if (i == m_clientSet.end()) { return false; } @@ -1521,8 +1521,8 @@ CServer::removeClient(IClient* client) client->getEventTarget()); // remove from list - m_clients.erase(i); - m_clientSet.erase(client); + m_clients.erase(getName(client)); + m_clientSet.erase(i); return true; } From 619dd75f0dbbd8653b5a8b17b1360c6e66fd3c58 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 6 Apr 2004 22:09:38 +0000 Subject: [PATCH 636/807] Added missing initialization of mutex attribute call. --- lib/arch/CArchMultithreadPosix.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index ea292412..1ccbeed2 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -295,7 +295,9 @@ CArchMutex CArchMultithreadPosix::newMutex() { pthread_mutexattr_t attr; - int status = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + int status = pthread_mutexattr_init(&attr); + assert(status == 0); + status = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); assert(status == 0); CArchMutexImpl* mutex = new CArchMutexImpl; status = pthread_mutex_init(&mutex->m_mutex, &attr); From 530be3ff9d30fff73f328a38683e76c26321ed33 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Apr 2004 14:58:08 +0000 Subject: [PATCH 637/807] Updates to support OS X. This improves support for building on multiple systems with automake, with X Windows and Carbon window system APIs supported. It's also a starting port for supporting win32 builds using mingw. OS X support is incomplete; the tree will compile and link but the binaries will not function. --- PORTING | 42 ++- cmd/synergyc/COSXClientTaskBarReceiver.cpp | 56 +++ cmd/synergyc/COSXClientTaskBarReceiver.h | 35 ++ cmd/synergyc/Makefile.am | 52 ++- cmd/synergyc/synergyc.cpp | 33 +- cmd/synergys/COSXServerTaskBarReceiver.cpp | 56 +++ cmd/synergys/COSXServerTaskBarReceiver.h | 35 ++ cmd/synergys/Makefile.am | 54 ++- cmd/synergys/synergys.cpp | 35 +- configure.in | 77 +++- lib/arch/CArch.cpp | 6 +- lib/arch/CArchDaemonUnix.cpp | 3 - lib/arch/CArchFileUnix.cpp | 1 + lib/arch/CArchImpl.cpp | 45 --- lib/arch/CArchNetworkBSD.cpp | 1 + lib/arch/CMultibyte.cpp | 2 +- lib/arch/CMultibyteOS.cpp | 1 + lib/arch/Makefile.am | 110 +++--- lib/arch/vsnprintf.cpp | 4 +- lib/base/CPriorityQueue.h | 2 +- lib/common/BasicTypes.h | 5 +- lib/common/common.h | 73 ++-- lib/platform/COSXClipboard.cpp | 70 ++++ lib/platform/COSXClipboard.h | 36 ++ lib/platform/COSXEventQueueBuffer.cpp | 76 ++++ lib/platform/COSXEventQueueBuffer.h | 40 ++ lib/platform/COSXKeyState.cpp | 294 +++++++++++++++ lib/platform/COSXKeyState.h | 55 +++ lib/platform/COSXScreen.cpp | 407 +++++++++++++++++++++ lib/platform/COSXScreen.h | 110 ++++++ lib/platform/COSXScreenSaver.cpp | 56 +++ lib/platform/COSXScreenSaver.h | 34 ++ lib/platform/CSynergyHook.h | 5 - lib/platform/CXWindowsEventQueueBuffer.cpp | 30 +- lib/platform/CXWindowsScreen.cpp | 18 - lib/platform/Makefile.am | 75 ++-- 36 files changed, 1757 insertions(+), 277 deletions(-) create mode 100644 cmd/synergyc/COSXClientTaskBarReceiver.cpp create mode 100644 cmd/synergyc/COSXClientTaskBarReceiver.h create mode 100644 cmd/synergys/COSXServerTaskBarReceiver.cpp create mode 100644 cmd/synergys/COSXServerTaskBarReceiver.h delete mode 100644 lib/arch/CArchImpl.cpp create mode 100644 lib/platform/COSXClipboard.cpp create mode 100644 lib/platform/COSXClipboard.h create mode 100644 lib/platform/COSXEventQueueBuffer.cpp create mode 100644 lib/platform/COSXEventQueueBuffer.h create mode 100644 lib/platform/COSXKeyState.cpp create mode 100644 lib/platform/COSXKeyState.h create mode 100644 lib/platform/COSXScreen.cpp create mode 100644 lib/platform/COSXScreen.h create mode 100644 lib/platform/COSXScreenSaver.cpp create mode 100644 lib/platform/COSXScreenSaver.h diff --git a/PORTING b/PORTING index a771799d..2e31bbe7 100644 --- a/PORTING +++ b/PORTING @@ -333,16 +333,25 @@ other files. On Unix, synergy uses autoconf/automake which produces a `configure' script that generates makefiles. On Windows, synergy uses Visual C++ workspace and project files. If you're porting to another Unix variant, you may need to adjust `configure.in', -`acinclude.m4', and Unix flavor dependent code in lib/arch. +`acinclude.m4', and Unix flavor dependent code in lib/arch. Note +especially the SYSAPI_* and WINAPI_* macro definitions in +ARCH_CFLAGS. Exactly one of each must be defined. It should also +add AM_CONDITIONALs if a new SYSAPI_* or WINAPI_* was added. Adjusting lib/common/common.h: The lib/common/common.h header file is included directly or indirectly -by every other file. It prepares some platform dependent macros for -integer sizes and defines a macro for conveniently testing which -platform we're building on. That macro is named *_LIKE (e.g. UNIX_LIKE) -and has the value `1'. Exactly one *_LIKE macro must be defined by -common.h. +by every other file. Its primary job is to include config.h, which +defines macros depending on what the 'configure' script discovered +about the system. If the platform does not use the 'configure' script +it must define the appropriate SYSAPI_* and WINAPI_* macro. It may +also do other platform specific setup. + +Adjusting lib/common/BasicTypes.h: + +No changes should be necessary in BasicTypes.h. However, if the +platform's system header files define SInt8, et al. you may need +to adjust the typedefs to match the system's definitions. Implementing lib/arch: @@ -355,6 +364,18 @@ CArchMiscXXX where XXX is the platform name. The class should have only static methods. Clients can include the appropriate header file and make calls directly, surrounded by a suitable #ifdef/#endif. +If using automake, the Makefile.am should list the system specific +files in a XXX_SOURCE_FILES macro where XXX matches the appropriate +AM_CONDITIONAL symbol. XXX_SOURCE_FILES must be added to EXTRA_DIST +and the following added above the INCLUDES macro: + + if XXX + libarch_a_SOURCES = \ + $(COMMON_SOURCE_FILES) \ + $(XXX_SOURCE_FILES) \ + $(NULL) + endif + Implementing lib/platform: Most of the remaining platform dependent code lives in lib/platform. @@ -381,6 +402,15 @@ classes should be derived and implemented: * CXXXScreenSaver : IScreenSaver Provides screen saver operations. +If using automake, the Makefile.am should list the window system +specific files in a XXX_SOURCE_FILES macro where XXX matches the +appropriate AM_CONDITIONAL symbol. XXX_SOURCE_FILES must be added +to EXTRA_DIST and the following added above the INCLUDES macro: + + if XXX + libplatform_a_SOURCES = $(XXX_SOURCE_FILES) + endif + Tweaks: Finally, each platform typically requires various adjustments here diff --git a/cmd/synergyc/COSXClientTaskBarReceiver.cpp b/cmd/synergyc/COSXClientTaskBarReceiver.cpp new file mode 100644 index 00000000..c380ac4d --- /dev/null +++ b/cmd/synergyc/COSXClientTaskBarReceiver.cpp @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COSXClientTaskBarReceiver.h" +#include "CArch.h" + +// +// COSXClientTaskBarReceiver +// + +COSXClientTaskBarReceiver::COSXClientTaskBarReceiver( + const CBufferedLogOutputter*) +{ + // add ourself to the task bar + ARCH->addReceiver(this); +} + +COSXClientTaskBarReceiver::~COSXClientTaskBarReceiver() +{ + ARCH->removeReceiver(this); +} + +void +COSXClientTaskBarReceiver::showStatus() +{ + // do nothing +} + +void +COSXClientTaskBarReceiver::runMenu(int, int) +{ + // do nothing +} + +void +COSXClientTaskBarReceiver::primaryAction() +{ + // do nothing +} + +const IArchTaskBarReceiver::Icon +COSXClientTaskBarReceiver::getIcon() const +{ + return NULL; +} diff --git a/cmd/synergyc/COSXClientTaskBarReceiver.h b/cmd/synergyc/COSXClientTaskBarReceiver.h new file mode 100644 index 00000000..59bca97c --- /dev/null +++ b/cmd/synergyc/COSXClientTaskBarReceiver.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXCLIENTTASKBARRECEIVER_H +#define COSXCLIENTTASKBARRECEIVER_H + +#include "CClientTaskBarReceiver.h" + +class CBufferedLogOutputter; + +//! Implementation of CClientTaskBarReceiver for OS X +class COSXClientTaskBarReceiver : public CClientTaskBarReceiver { +public: + COSXClientTaskBarReceiver(const CBufferedLogOutputter*); + virtual ~COSXClientTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; +}; + +#endif diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 950a0072..783472c2 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -13,17 +13,36 @@ ## Process this file with automake to produce Makefile.in NULL = -EXTRA_DIST = \ +COMMON_SOURCE_FILES = \ + CClientTaskBarReceiver.cpp \ + CClientTaskBarReceiver.h \ + synergyc.cpp \ + $(NULL) +XWINDOWS_SOURCE_FILES = \ + CXWindowsClientTaskBarReceiver.cpp \ + CXWindowsClientTaskBarReceiver.h \ + $(NULL) +MSWINDOWS_SOURCE_FILES = \ CMSWindowsClientTaskBarReceiver.cpp \ CMSWindowsClientTaskBarReceiver.h \ resource.h \ + synergyc.rc \ + $(NULL) +CARBON_SOURCE_FILES = \ + COSXClientTaskBarReceiver.cpp \ + COSXClientTaskBarReceiver.h \ + $(NULL) + +EXTRA_DIST = \ synergyc.dsp \ synergyc.ico \ - synergyc.rc \ tb_error.ico \ tb_idle.ico \ tb_run.ico \ tb_wait.ico \ + $(XWINDOWS_SOURCE_FILES) \ + $(MSWINDOWS_SOURCE_FILES) \ + $(CARBON_SOURCE_FILES) \ $(NULL) MAINTAINERCLEANFILES = \ @@ -31,13 +50,24 @@ MAINTAINERCLEANFILES = \ $(NULL) bin_PROGRAMS = synergyc -synergyc_SOURCES = \ - CClientTaskBarReceiver.cpp \ - CClientTaskBarReceiver.h \ - CXWindowsClientTaskBarReceiver.cpp \ - CXWindowsClientTaskBarReceiver.h \ - synergyc.cpp \ +if XWINDOWS +synergyc_SOURCES = \ + $(COMMON_SOURCE_FILES) \ + $(XWINDOWS_SOURCE_FILES) \ $(NULL) +endif +if MSWINDOWS +synergyc_SOURCES = \ + $(COMMON_SOURCE_FILES) \ + $(MSWINDOWS_SOURCE_FILES) \ + $(NULL) +endif +if CARBON +synergyc_SOURCES = \ + $(COMMON_SOURCE_FILES) \ + $(CARBON_SOURCE_FILES) \ + $(NULL) +endif synergyc_LDADD = \ $(top_builddir)/lib/client/libclient.a \ $(top_builddir)/lib/platform/libplatform.a \ @@ -48,12 +78,6 @@ synergyc_LDADD = \ $(top_builddir)/lib/base/libbase.a \ $(top_builddir)/lib/common/libcommon.a \ $(top_builddir)/lib/arch/libarch.a \ - $(X_LIBS) \ - $(X_PRE_LIBS) \ - -lXtst \ - -lXext \ - -lX11 \ - $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ -I$(top_srcdir)/lib/common \ diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 9e7be5ab..a10601fd 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -34,7 +34,7 @@ #include #define DAEMON_RUNNING(running_) -#if WINDOWS_LIKE +#if WINAPI_MSWINDOWS #include "CArchMiscWindows.h" #include "CMSWindowsScreen.h" #include "CMSWindowsUtil.h" @@ -42,22 +42,25 @@ #include "resource.h" #undef DAEMON_RUNNING #define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) -#elif UNIX_LIKE +#elif WINAPI_XWINDOWS #include "CXWindowsScreen.h" #include "CXWindowsClientTaskBarReceiver.h" +#elif WINAPI_CARBON +#include "COSXScreen.h" +#include "COSXClientTaskBarReceiver.h" #endif // platform dependent name of a daemon -#if WINDOWS_LIKE +#if SYSAPI_WIN32 #define DAEMON_NAME "Synergy Client" -#elif UNIX_LIKE +#elif SYSAPI_UNIX #define DAEMON_NAME "synergyc" #endif typedef int (*StartupFunc)(int, char**); static bool startClient(); static void parse(int argc, const char* const* argv); -#if WINDOWS_LIKE +#if WINAPI_MSWINDOWS static void handleSystemSuspend(void*); static void handleSystemResume(void*); #endif @@ -102,12 +105,14 @@ static CScreen* createScreen() { -#if WINDOWS_LIKE +#if WINAPI_MSWINDOWS return new CScreen(new CMSWindowsScreen(false, new CFunctionJob(&handleSystemSuspend), new CFunctionJob(&handleSystemResume))); -#elif UNIX_LIKE +#elif WINAPI_XWINDOWS return new CScreen(new CXWindowsScreen(false)); +#elif WINAPI_CARBON + return new CScreen(new COSXScreen(false)); #endif } @@ -115,11 +120,13 @@ static CClientTaskBarReceiver* createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) { -#if WINDOWS_LIKE +#if WINAPI_MSWINDOWS return new CMSWindowsClientTaskBarReceiver( CMSWindowsScreen::getInstance(), logBuffer); -#elif UNIX_LIKE +#elif WINAPI_XWINDOWS return new CXWindowsClientTaskBarReceiver(logBuffer); +#elif WINAPI_CARBON + return new COSXClientTaskBarReceiver(logBuffer); #endif } @@ -190,7 +197,7 @@ handleScreenError(const CEvent&, void*) EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } -#if WINDOWS_LIKE +#if WINAPI_MSWINDOWS static void handleSystemSuspend(void*) @@ -676,7 +683,7 @@ parse(int argc, const char* const* argv) // increase default filter level for daemon. the user must // explicitly request another level for a daemon. if (ARG->m_daemon && ARG->m_logFilter == NULL) { -#if WINDOWS_LIKE +#if SYSAPI_WIN32 if (CArchMiscWindows::isWindows95Family()) { // windows 95 has no place for logging so avoid showing // the log console window. @@ -702,7 +709,7 @@ parse(int argc, const char* const* argv) // platform dependent entry points // -#if WINDOWS_LIKE +#if SYSAPI_WIN32 static bool s_hasImportantLogMessages = false; @@ -825,7 +832,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) } } -#elif UNIX_LIKE +#elif SYSAPI_UNIX int main(int argc, char** argv) diff --git a/cmd/synergys/COSXServerTaskBarReceiver.cpp b/cmd/synergys/COSXServerTaskBarReceiver.cpp new file mode 100644 index 00000000..8195b84f --- /dev/null +++ b/cmd/synergys/COSXServerTaskBarReceiver.cpp @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COSXServerTaskBarReceiver.h" +#include "CArch.h" + +// +// COSXServerTaskBarReceiver +// + +COSXServerTaskBarReceiver::COSXServerTaskBarReceiver( + const CBufferedLogOutputter*) +{ + // add ourself to the task bar + ARCH->addReceiver(this); +} + +COSXServerTaskBarReceiver::~COSXServerTaskBarReceiver() +{ + ARCH->removeReceiver(this); +} + +void +COSXServerTaskBarReceiver::showStatus() +{ + // do nothing +} + +void +COSXServerTaskBarReceiver::runMenu(int, int) +{ + // do nothing +} + +void +COSXServerTaskBarReceiver::primaryAction() +{ + // do nothing +} + +const IArchTaskBarReceiver::Icon +COSXServerTaskBarReceiver::getIcon() const +{ + return NULL; +} diff --git a/cmd/synergys/COSXServerTaskBarReceiver.h b/cmd/synergys/COSXServerTaskBarReceiver.h new file mode 100644 index 00000000..7f6dc298 --- /dev/null +++ b/cmd/synergys/COSXServerTaskBarReceiver.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXSERVERTASKBARRECEIVER_H +#define COSXSERVERTASKBARRECEIVER_H + +#include "CServerTaskBarReceiver.h" + +class CBufferedLogOutputter; + +//! Implementation of CServerTaskBarReceiver for OS X +class COSXServerTaskBarReceiver : public CServerTaskBarReceiver { +public: + COSXServerTaskBarReceiver(const CBufferedLogOutputter*); + virtual ~COSXServerTaskBarReceiver(); + + // IArchTaskBarReceiver overrides + virtual void showStatus(); + virtual void runMenu(int x, int y); + virtual void primaryAction(); + virtual const Icon getIcon() const; +}; + +#endif diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index 1a45c95f..c45e6b4f 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -13,17 +13,36 @@ ## Process this file with automake to produce Makefile.in NULL = -EXTRA_DIST = \ +COMMON_SOURCE_FILES = \ + CServerTaskBarReceiver.cpp \ + CServerTaskBarReceiver.h \ + synergys.cpp \ + $(NULL) +XWINDOWS_SOURCE_FILES = \ + CXWindowsServerTaskBarReceiver.cpp \ + CXWindowsServerTaskBarReceiver.h \ + $(NULL) +MSWINDOWS_SOURCE_FILES = \ CMSWindowsServerTaskBarReceiver.cpp \ CMSWindowsServerTaskBarReceiver.h \ resource.h \ - synergys.ico \ - synergys.dsp \ synergys.rc \ + $(NULL) +CARBON_SOURCE_FILES = \ + COSXServerTaskBarReceiver.cpp \ + COSXServerTaskBarReceiver.h \ + $(NULL) + +EXTRA_DIST = \ + synergys.dsp \ + synergys.ico \ tb_error.ico \ tb_idle.ico \ tb_run.ico \ tb_wait.ico \ + $(XWINDOWS_SOURCE_FILES) \ + $(MSWINDOWS_SOURCE_FILES) \ + $(CARBON_SOURCE_FILES) \ $(NULL) MAINTAINERCLEANFILES = \ @@ -31,13 +50,24 @@ MAINTAINERCLEANFILES = \ $(NULL) bin_PROGRAMS = synergys -synergys_SOURCES = \ - CServerTaskBarReceiver.cpp \ - CServerTaskBarReceiver.h \ - CXWindowsServerTaskBarReceiver.cpp \ - CXWindowsServerTaskBarReceiver.h \ - synergys.cpp \ +if XWINDOWS +synergys_SOURCES = \ + $(COMMON_SOURCE_FILES) \ + $(XWINDOWS_SOURCE_FILES) \ $(NULL) +endif +if MSWINDOWS +synergys_SOURCES = \ + $(COMMON_SOURCE_FILES) \ + $(MSWINDOWS_SOURCE_FILES) \ + $(NULL) +endif +if CARBON +synergys_SOURCES = \ + $(COMMON_SOURCE_FILES) \ + $(CARBON_SOURCE_FILES) \ + $(NULL) +endif synergys_LDADD = \ $(top_builddir)/lib/server/libserver.a \ $(top_builddir)/lib/platform/libplatform.a \ @@ -48,12 +78,6 @@ synergys_LDADD = \ $(top_builddir)/lib/base/libbase.a \ $(top_builddir)/lib/common/libcommon.a \ $(top_builddir)/lib/arch/libarch.a \ - $(X_LIBS) \ - $(X_PRE_LIBS) \ - -lXtst \ - -lXext \ - -lX11 \ - $(X_EXTRA_LIBS) \ $(NULL) INCLUDES = \ -I$(top_srcdir)/lib/common \ diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index b1cdaed5..49aca596 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -37,7 +37,7 @@ #include #define DAEMON_RUNNING(running_) -#if WINDOWS_LIKE +#if WINAPI_MSWINDOWS #include "CArchMiscWindows.h" #include "CMSWindowsScreen.h" #include "CMSWindowsUtil.h" @@ -45,23 +45,26 @@ #include "resource.h" #undef DAEMON_RUNNING #define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) -#elif UNIX_LIKE +#elif WINAPI_XWINDOWS #include "CXWindowsScreen.h" #include "CXWindowsServerTaskBarReceiver.h" +#elif WINAPI_CARBON +#include "COSXScreen.h" +#include "COSXServerTaskBarReceiver.h" #endif // platform dependent name of a daemon -#if WINDOWS_LIKE +#if SYSAPI_WIN32 #define DAEMON_NAME "Synergy Server" -#elif UNIX_LIKE +#elif SYSAPI_UNIX #define DAEMON_NAME "synergys" #endif // configuration file name -#if WINDOWS_LIKE +#if SYSAPI_WIN32 #define USR_CONFIG_NAME "synergy.sgc" #define SYS_CONFIG_NAME "synergy.sgc" -#elif UNIX_LIKE +#elif SYSAPI_UNIX #define USR_CONFIG_NAME ".synergy.conf" #define SYS_CONFIG_NAME "synergy.conf" #endif @@ -113,10 +116,12 @@ static CScreen* createScreen() { -#if WINDOWS_LIKE +#if WINAPI_MSWINDOWS return new CScreen(new CMSWindowsScreen(true, NULL, NULL)); -#elif UNIX_LIKE +#elif WINAPI_XWINDOWS return new CScreen(new CXWindowsScreen(true)); +#elif WINAPI_CARBON + return new CScreen(new COSXScreen(true)); #endif } @@ -124,11 +129,13 @@ static CServerTaskBarReceiver* createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) { -#if WINDOWS_LIKE +#if WINAPI_MSWINDOWS return new CMSWindowsServerTaskBarReceiver( CMSWindowsScreen::getInstance(), logBuffer); -#elif UNIX_LIKE +#elif WINAPI_XWINDOWS return new CXWindowsServerTaskBarReceiver(logBuffer); +#elif WINAPI_CARBON + return new COSXServerTaskBarReceiver(logBuffer); #endif } @@ -579,7 +586,7 @@ static void help() { -#if WINDOWS_LIKE +#if SYSAPI_WIN32 # define PLATFORM_ARGS \ " {--daemon|--no-daemon}" @@ -778,7 +785,7 @@ parse(int argc, const char* const* argv) // increase default filter level for daemon. the user must // explicitly request another level for a daemon. if (ARG->m_daemon && ARG->m_logFilter == NULL) { -#if WINDOWS_LIKE +#if SYSAPI_WIN32 if (CArchMiscWindows::isWindows95Family()) { // windows 95 has no place for logging so avoid showing // the log console window. @@ -870,7 +877,7 @@ loadConfig() // platform dependent entry points // -#if WINDOWS_LIKE +#if SYSAPI_WIN32 static bool s_hasImportantLogMessages = false; @@ -994,7 +1001,7 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) } } -#elif UNIX_LIKE +#elif SYSAPI_UNIX int main(int argc, char** argv) diff --git a/configure.in b/configure.in index ea209543..242d6ca3 100644 --- a/configure.in +++ b/configure.in @@ -27,6 +27,33 @@ AM_CONFIG_HEADER(config.h) dnl information on the package +dnl decide on platform +ARCH_LIBS="" +ARCH_CFLAGS="" +AC_CANONICAL_HOST +case $host in + *-*-windows*) + acx_host_arch="WIN32" + acx_host_winapi="MSWINDOWS" + ARCH_CFLAGS="$ARCH_CFLAGS -DSYSAPI_WIN32=1 -DWINAPI_MSWINDOWS=1" + ;; + *-*-darwin*) + acx_host_arch="UNIX" + acx_host_winapi="CARBON" + ARCH_CFLAGS="$ARCH_CFLAGS -DSYSAPI_UNIX=1 -DWINAPI_CARBON=1" + ;; + *) + acx_host_arch="UNIX" + acx_host_winapi="XWINDOWS" + ARCH_CFLAGS="$ARCH_CFLAGS -DSYSAPI_UNIX=1 -DWINAPI_XWINDOWS=1" + ;; +esac +AM_CONDITIONAL(WIN32, test x$acx_host_arch = xWIN32) +AM_CONDITIONAL(UNIX, test x$acx_host_arch = xUNIX) +AM_CONDITIONAL(MSWINDOWS, test x$acx_host_winapi = xMSWINDOWS) +AM_CONDITIONAL(CARBON, test x$acx_host_winapi = xCARBON) +AM_CONDITIONAL(XWINDOWS, test x$acx_host_winapi = xXWINDOWS) + dnl checks for programs AC_PROG_CXX AC_PROG_RANLIB @@ -45,7 +72,14 @@ dnl macros we should do testing with CXXFLAGS="$CXXFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500" dnl checks for libraries -ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) +if test x"$acx_host_arch" = xUNIX; then + ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) + ARCH_LIBS="$PTHREAD_LIBS $ARCH_LIBS" + ARCH_CFLAGS="$ARCH_CFLAGS $PTHREAD_CFLAGS" +fi +if test x"$acx_host_winapi" = xCARBON; then + ARCH_LIBS="-framework Carbon $ARCH_LIBS" +fi ACX_CHECK_NANOSLEEP ACX_CHECK_INET_ATON @@ -55,14 +89,33 @@ AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h wchar.h]) AC_CHECK_HEADERS([sys/socket.h sys/select.h]) AC_CHECK_HEADERS([istream ostream sstream]) AC_HEADER_TIME -AC_PATH_X -AC_PATH_XTRA -save_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$X_CFLAGS $CPPFLAGS" -AC_CHECK_HEADERS([X11/extensions/XTest.h]) -AC_CHECK_LIB(Xinerama, XineramaQueryExtension, AC_CHECK_HEADERS([X11/extensions/Xinerama.h]) [X_LIBS="$X_LIBS -lXinerama"], , [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) -AC_CHECK_LIB(Xext, DPMSQueryExtension, AC_CHECK_HEADERS([X11/extensions/dpms.h]) [X_LIBS="$X_LIBS -lXext"], , [$X_LIBS -lX11 $X_EXTRA_LIBS]) -CPPFLAGS="$save_CPPFLAGS" +if test x"$acx_host_winapi" = xXWINDOWS; then + AC_PATH_X + AC_PATH_XTRA + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$X_CFLAGS $CPPFLAGS" + AC_CHECK_LIB(Xtst, + XTestQueryExtension, + AC_CHECK_HEADERS([X11/extensions/XTest.h], + [X_LIBS="$X_LIBS -lXtst"], + AC_MSG_ERROR(Your must have the XTest headers to compile synergy)), + AC_MSG_ERROR(You must have the XTest library to build synergy), + [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) + AC_CHECK_LIB(Xinerama, + XineramaQueryExtension, + AC_CHECK_HEADERS([X11/extensions/Xinerama.h], + [X_LIBS="$X_LIBS -lXinerama"]), + , + [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) + AC_CHECK_LIB(Xext, + DPMSQueryExtension, + AC_CHECK_HEADERS([X11/extensions/dpms.h]), + , + [$X_LIBS -lX11 $X_EXTRA_LIBS]) + CPPFLAGS="$save_CPPFLAGS" + ARCH_LIBS="$X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS $ARCH_LIBS" + ARCH_CFLAGS="$ARCH_CFLAGS $X_CFLAGS" +fi dnl checks for types AC_TYPE_SIZE_T @@ -100,9 +153,9 @@ dnl enable maximum compiler warnings and warnings are errors. ACX_CXX_WARNINGS ACX_CXX_WARNINGS_ARE_ERRORS -dnl adjust variables for X11 and pthreads -CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS" -LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $PTHREAD_LIBS $LIBS" +dnl adjust compiler and linker variables +CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $ARCH_CFLAGS" +LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $ARCH_LIBS $LIBS" AC_OUTPUT([ Makefile diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index 88f2eda4..943cbbb6 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -27,7 +27,7 @@ #undef ARCH_TIME // include appropriate architecture implementation -#if WINDOWS_LIKE +#if SYSAPI_WIN32 # include "CArchConsoleWindows.h" # include "CArchDaemonWindows.h" # include "CArchFileWindows.h" @@ -39,7 +39,7 @@ # include "CArchStringWindows.h" # include "CArchTaskBarWindows.h" # include "CArchTimeWindows.h" -#elif UNIX_LIKE +#elif SYSAPI_UNIX # include "CArchConsoleUnix.h" # include "CArchDaemonUnix.h" # include "CArchFileUnix.h" @@ -118,7 +118,7 @@ CArch::CArch(ARCH_ARGS* args) m_daemon = new ARCH_DAEMON; m_taskbar = new ARCH_TASKBAR(args); -#if WINDOWS_LIKE +#if SYSAPI_WIN32 CArchMiscWindows::init(); #endif } diff --git a/lib/arch/CArchDaemonUnix.cpp b/lib/arch/CArchDaemonUnix.cpp index 748780db..93d50d4d 100644 --- a/lib/arch/CArchDaemonUnix.cpp +++ b/lib/arch/CArchDaemonUnix.cpp @@ -20,9 +20,6 @@ #include #include -// we derive from CArchDaemonNone -#include "CArchDaemonNone.cpp" - // // CArchDaemonUnix // diff --git a/lib/arch/CArchFileUnix.cpp b/lib/arch/CArchFileUnix.cpp index dc4cc3b8..4048061f 100644 --- a/lib/arch/CArchFileUnix.cpp +++ b/lib/arch/CArchFileUnix.cpp @@ -13,6 +13,7 @@ */ #include "CArchFileUnix.h" +#include #include #include #include diff --git a/lib/arch/CArchImpl.cpp b/lib/arch/CArchImpl.cpp deleted file mode 100644 index 9e57d0a7..00000000 --- a/lib/arch/CArchImpl.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "common.h" - -// include appropriate architecture implementation -#if WINDOWS_LIKE -# include "CArchMiscWindows.cpp" -# include "CArchConsoleWindows.cpp" -# include "CArchDaemonWindows.cpp" -# include "CArchFileWindows.cpp" -# include "CArchLogWindows.cpp" -# include "CArchMultithreadWindows.cpp" -# include "CArchNetworkWinsock.cpp" -# include "CArchSleepWindows.cpp" -# include "CArchStringWindows.cpp" -# include "CArchTaskBarWindows.cpp" -# include "CArchTimeWindows.cpp" -# include "XArchWindows.cpp" -#elif UNIX_LIKE -# include "CArchConsoleUnix.cpp" -# include "CArchDaemonUnix.cpp" -# include "CArchFileUnix.cpp" -# include "CArchLogUnix.cpp" -# if HAVE_PTHREAD -# include "CArchMultithreadPosix.cpp" -# endif -# include "CArchNetworkBSD.cpp" -# include "CArchSleepUnix.cpp" -# include "CArchStringUnix.cpp" -# include "CArchTaskBarXWindows.cpp" -# include "CArchTimeUnix.cpp" -# include "XArchUnix.cpp" -#endif diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index fa7f7ebe..d8845801 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -14,6 +14,7 @@ #include "CArchNetworkBSD.h" #include "CArch.h" +#include "CArchMultithreadPosix.h" #include "XArchUnix.h" #if HAVE_SYS_TYPES_H # include diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp index 43b8b41f..9f318570 100644 --- a/lib/arch/CMultibyte.cpp +++ b/lib/arch/CMultibyte.cpp @@ -17,7 +17,7 @@ #include "common.h" -#if (HAVE_MBSINIT && HAVE_MBRTOWC && HAVE_WCRTOMB) || WINDOWS_LIKE +#if (HAVE_MBSINIT && HAVE_MBRTOWC && HAVE_WCRTOMB) || SYSAPI_WIN32 #include "CMultibyteOS.cpp" #else #include "CMultibyteEmu.cpp" diff --git a/lib/arch/CMultibyteOS.cpp b/lib/arch/CMultibyteOS.cpp index 49a85229..57f4fd22 100644 --- a/lib/arch/CMultibyteOS.cpp +++ b/lib/arch/CMultibyteOS.cpp @@ -14,6 +14,7 @@ #include #include +#include class CArchMBStateImpl { public: diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am index 47888ade..d1a43f04 100644 --- a/lib/arch/Makefile.am +++ b/lib/arch/Makefile.am @@ -13,8 +13,50 @@ ## Process this file with automake to produce Makefile.in NULL = -EXTRA_DIST = \ - arch.dsp \ +COMMON_SOURCE_FILES = \ + CArch.cpp \ + CArchDaemonNone.cpp \ + CArchDaemonNone.h \ + XArch.cpp \ + CArch.h \ + IArchConsole.h \ + IArchDaemon.h \ + IArchFile.h \ + IArchLog.h \ + IArchMultithread.h \ + IArchNetwork.h \ + IArchSleep.h \ + IArchString.h \ + IArchTaskBar.h \ + IArchTaskBarReceiver.h \ + IArchTime.h \ + XArch.h \ + $(NULL) +UNIX_SOURCE_FILES = \ + CArchConsoleUnix.cpp \ + CArchDaemonUnix.cpp \ + CArchFileUnix.cpp \ + CArchLogUnix.cpp \ + CArchMultithreadPosix.cpp \ + CArchNetworkBSD.cpp \ + CArchSleepUnix.cpp \ + CArchStringUnix.cpp \ + CArchTaskBarXWindows.cpp \ + CArchTimeUnix.cpp \ + XArchUnix.cpp \ + CArchConsoleUnix.h \ + CArchDaemonUnix.h \ + CArchFileUnix.h \ + CArchLogUnix.h \ + CArchMultithreadPosix.h \ + CArchNetworkBSD.h \ + CArchSleepUnix.h \ + CArchStringUnix.h \ + CArchTaskBarXWindows.h \ + CArchTimeUnix.h \ + XArchUnix.h \ + $(NULL) +WIN32_SOURCE_FILES = \ CArchConsoleWindows.cpp \ CArchDaemonWindows.cpp \ CArchFileWindows.cpp \ @@ -41,59 +83,33 @@ EXTRA_DIST = \ XArchWindows.h \ $(NULL) +EXTRA_DIST = \ + arch.dsp \ + CMultibyte.cpp \ + CMultibyteEmu.cpp \ + CMultibyteOS.cpp \ + vsnprintf.cpp \ + $(UNIX_SOURCE_FILES) \ + $(WIN32_SOURCE_FILES) \ + $(NULL) + MAINTAINERCLEANFILES = \ Makefile.in \ $(NULL) noinst_LIBRARIES = libarch.a +if UNIX libarch_a_SOURCES = \ - CArch.cpp \ - CArchImpl.cpp \ - XArch.cpp \ - CArch.h \ - IArchConsole.h \ - IArchDaemon.h \ - IArchFile.h \ - IArchLog.h \ - IArchMultithread.h \ - IArchNetwork.h \ - IArchSleep.h \ - IArchString.h \ - IArchTaskBar.h \ - IArchTaskBarReceiver.h \ - IArchTime.h \ - XArch.h \ + $(COMMON_SOURCE_FILES) \ + $(UNIX_SOURCE_FILES) \ $(NULL) -EXTRA_libarch_a_SOURCES = \ - CArchConsoleUnix.cpp \ - CArchDaemonNone.cpp \ - CArchDaemonUnix.cpp \ - CArchFileUnix.cpp \ - CArchLogUnix.cpp \ - CArchMultithreadPosix.cpp \ - CArchNetworkBSD.cpp \ - CArchSleepUnix.cpp \ - CArchStringUnix.cpp \ - CArchTaskBarXWindows.cpp \ - CArchTimeUnix.cpp \ - CMultibyte.cpp \ - CMultibyteOS.cpp \ - CMultibyteEmu.cpp \ - XArchUnix.cpp \ - vsnprintf.cpp \ - CArchConsoleUnix.h \ - CArchDaemonNone.h \ - CArchDaemonUnix.h \ - CArchFileUnix.h \ - CArchLogUnix.h \ - CArchMultithreadPosix.h \ - CArchNetworkBSD.h \ - CArchSleepUnix.h \ - CArchStringUnix.h \ - CArchTaskBarXWindows.h \ - CArchTimeUnix.h \ - XArchUnix.h \ +endif +if WIN32 +libarch_a_SOURCES = \ + $(COMMON_SOURCE_FILES) \ + $(WIN32_SOURCE_FILES) \ $(NULL) +endif INCLUDES = \ -I$(top_srcdir)/lib/common \ $(NULL) diff --git a/lib/arch/vsnprintf.cpp b/lib/arch/vsnprintf.cpp index 62ccd771..10800ec7 100644 --- a/lib/arch/vsnprintf.cpp +++ b/lib/arch/vsnprintf.cpp @@ -28,7 +28,7 @@ ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) return n; } -#elif UNIX_LIKE // !HAVE_VSNPRINTF +#elif SYSAPI_UNIX // !HAVE_VSNPRINTF #include @@ -54,7 +54,7 @@ ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap) } } -#else // !HAVE_VSNPRINTF && !UNIX_LIKE +#else // !HAVE_VSNPRINTF && !SYSAPI_UNIX #error vsnprintf not implemented diff --git a/lib/base/CPriorityQueue.h b/lib/base/CPriorityQueue.h index 8a7fd7e7..29129e31 100644 --- a/lib/base/CPriorityQueue.h +++ b/lib/base/CPriorityQueue.h @@ -26,7 +26,7 @@ it sorts by std::greater, it has a forward iterator through the elements (which can appear in any order), and its contents can be swapped. */ template , -#if WINDOWS_LIKE +#if defined(_MSC_VER) class Compare = std::greater > #else class Compare = std::greater > diff --git a/lib/common/BasicTypes.h b/lib/common/BasicTypes.h index c7176637..b9d8293e 100644 --- a/lib/common/BasicTypes.h +++ b/lib/common/BasicTypes.h @@ -36,7 +36,8 @@ #endif #if !defined(TYPE_OF_SIZE_4) -# if SIZEOF_INT == 4 + // Carbon defines SInt32 and UInt32 in terms of long +# if SIZEOF_INT == 4 && !defined(__APPLE__) # define TYPE_OF_SIZE_4 int # else # define TYPE_OF_SIZE_4 long @@ -83,5 +84,3 @@ typedef unsigned TYPE_OF_SIZE_4 UInt32; #undef TYPE_OF_SIZE_4 #endif - - diff --git a/lib/common/common.h b/lib/common/common.h index 74b8f1d9..d7963e94 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -19,48 +19,51 @@ #if HAVE_CONFIG_H # include "config.h" +#else + // we may not have run configure on win32 +# if defined(_WIN32) +# define SYSAPI_WIN32 1 +# define WINAPI_MSWINDOWS 1 +# endif #endif -// check if win32 platform -#if defined(_WIN32) -# define WINDOWS_LIKE 1 +// VC++ specific +#if (_MSC_VER >= 1200) + // work around for statement scoping bug +# define for if (false) { } else for - // VC++ specific -# if (_MSC_VER >= 1200) - // work around for statement scoping bug -# define for if (false) { } else for + // turn off bonehead warnings +# pragma warning(disable: 4786) // identifier truncated in debug info +# pragma warning(disable: 4514) // unreferenced inline function removed - // turn off bonehead warnings -# pragma warning(disable: 4786) // identifier truncated in debug info -# pragma warning(disable: 4514) // unreferenced inline function removed + // this one's a little too aggressive +# pragma warning(disable: 4127) // conditional expression is constant - // this one's a little too aggressive -# pragma warning(disable: 4127) // conditional expression is constant - - // emitted incorrectly under release build in some circumstances -# if defined(NDEBUG) -# pragma warning(disable: 4702) // unreachable code -# pragma warning(disable: 4701) // variable maybe used uninitialized -# endif - -# endif // (_MSC_VER >= 1200) - - // VC++ has built-in sized types -# if defined(_MSC_VER) -# define TYPE_OF_SIZE_1 __int8 -# define TYPE_OF_SIZE_2 __int16 -# define TYPE_OF_SIZE_4 __int32 -# else -# define SIZE_OF_CHAR 1 -# define SIZE_OF_SHORT 2 -# define SIZE_OF_INT 4 -# define SIZE_OF_LONG 4 + // emitted incorrectly under release build in some circumstances +# if defined(NDEBUG) +# pragma warning(disable: 4702) // unreachable code +# pragma warning(disable: 4701) // variable maybe used uninitialized # endif -#endif // defined(_WIN32) +#endif // (_MSC_VER >= 1200) -// unix-like if not like anything else -#if (!defined(WINDOWS_LIKE) || WINDOWS_LIKE == 0) -# define UNIX_LIKE 1 +// VC++ has built-in sized types +#if defined(_MSC_VER) +# define TYPE_OF_SIZE_1 __int8 +# define TYPE_OF_SIZE_2 __int16 +# define TYPE_OF_SIZE_4 __int32 +#else +# define SIZE_OF_CHAR 1 +# define SIZE_OF_SHORT 2 +# define SIZE_OF_INT 4 +# define SIZE_OF_LONG 4 +#endif + +// FIXME -- including fp.h from Carbon.h causes a undefined symbol error +// on my build system. the symbol is scalb. since we don't need any +// math functions we define __FP__, the include guard macro for fp.h, to +// prevent fp.h from being included. +#if defined(__APPLE__) +#define __FP__ #endif // define NULL diff --git a/lib/platform/COSXClipboard.cpp b/lib/platform/COSXClipboard.cpp new file mode 100644 index 00000000..8c0071f6 --- /dev/null +++ b/lib/platform/COSXClipboard.cpp @@ -0,0 +1,70 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COSXClipboard.h" +#include + +// FIXME -- implement this + +// +// COSXClipboard +// + +COSXClipboard::COSXClipboard() +{ +} + +COSXClipboard::~COSXClipboard() +{ +} + +bool +COSXClipboard::empty() +{ + return true; +} + +void +COSXClipboard::add(EFormat, const CString&) +{ +} + +bool +COSXClipboard::open(Time) const +{ + return false; +} + +void +COSXClipboard::close() const +{ +} + +IClipboard::Time +COSXClipboard::getTime() const +{ + return 0; +} + +bool +COSXClipboard::has(EFormat) const +{ + return false; +} + +CString +COSXClipboard::get(EFormat) const +{ + return ""; +} diff --git a/lib/platform/COSXClipboard.h b/lib/platform/COSXClipboard.h new file mode 100644 index 00000000..8b6382d8 --- /dev/null +++ b/lib/platform/COSXClipboard.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXCLIPBOARD_H +#define COSXCLIPBOARD_H + +#include "IClipboard.h" + +//! OS X clipboard implementation +class COSXClipboard : public IClipboard { +public: + COSXClipboard(); + virtual ~COSXClipboard(); + + // IClipboard overrides + virtual bool empty(); + virtual void add(EFormat, const CString& data); + virtual bool open(Time) const; + virtual void close() const; + virtual Time getTime() const; + virtual bool has(EFormat) const; + virtual CString get(EFormat) const; +}; + +#endif diff --git a/lib/platform/COSXEventQueueBuffer.cpp b/lib/platform/COSXEventQueueBuffer.cpp new file mode 100644 index 00000000..b373f63a --- /dev/null +++ b/lib/platform/COSXEventQueueBuffer.cpp @@ -0,0 +1,76 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COSXEventQueueBuffer.h" + +// +// COSXEventQueueBuffer +// + +COSXEventQueueBuffer::COSXEventQueueBuffer() +{ + // FIXME +} + +COSXEventQueueBuffer::~COSXEventQueueBuffer() +{ + // FIXME +} + +void +COSXEventQueueBuffer::waitForEvent(double timeout) +{ + EventRef event; + ReceiveNextEvent(0, NULL, timeout, false, &event); +} + +IEventQueueBuffer::Type +COSXEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) +{ + // FIXME + (void)event; + (void)dataID; + return kNone; +} + +bool +COSXEventQueueBuffer::addEvent(UInt32 dataID) +{ + // FIXME + (void)dataID; + return false; +} + +bool +COSXEventQueueBuffer::isEmpty() const +{ + EventRef event; + OSStatus status = ReceiveNextEvent(0, NULL, 0.0, false, &event); + return (status != eventLoopTimedOutErr); +} + +CEventQueueTimer* +COSXEventQueueBuffer::newTimer(double duration, bool oneShot) const +{ + // FIXME + (void)duration; + (void)oneShot; + return NULL; +} + +void +COSXEventQueueBuffer::deleteTimer(CEventQueueTimer*) const +{ + // FIXME +} diff --git a/lib/platform/COSXEventQueueBuffer.h b/lib/platform/COSXEventQueueBuffer.h new file mode 100644 index 00000000..215b018c --- /dev/null +++ b/lib/platform/COSXEventQueueBuffer.h @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXEVENTQUEUEBUFFER_H +#define COSXEVENTQUEUEBUFFER_H + +#include "IEventQueueBuffer.h" +#include + +//! Event queue buffer for OS X +class COSXEventQueueBuffer : public IEventQueueBuffer { +public: + COSXEventQueueBuffer(); + virtual ~COSXEventQueueBuffer(); + + // IEventQueueBuffer overrides + virtual void waitForEvent(double timeout); + virtual Type getEvent(CEvent& event, UInt32& dataID); + virtual bool addEvent(UInt32 dataID); + virtual bool isEmpty() const; + virtual CEventQueueTimer* + newTimer(double duration, bool oneShot) const; + virtual void deleteTimer(CEventQueueTimer*) const; + +private: + // FIXME +}; + +#endif diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp new file mode 100644 index 00000000..ec454ace --- /dev/null +++ b/lib/platform/COSXKeyState.cpp @@ -0,0 +1,294 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COSXKeyState.h" +#include "CLog.h" + +struct CKeyEntry { +public: + KeyID m_keyID; + KeyButton m_button; +}; +static const CKeyEntry s_keys[] = { + /* ASCII */ + { ' ', 49 }, + { '!', 18 }, + { '\"', 39 }, + { '#', 20 }, + { '$', 21 }, + { '%', 23 }, + { '&', 26 }, + { '\'', 39 }, + { '(', 25 }, + { ')', 29 }, + { '*', 28 }, + { '+', 24 }, + { ',', 43 }, + { '-', 27 }, + { '.', 47 }, + { '/', 44 }, + { '0', 29 }, + { '1', 18 }, + { '2', 19 }, + { '3', 20 }, + { '4', 21 }, + { '5', 23 }, + { '6', 22 }, + { '7', 26 }, + { '8', 28 }, + { '9', 25 }, + { ':', 41 }, + { ';', 41 }, + { '<', 43 }, + { '=', 24 }, + { '>', 47 }, + { '?', 44 }, + { '@', 19 }, + { 'A', 0 }, + { 'B', 11 }, + { 'C', 8 }, + { 'D', 2 }, + { 'E', 14 }, + { 'F', 3 }, + { 'G', 5 }, + { 'H', 4 }, + { 'I', 34 }, + { 'J', 38 }, + { 'K', 40 }, + { 'L', 37 }, + { 'M', 46 }, + { 'N', 45 }, + { 'O', 31 }, + { 'P', 35 }, + { 'Q', 12 }, + { 'R', 15 }, + { 'S', 1 }, + { 'T', 17 }, + { 'U', 32 }, + { 'V', 9 }, + { 'W', 13 }, + { 'X', 7 }, + { 'Y', 16 }, + { 'Z', 6 }, + { '[', 33 }, + { '\\', 42 }, + { ']', 30 }, + { '^', 22 }, + { '_', 27 }, + { '`', 50 }, + { 'a', 0 }, + { 'b', 11 }, + { 'c', 8 }, + { 'd', 2 }, + { 'e', 14 }, + { 'f', 3 }, + { 'g', 5 }, + { 'h', 4 }, + { 'i', 34 }, + { 'j', 38 }, + { 'k', 40 }, + { 'l', 37 }, + { 'm', 46 }, + { 'n', 45 }, + { 'o', 31 }, + { 'p', 35 }, + { 'q', 12 }, + { 'r', 15 }, + { 's', 1 }, + { 't', 17 }, + { 'u', 32 }, + { 'v', 9 }, + { 'w', 13 }, + { 'x', 7 }, + { 'y', 16 }, + { 'z', 6 }, + { '{', 33 }, + { '|', 42 }, + { '}', 30 }, + { '~', 50 }, + + /* TTY functions */ + { kKeyBackSpace, 51 }, + { kKeyTab, 48 }, + { kKeyLinefeed, 36 }, +// { kKeyClear, 0xFFFF }, + { kKeyReturn, 36 }, + { kKeyPause, 113 }, + { kKeyScrollLock, 107 }, +// { kKeySysReq, 0xFFFF }, /* no mapping on apple */ + { kKeyEscape, 53 }, + { kKeyDelete, 117 }, + + /* cursor control */ + { kKeyHome, 115 }, + { kKeyLeft, 123 }, + { kKeyUp, 126 }, + { kKeyRight, 124 }, + { kKeyDown, 125 }, + { kKeyPageUp, 116 }, + { kKeyPageDown, 121 }, + { kKeyEnd, 119 }, + { kKeyBegin, 115 }, + + /* numeric keypad */ + { kKeyKP_Space, 49 }, + { kKeyKP_0, 82 }, + { kKeyKP_1, 83 }, + { kKeyKP_2, 84 }, + { kKeyKP_3, 85 }, + { kKeyKP_4, 86 }, + { kKeyKP_5, 87 }, + { kKeyKP_6, 88 }, + { kKeyKP_7, 89 }, + { kKeyKP_8, 91 }, + { kKeyKP_9, 92 }, + { kKeyKP_Enter, 76 }, + { kKeyKP_Decimal, 65 }, + { kKeyKP_Add, 69 }, + { kKeyKP_Subtract, 78 }, + { kKeyKP_Multiply, 67 }, + { kKeyKP_Divide, 75 }, + + /* Function keys */ + { kKeyF1, 122 }, + { kKeyF2, 120 }, + { kKeyF3, 99 }, + { kKeyF4, 118 }, + { kKeyF5, 96 }, + { kKeyF6, 97 }, + { kKeyF7, 98 }, + { kKeyF8, 100 }, + { kKeyF9, 101 }, + { kKeyF10, 109 }, + { kKeyF11, 103 }, + { kKeyF12, 111 }, + + /* Modifier keys */ + { kKeyShift_L, 56 }, + { kKeyShift_R, 56 }, + { kKeyControl_L, 59 }, + { kKeyControl_R, 59 }, + { kKeyAlt_L, 55 }, + { kKeyAlt_R, 55 }, + { kKeyCapsLock, 57 }, + { kKeyNumLock, 71 }, + { kKeyMeta_L, 58 }, + { kKeyMeta_R, 58 }, + { kKeySuper_L, 58 }, + { kKeySuper_R, 58 }, + { kKeyLeftTab, 48 } +}; + +// +// COSXKeyState +// + +COSXKeyState::COSXKeyState() +{ + // FIXME +} + +COSXKeyState::~COSXKeyState() +{ + // FIXME +} + +bool +COSXKeyState::fakeCtrlAltDel() +{ + // pass keys through unchanged + return false; +} + +const char* +COSXKeyState::getKeyName(KeyButton) const +{ + // FIXME + return ""; +} + +void +COSXKeyState::doUpdateKeys() +{ + // FIXME -- get the current keyboard state. call setKeyDown(), + // setToggled(), and addModifier() as appropriate. + + // save key mapping + // FIXME -- this probably needs to be more dynamic to support + // non-english keyboards. also need to map modifiers needed + // for each KeyID. + for (UInt32 i = 0; i < sizeof(s_keys) / sizeof(s_keys[0]); ++i) { + m_keyMap.insert(std::make_pair(s_keys[i].m_keyID, s_keys[i].m_button)); + } +} + +void +COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool) +{ + // let system figure out character for us + CGPostKeyboardEvent(0, static_cast(button), press); +} + +KeyButton +COSXKeyState::mapKey(Keystrokes& keys, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + // look up virtual key + CKeyMap::const_iterator keyIndex = m_keyMap.find(id); + if (keyIndex == m_keyMap.end()) { + return 0; + } + CGKeyCode keyCode = keyIndex->second; + + // adjust the modifiers to match the desired modifiers + Keystrokes undo; + if (!adjustModifiers(keys, undo, desiredMask)) { + LOG((CLOG_DEBUG2 "failed to adjust modifiers")); + return 0; + } + + // add the key event + Keystroke keystroke; + keystroke.m_key = keyCode; + if (!isAutoRepeat) { + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + } + else { + keystroke.m_press = false; + keystroke.m_repeat = true; + keys.push_back(keystroke); + keystroke.m_press = true; + keys.push_back(keystroke); + } + + // put undo keystrokes at end of keystrokes in reverse order + while (!undo.empty()) { + keys.push_back(undo.back()); + undo.pop_back(); + } + + return keyCode; +} + +bool +COSXKeyState::adjustModifiers(Keystrokes& /*keys*/, + Keystrokes& /*undo*/, + KeyModifierMask /*desiredMask*/) const +{ + // FIXME -- should add necessary modifier events to keys and undo + return true; +} diff --git a/lib/platform/COSXKeyState.h b/lib/platform/COSXKeyState.h new file mode 100644 index 00000000..ef563e14 --- /dev/null +++ b/lib/platform/COSXKeyState.h @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXKEYSTATE_H +#define COSXKEYSTATE_H + +#include "CKeyState.h" +#include "stdmap.h" +#include + +//! OS X key state +/*! +A key state for OS X. +*/ +class COSXKeyState : public CKeyState { +public: + COSXKeyState(); + virtual ~COSXKeyState(); + + // IKeyState overrides + virtual bool fakeCtrlAltDel(); + virtual const char* getKeyName(KeyButton) const; + +protected: + // IKeyState overrides + virtual void doUpdateKeys(); + virtual void doFakeKeyEvent(KeyButton button, + bool press, bool isAutoRepeat); + virtual KeyButton mapKey(Keystrokes& keys, KeyID id, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + +private: + bool adjustModifiers(Keystrokes& keys, + Keystrokes& undo, + KeyModifierMask desiredMask) const; + +private: + typedef std::map CKeyMap; + + CKeyMap m_keyMap; +}; + +#endif diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp new file mode 100644 index 00000000..83175ec8 --- /dev/null +++ b/lib/platform/COSXScreen.cpp @@ -0,0 +1,407 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COSXScreen.h" +#include "COSXClipboard.h" +#include "COSXEventQueueBuffer.h" +#include "COSXKeyState.h" +#include "COSXScreenSaver.h" +#include "CClipboard.h" +#include "CLog.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" + +// +// COSXScreen +// + +COSXScreen::COSXScreen(bool isPrimary) : + m_isPrimary(isPrimary), + m_isOnScreen(m_isPrimary), + m_cursorHidden(false), + m_keyState(NULL), + m_sequenceNumber(0), + m_screensaver(NULL), + m_screensaverNotify(false) +{ + try { + m_displayID = CGMainDisplayID(); + updateScreenShape(); + m_screensaver = new COSXScreenSaver(); + m_keyState = new COSXKeyState(); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + } + catch (...) { + delete m_keyState; + delete m_screensaver; + throw; + } + + // install event handlers + EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(), + new TMethodEventJob(this, + &COSXScreen::handleSystemEvent)); + + // install the platform event queue + EVENTQUEUE->adoptBuffer(new COSXEventQueueBuffer); +} + +COSXScreen::~COSXScreen() +{ + disable(); + EVENTQUEUE->adoptBuffer(NULL); + EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget()); + delete m_keyState; + delete m_screensaver; +} + +void* +COSXScreen::getEventTarget() const +{ + return const_cast(this); +} + +bool +COSXScreen::getClipboard(ClipboardID, IClipboard* dst) const +{ + COSXClipboard src; + CClipboard::copy(dst, &src); + return true; +} + +void +COSXScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +COSXScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + Point mouse; + GetGlobalMouse(&mouse); + x = mouse.h; + y = mouse.v; +} + +void +COSXScreen::reconfigure(UInt32 activeSides) +{ + // FIXME + (void)activeSides; +} + +void +COSXScreen::warpCursor(SInt32 x, SInt32 y) +{ + // move cursor without generating events + CGPoint pos; + pos.x = x; + pos.y = y; + CGWarpMouseCursorPosition(pos); + + // save new cursor position + m_xCursor = x; + m_yCursor = y; +} + +SInt32 +COSXScreen::getJumpZoneSize() const +{ + // FIXME -- is this correct? + return 1; +} + +bool +COSXScreen::isAnyMouseButtonDown() const +{ + // FIXME + return false; +} + +void +COSXScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + x = m_xCenter; + y = m_yCenter; +} + +void +COSXScreen::fakeMouseButton(ButtonID id, bool press) const +{ + // get button index + UInt32 index = id - kButtonLeft; + if (index >= sizeof(m_buttons) / sizeof(m_buttons[0])) { + return; + } + + // update state + m_buttons[index] = press; + + // synthesize event. CGPostMouseEvent is a particularly good + // example of a bad API. we have to shadow the mouse state to + // use this API and if we want to support more buttons we have + // to recompile. + CGPoint pos; + pos.x = m_xCursor; + pos.y = m_yCursor; + CGPostMouseEvent(pos, false, sizeof(m_buttons) / sizeof(m_buttons[0]), + m_buttons[0], + m_buttons[1], + m_buttons[2], + m_buttons[3], + m_buttons[4]); +} + +void +COSXScreen::fakeMouseMove(SInt32 x, SInt32 y) const +{ + // synthesize event + CGPoint pos; + pos.x = x; + pos.y = y; + // FIXME -- is it okay to pass no buttons here? + CGPostMouseEvent(pos, true, 0, m_buttons[0]); + + // save new cursor position + m_xCursor = x; + m_yCursor = y; +} + +void +COSXScreen::fakeMouseWheel(SInt32 delta) const +{ + CGPostScrollWheelEvent(1, delta / 120); +} + +void +COSXScreen::enable() +{ + // FIXME -- install clipboard snooper (if we need one) + + if (m_isPrimary) { + // FIXME -- start watching jump zones + } + else { + // FIXME -- prevent system from entering power save mode + + // hide cursor + if (!m_cursorHidden) { + CGDisplayHideCursor(m_displayID); + m_cursorHidden = true; + } + + // warp the mouse to the cursor center + fakeMouseMove(m_xCenter, m_yCenter); + + // FIXME -- prepare to show cursor if it moves + } + + updateKeys(); +} + +void +COSXScreen::disable() +{ + if (m_isPrimary) { + // FIXME -- stop watching jump zones, stop capturing input + } + else { + // show cursor + if (m_cursorHidden) { + CGDisplayShowCursor(m_displayID); + m_cursorHidden = false; + } + + // FIXME -- allow system to enter power saving mode + } + + // FIXME -- uninstall clipboard snooper (if we needed one) + + m_isOnScreen = m_isPrimary; +} + +void +COSXScreen::enter() +{ + if (m_isPrimary) { + // FIXME -- stop capturing input, watch jump zones + } + else { + // show cursor + if (m_cursorHidden) { + CGDisplayShowCursor(m_displayID); + m_cursorHidden = false; + } + + // reset buttons + for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { + m_buttons[i] = false; + } + } + + // now on screen + m_isOnScreen = true; +} + +bool +COSXScreen::leave() +{ + // FIXME -- choose keyboard layout if per-process and activate it here + + if (m_isPrimary) { + // update key and button state + updateKeys(); + + // warp to center + warpCursor(m_xCenter, m_yCenter); + + // capture events + // FIXME + } + else { + // hide cursor + if (!m_cursorHidden) { + CGDisplayHideCursor(m_displayID); + m_cursorHidden = true; + } + + // warp the mouse to the cursor center + fakeMouseMove(m_xCenter, m_yCenter); + + // FIXME -- prepare to show cursor if it moves + + // take keyboard focus + // FIXME + } + + // now off screen + m_isOnScreen = false; + + return true; +} + +bool +COSXScreen::setClipboard(ClipboardID, const IClipboard* src) +{ + COSXClipboard dst; + if (src != NULL) { + // save clipboard data + return CClipboard::copy(&dst, src); + } + else { + // assert clipboard ownership + if (!dst.open(0)) { + return false; + } + dst.empty(); + dst.close(); + return true; + } +} + +void +COSXScreen::checkClipboards() +{ + // FIXME -- do nothing if we're always up to date +} + +void +COSXScreen::openScreensaver(bool notify) +{ + m_screensaverNotify = notify; + if (!m_screensaverNotify) { + m_screensaver->disable(); + } +} + +void +COSXScreen::closeScreensaver() +{ + if (!m_screensaverNotify) { + m_screensaver->enable(); + } +} + +void +COSXScreen::screensaver(bool activate) +{ + if (activate) { + m_screensaver->activate(); + } + else { + m_screensaver->deactivate(); + } +} + +void +COSXScreen::resetOptions() +{ + // no options +} + +void +COSXScreen::setOptions(const COptionsList&) +{ + // no options +} + +void +COSXScreen::setSequenceNumber(UInt32 seqNum) +{ + m_sequenceNumber = seqNum; +} + +bool +COSXScreen::isPrimary() const +{ + return m_isPrimary; +} + +void +COSXScreen::handleSystemEvent(const CEvent&, void*) +{ + // FIXME +} + +void +COSXScreen::updateButtons() +{ + // FIXME -- get current button state into m_buttons[] +} + +IKeyState* +COSXScreen::getKeyState() const +{ + return m_keyState; +} + +void +COSXScreen::updateScreenShape() +{ + // FIXME -- handle multiple monitors + + // get shape of default screen + m_x = 0; + m_y = 0; + m_w = CGDisplayPixelsWide(m_displayID); + m_h = CGDisplayPixelsHigh(m_displayID); + + // get center of default screen + m_xCenter = m_x + (m_w >> 1); + m_yCenter = m_y + (m_h >> 1); +} diff --git a/lib/platform/COSXScreen.h b/lib/platform/COSXScreen.h new file mode 100644 index 00000000..0e13c5ba --- /dev/null +++ b/lib/platform/COSXScreen.h @@ -0,0 +1,110 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXSCREEN_H +#define COSXSCREEN_H + +#include "CPlatformScreen.h" +#include "stdvector.h" +#include + +class COSXKeyState; +class COSXScreenSaver; + +//! Implementation of IPlatformScreen for OS X +class COSXScreen : public CPlatformScreen { +public: + COSXScreen(bool isPrimary); + virtual ~COSXScreen(); + + //! @name manipulators + //@{ + + //@} + + // IScreen overrides + virtual void* getEventTarget() const; + virtual bool getClipboard(ClipboardID id, IClipboard*) const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + + // IPrimaryScreen overrides + virtual void reconfigure(UInt32 activeSides); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual SInt32 getJumpZoneSize() const; + virtual bool isAnyMouseButtonDown() const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; + + // ISecondaryScreen overrides + virtual void fakeMouseButton(ButtonID id, bool press) const; + virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseWheel(SInt32 delta) const; + + // IPlatformScreen overrides + virtual void enable(); + virtual void disable(); + virtual void enter(); + virtual bool leave(); + virtual bool setClipboard(ClipboardID, const IClipboard*); + virtual void checkClipboards(); + virtual void openScreensaver(bool notify); + virtual void closeScreensaver(); + virtual void screensaver(bool activate); + virtual void resetOptions(); + virtual void setOptions(const COptionsList& options); + virtual void setSequenceNumber(UInt32); + virtual bool isPrimary() const; + +protected: + // IPlatformScreen overrides + virtual void handleSystemEvent(const CEvent&, void*); + virtual void updateButtons(); + virtual IKeyState* getKeyState() const; + +private: + void updateScreenShape(); + +private: + // true if screen is being used as a primary screen, false otherwise + bool m_isPrimary; + + // true if mouse has entered the screen + bool m_isOnScreen; + + // the display + CGDirectDisplayID m_displayID; + + // screen shape stuff + SInt32 m_x, m_y; + SInt32 m_w, m_h; + SInt32 m_xCenter, m_yCenter; + + // mouse state + mutable SInt32 m_xCursor, m_yCursor; + mutable boolean_t m_buttons[5]; + bool m_cursorHidden; + + // keyboard stuff + COSXKeyState* m_keyState; + + // clipboards + UInt32 m_sequenceNumber; + + // screen saver stuff + COSXScreenSaver* m_screensaver; + bool m_screensaverNotify; +}; + +#endif diff --git a/lib/platform/COSXScreenSaver.cpp b/lib/platform/COSXScreenSaver.cpp new file mode 100644 index 00000000..f67dc016 --- /dev/null +++ b/lib/platform/COSXScreenSaver.cpp @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COSXScreenSaver.h" +#include + +// FIXME -- implement this + +// +// COSXScreenSaver +// + +COSXScreenSaver::COSXScreenSaver() +{ +} + +COSXScreenSaver::~COSXScreenSaver() +{ +} + +void +COSXScreenSaver::enable() +{ +} + +void +COSXScreenSaver::disable() +{ +} + +void +COSXScreenSaver::activate() +{ +} + +void +COSXScreenSaver::deactivate() +{ +} + +bool +COSXScreenSaver::isActive() const +{ + return false; +} diff --git a/lib/platform/COSXScreenSaver.h b/lib/platform/COSXScreenSaver.h new file mode 100644 index 00000000..a40caefa --- /dev/null +++ b/lib/platform/COSXScreenSaver.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXSCREENSAVER_H +#define COSXSCREENSAVER_H + +#include "IScreenSaver.h" + +//! OSX screen saver implementation +class COSXScreenSaver : public IScreenSaver { +public: + COSXScreenSaver(); + virtual ~COSXScreenSaver(); + + // IScreenSaver overrides + virtual void enable(); + virtual void disable(); + virtual void activate(); + virtual void deactivate(); + virtual bool isActive() const; +}; + +#endif diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h index 045d96e7..b0c8e5d3 100644 --- a/lib/platform/CSynergyHook.h +++ b/lib/platform/CSynergyHook.h @@ -16,13 +16,8 @@ #define CSYNERGYHOOK_H #include "BasicTypes.h" - -#if WINDOWS_LIKE #define WIN32_LEAN_AND_MEAN #include -#else -#error CSynergyHook is a win32 specific file -#endif #if defined(SYNRGYHK_EXPORTS) #define CSYNERGYHOOK_API __declspec(dllexport) diff --git a/lib/platform/CXWindowsEventQueueBuffer.cpp b/lib/platform/CXWindowsEventQueueBuffer.cpp index ea450e17..7f41dc5a 100644 --- a/lib/platform/CXWindowsEventQueueBuffer.cpp +++ b/lib/platform/CXWindowsEventQueueBuffer.cpp @@ -16,22 +16,20 @@ #include "CThread.h" #include "CEvent.h" #include "IEventQueue.h" -#if UNIX_LIKE -# if HAVE_POLL -# include -# else -# if HAVE_SYS_SELECT_H -# include -# endif -# if HAVE_SYS_TIME_H -# include -# endif -# if HAVE_SYS_TYPES_H -# include -# endif -# if HAVE_UNISTD_H -# include -# endif +#if HAVE_POLL +# include +#else +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TIME_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include # endif #endif diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index fe368d13..ffe5a64e 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -45,24 +45,6 @@ } # endif #endif -#if UNIX_LIKE -# if HAVE_POLL -# include -# else -# if HAVE_SYS_SELECT_H -# include -# endif -# if HAVE_SYS_TIME_H -# include -# endif -# if HAVE_SYS_TYPES_H -# include -# endif -# if HAVE_UNISTD_H -# include -# endif -# endif -#endif #include "CArch.h" // map "Internet" keys to KeyIDs diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 9ac9c323..74074267 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -13,10 +13,27 @@ ## Process this file with automake to produce Makefile.in NULL = -EXTRA_DIST = \ - makehook.dsp \ - platform.dsp \ - synrgyhk.dsp \ +XWINDOWS_SOURCE_FILES = \ + CXWindowsClipboard.cpp \ + CXWindowsClipboardTextConverter.cpp \ + CXWindowsClipboardUCS2Converter.cpp \ + CXWindowsClipboardUTF8Converter.cpp \ + CXWindowsEventQueueBuffer.cpp \ + CXWindowsKeyState.cpp \ + CXWindowsScreen.cpp \ + CXWindowsScreenSaver.cpp \ + CXWindowsUtil.cpp \ + CXWindowsClipboard.h \ + CXWindowsClipboardTextConverter.h \ + CXWindowsClipboardUCS2Converter.h \ + CXWindowsClipboardUTF8Converter.h \ + CXWindowsEventQueueBuffer.h \ + CXWindowsKeyState.h \ + CXWindowsScreen.h \ + CXWindowsScreenSaver.h \ + CXWindowsUtil.h \ + $(NULL) +MSWINDOWS_SOURCE_FILES = \ CMSWindowsClipboard.cpp \ CMSWindowsClipboardAnyTextConverter.cpp \ CMSWindowsClipboardTextConverter.cpp \ @@ -40,32 +57,42 @@ EXTRA_DIST = \ CMSWindowsUtil.h \ CSynergyHook.h \ $(NULL) +CARBON_SOURCE_FILES = \ + COSXClipboard.cpp \ + COSXEventQueueBuffer.cpp \ + COSXKeyState.cpp \ + COSXScreen.cpp \ + COSXScreenSaver.cpp \ + COSXClipboard.h \ + COSXEventQueueBuffer.h \ + COSXKeyState.h \ + COSXScreen.h \ + COSXScreenSaver.h \ + $(NULL) + +EXTRA_DIST = \ + makehook.dsp \ + platform.dsp \ + synrgyhk.dsp \ + $(XWINDOWS_SOURCE_FILES) \ + $(MSWINDOWS_SOURCE_FILES) \ + $(CARBON_SOURCE_FILES) \ + $(NULL) MAINTAINERCLEANFILES = \ Makefile.in \ $(NULL) noinst_LIBRARIES = libplatform.a -libplatform_a_SOURCES = \ - CXWindowsClipboard.cpp \ - CXWindowsClipboardTextConverter.cpp \ - CXWindowsClipboardUCS2Converter.cpp \ - CXWindowsClipboardUTF8Converter.cpp \ - CXWindowsEventQueueBuffer.cpp \ - CXWindowsKeyState.cpp \ - CXWindowsScreen.cpp \ - CXWindowsScreenSaver.cpp \ - CXWindowsUtil.cpp \ - CXWindowsClipboard.h \ - CXWindowsClipboardTextConverter.h \ - CXWindowsClipboardUCS2Converter.h \ - CXWindowsClipboardUTF8Converter.h \ - CXWindowsEventQueueBuffer.h \ - CXWindowsKeyState.h \ - CXWindowsScreen.h \ - CXWindowsScreenSaver.h \ - CXWindowsUtil.h \ - $(NULL) +if XWINDOWS +libplatform_a_SOURCES = $(XWINDOWS_SOURCE_FILES) +endif +if MSWINDOWS +libplatform_a_SOURCES = $(MSWINDOWS_SOURCE_FILES) +endif +if CARBON +libplatform_a_SOURCES = $(CARBON_SOURCE_FILES) +endif INCLUDES = \ -I$(top_srcdir)/lib/common \ -I$(top_srcdir)/lib/arch \ From bf2879724f23aa2ea7c497b10700f7766d19dec0 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Apr 2004 19:15:09 +0000 Subject: [PATCH 638/807] Preliminary support for MSYS/MinGW builds. Doesn't yet build CSynergyHook as a DLL and does not compile or link in the resources for the binaries. --- cmd/launcher/launcher.rc | 5 +- cmd/synergyc/synergyc.rc | 5 +- cmd/synergys/synergys.cpp | 4 +- cmd/synergys/synergys.rc | 5 +- configure.in | 2 +- lib/arch/CArchDaemonWindows.cpp | 4 +- lib/arch/CArchMultithreadWindows.cpp | 6 +- lib/arch/CArchNetworkWinsock.cpp | 3 +- lib/arch/CArchStringWindows.cpp | 1 + lib/arch/CArchTaskBarWindows.cpp | 5 +- lib/arch/CMultibyte.cpp | 2 +- lib/arch/XArchWindows.cpp | 1 + lib/arch/arch.dsp | 150 ++++--- lib/common/common.h | 1 + lib/platform/CMSWindowsDesks.cpp | 5 +- lib/platform/CMSWindowsKeyState.cpp | 515 +++++++++++++------------ lib/platform/CMSWindowsScreen.cpp | 17 +- lib/platform/CMSWindowsScreenSaver.cpp | 4 +- lib/platform/CSynergyHook.cpp | 17 +- lib/synergy/CProtocolUtil.cpp | 7 +- 20 files changed, 386 insertions(+), 373 deletions(-) diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 82d60df4..51df8c83 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -7,7 +7,10 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" +#include "winres.h" +#if !defined(IDC_STATIC) +#define IDC_STATIC (-1) +#endif ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index 596ce309..e168a3b3 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -7,7 +7,10 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" +#include "winres.h" +#if !defined(IDC_STATIC) +#define IDC_STATIC (-1) +#endif ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 49aca596..6e1b758b 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -692,8 +692,8 @@ parse(int argc, const char* const* argv) ARG->m_name = ARCH->getHostName(); // parse options - int i; - for (i = 1; i < argc; ++i) { + int i = 1; + for (; i < argc; ++i) { if (isArg(i, argc, argv, "-d", "--debug", 1)) { // change logging level ARG->m_logFilter = argv[++i]; diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index 41e40115..c98a3d83 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -7,7 +7,10 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" +#include "winres.h" +#if !defined(IDC_STATIC) +#define IDC_STATIC (-1) +#endif ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS diff --git a/configure.in b/configure.in index 242d6ca3..dea16432 100644 --- a/configure.in +++ b/configure.in @@ -32,7 +32,7 @@ ARCH_LIBS="" ARCH_CFLAGS="" AC_CANONICAL_HOST case $host in - *-*-windows*) + *-*-mingw32* | *-*-windows*) acx_host_arch="WIN32" acx_host_winapi="MSWINDOWS" ARCH_CFLAGS="$ARCH_CFLAGS -DSYSAPI_WIN32=1 -DWINAPI_MSWINDOWS=1" diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index d702cb99..7e1d76a3 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -277,7 +277,7 @@ CArchDaemonWindows::daemonize(const char* name, DaemonFunc func) FreeLibrary(kernel); throw XArchDaemonFailed(new XArchEvalWindows(err)); } - if (RegisterServiceProcess(NULL, 1) == 0) { + if (RegisterServiceProcess(0, 1) == 0) { // RegisterServiceProcess failed DWORD err = GetLastError(); FreeLibrary(kernel); @@ -590,7 +590,7 @@ CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) // register our service handler function m_statusHandle = RegisterServiceCtrlHandler(argv[0], &CArchDaemonWindows::serviceHandlerEntry); - if (m_statusHandle == NULL) { + if (m_statusHandle == 0) { // cannot start as service m_daemonResult = -1; ARCH->closeCondVar(m_serviceCondVar); diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index 91ec38d4..3adcd46a 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -12,7 +12,7 @@ * GNU General Public License for more details. */ -#if !defined(_MT) +#if defined(_MSC_VER) && !defined(_MT) # error multithreading compile option is required #endif @@ -415,12 +415,12 @@ CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n) assert(thread != NULL); size_t index; - if (n > 0 && s_pBase < n) { + if (n > 0 && s_pBase < (size_t)n) { // lowest priority index = 0; } else { - index = s_pBase - n; + index = (size_t)((int)s_pBase - n); if (index > s_pMax) { // highest priority index = s_pMax; diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index bbe8cdfe..b91ba190 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -15,6 +15,7 @@ #include "CArchNetworkWinsock.h" #include "CArch.h" +#include "CArchMultithreadWindows.h" #include "IArchMultithread.h" #include "XArchWindows.h" #include @@ -578,7 +579,7 @@ CArchNetworkWinsock::throwErrorOnSocket(CArchSocket s) void CArchNetworkWinsock::setBlockingOnSocket(SOCKET s, bool blocking) { - assert(s != NULL); + assert(s != 0); int flag = blocking ? 0 : 1; if (ioctl_winsock(s, FIONBIO, &flag) == SOCKET_ERROR) { diff --git a/lib/arch/CArchStringWindows.cpp b/lib/arch/CArchStringWindows.cpp index 4aefda2e..c23f019c 100644 --- a/lib/arch/CArchStringWindows.cpp +++ b/lib/arch/CArchStringWindows.cpp @@ -16,6 +16,7 @@ #include "CArchStringWindows.h" #include +#include #include "CMultibyte.cpp" diff --git a/lib/arch/CArchTaskBarWindows.cpp b/lib/arch/CArchTaskBarWindows.cpp index a31745d9..29c57b66 100644 --- a/lib/arch/CArchTaskBarWindows.cpp +++ b/lib/arch/CArchTaskBarWindows.cpp @@ -13,6 +13,7 @@ */ #include "CArchTaskBarWindows.h" +#include "CArchMiscWindows.h" #include "IArchTaskBarReceiver.h" #include "CArch.h" #include "XArch.h" @@ -464,7 +465,7 @@ CArchTaskBarWindows::threadMainLoop() // handle failure if (m_hwnd == NULL) { - UnregisterClass((LPCTSTR)windowClass, s_appInstance); + UnregisterClass(reinterpret_cast(windowClass), s_appInstance); return; } @@ -480,7 +481,7 @@ CArchTaskBarWindows::threadMainLoop() // clean up removeAllIcons(); DestroyWindow(m_hwnd); - UnregisterClass((LPCTSTR)windowClass, s_appInstance); + UnregisterClass(reinterpret_cast(windowClass), s_appInstance); } void* diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp index 9f318570..01ec9550 100644 --- a/lib/arch/CMultibyte.cpp +++ b/lib/arch/CMultibyte.cpp @@ -17,7 +17,7 @@ #include "common.h" -#if (HAVE_MBSINIT && HAVE_MBRTOWC && HAVE_WCRTOMB) || SYSAPI_WIN32 +#if (HAVE_MBSINIT && HAVE_MBRTOWC && HAVE_WCRTOMB) || defined(_MSC_VER) #include "CMultibyteOS.cpp" #else #include "CMultibyteEmu.cpp" diff --git a/lib/arch/XArchWindows.cpp b/lib/arch/XArchWindows.cpp index a57bc71c..eebd6449 100644 --- a/lib/arch/XArchWindows.cpp +++ b/lib/arch/XArchWindows.cpp @@ -13,6 +13,7 @@ */ #include "XArchWindows.h" +#include "CArchNetworkWinsock.h" // // XArchEvalWindows diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp index 72fcc909..e77650b4 100644 --- a/lib/arch/arch.dsp +++ b/lib/arch/arch.dsp @@ -91,12 +91,76 @@ SOURCE=.\CArch.cpp # End Source File # Begin Source File -SOURCE=.\CArchImpl.cpp +SOURCE=.\CArchConsoleWindows.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchDaemonWindows.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchFileWindows.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchLogWindows.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchMiscWindows.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchMultithreadWindows.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchNetworkWinsock.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchSleepWindows.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchStringWindows.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchTaskBarWindows.cpp +# End Source File +# Begin Source File + +SOURCE=.\CArchTimeWindows.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMultibyte.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyteEmu.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\CMultibyteOS.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\vsnprintf.cpp +# PROP Exclude_From_Build 1 # End Source File # Begin Source File SOURCE=.\XArch.cpp # End Source File +# Begin Source File + +SOURCE=.\XArchWindows.cpp +# End Source File # End Group # Begin Group "Header Files" @@ -202,89 +266,5 @@ SOURCE=.\XArch.h SOURCE=.\XArchWindows.h # End Source File # End Group -# Begin Group "Included Files" - -# PROP Default_Filter "inc" -# Begin Source File - -SOURCE=.\CArchConsoleWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CArchDaemonWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CArchFileWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CArchLogWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CArchMiscWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CArchMultithreadWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CArchNetworkWinsock.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CArchSleepWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CArchStringWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CArchTaskBarWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CArchTimeWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CMultibyte.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CMultibyteEmu.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CMultibyteOS.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\vsnprintf.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\XArchWindows.cpp -# PROP Exclude_From_Build 1 -# End Source File -# End Group # End Target # End Project diff --git a/lib/common/common.h b/lib/common/common.h index d7963e94..96910efc 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -48,6 +48,7 @@ // VC++ has built-in sized types #if defined(_MSC_VER) +# include # define TYPE_OF_SIZE_1 __int8 # define TYPE_OF_SIZE_2 __int16 # define TYPE_OF_SIZE_4 __int32 diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index 28fcca22..2b62ba70 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -387,7 +387,8 @@ void CMSWindowsDesks::destroyClass(ATOM windowClass) const { if (windowClass != 0) { - UnregisterClass((LPCTSTR)windowClass, CMSWindowsScreen::getInstance()); + UnregisterClass(reinterpret_cast(windowClass), + CMSWindowsScreen::getInstance()); } } @@ -397,7 +398,7 @@ CMSWindowsDesks::createWindow(ATOM windowClass, const char* name) const HWND window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - (LPCTSTR)windowClass, + reinterpret_cast(windowClass), name, WS_POPUP, 0, 0, 1, 1, diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index 427323f9..e25e9c61 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -315,262 +315,262 @@ const char* CMSWindowsKeyState::s_vkToName[] = // map virtual keys to synergy key enumeration const KeyID CMSWindowsKeyState::s_virtualKey[][2] = { - /* 0x00 */ kKeyNone, kKeyNone, // reserved - /* 0x01 */ kKeyNone, kKeyNone, // VK_LBUTTON - /* 0x02 */ kKeyNone, kKeyNone, // VK_RBUTTON - /* 0x03 */ kKeyNone, kKeyBreak, // VK_CANCEL - /* 0x04 */ kKeyNone, kKeyNone, // VK_MBUTTON - /* 0x05 */ kKeyNone, kKeyNone, // undefined - /* 0x06 */ kKeyNone, kKeyNone, // undefined - /* 0x07 */ kKeyNone, kKeyNone, // undefined - /* 0x08 */ kKeyBackSpace, kKeyNone, // VK_BACK - /* 0x09 */ kKeyTab, kKeyNone, // VK_TAB - /* 0x0a */ kKeyNone, kKeyNone, // undefined - /* 0x0b */ kKeyNone, kKeyNone, // undefined - /* 0x0c */ kKeyClear, kKeyClear, // VK_CLEAR - /* 0x0d */ kKeyReturn, kKeyKP_Enter, // VK_RETURN - /* 0x0e */ kKeyNone, kKeyNone, // undefined - /* 0x0f */ kKeyNone, kKeyNone, // undefined - /* 0x10 */ kKeyShift_L, kKeyShift_R, // VK_SHIFT - /* 0x11 */ kKeyControl_L, kKeyControl_R, // VK_CONTROL - /* 0x12 */ kKeyAlt_L, kKeyAlt_R, // VK_MENU - /* 0x13 */ kKeyPause, kKeyNone, // VK_PAUSE - /* 0x14 */ kKeyCapsLock, kKeyNone, // VK_CAPITAL - /* 0x15 */ kKeyNone, kKeyNone, // VK_KANA - /* 0x16 */ kKeyNone, kKeyNone, // VK_HANGUL - /* 0x17 */ kKeyNone, kKeyNone, // VK_JUNJA - /* 0x18 */ kKeyNone, kKeyNone, // VK_FINAL - /* 0x19 */ kKeyZenkaku, kKeyNone, // VK_KANJI - /* 0x1a */ kKeyNone, kKeyNone, // undefined - /* 0x1b */ kKeyEscape, kKeyNone, // VK_ESCAPE - /* 0x1c */ kKeyNone, kKeyNone, // VK_CONVERT - /* 0x1d */ kKeyNone, kKeyNone, // VK_NONCONVERT - /* 0x1e */ kKeyNone, kKeyNone, // VK_ACCEPT - /* 0x1f */ kKeyNone, kKeyNone, // VK_MODECHANGE - /* 0x20 */ kKeyNone, kKeyNone, // VK_SPACE - /* 0x21 */ kKeyKP_PageUp, kKeyPageUp, // VK_PRIOR - /* 0x22 */ kKeyKP_PageDown, kKeyPageDown, // VK_NEXT - /* 0x23 */ kKeyKP_End, kKeyEnd, // VK_END - /* 0x24 */ kKeyKP_Home, kKeyHome, // VK_HOME - /* 0x25 */ kKeyKP_Left, kKeyLeft, // VK_LEFT - /* 0x26 */ kKeyKP_Up, kKeyUp, // VK_UP - /* 0x27 */ kKeyKP_Right, kKeyRight, // VK_RIGHT - /* 0x28 */ kKeyKP_Down, kKeyDown, // VK_DOWN - /* 0x29 */ kKeySelect, kKeySelect, // VK_SELECT - /* 0x2a */ kKeyNone, kKeyNone, // VK_PRINT - /* 0x2b */ kKeyExecute, kKeyExecute, // VK_EXECUTE - /* 0x2c */ kKeyPrint, kKeyPrint, // VK_SNAPSHOT - /* 0x2d */ kKeyKP_Insert, kKeyInsert, // VK_INSERT - /* 0x2e */ kKeyKP_Delete, kKeyDelete, // VK_DELETE - /* 0x2f */ kKeyHelp, kKeyHelp, // VK_HELP - /* 0x30 */ kKeyNone, kKeyNone, // VK_0 - /* 0x31 */ kKeyNone, kKeyNone, // VK_1 - /* 0x32 */ kKeyNone, kKeyNone, // VK_2 - /* 0x33 */ kKeyNone, kKeyNone, // VK_3 - /* 0x34 */ kKeyNone, kKeyNone, // VK_4 - /* 0x35 */ kKeyNone, kKeyNone, // VK_5 - /* 0x36 */ kKeyNone, kKeyNone, // VK_6 - /* 0x37 */ kKeyNone, kKeyNone, // VK_7 - /* 0x38 */ kKeyNone, kKeyNone, // VK_8 - /* 0x39 */ kKeyNone, kKeyNone, // VK_9 - /* 0x3a */ kKeyNone, kKeyNone, // undefined - /* 0x3b */ kKeyNone, kKeyNone, // undefined - /* 0x3c */ kKeyNone, kKeyNone, // undefined - /* 0x3d */ kKeyNone, kKeyNone, // undefined - /* 0x3e */ kKeyNone, kKeyNone, // undefined - /* 0x3f */ kKeyNone, kKeyNone, // undefined - /* 0x40 */ kKeyNone, kKeyNone, // undefined - /* 0x41 */ kKeyNone, kKeyNone, // VK_A - /* 0x42 */ kKeyNone, kKeyNone, // VK_B - /* 0x43 */ kKeyNone, kKeyNone, // VK_C - /* 0x44 */ kKeyNone, kKeyNone, // VK_D - /* 0x45 */ kKeyNone, kKeyNone, // VK_E - /* 0x46 */ kKeyNone, kKeyNone, // VK_F - /* 0x47 */ kKeyNone, kKeyNone, // VK_G - /* 0x48 */ kKeyNone, kKeyNone, // VK_H - /* 0x49 */ kKeyNone, kKeyNone, // VK_I - /* 0x4a */ kKeyNone, kKeyNone, // VK_J - /* 0x4b */ kKeyNone, kKeyNone, // VK_K - /* 0x4c */ kKeyNone, kKeyNone, // VK_L - /* 0x4d */ kKeyNone, kKeyNone, // VK_M - /* 0x4e */ kKeyNone, kKeyNone, // VK_N - /* 0x4f */ kKeyNone, kKeyNone, // VK_O - /* 0x50 */ kKeyNone, kKeyNone, // VK_P - /* 0x51 */ kKeyNone, kKeyNone, // VK_Q - /* 0x52 */ kKeyNone, kKeyNone, // VK_R - /* 0x53 */ kKeyNone, kKeyNone, // VK_S - /* 0x54 */ kKeyNone, kKeyNone, // VK_T - /* 0x55 */ kKeyNone, kKeyNone, // VK_U - /* 0x56 */ kKeyNone, kKeyNone, // VK_V - /* 0x57 */ kKeyNone, kKeyNone, // VK_W - /* 0x58 */ kKeyNone, kKeyNone, // VK_X - /* 0x59 */ kKeyNone, kKeyNone, // VK_Y - /* 0x5a */ kKeyNone, kKeyNone, // VK_Z - /* 0x5b */ kKeyNone, kKeySuper_L, // VK_LWIN - /* 0x5c */ kKeyNone, kKeySuper_R, // VK_RWIN - /* 0x5d */ kKeyMenu, kKeyMenu, // VK_APPS - /* 0x5e */ kKeyNone, kKeyNone, // undefined - /* 0x5f */ kKeyNone, kKeyNone, // undefined - /* 0x60 */ kKeyKP_0, kKeyNone, // VK_NUMPAD0 - /* 0x61 */ kKeyKP_1, kKeyNone, // VK_NUMPAD1 - /* 0x62 */ kKeyKP_2, kKeyNone, // VK_NUMPAD2 - /* 0x63 */ kKeyKP_3, kKeyNone, // VK_NUMPAD3 - /* 0x64 */ kKeyKP_4, kKeyNone, // VK_NUMPAD4 - /* 0x65 */ kKeyKP_5, kKeyNone, // VK_NUMPAD5 - /* 0x66 */ kKeyKP_6, kKeyNone, // VK_NUMPAD6 - /* 0x67 */ kKeyKP_7, kKeyNone, // VK_NUMPAD7 - /* 0x68 */ kKeyKP_8, kKeyNone, // VK_NUMPAD8 - /* 0x69 */ kKeyKP_9, kKeyNone, // VK_NUMPAD9 - /* 0x6a */ kKeyKP_Multiply, kKeyNone, // VK_MULTIPLY - /* 0x6b */ kKeyKP_Add, kKeyNone, // VK_ADD - /* 0x6c */ kKeyKP_Separator,kKeyKP_Separator,// VK_SEPARATOR - /* 0x6d */ kKeyKP_Subtract, kKeyNone, // VK_SUBTRACT - /* 0x6e */ kKeyKP_Decimal, kKeyNone, // VK_DECIMAL - /* 0x6f */ kKeyNone, kKeyKP_Divide, // VK_DIVIDE - /* 0x70 */ kKeyF1, kKeyNone, // VK_F1 - /* 0x71 */ kKeyF2, kKeyNone, // VK_F2 - /* 0x72 */ kKeyF3, kKeyNone, // VK_F3 - /* 0x73 */ kKeyF4, kKeyNone, // VK_F4 - /* 0x74 */ kKeyF5, kKeyNone, // VK_F5 - /* 0x75 */ kKeyF6, kKeyNone, // VK_F6 - /* 0x76 */ kKeyF7, kKeyNone, // VK_F7 - /* 0x77 */ kKeyF8, kKeyNone, // VK_F8 - /* 0x78 */ kKeyF9, kKeyNone, // VK_F9 - /* 0x79 */ kKeyF10, kKeyNone, // VK_F10 - /* 0x7a */ kKeyF11, kKeyNone, // VK_F11 - /* 0x7b */ kKeyF12, kKeyNone, // VK_F12 - /* 0x7c */ kKeyF13, kKeyF13, // VK_F13 - /* 0x7d */ kKeyF14, kKeyF14, // VK_F14 - /* 0x7e */ kKeyF15, kKeyF15, // VK_F15 - /* 0x7f */ kKeyF16, kKeyF16, // VK_F16 - /* 0x80 */ kKeyF17, kKeyF17, // VK_F17 - /* 0x81 */ kKeyF18, kKeyF18, // VK_F18 - /* 0x82 */ kKeyF19, kKeyF19, // VK_F19 - /* 0x83 */ kKeyF20, kKeyF20, // VK_F20 - /* 0x84 */ kKeyF21, kKeyF21, // VK_F21 - /* 0x85 */ kKeyF22, kKeyF22, // VK_F22 - /* 0x86 */ kKeyF23, kKeyF23, // VK_F23 - /* 0x87 */ kKeyF24, kKeyF24, // VK_F24 - /* 0x88 */ kKeyNone, kKeyNone, // unassigned - /* 0x89 */ kKeyNone, kKeyNone, // unassigned - /* 0x8a */ kKeyNone, kKeyNone, // unassigned - /* 0x8b */ kKeyNone, kKeyNone, // unassigned - /* 0x8c */ kKeyNone, kKeyNone, // unassigned - /* 0x8d */ kKeyNone, kKeyNone, // unassigned - /* 0x8e */ kKeyNone, kKeyNone, // unassigned - /* 0x8f */ kKeyNone, kKeyNone, // unassigned - /* 0x90 */ kKeyNumLock, kKeyNumLock, // VK_NUMLOCK - /* 0x91 */ kKeyScrollLock, kKeyNone, // VK_SCROLL - /* 0x92 */ kKeyNone, kKeyNone, // unassigned - /* 0x93 */ kKeyNone, kKeyNone, // unassigned - /* 0x94 */ kKeyNone, kKeyNone, // unassigned - /* 0x95 */ kKeyNone, kKeyNone, // unassigned - /* 0x96 */ kKeyNone, kKeyNone, // unassigned - /* 0x97 */ kKeyNone, kKeyNone, // unassigned - /* 0x98 */ kKeyNone, kKeyNone, // unassigned - /* 0x99 */ kKeyNone, kKeyNone, // unassigned - /* 0x9a */ kKeyNone, kKeyNone, // unassigned - /* 0x9b */ kKeyNone, kKeyNone, // unassigned - /* 0x9c */ kKeyNone, kKeyNone, // unassigned - /* 0x9d */ kKeyNone, kKeyNone, // unassigned - /* 0x9e */ kKeyNone, kKeyNone, // unassigned - /* 0x9f */ kKeyNone, kKeyNone, // unassigned - /* 0xa0 */ kKeyShift_L, kKeyShift_L, // VK_LSHIFT - /* 0xa1 */ kKeyShift_R, kKeyShift_R, // VK_RSHIFT - /* 0xa2 */ kKeyControl_L, kKeyControl_L, // VK_LCONTROL - /* 0xa3 */ kKeyControl_R, kKeyControl_R, // VK_RCONTROL - /* 0xa4 */ kKeyAlt_L, kKeyAlt_L, // VK_LMENU - /* 0xa5 */ kKeyAlt_R, kKeyAlt_R, // VK_RMENU - /* 0xa6 */ kKeyNone, kKeyWWWBack, // VK_BROWSER_BACK - /* 0xa7 */ kKeyNone, kKeyWWWForward, // VK_BROWSER_FORWARD - /* 0xa8 */ kKeyNone, kKeyWWWRefresh, // VK_BROWSER_REFRESH - /* 0xa9 */ kKeyNone, kKeyWWWStop, // VK_BROWSER_STOP - /* 0xaa */ kKeyNone, kKeyWWWSearch, // VK_BROWSER_SEARCH - /* 0xab */ kKeyNone, kKeyWWWFavorites, // VK_BROWSER_FAVORITES - /* 0xac */ kKeyNone, kKeyWWWHome, // VK_BROWSER_HOME - /* 0xad */ kKeyNone, kKeyAudioMute, // VK_VOLUME_MUTE - /* 0xae */ kKeyNone, kKeyAudioDown, // VK_VOLUME_DOWN - /* 0xaf */ kKeyNone, kKeyAudioUp, // VK_VOLUME_UP - /* 0xb0 */ kKeyNone, kKeyAudioNext, // VK_MEDIA_NEXT_TRACK - /* 0xb1 */ kKeyNone, kKeyAudioPrev, // VK_MEDIA_PREV_TRACK - /* 0xb2 */ kKeyNone, kKeyAudioStop, // VK_MEDIA_STOP - /* 0xb3 */ kKeyNone, kKeyAudioPlay, // VK_MEDIA_PLAY_PAUSE - /* 0xb4 */ kKeyNone, kKeyAppMail, // VK_LAUNCH_MAIL - /* 0xb5 */ kKeyNone, kKeyAppMedia, // VK_LAUNCH_MEDIA_SELECT - /* 0xb6 */ kKeyNone, kKeyAppUser1, // VK_LAUNCH_APP1 - /* 0xb7 */ kKeyNone, kKeyAppUser2, // VK_LAUNCH_APP2 - /* 0xb8 */ kKeyNone, kKeyNone, // unassigned - /* 0xb9 */ kKeyNone, kKeyNone, // unassigned - /* 0xba */ kKeyNone, kKeyNone, // OEM specific - /* 0xbb */ kKeyNone, kKeyNone, // OEM specific - /* 0xbc */ kKeyNone, kKeyNone, // OEM specific - /* 0xbd */ kKeyNone, kKeyNone, // OEM specific - /* 0xbe */ kKeyNone, kKeyNone, // OEM specific - /* 0xbf */ kKeyNone, kKeyNone, // OEM specific - /* 0xc0 */ kKeyNone, kKeyNone, // OEM specific - /* 0xc1 */ kKeyNone, kKeyNone, // unassigned - /* 0xc2 */ kKeyNone, kKeyNone, // unassigned - /* 0xc3 */ kKeyNone, kKeyNone, // unassigned - /* 0xc4 */ kKeyNone, kKeyNone, // unassigned - /* 0xc5 */ kKeyNone, kKeyNone, // unassigned - /* 0xc6 */ kKeyNone, kKeyNone, // unassigned - /* 0xc7 */ kKeyNone, kKeyNone, // unassigned - /* 0xc8 */ kKeyNone, kKeyNone, // unassigned - /* 0xc9 */ kKeyNone, kKeyNone, // unassigned - /* 0xca */ kKeyNone, kKeyNone, // unassigned - /* 0xcb */ kKeyNone, kKeyNone, // unassigned - /* 0xcc */ kKeyNone, kKeyNone, // unassigned - /* 0xcd */ kKeyNone, kKeyNone, // unassigned - /* 0xce */ kKeyNone, kKeyNone, // unassigned - /* 0xcf */ kKeyNone, kKeyNone, // unassigned - /* 0xd0 */ kKeyNone, kKeyNone, // unassigned - /* 0xd1 */ kKeyNone, kKeyNone, // unassigned - /* 0xd2 */ kKeyNone, kKeyNone, // unassigned - /* 0xd3 */ kKeyNone, kKeyNone, // unassigned - /* 0xd4 */ kKeyNone, kKeyNone, // unassigned - /* 0xd5 */ kKeyNone, kKeyNone, // unassigned - /* 0xd6 */ kKeyNone, kKeyNone, // unassigned - /* 0xd7 */ kKeyNone, kKeyNone, // unassigned - /* 0xd8 */ kKeyNone, kKeyNone, // unassigned - /* 0xd9 */ kKeyNone, kKeyNone, // unassigned - /* 0xda */ kKeyNone, kKeyNone, // unassigned - /* 0xdb */ kKeyNone, kKeyNone, // OEM specific - /* 0xdc */ kKeyNone, kKeyNone, // OEM specific - /* 0xdd */ kKeyNone, kKeyNone, // OEM specific - /* 0xde */ kKeyNone, kKeyNone, // OEM specific - /* 0xdf */ kKeyNone, kKeyNone, // OEM specific - /* 0xe0 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe1 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe2 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe3 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe4 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe5 */ kKeyNone, kKeyNone, // unassigned - /* 0xe6 */ kKeyNone, kKeyNone, // OEM specific - /* 0xe7 */ kKeyNone, kKeyNone, // unassigned - /* 0xe8 */ kKeyNone, kKeyNone, // unassigned - /* 0xe9 */ kKeyNone, kKeyNone, // OEM specific - /* 0xea */ kKeyNone, kKeyNone, // OEM specific - /* 0xeb */ kKeyNone, kKeyNone, // OEM specific - /* 0xec */ kKeyNone, kKeyNone, // OEM specific - /* 0xed */ kKeyNone, kKeyNone, // OEM specific - /* 0xee */ kKeyNone, kKeyNone, // OEM specific - /* 0xef */ kKeyNone, kKeyNone, // OEM specific - /* 0xf0 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf1 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf2 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf3 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf4 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf5 */ kKeyNone, kKeyNone, // OEM specific - /* 0xf6 */ kKeyNone, kKeyNone, // VK_ATTN - /* 0xf7 */ kKeyNone, kKeyNone, // VK_CRSEL - /* 0xf8 */ kKeyNone, kKeyNone, // VK_EXSEL - /* 0xf9 */ kKeyNone, kKeyNone, // VK_EREOF - /* 0xfa */ kKeyNone, kKeyNone, // VK_PLAY - /* 0xfb */ kKeyNone, kKeyNone, // VK_ZOOM - /* 0xfc */ kKeyNone, kKeyNone, // reserved - /* 0xfd */ kKeyNone, kKeyNone, // VK_PA1 - /* 0xfe */ kKeyNone, kKeyNone, // VK_OEM_CLEAR - /* 0xff */ kKeyNone, kKeyNone // reserved + /* 0x00 */ { kKeyNone, kKeyNone }, // reserved + /* 0x01 */ { kKeyNone, kKeyNone }, // VK_LBUTTON + /* 0x02 */ { kKeyNone, kKeyNone }, // VK_RBUTTON + /* 0x03 */ { kKeyNone, kKeyBreak }, // VK_CANCEL + /* 0x04 */ { kKeyNone, kKeyNone }, // VK_MBUTTON + /* 0x05 */ { kKeyNone, kKeyNone }, // undefined + /* 0x06 */ { kKeyNone, kKeyNone }, // undefined + /* 0x07 */ { kKeyNone, kKeyNone }, // undefined + /* 0x08 */ { kKeyBackSpace, kKeyNone }, // VK_BACK + /* 0x09 */ { kKeyTab, kKeyNone }, // VK_TAB + /* 0x0a */ { kKeyNone, kKeyNone }, // undefined + /* 0x0b */ { kKeyNone, kKeyNone }, // undefined + /* 0x0c */ { kKeyClear, kKeyClear }, // VK_CLEAR + /* 0x0d */ { kKeyReturn, kKeyKP_Enter }, // VK_RETURN + /* 0x0e */ { kKeyNone, kKeyNone }, // undefined + /* 0x0f */ { kKeyNone, kKeyNone }, // undefined + /* 0x10 */ { kKeyShift_L, kKeyShift_R }, // VK_SHIFT + /* 0x11 */ { kKeyControl_L, kKeyControl_R },// VK_CONTROL + /* 0x12 */ { kKeyAlt_L, kKeyAlt_R }, // VK_MENU + /* 0x13 */ { kKeyPause, kKeyNone }, // VK_PAUSE + /* 0x14 */ { kKeyCapsLock, kKeyNone }, // VK_CAPITAL + /* 0x15 */ { kKeyNone, kKeyNone }, // VK_KANA + /* 0x16 */ { kKeyNone, kKeyNone }, // VK_HANGUL + /* 0x17 */ { kKeyNone, kKeyNone }, // VK_JUNJA + /* 0x18 */ { kKeyNone, kKeyNone }, // VK_FINAL + /* 0x19 */ { kKeyZenkaku, kKeyNone }, // VK_KANJI + /* 0x1a */ { kKeyNone, kKeyNone }, // undefined + /* 0x1b */ { kKeyEscape, kKeyNone }, // VK_ESCAPE + /* 0x1c */ { kKeyNone, kKeyNone }, // VK_CONVERT + /* 0x1d */ { kKeyNone, kKeyNone }, // VK_NONCONVERT + /* 0x1e */ { kKeyNone, kKeyNone }, // VK_ACCEPT + /* 0x1f */ { kKeyNone, kKeyNone }, // VK_MODECHANGE + /* 0x20 */ { kKeyNone, kKeyNone }, // VK_SPACE + /* 0x21 */ { kKeyKP_PageUp, kKeyPageUp }, // VK_PRIOR + /* 0x22 */ { kKeyKP_PageDown, kKeyPageDown }, // VK_NEXT + /* 0x23 */ { kKeyKP_End, kKeyEnd }, // VK_END + /* 0x24 */ { kKeyKP_Home, kKeyHome }, // VK_HOME + /* 0x25 */ { kKeyKP_Left, kKeyLeft }, // VK_LEFT + /* 0x26 */ { kKeyKP_Up, kKeyUp }, // VK_UP + /* 0x27 */ { kKeyKP_Right, kKeyRight }, // VK_RIGHT + /* 0x28 */ { kKeyKP_Down, kKeyDown }, // VK_DOWN + /* 0x29 */ { kKeySelect, kKeySelect }, // VK_SELECT + /* 0x2a */ { kKeyNone, kKeyNone }, // VK_PRINT + /* 0x2b */ { kKeyExecute, kKeyExecute }, // VK_EXECUTE + /* 0x2c */ { kKeyPrint, kKeyPrint }, // VK_SNAPSHOT + /* 0x2d */ { kKeyKP_Insert, kKeyInsert }, // VK_INSERT + /* 0x2e */ { kKeyKP_Delete, kKeyDelete }, // VK_DELETE + /* 0x2f */ { kKeyHelp, kKeyHelp }, // VK_HELP + /* 0x30 */ { kKeyNone, kKeyNone }, // VK_0 + /* 0x31 */ { kKeyNone, kKeyNone }, // VK_1 + /* 0x32 */ { kKeyNone, kKeyNone }, // VK_2 + /* 0x33 */ { kKeyNone, kKeyNone }, // VK_3 + /* 0x34 */ { kKeyNone, kKeyNone }, // VK_4 + /* 0x35 */ { kKeyNone, kKeyNone }, // VK_5 + /* 0x36 */ { kKeyNone, kKeyNone }, // VK_6 + /* 0x37 */ { kKeyNone, kKeyNone }, // VK_7 + /* 0x38 */ { kKeyNone, kKeyNone }, // VK_8 + /* 0x39 */ { kKeyNone, kKeyNone }, // VK_9 + /* 0x3a */ { kKeyNone, kKeyNone }, // undefined + /* 0x3b */ { kKeyNone, kKeyNone }, // undefined + /* 0x3c */ { kKeyNone, kKeyNone }, // undefined + /* 0x3d */ { kKeyNone, kKeyNone }, // undefined + /* 0x3e */ { kKeyNone, kKeyNone }, // undefined + /* 0x3f */ { kKeyNone, kKeyNone }, // undefined + /* 0x40 */ { kKeyNone, kKeyNone }, // undefined + /* 0x41 */ { kKeyNone, kKeyNone }, // VK_A + /* 0x42 */ { kKeyNone, kKeyNone }, // VK_B + /* 0x43 */ { kKeyNone, kKeyNone }, // VK_C + /* 0x44 */ { kKeyNone, kKeyNone }, // VK_D + /* 0x45 */ { kKeyNone, kKeyNone }, // VK_E + /* 0x46 */ { kKeyNone, kKeyNone }, // VK_F + /* 0x47 */ { kKeyNone, kKeyNone }, // VK_G + /* 0x48 */ { kKeyNone, kKeyNone }, // VK_H + /* 0x49 */ { kKeyNone, kKeyNone }, // VK_I + /* 0x4a */ { kKeyNone, kKeyNone }, // VK_J + /* 0x4b */ { kKeyNone, kKeyNone }, // VK_K + /* 0x4c */ { kKeyNone, kKeyNone }, // VK_L + /* 0x4d */ { kKeyNone, kKeyNone }, // VK_M + /* 0x4e */ { kKeyNone, kKeyNone }, // VK_N + /* 0x4f */ { kKeyNone, kKeyNone }, // VK_O + /* 0x50 */ { kKeyNone, kKeyNone }, // VK_P + /* 0x51 */ { kKeyNone, kKeyNone }, // VK_Q + /* 0x52 */ { kKeyNone, kKeyNone }, // VK_R + /* 0x53 */ { kKeyNone, kKeyNone }, // VK_S + /* 0x54 */ { kKeyNone, kKeyNone }, // VK_T + /* 0x55 */ { kKeyNone, kKeyNone }, // VK_U + /* 0x56 */ { kKeyNone, kKeyNone }, // VK_V + /* 0x57 */ { kKeyNone, kKeyNone }, // VK_W + /* 0x58 */ { kKeyNone, kKeyNone }, // VK_X + /* 0x59 */ { kKeyNone, kKeyNone }, // VK_Y + /* 0x5a */ { kKeyNone, kKeyNone }, // VK_Z + /* 0x5b */ { kKeyNone, kKeySuper_L }, // VK_LWIN + /* 0x5c */ { kKeyNone, kKeySuper_R }, // VK_RWIN + /* 0x5d */ { kKeyMenu, kKeyMenu }, // VK_APPS + /* 0x5e */ { kKeyNone, kKeyNone }, // undefined + /* 0x5f */ { kKeyNone, kKeyNone }, // undefined + /* 0x60 */ { kKeyKP_0, kKeyNone }, // VK_NUMPAD0 + /* 0x61 */ { kKeyKP_1, kKeyNone }, // VK_NUMPAD1 + /* 0x62 */ { kKeyKP_2, kKeyNone }, // VK_NUMPAD2 + /* 0x63 */ { kKeyKP_3, kKeyNone }, // VK_NUMPAD3 + /* 0x64 */ { kKeyKP_4, kKeyNone }, // VK_NUMPAD4 + /* 0x65 */ { kKeyKP_5, kKeyNone }, // VK_NUMPAD5 + /* 0x66 */ { kKeyKP_6, kKeyNone }, // VK_NUMPAD6 + /* 0x67 */ { kKeyKP_7, kKeyNone }, // VK_NUMPAD7 + /* 0x68 */ { kKeyKP_8, kKeyNone }, // VK_NUMPAD8 + /* 0x69 */ { kKeyKP_9, kKeyNone }, // VK_NUMPAD9 + /* 0x6a */ { kKeyKP_Multiply, kKeyNone }, // VK_MULTIPLY + /* 0x6b */ { kKeyKP_Add, kKeyNone }, // VK_ADD + /* 0x6c */ { kKeyKP_Separator, kKeyKP_Separator },// VK_SEPARATOR + /* 0x6d */ { kKeyKP_Subtract, kKeyNone }, // VK_SUBTRACT + /* 0x6e */ { kKeyKP_Decimal, kKeyNone }, // VK_DECIMAL + /* 0x6f */ { kKeyNone, kKeyKP_Divide },// VK_DIVIDE + /* 0x70 */ { kKeyF1, kKeyNone }, // VK_F1 + /* 0x71 */ { kKeyF2, kKeyNone }, // VK_F2 + /* 0x72 */ { kKeyF3, kKeyNone }, // VK_F3 + /* 0x73 */ { kKeyF4, kKeyNone }, // VK_F4 + /* 0x74 */ { kKeyF5, kKeyNone }, // VK_F5 + /* 0x75 */ { kKeyF6, kKeyNone }, // VK_F6 + /* 0x76 */ { kKeyF7, kKeyNone }, // VK_F7 + /* 0x77 */ { kKeyF8, kKeyNone }, // VK_F8 + /* 0x78 */ { kKeyF9, kKeyNone }, // VK_F9 + /* 0x79 */ { kKeyF10, kKeyNone }, // VK_F10 + /* 0x7a */ { kKeyF11, kKeyNone }, // VK_F11 + /* 0x7b */ { kKeyF12, kKeyNone }, // VK_F12 + /* 0x7c */ { kKeyF13, kKeyF13 }, // VK_F13 + /* 0x7d */ { kKeyF14, kKeyF14 }, // VK_F14 + /* 0x7e */ { kKeyF15, kKeyF15 }, // VK_F15 + /* 0x7f */ { kKeyF16, kKeyF16 }, // VK_F16 + /* 0x80 */ { kKeyF17, kKeyF17 }, // VK_F17 + /* 0x81 */ { kKeyF18, kKeyF18 }, // VK_F18 + /* 0x82 */ { kKeyF19, kKeyF19 }, // VK_F19 + /* 0x83 */ { kKeyF20, kKeyF20 }, // VK_F20 + /* 0x84 */ { kKeyF21, kKeyF21 }, // VK_F21 + /* 0x85 */ { kKeyF22, kKeyF22 }, // VK_F22 + /* 0x86 */ { kKeyF23, kKeyF23 }, // VK_F23 + /* 0x87 */ { kKeyF24, kKeyF24 }, // VK_F24 + /* 0x88 */ { kKeyNone, kKeyNone }, // unassigned + /* 0x89 */ { kKeyNone, kKeyNone }, // unassigned + /* 0x8a */ { kKeyNone, kKeyNone }, // unassigned + /* 0x8b */ { kKeyNone, kKeyNone }, // unassigned + /* 0x8c */ { kKeyNone, kKeyNone }, // unassigned + /* 0x8d */ { kKeyNone, kKeyNone }, // unassigned + /* 0x8e */ { kKeyNone, kKeyNone }, // unassigned + /* 0x8f */ { kKeyNone, kKeyNone }, // unassigned + /* 0x90 */ { kKeyNumLock, kKeyNumLock }, // VK_NUMLOCK + /* 0x91 */ { kKeyScrollLock, kKeyNone }, // VK_SCROLL + /* 0x92 */ { kKeyNone, kKeyNone }, // unassigned + /* 0x93 */ { kKeyNone, kKeyNone }, // unassigned + /* 0x94 */ { kKeyNone, kKeyNone }, // unassigned + /* 0x95 */ { kKeyNone, kKeyNone }, // unassigned + /* 0x96 */ { kKeyNone, kKeyNone }, // unassigned + /* 0x97 */ { kKeyNone, kKeyNone }, // unassigned + /* 0x98 */ { kKeyNone, kKeyNone }, // unassigned + /* 0x99 */ { kKeyNone, kKeyNone }, // unassigned + /* 0x9a */ { kKeyNone, kKeyNone }, // unassigned + /* 0x9b */ { kKeyNone, kKeyNone }, // unassigned + /* 0x9c */ { kKeyNone, kKeyNone }, // unassigned + /* 0x9d */ { kKeyNone, kKeyNone }, // unassigned + /* 0x9e */ { kKeyNone, kKeyNone }, // unassigned + /* 0x9f */ { kKeyNone, kKeyNone }, // unassigned + /* 0xa0 */ { kKeyShift_L, kKeyShift_L }, // VK_LSHIFT + /* 0xa1 */ { kKeyShift_R, kKeyShift_R }, // VK_RSHIFT + /* 0xa2 */ { kKeyControl_L, kKeyControl_L },// VK_LCONTROL + /* 0xa3 */ { kKeyControl_R, kKeyControl_R },// VK_RCONTROL + /* 0xa4 */ { kKeyAlt_L, kKeyAlt_L }, // VK_LMENU + /* 0xa5 */ { kKeyAlt_R, kKeyAlt_R }, // VK_RMENU + /* 0xa6 */ { kKeyNone, kKeyWWWBack }, // VK_BROWSER_BACK + /* 0xa7 */ { kKeyNone, kKeyWWWForward },// VK_BROWSER_FORWARD + /* 0xa8 */ { kKeyNone, kKeyWWWRefresh },// VK_BROWSER_REFRESH + /* 0xa9 */ { kKeyNone, kKeyWWWStop }, // VK_BROWSER_STOP + /* 0xaa */ { kKeyNone, kKeyWWWSearch },// VK_BROWSER_SEARCH + /* 0xab */ { kKeyNone, kKeyWWWFavorites },// VK_BROWSER_FAVORITES + /* 0xac */ { kKeyNone, kKeyWWWHome }, // VK_BROWSER_HOME + /* 0xad */ { kKeyNone, kKeyAudioMute },// VK_VOLUME_MUTE + /* 0xae */ { kKeyNone, kKeyAudioDown },// VK_VOLUME_DOWN + /* 0xaf */ { kKeyNone, kKeyAudioUp }, // VK_VOLUME_UP + /* 0xb0 */ { kKeyNone, kKeyAudioNext },// VK_MEDIA_NEXT_TRACK + /* 0xb1 */ { kKeyNone, kKeyAudioPrev },// VK_MEDIA_PREV_TRACK + /* 0xb2 */ { kKeyNone, kKeyAudioStop },// VK_MEDIA_STOP + /* 0xb3 */ { kKeyNone, kKeyAudioPlay },// VK_MEDIA_PLAY_PAUSE + /* 0xb4 */ { kKeyNone, kKeyAppMail }, // VK_LAUNCH_MAIL + /* 0xb5 */ { kKeyNone, kKeyAppMedia }, // VK_LAUNCH_MEDIA_SELECT + /* 0xb6 */ { kKeyNone, kKeyAppUser1 }, // VK_LAUNCH_APP1 + /* 0xb7 */ { kKeyNone, kKeyAppUser2 }, // VK_LAUNCH_APP2 + /* 0xb8 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xb9 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xba */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xbb */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xbc */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xbd */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xbe */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xbf */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xc0 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xc1 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xc2 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xc3 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xc4 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xc5 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xc6 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xc7 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xc8 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xc9 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xca */ { kKeyNone, kKeyNone }, // unassigned + /* 0xcb */ { kKeyNone, kKeyNone }, // unassigned + /* 0xcc */ { kKeyNone, kKeyNone }, // unassigned + /* 0xcd */ { kKeyNone, kKeyNone }, // unassigned + /* 0xce */ { kKeyNone, kKeyNone }, // unassigned + /* 0xcf */ { kKeyNone, kKeyNone }, // unassigned + /* 0xd0 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xd1 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xd2 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xd3 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xd4 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xd5 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xd6 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xd7 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xd8 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xd9 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xda */ { kKeyNone, kKeyNone }, // unassigned + /* 0xdb */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xdc */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xdd */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xde */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xdf */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xe0 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xe1 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xe2 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xe3 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xe4 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xe5 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xe6 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xe7 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xe8 */ { kKeyNone, kKeyNone }, // unassigned + /* 0xe9 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xea */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xeb */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xec */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xed */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xee */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xef */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xf0 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xf1 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xf2 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xf3 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xf4 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xf5 */ { kKeyNone, kKeyNone }, // OEM specific + /* 0xf6 */ { kKeyNone, kKeyNone }, // VK_ATTN + /* 0xf7 */ { kKeyNone, kKeyNone }, // VK_CRSEL + /* 0xf8 */ { kKeyNone, kKeyNone }, // VK_EXSEL + /* 0xf9 */ { kKeyNone, kKeyNone }, // VK_EREOF + /* 0xfa */ { kKeyNone, kKeyNone }, // VK_PLAY + /* 0xfb */ { kKeyNone, kKeyNone }, // VK_ZOOM + /* 0xfc */ { kKeyNone, kKeyNone }, // reserved + /* 0xfd */ { kKeyNone, kKeyNone }, // VK_PA1 + /* 0xfe */ { kKeyNone, kKeyNone }, // VK_OEM_CLEAR + /* 0xff */ { kKeyNone, kKeyNone } // reserved }; // map special KeyID keys to virtual key codes plus whether or not @@ -1561,7 +1561,8 @@ CMSWindowsKeyState::mapCharacter(Keystrokes& keys, // first check the easy way. if that doesn't work // then see if it's a dead key. unsigned char uc = static_cast(c); - if (CharLower((LPTSTR)uc) != CharUpper((LPTSTR)uc) || + if (CharLower(reinterpret_cast(uc)) != + CharUpper(reinterpret_cast(uc)) || (MapVirtualKeyEx(virtualKey, 2, hkl) & 0x80000000lu) != 0) { LOG((CLOG_DEBUG2 "flip shift")); desiredMask ^= KeyModifierShift; diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 392093ff..0ee9d454 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -32,15 +32,19 @@ #include "CArch.h" #include "CArchMiscWindows.h" #include +#include // -// add backwards compatible multihead support (and suppress bogus warning) +// add backwards compatible multihead support (and suppress bogus warning). +// this isn't supported on MinGW yet AFAICT. // +#if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4706) // assignment within conditional #define COMPILE_MULTIMON_STUBS #include #pragma warning(pop) +#endif // X button stuff #if !defined(WM_XBUTTONDOWN) @@ -60,6 +64,11 @@ #define VK_XBUTTON2 0x06 #endif +// WM_POWERBROADCAST stuff +#if !defined(PBT_APMRESUMEAUTOMATIC) +#define PBT_APMRESUMEAUTOMATIC 0x0012 +#endif + // // CMSWindowsScreen // @@ -73,7 +82,6 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_is95Family(CArchMiscWindows::isWindows95Family()), m_isOnScreen(m_isPrimary), m_class(0), - m_window(NULL), m_x(0), m_y(0), m_w(0), m_h(0), m_xCenter(0), m_yCenter(0), @@ -86,6 +94,7 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_fixTimer(NULL), m_screensaver(NULL), m_screensaverNotify(false), + m_window(NULL), m_nextClipboardWindow(NULL), m_ownClipboard(false), m_desks(NULL), @@ -600,7 +609,7 @@ void CMSWindowsScreen::destroyClass(ATOM windowClass) const { if (windowClass != 0) { - UnregisterClass((LPCTSTR)windowClass, s_instance); + UnregisterClass(reinterpret_cast(windowClass), s_instance); } } @@ -610,7 +619,7 @@ CMSWindowsScreen::createWindow(ATOM windowClass, const char* name) const HWND window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - (LPCTSTR)windowClass, + reinterpret_cast(windowClass), name, WS_POPUP, 0, 0, 1, 1, diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index 6a821281..b9101c9f 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -42,8 +42,8 @@ CMSWindowsScreenSaver::CMSWindowsScreenSaver() : m_wasSecure(false), m_wasSecureAnInt(false), m_process(NULL), - m_threadID(0), - m_watch(NULL) + m_watch(NULL), + m_threadID(0) { // detect OS m_is95Family = false; diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index a4aeb098..b7441ae2 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -70,8 +70,10 @@ typedef struct tagMOUSEHOOKSTRUCTWin2000 { // globals // +#if defined(_MSC_VER) #pragma comment(linker, "-section:shared,rws") #pragma data_seg("shared") +#endif // all data in this shared section *must* be initialized static HINSTANCE g_hinstance = NULL; @@ -99,13 +101,17 @@ static BYTE g_deadKeyState[256] = { 0 }; static DWORD g_hookThread = 0; static DWORD g_attachedThread = 0; +#if defined(_MSC_VER) #pragma data_seg() +#endif // keep linker quiet about floating point stuff. we don't use any // floating point operations but our includes may define some // (unused) floating point values. #ifndef _DEBUG -extern "C" int _fltused=0; +extern "C" { +int _fltused=0; +} #endif @@ -554,6 +560,9 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) w = static_cast(HIWORD(info2k->mouseData)); break; } + + default: + break; } } @@ -601,7 +610,7 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_getMessage, code, wParam, lParam); } -#if (_WIN32_WINNT >= 0x0400) && !NO_LOWLEVEL_HOOKS +#if (_WIN32_WINNT >= 0x0400) && defined(_MSC_VER) && !NO_LOWLEVEL_HOOKS // // low-level keyboard hook -- this allows us to capture and handle @@ -693,6 +702,7 @@ getWheelSupport() } // not modern. see if we've got old-style support. +#if defined(MSH_WHEELSUPPORT) UINT wheelSupportMsg = RegisterWindowMessage(MSH_WHEELSUPPORT); HWND wheelSupportWindow = FindWindow(MSH_WHEELMODULE_CLASS, MSH_WHEELMODULE_TITLE); @@ -704,6 +714,7 @@ getWheelSupport() } } } +#endif // assume modern. we don't do anything special in this case // except respond to WM_MOUSEWHEEL messages. GetSystemMetrics() @@ -830,7 +841,7 @@ install() } // install low-level hooks. we require that they both get installed. -#if (_WIN32_WINNT >= 0x0400) && !NO_LOWLEVEL_HOOKS +#if (_WIN32_WINNT >= 0x0400) && defined(_MSC_VER) && !NO_LOWLEVEL_HOOKS g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL, &mouseLLHook, g_hinstance, diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp index de7351b9..f0f012a1 100644 --- a/lib/synergy/CProtocolUtil.cpp +++ b/lib/synergy/CProtocolUtil.cpp @@ -209,11 +209,8 @@ CProtocolUtil::vreadf(IStream* stream, const char* fmt, va_list args) const bool useFixed = (len <= sizeof(buffer)); // allocate a buffer to read the data - UInt8* sBuffer; - if (useFixed) { - sBuffer = buffer; - } - else { + UInt8* sBuffer = buffer; + if (!useFixed) { sBuffer = new UInt8[len]; } From e1027eeb3a9ba01d7ec51759a26d63e71434837b Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Apr 2004 19:43:16 +0000 Subject: [PATCH 639/807] More changes for MSYS/MinGW builds. Added makefile for launcher. Still need hook library and resource compiling. --- cmd/launcher/Makefile.am | 53 ++++++++++++++++++++++++++++++++-------- lib/platform/Makefile.am | 5 +++- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am index deb8c719..6c23aa91 100644 --- a/cmd/launcher/Makefile.am +++ b/cmd/launcher/Makefile.am @@ -13,22 +13,55 @@ ## Process this file with automake to produce Makefile.in NULL = -EXTRA_DIST = \ +MSWINDOWS_SOURCE_FILES = \ CAdvancedOptions.cpp \ - CAdvancedOptions.h \ CAutoStart.cpp \ - CAutoStart.h \ CGlobalOptions.cpp \ - CGlobalOptions.h \ LaunchUtil.cpp \ - LaunchUtil.h \ launcher.cpp \ - launcher.dsp \ - launcher.rc \ + CAdvancedOptions.h \ + CAutoStart.h \ + CGlobalOptions.h \ + LaunchUtil.h \ resource.h \ - synergy.ico \ + launcher.rc \ $(NULL) -MAINTAINERCLEANFILES = \ - Makefile.in \ +EXTRA_DIST = \ + launcher.dsp \ + synergy.ico \ + $(MSWINDOWS_SOURCE_FILES) \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + $(NULL) + +bin_PROGRAMS = synergy +if MSWINDOWS +synergy_SOURCES = \ + $(MSWINDOWS_SOURCE_FILES) \ + $(NULL) +endif +synergy_LDADD = \ + $(top_builddir)/lib/client/libserver.a \ + $(top_builddir)/lib/platform/libplatform.a \ + $(top_builddir)/lib/synergy/libsynergy.a \ + $(top_builddir)/lib/net/libnet.a \ + $(top_builddir)/lib/io/libio.a \ + $(top_builddir)/lib/mt/libmt.a \ + $(top_builddir)/lib/base/libbase.a \ + $(top_builddir)/lib/common/libcommon.a \ + $(top_builddir)/lib/arch/libarch.a \ + $(NULL) +INCLUDES = \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/arch \ + -I$(top_srcdir)/lib/base \ + -I$(top_srcdir)/lib/mt \ + -I$(top_srcdir)/lib/io \ + -I$(top_srcdir)/lib/net \ + -I$(top_srcdir)/lib/synergy \ + -I$(top_srcdir)/lib/platform \ + -I$(top_srcdir)/lib/server \ $(NULL) diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 74074267..3361b693 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -44,7 +44,6 @@ MSWINDOWS_SOURCE_FILES = \ CMSWindowsScreen.cpp \ CMSWindowsScreenSaver.cpp \ CMSWindowsUtil.cpp \ - CSynergyHook.cpp \ CMSWindowsClipboard.h \ CMSWindowsClipboardAnyTextConverter.h \ CMSWindowsClipboardTextConverter.h \ @@ -55,6 +54,9 @@ MSWINDOWS_SOURCE_FILES = \ CMSWindowsScreen.h \ CMSWindowsScreenSaver.h \ CMSWindowsUtil.h \ + $(NULL) +MSWINDOWS_HOOK_SOURCE_FILES = \ + CSynergyHook.cpp \ CSynergyHook.h \ $(NULL) CARBON_SOURCE_FILES = \ @@ -76,6 +78,7 @@ EXTRA_DIST = \ synrgyhk.dsp \ $(XWINDOWS_SOURCE_FILES) \ $(MSWINDOWS_SOURCE_FILES) \ + $(MSWINDOWS_HOOK_SOURCE_FILES) \ $(CARBON_SOURCE_FILES) \ $(NULL) From cc577dce7cab06e83a3d86e808d4a28ff41d2cbf Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 11 Apr 2004 20:01:18 +0000 Subject: [PATCH 640/807] Oops, broke build in launcher on non-win32 platforms. --- cmd/launcher/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am index 6c23aa91..4998a89a 100644 --- a/cmd/launcher/Makefile.am +++ b/cmd/launcher/Makefile.am @@ -37,14 +37,14 @@ MAINTAINERCLEANFILES = \ Makefile.in \ $(NULL) -bin_PROGRAMS = synergy if MSWINDOWS +bin_PROGRAMS = synergy synergy_SOURCES = \ $(MSWINDOWS_SOURCE_FILES) \ $(NULL) endif synergy_LDADD = \ - $(top_builddir)/lib/client/libserver.a \ + $(top_builddir)/lib/server/libserver.a \ $(top_builddir)/lib/platform/libplatform.a \ $(top_builddir)/lib/synergy/libsynergy.a \ $(top_builddir)/lib/net/libnet.a \ From 54b3884eba0dab9e2e78e09a02c50443a14e4c20 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 13 Apr 2004 19:39:04 +0000 Subject: [PATCH 641/807] Removed use of mbrtowc, wcrtomb, and mbsinit. Many platforms didn't support them and the emulated versions were just as good except for a performance problem with excessive locking and unlocking of a mutex. So this also changes IArchString to provide string rather than character conversion so we can lock the mutex once per string rather than once per character. --- configure.in | 1 - lib/arch/CArch.cpp | 34 +----- lib/arch/CArch.h | 10 +- lib/arch/CArchStringUnix.cpp | 13 +-- lib/arch/CArchStringUnix.h | 10 +- lib/arch/CArchStringWindows.cpp | 13 +-- lib/arch/CArchStringWindows.h | 10 +- lib/arch/CMultibyte.cpp | 193 +++++++++++++++++++++++++++++--- lib/arch/CMultibyteEmu.cpp | 114 ------------------- lib/arch/CMultibyteOS.cpp | 69 ------------ lib/arch/IArchString.h | 39 ++----- lib/arch/Makefile.am | 2 - lib/arch/arch.dsp | 10 -- lib/base/CUnicode.cpp | 133 ++-------------------- 14 files changed, 216 insertions(+), 435 deletions(-) delete mode 100644 lib/arch/CMultibyteEmu.cpp delete mode 100644 lib/arch/CMultibyteOS.cpp diff --git a/configure.in b/configure.in index dea16432..3be75a98 100644 --- a/configure.in +++ b/configure.in @@ -142,7 +142,6 @@ AC_FUNC_STRFTIME AC_CHECK_FUNCS(gmtime_r) ACX_CHECK_GETPWUID_R AC_CHECK_FUNCS(vsnprintf) -AC_CHECK_FUNCS(wcrtomb mbrtowc mbsinit) AC_FUNC_SELECT_ARGTYPES ACX_CHECK_POLL dnl use AC_REPLACE_FUNCS() for stuff in string.h diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index 943cbbb6..a0cb5d34 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -561,40 +561,16 @@ CArch::vsnprintf(char* str, int size, const char* fmt, va_list ap) return m_string->vsnprintf(str, size, fmt, ap); } -CArchMBState -CArch::newMBState() +int +CArch::convStringMBToWC(wchar_t* dst, const char* src, UInt32 n, bool* errors) { - return m_string->newMBState(); -} - -void -CArch::closeMBState(CArchMBState state) -{ - m_string->closeMBState(state); -} - -void -CArch::initMBState(CArchMBState state) -{ - m_string->initMBState(state); -} - -bool -CArch::isInitMBState(CArchMBState state) -{ - return m_string->isInitMBState(state); + return m_string->convStringMBToWC(dst, src, n, errors); } int -CArch::convMBToWC(wchar_t* dst, const char* src, int n, CArchMBState state) +CArch::convStringWCToMB(char* dst, const wchar_t* src, UInt32 n, bool* errors) { - return m_string->convMBToWC(dst, src, n, state); -} - -int -CArch::convWCToMB(char* dst, wchar_t src, CArchMBState state) -{ - return m_string->convWCToMB(dst, src, state); + return m_string->convStringWCToMB(dst, src, n, errors); } IArchString::EWideCharEncoding diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index 7e70bd84..07e91097 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -159,12 +159,10 @@ public: // IArchString overrides virtual int vsnprintf(char* str, int size, const char* fmt, va_list ap); - virtual CArchMBState newMBState(); - virtual void closeMBState(CArchMBState); - virtual void initMBState(CArchMBState); - virtual bool isInitMBState(CArchMBState); - virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); - virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual int convStringMBToWC(wchar_t*, + const char*, UInt32 n, bool* errors); + virtual int convStringWCToMB(char*, + const wchar_t*, UInt32 n, bool* errors); virtual EWideCharEncoding getWideCharEncoding(); diff --git a/lib/arch/CArchStringUnix.cpp b/lib/arch/CArchStringUnix.cpp index 32b2ad46..e0ad3457 100644 --- a/lib/arch/CArchStringUnix.cpp +++ b/lib/arch/CArchStringUnix.cpp @@ -15,22 +15,11 @@ #include "CArchStringUnix.h" #include -#include "CMultibyte.cpp" - // // CArchStringUnix // -CArchStringUnix::CArchStringUnix() -{ - initMB(); -} - -CArchStringUnix::~CArchStringUnix() -{ - cleanMB(); -} - +#include "CMultibyte.cpp" #include "vsnprintf.cpp" IArchString::EWideCharEncoding diff --git a/lib/arch/CArchStringUnix.h b/lib/arch/CArchStringUnix.h index b9fddcb3..20e5486b 100644 --- a/lib/arch/CArchStringUnix.h +++ b/lib/arch/CArchStringUnix.h @@ -28,12 +28,10 @@ public: // IArchString overrides virtual int vsnprintf(char* str, int size, const char* fmt, va_list ap); - virtual CArchMBState newMBState(); - virtual void closeMBState(CArchMBState); - virtual void initMBState(CArchMBState); - virtual bool isInitMBState(CArchMBState); - virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); - virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual int convStringMBToWC(wchar_t*, + const char*, UInt32 n, bool* errors); + virtual int convStringWCToMB(char*, + const wchar_t*, UInt32 n, bool* errors); virtual EWideCharEncoding getWideCharEncoding(); }; diff --git a/lib/arch/CArchStringWindows.cpp b/lib/arch/CArchStringWindows.cpp index c23f019c..a3b90765 100644 --- a/lib/arch/CArchStringWindows.cpp +++ b/lib/arch/CArchStringWindows.cpp @@ -18,22 +18,11 @@ #include #include -#include "CMultibyte.cpp" - // // CArchStringWindows // -CArchStringWindows::CArchStringWindows() -{ - initMB(); -} - -CArchStringWindows::~CArchStringWindows() -{ - cleanMB(); -} - +#include "CMultibyte.cpp" #define HAVE_VSNPRINTF 1 #define ARCH_VSNPRINTF _vsnprintf #include "vsnprintf.cpp" diff --git a/lib/arch/CArchStringWindows.h b/lib/arch/CArchStringWindows.h index c1303522..a67d8431 100644 --- a/lib/arch/CArchStringWindows.h +++ b/lib/arch/CArchStringWindows.h @@ -28,12 +28,10 @@ public: // IArchString overrides virtual int vsnprintf(char* str, int size, const char* fmt, va_list ap); - virtual CArchMBState newMBState(); - virtual void closeMBState(CArchMBState); - virtual void initMBState(CArchMBState); - virtual bool isInitMBState(CArchMBState); - virtual int convMBToWC(wchar_t*, const char*, int, CArchMBState); - virtual int convWCToMB(char*, wchar_t, CArchMBState); + virtual int convStringMBToWC(wchar_t*, + const char*, UInt32 n, bool* errors); + virtual int convStringWCToMB(char*, + const wchar_t*, UInt32 n, bool* errors); virtual EWideCharEncoding getWideCharEncoding(); }; diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp index 01ec9550..e3186190 100644 --- a/lib/arch/CMultibyte.cpp +++ b/lib/arch/CMultibyte.cpp @@ -16,25 +16,192 @@ #define CMULTIBYTE_H #include "common.h" - -#if (HAVE_MBSINIT && HAVE_MBRTOWC && HAVE_WCRTOMB) || defined(_MSC_VER) -#include "CMultibyteOS.cpp" +#include "CArch.h" +#include +#include +#if HAVE_WCHAR_H +# include +#elif __APPLE__ + // wtf? Darwin puts mbtowc() et al. in stdlib +# include #else -#include "CMultibyteEmu.cpp" -#endif + // platform apparently has no wchar_t support. provide dummy + // implementations. hopefully at least the C++ compiler has + // a built-in wchar_t type. -CArchMBState -ARCH_STRING::newMBState() +static inline +int +mbtowc(wchar_t* dst, const char* src, int n) { - CArchMBState state = new CArchMBStateImpl; - initMBState(state); - return state; + *dst = static_cast(*src); + return 1; } -void -ARCH_STRING::closeMBState(CArchMBState state) +static inline +int +wctomb(char* dst, wchar_t src) { - delete state; + *dst = static_cast(src); + return 1; +} + +#endif + +// +// use C library non-reentrant multibyte conversion with mutex +// + +static CArchMutex s_mutex = NULL; + +ARCH_STRING::ARCH_STRING() +{ + s_mutex = ARCH->newMutex(); +} + +ARCH_STRING::~ARCH_STRING() +{ + ARCH->closeMutex(s_mutex); + s_mutex = NULL; +} + +int +ARCH_STRING::convStringWCToMB(char* dst, + const wchar_t* src, UInt32 n, bool* errors) +{ + int len = 0; + + bool dummyErrors; + if (errors == NULL) { + errors = &dummyErrors; + } + + ARCH->lockMutex(s_mutex); + if (dst == NULL) { + char dummy[MB_LEN_MAX]; + for (const wchar_t* scan = src; n > 0; ++scan, --n) { + int mblen = wctomb(dummy, *scan); + if (mblen == -1) { + *errors = true; + mblen = 1; + } + len += mblen; + } + int mblen = wctomb(NULL, L'\0'); + if (mblen != -1) { + len += mblen - 1; + } + } + else { + char* dst0 = dst; + for (const wchar_t* scan = src; n > 0; ++scan, --n) { + int mblen = wctomb(dst, *scan); + if (mblen == -1) { + *errors = true; + *dst++ = '?'; + } + else { + dst += mblen; + } + } + int mblen = wctomb(dst, L'\0'); + if (mblen != -1) { + // don't include nul terminator + dst += mblen - 1; + } + len = (int)(dst - dst0); + } + ARCH->unlockMutex(s_mutex); + + return (ssize_t)len; +} + +int +ARCH_STRING::convStringMBToWC(wchar_t* dst, + const char* src, UInt32 n, bool* errors) +{ + int len = 0; + wchar_t dummy; + + bool dummyErrors; + if (errors == NULL) { + errors = &dummyErrors; + } + + ARCH->lockMutex(s_mutex); + if (dst == NULL) { + for (const char* scan = src; n > 0; ) { + int mblen = mbtowc(&dummy, scan, n); + switch (mblen) { + case -2: + // incomplete last character. convert to unknown character. + *errors = true; + len += 1; + n = 0; + break; + + case -1: + // invalid character. count one unknown character and + // start at the next byte. + *errors = true; + len += 1; + scan += 1; + n -= 1; + break; + + case 0: + len += 1; + scan += 1; + n -= 1; + break; + + default: + // normal character + len += 1; + scan += mblen; + n -= mblen; + break; + } + } + } + else { + wchar_t* dst0 = dst; + for (const char* scan = src; n > 0; ++dst) { + int mblen = mbtowc(dst, scan, n); + switch (mblen) { + case -2: + // incomplete character. convert to unknown character. + *errors = true; + *dst = (wchar_t)0xfffd; + n = 0; + break; + + case -1: + // invalid character. count one unknown character and + // start at the next byte. + *errors = true; + *dst = (wchar_t)0xfffd; + scan += 1; + n -= 1; + break; + + case 0: + *dst = (wchar_t)0x0000; + scan += 1; + n -= 1; + break; + + default: + // normal character + scan += mblen; + n -= mblen; + break; + } + } + len = (int)(dst - dst0); + } + ARCH->unlockMutex(s_mutex); + + return (ssize_t)len; } #endif diff --git a/lib/arch/CMultibyteEmu.cpp b/lib/arch/CMultibyteEmu.cpp deleted file mode 100644 index 0fd3087a..00000000 --- a/lib/arch/CMultibyteEmu.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CArch.h" -#include -#if HAVE_WCHAR_H -# include -#elif __APPLE__ - // wtf? Darwin puts mbtowc() et al. in stdlib -# include -#else - // platform apparently has no wchar_t support. provide dummy - // implementations. hopefully at least the C++ compiler has - // a built-in wchar_t type. -# undef HAVE_MBSINIT - -static inline -int -mbtowc(wchar_t* dst, const char* src, int n) -{ - *dst = static_cast(*src); - return 1; -} - -static inline -int -wctomb(char* dst, wchar_t src) -{ - *dst = static_cast(src); - return 1; -} - -#endif - -class CArchMBStateImpl { -public: -#if HAVE_MBSINIT - mbstate_t m_mbstate; -#else - int m_mbstate; // dummy data -#endif -}; - -// -// use C library non-reentrant multibyte conversion with mutex -// - -static CArchMutex s_mutex; - -static -void -initMB() -{ - s_mutex = ARCH->newMutex(); -} - -static -void -cleanMB() -{ - ARCH->closeMutex(s_mutex); -} - -void -ARCH_STRING::initMBState(CArchMBState state) -{ - memset(&state->m_mbstate, 0, sizeof(state->m_mbstate)); -} - -bool -ARCH_STRING::isInitMBState(CArchMBState state) -{ - // if we're using this file mbsinit() probably doesn't exist - // but use it if it does -#if HAVE_MBSINIT - return (mbsinit(&state->m_mbstate) != 0); -#else - return true; -#endif -} - -int -ARCH_STRING::convMBToWC(wchar_t* dst, const char* src, int n, CArchMBState) -{ - wchar_t dummy; - ARCH->lockMutex(s_mutex); - int result = mbtowc(dst != NULL ? dst : &dummy, src, n); - ARCH->unlockMutex(s_mutex); - if (result < 0) - return -1; - else - return result; -} - -int -ARCH_STRING::convWCToMB(char* dst, wchar_t src, CArchMBState) -{ - char dummy[MB_LEN_MAX]; - ARCH->lockMutex(s_mutex); - int n = wctomb(dst != NULL ? dst : dummy, src); - ARCH->unlockMutex(s_mutex); - return n; -} diff --git a/lib/arch/CMultibyteOS.cpp b/lib/arch/CMultibyteOS.cpp deleted file mode 100644 index 57f4fd22..00000000 --- a/lib/arch/CMultibyteOS.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include - -class CArchMBStateImpl { -public: - mbstate_t m_mbstate; -}; - -// -// use C library reentrant multibyte conversion -// - -static -void -initMB() -{ - // do nothing -} - -static -void -cleanMB() -{ - // do nothing -} - -void -ARCH_STRING::initMBState(CArchMBState state) -{ - memset(&state->m_mbstate, 0, sizeof(state->m_mbstate)); -} - -bool -ARCH_STRING::isInitMBState(CArchMBState state) -{ - return (mbsinit(&state->m_mbstate) != 0); -} - -int -ARCH_STRING::convMBToWC(wchar_t* dst, const char* src, - int n, CArchMBState state) -{ - wchar_t dummy; - return static_cast(mbrtowc(dst != NULL ? dst : &dummy, - src, static_cast(n), &state->m_mbstate)); -} - -int -ARCH_STRING::convWCToMB(char* dst, wchar_t src, CArchMBState state) -{ - char dummy[MB_LEN_MAX]; - return static_cast(wcrtomb(dst != NULL ? dst : dummy, - src, &state->m_mbstate)); -} diff --git a/lib/arch/IArchString.h b/lib/arch/IArchString.h index 2d9a507d..703d64b1 100644 --- a/lib/arch/IArchString.h +++ b/lib/arch/IArchString.h @@ -16,23 +16,9 @@ #define IARCHSTRING_H #include "IInterface.h" +#include "BasicTypes.h" #include -/*! -\class CArchMBStateImpl -\brief Internal multibyte conversion data. -An architecture dependent type holding the necessary data for a -multibyte to/from wide character conversion. -*/ -class CArchMBStateImpl; - -/*! -\var CArchMBState -\brief Opaque multibyte conversion state type. -An opaque type representing multibyte conversion state. -*/ -typedef CArchMBStateImpl* CArchMBState; - //! Interface for architecture dependent string operations /*! This interface defines the string operations required by @@ -64,24 +50,13 @@ public: virtual int vsnprintf(char* str, int size, const char* fmt, va_list ap) = 0; - //! Create a new multibyte conversion state - virtual CArchMBState newMBState() = 0; + //! Convert multibyte string to wide character string + virtual int convStringMBToWC(wchar_t*, + const char*, UInt32 n, bool* errors) = 0; - //! Destroy a multibyte conversion state - virtual void closeMBState(CArchMBState) = 0; - - //! Initialize a multibyte conversion state - virtual void initMBState(CArchMBState) = 0; - - //! Test a multibyte conversion state - virtual bool isInitMBState(CArchMBState) = 0; - - //! Convert multibyte to wide character - virtual int convMBToWC(wchar_t*, - const char*, int, CArchMBState) = 0; - - //! Convert wide character to multibyte - virtual int convWCToMB(char*, wchar_t, CArchMBState) = 0; + //! Convert wide character string to multibyte string + virtual int convStringWCToMB(char*, + const wchar_t*, UInt32 n, bool* errors) = 0; //! Return the architecture's native wide character encoding virtual EWideCharEncoding diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am index d1a43f04..8a45369d 100644 --- a/lib/arch/Makefile.am +++ b/lib/arch/Makefile.am @@ -86,8 +86,6 @@ WIN32_SOURCE_FILES = \ EXTRA_DIST = \ arch.dsp \ CMultibyte.cpp \ - CMultibyteEmu.cpp \ - CMultibyteOS.cpp \ vsnprintf.cpp \ $(UNIX_SOURCE_FILES) \ $(WIN32_SOURCE_FILES) \ diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp index e77650b4..ad172bc0 100644 --- a/lib/arch/arch.dsp +++ b/lib/arch/arch.dsp @@ -140,16 +140,6 @@ SOURCE=.\CMultibyte.cpp # End Source File # Begin Source File -SOURCE=.\CMultibyteEmu.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\CMultibyteOS.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - SOURCE=.\vsnprintf.cpp # PROP Exclude_From_Build 1 # End Source File diff --git a/lib/base/CUnicode.cpp b/lib/base/CUnicode.cpp index 9d8fd8d2..6fe10c36 100644 --- a/lib/base/CUnicode.cpp +++ b/lib/base/CUnicode.cpp @@ -220,57 +220,15 @@ CUnicode::UTF8ToText(const CString& src, bool* errors) UInt32 size; wchar_t* tmp = UTF8ToWideChar(src, size, errors); - // get length of multibyte string - int mblen; - CArchMBState state = ARCH->newMBState(); - size_t len = 0; - UInt32 n = size; - for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { - mblen = ARCH->convWCToMB(NULL, *scan, state); - if (mblen == -1) { - // unconvertable character - setError(errors); - len += 1; - } - else { - len += mblen; - } - } - - // handle nul terminator - mblen = ARCH->convWCToMB(NULL, L'\0', state); - if (mblen != -1) { - len += mblen; - } - assert(ARCH->isInitMBState(state) != 0); - - // allocate multibyte string - char* mbs = new char[len]; - - // convert to multibyte - char* dst = mbs; - n = size; - for (const wchar_t* scan = tmp; n > 0; ++scan, --n) { - mblen = ARCH->convWCToMB(dst, *scan, state); - if (mblen == -1) { - // unconvertable character - *dst++ = '?'; - } - else { - dst += mblen; - } - } - mblen = ARCH->convWCToMB(dst, L'\0', state); - if (mblen != -1) { - // don't include nul terminator - dst += mblen - 1; - } - CString text(mbs, dst - mbs); + // convert string to multibyte + int len = ARCH->convStringWCToMB(NULL, tmp, size, errors); + char* mbs = new char[len + 1]; + ARCH->convStringWCToMB(mbs, tmp, size, errors); + CString text(mbs, len); // clean up delete[] mbs; delete[] tmp; - ARCH->closeMBState(state); return text; } @@ -325,88 +283,17 @@ CUnicode::textToUTF8(const CString& src, bool* errors) // default to success resetError(errors); - // get length of multibyte string - UInt32 n = src.size(); - size_t len = 0; - CArchMBState state = ARCH->newMBState(); - for (const char* scan = src.c_str(); n > 0; ) { - int mblen = ARCH->convMBToWC(NULL, scan, n, state); - switch (mblen) { - case -2: - // incomplete last character. convert to unknown character. - setError(errors); - len += 1; - n = 0; - break; - - case -1: - // invalid character. count one unknown character and - // start at the next byte. - setError(errors); - len += 1; - scan += 1; - n -= 1; - break; - - case 0: - len += 1; - scan += 1; - n -= 1; - break; - - default: - // normal character - len += 1; - scan += mblen; - n -= mblen; - break; - } - } - ARCH->initMBState(state); - - // allocate wide character string - wchar_t* wcs = new wchar_t[len]; - - // convert multibyte to wide char - n = src.size(); - wchar_t* dst = wcs; - for (const char* scan = src.c_str(); n > 0; ++dst) { - int mblen = ARCH->convMBToWC(dst, scan, n, state); - switch (mblen) { - case -2: - // incomplete character. convert to unknown character. - *dst = (wchar_t)0xfffd; - n = 0; - break; - - case -1: - // invalid character. count one unknown character and - // start at the next byte. - *dst = (wchar_t)0xfffd; - scan += 1; - n -= 1; - break; - - case 0: - *dst = (wchar_t)0x0000; - scan += 1; - n -= 1; - break; - - default: - // normal character - scan += mblen; - n -= mblen; - break; - } - } + // convert string to wide characters + UInt32 n = src.size(); + int len = ARCH->convStringMBToWC(NULL, src.c_str(), n, errors); + wchar_t* wcs = new wchar_t[len + 1]; + ARCH->convStringMBToWC(wcs, src.c_str(), n, errors); // convert to UTF8 CString utf8 = wideCharToUTF8(wcs, len, errors); // clean up delete[] wcs; - ARCH->closeMBState(state); return utf8; } From 4d2d4a2171e77a57127ec1c1585dd449013567a5 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 May 2004 08:54:42 +0000 Subject: [PATCH 642/807] Fixed type cast warnings. --- lib/arch/CMultibyte.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp index e3186190..4f9dc5e1 100644 --- a/lib/arch/CMultibyte.cpp +++ b/lib/arch/CMultibyte.cpp @@ -19,7 +19,7 @@ #include "CArch.h" #include #include -#if HAVE_WCHAR_H +#if HAVE_WCHAR_H || defined(_MSC_VER) # include #elif __APPLE__ // wtf? Darwin puts mbtowc() et al. in stdlib @@ -112,7 +112,7 @@ ARCH_STRING::convStringWCToMB(char* dst, } ARCH->unlockMutex(s_mutex); - return (ssize_t)len; + return len; } int @@ -201,7 +201,7 @@ ARCH_STRING::convStringMBToWC(wchar_t* dst, } ARCH->unlockMutex(s_mutex); - return (ssize_t)len; + return len; } #endif From dcfe49ef4838f523d788f8206759f825e4e01862 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 May 2004 08:56:24 +0000 Subject: [PATCH 643/807] Fixed regression where cursor wasn't locked to screen when a mouse button is down on win32. --- lib/platform/CMSWindowsScreen.cpp | 74 ++++++++++++++++++------------- lib/platform/CMSWindowsScreen.h | 3 ++ 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 0ee9d454..39d40dc1 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -964,8 +964,8 @@ bool CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) { // get which button - bool pressed = false; - const ButtonID button = mapButtonFromEvent(wParam, lParam); + bool pressed = mapPressFromEvent(wParam, lParam); + ButtonID button = mapButtonFromEvent(wParam, lParam); // keep our shadow key state up to date if (button >= kButtonLeft && button <= kButtonExtra0 + 1) { @@ -979,44 +979,17 @@ CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) // ignore message if posted prior to last mark change if (!ignore()) { - switch (wParam) { - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_XBUTTONDOWN: - case WM_LBUTTONDBLCLK: - case WM_MBUTTONDBLCLK: - case WM_RBUTTONDBLCLK: - case WM_XBUTTONDBLCLK: - case WM_NCLBUTTONDOWN: - case WM_NCMBUTTONDOWN: - case WM_NCRBUTTONDOWN: - case WM_NCXBUTTONDOWN: - case WM_NCLBUTTONDBLCLK: - case WM_NCMBUTTONDBLCLK: - case WM_NCRBUTTONDBLCLK: - case WM_NCXBUTTONDBLCLK: + if (pressed) { LOG((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); } - pressed = true; - break; - - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: - case WM_XBUTTONUP: - case WM_NCLBUTTONUP: - case WM_NCMBUTTONUP: - case WM_NCRBUTTONUP: - case WM_NCXBUTTONUP: + } + else { LOG((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); } - pressed = false; - break; } } @@ -1302,6 +1275,43 @@ CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const } } +bool +CMSWindowsScreen::mapPressFromEvent(WPARAM msg, LPARAM) const +{ + switch (msg) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_XBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: + case WM_NCLBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCXBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: + case WM_NCMBUTTONDBLCLK: + case WM_NCRBUTTONDBLCLK: + case WM_NCXBUTTONDBLCLK: + return true; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONUP: + case WM_NCRBUTTONUP: + case WM_NCXBUTTONUP: + return false; + + default: + return false; + } +} + void CMSWindowsScreen::fixKeys() { diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index b0695f76..5dc64f73 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -157,6 +157,9 @@ private: // map a button event to a button ID ButtonID mapButtonFromEvent(WPARAM msg, LPARAM button) const; + // map a button event to a press (true) or release (false) + bool mapPressFromEvent(WPARAM msg, LPARAM button) const; + // fix the key state, synthesizing fake key releases for keys // that aren't down anymore. void fixKeys(); From 165c889c5d8fd9a3f57472964c9f6388944df727 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 May 2004 10:12:06 +0000 Subject: [PATCH 644/807] Fixes to compile on solaris 9 using g++. --- acinclude.m4 | 5 +++-- configure.in | 6 ++---- lib/arch/CArchNetworkBSD.cpp | 13 +++++++++++++ lib/platform/CXWindowsScreenSaver.cpp | 1 + 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 6492e615..d368949b 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -515,12 +515,13 @@ else fi ])dnl ACX_PTHREAD -dnl enable maximum compiler warnings +dnl enable maximum compiler warnings. must ignore unknown pragmas to +dnl build on solaris. dnl we only know how to do this for g++ AC_DEFUN([ACX_CXX_WARNINGS], [ AC_MSG_CHECKING([for C++ compiler warning flags]) if test "$GXX" = "yes"; then - acx_cxx_warnings="-Wall" + acx_cxx_warnings="-Wall -Wno-unknown-pragmas" fi if test -n "$acx_cxx_warnings"; then CXXFLAGS="$CXXFLAGS $acx_cxx_warnings" diff --git a/configure.in b/configure.in index 3be75a98..e6fcbbe6 100644 --- a/configure.in +++ b/configure.in @@ -35,19 +35,17 @@ case $host in *-*-mingw32* | *-*-windows*) acx_host_arch="WIN32" acx_host_winapi="MSWINDOWS" - ARCH_CFLAGS="$ARCH_CFLAGS -DSYSAPI_WIN32=1 -DWINAPI_MSWINDOWS=1" ;; *-*-darwin*) acx_host_arch="UNIX" acx_host_winapi="CARBON" - ARCH_CFLAGS="$ARCH_CFLAGS -DSYSAPI_UNIX=1 -DWINAPI_CARBON=1" ;; *) acx_host_arch="UNIX" acx_host_winapi="XWINDOWS" - ARCH_CFLAGS="$ARCH_CFLAGS -DSYSAPI_UNIX=1 -DWINAPI_XWINDOWS=1" ;; esac +ARCH_CFLAGS="$ARCH_CFLAGS -DSYSAPI_$acx_host_arch=1 -DWINAPI_$acx_host_winapi=1" AM_CONDITIONAL(WIN32, test x$acx_host_arch = xWIN32) AM_CONDITIONAL(UNIX, test x$acx_host_arch = xUNIX) AM_CONDITIONAL(MSWINDOWS, test x$acx_host_winapi = xMSWINDOWS) @@ -85,7 +83,7 @@ ACX_CHECK_INET_ATON dnl checks for header files AC_HEADER_STDC -AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h wchar.h]) +AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h wchar.h alloca.h]) AC_CHECK_HEADERS([sys/socket.h sys/select.h]) AC_CHECK_HEADERS([istream ostream sstream]) AC_HEADER_TIME diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index d8845801..27be0fea 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -34,6 +34,9 @@ #if HAVE_POLL # include +# if HAVE_ALLOCA_H +# include +# endif #else # if HAVE_SYS_SELECT_H # include @@ -43,6 +46,13 @@ # endif #endif +#if HAVE_ALLOCA_H +# define freea(x_) +#else +# define alloca(x_) malloc(x_) +# define freea(x_) free(x_) +#endif + static const int s_family[] = { PF_UNSPEC, PF_INET @@ -327,8 +337,10 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) if (errno == EINTR) { // interrupted system call ARCH->testCancelThread(); + freea(pfd); return 0; } + freea(pfd); throwError(errno); } @@ -349,6 +361,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) } } + freea(pfd); return n; } diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index 44c7c0b8..0b69f08f 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -27,6 +27,7 @@ #endif #if defined(HAVE_X11_EXTENSIONS_DPMS_H) extern "C" { +# include # include } #endif From 975d888d65345cbab0694d48729af3ae527c8a9b Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 May 2004 11:01:40 +0000 Subject: [PATCH 645/807] Fixes for FreeBSD. --- lib/arch/CArchNetworkBSD.cpp | 3 --- lib/arch/CArchNetworkBSD.h | 3 +++ lib/common/common.h | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 27be0fea..d7ecb33a 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -16,9 +16,6 @@ #include "CArch.h" #include "CArchMultithreadPosix.h" #include "XArchUnix.h" -#if HAVE_SYS_TYPES_H -# include -#endif #if HAVE_UNISTD_H # include #endif diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index b656e14a..671e653e 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -17,6 +17,9 @@ #include "IArchNetwork.h" #include "IArchMultithread.h" +#if HAVE_SYS_TYPES_H +# include +#endif #if HAVE_SYS_SOCKET_H # include #endif diff --git a/lib/common/common.h b/lib/common/common.h index 96910efc..4c0be0ae 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -67,6 +67,12 @@ #define __FP__ #endif +// Need this on FreeBSD to get nanosleep. We can't define this on, say, +// Linux because that unconditionally defines it. +#if defined(__FreeBSD__) +#define _POSIX_VERSION 199506L +#endif + // define NULL #ifndef NULL #define NULL 0 From 320cc754a2b0fb6d83aa430757485f69a85cb6c3 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 May 2004 12:11:28 +0000 Subject: [PATCH 646/807] Better fixes for compiling on FreeBSD and OpenBSD. --- configure.in | 17 ++++++++++++----- lib/arch/CArchNetworkBSD.cpp | 4 ++++ lib/common/common.h | 6 ------ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/configure.in b/configure.in index e6fcbbe6..c4fc8c28 100644 --- a/configure.in +++ b/configure.in @@ -32,15 +32,15 @@ ARCH_LIBS="" ARCH_CFLAGS="" AC_CANONICAL_HOST case $host in - *-*-mingw32* | *-*-windows*) +*-*-mingw32* | *-*-windows*) acx_host_arch="WIN32" acx_host_winapi="MSWINDOWS" ;; - *-*-darwin*) +*-*-darwin*) acx_host_arch="UNIX" acx_host_winapi="CARBON" ;; - *) +*) acx_host_arch="UNIX" acx_host_winapi="XWINDOWS" ;; @@ -66,8 +66,15 @@ ac_ext=cpp dnl check compiler ACX_CHECK_CXX -dnl macros we should do testing with -CXXFLAGS="$CXXFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500" +dnl different platforms have somewhat incompatible requirements for +dnl BSD and Posix macros. +case $host in +*-*-openbsd* | *-*-freebsd*) + ;; +*) + CXXFLAGS="$CXXFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500" + ;; +esac dnl checks for libraries if test x"$acx_host_arch" = xUNIX; then diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index d7ecb33a..d27ab3fd 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -43,6 +43,10 @@ # endif #endif +#if !HAVE_INET_ATON +# include +#endif + #if HAVE_ALLOCA_H # define freea(x_) #else diff --git a/lib/common/common.h b/lib/common/common.h index 4c0be0ae..96910efc 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -67,12 +67,6 @@ #define __FP__ #endif -// Need this on FreeBSD to get nanosleep. We can't define this on, say, -// Linux because that unconditionally defines it. -#if defined(__FreeBSD__) -#define _POSIX_VERSION 199506L -#endif - // define NULL #ifndef NULL #define NULL 0 From 16110acaa2377bc284d3b68bf36d087b1b33ec33 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 May 2004 15:18:59 +0000 Subject: [PATCH 647/807] Added support for a global relative mouse motion option. When true and on a secondary screen and locked to the screen (via scroll lock) mouse motion is sent as motion deltas. When true and scroll lock is toggled off the mouse is warped to the secondary screen's center so the server knows where it is. This option is intended to support games and other programs that repeatedly warp the mouse to the center of the screen. This change adds general and X11 support but not win32. The option name is "relativeMouseMoves". --- lib/client/CClient.cpp | 6 ++++ lib/client/CClient.h | 1 + lib/platform/CXWindowsScreen.cpp | 13 +++++++ lib/platform/CXWindowsScreen.h | 1 + lib/server/CClientProxy.h | 1 + lib/server/CClientProxy1_0.cpp | 6 ++++ lib/server/CClientProxy1_0.h | 1 + lib/server/CClientProxyUnknown.cpp | 5 +++ lib/server/CConfig.cpp | 9 ++++- lib/server/CPrimaryClient.cpp | 6 ++++ lib/server/CPrimaryClient.h | 1 + lib/server/CServer.cpp | 54 +++++++++++++++++++++++++----- lib/server/CServer.h | 8 +++-- lib/server/Makefile.am | 2 ++ lib/synergy/CPlatformScreen.h | 1 + lib/synergy/CScreen.cpp | 7 ++++ lib/synergy/CScreen.h | 7 ++++ lib/synergy/IClient.h | 7 ++++ lib/synergy/IPlatformScreen.h | 1 + lib/synergy/ISecondaryScreen.h | 6 ++++ lib/synergy/OptionTypes.h | 1 + lib/synergy/ProtocolTypes.h | 6 +++- 22 files changed, 138 insertions(+), 12 deletions(-) diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index f1f4ae22..59bbc4fc 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -257,6 +257,12 @@ CClient::mouseMove(SInt32 x, SInt32 y) m_screen->mouseMove(x, y); } +void +CClient::mouseRelativeMove(SInt32 dx, SInt32 dy) +{ + m_screen->mouseRelativeMove(dx, dy); +} + void CClient::mouseWheel(SInt32 delta) { diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 9c49d3cd..5087759b 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -135,6 +135,7 @@ public: virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); virtual void mouseWheel(SInt32 delta); virtual void screensaver(bool activate); virtual void resetOptions(); diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index ffe5a64e..7aee2341 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -550,6 +550,19 @@ CXWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const XFlush(m_display); } +void +CXWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const +{ + // FIXME -- ignore xinerama for now + if (false && m_xinerama && m_xtestIsXineramaUnaware) { +// XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); + } + else { + XTestFakeRelativeMotionEvent(m_display, dx, dy, CurrentTime); + } + XFlush(m_display); +} + void CXWindowsScreen::fakeMouseWheel(SInt32 delta) const { diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index c7be5bb4..d1289dca 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -55,6 +55,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; virtual void fakeMouseWheel(SInt32 delta) const; // IPlatformScreen overrides diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index 9453a203..b8e38fa8 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -96,6 +96,7 @@ public: virtual void mouseDown(ButtonID) = 0; virtual void mouseUp(ButtonID) = 0; virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0; virtual void mouseWheel(SInt32 delta) = 0; virtual void screensaver(bool activate) = 0; virtual void resetOptions() = 0; diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 617e12a2..2bb412ce 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -316,6 +316,12 @@ CClientProxy1_0::mouseMove(SInt32 xAbs, SInt32 yAbs) CProtocolUtil::writef(getStream(), kMsgDMouseMove, xAbs, yAbs); } +void +CClientProxy1_0::mouseRelativeMove(SInt32, SInt32) +{ + // ignore -- not supported in protocol 1.0 +} + void CClientProxy1_0::mouseWheel(SInt32 delta) { diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 503dcba9..4f752085 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -49,6 +49,7 @@ public: virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); virtual void mouseWheel(SInt32 delta); virtual void screensaver(bool activate); virtual void resetOptions(); diff --git a/lib/server/CClientProxyUnknown.cpp b/lib/server/CClientProxyUnknown.cpp index 4c45ba07..806f23b8 100644 --- a/lib/server/CClientProxyUnknown.cpp +++ b/lib/server/CClientProxyUnknown.cpp @@ -15,6 +15,7 @@ #include "CClientProxyUnknown.h" #include "CClientProxy1_0.h" #include "CClientProxy1_1.h" +#include "CClientProxy1_2.h" #include "ProtocolTypes.h" #include "CProtocolUtil.h" #include "XSynergy.h" @@ -214,6 +215,10 @@ CClientProxyUnknown::handleData(const CEvent&, void*) case 1: m_proxy = new CClientProxy1_1(name, m_stream); break; + + case 2: + m_proxy = new CClientProxy1_2(name, m_stream); + break; } } diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index a4893cc8..03e0eb2d 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -629,6 +629,9 @@ CConfig::getOptionName(OptionID id) if (id == kOptionXTestXineramaUnaware) { return "xtestIsXineramaUnaware"; } + if (id == kOptionRelativeMouseMoves) { + return "relativeMouseMoves"; + } return NULL; } @@ -638,7 +641,8 @@ CConfig::getOptionValue(OptionID id, OptionValue value) if (id == kOptionHalfDuplexCapsLock || id == kOptionHalfDuplexNumLock || id == kOptionScreenSaverSync || - id == kOptionXTestXineramaUnaware) { + id == kOptionXTestXineramaUnaware || + id == kOptionRelativeMouseMoves) { return (value != 0) ? "true" : "false"; } if (id == kOptionModifierMapForShift || @@ -778,6 +782,9 @@ CConfig::readSectionOptions(std::istream& s) else if (name == "screenSaverSync") { addOption("", kOptionScreenSaverSync, parseBoolean(value)); } + else if (name == "relativeMouseMoves") { + addOption("", kOptionRelativeMouseMoves, parseBoolean(value)); + } else { throw XConfigRead("unknown argument"); } diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index a8e21c3c..7ec681bc 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -185,6 +185,12 @@ CPrimaryClient::mouseMove(SInt32 x, SInt32 y) m_screen->warpCursor(x, y); } +void +CPrimaryClient::mouseRelativeMove(SInt32, SInt32) +{ + // ignore +} + void CPrimaryClient::mouseWheel(SInt32) { diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 985fc4a3..2006508c 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -102,6 +102,7 @@ public: virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); virtual void mouseWheel(SInt32 delta); virtual void screensaver(bool activate); virtual void resetOptions(); diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 27a8651a..fce2584a 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -53,7 +53,8 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_switchTwoTapDelay(0.0), m_switchTwoTapEngaged(false), m_switchTwoTapArmed(false), - m_switchTwoTapZone(3) + m_switchTwoTapZone(3), + m_relativeMoves(false) { // must have a primary client and it must have a canonical name assert(m_primaryClient != NULL); @@ -283,6 +284,9 @@ CServer::onCommandKey(KeyID id, KeyModifierMask /*mask*/, bool /*down*/) { if (id == kKeyScrollLock) { m_primaryClient->reconfigure(getActivePrimarySides()); + if (!isLockedToScreenServer()) { + stopRelativeMoves(); + } } return false; } @@ -322,13 +326,7 @@ bool CServer::isLockedToScreenServer() const { // locked if scroll-lock is toggled on - if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) { - LOG((CLOG_DEBUG "locked by ScrollLock")); - return true; - } - - // not locked - return false; + return ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0); } bool @@ -336,6 +334,7 @@ CServer::isLockedToScreen() const { // locked if we say we're locked if (isLockedToScreenServer()) { + LOG((CLOG_DEBUG "locked by ScrollLock")); return true; } @@ -841,6 +840,24 @@ CServer::isSwitchWaitStarted() const return (m_switchWaitTimer != NULL); } +void +CServer::stopRelativeMoves() +{ + if (m_relativeMoves && m_active != m_primaryClient) { + // warp to the center of the active client so we know where we are + SInt32 ax, ay, aw, ah; + m_active->getShape(ax, ay, aw, ah); + m_x = ax + (aw >> 1); + m_y = ay + (ah >> 1); + m_xDelta = 0; + m_yDelta = 0; + m_xDelta2 = 0; + m_yDelta2 = 0; + LOG((CLOG_DEBUG2 "synchronize move on %s by %d,%d", getName(m_active).c_str(), m_x, m_y)); + m_active->mouseMove(m_x, m_y); + } +} + void CServer::sendOptions(IClient* client) const { @@ -883,6 +900,7 @@ CServer::processOptions() return; } + bool newRelativeMoves = m_relativeMoves; for (CConfig::CScreenOptions::const_iterator index = options->begin(); index != options->end(); ++index) { const OptionID id = index->first; @@ -901,7 +919,15 @@ CServer::processOptions() } stopSwitchTwoTap(); } + else if (id == kOptionRelativeMouseMoves) { + newRelativeMoves = (value != 0); + } } + + if (m_relativeMoves && !newRelativeMoves) { + stopRelativeMoves(); + } + m_relativeMoves = newRelativeMoves; } void @@ -1339,6 +1365,18 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) return; } + // if doing relative motion on secondary screens and we're locked + // to the screen (which activates relative moves) then send a + // relative mouse motion. when we're doing this we pretend as if + // the mouse isn't actually moving because we're expecting some + // program on the secondary screen to warp the mouse on us, so we + // have no idea where it really is. + if (m_relativeMoves && isLockedToScreenServer()) { + LOG((CLOG_DEBUG2 "relative move on %s by %d,%d", getName(m_active).c_str(), dx, dy)); + m_active->mouseRelativeMove(dx, dy); + return; + } + // save old position const SInt32 xOld = m_x; const SInt32 yOld = m_y; diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 0e28eee7..3272d837 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -26,8 +26,6 @@ #include "stdset.h" #include "stdvector.h" -class CClientProxy; -class CClientProxyUnknown; class CEventQueueTimer; class CPrimaryClient; class IClient; @@ -183,6 +181,9 @@ private: // returns true iff the delay switch timer is started bool isSwitchWaitStarted() const; + // stop relative mouse moves + void stopRelativeMoves(); + // send screen options to \c client void sendOptions(IClient* client) const; @@ -312,6 +313,9 @@ private: bool m_switchTwoTapArmed; SInt32 m_switchTwoTapZone; + // relative mouse move option + bool m_relativeMoves; + static CEvent::Type s_errorEvent; static CEvent::Type s_disconnectedEvent; }; diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index 7ffb76b4..90f3d6e1 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -27,6 +27,7 @@ libserver_a_SOURCES = \ CClientProxy.cpp \ CClientProxy1_0.cpp \ CClientProxy1_1.cpp \ + CClientProxy1_2.cpp \ CClientProxyUnknown.cpp \ CConfig.cpp \ CPrimaryClient.cpp \ @@ -35,6 +36,7 @@ libserver_a_SOURCES = \ CClientProxy.h \ CClientProxy1_0.h \ CClientProxy1_1.h \ + CClientProxy1_2.h \ CClientProxyUnknown.h \ CConfig.h \ CPrimaryClient.h \ diff --git a/lib/synergy/CPlatformScreen.h b/lib/synergy/CPlatformScreen.h index b5fcde01..d5bb7fbb 100644 --- a/lib/synergy/CPlatformScreen.h +++ b/lib/synergy/CPlatformScreen.h @@ -45,6 +45,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; virtual void fakeMouseWheel(SInt32 delta) const = 0; // IKeyState overrides diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index 787e72ba..67140498 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -222,6 +222,13 @@ CScreen::mouseMove(SInt32 x, SInt32 y) m_screen->fakeMouseMove(x, y); } +void +CScreen::mouseRelativeMove(SInt32 dx, SInt32 dy) +{ + assert(!m_isPrimary); + m_screen->fakeMouseRelativeMove(dx, dy); +} + void CScreen::mouseWheel(SInt32 delta) { diff --git a/lib/synergy/CScreen.h b/lib/synergy/CScreen.h index ae605d9b..01b10417 100644 --- a/lib/synergy/CScreen.h +++ b/lib/synergy/CScreen.h @@ -146,6 +146,13 @@ public: */ void mouseMove(SInt32 xAbs, SInt32 yAbs); + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion by the relative + amount \c xRel,yRel. + */ + void mouseRelativeMove(SInt32 xRel, SInt32 yRel); + //! Notify of mouse wheel motion /*! Synthesize mouse events to generate mouse wheel motion of \c delta. diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index b4ef2eca..7679d104 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -120,6 +120,13 @@ public: */ virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + //! Notify of mouse motion + /*! + Synthesize mouse events to generate mouse motion by the relative + amount \c xRel,yRel. + */ + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0; + //! Notify of mouse wheel motion /*! Synthesize mouse events to generate mouse wheel motion of \c delta. diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index 13bd0a31..8a5da677 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -153,6 +153,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; virtual void fakeMouseWheel(SInt32 delta) const = 0; // IKeyState overrides diff --git a/lib/synergy/ISecondaryScreen.h b/lib/synergy/ISecondaryScreen.h index 1311e7af..092cd805 100644 --- a/lib/synergy/ISecondaryScreen.h +++ b/lib/synergy/ISecondaryScreen.h @@ -40,6 +40,12 @@ public: */ virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; + //! Fake mouse move + /*! + Synthesize a mouse move to the relative coordinates \c dx,dy. + */ + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; + //! Fake mouse wheel /*! Synthesize a mouse wheel event of amount \c delta. diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index aaefe7fa..31504993 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -55,6 +55,7 @@ static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); +static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT"); //@} #undef OPTION_CODE diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index c1162c2d..4fd6d475 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -21,7 +21,7 @@ // 1.0: initial protocol // 1.1: adds KeyCode to key press, release, and repeat static const SInt16 kProtocolMajorVersion = 1; -static const SInt16 kProtocolMinorVersion = 1; +static const SInt16 kProtocolMinorVersion = 2; // default contact port number static const UInt16 kDefaultPort = 24800; @@ -180,6 +180,10 @@ static const char kMsgDMouseUp[] = "DMUP%1i"; // $1 = x, $2 = y. x,y are absolute screen coordinates. static const char kMsgDMouseMove[] = "DMMV%2i%2i"; +// relative mouse move: primary -> secondary +// $1 = dx, $2 = dy. dx,dy are motion deltas. +static const char kMsgDMouseRelMove[] = "DMRM%2i%2i"; + // mouse button pressed: primary -> secondary // $1 = delta. the delta should be +120 for one tick forward (away // from the user) and -120 for one tick backward (toward the user). From 3758d9d282aa40a08a3002cf617888bb8c33160f Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 May 2004 15:19:53 +0000 Subject: [PATCH 648/807] Added files forgotten in previous checkin. --- lib/server/CClientProxy1_2.cpp | 39 ++++++++++++++++++++++++++++++++++ lib/server/CClientProxy1_2.h | 30 ++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 lib/server/CClientProxy1_2.cpp create mode 100644 lib/server/CClientProxy1_2.h diff --git a/lib/server/CClientProxy1_2.cpp b/lib/server/CClientProxy1_2.cpp new file mode 100644 index 00000000..29f0a56b --- /dev/null +++ b/lib/server/CClientProxy1_2.cpp @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientProxy1_2.h" +#include "CProtocolUtil.h" +#include "CLog.h" + +// +// CClientProxy1_1 +// + +CClientProxy1_2::CClientProxy1_2(const CString& name, IStream* stream) : + CClientProxy1_1(name, stream) +{ + // do nothing +} + +CClientProxy1_2::~CClientProxy1_2() +{ + // do nothing +} + +void +CClientProxy1_2::mouseRelativeMove(SInt32 xRel, SInt32 yRel) +{ + LOG((CLOG_DEBUG2 "send mouse relative move to \"%s\" %d,%d", getName().c_str(), xRel, yRel)); + CProtocolUtil::writef(getStream(), kMsgDMouseRelMove, xRel, yRel); +} diff --git a/lib/server/CClientProxy1_2.h b/lib/server/CClientProxy1_2.h new file mode 100644 index 00000000..3f8bb0e3 --- /dev/null +++ b/lib/server/CClientProxy1_2.h @@ -0,0 +1,30 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTPROXY1_2_H +#define CCLIENTPROXY1_2_H + +#include "CClientProxy1_1.h" + +//! Proxy for client implementing protocol version 1.2 +class CClientProxy1_2 : public CClientProxy1_1 { +public: + CClientProxy1_2(const CString& name, IStream* adoptedStream); + ~CClientProxy1_2(); + + // IClient overrides + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); +}; + +#endif From fddf7d04a3b67c95a83a538a65ea7de2272e4bdb Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 May 2004 16:10:09 +0000 Subject: [PATCH 649/807] X11 clipboard logging now also prints atom names, not just numbers. --- lib/platform/CXWindowsClipboard.cpp | 30 +++++++++++++++-------------- lib/platform/CXWindowsUtil.cpp | 27 ++++++++++++++++++++++++++ lib/platform/CXWindowsUtil.h | 14 ++++++++++++++ 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 52140624..2379850b 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -110,7 +110,7 @@ CXWindowsClipboard::addRequest(Window owner, Window requestor, // at the given time. bool success = false; if (owner == m_window) { - LOG((CLOG_DEBUG1 "request for clipboard %d, target %d by 0x%08x (property=%d)", m_selection, target, requestor, property)); + LOG((CLOG_DEBUG1 "request for clipboard %d, target %s by 0x%08x (property=%s)", m_selection, CXWindowsUtil::atomToString(m_display, target).c_str(), requestor, CXWindowsUtil::atomToString(m_display, property).c_str())); if (wasOwnedAtTime(time)) { if (target == m_atomMultiple) { // add a multiple request. property may not be None @@ -203,7 +203,7 @@ CXWindowsClipboard::processRequest(Window requestor, // unknown requestor window return false; } - LOG((CLOG_DEBUG1 "received property %d delete from 0x08%x", property, requestor)); + LOG((CLOG_DEBUG1 "received property %s delete from 0x08%x", CXWindowsUtil::atomToString(m_display, property).c_str(), requestor)); // find the property in the known requests. it should be the // first property but we'll check 'em all if we have to. @@ -398,7 +398,7 @@ CXWindowsClipboard::getConverter(Atom target, bool onlyIfNotAdded) const } } if (converter == NULL) { - LOG((CLOG_DEBUG1 " no converter for target %d", target)); + LOG((CLOG_DEBUG1 " no converter for target %s", CXWindowsUtil::atomToString(m_display, target).c_str())); return NULL; } @@ -503,10 +503,12 @@ CXWindowsClipboard::icccmFillCache() data.append(reinterpret_cast(&target), sizeof(target)); } - // try each converter in order (because they're in order of - // preference). const Atom* targets = reinterpret_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); + LOG((CLOG_DEBUG " available targets: %s", CXWindowsUtil::atomsToString(m_display, targets, numTargets).c_str())); + + // try each converter in order (because they're in order of + // preference). for (ConverterList::const_iterator index = m_converters.begin(); index != m_converters.end(); ++index) { IXWindowsClipboardConverter* converter = *index; @@ -532,7 +534,7 @@ CXWindowsClipboard::icccmFillCache() Atom actualTarget; CString targetData; if (!icccmGetSelection(target, &actualTarget, &targetData)) { - LOG((CLOG_DEBUG1 " no data for target %d", target)); + LOG((CLOG_DEBUG1 " no data for target %s", CXWindowsUtil::atomToString(m_display, target).c_str())); continue; } @@ -540,7 +542,7 @@ CXWindowsClipboard::icccmFillCache() IClipboard::EFormat format = converter->getFormat(); m_data[format] = converter->toIClipboard(targetData); m_added[format] = true; - LOG((CLOG_DEBUG " added format %d for target %d", format, target)); + LOG((CLOG_DEBUG " added format %d for target %s (%u %s)", format, CXWindowsUtil::atomToString(m_display, target).c_str(), targetData.size(), targetData.size() == 1 ? "byte" : "bytes")); } } @@ -555,12 +557,12 @@ CXWindowsClipboard::icccmGetSelection(Atom target, CICCCMGetClipboard getter(m_window, m_time, m_atomData); if (!getter.readClipboard(m_display, m_selection, target, actualTarget, data)) { - LOG((CLOG_DEBUG1 "can't get data for selection target %d", target)); + LOG((CLOG_DEBUG1 "can't get data for selection target %s", CXWindowsUtil::atomToString(m_display, target).c_str())); LOGC(getter.m_error, (CLOG_WARN "ICCCM violation by clipboard owner")); return false; } else if (*actualTarget == None) { - LOG((CLOG_DEBUG1 "selection conversion failed for target %d", target)); + LOG((CLOG_DEBUG1 "selection conversion failed for target %s", CXWindowsUtil::atomToString(m_display, target).c_str())); return false; } return true; @@ -770,7 +772,7 @@ CXWindowsClipboard::motifFillCache() Atom actualTarget; CString targetData; if (!motifGetSelection(motifFormat, &actualTarget, &targetData)) { - LOG((CLOG_DEBUG1 " no data for target %d", target)); + LOG((CLOG_DEBUG1 " no data for target %s", CXWindowsUtil::atomToString(m_display, target).c_str())); continue; } @@ -778,7 +780,7 @@ CXWindowsClipboard::motifFillCache() IClipboard::EFormat format = converter->getFormat(); m_data[format] = converter->toIClipboard(targetData); m_added[format] = true; - LOG((CLOG_DEBUG " added format %d for target %d", format, target)); + LOG((CLOG_DEBUG " added format %d for target %s", format, CXWindowsUtil::atomToString(m_display, target).c_str())); } } @@ -1255,7 +1257,7 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, assert(actualTarget != NULL); assert(data != NULL); - LOG((CLOG_DEBUG1 "request selection=%d, target=%d, window=%x", selection, target, m_requestor)); + LOG((CLOG_DEBUG1 "request selection=%s, target=%s, window=%x", CXWindowsUtil::atomToString(display, selection).c_str(), CXWindowsUtil::atomToString(display, target).c_str(), m_requestor)); // save output pointers m_actualTarget = actualTarget; @@ -1423,7 +1425,7 @@ CXWindowsClipboard::CICCCMGetClipboard::processEvent( else if (m_incr) { // if first incremental chunk then save target if (oldSize == 0) { - LOG((CLOG_DEBUG1 " INCR first chunk, target %d", target)); + LOG((CLOG_DEBUG1 " INCR first chunk, target %s", CXWindowsUtil::atomToString(display, target).c_str())); *m_actualTarget = target; } @@ -1445,7 +1447,7 @@ CXWindowsClipboard::CICCCMGetClipboard::processEvent( // not incremental; save the target. else { - LOG((CLOG_DEBUG1 " target %d", target)); + LOG((CLOG_DEBUG1 " target %s", CXWindowsUtil::atomToString(display, target).c_str())); *m_actualTarget = target; m_done = true; } diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 066085ab..f18d4647 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -15,6 +15,7 @@ #include "CXWindowsUtil.h" #include "CThread.h" #include "CLog.h" +#include "CStringUtil.h" #include #define XK_XKB_KEYS #define XK_LATIN1 @@ -1244,6 +1245,32 @@ CXWindowsUtil::decomposeKeySym(KeySym keysym, KeySyms& decomposed) return true; } +CString +CXWindowsUtil::atomToString(Display* display, Atom atom) +{ + char* name = XGetAtomName(display, atom); + CString msg = CStringUtil::print("%s (%d)", name, (int)atom); + XFree(name); + return msg; +} + +CString +CXWindowsUtil::atomsToString(Display* display, const Atom* atom, UInt32 num) +{ + char** names = new char*[num]; + XGetAtomNames(display, const_cast(atom), (int)num, names); + CString msg; + for (UInt32 i = 0; i < num; ++i) { + msg += CStringUtil::print("%s (%d), ", names[i], (int)atom[i]); + XFree(names[i]); + } + delete[] names; + if (msg.size() > 2) { + msg.erase(msg.size() - 2); + } + return msg; +} + Bool CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) { diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h index 179227eb..fb3fb9e7 100644 --- a/lib/platform/CXWindowsUtil.h +++ b/lib/platform/CXWindowsUtil.h @@ -81,6 +81,20 @@ public: */ static bool decomposeKeySym(KeySym keysym, KeySyms& decomposed); + //! Convert Atom to its string + /*! + Converts \p atom to its string representation. + */ + static CString atomToString(Display*, Atom atom); + + //! Convert several Atoms to a string + /*! + Converts each atom in \p atoms to its string representation and + concatenates the results. + */ + static CString atomsToString(Display* display, + const Atom* atom, UInt32 num); + //! X11 error handler /*! This class sets an X error handler in the c'tor and restores the From 0f45face2155f788085bc890b1466d5b07791775 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 May 2004 08:03:49 +0000 Subject: [PATCH 650/807] Forgot to change the client to handle relative moves. --- lib/client/CServerProxy.cpp | 61 ++++++++++++++++++++++++++++++++++--- lib/client/CServerProxy.h | 3 ++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 37c44f10..79e6698e 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -35,6 +35,11 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : m_timer(NULL), m_seqNum(0), m_compressMouse(false), + m_compressMouseRelative(false), + m_xMouse(0), + m_yMouse(0), + m_dxMouse(0), + m_dyMouse(0), m_ignoreMouse(false), m_heartRate(0.0), m_parser(&CServerProxy::parseHandshakeMessage) @@ -189,6 +194,10 @@ CServerProxy::parseMessage(const UInt8* code) mouseMove(); } + else if (memcmp(code, kMsgDMouseRelMove, 4) == 0) { + mouseRelativeMove(); + } + else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { mouseWheel(); } @@ -311,6 +320,12 @@ CServerProxy::flushCompressedMouse() m_compressMouse = false; m_client->mouseMove(m_xMouse, m_yMouse); } + if (m_compressMouseRelative) { + m_compressMouseRelative = false; + m_client->mouseRelativeMove(m_dxMouse, m_dyMouse); + m_dxMouse = 0; + m_dyMouse = 0; + } } void @@ -442,8 +457,11 @@ CServerProxy::enter() LOG((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask)); // discard old compressed mouse motion, if any - m_compressMouse = false; - m_seqNum = seqNum; + m_compressMouse = false; + m_compressMouseRelative = false; + m_dxMouse = 0; + m_dyMouse = 0; + m_seqNum = seqNum; // forward m_client->enter(x, y, seqNum, static_cast(mask), false); @@ -619,9 +637,12 @@ CServerProxy::mouseMove() // if compressing then ignore the motion but record it if (m_compressMouse) { - ignore = true; - m_xMouse = x; - m_yMouse = y; + m_compressMouseRelative = false; + ignore = true; + m_xMouse = x; + m_yMouse = y; + m_dxMouse = 0; + m_dyMouse = 0; } LOG((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); @@ -631,6 +652,36 @@ CServerProxy::mouseMove() } } +void +CServerProxy::mouseRelativeMove() +{ + // parse + bool ignore; + SInt16 dx, dy; + CProtocolUtil::readf(m_stream, kMsgDMouseRelMove + 4, &dx, &dy); + + // note if we should ignore the move + ignore = m_ignoreMouse; + + // compress mouse motion events if more input follows + if (!ignore && !m_compressMouseRelative && m_stream->isReady()) { + m_compressMouseRelative = true; + } + + // if compressing then ignore the motion but record it + if (m_compressMouseRelative) { + ignore = true; + m_dxMouse += dx; + m_dyMouse += dy; + } + LOG((CLOG_DEBUG2 "recv mouse relative move %d,%d", dx, dy)); + + // forward + if (!ignore) { + m_client->mouseRelativeMove(dx, dy); + } +} + void CServerProxy::mouseWheel() { diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index 61e5474b..51263792 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -80,6 +80,7 @@ private: void mouseDown(); void mouseUp(); void mouseMove(); + void mouseRelativeMove(); void mouseWheel(); void screensaver(); void resetOptions(); @@ -97,7 +98,9 @@ private: UInt32 m_seqNum; bool m_compressMouse; + bool m_compressMouseRelative; SInt32 m_xMouse, m_yMouse; + SInt32 m_dxMouse, m_dyMouse; bool m_ignoreMouse; From 9c35a45a2c16b695603bf040b4a6c38a5c7796af Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 May 2004 08:04:15 +0000 Subject: [PATCH 651/807] Added relative mouse move support to win32. --- lib/platform/CMSWindowsDesks.cpp | 76 ++++++++++++++++++++----------- lib/platform/CMSWindowsDesks.h | 7 +++ lib/platform/CMSWindowsScreen.cpp | 6 +++ lib/platform/CMSWindowsScreen.h | 1 + 4 files changed, 63 insertions(+), 27 deletions(-) diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index 2b62ba70..b6319bc7 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -72,6 +72,8 @@ #define SYNERGY_MSG_SYNC_KEYS SYNERGY_HOOK_LAST_MSG + 9 // install; #define SYNERGY_MSG_SCREENSAVER SYNERGY_HOOK_LAST_MSG + 10 +// dx; dy +#define SYNERGY_MSG_FAKE_REL_MOVE SYNERGY_HOOK_LAST_MSG + 11 // // CMSWindowsDesks @@ -293,6 +295,14 @@ CMSWindowsDesks::fakeMouseMove(SInt32 x, SInt32 y) const static_cast(y)); } +void +CMSWindowsDesks::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const +{ + sendMessage(SYNERGY_MSG_FAKE_REL_MOVE, + static_cast(dx), + static_cast(dy)); +} + void CMSWindowsDesks::fakeMouseWheel(SInt32 delta) const { @@ -492,40 +502,47 @@ CMSWindowsDesks::deskMouseMove(SInt32 x, SInt32 y) const // the right place, the effect is disconcerting. // // instead we'll get the cursor's current position and do just a - // relative move from there to the desired position. relative - // moves are subject to cursor acceleration which we don't want. - // so we disable acceleration, do the relative move, then restore - // acceleration. there's a slight chance we'll end up in the - // wrong place if the user moves the cursor using this system's + // relative move from there to the desired position. + else { + POINT pos; + GetCursorPos(&pos); + deskMouseRelativeMove(x - pos.x, y - pos.y); + } +} + +void +CMSWindowsDesks::deskMouseRelativeMove(SInt32 dx, SInt32 dy) const +{ + // relative moves are subject to cursor acceleration which we don't + // want.so we disable acceleration, do the relative move, then + // restore acceleration. there's a slight chance we'll end up in + // the wrong place if the user moves the cursor using this system's // mouse while simultaneously moving the mouse on the server // system. that defeats the purpose of synergy so we'll assume // that won't happen. even if it does, the next mouse move will // correct the position. - else { - // save mouse speed & acceleration - int oldSpeed[4]; - bool accelChanged = - SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && - SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); - // use 1:1 motion - if (accelChanged) { - int newSpeed[4] = { 0, 0, 0, 1 }; - accelChanged = - SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || - SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); - } + // save mouse speed & acceleration + int oldSpeed[4]; + bool accelChanged = + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && + SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); - // move relative to mouse position - POINT pos; - GetCursorPos(&pos); - mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); + // use 1:1 motion + if (accelChanged) { + int newSpeed[4] = { 0, 0, 0, 1 }; + accelChanged = + SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || + SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); + } - // restore mouse speed & acceleration - if (accelChanged) { - SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); - SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); - } + // move relative to mouse position + mouse_event(MOUSEEVENTF_MOVE, dx, dy, 0, 0); + + // restore mouse speed & acceleration + if (accelChanged) { + SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); + SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); } } @@ -693,6 +710,11 @@ CMSWindowsDesks::deskThread(void* vdesk) static_cast(msg.lParam)); break; + case SYNERGY_MSG_FAKE_REL_MOVE: + deskMouseRelativeMove(static_cast(msg.wParam), + static_cast(msg.lParam)); + break; + case SYNERGY_MSG_FAKE_WHEEL: mouse_event(MOUSEEVENTF_WHEEL, 0, 0, msg.wParam, 0); break; diff --git a/lib/platform/CMSWindowsDesks.h b/lib/platform/CMSWindowsDesks.h index 774fad5d..9b966416 100644 --- a/lib/platform/CMSWindowsDesks.h +++ b/lib/platform/CMSWindowsDesks.h @@ -144,6 +144,12 @@ public: */ void fakeMouseMove(SInt32 x, SInt32 y) const; + //! Fake mouse move + /*! + Synthesize a mouse move to the relative coordinates \c dx,dy. + */ + void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; + //! Fake mouse wheel /*! Synthesize a mouse wheel event of amount \c delta. @@ -176,6 +182,7 @@ private: // message handlers void deskMouseMove(SInt32 x, SInt32 y) const; + void deskMouseRelativeMove(SInt32 dx, SInt32 dy) const; void deskEnter(CDesk* desk); void deskLeave(CDesk* desk, HKL keyLayout); void deskThread(void* vdesk); diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 39d40dc1..5af88523 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -507,6 +507,12 @@ CMSWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const m_desks->fakeMouseMove(x, y); } +void +CMSWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const +{ + m_desks->fakeMouseRelativeMove(dx, dy); +} + void CMSWindowsScreen::fakeMouseWheel(SInt32 delta) const { diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 5dc64f73..5bb3d587 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -75,6 +75,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; virtual void fakeMouseWheel(SInt32 delta) const; // IKeyState overrides From d84e5a26be7d634ba274870351dd7da0021e47bf Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 May 2004 08:04:48 +0000 Subject: [PATCH 652/807] Added win32 clipboard support for images and HTML. Still need X11 support. --- lib/platform/CMSWindowsClipboard.cpp | 4 + .../CMSWindowsClipboardBitmapConverter.cpp | 146 ++++++++++++++++++ .../CMSWindowsClipboardBitmapConverter.h | 35 +++++ .../CMSWindowsClipboardHTMLConverter.cpp | 107 +++++++++++++ .../CMSWindowsClipboardHTMLConverter.h | 44 ++++++ lib/platform/platform.dsp | 16 ++ lib/server/server.dsp | 8 + lib/synergy/IClipboard.h | 2 + 8 files changed, 362 insertions(+) create mode 100644 lib/platform/CMSWindowsClipboardBitmapConverter.cpp create mode 100644 lib/platform/CMSWindowsClipboardBitmapConverter.h create mode 100644 lib/platform/CMSWindowsClipboardHTMLConverter.cpp create mode 100644 lib/platform/CMSWindowsClipboardHTMLConverter.h diff --git a/lib/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp index 5f4c7675..7df9db9f 100644 --- a/lib/platform/CMSWindowsClipboard.cpp +++ b/lib/platform/CMSWindowsClipboard.cpp @@ -15,6 +15,8 @@ #include "CMSWindowsClipboard.h" #include "CMSWindowsClipboardTextConverter.h" #include "CMSWindowsClipboardUTF16Converter.h" +#include "CMSWindowsClipboardBitmapConverter.h" +#include "CMSWindowsClipboardHTMLConverter.h" #include "CLog.h" #include "CArchMiscWindows.h" @@ -35,6 +37,8 @@ CMSWindowsClipboard::CMSWindowsClipboard(HWND window) : // let it do so to avoid text encoding issues. m_converters.push_back(new CMSWindowsClipboardTextConverter); } + m_converters.push_back(new CMSWindowsClipboardBitmapConverter); + m_converters.push_back(new CMSWindowsClipboardHTMLConverter); } CMSWindowsClipboard::~CMSWindowsClipboard() diff --git a/lib/platform/CMSWindowsClipboardBitmapConverter.cpp b/lib/platform/CMSWindowsClipboardBitmapConverter.cpp new file mode 100644 index 00000000..a17c66cf --- /dev/null +++ b/lib/platform/CMSWindowsClipboardBitmapConverter.cpp @@ -0,0 +1,146 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClipboardBitmapConverter.h" +#include "CLog.h" + +// +// CMSWindowsClipboardBitmapConverter +// + +CMSWindowsClipboardBitmapConverter::CMSWindowsClipboardBitmapConverter() +{ + // do nothing +} + +CMSWindowsClipboardBitmapConverter::~CMSWindowsClipboardBitmapConverter() +{ + // do nothing +} + +IClipboard::EFormat +CMSWindowsClipboardBitmapConverter::getFormat() const +{ + return IClipboard::kBitmap; +} + +UINT +CMSWindowsClipboardBitmapConverter::getWin32Format() const +{ + return CF_DIB; +} + +HANDLE +CMSWindowsClipboardBitmapConverter::fromIClipboard(const CString& data) const +{ + // copy to memory handle + HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, data.size()); + if (gData != NULL) { + // get a pointer to the allocated memory + char* dst = (char*)GlobalLock(gData); + if (dst != NULL) { + memcpy(dst, data.data(), data.size()); + GlobalUnlock(gData); + } + else { + GlobalFree(gData); + gData = NULL; + } + } + + return gData; +} + +CString +CMSWindowsClipboardBitmapConverter::toIClipboard(HANDLE data) const +{ + // get datator + const char* src = (const char*)GlobalLock(data); + if (src == NULL) { + return CString(); + } + UInt32 srcSize = (UInt32)GlobalSize(data); + + // check image type + const BITMAPINFO* bitmap = reinterpret_cast(src); + LOG((CLOG_INFO "bitmap: %dx%d %d", bitmap->bmiHeader.biWidth, bitmap->bmiHeader.biHeight, (int)bitmap->bmiHeader.biBitCount)); + if (bitmap->bmiHeader.biPlanes == 1 && + (bitmap->bmiHeader.biBitCount == 24 || + bitmap->bmiHeader.biBitCount == 32) && + bitmap->bmiHeader.biCompression == BI_RGB) { + // already in canonical form + CString image(src, srcSize); + GlobalUnlock(data); + return image; + } + + // create a destination DIB section + LOG((CLOG_INFO "convert image from: depth=%d comp=%d", bitmap->bmiHeader.biBitCount, bitmap->bmiHeader.biCompression)); + void* raw; + BITMAPINFOHEADER info; + LONG w = bitmap->bmiHeader.biWidth; + LONG h = bitmap->bmiHeader.biHeight; + info.biSize = sizeof(BITMAPINFOHEADER); + info.biWidth = w; + info.biHeight = h; + info.biPlanes = 1; + info.biBitCount = 32; + info.biCompression = BI_RGB; + info.biSizeImage = 0; + info.biXPelsPerMeter = 1000; + info.biYPelsPerMeter = 1000; + info.biClrUsed = 0; + info.biClrImportant = 0; + HDC dc = GetDC(NULL); + HBITMAP dst = CreateDIBSection(dc, (BITMAPINFO*)&info, + DIB_RGB_COLORS, &raw, NULL, 0); + + // find the start of the pixel data + const char* srcBits = (const char*)bitmap + bitmap->bmiHeader.biSize; + if (bitmap->bmiHeader.biBitCount >= 16) { + if (bitmap->bmiHeader.biCompression == BI_BITFIELDS && + (bitmap->bmiHeader.biBitCount == 16 || + bitmap->bmiHeader.biBitCount == 32)) { + srcBits += 3 * sizeof(DWORD); + } + } + else if (bitmap->bmiHeader.biClrUsed != 0) { + srcBits += bitmap->bmiHeader.biClrUsed * sizeof(RGBQUAD); + } + else { + srcBits += (1 << bitmap->bmiHeader.biBitCount) * sizeof(RGBQUAD); + } + + // copy source image to destination image + HDC dstDC = CreateCompatibleDC(dc); + HGDIOBJ oldBitmap = SelectObject(dstDC, dst); + SetDIBitsToDevice(dstDC, 0, 0, w, h, 0, 0, 0, h, + srcBits, bitmap, DIB_RGB_COLORS); + SelectObject(dstDC, oldBitmap); + DeleteDC(dstDC); + GdiFlush(); + + // extract data + CString image((const char*)&info, info.biSize); + image.append((const char*)raw, 4 * w * h); + + // clean up GDI + DeleteObject(dst); + ReleaseDC(NULL, dc); + + // release handle + GlobalUnlock(data); + + return image; +} diff --git a/lib/platform/CMSWindowsClipboardBitmapConverter.h b/lib/platform/CMSWindowsClipboardBitmapConverter.h new file mode 100644 index 00000000..6ddd7ce8 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardBitmapConverter.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIPBOARDBITMAPCONVERTER_H +#define CMSWINDOWSCLIPBOARDBITMAPCONVERTER_H + +#include "CMSWindowsClipboard.h" + +//! Convert to/from some text encoding +class CMSWindowsClipboardBitmapConverter : + public IMSWindowsClipboardConverter { +public: + CMSWindowsClipboardBitmapConverter(); + virtual ~CMSWindowsClipboardBitmapConverter(); + + // IMSWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual UINT getWin32Format() const; + virtual HANDLE fromIClipboard(const CString&) const; + virtual CString toIClipboard(HANDLE) const; +}; + +#endif diff --git a/lib/platform/CMSWindowsClipboardHTMLConverter.cpp b/lib/platform/CMSWindowsClipboardHTMLConverter.cpp new file mode 100644 index 00000000..a64a0f78 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardHTMLConverter.cpp @@ -0,0 +1,107 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CMSWindowsClipboardHTMLConverter.h" +#include "CStringUtil.h" + +// +// CMSWindowsClipboardHTMLConverter +// + +CMSWindowsClipboardHTMLConverter::CMSWindowsClipboardHTMLConverter() +{ + m_format = RegisterClipboardFormat("HTML Format"); +} + +CMSWindowsClipboardHTMLConverter::~CMSWindowsClipboardHTMLConverter() +{ + // do nothing +} + +IClipboard::EFormat +CMSWindowsClipboardHTMLConverter::getFormat() const +{ + return IClipboard::kHTML; +} + +UINT +CMSWindowsClipboardHTMLConverter::getWin32Format() const +{ + return m_format; +} + +CString +CMSWindowsClipboardHTMLConverter::doFromIClipboard(const CString& data) const +{ + // prepare to CF_HTML format prefix and suffix + CString prefix("Version:0.9\nStartHTML:-1\nEndHTML:-1\n" + "StartFragment:XXXXXXXXXX\nEndFragment:YYYYYYYYYY\n" + ""); + CString suffix("\n"); + UInt32 start = prefix.size(); + UInt32 end = start + data.size(); + prefix.replace(prefix.find("XXXXXXXXXX"), 10, + CStringUtil::print("%010u", start)); + prefix.replace(prefix.find("YYYYYYYYYY"), 10, + CStringUtil::print("%010u", end)); + + // concatenate + prefix += data; + prefix += suffix; + return prefix; +} + +CString +CMSWindowsClipboardHTMLConverter::doToIClipboard(const CString& data) const +{ + // get fragment start/end args + CString startArg = findArg(data, "StartFragment"); + CString endArg = findArg(data, "EndFragment"); + if (startArg.empty() || endArg.empty()) { + return CString(); + } + + // convert args to integers + SInt32 start = (SInt32)atoi(startArg.c_str()); + SInt32 end = (SInt32)atoi(endArg.c_str()); + if (start <= 0 || end <= 0 || start >= end) { + return CString(); + } + + // extract the fragment + return data.substr(start, end - start); +} + +CString +CMSWindowsClipboardHTMLConverter::findArg( + const CString& data, const CString& name) const +{ + CString::size_type i = data.find(name); + if (i == CString::npos) { + return CString(); + } + i = data.find_first_of(":\r\n", i); + if (i == CString::npos || data[i] != ':') { + return CString(); + } + i = data.find_first_of("0123456789\r\n", i + 1); + if (i == CString::npos || data[i] == '\r' || data[i] == '\n') { + return CString(); + } + CString::size_type j = data.find_first_not_of("0123456789", i); + if (j == CString::npos) { + j = data.size(); + } + return data.substr(i, j - i); +} diff --git a/lib/platform/CMSWindowsClipboardHTMLConverter.h b/lib/platform/CMSWindowsClipboardHTMLConverter.h new file mode 100644 index 00000000..02cd8f88 --- /dev/null +++ b/lib/platform/CMSWindowsClipboardHTMLConverter.h @@ -0,0 +1,44 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CMSWINDOWSCLIPBOARDHTMLCONVERTER_H +#define CMSWINDOWSCLIPBOARDHTMLCONVERTER_H + +#include "CMSWindowsClipboardAnyTextConverter.h" + +//! Convert to/from HTML encoding +class CMSWindowsClipboardHTMLConverter : + public CMSWindowsClipboardAnyTextConverter { +public: + CMSWindowsClipboardHTMLConverter(); + virtual ~CMSWindowsClipboardHTMLConverter(); + + // IMSWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual UINT getWin32Format() const; + +protected: + // CMSWindowsClipboardAnyTextConverter overrides + virtual CString doFromIClipboard(const CString&) const; + virtual CString doToIClipboard(const CString&) const; + +private: + CString findArg(const CString& data, const CString& name) const; + +private: + UINT m_format; +}; + +#endif diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index a5036971..ad302539 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -95,6 +95,14 @@ SOURCE=.\CMSWindowsClipboardAnyTextConverter.cpp # End Source File # Begin Source File +SOURCE=.\CMSWindowsClipboardBitmapConverter.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardHTMLConverter.cpp +# End Source File +# Begin Source File + SOURCE=.\CMSWindowsClipboardTextConverter.cpp # End Source File # Begin Source File @@ -139,6 +147,14 @@ SOURCE=.\CMSWindowsClipboardAnyTextConverter.h # End Source File # Begin Source File +SOURCE=.\CMSWindowsClipboardBitmapConverter.h +# End Source File +# Begin Source File + +SOURCE=.\CMSWindowsClipboardHTMLConverter.h +# End Source File +# Begin Source File + SOURCE=.\CMSWindowsClipboardTextConverter.h # End Source File # Begin Source File diff --git a/lib/server/server.dsp b/lib/server/server.dsp index 1aa729c5..91d0c683 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -103,6 +103,10 @@ SOURCE=.\CClientProxy1_1.cpp # End Source File # Begin Source File +SOURCE=.\CClientProxy1_2.cpp +# End Source File +# Begin Source File + SOURCE=.\CClientProxyUnknown.cpp # End Source File # Begin Source File @@ -139,6 +143,10 @@ SOURCE=.\CClientProxy1_1.h # End Source File # Begin Source File +SOURCE=.\CClientProxy1_2.h +# End Source File +# Begin Source File + SOURCE=.\CClientProxyUnknown.h # End Source File # Begin Source File diff --git a/lib/synergy/IClipboard.h b/lib/synergy/IClipboard.h index c0bfbeb4..1f75909e 100644 --- a/lib/synergy/IClipboard.h +++ b/lib/synergy/IClipboard.h @@ -43,6 +43,8 @@ public: */ enum EFormat { kText, //!< Text format, UTF-8, newline is LF + kBitmap, //!< Bitmap format, BMP 24/32bpp, BI_RGB/BI_BITFIELDS + kHTML, //!< HTML format, HTML fragment, UTF-8, newline is LF kNumFormats //!< The number of clipboard formats }; From d97879b25c6f02aa8026f85651b85983711069ea Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 May 2004 16:00:45 +0000 Subject: [PATCH 653/807] Fixed comment about canonical bitmap clipboard format. --- lib/synergy/IClipboard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/synergy/IClipboard.h b/lib/synergy/IClipboard.h index 1f75909e..a5999a68 100644 --- a/lib/synergy/IClipboard.h +++ b/lib/synergy/IClipboard.h @@ -43,7 +43,7 @@ public: */ enum EFormat { kText, //!< Text format, UTF-8, newline is LF - kBitmap, //!< Bitmap format, BMP 24/32bpp, BI_RGB/BI_BITFIELDS + kBitmap, //!< Bitmap format, BMP 24/32bpp, BI_RGB kHTML, //!< HTML format, HTML fragment, UTF-8, newline is LF kNumFormats //!< The number of clipboard formats }; From 423dd3b7183cf29889879fadb69ae205149cc5f7 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 May 2004 16:01:59 +0000 Subject: [PATCH 654/807] Added support for daemon startup dependencies. Made synergy dependent on NetBT, which I hope is right. --- cmd/launcher/CAutoStart.cpp | 5 +++-- lib/arch/CArch.cpp | 4 +++- lib/arch/CArch.h | 1 + lib/arch/CArchDaemonNone.cpp | 1 + lib/arch/CArchDaemonNone.h | 1 + lib/arch/CArchDaemonWindows.cpp | 3 ++- lib/arch/CArchDaemonWindows.h | 1 + lib/arch/IArchDaemon.h | 7 +++++-- 8 files changed, 17 insertions(+), 6 deletions(-) diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp index 66fb3fd0..9a6cc4a1 100644 --- a/cmd/launcher/CAutoStart.cpp +++ b/cmd/launcher/CAutoStart.cpp @@ -199,11 +199,12 @@ CAutoStart::onInstall(bool allUsers) // clear error message m_errorMessage = ""; - // install + // install. depend on NetBT (NetBIOS over TCP/IP). try { ARCH->installDaemon(m_name.c_str(), m_isServer ? SERVER_DAEMON_INFO : CLIENT_DAEMON_INFO, - appPath.c_str(), m_cmdLine.c_str(), allUsers); + appPath.c_str(), m_cmdLine.c_str(), + "NetBT\0", allUsers); askOkay(m_hwnd, getString(IDS_INSTALL_TITLE), getString(allUsers ? IDS_INSTALLED_SYSTEM : diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index a0cb5d34..70b52236 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -178,9 +178,11 @@ CArch::installDaemon(const char* name, const char* description, const char* pathname, const char* commandLine, + const char* dependencies, bool allUsers) { - m_daemon->installDaemon(name, description, pathname, commandLine, allUsers); + m_daemon->installDaemon(name, description, pathname, + commandLine, dependencies, allUsers); } void diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index 07e91097..d7940955 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -80,6 +80,7 @@ public: const char* description, const char* pathname, const char* commandLine, + const char* dependencies, bool allUsers); virtual void uninstallDaemon(const char* name, bool allUsers); virtual int daemonize(const char* name, DaemonFunc func); diff --git a/lib/arch/CArchDaemonNone.cpp b/lib/arch/CArchDaemonNone.cpp index 35a58236..f4d25ae1 100644 --- a/lib/arch/CArchDaemonNone.cpp +++ b/lib/arch/CArchDaemonNone.cpp @@ -33,6 +33,7 @@ CArchDaemonNone::installDaemon(const char*, const char*, const char*, const char*, + const char*, bool) { // do nothing diff --git a/lib/arch/CArchDaemonNone.h b/lib/arch/CArchDaemonNone.h index 5b540607..1c196c5d 100644 --- a/lib/arch/CArchDaemonNone.h +++ b/lib/arch/CArchDaemonNone.h @@ -36,6 +36,7 @@ public: const char* description, const char* pathname, const char* commandLine, + const char* dependencies, bool allUsers); virtual void uninstallDaemon(const char* name, bool allUsers); virtual int daemonize(const char* name, DaemonFunc func); diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index 7e1d76a3..cd88659e 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -82,6 +82,7 @@ CArchDaemonWindows::installDaemon(const char* name, const char* description, const char* pathname, const char* commandLine, + const char* dependencies, bool allUsers) { // if not for all users then use the user's autostart registry. @@ -130,7 +131,7 @@ CArchDaemonWindows::installDaemon(const char* name, pathname, NULL, NULL, - NULL, + dependencies, NULL, NULL); if (service == NULL) { diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h index 4fa31bc4..ed09fab9 100644 --- a/lib/arch/CArchDaemonWindows.h +++ b/lib/arch/CArchDaemonWindows.h @@ -76,6 +76,7 @@ public: const char* description, const char* pathname, const char* commandLine, + const char* dependencies, bool allUsers); virtual void uninstallDaemon(const char* name, bool allUsers); virtual int daemonize(const char* name, DaemonFunc func); diff --git a/lib/arch/IArchDaemon.h b/lib/arch/IArchDaemon.h index ce04eb43..ba6b049b 100644 --- a/lib/arch/IArchDaemon.h +++ b/lib/arch/IArchDaemon.h @@ -38,13 +38,16 @@ public: \c commandLine should \b not include the name of program as the first argument. If \c allUsers is true then the daemon will be installed to start at boot time, otherwise it will be installed to - start when the current user logs in. Throws an \c XArchDaemon - exception on failure. + start when the current user logs in. If \p dependencies is not NULL + then it's a concatenation of NUL terminated other daemon names + followed by a NUL; the daemon will be configured to startup after + the listed daemons. Throws an \c XArchDaemon exception on failure. */ virtual void installDaemon(const char* name, const char* description, const char* pathname, const char* commandLine, + const char* dependencies, bool allUsers) = 0; //! Uninstall daemon From a1bd77f91abacc67b079fbbdc07c5aff6cf5457e Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 May 2004 16:06:04 +0000 Subject: [PATCH 655/807] Used MouseKeys accessibility function to show the mouse cursor on a secondary screen when there's no physical mouse attached to the system. Kinda flaky when a mouse is attached or detached but seems to work well enough when the device is not attached to start with and not attached while running synergy. --- lib/platform/CMSWindowsScreen.cpp | 102 +++++++++++++++++++++++++++++- lib/platform/CMSWindowsScreen.h | 33 ++++++++++ 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 5af88523..4a65e810 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -106,7 +106,9 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_setMode(NULL), m_keyState(NULL), m_suspend(suspend), - m_resume(resume) + m_resume(resume), + m_hasMouse(GetSystemMetrics(SM_MOUSEPRESENT) != 0), + m_showingMouse(false) { assert(s_instance != NULL); assert(s_screen == NULL); @@ -125,6 +127,7 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, updateScreenShape(); m_class = createWindowClass(); m_window = createWindow(m_class, "Synergy"); + forceShowCursor(); LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "")); LOG((CLOG_DEBUG "window is 0x%08x", m_window)); } @@ -240,6 +243,7 @@ CMSWindowsScreen::disable() m_nextClipboardWindow = NULL; m_isOnScreen = m_isPrimary; + forceShowCursor(); } void @@ -259,6 +263,7 @@ CMSWindowsScreen::enter() // now on screen m_isOnScreen = true; + forceShowCursor(); } bool @@ -292,6 +297,7 @@ CMSWindowsScreen::leave() // now off screen m_isOnScreen = false; + forceShowCursor(); return true; } @@ -525,6 +531,36 @@ CMSWindowsScreen::updateKeys() m_desks->updateKeys(); } +void +CMSWindowsScreen::fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button) +{ + CPlatformScreen::fakeKeyDown(id, mask, button); + updateForceShowCursor(); +} + +void +CMSWindowsScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + CPlatformScreen::fakeKeyRepeat(id, mask, count, button); + updateForceShowCursor(); +} + +void +CMSWindowsScreen::fakeKeyUp(KeyButton button) +{ + CPlatformScreen::fakeKeyUp(button); + updateForceShowCursor(); +} + +void +CMSWindowsScreen::fakeToggle(KeyModifierMask modifier) +{ + CPlatformScreen::fakeToggle(modifier); + updateForceShowCursor(); +} + HINSTANCE CMSWindowsScreen::openHookLibrary(const char* name) { @@ -829,6 +865,16 @@ CMSWindowsScreen::onEvent(HWND, UINT msg, } *result = TRUE; return true; + + case WM_DEVICECHANGE: + forceShowCursor(); + break; + + case WM_SETTINGCHANGE: + if (wParam == SPI_SETMOUSEKEYS) { + forceShowCursor(); + } + break; } return false; @@ -1377,6 +1423,60 @@ CMSWindowsScreen::updateKeysCB(void*) updateButtons(); } +void +CMSWindowsScreen::forceShowCursor() +{ + // check for mouse + m_hasMouse = (GetSystemMetrics(SM_MOUSEPRESENT) != 0); + + // decide if we should show the mouse + bool showMouse = (!m_hasMouse && !m_isPrimary && m_isOnScreen); + + // show/hide the mouse + if (showMouse != m_showingMouse) { + if (showMouse) { + m_oldMouseKeys.cbSize = sizeof(m_oldMouseKeys); + m_gotOldMouseKeys = + (SystemParametersInfo(SPI_GETMOUSEKEYS, + m_oldMouseKeys.cbSize, &m_oldMouseKeys, 0) != 0); + if (m_gotOldMouseKeys) { + m_mouseKeys = m_oldMouseKeys; + m_showingMouse = true; + updateForceShowCursor(); + } + } + else { + if (m_gotOldMouseKeys) { + SystemParametersInfo(SPI_SETMOUSEKEYS, + m_oldMouseKeys.cbSize, + &m_oldMouseKeys, SPIF_SENDCHANGE); + m_showingMouse = false; + } + } + } +} + +void +CMSWindowsScreen::updateForceShowCursor() +{ + DWORD oldFlags = m_mouseKeys.dwFlags; + + // turn on MouseKeys + m_mouseKeys.dwFlags = MKF_AVAILABLE | MKF_MOUSEKEYSON; + + // make sure MouseKeys is active in whatever state the NumLock is + // not currently in. + if ((m_keyState->getActiveModifiers() & KeyModifierNumLock) != 0) { + m_mouseKeys.dwFlags |= MKF_REPLACENUMBERS; + } + + // update MouseKeys + if (oldFlags != m_mouseKeys.dwFlags) { + SystemParametersInfo(SPI_SETMOUSEKEYS, + m_mouseKeys.cbSize, &m_mouseKeys, SPIF_SENDCHANGE); + } +} + LRESULT CALLBACK CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 5bb3d587..6fcb691a 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -80,6 +80,12 @@ public: // IKeyState overrides virtual void updateKeys(); + virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button); + virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button); + virtual void fakeKeyUp(KeyButton button); + virtual void fakeToggle(KeyModifierMask modifier); // IPlatformScreen overrides virtual void enable(); @@ -174,6 +180,16 @@ private: // job to update the key state void updateKeysCB(void*); + // determine whether the mouse is hidden by the system and force + // it to be displayed if user has entered this secondary screen. + void forceShowCursor(); + + // forceShowCursor uses MouseKeys to show the cursor. since we + // don't actually want MouseKeys behavior we have to make sure + // it applies when NumLock is in whatever state it's not in now. + // this method does that. + void updateForceShowCursor(); + // our window proc static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); @@ -250,6 +266,23 @@ private: IJob* m_suspend; IJob* m_resume; + // the system shows the mouse cursor when an internal display count + // is >= 0. this count is maintained per application but there's + // apparently a system wide count added to the application's count. + // this system count is 0 if there's a mouse attached to the system + // and -1 otherwise. the MouseKeys accessibility feature can modify + // this system count by making the system appear to have a mouse. + // + // m_hasMouse is true iff there's a mouse attached to the system or + // MouseKeys is simulating one. we track this so we can force the + // cursor to be displayed when the user has entered this screen. + // m_showingMouse is true when we're doing that. + bool m_hasMouse; + bool m_showingMouse; + bool m_gotOldMouseKeys; + MOUSEKEYS m_mouseKeys; + MOUSEKEYS m_oldMouseKeys; + static CMSWindowsScreen* s_screen; }; From 500362d5c0d0387772fc77ffdc4d5a0162febd63 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 May 2004 16:13:11 +0000 Subject: [PATCH 656/807] Relative mouse motion for OS X. --- lib/platform/COSXScreen.cpp | 45 ++++++++++++++++++++++++++++++++----- lib/platform/COSXScreen.h | 2 ++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index 83175ec8..54fb4b47 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -29,6 +29,7 @@ COSXScreen::COSXScreen(bool isPrimary) : m_isPrimary(isPrimary), m_isOnScreen(m_isPrimary), + m_cursorPosValid(false), m_cursorHidden(false), m_keyState(NULL), m_sequenceNumber(0), @@ -94,8 +95,11 @@ COSXScreen::getCursorPos(SInt32& x, SInt32& y) const { Point mouse; GetGlobalMouse(&mouse); - x = mouse.h; - y = mouse.v; + x = mouse.h; + y = mouse.v; + m_cursorPosValid = true; + m_xCursor = x; + m_yCursor = y; } void @@ -115,8 +119,9 @@ COSXScreen::warpCursor(SInt32 x, SInt32 y) CGWarpMouseCursorPosition(pos); // save new cursor position - m_xCursor = x; - m_yCursor = y; + m_xCursor = x; + m_yCursor = y; + m_cursorPosValid = true; } SInt32 @@ -157,6 +162,10 @@ COSXScreen::fakeMouseButton(ButtonID id, bool press) const // use this API and if we want to support more buttons we have // to recompile. CGPoint pos; + if (!m_cursorPosValid) { + SInt32 x, y; + getCursorPos(x, y); + } pos.x = m_xCursor; pos.y = m_yCursor; CGPostMouseEvent(pos, false, sizeof(m_buttons) / sizeof(m_buttons[0]), @@ -178,8 +187,32 @@ COSXScreen::fakeMouseMove(SInt32 x, SInt32 y) const CGPostMouseEvent(pos, true, 0, m_buttons[0]); // save new cursor position - m_xCursor = x; - m_yCursor = y; + m_xCursor = x; + m_yCursor = y; + m_cursorPosValid = true; +} + +void +COSXScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const +{ + // OS X does not appear to have a fake relative mouse move function. + // simulate it by getting the current mouse position and adding to + // that. this can yield the wrong answer but there's not much else + // we can do. + + // get current position + Point oldPos; + GetGlobalMouse(&oldPos); + + // synthesize event + CGPoint pos; + pos.x = oldPos.h + dx; + pos.y = oldPos.v + dy; + // FIXME -- is it okay to pass no buttons here? + CGPostMouseEvent(pos, true, 0, m_buttons[0]); + + // we now assume we don't know the current cursor position + m_cursorPosValid = false; } void diff --git a/lib/platform/COSXScreen.h b/lib/platform/COSXScreen.h index 0e13c5ba..0bb2d310 100644 --- a/lib/platform/COSXScreen.h +++ b/lib/platform/COSXScreen.h @@ -50,6 +50,7 @@ public: // ISecondaryScreen overrides virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; + virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; virtual void fakeMouseWheel(SInt32 delta) const; // IPlatformScreen overrides @@ -93,6 +94,7 @@ private: // mouse state mutable SInt32 m_xCursor, m_yCursor; + mutable bool m_cursorPosValid; mutable boolean_t m_buttons[5]; bool m_cursorHidden; From 4b212ad7045713581dc7f599da71fe26b1bde7f2 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 2 May 2004 21:31:19 +0000 Subject: [PATCH 657/807] Added image/bmp and text/html support to X11. --- lib/base/CUnicode.cpp | 120 +++++++++-- lib/platform/CXWindowsClipboard.cpp | 5 + .../CXWindowsClipboardAnyBitmapConverter.cpp | 197 ++++++++++++++++++ .../CXWindowsClipboardAnyBitmapConverter.h | 59 ++++++ .../CXWindowsClipboardBMPConverter.cpp | 126 +++++++++++ lib/platform/CXWindowsClipboardBMPConverter.h | 39 ++++ .../CXWindowsClipboardHTMLConverter.cpp | 62 ++++++ .../CXWindowsClipboardHTMLConverter.h | 41 ++++ lib/platform/Makefile.am | 10 + lib/synergy/IClipboard.h | 11 + 10 files changed, 657 insertions(+), 13 deletions(-) create mode 100644 lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp create mode 100644 lib/platform/CXWindowsClipboardAnyBitmapConverter.h create mode 100644 lib/platform/CXWindowsClipboardBMPConverter.cpp create mode 100644 lib/platform/CXWindowsClipboardBMPConverter.h create mode 100644 lib/platform/CXWindowsClipboardHTMLConverter.cpp create mode 100644 lib/platform/CXWindowsClipboardHTMLConverter.h diff --git a/lib/base/CUnicode.cpp b/lib/base/CUnicode.cpp index 6fe10c36..4dcfcd6f 100644 --- a/lib/base/CUnicode.cpp +++ b/lib/base/CUnicode.cpp @@ -23,30 +23,44 @@ inline static UInt16 -decode16(const UInt8* n) +decode16(const UInt8* n, bool byteSwapped) { union x16 { UInt8 n8[2]; UInt16 n16; } c; - c.n8[0] = n[0]; - c.n8[1] = n[1]; + if (byteSwapped) { + c.n8[0] = n[1]; + c.n8[1] = n[0]; + } + else { + c.n8[0] = n[0]; + c.n8[1] = n[1]; + } return c.n16; } inline static UInt32 -decode32(const UInt8* n) +decode32(const UInt8* n, bool byteSwapped) { union x32 { UInt8 n8[4]; UInt32 n32; } c; - c.n8[0] = n[0]; - c.n8[1] = n[1]; - c.n8[2] = n[2]; - c.n8[3] = n[3]; + if (byteSwapped) { + c.n8[0] = n[3]; + c.n8[1] = n[2]; + c.n8[2] = n[1]; + c.n8[3] = n[0]; + } + else { + c.n8[0] = n[0]; + c.n8[1] = n[1]; + c.n8[2] = n[2]; + c.n8[3] = n[3]; + } return c.n32; } @@ -366,9 +380,29 @@ CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors) CString dst; dst.reserve(n); + // check if first character is 0xfffe or 0xfeff + bool byteSwapped = false; + if (n >= 1) { + switch (decode16(data, false)) { + case 0x0000feff: + data += 2; + --n; + break; + + case 0x0000fffe: + byteSwapped = true; + data += 2; + --n; + break; + + default: + break; + } + } + // convert each character for (; n > 0; data += 2, --n) { - UInt32 c = decode16(data); + UInt32 c = decode16(data, byteSwapped); toUTF8(dst, c, errors); } @@ -382,9 +416,29 @@ CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors) CString dst; dst.reserve(n); + // check if first character is 0xfffe or 0xfeff + bool byteSwapped = false; + if (n >= 1) { + switch (decode32(data, false)) { + case 0x0000feff: + data += 4; + --n; + break; + + case 0x0000fffe: + byteSwapped = true; + data += 4; + --n; + break; + + default: + break; + } + } + // convert each character for (; n > 0; data += 4, --n) { - UInt32 c = decode32(data); + UInt32 c = decode32(data, byteSwapped); toUTF8(dst, c, errors); } @@ -398,9 +452,29 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors) CString dst; dst.reserve(n); + // check if first character is 0xfffe or 0xfeff + bool byteSwapped = false; + if (n >= 1) { + switch (decode16(data, false)) { + case 0x0000feff: + data += 2; + --n; + break; + + case 0x0000fffe: + byteSwapped = true; + data += 2; + --n; + break; + + default: + break; + } + } + // convert each character for (; n > 0; data += 2, --n) { - UInt32 c = decode16(data); + UInt32 c = decode16(data, byteSwapped); if (c < 0x0000d800 || c > 0x0000dfff) { toUTF8(dst, c, errors); } @@ -410,7 +484,7 @@ CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors) toUTF8(dst, s_replacement, NULL); } else if (c >= 0x0000d800 && c <= 0x0000dbff) { - UInt32 c2 = decode16(data); + UInt32 c2 = decode16(data, byteSwapped); data += 2; --n; if (c2 < 0x0000dc00 || c2 > 0x0000dfff) { @@ -440,9 +514,29 @@ CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors) CString dst; dst.reserve(n); + // check if first character is 0xfffe or 0xfeff + bool byteSwapped = false; + if (n >= 1) { + switch (decode32(data, false)) { + case 0x0000feff: + data += 4; + --n; + break; + + case 0x0000fffe: + byteSwapped = true; + data += 4; + --n; + break; + + default: + break; + } + } + // convert each character for (; n > 0; data += 4, --n) { - UInt32 c = decode32(data); + UInt32 c = decode32(data, byteSwapped); if (c >= 0x00110000) { setError(errors); c = s_replacement; diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 2379850b..5bbe98c2 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -16,6 +16,8 @@ #include "CXWindowsClipboardTextConverter.h" #include "CXWindowsClipboardUCS2Converter.h" #include "CXWindowsClipboardUTF8Converter.h" +#include "CXWindowsClipboardHTMLConverter.h" +#include "CXWindowsClipboardBMPConverter.h" #include "CXWindowsUtil.h" #include "CThread.h" #include "CLog.h" @@ -68,6 +70,9 @@ CXWindowsClipboard::CXWindowsClipboard(Display* display, } // add converters, most desired first + m_converters.push_back(new CXWindowsClipboardHTMLConverter(m_display, + "text/html")); + m_converters.push_back(new CXWindowsClipboardBMPConverter(m_display)); m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, "text/plain;charset=UTF-8")); m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, diff --git a/lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp b/lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp new file mode 100644 index 00000000..e0d6e40a --- /dev/null +++ b/lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp @@ -0,0 +1,197 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboardAnyBitmapConverter.h" + +// BMP info header structure +struct CBMPInfoHeader { +public: + UInt32 biSize; + SInt32 biWidth; + SInt32 biHeight; + UInt16 biPlanes; + UInt16 biBitCount; + UInt32 biCompression; + UInt32 biSizeImage; + SInt32 biXPelsPerMeter; + SInt32 biYPelsPerMeter; + UInt32 biClrUsed; + UInt32 biClrImportant; +}; + +// BMP is little-endian +static inline +UInt16 +toLE(UInt16 data) +{ + union x16 { + UInt8 n8[2]; + UInt16 n16; + } c; + c.n8[0] = static_cast(data & 0xffu); + c.n8[1] = static_cast((data >> 8) & 0xffu); + return c.n16; +} + +static inline +SInt32 +toLE(SInt32 data) +{ + union x32 { + UInt8 n8[4]; + SInt32 n32; + } c; + c.n8[0] = static_cast(data & 0xffu); + c.n8[1] = static_cast((data >> 8) & 0xffu); + c.n8[2] = static_cast((data >> 16) & 0xffu); + c.n8[3] = static_cast((data >> 24) & 0xffu); + return c.n32; +} + +static inline +UInt32 +toLE(UInt32 data) +{ + union x32 { + UInt8 n8[4]; + UInt32 n32; + } c; + c.n8[0] = static_cast(data & 0xffu); + c.n8[1] = static_cast((data >> 8) & 0xffu); + c.n8[2] = static_cast((data >> 16) & 0xffu); + c.n8[3] = static_cast((data >> 24) & 0xffu); + return c.n32; +} + +static inline +UInt16 +fromLEU16(const UInt8* data) +{ + return static_cast(data[0]) | + (static_cast(data[1]) << 8); +} + +static inline +SInt32 +fromLES32(const UInt8* data) +{ + return static_cast(static_cast(data[0]) | + (static_cast(data[1]) << 8) | + (static_cast(data[2]) << 16) | + (static_cast(data[3]) << 24)); +} + +static inline +UInt32 +fromLEU32(const UInt8* data) +{ + return static_cast(data[0]) | + (static_cast(data[1]) << 8) | + (static_cast(data[2]) << 16) | + (static_cast(data[3]) << 24); +} + + +// +// CXWindowsClipboardAnyBitmapConverter +// + +CXWindowsClipboardAnyBitmapConverter::CXWindowsClipboardAnyBitmapConverter() +{ + // do nothing +} + +CXWindowsClipboardAnyBitmapConverter::~CXWindowsClipboardAnyBitmapConverter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardAnyBitmapConverter::getFormat() const +{ + return IClipboard::kBitmap; +} + +int +CXWindowsClipboardAnyBitmapConverter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardAnyBitmapConverter::fromIClipboard(const CString& bmp) const +{ + // fill BMP info header with native-endian data + CBMPInfoHeader infoHeader; + const UInt8* rawBMPInfoHeader = reinterpret_cast(bmp.data()); + infoHeader.biSize = fromLEU32(rawBMPInfoHeader + 0); + infoHeader.biWidth = fromLES32(rawBMPInfoHeader + 4); + infoHeader.biHeight = fromLES32(rawBMPInfoHeader + 8); + infoHeader.biPlanes = fromLEU16(rawBMPInfoHeader + 12); + infoHeader.biBitCount = fromLEU16(rawBMPInfoHeader + 14); + infoHeader.biCompression = fromLEU32(rawBMPInfoHeader + 16); + infoHeader.biSizeImage = fromLEU32(rawBMPInfoHeader + 20); + infoHeader.biXPelsPerMeter = fromLES32(rawBMPInfoHeader + 24); + infoHeader.biYPelsPerMeter = fromLES32(rawBMPInfoHeader + 28); + infoHeader.biClrUsed = fromLEU32(rawBMPInfoHeader + 32); + infoHeader.biClrImportant = fromLEU32(rawBMPInfoHeader + 36); + + // check that format is acceptable + if (infoHeader.biSize != 40 || + infoHeader.biWidth == 0 || infoHeader.biHeight == 0 || + infoHeader.biPlanes != 0 || infoHeader.biCompression != 0 || + (infoHeader.biBitCount != 24 && infoHeader.biBitCount != 32)) { + return CString(); + } + + // convert to image format + const UInt8* rawBMPPixels = rawBMPInfoHeader + 40; + if (infoHeader.biBitCount == 24) { + return doBGRFromIClipboard(rawBMPPixels, + infoHeader.biWidth, infoHeader.biHeight); + } + else { + return doBGRAFromIClipboard(rawBMPPixels, + infoHeader.biWidth, infoHeader.biHeight); + } +} + +CString +CXWindowsClipboardAnyBitmapConverter::toIClipboard(const CString& image) const +{ + // convert to raw BMP data + UInt32 w, h, depth; + CString rawBMP = doToIClipboard(image, w, h, depth); + if (rawBMP.empty() || w == 0 || h == 0 || (depth != 24 && depth != 32)) { + return CString(); + } + + // fill BMP info header with little-endian data + CBMPInfoHeader infoHeader; + infoHeader.biSize = toLE(40); + infoHeader.biWidth = toLE(w); + infoHeader.biHeight = toLE(h); + infoHeader.biPlanes = toLE(1); + infoHeader.biBitCount = toLE(depth); + infoHeader.biCompression = toLE(0); // BI_RGB + infoHeader.biSizeImage = image.size(); + infoHeader.biXPelsPerMeter = toLE(2834); // 72 dpi + infoHeader.biYPelsPerMeter = toLE(2834); // 72 dpi + infoHeader.biClrUsed = toLE(0); + infoHeader.biClrImportant = toLE(0); + + // construct image + return CString(reinterpret_cast(&infoHeader), + sizeof(infoHeader)) + rawBMP; +} diff --git a/lib/platform/CXWindowsClipboardAnyBitmapConverter.h b/lib/platform/CXWindowsClipboardAnyBitmapConverter.h new file mode 100644 index 00000000..6a153422 --- /dev/null +++ b/lib/platform/CXWindowsClipboardAnyBitmapConverter.h @@ -0,0 +1,59 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARDANYBITMAPCONVERTER_H +#define CXWINDOWSCLIPBOARDANYBITMAPCONVERTER_H + +#include "CXWindowsClipboard.h" + +//! Convert to/from some text encoding +class CXWindowsClipboardAnyBitmapConverter : + public IXWindowsClipboardConverter { +public: + CXWindowsClipboardAnyBitmapConverter(); + virtual ~CXWindowsClipboardAnyBitmapConverter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const = 0; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +protected: + //! Convert from IClipboard format + /*! + Convert raw BGR pixel data to another image format. + */ + virtual CString doBGRFromIClipboard(const UInt8* bgrData, + UInt32 w, UInt32 h) const = 0; + + //! Convert from IClipboard format + /*! + Convert raw BGRA pixel data to another image format. + */ + virtual CString doBGRAFromIClipboard(const UInt8* bgrData, + UInt32 w, UInt32 h) const = 0; + + //! Convert to IClipboard format + /*! + Convert an image into raw BGR or BGRA image data and store the + width, height, and image depth (24 or 32). + */ + virtual CString doToIClipboard(const CString&, + UInt32& w, UInt32& h, UInt32& depth) const = 0; +}; + +#endif diff --git a/lib/platform/CXWindowsClipboardBMPConverter.cpp b/lib/platform/CXWindowsClipboardBMPConverter.cpp new file mode 100644 index 00000000..af5cc59a --- /dev/null +++ b/lib/platform/CXWindowsClipboardBMPConverter.cpp @@ -0,0 +1,126 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboardBMPConverter.h" + +// BMP file header structure +struct CBMPHeader { +public: + UInt16 type; + UInt32 size; + UInt16 reserved1; + UInt16 reserved2; + UInt32 offset; +}; + +// BMP is little-endian +static inline +UInt32 +fromLEU32(const UInt8* data) +{ + return static_cast(data[0]) | + (static_cast(data[1]) << 8) | + (static_cast(data[2]) << 16) | + (static_cast(data[3]) << 24); +} + +static inline +UInt32 +toLE(UInt32 data) +{ + union x32 { + UInt8 n8[4]; + UInt32 n32; + } c; + c.n8[0] = static_cast(data & 0xffu); + c.n8[1] = static_cast((data >> 8) & 0xffu); + c.n8[2] = static_cast((data >> 16) & 0xffu); + c.n8[3] = static_cast((data >> 24) & 0xffu); + return c.n32; +} + +// +// CXWindowsClipboardBMPConverter +// + +CXWindowsClipboardBMPConverter::CXWindowsClipboardBMPConverter( + Display* display) : + m_atom(XInternAtom(display, "image/bmp", False)) +{ + // do nothing +} + +CXWindowsClipboardBMPConverter::~CXWindowsClipboardBMPConverter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardBMPConverter::getFormat() const +{ + return IClipboard::kBitmap; +} + +Atom +CXWindowsClipboardBMPConverter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardBMPConverter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardBMPConverter::fromIClipboard(const CString& bmp) const +{ + // create BMP image + CBMPHeader header; + char* type = reinterpret_cast(&header.type); + type[0] = 'B'; + type[1] = 'M'; + header.size = toLE(14 + bmp.size()); + header.reserved1 = 0; + header.reserved2 = 0; + header.offset = toLE(14 + 40); + return CString(reinterpret_cast(&header), 14) + bmp; +} + +CString +CXWindowsClipboardBMPConverter::toIClipboard(const CString& bmp) const +{ + // make sure data is big enough for a BMP file + if (bmp.size() <= 14 + 40) { + return CString(); + } + + // check BMP file header + const UInt8* rawBMPHeader = reinterpret_cast(bmp.data()); + if (rawBMPHeader[0] != 'B' || rawBMPHeader[1] != 'M') { + return CString(); + } + + // get offset to image data + UInt32 offset = fromLEU32(rawBMPHeader + 10); + + // construct BMP + if (offset == 14 + 40) { + return bmp.substr(14); + } + else { + return bmp.substr(14, 40) + bmp.substr(offset, bmp.size() - offset); + } +} diff --git a/lib/platform/CXWindowsClipboardBMPConverter.h b/lib/platform/CXWindowsClipboardBMPConverter.h new file mode 100644 index 00000000..a7d44549 --- /dev/null +++ b/lib/platform/CXWindowsClipboardBMPConverter.h @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARDBMPCONVERTER_H +#define CXWINDOWSCLIPBOARDBMPCONVERTER_H + +#include "CXWindowsClipboard.h" + +//! Convert to/from some text encoding +class CXWindowsClipboardBMPConverter : + public IXWindowsClipboardConverter { +public: + CXWindowsClipboardBMPConverter(Display* display); + virtual ~CXWindowsClipboardBMPConverter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/lib/platform/CXWindowsClipboardHTMLConverter.cpp b/lib/platform/CXWindowsClipboardHTMLConverter.cpp new file mode 100644 index 00000000..fafca54e --- /dev/null +++ b/lib/platform/CXWindowsClipboardHTMLConverter.cpp @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CXWindowsClipboardHTMLConverter.h" +#include "CUnicode.h" + +// +// CXWindowsClipboardHTMLConverter +// + +CXWindowsClipboardHTMLConverter::CXWindowsClipboardHTMLConverter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardHTMLConverter::~CXWindowsClipboardHTMLConverter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardHTMLConverter::getFormat() const +{ + return IClipboard::kHTML; +} + +Atom +CXWindowsClipboardHTMLConverter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardHTMLConverter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardHTMLConverter::fromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToUTF16(data); +} + +CString +CXWindowsClipboardHTMLConverter::toIClipboard(const CString& data) const +{ + return CUnicode::UTF16ToUTF8(data); +} diff --git a/lib/platform/CXWindowsClipboardHTMLConverter.h b/lib/platform/CXWindowsClipboardHTMLConverter.h new file mode 100644 index 00000000..7f761f20 --- /dev/null +++ b/lib/platform/CXWindowsClipboardHTMLConverter.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CXWINDOWSCLIPBOARDHTMLCONVERTER_H +#define CXWINDOWSCLIPBOARDHTMLCONVERTER_H + +#include "CXWindowsClipboard.h" + +//! Convert to/from HTML encoding +class CXWindowsClipboardHTMLConverter : public IXWindowsClipboardConverter { +public: + /*! + \c name is converted to an atom and that is reported by getAtom(). + */ + CXWindowsClipboardHTMLConverter(Display* display, const char* name); + virtual ~CXWindowsClipboardHTMLConverter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 3361b693..5b5bd13d 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -15,6 +15,9 @@ NULL = XWINDOWS_SOURCE_FILES = \ CXWindowsClipboard.cpp \ + CXWindowsClipboardAnyBitmapConverter.cpp\ + CXWindowsClipboardBMPConverter.cpp \ + CXWindowsClipboardHTMLConverter.cpp \ CXWindowsClipboardTextConverter.cpp \ CXWindowsClipboardUCS2Converter.cpp \ CXWindowsClipboardUTF8Converter.cpp \ @@ -24,6 +27,9 @@ XWINDOWS_SOURCE_FILES = \ CXWindowsScreenSaver.cpp \ CXWindowsUtil.cpp \ CXWindowsClipboard.h \ + CXWindowsClipboardAnyBitmapConverter.h \ + CXWindowsClipboardBMPConverter.h \ + CXWindowsClipboardHTMLConverter.h \ CXWindowsClipboardTextConverter.h \ CXWindowsClipboardUCS2Converter.h \ CXWindowsClipboardUTF8Converter.h \ @@ -36,6 +42,8 @@ XWINDOWS_SOURCE_FILES = \ MSWINDOWS_SOURCE_FILES = \ CMSWindowsClipboard.cpp \ CMSWindowsClipboardAnyTextConverter.cpp \ + CMSWindowsClipboardBitmapConverter.cpp \ + CMSWindowsClipboardHTMLConverter.cpp \ CMSWindowsClipboardTextConverter.cpp \ CMSWindowsClipboardUTF16Converter.cpp \ CMSWindowsDesks.cpp \ @@ -46,6 +54,8 @@ MSWINDOWS_SOURCE_FILES = \ CMSWindowsUtil.cpp \ CMSWindowsClipboard.h \ CMSWindowsClipboardAnyTextConverter.h \ + CMSWindowsClipboardBitmapConverter.h \ + CMSWindowsClipboardHTMLConverter.h \ CMSWindowsClipboardTextConverter.h \ CMSWindowsClipboardUTF16Converter.h \ CMSWindowsDesks.h \ diff --git a/lib/synergy/IClipboard.h b/lib/synergy/IClipboard.h index a5999a68..c883da8a 100644 --- a/lib/synergy/IClipboard.h +++ b/lib/synergy/IClipboard.h @@ -40,6 +40,17 @@ public: via add() and retrieved via get() must be in one of these formats. Platform dependent clipboard subclasses can and should present any suitable formats derivable from these formats. + + \c kText is a text format encoded in UTF-8. Newlines are LF (not + CR or LF/CR). + + \k kBitmap is an image format. The data is a BMP file without the + 14 byte header (i.e. starting at the INFOHEADER) and with the image + data immediately following the 40 byte INFOHEADER. + + \k kHTML is a text format encoded in UTF-8 and containing a valid + HTML fragment (but not necessarily a complete HTML document). + Newlines are LF. */ enum EFormat { kText, //!< Text format, UTF-8, newline is LF From fdf4b2604deac2685d76ab8d7131bbe521f121a6 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 3 May 2004 21:14:01 +0000 Subject: [PATCH 658/807] Fixed X11 BMP and other bitmap conversion. Had data alignment problems. --- .../CXWindowsClipboardAnyBitmapConverter.cpp | 84 ++++++++----------- .../CXWindowsClipboardBMPConverter.cpp | 55 +++++++----- 2 files changed, 71 insertions(+), 68 deletions(-) diff --git a/lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp b/lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp index e0d6e40a..ab6afae2 100644 --- a/lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp +++ b/lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp @@ -31,47 +31,36 @@ public: }; // BMP is little-endian -static inline -UInt16 -toLE(UInt16 data) + +static +void +toLE(UInt8*& dst, UInt16 src) { - union x16 { - UInt8 n8[2]; - UInt16 n16; - } c; - c.n8[0] = static_cast(data & 0xffu); - c.n8[1] = static_cast((data >> 8) & 0xffu); - return c.n16; + dst[0] = static_cast(src & 0xffu); + dst[1] = static_cast((src >> 8) & 0xffu); + dst += 2; } -static inline -SInt32 -toLE(SInt32 data) +static +void +toLE(UInt8*& dst, SInt32 src) { - union x32 { - UInt8 n8[4]; - SInt32 n32; - } c; - c.n8[0] = static_cast(data & 0xffu); - c.n8[1] = static_cast((data >> 8) & 0xffu); - c.n8[2] = static_cast((data >> 16) & 0xffu); - c.n8[3] = static_cast((data >> 24) & 0xffu); - return c.n32; + dst[0] = static_cast(src & 0xffu); + dst[1] = static_cast((src >> 8) & 0xffu); + dst[2] = static_cast((src >> 16) & 0xffu); + dst[3] = static_cast((src >> 24) & 0xffu); + dst += 4; } -static inline -UInt32 -toLE(UInt32 data) +static +void +toLE(UInt8*& dst, UInt32 src) { - union x32 { - UInt8 n8[4]; - UInt32 n32; - } c; - c.n8[0] = static_cast(data & 0xffu); - c.n8[1] = static_cast((data >> 8) & 0xffu); - c.n8[2] = static_cast((data >> 16) & 0xffu); - c.n8[3] = static_cast((data >> 24) & 0xffu); - return c.n32; + dst[0] = static_cast(src & 0xffu); + dst[1] = static_cast((src >> 8) & 0xffu); + dst[2] = static_cast((src >> 16) & 0xffu); + dst[3] = static_cast((src >> 24) & 0xffu); + dst += 4; } static inline @@ -178,20 +167,21 @@ CXWindowsClipboardAnyBitmapConverter::toIClipboard(const CString& image) const } // fill BMP info header with little-endian data - CBMPInfoHeader infoHeader; - infoHeader.biSize = toLE(40); - infoHeader.biWidth = toLE(w); - infoHeader.biHeight = toLE(h); - infoHeader.biPlanes = toLE(1); - infoHeader.biBitCount = toLE(depth); - infoHeader.biCompression = toLE(0); // BI_RGB - infoHeader.biSizeImage = image.size(); - infoHeader.biXPelsPerMeter = toLE(2834); // 72 dpi - infoHeader.biYPelsPerMeter = toLE(2834); // 72 dpi - infoHeader.biClrUsed = toLE(0); - infoHeader.biClrImportant = toLE(0); + UInt8 infoHeader[40]; + UInt8* dst = infoHeader; + toLE(dst, static_cast(40)); + toLE(dst, static_cast(w)); + toLE(dst, static_cast(h)); + toLE(dst, static_cast(1)); + toLE(dst, static_cast(depth)); + toLE(dst, static_cast(0)); // BI_RGB + toLE(dst, static_cast(image.size())); + toLE(dst, static_cast(2834)); // 72 dpi + toLE(dst, static_cast(2834)); // 72 dpi + toLE(dst, static_cast(0)); + toLE(dst, static_cast(0)); // construct image - return CString(reinterpret_cast(&infoHeader), + return CString(reinterpret_cast(infoHeader), sizeof(infoHeader)) + rawBMP; } diff --git a/lib/platform/CXWindowsClipboardBMPConverter.cpp b/lib/platform/CXWindowsClipboardBMPConverter.cpp index af5cc59a..bd3438a2 100644 --- a/lib/platform/CXWindowsClipboardBMPConverter.cpp +++ b/lib/platform/CXWindowsClipboardBMPConverter.cpp @@ -35,19 +35,32 @@ fromLEU32(const UInt8* data) (static_cast(data[3]) << 24); } -static inline -UInt32 -toLE(UInt32 data) +static +void +toLE(UInt8*& dst, char src) { - union x32 { - UInt8 n8[4]; - UInt32 n32; - } c; - c.n8[0] = static_cast(data & 0xffu); - c.n8[1] = static_cast((data >> 8) & 0xffu); - c.n8[2] = static_cast((data >> 16) & 0xffu); - c.n8[3] = static_cast((data >> 24) & 0xffu); - return c.n32; + dst[0] = static_cast(src); + dst += 1; +} + +static +void +toLE(UInt8*& dst, UInt16 src) +{ + dst[0] = static_cast(src & 0xffu); + dst[1] = static_cast((src >> 8) & 0xffu); + dst += 2; +} + +static +void +toLE(UInt8*& dst, UInt32 src) +{ + dst[0] = static_cast(src & 0xffu); + dst[1] = static_cast((src >> 8) & 0xffu); + dst[2] = static_cast((src >> 16) & 0xffu); + dst[3] = static_cast((src >> 24) & 0xffu); + dst += 4; } // @@ -88,15 +101,15 @@ CString CXWindowsClipboardBMPConverter::fromIClipboard(const CString& bmp) const { // create BMP image - CBMPHeader header; - char* type = reinterpret_cast(&header.type); - type[0] = 'B'; - type[1] = 'M'; - header.size = toLE(14 + bmp.size()); - header.reserved1 = 0; - header.reserved2 = 0; - header.offset = toLE(14 + 40); - return CString(reinterpret_cast(&header), 14) + bmp; + UInt8 header[14]; + UInt8* dst = header; + toLE(dst, 'B'); + toLE(dst, 'M'); + toLE(dst, 14 + bmp.size()); + toLE(dst, static_cast(0)); + toLE(dst, static_cast(0)); + toLE(dst, static_cast(14 + 40)); + return CString(reinterpret_cast(header), 14) + bmp; } CString From ab0a693891aa54aea2e0179b1a70eda43f20bbb1 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 4 May 2004 19:37:46 +0000 Subject: [PATCH 659/807] Fixed bug in TCP socket that caused a busy loop in the socket multiplexer. That caused a lock up on windows when quitting the server with a client connected. --- lib/net/CTCPSocket.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 7c9b12ce..ee276ee0 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -334,16 +334,22 @@ CTCPSocket::newJob() { // note -- must have m_mutex locked on entry - if (m_socket == NULL || !(m_readable || m_writable)) { + if (m_socket == NULL) { return NULL; } else if (!m_connected) { assert(!m_readable); + if (!(m_readable || m_writable)) { + return NULL; + } return new TSocketMultiplexerMethodJob( this, &CTCPSocket::serviceConnecting, m_socket, m_readable, m_writable); } else { + if (!(m_readable || (m_writable && (m_outputBuffer.getSize() > 0)))) { + return NULL; + } return new TSocketMultiplexerMethodJob( this, &CTCPSocket::serviceConnected, m_socket, m_readable, From a8c6b5e5ee9c2a64760ec63bb6c0e2e84ffd9206 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 4 May 2004 19:44:51 +0000 Subject: [PATCH 660/807] Configured default mac to build for X windows instead of the incomplete carbon implementation. --- configure.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.in b/configure.in index c4fc8c28..8d19d2d9 100644 --- a/configure.in +++ b/configure.in @@ -38,7 +38,8 @@ case $host in ;; *-*-darwin*) acx_host_arch="UNIX" - acx_host_winapi="CARBON" + acx_host_winapi="XWINDOWS" +# acx_host_winapi="CARBON" ;; *) acx_host_arch="UNIX" From 8aa258ef31bf251823e7f54314dc44a0600553a5 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 4 May 2004 20:45:06 +0000 Subject: [PATCH 661/807] Added GUI for relative mouse moves option on win32. --- cmd/launcher/CGlobalOptions.cpp | 11 +++++++++++ cmd/launcher/launcher.rc | 12 ++++++++---- cmd/launcher/resource.h | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/cmd/launcher/CGlobalOptions.cpp b/cmd/launcher/CGlobalOptions.cpp index 8a44643f..1777863b 100644 --- a/cmd/launcher/CGlobalOptions.cpp +++ b/cmd/launcher/CGlobalOptions.cpp @@ -77,6 +77,8 @@ CGlobalOptions::init(HWND hwnd) setWindowText(child, buffer); child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); setItemChecked(child, true); + child = getItem(hwnd, IDC_GLOBAL_RELATIVE_MOVES); + setItemChecked(child, false); // get the global options const CConfig::CScreenOptions* options = m_config->getOptions(""); @@ -116,6 +118,10 @@ CGlobalOptions::init(HWND hwnd) child = getItem(hwnd, IDC_GLOBAL_SCREENSAVER_SYNC); setItemChecked(child, (value != 0)); } + else if (id == kOptionRelativeMouseMoves) { + child = getItem(hwnd, IDC_GLOBAL_RELATIVE_MOVES); + setItemChecked(child, (value != 0)); + } } } } @@ -180,6 +186,7 @@ CGlobalOptions::save(HWND hwnd) m_config->removeOption("", kOptionScreenSwitchTwoTap); m_config->removeOption("", kOptionHeartbeat); m_config->removeOption("", kOptionScreenSaverSync); + m_config->removeOption("", kOptionRelativeMouseMoves); // add requested options child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); @@ -198,6 +205,10 @@ CGlobalOptions::save(HWND hwnd) if (!isItemChecked(child)) { m_config->addOption("", kOptionScreenSaverSync, 0); } + child = getItem(hwnd, IDC_GLOBAL_RELATIVE_MOVES); + if (isItemChecked(child)) { + m_config->addOption("", kOptionRelativeMouseMoves, 1); + } // save last values m_delayTime = newDelayTime; diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 51df8c83..d14e60f5 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -173,7 +173,7 @@ BEGIN IDC_STATIC,7,43,181,17 END -IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 233 +IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 269 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Options" FONT 8, "MS Sans Serif" @@ -205,8 +205,12 @@ BEGIN IDC_STATIC,7,176,193,8 CONTROL "Synchronize screen savers",IDC_GLOBAL_SCREENSAVER_SYNC, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,192,101,10 - DEFPUSHBUTTON "OK",IDOK,94,212,50,14 - PUSHBUTTON "Cancel",IDCANCEL,150,212,50,14 + LTEXT "Experimental: Relative mouse moves on secondary screens.", + IDC_STATIC,7,213,193,8 + CONTROL "Use relative mouse moves",IDC_GLOBAL_RELATIVE_MOVES, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,229,99,10 + DEFPUSHBUTTON "OK",IDOK,94,248,50,14 + PUSHBUTTON "Cancel",IDCANCEL,150,248,50,14 END IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 133 @@ -274,7 +278,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 200 TOPMARGIN, 7 - BOTTOMMARGIN, 226 + BOTTOMMARGIN, 262 END IDD_ADVANCED_OPTIONS, DIALOG diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index 3044c2d5..e6d90dac 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -100,6 +100,7 @@ #define IDC_GLOBAL_HEARTBEAT_TIME 1046 #define IDC_ADD_MOD_SUPER 1047 #define IDC_GLOBAL_SCREENSAVER_SYNC 1047 +#define IDC_GLOBAL_RELATIVE_MOVES 1048 #define IDC_ADVANCED_DEFAULTS 1049 // Next default values for new objects From b876f8b98e583098fd362fa2a98c7647f8d50142 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 4 May 2004 20:53:41 +0000 Subject: [PATCH 662/807] Updated documentation files. --- ChangeLog | 384 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ FAQ | 16 +++ INSTALL | 8 ++ NEWS | 17 +++ TODO | 13 +- 5 files changed, 431 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 03f063af..38a96f79 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,387 @@ +2004/05/04 20:45:06 crs +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added GUI for relative mouse moves option on win32. + +---------- +2004/05/04 19:44:51 crs +configure.in + +Configured default mac to build for X windows instead of the incomplete +carbon implementation. + +---------- +2004/05/04 19:37:46 crs +lib/net/CTCPSocket.cpp + +Fixed bug in TCP socket that caused a busy loop in the socket +multiplexer. That caused a lock up on windows when quitting +the server with a client connected. + +---------- +2004/05/03 21:14:01 crs +lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp +lib/platform/CXWindowsClipboardBMPConverter.cpp + +Fixed X11 BMP and other bitmap conversion. Had data alignment +problems. + +---------- +2004/05/02 21:31:19 crs +lib/base/CUnicode.cpp +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp +lib/platform/CXWindowsClipboardAnyBitmapConverter.h +lib/platform/CXWindowsClipboardBMPConverter.cpp +lib/platform/CXWindowsClipboardBMPConverter.h +lib/platform/CXWindowsClipboardHTMLConverter.cpp +lib/platform/CXWindowsClipboardHTMLConverter.h +lib/platform/Makefile.am +lib/synergy/IClipboard.h + +Added image/bmp and text/html support to X11. + +---------- +2004/05/02 16:13:11 crs +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Relative mouse motion for OS X. + +---------- +2004/05/02 16:06:04 crs +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h + +Used MouseKeys accessibility function to show the mouse cursor +on a secondary screen when there's no physical mouse attached to +the system. Kinda flaky when a mouse is attached or detached but +seems to work well enough when the device is not attached to start +with and not attached while running synergy. + +---------- +2004/05/02 16:01:59 crs +cmd/launcher/CAutoStart.cpp +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchDaemonNone.cpp +lib/arch/CArchDaemonNone.h +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchDaemonWindows.h +lib/arch/IArchDaemon.h + +Added support for daemon startup dependencies. Made synergy +dependent on NetBT, which I hope is right. + +---------- +2004/05/02 16:00:45 crs +lib/synergy/IClipboard.h + +Fixed comment about canonical bitmap clipboard format. + +---------- +2004/05/02 08:04:48 crs +lib/platform/CMSWindowsClipboard.cpp +lib/platform/CMSWindowsClipboardBitmapConverter.cpp +lib/platform/CMSWindowsClipboardBitmapConverter.h +lib/platform/CMSWindowsClipboardHTMLConverter.cpp +lib/platform/CMSWindowsClipboardHTMLConverter.h +lib/platform/platform.dsp +lib/server/server.dsp +lib/synergy/IClipboard.h + +Added win32 clipboard support for images and HTML. Still need X11 +support. + +---------- +2004/05/02 08:04:15 crs +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h + +Added relative mouse move support to win32. + +---------- +2004/05/02 08:03:49 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h + +Forgot to change the client to handle relative moves. + +---------- +2004/05/01 16:10:09 crs +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h + +X11 clipboard logging now also prints atom names, not just numbers. + +---------- +2004/05/01 15:19:53 crs +lib/server/CClientProxy1_2.cpp +lib/server/CClientProxy1_2.h + +Added files forgotten in previous checkin. + +---------- +2004/05/01 15:18:59 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxyUnknown.cpp +lib/server/CConfig.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/Makefile.am +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IClient.h +lib/synergy/IPlatformScreen.h +lib/synergy/ISecondaryScreen.h +lib/synergy/OptionTypes.h +lib/synergy/ProtocolTypes.h + +Added support for a global relative mouse motion option. When true +and on a secondary screen and locked to the screen (via scroll lock) +mouse motion is sent as motion deltas. When true and scroll lock +is toggled off the mouse is warped to the secondary screen's center +so the server knows where it is. This option is intended to support +games and other programs that repeatedly warp the mouse to the center +of the screen. This change adds general and X11 support but not +win32. The option name is "relativeMouseMoves". + +---------- +2004/05/01 12:11:28 crs +configure.in +lib/arch/CArchNetworkBSD.cpp +lib/common/common.h + +Better fixes for compiling on FreeBSD and OpenBSD. + +---------- +2004/05/01 11:01:40 crs +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/common/common.h + +Fixes for FreeBSD. + +---------- +2004/05/01 10:12:06 crs +acinclude.m4 +configure.in +lib/arch/CArchNetworkBSD.cpp +lib/platform/CXWindowsScreenSaver.cpp + +Fixes to compile on solaris 9 using g++. + +---------- +2004/05/01 08:56:24 crs +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h + +Fixed regression where cursor wasn't locked to screen when a mouse +button is down on win32. + +---------- +2004/05/01 08:54:42 crs +lib/arch/CMultibyte.cpp + +Fixed type cast warnings. + +---------- +2004/04/13 19:39:04 crs +configure.in +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchStringUnix.cpp +lib/arch/CArchStringUnix.h +lib/arch/CArchStringWindows.cpp +lib/arch/CArchStringWindows.h +lib/arch/CMultibyte.cpp +lib/arch/CMultibyteEmu.cpp +lib/arch/CMultibyteOS.cpp +lib/arch/IArchString.h +lib/arch/Makefile.am +lib/arch/arch.dsp +lib/base/CUnicode.cpp + +Removed use of mbrtowc, wcrtomb, and mbsinit. Many platforms +didn't support them and the emulated versions were just as good +except for a performance problem with excessive locking and +unlocking of a mutex. So this also changes IArchString to +provide string rather than character conversion so we can lock +the mutex once per string rather than once per character. + +---------- +2004/04/11 20:01:18 crs +cmd/launcher/Makefile.am + +Oops, broke build in launcher on non-win32 platforms. + +---------- +2004/04/11 19:43:16 crs +cmd/launcher/Makefile.am +lib/platform/Makefile.am + +More changes for MSYS/MinGW builds. Added makefile for launcher. +Still need hook library and resource compiling. + +---------- +2004/04/11 19:15:09 crs +cmd/launcher/launcher.rc +cmd/synergyc/synergyc.rc +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc +configure.in +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchMultithreadWindows.cpp +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchStringWindows.cpp +lib/arch/CArchTaskBarWindows.cpp +lib/arch/CMultibyte.cpp +lib/arch/XArchWindows.cpp +lib/arch/arch.dsp +lib/common/common.h +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CSynergyHook.cpp +lib/synergy/CProtocolUtil.cpp + +Preliminary support for MSYS/MinGW builds. Doesn't yet build +CSynergyHook as a DLL and does not compile or link in the +resources for the binaries. + +---------- +2004/04/11 14:58:08 crs +PORTING +cmd/synergyc/COSXClientTaskBarReceiver.cpp +cmd/synergyc/COSXClientTaskBarReceiver.h +cmd/synergyc/Makefile.am +cmd/synergyc/synergyc.cpp +cmd/synergys/COSXServerTaskBarReceiver.cpp +cmd/synergys/COSXServerTaskBarReceiver.h +cmd/synergys/Makefile.am +cmd/synergys/synergys.cpp +configure.in +lib/arch/CArch.cpp +lib/arch/CArchDaemonUnix.cpp +lib/arch/CArchFileUnix.cpp +lib/arch/CArchImpl.cpp +lib/arch/CArchNetworkBSD.cpp +lib/arch/CMultibyte.cpp +lib/arch/CMultibyteOS.cpp +lib/arch/Makefile.am +lib/arch/vsnprintf.cpp +lib/base/CPriorityQueue.h +lib/common/BasicTypes.h +lib/common/common.h +lib/platform/COSXClipboard.cpp +lib/platform/COSXClipboard.h +lib/platform/COSXEventQueueBuffer.cpp +lib/platform/COSXEventQueueBuffer.h +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/COSXScreenSaver.cpp +lib/platform/COSXScreenSaver.h +lib/platform/CSynergyHook.h +lib/platform/CXWindowsEventQueueBuffer.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/Makefile.am + +Updates to support OS X. This improves support for building on +multiple systems with automake, with X Windows and Carbon window +system APIs supported. It's also a starting port for supporting +win32 builds using mingw. OS X support is incomplete; the tree +will compile and link but the binaries will not function. + +---------- +2004/04/06 22:09:38 crs +lib/arch/CArchMultithreadPosix.cpp + +Added missing initialization of mutex attribute call. + +---------- +2004/04/05 21:23:44 crs +lib/server/CServer.cpp + +Fixed bug in handling rejection of screen with name that's already +in use. The client was being correctly rejected but the already +connected client was being forcefully disconnected too because the +client to disconnect was found by looking up the client by name. +We now instead look up the client by IClient*. + +---------- +2004/04/05 21:10:06 crs +lib/platform/CSynergyHook.cpp +lib/server/CServer.cpp +lib/server/CServer.h + +Added workaround for win32 low-level mouse hook position weirdness. +The low-level hook can report mouse positions outside the boundaries +of the screen and bogus retrograde motion. This messes up switch on +double tap. This change attempts to detect and suppress the bogus +events. + +---------- +2004/04/05 21:08:49 crs +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h + +Made hook debug logging print at DEBUG1 rather than INFO level. + +---------- +2004/04/04 12:12:32 crs +Makefile.am +cmd/Makefile.am +cmd/launcher/Makefile.am +cmd/synergyc/Makefile.am +cmd/synergys/Makefile.am +dist/Makefile.am +dist/nullsoft/Makefile.am +dist/rpm/Makefile.am +lib/Makefile.am +lib/arch/Makefile.am +lib/base/Makefile.am +lib/client/Makefile.am +lib/common/Makefile.am +lib/io/Makefile.am +lib/mt/Makefile.am +lib/net/Makefile.am +lib/platform/Makefile.am +lib/server/Makefile.am +lib/synergy/Makefile.am + +Removed DEPTH, VDEPTH, and VPATH from makefiles. + +---------- +2004/04/04 12:12:30 crs +configure.in +lib/common/Version.h + +Changed version to 1.1.5. + +---------- +2004/03/31 22:30:49 crs +dist/nullsoft/synergy.nsi + +Minor win32 installer tweaks. + +---------- 2004/03/31 22:15:13 crs lib/arch/CArchTaskBarWindows.cpp lib/arch/CArchTaskBarWindows.h diff --git a/FAQ b/FAQ index a238c6d2..c75f4194 100644 --- a/FAQ +++ b/FAQ @@ -19,6 +19,8 @@ Questions 14. The screen saver never starts. Why not? 15. I can't switch screens anymore for no apparent reason. Why? 16. I get the error 'Xlib: No protocol specified'. Why? +17. The cursor goes to secondary screen but won't come back. Why? +18. The cursor wraps from one edge of the screen to the opposite. Why? Answers ------- @@ -181,3 +183,17 @@ Answers X display. Typically the reason is running synergy as root when logged in as non-root. Just run synergy as the same user that's logged in. + +17. The cursor goes to secondary screen but won't come back. Why? + + Your configuration is incorrect. You must indicate the neighbors + of every screen. Just because you've configured 'Apple' to be to + the left of 'Orange' does not mean that 'Orange' is to the right + of 'Apple'. You must provide both in the configuration. + +18. The cursor wraps from one edge of the screen to the opposite. Why? + + Because you told it to. If you list 'Orange' to be to the left of + 'Orange' then moving the mouse off the left edge of 'Orange' will + make it jump to the right edge. Remove the offending line from the + configuration if you don't want that behavior. diff --git a/INSTALL b/INSTALL index c2caed75..0f2666a1 100644 --- a/INSTALL +++ b/INSTALL @@ -331,6 +331,14 @@ appear before the `links' and `aliases' sections. saver won't start if there is input, even if that input is directed toward a client screen. + relativeMouseMoves = {true|false} + If set to true then secondary screens move the mouse + using relative rather than absolute mouse moves when + and only when Scroll Lock is toggled on (i.e. the cursor + is locked to the screen). This is intended to make + synergy work better with certain games. If set to + false or not set then all mouse moves are absolute. + You can use both the switchDelay and switchDoubleTap options at the same time. Synergy will switch when either requirement is satisfied. diff --git a/NEWS b/NEWS index 69ad6968..e2888cab 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,23 @@ Synergy News ============ +* May-05-2004 - Synergy 1.1.5 released + + Made following changes: + * No longer switching screens when a mouse button is down + * Worked around win32 mouse hook bug, fixing switch on double tap + * Added support for HTML and bitmap (image/bmp) clipboard data + * Physical mouse no longer necessary on win32 secondary screens to see cursor + * Added experimental relative mouse moves on secondary screen option + * Fixed win32 lock up when closing server with clients still connected + * Fixed bug in handling duplicate connections + * Fixed pthread mutex initialization + * Made synergy dependent on NetBT on win32 (for service startup order) + * Automake fixes; now mostly works on darwin and MinGW + * Fixed builds on Solaris 9, FreeBSD, and OpenBSD + * Partial support for MSYS/MinGW builds (NOT COMPLETE) + * Partial merge of OS X port (NOT COMPLETE) + * Mar-31-2004 - Synergy 1.1.4 released Made following changes: diff --git a/TODO b/TODO index 0b1fc88d..5c05ce8b 100644 --- a/TODO +++ b/TODO @@ -26,13 +26,12 @@ Things to do to synergy, in no particular order: * Add more clipboard formats - Synergy currently supports only text on the clipboard. It should - support more formats, such as images and sound. For each format, - some canonical type must be chosen. For text, that's UTF-8 with - \n for newlines. For images, it might be BMP or PNG. Whatever it - is it should losslessly support any type it might be converted to. - The type is converted to each platform's native type. For example, - BMP for images on win32. + Synergy currently supports text, html and BMP bitmaps on the clipboard. + It should support more formats, such as other image types and sound. + For each format, some canonical type must be chosen. For text, that's + UTF-8 with \n for newlines. Whatever it is it should losslessly support + any type it might be converted to. The type is converted to each + platform's native type. For example, BMP for images on win32. Then there are major new features: From df4807950a914c7bd78441e511570ffcb244ad9c Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 12 May 2004 18:54:03 +0000 Subject: [PATCH 663/807] Fixed build error in gcc 3.3. --- lib/platform/CXWindowsClipboardBMPConverter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/CXWindowsClipboardBMPConverter.cpp b/lib/platform/CXWindowsClipboardBMPConverter.cpp index bd3438a2..747d6850 100644 --- a/lib/platform/CXWindowsClipboardBMPConverter.cpp +++ b/lib/platform/CXWindowsClipboardBMPConverter.cpp @@ -105,7 +105,7 @@ CXWindowsClipboardBMPConverter::fromIClipboard(const CString& bmp) const UInt8* dst = header; toLE(dst, 'B'); toLE(dst, 'M'); - toLE(dst, 14 + bmp.size()); + toLE(dst, static_cast(14 + bmp.size())); toLE(dst, static_cast(0)); toLE(dst, static_cast(0)); toLE(dst, static_cast(14 + 40)); From a59ad51ce28e3cb707af24bea0e00fef7cdd4736 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 12 May 2004 19:12:28 +0000 Subject: [PATCH 664/807] Added configure option to enable debug builds. If not enabled then asserts are disabled. --- configure.in | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/configure.in b/configure.in index 8d19d2d9..4a30b12e 100644 --- a/configure.in +++ b/configure.in @@ -64,6 +64,15 @@ AC_LANG_CPLUSPLUS dnl our files end in .cpp not .C so tests should also end in .cpp ac_ext=cpp +dnl enable debugging or disable asserts +AC_ARG_ENABLE([debug], + AC_HELP_STRING([--enable-debug], [enable debugging])) +if test "x$enable_debug" != xno; then + CXXFLAGS="$CXXFLAGS -g" +else + CXXFLAGS="$CXXFLAGS -DNDEBUG" +fi + dnl check compiler ACX_CHECK_CXX From 455d0210b2839dfc278572b8c1089030a2697a3b Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 12 May 2004 19:50:58 +0000 Subject: [PATCH 665/807] Fixed thread reference leak in network code. --- lib/arch/CArchNetworkBSD.cpp | 5 ++++- lib/arch/CArchNetworkWinsock.cpp | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index d27ab3fd..60253b00 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -827,7 +827,10 @@ const int* CArchNetworkBSD::getUnblockPipe() { CArchMultithreadPosix* mt = CArchMultithreadPosix::getInstance(); - return getUnblockPipeForThread(mt->newCurrentThread()); + CArchThread thread = mt->newCurrentThread(); + const int* p = getUnblockPipeForThread(thread); + ARCH->closeThread(thread); + return p; } const int* diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index b91ba190..0ed630e0 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -418,6 +418,7 @@ CArchNetworkWinsock::pollSocket(CPollEntry pe[], int num, double timeout) CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance(); CArchThread thread = mt->newCurrentThread(); WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread); + ARCH->closeThread(thread); if (unblockEvent == NULL) { unblockEvent = new WSAEVENT; *unblockEvent = WSACreateEvent_winsock(); From 242050d0f2ce0c1405dd7a755286eafede217e77 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 12 May 2004 20:28:00 +0000 Subject: [PATCH 666/807] Now restoring input focus when entering the screen to the window that had the focus when the screen was left. --- lib/platform/CXWindowsScreen.cpp | 12 ++++++++++++ lib/platform/CXWindowsScreen.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 7aee2341..e20e07c7 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -114,6 +114,8 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) : m_xCenter(0), m_yCenter(0), m_xCursor(0), m_yCursor(0), m_keyState(NULL), + m_lastFocus(None), + m_lastFocusRevert(RevertToNone), m_im(NULL), m_ic(NULL), m_lastKeycode(0), @@ -257,6 +259,13 @@ CXWindowsScreen::enter() // keyboard if they're grabbed. XUnmapWindow(m_display, m_window); + // set the input focus to what it had been when we took it + if (m_lastFocus != None) { + // the window may not exist anymore so ignore errors + CXWindowsUtil::CErrorLock lock(m_display); + XSetInputFocus(m_display, m_lastFocus, m_lastFocusRevert, CurrentTime); + } + /* maybe call this if entering for the screensaver // set keyboard focus to root window. the screensaver should then // pick up key events for when the user enters a password to unlock. @@ -305,6 +314,9 @@ CXWindowsScreen::leave() return false; } + // save current focus + XGetInputFocus(m_display, &m_lastFocus, &m_lastFocusRevert); + // take focus XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime); diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index d1289dca..f9adfd9d 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -156,6 +156,10 @@ private: // keyboard stuff CXWindowsKeyState* m_keyState; + // input focus stuff + Window m_lastFocus; + int m_lastFocusRevert; + // input method stuff XIM m_im; XIC m_ic; From d9387dbed7b0093daf53620642ceb48cddc7f692 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 15 May 2004 19:41:46 +0000 Subject: [PATCH 667/807] Added bertrand landry hetu's mac OS X port to date. --- Makefile.am | 87 +- configure.in | 29 +- lib/common/common.h | 50 + lib/platform/COSXClipboard.cpp | 169 +- lib/platform/COSXClipboard.h | 58 + .../COSXClipboardAnyTextConverter.cpp | 85 + lib/platform/COSXClipboardAnyTextConverter.h | 52 + lib/platform/COSXClipboardTextConverter.cpp | 48 + lib/platform/COSXClipboardTextConverter.h | 37 + lib/platform/COSXClipboardUTF16Converter.cpp | 50 + lib/platform/COSXClipboardUTF16Converter.h | 36 + lib/platform/COSXEventQueueBuffer.cpp | 84 +- lib/platform/COSXEventQueueBuffer.h | 5 +- lib/platform/COSXScreen.cpp | 188 +- lib/platform/COSXScreen.h | 10 +- lib/platform/Makefile.am | 6 + synergy.xcode/project.pbxproj | 4486 +++++++++++++++++ 17 files changed, 5370 insertions(+), 110 deletions(-) create mode 100644 lib/platform/COSXClipboardAnyTextConverter.cpp create mode 100644 lib/platform/COSXClipboardAnyTextConverter.h create mode 100644 lib/platform/COSXClipboardTextConverter.cpp create mode 100644 lib/platform/COSXClipboardTextConverter.h create mode 100644 lib/platform/COSXClipboardUTF16Converter.cpp create mode 100644 lib/platform/COSXClipboardUTF16Converter.h create mode 100644 synergy.xcode/project.pbxproj diff --git a/Makefile.am b/Makefile.am index d3e210ac..dff8861f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,56 +13,57 @@ ## Process this file with automake to produce Makefile.in NULL = -SUBDIRS = \ - lib \ - cmd \ - dist \ +SUBDIRS = \ + lib \ + cmd \ + dist \ $(NULL) -EXTRA_DIST = \ - BUGS \ - FAQ \ - HISTORY \ - PORTING \ - TODO \ - all.dsp \ - synergy.dsw \ - doc/doxygen.cfg.in \ - examples/synergy.conf \ +EXTRA_DIST = \ + BUGS \ + FAQ \ + HISTORY \ + PORTING \ + TODO \ + all.dsp \ + synergy.dsw \ + synergy.xcode/project.pbxproj \ + doc/doxygen.cfg.in \ + examples/synergy.conf \ $(NULL) -MAINTAINERCLEANFILES = \ - Makefile.in \ - aclocal.m4 \ - config.h \ - config.h.in \ - config.log \ - config.status \ - configure \ - stamp-h.in \ - stamp-h1 \ - doc/doxygen.cfg \ - doc/doxygen/html/* \ +MAINTAINERCLEANFILES = \ + Makefile.in \ + aclocal.m4 \ + config.h \ + config.h.in \ + config.log \ + config.status \ + configure \ + stamp-h.in \ + stamp-h1 \ + doc/doxygen.cfg \ + doc/doxygen/html/* \ $(NULL) -PKG_FILES = \ - AUTHORS \ - BUGS \ - COPYING \ - ChangeLog \ - FAQ \ - HISTORY \ - INSTALL \ - NEWS \ - README \ - TODO \ - cmd/synergyc/synergyc \ - cmd/synergys/synergys \ - examples/synergy.conf \ +PKG_FILES = \ + AUTHORS \ + BUGS \ + COPYING \ + ChangeLog \ + FAQ \ + HISTORY \ + INSTALL \ + NEWS \ + README \ + TODO \ + cmd/synergyc/synergyc \ + cmd/synergys/synergys \ + examples/synergy.conf \ $(NULL) -PKG_PROG_FILES = \ - synergyc \ - synergys \ +PKG_PROG_FILES = \ + synergyc \ + synergys \ $(NULL) # build doxygen documentation diff --git a/configure.in b/configure.in index 4a30b12e..dc451ded 100644 --- a/configure.in +++ b/configure.in @@ -38,8 +38,7 @@ case $host in ;; *-*-darwin*) acx_host_arch="UNIX" - acx_host_winapi="XWINDOWS" -# acx_host_winapi="CARBON" + acx_host_winapi="CARBON" ;; *) acx_host_arch="UNIX" @@ -65,8 +64,7 @@ dnl our files end in .cpp not .C so tests should also end in .cpp ac_ext=cpp dnl enable debugging or disable asserts -AC_ARG_ENABLE([debug], - AC_HELP_STRING([--enable-debug], [enable debugging])) +AC_ARG_ENABLE([debug], [ --enable-debug enable debugging]) if test "x$enable_debug" != xno; then CXXFLAGS="$CXXFLAGS -g" else @@ -111,22 +109,29 @@ if test x"$acx_host_winapi" = xXWINDOWS; then CPPFLAGS="$X_CFLAGS $CPPFLAGS" AC_CHECK_LIB(Xtst, XTestQueryExtension, - AC_CHECK_HEADERS([X11/extensions/XTest.h], - [X_LIBS="$X_LIBS -lXtst"], - AC_MSG_ERROR(Your must have the XTest headers to compile synergy)), + [X_LIBS="$X_LIBS -lXtst"], AC_MSG_ERROR(You must have the XTest library to build synergy), [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) + AC_CHECK_HEADERS([X11/extensions/XTest.h],, + AC_MSG_ERROR(You must have the XTest headers to compile synergy)) AC_CHECK_LIB(Xinerama, XineramaQueryExtension, - AC_CHECK_HEADERS([X11/extensions/Xinerama.h], - [X_LIBS="$X_LIBS -lXinerama"]), - , + [X_LIBS="$X_LIBS -lXinerama"], + [acx_xinerama_lib_ok=no], [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) + if test x"$acx_xinerama_lib_ok" != xno; then + AC_CHECK_HEADERS([X11/extensions/Xinerama.h],,, + [#include ]) + fi AC_CHECK_LIB(Xext, DPMSQueryExtension, - AC_CHECK_HEADERS([X11/extensions/dpms.h]), - , + [acx_dpms_lib_ok=yes], + [acx_dpms_lib_ok=no], [$X_LIBS -lX11 $X_EXTRA_LIBS]) + if test x"$acx_dpms_lib_ok" = xyes; then + AC_CHECK_HEADERS([X11/extensions/dpms.h],,, + [#include ]) + fi CPPFLAGS="$save_CPPFLAGS" ARCH_LIBS="$X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS $ARCH_LIBS" ARCH_CFLAGS="$ARCH_CFLAGS $X_CFLAGS" diff --git a/lib/common/common.h b/lib/common/common.h index 96910efc..c2c910e8 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -25,6 +25,56 @@ # define SYSAPI_WIN32 1 # define WINAPI_MSWINDOWS 1 # endif + + // we may not have run configure on OS X +# if defined(__APPLE__) +# define SYSAPI_UNIX 1 +# define WINAPI_CARBON 1 + +# define HAVE_ALLOCA_H 1 +# define HAVE_CXX_BOOL 1 +# define HAVE_CXX_CASTS 1 +# define HAVE_CXX_EXCEPTIONS 1 +# define HAVE_CXX_MUTABLE 1 +# define HAVE_CXX_STDLIB 1 +# define HAVE_GETPWUID_R 1 +# define HAVE_GMTIME_R 1 +# define HAVE_INET_ATON 1 +# define HAVE_INTTYPES_H 1 +# define HAVE_ISTREAM 1 +# define HAVE_MEMORY_H 1 +# define HAVE_NANOSLEEP 1 +# define HAVE_OSTREAM 1 +# define HAVE_POLL 1 +# define HAVE_POSIX_SIGWAIT 1 +# define HAVE_PTHREAD 1 +# define HAVE_PTHREAD_SIGNAL 1 +# define HAVE_SOCKLEN_T 1 +# define HAVE_SSTREAM 1 +# define HAVE_STDINT_H 1 +# define HAVE_STDLIB_H 1 +# define HAVE_STRINGS_H 1 +# define HAVE_STRING_H 1 +# define HAVE_SYS_SELECT_H 1 +# define HAVE_SYS_STAT_H 1 +# define HAVE_SYS_TIME_H 1 +# define HAVE_SYS_TYPES_H 1 +# define HAVE_UNISTD_H 1 +# define HAVE_VSNPRINTF 1 +# define HAVE_WCHAR_H 1 +# define HAVE_SYS_SOCKET_H 1 + +# define SELECT_TYPE_ARG1 int +# define SELECT_TYPE_ARG234 (fd_set *) +# define SELECT_TYPE_ARG5 (struct timeval *) +# define SIZEOF_CHAR 1 +# define SIZEOF_INT 4 +# define SIZEOF_LONG 4 +# define SIZEOF_SHORT 2 +# define STDC_HEADERS 1 +# define TIME_WITH_SYS_TIME 1 +# define X_DISPLAY_MISSING 1 +# endif #endif // VC++ specific diff --git a/lib/platform/COSXClipboard.cpp b/lib/platform/COSXClipboard.cpp index 8c0071f6..6a244587 100644 --- a/lib/platform/COSXClipboard.cpp +++ b/lib/platform/COSXClipboard.cpp @@ -13,58 +13,205 @@ */ #include "COSXClipboard.h" -#include - -// FIXME -- implement this +#include "COSXClipboardUTF16Converter.h" +#include "COSXClipboardTextConverter.h" +#include "CLog.h" +#include "XArch.h" // // COSXClipboard // -COSXClipboard::COSXClipboard() +COSXClipboard::COSXClipboard() : + m_time(0), + m_scrap(NULL) { + m_converters.push_back(new COSXClipboardUTF16Converter); + m_converters.push_back(new COSXClipboardTextConverter); } COSXClipboard::~COSXClipboard() { + clearConverters(); } bool COSXClipboard::empty() { + LOG((CLOG_DEBUG "empty clipboard")); + assert(m_scrap != NULL); + + OSStatus err = ClearScrap(&m_scrap); + // XXX -- check err? + + err = PutScrapFlavor( + m_scrap, + getOwnershipFlavor(), + kScrapFlavorMaskNone, + 0, + 0); + + if (err != noErr) { + LOG((CLOG_DEBUG "failed to grab clipboard")); + return false; + } + return true; } void -COSXClipboard::add(EFormat, const CString&) +COSXClipboard::add(EFormat format, const CString & data) { + LOG((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); + + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + + IOSXClipboardConverter* converter = *index; + + // skip converters for other formats + if (converter->getFormat() == format) { + CString osXData = converter->fromIClipboard(data); + ScrapFlavorType flavorType = converter->getOSXFormat(); + + PutScrapFlavor( + m_scrap, + flavorType, + kScrapFlavorMaskNone, + osXData.size(), + osXData.data()); + } + } } bool -COSXClipboard::open(Time) const +COSXClipboard::open(Time time) const { - return false; + LOG((CLOG_DEBUG "open clipboard")); + m_time = time; + OSStatus err = GetCurrentScrap(&m_scrap); + return (err == noErr); } void COSXClipboard::close() const { + LOG((CLOG_DEBUG "close clipboard")); + m_scrap = NULL; } IClipboard::Time COSXClipboard::getTime() const { - return 0; + return m_time; } bool -COSXClipboard::has(EFormat) const +COSXClipboard::has(EFormat format) const { + assert(m_scrap != NULL); + + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IOSXClipboardConverter* converter = *index; + if (converter->getFormat() == format) { + ScrapFlavorFlags flags; + ScrapFlavorType type = converter->getOSXFormat(); + + if (GetScrapFlavorFlags(m_scrap, type, &flags) == noErr) { + return true; + } + } + } + return false; } CString -COSXClipboard::get(EFormat) const +COSXClipboard::get(EFormat format) const { - return ""; + CString result; + + // find the converter for the first clipboard format we can handle + IOSXClipboardConverter* converter = NULL; + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + converter = *index; + + ScrapFlavorFlags flags; + ScrapFlavorType type = converter->getOSXFormat(); + + if (converter->getFormat() == format && + GetScrapFlavorFlags(m_scrap, type, &flags) == noErr) { + break; + } + converter = NULL; + } + + // if no converter then we don't recognize any formats + if (converter == NULL) { + return result; + } + + // get the clipboard data. + char* buffer = NULL; + try { + Size flavorSize; + OSStatus err = GetScrapFlavorSize(m_scrap, + converter->getOSXFormat(), &flavorSize); + if (err != noErr) { + throw err; + } + + buffer = new char[flavorSize]; + if (buffer == NULL) { + throw memFullErr; + } + + err = GetScrapFlavorData(m_scrap, + converter->getOSXFormat(), &flavorSize, buffer); + if (err != noErr) { + throw err; + } + + result = CString(buffer, flavorSize); + } + catch (OSStatus err) { + LOG((CLOG_DEBUG "exception thrown in COSXClipboard::get MacError (%d)", err)); + } + catch (...) { + LOG((CLOG_DEBUG "unknown exception in COSXClipboard::get")); + RETHROW_XTHREAD + } + delete[] buffer; + + return converter->toIClipboard(result); +} + +void +COSXClipboard::clearConverters() +{ + for (ConverterList::iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + delete *index; + } + m_converters.clear(); +} + +bool +COSXClipboard::isOwnedBySynergy() +{ + ScrapFlavorFlags flags; + ScrapRef scrap; + OSStatus err = GetCurrentScrap(&scrap); + if (err == noErr) { + err = GetScrapFlavorFlags(scrap, getOwnershipFlavor() , &flags); + } + return (err == noErr); +} + +ScrapFlavorType +COSXClipboard::getOwnershipFlavor() +{ + return 'Syne'; } diff --git a/lib/platform/COSXClipboard.h b/lib/platform/COSXClipboard.h index 8b6382d8..19f95ec7 100644 --- a/lib/platform/COSXClipboard.h +++ b/lib/platform/COSXClipboard.h @@ -16,6 +16,10 @@ #define COSXCLIPBOARD_H #include "IClipboard.h" +#include +#include + +class IOSXClipboardConverter; //! OS X clipboard implementation class COSXClipboard : public IClipboard { @@ -23,6 +27,9 @@ public: COSXClipboard(); virtual ~COSXClipboard(); + //! Test if clipboard is owned by synergy + static bool isOwnedBySynergy(); + // IClipboard overrides virtual bool empty(); virtual void add(EFormat, const CString& data); @@ -31,6 +38,57 @@ public: virtual Time getTime() const; virtual bool has(EFormat) const; virtual CString get(EFormat) const; + +private: + void clearConverters(); + static ScrapFlavorType + getOwnershipFlavor(); + +private: + typedef std::vector ConverterList; + + mutable Time m_time; + ConverterList m_converters; + mutable ScrapRef m_scrap; +}; + +//! Clipboard format converter interface +/*! +This interface defines the methods common to all Scrap book format +*/ +class IOSXClipboardConverter : public IInterface { +public: + //! @name accessors + //@{ + + //! Get clipboard format + /*! + Return the clipboard format this object converts from/to. + */ + virtual IClipboard::EFormat + getFormat() const = 0; + + //! returns the scrap flavor type that this object converts from/to + virtual ScrapFlavorType + getOSXFormat() const = 0; + + //! Convert from IClipboard format + /*! + Convert from the IClipboard format to the Carbon scrap format. + The input data must be in the IClipboard format returned by + getFormat(). The return data will be in the scrap + format returned by getOSXFormat(). + */ + virtual CString fromIClipboard(const CString&) const = 0; + + //! Convert to IClipboard format + /*! + Convert from the carbon scrap format to the IClipboard format + (i.e., the reverse of fromIClipboard()). + */ + virtual CString toIClipboard(const CString&) const = 0; + + //@} }; #endif diff --git a/lib/platform/COSXClipboardAnyTextConverter.cpp b/lib/platform/COSXClipboardAnyTextConverter.cpp new file mode 100644 index 00000000..67fc3029 --- /dev/null +++ b/lib/platform/COSXClipboardAnyTextConverter.cpp @@ -0,0 +1,85 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COSXClipboardAnyTextConverter.h" +#include + +// +// COSXClipboardAnyTextConverter +// + +COSXClipboardAnyTextConverter::COSXClipboardAnyTextConverter() +{ + // do nothing +} + +COSXClipboardAnyTextConverter::~COSXClipboardAnyTextConverter() +{ + // do nothing +} + +IClipboard::EFormat +COSXClipboardAnyTextConverter::getFormat() const +{ + return IClipboard::kText; +} + +CString +COSXClipboardAnyTextConverter::fromIClipboard(const CString& data) const +{ + // convert linefeeds and then convert to desired encoding + return doFromIClipboard(convertLinefeedToMacOS(data)); +} + +CString +COSXClipboardAnyTextConverter::toIClipboard(const CString& data) const +{ + // convert text then newlines + return convertLinefeedToUnix(doToIClipboard(data)); +} + +static +bool +isLF(char ch) +{ + return (ch == '\n'); +} + +static +bool +isCR(char ch) +{ + return (ch == '\r'); +} + +CString +COSXClipboardAnyTextConverter::convertLinefeedToMacOS(const CString& src) +{ + // note -- we assume src is a valid UTF-8 string + CString copy = src; + + std::replace_if(copy.begin(), copy.end(), isLF, '\r'); + + return copy; +} + +CString +COSXClipboardAnyTextConverter::convertLinefeedToUnix(const CString& src) +{ + CString copy = src; + + std::replace_if(copy.begin(), copy.end(), isCR, '\n'); + + return copy; +} diff --git a/lib/platform/COSXClipboardAnyTextConverter.h b/lib/platform/COSXClipboardAnyTextConverter.h new file mode 100644 index 00000000..5d0b79f3 --- /dev/null +++ b/lib/platform/COSXClipboardAnyTextConverter.h @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXCLIPBOARDANYTEXTCONVERTER_H +#define COSXCLIPBOARDANYTEXTCONVERTER_H + +#include "COSXClipboard.h" + +//! Convert to/from some text encoding +class COSXClipboardAnyTextConverter : public IOSXClipboardConverter { +public: + COSXClipboardAnyTextConverter(); + virtual ~COSXClipboardAnyTextConverter(); + + // IOSXClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual ScrapFlavorType + getOSXFormat() const = 0; + virtual CString fromIClipboard(const CString &) const; + virtual CString toIClipboard(const CString &) const; + +protected: + //! Convert from IClipboard format + /*! + Do UTF-8 conversion and linefeed conversion. + */ + virtual CString doFromIClipboard(const CString&) const = 0; + + //! Convert to IClipboard format + /*! + Do UTF-8 conversion and Linefeed conversion. + */ + virtual CString doToIClipboard(const CString&) const = 0; + +private: + static CString convertLinefeedToMacOS(const CString&); + static CString convertLinefeedToUnix(const CString&); +}; + +#endif diff --git a/lib/platform/COSXClipboardTextConverter.cpp b/lib/platform/COSXClipboardTextConverter.cpp new file mode 100644 index 00000000..0b92dee1 --- /dev/null +++ b/lib/platform/COSXClipboardTextConverter.cpp @@ -0,0 +1,48 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COSXClipboardTextConverter.h" +#include "CUnicode.h" + +// +// COSXClipboardTextConverter +// + +COSXClipboardTextConverter::COSXClipboardTextConverter() +{ + // do nothing +} + +COSXClipboardTextConverter::~COSXClipboardTextConverter() +{ + // do nothing +} + +ScrapFlavorType +COSXClipboardTextConverter::getOSXFormat() const +{ + return kScrapFlavorTypeText; +} + +CString +COSXClipboardTextConverter::doFromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToText(data); +} + +CString +COSXClipboardTextConverter::doToIClipboard(const CString& data) const +{ + return CUnicode::textToUTF8(data); +} diff --git a/lib/platform/COSXClipboardTextConverter.h b/lib/platform/COSXClipboardTextConverter.h new file mode 100644 index 00000000..dc2649d5 --- /dev/null +++ b/lib/platform/COSXClipboardTextConverter.h @@ -0,0 +1,37 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXCLIPBOARDTEXTCONVERTER_H +#define COSXCLIPBOARDTEXTCONVERTER_H + +#include "COSXClipboardAnyTextConverter.h" + +//! Convert to/from locale text encoding +class COSXClipboardTextConverter : public COSXClipboardAnyTextConverter { +public: + COSXClipboardTextConverter(); + virtual ~COSXClipboardTextConverter(); + + // IOSXClipboardAnyTextConverter overrides + virtual ScrapFlavorType + getOSXFormat() const; + +protected: + // COSXClipboardAnyTextConverter overrides + virtual CString doFromIClipboard(const CString&) const; + virtual CString doToIClipboard(const CString&) const; + +}; + +#endif diff --git a/lib/platform/COSXClipboardUTF16Converter.cpp b/lib/platform/COSXClipboardUTF16Converter.cpp new file mode 100644 index 00000000..10693c83 --- /dev/null +++ b/lib/platform/COSXClipboardUTF16Converter.cpp @@ -0,0 +1,50 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "COSXClipboardUTF16Converter.h" +#include "CUnicode.h" + +// +// COSXClipboardUTF16Converter +// + +COSXClipboardUTF16Converter::COSXClipboardUTF16Converter() +{ + // do nothing +} + +COSXClipboardUTF16Converter::~COSXClipboardUTF16Converter() +{ + // do nothing +} + +ScrapFlavorType +COSXClipboardUTF16Converter::getOSXFormat() const +{ + return kScrapFlavorTypeUnicode; +} + +CString +COSXClipboardUTF16Converter::doFromIClipboard(const CString& data) const +{ + // convert and add nul terminator + return CUnicode::UTF8ToUTF16(data); +} + +CString +COSXClipboardUTF16Converter::doToIClipboard(const CString& data) const +{ + // convert and strip nul terminator + return CUnicode::UTF16ToUTF8(data); +} diff --git a/lib/platform/COSXClipboardUTF16Converter.h b/lib/platform/COSXClipboardUTF16Converter.h new file mode 100644 index 00000000..1499a7ed --- /dev/null +++ b/lib/platform/COSXClipboardUTF16Converter.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXCLIPBOARDUTF16CONVERTER_H +#define COSXCLIPBOARDUTF16CONVERTER_H + +#include "COSXClipboardAnyTextConverter.h" + +//! Convert to/from UTF-16 encoding +class COSXClipboardUTF16Converter : public COSXClipboardAnyTextConverter { +public: + COSXClipboardUTF16Converter(); + virtual ~COSXClipboardUTF16Converter(); + + // IOSXClipboardAnyTextConverter overrides + virtual ScrapFlavorType + getOSXFormat() const; + +protected: + // COSXClipboardAnyTextConverter overrides + virtual CString doFromIClipboard(const CString&) const; + virtual CString doToIClipboard(const CString&) const; +}; + +#endif diff --git a/lib/platform/COSXEventQueueBuffer.cpp b/lib/platform/COSXEventQueueBuffer.cpp index b373f63a..22dadc9d 100644 --- a/lib/platform/COSXEventQueueBuffer.cpp +++ b/lib/platform/COSXEventQueueBuffer.cpp @@ -13,19 +13,28 @@ */ #include "COSXEventQueueBuffer.h" +#include "CEvent.h" +#include "IEventQueue.h" + +// +// CEventQueueTimer +// + +class CEventQueueTimer { }; // // COSXEventQueueBuffer // -COSXEventQueueBuffer::COSXEventQueueBuffer() +COSXEventQueueBuffer::COSXEventQueueBuffer() : + m_event(NULL) { - // FIXME + // do nothing } COSXEventQueueBuffer::~COSXEventQueueBuffer() { - // FIXME + setOSXEvent(NULL); } void @@ -38,18 +47,51 @@ COSXEventQueueBuffer::waitForEvent(double timeout) IEventQueueBuffer::Type COSXEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) { - // FIXME - (void)event; - (void)dataID; - return kNone; + EventRef carbonEvent = NULL; + OSStatus error = ReceiveNextEvent(0, NULL, 0.0, true, &carbonEvent); + setOSXEvent(carbonEvent); + + if (error == eventLoopQuitErr) { + event = CEvent(CEvent::kQuit); + return kSystem; + } + else if (error != noErr) { + return kNone; + } + else { + UInt32 eventClass = GetEventClass(m_event); + switch (eventClass) { + case 'Syne': + dataID = GetEventKind(m_event); + return kUser; + + default: + event = CEvent(CEvent::kSystem, + IEventQueue::getSystemTarget(), &m_event); + return kNone; + } + } } bool COSXEventQueueBuffer::addEvent(UInt32 dataID) { - // FIXME - (void)dataID; - return false; + EventRef event; + OSStatus error = CreateEvent( + kCFAllocatorDefault, + 'Syne', + dataID, + 0, + kEventAttributeNone, + &event); + + if (error == noErr) { + error = PostEventToQueue(GetMainEventQueue(), event, + kEventPriorityStandard); + ReleaseEvent(event); + } + + return (error == noErr); } bool @@ -57,20 +99,26 @@ COSXEventQueueBuffer::isEmpty() const { EventRef event; OSStatus status = ReceiveNextEvent(0, NULL, 0.0, false, &event); - return (status != eventLoopTimedOutErr); + return (status == eventLoopTimedOutErr); } CEventQueueTimer* -COSXEventQueueBuffer::newTimer(double duration, bool oneShot) const +COSXEventQueueBuffer::newTimer(double, bool) const { - // FIXME - (void)duration; - (void)oneShot; - return NULL; + return new CEventQueueTimer; } void -COSXEventQueueBuffer::deleteTimer(CEventQueueTimer*) const +COSXEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const { - // FIXME + delete timer; +} + +void +COSXEventQueueBuffer::setOSXEvent(EventRef event) +{ + if (m_event != NULL) { + ReleaseEvent(m_event); + } + m_event = RetainEvent(event); } diff --git a/lib/platform/COSXEventQueueBuffer.h b/lib/platform/COSXEventQueueBuffer.h index 215b018c..b4910026 100644 --- a/lib/platform/COSXEventQueueBuffer.h +++ b/lib/platform/COSXEventQueueBuffer.h @@ -33,8 +33,11 @@ public: newTimer(double duration, bool oneShot) const; virtual void deleteTimer(CEventQueueTimer*) const; +protected: + void setOSXEvent(EventRef event); + private: - // FIXME + EventRef m_event; }; #endif diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index 54fb4b47..80fb0677 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -34,7 +34,8 @@ COSXScreen::COSXScreen(bool isPrimary) : m_keyState(NULL), m_sequenceNumber(0), m_screensaver(NULL), - m_screensaverNotify(false) + m_screensaverNotify(false), + m_ownClipboard(false) { try { m_displayID = CGMainDisplayID(); @@ -145,6 +146,30 @@ COSXScreen::getCursorCenter(SInt32& x, SInt32& y) const y = m_yCenter; } +void +COSXScreen::postMouseEvent(const CGPoint & pos) const +{ + // synthesize event. CGPostMouseEvent is a particularly good + // example of a bad API. we have to shadow the mouse state to + // use this API and if we want to support more buttons we have + // to recompile. + // + // the order of buttons on the mac is: + // 1 - Left + // 2 - Right + // 3 - Middle + // Whatever the USB device defined. + // It is a bit weird that the behaviour of buttons over 3 are dependent + // on currently plugged in USB devices. + CGPostMouseEvent(pos, true, sizeof(m_buttons) / sizeof(m_buttons[0]), + m_buttons[0], + m_buttons[2], + m_buttons[1], + m_buttons[3], + m_buttons[4]); +} + + void COSXScreen::fakeMouseButton(ButtonID id, bool press) const { @@ -157,10 +182,6 @@ COSXScreen::fakeMouseButton(ButtonID id, bool press) const // update state m_buttons[index] = press; - // synthesize event. CGPostMouseEvent is a particularly good - // example of a bad API. we have to shadow the mouse state to - // use this API and if we want to support more buttons we have - // to recompile. CGPoint pos; if (!m_cursorPosValid) { SInt32 x, y; @@ -168,12 +189,7 @@ COSXScreen::fakeMouseButton(ButtonID id, bool press) const } pos.x = m_xCursor; pos.y = m_yCursor; - CGPostMouseEvent(pos, false, sizeof(m_buttons) / sizeof(m_buttons[0]), - m_buttons[0], - m_buttons[1], - m_buttons[2], - m_buttons[3], - m_buttons[4]); + postMouseEvent(pos); } void @@ -183,8 +199,7 @@ COSXScreen::fakeMouseMove(SInt32 x, SInt32 y) const CGPoint pos; pos.x = x; pos.y = y; - // FIXME -- is it okay to pass no buttons here? - CGPostMouseEvent(pos, true, 0, m_buttons[0]); + postMouseEvent(pos); // save new cursor position m_xCursor = x; @@ -208,8 +223,7 @@ COSXScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const CGPoint pos; pos.x = oldPos.h + dx; pos.y = oldPos.v + dy; - // FIXME -- is it okay to pass no buttons here? - CGPostMouseEvent(pos, true, 0, m_buttons[0]); + postMouseEvent(pos); // we now assume we don't know the current cursor position m_cursorPosValid = false; @@ -218,7 +232,32 @@ COSXScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const void COSXScreen::fakeMouseWheel(SInt32 delta) const { - CGPostScrollWheelEvent(1, delta / 120); + CFPropertyListRef pref = ::CFPreferencesCopyValue( + CFSTR("com.apple.scrollwheel.scaling"), + kCFPreferencesAnyApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + + int32_t wheelIncr = 10; + + if (pref != NULL) { + CFTypeID id = CFGetTypeID(pref); + if (id == CFNumberGetTypeID()) { + CFNumberRef value = static_cast(pref); + + double scaling; + if (CFNumberGetValue(value, kCFNumberDoubleType, &scaling)) { + wheelIncr = (int32_t)(8 * scaling); + } + } + CFRelease(pref); + } + + if (delta < 0) { + wheelIncr = -wheelIncr; + } + + CGPostScrollWheelEvent(1, wheelIncr); } void @@ -332,6 +371,7 @@ bool COSXScreen::setClipboard(ClipboardID, const IClipboard* src) { COSXClipboard dst; + m_ownClipboard = true; if (src != NULL) { // save clipboard data return CClipboard::copy(&dst, src); @@ -350,7 +390,18 @@ COSXScreen::setClipboard(ClipboardID, const IClipboard* src) void COSXScreen::checkClipboards() { - // FIXME -- do nothing if we're always up to date + if (m_ownClipboard && !COSXClipboard::isOwnedBySynergy()) { + static ScrapRef sScrapbook = NULL; + ScrapRef currentScrap; + GetCurrentScrap(¤tScrap); + + if (sScrapbook != currentScrap) { + m_ownClipboard = false; + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); + sScrapbook = currentScrap; + } + } } void @@ -406,9 +457,64 @@ COSXScreen::isPrimary() const } void -COSXScreen::handleSystemEvent(const CEvent&, void*) +COSXScreen::sendEvent(CEvent::Type type, void* data) { - // FIXME + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data)); +} + +void +COSXScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) +{ + CClipboardInfo* info = (CClipboardInfo*)malloc(sizeof(CClipboardInfo)); + info->m_id = id; + info->m_sequenceNumber = m_sequenceNumber; + sendEvent(type, info); +} + + +void +COSXScreen::handleSystemEvent(const CEvent& event, void*) +{ +/* + EventRef * carbonEvent = reinterpret_cast(event.getData()); + assert(carbonEvent != NULL); + + UInt32 eventClass = GetEventClass( *carbonEvent ); + + switch( eventClass ) + { + case kEventClassMouse: + { + UInt32 eventKind = GetEventKind( *carbonEvent ); + switch( eventKind ) + { + case kEventMouseMoved: + { + HIPoint point; + GetEventParameter( *carbonEvent, + kEventParamMouseDelta, + typeHIPoint, + NULL, + sizeof(point), + NULL, + &point); + + } break; + } + }break; + + case kEventClassKeyboard: + { + + } + + default: + { + + } break; + + } +*/ } void @@ -426,15 +532,49 @@ COSXScreen::getKeyState() const void COSXScreen::updateScreenShape() { - // FIXME -- handle multiple monitors + // get info for each display + CGDisplayCount displayCount = 0; + + if (CGGetActiveDisplayList(0, NULL, &displayCount) != CGDisplayNoErr) { + return; + } + + if (displayCount == 0) { + return; + } + + CGDirectDisplayID* displays = + (CGDirectDisplayID*)malloc(displayCount * sizeof(CGDirectDisplayID)); + + if (displays == NULL) { + return; + } + + if (CGGetActiveDisplayList(displayCount, + displays, &displayCount) != CGDisplayNoErr) { + free(displays); + return; + } + + // get smallest rect enclosing all display rects + CGRect totalBounds = CGRectZero; + for (CGDisplayCount i = 0; i < displayCount; ++i) { + CGRect bounds = CGDisplayBounds(displays[i]); + totalBounds = CGRectUnion(totalBounds, bounds); + } // get shape of default screen - m_x = 0; - m_y = 0; - m_w = CGDisplayPixelsWide(m_displayID); - m_h = CGDisplayPixelsHigh(m_displayID); + m_x = (SInt32)totalBounds.origin.x; + m_y = (SInt32)totalBounds.origin.y; + m_w = (SInt32)totalBounds.size.width; + m_h = (SInt32)totalBounds.size.height; // get center of default screen + // XXX -- this should compute the center of displays[0] m_xCenter = m_x + (m_w >> 1); m_yCenter = m_y + (m_h >> 1); + + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d on %u %s", m_x, m_y, m_w, m_h, displayCount, (displayCount == 1) ? "display" : "displays")); + + free(displays); } diff --git a/lib/platform/COSXScreen.h b/lib/platform/COSXScreen.h index 0bb2d310..12aca416 100644 --- a/lib/platform/COSXScreen.h +++ b/lib/platform/COSXScreen.h @@ -76,7 +76,12 @@ protected: private: void updateScreenShape(); - + void postMouseEvent(const CGPoint &) const; + + // convenience function to send events + void sendEvent(CEvent::Type type, void* = NULL); + void sendClipboardEvent(CEvent::Type type, ClipboardID id); + private: // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; @@ -107,6 +112,9 @@ private: // screen saver stuff COSXScreenSaver* m_screensaver; bool m_screensaverNotify; + + // clipboard stuff + bool m_ownClipboard; }; #endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 5b5bd13d..116d6566 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -71,11 +71,17 @@ MSWINDOWS_HOOK_SOURCE_FILES = \ $(NULL) CARBON_SOURCE_FILES = \ COSXClipboard.cpp \ + COSXClipboardAnyTextConverter.cpp \ + COSXClipboardTextConverter.cpp \ + COSXClipboardUTF16Converter.cpp \ COSXEventQueueBuffer.cpp \ COSXKeyState.cpp \ COSXScreen.cpp \ COSXScreenSaver.cpp \ COSXClipboard.h \ + COSXClipboardAnyTextConverter.h \ + COSXClipboardTextConverter.h \ + COSXClipboardUTF16Converter.h \ COSXEventQueueBuffer.h \ COSXKeyState.h \ COSXScreen.h \ diff --git a/synergy.xcode/project.pbxproj b/synergy.xcode/project.pbxproj new file mode 100644 index 00000000..37e1c34e --- /dev/null +++ b/synergy.xcode/project.pbxproj @@ -0,0 +1,4486 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 39; + objects = { + 014CEA460018CE2711CA2923 = { + buildRules = ( + ); + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + OPTIMIZATION_CFLAGS = "-O0"; + ZERO_LINK = NO; + }; + isa = PBXBuildStyle; + name = Development; + }; + 014CEA470018CE2711CA2923 = { + buildRules = ( + ); + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + ZERO_LINK = NO; + }; + isa = PBXBuildStyle; + name = Deployment; + }; +//010 +//011 +//012 +//013 +//014 +//020 +//021 +//022 +//023 +//024 + 0249A662FF388D9811CA2CEA = { + children = ( + 4CDF3892063E561B007B20A1, + 4CDF3893063E561B007B20A1, + 4CDF3894063E561B007B20A1, + 4CDF3895063E561B007B20A1, + 4CDF3896063E561B007B20A1, + 4CDF3897063E561B007B20A1, + 4CDF3898063E561B007B20A1, + 4CDF3899063E561B007B20A1, + 4CB43923063E4B1300969041, + ); + isa = PBXGroup; + name = "External Frameworks and Libraries"; + refType = 4; + sourceTree = ""; + }; +//020 +//021 +//022 +//023 +//024 +//080 +//081 +//082 +//083 +//084 + 08FB7793FE84155DC02AAC07 = { + buildSettings = { + }; + buildStyles = ( + 014CEA460018CE2711CA2923, + 014CEA470018CE2711CA2923, + ); + hasScannedForEncodings = 1; + isa = PBXProject; + mainGroup = 08FB7794FE84155DC02AAC07; + projectDirPath = ""; + targets = ( + 4CD033E80650965F00525ED1, + 4CB437B4063E443800969041, + 4C5E868C0648C2ED003C637B, + 4CB437D0063E44C200969041, + 4CB43800063E45F200969041, + 4CB43823063E46AB00969041, + 4CB43847063E475800969041, + 4CB4386D063E47F800969041, + 4CB438B0063E488600969041, + 4CB43777063E406A00969041, + 4C537F30064E29F800D3815C, + 4CB43913063E497700969041, + 4C537F0C064E286700D3815C, + ); + }; + 08FB7794FE84155DC02AAC07 = { + children = ( + 08FB7795FE84155DC02AAC07, + 0249A662FF388D9811CA2CEA, + 1AB674ADFE9D54B511CA2CBB, + ); + isa = PBXGroup; + name = synergy; + refType = 4; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 = { + children = ( + 4CB4378B063E432C00969041, + 4CB437D2063E44E400969041, + 4CB43779063E40B600969041, + 4C5E86CA0648C6FB003C637B, + 4CB4381E063E469300969041, + 4CB437FB063E45D700969041, + 4CB43849063E478900969041, + 4CB438AD063E487200969041, + 4C537F2B064E29C900D3815C, + 4CB43866063E47C800969041, + 4CB43916063E4A1A00969041, + 4C537F07064E283300D3815C, + ); + isa = PBXGroup; + name = Source; + refType = 4; + sourceTree = ""; + }; +//080 +//081 +//082 +//083 +//084 +//1A0 +//1A1 +//1A2 +//1A3 +//1A4 + 1AB674ADFE9D54B511CA2CBB = { + children = ( + 4CB43778063E406A00969041, + 4C5E868D0648C2ED003C637B, + 4CB437B5063E443800969041, + 4CB437D1063E44C200969041, + 4CB43801063E45F200969041, + 4CB43824063E46AB00969041, + 4CB43848063E475800969041, + 4C537F31064E29F800D3815C, + 4CB4386E063E47F800969041, + 4CB438B1063E488600969041, + 4CB43914063E497700969041, + 4C537F0D064E286700D3815C, + ); + isa = PBXGroup; + name = Products; + refType = 4; + sourceTree = ""; + }; +//1A0 +//1A1 +//1A2 +//1A3 +//1A4 +//4C0 +//4C1 +//4C2 +//4C3 +//4C4 + 4C537F07064E283300D3815C = { + children = ( + 4C537F0F064E293000D3815C, + 4C537F10064E293000D3815C, + 4C537F11064E293000D3815C, + 4C537F12064E293000D3815C, + 4C537F13064E293000D3815C, + ); + isa = PBXGroup; + name = SynergyS; + path = ""; + refType = 4; + sourceTree = ""; + }; + 4C537F0A064E286700D3815C = { + buildActionMask = 2147483647; + files = ( + 4C537F15064E293000D3815C, + 4C537F17064E293000D3815C, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4C537F0B064E286700D3815C = { + buildActionMask = 2147483647; + files = ( + 4C537F14064E293000D3815C, + 4C537F16064E293000D3815C, + 4C537F18064E293000D3815C, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4C537F0C064E286700D3815C = { + buildPhases = ( + 4C537F0A064E286700D3815C, + 4C537F0B064E286700D3815C, + 4C537F5A064E2B0700D3815C, + ); + buildRules = ( + ); + buildSettings = { + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform ./lib/client"; + INSTALL_PATH = /usr/local/bin; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = synergys; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + 4C537F1A064E298800D3815C, + 4C537F1C064E298800D3815C, + 4C537F1E064E298800D3815C, + 4C537F20064E298800D3815C, + 4C537F22064E298800D3815C, + 4C537F24064E298800D3815C, + 4C537F26064E298800D3815C, + 4C537F28064E298800D3815C, + 4C537F57064E2AA300D3815C, + ); + isa = PBXNativeTarget; + name = synergys; + productName = synergys; + productReference = 4C537F0D064E286700D3815C; + productType = "com.apple.product-type.tool"; + }; + 4C537F0D064E286700D3815C = { + explicitFileType = "compiled.mach-o.executable"; + includeInIndex = 0; + isa = PBXFileReference; + path = synergys; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4C537F0F064E293000D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = COSXServerTaskBarReceiver.cpp; + path = cmd/synergys/COSXServerTaskBarReceiver.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F10064E293000D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = COSXServerTaskBarReceiver.h; + path = cmd/synergys/COSXServerTaskBarReceiver.h; + refType = 4; + sourceTree = ""; + }; + 4C537F11064E293000D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CServerTaskBarReceiver.cpp; + path = cmd/synergys/CServerTaskBarReceiver.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F12064E293000D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CServerTaskBarReceiver.h; + path = cmd/synergys/CServerTaskBarReceiver.h; + refType = 4; + sourceTree = ""; + }; + 4C537F13064E293000D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = synergys.cpp; + path = cmd/synergys/synergys.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F14064E293000D3815C = { + fileRef = 4C537F0F064E293000D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F15064E293000D3815C = { + fileRef = 4C537F10064E293000D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F16064E293000D3815C = { + fileRef = 4C537F11064E293000D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F17064E293000D3815C = { + fileRef = 4C537F12064E293000D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F18064E293000D3815C = { + fileRef = 4C537F13064E293000D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F19064E298800D3815C = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB437B4063E443800969041; + remoteInfo = arch; + }; + 4C537F1A064E298800D3815C = { + isa = PBXTargetDependency; + target = 4CB437B4063E443800969041; + targetProxy = 4C537F19064E298800D3815C; + }; + 4C537F1B064E298800D3815C = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4C5E868C0648C2ED003C637B; + remoteInfo = common; + }; + 4C537F1C064E298800D3815C = { + isa = PBXTargetDependency; + target = 4C5E868C0648C2ED003C637B; + targetProxy = 4C537F1B064E298800D3815C; + }; + 4C537F1D064E298800D3815C = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB437D0063E44C200969041; + remoteInfo = base; + }; + 4C537F1E064E298800D3815C = { + isa = PBXTargetDependency; + target = 4CB437D0063E44C200969041; + targetProxy = 4C537F1D064E298800D3815C; + }; + 4C537F1F064E298800D3815C = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB43800063E45F200969041; + remoteInfo = mt; + }; + 4C537F20064E298800D3815C = { + isa = PBXTargetDependency; + target = 4CB43800063E45F200969041; + targetProxy = 4C537F1F064E298800D3815C; + }; + 4C537F21064E298800D3815C = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB43823063E46AB00969041; + remoteInfo = io; + }; + 4C537F22064E298800D3815C = { + isa = PBXTargetDependency; + target = 4CB43823063E46AB00969041; + targetProxy = 4C537F21064E298800D3815C; + }; + 4C537F23064E298800D3815C = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB43847063E475800969041; + remoteInfo = net; + }; + 4C537F24064E298800D3815C = { + isa = PBXTargetDependency; + target = 4CB43847063E475800969041; + targetProxy = 4C537F23064E298800D3815C; + }; + 4C537F25064E298800D3815C = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB4386D063E47F800969041; + remoteInfo = synergy; + }; + 4C537F26064E298800D3815C = { + isa = PBXTargetDependency; + target = 4CB4386D063E47F800969041; + targetProxy = 4C537F25064E298800D3815C; + }; + 4C537F27064E298800D3815C = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB438B0063E488600969041; + remoteInfo = platform; + }; + 4C537F28064E298800D3815C = { + isa = PBXTargetDependency; + target = 4CB438B0063E488600969041; + targetProxy = 4C537F27064E298800D3815C; + }; + 4C537F2B064E29C900D3815C = { + children = ( + 4C537F32064E2A0F00D3815C, + 4C537F33064E2A0F00D3815C, + 4C537F34064E2A0F00D3815C, + 4C537F35064E2A0F00D3815C, + 4C537F36064E2A0F00D3815C, + 4C537F37064E2A0F00D3815C, + 4C537F38064E2A0F00D3815C, + 4C537F39064E2A0F00D3815C, + 4C537F3A064E2A0F00D3815C, + 4C537F3B064E2A0F00D3815C, + 4C537F3C064E2A0F00D3815C, + 4C537F3D064E2A0F00D3815C, + 4C537F3E064E2A0F00D3815C, + 4C537F3F064E2A0F00D3815C, + 4C537F40064E2A0F00D3815C, + 4C537F41064E2A0F00D3815C, + 4C537F42064E2A0F00D3815C, + 4C537F43064E2A0F00D3815C, + ); + isa = PBXGroup; + name = Server; + refType = 4; + sourceTree = ""; + }; + 4C537F2E064E29F800D3815C = { + buildActionMask = 2147483647; + files = ( + 4C537F45064E2A0F00D3815C, + 4C537F47064E2A0F00D3815C, + 4C537F49064E2A0F00D3815C, + 4C537F4B064E2A0F00D3815C, + 4C537F4D064E2A0F00D3815C, + 4C537F4F064E2A0F00D3815C, + 4C537F51064E2A0F00D3815C, + 4C537F53064E2A0F00D3815C, + 4C537F55064E2A0F00D3815C, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4C537F2F064E29F800D3815C = { + buildActionMask = 2147483647; + files = ( + 4C537F44064E2A0F00D3815C, + 4C537F46064E2A0F00D3815C, + 4C537F48064E2A0F00D3815C, + 4C537F4A064E2A0F00D3815C, + 4C537F4C064E2A0F00D3815C, + 4C537F4E064E2A0F00D3815C, + 4C537F50064E2A0F00D3815C, + 4C537F52064E2A0F00D3815C, + 4C537F54064E2A0F00D3815C, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4C537F30064E29F800D3815C = { + buildPhases = ( + 4C537F2E064E29F800D3815C, + 4C537F2F064E29F800D3815C, + ); + buildRules = ( + ); + buildSettings = { + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform"; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = server; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = server; + productName = server; + productReference = 4C537F31064E29F800D3815C; + productType = "com.apple.product-type.library.static"; + }; + 4C537F31064E29F800D3815C = { + explicitFileType = archive.ar; + includeInIndex = 0; + isa = PBXFileReference; + path = libserver.a; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4C537F32064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CClientListener.cpp; + path = lib/server/CClientListener.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F33064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CClientListener.h; + path = lib/server/CClientListener.h; + refType = 4; + sourceTree = ""; + }; + 4C537F34064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CClientProxy.cpp; + path = lib/server/CClientProxy.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F35064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CClientProxy.h; + path = lib/server/CClientProxy.h; + refType = 4; + sourceTree = ""; + }; + 4C537F36064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CClientProxy1_0.cpp; + path = lib/server/CClientProxy1_0.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F37064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CClientProxy1_0.h; + path = lib/server/CClientProxy1_0.h; + refType = 4; + sourceTree = ""; + }; + 4C537F38064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CClientProxy1_1.cpp; + path = lib/server/CClientProxy1_1.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F39064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CClientProxy1_1.h; + path = lib/server/CClientProxy1_1.h; + refType = 4; + sourceTree = ""; + }; + 4C537F3A064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CClientProxy1_2.cpp; + path = lib/server/CClientProxy1_2.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F3B064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CClientProxy1_2.h; + path = lib/server/CClientProxy1_2.h; + refType = 4; + sourceTree = ""; + }; + 4C537F3C064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CClientProxyUnknown.cpp; + path = lib/server/CClientProxyUnknown.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F3D064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CClientProxyUnknown.h; + path = lib/server/CClientProxyUnknown.h; + refType = 4; + sourceTree = ""; + }; + 4C537F3E064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CConfig.cpp; + path = lib/server/CConfig.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F3F064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CConfig.h; + path = lib/server/CConfig.h; + refType = 4; + sourceTree = ""; + }; + 4C537F40064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CPrimaryClient.cpp; + path = lib/server/CPrimaryClient.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F41064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CPrimaryClient.h; + path = lib/server/CPrimaryClient.h; + refType = 4; + sourceTree = ""; + }; + 4C537F42064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CServer.cpp; + path = lib/server/CServer.cpp; + refType = 4; + sourceTree = ""; + }; + 4C537F43064E2A0F00D3815C = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CServer.h; + path = lib/server/CServer.h; + refType = 4; + sourceTree = ""; + }; + 4C537F44064E2A0F00D3815C = { + fileRef = 4C537F32064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F45064E2A0F00D3815C = { + fileRef = 4C537F33064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F46064E2A0F00D3815C = { + fileRef = 4C537F34064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F47064E2A0F00D3815C = { + fileRef = 4C537F35064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F48064E2A0F00D3815C = { + fileRef = 4C537F36064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F49064E2A0F00D3815C = { + fileRef = 4C537F37064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F4A064E2A0F00D3815C = { + fileRef = 4C537F38064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F4B064E2A0F00D3815C = { + fileRef = 4C537F39064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F4C064E2A0F00D3815C = { + fileRef = 4C537F3A064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F4D064E2A0F00D3815C = { + fileRef = 4C537F3B064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F4E064E2A0F00D3815C = { + fileRef = 4C537F3C064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F4F064E2A0F00D3815C = { + fileRef = 4C537F3D064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F50064E2A0F00D3815C = { + fileRef = 4C537F3E064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F51064E2A0F00D3815C = { + fileRef = 4C537F3F064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F52064E2A0F00D3815C = { + fileRef = 4C537F40064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F53064E2A0F00D3815C = { + fileRef = 4C537F41064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F54064E2A0F00D3815C = { + fileRef = 4C537F42064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F55064E2A0F00D3815C = { + fileRef = 4C537F43064E2A0F00D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F56064E2AA300D3815C = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4C537F30064E29F800D3815C; + remoteInfo = server; + }; + 4C537F57064E2AA300D3815C = { + isa = PBXTargetDependency; + target = 4C537F30064E29F800D3815C; + targetProxy = 4C537F56064E2AA300D3815C; + }; + 4C537F5A064E2B0700D3815C = { + buildActionMask = 2147483647; + files = ( + 4C537F64064E2B4B00D3815C, + 4C537F5B064E2B4200D3815C, + 4C537F5C064E2B4200D3815C, + 4C537F5D064E2B4200D3815C, + 4C537F5E064E2B4200D3815C, + 4C537F5F064E2B4200D3815C, + 4C537F60064E2B4200D3815C, + 4C537F61064E2B4200D3815C, + 4C537F62064E2B4200D3815C, + 4C537F63064E2B4200D3815C, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4C537F5B064E2B4200D3815C = { + fileRef = 4C5E868D0648C2ED003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F5C064E2B4200D3815C = { + fileRef = 4CB437B5063E443800969041; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F5D064E2B4200D3815C = { + fileRef = 4CB437D1063E44C200969041; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F5E064E2B4200D3815C = { + fileRef = 4CB43801063E45F200969041; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F5F064E2B4200D3815C = { + fileRef = 4CB43824063E46AB00969041; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F60064E2B4200D3815C = { + fileRef = 4CB43848063E475800969041; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F61064E2B4200D3815C = { + fileRef = 4C537F31064E29F800D3815C; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F62064E2B4200D3815C = { + fileRef = 4CB4386E063E47F800969041; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F63064E2B4200D3815C = { + fileRef = 4CB438B1063E488600969041; + isa = PBXBuildFile; + settings = { + }; + }; + 4C537F64064E2B4B00D3815C = { + fileRef = 4CB43923063E4B1300969041; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E868A0648C2ED003C637B = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4C5E868B0648C2ED003C637B = { + buildActionMask = 2147483647; + files = ( + 4C5E86CC0648C726003C637B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4C5E868C0648C2ED003C637B = { + buildPhases = ( + 4C5E868A0648C2ED003C637B, + 4C5E868B0648C2ED003C637B, + ); + buildRules = ( + ); + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRECOMP_DESTINATION_DIR = ""; + PRODUCT_NAME = common; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = common; + productName = common; + productReference = 4C5E868D0648C2ED003C637B; + productType = "com.apple.product-type.library.static"; + }; + 4C5E868D0648C2ED003C637B = { + explicitFileType = archive.ar; + includeInIndex = 0; + isa = PBXFileReference; + path = libcommon.a; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4C5E868F0648C32E003C637B = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4C5E868C0648C2ED003C637B; + remoteInfo = common; + }; + 4C5E86900648C32E003C637B = { + isa = PBXTargetDependency; + target = 4C5E868C0648C2ED003C637B; + targetProxy = 4C5E868F0648C32E003C637B; + }; + 4C5E86920648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArch.cpp; + path = lib/arch/CArch.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86930648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArch.h; + path = lib/arch/CArch.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86940648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchConsoleUnix.cpp; + path = lib/arch/CArchConsoleUnix.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86950648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchConsoleUnix.h; + path = lib/arch/CArchConsoleUnix.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86960648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchDaemonUnix.cpp; + path = lib/arch/CArchDaemonUnix.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86970648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchDaemonUnix.h; + path = lib/arch/CArchDaemonUnix.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86980648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchFileUnix.cpp; + path = lib/arch/CArchFileUnix.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86990648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchFileUnix.h; + path = lib/arch/CArchFileUnix.h; + refType = 4; + sourceTree = ""; + }; + 4C5E869A0648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchLogUnix.cpp; + path = lib/arch/CArchLogUnix.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E869B0648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchLogUnix.h; + path = lib/arch/CArchLogUnix.h; + refType = 4; + sourceTree = ""; + }; + 4C5E869C0648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchMultithreadPosix.cpp; + path = lib/arch/CArchMultithreadPosix.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E869D0648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchMultithreadPosix.h; + path = lib/arch/CArchMultithreadPosix.h; + refType = 4; + sourceTree = ""; + }; + 4C5E869E0648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchNetworkBSD.cpp; + path = lib/arch/CArchNetworkBSD.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E869F0648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchNetworkBSD.h; + path = lib/arch/CArchNetworkBSD.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86A00648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchSleepUnix.cpp; + path = lib/arch/CArchSleepUnix.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86A10648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchSleepUnix.h; + path = lib/arch/CArchSleepUnix.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86A20648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchStringUnix.cpp; + path = lib/arch/CArchStringUnix.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86A30648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchStringUnix.h; + path = lib/arch/CArchStringUnix.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86A40648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchTimeUnix.cpp; + path = lib/arch/CArchTimeUnix.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86A50648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchTimeUnix.h; + path = lib/arch/CArchTimeUnix.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86A70648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = XArchUnix.cpp; + path = lib/arch/XArchUnix.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86A80648C412003C637B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = XArchUnix.h; + path = lib/arch/XArchUnix.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86A90648C412003C637B = { + fileRef = 4C5E86920648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86AA0648C412003C637B = { + fileRef = 4C5E86930648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86AB0648C412003C637B = { + fileRef = 4C5E86940648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86AC0648C412003C637B = { + fileRef = 4C5E86950648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86AD0648C412003C637B = { + fileRef = 4C5E86960648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86AE0648C412003C637B = { + fileRef = 4C5E86970648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86AF0648C412003C637B = { + fileRef = 4C5E86980648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86B00648C412003C637B = { + fileRef = 4C5E86990648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86B10648C412003C637B = { + fileRef = 4C5E869A0648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86B20648C412003C637B = { + fileRef = 4C5E869B0648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86B30648C412003C637B = { + fileRef = 4C5E869C0648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86B40648C412003C637B = { + fileRef = 4C5E869D0648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86B50648C412003C637B = { + fileRef = 4C5E869E0648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86B60648C412003C637B = { + fileRef = 4C5E869F0648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86B70648C412003C637B = { + fileRef = 4C5E86A00648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86B80648C412003C637B = { + fileRef = 4C5E86A10648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86B90648C412003C637B = { + fileRef = 4C5E86A20648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86BA0648C412003C637B = { + fileRef = 4C5E86A30648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86BB0648C412003C637B = { + fileRef = 4C5E86A40648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86BC0648C412003C637B = { + fileRef = 4C5E86A50648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86BE0648C412003C637B = { + fileRef = 4C5E86A70648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86BF0648C412003C637B = { + fileRef = 4C5E86A80648C412003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86C00648C653003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchDaemonNone.cpp; + path = lib/arch/CArchDaemonNone.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86C10648C653003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchDaemonNone.h; + path = lib/arch/CArchDaemonNone.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86C20648C653003C637B = { + fileRef = 4C5E86C00648C653003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86C30648C653003C637B = { + fileRef = 4C5E86C10648C653003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86C40648C6B7003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchTaskBarXWindows.cpp; + path = lib/arch/CArchTaskBarXWindows.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86C50648C6B7003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchTaskBarXWindows.h; + path = lib/arch/CArchTaskBarXWindows.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86C60648C6B7003C637B = { + fileRef = 4C5E86C40648C6B7003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86C70648C6B7003C637B = { + fileRef = 4C5E86C50648C6B7003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86CA0648C6FB003C637B = { + children = ( + 4C5E86CB0648C725003C637B, + ); + isa = PBXGroup; + name = Common; + refType = 4; + sourceTree = ""; + }; + 4C5E86CB0648C725003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = Version.cpp; + path = lib/common/Version.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86CC0648C726003C637B = { + fileRef = 4C5E86CB0648C725003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86CD0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CEvent.cpp; + path = lib/base/CEvent.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86CE0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CEvent.h; + path = lib/base/CEvent.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86CF0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CEventQueue.cpp; + path = lib/base/CEventQueue.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86D00648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CEventQueue.h; + path = lib/base/CEventQueue.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86D10648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CFunctionEventJob.cpp; + path = lib/base/CFunctionEventJob.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86D20648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CFunctionEventJob.h; + path = lib/base/CFunctionEventJob.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86D30648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CFunctionJob.cpp; + path = lib/base/CFunctionJob.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86D40648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CFunctionJob.h; + path = lib/base/CFunctionJob.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86D50648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CLog.cpp; + path = lib/base/CLog.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86D60648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CLog.h; + path = lib/base/CLog.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86D70648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CPriorityQueue.h; + path = lib/base/CPriorityQueue.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86D80648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CSimpleEventQueueBuffer.cpp; + path = lib/base/CSimpleEventQueueBuffer.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86D90648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CSimpleEventQueueBuffer.h; + path = lib/base/CSimpleEventQueueBuffer.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86DA0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CStopwatch.cpp; + path = lib/base/CStopwatch.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86DB0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CStopwatch.h; + path = lib/base/CStopwatch.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86DC0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CString.h; + path = lib/base/CString.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86DD0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CStringUtil.cpp; + path = lib/base/CStringUtil.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86DE0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CStringUtil.h; + path = lib/base/CStringUtil.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86DF0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CUnicode.cpp; + path = lib/base/CUnicode.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86E00648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CUnicode.h; + path = lib/base/CUnicode.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86E10648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IEventJob.h; + path = lib/base/IEventJob.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86E20648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = IEventQueue.cpp; + path = lib/base/IEventQueue.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86E30648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IEventQueue.h; + path = lib/base/IEventQueue.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86E40648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IEventQueueBuffer.h; + path = lib/base/IEventQueueBuffer.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86E50648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IJob.h; + path = lib/base/IJob.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86E60648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = ILogOutputter.h; + path = lib/base/ILogOutputter.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86E70648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = LogOutputters.cpp; + path = lib/base/LogOutputters.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86E80648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = LogOutputters.h; + path = lib/base/LogOutputters.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86E90648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = TMethodEventJob.h; + path = lib/base/TMethodEventJob.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86EA0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = TMethodJob.h; + path = lib/base/TMethodJob.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86EB0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = XBase.cpp; + path = lib/base/XBase.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E86EC0648C7B9003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = XBase.h; + path = lib/base/XBase.h; + refType = 4; + sourceTree = ""; + }; + 4C5E86ED0648C7B9003C637B = { + fileRef = 4C5E86CD0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86EE0648C7B9003C637B = { + fileRef = 4C5E86CE0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86EF0648C7B9003C637B = { + fileRef = 4C5E86CF0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86F00648C7B9003C637B = { + fileRef = 4C5E86D00648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86F10648C7B9003C637B = { + fileRef = 4C5E86D10648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86F20648C7B9003C637B = { + fileRef = 4C5E86D20648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86F30648C7B9003C637B = { + fileRef = 4C5E86D30648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86F40648C7B9003C637B = { + fileRef = 4C5E86D40648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86F50648C7B9003C637B = { + fileRef = 4C5E86D50648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86F60648C7B9003C637B = { + fileRef = 4C5E86D60648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86F70648C7B9003C637B = { + fileRef = 4C5E86D70648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86F80648C7B9003C637B = { + fileRef = 4C5E86D80648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86F90648C7B9003C637B = { + fileRef = 4C5E86D90648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86FA0648C7B9003C637B = { + fileRef = 4C5E86DA0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86FB0648C7B9003C637B = { + fileRef = 4C5E86DB0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86FC0648C7B9003C637B = { + fileRef = 4C5E86DC0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86FD0648C7B9003C637B = { + fileRef = 4C5E86DD0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86FE0648C7B9003C637B = { + fileRef = 4C5E86DE0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E86FF0648C7B9003C637B = { + fileRef = 4C5E86DF0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87000648C7B9003C637B = { + fileRef = 4C5E86E00648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87010648C7B9003C637B = { + fileRef = 4C5E86E10648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87020648C7B9003C637B = { + fileRef = 4C5E86E20648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87030648C7B9003C637B = { + fileRef = 4C5E86E30648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87040648C7B9003C637B = { + fileRef = 4C5E86E40648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87050648C7B9003C637B = { + fileRef = 4C5E86E50648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87060648C7B9003C637B = { + fileRef = 4C5E86E60648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87070648C7B9003C637B = { + fileRef = 4C5E86E70648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87080648C7B9003C637B = { + fileRef = 4C5E86E80648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87090648C7B9003C637B = { + fileRef = 4C5E86E90648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E870A0648C7B9003C637B = { + fileRef = 4C5E86EA0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E870B0648C7B9003C637B = { + fileRef = 4C5E86EB0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E870C0648C7B9003C637B = { + fileRef = 4C5E86EC0648C7B9003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E870D0648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CCondVar.cpp; + path = lib/mt/CCondVar.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E870E0648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CCondVar.h; + path = lib/mt/CCondVar.h; + refType = 4; + sourceTree = ""; + }; + 4C5E870F0648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CLock.cpp; + path = lib/mt/CLock.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87100648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CLock.h; + path = lib/mt/CLock.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87110648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CMutex.cpp; + path = lib/mt/CMutex.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87120648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CMutex.h; + path = lib/mt/CMutex.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87130648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CThread.cpp; + path = lib/mt/CThread.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87140648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CThread.h; + path = lib/mt/CThread.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87150648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = XMT.cpp; + path = lib/mt/XMT.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87160648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = XMT.h; + path = lib/mt/XMT.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87170648C809003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = XThread.h; + path = lib/mt/XThread.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87180648C809003C637B = { + fileRef = 4C5E870D0648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87190648C809003C637B = { + fileRef = 4C5E870E0648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E871A0648C809003C637B = { + fileRef = 4C5E870F0648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E871B0648C809003C637B = { + fileRef = 4C5E87100648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E871C0648C809003C637B = { + fileRef = 4C5E87110648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E871D0648C809003C637B = { + fileRef = 4C5E87120648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E871E0648C809003C637B = { + fileRef = 4C5E87130648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E871F0648C809003C637B = { + fileRef = 4C5E87140648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87200648C809003C637B = { + fileRef = 4C5E87150648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87210648C809003C637B = { + fileRef = 4C5E87160648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87220648C809003C637B = { + fileRef = 4C5E87170648C809003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87230648C83C003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CStreamBuffer.cpp; + path = lib/io/CStreamBuffer.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87240648C83C003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CStreamBuffer.h; + path = lib/io/CStreamBuffer.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87250648C83C003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CStreamFilter.cpp; + path = lib/io/CStreamFilter.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87260648C83C003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CStreamFilter.h; + path = lib/io/CStreamFilter.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87270648C83C003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = IStream.cpp; + path = lib/io/IStream.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87280648C83C003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IStream.h; + path = lib/io/IStream.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87290648C83C003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IStreamFilterFactory.h; + path = lib/io/IStreamFilterFactory.h; + refType = 4; + sourceTree = ""; + }; + 4C5E872A0648C83C003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = XIO.cpp; + path = lib/io/XIO.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E872B0648C83C003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = XIO.h; + path = lib/io/XIO.h; + refType = 4; + sourceTree = ""; + }; + 4C5E872C0648C83C003C637B = { + fileRef = 4C5E87230648C83C003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E872D0648C83C003C637B = { + fileRef = 4C5E87240648C83C003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E872E0648C83C003C637B = { + fileRef = 4C5E87250648C83C003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E872F0648C83C003C637B = { + fileRef = 4C5E87260648C83C003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87300648C83C003C637B = { + fileRef = 4C5E87270648C83C003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87310648C83C003C637B = { + fileRef = 4C5E87280648C83C003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87320648C83C003C637B = { + fileRef = 4C5E87290648C83C003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87330648C83C003C637B = { + fileRef = 4C5E872A0648C83C003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87340648C83C003C637B = { + fileRef = 4C5E872B0648C83C003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87350648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CNetworkAddress.cpp; + path = lib/net/CNetworkAddress.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87360648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CNetworkAddress.h; + path = lib/net/CNetworkAddress.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87370648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CSocketMultiplexer.cpp; + path = lib/net/CSocketMultiplexer.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87380648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CSocketMultiplexer.h; + path = lib/net/CSocketMultiplexer.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87390648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CTCPListenSocket.cpp; + path = lib/net/CTCPListenSocket.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E873A0648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CTCPListenSocket.h; + path = lib/net/CTCPListenSocket.h; + refType = 4; + sourceTree = ""; + }; + 4C5E873B0648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CTCPSocket.cpp; + path = lib/net/CTCPSocket.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E873C0648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CTCPSocket.h; + path = lib/net/CTCPSocket.h; + refType = 4; + sourceTree = ""; + }; + 4C5E873D0648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CTCPSocketFactory.cpp; + path = lib/net/CTCPSocketFactory.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E873E0648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CTCPSocketFactory.h; + path = lib/net/CTCPSocketFactory.h; + refType = 4; + sourceTree = ""; + }; + 4C5E873F0648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = IDataSocket.cpp; + path = lib/net/IDataSocket.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87400648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IDataSocket.h; + path = lib/net/IDataSocket.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87410648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = IListenSocket.cpp; + path = lib/net/IListenSocket.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87420648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IListenSocket.h; + path = lib/net/IListenSocket.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87430648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = ISocket.cpp; + path = lib/net/ISocket.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87440648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = ISocket.h; + path = lib/net/ISocket.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87450648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = ISocketFactory.h; + path = lib/net/ISocketFactory.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87460648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = ISocketMultiplexerJob.h; + path = lib/net/ISocketMultiplexerJob.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87470648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = TSocketMultiplexerMethodJob.h; + path = lib/net/TSocketMultiplexerMethodJob.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87480648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = XSocket.cpp; + path = lib/net/XSocket.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87490648C870003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = XSocket.h; + path = lib/net/XSocket.h; + refType = 4; + sourceTree = ""; + }; + 4C5E874A0648C870003C637B = { + fileRef = 4C5E87350648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E874B0648C870003C637B = { + fileRef = 4C5E87360648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E874C0648C870003C637B = { + fileRef = 4C5E87370648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E874D0648C870003C637B = { + fileRef = 4C5E87380648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E874E0648C870003C637B = { + fileRef = 4C5E87390648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E874F0648C870003C637B = { + fileRef = 4C5E873A0648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87500648C870003C637B = { + fileRef = 4C5E873B0648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87510648C870003C637B = { + fileRef = 4C5E873C0648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87520648C870003C637B = { + fileRef = 4C5E873D0648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87530648C870003C637B = { + fileRef = 4C5E873E0648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87540648C870003C637B = { + fileRef = 4C5E873F0648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87550648C870003C637B = { + fileRef = 4C5E87400648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87560648C870003C637B = { + fileRef = 4C5E87410648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87570648C870003C637B = { + fileRef = 4C5E87420648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87580648C870003C637B = { + fileRef = 4C5E87430648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87590648C870003C637B = { + fileRef = 4C5E87440648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E875A0648C870003C637B = { + fileRef = 4C5E87450648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E875B0648C870003C637B = { + fileRef = 4C5E87460648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E875C0648C870003C637B = { + fileRef = 4C5E87470648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E875D0648C870003C637B = { + fileRef = 4C5E87480648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E875E0648C870003C637B = { + fileRef = 4C5E87490648C870003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E875F0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CClipboard.cpp; + path = lib/synergy/CClipboard.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87600648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CClipboard.h; + path = lib/synergy/CClipboard.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87610648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CKeyState.cpp; + path = lib/synergy/CKeyState.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87620648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CKeyState.h; + path = lib/synergy/CKeyState.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87630648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = ClipboardTypes.h; + path = lib/synergy/ClipboardTypes.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87640648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CPacketStreamFilter.cpp; + path = lib/synergy/CPacketStreamFilter.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87650648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CPacketStreamFilter.h; + path = lib/synergy/CPacketStreamFilter.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87660648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CPlatformScreen.cpp; + path = lib/synergy/CPlatformScreen.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87670648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CPlatformScreen.h; + path = lib/synergy/CPlatformScreen.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87680648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CProtocolUtil.cpp; + path = lib/synergy/CProtocolUtil.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87690648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CProtocolUtil.h; + path = lib/synergy/CProtocolUtil.h; + refType = 4; + sourceTree = ""; + }; + 4C5E876A0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CScreen.cpp; + path = lib/synergy/CScreen.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E876B0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CScreen.h; + path = lib/synergy/CScreen.h; + refType = 4; + sourceTree = ""; + }; + 4C5E876C0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IClient.h; + path = lib/synergy/IClient.h; + refType = 4; + sourceTree = ""; + }; + 4C5E876D0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = IClipboard.cpp; + path = lib/synergy/IClipboard.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E876E0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IClipboard.h; + path = lib/synergy/IClipboard.h; + refType = 4; + sourceTree = ""; + }; + 4C5E876F0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = IKeyState.cpp; + path = lib/synergy/IKeyState.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87700648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IKeyState.h; + path = lib/synergy/IKeyState.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87710648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IPlatformScreen.h; + path = lib/synergy/IPlatformScreen.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87720648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = IPrimaryScreen.cpp; + path = lib/synergy/IPrimaryScreen.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87730648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IPrimaryScreen.h; + path = lib/synergy/IPrimaryScreen.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87740648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = IScreen.cpp; + path = lib/synergy/IScreen.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87750648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IScreen.h; + path = lib/synergy/IScreen.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87760648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IScreenSaver.h; + path = lib/synergy/IScreenSaver.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87770648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = ISecondaryScreen.h; + path = lib/synergy/ISecondaryScreen.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87780648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = KeyTypes.h; + path = lib/synergy/KeyTypes.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87790648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MouseTypes.h; + path = lib/synergy/MouseTypes.h; + refType = 4; + sourceTree = ""; + }; + 4C5E877A0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = OptionTypes.h; + path = lib/synergy/OptionTypes.h; + refType = 4; + sourceTree = ""; + }; + 4C5E877B0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = ProtocolTypes.h; + path = lib/synergy/ProtocolTypes.h; + refType = 4; + sourceTree = ""; + }; + 4C5E877C0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = XScreen.cpp; + path = lib/synergy/XScreen.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E877D0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = XScreen.h; + path = lib/synergy/XScreen.h; + refType = 4; + sourceTree = ""; + }; + 4C5E877E0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = XSynergy.cpp; + path = lib/synergy/XSynergy.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E877F0648C8BD003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = XSynergy.h; + path = lib/synergy/XSynergy.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87800648C8BD003C637B = { + fileRef = 4C5E875F0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87810648C8BD003C637B = { + fileRef = 4C5E87600648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87820648C8BD003C637B = { + fileRef = 4C5E87610648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87830648C8BD003C637B = { + fileRef = 4C5E87620648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87840648C8BD003C637B = { + fileRef = 4C5E87630648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87850648C8BD003C637B = { + fileRef = 4C5E87640648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87860648C8BD003C637B = { + fileRef = 4C5E87650648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87870648C8BD003C637B = { + fileRef = 4C5E87660648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87880648C8BD003C637B = { + fileRef = 4C5E87670648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87890648C8BD003C637B = { + fileRef = 4C5E87680648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E878A0648C8BD003C637B = { + fileRef = 4C5E87690648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E878B0648C8BD003C637B = { + fileRef = 4C5E876A0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E878C0648C8BD003C637B = { + fileRef = 4C5E876B0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E878D0648C8BD003C637B = { + fileRef = 4C5E876C0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E878E0648C8BD003C637B = { + fileRef = 4C5E876D0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E878F0648C8BD003C637B = { + fileRef = 4C5E876E0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87900648C8BD003C637B = { + fileRef = 4C5E876F0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87910648C8BD003C637B = { + fileRef = 4C5E87700648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87920648C8BD003C637B = { + fileRef = 4C5E87710648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87930648C8BD003C637B = { + fileRef = 4C5E87720648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87940648C8BD003C637B = { + fileRef = 4C5E87730648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87950648C8BD003C637B = { + fileRef = 4C5E87740648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87960648C8BD003C637B = { + fileRef = 4C5E87750648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87970648C8BD003C637B = { + fileRef = 4C5E87760648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87980648C8BD003C637B = { + fileRef = 4C5E87770648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87990648C8BD003C637B = { + fileRef = 4C5E87780648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E879A0648C8BD003C637B = { + fileRef = 4C5E87790648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E879B0648C8BD003C637B = { + fileRef = 4C5E877A0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E879C0648C8BD003C637B = { + fileRef = 4C5E877B0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E879D0648C8BD003C637B = { + fileRef = 4C5E877C0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E879E0648C8BD003C637B = { + fileRef = 4C5E877D0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E879F0648C8BD003C637B = { + fileRef = 4C5E877E0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87A00648C8BD003C637B = { + fileRef = 4C5E877F0648C8BD003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87A10648C913003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = COSXClipboard.cpp; + path = lib/platform/COSXClipboard.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87A20648C913003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = COSXClipboard.h; + path = lib/platform/COSXClipboard.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87A30648C913003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = COSXEventQueueBuffer.cpp; + path = lib/platform/COSXEventQueueBuffer.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87A40648C913003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = COSXEventQueueBuffer.h; + path = lib/platform/COSXEventQueueBuffer.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87A50648C913003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = COSXKeyState.cpp; + path = lib/platform/COSXKeyState.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87A60648C913003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = COSXKeyState.h; + path = lib/platform/COSXKeyState.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87A70648C913003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = COSXScreen.cpp; + path = lib/platform/COSXScreen.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87A80648C913003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = COSXScreen.h; + path = lib/platform/COSXScreen.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87A90648C913003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = COSXScreenSaver.cpp; + path = lib/platform/COSXScreenSaver.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87AA0648C913003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = COSXScreenSaver.h; + path = lib/platform/COSXScreenSaver.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87AD0648C913003C637B = { + fileRef = 4C5E87A10648C913003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87AE0648C913003C637B = { + fileRef = 4C5E87A20648C913003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87AF0648C913003C637B = { + fileRef = 4C5E87A30648C913003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87B00648C913003C637B = { + fileRef = 4C5E87A40648C913003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87B10648C913003C637B = { + fileRef = 4C5E87A50648C913003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87B20648C913003C637B = { + fileRef = 4C5E87A60648C913003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87B30648C913003C637B = { + fileRef = 4C5E87A70648C913003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87B40648C913003C637B = { + fileRef = 4C5E87A80648C913003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87B50648C913003C637B = { + fileRef = 4C5E87A90648C913003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87B60648C913003C637B = { + fileRef = 4C5E87AA0648C913003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87B90648C969003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CClient.cpp; + path = lib/client/CClient.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87BA0648C969003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CClient.h; + path = lib/client/CClient.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87BB0648C969003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CServerProxy.cpp; + path = lib/client/CServerProxy.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87BC0648C969003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CServerProxy.h; + path = lib/client/CServerProxy.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87BD0648C969003C637B = { + fileRef = 4C5E87B90648C969003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87BE0648C969003C637B = { + fileRef = 4C5E87BA0648C969003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87BF0648C969003C637B = { + fileRef = 4C5E87BB0648C969003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87C00648C969003C637B = { + fileRef = 4C5E87BC0648C969003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87C10648C9D2003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CClientTaskBarReceiver.cpp; + path = cmd/synergyc/CClientTaskBarReceiver.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87C20648C9D2003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CClientTaskBarReceiver.h; + path = cmd/synergyc/CClientTaskBarReceiver.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87C30648C9D2003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = COSXClientTaskBarReceiver.cpp; + path = cmd/synergyc/COSXClientTaskBarReceiver.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87C40648C9D2003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = COSXClientTaskBarReceiver.h; + path = cmd/synergyc/COSXClientTaskBarReceiver.h; + refType = 4; + sourceTree = ""; + }; + 4C5E87C50648C9D2003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = synergyc.cpp; + path = cmd/synergyc/synergyc.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87C60648C9D2003C637B = { + fileRef = 4C5E87C10648C9D2003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87C70648C9D2003C637B = { + fileRef = 4C5E87C20648C9D2003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87C80648C9D2003C637B = { + fileRef = 4C5E87C30648C9D2003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87C90648C9D2003C637B = { + fileRef = 4C5E87C40648C9D2003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87CA0648C9D2003C637B = { + fileRef = 4C5E87C50648C9D2003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87CF0648CA4B003C637B = { + fileRef = 4C5E868D0648C2ED003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4C5E87D00648CA75003C637B = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = XArch.cpp; + path = lib/arch/XArch.cpp; + refType = 4; + sourceTree = ""; + }; + 4C5E87D10648CA75003C637B = { + fileRef = 4C5E87D00648CA75003C637B; + isa = PBXBuildFile; + settings = { + }; + }; + 4CB43775063E406A00969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E87BE0648C969003C637B, + 4C5E87C00648C969003C637B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43776063E406A00969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E87BD0648C969003C637B, + 4C5E87BF0648C969003C637B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43777063E406A00969041 = { + buildPhases = ( + 4CB43775063E406A00969041, + 4CB43776063E406A00969041, + ); + buildRules = ( + ); + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform"; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = client; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = client; + productName = client; + productReference = 4CB43778063E406A00969041; + productType = "com.apple.product-type.library.static"; + }; + 4CB43778063E406A00969041 = { + explicitFileType = archive.ar; + includeInIndex = 0; + isa = PBXFileReference; + path = libclient.a; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4CB43779063E40B600969041 = { + children = ( + 4C5E87B90648C969003C637B, + 4C5E87BA0648C969003C637B, + 4C5E87BB0648C969003C637B, + 4C5E87BC0648C969003C637B, + ); + isa = PBXGroup; + name = Client; + refType = 4; + sourceTree = ""; + }; + 4CB4378B063E432C00969041 = { + children = ( + 4C5E86920648C412003C637B, + 4C5E86930648C412003C637B, + 4C5E86C00648C653003C637B, + 4C5E86C10648C653003C637B, + 4C5E86940648C412003C637B, + 4C5E86950648C412003C637B, + 4C5E86960648C412003C637B, + 4C5E86970648C412003C637B, + 4C5E86980648C412003C637B, + 4C5E86990648C412003C637B, + 4C5E869A0648C412003C637B, + 4C5E869B0648C412003C637B, + 4C5E869C0648C412003C637B, + 4C5E869D0648C412003C637B, + 4C5E869E0648C412003C637B, + 4C5E869F0648C412003C637B, + 4C5E86A00648C412003C637B, + 4C5E86A10648C412003C637B, + 4C5E86A20648C412003C637B, + 4C5E86A30648C412003C637B, + 4C5E86C40648C6B7003C637B, + 4C5E86C50648C6B7003C637B, + 4C5E86A40648C412003C637B, + 4C5E86A50648C412003C637B, + 4C5E87D00648CA75003C637B, + 4C5E86A70648C412003C637B, + 4C5E86A80648C412003C637B, + ); + isa = PBXGroup; + name = Arch; + refType = 4; + sourceTree = ""; + }; + 4CB437B2063E443800969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E86AA0648C412003C637B, + 4C5E86AC0648C412003C637B, + 4C5E86AE0648C412003C637B, + 4C5E86B00648C412003C637B, + 4C5E86B20648C412003C637B, + 4C5E86B40648C412003C637B, + 4C5E86B60648C412003C637B, + 4C5E86B80648C412003C637B, + 4C5E86BA0648C412003C637B, + 4C5E86BC0648C412003C637B, + 4C5E86BF0648C412003C637B, + 4C5E86C30648C653003C637B, + 4C5E86C70648C6B7003C637B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437B3063E443800969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E86A90648C412003C637B, + 4C5E86AB0648C412003C637B, + 4C5E86AD0648C412003C637B, + 4C5E86AF0648C412003C637B, + 4C5E86B10648C412003C637B, + 4C5E86B30648C412003C637B, + 4C5E86B50648C412003C637B, + 4C5E86B70648C412003C637B, + 4C5E86B90648C412003C637B, + 4C5E86BB0648C412003C637B, + 4C5E86BE0648C412003C637B, + 4C5E86C20648C653003C637B, + 4C5E86C60648C6B7003C637B, + 4C5E87D10648CA75003C637B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437B4063E443800969041 = { + buildPhases = ( + 4CB437B2063E443800969041, + 4CB437B3063E443800969041, + ); + buildRules = ( + ); + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = ./lib/common; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRECOMP_DESTINATION_DIR = lib/common/; + PRODUCT_NAME = arch; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = arch; + productName = arch; + productReference = 4CB437B5063E443800969041; + productType = "com.apple.product-type.library.static"; + }; + 4CB437B5063E443800969041 = { + explicitFileType = archive.ar; + includeInIndex = 0; + isa = PBXFileReference; + path = libarch.a; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4CB437CE063E44C200969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E86EE0648C7B9003C637B, + 4C5E86F00648C7B9003C637B, + 4C5E86F20648C7B9003C637B, + 4C5E86F40648C7B9003C637B, + 4C5E86F60648C7B9003C637B, + 4C5E86F70648C7B9003C637B, + 4C5E86F90648C7B9003C637B, + 4C5E86FB0648C7B9003C637B, + 4C5E86FC0648C7B9003C637B, + 4C5E86FE0648C7B9003C637B, + 4C5E87000648C7B9003C637B, + 4C5E87010648C7B9003C637B, + 4C5E87030648C7B9003C637B, + 4C5E87040648C7B9003C637B, + 4C5E87050648C7B9003C637B, + 4C5E87060648C7B9003C637B, + 4C5E87080648C7B9003C637B, + 4C5E87090648C7B9003C637B, + 4C5E870A0648C7B9003C637B, + 4C5E870C0648C7B9003C637B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437CF063E44C200969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E86ED0648C7B9003C637B, + 4C5E86EF0648C7B9003C637B, + 4C5E86F10648C7B9003C637B, + 4C5E86F30648C7B9003C637B, + 4C5E86F50648C7B9003C637B, + 4C5E86F80648C7B9003C637B, + 4C5E86FA0648C7B9003C637B, + 4C5E86FD0648C7B9003C637B, + 4C5E86FF0648C7B9003C637B, + 4C5E87020648C7B9003C637B, + 4C5E87070648C7B9003C637B, + 4C5E870B0648C7B9003C637B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437D0063E44C200969041 = { + buildPhases = ( + 4CB437CE063E44C200969041, + 4CB437CF063E44C200969041, + ); + buildRules = ( + ); + buildSettings = { + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = "./lib/common ./lib/arch"; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = base; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = base; + productName = base; + productReference = 4CB437D1063E44C200969041; + productType = "com.apple.product-type.library.static"; + }; + 4CB437D1063E44C200969041 = { + explicitFileType = archive.ar; + includeInIndex = 0; + isa = PBXFileReference; + path = libbase.a; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4CB437D2063E44E400969041 = { + children = ( + 4C5E86CD0648C7B9003C637B, + 4C5E86CE0648C7B9003C637B, + 4C5E86CF0648C7B9003C637B, + 4C5E86D00648C7B9003C637B, + 4C5E86D10648C7B9003C637B, + 4C5E86D20648C7B9003C637B, + 4C5E86D30648C7B9003C637B, + 4C5E86D40648C7B9003C637B, + 4C5E86D50648C7B9003C637B, + 4C5E86D60648C7B9003C637B, + 4C5E86D70648C7B9003C637B, + 4C5E86D80648C7B9003C637B, + 4C5E86D90648C7B9003C637B, + 4C5E86DA0648C7B9003C637B, + 4C5E86DB0648C7B9003C637B, + 4C5E86DC0648C7B9003C637B, + 4C5E86DD0648C7B9003C637B, + 4C5E86DE0648C7B9003C637B, + 4C5E86DF0648C7B9003C637B, + 4C5E86E00648C7B9003C637B, + 4C5E86E10648C7B9003C637B, + 4C5E86E20648C7B9003C637B, + 4C5E86E30648C7B9003C637B, + 4C5E86E40648C7B9003C637B, + 4C5E86E50648C7B9003C637B, + 4C5E86E60648C7B9003C637B, + 4C5E86E70648C7B9003C637B, + 4C5E86E80648C7B9003C637B, + 4C5E86E90648C7B9003C637B, + 4C5E86EA0648C7B9003C637B, + 4C5E86EB0648C7B9003C637B, + 4C5E86EC0648C7B9003C637B, + ); + isa = PBXGroup; + name = Base; + refType = 4; + sourceTree = ""; + }; + 4CB437FB063E45D700969041 = { + children = ( + 4C5E870D0648C809003C637B, + 4C5E870E0648C809003C637B, + 4C5E870F0648C809003C637B, + 4C5E87100648C809003C637B, + 4C5E87110648C809003C637B, + 4C5E87120648C809003C637B, + 4C5E87130648C809003C637B, + 4C5E87140648C809003C637B, + 4C5E87150648C809003C637B, + 4C5E87160648C809003C637B, + 4C5E87170648C809003C637B, + ); + isa = PBXGroup; + name = MT; + refType = 4; + sourceTree = ""; + }; + 4CB437FE063E45F200969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E87190648C809003C637B, + 4C5E871B0648C809003C637B, + 4C5E871D0648C809003C637B, + 4C5E871F0648C809003C637B, + 4C5E87210648C809003C637B, + 4C5E87220648C809003C637B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437FF063E45F200969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E87180648C809003C637B, + 4C5E871A0648C809003C637B, + 4C5E871C0648C809003C637B, + 4C5E871E0648C809003C637B, + 4C5E87200648C809003C637B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43800063E45F200969041 = { + buildPhases = ( + 4CB437FE063E45F200969041, + 4CB437FF063E45F200969041, + ); + buildRules = ( + ); + buildSettings = { + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base"; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = mt; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = mt; + productName = mt; + productReference = 4CB43801063E45F200969041; + productType = "com.apple.product-type.library.static"; + }; + 4CB43801063E45F200969041 = { + explicitFileType = archive.ar; + includeInIndex = 0; + isa = PBXFileReference; + path = libmt.a; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4CB4381E063E469300969041 = { + children = ( + 4C5E87230648C83C003C637B, + 4C5E87240648C83C003C637B, + 4C5E87250648C83C003C637B, + 4C5E87260648C83C003C637B, + 4C5E87270648C83C003C637B, + 4C5E87280648C83C003C637B, + 4C5E87290648C83C003C637B, + 4C5E872A0648C83C003C637B, + 4C5E872B0648C83C003C637B, + ); + isa = PBXGroup; + name = IO; + refType = 4; + sourceTree = ""; + }; + 4CB43821063E46AB00969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E872D0648C83C003C637B, + 4C5E872F0648C83C003C637B, + 4C5E87310648C83C003C637B, + 4C5E87320648C83C003C637B, + 4C5E87340648C83C003C637B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43822063E46AB00969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E872C0648C83C003C637B, + 4C5E872E0648C83C003C637B, + 4C5E87300648C83C003C637B, + 4C5E87330648C83C003C637B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43823063E46AB00969041 = { + buildPhases = ( + 4CB43821063E46AB00969041, + 4CB43822063E46AB00969041, + ); + buildRules = ( + ); + buildSettings = { + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt"; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = io; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = io; + productName = io; + productReference = 4CB43824063E46AB00969041; + productType = "com.apple.product-type.library.static"; + }; + 4CB43824063E46AB00969041 = { + explicitFileType = archive.ar; + includeInIndex = 0; + isa = PBXFileReference; + path = libio.a; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4CB43845063E475800969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E874B0648C870003C637B, + 4C5E874D0648C870003C637B, + 4C5E874F0648C870003C637B, + 4C5E87510648C870003C637B, + 4C5E87530648C870003C637B, + 4C5E87550648C870003C637B, + 4C5E87570648C870003C637B, + 4C5E87590648C870003C637B, + 4C5E875A0648C870003C637B, + 4C5E875B0648C870003C637B, + 4C5E875C0648C870003C637B, + 4C5E875E0648C870003C637B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43846063E475800969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E874A0648C870003C637B, + 4C5E874C0648C870003C637B, + 4C5E874E0648C870003C637B, + 4C5E87500648C870003C637B, + 4C5E87520648C870003C637B, + 4C5E87540648C870003C637B, + 4C5E87560648C870003C637B, + 4C5E87580648C870003C637B, + 4C5E875D0648C870003C637B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43847063E475800969041 = { + buildPhases = ( + 4CB43845063E475800969041, + 4CB43846063E475800969041, + ); + buildRules = ( + ); + buildSettings = { + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io"; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = net; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = net; + productName = net; + productReference = 4CB43848063E475800969041; + productType = "com.apple.product-type.library.static"; + }; + 4CB43848063E475800969041 = { + explicitFileType = archive.ar; + includeInIndex = 0; + isa = PBXFileReference; + path = libnet.a; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4CB43849063E478900969041 = { + children = ( + 4C5E87350648C870003C637B, + 4C5E87360648C870003C637B, + 4C5E87370648C870003C637B, + 4C5E87380648C870003C637B, + 4C5E87390648C870003C637B, + 4C5E873A0648C870003C637B, + 4C5E873B0648C870003C637B, + 4C5E873C0648C870003C637B, + 4C5E873D0648C870003C637B, + 4C5E873E0648C870003C637B, + 4C5E873F0648C870003C637B, + 4C5E87400648C870003C637B, + 4C5E87410648C870003C637B, + 4C5E87420648C870003C637B, + 4C5E87430648C870003C637B, + 4C5E87440648C870003C637B, + 4C5E87450648C870003C637B, + 4C5E87460648C870003C637B, + 4C5E87470648C870003C637B, + 4C5E87480648C870003C637B, + 4C5E87490648C870003C637B, + ); + isa = PBXGroup; + name = Net; + refType = 4; + sourceTree = ""; + }; + 4CB43866063E47C800969041 = { + children = ( + 4C5E875F0648C8BD003C637B, + 4C5E87600648C8BD003C637B, + 4C5E87610648C8BD003C637B, + 4C5E87620648C8BD003C637B, + 4C5E87630648C8BD003C637B, + 4C5E87640648C8BD003C637B, + 4C5E87650648C8BD003C637B, + 4C5E87660648C8BD003C637B, + 4C5E87670648C8BD003C637B, + 4C5E87680648C8BD003C637B, + 4C5E87690648C8BD003C637B, + 4C5E876A0648C8BD003C637B, + 4C5E876B0648C8BD003C637B, + 4C5E876C0648C8BD003C637B, + 4C5E876D0648C8BD003C637B, + 4C5E876E0648C8BD003C637B, + 4C5E876F0648C8BD003C637B, + 4C5E87700648C8BD003C637B, + 4C5E87710648C8BD003C637B, + 4C5E87720648C8BD003C637B, + 4C5E87730648C8BD003C637B, + 4C5E87740648C8BD003C637B, + 4C5E87750648C8BD003C637B, + 4C5E87760648C8BD003C637B, + 4C5E87770648C8BD003C637B, + 4C5E87780648C8BD003C637B, + 4C5E87790648C8BD003C637B, + 4C5E877A0648C8BD003C637B, + 4C5E877B0648C8BD003C637B, + 4C5E877C0648C8BD003C637B, + 4C5E877D0648C8BD003C637B, + 4C5E877E0648C8BD003C637B, + 4C5E877F0648C8BD003C637B, + ); + isa = PBXGroup; + name = Synergy; + refType = 4; + sourceTree = ""; + }; + 4CB4386B063E47F800969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E87810648C8BD003C637B, + 4C5E87830648C8BD003C637B, + 4C5E87840648C8BD003C637B, + 4C5E87860648C8BD003C637B, + 4C5E87880648C8BD003C637B, + 4C5E878A0648C8BD003C637B, + 4C5E878C0648C8BD003C637B, + 4C5E878D0648C8BD003C637B, + 4C5E878F0648C8BD003C637B, + 4C5E87910648C8BD003C637B, + 4C5E87920648C8BD003C637B, + 4C5E87940648C8BD003C637B, + 4C5E87960648C8BD003C637B, + 4C5E87970648C8BD003C637B, + 4C5E87980648C8BD003C637B, + 4C5E87990648C8BD003C637B, + 4C5E879A0648C8BD003C637B, + 4C5E879B0648C8BD003C637B, + 4C5E879C0648C8BD003C637B, + 4C5E879E0648C8BD003C637B, + 4C5E87A00648C8BD003C637B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB4386C063E47F800969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E87800648C8BD003C637B, + 4C5E87820648C8BD003C637B, + 4C5E87850648C8BD003C637B, + 4C5E87870648C8BD003C637B, + 4C5E87890648C8BD003C637B, + 4C5E878B0648C8BD003C637B, + 4C5E878E0648C8BD003C637B, + 4C5E87900648C8BD003C637B, + 4C5E87930648C8BD003C637B, + 4C5E87950648C8BD003C637B, + 4C5E879D0648C8BD003C637B, + 4C5E879F0648C8BD003C637B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB4386D063E47F800969041 = { + buildPhases = ( + 4CB4386B063E47F800969041, + 4CB4386C063E47F800969041, + ); + buildRules = ( + ); + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net"; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = synergy; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = synergy; + productName = synergy; + productReference = 4CB4386E063E47F800969041; + productType = "com.apple.product-type.library.static"; + }; + 4CB4386E063E47F800969041 = { + explicitFileType = archive.ar; + includeInIndex = 0; + isa = PBXFileReference; + path = libsynergy.a; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4CB438AD063E487200969041 = { + children = ( + 4C5E87A10648C913003C637B, + 4C5E87A20648C913003C637B, + 4CD0348F0650B6F100525ED1, + 4CD034900650B6F100525ED1, + 4CD034910650B6F100525ED1, + 4CD034920650B6F100525ED1, + 4CD034930650B6F100525ED1, + 4CD034940650B6F100525ED1, + 4C5E87A30648C913003C637B, + 4C5E87A40648C913003C637B, + 4C5E87A50648C913003C637B, + 4C5E87A60648C913003C637B, + 4C5E87A70648C913003C637B, + 4C5E87A80648C913003C637B, + 4C5E87A90648C913003C637B, + 4C5E87AA0648C913003C637B, + ); + isa = PBXGroup; + name = Platform; + refType = 4; + sourceTree = ""; + }; + 4CB438AE063E488600969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E87AE0648C913003C637B, + 4C5E87B00648C913003C637B, + 4C5E87B20648C913003C637B, + 4C5E87B40648C913003C637B, + 4C5E87B60648C913003C637B, + 4CD034960650B6F100525ED1, + 4CD034980650B6F100525ED1, + 4CD0349A0650B6F100525ED1, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB438AF063E488600969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E87AD0648C913003C637B, + 4C5E87AF0648C913003C637B, + 4C5E87B10648C913003C637B, + 4C5E87B30648C913003C637B, + 4C5E87B50648C913003C637B, + 4CD034950650B6F100525ED1, + 4CD034970650B6F100525ED1, + 4CD034990650B6F100525ED1, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB438B0063E488600969041 = { + buildPhases = ( + 4CB438AE063E488600969041, + 4CB438AF063E488600969041, + ); + buildRules = ( + ); + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io /lib/synergy"; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = platform; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = platform; + productName = platform; + productReference = 4CB438B1063E488600969041; + productType = "com.apple.product-type.library.static"; + }; + 4CB438B1063E488600969041 = { + explicitFileType = archive.ar; + includeInIndex = 0; + isa = PBXFileReference; + path = libplatform.a; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4CB43911063E497700969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E87C70648C9D2003C637B, + 4C5E87C90648C9D2003C637B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43912063E497700969041 = { + buildActionMask = 2147483647; + files = ( + 4C5E87C60648C9D2003C637B, + 4C5E87C80648C9D2003C637B, + 4C5E87CA0648C9D2003C637B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43913063E497700969041 = { + buildPhases = ( + 4CB43911063E497700969041, + 4CB43912063E497700969041, + 4CB43930063E4B4300969041, + ); + buildRules = ( + ); + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform ./lib/client"; + INSTALL_PATH = /usr/local/bin; + LIBRARY_SEARCH_PATHS = ""; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = synergyc; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + 4CB43948063E4E1600969041, + 4C5E86900648C32E003C637B, + 4CB4394A063E4E1600969041, + 4CB4394C063E4E1600969041, + 4CB4394E063E4E1600969041, + 4CB43950063E4E1600969041, + 4CB43952063E4E1600969041, + 4CB43954063E4E1600969041, + 4CB43946063E4E1600969041, + ); + isa = PBXNativeTarget; + name = synergyc; + productName = synergyc; + productReference = 4CB43914063E497700969041; + productType = "com.apple.product-type.tool"; + }; + 4CB43914063E497700969041 = { + explicitFileType = "compiled.mach-o.executable"; + includeInIndex = 0; + isa = PBXFileReference; + path = synergyc; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4CB43916063E4A1A00969041 = { + children = ( + 4C5E87C10648C9D2003C637B, + 4C5E87C20648C9D2003C637B, + 4C5E87C30648C9D2003C637B, + 4C5E87C40648C9D2003C637B, + 4C5E87C50648C9D2003C637B, + ); + isa = PBXGroup; + name = SynergyC; + refType = 4; + sourceTree = ""; + }; + 4CB43923063E4B1300969041 = { + isa = PBXFileReference; + lastKnownFileType = wrapper.framework; + name = Carbon.framework; + path = ../../../../../System/Library/Frameworks/Carbon.framework; + refType = 4; + sourceTree = ""; + }; + 4CB43930063E4B4300969041 = { + buildActionMask = 2147483647; + files = ( + 4CB43934063E4B4A00969041, + 4CDF389A063E561B007B20A1, + 4C5E87CF0648CA4B003C637B, + 4CDF389B063E561B007B20A1, + 4CDF389C063E561B007B20A1, + 4CDF389D063E561B007B20A1, + 4CDF389E063E561B007B20A1, + 4CDF389F063E561B007B20A1, + 4CDF38A0063E561B007B20A1, + 4CDF38A1063E561B007B20A1, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43934063E4B4A00969041 = { + fileRef = 4CB43923063E4B1300969041; + isa = PBXBuildFile; + settings = { + }; + }; + 4CB43945063E4E1600969041 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB43777063E406A00969041; + remoteInfo = client; + }; + 4CB43946063E4E1600969041 = { + isa = PBXTargetDependency; + target = 4CB43777063E406A00969041; + targetProxy = 4CB43945063E4E1600969041; + }; + 4CB43947063E4E1600969041 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB437B4063E443800969041; + remoteInfo = arch; + }; + 4CB43948063E4E1600969041 = { + isa = PBXTargetDependency; + target = 4CB437B4063E443800969041; + targetProxy = 4CB43947063E4E1600969041; + }; + 4CB43949063E4E1600969041 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB437D0063E44C200969041; + remoteInfo = base; + }; + 4CB4394A063E4E1600969041 = { + isa = PBXTargetDependency; + target = 4CB437D0063E44C200969041; + targetProxy = 4CB43949063E4E1600969041; + }; + 4CB4394B063E4E1600969041 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB43800063E45F200969041; + remoteInfo = mt; + }; + 4CB4394C063E4E1600969041 = { + isa = PBXTargetDependency; + target = 4CB43800063E45F200969041; + targetProxy = 4CB4394B063E4E1600969041; + }; + 4CB4394D063E4E1600969041 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB43823063E46AB00969041; + remoteInfo = io; + }; + 4CB4394E063E4E1600969041 = { + isa = PBXTargetDependency; + target = 4CB43823063E46AB00969041; + targetProxy = 4CB4394D063E4E1600969041; + }; + 4CB4394F063E4E1600969041 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB43847063E475800969041; + remoteInfo = net; + }; + 4CB43950063E4E1600969041 = { + isa = PBXTargetDependency; + target = 4CB43847063E475800969041; + targetProxy = 4CB4394F063E4E1600969041; + }; + 4CB43951063E4E1600969041 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB4386D063E47F800969041; + remoteInfo = synergy; + }; + 4CB43952063E4E1600969041 = { + isa = PBXTargetDependency; + target = 4CB4386D063E47F800969041; + targetProxy = 4CB43951063E4E1600969041; + }; + 4CB43953063E4E1600969041 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB438B0063E488600969041; + remoteInfo = platform; + }; + 4CB43954063E4E1600969041 = { + isa = PBXTargetDependency; + target = 4CB438B0063E488600969041; + targetProxy = 4CB43953063E4E1600969041; + }; + 4CD033E80650965F00525ED1 = { + buildPhases = ( + ); + buildSettings = { + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = ALL; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + 4CD033EA0650968500525ED1, + 4CD033EC0650968500525ED1, + ); + isa = PBXAggregateTarget; + name = ALL; + productName = ALL; + }; + 4CD033E90650968500525ED1 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4CB43913063E497700969041; + remoteInfo = synergyc; + }; + 4CD033EA0650968500525ED1 = { + isa = PBXTargetDependency; + target = 4CB43913063E497700969041; + targetProxy = 4CD033E90650968500525ED1; + }; + 4CD033EB0650968500525ED1 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 4C537F0C064E286700D3815C; + remoteInfo = synergys; + }; + 4CD033EC0650968500525ED1 = { + isa = PBXTargetDependency; + target = 4C537F0C064E286700D3815C; + targetProxy = 4CD033EB0650968500525ED1; + }; + 4CD0348F0650B6F100525ED1 = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = COSXClipboardAnyTextConverter.cpp; + path = lib/platform/COSXClipboardAnyTextConverter.cpp; + refType = 4; + sourceTree = ""; + }; + 4CD034900650B6F100525ED1 = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = COSXClipboardAnyTextConverter.h; + path = lib/platform/COSXClipboardAnyTextConverter.h; + refType = 4; + sourceTree = ""; + }; + 4CD034910650B6F100525ED1 = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = COSXClipboardTextConverter.cpp; + path = lib/platform/COSXClipboardTextConverter.cpp; + refType = 4; + sourceTree = ""; + }; + 4CD034920650B6F100525ED1 = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = COSXClipboardTextConverter.h; + path = lib/platform/COSXClipboardTextConverter.h; + refType = 4; + sourceTree = ""; + }; + 4CD034930650B6F100525ED1 = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = COSXClipboardUTF16Converter.cpp; + path = lib/platform/COSXClipboardUTF16Converter.cpp; + refType = 4; + sourceTree = ""; + }; + 4CD034940650B6F100525ED1 = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = COSXClipboardUTF16Converter.h; + path = lib/platform/COSXClipboardUTF16Converter.h; + refType = 4; + sourceTree = ""; + }; + 4CD034950650B6F100525ED1 = { + fileRef = 4CD0348F0650B6F100525ED1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CD034960650B6F100525ED1 = { + fileRef = 4CD034900650B6F100525ED1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CD034970650B6F100525ED1 = { + fileRef = 4CD034910650B6F100525ED1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CD034980650B6F100525ED1 = { + fileRef = 4CD034920650B6F100525ED1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CD034990650B6F100525ED1 = { + fileRef = 4CD034930650B6F100525ED1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CD0349A0650B6F100525ED1 = { + fileRef = 4CD034940650B6F100525ED1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CDF3892063E561B007B20A1 = { + isa = PBXFileReference; + lastKnownFileType = archive.ar; + name = libarch.a; + path = build/libarch.a; + refType = 4; + sourceTree = ""; + }; + 4CDF3893063E561B007B20A1 = { + isa = PBXFileReference; + lastKnownFileType = archive.ar; + name = libbase.a; + path = build/libbase.a; + refType = 4; + sourceTree = ""; + }; + 4CDF3894063E561B007B20A1 = { + isa = PBXFileReference; + lastKnownFileType = archive.ar; + name = libclient.a; + path = build/libclient.a; + refType = 4; + sourceTree = ""; + }; + 4CDF3895063E561B007B20A1 = { + isa = PBXFileReference; + lastKnownFileType = archive.ar; + name = libio.a; + path = build/libio.a; + refType = 4; + sourceTree = ""; + }; + 4CDF3896063E561B007B20A1 = { + isa = PBXFileReference; + lastKnownFileType = archive.ar; + name = libmt.a; + path = build/libmt.a; + refType = 4; + sourceTree = ""; + }; + 4CDF3897063E561B007B20A1 = { + isa = PBXFileReference; + lastKnownFileType = archive.ar; + name = libnet.a; + path = build/libnet.a; + refType = 4; + sourceTree = ""; + }; + 4CDF3898063E561B007B20A1 = { + isa = PBXFileReference; + lastKnownFileType = archive.ar; + name = libplatform.a; + path = build/libplatform.a; + refType = 4; + sourceTree = ""; + }; + 4CDF3899063E561B007B20A1 = { + isa = PBXFileReference; + lastKnownFileType = archive.ar; + name = libsynergy.a; + path = build/libsynergy.a; + refType = 4; + sourceTree = ""; + }; + 4CDF389A063E561B007B20A1 = { + fileRef = 4CDF3892063E561B007B20A1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CDF389B063E561B007B20A1 = { + fileRef = 4CDF3893063E561B007B20A1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CDF389C063E561B007B20A1 = { + fileRef = 4CDF3894063E561B007B20A1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CDF389D063E561B007B20A1 = { + fileRef = 4CDF3895063E561B007B20A1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CDF389E063E561B007B20A1 = { + fileRef = 4CDF3896063E561B007B20A1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CDF389F063E561B007B20A1 = { + fileRef = 4CDF3897063E561B007B20A1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CDF38A0063E561B007B20A1 = { + fileRef = 4CDF3898063E561B007B20A1; + isa = PBXBuildFile; + settings = { + }; + }; + 4CDF38A1063E561B007B20A1 = { + fileRef = 4CDF3899063E561B007B20A1; + isa = PBXBuildFile; + settings = { + }; + }; + }; + rootObject = 08FB7793FE84155DC02AAC07; +} From 5a990691ca1d0403a459a8aa3532424a9257e479 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 15 May 2004 19:43:33 +0000 Subject: [PATCH 668/807] Avoided duplicate logging of screen size on win32. --- lib/platform/CMSWindowsScreen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 4a65e810..991a2568 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -1168,6 +1168,8 @@ CMSWindowsScreen::onDisplayChange() // send new screen info sendEvent(getShapeChangedEvent()); + + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "")); } return true; @@ -1259,7 +1261,6 @@ CMSWindowsScreen::updateScreenShape() // check for multiple monitors m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || m_h != GetSystemMetrics(SM_CYSCREEN)); - LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "")); // tell the desks m_desks->setShape(m_x, m_y, m_w, m_h, m_xCenter, m_yCenter, m_multimon); From c9046a9557544efc0fe27df5241853f36fdaa318 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 15 May 2004 19:44:05 +0000 Subject: [PATCH 669/807] Changed version to 1.1.6. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index dc451ded..8f5d4b52 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=1 -RELEASE_VERSION=5 +RELEASE_VERSION=6 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 2e2adcaa..baead10e 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.5" +# define VERSION "1.1.6" #endif // important strings From 62b5cefb6494e9abbf00674fe9ac87248aeb0489 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 May 2004 18:02:49 +0000 Subject: [PATCH 670/807] Changed VC++ projects to put release targets in ./build, debug targets in ./debug, and intermediate files under ./gen. --- all.dsp | 8 ++++---- cmd/exec.dsp | 8 ++++---- cmd/launcher/launcher.dsp | 16 ++++++++-------- cmd/synergyc/synergyc.dsp | 12 ++++++------ cmd/synergys/synergys.dsp | 12 ++++++------ dist/nullsoft/installer.dsp | 12 +----------- dist/nullsoft/synergy.nsi | 10 +++++----- lib/arch/arch.dsp | 12 ++++++------ lib/base/base.dsp | 12 ++++++------ lib/client/client.dsp | 12 ++++++------ lib/common/common.dsp | 12 ++++++------ lib/io/io.dsp | 12 ++++++------ lib/mt/mt.dsp | 12 ++++++------ lib/net/net.dsp | 12 ++++++------ lib/platform/makehook.dsp | 8 ++++---- lib/platform/platform.dsp | 12 ++++++------ lib/platform/synrgyhk.dsp | 12 ++++++------ lib/server/server.dsp | 12 ++++++------ lib/synergy/libsynergy.dsp | 12 ++++++------ 19 files changed, 104 insertions(+), 114 deletions(-) diff --git a/all.dsp b/all.dsp index 2358823a..741b7b6e 100644 --- a/all.dsp +++ b/all.dsp @@ -36,8 +36,8 @@ MTL=midl.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\build" +# PROP Intermediate_Dir "..\..\gen\build" # PROP Target_Dir "" !ELSEIF "$(CFG)" == "all - Win32 Debug" @@ -49,8 +49,8 @@ MTL=midl.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # PROP Target_Dir "" !ENDIF diff --git a/cmd/exec.dsp b/cmd/exec.dsp index 99fc73b7..e6e5fafc 100644 --- a/cmd/exec.dsp +++ b/cmd/exec.dsp @@ -36,8 +36,8 @@ MTL=midl.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\build" +# PROP Intermediate_Dir "..\..\gen\build" # PROP Target_Dir "" !ELSEIF "$(CFG)" == "exec - Win32 Debug" @@ -49,8 +49,8 @@ MTL=midl.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # PROP Target_Dir "" !ENDIF diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp index afb8ed89..b1ce64d4 100644 --- a/cmd/launcher/launcher.dsp +++ b/cmd/launcher/launcher.dsp @@ -38,12 +38,12 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../../Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\build" +# PROP Intermediate_Dir "..\..\gen\build" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\launcher.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -54,7 +54,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"../../Release/synergy.exe" +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"..\..\build\synergy.exe" # SUBTRACT LINK32 /map !ELSEIF "$(CFG)" == "launcher - Win32 Debug" @@ -66,12 +66,12 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../../Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\launcher.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -82,7 +82,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"../../Debug/synergy.exe" /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"..\..\debug\synergy.exe" /pdbtype:sept !ENDIF diff --git a/cmd/synergyc/synergyc.dsp b/cmd/synergyc/synergyc.dsp index 21842b62..ff793b14 100644 --- a/cmd/synergyc/synergyc.dsp +++ b/cmd/synergyc/synergyc.dsp @@ -38,12 +38,12 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../../Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\build" +# PROP Intermediate_Dir "..\..\gen\build" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c /Fd..\..\gen\build\synergyc.pdb # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -65,12 +65,12 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../../Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c /Fd..\..\gen\debug\synergyc.pdb # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/cmd/synergys/synergys.dsp b/cmd/synergys/synergys.dsp index 989ba8c5..7fdf1f01 100644 --- a/cmd/synergys/synergys.dsp +++ b/cmd/synergys/synergys.dsp @@ -38,12 +38,12 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../../Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\build" +# PROP Intermediate_Dir "..\..\gen\build" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\synergys.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -65,12 +65,12 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../../Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\synergys.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/dist/nullsoft/installer.dsp b/dist/nullsoft/installer.dsp index c101f53f..2fe0bd5d 100644 --- a/dist/nullsoft/installer.dsp +++ b/dist/nullsoft/installer.dsp @@ -43,7 +43,7 @@ CFG=installer - Win32 Debug # PROP Intermediate_Dir "." # PROP Cmd_Line "nmake /nologo /f "installer.mak"" # PROP Rebuild_Opt "/a" -# PROP Target_File "..\..\SynergyInstaller.exe" +# PROP Target_File "..\..\build\SynergyInstaller.exe" # PROP Bsc_Name "" # PROP Target_Dir "" NSIS=makensis @@ -94,17 +94,7 @@ SOURCE=.\dosify.c # Begin Source File SOURCE=.\installer.mak - -!IF "$(CFG)" == "installer - Win32 Release" - # PROP Intermediate_Dir "." - -!ELSEIF "$(CFG)" == "installer - Win32 Debug" - -# PROP Intermediate_Dir "." - -!ENDIF - # End Source File # Begin Source File diff --git a/dist/nullsoft/synergy.nsi b/dist/nullsoft/synergy.nsi index 2351176a..8ee33656 100644 --- a/dist/nullsoft/synergy.nsi +++ b/dist/nullsoft/synergy.nsi @@ -14,7 +14,7 @@ Name "Synergy" ; The file to write -OutFile "${DEPTH}/SynergyInstaller.exe" +OutFile "${DEPTH}\build\SynergyInstaller.exe" ; The default installation directory InstallDir $PROGRAMFILES\Synergy @@ -55,10 +55,10 @@ Section "Synergy (required)" SetOutPath $INSTDIR ; Put files there - File "${DEPTH}\Release\synergy.exe" - File "${DEPTH}\Release\synergyc.exe" - File "${DEPTH}\Release\synergys.exe" - File "${DEPTH}\Release\*.dll" + File "${DEPTH}\build\synergy.exe" + File "${DEPTH}\build\synergyc.exe" + File "${DEPTH}\build\synergys.exe" + File "${DEPTH}\build\*.dll" File AUTHORS.txt File BUGS.txt File COPYING.txt diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp index ad172bc0..17c05936 100644 --- a/lib/arch/arch.dsp +++ b/lib/arch/arch.dsp @@ -37,11 +37,11 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\gen\build" +# PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\arch.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -61,11 +61,11 @@ LIB32=link.exe -lib # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\gen\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\arch.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/base/base.dsp b/lib/base/base.dsp index a20e4fb4..f1eb30aa 100644 --- a/lib/base/base.dsp +++ b/lib/base/base.dsp @@ -37,11 +37,11 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\gen\build" +# PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\base.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -61,11 +61,11 @@ LIB32=link.exe -lib # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\gen\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\base.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/client/client.dsp b/lib/client/client.dsp index 9be31758..1764b7be 100644 --- a/lib/client/client.dsp +++ b/lib/client/client.dsp @@ -37,11 +37,11 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\gen\build" +# PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\client.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -61,11 +61,11 @@ LIB32=link.exe -lib # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\gen\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\client.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/common/common.dsp b/lib/common/common.dsp index a95905d2..55714483 100644 --- a/lib/common/common.dsp +++ b/lib/common/common.dsp @@ -37,11 +37,11 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\gen\build" +# PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\common.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -61,11 +61,11 @@ LIB32=link.exe -lib # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\gen\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\common.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/io/io.dsp b/lib/io/io.dsp index c1346d05..aea8848e 100644 --- a/lib/io/io.dsp +++ b/lib/io/io.dsp @@ -37,11 +37,11 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\gen\build" +# PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\io.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -61,11 +61,11 @@ LIB32=link.exe -lib # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\gen\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\io.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp index fd5b18fd..eae9e001 100644 --- a/lib/mt/mt.dsp +++ b/lib/mt/mt.dsp @@ -37,11 +37,11 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\gen\build" +# PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\mt.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -61,11 +61,11 @@ LIB32=link.exe -lib # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\gen\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\mt.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/net/net.dsp b/lib/net/net.dsp index 42314fc2..53b1a438 100644 --- a/lib/net/net.dsp +++ b/lib/net/net.dsp @@ -37,11 +37,11 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\gen\build" +# PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\net.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -61,11 +61,11 @@ LIB32=link.exe -lib # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\gen\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\net.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/platform/makehook.dsp b/lib/platform/makehook.dsp index f14304a9..c6528080 100644 --- a/lib/platform/makehook.dsp +++ b/lib/platform/makehook.dsp @@ -36,8 +36,8 @@ MTL=midl.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\build" +# PROP Intermediate_Dir "..\..\gen\build" # PROP Target_Dir "" !ELSEIF "$(CFG)" == "makehook - Win32 Debug" @@ -49,8 +49,8 @@ MTL=midl.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # PROP Target_Dir "" !ENDIF diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index ad302539..caab922e 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -37,11 +37,11 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\gen\build" +# PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\platform.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -61,11 +61,11 @@ LIB32=link.exe -lib # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\gen\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\platform.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp index 54fcc6a7..905c5aa6 100644 --- a/lib/platform/synrgyhk.dsp +++ b/lib/platform/synrgyhk.dsp @@ -38,12 +38,12 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../../Release" -# PROP Intermediate_Dir "ReleaseHook" +# PROP Output_Dir "..\..\build" +# PROP Intermediate_Dir "..\..\gen\build" # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c /Fd..\..\gen\build\synrgyhk.pdb # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -66,12 +66,12 @@ LINK32=link.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../../Debug" -# PROP Intermediate_Dir "DebugHook" +# PROP Output_Dir "..\..\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c /Fd..\..\gen\debug\synrgyhk.pdb # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/lib/server/server.dsp b/lib/server/server.dsp index 91d0c683..992fd7c3 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -37,11 +37,11 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\gen\build" +# PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\server.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -61,11 +61,11 @@ LIB32=link.exe -lib # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\gen\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\server.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index f0fd4cc9..9cf43e79 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -37,11 +37,11 @@ RSC=rc.exe # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" +# PROP Output_Dir "..\..\gen\build" +# PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\libsynergy.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -61,11 +61,11 @@ LIB32=link.exe -lib # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" +# PROP Output_Dir "..\..\gen\debug" +# PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\libsynergy.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" From 5a1650914a0b73b0a4ffbb04639071729845fab7 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 May 2004 18:03:36 +0000 Subject: [PATCH 671/807] Changed (win NT) service to depend on the 'Browser' service to ensure correct startup order. --- cmd/launcher/CAutoStart.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp index 9a6cc4a1..2b58b202 100644 --- a/cmd/launcher/CAutoStart.cpp +++ b/cmd/launcher/CAutoStart.cpp @@ -199,12 +199,12 @@ CAutoStart::onInstall(bool allUsers) // clear error message m_errorMessage = ""; - // install. depend on NetBT (NetBIOS over TCP/IP). + // install. depend on Browser try { ARCH->installDaemon(m_name.c_str(), m_isServer ? SERVER_DAEMON_INFO : CLIENT_DAEMON_INFO, appPath.c_str(), m_cmdLine.c_str(), - "NetBT\0", allUsers); + "Browser\0", allUsers); askOkay(m_hwnd, getString(IDS_INSTALL_TITLE), getString(allUsers ? IDS_INSTALLED_SYSTEM : From 93d74a1fda1883fb6a46eba1f9042db51056c597 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 16 May 2004 18:04:36 +0000 Subject: [PATCH 672/807] Fixed handling of screen resolution changes. --- lib/client/CServerProxy.cpp | 4 +++- lib/server/CClientProxy1_0.cpp | 20 ++++++++++++-------- lib/synergy/ProtocolTypes.h | 9 ++++++--- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 79e6698e..e9f91054 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -334,7 +334,8 @@ CServerProxy::sendInfo(const CClientInfo& info) LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d", info.m_x, info.m_y, info.m_w, info.m_h)); CProtocolUtil::writef(m_stream, kMsgDInfo, info.m_x, info.m_y, - info.m_w, info.m_h, 0, 0, 0); + info.m_w, info.m_h, 0, + info.m_mx, info.m_my); } KeyID @@ -779,6 +780,7 @@ CServerProxy::queryInfo() { CClientInfo info; m_client->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + m_client->getCursorPos(info.m_mx, info.m_my); sendInfo(info); } diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 2bb412ce..3d2bd7e0 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -218,9 +218,11 @@ CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const } void -CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const +CClientProxy1_0::getCursorPos(SInt32& x, SInt32& y) const { - assert(0 && "shouldn't be called"); + // note -- this returns the cursor pos from when we last got client info + x = m_info.m_mx; + y = m_info.m_my; } void @@ -372,9 +374,9 @@ bool CClientProxy1_0::recvInfo() { // parse the message - SInt16 x, y, w, h, dummy1, dummy2, dummy3; + SInt16 x, y, w, h, dummy1, mx, my; if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4, - &x, &y, &w, &h, &dummy1, &dummy2, &dummy3)) { + &x, &y, &w, &h, &dummy1, &mx, &my)) { return false; } LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d", getName().c_str(), x, y, w, h)); @@ -385,10 +387,12 @@ CClientProxy1_0::recvInfo() } // save - m_info.m_x = x; - m_info.m_y = y; - m_info.m_w = w; - m_info.m_h = h; + m_info.m_x = x; + m_info.m_y = y; + m_info.m_w = w; + m_info.m_h = h; + m_info.m_mx = mx; + m_info.m_my = my; // acknowledge receipt LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index 4fd6d475..b4b8ae22 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -201,7 +201,7 @@ static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; // $2 = coordinate of topmost pixel on secondary screen, // $3 = width of secondary screen in pixels, // $4 = height of secondary screen in pixels, -// $5 = size of warp zone, +// $5 = size of warp zone, (obsolete) // $6, $7 = the x,y position of the mouse on the secondary screen. // // the secondary screen must send this message in response to the @@ -274,8 +274,11 @@ public: //! Obsolete (jump zone size) SInt32 obsolete1; - //! Obsolete (mouse position) - SInt32 obsolete2, obsolete3; + //! Mouse position + /*! + The current location of the mouse cursor. + */ + SInt32 m_mx, m_my; }; #endif From ce399937f0f9cd0a53e5be0219b549da8a9122ca Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 May 2004 21:55:38 +0000 Subject: [PATCH 673/807] Fixed ctrl+alt+del emulation on win32 server. It was mapping VK_DELETE to the keypad delete key. This key is not interpreted on the client as causing ctrl+alt+del. --- lib/platform/CMSWindowsKeyState.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index e25e9c61..1100d7aa 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -1078,6 +1078,13 @@ CMSWindowsKeyState::doUpdateKeys() m_scanCodeToVirtKey[button2] = i; m_virtKeyToScanCode[i] = button; + // if the virtual key is VK_DELETE then use the extended + // scan code. this is important for simulating ctrl+alt+del + // which only works with the extended key. + if (i == VK_DELETE) { + m_virtKeyToScanCode[i] |= 0x100u; + } + // save the key state if ((keyState[i] & 0x80) != 0) { setKeyDown(button, true); From 7e36454b0131a47f6a3c27ae0dde8e054199ea0b Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 17 May 2004 21:55:55 +0000 Subject: [PATCH 674/807] Fixed logging of connection to server. Was DEBUG now NOTE. --- cmd/synergyc/synergyc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index a10601fd..ed3b34e2 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -270,7 +270,7 @@ static void handleClientConnected(const CEvent&, void*) { - LOG((CLOG_DEBUG "connected to server")); + LOG((CLOG_NOTE "connected to server")); resetRestartTimeout(); updateStatus(); } From 88c6769ebe95afec4ecf0967ce7de7bd4efe7596 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 18 May 2004 20:26:48 +0000 Subject: [PATCH 675/807] Fixed bug that could allow multiple threads to simultaneously access the X11 display connection. The only problematic method was CXWindowsEventQueueBuffer::addEvent given that the other event queue methods are only called from the main thread. --- lib/platform/CXWindowsEventQueueBuffer.cpp | 59 +++++++++++++++++++--- lib/platform/CXWindowsEventQueueBuffer.h | 10 ++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/lib/platform/CXWindowsEventQueueBuffer.cpp b/lib/platform/CXWindowsEventQueueBuffer.cpp index 7f41dc5a..686865b9 100644 --- a/lib/platform/CXWindowsEventQueueBuffer.cpp +++ b/lib/platform/CXWindowsEventQueueBuffer.cpp @@ -13,6 +13,7 @@ */ #include "CXWindowsEventQueueBuffer.h" +#include "CLock.h" #include "CThread.h" #include "CEvent.h" #include "IEventQueue.h" @@ -47,7 +48,8 @@ class CEventQueueTimer { }; CXWindowsEventQueueBuffer::CXWindowsEventQueueBuffer( Display* display, Window window) : m_display(display), - m_window(window) + m_window(window), + m_waiting(false) { assert(m_display != NULL); assert(m_window != None); @@ -63,6 +65,17 @@ CXWindowsEventQueueBuffer::~CXWindowsEventQueueBuffer() void CXWindowsEventQueueBuffer::waitForEvent(double dtimeout) { + CThread::testCancel(); + + { + CLock lock(&m_mutex); + // we're now waiting for events + m_waiting = true; + + // push out pending events + flush(); + } + // use poll() to wait for a message from the X server or for timeout. // this is a good deal more efficient than polling and sleeping. #if HAVE_POLL @@ -93,7 +106,6 @@ CXWindowsEventQueueBuffer::waitForEvent(double dtimeout) // wait for message from X server or for timeout. also check // if the thread has been cancelled. poll() should return -1 // with EINTR when the thread is cancelled. - CThread::testCancel(); #if HAVE_POLL poll(pfds, 1, timeout); #else @@ -103,12 +115,24 @@ CXWindowsEventQueueBuffer::waitForEvent(double dtimeout) SELECT_TYPE_ARG234 NULL, SELECT_TYPE_ARG5 timeoutPtr); #endif + + { + // we're no longer waiting for events + CLock lock(&m_mutex); + m_waiting = true; + } + CThread::testCancel(); } IEventQueueBuffer::Type CXWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) { + CLock lock(&m_mutex); + + // push out pending events + flush(); + // get next event XNextEvent(m_display, &m_event); @@ -128,25 +152,33 @@ CXWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) bool CXWindowsEventQueueBuffer::addEvent(UInt32 dataID) { - // send ourself a message + // prepare a message XEvent xevent; xevent.xclient.type = ClientMessage; xevent.xclient.window = m_window; xevent.xclient.message_type = m_userEvent; xevent.xclient.format = 32; xevent.xclient.data.l[0] = static_cast(dataID); - if (XSendEvent(m_display, m_window, False, 0, &xevent) == 0) { - return false; + + // save the message + CLock lock(&m_mutex); + m_postedEvents.push_back(xevent); + + // if we're currently waiting for an event then send saved events to + // the X server now. if we're not waiting then some other thread + // might be using the display connection so we can't safely use it + // too. + if (m_waiting) { + flush(); } - // force waitForEvent() to return - XFlush(m_display); return true; } bool CXWindowsEventQueueBuffer::isEmpty() const { + CLock lock(&m_mutex); return (XPending(m_display) == 0); } @@ -161,3 +193,16 @@ CXWindowsEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const { delete timer; } + +void +CXWindowsEventQueueBuffer::flush() +{ + // note -- m_mutex must be locked on entry + + // flush the posted event list to the X server + for (size_t i = 0; i < m_postedEvents.size(); ++i) { + XSendEvent(m_display, m_window, False, 0, &m_postedEvents[i]); + } + XFlush(m_display); + m_postedEvents.clear(); +} diff --git a/lib/platform/CXWindowsEventQueueBuffer.h b/lib/platform/CXWindowsEventQueueBuffer.h index 8b3d4312..9c2bdfa3 100644 --- a/lib/platform/CXWindowsEventQueueBuffer.h +++ b/lib/platform/CXWindowsEventQueueBuffer.h @@ -16,6 +16,8 @@ #define CXWINDOWSEVENTQUEUEBUFFER_H #include "IEventQueueBuffer.h" +#include "CMutex.h" +#include "stdvector.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else @@ -38,10 +40,18 @@ public: virtual void deleteTimer(CEventQueueTimer*) const; private: + void flush(); + +private: + typedef std::vector CEventList; + + CMutex m_mutex; Display* m_display; Window m_window; Atom m_userEvent; XEvent m_event; + CEventList m_postedEvents; + bool m_waiting; }; #endif From 84247b4c6c6d84c1b9ad1ba23a3addad36cab465 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 18 May 2004 20:32:13 +0000 Subject: [PATCH 676/807] If the server manages to detect ctrl+alt+del it will no longer send that to the client. If it did then the user could see the effect of ctrl+alt+del on both the server and client which we never want. The user can use ctrl+alt+pause to emulate ctrl+alt+del on the client. --- lib/platform/CMSWindowsScreen.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 991a2568..720b9669 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -950,8 +950,15 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) // ignore message if posted prior to last mark change if (!ignore()) { - // check for ctrl+alt+del emulation + // check for ctrl+alt+del. we do not want to pass that to the + // client. the user can use ctrl+alt+pause to emulate it. UINT virtKey = (wParam & 0xffu); + if (virtKey == VK_DELETE && ctrlAlt) { + LOG((CLOG_DEBUG "discard ctrl+alt+del")); + return true; + } + + // check for ctrl+alt+del emulation if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) && ctrlAlt) { LOG((CLOG_DEBUG "emulate ctrl+alt+del")); // switch wParam and lParam to be as if VK_DELETE was From 06d77fe314d5967d70c66963297ddbc894f1690f Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 26 May 2004 19:23:32 +0000 Subject: [PATCH 677/807] Merged Bertrand's OS X changes. Also added support for mouse wheel on OS X server. --- lib/common/MacOSXPrecomp.h | 8 + lib/common/common.h | 6 +- lib/platform/COSXClipboardTextConverter.cpp | 44 ++- lib/platform/COSXClipboardTextConverter.h | 4 + lib/platform/COSXEventQueueBuffer.cpp | 2 +- lib/platform/COSXKeyState.cpp | 346 +++++++++++++++- lib/platform/COSXKeyState.h | 15 +- lib/platform/COSXScreen.cpp | 413 +++++++++++++++++--- lib/platform/COSXScreen.h | 40 +- synergy.xcode/project.pbxproj | 108 +++++ 10 files changed, 922 insertions(+), 64 deletions(-) create mode 100644 lib/common/MacOSXPrecomp.h diff --git a/lib/common/MacOSXPrecomp.h b/lib/common/MacOSXPrecomp.h new file mode 100644 index 00000000..64e7c90a --- /dev/null +++ b/lib/common/MacOSXPrecomp.h @@ -0,0 +1,8 @@ +// +// Prefix header for all source files of the 'deleteme' target in the 'deleteme' project. +// + +#define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_2 + + +#include diff --git a/lib/common/common.h b/lib/common/common.h index c2c910e8..1e406693 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -27,7 +27,7 @@ # endif // we may not have run configure on OS X -# if defined(__APPLE__) +# if defined(__APPLE__) # define SYSAPI_UNIX 1 # define WINAPI_CARBON 1 @@ -45,11 +45,9 @@ # define HAVE_MEMORY_H 1 # define HAVE_NANOSLEEP 1 # define HAVE_OSTREAM 1 -# define HAVE_POLL 1 # define HAVE_POSIX_SIGWAIT 1 # define HAVE_PTHREAD 1 # define HAVE_PTHREAD_SIGNAL 1 -# define HAVE_SOCKLEN_T 1 # define HAVE_SSTREAM 1 # define HAVE_STDINT_H 1 # define HAVE_STDLIB_H 1 @@ -74,7 +72,7 @@ # define STDC_HEADERS 1 # define TIME_WITH_SYS_TIME 1 # define X_DISPLAY_MISSING 1 -# endif +# endif #endif // VC++ specific diff --git a/lib/platform/COSXClipboardTextConverter.cpp b/lib/platform/COSXClipboardTextConverter.cpp index 0b92dee1..bcbc228e 100644 --- a/lib/platform/COSXClipboardTextConverter.cpp +++ b/lib/platform/COSXClipboardTextConverter.cpp @@ -35,14 +35,54 @@ COSXClipboardTextConverter::getOSXFormat() const return kScrapFlavorTypeText; } +CString +COSXClipboardTextConverter::convertString( + const CString& data, + CFStringEncoding fromEncoding, + CFStringEncoding toEncoding) +{ + CFStringRef stringRef = + CFStringCreateWithCString(kCFAllocatorDefault, + data.c_str(), fromEncoding); + + if (stringRef == NULL) { + return CString(); + } + + CFIndex buffSize; + CFRange entireString = CFRangeMake(0, CFStringGetLength(stringRef)); + + CFStringGetBytes(stringRef, entireString, toEncoding, + 0, false, NULL, 0, &buffSize); + + char* buffer = new char[buffSize]; + + if (buffer == NULL) { + CFRelease(stringRef); + return CString(); + } + + CFStringGetBytes(stringRef, entireString, toEncoding, + 0, false, (UInt8*)buffer, buffSize, NULL); + + CString result(buffer, buffSize); + + delete[] buffer; + CFRelease(stringRef); + + return result; +} + CString COSXClipboardTextConverter::doFromIClipboard(const CString& data) const { - return CUnicode::UTF8ToText(data); + return convertString(data, kCFStringEncodingUTF8, + CFStringGetSystemEncoding()); } CString COSXClipboardTextConverter::doToIClipboard(const CString& data) const { - return CUnicode::textToUTF8(data); + return convertString(data, CFStringGetSystemEncoding(), + kCFStringEncodingUTF8); } diff --git a/lib/platform/COSXClipboardTextConverter.h b/lib/platform/COSXClipboardTextConverter.h index dc2649d5..2a75f4f0 100644 --- a/lib/platform/COSXClipboardTextConverter.h +++ b/lib/platform/COSXClipboardTextConverter.h @@ -32,6 +32,10 @@ protected: virtual CString doFromIClipboard(const CString&) const; virtual CString doToIClipboard(const CString&) const; + // generic encoding converter + static CString convertString(const CString& data, + CFStringEncoding fromEncoding, + CFStringEncoding toEncoding); }; #endif diff --git a/lib/platform/COSXEventQueueBuffer.cpp b/lib/platform/COSXEventQueueBuffer.cpp index 22dadc9d..76dcd35c 100644 --- a/lib/platform/COSXEventQueueBuffer.cpp +++ b/lib/platform/COSXEventQueueBuffer.cpp @@ -68,7 +68,7 @@ COSXEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) default: event = CEvent(CEvent::kSystem, IEventQueue::getSystemTarget(), &m_event); - return kNone; + return kSystem; } } } diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index ec454ace..ed9d295f 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -190,18 +190,308 @@ static const CKeyEntry s_keys[] = { { kKeyLeftTab, 48 } }; +const KeyID COSXKeyState::s_virtualKey[] = +{ + /* 0x00 */ kKeyNone, // A + /* 0x01 */ kKeyNone, // S + /* 0x02 */ kKeyNone, // D + /* 0x03 */ kKeyNone, // F + /* 0x04 */ kKeyNone, // H + /* 0x05 */ kKeyNone, // G + /* 0x06 */ kKeyNone, // Z + /* 0x07 */ kKeyNone, // X + /* 0x08 */ kKeyNone, // C + /* 0x09 */ kKeyNone, // V + /* 0x0a */ kKeyNone, // ~ on some european keyboards + /* 0x0b */ kKeyNone, // B + /* 0x0c */ kKeyNone, // Q + /* 0x0d */ kKeyNone, // W + /* 0x0e */ kKeyNone, // E + /* 0x0f */ kKeyNone, // R + /* 0x10 */ kKeyNone, // Y + /* 0x11 */ kKeyNone, // T + /* 0x12 */ kKeyNone, // 1 + /* 0x13 */ kKeyNone, // 2 + /* 0x14 */ kKeyNone, // 3 + /* 0x15 */ kKeyNone, // 4 + /* 0x16 */ kKeyNone, // 6 + /* 0x17 */ kKeyNone, // 5 + /* 0x18 */ kKeyNone, // = + /* 0x19 */ kKeyNone, // 9 + /* 0x1a */ kKeyNone, // 7 + /* 0x1b */ kKeyNone, // - + /* 0x1c */ kKeyNone, // 8 + /* 0x1d */ kKeyNone, // 0 + /* 0x1e */ kKeyNone, // ] + /* 0x1f */ kKeyNone, // O + /* 0x20 */ kKeyNone, // U + /* 0x21 */ kKeyNone, // [ + /* 0x22 */ kKeyNone, // I + /* 0x23 */ kKeyNone, // P + /* 0x24 */ kKeyReturn, // return + /* 0x25 */ kKeyNone, // L + /* 0x26 */ kKeyNone, // J + /* 0x27 */ kKeyNone, // ' + /* 0x28 */ kKeyNone, // K + /* 0x29 */ kKeyNone, // ; + /* 0x2a */ kKeyNone, /* \ */ + /* 0x2b */ kKeyNone, // , + /* 0x2c */ kKeyNone, // / + /* 0x2d */ kKeyNone, // M + /* 0x2e */ kKeyNone, // M + /* 0x2f */ kKeyNone, // . + /* 0x30 */ kKeyTab, // tab + /* 0x31 */ kKeyKP_Space, // space + /* 0x32 */ kKeyNone, // ` + /* 0x33 */ kKeyBackSpace, // Backspace + /* 0x34 */ kKeyNone, // undefined + /* 0x35 */ kKeyEscape, // escape + /* 0x36 */ kKeyNone, // undefined + /* 0x37 */ kKeyAlt_L, // alt + /* 0x38 */ kKeyShift_L, // shift + /* 0x39 */ kKeyCapsLock, // capslok + /* 0x3a */ kKeyMeta_L, // meta + /* 0x3b */ kKeyControl_L, // control + /* 0x3c */ kKeyNone, // undefined + /* 0x3d */ kKeyNone, // undefined + /* 0x3e */ kKeyNone, // undefined + /* 0x3f */ kKeyNone, // undefined + /* 0x40 */ kKeyNone, // undefined + /* 0x41 */ kKeyKP_Decimal, // keypad . + /* 0x42 */ kKeyNone, // undefined + /* 0x43 */ kKeyKP_Multiply, // keypad * + /* 0x44 */ kKeyNone, // undefined + /* 0x45 */ kKeyKP_Add, // keypad + + /* 0x46 */ kKeyNone, // undefined + /* 0x47 */ kKeyNumLock, // numlock + /* 0x48 */ kKeyNone, // undefined + /* 0x49 */ kKeyNone, // undefined + /* 0x4a */ kKeyNone, // undefined + /* 0x4b */ kKeyKP_Divide, /* keypad \ */ + /* 0x4c */ kKeyKP_Enter, // keypad enter + /* 0x4d */ kKeyNone, // undefined + /* 0x4e */ kKeyKP_Subtract, // keypad - + /* 0x4f */ kKeyNone, // undefined + /* 0x50 */ kKeyNone, // undefined + /* 0x51 */ kKeyNone, // undefined + /* 0x52 */ kKeyKP_0, // keypad 0 + /* 0x53 */ kKeyKP_1, // keypad 1 + /* 0x54 */ kKeyKP_2, // keypad 2 + /* 0x55 */ kKeyKP_3, // keypad 3 + /* 0x56 */ kKeyKP_4, // keypad 4 + /* 0x57 */ kKeyKP_5, // keypad 5 + /* 0x58 */ kKeyKP_6, // keypad 6 + /* 0x59 */ kKeyKP_7, // keypad 7 + /* 0x5a */ kKeyKP_8, // keypad 8 + /* 0x5b */ kKeyKP_9, // keypad 9 + /* 0x5c */ kKeyNone, // undefined + /* 0x5d */ kKeyNone, // undefined + /* 0x5e */ kKeyNone, // undefined + /* 0x5f */ kKeyNone, // undefined + /* 0x60 */ kKeyF5, // F5 + /* 0x61 */ kKeyF6, // F6 + /* 0x62 */ kKeyF7, // F7 + /* 0x63 */ kKeyF3, // F3 + /* 0x64 */ kKeyF8, // F8 + /* 0x65 */ kKeyF9, // F9 + /* 0x66 */ kKeyNone, // undefined + /* 0x67 */ kKeyF11, // F11 + /* 0x68 */ kKeyNone, // undefined + /* 0x69 */ kKeyNone, // undefined + /* 0x6a */ kKeyNone, // undefined + /* 0x6b */ kKeyF10, // F10 + /* 0x6c */ kKeyNone, // undefined + /* 0x6d */ kKeyF12, // F12 + /* 0x6e */ kKeyNone, // undefined + /* 0x6f */ kKeyPause, // pause + /* 0x70 */ kKeyNone, // undefined + /* 0x71 */ kKeyBegin, // home + /* 0x72 */ kKeyPageUp, // PageUP + /* 0x73 */ kKeyDelete, // Delete + /* 0x74 */ kKeyF4, // F4 + /* 0x75 */ kKeyEnd, // end + /* 0x76 */ kKeyF2, // F2 + /* 0x77 */ kKeyNone, // undefined + /* 0x78 */ kKeyF1, // F1 + /* 0x79 */ kKeyPageDown, // PageDown + /* 0x7a */ kKeyNone, // undefined + /* 0x7b */ kKeyLeft, // left + /* 0x7c */ kKeyRight, // right + /* 0x7d */ kKeyDown, // down + /* 0x7e */ kKeyUp, // up + + /* 0x7f */ kKeyNone, // unassigned + /* 0x80 */ kKeyNone, // unassigned + /* 0x81 */ kKeyNone, // unassigned + /* 0x82 */ kKeyNone, // unassigned + /* 0x83 */ kKeyNone, // unassigned + /* 0x84 */ kKeyNone, // unassigned + /* 0x85 */ kKeyNone, // unassigned + /* 0x86 */ kKeyNone, // unassigned + /* 0x87 */ kKeyNone, // unassigned + /* 0x88 */ kKeyNone, // unassigned + /* 0x89 */ kKeyNone, // unassigned + /* 0x8a */ kKeyNone, // unassigned + /* 0x8b */ kKeyNone, // unassigned + /* 0x8c */ kKeyNone, // unassigned + /* 0x8d */ kKeyNone, // unassigned + /* 0x8e */ kKeyNone, // unassigned + /* 0x8f */ kKeyNone, // unassigned + /* 0x90 */ kKeyNone, // unassigned + /* 0x91 */ kKeyNone, // unassigned + /* 0x92 */ kKeyNone, // unassigned + /* 0x93 */ kKeyNone, // unassigned + /* 0x94 */ kKeyNone, // unassigned + /* 0x95 */ kKeyNone, // unassigned + /* 0x96 */ kKeyNone, // unassigned + /* 0x97 */ kKeyNone, // unassigned + /* 0x98 */ kKeyNone, // unassigned + /* 0x99 */ kKeyNone, // unassigned + /* 0x9a */ kKeyNone, // unassigned + /* 0x9b */ kKeyNone, // unassigned + /* 0x9c */ kKeyNone, // unassigned + /* 0x9d */ kKeyNone, // unassigned + /* 0x9e */ kKeyNone, // unassigned + /* 0x9f */ kKeyNone, // unassigned + /* 0xa0 */ kKeyNone, // unassigned + /* 0xa1 */ kKeyNone, // unassigned + /* 0xa2 */ kKeyNone, // unassigned + /* 0xa3 */ kKeyNone, // unassigned + /* 0xa4 */ kKeyNone, // unassigned + /* 0xa5 */ kKeyNone, // unassigned + /* 0xa6 */ kKeyNone, // unassigned + /* 0xa7 */ kKeyNone, // unassigned + /* 0xa8 */ kKeyNone, // unassigned + /* 0xa9 */ kKeyNone, // unassigned + /* 0xaa */ kKeyNone, // unassigned + /* 0xab */ kKeyNone, // unassigned + /* 0xac */ kKeyNone, // unassigned + /* 0xad */ kKeyNone, // unassigned + /* 0xae */ kKeyNone, // unassigned + /* 0xaf */ kKeyNone, // unassigned + /* 0xb0 */ kKeyNone, // unassigned + /* 0xb1 */ kKeyNone, // unassigned + /* 0xb2 */ kKeyNone, // unassigned + /* 0xb3 */ kKeyNone, // unassigned + /* 0xb4 */ kKeyNone, // unassigned + /* 0xb5 */ kKeyNone, // unassigned + /* 0xb6 */ kKeyNone, // unassigned + /* 0xb7 */ kKeyNone, // unassigned + /* 0xb8 */ kKeyNone, // unassigned + /* 0xb9 */ kKeyNone, // unassigned + /* 0xba */ kKeyNone, // unassigned + /* 0xbb */ kKeyNone, // unassigned + /* 0xbc */ kKeyNone, // unassigned + /* 0xbd */ kKeyNone, // unassigned + /* 0xbe */ kKeyNone, // unassigned + /* 0xbf */ kKeyNone, // unassigned + /* 0xc0 */ kKeyNone, // unassigned + /* 0xc1 */ kKeyNone, // unassigned + /* 0xc2 */ kKeyNone, // unassigned + /* 0xc3 */ kKeyNone, // unassigned + /* 0xc4 */ kKeyNone, // unassigned + /* 0xc5 */ kKeyNone, // unassigned + /* 0xc6 */ kKeyNone, // unassigned + /* 0xc7 */ kKeyNone, // unassigned + /* 0xc8 */ kKeyNone, // unassigned + /* 0xc9 */ kKeyNone, // unassigned + /* 0xca */ kKeyNone, // unassigned + /* 0xcb */ kKeyNone, // unassigned + /* 0xcc */ kKeyNone, // unassigned + /* 0xcd */ kKeyNone, // unassigned + /* 0xce */ kKeyNone, // unassigned + /* 0xcf */ kKeyNone, // unassigned + /* 0xd0 */ kKeyNone, // unassigned + /* 0xd1 */ kKeyNone, // unassigned + /* 0xd2 */ kKeyNone, // unassigned + /* 0xd3 */ kKeyNone, // unassigned + /* 0xd4 */ kKeyNone, // unassigned + /* 0xd5 */ kKeyNone, // unassigned + /* 0xd6 */ kKeyNone, // unassigned + /* 0xd7 */ kKeyNone, // unassigned + /* 0xd8 */ kKeyNone, // unassigned + /* 0xd9 */ kKeyNone, // unassigned + /* 0xda */ kKeyNone, // unassigned + /* 0xdb */ kKeyNone, // unassigned + /* 0xdc */ kKeyNone, // unassigned + /* 0xdd */ kKeyNone, // unassigned + /* 0xde */ kKeyNone, // unassigned + /* 0xdf */ kKeyNone, // unassigned + /* 0xe0 */ kKeyNone, // unassigned + /* 0xe1 */ kKeyNone, // unassigned + /* 0xe2 */ kKeyNone, // unassigned + /* 0xe3 */ kKeyNone, // unassigned + /* 0xe4 */ kKeyNone, // unassigned + /* 0xe5 */ kKeyNone, // unassigned + /* 0xe6 */ kKeyNone, // unassigned + /* 0xe7 */ kKeyNone, // unassigned + /* 0xe8 */ kKeyNone, // unassigned + /* 0xe9 */ kKeyNone, // unassigned + /* 0xea */ kKeyNone, // unassigned + /* 0xeb */ kKeyNone, // unassigned + /* 0xec */ kKeyNone, // unassigned + /* 0xed */ kKeyNone, // unassigned + /* 0xee */ kKeyNone, // unassigned + /* 0xef */ kKeyNone, // unassigned + /* 0xf0 */ kKeyNone, // unassigned + /* 0xf1 */ kKeyNone, // unassigned + /* 0xf2 */ kKeyNone, // unassigned + /* 0xf3 */ kKeyNone, // unassigned + /* 0xf4 */ kKeyNone, // unassigned + /* 0xf5 */ kKeyNone, // unassigned + /* 0xf6 */ kKeyNone, // unassigned + /* 0xf7 */ kKeyNone, // unassigned + /* 0xf8 */ kKeyNone, // unassigned + /* 0xf9 */ kKeyNone, // unassigned + /* 0xfa */ kKeyNone, // unassigned + /* 0xfb */ kKeyNone, // unassigned + /* 0xfc */ kKeyNone, // unassigned + /* 0xfd */ kKeyNone, // unassigned + /* 0xfe */ kKeyNone, // unassigned + /* 0xff */ kKeyNone // unassigned +}; + + // // COSXKeyState // COSXKeyState::COSXKeyState() { - // FIXME + // do nothing } COSXKeyState::~COSXKeyState() { - // FIXME + // do nothing +} + +void +COSXKeyState::sendKeyEvent(void* target, + bool press, bool isAutoRepeat, + KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + if (press || isAutoRepeat) { + // send key + if (press) { + CKeyState::sendKeyEvent(target, true, false, + key, mask, 1, button); + if (count > 0) { + --count; + } + } + + if (count >= 1) { + CKeyState::sendKeyEvent(target, true, true, + key, mask, count, button); + } + + } + else { + // do key up + CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button); + } } bool @@ -229,15 +519,17 @@ COSXKeyState::doUpdateKeys() // non-english keyboards. also need to map modifiers needed // for each KeyID. for (UInt32 i = 0; i < sizeof(s_keys) / sizeof(s_keys[0]); ++i) { - m_keyMap.insert(std::make_pair(s_keys[i].m_keyID, s_keys[i].m_button)); + m_keyMap.insert(std::make_pair(s_keys[i].m_keyID, + s_keys[i].m_button + 1)); } } void -COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool) +COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool isAutoRepeat) { + LOG((CLOG_DEBUG2 "doFakeKeyEvent button:%d, press:%d", button, press)); // let system figure out character for us - CGPostKeyboardEvent(0, static_cast(button), press); + CGPostKeyboardEvent(0, static_cast(button) - 1, press); } KeyButton @@ -292,3 +584,47 @@ COSXKeyState::adjustModifiers(Keystrokes& /*keys*/, // FIXME -- should add necessary modifier events to keys and undo return true; } + +KeyID +COSXKeyState::mapKeyFromEvent(EventRef event, KeyModifierMask* maskOut) const +{ + char c; + GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, + NULL, sizeof(c), NULL, &c); + + UInt32 vkCode; + GetEventParameter(event, kEventParamKeyCode, typeUInt32, + NULL, sizeof(vkCode), NULL, &vkCode); + + KeyID id = s_virtualKey[vkCode]; + + // check if not in table; map character to key id + if (id == kKeyNone && c != 0) { + if ((c & 0x80u) == 0) { + // ASCII + id = static_cast(c) & 0xffu; + } + else { + // character is not really ASCII. instead it's some + // character in the current ANSI code page. try to + // convert that to a Unicode character. if we fail + // then use the single byte character as is. + //FIXME + id = static_cast(c) & 0xffu; + } + } + + KeyModifierMask activeMask = getActiveModifiers(); + if (id != kKeyNone && c != 0) { + // FIXME + } + + // map modifier key + if (maskOut != NULL) { + activeMask &= ~KeyModifierModeSwitch; + *maskOut = activeMask; + } + + return id; + +} diff --git a/lib/platform/COSXKeyState.h b/lib/platform/COSXKeyState.h index ef563e14..94e98f07 100644 --- a/lib/platform/COSXKeyState.h +++ b/lib/platform/COSXKeyState.h @@ -28,10 +28,21 @@ public: COSXKeyState(); virtual ~COSXKeyState(); + //! Map key event to a key + /*! + Converts a key event into a KeyID and the shadow modifier state + to a modifier mask. + */ + KeyID mapKeyFromEvent(EventRef event, + KeyModifierMask* maskOut) const; + // IKeyState overrides virtual bool fakeCtrlAltDel(); virtual const char* getKeyName(KeyButton) const; - + virtual void sendKeyEvent(void* target, + bool press, bool isAutoRepeat, + KeyID key, KeyModifierMask mask, + SInt32 count, KeyButton button); protected: // IKeyState overrides virtual void doUpdateKeys(); @@ -50,6 +61,8 @@ private: typedef std::map CKeyMap; CKeyMap m_keyMap; + + static const KeyID s_virtualKey[]; }; #endif diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index 80fb0677..2cd002a1 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -35,16 +35,72 @@ COSXScreen::COSXScreen(bool isPrimary) : m_sequenceNumber(0), m_screensaver(NULL), m_screensaverNotify(false), - m_ownClipboard(false) + m_ownClipboard(false), + m_hiddenWindow(NULL), + m_userInputWindow(NULL), + m_displayManagerNotificationUPP(NULL) { try { m_displayID = CGMainDisplayID(); updateScreenShape(); m_screensaver = new COSXScreenSaver(); m_keyState = new COSXKeyState(); - LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + + if (m_isPrimary) { + // 1x1 window (to minimze the back buffer allocated for this + // window. + Rect bounds = { 100, 100, 101, 101 }; + + // m_hiddenWindow is a window meant to let us get mouse moves + // when the focus is on this computer. If you get your event + // from the application event target you'll get every mouse + // moves. On the other hand the Window event target will only + // get events when the mouse moves over the window. + + // The ignoreClicks attributes makes it impossible for the + // user to click on our invisible window. + CreateNewWindow(kUtilityWindowClass, + kWindowNoShadowAttribute | + kWindowIgnoreClicksAttribute | + kWindowNoActivatesAttribute, + &bounds, &m_hiddenWindow); + + // Make it invisible + SetWindowAlpha(m_hiddenWindow, 0); + ShowWindow(m_hiddenWindow); + + // m_userInputWindow is a window meant to let us get mouse moves + // when the focus is on this computer. + Rect inputBounds = { 100, 100, 200, 200 }; + CreateNewWindow(kUtilityWindowClass, + kWindowNoShadowAttribute | + kWindowOpaqueForEventsAttribute | + kWindowStandardHandlerAttribute, + &inputBounds, &m_userInputWindow); + + SetWindowAlpha(m_userInputWindow, 0); + } + + m_displayManagerNotificationUPP = + NewDMExtendedNotificationUPP(displayManagerCallback); + + OSStatus err = GetCurrentProcess(&m_PSN); + + err = DMRegisterExtendedNotifyProc(m_displayManagerNotificationUPP, + this, 0, &m_PSN); } catch (...) { + DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, + NULL, &m_PSN, 0); + if (m_hiddenWindow) { + ReleaseWindow(m_hiddenWindow); + m_hiddenWindow = NULL; + } + + if (m_userInputWindow) { + ReleaseWindow(m_userInputWindow); + m_userInputWindow = NULL; + } delete m_keyState; delete m_screensaver; throw; @@ -64,6 +120,20 @@ COSXScreen::~COSXScreen() disable(); EVENTQUEUE->adoptBuffer(NULL); EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget()); + + if (m_hiddenWindow) { + ReleaseWindow(m_hiddenWindow); + m_hiddenWindow = NULL; + } + + if (m_userInputWindow) { + ReleaseWindow(m_userInputWindow); + m_userInputWindow = NULL; + } + + DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, + NULL, &m_PSN, 0); + delete m_keyState; delete m_screensaver; } @@ -159,6 +229,7 @@ COSXScreen::postMouseEvent(const CGPoint & pos) const // 2 - Right // 3 - Middle // Whatever the USB device defined. + // // It is a bit weird that the behaviour of buttons over 3 are dependent // on currently plugged in USB devices. CGPostMouseEvent(pos, true, sizeof(m_buttons) / sizeof(m_buttons[0]), @@ -233,12 +304,12 @@ void COSXScreen::fakeMouseWheel(SInt32 delta) const { CFPropertyListRef pref = ::CFPreferencesCopyValue( - CFSTR("com.apple.scrollwheel.scaling"), - kCFPreferencesAnyApplication, + CFSTR("com.apple.scrollwheel.scaling") , + kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); - int32_t wheelIncr = 10; + int32_t wheelIncr = 1; if (pref != NULL) { CFTypeID id = CFGetTypeID(pref); @@ -248,6 +319,9 @@ COSXScreen::fakeMouseWheel(SInt32 delta) const double scaling; if (CFNumberGetValue(value, kCFNumberDoubleType, &scaling)) { wheelIncr = (int32_t)(8 * scaling); + if (wheelIncr == 0) { + wheelIncr = 1; + } } } CFRelease(pref); @@ -311,7 +385,13 @@ void COSXScreen::enter() { if (m_isPrimary) { - // FIXME -- stop capturing input, watch jump zones + // stop capturing input, watch jump zones + HideWindow( m_userInputWindow ); + ShowWindow( m_hiddenWindow ); + + SetMouseCoalescingEnabled(true, NULL); + + CGSetLocalEventsSuppressionInterval(HUGE_VAL); } else { // show cursor @@ -340,10 +420,21 @@ COSXScreen::leave() updateKeys(); // warp to center + // FIXME -- this should be the center of the main monitor warpCursor(m_xCenter, m_yCenter); // capture events - // FIXME + HideWindow(m_hiddenWindow); + ShowWindow(m_userInputWindow); + RepositionWindow(m_userInputWindow, + m_userInputWindow, kWindowCenterOnMainScreen); + SetUserFocusWindow(m_userInputWindow); + + // The OS will coalesce some events if they are similar enough in a + // short period of time this is bad for us since we need every event + // to send it over to other machines. So disable it. + SetMouseCoalescingEnabled(false, NULL); + CGSetLocalEventsSuppressionInterval(0.0001); } else { // hide cursor @@ -457,13 +548,13 @@ COSXScreen::isPrimary() const } void -COSXScreen::sendEvent(CEvent::Type type, void* data) +COSXScreen::sendEvent(CEvent::Type type, void* data) const { EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data)); } void -COSXScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) +COSXScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) const { CClipboardInfo* info = (CClipboardInfo*)malloc(sizeof(CClipboardInfo)); info->m_id = id; @@ -471,50 +562,284 @@ COSXScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) sendEvent(type, info); } - void COSXScreen::handleSystemEvent(const CEvent& event, void*) { -/* - EventRef * carbonEvent = reinterpret_cast(event.getData()); + EventRef* carbonEvent = reinterpret_cast(event.getData()); assert(carbonEvent != NULL); - UInt32 eventClass = GetEventClass( *carbonEvent ); + UInt32 eventClass = GetEventClass(*carbonEvent); - switch( eventClass ) - { - case kEventClassMouse: + switch (eventClass) { + case kEventClassMouse: + switch (GetEventKind(*carbonEvent)) { + case kEventMouseDown: { - UInt32 eventKind = GetEventKind( *carbonEvent ); - switch( eventKind ) - { - case kEventMouseMoved: - { - HIPoint point; - GetEventParameter( *carbonEvent, - kEventParamMouseDelta, - typeHIPoint, - NULL, - sizeof(point), - NULL, - &point); - - } break; - } - }break; - - case kEventClassKeyboard: - { - + UInt16 myButton; + GetEventParameter(*carbonEvent, + kEventParamMouseButton, + typeMouseButton, + NULL, + sizeof(myButton), + NULL, + &myButton); + onMouseButton(true, myButton); + break; } - default: + case kEventMouseUp: { - - } break; - + UInt16 myButton; + GetEventParameter(*carbonEvent, + kEventParamMouseButton, + typeMouseButton, + NULL, + sizeof(myButton), + NULL, + &myButton); + onMouseButton(false, myButton); + break; + } + + case kEventMouseDragged: + case kEventMouseMoved: + { + HIPoint point; + GetEventParameter(*carbonEvent, + kEventParamMouseLocation, + typeHIPoint, + NULL, + sizeof(point), + NULL, + &point); + onMouseMove((SInt32)point.x, (SInt32)point.y); + break; + } + + case kEventMouseWheelMoved: + { + EventMouseWheelAxis axis; + SInt32 delta; + GetEventParameter(*carbonEvent, + kEventParamMouseWheelAxis, + typeMouseWheelAxis, + NULL, + sizeof(axis), + NULL, + &axis); + if (axis == kEventMouseWheelAxisY) { + GetEventParameter(*carbonEvent, + kEventParamMouseWheelDelta, + typeLongInteger, + NULL, + sizeof(delta), + NULL, + &delta); + onMouseWheel(120 * (SInt32)delta); + } + break; + } + } + break; + + case kEventClassKeyboard: + switch (GetEventKind(*carbonEvent)) { + case kEventRawKeyUp: + case kEventRawKeyDown: + case kEventRawKeyRepeat: + case kEventRawKeyModifiersChanged: +// case kEventHotKeyPressed: +// case kEventHotKeyReleased: + onKey(*carbonEvent); + break; + } + break; + + case kEventClassWindow: + SendEventToWindow(*carbonEvent, m_userInputWindow); + switch (GetEventKind(*carbonEvent)) { + case kEventWindowActivated: + LOG((CLOG_DEBUG1 "window activated")); + break; + + case kEventWindowDeactivated: + LOG((CLOG_DEBUG1 "window deactivated")); + break; + + case kEventWindowFocusAcquired: + LOG((CLOG_DEBUG1 "focus acquired")); + break; + + case kEventWindowFocusRelinquish: + LOG((CLOG_DEBUG1 "focus released")); + break; + } + break; + + default: + break; } -*/ +} + +bool +COSXScreen::onMouseMove(SInt32 mx, SInt32 my) +{ + LOG((CLOG_DEBUG2 "mouse move %+d,%+d", mx, my)); + + SInt32 x = mx - m_xCursor; + SInt32 y = my - m_yCursor; + + if ((x == 0 && y == 0) || (mx == m_xCenter && mx == m_yCenter)) { + return true; + } + + // save position to compute delta of next motion + m_xCursor = mx; + m_yCursor = my; + + if (m_isOnScreen) { + // motion on primary screen + sendEvent(getMotionOnPrimaryEvent(), + CMotionInfo::alloc(m_xCursor, m_yCursor)); + } + else { + // motion on secondary screen. warp mouse back to + // center. + warpCursor(m_xCenter, m_yCenter); + + // examine the motion. if it's about the distance + // from the center of the screen to an edge then + // it's probably a bogus motion that we want to + // ignore (see warpCursorNoFlush() for a further + // description). + static SInt32 bogusZoneSize = 10; + if (-x + bogusZoneSize > m_xCenter - m_x || + x + bogusZoneSize > m_x + m_w - m_xCenter || + -y + bogusZoneSize > m_yCenter - m_y || + y + bogusZoneSize > m_y + m_h - m_yCenter) { + LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y)); + } + else { + // send motion + sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y)); + } + } + + return true; +} + +bool +COSXScreen::onMouseButton(bool pressed, UInt16 macButton) const +{ + // Buttons 2 and 3 are inverted on the mac + ButtonID button = mapMacButtonToSynergy(macButton); + + if (pressed) { + LOG((CLOG_DEBUG1 "event: button press button=%d", button)); + if (button != kButtonNone) { + sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); + } + } + else { + LOG((CLOG_DEBUG1 "event: button release button=%d", button)); + if (button != kButtonNone) { + sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); + } + } + + return true; +} + +bool +COSXScreen::onMouseWheel(SInt32 delta) const +{ + LOG((CLOG_DEBUG1 "event: button wheel delta=%d", delta)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(delta)); + return true; +} + +pascal void +COSXScreen::displayManagerCallback(void* inUserData, SInt16 inMessage, void*) +{ + COSXScreen* screen = (COSXScreen*)inUserData; + + if (inMessage == kDMNotifyEvent) { + screen->onDisplayChange(); + } +} + +bool +COSXScreen::onDisplayChange() +{ + // screen resolution may have changed. save old shape. + SInt32 xOld = m_x, yOld = m_y, wOld = m_w, hOld = m_h; + + // update shape + updateScreenShape(); + + // do nothing if resolution hasn't changed + if (xOld != m_x || yOld != m_y || wOld != m_w || hOld != m_h) { + if (m_isPrimary) { + // warp mouse to center if off screen + if (!m_isOnScreen) { + warpCursor(m_xCenter, m_yCenter); + } + } + + // send new screen info + sendEvent(getShapeChangedEvent()); + } + + return true; +} + + +bool +COSXScreen::onKey(EventRef event) const +{ + UInt32 eventKind = GetEventKind(event); + + KeyButton button; + GetEventParameter(event, kEventParamKeyCode, typeUInt32, + NULL, sizeof(button), NULL, &button); + + bool down = (eventKind == kEventRawKeyDown); + bool up = (eventKind == kEventRawKeyUp); + bool isRepeat = (eventKind == kEventRawKeyRepeat); + + LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, button)); + + if (down) { + m_keyState->setKeyDown(button, true); + } + else if (up) { + m_keyState->setKeyDown(button, false); + } + + KeyModifierMask mask; + KeyID key = m_keyState->mapKeyFromEvent(event, &mask); + + m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, + key, mask, 0, button); + + return true; +} + +ButtonID +COSXScreen::mapMacButtonToSynergy(UInt16 macButton) const +{ + switch (macButton) { + case 1: + return kButtonLeft; + + case 2: + return kButtonRight; + + case 3: + return kButtonMiddle; + } + + return kButtonNone; } void @@ -574,7 +899,7 @@ COSXScreen::updateScreenShape() m_xCenter = m_x + (m_w >> 1); m_yCenter = m_y + (m_h >> 1); - LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d on %u %s", m_x, m_y, m_w, m_h, displayCount, (displayCount == 1) ? "display" : "displays")); - free(displays); + + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d on %u %s", m_x, m_y, m_w, m_h, displayCount, (displayCount == 1) ? "display" : "displays")); } diff --git a/lib/platform/COSXScreen.h b/lib/platform/COSXScreen.h index 12aca416..dc61af3e 100644 --- a/lib/platform/COSXScreen.h +++ b/lib/platform/COSXScreen.h @@ -28,11 +28,6 @@ public: COSXScreen(bool isPrimary); virtual ~COSXScreen(); - //! @name manipulators - //@{ - - //@} - // IScreen overrides virtual void* getEventTarget() const; virtual bool getClipboard(ClipboardID id, IClipboard*) const; @@ -79,8 +74,28 @@ private: void postMouseEvent(const CGPoint &) const; // convenience function to send events - void sendEvent(CEvent::Type type, void* = NULL); - void sendClipboardEvent(CEvent::Type type, ClipboardID id); + void sendEvent(CEvent::Type type, void* = NULL) const; + void sendClipboardEvent(CEvent::Type type, ClipboardID id) const; + + // message handlers + bool onMouseMove(SInt32 x, SInt32 y); + // mouse button handler. pressed is true if this is a mousedown + // event, false if it is a mouseup event. macButton is the index + // of the button pressed using the mac button mapping. + bool onMouseButton(bool pressed, UInt16 macButton) const; + bool onMouseWheel(SInt32 delta) const; + + bool onDisplayChange(); + + bool onKey(EventRef event) const; + + // map mac mouse button to synergy buttons + ButtonID mapMacButtonToSynergy(UInt16) const; + + /// Resolution switch callback + static pascal void displayManagerCallback(void* inUserData, + SInt16 inMessage, void* inNotifyData); + private: // true if screen is being used as a primary screen, false otherwise @@ -115,6 +130,17 @@ private: // clipboard stuff bool m_ownClipboard; + + // window object that gets user input events when the server + // has focus. + WindowRef m_hiddenWindow; + // window object that gets user input events when the server + // does not have focus. + WindowRef m_userInputWindow; + + // display manager stuff (to get screen resolution switches). + DMExtendedNotificationUPP m_displayManagerNotificationUPP; + ProcessSerialNumber m_PSN; }; #endif diff --git a/synergy.xcode/project.pbxproj b/synergy.xcode/project.pbxproj index 37e1c34e..0e9f1125 100644 --- a/synergy.xcode/project.pbxproj +++ b/synergy.xcode/project.pbxproj @@ -71,6 +71,8 @@ //084 08FB7793FE84155DC02AAC07 = { buildSettings = { + MACOSX_DEPLOYMENT_TARGET = 10.2; + SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; }; buildStyles = ( 014CEA460018CE2711CA2923, @@ -186,6 +188,7 @@ files = ( 4C537F15064E293000D3815C, 4C537F17064E293000D3815C, + 4C7D7CDA066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -214,6 +217,7 @@ GCC_WARN_UNKNOWN_PRAGMAS = NO; HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform ./lib/client"; INSTALL_PATH = /usr/local/bin; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -455,6 +459,7 @@ 4C537F51064E2A0F00D3815C, 4C537F53064E2A0F00D3815C, 4C537F55064E2A0F00D3815C, + 4C7D7CE4066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -489,6 +494,7 @@ HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform"; INSTALL_PATH = /usr/local/lib; LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -874,6 +880,7 @@ 4C5E868A0648C2ED003C637B = { buildActionMask = 2147483647; files = ( + 4C7D7CDD066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -900,6 +907,7 @@ GCC_WARN_UNKNOWN_PRAGMAS = NO; INSTALL_PATH = /usr/local/lib; LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -1328,6 +1336,7 @@ }; 4C5E86CA0648C6FB003C637B = { children = ( + 4C7D7CD9066319560097CA11, 4C5E86CB0648C725003C637B, ); isa = PBXGroup; @@ -3246,11 +3255,93 @@ settings = { }; }; + 4C7D7CD9066319560097CA11 = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = common.h; + path = lib/common/common.h; + refType = 4; + sourceTree = ""; + }; + 4C7D7CDA066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CDB066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CDC066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CDD066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CDE066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CDF066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CE0066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CE1066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CE2066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CE3066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CE4066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; + 4C7D7CE5066319560097CA11 = { + fileRef = 4C7D7CD9066319560097CA11; + isa = PBXBuildFile; + settings = { + }; + }; 4CB43775063E406A00969041 = { buildActionMask = 2147483647; files = ( 4C5E87BE0648C969003C637B, 4C5E87C00648C969003C637B, + 4C7D7CE5066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -3279,6 +3370,7 @@ HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform"; INSTALL_PATH = /usr/local/lib; LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -3365,6 +3457,7 @@ 4C5E86BF0648C412003C637B, 4C5E86C30648C653003C637B, 4C5E86C70648C6B7003C637B, + 4C7D7CDC066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -3405,6 +3498,7 @@ HEADER_SEARCH_PATHS = ./lib/common; INSTALL_PATH = /usr/local/lib; LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -3452,6 +3546,7 @@ 4C5E87090648C7B9003C637B, 4C5E870A0648C7B9003C637B, 4C5E870C0648C7B9003C637B, + 4C7D7CDE066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -3489,6 +3584,7 @@ HEADER_SEARCH_PATHS = "./lib/common ./lib/arch"; INSTALL_PATH = /usr/local/lib; LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -3580,6 +3676,7 @@ 4C5E871F0648C809003C637B, 4C5E87210648C809003C637B, 4C5E87220648C809003C637B, + 4C7D7CDF066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -3610,6 +3707,7 @@ HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base"; INSTALL_PATH = /usr/local/lib; LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -3658,6 +3756,7 @@ 4C5E87310648C83C003C637B, 4C5E87320648C83C003C637B, 4C5E87340648C83C003C637B, + 4C7D7CE0066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -3687,6 +3786,7 @@ HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt"; INSTALL_PATH = /usr/local/lib; LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -3725,6 +3825,7 @@ 4C5E875B0648C870003C637B, 4C5E875C0648C870003C637B, 4C5E875E0648C870003C637B, + 4C7D7CE1066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -3759,6 +3860,7 @@ HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io"; INSTALL_PATH = /usr/local/lib; LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -3876,6 +3978,7 @@ 4C5E879C0648C8BD003C637B, 4C5E879E0648C8BD003C637B, 4C5E87A00648C8BD003C637B, + 4C7D7CE2066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -3914,6 +4017,7 @@ HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net"; INSTALL_PATH = /usr/local/lib; LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -3972,6 +4076,7 @@ 4CD034960650B6F100525ED1, 4CD034980650B6F100525ED1, 4CD0349A0650B6F100525ED1, + 4C7D7CE3066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -4006,6 +4111,7 @@ HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io /lib/synergy"; INSTALL_PATH = /usr/local/lib; LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; @@ -4034,6 +4140,7 @@ files = ( 4C5E87C70648C9D2003C637B, 4C5E87C90648C9D2003C637B, + 4C7D7CDB066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -4064,6 +4171,7 @@ HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform ./lib/client"; INSTALL_PATH = /usr/local/bin; LIBRARY_SEARCH_PATHS = ""; + MACOSX_DEPLOYMENT_TARGET = 10.2; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; From 70e2d2b89a28edac1270b559298f6981468a9473 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 26 May 2004 20:20:23 +0000 Subject: [PATCH 678/807] Updated documentation. --- AUTHORS | 7 +- ChangeLog | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ INSTALL | 43 ++++++-- NEWS | 17 +++ README | 16 ++- TODO | 4 +- 6 files changed, 372 insertions(+), 21 deletions(-) diff --git a/AUTHORS b/AUTHORS index 22f10267..c2aa5065 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,6 +1,7 @@ Synergy Authors =============== -Chris Schoeneman -- creator, maintainer, X11, Win32 -Ryan Breen -- Mac OS X port -Guido Poschta -- Windows installer +Chris Schoeneman -- Creator, owner, X11, Win32 +Ryan Breen -- Initial Mac OS X port +Guido Poschta -- Windows installer +Bertrand Landry Hetu -- Mac OS X port diff --git a/ChangeLog b/ChangeLog index 38a96f79..2e77a52c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,309 @@ +2004/05/26 19:23:32 crs +//depot/project/synergy/lib/common/MacOSXPrecomp.h +//depot/project/synergy/lib/common/common.h +//depot/project/synergy/lib/platform/COSXClipboardTextConverter.cpp +//depot/project/synergy/lib/platform/COSXClipboardTextConverter.h +//depot/project/synergy/lib/platform/COSXEventQueueBuffer.cpp +//depot/project/synergy/lib/platform/COSXKeyState.cpp +//depot/project/synergy/lib/platform/COSXKeyState.h +//depot/project/synergy/lib/platform/COSXScreen.cpp +//depot/project/synergy/lib/platform/COSXScreen.h +//depot/project/synergy/synergy.xcode/project.pbxproj + +Merged Bertrand's OS X changes. Also added support for mouse wheel +on OS X server. + +---------- +2004/05/18 20:32:13 crs +//depot/project/synergy/lib/platform/CMSWindowsScreen.cpp + +If the server manages to detect ctrl+alt+del it will no longer send +that to the client. If it did then the user could see the effect of +ctrl+alt+del on both the server and client which we never want. The +user can use ctrl+alt+pause to emulate ctrl+alt+del on the client. + +---------- +2004/05/18 20:26:48 crs +//depot/project/synergy/lib/platform/CXWindowsEventQueueBuffer.cpp +//depot/project/synergy/lib/platform/CXWindowsEventQueueBuffer.h + +Fixed bug that could allow multiple threads to simultaneously access +the X11 display connection. The only problematic method was +CXWindowsEventQueueBuffer::addEvent given that the other event queue +methods are only called from the main thread. + +---------- +2004/05/17 21:55:55 crs +//depot/project/synergy/cmd/synergyc/synergyc.cpp + +Fixed logging of connection to server. Was DEBUG now NOTE. + +---------- +2004/05/17 21:55:38 crs +//depot/project/synergy/lib/platform/CMSWindowsKeyState.cpp + +Fixed ctrl+alt+del emulation on win32 server. It was mapping +VK_DELETE to the keypad delete key. This key is not interpreted +on the client as causing ctrl+alt+del. + +---------- +2004/05/16 18:04:36 crs +//depot/project/synergy/lib/client/CServerProxy.cpp +//depot/project/synergy/lib/server/CClientProxy1_0.cpp +//depot/project/synergy/lib/synergy/ProtocolTypes.h + +Fixed handling of screen resolution changes. + +---------- +2004/05/16 18:03:36 crs +//depot/project/synergy/cmd/launcher/CAutoStart.cpp + +Changed (win NT) service to depend on the 'Browser' service to +ensure correct startup order. + +---------- +2004/05/16 18:02:49 crs +//depot/project/synergy/all.dsp +//depot/project/synergy/cmd/exec.dsp +//depot/project/synergy/cmd/launcher/launcher.dsp +//depot/project/synergy/cmd/synergyc/synergyc.dsp +//depot/project/synergy/cmd/synergys/synergys.dsp +//depot/project/synergy/dist/nullsoft/installer.dsp +//depot/project/synergy/dist/nullsoft/synergy.nsi +//depot/project/synergy/lib/arch/arch.dsp +//depot/project/synergy/lib/base/base.dsp +//depot/project/synergy/lib/client/client.dsp +//depot/project/synergy/lib/common/common.dsp +//depot/project/synergy/lib/io/io.dsp +//depot/project/synergy/lib/mt/mt.dsp +//depot/project/synergy/lib/net/net.dsp +//depot/project/synergy/lib/platform/makehook.dsp +//depot/project/synergy/lib/platform/platform.dsp +//depot/project/synergy/lib/platform/synrgyhk.dsp +//depot/project/synergy/lib/server/server.dsp +//depot/project/synergy/lib/synergy/libsynergy.dsp + +Changed VC++ projects to put release targets in ./build, debug +targets in ./debug, and intermediate files under ./gen. + +---------- +2004/05/15 19:44:05 crs +//depot/project/synergy/configure.in +//depot/project/synergy/lib/common/Version.h + +Changed version to 1.1.6. + +---------- +2004/05/15 19:43:33 crs +//depot/project/synergy/lib/platform/CMSWindowsScreen.cpp + +Avoided duplicate logging of screen size on win32. + +---------- +2004/05/15 19:41:46 crs +//depot/project/synergy/Makefile.am +//depot/project/synergy/configure.in +//depot/project/synergy/lib/common/common.h +//depot/project/synergy/lib/platform/COSXClipboard.cpp +//depot/project/synergy/lib/platform/COSXClipboard.h +//depot/project/synergy/lib/platform/COSXClipboardAnyTextConverter.cpp +//depot/project/synergy/lib/platform/COSXClipboardAnyTextConverter.h +//depot/project/synergy/lib/platform/COSXClipboardTextConverter.cpp +//depot/project/synergy/lib/platform/COSXClipboardTextConverter.h +//depot/project/synergy/lib/platform/COSXClipboardUTF16Converter.cpp +//depot/project/synergy/lib/platform/COSXClipboardUTF16Converter.h +//depot/project/synergy/lib/platform/COSXEventQueueBuffer.cpp +//depot/project/synergy/lib/platform/COSXEventQueueBuffer.h +//depot/project/synergy/lib/platform/COSXScreen.cpp +//depot/project/synergy/lib/platform/COSXScreen.h +//depot/project/synergy/lib/platform/Makefile.am +//depot/project/synergy/synergy.xcode/project.pbxproj + +Added bertrand landry hetu's mac OS X port to date. + +---------- +2004/05/12 20:28:00 crs +//depot/project/synergy/lib/platform/CXWindowsScreen.cpp +//depot/project/synergy/lib/platform/CXWindowsScreen.h + +Now restoring input focus when entering the screen to the window +that had the focus when the screen was left. + +---------- +2004/05/12 19:50:58 crs +//depot/project/synergy/lib/arch/CArchNetworkBSD.cpp +//depot/project/synergy/lib/arch/CArchNetworkWinsock.cpp + +Fixed thread reference leak in network code. + +---------- +2004/05/12 19:12:28 crs +//depot/project/synergy/configure.in + +Added configure option to enable debug builds. If not enabled then +asserts are disabled. + +---------- +2004/05/12 18:54:03 crs +//depot/project/synergy/lib/platform/CXWindowsClipboardBMPConverter.cpp + +Fixed build error in gcc 3.3. + +---------- +audrey2% ./nodist/p4tolog -p //depot/project/synergy/ -m 15 | less +audrey2% ./nodist/p4tolog -p //depot/project/synergy/ -m 15 +2004/05/26 19:23:32 crs +lib/common/MacOSXPrecomp.h +lib/common/common.h +lib/platform/COSXClipboardTextConverter.cpp +lib/platform/COSXClipboardTextConverter.h +lib/platform/COSXEventQueueBuffer.cpp +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +synergy.xcode/project.pbxproj + +Merged Bertrand's OS X changes. Also added support for mouse wheel +on OS X server. + +---------- +2004/05/18 20:32:13 crs +lib/platform/CMSWindowsScreen.cpp + +If the server manages to detect ctrl+alt+del it will no longer send +that to the client. If it did then the user could see the effect of +ctrl+alt+del on both the server and client which we never want. The +user can use ctrl+alt+pause to emulate ctrl+alt+del on the client. + +---------- +2004/05/18 20:26:48 crs +lib/platform/CXWindowsEventQueueBuffer.cpp +lib/platform/CXWindowsEventQueueBuffer.h + +Fixed bug that could allow multiple threads to simultaneously access +the X11 display connection. The only problematic method was +CXWindowsEventQueueBuffer::addEvent given that the other event queue +methods are only called from the main thread. + +---------- +2004/05/17 21:55:55 crs +cmd/synergyc/synergyc.cpp + +Fixed logging of connection to server. Was DEBUG now NOTE. + +---------- +2004/05/17 21:55:38 crs +lib/platform/CMSWindowsKeyState.cpp + +Fixed ctrl+alt+del emulation on win32 server. It was mapping +VK_DELETE to the keypad delete key. This key is not interpreted +on the client as causing ctrl+alt+del. + +---------- +2004/05/16 18:04:36 crs +lib/client/CServerProxy.cpp +lib/server/CClientProxy1_0.cpp +lib/synergy/ProtocolTypes.h + +Fixed handling of screen resolution changes. + +---------- +2004/05/16 18:03:36 crs +cmd/launcher/CAutoStart.cpp + +Changed (win NT) service to depend on the 'Browser' service to +ensure correct startup order. + +---------- +2004/05/16 18:02:49 crs +all.dsp +cmd/exec.dsp +cmd/launcher/launcher.dsp +cmd/synergyc/synergyc.dsp +cmd/synergys/synergys.dsp +dist/nullsoft/installer.dsp +dist/nullsoft/synergy.nsi +lib/arch/arch.dsp +lib/base/base.dsp +lib/client/client.dsp +lib/common/common.dsp +lib/io/io.dsp +lib/mt/mt.dsp +lib/net/net.dsp +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/server.dsp +lib/synergy/libsynergy.dsp + +Changed VC++ projects to put release targets in ./build, debug +targets in ./debug, and intermediate files under ./gen. + +---------- +2004/05/15 19:44:05 crs +configure.in +lib/common/Version.h + +Changed version to 1.1.6. + +---------- +2004/05/15 19:43:33 crs +lib/platform/CMSWindowsScreen.cpp + +Avoided duplicate logging of screen size on win32. + +---------- +2004/05/15 19:41:46 crs +Makefile.am +configure.in +lib/common/common.h +lib/platform/COSXClipboard.cpp +lib/platform/COSXClipboard.h +lib/platform/COSXClipboardAnyTextConverter.cpp +lib/platform/COSXClipboardAnyTextConverter.h +lib/platform/COSXClipboardTextConverter.cpp +lib/platform/COSXClipboardTextConverter.h +lib/platform/COSXClipboardUTF16Converter.cpp +lib/platform/COSXClipboardUTF16Converter.h +lib/platform/COSXEventQueueBuffer.cpp +lib/platform/COSXEventQueueBuffer.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/Makefile.am +synergy.xcode/project.pbxproj + +Added bertrand landry hetu's mac OS X port to date. + +---------- +2004/05/12 20:28:00 crs +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h + +Now restoring input focus when entering the screen to the window +that had the focus when the screen was left. + +---------- +2004/05/12 19:50:58 crs +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkWinsock.cpp + +Fixed thread reference leak in network code. + +---------- +2004/05/12 19:12:28 crs +configure.in + +Added configure option to enable debug builds. If not enabled then +asserts are disabled. + +---------- +2004/05/12 18:54:03 crs +lib/platform/CXWindowsClipboardBMPConverter.cpp + +Fixed build error in gcc 3.3. + +---------- 2004/05/04 20:45:06 crs cmd/launcher/CGlobalOptions.cpp cmd/launcher/launcher.rc diff --git a/INSTALL b/INSTALL index 0f2666a1..5bf35641 100644 --- a/INSTALL +++ b/INSTALL @@ -13,6 +13,9 @@ To build synergy from the sources you'll need the following: * gcc 2.95 (or up) * X11R4 or up headers and libraries + Mac OS X: + * XCode; or gcc 2.95 (or up) and make + In this document, "Unix" means any of the supported Unix or Unix-like (e.g. Linux) operating systems. @@ -20,7 +23,8 @@ In this document, "Unix" means any of the supported Unix or Unix-like Configuring the build --------------------- -This step is only necessary when building on Unix. +This step is only necessary when building on Unix or Mac OS X if not +using XCode. To configure the build for your platform use the configure script: @@ -43,9 +47,9 @@ Building Windows: Start VC++ and open `synergy.dsw'. Set the active configuration (Build | Set Active Configuration) to `All - Debug' or `All - Release' - then build. Binaries are built into ./Debug or ./Release. + then build. Binaries are built into ./debug or ./build. -Unix: +Unix or Mac OS X without XCode: Simply enter: make @@ -53,6 +57,10 @@ Unix: This will build the client and server and leave them in their respective source directories. +Mac OS X with XCode: + Start XCode and open the synery.xcode project. Build the 'all' + project using the Deployment flavor. + Installing ---------- @@ -72,12 +80,18 @@ Windows: * synergys.exe * synrgyhk.dll -Unix: +Unix or Mac OS X without XCode: make install will install the client and server into /usr/local/bin unless you specified a different directory when you ran configure. +Mac OS X with XCode: + Copy the following files from ./build to a convenient location: + + * synergyc + * synergys + See `Starting Automatically on ...' below for details on how to have synergy start up automatically when the computer starts. @@ -152,8 +166,8 @@ See `Starting Automatically on Windows' below for configuring synergy to start automatically when the computer starts. -Configuring the Server on Unix ------------------------------- +Configuring the Server on Unix or Mac OS X +------------------------------------------ The synergy server requires configuration. The configuration file is a plain text file broken into sections. Each section has the form: @@ -164,7 +178,8 @@ plain text file broken into sections. Each section has the form: Comments are introduced by `#' and continue to the end of the line. The file can have the following sections. The `screens' section must -appear before the `links' and `aliases' sections. +appear before the `links' and `aliases' sections. Use any text editor +to create the configuration file. * screens is a list of screen names, one name per line, each @@ -348,8 +363,8 @@ file if the user doesn't specify a path using the `--config' command line option. `synergys --help' reports those pathnames. -Running the Server on Unix --------------------------- +Running the Server on Unix or Mac OS X +-------------------------------------- Run the server on the computer that has the keyboard and mouse to be shared. You must have prepared a configuration file before @@ -374,8 +389,8 @@ See `Starting Automatically on Unix' below for running synergy automatically when the X server starts. -Running the Client on Unix --------------------------- +Running the Client on Unix or Mac OS X +-------------------------------------- Run the client on all computers that aren't the server using the following command line: @@ -500,6 +515,12 @@ prevent a synergy client from synthesizing mouse and keyboard input, though. +Starting Automatically on Mac OS X +---------------------------------- + +TBD + + Network Security ---------------- diff --git a/NEWS b/NEWS index e2888cab..eca065d9 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,23 @@ Synergy News ============ +* May-26-2004 - Synergy 1.1.6 released + + Made following changes: + * Added preliminary Mac OS X support (client and server) + * Fixed ctrl+alt+del emulation on win32 + * Fixed ctrl+alt+del on win32 server acting on both client and server + * Fixed handling of screen resolution changes on win32 + * Fixed 'unexpected async reply' on X11 + * Added dependency to win32 service to avoid startup race condition + * Fixed reference count bug + * Keyboard input focus now restored on X11 (fixes loss of input in some games) + + The OS X port does not yet support: + * HTML and bitmap clipboard data + * Screen saver synchronization + * Non-US English keyboards + * May-05-2004 - Synergy 1.1.5 released Made following changes: diff --git a/README b/README index 5cdfc938..f67347a7 100644 --- a/README +++ b/README @@ -52,6 +52,8 @@ System Requirements * Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family); +* Mac OS X 10.3 (10.2 may work); + * Unix: X Windows version 11 revision 4 or up with the XTEST extension (use `xdpyinfo | grep XTEST' to check for XTEST). @@ -62,6 +64,12 @@ for other platforms are welcome (including patches that package binaries); See the contact information available off of the synergy home page or use the patch page on sourceforge. +The Mac OS X port is incomplete. It does not synchronize the screen saver, +only text clipboard data works (i.e. HTML and bitmap data do not work), +non-US English keyboards are untested and probably don't work, and there +may be problems with mouse pointer and mouse wheel acceleration. Other +problems should be filed as bugs (see BUGS). + Installation ------------ @@ -120,8 +128,8 @@ Third, you configure and start the server. read the log messages to determine the problem then correct it and try `Test' again. - Unix - ---- + Unix or MacOS X + --------------- Create a text file named synergy.conf with the following: section: screens @@ -175,8 +183,8 @@ Finally, start the clients. about to quit; read the log messages to determine the problem then correct it and try `Test' again. - Unix - ---- + Unix or MacOS X + --------------- To start a client, enter the following: diff --git a/TODO b/TODO index 5c05ce8b..bbb2a1aa 100644 --- a/TODO +++ b/TODO @@ -18,9 +18,7 @@ Things to do to synergy, in no particular order: * Port to other platforms - Most desired is MacOS X. Ryan Breen has a partial port to Mac OS X. - Only the client is ported and there is no clipboard or screen saver - support. It's available from http://www.ryanbreen.com/synergy. + The MacOS X port is still incomplete but getting close. * Write man/html pages From 2de2b72af9c338b1fd99a378e7a2a597b9a94a61 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 7 Jun 2004 21:06:49 +0000 Subject: [PATCH 679/807] Failed to reset flag in X11 event queue buffer and that could cause multiple threads to access the X display connection simultaneously which causes synergy to die. --- lib/platform/CXWindowsEventQueueBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/CXWindowsEventQueueBuffer.cpp b/lib/platform/CXWindowsEventQueueBuffer.cpp index 686865b9..f1ef5551 100644 --- a/lib/platform/CXWindowsEventQueueBuffer.cpp +++ b/lib/platform/CXWindowsEventQueueBuffer.cpp @@ -119,7 +119,7 @@ CXWindowsEventQueueBuffer::waitForEvent(double dtimeout) { // we're no longer waiting for events CLock lock(&m_mutex); - m_waiting = true; + m_waiting = false; } CThread::testCancel(); From 20bfb04886be480654d5a31f176860b186f0ac2d Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 10 Jun 2004 19:32:40 +0000 Subject: [PATCH 680/807] Changed version to 1.1.7. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 8f5d4b52..b6e02bd1 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=1 -RELEASE_VERSION=6 +RELEASE_VERSION=7 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index baead10e..997b811d 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.6" +# define VERSION "1.1.7" #endif // important strings From f1dfc69d083a68447fcb4b678def29a862f5517f Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 10 Jun 2004 19:39:07 +0000 Subject: [PATCH 681/807] Removed dependency of service on Browser. Browser isn't always available and, if it's not, synergy won't start. Users may have to use an IP server address instead of a hostname since the service may start before the service that resolves hostnames. If I knew what that service was I'd depend on it instead. --- cmd/launcher/CAutoStart.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp index 2b58b202..d5eee0ce 100644 --- a/cmd/launcher/CAutoStart.cpp +++ b/cmd/launcher/CAutoStart.cpp @@ -199,12 +199,12 @@ CAutoStart::onInstall(bool allUsers) // clear error message m_errorMessage = ""; - // install. depend on Browser + // install try { ARCH->installDaemon(m_name.c_str(), m_isServer ? SERVER_DAEMON_INFO : CLIENT_DAEMON_INFO, appPath.c_str(), m_cmdLine.c_str(), - "Browser\0", allUsers); + NULL, allUsers); askOkay(m_hwnd, getString(IDS_INSTALL_TITLE), getString(allUsers ? IDS_INSTALLED_SYSTEM : From 613413fb50252637b718656a08c0bccfc19d7019 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 10 Jun 2004 19:42:01 +0000 Subject: [PATCH 682/807] Added OS X precompiled header file for XCode compiles. --- lib/common/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index 092fef3e..4b9e634f 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -17,6 +17,7 @@ EXTRA_DIST = \ common.dsp \ BasicTypes.h \ IInterface.h \ + MacOSXPrecomp.h \ common.h \ stdbitset.h \ stddeque.h \ From 9c9db6958f6989930bce57f8a26d7d58135e7cca Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 10 Jun 2004 19:56:35 +0000 Subject: [PATCH 683/807] Changed O_NDELAY to O_NONBLOCK. On some versions of Unix, read return 0 when O_NDELAY is set and there is nothing to read. We want the O_NONBLOCK behavior where read returns -1 and sets errno to EAGAIN when there is nothing to read. --- lib/arch/CArchNetworkBSD.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 60253b00..e2efc8da 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -561,10 +561,10 @@ CArchNetworkBSD::setBlockingOnSocket(int fd, bool blocking) throwError(errno); } if (blocking) { - mode &= ~O_NDELAY; + mode &= ~O_NONBLOCK; } else { - mode |= O_NDELAY; + mode |= O_NONBLOCK; } if (fcntl(fd, F_SETFL, mode) == -1) { throwError(errno); From 483645203800d5afe165091ba4b1641e6a980869 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 10 Jun 2004 21:25:09 +0000 Subject: [PATCH 684/807] Fixed assertion failure when client connection fails immediately. --- lib/client/CClient.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 59bbc4fc..15092cf6 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -90,8 +90,11 @@ CClient::connect() socket->connect(m_serverAddress); } catch (XBase& e) { + cleanupTimer(); + cleanupConnecting(); delete m_stream; m_stream = NULL; + LOG((CLOG_DEBUG1 "connection failed")); sendConnectionFailedEvent(e.what()); return; } From a80ddb4a7808af6a84e8e055c4c697cd1ca08325 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 12 Jun 2004 20:46:35 +0000 Subject: [PATCH 685/807] Fixed bug in converting wide characters to multibyte. --- lib/arch/CMultibyte.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp index 4f9dc5e1..2b9bb8ca 100644 --- a/lib/arch/CMultibyte.cpp +++ b/lib/arch/CMultibyte.cpp @@ -86,7 +86,7 @@ ARCH_STRING::convStringWCToMB(char* dst, } len += mblen; } - int mblen = wctomb(NULL, L'\0'); + int mblen = wctomb(dummy, L'\0'); if (mblen != -1) { len += mblen - 1; } From ccb45bc2bc904e7c3fcdf0fc4d5e6c4aca989e73 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 12 Jun 2004 20:48:04 +0000 Subject: [PATCH 686/807] (Maybe) fixed a problem detecting when win32 screen saver started. --- lib/platform/CMSWindowsDesks.cpp | 6 ++++++ lib/platform/CMSWindowsDesks.h | 1 + lib/platform/CMSWindowsScreenSaver.cpp | 17 ++++++++++++++--- lib/platform/CMSWindowsScreenSaver.h | 4 ++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index b6319bc7..8c71b4b5 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -115,6 +115,8 @@ CMSWindowsDesks::~CMSWindowsDesks() void CMSWindowsDesks::enable() { + m_threadID = GetCurrentThreadId(); + // set the active desk and (re)install the hooks checkDesk(); @@ -844,6 +846,10 @@ CMSWindowsDesks::checkDesk() sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0); } } + else if (name != m_activeDeskName) { + // screen saver might have started + PostThreadMessage(m_threadID, SYNERGY_MSG_SCREEN_SAVER, TRUE, 0); + } } bool diff --git a/lib/platform/CMSWindowsDesks.h b/lib/platform/CMSWindowsDesks.h index 9b966416..df209eca 100644 --- a/lib/platform/CMSWindowsDesks.h +++ b/lib/platform/CMSWindowsDesks.h @@ -233,6 +233,7 @@ private: CEventQueueTimer* m_timer; // screen saver stuff + DWORD m_threadID; const IScreenSaver* m_screensaver; bool m_screensaverNotify; diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index b9101c9f..b3ab2779 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -43,7 +43,8 @@ CMSWindowsScreenSaver::CMSWindowsScreenSaver() : m_wasSecureAnInt(false), m_process(NULL), m_watch(NULL), - m_threadID(0) + m_threadID(0), + m_active(false) { // detect OS m_is95Family = false; @@ -76,6 +77,11 @@ CMSWindowsScreenSaver::~CMSWindowsScreenSaver() bool CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) { + // if already started then say it didn't just start + if (m_active) { + return false; + } + // screen saver may have started. look for it and get // the process. if we can't find it then assume it // didn't really start. we wait a moment before @@ -322,7 +328,8 @@ CMSWindowsScreenSaver::watchDesktop() // watch desktop in another thread LOG((CLOG_DEBUG "watching screen saver desktop")); - m_watch = new CThread(new TMethodJob(this, + m_active = true; + m_watch = new CThread(new TMethodJob(this, &CMSWindowsScreenSaver::watchDesktopThread)); } @@ -336,6 +343,7 @@ CMSWindowsScreenSaver::watchProcess(HANDLE process) if (process != NULL) { LOG((CLOG_DEBUG "watching screen saver process")); m_process = process; + m_active = true; m_watch = new CThread(new TMethodJob(this, &CMSWindowsScreenSaver::watchProcessThread)); } @@ -349,7 +357,8 @@ CMSWindowsScreenSaver::unwatchProcess() m_watch->cancel(); m_watch->wait(); delete m_watch; - m_watch = NULL; + m_watch = NULL; + m_active = false; } if (m_process != NULL) { CloseHandle(m_process); @@ -394,6 +403,7 @@ CMSWindowsScreenSaver::watchDesktopThread(void*) } // send screen saver deactivation message + m_active = false; PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); return; } @@ -409,6 +419,7 @@ CMSWindowsScreenSaver::watchProcessThread(void*) LOG((CLOG_DEBUG "screen saver died")); // send screen saver deactivation message + m_active = false; PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); return; } diff --git a/lib/platform/CMSWindowsScreenSaver.h b/lib/platform/CMSWindowsScreenSaver.h index 7cc7cdc9..bb43c703 100644 --- a/lib/platform/CMSWindowsScreenSaver.h +++ b/lib/platform/CMSWindowsScreenSaver.h @@ -83,6 +83,10 @@ private: UINT m_msg; WPARAM m_wParam; LPARAM m_lParam; + + // checkActive state. true if the screen saver is being watched + // for deactivation (and is therefore active). + bool m_active; }; #endif From 5c70584859b0ab3a13f0187ba942d8b947f322d8 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 13 Jun 2004 17:11:19 +0000 Subject: [PATCH 687/807] Updated documentation. --- ChangeLog | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ NEWS | 11 ++++++++++ 2 files changed, 72 insertions(+) diff --git a/ChangeLog b/ChangeLog index 2e77a52c..c533e6ba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,64 @@ +2004/06/12 20:48:04 crs +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CMSWindowsScreenSaver.h + +(Maybe) fixed a problem detecting when win32 screen saver started. + +---------- +2004/06/12 20:46:35 crs +lib/arch/CMultibyte.cpp + +Fixed bug in converting wide characters to multibyte. + +---------- +2004/06/10 21:25:09 crs +lib/client/CClient.cpp + +Fixed assertion failure when client connection fails immediately. + +---------- +2004/06/10 19:56:35 crs +lib/arch/CArchNetworkBSD.cpp + +Changed O_NDELAY to O_NONBLOCK. On some versions of Unix, read +return 0 when O_NDELAY is set and there is nothing to read. We +want the O_NONBLOCK behavior where read returns -1 and sets +errno to EAGAIN when there is nothing to read. + +---------- +2004/06/10 19:42:01 crs +lib/common/Makefile.am + +Added OS X precompiled header file for XCode compiles. + +---------- +2004/06/10 19:39:07 crs +cmd/launcher/CAutoStart.cpp + +Removed dependency of service on Browser. Browser isn't always +available and, if it's not, synergy won't start. Users may have +to use an IP server address instead of a hostname since the +service may start before the service that resolves hostnames. +If I knew what that service was I'd depend on it instead. + +---------- +2004/06/10 19:32:40 crs +configure.in +lib/common/Version.h + +Changed version to 1.1.7. + +---------- +2004/06/07 21:06:49 crs +lib/platform/CXWindowsEventQueueBuffer.cpp + +Failed to reset flag in X11 event queue buffer and that could cause +multiple threads to access the X display connection simultaneously +which causes synergy to die. + +---------- 2004/05/26 19:23:32 crs //depot/project/synergy/lib/common/MacOSXPrecomp.h //depot/project/synergy/lib/common/common.h diff --git a/NEWS b/NEWS index eca065d9..4cf80b42 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,17 @@ Synergy News ============ +* Jun-13-2004 - Synergy 1.1.7 released + + Made following changes: + * Added OS X precompiled header file forgotten in last build + * Fixed bug in fix for 'unexpected async reply' on X11 + * Removed dependency on "browser" service on win32 + * Fixed assertion failure when connection fails immediately + * Fixed failure to connect on AIX + * Fixed error in conversion from multibyte to wide characters + * Maybe fixed win32 screen saver detection + * May-26-2004 - Synergy 1.1.6 released Made following changes: From 43548cee892aa7645e86bf05556bdf0b566b69bc Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Jun 2004 21:07:48 +0000 Subject: [PATCH 688/807] Added NDEBUG to and removed debugging symbols from XCode deployment project. --- synergy.xcode/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/synergy.xcode/project.pbxproj b/synergy.xcode/project.pbxproj index 0e9f1125..cfc89ba2 100644 --- a/synergy.xcode/project.pbxproj +++ b/synergy.xcode/project.pbxproj @@ -27,6 +27,8 @@ buildSettings = { COPY_PHASE_STRIP = YES; GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; ZERO_LINK = NO; }; isa = PBXBuildStyle; From 95e4963f1d2c9e018c7cafaee0e593566fcda412 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 22 Jun 2004 21:11:14 +0000 Subject: [PATCH 689/807] Disable key event capture on X11. This was going to be used to detect synergy hotkeys but a design flaw in X11 makes it problematic with many applications. We'll have to fall back to the more traditional XGrabKey when the time comes. --- lib/platform/CXWindowsScreen.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index e20e07c7..e176ca37 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -1313,8 +1313,7 @@ CXWindowsScreen::doSelectEvents(Window w) const // select events of interest. do this before querying the tree so // we'll get notifications of children created after the XQueryTree() // so we won't miss them. - XSelectInput(m_display, w, KeyPressMask | KeyReleaseMask | - PointerMotionMask | SubstructureNotifyMask); + XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask); // recurse on child windows Window rw, pw, *cw; From 59e466da0a45eef118b8f054e43e792bddd9eed2 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 25 Jul 2004 14:18:50 +0000 Subject: [PATCH 690/807] Changed version to 1.1.8. --- configure.in | 2 +- lib/common/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index b6e02bd1..a7fc4b83 100644 --- a/configure.in +++ b/configure.in @@ -19,7 +19,7 @@ AC_CONFIG_AUX_DIR(config) dnl current version MAJOR_VERSION=1 MINOR_VERSION=1 -RELEASE_VERSION=7 +RELEASE_VERSION=8 dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 997b811d..766843ad 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.7" +# define VERSION "1.1.8" #endif // important strings From 970c8b2fbaae0d64b6207acb2a104534ff6e6a8a Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 28 Jul 2004 21:54:39 +0000 Subject: [PATCH 691/807] Added workaround for apparent bug in OS X 10.3.4. If you started a synergy client on that OS and pointed it at a system that wasn't listening for connections then instead of the connection attempt failing with 'connection refused' the system would claim the connection succeeded. A subsequent read would reveal the problem and synergy would "disconnect" and retry, causing the CPU to spin. The system does correctly set the socket error state so this workaround checks for socket errors when connecting whether or not select reports an error state. Also, sometimes the system doesn't claim success but doesn't report an error. Synergy eventually times out these attempts. --- lib/net/CTCPSocket.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index ee276ee0..213a0046 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -425,7 +425,24 @@ CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job, { CLock lock(&m_mutex); - if (error) { + // should only check for errors if error is true but checking a new + // socket (and a socket that's connecting should be new) for errors + // should be safe and Mac OS X appears to have a bug where a + // non-blocking stream socket that fails to connect immediately is + // reported by select as being writable (i.e. connected) even when + // the connection has failed. this is easily demonstrated on OS X + // 10.3.4 by starting a synergy client and telling to connect to + // another system that's not running a synergy server. it will + // claim to have connected then quickly disconnect (i guess because + // read returns 0 bytes). unfortunately, synergy attempts to + // reconnect immediately, the process repeats and we end up + // spinning the CPU. luckily, OS X does set SO_ERROR on the + // socket correctly when the connection has failed so checking for + // errors works. (curiously, sometimes OS X doesn't report + // connection refused. when that happens it at least doesn't + // report the socket as being writable so synergy is able to time + // out the attempt.) + if (true || error) { try { // connection may have failed or succeeded ARCH->throwErrorOnSocket(m_socket); From ef85356f454ea0729b441909ab795ff9302b43e6 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 29 Jul 2004 21:48:40 +0000 Subject: [PATCH 692/807] Worked around bug/weirdness on OS X. It's reporting that a non-blocking connect is available for writing (i.e. the connection was successful) when the connection has actually failed. This caused synergy to rapidly retry connecting. This change makes synergy check for a connection error whether one was reported or not. Thankfully, OS X does correctly set the socket error state when it bogusly reports a successful connection so we can tell the connection failed. --- lib/net/CTCPSocket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 213a0046..61ff3b8c 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -442,7 +442,7 @@ CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job, // connection refused. when that happens it at least doesn't // report the socket as being writable so synergy is able to time // out the attempt.) - if (true || error) { + if (error || true) { try { // connection may have failed or succeeded ARCH->throwErrorOnSocket(m_socket); From bac1f24a53aaf53ba22cd348f600176ba59e80fb Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 29 Jul 2004 21:50:17 +0000 Subject: [PATCH 693/807] Synergy now steals window activation when using low level hooks and a console window is the active window. This is to work around console windows preventing the hook from detecting the shift key. --- lib/platform/CMSWindowsDesks.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index 8c71b4b5..18e8bb0f 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -558,6 +558,10 @@ CMSWindowsDesks::deskEnter(CDesk* desk) SetWindowPos(desk->m_window, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW); + + // this is here only because of the "ConsoleWindowClass" stuff in + // deskLeave. + EnableWindow(desk->m_window, desk->m_lowLevel ? FALSE : TRUE); } void @@ -600,6 +604,26 @@ CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout) SetActiveWindow(desk->m_window); } + // if the active window is a console then activate our window. + // we do this because for some reason our hook reports unshifted + // characters when the shift is down and a console window is + // active. interestingly we do see the shift key go down and up. + // note that we must enable the window to activate it and we + // need to disable the window on deskEnter. + // FIXME -- figure out the real problem here and solve it. + else { + HWND foreground = GetForegroundWindow(); + if (foreground != NULL) { + char className[40]; + if (GetClassName(foreground, className, + sizeof(className) / sizeof(className[0])) && + strcmp(className, "ConsoleWindowClass") == 0) { + EnableWindow(desk->m_window, TRUE); + SetActiveWindow(desk->m_window); + } + } + } + // switch to requested keyboard layout ActivateKeyboardLayout(keyLayout, 0); } From b1ead05a3cb4b9b6ee97d76fd99cbdf68a1b8d75 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 29 Jul 2004 21:53:30 +0000 Subject: [PATCH 694/807] Worked around bogus key mapping on 95/98/me where multiple virtual keys are mapped to the same button. For example, the backslash virtual key shares a button with some other virtual key on british english key mappings. Synergy could end up using the wrong virtual key. In the given case, the other virtual key produced no character at all. To determine which virtual key should really be mapped to a button we map the button back to a virtual key and see if it's the virtual key we started with. Also fixed mapping of pause key. Previously, windows+pause sent to a win32 client wouldn't bring up system properties like it should. --- lib/platform/CMSWindowsKeyState.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index 1100d7aa..5f9d0ad5 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -1375,6 +1375,18 @@ CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey, default: button = (KeyButton)MapVirtualKey(virtualKey, 0); + + // okay, now we have the scan code for the virtual key. windows + // may map different virtual keys to the same button. for example, + // windows 95/98/me maps virtual keys 220 and 226 to scan code 86 + // in the british english keyboard map. why? who knows. it + // doesn't make any sense since a button can't actually generate + // more than one virtual key. to avoid this stupidity, we map the + // button back to a virtual key to see if it matches the starting + // point. + if (button == 0 || MapVirtualKey(button, 1) != virtualKey) { + return 0; + } break; } } @@ -1442,6 +1454,9 @@ CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey, case VK_DECIMAL: break; + case VK_PAUSE: + break; + default: button |= 0x100u; extended = 0; From 898a9af3b0f7a5855371cdb35d81e4f2b4685421 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 29 Jul 2004 21:57:42 +0000 Subject: [PATCH 695/807] Added comment about a problem detecting the screen saver. --- lib/platform/CMSWindowsScreenSaver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index b3ab2779..4a9130b1 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -398,6 +398,9 @@ CMSWindowsScreenSaver::watchDesktopThread(void*) // compare name to screen saver desktop name if (_tcsicmp(name, TEXT("Screen-saver")) == 0) { +// XXX -- getting "Default" as desk when screen saver was running +// before we started checking on it. can it be "Default" at other +// times to? how do we properly detect a running screen saver? // still the screen saver desktop so keep waiting continue; } From 3d2fa9265410dcbc29e6d42a75f2ebf97ab24867 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 29 Jul 2004 21:59:26 +0000 Subject: [PATCH 696/807] Fixed handling of ctrl and alt keys when using low level hooks. They were being discarded so the server wouldn't correctly send ctrl, alt, or AltGr to clients. --- lib/platform/CSynergyHook.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index b7441ae2..10a36cbf 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -400,8 +400,13 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) case VK_LMENU: case VK_RMENU: case VK_HANGUL: - // discard the control and alt modifiers - return true; + // pass the control and alt modifiers if using a low + // level hook, discard them if not. + if (g_hookThread == 0) { + return true; + } + break; + default: // discard From cf3647f7ccaa25a819d7b58645ab0d7887877763 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 29 Jul 2004 22:09:28 +0000 Subject: [PATCH 697/807] Worked around bug in ifstream::operator!() on OS X. --- cmd/synergys/synergys.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 6e1b758b..c5c45e51 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -814,7 +814,7 @@ loadConfig(const CString& pathname) // load configuration LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str())); std::ifstream configStream(pathname.c_str()); - if (!configStream) { + if (!configStream.is_open()) { throw XConfigRead("cannot open file"); } configStream >> *ARG->m_config; From 6284286d4f67c1c3415cf255730b14567b109b2e Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 29 Jul 2004 22:11:27 +0000 Subject: [PATCH 698/807] Fixed handling of modifier keys on OS X. Also made OS X client ignore small mouse wheel events (which seem to get sent by some win32 systems). Other platforms were already ignoring them. --- lib/platform/COSXKeyState.cpp | 90 +++++++++++++++++++++++++++++++++-- lib/platform/COSXKeyState.h | 12 +++++ lib/platform/COSXScreen.cpp | 63 ++++++++++++++++++++++-- lib/platform/COSXScreen.h | 3 ++ 4 files changed, 162 insertions(+), 6 deletions(-) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index ed9d295f..e1cd57a1 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -458,7 +458,7 @@ const KeyID COSXKeyState::s_virtualKey[] = COSXKeyState::COSXKeyState() { - // do nothing + setHalfDuplexMask(0); } COSXKeyState::~COSXKeyState() @@ -494,6 +494,12 @@ COSXKeyState::sendKeyEvent(void* target, } } +void +COSXKeyState::setHalfDuplexMask(KeyModifierMask mask) +{ + CKeyState::setHalfDuplexMask(mask | KeyModifierCapsLock); +} + bool COSXKeyState::fakeCtrlAltDel() { @@ -518,10 +524,33 @@ COSXKeyState::doUpdateKeys() // FIXME -- this probably needs to be more dynamic to support // non-english keyboards. also need to map modifiers needed // for each KeyID. + // FIXME -- add one so we don't use KeyButton 0 (reserved to be no key) for (UInt32 i = 0; i < sizeof(s_keys) / sizeof(s_keys[0]); ++i) { m_keyMap.insert(std::make_pair(s_keys[i].m_keyID, s_keys[i].m_button + 1)); } + + // add modifiers + KeyButtons keys; + addKeyButton(keys, kKeyShift_L); + addKeyButton(keys, kKeyShift_R); + addModifier(KeyModifierShift, keys); + keys.clear(); + addKeyButton(keys, kKeyControl_L); + addKeyButton(keys, kKeyControl_R); + addModifier(KeyModifierControl, keys); + keys.clear(); + addKeyButton(keys, kKeyAlt_L); + addKeyButton(keys, kKeyAlt_R); + addModifier(KeyModifierAlt, keys); + keys.clear(); + addKeyButton(keys, kKeySuper_L); + addKeyButton(keys, kKeySuper_R); + addModifier(KeyModifierSuper, keys); + keys.clear(); + addKeyButton(keys, kKeyCapsLock); + addModifier(KeyModifierCapsLock, keys); + keys.clear(); } void @@ -529,6 +558,7 @@ COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool isAutoRepeat) { LOG((CLOG_DEBUG2 "doFakeKeyEvent button:%d, press:%d", button, press)); // let system figure out character for us + // FIXME -- subtracting one because we added one in doUpdateKeys. CGPostKeyboardEvent(0, static_cast(button) - 1, press); } @@ -618,7 +648,7 @@ COSXKeyState::mapKeyFromEvent(EventRef event, KeyModifierMask* maskOut) const if (id != kKeyNone && c != 0) { // FIXME } - + // map modifier key if (maskOut != NULL) { activeMask &= ~KeyModifierModeSwitch; @@ -626,5 +656,59 @@ COSXKeyState::mapKeyFromEvent(EventRef event, KeyModifierMask* maskOut) const } return id; - } + +void +COSXKeyState::addKeyButton(KeyButtons& keys, KeyID id) const +{ + CKeyMap::const_iterator keyIndex = m_keyMap.find(id); + if (keyIndex == m_keyMap.end()) { + return; + } + // XXX -- subtract one because added one in doUpdateKeys + keys.push_back(keyIndex->second - 1); +} + +void +COSXKeyState::handleModifierKeys(void* target, + KeyModifierMask oldMask, KeyModifierMask newMask) +{ + // compute changed modifiers + KeyModifierMask changed = (oldMask ^ newMask); + + // synthesize changed modifier keys + if ((changed & KeyModifierShift) != 0) { + handleModifierKey(target, kKeyShift_L, + (newMask & KeyModifierShift) != 0); + } + if ((changed & KeyModifierControl) != 0) { + handleModifierKey(target, kKeyControl_L, + (newMask & KeyModifierControl) != 0); + } + if ((changed & KeyModifierAlt) != 0) { + handleModifierKey(target, kKeyAlt_L, + (newMask & KeyModifierAlt) != 0); + } + if ((changed & KeyModifierSuper) != 0) { + handleModifierKey(target, kKeySuper_L, + (newMask & KeyModifierSuper) != 0); + } + if ((changed & KeyModifierCapsLock) != 0) { + handleModifierKey(target, kKeyCapsLock, + (newMask & KeyModifierCapsLock) != 0); + } +} + +void +COSXKeyState::handleModifierKey(void* target, KeyID id, bool down) +{ + CKeyMap::const_iterator keyIndex = m_keyMap.find(id); + if (keyIndex == m_keyMap.end()) { + return; + } + // FIXME -- subtract one because we added one in doUpdateKeys + KeyButton button = keyIndex->second - 1; + setKeyDown(button, down); + sendKeyEvent(target, down, false, id, getActiveModifiers(), 0, button); +} + diff --git a/lib/platform/COSXKeyState.h b/lib/platform/COSXKeyState.h index 94e98f07..f83ec07d 100644 --- a/lib/platform/COSXKeyState.h +++ b/lib/platform/COSXKeyState.h @@ -36,13 +36,23 @@ public: KeyID mapKeyFromEvent(EventRef event, KeyModifierMask* maskOut) const; + //! Handle modifier key change + /*! + Determines which modifier keys have changed and updates the modifier + state and sends key events as appropriate. + */ + void handleModifierKeys(void* target, + KeyModifierMask oldMask, KeyModifierMask newMask); + // IKeyState overrides + virtual void setHalfDuplexMask(KeyModifierMask); virtual bool fakeCtrlAltDel(); virtual const char* getKeyName(KeyButton) const; virtual void sendKeyEvent(void* target, bool press, bool isAutoRepeat, KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button); + protected: // IKeyState overrides virtual void doUpdateKeys(); @@ -56,6 +66,8 @@ private: bool adjustModifiers(Keystrokes& keys, Keystrokes& undo, KeyModifierMask desiredMask) const; + void addKeyButton(KeyButtons& keys, KeyID id) const; + void handleModifierKey(void* target, KeyID id, bool down); private: typedef std::map CKeyMap; diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index 2cd002a1..d9dd62f8 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -303,6 +303,12 @@ COSXScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const void COSXScreen::fakeMouseWheel(SInt32 delta) const { + // synergy uses a wheel step size of 120. the mac uses a step size of 1. + delta /= 120; + if (delta == 0) { + return; + } + CFPropertyListRef pref = ::CFPreferencesCopyValue( CFSTR("com.apple.scrollwheel.scaling") , kCFPreferencesAnyApplication, @@ -327,6 +333,8 @@ COSXScreen::fakeMouseWheel(SInt32 delta) const CFRelease(pref); } + // note that we ignore the magnitude of the delta. i think this is to + // avoid local wheel acceleration. if (delta < 0) { wheelIncr = -wheelIncr; } @@ -799,15 +807,26 @@ COSXScreen::onKey(EventRef event) const { UInt32 eventKind = GetEventKind(event); + // get the key KeyButton button; GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(button), NULL, &button); - + LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, button)); + + // sadly, OS X doesn't report the button for modifier keys. button will + // be zero for modifier keys. since that's not good enough we'll have + // to figure out what the key was. + if (button == 0 && eventKind == kEventRawKeyModifiersChanged) { + // get old and new modifier state + KeyModifierMask oldMask = getActiveModifiers(); + KeyModifierMask newMask = mapMacModifiersToSynergy(event); + m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask); + return true; + } + bool down = (eventKind == kEventRawKeyDown); bool up = (eventKind == kEventRawKeyUp); bool isRepeat = (eventKind == kEventRawKeyRepeat); - - LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, button)); if (down) { m_keyState->setKeyDown(button, true); @@ -842,6 +861,44 @@ COSXScreen::mapMacButtonToSynergy(UInt16 macButton) const return kButtonNone; } +KeyModifierMask +COSXScreen::mapMacModifiersToSynergy(EventRef event) const +{ + // get native bit mask + UInt32 macMask; + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, + NULL, sizeof(macMask), NULL, &macMask); + + // convert + KeyModifierMask outMask = 0; + if ((macMask & shiftKey) != 0) { + outMask |= KeyModifierShift; + } + if ((macMask & rightShiftKey) != 0) { + outMask |= KeyModifierShift; + } + if ((macMask & controlKey) != 0) { + outMask |= KeyModifierControl; + } + if ((macMask & rightControlKey) != 0) { + outMask |= KeyModifierControl; + } + if ((macMask & cmdKey) != 0) { + outMask |= KeyModifierAlt; + } + if ((macMask & optionKey) != 0) { + outMask |= KeyModifierSuper; + } + if ((macMask & rightOptionKey) != 0) { + outMask |= KeyModifierSuper; + } + if ((macMask & alphaLock) != 0) { + outMask |= KeyModifierCapsLock; + } + + return outMask; +} + void COSXScreen::updateButtons() { diff --git a/lib/platform/COSXScreen.h b/lib/platform/COSXScreen.h index dc61af3e..292c1a40 100644 --- a/lib/platform/COSXScreen.h +++ b/lib/platform/COSXScreen.h @@ -91,6 +91,9 @@ private: // map mac mouse button to synergy buttons ButtonID mapMacButtonToSynergy(UInt16) const; + + // map mac modifier mask to synergy modifier mask + KeyModifierMask mapMacModifiersToSynergy(EventRef event) const; /// Resolution switch callback static pascal void displayManagerCallback(void* inUserData, From b66a4039419a54ae4805c93195844e6393bff763 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 31 Jul 2004 11:19:39 +0000 Subject: [PATCH 699/807] Now using instead of . Also added a bit to autoconf to ensure we don't use poll on OS X. --- acinclude.m4 | 7 +++++-- lib/arch/CArchNetworkBSD.cpp | 2 +- lib/platform/CXWindowsEventQueueBuffer.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index d368949b..12d80bda 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -130,8 +130,11 @@ AC_DEFUN([ACX_CHECK_GETPWUID_R], [ AC_DEFUN([ACX_CHECK_POLL], [ AC_MSG_CHECKING([for poll]) - AC_TRY_LINK([#include ], - [struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10);], + AC_TRY_LINK([#include ], + [#if defined(_POLL_EMUL_H_) + #error emulated poll + #endif + struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10);], acx_poll_ok=yes, acx_poll_ok=no) AC_MSG_RESULT($acx_poll_ok) if test x"$acx_poll_ok" = xyes; then diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index e2efc8da..2432c9e0 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -30,7 +30,7 @@ #include #if HAVE_POLL -# include +# include # if HAVE_ALLOCA_H # include # endif diff --git a/lib/platform/CXWindowsEventQueueBuffer.cpp b/lib/platform/CXWindowsEventQueueBuffer.cpp index f1ef5551..170e5bc6 100644 --- a/lib/platform/CXWindowsEventQueueBuffer.cpp +++ b/lib/platform/CXWindowsEventQueueBuffer.cpp @@ -18,7 +18,7 @@ #include "CEvent.h" #include "IEventQueue.h" #if HAVE_POLL -# include +# include #else # if HAVE_SYS_SELECT_H # include From 1623701fc9e2969562ba18649e4bd9deeb83c8ba Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 31 Jul 2004 14:34:02 +0000 Subject: [PATCH 700/807] Documentation changes. --- INSTALL | 120 ++++++++++++++++++++++++++++++++++++++------------------ PORTING | 11 ++---- README | 23 +++++++---- TODO | 8 +++- 4 files changed, 105 insertions(+), 57 deletions(-) diff --git a/INSTALL b/INSTALL index 5bf35641..0fb1a205 100644 --- a/INSTALL +++ b/INSTALL @@ -100,7 +100,7 @@ Running on Windows ------------------ Double click `synergy' on the server computer. The server is the -computer who's mouse and keyboard will be shared. This brings up a +computer that shares its mouse and keyboard. This brings up a dialog that lets you configure the server then test out the configuration or start the server. @@ -127,6 +127,12 @@ First configure the server. Click the `Server' radio button * Click `OK' * Click `Test' +Note that when you link screens together you must explictly link in both +directions. For instance, if you have computer A on the left of B then +you must indicate A is to the left of B *and* that B is to the right of +A. If you don't do both then you'll find you're unable to leave one of +the screens. + The server will start and you'll see a console window with log messages telling you about synergy's progress. If an error occurs you'll get one or more dialog boxes telling you what the errors are; read the errors @@ -251,6 +257,28 @@ to create the configuration file. the windows client to map meta to alt (using `meta = alt'). + * aliases + is a list of screen names just like in the `screens' + section except each screen is followed by a list of aliases, + one per line *not* followed by a colon. An alias is a + screen name and must be unique. During screen name lookup + each alias is equivalent to the screen name it aliases. So + a client can connect using its canonical screen name or any + of its aliases. + + Example: + + section: aliases + larry: + larry.stooges.com + curly: + shemp + end + + Screen `larry' is also known as `larry.stooges.com' and can + connect as either name. Screen `curly' is also known as + `shemp'. (Hey, it's just an example.) + * links is a list of screen names just like in the `screens' section except each screen is followed by a list of links, @@ -280,28 +308,6 @@ to create the configuration file. not have to be symmetrical; moving up from moe then down from curly lands the cursor on larry. - * aliases - is a list of screen names just like in the `screens' - section except each screen is followed by a list of aliases, - one per line *not* followed by a colon. An alias is a - screen name and must be unique. During screen name lookup - each alias is equivalent to the screen name it aliases. So - a client can connect using its canonical screen name or any - of its aliases. - - Example: - - section: aliases - larry: - larry.stooges.com - curly: - shemp - end - - Screen `larry' is also known as `larry.stooges.com' and can - connect as either name. Screen `curly' is also known as - `shemp'. (Hey, it's just an example.) - * options is a list of lines of the form `name = value'. These set the global options. @@ -485,30 +491,66 @@ the necessary (probably root) permission to modify the display manager configuration files. If you don't have that permission you can start synergy after logging in via the .xsession file. -To have the display manager start synergy, edit the Xsetup script. -The location of this file depends on your installation. It might -be /etc/X11/xdm/Xsetup. Near the end of the file but before -anyplace the script calls exit, start the client with something -like: +Typically, you need to edit three script files. The first file +will start synergy before a user logs in, the second will kill +that copy of synergy, and the third will start it again after +the user logs in. + +The contents of the scripts varies +greatly between systems so there's no one definite place where +you should insert your edits. However, these scripts often exit +before reaching the bottom so put the edits near the top of the +script. + +The location and names of these files depend on the operating +system and display manager you're using. A good guess for the +location is /etc/X11. Typical file names are: + + xdm gdm + --- --- + 1) xdm/Xsetup gdm/Init/Default (*) + 2) xdm/Xstartup gdm/PostLogin/Default (*) + 3) xdm/Xsession gdm/Sessions/Default (*, **) + +*) The Default' file is used if no other suitable file is found. +gdm will try (e.g. ':0', ':1') and (e.g. +'somehost'), in that order, before and instead of 'Default'. +**) gdm may use gdm/Xsession, xdm/Xsession or dm/Xsession if +gdm/Sessions/Default doesn't exist. + +For a synergy client, add the following to the first file: /usr/bin/killall synergyc - /usr/local/bin/synergyc [] + sleep 1 + /usr/bin/synergyc [] - must not include `-f' or `--no-daemon'. Change the -paths as necessary. It's important to make sure no old copies -of synergy are running so they can't interfere with the new one. +Of course, the path to synergyc depends on where you installed it +so adjust as necessary. -To start the server use something like: +Add to the second file: - /usr/bin/killall synergys - /usr/local/bin/synergys [] --config + /usr/bin/killall synergyc + sleep 1 -Again, must not include `-f' or `--no-daemon'. If -the configuration pathname is one of the default locations then -you don't need the `--config' option. +And to the third file: + + /usr/bin/killall synergyc + sleep 1 + /usr/bin/synergyc [] + +Note that must not include '-f' or '--no-daemon' or +the script will never exit and you won't be able to log in. + +The changes are the same for the synergy server except replace +'synergyc' with 'synergys' and use the appropriate synergys +command line options. Note that the first script is run as root +so synergys will look for the configuration file in root's home +directory then in /etc. Make sure it exists in one of those +places or use the '--config - VDEPTH = ./$(VPATH)/$(DEPTH) - is `..', `../..', `../../..', etc, - whichever references the top directory of the synergy tree. For - example, for a subdirectory of the top level use `..', for a - subdirectory of a subdirectory of the top level use `../..'. * Lists should have one item per line and end in $(NULL). For example: EXTRA_DIST = \ @@ -256,7 +250,8 @@ following these guidelines. $(NULL) Indentation must use tabs in a makefile. Line continuations (backslashes) should be aligned using tabs. - * Lists of files should be sorted alphabetically. Lists of + * Lists of files should be sorted alphabetically in groups (e..g + source files, header files, then other files). Lists of subdirectories must be in the desired build order. - Source Formatting diff --git a/README b/README index f67347a7..830543c9 100644 --- a/README +++ b/README @@ -52,7 +52,7 @@ System Requirements * Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family); -* Mac OS X 10.3 (10.2 may work); +* Mac OS X 10.2 or higher; * Unix: X Windows version 11 revision 4 or up with the XTEST extension @@ -87,17 +87,17 @@ via TCP/IP networking. Most systems come with this installed. The first step is to pick which keyboard and mouse you want to share. The computer with that keyboard and mouse is called the "primary screen" and it runs the synergy server. All of the other computers -are "secondary screens" and run the synergy client. The Windows NT -family, starting with NT 4 with service pack 3, and Unix are the best -choices. The Windows version provides a convenient GUI for -configuration. +are "secondary screens" and run the synergy client. Second, you install the software. Choose the appropriate package and install it. On Windows you should run SynergyInstaller. You must install the software on all the computers that will share the mouse and keyboard. -Third, you configure and start the server. +Third, you configure and start the server. The Windows version +provides a convenient GUI for configuration. Other platforms +require creating a simple text file. + Windows ------- Run `synergy' by double clicking on it. This brings up a dialog. @@ -122,6 +122,12 @@ Third, you configure and start the server. * Enter the server's screen name next to `Screen Name' * Click `OK' + Note that when you link screens together you must explictly link in both + directions. For instance, if you have computer A on the left of B then + you must indicate A is to the left of B *and* that B is to the right of + A. If you don't do both then you'll find you're unable to leave one of + the screens. + Now click `Test'. The server will start and you'll see a console window with log messages telling you about synergy's progress. If an error occurs you'll get a dialog box telling you synergy is about to quit; @@ -202,8 +208,9 @@ on Windows 95, 98, or Me then make sure the synergy log window is not the active window; just click on another window, like synergy's `Running Test...' window, if it is. Use the mouse and keyboard normally except use the edge of the screens to jump to other screens. -You can also cut-and-paste across computers. Currently, only text -transfers between computers. Start the remaining clients. +You can also cut-and-paste text, HTML, and images across computers +(HTML and images are not supported on OS X yet). Start the remaining +clients. Once the configuration is verified, see the instructions in INSTALL under `Starting Automatically on ...' for details on running synergy diff --git a/TODO b/TODO index bbb2a1aa..624fcb78 100644 --- a/TODO +++ b/TODO @@ -14,11 +14,11 @@ Things to do to synergy, in no particular order: There's a tray icon on win32 for checking synergy's current status and to quit synergy. It'd be nice to have something similar on - X11. + other platforms. * Port to other platforms - The MacOS X port is still incomplete but getting close. + The MacOS X port is usable but still incomplete. * Write man/html pages @@ -52,3 +52,7 @@ Then there are major new features: directing certain keystrokes to the same system, never sending certain keystrokes to some systems, and remapping keys on the server to other keys on the clients. + +* On-the-fly configuration + + There should be a way of reconfiguring synergy while it's running. From 4be61c381739376f7a9b43d931747d4ac6e626f8 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Aug 2004 16:00:18 +0000 Subject: [PATCH 701/807] Updated documentation. Converted most documation to HTML. --- AUTHORS | 7 - BUGS | 12 - FAQ | 199 ----------- INSTALL | 656 ------------------------------------ Makefile.am | 24 +- NEWS | 248 -------------- README | 259 +------------- TODO | 58 ---- configure.in | 1 + doc/Makefile.am | 40 +++ PORTING => doc/PORTING | 0 doc/authors.html | 30 ++ doc/autostart.html | 159 +++++++++ doc/compiling.html | 126 +++++++ doc/configuration.html | 275 +++++++++++++++ doc/developer.html | 84 +++++ doc/faq.html | 249 ++++++++++++++ HISTORY => doc/history.html | 18 +- doc/index.html | 96 ++++++ COPYING => doc/license.html | 174 ++++++---- doc/news.html | 335 ++++++++++++++++++ doc/running.html | 361 ++++++++++++++++++++ doc/security.html | 56 +++ doc/synergy.css | 133 ++++++++ doc/tips.html | 93 +++++ doc/todo.html | 70 ++++ 26 files changed, 2248 insertions(+), 1515 deletions(-) delete mode 100644 AUTHORS delete mode 100644 BUGS delete mode 100644 FAQ delete mode 100644 INSTALL delete mode 100644 NEWS delete mode 100644 TODO create mode 100644 doc/Makefile.am rename PORTING => doc/PORTING (100%) create mode 100644 doc/authors.html create mode 100644 doc/autostart.html create mode 100644 doc/compiling.html create mode 100644 doc/configuration.html create mode 100644 doc/developer.html create mode 100644 doc/faq.html rename HISTORY => doc/history.html (71%) create mode 100644 doc/index.html rename COPYING => doc/license.html (91%) create mode 100644 doc/news.html create mode 100644 doc/running.html create mode 100644 doc/security.html create mode 100644 doc/synergy.css create mode 100644 doc/tips.html create mode 100644 doc/todo.html diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index c2aa5065..00000000 --- a/AUTHORS +++ /dev/null @@ -1,7 +0,0 @@ -Synergy Authors -=============== - -Chris Schoeneman -- Creator, owner, X11, Win32 -Ryan Breen -- Initial Mac OS X port -Guido Poschta -- Windows installer -Bertrand Landry Hetu -- Mac OS X port diff --git a/BUGS b/BUGS deleted file mode 100644 index 7cbc4373..00000000 --- a/BUGS +++ /dev/null @@ -1,12 +0,0 @@ -Known Bugs in Synergy -===================== - -View known bugs at: -http://sourceforge.net/tracker/?group_id=59275&atid=490467 - -Report bugs at: -http://sourceforge.net/tracker/?group_id=59275&atid=490467 - -When reporting bugs, please include the version of the operating -system you're using (on the server and relevant clients) and what -locales you use. diff --git a/FAQ b/FAQ deleted file mode 100644 index c75f4194..00000000 --- a/FAQ +++ /dev/null @@ -1,199 +0,0 @@ -Synergy Frequently Asked Questions -================================== - -Questions ---------- -1. Why doesn't ctrl+alt+del work on secondary screens? -2. Can the server and client be using different operating systems? -3. What's the difference between synergy and x2x, x2vnc, etc? -4. What does "Cannot initialize hook library" mean? -5. What security/encryption does synergy provide? -6. What should I call my screens in the configuration? -7. Why do my CapsLock and NumLock keys act funny? -8. Can synergy share the display in addition to the mouse and keyboard? -9. Can synergy do drag and drop between computers? -10. Do AltGr or Mode-Switch or ISO_Level3_Shift work? -11. Why isn't synergy ported to platform XYZ? -12. My client can't connect. What's wrong? -13. Linking fails on Solaris. What's wrong? -14. The screen saver never starts. Why not? -15. I can't switch screens anymore for no apparent reason. Why? -16. I get the error 'Xlib: No protocol specified'. Why? -17. The cursor goes to secondary screen but won't come back. Why? -18. The cursor wraps from one edge of the screen to the opposite. Why? - -Answers -------- -1. Why doesn't ctrl+alt+del work on secondary screens? - - Synergy isn't able to capture ctrl+alt+del on PC compatible - systems because it's handled completely differently than - other keystrokes. However, when the mouse is on a client - screen, pressing ctrl+alt+pause will simulate ctrl+alt+del - on the client. (A client running on Windows NT, 2000, or XP - must be running as a service for this to work.) - -2. Can the server and client be using different operating systems? - - Yes. The synergy network protocol is platform neutral so - synergy doesn't care what operating systems are running on - the server and clients. - -3. What's the difference between synergy and x2x, x2vnc, etc? - - Unlike x2x, synergy supports any number of computers and - it doesn't require X on Microsoft Windows platforms. It - also has more advanced clipboard support and synchronizes - screensavers. x2vnc is also limited to two computers, - requires the separate vnc package, and is really only - appropriate for using an X system to control a non-X system. - However, the right tool for the job is whatever tool works - best for you. - -4. What does "Cannot initialize hook library" mean? - - This error can occur on a synergy server running on a - Microsoft Windows operating system. It means that synergy - is already running or possibly was not shut down properly. - If it's running then first end the synergy task. If it's - not then try logging off and back on or rebooting then - starting synergy again. - -5. What security/encryption does synergy provide? - - Synergy provides no built-in encryption or authentication. - Given that, synergy should not be used on or over any untrusted - network, especially the Internet. It's generally fine for home - networks. Future versions may provide built-in encryption and - authentication. - - Strong encryption and authentication is available through SSH - (secure shell). Run the SSH daemon (i.e. server) on the same - computer that you run the synergy server. It requires no - special configuration to support synergy. On each synergy - client system, run SSH with port forwarding: - - ssh -f -N -L 24800::24800 - - where is the name of the SSH/synergy server. - Once ssh authenticates itself, start the synergy client - normally except use `localhost' or `127.0.0.1' as the server's - address. SSH will then encrypt all communication on behalf of - synergy. Authentication is handled by the SSH authentication. - - A free implementation of SSH for Linux and many Unix systems - called OpenSSH is available from http://www.openssh.com/. For - Windows there's a port of OpenSSH using Cygwin - (http://www.cygwin.com/). - -6. What should I call my screens in the configuration? - - You can use any unique name in the configuration file for each - screen but it's easiest to use the hostname of the computer. - That's the computer name not including the domain. For example, - a computer with the fully qualified domain name `xyz.foo.com' has - the hostname `xyz'. There should also be an alias for `xyz' to - `xyz.foo.com'. If you don't use the computer's hostname, you - have to tell synergy the name of the screen using a command line - option, or the startup dialog on Windows. - -7. Why do my CapsLock and NumLock keys act funny? - - Some systems treat the Caps-Lock and Num-Lock keys differently - than all the others. Whereas most keys report going down when - physically pressed and going up when physically released, on - these systems the Caps-Lock and Num-Lock keys report going down - when being activated and going up when being deactivated. That - is, when you press and release, say, Caps-Lock to activate it, it - only reports going down, and when you press and release to - deactivate it, it only reports going up. This confuses synergy. - - You can solve the problem by changing your configuration file. - In the screens section, following each screen that has the - problem, add either or both of these lines as appropriate: - - halfDuplexCapsLock = true - halfDuplexNumLock = true - - Then restart synergy on the server. - -8. Can synergy share the display in addition to the mouse and keyboard? - - No. Synergy is a KM solution not a KVM (keyboard, video, mouse) - solution. However, future versions will probably support KVM. - Hopefully, this will make synergy suitable for managing large - numbers of headless servers. - -9. Can synergy do drag and drop between computers? - - No. That's a very cool idea and it'll be explored. However, it's - also clearly difficult and may take a long time to implement. - -10. Does AltGr/Mode-Switch/ISO_Level3_Shift work? - - Yes, as of 1.0.12 synergy has full support for AltGr/Mode-switch. - That includes support for most (all?) European keyboard layouts. - All systems should be using the same keyboard layout, though, for - all characters to work. (Any character missing from a client's - layout cannot be generated by synergy.) There is experimental - support for ISO_Level3_Shift in 1.1.3. - -11. Why isn't synergy ported to platform XYZ? - - Probably because the developers don't have access to platform XYZ - and/or are unfamiliar with development on XYZ. Also, synergy has - inherently non-portable aspects so there's a not insignificant - effort involved in porting. - -12. My client can't connect. What's wrong? - - A common mistake when starting the client is to give the wrong - server host name. The last synergyc command line option (Unix) - or the "Server Host Name" edit field (Windows) should be the - host name (or IP address) of the server *not* the client's host - name. If you get the error "connection failed: cannot connect - socket" followed by "the attempt to connect was forcefully - rejected" or "connection refused" then the server isn't started, - can't bind the address, or the client is connecting to the wrong - host name/address or port. - -13. Linking fails on Solaris. What's wrong? - - Did you add `--x-includes=/usr/openwin/include - --x-libraries=/usr/openwin/lib' (without the linebreak) to the - `configure' command line? Solaris puts the X11 includes and - libraries in an unusual place and the above lets synergy find - them. - -14. The screen saver never starts. Why not? - - If the synergy server is on X Windows then the screen saver will - not start while the mouse is on a client screen. This is a - consequence of how X Windows, synergy and xscreensaver work. - -15. I can't switch screens anymore for no apparent reason. Why? - - This should not happen with 1.1.3 and up. Earlier versions of - synergy would not allow switching screens when a key was down and - sometimes it would believe a key was down when it was not. - -16. I get the error 'Xlib: No protocol specified'. Why? - - You're running synergy without authorization to connect to the - X display. Typically the reason is running synergy as root when - logged in as non-root. Just run synergy as the same user that's - logged in. - -17. The cursor goes to secondary screen but won't come back. Why? - - Your configuration is incorrect. You must indicate the neighbors - of every screen. Just because you've configured 'Apple' to be to - the left of 'Orange' does not mean that 'Orange' is to the right - of 'Apple'. You must provide both in the configuration. - -18. The cursor wraps from one edge of the screen to the opposite. Why? - - Because you told it to. If you list 'Orange' to be to the left of - 'Orange' then moving the mouse off the left edge of 'Orange' will - make it jump to the right edge. Remove the offending line from the - configuration if you don't want that behavior. diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 0fb1a205..00000000 --- a/INSTALL +++ /dev/null @@ -1,656 +0,0 @@ -Synergy Installation Instructions -================================= - -Prerequisites for building --------------------------- - -To build synergy from the sources you'll need the following: - - Windows: - * VC++ 6.0 or up - - Unix: - * gcc 2.95 (or up) - * X11R4 or up headers and libraries - - Mac OS X: - * XCode; or gcc 2.95 (or up) and make - -In this document, "Unix" means any of the supported Unix or Unix-like -(e.g. Linux) operating systems. - - -Configuring the build ---------------------- - -This step is only necessary when building on Unix or Mac OS X if not -using XCode. - -To configure the build for your platform use the configure script: - - ./configure - -For a list of options to configure use: - - ./configure --help - -On Solaris you may need to use: - - ./configure --x-includes=/usr/openwin/include --x-libraries=/usr/openwin/lib - -so synergy can find the X11 includes and libraries. - - -Building --------- - -Windows: - Start VC++ and open `synergy.dsw'. Set the active configuration - (Build | Set Active Configuration) to `All - Debug' or `All - Release' - then build. Binaries are built into ./debug or ./build. - -Unix or Mac OS X without XCode: - Simply enter: - - make - - This will build the client and server and leave them in their - respective source directories. - -Mac OS X with XCode: - Start XCode and open the synery.xcode project. Build the 'all' - project using the Deployment flavor. - - -Installing ----------- - -Windows: - You'll need NSIS, the Nullsoft Scriptable Install System, available - from http://nsis.sourceforge.net/. Build `All - Release' then build - 'Installer - Release'. This creates SynergyInstaller.exe in the top - level directory. Run this to install. - - Alternatively, you can simply copy the following files from the Debug - or Release directory to a directory you choose (perhaps under the - Program Files directory): - - * synergy.exe - * synergyc.exe - * synergys.exe - * synrgyhk.dll - -Unix or Mac OS X without XCode: - make install - - will install the client and server into /usr/local/bin unless you - specified a different directory when you ran configure. - -Mac OS X with XCode: - Copy the following files from ./build to a convenient location: - - * synergyc - * synergys - -See `Starting Automatically on ...' below for details on how to have -synergy start up automatically when the computer starts. - - -Running on Windows ------------------- - -Double click `synergy' on the server computer. The server is the -computer that shares its mouse and keyboard. This brings up a -dialog that lets you configure the server then test out the -configuration or start the server. - -First configure the server. Click the `Server' radio button - - * Click the `Server' radio button - * Click `Add' to add the server to the `Screens' list - * Enter the name of server (the computer's name is recommended) - * Enter other names the server is known by - * Click OK - * Use `Add' to add your other computers - * Using a computer's name as its screen name is recommended - * Choose desired screen options on the `Add' dialog - * Use the controls under `Layout' to link screens together - * Click (once) on the server's name in the `Screens' list - * Choose the screen to the left of the server - * Use `---' if there is no screen to the left of the server - * Choose the screens to the right, above and below the server - * Repeat the above steps for all the other screens - * Use `Options...' to set desired options - * If the server's screen name is not the server's computer name: - * Click `Advanced...' - * Enter the server's screen name next to `Screen Name' - * Click `OK' - * Click `Test' - -Note that when you link screens together you must explictly link in both -directions. For instance, if you have computer A on the left of B then -you must indicate A is to the left of B *and* that B is to the right of -A. If you don't do both then you'll find you're unable to leave one of -the screens. - -The server will start and you'll see a console window with log messages -telling you about synergy's progress. If an error occurs you'll get one -or more dialog boxes telling you what the errors are; read the errors -to determine the problem then correct them and try `Test' again. - -Now that the server is running, you'll need to start a client. On any -client computer, double click `synergy'. Of course, you'll need to -have installed the four files listed under `Installing' above on the -client computer. Then configure the client: - - * Click the `Client' radio button - * Enter the server's computer name in `Server Host Name' - * Do not use any of the server's screen names, unless one of those - is also the computer name - * If the client's screen name is not the client's computer name: - * Click `Advanced...' - * Enter the client's screen name next to `Screen Name' - * Click `OK' - * Click `Test' - -If all goes well, the client connects to the server successfully and -the mouse and keyboard are shared. If an error occurs you'll get one -or more dialog boxes telling you what the errors are; read the errors -to determine the problem then correct them and try `Test' again. When -everything is working correctly, install the software on the other -client computers (if any) and repeat the steps for configuring the -client on each. - -Once the clients and server are working you can stop the clients and -server by clicking the `Stop' button on each computer or by right -clicking on the tray icon (by the clock in the task bar) and choosing -`Quit'. Then click `Start' on the server computer then on each of -the clients. Synergy will start and the dialog window will close. -You can stop synergy or check on its status using the tray icon. - -See `Starting Automatically on Windows' below for configuring synergy -to start automatically when the computer starts. - - -Configuring the Server on Unix or Mac OS X ------------------------------------------- - -The synergy server requires configuration. The configuration file is a -plain text file broken into sections. Each section has the form: - - section: - - end - -Comments are introduced by `#' and continue to the end of the line. -The file can have the following sections. The `screens' section must -appear before the `links' and `aliases' sections. Use any text editor -to create the configuration file. - - * screens - is a list of screen names, one name per line, each - followed by a colon. Names are arbitrary strings but they - must be unique. The hostname of each computer is recommended. - There must be a screen name for the server and each client. - Each screen can specify a number of options. Options have the - form `name = value' and a listed one per line after the screen - name. - - Example: - - section: screens - moe: - larry: - halfDuplexCapsLock = true - halfDuplexNumLock = true - curly: - meta = alt - end - - This declares three screens named: moe, larry, and curly. - Screen `larry' has half-duplex caps lock and num lock keys - (see below) and screen `curly' converts the meta modifier key - to the alt key. - - Screen can have the following options: - - halfDuplexCapsLock = {true|false} - This computer has a caps lock key that doesn't report a - press and a release event when the user presses it but - instead reports a press event when it's turned on and a - release event when it's turned off. If caps lock acts - strangely on all screens then you may need this option - on the server screen. If it acts strangely on one - screen then that screen may need the option. - - halfDuplexNumLock = {true|false} - This is identical to halfDuplexCapsLock except it - applies to the num lock key. - - xtestIsXineramaUnaware = {true|false} - This option works around a bug in the XTest extension - when used in combination with Xinerama. It affects - X11 clients only. Not all versions of the XTest - extension are aware of the Xinerama extension. As a - result, they do not move the mouse correctly when - using multiple Xinerama screens. This option is - currently true by default. If you know your XTest - extension is Xinerama aware then set this option to - false. - - shift = {shift|ctrl|alt|meta|super|none} - ctrl = {shift|ctrl|alt|meta|super|none} - alt = {shift|ctrl|alt|meta|super|none} - meta = {shift|ctrl|alt|meta|super|none} - super = {shift|ctrl|alt|meta|super|none} - Map a modifier key pressed on the server's keyboard to - a different modifier on this client. This option only - has an effect on a client screen; it's accepted and - ignored on the server screen. - - You can map, say, the shift key to shift (the default), - ctrl, alt, meta, super or nothing. Normally, you - wouldn't remap shift or ctrl. You might, however, have - an X11 server with meta bound to the Alt keys. To use - this server effectively with a windows client, which - doesn't use meta but uses alt extensively, you'll want - the windows client to map meta to alt (using `meta = - alt'). - - * aliases - is a list of screen names just like in the `screens' - section except each screen is followed by a list of aliases, - one per line *not* followed by a colon. An alias is a - screen name and must be unique. During screen name lookup - each alias is equivalent to the screen name it aliases. So - a client can connect using its canonical screen name or any - of its aliases. - - Example: - - section: aliases - larry: - larry.stooges.com - curly: - shemp - end - - Screen `larry' is also known as `larry.stooges.com' and can - connect as either name. Screen `curly' is also known as - `shemp'. (Hey, it's just an example.) - - * links - is a list of screen names just like in the `screens' - section except each screen is followed by a list of links, - one per line. Each link has the form ` = - '. A link indicates which screen is adjacent in the - given direction. - - - Example: - - section: links - moe: - right = larry - up = curly - larry: - left = moe - up = curly - curly: - down = larry - end - - This indicates that screen `larry' is to the right of screen - `moe' (so moving the cursor off the right edge of moe would - make it appear at the left edge of larry), `curly' is above - 'moe', `moe' is to the left of `larry', `curly' is above - `larry', and `larry' is below `curly'. Note that links do - not have to be symmetrical; moving up from moe then down - from curly lands the cursor on larry. - - * options - is a list of lines of the form `name = value'. These - set the global options. - - Example: - - section: options - heatbeat = 5000 - switchDelay = 500 - end - - You can use the following options: - - heartbeat = N - The server will expect each client to send a message no - less than every N milliseconds. If no message arrives - from a client within 3N seconds the server forces that - client to disconnect. - - If synergy fails to detect clients disconnecting while - the server is sleeping or vice versa, try using this - option. - - switchDelay = N - Synergy won't switch screens when the mouse reaches the - edge of a screen unless it stays on the edge for N - milliseconds. This helps prevent unintentional - switching when working near the edge of a screen. - - switchDoubleTap = N - Synergy won't switch screens when the mouse reaches the - edge of a screen unless it's moved away from the edge - and then back to the edge within N milliseconds. With - the option you have to quickly tap the edge twice to - switch. This helps prevent unintentional switching - when working near the edge of a screen. - - screenSaverSync = {true|false} - If set to false then synergy won't synchronize screen - savers. Client screen savers will start according to - their individual configurations. The server screen - saver won't start if there is input, even if that input - is directed toward a client screen. - - relativeMouseMoves = {true|false} - If set to true then secondary screens move the mouse - using relative rather than absolute mouse moves when - and only when Scroll Lock is toggled on (i.e. the cursor - is locked to the screen). This is intended to make - synergy work better with certain games. If set to - false or not set then all mouse moves are absolute. - - You can use both the switchDelay and switchDoubleTap options at - the same time. Synergy will switch when either requirement is - satisfied. - -The synergy server will try certain pathnames to load the configuration -file if the user doesn't specify a path using the `--config' command -line option. `synergys --help' reports those pathnames. - - -Running the Server on Unix or Mac OS X --------------------------------------- - -Run the server on the computer that has the keyboard and mouse to -be shared. You must have prepared a configuration file before -starting the server. The server should be started before the -clients but that's not required. - -Run the synergy server on the server system using the following -command line: - - synergys -f --config - -Replace with the path to the configuration file. -The `-f' option causes synergys to run in the foreground. This is -recommended until you've verified that the configuration works. -If you didn't include the system's hostname in the configuration -file (either as a screen name or an alias) then you'll have to add -`--name ' to the command line, where is -a name in the configuration file. You can use `synergys --help' -for a list of command line options. - -See `Starting Automatically on Unix' below for running synergy -automatically when the X server starts. - - -Running the Client on Unix or Mac OS X --------------------------------------- - -Run the client on all computers that aren't the server using the -following command line: - - synergyc -f --no-camp - -Replace with the hostname or address of the -server system. The `-f' option causes synergy to run in the -foreground. The `--no-camp' prevents synergy from retrying to -connect to the server until it succeeds. Both are recommended -until you've verified that the configuration works. If you -didn't include the system's hostname in the configuration file -(either as a screen name or an alias) then you'll have to add -`--name ' to the command line, where -is a name in the configuration file. - -The client should quickly report `connected to server'. If it -does not but doesn't print an error and exit immediately then -it's trying to connect to the server but cannot. It will time -out in 30 seconds and exit (use ctrl+c to exit earlier). You -should check that the server is running and is reachable over -the network and try again. - -If the client fails and exits it should print an error describing -the problem. Here are typical problems and possible solutions: - - * failed to open screen: - check permission to open the X display; - check that the DISPLAY environment variable is set. - * already connected: - check that the synergy client isn't already running. - * refused client: - add client to the server's configuration file. - * connection failed: - check ; - the server cannot open the desired port, stop the - program using that port (24800) and restart the - server. - -If you get the error "Xlib: No protocol specified" you're probably -running synergy as root while logged in as another user. X11 may -prevent this for security reasons. Either run synergy as the same -user that's logged in or (not recommended) use 'xhost +' to allow -anyone to connect to the display. - -Once all the clients are running, try moving the mouse to each -screen. Be sure to check all the configured links. - -See `Starting Automatically on Unix' below for running synergy -automatically when the X server starts. - - -Starting Automatically on Windows ---------------------------------- - -When all the clients work you're ready to have synergy start -automatically each time the system (re)starts. Click `Stop' on all -the clients then on the server'. Now click the `Configure...' button -by the text `Automatic Startup'. The `Auto Start' dialog will pop up. -If an error occurs then correct the problem and click `Configure' -again. - -On the `Auto Start' dialog you'll configure synergy to start -automatically when the computer starts or when you log in. You can -also configure synergy to not start automatically. You can only -start synergy automatically when the computer starts if you have -sufficient access rights. The dialog will let you know if you have -sufficient permission. - -If synergy is already configured to automatically start then there -will be two `Uninstall' buttons, at most one of which is enabled. -Click the enabled button, if any, to configure synergy to not start -automatically. - -If synergy is not configured to start automatically then there will -be two `Install' buttons. If you have sufficient permission to -have synergy start automatically when the computer does then the -`Install' button in the `When Computer Starts' box will be enabled. -Click it to have synergy start for all users when the computer starts. -In this case, synergy will be available during the login screen. -Otherwise, click the `Install' button in the `When You Log In' box -to have synergy automatically start when you log in. - - -Starting Automatically on Unix ------------------------------- - -Synergy requires an X server. That means a server must be -running and synergy must be authorized to connect to that server. -It's best to have the display manager start synergy. You'll need -the necessary (probably root) permission to modify the display -manager configuration files. If you don't have that permission -you can start synergy after logging in via the .xsession file. - -Typically, you need to edit three script files. The first file -will start synergy before a user logs in, the second will kill -that copy of synergy, and the third will start it again after -the user logs in. - -The contents of the scripts varies -greatly between systems so there's no one definite place where -you should insert your edits. However, these scripts often exit -before reaching the bottom so put the edits near the top of the -script. - -The location and names of these files depend on the operating -system and display manager you're using. A good guess for the -location is /etc/X11. Typical file names are: - - xdm gdm - --- --- - 1) xdm/Xsetup gdm/Init/Default (*) - 2) xdm/Xstartup gdm/PostLogin/Default (*) - 3) xdm/Xsession gdm/Sessions/Default (*, **) - -*) The Default' file is used if no other suitable file is found. -gdm will try (e.g. ':0', ':1') and (e.g. -'somehost'), in that order, before and instead of 'Default'. -**) gdm may use gdm/Xsession, xdm/Xsession or dm/Xsession if -gdm/Sessions/Default doesn't exist. - -For a synergy client, add the following to the first file: - - /usr/bin/killall synergyc - sleep 1 - /usr/bin/synergyc [] - -Of course, the path to synergyc depends on where you installed it -so adjust as necessary. - -Add to the second file: - - /usr/bin/killall synergyc - sleep 1 - -And to the third file: - - /usr/bin/killall synergyc - sleep 1 - /usr/bin/synergyc [] - -Note that must not include '-f' or '--no-daemon' or -the script will never exit and you won't be able to log in. - -The changes are the same for the synergy server except replace -'synergyc' with 'synergys' and use the appropriate synergys -command line options. Note that the first script is run as root -so synergys will look for the configuration file in root's home -directory then in /etc. Make sure it exists in one of those -places or use the '--config :24800 - -where is the name or address of the SSH and -synergy server host. 24800 is the default synergy port; replace -it with whichever port you use if you don't use the default. Once -ssh authenticates with the server, start the synergy client as -usual except use `localhost' or `127.0.0.1' for the server -address. Synergy will then pass all communication through SSH -which encrypts it, passes it over the network, decrypts it, and -hands it back to synergy. Authentication is provided by SSH's -authentication. - - - -Common Command Line Options ---------------------------- - -d, --debug use debugging level - --daemon run as a daemon (Unix) or background (Windows) - -f, --no-daemon run in the foreground - -n, --name use instead of the hostname - --restart automatically restart on failures - -1, --no-restart do not restart on failure - -h, --help print help and exit - --version print version information and exit - -Debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, -INFO, DEBUG, DEBUG1, and DEBUG2. Only messages at or above the given -level are logged. Messages are logged to a terminal window when -running in the foreground. Unix logs messages to syslog when running -as a daemon. The Windows NT family logs messages to the event log -when running as a service. The Windows 95 family shows FATAL log -messages in a message box and others in a terminal window when running -as a service. - -The `--name' option lets the client or server use a name other than -its hostname for its screen. This name is used when checking the -configuration. - -Neither the client nor server will automatically restart if an error -occurs that is sure to happen every time. For example, the server -will exit immediately if it can't find itself in the configuration. -On X11 both the client and server will also terminate if the -connection to the X server is lost. Since xdm will normally restart -the X server and synergy, this is the correct behavior. - - -Server Command Line Options ---------------------------- - -a, --address
listen for connections on the given address - -c, --config read configuration from - -
has one of the following forms: - - : - : - is a hostname or address of a network interface on the -server system. is a port number from 1 to 65535. -defaults to the system's hostname and defaults to 24800. - - -Client Command Line Options ---------------------------- - --camp retry connection to server until successful - --no-camp try connection to server only once -
address of server - -see the "server command line options" for a description of
-but note that there is no default though there is a -default . diff --git a/Makefile.am b/Makefile.am index dff8861f..7b922468 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,19 +16,14 @@ NULL = SUBDIRS = \ lib \ cmd \ + doc \ dist \ $(NULL) EXTRA_DIST = \ - BUGS \ - FAQ \ - HISTORY \ - PORTING \ - TODO \ all.dsp \ synergy.dsw \ synergy.xcode/project.pbxproj \ - doc/doxygen.cfg.in \ examples/synergy.conf \ $(NULL) @@ -42,25 +37,20 @@ MAINTAINERCLEANFILES = \ configure \ stamp-h.in \ stamp-h1 \ - doc/doxygen.cfg \ - doc/doxygen/html/* \ $(NULL) PKG_FILES = \ - AUTHORS \ - BUGS \ - COPYING \ ChangeLog \ - FAQ \ - HISTORY \ - INSTALL \ - NEWS \ README \ - TODO \ cmd/synergyc/synergyc \ cmd/synergys/synergys \ examples/synergy.conf \ $(NULL) +PKG_DOC_FILES = \ + doc/PORTING \ + doc/*.html \ + doc/*.css \ + $(NULL) PKG_PROG_FILES = \ synergyc \ synergys \ @@ -95,7 +85,9 @@ dist-pkg: all rm -rf $(PKGTOPDIR) mkdir $(PKGTOPDIR) mkdir $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + mkdir $(PKGTOPDIR)/@PACKAGE@-@VERSION@/doc cp $(PKG_FILES) $(PKGTOPDIR)/@PACKAGE@-@VERSION@ + cp $(PKG_DOC_FILES) $(PKGTOPDIR)/@PACKAGE@-@VERSION@/doc (cd $(PKGTOPDIR)/@PACKAGE@-@VERSION@; \ chmod 644 *; \ chmod 755 $(PKG_PROG_FILES); \ diff --git a/NEWS b/NEWS deleted file mode 100644 index 4cf80b42..00000000 --- a/NEWS +++ /dev/null @@ -1,248 +0,0 @@ -Synergy News -============ - -* Jun-13-2004 - Synergy 1.1.7 released - - Made following changes: - * Added OS X precompiled header file forgotten in last build - * Fixed bug in fix for 'unexpected async reply' on X11 - * Removed dependency on "browser" service on win32 - * Fixed assertion failure when connection fails immediately - * Fixed failure to connect on AIX - * Fixed error in conversion from multibyte to wide characters - * Maybe fixed win32 screen saver detection - -* May-26-2004 - Synergy 1.1.6 released - - Made following changes: - * Added preliminary Mac OS X support (client and server) - * Fixed ctrl+alt+del emulation on win32 - * Fixed ctrl+alt+del on win32 server acting on both client and server - * Fixed handling of screen resolution changes on win32 - * Fixed 'unexpected async reply' on X11 - * Added dependency to win32 service to avoid startup race condition - * Fixed reference count bug - * Keyboard input focus now restored on X11 (fixes loss of input in some games) - - The OS X port does not yet support: - * HTML and bitmap clipboard data - * Screen saver synchronization - * Non-US English keyboards - -* May-05-2004 - Synergy 1.1.5 released - - Made following changes: - * No longer switching screens when a mouse button is down - * Worked around win32 mouse hook bug, fixing switch on double tap - * Added support for HTML and bitmap (image/bmp) clipboard data - * Physical mouse no longer necessary on win32 secondary screens to see cursor - * Added experimental relative mouse moves on secondary screen option - * Fixed win32 lock up when closing server with clients still connected - * Fixed bug in handling duplicate connections - * Fixed pthread mutex initialization - * Made synergy dependent on NetBT on win32 (for service startup order) - * Automake fixes; now mostly works on darwin and MinGW - * Fixed builds on Solaris 9, FreeBSD, and OpenBSD - * Partial support for MSYS/MinGW builds (NOT COMPLETE) - * Partial merge of OS X port (NOT COMPLETE) - -* Mar-31-2004 - Synergy 1.1.4 released - - Made following changes: - * Fixed lookup of hosts by name of win32 - * Reverted tray icon code to 1.0.15 version; seems to fix the bugs - * Fixed crash when caps, num, or scroll lock not in key map on X11 - * Fixed double tap and wait to switch features - -* Mar-28-2004 - Synergy 1.1.3 released - - Made following changes: - * Major code refactoring; reduced use of threads, added event queue - * Removed unused HTTP support code - * No longer interfering with mouse when scroll lock is toggled on - * Fixed minor mispositioning of mouse on win32 - * Unix portability fixes - * Added support for power management - * Improved keyboard handling and bug fixes - * Fixed dead key handling - - Note: the tray icon on windows is known to not work correctly when - running the synergy server on Windows 95/95/Me. - -* Aug-24-2003 - Synergy 1.0.14 released - - Made following changes: - * Fixed bugs in setting win32 process/thread priority - * Fixed resource leak in opening win32 system log - * Fixed win32 launcher not getting non-default advanced options - * Synergy log copied to clipboard now transferred to other screens - * Hack to work around lesstif clipboard removed (fixes pasting on X) - -* Jul-20-2003 - Synergy 1.0.12 released - - This release finally completes support for non-ASCII characters, - fully supporting most (all?) European keyboard layouts including - dead key composition. This release includes changes from several - experimental versions (1.0.9, 1.0.11, 1.1.0, 1.1.1, 1.1.2, and - 1.1.3). - - Made following changes: - * Added non-ASCII support to win32 and X11 - * Added dead key support to win32 and X11 - * Fixed AltGr handling - * Added ctrl+alt+del simulation using ctrl+alt+pause - * Fixed loss of key event when user releases ctrl+alt+del - * Fixed incorrect synthesis of pointer-keys event on X11 - * Fixed Xinerama support - * Made some clipboard fixes on win32 and X11 - * Add tray icon menu item to copy log to clipboard - * Fixed mouse warping on unconnected client - * Stopped unconnected client from filling up event logs - -* May-10-2003 - Synergy 1.0.8 released - - Made following changes: - * Fixed hook forwarding (fixing interaction with objectbar) - * Fixed "Windows" key handling and added support Win+E, Win+F, etc - * Added win 95/98/me support for Alt+Tab, Alt+Esc, Ctrl+Esc - * Fixed scroll lock locking to server screen - * Fixed screen flashing on X11 and Windows - * Fixed compile problem on 64 bit systems - * Fixed Xinerama support - * Now allowing screen names that include underscores - * Improved non-ASCII key handling on Windows - * Fixed lagginess - * Fixed failure to capture all mouse input on Windows - * Fixed auto-repeat bugs on X11 - * Added option to disable screen saver synchronization - * Added support for 4th and 5th mouse buttons on Windows - * Added support for "Internet" and "Multimedia" keys - * Fixed jumping from client to itself (mouse wrapping) - -* Mar-27-2003 - Synergy 1.0.6 released - - Made following changes: - * Added tray icon on win32 - * Fixed multi-monitor support on win32 - * Fixed win32 screen saver detection on NT/2k/XP - * Added per-screen options to remap modifier keys - * Added global options for restricting screen jumping - * Added global option for detecting unresponsive clients - * Added more logging for why screen jump won't happen - * Fixed problem sending the CLIPBOARD to motif/lesstif apps - * Win32 launcher now remembers non-config-file state - -* Feb-18-2003 - Synergy 1.0.3 released - - Made following changes: - * Support for X11 keymaps with only uppercase letters - * Fixed memory leaks - * Added documentation on using synergy with SSH - * Fixed unnecessary left-handed mouse button swapping - * Fixed debug build error on win32 - * Reduced frequency of large cursor jumps when leaving win32 server - * Changed cursor motion on win32 multimon to relative moves only - - -* Jan-25-2003 - Synergy 1.0.2 released - - Made following changes: - * Fixed out-of-bounds array lookup in the BSD and Windows network code - * Added ability to set screen options from Windows launch dialog - - -* Jan-22-2003 - Synergy 1.0.1 released - - Made following changes: - * Fixed running as a service on Windows NT family - - -* Jan-20-2003 - Synergy 1.0.0 released - - Made following changes: - * Refactored to centralize platform dependent code - * Added support for mouse wheel on Windows NT (SP3 and up) - * Portability improvements - * Added more documentation - * Fixes for working with xscreensaver - * Fixes for circular screen links - - This release has been tested on Linux and Windows. It builds and - is believed to run on Solaris and FreeBSD. It is believed to - build and run on Irix and AIX. It builds but does not work on - MacOS X. - - -* Dec-25-2002 - Synergy 0.9.14 released - - Made following changes: - * Fixed solaris compile problems (untested) - * Fixed irix compile problems (untested) - * Fixed windows client not reconnecting when server dies bug - * Fixed loss of ctrl+alt from windows server to non-windows clients - * Fixed handling of password protected windows client screen saver - * Now handling any number of pointer buttons on X11 - * Toggle key states now restored when leaving clients - * Added support for per-screen config options - * Added config options for half-duplex toggle keys on X11 - * Enabled class diagrams in doxygen documentation - - -* Nov-05-2002 - Synergy 0.9.13 released - - Made following changes: - * Fixed solaris compile problems (untested) - * Fixed MacOS X compile problems (semi-functional) - * Fixed gcc-3.2 compile problems - * Fixed some thread startup and shutdown bugs - * Server now quits if bind() fails with an error other than in use - * Fixed bug in moving mouse on Win98 without multiple monitors - * Fixed bug in handling TCP socket errors on read and write - * Fixed spurious screen saver activation on X11 - * Unix platforms can now read Win32 configuration files - * Minor error reporting fixes - - -* Sep-14-2002 - Synergy 0.9.12 released - - Made following changes: - * Win32 was not reporting log messages properly when run from synergy.exe - * Network error messages weren't reporting useful information - * Synergy won't build on gcc 3.2; added workaround for known problem - * X11 wasn't handling some keys/key combinations correctly - * Added option to change logging level when testing from synergy.exe - - -* Sep-04-2002 - Synergy 0.9.11 released - - Fixed following bugs: - * Worked around missing SendInput() on windows 95/NT 4 prior to SP3 - * Fixed keyboard mapping on X11 synergy client - - -* Sep-02-2002 - Synergy 0.9.10 released - - Fixed following bugs: - * The Pause/Break and KP_Enter buttons were not working correctly on windows - * Configuration options were being lost on windows after a reboot - * Added support for AltGr/ModeSwitch keys - * Added support for auto-start on windows when not administrator - * Improved autoconf - * Added workaround for lack of sstream header on g++ 2.95. - - -* Aug-18-2002 - Synergy 0.9.9 released - - Fixed three bugs: - * The PrintScrn button was not working correctly on windows - * The Win32 server could hang when a client disconnected - * Using the mouse wheel could hang the X server - - -* Aug-11-2002 - Synergy 0.9.8 released - - Supports any number of clients under Linux or Windows 95 or NT4 - or later. Includes mouse and keyboard sharing, clipboard - synchronization and screen saver synchronization. Supports ASCII - keystrokes, 5 button mouse with wheel, and Unicode text clipboard - format. diff --git a/README b/README index 830543c9..aa6d7ea3 100644 --- a/README +++ b/README @@ -18,261 +18,4 @@ a password to unlock them all. Synergy is open source and released under the GNU Public License (GPL). -The synergy home page is: -http://synergy2.sourceforge.net/ - -The synergy project page is: -http://sourceforge.net/projects/synergy2/ - -Report bugs to: -http://sourceforge.net/tracker/?func=add&group_id=59275&atid=490467 - - -Please see the following files for more information: -AUTHORS -- The list of synergy's authors -BUGS -- A list of known bugs and limitations -COPYING -- The license synergy is release under -FAQ -- Frequently asked questions about synergy -HISTORY -- A brief history of synergy -INSTALL -- Detailed build and installation instructions -NEWS -- News about the synergy project -PORTING -- Porting guide for developers -TODO -- List of things to add to synergy - - -System Requirements -------------------- - -* All operating systems: - keyboard, - mouse, - TCP/IP networking; - -* Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family); - -* Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family); - -* Mac OS X 10.2 or higher; - -* Unix: - X Windows version 11 revision 4 or up with the XTEST extension - (use `xdpyinfo | grep XTEST' to check for XTEST). - -In this document, "Unix" means any of the following: Linux, Solaris, -Irix. Synergy may compile and run on other Unix variants, too. Patches -for other platforms are welcome (including patches that package binaries); -See the contact information available off of the synergy home page or use -the patch page on sourceforge. - -The Mac OS X port is incomplete. It does not synchronize the screen saver, -only text clipboard data works (i.e. HTML and bitmap data do not work), -non-US English keyboards are untested and probably don't work, and there -may be problems with mouse pointer and mouse wheel acceleration. Other -problems should be filed as bugs (see BUGS). - - -Installation ------------- - -See INSTALL for detailed build and installation instructions and for -more information on configuring synergy. - - -Quick Start ------------ -Synergy lets you use one keyboard and mouse across multiple computers. -To do so it requires that all the computers are connected to each other -via TCP/IP networking. Most systems come with this installed. - -The first step is to pick which keyboard and mouse you want to share. -The computer with that keyboard and mouse is called the "primary -screen" and it runs the synergy server. All of the other computers -are "secondary screens" and run the synergy client. - -Second, you install the software. Choose the appropriate package -and install it. On Windows you should run SynergyInstaller. You -must install the software on all the computers that will share the -mouse and keyboard. - -Third, you configure and start the server. The Windows version -provides a convenient GUI for configuration. Other platforms -require creating a simple text file. - - Windows - ------- - Run `synergy' by double clicking on it. This brings up a dialog. - Configure the server: - - * Click the `Server' radio button - * Click `Add' to add the server to the `Screens' list - * Enter the name of server (the computer's name is recommended) - * Enter other names the server is known by - * Click OK - * Use `Add' to add your other computers - * Using a computer's name as its screen name is recommended - * Use the controls under `Layout' to link screens together - * Click (once) on the server's name in the `Screens' list - * Choose the screen to the left of the server - * Use `---' if there is no screen to the left of the server - * Choose the screens to the right, above and below the server - * Repeat the above steps for all the other screens - * Use `Options...' to set desired options. - * If the server's screen name is not the server's computer name: - * Click `Advanced...' - * Enter the server's screen name next to `Screen Name' - * Click `OK' - - Note that when you link screens together you must explictly link in both - directions. For instance, if you have computer A on the left of B then - you must indicate A is to the left of B *and* that B is to the right of - A. If you don't do both then you'll find you're unable to leave one of - the screens. - - Now click `Test'. The server will start and you'll see a console window - with log messages telling you about synergy's progress. If an error - occurs you'll get a dialog box telling you synergy is about to quit; - read the log messages to determine the problem then correct it and try - `Test' again. - - Unix or MacOS X - --------------- - Create a text file named synergy.conf with the following: - - section: screens - : - : - end - section: links - : - right = - : - left = - end - - Replace each occurrence of `' with the host name of the - primary screen computer and `' with the host name of a - secondary screen computer. In the above example, is to - the right of and is to the left of . - If necessary you should replace `right' and `left' with `left', - `right', 'up', or `down'. If you have more than two computers - you can add those too: add each computer's host name in the - `screens' section and add the appropriate links. - - Now start the server. Normally synergy wants to run "in the - background." It detaches from the terminal and doesn't have a - visible window, effectively disappearing from view. Until you're - sure your configuration works you should start synergy "in the - foreground" using the `-f' command line option: - - synergys -f --config synergy.conf - - Check the reported messages for errors. Use ctrl+c to stop synergy, - correct any problems, and start it again. - -Finally, start the clients. - Windows - ------- - Run `synergy' on the client by double clicking on it. Configure the - client: - - * Click the `Client' radio button - * Enter the server's computer name in `Server Host Name' - * Do not use any of the server's screen names, unless one of those - is also the computer name - * If the client's screen name is not the client's computer name: - * Click `Advanced...' - * Enter the client's screen name next to `Screen Name' - * Click `OK' - * Click `Test' - - If an error occurs you'll get a dialog box telling you synergy is - about to quit; read the log messages to determine the problem then - correct it and try `Test' again. - - Unix or MacOS X - --------------- - - To start a client, enter the following: - - synergyc -f - - where `' is replaced by the name of the computer - running the synergy server. If an error is reported use ctrl+c to - stop synergy, fix the error, and try again. - -Both the client and server should immediately report the connection -or an error. If successful, you should now be able to move the -mouse off the appropriate edge of your server's screen and have it -appear on the client's screen. If you're running the synery server -on Windows 95, 98, or Me then make sure the synergy log window is -not the active window; just click on another window, like synergy's -`Running Test...' window, if it is. Use the mouse and keyboard -normally except use the edge of the screens to jump to other screens. -You can also cut-and-paste text, HTML, and images across computers -(HTML and images are not supported on OS X yet). Start the remaining -clients. - -Once the configuration is verified, see the instructions in INSTALL -under `Starting Automatically on ...' for details on running synergy -in the background and on starting synergy automatically when you start -your computers. - - -Tips and Tricks ---------------- -* Be aware that not all keystrokes can be handled by synergy. In - particular, ctrl+alt+del is not handled. However, synergy can - convert ctrl+alt+pause into ctrl+alt+del on the client side. - (Synergy must be installed as a service on the client for this to - work on the Windows NT family.) Some non-standard keys may not - work, especially "multimedia" buttons, though several are - correctly handled. - -* A screen can be its own neighbor. That allows a screen to "wrap". - For example, if a configuration linked the left and right sides of - a screen to itself then moving off the left of the screen would put - the mouse at the right of the screen and vice versa. - -* You cannot switch screens when the scroll lock it toggled on. Use - this to prevent unintentional switching. - -* Turn off mouse driven virtual desktop switching on X windows. It - will interfere with synergy. Use keyboard shortcuts instead. - -* Synergy's screen saver synchronization works best with xscreensaver - under X windows. Synergy works better with xscreensaver if it is - using one of the screen saver extensions. Prior to xscreensaver 4.0 - you can use `-mit-extension', `-sgi-extension', or `-xidle-extension' - command line options to enable an extension (assuming your server has - the extension). Starting with 4.0 you must enable the corresponding - option in your .xscreensaver file. - -* Synergy automatically converts newlines in clipboard text (Unix - expects \n to end each line while Windows expects \r\n). - -* Clients can be started and stopped at any time. When a screen is - not connected, the mouse will jump over that screen as if the mouse - had moved all the way across it and jumped to the next screen. - -* A client's keyboard and mouse are fully functional while synergy is - running. You can use them in case synergy locks up. - -* Strong authentication and encryption is available by using SSH. See - the INSTALL file for more information. Synergy does not otherwise - provide secure communications and it should not be used on or over - untrusted networks. - -* Synergy doesn't work if a 16-bit Windows application has the focus - on Windows 95/98/Me. This is due to limitations of Windows. One - commonly used 16-bit application is the command prompt (command.exe) - and this includes synergy's log window when running in test mode. - - -Bug Reports ------------ - -Synergy is being improved all the time but we can only fix problems -that we know about. Please let us know of any problems you encounter, -including confusing or unhelpful documentation. File reports at: - - http://sourceforge.net/tracker/?func=add&group_id=59275&atid=490467 +Please see doc/index.html for more information. diff --git a/TODO b/TODO deleted file mode 100644 index 624fcb78..00000000 --- a/TODO +++ /dev/null @@ -1,58 +0,0 @@ -Synergy To Do List -================== - -Things to do to synergy, in no particular order: - -* Provide GUI configuration - - There's a GUI tool on win32 but no other platforms. Also, it'd be - nice if the tool allowed users to drag screen icons around to set - the links between them (though this assumes links are symmetrical - and synergy supports asymmetrical links). - -* Provide taskbar feedback - - There's a tray icon on win32 for checking synergy's current status - and to quit synergy. It'd be nice to have something similar on - other platforms. - -* Port to other platforms - - The MacOS X port is usable but still incomplete. - -* Write man/html pages - -* Add more clipboard formats - - Synergy currently supports text, html and BMP bitmaps on the clipboard. - It should support more formats, such as other image types and sound. - For each format, some canonical type must be chosen. For text, that's - UTF-8 with \n for newlines. Whatever it is it should losslessly support - any type it might be converted to. The type is converted to each - platform's native type. For example, BMP for images on win32. - -Then there are major new features: - -* Provide a KVM mode - - In this mode synergy would share the monitor in addition to the - keyboard and mouse. - -* Support for limited drag and drop between systems - -* Support for (virtual) terminals on unix - - This would be useful in KVM mode to administer several remote - headless systems that you don't want running X just so synergy - can work. - -* Configurable keys - - This includes shortcuts to jump to different screens, always - directing certain keystrokes to the same system, never sending - certain keystrokes to some systems, and remapping keys on the - server to other keys on the clients. - -* On-the-fly configuration - - There should be a way of reconfiguring synergy while it's running. diff --git a/configure.in b/configure.in index a7fc4b83..20165442 100644 --- a/configure.in +++ b/configure.in @@ -186,6 +186,7 @@ dist/Makefile dist/nullsoft/Makefile dist/rpm/Makefile dist/rpm/synergy.spec +doc/Makefile doc/doxygen.cfg lib/Makefile lib/arch/Makefile diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..45494563 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,40 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2002 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +## Process this file with automake to produce Makefile.in +NULL = + +EXTRA_DIST = \ + PORTING \ + doxygen.cfg.in \ + synergy.css \ + authors.html \ + autostart.html \ + compiling.html \ + configuration.html \ + developer.html \ + faq.html \ + history.html \ + index.html \ + license.html \ + news.html \ + running.html \ + security.html \ + tips.html \ + todo.html \ + $(NULL) + +MAINTAINERCLEANFILES = \ + Makefile.in \ + doc/doxygen.cfg \ + doc/doxygen/html/* \ + $(NULL) diff --git a/PORTING b/doc/PORTING similarity index 100% rename from PORTING rename to doc/PORTING diff --git a/doc/authors.html b/doc/authors.html new file mode 100644 index 00000000..18c420b7 --- /dev/null +++ b/doc/authors.html @@ -0,0 +1,30 @@ + + + + + Synergy Authors + + +

Synergy Authors

+

+ + + + + + + + + + + + + + + + + +
Chris SchoenemanCreator, owner, primary developer
Ryan BreenInitial Mac OS X port
Guido PoschtaWindows installer
Bertrand Landry HetuMac OS X port
+

+ + diff --git a/doc/autostart.html b/doc/autostart.html new file mode 100644 index 00000000..9d0d7555 --- /dev/null +++ b/doc/autostart.html @@ -0,0 +1,159 @@ + + + + + Synergy Autostart Guide + + +

Starting synergy automatically

+

+You can configure synergy to start automatically when the computer +starts or when you log in. The steps to do that are different on +each platform. Note that changing these configurations doesn't +actually start or stop synergy. The changes take effect the next +time you start your computer or log in. +

+ +

Windows

+

+Start synergy and click the Configure... button +by the text Automatic Startup. The +Auto Start dialog will pop up. +If an error occurs then correct the problem and click +Configure again. +

+

+On the Auto Start dialog you'll configure +synergy to start or not start automatically when the computer starts +or when you log in. You need Administrator access rights to start +synergy automatically when the computer starts. The dialog will let +you know if you have sufficient permission. +

+

+If synergy is already configured to automatically start then there +will be two Uninstall buttons, at most one +of which is enabled. Click the enabled button, if any, to tell +synergy to not start automatically. +

+

+If synergy is not configured to start automatically then there will +be two Install buttons. If you have +sufficient permission to have synergy start automatically when the +computer does then the Install button in the +When Computer Starts box will be enabled. +Click it to have synergy start for all users when the computer starts. +In this case, synergy will be available during the login screen. +Otherwise, click the Install button in the +When You Log In box to have synergy +automatically start when you log in. +

+ +

Unix

+

+Synergy requires an X server. That means a server must be +running and synergy must be authorized to connect to that server. +It's best to have the display manager start synergy. You'll need +the necessary (probably root) permission to modify the display +manager configuration files. If you don't have that permission +you can start synergy after logging in via the +.xsession file. +

+

+Typically, you need to edit three script files. The first file +will start synergy before a user logs in, the second will kill +that copy of synergy, and the third will start it again after +the user logs in. +

+

+The contents of the scripts varies greatly between systems so +there's no one definite place where you should insert your edits. +However, these scripts often exit before reaching the bottom so +put the edits near the top of the script. +

+

+The location and names of these files depend on the operating +system and display manager you're using. A good guess for the +location is /etc/X11. Typical file names +are: +

+

+ + + + + +
      xdm    gdm
1 xdm/Xsetup gdm/Init/Default (*)
2 xdm/Xstartup gdm/PostLogin/Default (*)
3 xdm/Xsession gdm/Sessions/Default (*, **)
+

+

+*) The Default file is used if no other +suitable file is found. gdm will try +displayname (e.g. :0) +and hostname (e.g. somehost), +in that order, before and instead of Default. +
+**) gdm may use gdm/Xsession, +xdm/Xsession or +dm/Xsession if +gdm/Sessions/Default doesn't exist. +

+

+For a synergy client, add the following to the first file: +

+  /usr/bin/killall synergyc
+  sleep 1
+  /usr/bin/synergyc [<options>] synergy-server-hostname
+
+Of course, the path to synergyc depends on where you installed it +so adjust as necessary. +

+

+Add to the second file: +

+  /usr/bin/killall synergyc
+  sleep 1
+
+

+

+And to the third file: +

+  /usr/bin/killall synergyc
+  sleep 1
+  /usr/bin/synergyc [<options>] synergy-server-hostname
+
+Note that <options> +must not include +-f or --no-daemon or +the script will never exit and you won't be able to log in. +

+

+The changes are the same for the synergy server except replace +synergyc with synergys +and use the appropriate synergys command +line options. Note that the +first script is run as root so synergys will look for the configuration +file in root's home directory then in /etc. +Make sure it exists in one of those places or use the +--config config-pathname +option to specify its location. +

+

+Note that some display managers (xdm and kdm, but not gdm) grab +the keyboard and do not release it until the user logs in for +security reasons. This prevents a synergy server from sharing +the mouse and keyboard until the user logs in. It doesn't +prevent a synergy client from synthesizing mouse and keyboard +input, though. +

+

+If you're configuring synergy to start only after you log in then edit +your .xsession file. Add just what you +would add to the third file above. +

+ +

Mac OS X

+

+TBD +

+ + + diff --git a/doc/compiling.html b/doc/compiling.html new file mode 100644 index 00000000..f3f716ef --- /dev/null +++ b/doc/compiling.html @@ -0,0 +1,126 @@ + + + + + Building and Installing Synergy + + +

Prerequisites for building

+

+To build synergy from the sources you'll need the following: + +

    +
  • Windows +
      +
    • VC++ 6.0 or up +
    + +
  • Unix +
      +
    • gcc 2.95 or up +
    • X11R4 or up headers and libraries +
    + +
  • Mac OS X +
      +
    • XCode; or gcc 2.95 or up +
    • Carbon development headers and libraries +
    +
+

+ +

Configuring the build

+

+This step is not necessary when using VC++ or XCode. +

+

+To configure the build for your platform use the configure script: +

+  ./configure
+
+For a list of options to configure use: +
+  ./configure --help
+
+On Solaris you may need to use: +
+  ./configure --x-includes=/usr/openwin/include --x-libraries=/usr/openwin/lib
+
+so synergy can find the X11 includes and libraries. +

+ +

Building

+
    +
  • Windows +

    + Start VC++ and open synergy.dsw. Set the + active configuration (Build > Set Active Configuration) to + All - Debug or All - + Release then build. Binaries are built into + ./debug or ./build. +

    + +
  • Unix or Mac OS X without XCode +

    + Simply enter: +

    +  make
    +  
    + This will build the client and server and leave them in their + respective source directories. +

    + +
  • Mac OS X with XCode +

    + Start XCode and open the synery.xcode + project. Build the all project using + the Deployment flavor. +

    + +
+ +

Installing

+
    +
  • Windows +

    + You'll need NSIS, the + Nullsoft Scriptable Install System. Build All - + Release then build Installer - Release. + This creates SynergyInstaller.exe in the top + level directory. Run this to install synergy. +

    +

    + Alternatively, you can simply copy the following files from the + debug or build + directory to a directory you choose (perhaps under the + Program Files directory): + +

      +
    • synergy.exe +
    • synergyc.exe +
    • synergys.exe +
    • synrgyhk.dll +
    +

    + +
  • Unix or Mac OS X without XCode +

    +

    +  make install
    +  
    + will install the client and server into + /usr/local/bin unless you + specified a different directory when you ran configure. +

    + +
  • Mac OS X with XCode +

    + Copy the following files from ./build to a convenient location: + +

      +
    • synergyc +
    • synergys +
    +

    + + diff --git a/doc/configuration.html b/doc/configuration.html new file mode 100644 index 00000000..494b8718 --- /dev/null +++ b/doc/configuration.html @@ -0,0 +1,275 @@ + + + + + Synergy Configuration Guide + + +

    Synergy Configuration File Format

    +

    +The synergy server requires configuration. It will try certain +pathnames to load the configuration file if you don't specify a +path using the --config command line +option. synergys --help reports those +pathnames. +

    +

    +The configuration file is a plain text file. Use any text editor +to create the configuration file. The file is broken into sections +and each section has the form: +

    +  section: name
    +    args
    +  end
    +
    +Comments are introduced by # and continue to +the end of the line. name must be one of the +following: +
      +
    • screens +
    • aliases +
    • links +
    • options +
    +See below for further explanation of each section type. The +configuration file is case-sensitive so Section, +SECTION, and section +are all different and only the last is valid. Screen names are the +exception; screen names are case-insensitive. +

    +

    +The file is parsed top to bottom and names cannot be used before +they've been defined in the screens or +aliases sections. So the +links and aliases +must appear after the screens and links +cannot refer to aliases unless the aliases +appear before the links. +

    +

    screens

    +

    +args is a list of screen names, one name per +line, each followed by a colon. Names are arbitrary strings but they +must be unique. The hostname of each computer is recommended. (This +is the computer's network name on win32 and the name reported by the +program hostname on Unix and OS X. Note +that OS X may append .local to the name you +gave your computer; e.g. somehost.local.) +There must be a screen name for the server and each client. Each +screen can specify a number of options. Options have the form +name = +value and are listed one per line +after the screen name. +

    +

    +Example: +

    +    section: screens
    +    	moe:
    +    	larry:
    +            halfDuplexCapsLock = true
    +            halfDuplexNumLock = true
    +    	curly:
    +            meta = alt
    +    end
    +
    +This declares three screens named moe, +larry, and curly. +Screen larry has half-duplex Caps Lock and +Num Lock keys (see below) and screen curly +converts the meta modifier key to the alt modifier key. +

    +

    +A screen can have the following options: +

      +
    • halfDuplexCapsLock = {true|false} +

      + This computer has a Caps Lock key that doesn't report a + press and a release event when the user presses it but + instead reports a press event when it's turned on and a + release event when it's turned off. If Caps Lock acts + strangely on all screens then you may need to set this + option to true + on the server screen. If it acts strangely on one + screen then that screen may need the option set to + true. +

      +
    • halfDuplexNumLock = {true|false} +

      + This is identical to halfDuplexCapsLock + except it applies to the Num Lock key. +

      +
    • xtestIsXineramaUnaware = {true|false} +

      + This option works around a bug in the XTest extension + when used in combination with Xinerama. It affects + X11 clients only. Not all versions of the XTest + extension are aware of the Xinerama extension. As a + result, they do not move the mouse correctly when + using multiple Xinerama screens. This option is + currently true by default. If + you know your XTest extension is Xinerama aware then set + this option to false. +

      +
    • shift = {shift|ctrl|alt|meta|super|none}
      + ctrl = {shift|ctrl|alt|meta|super|none}
      + alt = {shift|ctrl|alt|meta|super|none}
      + meta = {shift|ctrl|alt|meta|super|none}
      + super = {shift|ctrl|alt|meta|super|none}
      +

      + Map a modifier key pressed on the server's keyboard to + a different modifier on this client. This option only + has an effect on a client screen; it's accepted and + ignored on the server screen. +

      +

      + You can map, say, the shift key to shift (the default), + ctrl, alt, meta, super or nothing. Normally, you + wouldn't remap shift or ctrl. You might, however, have + an X11 server with meta bound to the Alt keys. To use + this server effectively with a windows client, which + doesn't use meta but uses alt extensively, you'll want + the windows client to map meta to alt (using + meta = alt). +

      +
    +

    + +

    aliases

    +

    + args is a list of screen names just like + in the screens section except each screen + is followed by a list of aliases, one per line, not followed + by a colon. An alias is a screen name and must be unique. During + screen name lookup each alias is equivalent to the screen name it + aliases. So a client can connect using its canonical screen name + or any of its aliases. +

    +

    + Example: +

    +    section: aliases
    +        larry:
    +            larry.stooges.com
    +        curly:
    +            shemp
    +    end
    +
    + Screen larry is also known as + larry.stooges.com and can connect as + either name. Screen curly is also + known as shemp (hey, it's just an example). +

    + +

    links

    +

    + args is a list of screen names just like + in the screens section except each screen + is followed by a list of links, one per line. Each link has the + form {left|right|up|down} = + name. A link indicates which screen is + adjacent in the given direction. +

    +

    + Example: +

    +    section: links
    +        moe:
    +            right = larry
    +            up    = curly
    +        larry:
    +            left  = moe
    +            up    = curly
    +        curly:
    +            down  = larry
    +    end
    +
    + This indicates that screen larry is to + the right of screen moe (so moving the + cursor off the right edge of moe would + make it appear at the left edge of larry), + curly is above moe, + moe is to the left of + larry, curly is + above larry, and + larry is below + curly. Note that links do not have to be + symmetrical; moving up from moe then down + from curly lands the cursor on + larry. +

    + +

    options

    +

    + args is a list of lines of the form + name = value. These set the global + options. +

    +

    + Example: +

    +    section: options
    +        heatbeat = 5000
    +        switchDelay = 500
    +    end
    +
    +

    +

    + You can use the following options: +

      +
    • heartbeat = N +

      + The server will expect each client to send a message no + less than every N milliseconds. + If no message arrives from a client within + 3N seconds the server forces that + client to disconnect. +

      +

      + If synergy fails to detect clients disconnecting while + the server is sleeping or vice versa, try using this + option. +

      +
    • switchDelay = N +

      + Synergy won't switch screens when the mouse reaches the + edge of a screen unless it stays on the edge for + N + milliseconds. This helps prevent unintentional + switching when working near the edge of a screen. +

      +
    • switchDoubleTap = N +

      + Synergy won't switch screens when the mouse reaches the + edge of a screen unless it's moved away from the edge + and then back to the edge within N + milliseconds. With + the option you have to quickly tap the edge twice to + switch. This helps prevent unintentional switching + when working near the edge of a screen. +

      +
    • screenSaverSync = {true|false} +

      + If set to false then synergy + won't synchronize screen savers. Client screen savers + will start according to their individual configurations. + The server screen saver won't start if there is input, + even if that input is directed toward a client screen. +

      +
    • relativeMouseMoves = {true|false} +

      + If set to true then secondary + screens move the mouse using relative rather than + absolute mouse moves when and only when Scroll Lock is + toggled on (i.e. the cursor is locked to the screen). + This is intended to make synergy work better with certain + games. If set to false or not + set then all mouse moves are absolute. +

      +
    + You can use both the switchDelay and + switchDoubleTap options at the same + time. Synergy will switch when either requirement is satisfied. +

    + + diff --git a/doc/developer.html b/doc/developer.html new file mode 100644 index 00000000..7391dd2a --- /dev/null +++ b/doc/developer.html @@ -0,0 +1,84 @@ + + + + + Synergy Developer's Guide + + +

    Developer's Guide

    +

    +Synergy is reasonably well commented so reading the source code +should be enough to understand particular pieces. See the +doc/PORTING +file in the synergy source code for more high-level information. +

    + +

    How it works

    +

    +The theory behind synergy is simple: the server captures mouse, +keyboard, clipboard, and screen saver events and forwards them to +one or more clients. If input is directed to the server itself +then the input is delivered normally. In practice, however, many +complications arise. +

    +

    +First, different keyboard mappings can produce different characters. +Synergy attempts to generate the same character on the client as +would've been generated on the server, including appropriate modifier +keys (like Control and Alt). Non-character keys like Shift are also +synthesized if possible. Sometimes the client simply cannot create +the character or doesn't have a corresponding non-character key and +synergy must discard the event. Note that synergy won't necessarily +synthesize an event for the corresponding key on the client's +keyboard. For example, if the client or server can't distinguish +between the left and right shift keys then synergy can't be certain +to synthesize the shift on the same side of the keyboard as the user +pressed. +

    +

    +Second, different systems have different clipboards and clipboard +formats. The X window system has a system-wide selection and +clipboard (and yet other buffers) while Microsoft Windows has only +a system-wide clipboard. Synergy has to choose which of these +buffers correspond to one another. Furthermore, different systems +use different text encodings and line breaks. Synergy mediates and +converts between them. +

    +

    +Finally, there are no standards across operating systems for some +operations that synergy requires. Among these are: intercepting +and synthesizing events; enabling, disabling, starting and stopping +the screen saver; detecting when the screen saver starts; reading +and writing the clipboard(s). +

    +

    +All this means that synergy must be customized to each operating +system (or windowing system in the case of X windows). Synergy +breaks platform differences into two groups. The first includes +the mundane platform dependent things: file system stuff, +multithreading, network I/O, multi-byte and wide character +conversion, time and sleeping, message display and logging, and +running a process detached from a terminal. This code lives in +lib/arch. +

    +

    +The second includes screen and window management handling, user +event handling, event synthesis, the clipboards, and the screen +saver. This code lives in lib/platform. +

    +

    +For both groups, there are particular classes or interfaces that +must be inherited and implemented for each platform. See the +doc/PORTING file in +the synergy source code for more information. +

    +

    Auto-generated Documentation

    +

    +Synergy can automatically generate documentation from the comments +in the code using doxygen. +Use "make doxygen" to build it yourself +from the source code into the doc/doxygen/html +directory. +

    + + diff --git a/doc/faq.html b/doc/faq.html new file mode 100644 index 00000000..1824d3e2 --- /dev/null +++ b/doc/faq.html @@ -0,0 +1,249 @@ + + + + + Synergy FAQ + + +

    Synergy Frequently Asked Questions

    + +

    Questions

    +
      +
    1. Why doesn't ctrl+alt+del work on secondary screens? +
    2. Can the server and client be using different operating systems? +
    3. What's the difference between synergy and x2x, x2vnc, etc? +
    4. What does "Cannot initialize hook library" mean? +
    5. What security/encryption does synergy provide? +
    6. What should I call my screens in the configuration? +
    7. Why do my Caps-Lock and Num-Lock keys act funny? +
    8. Can synergy share the display in addition to the mouse and keyboard? +
    9. Can synergy do drag and drop between computers? +
    10. Do AltGr or Mode-Switch or ISO_Level3_Shift work? +
    11. Why isn't synergy ported to platform XYZ? +
    12. My client can't connect. What's wrong? +
    13. Linking fails on Solaris. What's wrong? +
    14. The screen saver never starts. Why not? +
    15. I can't switch screens anymore for no apparent reason. Why? +
    16. I get the error 'Xlib: No protocol specified'. Why? +
    17. The cursor goes to secondary screen but won't come back. Why? +
    18. The cursor wraps from one edge of the screen to the opposite. Why? +
    + +

    Answers

    +
      +
    1. Why doesn't ctrl+alt+del work on secondary screens? +

      + Synergy isn't able to capture ctrl+alt+del on PC compatible + systems because it's handled completely differently than + other keystrokes. However, when the mouse is on a client + screen, pressing ctrl+alt+pause will simulate ctrl+alt+del + on the client. (A client running on Windows NT, 2000, or XP + must be running as a service for this to work.) +

      + +
    2. Can the server and client be using different operating systems? +

      + Yes. The synergy network protocol is platform neutral so + synergy doesn't care what operating systems are running on + the server and clients. +

      + +
    3. What's the difference between synergy and +x2x, x2vnc, etc? +

      + Unlike x2x, synergy supports any number of computers and + it doesn't require X on Microsoft Windows platforms. It + also has more advanced clipboard support and synchronizes + screensavers. x2vnc is also limited to two computers, + requires the separate vnc package, and is really only + appropriate for using an X system to control a non-X system. + However, the right tool for the job is whatever tool works + best for you. +

      + +
    4. What does "Cannot initialize hook library" mean? +

      + This error can occur on a synergy server running on a + Microsoft Windows operating system. It means that synergy + is already running or possibly was not shut down properly. + If it's running then first end the synergy task. If it's + not then try logging off and back on or rebooting then + starting synergy again. +

      + +
    5. What security/encryption does synergy provide? +

      + Synergy provides no built-in encryption or authentication. + Given that, synergy should not be used on or over any untrusted + network, especially the Internet. It's generally fine for home + networks. Future versions may provide built-in encryption and + authentication. +

      +

      + Strong encryption and authentication is available through SSH + (secure shell). Run the SSH daemon (i.e. server) on the same + computer that you run the synergy server. It requires no + special configuration to support synergy. On each synergy + client system, run SSH with port forwarding: +

      +
      +        ssh -f -N -L 24800:server-hostname:24800 server-hostname
      +
      +

      + where server-hostname is the name of the + SSH/synergy server. + Once ssh authenticates itself, start the synergy client + normally except use localhost or + 127.0.0.1 as the server's + address. SSH will then encrypt all communication on behalf of + synergy. Authentication is handled by the SSH authentication. +

      +

      + A free implementation of SSH for Linux and many Unix systems is + OpenSSH. For + Windows there's a port of OpenSSH using + Cygwin. +

      + +
    6. What should I call my screens in the configuration? +

      + You can use any unique name in the configuration file for each + screen but it's easiest to use the hostname of the computer. + That's the computer name not including the domain. For example, + a computer with the fully qualified domain name xyz.foo.com has + the hostname xyz. There should also be an alias for xyz to + xyz.foo.com. If you don't use the computer's hostname, you + have to tell synergy the name of the screen using a command line + option, or the startup dialog on Windows. +

      +

      + Some systems are configured to report the fully qualified domain + name as the hostname. For those systems it will be easier to use + the FQDN as the screen name. Also note that a Mac OS X system + named xyz may report its hostname as + xyz.local. If that's the case for you + then use xyz.local as the screen name. +

      + +
    7. Why do my Caps-Lock and Num-Lock keys act funny? +

      + Some systems treat the Caps-Lock and Num-Lock keys differently + than all the others. Whereas most keys report going down when + physically pressed and going up when physically released, on + these systems the Caps-Lock and Num-Lock keys report going down + when being activated and going up when being deactivated. That + is, when you press and release, say, Caps-Lock to activate it, it + only reports going down, and when you press and release to + deactivate it, it only reports going up. This confuses synergy. +

      +

      + You can solve the problem by changing your configuration file. + In the screens section, following each screen that has the + problem, add either or both of these lines as appropriate: +

      +
      +        halfDuplexCapsLock = true
      +        halfDuplexNumLock = true
      +
      +

      + Then restart synergy on the server. +

      + +
    8. Can synergy share the display in addition to the mouse and keyboard? +

      + No. Synergy is a KM solution not a KVM (keyboard, video, mouse) + solution. However, future versions will probably support KVM. + Hopefully, this will make synergy suitable for managing large + numbers of headless servers. +

      + +
    9. Can synergy do drag and drop between computers? +

      + No. That's a very cool idea and it'll be explored. However, it's + also clearly difficult and may take a long time to implement. +

      + +
    10. Does AltGr/Mode-Switch/ISO_Level3_Shift work? +

      + Yes, as of 1.0.12 synergy has full support for AltGr/Mode-switch. + That includes support for most (all?) European keyboard layouts. + All systems should be using the same keyboard layout, though, for + all characters to work. (Any character missing from a client's + layout cannot be generated by synergy.) There is experimental + support for ISO_Level3_Shift in 1.1.3. +

      + +
    11. Why isn't synergy ported to platform XYZ? +

      + Probably because the developers don't have access to platform XYZ + and/or are unfamiliar with development on XYZ. Also, synergy has + inherently non-portable aspects so there's a not insignificant + effort involved in porting. +

      + +
    12. My client can't connect. What's wrong? +

      + A common mistake when starting the client is to give the wrong + server host name. The last synergyc command line option (Unix) + or the "Server Host Name" edit field (Windows) should be the + host name (or IP address) of the server not the client's host + name. If you get the error connection failed: cannot connect + socket followed by the attempt to connect was forcefully + rejected or connection refused then the server isn't started, + can't bind the address, or the client is connecting to the wrong + host name/address or port. +

      + +
    13. Linking fails on Solaris. What's wrong? +

      + Did you add +

      +
      +        --x-includes=/usr/openwin/include --x-libraries=/usr/openwin/lib
      +
      +

      + to the configure command line? Solaris puts + the X11 includes and libraries in an unusual place and the above lets + synergy find them. +

      + +
    14. The screen saver never starts. Why not? +

      + If the synergy server is on X Windows then the screen saver will + not start while the mouse is on a client screen. This is a + consequence of how X Windows, synergy and xscreensaver work. +

      + +
    15. I can't switch screens anymore for no apparent reason. Why? +

      + This should not happen with 1.1.3 and up. Earlier versions of + synergy would not allow switching screens when a key was down and + sometimes it would believe a key was down when it was not. +

      + +
    16. I get the error 'Xlib: No protocol specified'. Why? +

      + You're running synergy without authorization to connect to the + X display. Typically the reason is running synergy as root when + logged in as non-root. Just run synergy as the same user that's + logged in. +

      + +
    17. The cursor goes to secondary screen but won't come back. Why? +

      + Your configuration is incorrect. You must indicate the neighbors + of every screen. Just because you've configured 'Apple' to be to + the left of 'Orange' does not mean that 'Orange' is to the right + of 'Apple'. You must provide both in the configuration. +

      + +
    18. The cursor wraps from one edge of the screen to the opposite. Why? +

      + Because you told it to. If you list 'Orange' to be to the left of + 'Orange' then moving the mouse off the left edge of 'Orange' will + make it jump to the right edge. Remove the offending line from the + configuration if you don't want that behavior. +

      + + + diff --git a/HISTORY b/doc/history.html similarity index 71% rename from HISTORY rename to doc/history.html index 64c306d7..dedbb016 100644 --- a/HISTORY +++ b/doc/history.html @@ -1,6 +1,12 @@ -History of Synergy -================== - + + + + + Synergy History + + +

      Synergy History

      +

      The first incarnation of synergy was CosmoSynergy, created by Richard Lee and Adam Feder then at Cosmo Software, Inc., a subsidiary of SGI (nee Silicon Graphics, Inc.), at the end of @@ -10,7 +16,11 @@ both an Irix and a Windows box on their desks and switchboxes were expensive and annoying. CosmoSynergy was a great success but Cosmo Software declined to productize it and the company was later closed. - +

      +

      Synergy is a from-scratch reimplementation of CosmoSynergy. It provides most of the features of the original and adds a few improvements. +

      + + diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 00000000..19ace6c3 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,96 @@ + + + + + Synergy Introduction + + +

      Synergy

      +

      Introduction

      +

      +synergy: [noun] a mutually advantageous conjunction of distinct elements +

      +

      +Synergy lets you easily share a single mouse and keyboard between +multiple computers with different operating systems, each with its +own display, without special hardware. It's intended for users +with multiple computers on their desk since each system uses its +own monitor(s). +

      +

      +Redirecting the mouse and keyboard is as simple as moving the mouse +off the edge of your screen. Synergy also merges the clipboards of +all the systems into one, allowing cut-and-paste between systems. +Furthermore, it synchronizes screen savers so they all start and stop +together and, if screen locking is enabled, only one screen requires +a password to unlock them all. +

      +

      +Synergy is open source and released under the +GNU Public License (GPL). +

      +

      Links

      +

      +Local
      +Getting started:
      +how to run synergy
      +how to build synergy
      +
      +Using synergy:
      +FAQ
      +tips on using synergy
      +autostart guide
      +configuration file format guide
      +
      +Future directions:
      +roadmap to future enhancements
      +
      +For developers:
      +developer's guide
      +
      +Security:
      +important note about security with synergy
      +
      +Miscellaneous documents:
      +the authors of synergy
      +the history of synergy
      +the synergy license terms
      +news about synergy
      +
      +Internet
      +synergy home page
      +synergy project page
      +synergy bug list
      +synergy community forums
      +

      +

      System Requirements

      +

      +

        +
      • Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family) +
      • Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family) +
      • Mac OS X 10.2 or higher +
      • Unix +
          +
        • X Windows version 11 revision 4 or up +
        • XTEST extension
          + (use "xdpyinfo | grep XTEST" to check for XTEST) +
        +
      +All systems must support TCP/IP networking. +

      +

      +"Unix" includes Linux, Solaris, Irix and other variants. Synergy has +only been extensively tested on Linux and may not work completely or +at all on other versions of Unix. Patches are welcome (including +patches that package binaries) at the +patches page. +

      +

      +The Mac OS X port is incomplete. It does not synchronize the screen saver, +only text clipboard data works (i.e. HTML and bitmap data do not work), +non-US English keyboards are untested and probably don't work, and there +may be problems with mouse pointer and mouse wheel acceleration. Other +problems should be filed as bugs. +

      + + diff --git a/COPYING b/doc/license.html similarity index 91% rename from COPYING rename to doc/license.html index 43bb4f3e..0c4eaa0b 100644 --- a/COPYING +++ b/doc/license.html @@ -1,16 +1,25 @@ -Synergy is copyright (C) 2002 Chris Schoeneman. -Synergy is distributed under the following license. - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - + + + + + Synergy License and Copyright + + +

      Synergy License and Copyright

      +

      +Synergy is copyright (C) 2002 Chris Schoeneman.
      +Synergy is distributed under the GNU GENERAL PUBLIC LICENSE. +

      +

      GNU GENERAL PUBLIC LICENSE

      +Version 2, June 1991 +

      +Copyright (C) 1989, 1991 Free Software Foundation, Inc.
      +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
      +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +

      +

      Preamble

      +

      The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free @@ -20,48 +29,55 @@ Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. - +

      +

      When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. - +

      +

      To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. - +

      +

      For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. - +

      +

      We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. - +

      +

      Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. - +

      +

      Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. - +

      +

      The precise terms and conditions for copying, distribution and modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - +

      +

      GNU GENERAL PUBLIC LICENSE
      +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

      +

      0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, @@ -71,14 +87,16 @@ that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". - +

      +

      Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. - +

      +

      1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate @@ -86,23 +104,31 @@ copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. - +

      +

      You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - +

      +

      2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: - +

      +

      + +
           +

      a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. - +

      +

      b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. - +

      +

      c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an @@ -113,7 +139,10 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - +

      +
      +

      +

      These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -123,38 +152,50 @@ distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. - +

      +

      Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. - +

      +

      In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. - +

      +

      3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: - +

      +

      + +
           +

      a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - +

      +

      b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - +

      +

      c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) - +

      +
      +

      +

      The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any @@ -165,13 +206,15 @@ anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. - +

      +

      If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - +

      +

      4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -179,7 +222,8 @@ void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. - +

      +

      5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are @@ -188,7 +232,8 @@ modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. - +

      +

      6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to @@ -196,7 +241,8 @@ these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. - +

      +

      7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or @@ -209,12 +255,14 @@ license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. - +

      +

      If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. - +

      +

      It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the @@ -225,10 +273,12 @@ through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. - +

      +

      This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - +

      +

      8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -236,12 +286,14 @@ may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. - +

      +

      9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - +

      +

      Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions @@ -249,7 +301,8 @@ either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. - +

      +

      10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free @@ -257,9 +310,11 @@ Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - - NO WARRANTY - +

      +

      +NO WARRANTY +

      +

      11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE @@ -270,7 +325,8 @@ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - +

      +

      12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED @@ -282,5 +338,9 @@ LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS +

      +

      +END OF TERMS AND CONDITIONS +

      + + diff --git a/doc/news.html b/doc/news.html new file mode 100644 index 00000000..b7ec37f8 --- /dev/null +++ b/doc/news.html @@ -0,0 +1,335 @@ + + + + + Synergy News + + +

      Synergy News

      + +Jun-13-2004 - Synergy 1.1.7 released + +

      +Made following changes: +

      +
        +
      • Added OS X precompiled header file forgotten in last build +
      • Fixed bug in fix for 'unexpected async reply' on X11 +
      • Removed dependency on "browser" service on win32 +
      • Fixed assertion failure when connection fails immediately +
      • Fixed failure to connect on AIX +
      • Fixed error in conversion from multibyte to wide characters +
      • Maybe fixed win32 screen saver detection +
      + +May-26-2004 - Synergy 1.1.6 released + +

      +Made following changes: +

      +
        +
      • Added preliminary Mac OS X support (client and server) +
      • Fixed ctrl+alt+del emulation on win32 +
      • Fixed ctrl+alt+del on win32 server acting on both client and server +
      • Fixed handling of screen resolution changes on win32 +
      • Fixed 'unexpected async reply' on X11 +
      • Added dependency to win32 service to avoid startup race condition +
      • Fixed reference count bug +
      • Keyboard input focus now restored on X11 (fixes loss of input in some games) +
      + +

      +The OS X port does not yet support: +

      +
        +
      • HTML and bitmap clipboard data +
      • Screen saver synchronization +
      • Non-US English keyboards +
      + +May-05-2004 - Synergy 1.1.5 released + +

      +Made following changes: +

      +
        +
      • No longer switching screens when a mouse button is down +
      • Worked around win32 mouse hook bug, fixing switch on double tap +
      • Added support for HTML and bitmap (image/bmp) clipboard data +
      • Physical mouse no longer necessary on win32 secondary screens to see cursor +
      • Added experimental relative mouse moves on secondary screen option +
      • Fixed win32 lock up when closing server with clients still connected +
      • Fixed bug in handling duplicate connections +
      • Fixed pthread mutex initialization +
      • Made synergy dependent on NetBT on win32 (for service startup order) +
      • Automake fixes; now mostly works on darwin and MinGW +
      • Fixed builds on Solaris 9, FreeBSD, and OpenBSD +
      • Partial support for MSYS/MinGW builds (NOT COMPLETE) +
      • Partial merge of OS X port (NOT COMPLETE) +
      + +Mar-31-2004 - Synergy 1.1.4 released + +

      +Made following changes: +

      +
        +
      • Fixed lookup of hosts by name of win32 +
      • Reverted tray icon code to 1.0.15 version; seems to fix the bugs +
      • Fixed crash when caps, num, or scroll lock not in key map on X11 +
      • Fixed double tap and wait to switch features +
      + +Mar-28-2004 - Synergy 1.1.3 released + +

      +Made following changes: +

      +
        +
      • Major code refactoring; reduced use of threads, added event queue +
      • Removed unused HTTP support code +
      • No longer interfering with mouse when scroll lock is toggled on +
      • Fixed minor mispositioning of mouse on win32 +
      • Unix portability fixes +
      • Added support for power management +
      • Improved keyboard handling and bug fixes +
      • Fixed dead key handling +
      + +

      +Note: the tray icon on windows is known to not work correctly when +running the synergy server on Windows 95/95/Me. +

      + +Aug-24-2003 - Synergy 1.0.14 released + +

      +Made following changes: +

      +
        +
      • Fixed bugs in setting win32 process/thread priority +
      • Fixed resource leak in opening win32 system log +
      • Fixed win32 launcher not getting non-default advanced options +
      • Synergy log copied to clipboard now transferred to other screens +
      • Hack to work around lesstif clipboard removed (fixes pasting on X) +
      + +Jul-20-2003 - Synergy 1.0.12 released + +

      +This release finally completes support for non-ASCII characters, +fully supporting most (all?) European keyboard layouts including +dead key composition. This release includes changes from several +experimental versions (1.0.9, 1.0.11, 1.1.0, 1.1.1, 1.1.2, and +1.1.3). +

      + +

      +Made following changes: +

      +
        +
      • Added non-ASCII support to win32 and X11 +
      • Added dead key support to win32 and X11 +
      • Fixed AltGr handling +
      • Added ctrl+alt+del simulation using ctrl+alt+pause +
      • Fixed loss of key event when user releases ctrl+alt+del +
      • Fixed incorrect synthesis of pointer-keys event on X11 +
      • Fixed Xinerama support +
      • Made some clipboard fixes on win32 and X11 +
      • Add tray icon menu item to copy log to clipboard +
      • Fixed mouse warping on unconnected client +
      • Stopped unconnected client from filling up event logs +
      + +May-10-2003 - Synergy 1.0.8 released + +

      +Made following changes: +

      +
        +
      • Fixed hook forwarding (fixing interaction with objectbar) +
      • Fixed "Windows" key handling and added support Win+E, Win+F, etc +
      • Added win 95/98/me support for Alt+Tab, Alt+Esc, Ctrl+Esc +
      • Fixed scroll lock locking to server screen +
      • Fixed screen flashing on X11 and Windows +
      • Fixed compile problem on 64 bit systems +
      • Fixed Xinerama support +
      • Now allowing screen names that include underscores +
      • Improved non-ASCII key handling on Windows +
      • Fixed lagginess +
      • Fixed failure to capture all mouse input on Windows +
      • Fixed auto-repeat bugs on X11 +
      • Added option to disable screen saver synchronization +
      • Added support for 4th and 5th mouse buttons on Windows +
      • Added support for "Internet" and "Multimedia" keys +
      • Fixed jumping from client to itself (mouse wrapping) +
      + +Mar-27-2003 - Synergy 1.0.6 released + +

      +Made following changes: +

      +
        +
      • Added tray icon on win32 +
      • Fixed multi-monitor support on win32 +
      • Fixed win32 screen saver detection on NT/2k/XP +
      • Added per-screen options to remap modifier keys +
      • Added global options for restricting screen jumping +
      • Added global option for detecting unresponsive clients +
      • Added more logging for why screen jump won't happen +
      • Fixed problem sending the CLIPBOARD to motif/lesstif apps +
      • Win32 launcher now remembers non-config-file state +
      + +Feb-18-2003 - Synergy 1.0.3 released + +

      +Made following changes: +

      +
        +
      • Support for X11 keymaps with only uppercase letters +
      • Fixed memory leaks +
      • Added documentation on using synergy with SSH +
      • Fixed unnecessary left-handed mouse button swapping +
      • Fixed debug build error on win32 +
      • Reduced frequency of large cursor jumps when leaving win32 server +
      • Changed cursor motion on win32 multimon to relative moves only +
      + +Jan-25-2003 - Synergy 1.0.2 released + +

      +Made following changes: +

      +
        +
      • Fixed out-of-bounds array lookup in the BSD and Windows network code +
      • Added ability to set screen options from Windows launch dialog +
      + +Jan-22-2003 - Synergy 1.0.1 released + +

      +Made following changes: +

      +
        +
      • Fixed running as a service on Windows NT family +
      + +Jan-20-2003 - Synergy 1.0.0 released + +

      +Made following changes: +

      +
        +
      • Refactored to centralize platform dependent code +
      • Added support for mouse wheel on Windows NT (SP3 and up) +
      • Portability improvements +
      • Added more documentation +
      • Fixes for working with xscreensaver +
      • Fixes for circular screen links +
      + +

      +This release has been tested on Linux and Windows. It builds and +is believed to run on Solaris and FreeBSD. It is believed to +build and run on Irix and AIX. It builds but does not work on +MacOS X. +

      + +Dec-25-2002 - Synergy 0.9.14 released + +

      +Made following changes: +

      +
        +
      • Fixed solaris compile problems (untested) +
      • Fixed irix compile problems (untested) +
      • Fixed windows client not reconnecting when server dies bug +
      • Fixed loss of ctrl+alt from windows server to non-windows clients +
      • Fixed handling of password protected windows client screen saver +
      • Now handling any number of pointer buttons on X11 +
      • Toggle key states now restored when leaving clients +
      • Added support for per-screen config options +
      • Added config options for half-duplex toggle keys on X11 +
      • Enabled class diagrams in doxygen documentation +
      + +Nov-05-2002 - Synergy 0.9.13 released + +

      +Made following changes: +

      +
        +
      • Fixed solaris compile problems (untested) +
      • Fixed MacOS X compile problems (semi-functional) +
      • Fixed gcc-3.2 compile problems +
      • Fixed some thread startup and shutdown bugs +
      • Server now quits if bind() fails with an error other than in use +
      • Fixed bug in moving mouse on Win98 without multiple monitors +
      • Fixed bug in handling TCP socket errors on read and write +
      • Fixed spurious screen saver activation on X11 +
      • Unix platforms can now read Win32 configuration files +
      • Minor error reporting fixes +
      + +Sep-14-2002 - Synergy 0.9.12 released + +

      +Made following changes: +

      +
        +
      • Win32 was not reporting log messages properly when run from synergy.exe +
      • Network error messages weren't reporting useful information +
      • Synergy won't build on gcc 3.2; added workaround for known problem +
      • X11 wasn't handling some keys/key combinations correctly +
      • Added option to change logging level when testing from synergy.exe +
      + +Sep-04-2002 - Synergy 0.9.11 released + +

      +Fixed following bugs: +

      +
        +
      • Worked around missing SendInput() on windows 95/NT 4 prior to SP3 +
      • Fixed keyboard mapping on X11 synergy client +
      + +Sep-02-2002 - Synergy 0.9.10 released + +

      +Fixed following bugs: +

      +
        +
      • The Pause/Break and KP_Enter buttons were not working correctly on windows +
      • Configuration options were being lost on windows after a reboot +
      • Added support for AltGr/ModeSwitch keys +
      • Added support for auto-start on windows when not administrator +
      • Improved autoconf +
      • Added workaround for lack of sstream header on g++ 2.95. +
      + +Aug-18-2002 - Synergy 0.9.9 released + +

      +Fixed three bugs: +

      +
        +
      • The PrintScrn button was not working correctly on windows +
      • The Win32 server could hang when a client disconnected +
      • Using the mouse wheel could hang the X server +
      + +Aug-11-2002 - Synergy 0.9.8 released + +

      +Supports any number of clients under Linux or Windows 95 or NT4 +or later. Includes mouse and keyboard sharing, clipboard +synchronization and screen saver synchronization. Supports ASCII +keystrokes, 5 button mouse with wheel, and Unicode text clipboard +format. +

      + + + diff --git a/doc/running.html b/doc/running.html new file mode 100644 index 00000000..c1775af7 --- /dev/null +++ b/doc/running.html @@ -0,0 +1,361 @@ + + + + + Guide to Running Synergy + + +

      Running Synergy

      +

      +Synergy lets you use one keyboard and mouse across multiple computers. +To do so it requires that all the computers are connected to each other +via TCP/IP networking. Most systems come with this installed. +

      + +

      Step 1 - Choose a server

      +

      +The first step is to pick which keyboard and mouse you want to share. +The computer with that keyboard and mouse is called the "primary +screen" and it runs the synergy server. All of the other computers +are "secondary screens" and run the synergy client. +

      + +

      Step 2 - Install the software

      +

      +Second, you install the software. Choose the appropriate package +and install it. For example, on Windows you would run +SynergyInstaller. You must install the +software on all the computers that will share the mouse and keyboard. +

      + +

      Step 3 - Configure and start the server

      +

      +Next you configure the server. You'll tell synergy the name of +the primary and secondary screens, which screens are next to which, +and choose desired options. On Windows there's a dialog box for +setting the configuration. On other systems you'll create a simple +text file. +

      +

      +Note that when you tell synergy that screen A +is to the left of screen B this does not +imply that B is to the right of +A. You must explicitly indicate both +relations. If you don't do both then when you're running synergy you'll +find you're unable to leave one of the screens. +

      +

      +Windows
      +On Windows run synergy by double clicking on the +synergy file. This brings up a dialog. +Configure the server: +

        +
      • Click the Server radio button +
      • Click Add to add the server to the + Screens list +
          +
        • Enter the name of server (the computer's name is the recommended name) +
        • Optionally enter other names the server is known by +
        • Click OK +
        +
      • Use Add to add your other computers +
          +
        • Using a computer's name as its screen name is recommended +
        • Choose desired screen options on the Add dialog +
        +
      • Use the controls under Layout to link screens together +
          +
        • Click (once) on the server's name in the Screens list +
        • Choose the screen to the left of the server; use --- + if there is no screen to the left of the server +
        • Choose the screens to the right, above and below the server +
        • Repeat the above steps for all the other screens +
        +
      • Use Options... to set desired options +
      • If the server's screen name is not the server's computer name: +
          +
        • Click Advanced... +
        • Enter the server's screen name next to + Screen Name +
        • Click OK +
        +
      +

      +

      +Now click Test. The server will start and +you'll see a console window with log messages telling you about synergy's +progress. If an error occurs you'll get one or more dialog boxes telling +you what the errors are; read the errors to determine the problem then +correct them and try Test again. +

      + +

      +Unix or Mac OS X
      +Create a text file named synergy.conf with the +following: +

      +    section: screens
      +       screen1:
      +       screen2:
      +    end
      +    section: links
      +       screen1:
      +           right = screen2
      +       screen2:
      +           left = screen1
      +    end
      +
      +Replace each occurrence of screen1 with the host name +of the primary screen computer (as reported by the +hostname program) and screen2 +with the host name of a secondary screen computer. In the above example, +screen2 is to the right of +screen1 and screen1 is to the +left of screen2. If necessary you should replace +right and left with +left, right, +up, or down. If you +have more than two computers you can add those too: add each computer's host +name in the screens section and add the +appropriate links. See the configuration +guide for more configuration possibilities. +

      +

      +Now start the server. Normally synergy wants to run "in the background." +It detaches from the terminal and doesn't have a visible window, effectively +disappearing from view. Until you're sure your configuration works, you +should start synergy "in the foreground" using the -f +command line option. +

      +    synergys -f --config synergy.conf
      +
      +Check the reported messages for errors. Use ctrl+c to stop synergy if +it didn't stop automatically, correct any problems, and start it again. +

      + +

      Step 4 - Start the clients

      +

      +Next you start the client on each computer that will share the server's +keyboard and mouse. +

      +

      +Windows
      +On Windows run synergy by double clicking on the +synergy file. This brings up a dialog. +Configure the client: + +

        +
      • Click the Client radio button +
      • Enter the server's computer name in Server Host Name +
          +
        • This is not the server's screen name, unless you made that the + server's host name as recommended +
        +
      • If the client's screen name is not the client's computer name: +
          +
        • Click Advanced... +
        • Enter the client's screen name next to Screen Name +
        • Click OK +
        +
      +

      +

      +Now click Test. +

      +

      +Unix or Mac OS X
      +To start a client, enter the following: +

      +    synergyc -f server-host-name
      +
      +where server-host-name is replaced by the host +name of the computer running the synergy server. +

      +

      Step 5 - Test

      +

      +Clients should immediately report a successful connection or one or +more error messages. Here are the typical problems and possible +solutions: +

        +
      • failed to open screen (X11 only) +

        + Check permission to open the X display;
        + check that the DISPLAY environment variable is set. +

        +
      • already connected +

        + Check that the synergy client isn't already running. +

        +
      • refused client +

        + Add the client to the server's configuration file. +

        +
      • connection failed +

        + check that server-host-name is + correct;
        the server cannot open the desired port, stop + the program using that port (24800) and restart the server. +

        +
      +If you get the error "Xlib: No protocol specified" +you're probably running synergy as root while logged in as another user. +X11 may prevent this for security reasons. Either run synergy as the same +user that's logged in or (not recommended) use +"xhost +" to allow anyone to connect +to the display. +

      +

      +When successful you should be able to move the mouse off the appropriate +edges of your server's screen and have it appear on a client screen. +Try to move the mouse to each screen and check all the configured links. +Check the mouse buttons and wheel and try the keyboard on each client. +You can also cut-and-paste text, HTML, and images across computers (HTML +and images are not supported on OS X yet). +

      +

      Step 6 - Run

      +

      +Once everything works correctly, stop all the clients then the server. +Then start the server with the Start button +on Windows and without the -f option on Unix +and Mac OS X. Finally start the clients similarly. +

      +

      +You can also configure synergy to start automatically when your computer +starts or when you log in. See the autostart +guide for more information. +

      +

      Command Line Options Guide

      +

      +Common Command Line Options
      +The following options are supported by synergys +and synergyc. +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       -d,--debug level  use debugging level level
       --daemonrun as a daemon (Unix) or background (Windows)
       -f,--no-daemonrun in the foreground
       -n,--name nameuse name instead of the hostname
       --restartautomatically restart on failures
       -1,--no-restartdo not restart on failure
       -h,--helpprint help and exit
       --versionprint version information and exit
      +

      +Debug levels are from highest to lowest: FATAL, +ERROR, WARNING, +NOTE, INFO, +DEBUG, DEBUG1, and +DEBUG2. Only messages at or above the given +level are logged. Messages are logged to a terminal window when +running in the foreground. Unix logs messages to syslog when running +as a daemon. The Windows NT family logs messages to the event log +when running as a service. The Windows 95 family shows FATAL log +messages in a message box and others in a terminal window when running +as a service. +

      +

      +The --name option lets the client or server +use a name other than its hostname for its screen. This name is used +when checking the configuration. +

      +

      +Neither the client nor server will automatically restart if an error +occurs that is sure to happen every time. For example, the server +will exit immediately if it can't find itself in the configuration. +On X11 both the client and server will also terminate if the +connection to the X server is lost (usually because it died). +

      +

      +Server Command Line Options
      +

      +

      +    synergys [options]
      +
      +The server accepts the common options and: +

      +

      + + + + + + + + + + + +
       -a,--address address  listen for connections on address address
       -c,--config pathname  read configuration from pathname
      +

      +

      +address has one of the following forms: +

      +    hostname
      +    :port
      +    hostname:port
      +
      +hostname is a hostname or IP address of a network +interface on the server system (e.g. somehost +or 192.168.1.100). port +is a port number from 1 to 65535. hostname defaults to +the system's hostname and port defaults to 24800. +

      +

      +Client Command Line Options
      +

      +

      +    synergyc [options] address[:port]
      +
      +address is the hostname or IP address of +the server and port is the optional network +port on the server to connect to. The client accepts the +common options and: +

      +

      + + + + + + + + + + + +
        --camp  retry connection to server until successful
        --no-camp  try connection to server only once
      +

      + + diff --git a/doc/security.html b/doc/security.html new file mode 100644 index 00000000..db8ad3f5 --- /dev/null +++ b/doc/security.html @@ -0,0 +1,56 @@ + + + + + Synergy Security Guide + + +

      Authentication and Encryption

      +

      +Synergy does not do any authentication or encryption. Any computer +can connect to the synergy server if it provides a screen name known +to the server, and all data is transferred between the server and the +clients unencrypted which means that anyone can, say, extract the +key presses used to type a password. Therefore, synergy should not +be used on untrusted networks. +

      +

      +However, there are tools that can add authentication and encryption +to synergy without modifying either those tools or synergy. One +such tool is SSH (which stands for secure shell). A free implementation +of SSH is called OpenSSH and runs +on Linux, many Unixes, and Windows (in combination with +Cygwin). +

      + +

      Configuring the Server

      +

      +Install the OpenSSH server on the same computer as the synergy server. +Configure the OpenSSH server as usual (synergy doesn't demand any +special options in OpenSSH) and start it. Start the synergy server as +usual; the synergy server requires no special options to work with +OpenSSH. +

      + +

      Configuring the Clients

      +

      +Install the OpenSSH client on each synergy client computer. Then, on +each client, start the OpenSSH client using port forwarding: +

      +  ssh -f -N -L 24800:server-hostname:24800 server-hostname
      +
      +The server-hostname is the name or address +of the computer with the OpenSSH and synergy servers. +The 24800 is the default network port used by synergy; if you use +a different port then replace both instances of 24800 with the port +number that you use. Finally, start the synergy client normally +except use localhost as the server host +name. For example: +
      +  synergyc -f localhost
      +
      +Synergy will then run normally except all communication is passed +through OpenSSH which decrypts/encrypts it on behalf of synergy. +

      + + diff --git a/doc/synergy.css b/doc/synergy.css new file mode 100644 index 00000000..6f4c3968 --- /dev/null +++ b/doc/synergy.css @@ -0,0 +1,133 @@ +body { + font-family: arial, helvetica, sans-serif; + font-size: small; + font-weight: normal; + margin-left: 0in; + margin-right: 0in; +} + +/* show underline on light blue links only on hover */ +a { + text-decoration: none; + color: #6699ff; +} +a:hover { + text-decoration: underline; +} + +/* heading */ +h3 { + display: block; + margin-top: 0em; + margin-bottom: 1.25em; + font-weight: bold; + font-variant: small-caps; + font-size: 125%; +} + +/* subheading */ +h4 { + display: block; + margin-top: 0em; + margin-bottom: 1em; + font-weight: bold; + font-variant: small-caps; + font-size: 100%; +} + +/* emphasis */ +b { + font-weight: bold; +} + +/* formatted code */ +pre { + display: block; + white-space: pre; + font-family: courier; + font-size: 87.5%; +} + +.banner { + font-weight: normal; + font-variant: small-caps; + font-size: 400%; + width: 100%; + padding: 0px 0px 0px 5px; + border-bottom: solid #6699ff 1px; +} +.banner a { + color: #000000; +} +.banner a:hover { + text-decoration: none; + color: #000000; +} + +.nav { + font-size: x-small; + font-weight: normal; + background-color: #d4d4d4; + + padding: 2px 0px 2px 0px; + margin: 0px; +} +.nav a:hover { + text-decoration: none; + color: #666666; +} +.nav td { + padding-right: 20px; + padding-left: 5px; + text-indent: 1em; +} +.nav .section { + width: 100%; + text-indent: 0em; + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-bottom: solid #aaaaaa 1px; + padding-bottom: 0px; + font-weight: bold; + color: #777777; +} + +.main { + font-size: small; + font-weight: normal; + margin-left: 0.1in; + margin-right: 0.25in; +} + +.main table { + font-size: small; + font-weight: normal; + margin-left: 0.1in; + margin-right: 0.25in; +} + +.date { + font-weight: bold; +} + +.arg { + font-style: italic; + font-family: courier; +} + +.code { + font-family: courier; +} + +.code table { + font-size: small; +} + +.fakelink { + color: #6699ff; +} + +.hide { + display:none +} diff --git a/doc/tips.html b/doc/tips.html new file mode 100644 index 00000000..175c094a --- /dev/null +++ b/doc/tips.html @@ -0,0 +1,93 @@ + + + + + Synergy Tips and Tricks + + +

      Tips and Tricks

      +
        +
      • +

        + Be aware that not all keystrokes can be handled by synergy. In + particular, ctrl+alt+del is not handled. However, synergy can + convert ctrl+alt+pause into ctrl+alt+del on the client side. + (Synergy must be installed as a service on the client for this to + work on the Windows NT family.) Some non-standard keys may not + work, especially "multimedia" buttons, though several are + correctly handled. +

        + +
      • +

        + A screen can be its own neighbor. That allows a screen to "wrap". + For example, if a configuration linked the left and right sides of + a screen to itself then moving off the left of the screen would put + the mouse at the right of the screen and vice versa. +

        + +
      • +

        + You cannot switch screens when the Scroll Lock is toggled on. Use + this to prevent unintentional switching. +

        + +
      • +

        + Turn off mouse driven virtual desktop switching on X windows. It + will interfere with synergy. Use keyboard shortcuts instead. +

        + +
      • +

        + Synergy's screen saver synchronization works best with xscreensaver + under X windows. Synergy works better with xscreensaver if it is + using one of the screen saver extensions. Prior to xscreensaver 4.0 + you can use -mit-extension, + -sgi-extension, or + -xidle-extension + command line options to enable an extension (assuming your server has + the extension). Starting with 4.0 you must enable the corresponding + option in your .xscreensaver file. +

        + +
      • +

        + Synergy automatically converts newlines in clipboard text (Unix + expects \n to end each line while Windows + expects \r\n). +

        + +
      • +

        + Clients can be started and stopped at any time. When a screen is + not connected, the mouse will jump over that screen as if the mouse + had moved all the way across it and jumped to the next screen. +

        + +
      • +

        + A client's keyboard and mouse are fully functional while synergy is + running. You can use them in case synergy locks up. +

        + +
      • +

        + Strong authentication and encryption is available by using SSH. See + the security guide for more information. + Synergy does not otherwise provide secure communications and it should + not be used on or over untrusted networks. +

        + +
      • +

        + Synergy doesn't work if a 16-bit Windows application has the focus + on Windows 95/98/Me. This is due to limitations of Windows. One + commonly used 16-bit application is the command prompt + (command.exe) + and this includes synergy's log window when running in test mode. +

        +
      + + + diff --git a/doc/todo.html b/doc/todo.html new file mode 100644 index 00000000..02a12a07 --- /dev/null +++ b/doc/todo.html @@ -0,0 +1,70 @@ + + + + + Synergy To Do List + + +

      Synergy To Do List

      +

      +This page describes the planned development of Synergy. There are +no dates or deadlines. Instead, you'll find the features to come +and the rough order they can be expected to arrive. +

      + +

      Short term

      +

      +Synergy should work seamlessly. When it works correctly, it works +transparently so you don't even think about it. When it breaks, +you're forced out of the illusion of a unified desktop. The first +priority is fixing those bugs that break the illusion. +

      +

      +Some of these bugs are pretty minor and some people would rather +have new features first. But I'd rather fix the current +foundation before building on it. That's not to say features +won't get added until after bug fixes; sometimes it's just too +tempting to code up a feature. +

      + +

      Medium term

      +

      +Some features fit well into Synergy's current design and may simply +enhance it's current capabilities. +

        +
      • Configurable hot key screen switching +
      • Configurable hot key to lock to a screen +
      • Configurable hot key to pop up a screen switch menu +
      • Configure screen saver synchronization on or off +
      • Graphical interface configuration and control on all platforms +
      • Graphical status feedback on all platforms +
      • More supported clipboard formats (particularly rich text) +
      +

      + +

      Long term

      +

      +Two features stand out as long term goals: +

        +
      • Support N computers on +M monitors +
      • Drag and drop across computers +
      +

      +

      +The first feature means sharing a monitor or monitors the way the +keyboard and mouse are shared. With this, Synergy would be a full +KVM solution. Not only would it support a few computers sharing +one screen (still using the mouse to roll from one screen to +another), but it should also support dozens of computers to provide +a solution for server farm administrators. In this capacity, it +may need to support text (as opposed to bitmap graphics) screens. +

      +

      +The second feature would enhance the unified desktop illusion. It +would make it possible to drag a file and possibly other objects +to another screen. The object would be copied (or moved). I expect +this to be a very tricky feature. +

      + + From 5fd563b27899c3cfef493aa5dc07e0e016dca4b0 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Aug 2004 16:04:21 +0000 Subject: [PATCH 702/807] Added files required by automake. They simply reference the corresponding HTML file. --- AUTHORS | 1 + COPYING | 1 + INSTALL | 1 + NEWS | 1 + 4 files changed, 4 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 NEWS diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..ea648387 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +See doc/authors.html. diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..e2406b42 --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +See doc/license.html diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..7262d958 --- /dev/null +++ b/INSTALL @@ -0,0 +1 @@ +See doc/compiling.html. diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..e9aa7916 --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +See doc/news.html. From cab52ae6a6493d985a91fa5908c79f8b3959d7f0 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Aug 2004 16:31:47 +0000 Subject: [PATCH 703/807] Updated packagers to handle new documentation. --- Makefile.am | 2 +- dist/nullsoft/installer.mak | 13 ++----------- dist/nullsoft/synergy.nsi | 23 ++++++++++++++--------- dist/rpm/synergy.spec.in | 19 +++++++++++++++---- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/Makefile.am b/Makefile.am index 7b922468..6ef89d99 100644 --- a/Makefile.am +++ b/Makefile.am @@ -90,7 +90,7 @@ dist-pkg: all cp $(PKG_DOC_FILES) $(PKGTOPDIR)/@PACKAGE@-@VERSION@/doc (cd $(PKGTOPDIR)/@PACKAGE@-@VERSION@; \ chmod 644 *; \ - chmod 755 $(PKG_PROG_FILES); \ + chmod 755 doc $(PKG_PROG_FILES); \ strip $(PKG_PROG_FILES) ) type=`uname -s -m | tr '[A-Z] ' '[a-z].'`; \ (cd $(PKGTOPDIR); tar cf - @PACKAGE@-@VERSION@ | \ diff --git a/dist/nullsoft/installer.mak b/dist/nullsoft/installer.mak index 30d4d205..8ee184a0 100755 --- a/dist/nullsoft/installer.mak +++ b/dist/nullsoft/installer.mak @@ -3,17 +3,8 @@ DEPTH=..\.. NSIS="D:\Program Files\NSIS\makensis" -DOCS = \ - AUTHORS \ - BUGS \ - COPYING \ - ChangeLog \ - FAQ \ - HISTORY \ - INSTALL \ - NEWS \ - README \ - TODO \ +DOCS = \ + ChangeLog \ $(NULL) default: dosifydocs installer diff --git a/dist/nullsoft/synergy.nsi b/dist/nullsoft/synergy.nsi index 8ee33656..07b07cc2 100644 --- a/dist/nullsoft/synergy.nsi +++ b/dist/nullsoft/synergy.nsi @@ -59,16 +59,21 @@ Section "Synergy (required)" File "${DEPTH}\build\synergyc.exe" File "${DEPTH}\build\synergys.exe" File "${DEPTH}\build\*.dll" - File AUTHORS.txt - File BUGS.txt - File COPYING.txt File ChangeLog.txt - File FAQ.txt - File HISTORY.txt - File INSTALL.txt - File NEWS.txt - File README.txt - File TODO.txt + File doc\authors.html + File doc\autostart.html + File doc\compiling.html + File doc\configuration.html + File doc\developer.html + File doc\faq.html + File doc\history.html + File doc\index.html + File doc\license.html + File doc\news.html + File doc\running.html + File doc\security.html + File doc\tips.html + File doc\todo.html ; Write the installation path into the registry WriteRegStr HKLM SOFTWARE\Synergy "Install_Dir" "$INSTDIR" diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in index 073f2f68..a4b368ba 100644 --- a/dist/rpm/synergy.spec.in +++ b/dist/rpm/synergy.spec.in @@ -36,13 +36,24 @@ rm -rf $RPM_BUILD_ROOT /usr/bin/synergyc /usr/bin/synergys %doc AUTHORS -%doc BUGS %doc COPYING %doc ChangeLog -%doc FAQ -%doc HISTORY %doc INSTALL %doc NEWS %doc README -%doc TODO +%doc doc/authors.html +%doc doc/autostart.html +%doc doc/compiling.html +%doc doc/configuration.html +%doc doc/developer.html +%doc doc/faq.html +%doc doc/history.html +%doc doc/index.html +%doc doc/license.html +%doc doc/news.html +%doc doc/running.html +%doc doc/security.html +%doc doc/tips.html +%doc doc/todo.html +%doc doc/synergy.css %doc examples/synergy.conf From 8a5f6f87524730ac9bda23b55cfacaf58cc3e18f Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Aug 2004 17:36:53 +0000 Subject: [PATCH 704/807] Reverted COPYING so win32 installer build can use it. --- COPYING | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 286 insertions(+), 1 deletion(-) diff --git a/COPYING b/COPYING index e2406b42..43bb4f3e 100644 --- a/COPYING +++ b/COPYING @@ -1 +1,286 @@ -See doc/license.html +Synergy is copyright (C) 2002 Chris Schoeneman. +Synergy is distributed under the following license. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE +IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE +COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM +"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR +IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED +TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY +WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED +ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, +SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF +THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT +LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + END OF TERMS AND CONDITIONS From 59163aeb34047eec41b8ea99fcf41136a297db93 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Aug 2004 17:37:11 +0000 Subject: [PATCH 705/807] Updated win32 installer to use new docs. --- dist/nullsoft/synergy.nsi | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/dist/nullsoft/synergy.nsi b/dist/nullsoft/synergy.nsi index 07b07cc2..1cd94840 100644 --- a/dist/nullsoft/synergy.nsi +++ b/dist/nullsoft/synergy.nsi @@ -60,20 +60,20 @@ Section "Synergy (required)" File "${DEPTH}\build\synergys.exe" File "${DEPTH}\build\*.dll" File ChangeLog.txt - File doc\authors.html - File doc\autostart.html - File doc\compiling.html - File doc\configuration.html - File doc\developer.html - File doc\faq.html - File doc\history.html - File doc\index.html - File doc\license.html - File doc\news.html - File doc\running.html - File doc\security.html - File doc\tips.html - File doc\todo.html + File ${DEPTH}\doc\authors.html + File ${DEPTH}\doc\autostart.html + File ${DEPTH}\doc\compiling.html + File ${DEPTH}\doc\configuration.html + File ${DEPTH}\doc\developer.html + File ${DEPTH}\doc\faq.html + File ${DEPTH}\doc\history.html + File ${DEPTH}\doc\index.html + File ${DEPTH}\doc\license.html + File ${DEPTH}\doc\news.html + File ${DEPTH}\doc\running.html + File ${DEPTH}\doc\security.html + File ${DEPTH}\doc\tips.html + File ${DEPTH}\doc\todo.html ; Write the installation path into the registry WriteRegStr HKLM SOFTWARE\Synergy "Install_Dir" "$INSTDIR" @@ -92,9 +92,8 @@ Section "Start Menu Shortcuts" CreateDirectory "$SMPROGRAMS\Synergy" CreateShortCut "$SMPROGRAMS\Synergy\Synergy.lnk" "$INSTDIR\synergy.exe" "" "$INSTDIR\synergy.exe" 0 - CreateShortCut "$SMPROGRAMS\Synergy\README.lnk" "$INSTDIR\README.txt" - CreateShortCut "$SMPROGRAMS\Synergy\NEWS.lnk" "$INSTDIR\NEWS.txt" - CreateShortCut "$SMPROGRAMS\Synergy\FAQ.lnk" "$INSTDIR\FAQ.txt" + CreateShortCut "$SMPROGRAMS\Synergy\README.lnk" "$INSTDIR\index.html" + CreateShortCut "$SMPROGRAMS\Synergy\FAQ.lnk" "$INSTDIR\faq.html" CreateShortCut "$SMPROGRAMS\Synergy\Synergy Folder.lnk" "$INSTDIR" CreateShortCut "$SMPROGRAMS\Synergy\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 From 14ebaf0515e2ed72ef0071476aa1026ac81fc0a4 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 1 Aug 2004 17:37:28 +0000 Subject: [PATCH 706/807] Fixed errors in new docs markup. --- doc/faq.html | 2 +- doc/index.html | 2 +- doc/running.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/faq.html b/doc/faq.html index 1824d3e2..86f2d539 100644 --- a/doc/faq.html +++ b/doc/faq.html @@ -87,7 +87,7 @@ client system, run SSH with port forwarding:

      -        ssh -f -N -L 24800:server-hostname:24800 server-hostname
      +        ssh -f -N -L 24800:server-hostname:24800 server-hostname
       

      where server-hostname is the name of the diff --git a/doc/index.html b/doc/index.html index 19ace6c3..22a0dfe7 100644 --- a/doc/index.html +++ b/doc/index.html @@ -73,7 +73,7 @@ Miscellaneous documents:

      • X Windows version 11 revision 4 or up
      • XTEST extension
        - (use "xdpyinfo | grep XTEST" to check for XTEST) + (use "xdpyinfo | grep XTEST" to check for XTEST)
All systems must support TCP/IP networking. diff --git a/doc/running.html b/doc/running.html index c1775af7..61bbd129 100644 --- a/doc/running.html +++ b/doc/running.html @@ -201,7 +201,7 @@ If you get the error "Xlib: No protocol specified" you're probably running synergy as root while logged in as another user. X11 may prevent this for security reasons. Either run synergy as the same user that's logged in or (not recommended) use -"xhost +" to allow anyone to connect +"xhost +" to allow anyone to connect to the display.

From 0df2e9ce6a00cd01b82cd0c803e67c74e72c588f Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 3 Aug 2004 21:14:30 +0000 Subject: [PATCH 707/807] Fixed warnings in deployment build style on OS X. --- lib/arch/CArchMultithreadPosix.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 1ccbeed2..26bb0f5a 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -179,6 +179,7 @@ CArchMultithreadPosix::newCondVar() { CArchCondImpl* cond = new CArchCondImpl; int status = pthread_cond_init(&cond->m_cond, NULL); + (void)status; assert(status == 0); return cond; } @@ -187,6 +188,7 @@ void CArchMultithreadPosix::closeCondVar(CArchCond cond) { int status = pthread_cond_destroy(&cond->m_cond); + (void)status; assert(status == 0); delete cond; } @@ -195,6 +197,7 @@ void CArchMultithreadPosix::signalCondVar(CArchCond cond) { int status = pthread_cond_signal(&cond->m_cond); + (void)status; assert(status == 0); } @@ -202,6 +205,7 @@ void CArchMultithreadPosix::broadcastCondVar(CArchCond cond) { int status = pthread_cond_broadcast(&cond->m_cond); + (void)status; assert(status == 0); } @@ -309,6 +313,7 @@ void CArchMultithreadPosix::closeMutex(CArchMutex mutex) { int status = pthread_mutex_destroy(&mutex->m_mutex); + (void)status; assert(status == 0); delete mutex; } From 8c93cfdb2486982b1fc17c9456084db2fff0d56d Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 3 Aug 2004 22:02:57 +0000 Subject: [PATCH 708/807] Fixed space key on OS X server -> client. --- lib/platform/COSXKeyState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index e1cd57a1..bbbfc2ff 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -241,7 +241,7 @@ const KeyID COSXKeyState::s_virtualKey[] = /* 0x2e */ kKeyNone, // M /* 0x2f */ kKeyNone, // . /* 0x30 */ kKeyTab, // tab - /* 0x31 */ kKeyKP_Space, // space + /* 0x31 */ kKeyNone, // space /* 0x32 */ kKeyNone, // ` /* 0x33 */ kKeyBackSpace, // Backspace /* 0x34 */ kKeyNone, // undefined From b72d882f41f3e03c678c5011dff08cc6df5fb930 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 4 Aug 2004 22:48:08 +0000 Subject: [PATCH 709/807] Fixed handling of auto-repeat, ctrl + character key, and button to KeyCode translation. --- lib/platform/COSXKeyState.cpp | 47 ++++++++++++++++++++++++----------- lib/platform/COSXKeyState.h | 22 ++++++++++++++++ lib/platform/COSXScreen.cpp | 17 +++++++------ lib/synergy/KeyTypes.h | 4 ++- 4 files changed, 66 insertions(+), 24 deletions(-) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index bbbfc2ff..bab7c1fb 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -18,7 +18,7 @@ struct CKeyEntry { public: KeyID m_keyID; - KeyButton m_button; + UInt32 m_keyCode; }; static const CKeyEntry s_keys[] = { /* ASCII */ @@ -466,6 +466,19 @@ COSXKeyState::~COSXKeyState() // do nothing } +KeyButton +COSXKeyState::mapKeyCodeToKeyButton(UInt32 keyCode) +{ + // 'A' maps to 0 so shift every id by +1 + return static_cast(keyCode + 1); +} + +UInt32 +COSXKeyState::mapKeyButtonToKeyCode(KeyButton keyButton) +{ + return static_cast(keyButton - 1); +} + void COSXKeyState::sendKeyEvent(void* target, bool press, bool isAutoRepeat, @@ -524,10 +537,9 @@ COSXKeyState::doUpdateKeys() // FIXME -- this probably needs to be more dynamic to support // non-english keyboards. also need to map modifiers needed // for each KeyID. - // FIXME -- add one so we don't use KeyButton 0 (reserved to be no key) for (UInt32 i = 0; i < sizeof(s_keys) / sizeof(s_keys[0]); ++i) { m_keyMap.insert(std::make_pair(s_keys[i].m_keyID, - s_keys[i].m_button + 1)); + mapKeyCodeToKeyButton(s_keys[i].m_keyCode))); } // add modifiers @@ -558,8 +570,7 @@ COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool isAutoRepeat) { LOG((CLOG_DEBUG2 "doFakeKeyEvent button:%d, press:%d", button, press)); // let system figure out character for us - // FIXME -- subtracting one because we added one in doUpdateKeys. - CGPostKeyboardEvent(0, static_cast(button) - 1, press); + CGPostKeyboardEvent(0, mapKeyButtonToKeyCode(button), press); } KeyButton @@ -629,9 +640,20 @@ COSXKeyState::mapKeyFromEvent(EventRef event, KeyModifierMask* maskOut) const KeyID id = s_virtualKey[vkCode]; // check if not in table; map character to key id + KeyModifierMask activeMask = getActiveModifiers(); if (id == kKeyNone && c != 0) { if ((c & 0x80u) == 0) { - // ASCII + // ASCII. if it's a control code and the control key is + // pressed then map it back to the original character. + if ((activeMask & KeyModifierControl) != 0 && c >= 1 && c <= 31) { + c += 'A' - 1; + + // if shift isn't pressed then map to lowercase + if ((activeMask & KeyModifierShift) == 0) { + c += 'a' - 'A'; + } + } + id = static_cast(c) & 0xffu; } else { @@ -643,11 +665,6 @@ COSXKeyState::mapKeyFromEvent(EventRef event, KeyModifierMask* maskOut) const id = static_cast(c) & 0xffu; } } - - KeyModifierMask activeMask = getActiveModifiers(); - if (id != kKeyNone && c != 0) { - // FIXME - } // map modifier key if (maskOut != NULL) { @@ -665,8 +682,8 @@ COSXKeyState::addKeyButton(KeyButtons& keys, KeyID id) const if (keyIndex == m_keyMap.end()) { return; } - // XXX -- subtract one because added one in doUpdateKeys - keys.push_back(keyIndex->second - 1); +// YYY -1 + keys.push_back(keyIndex->second); } void @@ -706,8 +723,8 @@ COSXKeyState::handleModifierKey(void* target, KeyID id, bool down) if (keyIndex == m_keyMap.end()) { return; } - // FIXME -- subtract one because we added one in doUpdateKeys - KeyButton button = keyIndex->second - 1; +// YYY -1 + KeyButton button = keyIndex->second; setKeyDown(button, down); sendKeyEvent(target, down, false, id, getActiveModifiers(), 0, button); } diff --git a/lib/platform/COSXKeyState.h b/lib/platform/COSXKeyState.h index f83ec07d..037efb16 100644 --- a/lib/platform/COSXKeyState.h +++ b/lib/platform/COSXKeyState.h @@ -25,9 +25,31 @@ A key state for OS X. */ class COSXKeyState : public CKeyState { public: + // OS X uses a physical key if 0 for the 'A' key. synergy reserves + // KeyButton 0 so we offset all OS X physical key ids by this much + // when used as a KeyButton and by minus this much to map a KeyButton + // to a physical button. + enum { + KeyButtonOffset = 1 + }; + COSXKeyState(); virtual ~COSXKeyState(); + //! Map physical key id to a KeyButton id + /*! + Maps an OS X key code to a KeyButton. This simply remaps the ids + so we don't use KeyButton 0. + */ + static KeyButton mapKeyCodeToKeyButton(UInt32 keyCode); + + //! Map KeyButton id to a physical key id + /*! + Maps a KeyButton to an OS X key code. This is the inverse of + mapKeyCodeToKeyButton. + */ + static UInt32 mapKeyButtonToKeyCode(KeyButton keyButton); + //! Map key event to a key /*! Converts a key event into a KeyID and the shadow modifier state diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index d9dd62f8..75016d5f 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -808,15 +808,16 @@ COSXScreen::onKey(EventRef event) const UInt32 eventKind = GetEventKind(event); // get the key - KeyButton button; + UInt32 keyCode; GetEventParameter(event, kEventParamKeyCode, typeUInt32, - NULL, sizeof(button), NULL, &button); - LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, button)); + NULL, sizeof(keyCode), NULL, &keyCode); + LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, keyCode)); + KeyButton button = COSXKeyState::mapKeyCodeToKeyButton(keyCode); - // sadly, OS X doesn't report the button for modifier keys. button will - // be zero for modifier keys. since that's not good enough we'll have - // to figure out what the key was. - if (button == 0 && eventKind == kEventRawKeyModifiersChanged) { + // sadly, OS X doesn't report the keyCode for modifier keys. keyCode + // will be zero for modifier keys. since that's not good enough we'll + // have to figure out what the key was. + if (keyCode == 0 && eventKind == kEventRawKeyModifiersChanged) { // get old and new modifier state KeyModifierMask oldMask = getActiveModifiers(); KeyModifierMask newMask = mapMacModifiersToSynergy(event); @@ -839,7 +840,7 @@ COSXScreen::onKey(EventRef event) const KeyID key = m_keyState->mapKeyFromEvent(event, &mask); m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, - key, mask, 0, button); + key, mask, 1, button); return true; } diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 687408be..c988779a 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -28,7 +28,9 @@ typedef UInt32 KeyID; //! Key Code /*! Type to hold a physical key identifier. That is, it identifies a -physical key on the keyboard. +physical key on the keyboard. KeyButton 0 is reserved to be an +invalid key; platforms that use 0 as a physical key identifier +will have to remap that value to some arbitrary unused id. */ typedef UInt16 KeyButton; From b564807d872238cc20516a294550a133720ad375 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 4 Aug 2004 22:48:30 +0000 Subject: [PATCH 710/807] Removed cross-compile setting from project. --- synergy.xcode/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/synergy.xcode/project.pbxproj b/synergy.xcode/project.pbxproj index cfc89ba2..9f04402f 100644 --- a/synergy.xcode/project.pbxproj +++ b/synergy.xcode/project.pbxproj @@ -73,8 +73,6 @@ //084 08FB7793FE84155DC02AAC07 = { buildSettings = { - MACOSX_DEPLOYMENT_TARGET = 10.2; - SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; }; buildStyles = ( 014CEA460018CE2711CA2923, From 9e48870451de300d6c65dd7336b62be5c1acdd2a Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 5 Aug 2004 20:42:51 +0000 Subject: [PATCH 711/807] Fixed missing license file and PORTING in win32 installer build. --- dist/nullsoft/synergy.nsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dist/nullsoft/synergy.nsi b/dist/nullsoft/synergy.nsi index 1cd94840..e7d61938 100644 --- a/dist/nullsoft/synergy.nsi +++ b/dist/nullsoft/synergy.nsi @@ -59,7 +59,9 @@ Section "Synergy (required)" File "${DEPTH}\build\synergyc.exe" File "${DEPTH}\build\synergys.exe" File "${DEPTH}\build\*.dll" + File COPYING.txt File ChangeLog.txt + File ${DEPTH}\doc\PORTING File ${DEPTH}\doc\authors.html File ${DEPTH}\doc\autostart.html File ${DEPTH}\doc\compiling.html From 65ee423274e45f0b927fcbbbc0022d21d7c733b1 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 5 Aug 2004 21:03:20 +0000 Subject: [PATCH 712/807] Updated documentation. --- ChangeLog | 370 +++++++++++++++++++++++++++++++++++++++++--------- doc/news.html | 21 +++ 2 files changed, 325 insertions(+), 66 deletions(-) diff --git a/ChangeLog b/ChangeLog index c533e6ba..da5bb3ba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,243 @@ +2004/08/05 20:42:51 crs +dist/nullsoft/synergy.nsi + +Fixed missing license file and PORTING in win32 installer build. + +---------- +2004/08/04 22:48:30 crs +synergy.xcode/project.pbxproj + +Removed cross-compile setting from project. + +---------- +2004/08/04 22:48:08 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp +lib/synergy/KeyTypes.h + +Fixed handling of auto-repeat, ctrl + character key, and button +to KeyCode translation. + +---------- +2004/08/03 22:02:57 crs +lib/platform/COSXKeyState.cpp + +Fixed space key on OS X server -> client. + +---------- +2004/08/03 21:14:30 crs +lib/arch/CArchMultithreadPosix.cpp + +Fixed warnings in deployment build style on OS X. + +---------- +2004/08/01 17:37:28 crs +doc/faq.html +doc/index.html +doc/running.html + +Fixed errors in new docs markup. + +---------- +2004/08/01 17:37:11 crs +dist/nullsoft/synergy.nsi + +Updated win32 installer to use new docs. + +---------- +2004/08/01 17:36:53 crs +COPYING + +Reverted COPYING so win32 installer build can use it. + +---------- +2004/08/01 16:31:47 crs +Makefile.am +dist/nullsoft/installer.mak +dist/nullsoft/synergy.nsi +dist/rpm/synergy.spec.in + +Updated packagers to handle new documentation. + +---------- +2004/08/01 16:04:21 crs +AUTHORS +COPYING +INSTALL +NEWS + +Added files required by automake. They simply reference the +corresponding HTML file. + +---------- +2004/08/01 16:00:18 crs +AUTHORS +BUGS +COPYING +FAQ +HISTORY +INSTALL +Makefile.am +NEWS +PORTING +README +TODO +configure.in +doc/Makefile.am +doc/PORTING +doc/authors.html +doc/autostart.html +doc/compiling.html +doc/configuration.html +doc/developer.html +doc/faq.html +doc/history.html +doc/index.html +doc/license.html +doc/news.html +doc/running.html +doc/security.html +doc/synergy.css +doc/tips.html +doc/todo.html + +Updated documentation. Converted most documation to HTML. + +---------- +2004/07/31 14:34:02 crs +INSTALL +PORTING +README +TODO + +Documentation changes. + +---------- +2004/07/31 11:19:39 crs +acinclude.m4 +lib/arch/CArchNetworkBSD.cpp +lib/platform/CXWindowsEventQueueBuffer.cpp + +Now using instead of . Also added a bit +to autoconf to ensure we don't use poll on OS X. + +---------- +2004/07/29 22:11:27 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Fixed handling of modifier keys on OS X. Also made OS X client +ignore small mouse wheel events (which seem to get sent by some +win32 systems). Other platforms were already ignoring them. + +---------- +2004/07/29 22:09:28 crs +cmd/synergys/synergys.cpp + +Worked around bug in ifstream::operator!() on OS X. + +---------- +2004/07/29 21:59:26 crs +lib/platform/CSynergyHook.cpp + +Fixed handling of ctrl and alt keys when using low level hooks. +They were being discarded so the server wouldn't correctly send +ctrl, alt, or AltGr to clients. + +---------- +2004/07/29 21:57:42 crs +lib/platform/CMSWindowsScreenSaver.cpp + +Added comment about a problem detecting the screen saver. + +---------- +2004/07/29 21:53:30 crs +lib/platform/CMSWindowsKeyState.cpp + +Worked around bogus key mapping on 95/98/me where multiple virtual +keys are mapped to the same button. For example, the backslash +virtual key shares a button with some other virtual key on british +english key mappings. Synergy could end up using the wrong virtual +key. In the given case, the other virtual key produced no character +at all. To determine which virtual key should really be mapped to +a button we map the button back to a virtual key and see if it's the +virtual key we started with. + +Also fixed mapping of pause key. Previously, windows+pause sent to +a win32 client wouldn't bring up system properties like it should. + +---------- +2004/07/29 21:50:17 crs +lib/platform/CMSWindowsDesks.cpp + +Synergy now steals window activation when using low level hooks +and a console window is the active window. This is to work around +console windows preventing the hook from detecting the shift key. + +---------- +2004/07/29 21:48:40 crs +lib/net/CTCPSocket.cpp + +Worked around bug/weirdness on OS X. It's reporting that a +non-blocking connect is available for writing (i.e. the connection +was successful) when the connection has actually failed. This +caused synergy to rapidly retry connecting. This change makes +synergy check for a connection error whether one was reported or +not. Thankfully, OS X does correctly set the socket error state +when it bogusly reports a successful connection so we can tell the +connection failed. + +---------- +2004/07/28 21:54:39 crs +lib/net/CTCPSocket.cpp + +Added workaround for apparent bug in OS X 10.3.4. If you started +a synergy client on that OS and pointed it at a system that wasn't +listening for connections then instead of the connection attempt +failing with 'connection refused' the system would claim the +connection succeeded. A subsequent read would reveal the problem +and synergy would "disconnect" and retry, causing the CPU to spin. +The system does correctly set the socket error state so this +workaround checks for socket errors when connecting whether or not +select reports an error state. + +Also, sometimes the system doesn't claim success but doesn't report +an error. Synergy eventually times out these attempts. + +---------- +2004/07/25 14:18:50 crs +configure.in +lib/common/Version.h + +Changed version to 1.1.8. + +---------- +2004/06/22 21:11:14 crs +lib/platform/CXWindowsScreen.cpp + +Disable key event capture on X11. This was going to be used to +detect synergy hotkeys but a design flaw in X11 makes it problematic +with many applications. We'll have to fall back to the more +traditional XGrabKey when the time comes. + +---------- +2004/06/16 21:07:48 crs +synergy.xcode/project.pbxproj + +Added NDEBUG to and removed debugging symbols from XCode deployment +project. + +---------- +2004/06/13 17:11:19 crs +ChangeLog +NEWS + +Updated documentation. + +---------- 2004/06/12 20:48:04 crs lib/platform/CMSWindowsDesks.cpp lib/platform/CMSWindowsDesks.h @@ -60,23 +300,23 @@ which causes synergy to die. ---------- 2004/05/26 19:23:32 crs -//depot/project/synergy/lib/common/MacOSXPrecomp.h -//depot/project/synergy/lib/common/common.h -//depot/project/synergy/lib/platform/COSXClipboardTextConverter.cpp -//depot/project/synergy/lib/platform/COSXClipboardTextConverter.h -//depot/project/synergy/lib/platform/COSXEventQueueBuffer.cpp -//depot/project/synergy/lib/platform/COSXKeyState.cpp -//depot/project/synergy/lib/platform/COSXKeyState.h -//depot/project/synergy/lib/platform/COSXScreen.cpp -//depot/project/synergy/lib/platform/COSXScreen.h -//depot/project/synergy/synergy.xcode/project.pbxproj +lib/common/MacOSXPrecomp.h +lib/common/common.h +lib/platform/COSXClipboardTextConverter.cpp +lib/platform/COSXClipboardTextConverter.h +lib/platform/COSXEventQueueBuffer.cpp +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +synergy.xcode/project.pbxproj Merged Bertrand's OS X changes. Also added support for mouse wheel on OS X server. ---------- 2004/05/18 20:32:13 crs -//depot/project/synergy/lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.cpp If the server manages to detect ctrl+alt+del it will no longer send that to the client. If it did then the user could see the effect of @@ -85,8 +325,8 @@ user can use ctrl+alt+pause to emulate ctrl+alt+del on the client. ---------- 2004/05/18 20:26:48 crs -//depot/project/synergy/lib/platform/CXWindowsEventQueueBuffer.cpp -//depot/project/synergy/lib/platform/CXWindowsEventQueueBuffer.h +lib/platform/CXWindowsEventQueueBuffer.cpp +lib/platform/CXWindowsEventQueueBuffer.h Fixed bug that could allow multiple threads to simultaneously access the X11 display connection. The only problematic method was @@ -95,13 +335,13 @@ methods are only called from the main thread. ---------- 2004/05/17 21:55:55 crs -//depot/project/synergy/cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.cpp Fixed logging of connection to server. Was DEBUG now NOTE. ---------- 2004/05/17 21:55:38 crs -//depot/project/synergy/lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.cpp Fixed ctrl+alt+del emulation on win32 server. It was mapping VK_DELETE to the keypad delete key. This key is not interpreted @@ -109,110 +349,108 @@ on the client as causing ctrl+alt+del. ---------- 2004/05/16 18:04:36 crs -//depot/project/synergy/lib/client/CServerProxy.cpp -//depot/project/synergy/lib/server/CClientProxy1_0.cpp -//depot/project/synergy/lib/synergy/ProtocolTypes.h +lib/client/CServerProxy.cpp +lib/server/CClientProxy1_0.cpp +lib/synergy/ProtocolTypes.h Fixed handling of screen resolution changes. ---------- 2004/05/16 18:03:36 crs -//depot/project/synergy/cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.cpp Changed (win NT) service to depend on the 'Browser' service to ensure correct startup order. ---------- 2004/05/16 18:02:49 crs -//depot/project/synergy/all.dsp -//depot/project/synergy/cmd/exec.dsp -//depot/project/synergy/cmd/launcher/launcher.dsp -//depot/project/synergy/cmd/synergyc/synergyc.dsp -//depot/project/synergy/cmd/synergys/synergys.dsp -//depot/project/synergy/dist/nullsoft/installer.dsp -//depot/project/synergy/dist/nullsoft/synergy.nsi -//depot/project/synergy/lib/arch/arch.dsp -//depot/project/synergy/lib/base/base.dsp -//depot/project/synergy/lib/client/client.dsp -//depot/project/synergy/lib/common/common.dsp -//depot/project/synergy/lib/io/io.dsp -//depot/project/synergy/lib/mt/mt.dsp -//depot/project/synergy/lib/net/net.dsp -//depot/project/synergy/lib/platform/makehook.dsp -//depot/project/synergy/lib/platform/platform.dsp -//depot/project/synergy/lib/platform/synrgyhk.dsp -//depot/project/synergy/lib/server/server.dsp -//depot/project/synergy/lib/synergy/libsynergy.dsp +all.dsp +cmd/exec.dsp +cmd/launcher/launcher.dsp +cmd/synergyc/synergyc.dsp +cmd/synergys/synergys.dsp +dist/nullsoft/installer.dsp +dist/nullsoft/synergy.nsi +lib/arch/arch.dsp +lib/base/base.dsp +lib/client/client.dsp +lib/common/common.dsp +lib/io/io.dsp +lib/mt/mt.dsp +lib/net/net.dsp +lib/platform/makehook.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/server.dsp +lib/synergy/libsynergy.dsp Changed VC++ projects to put release targets in ./build, debug targets in ./debug, and intermediate files under ./gen. ---------- 2004/05/15 19:44:05 crs -//depot/project/synergy/configure.in -//depot/project/synergy/lib/common/Version.h +configure.in +lib/common/Version.h Changed version to 1.1.6. ---------- 2004/05/15 19:43:33 crs -//depot/project/synergy/lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.cpp Avoided duplicate logging of screen size on win32. ---------- 2004/05/15 19:41:46 crs -//depot/project/synergy/Makefile.am -//depot/project/synergy/configure.in -//depot/project/synergy/lib/common/common.h -//depot/project/synergy/lib/platform/COSXClipboard.cpp -//depot/project/synergy/lib/platform/COSXClipboard.h -//depot/project/synergy/lib/platform/COSXClipboardAnyTextConverter.cpp -//depot/project/synergy/lib/platform/COSXClipboardAnyTextConverter.h -//depot/project/synergy/lib/platform/COSXClipboardTextConverter.cpp -//depot/project/synergy/lib/platform/COSXClipboardTextConverter.h -//depot/project/synergy/lib/platform/COSXClipboardUTF16Converter.cpp -//depot/project/synergy/lib/platform/COSXClipboardUTF16Converter.h -//depot/project/synergy/lib/platform/COSXEventQueueBuffer.cpp -//depot/project/synergy/lib/platform/COSXEventQueueBuffer.h -//depot/project/synergy/lib/platform/COSXScreen.cpp -//depot/project/synergy/lib/platform/COSXScreen.h -//depot/project/synergy/lib/platform/Makefile.am -//depot/project/synergy/synergy.xcode/project.pbxproj +Makefile.am +configure.in +lib/common/common.h +lib/platform/COSXClipboard.cpp +lib/platform/COSXClipboard.h +lib/platform/COSXClipboardAnyTextConverter.cpp +lib/platform/COSXClipboardAnyTextConverter.h +lib/platform/COSXClipboardTextConverter.cpp +lib/platform/COSXClipboardTextConverter.h +lib/platform/COSXClipboardUTF16Converter.cpp +lib/platform/COSXClipboardUTF16Converter.h +lib/platform/COSXEventQueueBuffer.cpp +lib/platform/COSXEventQueueBuffer.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/Makefile.am +synergy.xcode/project.pbxproj Added bertrand landry hetu's mac OS X port to date. ---------- 2004/05/12 20:28:00 crs -//depot/project/synergy/lib/platform/CXWindowsScreen.cpp -//depot/project/synergy/lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h Now restoring input focus when entering the screen to the window that had the focus when the screen was left. ---------- 2004/05/12 19:50:58 crs -//depot/project/synergy/lib/arch/CArchNetworkBSD.cpp -//depot/project/synergy/lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkWinsock.cpp Fixed thread reference leak in network code. ---------- 2004/05/12 19:12:28 crs -//depot/project/synergy/configure.in +configure.in Added configure option to enable debug builds. If not enabled then asserts are disabled. ---------- 2004/05/12 18:54:03 crs -//depot/project/synergy/lib/platform/CXWindowsClipboardBMPConverter.cpp +lib/platform/CXWindowsClipboardBMPConverter.cpp Fixed build error in gcc 3.3. ---------- -audrey2% ./nodist/p4tolog -p //depot/project/synergy/ -m 15 | less -audrey2% ./nodist/p4tolog -p //depot/project/synergy/ -m 15 2004/05/26 19:23:32 crs lib/common/MacOSXPrecomp.h lib/common/common.h diff --git a/doc/news.html b/doc/news.html index b7ec37f8..6ef7f69a 100644 --- a/doc/news.html +++ b/doc/news.html @@ -7,6 +7,27 @@

Synergy News

+Aug-05-2004 - Synergy 1.1.8 released + +

+Made following changes: +

+
    +
  • Removed key event capture on X11 (was breaking terminal keyboard input) +
  • Worked around win32 command prompt stealing shift key events +
  • Fixed handling of pause key on win32 +
  • Fixed handling of backslash on win32 internation keyboard mapping +
  • Fixed handling of ctrl and alt keys on NT/2k/XP +
  • Fixed XCode project (removed cross-compile) +
  • Worked around select() bug in OS X +
  • Worked around bug in ifstream on OS X +
  • Fixed handling of modifier keys on OS X synergy server +
  • Fixed handling of space key on OS X synergy server +
  • Fixed handling of key autorepeat on OS X server +
  • Fixed mouse wheel drift on OS X client +
  • Reorganized documentation and converted to HTML +
+ Jun-13-2004 - Synergy 1.1.7 released

From 06987c2070ba5ddeba521fbfef674fffb45da9d0 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 Sep 2004 20:53:54 +0000 Subject: [PATCH 713/807] Changed version to 1.1.9. Changed configure.in to get version number from lib/common/Version.h so it only has to be changed there. --- configure.in | 8 ++++---- lib/common/Version.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/configure.in b/configure.in index 20165442..b180baba 100644 --- a/configure.in +++ b/configure.in @@ -16,10 +16,10 @@ dnl initialize AC_INIT(lib/common/common.h) AC_CONFIG_AUX_DIR(config) -dnl current version -MAJOR_VERSION=1 -MINOR_VERSION=1 -RELEASE_VERSION=8 +dnl current version, extracted from $srcdir/lib/common/Version.h +MAJOR_VERSION=`grep '#.*define VERSION "' $srcdir/lib/common/Version.h | sed -e 's/.*"\([0-9]*\)\.[0-9]*\.[0-9]*".*/\1/'` +MINOR_VERSION=`grep '#.*define VERSION "' $srcdir/lib/common/Version.h | sed -e 's/.*"[0-9]*\.\([0-9]*\)\.[0-9]*".*/\1/'` +RELEASE_VERSION=`grep '#.*define VERSION "' $srcdir/lib/common/Version.h | sed -e 's/.*"[0-9]*\.[0-9]*\.\([0-9]*\)".*/\1/'` dnl initialize automake AM_INIT_AUTOMAKE(synergy, $MAJOR_VERSION.$MINOR_VERSION.$RELEASE_VERSION) diff --git a/lib/common/Version.h b/lib/common/Version.h index 766843ad..bcf037a1 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.8" +# define VERSION "1.1.9" #endif // important strings From c51c748a173e34569178fea133571ee5f1a33bbb Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 Sep 2004 21:04:37 +0000 Subject: [PATCH 714/807] Server now reports configuration file errors as ERROR level log messages rather than DEBUG level. Missing files are still reported as DEBUG level messages since we expect some to be missing. --- cmd/synergys/synergys.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index c5c45e51..b9464fb3 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -815,14 +815,20 @@ loadConfig(const CString& pathname) LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str())); std::ifstream configStream(pathname.c_str()); if (!configStream.is_open()) { - throw XConfigRead("cannot open file"); + // report failure to open configuration as a debug message + // since we try several paths and we expect some to be + // missing. + LOG((CLOG_DEBUG "cannot open configuration \"%s\"", + pathname.c_str())); + return false; } configStream >> *ARG->m_config; LOG((CLOG_DEBUG "configuration read successfully")); return true; } catch (XConfigRead& e) { - LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", + // report error in configuration file + LOG((CLOG_ERR "cannot read configuration \"%s\": %s", pathname.c_str(), e.what())); } return false; From e7ae6831cfd9dfeeaef6b1579633c9dd4b8d2372 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 Sep 2004 21:23:47 +0000 Subject: [PATCH 715/807] Worked around minor gcc 3.3.2 -O3 compiler bug. --- lib/arch/CArchMultithreadPosix.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 26bb0f5a..b3b2eeba 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -828,4 +828,6 @@ CArchMultithreadPosix::threadSignalHandler(void*) break; } } + + return NULL; } From dc499149f4ea4225bffe5f389ee8342a8e73ea6c Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 27 Sep 2004 21:54:49 +0000 Subject: [PATCH 716/807] Fixed compile on gcc 3.4 and later. gcc started doing access checks for class visibility on pointers to member function 'using the qualifying scope of the name itself.' what this means is if method 'prot' is declared protected in class A and B inherits from A then a method in B cannot use &A::prot but can use &B::prot. Synergy now does this in the one place it had not. --- lib/platform/CXWindowsScreen.cpp | 4 ++-- lib/synergy/IPlatformScreen.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index e176ca37..d085a441 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -169,8 +169,8 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) : // install event handlers EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(), - new TMethodEventJob(this, - &IPlatformScreen::handleSystemEvent)); + new TMethodEventJob(this, + &CXWindowsScreen::handleSystemEvent)); // install the platform event queue EVENTQUEUE->adoptBuffer(new CXWindowsEventQueueBuffer(m_display, m_window)); diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index 8a5da677..47f5d36a 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -179,8 +179,8 @@ protected: \code EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(), - new TMethodEventJob(this, - &IPlatformScreen::handleSystemEvent)); + new TMethodEventJob(this, + &CXXXPlatformScreen::handleSystemEvent)); \endcode It should remove the handler in its d'tor. Override the \c handleSystemEvent() method to process system events. From 3c3c31008e4e56b18303ce3ac13eeaa5923387d9 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 28 Sep 2004 22:19:11 +0000 Subject: [PATCH 717/807] Added half-duplex option for scroll lock key. --- cmd/launcher/launcher.cpp | 12 +++++++++++- cmd/launcher/launcher.rc | 14 ++++++++------ cmd/launcher/resource.h | 1 + doc/configuration.html | 8 ++++++++ doc/faq.html | 25 +++++++++++++------------ lib/server/CConfig.cpp | 8 ++++++++ lib/synergy/CScreen.cpp | 10 ++++++++++ 7 files changed, 59 insertions(+), 19 deletions(-) diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 1b89e9fb..66019f01 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -829,6 +829,10 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) index->second != 0)); child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); index = info->m_options.find(kOptionHalfDuplexNumLock); + setItemChecked(child, (index != info->m_options.end() && + index->second != 0)); + child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK); + index = info->m_options.find(kOptionHalfDuplexScrollLock); setItemChecked(child, (index != info->m_options.end() && index->second != 0)); @@ -940,9 +944,15 @@ addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) else { info->m_options.erase(kOptionHalfDuplexNumLock); } + child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK); + if (isItemChecked(child)) { + info->m_options[kOptionHalfDuplexScrollLock] = 1; + } + else { + info->m_options.erase(kOptionHalfDuplexScrollLock); + } // save modifier options - child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); for (UInt32 i = 0; i < sizeof(s_modifiers) / sizeof(s_modifiers[0]); ++i) { child = getItem(hwnd, s_modifiers[i].m_ctrlID); diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index d14e60f5..71742019 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -116,13 +116,15 @@ BEGIN LTEXT "&Aliases:",IDC_STATIC,7,25,25,8 EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,40,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN - GROUPBOX "Options",IDC_STATIC,7,72,178,64 - LTEXT "If your Caps Lock or Num Lock keys behave strangely on this client screen then try turning the half-duplex options on and reconnect the client.", + GROUPBOX "Half-Duplex",IDC_STATIC,7,72,178,64 + LTEXT "If your Caps, Num, or Scroll Lock keys behave strangely on this screen then turn on the half-duplex options and restart the server.", IDC_STATIC,13,82,165,25 - CONTROL "Half-duplex &Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,13,110,165,10 - CONTROL "Half-duplex &Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,13,122,165,10 + CONTROL "&Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,110,81,10 + CONTROL "&Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,122,81,10 + CONTROL "Sc&roll Lock",IDC_ADD_HD_SCROLL_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,98,110,81,10 GROUPBOX "Modifiers",IDC_STATIC,7,139,178,65 LTEXT "Shift",IDC_STATIC,13,155,15,8 COMBOBOX IDC_ADD_MOD_SHIFT,37,152,48,60,CBS_DROPDOWNLIST | diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index e6d90dac..b8c746ff 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -87,6 +87,7 @@ #define IDC_ADD_HD_NUM_CHECK 1038 #define IDC_ADVANCED_NAME_EDIT 1038 #define IDC_ADVANCED_PORT_EDIT 1039 +#define IDC_ADD_HD_SCROLL_CHECK 1039 #define IDC_MAIN_DEBUG 1040 #define IDC_GLOBAL_DELAY_CHECK 1041 #define IDC_GLOBAL_DELAY_TIME 1042 diff --git a/doc/configuration.html b/doc/configuration.html index 494b8718..2cee328a 100644 --- a/doc/configuration.html +++ b/doc/configuration.html @@ -99,6 +99,14 @@ A screen can have the following options: This is identical to halfDuplexCapsLock except it applies to the Num Lock key.

+
  • halfDuplexScrollLock = {true|false} +

    + This is identical to halfDuplexCapsLock + except it applies to the Scroll Lock key. Note that synergy uses + Scroll Lock to keep the cursor on the current screen. That is, + when Scroll Lock is toggled on, the cursor is locked to the screen + that it's currently on. Use it to prevent accidental switching. +

  • xtestIsXineramaUnaware = {true|false}

    This option works around a bug in the XTest extension diff --git a/doc/faq.html b/doc/faq.html index 86f2d539..1b355594 100644 --- a/doc/faq.html +++ b/doc/faq.html @@ -15,7 +15,7 @@

  • What does "Cannot initialize hook library" mean?
  • What security/encryption does synergy provide?
  • What should I call my screens in the configuration? -
  • Why do my Caps-Lock and Num-Lock keys act funny? +
  • Why do my Caps-Lock, Num-Lock, Scroll-Lock keys act funny?
  • Can synergy share the display in addition to the mouse and keyboard?
  • Can synergy do drag and drop between computers?
  • Do AltGr or Mode-Switch or ISO_Level3_Shift work? @@ -125,28 +125,29 @@ then use xyz.local as the screen name.

    -
  • Why do my Caps-Lock and Num-Lock keys act funny? +
  • Why do my Caps-Lock, Num-Lock, Scroll-Lock keys act funny?

    - Some systems treat the Caps-Lock and Num-Lock keys differently - than all the others. Whereas most keys report going down when - physically pressed and going up when physically released, on - these systems the Caps-Lock and Num-Lock keys report going down - when being activated and going up when being deactivated. That - is, when you press and release, say, Caps-Lock to activate it, it - only reports going down, and when you press and release to - deactivate it, it only reports going up. This confuses synergy. + Some systems treat the Caps-Lock, Num-Lock, and Scroll-Lock keys + differently than all the others. Whereas most keys report going down + when physically pressed and going up when physically released, on + these systems the keys report going down when being activated and + going up when being deactivated. That is, when you press and release, + say, Caps-Lock to activate it, it only reports going down, and when + you press and release to deactivate it, it only reports going up. + This confuses synergy.

    You can solve the problem by changing your configuration file. In the screens section, following each screen that has the - problem, add either or both of these lines as appropriate: + problem, add any or all of these lines as appropriate:

             halfDuplexCapsLock = true
             halfDuplexNumLock = true
    +        halfDuplexScrollLock = true
     

    - Then restart synergy on the server. + Then restart synergy on the server or reload the configuration.

  • Can synergy share the display in addition to the mouse and keyboard? diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 03e0eb2d..1598854f 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -599,6 +599,9 @@ CConfig::getOptionName(OptionID id) if (id == kOptionHalfDuplexNumLock) { return "halfDuplexNumLock"; } + if (id == kOptionHalfDuplexScrollLock) { + return "halfDuplexScrollLock"; + } if (id == kOptionModifierMapForShift) { return "shift"; } @@ -640,6 +643,7 @@ CConfig::getOptionValue(OptionID id, OptionValue value) { if (id == kOptionHalfDuplexCapsLock || id == kOptionHalfDuplexNumLock || + id == kOptionHalfDuplexScrollLock || id == kOptionScreenSaverSync || id == kOptionXTestXineramaUnaware || id == kOptionRelativeMouseMoves) { @@ -850,6 +854,10 @@ CConfig::readSectionScreens(std::istream& s) addOption(screen, kOptionHalfDuplexNumLock, parseBoolean(value)); } + else if (name == "halfDuplexScrollLock") { + addOption(screen, kOptionHalfDuplexScrollLock, + parseBoolean(value)); + } else if (name == "shift") { addOption(screen, kOptionModifierMapForShift, parseModifierKey(value)); diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index 67140498..0323d8a8 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -285,6 +285,16 @@ CScreen::setOptions(const COptionsList& options) m_screen->setHalfDuplexMask(m_halfDuplex); LOG((CLOG_DEBUG1 "half-duplex num-lock %s", ((m_halfDuplex & KeyModifierNumLock) != 0) ? "on" : "off")); } + else if (options[i] == kOptionHalfDuplexScrollLock) { + if (options[i + 1] != 0) { + m_halfDuplex |= KeyModifierScrollLock; + } + else { + m_halfDuplex &= ~KeyModifierScrollLock; + } + m_screen->setHalfDuplexMask(m_halfDuplex); + LOG((CLOG_DEBUG1 "half-duplex scroll-lock %s", ((m_halfDuplex & KeyModifierScrollLock) != 0) ? "on" : "off")); + } } // update screen saver synchronization From f8e133dc291f9475aa851f30b25703aa1ea68993 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 28 Sep 2004 22:19:24 +0000 Subject: [PATCH 718/807] Fixed error in win32 installer packaging. --- dist/nullsoft/installer.mak | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/nullsoft/installer.mak b/dist/nullsoft/installer.mak index 8ee184a0..4f4b69b5 100755 --- a/dist/nullsoft/installer.mak +++ b/dist/nullsoft/installer.mak @@ -4,6 +4,7 @@ DEPTH=..\.. NSIS="D:\Program Files\NSIS\makensis" DOCS = \ + COPYING \ ChangeLog \ $(NULL) From 376bba970b7bdb9ee93733958aaf03ef0028c71c Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 28 Sep 2004 22:25:35 +0000 Subject: [PATCH 719/807] Now accepting screen names that end in a dot since many OS X users have misconfigured their systems to have hostnames that end in a dot. --- lib/server/CConfig.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 1598854f..dddd2586 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -315,10 +315,22 @@ CConfig::isValidScreenName(const CString& name) const // name ::= [_A-Za-z0-9] | [_A-Za-z0-9][-_A-Za-z0-9]*[_A-Za-z0-9] // domain ::= . name // validname ::= name domain* + // we also accept names ending in . because many OS X users have + // so misconfigured their systems. + + // empty name is invalid + if (name.empty()) { + return false; + } // check each dot separated part CString::size_type b = 0; for (;;) { + // accept trailing . + if (b == name.size()) { + break; + } + // find end of part CString::size_type e = name.find('.', b); if (e == CString::npos) { From 07e2a3e5117251d37ddd5aa376902d4fab5eedb9 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 29 Sep 2004 21:59:26 +0000 Subject: [PATCH 720/807] Removed recursive mutexes. Simplified stream filters as a side effect. Removed -D_BSD_SOURCE and -D_XOPEN_SOURCE=500 from compile since they're not longer necessary. --- cmd/synergyc/CClientTaskBarReceiver.cpp | 5 +- cmd/synergyc/CClientTaskBarReceiver.h | 2 - cmd/synergys/CServerTaskBarReceiver.cpp | 5 +- cmd/synergys/CServerTaskBarReceiver.h | 2 - configure.in | 10 -- lib/arch/CArchMultithreadPosix.cpp | 2 - lib/base/CEventQueue.cpp | 123 +++++++++--------------- lib/base/CEventQueue.h | 23 +---- lib/base/IEventQueue.h | 45 ++------- lib/io/CStreamFilter.cpp | 36 ++++--- lib/io/CStreamFilter.h | 16 ++- lib/io/IStream.h | 20 +--- lib/net/CTCPSocket.cpp | 71 +++++--------- lib/net/CTCPSocket.h | 6 +- lib/net/IDataSocket.h | 2 - lib/server/CClientListener.cpp | 7 +- lib/synergy/CPacketStreamFilter.cpp | 63 +++--------- lib/synergy/CPacketStreamFilter.h | 12 +-- 18 files changed, 148 insertions(+), 302 deletions(-) diff --git a/cmd/synergyc/CClientTaskBarReceiver.cpp b/cmd/synergyc/CClientTaskBarReceiver.cpp index e1cbcfac..ddd8ec5a 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CClientTaskBarReceiver.cpp @@ -38,7 +38,6 @@ CClientTaskBarReceiver::updateStatus(CClient* client, const CString& errorMsg) { { // update our status - CLock lock(&m_mutex); m_errorMessage = errorMsg; if (client == NULL) { if (m_errorMessage.empty()) { @@ -95,13 +94,13 @@ CClientTaskBarReceiver::onStatusChanged(CClient*) void CClientTaskBarReceiver::lock() const { - m_mutex.lock(); + // do nothing } void CClientTaskBarReceiver::unlock() const { - m_mutex.unlock(); + // do nothing } std::string diff --git a/cmd/synergyc/CClientTaskBarReceiver.h b/cmd/synergyc/CClientTaskBarReceiver.h index ab9c371c..a9fe50c2 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.h +++ b/cmd/synergyc/CClientTaskBarReceiver.h @@ -15,7 +15,6 @@ #ifndef CCLIENTTASKBARRECEIVER_H #define CCLIENTTASKBARRECEIVER_H -#include "CMutex.h" #include "CString.h" #include "IArchTaskBarReceiver.h" @@ -76,7 +75,6 @@ protected: virtual void onStatusChanged(CClient* client); private: - CMutex m_mutex; EState m_state; CString m_errorMessage; }; diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp index b3858827..ee04d79e 100644 --- a/cmd/synergys/CServerTaskBarReceiver.cpp +++ b/cmd/synergys/CServerTaskBarReceiver.cpp @@ -38,7 +38,6 @@ CServerTaskBarReceiver::updateStatus(CServer* server, const CString& errorMsg) { { // update our status - CLock lock(&m_mutex); m_errorMessage = errorMsg; if (server == NULL) { if (m_errorMessage.empty()) { @@ -100,13 +99,13 @@ CServerTaskBarReceiver::onStatusChanged(CServer*) void CServerTaskBarReceiver::lock() const { - m_mutex.lock(); + // do nothing } void CServerTaskBarReceiver::unlock() const { - m_mutex.unlock(); + // do nothing } std::string diff --git a/cmd/synergys/CServerTaskBarReceiver.h b/cmd/synergys/CServerTaskBarReceiver.h index 372d73f2..d6ec8571 100644 --- a/cmd/synergys/CServerTaskBarReceiver.h +++ b/cmd/synergys/CServerTaskBarReceiver.h @@ -15,7 +15,6 @@ #ifndef CSERVERTASKBARRECEIVER_H #define CSERVERTASKBARRECEIVER_H -#include "CMutex.h" #include "CString.h" #include "IArchTaskBarReceiver.h" #include "stdvector.h" @@ -81,7 +80,6 @@ protected: virtual void onStatusChanged(CServer* server); private: - CMutex m_mutex; EState m_state; CString m_errorMessage; CClients m_clients; diff --git a/configure.in b/configure.in index b180baba..a066a4d1 100644 --- a/configure.in +++ b/configure.in @@ -74,16 +74,6 @@ fi dnl check compiler ACX_CHECK_CXX -dnl different platforms have somewhat incompatible requirements for -dnl BSD and Posix macros. -case $host in -*-*-openbsd* | *-*-freebsd*) - ;; -*) - CXXFLAGS="$CXXFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500" - ;; -esac - dnl checks for libraries if test x"$acx_host_arch" = xUNIX; then ACX_PTHREAD(,AC_MSG_ERROR(You must have pthreads to compile synergy)) diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index b3b2eeba..64baf6af 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -301,8 +301,6 @@ CArchMultithreadPosix::newMutex() pthread_mutexattr_t attr; int status = pthread_mutexattr_init(&attr); assert(status == 0); - status = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - assert(status == 0); CArchMutexImpl* mutex = new CArchMutexImpl; status = pthread_mutex_init(&mutex->m_mutex, &attr); assert(status == 0); diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp index 8e309ce3..16a0d52b 100644 --- a/lib/base/CEventQueue.cpp +++ b/lib/base/CEventQueue.cpp @@ -183,6 +183,9 @@ CEventQueue::dispatchEvent(const CEvent& event) { void* target = event.getTarget(); IEventJob* job = getHandler(event.getType(), target); + if (job == NULL) { + job = getHandler(CEvent::kUnknown, target); + } if (job != NULL) { job->run(event); return true; @@ -273,65 +276,56 @@ CEventQueue::deleteTimer(CEventQueueTimer* timer) m_buffer->deleteTimer(timer); } -void -CEventQueue::adoptHandler(void* target, IEventJob* handler) -{ - doAdoptHandler(CEvent::kUnknown, target, handler); -} - void CEventQueue::adoptHandler(CEvent::Type type, void* target, IEventJob* handler) { - assert(type != CEvent::kUnknown); - doAdoptHandler(type, target, handler); -} - -IEventJob* -CEventQueue::orphanHandler(void* target) -{ - return doOrphanHandler(CEvent::kUnknown, target); -} - -IEventJob* -CEventQueue::orphanHandler(CEvent::Type type, void* target) -{ - assert(type != CEvent::kUnknown); - return doOrphanHandler(type, target); -} - -void -CEventQueue::removeHandler(void* target) -{ - delete orphanHandler(target); + CArchMutexLock lock(m_mutex); + IEventJob*& job = m_handlers[target][type]; + delete job; + job = handler; } void CEventQueue::removeHandler(CEvent::Type type, void* target) { - delete orphanHandler(type, target); + IEventJob* handler = NULL; + { + CArchMutexLock lock(m_mutex); + CHandlerTable::iterator index = m_handlers.find(target); + if (index != m_handlers.end()) { + CTypeHandlerTable& typeHandlers = index->second; + CTypeHandlerTable::iterator index2 = typeHandlers.find(type); + if (index2 != typeHandlers.end()) { + handler = index2->second; + typeHandlers.erase(index2); + } + } + } + delete handler; } void -CEventQueue::doAdoptHandler(CEvent::Type type, void* target, IEventJob* handler) +CEventQueue::removeHandlers(void* target) { - CArchMutexLock lock(m_mutex); - IEventJob*& job = m_handlers[CTypeTarget(type, target)]; - delete job; - job = handler; -} - -IEventJob* -CEventQueue::doOrphanHandler(CEvent::Type type, void* target) -{ - CArchMutexLock lock(m_mutex); - CHandlerTable::iterator index = m_handlers.find(CTypeTarget(type, target)); - if (index != m_handlers.end()) { - IEventJob* handler = index->second; - m_handlers.erase(index); - return handler; + std::vector handlers; + { + CArchMutexLock lock(m_mutex); + CHandlerTable::iterator index = m_handlers.find(target); + if (index != m_handlers.end()) { + // copy to handlers array and clear table for target + CTypeHandlerTable& typeHandlers = index->second; + for (CTypeHandlerTable::iterator index2 = typeHandlers.begin(); + index2 != typeHandlers.end(); ++index2) { + handlers.push_back(index2->second); + } + typeHandlers.clear(); + } } - else { - return NULL; + + // delete handlers + for (std::vector::iterator index = handlers.begin(); + index != handlers.end(); ++index) { + delete *index; } } @@ -345,14 +339,13 @@ IEventJob* CEventQueue::getHandler(CEvent::Type type, void* target) const { CArchMutexLock lock(m_mutex); - CHandlerTable::const_iterator index = - m_handlers.find(CTypeTarget(type, target)); + CHandlerTable::const_iterator index = m_handlers.find(target); if (index != m_handlers.end()) { - return index->second; - } - index = m_handlers.find(CTypeTarget(CEvent::kUnknown, target)); - if (index != m_handlers.end()) { - return index->second; + const CTypeHandlerTable& typeHandlers = index->second; + CTypeHandlerTable::const_iterator index2 = typeHandlers.find(type); + if (index2 != typeHandlers.end()) { + return index2->second; + } } return NULL; } @@ -454,30 +447,6 @@ CEventQueue::getNextTimerTimeout() const } -// -// CEventQueue::CTypeTarget -// - -CEventQueue::CTypeTarget::CTypeTarget(CEvent::Type type, void* target) : - m_type(type), - m_target(target) -{ - // do nothing -} - -CEventQueue::CTypeTarget::~CTypeTarget() -{ - // do nothing -} - -bool -CEventQueue::CTypeTarget::operator<(const CTypeTarget& tt) const -{ - return (m_type < tt.m_type || - (m_type == tt.m_type && m_target < tt.m_target)); -} - - // // CEventQueue::CTimer // diff --git a/lib/base/CEventQueue.h b/lib/base/CEventQueue.h index b61eb1e3..a63c7b16 100644 --- a/lib/base/CEventQueue.h +++ b/lib/base/CEventQueue.h @@ -43,13 +43,10 @@ public: virtual CEventQueueTimer* newOneShotTimer(double duration, void* target); virtual void deleteTimer(CEventQueueTimer*); - virtual void adoptHandler(void* target, IEventJob* dispatcher); virtual void adoptHandler(CEvent::Type type, void* target, IEventJob* handler); - virtual IEventJob* orphanHandler(void* target); - virtual IEventJob* orphanHandler(CEvent::Type type, void* target); - virtual void removeHandler(void* target); virtual void removeHandler(CEvent::Type type, void* target); + virtual void removeHandlers(void* target); virtual CEvent::Type registerType(const char* name); virtual CEvent::Type @@ -59,27 +56,12 @@ public: virtual const char* getTypeName(CEvent::Type type); private: - void doAdoptHandler(CEvent::Type type, - void* target, IEventJob* handler); - IEventJob* doOrphanHandler(CEvent::Type type, void* target); - UInt32 saveEvent(const CEvent& event); CEvent removeEvent(UInt32 eventID); bool hasTimerExpired(CEvent& event); double getNextTimerTimeout() const; private: - class CTypeTarget { - public: - CTypeTarget(CEvent::Type type, void* target); - ~CTypeTarget(); - - bool operator<(const CTypeTarget&) const; - - private: - CEvent::Type m_type; - void* m_target; - }; class CTimer { public: CTimer(CEventQueueTimer*, double timeout, double initialTime, @@ -111,8 +93,9 @@ private: typedef CPriorityQueue CTimerQueue; typedef std::map CEventTable; typedef std::vector CEventIDList; - typedef std::map CHandlerTable; typedef std::map CTypeMap; + typedef std::map CTypeHandlerTable; + typedef std::map CHandlerTable; CArchMutex m_mutex; diff --git a/lib/base/IEventQueue.h b/lib/base/IEventQueue.h index 765d4143..6f48f25c 100644 --- a/lib/base/IEventQueue.h +++ b/lib/base/IEventQueue.h @@ -114,47 +114,17 @@ public: */ virtual void deleteTimer(CEventQueueTimer*) = 0; - //! Register an event handler - /*! - Registers an event handler for \p target. The \p handler is - adopted. Any existing handler for the target is deleted. - \c dispatchEvent() will invoke \p handler for any event for - \p target that doesn't have a type specific handler. - */ - virtual void adoptHandler(void* target, IEventJob* handler) = 0; - //! Register an event handler for an event type /*! Registers an event handler for \p type and \p target. The \p handler is adopted. Any existing handler for the type,target pair is deleted. \c dispatchEvent() will invoke \p handler for any event for \p target - of type \p type. + of type \p type. If no such handler exists it will use the handler + for \p target and type \p kUnknown if it exists. */ virtual void adoptHandler(CEvent::Type type, void* target, IEventJob* handler) = 0; - //! Unregister an event handler - /*! - Unregisters an event handler for \p target and returns it. - Returns NULL if there was no such handler. The client becomes - responsible for deleting the returned handler. - */ - virtual IEventJob* orphanHandler(void* target) = 0; - - //! Unregister an event handler for an event type - /*! - Unregisters an event handler for the \p type, \p target pair and - returns it. Returns NULL if there was no such handler. The - client becomes responsible for deleting the returned handler. - */ - virtual IEventJob* orphanHandler(CEvent::Type type, void* target) = 0; - - //! Unregister an event handler - /*! - Unregisters an event handler for \p target and deletes it. - */ - virtual void removeHandler(void* target) = 0; - //! Unregister an event handler for an event type /*! Unregisters an event handler for the \p type, \p target pair and @@ -162,6 +132,12 @@ public: */ virtual void removeHandler(CEvent::Type type, void* target) = 0; + //! Unregister all event handlers for an event target + /*! + Unregisters all event handlers for the \p target and deletes them. + */ + virtual void removeHandlers(void* target) = 0; + //! Creates a new event type /*! Returns a unique event type id. @@ -192,9 +168,8 @@ public: //! Get an event handler /*! - Finds and returns the event handler for the \p type, \p target pair. - If there is no such handler, returns the handler for \p target. If - that doesn't exist, returns NULL. + Finds and returns the event handler for the \p type, \p target pair + if it exists, otherwise it returns NULL. */ virtual IEventJob* getHandler(CEvent::Type type, void* target) const = 0; diff --git a/lib/io/CStreamFilter.cpp b/lib/io/CStreamFilter.cpp index d5a2a350..312b0dfd 100644 --- a/lib/io/CStreamFilter.cpp +++ b/lib/io/CStreamFilter.cpp @@ -13,6 +13,8 @@ */ #include "CStreamFilter.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" // // CStreamFilter @@ -22,11 +24,16 @@ CStreamFilter::CStreamFilter(IStream* stream, bool adoptStream) : m_stream(stream), m_adopted(adoptStream) { - // do nothing + // replace handlers for m_stream + EVENTQUEUE->removeHandlers(m_stream->getEventTarget()); + EVENTQUEUE->adoptHandler(CEvent::kUnknown, m_stream->getEventTarget(), + new TMethodEventJob(this, + &CStreamFilter::handleUpstreamEvent)); } CStreamFilter::~CStreamFilter() { + EVENTQUEUE->removeHandler(CEvent::kUnknown, m_stream->getEventTarget()); if (m_adopted) { delete m_stream; } @@ -68,16 +75,10 @@ CStreamFilter::shutdownOutput() getStream()->shutdownOutput(); } -void -CStreamFilter::setEventFilter(IEventJob* filter) -{ - getStream()->setEventFilter(filter); -} - void* CStreamFilter::getEventTarget() const { - return getStream()->getEventTarget(); + return const_cast(reinterpret_cast(this)); } bool @@ -92,14 +93,21 @@ CStreamFilter::getSize() const return getStream()->getSize(); } -IEventJob* -CStreamFilter::getEventFilter() const -{ - return getStream()->getEventFilter(); -} - IStream* CStreamFilter::getStream() const { return m_stream; } + +void +CStreamFilter::filterEvent(const CEvent& event) +{ + EVENTQUEUE->dispatchEvent(CEvent(event.getType(), + getEventTarget(), event.getData())); +} + +void +CStreamFilter::handleUpstreamEvent(const CEvent& event, void*) +{ + filterEvent(event); +} diff --git a/lib/io/CStreamFilter.h b/lib/io/CStreamFilter.h index 5706cf4a..4dc87094 100644 --- a/lib/io/CStreamFilter.h +++ b/lib/io/CStreamFilter.h @@ -33,19 +33,17 @@ public: ~CStreamFilter(); // IStream overrides - // These all just forward to the underlying stream. Override as - // necessary. + // These all just forward to the underlying stream except getEventTarget. + // Override as necessary. getEventTarget returns a pointer to this. virtual void close(); virtual UInt32 read(void* buffer, UInt32 n); virtual void write(const void* buffer, UInt32 n); virtual void flush(); virtual void shutdownInput(); virtual void shutdownOutput(); - virtual void setEventFilter(IEventJob* filter); virtual void* getEventTarget() const; virtual bool isReady() const; virtual UInt32 getSize() const; - virtual IEventJob* getEventFilter() const; protected: //! Get the stream @@ -54,6 +52,16 @@ protected: */ IStream* getStream() const; + //! Handle events from source stream + /*! + Does the event filtering. The default simply dispatches an event + identical except using this object as the event target. + */ + virtual void filterEvent(const CEvent&); + +private: + void handleUpstreamEvent(const CEvent&, void*); + private: IStream* m_stream; bool m_adopted; diff --git a/lib/io/IStream.h b/lib/io/IStream.h index 37d8514f..cb5b54c9 100644 --- a/lib/io/IStream.h +++ b/lib/io/IStream.h @@ -18,8 +18,6 @@ #include "IInterface.h" #include "CEvent.h" -class IEventJob; - //! Bidirectional stream interface /*! Defines the interface for all streams. @@ -77,22 +75,14 @@ public: */ virtual void shutdownOutput() = 0; - //! Set the event filter - /*! - If not NULL, the \p filter is passed any event that would've been - added to the queue. The filter can discard the event, modify it - and add it to the queue, and add other events. The default filter - is NULL. The caller retains ownership of the filter. - */ - virtual void setEventFilter(IEventJob* filter) = 0; - //@} //! @name accessors //@{ //! Get event target /*! - Returns the event target for events generated by this stream. + Returns the event target for events generated by this stream. It + should be the source stream in a chain of stream filters. */ virtual void* getEventTarget() const = 0; @@ -113,12 +103,6 @@ public: */ virtual UInt32 getSize() const = 0; - //! Get the event filter - /*! - Returns the current event filter. - */ - virtual IEventJob* getEventFilter() const = 0; - //! Get input ready event type /*! Returns the input ready event type. A stream sends this event diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 61ff3b8c..c6c87789 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -31,8 +31,7 @@ CTCPSocket::CTCPSocket() : m_mutex(), - m_flushed(&m_mutex, true), - m_eventFilter(NULL) + m_flushed(&m_mutex, true) { try { m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM); @@ -47,8 +46,7 @@ CTCPSocket::CTCPSocket() : CTCPSocket::CTCPSocket(CArchSocket socket) : m_mutex(), m_socket(socket), - m_flushed(&m_mutex, true), - m_eventFilter(NULL) + m_flushed(&m_mutex, true) { assert(m_socket != NULL); @@ -92,7 +90,7 @@ CTCPSocket::close() // clear buffers and enter disconnected state if (m_connected) { - sendSocketEvent(getDisconnectedEvent()); + sendEvent(getDisconnectedEvent()); } onDisconnected(); @@ -132,7 +130,7 @@ CTCPSocket::read(void* buffer, UInt32 n) // if no more data and we cannot read or write then send disconnected if (n > 0 && m_inputBuffer.getSize() == 0 && !m_readable && !m_writable) { - sendSocketEvent(getDisconnectedEvent()); + sendEvent(getDisconnectedEvent()); m_connected = false; } @@ -148,7 +146,7 @@ CTCPSocket::write(const void* buffer, UInt32 n) // must not have shutdown output if (!m_writable) { - sendStreamEvent(getOutputErrorEvent()); + sendEvent(getOutputErrorEvent()); return; } @@ -197,7 +195,7 @@ CTCPSocket::shutdownInput() // shutdown buffer for reading if (m_readable) { - sendStreamEvent(getInputShutdownEvent()); + sendEvent(getInputShutdownEvent()); onInputShutdown(); useNewJob = true; } @@ -224,7 +222,7 @@ CTCPSocket::shutdownOutput() // shutdown buffer for writing if (m_writable) { - sendStreamEvent(getOutputShutdownEvent()); + sendEvent(getOutputShutdownEvent()); onOutputShutdown(); useNewJob = true; } @@ -234,13 +232,6 @@ CTCPSocket::shutdownOutput() } } -void -CTCPSocket::setEventFilter(IEventJob* filter) -{ - CLock lock(&m_mutex); - m_eventFilter = filter; -} - bool CTCPSocket::isReady() const { @@ -255,13 +246,6 @@ CTCPSocket::getSize() const return m_inputBuffer.getSize(); } -IEventJob* -CTCPSocket::getEventFilter() const -{ - CLock lock(&m_mutex); - return m_eventFilter; -} - void CTCPSocket::connect(const CNetworkAddress& addr) { @@ -276,7 +260,7 @@ CTCPSocket::connect(const CNetworkAddress& addr) try { if (ARCH->connectSocket(m_socket, addr.getAddress())) { - sendSocketEvent(getConnectedEvent()); + sendEvent(getConnectedEvent()); onConnected(); } else { @@ -357,12 +341,6 @@ CTCPSocket::newJob() } } -void -CTCPSocket::sendSocketEvent(CEvent::Type type) -{ - EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL)); -} - void CTCPSocket::sendConnectionFailedEvent(const char* msg) { @@ -374,14 +352,9 @@ CTCPSocket::sendConnectionFailedEvent(const char* msg) } void -CTCPSocket::sendStreamEvent(CEvent::Type type) +CTCPSocket::sendEvent(CEvent::Type type) { - if (m_eventFilter != NULL) { - m_eventFilter->run(CEvent(type, getEventTarget(), NULL)); - } - else { - EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL)); - } + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL)); } void @@ -455,7 +428,7 @@ CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job, } if (write) { - sendSocketEvent(getConnectedEvent()); + sendEvent(getConnectedEvent()); onConnected(); return newJob(); } @@ -470,7 +443,7 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, CLock lock(&m_mutex); if (error) { - sendSocketEvent(getDisconnectedEvent()); + sendEvent(getDisconnectedEvent()); onDisconnected(); return newJob(); } @@ -488,7 +461,7 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, if (n > 0) { m_outputBuffer.pop(n); if (m_outputBuffer.getSize() == 0) { - sendStreamEvent(getOutputFlushedEvent()); + sendEvent(getOutputFlushedEvent()); m_flushed = true; m_flushed.broadcast(); needNewJob = true; @@ -499,9 +472,9 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, // remote read end of stream hungup. our output side // has therefore shutdown. onOutputShutdown(); - sendStreamEvent(getOutputShutdownEvent()); + sendEvent(getOutputShutdownEvent()); if (!m_readable && m_inputBuffer.getSize() == 0) { - sendSocketEvent(getDisconnectedEvent()); + sendEvent(getDisconnectedEvent()); m_connected = false; } needNewJob = true; @@ -509,14 +482,14 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, catch (XArchNetworkDisconnected&) { // stream hungup onDisconnected(); - sendSocketEvent(getDisconnectedEvent()); + sendEvent(getDisconnectedEvent()); needNewJob = true; } catch (XArchNetwork&) { // other write error onDisconnected(); - sendStreamEvent(getOutputErrorEvent()); - sendSocketEvent(getDisconnectedEvent()); + sendEvent(getOutputErrorEvent()); + sendEvent(getDisconnectedEvent()); needNewJob = true; } } @@ -536,16 +509,16 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, // send input ready if input buffer was empty if (wasEmpty) { - sendStreamEvent(getInputReadyEvent()); + sendEvent(getInputReadyEvent()); } } else { // remote write end of stream hungup. our input side // has therefore shutdown but don't flush our buffer // since there's still data to be read. - sendStreamEvent(getInputShutdownEvent()); + sendEvent(getInputShutdownEvent()); if (!m_writable && m_inputBuffer.getSize() == 0) { - sendSocketEvent(getDisconnectedEvent()); + sendEvent(getDisconnectedEvent()); m_connected = false; } m_readable = false; @@ -554,7 +527,7 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, } catch (XArchNetworkDisconnected&) { // stream hungup - sendSocketEvent(getDisconnectedEvent()); + sendEvent(getDisconnectedEvent()); onDisconnected(); needNewJob = true; } diff --git a/lib/net/CTCPSocket.h b/lib/net/CTCPSocket.h index 8b60d5b6..aa1df8c1 100644 --- a/lib/net/CTCPSocket.h +++ b/lib/net/CTCPSocket.h @@ -46,10 +46,8 @@ public: virtual void flush(); virtual void shutdownInput(); virtual void shutdownOutput(); - virtual void setEventFilter(IEventJob* filter); virtual bool isReady() const; virtual UInt32 getSize() const; - virtual IEventJob* getEventFilter() const; // IDataSocket overrides virtual void connect(const CNetworkAddress&); @@ -59,9 +57,8 @@ private: void setJob(ISocketMultiplexerJob*); ISocketMultiplexerJob* newJob(); - void sendSocketEvent(CEvent::Type); void sendConnectionFailedEvent(const char*); - void sendStreamEvent(CEvent::Type); + void sendEvent(CEvent::Type); void onConnected(); void onInputShutdown(); @@ -84,7 +81,6 @@ private: bool m_connected; bool m_readable; bool m_writable; - IEventJob* m_eventFilter; }; #endif diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h index 85b85480..d760d4ab 100644 --- a/lib/net/IDataSocket.h +++ b/lib/net/IDataSocket.h @@ -79,10 +79,8 @@ public: virtual void flush() = 0; virtual void shutdownInput() = 0; virtual void shutdownOutput() = 0; - virtual void setEventFilter(IEventJob* filter) = 0; virtual bool isReady() const = 0; virtual UInt32 getSize() const = 0; - virtual IEventJob* getEventFilter() const = 0; private: static CEvent::Type s_connectedEvent; diff --git a/lib/server/CClientListener.cpp b/lib/server/CClientListener.cpp index 45f3a713..803f44a0 100644 --- a/lib/server/CClientListener.cpp +++ b/lib/server/CClientListener.cpp @@ -75,7 +75,12 @@ CClientListener::~CClientListener() for (CNewClients::iterator index = m_newClients.begin(); index != m_newClients.end(); ++index) { CClientProxyUnknown* client = *index; - EVENTQUEUE->removeHandler(client); + EVENTQUEUE->removeHandler( + CClientProxyUnknown::getSuccessEvent(), client); + EVENTQUEUE->removeHandler( + CClientProxyUnknown::getFailureEvent(), client); + EVENTQUEUE->removeHandler( + CClientProxy::getDisconnectedEvent(), client); delete client; } diff --git a/lib/synergy/CPacketStreamFilter.cpp b/lib/synergy/CPacketStreamFilter.cpp index d59d2fa1..4aad9c02 100644 --- a/lib/synergy/CPacketStreamFilter.cpp +++ b/lib/synergy/CPacketStreamFilter.cpp @@ -24,19 +24,14 @@ CPacketStreamFilter::CPacketStreamFilter(IStream* stream, bool adoptStream) : CStreamFilter(stream, adoptStream), m_size(0), - m_eventFilter(NULL), m_inputShutdown(false) { - // install event filter - getStream()->setEventFilter(new TMethodEventJob( - this, &CPacketStreamFilter::filterEvent, NULL)); + // do nothing } CPacketStreamFilter::~CPacketStreamFilter() { - IEventJob* job = getStream()->getEventFilter(); - getStream()->setEventFilter(NULL); - delete job; + // do nothing } void @@ -79,7 +74,8 @@ CPacketStreamFilter::read(void* buffer, UInt32 n) readPacketSize(); if (m_inputShutdown && m_size == 0) { - sendEvent(CEvent(getInputShutdownEvent(), getEventTarget(), NULL)); + EVENTQUEUE->addEvent(CEvent(getInputShutdownEvent(), + getEventTarget(), NULL)); } return n; @@ -109,13 +105,6 @@ CPacketStreamFilter::shutdownInput() CStreamFilter::shutdownInput(); } -void -CPacketStreamFilter::setEventFilter(IEventJob* filter) -{ - CLock lock(&m_mutex); - m_eventFilter = filter; -} - bool CPacketStreamFilter::isReady() const { @@ -130,13 +119,6 @@ CPacketStreamFilter::getSize() const return isReadyNoLock() ? m_size : 0; } -IEventJob* -CPacketStreamFilter::getEventFilter() const -{ - CLock lock(&m_mutex); - return m_eventFilter; -} - bool CPacketStreamFilter::isReadyNoLock() const { @@ -159,11 +141,9 @@ CPacketStreamFilter::readPacketSize() } } -void +bool CPacketStreamFilter::readMore() { - // note -- m_mutex must be locked on entry - // note if we have whole packet bool wasReady = isReadyNoLock(); @@ -184,40 +164,27 @@ CPacketStreamFilter::readMore() // if we weren't ready before but now we are then send a // input ready event apparently from the filtered stream. - if (wasReady != isReady) { - sendEvent(CEvent(getInputReadyEvent(), getEventTarget(), NULL)); - } + return (wasReady != isReady); } void -CPacketStreamFilter::sendEvent(const CEvent& event) +CPacketStreamFilter::filterEvent(const CEvent& event) { - if (m_eventFilter != NULL) { - m_eventFilter->run(event); - } - else { - EVENTQUEUE->addEvent(event); - } -} - -void -CPacketStreamFilter::filterEvent(const CEvent& event, void*) -{ - CLock lock(&m_mutex); - if (event.getType() == getInputReadyEvent()) { - readMore(); - return; + CLock lock(&m_mutex); + if (!readMore()) { + return; + } } else if (event.getType() == getInputShutdownEvent()) { // discard this if we have buffered data + CLock lock(&m_mutex); m_inputShutdown = true; - if (m_size == 0) { - sendEvent(CEvent(getInputShutdownEvent(), getEventTarget(), NULL)); + if (m_size != 0) { + return; } - return; } // pass event - sendEvent(event); + CStreamFilter::filterEvent(event); } diff --git a/lib/synergy/CPacketStreamFilter.h b/lib/synergy/CPacketStreamFilter.h index 3156f40d..93ddd8fa 100644 --- a/lib/synergy/CPacketStreamFilter.h +++ b/lib/synergy/CPacketStreamFilter.h @@ -33,24 +33,22 @@ public: virtual UInt32 read(void* buffer, UInt32 n); virtual void write(const void* buffer, UInt32 n); virtual void shutdownInput(); - virtual void setEventFilter(IEventJob* filter); virtual bool isReady() const; virtual UInt32 getSize() const; - virtual IEventJob* getEventFilter() const; + +protected: + // CStreamFilter overrides + virtual void filterEvent(const CEvent&); private: bool isReadyNoLock() const; void readPacketSize(); - - void readMore(); - void sendEvent(const CEvent&); - void filterEvent(const CEvent&, void*); + bool readMore(); private: CMutex m_mutex; UInt32 m_size; CStreamBuffer m_buffer; - IEventJob* m_eventFilter; bool m_inputShutdown; }; From 91acd26d937355d68567508222c84c2fe601247d Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 13 Oct 2004 20:39:22 +0000 Subject: [PATCH 721/807] Fixed typo in the documentation of configuration options. --- doc/configuration.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/configuration.html b/doc/configuration.html index 2cee328a..ca3f55f8 100644 --- a/doc/configuration.html +++ b/doc/configuration.html @@ -217,7 +217,7 @@ A screen can have the following options: Example:
         section: options
    -        heatbeat = 5000
    +        heartbeat = 5000
             switchDelay = 500
         end
     
    From 3f7f946ab04c7fd82516da85f1ee0a970c7441b8 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 23 Oct 2004 18:40:31 +0000 Subject: [PATCH 722/807] Fixed bug in half-duplex keys. Was updating their toggled state om every release as well as press. --- lib/synergy/CKeyState.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/synergy/CKeyState.cpp b/lib/synergy/CKeyState.cpp index 89e2001c..c5c75007 100644 --- a/lib/synergy/CKeyState.cpp +++ b/lib/synergy/CKeyState.cpp @@ -510,7 +510,6 @@ CKeyState::updateKeyState(KeyButton serverID, KeyButton localID, bool press) // never report half-duplex keys as down if (isHalfDuplex(mask)) { m_keys[localID] &= ~kDown; - press = true; } // toggle on the press From 9a1de9f10524ff7235af88a0b930d517335347d9 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 23 Oct 2004 19:43:37 +0000 Subject: [PATCH 723/807] Previous half-duplex fix fixed secondary screens with half duplex keys but broke primary screens. This fixes both and also ensures that the primary screen updates its shadow toggle modifier state when leaving so the secondary screens get the correct toggle modifier state. Together these fix some strange inconsistencies in toggle state across screens. --- lib/synergy/CKeyState.cpp | 19 +++++++++++++++---- lib/synergy/CKeyState.h | 2 +- lib/synergy/CScreen.cpp | 5 ++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/synergy/CKeyState.cpp b/lib/synergy/CKeyState.cpp index c5c75007..41a859d9 100644 --- a/lib/synergy/CKeyState.cpp +++ b/lib/synergy/CKeyState.cpp @@ -39,7 +39,7 @@ void CKeyState::setKeyDown(KeyButton button, bool down) { button &= kButtonMask; - updateKeyState(button, button, down); + updateKeyState(button, button, down, false); } void @@ -149,7 +149,7 @@ CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) fakeKeyEvents(keys, 1); // note that key is down - updateKeyState((KeyButton)(button & kButtonMask), localID, true); + updateKeyState((KeyButton)(button & kButtonMask), localID, true, true); } void @@ -230,7 +230,7 @@ CKeyState::fakeKeyUp(KeyButton button) fakeKeyEvents(keys, 1); // note that key is now up - updateKeyState(button, localID, false); + updateKeyState(button, localID, false, true); } void @@ -486,7 +486,8 @@ CKeyState::fakeKeyEvent(KeyButton button, bool press, bool isAutoRepeat) } void -CKeyState::updateKeyState(KeyButton serverID, KeyButton localID, bool press) +CKeyState::updateKeyState(KeyButton serverID, KeyButton localID, + bool press, bool fake) { // ignore bogus keys if (serverID == 0 || localID == 0) { @@ -510,6 +511,16 @@ CKeyState::updateKeyState(KeyButton serverID, KeyButton localID, bool press) // never report half-duplex keys as down if (isHalfDuplex(mask)) { m_keys[localID] &= ~kDown; + // half-duplex keys on the primary screen don't send the + // usual press/release pairs but instead send the press + // when toggling on and the release when toggleing off. + // since we normally toggle our shadow state on the press + // we need to treat the release as a press on the primary + // screen. we know we're on the primary screen if fake is + // false. secondary screens always get press/release pairs. + if (!fake) { + press = true; + } } // toggle on the press diff --git a/lib/synergy/CKeyState.h b/lib/synergy/CKeyState.h index 00c28eb4..a003b78a 100644 --- a/lib/synergy/CKeyState.h +++ b/lib/synergy/CKeyState.h @@ -152,7 +152,7 @@ private: void fakeKeyEvents(const Keystrokes&, UInt32 count); void fakeKeyEvent(KeyButton, bool press, bool isAutoRepeat); void updateKeyState(KeyButton serverID, - KeyButton localID, bool press); + KeyButton localID, bool press, bool fake); private: enum { diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index 0323d8a8..c7543645 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -469,7 +469,10 @@ CScreen::enterSecondary(KeyModifierMask toggleMask) void CScreen::leavePrimary() { - // do nothing + // we don't track keys while on the primary screen so update our + // idea of them now. this is particularly to update the state of + // the toggle modifiers. + m_screen->updateKeys(); } void From 135c670182308769a22e9b16f659690537592be3 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 24 Oct 2004 18:12:38 +0000 Subject: [PATCH 724/807] Added VK_SLEEP. --- lib/platform/CMSWindowsKeyState.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index 5f9d0ad5..1d47e0e7 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -47,6 +47,9 @@ #define VK_LAUNCH_APP1 0xB6 #define VK_LAUNCH_APP2 0xB7 #endif +#if !defined(VK_SLEEP) +#define VK_SLEEP 0x5F +#endif // // CMSWindowsKeyState @@ -149,7 +152,7 @@ const char* CMSWindowsKeyState::s_vkToName[] = "VK_RWIN", "VK_APPS", "vk 0x5e", - "vk 0x5f", + "VK_SLEEP", "VK_NUMPAD0", "VK_NUMPAD1", "VK_NUMPAD2", @@ -410,7 +413,7 @@ const KeyID CMSWindowsKeyState::s_virtualKey[][2] = /* 0x5c */ { kKeyNone, kKeySuper_R }, // VK_RWIN /* 0x5d */ { kKeyMenu, kKeyMenu }, // VK_APPS /* 0x5e */ { kKeyNone, kKeyNone }, // undefined - /* 0x5f */ { kKeyNone, kKeyNone }, // undefined + /* 0x5f */ { kKeySleep, kKeyNone }, // VK_SLEEP /* 0x60 */ { kKeyKP_0, kKeyNone }, // VK_NUMPAD0 /* 0x61 */ { kKeyKP_1, kKeyNone }, // VK_NUMPAD1 /* 0x62 */ { kKeyKP_2, kKeyNone }, // VK_NUMPAD2 @@ -588,7 +591,7 @@ const UINT CMSWindowsKeyState::s_mapE000[] = /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, VK_SLEEP | 0x100u, /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, From 13a0f8671fbf752aa7812cb630aefd55d1b53491 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 24 Oct 2004 18:14:18 +0000 Subject: [PATCH 725/807] Added eject and sleep key IDs. --- lib/synergy/KeyTypes.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index c988779a..b444c476 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -216,6 +216,8 @@ static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */ static const KeyID kKeyLeftTab = 0xEE20; // extended keys +static const KeyID kKeyEject = 0xE001; +static const KeyID kKeySleep = 0xE05F; static const KeyID kKeyWWWBack = 0xE0A6; static const KeyID kKeyWWWForward = 0xE0A7; static const KeyID kKeyWWWRefresh = 0xE0A8; From 9f6c8f937a7563fed4b22e58d499a0b03ad048da Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 24 Oct 2004 18:15:33 +0000 Subject: [PATCH 726/807] Made OS X key mapping dynamic based on current key layout. It now includes full support for dead keys and non-ascii glyph keys. --- lib/platform/COSXKeyState.cpp | 937 +++++++++++++++++----------------- lib/platform/COSXKeyState.h | 117 +++-- lib/platform/COSXScreen.cpp | 42 +- 3 files changed, 592 insertions(+), 504 deletions(-) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index bab7c1fb..9aca430d 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -14,124 +14,55 @@ #include "COSXKeyState.h" #include "CLog.h" +#include + +struct CKCHRDeadKeyRecord { +public: + UInt8 m_tableIndex; + UInt8 m_virtualKey; + SInt16 m_numCompletions; + UInt8 m_completion[1][2]; +}; + +struct CKCHRDeadKeys { +public: + SInt16 m_numRecords; + CKCHRDeadKeyRecord m_records[1]; +}; struct CKeyEntry { public: KeyID m_keyID; - UInt32 m_keyCode; + UInt32 m_virtualKey; }; -static const CKeyEntry s_keys[] = { - /* ASCII */ - { ' ', 49 }, - { '!', 18 }, - { '\"', 39 }, - { '#', 20 }, - { '$', 21 }, - { '%', 23 }, - { '&', 26 }, - { '\'', 39 }, - { '(', 25 }, - { ')', 29 }, - { '*', 28 }, - { '+', 24 }, - { ',', 43 }, - { '-', 27 }, - { '.', 47 }, - { '/', 44 }, - { '0', 29 }, - { '1', 18 }, - { '2', 19 }, - { '3', 20 }, - { '4', 21 }, - { '5', 23 }, - { '6', 22 }, - { '7', 26 }, - { '8', 28 }, - { '9', 25 }, - { ':', 41 }, - { ';', 41 }, - { '<', 43 }, - { '=', 24 }, - { '>', 47 }, - { '?', 44 }, - { '@', 19 }, - { 'A', 0 }, - { 'B', 11 }, - { 'C', 8 }, - { 'D', 2 }, - { 'E', 14 }, - { 'F', 3 }, - { 'G', 5 }, - { 'H', 4 }, - { 'I', 34 }, - { 'J', 38 }, - { 'K', 40 }, - { 'L', 37 }, - { 'M', 46 }, - { 'N', 45 }, - { 'O', 31 }, - { 'P', 35 }, - { 'Q', 12 }, - { 'R', 15 }, - { 'S', 1 }, - { 'T', 17 }, - { 'U', 32 }, - { 'V', 9 }, - { 'W', 13 }, - { 'X', 7 }, - { 'Y', 16 }, - { 'Z', 6 }, - { '[', 33 }, - { '\\', 42 }, - { ']', 30 }, - { '^', 22 }, - { '_', 27 }, - { '`', 50 }, - { 'a', 0 }, - { 'b', 11 }, - { 'c', 8 }, - { 'd', 2 }, - { 'e', 14 }, - { 'f', 3 }, - { 'g', 5 }, - { 'h', 4 }, - { 'i', 34 }, - { 'j', 38 }, - { 'k', 40 }, - { 'l', 37 }, - { 'm', 46 }, - { 'n', 45 }, - { 'o', 31 }, - { 'p', 35 }, - { 'q', 12 }, - { 'r', 15 }, - { 's', 1 }, - { 't', 17 }, - { 'u', 32 }, - { 'v', 9 }, - { 'w', 13 }, - { 'x', 7 }, - { 'y', 16 }, - { 'z', 6 }, - { '{', 33 }, - { '|', 42 }, - { '}', 30 }, - { '~', 50 }, - - /* TTY functions */ +// Hardcoded virtual key table. Oddly, Apple doesn't document the +// meaning of virtual key codes. The whole point of *virtual* key +// codes is to make them hardware independent so these codes should +// be constant across OS versions and hardware. Yet they don't +// tell us what codes map to what keys so we have to figure it out +// for ourselves. +// +// Note that some virtual keys codes appear more than once. The +// first instance of a virtual key code maps to the KeyID that we +// want to generate for that code. The others are for mapping +// different KeyIDs to a single key code. +static const CKeyEntry s_controlKeys[] = { + // TTY functions { kKeyBackSpace, 51 }, { kKeyTab, 48 }, - { kKeyLinefeed, 36 }, -// { kKeyClear, 0xFFFF }, + { kKeyLeftTab, 48 }, { kKeyReturn, 36 }, - { kKeyPause, 113 }, - { kKeyScrollLock, 107 }, + { kKeyLinefeed, 36 }, +// { kKeyClear, 0xFFFF }, /* no mapping on apple */ +// { kKeyPause, 0xFFFF }, /* no mapping on apple */ +// { kKeyScrollLock, 0xFFFF }, /* no mapping on apple */ // { kKeySysReq, 0xFFFF }, /* no mapping on apple */ { kKeyEscape, 53 }, { kKeyDelete, 117 }, - /* cursor control */ + // cursor control { kKeyHome, 115 }, + { kKeyBegin, 115 }, { kKeyLeft, 123 }, { kKeyUp, 126 }, { kKeyRight, 124 }, @@ -139,10 +70,8 @@ static const CKeyEntry s_keys[] = { { kKeyPageUp, 116 }, { kKeyPageDown, 121 }, { kKeyEnd, 119 }, - { kKeyBegin, 115 }, - /* numeric keypad */ - { kKeyKP_Space, 49 }, + // numeric keypad { kKeyKP_0, 82 }, { kKeyKP_1, 83 }, { kKeyKP_2, 84 }, @@ -160,7 +89,7 @@ static const CKeyEntry s_keys[] = { { kKeyKP_Multiply, 67 }, { kKeyKP_Divide, 75 }, - /* Function keys */ + // function keys { kKeyF1, 122 }, { kKeyF2, 120 }, { kKeyF3, 99 }, @@ -173,285 +102,30 @@ static const CKeyEntry s_keys[] = { { kKeyF10, 109 }, { kKeyF11, 103 }, { kKeyF12, 111 }, + { kKeyF13, 105 }, + { kKeyF14, 107 }, + { kKeyF15, 113 }, - /* Modifier keys */ + // misc keys + { kKeyHelp, 114 }, + + // modifier keys. i don't know how to make the mac properly + // interpret the right hand versions of modifier keys so they're + // currently mapped to the left hand version. { kKeyShift_L, 56 }, - { kKeyShift_R, 56 }, + { kKeyShift_R, 56 /*60*/ }, { kKeyControl_L, 59 }, - { kKeyControl_R, 59 }, + { kKeyControl_R, 59 /*62*/ }, { kKeyAlt_L, 55 }, { kKeyAlt_R, 55 }, - { kKeyCapsLock, 57 }, - { kKeyNumLock, 71 }, - { kKeyMeta_L, 58 }, - { kKeyMeta_R, 58 }, { kKeySuper_L, 58 }, - { kKeySuper_R, 58 }, - { kKeyLeftTab, 48 } + { kKeySuper_R, 58 /*61*/ }, + { kKeyMeta_L, 58 }, + { kKeyMeta_R, 58 /*61*/ }, + { kKeyCapsLock, 57 }, + { kKeyNumLock, 71 } }; -const KeyID COSXKeyState::s_virtualKey[] = -{ - /* 0x00 */ kKeyNone, // A - /* 0x01 */ kKeyNone, // S - /* 0x02 */ kKeyNone, // D - /* 0x03 */ kKeyNone, // F - /* 0x04 */ kKeyNone, // H - /* 0x05 */ kKeyNone, // G - /* 0x06 */ kKeyNone, // Z - /* 0x07 */ kKeyNone, // X - /* 0x08 */ kKeyNone, // C - /* 0x09 */ kKeyNone, // V - /* 0x0a */ kKeyNone, // ~ on some european keyboards - /* 0x0b */ kKeyNone, // B - /* 0x0c */ kKeyNone, // Q - /* 0x0d */ kKeyNone, // W - /* 0x0e */ kKeyNone, // E - /* 0x0f */ kKeyNone, // R - /* 0x10 */ kKeyNone, // Y - /* 0x11 */ kKeyNone, // T - /* 0x12 */ kKeyNone, // 1 - /* 0x13 */ kKeyNone, // 2 - /* 0x14 */ kKeyNone, // 3 - /* 0x15 */ kKeyNone, // 4 - /* 0x16 */ kKeyNone, // 6 - /* 0x17 */ kKeyNone, // 5 - /* 0x18 */ kKeyNone, // = - /* 0x19 */ kKeyNone, // 9 - /* 0x1a */ kKeyNone, // 7 - /* 0x1b */ kKeyNone, // - - /* 0x1c */ kKeyNone, // 8 - /* 0x1d */ kKeyNone, // 0 - /* 0x1e */ kKeyNone, // ] - /* 0x1f */ kKeyNone, // O - /* 0x20 */ kKeyNone, // U - /* 0x21 */ kKeyNone, // [ - /* 0x22 */ kKeyNone, // I - /* 0x23 */ kKeyNone, // P - /* 0x24 */ kKeyReturn, // return - /* 0x25 */ kKeyNone, // L - /* 0x26 */ kKeyNone, // J - /* 0x27 */ kKeyNone, // ' - /* 0x28 */ kKeyNone, // K - /* 0x29 */ kKeyNone, // ; - /* 0x2a */ kKeyNone, /* \ */ - /* 0x2b */ kKeyNone, // , - /* 0x2c */ kKeyNone, // / - /* 0x2d */ kKeyNone, // M - /* 0x2e */ kKeyNone, // M - /* 0x2f */ kKeyNone, // . - /* 0x30 */ kKeyTab, // tab - /* 0x31 */ kKeyNone, // space - /* 0x32 */ kKeyNone, // ` - /* 0x33 */ kKeyBackSpace, // Backspace - /* 0x34 */ kKeyNone, // undefined - /* 0x35 */ kKeyEscape, // escape - /* 0x36 */ kKeyNone, // undefined - /* 0x37 */ kKeyAlt_L, // alt - /* 0x38 */ kKeyShift_L, // shift - /* 0x39 */ kKeyCapsLock, // capslok - /* 0x3a */ kKeyMeta_L, // meta - /* 0x3b */ kKeyControl_L, // control - /* 0x3c */ kKeyNone, // undefined - /* 0x3d */ kKeyNone, // undefined - /* 0x3e */ kKeyNone, // undefined - /* 0x3f */ kKeyNone, // undefined - /* 0x40 */ kKeyNone, // undefined - /* 0x41 */ kKeyKP_Decimal, // keypad . - /* 0x42 */ kKeyNone, // undefined - /* 0x43 */ kKeyKP_Multiply, // keypad * - /* 0x44 */ kKeyNone, // undefined - /* 0x45 */ kKeyKP_Add, // keypad + - /* 0x46 */ kKeyNone, // undefined - /* 0x47 */ kKeyNumLock, // numlock - /* 0x48 */ kKeyNone, // undefined - /* 0x49 */ kKeyNone, // undefined - /* 0x4a */ kKeyNone, // undefined - /* 0x4b */ kKeyKP_Divide, /* keypad \ */ - /* 0x4c */ kKeyKP_Enter, // keypad enter - /* 0x4d */ kKeyNone, // undefined - /* 0x4e */ kKeyKP_Subtract, // keypad - - /* 0x4f */ kKeyNone, // undefined - /* 0x50 */ kKeyNone, // undefined - /* 0x51 */ kKeyNone, // undefined - /* 0x52 */ kKeyKP_0, // keypad 0 - /* 0x53 */ kKeyKP_1, // keypad 1 - /* 0x54 */ kKeyKP_2, // keypad 2 - /* 0x55 */ kKeyKP_3, // keypad 3 - /* 0x56 */ kKeyKP_4, // keypad 4 - /* 0x57 */ kKeyKP_5, // keypad 5 - /* 0x58 */ kKeyKP_6, // keypad 6 - /* 0x59 */ kKeyKP_7, // keypad 7 - /* 0x5a */ kKeyKP_8, // keypad 8 - /* 0x5b */ kKeyKP_9, // keypad 9 - /* 0x5c */ kKeyNone, // undefined - /* 0x5d */ kKeyNone, // undefined - /* 0x5e */ kKeyNone, // undefined - /* 0x5f */ kKeyNone, // undefined - /* 0x60 */ kKeyF5, // F5 - /* 0x61 */ kKeyF6, // F6 - /* 0x62 */ kKeyF7, // F7 - /* 0x63 */ kKeyF3, // F3 - /* 0x64 */ kKeyF8, // F8 - /* 0x65 */ kKeyF9, // F9 - /* 0x66 */ kKeyNone, // undefined - /* 0x67 */ kKeyF11, // F11 - /* 0x68 */ kKeyNone, // undefined - /* 0x69 */ kKeyNone, // undefined - /* 0x6a */ kKeyNone, // undefined - /* 0x6b */ kKeyF10, // F10 - /* 0x6c */ kKeyNone, // undefined - /* 0x6d */ kKeyF12, // F12 - /* 0x6e */ kKeyNone, // undefined - /* 0x6f */ kKeyPause, // pause - /* 0x70 */ kKeyNone, // undefined - /* 0x71 */ kKeyBegin, // home - /* 0x72 */ kKeyPageUp, // PageUP - /* 0x73 */ kKeyDelete, // Delete - /* 0x74 */ kKeyF4, // F4 - /* 0x75 */ kKeyEnd, // end - /* 0x76 */ kKeyF2, // F2 - /* 0x77 */ kKeyNone, // undefined - /* 0x78 */ kKeyF1, // F1 - /* 0x79 */ kKeyPageDown, // PageDown - /* 0x7a */ kKeyNone, // undefined - /* 0x7b */ kKeyLeft, // left - /* 0x7c */ kKeyRight, // right - /* 0x7d */ kKeyDown, // down - /* 0x7e */ kKeyUp, // up - - /* 0x7f */ kKeyNone, // unassigned - /* 0x80 */ kKeyNone, // unassigned - /* 0x81 */ kKeyNone, // unassigned - /* 0x82 */ kKeyNone, // unassigned - /* 0x83 */ kKeyNone, // unassigned - /* 0x84 */ kKeyNone, // unassigned - /* 0x85 */ kKeyNone, // unassigned - /* 0x86 */ kKeyNone, // unassigned - /* 0x87 */ kKeyNone, // unassigned - /* 0x88 */ kKeyNone, // unassigned - /* 0x89 */ kKeyNone, // unassigned - /* 0x8a */ kKeyNone, // unassigned - /* 0x8b */ kKeyNone, // unassigned - /* 0x8c */ kKeyNone, // unassigned - /* 0x8d */ kKeyNone, // unassigned - /* 0x8e */ kKeyNone, // unassigned - /* 0x8f */ kKeyNone, // unassigned - /* 0x90 */ kKeyNone, // unassigned - /* 0x91 */ kKeyNone, // unassigned - /* 0x92 */ kKeyNone, // unassigned - /* 0x93 */ kKeyNone, // unassigned - /* 0x94 */ kKeyNone, // unassigned - /* 0x95 */ kKeyNone, // unassigned - /* 0x96 */ kKeyNone, // unassigned - /* 0x97 */ kKeyNone, // unassigned - /* 0x98 */ kKeyNone, // unassigned - /* 0x99 */ kKeyNone, // unassigned - /* 0x9a */ kKeyNone, // unassigned - /* 0x9b */ kKeyNone, // unassigned - /* 0x9c */ kKeyNone, // unassigned - /* 0x9d */ kKeyNone, // unassigned - /* 0x9e */ kKeyNone, // unassigned - /* 0x9f */ kKeyNone, // unassigned - /* 0xa0 */ kKeyNone, // unassigned - /* 0xa1 */ kKeyNone, // unassigned - /* 0xa2 */ kKeyNone, // unassigned - /* 0xa3 */ kKeyNone, // unassigned - /* 0xa4 */ kKeyNone, // unassigned - /* 0xa5 */ kKeyNone, // unassigned - /* 0xa6 */ kKeyNone, // unassigned - /* 0xa7 */ kKeyNone, // unassigned - /* 0xa8 */ kKeyNone, // unassigned - /* 0xa9 */ kKeyNone, // unassigned - /* 0xaa */ kKeyNone, // unassigned - /* 0xab */ kKeyNone, // unassigned - /* 0xac */ kKeyNone, // unassigned - /* 0xad */ kKeyNone, // unassigned - /* 0xae */ kKeyNone, // unassigned - /* 0xaf */ kKeyNone, // unassigned - /* 0xb0 */ kKeyNone, // unassigned - /* 0xb1 */ kKeyNone, // unassigned - /* 0xb2 */ kKeyNone, // unassigned - /* 0xb3 */ kKeyNone, // unassigned - /* 0xb4 */ kKeyNone, // unassigned - /* 0xb5 */ kKeyNone, // unassigned - /* 0xb6 */ kKeyNone, // unassigned - /* 0xb7 */ kKeyNone, // unassigned - /* 0xb8 */ kKeyNone, // unassigned - /* 0xb9 */ kKeyNone, // unassigned - /* 0xba */ kKeyNone, // unassigned - /* 0xbb */ kKeyNone, // unassigned - /* 0xbc */ kKeyNone, // unassigned - /* 0xbd */ kKeyNone, // unassigned - /* 0xbe */ kKeyNone, // unassigned - /* 0xbf */ kKeyNone, // unassigned - /* 0xc0 */ kKeyNone, // unassigned - /* 0xc1 */ kKeyNone, // unassigned - /* 0xc2 */ kKeyNone, // unassigned - /* 0xc3 */ kKeyNone, // unassigned - /* 0xc4 */ kKeyNone, // unassigned - /* 0xc5 */ kKeyNone, // unassigned - /* 0xc6 */ kKeyNone, // unassigned - /* 0xc7 */ kKeyNone, // unassigned - /* 0xc8 */ kKeyNone, // unassigned - /* 0xc9 */ kKeyNone, // unassigned - /* 0xca */ kKeyNone, // unassigned - /* 0xcb */ kKeyNone, // unassigned - /* 0xcc */ kKeyNone, // unassigned - /* 0xcd */ kKeyNone, // unassigned - /* 0xce */ kKeyNone, // unassigned - /* 0xcf */ kKeyNone, // unassigned - /* 0xd0 */ kKeyNone, // unassigned - /* 0xd1 */ kKeyNone, // unassigned - /* 0xd2 */ kKeyNone, // unassigned - /* 0xd3 */ kKeyNone, // unassigned - /* 0xd4 */ kKeyNone, // unassigned - /* 0xd5 */ kKeyNone, // unassigned - /* 0xd6 */ kKeyNone, // unassigned - /* 0xd7 */ kKeyNone, // unassigned - /* 0xd8 */ kKeyNone, // unassigned - /* 0xd9 */ kKeyNone, // unassigned - /* 0xda */ kKeyNone, // unassigned - /* 0xdb */ kKeyNone, // unassigned - /* 0xdc */ kKeyNone, // unassigned - /* 0xdd */ kKeyNone, // unassigned - /* 0xde */ kKeyNone, // unassigned - /* 0xdf */ kKeyNone, // unassigned - /* 0xe0 */ kKeyNone, // unassigned - /* 0xe1 */ kKeyNone, // unassigned - /* 0xe2 */ kKeyNone, // unassigned - /* 0xe3 */ kKeyNone, // unassigned - /* 0xe4 */ kKeyNone, // unassigned - /* 0xe5 */ kKeyNone, // unassigned - /* 0xe6 */ kKeyNone, // unassigned - /* 0xe7 */ kKeyNone, // unassigned - /* 0xe8 */ kKeyNone, // unassigned - /* 0xe9 */ kKeyNone, // unassigned - /* 0xea */ kKeyNone, // unassigned - /* 0xeb */ kKeyNone, // unassigned - /* 0xec */ kKeyNone, // unassigned - /* 0xed */ kKeyNone, // unassigned - /* 0xee */ kKeyNone, // unassigned - /* 0xef */ kKeyNone, // unassigned - /* 0xf0 */ kKeyNone, // unassigned - /* 0xf1 */ kKeyNone, // unassigned - /* 0xf2 */ kKeyNone, // unassigned - /* 0xf3 */ kKeyNone, // unassigned - /* 0xf4 */ kKeyNone, // unassigned - /* 0xf5 */ kKeyNone, // unassigned - /* 0xf6 */ kKeyNone, // unassigned - /* 0xf7 */ kKeyNone, // unassigned - /* 0xf8 */ kKeyNone, // unassigned - /* 0xf9 */ kKeyNone, // unassigned - /* 0xfa */ kKeyNone, // unassigned - /* 0xfb */ kKeyNone, // unassigned - /* 0xfc */ kKeyNone, // unassigned - /* 0xfd */ kKeyNone, // unassigned - /* 0xfe */ kKeyNone, // unassigned - /* 0xff */ kKeyNone // unassigned -}; - - // // COSXKeyState // @@ -459,6 +133,9 @@ const KeyID COSXKeyState::s_virtualKey[] = COSXKeyState::COSXKeyState() { setHalfDuplexMask(0); + SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript); + SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys); + setKeyboardLayout(keyboardLayoutID); } COSXKeyState::~COSXKeyState() @@ -466,19 +143,6 @@ COSXKeyState::~COSXKeyState() // do nothing } -KeyButton -COSXKeyState::mapKeyCodeToKeyButton(UInt32 keyCode) -{ - // 'A' maps to 0 so shift every id by +1 - return static_cast(keyCode + 1); -} - -UInt32 -COSXKeyState::mapKeyButtonToKeyCode(KeyButton keyButton) -{ - return static_cast(keyButton - 1); -} - void COSXKeyState::sendKeyEvent(void* target, bool press, bool isAutoRepeat, @@ -521,26 +185,22 @@ COSXKeyState::fakeCtrlAltDel() } const char* -COSXKeyState::getKeyName(KeyButton) const +COSXKeyState::getKeyName(KeyButton button) const { - // FIXME - return ""; + static char name[10]; + sprintf(name, "vk 0x%02x", button); + return name; } void COSXKeyState::doUpdateKeys() { - // FIXME -- get the current keyboard state. call setKeyDown(), - // setToggled(), and addModifier() as appropriate. - // save key mapping - // FIXME -- this probably needs to be more dynamic to support - // non-english keyboards. also need to map modifiers needed - // for each KeyID. - for (UInt32 i = 0; i < sizeof(s_keys) / sizeof(s_keys[0]); ++i) { - m_keyMap.insert(std::make_pair(s_keys[i].m_keyID, - mapKeyCodeToKeyButton(s_keys[i].m_keyCode))); + m_keyMap.clear(); + if (!filluchrKeysMap(m_keyMap)) { + fillKCHRKeysMap(m_keyMap); } + fillSpecialKeys(m_keyMap, m_virtualKeyMap); // add modifiers KeyButtons keys; @@ -563,38 +223,96 @@ COSXKeyState::doUpdateKeys() addKeyButton(keys, kKeyCapsLock); addModifier(KeyModifierCapsLock, keys); keys.clear(); + addKeyButton(keys, kKeyNumLock); + addModifier(KeyModifierNumLock, keys); + keys.clear(); + + // FIXME -- get the current keyboard state. call setKeyDown() + // and setToggled() as appropriate. } void -COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool isAutoRepeat) +COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool) { LOG((CLOG_DEBUG2 "doFakeKeyEvent button:%d, press:%d", button, press)); // let system figure out character for us - CGPostKeyboardEvent(0, mapKeyButtonToKeyCode(button), press); + CGPostKeyboardEvent(0, mapKeyButtonToVirtualKey(button), press); } KeyButton COSXKeyState::mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const + KeyModifierMask /*desiredMask*/, + bool isAutoRepeat) const { // look up virtual key - CKeyMap::const_iterator keyIndex = m_keyMap.find(id); + CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id); if (keyIndex == m_keyMap.end()) { return 0; } - CGKeyCode keyCode = keyIndex->second; + const CKeySequence& sequence = keyIndex->second; + if (sequence.empty()) { + return 0; + } - // adjust the modifiers to match the desired modifiers + // FIXME -- for both calls to addKeystrokes below we'd prefer to use + // a required mask that generates the same character but matches + // the desiredMask as closely as possible. + // FIXME -- would prefer to not restore the modifier keys after each + // dead key since it's unnecessary but we don't have a mechanism + // for tracking the modifier state without actually updating the + // internal keyboard state. we'd have to track the state to + // ensure we adjust the right modifiers for remaining dead keys + // and the final key. + + // FIXME -- required modifier masks. we should determine this for + // each key when parsing the layout resource. for now we'll just + // force shift, option and caps-lock to always match exactly in + // addition to whatever other modifiers the key needs. +// FIXME -- this doesn't work. it forces modifiers when modifiers are +// pressed (making having two modifiers at once impossible). + static const KeyModifierMask requiredMask = + KeyModifierShift | + KeyModifierSuper | + KeyModifierCapsLock; + + // add dead keys + for (size_t i = 0; i < sequence.size() - 1; ++i) { + // simulate press + KeyButton keyButton = + addKeystrokes(keys, sequence[i].first, + sequence[i].second, + sequence[i].second | requiredMask, false); + + // simulate release + Keystroke keystroke; + keystroke.m_key = keyButton; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + } + + // add final key + return addKeystrokes(keys, sequence.back().first, + sequence.back().second, + sequence.back().second | requiredMask, + isAutoRepeat); +} + +KeyButton +COSXKeyState::addKeystrokes(Keystrokes& keys, KeyButton keyButton, + KeyModifierMask desiredMask, KeyModifierMask requiredMask, + bool isAutoRepeat) const +{ + // adjust the modifiers Keystrokes undo; - if (!adjustModifiers(keys, undo, desiredMask)) { + if (!adjustModifiers(keys, undo, desiredMask, requiredMask)) { LOG((CLOG_DEBUG2 "failed to adjust modifiers")); return 0; } // add the key event Keystroke keystroke; - keystroke.m_key = keyCode; + keystroke.m_key = keyButton; if (!isAutoRepeat) { keystroke.m_press = true; keystroke.m_repeat = false; @@ -614,76 +332,108 @@ COSXKeyState::mapKey(Keystrokes& keys, KeyID id, undo.pop_back(); } - return keyCode; + return keyButton; } bool -COSXKeyState::adjustModifiers(Keystrokes& /*keys*/, - Keystrokes& /*undo*/, - KeyModifierMask /*desiredMask*/) const +COSXKeyState::adjustModifiers(Keystrokes& keys, + Keystrokes& undo, + KeyModifierMask desiredMask, + KeyModifierMask requiredMask) const { - // FIXME -- should add necessary modifier events to keys and undo + // for each modifier in requiredMask make sure the current state + // of that modifier matches the bit in desiredMask. + for (KeyModifierMask mask = 1u; requiredMask != 0; mask <<= 1) { + if ((mask & requiredMask) != 0) { + bool active = ((desiredMask & mask) != 0); + if (!mapModifier(keys, undo, mask, active)) { + return false; + } + requiredMask ^= mask; + } + } return true; } -KeyID -COSXKeyState::mapKeyFromEvent(EventRef event, KeyModifierMask* maskOut) const +KeyButton +COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, + KeyModifierMask* maskOut, EventRef event) const { - char c; - GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, - NULL, sizeof(c), NULL, &c); + ids.clear(); + // map modifier key + if (maskOut != NULL) { + KeyModifierMask activeMask = getActiveModifiers(); + activeMask &= ~KeyModifierModeSwitch; + *maskOut = activeMask; + } + + // get virtual key UInt32 vkCode; GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(vkCode), NULL, &vkCode); - KeyID id = s_virtualKey[vkCode]; + // handle up events + UInt32 eventKind = GetEventKind(event); + if (eventKind == kEventRawKeyUp) { + // the id isn't used. we just need the same button we used on + // the key press. note that we don't use or reset the dead key + // state; up events should not affect the dead key state. + ids.push_back(kKeyNone); + return mapVirtualKeyToKeyButton(vkCode); + } - // check if not in table; map character to key id - KeyModifierMask activeMask = getActiveModifiers(); - if (id == kKeyNone && c != 0) { - if ((c & 0x80u) == 0) { - // ASCII. if it's a control code and the control key is - // pressed then map it back to the original character. - if ((activeMask & KeyModifierControl) != 0 && c >= 1 && c <= 31) { - c += 'A' - 1; + // check for special keys + CVirtualKeyMap::const_iterator i = m_virtualKeyMap.find(vkCode); + if (i != m_virtualKeyMap.end()) { + m_deadKeyState = 0; + ids.push_back(i->second); + return mapVirtualKeyToKeyButton(vkCode); + } - // if shift isn't pressed then map to lowercase - if ((activeMask & KeyModifierShift) == 0) { - c += 'a' - 'A'; - } + // check for character keys + if (m_uchrResource != NULL) { + // FIXME -- implement this + } + else if (m_KCHRResource != NULL) { + // get the event modifiers and remove the command and control + // keys. + UInt32 modifiers; + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, + NULL, sizeof(modifiers), NULL, &modifiers); + modifiers &= ~(cmdKey | controlKey | rightControlKey); + + // build keycode + UInt16 keycode = + static_cast((modifiers & 0xff00u) | (vkCode & 0x00ffu)); + + // translate key + UInt32 result = KeyTranslate(m_KCHRResource, keycode, &m_deadKeyState); + + // get the characters + UInt8 c1 = static_cast((result >> 16) & 0xffu); + UInt8 c2 = static_cast( result & 0xffu); + if (c2 != 0) { + m_deadKeyState = 0; + if (c1 != 0) { + ids.push_back(charToKeyID(c1)); } - - id = static_cast(c) & 0xffu; - } - else { - // character is not really ASCII. instead it's some - // character in the current ANSI code page. try to - // convert that to a Unicode character. if we fail - // then use the single byte character as is. - //FIXME - id = static_cast(c) & 0xffu; + ids.push_back(charToKeyID(c2)); + return mapVirtualKeyToKeyButton(vkCode); } } - // map modifier key - if (maskOut != NULL) { - activeMask &= ~KeyModifierModeSwitch; - *maskOut = activeMask; - } - - return id; + return 0; } void COSXKeyState::addKeyButton(KeyButtons& keys, KeyID id) const { - CKeyMap::const_iterator keyIndex = m_keyMap.find(id); + CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id); if (keyIndex == m_keyMap.end()) { return; } -// YYY -1 - keys.push_back(keyIndex->second); + keys.push_back(keyIndex->second[0].first); } void @@ -719,13 +469,280 @@ COSXKeyState::handleModifierKeys(void* target, void COSXKeyState::handleModifierKey(void* target, KeyID id, bool down) { - CKeyMap::const_iterator keyIndex = m_keyMap.find(id); + CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id); if (keyIndex == m_keyMap.end()) { return; } -// YYY -1 - KeyButton button = keyIndex->second; + KeyButton button = keyIndex->second[0].first; setKeyDown(button, down); sendKeyEvent(target, down, false, id, getActiveModifiers(), 0, button); } +void +COSXKeyState::checkKeyboardLayout() +{ + SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript); + SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys); + if (keyboardLayoutID != m_keyboardLayoutID) { + // layout changed + setKeyboardLayout(keyboardLayoutID); + doUpdateKeys(); + } +} + +void +COSXKeyState::setKeyboardLayout(SInt16 keyboardLayoutID) +{ + m_keyboardLayoutID = keyboardLayoutID; + m_deadKeyState = 0; + m_KCHRHandle = GetResource('KCHR', m_keyboardLayoutID); + m_uchrHandle = GetResource('uchr', m_keyboardLayoutID); + m_KCHRResource = NULL; + m_uchrResource = NULL; +/* FIXME -- don't use uchr resource yet + if (m_uchrHandle != NULL) { + m_uchrResource = reinterpret_cast(*m_uchrHandle); + } + else */if (m_KCHRHandle != NULL) { + m_KCHRResource = reinterpret_cast(*m_KCHRHandle); + } +} + +void +COSXKeyState::fillSpecialKeys(CKeyIDMap& keyMap, + CVirtualKeyMap& virtualKeyMap) const +{ + // FIXME -- would like to avoid hard coded tables + for (UInt32 i = 0; i < sizeof(s_controlKeys) / + sizeof(s_controlKeys[0]); ++i) { + const CKeyEntry& entry = s_controlKeys[i]; + KeyID keyID = entry.m_keyID; + KeyButton keyButton = mapVirtualKeyToKeyButton(entry.m_virtualKey); + if (keyMap.count(keyID) == 0) { + keyMap[keyID].push_back(std::make_pair(keyButton, 0)); + } + if (virtualKeyMap.count(entry.m_virtualKey) == 0) { + virtualKeyMap[entry.m_virtualKey] = entry.m_keyID; + } + } +} + +bool +COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const +{ + assert(m_KCHRResource != NULL); + + CKCHRResource* r = m_KCHRResource; + + // build non-composed keys to virtual keys mapping + std::map > vkMap; + for (SInt32 i = 0; i < r->m_numTables; ++i) { + // determine the modifier keys for table i + KeyModifierMask mask = + maskForTable(static_cast(i), r->m_tableSelectionIndex); + + // build the KeyID to virtual key map + for (SInt32 j = 0; j < 128; ++j) { + // get character + UInt8 c = r->m_characterTables[i][j]; + + // save character to virtual key mapping + if (keyMap.count(c) == 0) { + vkMap[c] = std::make_pair(mapVirtualKeyToKeyButton(j), mask); + } + + // skip non-glyph character + if (c < 32 || c == 127) { + continue; + } + + // map character to KeyID + KeyID keyID = charToKeyID(c); + + // if we've seen this character already then do nothing + if (keyMap.count(keyID) != 0) { + continue; + } + + // save entry for character + keyMap[keyID].push_back(std::make_pair( + mapVirtualKeyToKeyButton(j), mask)); + } + } + + // build composed keys to virtual keys mapping + CKCHRDeadKeys* dkp = + reinterpret_cast(r->m_characterTables[r->m_numTables]); + CKCHRDeadKeyRecord* dkr = dkp->m_records; + for (SInt32 i = 0; i < dkp->m_numRecords; ++i) { + // determine the modifier keys for table i + KeyModifierMask mask = + maskForTable(dkr->m_tableIndex, r->m_tableSelectionIndex); + + // map each completion + for (SInt32 j = 0; j < dkr->m_numCompletions; ++j) { + // get character + UInt8 c = dkr->m_completion[j][1]; + + // skip non-glyph character + if (c < 32 || c == 127) { + continue; + } + + // map character to KeyID + KeyID keyID = charToKeyID(c); + + // if we've seen this character already then do nothing + if (keyMap.count(keyID) != 0) { + continue; + } + + // map keyID, first to the dead key then to uncomposed + // character. we must find a virtual key that maps to + // to the uncomposed character. + if (vkMap.count(dkr->m_completion[j][0]) != 0) { + CKeySequence& sequence = keyMap[keyID]; + sequence.push_back(std::make_pair( + mapVirtualKeyToKeyButton(dkr->m_virtualKey), mask)); + sequence.push_back(vkMap[dkr->m_completion[j][0]]); + } + } + + // next table. skip all the completions and the no match + // pair to get the next table. + dkr = reinterpret_cast( + dkr->m_completion[dkr->m_numCompletions + 1]); + } + + return true; +} + +bool +COSXKeyState::filluchrKeysMap(CKeyIDMap&) const +{ + // FIXME -- implement this + return false; +} + +KeyButton +COSXKeyState::mapVirtualKeyToKeyButton(UInt32 keyCode) +{ + // 'A' maps to 0 so shift every id + return static_cast(keyCode + KeyButtonOffset); +} + +UInt32 +COSXKeyState::mapKeyButtonToVirtualKey(KeyButton keyButton) +{ + return static_cast(keyButton - KeyButtonOffset); +} + +KeyID +COSXKeyState::charToKeyID(UInt8 c) +{ + if (c == 0) { + return kKeyNone; + } + else if (c >= 32 && c < 127) { + // ASCII + return static_cast(c); + } + else { + // create string with character + char str[2]; + str[0] = static_cast(c); + str[1] = 0; + + // convert to unicode + CFStringRef cfString = + CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, + str, GetScriptManagerVariable(smKeyScript), + kCFAllocatorNull); + + // convert to precomposed + CFMutableStringRef mcfString = + CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString); + CFRelease(cfString); + CFStringNormalize(mcfString, kCFStringNormalizationFormC); + + // check result + int unicodeLength = CFStringGetLength(mcfString); + if (unicodeLength == 0) { + return kKeyNone; + } + if (unicodeLength > 1) { + // FIXME -- more than one character, we should handle this + return kKeyNone; + } + + // get unicode character + UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0); + CFRelease(mcfString); + + // convert to KeyID + return static_cast(uc); + } +} + +KeyID +COSXKeyState::unicharToKeyID(UniChar c) +{ + return static_cast(c); +} + +KeyModifierMask +COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors) +{ + // this is a table of 0 to 255 sorted by the number of 1 bits then + // numerical order. + static const UInt8 s_indexTable[] = { +0, 1, 2, 4, 8, 16, 32, 64, 128, 3, 5, 6, 9, 10, 12, 17, +18, 20, 24, 33, 34, 36, 40, 48, 65, 66, 68, 72, 80, 96, 129, 130, +132, 136, 144, 160, 192, 7, 11, 13, 14, 19, 21, 22, 25, 26, 28, 35, +37, 38, 41, 42, 44, 49, 50, 52, 56, 67, 69, 70, 73, 74, 76, 81, +82, 84, 88, 97, 98, 100, 104, 112, 131, 133, 134, 137, 138, 140, 145, 146, +148, 152, 161, 162, 164, 168, 176, 193, 194, 196, 200, 208, 224, 15, 23, 27, +29, 30, 39, 43, 45, 46, 51, 53, 54, 57, 58, 60, 71, 75, 77, 78, +83, 85, 86, 89, 90, 92, 99, 101, 102, 105, 106, 108, 113, 114, 116, 120, +135, 139, 141, 142, 147, 149, 150, 153, 154, 156, 163, 165, 166, 169, 170, 172, +177, 178, 180, 184, 195, 197, 198, 201, 202, 204, 209, 210, 212, 216, 225, 226, +228, 232, 240, 31, 47, 55, 59, 61, 62, 79, 87, 91, 93, 94, 103, 107, +109, 110, 115, 117, 118, 121, 122, 124, 143, 151, 155, 157, 158, 167, 171, 173, +174, 179, 181, 182, 185, 186, 188, 199, 203, 205, 206, 211, 213, 214, 217, 218, +220, 227, 229, 230, 233, 234, 236, 241, 242, 244, 248, 63, 95, 111, 119, 123, +125, 126, 159, 175, 183, 187, 189, 190, 207, 215, 219, 221, 222, 231, 235, 237, +238, 243, 245, 246, 249, 250, 252, 127, 191, 223, 239, 247, 251, 253, 254, 255 + }; + + // find first entry in tableSelectors that maps to i. this is the + // one that uses the fewest modifier keys. + for (UInt32 j = 0; j < 256; ++j) { + if (tableSelectors[s_indexTable[j]] == i) { + // convert our mask to a traditional mac modifier mask + // (which just means shifting it left 8 bits). + UInt16 macMask = (static_cast(s_indexTable[j]) << 8); + + // convert the mac modifier mask to our mask. + KeyModifierMask mask = 0; + if ((macMask & (shiftKey | rightShiftKey)) != 0) { + mask |= KeyModifierShift; + } + if ((macMask & (controlKey | rightControlKey)) != 0) { + mask |= KeyModifierControl; + } + if ((macMask & cmdKey) != 0) { + mask |= KeyModifierAlt; + } + if ((macMask & (optionKey | rightOptionKey)) != 0) { + mask |= KeyModifierSuper; + } + if ((macMask & alphaLock) != 0) { + mask |= KeyModifierCapsLock; + } + return mask; + } + } + + // should never get here since we've tried every 8 bit number + return 0; +} diff --git a/lib/platform/COSXKeyState.h b/lib/platform/COSXKeyState.h index 037efb16..13e11642 100644 --- a/lib/platform/COSXKeyState.h +++ b/lib/platform/COSXKeyState.h @@ -17,6 +17,7 @@ #include "CKeyState.h" #include "stdmap.h" +#include "stdvector.h" #include //! OS X key state @@ -25,38 +26,21 @@ A key state for OS X. */ class COSXKeyState : public CKeyState { public: - // OS X uses a physical key if 0 for the 'A' key. synergy reserves - // KeyButton 0 so we offset all OS X physical key ids by this much - // when used as a KeyButton and by minus this much to map a KeyButton - // to a physical button. - enum { - KeyButtonOffset = 1 - }; + typedef std::vector CKeyIDs; COSXKeyState(); virtual ~COSXKeyState(); - //! Map physical key id to a KeyButton id + //! Map key event to keys /*! - Maps an OS X key code to a KeyButton. This simply remaps the ids - so we don't use KeyButton 0. + Converts a key event into a sequence of KeyIDs and the shadow modifier + state to a modifier mask. The KeyIDs list, in order, the characters + generated by the key press/release. It returns the id of the button + that was pressed or released, or 0 if the button doesn't map to a known + KeyID. */ - static KeyButton mapKeyCodeToKeyButton(UInt32 keyCode); - - //! Map KeyButton id to a physical key id - /*! - Maps a KeyButton to an OS X key code. This is the inverse of - mapKeyCodeToKeyButton. - */ - static UInt32 mapKeyButtonToKeyCode(KeyButton keyButton); - - //! Map key event to a key - /*! - Converts a key event into a KeyID and the shadow modifier state - to a modifier mask. - */ - KeyID mapKeyFromEvent(EventRef event, - KeyModifierMask* maskOut) const; + KeyButton mapKeyFromEvent(CKeyIDs& ids, + KeyModifierMask* maskOut, EventRef event) const; //! Handle modifier key change /*! @@ -85,18 +69,91 @@ protected: bool isAutoRepeat) const; private: + typedef std::vector > CKeySequence; + typedef std::map CKeyIDMap; + typedef std::map CVirtualKeyMap; + + KeyButton addKeystrokes(Keystrokes& keys, + KeyButton keyButton, + KeyModifierMask desiredMask, + KeyModifierMask requiredMask, + bool isAutoRepeat) const; bool adjustModifiers(Keystrokes& keys, Keystrokes& undo, - KeyModifierMask desiredMask) const; + KeyModifierMask desiredMask, + KeyModifierMask requiredMask) const; void addKeyButton(KeyButtons& keys, KeyID id) const; void handleModifierKey(void* target, KeyID id, bool down); + // Check if the keyboard layout has changed and call doUpdateKeys + // if so. + void checkKeyboardLayout(); + + // Switch to a new keyboard layout. + void setKeyboardLayout(SInt16 keyboardLayoutID); + + // Insert KeyID to key sequences for non-printing characters, like + // delete, home, up arrow, etc. and the virtual key to KeyID mapping. + void fillSpecialKeys(CKeyIDMap& keyMap, + CVirtualKeyMap& virtualKeyMap) const; + + // Convert the KCHR resource to a KeyID to key sequence map. the + // map maps each KeyID to the sequence of keys (with modifiers) + // that would have to be synthesized to generate the KeyID character. + // Returns false iff no KCHR resource was found. + bool fillKCHRKeysMap(CKeyIDMap& keyMap) const; + + // Convert the uchr resource to a KeyID to key sequence map. the + // map maps each KeyID to the sequence of keys (with modifiers) + // that would have to be synthesized to generate the KeyID character. + // Returns false iff no uchr resource was found. + bool filluchrKeysMap(CKeyIDMap& keyMap) const; + + // Maps an OS X virtual key id to a KeyButton. This simply remaps + // the ids so we don't use KeyButton 0. + static KeyButton mapVirtualKeyToKeyButton(UInt32 keyCode); + + // Maps a KeyButton to an OS X key code. This is the inverse of + // mapVirtualKeyToKeyButton. + static UInt32 mapKeyButtonToVirtualKey(KeyButton keyButton); + + // Convert a character in the current script to the equivalent KeyID. + static KeyID charToKeyID(UInt8); + + // Convert a unicode character to the equivalent KeyID. + static KeyID unicharToKeyID(UniChar); + + // Choose the modifier mask with the fewest modifiers for character + // mapping table i. + static KeyModifierMask + maskForTable(UInt8 i, UInt8* tableSelectors); + private: - typedef std::map CKeyMap; + // OS X uses a physical key if 0 for the 'A' key. synergy reserves + // KeyButton 0 so we offset all OS X physical key ids by this much + // when used as a KeyButton and by minus this much to map a KeyButton + // to a physical button. + enum { + KeyButtonOffset = 1 + }; - CKeyMap m_keyMap; + // KCHR resource header + struct CKCHRResource { + public: + SInt16 m_version; + UInt8 m_tableSelectionIndex[256]; + SInt16 m_numTables; + UInt8 m_characterTables[1][128]; + }; - static const KeyID s_virtualKey[]; + SInt16 m_keyboardLayoutID; + mutable UInt32 m_deadKeyState; + Handle m_KCHRHandle; + Handle m_uchrHandle; + CKCHRResource* m_KCHRResource; + UCKeyboardLayout* m_uchrResource; + CKeyIDMap m_keyMap; + CVirtualKeyMap m_virtualKeyMap; }; #endif diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index 75016d5f..cb6cd223 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -808,16 +808,15 @@ COSXScreen::onKey(EventRef event) const UInt32 eventKind = GetEventKind(event); // get the key - UInt32 keyCode; + UInt32 virtualKey; GetEventParameter(event, kEventParamKeyCode, typeUInt32, - NULL, sizeof(keyCode), NULL, &keyCode); - LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, keyCode)); - KeyButton button = COSXKeyState::mapKeyCodeToKeyButton(keyCode); + NULL, sizeof(virtualKey), NULL, &virtualKey); + LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey)); - // sadly, OS X doesn't report the keyCode for modifier keys. keyCode - // will be zero for modifier keys. since that's not good enough we'll - // have to figure out what the key was. - if (keyCode == 0 && eventKind == kEventRawKeyModifiersChanged) { + // sadly, OS X doesn't report the virtualKey for modifier keys. + // virtualKey will be zero for modifier keys. since that's not good + // enough we'll have to figure out what the key was. + if (virtualKey == 0 && eventKind == kEventRawKeyModifiersChanged) { // get old and new modifier state KeyModifierMask oldMask = getActiveModifiers(); KeyModifierMask newMask = mapMacModifiersToSynergy(event); @@ -825,22 +824,37 @@ COSXScreen::onKey(EventRef event) const return true; } + // decode event type bool down = (eventKind == kEventRawKeyDown); bool up = (eventKind == kEventRawKeyUp); bool isRepeat = (eventKind == kEventRawKeyRepeat); - + + // map event to keys + KeyModifierMask mask; + COSXKeyState::CKeyIDs keys; + KeyButton button = m_keyState->mapKeyFromEvent(keys, &mask, event); + if (button == 0) { + return false; + } + + // update button state if (down) { m_keyState->setKeyDown(button, true); } else if (up) { + if (!isKeyDown(button)) { + // up event for a dead key. throw it away. + return false; + } m_keyState->setKeyDown(button, false); } - KeyModifierMask mask; - KeyID key = m_keyState->mapKeyFromEvent(event, &mask); - - m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, - key, mask, 1, button); + // send key events + for (COSXKeyState::CKeyIDs::const_iterator i = keys.begin(); + i != keys.end(); ++i) { + m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, + *i, mask, 1, button); + } return true; } From f65d53d06a84cfbe04ad418d7066e123189a76c4 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 24 Oct 2004 18:18:01 +0000 Subject: [PATCH 727/807] Fixed dead key and AltGr+shift handling on X windows. Also fixed bug with decomposing characters that have no direct key mapping but do have a direct key mapping for the character with the opposite case. --- lib/platform/CXWindowsKeyState.cpp | 114 ++++++++++++++++------------- lib/synergy/CKeyState.cpp | 4 +- lib/synergy/CKeyState.h | 19 +++-- 3 files changed, 78 insertions(+), 59 deletions(-) diff --git a/lib/platform/CXWindowsKeyState.cpp b/lib/platform/CXWindowsKeyState.cpp index b4efddcb..c0ec6f75 100644 --- a/lib/platform/CXWindowsKeyState.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -95,7 +95,7 @@ #if defined(HAVE_X11_XF86KEYSYM_H) static const KeySym g_mapE000[] = { - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x00 */ 0, XF86XK_Eject, 0, 0, 0, 0, 0, 0, /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, @@ -278,6 +278,45 @@ CXWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, // get the mapping for this keysym KeySymIndex keyIndex = m_keysymMap.find(keysym); + if (keyIndex != m_keysymMap.end()) { + // the keysym is mapped to some keycode. create the keystrokes + // for this keysym. + return mapToKeystrokes(keys, keyIndex, isAutoRepeat); + } + + // we can't find the keysym mapped to any keycode. this doesn't + // necessarily mean we can't generate the keysym, though. if the + // keysym can be created by combining keysyms then we may still + // be okay. + CXWindowsUtil::KeySyms decomposition; + if (CXWindowsUtil::decomposeKeySym(keysym, decomposition)) { + LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms", keysym, decomposition.size())); + + // map each decomposed keysym to keystrokes. we want the mask + // and the keycode from the last keysym (which should be the + // only non-dead key). the dead keys are not sensitive to + // anything but shift and mode switch. + KeyButton keycode = 0; + for (CXWindowsUtil::KeySyms::const_iterator i = decomposition.begin(); + i != decomposition.end(); ++i) { + // lookup the key + keysym = *i; + keyIndex = m_keysymMap.find(keysym); + if (keyIndex == m_keysymMap.end()) { + // missing a required keysym + LOG((CLOG_DEBUG2 "can't map keysym %d: 0x%04x", i - decomposition.begin(), keysym)); + return 0; + } + + // the keysym is mapped to some keycode + keycode = mapToKeystrokes(keys, keyIndex, isAutoRepeat); + if (keycode == 0) { + return 0; + } + } + + return keycode; + } // if the mapping isn't found and keysym is caps lock sensitive // then convert the case of the keysym and try again. @@ -292,47 +331,14 @@ CXWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, keyIndex = m_keysymMap.find(lKey); } } - } - - if (keyIndex != m_keysymMap.end()) { - // the keysym is mapped to some keycode. create the keystrokes - // for this keysym. - return mapToKeystrokes(keys, keyIndex, isAutoRepeat); - } - - // we can't find the keysym mapped to any keycode. this doesn't - // necessarily mean we can't generate the keysym, though. if the - // keysym can be created by combining keysyms then we may still - // be okay. - CXWindowsUtil::KeySyms decomposition; - if (!CXWindowsUtil::decomposeKeySym(keysym, decomposition)) { - return 0; - } - LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms", keysym, decomposition.size())); - - // map each decomposed keysym to keystrokes. we want the mask - // and the keycode from the last keysym (which should be the - // only non-dead key). the dead keys are not sensitive to - // anything but shift and mode switch. - KeyButton keycode = 0; - for (CXWindowsUtil::KeySyms::const_iterator i = decomposition.begin(); - i != decomposition.end(); ++i) { - // lookup the key - keysym = *i; - keyIndex = m_keysymMap.find(keysym); - if (keyIndex == m_keysymMap.end()) { - // missing a required keysym - return 0; - } - - // the keysym is mapped to some keycode - keycode = mapToKeystrokes(keys, keyIndex, isAutoRepeat); - if (keycode == 0) { - return 0; + if (keyIndex != m_keysymMap.end()) { + // the keysym is mapped to some keycode. create the keystrokes + // for this keysym. + return mapToKeystrokes(keys, keyIndex, isAutoRepeat); } } - return keycode; + return 0; } void @@ -794,6 +800,7 @@ CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys, if (isAutoRepeat && (m_keyControl.auto_repeats[keycode >> 3] & static_cast(1 << (keycode & 7))) == 0) { + LOG((CLOG_DEBUG2 "non-autorepeating")); return 0; } @@ -923,24 +930,34 @@ CXWindowsKeyState::adjustModifiers(Keystrokes& keys, // get mode switch set correctly. do this before shift because // mode switch may be sensitive to the shift modifier and will // set/reset it as necessary. - const bool wantModeSwitch = ((desiredMask & KeyModifierModeSwitch) != 0); - const bool haveModeSwitch = ((currentMask & KeyModifierModeSwitch) != 0); + bool forceShift = false; + bool wantShift = ((desiredMask & KeyModifierShift) != 0); + bool wantModeSwitch = ((desiredMask & KeyModifierModeSwitch) != 0); + bool haveModeSwitch = ((currentMask & KeyModifierModeSwitch) != 0); if (wantModeSwitch != haveModeSwitch) { LOG((CLOG_DEBUG2 "fix mode switch")); - // adjust shift if necessary + // adjust shift if necessary (i.e. turn it off it's on and mode + // shift is sensitive to the shift key) KeySymIndex modeSwitchIndex = m_keysymMap.find(m_modeSwitchKeysym); assert(modeSwitchIndex != m_keysymMap.end()); if (modeSwitchIndex->second.m_shiftSensitive[0]) { - const bool wantShift = false; - const bool haveShift = ((currentMask & KeyModifierShift) != 0); - if (wantShift != haveShift) { + bool haveShift = ((currentMask & KeyModifierShift) != 0); + if (haveShift) { // add shift keystrokes LOG((CLOG_DEBUG2 "fix shift for mode switch")); - if (!mapModifier(keys, undo, KeyModifierShift, wantShift)) { + if (!mapModifier(keys, undo, KeyModifierShift, false, true)) { return false; } + + // our local concept of shift has flipped currentMask ^= KeyModifierShift; + + // force shift to get turned on below if we had to turn + // off here and shift is desired. if we didn't force it + // then mapModifier would think shift is already down + // and ignore the request. + forceShift = wantShift; } } @@ -952,12 +969,11 @@ CXWindowsKeyState::adjustModifiers(Keystrokes& keys, } // get shift set correctly - const bool wantShift = ((desiredMask & KeyModifierShift) != 0); - const bool haveShift = ((currentMask & KeyModifierShift) != 0); + bool haveShift = ((currentMask & KeyModifierShift) != 0); if (wantShift != haveShift) { // add shift keystrokes LOG((CLOG_DEBUG2 "fix shift")); - if (!mapModifier(keys, undo, KeyModifierShift, wantShift)) { + if (!mapModifier(keys, undo, KeyModifierShift, wantShift, forceShift)) { return false; } currentMask ^= KeyModifierShift; diff --git a/lib/synergy/CKeyState.cpp b/lib/synergy/CKeyState.cpp index 41a859d9..481354fe 100644 --- a/lib/synergy/CKeyState.cpp +++ b/lib/synergy/CKeyState.cpp @@ -295,7 +295,7 @@ CKeyState::addModifier(KeyModifierMask modifier, const KeyButtons& buttons) bool CKeyState::mapModifier(Keystrokes& keys, Keystrokes& undo, - KeyModifierMask mask, bool desireActive) const + KeyModifierMask mask, bool desireActive, bool force) const { // look up modifier const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)]; @@ -304,7 +304,7 @@ CKeyState::mapModifier(Keystrokes& keys, Keystrokes& undo, } // ignore if already in desired state - if (isModifierActive(mask) == desireActive) { + if (!force && isModifierActive(mask) == desireActive) { return true; } diff --git a/lib/synergy/CKeyState.h b/lib/synergy/CKeyState.h index a003b78a..2e8c44b4 100644 --- a/lib/synergy/CKeyState.h +++ b/lib/synergy/CKeyState.h @@ -102,16 +102,19 @@ protected: //! Get key events to change modifier state /*! - Retrieves the key events necessary to activate (\c desireActive is true) - or deactivate (\c desireActive is false) the modifier given by \c mask - by pushing them onto the back of \c keys. \c mask must specify exactly - one modifier. \c undo receives the key events necessary to restore the - modifier's previous state. They're pushed onto \c undo in the reverse - order they should be executed. Returns true if the modifier can be - adjusted, false otherwise. + Retrieves the key events necessary to activate (\p desireActive is true) + or deactivate (\p desireActive is false) the modifier given by \p mask + by pushing them onto the back of \p keys. \p mask must specify exactly + one modifier. \p undo receives the key events necessary to restore the + modifier's previous state. They're pushed onto \p undo in the reverse + order they should be executed. If \p force is false then \p keys and + \p undo are only changed if the modifier is not currently in the + desired state. If \p force is true then \p keys and \p undo are always + changed. Returns true if the modifier can be adjusted, false otherwise. */ bool mapModifier(Keystrokes& keys, Keystrokes& undo, - KeyModifierMask mask, bool desireActive) const; + KeyModifierMask mask, bool desireActive, + bool force = false) const; //! Update the key state /*! From 7ff65e296e2a28e3ea9d29b54f61bd8134e648ac Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 24 Oct 2004 18:18:11 +0000 Subject: [PATCH 728/807] Fixed comment. --- lib/platform/CXWindowsUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index f18d4647..9d6641cc 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -1287,7 +1287,7 @@ CXWindowsUtil::initKeyMaps() { // note that keysyms 0x13a4 and 0x20ac both map to 0x20ac, which // means ambiguity when converting unicode 0x20ac to a keysym. - // as written, the m_UCS4ToKeySym will map to XK_EuroSign. + // as written, the s_UCS4ToKeySym will map to XK_EuroSign. if (s_keySymToUCS4.empty()) { for (size_t i =0; i < sizeof(s_keymap) / sizeof(s_keymap[0]); ++i) { s_keySymToUCS4[s_keymap[i].keysym] = s_keymap[i].ucs4; From 7c7b41d751e3d15ede7cade976ef40e68906fbcf Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 24 Oct 2004 18:18:21 +0000 Subject: [PATCH 729/807] Added eject key mapping. --- lib/platform/CXWindowsScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index d085a441..86d9a780 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -56,7 +56,7 @@ static const KeySym g_map1008FF[] = /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, /* 0x18 */ kKeyWWWHome, kKeyAppMail, 0, kKeyWWWSearch, 0, 0, 0, 0, /* 0x20 */ 0, 0, 0, 0, 0, 0, kKeyWWWBack, kKeyWWWForward, - /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, 0, 0, 0, 0, + /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, kKeyEject, 0, 0, 0, /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, From fe044cfab1c02b5d0b17059a7d341132e5538186 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 27 Oct 2004 21:22:36 +0000 Subject: [PATCH 730/807] Fixed problem with multimonitor on OS X. The bug was simply that the cursor wasn't being parked in the center of the main screen but instead at the center of the total display surface. This could place it off or dangerously close to the edge of the transparent window that covers the main screen and prevent synergy from capturing mouse motion. --- lib/platform/COSXScreen.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index cb6cd223..e9f2bda4 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -52,7 +52,7 @@ COSXScreen::COSXScreen(bool isPrimary) : Rect bounds = { 100, 100, 101, 101 }; // m_hiddenWindow is a window meant to let us get mouse moves - // when the focus is on this computer. If you get your event + // when the focus is on another computer. If you get your event // from the application event target you'll get every mouse // moves. On the other hand the Window event target will only // get events when the mouse moves over the window. @@ -428,7 +428,6 @@ COSXScreen::leave() updateKeys(); // warp to center - // FIXME -- this should be the center of the main monitor warpCursor(m_xCenter, m_yCenter); // capture events @@ -940,16 +939,14 @@ COSXScreen::updateScreenShape() return; } - CGDirectDisplayID* displays = - (CGDirectDisplayID*)malloc(displayCount * sizeof(CGDirectDisplayID)); - + CGDirectDisplayID* displays = new CGDirectDisplayID[displayCount]; if (displays == NULL) { return; } if (CGGetActiveDisplayList(displayCount, displays, &displayCount) != CGDisplayNoErr) { - free(displays); + delete[] displays; return; } @@ -967,11 +964,18 @@ COSXScreen::updateScreenShape() m_h = (SInt32)totalBounds.size.height; // get center of default screen - // XXX -- this should compute the center of displays[0] - m_xCenter = m_x + (m_w >> 1); - m_yCenter = m_y + (m_h >> 1); + GDHandle mainScreen = GetMainDevice(); + if (mainScreen != NULL) { + const Rect& rect = (*mainScreen)->gdRect; + m_xCenter = (rect.left + rect.right) / 2; + m_yCenter = (rect.top + rect.bottom) / 2; + } + else { + m_xCenter = m_x + (m_w >> 1); + m_yCenter = m_y + (m_h >> 1); + } - free(displays); + delete[] displays; LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d on %u %s", m_x, m_y, m_w, m_h, displayCount, (displayCount == 1) ? "display" : "displays")); } From 4d7d2c618f9f0bfb122da2b8ee8fa006c5314eec Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 27 Oct 2004 21:29:19 +0000 Subject: [PATCH 731/807] Fixed bug in mouse wheel handling. Was reacting with mouse wheel events when receiving any event with message == 0 when the system doesn't use old style mouse wheel events. Some programs (especially the flash plugin) would send events with message == 0 causing undesired wheel scrolling. --- lib/platform/CSynergyHook.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 10a36cbf..9b622bc4 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -599,7 +599,7 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam) } if (g_mode == kHOOK_RELAY_EVENTS) { MSG* msg = reinterpret_cast(lParam); - if (msg->message == g_wmMouseWheel) { + if (g_wheelSupport == kWheelOld && msg->message == g_wmMouseWheel) { // post message to our window PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, From 0ff6cdf5b14d66abaea8bc1981a237b10c22adff Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 27 Oct 2004 21:38:05 +0000 Subject: [PATCH 732/807] Now detaching hook thread after event processing. This may fix problems with the Alt key being synthetically down when using the back and forward bindings on a mouse with extra buttons. --- lib/platform/CSynergyHook.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 9b622bc4..525057ed 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -185,10 +185,8 @@ keyboardGetState(BYTE keys[256]) static bool -keyboardHookHandler(WPARAM wParam, LPARAM lParam) +doKeyboardHookHandler(WPARAM wParam, LPARAM lParam) { - attachThreadToForeground(); - // check for dead keys. we don't forward those to our window. // instead we'll leave the key in the keyboard layout (a buffer // internal to the system) for translation when the next key is @@ -416,14 +414,22 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) return false; } + +static +bool +keyboardHookHandler(WPARAM wParam, LPARAM lParam) +{ + attachThreadToForeground(); + bool result = doKeyboardHookHandler(wParam, lParam); + detachThread(); + return result; +} #endif static bool -mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) +doMouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) { - attachThreadToForeground(); - switch (wParam) { case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: @@ -522,6 +528,16 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) return false; } +static +bool +mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) +{ + attachThreadToForeground(); + bool result = doMouseHookHandler(wParam, x, y, data); + detachThread(); + return result; +} + #if !NO_GRAB_KEYBOARD static LRESULT CALLBACK From 12c95723b77a871f704c6357fc1e1fdbacdcea6e Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 27 Oct 2004 21:46:22 +0000 Subject: [PATCH 733/807] Fixed use of freed memory. --- lib/arch/CArchFileUnix.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/arch/CArchFileUnix.cpp b/lib/arch/CArchFileUnix.cpp index 4048061f..89bb51dc 100644 --- a/lib/arch/CArchFileUnix.cpp +++ b/lib/arch/CArchFileUnix.cpp @@ -52,6 +52,8 @@ CArchFileUnix::getBasename(const char* pathname) std::string CArchFileUnix::getUserDirectory() { + char* buffer = NULL; + std::string dir; #if HAVE_GETPWUID_R struct passwd pwent; struct passwd* pwentp; @@ -63,18 +65,16 @@ CArchFileUnix::getUserDirectory() #else long size = BUFSIZ; #endif - char* buffer = new char[size]; + buffer = new char[size]; getpwuid_r(getuid(), &pwent, buffer, size, &pwentp); - delete[] buffer; #else struct passwd* pwentp = getpwuid(getuid()); #endif if (pwentp != NULL && pwentp->pw_dir != NULL) { - return pwentp->pw_dir; - } - else { - return std::string(); + dir = pwentp->pw_dir; } + delete[] buffer; + return dir; } std::string From 91d1fcf38d0a1763a186cc4d981ded842b1d4274 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 28 Oct 2004 21:40:56 +0000 Subject: [PATCH 734/807] Fixed bugs in configuration. Wasn't doing configuration for DPMS and Xinerama correctly. Also was using '#if defined(...)' instead of '#if ...' for testing configure macros in some places. This yields the wrong answer if the macro is set to 0, which means missing/disabled. --- configure.in | 28 ++++++++++++++---------- lib/arch/CArchNetworkBSD.h | 2 +- lib/common/stdistream.h | 2 +- lib/common/stdostream.h | 2 +- lib/common/stdsstream.h | 2 +- lib/platform/CXWindowsClipboard.h | 2 +- lib/platform/CXWindowsEventQueueBuffer.h | 2 +- lib/platform/CXWindowsKeyState.cpp | 6 ++--- lib/platform/CXWindowsKeyState.h | 4 ++-- lib/platform/CXWindowsScreen.cpp | 4 ++-- lib/platform/CXWindowsScreen.h | 2 +- lib/platform/CXWindowsScreenSaver.cpp | 14 ++++++------ lib/platform/CXWindowsScreenSaver.h | 2 +- lib/platform/CXWindowsUtil.h | 2 +- 14 files changed, 39 insertions(+), 35 deletions(-) diff --git a/configure.in b/configure.in index a066a4d1..884b3481 100644 --- a/configure.in +++ b/configure.in @@ -104,23 +104,27 @@ if test x"$acx_host_winapi" = xXWINDOWS; then [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) AC_CHECK_HEADERS([X11/extensions/XTest.h],, AC_MSG_ERROR(You must have the XTest headers to compile synergy)) - AC_CHECK_LIB(Xinerama, - XineramaQueryExtension, - [X_LIBS="$X_LIBS -lXinerama"], + AC_CHECK_HEADERS([X11/extensions/Xinerama.h], + [acx_xinerama_lib_ok=yes], [acx_xinerama_lib_ok=no], - [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) - if test x"$acx_xinerama_lib_ok" != xno; then - AC_CHECK_HEADERS([X11/extensions/Xinerama.h],,, - [#include ]) + [#include ]) + if test x"$acx_xinerama_lib_ok" = xyes; then + AC_CHECK_LIB(Xinerama, + XineramaQueryExtension, + [X_LIBS="$X_LIBS -lXinerama"], + [AC_DEFINE(HAVE_X11_EXTENSIONS_XINERAMA_H, 0)], + [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) fi - AC_CHECK_LIB(Xext, - DPMSQueryExtension, + AC_CHECK_HEADERS([X11/extensions/dpms.h], [acx_dpms_lib_ok=yes], [acx_dpms_lib_ok=no], - [$X_LIBS -lX11 $X_EXTRA_LIBS]) + [#include ]) if test x"$acx_dpms_lib_ok" = xyes; then - AC_CHECK_HEADERS([X11/extensions/dpms.h],,, - [#include ]) + AC_CHECK_LIB(Xext, + DPMSQueryExtension, + [true], + [AC_DEFINE(HAVE_X11_EXTENSIONS_DPMS_H, 0)], + [$X_LIBS -lX11 $X_EXTRA_LIBS]) fi CPPFLAGS="$save_CPPFLAGS" ARCH_LIBS="$X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS $ARCH_LIBS" diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index 671e653e..3915b3ff 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -24,7 +24,7 @@ # include #endif -#if !defined(HAVE_SOCKLEN_T) +#if !HAVE_SOCKLEN_T typedef int socklen_t; #endif diff --git a/lib/common/stdistream.h b/lib/common/stdistream.h index bd903578..33667398 100644 --- a/lib/common/stdistream.h +++ b/lib/common/stdistream.h @@ -13,7 +13,7 @@ */ #include "stdpre.h" -#if defined(HAVE_ISTREAM) +#if HAVE_ISTREAM #include #else #include diff --git a/lib/common/stdostream.h b/lib/common/stdostream.h index e7496f5b..48e89862 100644 --- a/lib/common/stdostream.h +++ b/lib/common/stdostream.h @@ -13,7 +13,7 @@ */ #include "stdpre.h" -#if defined(HAVE_OSTREAM) +#if HAVE_OSTREAM #include #else #include diff --git a/lib/common/stdsstream.h b/lib/common/stdsstream.h index ed8e723f..45b28124 100644 --- a/lib/common/stdsstream.h +++ b/lib/common/stdsstream.h @@ -14,7 +14,7 @@ #include "stdpre.h" -#if defined(HAVE_SSTREAM) || !defined(__GNUC__) || (__GNUC__ >= 3) +#if HAVE_SSTREAM || !defined(__GNUC__) || (__GNUC__ >= 3) #include diff --git a/lib/platform/CXWindowsClipboard.h b/lib/platform/CXWindowsClipboard.h index 25c221bd..5fe126f8 100644 --- a/lib/platform/CXWindowsClipboard.h +++ b/lib/platform/CXWindowsClipboard.h @@ -20,7 +20,7 @@ #include "stdmap.h" #include "stdlist.h" #include "stdvector.h" -#if defined(X_DISPLAY_MISSING) +#if X_DISPLAY_MISSING # error X11 is required to build synergy #else # include diff --git a/lib/platform/CXWindowsEventQueueBuffer.h b/lib/platform/CXWindowsEventQueueBuffer.h index 9c2bdfa3..5d9b6dd4 100644 --- a/lib/platform/CXWindowsEventQueueBuffer.h +++ b/lib/platform/CXWindowsEventQueueBuffer.h @@ -18,7 +18,7 @@ #include "IEventQueueBuffer.h" #include "CMutex.h" #include "stdvector.h" -#if defined(X_DISPLAY_MISSING) +#if X_DISPLAY_MISSING # error X11 is required to build synergy #else # include diff --git a/lib/platform/CXWindowsKeyState.cpp b/lib/platform/CXWindowsKeyState.cpp index c0ec6f75..09058653 100644 --- a/lib/platform/CXWindowsKeyState.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -16,7 +16,7 @@ #include "CXWindowsUtil.h" #include "CLog.h" #include "CStringUtil.h" -#if defined(X_DISPLAY_MISSING) +#if X_DISPLAY_MISSING # error X11 is required to build synergy #else # include @@ -92,7 +92,7 @@ #endif // map special KeyID keys to KeySyms -#if defined(HAVE_X11_XF86KEYSYM_H) +#if HAVE_X11_XF86KEYSYM_H static const KeySym g_mapE000[] = { /* 0x00 */ 0, XF86XK_Eject, 0, 0, 0, 0, 0, 0, @@ -647,7 +647,7 @@ CXWindowsKeyState::keyIDToKeySym(KeyID id, KeyModifierMask mask) const if ((id & 0xfffff000) == 0xe000) { // special character switch (id & 0x0000ff00) { -#if defined(HAVE_X11_XF86KEYSYM_H) +#if HAVE_X11_XF86KEYSYM_H case 0xe000: return g_mapE000[id & 0xff]; #endif diff --git a/lib/platform/CXWindowsKeyState.h b/lib/platform/CXWindowsKeyState.h index 36abeeb2..15d8f894 100644 --- a/lib/platform/CXWindowsKeyState.h +++ b/lib/platform/CXWindowsKeyState.h @@ -17,11 +17,11 @@ #include "CKeyState.h" #include "stdmap.h" -#if defined(X_DISPLAY_MISSING) +#if X_DISPLAY_MISSING # error X11 is required to build synergy #else # include -# if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# if HAVE_X11_EXTENSIONS_XTEST_H # include # else # error The XTest extension is required to build synergy diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 86d9a780..082e9a0e 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -26,14 +26,14 @@ #include "IEventQueue.h" #include "TMethodEventJob.h" #include -#if defined(X_DISPLAY_MISSING) +#if X_DISPLAY_MISSING # error X11 is required to build synergy #else # include # include # define XK_XKB_KEYS # include -# if defined(HAVE_X11_EXTENSIONS_XTEST_H) +# if HAVE_X11_EXTENSIONS_XTEST_H # include # else # error The XTest extension is required to build synergy diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index f9adfd9d..131cfb60 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -17,7 +17,7 @@ #include "CPlatformScreen.h" #include "stdvector.h" -#if defined(X_DISPLAY_MISSING) +#if X_DISPLAY_MISSING # error X11 is required to build synergy #else # include diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index 0b69f08f..222c9a31 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -20,12 +20,12 @@ #include "IEventQueue.h" #include "TMethodEventJob.h" #include -#if defined(HAVE_X11_EXTENSIONS_XTEST_H) +#if HAVE_X11_EXTENSIONS_XTEST_H # include #else # error The XTest extension is required to build synergy #endif -#if defined(HAVE_X11_EXTENSIONS_DPMS_H) +#if HAVE_X11_EXTENSIONS_DPMS_H extern "C" { # include # include @@ -60,7 +60,7 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( // check for DPMS extension. this is an alternative screen saver // that powers down the display. -#if defined(HAVE_X11_EXTENSIONS_DPMS_H) +#if HAVE_X11_EXTENSIONS_DPMS_H int eventBase, errorBase; if (DPMSQueryExtension(m_display, &eventBase, &errorBase)) { if (DPMSCapable(m_display)) { @@ -509,7 +509,7 @@ CXWindowsScreenSaver::handleDisableTimer(const CEvent&, void*) void CXWindowsScreenSaver::activateDPMS(bool activate) { -#if defined(HAVE_X11_EXTENSIONS_DPMS_H) +#if HAVE_X11_EXTENSIONS_DPMS_H if (m_dpms) { // DPMSForceLevel will generate a BadMatch if DPMS is disabled CXWindowsUtil::CErrorLock lock(m_display); @@ -521,7 +521,7 @@ CXWindowsScreenSaver::activateDPMS(bool activate) void CXWindowsScreenSaver::enableDPMS(bool enable) { -#if defined(HAVE_X11_EXTENSIONS_DPMS_H) +#if HAVE_X11_EXTENSIONS_DPMS_H if (m_dpms) { if (enable) { DPMSEnable(m_display); @@ -536,7 +536,7 @@ CXWindowsScreenSaver::enableDPMS(bool enable) bool CXWindowsScreenSaver::isDPMSEnabled() const { -#if defined(HAVE_X11_EXTENSIONS_DPMS_H) +#if HAVE_X11_EXTENSIONS_DPMS_H if (m_dpms) { CARD16 level; BOOL state; @@ -554,7 +554,7 @@ CXWindowsScreenSaver::isDPMSEnabled() const bool CXWindowsScreenSaver::isDPMSActivated() const { -#if defined(HAVE_X11_EXTENSIONS_DPMS_H) +#if HAVE_X11_EXTENSIONS_DPMS_H if (m_dpms) { CARD16 level; BOOL state; diff --git a/lib/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h index 20c2237d..193e1e7b 100644 --- a/lib/platform/CXWindowsScreenSaver.h +++ b/lib/platform/CXWindowsScreenSaver.h @@ -17,7 +17,7 @@ #include "IScreenSaver.h" #include "stdmap.h" -#if defined(X_DISPLAY_MISSING) +#if X_DISPLAY_MISSING # error X11 is required to build synergy #else # include diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h index fb3fb9e7..5765c94a 100644 --- a/lib/platform/CXWindowsUtil.h +++ b/lib/platform/CXWindowsUtil.h @@ -19,7 +19,7 @@ #include "BasicTypes.h" #include "stdmap.h" #include "stdvector.h" -#if defined(X_DISPLAY_MISSING) +#if X_DISPLAY_MISSING # error X11 is required to build synergy #else # include From 719757ca22776c3ec3379baa658d7c856ef5f6e0 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 30 Oct 2004 16:16:32 +0000 Subject: [PATCH 735/807] Improved X extension detection in configure and added handling of dpms.h headers that don't have function prototypes. --- configure.in | 80 ++++++++++++++++++++------- lib/platform/CXWindowsScreenSaver.cpp | 16 ++++++ 2 files changed, 75 insertions(+), 21 deletions(-) diff --git a/configure.in b/configure.in index 884b3481..791acd83 100644 --- a/configure.in +++ b/configure.in @@ -97,37 +97,75 @@ if test x"$acx_host_winapi" = xXWINDOWS; then AC_PATH_XTRA save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$X_CFLAGS $CPPFLAGS" + XEXT_LDADD= + AC_CHECK_LIB(Xtst, XTestQueryExtension, - [X_LIBS="$X_LIBS -lXtst"], + [XEXT_LDADD="$XEXT_LDADD -lXtst"], AC_MSG_ERROR(You must have the XTest library to build synergy), [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) - AC_CHECK_HEADERS([X11/extensions/XTest.h],, + AC_CHECK_HEADERS([X11/extensions/XTest.h], + , AC_MSG_ERROR(You must have the XTest headers to compile synergy)) - AC_CHECK_HEADERS([X11/extensions/Xinerama.h], - [acx_xinerama_lib_ok=yes], - [acx_xinerama_lib_ok=no], - [#include ]) - if test x"$acx_xinerama_lib_ok" = xyes; then - AC_CHECK_LIB(Xinerama, - XineramaQueryExtension, - [X_LIBS="$X_LIBS -lXinerama"], - [AC_DEFINE(HAVE_X11_EXTENSIONS_XINERAMA_H, 0)], - [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) + + acx_have_xinerama=yes + AC_CHECK_LIB(Xinerama, + XineramaQueryExtension, + [acx_have_xinerama=yes], + [acx_have_xinerama=no], + [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS]) + if test x"$acx_have_xinerama" = xyes; then + AC_CHECK_HEADERS([X11/extensions/Xinerama.h], + [acx_have_xinerama=yes], + [acx_have_xinerama=no], + [#include ]) fi - AC_CHECK_HEADERS([X11/extensions/dpms.h], - [acx_dpms_lib_ok=yes], - [acx_dpms_lib_ok=no], - [#include ]) - if test x"$acx_dpms_lib_ok" = xyes; then - AC_CHECK_LIB(Xext, + if test x"$acx_have_xinerama" = xyes; then + XEXT_LDADD="$XEXT_LDADD -lXinerama" + fi + + X_DPMS_LDADD= + acx_have_dpms=no + AC_CHECK_LIB(Xext, + DPMSQueryExtension, + [acx_have_dpms=yes], + [acx_have_dpms=no], + [$X_LIBS -lX11 $X_EXTRA_LIBS]) + if test x"$acx_have_dpms" != xyes; then + AC_CHECK_LIB(Xdpms, DPMSQueryExtension, - [true], - [AC_DEFINE(HAVE_X11_EXTENSIONS_DPMS_H, 0)], + [acx_have_dpms=yes; XDPMS_LDADD=-lXdpms], + [acx_have_dpms=no], [$X_LIBS -lX11 $X_EXTRA_LIBS]) fi + if test x"$acx_have_dpms" = xyes; then + AC_CHECK_HEADERS([X11/extensions/dpms.h], + [acx_have_dpms_h=yes], + [acx_have_dpms_h=no], + [#include ]) + if test x"$acx_have_dpms_h" = xyes; then + XEXT_LDADD="$XEXT_LDADD $XDPMS_LDADD" + AC_MSG_CHECKING(for prototypes in X11/extensions/dpms.h) + acx_have_dpms_protos=no + AC_TRY_COMPILE([ + #include + extern "C" { + #include + } + ],[ + int s = DPMSModeOn; + DPMSQueryExtension(0, 0, 0); + ], + [acx_have_dpms_protos=yes]) + AC_MSG_RESULT($acx_have_dpms_protos) + if test x"$acx_have_dpms_protos" = xyes; then + AC_DEFINE(HAVE_DPMS_PROTOTYPES,1,[Define if the header file declares function prototypes.]) + fi + fi + fi + CPPFLAGS="$save_CPPFLAGS" - ARCH_LIBS="$X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS $ARCH_LIBS" + ARCH_LIBS="$X_LIBS $X_PRE_LIBS $XEXT_LDADD -lXext -lX11 $X_EXTRA_LIBS $ARCH_LIBS" ARCH_CFLAGS="$ARCH_CFLAGS $X_CFLAGS" fi diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index 222c9a31..6c8efbde 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -29,6 +29,22 @@ extern "C" { # include # include +# if !HAVE_DPMS_PROTOTYPES +# undef DPMSModeOn +# undef DPMSModeStandby +# undef DPMSModeSuspend +# undef DPMSModeOff +# define DPMSModeOn 0 +# define DPMSModeStandby 1 +# define DPMSModeSuspend 2 +# define DPMSModeOff 3 +extern Bool DPMSQueryExtension(Display *, int *, int *); +extern Bool DPMSCapable(Display *); +extern Status DPMSEnable(Display *); +extern Status DPMSDisable(Display *); +extern Status DPMSForceLevel(Display *, CARD16); +extern Status DPMSInfo(Display *, CARD16 *, BOOL *); +# endif } #endif From 519871fe15abb76f4b28dea1bf03b9188ae94eeb Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 30 Oct 2004 16:41:36 +0000 Subject: [PATCH 736/807] Changed X11 key mapping to fallback to the modifier keysym with the opposite handedness if the desired handedness is missing. For example, if Alt_R is unmapped as it often is on keyboards with Mode_switch, the client will use Alt_L if it is mapped when told to synthesize Alt_R. This should fix handling of AltGr sent from a win32 server. AltGr is literally just Control_L and Alt_R and, previously, an X11 client without Alt_R mapped would only simulate Control_L. This does not address the fact that the user may really just want the Alt modifier; there are already hueristics to attempt to figure that out. --- lib/platform/CXWindowsKeyState.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/platform/CXWindowsKeyState.cpp b/lib/platform/CXWindowsKeyState.cpp index 09058653..ea4184c4 100644 --- a/lib/platform/CXWindowsKeyState.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -584,6 +584,18 @@ CXWindowsKeyState::mapToModifierMask(unsigned int i, KeySym keysym) void CXWindowsKeyState::updateModifiers() { + struct CHandedModifiers { + KeySym m_left; + KeySym m_right; + }; + static const CHandedModifiers s_handedModifiers[] = { + { XK_Shift_L, XK_Shift_R }, + { XK_Control_L, XK_Control_R }, + { XK_Meta_L, XK_Meta_R }, + { XK_Alt_L, XK_Alt_R }, + { XK_Super_L, XK_Super_R }, + { XK_Hyper_L, XK_Hyper_R } + }; struct CModifierBitInfo { public: KeySym CXWindowsKeyState::*m_keysym; @@ -595,6 +607,24 @@ CXWindowsKeyState::updateModifiers() { &CXWindowsKeyState::m_modeSwitchKeysym, XK_ISO_Level3_Shift, NoSymbol }, }; + // for any modifier with left/right versions that has one side but + // not the other mapped, map the missing side to the existing side. + // this will map, for example, Alt_R to Alt_L if Alt_L is mapped + // but Alt_R isn't. this is almost always what the user wants + // since the user almost never cares about the difference between + // Alt_L and Alt_R. + for (size_t i = 0; i < sizeof(s_handedModifiers) / + sizeof(s_handedModifiers[0]); ++i) { + KeySymIndex lIndex = m_keysymMap.find(s_handedModifiers[i].m_left); + KeySymIndex rIndex = m_keysymMap.find(s_handedModifiers[i].m_right); + if (lIndex == m_keysymMap.end() && rIndex != m_keysymMap.end()) { + m_keysymMap[s_handedModifiers[i].m_left] = rIndex->second; + } + else if (lIndex != m_keysymMap.end() && rIndex == m_keysymMap.end()) { + m_keysymMap[s_handedModifiers[i].m_right] = lIndex->second; + } + } + // choose the keysym to use for some modifiers. if a modifier has // both left and right versions then (arbitrarily) prefer the left. for (size_t i = 0; i < sizeof(s_modifierBitTable) / From 58de7a21fe287e90791fb7a374954f3967bc3029 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Nov 2004 18:21:00 +0000 Subject: [PATCH 737/807] Added option to set the listen address via the win32 GUI. This allows the user to specify one (and only one) network interface to listen on. --- cmd/launcher/CAdvancedOptions.cpp | 50 +++++++++++++++++++++++-- cmd/launcher/CAdvancedOptions.h | 4 ++ cmd/launcher/launcher.rc | 61 +++++++++++++++++-------------- cmd/launcher/resource.h | 2 + 4 files changed, 85 insertions(+), 32 deletions(-) diff --git a/cmd/launcher/CAdvancedOptions.cpp b/cmd/launcher/CAdvancedOptions.cpp index bd4009b3..4c72e8d4 100644 --- a/cmd/launcher/CAdvancedOptions.cpp +++ b/cmd/launcher/CAdvancedOptions.cpp @@ -19,6 +19,7 @@ #include "CArchMiscWindows.h" #include "CAdvancedOptions.h" #include "LaunchUtil.h" +#include "XArch.h" #include "resource.h" // @@ -32,7 +33,8 @@ CAdvancedOptions::CAdvancedOptions(HWND parent, CConfig* config) : m_config(config), m_isClient(false), m_screenName(ARCH->getHostName()), - m_port(kDefaultPort) + m_port(kDefaultPort), + m_interface() { assert(s_singleton == NULL); s_singleton = this; @@ -67,6 +69,12 @@ CAdvancedOptions::getPort() const return m_port; } +CString +CAdvancedOptions::getInterface() const +{ + return m_interface; +} + CString CAdvancedOptions::getCommandLine(bool isClient, const CString& serverName) const { @@ -88,7 +96,11 @@ CAdvancedOptions::getCommandLine(bool isClient, const CString& serverName) const cmdLine += portString; } else { - cmdLine += " --address :"; + cmdLine += " --address "; + if (!m_interface.empty()) { + cmdLine += m_interface; + } + cmdLine += ":"; cmdLine += portString; } @@ -101,14 +113,19 @@ CAdvancedOptions::init() // get values from registry HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); if (key != NULL) { - DWORD newPort = CArchMiscWindows::readValueInt(key, "port"); - CString newName = CArchMiscWindows::readValueString(key, "name"); + DWORD newPort = CArchMiscWindows::readValueInt(key, "port"); + CString newName = CArchMiscWindows::readValueString(key, "name"); + CString newInterface = + CArchMiscWindows::readValueString(key, "interface"); if (newPort != 0) { m_port = static_cast(newPort); } if (!newName.empty()) { m_screenName = newName; } + if (!newInterface.empty()) { + m_interface = newInterface; + } CArchMiscWindows::closeKey(key); } } @@ -125,11 +142,16 @@ CAdvancedOptions::doInit(HWND hwnd) child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT); SendMessage(child, WM_SETTEXT, 0, (LPARAM)m_screenName.c_str()); + + child = getItem(hwnd, IDC_ADVANCED_INTERFACE_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)m_interface.c_str()); } bool CAdvancedOptions::save(HWND hwnd) { + SetCursor(LoadCursor(NULL, IDC_WAIT)); + HWND child = getItem(hwnd, IDC_ADVANCED_NAME_EDIT); CString name = getWindowText(child); if (!m_config->isValidScreenName(name)) { @@ -147,6 +169,23 @@ CAdvancedOptions::save(HWND hwnd) return false; } + child = getItem(hwnd, IDC_ADVANCED_INTERFACE_EDIT); + CString iface = getWindowText(child); + if (!m_isClient) { + try { + if (!iface.empty()) { + ARCH->nameToAddr(iface); + } + } + catch (XArchNetworkName& e) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_INTERFACE_NAME).c_str(), + iface.c_str(), e.what().c_str())); + SetFocus(child); + return false; + } + } + // get and verify port child = getItem(hwnd, IDC_ADVANCED_PORT_EDIT); CString portString = getWindowText(child); @@ -164,12 +203,14 @@ CAdvancedOptions::save(HWND hwnd) // save state m_screenName = name; m_port = port; + m_interface = iface; // save values to registry HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); if (key != NULL) { CArchMiscWindows::setValue(key, "port", m_port); CArchMiscWindows::setValue(key, "name", m_screenName); + CArchMiscWindows::setValue(key, "interface", m_interface); CArchMiscWindows::closeKey(key); } @@ -182,6 +223,7 @@ CAdvancedOptions::setDefaults(HWND hwnd) // restore defaults m_screenName = ARCH->getHostName(); m_port = kDefaultPort; + m_interface = ""; // update GUI doInit(hwnd); diff --git a/cmd/launcher/CAdvancedOptions.h b/cmd/launcher/CAdvancedOptions.h index c86bf38a..1dd9dc44 100644 --- a/cmd/launcher/CAdvancedOptions.h +++ b/cmd/launcher/CAdvancedOptions.h @@ -47,6 +47,9 @@ public: //! Get the port int getPort() const; + //! Get the interface + CString getInterface() const; + //! Convert options to command line string CString getCommandLine(bool isClient, const CString& serverName) const; @@ -71,6 +74,7 @@ private: bool m_isClient; CString m_screenName; int m_port; + CString m_interface; }; #endif diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 71742019..53f15770 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -106,7 +106,7 @@ BEGIN PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 END -IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 236 +IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 254 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION CAPTION "Add Screen" FONT 8, "MS Sans Serif" @@ -116,33 +116,33 @@ BEGIN LTEXT "&Aliases:",IDC_STATIC,7,25,25,8 EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,40,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN - GROUPBOX "Half-Duplex",IDC_STATIC,7,72,178,64 - LTEXT "If your Caps, Num, or Scroll Lock keys behave strangely on this screen then turn on the half-duplex options and restart the server.", + GROUPBOX "Options",IDC_STATIC,7,72,178,80 + LTEXT "If your Caps, Num, or Scroll Lock keys behave strangely on this client screen then try turning the half-duplex options on and reconnect the client.", IDC_STATIC,13,82,165,25 - CONTROL "&Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,13,110,81,10 - CONTROL "&Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,13,122,81,10 - CONTROL "Sc&roll Lock",IDC_ADD_HD_SCROLL_CHECK,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,98,110,81,10 - GROUPBOX "Modifiers",IDC_STATIC,7,139,178,65 - LTEXT "Shift",IDC_STATIC,13,155,15,8 - COMBOBOX IDC_ADD_MOD_SHIFT,37,152,48,60,CBS_DROPDOWNLIST | + CONTROL "Half-duplex &Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,110,165,10 + CONTROL "Half-duplex &Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,122,165,10 + CONTROL "Half-duplex Sc&roll Lock",IDC_ADD_HD_SCROLL_CHECK, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,134,165,10 + GROUPBOX "Modifiers",IDC_STATIC,7,155,178,65 + LTEXT "Shift",IDC_STATIC,13,171,15,8 + COMBOBOX IDC_ADD_MOD_SHIFT,37,168,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Ctrl",IDC_STATIC,13,170,11,8 - COMBOBOX IDC_ADD_MOD_CTRL,37,168,48,60,CBS_DROPDOWNLIST | + LTEXT "Ctrl",IDC_STATIC,13,186,11,8 + COMBOBOX IDC_ADD_MOD_CTRL,37,184,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Alt",IDC_STATIC,13,186,9,8 - COMBOBOX IDC_ADD_MOD_ALT,37,184,48,60,CBS_DROPDOWNLIST | + LTEXT "Alt",IDC_STATIC,13,202,9,8 + COMBOBOX IDC_ADD_MOD_ALT,37,200,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Meta",IDC_STATIC,101,154,17,8 - COMBOBOX IDC_ADD_MOD_META,125,152,48,60,CBS_DROPDOWNLIST | + LTEXT "Meta",IDC_STATIC,101,170,17,8 + COMBOBOX IDC_ADD_MOD_META,125,168,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Super",IDC_STATIC,101,170,20,8 - COMBOBOX IDC_ADD_MOD_SUPER,125,168,48,60,CBS_DROPDOWNLIST | + LTEXT "Super",IDC_STATIC,101,186,20,8 + COMBOBOX IDC_ADD_MOD_SUPER,125,184,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - DEFPUSHBUTTON "OK",IDOK,79,215,50,14 - PUSHBUTTON "Cancel",IDCANCEL,135,215,50,14 + DEFPUSHBUTTON "OK",IDOK,79,233,50,14 + PUSHBUTTON "Cancel",IDCANCEL,135,233,50,14 END IDD_WAIT DIALOG DISCARDABLE 0, 0, 186, 54 @@ -215,7 +215,7 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,150,248,50,14 END -IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 133 +IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 186 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Advanced Options" FONT 8, "MS Sans Serif" @@ -229,9 +229,13 @@ BEGIN LTEXT "&Port:",IDC_STATIC,7,90,16,8 EDITTEXT IDC_ADVANCED_PORT_EDIT,63,88,40,12,ES_AUTOHSCROLL | ES_NUMBER - PUSHBUTTON "&Defaults",IDC_ADVANCED_DEFAULTS,7,112,50,14 - DEFPUSHBUTTON "OK",IDOK,118,112,50,14 - PUSHBUTTON "Cancel",IDCANCEL,173,112,50,14 + LTEXT "The server normally listens for client connections on all network interfaces. Enter the address of a particular interface to listen on just that interface.", + IDC_STATIC,7,110,216,26 + LTEXT "&Interface:",IDC_STATIC,7,144,31,8 + EDITTEXT IDC_ADVANCED_INTERFACE_EDIT,63,142,81,12,ES_AUTOHSCROLL + PUSHBUTTON "&Defaults",IDC_ADVANCED_DEFAULTS,7,165,50,14 + DEFPUSHBUTTON "OK",IDOK,118,165,50,14 + PUSHBUTTON "Cancel",IDCANCEL,173,165,50,14 END @@ -256,7 +260,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 185 TOPMARGIN, 7 - BOTTOMMARGIN, 229 + BOTTOMMARGIN, 247 END IDD_WAIT, DIALOG @@ -288,7 +292,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 223 TOPMARGIN, 7 - BOTTOMMARGIN, 126 + BOTTOMMARGIN, 179 END END #endif // APSTUDIO_INVOKED @@ -360,6 +364,7 @@ BEGIN IDS_ERROR_CODE "Error code: %{1}" IDS_AUTOSTART_PERMISSION_ALL "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself." + IDS_INVALID_INTERFACE_NAME "The interface '%{1}' is invalid: %{2}" END #endif // English (U.S.) resources diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index b8c746ff..caa48052 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -43,6 +43,7 @@ #define IDS_INVALID_TIME 39 #define IDS_ERROR_CODE 39 #define IDS_AUTOSTART_PERMISSION_ALL 40 +#define IDS_INVALID_INTERFACE_NAME 41 #define IDD_MAIN 101 #define IDD_ADD 102 #define IDD_WAIT 103 @@ -89,6 +90,7 @@ #define IDC_ADVANCED_PORT_EDIT 1039 #define IDC_ADD_HD_SCROLL_CHECK 1039 #define IDC_MAIN_DEBUG 1040 +#define IDC_ADVANCED_INTERFACE_EDIT 1040 #define IDC_GLOBAL_DELAY_CHECK 1041 #define IDC_GLOBAL_DELAY_TIME 1042 #define IDC_GLOBAL_TWO_TAP_CHECK 1043 From 2a3015f06aa0e917780d9c630f352a57c99eee2a Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Nov 2004 18:22:45 +0000 Subject: [PATCH 738/807] Fixed a resource leak. Also fixed the detection of the screen saver closing on windows 2000 and XP. --- lib/platform/CMSWindowsScreenSaver.cpp | 60 +++++++++++++++----------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index 4a9130b1..b321fddb 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -376,33 +376,41 @@ CMSWindowsScreenSaver::watchDesktopThread(void*) // wait a bit ARCH->sleep(0.2); - // get current desktop - HDESK desk = OpenInputDesktop(0, FALSE, GENERIC_READ); - if (desk == NULL) { - // can't open desktop so keep waiting - continue; + if (m_isNT) { + // get current desktop + HDESK desk = OpenInputDesktop(0, FALSE, GENERIC_READ); + if (desk == NULL) { + // can't open desktop so keep waiting + continue; + } + + // get current desktop name length + DWORD size; + GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); + + // allocate more space for the name, if necessary + if (size > reserved) { + reserved = size; + name = (TCHAR*)alloca(reserved + sizeof(TCHAR)); + } + + // get current desktop name + GetUserObjectInformation(desk, UOI_NAME, name, size, &size); + CloseDesktop(desk); + + // compare name to screen saver desktop name + if (_tcsicmp(name, TEXT("Screen-saver")) == 0) { + // still the screen saver desktop so keep waiting + continue; + } } - - // get current desktop name length - DWORD size; - GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); - - // allocate more space for the name, if necessary - if (size > reserved) { - reserved = size; - name = (TCHAR*)alloca(reserved + sizeof(TCHAR)); - } - - // get current desktop name - GetUserObjectInformation(desk, UOI_NAME, name, size, &size); - - // compare name to screen saver desktop name - if (_tcsicmp(name, TEXT("Screen-saver")) == 0) { -// XXX -- getting "Default" as desk when screen saver was running -// before we started checking on it. can it be "Default" at other -// times to? how do we properly detect a running screen saver? - // still the screen saver desktop so keep waiting - continue; + else { + // 2000/XP have a sane way to detect a runnin screensaver. + BOOL running; + SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0); + if (running) { + continue; + } } // send screen saver deactivation message From 18dda312d2f08ed6b4e12b9472ab03174b35c976 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Nov 2004 18:24:37 +0000 Subject: [PATCH 739/807] Fixed multimon support for win NT/2000/XP running as client. Mouse would jump between two points. --- lib/platform/CMSWindowsDesks.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index 18e8bb0f..d15eaad4 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -468,8 +468,10 @@ void CMSWindowsDesks::deskMouseMove(SInt32 x, SInt32 y) const { // motion is simple (i.e. it's on the primary monitor) if there - // is only one monitor. - bool simple = !m_multimon; + // is only one monitor. it's also simple if we're not on the + // windows 95 family since those platforms don't have a broken + // mouse_event() function (see the comment below). + bool simple = (!m_multimon || !m_is95Family); if (!simple) { // also simple if motion is within the primary monitor simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && @@ -489,7 +491,7 @@ CMSWindowsDesks::deskMouseMove(SInt32 x, SInt32 y) const 0, 0); } - // windows 98 (and Me?) is broken. you cannot set the absolute + // windows 98 and Me are broken. you cannot set the absolute // position of the mouse except on the primary monitor but you // can do relative moves onto any monitor. this is, in microsoft's // words, "by design." apparently the designers of windows 2000 From 169c1e5008c1390f3eef831d98685820024362d2 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Nov 2004 18:26:29 +0000 Subject: [PATCH 740/807] Fixed synergy quiting when powerdvd stops playing a DVD. This may fix some other bugs that involve unexpectedly quiting. The problem was that synergy would (cleanly) quit when receiving an event with a message id of 0 when not running as a service. --- lib/platform/CMSWindowsEventQueueBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/CMSWindowsEventQueueBuffer.cpp b/lib/platform/CMSWindowsEventQueueBuffer.cpp index 69dee930..5bab2855 100644 --- a/lib/platform/CMSWindowsEventQueueBuffer.cpp +++ b/lib/platform/CMSWindowsEventQueueBuffer.cpp @@ -97,7 +97,7 @@ CMSWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) event = CEvent(CEvent::kQuit); return kSystem; } - else if (m_event.message == m_daemonQuit) { + else if (m_daemonQuit != 0 && m_event.message == m_daemonQuit) { event = CEvent(CEvent::kQuit); return kSystem; } From 5717460221594d1a725b1715a344c0e913df5194 Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Nov 2004 22:10:34 +0000 Subject: [PATCH 741/807] Added debugging output to check window class of active window when leaving screen. This may help determine how to avoid DOS command prompt windows being in the foreground when leaving the screen since they suppress handling of the shift key. --- lib/platform/CMSWindowsDesks.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index d15eaad4..0a95df8d 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -623,6 +623,7 @@ CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout) EnableWindow(desk->m_window, TRUE); SetActiveWindow(desk->m_window); } + LOG((CLOG_DEBUG1 "active window class: %s", className)); } } From 4d427a091d5b17b35277ce8017fc4d4e4dbb523f Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Nov 2004 22:25:39 +0000 Subject: [PATCH 742/807] Added operating system identification log message for debugging purposes. --- cmd/synergyc/synergyc.cpp | 3 ++ cmd/synergys/synergys.cpp | 39 ++++++++++++--- configure.in | 1 + lib/arch/CArch.cpp | 15 ++++++ lib/arch/CArch.h | 6 +++ lib/arch/CArchFileWindows.cpp | 2 +- lib/arch/CArchSystemWindows.cpp | 86 +++++++++++++++++++++++++++++++++ lib/arch/CArchSystemWindows.h | 32 ++++++++++++ lib/arch/IArchSystem.h | 39 +++++++++++++++ lib/arch/Makefile.am | 5 ++ lib/arch/arch.dsp | 12 +++++ 11 files changed, 233 insertions(+), 7 deletions(-) create mode 100644 lib/arch/CArchSystemWindows.cpp create mode 100644 lib/arch/CArchSystemWindows.h create mode 100644 lib/arch/IArchSystem.h diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index ed3b34e2..9127592a 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -480,6 +480,9 @@ run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) // through the task bar. s_taskBarReceiver = createTaskBarReceiver(&logBuffer); + // identify system + LOG((CLOG_INFO "Synergy client on %s", ARCH->getOSName().c_str())); + // run int result = startup(argc, argv); diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index b9464fb3..854f4e99 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -144,12 +144,13 @@ createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) // platform independent main // -static CServer* s_server = NULL; -static CScreen* s_serverScreen = NULL; -static CPrimaryClient* s_primaryClient = NULL; -static CClientListener* s_listener = NULL; -static CServerTaskBarReceiver* s_taskBarReceiver = NULL; -static CEvent::Type s_reloadConfigEvent = CEvent::kUnknown; +static CServer* s_server = NULL; +static CScreen* s_serverScreen = NULL; +static CPrimaryClient* s_primaryClient = NULL; +static CClientListener* s_listener = NULL; +static CServerTaskBarReceiver* s_taskBarReceiver = NULL; +static CEvent::Type s_reloadConfigEvent = CEvent::kUnknown; +static CEvent::Type s_forceReconnectEvent = CEvent::kUnknown; CEvent::Type getReloadConfigEvent() @@ -157,6 +158,12 @@ getReloadConfigEvent() return CEvent::registerTypeOnce(s_reloadConfigEvent, "reloadConfig"); } +CEvent::Type +getForceReconnectEvent() +{ + return CEvent::registerTypeOnce(s_forceReconnectEvent, "forceReconnect"); +} + static void updateStatus() @@ -425,6 +432,15 @@ reloadConfig(const CEvent&, void*) } } +static +void +forceReconnect(const CEvent&, void*) +{ + if (s_server != NULL) { + s_server->disconnect(); + } +} + static int mainLoop() @@ -472,6 +488,12 @@ mainLoop() IEventQueue::getSystemTarget(), new CFunctionEventJob(&reloadConfig)); + // handle force reconnect event by disconnecting clients. they'll + // reconnect automatically. + EVENTQUEUE->adoptHandler(getForceReconnectEvent(), + IEventQueue::getSystemTarget(), + new CFunctionEventJob(&forceReconnect)); + // run event loop. if startServer() failed we're supposed to retry // later. the timer installed by startServer() will take care of // that. @@ -487,6 +509,8 @@ mainLoop() // close down LOG((CLOG_DEBUG1 "stopping server")); + EVENTQUEUE->removeHandler(getForceReconnectEvent(), + IEventQueue::getSystemTarget()); EVENTQUEUE->removeHandler(getReloadConfigEvent(), IEventQueue::getSystemTarget()); stopServer(); @@ -545,6 +569,9 @@ run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) // through the task bar. s_taskBarReceiver = createTaskBarReceiver(&logBuffer); + // identify system + LOG((CLOG_INFO "Synergy server on %s", ARCH->getOSName().c_str())); + // run int result = startup(argc, argv); diff --git a/configure.in b/configure.in index 791acd83..41727153 100644 --- a/configure.in +++ b/configure.in @@ -90,6 +90,7 @@ dnl checks for header files AC_HEADER_STDC AC_CHECK_HEADERS([unistd.h sys/time.h sys/types.h wchar.h alloca.h]) AC_CHECK_HEADERS([sys/socket.h sys/select.h]) +AC_CHECK_HEADERS([sys/utsname.h]) AC_CHECK_HEADERS([istream ostream sstream]) AC_HEADER_TIME if test x"$acx_host_winapi" = xXWINDOWS; then diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index 70b52236..37dee72b 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -23,6 +23,7 @@ #undef ARCH_NETWORK #undef ARCH_SLEEP #undef ARCH_STRING +#undef ARCH_SYSTEM #undef ARCH_TASKBAR #undef ARCH_TIME @@ -37,6 +38,7 @@ # include "CArchNetworkWinsock.h" # include "CArchSleepWindows.h" # include "CArchStringWindows.h" +# include "CArchSystemWindows.h" # include "CArchTaskBarWindows.h" # include "CArchTimeWindows.h" #elif SYSAPI_UNIX @@ -50,6 +52,7 @@ # include "CArchNetworkBSD.h" # include "CArchSleepUnix.h" # include "CArchStringUnix.h" +# include "CArchSystemUnix.h" # include "CArchTaskBarXWindows.h" # include "CArchTimeUnix.h" #endif @@ -86,6 +89,10 @@ # error unsupported platform for string #endif +#if !defined(ARCH_SYSTEM) +# error unsupported platform for system +#endif + #if !defined(ARCH_TASKBAR) # error unsupported platform for taskbar #endif @@ -108,6 +115,7 @@ CArch::CArch(ARCH_ARGS* args) // create architecture implementation objects m_mt = new ARCH_MULTITHREAD; + m_system = new ARCH_SYSTEM; m_file = new ARCH_FILE; m_log = new ARCH_LOG; m_net = new ARCH_NETWORK; @@ -135,6 +143,7 @@ CArch::~CArch() delete m_net; delete m_log; delete m_file; + delete m_system; delete m_mt; // no instance @@ -581,6 +590,12 @@ CArch::getWideCharEncoding() return m_string->getWideCharEncoding(); } +std::string +CArch::getOSName() const +{ + return m_system->getOSName(); +} + void CArch::addReceiver(IArchTaskBarReceiver* receiver) { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index d7940955..8a42bd0c 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -23,6 +23,7 @@ #include "IArchNetwork.h" #include "IArchSleep.h" #include "IArchString.h" +#include "IArchSystem.h" #include "IArchTaskBar.h" #include "IArchTime.h" @@ -52,6 +53,7 @@ class CArch : public IArchConsole, public IArchNetwork, public IArchSleep, public IArchString, + public IArchSystem, public IArchTaskBar, public IArchTime { public: @@ -167,6 +169,9 @@ public: virtual EWideCharEncoding getWideCharEncoding(); + // IArchSystem overrides + virtual std::string getOSName() const; + // IArchTaskBar virtual void addReceiver(IArchTaskBarReceiver*); virtual void removeReceiver(IArchTaskBarReceiver*); @@ -186,6 +191,7 @@ private: IArchNetwork* m_net; IArchSleep* m_sleep; IArchString* m_string; + IArchSystem* m_system; IArchTaskBar* m_taskbar; IArchTime* m_time; }; diff --git a/lib/arch/CArchFileWindows.cpp b/lib/arch/CArchFileWindows.cpp index 7d9fd51b..5debb17b 100644 --- a/lib/arch/CArchFileWindows.cpp +++ b/lib/arch/CArchFileWindows.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include // // CArchFileWindows diff --git a/lib/arch/CArchSystemWindows.cpp b/lib/arch/CArchSystemWindows.cpp new file mode 100644 index 00000000..b634b4bc --- /dev/null +++ b/lib/arch/CArchSystemWindows.cpp @@ -0,0 +1,86 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define WIN32_LEAN_AND_MEAN + +#include "CArchSystemWindows.h" +#include + +// +// CArchSystemWindows +// + +CArchSystemWindows::CArchSystemWindows() +{ + // do nothing +} + +CArchSystemWindows::~CArchSystemWindows() +{ + // do nothing +} + +std::string +CArchSystemWindows::getOSName() const +{ + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(info); + if (GetVersionEx(&info)) { + switch (info.dwPlatformId) { + case VER_PLATFORM_WIN32_NT: + if (info.dwMajorVersion == 5 && info.dwMinorVersion == 2) { + return "Microsoft Windows Server 2003"; + } + if (info.dwMajorVersion == 5 && info.dwMinorVersion == 1) { + return "Microsoft Windows Server XP"; + } + if (info.dwMajorVersion == 5 && info.dwMinorVersion == 0) { + return "Microsoft Windows Server 2000"; + } + if (info.dwMajorVersion <= 4) { + return "Microsoft Windows NT"; + } + char buffer[100]; + sprintf(buffer, "Microsoft Windows %d.%d", + info.dwMajorVersion, info.dwMinorVersion); + return buffer; + + case VER_PLATFORM_WIN32_WINDOWS: + if (info.dwMajorVersion == 4 && info.dwMinorVersion == 0) { + if (info.szCSDVersion[1] == 'C' || + info.szCSDVersion[1] == 'B') { + return "Microsoft Windows 95 OSR2"; + } + return "Microsoft Windows 95"; + } + if (info.dwMajorVersion == 4 && info.dwMinorVersion == 10) { + if (info.szCSDVersion[1] == 'A') { + return "Microsoft Windows 98 SE"; + } + return "Microsoft Windows 98"; + } + if (info.dwMajorVersion == 4 && info.dwMinorVersion == 90) { + return "Microsoft Windows ME"; + } + if (info.dwMajorVersion == 4) { + return "Microsoft Windows unknown 95 family"; + } + break; + + default: + break; + } + } + return "Microsoft Windows "; +} diff --git a/lib/arch/CArchSystemWindows.h b/lib/arch/CArchSystemWindows.h new file mode 100644 index 00000000..e23913d0 --- /dev/null +++ b/lib/arch/CArchSystemWindows.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSYSTEMWINDOWS_H +#define CARCHSYSTEMWINDOWS_H + +#include "IArchSystem.h" + +#define ARCH_SYSTEM CArchSystemWindows + +//! Win32 implementation of IArchString +class CArchSystemWindows : public IArchSystem { +public: + CArchSystemWindows(); + virtual ~CArchSystemWindows(); + + // IArchSystem overrides + virtual std::string getOSName() const; +}; + +#endif diff --git a/lib/arch/IArchSystem.h b/lib/arch/IArchSystem.h new file mode 100644 index 00000000..7a6c941b --- /dev/null +++ b/lib/arch/IArchSystem.h @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef IARCHSYSTEM_H +#define IARCHSYSTEM_H + +#include "IInterface.h" +#include "stdstring.h" + +//! Interface for architecture dependent system queries +/*! +This interface defines operations for querying system info. +*/ +class IArchSystem : public IInterface { +public: + //! @name accessors + //@{ + + //! Identify the OS + /*! + Returns a string identifying the operating system. + */ + virtual std::string getOSName() const = 0; + + //@} +}; + +#endif diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am index 8a45369d..0e9aa158 100644 --- a/lib/arch/Makefile.am +++ b/lib/arch/Makefile.am @@ -27,6 +27,7 @@ COMMON_SOURCE_FILES = \ IArchNetwork.h \ IArchSleep.h \ IArchString.h \ + IArchSystem.h \ IArchTaskBar.h \ IArchTaskBarReceiver.h \ IArchTime.h \ @@ -41,6 +42,7 @@ UNIX_SOURCE_FILES = \ CArchNetworkBSD.cpp \ CArchSleepUnix.cpp \ CArchStringUnix.cpp \ + CArchSystemUnix.cpp \ CArchTaskBarXWindows.cpp \ CArchTimeUnix.cpp \ XArchUnix.cpp \ @@ -52,6 +54,7 @@ UNIX_SOURCE_FILES = \ CArchNetworkBSD.h \ CArchSleepUnix.h \ CArchStringUnix.h \ + CArchSystemUnix.h \ CArchTaskBarXWindows.h \ CArchTimeUnix.h \ XArchUnix.h \ @@ -66,6 +69,7 @@ WIN32_SOURCE_FILES = \ CArchNetworkWinsock.cpp \ CArchSleepWindows.cpp \ CArchStringWindows.cpp \ + CArchSystemWindows.cpp \ CArchTaskBarWindows.cpp \ CArchTimeWindows.cpp \ XArchWindows.cpp \ @@ -78,6 +82,7 @@ WIN32_SOURCE_FILES = \ CArchNetworkWinsock.h \ CArchSleepWindows.h \ CArchStringWindows.h \ + CArchSystemWindows.h \ CArchTaskBarWindows.h \ CArchTimeWindows.h \ XArchWindows.h \ diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp index 17c05936..73575be9 100644 --- a/lib/arch/arch.dsp +++ b/lib/arch/arch.dsp @@ -127,6 +127,10 @@ SOURCE=.\CArchStringWindows.cpp # End Source File # Begin Source File +SOURCE=.\CArchSystemWindows.cpp +# End Source File +# Begin Source File + SOURCE=.\CArchTaskBarWindows.cpp # End Source File # Begin Source File @@ -197,6 +201,10 @@ SOURCE=.\CArchStringWindows.h # End Source File # Begin Source File +SOURCE=.\CArchSystemWindows.h +# End Source File +# Begin Source File + SOURCE=.\CArchTaskBarWindows.h # End Source File # Begin Source File @@ -237,6 +245,10 @@ SOURCE=.\IArchString.h # End Source File # Begin Source File +SOURCE=.\IArchSystem.h +# End Source File +# Begin Source File + SOURCE=.\IArchTaskBar.h # End Source File # Begin Source File From 65a86cb13ba5328a82a6a9a9995c81f3186d2a2a Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Nov 2004 22:26:02 +0000 Subject: [PATCH 743/807] Added tray menu item on win32 to force clients to reconnect. --- cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp | 6 ++++++ cmd/synergys/resource.h | 3 ++- cmd/synergys/synergys.rc | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp index 3a79cb27..a1011fda 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -23,6 +23,7 @@ #include "resource.h" extern CEvent::Type getReloadConfigEvent(); +extern CEvent::Type getForceReconnectEvent(); // // CMSWindowsServerTaskBarReceiver @@ -179,6 +180,11 @@ CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) IEventQueue::getSystemTarget())); break; + case IDC_FORCE_RECONNECT: + EVENTQUEUE->addEvent(CEvent(getForceReconnectEvent(), + IEventQueue::getSystemTarget())); + break; + case IDC_TASKBAR_QUIT: quit(); break; diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index 57fd8c35..f3127816 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -18,13 +18,14 @@ #define IDC_TASKBAR_STATUS 40004 #define IDC_TASKBAR_LOG 40005 #define IDC_RELOAD_CONFIG 40006 +#define IDC_FORCE_RECONNECT 40007 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 109 -#define _APS_NEXT_COMMAND_VALUE 40007 +#define _APS_NEXT_COMMAND_VALUE 40008 #define _APS_NEXT_CONTROL_VALUE 1003 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index c98a3d83..c8f7da21 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -75,6 +75,7 @@ BEGIN MENUITEM "Show Status", IDC_TASKBAR_STATUS MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG MENUITEM "Reload Configuration", IDC_RELOAD_CONFIG + MENUITEM "Force Reconnect", IDC_FORCE_RECONNECT MENUITEM SEPARATOR MENUITEM "Quit", IDC_TASKBAR_QUIT END From f10ef9a97c4c81a3a618ec99faaaf6bfa532674d Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 1 Nov 2004 22:26:52 +0000 Subject: [PATCH 744/807] Reverted change to detach threads in hook DLL. It was breaking double clicking. --- lib/platform/CSynergyHook.cpp | 41 ++++++++++++++++------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 525057ed..279da06d 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -121,6 +121,16 @@ int _fltused=0; static void +detachThread() +{ + if (g_attachedThread != 0 && g_hookThread != g_attachedThread) { + AttachThreadInput(g_hookThread, g_attachedThread, FALSE); + g_attachedThread = 0; + } +} + +static +bool attachThreadToForeground() { // only attach threads if using low level hooks. a low level hook @@ -135,26 +145,17 @@ attachThreadToForeground() // skip if no change if (g_attachedThread != threadID) { // detach from previous thread - if (g_attachedThread != 0 && g_attachedThread != g_hookThread) { - AttachThreadInput(g_hookThread, g_attachedThread, FALSE); - } + detachThread(); + // attach to new thread - g_attachedThread = threadID; - if (g_attachedThread != 0 && g_attachedThread != g_hookThread) { - AttachThreadInput(g_hookThread, g_attachedThread, TRUE); + if (threadID != 0 && threadID != g_hookThread) { + AttachThreadInput(g_hookThread, threadID, TRUE); + g_attachedThread = threadID; } + return true; } } -} - -static -void -detachThread() -{ - if (g_attachedThread != 0) { - AttachThreadInput(g_hookThread, g_attachedThread, FALSE); - g_attachedThread = 0; - } + return false; } #if !NO_GRAB_KEYBOARD @@ -420,9 +421,7 @@ bool keyboardHookHandler(WPARAM wParam, LPARAM lParam) { attachThreadToForeground(); - bool result = doKeyboardHookHandler(wParam, lParam); - detachThread(); - return result; + return doKeyboardHookHandler(wParam, lParam); } #endif @@ -533,9 +532,7 @@ bool mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) { attachThreadToForeground(); - bool result = doMouseHookHandler(wParam, x, y, data); - detachThread(); - return result; + return doMouseHookHandler(wParam, x, y, data); } #if !NO_GRAB_KEYBOARD From 2513ae89721aa8263eb068be98fa407546be1561 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 2 Nov 2004 20:43:55 +0000 Subject: [PATCH 745/807] Added -display option for X11 version. --- cmd/synergyc/synergyc.cpp | 27 +++++++++++++++++++--- cmd/synergys/synergys.cpp | 39 +++++++++++++++++++++++++------- lib/platform/CXWindowsScreen.cpp | 12 ++++++---- lib/platform/CXWindowsScreen.h | 4 ++-- 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 9127592a..88097a25 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -79,6 +79,7 @@ public: m_restartable(true), m_daemon(true), m_logFilter(NULL), + m_display(NULL), m_serverAddress(NULL) { s_instance = this; } ~CArgs() { s_instance = NULL; } @@ -90,6 +91,7 @@ public: bool m_restartable; bool m_daemon; const char* m_logFilter; + const char* m_display; CString m_name; CNetworkAddress* m_serverAddress; }; @@ -110,7 +112,7 @@ createScreen() new CFunctionJob(&handleSystemSuspend), new CFunctionJob(&handleSystemResume))); #elif WINAPI_XWINDOWS - return new CScreen(new CXWindowsScreen(false)); + return new CScreen(new CXWindowsScreen(ARG->m_display, false)); #elif WINAPI_CARBON return new CScreen(new COSXScreen(false)); #endif @@ -521,19 +523,31 @@ static void help() { +#if WINAPI_XWINDOWS +# define USAGE_DISPLAY_ARG \ +" [--display ]" +# define USAGE_DISPLAY_INFO \ +" --display connect to the X server at \n" +#else +# define USAGE_DISPLAY_ARG +# define USAGE_DISPLAY_INFO +#endif + LOG((CLOG_PRINT "Usage: %s" " [--daemon|--no-daemon]" " [--debug ]" +USAGE_DISPLAY_ARG " [--name ]" " [--restart|--no-restart]" -" \n" -"\n" +" " +"\n\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" +USAGE_DISPLAY_INFO " -f, --no-daemon run the client in the foreground.\n" "* --daemon run the client as a daemon.\n" " -n, --name use screen-name instead the hostname to identify\n" @@ -619,6 +633,13 @@ parse(int argc, const char* const* argv) ARG->m_daemon = true; } +#if WINAPI_XWINDOWS + else if (isArg(i, argc, argv, "-display", "--display", 1)) { + // use alternative display + ARG->m_display = argv[++i]; + } +#endif + else if (isArg(i, argc, argv, "-1", "--no-restart")) { // don't try to restart ARG->m_restartable = false; diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 854f4e99..b61fa463 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -88,7 +88,10 @@ public: m_restartable(true), m_daemon(true), m_configFile(), - m_logFilter(NULL) + m_logFilter(NULL), + m_display(NULL), + m_synergyAddress(NULL), + m_config(NULL) { s_instance = this; } ~CArgs() { s_instance = NULL; } @@ -100,6 +103,7 @@ public: bool m_daemon; CString m_configFile; const char* m_logFilter; + const char* m_display; CString m_name; CNetworkAddress* m_synergyAddress; CConfig* m_config; @@ -119,7 +123,7 @@ createScreen() #if WINAPI_MSWINDOWS return new CScreen(new CMSWindowsScreen(true, NULL, NULL)); #elif WINAPI_XWINDOWS - return new CScreen(new CXWindowsScreen(true)); + return new CScreen(new CXWindowsScreen(ARG->m_display, true)); #elif WINAPI_CARBON return new CScreen(new COSXScreen(true)); #endif @@ -613,19 +617,29 @@ static void help() { +#if WINAPI_XWINDOWS +# define USAGE_DISPLAY_ARG \ +" [--display ]" +# define USAGE_DISPLAY_INFO \ +" --display connect to the X server at \n" +#else +# define USAGE_DISPLAY_ARG +# define USAGE_DISPLAY_INFO +#endif + #if SYSAPI_WIN32 -# define PLATFORM_ARGS \ -" {--daemon|--no-daemon}" +# define PLATFORM_ARGS \ +" [--daemon|--no-daemon]" # define PLATFORM_DESC -# define PLATFORM_EXTRA \ +# define PLATFORM_EXTRA \ "At least one command line argument is required. If you don't otherwise\n" \ "need an argument use `--daemon'.\n" \ "\n" #else -# define PLATFORM_ARGS \ +# define PLATFORM_ARGS \ " [--daemon|--no-daemon]" # define PLATFORM_DESC # define PLATFORM_EXTRA @@ -637,10 +651,11 @@ help() " [--address
    ]" " [--config ]" " [--debug ]" +USAGE_DISPLAY_ARG " [--name ]" -" [--restart|--no-restart]\n" +" [--restart|--no-restart]" PLATFORM_ARGS -"\n" +"\n\n" "Start the synergy mouse/keyboard sharing server.\n" "\n" " -a, --address
    listen for clients on the given address.\n" @@ -648,6 +663,7 @@ PLATFORM_ARGS " -d, --debug filter out log messages with priorty below level.\n" " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" " DEBUG, DEBUG1, DEBUG2.\n" +USAGE_DISPLAY_INFO " -f, --no-daemon run the server in the foreground.\n" "* --daemon run the server as a daemon.\n" " -n, --name use screen-name instead the hostname to identify\n" @@ -750,6 +766,13 @@ parse(int argc, const char* const* argv) ARG->m_configFile = argv[++i]; } +#if WINAPI_XWINDOWS + else if (isArg(i, argc, argv, "-display", "--display", 1)) { + // use alternative display + ARG->m_display = argv[++i]; + } +#endif + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { // not a daemon ARG->m_daemon = false; diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 082e9a0e..80b7fc73 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -103,7 +103,7 @@ static const KeySym g_map1008FF[] = CXWindowsScreen* CXWindowsScreen::s_screen = NULL; -CXWindowsScreen::CXWindowsScreen(bool isPrimary) : +CXWindowsScreen::CXWindowsScreen(const char* displayName, bool isPrimary) : m_isPrimary(isPrimary), m_display(NULL), m_root(None), @@ -132,7 +132,7 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) : XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); try { - m_display = openDisplay(); + m_display = openDisplay(displayName); m_root = DefaultRootWindow(m_display); saveShape(); m_window = openWindow(); @@ -599,12 +599,14 @@ CXWindowsScreen::fakeMouseWheel(SInt32 delta) const } Display* -CXWindowsScreen::openDisplay() const +CXWindowsScreen::openDisplay(const char* displayName) const { // get the DISPLAY - const char* displayName = getenv("DISPLAY"); if (displayName == NULL) { - displayName = ":0.0"; + displayName = getenv("DISPLAY"); + if (displayName == NULL) { + displayName = ":0.0"; + } } // open the display diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index 131cfb60..0a93f8b2 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -30,7 +30,7 @@ class CXWindowsScreenSaver; //! Implementation of IPlatformScreen for X11 class CXWindowsScreen : public CPlatformScreen { public: - CXWindowsScreen(bool isPrimary); + CXWindowsScreen(const char* displayName, bool isPrimary); virtual ~CXWindowsScreen(); //! @name manipulators @@ -111,7 +111,7 @@ private: KeyCode m_keycode; }; - Display* openDisplay() const; + Display* openDisplay(const char* displayName) const; void saveShape(); Window openWindow() const; void openIM(); From 4be95841d2eb4b8e18908244de7206f3af229222 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 2 Nov 2004 20:50:36 +0000 Subject: [PATCH 746/807] Added documentation for -display option. --- doc/running.html | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/doc/running.html b/doc/running.html index 61bbd129..24b7e1ef 100644 --- a/doc/running.html +++ b/doc/running.html @@ -180,7 +180,8 @@ solutions:
  • failed to open screen (X11 only)

    Check permission to open the X display;
    - check that the DISPLAY environment variable is set. + check that the DISPLAY environment variable is set;
    + use the --display command line option.

  • already connected

    @@ -241,6 +242,11 @@ and synergyc. --daemon run as a daemon (Unix) or background (Windows) + +    + --display display +   connect to X server at display (X11 only) +  -f, --no-daemon @@ -270,8 +276,9 @@ and synergyc.   --version print version information and exit - + +

    Debug levels are from highest to lowest: FATAL, ERROR, WARNING, From bdd3635f4b8629b6badd1f462b65f577ad25ba41 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 4 Nov 2004 21:26:43 +0000 Subject: [PATCH 747/807] Added support for X11 compose key (Multi_key). This change fixes the handling of compose key sequences. The key presses were suppressed but not the corresponding releases, confusing the clients. It also adds support for generating keysyms via the compose key if the necessary dead keys or Mode_switch are not available. --- lib/platform/CXWindowsKeyState.cpp | 129 +++++++++++++++-------- lib/platform/CXWindowsKeyState.h | 8 +- lib/platform/CXWindowsScreen.cpp | 25 +++++ lib/platform/CXWindowsScreen.h | 3 + lib/platform/CXWindowsUtil.cpp | 161 +++++++++++++++++++++++++++-- lib/platform/CXWindowsUtil.h | 17 ++- 6 files changed, 285 insertions(+), 58 deletions(-) diff --git a/lib/platform/CXWindowsKeyState.cpp b/lib/platform/CXWindowsKeyState.cpp index ea4184c4..fbcc53c2 100644 --- a/lib/platform/CXWindowsKeyState.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -281,60 +281,41 @@ CXWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, if (keyIndex != m_keysymMap.end()) { // the keysym is mapped to some keycode. create the keystrokes // for this keysym. - return mapToKeystrokes(keys, keyIndex, isAutoRepeat); + return mapToKeystrokes(keys, keyIndex, isAutoRepeat, false); } // we can't find the keysym mapped to any keycode. this doesn't // necessarily mean we can't generate the keysym, though. if the // keysym can be created by combining keysyms then we may still // be okay. - CXWindowsUtil::KeySyms decomposition; - if (CXWindowsUtil::decomposeKeySym(keysym, decomposition)) { - LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms", keysym, decomposition.size())); - - // map each decomposed keysym to keystrokes. we want the mask - // and the keycode from the last keysym (which should be the - // only non-dead key). the dead keys are not sensitive to - // anything but shift and mode switch. - KeyButton keycode = 0; - for (CXWindowsUtil::KeySyms::const_iterator i = decomposition.begin(); - i != decomposition.end(); ++i) { - // lookup the key - keysym = *i; - keyIndex = m_keysymMap.find(keysym); - if (keyIndex == m_keysymMap.end()) { - // missing a required keysym - LOG((CLOG_DEBUG2 "can't map keysym %d: 0x%04x", i - decomposition.begin(), keysym)); - return 0; - } - - // the keysym is mapped to some keycode - keycode = mapToKeystrokes(keys, keyIndex, isAutoRepeat); - if (keycode == 0) { - return 0; - } + if (!isAutoRepeat) { + KeyButton keycode = mapDecompositionToKeystrokes(keys, keysym, true); + if (keycode != 0) { + return keycode; + } + keycode = mapDecompositionToKeystrokes(keys, keysym, false); + if (keycode != 0) { + // no key is left synthetically down when using the compose key + // so return 0 even though we succeeded. + return 0; } - - return keycode; } - // if the mapping isn't found and keysym is caps lock sensitive - // then convert the case of the keysym and try again. - if (keyIndex == m_keysymMap.end()) { - KeySym lKey, uKey; - XConvertCase(keysym, &lKey, &uKey); - if (lKey != uKey) { - if (lKey == keysym) { - keyIndex = m_keysymMap.find(uKey); - } - else { - keyIndex = m_keysymMap.find(lKey); - } + // if the keysym is caps lock sensitive then convert the case of + // the keysym and try again. + KeySym lKey, uKey; + XConvertCase(keysym, &lKey, &uKey); + if (lKey != uKey) { + if (lKey == keysym) { + keyIndex = m_keysymMap.find(uKey); + } + else { + keyIndex = m_keysymMap.find(lKey); } if (keyIndex != m_keysymMap.end()) { // the keysym is mapped to some keycode. create the keystrokes // for this keysym. - return mapToKeystrokes(keys, keyIndex, isAutoRepeat); + return mapToKeystrokes(keys, keyIndex, isAutoRepeat, false); } } @@ -786,7 +767,8 @@ CXWindowsKeyState::keyIDToKeySym(KeyID id, KeyModifierMask mask) const KeyButton CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys, - KeySymIndex keyIndex, bool isAutoRepeat) const + KeySymIndex keyIndex, bool isAutoRepeat, + bool pressAndRelease) const { // keyIndex must be valid assert(keyIndex != m_keysymMap.end()); @@ -873,7 +855,14 @@ CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys, // add the key event Keystroke keystroke; keystroke.m_key = keycode; - if (!isAutoRepeat) { + if (pressAndRelease) { + keystroke.m_press = true; + keystroke.m_repeat = false; + keys.push_back(keystroke); + keystroke.m_press = false; + keys.push_back(keystroke); + } + else if (!isAutoRepeat) { keystroke.m_press = true; keystroke.m_repeat = false; keys.push_back(keystroke); @@ -895,6 +884,60 @@ CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys, return keycode; } +KeyButton +CXWindowsKeyState::mapDecompositionToKeystrokes( + Keystrokes& keys, KeySym keysym, bool usingDeadKeys) const +{ + // decompose the keysym + CXWindowsUtil::KeySyms decomposed; + if (usingDeadKeys) { + if (!CXWindowsUtil::decomposeKeySymWithDeadKeys(keysym, decomposed)) { + // no decomposition + return 0; + } + LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms using dead keys", keysym, decomposed.size())); + } + else { + if (!CXWindowsUtil::decomposeKeySymWithCompose(keysym, decomposed)) { + // no decomposition + return 0; + } + LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms using compose key", keysym, decomposed.size())); + } + size_t n = decomposed.size(); + if (n == 0) { + // nothing in the decomposition + return 0; + } + + // map to keystrokes + Keystrokes keystrokes; + KeyButton keycode = 0; + for (size_t i = 0; i < n; ++i) { + // lookup the key + keysym = decomposed[i]; + KeySymIndex keyIndex = m_keysymMap.find(keysym); + if (keyIndex == m_keysymMap.end()) { + // missing a required keysym + LOG((CLOG_DEBUG2 "can't map keysym %d: 0x%04x", i, keysym)); + return 0; + } + + // the keysym is mapped to some keycode. add press and + // release unless this is the last key and usingDeadKeys. + keycode = mapToKeystrokes(keystrokes, keyIndex, + false, (i + 1 < n || !usingDeadKeys)); + if (keycode == 0) { + return 0; + } + } + + // copy keystrokes + keys.insert(keys.end(), keystrokes.begin(), keystrokes.end()); + + return keycode; +} + unsigned int CXWindowsKeyState::findBestKeyIndex(KeySymIndex keyIndex, KeyModifierMask /*currentMask*/) const diff --git a/lib/platform/CXWindowsKeyState.h b/lib/platform/CXWindowsKeyState.h index 15d8f894..bb660d48 100644 --- a/lib/platform/CXWindowsKeyState.h +++ b/lib/platform/CXWindowsKeyState.h @@ -100,7 +100,13 @@ private: // map a KeySym into the keystrokes to produce it KeyButton mapToKeystrokes(Keystrokes& keys, KeySymIndex keyIndex, - bool isAutoRepeat) const; + bool isAutoRepeat, + bool pressAndRelease) const; + + // map a decomposition into keystrokes to produce it. returns the + // last key added to keys iff successful and 0 otherwise. + KeyButton mapDecompositionToKeystrokes(Keystrokes& keys, + KeySym keysym, bool usingDeadKeys) const; // choose the best set of modifiers to generate the KeySym unsigned int findBestKeyIndex(KeySymIndex keyIndex, diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 80b7fc73..b7a84a8a 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -334,6 +334,7 @@ CXWindowsScreen::leave() if (m_ic != NULL) { XmbResetIC(m_ic); XSetICFocus(m_ic); + m_filtered.clear(); } // now off screen @@ -876,6 +877,18 @@ CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) // now filter the event if (XFilterEvent(xevent, None)) { + if (xevent->type == KeyPress) { + // add filtered presses to the filtered list + m_filtered.insert(m_lastKeycode); + } + return; + } + + // discard matching key releases for key presses that were + // filtered and remove them from our filtered list. + else if (xevent->type == KeyRelease && + m_filtered.count(xevent->xkey.keycode) > 0) { + m_filtered.erase(xevent->xkey.keycode); return; } } @@ -1030,14 +1043,26 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey) // get which button. see call to XFilterEvent() in onEvent() // for more info. + bool isFake = false; KeyButton keycode = static_cast(xkey.keycode); if (keycode == 0) { + isFake = true; keycode = static_cast(m_lastKeycode); + if (keycode == 0) { + // no keycode + return; + } } // handle key m_keyState->sendKeyEvent(getEventTarget(), true, false, key, mask, 1, keycode); + + // do fake release if this is a fake press + if (isFake) { + m_keyState->sendKeyEvent(getEventTarget(), + false, false, key, mask, 1, keycode); + } } } diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index 0a93f8b2..cec6c6fa 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -16,6 +16,7 @@ #define CXWINDOWSSCREEN_H #include "CPlatformScreen.h" +#include "stdset.h" #include "stdvector.h" #if X_DISPLAY_MISSING # error X11 is required to build synergy @@ -135,6 +136,7 @@ private: static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: + typedef std::set CFilteredKeycodes; // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; @@ -164,6 +166,7 @@ private: XIM m_im; XIC m_ic; KeyCode m_lastKeycode; + CFilteredKeycodes m_filtered; // clipboards CXWindowsClipboard* m_clipboard[kClipboardEnd]; diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 9d6641cc..b556da00 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -17,6 +17,7 @@ #include "CLog.h" #include "CStringUtil.h" #include +#define XK_MISCELLANY #define XK_XKB_KEYS #define XK_LATIN1 #define XK_LATIN2 @@ -814,7 +815,7 @@ struct codepair { { 0x20ac, 0x20ac } /* EuroSign EURO SIGN */ }; -static const KeySym s_rawDecomposeTable[] = { +static const KeySym s_rawDeadDecomposeTable[] = { // non-dead version of dead keys XK_grave, XK_dead_grave, XK_space, 0, XK_acute, XK_dead_acute, XK_space, 0, @@ -1026,6 +1027,116 @@ static const KeySym s_rawDecomposeTable[] = { 0 }; +static const KeySym s_rawComposedDecomposeTable[] = { + XK_AE, XK_Multi_key, XK_A, XK_E, 0, + XK_Aacute, XK_Multi_key, XK_A, XK_apostrophe, 0, + XK_Acircumflex, XK_Multi_key, XK_A, XK_asciicircum, 0, + XK_Adiaeresis, XK_Multi_key, XK_A, XK_quotedbl, 0, + XK_Agrave, XK_Multi_key, XK_A, XK_grave, 0, + XK_Aring, XK_Multi_key, XK_A, XK_asterisk, 0, + XK_Atilde, XK_Multi_key, XK_A, XK_asciitilde, 0, + XK_Ccedilla, XK_Multi_key, XK_C, XK_comma, 0, + XK_ETH, XK_Multi_key, XK_D, XK_minus, 0, + XK_Eacute, XK_Multi_key, XK_E, XK_apostrophe, 0, + XK_Ecircumflex, XK_Multi_key, XK_E, XK_asciicircum, 0, + XK_Ediaeresis, XK_Multi_key, XK_E, XK_quotedbl, 0, + XK_Egrave, XK_Multi_key, XK_E, XK_grave, 0, + XK_Iacute, XK_Multi_key, XK_I, XK_apostrophe, 0, + XK_Icircumflex, XK_Multi_key, XK_I, XK_asciicircum, 0, + XK_Idiaeresis, XK_Multi_key, XK_I, XK_quotedbl, 0, + XK_Igrave, XK_Multi_key, XK_I, XK_grave, 0, + XK_Ntilde, XK_Multi_key, XK_N, XK_asciitilde, 0, + XK_Oacute, XK_Multi_key, XK_O, XK_apostrophe, 0, + XK_Ocircumflex, XK_Multi_key, XK_O, XK_asciicircum, 0, + XK_Odiaeresis, XK_Multi_key, XK_O, XK_quotedbl, 0, + XK_Ograve, XK_Multi_key, XK_O, XK_grave, 0, + XK_Ooblique, XK_Multi_key, XK_O, XK_slash, 0, + XK_Otilde, XK_Multi_key, XK_O, XK_asciitilde, 0, + XK_THORN, XK_Multi_key, XK_T, XK_H, 0, + XK_Uacute, XK_Multi_key, XK_U, XK_apostrophe, 0, + XK_Ucircumflex, XK_Multi_key, XK_U, XK_asciicircum, 0, + XK_Udiaeresis, XK_Multi_key, XK_U, XK_quotedbl, 0, + XK_Ugrave, XK_Multi_key, XK_U, XK_grave, 0, + XK_Yacute, XK_Multi_key, XK_Y, XK_apostrophe, 0, + XK_aacute, XK_Multi_key, XK_a, XK_apostrophe, 0, + XK_acircumflex, XK_Multi_key, XK_a, XK_asciicircum, 0, + XK_acute, XK_Multi_key, XK_apostrophe, XK_apostrophe, 0, + XK_adiaeresis, XK_Multi_key, XK_a, XK_quotedbl, 0, + XK_ae, XK_Multi_key, XK_a, XK_e, 0, + XK_agrave, XK_Multi_key, XK_a, XK_grave, 0, + XK_aring, XK_Multi_key, XK_a, XK_asterisk, 0, + XK_at, XK_Multi_key, XK_A, XK_T, 0, + XK_atilde, XK_Multi_key, XK_a, XK_asciitilde, 0, + XK_backslash, XK_Multi_key, XK_slash, XK_slash, 0, + XK_bar, XK_Multi_key, XK_L, XK_V, 0, + XK_braceleft, XK_Multi_key, XK_parenleft, XK_minus, 0, + XK_braceright, XK_Multi_key, XK_parenright, XK_minus, 0, + XK_bracketleft, XK_Multi_key, XK_parenleft, XK_parenleft, 0, + XK_bracketright, XK_Multi_key, XK_parenright, XK_parenright, 0, + XK_brokenbar, XK_Multi_key, XK_B, XK_V, 0, + XK_ccedilla, XK_Multi_key, XK_c, XK_comma, 0, + XK_cedilla, XK_Multi_key, XK_comma, XK_comma, 0, + XK_cent, XK_Multi_key, XK_c, XK_slash, 0, + XK_copyright, XK_Multi_key, XK_parenleft, XK_c, 0, + XK_currency, XK_Multi_key, XK_o, XK_x, 0, + XK_degree, XK_Multi_key, XK_0, XK_asciicircum, 0, + XK_diaeresis, XK_Multi_key, XK_quotedbl, XK_quotedbl, 0, + XK_division, XK_Multi_key, XK_colon, XK_minus, 0, + XK_eacute, XK_Multi_key, XK_e, XK_apostrophe, 0, + XK_ecircumflex, XK_Multi_key, XK_e, XK_asciicircum, 0, + XK_ediaeresis, XK_Multi_key, XK_e, XK_quotedbl, 0, + XK_egrave, XK_Multi_key, XK_e, XK_grave, 0, + XK_eth, XK_Multi_key, XK_d, XK_minus, 0, + XK_exclamdown, XK_Multi_key, XK_exclam, XK_exclam, 0, + XK_guillemotleft, XK_Multi_key, XK_less, XK_less, 0, + XK_guillemotright, XK_Multi_key, XK_greater, XK_greater, 0, + XK_numbersign, XK_Multi_key, XK_plus, XK_plus, 0, + XK_hyphen, XK_Multi_key, XK_minus, XK_minus, 0, + XK_iacute, XK_Multi_key, XK_i, XK_apostrophe, 0, + XK_icircumflex, XK_Multi_key, XK_i, XK_asciicircum, 0, + XK_idiaeresis, XK_Multi_key, XK_i, XK_quotedbl, 0, + XK_igrave, XK_Multi_key, XK_i, XK_grave, 0, + XK_macron, XK_Multi_key, XK_minus, XK_asciicircum, 0, + XK_masculine, XK_Multi_key, XK_o, XK_underscore, 0, + XK_mu, XK_Multi_key, XK_u, XK_slash, 0, + XK_multiply, XK_Multi_key, XK_x, XK_x, 0, + XK_nobreakspace, XK_Multi_key, XK_space, XK_space, 0, + XK_notsign, XK_Multi_key, XK_comma, XK_minus, 0, + XK_ntilde, XK_Multi_key, XK_n, XK_asciitilde, 0, + XK_oacute, XK_Multi_key, XK_o, XK_apostrophe, 0, + XK_ocircumflex, XK_Multi_key, XK_o, XK_asciicircum, 0, + XK_odiaeresis, XK_Multi_key, XK_o, XK_quotedbl, 0, + XK_ograve, XK_Multi_key, XK_o, XK_grave, 0, + XK_onehalf, XK_Multi_key, XK_1, XK_2, 0, + XK_onequarter, XK_Multi_key, XK_1, XK_4, 0, + XK_onesuperior, XK_Multi_key, XK_1, XK_asciicircum, 0, + XK_ordfeminine, XK_Multi_key, XK_a, XK_underscore, 0, + XK_oslash, XK_Multi_key, XK_o, XK_slash, 0, + XK_otilde, XK_Multi_key, XK_o, XK_asciitilde, 0, + XK_paragraph, XK_Multi_key, XK_p, XK_exclam, 0, + XK_periodcentered, XK_Multi_key, XK_period, XK_period, 0, + XK_plusminus, XK_Multi_key, XK_plus, XK_minus, 0, + XK_questiondown, XK_Multi_key, XK_question, XK_question, 0, + XK_registered, XK_Multi_key, XK_parenleft, XK_r, 0, + XK_section, XK_Multi_key, XK_s, XK_o, 0, + XK_ssharp, XK_Multi_key, XK_s, XK_s, 0, + XK_sterling, XK_Multi_key, XK_L, XK_minus, 0, + XK_thorn, XK_Multi_key, XK_t, XK_h, 0, + XK_threequarters, XK_Multi_key, XK_3, XK_4, 0, + XK_threesuperior, XK_Multi_key, XK_3, XK_asciicircum, 0, + XK_twosuperior, XK_Multi_key, XK_2, XK_asciicircum, 0, + XK_uacute, XK_Multi_key, XK_u, XK_apostrophe, 0, + XK_ucircumflex, XK_Multi_key, XK_u, XK_asciicircum, 0, + XK_udiaeresis, XK_Multi_key, XK_u, XK_quotedbl, 0, + XK_ugrave, XK_Multi_key, XK_u, XK_grave, 0, + XK_yacute, XK_Multi_key, XK_y, XK_apostrophe, 0, + XK_ydiaeresis, XK_Multi_key, XK_y, XK_quotedbl, 0, + XK_yen, XK_Multi_key, XK_y, XK_equal, 0, + + // end of table + 0 +}; + // // CXWindowsUtil @@ -1033,7 +1144,8 @@ static const KeySym s_rawDecomposeTable[] = { CXWindowsUtil::CKeySymMap CXWindowsUtil::s_keySymToUCS4; CXWindowsUtil::CUCS4Map CXWindowsUtil::s_UCS4ToKeySym; -CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_decomposedKeySyms; +CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_deadKeyDecomposedKeySyms; +CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_composeDecomposedKeySyms; bool CXWindowsUtil::getWindowProperty(Display* display, Window window, @@ -1231,18 +1343,33 @@ CXWindowsUtil::mapUCS4ToKeySym(UInt32 c) } bool -CXWindowsUtil::decomposeKeySym(KeySym keysym, KeySyms& decomposed) +CXWindowsUtil::decomposeKeySymWithDeadKeys(KeySym keysym, KeySyms& decomposed) { // unfortunately, X11 doesn't appear to have any way of // decomposing a keysym into its component keysyms. we'll // use a lookup table for certain character sets. initKeyMaps(); - CKeySymsMap::const_iterator i = s_decomposedKeySyms.find(keysym); - if (i == s_decomposedKeySyms.end()) { - return false; + CKeySymsMap::const_iterator i = s_deadKeyDecomposedKeySyms.find(keysym); + if (i != s_deadKeyDecomposedKeySyms.end()) { + decomposed = i->second; + return true; } - decomposed = i->second; - return true; + return false; +} + +bool +CXWindowsUtil::decomposeKeySymWithCompose(KeySym keysym, KeySyms& decomposed) +{ + // unfortunately, X11 doesn't appear to have any way of + // decomposing a keysym into its component keysyms. we'll + // use a lookup table for certain character sets. + initKeyMaps(); + CKeySymsMap::const_iterator i = i = s_composeDecomposedKeySyms.find(keysym); + if (i != s_composeDecomposedKeySyms.end()) { + decomposed = i->second; + return true; + } + return false; } CString @@ -1296,10 +1423,22 @@ CXWindowsUtil::initKeyMaps() } // fill decomposed key table if not filled yet - if (s_decomposedKeySyms.empty()) { - for (const KeySym* scan = s_rawDecomposeTable; *scan != 0; ++scan) { + if (s_deadKeyDecomposedKeySyms.empty()) { + for (const KeySym* scan = s_rawDeadDecomposeTable; *scan != 0; ++scan) { // add an entry for this keysym - KeySyms& entry = s_decomposedKeySyms[*scan]; + KeySyms& entry = s_deadKeyDecomposedKeySyms[*scan]; + + // add the decomposed keysyms for the keysym + while (*++scan != 0) { + entry.push_back(*scan); + } + } + } + if (s_composeDecomposedKeySyms.empty()) { + for (const KeySym* scan = + s_rawComposedDecomposeTable; *scan != 0; ++scan) { + // add an entry for this keysym + KeySyms& entry = s_composeDecomposedKeySyms[*scan]; // add the decomposed keysyms for the keysym while (*++scan != 0) { diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h index 5765c94a..13e9313e 100644 --- a/lib/platform/CXWindowsUtil.h +++ b/lib/platform/CXWindowsUtil.h @@ -73,13 +73,23 @@ public: */ static KeySym mapUCS4ToKeySym(UInt32); - //! Decompose a KeySym + //! Decompose a KeySym using dead keys /*! Decomposes \c keysym into its component keysyms. All but the last decomposed KeySym are dead keys. Returns true iff the decomposition was successful. */ - static bool decomposeKeySym(KeySym keysym, KeySyms& decomposed); + static bool decomposeKeySymWithDeadKeys(KeySym keysym, + KeySyms& decomposed); + + //! Decompose a KeySym using the compose key + /*! + Decomposes \c keysym into its component keysyms. The first key is + Multi_key and the rest are normal (i.e. not dead) keys. Returns + true iff the decomposition was successful. + */ + static bool decomposeKeySymWithCompose(KeySym keysym, + KeySyms& decomposed); //! Convert Atom to its string /*! @@ -162,7 +172,8 @@ private: static CKeySymMap s_keySymToUCS4; static CUCS4Map s_UCS4ToKeySym; - static CKeySymsMap s_decomposedKeySyms; + static CKeySymsMap s_deadKeyDecomposedKeySyms; + static CKeySymsMap s_composeDecomposedKeySyms; }; #endif From b7f0311eb2fe5f03232668cba8bb7087f7bb7f59 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 6 Nov 2004 16:11:39 +0000 Subject: [PATCH 748/807] Fixed console appearing when running synergy as a service. This was introduced with the change to print system info to the start of the log. This message was printed before the service installed the log handler that directs messages to the event log. --- cmd/synergyc/synergyc.cpp | 6 +++--- cmd/synergys/synergys.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 88097a25..21cc95c3 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -482,9 +482,6 @@ run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) // through the task bar. s_taskBarReceiver = createTaskBarReceiver(&logBuffer); - // identify system - LOG((CLOG_INFO "Synergy client on %s", ARCH->getOSName().c_str())); - // run int result = startup(argc, argv); @@ -602,6 +599,9 @@ parse(int argc, const char* const* argv) // set defaults ARG->m_name = ARCH->getHostName(); + // identify system + LOG((CLOG_INFO "Synergy client on %s", ARCH->getOSName().c_str())); + // parse options int i; for (i = 1; i < argc; ++i) { diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index b61fa463..7e9ac61f 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -573,9 +573,6 @@ run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) // through the task bar. s_taskBarReceiver = createTaskBarReceiver(&logBuffer); - // identify system - LOG((CLOG_INFO "Synergy server on %s", ARCH->getOSName().c_str())); - // run int result = startup(argc, argv); @@ -734,6 +731,9 @@ parse(int argc, const char* const* argv) // set defaults ARG->m_name = ARCH->getHostName(); + // identify system + LOG((CLOG_INFO "Synergy server on %s", ARCH->getOSName().c_str())); + // parse options int i = 1; for (; i < argc; ++i) { From 01dc8fa337804f20826afa5ee21aab0142540650 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 6 Nov 2004 16:13:01 +0000 Subject: [PATCH 749/807] Fixed handling of number pad keys with num-lock off. Was synthesizing events for the numbers even with num-lock off. Now synthesizing the cursor movements. --- lib/platform/CMSWindowsKeyState.cpp | 30 +++++++++++++++++++++++------ lib/platform/CMSWindowsKeyState.h | 1 + 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index 1d47e0e7..50f810e7 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -712,6 +712,7 @@ CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks) : m_desks(desks), m_keyLayout(GetKeyboardLayout(0)) { + // do nothing } CMSWindowsKeyState::~CMSWindowsKeyState() @@ -751,7 +752,6 @@ KeyID CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, KeyModifierMask* maskOut) const { -// FIXME -- look into this // note: known microsoft bugs // Q72583 -- MapVirtualKey() maps keypad keys incorrectly // 95,98: num pad vk code -> invalid scan code @@ -1017,6 +1017,7 @@ CMSWindowsKeyState::doUpdateKeys() { // clear scan code to/from virtual key mapping memset(m_scanCodeToVirtKey, 0, sizeof(m_scanCodeToVirtKey)); + memset(m_scanCodeToVirtKeyNumLock, 0, sizeof(m_scanCodeToVirtKeyNumLock)); memset(m_virtKeyToScanCode, 0, sizeof(m_virtKeyToScanCode)); // add modifiers. note that ModeSwitch is mapped to VK_RMENU and @@ -1076,10 +1077,21 @@ CMSWindowsKeyState::doUpdateKeys() continue; } - // okay, now we have the scan code for the virtual key. - m_scanCodeToVirtKey[button] = i; - m_scanCodeToVirtKey[button2] = i; - m_virtKeyToScanCode[i] = button; + // okay, now we have the scan code for the virtual key. the + // numpad causes some confusion. buttons on the numpad are + // used for two virtual keys (one for num lock off and one + // for num lock on). keep a separate map for virtual keys + // used when num lock is on. + if ((i >= VK_NUMPAD0 && i <= VK_NUMPAD9) || + i == VK_SEPARATOR || i == VK_DECIMAL) { + m_scanCodeToVirtKeyNumLock[button] = i; + m_scanCodeToVirtKeyNumLock[button2] = i; + } + else { + m_scanCodeToVirtKey[button] = i; + m_scanCodeToVirtKey[button2] = i; + } + m_virtKeyToScanCode[i] = button; // if the virtual key is VK_DELETE then use the extended // scan code. this is important for simulating ctrl+alt+del @@ -1117,7 +1129,13 @@ void CMSWindowsKeyState::doFakeKeyEvent(KeyButton button, bool press, bool isAutoRepeat) { - UINT vk = m_scanCodeToVirtKey[button]; + UINT vk = 0; + if ((getActiveModifiers() & KeyModifierNumLock) != 0) { + vk = m_scanCodeToVirtKeyNumLock[button]; + } + if (vk == 0) { + vk = m_scanCodeToVirtKey[button]; + } m_desks->fakeKeyEvent(button, vk, press, isAutoRepeat); } diff --git a/lib/platform/CMSWindowsKeyState.h b/lib/platform/CMSWindowsKeyState.h index d4c7e1e0..c2b7b149 100644 --- a/lib/platform/CMSWindowsKeyState.h +++ b/lib/platform/CMSWindowsKeyState.h @@ -125,6 +125,7 @@ private: HKL m_keyLayout; CString m_keyName; UINT m_scanCodeToVirtKey[512]; + UINT m_scanCodeToVirtKeyNumLock[512]; KeyButton m_virtKeyToScanCode[256]; static const char* s_vkToName[]; From 57fddf4cdc9722f172435acaaf66de12326ce164 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 6 Nov 2004 16:13:52 +0000 Subject: [PATCH 750/807] Fixed screensaver detection on XP. --- lib/arch/CArchMiscWindows.cpp | 25 +++++++++++++++++++++++++ lib/arch/CArchMiscWindows.h | 7 +++++++ lib/platform/CMSWindowsDesks.cpp | 28 +++++++++++++++++++++++++++- lib/platform/CMSWindowsDesks.h | 3 +++ lib/platform/CMSWindowsScreen.cpp | 14 ++++++++++---- lib/platform/CMSWindowsScreen.h | 1 + 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index b4010040..f900f741 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -62,6 +62,31 @@ CArchMiscWindows::isWindows95Family() return result; } +bool +CArchMiscWindows::isWindowsModern() +{ + static bool init = false; + static bool result = false; + + if (!init) { + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + // cannot determine OS; assume not modern + result = false; + } + else { + result = ((version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && + version.dwMajorVersion == 4 && + version.dwMinorVersion > 0) || + (version.dwPlatformId == VER_PLATFORM_WIN32_NT && + version.dwMajorVersion > 4)); + } + init = true; + } + return result; +} + int CArchMiscWindows::runDaemon(RunFunc runFunc) { diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index e55b6dd2..973f9b4e 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -49,6 +49,13 @@ public: */ static bool isWindows95Family(); + //! Test if windows 95, et al. + /*! + Returns true iff the platform is win98 or win2k or higher (i.e. + not windows 95 or windows NT). + */ + static bool isWindowsModern(); + //! Run the daemon /*! Delegates to CArchDaemonWindows. diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index 0a95df8d..ec56d2c1 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -33,6 +33,9 @@ #if !defined(SPI_SETMOUSESPEED) #define SPI_SETMOUSESPEED 113 #endif +#if !defined(SPI_GETSCREENSAVERRUNNING) +#define SPI_GETSCREENSAVERRUNNING 114 +#endif // X button stuff #if !defined(WM_XBUTTONDOWN) @@ -84,6 +87,7 @@ CMSWindowsDesks::CMSWindowsDesks( const IScreenSaver* screensaver, IJob* updateKeys) : m_isPrimary(isPrimary), m_is95Family(CArchMiscWindows::isWindows95Family()), + m_isModernFamily(CArchMiscWindows::isWindowsModern()), m_isOnScreen(m_isPrimary), m_x(0), m_y(0), m_w(0), m_h(0), @@ -622,8 +626,22 @@ CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout) strcmp(className, "ConsoleWindowClass") == 0) { EnableWindow(desk->m_window, TRUE); SetActiveWindow(desk->m_window); + + // force our window to the foreground. we can't + // simply call SetForegroundWindow() because that + // will only alert the user that the window wants + // to be the foreground as of windows 98/2000. we + // have to attach to the thread of the current + // foreground window then call it on our window + // and finally detach the threads. + DWORD thisThread = + GetWindowThreadProcessId(desk->m_window, NULL); + DWORD thatThread = + GetWindowThreadProcessId(foreground, NULL); + AttachThreadInput(thatThread, thisThread, TRUE); + SetForegroundWindow(desk->m_window); + AttachThreadInput(thatThread, thisThread, FALSE); } - LOG((CLOG_DEBUG1 "active window class: %s", className)); } } @@ -901,6 +919,14 @@ void CMSWindowsDesks::handleCheckDesk(const CEvent&, void*) { checkDesk(); + + // also check if screen saver is running if on a modern OS and + // this is the primary screen. + if (m_isPrimary && m_isModernFamily) { + BOOL running; + SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, FALSE); + PostThreadMessage(m_threadID, SYNERGY_MSG_SCREEN_SAVER, running, 0); + } } HDESK diff --git a/lib/platform/CMSWindowsDesks.h b/lib/platform/CMSWindowsDesks.h index df209eca..caa1bb7d 100644 --- a/lib/platform/CMSWindowsDesks.h +++ b/lib/platform/CMSWindowsDesks.h @@ -214,6 +214,9 @@ private: // true if windows 95/98/me bool m_is95Family; + // true if windows 98/2k or higher (i.e. not 95/nt) + bool m_isModernFamily; + // true if mouse has entered the screen bool m_isOnScreen; diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 720b9669..4951dad3 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -94,6 +94,7 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_fixTimer(NULL), m_screensaver(NULL), m_screensaverNotify(false), + m_screensaverActive(false), m_window(NULL), m_nextClipboardWindow(NULL), m_ownClipboard(false), @@ -1133,7 +1134,9 @@ CMSWindowsScreen::onScreensaver(bool activated) } if (activated) { - if (m_screensaver->checkStarted(SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) { + if (!m_screensaverActive && + m_screensaver->checkStarted(SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) { + m_screensaverActive = true; sendEvent(getScreensaverActivatedEvent()); // enable display power down @@ -1141,10 +1144,13 @@ CMSWindowsScreen::onScreensaver(bool activated) } } else { - sendEvent(getScreensaverDeactivatedEvent()); + if (m_screensaverActive) { + m_screensaverActive = false; + sendEvent(getScreensaverDeactivatedEvent()); - // disable display power down - CArchMiscWindows::addBusyState(CArchMiscWindows::kDISPLAY); + // disable display power down + CArchMiscWindows::addBusyState(CArchMiscWindows::kDISPLAY); + } } return true; diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 6fcb691a..2b8b7366 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -238,6 +238,7 @@ private: // screen saver stuff CMSWindowsScreenSaver* m_screensaver; bool m_screensaverNotify; + bool m_screensaverActive; // clipboard stuff. our window is used mainly as a clipboard // owner and as a link in the clipboard viewer chain. From 8ce1e0217a4a68ab2a6617fb3d62369e6ab218d4 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 6 Nov 2004 16:29:06 +0000 Subject: [PATCH 751/807] Attempt to workaround laggy mouse on OS X with linux as server. --- lib/client/CServerProxy.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index e9f91054..a44347db 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -277,6 +277,15 @@ CServerProxy::parseMessage(const UInt8* code) return kUnknown; } + // send a reply. this is intended to work around a delay when + // running a linux server and an OS X (any BSD?) client. the + // client waits to send an ACK (if the system control flag + // net.inet.tcp.delayed_ack is 1) in hopes of piggybacking it + // on a data packet. we provide that packet here. i don't + // know why a delayed ACK should cause the server to wait since + // TCP_NODELAY is enabled. + CProtocolUtil::writef(m_stream, kMsgCNoop); + return kOkay; } From 6e58829c5122f4325cdc64fbf3029eab7d886ba8 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 7 Nov 2004 12:34:01 +0000 Subject: [PATCH 752/807] Updated news and change log for 1.1.9. --- ChangeLog | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++ doc/news.html | 36 +++++ 2 files changed, 416 insertions(+) diff --git a/ChangeLog b/ChangeLog index da5bb3ba..6d3c5a25 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,383 @@ +2004/11/06 16:29:06 crs +lib/client/CServerProxy.cpp + +Attempt to workaround laggy mouse on OS X with linux as server. + +---------- +2004/11/06 16:13:52 crs +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h + +Fixed screensaver detection on XP. + +---------- +2004/11/06 16:13:01 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h + +Fixed handling of number pad keys with num-lock off. Was +synthesizing events for the numbers even with num-lock off. Now +synthesizing the cursor movements. + +---------- +2004/11/06 16:11:39 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Fixed console appearing when running synergy as a service. This +was introduced with the change to print system info to the start +of the log. This message was printed before the service installed +the log handler that directs messages to the event log. + +---------- +2004/11/04 21:26:43 crs +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h + +Added support for X11 compose key (Multi_key). This change fixes +the handling of compose key sequences. The key presses were +suppressed but not the corresponding releases, confusing the +clients. It also adds support for generating keysyms via the +compose key if the necessary dead keys or Mode_switch are not +available. + +---------- +2004/11/02 20:50:36 crs +doc/running.html + +Added documentation for -display option. + +---------- +2004/11/02 20:43:55 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h + +Added -display option for X11 version. + +---------- +2004/11/01 22:26:52 crs +lib/platform/CSynergyHook.cpp + +Reverted change to detach threads in hook DLL. It was breaking +double clicking. + +---------- +2004/11/01 22:26:02 crs +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/resource.h +cmd/synergys/synergys.rc + +Added tray menu item on win32 to force clients to reconnect. + +---------- +2004/11/01 22:25:39 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +configure.in +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchFileWindows.cpp +lib/arch/CArchSystemWindows.cpp +lib/arch/CArchSystemWindows.h +lib/arch/IArchSystem.h +lib/arch/Makefile.am +lib/arch/arch.dsp + +Added operating system identification log message for debugging +purposes. + +---------- +2004/11/01 22:10:34 crs +lib/platform/CMSWindowsDesks.cpp + +Added debugging output to check window class of active window +when leaving screen. This may help determine how to avoid +DOS command prompt windows being in the foreground when leaving +the screen since they suppress handling of the shift key. + +---------- +2004/11/01 18:26:29 crs +lib/platform/CMSWindowsEventQueueBuffer.cpp + +Fixed synergy quiting when powerdvd stops playing a DVD. This may +fix some other bugs that involve unexpectedly quiting. The problem +was that synergy would (cleanly) quit when receiving an event with +a message id of 0 when not running as a service. + +---------- +2004/11/01 18:24:37 crs +lib/platform/CMSWindowsDesks.cpp + +Fixed multimon support for win NT/2000/XP running as client. Mouse +would jump between two points. + +---------- +2004/11/01 18:22:45 crs +lib/platform/CMSWindowsScreenSaver.cpp + +Fixed a resource leak. Also fixed the detection of the screen saver +closing on windows 2000 and XP. + +---------- +2004/11/01 18:21:00 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAdvancedOptions.h +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Added option to set the listen address via the win32 GUI. This +allows the user to specify one (and only one) network interface +to listen on. + +---------- +2004/10/30 16:41:36 crs +lib/platform/CXWindowsKeyState.cpp + +Changed X11 key mapping to fallback to the modifier keysym with +the opposite handedness if the desired handedness is missing. +For example, if Alt_R is unmapped as it often is on keyboards +with Mode_switch, the client will use Alt_L if it is mapped +when told to synthesize Alt_R. This should fix handling of +AltGr sent from a win32 server. AltGr is literally just +Control_L and Alt_R and, previously, an X11 client without +Alt_R mapped would only simulate Control_L. This does not +address the fact that the user may really just want the Alt +modifier; there are already hueristics to attempt to figure +that out. + +---------- +2004/10/30 16:16:32 crs +configure.in +lib/platform/CXWindowsScreenSaver.cpp + +Improved X extension detection in configure and added handling +of dpms.h headers that don't have function prototypes. + +---------- +2004/10/28 21:40:56 crs +configure.in +lib/arch/CArchNetworkBSD.h +lib/common/stdistream.h +lib/common/stdostream.h +lib/common/stdsstream.h +lib/platform/CXWindowsClipboard.h +lib/platform/CXWindowsEventQueueBuffer.h +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h +lib/platform/CXWindowsUtil.h + +Fixed bugs in configuration. Wasn't doing configuration for DPMS +and Xinerama correctly. Also was using '#if defined(...)' instead +of '#if ...' for testing configure macros in some places. This +yields the wrong answer if the macro is set to 0, which means +missing/disabled. + +---------- +2004/10/27 21:46:22 crs +lib/arch/CArchFileUnix.cpp + +Fixed use of freed memory. + +---------- +2004/10/27 21:38:05 crs +lib/platform/CSynergyHook.cpp + +Now detaching hook thread after event processing. This may fix +problems with the Alt key being synthetically down when using +the back and forward bindings on a mouse with extra buttons. + +---------- +2004/10/27 21:29:19 crs +lib/platform/CSynergyHook.cpp + +Fixed bug in mouse wheel handling. Was reacting with mouse wheel +events when receiving any event with message == 0 when the system +doesn't use old style mouse wheel events. Some programs (especially +the flash plugin) would send events with message == 0 causing +undesired wheel scrolling. + +---------- +2004/10/27 21:22:36 crs +lib/platform/COSXScreen.cpp + +Fixed problem with multimonitor on OS X. The bug was simply that +the cursor wasn't being parked in the center of the main screen +but instead at the center of the total display surface. This could +place it off or dangerously close to the edge of the transparent +window that covers the main screen and prevent synergy from capturing +mouse motion. + +---------- +2004/10/24 18:18:21 crs +lib/platform/CXWindowsScreen.cpp + +Added eject key mapping. + +---------- +2004/10/24 18:18:11 crs +lib/platform/CXWindowsUtil.cpp + +Fixed comment. + +---------- +2004/10/24 18:18:01 crs +lib/platform/CXWindowsKeyState.cpp +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h + +Fixed dead key and AltGr+shift handling on X windows. Also fixed +bug with decomposing characters that have no direct key mapping +but do have a direct key mapping for the character with the opposite +case. + +---------- +2004/10/24 18:15:33 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp + +Made OS X key mapping dynamic based on current key layout. It +now includes full support for dead keys and non-ascii glyph keys. + +---------- +2004/10/24 18:14:18 crs +lib/synergy/KeyTypes.h + +Added eject and sleep key IDs. + +---------- +2004/10/24 18:12:38 crs +lib/platform/CMSWindowsKeyState.cpp + +Added VK_SLEEP. + +---------- +2004/10/23 19:43:37 crs +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CScreen.cpp + +Previous half-duplex fix fixed secondary screens with half +duplex keys but broke primary screens. This fixes both and +also ensures that the primary screen updates its shadow toggle +modifier state when leaving so the secondary screens get the +correct toggle modifier state. Together these fix some strange +inconsistencies in toggle state across screens. + +---------- +2004/10/23 18:40:31 crs +lib/synergy/CKeyState.cpp + +Fixed bug in half-duplex keys. Was updating their toggled state +om every release as well as press. + +---------- +2004/10/13 20:39:22 crs +doc/configuration.html + +Fixed typo in the documentation of configuration options. + +---------- +2004/09/29 21:59:26 crs +cmd/synergyc/CClientTaskBarReceiver.cpp +cmd/synergyc/CClientTaskBarReceiver.h +cmd/synergys/CServerTaskBarReceiver.cpp +cmd/synergys/CServerTaskBarReceiver.h +configure.in +lib/arch/CArchMultithreadPosix.cpp +lib/base/CEventQueue.cpp +lib/base/CEventQueue.h +lib/base/IEventQueue.h +lib/io/CStreamFilter.cpp +lib/io/CStreamFilter.h +lib/io/IStream.h +lib/net/CTCPSocket.cpp +lib/net/CTCPSocket.h +lib/net/IDataSocket.h +lib/server/CClientListener.cpp +lib/synergy/CPacketStreamFilter.cpp +lib/synergy/CPacketStreamFilter.h + +Removed recursive mutexes. Simplified stream filters as a side +effect. Removed -D_BSD_SOURCE and -D_XOPEN_SOURCE=500 from +compile since they're not longer necessary. + +---------- +2004/09/28 22:25:35 crs +lib/server/CConfig.cpp + +Now accepting screen names that end in a dot since many OS X +users have misconfigured their systems to have hostnames that +end in a dot. + +---------- +2004/09/28 22:19:24 crs +dist/nullsoft/installer.mak + +Fixed error in win32 installer packaging. + +---------- +2004/09/28 22:19:11 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +doc/configuration.html +doc/faq.html +lib/server/CConfig.cpp +lib/synergy/CScreen.cpp + +Added half-duplex option for scroll lock key. + +---------- +2004/09/27 21:54:49 crs +lib/platform/CXWindowsScreen.cpp +lib/synergy/IPlatformScreen.h + +Fixed compile on gcc 3.4 and later. gcc started doing access checks +for class visibility on pointers to member function 'using the +qualifying scope of the name itself.' what this means is if method +'prot' is declared protected in class A and B inherits from A then +a method in B cannot use &A::prot but can use &B::prot. Synergy +now does this in the one place it had not. + +---------- +2004/09/27 21:23:47 crs +lib/arch/CArchMultithreadPosix.cpp + +Worked around minor gcc 3.3.2 -O3 compiler bug. + +---------- +2004/09/27 21:04:37 crs +cmd/synergys/synergys.cpp + +Server now reports configuration file errors as ERROR level log +messages rather than DEBUG level. Missing files are still reported +as DEBUG level messages since we expect some to be missing. + +---------- +2004/09/27 20:53:54 crs +configure.in +lib/common/Version.h + +Changed version to 1.1.9. Changed configure.in to get version +number from lib/common/Version.h so it only has to be changed +there. + +---------- 2004/08/05 20:42:51 crs dist/nullsoft/synergy.nsi diff --git a/doc/news.html b/doc/news.html index 6ef7f69a..2b96bbd7 100644 --- a/doc/news.html +++ b/doc/news.html @@ -7,6 +7,42 @@

    Synergy News

    +Nov-07-2004 - Synergy 1.1.9 released + +

    +Made following changes: +

    +
      +
    • Fixed compiler error on gcc 3.4 and later +
    • Worked around minor gcc -O3 compiler bug +
    • Now logging system info at startup +
    • Config file errors now logged as errors rather than debug warnings +
    • Added half-duplex scroll lock option +
    • Fixed tracking of half-duplex toggle key state +
    • Now accepting screen names ending in dot (.) for OS X convenience +
    • OS X key mapping now loaded from system resources rather than hard coded +
    • Fixed multimonitor OS X pimary screen bug; multimon OS X should now work +
    • Added experimental workaround for laggy mouse when running linux -> OS X +
    • Fixed bug in win32 installer packaging +
    • Fixed unrequested continuous mouse wheel scrolling on win32 +
    • Added win32 GUI to set server address to listen on +
    • Fixed resource leak on win32 +
    • Fixed screensaver detection on windows 2000 and XP +
    • Fixed flickering mouse on multimon windows NT/2000/XP +
    • Fixed quiting when powerdvd stops playing (may fix other situations, too) +
    • Added tray icon menu item to force clients to reconnect +
    • Fixed handling of number pad keys with num-lock off on win32 +
    • Fixed shift key not working when a console windows has focus on win32 server +
    • Improved configure of Xinerama and DPMS +
    • Improved portability (removed recursive mutexes and _*_SOURCE defines) +
    • Now handling DPMS headers without prototypes +
    • Fixed dead key and AltGr+shift handling on X11 +
    • Fixed use of freed memory on unix +
    • Fixed AltGr mapping to Ctrl and not Ctrl+Alt on X11 without Alt_R mapped +
    • Added -display option for X11 +
    • Added support for X11 compose key (Multi_key) +
    + Aug-05-2004 - Synergy 1.1.8 released

    From cc16298febc1461a3640138dda777ab50a9f4ca6 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 7 Nov 2004 13:11:41 +0000 Subject: [PATCH 753/807] Added new files to xcode project. --- synergy.xcode/project.pbxproj | 392 +++++++++++++++++++++++++++++----- 1 file changed, 337 insertions(+), 55 deletions(-) diff --git a/synergy.xcode/project.pbxproj b/synergy.xcode/project.pbxproj index 9f04402f..ae2d35d4 100644 --- a/synergy.xcode/project.pbxproj +++ b/synergy.xcode/project.pbxproj @@ -881,6 +881,7 @@ buildActionMask = 2147483647; files = ( 4C7D7CDD066319560097CA11, + 6887169B073EC03800C5ABE7, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -1274,15 +1275,6 @@ settings = { }; }; - 4C5E86C00648C653003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchDaemonNone.cpp; - path = lib/arch/CArchDaemonNone.cpp; - refType = 4; - sourceTree = ""; - }; 4C5E86C10648C653003C637B = { fileEncoding = 4; isa = PBXFileReference; @@ -1292,51 +1284,16 @@ refType = 4; sourceTree = ""; }; - 4C5E86C20648C653003C637B = { - fileRef = 4C5E86C00648C653003C637B; - isa = PBXBuildFile; - settings = { - }; - }; 4C5E86C30648C653003C637B = { fileRef = 4C5E86C10648C653003C637B; isa = PBXBuildFile; settings = { }; }; - 4C5E86C40648C6B7003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchTaskBarXWindows.cpp; - path = lib/arch/CArchTaskBarXWindows.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86C50648C6B7003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchTaskBarXWindows.h; - path = lib/arch/CArchTaskBarXWindows.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86C60648C6B7003C637B = { - fileRef = 4C5E86C40648C6B7003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86C70648C6B7003C637B = { - fileRef = 4C5E86C50648C6B7003C637B; - isa = PBXBuildFile; - settings = { - }; - }; 4C5E86CA0648C6FB003C637B = { children = ( 4C7D7CD9066319560097CA11, + 68871699073EC02100C5ABE7, 4C5E86CB0648C725003C637B, ); isa = PBXGroup; @@ -3410,10 +3367,10 @@ children = ( 4C5E86920648C412003C637B, 4C5E86930648C412003C637B, - 4C5E86C00648C653003C637B, - 4C5E86C10648C653003C637B, 4C5E86940648C412003C637B, 4C5E86950648C412003C637B, + 68968F5A073EC484004B2F9B, + 4C5E86C10648C653003C637B, 4C5E86960648C412003C637B, 4C5E86970648C412003C637B, 4C5E86980648C412003C637B, @@ -3428,10 +3385,25 @@ 4C5E86A10648C412003C637B, 4C5E86A20648C412003C637B, 4C5E86A30648C412003C637B, - 4C5E86C40648C6B7003C637B, - 4C5E86C50648C6B7003C637B, + 68871676073EBF6F00C5ABE7, + 68871677073EBF6F00C5ABE7, + 68968F5B073EC484004B2F9B, + 68968F5C073EC484004B2F9B, 4C5E86A40648C412003C637B, 4C5E86A50648C412003C637B, + 68871678073EBF6F00C5ABE7, + 68871679073EBF6F00C5ABE7, + 6887167A073EBF6F00C5ABE7, + 6887167B073EBF6F00C5ABE7, + 6887167C073EBF6F00C5ABE7, + 6887167D073EBF6F00C5ABE7, + 6887167E073EBF6F00C5ABE7, + 6887167F073EBF6F00C5ABE7, + 68871680073EBF6F00C5ABE7, + 68871681073EBF7000C5ABE7, + 68871682073EBF7000C5ABE7, + 68871683073EBF7000C5ABE7, + 68871684073EBF7000C5ABE7, 4C5E87D00648CA75003C637B, 4C5E86A70648C412003C637B, 4C5E86A80648C412003C637B, @@ -3444,8 +3416,10 @@ 4CB437B2063E443800969041 = { buildActionMask = 2147483647; files = ( + 4C7D7CDC066319560097CA11, 4C5E86AA0648C412003C637B, 4C5E86AC0648C412003C637B, + 4C5E86C30648C653003C637B, 4C5E86AE0648C412003C637B, 4C5E86B00648C412003C637B, 4C5E86B20648C412003C637B, @@ -3453,11 +3427,23 @@ 4C5E86B60648C412003C637B, 4C5E86B80648C412003C637B, 4C5E86BA0648C412003C637B, + 68871686073EBF7000C5ABE7, + 68968F5F073EC484004B2F9B, 4C5E86BC0648C412003C637B, + 68871687073EBF7000C5ABE7, + 68871688073EBF7000C5ABE7, + 68871689073EBF7000C5ABE7, + 6887168A073EBF7000C5ABE7, + 6887168B073EBF7000C5ABE7, + 6887168C073EBF7000C5ABE7, + 6887168D073EBF7000C5ABE7, + 6887168E073EBF7000C5ABE7, + 6887168F073EBF7000C5ABE7, + 68871690073EBF7000C5ABE7, + 68871691073EBF7000C5ABE7, + 68871692073EBF7000C5ABE7, + 68871693073EBF7000C5ABE7, 4C5E86BF0648C412003C637B, - 4C5E86C30648C653003C637B, - 4C5E86C70648C6B7003C637B, - 4C7D7CDC066319560097CA11, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -3467,6 +3453,7 @@ files = ( 4C5E86A90648C412003C637B, 4C5E86AB0648C412003C637B, + 68968F5D073EC484004B2F9B, 4C5E86AD0648C412003C637B, 4C5E86AF0648C412003C637B, 4C5E86B10648C412003C637B, @@ -3474,11 +3461,11 @@ 4C5E86B50648C412003C637B, 4C5E86B70648C412003C637B, 4C5E86B90648C412003C637B, + 68871685073EBF7000C5ABE7, + 68968F5E073EC484004B2F9B, 4C5E86BB0648C412003C637B, - 4C5E86BE0648C412003C637B, - 4C5E86C20648C653003C637B, - 4C5E86C60648C6B7003C637B, 4C5E87D10648CA75003C637B, + 4C5E86BE0648C412003C637B, ); isa = PBXSourcesBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -4589,6 +4576,301 @@ settings = { }; }; +//4C0 +//4C1 +//4C2 +//4C3 +//4C4 +//680 +//681 +//682 +//683 +//684 + 68871676073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchSystemUnix.cpp; + path = lib/arch/CArchSystemUnix.cpp; + refType = 4; + sourceTree = ""; + }; + 68871677073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchSystemUnix.h; + path = lib/arch/CArchSystemUnix.h; + refType = 4; + sourceTree = ""; + }; + 68871678073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchConsole.h; + path = lib/arch/IArchConsole.h; + refType = 4; + sourceTree = ""; + }; + 68871679073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchDaemon.h; + path = lib/arch/IArchDaemon.h; + refType = 4; + sourceTree = ""; + }; + 6887167A073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchFile.h; + path = lib/arch/IArchFile.h; + refType = 4; + sourceTree = ""; + }; + 6887167B073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchLog.h; + path = lib/arch/IArchLog.h; + refType = 4; + sourceTree = ""; + }; + 6887167C073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchMultithread.h; + path = lib/arch/IArchMultithread.h; + refType = 4; + sourceTree = ""; + }; + 6887167D073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchNetwork.h; + path = lib/arch/IArchNetwork.h; + refType = 4; + sourceTree = ""; + }; + 6887167E073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchSleep.h; + path = lib/arch/IArchSleep.h; + refType = 4; + sourceTree = ""; + }; + 6887167F073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchString.h; + path = lib/arch/IArchString.h; + refType = 4; + sourceTree = ""; + }; + 68871680073EBF6F00C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchSystem.h; + path = lib/arch/IArchSystem.h; + refType = 4; + sourceTree = ""; + }; + 68871681073EBF7000C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchTaskBar.h; + path = lib/arch/IArchTaskBar.h; + refType = 4; + sourceTree = ""; + }; + 68871682073EBF7000C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchTaskBarReceiver.h; + path = lib/arch/IArchTaskBarReceiver.h; + refType = 4; + sourceTree = ""; + }; + 68871683073EBF7000C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = IArchTime.h; + path = lib/arch/IArchTime.h; + refType = 4; + sourceTree = ""; + }; + 68871684073EBF7000C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = XArch.h; + path = lib/arch/XArch.h; + refType = 4; + sourceTree = ""; + }; + 68871685073EBF7000C5ABE7 = { + fileRef = 68871676073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 68871686073EBF7000C5ABE7 = { + fileRef = 68871677073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 68871687073EBF7000C5ABE7 = { + fileRef = 68871678073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 68871688073EBF7000C5ABE7 = { + fileRef = 68871679073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 68871689073EBF7000C5ABE7 = { + fileRef = 6887167A073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 6887168A073EBF7000C5ABE7 = { + fileRef = 6887167B073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 6887168B073EBF7000C5ABE7 = { + fileRef = 6887167C073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 6887168C073EBF7000C5ABE7 = { + fileRef = 6887167D073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 6887168D073EBF7000C5ABE7 = { + fileRef = 6887167E073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 6887168E073EBF7000C5ABE7 = { + fileRef = 6887167F073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 6887168F073EBF7000C5ABE7 = { + fileRef = 68871680073EBF6F00C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 68871690073EBF7000C5ABE7 = { + fileRef = 68871681073EBF7000C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 68871691073EBF7000C5ABE7 = { + fileRef = 68871682073EBF7000C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 68871692073EBF7000C5ABE7 = { + fileRef = 68871683073EBF7000C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 68871693073EBF7000C5ABE7 = { + fileRef = 68871684073EBF7000C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 68871699073EC02100C5ABE7 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MacOSXPrecomp.h; + path = lib/common/MacOSXPrecomp.h; + refType = 4; + sourceTree = ""; + }; + 6887169B073EC03800C5ABE7 = { + fileRef = 68871699073EC02100C5ABE7; + isa = PBXBuildFile; + settings = { + }; + }; + 68968F5A073EC484004B2F9B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchDaemonNone.cpp; + path = lib/arch/CArchDaemonNone.cpp; + refType = 4; + sourceTree = ""; + }; + 68968F5B073EC484004B2F9B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.cpp.cpp; + name = CArchTaskBarXWindows.cpp; + path = lib/arch/CArchTaskBarXWindows.cpp; + refType = 4; + sourceTree = ""; + }; + 68968F5C073EC484004B2F9B = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = CArchTaskBarXWindows.h; + path = lib/arch/CArchTaskBarXWindows.h; + refType = 4; + sourceTree = ""; + }; + 68968F5D073EC484004B2F9B = { + fileRef = 68968F5A073EC484004B2F9B; + isa = PBXBuildFile; + settings = { + }; + }; + 68968F5E073EC484004B2F9B = { + fileRef = 68968F5B073EC484004B2F9B; + isa = PBXBuildFile; + settings = { + }; + }; + 68968F5F073EC484004B2F9B = { + fileRef = 68968F5C073EC484004B2F9B; + isa = PBXBuildFile; + settings = { + }; + }; }; rootObject = 08FB7793FE84155DC02AAC07; } From ca23cd031ff486f64e23d4dcd673c04956a9754d Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 9 Nov 2004 18:31:25 +0000 Subject: [PATCH 754/807] Added forgotten files. Added define to common.h for OS X XCode build to get uname(). --- lib/arch/CArchSystemUnix.cpp | 50 ++++++++++++++++++++++++++++++++++++ lib/arch/CArchSystemUnix.h | 32 +++++++++++++++++++++++ lib/common/common.h | 1 + 3 files changed, 83 insertions(+) create mode 100644 lib/arch/CArchSystemUnix.cpp create mode 100644 lib/arch/CArchSystemUnix.h diff --git a/lib/arch/CArchSystemUnix.cpp b/lib/arch/CArchSystemUnix.cpp new file mode 100644 index 00000000..541038db --- /dev/null +++ b/lib/arch/CArchSystemUnix.cpp @@ -0,0 +1,50 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchSystemUnix.h" +#include + +// +// CArchSystemUnix +// + +CArchSystemUnix::CArchSystemUnix() +{ + // do nothing +} + +CArchSystemUnix::~CArchSystemUnix() +{ + // do nothing +} + +std::string +CArchSystemUnix::getOSName() const +{ +#if defined(HAVE_SYS_UTSNAME_H) + struct utsname info; + if (uname(&info) == 0) { + std::string msg; + msg += info.sysname; + msg += " "; + msg += info.release; + msg += " "; + msg += info.version; + msg += " "; + msg += info.machine; + return msg; + } +#endif + return "Unix "; +} diff --git a/lib/arch/CArchSystemUnix.h b/lib/arch/CArchSystemUnix.h new file mode 100644 index 00000000..525aed1c --- /dev/null +++ b/lib/arch/CArchSystemUnix.h @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CARCHSYSTEMUNIX_H +#define CARCHSYSTEMUNIX_H + +#include "IArchSystem.h" + +#define ARCH_SYSTEM CArchSystemUnix + +//! Unix implementation of IArchString +class CArchSystemUnix : public IArchSystem { +public: + CArchSystemUnix(); + virtual ~CArchSystemUnix(); + + // IArchSystem overrides + virtual std::string getOSName() const; +}; + +#endif diff --git a/lib/common/common.h b/lib/common/common.h index 1e406693..c8f26831 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -57,6 +57,7 @@ # define HAVE_SYS_STAT_H 1 # define HAVE_SYS_TIME_H 1 # define HAVE_SYS_TYPES_H 1 +# define HAVE_SYS_UTSNAME_H 1 # define HAVE_UNISTD_H 1 # define HAVE_VSNPRINTF 1 # define HAVE_WCHAR_H 1 From 73488bc5b41293dd2a2e34be73f1597e0548dd4c Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 9 Nov 2004 18:31:54 +0000 Subject: [PATCH 755/807] Moved log message that prints system info to after installation of user requested log level so it can be filtered. --- cmd/synergyc/synergyc.cpp | 6 +++--- cmd/synergys/synergys.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 21cc95c3..2aa076fa 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -599,9 +599,6 @@ parse(int argc, const char* const* argv) // set defaults ARG->m_name = ARCH->getHostName(); - // identify system - LOG((CLOG_INFO "Synergy client on %s", ARCH->getOSName().c_str())); - // parse options int i; for (i = 1; i < argc; ++i) { @@ -726,6 +723,9 @@ parse(int argc, const char* const* argv) ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); bye(kExitArgs); } + + // identify system + LOG((CLOG_INFO "Synergy client on %s", ARCH->getOSName().c_str())); } diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 7e9ac61f..eae1ece4 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -731,9 +731,6 @@ parse(int argc, const char* const* argv) // set defaults ARG->m_name = ARCH->getHostName(); - // identify system - LOG((CLOG_INFO "Synergy server on %s", ARCH->getOSName().c_str())); - // parse options int i = 1; for (; i < argc; ++i) { @@ -854,6 +851,9 @@ parse(int argc, const char* const* argv) ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); bye(kExitArgs); } + + // identify system + LOG((CLOG_INFO "Synergy server on %s", ARCH->getOSName().c_str())); } static From 6332f879fc0a767577cc5382400af30e23e754ca Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 9 Nov 2004 18:38:14 +0000 Subject: [PATCH 756/807] Added synergy version number to first log message. --- cmd/synergys/synergys.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index eae1ece4..7f40264f 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -853,7 +853,7 @@ parse(int argc, const char* const* argv) } // identify system - LOG((CLOG_INFO "Synergy server on %s", ARCH->getOSName().c_str())); + LOG((CLOG_INFO "Synergy server %s on %s", kVersion, ARCH->getOSName().c_str())); } static From 99da35de61a2d5ba402b75bc36b03bb7be382d5e Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 9 Nov 2004 18:38:52 +0000 Subject: [PATCH 757/807] Added synergy version number to first log message. --- cmd/synergyc/synergyc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 2aa076fa..c214034c 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -725,7 +725,7 @@ parse(int argc, const char* const* argv) } // identify system - LOG((CLOG_INFO "Synergy client on %s", ARCH->getOSName().c_str())); + LOG((CLOG_INFO "Synergy client %s on %s", kVersion, ARCH->getOSName().c_str())); } From fb266a00fddae44e3e50b9eb81f9196b21b4c48f Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 9 Nov 2004 18:42:47 +0000 Subject: [PATCH 758/807] Changed version to 1.1.10. --- lib/common/Version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/Version.h b/lib/common/Version.h index bcf037a1..a975e37f 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.9" +# define VERSION "1.1.10" #endif // important strings From 57e985189443826f0cd083471aeb0011a2976df6 Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 9 Nov 2004 20:05:33 +0000 Subject: [PATCH 759/807] Fixed modifier handling on OSX client. Had hardcoded one set of modifiers for all keys for testing purposes and forgotton to fix that. Now choosing required modifiers per key. This fixes shift+arrow keys suppressing the shift key and, i think, the option key not working. --- lib/platform/COSXKeyState.cpp | 64 ++++++++++++++++++++--------------- lib/platform/COSXKeyState.h | 8 ++++- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index 9aca430d..ed28eac1 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -264,24 +264,13 @@ COSXKeyState::mapKey(Keystrokes& keys, KeyID id, // ensure we adjust the right modifiers for remaining dead keys // and the final key. - // FIXME -- required modifier masks. we should determine this for - // each key when parsing the layout resource. for now we'll just - // force shift, option and caps-lock to always match exactly in - // addition to whatever other modifiers the key needs. -// FIXME -- this doesn't work. it forces modifiers when modifiers are -// pressed (making having two modifiers at once impossible). - static const KeyModifierMask requiredMask = - KeyModifierShift | - KeyModifierSuper | - KeyModifierCapsLock; - // add dead keys for (size_t i = 0; i < sequence.size() - 1; ++i) { // simulate press KeyButton keyButton = - addKeystrokes(keys, sequence[i].first, - sequence[i].second, - sequence[i].second | requiredMask, false); + addKeystrokes(keys, sequence[i].m_button, + sequence[i].m_requiredState, + sequence[i].m_requiredMask, false); // simulate release Keystroke keystroke; @@ -292,9 +281,9 @@ COSXKeyState::mapKey(Keystrokes& keys, KeyID id, } // add final key - return addKeystrokes(keys, sequence.back().first, - sequence.back().second, - sequence.back().second | requiredMask, + return addKeystrokes(keys, sequence.back().m_button, + sequence.back().m_requiredState, + sequence.back().m_requiredMask, isAutoRepeat); } @@ -433,7 +422,7 @@ COSXKeyState::addKeyButton(KeyButtons& keys, KeyID id) const if (keyIndex == m_keyMap.end()) { return; } - keys.push_back(keyIndex->second[0].first); + keys.push_back(keyIndex->second[0].m_button); } void @@ -473,7 +462,7 @@ COSXKeyState::handleModifierKey(void* target, KeyID id, bool down) if (keyIndex == m_keyMap.end()) { return; } - KeyButton button = keyIndex->second[0].first; + KeyButton button = keyIndex->second[0].m_button; setKeyDown(button, down); sendKeyEvent(target, down, false, id, getActiveModifiers(), 0, button); } @@ -513,13 +502,16 @@ COSXKeyState::fillSpecialKeys(CKeyIDMap& keyMap, CVirtualKeyMap& virtualKeyMap) const { // FIXME -- would like to avoid hard coded tables + CKeyEventInfo info; for (UInt32 i = 0; i < sizeof(s_controlKeys) / sizeof(s_controlKeys[0]); ++i) { const CKeyEntry& entry = s_controlKeys[i]; KeyID keyID = entry.m_keyID; - KeyButton keyButton = mapVirtualKeyToKeyButton(entry.m_virtualKey); + info.m_button = mapVirtualKeyToKeyButton(entry.m_virtualKey); + info.m_requiredMask = 0; + info.m_requiredState = 0; if (keyMap.count(keyID) == 0) { - keyMap[keyID].push_back(std::make_pair(keyButton, 0)); + keyMap[keyID].push_back(info); } if (virtualKeyMap.count(entry.m_virtualKey) == 0) { virtualKeyMap[entry.m_virtualKey] = entry.m_keyID; @@ -535,7 +527,7 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const CKCHRResource* r = m_KCHRResource; // build non-composed keys to virtual keys mapping - std::map > vkMap; + std::map vkMap; for (SInt32 i = 0; i < r->m_numTables; ++i) { // determine the modifier keys for table i KeyModifierMask mask = @@ -546,9 +538,18 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const // get character UInt8 c = r->m_characterTables[i][j]; + // save key info + // FIXME -- should set only those bits in m_requiredMask that + // correspond to modifiers that are truly necessary to + // generate the character. this mostly works as-is, though. + CKeyEventInfo info; + info.m_button = mapVirtualKeyToKeyButton(j); + info.m_requiredMask = mask; + info.m_requiredState = mask; + // save character to virtual key mapping if (keyMap.count(c) == 0) { - vkMap[c] = std::make_pair(mapVirtualKeyToKeyButton(j), mask); + vkMap[c] = info; } // skip non-glyph character @@ -565,8 +566,7 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const } // save entry for character - keyMap[keyID].push_back(std::make_pair( - mapVirtualKeyToKeyButton(j), mask)); + keyMap[keyID].push_back(info); } } @@ -602,8 +602,18 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const // to the uncomposed character. if (vkMap.count(dkr->m_completion[j][0]) != 0) { CKeySequence& sequence = keyMap[keyID]; - sequence.push_back(std::make_pair( - mapVirtualKeyToKeyButton(dkr->m_virtualKey), mask)); + + // save key info + // FIXME -- should set only those bits in m_requiredMask that + // correspond to modifiers that are truly necessary to + // generate the character. this mostly works as-is, though. + CKeyEventInfo info; + info.m_button = mapVirtualKeyToKeyButton( + dkr->m_virtualKey); + info.m_requiredMask = mask; + info.m_requiredState = mask; + + sequence.push_back(info); sequence.push_back(vkMap[dkr->m_completion[j][0]]); } } diff --git a/lib/platform/COSXKeyState.h b/lib/platform/COSXKeyState.h index 13e11642..e7972a73 100644 --- a/lib/platform/COSXKeyState.h +++ b/lib/platform/COSXKeyState.h @@ -69,7 +69,13 @@ protected: bool isAutoRepeat) const; private: - typedef std::vector > CKeySequence; + struct CKeyEventInfo { + public: + KeyButton m_button; + KeyModifierMask m_requiredMask; + KeyModifierMask m_requiredState; + }; + typedef std::vector CKeySequence; typedef std::map CKeyIDMap; typedef std::map CVirtualKeyMap; From d8b6fab8bbb3a13f661dc1e866c5be613801a41e Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 10 Nov 2004 19:11:33 +0000 Subject: [PATCH 760/807] Fixed typo. --- lib/platform/CXWindowsUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index b556da00..440be0ed 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -1364,7 +1364,7 @@ CXWindowsUtil::decomposeKeySymWithCompose(KeySym keysym, KeySyms& decomposed) // decomposing a keysym into its component keysyms. we'll // use a lookup table for certain character sets. initKeyMaps(); - CKeySymsMap::const_iterator i = i = s_composeDecomposedKeySyms.find(keysym); + CKeySymsMap::const_iterator i = s_composeDecomposedKeySyms.find(keysym); if (i != s_composeDecomposedKeySyms.end()) { decomposed = i->second; return true; From 6ea96719abd8c3fdff143a9bf10e474923bac962 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 10 Nov 2004 21:00:30 +0000 Subject: [PATCH 761/807] Made condition variable data volatile. This will hopefully fix an strange deadlock seen on OSX. The CSocketMultiplexer deadlocks with two threads, one waiting for m_polling to become false and the other waiting for m_pollable to become true. The weird part is that they're both false so the first thread should proceed. It either didn't receive the broadcast when m_polling went to false or it's not really checking the actual value of that flag. I can't see how the former is possible and this change fixes the latter. --- lib/mt/CCondVar.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/mt/CCondVar.h b/lib/mt/CCondVar.h index 78fbfe90..a04102c4 100644 --- a/lib/mt/CCondVar.h +++ b/lib/mt/CCondVar.h @@ -160,12 +160,12 @@ public: Get the variable's value. The condition variable should be locked before calling this method. */ - operator const T&() const; + operator const volatile T&() const; //@} private: - T m_data; + volatile T m_data; }; template @@ -199,8 +199,7 @@ CCondVar::~CCondVar() template inline CCondVar& -CCondVar::operator=( - const CCondVar& cv) +CCondVar::operator=(const CCondVar& cv) { m_data = cv.m_data; return *this; @@ -209,8 +208,7 @@ CCondVar::operator=( template inline CCondVar& -CCondVar::operator=( - const T& data) +CCondVar::operator=(const T& data) { m_data = data; return *this; @@ -218,7 +216,7 @@ CCondVar::operator=( template inline -CCondVar::operator const T&() const +CCondVar::operator const volatile T&() const { return m_data; } From c1354320404c02a7bfb48e9ab372dfb3cff17f0c Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 11 Nov 2004 19:17:03 +0000 Subject: [PATCH 762/807] Changed scheme used to lock the socket multiplexer's job list. I think the new scheme is easier to understand. It should have exactly the same behavior. --- lib/net/CSocketMultiplexer.cpp | 136 ++++++++++++++++++++++----------- lib/net/CSocketMultiplexer.h | 20 ++++- 2 files changed, 110 insertions(+), 46 deletions(-) diff --git a/lib/net/CSocketMultiplexer.cpp b/lib/net/CSocketMultiplexer.cpp index 1a9e11b6..742198f9 100644 --- a/lib/net/CSocketMultiplexer.cpp +++ b/lib/net/CSocketMultiplexer.cpp @@ -32,10 +32,13 @@ CSocketMultiplexer* CSocketMultiplexer::s_instance = NULL; CSocketMultiplexer::CSocketMultiplexer() : m_mutex(new CMutex), - m_pollable(new CCondVar(m_mutex, false)), - m_polling(new CCondVar(m_mutex, false)), m_thread(NULL), - m_update(false) + m_update(false), + m_jobsReady(new CCondVar(m_mutex, false)), + m_jobListLock(new CCondVar(m_mutex, false)), + m_jobListLockLocked(new CCondVar(m_mutex, false)), + m_jobListLocker(NULL), + m_jobListLockLocker(NULL) { assert(s_instance == NULL); @@ -57,8 +60,11 @@ CSocketMultiplexer::~CSocketMultiplexer() m_thread->unblockPollSocket(); m_thread->wait(); delete m_thread; - delete m_polling; - delete m_pollable; + delete m_jobsReady; + delete m_jobListLock; + delete m_jobListLockLocked; + delete m_jobListLocker; + delete m_jobListLockLocker; delete m_mutex; // clean up jobs @@ -82,18 +88,14 @@ CSocketMultiplexer::addSocket(ISocket* socket, ISocketMultiplexerJob* job) assert(socket != NULL); assert(job != NULL); - CLock lock(m_mutex); - - // prevent service thread from restarting poll - *m_pollable = false; + // prevent other threads from locking the job list + lockJobListLock(); // break thread out of poll m_thread->unblockPollSocket(); - // wait for poll to finish - while (*m_polling) { - m_polling->wait(); - } + // lock the job list + lockJobList(); // insert/replace job CSocketJobMap::iterator i = m_socketJobMap.find(socket); @@ -114,9 +116,8 @@ CSocketMultiplexer::addSocket(ISocket* socket, ISocketMultiplexerJob* job) m_update = true; } - // there must be at least one socket so we can poll now - *m_pollable = true; - m_pollable->broadcast(); + // unlock the job list + unlockJobList(); } void @@ -124,18 +125,14 @@ CSocketMultiplexer::removeSocket(ISocket* socket) { assert(socket != NULL); - CLock lock(m_mutex); - - // prevent service thread from restarting poll - *m_pollable = false; + // prevent other threads from locking the job list + lockJobListLock(); // break thread out of poll m_thread->unblockPollSocket(); - // wait until thread finishes poll - while (*m_polling) { - m_polling->wait(); - } + // lock the job list + lockJobList(); // remove job. rather than removing it from the map we put NULL // in the list instead so the order of jobs in the list continues @@ -149,8 +146,8 @@ CSocketMultiplexer::removeSocket(ISocket* socket) } } - *m_pollable = true; - m_pollable->broadcast(); + // unlock the job list + unlockJobList(); } void @@ -162,22 +159,18 @@ CSocketMultiplexer::serviceThread(void*) // service the connections for (;;) { CThread::testCancel(); + + // wait until there are jobs to handle { CLock lock(m_mutex); - - // wait until pollable - while (!(bool)*m_pollable) { - m_pollable->wait(); + while (!(bool)*m_jobsReady) { + m_jobsReady->wait(); } - - // now we're polling - *m_polling = true; - m_polling->broadcast(); } - // we're now the only thread that can access m_sockets and - // m_update because m_polling and m_pollable are both true. - // therefore, we don't need to hold the mutex. + // lock the job list + lockJobListLock(); + lockJobList(); // collect poll entries if (m_update) { @@ -251,7 +244,6 @@ CSocketMultiplexer::serviceThread(void*) } // delete any removed socket jobs - CLock lock(m_mutex); for (CSocketJobMap::iterator i = m_socketJobMap.begin(); i != m_socketJobMap.end();) { if (*(i->second) == NULL) { @@ -262,13 +254,9 @@ CSocketMultiplexer::serviceThread(void*) ++i; } } - if (m_socketJobMap.empty()) { - *m_pollable = false; - } - // done polling - *m_polling = false; - m_polling->broadcast(); + // unlock the job list + unlockJobList(); } } @@ -304,3 +292,63 @@ CSocketMultiplexer::deleteCursor(CJobCursor cursor) CLock lock(m_mutex); m_socketJobs.erase(cursor); } + +void +CSocketMultiplexer::lockJobListLock() +{ + CLock lock(m_mutex); + + // wait for the lock on the lock + while (*m_jobListLockLocked) { + m_jobListLockLocked->wait(); + } + + // take ownership of the lock on the lock + *m_jobListLockLocked = true; + m_jobListLockLocker = new CThread(CThread::getCurrentThread()); +} + +void +CSocketMultiplexer::lockJobList() +{ + CLock lock(m_mutex); + + // make sure we're the one that called lockJobListLock() + assert(*m_jobListLockLocker == CThread::getCurrentThread()); + + // wait for the job list lock + while (*m_jobListLock) { + m_jobListLock->wait(); + } + + // take ownership of the lock + *m_jobListLock = true; + m_jobListLocker = m_jobListLockLocker; + m_jobListLockLocker = NULL; + + // release the lock on the lock + *m_jobListLockLocked = false; + m_jobListLockLocked->broadcast(); +} + +void +CSocketMultiplexer::unlockJobList() +{ + CLock lock(m_mutex); + + // make sure we're the one that called lockJobList() + assert(*m_jobListLocker == CThread::getCurrentThread()); + + // release the lock + delete m_jobListLocker; + m_jobListLocker = NULL; + *m_jobListLock = false; + m_jobListLock->signal(); + + // set new jobs ready state + bool isReady = !m_socketJobMap.empty(); + if (*m_jobsReady != isReady) { + *m_jobsReady = isReady; + m_jobsReady->signal(); + } +} diff --git a/lib/net/CSocketMultiplexer.h b/lib/net/CSocketMultiplexer.h index 8abd57ba..bb96ca70 100644 --- a/lib/net/CSocketMultiplexer.h +++ b/lib/net/CSocketMultiplexer.h @@ -78,12 +78,28 @@ private: CJobCursor nextCursor(CJobCursor); void deleteCursor(CJobCursor); + // lock out locking the job list. this blocks if another thread + // has already locked out locking. once it returns, only the + // calling thread will be able to lock the job list after any + // current lock is released. + void lockJobListLock(); + + // lock the job list. this blocks if the job list is already + // locked. the calling thread must have called requestJobLock. + void lockJobList(); + + // unlock the job list and the lock out on locking. + void unlockJobList(); + private: CMutex* m_mutex; - CCondVar* m_pollable; - CCondVar* m_polling; CThread* m_thread; bool m_update; + CCondVar* m_jobsReady; + CCondVar* m_jobListLock; + CCondVar* m_jobListLockLocked; + CThread* m_jobListLocker; + CThread* m_jobListLockLocker; CSocketJobs m_socketJobs; CSocketJobMap m_socketJobMap; From 014578b875a8bb89c31ce40fbd6b37e54ec9a353 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 11 Nov 2004 19:23:14 +0000 Subject: [PATCH 763/807] Fixed a serious flaw in wrapper for posix condition variable wait function. Because synergy doesn't use posix cancellation, it cannot wake up a thread waiting on a condition variable. So the wrapper would wake up periodically to test if the thread was cancelled (according to synergy's cancellation state) then go back to waiting. Well the condition could be signalled while we're testing and be lost and we'd never return from the wait. So now we wake up after a maximum timeout and return to the caller. The caller must check for this spurious wakeup but all callers should do this anyway so we're okay there. --- lib/arch/CArchMultithreadPosix.cpp | 90 ++++++++++-------------------- 1 file changed, 30 insertions(+), 60 deletions(-) diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 64baf6af..5750b51d 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -213,73 +213,43 @@ bool CArchMultithreadPosix::waitCondVar(CArchCond cond, CArchMutex mutex, double timeout) { + // we can't wait on a condition variable and also wake it up for + // cancellation since we don't use posix cancellation. so we + // must wake up periodically to check for cancellation. we + // can't simply go back to waiting after the check since the + // condition may have changed and we'll have lost the signal. + // so we have to return to the caller. since the caller will + // always check for spurious wakeups the only drawback here is + // performance: we're waking up a lot more than desired. + static const double maxCancellationLatency = 0.1; + if (timeout < 0.0 || timeout > maxCancellationLatency) { + timeout = maxCancellationLatency; + } + + // see if we should cancel this thread + testCancelThread(); + // get final time struct timeval now; gettimeofday(&now, NULL); struct timespec finalTime; - finalTime.tv_sec = now.tv_sec; - finalTime.tv_nsec = now.tv_usec * 1000; - if (timeout >= 0.0) { - const long timeout_sec = (long)timeout; - const long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec)); - finalTime.tv_sec += timeout_sec; - finalTime.tv_nsec += timeout_nsec; - if (finalTime.tv_nsec >= 1000000000) { - finalTime.tv_nsec -= 1000000000; - finalTime.tv_sec += 1; - } + finalTime.tv_sec = now.tv_sec; + finalTime.tv_nsec = now.tv_usec * 1000; + long timeout_sec = (long)timeout; + long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec)); + finalTime.tv_sec += timeout_sec; + finalTime.tv_nsec += timeout_nsec; + if (finalTime.tv_nsec >= 1000000000) { + finalTime.tv_nsec -= 1000000000; + finalTime.tv_sec += 1; } - // repeat until we reach the final time - int status; - for (;;) { - // get current time - gettimeofday(&now, NULL); - struct timespec endTime; - endTime.tv_sec = now.tv_sec; - endTime.tv_nsec = now.tv_usec * 1000; + // wait + int status = pthread_cond_timedwait(&cond->m_cond, + &mutex->m_mutex, &finalTime); - // done if past final timeout - if (timeout >= 0.0) { - if (endTime.tv_sec > finalTime.tv_sec || - (endTime.tv_sec == finalTime.tv_sec && - endTime.tv_nsec >= finalTime.tv_nsec)) { - status = ETIMEDOUT; - break; - } - } - - // compute the next timeout - endTime.tv_nsec += 50000000; - if (endTime.tv_nsec >= 1000000000) { - endTime.tv_nsec -= 1000000000; - endTime.tv_sec += 1; - } - - // don't wait past final timeout - if (timeout >= 0.0) { - if (endTime.tv_sec > finalTime.tv_sec || - (endTime.tv_sec == finalTime.tv_sec && - endTime.tv_nsec >= finalTime.tv_nsec)) { - endTime = finalTime; - } - } - - // see if we should cancel this thread - testCancelThread(); - - // wait - status = pthread_cond_timedwait(&cond->m_cond, - &mutex->m_mutex, &endTime); - - // check for cancel again - testCancelThread(); - - // check wait status - if (status != ETIMEDOUT && status != EINTR) { - break; - } - } + // check for cancel again + testCancelThread(); switch (status) { case 0: From ee7dcc1e83df80d45fb8475a26eff7434fd17c19 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 12 Nov 2004 15:50:04 +0000 Subject: [PATCH 764/807] Now suppressing shift key when caps-lock is enabled on OSX. This fixes handling of, say, Command+N with caps-lock enabled which was being synthesized as Command+Shift+N. --- lib/platform/COSXKeyState.cpp | 29 +++++++++++++++++++++++------ lib/platform/COSXKeyState.h | 9 +++++++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index ed28eac1..39267232 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -197,8 +197,8 @@ COSXKeyState::doUpdateKeys() { // save key mapping m_keyMap.clear(); - if (!filluchrKeysMap(m_keyMap)) { - fillKCHRKeysMap(m_keyMap); + if (!filluchrKeysMap(m_keyMap, m_capsLockSet)) { + fillKCHRKeysMap(m_keyMap, m_capsLockSet); } fillSpecialKeys(m_keyMap, m_virtualKeyMap); @@ -254,6 +254,12 @@ COSXKeyState::mapKey(Keystrokes& keys, KeyID id, return 0; } + // if the virtual key is caps-lock sensitive then suppress shift + KeyModifierMask mask = ~0; + if (m_capsLockSet.count(id) != 0) { + mask &= ~KeyModifierShift; + } + // FIXME -- for both calls to addKeystrokes below we'd prefer to use // a required mask that generates the same character but matches // the desiredMask as closely as possible. @@ -283,7 +289,7 @@ COSXKeyState::mapKey(Keystrokes& keys, KeyID id, // add final key return addKeystrokes(keys, sequence.back().m_button, sequence.back().m_requiredState, - sequence.back().m_requiredMask, + sequence.back().m_requiredMask & mask, isAutoRepeat); } @@ -520,12 +526,23 @@ COSXKeyState::fillSpecialKeys(CKeyIDMap& keyMap, } bool -COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const +COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const { assert(m_KCHRResource != NULL); CKCHRResource* r = m_KCHRResource; + // note caps-lock sensitive keys + SInt32 uIndex = r->m_tableSelectionIndex[0]; + SInt32 clIndex = r->m_tableSelectionIndex[alphaLock >> 8]; + for (SInt32 j = 0; j < 128; ++j) { + UInt8 c = r->m_characterTables[clIndex][j]; + if (r->m_characterTables[uIndex][j] != c) { + KeyID keyID = charToKeyID(c); + capsLockSet.insert(keyID); + } + } + // build non-composed keys to virtual keys mapping std::map vkMap; for (SInt32 i = 0; i < r->m_numTables; ++i) { @@ -548,7 +565,7 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const info.m_requiredState = mask; // save character to virtual key mapping - if (keyMap.count(c) == 0) { + if (vkMap.count(c) == 0) { vkMap[c] = info; } @@ -628,7 +645,7 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const } bool -COSXKeyState::filluchrKeysMap(CKeyIDMap&) const +COSXKeyState::filluchrKeysMap(CKeyIDMap&, CKeySet&) const { // FIXME -- implement this return false; diff --git a/lib/platform/COSXKeyState.h b/lib/platform/COSXKeyState.h index e7972a73..657c9b76 100644 --- a/lib/platform/COSXKeyState.h +++ b/lib/platform/COSXKeyState.h @@ -17,6 +17,7 @@ #include "CKeyState.h" #include "stdmap.h" +#include "stdset.h" #include "stdvector.h" #include @@ -78,6 +79,7 @@ private: typedef std::vector CKeySequence; typedef std::map CKeyIDMap; typedef std::map CVirtualKeyMap; + typedef std::set CKeySet; KeyButton addKeystrokes(Keystrokes& keys, KeyButton keyButton, @@ -107,13 +109,15 @@ private: // map maps each KeyID to the sequence of keys (with modifiers) // that would have to be synthesized to generate the KeyID character. // Returns false iff no KCHR resource was found. - bool fillKCHRKeysMap(CKeyIDMap& keyMap) const; + bool fillKCHRKeysMap(CKeyIDMap& keyMap, + CKeySet& capsLockSet) const; // Convert the uchr resource to a KeyID to key sequence map. the // map maps each KeyID to the sequence of keys (with modifiers) // that would have to be synthesized to generate the KeyID character. // Returns false iff no uchr resource was found. - bool filluchrKeysMap(CKeyIDMap& keyMap) const; + bool filluchrKeysMap(CKeyIDMap& keyMap, + CKeySet& capsLockSet) const; // Maps an OS X virtual key id to a KeyButton. This simply remaps // the ids so we don't use KeyButton 0. @@ -160,6 +164,7 @@ private: UCKeyboardLayout* m_uchrResource; CKeyIDMap m_keyMap; CVirtualKeyMap m_virtualKeyMap; + CKeySet m_capsLockSet; }; #endif From 4750c1214df618e3756284560e88ef5138df4593 Mon Sep 17 00:00:00 2001 From: crs Date: Fri, 12 Nov 2004 15:59:09 +0000 Subject: [PATCH 765/807] Updated documentation. --- ChangeLog | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/news.html | 11 +++++++ 2 files changed, 96 insertions(+) diff --git a/ChangeLog b/ChangeLog index 6d3c5a25..70bd8328 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,88 @@ +2004/11/12 15:50:04 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h + +Now suppressing shift key when caps-lock is enabled on OSX. This +fixes handling of, say, Command+N with caps-lock enabled which +was being synthesized as Command+Shift+N. + +---------- +2004/11/11 19:23:14 crs +lib/arch/CArchMultithreadPosix.cpp + +Fixed a serious flaw in wrapper for posix condition variable wait +function. Because synergy doesn't use posix cancellation, it +cannot wake up a thread waiting on a condition variable. So +the wrapper would wake up periodically to test if the thread was +cancelled (according to synergy's cancellation state) then go +back to waiting. Well the condition could be signalled while +we're testing and be lost and we'd never return from the wait. +So now we wake up after a maximum timeout and return to the +caller. The caller must check for this spurious wakeup but all +callers should do this anyway so we're okay there. + +---------- +2004/11/11 19:17:03 crs +lib/net/CSocketMultiplexer.cpp +lib/net/CSocketMultiplexer.h + +Changed scheme used to lock the socket multiplexer's job list. +I think the new scheme is easier to understand. It should have +exactly the same behavior. + +---------- +2004/11/10 21:00:30 crs +lib/mt/CCondVar.h + +Made condition variable data volatile. This will hopefully fix +an strange deadlock seen on OSX. The CSocketMultiplexer deadlocks +with two threads, one waiting for m_polling to become false and +the other waiting for m_pollable to become true. The weird part +is that they're both false so the first thread should proceed. +It either didn't receive the broadcast when m_polling went to +false or it's not really checking the actual value of that flag. +I can't see how the former is possible and this change fixes the +latter. + +---------- +2004/11/10 19:11:33 crs +lib/platform/CXWindowsUtil.cpp + +Fixed typo. + +---------- +2004/11/09 20:05:33 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h + +Fixed modifier handling on OSX client. Had hardcoded one set of +modifiers for all keys for testing purposes and forgotton to fix +that. Now choosing required modifiers per key. This fixes +shift+arrow keys suppressing the shift key and, i think, the +option key not working. + +---------- +2004/11/09 18:42:47 crs +lib/common/Version.h + +Changed version to 1.1.10. + +---------- +2004/11/09 18:38:14 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Added synergy version number to first log message. + +---------- +2004/11/09 18:31:54 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Moved log message that prints system info to after installation +of user requested log level so it can be filtered. + +---------- 2004/11/06 16:29:06 crs lib/client/CServerProxy.cpp diff --git a/doc/news.html b/doc/news.html index 2b96bbd7..5470627f 100644 --- a/doc/news.html +++ b/doc/news.html @@ -7,6 +7,17 @@

    Synergy News

    +Nov-12-2004 - Synergy 1.1.10 released + +

    +Made following changes: +

    +
      +
    • Fixed race in condition variable wrapper; caused synergy to hang randomly +
    • Fixed modifier key and caps-lock handling on OSX +
    • System info log message now filtered like all other messages +
    + Nov-07-2004 - Synergy 1.1.9 released

    From fc3f17fd83c7a9dc7d34e9ecf7d1a054571f055f Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 19 Dec 2004 15:48:09 +0000 Subject: [PATCH 766/807] Changed version to 1.1.11. --- lib/common/Version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/Version.h b/lib/common/Version.h index a975e37f..79d9ae6c 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.10" +# define VERSION "1.1.11" #endif // important strings From 01fe5fb0a344c74c5f35ad20229f93591c96c291 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 29 Dec 2004 17:00:17 +0000 Subject: [PATCH 767/807] Added Mac OS X autostart documentation from Tor Slettnes (tor@slett.net). --- doc/autostart.html | 323 ++++++++++++++++++++++++++++++++++++++++++++- doc/synergy.css | 22 +++ 2 files changed, 338 insertions(+), 7 deletions(-) diff --git a/doc/autostart.html b/doc/autostart.html index 9d0d7555..9a194ce2 100644 --- a/doc/autostart.html +++ b/doc/autostart.html @@ -98,28 +98,28 @@ in that order, before and instead of Default.

    For a synergy client, add the following to the first file: -

    +
       /usr/bin/killall synergyc
       sleep 1
       /usr/bin/synergyc [<options>] synergy-server-hostname
    -
    + Of course, the path to synergyc depends on where you installed it so adjust as necessary.

    Add to the second file: -

    +
       /usr/bin/killall synergyc
       sleep 1
    -
    +

    And to the third file: -

    +
       /usr/bin/killall synergyc
       sleep 1
       /usr/bin/synergyc [<options>] synergy-server-hostname
    -
    + Note that <options> must not include -f or --no-daemon or @@ -152,7 +152,316 @@ would add to the third file above.

    Mac OS X

    -TBD +[By Tor Slettnes] +

    +

    +There are three different ways to automatically start Synergy +(client or server) on Mac OS X: +

    +

    +

      +
    1. + The first method involves creating a StartupItem + at the system level, which is executed when the machine starts up + or shuts down. This script will run in the background, and + relaunch synergy as needed. +

      +

      +

      +
      Pros:
      +
      + Synergy is persistent, so this allows for a multi-user + setup and interactive logins. +
      +
      Cons:
      +
      + The synergy process does not have access to the clipboard + of the logged-in user. +
      +
      +
    2. +

      +

      +

    3. + The second method will launch Synergy from the + LoginWindow application, once a particular + user has logged in. +

      +

      +

      +
      Pros:
      +
      + The synergy process inherits the + $SECURITYSESSIONID environment variable, + and therefore copy/paste works. +
      +
      Cons:
      +
      + Once the user logs out, synergy dies, and no remote + control is possible. +
      +
      +
    4. +

      +

      +

    5. + The third method is to launch a startup script from the + "Startup Items" tab under System Preferences -> Accounts. +

      +

      +

      +
      Pros:
      +
      + Does not require root (Administrator) access +
      +
      Cons:
      +
      + Once the user logs out, synergy dies, and no remote + control is possible. +
      +
      +
    6. +
    +

    +

    +The text below describes how to implement a Synergy client using +the first two methods simultaneously. This way, Synergy is +always running, and the clipboard is available when someone is +logged in. A Mac OS X Synergy server setup will be quite similar. +

    +

    +1. Create a System Level Startup Item +

    +

    +

      +
    • + Open a Terminal window, and become root: + + $ sudo su - + +
    • +
    • + Create a folder for this item: + + # mkdir -p /Library/StartupItems/Synergy + +
    • +
    • + In this folder, create a new script file by the same name as + the directory itself, Synergy. This script + should contain the following text: +

      +

      + +#!/bin/sh +. /etc/rc.common +  +run=(/usr/local/bin/synergyc -n $(hostname -s) -1 -f synergy-server) +  +KeepAlive () +{ + proc=${1##*/} +  + while [ -x "$1" ] + do + if ! ps axco command | grep -q "^${proc}\$" + then + "$@" + fi +  + sleep 3 + done +} +  +StartService () +{ + ConsoleMessage "Starting Synergy" + KeepAlive "${run[@]}" & +} +  +StopService () +{ + return 0 +} +  +RestartService () +{ + return 0 +} +  +RunService "$1" + +

      +

      + However, replace synergy-server with the actual + name or IP address of your Synergy server. +

      +

      + Note that this scripts takes care not to start + Synergy if another instance is currently running. This + allows it to run in the background even when synergy is also + started independently, e.g. from the LoginWindow + application as described below. +

    • +
    • + Make this script executable: + + # chmod 755 /Library/StartupItems/Synergy/Synergy + +
    • +
    • + In the same folder, create a file named + StartupParameters.plist containing: +

      +

      + +{ + Description = "Synergy Client"; + Provides = ("synergy-client"); + Requires = "Network"; + OrderPreference = "None"; +} + +

    • +
    +

    +

    +That's it! If you want to test this setup, you can run the +startup script as follow: +

    +

    + + # /Library/StartupItems/Synergy/Synergy start + +

    +

    +Any errors, as well as output from Synergy, will be shown in +your terminal window. +

    +

    +Next time you reboot, Synergy should start automatically. +

    +

    +2. Run Synergy When a User Logs In +

    +

    +Each time a user successfully logs in via the console, the +LoginWindow application creates a unique session +cookie and stores it in the environment variable +$SECURITYSESSIONID. For copy and paste operations +to work, Synergy needs access to this environment variable. In +other words, Synergy needs to be launched (directly or +indirectly) via the LoginWindow application. +

    +

    +However, in order to kill any synergy processes started at the +system level (as described above), we need root access. Thus, +launching Synergy within the User's environment (e.g. via the +Startup Items tab in System Preferences -> Accounts) is not an +option that work in conjunction with the method above. +

    +

    +Fortunately, the LoginWindow application provides +a "hook" for running a custom program (as root, with the username provided as +the first and only argument) once a user has authenticated, but +before the user is logged in. +

    +

    +Unfortunately, only one such hook is available. If you have +already installed a Login Hook, you may need to add the text +from below to your existing script, rather than creating a new +one. +

    +

    +

      +
    • + Launch a Terminal window, and become root: + + $ sudo su - +
    • +

      +

      +

    • + Find out if a LoginHook already exists: + + # defaults read /Library/Preferences/com.apple.loginwindow LoginHook + +

      +

      + This will either show the full path to a script or + executable file, or the text: + + The domain/default pair of (/Library/Preferences/com.apple.loginwindow, LoginHook) does not exist + + In the former case, you need to modify your existing script, + and/or create a "superscript" which in turn calls your + existing script plus the one we will create here. +

      +

      + The rest of this text assumes that this item did not already + exist, and that we will create a new script. +

    • +

      +

      +

    • + Create a folder in which we will store our custom startup + script: + + # mkdir -p /Library/LoginWindow + +
    • +

      +

      +

    • + In this folder, create a new script file (let's name it + LoginHook.sh), containing the following text: +

      +

      + +#!/bin/sh +prog=(/usr/local/bin/synergyc -n $(hostname -s) --camp ip-address-of-server) +  +### Stop any currently running Synergy client +killall ${prog[0]##*/} +  +### Start the new client +exec "${prog[@]}" + +

    • +

      +

      +

    • + Create a login hook to call the script you just created: + + # defaults write /Library/Preferences/com.apple.loginwindow \ + LoginHook /Library/LoginWindow/LoginHook.sh + + You can instead type the above all on one line but remove the backslash. +
    • +
    +

    +

    +When running the Synergy client, you may need to use the IP +address of the Synergy server rather than its host name. +Specifically, unless you have listed the server in your +local /etc/hosts file or in your local NetInfo +database, name services (i.e. DNS) may not yet be available by the +time you log in after power-up. synergyc will +quit if it cannot resolve the server name. +

    +

    +(This is not an issue with the previous method, because the +StartupItems.plist file specifies that this +script should not be run until "network" is available). +

    +

    +3. Good Luck! +

    +

    +Remember to look in your system log on both your server and your +client(s) for clues to any problems you may have +(/var/log/system.log on your OS X box, typically +/var/log/syslog on Linux boxes).

    diff --git a/doc/synergy.css b/doc/synergy.css index 6f4c3968..9085c706 100644 --- a/doc/synergy.css +++ b/doc/synergy.css @@ -116,6 +116,14 @@ pre { font-family: courier; } +.userinput { + display: block; + white-space: pre; + font-family: courier; + font-size: 87.5%; + font-weight: bold; +} + .code { font-family: courier; } @@ -124,6 +132,20 @@ pre { font-size: small; } +/* block of code */ +.codeblock { + display: block; + white-space: pre; + font-family: courier; + font-size: 87.5%; + border: 1px solid #000000; + padding: 1em; + padding-top: 0em; + margin: 1em; + background-color: #cccccc; + color: #000000; +} + .fakelink { color: #6699ff; } From f0a5d3162eefb3586d4398cd816cbe2bc0e93409 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 29 Dec 2004 17:06:00 +0000 Subject: [PATCH 768/807] Fixed leak of event objects on OS X. --- lib/platform/COSXEventQueueBuffer.cpp | 26 +++++++++++++------------- lib/platform/COSXEventQueueBuffer.h | 3 --- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/platform/COSXEventQueueBuffer.cpp b/lib/platform/COSXEventQueueBuffer.cpp index 76dcd35c..5bd0d747 100644 --- a/lib/platform/COSXEventQueueBuffer.cpp +++ b/lib/platform/COSXEventQueueBuffer.cpp @@ -34,7 +34,10 @@ COSXEventQueueBuffer::COSXEventQueueBuffer() : COSXEventQueueBuffer::~COSXEventQueueBuffer() { - setOSXEvent(NULL); + // release the last event + if (m_event != NULL) { + ReleaseEvent(m_event); + } } void @@ -47,10 +50,16 @@ COSXEventQueueBuffer::waitForEvent(double timeout) IEventQueueBuffer::Type COSXEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) { - EventRef carbonEvent = NULL; - OSStatus error = ReceiveNextEvent(0, NULL, 0.0, true, &carbonEvent); - setOSXEvent(carbonEvent); + // release the previous event + if (m_event != NULL) { + ReleaseEvent(m_event); + m_event = NULL; + } + // get the next event + OSStatus error = ReceiveNextEvent(0, NULL, 0.0, true, &m_event); + + // handle the event if (error == eventLoopQuitErr) { event = CEvent(CEvent::kQuit); return kSystem; @@ -113,12 +122,3 @@ COSXEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const { delete timer; } - -void -COSXEventQueueBuffer::setOSXEvent(EventRef event) -{ - if (m_event != NULL) { - ReleaseEvent(m_event); - } - m_event = RetainEvent(event); -} diff --git a/lib/platform/COSXEventQueueBuffer.h b/lib/platform/COSXEventQueueBuffer.h index b4910026..659b5940 100644 --- a/lib/platform/COSXEventQueueBuffer.h +++ b/lib/platform/COSXEventQueueBuffer.h @@ -33,9 +33,6 @@ public: newTimer(double duration, bool oneShot) const; virtual void deleteTimer(CEventQueueTimer*) const; -protected: - void setOSXEvent(EventRef event); - private: EventRef m_event; }; From fedd2224e8e52a02ab5caea7d0d8f30902d1319a Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 29 Dec 2004 17:06:49 +0000 Subject: [PATCH 769/807] Removed calls to show/hide mouse because they only work if we've taken exclusive access to the display and we don't do that. --- lib/platform/COSXScreen.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index e9f2bda4..8ce872f1 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -174,10 +174,9 @@ COSXScreen::getCursorPos(SInt32& x, SInt32& y) const } void -COSXScreen::reconfigure(UInt32 activeSides) +COSXScreen::reconfigure(UInt32) { - // FIXME - (void)activeSides; + // do nothing } void @@ -198,7 +197,6 @@ COSXScreen::warpCursor(SInt32 x, SInt32 y) SInt32 COSXScreen::getJumpZoneSize() const { - // FIXME -- is this correct? return 1; } @@ -355,7 +353,7 @@ COSXScreen::enable() // hide cursor if (!m_cursorHidden) { - CGDisplayHideCursor(m_displayID); +// CGDisplayHideCursor(m_displayID); m_cursorHidden = true; } @@ -377,7 +375,7 @@ COSXScreen::disable() else { // show cursor if (m_cursorHidden) { - CGDisplayShowCursor(m_displayID); +// CGDisplayShowCursor(m_displayID); m_cursorHidden = false; } @@ -404,7 +402,7 @@ COSXScreen::enter() else { // show cursor if (m_cursorHidden) { - CGDisplayShowCursor(m_displayID); +// CGDisplayShowCursor(m_displayID); m_cursorHidden = false; } @@ -446,7 +444,7 @@ COSXScreen::leave() else { // hide cursor if (!m_cursorHidden) { - CGDisplayHideCursor(m_displayID); +// CGDisplayHideCursor(m_displayID); m_cursorHidden = true; } @@ -576,7 +574,7 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*) assert(carbonEvent != NULL); UInt32 eventClass = GetEventClass(*carbonEvent); - + switch (eventClass) { case kEventClassMouse: switch (GetEventKind(*carbonEvent)) { From ee787415c2d97cac8a1c8243d64923d1d3aded47 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 29 Dec 2004 17:07:08 +0000 Subject: [PATCH 770/807] Added support for unicode keyboard layouts on OS X. --- lib/platform/COSXKeyState.cpp | 421 ++++++++++++++++++++++++++++------ lib/platform/COSXKeyState.h | 28 ++- 2 files changed, 368 insertions(+), 81 deletions(-) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index 39267232..9704d9aa 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -14,6 +14,7 @@ #include "COSXKeyState.h" #include "CLog.h" +#include "CArch.h" #include struct CKCHRDeadKeyRecord { @@ -112,6 +113,7 @@ static const CKeyEntry s_controlKeys[] = { // modifier keys. i don't know how to make the mac properly // interpret the right hand versions of modifier keys so they're // currently mapped to the left hand version. +// { kKeyNumLock, 71 }, { kKeyShift_L, 56 }, { kKeyShift_R, 56 /*60*/ }, { kKeyControl_L, 59 }, @@ -122,15 +124,20 @@ static const CKeyEntry s_controlKeys[] = { { kKeySuper_R, 58 /*61*/ }, { kKeyMeta_L, 58 }, { kKeyMeta_R, 58 /*61*/ }, - { kKeyCapsLock, 57 }, - { kKeyNumLock, 71 } + { kKeyCapsLock, 57 } }; +// special key that synthesizes a delay. see doFakeKeyEvent() and +// mapKey(). +static const KeyButton kDelayKey = 510; + + // // COSXKeyState // -COSXKeyState::COSXKeyState() +COSXKeyState::COSXKeyState() : + m_uchrFound(false) { setHalfDuplexMask(0); SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript); @@ -197,8 +204,12 @@ COSXKeyState::doUpdateKeys() { // save key mapping m_keyMap.clear(); - if (!filluchrKeysMap(m_keyMap, m_capsLockSet)) { - fillKCHRKeysMap(m_keyMap, m_capsLockSet); + m_uchrFound = false; + if (m_uchrResource != NULL) { + m_uchrFound = filluchrKeysMap(m_keyMap); + } + if (!m_uchrFound && m_KCHRResource != NULL) { + fillKCHRKeysMap(m_keyMap); } fillSpecialKeys(m_keyMap, m_virtualKeyMap); @@ -235,15 +246,26 @@ void COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool) { LOG((CLOG_DEBUG2 "doFakeKeyEvent button:%d, press:%d", button, press)); + + // if it's the special delay key then just pause briefly + if (button == kDelayKey) { + ARCH->sleep(0.01); + return; + } + // let system figure out character for us CGPostKeyboardEvent(0, mapKeyButtonToVirtualKey(button), press); } KeyButton COSXKeyState::mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask /*desiredMask*/, + KeyModifierMask desiredMask, bool isAutoRepeat) const { + // see if the keyboard layout has changed since we last checked it. + // reload the keyboard info if so. + const_cast(this)->checkKeyboardLayout(); + // look up virtual key CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id); if (keyIndex == m_keyMap.end()) { @@ -254,12 +276,6 @@ COSXKeyState::mapKey(Keystrokes& keys, KeyID id, return 0; } - // if the virtual key is caps-lock sensitive then suppress shift - KeyModifierMask mask = ~0; - if (m_capsLockSet.count(id) != 0) { - mask &= ~KeyModifierShift; - } - // FIXME -- for both calls to addKeystrokes below we'd prefer to use // a required mask that generates the same character but matches // the desiredMask as closely as possible. @@ -280,17 +296,40 @@ COSXKeyState::mapKey(Keystrokes& keys, KeyID id, // simulate release Keystroke keystroke; - keystroke.m_key = keyButton; + keystroke.m_key = keyButton; + keystroke.m_press = false; + keystroke.m_repeat = false; + keys.push_back(keystroke); + + // pause briefly before sending the next key because some + // apps (e.g. TextEdit) seem to ignore dead keys that occur + // very shortly before the next key. why they'd do that i + // don't know. + keystroke.m_key = kDelayKey; keystroke.m_press = false; keystroke.m_repeat = false; keys.push_back(keystroke); } - // add final key - return addKeystrokes(keys, sequence.back().m_button, - sequence.back().m_requiredState, - sequence.back().m_requiredMask & mask, + // add final key. + // if the desired mask includes Alt or Control then match the + // desired mask. this ensures that combinations like + // Command+Shift+S use the Command and Shift modifiers and + // those like Command+S do not use the shift modifier. + if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) != 0) { + return addKeystrokes(keys, sequence.back().m_button, + desiredMask, + KeyModifierShift | KeyModifierSuper | + KeyModifierAlt | KeyModifierControl | + KeyModifierCapsLock, isAutoRepeat); + } + else { + return addKeystrokes(keys, sequence.back().m_button, + sequence.back().m_requiredState, + sequence.back().m_requiredMask, + isAutoRepeat); + } } KeyButton @@ -387,8 +426,50 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, } // check for character keys - if (m_uchrResource != NULL) { - // FIXME -- implement this + if (m_uchrFound) { + // get the event modifiers and remove the command and control + // keys. + UInt32 modifiers; + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, + NULL, sizeof(modifiers), NULL, &modifiers); + modifiers &= ~(cmdKey | controlKey | rightControlKey); + + // build keycode + UInt16 keycode = + static_cast((modifiers & 0xff00u) | (vkCode & 0x00ffu)); + + // choose action + UInt16 action; + switch (eventKind) { + case kEventRawKeyDown: + action = kUCKeyActionDown; + break; + + case kEventRawKeyRepeat: + action = kUCKeyActionAutoKey; + break; + + default: + return 0; + } + + // translate key + UniCharCount count; + UniChar chars[2]; + OSStatus status = UCKeyTranslate(m_uchrResource, keycode, action, + modifiers, m_keyboardType, 0, &m_deadKeyState, + sizeof(chars) / sizeof(chars[0]), &count, chars); + + // get the characters + if (status == 0) { + if (count != 0 || m_deadKeyState == 0) { + m_deadKeyState = 0; + for (UniCharCount i = 0; i < count; ++i) { + ids.push_back(unicharToKeyID(chars[i])); + } + return mapVirtualKeyToKeyButton(vkCode); + } + } } else if (m_KCHRResource != NULL) { // get the event modifiers and remove the command and control @@ -478,10 +559,13 @@ COSXKeyState::checkKeyboardLayout() { SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript); SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys); - if (keyboardLayoutID != m_keyboardLayoutID) { + UInt32 keyboardType = LMGetKbdType(); + if (keyboardLayoutID != m_keyboardLayoutID || + keyboardType != m_keyboardType) { // layout changed + m_keyboardType = keyboardType; setKeyboardLayout(keyboardLayoutID); - doUpdateKeys(); + updateKeys(); } } @@ -494,11 +578,10 @@ COSXKeyState::setKeyboardLayout(SInt16 keyboardLayoutID) m_uchrHandle = GetResource('uchr', m_keyboardLayoutID); m_KCHRResource = NULL; m_uchrResource = NULL; -/* FIXME -- don't use uchr resource yet if (m_uchrHandle != NULL) { m_uchrResource = reinterpret_cast(*m_uchrHandle); } - else */if (m_KCHRHandle != NULL) { + if (m_KCHRHandle != NULL) { m_KCHRResource = reinterpret_cast(*m_KCHRHandle); } } @@ -526,29 +609,19 @@ COSXKeyState::fillSpecialKeys(CKeyIDMap& keyMap, } bool -COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const +COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const { assert(m_KCHRResource != NULL); CKCHRResource* r = m_KCHRResource; - // note caps-lock sensitive keys - SInt32 uIndex = r->m_tableSelectionIndex[0]; - SInt32 clIndex = r->m_tableSelectionIndex[alphaLock >> 8]; - for (SInt32 j = 0; j < 128; ++j) { - UInt8 c = r->m_characterTables[clIndex][j]; - if (r->m_characterTables[uIndex][j] != c) { - KeyID keyID = charToKeyID(c); - capsLockSet.insert(keyID); - } - } - // build non-composed keys to virtual keys mapping std::map vkMap; for (SInt32 i = 0; i < r->m_numTables; ++i) { // determine the modifier keys for table i KeyModifierMask mask = - maskForTable(static_cast(i), r->m_tableSelectionIndex); + maskForTable(static_cast(i), r->m_tableSelectionIndex, + 256, static_cast(i)); // build the KeyID to virtual key map for (SInt32 j = 0; j < 128; ++j) { @@ -561,7 +634,9 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const // generate the character. this mostly works as-is, though. CKeyEventInfo info; info.m_button = mapVirtualKeyToKeyButton(j); - info.m_requiredMask = mask; + info.m_requiredMask = + KeyModifierShift | KeyModifierSuper | + KeyModifierCapsLock; info.m_requiredState = mask; // save character to virtual key mapping @@ -594,7 +669,8 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const for (SInt32 i = 0; i < dkp->m_numRecords; ++i) { // determine the modifier keys for table i KeyModifierMask mask = - maskForTable(dkr->m_tableIndex, r->m_tableSelectionIndex); + maskForTable(dkr->m_tableIndex, r->m_tableSelectionIndex, + 256, dkr->m_tableIndex); // map each completion for (SInt32 j = 0; j < dkr->m_numCompletions; ++j) { @@ -627,9 +703,11 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const CKeyEventInfo info; info.m_button = mapVirtualKeyToKeyButton( dkr->m_virtualKey); - info.m_requiredMask = mask; + info.m_requiredMask = + KeyModifierShift | KeyModifierSuper | + KeyModifierAlt | KeyModifierControl | + KeyModifierCapsLock; info.m_requiredState = mask; - sequence.push_back(info); sequence.push_back(vkMap[dkr->m_completion[j][0]]); } @@ -645,10 +723,198 @@ COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap, CKeySet& capsLockSet) const } bool -COSXKeyState::filluchrKeysMap(CKeyIDMap&, CKeySet&) const +COSXKeyState::filluchrKeysMap(CKeyIDMap& keyMap) const { - // FIXME -- implement this - return false; + assert(m_uchrResource != NULL); + + UCKeyboardLayout* r = m_uchrResource; + UInt8* base = reinterpret_cast(r); + + UCKeyLayoutFeatureInfo* fi = NULL; + if (r->keyLayoutFeatureInfoOffset != 0) { + fi = reinterpret_cast( + base + r->keyLayoutFeatureInfoOffset); + } + + // find the keyboard info for the current keyboard type + UCKeyboardTypeHeader* th = NULL; + for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) { + if (m_keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst && + m_keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) { + th = r->keyboardTypeList + i; + break; + } + if (r->keyboardTypeList[i].keyboardTypeFirst == 0) { + // found the default. use it unless we find a match. + th = r->keyboardTypeList + i; + } + } + if (th == NULL) { + // cannot find a suitable keyboard type + return false; + } + + // get tables for keyboard type + UCKeyModifiersToTableNum* m = + reinterpret_cast( + base + th->keyModifiersToTableNumOffset); + UCKeyToCharTableIndex* cti = + reinterpret_cast( + base + th->keyToCharTableIndexOffset); + UCKeySequenceDataIndex* sdi = + reinterpret_cast( + base + th->keySequenceDataIndexOffset); + + // build non-composed keys to virtual keys mapping + CDeadKeyMap dkMap; + for (UInt32 i = 0; i < cti->keyToCharTableCount; ++i) { + // determine the modifier keys for table i + KeyModifierMask mask = + maskForTable(static_cast(i), m->tableNum, + m->modifiersCount, m->defaultTableNum); + + // get the character table + UCKeyOutput* ct = + reinterpret_cast( + base + cti->keyToCharTableOffsets[i]); + + // build the KeyID to virtual key map + for (SInt32 j = 0; j < cti->keyToCharTableSize; ++j) { + // get character + UInt16 c = ct[j]; + + // ignore key sequence mappings + if ((c & 0xc000) == 0x8000) { + UInt16 index = (c & 0x3fff); + if (index < sdi->charSequenceCount && + sdi->charSequenceOffsets[index] != + sdi->charSequenceOffsets[index + 1]) { + continue; + } + + // not a sequence mapping. use character as-is. + } + + // just record dead key mappings + else if ((c & 0xc000) == 0x4000) { + UInt16 index = (c & 0x3fff); + if (dkMap.count(index) == 0) { + dkMap[index] = std::make_pair(j, mask); + } + continue; + } + + // skip non-glyph character + if (c < 32 || c == 127 || c == 0xfffe || c == 0xffff) { + continue; + } + + // map character to KeyID + KeyID keyID = unicharToKeyID(c); + + // if we've seen this character already then do nothing + if (keyMap.count(keyID) != 0) { + continue; + } + + // save entry for character + // FIXME -- should set only those bits in m_requiredMask that + // correspond to modifiers that are truly necessary to + // generate the character. + CKeyEventInfo info; + info.m_button = mapVirtualKeyToKeyButton(j); + info.m_requiredMask = + KeyModifierShift | KeyModifierSuper | + KeyModifierCapsLock; + info.m_requiredState = mask; + keyMap[keyID].push_back(info); + } + } + + // build composed keys to virtual keys mapping + UCKeyStateRecordsIndex* sri = NULL; + if (th->keyStateRecordsIndexOffset != NULL) { + sri = reinterpret_cast( + base + th->keyStateRecordsIndexOffset); + } + UCKeyStateTerminators* st = NULL; + if (th->keyStateTerminatorsOffset != NULL) { + st = reinterpret_cast( + base + th->keyStateTerminatorsOffset); + } + CKeySequence sequence; + mapDeadKeySequence(keyMap, sequence, 0, base, sri, st, dkMap); + + return true; +} + +void +COSXKeyState::mapDeadKeySequence(CKeyIDMap& keyMap, + CKeySequence& sequence, + UInt16 state, const UInt8* base, + const UCKeyStateRecordsIndex* sri, + const UCKeyStateTerminators* st, + CDeadKeyMap& dkMap) +{ + for (CDeadKeyMap::const_iterator i = dkMap.begin(); i != dkMap.end(); ++i) { + UInt16 index = i->first; + const UCKeyStateRecord* sr = + reinterpret_cast( + base + sri->keyStateRecordOffsets[index]); + const UCKeyStateEntryTerminal* kset = + reinterpret_cast( + sr->stateEntryData); + + UInt16 c = 0; + UInt16 nextState = 0; + if (state == 0) { + c = sr->stateZeroCharData; + nextState = sr->stateZeroNextState; + } + else if (sr->stateEntryFormat == kUCKeyStateEntryTerminalFormat) { + for (UInt16 j = 0; j < sr->stateEntryCount; ++j) { + if (kset[j].curState == state) { + c = kset[j].charData; + break; + } + } + // XXX -- default state terminator not supported yet + } + else if (sr->stateEntryFormat == kUCKeyStateEntryRangeFormat) { + // XXX -- not supported yet + } + + // push character onto sequence. m_requiredMask should only + // have those modifiers that are truly necessary to generate + // the character. + CKeyEventInfo info; + info.m_button = mapVirtualKeyToKeyButton( + i->second.first); + info.m_requiredMask = + KeyModifierShift | KeyModifierSuper | + KeyModifierCapsLock; + info.m_requiredState = i->second.second; + sequence.push_back(info); + + if (nextState != 0) { + // next in dead key sequence + mapDeadKeySequence(keyMap, sequence, + nextState, base, sri, st, dkMap); + } + else if (c >= 32 && c != 127 && c != 0xfffe && c != 0xffff) { + // terminate sequence + KeyID keyID = unicharToKeyID(c); + + // if we've seen this character already then do nothing, + // otherwise save it in the key map. + if (keyMap.count(keyID) == 0) { + keyMap[keyID] = sequence; + } + } + + // pop character from sequence + sequence.pop_back(); + } } KeyButton @@ -718,7 +984,8 @@ COSXKeyState::unicharToKeyID(UniChar c) } KeyModifierMask -COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors) +COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors, + UInt32 numEntries, UInt8 defaultIndex) { // this is a table of 0 to 255 sorted by the number of 1 bits then // numerical order. @@ -741,35 +1008,45 @@ COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors) 238, 243, 245, 246, 249, 250, 252, 127, 191, 223, 239, 247, 251, 253, 254, 255 }; - // find first entry in tableSelectors that maps to i. this is the - // one that uses the fewest modifier keys. - for (UInt32 j = 0; j < 256; ++j) { - if (tableSelectors[s_indexTable[j]] == i) { - // convert our mask to a traditional mac modifier mask - // (which just means shifting it left 8 bits). - UInt16 macMask = (static_cast(s_indexTable[j]) << 8); + while (true) { + // find first entry in tableSelectors that maps to i. this is the + // one that uses the fewest modifier keys. + UInt8 maxIndex = static_cast(numEntries - 1); + for (UInt32 j = 0; j < 256; ++j) { + if (s_indexTable[j] <= maxIndex && + tableSelectors[s_indexTable[j]] == i) { + // convert our mask to a traditional mac modifier mask + // (which just means shifting it left 8 bits). + UInt16 macMask = (static_cast(s_indexTable[j]) << 8); - // convert the mac modifier mask to our mask. - KeyModifierMask mask = 0; - if ((macMask & (shiftKey | rightShiftKey)) != 0) { - mask |= KeyModifierShift; - } - if ((macMask & (controlKey | rightControlKey)) != 0) { - mask |= KeyModifierControl; - } - if ((macMask & cmdKey) != 0) { - mask |= KeyModifierAlt; - } - if ((macMask & (optionKey | rightOptionKey)) != 0) { - mask |= KeyModifierSuper; - } - if ((macMask & alphaLock) != 0) { - mask |= KeyModifierCapsLock; - } - return mask; - } - } + // convert the mac modifier mask to our mask. + KeyModifierMask mask = 0; + if ((macMask & (shiftKey | rightShiftKey)) != 0) { + mask |= KeyModifierShift; + } + if ((macMask & (controlKey | rightControlKey)) != 0) { + mask |= KeyModifierControl; + } + if ((macMask & cmdKey) != 0) { + mask |= KeyModifierAlt; + } + if ((macMask & (optionKey | rightOptionKey)) != 0) { + mask |= KeyModifierSuper; + } + if ((macMask & alphaLock) != 0) { + mask |= KeyModifierCapsLock; + } + return mask; + } + } - // should never get here since we've tried every 8 bit number + // no match. try defaultIndex. + if (i == defaultIndex) { + break; + } + i = defaultIndex; + } + + // should never get here. return 0; } diff --git a/lib/platform/COSXKeyState.h b/lib/platform/COSXKeyState.h index 657c9b76..685a56b1 100644 --- a/lib/platform/COSXKeyState.h +++ b/lib/platform/COSXKeyState.h @@ -79,7 +79,7 @@ private: typedef std::vector CKeySequence; typedef std::map CKeyIDMap; typedef std::map CVirtualKeyMap; - typedef std::set CKeySet; + typedef std::map > CDeadKeyMap; KeyButton addKeystrokes(Keystrokes& keys, KeyButton keyButton, @@ -109,15 +109,14 @@ private: // map maps each KeyID to the sequence of keys (with modifiers) // that would have to be synthesized to generate the KeyID character. // Returns false iff no KCHR resource was found. - bool fillKCHRKeysMap(CKeyIDMap& keyMap, - CKeySet& capsLockSet) const; + bool fillKCHRKeysMap(CKeyIDMap& keyMap) const; // Convert the uchr resource to a KeyID to key sequence map. the // map maps each KeyID to the sequence of keys (with modifiers) // that would have to be synthesized to generate the KeyID character. - // Returns false iff no uchr resource was found. - bool filluchrKeysMap(CKeyIDMap& keyMap, - CKeySet& capsLockSet) const; + // Returns false iff no uchr resource was found or it couldn't be + // mapped. + bool filluchrKeysMap(CKeyIDMap& keyMap) const; // Maps an OS X virtual key id to a KeyButton. This simply remaps // the ids so we don't use KeyButton 0. @@ -134,9 +133,19 @@ private: static KeyID unicharToKeyID(UniChar); // Choose the modifier mask with the fewest modifiers for character - // mapping table i. + // mapping table i. The tableSelectors table has numEntries. If + // no mapping is found for i, try mapping defaultIndex. static KeyModifierMask - maskForTable(UInt8 i, UInt8* tableSelectors); + maskForTable(UInt8 i, UInt8* tableSelectors, + UInt32 numEntries, UInt8 defaultIndex); + + // Save characters built from dead key sequences. + static void mapDeadKeySequence(CKeyIDMap& keyMap, + CKeySequence& sequence, + UInt16 state, const UInt8* base, + const UCKeyStateRecordsIndex* sri, + const UCKeyStateTerminators* st, + CDeadKeyMap& dkMap); private: // OS X uses a physical key if 0 for the 'A' key. synergy reserves @@ -157,6 +166,7 @@ private: }; SInt16 m_keyboardLayoutID; + UInt32 m_keyboardType; mutable UInt32 m_deadKeyState; Handle m_KCHRHandle; Handle m_uchrHandle; @@ -164,7 +174,7 @@ private: UCKeyboardLayout* m_uchrResource; CKeyIDMap m_keyMap; CVirtualKeyMap m_virtualKeyMap; - CKeySet m_capsLockSet; + bool m_uchrFound; }; #endif From d70ad662bf172955010ae900b51b88c7e2515668 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 29 Dec 2004 17:53:44 +0000 Subject: [PATCH 771/807] Added support for ISO_Level3_Shift on X windows server. It's treated as if it were Mode_switch. --- lib/platform/CXWindowsScreen.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index b7a84a8a..677bfc54 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -1404,6 +1404,10 @@ CXWindowsScreen::mapKeyFromX(XKeyEvent* event) const if (keysym == XK_ISO_Left_Tab) { return kKeyLeftTab; } + if (keysym == XK_ISO_Level3_Shift) { + // treat ISO_Level3_Shift as ModeSwitch + return kKeyModeSwitch; + } return kKeyNone; case 0xff00: From 7aea3964f54809b92fdc82ed08b0a7644fd2e345 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 29 Dec 2004 21:10:49 +0000 Subject: [PATCH 772/807] Fixed handling of number pad number and decimal point keys when NumLock is on on client on windows 95 family. --- lib/platform/CMSWindowsKeyState.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index 50f810e7..67cae68a 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -1404,9 +1404,14 @@ CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey, // doesn't make any sense since a button can't actually generate // more than one virtual key. to avoid this stupidity, we map the // button back to a virtual key to see if it matches the starting - // point. - if (button == 0 || MapVirtualKey(button, 1) != virtualKey) { - return 0; + // point. we don't do this for number pad keys since we expect + // each key to generate one of two virtual keys, depending on the + // state of NumLock, a state we can't pass to MapVirtualKey. + if ((virtualKey < VK_NUMPAD0 || virtualKey > VK_NUMPAD9) && + virtualKey != VK_SEPARATOR && virtualKey != VK_DECIMAL) { + if (button == 0 || MapVirtualKey(button, 1) != virtualKey) { + return 0; + } } break; } From 3ae1b916ea890ead7f4ede1a46f7940c9c7a0b4c Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 29 Dec 2004 21:12:05 +0000 Subject: [PATCH 773/807] Now ignoring 4th and 5th mouse buttons if they don't exist. Was previously querying their state, sometimes getting the wrong answer from the OS that they were down, which prevented switching screens. --- lib/platform/CMSWindowsScreen.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 4951dad3..2ad2cd57 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -718,12 +718,15 @@ CMSWindowsScreen::handleSystemEvent(const CEvent& event, void*) void CMSWindowsScreen::updateButtons() { + int numButtons = GetSystemMetrics(SM_CMOUSEBUTTONS); m_buttons[kButtonNone] = false; m_buttons[kButtonLeft] = (GetKeyState(VK_LBUTTON) < 0); - m_buttons[kButtonMiddle] = (GetKeyState(VK_MBUTTON) < 0); m_buttons[kButtonRight] = (GetKeyState(VK_RBUTTON) < 0); - m_buttons[kButtonExtra0 + 0] = (GetKeyState(VK_XBUTTON1) < 0); - m_buttons[kButtonExtra0 + 1] = (GetKeyState(VK_XBUTTON2) < 0); + m_buttons[kButtonMiddle] = (GetKeyState(VK_MBUTTON) < 0); + m_buttons[kButtonExtra0 + 0] = (numButtons >= 4) && + (GetKeyState(VK_XBUTTON1) < 0); + m_buttons[kButtonExtra0 + 1] = (numButtons >= 5) && + (GetKeyState(VK_XBUTTON2) < 0); } IKeyState* @@ -1329,10 +1332,16 @@ CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const case WM_NCXBUTTONUP: switch (button) { case XBUTTON1: - return kButtonExtra0 + 0; + if (GetSystemMetrics(SM_CMOUSEBUTTONS) >= 4) { + return kButtonExtra0 + 0; + } + break; case XBUTTON2: - return kButtonExtra0 + 1; + if (GetSystemMetrics(SM_CMOUSEBUTTONS) >= 5) { + return kButtonExtra0 + 1; + } + break; } return kButtonNone; From 1d0436b31cd6559c3cdcbe1dd72ed5c0c51c46fc Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 30 Dec 2004 11:54:23 +0000 Subject: [PATCH 774/807] Applied patch from Tom Chadwick to synthesize PageUp/PageDown on X servers that don't support the mouse wheel. --- doc/authors.html | 4 ++++ lib/platform/CXWindowsScreen.cpp | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/doc/authors.html b/doc/authors.html index 18c420b7..f96dfcfd 100644 --- a/doc/authors.html +++ b/doc/authors.html @@ -24,6 +24,10 @@ Bertrand Landry Hetu Mac OS X port + + Tom Chadwick + PageUp/PageDown on X servers without mouse wheel support +

    diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 677bfc54..ad62425b 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -31,6 +31,7 @@ #else # include # include +# define XK_MISCELLANY # define XK_XKB_KEYS # include # if HAVE_X11_EXTENSIONS_XTEST_H @@ -583,6 +584,20 @@ CXWindowsScreen::fakeMouseWheel(SInt32 delta) const const unsigned int xButton = mapButtonToX(static_cast( (delta >= 0) ? -1 : -2)); if (xButton == 0) { + // If we get here, then the XServer does not support the scroll + // wheel buttons, so send PageUp/PageDown keystrokes instead. + // Patch by Tom Chadwick. + KeyCode keycode = 0; + if (delta >= 0) { + keycode = XKeysymToKeycode(m_display, XK_Page_Up); + } + else { + keycode = XKeysymToKeycode(m_display, XK_Page_Down); + } + if (keycode != 0) { + XTestFakeKeyEvent(m_display, keycode, True, CurrentTime); + XTestFakeKeyEvent(m_display, keycode, False, CurrentTime); + } return; } From 34c2be00e571d5c292fdbcfabe16df9266fc4bce Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 30 Dec 2004 12:10:47 +0000 Subject: [PATCH 775/807] Added Henkan key. Patch from rniitani at sourceforge.net. --- lib/platform/CMSWindowsKeyState.cpp | 4 ++-- lib/synergy/KeyTypes.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index 67cae68a..e65030ac 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -346,7 +346,7 @@ const KeyID CMSWindowsKeyState::s_virtualKey[][2] = /* 0x19 */ { kKeyZenkaku, kKeyNone }, // VK_KANJI /* 0x1a */ { kKeyNone, kKeyNone }, // undefined /* 0x1b */ { kKeyEscape, kKeyNone }, // VK_ESCAPE - /* 0x1c */ { kKeyNone, kKeyNone }, // VK_CONVERT + /* 0x1c */ { kKeyHenkan, kKeyNone }, // VK_CONVERT /* 0x1d */ { kKeyNone, kKeyNone }, // VK_NONCONVERT /* 0x1e */ { kKeyNone, kKeyNone }, // VK_ACCEPT /* 0x1f */ { kKeyNone, kKeyNone }, // VK_MODECHANGE @@ -664,7 +664,7 @@ const UINT CMSWindowsKeyState::s_mapEF00[] = /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, /* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0, /* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, VK_CONVERT, 0, 0, 0, 0, /* 0x28 */ 0, 0, VK_KANJI, 0, 0, 0, 0, 0, /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index b444c476..c6ff7148 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -88,6 +88,7 @@ static const KeyID kKeyPause = 0xEF13; /* Pause, hold */ static const KeyID kKeyScrollLock = 0xEF14; static const KeyID kKeySysReq = 0xEF15; static const KeyID kKeyEscape = 0xEF1B; +static const KeyID kKeyHenkan = 0xEF23; /* Start/Stop Conversion */ static const KeyID kKeyZenkaku = 0xEF2A; /* Zenkaku/Hankaku */ static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ From 3d961e47673360817e67c80f420e583dec7f4823 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 30 Dec 2004 13:28:51 +0000 Subject: [PATCH 776/807] Adapted and applied patch by Brent Priddy for re-resolving the server hostname on each connection. This allows the client to startup without being able to resolve the server's hostname. It also lets it handle changes in the server's address, a typical scenario when the client is a laptop moving between networks. --- cmd/synergyc/synergyc.cpp | 11 ++- cmd/synergys/synergys.cpp | 1 + doc/authors.html | 4 + lib/client/CClient.cpp | 8 ++ lib/net/CNetworkAddress.cpp | 141 +++++++++++++++++++++--------------- lib/net/CNetworkAddress.h | 33 +++++++-- lib/server/CConfig.cpp | 1 + 7 files changed, 133 insertions(+), 66 deletions(-) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index c214034c..2b548be8 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -694,11 +694,18 @@ parse(int argc, const char* const* argv) // save server address try { *ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); + ARG->m_serverAddress->resolve(); } catch (XSocketAddress& e) { - LOG((CLOG_PRINT "%s: %s" BYE, + // allow an address that we can't look up if we're restartable. + // we'll try to resolve the address each time we connect to the + // server. a bad port will never get better. patch by Brent + // Priddy. + if (!ARG->m_restartable || e.getError() == XSocketAddress::kBadPort) { + LOG((CLOG_PRINT "%s: %s" BYE, ARG->m_pname, e.what(), ARG->m_pname)); - bye(kExitFailed); + bye(kExitFailed); + } } // increase default filter level for daemon. the user must diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 7f40264f..a8530dec 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -744,6 +744,7 @@ parse(int argc, const char* const* argv) try { *ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], kDefaultPort); + ARG->m_synergyAddress->resolve(); } catch (XSocketAddress& e) { LOG((CLOG_PRINT "%s: %s" BYE, diff --git a/doc/authors.html b/doc/authors.html index f96dfcfd..f908c0bf 100644 --- a/doc/authors.html +++ b/doc/authors.html @@ -28,6 +28,10 @@ Tom Chadwick PageUp/PageDown on X servers without mouse wheel support + + Brent Priddy + Re-resolving server hostname on each connection +

    diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 15092cf6..d339156f 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -74,6 +74,14 @@ CClient::connect() } try { + // resolve the server hostname. do this every time we connect + // in case we couldn't resolve the address earlier or the address + // has changed (which can happen frequently if this is a laptop + // being shuttled between various networks). patch by Brent + // Priddy. + m_serverAddress.resolve(); + + // create the socket IDataSocket* socket = m_socketFactory->create(); // filter socket messages, including a packetizing filter diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp index 758f76dc..7daeed55 100644 --- a/lib/net/CNetworkAddress.cpp +++ b/lib/net/CNetworkAddress.cpp @@ -22,54 +22,56 @@ // CNetworkAddress // +// name re-resolution adapted from a patch by Brent Priddy. + CNetworkAddress::CNetworkAddress() : - m_address(NULL) + m_address(NULL), + m_hostname(), + m_port(0) { // note -- make no calls to CNetwork socket interface here; // we're often called prior to CNetwork::init(). } -CNetworkAddress::CNetworkAddress(int port) +CNetworkAddress::CNetworkAddress(int port) : + m_address(NULL), + m_hostname(), + m_port(port) { - if (port == 0) { - throw XSocketAddress(XSocketAddress::kBadPort, "", port); - } - + checkPort(); m_address = ARCH->newAnyAddr(IArchNetwork::kINET); - ARCH->setAddrPort(m_address, port); + ARCH->setAddrPort(m_address, m_port); } CNetworkAddress::CNetworkAddress(const CNetworkAddress& addr) : - m_address(ARCH->copyAddr(addr.m_address)), - m_hostname(addr.m_hostname) + m_address(addr.m_address != NULL ? ARCH->copyAddr(addr.m_address) : NULL), + m_hostname(addr.m_hostname), + m_port(addr.m_port) { // do nothing } -CNetworkAddress::CNetworkAddress(const CString& hostname_, int port) : - m_hostname(hostname_) +CNetworkAddress::CNetworkAddress(const CString& hostname, int port) : + m_address(NULL), + m_hostname(hostname), + m_port(port) { - if (port == 0) { - throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, port); - } - // check for port suffix - CString hostname(m_hostname); - CString::size_type i = hostname.rfind(':'); - if (i != CString::npos && i + 1 < hostname.size()) { + CString::size_type i = m_hostname.rfind(':'); + if (i != CString::npos && i + 1 < m_hostname.size()) { // found a colon. see if it looks like an IPv6 address. bool colonNotation = false; bool dotNotation = false; bool doubleColon = false; for (CString::size_type j = 0; j < i; ++j) { - if (hostname[j] == ':') { + if (m_hostname[j] == ':') { colonNotation = true; dotNotation = false; - if (hostname[j + 1] == ':') { + if (m_hostname[j + 1] == ':') { doubleColon = true; } } - else if (hostname[j] == '.' && colonNotation) { + else if (m_hostname[j] == '.' && colonNotation) { dotNotation = true; } } @@ -80,46 +82,25 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, int port) : // the user can replace the double colon with zeros to // disambiguate. if ((!doubleColon || dotNotation) || !colonNotation) { + // parse port from hostname char* end; - long suffixPort = strtol(hostname.c_str() + i + 1, &end, 10); - if (end == hostname.c_str() + i + 1 || *end != '\0' || - suffixPort <= 0 || suffixPort > 65535) { - // bogus port + const char* chostname = m_hostname.c_str(); + long suffixPort = strtol(chostname + i + 1, &end, 10); + if (end == chostname + i + 1 || *end != '\0') { throw XSocketAddress(XSocketAddress::kBadPort, - m_hostname, port); - } - else { - // good port - port = static_cast(suffixPort); - hostname.erase(i); + m_hostname, m_port); } + + // trim port from hostname + m_hostname.erase(i); + + // save port + m_port = static_cast(suffixPort); } } - // if hostname is empty then use wildcard address - if (hostname.empty()) { - m_address = ARCH->newAnyAddr(IArchNetwork::kINET); - ARCH->setAddrPort(m_address, port); - } - else { - // look up name - try { - m_address = ARCH->nameToAddr(hostname); - ARCH->setAddrPort(m_address, port); - } - catch (XArchNetworkNameUnknown&) { - throw XSocketAddress(XSocketAddress::kNotFound, hostname, port); - } - catch (XArchNetworkNameNoAddress&) { - throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); - } - catch (XArchNetworkNameUnsupported&) { - throw XSocketAddress(XSocketAddress::kUnsupported, hostname, port); - } - catch (XArchNetworkName&) { - throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); - } - } + // check port number + checkPort(); } CNetworkAddress::~CNetworkAddress() @@ -139,11 +120,48 @@ CNetworkAddress::operator=(const CNetworkAddress& addr) if (m_address != NULL) { ARCH->closeAddr(m_address); } - m_hostname = addr.m_hostname; m_address = newAddr; + m_hostname = addr.m_hostname; + m_port = addr.m_port; return *this; } +void +CNetworkAddress::resolve() +{ + // discard previous address + if (m_address != NULL) { + ARCH->closeAddr(m_address); + m_address = NULL; + } + + try { + // if hostname is empty then use wildcard address otherwise look + // up the name. + if (m_hostname.empty()) { + m_address = ARCH->newAnyAddr(IArchNetwork::kINET); + } + else { + m_address = ARCH->nameToAddr(m_hostname); + } + } + catch (XArchNetworkNameUnknown&) { + throw XSocketAddress(XSocketAddress::kNotFound, m_hostname, m_port); + } + catch (XArchNetworkNameNoAddress&) { + throw XSocketAddress(XSocketAddress::kNoAddress, m_hostname, m_port); + } + catch (XArchNetworkNameUnsupported&) { + throw XSocketAddress(XSocketAddress::kUnsupported, m_hostname, m_port); + } + catch (XArchNetworkName&) { + throw XSocketAddress(XSocketAddress::kUnknown, m_hostname, m_port); + } + + // set port in address + ARCH->setAddrPort(m_address, m_port); +} + bool CNetworkAddress::operator==(const CNetworkAddress& addr) const { @@ -171,7 +189,7 @@ CNetworkAddress::getAddress() const int CNetworkAddress::getPort() const { - return (m_address == NULL) ? 0 : ARCH->getAddrPort(m_address); + return m_port; } CString @@ -179,3 +197,12 @@ CNetworkAddress::getHostname() const { return m_hostname; } + +void +CNetworkAddress::checkPort() +{ + // check port number + if (m_port <= 0 || m_port > 65535) { + throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, m_port); + } +} diff --git a/lib/net/CNetworkAddress.h b/lib/net/CNetworkAddress.h index f1e66bab..d29b93b1 100644 --- a/lib/net/CNetworkAddress.h +++ b/lib/net/CNetworkAddress.h @@ -39,10 +39,12 @@ public: /*! Construct the network address for the given \c hostname and \c port. If \c hostname can be parsed as a numerical address then that's how - it's used, otherwise the host name is looked up. If the lookup fails - then this throws XSocketAddress. If \c hostname ends in ":[0-9]+" then - that suffix is extracted and used as the port, overridding the port - parameter. Neither the extracted port or \c port may be zero. + it's used, otherwise it's used as a host name. If \c hostname ends + in ":[0-9]+" then that suffix is extracted and used as the port, + overridding the port parameter. The resulting port must be a valid + port number (zero is not a valid port number) otherwise \c XSocketAddress + is thrown with an error of \c XSocketAddress::kBadPort. The hostname + is not resolved by the c'tor; use \c resolve to do that. */ CNetworkAddress(const CString& hostname, int port); @@ -52,6 +54,19 @@ public: CNetworkAddress& operator=(const CNetworkAddress&); + //! @name manipulators + //@{ + + //! Resolve address + /*! + Resolves the hostname to an address. This can be done any number of + times and is done automatically by the c'tor taking a hostname. + Throws XSocketAddress if resolution is unsuccessful, after which + \c isValid returns false until the next call to this method. + */ + void resolve(); + + //@} //! @name accessors //@{ @@ -59,13 +74,13 @@ public: /*! Returns true if this address is equal to \p address. */ - bool operator==(const CNetworkAddress&) const; + bool operator==(const CNetworkAddress& address) const; //! Check address inequality /*! Returns true if this address is not equal to \p address. */ - bool operator!=(const CNetworkAddress&) const; + bool operator!=(const CNetworkAddress& address) const; //! Check address validity /*! @@ -89,15 +104,19 @@ public: //! Get hostname /*! - Returns the hostname passed to the c'tor sans the port suffix. + Returns the hostname passed to the c'tor sans any port suffix. */ CString getHostname() const; //@} +private: + void checkPort(); + private: CArchNetAddress m_address; CString m_hostname; + int m_port; }; #endif diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index dddd2586..360379fb 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -780,6 +780,7 @@ CConfig::readSectionOptions(std::istream& s) if (name == "address") { try { m_synergyAddress = CNetworkAddress(value, kDefaultPort); + m_synergyAddress.resolve(); } catch (XSocketAddress& e) { throw XConfigRead(CString("invalid address argument: ") + From d9b6575ee6e57a92f0e91ec81b8501ff35481950 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 30 Dec 2004 13:52:20 +0000 Subject: [PATCH 777/807] Updated documentation. --- ChangeLog | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/news.html | 18 ++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 70bd8328..fe887657 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,88 @@ +2004/12/30 13:28:51 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp +doc/authors.html +lib/client/CClient.cpp +lib/net/CNetworkAddress.cpp +lib/net/CNetworkAddress.h +lib/server/CConfig.cpp + +Adapted and applied patch by Brent Priddy for re-resolving the server +hostname on each connection. This allows the client to startup +without being able to resolve the server's hostname. It also lets +it handle changes in the server's address, a typical scenario when +the client is a laptop moving between networks. + +---------- +2004/12/30 12:10:47 crs +lib/platform/CMSWindowsKeyState.cpp +lib/synergy/KeyTypes.h + +Added Henkan key. Patch from rniitani at sourceforge.net. + +---------- +2004/12/30 11:54:23 crs +doc/authors.html +lib/platform/CXWindowsScreen.cpp + +Applied patch from Tom Chadwick to synthesize PageUp/PageDown on +X servers that don't support the mouse wheel. + +---------- +2004/12/29 21:12:05 crs +lib/platform/CMSWindowsScreen.cpp + +Now ignoring 4th and 5th mouse buttons if they don't exist. Was +previously querying their state, sometimes getting the wrong +answer from the OS that they were down, which prevented switching +screens. + +---------- +2004/12/29 21:10:49 crs +lib/platform/CMSWindowsKeyState.cpp + +Fixed handling of number pad number and decimal point keys when +NumLock is on on client on windows 95 family. + +---------- +2004/12/29 17:53:44 crs +lib/platform/CXWindowsScreen.cpp + +Added support for ISO_Level3_Shift on X windows server. It's +treated as if it were Mode_switch. + +---------- +2004/12/29 17:07:08 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h + +Added support for unicode keyboard layouts on OS X. + +---------- +2004/12/29 17:06:49 crs +lib/platform/COSXScreen.cpp + +Removed calls to show/hide mouse because they only work if we've +taken exclusive access to the display and we don't do that. + +---------- +2004/12/29 17:06:00 crs +lib/platform/COSXEventQueueBuffer.cpp +lib/platform/COSXEventQueueBuffer.h + +Fixed leak of event objects on OS X. + +---------- +2004/12/29 17:00:17 crs +//depot/project/synergy-web/Makefile +//depot/project/synergy-web/autostart.htmls +//depot/project/synergy-web/synergy.css +doc/autostart.html +doc/synergy.css + +Added Mac OS X autostart documentation from Tor Slettnes (tor@slett.net). + +---------- 2004/11/12 15:50:04 crs lib/platform/COSXKeyState.cpp lib/platform/COSXKeyState.h diff --git a/doc/news.html b/doc/news.html index 5470627f..f888c1f6 100644 --- a/doc/news.html +++ b/doc/news.html @@ -7,6 +7,22 @@

    Synergy News

    +Dec-30-2004 - Synergy 1.2.0 released + +

    +Made following changes: +

    +
      +
    • Improved support for moving laptops between networks (Brent Priddy) +
    • Added ISO_Level3_Shift support on X windows +
    • Now doing PageUp/PageDown if no mouse wheel on X windows (Tom Chadwick) +
    • Fixed handling of number pad number keys on Windows 95/98/Me +
    • Fixed handling of non-existant 4th and 5th mouse buttons on Windows +
    • Added support for Unicode keyboard layouts on OS X +
    • Fixed memory leak on OS X +
    • Added OS X autostart documentation (Tor Slettnes) +
    + Nov-12-2004 - Synergy 1.1.10 released

    @@ -14,7 +30,7 @@ Made following changes:

    • Fixed race in condition variable wrapper; caused synergy to hang randomly -
    • Fixed modifier key and caps-lock handling on OSX +
    • Fixed modifier key and caps-lock handling on OS X
    • System info log message now filtered like all other messages
    From 03df4ce6314e557e61079e6fba3cfed7cf654a22 Mon Sep 17 00:00:00 2001 From: crs Date: Thu, 30 Dec 2004 13:52:52 +0000 Subject: [PATCH 778/807] Changed version to 1.2.0. --- lib/common/Version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/Version.h b/lib/common/Version.h index 79d9ae6c..fc7ed48c 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.1.11" +# define VERSION "1.2.0" #endif // important strings From 8ddb77b8119f710f3ea723c774acac77e0c25b4c Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 Jan 2005 20:16:50 +0000 Subject: [PATCH 779/807] Changed version to 1.3.0. --- lib/common/Version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/Version.h b/lib/common/Version.h index fc7ed48c..0eadf6fc 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.2.0" +# define VERSION "1.3.0" #endif // important strings From 2e67f1e0d420d7582a1f402a5b8ad364734eb826 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 Jan 2005 20:17:24 +0000 Subject: [PATCH 780/807] Fixed description of client command line options (removed --camp and --no-camp, which are obsolete). --- doc/running.html | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/doc/running.html b/doc/running.html index 24b7e1ef..6f6ced05 100644 --- a/doc/running.html +++ b/doc/running.html @@ -348,21 +348,7 @@ the system's hostname and port defaults to 24800. address is the hostname or IP address of the server and port is the optional network port on the server to connect to. The client accepts the -common options and: -

    -

    - - - - - - - - - - - -
      --camp  retry connection to server until successful
      --no-camp  try connection to server only once
    +common options.

    From 23ea82968dd823aabceebf6bccef78ab6c02cfd9 Mon Sep 17 00:00:00 2001 From: crs Date: Sat, 1 Jan 2005 20:52:01 +0000 Subject: [PATCH 781/807] Fixed compiling documentation. --- doc/compiling.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/compiling.html b/doc/compiling.html index f3f716ef..49f2c764 100644 --- a/doc/compiling.html +++ b/doc/compiling.html @@ -72,7 +72,7 @@ so synergy can find the X11 includes and libraries.
  • Mac OS X with XCode

    - Start XCode and open the synery.xcode + Start XCode and open the synergy.xcode project. Build the all project using the Deployment flavor.

    @@ -86,8 +86,8 @@ so synergy can find the X11 includes and libraries. You'll need NSIS, the Nullsoft Scriptable Install System. Build All - Release then build Installer - Release. - This creates SynergyInstaller.exe in the top - level directory. Run this to install synergy. + This creates SynergyInstaller.exe in the + build directory. Run this to install synergy.

    Alternatively, you can simply copy the following files from the From 42b4e2488a0591dbbd4db69e3bcea9380650d0ee Mon Sep 17 00:00:00 2001 From: crs Date: Tue, 4 Jan 2005 19:28:22 +0000 Subject: [PATCH 782/807] Fixed bug in OS X server key translation which pretty much broke any keystroke with a modifier key. --- lib/platform/COSXKeyState.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index 9704d9aa..c5c27e8f 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -432,11 +432,7 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, UInt32 modifiers; GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers); - modifiers &= ~(cmdKey | controlKey | rightControlKey); - - // build keycode - UInt16 keycode = - static_cast((modifiers & 0xff00u) | (vkCode & 0x00ffu)); + modifiers = (modifiers & ~(cmdKey | controlKey | rightControlKey)) >> 8; // choose action UInt16 action; @@ -456,8 +452,10 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, // translate key UniCharCount count; UniChar chars[2]; - OSStatus status = UCKeyTranslate(m_uchrResource, keycode, action, - modifiers, m_keyboardType, 0, &m_deadKeyState, + OSStatus status = UCKeyTranslate(m_uchrResource, + vkCode & 0xffu, action, + modifiers & 0xffu, + m_keyboardType, 0, &m_deadKeyState, sizeof(chars) / sizeof(chars[0]), &count, chars); // get the characters From 1bd227922e2ef1e9e7eb1772316ed43f07db6aa3 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 26 Jan 2005 19:01:53 +0000 Subject: [PATCH 783/807] Integrated some changes from 1.2 branch. --- doc/news.html | 20 +++++++++++++++++ lib/platform/COSXKeyState.cpp | 13 +++++++++-- lib/platform/CXWindowsKeyState.cpp | 35 ++++++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/doc/news.html b/doc/news.html index f888c1f6..fac5f2a8 100644 --- a/doc/news.html +++ b/doc/news.html @@ -7,6 +7,26 @@

    Synergy News

    +Jan-26-2005 - Synergy 1.2.2 released + +

    +Made following changes: +

    +
      +
    • Fixed major OS X modifier key handling bug +
    • Fixed handling of ISO_Level3_Shift on X11 +
    + +Jan-04-2005 - Synergy 1.2.1 released + +

    +Made following changes: +

    +
      +
    • Fixed major OS X keyboard handling bug +
    • Fixed some minor documentation bugs +
    + Dec-30-2004 - Synergy 1.2.0 released

    diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index c5c27e8f..035a0468 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -315,8 +315,17 @@ COSXKeyState::mapKey(Keystrokes& keys, KeyID id, // if the desired mask includes Alt or Control then match the // desired mask. this ensures that combinations like // Command+Shift+S use the Command and Shift modifiers and - // those like Command+S do not use the shift modifier. - if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) != 0) { + // those like Command+S do not use the shift modifier. do not + // do this if the key to synthesize is a modifier key, otherwise + // we'd apply modifiers to modifiers which breaks things (by + // say, putting a Control press and release around a Control + // press). + if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) != 0 && + id != kKeyShift_L && id != kKeyShift_R && + id != kKeyControl_L && id != kKeyControl_R && + id != kKeyAlt_L && id != kKeyAlt_R && + id != kKeySuper_L && id != kKeySuper_R && + id != kKeyMeta_L && id != kKeyMeta_R) { return addKeystrokes(keys, sequence.back().m_button, desiredMask, KeyModifierShift | KeyModifierSuper | diff --git a/lib/platform/CXWindowsKeyState.cpp b/lib/platform/CXWindowsKeyState.cpp index fbcc53c2..8e303e2e 100644 --- a/lib/platform/CXWindowsKeyState.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -414,10 +414,41 @@ CXWindowsKeyState::updateKeysymMap() } // get keysym and get/create key mapping - const int keycodeIndex = keycode - minKeycode; - const KeySym keysym = keysyms[keycodeIndex * + int keycodeIndex = keycode - minKeycode; + KeySym keysym = keysyms[keycodeIndex * keysymsPerKeycode + 0]; + // prefer XK_ISO_Level3_Shift over XK_Mode_switch. newer + // versions of X use the former only. we assume here that + // if you have XK_ISO_Level3_Shift mapped then that's what + // you want to use even if XK_Mode_switch is also mapped. + if (j == 0 && keysym == XK_Mode_switch) { + // sort modifiers->modifiermap for this modifier so + // that a keycode mapped to XK_ISO_Level3_Shift appears + // before those mapped to anything else. this one keycode + // is enough so long as it's first because mapModifier in + // CKeyState uses the first keycode in modifierKeys to + // generate the key event for this modifier. + KeyCode* keycodes = modifiers->modifiermap + + i * keysPerModifier + j; + for (unsigned int k = j + 1; k < keysPerModifier; ++k) { + KeySym keysym2 = keysyms[(keycodes[k] - minKeycode) * + keysymsPerKeycode + 0]; + if (keysym2 == XK_ISO_Level3_Shift) { + // found an XK_ISO_Level3_Shift. swap it with + // the keycode in index j. + keycodes[j] = keycodes[k]; + keycodes[k] = keycode; + + // now use the new first keycode + keycode = keycodes[j]; + keycodeIndex = keycode - minKeycode; + keysym = keysym2; + break; + } + } + } + // get modifier mask if we haven't yet. this has the side // effect of setting the m_*Mask members. if (mask == 0) { From 73acb7860d2e501335a0462ce25074220c692f79 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 24 Apr 2005 11:56:28 +0000 Subject: [PATCH 784/807] Integrated with 1.2.2. --- ChangeLog | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/ChangeLog b/ChangeLog index fe887657..7d1f8d2b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,47 @@ +2005/01/26 18:45:45 crs +lib/common/Version.h + +Changed version to 1.2.2. + +---------- +2005/01/26 18:43:33 crs +lib/platform/COSXKeyState.cpp + +Fixed bug in handling modifier keys on OS X clients. Was applying +modifiers to modifiers yielding, for example: ctrl down, ctrl down, +ctrl up for press of the control key. The first down and the up were +there because we were applying the control modifier to the control +key. + +---------- +2005/01/26 18:41:28 crs +lib/platform/CXWindowsKeyState.cpp + +Fixed handling of ISO_Level3_Shift. We now prefer ISO_Level3_Shift +over Mode_switch if it's mapped to any key. ISO_Level3_Shift +replaces Mode_switch in newer versions of X and Mode_switch does +nothing, so we have to use ISO_Level3_Shift if it's there. + +---------- +2005/01/04 19:29:58 crs +lib/platform/COSXKeyState.cpp + +Fixed bug in OS X server key translation which pretty much broke any +keystroke with a modifier key. + +---------- +2005/01/01 20:52:43 crs +doc/compiling.html + +Merged documentation fixes. + +---------- +2005/01/01 20:19:42 crs +doc/running.html + +Merged documentation fix from mainline. + +---------- 2004/12/30 13:28:51 crs cmd/synergyc/synergyc.cpp cmd/synergys/synergys.cpp From 52ae6564112ebc94f9d145d14182e67788ea43f5 Mon Sep 17 00:00:00 2001 From: crs Date: Sun, 17 Jun 2007 11:19:18 +0000 Subject: [PATCH 785/807] Restored lost files and changes in version 1.3.1 to depot. --- ChangeLog | 1894 +++++ acinclude.m4 | 96 +- cmd/launcher/CAddScreen.cpp | 427 + cmd/launcher/CAddScreen.h | 74 + cmd/launcher/CAdvancedOptions.cpp | 2 +- cmd/launcher/CAutoStart.cpp | 131 +- cmd/launcher/CAutoStart.h | 34 +- cmd/launcher/CGlobalOptions.cpp | 11 + cmd/launcher/CHotkeyOptions.cpp | 1864 +++++ cmd/launcher/CHotkeyOptions.h | 225 + cmd/launcher/CInfo.cpp | 111 + cmd/launcher/CInfo.h | 57 + cmd/launcher/CScreensLinks.cpp | 855 ++ cmd/launcher/CScreensLinks.h | 138 + cmd/launcher/LaunchUtil.cpp | 57 +- cmd/launcher/LaunchUtil.h | 10 +- cmd/launcher/Makefile.am | 8 + cmd/launcher/launcher.cpp | 921 +-- cmd/launcher/launcher.dsp | 36 +- cmd/launcher/launcher.rc | 369 +- cmd/launcher/resource.h | 116 +- cmd/launcher/synergy.ico | Bin 1078 -> 8478 bytes cmd/synergyc/CClientTaskBarReceiver.cpp | 18 +- cmd/synergyc/CClientTaskBarReceiver.h | 1 + .../CMSWindowsClientTaskBarReceiver.cpp | 35 + cmd/synergyc/Makefile.am | 6 + cmd/synergyc/resource.h | 10 +- cmd/synergyc/synergyc.cpp | 79 +- cmd/synergyc/synergyc.dsp | 4 +- cmd/synergyc/synergyc.ico | Bin 1078 -> 8478 bytes cmd/synergyc/synergyc.rc | 18 + cmd/synergyc/tb_error.ico | Bin 318 -> 318 bytes cmd/synergyc/tb_idle.ico | Bin 318 -> 318 bytes cmd/synergyc/tb_run.ico | Bin 318 -> 318 bytes cmd/synergyc/tb_wait.ico | Bin 318 -> 318 bytes .../CMSWindowsServerTaskBarReceiver.cpp | 35 + cmd/synergys/CServerTaskBarReceiver.cpp | 13 +- cmd/synergys/Makefile.am | 6 + cmd/synergys/resource.h | 10 +- cmd/synergys/synergys.cpp | 300 +- cmd/synergys/synergys.dsp | 4 +- cmd/synergys/synergys.ico | Bin 1078 -> 8478 bytes cmd/synergys/synergys.rc | 18 + cmd/synergys/tb_error.ico | Bin 318 -> 318 bytes cmd/synergys/tb_idle.ico | Bin 318 -> 318 bytes cmd/synergys/tb_run.ico | Bin 318 -> 318 bytes cmd/synergys/tb_wait.ico | Bin 318 -> 318 bytes configure.in | 57 +- dist/nullsoft/synergy.nsi | 26 +- dist/rpm/synergy.spec.in | 11 +- doc/Makefile.am | 11 +- doc/about.html | 55 + doc/authors.html | 47 +- doc/autostart.html | 222 +- doc/banner.html | 16 + doc/border.html | 14 + doc/compiling.html | 96 +- doc/configuration.html | 589 +- doc/contact.html | 44 + doc/developer.html | 53 +- doc/faq.html | 124 +- doc/history.html | 16 +- doc/home.html | 61 + doc/index.html | 119 +- doc/license.html | 155 +- doc/news.html | 406 +- doc/roadmap.html | 92 + doc/running.html | 240 +- doc/security.html | 31 +- doc/synergy.css | 17 +- doc/tips.html | 66 +- doc/toc.html | 43 + doc/trouble.html | 204 + lib/arch/CArch.cpp | 20 +- lib/arch/CArch.h | 3 + lib/arch/CArchConsoleUnix.cpp | 8 +- lib/arch/CArchConsoleUnix.h | 3 +- lib/arch/CArchConsoleWindows.cpp | 445 +- lib/arch/CArchConsoleWindows.h | 37 +- lib/arch/CArchDaemonWindows.cpp | 35 +- lib/arch/CArchLogUnix.cpp | 6 + lib/arch/CArchLogUnix.h | 1 + lib/arch/CArchLogWindows.cpp | 6 + lib/arch/CArchLogWindows.h | 1 + lib/arch/CArchMiscWindows.cpp | 46 +- lib/arch/CArchMiscWindows.h | 27 + lib/arch/CArchMultithreadPosix.cpp | 7 +- lib/arch/CArchNetworkBSD.cpp | 46 +- lib/arch/CArchNetworkBSD.h | 1 + lib/arch/CArchNetworkWinsock.cpp | 24 + lib/arch/CArchNetworkWinsock.h | 1 + lib/arch/CMultibyte.cpp | 12 + lib/arch/IArchConsole.h | 9 + lib/arch/IArchLog.h | 9 + lib/arch/IArchMultithread.h | 1 + lib/arch/IArchNetwork.h | 7 + lib/arch/arch.dsp | 4 +- lib/base/CEvent.cpp | 18 +- lib/base/CEvent.h | 19 +- lib/base/CEventQueue.cpp | 26 +- lib/base/CLog.cpp | 107 +- lib/base/CLog.h | 23 +- lib/base/CStringUtil.cpp | 69 +- lib/base/CStringUtil.h | 22 - lib/base/ILogOutputter.h | 9 + lib/base/LogOutputters.cpp | 39 +- lib/base/LogOutputters.h | 6 +- lib/base/base.dsp | 4 +- lib/client/CClient.cpp | 71 +- lib/client/CClient.h | 16 +- lib/client/CServerProxy.cpp | 75 +- lib/client/CServerProxy.h | 10 +- lib/client/client.dsp | 4 +- lib/common/Version.cpp | 4 +- lib/common/Version.h | 2 +- lib/common/common.dsp | 4 +- lib/common/common.h | 16 +- lib/io/io.dsp | 4 +- lib/mt/mt.dsp | 4 +- lib/net/CTCPListenSocket.cpp | 1 + lib/net/CTCPSocket.cpp | 6 +- lib/net/net.dsp | 4 +- lib/platform/CMSWindowsDesks.cpp | 147 +- lib/platform/CMSWindowsDesks.h | 37 +- lib/platform/CMSWindowsKeyState.cpp | 2722 +++--- lib/platform/CMSWindowsKeyState.h | 194 +- lib/platform/CMSWindowsScreen.cpp | 539 +- lib/platform/CMSWindowsScreen.h | 62 +- lib/platform/CMSWindowsScreenSaver.cpp | 6 +- lib/platform/COSXClipboard.cpp | 9 +- lib/platform/COSXKeyState.cpp | 1759 ++-- lib/platform/COSXKeyState.h | 249 +- lib/platform/COSXScreen.cpp | 982 ++- lib/platform/COSXScreen.h | 127 +- lib/platform/COSXScreenSaver.cpp | 131 +- lib/platform/COSXScreenSaver.h | 22 +- lib/platform/COSXScreenSaverUtil.h | 39 + lib/platform/COSXScreenSaverUtil.m | 81 + lib/platform/CSynergyHook.cpp | 385 +- lib/platform/CSynergyHook.h | 3 + lib/platform/CXWindowsClipboard.cpp | 64 +- lib/platform/CXWindowsClipboard.h | 10 +- lib/platform/CXWindowsKeyState.cpp | 1662 ++-- lib/platform/CXWindowsKeyState.h | 171 +- lib/platform/CXWindowsScreen.cpp | 511 +- lib/platform/CXWindowsScreen.h | 35 +- lib/platform/CXWindowsScreenSaver.cpp | 46 +- lib/platform/CXWindowsScreenSaver.h | 5 + lib/platform/CXWindowsUtil.cpp | 2562 +++--- lib/platform/CXWindowsUtil.h | 68 +- lib/platform/Makefile.am | 3 + lib/platform/OSXScreenSaverControl.h | 36 + lib/platform/platform.dsp | 4 +- lib/platform/synrgyhk.dsp | 4 +- lib/server/CBaseClientProxy.cpp | 52 + lib/server/CBaseClientProxy.h | 85 + lib/server/CClientProxy.cpp | 8 +- lib/server/CClientProxy.h | 8 +- lib/server/CClientProxy1_0.cpp | 40 +- lib/server/CClientProxy1_0.h | 10 +- lib/server/CClientProxy1_3.cpp | 114 + lib/server/CClientProxy1_3.h | 47 + lib/server/CClientProxyUnknown.cpp | 5 + lib/server/CConfig.cpp | 1959 ++++- lib/server/CConfig.h | 262 +- lib/server/CInputFilter.cpp | 999 +++ lib/server/CInputFilter.h | 322 + lib/server/CPrimaryClient.cpp | 63 +- lib/server/CPrimaryClient.h | 39 +- lib/server/CServer.cpp | 781 +- lib/server/CServer.h | 178 +- lib/server/Makefile.am | 6 + lib/server/server.dsp | 28 +- lib/synergy/CKeyMap.cpp | 1330 +++ lib/synergy/CKeyMap.h | 491 ++ lib/synergy/CKeyState.cpp | 1012 ++- lib/synergy/CKeyState.h | 220 +- lib/synergy/CPlatformScreen.cpp | 32 +- lib/synergy/CPlatformScreen.h | 17 +- lib/synergy/CScreen.cpp | 138 +- lib/synergy/CScreen.h | 57 +- lib/synergy/IClient.h | 10 +- lib/synergy/IKeyState.cpp | 106 +- lib/synergy/IKeyState.h | 56 +- lib/synergy/IPlatformScreen.h | 20 +- lib/synergy/IPrimaryScreen.cpp | 68 +- lib/synergy/IPrimaryScreen.h | 78 +- lib/synergy/IScreen.cpp | 16 + lib/synergy/IScreen.h | 16 + lib/synergy/ISecondaryScreen.h | 4 +- lib/synergy/KeyTypes.cpp | 204 + lib/synergy/KeyTypes.h | 76 +- lib/synergy/Makefile.am | 4 + lib/synergy/OptionTypes.h | 57 +- lib/synergy/ProtocolTypes.cpp | 47 + lib/synergy/ProtocolTypes.h | 97 +- lib/synergy/libsynergy.dsp | 20 +- synergy.dsw | 9 +- synergy.xcode/project.pbxproj | 7352 ++++++----------- 199 files changed, 27622 insertions(+), 13726 deletions(-) create mode 100644 cmd/launcher/CAddScreen.cpp create mode 100644 cmd/launcher/CAddScreen.h create mode 100644 cmd/launcher/CHotkeyOptions.cpp create mode 100644 cmd/launcher/CHotkeyOptions.h create mode 100644 cmd/launcher/CInfo.cpp create mode 100644 cmd/launcher/CInfo.h create mode 100644 cmd/launcher/CScreensLinks.cpp create mode 100644 cmd/launcher/CScreensLinks.h create mode 100644 doc/about.html create mode 100644 doc/banner.html create mode 100644 doc/border.html create mode 100644 doc/contact.html create mode 100644 doc/home.html create mode 100644 doc/roadmap.html create mode 100644 doc/toc.html create mode 100644 doc/trouble.html create mode 100644 lib/platform/COSXScreenSaverUtil.h create mode 100644 lib/platform/COSXScreenSaverUtil.m create mode 100644 lib/platform/OSXScreenSaverControl.h create mode 100644 lib/server/CBaseClientProxy.cpp create mode 100644 lib/server/CBaseClientProxy.h create mode 100644 lib/server/CClientProxy1_3.cpp create mode 100644 lib/server/CClientProxy1_3.h create mode 100644 lib/server/CInputFilter.cpp create mode 100644 lib/server/CInputFilter.h create mode 100644 lib/synergy/CKeyMap.cpp create mode 100644 lib/synergy/CKeyMap.h create mode 100644 lib/synergy/KeyTypes.cpp create mode 100644 lib/synergy/ProtocolTypes.cpp diff --git a/ChangeLog b/ChangeLog index 7d1f8d2b..10ccab7b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,1897 @@ +2006/04/02 12:16:23 crs +lib/platform/CXWindowsKeyState.cpp + +Fixed non-XKB handling of Mode_switch. There were two problems. +First the XkbLockGroup function was being called even for non-XKB +layouts. (And was being included in the compile even if the XKB +headers were not available!) Second, modifiers were assumed to +exist only the group in which a keysym was found and when looking +for modifiers for a key we only check modifiers in the same group. +So keys that needed Mode_switch (in group 1) would check to see if +Mode_switch was explictly mapped in group 1. It never is so that +would never work. Now we handle implicitly mapped modifiers. I +hadn't noticed this problem because my system (like most others) +uses XKB and when I forced non-XKB, the keys I tested worked anyway +through Multi_key. + +---------- +2006/04/01 22:25:33 crs +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp + +Fixed autorepeat on X11. Was autorepeating all keys sent from +server. Now autorepeating only those keys which are locally +configured in X to autorepeat. + +---------- +2006/04/01 21:37:24 crs +lib/platform/CMSWindowsKeyState.cpp + +Fixed autorepeat on win32 clients. Was synthesizing a key release +for each repeat. Win32 wants only repeat press events. + +---------- +2006/04/01 21:36:50 crs +cmd/launcher/CScreensLinks.cpp + +Fixed two bugs in screens and link dialog. First, the link controls +were not updated when reopening the dialog. Second, a change in any +link edit control would be discarded if the user pressed enter. + +---------- +2006/04/01 21:35:10 crs +cmd/launcher/CHotkeyOptions.cpp + +Fixed crash when creating a new hotkey but picking a key or mouse +button combination before clicking OK. + +---------- +2006/04/01 21:30:43 crs +configure.in +lib/arch/CArchNetworkBSD.cpp +lib/common/common.h + +Removed use of alloca() from unix and portable code. It's still +in win32 code but i don't have to play guessing games about +whether it's there or not on that platform. + +---------- +2006/04/01 17:53:27 crs +synergy.xcode/project.pbxproj + +Added new files to Xcode project. + +---------- +2006/04/01 17:41:59 crs +lib/client/CServerProxy.cpp +lib/net/CTCPSocket.cpp +lib/server/CClientProxy1_0.cpp + +Added more debugging output for network problems. + +---------- +2006/04/01 17:01:56 crs +lib/platform/COSXKeyState.cpp + +Fixed keymapping on OSX. Was checking keyboard tables for all +modifier combinations, including right-handed modifiers. It +turns out OSX doesn't set up the tables correctly for those +modifiers and acts if they have no effect. Since OSX never +generates right-handed modifiers this isn't too surprising. + +---------- +2006/04/01 15:32:19 crs +lib/server/CBaseClientProxy.cpp +lib/server/CBaseClientProxy.h +lib/server/CClientProxy.cpp +lib/server/CClientProxy.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/Makefile.am +lib/server/server.dsp + +Added new class to allow the server to keep information that every +screen has. The first such info is the cursor position when last +jumping from the screen. Using a hotkey to jump to a screen now +puts the cursor where it was the last time the user jumped from +that screen. + +---------- +2006/04/01 14:51:22 crs +lib/server/CInputFilter.cpp + +Fixed bug in reloading configurations. Was losing hotkeys. + +---------- +2006/04/01 14:49:41 crs +lib/platform/CXWindowsKeyState.cpp + +Fixed bug when recollecting the keyboard map on non-XKB keyboards. +Wasn't reseting a key map. It's unlikely anyone ever hit this bug. + +---------- +2006/04/01 13:39:09 crs +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreenSaver.cpp + +Fixed several uses of CXWindowsUtil::CErrorLock that take a flag. +Was checking the flag before destroying the lock object. That +doesn't reliably work because the X protocol is asynchronous. The +lock object ensures that the flag is correctly set in its d'tor by +synchronizing with the server. The X11 hotkey (un)registration was +one place where this was done. There were places in the X11 +screensaver and clipboard handling, too. + +---------- +2006/04/01 12:55:17 crs +lib/synergy/CKeyState.cpp + +Fixed failure to clear the state of which keys are pressed in +fakeAllKeysUp(). This fixes problems with synergy clients +thinking some keys are still down, causing weird key behavior. + +---------- +2006/04/01 12:53:31 crs +lib/common/Version.h + +Changed version to 1.3.1. + +---------- +2006/03/21 21:54:16 crs +lib/platform/CXWindowsUtil.cpp + +Add all #defines for including keysyms that we use. The #defines in +X11/keysym.h vary from platform to platform and cannot be relied on. + +---------- +2006/03/21 21:42:53 crs +lib/common/Version.h + +Changed version to 1.3.0. + +---------- +2006/03/21 21:38:52 crs +cmd/launcher/Makefile.am + +Added new files to makefile. + +---------- +2006/03/21 21:38:02 crs +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/CHotkeyOptions.cpp +cmd/launcher/CHotkeyOptions.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergyc/synergyc.dsp +cmd/synergys/synergys.dsp +lib/arch/arch.dsp +lib/base/base.dsp +lib/client/client.dsp +lib/common/common.dsp +lib/io/io.dsp +lib/mt/mt.dsp +lib/net/net.dsp +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/CConfig.cpp +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/server.dsp +lib/synergy/IKeyState.cpp +lib/synergy/IKeyState.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h +lib/synergy/libsynergy.dsp + +Added a hot key dialog to the win32 launcher, providing full support +for all features of hot keys. This required a bunch of small changes +to CInputFilter and stuff used by it (mainly getter methods). + +The hot key dialog uses dynamic_cast<> to determine the kinds of +conditions and actions for each rule in the configuration. This +required enabling RTTI in the project files. + +Also changed the 'Start' button to start the synergy service if +installed (i.e. synergy is configured to start when the computer +starts). + +---------- +2006/03/21 21:37:59 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h + +Changed AltGr handling on win32. Previously the server would send +ctrl and alt up events when sending a key that used AltGr then send +matching down events. Now we just clear the ctrl and alt bits in +the mask sent with the key; clients will temporarily release the +ctrl and alt modifiers as necessary to yield the key. If the key +doesn't need AltGr then the ctrl and alt bits are kept. We also +used to reserve the right alt key for AltGr even if the keyboard +layout didn't use it that way. This has been removed. The keyboard +mapping now presses the ctrl and alt keys for AltGr rather than use +the right alt. + +Also made getKeyID() a public method. + +---------- +2006/03/21 21:37:57 crs +lib/platform/CMSWindowsScreen.cpp +lib/platform/COSXScreen.cpp +lib/platform/CXWindowsScreen.cpp + +Improved log output when registering hot keys. + +---------- +2006/03/21 21:37:53 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Added "server" and "client" to synergy log windows. + +---------- +2006/03/21 21:37:52 crs +lib/synergy/CKeyMap.cpp + +Fixed bug in keyboard mapping when releasing modifiers. This caused +a problem with AltGr on win32. The left alt key was being +(synthetically) released but it was the right alt key that was down. + +---------- +2006/03/21 21:37:49 crs +doc/configuration.html + +Documentation fix. + +---------- +2006/03/20 23:13:11 crs +doc/images/warp.gif + +Replaced animated GIF demonstrating cursor warp with one that +doesn't show the cursor in the region between the monitors. +This is the original GIF with frames removed. Supplied by +user Brian A. + +---------- +2006/03/18 19:17:46 crs +synergy.xcode/project.pbxproj + +Updated Xcode project to build universal binaries. The PPC build +uses the 10.2.8 SDK while the i386 build uses the 10.4u SDK. Also +removed the "Default" configuration; "Development" is now the +default. + +---------- +2006/03/18 16:39:43 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp + +Improved handling of option key on OS X servers. Previously it +was always treated as the super modifier. Now it's treated as +AltGr if it generates a glyph, though AltGr is not sent to the +clients. So if option+key produces a glyph then key is sent +without the option or AltGr modifiers. If option+key does not +produce a glpyh then option is sent as the super modifier. +Note that combining an option+key combination that would produce +a glyph with the command or control modifiers will cause it to +not produce a glyph. In that case we send option as the super +modifier and we send the glyph that would've been produced on +the server had the option key not been pressed. So option+s +sends the beta key id with no modifiers but command+option+s +sends the "s" key id with the alt and super modifiers. + +This seems to handle the user expectations. However some users +may expect option+L to produce win+L on win32 clients. These +same users probably expect option+? to produce an upside down +question mark. But these two expectations are fundamentally at +odds. We cannot satisfy everyone because OS X doesn't have +enough modifier keys. + +---------- +2006/03/18 13:20:18 crs +lib/server/CInputFilter.cpp + +Fixed mousebutton condition. Wasn't working if num lock, caps lock +or scroll lock was on. + +---------- +2006/03/18 12:05:34 crs +lib/platform/CXWindowsScreen.cpp + +Applied patch from Jaco Kroon to fix a problem with mouse focus +on X11. + +---------- +2006/03/18 11:54:40 crs +doc/index.html + +Added support for index.html?child#anchor syntax. This will open +the index.html page then child in the page frame and jump to anchor. + +---------- +2006/03/12 20:24:43 crs +cmd/launcher/CAutoStart.cpp +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/synergyc.cpp +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/synergys.cpp +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchConsoleUnix.cpp +lib/arch/CArchConsoleUnix.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchConsoleWindows.h +lib/arch/CArchLogUnix.cpp +lib/arch/CArchLogUnix.h +lib/arch/CArchLogWindows.cpp +lib/arch/CArchLogWindows.h +lib/arch/IArchConsole.h +lib/arch/IArchLog.h +lib/base/CLog.cpp +lib/base/ILogOutputter.h +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h + +Added show() method to console and logs. + +---------- +2006/03/12 20:24:14 crs +dist/nullsoft/synergy.nsi + +Updated windows installer to install new documentation pages and +to put a shortcut on the desktop. + +---------- +2006/03/12 20:23:46 crs +cmd/launcher/synergy.ico +cmd/synergyc/synergyc.ico +cmd/synergyc/tb_error.ico +cmd/synergyc/tb_idle.ico +cmd/synergyc/tb_run.ico +cmd/synergyc/tb_wait.ico +cmd/synergys/synergys.ico +cmd/synergys/tb_error.ico +cmd/synergys/tb_idle.ico +cmd/synergys/tb_run.ico +cmd/synergys/tb_wait.ico + +Updated icons on win32. + +---------- +2006/03/12 12:42:18 crs +doc/configuration.html +doc/faq.html +doc/running.html +doc/trouble.html + +More documentation fixes. + +---------- +2006/03/12 12:19:02 crs +doc/configuration.html +doc/faq.html +doc/Makefile.am +doc/toc.html +doc/trouble.html + +Added a page with typical problems and solutions. + +---------- +2006/03/12 10:25:15 crs +doc/tips.html + +More documentation fixes. + +---------- +2006/03/12 10:20:14 crs +doc/about.html +doc/configuration.html +doc/faq.html + +Documentation fixes. + +---------- +2006/03/12 09:34:16 crs +doc/toc.html + +Fixed link in table of contents. + +---------- +2006/03/11 15:01:00 crs +doc/banner.html +doc/border.html +doc/index.html +doc/synergy.css + +Adjusted how the border under the banner is drawn. + +---------- +2006/03/11 14:49:38 crs +doc/images/logo.gif + +Updated logo. + +---------- +2006/03/11 14:42:00 crs +doc/about.html +doc/authors.html +doc/autostart.html +doc/banner.html +doc/compiling.html +doc/configuration.html +doc/contact.html +doc/developer.html +doc/faq.html +doc/history.html +doc/home.html +doc/images/logo.gif +doc/images/warp.gif +doc/index.html +doc/license.html +doc/Makefile.am +doc/news.html +doc/roadmap.html +doc/running.html +doc/security.html +doc/synergy.css +doc/tips.html +doc/toc.html +doc/todo.html + +Updated documentation pages. They're now the web site pages except +they now use frames. + +---------- +2006/03/08 20:07:09 crs +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/CSynergyHook.cpp +lib/platform/CSynergyHook.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/server/CInputFilter.cpp +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IPlatformScreen.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h + +Added beginnings of support for synthesizing keystrokes on server's +screen. It's partly implemented on win32; it just needs to track +the modifier keys and adjust them as appropriate when synthesizing +keys. It's not implemented on X11 or OS X. It's also currently +disabled (in CPrimaryClient.cpp). + +---------- +2006/03/08 20:05:38 crs +cmd/synergyc/CClientTaskBarReceiver.cpp +cmd/synergyc/CClientTaskBarReceiver.h +cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +cmd/synergyc/resource.h +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.rc +cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +cmd/synergys/CServerTaskBarReceiver.cpp +cmd/synergys/resource.h +cmd/synergys/synergys.cpp +cmd/synergys/synergys.rc +lib/arch/CArch.cpp +lib/arch/CArchConsoleUnix.cpp +lib/arch/CArchConsoleUnix.h +lib/arch/CArchConsoleWindows.cpp +lib/arch/CArchConsoleWindows.h +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/base/CLog.cpp +lib/base/LogOutputters.cpp +lib/base/LogOutputters.h +lib/client/CClient.cpp +lib/client/CClient.h +lib/common/Version.cpp + +Replaced using win32 console for log with a dialog containing a rich +edit control. The user can close this window without quiting synergy +and can reopen the window using the tray icon menu. Also added menu +items to switch the current logging level. + +---------- +2006/02/22 19:21:21 crs +lib/platform/COSXScreen.cpp + +Removed bogus logging call. + +---------- +2006/02/20 19:46:47 crs +cmd/launcher/launcher.cpp +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h + +Fixed infinite loop of error dialogs in win32 launcher. + +---------- +2006/02/20 12:59:20 crs +doc/faq.html + +Added firewall info to faq12. Added faq19, a discussion of not taking +the foreground on win32. + +---------- +2006/02/20 12:46:18 crs +doc/authors.html +doc/autostart.html +doc/compiling.html +doc/configuration.html +doc/developer.html +doc/faq.html +doc/history.html +doc/index.html +doc/license.html +doc/news.html +doc/running.html +doc/security.html +doc/tips.html +doc/todo.html + +Changed !DOCTYPE to HTML 4.0 (from 3.2). + +---------- +2006/02/20 12:21:34 crs +doc/configuration.html +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/CServer.cpp +lib/server/CServer.h + +Hot key overhaul. Added support for multiple actions per hot key. +Actions and conditions are now idempotent so they no longer track +the active state (on, off, toggled, etc). Actions can be assigned +to the activation or deactivation of a condition, basically a hot +key or mouse button being pressed (activation) or released +(deactivation). The keystroke and mousebutton actions map to both +and the new keyDown, keyUp, mouseDown and mouseUp map to one or the +other. The lock cursor to screen action now takes a mode: on, off +or toggle and the corresponding event state is respected by the +server. Removed the modifiers action. Mouse button actions now use +the new kKeySetModifiers and kKeyClearModifiers keys to set/reset +modifier state directly on the client. + +Conditions and actions are much simpler now that they're idempotent +and so is CInputFilter. Refactored CRule into a class since there's +now more to a rule than a condition and action. + +---------- +2006/02/20 11:29:41 crs +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h +lib/synergy/CKeyState.cpp + +Added support for kKeySetModifiers and kKeyClearModifiers keys. +The former activates the given modifiers and the latter deactivates +them. + +---------- +2006/02/20 11:25:44 crs +lib/synergy/KeyTypes.h + +Added special keys for setting/clearing modifiers. The intent +is to use these to set/reset modifiers for mouse button hot key +actions. + +---------- +2006/02/19 21:01:08 crs +lib/platform/COSXScreen.cpp + +Fixed OS X to send current keyboard modifiers with mouse button events. + +---------- +2006/02/19 13:26:54 crs +lib/client/CClient.cpp +lib/client/CClient.h + +Fixed two clipboard problems. + +First, synergy would blow an assert given the following sequence +of events: + enter client screen A + A takes clipboard + enter server screen B + B takes clipboard + clipboard on A changes (while we're not on the screen) + enter A + enter B +On entering B we find that the clipboard sender is not the owner +of the clipboard. This is because when A changed the clipboard +while we were on B, B ignored the grab from A. A now thinks it +owns the clipboard (even though B does). So when we leave A the +second time, it sends the clipboard (which contains what B sent +it) to B. The assertion is blown because B owns the clipboard +but A sends a clipboard with a valid sequence number. The fix +is simply that clients must reset their internal clipboard +ownership flag when told to set the clipboard by the server. + +Second, synergy clients would fail to send the clipboard to the +server given the following sequence of events: + enter client A + A takes the clipboard + enter screen B + B takes the clipboard + enter A + A takes the clipboard with the same contents as before +In an effort to reduce bandwidth, clients don't send unchanged +clipboard contents. Clients were incorrectly treating this +case as unchanged contents when, in fact, the contents are those +set by B not those of A. Clients now handle this case. + +---------- +2006/02/19 13:13:55 crs +lib/server/CServer.cpp + +Fixed error in log message. Was trying to report the sender of +a clipboard but was reporting the owner of the clipboard. + +---------- +2006/02/16 22:12:37 crs +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h + +Added a hack to work around how VMware handles modifier keys on X11 +when a virtual machine has focus. VMware removes all of the modifier +mappings so synergy doesn't know about any modifiers. The hack is to +use the last known good modifiers when there aren't any active +modifiers. + +---------- +2006/02/14 18:10:12 crs +lib/server/CServer.cpp + +Made switch in direction hot keys use the cursor's current position +when calculating what the neighbor is. This only affects layouts +using fractional edges. + +---------- +2006/02/12 16:49:16 crs +synergy.xcode/project.pbxproj + +Updated Xcode project. + +---------- +2006/02/12 16:40:02 crs +doc/configuration.html +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/KeyTypes.cpp + +Removed onCommandKey() from server. This was used only for handling +the ScrollLock key to lock the cursor to the screen but this has been +obsoleted by the hotkey support for locking the cursor. Also, +ScrollLock is now only added as a hotkey to lock the cursor if the +user hasn't configured some other hotkey to lock the cursor. + +---------- +2006/02/12 16:22:41 crs +doc/configuration.html +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CInputFilter.cpp +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/IKeyState.cpp +lib/synergy/IKeyState.h + +Added support for directing hotkey key actions to a particular screen +or screens or to broadcast to all screens. However, key action +keystrokes are never sent to the server screen. This should be fixed. + +---------- +2006/02/12 16:16:11 crs +doc/configuration.html +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h +lib/synergy/KeyTypes.cpp +lib/synergy/KeyTypes.h +lib/synergy/libsynergy.dsp +lib/synergy/Makefile.am +lib/synergy/mkspecialkeynames.pl +lib/synergy/SpecialKeyNameMap.h + +Moved and restructed key name maps. Also added names for all +non-alphanumeric ASCII characters and added support for \uXXXX +unicode character codes. + +---------- +2006/02/12 14:47:23 crs +lib/synergy/CKeyState.cpp + +Now allowing fake key presses from server with a server button ID +of 0. Hot keys that synthesize key events use a server button ID +of 0. Note that other code will prevent a client from processing +a hotkey press while another hotkey is pressed. The nature of +hotkeys should ensure that never happens except for modifier only +hotkeys. Worry about that later. + +---------- +2006/02/12 12:06:50 crs +lib/platform/COSXScreen.cpp + +Fixed 2 axis scrolling on OS X. + +---------- +2006/02/12 11:53:35 crs +lib/client/CClient.cpp +lib/client/CClient.h +lib/client/CServerProxy.cpp +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h +lib/server/CClientProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_3.cpp +lib/server/CClientProxy1_3.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IClient.h +lib/synergy/IPlatformScreen.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h +lib/synergy/ISecondaryScreen.h +lib/synergy/ProtocolTypes.cpp +lib/synergy/ProtocolTypes.h + +Added support for horizontal scrolling. + +---------- +2006/02/12 10:08:49 crs +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Applied patch from stkamp@users.sf.net that clamps mouse positions +to valid areas on OS X. It also improves using the local keyboard +with the remote mouse on OS X. + +---------- +2006/02/11 20:01:42 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/CMSWindowsScreen.cpp + +Fixed synthesis of ctrl+alt+del and handling of AltGr on win32 +client. + +---------- +2006/02/11 20:00:32 crs +lib/server/server.dsp + +Added CClientProxy1_3 to project. + +---------- +2006/02/06 19:27:45 crs +lib/server/CConfig.cpp + +Fixed handling of comments when parsing the configuration. +Had changed leading whitespace stripping which broke it. + +---------- +2006/02/05 19:42:55 crs +synergy.xcode/project.pbxproj + +Updated Xcode project. + +---------- +2006/02/05 18:48:35 crs +lib/server/CClientProxy1_3.cpp + +Fixed warning. + +---------- +2006/02/05 18:02:47 crs +lib/server/CInputFilter.cpp + +Fixed updates of input filters when configuration is changed. + +---------- +2006/02/05 17:55:45 crs +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_3.cpp + +More fixes for server side keep alives. + +---------- +2006/02/05 17:55:19 crs +lib/server/CServer.cpp + +Fixed sending of options to client. Wasn't reseting options first. + +---------- +2006/02/05 17:39:20 crs +lib/client/CServerProxy.cpp + +Fixed memory bug in releasing keep alive timer. + +---------- +2006/02/05 17:36:17 crs +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_3.cpp +lib/server/CClientProxy1_3.h +lib/server/CClientProxyUnknown.cpp + +Fixed server side handling of keep alives. + +---------- +2006/02/05 17:34:14 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h + +Fixed client side handling of keep alives. + +---------- +2006/02/05 16:56:00 crs +lib/synergy/ProtocolTypes.h + +Added comment for protocol version 1.3. + +---------- +2006/02/05 16:54:39 crs +lib/client/CServerProxy.cpp +lib/client/CServerProxy.h +lib/server/CClientProxy1_0.cpp +lib/server/CClientProxy1_0.h +lib/server/CClientProxy1_3.cpp +lib/server/CClientProxy1_3.h +lib/server/Makefile.am +lib/synergy/ProtocolTypes.cpp +lib/synergy/ProtocolTypes.h + +Deprecated heartbeat and added keep alive to replace it. While a +heartbeat was generated by clients and sent to the server, a keep +alive is sent from the server and echoed by the client. This +checks both directions of the connection. Either side will hang +up if the other hasn't been heard from in a reasonable amount of +time. This fixes a problem where clients would not hang up on +an unavailable server. + +---------- +2006/02/05 16:29:01 crs +cmd/launcher/Makefile.am + +Added CInfo files to makefile. + +---------- +2006/02/05 15:30:49 crs +lib/synergy/CKeyState.cpp + +Fixed handling of modifier keys that are held down while leaving +a client screen. Was correctly releasing those keys on the client +but wasn't reseting the active modifier state so as soon as a key +was pressed after reentering the client the modifiers were +reactivated. However, the user could only see the problem by +typing on the local keyboard because the modifier was correctly +adjusted for keys from the server. Now reseting modifier state +when leaving a client screen. + +---------- +2006/02/05 14:47:59 crs +cmd/launcher/CInfo.cpp +cmd/launcher/CInfo.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/LaunchUtil.cpp +cmd/launcher/LaunchUtil.h +cmd/launcher/resource.h + +Added two features to the win32 launcher. First there's now a +info dialog which reports some useful information. The other is +that configuration files are now re-read when the application is +activated and the file's modification time has changed. This +should help when users are hand editing the configuration file +while the launcher is running. + +---------- +2006/02/05 14:45:39 crs +lib/server/CConfig.cpp + +Fixed a bug in writing configuration files with fractional edges. + +---------- +2006/02/05 14:43:17 crs +cmd/launcher/CAddScreen.cpp +cmd/launcher/CScreensLinks.cpp +cmd/launcher/CScreensLinks.h +cmd/launcher/launcher.rc +cmd/launcher/resource.h +doc/configuration.html +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CServer.cpp +lib/server/CServer.h + +Added support for fractional edge links. This allows users to +configure a portion of an edge to map to a portion of another +edge, rather than linking entire edges. This does not allow +users to split a (presumably multimonitor) screen to allow +switching on reaching an interior edge. + +---------- +2006/02/01 21:34:28 crs +lib/synergy/CKeyMap.cpp + +Fixed parsing of modifier plus single character keystroke and +mousebutton entries in the configuration. Was discarding single +characters. + +---------- +2006/02/01 21:20:46 crs +lib/platform/COSXKeyState.cpp + +Fixed OS X keypad enter key. + +---------- +2006/01/29 20:50:54 crs +lib/server/CConfig.cpp +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/CServer.cpp +lib/server/CServer.h + +Added support for performing an action when a screen connects. +Also added support for jumping to the screen that just connected. +The line 'connect() = switchToScreen()' in the global options will +cause the cursor to jump to a screen as soon as it connects. This +isn't especially useful but serves as an example. + +---------- +2006/01/29 19:56:31 crs +lib/platform/CXWindowsScreen.cpp + +Fixed X11 hot key grabbing. Previously was sensitive to CapsLock, +NumLock and ScrollLock and is now insensitive to all of those. + +---------- +2006/01/29 17:54:08 crs +lib/platform/COSXScreen.cpp + +Changed input suppress delay to 0. This might be right or it +might need to be a small positive value like 0.1. + +---------- +2006/01/29 15:52:44 crs +lib/server/CConfig.h +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/CServer.cpp + +Fixed support for ScrollLock to lock the cursor to the screen. +It now works via the hotkey mechanism. + +---------- +2006/01/29 15:51:59 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h + +Fixed failure to run on win95 family. Was linked against +ToUnicodeEx which is not available on that family. Now looking +up that symbol at run time. + +---------- +2006/01/29 15:50:29 crs +lib/platform/COSXScreen.cpp + +Fixed minor bug in call to CGSetLocalEventsSuppressionInterval. + +---------- +2006/01/29 13:26:48 crs +lib/platform/CSynergyHook.cpp + +Win32 reports VK_RSHIFT as an extended key, which it is not. +We now correct this which fixes the right shift key when using +win32 servers. + +---------- +2006/01/29 12:47:31 crs +lib/synergy/CKeyMap.cpp + +Fixed handling of modifiers when synthesizing a modifier. We +were previously trying to match the desired modifier state +when synthesizing any key, even if some of those modifiers +weren't required (i.e. the key was insensitive to the state +of some of those modifiers). We no longer try to match the +state of non-required modifiers because doing so can break +the synthesis of some keys, particularly modifiers. For +example, previously pressing Control with NumLock on would +turn NumLock off, press Control, then turn NumLock back on. +If this Control was used with Alt on win32 to effect AltGr +then AltGr would not take effect. On X11 the Shift key was +improperly combined with NumLock, causing mouse keys to take +effect. + +---------- +2006/01/29 12:37:30 crs +lib/common/Version.h + +Changed to version 1.2.8. + +---------- +2005/12/18 18:00:56 crs +synergy.xcode/project.pbxproj + +Added new files to xcode project. + +---------- +2005/12/18 17:35:57 crs +doc/configuration.html +doc/news.html + +More documentation for 1.2.7. + +---------- +2005/12/18 16:51:52 crs +doc/news.html + +Fixed error in documentation. + +---------- +2005/12/18 16:15:42 crs +lib/platform/CSynergyHook.cpp + +Removed use of function from C standard library in the hook DLL. + +---------- +2005/12/18 16:11:15 crs +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Added workaround for not receiving drag events for any mouse buttons +besides 1 and 2 (left and right). That appears to be a limitation +of the OS so we simply start polling the position while any buttons +besides 1 and 2 are down. Code based on a patch by Brian Kendall. + +---------- +2005/12/18 15:29:25 crs +lib/common/Version.h + +Changed version to 1.2.7. + +---------- +2005/12/18 15:12:39 crs +lib/platform/COSXKeyState.cpp + +Fixed mapping of unicode key layouts. Was discarding some +characters (backspace and return, in particular) that should +not have been discarded. + +---------- +2005/12/18 10:49:23 crs +cmd/launcher/launcher.rc +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/CServer.cpp +lib/server/server.dsp +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h +lib/synergy/IKeyState.cpp +lib/synergy/IKeyState.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h +lib/synergy/libsynergy.dsp +lib/synergy/SpecialKeyNameMap.h +synergy.dsw + +Added support for hot keys on win32. Also fixed memory leaks in +CInputFilter and changed CConfig to write hot keys in its +CInputFilter object to the options section. + +---------- +2005/12/15 18:57:38 crs +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Fixed hot keys on OS X when on a secondary screen. This worked +in the original patch but broken during the modify/merge. It +was broken because we turn off all hot key processing by the OS +while on a secondary screen. The fix is to check on key press +and release if the key and modifier state matches a hot key. + +This is known to be broken on hot key release if you press the +hot key then change the modifiers before releasing the provoking +key. + +---------- +2005/12/14 21:51:01 crs +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h + +Added support for hotkeys on X11. + +---------- +2005/12/14 21:50:49 crs +lib/server/CServer.cpp + +When jumping to another screen was computing the center using the +size of the active screen instead of the size of the destination +screen. + +---------- +2005/12/14 21:50:09 crs +lib/server/CConfig.cpp + +Switched to ctype.h from cctype to fix linux build. + +---------- +2005/12/14 09:33:25 crs +lib/base/CLog.cpp +lib/base/CLog.h +lib/base/CStringUtil.cpp +lib/base/CStringUtil.h + +Fixed bug in CStringUtil::vsprint. Was reusing va_list across +calls to vnsprintf, which is not allowed. Modified CStringUtil +and CLog to avoid doing that. This should fix a crash when +using --help on some platforms. Tthe --help message was the +only message long enough to exceed the static buffer length +which caused a va_list to get reused. + +---------- +2005/12/14 08:43:36 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CInputFilter.cpp +lib/server/CInputFilter.h +lib/server/CPrimaryClient.cpp +lib/server/CPrimaryClient.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/server/Makefile.am +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IPlatformScreen.h +lib/synergy/IPrimaryScreen.cpp +lib/synergy/IPrimaryScreen.h +lib/synergy/Makefile.am +lib/synergy/mkspecialkeynames.pl +lib/synergy/SpecialKeyNameMap.h + +Checkpoint of configurable hotkey support. Authored by Lorenz +Schori, modified and merged by me. Hotkeys are only implemented +on OS X so far; this change breaks the build on linux and win32. + +---------- +2005/11/29 21:22:02 crs +lib/platform/CXWindowsKeyState.cpp + +Fixed bug in X11 keyboard code. Wasn't checking for a NULL pointer. + +---------- +2005/11/28 22:12:49 crs +doc/running.html + +Added a little more detail to the documentation for for OS X users +not familiar with the shell. + +---------- +2005/11/28 20:59:39 crs +doc/autostart.html + +Added X11 autostart info for kdm. + +---------- +2005/11/27 16:41:06 crs +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CSynergyHook.cpp +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyState.cpp + +Fixed several win32 keyboard bugs. + +---------- +2005/11/27 16:30:50 crs +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h + +Fixed a couple of problems with key event synthesis. + +---------- +2005/11/25 18:19:28 crs +lib/platform/CXWindowsScreenSaver.cpp +lib/platform/CXWindowsScreenSaver.h + +Now sending a more significant fake mouse motion to xscreensaver +to keep it from activating. xscreensaver since 2.21 requires a +motion of at least 10 pixels to be considered significant. + +---------- +2005/11/25 14:42:30 crs +lib/platform/COSXClipboard.cpp +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Added periodic check for clipboard change on OS X. We're polling +because there doesn't seem to be any event to notify of a clipboard +change. + +---------- +2005/11/24 11:00:39 crs +cmd/launcher/launcher.cpp + +Fixed bug in win32 GUI. If synergy wasn't previously installed for +autostart then the GUI would incorrectly install it for autostart. +It was incorrectly installed in such a way that synergy would think +it wasn't installed and it would not function when the service +manager tried to start it. + +---------- +2005/11/20 22:34:06 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/CXWindowsScreen.cpp +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.cpp +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.h + +Converted OS X to new keyboard handling. Two known problems +with OS X: the thorn character is mapped incorrectly (because +OS X lies) and we're using every keyboard layout, not just the +enabled one, because we have no idea how to detect if a layout +is enabled or not. The latter problem only causes some startup +slowness. + +---------- +2005/11/20 22:29:01 crs +configure.in +lib/arch/CMultibyte.cpp + +Added support for converting clipboard data to Latin-1 encoding +if the default encoding is ASCII by switching to the en_US locale +at startup. + +---------- +2005/11/14 18:35:07 crs +lib/synergy/CKeyState.h + +Removed unnecessary headers. + +---------- +2005/11/13 17:08:45 crs +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h + +Added improved support for key combinations intended to perform a +keyboard shortcut rather than synthesize a particular key. + +---------- +2005/11/13 12:52:16 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h + +Finished X11 non-XKB keyboard mapping. Also added a convenience +function to CKeyMap used by both the X11 and win32 implemenetations. + +---------- +2005/11/13 10:31:32 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h +lib/synergy/CKeyMap.cpp +lib/synergy/CKeyMap.h +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.cpp +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.h +lib/synergy/KeyTypes.h +lib/synergy/libsynergy.dsp +lib/synergy/Makefile.am + +Checkpointing keyboard handling refactoring. This is a major change +to the client side handling of key events. This change converts the +X11 and win32 handling but not the OS X handling. + +The new class CKeyMap contains the information necessary to convert +a synergy key event to the platform events necessary to synthesize +the key. Platforms fill in a CKeyMap whenever their keyboard layouts +change using the addKeyEntry(), calling it once for each unique way +to synthesize a particular KeyID on the keyboard. The CKeyMap takes +it from there, first adding dead-key combinations and some other keys, +then doing the translation from KeyID to platform keystroke sequences. +Platforms no longer need to implement the KeyID to keystroke sequence +conversion, simplifying the platform implementations. + +This change also supports multiple keyboard layouts, typically used +to support various languages. If a key is not available on the active +layout but is on a different installed layout then synergy will switch +layouts automatically. + +The X11 version now fully supports the XKB extension. This should fix +problems with Mode_switch vs ISO_Level3_Shift. Non-XKB support is +incomplete in this checkpoint. + +---------- +2005/11/12 11:43:54 crs +lib/platform/CMSWindowsScreen.cpp + +Disabled code to periodically reinstall synergy in the clipboard +chain. It was causing an infinite recursion in the WM_DRAWCLIPBOARD +message handler. + +---------- +2005/11/12 11:28:25 crs +lib/platform/CMSWindowsScreenSaver.cpp + +Changed a couple of logging messages to DEBUG2 from DEBUG. + +---------- +2005/11/12 11:27:55 crs +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/launcher.cpp + +Fixed saving of autostart configuration in win32 launcher. It +was erroring out when the user did not have permission to write +to that portion of the registry. Now it will only write the +configuration if synergy is installed for autostart. This is +still not quite right since it'll still error out if some other +user with sufficient permission has installed the autostart and +this user doesn't have enough permission. + +---------- +2005/11/12 11:22:34 crs +lib/common/Version.h + +Changed version to 1.2.6. + +---------- +2005/11/01 19:58:21 crs +acinclude.m4 + +Restored pthread signal configuration checks accidentally removed +in previous version. + +---------- +2005/10/12 21:37:58 crs +doc/running.html + +Updated setup documentation. + +---------- +2005/10/11 21:12:18 crs +lib/arch/CArchMultithreadPosix.cpp + +Fixed warning in posix multithread code. + +---------- +2005/10/11 20:59:29 crs +acinclude.m4 + +Updated autoconf script for detecting pthreads. + +---------- +2005/10/06 21:45:01 crs +configure.in +lib/platform/CXWindowsScreen.cpp +lib/platform/CXWindowsScreen.h + +Added initial XKB code. This only checks for the extension in +the configuration, queries the extension at runtime and handles +mapping notify events. Still need to use the extension to get +the keyboard map. + +---------- +2005/10/02 17:40:56 crs +lib/platform/CXWindowsClipboard.cpp +lib/platform/CXWindowsClipboard.h +lib/platform/CXWindowsUtil.cpp +lib/platform/CXWindowsUtil.h + +Fixed X11 clipboard handling on 64-bit systems. Also fixed a +problem with detecting when a clipboard owner says a format is +unavailable. + +---------- +2005/10/01 19:26:57 crs +lib/server/CServer.cpp + +Fixed unnecessary comparison to NULL that chokes some compilers. + +---------- +2005/10/01 19:23:38 crs +cmd/launcher/CAdvancedOptions.cpp +cmd/launcher/CAutoStart.cpp +cmd/launcher/CAutoStart.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +dist/nullsoft/synergy.nsi +lib/arch/CArchDaemonWindows.cpp +lib/arch/CArchMiscWindows.cpp +lib/arch/CArchMiscWindows.h +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/CMSWindowsScreenSaver.cpp +lib/platform/CSynergyHook.cpp +lib/synergy/CKeyState.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.cpp +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.h + +Fixed several win32 bugs. Removed the 'Save' button from the +launcher; configuration is now saved transparently. Autostart +configuration is now saved transparently whenever it changes +and autostart is enabled. Configuration is now saved when the +debug logging level changes. + +Now releasing synthetically pressed keys when leaving the client. +The existing code was checking for key down using a local key +button id then trying to release it using the same id as a server +key button id. + +Now looking up a local button id for key events that have a scan +code of 0. These are sent by some programs that synthesize events. + +Now periodically reinstalling the clipboard chain viewer. It appears +that the chain is breaking sometimes (probably due a badly behaved +or killed app) but there's no way to detect that so this is a +workaround. + +Now stopping and deleting the autostart services during uninstall. + +---------- +2005/10/01 18:55:59 crs +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h + +Added support for apple's mighty mouse. We now correctly handle +button 4 (and 5 if it exists). We now detect the mighty mouse's +scroll ball and convert vertical motion into scroll wheel events. +Also changed handling of scroll wheel scaling; the mac works +differently from other platforms so it's not perfect. + +---------- +2005/10/01 15:01:15 crs +cmd/launcher/Makefile.am + +Added new files to makefiles. + +---------- +2005/09/26 20:58:53 crs +lib/server/CConfig.cpp + +Fixed bug in output of configuration files. Was writing bottom-right +corner as "bottom-left" instead of "bottom-right". + +---------- +2005/09/26 20:55:24 crs +cmd/launcher/CAddScreen.cpp +cmd/launcher/CAddScreen.h +cmd/launcher/CScreensLinks.cpp +cmd/launcher/CScreensLinks.h +cmd/launcher/launcher.cpp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/resource.h + +Refactored code in win32 launcher GUI. + +---------- +2005/08/22 19:40:34 crs +lib/common/Version.h + +Changed version to 1.2.5. + +---------- +2005/08/07 10:32:54 crs +doc/autostart.html + +Updated OS X autostart documentation. + +---------- +2005/08/07 10:32:19 crs +lib/arch/CArchMultithreadPosix.cpp +lib/arch/IArchMultithread.h + +Added support for SIGUSR2. Not using it yet, though. + +---------- +2005/08/03 21:03:49 crs +cmd/synergyc/synergyc.cpp + +Client now quits after failing to connect if not restartable. +Was failing to quit. + +---------- +2005/08/03 20:14:42 crs +cmd/synergyc/Makefile.am +cmd/synergys/Makefile.am +configure.in +lib/arch/Makefile.am +lib/platform/Makefile.am + +Changed how we export MACOSX_DEPLOYMENT_TARGET to a hopefully more +portable solution. + +---------- +2005/08/02 21:42:12 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Fixed --daemon fix when compiled on unix. + +---------- +2005/08/02 20:57:52 crs +cmd/launcher/CGlobalOptions.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h +lib/platform/CMSWindowsScreen.cpp +lib/server/CConfig.cpp +lib/synergy/OptionTypes.h + +Added option to suppress grabbing of the foreground window on win32 +synergy servers. Grabbing the foreground window prevents certain +apps from messing up low level keyboard events but doing that can +break certain workflow, particularly with games. Since the option +is only for win32 and it's in the launcher GUI and I hope to get rid +of it someday, there is no configuration documentation for it. + +---------- +2005/08/02 20:55:35 crs +cmd/synergyc/synergyc.cpp +cmd/synergys/synergys.cpp + +Fixed '--daemon' command line option on Windows NT family client and +server. Those platforms now ignore --daemon and --no-daemon like +they should. + +---------- +2005/08/01 22:34:05 crs +lib/platform/CMSWindowsKeyState.cpp +lib/platform/CMSWindowsKeyState.h +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h +lib/platform/CXWindowsKeyState.cpp +lib/platform/CXWindowsKeyState.h +lib/server/CPrimaryClient.cpp +lib/synergy/CKeyState.h +lib/synergy/CPlatformScreen.cpp +lib/synergy/CPlatformScreen.h +lib/synergy/CScreen.cpp +lib/synergy/CScreen.h +lib/synergy/IKeyState.h +lib/synergy/IPlatformScreen.h + +Fixed scroll-lock screen locking on linux servers. An earlier +change made the function that returns modifier state return the +shadowed state not the current state. On X windows we don't +shadow the state when on the primary screen so the synergy server +would never see the scroll-lock state turned on until it left the +primary screen. As a result scroll-lock would not lock to the +primary screen but would lock you to whatever secondary screen +you ended up on. + +Changed win32 and OSX versions to work like the linux version. +Win32 used to work anyway because we constantly shadow keyboard +state on that. OSX did not query current keyboard state at all +before and it still doesn't; this needs to be fixed. + +---------- +2005/08/01 22:29:48 crs +lib/synergy/libsynergy.dsp + +Added ProtocolTypes.cpp to VC++ project. + +---------- +2005/08/01 21:02:07 crs +lib/common/common.h +synergy.xcode/project.pbxproj + +Changes to get gcc3.3, 10.2.8 SDK builds working on OS X. + +---------- +2005/07/31 22:50:10 crs +configure.in +lib/platform/COSXScreenSaver.cpp +lib/platform/COSXScreenSaver.mm +lib/platform/COSXScreenSaverUtil.h +lib/platform/COSXScreenSaverUtil.m +lib/platform/Makefile.am + +Removed Objective-C++ file. Now using an Objective-C file instead +which works better with autoconf/automake. + +---------- +2005/07/31 22:49:03 crs +lib/synergy/Makefile.am +lib/synergy/ProtocolTypes.cpp +lib/synergy/ProtocolTypes.h + +Fixed warnings on gcc 4.0. + +---------- +2005/07/31 22:48:30 crs +lib/common/Version.h + +Changed version to 1.2.4. + +---------- +2005/07/26 21:37:41 crs +lib/common/Version.h + +Changed version to 1.2.3. + +---------- +2005/07/26 21:37:27 crs +lib/platform/CMSWindowsDesks.cpp +lib/platform/CMSWindowsDesks.h + +Now disabling the win32 foreground window on the server when +leaving the server screen. This prevents the system from +messing up keyboard events. The console (command prompt) and +remote desktop are known to interfere with the shift key when +they have the focus. This introduces a bug where the window +that was the foreground is raised to the top of the stacking +order when the cursor enters the server screen. This should +only be a problem for Mouse-X (activation follows mouse) users. +I don't know how Mouse-X changes the foreground/active window +without raising it; if I did I'd fix this bug. + +---------- +2005/07/26 21:24:13 crs +doc/configuration.html + +Fixed typo in documentation. + +---------- +2005/07/26 19:58:37 crs +cmd/launcher/launcher.cpp + +Now defaulting to WARNING as the default debug level on win32. +This is a workaround for NOTE and higher always popping up the +command window, which can't be hidden or closed. When the +user clicks 'Test', the debug level is forced to be at least +INFO to workaround this workaround. Future versions will do +away with the command window altogether so this weirdness isn't +too much of a problem. + +---------- +2005/07/26 19:50:44 crs +doc/faq.html +doc/tips.html + +Improved documentation of how to get ctrl+alt+pause working on +Windows NT,2k,XP. + +---------- +2005/07/21 21:25:51 crs +lib/common/common.h + +Forcing use of select() rather than poll() on OS X. Wasn't +getting a read event on a socket when the remote side closed +down when using poll(). Probably a bug in synergy code but +I couldn't find it. + +---------- +2005/07/21 21:24:28 crs +lib/arch/CArchNetworkBSD.cpp + +Fixed "resource temporarily unavailable" exception on OS X when +using the unblock pipe. + +---------- +2005/07/20 22:09:05 crs +cmd/launcher/launcher.cpp +cmd/launcher/launcher.rc +cmd/launcher/resource.h +cmd/synergys/synergys.cpp +doc/configuration.html +lib/client/CClient.cpp +lib/server/CConfig.cpp +lib/server/CConfig.h +lib/server/CServer.cpp +lib/server/CServer.h +lib/synergy/OptionTypes.h + +Added "dead" corners support. The cursor can't switch screens +from a dead corner. + +---------- +2005/07/19 20:43:51 crs +lib/platform/COSXKeyState.cpp +lib/platform/COSXKeyState.h + +Applied patch (from Lorenz Schori) to fix getting the keyboard +layout. It previously was failing for non-Apple keyboards. + +---------- +2005/07/19 20:23:29 crs +lib/platform/COSXKeyState.cpp + +Replaced NULL with 0 in arithmetic expression. + +---------- +2005/05/08 11:40:26 crs +lib/platform/CMSWindowsKeyState.cpp + +Applied patch 1113363 to support the kanji key on win32. + +---------- +2005/05/08 11:08:12 crs +lib/arch/CArch.cpp +lib/arch/CArch.h +lib/arch/CArchNetworkBSD.cpp +lib/arch/CArchNetworkBSD.h +lib/arch/CArchNetworkWinsock.cpp +lib/arch/CArchNetworkWinsock.h +lib/arch/IArchNetwork.h +lib/net/CTCPListenSocket.cpp + +Added support for SO_REUSEADDR. It is always enabled. + +---------- +2005/05/08 11:00:42 crs +cmd/synergys/synergys.cpp + +Fixed bug in retrying to initialize or start the server. Was +waiting the retry interval then doing nothing. + +---------- +2005/04/29 21:27:10 crs +lib/common/common.h +lib/platform/COSXScreen.cpp +synergy.xcode/project.pbxproj + +A few OSX build fixes. + +---------- +2005/04/28 22:05:51 crs +lib/common/common.h + +Now setting HAVE_SOCKLEN_T to 1 when on OSX and _SOCKLEN_T is +defined and HAVE_CONFIG_H isn't is set. + +---------- +2005/04/28 22:04:23 crs +cmd/synergyc/Makefile.am +cmd/synergys/Makefile.am +configure.in +lib/arch/Makefile.am +lib/platform/Makefile.am + +Added support for MACOSX_DEPLOYMENT_TARGET. It's set to 10.2. + +---------- +2005/04/28 22:02:47 crs +cmd/synergys/synergys.cpp +lib/client/CClient.cpp + +Now reporting sleep suspend/resume at INFO level. + +---------- +2005/04/25 22:12:04 crs +cmd/synergyc/Makefile.am +cmd/synergyc/synergyc.cpp +cmd/synergys/Makefile.am +cmd/synergys/synergys.cpp +configure.in +lib/base/CEvent.cpp +lib/base/CEvent.h +lib/base/CEventQueue.cpp +lib/client/CClient.cpp +lib/client/CClient.h +lib/platform/CMSWindowsScreen.cpp +lib/platform/CMSWindowsScreen.h +lib/platform/COSXScreen.cpp +lib/platform/COSXScreen.h +lib/platform/COSXScreenSaver.cpp +lib/platform/COSXScreenSaver.h +lib/platform/COSXScreenSaver.mm +lib/platform/Makefile.am +lib/platform/OSXScreenSaverControl.h +lib/server/CServer.cpp +lib/synergy/IScreen.cpp +lib/synergy/IScreen.h +synergy.xcode/project.pbxproj + +Added support on OS X for screensaver synchronization, +sleeping and fast user switching. OS X Server also now +captures global hot keys (cmd+tab, F9, etc.) and sends them +to the client. Changes mostly due to lorenz schori. Some +bug fixes (in lorenz's code and in synergy) and integration +into automake by crs (had to add support for Objective C++ +files) and the XCode project. + +This change also adds flags to events. Flags can cause events +to be dispatched immediately and to not free event data. Both +features are used in the new code. + +This change adds events on IScreen for notification of the +system going to sleep or waking up (or the user's session +being somehow suspended and restored). CClient and synergys +now listen for and respond to these events. CMSWindowsScreen +used to use IJobs for handling these events synchronously. It +now uses the new IScreen events and delivers them immediately. + +---------- 2005/01/26 18:45:45 crs lib/common/Version.h diff --git a/acinclude.m4 b/acinclude.m4 index 12d80bda..e7666464 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -300,6 +300,8 @@ dnl version AC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h @@ -310,11 +312,11 @@ acx_pthread_ok=no # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then - save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS]) + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then @@ -322,7 +324,7 @@ if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" - CXXFLAGS="$save_CXXFLAGS" + CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different @@ -332,9 +334,10 @@ fi # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" -# which indicates that we try without any flags at all. +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. -acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt" +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: @@ -353,6 +356,7 @@ acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -m # also defines -D_REENTRANT) # pthread: Linux, etcetera # --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) @@ -382,6 +386,13 @@ for flag in $acx_pthread_flags; do PTHREAD_CFLAGS="$flag" ;; + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" @@ -389,9 +400,9 @@ for flag in $acx_pthread_flags; do esac save_LIBS="$LIBS" - save_CXXFLAGS="$CXXFLAGS" + save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" - CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we @@ -409,7 +420,7 @@ for flag in $acx_pthread_flags; do [acx_pthread_ok=yes]) LIBS="$save_LIBS" - CXXFLAGS="$save_CXXFLAGS" + CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then @@ -425,69 +436,61 @@ fi if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" - save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - # Detect AIX lossage: threads are created detached by default - # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) - AC_TRY_LINK([#include ], - [int attr=PTHREAD_CREATE_JOINABLE;], - ok=PTHREAD_CREATE_JOINABLE, ok=unknown) - if test x"$ok" = xunknown; then - AC_TRY_LINK([#include ], - [int attr=PTHREAD_CREATE_UNDETACHED;], - ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) - fi - if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then - AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, - [Define to the necessary symbol if this constant - uses a non-standard name on your system.]) - fi - AC_MSG_RESULT(${ok}) - if test x"$ok" = xunknown; then - AC_MSG_WARN([we do not know how to create joinable pthreads]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_TRY_LINK([#include ], [int attr=$attr;], + [attr_name=$attr; break]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in - *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; - alpha*-osf*) flag="-D_REENTRANT";; - *solaris*) flag="-D_REENTRANT";; + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi - # Detect POSIX sigwait() + # Detect POSIX sigwait() AC_MSG_CHECKING([for POSIX sigwait]) AC_TRY_LINK([#include - #include ], - [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], ok=yes, ok=unknown) if test x"$ok" = xunknown; then - save_CXXFLAGS2="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS -D_POSIX_PTHREAD_SEMANTICS" + save_CFLAGS2="$CFLAGS" + CFLAGS="$CFLAGS -D_POSIX_PTHREAD_SEMANTICS" AC_TRY_LINK([#include - #include ], - [sigset_t sigset; int signal; sigwait(&sigset, &signal);], - ok=-D_POSIX_PTHREAD_SEMANTICS, ok=no) - CXXFLAGS="$save_CXXFLAGS2" + #include ], + [sigset_t sigset; int signal; sigwait(&sigset, &signal);], + ok=-D_POSIX_PTHREAD_SEMANTICS, ok=no) + CFLAGS="$save_CFLAGS2" fi AC_MSG_RESULT(${ok}) if test x"$ok" != xno; then AC_DEFINE(HAVE_POSIX_SIGWAIT,1,[Define if you have a POSIX \`sigwait\' function.]) if test x"$ok" != xyes; then - PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS" - fi + PTHREAD_CFLAGS="$ok $PTHREAD_CFLAGS" + fi fi - # Detect pthread signal functions + # Detect pthread signal functions AC_MSG_CHECKING([for pthread signal functions]) AC_TRY_LINK([#include - #include ], + #include ], [pthread_kill(pthread_self(), SIGTERM);], ok=yes, ok=no) AC_MSG_RESULT(${ok}) @@ -496,7 +499,7 @@ if test "x$acx_pthread_ok" = xyes; then fi LIBS="$save_LIBS" - CXXFLAGS="$save_CXXFLAGS" + CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with cc_r AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) @@ -516,6 +519,7 @@ else acx_pthread_ok=no $2 fi +AC_LANG_RESTORE ])dnl ACX_PTHREAD dnl enable maximum compiler warnings. must ignore unknown pragmas to diff --git a/cmd/launcher/CAddScreen.cpp b/cmd/launcher/CAddScreen.cpp new file mode 100644 index 00000000..bc65f283 --- /dev/null +++ b/cmd/launcher/CAddScreen.cpp @@ -0,0 +1,427 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "KeyTypes.h" +#include "OptionTypes.h" +#include "ProtocolTypes.h" +#include "CStringUtil.h" +#include "CArch.h" +#include "CAddScreen.h" +#include "LaunchUtil.h" +#include "resource.h" + +struct CModifierInfo { +public: + int m_ctrlID; + const char* m_name; + KeyModifierID m_modifierID; + OptionID m_optionID; +}; + +static const CModifierInfo s_modifiers[] = { + { IDC_ADD_MOD_SHIFT, "Shift", + kKeyModifierIDShift, kOptionModifierMapForShift }, + { IDC_ADD_MOD_CTRL, "Ctrl", + kKeyModifierIDControl, kOptionModifierMapForControl }, + { IDC_ADD_MOD_ALT, "Alt", + kKeyModifierIDAlt, kOptionModifierMapForAlt }, + { IDC_ADD_MOD_META, "Meta", + kKeyModifierIDMeta, kOptionModifierMapForMeta }, + { IDC_ADD_MOD_SUPER, "Super", + kKeyModifierIDSuper, kOptionModifierMapForSuper } +}; + +static const KeyModifierID baseModifier = kKeyModifierIDShift; + +// +// CAddScreen +// + +CAddScreen* CAddScreen::s_singleton = NULL; + +CAddScreen::CAddScreen(HWND parent, CConfig* config, const CString& name) : + m_parent(parent), + m_config(config), + m_name(name) +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CAddScreen::~CAddScreen() +{ + s_singleton = NULL; +} + +bool +CAddScreen::doModal() +{ + // do dialog + return (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), + m_parent, dlgProc, (LPARAM)this) != 0); +} + +CString +CAddScreen::getName() const +{ + return m_name; +} + +void +CAddScreen::init(HWND hwnd) +{ + // set title + CString title; + if (m_name.empty()) { + title = getString(IDS_ADD_SCREEN); + } + else { + title = CStringUtil::format( + getString(IDS_EDIT_SCREEN).c_str(), + m_name.c_str()); + } + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)title.c_str()); + + // fill in screen name + HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)m_name.c_str()); + + // fill in aliases + CString aliases; + for (CConfig::all_const_iterator index = m_config->beginAll(); + index != m_config->endAll(); ++index) { + if (CStringUtil::CaselessCmp::equal(index->second, m_name) && + !CStringUtil::CaselessCmp::equal(index->second, index->first)) { + if (!aliases.empty()) { + aliases += "\r\n"; + } + aliases += index->first; + } + } + child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)aliases.c_str()); + + // set options + CConfig::CScreenOptions options; + getOptions(options); + CConfig::CScreenOptions::const_iterator index; + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + index = options.find(kOptionHalfDuplexCapsLock); + setItemChecked(child, (index != options.end() && index->second != 0)); + child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); + index = options.find(kOptionHalfDuplexNumLock); + setItemChecked(child, (index != options.end() && index->second != 0)); + child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK); + index = options.find(kOptionHalfDuplexScrollLock); + setItemChecked(child, (index != options.end() && index->second != 0)); + + // modifier options + for (UInt32 i = 0; i < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++i) { + child = getItem(hwnd, s_modifiers[i].m_ctrlID); + + // fill in options + for (UInt32 j = 0; j < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++j) { + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)s_modifiers[j].m_name); + } + + // choose current value + index = options.find(s_modifiers[i].m_optionID); + KeyModifierID id = s_modifiers[i].m_modifierID; + if (index != options.end()) { + id = index->second; + } + SendMessage(child, CB_SETCURSEL, id - baseModifier, 0); + } + + // dead corners + UInt32 corners = 0; + index = options.find(kOptionScreenSwitchCorners); + if (index != options.end()) { + corners = index->second; + } + child = getItem(hwnd, IDC_ADD_DC_TOP_LEFT); + setItemChecked(child, (corners & kTopLeftMask) != 0); + child = getItem(hwnd, IDC_ADD_DC_TOP_RIGHT); + setItemChecked(child, (corners & kTopRightMask) != 0); + child = getItem(hwnd, IDC_ADD_DC_BOTTOM_LEFT); + setItemChecked(child, (corners & kBottomLeftMask) != 0); + child = getItem(hwnd, IDC_ADD_DC_BOTTOM_RIGHT); + setItemChecked(child, (corners & kBottomRightMask) != 0); + index = options.find(kOptionScreenSwitchCornerSize); + SInt32 size = 0; + if (index != options.end()) { + size = index->second; + } + char buffer[20]; + sprintf(buffer, "%d", size); + child = getItem(hwnd, IDC_ADD_DC_SIZE); + SendMessage(child, WM_SETTEXT, 0, (LPARAM)buffer); +} + +bool +CAddScreen::save(HWND hwnd) +{ + // get the old aliases and options + CStringList oldAliases; + getAliases(oldAliases); + CConfig::CScreenOptions options; + getOptions(options); + + // extract name and aliases + CString newName; + HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); + newName = getWindowText(child); + CStringList newAliases; + child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); + tokenize(newAliases, getWindowText(child)); + + // name must be valid + if (!m_config->isValidScreenName(newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + newName.c_str())); + return false; + } + + // aliases must be valid + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + if (!m_config->isValidScreenName(*index)) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_SCREEN_NAME).c_str(), + index->c_str())); + return false; + } + } + + // new name may not be in the new alias list + if (isNameInList(newAliases, newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_SCREEN_NAME_IS_ALIAS).c_str(), + newName.c_str())); + return false; + } + + // name must not exist in config but allow same name. also + // allow name if it exists in the old alias list but not the + // new one. + if (m_config->isScreen(newName) && + !CStringUtil::CaselessCmp::equal(newName, m_name) && + !isNameInList(oldAliases, newName)) { + showError(hwnd, CStringUtil::format( + getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), + newName.c_str())); + return false; + } + + // aliases must not exist in config but allow same aliases and + // allow an alias to be the old name. + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + if (m_config->isScreen(*index) && + !CStringUtil::CaselessCmp::equal(*index, m_name) && + !isNameInList(oldAliases, *index)) { + showError(hwnd, CStringUtil::format( + getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), + index->c_str())); + return false; + } + } + + // dead corner size must be non-negative + child = getItem(hwnd, IDC_ADD_DC_SIZE); + CString valueString = getWindowText(child); + int cornerSize = atoi(valueString.c_str()); + if (cornerSize < 0) { + showError(hwnd, CStringUtil::format( + getString(IDS_INVALID_CORNER_SIZE).c_str(), + valueString.c_str())); + SetFocus(child); + return false; + } + + // collect options + child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); + if (isItemChecked(child)) { + options[kOptionHalfDuplexCapsLock] = 1; + } + else { + options.erase(kOptionHalfDuplexCapsLock); + } + child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); + if (isItemChecked(child)) { + options[kOptionHalfDuplexNumLock] = 1; + } + else { + options.erase(kOptionHalfDuplexNumLock); + } + child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK); + if (isItemChecked(child)) { + options[kOptionHalfDuplexScrollLock] = 1; + } + else { + options.erase(kOptionHalfDuplexScrollLock); + } + + // save modifier options + for (UInt32 i = 0; i < sizeof(s_modifiers) / + sizeof(s_modifiers[0]); ++i) { + child = getItem(hwnd, s_modifiers[i].m_ctrlID); + KeyModifierID id = static_cast( + SendMessage(child, CB_GETCURSEL, 0, 0) + + baseModifier); + if (id != s_modifiers[i].m_modifierID) { + options[s_modifiers[i].m_optionID] = id; + } + else { + options.erase(s_modifiers[i].m_optionID); + } + } + + // save dead corner options + UInt32 corners = 0; + if (isItemChecked(getItem(hwnd, IDC_ADD_DC_TOP_LEFT))) { + corners |= kTopLeftMask; + } + if (isItemChecked(getItem(hwnd, IDC_ADD_DC_TOP_RIGHT))) { + corners |= kTopRightMask; + } + if (isItemChecked(getItem(hwnd, IDC_ADD_DC_BOTTOM_LEFT))) { + corners |= kBottomLeftMask; + } + if (isItemChecked(getItem(hwnd, IDC_ADD_DC_BOTTOM_RIGHT))) { + corners |= kBottomRightMask; + } + options[kOptionScreenSwitchCorners] = corners; + options[kOptionScreenSwitchCornerSize] = cornerSize; + + // save new data to config + if (m_name.empty()) { + // added screen + m_config->addScreen(newName); + } + else { + // edited screen + m_config->removeAliases(m_name); + m_config->removeOptions(m_name); + m_config->renameScreen(m_name, newName); + } + m_name = newName; + for (CStringList::const_iterator index = newAliases.begin(); + index != newAliases.end(); ++index) { + m_config->addAlias(m_name, *index); + } + for (CConfig::CScreenOptions::const_iterator + index = options.begin(); + index != options.end(); ++index) { + m_config->addOption(m_name, index->first, index->second); + } + + return true; +} + +BOOL +CAddScreen::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + init(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (save(hwnd)) { + EndDialog(hwnd, 1); + } + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CAddScreen::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} + +void +CAddScreen::getAliases(CStringList& aliases) const +{ + for (CConfig::all_const_iterator index = m_config->beginAll(); + index != m_config->endAll(); ++index) { + if (CStringUtil::CaselessCmp::equal(index->second, m_name) && + !CStringUtil::CaselessCmp::equal(index->second, index->first)) { + aliases.push_back(index->first); + } + } +} + +void +CAddScreen::getOptions(CConfig::CScreenOptions& optionsOut) const +{ + const CConfig::CScreenOptions* options = m_config->getOptions(m_name); + if (options == NULL) { + optionsOut = CConfig::CScreenOptions(); + } + else { + optionsOut = *options; + } +} + +void +CAddScreen::tokenize(CStringList& tokens, const CString& src) +{ + // find first non-whitespace + CString::size_type x = src.find_first_not_of(" \t\r\n"); + if (x == CString::npos) { + return; + } + + // find next whitespace + do { + CString::size_type y = src.find_first_of(" \t\r\n", x); + if (y == CString::npos) { + y = src.size(); + } + tokens.push_back(src.substr(x, y - x)); + x = src.find_first_not_of(" \t\r\n", y); + } while (x != CString::npos); +} + +bool +CAddScreen::isNameInList(const CStringList& names, const CString& name) +{ + for (CStringList::const_iterator index = names.begin(); + index != names.end(); ++index) { + if (CStringUtil::CaselessCmp::equal(name, *index)) { + return true; + } + } + return false; +} diff --git a/cmd/launcher/CAddScreen.h b/cmd/launcher/CAddScreen.h new file mode 100644 index 00000000..e926c498 --- /dev/null +++ b/cmd/launcher/CAddScreen.h @@ -0,0 +1,74 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CADDSCREEN_H +#define CADDSCREEN_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Add screen dialog for Microsoft Windows launcher +class CAddScreen { +public: + CAddScreen(HWND parent, CConfig*, const CString& name); + ~CAddScreen(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. Return + \c true if the user accepted the changes, false otherwise. + */ + bool doModal(); + + //@} + //! @name accessors + //@{ + + CString getName() const; + + //@} + +private: + typedef std::vector CStringList; + + void getAliases(CStringList&) const; + void getOptions(CConfig::CScreenOptions&) const; + + static void tokenize(CStringList& tokens, const CString& src); + static bool isNameInList(const CStringList& tokens, + const CString& src); + + void init(HWND hwnd); + bool save(HWND hwnd); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CAddScreen* s_singleton; + + HWND m_parent; + CConfig* m_config; + CString m_name; +}; + +#endif diff --git a/cmd/launcher/CAdvancedOptions.cpp b/cmd/launcher/CAdvancedOptions.cpp index 4c72e8d4..ee5b45ea 100644 --- a/cmd/launcher/CAdvancedOptions.cpp +++ b/cmd/launcher/CAdvancedOptions.cpp @@ -206,7 +206,7 @@ CAdvancedOptions::save(HWND hwnd) m_interface = iface; // save values to registry - HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + HKEY key = CArchMiscWindows::addKey(HKEY_CURRENT_USER, getSettingsPath()); if (key != NULL) { CArchMiscWindows::setValue(key, "port", m_port); CArchMiscWindows::setValue(key, "name", m_screenName); diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp index d5eee0ce..7b390a58 100644 --- a/cmd/launcher/CAutoStart.cpp +++ b/cmd/launcher/CAutoStart.cpp @@ -21,10 +21,10 @@ #include "LaunchUtil.h" #include "resource.h" -#define CLIENT_DAEMON_NAME "Synergy Client" -#define SERVER_DAEMON_NAME "Synergy Server" -#define CLIENT_DAEMON_INFO "Shares this system's mouse and keyboard with others." -#define SERVER_DAEMON_INFO "Shares this system's mouse and keyboard with others." +static const char* CLIENT_DAEMON_NAME = "Synergy Client"; +static const char* SERVER_DAEMON_NAME = "Synergy Server"; +static const char* CLIENT_DAEMON_INFO = "Uses a shared mouse and keyboard."; +static const char* SERVER_DAEMON_INFO = "Shares this system's mouse and keyboard with others."; // // CAutoStartOutputter @@ -40,6 +40,7 @@ public: // ILogOutputter overrides virtual void open(const char*) { } virtual void close() { } + virtual void show(bool) { } virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const { return ""; } @@ -63,14 +64,11 @@ CAutoStartOutputter::write(ELevel level, const char* message) CAutoStart* CAutoStart::s_singleton = NULL; -CAutoStart::CAutoStart(HWND parent, CConfig* config, const CString& cmdLine) : +CAutoStart::CAutoStart(HWND parent, bool isServer, const CString& cmdLine) : m_parent(parent), - m_config(config), - m_isServer(config != NULL), + m_isServer(isServer), m_cmdLine(cmdLine), - m_name((config != NULL) ? SERVER_DAEMON_NAME : CLIENT_DAEMON_NAME), - m_userConfigSaved(false) - + m_name(isServer ? SERVER_DAEMON_NAME : CLIENT_DAEMON_NAME) { assert(s_singleton == NULL); s_singleton = this; @@ -87,9 +85,6 @@ CAutoStart::doModal() // install our log outputter CLOG->insert(new CAutoStartOutputter(&m_errorMessage)); - // reset saved flag - m_userConfigSaved = false; - // do dialog DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_AUTOSTART), m_parent, dlgProc, (LPARAM)this); @@ -98,10 +93,98 @@ CAutoStart::doModal() CLOG->pop_front(); } -bool -CAutoStart::wasUserConfigSaved() const +void +CAutoStart::reinstallDaemon(bool isClient, const CString& cmdLine) { - return m_userConfigSaved; + // get installation state + const char* name = (isClient ? CLIENT_DAEMON_NAME : SERVER_DAEMON_NAME); + bool installedSystem = ARCH->isDaemonInstalled(name, true); + bool installedUser = ARCH->isDaemonInstalled(name, false); + + // reinstall if anything is installed + if (installedSystem || installedUser) { + ARCH->installDaemon(name, + isClient ? CLIENT_DAEMON_INFO : SERVER_DAEMON_INFO, + getAppPath(isClient ? CLIENT_APP : SERVER_APP).c_str(), + cmdLine.c_str(), + NULL, + installedSystem); + } +} + +void +CAutoStart::uninstallDaemons(bool client) +{ + if (client) { + try { + ARCH->uninstallDaemon(CLIENT_DAEMON_NAME, true); + } + catch (...) { + } + try { + ARCH->uninstallDaemon(CLIENT_DAEMON_NAME, false); + } + catch (...) { + } + } + else { + try { + ARCH->uninstallDaemon(SERVER_DAEMON_NAME, true); + } + catch (...) { + } + try { + ARCH->uninstallDaemon(SERVER_DAEMON_NAME, false); + } + catch (...) { + } + } +} + +bool +CAutoStart::startDaemon() +{ + const char* name = NULL; + if (ARCH->isDaemonInstalled(CLIENT_DAEMON_NAME, true)) { + name = CLIENT_DAEMON_NAME; + } + else if (ARCH->isDaemonInstalled(SERVER_DAEMON_NAME, true)) { + name = SERVER_DAEMON_NAME; + } + if (name == NULL) { + return false; + } + + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ); + if (mgr == NULL) { + return false; + } + + // open the service + SC_HANDLE service = OpenService(mgr, name, SERVICE_START); + if (service == NULL) { + CloseServiceHandle(mgr); + return false; + } + + // start the service + BOOL okay = StartService(service, 0, NULL); + + // clean up + CloseServiceHandle(service); + CloseServiceHandle(mgr); + + return (okay != 0); +} + +bool +CAutoStart::isDaemonInstalled() +{ + return (ARCH->isDaemonInstalled(CLIENT_DAEMON_NAME, false) || + ARCH->isDaemonInstalled(CLIENT_DAEMON_NAME, true) || + ARCH->isDaemonInstalled(SERVER_DAEMON_NAME, false) || + ARCH->isDaemonInstalled(SERVER_DAEMON_NAME, true)); } void @@ -177,22 +260,6 @@ CAutoStart::onInstall(bool allUsers) return onUninstall(allUsers); } - // try saving configuration. if we can't then don't try - // installing the daemon. - if (m_config != NULL) { - if (!saveConfig(*m_config, allUsers)) { - showError(m_hwnd, CStringUtil::format( - getString(IDS_SAVE_FAILED).c_str(), - getErrorString(GetLastError()).c_str())); - return false; - } - - // note if we've saved the user's configuration - if (!allUsers) { - m_userConfigSaved = true; - } - } - // get the app path CString appPath = getAppPath(m_isServer ? SERVER_APP : CLIENT_APP); diff --git a/cmd/launcher/CAutoStart.h b/cmd/launcher/CAutoStart.h index e931530e..46aad100 100644 --- a/cmd/launcher/CAutoStart.h +++ b/cmd/launcher/CAutoStart.h @@ -20,14 +20,10 @@ #define WINDOWS_LEAN_AND_MEAN #include -class CConfig; - //! Auto start dialog for Microsoft Windows launcher class CAutoStart { public: - // if config == NULL then it's assumed we're installing/uninstalling - // the client, otherwise the server. - CAutoStart(HWND parent, CConfig* config, const CString& cmdLine); + CAutoStart(HWND parent, bool isServer, const CString& cmdLine); ~CAutoStart(); //! @name manipulators @@ -39,16 +35,34 @@ public: */ void doModal(); + //! Reinstall daemon + /*! + Reinstalls the currently installed daemon. + */ + static void reinstallDaemon(bool isClient, const CString& cmdLine); + + //! Uninstalls daemon + /*! + Uninstalls all installed client (\p client is \c true) or server daemons. + */ + static void uninstallDaemons(bool client); + + //! Starts an installed daemon + /*! + Returns \c true iff a daemon was started. This will only start daemons + installed for all users. + */ + static bool startDaemon(); + //@} //! @name accessors //@{ - //! Test if user configuration was saved + //! Tests if any daemons are installed /*! - Returns true if the user's configuration (as opposed to the system-wide - configuration) was saved successfully while in doModal(). + Returns \c true if any daemons are installed. */ - bool wasUserConfigSaved() const; + static bool isDaemonInstalled(); //@} @@ -65,14 +79,12 @@ private: static CAutoStart* s_singleton; HWND m_parent; - CConfig* m_config; bool m_isServer; CString m_cmdLine; CString m_name; HWND m_hwnd; bool m_install; CString m_errorMessage; - bool m_userConfigSaved; }; #endif diff --git a/cmd/launcher/CGlobalOptions.cpp b/cmd/launcher/CGlobalOptions.cpp index 1777863b..b7c15801 100644 --- a/cmd/launcher/CGlobalOptions.cpp +++ b/cmd/launcher/CGlobalOptions.cpp @@ -79,6 +79,8 @@ CGlobalOptions::init(HWND hwnd) setItemChecked(child, true); child = getItem(hwnd, IDC_GLOBAL_RELATIVE_MOVES); setItemChecked(child, false); + child = getItem(hwnd, IDC_GLOBAL_LEAVE_FOREGROUND); + setItemChecked(child, false); // get the global options const CConfig::CScreenOptions* options = m_config->getOptions(""); @@ -122,6 +124,10 @@ CGlobalOptions::init(HWND hwnd) child = getItem(hwnd, IDC_GLOBAL_RELATIVE_MOVES); setItemChecked(child, (value != 0)); } + else if (id == kOptionWin32KeepForeground) { + child = getItem(hwnd, IDC_GLOBAL_LEAVE_FOREGROUND); + setItemChecked(child, (value != 0)); + } } } } @@ -187,6 +193,7 @@ CGlobalOptions::save(HWND hwnd) m_config->removeOption("", kOptionHeartbeat); m_config->removeOption("", kOptionScreenSaverSync); m_config->removeOption("", kOptionRelativeMouseMoves); + m_config->removeOption("", kOptionWin32KeepForeground); // add requested options child = getItem(hwnd, IDC_GLOBAL_DELAY_CHECK); @@ -209,6 +216,10 @@ CGlobalOptions::save(HWND hwnd) if (isItemChecked(child)) { m_config->addOption("", kOptionRelativeMouseMoves, 1); } + child = getItem(hwnd, IDC_GLOBAL_LEAVE_FOREGROUND); + if (isItemChecked(child)) { + m_config->addOption("", kOptionWin32KeepForeground, 1); + } // save last values m_delayTime = newDelayTime; diff --git a/cmd/launcher/CHotkeyOptions.cpp b/cmd/launcher/CHotkeyOptions.cpp new file mode 100644 index 00000000..7e7a1b56 --- /dev/null +++ b/cmd/launcher/CHotkeyOptions.cpp @@ -0,0 +1,1864 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchMiscWindows.h" +#include "CMSWindowsKeyState.h" +#include "CConfig.h" +#include "CHotkeyOptions.h" +#include "CStringUtil.h" +#include "LaunchUtil.h" +#include "resource.h" + +#if !defined(WM_XBUTTONDOWN) +#define WM_XBUTTONDOWN 0x020B +#define WM_XBUTTONUP 0x020C +#define WM_XBUTTONDBLCLK 0x020D +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif + +// +// CAdvancedOptions +// + +CHotkeyOptions* CHotkeyOptions::s_singleton = NULL; + +CHotkeyOptions::CHotkeyOptions(HWND parent, CConfig* config) : + m_parent(parent), + m_config(config) +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CHotkeyOptions::~CHotkeyOptions() +{ + s_singleton = NULL; +} + +void +CHotkeyOptions::doModal() +{ + // do dialog + m_inputFilter = m_config->getInputFilter(); + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_OPTIONS), + m_parent, dlgProc, (LPARAM)this); +} + +void +CHotkeyOptions::doInit(HWND hwnd) +{ + m_activeRuleIndex = (UInt32)-1; + fillHotkeys(hwnd); + openRule(hwnd); +} + +void +CHotkeyOptions::fillHotkeys(HWND hwnd, UInt32 select) +{ + HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS); + + SendMessage(rules, LB_RESETCONTENT, 0, 0); + for (UInt32 i = 0, n = m_inputFilter->getNumRules(); i < n; ++i) { + CInputFilter::CRule& rule = m_inputFilter->getRule(i); + SendMessage(rules, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)rule.getCondition()->format().c_str()); + } + + if (select < m_inputFilter->getNumRules()) { + SendMessage(rules, LB_SETCURSEL, select, 0); + } + + updateHotkeysControls(hwnd); +} + +void +CHotkeyOptions::updateHotkeysControls(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS); + bool selected = (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR); + + enableItem(hwnd, IDC_HOTKEY_ADD_HOTKEY, TRUE); + enableItem(hwnd, IDC_HOTKEY_EDIT_HOTKEY, selected); + enableItem(hwnd, IDC_HOTKEY_REMOVE_HOTKEY, selected); +} + +void +CHotkeyOptions::addHotkey(HWND hwnd) +{ + closeRule(hwnd); + CInputFilter::CCondition* condition = NULL; + if (editCondition(hwnd, condition)) { + m_inputFilter->addFilterRule(CInputFilter::CRule(condition)); + fillHotkeys(hwnd, m_inputFilter->getNumRules() - 1); + } + else { + delete condition; + } + openRule(hwnd); +} + +void +CHotkeyOptions::removeHotkey(HWND hwnd) +{ + UInt32 ruleIndex = m_activeRuleIndex; + closeRule(hwnd); + + m_inputFilter->removeFilterRule(ruleIndex); + UInt32 n = m_inputFilter->getNumRules(); + if (n > 0 && ruleIndex >= n) { + ruleIndex = n - 1; + } + fillHotkeys(hwnd, ruleIndex); + + openRule(hwnd); +} + +void +CHotkeyOptions::editHotkey(HWND hwnd) +{ + // save selected item in action list + HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS); + LRESULT aIndex = SendMessage(actions, LB_GETCURSEL, 0, 0); + + UInt32 index = m_activeRuleIndex; + closeRule(hwnd); + + CInputFilter::CRule& rule = m_inputFilter->getRule(index); + CInputFilter::CCondition* condition = rule.getCondition()->clone(); + if (editCondition(hwnd, condition)) { + rule.setCondition(condition); + fillHotkeys(hwnd, index); + } + else { + delete condition; + } + + openRule(hwnd); + + // restore selected item in action list + if (aIndex != LB_ERR) { + SendMessage(actions, LB_SETCURSEL, aIndex, 0); + } +} + +void +CHotkeyOptions::fillActions(HWND hwnd, UInt32 select) +{ + HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS); + SendMessage(actions, LB_RESETCONTENT, 0, 0); + if (m_activeRuleIndex != (UInt32)-1) { + UInt32 n = m_activeRule.getNumActions(true); + UInt32 n2 = m_activeRule.getNumActions(false); + for (UInt32 i = 0; i < n; ++i) { + const CInputFilter::CAction& action = + m_activeRule.getAction(true, i); + CString line("A "); + line += action.format(); + SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)line.c_str()); + SendMessage(actions, LB_SETITEMDATA, (WPARAM)i, (LPARAM)i); + } + for (UInt32 i = 0; i < n2; ++i) { + const CInputFilter::CAction& action = + m_activeRule.getAction(false, i); + CString line("D "); + line += action.format(); + SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)line.c_str()); + SendMessage(actions, LB_SETITEMDATA, (WPARAM)i + n, + (LPARAM)(i | 0x80000000u)); + } + + if (select < n + n2) { + SendMessage(actions, LB_SETCURSEL, select, 0); + } + } + + updateActionsControls(hwnd); +} + +void +CHotkeyOptions::updateActionsControls(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS); + bool active = (m_activeRuleIndex != (UInt32)-1); + + child = getItem(hwnd, IDC_HOTKEY_ACTIONS); + bool selected = + (active && (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR)); + + enableItem(hwnd, IDC_HOTKEY_ADD_ACTION, active); + enableItem(hwnd, IDC_HOTKEY_EDIT_ACTION, selected); + enableItem(hwnd, IDC_HOTKEY_REMOVE_ACTION, selected); +} + +void +CHotkeyOptions::addAction(HWND hwnd) +{ + CInputFilter::CAction* action = NULL; + bool onActivate = true; + if (editAction(hwnd, action, onActivate)) { + m_activeRule.adoptAction(action, onActivate); + + UInt32 actionIndex = m_activeRule.getNumActions(true) - 1; + if (!onActivate) { + actionIndex += m_activeRule.getNumActions(false); + } + fillActions(hwnd, actionIndex); + } + else { + delete action; + } +} + +void +CHotkeyOptions::removeAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index != LB_ERR) { + UInt32 actionIndex = + (UInt32)SendMessage(child, LB_GETITEMDATA, index, 0); + bool onActivate = ((actionIndex & 0x80000000u) == 0); + actionIndex &= ~0x80000000u; + + m_activeRule.removeAction(onActivate, actionIndex); + + actionIndex = static_cast(index); + UInt32 n = m_activeRule.getNumActions(true) + + m_activeRule.getNumActions(false); + if (n > 0 && actionIndex >= n) { + actionIndex = n - 1; + } + fillActions(hwnd, actionIndex); + } +} + +void +CHotkeyOptions::editAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index != LB_ERR) { + UInt32 actionIndex = + (UInt32)SendMessage(child, LB_GETITEMDATA, index, 0); + bool onActivate = ((actionIndex & 0x80000000u) == 0); + actionIndex &= ~0x80000000u; + + CInputFilter::CAction* action = + m_activeRule.getAction(onActivate, actionIndex).clone(); + bool newOnActivate = onActivate; + if (editAction(hwnd, action, newOnActivate)) { + if (onActivate == newOnActivate) { + m_activeRule.replaceAction(action, onActivate, actionIndex); + actionIndex = static_cast(index); + } + else { + m_activeRule.removeAction(onActivate, actionIndex); + m_activeRule.adoptAction(action, newOnActivate); + actionIndex = m_activeRule.getNumActions(true) - 1; + if (!newOnActivate) { + actionIndex += m_activeRule.getNumActions(false); + } + } + fillActions(hwnd, actionIndex); + } + else { + delete action; + } + } +} + +bool +CHotkeyOptions::editCondition(HWND hwnd, CInputFilter::CCondition*& condition) +{ + return CConditionDialog::doModal(hwnd, condition); +} + +bool +CHotkeyOptions::editAction(HWND hwnd, CInputFilter::CAction*& action, + bool& onActivate) +{ + return CActionDialog::doModal(hwnd, m_config, action, onActivate); +} + +void +CHotkeyOptions::openRule(HWND hwnd) +{ + // get the active rule and copy it, merging down/up pairs of keystroke + // and mouse button actions into single actions for the convenience of + // of the user. + HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS); + LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0); + if (index != LB_ERR) { + // copy the rule as is + m_activeRuleIndex = (SInt32)index; + m_activeRule = m_inputFilter->getRule(m_activeRuleIndex); + + // look for actions to combine + for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) { + // get next activate action + const CInputFilter::CAction* action = + &m_activeRule.getAction(true, i); + + // check if it's a key or mouse action + const CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(action); + const CInputFilter::CMouseButtonAction* mouseAction = + dynamic_cast(action); + if (keyAction == NULL && mouseAction == NULL) { + continue; + } + + // check for matching deactivate action + UInt32 j = (UInt32)-1; + CInputFilter::CAction* newAction = NULL; + if (keyAction != NULL) { + j = findMatchingAction(keyAction); + if (j != (UInt32)-1) { + // found a match + const IPlatformScreen::CKeyInfo* oldInfo = + keyAction->getInfo(); + IPlatformScreen::CKeyInfo* newInfo = + IKeyState::CKeyInfo::alloc(*oldInfo); + newAction = new CKeystrokeDownUpAction(newInfo); + } + } + else if (mouseAction != NULL) { + j = findMatchingAction(mouseAction); + if (j != (UInt32)-1) { + // found a match + const IPlatformScreen::CButtonInfo* oldInfo = + mouseAction->getInfo(); + IPlatformScreen::CButtonInfo* newInfo = + IPrimaryScreen::CButtonInfo::alloc(*oldInfo); + newAction = new CMouseButtonDownUpAction(newInfo); + } + } + + // perform merge + if (newAction != NULL) { + m_activeRule.replaceAction(newAction, true, i); + m_activeRule.removeAction(false, j); + } + } + } + else { + m_activeRuleIndex = (UInt32)-1; + } + fillActions(hwnd); +} + +void +CHotkeyOptions::closeRule(HWND) +{ + // copy rule back to input filter, expanding merged actions into the + // two component actions. + if (m_activeRuleIndex != (UInt32)-1) { + // expand merged rules + for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) { + // get action + const CInputFilter::CAction* action = + &m_activeRule.getAction(true, i); + + // check if it's a merged key or mouse action + const CKeystrokeDownUpAction* keyAction = + dynamic_cast(action); + const CMouseButtonDownUpAction* mouseAction = + dynamic_cast(action); + if (keyAction == NULL && mouseAction == NULL) { + continue; + } + + // expand + if (keyAction != NULL) { + const IPlatformScreen::CKeyInfo* oldInfo = + keyAction->getInfo(); + IPlatformScreen::CKeyInfo* newInfo = + IKeyState::CKeyInfo::alloc(*oldInfo); + CInputFilter::CKeystrokeAction* downAction = + new CInputFilter::CKeystrokeAction(newInfo, true); + newInfo = IKeyState::CKeyInfo::alloc(*oldInfo); + CInputFilter::CKeystrokeAction* upAction = + new CInputFilter::CKeystrokeAction(newInfo, false); + m_activeRule.replaceAction(downAction, true, i); + m_activeRule.adoptAction(upAction, false); + } + else if (mouseAction != NULL) { + const IPlatformScreen::CButtonInfo* oldInfo = + mouseAction->getInfo(); + IPlatformScreen::CButtonInfo* newInfo = + IPrimaryScreen::CButtonInfo::alloc(*oldInfo); + CInputFilter::CMouseButtonAction* downAction = + new CInputFilter::CMouseButtonAction(newInfo, true); + newInfo = IPrimaryScreen::CButtonInfo::alloc(*oldInfo); + CInputFilter::CMouseButtonAction* upAction = + new CInputFilter::CMouseButtonAction(newInfo, false); + m_activeRule.replaceAction(downAction, true, i); + m_activeRule.adoptAction(upAction, false); + } + } + + // copy it back + m_inputFilter->getRule(m_activeRuleIndex) = m_activeRule; + } + m_activeRuleIndex = (UInt32)-1; +} + +UInt32 +CHotkeyOptions::findMatchingAction( + const CInputFilter::CKeystrokeAction* src) const +{ + for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) { + const CInputFilter::CKeystrokeAction* dst = + dynamic_cast( + &m_activeRule.getAction(false, i)); + if (dst != NULL && + IKeyState::CKeyInfo::equal(src->getInfo(), dst->getInfo())) { + return i; + } + } + return (UInt32)-1; +} + +UInt32 +CHotkeyOptions::findMatchingAction( + const CInputFilter::CMouseButtonAction* src) const +{ + for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) { + const CInputFilter::CMouseButtonAction* dst = + dynamic_cast( + &m_activeRule.getAction(false, i)); + if (dst != NULL && + IPrimaryScreen::CButtonInfo::equal( + src->getInfo(), dst->getInfo())) { + return i; + } + } + return (UInt32)-1; +} + +BOOL +CHotkeyOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + doInit(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: + closeRule(hwnd); + EndDialog(hwnd, 0); + return TRUE; + + case IDC_HOTKEY_HOTKEYS: + switch (HIWORD(wParam)) { + case LBN_DBLCLK: + editHotkey(hwnd); + return TRUE; + + case LBN_SELCHANGE: { + HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS); + LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0); + if (m_activeRuleIndex != (UInt32)index) { + closeRule(hwnd); + updateHotkeysControls(hwnd); + openRule(hwnd); + } + return TRUE; + } + } + break; + + case IDC_HOTKEY_ADD_HOTKEY: + addHotkey(hwnd); + return TRUE; + + case IDC_HOTKEY_REMOVE_HOTKEY: + removeHotkey(hwnd); + return TRUE; + + case IDC_HOTKEY_EDIT_HOTKEY: + editHotkey(hwnd); + return TRUE; + + case IDC_HOTKEY_ACTIONS: + switch (HIWORD(wParam)) { + case LBN_DBLCLK: + editAction(hwnd); + return TRUE; + + case LBN_SELCHANGE: + updateActionsControls(hwnd); + return TRUE; + } + break; + + case IDC_HOTKEY_ADD_ACTION: + addAction(hwnd); + return TRUE; + + case IDC_HOTKEY_REMOVE_ACTION: + removeAction(hwnd); + return TRUE; + + case IDC_HOTKEY_EDIT_ACTION: + editAction(hwnd); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CHotkeyOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} + + +// +// CHotkeyOptions::CConditionDialog +// + +CInputFilter::CCondition* + CHotkeyOptions::CConditionDialog::s_condition = NULL; +CInputFilter::CCondition* + CHotkeyOptions::CConditionDialog::s_lastGoodCondition = NULL; +WNDPROC CHotkeyOptions::CConditionDialog::s_editWndProc = NULL; + +bool +CHotkeyOptions::CConditionDialog::doModal(HWND parent, + CInputFilter::CCondition*& condition) +{ + s_condition = condition; + if (s_condition != NULL) { + s_lastGoodCondition = s_condition->clone(); + } + else { + s_lastGoodCondition = NULL; + } + int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_CONDITION), + parent, dlgProc); + + condition = s_condition; + delete s_lastGoodCondition; + s_condition = NULL; + s_lastGoodCondition = NULL; + + // user effectively cancelled if the condition is NULL + if (condition == NULL) { + n = 0; + } + + return (n == 1); +} + +void +CHotkeyOptions::CConditionDialog::doInit(HWND hwnd) +{ + // subclass edit control + HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY); + s_editWndProc = (WNDPROC)GetWindowLong(child, GWL_WNDPROC); + SetWindowLong(child, GWL_WNDPROC, (LONG)editProc); + + // fill control + fillHotkey(hwnd); +} + +void +CHotkeyOptions::CConditionDialog::fillHotkey(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY); + if (s_condition != NULL) { + setWindowText(child, s_condition->format().c_str()); + } + else { + setWindowText(child, ""); + } +} + +void +CHotkeyOptions::CConditionDialog::onButton(HWND hwnd, ButtonID button) +{ + delete s_condition; + s_condition = + new CInputFilter::CMouseButtonCondition(button, getModifiers()); + + fillHotkey(GetParent(hwnd)); +} + +void +CHotkeyOptions::CConditionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + // ignore key repeats + if ((lParam & 0xc0000000u) == 0x40000000u) { + return; + } + + // ignore key releases if the condition is complete and for the tab + // key (in case we were just tabbed to) + if ((lParam & 0x80000000u) != 0) { + if (isGoodCondition() || wParam == VK_TAB) { + return; + } + } + + KeyID key = kKeyNone; + KeyModifierMask mask = getModifiers(); + switch (wParam) { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + case VK_LWIN: + case VK_RWIN: + break; + + case VK_TAB: + // allow tabbing out of control + if ((mask & (KeyModifierControl | + KeyModifierAlt | KeyModifierSuper)) == 0) { + HWND next = hwnd; + if ((mask & KeyModifierShift) == 0) { + do { + next = GetWindow(next, GW_HWNDNEXT); + if (next == NULL) { + next = GetWindow(hwnd, GW_HWNDFIRST); + } + } while (next != hwnd && + (!IsWindowVisible(next) || + (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0)); + } + else { + do { + next = GetWindow(next, GW_HWNDPREV); + if (next == NULL) { + next = GetWindow(hwnd, GW_HWNDLAST); + } + } while (next != hwnd && + (!IsWindowVisible(next) || + (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0)); + } + SetFocus(next); + return; + } + // fall through + + default: + key = CMSWindowsKeyState::getKeyID(wParam, + static_cast((lParam & 0x1ff0000u) >> 16)); + switch (key) { + case kKeyNone: + // could be a character + key = getChar(wParam, lParam); + if (key == kKeyNone) { + return; + } + break; + + case kKeyShift_L: + case kKeyShift_R: + case kKeyControl_L: + case kKeyControl_R: + case kKeyAlt_L: + case kKeyAlt_R: + case kKeyMeta_L: + case kKeyMeta_R: + case kKeySuper_L: + case kKeySuper_R: + case kKeyCapsLock: + case kKeyNumLock: + case kKeyScrollLock: + // bogus + return; + } + break; + } + + delete s_condition; + s_condition = new CInputFilter::CKeystrokeCondition(key, mask); + + fillHotkey(GetParent(hwnd)); +} + +KeyID +CHotkeyOptions::CConditionDialog::getChar(WPARAM wParam, LPARAM lParam) +{ + BYTE keyState[256]; + UINT virtualKey = (UINT)wParam; + UINT scanCode = (UINT)((lParam & 0x0ff0000u) >> 16); + GetKeyboardState(keyState); + + // reset modifier state + keyState[VK_SHIFT] = 0; + keyState[VK_LSHIFT] = 0; + keyState[VK_RSHIFT] = 0; + keyState[VK_CONTROL] = 0; + keyState[VK_LCONTROL] = 0; + keyState[VK_RCONTROL] = 0; + keyState[VK_MENU] = 0; + keyState[VK_LMENU] = 0; + keyState[VK_RMENU] = 0; + keyState[VK_LWIN] = 0; + keyState[VK_RWIN] = 0; + + // translate virtual key to character + int n; + KeyID id; + if (CArchMiscWindows::isWindows95Family()) { + // XXX -- how do we get characters not in Latin-1? + WORD ascii; + n = ToAscii(virtualKey, scanCode, keyState, &ascii, 0); + id = static_cast(ascii & 0xffu); + } + else { + typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey, + UINT wScanCode, + PBYTE lpKeyState, + LPWSTR pwszBuff, + int cchBuff, + UINT wFlags); + ToUnicode_t s_ToUnicode = NULL; + if (s_ToUnicode == NULL) { + HMODULE userModule = GetModuleHandle("user32.dll"); + s_ToUnicode = + (ToUnicode_t)GetProcAddress(userModule, "ToUnicode"); + } + + WCHAR unicode[2]; + n = s_ToUnicode(virtualKey, scanCode, keyState, + unicode, sizeof(unicode) / sizeof(unicode[0]), + 0); + id = static_cast(unicode[0]); + } + switch (n) { + case -1: + // no hot keys on dead keys + return kKeyNone; + + default: + case 0: + // unmapped + return kKeyNone; + + case 1: + return id; + } +} + +KeyModifierMask +CHotkeyOptions::CConditionDialog::getModifiers() +{ + KeyModifierMask mask = 0; + if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) { + mask |= KeyModifierShift; + } + if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) { + mask |= KeyModifierControl; + } + if ((GetKeyState(VK_MENU) & 0x8000) != 0) { + mask |= KeyModifierAlt; + } + if ((GetKeyState(VK_LWIN) & 0x8000) != 0 || + (GetKeyState(VK_RWIN) & 0x8000) != 0) { + mask |= KeyModifierSuper; + } + return mask; +} + +bool +CHotkeyOptions::CConditionDialog::isGoodCondition() +{ + CInputFilter::CKeystrokeCondition* keyCondition = + dynamic_cast(s_condition); + return (keyCondition == NULL || keyCondition->getKey() != kKeyNone); +} + +BOOL CALLBACK +CHotkeyOptions::CConditionDialog::dlgProc(HWND hwnd, + UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + doInit(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hwnd, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +LRESULT CALLBACK +CHotkeyOptions::CConditionDialog::editProc(HWND hwnd, + UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_LBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonLeft); + } + else { + SetFocus(hwnd); + } + return 0; + + case WM_MBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonMiddle); + } + return 0; + + case WM_RBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonRight); + } + return 0; + + case WM_XBUTTONDOWN: + if (GetFocus() == hwnd) { + switch (HIWORD(wParam)) { + case XBUTTON1: + onButton(hwnd, kButtonExtra0 + 0); + break; + + case XBUTTON2: + onButton(hwnd, kButtonExtra0 + 1); + break; + } + } + return 0; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + onKey(hwnd, wParam, lParam); + return 0; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_CHAR: + case WM_SYSCHAR: + case WM_DEADCHAR: + case WM_SYSDEADCHAR: + return 0; + + case WM_SETFOCUS: + if (s_condition != NULL) { + delete s_lastGoodCondition; + s_lastGoodCondition = s_condition->clone(); + } + break; + + case WM_KILLFOCUS: + if (!isGoodCondition()) { + delete s_condition; + if (s_lastGoodCondition != NULL) { + s_condition = s_lastGoodCondition->clone(); + } + else { + s_condition = NULL; + } + } + fillHotkey(GetParent(hwnd)); + break; + + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + default: + break; + } + return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam); +} + + +// +// CHotkeyOptions::CActionDialog +// + +CConfig* CHotkeyOptions::CActionDialog::s_config = NULL; +bool CHotkeyOptions::CActionDialog::s_onActivate = false; +CInputFilter::CAction* + CHotkeyOptions::CActionDialog::s_action = NULL; +CInputFilter::CAction* + CHotkeyOptions::CActionDialog::s_lastGoodAction = NULL; +WNDPROC CHotkeyOptions::CActionDialog::s_editWndProc = NULL; + +bool +CHotkeyOptions::CActionDialog::doModal(HWND parent, CConfig* config, + CInputFilter::CAction*& action, bool& onActivate) +{ + s_config = config; + s_onActivate = onActivate; + s_action = action; + if (s_action != NULL) { + s_lastGoodAction = s_action->clone(); + } + else { + s_lastGoodAction = NULL; + } + + int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_ACTION), + parent, dlgProc); + + onActivate = s_onActivate; + action = s_action; + delete s_lastGoodAction; + s_action = NULL; + s_lastGoodAction = NULL; + + return (n == 1); +} + +void +CHotkeyOptions::CActionDialog::doInit(HWND hwnd) +{ + // subclass edit control + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY); + s_editWndProc = (WNDPROC)GetWindowLong(child, GWL_WNDPROC); + SetWindowLong(child, GWL_WNDPROC, (LONG)editProc); + setWindowText(getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY), ""); + fillHotkey(hwnd); + + // fill screens + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST); + SendMessage(child, CB_RESETCONTENT, 0, 0); + for (CConfig::const_iterator index = s_config->begin(); + index != s_config->end(); ) { + const CString& name = *index; + ++index; + if (index != s_config->end()) { + SendMessage(child, CB_INSERTSTRING, + (WPARAM)-1, (LPARAM)name.c_str()); + } + else { + SendMessage(child, CB_ADDSTRING, 0, (LPARAM)name.c_str()); + } + } + SendMessage(child, CB_SETCURSEL, 0, 0); + + // fill directions + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_LEFT).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_RIGHT).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_TOP).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_BOTTOM).c_str()); + SendMessage(child, CB_SETCURSEL, 0, 0); + + // fill lock modes + child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_LOCK_MODE_OFF).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_LOCK_MODE_ON).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_LOCK_MODE_TOGGLE).c_str()); + SendMessage(child, CB_SETCURSEL, 0, 0); + + // select when + if (s_onActivate) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_ACTIVATE); + } + else { + child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_DEACTIVATE); + } + setItemChecked(child, true); + + // select mode + child = NULL; + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_action); + CInputFilter::CMouseButtonAction* mouseAction = + dynamic_cast(s_action); + CInputFilter::CLockCursorToScreenAction* lockAction = + dynamic_cast(s_action); + CInputFilter::CSwitchToScreenAction* switchToAction = + dynamic_cast(s_action); + CInputFilter::CSwitchInDirectionAction* switchInAction = + dynamic_cast(s_action); + if (keyAction != NULL) { + if (dynamic_cast(s_action) != NULL) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP); + } + else if (keyAction->isOnPress()) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN); + } + else { + child = getItem(hwnd, IDC_HOTKEY_ACTION_UP); + } + } + else if (mouseAction != NULL) { + if (dynamic_cast(s_action) != NULL) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP); + } + else if (keyAction->isOnPress()) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN); + } + else { + child = getItem(hwnd, IDC_HOTKEY_ACTION_UP); + } + } + else if (lockAction != NULL) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST); + SendMessage(child, CB_SETCURSEL, lockAction->getMode(), 0); + child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK); + } + else if (switchToAction != NULL) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST); + DWORD i = SendMessage(child, CB_FINDSTRINGEXACT, (WPARAM)-1, + (LPARAM)switchToAction->getScreen().c_str()); + if (i == CB_ERR) { + i = 0; + } + SendMessage(child, CB_SETCURSEL, i, 0); + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO); + } + else if (switchInAction != NULL) { + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST); + SendMessage(child, CB_SETCURSEL, + switchInAction->getDirection() - kLeft, 0); + child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN); + } + if (child != NULL) { + setItemChecked(child, true); + } + + updateControls(hwnd); +} + +void +CHotkeyOptions::CActionDialog::fillHotkey(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY); + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_action); + CInputFilter::CMouseButtonAction* mouseAction = + dynamic_cast(s_action); + if (keyAction != NULL || mouseAction != NULL) { + setWindowText(child, s_action->format().c_str()); + } + else { + setWindowText(child, ""); + } + + // can only set screens in key actions + enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL); +} + +void +CHotkeyOptions::CActionDialog::updateControls(HWND hwnd) +{ + // determine which mode we're in + UInt32 mode = 0; + if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP)) || + isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN)) || + isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) { + mode = 1; + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO))) { + mode = 2; + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN))) { + mode = 3; + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_LOCK))) { + mode = 4; + } + + // enable/disable all mode specific controls + enableItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY, mode == 1); + enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST, mode == 2); + enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST, mode == 3); + enableItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST, mode == 4); + + // can only set screens in key actions + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_action); + enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL); +} + +void +CHotkeyOptions::CActionDialog::onButton(HWND hwnd, ButtonID button) +{ + IPlatformScreen::CButtonInfo* info = + IPrimaryScreen::CButtonInfo::alloc(button, getModifiers()); + delete s_action; + HWND parent = GetParent(hwnd); + if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) { + s_action = new CMouseButtonDownUpAction(info); + } + else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) { + s_action = new CInputFilter::CMouseButtonAction(info, true); + } + else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) { + s_action = new CInputFilter::CMouseButtonAction(info, false); + } + else { + s_action = NULL; + } + + fillHotkey(parent); +} + +void +CHotkeyOptions::CActionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + // ignore key repeats + if ((lParam & 0xc0000000u) == 0x40000000u) { + return; + } + + // ignore key releases if the action is complete and for the tab + // key (in case we were just tabbed to) + if ((lParam & 0x80000000u) != 0) { + if (isGoodAction() || wParam == VK_TAB) { + return; + } + } + + KeyID key = kKeyNone; + KeyModifierMask mask = getModifiers(); + switch (wParam) { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + case VK_CONTROL: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_MENU: + case VK_LMENU: + case VK_RMENU: + case VK_LWIN: + case VK_RWIN: + break; + + case VK_TAB: + // allow tabbing out of control + if ((mask & (KeyModifierControl | + KeyModifierAlt | KeyModifierSuper)) == 0) { + HWND next = hwnd; + if ((mask & KeyModifierShift) == 0) { + do { + next = GetWindow(next, GW_HWNDNEXT); + if (next == NULL) { + next = GetWindow(hwnd, GW_HWNDFIRST); + } + } while (next != hwnd && + (!IsWindowVisible(next) || + (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0)); + } + else { + do { + next = GetWindow(next, GW_HWNDPREV); + if (next == NULL) { + next = GetWindow(hwnd, GW_HWNDLAST); + } + } while (next != hwnd && + (!IsWindowVisible(next) || + (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0)); + } + SetFocus(next); + return; + } + // fall through + + default: + key = CMSWindowsKeyState::getKeyID(wParam, + static_cast((lParam & 0x1ff0000u) >> 16)); + switch (key) { + case kKeyNone: + // could be a character + key = getChar(wParam, lParam); + if (key == kKeyNone) { + return; + } + break; + + case kKeyShift_L: + case kKeyShift_R: + case kKeyControl_L: + case kKeyControl_R: + case kKeyAlt_L: + case kKeyAlt_R: + case kKeyMeta_L: + case kKeyMeta_R: + case kKeySuper_L: + case kKeySuper_R: + case kKeyCapsLock: + case kKeyNumLock: + case kKeyScrollLock: + // bogus + return; + } + break; + } + + // get old screen list + std::set screens; + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_action); + if (keyAction == NULL) { + keyAction = + dynamic_cast(s_lastGoodAction); + } + if (keyAction != NULL) { + IKeyState::CKeyInfo::split(keyAction->getInfo()->m_screens, screens); + } + + // create new action + IPlatformScreen::CKeyInfo* info = + IKeyState::CKeyInfo::alloc(key, mask, 0, 0, screens); + delete s_action; + HWND parent = GetParent(hwnd); + if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) { + s_action = new CKeystrokeDownUpAction(info); + } + else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) { + s_action = new CInputFilter::CKeystrokeAction(info, true); + } + else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) { + s_action = new CInputFilter::CKeystrokeAction(info, false); + } + else { + s_action = NULL; + } + + fillHotkey(parent); +} + +void +CHotkeyOptions::CActionDialog::onLockAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST); + LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0); + if (index != CB_ERR) { + delete s_action; + s_action = new CInputFilter::CLockCursorToScreenAction( + (CInputFilter::CLockCursorToScreenAction::Mode)index); + } +} + +void +CHotkeyOptions::CActionDialog::onSwitchToAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST); + CString screen = getWindowText(child); + delete s_action; + s_action = new CInputFilter::CSwitchToScreenAction(screen); +} + +void +CHotkeyOptions::CActionDialog::onSwitchInAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST); + LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0); + if (index != CB_ERR) { + delete s_action; + s_action = new CInputFilter::CSwitchInDirectionAction( + (EDirection)(index + kLeft)); + } +} + +KeyID +CHotkeyOptions::CActionDialog::getChar(WPARAM wParam, LPARAM lParam) +{ + BYTE keyState[256]; + UINT virtualKey = (UINT)wParam; + UINT scanCode = (UINT)((lParam & 0x0ff0000u) >> 16); + GetKeyboardState(keyState); + + // reset modifier state + keyState[VK_SHIFT] = 0; + keyState[VK_LSHIFT] = 0; + keyState[VK_RSHIFT] = 0; + keyState[VK_CONTROL] = 0; + keyState[VK_LCONTROL] = 0; + keyState[VK_RCONTROL] = 0; + keyState[VK_MENU] = 0; + keyState[VK_LMENU] = 0; + keyState[VK_RMENU] = 0; + keyState[VK_LWIN] = 0; + keyState[VK_RWIN] = 0; + + // translate virtual key to character + int n; + KeyID id; + if (CArchMiscWindows::isWindows95Family()) { + // XXX -- how do we get characters not in Latin-1? + WORD ascii; + n = ToAscii(virtualKey, scanCode, keyState, &ascii, 0); + id = static_cast(ascii & 0xffu); + } + else { + typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey, + UINT wScanCode, + PBYTE lpKeyState, + LPWSTR pwszBuff, + int cchBuff, + UINT wFlags); + ToUnicode_t s_ToUnicode = NULL; + if (s_ToUnicode == NULL) { + HMODULE userModule = GetModuleHandle("user32.dll"); + s_ToUnicode = + (ToUnicode_t)GetProcAddress(userModule, "ToUnicode"); + } + + WCHAR unicode[2]; + n = s_ToUnicode(virtualKey, scanCode, keyState, + unicode, sizeof(unicode) / sizeof(unicode[0]), + 0); + id = static_cast(unicode[0]); + } + switch (n) { + case -1: + // no hot keys on dead keys + return kKeyNone; + + default: + case 0: + // unmapped + return kKeyNone; + + case 1: + return id; + } +} + +KeyModifierMask +CHotkeyOptions::CActionDialog::getModifiers() +{ + KeyModifierMask mask = 0; + if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) { + mask |= KeyModifierShift; + } + if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) { + mask |= KeyModifierControl; + } + if ((GetKeyState(VK_MENU) & 0x8000) != 0) { + mask |= KeyModifierAlt; + } + if ((GetKeyState(VK_LWIN) & 0x8000) != 0 || + (GetKeyState(VK_RWIN) & 0x8000) != 0) { + mask |= KeyModifierSuper; + } + return mask; +} + +bool +CHotkeyOptions::CActionDialog::isGoodAction() +{ + CInputFilter::CMouseButtonAction* mouseAction = + dynamic_cast(s_action); + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_action); + return (mouseAction == NULL || keyAction == NULL || + keyAction->getInfo()->m_key != kKeyNone); +} + +void +CHotkeyOptions::CActionDialog::convertAction(HWND hwnd) +{ + if (s_lastGoodAction != NULL) { + CInputFilter::CMouseButtonAction* mouseAction = + dynamic_cast(s_lastGoodAction); + CInputFilter::CKeystrokeAction* keyAction = + dynamic_cast(s_lastGoodAction); + if (mouseAction != NULL) { + IPlatformScreen::CButtonInfo* info = + IPrimaryScreen::CButtonInfo::alloc(*mouseAction->getInfo()); + delete s_action; + if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) { + s_action = new CMouseButtonDownUpAction(info); + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) { + s_action = new CInputFilter::CMouseButtonAction(info, true); + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) { + s_action = new CInputFilter::CMouseButtonAction(info, false); + } + else { + free(info); + s_action = NULL; + } + } + else if (keyAction != NULL) { + IPlatformScreen::CKeyInfo* info = + IKeyState::CKeyInfo::alloc(*keyAction->getInfo()); + delete s_action; + if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) { + s_action = new CKeystrokeDownUpAction(info); + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) { + s_action = new CInputFilter::CKeystrokeAction(info, true); + } + else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) { + s_action = new CInputFilter::CKeystrokeAction(info, false); + } + else { + free(info); + s_action = NULL; + } + } + } +} + +bool +CHotkeyOptions::CActionDialog::isDownUpAction() +{ + return (dynamic_cast(s_action) != NULL || + dynamic_cast(s_action) != NULL); +} + +BOOL CALLBACK +CHotkeyOptions::CActionDialog::dlgProc(HWND hwnd, + UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + doInit(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + if (isDownUpAction()) { + s_onActivate = true; + } + EndDialog(hwnd, 1); + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + + case IDC_HOTKEY_ACTION_ON_ACTIVATE: + s_onActivate = true; + return TRUE; + + case IDC_HOTKEY_ACTION_ON_DEACTIVATE: + s_onActivate = false; + return TRUE; + + case IDC_HOTKEY_ACTION_DOWNUP: + case IDC_HOTKEY_ACTION_DOWN: + case IDC_HOTKEY_ACTION_UP: + convertAction(hwnd); + fillHotkey(hwnd); + updateControls(hwnd); + return TRUE; + + case IDC_HOTKEY_ACTION_LOCK: + onLockAction(hwnd); + updateControls(hwnd); + return TRUE; + + case IDC_HOTKEY_ACTION_SWITCH_TO: + onSwitchToAction(hwnd); + updateControls(hwnd); + return TRUE; + + case IDC_HOTKEY_ACTION_SWITCH_IN: + onSwitchInAction(hwnd); + updateControls(hwnd); + return TRUE; + + case IDC_HOTKEY_ACTION_LOCK_LIST: + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + onLockAction(hwnd); + return TRUE; + } + break; + + case IDC_HOTKEY_ACTION_SWITCH_TO_LIST: + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + onSwitchToAction(hwnd); + return TRUE; + } + break; + + case IDC_HOTKEY_ACTION_SWITCH_IN_LIST: + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + onSwitchInAction(hwnd); + return TRUE; + } + break; + + case IDC_HOTKEY_ACTION_SCREENS: + CScreensDialog::doModal(hwnd, s_config, + dynamic_cast(s_action)); + fillHotkey(hwnd); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +LRESULT CALLBACK +CHotkeyOptions::CActionDialog::editProc(HWND hwnd, + UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_LBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonLeft); + } + else { + SetFocus(hwnd); + } + return 0; + + case WM_MBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonMiddle); + } + return 0; + + case WM_RBUTTONDOWN: + if (GetFocus() == hwnd) { + onButton(hwnd, kButtonRight); + } + return 0; + + case WM_XBUTTONDOWN: + if (GetFocus() == hwnd) { + switch (HIWORD(wParam)) { + case XBUTTON1: + onButton(hwnd, kButtonExtra0 + 0); + break; + + case XBUTTON2: + onButton(hwnd, kButtonExtra0 + 1); + break; + } + } + return 0; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + onKey(hwnd, wParam, lParam); + return 0; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_CHAR: + case WM_SYSCHAR: + case WM_DEADCHAR: + case WM_SYSDEADCHAR: + return 0; + + case WM_SETFOCUS: + if (s_action != NULL) { + delete s_lastGoodAction; + s_lastGoodAction = s_action->clone(); + } + break; + + case WM_KILLFOCUS: + if (!isGoodAction()) { + delete s_action; + if (s_lastGoodAction != NULL) { + s_action = s_lastGoodAction->clone(); + } + else { + s_action = NULL; + } + } + else if (s_action != NULL) { + delete s_lastGoodAction; + s_lastGoodAction = s_action->clone(); + } + fillHotkey(GetParent(hwnd)); + break; + + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + default: + break; + } + return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam); +} + + +// +// CHotkeyOptions::CScreensDialog +// + +CConfig* CHotkeyOptions::CScreensDialog::s_config = NULL; +CInputFilter::CKeystrokeAction* + CHotkeyOptions::CScreensDialog::s_action = NULL; +CHotkeyOptions::CScreensDialog::CScreens + CHotkeyOptions::CScreensDialog::s_nonTargets; +CHotkeyOptions::CScreensDialog::CScreens + CHotkeyOptions::CScreensDialog::s_targets; +CString CHotkeyOptions::CScreensDialog::s_allScreens; + +void +CHotkeyOptions::CScreensDialog::doModal(HWND parent, CConfig* config, + CInputFilter::CKeystrokeAction* action) +{ + s_allScreens = getString(IDS_ALL_SCREENS); + s_config = config; + s_action = action; + DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_SCREENS), + parent, dlgProc); + s_config = NULL; + s_action = NULL; +} + +void +CHotkeyOptions::CScreensDialog::doInit(HWND hwnd) +{ + s_nonTargets.clear(); + s_targets.clear(); + + // get screens from config + s_nonTargets.insert("*"); + for (CConfig::const_iterator i = s_config->begin(); + i != s_config->end(); ++i) { + s_nonTargets.insert(*i); + } + + // get screens in action + IKeyState::CKeyInfo::split(s_action->getInfo()->m_screens, s_targets); + + // remove screens in action from screens in config + for (CScreens::const_iterator i = s_targets.begin(); + i != s_targets.end(); ++i) { + s_nonTargets.erase(*i); + } + + // fill dialog + fillScreens(hwnd); + updateControls(hwnd); +} + +void +CHotkeyOptions::CScreensDialog::doFini(HWND) +{ + // put screens into action + const IPlatformScreen::CKeyInfo* oldInfo = s_action->getInfo(); + IPlatformScreen::CKeyInfo* newInfo = + IKeyState::CKeyInfo::alloc(oldInfo->m_key, + oldInfo->m_mask, 0, 0, s_targets); + s_action->adoptInfo(newInfo); +} + +void +CHotkeyOptions::CScreensDialog::fillScreens(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC); + SendMessage(child, LB_RESETCONTENT, 0, 0); + for (CScreens::const_iterator i = s_nonTargets.begin(); + i != s_nonTargets.end(); ++i) { + CString name = *i; + if (name == "*") { + name = s_allScreens; + } + SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)name.c_str()); + } + + child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST); + SendMessage(child, LB_RESETCONTENT, 0, 0); + for (CScreens::const_iterator i = s_targets.begin(); + i != s_targets.end(); ++i) { + CString name = *i; + if (name == "*") { + name = s_allScreens; + } + SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)name.c_str()); + } + if (s_targets.empty()) { + // if no targets then add a special item so the user knows + // what'll happen + CString activeScreenLabel = getString(IDS_ACTIVE_SCREEN); + SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)activeScreenLabel.c_str()); + } +} + +void +CHotkeyOptions::CScreensDialog::updateControls(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC); + bool canAdd = (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0); + child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST); + bool canRemove = (!s_targets.empty() && + (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0)); + + enableItem(hwnd, IDC_HOTKEY_SCREENS_ADD, canAdd); + enableItem(hwnd, IDC_HOTKEY_SCREENS_REMOVE, canRemove); +} + +void +CHotkeyOptions::CScreensDialog::add(HWND hwnd) +{ + CScreens selected; + getSelected(hwnd, IDC_HOTKEY_SCREENS_SRC, s_nonTargets, selected); + for (CScreens::const_iterator i = selected.begin(); + i != selected.end(); ++i) { + s_targets.insert(*i); + s_nonTargets.erase(*i); + } + fillScreens(hwnd); + updateControls(hwnd); +} + +void +CHotkeyOptions::CScreensDialog::remove(HWND hwnd) +{ + CScreens selected; + getSelected(hwnd, IDC_HOTKEY_SCREENS_DST, s_targets, selected); + for (CScreens::const_iterator i = selected.begin(); + i != selected.end(); ++i) { + s_nonTargets.insert(*i); + s_targets.erase(*i); + } + fillScreens(hwnd); + updateControls(hwnd); +} + +void +CHotkeyOptions::CScreensDialog::getSelected(HWND hwnd, UINT id, + const CScreens& inScreens, CScreens& outScreens) +{ + // get the selected item indices + HWND child = getItem(hwnd, id); + UInt32 n = (UInt32)SendMessage(child, LB_GETSELCOUNT, 0, 0); + int* index = new int[n]; + SendMessage(child, LB_GETSELITEMS, (WPARAM)n, (LPARAM)index); + + // get the items in a vector + std::vector tmpList; + for (CScreens::const_iterator i = inScreens.begin(); + i != inScreens.end(); ++i) { + tmpList.push_back(*i); + } + + // get selected items into the output set + outScreens.clear(); + for (UInt32 i = 0; i < n; ++i) { + outScreens.insert(tmpList[index[i]]); + } + + // clean up + delete[] index; +} + +BOOL CALLBACK +CHotkeyOptions::CScreensDialog::dlgProc(HWND hwnd, + UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_INITDIALOG: + doInit(hwnd); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + doFini(hwnd); + EndDialog(hwnd, 0); + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + + case IDC_HOTKEY_SCREENS_ADD: + add(hwnd); + return TRUE; + + case IDC_HOTKEY_SCREENS_REMOVE: + remove(hwnd); + return TRUE; + + case IDC_HOTKEY_SCREENS_SRC: + case IDC_HOTKEY_SCREENS_DST: + switch (HIWORD(wParam)) { + case LBN_SELCANCEL: + case LBN_SELCHANGE: + updateControls(hwnd); + return TRUE; + } + break; + } + break; + + case WM_CTLCOLORLISTBOX: + if (s_targets.empty() && + (HWND)lParam == getItem(hwnd, IDC_HOTKEY_SCREENS_DST)) { + // override colors + HDC dc = (HDC)wParam; + SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT)); + return (BOOL)GetSysColorBrush(COLOR_WINDOW); + } + break; + + default: + break; + } + + return FALSE; +} diff --git a/cmd/launcher/CHotkeyOptions.h b/cmd/launcher/CHotkeyOptions.h new file mode 100644 index 00000000..b63fcf2b --- /dev/null +++ b/cmd/launcher/CHotkeyOptions.h @@ -0,0 +1,225 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CHOTKEYOPTIONS_H +#define CHOTKEYOPTIONS_H + +#include "CString.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CInputFilter.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +class CConfig; + +//! Hotkey options dialog for Microsoft Windows launcher +class CHotkeyOptions { +public: + CHotkeyOptions(HWND parent, CConfig*); + ~CHotkeyOptions(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + //@} + +private: + void doInit(HWND hwnd); + + void fillHotkeys(HWND hwnd, UInt32 select = (UInt32)-1); + void updateHotkeysControls(HWND hwnd); + + void addHotkey(HWND hwnd); + void removeHotkey(HWND hwnd); + void editHotkey(HWND hwnd); + + void fillActions(HWND hwnd, UInt32 select = (UInt32)-1); + void updateActionsControls(HWND hwnd); + + void addAction(HWND hwnd); + void removeAction(HWND hwnd); + void editAction(HWND hwnd); + + bool editCondition(HWND hwnd, CInputFilter::CCondition*&); + bool editAction(HWND hwnd, CInputFilter::CAction*&, + bool& onActivate); + + void openRule(HWND hwnd); + void closeRule(HWND hwnd); + UInt32 findMatchingAction( + const CInputFilter::CKeystrokeAction*) const; + UInt32 findMatchingAction( + const CInputFilter::CMouseButtonAction*) const; + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + + // special actions we use to combine matching down/up actions into a + // single action for the convenience of the user. + class CKeystrokeDownUpAction : public CInputFilter::CKeystrokeAction { + public: + CKeystrokeDownUpAction(IPlatformScreen::CKeyInfo* adoptedInfo) : + CInputFilter::CKeystrokeAction(adoptedInfo, true) { } + + // CAction overrides + virtual CInputFilter::CAction* clone() const + { + IKeyState::CKeyInfo* info = IKeyState::CKeyInfo::alloc(*getInfo()); + return new CKeystrokeDownUpAction(info); + } + + protected: + // CKeystrokeAction overrides + virtual const char* formatName() const { return "keystroke"; } + }; + class CMouseButtonDownUpAction : public CInputFilter::CMouseButtonAction { + public: + CMouseButtonDownUpAction(IPrimaryScreen::CButtonInfo* adoptedInfo) : + CInputFilter::CMouseButtonAction(adoptedInfo, true) { } + + // CAction overrides + virtual CInputFilter::CAction* clone() const + { + IPlatformScreen::CButtonInfo* info = + IPrimaryScreen::CButtonInfo::alloc(*getInfo()); + return new CMouseButtonDownUpAction(info); + } + + protected: + // CMouseButtonAction overrides + virtual const char* formatName() const { return "mousebutton"; } + }; + + class CConditionDialog { + public: + static bool doModal(HWND parent, CInputFilter::CCondition*&); + + private: + static void doInit(HWND hwnd); + static void fillHotkey(HWND hwnd); + + static void onButton(HWND hwnd, ButtonID button); + static void onKey(HWND hwnd, WPARAM wParam, LPARAM lParam); + static KeyID getChar(WPARAM wParam, LPARAM lParam); + static KeyModifierMask + getModifiers(); + + static bool isGoodCondition(); + + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK editProc(HWND hwnd, UINT, WPARAM, LPARAM); + + private: + static CInputFilter::CCondition* + s_condition; + static CInputFilter::CCondition* + s_lastGoodCondition; + static WNDPROC s_editWndProc; + }; + + class CActionDialog { + public: + static bool doModal(HWND parent, CConfig* config, + CInputFilter::CAction*&, bool& onActivate); + + private: + static void doInit(HWND hwnd); + static void fillHotkey(HWND hwnd); + static void updateControls(HWND hwnd); + + static void onButton(HWND hwnd, ButtonID button); + static void onKey(HWND hwnd, WPARAM wParam, LPARAM lParam); + static void onLockAction(HWND hwnd); + static void onSwitchToAction(HWND hwnd); + static void onSwitchInAction(HWND hwnd); + + static KeyID getChar(WPARAM wParam, LPARAM lParam); + static KeyModifierMask + getModifiers(); + + static bool isGoodAction(); + static void convertAction(HWND hwnd); + + static bool isDownUpAction(); + + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK editProc(HWND hwnd, UINT, WPARAM, LPARAM); + + private: + static CConfig* s_config; + static bool s_onActivate; + static CInputFilter::CAction* + s_action; + static CInputFilter::CAction* + s_lastGoodAction; + static WNDPROC s_editWndProc; + }; + +// public to allow CActionDialog to use it +public: + class CScreensDialog { + public: + static void doModal(HWND parent, CConfig* config, + CInputFilter::CKeystrokeAction*); + + // public due to compiler brokenness + typedef std::set CScreens; + + private: + + static void doInit(HWND hwnd); + static void doFini(HWND hwnd); + static void fillScreens(HWND hwnd); + static void updateControls(HWND hwnd); + + static void add(HWND hwnd); + static void remove(HWND hwnd); + + static void getSelected(HWND hwnd, UINT id, + const CScreens& inScreens, CScreens& outScreens); + + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + + private: + static CConfig* s_config; + static CInputFilter::CKeystrokeAction* s_action; + static CScreens s_nonTargets; + static CScreens s_targets; + static CString s_allScreens; + }; + +private: + static CHotkeyOptions* s_singleton; + + HWND m_parent; + CConfig* m_config; + CInputFilter* m_inputFilter; + CInputFilter::CRule m_activeRule; + UInt32 m_activeRuleIndex; +}; + +#endif diff --git a/cmd/launcher/CInfo.cpp b/cmd/launcher/CInfo.cpp new file mode 100644 index 00000000..f2292db8 --- /dev/null +++ b/cmd/launcher/CInfo.cpp @@ -0,0 +1,111 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ProtocolTypes.h" +#include "CStringUtil.h" +#include "Version.h" +#include "CArch.h" +#include "CInfo.h" +#include "LaunchUtil.h" +#include "resource.h" + +// +// CInfo +// + +CInfo* CInfo::s_singleton = NULL; + +CInfo::CInfo(HWND parent) : + m_parent(parent) +{ + assert(s_singleton == NULL); + s_singleton = this; +} + +CInfo::~CInfo() +{ + s_singleton = NULL; +} + +void +CInfo::doModal() +{ + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_INFO), + m_parent, dlgProc, (LPARAM)this); +} + +void +CInfo::init(HWND hwnd) +{ + // collect info + CString version = + CStringUtil::format(getString(IDS_TITLE).c_str(), VERSION); + CString hostname = ARCH->getHostName(); + CString address = ARCH->addrToString(ARCH->nameToAddr(hostname)); + CString userConfig = ARCH->getUserDirectory(); + if (!userConfig.empty()) { + userConfig = ARCH->concatPath(userConfig, CONFIG_NAME); + } + CString sysConfig = ARCH->getSystemDirectory(); + if (!sysConfig.empty()) { + sysConfig = ARCH->concatPath(sysConfig, CONFIG_NAME); + } + + // set info + HWND child; + child = getItem(hwnd, IDC_INFO_VERSION); + setWindowText(child, version); + child = getItem(hwnd, IDC_INFO_HOSTNAME); + setWindowText(child, hostname); + child = getItem(hwnd, IDC_INFO_IP_ADDRESS); + setWindowText(child, address); + child = getItem(hwnd, IDC_INFO_USER_CONFIG); + setWindowText(child, userConfig); + child = getItem(hwnd, IDC_INFO_SYS_CONFIG); + setWindowText(child, sysConfig); + + // focus on okay button + SetFocus(getItem(hwnd, IDOK)); +} + +BOOL +CInfo::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM) +{ + switch (message) { + case WM_INITDIALOG: + init(hwnd); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + } + break; + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CInfo::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} diff --git a/cmd/launcher/CInfo.h b/cmd/launcher/CInfo.h new file mode 100644 index 00000000..2d85ec7d --- /dev/null +++ b/cmd/launcher/CInfo.h @@ -0,0 +1,57 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CINFO_H +#define CINFO_H + +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +//! Info dialog for Microsoft Windows launcher +class CInfo { +public: + CInfo(HWND parent); + ~CInfo(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + //@} + +private: + void init(HWND hwnd); + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CInfo* s_singleton; + + HWND m_parent; +}; + +#endif diff --git a/cmd/launcher/CScreensLinks.cpp b/cmd/launcher/CScreensLinks.cpp new file mode 100644 index 00000000..dbbca67b --- /dev/null +++ b/cmd/launcher/CScreensLinks.cpp @@ -0,0 +1,855 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CConfig.h" +#include "ProtocolTypes.h" +#include "CStringUtil.h" +#include "CArch.h" +#include "CScreensLinks.h" +#include "CAddScreen.h" +#include "LaunchUtil.h" +#include "resource.h" + +// +// CScreensLinks +// + +CScreensLinks* CScreensLinks::s_singleton = NULL; + +CScreensLinks::CScreensLinks(HWND parent, CConfig* config) : + m_parent(parent), + m_mainConfig(config), + m_config(&m_scratchConfig) +{ + assert(s_singleton == NULL); + s_singleton = this; + + // get formatting strings + m_linkFormat = getString(IDS_LINK_FORMAT); + m_intervalFormat = getString(IDS_LINK_INTERVAL_FORMAT); + m_newLinkLabel = getString(IDS_NEW_LINK); + m_sideLabel[kLeft - kFirstDirection] = getString(IDS_SIDE_LEFT); + m_sideLabel[kRight - kFirstDirection] = getString(IDS_SIDE_RIGHT); + m_sideLabel[kTop - kFirstDirection] = getString(IDS_SIDE_TOP); + m_sideLabel[kBottom - kFirstDirection] = getString(IDS_SIDE_BOTTOM); + + // GDI objects + m_redPen = CreatePen(PS_INSIDEFRAME, 1, RGB(255, 0, 0)); +} + +CScreensLinks::~CScreensLinks() +{ + DeleteObject(m_redPen); + s_singleton = NULL; +} + +void +CScreensLinks::doModal() +{ + // do dialog + DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_SCREENS_LINKS), + m_parent, dlgProc, (LPARAM)this); +} + +void +CScreensLinks::init(HWND hwnd) +{ + // get initial config + m_scratchConfig = *m_mainConfig; + + // fill side list box (in EDirection order) + HWND child = getItem(hwnd, IDC_SCREENS_SRC_SIDE); + SendMessage(child, CB_ADDSTRING, 0, (LPARAM)TEXT("---")); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_LEFT).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_RIGHT).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_TOP).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_EDGE_BOTTOM).c_str()); + + // create error boxes + m_srcSideError = createErrorBox(hwnd); + m_srcScreenError = createErrorBox(hwnd); + m_dstScreenError = createErrorBox(hwnd); + resizeErrorBoxes(); + + m_selectedLink = -1; + m_editedLink = CEdgeLink(); + m_edgeLinks.clear(); + updateScreens(hwnd, ""); + updateScreensControls(hwnd); + updateLinks(hwnd); + updateLinksControls(hwnd); +} + +bool +CScreensLinks::save(HWND /*hwnd*/) +{ + *m_mainConfig = m_scratchConfig; + return true; +} + +BOOL +CScreensLinks::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_INITDIALOG: + init(hwnd); + return TRUE; + + case WM_SIZE: + resizeErrorBoxes(); + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + SetFocus(getItem(hwnd, IDOK)); + if (save(hwnd)) { + EndDialog(hwnd, 0); + } + return TRUE; + + case IDCANCEL: + EndDialog(hwnd, 0); + return TRUE; + + case IDC_SCREENS_SCREENS: + switch (HIWORD(wParam)) { + case LBN_DBLCLK: + editScreen(hwnd); + return TRUE; + + case LBN_SELCHANGE: + updateScreensControls(hwnd); + updateLinkView(hwnd); + return TRUE; + + case LBN_SELCANCEL: + updateScreensControls(hwnd); + updateLinkView(hwnd); + return TRUE; + } + break; + + case IDC_SCREENS_ADD_SCREEN: + addScreen(hwnd); + return TRUE; + + case IDC_SCREENS_REMOVE_SCREEN: + removeScreen(hwnd); + return TRUE; + + case IDC_SCREENS_EDIT_SCREEN: + editScreen(hwnd); + return TRUE; + + case IDC_SCREENS_LINKS: + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + editLink(hwnd); + return TRUE; + + case LBN_SELCANCEL: + editLink(hwnd); + return TRUE; + } + break; + + case IDC_SCREENS_ADD_LINK: + addLink(hwnd); + return TRUE; + + case IDC_SCREENS_REMOVE_LINK: + removeLink(hwnd); + return TRUE; + + case IDC_SCREENS_SRC_SIDE: + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: + changeSrcSide(hwnd); + break; + } + break; + + case IDC_SCREENS_SRC_SCREEN: + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: + changeSrcScreen(hwnd); + break; + } + break; + + case IDC_SCREENS_DST_SCREEN: + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: + changeDstScreen(hwnd); + break; + } + break; + + case IDC_SCREENS_SRC_START: + switch (HIWORD(wParam)) { + case EN_KILLFOCUS: + changeIntervalStart(hwnd, LOWORD(wParam), + m_editedLink.m_srcInterval); + break; + } + break; + + case IDC_SCREENS_SRC_END: + switch (HIWORD(wParam)) { + case EN_KILLFOCUS: + changeIntervalEnd(hwnd, LOWORD(wParam), + m_editedLink.m_srcInterval); + break; + } + break; + + case IDC_SCREENS_DST_START: + switch (HIWORD(wParam)) { + case EN_KILLFOCUS: + changeIntervalStart(hwnd, LOWORD(wParam), + m_editedLink.m_dstInterval); + break; + } + break; + + case IDC_SCREENS_DST_END: + switch (HIWORD(wParam)) { + case EN_KILLFOCUS: + changeIntervalEnd(hwnd, LOWORD(wParam), + m_editedLink.m_dstInterval); + break; + } + break; + } + + break; + + case WM_CTLCOLORSTATIC: + switch (GetDlgCtrlID((HWND)lParam)) { + case IDC_SCREENS_OVERLAP_ERROR: + SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE)); + SetTextColor((HDC)wParam, RGB(255, 0, 0)); + return (BOOL)GetSysColorBrush(COLOR_3DFACE); + } + break; + + // error outlines + case WM_DRAWITEM: { + DRAWITEMSTRUCT* di = (DRAWITEMSTRUCT*)lParam; + if (di->CtlType == ODT_STATIC) { + HGDIOBJ oldPen = SelectObject(di->hDC, m_redPen); + HGDIOBJ oldBrush = SelectObject(di->hDC, + GetStockObject(NULL_BRUSH)); + Rectangle(di->hDC, di->rcItem.left, di->rcItem.top, + di->rcItem.right, di->rcItem.bottom); + SelectObject(di->hDC, oldPen); + SelectObject(di->hDC, oldBrush); + return TRUE; + } + break; + } + + default: + break; + } + + return FALSE; +} + +BOOL CALLBACK +CScreensLinks::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return s_singleton->doDlgProc(hwnd, message, wParam, lParam); +} + +CString +CScreensLinks::getSelectedScreen(HWND hwnd) const +{ + HWND child = getItem(hwnd, IDC_SCREENS_SCREENS); + LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); + if (index == LB_ERR) { + return CString(); + } + + LRESULT size = SendMessage(child, LB_GETTEXTLEN, index, 0); + char* buffer = new char[size + 1]; + SendMessage(child, LB_GETTEXT, index, (LPARAM)buffer); + buffer[size] = '\0'; + CString result(buffer); + delete[] buffer; + return result; +} + +void +CScreensLinks::addScreen(HWND hwnd) +{ + CAddScreen dialog(hwnd, m_config, ""); + if (dialog.doModal()) { + updateScreens(hwnd, dialog.getName()); + updateScreensControls(hwnd); + updateLinks(hwnd); + updateLinksControls(hwnd); + } +} + +void +CScreensLinks::editScreen(HWND hwnd) +{ + CString oldName = getSelectedScreen(hwnd); + CAddScreen dialog(hwnd, m_config, oldName); + if (dialog.doModal()) { + CString newName = dialog.getName(); + + // rename screens in the edge list + if (newName != oldName) { + for (size_t i = 0; i < m_edgeLinks.size(); ++i) { + m_edgeLinks[i].rename(oldName, newName); + } + m_editedLink.rename(oldName, newName); + } + + updateScreens(hwnd, newName); + updateScreensControls(hwnd); + updateLinks(hwnd); + updateLinksControls(hwnd); + } +} + +void +CScreensLinks::removeScreen(HWND hwnd) +{ + // remove screen from config (this also removes aliases) + m_config->removeScreen(getSelectedScreen(hwnd)); + + // update dialog + updateScreens(hwnd, ""); + updateScreensControls(hwnd); + updateLinks(hwnd); + updateLinksControls(hwnd); +} + +void +CScreensLinks::addLink(HWND hwnd) +{ + if (m_editedLink.connect(m_config)) { + m_editedLink = CEdgeLink(); + updateLinks(hwnd); + updateLinksControls(hwnd); + } +} + +void +CScreensLinks::editLink(HWND hwnd) +{ + // get selection + HWND child = getItem(hwnd, IDC_SCREENS_LINKS); + DWORD i = SendMessage(child, LB_GETCURSEL, 0, 0); + if (i != LB_ERR && i != (DWORD)m_edgeLinks.size()) { + // existing link + m_selectedLink = (SInt32)SendMessage(child, LB_GETITEMDATA, i, 0); + m_editedLink = m_edgeLinks[m_selectedLink]; + } + else { + // new link + m_selectedLink = -1; + m_editedLink = CEdgeLink(); + } + updateLinksControls(hwnd); +} + +void +CScreensLinks::removeLink(HWND hwnd) +{ + if (m_editedLink.disconnect(m_config)) { + updateLinks(hwnd); + updateLinksControls(hwnd); + } +} + +void +CScreensLinks::updateScreens(HWND hwnd, const CString& selectName) +{ + HWND child; + + // set screen list + child = getItem(hwnd, IDC_SCREENS_SCREENS); + SendMessage(child, LB_RESETCONTENT, 0, 0); + for (CConfig::const_iterator index = m_config->begin(); + index != m_config->end(); ) { + const CString& name = *index; + ++index; + if (index != m_config->end()) { + SendMessage(child, LB_INSERTSTRING, + (WPARAM)-1, (LPARAM)name.c_str()); + } + else { + SendMessage(child, LB_ADDSTRING, 0, (LPARAM)name.c_str()); + } + } + + // find the named screen + if (!selectName.empty()) { + DWORD i = SendMessage(child, LB_FINDSTRINGEXACT, + (UINT)-1, (LPARAM)selectName.c_str()); + if (i != LB_ERR) { + SendMessage(child, LB_SETSEL, TRUE, i); + } + } +} + +void +CScreensLinks::updateScreensControls(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_SCREENS_SCREENS); + bool screenSelected = (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR); + + enableItem(hwnd, IDC_SCREENS_ADD_SCREEN, TRUE); + enableItem(hwnd, IDC_SCREENS_EDIT_SCREEN, screenSelected); + enableItem(hwnd, IDC_SCREENS_REMOVE_SCREEN, screenSelected); +} + +void +CScreensLinks::updateLinks(HWND hwnd) +{ + HWND links = getItem(hwnd, IDC_SCREENS_LINKS); + HWND srcScreens = getItem(hwnd, IDC_SCREENS_SRC_SCREEN); + HWND dstScreens = getItem(hwnd, IDC_SCREENS_DST_SCREEN); + + // get old selection + CEdgeLink oldLink; + if (m_selectedLink != -1) { + oldLink = m_edgeLinks[m_selectedLink]; + } + + // clear links and screens + SendMessage(links, LB_RESETCONTENT, 0, 0); + SendMessage(srcScreens, CB_RESETCONTENT, 0, 0); + SendMessage(dstScreens, CB_RESETCONTENT, 0, 0); + m_edgeLinks.clear(); + + // add "no screen" items + SendMessage(srcScreens, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)TEXT("----")); + SendMessage(dstScreens, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)TEXT("----")); + + // add links and screens + for (CConfig::const_iterator i = m_config->begin(); + i != m_config->end(); ++i) { + const CString& name = *i; + + // add screen + SendMessage(srcScreens, CB_INSERTSTRING, (WPARAM)-1, + (LPARAM)name.c_str()); + SendMessage(dstScreens, CB_INSERTSTRING, (WPARAM)-1, + (LPARAM)name.c_str()); + + // add links for screen + for (CConfig::link_const_iterator j = m_config->beginNeighbor(name), + n = m_config->endNeighbor(name); + j != n; ++j) { + DWORD k = m_edgeLinks.size(); + m_edgeLinks.push_back(CEdgeLink(name, *j)); + SendMessage(links, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)formatLink(m_edgeLinks.back()).c_str()); + SendMessage(links, LB_SETITEMDATA, (WPARAM)k, (LPARAM)k); + } + } + + // add "new link" item to sort + SendMessage(links, LB_ADDSTRING, 0, (LPARAM)m_newLinkLabel.c_str()); + + // remove the "new link" item then insert it on the end + DWORD i = SendMessage(links, LB_FINDSTRINGEXACT, + (UINT)-1, (LPARAM)m_newLinkLabel.c_str()); + if (i != LB_ERR) { + SendMessage(links, LB_DELETESTRING, i, 0); + } + SendMessage(links, LB_INSERTSTRING, (WPARAM)-1, + (LPARAM)getString(IDS_NEW_LINK).c_str()); + SendMessage(links, LB_SETITEMDATA, (WPARAM)m_edgeLinks.size(), + (LPARAM)-1); + + // select the same link as before + SendMessage(links, LB_SETCURSEL, (WPARAM)m_edgeLinks.size(), 0); + if (m_selectedLink != -1) { + m_selectedLink = -1; + for (size_t j = 0; j < m_edgeLinks.size(); ++j) { + if (m_edgeLinks[j] == oldLink) { + // found matching link + m_selectedLink = j; + for (size_t k = 0; k < m_edgeLinks.size(); ++k) { + if (SendMessage(links, LB_GETITEMDATA, k, 0) == (int)j) { + SendMessage(links, LB_SETCURSEL, k, 0); + break; + } + } + break; + } + } + + // if we can't find the link anymore then reset edited link + if (m_selectedLink == -1) { + m_editedLink = CEdgeLink(); + } + } +} + +void +CScreensLinks::updateLinksControls(HWND hwnd) +{ + // get selection. select "new link" if nothing is selected. + HWND child = getItem(hwnd, IDC_SCREENS_LINKS); + if (m_selectedLink == -1) { + SendMessage(child, LB_SETCURSEL, m_edgeLinks.size(), 0); + } + + // enable/disable remove button + enableItem(hwnd, IDC_SCREENS_REMOVE_LINK, m_selectedLink != -1); + + // fill link entry controls from m_editedLink + updateLinkEditControls(hwnd, m_editedLink); + updateLinkValid(hwnd, m_editedLink); + updateLinkView(hwnd); +} + +void +CScreensLinks::changeSrcSide(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_SCREENS_SRC_SIDE); + m_editedLink.m_srcSide = (EDirection)SendMessage(child, CB_GETCURSEL, 0, 0); + updateLink(hwnd); +} + +void +CScreensLinks::changeSrcScreen(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_SCREENS_SRC_SCREEN); + m_editedLink.m_srcName = getWindowText(child); + updateLink(hwnd); +} + +void +CScreensLinks::changeDstScreen(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_SCREENS_DST_SCREEN); + m_editedLink.m_dstName = getWindowText(child); + updateLink(hwnd); +} + +void +CScreensLinks::changeIntervalStart(HWND hwnd, int id, CConfig::CInterval& i) +{ + int x = (int)GetDlgItemInt(hwnd, id, NULL, FALSE); + if (x < 0) { + x = 0; + } + else if (x > 99) { + x = 99; + } + + i.first = 0.01f * (float)x; + if (i.first >= i.second) { + i.second = 0.01f * (float)(x + 1); + } + + updateLinkIntervalControls(hwnd, m_editedLink); + updateLink(hwnd); +} + +void +CScreensLinks::changeIntervalEnd(HWND hwnd, int id, CConfig::CInterval& i) +{ + int x = (int)GetDlgItemInt(hwnd, id, NULL, FALSE); + if (x < 1) { + x = 1; + } + else if (x > 100) { + x = 100; + } + + i.second = 0.01f * (float)x; + if (i.first >= i.second) { + i.first = 0.01f * (float)(x - 1); + } + + updateLinkIntervalControls(hwnd, m_editedLink); + updateLink(hwnd); +} + +void +CScreensLinks::selectScreen(HWND hwnd, int id, const CString& name) +{ + HWND child = getItem(hwnd, id); + DWORD i = SendMessage(child, CB_FINDSTRINGEXACT, (WPARAM)-1, + (LPARAM)name.c_str()); + if (i == CB_ERR) { + // no match, select no screen + SendMessage(child, CB_SETCURSEL, 0, 0); + } + else { + SendMessage(child, CB_SETCURSEL, i, 0); + } +} + +void +CScreensLinks::updateLinkEditControls(HWND hwnd, const CEdgeLink& link) +{ + // fill link entry controls from link + HWND child = getItem(hwnd, IDC_SCREENS_SRC_SIDE); + SendMessage(child, CB_SETCURSEL, link.m_srcSide, 0); + selectScreen(hwnd, IDC_SCREENS_SRC_SCREEN, link.m_srcName); + selectScreen(hwnd, IDC_SCREENS_DST_SCREEN, link.m_dstName); + updateLinkIntervalControls(hwnd, link); +} + +void +CScreensLinks::updateLinkIntervalControls(HWND hwnd, const CEdgeLink& link) +{ + HWND child; + + // src interval + child = getItem(hwnd, IDC_SCREENS_SRC_START); + setWindowText(child, formatIntervalValue(link.m_srcInterval.first)); + child = getItem(hwnd, IDC_SCREENS_SRC_END); + setWindowText(child, formatIntervalValue(link.m_srcInterval.second)); + + // dst interval + child = getItem(hwnd, IDC_SCREENS_DST_START); + setWindowText(child, formatIntervalValue(link.m_dstInterval.first)); + child = getItem(hwnd, IDC_SCREENS_DST_END); + setWindowText(child, formatIntervalValue(link.m_dstInterval.second)); +} + +void +CScreensLinks::updateLink(HWND hwnd) +{ + updateLinkValid(hwnd, m_editedLink); + + // update link in config + if (m_selectedLink != -1 && m_editedLinkIsValid) { + // editing an existing link and entry is valid + if (m_edgeLinks[m_selectedLink].disconnect(m_config)) { + // successfully removed old link + if (!m_editedLink.connect(m_config)) { + // couldn't set new link so restore old link + m_edgeLinks[m_selectedLink].connect(m_config); + } + else { + m_edgeLinks[m_selectedLink] = m_editedLink; + updateLinks(hwnd); + updateLinkEditControls(hwnd, m_editedLink); + } + } + } + + updateLinkView(hwnd); +} + +void +CScreensLinks::updateLinkValid(HWND hwnd, const CEdgeLink& link) +{ + m_editedLinkIsValid = true; + + // check source side and screen + if (link.m_srcSide == kNoDirection) { + m_editedLinkIsValid = false; + ShowWindow(m_srcSideError, SW_SHOWNA); + } + else { + ShowWindow(m_srcSideError, SW_HIDE); + } + if (!m_config->isCanonicalName(link.m_srcName)) { + m_editedLinkIsValid = false; + ShowWindow(m_srcScreenError, SW_SHOWNA); + } + else { + ShowWindow(m_srcScreenError, SW_HIDE); + } + + // check for overlap. if editing a link we must remove it, then + // check for overlap and restore the old link. + bool overlap = false; + if (m_editedLinkIsValid) { + if (m_selectedLink == -1) { + if (link.overlaps(m_config)) { + m_editedLinkIsValid = false; + overlap = true; + } + } + else { + if (m_edgeLinks[m_selectedLink].disconnect(m_config)) { + overlap = link.overlaps(m_config); + m_edgeLinks[m_selectedLink].connect(m_config); + if (overlap) { + m_editedLinkIsValid = false; + } + } + } + } + ShowWindow(getItem(hwnd, IDC_SCREENS_OVERLAP_ERROR), + overlap ? SW_SHOWNA : SW_HIDE); + + // check dst screen + if (!m_config->isCanonicalName(link.m_dstName)) { + m_editedLinkIsValid = false; + ShowWindow(m_dstScreenError, SW_SHOWNA); + } + else { + ShowWindow(m_dstScreenError, SW_HIDE); + } + + // update add link button + enableItem(hwnd, IDC_SCREENS_ADD_LINK, + m_selectedLink == -1 && m_editedLinkIsValid); +} + +void +CScreensLinks::updateLinkView(HWND /*hwnd*/) +{ + // XXX -- draw visual of selected screen, highlighting selected link +} + +HWND +CScreensLinks::createErrorBox(HWND parent) +{ + return CreateWindow(TEXT("STATIC"), TEXT(""), + WS_CHILD | SS_OWNERDRAW, + 0, 0, 1, 1, + parent, (HMENU)-1, + s_instance, NULL); +} + +void +CScreensLinks::resizeErrorBoxes() +{ + HWND hwnd = GetParent(m_srcSideError); + resizeErrorBox(m_srcSideError, getItem(hwnd, IDC_SCREENS_SRC_SIDE)); + resizeErrorBox(m_srcScreenError, getItem(hwnd, IDC_SCREENS_SRC_SCREEN)); + resizeErrorBox(m_dstScreenError, getItem(hwnd, IDC_SCREENS_DST_SCREEN)); +} + +void +CScreensLinks::resizeErrorBox(HWND box, HWND assoc) +{ + RECT rect; + GetWindowRect(assoc, &rect); + MapWindowPoints(NULL, GetParent(box), (POINT*)&rect, 2); + SetWindowPos(box, HWND_TOP, rect.left - 1, rect.top - 1, + rect.right - rect.left + 2, + rect.bottom - rect.top + 2, SWP_NOACTIVATE); +} + +CString +CScreensLinks::formatIntervalValue(float x) const +{ + return CStringUtil::print("%d", (int)(x * 100.0f + 0.5f)); +} + +CString +CScreensLinks::formatInterval(const CConfig::CInterval& i) const +{ + if (i.first == 0.0f && i.second == 1.0f) { + return ""; + } + else { + CString start = formatIntervalValue(i.first); + CString end = formatIntervalValue(i.second); + return CStringUtil::format(m_intervalFormat.c_str(), + start.c_str(), end.c_str()); + } +} + +CString +CScreensLinks::formatLink(const CEdgeLink& link) const +{ + CString srcInterval = formatInterval(link.m_srcInterval); + CString dstInterval = formatInterval(link.m_dstInterval); + return CStringUtil::format(m_linkFormat.c_str(), + link.m_srcName.c_str(), srcInterval.c_str(), + m_sideLabel[link.m_srcSide - kFirstDirection].c_str(), + link.m_dstName.c_str(), dstInterval.c_str()); +} + +// +// CScreensLinks::CEdgeLink +// + +CScreensLinks::CEdgeLink::CEdgeLink() : + m_srcName(), + m_srcSide(kNoDirection), + m_srcInterval(0.0f, 1.0f), + m_dstName(), + m_dstInterval(0.0f, 1.0f) +{ + // do nothing +} + +CScreensLinks::CEdgeLink::CEdgeLink(const CString& name, + const CConfigLink& link) : + m_srcName(name), + m_srcSide(link.first.getSide()), + m_srcInterval(link.first.getInterval()), + m_dstName(link.second.getName()), + m_dstInterval(link.second.getInterval()) +{ + // do nothing +} + +bool +CScreensLinks::CEdgeLink::connect(CConfig* config) +{ + return config->connect(m_srcName, m_srcSide, + m_srcInterval.first, m_srcInterval.second, + m_dstName, + m_dstInterval.first, m_dstInterval.second); +} + +bool +CScreensLinks::CEdgeLink::disconnect(CConfig* config) +{ + return config->disconnect(m_srcName, m_srcSide, 0.5f * + (m_srcInterval.first + m_srcInterval.second)); +} + +void +CScreensLinks::CEdgeLink::rename(const CString& oldName, const CString& newName) +{ + if (m_srcName == oldName) { + m_srcName = newName; + } + if (m_dstName == oldName) { + m_dstName = newName; + } +} + +bool +CScreensLinks::CEdgeLink::overlaps(const CConfig* config) const +{ + return config->hasNeighbor(m_srcName, m_srcSide, + m_srcInterval.first, m_srcInterval.second); +} + +bool +CScreensLinks::CEdgeLink::operator==(const CEdgeLink& x) const +{ + return (m_srcName == x.m_srcName && + m_srcSide == x.m_srcSide && + m_srcInterval == x.m_srcInterval && + m_dstName == x.m_dstName && + m_dstInterval == x.m_dstInterval); +} diff --git a/cmd/launcher/CScreensLinks.h b/cmd/launcher/CScreensLinks.h new file mode 100644 index 00000000..8ffd0089 --- /dev/null +++ b/cmd/launcher/CScreensLinks.h @@ -0,0 +1,138 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2003 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CSCREENSLINKS_H +#define CSCREENSLINKS_H + +#include "CConfig.h" +#include "ProtocolTypes.h" +#include "CString.h" + +#define WINDOWS_LEAN_AND_MEAN +#include + +//! Screens and links dialog for Microsoft Windows launcher +class CScreensLinks { +public: + CScreensLinks(HWND parent, CConfig*); + ~CScreensLinks(); + + //! @name manipulators + //@{ + + //! Run dialog + /*! + Display and handle the dialog until closed by the user. + */ + void doModal(); + + //@} + //! @name accessors + //@{ + + + //@} + +private: + typedef std::pair CConfigLink; + struct CEdgeLink { + public: + CEdgeLink(); + CEdgeLink(const CString& name, const CConfigLink&); + + bool connect(CConfig*); + bool disconnect(CConfig*); + void rename(const CString& oldName, const CString& newName); + + bool overlaps(const CConfig* config) const; + bool operator==(const CEdgeLink&) const; + + public: + CString m_srcName; + EDirection m_srcSide; + CConfig::CInterval m_srcInterval; + CString m_dstName; + CConfig::CInterval m_dstInterval; + }; + typedef std::vector CEdgeLinkList; + + void init(HWND hwnd); + bool save(HWND hwnd); + + CString getSelectedScreen(HWND hwnd) const; + void addScreen(HWND hwnd); + void editScreen(HWND hwnd); + void removeScreen(HWND hwnd); + void addLink(HWND hwnd); + void editLink(HWND hwnd); + void removeLink(HWND hwnd); + + void updateScreens(HWND hwnd, const CString& name); + void updateScreensControls(HWND hwnd); + void updateLinks(HWND hwnd); + void updateLinksControls(HWND hwnd); + + void changeSrcSide(HWND hwnd); + void changeSrcScreen(HWND hwnd); + void changeDstScreen(HWND hwnd); + void changeIntervalStart(HWND hwnd, int id, + CConfig::CInterval&); + void changeIntervalEnd(HWND hwnd, int id, + CConfig::CInterval&); + + void selectScreen(HWND hwnd, int id, const CString& name); + void updateLinkEditControls(HWND hwnd, + const CEdgeLink& link); + void updateLinkIntervalControls(HWND hwnd, + const CEdgeLink& link); + void updateLink(HWND hwnd); + void updateLinkValid(HWND hwnd, const CEdgeLink& link); + + void updateLinkView(HWND hwnd); + + HWND createErrorBox(HWND parent); + void resizeErrorBoxes(); + void resizeErrorBox(HWND box, HWND assoc); + + CString formatIntervalValue(float) const; + CString formatInterval(const CConfig::CInterval&) const; + CString formatLink(const CEdgeLink&) const; + + // message handling + BOOL doDlgProc(HWND, UINT, WPARAM, LPARAM); + static BOOL CALLBACK dlgProc(HWND, UINT, WPARAM, LPARAM); + +private: + static CScreensLinks* s_singleton; + + HWND m_parent; + CConfig* m_mainConfig; + CConfig m_scratchConfig; + CConfig* m_config; + + CString m_linkFormat; + CString m_intervalFormat; + CString m_newLinkLabel; + CString m_sideLabel[kNumDirections]; + CEdgeLinkList m_edgeLinks; + SInt32 m_selectedLink; + CEdgeLink m_editedLink; + bool m_editedLinkIsValid; + HPEN m_redPen; + HWND m_srcSideError; + HWND m_srcScreenError; + HWND m_dstScreenError; +}; + +#endif diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp index 240fd1d9..05b517e7 100644 --- a/cmd/launcher/LaunchUtil.cpp +++ b/cmd/launcher/LaunchUtil.cpp @@ -19,7 +19,7 @@ #include "resource.h" #include "stdfstream.h" -#define CONFIG_NAME "synergy.sgc" +size_t s_showingDialog = 0; CString getString(DWORD id) @@ -37,24 +37,36 @@ void showError(HWND hwnd, const CString& msg) { CString title = getString(IDS_ERROR); + ++s_showingDialog; MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); + --s_showingDialog; } void askOkay(HWND hwnd, const CString& title, const CString& msg) { + ++s_showingDialog; MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OK | MB_APPLMODAL); + --s_showingDialog; } bool askVerify(HWND hwnd, const CString& msg) { CString title = getString(IDS_VERIFY); + ++s_showingDialog; int result = MessageBox(hwnd, msg.c_str(), title.c_str(), MB_OKCANCEL | MB_APPLMODAL); + --s_showingDialog; return (result == IDOK); } +bool +isShowingDialog() +{ + return (s_showingDialog != 0); +} + void setWindowText(HWND hwnd, const CString& msg) { @@ -109,6 +121,39 @@ getAppPath(const CString& appName) return appPath; } +static +void +getFileTime(const CString& path, time_t& t) +{ + struct _stat s; + if (_stat(path.c_str(), &s) != -1) { + t = s.st_mtime; + } +} + +bool +isConfigNewer(time_t& oldTime, bool userConfig) +{ + time_t newTime = oldTime; + if (userConfig) { + CString path = ARCH->getUserDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, CONFIG_NAME); + getFileTime(path, newTime); + } + } + else { + CString path = ARCH->getSystemDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, CONFIG_NAME); + getFileTime(path, newTime); + } + } + bool result = (newTime > oldTime); + oldTime = newTime; + return result; +} + static bool loadConfig(const CString& pathname, CConfig& config) @@ -127,7 +172,7 @@ loadConfig(const CString& pathname, CConfig& config) } bool -loadConfig(CConfig& config) +loadConfig(CConfig& config, time_t& t, bool& userConfig) { // load configuration bool configLoaded = false; @@ -137,6 +182,8 @@ loadConfig(CConfig& config) path = ARCH->concatPath(path, CONFIG_NAME); if (loadConfig(path, config)) { configLoaded = true; + userConfig = true; + getFileTime(path, t); } else { // try the system-wide config file @@ -145,6 +192,8 @@ loadConfig(CConfig& config) path = ARCH->concatPath(path, CONFIG_NAME); if (loadConfig(path, config)) { configLoaded = true; + userConfig = false; + getFileTime(path, t); } } } @@ -170,7 +219,7 @@ saveConfig(const CString& pathname, const CConfig& config) } bool -saveConfig(const CConfig& config, bool sysOnly) +saveConfig(const CConfig& config, bool sysOnly, time_t& t) { // try saving the user's configuration if (!sysOnly) { @@ -178,6 +227,7 @@ saveConfig(const CConfig& config, bool sysOnly) if (!path.empty()) { path = ARCH->concatPath(path, CONFIG_NAME); if (saveConfig(path, config)) { + getFileTime(path, t); return true; } } @@ -189,6 +239,7 @@ saveConfig(const CConfig& config, bool sysOnly) if (!path.empty()) { path = ARCH->concatPath(path, CONFIG_NAME); if (saveConfig(path, config)) { + getFileTime(path, t); return true; } } diff --git a/cmd/launcher/LaunchUtil.h b/cmd/launcher/LaunchUtil.h index 48889264..75954046 100644 --- a/cmd/launcher/LaunchUtil.h +++ b/cmd/launcher/LaunchUtil.h @@ -19,9 +19,12 @@ #define WINDOWS_LEAN_AND_MEAN #include +#include +#include #define CLIENT_APP "synergyc.exe" #define SERVER_APP "synergys.exe" +#define CONFIG_NAME "synergy.sgc" class CConfig; @@ -35,6 +38,7 @@ void showError(HWND hwnd, const CString& msg); void askOkay(HWND hwnd, const CString& title, const CString& msg); bool askVerify(HWND hwnd, const CString& msg); +bool isShowingDialog(); void setWindowText(HWND hwnd, const CString& msg); CString getWindowText(HWND hwnd); @@ -47,8 +51,10 @@ bool isItemChecked(HWND); CString getAppPath(const CString& appName); -bool loadConfig(CConfig& config); -bool saveConfig(const CConfig& config, bool sysOnly); +bool isConfigNewer(time_t&, bool userConfig); +bool loadConfig(CConfig& config, time_t&, bool& userConfig); +bool saveConfig(const CConfig& config, + bool sysOnly, time_t&); const TCHAR* const* getSettingsPath(); diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am index 4998a89a..729aeb79 100644 --- a/cmd/launcher/Makefile.am +++ b/cmd/launcher/Makefile.am @@ -14,14 +14,22 @@ NULL = MSWINDOWS_SOURCE_FILES = \ + CAddScreen.cpp \ CAdvancedOptions.cpp \ CAutoStart.cpp \ CGlobalOptions.cpp \ + CHotkeyOptions.cpp \ + CInfo.cpp \ + CScreensLinks.cpp \ LaunchUtil.cpp \ launcher.cpp \ + CAddScreen.h \ CAdvancedOptions.h \ CAutoStart.h \ CGlobalOptions.h \ + CHotkeyOptions.h \ + CInfo.h \ + CScreensLinks.h \ LaunchUtil.h \ resource.h \ launcher.rc \ diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 66019f01..07eab676 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -20,29 +20,23 @@ #include "CStringUtil.h" #include "CArch.h" #include "CArchMiscWindows.h" +#include "XArch.h" #include "Version.h" #include "stdvector.h" #include "resource.h" // these must come after the above because it includes windows.h #include "LaunchUtil.h" +#include "CAddScreen.h" +#include "CAdvancedOptions.h" #include "CAutoStart.h" #include "CGlobalOptions.h" -#include "CAdvancedOptions.h" - -#define CONFIG_NAME "synergy.sgc" -#define CLIENT_APP "synergyc.exe" -#define SERVER_APP "synergys.exe" +#include "CHotkeyOptions.h" +#include "CInfo.h" +#include "CScreensLinks.h" typedef std::vector CStringList; -class CScreenInfo { -public: - CString m_screen; - CStringList m_aliases; - CConfig::CScreenOptions m_options; -}; - class CChildWaitInfo { public: HWND m_dialog; @@ -52,29 +46,6 @@ public: HANDLE m_stop; }; -struct CModifierInfo { -public: - int m_ctrlID; - const char* m_name; - KeyModifierID m_modifierID; - OptionID m_optionID; -}; - -static const CModifierInfo s_modifiers[] = { - { IDC_ADD_MOD_SHIFT, "Shift", - kKeyModifierIDShift, kOptionModifierMapForShift }, - { IDC_ADD_MOD_CTRL, "Ctrl", - kKeyModifierIDControl, kOptionModifierMapForControl }, - { IDC_ADD_MOD_ALT, "Alt", - kKeyModifierIDAlt, kOptionModifierMapForAlt }, - { IDC_ADD_MOD_META, "Meta", - kKeyModifierIDMeta, kOptionModifierMapForMeta }, - { IDC_ADD_MOD_SUPER, "Super", - kKeyModifierIDSuper, kOptionModifierMapForSuper } -}; - -static const KeyModifierID baseModifier = kKeyModifierIDShift; - static const char* s_debugName[][2] = { { TEXT("Error"), "ERROR" }, { TEXT("Warning"), "WARNING" }, @@ -84,16 +55,30 @@ static const char* s_debugName[][2] = { { TEXT("Debug1"), "DEBUG1" }, { TEXT("Debug2"), "DEBUG2" } }; -static const int s_defaultDebug = 3; // INFO +static const int s_defaultDebug = 1; // WARNING +static const int s_minTestDebug = 3; // INFO HINSTANCE s_instance = NULL; static CGlobalOptions* s_globalOptions = NULL; static CAdvancedOptions* s_advancedOptions = NULL; +static CHotkeyOptions* s_hotkeyOptions = NULL; +static CScreensLinks* s_screensLinks = NULL; +static CInfo* s_info = NULL; + +static bool s_userConfig = true; +static time_t s_configTime = 0; +static CConfig s_lastConfig; static const TCHAR* s_mainClass = TEXT("GoSynergy"); static const TCHAR* s_layoutClass = TEXT("SynergyLayout"); +enum SaveMode { + SAVE_QUITING, + SAVE_NORMAL, + SAVE_QUIET +}; + // // program arguments // @@ -108,7 +93,6 @@ public: public: static CArgs* s_instance; CConfig m_config; - CConfig m_oldConfig; CStringList m_screens; }; @@ -119,40 +103,6 @@ static BOOL CALLBACK addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); -static -void -tokenize(CStringList& tokens, const CString& src) -{ - // find first non-whitespace - CString::size_type x = src.find_first_not_of(" \t\r\n"); - if (x == CString::npos) { - return; - } - - // find next whitespace - do { - CString::size_type y = src.find_first_of(" \t\r\n", x); - if (y == CString::npos) { - y = src.size(); - } - tokens.push_back(src.substr(x, y - x)); - x = src.find_first_not_of(" \t\r\n", y); - } while (x != CString::npos); -} - -static -bool -isNameInList(const CStringList& names, const CString& name) -{ - for (CStringList::const_iterator index = names.begin(); - index != names.end(); ++index) { - if (CStringUtil::CaselessCmp::equal(name, *index)) { - return true; - } - } - return false; -} - static bool isClientChecked(HWND hwnd) @@ -161,44 +111,6 @@ isClientChecked(HWND hwnd) return isItemChecked(child); } -static -void -enableSaveControls(HWND hwnd) -{ - enableItem(hwnd, IDC_MAIN_SAVE, ARG->m_config != ARG->m_oldConfig); -} - -static -void -enableScreensControls(HWND hwnd) -{ - // decide if edit and remove buttons should be enabled - bool client = isClientChecked(hwnd); - bool screenSelected = false; - if (!client) { - HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); - if (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR) { - screenSelected = true; - } - } - - // enable/disable controls - enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LABEL, !client); - enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST, !client); - enableItem(hwnd, IDC_MAIN_SERVER_ADD_BUTTON, !client); - enableItem(hwnd, IDC_MAIN_SERVER_EDIT_BUTTON, screenSelected); - enableItem(hwnd, IDC_MAIN_SERVER_REMOVE_BUTTON, screenSelected); - enableItem(hwnd, IDC_MAIN_SERVER_LAYOUT_LABEL, !client); - enableItem(hwnd, IDC_MAIN_SERVER_LEFT_COMBO, screenSelected); - enableItem(hwnd, IDC_MAIN_SERVER_RIGHT_COMBO, screenSelected); - enableItem(hwnd, IDC_MAIN_SERVER_TOP_COMBO, screenSelected); - enableItem(hwnd, IDC_MAIN_SERVER_BOTTOM_COMBO, screenSelected); - enableItem(hwnd, IDC_MAIN_SERVER_LEFT_LABEL, screenSelected); - enableItem(hwnd, IDC_MAIN_SERVER_RIGHT_LABEL, screenSelected); - enableItem(hwnd, IDC_MAIN_SERVER_TOP_LABEL, screenSelected); - enableItem(hwnd, IDC_MAIN_SERVER_BOTTOM_LABEL, screenSelected); -} - static void enableMainWindowControls(HWND hwnd) @@ -206,248 +118,10 @@ enableMainWindowControls(HWND hwnd) bool client = isClientChecked(hwnd); enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_LABEL, client); enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT, client); - enableScreensControls(hwnd); - enableSaveControls(hwnd); -} - -static -void -updateNeighbor(HWND hwnd, const CString& screen, EDirection direction) -{ - // remove all neighbors from combo box - SendMessage(hwnd, CB_RESETCONTENT, 0, 0); - - // add all screens to combo box - if (!screen.empty()) { - for (CConfig::const_iterator index = ARG->m_config.begin(); - index != ARG->m_config.end(); ++index) { - SendMessage(hwnd, CB_INSERTSTRING, - (WPARAM)-1, (LPARAM)index->c_str()); - } - } - - // add empty neighbor to combo box - SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)TEXT("---")); - - // select neighbor in combo box - LRESULT index = 0; - if (!screen.empty()) { - const CString& neighbor = ARG->m_config.getNeighbor(screen, direction); - if (!neighbor.empty()) { - index = SendMessage(hwnd, CB_FINDSTRINGEXACT, - 0, (LPARAM)neighbor.c_str()); - if (index == LB_ERR) { - index = 0; - } - } - } - SendMessage(hwnd, CB_SETCURSEL, index, 0); -} - -static -void -updateNeighbors(HWND hwnd) -{ - // get selected screen name or empty string if no selection - CString screen; - HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); - LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); - if (index != LB_ERR) { - screen = ARG->m_screens[index]; - } - - // set neighbor combo boxes - child = getItem(hwnd, IDC_MAIN_SERVER_LEFT_COMBO); - updateNeighbor(child, screen, kLeft); - child = getItem(hwnd, IDC_MAIN_SERVER_RIGHT_COMBO); - updateNeighbor(child, screen, kRight); - child = getItem(hwnd, IDC_MAIN_SERVER_TOP_COMBO); - updateNeighbor(child, screen, kTop); - child = getItem(hwnd, IDC_MAIN_SERVER_BOTTOM_COMBO); - updateNeighbor(child, screen, kBottom); -} - -static -void -addScreen(HWND hwnd) -{ - // empty screen info - CScreenInfo info; - - // run dialog - if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), - hwnd, addDlgProc, (LPARAM)&info) != 0) { - // get current number of screens - UInt32 i = ARG->m_screens.size(); - - // add screen to list control - HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); - CString item = CStringUtil::print("%d. %s", - i + 1, info.m_screen.c_str()); - SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); - - // add screen to screen list - ARG->m_screens.push_back(info.m_screen); - - // add screen to config - ARG->m_config.addScreen(info.m_screen); - - // add aliases to config - for (CStringList::const_iterator index = info.m_aliases.begin(); - index != info.m_aliases.end(); ++index) { - ARG->m_config.addAlias(info.m_screen, *index); - } - - // set options - ARG->m_config.removeOptions(info.m_screen); - for (CConfig::CScreenOptions::const_iterator - index = info.m_options.begin(); - index != info.m_options.end(); ++index) { - ARG->m_config.addOption(info.m_screen, index->first, index->second); - } - - // update neighbors - updateNeighbors(hwnd); - enableScreensControls(hwnd); - enableSaveControls(hwnd); - } -} - -static -void -editScreen(HWND hwnd) -{ - // get selected list item - HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); - LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); - if (index == LB_ERR) { - // no selection - return; - } - - // fill in screen info - CScreenInfo info; - info.m_screen = ARG->m_screens[index]; - for (CConfig::all_const_iterator index = ARG->m_config.beginAll(); - index != ARG->m_config.endAll(); ++index) { - if (CStringUtil::CaselessCmp::equal(index->second, info.m_screen) && - !CStringUtil::CaselessCmp::equal(index->second, index->first)) { - info.m_aliases.push_back(index->first); - } - } - const CConfig::CScreenOptions* options = - ARG->m_config.getOptions(info.m_screen); - if (options != NULL) { - info.m_options = *options; - } - - // save current info - CScreenInfo oldInfo = info; - - // run dialog - if (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), - hwnd, addDlgProc, (LPARAM)&info) != 0) { - // replace screen - ARG->m_screens[index] = info.m_screen; - - // remove old aliases - for (CStringList::const_iterator index = oldInfo.m_aliases.begin(); - index != oldInfo.m_aliases.end(); ++index) { - ARG->m_config.removeAlias(*index); - } - - // replace name - ARG->m_config.renameScreen(oldInfo.m_screen, info.m_screen); - - // add new aliases - for (CStringList::const_iterator index = info.m_aliases.begin(); - index != info.m_aliases.end(); ++index) { - ARG->m_config.addAlias(info.m_screen, *index); - } - - // set options - ARG->m_config.removeOptions(info.m_screen); - for (CConfig::CScreenOptions::const_iterator - index = info.m_options.begin(); - index != info.m_options.end(); ++index) { - ARG->m_config.addOption(info.m_screen, index->first, index->second); - } - - // update list - CString item = CStringUtil::print("%d. %s", - index + 1, info.m_screen.c_str()); - SendMessage(child, LB_DELETESTRING, index, 0); - SendMessage(child, LB_INSERTSTRING, index, - (LPARAM)item.c_str()); - SendMessage(child, LB_SETCURSEL, index, 0); - - // update neighbors - updateNeighbors(hwnd); - enableSaveControls(hwnd); - } -} - -static -void -removeScreen(HWND hwnd) -{ - // get selected list item - HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); - LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); - if (index == LB_ERR) { - // no selection - return; - } - - // get screen name - CString name = ARG->m_screens[index]; - - // remove screen from list control - SendMessage(child, LB_DELETESTRING, index, 0); - - // remove screen from screen list - ARG->m_screens.erase(&ARG->m_screens[index]); - - // remove screen from config (this also removes aliases) - ARG->m_config.removeScreen(name); - - // update neighbors - updateNeighbors(hwnd); - enableScreensControls(hwnd); - enableSaveControls(hwnd); -} - -static -void -changeNeighbor(HWND hwnd, HWND combo, EDirection direction) -{ - // get selected screen - HWND child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); - LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0); - if (index == LB_ERR) { - // no selection - return; - } - - // get screen name - CString screen = ARG->m_screens[index]; - - // get selected neighbor - index = SendMessage(combo, CB_GETCURSEL, 0, 0); - - // remove old connection - ARG->m_config.disconnect(screen, direction); - - // add new connection - if (index != LB_ERR && index != 0) { - LRESULT size = SendMessage(combo, CB_GETLBTEXTLEN, index, 0); - char* neighbor = new char[size + 1]; - SendMessage(combo, CB_GETLBTEXT, index, (LPARAM)neighbor); - ARG->m_config.connect(screen, direction, CString(neighbor)); - delete[] neighbor; - } - - enableSaveControls(hwnd); + enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LABEL, !client); + enableItem(hwnd, IDC_MAIN_SCREENS, !client); + enableItem(hwnd, IDC_MAIN_OPTIONS, !client); + enableItem(hwnd, IDC_MAIN_HOTKEYS, !client); } static @@ -505,7 +179,7 @@ execApp(const char* app, const CString& cmdLine, PROCESS_INFORMATION* procInfo) static CString -getCommandLine(HWND hwnd, bool testing) +getCommandLine(HWND hwnd, bool testing, bool silent) { CString cmdLine; @@ -527,9 +201,11 @@ getCommandLine(HWND hwnd, bool testing) HWND child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); server = getWindowText(child); if (!ARG->m_config.isValidScreenName(server)) { - showError(hwnd, CStringUtil::format( + if (!silent) { + showError(hwnd, CStringUtil::format( getString(IDS_INVALID_SERVER_NAME).c_str(), server.c_str())); + } SetFocus(child); return CString(); } @@ -539,9 +215,11 @@ getCommandLine(HWND hwnd, bool testing) // don't bother to check the addresses though that'd be // more accurate. if (CStringUtil::CaselessCmp::equal(ARCH->getHostName(), server)) { - showError(hwnd, CStringUtil::format( + if (!silent) { + showError(hwnd, CStringUtil::format( getString(IDS_SERVER_IS_CLIENT).c_str(), server.c_str())); + } SetFocus(child); return CString(); } @@ -549,8 +227,16 @@ getCommandLine(HWND hwnd, bool testing) // debug level. always include this. if (true) { - HWND child = getItem(hwnd, IDC_MAIN_DEBUG); - DWORD debug = SendMessage(child, CB_GETCURSEL, 0, 0); + HWND child = getItem(hwnd, IDC_MAIN_DEBUG); + int debug = (int)SendMessage(child, CB_GETCURSEL, 0, 0); + + // if testing then we force the debug level to be no less than + // s_minTestDebug. what's the point of testing if you can't + // see the debugging info? + if (testing && debug < s_minTestDebug) { + debug = s_minTestDebug; + } + cmdLine += " --debug "; cmdLine += s_debugName[debug][1]; } @@ -562,17 +248,29 @@ getCommandLine(HWND hwnd, bool testing) } static -HANDLE -launchApp(HWND hwnd, bool testing, DWORD* threadID) +bool +launchApp(HWND hwnd, bool testing, HANDLE* thread, DWORD* threadID) { + if (thread != NULL) { + *thread = NULL; + } + if (threadID != NULL) { + *threadID = 0; + } + + // start daemon if it's installed and we're not testing + if (!testing && CAutoStart::startDaemon()) { + return true; + } + // decide if client or server const bool isClient = isClientChecked(hwnd); const char* app = isClient ? CLIENT_APP : SERVER_APP; // prepare command line - CString cmdLine = getCommandLine(hwnd, testing); + CString cmdLine = getCommandLine(hwnd, testing, false); if (cmdLine.empty()) { - return NULL; + return false; } // start child @@ -581,19 +279,21 @@ launchApp(HWND hwnd, bool testing, DWORD* threadID) showError(hwnd, CStringUtil::format( getString(IDS_STARTUP_FAILED).c_str(), getErrorString(GetLastError()).c_str())); - return NULL; + return false; } // don't need process handle CloseHandle(procInfo.hProcess); - // save thread ID if desired + // save thread handle and thread ID if desired + if (thread != NULL) { + *thread = procInfo.hThread; + } if (threadID != NULL) { *threadID = procInfo.dwThreadId; } - // return thread handle - return procInfo.hThread; + return true; } static @@ -701,9 +401,11 @@ initMainWindow(HWND hwnd) setWindowText(hwnd, CStringUtil::format(titleFormat.c_str(), VERSION)); // load configuration - bool configLoaded = loadConfig(ARG->m_config); - ARG->m_oldConfig = ARG->m_config; - enableSaveControls(hwnd); + bool configLoaded = + loadConfig(ARG->m_config, s_configTime, s_userConfig); + if (configLoaded) { + s_lastConfig = ARG->m_config; + } // get settings from registry bool isServer = configLoaded; @@ -739,18 +441,6 @@ initMainWindow(HWND hwnd) child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); setWindowText(child, server); - // if config is loaded then initialize server controls - if (configLoaded) { - int i = 1; - child = getItem(hwnd, IDC_MAIN_SERVER_SCREENS_LIST); - for (CConfig::const_iterator index = ARG->m_config.begin(); - index != ARG->m_config.end(); ++i, ++index) { - ARG->m_screens.push_back(*index); - CString item = CStringUtil::print("%d. %s", i, index->c_str()); - SendMessage(child, LB_ADDSTRING, 0, (LPARAM)item.c_str()); - } - } - // debug level child = getItem(hwnd, IDC_MAIN_DEBUG); for (unsigned int i = 0; i < sizeof(s_debugName) / @@ -759,16 +449,21 @@ initMainWindow(HWND hwnd) } SendMessage(child, CB_SETCURSEL, debugLevel, 0); - // update neighbor combo boxes + // update controls enableMainWindowControls(hwnd); - updateNeighbors(hwnd); } static -void -saveMainWindow(HWND hwnd) +bool +saveMainWindow(HWND hwnd, SaveMode mode, CString* cmdLineOut = NULL) { - HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath()); + DWORD errorID = 0; + CString arg; + CString cmdLine; + + // save dialog state + bool isClient = isClientChecked(hwnd); + HKEY key = CArchMiscWindows::addKey(HKEY_CURRENT_USER, getSettingsPath()); if (key != NULL) { HWND child; child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT); @@ -776,214 +471,78 @@ saveMainWindow(HWND hwnd) child = getItem(hwnd, IDC_MAIN_DEBUG); CArchMiscWindows::setValue(key, "debug", SendMessage(child, CB_GETCURSEL, 0, 0)); - CArchMiscWindows::setValue(key, "isServer", - isClientChecked(hwnd) ? 0 : 1); + CArchMiscWindows::setValue(key, "isServer", isClient ? 0 : 1); CArchMiscWindows::closeKey(key); } -} -static -BOOL CALLBACK -addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - // only one add dialog at a time! - static CScreenInfo* info = NULL; - - switch (message) { - case WM_INITDIALOG: { - info = (CScreenInfo*)lParam; - - // set title - CString title; - if (info->m_screen.empty()) { - title = getString(IDS_ADD_SCREEN); + // save user's configuration + if (!s_userConfig || ARG->m_config != s_lastConfig) { + time_t t; + if (!saveConfig(ARG->m_config, false, t)) { + errorID = IDS_SAVE_FAILED; + arg = getErrorString(GetLastError()); + goto failed; } - else { - title = CStringUtil::format( - getString(IDS_EDIT_SCREEN).c_str(), - info->m_screen.c_str()); + if (s_userConfig) { + s_configTime = t; + s_lastConfig = ARG->m_config; } - SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)title.c_str()); - - // fill in screen name - HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); - SendMessage(child, WM_SETTEXT, 0, (LPARAM)info->m_screen.c_str()); - - // fill in aliases - CString aliases; - for (CStringList::const_iterator index = info->m_aliases.begin(); - index != info->m_aliases.end(); ++index) { - if (!aliases.empty()) { - aliases += "\r\n"; - } - aliases += *index; - } - child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); - SendMessage(child, WM_SETTEXT, 0, (LPARAM)aliases.c_str()); - - // set options - CConfig::CScreenOptions::const_iterator index; - child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); - index = info->m_options.find(kOptionHalfDuplexCapsLock); - setItemChecked(child, (index != info->m_options.end() && - index->second != 0)); - child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); - index = info->m_options.find(kOptionHalfDuplexNumLock); - setItemChecked(child, (index != info->m_options.end() && - index->second != 0)); - child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK); - index = info->m_options.find(kOptionHalfDuplexScrollLock); - setItemChecked(child, (index != info->m_options.end() && - index->second != 0)); - - // modifier options - for (UInt32 i = 0; i < sizeof(s_modifiers) / - sizeof(s_modifiers[0]); ++i) { - child = getItem(hwnd, s_modifiers[i].m_ctrlID); - - // fill in options - for (UInt32 j = 0; j < sizeof(s_modifiers) / - sizeof(s_modifiers[0]); ++j) { - SendMessage(child, CB_ADDSTRING, 0, - (LPARAM)s_modifiers[j].m_name); - } - - // choose current value - index = info->m_options.find(s_modifiers[i].m_optionID); - KeyModifierID id = s_modifiers[i].m_modifierID; - if (index != info->m_options.end()) { - id = index->second; - } - SendMessage(child, CB_SETCURSEL, id - baseModifier, 0); - } - - return TRUE; } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: { - CString newName; - CStringList newAliases; - - // extract name and aliases - HWND child = getItem(hwnd, IDC_ADD_SCREEN_NAME_EDIT); - newName = getWindowText(child); - child = getItem(hwnd, IDC_ADD_ALIASES_EDIT); - tokenize(newAliases, getWindowText(child)); - - // name must be valid - if (!ARG->m_config.isValidScreenName(newName)) { - showError(hwnd, CStringUtil::format( - getString(IDS_INVALID_SCREEN_NAME).c_str(), - newName.c_str())); - return TRUE; + // save autostart configuration + if (CAutoStart::isDaemonInstalled()) { + if (s_userConfig || ARG->m_config != s_lastConfig) { + time_t t; + if (!saveConfig(ARG->m_config, true, t)) { + errorID = IDS_AUTOSTART_SAVE_FAILED; + arg = getErrorString(GetLastError()); + goto failed; } - - // aliases must be valid - for (CStringList::const_iterator index = newAliases.begin(); - index != newAliases.end(); ++index) { - if (!ARG->m_config.isValidScreenName(*index)) { - showError(hwnd, CStringUtil::format( - getString(IDS_INVALID_SCREEN_NAME).c_str(), - index->c_str())); - return TRUE; - } + if (!s_userConfig) { + s_configTime = t; + s_lastConfig = ARG->m_config; } - - // new name may not be in the new alias list - if (isNameInList(newAliases, newName)) { - showError(hwnd, CStringUtil::format( - getString(IDS_SCREEN_NAME_IS_ALIAS).c_str(), - newName.c_str())); - return TRUE; - } - - // name must not exist in config but allow same name. also - // allow name if it exists in the old alias list but not the - // new one. - if (ARG->m_config.isScreen(newName) && - !CStringUtil::CaselessCmp::equal(newName, info->m_screen) && - !isNameInList(info->m_aliases, newName)) { - showError(hwnd, CStringUtil::format( - getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), - newName.c_str())); - return TRUE; - } - - // aliases must not exist in config but allow same aliases and - // allow an alias to be the old name. - for (CStringList::const_iterator index = newAliases.begin(); - index != newAliases.end(); ++index) { - if (ARG->m_config.isScreen(*index) && - !CStringUtil::CaselessCmp::equal(*index, info->m_screen) && - !isNameInList(info->m_aliases, *index)) { - showError(hwnd, CStringUtil::format( - getString(IDS_DUPLICATE_SCREEN_NAME).c_str(), - index->c_str())); - return TRUE; - } - } - - // save name data - info->m_screen = newName; - info->m_aliases = newAliases; - - // save options - child = getItem(hwnd, IDC_ADD_HD_CAPS_CHECK); - if (isItemChecked(child)) { - info->m_options[kOptionHalfDuplexCapsLock] = 1; - } - else { - info->m_options.erase(kOptionHalfDuplexCapsLock); - } - child = getItem(hwnd, IDC_ADD_HD_NUM_CHECK); - if (isItemChecked(child)) { - info->m_options[kOptionHalfDuplexNumLock] = 1; - } - else { - info->m_options.erase(kOptionHalfDuplexNumLock); - } - child = getItem(hwnd, IDC_ADD_HD_SCROLL_CHECK); - if (isItemChecked(child)) { - info->m_options[kOptionHalfDuplexScrollLock] = 1; - } - else { - info->m_options.erase(kOptionHalfDuplexScrollLock); - } - - // save modifier options - for (UInt32 i = 0; i < sizeof(s_modifiers) / - sizeof(s_modifiers[0]); ++i) { - child = getItem(hwnd, s_modifiers[i].m_ctrlID); - KeyModifierID id = static_cast( - SendMessage(child, CB_GETCURSEL, 0, 0) + - baseModifier); - if (id != s_modifiers[i].m_modifierID) { - info->m_options[s_modifiers[i].m_optionID] = id; - } - else { - info->m_options.erase(s_modifiers[i].m_optionID); - } - } - - // success - EndDialog(hwnd, 1); - info = NULL; - return TRUE; } - - case IDCANCEL: - EndDialog(hwnd, 0); - info = NULL; - return TRUE; - } - - default: - break; } - return FALSE; + // get autostart command + cmdLine = getCommandLine(hwnd, false, mode == SAVE_QUITING); + if (cmdLineOut != NULL) { + *cmdLineOut = cmdLine; + } + if (cmdLine.empty()) { + return (mode == SAVE_QUITING); + } + + // save autostart command + if (CAutoStart::isDaemonInstalled()) { + try { + CAutoStart::reinstallDaemon(isClient, cmdLine); + CAutoStart::uninstallDaemons(!isClient); + } + catch (XArchDaemon& e) { + errorID = IDS_INSTALL_GENERIC_ERROR; + arg = e.what(); + goto failed; + } + } + + return true; + +failed: + CString errorMessage = + CStringUtil::format(getString(errorID).c_str(), arg.c_str()); + if (mode == SAVE_QUITING) { + errorMessage += "\n"; + errorMessage += getString(IDS_UNSAVED_DATA_REALLY_QUIT); + if (askVerify(hwnd, errorMessage)) { + return true; + } + } + else if (mode == SAVE_NORMAL) { + showError(hwnd, errorMessage); + } + return false; } static @@ -991,18 +550,46 @@ LRESULT CALLBACK mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { + case WM_ACTIVATE: + if (LOWORD(wParam) != WA_INACTIVE) { + // activated + + // see if the configuration changed + if (isConfigNewer(s_configTime, s_userConfig)) { + CString message = getString(IDS_CONFIG_CHANGED); + if (askVerify(hwnd, message)) { + time_t configTime; + bool userConfig; + CConfig newConfig; + if (loadConfig(newConfig, configTime, userConfig) && + userConfig == s_userConfig) { + ARG->m_config = newConfig; + s_lastConfig = ARG->m_config; + } + else { + message = getString(IDS_LOAD_FAILED); + showError(hwnd, message); + s_lastConfig = CConfig(); + } + } + } + } + else { + // deactivated; write configuration + if (!isShowingDialog()) { + saveMainWindow(hwnd, SAVE_QUIET); + } + } + break; + case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: - // test for unsaved data - if (ARG->m_config != ARG->m_oldConfig) { - if (!askVerify(hwnd, getString(IDS_UNSAVED_DATA_REALLY_QUIT))) { - return 0; - } + // save data + if (saveMainWindow(hwnd, SAVE_QUITING)) { + // quit + PostQuitMessage(0); } - - // quit - PostQuitMessage(0); return 0; case IDOK: @@ -1011,139 +598,72 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) const bool testing = (LOWORD(wParam) == IDC_MAIN_TEST); // save data - if (ARG->m_config != ARG->m_oldConfig) { - if (!saveConfig(ARG->m_config, false)) { - showError(hwnd, CStringUtil::format( - getString(IDS_SAVE_FAILED).c_str(), - getErrorString(GetLastError()).c_str())); + if (saveMainWindow(hwnd, SAVE_NORMAL)) { + // launch child app + DWORD threadID; + HANDLE thread; + if (!launchApp(hwnd, testing, &thread, &threadID)) { return 0; } - ARG->m_oldConfig = ARG->m_config; - enableSaveControls(hwnd); - } - // launch child app - DWORD threadID; - HANDLE thread = launchApp(hwnd, testing, &threadID); - if (thread == NULL) { - return 0; - } + // handle child program + if (testing) { + // wait for process to stop, allowing the user to kill it + waitForChild(hwnd, thread, threadID); - // handle child program - if (testing) { - // wait for process to stop, allowing the user to kill it - waitForChild(hwnd, thread, threadID); + // clean up + CloseHandle(thread); + } + else { + // don't need thread handle + if (thread != NULL) { + CloseHandle(thread); + } - // clean up - CloseHandle(thread); - } - else { - // don't need thread handle - CloseHandle(thread); + // notify of success + askOkay(hwnd, getString(IDS_STARTED_TITLE), + getString(IDS_STARTED)); - // notify of success - askOkay(hwnd, getString(IDS_STARTED_TITLE), - getString(IDS_STARTED)); - - // quit - PostQuitMessage(0); + // quit + PostQuitMessage(0); + } } return 0; } case IDC_MAIN_AUTOSTART: { - // construct command line - CString cmdLine = getCommandLine(hwnd, false); - if (!cmdLine.empty()) { + CString cmdLine; + if (saveMainWindow(hwnd, SAVE_NORMAL, &cmdLine)) { // run dialog - CAutoStart autoStart(hwnd, - isClientChecked(hwnd) ? NULL : &ARG->m_config, - cmdLine); + CAutoStart autoStart(hwnd, !isClientChecked(hwnd), cmdLine); autoStart.doModal(); - if (autoStart.wasUserConfigSaved()) { - ARG->m_oldConfig = ARG->m_config; - enableSaveControls(hwnd); - } } return 0; } - case IDC_MAIN_SAVE: - if (!saveConfig(ARG->m_config, false)) { - showError(hwnd, CStringUtil::format( - getString(IDS_SAVE_FAILED).c_str(), - getErrorString(GetLastError()).c_str())); - } - else { - ARG->m_oldConfig = ARG->m_config; - enableSaveControls(hwnd); - } - return 0; - case IDC_MAIN_CLIENT_RADIO: case IDC_MAIN_SERVER_RADIO: enableMainWindowControls(hwnd); return 0; - case IDC_MAIN_SERVER_ADD_BUTTON: - addScreen(hwnd); - return 0; - - case IDC_MAIN_SERVER_EDIT_BUTTON: - editScreen(hwnd); - return 0; - - case IDC_MAIN_SERVER_REMOVE_BUTTON: - removeScreen(hwnd); - return 0; - - case IDC_MAIN_SERVER_SCREENS_LIST: - if (HIWORD(wParam) == LBN_SELCHANGE) { - enableScreensControls(hwnd); - updateNeighbors(hwnd); - } - else if (HIWORD(wParam) == LBN_DBLCLK) { - editScreen(hwnd); - return 0; - } - break; - - case IDC_MAIN_SERVER_LEFT_COMBO: - if (HIWORD(wParam) == CBN_SELENDOK) { - changeNeighbor(hwnd, (HWND)lParam, kLeft); - return 0; - } - break; - - case IDC_MAIN_SERVER_RIGHT_COMBO: - if (HIWORD(wParam) == CBN_SELENDOK) { - changeNeighbor(hwnd, (HWND)lParam, kRight); - return 0; - } - break; - - case IDC_MAIN_SERVER_TOP_COMBO: - if (HIWORD(wParam) == CBN_SELENDOK) { - changeNeighbor(hwnd, (HWND)lParam, kTop); - return 0; - } - break; - - case IDC_MAIN_SERVER_BOTTOM_COMBO: - if (HIWORD(wParam) == CBN_SELENDOK) { - changeNeighbor(hwnd, (HWND)lParam, kBottom); - return 0; - } + case IDC_MAIN_SCREENS: + s_screensLinks->doModal(); break; case IDC_MAIN_OPTIONS: s_globalOptions->doModal(); - enableSaveControls(hwnd); break; case IDC_MAIN_ADVANCED: s_advancedOptions->doModal(isClientChecked(hwnd)); - enableSaveControls(hwnd); + break; + + case IDC_MAIN_HOTKEYS: + s_hotkeyOptions->doModal(); + break; + + case IDC_MAIN_INFO: + s_info->doModal(); break; } @@ -1154,7 +674,7 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } int WINAPI -WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) +WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmdLine, int nCmdShow) { CArch arch(instance); CLOG; @@ -1162,6 +682,15 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) s_instance = instance; + // if "/uninstall" is on the command line then just stop and + // uninstall the service and quit. this is the only option + // but we ignore any others. + if (CString(cmdLine).find("/uninstall") != CString::npos) { + CAutoStart::uninstallDaemons(false); + CAutoStart::uninstallDaemons(true); + return 0; + } + // register main window (dialog) class WNDCLASSEX classInfo; classInfo.cbSize = sizeof(classInfo); @@ -1190,8 +719,11 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) // prep windows initMainWindow(mainWindow); - s_globalOptions = new CGlobalOptions(mainWindow, &ARG->m_config); + s_globalOptions = new CGlobalOptions(mainWindow, &ARG->m_config); s_advancedOptions = new CAdvancedOptions(mainWindow, &ARG->m_config); + s_hotkeyOptions = new CHotkeyOptions(mainWindow, &ARG->m_config); + s_screensLinks = new CScreensLinks(mainWindow, &ARG->m_config); + s_info = new CInfo(mainWindow); // show window ShowWindow(mainWindow, nCmdShow); @@ -1219,8 +751,5 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int nCmdShow) } } while (!done); - // save values to registry - saveMainWindow(mainWindow); - return msg.wParam; } diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp index b1ce64d4..f799afc9 100644 --- a/cmd/launcher/launcher.dsp +++ b/cmd/launcher/launcher.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\launcher.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\launcher.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -71,7 +71,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\launcher.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\launcher.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -95,6 +95,10 @@ LINK32=link.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CAddScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\CAdvancedOptions.cpp # End Source File # Begin Source File @@ -107,6 +111,18 @@ SOURCE=.\CGlobalOptions.cpp # End Source File # Begin Source File +SOURCE=.\CHotkeyOptions.cpp +# End Source File +# Begin Source File + +SOURCE=.\CInfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\CScreensLinks.cpp +# End Source File +# Begin Source File + SOURCE=.\launcher.cpp # End Source File # Begin Source File @@ -123,6 +139,10 @@ SOURCE=.\LaunchUtil.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CAddScreen.h +# End Source File +# Begin Source File + SOURCE=.\CAdvancedOptions.h # End Source File # Begin Source File @@ -135,6 +155,18 @@ SOURCE=.\CGlobalOptions.h # End Source File # Begin Source File +SOURCE=.\CHotkeyOptions.h +# End Source File +# Begin Source File + +SOURCE=.\CInfo.h +# End Source File +# Begin Source File + +SOURCE=.\CScreensLinks.h +# End Source File +# Begin Source File + SOURCE=.\LaunchUtil.h # End Source File # Begin Source File diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 53f15770..802f3d3e 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -7,10 +7,7 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "winres.h" -#if !defined(IDC_STATIC) -#define IDC_STATIC (-1) -#endif +#include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS @@ -55,55 +52,41 @@ END // Dialog // -IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 262 +IDD_MAIN DIALOG DISCARDABLE 32768, 0, 300, 199 STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU CAPTION "Synergy" CLASS "GoSynergy" FONT 8, "MS Sans Serif" BEGIN - LTEXT "Choose to start either the Client or Server and provide the requested information. Then click Test to check your settings or Start to save your settings and start Synergy.", + LTEXT "Choose to share or use a shared keyboard and mouse, provide the requested information, then click Test to check your settings or Start to save your settings and start Synergy.", IDC_STATIC,7,7,286,19 - GROUPBOX "",IDC_STATIC,7,29,286,31 - GROUPBOX "",IDC_STATIC,7,67,286,103 - GROUPBOX "Options",IDC_STATIC,7,177,286,56 - CONTROL "&Client",IDC_MAIN_CLIENT_RADIO,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,11,29,33,10 - CONTROL "Server",IDC_MAIN_SERVER_RADIO,"Button", - BS_AUTORADIOBUTTON,11,67,37,10 - LTEXT "Server &Host Name:",IDC_MAIN_CLIENT_SERVER_NAME_LABEL, - 12,41,61,8 - EDITTEXT IDC_MAIN_CLIENT_SERVER_NAME_EDIT,79,39,106,12, + GROUPBOX "",IDC_STATIC,7,29,286,36 + GROUPBOX "",IDC_STATIC,7,72,286,36 + GROUPBOX "Options",IDC_STATIC,7,115,286,56 + CONTROL "&Use another computer's shared keyboard and mouse (client)", + IDC_MAIN_CLIENT_RADIO,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,11,29,205,10 + CONTROL "Share this computer's keyboard and mouse (server)", + IDC_MAIN_SERVER_RADIO,"Button",BS_AUTORADIOBUTTON,11,72, + 177,10 + LTEXT "Other Computer's &Host Name:", + IDC_MAIN_CLIENT_SERVER_NAME_LABEL,12,46,94,8 + EDITTEXT IDC_MAIN_CLIENT_SERVER_NAME_EDIT,111,44,106,12, ES_AUTOHSCROLL - LTEXT "&Screens:",IDC_MAIN_SERVER_SCREENS_LABEL,12,79,29,8 - LISTBOX IDC_MAIN_SERVER_SCREENS_LIST,12,91,106,36, - LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "&Add",IDC_MAIN_SERVER_ADD_BUTTON,12,132,50,14 - PUSHBUTTON "&Edit",IDC_MAIN_SERVER_EDIT_BUTTON,68,132,50,14 - PUSHBUTTON "&Remove",IDC_MAIN_SERVER_REMOVE_BUTTON,12,150,50,14 - LTEXT "&Layout:",IDC_MAIN_SERVER_LAYOUT_LABEL,138,79,24,8 - LTEXT "Left:",IDC_MAIN_SERVER_LEFT_LABEL,144,93,15,8 - COMBOBOX IDC_MAIN_SERVER_LEFT_COMBO,175,91,111,75, - CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Right:",IDC_MAIN_SERVER_RIGHT_LABEL,144,109,20,8 - COMBOBOX IDC_MAIN_SERVER_RIGHT_COMBO,175,107,112,75, - CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Above:",IDC_MAIN_SERVER_TOP_LABEL,144,125,24,8 - COMBOBOX IDC_MAIN_SERVER_TOP_COMBO,175,123,112,75, - CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Below:",IDC_MAIN_SERVER_BOTTOM_LABEL,144,141,22,8 - COMBOBOX IDC_MAIN_SERVER_BOTTOM_COMBO,175,139,112,75, - CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "&Options...",IDC_MAIN_OPTIONS,12,191,50,14 - PUSHBUTTON "Adva&nced...",IDC_MAIN_ADVANCED,68,191,50,14 - LTEXT "Automatic Startup:",IDC_STATIC,138,193,59,8 - PUSHBUTTON "Con&figure...",IDC_MAIN_AUTOSTART,202,191,50,14 - LTEXT "Lo&gging Level:",IDC_STATIC,12,216,48,8 - COMBOBOX IDC_MAIN_DEBUG,68,213,61,60,CBS_DROPDOWNLIST | + LTEXT "&Screens && Links:",IDC_MAIN_SERVER_SCREENS_LABEL,12,89, + 54,8 + PUSHBUTTON "Configure...",IDC_MAIN_SCREENS,71,86,50,14 + PUSHBUTTON "&Options...",IDC_MAIN_OPTIONS,12,129,50,14 + PUSHBUTTON "Hot &Keys...",IDC_MAIN_HOTKEYS,68,129,50,14 + PUSHBUTTON "Adva&nced...",IDC_MAIN_ADVANCED,124,129,50,14 + PUSHBUTTON "&AutoStart...",IDC_MAIN_AUTOSTART,180,129,50,14 + LTEXT "&Logging Level:",IDC_STATIC,12,154,48,8 + COMBOBOX IDC_MAIN_DEBUG,68,151,61,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Sa&ve",IDC_MAIN_SAVE,75,241,50,14 - DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,241,50,14 - PUSHBUTTON "Start",IDOK,187,241,50,14 - PUSHBUTTON "Quit",IDCANCEL,243,241,50,14 + PUSHBUTTON "&Info",IDC_MAIN_INFO,7,178,50,14 + DEFPUSHBUTTON "&Test",IDC_MAIN_TEST,131,179,50,14 + PUSHBUTTON "Start",IDOK,187,179,50,14 + PUSHBUTTON "Quit",IDCANCEL,243,179,50,14 END IDD_ADD DIALOG DISCARDABLE 0, 0, 192, 254 @@ -114,33 +97,46 @@ BEGIN LTEXT "&Screen Name:",IDC_STATIC,7,9,46,8 EDITTEXT IDC_ADD_SCREEN_NAME_EDIT,79,7,106,12,ES_AUTOHSCROLL LTEXT "&Aliases:",IDC_STATIC,7,25,25,8 - EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,40,ES_MULTILINE | + EDITTEXT IDC_ADD_ALIASES_EDIT,79,26,106,24,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN - GROUPBOX "Options",IDC_STATIC,7,72,178,80 + GROUPBOX "Options",IDC_STATIC,7,55,178,54 LTEXT "If your Caps, Num, or Scroll Lock keys behave strangely on this client screen then try turning the half-duplex options on and reconnect the client.", - IDC_STATIC,13,82,165,25 - CONTROL "Half-duplex &Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,13,110,165,10 - CONTROL "Half-duplex &Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,13,122,165,10 - CONTROL "Half-duplex Sc&roll Lock",IDC_ADD_HD_SCROLL_CHECK, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,134,165,10 - GROUPBOX "Modifiers",IDC_STATIC,7,155,178,65 - LTEXT "Shift",IDC_STATIC,13,171,15,8 - COMBOBOX IDC_ADD_MOD_SHIFT,37,168,48,60,CBS_DROPDOWNLIST | + IDC_STATIC,13,65,165,25 + CONTROL "&Caps Lock",IDC_ADD_HD_CAPS_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,93,51,10 + CONTROL "&Num Lock",IDC_ADD_HD_NUM_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,69,93,51,10 + CONTROL "Sc&roll Lock",IDC_ADD_HD_SCROLL_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,125,93,51,10 + GROUPBOX "Modifiers",IDC_STATIC,7,113,178,65 + LTEXT "Shift",IDC_STATIC,13,129,15,8 + COMBOBOX IDC_ADD_MOD_SHIFT,37,126,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Ctrl",IDC_STATIC,13,186,11,8 - COMBOBOX IDC_ADD_MOD_CTRL,37,184,48,60,CBS_DROPDOWNLIST | + LTEXT "Ctrl",IDC_STATIC,13,144,11,8 + COMBOBOX IDC_ADD_MOD_CTRL,37,142,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Alt",IDC_STATIC,13,202,9,8 - COMBOBOX IDC_ADD_MOD_ALT,37,200,48,60,CBS_DROPDOWNLIST | + LTEXT "Alt",IDC_STATIC,13,160,9,8 + COMBOBOX IDC_ADD_MOD_ALT,37,158,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Meta",IDC_STATIC,101,170,17,8 - COMBOBOX IDC_ADD_MOD_META,125,168,48,60,CBS_DROPDOWNLIST | + LTEXT "Meta",IDC_STATIC,101,128,17,8 + COMBOBOX IDC_ADD_MOD_META,125,126,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Super",IDC_STATIC,101,186,20,8 - COMBOBOX IDC_ADD_MOD_SUPER,125,184,48,60,CBS_DROPDOWNLIST | + LTEXT "Super",IDC_STATIC,101,144,20,8 + COMBOBOX IDC_ADD_MOD_SUPER,125,142,48,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Dead Corners",IDC_STATIC,7,183,178,43 + LTEXT "Don't switch in these corners:",IDC_STATIC,14,198,52,18 + CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME,68,193,47,28 + CONTROL "",IDC_ADD_DC_TOP_LEFT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,76,197,16,8 + CONTROL "",IDC_ADD_DC_TOP_RIGHT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,98,197,16,8 + CONTROL "",IDC_ADD_DC_BOTTOM_LEFT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,76,210,16,8 + CONTROL "",IDC_ADD_DC_BOTTOM_RIGHT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,98,210,16,8 + LTEXT "Size",IDC_STATIC,120,202,14,8 + EDITTEXT IDC_ADD_DC_SIZE,139,200,40,12,ES_AUTOHSCROLL | ES_NUMBER DEFPUSHBUTTON "OK",IDOK,79,233,50,14 PUSHBUTTON "Cancel",IDCANCEL,135,233,50,14 END @@ -175,7 +171,7 @@ BEGIN IDC_STATIC,7,43,181,17 END -IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 269 +IDD_GLOBAL_OPTIONS DIALOG DISCARDABLE 0, 0, 207, 290 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Options" FONT 8, "MS Sans Serif" @@ -207,12 +203,15 @@ BEGIN IDC_STATIC,7,176,193,8 CONTROL "Synchronize screen savers",IDC_GLOBAL_SCREENSAVER_SYNC, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,192,101,10 - LTEXT "Experimental: Relative mouse moves on secondary screens.", - IDC_STATIC,7,213,193,8 + LTEXT "Relative mouse moves on secondary screens.",IDC_STATIC, + 7,213,193,8 CONTROL "Use relative mouse moves",IDC_GLOBAL_RELATIVE_MOVES, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,229,99,10 - DEFPUSHBUTTON "OK",IDOK,94,248,50,14 - PUSHBUTTON "Cancel",IDCANCEL,150,248,50,14 + CONTROL "Don't take foreground window on Windows servers", + IDC_GLOBAL_LEAVE_FOREGROUND,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,250,177,10 + DEFPUSHBUTTON "OK",IDOK,94,269,50,14 + PUSHBUTTON "Cancel",IDCANCEL,150,269,50,14 END IDD_ADVANCED_OPTIONS DIALOG DISCARDABLE 0, 0, 230, 186 @@ -238,6 +237,158 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,173,165,50,14 END +IDD_SCREENS_LINKS DIALOG DISCARDABLE 0, 0, 354, 213 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Screens & Links" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Screens:",IDC_STATIC,7,7,29,8 + LISTBOX IDC_SCREENS_SCREENS,7,18,100,36,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "+",IDC_SCREENS_ADD_SCREEN,7,57,17,14 + PUSHBUTTON "-",IDC_SCREENS_REMOVE_SCREEN,28,57,17,14 + PUSHBUTTON "Edit",IDC_SCREENS_EDIT_SCREEN,49,57,24,14 + LTEXT "&Links:",IDC_STATIC,7,83,20,8 + LISTBOX IDC_SCREENS_LINKS,7,94,339,59,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_SCREENS_SRC_START,7,156,16,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "to",IDC_STATIC,25,158,8,8 + EDITTEXT IDC_SCREENS_SRC_END,33,156,16,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "% of the",IDC_STATIC,52,158,27,8 + COMBOBOX IDC_SCREENS_SRC_SIDE,80,156,48,69,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "of",IDC_STATIC,129,158,8,8 + COMBOBOX IDC_SCREENS_SRC_SCREEN,139,156,59,53,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "goes to",IDC_STATIC,200,158,24,8 + EDITTEXT IDC_SCREENS_DST_START,225,156,16,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "to",IDC_STATIC,243,158,8,8 + EDITTEXT IDC_SCREENS_DST_END,251,156,16,12,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "% of",IDC_STATIC,270,158,15,8 + COMBOBOX IDC_SCREENS_DST_SCREEN,287,156,59,53,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "+",IDC_SCREENS_ADD_LINK,7,172,17,14 + PUSHBUTTON "-",IDC_SCREENS_REMOVE_LINK,28,172,17,14 + DEFPUSHBUTTON "OK",IDOK,241,192,50,14 + PUSHBUTTON "Cancel",IDCANCEL,297,192,50,14 + LTEXT "Source edge overlaps an existing edge.", + IDC_SCREENS_OVERLAP_ERROR,72,175,126,8,NOT WS_VISIBLE | + NOT WS_GROUP +END + +IDD_INFO DIALOG DISCARDABLE 0, 0, 186, 95 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Info" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Version:",IDC_STATIC,7,7,26,8 + EDITTEXT IDC_INFO_VERSION,52,7,127,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LTEXT "Hostname:",IDC_STATIC,7,19,35,8 + EDITTEXT IDC_INFO_HOSTNAME,52,19,127,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LTEXT "IP Address:",IDC_STATIC,7,31,37,8 + EDITTEXT IDC_INFO_IP_ADDRESS,52,31,127,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LTEXT "User Config:",IDC_STATIC,7,43,40,8 + EDITTEXT IDC_INFO_USER_CONFIG,52,43,127,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + LTEXT "Sys Config:",IDC_STATIC,7,55,36,8 + EDITTEXT IDC_INFO_SYS_CONFIG,52,55,127,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER + DEFPUSHBUTTON "OK",IDOK,129,74,50,14 +END + +IDD_HOTKEY_OPTIONS DIALOG DISCARDABLE 0, 0, 360, 151 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Hot Keys" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Hot Keys:",IDC_STATIC,7,7,32,8 + LISTBOX IDC_HOTKEY_HOTKEYS,7,18,169,88,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "+",IDC_HOTKEY_ADD_HOTKEY,7,109,17,14 + PUSHBUTTON "-",IDC_HOTKEY_REMOVE_HOTKEY,28,109,17,14 + PUSHBUTTON "Edit",IDC_HOTKEY_EDIT_HOTKEY,49,109,24,14 + LTEXT "&Actions:",IDC_STATIC,183,7,26,8 + LISTBOX IDC_HOTKEY_ACTIONS,183,18,169,88,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "+",IDC_HOTKEY_ADD_ACTION,183,109,17,14 + PUSHBUTTON "-",IDC_HOTKEY_REMOVE_ACTION,204,109,17,14 + PUSHBUTTON "Edit",IDC_HOTKEY_EDIT_ACTION,225,109,24,14 + DEFPUSHBUTTON "OK",IDOK,302,130,50,14 +END + +IDD_HOTKEY_CONDITION DIALOG DISCARDABLE 0, 0, 183, 58 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Hot Key" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Enter &new hot key or mouse button:",IDC_STATIC,7,7,113, + 8 + EDITTEXT IDC_HOTKEY_CONDITION_HOTKEY,7,17,169,12,ES_WANTRETURN + DEFPUSHBUTTON "OK",IDOK,70,37,50,14 + PUSHBUTTON "Cancel",IDCANCEL,126,37,50,14 +END + +IDD_HOTKEY_ACTION DIALOG DISCARDABLE 0, 0, 183, 202 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Action" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Action:",IDC_STATIC,7,7,23,8 + CONTROL "Press:",IDC_HOTKEY_ACTION_DOWN,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,7,19,35,10 + CONTROL "Release:",IDC_HOTKEY_ACTION_UP,"Button", + BS_AUTORADIOBUTTON,7,31,44,10 + CONTROL "Press && Release:",IDC_HOTKEY_ACTION_DOWNUP,"Button", + BS_AUTORADIOBUTTON,7,43,69,10 + CONTROL "Switch To Screen:",IDC_HOTKEY_ACTION_SWITCH_TO,"Button", + BS_AUTORADIOBUTTON,7,85,75,10 + CONTROL "Switch In Direction:",IDC_HOTKEY_ACTION_SWITCH_IN, + "Button",BS_AUTORADIOBUTTON,7,101,77,10 + CONTROL "Lock Cursor to Screen:",IDC_HOTKEY_ACTION_LOCK,"Button", + BS_AUTORADIOBUTTON,7,117,89,10 + LTEXT "&Hot key or mouse button:",IDC_STATIC,7,55,80,8 + EDITTEXT IDC_HOTKEY_ACTION_HOTKEY,7,67,152,12,ES_WANTRETURN + PUSHBUTTON "...",IDC_HOTKEY_ACTION_SCREENS,162,67,14,12 + COMBOBOX IDC_HOTKEY_ACTION_SWITCH_TO_LIST,87,83,89,62, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_HOTKEY_ACTION_SWITCH_IN_LIST,106,99,70,66, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_HOTKEY_ACTION_LOCK_LIST,106,115,70,58, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Action takes place &when:",IDC_STATIC,7,137,81,8 + CONTROL "Hot key is pressed",IDC_HOTKEY_ACTION_ON_ACTIVATE, + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,149,74,10 + CONTROL "Hot key is released",IDC_HOTKEY_ACTION_ON_DEACTIVATE, + "Button",BS_AUTORADIOBUTTON,7,161,76,10 + DEFPUSHBUTTON "OK",IDOK,70,181,50,14 + PUSHBUTTON "Cancel",IDCANCEL,126,181,50,14 +END + +IDD_HOTKEY_SCREENS DIALOG DISCARDABLE 0, 0, 237, 79 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Target Screens" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Available screens:",IDC_STATIC,7,7,58,8 + LISTBOX IDC_HOTKEY_SCREENS_SRC,7,17,100,36,LBS_NOINTEGRALHEIGHT | + LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP + LISTBOX IDC_HOTKEY_SCREENS_DST,130,17,100,36, + LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "-->",IDC_HOTKEY_SCREENS_ADD,109,21,17,14 + PUSHBUTTON "<--",IDC_HOTKEY_SCREENS_REMOVE,109,38,17,14 + DEFPUSHBUTTON "OK",IDOK,124,58,50,14 + PUSHBUTTON "Cancel",IDCANCEL,180,58,50,14 + LTEXT "&Send action to screens:",IDC_STATIC,130,7,76,8 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -252,7 +403,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 293 TOPMARGIN, 7 - BOTTOMMARGIN, 255 + BOTTOMMARGIN, 192 END IDD_ADD, DIALOG @@ -284,7 +435,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 200 TOPMARGIN, 7 - BOTTOMMARGIN, 262 + BOTTOMMARGIN, 283 END IDD_ADVANCED_OPTIONS, DIALOG @@ -294,6 +445,54 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 179 END + + IDD_SCREENS_LINKS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 347 + TOPMARGIN, 7 + BOTTOMMARGIN, 206 + END + + IDD_INFO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END + + IDD_HOTKEY_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 353 + TOPMARGIN, 7 + BOTTOMMARGIN, 144 + END + + IDD_HOTKEY_CONDITION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 176 + TOPMARGIN, 7 + BOTTOMMARGIN, 51 + END + + IDD_HOTKEY_ACTION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 176 + TOPMARGIN, 7 + BOTTOMMARGIN, 195 + END + + IDD_HOTKEY_SCREENS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 230 + TOPMARGIN, 7 + BOTTOMMARGIN, 72 + END END #endif // APSTUDIO_INVOKED @@ -358,13 +557,37 @@ BEGIN IDS_UNINSTALLED_USER "Removed auto-start. Synergy will not automatically start each time you log in." IDS_INVALID_SERVER_NAME "Server name `%{1}' is invalid." IDS_TITLE "Synergy - Version %{1}" - IDS_SERVER_IS_CLIENT "Please enter the computer name of the synergy server, not\nthe name of this computer, in the Server Host Name field." + IDS_SERVER_IS_CLIENT "Please enter the name of the computer sharing a\nkeyboard and mouse, not the name of this computer,\nin the Other Computer's Host Name field." IDS_ADD_SCREEN "Add Screen" IDS_EDIT_SCREEN "Edit Screen %{1}" IDS_ERROR_CODE "Error code: %{1}" IDS_AUTOSTART_PERMISSION_ALL "You have sufficient access rights to install and uninstall Auto Start for all users or for just yourself." IDS_INVALID_INTERFACE_NAME "The interface '%{1}' is invalid: %{2}" + IDS_INVALID_CORNER_SIZE "The dead corner size %{1} is invalid; it must be 0 or higher." + IDS_NEW_LINK "[New Link]" + IDS_SIDE_LEFT "left of" + IDS_SIDE_RIGHT "right of" + IDS_SIDE_TOP "above" + IDS_SIDE_BOTTOM "below" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_LINK_FORMAT "%{4}%{5} is %{3} %{1}%{2}" + IDS_LINK_INTERVAL_FORMAT "(%{1},%{2})" + IDS_EDGE_LEFT "left" + IDS_EDGE_RIGHT "right" + IDS_EDGE_TOP "top" + IDS_EDGE_BOTTOM "bottom" + IDS_AUTOSTART_SAVE_FAILED "Failed to save autostart configuration: %{1}" + IDS_LOAD_FAILED "Failed to load configuration." + IDS_CONFIG_CHANGED "Configuration changed on disk. Reload?" + IDS_LOCK_MODE_OFF "off" + IDS_LOCK_MODE_ON "on" + IDS_LOCK_MODE_TOGGLE "toggle" + IDS_ALL_SCREENS "All Screens" + IDS_ACTIVE_SCREEN "Active Screen" END #endif // English (U.S.) resources diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index caa48052..e4e7b487 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -44,6 +44,26 @@ #define IDS_ERROR_CODE 39 #define IDS_AUTOSTART_PERMISSION_ALL 40 #define IDS_INVALID_INTERFACE_NAME 41 +#define IDS_INVALID_CORNER_SIZE 42 +#define IDS_NEW_LINK 43 +#define IDS_SIDE_LEFT 44 +#define IDS_SIDE_RIGHT 45 +#define IDS_SIDE_TOP 46 +#define IDS_SIDE_BOTTOM 47 +#define IDS_LINK_FORMAT 48 +#define IDS_LINK_INTERVAL_FORMAT 49 +#define IDS_EDGE_LEFT 50 +#define IDS_EDGE_RIGHT 51 +#define IDS_EDGE_TOP 52 +#define IDS_EDGE_BOTTOM 53 +#define IDS_AUTOSTART_SAVE_FAILED 54 +#define IDS_LOAD_FAILED 55 +#define IDS_CONFIG_CHANGED 56 +#define IDS_LOCK_MODE_OFF 57 +#define IDS_LOCK_MODE_ON 58 +#define IDS_LOCK_MODE_TOGGLE 59 +#define IDS_ALL_SCREENS 60 +#define IDS_ACTIVE_SCREEN 61 #define IDD_MAIN 101 #define IDD_ADD 102 #define IDD_WAIT 103 @@ -51,45 +71,36 @@ #define IDD_AUTOSTART 105 #define IDD_ADVANCED_OPTIONS 106 #define IDD_GLOBAL_OPTIONS 107 +#define IDD_SCREENS_LINKS 110 +#define IDD_INFO 111 +#define IDD_HOTKEY_OPTIONS 112 +#define IDD_HOTKEY_CONDITION 113 +#define IDD_HOTKEY_ACTION 114 +#define IDD_HOTKEY_SCREENS 115 #define IDC_MAIN_CLIENT_RADIO 1000 #define IDC_MAIN_SERVER_RADIO 1001 -#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1002 -#define IDC_MAIN_ADVANCED_NAME_EDIT 1006 -#define IDC_MAIN_ADVANCED_PORT_EDIT 1008 +#define IDC_MAIN_CLIENT_SERVER_NAME_LABEL 1002 +#define IDC_MAIN_CLIENT_SERVER_NAME_EDIT 1003 +#define IDC_MAIN_SERVER_SCREENS_LABEL 1004 +#define IDC_MAIN_SCREENS 1005 +#define IDC_MAIN_OPTIONS 1006 +#define IDC_MAIN_ADVANCED 1007 +#define IDC_MAIN_AUTOSTART 1008 #define IDC_MAIN_TEST 1009 #define IDC_MAIN_SAVE 1010 -#define IDC_MAIN_CLIENT_SERVER_NAME_LABEL 1011 -#define IDC_MAIN_SERVER_SCREENS_LIST 1012 -#define IDC_MAIN_SERVER_SCREENS_LABEL 1013 -#define IDC_MAIN_SERVER_LAYOUT_LABEL 1014 -#define IDC_MAIN_SERVER_ADD_BUTTON 1018 -#define IDC_MAIN_SERVER_EDIT_BUTTON 1019 +#define IDC_MAIN_HOTKEYS 1010 +#define IDC_MAIN_DEBUG 1011 #define IDC_ADD_SCREEN_NAME_EDIT 1020 -#define IDC_MAIN_SERVER_REMOVE_BUTTON 1020 #define IDC_ADD_ALIASES_EDIT 1021 -#define IDC_MAIN_SERVER_OPTIONS_BUTTON 1021 -#define IDC_MAIN_SERVER_LEFT_COMBO 1022 -#define IDC_MAIN_SERVER_RIGHT_COMBO 1023 -#define IDC_MAIN_SERVER_TOP_COMBO 1024 -#define IDC_MAIN_SERVER_BOTTOM_COMBO 1025 -#define IDC_MAIN_SERVER_LEFT_LABEL 1026 -#define IDC_MAIN_SERVER_RIGHT_LABEL 1027 -#define IDC_MAIN_SERVER_TOP_LABEL 1028 -#define IDC_MAIN_SERVER_BOTTOM_LABEL 1029 -#define IDC_MAIN_UNINSTALL 1030 #define IDC_AUTOSTART_INSTALLED_MSG 1031 #define IDC_AUTOSTART_PERMISSION_MSG 1032 #define IDC_AUTOSTART_INSTALL_USER 1033 #define IDC_AUTOSTART_INSTALL_SYSTEM 1034 -#define IDC_MAIN_AUTOSTART 1035 -#define IDC_MAIN_OPTIONS 1036 #define IDC_ADD_HD_CAPS_CHECK 1037 -#define IDC_MAIN_ADVANCED 1037 #define IDC_ADD_HD_NUM_CHECK 1038 #define IDC_ADVANCED_NAME_EDIT 1038 #define IDC_ADVANCED_PORT_EDIT 1039 #define IDC_ADD_HD_SCROLL_CHECK 1039 -#define IDC_MAIN_DEBUG 1040 #define IDC_ADVANCED_INTERFACE_EDIT 1040 #define IDC_GLOBAL_DELAY_CHECK 1041 #define IDC_GLOBAL_DELAY_TIME 1042 @@ -105,15 +116,68 @@ #define IDC_GLOBAL_SCREENSAVER_SYNC 1047 #define IDC_GLOBAL_RELATIVE_MOVES 1048 #define IDC_ADVANCED_DEFAULTS 1049 +#define IDC_GLOBAL_LEAVE_FOREGROUND 1049 +#define IDC_ADD_DC_SIZE 1052 +#define IDC_ADD_DC_TOP_LEFT 1053 +#define IDC_ADD_DC_TOP_RIGHT 1054 +#define IDC_ADD_DC_BOTTOM_LEFT 1055 +#define IDC_ADD_DC_BOTTOM_RIGHT 1056 +#define IDC_SCREENS_SRC_SIDE 1057 +#define IDC_SCREENS_SRC_START 1058 +#define IDC_SCREENS_SRC_END 1059 +#define IDC_SCREENS_SRC_SCREEN 1060 +#define IDC_SCREENS_SCREENS 1061 +#define IDC_SCREENS_ADD_SCREEN 1062 +#define IDC_SCREENS_LINKS 1063 +#define IDC_SCREENS_DST_START 1064 +#define IDC_SCREENS_DST_END 1065 +#define IDC_SCREENS_DST_SCREEN 1066 +#define IDC_SCREENS_REMOVE_SCREEN 1067 +#define IDC_SCREENS_EDIT_SCREEN 1068 +#define IDC_SCREENS_ADD_LINK 1069 +#define IDC_SCREENS_REMOVE_LINK 1070 +#define IDC_SCREENS_OVERLAP_ERROR 1071 +#define IDC_INFO_VERSION 1073 +#define IDC_MAIN_INFO 1074 +#define IDC_INFO_HOSTNAME 1076 +#define IDC_HOTKEY_HOTKEYS 1076 +#define IDC_INFO_IP_ADDRESS 1077 +#define IDC_HOTKEY_ADD_HOTKEY 1077 +#define IDC_INFO_USER_CONFIG 1078 +#define IDC_HOTKEY_REMOVE_HOTKEY 1078 +#define IDC_INFO_SYS_CONFIG 1079 +#define IDC_HOTKEY_EDIT_HOTKEY 1079 +#define IDC_HOTKEY_ACTIONS 1080 +#define IDC_HOTKEY_CONDITION_HOTKEY 1080 +#define IDC_HOTKEY_ACTION_DOWNUP 1081 +#define IDC_HOTKEY_ADD_ACTION 1082 +#define IDC_HOTKEY_ACTION_DOWN 1082 +#define IDC_HOTKEY_REMOVE_ACTION 1083 +#define IDC_HOTKEY_ACTION_UP 1083 +#define IDC_HOTKEY_EDIT_ACTION 1084 +#define IDC_HOTKEY_ACTION_HOTKEY 1085 +#define IDC_HOTKEY_ACTION_SWITCH_TO_LIST 1086 +#define IDC_HOTKEY_ACTION_SWITCH_TO 1087 +#define IDC_HOTKEY_ACTION_SWITCH_IN 1088 +#define IDC_HOTKEY_ACTION_LOCK 1089 +#define IDC_HOTKEY_ACTION_SWITCH_IN_LIST 1090 +#define IDC_HOTKEY_ACTION_LOCK_LIST 1091 +#define IDC_HOTKEY_ACTION_ON_ACTIVATE 1092 +#define IDC_HOTKEY_ACTION_ON_DEACTIVATE 1093 +#define IDC_HOTKEY_ACTION_SCREENS 1094 +#define IDC_HOTKEY_SCREENS_SRC 1095 +#define IDC_HOTKEY_SCREENS_DST 1096 +#define IDC_HOTKEY_SCREENS_ADD 1097 +#define IDC_HOTKEY_SCREENS_REMOVE 1098 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 110 +#define _APS_NEXT_RESOURCE_VALUE 116 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1052 +#define _APS_NEXT_CONTROL_VALUE 1098 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/cmd/launcher/synergy.ico b/cmd/launcher/synergy.ico index 39e0ff8d3a06d50e01aeab55c816f77d949c772d..89f965f4432c5f58054b9a1b0eae5bf6cbfd245d 100644 GIT binary patch literal 8478 zcmeI1KWH3B6vm(a*e8S|%Z-~Dq__}vXV}UrV&mT6#z|>aLkbt^+yrSM;8FwvhV6t% zLQiZmzTr%%8baza{{iY$oC5(ZeHZtwQ9U1vh;!S!}zl^B0qm9a)se4 z$4!nI!GV?&yT9dtAbB1-&RoiHIHXs=+duJMuQ>Qqlb(9p`ug^E1I+I3?(XmJkD_Ir zwoR+XpoZ`7n_*F5!?$xa7_9y@9UB~QjWDbDj>v-IqX1iNmO=`Jq0y9JcvU(jnEQ^w z7Gb2>!%ze~pRL_3zyv-RkH?YkoZDF1+}w=gwY9a?KK%1QIIFneKdQ#~i{M`c0A3Tt z1K}(%xF*;nf_mc~2;;GW3Worjn~JKt$bbM9zM*5w5O5oY2+>S!bo#xVIM#9I(&{w)eJBUN zeJAtt^Rlq8Am`4VlM5Fv$i<5n<({SK{)Lx2 zcf5QpPsRUH%CArVl&9mdeD%YRB+58+ffXEO7-rZqY#FvpZQOaKIPAI@9d?J^VOO^T zdx5=RP+%{x(@BZF#9m@AF_hRnW6#)^mO0~`5j-PMhEvA(o2BQJp&>&8GBjjp$k32^Z0N|)k*N+oJ*YAjjs+pbjlo20;DbJG3~oSXt2F(BaVG(BaTwr9y{8gF}NugO<*r!=b^Up|o%I zGL(=!;g7MESF3|6au@Kd)Z{MERFJeFL4l@%+y!7k?t?N}3)s-$(csbG(csbG(O}C74J8^p8ax_G zG!TXZJ{mk4JQ_S22+e^V4IT|14ITlW{3XFWjvkUDhGXo|;VrguoO~qT%DsE{eVYbJUo4A)*c53emtZek(@;r+A&?7`%alYMX$a zY+S?5oN9)eBHqA@i;?Pjweae)tI(w2f&-E#+${e5S>63iAM`TagzjPcMcFJ$Y(hZaAL zK9Rw|H_|&9)FI_tfX*qMLxQ;r%W>h`ir$IeFQ=8D75`u1H(R)5quI_B_inZY z`w#h31SN9V5+-_1#7`aC(~P4ezM76pkkD<1n(})TrS!0)eWSx9y(K8_{d=_ISy@{N zT9R7PBfXOV?+~X=Zo;=qR3)1r*J?T?JqdqOQS0|g$Q4UqeS6A`CiJxElT+yOJM;;M z4u68)b!hW@=$O(;r2KxiS{sjsKb@U=ME;xEhFTKipMZQE#eS#RJDbrUyt}d2IpX6> za(ySP_)ug{dwv(qXfHHQN7`?RK8(?u08Lb>z>#22Rc4!-|>%e>pWH7s3Yo%I-~CHeBwOgyeWaz zp{~oYs#C06tRuuw-%(X(t$cW&zD$T!j`%zECiCoXIRyaW$EkIkxjg-ydip!{^nLpK z@IIZrF`MQ1m2Nxb=7&PqIET$NlxiPcZLX`ax&{w}LnJfkI1B@$JJyFz#P!+JH%|8{ t;Zdj_)sxDeTJ=5vdoUQ9?87Q{NCkzlT_5sH8R_#vpACFJzU}{8{tX4i2$ui= literal 1078 zcmcIjF>ZrE5FBhIED^>0AXJf`ktfl)y)G-^$50||bEOL&;nF2tnn+}JFIZr?iL8vh zyEE(_9Op5RVS~D^V7P98ch;&(`j7#yTj0QJHRFSkA!h~xXCEW5$xaBVFq0z2$ZCzr zznCUt41VmArGJJv>>-Jz+yJ}8xQA)_q84#F7>Wj+$hY&P%aul{OC3v1I=iZr3+-$OY_kFlMUs(<&!6Qm*oKUsy=X4hhYoZPM#moz)C`vDH;Q(pi8 diff --git a/cmd/synergyc/CClientTaskBarReceiver.cpp b/cmd/synergyc/CClientTaskBarReceiver.cpp index ddd8ec5a..025b43f6 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CClientTaskBarReceiver.cpp @@ -15,8 +15,10 @@ #include "CClientTaskBarReceiver.h" #include "CClient.h" #include "CLock.h" +#include "CStringUtil.h" #include "IEventQueue.h" #include "CArch.h" +#include "Version.h" // // CClientTaskBarReceiver @@ -48,6 +50,8 @@ CClientTaskBarReceiver::updateStatus(CClient* client, const CString& errorMsg) } } else { + m_server = client->getServerAddress().getHostname(); + if (client->isConnected()) { m_state = kConnected; } @@ -108,19 +112,23 @@ CClientTaskBarReceiver::getToolTip() const { switch (m_state) { case kNotRunning: - return "Synergy: Not running"; + return CStringUtil::print("%s: Not running", kAppVersion); case kNotWorking: - return CString("Synergy: ") + m_errorMessage; + return CStringUtil::print("%s: %s", + kAppVersion, m_errorMessage.c_str()); case kNotConnected: - return CString("Synergy: Not connected: ") + m_errorMessage; + return CStringUtil::print("%s: Not connected: %s", + kAppVersion, m_errorMessage.c_str()); case kConnecting: - return "Synergy: Connecting..."; + return CStringUtil::print("%s: Connecting to %s...", + kAppVersion, m_server.c_str()); case kConnected: - return "Synergy: Connected"; + return CStringUtil::print("%s: Connected to %s", + kAppVersion, m_server.c_str()); default: return ""; diff --git a/cmd/synergyc/CClientTaskBarReceiver.h b/cmd/synergyc/CClientTaskBarReceiver.h index a9fe50c2..0e3440e1 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.h +++ b/cmd/synergyc/CClientTaskBarReceiver.h @@ -77,6 +77,7 @@ protected: private: EState m_state; CString m_errorMessage; + CString m_server; }; #endif diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp index c53ea1c8..5c31f4a5 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -139,6 +139,9 @@ CMSWindowsClientTaskBarReceiver::runMenu(int x, int y) SetForegroundWindow(m_window); HMENU menu = GetSubMenu(m_menu, 0); SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE); + HMENU logLevelMenu = GetSubMenu(menu, 3); + CheckMenuRadioItem(logLevelMenu, 0, 6, + CLOG->getFilter() - CLog::kERROR, MF_BYPOSITION); int n = TrackPopupMenu(menu, TPM_NONOTIFY | TPM_RETURNCMD | @@ -157,6 +160,38 @@ CMSWindowsClientTaskBarReceiver::runMenu(int x, int y) copyLog(); break; + case IDC_TASKBAR_SHOW_LOG: + ARCH->showConsole(true); + break; + + case IDC_TASKBAR_LOG_LEVEL_ERROR: + CLOG->setFilter(CLog::kERROR); + break; + + case IDC_TASKBAR_LOG_LEVEL_WARNING: + CLOG->setFilter(CLog::kWARNING); + break; + + case IDC_TASKBAR_LOG_LEVEL_NOTE: + CLOG->setFilter(CLog::kNOTE); + break; + + case IDC_TASKBAR_LOG_LEVEL_INFO: + CLOG->setFilter(CLog::kINFO); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG: + CLOG->setFilter(CLog::kDEBUG); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG1: + CLOG->setFilter(CLog::kDEBUG1); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG2: + CLOG->setFilter(CLog::kDEBUG2); + break; + case IDC_TASKBAR_QUIT: quit(); break; diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 783472c2..89942666 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -67,6 +67,12 @@ synergyc_SOURCES = \ $(COMMON_SOURCE_FILES) \ $(CARBON_SOURCE_FILES) \ $(NULL) +synergyc_LDFLAGS = \ + -framework ScreenSaver \ + -framework IOKit \ + -framework ApplicationServices \ + -framework Foundation \ + $(NULL) endif synergyc_LDADD = \ $(top_builddir)/lib/client/libclient.a \ diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h index 1a5455a1..eeee6e1e 100644 --- a/cmd/synergyc/resource.h +++ b/cmd/synergyc/resource.h @@ -16,13 +16,21 @@ #define IDC_TASKBAR_QUIT 40001 #define IDC_TASKBAR_STATUS 40002 #define IDC_TASKBAR_LOG 40003 +#define IDC_TASKBAR_SHOW_LOG 40004 +#define IDC_TASKBAR_LOG_LEVEL_ERROR 40009 +#define IDC_TASKBAR_LOG_LEVEL_WARNING 40010 +#define IDC_TASKBAR_LOG_LEVEL_NOTE 40011 +#define IDC_TASKBAR_LOG_LEVEL_INFO 40012 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG 40013 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG1 40014 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG2 40015 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 109 -#define _APS_NEXT_COMMAND_VALUE 40004 +#define _APS_NEXT_COMMAND_VALUE 40016 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 2b548be8..eabddb49 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -60,10 +60,6 @@ typedef int (*StartupFunc)(int, char**); static bool startClient(); static void parse(int argc, const char* const* argv); -#if WINAPI_MSWINDOWS -static void handleSystemSuspend(void*); -static void handleSystemResume(void*); -#endif // // program arguments @@ -108,9 +104,7 @@ CScreen* createScreen() { #if WINAPI_MSWINDOWS - return new CScreen(new CMSWindowsScreen(false, - new CFunctionJob(&handleSystemSuspend), - new CFunctionJob(&handleSystemResume))); + return new CScreen(new CMSWindowsScreen(false)); #elif WINAPI_XWINDOWS return new CScreen(new CXWindowsScreen(ARG->m_display, false)); #elif WINAPI_CARBON @@ -199,26 +193,6 @@ handleScreenError(const CEvent&, void*) EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } -#if WINAPI_MSWINDOWS -static -void -handleSystemSuspend(void*) -{ - LOG((CLOG_NOTE "system suspending")); - s_suspened = true; - s_client->disconnect(NULL); -} - -static -void -handleSystemResume(void*) -{ - LOG((CLOG_NOTE "system resuming")); - s_suspened = false; - startClient(); -} -#endif - static CScreen* openClientScreen() @@ -252,9 +226,7 @@ handleClientRestart(const CEvent&, void* vtimer) EVENTQUEUE->removeHandler(CEvent::kTimer, timer); // reconnect - if (!s_suspened) { - startClient(); - } + startClient(); } static @@ -287,6 +259,7 @@ handleClientFailed(const CEvent& e, void*) updateStatus(CString("Failed to connect to server: ") + info->m_what); if (!ARG->m_restartable || !info->m_retry) { LOG((CLOG_ERR "failed to connect to server: %s", info->m_what)); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } else { LOG((CLOG_WARN "failed to connect to server: %s", info->m_what)); @@ -441,7 +414,11 @@ static int daemonMainLoop(int, const char**) { - CSystemLogger sysLogger(DAEMON_NAME); +#if SYSAPI_WIN32 + CSystemLogger sysLogger(DAEMON_NAME, false); +#else + CSystemLogger sysLogger(DAEMON_NAME, true); +#endif return mainLoop(); } @@ -449,6 +426,10 @@ static int standardStartup(int argc, char** argv) { + if (!ARG->m_daemon) { + ARCH->showConsole(false); + } + // parse command line parse(argc, argv); @@ -758,6 +739,7 @@ public: // ILogOutputter overrides virtual void open(const char*) { } virtual void close() { } + virtual void show(bool) { } virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const { return ""; } }; @@ -802,11 +784,24 @@ static int daemonNTStartup(int, char**) { - CSystemLogger sysLogger(DAEMON_NAME); + CSystemLogger sysLogger(DAEMON_NAME, false); bye = &byeThrow; return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop); } +static +int +foregroundStartup(int argc, char** argv) +{ + ARCH->showConsole(false); + + // parse command line + parse(argc, argv); + + // never daemonize + return mainLoop(); +} + static void showError(HINSTANCE instance, const char* title, UINT id, const char* arg) @@ -820,12 +815,23 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { try { + CArchMiscWindows::setIcons((HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 32, 32, LR_SHARED), + (HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 16, 16, LR_SHARED)); CArch arch(instance); CMSWindowsScreen::init(instance); CLOG; CThread::getCurrentThread().setPriority(-14); CArgs args; + // set title on log window + ARCH->openConsole((CString(kAppVersion) + " " + "Client").c_str()); + // windows NT family starts services using no command line options. // since i'm not sure how to tell the difference between that and // a user providing no options we'll assume that if there are no @@ -833,8 +839,13 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. StartupFunc startup = &standardStartup; - if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - startup = &daemonNTStartup; + if (!CArchMiscWindows::isWindows95Family()) { + if (__argc <= 1) { + startup = &daemonNTStartup; + } + else { + startup = &foregroundStartup; + } } // send PRINT and FATAL output to a message box diff --git a/cmd/synergyc/synergyc.dsp b/cmd/synergyc/synergyc.dsp index ff793b14..4238ea03 100644 --- a/cmd/synergyc/synergyc.dsp +++ b/cmd/synergyc/synergyc.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c /Fd..\..\gen\build\synergyc.pdb +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\synergyc.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -70,7 +70,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c /Fd..\..\gen\debug\synergyc.pdb +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\synergyc.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/cmd/synergyc/synergyc.ico b/cmd/synergyc/synergyc.ico index 23d9a0906462ab9c0d97b7493376c20036ea471e..89f965f4432c5f58054b9a1b0eae5bf6cbfd245d 100644 GIT binary patch literal 8478 zcmeI1KWH3B6vm(a*e8S|%Z-~Dq__}vXV}UrV&mT6#z|>aLkbt^+yrSM;8FwvhV6t% zLQiZmzTr%%8baza{{iY$oC5(ZeHZtwQ9U1vh;!S!}zl^B0qm9a)se4 z$4!nI!GV?&yT9dtAbB1-&RoiHIHXs=+duJMuQ>Qqlb(9p`ug^E1I+I3?(XmJkD_Ir zwoR+XpoZ`7n_*F5!?$xa7_9y@9UB~QjWDbDj>v-IqX1iNmO=`Jq0y9JcvU(jnEQ^w z7Gb2>!%ze~pRL_3zyv-RkH?YkoZDF1+}w=gwY9a?KK%1QIIFneKdQ#~i{M`c0A3Tt z1K}(%xF*;nf_mc~2;;GW3Worjn~JKt$bbM9zM*5w5O5oY2+>S!bo#xVIM#9I(&{w)eJBUN zeJAtt^Rlq8Am`4VlM5Fv$i<5n<({SK{)Lx2 zcf5QpPsRUH%CArVl&9mdeD%YRB+58+ffXEO7-rZqY#FvpZQOaKIPAI@9d?J^VOO^T zdx5=RP+%{x(@BZF#9m@AF_hRnW6#)^mO0~`5j-PMhEvA(o2BQJp&>&8GBjjp$k32^Z0N|)k*N+oJ*YAjjs+pbjlo20;DbJG3~oSXt2F(BaVG(BaTwr9y{8gF}NugO<*r!=b^Up|o%I zGL(=!;g7MESF3|6au@Kd)Z{MERFJeFL4l@%+y!7k?t?N}3)s-$(csbG(csbG(O}C74J8^p8ax_G zG!TXZJ{mk4JQ_S22+e^V4IT|14ITlW{3XFWjvkUDhGXo|;VrguoO~qT%DsE{eVYbJUo4A)*c53emtZek(@;r+A&?7`%alYMX$a zY+S?5oN9)eBHqA@i;?Pjweae)tI(w2f&-E#+${e5S>63iAM`TagzjPcMcFJ$Y(hZaAL zK9Rw|H_|&9)FI_tfX*qMLxQ;r%W>h`ir$IeFQ=8D75`u1H(R)5quI_B_inZY z`w#h31SN9V5+-_1#7`aC(~P4ezM76pkkD<1n(})TrS!0)eWSx9y(K8_{d=_ISy@{N zT9R7PBfXOV?+~X=Zo;=qR3)1r*J?T?JqdqOQS0|g$Q4UqeS6A`CiJxElT+yOJM;;M z4u68)b!hW@=$O(;r2KxiS{sjsKb@U=ME;xEhFTKipMZQE#eS#RJDbrUyt}d2IpX6> za(ySP_)ug{dwv(qXfHHQN7`?RK8(?u08Lb>z>#22Rc4!-|>%e>pWH7s3Yo%I-~CHeBwOgyeWaz zp{~oYs#C06tRuuw-%(X(t$cW&zD$T!j`%zECiCoXIRyaW$EkIkxjg-ydip!{^nLpK z@IIZrF`MQ1m2Nxb=7&PqIET$NlxiPcZLX`ax&{w}LnJfkI1B@$JJyFz#P!+JH%|8{ t;Zdj_)sxDeTJ=5vdoUQ9?87Q{NCkzlT_5sH8R_#vpACFJzU}{8{tX4i2$ui= literal 1078 zcmcIjF>ZrE5FC6+xQh8fs3Mn0+fY(a_&Id6Ct)f3ES};rr3*_b$?RURz;Y8=8GCnU z*gZJTV<5v0RaL_9wF5p_%QER>2D}B}$ZHj&Wn{>ifymkCh-|VGV=By~n5K!<8nb^f z&&C-1*da;TyJ!;jP^Q6N@Bgk5}L6UBBn>K0sicl|7 z>c~aHzIP$D4S{V|pr=T(rQ-7!*>yJK8teUVc(XssjjkWGz2AEMUHkF#ec5+9I81*g z>e-HOCD}%iv@d!g`Jq1~9Im|o1qc%Zpk4onNYgUHDh}lDlX-3f^Lfdr7>my*^5=;3 zI6L&T7iQ9ze6GqH(2HHmL*4TyYI|+Gsx*5&cPi41(wy8OavnuT5$#UelluIqoTrDl j{)%DX{(iHu1?m5W`_UE`Ag|c9SRp63>%}F_F6({(d|mCB diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index e168a3b3..519e8e48 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -87,7 +87,25 @@ BEGIN POPUP "Synergy" BEGIN MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Show Log", IDC_TASKBAR_SHOW_LOG MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG + POPUP "Set Log Level" + BEGIN + MENUITEM "Error", IDC_TASKBAR_LOG_LEVEL_ERROR + + MENUITEM "Warning", IDC_TASKBAR_LOG_LEVEL_WARNING + + MENUITEM "Note", IDC_TASKBAR_LOG_LEVEL_NOTE + + MENUITEM "Info", IDC_TASKBAR_LOG_LEVEL_INFO + + MENUITEM "Debug", IDC_TASKBAR_LOG_LEVEL_DEBUG + + MENUITEM "Debug1", IDC_TASKBAR_LOG_LEVEL_DEBUG1 + + MENUITEM "Debug2", IDC_TASKBAR_LOG_LEVEL_DEBUG2 + + END MENUITEM SEPARATOR MENUITEM "Quit", IDC_TASKBAR_QUIT END diff --git a/cmd/synergyc/tb_error.ico b/cmd/synergyc/tb_error.ico index a304dc1815fa0ababb136f6f1ba13735d2fbb5a6..746a87c9ec8ae70f24b4125afe9ca68defb2f6d1 100644 GIT binary patch delta 177 zcmYLI zdy84jGmU1R5{}3}fJpWtFkU;Su0ox*`K=tL?xB6&V)WT(qEfV(V;r)#M6RqNmiwRr k(H$x<10fE!BH#-5z_+~tpA(Rp{a$Tzz3iy3)m5)*KPi4Xc>n+a literal 318 zcmaKmI}U?D3`8Fa1(GSKQd6eP(da0%M{>W753q;H0Z_=8bSPr%nek^=GIWY*nn*l4 zxTlLbnZ=G?3UTN^DWIg3pGj2`5%OH@N3!o(iHaPhoKX)@YIUo1XqN6xF32N$#zwk% rh~{<7+ME_FZfttD==auOF(2NnD_ToW$P?mr8eX*h4C50%ue=|=dPPd3 diff --git a/cmd/synergyc/tb_idle.ico b/cmd/synergyc/tb_idle.ico index 9ba599de07841a8899275e54b91383ebdba39fb1..4e13a264b92f74bcb167895cbdaaca320254a8ab 100644 GIT binary patch delta 198 zcmXAhAr8Vo5Jg{?gaRSJ4M36LYK!42RZVc%8rTGdX&S`RK_Ke5L<2XeD|N_9X2ZDm z|NB#$+7$L=s@wvTSfp&A6208x_M?H3D~Q-wBF1VwJrP9d^Y6UlUBBsd F`vWq@KOF!7 literal 318 zcmZurAr8YZ5OcLkQ#4XEy23qgEVQs=fwj_)L?NM-@}RNK*GMJXU2NYux?F%xISd0V zo*i&E70elh4!syC2mhl9(7`k{5vxQgMQRuOaj@@J2}-k!&TwR~h6-SQi@40yOtNl# zA(87AkhwT%PDBb@%#1m$E;{BcpK8Iik~cV3LGuCL+q>RFM%&XC+ZR5MR$mHmRoS=p Mmq%Hp**0UdUu;rNsQ>@~ diff --git a/cmd/synergyc/tb_run.ico b/cmd/synergyc/tb_run.ico index 86aa9f3ac989981768b950663e06469635252f8e..88e160cbfcd029978599f49605ba1a3c7695027e 100644 GIT binary patch literal 318 zcmZvXJ#K_B5QRT>QHT}^QKbzPO1ZU9vz2R3fRNJr5IzCD8;(L}A7MN84cny1jNhAi z^COL+lJ|X&*-r&u76q#eLPafx?d1Px0X>%G9mGo6woTC*$N4x8%LKWVjJU*LBRA(t z*&)UlLNF;^_DhTq!s6VW^|KVov_j`difNtFX&-H(O$k3SDILcq=N9iD-8@T;1372! my*B6BBsAGS;Q0-Eqg$^!Uw{7 literal 318 zcmZ{fJqp7x428ddp@9UNsdL62FI!fRK2CNoI)D$6Bjf;u^vNdBp^^N4kR@b8r<|q< zk9P+zSHUZ@&=JI7mH$x$9b#-Emc`7ZesR{rdAbvnUUIsLlp^#g69w)PJ`2R?tWuD+=8@5=TLcpmWBg&BfSKQ6f<$ap>Mt@oPy0w4@W Ai2wiq diff --git a/cmd/synergyc/tb_wait.ico b/cmd/synergyc/tb_wait.ico index 3e2edd5f305fecd3883d8d44892dcf091156b8ee..257be0a1d1bc613eec8cc1838a1dfd25d4644844 100644 GIT binary patch literal 318 zcmZvXF%H5o3`Ji7QN#e9SYe77nRA*>nK?mJi9LtNNy<&SB_ktS@h=k+vHidOZA%U` zW?k2zcWvM#wvckMXxJFSxZpn+z?@1UcuF zl1i)Vw8|M$8oa;3u2z*2ycgFRqu9Ap#396Zhplt1gb?~ejL|uFp_CFr025R~TS5=- dGfb`By0-J}?~kW-COE!+Lz;S;(X4i~`vJXUO(y^V literal 318 zcmZ{fJr08~423^Lq%u@0Q)kAGNWK6A2V!H*m}BJt9D*ZofJ*c^5mJZN&ij#L%Lz2f zzVGpPHSly5yfO$4K@7R@FN&Z;j8(+4getFilter() - CLog::kERROR, MF_BYPOSITION); int n = TrackPopupMenu(menu, TPM_NONOTIFY | TPM_RETURNCMD | @@ -175,6 +178,10 @@ CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) copyLog(); break; + case IDC_TASKBAR_SHOW_LOG: + ARCH->showConsole(true); + break; + case IDC_RELOAD_CONFIG: EVENTQUEUE->addEvent(CEvent(getReloadConfigEvent(), IEventQueue::getSystemTarget())); @@ -185,6 +192,34 @@ CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) IEventQueue::getSystemTarget())); break; + case IDC_TASKBAR_LOG_LEVEL_ERROR: + CLOG->setFilter(CLog::kERROR); + break; + + case IDC_TASKBAR_LOG_LEVEL_WARNING: + CLOG->setFilter(CLog::kWARNING); + break; + + case IDC_TASKBAR_LOG_LEVEL_NOTE: + CLOG->setFilter(CLog::kNOTE); + break; + + case IDC_TASKBAR_LOG_LEVEL_INFO: + CLOG->setFilter(CLog::kINFO); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG: + CLOG->setFilter(CLog::kDEBUG); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG1: + CLOG->setFilter(CLog::kDEBUG1); + break; + + case IDC_TASKBAR_LOG_LEVEL_DEBUG2: + CLOG->setFilter(CLog::kDEBUG2); + break; + case IDC_TASKBAR_QUIT: quit(); break; diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp index ee04d79e..6555b214 100644 --- a/cmd/synergys/CServerTaskBarReceiver.cpp +++ b/cmd/synergys/CServerTaskBarReceiver.cpp @@ -15,8 +15,10 @@ #include "CServerTaskBarReceiver.h" #include "CServer.h" #include "CLock.h" +#include "CStringUtil.h" #include "IEventQueue.h" #include "CArch.h" +#include "Version.h" // // CServerTaskBarReceiver @@ -113,16 +115,17 @@ CServerTaskBarReceiver::getToolTip() const { switch (m_state) { case kNotRunning: - return "Synergy: Not running"; + return CStringUtil::print("%s: Not running", kAppVersion); case kNotWorking: - return std::string("Synergy: ") + m_errorMessage; - + return CStringUtil::print("%s: %s", + kAppVersion, m_errorMessage.c_str()); + case kNotConnected: - return "Synergy: Waiting for clients"; + return CStringUtil::print("%s: Waiting for clients", kAppVersion); case kConnected: - return "Synergy: Connected"; + return CStringUtil::print("%s: Connected", kAppVersion); default: return ""; diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index c45e6b4f..3e02739d 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -67,6 +67,12 @@ synergys_SOURCES = \ $(COMMON_SOURCE_FILES) \ $(CARBON_SOURCE_FILES) \ $(NULL) +synergys_LDFLAGS = \ + -framework ScreenSaver \ + -framework IOKit \ + -framework ApplicationServices \ + -framework Foundation \ + $(NULL) endif synergys_LDADD = \ $(top_builddir)/lib/server/libserver.a \ diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index f3127816..0ad5868a 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -19,13 +19,21 @@ #define IDC_TASKBAR_LOG 40005 #define IDC_RELOAD_CONFIG 40006 #define IDC_FORCE_RECONNECT 40007 +#define IDC_TASKBAR_SHOW_LOG 40008 +#define IDC_TASKBAR_LOG_LEVEL_ERROR 40009 +#define IDC_TASKBAR_LOG_LEVEL_WARNING 40010 +#define IDC_TASKBAR_LOG_LEVEL_NOTE 40011 +#define IDC_TASKBAR_LOG_LEVEL_INFO 40012 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG 40013 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG1 40014 +#define IDC_TASKBAR_LOG_LEVEL_DEBUG2 40015 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 109 -#define _APS_NEXT_COMMAND_VALUE 40008 +#define _APS_NEXT_COMMAND_VALUE 40016 #define _APS_NEXT_CONTROL_VALUE 1003 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index a8530dec..6821a4e2 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -121,7 +121,7 @@ CScreen* createScreen() { #if WINAPI_MSWINDOWS - return new CScreen(new CMSWindowsScreen(true, NULL, NULL)); + return new CScreen(new CMSWindowsScreen(true)); #elif WINAPI_XWINDOWS return new CScreen(new CXWindowsScreen(ARG->m_display, true)); #elif WINAPI_CARBON @@ -148,6 +148,16 @@ createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) // platform independent main // +enum EServerState { + kUninitialized, + kInitializing, + kInitializingToStart, + kInitialized, + kStarting, + kStarted +}; + +static EServerState s_serverState = kUninitialized; static CServer* s_server = NULL; static CScreen* s_serverScreen = NULL; static CPrimaryClient* s_primaryClient = NULL; @@ -155,6 +165,8 @@ static CClientListener* s_listener = NULL; static CServerTaskBarReceiver* s_taskBarReceiver = NULL; static CEvent::Type s_reloadConfigEvent = CEvent::kUnknown; static CEvent::Type s_forceReconnectEvent = CEvent::kUnknown; +static bool s_suspended = false; +static CEventQueueTimer* s_timer = NULL; CEvent::Type getReloadConfigEvent() @@ -224,6 +236,10 @@ handleScreenError(const CEvent&, void*) EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } + +static void handleSuspend(const CEvent& event, void*); +static void handleResume(const CEvent& event, void*); + static CScreen* openServerScreen() @@ -233,6 +249,14 @@ openServerScreen() screen->getEventTarget(), new CFunctionEventJob( &handleScreenError)); + EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(), + screen->getEventTarget(), + new CFunctionEventJob( + &handleSuspend)); + EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(), + screen->getEventTarget(), + new CFunctionEventJob( + &handleResume)); return screen; } @@ -243,6 +267,10 @@ closeServerScreen(CScreen* screen) if (screen != NULL) { EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), screen->getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(), + screen->getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getResumeEvent(), + screen->getEventTarget()); delete screen; } } @@ -319,80 +347,180 @@ closeServer(CServer* server) delete server; } +static bool initServer(); static bool startServer(); static void -retryStartHandler(const CEvent&, void* vtimer) +stopRetryTimer() +{ + if (s_timer != NULL) { + EVENTQUEUE->deleteTimer(s_timer); + EVENTQUEUE->removeHandler(CEvent::kTimer, NULL); + s_timer = NULL; + } +} + +static +void +retryHandler(const CEvent&, void*) { // discard old timer - CEventQueueTimer* timer = reinterpret_cast(vtimer); - EVENTQUEUE->deleteTimer(timer); - EVENTQUEUE->removeHandler(CEvent::kTimer, NULL); + assert(s_timer != NULL); + stopRetryTimer(); - // try starting the server again - LOG((CLOG_DEBUG1 "retry starting server")); - startServer(); + // try initializing/starting the server again + switch (s_serverState) { + case kUninitialized: + case kInitialized: + case kStarted: + assert(0 && "bad internal server state"); + break; + + case kInitializing: + LOG((CLOG_DEBUG1 "retry server initialization")); + s_serverState = kUninitialized; + if (!initServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + break; + + case kInitializingToStart: + LOG((CLOG_DEBUG1 "retry server initialization")); + s_serverState = kUninitialized; + if (!initServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + else if (s_serverState == kInitialized) { + LOG((CLOG_DEBUG1 "starting server")); + if (!startServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + } + break; + + case kStarting: + LOG((CLOG_DEBUG1 "retry starting server")); + s_serverState = kInitialized; + if (!startServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + break; + } } static bool -startServer() +initServer() { + // skip if already initialized or initializing + if (s_serverState != kUninitialized) { + return true; + } + double retryTime; CScreen* serverScreen = NULL; CPrimaryClient* primaryClient = NULL; - CClientListener* listener = NULL; try { CString name = ARG->m_config->getCanonicalName(ARG->m_name); serverScreen = openServerScreen(); primaryClient = openPrimaryClient(name, serverScreen); - listener = openClientListener(ARG->m_config->getSynergyAddress()); - s_server = openServer(*ARG->m_config, primaryClient); s_serverScreen = serverScreen; s_primaryClient = primaryClient; - s_listener = listener; + s_serverState = kInitialized; updateStatus(); - LOG((CLOG_NOTE "started server")); return true; } catch (XScreenUnavailable& e) { LOG((CLOG_WARN "cannot open primary screen: %s", e.what())); - closeClientListener(listener); closePrimaryClient(primaryClient); closeServerScreen(serverScreen); updateStatus(CString("cannot open primary screen: ") + e.what()); retryTime = e.getRetryTime(); } - catch (XSocketAddressInUse& e) { - LOG((CLOG_WARN "cannot listen for clients: %s", e.what())); - closeClientListener(listener); - closePrimaryClient(primaryClient); - closeServerScreen(serverScreen); - updateStatus(CString("cannot listen for clients: ") + e.what()); - retryTime = 10.0; - } catch (XScreenOpenFailure& e) { LOG((CLOG_CRIT "cannot open primary screen: %s", e.what())); - closeClientListener(listener); closePrimaryClient(primaryClient); closeServerScreen(serverScreen); return false; } catch (XBase& e) { LOG((CLOG_CRIT "failed to start server: %s", e.what())); - closeClientListener(listener); closePrimaryClient(primaryClient); closeServerScreen(serverScreen); return false; } + + if (ARG->m_restartable) { + // install a timer and handler to retry later + assert(s_timer == NULL); + LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); + s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer, + new CFunctionEventJob(&retryHandler, NULL)); + s_serverState = kInitializing; + return true; + } + else { + // don't try again + return false; + } +} + +static +bool +startServer() +{ + // skip if already started or starting + if (s_serverState == kStarting || s_serverState == kStarted) { + return true; + } + + // initialize if necessary + if (s_serverState != kInitialized) { + if (!initServer()) { + // hard initialization failure + return false; + } + if (s_serverState == kInitializing) { + // not ready to start + s_serverState = kInitializingToStart; + return true; + } + assert(s_serverState == kInitialized); + } + + double retryTime; + CClientListener* listener = NULL; + try { + listener = openClientListener(ARG->m_config->getSynergyAddress()); + s_server = openServer(*ARG->m_config, s_primaryClient); + s_listener = listener; + updateStatus(); + LOG((CLOG_NOTE "started server")); + s_serverState = kStarted; + return true; + } + catch (XSocketAddressInUse& e) { + LOG((CLOG_WARN "cannot listen for clients: %s", e.what())); + closeClientListener(listener); + updateStatus(CString("cannot listen for clients: ") + e.what()); + retryTime = 10.0; + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed to start server: %s", e.what())); + closeClientListener(listener); + return false; + } if (ARG->m_restartable) { // install a timer and handler to retry later + assert(s_timer == NULL); LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); - CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, - new CFunctionEventJob(&retryStartHandler, timer)); + s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer, + new CFunctionEventJob(&retryHandler, NULL)); + s_serverState = kStarting; return true; } else { @@ -405,14 +533,63 @@ static void stopServer() { - closeClientListener(s_listener); - closeServer(s_server); - closePrimaryClient(s_primaryClient); - closeServerScreen(s_serverScreen); - s_server = NULL; - s_listener = NULL; - s_primaryClient = NULL; - s_serverScreen = NULL; + if (s_serverState == kStarted) { + closeClientListener(s_listener); + closeServer(s_server); + s_server = NULL; + s_listener = NULL; + s_serverState = kInitialized; + } + else if (s_serverState == kStarting) { + stopRetryTimer(); + s_serverState = kInitialized; + } + assert(s_server == NULL); + assert(s_listener == NULL); +} + +static +void +cleanupServer() +{ + stopServer(); + if (s_serverState == kInitialized) { + closePrimaryClient(s_primaryClient); + closeServerScreen(s_serverScreen); + s_primaryClient = NULL; + s_serverScreen = NULL; + s_serverState = kUninitialized; + } + else if (s_serverState == kInitializing || + s_serverState == kInitializingToStart) { + stopRetryTimer(); + s_serverState = kUninitialized; + } + assert(s_primaryClient == NULL); + assert(s_serverScreen == NULL); + assert(s_serverState == kUninitialized); +} + +static +void +handleSuspend(const CEvent&, void*) +{ + if (!s_suspended) { + LOG((CLOG_INFO "suspend")); + stopServer(); + s_suspended = true; + } +} + +static +void +handleResume(const CEvent&, void*) +{ + if (s_suspended) { + LOG((CLOG_INFO "resume")); + startServer(); + s_suspended = false; + } } static @@ -517,7 +694,7 @@ mainLoop() IEventQueue::getSystemTarget()); EVENTQUEUE->removeHandler(getReloadConfigEvent(), IEventQueue::getSystemTarget()); - stopServer(); + cleanupServer(); updateStatus(); LOG((CLOG_NOTE "stopped server")); @@ -528,7 +705,11 @@ static int daemonMainLoop(int, const char**) { - CSystemLogger sysLogger(DAEMON_NAME); +#if SYSAPI_WIN32 + CSystemLogger sysLogger(DAEMON_NAME, false); +#else + CSystemLogger sysLogger(DAEMON_NAME, true); +#endif return mainLoop(); } @@ -536,6 +717,10 @@ static int standardStartup(int argc, char** argv) { + if (!ARG->m_daemon) { + ARCH->showConsole(false); + } + // parse command line parse(argc, argv); @@ -952,6 +1137,7 @@ public: // ILogOutputter overrides virtual void open(const char*) { } virtual void close() { } + virtual void show(bool) { } virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const { return ""; } }; @@ -997,11 +1183,27 @@ static int daemonNTStartup(int, char**) { - CSystemLogger sysLogger(DAEMON_NAME); + CSystemLogger sysLogger(DAEMON_NAME, false); bye = &byeThrow; return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop); } +static +int +foregroundStartup(int argc, char** argv) +{ + ARCH->showConsole(false); + + // parse command line + parse(argc, argv); + + // load configuration + loadConfig(); + + // never daemonize + return mainLoop(); +} + static void showError(HINSTANCE instance, const char* title, UINT id, const char* arg) @@ -1015,12 +1217,23 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { try { + CArchMiscWindows::setIcons((HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 32, 32, LR_SHARED), + (HICON)LoadImage(instance, + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 16, 16, LR_SHARED)); CArch arch(instance); CMSWindowsScreen::init(instance); CLOG; CThread::getCurrentThread().setPriority(-14); CArgs args; + // set title on log window + ARCH->openConsole((CString(kAppVersion) + " " + "Server").c_str()); + // windows NT family starts services using no command line options. // since i'm not sure how to tell the difference between that and // a user providing no options we'll assume that if there are no @@ -1028,8 +1241,13 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // users on NT can use `--daemon' or `--no-daemon' to force us out // of the service code path. StartupFunc startup = &standardStartup; - if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - startup = &daemonNTStartup; + if (!CArchMiscWindows::isWindows95Family()) { + if (__argc <= 1) { + startup = &daemonNTStartup; + } + else { + startup = &foregroundStartup; + } } // send PRINT and FATAL output to a message box diff --git a/cmd/synergys/synergys.dsp b/cmd/synergys/synergys.dsp index 7fdf1f01..a8d791de 100644 --- a/cmd/synergys/synergys.dsp +++ b/cmd/synergys/synergys.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\synergys.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\synergys.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -70,7 +70,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\synergys.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\synergys.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/cmd/synergys/synergys.ico b/cmd/synergys/synergys.ico index 23d9a0906462ab9c0d97b7493376c20036ea471e..89f965f4432c5f58054b9a1b0eae5bf6cbfd245d 100644 GIT binary patch literal 8478 zcmeI1KWH3B6vm(a*e8S|%Z-~Dq__}vXV}UrV&mT6#z|>aLkbt^+yrSM;8FwvhV6t% zLQiZmzTr%%8baza{{iY$oC5(ZeHZtwQ9U1vh;!S!}zl^B0qm9a)se4 z$4!nI!GV?&yT9dtAbB1-&RoiHIHXs=+duJMuQ>Qqlb(9p`ug^E1I+I3?(XmJkD_Ir zwoR+XpoZ`7n_*F5!?$xa7_9y@9UB~QjWDbDj>v-IqX1iNmO=`Jq0y9JcvU(jnEQ^w z7Gb2>!%ze~pRL_3zyv-RkH?YkoZDF1+}w=gwY9a?KK%1QIIFneKdQ#~i{M`c0A3Tt z1K}(%xF*;nf_mc~2;;GW3Worjn~JKt$bbM9zM*5w5O5oY2+>S!bo#xVIM#9I(&{w)eJBUN zeJAtt^Rlq8Am`4VlM5Fv$i<5n<({SK{)Lx2 zcf5QpPsRUH%CArVl&9mdeD%YRB+58+ffXEO7-rZqY#FvpZQOaKIPAI@9d?J^VOO^T zdx5=RP+%{x(@BZF#9m@AF_hRnW6#)^mO0~`5j-PMhEvA(o2BQJp&>&8GBjjp$k32^Z0N|)k*N+oJ*YAjjs+pbjlo20;DbJG3~oSXt2F(BaVG(BaTwr9y{8gF}NugO<*r!=b^Up|o%I zGL(=!;g7MESF3|6au@Kd)Z{MERFJeFL4l@%+y!7k?t?N}3)s-$(csbG(csbG(O}C74J8^p8ax_G zG!TXZJ{mk4JQ_S22+e^V4IT|14ITlW{3XFWjvkUDhGXo|;VrguoO~qT%DsE{eVYbJUo4A)*c53emtZek(@;r+A&?7`%alYMX$a zY+S?5oN9)eBHqA@i;?Pjweae)tI(w2f&-E#+${e5S>63iAM`TagzjPcMcFJ$Y(hZaAL zK9Rw|H_|&9)FI_tfX*qMLxQ;r%W>h`ir$IeFQ=8D75`u1H(R)5quI_B_inZY z`w#h31SN9V5+-_1#7`aC(~P4ezM76pkkD<1n(})TrS!0)eWSx9y(K8_{d=_ISy@{N zT9R7PBfXOV?+~X=Zo;=qR3)1r*J?T?JqdqOQS0|g$Q4UqeS6A`CiJxElT+yOJM;;M z4u68)b!hW@=$O(;r2KxiS{sjsKb@U=ME;xEhFTKipMZQE#eS#RJDbrUyt}d2IpX6> za(ySP_)ug{dwv(qXfHHQN7`?RK8(?u08Lb>z>#22Rc4!-|>%e>pWH7s3Yo%I-~CHeBwOgyeWaz zp{~oYs#C06tRuuw-%(X(t$cW&zD$T!j`%zECiCoXIRyaW$EkIkxjg-ydip!{^nLpK z@IIZrF`MQ1m2Nxb=7&PqIET$NlxiPcZLX`ax&{w}LnJfkI1B@$JJyFz#P!+JH%|8{ t;Zdj_)sxDeTJ=5vdoUQ9?87Q{NCkzlT_5sH8R_#vpACFJzU}{8{tX4i2$ui= literal 1078 zcmcIjF>ZrE5FC6+xQh8fs3Mn0+fY(a_&Id6Ct)f3ES};rr3*_b$?RURz;Y8=8GCnU z*gZJTV<5v0RaL_9wF5p_%QER>2D}B}$ZHj&Wn{>ifymkCh-|VGV=By~n5K!<8nb^f z&&C-1*da;TyJ!;jP^Q6N@Bgk5}L6UBBn>K0sicl|7 z>c~aHzIP$D4S{V|pr=T(rQ-7!*>yJK8teUVc(XssjjkWGz2AEMUHkF#ec5+9I81*g z>e-HOCD}%iv@d!g`Jq1~9Im|o1qc%Zpk4onNYgUHDh}lDlX-3f^Lfdr7>my*^5=;3 zI6L&T7iQ9ze6GqH(2HHmL*4TyYI|+Gsx*5&cPi41(wy8OavnuT5$#UelluIqoTrDl j{)%DX{(iHu1?m5W`_UE`Ag|c9SRp63>%}F_F6({(d|mCB diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index c8f7da21..117b1852 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -73,7 +73,25 @@ BEGIN POPUP "Synergy" BEGIN MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Show Log", IDC_TASKBAR_SHOW_LOG MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG + POPUP "Set Log Level" + BEGIN + MENUITEM "Error", IDC_TASKBAR_LOG_LEVEL_ERROR + + MENUITEM "Warning", IDC_TASKBAR_LOG_LEVEL_WARNING + + MENUITEM "Note", IDC_TASKBAR_LOG_LEVEL_NOTE + + MENUITEM "Info", IDC_TASKBAR_LOG_LEVEL_INFO + + MENUITEM "Debug", IDC_TASKBAR_LOG_LEVEL_DEBUG + + MENUITEM "Debug1", IDC_TASKBAR_LOG_LEVEL_DEBUG1 + + MENUITEM "Debug2", IDC_TASKBAR_LOG_LEVEL_DEBUG2 + + END MENUITEM "Reload Configuration", IDC_RELOAD_CONFIG MENUITEM "Force Reconnect", IDC_FORCE_RECONNECT MENUITEM SEPARATOR diff --git a/cmd/synergys/tb_error.ico b/cmd/synergys/tb_error.ico index a304dc1815fa0ababb136f6f1ba13735d2fbb5a6..746a87c9ec8ae70f24b4125afe9ca68defb2f6d1 100644 GIT binary patch delta 177 zcmYLI zdy84jGmU1R5{}3}fJpWtFkU;Su0ox*`K=tL?xB6&V)WT(qEfV(V;r)#M6RqNmiwRr k(H$x<10fE!BH#-5z_+~tpA(Rp{a$Tzz3iy3)m5)*KPi4Xc>n+a literal 318 zcmaKmI}U?D3`8Fa1(GSKQd6eP(da0%M{>W753q;H0Z_=8bSPr%nek^=GIWY*nn*l4 zxTlLbnZ=G?3UTN^DWIg3pGj2`5%OH@N3!o(iHaPhoKX)@YIUo1XqN6xF32N$#zwk% rh~{<7+ME_FZfttD==auOF(2NnD_ToW$P?mr8eX*h4C50%ue=|=dPPd3 diff --git a/cmd/synergys/tb_idle.ico b/cmd/synergys/tb_idle.ico index 9ba599de07841a8899275e54b91383ebdba39fb1..4e13a264b92f74bcb167895cbdaaca320254a8ab 100644 GIT binary patch delta 198 zcmXAhAr8Vo5Jg{?gaRSJ4M36LYK!42RZVc%8rTGdX&S`RK_Ke5L<2XeD|N_9X2ZDm z|NB#$+7$L=s@wvTSfp&A6208x_M?H3D~Q-wBF1VwJrP9d^Y6UlUBBsd F`vWq@KOF!7 literal 318 zcmZurAr8YZ5OcLkQ#4XEy23qgEVQs=fwj_)L?NM-@}RNK*GMJXU2NYux?F%xISd0V zo*i&E70elh4!syC2mhl9(7`k{5vxQgMQRuOaj@@J2}-k!&TwR~h6-SQi@40yOtNl# zA(87AkhwT%PDBb@%#1m$E;{BcpK8Iik~cV3LGuCL+q>RFM%&XC+ZR5MR$mHmRoS=p Mmq%Hp**0UdUu;rNsQ>@~ diff --git a/cmd/synergys/tb_run.ico b/cmd/synergys/tb_run.ico index 86aa9f3ac989981768b950663e06469635252f8e..88e160cbfcd029978599f49605ba1a3c7695027e 100644 GIT binary patch literal 318 zcmZvXJ#K_B5QRT>QHT}^QKbzPO1ZU9vz2R3fRNJr5IzCD8;(L}A7MN84cny1jNhAi z^COL+lJ|X&*-r&u76q#eLPafx?d1Px0X>%G9mGo6woTC*$N4x8%LKWVjJU*LBRA(t z*&)UlLNF;^_DhTq!s6VW^|KVov_j`difNtFX&-H(O$k3SDILcq=N9iD-8@T;1372! my*B6BBsAGS;Q0-Eqg$^!Uw{7 literal 318 zcmZ{fJqp7x428ddp@9UNsdL62FI!fRK2CNoI)D$6Bjf;u^vNdBp^^N4kR@b8r<|q< zk9P+zSHUZ@&=JI7mH$x$9b#-Emc`7ZesR{rdAbvnUUIsLlp^#g69w)PJ`2R?tWuD+=8@5=TLcpmWBg&BfSKQ6f<$ap>Mt@oPy0w4@W Ai2wiq diff --git a/cmd/synergys/tb_wait.ico b/cmd/synergys/tb_wait.ico index e94db3c30602626ad420b8a3a37d1bc0750baff8..257be0a1d1bc613eec8cc1838a1dfd25d4644844 100644 GIT binary patch literal 318 zcmZvXF%H5o3`Ji7QN#e9SYe77nRA*>nK?mJi9LtNNy<&SB_ktS@h=k+vHidOZA%U` zW?k2zcWvM#wvckMXxJFSxZpn+z?@1UcuF zl1i)Vw8|M$8oa;3u2z*2ycgFRqu9Ap#396Zhplt1gb?~ejL|uFp_CFr025R~TS5=- dGfb`By0-J}?~kW-COE!+Lz;S;(X4i~`vJXUO(y^V literal 318 zcmZ{fJr08~423^Lq_UN%Gh;_2Ux5R4W6hXj`2ZY(BQSKQ&xw#aw07Q)99vGHQ;y?E z#Iu7(s1TJ&=tyGl%D*Us4mmdw%i`|RxH#+KJi`gfCo? zvsu=yM(T~;jKz7$*8V=`Am$I&H5N7gUfJFO_XBRbFhLUP$0b)JnW(3|^]) + if test x"$acx_have_xkb" = xyes; then + AC_TRY_COMPILE([ + #include + #include + ],[ + XkbQueryExtension(0, 0, 0, 0, 0, 0); + ], + [acx_have_xkb=yes], + [acx_have_xkb=no]) + fi + fi + if test x"$acx_have_xkb" = xyes; then + AC_DEFINE(HAVE_XKB_EXTENSION, 1, + [Define this if the XKB extension is available.]) + fi + acx_have_xinerama=yes AC_CHECK_LIB(Xinerama, XineramaQueryExtension, @@ -149,7 +185,7 @@ if test x"$acx_host_winapi" = xXWINDOWS; then AC_MSG_CHECKING(for prototypes in X11/extensions/dpms.h) acx_have_dpms_protos=no AC_TRY_COMPILE([ - #include + #include extern "C" { #include } @@ -207,8 +243,25 @@ ACX_CXX_WARNINGS_ARE_ERRORS dnl adjust compiler and linker variables CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $ARCH_CFLAGS" +OBJCXXFLAGS="$OBJCXXFLAGS $CXXFLAGS $ARCH_CFLAGS" LIBS="$NANOSLEEP_LIBS $INET_ATON_LIBS $ARCH_LIBS $LIBS" +dnl we need to have an environment variable set when building on OS X. +dnl i'm not sure of the right way to do that. writing 'export ...' to +dnl the makefiles isn't portable. here we'll hijack XXXDEPMODE (where +dnl XXX depends on the language) to insert setting the environment +dnl variable when running the compiler. we'd like to put that in CC, +dnl CXX and OBJC but that breaks depcomp. let's hope this works. +if test x"$acx_host_winapi" = xCARBON; then + MACOSX_DEPLOYMENT_TARGET="10.2" + CCDEPMODE="MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET $CCDEPMODE" + CXXDEPMODE="MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET $CXXDEPMODE" + OBJCDEPMODE="MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET $OBJCDEPMODE" +else + MACOSX_DEPLOYMENT_TARGET="5" + CXXDEPMODE="FOO=$MACOSX_DEPLOYMENT_TARGET $CXXDEPMODE" +fi + AC_OUTPUT([ Makefile cmd/Makefile diff --git a/dist/nullsoft/synergy.nsi b/dist/nullsoft/synergy.nsi index e7d61938..bbc26cfc 100644 --- a/dist/nullsoft/synergy.nsi +++ b/dist/nullsoft/synergy.nsi @@ -62,20 +62,31 @@ Section "Synergy (required)" File COPYING.txt File ChangeLog.txt File ${DEPTH}\doc\PORTING + File ${DEPTH}\doc\about.html File ${DEPTH}\doc\authors.html File ${DEPTH}\doc\autostart.html + File ${DEPTH}\doc\banner.html File ${DEPTH}\doc\compiling.html File ${DEPTH}\doc\configuration.html + File ${DEPTH}\doc\contact.html File ${DEPTH}\doc\developer.html File ${DEPTH}\doc\faq.html File ${DEPTH}\doc\history.html + File ${DEPTH}\doc\home.html File ${DEPTH}\doc\index.html File ${DEPTH}\doc\license.html File ${DEPTH}\doc\news.html + File ${DEPTH}\doc\roadmap.html File ${DEPTH}\doc\running.html File ${DEPTH}\doc\security.html + File ${DEPTH}\doc\synergy.css File ${DEPTH}\doc\tips.html - File ${DEPTH}\doc\todo.html + File ${DEPTH}\doc\toc.html + File ${DEPTH}\doc\trouble.html + + SetOutPath $INSTDIR\images + File ${DEPTH}\doc\images\logo.gif + File ${DEPTH}\doc\images\warp.gif ; Write the installation path into the registry WriteRegStr HKLM SOFTWARE\Synergy "Install_Dir" "$INSTDIR" @@ -95,18 +106,26 @@ Section "Start Menu Shortcuts" CreateDirectory "$SMPROGRAMS\Synergy" CreateShortCut "$SMPROGRAMS\Synergy\Synergy.lnk" "$INSTDIR\synergy.exe" "" "$INSTDIR\synergy.exe" 0 CreateShortCut "$SMPROGRAMS\Synergy\README.lnk" "$INSTDIR\index.html" - CreateShortCut "$SMPROGRAMS\Synergy\FAQ.lnk" "$INSTDIR\faq.html" CreateShortCut "$SMPROGRAMS\Synergy\Synergy Folder.lnk" "$INSTDIR" CreateShortCut "$SMPROGRAMS\Synergy\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 SectionEnd +; Optional section (can be disabled by the user) +Section "Desktop Icon" + + CreateShortCut "$DESKTOP\Synergy.lnk" "$INSTDIR\synergy.exe" "" "$INSTDIR\synergy.exe" 0 + +SectionEnd + ;-------------------------------- ; Uninstaller Section "Uninstall" - + ; Stop and uninstall the daemons + ExecWait '"$INSTDIR\synergy.exe" /uninstall' + ; Remove autorun registry keys for synergy DeleteRegKey HKLM "SYSTEM\CurrentControlSet\Services\Synergy Server" DeleteRegKey HKLM "SYSTEM\CurrentControlSet\Services\Synergy Client" @@ -136,6 +155,7 @@ Section "Uninstall" ; Remove shortcuts, if any Delete "$SMPROGRAMS\Synergy\*.*" + Delete "$DESKTOP\Synergy.lnk" ; Remove directories used RMDir "$SMPROGRAMS\Synergy" diff --git a/dist/rpm/synergy.spec.in b/dist/rpm/synergy.spec.in index a4b368ba..0d2b6f48 100644 --- a/dist/rpm/synergy.spec.in +++ b/dist/rpm/synergy.spec.in @@ -3,7 +3,7 @@ Name: @PACKAGE@ Version: @VERSION@ Release: 1 License: GPL -Packager: Chris Schoeneman +Packager: Chris Schoeneman Group: System Environment/Daemons Prefixes: /usr/bin Source: @PACKAGE@-@VERSION@.tar.gz @@ -41,19 +41,26 @@ rm -rf $RPM_BUILD_ROOT %doc INSTALL %doc NEWS %doc README +%doc doc/about.html %doc doc/authors.html %doc doc/autostart.html +%doc doc/banner.html +%doc doc/border.html %doc doc/compiling.html %doc doc/configuration.html +%doc doc/contact.html %doc doc/developer.html %doc doc/faq.html %doc doc/history.html +%doc doc/home.html %doc doc/index.html %doc doc/license.html %doc doc/news.html +%doc doc/roadmap.html %doc doc/running.html %doc doc/security.html %doc doc/tips.html -%doc doc/todo.html +%doc doc/toc.html +%doc doc/trouble.html %doc doc/synergy.css %doc examples/synergy.conf diff --git a/doc/Makefile.am b/doc/Makefile.am index 45494563..2efec24c 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -17,20 +17,29 @@ EXTRA_DIST = \ PORTING \ doxygen.cfg.in \ synergy.css \ + about.html \ authors.html \ autostart.html \ + banner.html \ + border.html \ compiling.html \ configuration.html \ + contact.html \ developer.html \ faq.html \ history.html \ + home.html \ index.html \ license.html \ news.html \ + roadmap.html \ running.html \ security.html \ tips.html \ - todo.html \ + toc.html \ + trouble.html \ + images/logo.gif \ + images/warp.gif \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/doc/about.html b/doc/about.html new file mode 100644 index 00000000..aadd5764 --- /dev/null +++ b/doc/about.html @@ -0,0 +1,55 @@ + + + + + + + + About Synergy + + +

    +With synergy, all the computers on your desktop form a single virtual +screen. You use the mouse and keyboard of only one of the computers +while you use all of the monitors on all of the computers. +You tell synergy how many screens you have and their positions relative +to one another. Synergy then detects when the mouse moves off +the edge of a screen and jumps it instantly to the neighboring screen. +The keyboard works normally on each screen; input goes to whichever +screen has the cursor. +

    +In this example, the user is moving the mouse from left to right. +When the cursor reaches the right edge of the left screen it jumps +instantly to the left edge of the right screen. +

    +

    +

    +You can arrange screens side-by-side, above and below one another, +or any combination. You can even have a screen jump to the opposite +edge of itself. Synergy also understands multiple screens attached +to the same computer. +

    +Running a game and don't want synergy to jump screens? No problem. +Just toggle Scroll Lock. Synergy keeps the cursor on the same screen +when Scroll Lock is on. (This can be configured to another hot key.) +

    +Do you wish you could cut and paste between computers? Now you can! +Just copy text, HTML, or an image as you normally would on one screen +then switch to another screen and paste it. It's as if all your +computers shared a single clipboard (and separate primary selection for +you X11 users). It even converts newlines to each computer's native +form so cut and paste between different operating systems works +seamlessly. And it does it all in Unicode so any text can be copied. +

    +

    +Do you use a screen saver? With synergy all your screen savers act in +concert. When one starts they all start. When one stops they all +stop. And, if you require a password to unlock the screen, you'll +only have to enter a password on one screen. +

    +If you regularly use multiple computers on one desk, give synergy a +try. You'll wonder how you ever lived without it. +

    + + + diff --git a/doc/authors.html b/doc/authors.html index f908c0bf..bbf3fd10 100644 --- a/doc/authors.html +++ b/doc/authors.html @@ -1,38 +1,65 @@ - + - - Synergy Authors + + + + + Synergy Authors -

    Synergy Authors

    +

    Synergy Authors

    +

    - + + + + - + + + + - + + + + - + + + + - + + + + - + + + +
    Chris SchoenemanChris Schoeneman crs23@users.sourceforge.no_spam.net  Creator, owner, primary developer
    Ryan BreenRyan Breen ryan@ryanbreen.no_spam.com  Initial Mac OS X port
    Guido PoschtaGuido Poschta moolder@gmx.no_spam.net  Windows installer
    Bertrand Landry HetuBertrand Landry Hetu bertrand@landryhetu.no_spam.com  Mac OS X port
    Tom ChadwickTom Chadwick vttom@users.sourceforge.no_spam.net  PageUp/PageDown on X servers without mouse wheel support
    Brent PriddyBrent Priddy toopriddy@users.sourceforge.no_spam.net  Re-resolving server hostname on each connection
    +

    +To avoid spam bots, the above email addresses have ".no_spam" +hidden near the end. If you copy and paste the text be sure to +remove it.

    + diff --git a/doc/autostart.html b/doc/autostart.html index 9a194ce2..0343048a 100644 --- a/doc/autostart.html +++ b/doc/autostart.html @@ -1,41 +1,41 @@ - + - - Synergy Autostart Guide + + + + + Synergy Autostart Guide -

    Starting synergy automatically

    +

    Starting synergy automatically

    +

    You can configure synergy to start automatically when the computer starts or when you log in. The steps to do that are different on each platform. Note that changing these configurations doesn't actually start or stop synergy. The changes take effect the next time you start your computer or log in. -

    - -

    Windows

    -

    +

    +

    Windows

    +

    Start synergy and click the Configure... button by the text Automatic Startup. The Auto Start dialog will pop up. If an error occurs then correct the problem and click Configure again. -

    -

    +

    On the Auto Start dialog you'll configure synergy to start or not start automatically when the computer starts or when you log in. You need Administrator access rights to start synergy automatically when the computer starts. The dialog will let you know if you have sufficient permission. -

    -

    +

    If synergy is already configured to automatically start then there will be two Uninstall buttons, at most one of which is enabled. Click the enabled button, if any, to tell synergy to not start automatically. -

    -

    +

    If synergy is not configured to start automatically then there will be two Install buttons. If you have sufficient permission to have synergy start automatically when the @@ -46,10 +46,9 @@ In this case, synergy will be available during the login screen. Otherwise, click the Install button in the When You Log In box to have synergy automatically start when you log in. -

    - -

    Unix

    -

    +

    +

    Unix

    +

    Synergy requires an X server. That means a server must be running and synergy must be authorized to connect to that server. It's best to have the display manager start synergy. You'll need @@ -57,34 +56,33 @@ the necessary (probably root) permission to modify the display manager configuration files. If you don't have that permission you can start synergy after logging in via the .xsession file. -

    -

    +

    Typically, you need to edit three script files. The first file will start synergy before a user logs in, the second will kill that copy of synergy, and the third will start it again after the user logs in. -

    -

    +

    The contents of the scripts varies greatly between systems so there's no one definite place where you should insert your edits. However, these scripts often exit before reaching the bottom so put the edits near the top of the script. -

    -

    +

    The location and names of these files depend on the operating system and display manager you're using. A good guess for the -location is /etc/X11. Typical file names -are: -

    -

    +location is /etc/X11. If you use kdm +then try looking in /etc/kde3 or +/usr/kde/version/share/config. +Typical file names are: +

    + - - - - + + + +
          xdm    gdm
    1 xdm/Xsetup gdm/Init/Default (*)
    2 xdm/Xstartup gdm/PostLogin/Default (*)
    3 xdm/Xsession gdm/Sessions/Default (*, **)
          xdm    kdm    gdm
    1 xdm/Xsetup kdm/Xsetup gdm/Init/Default (*)
    2 xdm/Xstartup kdm/Xstartup gdm/PostLogin/Default (*)
    3 xdm/Xsession kdm/Xsession gdm/Sessions/Default (*, **)
    -

    -

    + +

    *) The Default file is used if no other suitable file is found. gdm will try displayname (e.g. :0) @@ -95,8 +93,7 @@ in that order, before and instead of Default. xdm/Xsession or dm/Xsession if gdm/Sessions/Default doesn't exist. -

    -

    +

    For a synergy client, add the following to the first file: /usr/bin/killall synergyc @@ -105,15 +102,13 @@ For a synergy client, add the following to the first file: Of course, the path to synergyc depends on where you installed it so adjust as necessary. -

    -

    +

    Add to the second file: /usr/bin/killall synergyc sleep 1 -

    -

    +

    And to the third file: /usr/bin/killall synergyc @@ -124,8 +119,7 @@ Note that <options>-f or --no-daemon or the script will never exit and you won't be able to log in. -

    -

    +

    The changes are the same for the synergy server except replace synergyc with synergys and use the appropriate synergys command @@ -135,38 +129,32 @@ file in root's home directory then in /etc. Make sure it exists in one of those places or use the --config config-pathname option to specify its location. -

    -

    +

    Note that some display managers (xdm and kdm, but not gdm) grab the keyboard and do not release it until the user logs in for security reasons. This prevents a synergy server from sharing the mouse and keyboard until the user logs in. It doesn't prevent a synergy client from synthesizing mouse and keyboard input, though. -

    -

    +

    If you're configuring synergy to start only after you log in then edit your .xsession file. Add just what you would add to the third file above. -

    - -

    Mac OS X

    -

    +

    +

    Mac OS X

    +

    [By Tor Slettnes] -

    -

    +

    There are three different ways to automatically start Synergy (client or server) on Mac OS X: -

    -

    +

    1. The first method involves creating a StartupItem at the system level, which is executed when the machine starts up or shuts down. This script will run in the background, and relaunch synergy as needed. -

      -

      +

      Pros:
      @@ -180,14 +168,12 @@ There are three different ways to automatically start Synergy
    2. -

      -

      +

    3. The second method will launch Synergy from the LoginWindow application, once a particular user has logged in. -

      -

      +

      Pros:
      @@ -202,13 +188,11 @@ There are three different ways to automatically start Synergy
    4. -

      -

      +

    5. The third method is to launch a startup script from the "Startup Items" tab under System Preferences -> Accounts. -

      -

      +

      Pros:
      @@ -222,17 +206,14 @@ There are three different ways to automatically start Synergy
    -

    -

    +

    The text below describes how to implement a Synergy client using the first two methods simultaneously. This way, Synergy is always running, and the clipboard is available when someone is logged in. A Mac OS X Synergy server setup will be quite similar. -

    -

    +

    1. Create a System Level Startup Item -

    -

    +

    • Open a Terminal window, and become root: @@ -250,8 +231,7 @@ logged in. A Mac OS X Synergy server setup will be quite similar. In this folder, create a new script file by the same name as the directory itself, Synergy. This script should contain the following text: -

      -

      +

      #!/bin/sh . /etc/rc.common @@ -291,12 +271,10 @@ RestartService ()   RunService "$1" -

      -

      +

      However, replace synergy-server with the actual name or IP address of your Synergy server. -

      -

      +

      Note that this scripts takes care not to start Synergy if another instance is currently running. This allows it to run in the background even when synergy is also @@ -312,39 +290,32 @@ RunService "$1"

    • In the same folder, create a file named StartupParameters.plist containing: -

      -

      +

      { Description = "Synergy Client"; - Provides = ("synergy-client"); - Requires = "Network"; + Provides = ("Synergy"); + Requires = ("Network"); OrderPreference = "None"; }

    -

    -

    +

    That's it! If you want to test this setup, you can run the -startup script as follow: -

    -

    +startup script as follows: +

    # /Library/StartupItems/Synergy/Synergy start -

    -

    +

    Any errors, as well as output from Synergy, will be shown in your terminal window. -

    -

    +

    Next time you reboot, Synergy should start automatically. -

    -

    +

    2. Run Synergy When a User Logs In -

    -

    +

    Each time a user successfully logs in via the console, the LoginWindow application creates a unique session cookie and stores it in the environment variable @@ -352,57 +323,48 @@ cookie and stores it in the environment variable to work, Synergy needs access to this environment variable. In other words, Synergy needs to be launched (directly or indirectly) via the LoginWindow application. -

    -

    +

    However, in order to kill any synergy processes started at the system level (as described above), we need root access. Thus, launching Synergy within the User's environment (e.g. via the Startup Items tab in System Preferences -> Accounts) is not an option that work in conjunction with the method above. -

    -

    +

    Fortunately, the LoginWindow application provides a "hook" for running a custom program (as root, with the username provided as the first and only argument) once a user has authenticated, but before the user is logged in. -

    -

    +

    Unfortunately, only one such hook is available. If you have already installed a Login Hook, you may need to add the text from below to your existing script, rather than creating a new one. -

    -

    +

    • Launch a Terminal window, and become root: $ sudo su - +
    • -

      -

      +

    • Find out if a LoginHook already exists: - # defaults read /Library/Preferences/com.apple.loginwindow LoginHook + # defaults read com.apple.loginwindow LoginHook -

      -

      This will either show the full path to a script or executable file, or the text: - The domain/default pair of (/Library/Preferences/com.apple.loginwindow, LoginHook) does not exist + The domain/default pair of (com.apple.loginwindow, LoginHook) does not exist In the former case, you need to modify your existing script, and/or create a "superscript" which in turn calls your existing script plus the one we will create here. -

      -

      +

      The rest of this text assumes that this item did not already exist, and that we will create a new script.

    • -

      -

    • Create a folder in which we will store our custom startup script: @@ -410,16 +372,13 @@ one. # mkdir -p /Library/LoginWindow
    • -

      -

    • In this folder, create a new script file (let's name it LoginHook.sh), containing the following text: -

      -

      +

      #!/bin/sh -prog=(/usr/local/bin/synergyc -n $(hostname -s) --camp ip-address-of-server) +prog=(/usr/local/bin/synergyc -n $(hostname -s) ip-address-of-server)   ### Stop any currently running Synergy client killall ${prog[0]##*/} @@ -428,19 +387,23 @@ killall ${prog[0]##*/} exec "${prog[@]}"

    • -

      -

      +

    • + Make this script executable: + + # chmod 755 /Library/LoginWindow/LoginHook.sh + +
    • Create a login hook to call the script you just created: - # defaults write /Library/Preferences/com.apple.loginwindow \ - LoginHook /Library/LoginWindow/LoginHook.sh + # defaults write com.apple.loginwindow LoginHook /Library/LoginWindow/LoginHook.sh - You can instead type the above all on one line but remove the backslash.
    -

    -

    +

    +More information on setting up login hooks can be found at +Apple. +

    When running the Synergy client, you may need to use the IP address of the Synergy server rather than its host name. Specifically, unless you have listed the server in your @@ -448,21 +411,18 @@ local /etc/hosts file or in your local NetInfo database, name services (i.e. DNS) may not yet be available by the time you log in after power-up. synergyc will quit if it cannot resolve the server name. -

    -

    +

    (This is not an issue with the previous method, because the -StartupItems.plist file specifies that this +StartupParameters.plist file specifies that this script should not be run until "network" is available). -

    -

    +

    3. Good Luck! -

    -

    +

    Remember to look in your system log on both your server and your client(s) for clues to any problems you may have (/var/log/system.log on your OS X box, typically /var/log/syslog on Linux boxes).

    - + diff --git a/doc/banner.html b/doc/banner.html new file mode 100644 index 00000000..eed66918 --- /dev/null +++ b/doc/banner.html @@ -0,0 +1,16 @@ + + + + + + + + Synergy Header + + + + + +
    Synergy
    + + diff --git a/doc/border.html b/doc/border.html new file mode 100644 index 00000000..ce45847a --- /dev/null +++ b/doc/border.html @@ -0,0 +1,14 @@ + + + + + + + + Synergy + + + +
    + + diff --git a/doc/compiling.html b/doc/compiling.html index 49f2c764..3fe5c393 100644 --- a/doc/compiling.html +++ b/doc/compiling.html @@ -1,39 +1,40 @@ - + - - Building and Installing Synergy + + + + + Building and Installing Synergy -

    Prerequisites for building

    +

    Prerequisites for building

    +

    To build synergy from the sources you'll need the following: -

    • Windows -
        -
      • VC++ 6.0 or up -
      - +
        +
      • VC++ 6.0 or up +
      +

    • Unix -
        -
      • gcc 2.95 or up -
      • X11R4 or up headers and libraries -
      - +
        +
      • gcc 2.95 or up +
      • X11R4 or up headers and libraries +
      +

    • Mac OS X -
        -
      • XCode; or gcc 2.95 or up -
      • Carbon development headers and libraries -
      +
        +
      • XCode; or gcc 2.95 or up +
      • Carbon development headers and libraries +
    -

    - -

    Configuring the build

    -

    +

    +

    Configuring the build

    +

    This step is not necessary when using VC++ or XCode. -

    -

    +

    To configure the build for your platform use the configure script:

       ./configure
    @@ -47,80 +48,73 @@ On Solaris you may need to use:
       ./configure --x-includes=/usr/openwin/include --x-libraries=/usr/openwin/lib
     
    so synergy can find the X11 includes and libraries. -

    - -

    Building

    +

    +

    Building

    • Windows -

      +

      Start VC++ and open synergy.dsw. Set the active configuration (Build > Set Active Configuration) to All - Debug or All - Release then build. Binaries are built into ./debug or ./build. -

      - +

    • Unix or Mac OS X without XCode -

      +

      Simply enter:

         make
         
      This will build the client and server and leave them in their respective source directories. -

      - +

    • Mac OS X with XCode -

      +

      Start XCode and open the synergy.xcode project. Build the all project using the Deployment flavor. -

      - +

    - -

    Installing

    +

    +

    Installing

    • Windows -

      - You'll need NSIS, the +

      + You'll need NSIS, the Nullsoft Scriptable Install System. Build All - Release then build Installer - Release. This creates SynergyInstaller.exe in the build directory. Run this to install synergy. -

      -

      +

      Alternatively, you can simply copy the following files from the debug or build directory to a directory you choose (perhaps under the Program Files directory): -

      • synergy.exe
      • synergyc.exe
      • synergys.exe
      • synrgyhk.dll
      -

      - +

    • Unix or Mac OS X without XCode -

      +

         make install
         
      will install the client and server into /usr/local/bin unless you specified a different directory when you ran configure. -

      - +

    • Mac OS X with XCode -

      +

      Copy the following files from ./build to a convenient location: - +

      • synergyc
      • synergys
      -

      +

      + diff --git a/doc/configuration.html b/doc/configuration.html index ca3f55f8..48b2cc2d 100644 --- a/doc/configuration.html +++ b/doc/configuration.html @@ -1,27 +1,30 @@ - + - - Synergy Configuration Guide + + + + + Synergy Configuration Guide -

      Synergy Configuration File Format

      +

      Synergy Configuration File Format

      +

      The synergy server requires configuration. It will try certain pathnames to load the configuration file if you don't specify a path using the --config command line option. synergys --help reports those pathnames. -

      -

      +

      The configuration file is a plain text file. Use any text editor to create the configuration file. The file is broken into sections and each section has the form: -

      +
         section: name
           args
         end
      -
      + Comments are introduced by # and continue to the end of the line. name must be one of the following: @@ -36,8 +39,7 @@ configuration file is case-sensitive so Section, SECTION, and section are all different and only the last is valid. Screen names are the exception; screen names are case-insensitive. -

      -

      +

      The file is parsed top to bottom and names cannot be used before they've been defined in the screens or aliases sections. So the @@ -45,9 +47,9 @@ they've been defined in the screens or must appear after the screens and links cannot refer to aliases unless the aliases appear before the links. -

      -

      screens

      -

      +

      +

      screens

      +

      args is a list of screen names, one name per line, each followed by a colon. Names are arbitrary strings but they must be unique. The hostname of each computer is recommended. (This @@ -60,30 +62,28 @@ screen can specify a number of options. Options have the form name = value and are listed one per line after the screen name. -

      -

      +

      Example: -

      +
           section: screens
      -    	moe:
      -    	larry:
      +     moe:
      +     larry:
                   halfDuplexCapsLock = true
                   halfDuplexNumLock = true
      -    	curly:
      +     curly:
                   meta = alt
           end
      -
      + This declares three screens named moe, larry, and curly. Screen larry has half-duplex Caps Lock and Num Lock keys (see below) and screen curly converts the meta modifier key to the alt modifier key. -

      -

      +

      A screen can have the following options:

      • halfDuplexCapsLock = {true|false} -

        +

        This computer has a Caps Lock key that doesn't report a press and a release event when the user presses it but instead reports a press event when it's turned on and a @@ -93,22 +93,32 @@ A screen can have the following options: on the server screen. If it acts strangely on one screen then that screen may need the option set to true. -

        +

      • halfDuplexNumLock = {true|false} -

        +

        This is identical to halfDuplexCapsLock except it applies to the Num Lock key. -

        +

      • halfDuplexScrollLock = {true|false} -

        +

        This is identical to halfDuplexCapsLock - except it applies to the Scroll Lock key. Note that synergy uses - Scroll Lock to keep the cursor on the current screen. That is, - when Scroll Lock is toggled on, the cursor is locked to the screen - that it's currently on. Use it to prevent accidental switching. -

        + except it applies to the Scroll Lock key. Note that, by default, + synergy uses Scroll Lock to keep the cursor on the current screen. That + is, when Scroll Lock is toggled on, the cursor is locked to the screen + that it's currently on. You can use that to prevent accidental switching. + You can also configure other hot keys to do that; see + lockCursorToScreen. +

        +

      • switchCorners = <corners> +

        + See switchCorners below. +

        +

      • switchCornerSize = N +

        + See switchCornerSize below. +

      • xtestIsXineramaUnaware = {true|false} -

        +

        This option works around a bug in the XTest extension when used in combination with Xinerama. It affects X11 clients only. Not all versions of the XTest @@ -118,33 +128,31 @@ A screen can have the following options: currently true by default. If you know your XTest extension is Xinerama aware then set this option to false. -

        +

      • shift = {shift|ctrl|alt|meta|super|none}
        ctrl = {shift|ctrl|alt|meta|super|none}
        alt = {shift|ctrl|alt|meta|super|none}
        meta = {shift|ctrl|alt|meta|super|none}
        super = {shift|ctrl|alt|meta|super|none}
        -

        +

        Map a modifier key pressed on the server's keyboard to a different modifier on this client. This option only has an effect on a client screen; it's accepted and ignored on the server screen. -

        -

        +

        You can map, say, the shift key to shift (the default), - ctrl, alt, meta, super or nothing. Normally, you + ctrl, alt, meta, super or nothing. Normally, you wouldn't remap shift or ctrl. You might, however, have an X11 server with meta bound to the Alt keys. To use this server effectively with a windows client, which doesn't use meta but uses alt extensively, you'll want the windows client to map meta to alt (using meta = alt). -

        +

      -

      - -

      aliases

      -

      +

      +

      aliases

      +

      args is a list of screen names just like in the screens section except each screen is followed by a list of aliases, one per line, not followed @@ -152,102 +160,163 @@ A screen can have the following options: screen name lookup each alias is equivalent to the screen name it aliases. So a client can connect using its canonical screen name or any of its aliases. -

      -

      +

      Example: -

      +
           section: aliases
               larry:
                   larry.stooges.com
               curly:
                   shemp
           end
      -
      + Screen larry is also known as - larry.stooges.com and can connect as + larry.stooges.com and can connect as either name. Screen curly is also known as shemp (hey, it's just an example). -

      - -

      links

      -

      +

      +

      links

      +

      args is a list of screen names just like in the screens section except each screen is followed by a list of links, one per line. Each link has the - form {left|right|up|down} = - name. A link indicates which screen is - adjacent in the given direction. -

      -

      + form {left|right|up|down}[<range>] = + name[<range>]. A link indicates which + screen is adjacent in the given direction. +

      + Each side of a link can specify a range which defines a portion + of an edge. A range on the direction is the portion of edge you can + leave from while a range on the screen is the portion of edge you'll + enter into. Ranges are optional and default to the entire edge. All + ranges on a particular direction of a particular screen must not + overlap. +

      + A <range> is written as (<start>,<end>). + Both start and end + are percentages in the range 0 to 100, inclusive. The start must be + less than the end. 0 is the left or top of an edge and 100 is the + right or bottom. +

      Example: -

      +
           section: links
               moe:
      -            right = larry
      -            up    = curly
      +            right        = larry
      +            up(50,100)   = curly(0,50)
               larry:
      -            left  = moe
      -            up    = curly
      +            left         = moe
      +            up(0,50)     = curly(50,100)
               curly:
      -            down  = larry
      +            down(0,50)   = moe
      +            down(50,100) = larry(0,50)
           end
      -
      + This indicates that screen larry is to the right of screen moe (so moving the cursor off the right edge of moe would make it appear at the left edge of larry), - curly is above moe, + the left half of + curly is above the right half of + moe, moe is to the left of - larry, curly is - above larry, and - larry is below - curly. Note that links do not have to be - symmetrical; moving up from moe then down - from curly lands the cursor on - larry. -

      - -

      options

      -

      + larry (edges are not necessarily symmetric + so you have to provide both directions), the right half of + curly is above the left half of + larry, all of moe + is below the left half of curly, and the + left half of larry is below the right half of + curly. +

      + Note that links do not have to be + symmetrical; for instance, here the edge between + moe and curly + maps to different ranges depending on if you're going up or down. + In fact links don't have to be bidirectional. You can configure + the right of moe to go to + larry without a link from the left of + larry to moe. + It's possible to configure a screen with no outgoing links; the + cursor will get stuck on that screen unless you have a hot key + configured to switch off of that screen. +

      +

      options

      +

      args is a list of lines of the form name = value. These set the global options. -

      -

      +

      Example: -

      +
           section: options
               heartbeat = 5000
               switchDelay = 500
           end
      -
      -

      -

      + +

      You can use the following options:

      • heartbeat = N -

        +

        The server will expect each client to send a message no less than every N milliseconds. If no message arrives from a client within 3N seconds the server forces that client to disconnect. -

        -

        +

        If synergy fails to detect clients disconnecting while the server is sleeping or vice versa, try using this option. -

        +

        +

      • switchCorners = <corners> +

        + Synergy won't switch screens when the mouse reaches the edge of + the screen if it's in a listed corner. The size of all corners + is given by the switchCornerSize + option. +

        + Corners are specified by a list using the following names: +

          +
        • none -- no corners +
        • top-left -- the top left corner +
        • top-right -- the top right corner +
        • bottom-left -- the bottom left corner +
        • bottom-right -- the bottom right corner +
        • left -- top and bottom left corners +
        • right -- top and bottom right corners +
        • top -- left and right top corners +
        • bottom -- left and right bottom corners +
        • all -- all corners +
        +

        + The first name in the list is one of the above names and defines + the initial set of corners. Subsequent names are prefixed with + + or - to add the corner to or remove the corner from the set, + respectively. For example: +

        + + all -left +top-left + +

        + starts will all corners, removes the left corners (top and bottom) + then adds the top-left back in, resulting in the top-left, + bottom-left and bottom-right corners. +

        +

      • switchCornerSize = N +

        + Sets the size of all corners in pixels. The cursor must be within + N pixels of the corner to be considered + to be in the corner. +

      • switchDelay = N -

        +

        Synergy won't switch screens when the mouse reaches the edge of a screen unless it stays on the edge for N milliseconds. This helps prevent unintentional switching when working near the edge of a screen. -

        +

      • switchDoubleTap = N -

        +

        Synergy won't switch screens when the mouse reaches the edge of a screen unless it's moved away from the edge and then back to the edge within N @@ -255,29 +324,337 @@ A screen can have the following options: the option you have to quickly tap the edge twice to switch. This helps prevent unintentional switching when working near the edge of a screen. -

        +

      • screenSaverSync = {true|false} -

        - If set to false then synergy - won't synchronize screen savers. Client screen savers - will start according to their individual configurations. - The server screen saver won't start if there is input, - even if that input is directed toward a client screen. -

        +

        + If set to false then synergy + won't synchronize screen savers. Client screen savers + will start according to their individual configurations. + The server screen saver won't start if there is input, + even if that input is directed toward a client screen. +

      • relativeMouseMoves = {true|false} -

        - If set to true then secondary - screens move the mouse using relative rather than - absolute mouse moves when and only when Scroll Lock is - toggled on (i.e. the cursor is locked to the screen). - This is intended to make synergy work better with certain - games. If set to false or not - set then all mouse moves are absolute. -

        +

        + If set to true then secondary + screens move the mouse using relative rather than absolute + mouse moves when and only when the cursor is locked to the + screen (by Scroll Lock or a configured + hot key). + This is intended to make synergy work better with certain + games. If set to false or not + set then all mouse moves are absolute. +

        +

      • keystroke(key) = actions +

        + Binds the key combination key to the + given actions. key + is an optional list of modifiers (shift, + control, alt, + meta or super) + optionally followed by a character or a key name, all separated by + + (plus signs). You must have either + modifiers or a character/key name or both. See below for + valid key names. +

        + Actions are described below. +

        + Keyboard hot keys are handled while the cursor is on the primary + screen and secondary screens. Separate actions can be assigned + to press and release. +

        +

      • mousebutton(button) = actions +

        + Binds the modifier and mouse button combination + button to the given + actions. button + is an optional list of modifiers (shift, + control, alt, + meta or super) + followed by a button number. The primary button (the + left button for right handed users) is button 1, the middle button + is 2, etc. +

        + Actions are described below. +

        + Mouse button actions are not handled while the cursor is on the + primary screen. You cannot use these to perform an action while + on the primary screen. Separate actions can be assigned to press + and release. +

      You can use both the switchDelay and switchDoubleTap options at the same time. Synergy will switch when either requirement is satisfied. +

      +Actions are two lists of individual actions separated +by commas. The two lists are separated by a semicolon. Either list can be +empty and if the second list is empty then the semicolon is optional. The +first list lists actions to take when the condition becomes true (e.g. the +hot key or mouse button is pressed) and the second lists actions to take +when the condition becomes false (e.g. the hot key or button is released). +The condition becoming true is called activation and becoming false is +called deactivation. +Allowed individual actions are: +

        +
      • keystroke(key[,screens]) +
      • keyDown(key[,screens]) +
      • keyUp(key[,screens]) +

        + Synthesizes the modifiers and key given in key + which has the same form as described in the + keystroke option. If given, + screens lists the screen or screens to + direct the event to, regardless of the active screen. If not + given then the event is directed to the active screen only. +

        + keyDown synthesizes a key press and + keyUp synthesizes a key release. + keystroke synthesizes a key press on + activation and a release on deactivation and is equivalent to + a keyDown on activation and + keyUp on deactivation. +

        + screens is either * + to indicate all screens or a colon (:) separated list of screen + names. (Note that the screen name must have already been encountered + in the configuration file so you'll probably want to put actions at + the bottom of the file.) +

        +

      • mousebutton(button) +
      • mouseDown(button) +
      • mouseUp(button) +

        + Synthesizes the modifiers and mouse button given in + button + which has the same form as described in the + mousebutton option. +

        + mouseDown synthesizes a mouse press and + mouseUp synthesizes a mouse release. + mousebutton synthesizes a mouse press on + activation and a release on deactivation and is equivalent to + a mouseDown on activation and + mouseUp on deactivation. +

        +

      • lockCursorToScreen(mode) +

        + Locks the cursor to or unlocks the cursor from the active screen. + mode can be off + to unlock the cursor, on to lock the + cursor, or toggle to toggle the current + state. The default is toggle. If the + configuration has no lockCursorToScreen + action and Scroll Lock is not used as a hot key then Scroll Lock + toggles cursor locking. +

        +

      • switchToScreen(screen) +

        + Jump to screen with name or alias screen. +

        +

      • switchInDirection(dir) +

        + Switch to the screen in the direction dir, + which may be one of left, + right, up or + down. +

        +

      +

      +Examples: +

        +
      • keystroke(alt+left) = switchInDirection(left) +

        + Switches to the screen to left when the left arrow key is pressed + in combination with the Alt key. +

        +

      • keystroke(shift+control+alt+super) = switchToScreen(moe) +

        + Switches to screen moe when all of the + Shift, Control, Alt, and Super modifier keys are pressed together. +

        +

      • keystroke(alt+f1) = ; lockCursorToScreen(toggle) +

        + Toggles locking the cursor to the screen when Alt+F1 is released. +

        +

      • mousebutton(2) = mouseDown(control+1) ; mouseUp(control+1) +

        + While on a secondary screen clicking the middle mouse button will + become a Control click of the primary button. +

        +

      • keystroke(super+f1) = keystroke(super+L,larry), keystroke(control+alt+delete,curly) +

        + Pressing Super+F1 (on any screen) will synthesize Super+L on screen + larry and Control+Alt+Delete on screen + curly. +

        +

      +

      +Valid key names are: +

        +
      • AppMail +
      • AppMedia +
      • AppUser1 +
      • AppUser2 +
      • AudioDown +
      • AudioMute +
      • AudioNext +
      • AudioPlay +
      • AudioPrev +
      • AudioStop +
      • AudioUp +
      • BackSpace +
      • Begin +
      • Break +
      • Cancel +
      • CapsLock +
      • Clear +
      • Delete +
      • Down +
      • Eject +
      • End +
      • Escape +
      • Execute +
      • F1 +
      • F2 +
      • F3 +
      • F4 +
      • F5 +
      • F6 +
      • F7 +
      • F8 +
      • F9 +
      • F10 +
      • F11 +
      • F12 +
      • F13 +
      • F14 +
      • F15 +
      • F16 +
      • F17 +
      • F18 +
      • F19 +
      • F20 +
      • F21 +
      • F22 +
      • F23 +
      • F24 +
      • F25 +
      • F26 +
      • F27 +
      • F28 +
      • F29 +
      • F30 +
      • F31 +
      • F32 +
      • F33 +
      • F34 +
      • F35 +
      • Find +
      • Help +
      • Home +
      • Insert +
      • KP_0 +
      • KP_1 +
      • KP_2 +
      • KP_3 +
      • KP_4 +
      • KP_5 +
      • KP_6 +
      • KP_7 +
      • KP_8 +
      • KP_9 +
      • KP_Add +
      • KP_Begin +
      • KP_Decimal +
      • KP_Delete +
      • KP_Divide +
      • KP_Down +
      • KP_End +
      • KP_Enter +
      • KP_Equal +
      • KP_F1 +
      • KP_F2 +
      • KP_F3 +
      • KP_F4 +
      • KP_Home +
      • KP_Insert +
      • KP_Left +
      • KP_Multiply +
      • KP_PageDown +
      • KP_PageUp +
      • KP_Right +
      • KP_Separator +
      • KP_Space +
      • KP_Subtract +
      • KP_Tab +
      • KP_Up +
      • Left +
      • LeftTab +
      • Linefeed +
      • Menu +
      • NumLock +
      • PageDown +
      • PageUp +
      • Pause +
      • Print +
      • Redo +
      • Return +
      • Right +
      • ScrollLock +
      • Select +
      • Sleep +
      • Space +
      • SysReq +
      • Tab +
      • Undo +
      • Up +
      • WWWBack +
      • WWWFavorites +
      • WWWForward +
      • WWWHome +
      • WWWRefresh +
      • WWWSearch +
      • WWWStop +
      • Space +
      • Exclaim +
      • DoubleQuote +
      • Number +
      • Dollar +
      • Percent +
      • Ampersand +
      • Apostrophe +
      • ParenthesisL +
      • ParenthesisR +
      • Asterisk +
      • Plus +
      • Comma +
      • Minus +
      • Period +
      • Slash +
      • Colon +
      • Semicolon +
      • Less +
      • Equal +
      • Greater +
      • Question +
      • At +
      • BracketL +
      • Backslash +
      • BracketR +
      • Circumflex +
      • Underscore +
      • Grave +
      • BraceL +
      • Bar +
      • BraceR +
      • Tilde +
      +Additionally, a name of the form \uXXXX where +XXXX is a hexadecimal number is interpreted as +a unicode character code. +Key and modifier names are case-insensitive. Keys that don't exist on +the keyboard or in the default keyboard layout will not work.

      + diff --git a/doc/contact.html b/doc/contact.html new file mode 100644 index 00000000..1b378b85 --- /dev/null +++ b/doc/contact.html @@ -0,0 +1,44 @@ + + + + + + + + Synergy Contact Info + + +

      +Use the following addresses to contact the synergy project: +

      + + + + + + + + + + + + + + + + +
      Bug reports: Add Synergy Bug
      Help: synergy-help@groundhog.pair..no_spamcom
      General: crs23@users.sourceforge..no_spamnet
      +

      +To avoid spam bots, the above email addresses have ".no_spam" +hidden near the end. If you copy and paste the text be sure to +remove it. +

      +Please check the + +bug list before reporting a bug. You may also find answers at the +synergy forums. +Emails for help asking questions answered on this site will go unanswered. +

      + + + diff --git a/doc/developer.html b/doc/developer.html index 7391dd2a..acfdff9a 100644 --- a/doc/developer.html +++ b/doc/developer.html @@ -1,27 +1,27 @@ - + - - Synergy Developer's Guide + + + + + Synergy Developer Documentation -

      Developer's Guide

      Synergy is reasonably well commented so reading the source code should be enough to understand particular pieces. See the -doc/PORTING +doc/PORTING file in the synergy source code for more high-level information. -

      - -

      How it works

      -

      +

      +

      How it works

      +

      The theory behind synergy is simple: the server captures mouse, keyboard, clipboard, and screen saver events and forwards them to one or more clients. If input is directed to the server itself then the input is delivered normally. In practice, however, many complications arise. -

      -

      +

      First, different keyboard mappings can produce different characters. Synergy attempts to generate the same character on the client as would've been generated on the server, including appropriate modifier @@ -34,8 +34,7 @@ keyboard. For example, if the client or server can't distinguish between the left and right shift keys then synergy can't be certain to synthesize the shift on the same side of the keyboard as the user pressed. -

      -

      +

      Second, different systems have different clipboards and clipboard formats. The X window system has a system-wide selection and clipboard (and yet other buffers) while Microsoft Windows has only @@ -43,15 +42,13 @@ a system-wide clipboard. Synergy has to choose which of these buffers correspond to one another. Furthermore, different systems use different text encodings and line breaks. Synergy mediates and converts between them. -

      -

      +

      Finally, there are no standards across operating systems for some operations that synergy requires. Among these are: intercepting and synthesizing events; enabling, disabling, starting and stopping the screen saver; detecting when the screen saver starts; reading and writing the clipboard(s). -

      -

      +

      All this means that synergy must be customized to each operating system (or windowing system in the case of X windows). Synergy breaks platform differences into two groups. The first includes @@ -60,25 +57,25 @@ multithreading, network I/O, multi-byte and wide character conversion, time and sleeping, message display and logging, and running a process detached from a terminal. This code lives in lib/arch. -

      -

      +

      The second includes screen and window management handling, user event handling, event synthesis, the clipboards, and the screen saver. This code lives in lib/platform. -

      -

      +

      For both groups, there are particular classes or interfaces that must be inherited and implemented for each platform. See the -doc/PORTING file in -the synergy source code for more information. -

      -

      Auto-generated Documentation

      -

      +doc/PORTING file in the synergy source +code for more information. +

      +

      Auto-generated Documentation

      +

      Synergy can automatically generate documentation from the comments -in the code using doxygen. -Use "make doxygen" to build it yourself +in the code using doxygen. +Use make doxygen to build it yourself from the source code into the doc/doxygen/html directory.

      +

      + diff --git a/doc/faq.html b/doc/faq.html index 1b355594..b9696391 100644 --- a/doc/faq.html +++ b/doc/faq.html @@ -1,14 +1,18 @@ - + - - Synergy FAQ + + + + + Synergy Frequently Asked Questions +

      Synergy Frequently Asked Questions

      - +

      Questions

      -
        +
        1. Why doesn't ctrl+alt+del work on secondary screens?
        2. Can the server and client be using different operating systems?
        3. What's the difference between synergy and x2x, x2vnc, etc? @@ -27,27 +31,42 @@
        4. I get the error 'Xlib: No protocol specified'. Why?
        5. The cursor goes to secondary screen but won't come back. Why?
        6. The cursor wraps from one edge of the screen to the opposite. Why? +
        7. How do I stop my game from minimizing when I leave the screen?
        -

        Answers

        -
          +
          1. Why doesn't ctrl+alt+del work on secondary screens?

            Synergy isn't able to capture ctrl+alt+del on PC compatible - systems because it's handled completely differently than + primary screens because it's handled completely differently than other keystrokes. However, when the mouse is on a client screen, pressing ctrl+alt+pause will simulate ctrl+alt+del on the client. (A client running on Windows NT, 2000, or XP - must be running as a service for this to work.) + must be configured to autostart when the computer starts for + this to work.) +

            + On a primary screen running on an OS X system, you can use + ctrl+command+del. Using the pause key isn't necessary since OS X + doesn't treat ctrl+command+del differently. And using the pause + key isn't usually possible because there isn't one on most OS X + systems. Use command instead of option/alt because + the command key, not the option/alt key, maps to alt on windows. + The reason is because the command key is in the same physical + location and performs the same general function (menu shortcuts) + as alt on a windows system. This mapping can be modified in + the configuration. +

            + On mac laptops, the key labeled "delete" is actually backspace + and ctrl+command+delete won't work. However fn+delete really + is delete so fn+ctrl+command+delete will act as ctrl+alt+del + on a windows secondary screen.

            -
          2. Can the server and client be using different operating systems?

            Yes. The synergy network protocol is platform neutral so synergy doesn't care what operating systems are running on the server and clients.

            -
          3. What's the difference between synergy and x2x, x2vnc, etc?

            @@ -60,7 +79,6 @@ However, the right tool for the job is whatever tool works best for you.

            -
          4. What does "Cannot initialize hook library" mean?

            This error can occur on a synergy server running on a @@ -70,7 +88,6 @@ not then try logging off and back on or rebooting then starting synergy again.

            -
          5. What security/encryption does synergy provide?

            Synergy provides no built-in encryption or authentication. @@ -78,33 +95,30 @@ network, especially the Internet. It's generally fine for home networks. Future versions may provide built-in encryption and authentication. -

            -

            +

            Strong encryption and authentication is available through SSH (secure shell). Run the SSH daemon (i.e. server) on the same computer that you run the synergy server. It requires no special configuration to support synergy. On each synergy client system, run SSH with port forwarding: -

            +

                     ssh -f -N -L 24800:server-hostname:24800 server-hostname
             
            -

            +

            where server-hostname is the name of the - SSH/synergy server. + SSH/synergy server. Once ssh authenticates itself, start the synergy client normally except use localhost or 127.0.0.1 as the server's address. SSH will then encrypt all communication on behalf of synergy. Authentication is handled by the SSH authentication. -

            -

            +

            A free implementation of SSH for Linux and many Unix systems is - OpenSSH. For + OpenSSH. For Windows there's a port of OpenSSH using - Cygwin. + Cygwin.

            -
          6. What should I call my screens in the configuration?

            You can use any unique name in the configuration file for each @@ -115,8 +129,7 @@ xyz.foo.com. If you don't use the computer's hostname, you have to tell synergy the name of the screen using a command line option, or the startup dialog on Windows. -

            -

            +

            Some systems are configured to report the fully qualified domain name as the hostname. For those systems it will be easier to use the FQDN as the screen name. Also note that a Mac OS X system @@ -124,32 +137,29 @@ xyz.local. If that's the case for you then use xyz.local as the screen name.

            -
          7. Why do my Caps-Lock, Num-Lock, Scroll-Lock keys act funny?

            Some systems treat the Caps-Lock, Num-Lock, and Scroll-Lock keys - differently than all the others. Whereas most keys report going down - when physically pressed and going up when physically released, on - these systems the keys report going down when being activated and - going up when being deactivated. That is, when you press and release, - say, Caps-Lock to activate it, it only reports going down, and when - you press and release to deactivate it, it only reports going up. - This confuses synergy. -

            -

            + differently than all the others. Whereas most keys report going down + when physically pressed and going up when physically released, on + these systems the Caps-Lock and Num-Lock keys report going down + when being activated and going up when being deactivated. That + is, when you press and release, say, Caps-Lock to activate it, it + only reports going down, and when you press and release to + deactivate it, it only reports going up. This confuses synergy. +

            You can solve the problem by changing your configuration file. In the screens section, following each screen that has the - problem, add any or all of these lines as appropriate: -

            + problem, any or all of these lines as appropriate: +

                     halfDuplexCapsLock = true
                     halfDuplexNumLock = true
                     halfDuplexScrollLock = true
             
            -

            +

            Then restart synergy on the server or reload the configuration.

            -
          8. Can synergy share the display in addition to the mouse and keyboard?

            No. Synergy is a KM solution not a KVM (keyboard, video, mouse) @@ -157,13 +167,11 @@ Hopefully, this will make synergy suitable for managing large numbers of headless servers.

            -
          9. Can synergy do drag and drop between computers?

            No. That's a very cool idea and it'll be explored. However, it's also clearly difficult and may take a long time to implement.

            -
          10. Does AltGr/Mode-Switch/ISO_Level3_Shift work?

            Yes, as of 1.0.12 synergy has full support for AltGr/Mode-switch. @@ -173,7 +181,6 @@ layout cannot be generated by synergy.) There is experimental support for ISO_Level3_Shift in 1.1.3.

            -
          11. Why isn't synergy ported to platform XYZ?

            Probably because the developers don't have access to platform XYZ @@ -181,7 +188,6 @@ inherently non-portable aspects so there's a not insignificant effort involved in porting.

            -
          12. My client can't connect. What's wrong?

            A common mistake when starting the client is to give the wrong @@ -192,36 +198,33 @@ socket followed by the attempt to connect was forcefully rejected or connection refused then the server isn't started, can't bind the address, or the client is connecting to the wrong - host name/address or port. + host name/address or port. See the + troublshooting page for more help.

            -
          13. Linking fails on Solaris. What's wrong?

            - Did you add -

            + Did you add +

                     --x-includes=/usr/openwin/include --x-libraries=/usr/openwin/lib
             
            -

            +

            to the configure command line? Solaris puts the X11 includes and libraries in an unusual place and the above lets synergy find them.

            -
          14. The screen saver never starts. Why not?

            If the synergy server is on X Windows then the screen saver will not start while the mouse is on a client screen. This is a consequence of how X Windows, synergy and xscreensaver work.

            -
          15. I can't switch screens anymore for no apparent reason. Why?

            This should not happen with 1.1.3 and up. Earlier versions of synergy would not allow switching screens when a key was down and sometimes it would believe a key was down when it was not.

            -
          16. I get the error 'Xlib: No protocol specified'. Why?

            You're running synergy without authorization to connect to the @@ -229,7 +232,6 @@ logged in as non-root. Just run synergy as the same user that's logged in.

            -
          17. The cursor goes to secondary screen but won't come back. Why?

            Your configuration is incorrect. You must indicate the neighbors @@ -237,7 +239,6 @@ the left of 'Orange' does not mean that 'Orange' is to the right of 'Apple'. You must provide both in the configuration.

            -
          18. The cursor wraps from one edge of the screen to the opposite. Why?

            Because you told it to. If you list 'Orange' to be to the left of @@ -245,6 +246,21 @@ make it jump to the right edge. Remove the offending line from the configuration if you don't want that behavior.

            - +
          19. How do I stop my game from minimizing when I leave the screen? +

            + Many full screen applications, particularly games, automatically + minimize when they're no longer the active (foreground) application + on Microsoft Windows. The synergy server normally becomes the foreground + when you switch to another screen in order to more reliably capture all + user input causing those full screen applications to minimize. To + prevent synergy from stealing the foreground just click "Options..." + and check "Don't take foreground window on Windows servers." If you + turn this on then be aware that synergy may not function correctly when + certain programs, particularly the command prompt, are the foreground + when you switch to other screens. Simply make a different program the + foreground before switching to work around that. +

            +
          + diff --git a/doc/history.html b/doc/history.html index dedbb016..48f921c3 100644 --- a/doc/history.html +++ b/doc/history.html @@ -1,12 +1,16 @@ - + - - Synergy History + + + + + Synergy History -

          Synergy History

          +

          Synergy History

          +

          The first incarnation of synergy was CosmoSynergy, created by Richard Lee and Adam Feder then at Cosmo Software, Inc., a subsidiary of SGI (nee Silicon Graphics, Inc.), at the end of @@ -16,11 +20,11 @@ both an Irix and a Windows box on their desks and switchboxes were expensive and annoying. CosmoSynergy was a great success but Cosmo Software declined to productize it and the company was later closed. -

          -

          +

          Synergy is a from-scratch reimplementation of CosmoSynergy. It provides most of the features of the original and adds a few improvements.

          + diff --git a/doc/home.html b/doc/home.html new file mode 100644 index 00000000..df0775db --- /dev/null +++ b/doc/home.html @@ -0,0 +1,61 @@ + + + + + + + + Synergy + + +

          +

          Introduction

          +synergy: [noun] a mutually advantageous conjunction of distinct elements +

          +Synergy lets you easily share a single mouse and keyboard between +multiple computers with different operating systems, each with its +own display, without special hardware. It's intended for users +with multiple computers on their desk since each system uses its +own monitor(s). +

          +Redirecting the mouse and keyboard is as simple as moving the mouse +off the edge of your screen. Synergy also merges the clipboards of +all the systems into one, allowing cut-and-paste between systems. +Furthermore, it synchronizes screen savers so they all start and stop +together and, if screen locking is enabled, only one screen requires +a password to unlock them all. Learn more +about how it works. +

          +Synergy is open source and released under the +GNU Public License (GPL). +

          +

          System Requirements

          +

          +

            +
          • Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family) +
          • Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family) +
          • Mac OS X 10.2 or higher +
          • Unix +
              +
            • X Windows version 11 revision 4 or up +
            • XTEST extension
              + (use "xdpyinfo | grep XTEST" to check for XTEST) +
            +
          +All systems must support TCP/IP networking. +

          +"Unix" includes Linux, Solaris, Irix and other variants. Synergy has +only been extensively tested on Linux and may not work completely or +at all on other versions of Unix. Patches are welcome (including +patches that package binaries) at the +patches page. +

          +The Mac OS X port is incomplete. It does not synchronize the screen saver, +only text clipboard data works (i.e. HTML and bitmap data do not work), +the cursor won't hide when not on the screen, and there may be problems +with mouse wheel acceleration. Other problems should be +filed as bugs. +

          + + + diff --git a/doc/index.html b/doc/index.html index 22a0dfe7..9e9ef302 100644 --- a/doc/index.html +++ b/doc/index.html @@ -1,96 +1,33 @@ - + + + + - Synergy Introduction + Synergy - -

          Synergy

          -

          Introduction

          -

          -synergy: [noun] a mutually advantageous conjunction of distinct elements -

          -

          -Synergy lets you easily share a single mouse and keyboard between -multiple computers with different operating systems, each with its -own display, without special hardware. It's intended for users -with multiple computers on their desk since each system uses its -own monitor(s). -

          -

          -Redirecting the mouse and keyboard is as simple as moving the mouse -off the edge of your screen. Synergy also merges the clipboards of -all the systems into one, allowing cut-and-paste between systems. -Furthermore, it synchronizes screen savers so they all start and stop -together and, if screen locking is enabled, only one screen requires -a password to unlock them all. -

          -

          -Synergy is open source and released under the -GNU Public License (GPL). -

          -

          Links

          -

          -Local
          -Getting started:
          -how to run synergy
          -how to build synergy
          -
          -Using synergy:
          -FAQ
          -tips on using synergy
          -autostart guide
          -configuration file format guide
          -
          -Future directions:
          -roadmap to future enhancements
          -
          -For developers:
          -developer's guide
          -
          -Security:
          -important note about security with synergy
          -
          -Miscellaneous documents:
          -the authors of synergy
          -the history of synergy
          -the synergy license terms
          -news about synergy
          -
          -Internet
          -synergy home page
          -synergy project page
          -synergy bug list
          -synergy community forums
          -

          -

          System Requirements

          -

          -

            -
          • Microsoft Windows 95, Windows 98, Windows Me (the Windows 95 family) -
          • Microsoft Windows NT, Windows 2000, Windows XP (the Windows NT family) -
          • Mac OS X 10.2 or higher -
          • Unix -
              -
            • X Windows version 11 revision 4 or up -
            • XTEST extension
              - (use "xdpyinfo | grep XTEST" to check for XTEST) -
            -
          -All systems must support TCP/IP networking. -

          -

          -"Unix" includes Linux, Solaris, Irix and other variants. Synergy has -only been extensively tested on Linux and may not work completely or -at all on other versions of Unix. Patches are welcome (including -patches that package binaries) at the -patches page. -

          -

          -The Mac OS X port is incomplete. It does not synchronize the screen saver, -only text clipboard data works (i.e. HTML and bitmap data do not work), -non-US English keyboards are untested and probably don't work, and there -may be problems with mouse pointer and mouse wheel acceleration. Other -problems should be filed as bugs. -

          - + + diff --git a/doc/license.html b/doc/license.html index 0c4eaa0b..5e748123 100644 --- a/doc/license.html +++ b/doc/license.html @@ -1,25 +1,29 @@ - + - - Synergy License and Copyright + + + + + Synergy License and Copyright -

          Synergy License and Copyright

          +

          Synergy License and Copyright

          +

          Synergy is copyright (C) 2002 Chris Schoeneman.
          Synergy is distributed under the GNU GENERAL PUBLIC LICENSE. -

          -

          GNU GENERAL PUBLIC LICENSE

          +

          +

          GNU GENERAL PUBLIC LICENSE

          Version 2, June 1991 -

          +

          Copyright (C) 1989, 1991 Free Software Foundation, Inc.
          59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
          Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. -

          -

          Preamble

          -

          +

          +

          Preamble

          +

          The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free @@ -29,55 +33,48 @@ Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. -

          -

          +

          When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. -

          -

          +

          To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. -

          -

          +

          For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. -

          -

          +

          We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. -

          -

          +

          Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. -

          -

          +

          Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. -

          -

          +

          The precise terms and conditions for copying, distribution and modification follow. -

          -

          GNU GENERAL PUBLIC LICENSE
          -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

          -

          +

          +

          GNU GENERAL PUBLIC LICENSE
          +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

          +

          0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, @@ -87,16 +84,14 @@ that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". -

          -

          +

          Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. -

          -

          +

          1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate @@ -104,31 +99,26 @@ copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. -

          -

          +

          You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. -

          -

          +

          2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: -

          -

          +

               -

          +

          a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. -

          -

          +

          b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. -

          -

          +

          c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an @@ -139,10 +129,9 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) -

          +

          -

          -

          +

          These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -152,50 +141,43 @@ distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. -

          -

          +

          Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. -

          -

          +

          In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. -

          -

          +

          3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: -

          -

          +

               -

          +

          a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, -

          -

          +

          b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, -

          -

          +

          c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) -

          +

          -

          -

          +

          The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any @@ -206,15 +188,13 @@ anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. -

          -

          +

          If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. -

          -

          +

          4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -222,8 +202,7 @@ void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. -

          -

          +

          5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are @@ -232,8 +211,7 @@ modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. -

          -

          +

          6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to @@ -241,8 +219,7 @@ these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. -

          -

          +

          7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or @@ -255,14 +232,12 @@ license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. -

          -

          +

          If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. -

          -

          +

          It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the @@ -273,12 +248,10 @@ through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. -

          -

          +

          This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. -

          -

          +

          8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -286,14 +259,12 @@ may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. -

          -

          +

          9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. -

          -

          +

          Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions @@ -301,8 +272,7 @@ either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. -

          -

          +

          10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free @@ -310,11 +280,9 @@ Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. -

          -

          +

          NO WARRANTY -

          -

          +

          11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE @@ -325,8 +293,7 @@ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -

          -

          +

          12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED @@ -338,9 +305,9 @@ LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -

          -

          +

          END OF TERMS AND CONDITIONS

          + diff --git a/doc/news.html b/doc/news.html index fac5f2a8..b3729ec6 100644 --- a/doc/news.html +++ b/doc/news.html @@ -1,37 +1,179 @@ - + - - Synergy News + + + + + Synergy News -

          Synergy News

          - -Jan-26-2005 - Synergy 1.2.2 released -

          +Apr-02-2006 - Synergy 1.3.1 released +

          +Made following changes: +

            +
          • Hot key screen switching now restores last cursor position +
          • Fixed loss of hot keys when reloading configuration +
          • Fixed autorepeating on win32 (no longer sending repeating key releases) +
          • Fixed autorepeating on X11 (non-repeating keys were repeating) +
          • Fixed AltGr issues on X11 +
          • Fixed modifier mapping bug on OS X client (caused wrong characters) +
          • Fixed one way for modifiers to get stuck active on all platforms +
          • Fixed bugs in win32 GUI +
          • Removed alloca() from unix code (should fix FreeBSD build) +
          • Added more debugging output for network problems +
          • Fixed failure to detect some errors on X11 +
          +

          +Mar-22-2006 - Synergy 1.3.0 released +

          +Made following additions: +

            +
          • Console window on win32 can now be closed (reopened from tray menu) +
          • Can now change logging level on the fly from win32 tray menu +
          • Added client keep alive (lost connections are now detected reliably) +
          • Added support for linking portions of screen edges +
          • Added version number to UI in win32 +
          • Added GUI for hot key configuration on win32 +
          • Hot keys can now perform actions on press and/or release +
          • Added key down, key up, mouse down, and mouse up hot key actions +
          • Key actions can be directed to particular screens +
          • Hot keys can each perform multiple actions +
          +

          +Made following fixes: +

            +
          • Fixed AltGr key mappings (again) +
          • Fixed assertion when pasting on X11 +
          • Fixed modifier keys in VMware on X11 +
          • OS X server now treats sends option/alt as AltGr or super depending on key +
          • Improved handling of AltGr on win32 +
          • Fixed not removing client when connection is lost +
          • Clients now detect loss of connection to server and reconnect +
          • Fixed mouse jumping on OS X multimonitor systems +
          • Closing console on win32 no longer quits synergy +
          • Fixed Num Lock breaking certain keys +
          • Fixed Scroll Lock not locking cursor to screen +
          • Fixed mapping of delete key on X11 +
          • Fixed loss of clipboard after a particular copy/paste sequence +
          • Fixed compatibility with windows 95/98/Me (ToUnicodeEx) +
          • Fixed bad argument to function on OS X +
          • Fixed error parsing comments in configuration +
          • Fixed autorepeat on win32 servers +
          • Fixed X11 keyboard focus bug when reentering screen +
          • Fixed (suppressed) hot key autorepeating +
          • Fixed mousebutton action when Caps/Num/Scroll Lock were on +
          • Added documentation on firewalls +
          • Fixed documentation formatting on IE6 +
          +

          +Hot keys support has one known major bug: key actions cannot be directed +to the server (primary) screen. The configuration file syntax has changed +from earlier versions; users will have to modify the configurations by +hand. +

          +Dec-18-2005 - Synergy 1.2.7 released +

          +Made following changes: +

            +
          • Added preliminary support for configurable hot keys (Lorenz Schori) +
          • Major rewrite of keyboard handling code +
          • Fixed non-US keyboard handling (AltGr and ISO_Level3_Shift) +
          • Now supporting all installed keyboard layouts simultaneously +
          • Fixed bug in handling remapped caps-lock on X11 +
          • Fixed control and alt keys getting stuck on on X11 +
          • Fixed desktop focus problems requiring extra clicks on win32 +
          • Fixed alt key event getting passed to server when on client on win32 +
          • Synergy would prevent alt+numpad character entry; this is fixed +
          • Fixed suppression of xscreensaver 2.21 on X11 +
          • Fixed middle mouse button dragging on OSX server (Brian Kendall) +
          • Fixed caps/num/scroll lock toggles getting out of sync +
          • Enhanced support for converting clipboard text to the Latin-1 encoding +
          • Added autostart documentation for KDE users +
          • Added more details about using Terminal for OSX users +
          • Fixed crash when using --help on certain platforms +
          +

          +The hot key support is known to have bugs. The configuration file +syntax for hot keys is likely to change and the documentation for it +is minimal. The graphical UI on windows does not provide any support +for editing hot keys. +

          +Nov-12-2005 - Synergy 1.2.6 released +

          +Made following changes: +

            +
          • Fixed permission problem saving autostart configuration in win32 launcher +
          • Disabled buggy fix for loss of clipboard change detection +
          • Restored pthread signal autoconf code +
          +

          +Oct-17-2005 - Synergy 1.2.5 released +

          +Made following changes: +

            +
          • Win32 launcher now saves configuration automatically +
          • Fixed failure to save autostart configuration on win32 +
          • Fixed output bottom-right configuration flag +
          • Now properly releasing keys when leaving a client screen +
          • Fixed stuck-Alt on win32 +
          • Fixed 64-bit problem with clipboard on X11 +
          • Fixed BadAtom bug on X11 +
          • Fixed loss of detection of clipboard changes on win32 +
          • Added support for the MightyMouse +
          • Added support for buttons 4 and 5 on OSX +
          • Now shutting down win32 services when uninstalling +
          +

          +Aug-07-2005 - Synergy 1.2.4 released +

          +Made following changes: +

            +
          • Fixed gcc 4.0 warnings +
          • Fixed autoconf/automake problems +
          • Fixed scroll-lock on X windows +
          • Added option to suppress foreground window grabbing on win32 +
          • Fixed --daemon option on win32 client +
          • Fixed --no-restart on client +
          • Updated OS X autostart documentation +
          +

          +Jul-27-2005 - Synergy 1.2.3 released +

          +Made following changes: +

            +
          • Added OS X screensaver synchronization support (Lorenz Schori) +
          • Added OS X sleep support (Lorenz Schori) +
          • Added OS X fast user switching support (Lorenz Schori) +
          • Fixed international keyboard support on OS X (Lorenz Schori) +
          • Now capturing global hotkeys (e.g. cmd+tab, etc) on OS X (Lorenz Schori) +
          • Added support for SO_REUSEADDR (Don Eisele) +
          • Added "dead" corners feature +
          • Fixed "resource temporarily unavailable" warning when quiting on OS X +
          • Win32 now defaults to WARNING log level to avoid console window +
          • Now disabling foreground window on win32 when leaving server (Brent Priddy) +
          +

          +Jan-26-2005 - Synergy 1.2.2 released +

          Made following changes: -

          • Fixed major OS X modifier key handling bug
          • Fixed handling of ISO_Level3_Shift on X11
          - +

          Jan-04-2005 - Synergy 1.2.1 released - -

          +

          Made following changes: -

          • Fixed major OS X keyboard handling bug
          • Fixed some minor documentation bugs
          - +

          Dec-30-2004 - Synergy 1.2.0 released - -

          +

          Made following changes: -

          • Improved support for moving laptops between networks (Brent Priddy)
          • Added ISO_Level3_Shift support on X windows @@ -42,23 +184,19 @@ Made following changes:
          • Fixed memory leak on OS X
          • Added OS X autostart documentation (Tor Slettnes)
          - +

          Nov-12-2004 - Synergy 1.1.10 released - -

          +

          Made following changes: -

          • Fixed race in condition variable wrapper; caused synergy to hang randomly -
          • Fixed modifier key and caps-lock handling on OS X +
          • Fixed modifier key and caps-lock handling on OSX
          • System info log message now filtered like all other messages
          - +

          Nov-07-2004 - Synergy 1.1.9 released - -

          +

          Made following changes: -

          • Fixed compiler error on gcc 3.4 and later
          • Worked around minor gcc -O3 compiler bug @@ -89,12 +227,10 @@ Made following changes:
          • Added -display option for X11
          • Added support for X11 compose key (Multi_key)
          - +

          Aug-05-2004 - Synergy 1.1.8 released - -

          +

          Made following changes: -

          • Removed key event capture on X11 (was breaking terminal keyboard input)
          • Worked around win32 command prompt stealing shift key events @@ -110,12 +246,10 @@ Made following changes:
          • Fixed mouse wheel drift on OS X client
          • Reorganized documentation and converted to HTML
          - +

          Jun-13-2004 - Synergy 1.1.7 released - -

          +

          Made following changes: -

          • Added OS X precompiled header file forgotten in last build
          • Fixed bug in fix for 'unexpected async reply' on X11 @@ -125,12 +259,10 @@ Made following changes:
          • Fixed error in conversion from multibyte to wide characters
          • Maybe fixed win32 screen saver detection
          - +

          May-26-2004 - Synergy 1.1.6 released - -

          +

          Made following changes: -

          • Added preliminary Mac OS X support (client and server)
          • Fixed ctrl+alt+del emulation on win32 @@ -141,21 +273,17 @@ Made following changes:
          • Fixed reference count bug
          • Keyboard input focus now restored on X11 (fixes loss of input in some games)
          - -

          +

          The OS X port does not yet support: -

          • HTML and bitmap clipboard data
          • Screen saver synchronization
          • Non-US English keyboards
          - +

          May-05-2004 - Synergy 1.1.5 released - -

          +

          Made following changes: -

          • No longer switching screens when a mouse button is down
          • Worked around win32 mouse hook bug, fixing switch on double tap @@ -171,24 +299,20 @@ Made following changes:
          • Partial support for MSYS/MinGW builds (NOT COMPLETE)
          • Partial merge of OS X port (NOT COMPLETE)
          - +

          Mar-31-2004 - Synergy 1.1.4 released - -

          +

          Made following changes: -

          • Fixed lookup of hosts by name of win32
          • Reverted tray icon code to 1.0.15 version; seems to fix the bugs
          • Fixed crash when caps, num, or scroll lock not in key map on X11
          • Fixed double tap and wait to switch features
          - +

          Mar-28-2004 - Synergy 1.1.3 released - -

          +

          Made following changes: -

          • Major code refactoring; reduced use of threads, added event queue
          • Removed unused HTTP support code @@ -199,17 +323,13 @@ Made following changes:
          • Improved keyboard handling and bug fixes
          • Fixed dead key handling
          - -

          +

          Note: the tray icon on windows is known to not work correctly when running the synergy server on Windows 95/95/Me. -

          - +

          Aug-24-2003 - Synergy 1.0.14 released - -

          +

          Made following changes: -

          • Fixed bugs in setting win32 process/thread priority
          • Fixed resource leak in opening win32 system log @@ -217,20 +337,18 @@ Made following changes:
          • Synergy log copied to clipboard now transferred to other screens
          • Hack to work around lesstif clipboard removed (fixes pasting on X)
          - +

          Jul-20-2003 - Synergy 1.0.12 released - -

          +

          +Made following changes: +

          This release finally completes support for non-ASCII characters, fully supporting most (all?) European keyboard layouts including dead key composition. This release includes changes from several experimental versions (1.0.9, 1.0.11, 1.1.0, 1.1.1, 1.1.2, and 1.1.3). -

          - -

          +

          Made following changes: -

          • Added non-ASCII support to win32 and X11
          • Added dead key support to win32 and X11 @@ -244,12 +362,11 @@ Made following changes:
          • Fixed mouse warping on unconnected client
          • Stopped unconnected client from filling up event logs
          - +

          May-10-2003 - Synergy 1.0.8 released - -

          +

          Made following changes: -

          +

          • Fixed hook forwarding (fixing interaction with objectbar)
          • Fixed "Windows" key handling and added support Win+E, Win+F, etc @@ -268,12 +385,48 @@ Made following changes:
          • Added support for "Internet" and "Multimedia" keys
          • Fixed jumping from client to itself (mouse wrapping)
          - +

          +Apr-26-2003 - Added roadmap +

          +There's now a roadmap for Synergy +describing the plans for further development. +

          +Apr-26-2003 - Added Paypal donation page +

          +There's now a donate button for those +who'd like to make a monetary contribution to the further +development of Synergy. +

          +Apr-26-2003 - Development update +

          +Synergy 1.0.8 will include fixes for the following problems. +These are already fixed and some are in development version 1.0.7. +

          +

            +
          • Mouse events at edge of screen are stolen +
          • Windows key doesn't work on clients +
          • Alt+[Shift+]Tab, Alt+[Shift+]Esc, Ctrl+Esc don't work on Win 95/98/Me +
          • Scroll lock doesn't lock to Windows server screen +
          • Screen flashes every 5 seconds on some X11 systems +
          • Synergy doesn't work properly with Xinerama +
          • Screen names with underscores are not allowed +
          +

          +Synergy 1.0.8 will probably include fixes for these problems: +

          +

            +
          • AltGr/Mode_switch doesn't work +
          • Non-ASCII keys aren't supported +
          • Synergy performs badly on a busy Windows system +
          • Unexpected key repeats on X11 clients +
          +

          +Synergy 1.0.8 should be available in the first half of May. +

          Mar-27-2003 - Synergy 1.0.6 released - -

          +

          Made following changes: -

          +

          • Added tray icon on win32
          • Fixed multi-monitor support on win32 @@ -285,14 +438,33 @@ Made following changes:
          • Fixed problem sending the CLIPBOARD to motif/lesstif apps
          • Win32 launcher now remembers non-config-file state
          - +

          +In addition, the version number scheme has been changed. Given a +version number X.Y.Z, release versions will always have Y and Z +even while development versions will have Y and Z odd. +

          +Mar-27-2003 - Synergy featured in Linux Journal. +

          +The April 2003 issue of Linux Journal includes an article on Synergy. +Written by Chris Schoeneman, it describes configuring synergy between +two linux systems. +

          +Mar-27-2003 - Contributions to Synergy. +

          +Many thanks to Girard Thibaut for providing a version of the win32 +launch dialog translated into French. I hope to integrate these +changes into future releases. +

          +Thanks also to "wrhodes" who provided source files for +building an InstallShield installer for Synergy. They'll be +integrated into an upcoming release. +

          Feb-18-2003 - Synergy 1.0.3 released - -

          +

          Made following changes: -

          +

            -
          • Support for X11 keymaps with only uppercase letters +
          • Added support for X11 keymaps with only uppercase letters
          • Fixed memory leaks
          • Added documentation on using synergy with SSH
          • Fixed unnecessary left-handed mouse button swapping @@ -300,31 +472,28 @@ Made following changes:
          • Reduced frequency of large cursor jumps when leaving win32 server
          • Changed cursor motion on win32 multimon to relative moves only
          - +

          Jan-25-2003 - Synergy 1.0.2 released - -

          +

          Made following changes: -

          +

          • Fixed out-of-bounds array lookup in the BSD and Windows network code
          • Added ability to set screen options from Windows launch dialog
          - +

          Jan-22-2003 - Synergy 1.0.1 released - -

          +

          Made following changes: -

          +

          • Fixed running as a service on Windows NT family
          - +

          Jan-20-2003 - Synergy 1.0.0 released - -

          +

          Made following changes: -

          +

          • Refactored to centralize platform dependent code
          • Added support for mouse wheel on Windows NT (SP3 and up) @@ -333,19 +502,16 @@ Made following changes:
          • Fixes for working with xscreensaver
          • Fixes for circular screen links
          - -

          +

          This release has been tested on Linux and Windows. It builds and is believed to run on Solaris and FreeBSD. It is believed to build and run on Irix and AIX. It builds but does not work on MacOS X. -

          - +

          Dec-25-2002 - Synergy 0.9.14 released - -

          +

          Made following changes: -

          +

          • Fixed solaris compile problems (untested)
          • Fixed irix compile problems (untested) @@ -358,12 +524,11 @@ Made following changes:
          • Added config options for half-duplex toggle keys on X11
          • Enabled class diagrams in doxygen documentation
          - +

          Nov-05-2002 - Synergy 0.9.13 released - -

          +

          Made following changes: -

          +

          • Fixed solaris compile problems (untested)
          • Fixed MacOS X compile problems (semi-functional) @@ -376,12 +541,11 @@ Made following changes:
          • Unix platforms can now read Win32 configuration files
          • Minor error reporting fixes
          - +

          Sep-14-2002 - Synergy 0.9.12 released - -

          +

          Made following changes: -

          +

          • Win32 was not reporting log messages properly when run from synergy.exe
          • Network error messages weren't reporting useful information @@ -389,51 +553,47 @@ Made following changes:
          • X11 wasn't handling some keys/key combinations correctly
          • Added option to change logging level when testing from synergy.exe
          - +

          Sep-04-2002 - Synergy 0.9.11 released - -

          +

          Fixed following bugs: -

          +

          • Worked around missing SendInput() on windows 95/NT 4 prior to SP3
          • Fixed keyboard mapping on X11 synergy client
          - +

          Sep-02-2002 - Synergy 0.9.10 released - -

          +

          Fixed following bugs: -

          +

            -
          • The Pause/Break and KP_Enter buttons were not working correctly on windows +
          • The Pause/Break and keypad Enter buttons were not working correctly on windows
          • Configuration options were being lost on windows after a reboot
          • Added support for AltGr/ModeSwitch keys
          • Added support for auto-start on windows when not administrator
          • Improved autoconf
          • Added workaround for lack of sstream header on g++ 2.95.
          - +

          Aug-18-2002 - Synergy 0.9.9 released - -

          +

          Fixed three bugs: -

          +

          • The PrintScrn button was not working correctly on windows
          • The Win32 server could hang when a client disconnected
          • Using the mouse wheel could hang the X server
          - +

          Aug-11-2002 - Synergy 0.9.8 released - -

          +

          Supports any number of clients under Linux or Windows 95 or NT4 or later. Includes mouse and keyboard sharing, clipboard synchronization and screen saver synchronization. Supports ASCII keystrokes, 5 button mouse with wheel, and Unicode text clipboard format.

          - + diff --git a/doc/roadmap.html b/doc/roadmap.html new file mode 100644 index 00000000..7f8681bc --- /dev/null +++ b/doc/roadmap.html @@ -0,0 +1,92 @@ + + + + + + + + Synergy Roadmap + + +

          +

          Synergy Roadmap

          +

          +This page describes the planned development of Synergy. There are +no dates or deadlines. Instead, you'll find the features to come +and the rough order they'll arrive. +

          +

          Short term

          +

          +Synergy should work seamlessly. When it works correctly, it works +transparently so you don't even think about it. When it breaks, +you're forced out of the illusion of a unified desktop. The first +priority is fixing those bugs that break the illusion. +

          +Some of these bugs are pretty minor and some people would rather +have new features first. But I'd rather fix the current +foundation before building on it. That's not to say features +won't get added until after bug fixes; sometimes it's just too +tempting to code up a feature. +

          +The highest priority feature is currently splitting synergy into +front-ends and a back-end. The back-end does the real work. The +front-ends are console, GUI, or background applications that +communicate with the back-end, either controlling it or receiving +notifications from it. +

          +On win32, there'd be a front-end for the tray icon and a dialog to +start, stop, and control the back-end. OS X and X11 would have +similar front-ends. Splitting out the front-end has the added +benefit on X11 of keeping the back-end totally independent of +choice of GUI toolkit (KDE, Gnome, etc.) +

          +One can also imagine a front-end that does nothing but put monitors +into power-saving mode when the cursor is not on them. If you have +one monitor auto-senses two inputs, this would automatically switch +the display when you move the cursor to one screen or another. +

          +

          Medium term

          +

          +Some features fit well into Synergy's current design and may simply +enhance it's current capabilities. +

          +

            +
          • Configurable hot key to pop up a screen switch menu +
          • Configure screen saver synchronization on or off +
          • Graphical interface configuration and control on all platforms +
          • Graphical status feedback on all platforms +
          • More supported clipboard formats (particularly rich text) +
          +

          +A popup menu would be new for Synergy, which currently doesn't have +to do any drawing. That opens up many possibilities. Ideally, +front-ends request hot keys from the back-end and then tell the back +end what to do when they're invoked. This keeps the back-end +independent of the user interface. +

          +

          Long term

          +

          +Two features stand out as long term goals: +

          +

            +
          • Support N computers on +M monitors +
          • Drag and drop across computers +
          +

          +The first feature means sharing a monitor or monitors the way the +keyboard and mouse are shared. With this, Synergy would be a full +KVM solution. Not only would it support a few computers sharing +one screen (still using the mouse to roll from one screen to +another), but it should also support dozens of computers to provide +a solution for server farm administrators. In this capacity, it +may need to support text (as opposed to bitmap graphics) screens. +

          +The second feature would enhance the unified desktop illusion. It +would make it possible to drag a file and possibly other objects +to another screen. The object would be copied (or moved). I expect +this to be a very tricky feature. +

          + + + diff --git a/doc/running.html b/doc/running.html index 6f6ced05..4ad2d594 100644 --- a/doc/running.html +++ b/doc/running.html @@ -1,69 +1,72 @@ - + - - Guide to Running Synergy + + + + + Synergy User Guide -

          Running Synergy

          +

          Running Synergy

          +

          Synergy lets you use one keyboard and mouse across multiple computers. To do so it requires that all the computers are connected to each other via TCP/IP networking. Most systems come with this installed. -

          - -

          Step 1 - Choose a server

          -

          +

          +

          Step 1 - Choose a server

          +

          The first step is to pick which keyboard and mouse you want to share. The computer with that keyboard and mouse is called the "primary screen" and it runs the synergy server. All of the other computers are "secondary screens" and run the synergy client. -

          - -

          Step 2 - Install the software

          -

          +

          +

          Step 2 - Install the software

          +

          Second, you install the software. Choose the appropriate package and install it. For example, on Windows you would run SynergyInstaller. You must install the -software on all the computers that will share the mouse and keyboard. -

          - -

          Step 3 - Configure and start the server

          -

          +software on all the computers that will share the mouse and keyboard +(clients and server). On OS X you'll just have a folder with some +documentation and two programs. You can put this folder anywhere. +

          +

          Step 3 - Configure and start the server

          +

          Next you configure the server. You'll tell synergy the name of the primary and secondary screens, which screens are next to which, and choose desired options. On Windows there's a dialog box for setting the configuration. On other systems you'll create a simple text file. -

          -

          +

          + Note that when you tell synergy that screen A is to the left of screen B this does not imply that B is to the right of A. You must explicitly indicate both relations. If you don't do both then when you're running synergy you'll find you're unable to leave one of the screens. -

          -

          +

          Windows
          On Windows run synergy by double clicking on the synergy file. This brings up a dialog. Configure the server:

            -
          • Click the Server radio button -
          • Click Add to add the server to the +
          • Click the Share this computer's keyboard and mouse (server) radio button +
          • Click the Screens & Links Configure... button +
          • Click the + button to add the server to the Screens list
            • Enter the name of server (the computer's name is the recommended name)
            • Optionally enter other names the server is known by
            • Click OK
            -
          • Use Add to add your other computers +
          • Use the + button to add your other computers
            • Using a computer's name as its screen name is recommended -
            • Choose desired screen options on the Add dialog +
            • Choose desired screen options on the Add Screen dialog
            -
          • Use the controls under Layout to link screens together +
          • Use the controls under Links to link screens together
            • Click (once) on the server's name in the Screens list
            • Choose the screen to the left of the server; use --- @@ -71,6 +74,7 @@ Configure the server:
            • Choose the screens to the right, above and below the server
            • Repeat the above steps for all the other screens
            +
          • Click OK to close the Screens & Links dialog
          • Use Options... to set desired options
          • If the server's screen name is not the server's computer name:
              @@ -80,16 +84,14 @@ Configure the server:
            • Click OK
          -

          -

          +

          Now click Test. The server will start and you'll see a console window with log messages telling you about synergy's progress. If an error occurs you'll get one or more dialog boxes telling you what the errors are; read the errors to determine the problem then -correct them and try Test again. -

          - -

          +correct them and try Test again. See Step 5 +for typical errors. +

          Unix or Mac OS X
          Create a text file named synergy.conf with the following: @@ -119,34 +121,41 @@ have more than two computers you can add those too: add each computer's host name in the screens section and add the appropriate links. See the configuration guide for more configuration possibilities. -

          -

          +

          Now start the server. Normally synergy wants to run "in the background." It detaches from the terminal and doesn't have a visible window, effectively disappearing from view. Until you're sure your configuration works, you should start synergy "in the foreground" using the -f command line option. +

          +On unix type the command below in a shell. If synergys is not in your +PATH then use the full pathname.

               synergys -f --config synergy.conf
           
          +On OS X open Terminal in the Utilities folder in the Applications folder. +Drag the synergys program from the synergy folder onto the Terminal window. +The path to the synergys program will appear. Add the following to the +same line, type a space at the end of the line but don't press enter: +
          +    -f --config 
          +
          +Now drag the synergy.conf file onto the Terminal window and press enter. Check the reported messages for errors. Use ctrl+c to stop synergy if it didn't stop automatically, correct any problems, and start it again. -

          - -

          Step 4 - Start the clients

          -

          +

          +

          Step 4 - Start the clients

          +

          Next you start the client on each computer that will share the server's keyboard and mouse. -

          -

          +

          Windows
          On Windows run synergy by double clicking on the synergy file. This brings up a dialog. Configure the client: -

            -
          • Click the Client radio button -
          • Enter the server's computer name in Server Host Name +
          • Click the Use another computer's shared keyboard and mouse (client) radio button +
          • Enter the server's computer name next to Other Computer's Host Name
            • This is not the server's screen name, unless you made that the server's host name as recommended @@ -158,45 +167,81 @@ Configure the client:
            • Click OK
          -

          -

          +

          Now click Test. -

          -

          +

          Unix or Mac OS X
          -To start a client, enter the following: +To start a client on unix, enter the following:

               synergyc -f server-host-name
           
          where server-host-name is replaced by the host -name of the computer running the synergy server. -

          -

          Step 5 - Test

          -

          +name of the computer running the synergy server. If synergyc is not in +your PATH then use the full pathname. +

          +On OS X open Terminal in the Utilities folder in the Applications folder. +Drag the synergyc program from the synergy folder onto the Terminal window. +The path to the synergys program will appear. Add the following to the +same line and press enter: +

          +    -f server-host-name
          +
          +

          +When you added the client to the server's configuration you chose a +name for the client. If that name was not client's host name then +you must tell the client the name you used. Instead of the above +command use this instead: +

          +    synergyc -f --name name server-host-name
          +
          +where name is the name for the client in +the server's configuration. (On OS X drag the synergyc program to the +Terminal window rather than typing synergyc.) +

          +

          Step 5 - Test

          +

          Clients should immediately report a successful connection or one or -more error messages. Here are the typical problems and possible -solutions: +more error messages. Some typical problems and possible solutions are +below. See the troubleshooting and the +FAQ pages for more help.

          • failed to open screen (X11 only) -

            +

            Check permission to open the X display;
            - check that the DISPLAY environment variable is set;
            + check that the DISPLAY environment variable is set
            use the --display command line option. -

            +

            +

          • address already in use +

            + Another program (maybe another copy of synergy) is using the synergy port; + stop the other program or choose a different port in the + Advanced... dialog. If you change the port + you must make the same change on all of the clients, too. +

            +

          • connection forcefully rejected +

            + The synergy client successfully contacted the server but synergy wasn't + running or it's running on a different port. You may also see this if + there's a firewall blocking the host or port. Make sure synergy is + running on the server and check for a firewall. +

          • already connected -

            +

            Check that the synergy client isn't already running. -

            +

          • refused client -

            +

            Add the client to the server's configuration file. -

            +

            +

          • connection timed out +

            + Check that server-host-name is correct.
            + Check that you don't have a firewall blocking the server or synergy port. +

          • connection failed -

            - check that server-host-name is - correct;
            the server cannot open the desired port, stop - the program using that port (24800) and restart the server. -

            +

            + Check that server-host-name is correct. +

          If you get the error "Xlib: No protocol specified" you're probably running synergy as root while logged in as another user. @@ -204,33 +249,34 @@ X11 may prevent this for security reasons. Either run synergy as the same user that's logged in or (not recommended) use "xhost +" to allow anyone to connect to the display. -

          -

          +

          When successful you should be able to move the mouse off the appropriate edges of your server's screen and have it appear on a client screen. Try to move the mouse to each screen and check all the configured links. Check the mouse buttons and wheel and try the keyboard on each client. You can also cut-and-paste text, HTML, and images across computers (HTML and images are not supported on OS X yet). -

          -

          Step 6 - Run

          -

          +

          +

          Step 6 - Run

          +

          Once everything works correctly, stop all the clients then the server. Then start the server with the Start button on Windows and without the -f option on Unix -and Mac OS X. Finally start the clients similarly. -

          -

          +and Mac OS X. Finally start the clients similarly. On Windows before +clicking Start you may want to set the +Logging Level to +Warning so the logging window doesn't pop +up (because you currently can't close it, just minimize it). +

          You can also configure synergy to start automatically when your computer starts or when you log in. See the autostart guide for more information. -

          -

          Command Line Options Guide

          -

          +

          +

          Command Line Options Guide

          +

          Common Command Line Options
          The following options are supported by synergys and synergyc. -

          @@ -242,16 +288,16 @@ and synergyc. - - - - - + + + + + @@ -276,10 +322,9 @@ and synergyc. - +
           -d,--daemon run as a daemon (Unix) or background (Windows)
            --display display  connect to X server at display (X11 only)
           -f, --no-daemon run in the foreground
            --display display  connect to X server at display (X11 only)
           -n, --name name  --version print version information and exit
          -

          -

          +

          Debug levels are from highest to lowest: FATAL, ERROR, WARNING, NOTE, INFO, @@ -291,28 +336,24 @@ as a daemon. The Windows NT family logs messages to the event log when running as a service. The Windows 95 family shows FATAL log messages in a message box and others in a terminal window when running as a service. -

          -

          +

          The --name option lets the client or server use a name other than its hostname for its screen. This name is used when checking the configuration. -

          -

          +

          Neither the client nor server will automatically restart if an error occurs that is sure to happen every time. For example, the server will exit immediately if it can't find itself in the configuration. On X11 both the client and server will also terminate if the connection to the X server is lost (usually because it died). -

          -

          +

          Server Command Line Options
          -

          +

               synergys [options]
           
          The server accepts the common options and: -

          -

          +

          @@ -325,8 +366,7 @@ The server accepts the common options and:
           -a,  read configuration from pathname
          -

          -

          +

          address has one of the following forms:

               hostname
          @@ -338,10 +378,9 @@ interface on the server system (e.g. somehost
           or 192.168.1.100).  port
           is a port number from 1 to 65535.  hostname defaults to
           the system's hostname and port defaults to 24800.
          -

          -

          +

          Client Command Line Options
          -

          +

               synergyc [options] address[:port]
           
          @@ -351,4 +390,5 @@ port on the server to connect to. The client accepts the common options.

          + diff --git a/doc/security.html b/doc/security.html index db8ad3f5..c8013c27 100644 --- a/doc/security.html +++ b/doc/security.html @@ -1,39 +1,37 @@ - + - - Synergy Security Guide + + + + + Synergy Network Security Guide -

          Authentication and Encryption

          +

          Authentication and Encryption

          Synergy does not do any authentication or encryption. Any computer can connect to the synergy server if it provides a screen name known to the server, and all data is transferred between the server and the clients unencrypted which means that anyone can, say, extract the key presses used to type a password. Therefore, synergy should not be used on untrusted networks. -

          -

          +

          However, there are tools that can add authentication and encryption to synergy without modifying either those tools or synergy. One such tool is SSH (which stands for secure shell). A free implementation -of SSH is called OpenSSH and runs +of SSH is called OpenSSH and runs on Linux, many Unixes, and Windows (in combination with -Cygwin). -

          - -

          Configuring the Server

          -

          +Cygwin). +

          +

          Configuring the Server

          Install the OpenSSH server on the same computer as the synergy server. Configure the OpenSSH server as usual (synergy doesn't demand any special options in OpenSSH) and start it. Start the synergy server as usual; the synergy server requires no special options to work with OpenSSH. -

          - -

          Configuring the Clients

          -

          +

          +

          Configuring the Clients

          Install the OpenSSH client on each synergy client computer. Then, on each client, start the OpenSSH client using port forwarding:

          @@ -53,4 +51,5 @@ Synergy will then run normally except all communication is passed
           through OpenSSH which decrypts/encrypts it on behalf of synergy.
           

          + diff --git a/doc/synergy.css b/doc/synergy.css index 9085c706..8d831aa0 100644 --- a/doc/synergy.css +++ b/doc/synergy.css @@ -53,8 +53,9 @@ pre { font-variant: small-caps; font-size: 400%; width: 100%; - padding: 0px 0px 0px 5px; - border-bottom: solid #6699ff 1px; + padding: 0px; + margin: 0px; + border: 0px; } .banner a { color: #000000; @@ -63,6 +64,15 @@ pre { text-decoration: none; color: #000000; } +.bannerb { + color: #ffffff; + background-color: #ffffff; + width: 100%; + height: 1px; + padding: 0px; + margin: 0px; + border-bottom: solid #6699ff 1px; +} .nav { font-size: x-small; @@ -71,6 +81,7 @@ pre { padding: 2px 0px 2px 0px; margin: 0px; + border-bottom: solid #d4d4d4 300px; } .nav a:hover { text-decoration: none; @@ -82,7 +93,7 @@ pre { text-indent: 1em; } .nav .section { - width: 100%; + width: 120px; text-indent: 0em; border-top: 0px; border-left: 0px; diff --git a/doc/tips.html b/doc/tips.html index 175c094a..9f8e9e24 100644 --- a/doc/tips.html +++ b/doc/tips.html @@ -1,45 +1,42 @@ - + - - Synergy Tips and Tricks + + + + + Synergy Tips and Tricks -

          Tips and Tricks

          +

          +

          Tips and Tricks

          • -

            Be aware that not all keystrokes can be handled by synergy. In particular, ctrl+alt+del is not handled. However, synergy can convert ctrl+alt+pause into ctrl+alt+del on the client side. - (Synergy must be installed as a service on the client for this to - work on the Windows NT family.) Some non-standard keys may not - work, especially "multimedia" buttons, though several are - correctly handled. -

            - + (Synergy must be configured to autostart when the computer starts + on the client for this to work on the Windows NT family.) Some + non-standard keys may not work, especially "multimedia" buttons, + though several are correctly handled. +

          • -

            A screen can be its own neighbor. That allows a screen to "wrap". For example, if a configuration linked the left and right sides of a screen to itself then moving off the left of the screen would put the mouse at the right of the screen and vice versa. -

            - +

          • -

            You cannot switch screens when the Scroll Lock is toggled on. Use - this to prevent unintentional switching. -

            - + this to prevent unintentional switching. You can configure other + hot keys to do this instead; see + lockCursorToScreen. +

          • -

            Turn off mouse driven virtual desktop switching on X windows. It will interfere with synergy. Use keyboard shortcuts instead. -

            - +

          • -

            Synergy's screen saver synchronization works best with xscreensaver under X windows. Synergy works better with xscreensaver if it is using one of the screen saver extensions. Prior to xscreensaver 4.0 @@ -49,45 +46,36 @@ command line options to enable an extension (assuming your server has the extension). Starting with 4.0 you must enable the corresponding option in your .xscreensaver file. -

            - +

          • -

            Synergy automatically converts newlines in clipboard text (Unix expects \n to end each line while Windows expects \r\n). -

            - +

          • -

            Clients can be started and stopped at any time. When a screen is not connected, the mouse will jump over that screen as if the mouse had moved all the way across it and jumped to the next screen. -

            - +

          • -

            A client's keyboard and mouse are fully functional while synergy is running. You can use them in case synergy locks up. -

            - +

          • -

            Strong authentication and encryption is available by using SSH. See the security guide for more information. Synergy does not otherwise provide secure communications and it should not be used on or over untrusted networks. -

            - +

          • -

            Synergy doesn't work if a 16-bit Windows application has the focus on Windows 95/98/Me. This is due to limitations of Windows. One commonly used 16-bit application is the command prompt (command.exe) and this includes synergy's log window when running in test mode. -

            +

          - +

          + diff --git a/doc/toc.html b/doc/toc.html new file mode 100644 index 00000000..3c43bd7c --- /dev/null +++ b/doc/toc.html @@ -0,0 +1,43 @@ + + + + + + + + Synergy TOC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/trouble.html b/doc/trouble.html new file mode 100644 index 00000000..f2923a5b --- /dev/null +++ b/doc/trouble.html @@ -0,0 +1,204 @@ + + + + + + + + Synergy Troubleshooting + + +

          +

          Synergy Troubleshooting

          +

          Problems

          +
            +
          1. Cannot read configuration +
          2. Connection forcefully rejected +
          3. Connection timed out +
          4. Cannot listen for clients +
          5. Unknown screen name "XXX" +
          6. Server refused client with name "XXX" +
            A client with name "XXX" is not in the map +
          7. Server already has a connected client with name "XXX" +
            A client with name "XXX" is already connected +
          8. Server has incompatible version +
          9. The cursor goes to secondary screen but won't come back +
          +

          Solutions

          +
            +
          1. Cannot read configuration +

            +There's an error in the configuration file. This error is always +accompanied by another message describing the problem. Use that +message and the configuration documentation +to determine the fix. +

            +
          2. Connection forcefully rejected +

            +The client was able to contact the server computer but the server was +not listening for clients. Possible reasons are: +

            +
              +
            • The client is using the wrong server +

              +Make sure the client is using the hostname or IP address of the computer +running the synergy server. +

              +
            • Synergy isn't running on the server +

              +Make sure the synergy server is running on the server computer. Make +sure the server is ready to accept connections. If another program is +using synergy's port (24800 by default) then synergy can't start unless +you specify a different port. +

              +
            • The client is using the wrong port +

              +Synergy uses port 24800 by default but you can specify a different port. +If you do use a different port you must use that port on the server and +all clients. +

              +
            +
          3. Connection timed out +

            +The most likely reasons for this are: +

            +
              +
            • A firewall +

              +A firewall is a program or device that deliberately blocks network +connections for security reasons. Typically, they'll silently drop +packets they don't want rather than sending a rejection to the sender. +This makes it more difficult for intruders to break in. +

              +When synergy traffic hits a firewall and gets dropped, eventually the +synergy client will give up waiting for a response and time out. To +allow synergy traffic through first find all the firewalls on the +network between and on the synergy client and server computers. +

              +A firewall on the server or any network device between the server and +any client should allow packets to TCP port 24800. (Port 24800 is the +default; use whichever port you've selected.) You'll have to consult +the manual for your operating system, device, or firewall software to +find out how to do this. +

              +Usually you'll won't need to adjust a firewall on client machines. +That's because firewalls normally allow incoming traffic on any port +they've initiated a connection on. The reasoning is, of course, if +you started a conversation you probably want to hear the reply. +

              +
            • The network is down or busy +

              +Correct the network problem and try again. You might try +ping to see if the two computers can see +each other on the network. +

              +
            • The server is frozen +

              +If the synergy server is running but locked up or very busy then the +client may get this message. If the server is locked up then you'll +probably have to restart it. If it's just very busy then the client +should successfully connect automatically once the server settles down. +

              +
            +
          4. Cannot listen for clients +

            +Synergy tried to start listening for clients but the network port is +unavailable for some reason. Typical reasons are: +

            +
              +
            • No network devices +

              +You must have a TCP/IP network device installed and enabled to use +synergy. +

              +
            • A synergy server is already running +

              +Check that a synergy server isn't already running. +

              +
            • Another program is using synergy's port +

              +Only one program at a time can listen for connections on a given port. +If the specific error is that the address is already in use and you've +ruled out the other causes, then it's likely another program is already +using synergy's port. By default synergy uses port 24800. Try having +synergy use a different port number, like 24801 or 24900. Note that +the server and all clients must use the same port number. Alternatively, +find the other program and stop it or have it use another port. +

              +
            +
          5. Unknown screen name "XXX" +

            +This error can be reported when reading the configuration; see +cannot read configuration. If the configuration +was read successfully and you get this error then it means that the +server's screen is not in the configuration. All screens must be listed +in the configuration. +

            +A common reason for this is when you haven't used the system's hostname +as its screen name. By default, synergy uses the hostname as the screen +name. If you used a different screen name in the configuration then you +must tell synergy what that name is. Let's say the hostname is +frederick but the configuration defines a screen +named fred. Then you must tell the server +that its screen name is fred by using the +--name fred command line option or setting +the screen name in the advanced options dialog to +fred. +

            +Alternatively, you can specify one name as an alias of another. See +the configuration documentation +for details. +

            +Another common reason for this is a mismatch between what you think the +hostname is and what synergy thinks it is. Typically this is a problem +with fully qualified domain names (FQDN). Perhaps you think your system +is named fred but synergy thinks it's +fred.nowhere.com or +fred.local. You can use either solution above +to fix this. +

            +
          6. Server refused client with name "XXX" +
            A client with name "XXX" is not in the map +

            +The client is using a screen name not in the server's configuration. +This is essentially the same problem as Unknown +screen name "XXX" and has the same solutions: specify another +screen name or add an alias. +

            +
          7. Server already has a connected client with name "XXX" +
            A client with name "XXX" is already connected +

            +This happens when: +

            +
              +
            • Two clients try use the same screen name +

              +Each client must have a unique screen name. Configure at least one +client to use a different screen name. +

              +
            • One client reconnects without cleanly disconnecting +

              +It's possible for a client to disconnect without the server knowing, +usually by being disconnected from the network or possibly by going +to sleep or even crashing. The server is left thinking the client is +still connected so when the client reconnects the server will think +this is a different client using the same name. Synergy will usually +detect and correct this problem within a few seconds. If it doesn't +then restart the server. +

              +
            +
          8. Server has incompatible version +

            +You're using different versions of synergy on the client and server. +You should use the same version on all systems. +

            +
          9. The cursor goes to secondary screen but won't come back +

            +This is FAQ #17 and is also mentioned in +the documentation for using synergy +and configuration. +

            +
          + + + diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index 37dee72b..80c613ab 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -122,7 +122,7 @@ CArch::CArch(ARCH_ARGS* args) m_sleep = new ARCH_SLEEP; m_string = new ARCH_STRING; m_time = new ARCH_TIME; - m_console = new ARCH_CONSOLE; + m_console = new ARCH_CONSOLE(args); m_daemon = new ARCH_DAEMON; m_taskbar = new ARCH_TASKBAR(args); @@ -170,6 +170,12 @@ CArch::closeConsole() m_console->closeConsole(); } +void +CArch::showConsole(bool showIfEmpty) +{ + m_console->showConsole(showIfEmpty); +} + void CArch::writeConsole(const char* str) { @@ -254,6 +260,12 @@ CArch::closeLog() m_log->closeLog(); } +void +CArch::showLog(bool showIfEmpty) +{ + m_log->showLog(showIfEmpty); +} + void CArch::writeLog(ELevel level, const char* msg) { @@ -488,6 +500,12 @@ CArch::setNoDelayOnSocket(CArchSocket s, bool noDelay) return m_net->setNoDelayOnSocket(s, noDelay); } +bool +CArch::setReuseAddrOnSocket(CArchSocket s, bool reuse) +{ + return m_net->setReuseAddrOnSocket(s, reuse); +} + std::string CArch::getHostName() { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index 8a42bd0c..644f015c 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -74,6 +74,7 @@ public: // IArchConsole overrides virtual void openConsole(const char*); virtual void closeConsole(); + virtual void showConsole(bool showIfEmpty); virtual void writeConsole(const char*); virtual const char* getNewlineForConsole(); @@ -99,6 +100,7 @@ public: // IArchLog overrides virtual void openLog(const char*); virtual void closeLog(); + virtual void showLog(bool showIfEmpty); virtual void writeLog(ELevel, const char*); // IArchMultithread overrides @@ -143,6 +145,7 @@ public: const void* buf, size_t len); virtual void throwErrorOnSocket(CArchSocket); virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse); virtual std::string getHostName(); virtual CArchNetAddress newAnyAddr(EAddressFamily); virtual CArchNetAddress copyAddr(CArchNetAddress); diff --git a/lib/arch/CArchConsoleUnix.cpp b/lib/arch/CArchConsoleUnix.cpp index 79c4ae2c..dcb6e961 100644 --- a/lib/arch/CArchConsoleUnix.cpp +++ b/lib/arch/CArchConsoleUnix.cpp @@ -19,7 +19,7 @@ // CArchConsoleUnix // -CArchConsoleUnix::CArchConsoleUnix() +CArchConsoleUnix::CArchConsoleUnix(void*) { // do nothing } @@ -41,6 +41,12 @@ CArchConsoleUnix::closeConsole() // do nothing } +void +CArchConsoleUnix::showConsole(bool) +{ + // do nothing +} + void CArchConsoleUnix::writeConsole(const char* str) { diff --git a/lib/arch/CArchConsoleUnix.h b/lib/arch/CArchConsoleUnix.h index 5e560fec..f93630bd 100644 --- a/lib/arch/CArchConsoleUnix.h +++ b/lib/arch/CArchConsoleUnix.h @@ -22,12 +22,13 @@ //! Unix implementation of IArchConsole class CArchConsoleUnix : public IArchConsole { public: - CArchConsoleUnix(); + CArchConsoleUnix(void*); virtual ~CArchConsoleUnix(); // IArchConsole overrides virtual void openConsole(const char* title); virtual void closeConsole(); + virtual void showConsole(bool); virtual void writeConsole(const char*); virtual const char* getNewlineForConsole(); }; diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp index 6796a454..14d418ac 100644 --- a/lib/arch/CArchConsoleWindows.cpp +++ b/lib/arch/CArchConsoleWindows.cpp @@ -15,90 +15,100 @@ #include "CArchConsoleWindows.h" #include "IArchMultithread.h" #include "CArch.h" -#include +#include "CArchMiscWindows.h" +#include + +#define SYNERGY_MSG_CONSOLE_OPEN WM_APP + 0x0021 +#define SYNERGY_MSG_CONSOLE_CLOSE WM_APP + 0x0022 +#define SYNERGY_MSG_CONSOLE_SHOW WM_APP + 0x0023 +#define SYNERGY_MSG_CONSOLE_WRITE WM_APP + 0x0024 +#define SYNERGY_MSG_CONSOLE_CLEAR WM_APP + 0x0025 // // CArchConsoleWindows // -CArchThread CArchConsoleWindows::s_thread = 0; +CArchConsoleWindows* CArchConsoleWindows::s_instance = NULL; +HINSTANCE CArchConsoleWindows::s_appInstance = NULL; -CArchConsoleWindows::CArchConsoleWindows() : - m_output(NULL) +CArchConsoleWindows::CArchConsoleWindows(void* appInstance) : + m_show(false), + m_maxLines(1000), + m_numCharacters(0), + m_maxCharacters(65536) { - s_thread = ARCH->newCurrentThread(); + // save the singleton instance + s_instance = this; - m_mutex = ARCH->newMutex(); + // save app instance + s_appInstance = reinterpret_cast(appInstance); + + // we need a mutex + m_mutex = ARCH->newMutex(); + + // and a condition variable which uses the above mutex + m_ready = false; + m_condVar = ARCH->newCondVar(); + + // we're going to want to get a result from the thread we're + // about to create to know if it initialized successfully. + // so we lock the condition variable. + ARCH->lockMutex(m_mutex); + + // open a window and run an event loop in a separate thread. + // this has to happen in a separate thread because if we + // create a window on the current desktop with the current + // thread then the current thread won't be able to switch + // desktops if it needs to. + m_thread = ARCH->newThread(&CArchConsoleWindows::threadEntry, this); + + // wait for child thread + while (!m_ready) { + ARCH->waitCondVar(m_condVar, m_mutex, -1.0); + } + + // ready + ARCH->unlockMutex(m_mutex); - // dummy write to stderr to create locks in stdio from the main - // thread. if we open the console from another thread then we - // can deadlock in stdio when trying to write from a 3rd thread. - // writes to stderr without a console don't go anywhere so the - // user won't notice this. - fprintf(stderr, "\n"); } CArchConsoleWindows::~CArchConsoleWindows() { + if (m_thread != NULL) { + PostMessage(m_hwnd, WM_QUIT, 0, 0); + ARCH->wait(m_thread, -1.0); + ARCH->closeThread(m_thread); + } + ARCH->closeCondVar(m_condVar); ARCH->closeMutex(m_mutex); - ARCH->closeThread(s_thread); + s_instance = NULL; } void CArchConsoleWindows::openConsole(const char* title) { - ARCH->lockMutex(m_mutex); - if (m_output == NULL) { - if (AllocConsole()) { - // get console output handle - m_output = GetStdHandle(STD_ERROR_HANDLE); - - // set console title - if (title != NULL) { - SetConsoleTitle(title); - } - - // prep console. windows 95 and its ilk have braindead - // consoles that can't even resize independently of the - // buffer size. use a 25 line buffer for those systems. - OSVERSIONINFO osInfo; - COORD size = { 80, 1000 }; - osInfo.dwOSVersionInfoSize = sizeof(osInfo); - if (GetVersionEx(&osInfo) && - osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) - size.Y = 25; - SetConsoleScreenBufferSize(m_output, size); - SetConsoleTextAttribute(m_output, - FOREGROUND_RED | - FOREGROUND_GREEN | - FOREGROUND_BLUE); - - // catch console signals - SetConsoleCtrlHandler(&CArchConsoleWindows::signalHandler, TRUE); - - // reopen stderr to point at console - freopen("con", "w", stderr); - } - } - ARCH->unlockMutex(m_mutex); + SetWindowText(m_frame, title); + SendMessage(m_frame, SYNERGY_MSG_CONSOLE_OPEN, 0, 0); } void CArchConsoleWindows::closeConsole() { - ARCH->lockMutex(m_mutex); - if (m_output != NULL) { - if (FreeConsole()) { - m_output = NULL; - } - } - ARCH->unlockMutex(m_mutex); + SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLOSE, 0, 0); + SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLEAR, 0, 0); +} + +void +CArchConsoleWindows::showConsole(bool showIfEmpty) +{ + SendMessage(m_frame, SYNERGY_MSG_CONSOLE_SHOW, showIfEmpty ? 1 : 0, 0); } void CArchConsoleWindows::writeConsole(const char* str) { - fprintf(stderr, "%s", str); + SendMessage(m_frame, SYNERGY_MSG_CONSOLE_WRITE, + reinterpret_cast(str), 0); } const char* @@ -107,21 +117,322 @@ CArchConsoleWindows::getNewlineForConsole() return "\r\n"; } -BOOL WINAPI -CArchConsoleWindows::signalHandler(DWORD ctrlType) +void +CArchConsoleWindows::clearBuffer() { - // terminate app and skip remaining handlers - switch (ctrlType) { - case CTRL_C_EVENT: - ARCH->raiseSignal(CArch::kINTERRUPT); - return TRUE; + m_buffer.clear(); + m_numCharacters = 0; + SetWindowText(m_hwnd, ""); +} - case CTRL_BREAK_EVENT: - ARCH->raiseSignal(CArch::kTERMINATE); - return TRUE; +void +CArchConsoleWindows::appendBuffer(const char* msg) +{ + bool wasEmpty = m_buffer.empty(); + + // get current selection + CHARRANGE selection; + SendMessage(m_hwnd, EM_EXGETSEL, 0, reinterpret_cast(&selection)); + + // remove tail of buffer + size_t removedCharacters = 0; + while (m_buffer.size() >= m_maxLines) { + removedCharacters += m_buffer.front().size(); + m_buffer.pop_front(); + } + + // remove lines from top of control + if (removedCharacters > 0) { + CHARRANGE range; + range.cpMin = 0; + range.cpMax = static_cast(removedCharacters); + SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&range)); + SendMessage(m_hwnd, EM_REPLACESEL, FALSE, reinterpret_cast("")); + + // adjust selection + if (selection.cpMin < static_cast(removedCharacters) || + selection.cpMax < static_cast(removedCharacters)) { + selection.cpMin = 0; + selection.cpMax = 0; + } + else { + selection.cpMin -= static_cast(removedCharacters); + selection.cpMax -= static_cast(removedCharacters); + } + + m_numCharacters -= removedCharacters; + } + + // append message + m_buffer.push_back(msg); + size_t newNumCharacters = m_numCharacters + m_buffer.back().size(); + + // add line to bottom of control + if (newNumCharacters > m_maxCharacters) { + m_maxCharacters = newNumCharacters; + SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters); + } + CHARRANGE range; + range.cpMin = m_numCharacters; + range.cpMax = m_numCharacters; + SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&range)); + SendMessage(m_hwnd, EM_REPLACESEL, FALSE, + reinterpret_cast(m_buffer.back().c_str())); + + // adjust selection + bool atEnd = false; + if (selection.cpMax == static_cast(m_numCharacters)) { + selection.cpMin = static_cast(newNumCharacters); + selection.cpMax = static_cast(newNumCharacters); + atEnd = true; + } + + // restore the selection + SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&selection)); + if (atEnd) { + SendMessage(m_hwnd, EM_SCROLLCARET, 0, 0); + } + + if (wasEmpty && m_show) { + ShowWindow(m_frame, TRUE); + } + + m_numCharacters = newNumCharacters; +} + +void +CArchConsoleWindows::setSize(int width, int height) +{ + DWORD style = GetWindowLong(m_frame, GWL_STYLE); + DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE); + RECT rect; + rect.left = 100; + rect.top = 100; + rect.right = rect.left + width * m_wChar; + rect.bottom = rect.top + height * m_hChar; + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + SetWindowPos(m_frame, NULL, 0, 0, rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER); +} + +LRESULT +CArchConsoleWindows::wndProc(HWND hwnd, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_CLOSE: + ShowWindow(m_frame, FALSE); + m_show = false; + return 0; + + case SYNERGY_MSG_CONSOLE_OPEN: + return 0; + + case SYNERGY_MSG_CONSOLE_CLOSE: + SendMessage(m_frame, WM_CLOSE, 0, 0); + m_show = false; + return 0; + + case SYNERGY_MSG_CONSOLE_SHOW: + m_show = true; + if (wParam != 0 || !m_buffer.empty()) { + ShowWindow(m_frame, TRUE); + } + return 0; + + case SYNERGY_MSG_CONSOLE_WRITE: + appendBuffer(reinterpret_cast(wParam)); + return 0; + + case SYNERGY_MSG_CONSOLE_CLEAR: + clearBuffer(); + return 0; + + case WM_SIZE: + if (hwnd == m_frame) { + MoveWindow(m_hwnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); + } + break; + + case WM_SIZING: + if (hwnd == m_frame) { + // get window vs client area info + int wBase = 40 * m_wChar; + int hBase = 40 * m_hChar; + DWORD style = GetWindowLong(m_frame, GWL_STYLE); + DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE); + RECT rect; + rect.left = 100; + rect.top = 100; + rect.right = rect.left + wBase; + rect.bottom = rect.top + hBase; + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + wBase = rect.right - rect.left - wBase; + hBase = rect.bottom - rect.top - hBase; + + // get closest size that's a multiple of the character size + RECT* newRect = (RECT*)lParam; + int width = (newRect->right - newRect->left - wBase) / m_wChar; + int height = (newRect->bottom - newRect->top - hBase) / m_hChar; + width = width * m_wChar + wBase; + height = height * m_hChar + hBase; + + // adjust sizing rect + switch (wParam) { + case WMSZ_LEFT: + case WMSZ_TOPLEFT: + case WMSZ_BOTTOMLEFT: + newRect->left = newRect->right - width; + break; + + case WMSZ_RIGHT: + case WMSZ_TOPRIGHT: + case WMSZ_BOTTOMRIGHT: + newRect->right = newRect->left + width; + break; + } + switch (wParam) { + case WMSZ_TOP: + case WMSZ_TOPLEFT: + case WMSZ_TOPRIGHT: + newRect->top = newRect->bottom - height; + break; + + case WMSZ_BOTTOM: + case WMSZ_BOTTOMLEFT: + case WMSZ_BOTTOMRIGHT: + newRect->bottom = newRect->top + height; + break; + } + return TRUE; + } + break; default: - ARCH->raiseSignal(CArch::kINTERRUPT); - return TRUE; + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +LRESULT CALLBACK +CArchConsoleWindows::staticWndProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + // forward the message + if (s_instance != NULL) { + return s_instance->wndProc(hwnd, msg, wParam, lParam); + } + else { + return DefWindowProc(hwnd, msg, wParam, lParam); } } + +void +CArchConsoleWindows::threadMainLoop() +{ + LoadLibrary("RICHED32.DLL"); + + // get the app icons + HICON largeIcon, smallIcon; + CArchMiscWindows::getIcons(largeIcon, smallIcon); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = 0; + classInfo.lpfnWndProc = &CArchConsoleWindows::staticWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = sizeof(CArchConsoleWindows*); + classInfo.hInstance = s_appInstance; + classInfo.hIcon = largeIcon; + classInfo.hCursor = NULL; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = TEXT("SynergyConsole"); + classInfo.hIconSm = smallIcon; + ATOM windowClass = RegisterClassEx(&classInfo); + + // create frame window + m_frame = CreateWindowEx(0, + reinterpret_cast(windowClass), + TEXT("Synergy Log"), + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, + NULL, + NULL, + s_appInstance, + NULL); + + // create log window + m_hwnd = CreateWindowEx(0, + "RichEdit", + TEXT(""), + WS_CHILD | WS_VISIBLE | WS_VSCROLL | + ES_MULTILINE | ES_READONLY, + 0, 0, 1, 1, + m_frame, + (HMENU)1, + s_appInstance, + NULL); + + // select font and get info + HDC hdc = GetDC(m_hwnd); + HGDIOBJ oldFont = SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT)); + TEXTMETRIC metrics; + GetTextMetrics(hdc, &metrics); + CHARFORMAT format; + format.cbSize = sizeof(format); + format.dwMask = CFM_CHARSET | CFM_COLOR | CFM_FACE | + CFM_OFFSET | CFM_SIZE | CFM_PROTECTED | + CFM_BOLD | CFM_ITALIC | + CFM_STRIKEOUT | CFM_UNDERLINE; + format.dwEffects = 0; + format.yHeight = metrics.tmHeight; + format.yOffset = 0; + format.crTextColor = RGB(0, 0, 0); + format.bCharSet = DEFAULT_CHARSET; + format.bPitchAndFamily = FIXED_PITCH | FF_MODERN; + GetTextFace(hdc, sizeof(format.szFaceName), format.szFaceName); + SelectObject(hdc, oldFont); + ReleaseDC(m_hwnd, hdc); + + // prep window + SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters); + SendMessage(m_hwnd, EM_SETCHARFORMAT, 0, reinterpret_cast(&format)); + SendMessage(m_hwnd, EM_SETBKGNDCOLOR, 0, RGB(255, 255, 255)); + m_wChar = metrics.tmAveCharWidth; + m_hChar = metrics.tmHeight + metrics.tmExternalLeading; + setSize(80, 25); + + // signal ready + ARCH->lockMutex(m_mutex); + m_ready = true; + ARCH->broadcastCondVar(m_condVar); + ARCH->unlockMutex(m_mutex); + + // handle failure + if (m_hwnd == NULL) { + UnregisterClass(reinterpret_cast(windowClass), s_appInstance); + return; + } + + // main loop + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // clean up + DestroyWindow(m_hwnd); + UnregisterClass(reinterpret_cast(windowClass), s_appInstance); +} + +void* +CArchConsoleWindows::threadEntry(void* self) +{ + reinterpret_cast(self)->threadMainLoop(); + return NULL; +} diff --git a/lib/arch/CArchConsoleWindows.h b/lib/arch/CArchConsoleWindows.h index bf9f0a32..0d59e6ef 100644 --- a/lib/arch/CArchConsoleWindows.h +++ b/lib/arch/CArchConsoleWindows.h @@ -19,6 +19,7 @@ #include "IArchConsole.h" #include "IArchMultithread.h" +#include "stddeque.h" #include #define ARCH_CONSOLE CArchConsoleWindows @@ -26,23 +27,51 @@ //! Win32 implementation of IArchConsole class CArchConsoleWindows : public IArchConsole { public: - CArchConsoleWindows(); + CArchConsoleWindows(void*); virtual ~CArchConsoleWindows(); // IArchConsole overrides virtual void openConsole(const char* title); virtual void closeConsole(); + virtual void showConsole(bool showIfEmpty); virtual void writeConsole(const char*); virtual const char* getNewlineForConsole(); private: - static BOOL WINAPI signalHandler(DWORD); + void clearBuffer(); + void appendBuffer(const char*); + void setSize(int width, int height); + + LRESULT wndProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK + staticWndProc(HWND, UINT, WPARAM, LPARAM); + void threadMainLoop(); + static void* threadEntry(void*); private: - static CArchThread s_thread; + typedef std::deque MessageBuffer; + static CArchConsoleWindows* s_instance; + static HINSTANCE s_appInstance; + + // multithread data CArchMutex m_mutex; - HANDLE m_output; + CArchCond m_condVar; + bool m_ready; + CArchThread m_thread; + + // child thread data + HWND m_frame; + HWND m_hwnd; + LONG m_wChar; + LONG m_hChar; + bool m_show; + + // messages + size_t m_maxLines; + size_t m_maxCharacters; + size_t m_numCharacters; + MessageBuffer m_buffer; }; #endif diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index cd88659e..ab42ceab 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -136,10 +136,11 @@ CArchDaemonWindows::installDaemon(const char* name, NULL); if (service == NULL) { // can't create service - // FIXME -- handle ERROR_SERVICE_EXISTS DWORD err = GetLastError(); - CloseServiceHandle(mgr); - throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + if (err != ERROR_SERVICE_EXISTS) { + CloseServiceHandle(mgr); + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } } // done with service and manager @@ -148,7 +149,7 @@ CArchDaemonWindows::installDaemon(const char* name, // open the registry key for this service HKEY key = openNTServicesKey(); - key = CArchMiscWindows::openKey(key, name); + key = CArchMiscWindows::addKey(key, name); if (key == NULL) { // can't open key DWORD err = GetLastError(); @@ -165,7 +166,7 @@ CArchDaemonWindows::installDaemon(const char* name, CArchMiscWindows::setValue(key, _T("Description"), description); // set command line - key = CArchMiscWindows::openKey(key, _T("Parameters")); + key = CArchMiscWindows::addKey(key, _T("Parameters")); if (key == NULL) { // can't open key DWORD err = GetLastError(); @@ -224,7 +225,7 @@ CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) } // open the service. oddly, you must open a service to delete it. - SC_HANDLE service = OpenService(mgr, name, DELETE); + SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP); if (service == NULL) { DWORD err = GetLastError(); CloseServiceHandle(mgr); @@ -234,6 +235,10 @@ CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); } + // stop the service. we don't care if we fail. + SERVICE_STATUS status; + ControlService(service, SERVICE_CONTROL_STOP, &status); + // delete the service const bool okay = (DeleteService(service) == 0); const DWORD err = GetLastError(); @@ -244,6 +249,10 @@ CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) // handle failure. ignore error if service isn't installed anymore. if (!okay && isDaemonInstalled(name, allUsers)) { + if (err == ERROR_IO_PENDING) { + // this seems to be a spurious error + return; + } if (err != ERROR_SERVICE_MARKED_FOR_DELETE) { throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); } @@ -317,7 +326,7 @@ CArchDaemonWindows::daemonize(const char* name, DaemonFunc func) } bool -CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) +CArchDaemonWindows::canInstallDaemon(const char* /*name*/, bool allUsers) { // if not for all users then use the user's autostart registry. // key. if windows 95 family then use windows 95 services key. @@ -338,10 +347,10 @@ CArchDaemonWindows::canInstallDaemon(const char* name, bool allUsers) } CloseServiceHandle(mgr); - // check if we can open the registry key for this service + // check if we can open the registry key HKEY key = openNTServicesKey(); - key = CArchMiscWindows::openKey(key, name); - key = CArchMiscWindows::openKey(key, _T("Parameters")); +// key = CArchMiscWindows::addKey(key, name); +// key = CArchMiscWindows::addKey(key, _T("Parameters")); CArchMiscWindows::closeKey(key); return (key != NULL); @@ -415,7 +424,7 @@ CArchDaemonWindows::openNTServicesKey() NULL }; - return CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_keyNames); + return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames); } HKEY @@ -430,7 +439,7 @@ CArchDaemonWindows::open95ServicesKey() NULL }; - return CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_keyNames); + return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames); } HKEY @@ -445,7 +454,7 @@ CArchDaemonWindows::openUserStartupKey() NULL }; - return CArchMiscWindows::openKey(HKEY_CURRENT_USER, s_keyNames); + return CArchMiscWindows::addKey(HKEY_CURRENT_USER, s_keyNames); } bool diff --git a/lib/arch/CArchLogUnix.cpp b/lib/arch/CArchLogUnix.cpp index c75696e3..093d89f9 100644 --- a/lib/arch/CArchLogUnix.cpp +++ b/lib/arch/CArchLogUnix.cpp @@ -41,6 +41,12 @@ CArchLogUnix::closeLog() closelog(); } +void +CArchLogUnix::showLog(bool) +{ + // do nothing +} + void CArchLogUnix::writeLog(ELevel level, const char* msg) { diff --git a/lib/arch/CArchLogUnix.h b/lib/arch/CArchLogUnix.h index b3717844..91070b45 100644 --- a/lib/arch/CArchLogUnix.h +++ b/lib/arch/CArchLogUnix.h @@ -28,6 +28,7 @@ public: // IArchLog overrides virtual void openLog(const char* name); virtual void closeLog(); + virtual void showLog(bool); virtual void writeLog(ELevel, const char*); }; diff --git a/lib/arch/CArchLogWindows.cpp b/lib/arch/CArchLogWindows.cpp index 6d21d6ba..0ac89131 100644 --- a/lib/arch/CArchLogWindows.cpp +++ b/lib/arch/CArchLogWindows.cpp @@ -47,6 +47,12 @@ CArchLogWindows::closeLog() } } +void +CArchLogWindows::showLog(bool) +{ + // do nothing +} + void CArchLogWindows::writeLog(ELevel level, const char* msg) { diff --git a/lib/arch/CArchLogWindows.h b/lib/arch/CArchLogWindows.h index d2385765..e8812536 100644 --- a/lib/arch/CArchLogWindows.h +++ b/lib/arch/CArchLogWindows.h @@ -31,6 +31,7 @@ public: // IArchLog overrides virtual void openLog(const char* name); virtual void closeLog(); + virtual void showLog(bool showIfEmpty); virtual void writeLog(ELevel, const char*); private: diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index f900f741..e2fb2dce 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -33,6 +33,8 @@ typedef DWORD EXECUTION_STATE; CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL; DWORD CArchMiscWindows::s_busyState = 0; CArchMiscWindows::STES_t CArchMiscWindows::s_stes = NULL; +HICON CArchMiscWindows::s_largeIcon = NULL; +HICON CArchMiscWindows::s_smallIcon = NULL; void CArchMiscWindows::init() @@ -87,6 +89,20 @@ CArchMiscWindows::isWindowsModern() return result; } +void +CArchMiscWindows::setIcons(HICON largeIcon, HICON smallIcon) +{ + s_largeIcon = largeIcon; + s_smallIcon = smallIcon; +} + +void +CArchMiscWindows::getIcons(HICON& largeIcon, HICON& smallIcon) +{ + largeIcon = s_largeIcon; + smallIcon = s_smallIcon; +} + int CArchMiscWindows::runDaemon(RunFunc runFunc) { @@ -113,6 +129,30 @@ CArchMiscWindows::getDaemonQuitMessage() HKEY CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) +{ + return openKey(key, keyName, false); +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames) +{ + return openKey(key, keyNames, false); +} + +HKEY +CArchMiscWindows::addKey(HKEY key, const TCHAR* keyName) +{ + return openKey(key, keyName, true); +} + +HKEY +CArchMiscWindows::addKey(HKEY key, const TCHAR* const* keyNames) +{ + return openKey(key, keyNames, true); +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName, bool create) { // ignore if parent is NULL if (key == NULL) { @@ -123,7 +163,7 @@ CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) HKEY newKey; LONG result = RegOpenKeyEx(key, keyName, 0, KEY_WRITE | KEY_QUERY_VALUE, &newKey); - if (result != ERROR_SUCCESS) { + if (result != ERROR_SUCCESS && create) { DWORD disp; result = RegCreateKeyEx(key, keyName, 0, TEXT(""), 0, KEY_WRITE | KEY_QUERY_VALUE, @@ -140,11 +180,11 @@ CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) } HKEY -CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames) +CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames, bool create) { for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) { // open next key - key = openKey(key, keyNames[i]); + key = openKey(key, keyNames[i], create); } return key; } diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index 973f9b4e..95a1d136 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -56,6 +56,18 @@ public: */ static bool isWindowsModern(); + //! Set the application icons + /*! + Set the application icons. + */ + static void setIcons(HICON largeIcon, HICON smallIcon); + + //! Get the application icons + /*! + Get the application icons. + */ + static void getIcons(HICON& largeIcon, HICON& smallIcon); + //! Run the daemon /*! Delegates to CArchDaemonWindows. @@ -86,6 +98,12 @@ public: //! Open and return a registry key, closing the parent key static HKEY openKey(HKEY parent, const TCHAR* const* keyPath); + //! Open/create and return a registry key, closing the parent key + static HKEY addKey(HKEY parent, const TCHAR* child); + + //! Open/create and return a registry key, closing the parent key + static HKEY addKey(HKEY parent, const TCHAR* const* keyPath); + //! Close a key static void closeKey(HKEY); @@ -144,6 +162,13 @@ public: static void removeBusyState(DWORD busyModes); private: + //! Open and return a registry key, closing the parent key + static HKEY openKey(HKEY parent, const TCHAR* child, bool create); + + //! Open and return a registry key, closing the parent key + static HKEY openKey(HKEY parent, const TCHAR* const* keyPath, + bool create); + //! Read a string value from the registry static std::string readBinaryOrString(HKEY, const TCHAR* name, DWORD type); @@ -159,6 +184,8 @@ private: static CDialogs* s_dialogs; static DWORD s_busyState; static STES_t s_stes; + static HICON s_largeIcon; + static HICON s_smallIcon; }; #endif diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 5750b51d..ec11fc50 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -49,6 +49,7 @@ setSignalSet(sigset_t* sigset) sigaddset(sigset, SIGHUP); sigaddset(sigset, SIGINT); sigaddset(sigset, SIGTERM); + sigaddset(sigset, SIGUSR2); } // @@ -771,7 +772,7 @@ CArchMultithreadPosix::threadSignalHandler(void*) for (;;) { // wait #if HAVE_POSIX_SIGWAIT - int signal; + int signal = 0; sigwait(&sigset, &signal); #else sigwait(&sigset); @@ -791,6 +792,10 @@ CArchMultithreadPosix::threadSignalHandler(void*) ARCH->raiseSignal(kHANGUP); break; + case SIGUSR2: + ARCH->raiseSignal(kUSER); + break; + default: // ignore break; diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 2432c9e0..6b36cff5 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -31,9 +31,6 @@ #if HAVE_POLL # include -# if HAVE_ALLOCA_H -# include -# endif #else # if HAVE_SYS_SELECT_H # include @@ -47,13 +44,6 @@ # include #endif -#if HAVE_ALLOCA_H -# define freea(x_) -#else -# define alloca(x_) malloc(x_) -# define freea(x_) free(x_) -#endif - static const int s_family[] = { PF_UNSPEC, PF_INET @@ -291,8 +281,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) } // allocate space for translated query - struct pollfd* pfd = reinterpret_cast( - alloca((1 + num) * sizeof(struct pollfd))); + struct pollfd* pfd = new struct pollfd[1 + num]; // translate query for (int i = 0; i < num; ++i) { @@ -322,7 +311,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) n = poll(pfd, n, t); // reset the unblock pipe - if (unblockPipe != NULL && (pfd[num].revents & POLLIN) != 0) { + if (n > 0 && unblockPipe != NULL && (pfd[num].revents & POLLIN) != 0) { // the unblock event was signalled. flush the pipe. char dummy[100]; do { @@ -338,10 +327,10 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) if (errno == EINTR) { // interrupted system call ARCH->testCancelThread(); - freea(pfd); + delete[] pfd; return 0; } - freea(pfd); + delete[] pfd; throwError(errno); } @@ -362,7 +351,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) } } - freea(pfd); + delete[] pfd; return n; } @@ -452,7 +441,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) SELECT_TYPE_ARG5 timeout2P); // reset the unblock pipe - if (unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) { + if (n > 0 && unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) { // the unblock event was signalled. flush the pipe. char dummy[100]; do { @@ -594,6 +583,29 @@ CArchNetworkBSD::setNoDelayOnSocket(CArchSocket s, bool noDelay) return (oflag != 0); } +bool +CArchNetworkBSD::setReuseAddrOnSocket(CArchSocket s, bool reuse) +{ + assert(s != NULL); + + // get old state + int oflag; + socklen_t size = sizeof(oflag); + if (getsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR, + (optval_t*)&oflag, &size) == -1) { + throwError(errno); + } + + int flag = reuse ? 1 : 0; + size = sizeof(flag); + if (setsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR, + (optval_t*)&flag, size) == -1) { + throwError(errno); + } + + return (oflag != 0); +} + std::string CArchNetworkBSD::getHostName() { diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index 3915b3ff..bba60272 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -73,6 +73,7 @@ public: const void* buf, size_t len); virtual void throwErrorOnSocket(CArchSocket); virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse); virtual std::string getHostName(); virtual CArchNetAddress newAnyAddr(EAddressFamily); virtual CArchNetAddress copyAddr(CArchNetAddress); diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index 0ed630e0..ac40596a 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -612,6 +612,30 @@ CArchNetworkWinsock::setNoDelayOnSocket(CArchSocket s, bool noDelay) return (oflag != 0); } +bool +CArchNetworkWinsock::setReuseAddrOnSocket(CArchSocket s, bool reuse) +{ + assert(s != NULL); + + // get old state + BOOL oflag; + int size = sizeof(oflag); + if (getsockopt_winsock(s->m_socket, SOL_SOCKET, + SO_REUSEADDR, &oflag, &size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + // set new state + BOOL flag = reuse ? 1 : 0; + size = sizeof(flag); + if (setsockopt_winsock(s->m_socket, SOL_SOCKET, + SO_REUSEADDR, &flag, size) == SOCKET_ERROR) { + throwError(getsockerror_winsock()); + } + + return (oflag != 0); +} + std::string CArchNetworkWinsock::getHostName() { diff --git a/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h index 8124019c..3912ba5b 100644 --- a/lib/arch/CArchNetworkWinsock.h +++ b/lib/arch/CArchNetworkWinsock.h @@ -70,6 +70,7 @@ public: const void* buf, size_t len); virtual void throwErrorOnSocket(CArchSocket); virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay); + virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse); virtual std::string getHostName(); virtual CArchNetAddress newAnyAddr(EAddressFamily); virtual CArchNetAddress copyAddr(CArchNetAddress); diff --git a/lib/arch/CMultibyte.cpp b/lib/arch/CMultibyte.cpp index 2b9bb8ca..517d72d6 100644 --- a/lib/arch/CMultibyte.cpp +++ b/lib/arch/CMultibyte.cpp @@ -19,6 +19,9 @@ #include "CArch.h" #include #include +#if HAVE_LOCALE_H +# include +#endif #if HAVE_WCHAR_H || defined(_MSC_VER) # include #elif __APPLE__ @@ -56,6 +59,15 @@ static CArchMutex s_mutex = NULL; ARCH_STRING::ARCH_STRING() { s_mutex = ARCH->newMutex(); + +#if HAVE_LOCALE_H + // see if we can convert a Latin-1 character + char mb[MB_LEN_MAX]; + if (wctomb(mb, 0xe3) == -1) { + // can't convert. try another locale so we can convert latin-1. + setlocale(LC_CTYPE, "en_US"); + } +#endif } ARCH_STRING::~ARCH_STRING() diff --git a/lib/arch/IArchConsole.h b/lib/arch/IArchConsole.h index c9da4100..2befb196 100644 --- a/lib/arch/IArchConsole.h +++ b/lib/arch/IArchConsole.h @@ -43,6 +43,15 @@ public: */ virtual void closeConsole() = 0; + //! Show the console + /*! + Causes the console to become visible. This generally only makes sense + for a console in a graphical user interface. Other implementations + will do nothing. Iff \p showIfEmpty is \c false then the implementation + may optionally only show the console if it's not empty. + */ + virtual void showConsole(bool showIfEmpty) = 0; + //! Write to the console /*! Writes the given string to the console, opening it if necessary. diff --git a/lib/arch/IArchLog.h b/lib/arch/IArchLog.h index 0c259187..7655ff95 100644 --- a/lib/arch/IArchLog.h +++ b/lib/arch/IArchLog.h @@ -52,6 +52,15 @@ public: */ virtual void closeLog() = 0; + //! Show the log + /*! + Causes the log to become visible. This generally only makes sense + for a log in a graphical user interface. Other implementations + will do nothing. Iff \p showIfEmpty is \c false then the implementation + may optionally only show the log if it's not empty. + */ + virtual void showLog(bool showIfEmpty) = 0; + //! Write to the log /*! Writes the given string to the log with the given level. diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index 55db7af9..b7b72293 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -80,6 +80,7 @@ public: kINTERRUPT, //!< Interrupt (e.g. Ctrl+C) kTERMINATE, //!< Terminate (e.g. Ctrl+Break) kHANGUP, //!< Hangup (SIGHUP) + kUSER, //!< User (SIGUSR2) kNUM_SIGNALS }; //! Type of signal handler function diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h index 2c821afb..007fb442 100644 --- a/lib/arch/IArchNetwork.h +++ b/lib/arch/IArchNetwork.h @@ -226,6 +226,13 @@ public: */ virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay) = 0; + //! Turn address reuse on or off on socket + /*! + Allows the address this socket is bound to to be reused while in the + TIME_WAIT state. Returns the previous state. + */ + virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse) = 0; + //! Return local host's name virtual std::string getHostName() = 0; diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp index 73575be9..37c9bcd8 100644 --- a/lib/arch/arch.dsp +++ b/lib/arch/arch.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\arch.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\arch.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\arch.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\arch.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/base/CEvent.cpp b/lib/base/CEvent.cpp index 5d828989..bfdf88ed 100644 --- a/lib/base/CEvent.cpp +++ b/lib/base/CEvent.cpp @@ -22,15 +22,17 @@ CEvent::CEvent() : m_type(kUnknown), m_target(NULL), - m_data(NULL) + m_data(NULL), + m_flags(0) { // do nothing } -CEvent::CEvent(Type type, void* target, void* data) : +CEvent::CEvent(Type type, void* target, void* data, Flags flags) : m_type(type), m_target(target), - m_data(data) + m_data(data), + m_flags(flags) { // do nothing } @@ -53,6 +55,12 @@ CEvent::getData() const return m_data; } +CEvent::Flags +CEvent::getFlags() const +{ + return m_flags; +} + CEvent::Type CEvent::registerType(const char* name) { @@ -82,7 +90,9 @@ CEvent::deleteData(const CEvent& event) break; default: - free(event.getData()); + if ((event.getFlags() & kDontFreeData) == 0) { + free(event.getData()); + } break; } } diff --git a/lib/base/CEvent.h b/lib/base/CEvent.h index 272369ba..8b637cef 100644 --- a/lib/base/CEvent.h +++ b/lib/base/CEvent.h @@ -33,6 +33,13 @@ public: kLast //!< Must be last }; + typedef UInt32 Flags; + enum { + kNone = 0x00, //!< No flags + kDeliverImmediately = 0x01, //!< Dispatch and free event immediately + kDontFreeData = 0x02 //!< Don't free data in deleteData + }; + CEvent(); //! Create \c CEvent with data @@ -41,9 +48,10 @@ public: The \p data must be POD (plain old data) allocated by malloc(), which means it cannot have a constructor, destructor or be composed of any types that do. \p target is the intended - recipient of the event. + recipient of the event. \p flags is any combination of \c Flags. */ - CEvent(Type type, void* target = NULL, void* data = NULL); + CEvent(Type type, void* target = NULL, void* data = NULL, + UInt32 flags = kNone); //! @name manipulators //@{ @@ -97,12 +105,19 @@ public: */ void* getData() const; + //! Get event flags + /*! + Returns the event flags. + */ + Flags getFlags() const; + //@} private: Type m_type; void* m_target; void* m_data; + Flags m_flags; }; #endif diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp index 16a0d52b..d0a93391 100644 --- a/lib/base/CEventQueue.cpp +++ b/lib/base/CEventQueue.cpp @@ -206,18 +206,24 @@ CEventQueue::addEvent(const CEvent& event) default: break; } - - CArchMutexLock lock(m_mutex); - - // store the event's data locally - UInt32 eventID = saveEvent(event); - - // add it - if (!m_buffer->addEvent(eventID)) { - // failed to send event - removeEvent(eventID); + + if ((event.getFlags() & CEvent::kDeliverImmediately) != 0) { + dispatchEvent(event); CEvent::deleteData(event); } + else { + CArchMutexLock lock(m_mutex); + + // store the event's data locally + UInt32 eventID = saveEvent(event); + + // add it + if (!m_buffer->addEvent(eventID)) { + // failed to send event + removeEvent(eventID); + CEvent::deleteData(event); + } + } } CEventQueueTimer* diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index 1cc007da..7c73ac87 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -100,42 +100,7 @@ CLog::getInstance() } void -CLog::print(const char* fmt, ...) const -{ - // check if fmt begins with a priority argument - int priority = 4; - if (fmt[0] == '%' && fmt[1] == 'z') { - priority = fmt[2] - '\060'; - fmt += 3; - } - - // done if below priority threshold - if (priority > getFilter()) { - return; - } - - // compute prefix padding length - int pad = g_priorityPad; - - // print to buffer - char stack[1024]; - va_list args; - va_start(args, fmt); - char* buffer = CStringUtil::vsprint(stack, - sizeof(stack) / sizeof(stack[0]), - pad, m_maxNewlineLength, fmt, args); - va_end(args); - - // output buffer - output(priority, buffer); - - // clean up - if (buffer != stack) - delete[] buffer; -} - -void -CLog::printt(const char* file, int line, const char* fmt, ...) const +CLog::print(const char* file, int line, const char* fmt, ...) const { // check if fmt begins with a priority argument int priority = 4; @@ -151,35 +116,61 @@ CLog::printt(const char* file, int line, const char* fmt, ...) const // compute prefix padding length char stack[1024]; - sprintf(stack, "%d", line); - int pad = strlen(file) + 1 /* comma */ + - strlen(stack) + 1 /* colon */ + 1 /* space */ + - g_priorityPad; + int pPad = g_priorityPad; + if (file != NULL) { + sprintf(stack, "%d", line); + pPad += strlen(file) + 1 /* comma */ + + strlen(stack) + 1 /* colon */ + 1 /* space */; + } - // print to buffer, leaving space for a newline at the end - va_list args; - va_start(args, fmt); - char* buffer = CStringUtil::vsprint(stack, - sizeof(stack) / sizeof(stack[0]), - pad, m_maxNewlineLength, fmt, args); - va_end(args); + // compute suffix padding length + int sPad = m_maxNewlineLength; + + // print to buffer, leaving space for a newline at the end and prefix + // at the beginning. + char* buffer = stack; + int len = (int)(sizeof(stack) / sizeof(stack[0])); + while (true) { + // try printing into the buffer + va_list args; + va_start(args, fmt); + int n = ARCH->vsnprintf(buffer + pPad, len - pPad - sPad, fmt, args); + va_end(args); + + // if the buffer wasn't big enough then make it bigger and try again + if (n < 0 || n > (int)len) { + if (buffer != stack) { + delete[] buffer; + } + len *= 2; + buffer = new char[len]; + } + + // if the buffer was big enough then continue + else { + break; + } + } // print the prefix to the buffer. leave space for priority label. - sprintf(buffer + g_priorityPad, "%s,%d:", file, line); - buffer[pad - 1] = ' '; - - // discard file and line if priority < 0 char* message = buffer; - if (priority < 0) { - message += pad - g_priorityPad; + if (file != NULL) { + sprintf(buffer + g_priorityPad, "%s,%d:", file, line); + buffer[pPad - 1] = ' '; + + // discard file and line if priority < 0 + if (priority < 0) { + message += pPad - g_priorityPad; + } } // output buffer output(priority, message); // clean up - if (buffer != stack) + if (buffer != stack) { delete[] buffer; + } } void @@ -199,6 +190,8 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) if (newlineLength > m_maxNewlineLength) { m_maxNewlineLength = newlineLength; } + outputter->open(kAppVersion); + outputter->show(false); } void @@ -279,9 +272,6 @@ CLog::output(int priority, char* msg) const // put an appropriate newline at the end strcpy(end, outputter->getNewline()); - // open the outputter - outputter->open(kApplication); - // write message outputter->write(static_cast(priority), msg + g_maxPriorityLength - n); @@ -294,9 +284,6 @@ CLog::output(int priority, char* msg) const // put an appropriate newline at the end strcpy(end, outputter->getNewline()); - // open the outputter - outputter->open(kApplication); - // write message and break out of loop if it returns false if (!outputter->write(static_cast(priority), msg + g_maxPriorityLength - n)) { diff --git a/lib/base/CLog.h b/lib/base/CLog.h index b25e0ba6..391480e2 100644 --- a/lib/base/CLog.h +++ b/lib/base/CLog.h @@ -102,18 +102,13 @@ public: //! @name accessors //@{ - //! Print a log message - /*! - Print a log message using the printf-like \c format and arguments. - */ - void print(const char* format, ...) const; - //! Print a log message /*! Print a log message using the printf-like \c format and arguments - preceded by the filename and line number. + preceded by the filename and line number. If \c file is NULL then + neither the file nor the line are printed. */ - void printt(const char* file, int line, + void print(const char* file, int line, const char* format, ...) const; //! Get the minimum priority level. @@ -175,9 +170,9 @@ without the leading \c k. For example, \c CLOG_INFO. The special filename and line number. If \c NOLOGGING is defined during the build then this macro expands to -nothing. If \c NDEBUG is defined during the build then it expands to a -call to CLog::print. Otherwise it expands to a call to CLog::printt, -which includes the filename and line number. +nothing. If \c NDEBUG is not defined during the build then it expands +to a call to CLog::print that prints the filename and line number, +otherwise it expands to a call that doesn't. */ #if defined(NOLOGGING) @@ -187,10 +182,10 @@ which includes the filename and line number. #elif defined(NDEBUG) #define LOG(_a1) CLOG->print _a1 #define LOGC(_a1, _a2) if (_a1) CLOG->print _a2 -#define CLOG_TRACE +#define CLOG_TRACE NULL, 0, #else -#define LOG(_a1) CLOG->printt _a1 -#define LOGC(_a1, _a2) if (_a1) CLOG->printt _a2 +#define LOG(_a1) CLOG->print _a1 +#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2 #define CLOG_TRACE __FILE__, __LINE__, #endif diff --git a/lib/base/CStringUtil.cpp b/lib/base/CStringUtil.cpp index 08dc61c8..46361932 100644 --- a/lib/base/CStringUtil.cpp +++ b/lib/base/CStringUtil.cpp @@ -115,55 +115,38 @@ CStringUtil::vformat(const char* fmt, va_list args) CString CStringUtil::print(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - CString result = vprint(fmt, args); - va_end(args); - return result; -} - -CString -CStringUtil::vprint(const char* fmt, va_list args) { char tmp[1024]; - char* buffer = vsprint(tmp, sizeof(tmp) / sizeof(tmp[0]), 0, 0, fmt, args); - if (buffer == tmp) { - return buffer; - } - else { - CString result(buffer); - delete[] buffer; - return result; - } -} + char* buffer = tmp; + int len = (int)(sizeof(tmp) / sizeof(tmp[0])); + CString result; + while (buffer != NULL) { + // try printing into the buffer + va_list args; + va_start(args, fmt); + int n = ARCH->vsnprintf(buffer, len, fmt, args); + va_end(args); -char* -CStringUtil::vsprint(char* buffer, int len, - int prefix, int suffix, const char* fmt, va_list args) -{ - assert(len > 0); + // if the buffer wasn't big enough then make it bigger and try again + if (n < 0 || n > len) { + if (buffer != tmp) { + delete[] buffer; + } + len *= 2; + buffer = new char[len]; + } - // try writing to input buffer - int n; - if (buffer != NULL && len >= prefix + suffix) { - n = ARCH->vsnprintf(buffer + prefix, - len - (prefix + suffix), fmt, args); - if (n >= 0 && n <= len - (prefix + suffix)) - return buffer; + // if it was big enough then save the string and don't try again + else { + result = buffer; + if (buffer != tmp) { + delete[] buffer; + } + buffer = NULL; + } } - // start allocating buffers until we write the whole string - buffer = NULL; - do { - delete[] buffer; - len *= 2; - buffer = new char[len + (prefix + suffix)]; - n = ARCH->vsnprintf(buffer + prefix, - len - (prefix + suffix), fmt, args); - } while (n < 0 || n > len - (prefix + suffix)); - - return buffer; + return result; } diff --git a/lib/base/CStringUtil.h b/lib/base/CStringUtil.h index 1ad5824c..8ee86647 100644 --- a/lib/base/CStringUtil.h +++ b/lib/base/CStringUtil.h @@ -48,28 +48,6 @@ public: */ static CString print(const char* fmt, ...); - //! Print a string using printf-style formatting - /*! - Same as print() except takes va_list. - */ - static CString vprint(const char* fmt, va_list); - - //! Print a string using printf-style formatting into a buffer - /*! - This is like print but print into a given buffer. If the resulting - string will not fit into \c buffer then a new buffer is allocated and - returned, otherwise \c buffer is returned. the caller must delete[] - the returned memory if is not \c buffer. - - \c prefix and \c suffix must be >= 0. Exactly \c prefix characters and - at least \c suffix characters are available in the buffer before - and after the printed string, respectively. \c bufferLength is the - length of buffer and should not be adjusted by the caller to - account for \c prefix or \c suffix. - */ - static char* vsprint(char* buffer, int bufferLength, - int prefix, int suffix, const char* fmt, va_list); - //! Case-insensitive comparisons /*! This class provides case-insensitve comparison functions. diff --git a/lib/base/ILogOutputter.h b/lib/base/ILogOutputter.h index 585598c2..2be4dcc9 100644 --- a/lib/base/ILogOutputter.h +++ b/lib/base/ILogOutputter.h @@ -45,6 +45,15 @@ public: */ virtual void close() = 0; + //! Show the outputter + /*! + Causes the output to become visible. This generally only makes sense + for a logger in a graphical user interface. Other implementations + will do nothing. Iff \p showIfEmpty is \c false then the implementation + may optionally only show the log if it's not empty. + */ + virtual void show(bool showIfEmpty) = 0; + //! Write a message with level /*! Writes \c message, which has the given \c level, to a log. diff --git a/lib/base/LogOutputters.cpp b/lib/base/LogOutputters.cpp index 5f7a40ba..f556d53f 100644 --- a/lib/base/LogOutputters.cpp +++ b/lib/base/LogOutputters.cpp @@ -41,6 +41,12 @@ CStopLogOutputter::close() // do nothing } +void +CStopLogOutputter::show(bool) +{ + // do nothing +} + bool CStopLogOutputter::write(ELevel, const char*) { @@ -80,6 +86,12 @@ CConsoleLogOutputter::close() ARCH->closeConsole(); } +void +CConsoleLogOutputter::show(bool showIfEmpty) +{ + ARCH->showConsole(showIfEmpty); +} + bool CConsoleLogOutputter::write(ELevel, const char* msg) { @@ -120,6 +132,12 @@ CSystemLogOutputter::close() ARCH->closeLog(); } +void +CSystemLogOutputter::show(bool showIfEmpty) +{ + ARCH->showLog(showIfEmpty); +} + bool CSystemLogOutputter::write(ELevel level, const char* msg) { @@ -162,22 +180,27 @@ CSystemLogOutputter::getNewline() const // CSystemLogger // -CSystemLogger::CSystemLogger(const char* title) +CSystemLogger::CSystemLogger(const char* title, bool blockConsole) : + m_stop(NULL) { // redirect log messages + if (blockConsole) { + m_stop = new CStopLogOutputter; + CLOG->insert(m_stop); + } m_syslog = new CSystemLogOutputter; - m_stop = new CStopLogOutputter; m_syslog->open(title); - CLOG->insert(m_stop); CLOG->insert(m_syslog); } CSystemLogger::~CSystemLogger() { CLOG->remove(m_syslog); - CLOG->remove(m_stop); - delete m_stop; delete m_syslog; + if (m_stop != NULL) { + CLOG->remove(m_stop); + delete m_stop; + } } @@ -221,6 +244,12 @@ CBufferedLogOutputter::close() m_buffer.clear(); } +void +CBufferedLogOutputter::show(bool) +{ + // do nothing +} + bool CBufferedLogOutputter::write(ELevel, const char* message) { diff --git a/lib/base/LogOutputters.h b/lib/base/LogOutputters.h index 64befe5f..a8087593 100644 --- a/lib/base/LogOutputters.h +++ b/lib/base/LogOutputters.h @@ -34,6 +34,7 @@ public: // ILogOutputter overrides virtual void open(const char* title); virtual void close(); + virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const; }; @@ -51,6 +52,7 @@ public: // ILogOutputter overrides virtual void open(const char* title); virtual void close(); + virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const; }; @@ -67,6 +69,7 @@ public: // ILogOutputter overrides virtual void open(const char* title); virtual void close(); + virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const; }; @@ -81,7 +84,7 @@ the scope. */ class CSystemLogger { public: - CSystemLogger(const char* title); + CSystemLogger(const char* title, bool blockConsole); ~CSystemLogger(); private: @@ -117,6 +120,7 @@ public: // ILogOutputter overrides virtual void open(const char* title); virtual void close(); + virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); virtual const char* getNewline() const; diff --git a/lib/base/base.dsp b/lib/base/base.dsp index f1eb30aa..542cfe8d 100644 --- a/lib/base/base.dsp +++ b/lib/base/base.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\base.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\base.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\base.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\base.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index d339156f..1e450a17 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -47,17 +47,32 @@ CClient::CClient(const CString& name, const CNetworkAddress& address, m_stream(NULL), m_timer(NULL), m_server(NULL), - - m_active(false) + m_ready(false), + m_active(false), + m_suspended(false), + m_connectOnResume(false) { assert(m_socketFactory != NULL); assert(m_screen != NULL); - // do nothing + // register suspend/resume event handlers + EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(), + getEventTarget(), + new TMethodEventJob(this, + &CClient::handleSuspend)); + EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(), + getEventTarget(), + new TMethodEventJob(this, + &CClient::handleResume)); } CClient::~CClient() { + EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(), + getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getResumeEvent(), + getEventTarget()); + cleanupTimer(); cleanupScreen(); cleanupConnecting(); @@ -72,6 +87,10 @@ CClient::connect() if (m_stream != NULL) { return; } + if (m_suspended) { + m_connectOnResume = true; + return; + } try { // resolve the server hostname. do this every time we connect @@ -111,8 +130,10 @@ CClient::connect() void CClient::disconnect(const char* msg) { + m_connectOnResume = false; cleanupTimer(); cleanupScreen(); + cleanupConnecting(); cleanupConnection(); if (msg != NULL) { sendConnectionFailedEvent(msg); @@ -142,6 +163,12 @@ CClient::isConnecting() const return (m_timer != NULL); } +CNetworkAddress +CClient::getServerAddress() const +{ + return m_serverAddress; +} + CEvent::Type CClient::getConnectedEvent() { @@ -216,13 +243,16 @@ void CClient::setClipboard(ClipboardID id, const IClipboard* clipboard) { m_screen->setClipboard(id, clipboard); + m_ownClipboard[id] = false; + m_sentClipboard[id] = false; } void CClient::grabClipboard(ClipboardID id) { m_screen->grabClipboard(id); - m_ownClipboard[id] = false; + m_ownClipboard[id] = false; + m_sentClipboard[id] = false; } void @@ -275,9 +305,9 @@ CClient::mouseRelativeMove(SInt32 dx, SInt32 dy) } void -CClient::mouseWheel(SInt32 delta) +CClient::mouseWheel(SInt32 xDelta, SInt32 yDelta) { - m_screen->mouseWheel(delta); + m_screen->mouseWheel(xDelta, yDelta); } void @@ -330,8 +360,9 @@ CClient::sendClipboard(ClipboardID id) // marshall the data CString data = clipboard.marshall(); - // save and send data if different - if (data != m_dataClipboard[id]) { + // save and send data if different or not yet sent + if (!m_sentClipboard[id] || data != m_dataClipboard[id]) { + m_sentClipboard[id] = true; m_dataClipboard[id] = data; m_server->onClipboardChanged(id, &clipboard); } @@ -490,6 +521,7 @@ CClient::handleConnected(const CEvent&, void*) // reset clipboard state for (ClipboardID id = 0; id < kClipboardEnd; ++id) { m_ownClipboard[id] = false; + m_sentClipboard[id] = false; m_timeClipboard[id] = 0; } } @@ -513,6 +545,7 @@ CClient::handleConnectTimeout(const CEvent&, void*) { cleanupTimer(); cleanupConnecting(); + cleanupConnection(); delete m_stream; m_stream = NULL; LOG((CLOG_DEBUG1 "connection timed out")); @@ -557,6 +590,7 @@ CClient::handleClipboardGrabbed(const CEvent& event, void*) // we now own the clipboard and it has not been sent to the server m_ownClipboard[info->m_id] = true; + m_sentClipboard[info->m_id] = false; m_timeClipboard[info->m_id] = 0; // if we're not the active screen then send the clipboard now, @@ -605,3 +639,24 @@ CClient::handleHello(const CEvent&, void*) m_stream->getEventTarget())); } } + +void +CClient::handleSuspend(const CEvent&, void*) +{ + LOG((CLOG_INFO "suspend")); + m_suspended = true; + bool wasConnected = isConnected(); + disconnect(NULL); + m_connectOnResume = wasConnected; +} + +void +CClient::handleResume(const CEvent&, void*) +{ + LOG((CLOG_INFO "resume")); + m_suspended = false; + if (m_connectOnResume) { + m_connectOnResume = false; + connect(); + } +} diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 5087759b..4bd11c66 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -89,6 +89,13 @@ public: */ bool isConnecting() const; + //! Get address of server + /*! + Returns the address of the server the client is connected (or wants + to connect) to. + */ + CNetworkAddress getServerAddress() const; + //! Get connected event type /*! Returns the connected event type. This is sent when the client has @@ -136,7 +143,7 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); - virtual void mouseWheel(SInt32 delta); + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta); virtual void screensaver(bool activate); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); @@ -162,7 +169,9 @@ private: void handleShapeChanged(const CEvent&, void*); void handleClipboardGrabbed(const CEvent&, void*); void handleHello(const CEvent&, void*); - + void handleSuspend(const CEvent& event, void*); + void handleResume(const CEvent& event, void*); + private: CString m_name; CNetworkAddress m_serverAddress; @@ -174,7 +183,10 @@ private: CServerProxy* m_server; bool m_ready; bool m_active; + bool m_suspended; + bool m_connectOnResume; bool m_ownClipboard[kClipboardEnd]; + bool m_sentClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; CString m_dataClipboard[kClipboardEnd]; diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index a44347db..acef43b2 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -32,7 +32,6 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : m_client(client), m_stream(stream), - m_timer(NULL), m_seqNum(0), m_compressMouse(false), m_compressMouseRelative(false), @@ -41,7 +40,8 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : m_dxMouse(0), m_dyMouse(0), m_ignoreMouse(false), - m_heartRate(0.0), + m_keepAliveAlarm(0.0), + m_keepAliveAlarmTimer(NULL), m_parser(&CServerProxy::parseHandshakeMessage) { assert(m_client != NULL); @@ -58,32 +58,40 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : &CServerProxy::handleData)); // send heartbeat - installHeartBeat(kHeartRate); + setKeepAliveRate(kKeepAliveRate); } CServerProxy::~CServerProxy() { - installHeartBeat(-1.0); + setKeepAliveRate(-1.0); EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(), m_stream->getEventTarget()); } void -CServerProxy::installHeartBeat(double heartRate) +CServerProxy::resetKeepAliveAlarm() { - if (m_timer != NULL) { - EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer); - EVENTQUEUE->deleteTimer(m_timer); + if (m_keepAliveAlarmTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_keepAliveAlarmTimer); + EVENTQUEUE->deleteTimer(m_keepAliveAlarmTimer); + m_keepAliveAlarmTimer = NULL; } - m_heartRate = heartRate; - if (m_heartRate > 0.0) { - m_timer = EVENTQUEUE->newTimer(m_heartRate, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer, + if (m_keepAliveAlarm > 0.0) { + m_keepAliveAlarmTimer = + EVENTQUEUE->newOneShotTimer(m_keepAliveAlarm, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_keepAliveAlarmTimer, new TMethodEventJob(this, - &CServerProxy::handleHeartBeat)); + &CServerProxy::handleKeepAliveAlarm)); } } +void +CServerProxy::setKeepAliveRate(double rate) +{ + m_keepAliveAlarm = rate * kKeepAlivesUntilDeath; + resetKeepAliveAlarm(); +} + void CServerProxy::handleData(const CEvent&, void*) { @@ -105,7 +113,7 @@ CServerProxy::handleData(const CEvent&, void*) break; case kUnknown: - LOG((CLOG_ERR "invalid message from server")); + LOG((CLOG_ERR "invalid message from server: %c%c%c%c", code[0], code[1], code[2], code[3])); m_client->disconnect("invalid message from server"); return; @@ -143,6 +151,12 @@ CServerProxy::parseHandshakeMessage(const UInt8* code) resetOptions(); } + else if (memcmp(code, kMsgCKeepAlive, 4) == 0) { + // echo keep alives and reset alarm + CProtocolUtil::writef(m_stream, kMsgCKeepAlive); + resetKeepAliveAlarm(); + } + else if (memcmp(code, kMsgCNoop, 4) == 0) { // accept and discard no-op } @@ -222,6 +236,12 @@ CServerProxy::parseMessage(const UInt8* code) keyRepeat(); } + else if (memcmp(code, kMsgCKeepAlive, 4) == 0) { + // echo keep alives and reset alarm + CProtocolUtil::writef(m_stream, kMsgCKeepAlive); + resetKeepAliveAlarm(); + } + else if (memcmp(code, kMsgCNoop, 4) == 0) { // accept and discard no-op } @@ -290,9 +310,10 @@ CServerProxy::parseMessage(const UInt8* code) } void -CServerProxy::handleHeartBeat(const CEvent&, void*) +CServerProxy::handleKeepAliveAlarm(const CEvent&, void*) { - CProtocolUtil::writef(m_stream, kMsgCNoop); + LOG((CLOG_NOTE "server is dead")); + m_client->disconnect("server is not responding"); } void @@ -699,12 +720,12 @@ CServerProxy::mouseWheel() flushCompressedMouse(); // parse - SInt16 delta; - CProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &delta); - LOG((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); + SInt16 xDelta, yDelta; + CProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &xDelta, &yDelta); + LOG((CLOG_DEBUG2 "recv mouse wheel %+d,%+d", xDelta, yDelta)); // forward - m_client->mouseWheel(delta); + m_client->mouseWheel(xDelta, yDelta); } void @@ -728,11 +749,8 @@ CServerProxy::resetOptions() // forward m_client->resetOptions(); - // reset heart rate and send heartbeat if necessary - installHeartBeat(kHeartRate); - if (m_heartRate >= 0.0) { - CProtocolUtil::writef(m_stream, kMsgCNoop); - } + // reset keep alive + setKeepAliveRate(kKeepAliveRate); // reset modifier translation table for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) { @@ -770,11 +788,8 @@ CServerProxy::setOptions() id = kKeyModifierIDSuper; } else if (options[i] == kOptionHeartbeat) { - // update heart rate and send heartbeat if necessary - installHeartBeat(1.0e-3 * static_cast(options[i + 1])); - if (m_heartRate >= 0.0) { - CProtocolUtil::writef(m_stream, kMsgCNoop); - } + // update keep alive + setKeepAliveRate(1.0e-3 * static_cast(options[i + 1])); } if (id != kKeyModifierIDNull) { m_modifierTranslationTable[id] = diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index 51263792..f6642696 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -59,7 +59,8 @@ private: void sendInfo(const CClientInfo&); - void installHeartBeat(double); + void resetKeepAliveAlarm(); + void setKeepAliveRate(double); // modifier key translation KeyID translateKey(KeyID) const; @@ -67,7 +68,7 @@ private: // event handlers void handleData(const CEvent&, void*); - void handleHeartBeat(const CEvent&, void*); + void handleKeepAliveAlarm(const CEvent&, void*); // message handlers void enter(); @@ -93,7 +94,6 @@ private: CClient* m_client; IStream* m_stream; - CEventQueueTimer* m_timer; UInt32 m_seqNum; @@ -105,7 +105,9 @@ private: bool m_ignoreMouse; KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; - double m_heartRate; + + double m_keepAliveAlarm; + CEventQueueTimer* m_keepAliveAlarmTimer; MessageParser m_parser; }; diff --git a/lib/client/client.dsp b/lib/client/client.dsp index 1764b7be..6fe46b93 100644 --- a/lib/client/client.dsp +++ b/lib/client/client.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\client.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\client.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\client.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\client.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/common/Version.cpp b/lib/common/Version.cpp index 2347e1df..86fcdef7 100644 --- a/lib/common/Version.cpp +++ b/lib/common/Version.cpp @@ -14,9 +14,9 @@ #include "Version.h" -const char* kApplication = "synergy"; +const char* kApplication = "Synergy"; const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; const char* kWebsite = "http://synergy2.sourceforge.net/"; const char* kVersion = VERSION; -const char* kAppVersion = "synergy " VERSION; +const char* kAppVersion = "Synergy " VERSION; diff --git a/lib/common/Version.h b/lib/common/Version.h index 0eadf6fc..1f98a6a2 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.3.0" +# define VERSION "1.3.1" #endif // important strings diff --git a/lib/common/common.dsp b/lib/common/common.dsp index 55714483..f4f4b8a2 100644 --- a/lib/common/common.dsp +++ b/lib/common/common.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\common.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\common.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\common.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\common.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/common/common.h b/lib/common/common.h index c8f26831..9f51d4e3 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -19,6 +19,11 @@ #if HAVE_CONFIG_H # include "config.h" + + // don't use poll() on mac +# if defined(__APPLE__) +# undef HAVE_POLL +# endif #else // we may not have run configure on win32 # if defined(_WIN32) @@ -31,7 +36,6 @@ # define SYSAPI_UNIX 1 # define WINAPI_CARBON 1 -# define HAVE_ALLOCA_H 1 # define HAVE_CXX_BOOL 1 # define HAVE_CXX_CASTS 1 # define HAVE_CXX_EXCEPTIONS 1 @@ -48,20 +52,26 @@ # define HAVE_POSIX_SIGWAIT 1 # define HAVE_PTHREAD 1 # define HAVE_PTHREAD_SIGNAL 1 +# include +# include +# if defined(_SOCKLEN_T) +# define HAVE_SOCKLEN_T 1 +# endif # define HAVE_SSTREAM 1 # define HAVE_STDINT_H 1 # define HAVE_STDLIB_H 1 # define HAVE_STRINGS_H 1 # define HAVE_STRING_H 1 # define HAVE_SYS_SELECT_H 1 +# define HAVE_SYS_SOCKET_H 1 # define HAVE_SYS_STAT_H 1 # define HAVE_SYS_TIME_H 1 # define HAVE_SYS_TYPES_H 1 # define HAVE_SYS_UTSNAME_H 1 # define HAVE_UNISTD_H 1 # define HAVE_VSNPRINTF 1 -# define HAVE_WCHAR_H 1 -# define HAVE_SYS_SOCKET_H 1 +/* disable this so we can build with the 10.2.8 SDK */ +/*# define HAVE_WCHAR_H 1*/ # define SELECT_TYPE_ARG1 int # define SELECT_TYPE_ARG234 (fd_set *) diff --git a/lib/io/io.dsp b/lib/io/io.dsp index aea8848e..5d117b62 100644 --- a/lib/io/io.dsp +++ b/lib/io/io.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\io.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\io.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\io.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\io.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp index eae9e001..6d7400ed 100644 --- a/lib/mt/mt.dsp +++ b/lib/mt/mt.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\mt.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\mt.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\mt.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\mt.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index 296fa42c..938bf95a 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -59,6 +59,7 @@ CTCPListenSocket::bind(const CNetworkAddress& addr) { try { CLock lock(m_mutex); + ARCH->setReuseAddrOnSocket(m_socket, true); ARCH->bindSocket(m_socket, addr.getAddress()); ARCH->listenOnSocket(m_socket); CSocketMultiplexer::getInstance()->addSocket(this, diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index c6c87789..8e313176 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -485,8 +485,9 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, sendEvent(getDisconnectedEvent()); needNewJob = true; } - catch (XArchNetwork&) { + catch (XArchNetwork& e) { // other write error + LOG((CLOG_WARN "error writing socket: %s", e.what().c_str())); onDisconnected(); sendEvent(getOutputErrorEvent()); sendEvent(getDisconnectedEvent()); @@ -531,8 +532,9 @@ CTCPSocket::serviceConnected(ISocketMultiplexerJob* job, onDisconnected(); needNewJob = true; } - catch (XArchNetwork&) { + catch (XArchNetwork& e) { // ignore other read error + LOG((CLOG_WARN "error reading socket: %s", e.what().c_str())); } } diff --git a/lib/net/net.dsp b/lib/net/net.dsp index 53b1a438..9080dbdb 100644 --- a/lib/net/net.dsp +++ b/lib/net/net.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\net.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\net.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\net.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\net.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index ec56d2c1..4a1c4474 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -14,6 +14,7 @@ #include "CMSWindowsDesks.h" #include "CMSWindowsScreen.h" +#include "CSynergyHook.h" #include "IScreenSaver.h" #include "XScreen.h" #include "CLock.h" @@ -45,8 +46,8 @@ #define WM_NCXBUTTONDOWN 0x00AB #define WM_NCXBUTTONUP 0x00AC #define WM_NCXBUTTONDBLCLK 0x00AD -#define MOUSEEVENTF_XDOWN 0x0100 -#define MOUSEEVENTF_XUP 0x0200 +#define MOUSEEVENTF_XDOWN 0x0080 +#define MOUSEEVENTF_XUP 0x0100 #define XBUTTON1 0x0001 #define XBUTTON2 0x0002 #endif @@ -67,7 +68,7 @@ #define SYNERGY_MSG_FAKE_BUTTON SYNERGY_HOOK_LAST_MSG + 5 // x; y #define SYNERGY_MSG_FAKE_MOVE SYNERGY_HOOK_LAST_MSG + 6 -// delta; +// xDelta; yDelta #define SYNERGY_MSG_FAKE_WHEEL SYNERGY_HOOK_LAST_MSG + 7 // POINT*; #define SYNERGY_MSG_CURSOR_POS SYNERGY_HOOK_LAST_MSG + 8 @@ -77,6 +78,8 @@ #define SYNERGY_MSG_SCREENSAVER SYNERGY_HOOK_LAST_MSG + 10 // dx; dy #define SYNERGY_MSG_FAKE_REL_MOVE SYNERGY_HOOK_LAST_MSG + 11 +// enable; +#define SYNERGY_MSG_FAKE_INPUT SYNERGY_HOOK_LAST_MSG + 12 // // CMSWindowsDesks @@ -106,6 +109,7 @@ CMSWindowsDesks::CMSWindowsDesks( m_cursor = createBlankCursor(); m_deskClass = createDeskWindowClass(m_isPrimary); m_keyLayout = GetKeyboardLayout(GetCurrentThreadId()); + resetOptions(); } CMSWindowsDesks::~CMSWindowsDesks() @@ -164,6 +168,23 @@ CMSWindowsDesks::leave(HKL keyLayout) sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)keyLayout, 0); } +void +CMSWindowsDesks::resetOptions() +{ + m_leaveForegroundOption = false; +} + +void +CMSWindowsDesks::setOptions(const COptionsList& options) +{ + for (UInt32 i = 0, n = options.size(); i < n; i += 2) { + if (options[i] == kOptionWin32KeepForeground) { + m_leaveForegroundOption = (options[i + 1] != 0); + LOG((CLOG_DEBUG1 "%s the foreground window", m_leaveForegroundOption ? "Don\'t grab" : "Grab")); + } + } +} + void CMSWindowsDesks::updateKeys() { @@ -193,6 +214,18 @@ CMSWindowsDesks::installScreensaverHooks(bool install) } } +void +CMSWindowsDesks::fakeInputBegin() +{ + sendMessage(SYNERGY_MSG_FAKE_INPUT, 1, 0); +} + +void +CMSWindowsDesks::fakeInputEnd() +{ + sendMessage(SYNERGY_MSG_FAKE_INPUT, 0, 0); +} + void CMSWindowsDesks::getCursorPos(SInt32& x, SInt32& y) const { @@ -310,9 +343,9 @@ CMSWindowsDesks::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const } void -CMSWindowsDesks::fakeMouseWheel(SInt32 delta) const +CMSWindowsDesks::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const { - sendMessage(SYNERGY_MSG_FAKE_WHEEL, delta, 0); + sendMessage(SYNERGY_MSG_FAKE_WHEEL, xDelta, yDelta); } void @@ -565,9 +598,21 @@ CMSWindowsDesks::deskEnter(CDesk* desk) SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW); - // this is here only because of the "ConsoleWindowClass" stuff in - // deskLeave. + // restore the foreground window + // XXX -- this raises the window to the top of the Z-order. we + // want it to stay wherever it was to properly support X-mouse + // (mouse over activation) but i've no idea how to do that. + // the obvious workaround of using SetWindowPos() to move it back + // after being raised doesn't work. + DWORD thisThread = + GetWindowThreadProcessId(desk->m_window, NULL); + DWORD thatThread = + GetWindowThreadProcessId(desk->m_foregroundWindow, NULL); + AttachThreadInput(thatThread, thisThread, TRUE); + SetForegroundWindow(desk->m_foregroundWindow); + AttachThreadInput(thatThread, thisThread, FALSE); EnableWindow(desk->m_window, desk->m_lowLevel ? FALSE : TRUE); + desk->m_foregroundWindow = NULL; } void @@ -575,9 +620,6 @@ CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout) { ShowCursor(FALSE); if (m_isPrimary) { - // update key state - m_updateKeys->run(); - // map a window to hide the cursor and to use whatever keyboard // layout we choose rather than the keyboard layout of the last // active window. @@ -610,38 +652,26 @@ CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout) SetActiveWindow(desk->m_window); } - // if the active window is a console then activate our window. - // we do this because for some reason our hook reports unshifted - // characters when the shift is down and a console window is - // active. interestingly we do see the shift key go down and up. + // if using low-level hooks then disable the foreground window + // so it can't mess up any of our keyboard events. the console + // program, for example, will cause characters to be reported as + // unshifted, regardless of the shift key state. interestingly + // we do see the shift key go down and up. + // // note that we must enable the window to activate it and we // need to disable the window on deskEnter. - // FIXME -- figure out the real problem here and solve it. else { - HWND foreground = GetForegroundWindow(); - if (foreground != NULL) { - char className[40]; - if (GetClassName(foreground, className, - sizeof(className) / sizeof(className[0])) && - strcmp(className, "ConsoleWindowClass") == 0) { - EnableWindow(desk->m_window, TRUE); - SetActiveWindow(desk->m_window); - - // force our window to the foreground. we can't - // simply call SetForegroundWindow() because that - // will only alert the user that the window wants - // to be the foreground as of windows 98/2000. we - // have to attach to the thread of the current - // foreground window then call it on our window - // and finally detach the threads. - DWORD thisThread = - GetWindowThreadProcessId(desk->m_window, NULL); - DWORD thatThread = - GetWindowThreadProcessId(foreground, NULL); - AttachThreadInput(thatThread, thisThread, TRUE); - SetForegroundWindow(desk->m_window); - AttachThreadInput(thatThread, thisThread, FALSE); - } + desk->m_foregroundWindow = getForegroundWindow(); + if (desk->m_foregroundWindow != NULL) { + EnableWindow(desk->m_window, TRUE); + SetActiveWindow(desk->m_window); + DWORD thisThread = + GetWindowThreadProcessId(desk->m_window, NULL); + DWORD thatThread = + GetWindowThreadProcessId(desk->m_foregroundWindow, NULL); + AttachThreadInput(thatThread, thisThread, TRUE); + SetForegroundWindow(desk->m_window); + AttachThreadInput(thatThread, thisThread, FALSE); } } @@ -671,9 +701,10 @@ CMSWindowsDesks::deskThread(void* vdesk) MSG msg; // use given desktop for this thread - CDesk* desk = reinterpret_cast(vdesk); - desk->m_threadID = GetCurrentThreadId(); - desk->m_window = NULL; + CDesk* desk = reinterpret_cast(vdesk); + desk->m_threadID = GetCurrentThreadId(); + desk->m_window = NULL; + desk->m_foregroundWindow = NULL; if (desk->m_desk != NULL && SetThreadDesktop(desk->m_desk) != 0) { // create a message queue PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE); @@ -763,7 +794,10 @@ CMSWindowsDesks::deskThread(void* vdesk) break; case SYNERGY_MSG_FAKE_WHEEL: - mouse_event(MOUSEEVENTF_WHEEL, 0, 0, msg.wParam, 0); + // XXX -- add support for x-axis scrolling + if (msg.lParam != 0) { + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, msg.lParam, 0); + } break; case SYNERGY_MSG_CURSOR_POS: { @@ -787,6 +821,12 @@ CMSWindowsDesks::deskThread(void* vdesk) m_uninstallScreensaver(); } break; + + case SYNERGY_MSG_FAKE_INPUT: + keybd_event(SYNERGY_HOOK_FAKE_INPUT_VIRTUAL_KEY, + SYNERGY_HOOK_FAKE_INPUT_SCANCODE, + msg.wParam ? 0 : KEYEVENTF_KEYUP, 0); + break; } // notify that message was processed @@ -870,11 +910,12 @@ CMSWindowsDesks::checkDesk() // inaccessible desktop to an accessible one we have to // update the keyboard state. LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str())); + bool syncKeys = false; bool isAccessible = isDeskAccessible(desk); if (isDeskAccessible(m_activeDesk) != isAccessible) { if (isAccessible) { LOG((CLOG_DEBUG "desktop is now accessible")); - sendMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); + syncKeys = true; } else { LOG((CLOG_DEBUG "desktop is now inaccessible")); @@ -890,6 +931,11 @@ CMSWindowsDesks::checkDesk() if (!wasOnScreen) { sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0); } + + // update keys if necessary + if (syncKeys) { + updateKeys(); + } } else if (name != m_activeDeskName) { // screen saver might have started @@ -972,3 +1018,16 @@ CMSWindowsDesks::getDesktopName(HDESK desk) return result; } } + +HWND +CMSWindowsDesks::getForegroundWindow() const +{ + // Ideally we'd return NULL as much as possible, only returning + // the actual foreground window when we know it's going to mess + // up our keyboard input. For now we'll just let the user + // decide. + if (m_leaveForegroundOption) { + return NULL; + } + return GetForegroundWindow(); +} diff --git a/lib/platform/CMSWindowsDesks.h b/lib/platform/CMSWindowsDesks.h index caa1bb7d..f0388f98 100644 --- a/lib/platform/CMSWindowsDesks.h +++ b/lib/platform/CMSWindowsDesks.h @@ -18,6 +18,7 @@ #include "CSynergyHook.h" #include "KeyTypes.h" #include "MouseTypes.h" +#include "OptionTypes.h" #include "CCondVar.h" #include "CMutex.h" #include "CString.h" @@ -92,6 +93,19 @@ public: */ void leave(HKL keyLayout); + //! Notify of options changes + /*! + Resets all options to their default values. + */ + void resetOptions(); + + //! Notify of options changes + /*! + Set options to given values. Ignores unknown options and doesn't + modify options that aren't given in \c options. + */ + void setOptions(const COptionsList& options); + //! Update the key state /*! Causes the key state to get updated to reflect the physical keyboard @@ -115,6 +129,18 @@ public: */ void installScreensaverHooks(bool install); + //! Start ignoring user input + /*! + Starts ignoring user input so we don't pick up our own synthesized events. + */ + void fakeInputBegin(); + + //! Stop ignoring user input + /*! + Undoes whatever \c fakeInputBegin() did. + */ + void fakeInputEnd(); + //@} //! @name accessors //@{ @@ -152,9 +178,9 @@ public: //! Fake mouse wheel /*! - Synthesize a mouse wheel event of amount \c delta. + Synthesize a mouse wheel event of amount \c delta in direction \c axis. */ - void fakeMouseWheel(SInt32 delta) const; + void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; //@} @@ -167,6 +193,7 @@ private: DWORD m_targetID; HDESK m_desk; HWND m_window; + HWND m_foregroundWindow; bool m_lowLevel; }; typedef std::map CDesks; @@ -198,6 +225,9 @@ private: void waitForDesk() const; void sendMessage(UINT, WPARAM, LPARAM) const; + // work around for messed up keyboard events from low-level hooks + HWND getForegroundWindow() const; + // desk API wrappers HDESK openInputDesktop(); void closeDesktop(HDESK); @@ -258,6 +288,9 @@ private: // keyboard stuff IJob* m_updateKeys; HKL m_keyLayout; + + // options + bool m_leaveForegroundOption; }; #endif diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index e65030ac..2baba7d0 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -18,6 +18,8 @@ #include "CFunctionJob.h" #include "CLog.h" #include "CStringUtil.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" #include "CArchMiscWindows.h" // extended mouse buttons @@ -26,698 +28,586 @@ #define VK_XBUTTON2 0x06 #endif -// multimedia keys -#if !defined(VK_BROWSER_BACK) -#define VK_BROWSER_BACK 0xA6 -#define VK_BROWSER_FORWARD 0xA7 -#define VK_BROWSER_REFRESH 0xA8 -#define VK_BROWSER_STOP 0xA9 -#define VK_BROWSER_SEARCH 0xAA -#define VK_BROWSER_FAVORITES 0xAB -#define VK_BROWSER_HOME 0xAC -#define VK_VOLUME_MUTE 0xAD -#define VK_VOLUME_DOWN 0xAE -#define VK_VOLUME_UP 0xAF -#define VK_MEDIA_NEXT_TRACK 0xB0 -#define VK_MEDIA_PREV_TRACK 0xB1 -#define VK_MEDIA_STOP 0xB2 -#define VK_MEDIA_PLAY_PAUSE 0xB3 -#define VK_LAUNCH_MAIL 0xB4 -#define VK_LAUNCH_MEDIA_SELECT 0xB5 -#define VK_LAUNCH_APP1 0xB6 -#define VK_LAUNCH_APP2 0xB7 -#endif -#if !defined(VK_SLEEP) -#define VK_SLEEP 0x5F -#endif - // // CMSWindowsKeyState // -const char* CMSWindowsKeyState::s_vkToName[] = -{ - "vk 0x00", - "Left Button", - "Right Button", - "VK_CANCEL", - "Middle Button", - "vk 0x05", - "vk 0x06", - "vk 0x07", - "VK_BACK", - "VK_TAB", - "vk 0x0a", - "vk 0x0b", - "VK_CLEAR", - "VK_RETURN", - "vk 0x0e", - "vk 0x0f", - "VK_SHIFT", - "VK_CONTROL", - "VK_MENU", - "VK_PAUSE", - "VK_CAPITAL", - "VK_KANA", - "vk 0x16", - "VK_JUNJA", - "VK_FINAL", - "VK_KANJI", - "vk 0x1a", - "VK_ESCAPE", - "VK_CONVERT", - "VK_NONCONVERT", - "VK_ACCEPT", - "VK_MODECHANGE", - "VK_SPACE", - "VK_PRIOR", - "VK_NEXT", - "VK_END", - "VK_HOME", - "VK_LEFT", - "VK_UP", - "VK_RIGHT", - "VK_DOWN", - "VK_SELECT", - "VK_PRINT", - "VK_EXECUTE", - "VK_SNAPSHOT", - "VK_INSERT", - "VK_DELETE", - "VK_HELP", - "VK_0", - "VK_1", - "VK_2", - "VK_3", - "VK_4", - "VK_5", - "VK_6", - "VK_7", - "VK_8", - "VK_9", - "vk 0x3a", - "vk 0x3b", - "vk 0x3c", - "vk 0x3d", - "vk 0x3e", - "vk 0x3f", - "vk 0x40", - "VK_A", - "VK_B", - "VK_C", - "VK_D", - "VK_E", - "VK_F", - "VK_G", - "VK_H", - "VK_I", - "VK_J", - "VK_K", - "VK_L", - "VK_M", - "VK_N", - "VK_O", - "VK_P", - "VK_Q", - "VK_R", - "VK_S", - "VK_T", - "VK_U", - "VK_V", - "VK_W", - "VK_X", - "VK_Y", - "VK_Z", - "VK_LWIN", - "VK_RWIN", - "VK_APPS", - "vk 0x5e", - "VK_SLEEP", - "VK_NUMPAD0", - "VK_NUMPAD1", - "VK_NUMPAD2", - "VK_NUMPAD3", - "VK_NUMPAD4", - "VK_NUMPAD5", - "VK_NUMPAD6", - "VK_NUMPAD7", - "VK_NUMPAD8", - "VK_NUMPAD9", - "VK_MULTIPLY", - "VK_ADD", - "VK_SEPARATOR", - "VK_SUBTRACT", - "VK_DECIMAL", - "VK_DIVIDE", - "VK_F1", - "VK_F2", - "VK_F3", - "VK_F4", - "VK_F5", - "VK_F6", - "VK_F7", - "VK_F8", - "VK_F9", - "VK_F10", - "VK_F11", - "VK_F12", - "VK_F13", - "VK_F14", - "VK_F15", - "VK_F16", - "VK_F17", - "VK_F18", - "VK_F19", - "VK_F20", - "VK_F21", - "VK_F22", - "VK_F23", - "VK_F24", - "vk 0x88", - "vk 0x89", - "vk 0x8a", - "vk 0x8b", - "vk 0x8c", - "vk 0x8d", - "vk 0x8e", - "vk 0x8f", - "VK_NUMLOCK", - "VK_SCROLL", - "vk 0x92", - "vk 0x93", - "vk 0x94", - "vk 0x95", - "vk 0x96", - "vk 0x97", - "vk 0x98", - "vk 0x99", - "vk 0x9a", - "vk 0x9b", - "vk 0x9c", - "vk 0x9d", - "vk 0x9e", - "vk 0x9f", - "VK_LSHIFT", - "VK_RSHIFT", - "VK_LCONTROL", - "VK_RCONTROL", - "VK_LMENU", - "VK_RMENU", - "VK_BROWSER_BACK", - "VK_BROWSER_FORWARD", - "VK_BROWSER_REFRESH", - "VK_BROWSER_STOP", - "VK_BROWSER_SEARCH", - "VK_BROWSER_FAVORITES", - "VK_BROWSER_HOME", - "VK_VOLUME_MUTE", - "VK_VOLUME_DOWN", - "VK_VOLUME_UP", - "VK_MEDIA_NEXT_TRACK", - "VK_MEDIA_PREV_TRACK", - "VK_MEDIA_STOP", - "VK_MEDIA_PLAY_PAUSE", - "VK_LAUNCH_MAIL", - "VK_LAUNCH_MEDIA_SELECT", - "VK_LAUNCH_APP1", - "VK_LAUNCH_APP2", - "vk 0xb8", - "vk 0xb9", - "vk 0xba", - "vk 0xbb", - "vk 0xbc", - "vk 0xbd", - "vk 0xbe", - "vk 0xbf", - "vk 0xc0", - "vk 0xc1", - "vk 0xc2", - "vk 0xc3", - "vk 0xc4", - "vk 0xc5", - "vk 0xc6", - "vk 0xc7", - "vk 0xc8", - "vk 0xc9", - "vk 0xca", - "vk 0xcb", - "vk 0xcc", - "vk 0xcd", - "vk 0xce", - "vk 0xcf", - "vk 0xd0", - "vk 0xd1", - "vk 0xd2", - "vk 0xd3", - "vk 0xd4", - "vk 0xd5", - "vk 0xd6", - "vk 0xd7", - "vk 0xd8", - "vk 0xd9", - "vk 0xda", - "vk 0xdb", - "vk 0xdc", - "vk 0xdd", - "vk 0xde", - "vk 0xdf", - "vk 0xe0", - "vk 0xe1", - "vk 0xe2", - "vk 0xe3", - "vk 0xe4", - "VK_PROCESSKEY", - "vk 0xe6", - "vk 0xe7", - "vk 0xe8", - "vk 0xe9", - "vk 0xea", - "vk 0xeb", - "vk 0xec", - "vk 0xed", - "vk 0xee", - "vk 0xef", - "vk 0xf0", - "vk 0xf1", - "vk 0xf2", - "vk 0xf3", - "vk 0xf4", - "vk 0xf5", - "VK_ATTN", - "VK_CRSEL", - "VK_EXSEL", - "VK_EREOF", - "VK_PLAY", - "VK_ZOOM", - "VK_NONAME", - "VK_PA1", - "VK_OEM_CLEAR", - "vk 0xff" -}; - // map virtual keys to synergy key enumeration -const KeyID CMSWindowsKeyState::s_virtualKey[][2] = +const KeyID CMSWindowsKeyState::s_virtualKey[] = { - /* 0x00 */ { kKeyNone, kKeyNone }, // reserved - /* 0x01 */ { kKeyNone, kKeyNone }, // VK_LBUTTON - /* 0x02 */ { kKeyNone, kKeyNone }, // VK_RBUTTON - /* 0x03 */ { kKeyNone, kKeyBreak }, // VK_CANCEL - /* 0x04 */ { kKeyNone, kKeyNone }, // VK_MBUTTON - /* 0x05 */ { kKeyNone, kKeyNone }, // undefined - /* 0x06 */ { kKeyNone, kKeyNone }, // undefined - /* 0x07 */ { kKeyNone, kKeyNone }, // undefined - /* 0x08 */ { kKeyBackSpace, kKeyNone }, // VK_BACK - /* 0x09 */ { kKeyTab, kKeyNone }, // VK_TAB - /* 0x0a */ { kKeyNone, kKeyNone }, // undefined - /* 0x0b */ { kKeyNone, kKeyNone }, // undefined - /* 0x0c */ { kKeyClear, kKeyClear }, // VK_CLEAR - /* 0x0d */ { kKeyReturn, kKeyKP_Enter }, // VK_RETURN - /* 0x0e */ { kKeyNone, kKeyNone }, // undefined - /* 0x0f */ { kKeyNone, kKeyNone }, // undefined - /* 0x10 */ { kKeyShift_L, kKeyShift_R }, // VK_SHIFT - /* 0x11 */ { kKeyControl_L, kKeyControl_R },// VK_CONTROL - /* 0x12 */ { kKeyAlt_L, kKeyAlt_R }, // VK_MENU - /* 0x13 */ { kKeyPause, kKeyNone }, // VK_PAUSE - /* 0x14 */ { kKeyCapsLock, kKeyNone }, // VK_CAPITAL - /* 0x15 */ { kKeyNone, kKeyNone }, // VK_KANA - /* 0x16 */ { kKeyNone, kKeyNone }, // VK_HANGUL - /* 0x17 */ { kKeyNone, kKeyNone }, // VK_JUNJA - /* 0x18 */ { kKeyNone, kKeyNone }, // VK_FINAL - /* 0x19 */ { kKeyZenkaku, kKeyNone }, // VK_KANJI - /* 0x1a */ { kKeyNone, kKeyNone }, // undefined - /* 0x1b */ { kKeyEscape, kKeyNone }, // VK_ESCAPE - /* 0x1c */ { kKeyHenkan, kKeyNone }, // VK_CONVERT - /* 0x1d */ { kKeyNone, kKeyNone }, // VK_NONCONVERT - /* 0x1e */ { kKeyNone, kKeyNone }, // VK_ACCEPT - /* 0x1f */ { kKeyNone, kKeyNone }, // VK_MODECHANGE - /* 0x20 */ { kKeyNone, kKeyNone }, // VK_SPACE - /* 0x21 */ { kKeyKP_PageUp, kKeyPageUp }, // VK_PRIOR - /* 0x22 */ { kKeyKP_PageDown, kKeyPageDown }, // VK_NEXT - /* 0x23 */ { kKeyKP_End, kKeyEnd }, // VK_END - /* 0x24 */ { kKeyKP_Home, kKeyHome }, // VK_HOME - /* 0x25 */ { kKeyKP_Left, kKeyLeft }, // VK_LEFT - /* 0x26 */ { kKeyKP_Up, kKeyUp }, // VK_UP - /* 0x27 */ { kKeyKP_Right, kKeyRight }, // VK_RIGHT - /* 0x28 */ { kKeyKP_Down, kKeyDown }, // VK_DOWN - /* 0x29 */ { kKeySelect, kKeySelect }, // VK_SELECT - /* 0x2a */ { kKeyNone, kKeyNone }, // VK_PRINT - /* 0x2b */ { kKeyExecute, kKeyExecute }, // VK_EXECUTE - /* 0x2c */ { kKeyPrint, kKeyPrint }, // VK_SNAPSHOT - /* 0x2d */ { kKeyKP_Insert, kKeyInsert }, // VK_INSERT - /* 0x2e */ { kKeyKP_Delete, kKeyDelete }, // VK_DELETE - /* 0x2f */ { kKeyHelp, kKeyHelp }, // VK_HELP - /* 0x30 */ { kKeyNone, kKeyNone }, // VK_0 - /* 0x31 */ { kKeyNone, kKeyNone }, // VK_1 - /* 0x32 */ { kKeyNone, kKeyNone }, // VK_2 - /* 0x33 */ { kKeyNone, kKeyNone }, // VK_3 - /* 0x34 */ { kKeyNone, kKeyNone }, // VK_4 - /* 0x35 */ { kKeyNone, kKeyNone }, // VK_5 - /* 0x36 */ { kKeyNone, kKeyNone }, // VK_6 - /* 0x37 */ { kKeyNone, kKeyNone }, // VK_7 - /* 0x38 */ { kKeyNone, kKeyNone }, // VK_8 - /* 0x39 */ { kKeyNone, kKeyNone }, // VK_9 - /* 0x3a */ { kKeyNone, kKeyNone }, // undefined - /* 0x3b */ { kKeyNone, kKeyNone }, // undefined - /* 0x3c */ { kKeyNone, kKeyNone }, // undefined - /* 0x3d */ { kKeyNone, kKeyNone }, // undefined - /* 0x3e */ { kKeyNone, kKeyNone }, // undefined - /* 0x3f */ { kKeyNone, kKeyNone }, // undefined - /* 0x40 */ { kKeyNone, kKeyNone }, // undefined - /* 0x41 */ { kKeyNone, kKeyNone }, // VK_A - /* 0x42 */ { kKeyNone, kKeyNone }, // VK_B - /* 0x43 */ { kKeyNone, kKeyNone }, // VK_C - /* 0x44 */ { kKeyNone, kKeyNone }, // VK_D - /* 0x45 */ { kKeyNone, kKeyNone }, // VK_E - /* 0x46 */ { kKeyNone, kKeyNone }, // VK_F - /* 0x47 */ { kKeyNone, kKeyNone }, // VK_G - /* 0x48 */ { kKeyNone, kKeyNone }, // VK_H - /* 0x49 */ { kKeyNone, kKeyNone }, // VK_I - /* 0x4a */ { kKeyNone, kKeyNone }, // VK_J - /* 0x4b */ { kKeyNone, kKeyNone }, // VK_K - /* 0x4c */ { kKeyNone, kKeyNone }, // VK_L - /* 0x4d */ { kKeyNone, kKeyNone }, // VK_M - /* 0x4e */ { kKeyNone, kKeyNone }, // VK_N - /* 0x4f */ { kKeyNone, kKeyNone }, // VK_O - /* 0x50 */ { kKeyNone, kKeyNone }, // VK_P - /* 0x51 */ { kKeyNone, kKeyNone }, // VK_Q - /* 0x52 */ { kKeyNone, kKeyNone }, // VK_R - /* 0x53 */ { kKeyNone, kKeyNone }, // VK_S - /* 0x54 */ { kKeyNone, kKeyNone }, // VK_T - /* 0x55 */ { kKeyNone, kKeyNone }, // VK_U - /* 0x56 */ { kKeyNone, kKeyNone }, // VK_V - /* 0x57 */ { kKeyNone, kKeyNone }, // VK_W - /* 0x58 */ { kKeyNone, kKeyNone }, // VK_X - /* 0x59 */ { kKeyNone, kKeyNone }, // VK_Y - /* 0x5a */ { kKeyNone, kKeyNone }, // VK_Z - /* 0x5b */ { kKeyNone, kKeySuper_L }, // VK_LWIN - /* 0x5c */ { kKeyNone, kKeySuper_R }, // VK_RWIN - /* 0x5d */ { kKeyMenu, kKeyMenu }, // VK_APPS - /* 0x5e */ { kKeyNone, kKeyNone }, // undefined - /* 0x5f */ { kKeySleep, kKeyNone }, // VK_SLEEP - /* 0x60 */ { kKeyKP_0, kKeyNone }, // VK_NUMPAD0 - /* 0x61 */ { kKeyKP_1, kKeyNone }, // VK_NUMPAD1 - /* 0x62 */ { kKeyKP_2, kKeyNone }, // VK_NUMPAD2 - /* 0x63 */ { kKeyKP_3, kKeyNone }, // VK_NUMPAD3 - /* 0x64 */ { kKeyKP_4, kKeyNone }, // VK_NUMPAD4 - /* 0x65 */ { kKeyKP_5, kKeyNone }, // VK_NUMPAD5 - /* 0x66 */ { kKeyKP_6, kKeyNone }, // VK_NUMPAD6 - /* 0x67 */ { kKeyKP_7, kKeyNone }, // VK_NUMPAD7 - /* 0x68 */ { kKeyKP_8, kKeyNone }, // VK_NUMPAD8 - /* 0x69 */ { kKeyKP_9, kKeyNone }, // VK_NUMPAD9 - /* 0x6a */ { kKeyKP_Multiply, kKeyNone }, // VK_MULTIPLY - /* 0x6b */ { kKeyKP_Add, kKeyNone }, // VK_ADD - /* 0x6c */ { kKeyKP_Separator, kKeyKP_Separator },// VK_SEPARATOR - /* 0x6d */ { kKeyKP_Subtract, kKeyNone }, // VK_SUBTRACT - /* 0x6e */ { kKeyKP_Decimal, kKeyNone }, // VK_DECIMAL - /* 0x6f */ { kKeyNone, kKeyKP_Divide },// VK_DIVIDE - /* 0x70 */ { kKeyF1, kKeyNone }, // VK_F1 - /* 0x71 */ { kKeyF2, kKeyNone }, // VK_F2 - /* 0x72 */ { kKeyF3, kKeyNone }, // VK_F3 - /* 0x73 */ { kKeyF4, kKeyNone }, // VK_F4 - /* 0x74 */ { kKeyF5, kKeyNone }, // VK_F5 - /* 0x75 */ { kKeyF6, kKeyNone }, // VK_F6 - /* 0x76 */ { kKeyF7, kKeyNone }, // VK_F7 - /* 0x77 */ { kKeyF8, kKeyNone }, // VK_F8 - /* 0x78 */ { kKeyF9, kKeyNone }, // VK_F9 - /* 0x79 */ { kKeyF10, kKeyNone }, // VK_F10 - /* 0x7a */ { kKeyF11, kKeyNone }, // VK_F11 - /* 0x7b */ { kKeyF12, kKeyNone }, // VK_F12 - /* 0x7c */ { kKeyF13, kKeyF13 }, // VK_F13 - /* 0x7d */ { kKeyF14, kKeyF14 }, // VK_F14 - /* 0x7e */ { kKeyF15, kKeyF15 }, // VK_F15 - /* 0x7f */ { kKeyF16, kKeyF16 }, // VK_F16 - /* 0x80 */ { kKeyF17, kKeyF17 }, // VK_F17 - /* 0x81 */ { kKeyF18, kKeyF18 }, // VK_F18 - /* 0x82 */ { kKeyF19, kKeyF19 }, // VK_F19 - /* 0x83 */ { kKeyF20, kKeyF20 }, // VK_F20 - /* 0x84 */ { kKeyF21, kKeyF21 }, // VK_F21 - /* 0x85 */ { kKeyF22, kKeyF22 }, // VK_F22 - /* 0x86 */ { kKeyF23, kKeyF23 }, // VK_F23 - /* 0x87 */ { kKeyF24, kKeyF24 }, // VK_F24 - /* 0x88 */ { kKeyNone, kKeyNone }, // unassigned - /* 0x89 */ { kKeyNone, kKeyNone }, // unassigned - /* 0x8a */ { kKeyNone, kKeyNone }, // unassigned - /* 0x8b */ { kKeyNone, kKeyNone }, // unassigned - /* 0x8c */ { kKeyNone, kKeyNone }, // unassigned - /* 0x8d */ { kKeyNone, kKeyNone }, // unassigned - /* 0x8e */ { kKeyNone, kKeyNone }, // unassigned - /* 0x8f */ { kKeyNone, kKeyNone }, // unassigned - /* 0x90 */ { kKeyNumLock, kKeyNumLock }, // VK_NUMLOCK - /* 0x91 */ { kKeyScrollLock, kKeyNone }, // VK_SCROLL - /* 0x92 */ { kKeyNone, kKeyNone }, // unassigned - /* 0x93 */ { kKeyNone, kKeyNone }, // unassigned - /* 0x94 */ { kKeyNone, kKeyNone }, // unassigned - /* 0x95 */ { kKeyNone, kKeyNone }, // unassigned - /* 0x96 */ { kKeyNone, kKeyNone }, // unassigned - /* 0x97 */ { kKeyNone, kKeyNone }, // unassigned - /* 0x98 */ { kKeyNone, kKeyNone }, // unassigned - /* 0x99 */ { kKeyNone, kKeyNone }, // unassigned - /* 0x9a */ { kKeyNone, kKeyNone }, // unassigned - /* 0x9b */ { kKeyNone, kKeyNone }, // unassigned - /* 0x9c */ { kKeyNone, kKeyNone }, // unassigned - /* 0x9d */ { kKeyNone, kKeyNone }, // unassigned - /* 0x9e */ { kKeyNone, kKeyNone }, // unassigned - /* 0x9f */ { kKeyNone, kKeyNone }, // unassigned - /* 0xa0 */ { kKeyShift_L, kKeyShift_L }, // VK_LSHIFT - /* 0xa1 */ { kKeyShift_R, kKeyShift_R }, // VK_RSHIFT - /* 0xa2 */ { kKeyControl_L, kKeyControl_L },// VK_LCONTROL - /* 0xa3 */ { kKeyControl_R, kKeyControl_R },// VK_RCONTROL - /* 0xa4 */ { kKeyAlt_L, kKeyAlt_L }, // VK_LMENU - /* 0xa5 */ { kKeyAlt_R, kKeyAlt_R }, // VK_RMENU - /* 0xa6 */ { kKeyNone, kKeyWWWBack }, // VK_BROWSER_BACK - /* 0xa7 */ { kKeyNone, kKeyWWWForward },// VK_BROWSER_FORWARD - /* 0xa8 */ { kKeyNone, kKeyWWWRefresh },// VK_BROWSER_REFRESH - /* 0xa9 */ { kKeyNone, kKeyWWWStop }, // VK_BROWSER_STOP - /* 0xaa */ { kKeyNone, kKeyWWWSearch },// VK_BROWSER_SEARCH - /* 0xab */ { kKeyNone, kKeyWWWFavorites },// VK_BROWSER_FAVORITES - /* 0xac */ { kKeyNone, kKeyWWWHome }, // VK_BROWSER_HOME - /* 0xad */ { kKeyNone, kKeyAudioMute },// VK_VOLUME_MUTE - /* 0xae */ { kKeyNone, kKeyAudioDown },// VK_VOLUME_DOWN - /* 0xaf */ { kKeyNone, kKeyAudioUp }, // VK_VOLUME_UP - /* 0xb0 */ { kKeyNone, kKeyAudioNext },// VK_MEDIA_NEXT_TRACK - /* 0xb1 */ { kKeyNone, kKeyAudioPrev },// VK_MEDIA_PREV_TRACK - /* 0xb2 */ { kKeyNone, kKeyAudioStop },// VK_MEDIA_STOP - /* 0xb3 */ { kKeyNone, kKeyAudioPlay },// VK_MEDIA_PLAY_PAUSE - /* 0xb4 */ { kKeyNone, kKeyAppMail }, // VK_LAUNCH_MAIL - /* 0xb5 */ { kKeyNone, kKeyAppMedia }, // VK_LAUNCH_MEDIA_SELECT - /* 0xb6 */ { kKeyNone, kKeyAppUser1 }, // VK_LAUNCH_APP1 - /* 0xb7 */ { kKeyNone, kKeyAppUser2 }, // VK_LAUNCH_APP2 - /* 0xb8 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xb9 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xba */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xbb */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xbc */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xbd */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xbe */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xbf */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xc0 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xc1 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xc2 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xc3 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xc4 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xc5 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xc6 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xc7 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xc8 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xc9 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xca */ { kKeyNone, kKeyNone }, // unassigned - /* 0xcb */ { kKeyNone, kKeyNone }, // unassigned - /* 0xcc */ { kKeyNone, kKeyNone }, // unassigned - /* 0xcd */ { kKeyNone, kKeyNone }, // unassigned - /* 0xce */ { kKeyNone, kKeyNone }, // unassigned - /* 0xcf */ { kKeyNone, kKeyNone }, // unassigned - /* 0xd0 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xd1 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xd2 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xd3 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xd4 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xd5 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xd6 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xd7 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xd8 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xd9 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xda */ { kKeyNone, kKeyNone }, // unassigned - /* 0xdb */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xdc */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xdd */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xde */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xdf */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xe0 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xe1 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xe2 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xe3 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xe4 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xe5 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xe6 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xe7 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xe8 */ { kKeyNone, kKeyNone }, // unassigned - /* 0xe9 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xea */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xeb */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xec */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xed */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xee */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xef */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xf0 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xf1 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xf2 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xf3 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xf4 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xf5 */ { kKeyNone, kKeyNone }, // OEM specific - /* 0xf6 */ { kKeyNone, kKeyNone }, // VK_ATTN - /* 0xf7 */ { kKeyNone, kKeyNone }, // VK_CRSEL - /* 0xf8 */ { kKeyNone, kKeyNone }, // VK_EXSEL - /* 0xf9 */ { kKeyNone, kKeyNone }, // VK_EREOF - /* 0xfa */ { kKeyNone, kKeyNone }, // VK_PLAY - /* 0xfb */ { kKeyNone, kKeyNone }, // VK_ZOOM - /* 0xfc */ { kKeyNone, kKeyNone }, // reserved - /* 0xfd */ { kKeyNone, kKeyNone }, // VK_PA1 - /* 0xfe */ { kKeyNone, kKeyNone }, // VK_OEM_CLEAR - /* 0xff */ { kKeyNone, kKeyNone } // reserved + /* 0x000 */ { kKeyNone }, // reserved + /* 0x001 */ { kKeyNone }, // VK_LBUTTON + /* 0x002 */ { kKeyNone }, // VK_RBUTTON + /* 0x003 */ { kKeyNone }, // VK_CANCEL + /* 0x004 */ { kKeyNone }, // VK_MBUTTON + /* 0x005 */ { kKeyNone }, // VK_XBUTTON1 + /* 0x006 */ { kKeyNone }, // VK_XBUTTON2 + /* 0x007 */ { kKeyNone }, // undefined + /* 0x008 */ { kKeyBackSpace }, // VK_BACK + /* 0x009 */ { kKeyTab }, // VK_TAB + /* 0x00a */ { kKeyNone }, // undefined + /* 0x00b */ { kKeyNone }, // undefined + /* 0x00c */ { kKeyClear }, // VK_CLEAR + /* 0x00d */ { kKeyReturn }, // VK_RETURN + /* 0x00e */ { kKeyNone }, // undefined + /* 0x00f */ { kKeyNone }, // undefined + /* 0x010 */ { kKeyShift_L }, // VK_SHIFT + /* 0x011 */ { kKeyControl_L }, // VK_CONTROL + /* 0x012 */ { kKeyAlt_L }, // VK_MENU + /* 0x013 */ { kKeyPause }, // VK_PAUSE + /* 0x014 */ { kKeyCapsLock }, // VK_CAPITAL + /* 0x015 */ { kKeyNone }, // VK_KANA + /* 0x016 */ { kKeyNone }, // VK_HANGUL + /* 0x017 */ { kKeyNone }, // VK_JUNJA + /* 0x018 */ { kKeyNone }, // VK_FINAL + /* 0x019 */ { kKeyZenkaku }, // VK_KANJI + /* 0x01a */ { kKeyNone }, // undefined + /* 0x01b */ { kKeyEscape }, // VK_ESCAPE + /* 0x01c */ { kKeyHenkan }, // VK_CONVERT + /* 0x01d */ { kKeyNone }, // VK_NONCONVERT + /* 0x01e */ { kKeyNone }, // VK_ACCEPT + /* 0x01f */ { kKeyNone }, // VK_MODECHANGE + /* 0x020 */ { kKeyNone }, // VK_SPACE + /* 0x021 */ { kKeyKP_PageUp }, // VK_PRIOR + /* 0x022 */ { kKeyKP_PageDown },// VK_NEXT + /* 0x023 */ { kKeyKP_End }, // VK_END + /* 0x024 */ { kKeyKP_Home }, // VK_HOME + /* 0x025 */ { kKeyKP_Left }, // VK_LEFT + /* 0x026 */ { kKeyKP_Up }, // VK_UP + /* 0x027 */ { kKeyKP_Right }, // VK_RIGHT + /* 0x028 */ { kKeyKP_Down }, // VK_DOWN + /* 0x029 */ { kKeySelect }, // VK_SELECT + /* 0x02a */ { kKeyNone }, // VK_PRINT + /* 0x02b */ { kKeyExecute }, // VK_EXECUTE + /* 0x02c */ { kKeyPrint }, // VK_SNAPSHOT + /* 0x02d */ { kKeyKP_Insert }, // VK_INSERT + /* 0x02e */ { kKeyKP_Delete }, // VK_DELETE + /* 0x02f */ { kKeyHelp }, // VK_HELP + /* 0x030 */ { kKeyNone }, // VK_0 + /* 0x031 */ { kKeyNone }, // VK_1 + /* 0x032 */ { kKeyNone }, // VK_2 + /* 0x033 */ { kKeyNone }, // VK_3 + /* 0x034 */ { kKeyNone }, // VK_4 + /* 0x035 */ { kKeyNone }, // VK_5 + /* 0x036 */ { kKeyNone }, // VK_6 + /* 0x037 */ { kKeyNone }, // VK_7 + /* 0x038 */ { kKeyNone }, // VK_8 + /* 0x039 */ { kKeyNone }, // VK_9 + /* 0x03a */ { kKeyNone }, // undefined + /* 0x03b */ { kKeyNone }, // undefined + /* 0x03c */ { kKeyNone }, // undefined + /* 0x03d */ { kKeyNone }, // undefined + /* 0x03e */ { kKeyNone }, // undefined + /* 0x03f */ { kKeyNone }, // undefined + /* 0x040 */ { kKeyNone }, // undefined + /* 0x041 */ { kKeyNone }, // VK_A + /* 0x042 */ { kKeyNone }, // VK_B + /* 0x043 */ { kKeyNone }, // VK_C + /* 0x044 */ { kKeyNone }, // VK_D + /* 0x045 */ { kKeyNone }, // VK_E + /* 0x046 */ { kKeyNone }, // VK_F + /* 0x047 */ { kKeyNone }, // VK_G + /* 0x048 */ { kKeyNone }, // VK_H + /* 0x049 */ { kKeyNone }, // VK_I + /* 0x04a */ { kKeyNone }, // VK_J + /* 0x04b */ { kKeyNone }, // VK_K + /* 0x04c */ { kKeyNone }, // VK_L + /* 0x04d */ { kKeyNone }, // VK_M + /* 0x04e */ { kKeyNone }, // VK_N + /* 0x04f */ { kKeyNone }, // VK_O + /* 0x050 */ { kKeyNone }, // VK_P + /* 0x051 */ { kKeyNone }, // VK_Q + /* 0x052 */ { kKeyNone }, // VK_R + /* 0x053 */ { kKeyNone }, // VK_S + /* 0x054 */ { kKeyNone }, // VK_T + /* 0x055 */ { kKeyNone }, // VK_U + /* 0x056 */ { kKeyNone }, // VK_V + /* 0x057 */ { kKeyNone }, // VK_W + /* 0x058 */ { kKeyNone }, // VK_X + /* 0x059 */ { kKeyNone }, // VK_Y + /* 0x05a */ { kKeyNone }, // VK_Z + /* 0x05b */ { kKeySuper_L }, // VK_LWIN + /* 0x05c */ { kKeySuper_R }, // VK_RWIN + /* 0x05d */ { kKeyMenu }, // VK_APPS + /* 0x05e */ { kKeyNone }, // undefined + /* 0x05f */ { kKeySleep }, // VK_SLEEP + /* 0x060 */ { kKeyKP_0 }, // VK_NUMPAD0 + /* 0x061 */ { kKeyKP_1 }, // VK_NUMPAD1 + /* 0x062 */ { kKeyKP_2 }, // VK_NUMPAD2 + /* 0x063 */ { kKeyKP_3 }, // VK_NUMPAD3 + /* 0x064 */ { kKeyKP_4 }, // VK_NUMPAD4 + /* 0x065 */ { kKeyKP_5 }, // VK_NUMPAD5 + /* 0x066 */ { kKeyKP_6 }, // VK_NUMPAD6 + /* 0x067 */ { kKeyKP_7 }, // VK_NUMPAD7 + /* 0x068 */ { kKeyKP_8 }, // VK_NUMPAD8 + /* 0x069 */ { kKeyKP_9 }, // VK_NUMPAD9 + /* 0x06a */ { kKeyKP_Multiply },// VK_MULTIPLY + /* 0x06b */ { kKeyKP_Add }, // VK_ADD + /* 0x06c */ { kKeyKP_Separator },// VK_SEPARATOR + /* 0x06d */ { kKeyKP_Subtract },// VK_SUBTRACT + /* 0x06e */ { kKeyKP_Decimal }, // VK_DECIMAL + /* 0x06f */ { kKeyNone }, // VK_DIVIDE + /* 0x070 */ { kKeyF1 }, // VK_F1 + /* 0x071 */ { kKeyF2 }, // VK_F2 + /* 0x072 */ { kKeyF3 }, // VK_F3 + /* 0x073 */ { kKeyF4 }, // VK_F4 + /* 0x074 */ { kKeyF5 }, // VK_F5 + /* 0x075 */ { kKeyF6 }, // VK_F6 + /* 0x076 */ { kKeyF7 }, // VK_F7 + /* 0x077 */ { kKeyF8 }, // VK_F8 + /* 0x078 */ { kKeyF9 }, // VK_F9 + /* 0x079 */ { kKeyF10 }, // VK_F10 + /* 0x07a */ { kKeyF11 }, // VK_F11 + /* 0x07b */ { kKeyF12 }, // VK_F12 + /* 0x07c */ { kKeyF13 }, // VK_F13 + /* 0x07d */ { kKeyF14 }, // VK_F14 + /* 0x07e */ { kKeyF15 }, // VK_F15 + /* 0x07f */ { kKeyF16 }, // VK_F16 + /* 0x080 */ { kKeyF17 }, // VK_F17 + /* 0x081 */ { kKeyF18 }, // VK_F18 + /* 0x082 */ { kKeyF19 }, // VK_F19 + /* 0x083 */ { kKeyF20 }, // VK_F20 + /* 0x084 */ { kKeyF21 }, // VK_F21 + /* 0x085 */ { kKeyF22 }, // VK_F22 + /* 0x086 */ { kKeyF23 }, // VK_F23 + /* 0x087 */ { kKeyF24 }, // VK_F24 + /* 0x088 */ { kKeyNone }, // unassigned + /* 0x089 */ { kKeyNone }, // unassigned + /* 0x08a */ { kKeyNone }, // unassigned + /* 0x08b */ { kKeyNone }, // unassigned + /* 0x08c */ { kKeyNone }, // unassigned + /* 0x08d */ { kKeyNone }, // unassigned + /* 0x08e */ { kKeyNone }, // unassigned + /* 0x08f */ { kKeyNone }, // unassigned + /* 0x090 */ { kKeyNumLock }, // VK_NUMLOCK + /* 0x091 */ { kKeyScrollLock }, // VK_SCROLL + /* 0x092 */ { kKeyNone }, // unassigned + /* 0x093 */ { kKeyNone }, // unassigned + /* 0x094 */ { kKeyNone }, // unassigned + /* 0x095 */ { kKeyNone }, // unassigned + /* 0x096 */ { kKeyNone }, // unassigned + /* 0x097 */ { kKeyNone }, // unassigned + /* 0x098 */ { kKeyNone }, // unassigned + /* 0x099 */ { kKeyNone }, // unassigned + /* 0x09a */ { kKeyNone }, // unassigned + /* 0x09b */ { kKeyNone }, // unassigned + /* 0x09c */ { kKeyNone }, // unassigned + /* 0x09d */ { kKeyNone }, // unassigned + /* 0x09e */ { kKeyNone }, // unassigned + /* 0x09f */ { kKeyNone }, // unassigned + /* 0x0a0 */ { kKeyShift_L }, // VK_LSHIFT + /* 0x0a1 */ { kKeyShift_R }, // VK_RSHIFT + /* 0x0a2 */ { kKeyControl_L }, // VK_LCONTROL + /* 0x0a3 */ { kKeyControl_R }, // VK_RCONTROL + /* 0x0a4 */ { kKeyAlt_L }, // VK_LMENU + /* 0x0a5 */ { kKeyAlt_R }, // VK_RMENU + /* 0x0a6 */ { kKeyNone }, // VK_BROWSER_BACK + /* 0x0a7 */ { kKeyNone }, // VK_BROWSER_FORWARD + /* 0x0a8 */ { kKeyNone }, // VK_BROWSER_REFRESH + /* 0x0a9 */ { kKeyNone }, // VK_BROWSER_STOP + /* 0x0aa */ { kKeyNone }, // VK_BROWSER_SEARCH + /* 0x0ab */ { kKeyNone }, // VK_BROWSER_FAVORITES + /* 0x0ac */ { kKeyNone }, // VK_BROWSER_HOME + /* 0x0ad */ { kKeyNone }, // VK_VOLUME_MUTE + /* 0x0ae */ { kKeyNone }, // VK_VOLUME_DOWN + /* 0x0af */ { kKeyNone }, // VK_VOLUME_UP + /* 0x0b0 */ { kKeyNone }, // VK_MEDIA_NEXT_TRACK + /* 0x0b1 */ { kKeyNone }, // VK_MEDIA_PREV_TRACK + /* 0x0b2 */ { kKeyNone }, // VK_MEDIA_STOP + /* 0x0b3 */ { kKeyNone }, // VK_MEDIA_PLAY_PAUSE + /* 0x0b4 */ { kKeyNone }, // VK_LAUNCH_MAIL + /* 0x0b5 */ { kKeyNone }, // VK_LAUNCH_MEDIA_SELECT + /* 0x0b6 */ { kKeyNone }, // VK_LAUNCH_APP1 + /* 0x0b7 */ { kKeyNone }, // VK_LAUNCH_APP2 + /* 0x0b8 */ { kKeyNone }, // unassigned + /* 0x0b9 */ { kKeyNone }, // unassigned + /* 0x0ba */ { kKeyNone }, // OEM specific + /* 0x0bb */ { kKeyNone }, // OEM specific + /* 0x0bc */ { kKeyNone }, // OEM specific + /* 0x0bd */ { kKeyNone }, // OEM specific + /* 0x0be */ { kKeyNone }, // OEM specific + /* 0x0bf */ { kKeyNone }, // OEM specific + /* 0x0c0 */ { kKeyNone }, // OEM specific + /* 0x0c1 */ { kKeyNone }, // unassigned + /* 0x0c2 */ { kKeyNone }, // unassigned + /* 0x0c3 */ { kKeyNone }, // unassigned + /* 0x0c4 */ { kKeyNone }, // unassigned + /* 0x0c5 */ { kKeyNone }, // unassigned + /* 0x0c6 */ { kKeyNone }, // unassigned + /* 0x0c7 */ { kKeyNone }, // unassigned + /* 0x0c8 */ { kKeyNone }, // unassigned + /* 0x0c9 */ { kKeyNone }, // unassigned + /* 0x0ca */ { kKeyNone }, // unassigned + /* 0x0cb */ { kKeyNone }, // unassigned + /* 0x0cc */ { kKeyNone }, // unassigned + /* 0x0cd */ { kKeyNone }, // unassigned + /* 0x0ce */ { kKeyNone }, // unassigned + /* 0x0cf */ { kKeyNone }, // unassigned + /* 0x0d0 */ { kKeyNone }, // unassigned + /* 0x0d1 */ { kKeyNone }, // unassigned + /* 0x0d2 */ { kKeyNone }, // unassigned + /* 0x0d3 */ { kKeyNone }, // unassigned + /* 0x0d4 */ { kKeyNone }, // unassigned + /* 0x0d5 */ { kKeyNone }, // unassigned + /* 0x0d6 */ { kKeyNone }, // unassigned + /* 0x0d7 */ { kKeyNone }, // unassigned + /* 0x0d8 */ { kKeyNone }, // unassigned + /* 0x0d9 */ { kKeyNone }, // unassigned + /* 0x0da */ { kKeyNone }, // unassigned + /* 0x0db */ { kKeyNone }, // OEM specific + /* 0x0dc */ { kKeyNone }, // OEM specific + /* 0x0dd */ { kKeyNone }, // OEM specific + /* 0x0de */ { kKeyNone }, // OEM specific + /* 0x0df */ { kKeyNone }, // OEM specific + /* 0x0e0 */ { kKeyNone }, // OEM specific + /* 0x0e1 */ { kKeyNone }, // OEM specific + /* 0x0e2 */ { kKeyNone }, // OEM specific + /* 0x0e3 */ { kKeyNone }, // OEM specific + /* 0x0e4 */ { kKeyNone }, // OEM specific + /* 0x0e5 */ { kKeyNone }, // unassigned + /* 0x0e6 */ { kKeyNone }, // OEM specific + /* 0x0e7 */ { kKeyNone }, // unassigned + /* 0x0e8 */ { kKeyNone }, // unassigned + /* 0x0e9 */ { kKeyNone }, // OEM specific + /* 0x0ea */ { kKeyNone }, // OEM specific + /* 0x0eb */ { kKeyNone }, // OEM specific + /* 0x0ec */ { kKeyNone }, // OEM specific + /* 0x0ed */ { kKeyNone }, // OEM specific + /* 0x0ee */ { kKeyNone }, // OEM specific + /* 0x0ef */ { kKeyNone }, // OEM specific + /* 0x0f0 */ { kKeyNone }, // OEM specific + /* 0x0f1 */ { kKeyNone }, // OEM specific + /* 0x0f2 */ { kKeyNone }, // OEM specific + /* 0x0f3 */ { kKeyNone }, // OEM specific + /* 0x0f4 */ { kKeyNone }, // OEM specific + /* 0x0f5 */ { kKeyNone }, // OEM specific + /* 0x0f6 */ { kKeyNone }, // VK_ATTN + /* 0x0f7 */ { kKeyNone }, // VK_CRSEL + /* 0x0f8 */ { kKeyNone }, // VK_EXSEL + /* 0x0f9 */ { kKeyNone }, // VK_EREOF + /* 0x0fa */ { kKeyNone }, // VK_PLAY + /* 0x0fb */ { kKeyNone }, // VK_ZOOM + /* 0x0fc */ { kKeyNone }, // reserved + /* 0x0fd */ { kKeyNone }, // VK_PA1 + /* 0x0fe */ { kKeyNone }, // VK_OEM_CLEAR + /* 0x0ff */ { kKeyNone }, // reserved + + /* 0x100 */ { kKeyNone }, // reserved + /* 0x101 */ { kKeyNone }, // VK_LBUTTON + /* 0x102 */ { kKeyNone }, // VK_RBUTTON + /* 0x103 */ { kKeyBreak }, // VK_CANCEL + /* 0x104 */ { kKeyNone }, // VK_MBUTTON + /* 0x105 */ { kKeyNone }, // VK_XBUTTON1 + /* 0x106 */ { kKeyNone }, // VK_XBUTTON2 + /* 0x107 */ { kKeyNone }, // undefined + /* 0x108 */ { kKeyNone }, // VK_BACK + /* 0x109 */ { kKeyNone }, // VK_TAB + /* 0x10a */ { kKeyNone }, // undefined + /* 0x10b */ { kKeyNone }, // undefined + /* 0x10c */ { kKeyClear }, // VK_CLEAR + /* 0x10d */ { kKeyKP_Enter }, // VK_RETURN + /* 0x10e */ { kKeyNone }, // undefined + /* 0x10f */ { kKeyNone }, // undefined + /* 0x110 */ { kKeyShift_R }, // VK_SHIFT + /* 0x111 */ { kKeyControl_R }, // VK_CONTROL + /* 0x112 */ { kKeyAlt_R }, // VK_MENU + /* 0x113 */ { kKeyNone }, // VK_PAUSE + /* 0x114 */ { kKeyNone }, // VK_CAPITAL + /* 0x115 */ { kKeyNone }, // VK_KANA + /* 0x116 */ { kKeyNone }, // VK_HANGUL + /* 0x117 */ { kKeyNone }, // VK_JUNJA + /* 0x118 */ { kKeyNone }, // VK_FINAL + /* 0x119 */ { kKeyNone }, // VK_KANJI + /* 0x11a */ { kKeyNone }, // undefined + /* 0x11b */ { kKeyNone }, // VK_ESCAPE + /* 0x11c */ { kKeyNone }, // VK_CONVERT + /* 0x11d */ { kKeyNone }, // VK_NONCONVERT + /* 0x11e */ { kKeyNone }, // VK_ACCEPT + /* 0x11f */ { kKeyNone }, // VK_MODECHANGE + /* 0x120 */ { kKeyNone }, // VK_SPACE + /* 0x121 */ { kKeyPageUp }, // VK_PRIOR + /* 0x122 */ { kKeyPageDown }, // VK_NEXT + /* 0x123 */ { kKeyEnd }, // VK_END + /* 0x124 */ { kKeyHome }, // VK_HOME + /* 0x125 */ { kKeyLeft }, // VK_LEFT + /* 0x126 */ { kKeyUp }, // VK_UP + /* 0x127 */ { kKeyRight }, // VK_RIGHT + /* 0x128 */ { kKeyDown }, // VK_DOWN + /* 0x129 */ { kKeySelect }, // VK_SELECT + /* 0x12a */ { kKeyNone }, // VK_PRINT + /* 0x12b */ { kKeyExecute }, // VK_EXECUTE + /* 0x12c */ { kKeyPrint }, // VK_SNAPSHOT + /* 0x12d */ { kKeyInsert }, // VK_INSERT + /* 0x12e */ { kKeyDelete }, // VK_DELETE + /* 0x12f */ { kKeyHelp }, // VK_HELP + /* 0x130 */ { kKeyNone }, // VK_0 + /* 0x131 */ { kKeyNone }, // VK_1 + /* 0x132 */ { kKeyNone }, // VK_2 + /* 0x133 */ { kKeyNone }, // VK_3 + /* 0x134 */ { kKeyNone }, // VK_4 + /* 0x135 */ { kKeyNone }, // VK_5 + /* 0x136 */ { kKeyNone }, // VK_6 + /* 0x137 */ { kKeyNone }, // VK_7 + /* 0x138 */ { kKeyNone }, // VK_8 + /* 0x139 */ { kKeyNone }, // VK_9 + /* 0x13a */ { kKeyNone }, // undefined + /* 0x13b */ { kKeyNone }, // undefined + /* 0x13c */ { kKeyNone }, // undefined + /* 0x13d */ { kKeyNone }, // undefined + /* 0x13e */ { kKeyNone }, // undefined + /* 0x13f */ { kKeyNone }, // undefined + /* 0x140 */ { kKeyNone }, // undefined + /* 0x141 */ { kKeyNone }, // VK_A + /* 0x142 */ { kKeyNone }, // VK_B + /* 0x143 */ { kKeyNone }, // VK_C + /* 0x144 */ { kKeyNone }, // VK_D + /* 0x145 */ { kKeyNone }, // VK_E + /* 0x146 */ { kKeyNone }, // VK_F + /* 0x147 */ { kKeyNone }, // VK_G + /* 0x148 */ { kKeyNone }, // VK_H + /* 0x149 */ { kKeyNone }, // VK_I + /* 0x14a */ { kKeyNone }, // VK_J + /* 0x14b */ { kKeyNone }, // VK_K + /* 0x14c */ { kKeyNone }, // VK_L + /* 0x14d */ { kKeyNone }, // VK_M + /* 0x14e */ { kKeyNone }, // VK_N + /* 0x14f */ { kKeyNone }, // VK_O + /* 0x150 */ { kKeyNone }, // VK_P + /* 0x151 */ { kKeyNone }, // VK_Q + /* 0x152 */ { kKeyNone }, // VK_R + /* 0x153 */ { kKeyNone }, // VK_S + /* 0x154 */ { kKeyNone }, // VK_T + /* 0x155 */ { kKeyNone }, // VK_U + /* 0x156 */ { kKeyNone }, // VK_V + /* 0x157 */ { kKeyNone }, // VK_W + /* 0x158 */ { kKeyNone }, // VK_X + /* 0x159 */ { kKeyNone }, // VK_Y + /* 0x15a */ { kKeyNone }, // VK_Z + /* 0x15b */ { kKeySuper_L }, // VK_LWIN + /* 0x15c */ { kKeySuper_R }, // VK_RWIN + /* 0x15d */ { kKeyMenu }, // VK_APPS + /* 0x15e */ { kKeyNone }, // undefined + /* 0x15f */ { kKeyNone }, // VK_SLEEP + /* 0x160 */ { kKeyNone }, // VK_NUMPAD0 + /* 0x161 */ { kKeyNone }, // VK_NUMPAD1 + /* 0x162 */ { kKeyNone }, // VK_NUMPAD2 + /* 0x163 */ { kKeyNone }, // VK_NUMPAD3 + /* 0x164 */ { kKeyNone }, // VK_NUMPAD4 + /* 0x165 */ { kKeyNone }, // VK_NUMPAD5 + /* 0x166 */ { kKeyNone }, // VK_NUMPAD6 + /* 0x167 */ { kKeyNone }, // VK_NUMPAD7 + /* 0x168 */ { kKeyNone }, // VK_NUMPAD8 + /* 0x169 */ { kKeyNone }, // VK_NUMPAD9 + /* 0x16a */ { kKeyNone }, // VK_MULTIPLY + /* 0x16b */ { kKeyNone }, // VK_ADD + /* 0x16c */ { kKeyKP_Separator },// VK_SEPARATOR + /* 0x16d */ { kKeyNone }, // VK_SUBTRACT + /* 0x16e */ { kKeyNone }, // VK_DECIMAL + /* 0x16f */ { kKeyKP_Divide }, // VK_DIVIDE + /* 0x170 */ { kKeyNone }, // VK_F1 + /* 0x171 */ { kKeyNone }, // VK_F2 + /* 0x172 */ { kKeyNone }, // VK_F3 + /* 0x173 */ { kKeyNone }, // VK_F4 + /* 0x174 */ { kKeyNone }, // VK_F5 + /* 0x175 */ { kKeyNone }, // VK_F6 + /* 0x176 */ { kKeyNone }, // VK_F7 + /* 0x177 */ { kKeyNone }, // VK_F8 + /* 0x178 */ { kKeyNone }, // VK_F9 + /* 0x179 */ { kKeyNone }, // VK_F10 + /* 0x17a */ { kKeyNone }, // VK_F11 + /* 0x17b */ { kKeyNone }, // VK_F12 + /* 0x17c */ { kKeyF13 }, // VK_F13 + /* 0x17d */ { kKeyF14 }, // VK_F14 + /* 0x17e */ { kKeyF15 }, // VK_F15 + /* 0x17f */ { kKeyF16 }, // VK_F16 + /* 0x180 */ { kKeyF17 }, // VK_F17 + /* 0x181 */ { kKeyF18 }, // VK_F18 + /* 0x182 */ { kKeyF19 }, // VK_F19 + /* 0x183 */ { kKeyF20 }, // VK_F20 + /* 0x184 */ { kKeyF21 }, // VK_F21 + /* 0x185 */ { kKeyF22 }, // VK_F22 + /* 0x186 */ { kKeyF23 }, // VK_F23 + /* 0x187 */ { kKeyF24 }, // VK_F24 + /* 0x188 */ { kKeyNone }, // unassigned + /* 0x189 */ { kKeyNone }, // unassigned + /* 0x18a */ { kKeyNone }, // unassigned + /* 0x18b */ { kKeyNone }, // unassigned + /* 0x18c */ { kKeyNone }, // unassigned + /* 0x18d */ { kKeyNone }, // unassigned + /* 0x18e */ { kKeyNone }, // unassigned + /* 0x18f */ { kKeyNone }, // unassigned + /* 0x190 */ { kKeyNumLock }, // VK_NUMLOCK + /* 0x191 */ { kKeyNone }, // VK_SCROLL + /* 0x192 */ { kKeyNone }, // unassigned + /* 0x193 */ { kKeyNone }, // unassigned + /* 0x194 */ { kKeyNone }, // unassigned + /* 0x195 */ { kKeyNone }, // unassigned + /* 0x196 */ { kKeyNone }, // unassigned + /* 0x197 */ { kKeyNone }, // unassigned + /* 0x198 */ { kKeyNone }, // unassigned + /* 0x199 */ { kKeyNone }, // unassigned + /* 0x19a */ { kKeyNone }, // unassigned + /* 0x19b */ { kKeyNone }, // unassigned + /* 0x19c */ { kKeyNone }, // unassigned + /* 0x19d */ { kKeyNone }, // unassigned + /* 0x19e */ { kKeyNone }, // unassigned + /* 0x19f */ { kKeyNone }, // unassigned + /* 0x1a0 */ { kKeyShift_L }, // VK_LSHIFT + /* 0x1a1 */ { kKeyShift_R }, // VK_RSHIFT + /* 0x1a2 */ { kKeyControl_L }, // VK_LCONTROL + /* 0x1a3 */ { kKeyControl_R }, // VK_RCONTROL + /* 0x1a4 */ { kKeyAlt_L }, // VK_LMENU + /* 0x1a5 */ { kKeyAlt_R }, // VK_RMENU + /* 0x1a6 */ { kKeyWWWBack }, // VK_BROWSER_BACK + /* 0x1a7 */ { kKeyWWWForward }, // VK_BROWSER_FORWARD + /* 0x1a8 */ { kKeyWWWRefresh }, // VK_BROWSER_REFRESH + /* 0x1a9 */ { kKeyWWWStop }, // VK_BROWSER_STOP + /* 0x1aa */ { kKeyWWWSearch }, // VK_BROWSER_SEARCH + /* 0x1ab */ { kKeyWWWFavorites },// VK_BROWSER_FAVORITES + /* 0x1ac */ { kKeyWWWHome }, // VK_BROWSER_HOME + /* 0x1ad */ { kKeyAudioMute }, // VK_VOLUME_MUTE + /* 0x1ae */ { kKeyAudioDown }, // VK_VOLUME_DOWN + /* 0x1af */ { kKeyAudioUp }, // VK_VOLUME_UP + /* 0x1b0 */ { kKeyAudioNext }, // VK_MEDIA_NEXT_TRACK + /* 0x1b1 */ { kKeyAudioPrev }, // VK_MEDIA_PREV_TRACK + /* 0x1b2 */ { kKeyAudioStop }, // VK_MEDIA_STOP + /* 0x1b3 */ { kKeyAudioPlay }, // VK_MEDIA_PLAY_PAUSE + /* 0x1b4 */ { kKeyAppMail }, // VK_LAUNCH_MAIL + /* 0x1b5 */ { kKeyAppMedia }, // VK_LAUNCH_MEDIA_SELECT + /* 0x1b6 */ { kKeyAppUser1 }, // VK_LAUNCH_APP1 + /* 0x1b7 */ { kKeyAppUser2 }, // VK_LAUNCH_APP2 + /* 0x1b8 */ { kKeyNone }, // unassigned + /* 0x1b9 */ { kKeyNone }, // unassigned + /* 0x1ba */ { kKeyNone }, // OEM specific + /* 0x1bb */ { kKeyNone }, // OEM specific + /* 0x1bc */ { kKeyNone }, // OEM specific + /* 0x1bd */ { kKeyNone }, // OEM specific + /* 0x1be */ { kKeyNone }, // OEM specific + /* 0x1bf */ { kKeyNone }, // OEM specific + /* 0x1c0 */ { kKeyNone }, // OEM specific + /* 0x1c1 */ { kKeyNone }, // unassigned + /* 0x1c2 */ { kKeyNone }, // unassigned + /* 0x1c3 */ { kKeyNone }, // unassigned + /* 0x1c4 */ { kKeyNone }, // unassigned + /* 0x1c5 */ { kKeyNone }, // unassigned + /* 0x1c6 */ { kKeyNone }, // unassigned + /* 0x1c7 */ { kKeyNone }, // unassigned + /* 0x1c8 */ { kKeyNone }, // unassigned + /* 0x1c9 */ { kKeyNone }, // unassigned + /* 0x1ca */ { kKeyNone }, // unassigned + /* 0x1cb */ { kKeyNone }, // unassigned + /* 0x1cc */ { kKeyNone }, // unassigned + /* 0x1cd */ { kKeyNone }, // unassigned + /* 0x1ce */ { kKeyNone }, // unassigned + /* 0x1cf */ { kKeyNone }, // unassigned + /* 0x1d0 */ { kKeyNone }, // unassigned + /* 0x1d1 */ { kKeyNone }, // unassigned + /* 0x1d2 */ { kKeyNone }, // unassigned + /* 0x1d3 */ { kKeyNone }, // unassigned + /* 0x1d4 */ { kKeyNone }, // unassigned + /* 0x1d5 */ { kKeyNone }, // unassigned + /* 0x1d6 */ { kKeyNone }, // unassigned + /* 0x1d7 */ { kKeyNone }, // unassigned + /* 0x1d8 */ { kKeyNone }, // unassigned + /* 0x1d9 */ { kKeyNone }, // unassigned + /* 0x1da */ { kKeyNone }, // unassigned + /* 0x1db */ { kKeyNone }, // OEM specific + /* 0x1dc */ { kKeyNone }, // OEM specific + /* 0x1dd */ { kKeyNone }, // OEM specific + /* 0x1de */ { kKeyNone }, // OEM specific + /* 0x1df */ { kKeyNone }, // OEM specific + /* 0x1e0 */ { kKeyNone }, // OEM specific + /* 0x1e1 */ { kKeyNone }, // OEM specific + /* 0x1e2 */ { kKeyNone }, // OEM specific + /* 0x1e3 */ { kKeyNone }, // OEM specific + /* 0x1e4 */ { kKeyNone }, // OEM specific + /* 0x1e5 */ { kKeyNone }, // unassigned + /* 0x1e6 */ { kKeyNone }, // OEM specific + /* 0x1e7 */ { kKeyNone }, // unassigned + /* 0x1e8 */ { kKeyNone }, // unassigned + /* 0x1e9 */ { kKeyNone }, // OEM specific + /* 0x1ea */ { kKeyNone }, // OEM specific + /* 0x1eb */ { kKeyNone }, // OEM specific + /* 0x1ec */ { kKeyNone }, // OEM specific + /* 0x1ed */ { kKeyNone }, // OEM specific + /* 0x1ee */ { kKeyNone }, // OEM specific + /* 0x1ef */ { kKeyNone }, // OEM specific + /* 0x1f0 */ { kKeyNone }, // OEM specific + /* 0x1f1 */ { kKeyNone }, // OEM specific + /* 0x1f2 */ { kKeyNone }, // OEM specific + /* 0x1f3 */ { kKeyNone }, // OEM specific + /* 0x1f4 */ { kKeyNone }, // OEM specific + /* 0x1f5 */ { kKeyNone }, // OEM specific + /* 0x1f6 */ { kKeyNone }, // VK_ATTN + /* 0x1f7 */ { kKeyNone }, // VK_CRSEL + /* 0x1f8 */ { kKeyNone }, // VK_EXSEL + /* 0x1f9 */ { kKeyNone }, // VK_EREOF + /* 0x1fa */ { kKeyNone }, // VK_PLAY + /* 0x1fb */ { kKeyNone }, // VK_ZOOM + /* 0x1fc */ { kKeyNone }, // reserved + /* 0x1fd */ { kKeyNone }, // VK_PA1 + /* 0x1fe */ { kKeyNone }, // VK_OEM_CLEAR + /* 0x1ff */ { kKeyNone } // reserved }; -// map special KeyID keys to virtual key codes plus whether or not -// the key maps to an extended scan code -const UINT CMSWindowsKeyState::s_mapE000[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, VK_SLEEP | 0x100u, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, - /* 0xa4 */ 0, 0, VK_BROWSER_BACK | 0x100u, VK_BROWSER_FORWARD | 0x100u, - /* 0xa8 */ VK_BROWSER_REFRESH | 0x100u, VK_BROWSER_STOP | 0x100u, - /* 0xaa */ VK_BROWSER_SEARCH | 0x100u, VK_BROWSER_FAVORITES | 0x100u, - /* 0xac */ VK_BROWSER_HOME | 0x100u, VK_VOLUME_MUTE | 0x100u, - /* 0xae */ VK_VOLUME_DOWN | 0x100u, VK_VOLUME_UP | 0x100u, - /* 0xb0 */ VK_MEDIA_NEXT_TRACK | 0x100u, VK_MEDIA_PREV_TRACK | 0x100u, - /* 0xb2 */ VK_MEDIA_STOP | 0x100u, VK_MEDIA_PLAY_PAUSE | 0x100u, - /* 0xb4 */ VK_LAUNCH_MAIL | 0x100u, VK_LAUNCH_MEDIA_SELECT | 0x100u, - /* 0xb6 */ VK_LAUNCH_APP1 | 0x100u, VK_LAUNCH_APP2 | 0x100u, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -const UINT CMSWindowsKeyState::s_mapEE00[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ VK_TAB, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -/* in g_mapEF00, 0xac is VK_DECIMAL not VK_SEPARATOR because win32 - * doesn't seem to use VK_SEPARATOR but instead maps VK_DECIMAL to - * the same meaning. */ -const UINT CMSWindowsKeyState::s_mapEF00[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0, - /* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0, - /* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, VK_CONVERT, 0, 0, 0, 0, - /* 0x28 */ 0, 0, VK_KANJI, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ VK_HOME | 0x100u, VK_LEFT | 0x100u, - /* 0x52 */ VK_UP | 0x100u, VK_RIGHT | 0x100u, - /* 0x54 */ VK_DOWN | 0x100u, VK_PRIOR | 0x100u, - /* 0x56 */ VK_NEXT | 0x100u, VK_END | 0x100u, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT | 0x100u, - /* 0x64 */ 0, 0, 0, VK_APPS | 0x100u, - /* 0x68 */ 0, 0, VK_HELP, VK_CANCEL | 0x100u, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK | 0x100u, - /* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN | 0x100u, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP, - /* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT, - /* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD, - /* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE | 0x100u, - /* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, - /* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, - /* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2, - /* 0xc0 */ VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, - /* 0xc8 */ VK_F11, VK_F12, VK_F13 | 0x100u, VK_F14 | 0x100u, - /* 0xcc */ VK_F15 | 0x100u, VK_F16 | 0x100u, - /* 0xce */ VK_F17 | 0x100u, VK_F18 | 0x100u, - /* 0xd0 */ VK_F19 | 0x100u, VK_F20 | 0x100u, - /* 0xd2 */ VK_F21 | 0x100u, VK_F22 | 0x100u, - /* 0xd4 */ VK_F23 | 0x100u, VK_F24 | 0x100u, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT | 0x100u, VK_LCONTROL, - /* 0xe4 */ VK_RCONTROL | 0x100u, VK_CAPITAL, 0, 0, - /* 0xe8 */ 0, VK_LMENU, VK_RMENU | 0x100u, VK_LWIN | 0x100u, - /* 0xec */ VK_RWIN | 0x100u, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE | 0x100u +struct CWin32Modifiers { +public: + UINT m_vk; + KeyModifierMask m_mask; }; -CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks) : +static const CWin32Modifiers s_modifiers[] = +{ + { VK_SHIFT, KeyModifierShift }, + { VK_LSHIFT, KeyModifierShift }, + { VK_RSHIFT, KeyModifierShift }, + { VK_CONTROL, KeyModifierControl }, + { VK_LCONTROL, KeyModifierControl }, + { VK_RCONTROL, KeyModifierControl }, + { VK_MENU, KeyModifierAlt }, + { VK_LMENU, KeyModifierAlt }, + { VK_RMENU, KeyModifierAlt }, + { VK_LWIN, KeyModifierSuper }, + { VK_RWIN, KeyModifierSuper } +}; + +CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks, + void* eventTarget) : m_is95Family(CArchMiscWindows::isWindows95Family()), + m_eventTarget(eventTarget), m_desks(desks), - m_keyLayout(GetKeyboardLayout(0)) + m_keyLayout(GetKeyboardLayout(0)), + m_fixTimer(NULL), + m_lastDown(0), + m_useSavedModifiers(false), + m_savedModifiers(0), + m_originalSavedModifiers(0) { - // do nothing + // look up symbol that's available on winNT family but not win95 + HMODULE userModule = GetModuleHandle("user32.dll"); + m_ToUnicodeEx = (ToUnicodeEx_t)GetProcAddress(userModule, "ToUnicodeEx"); } CMSWindowsKeyState::~CMSWindowsKeyState() { - // do nothing + disable(); +} + +void +CMSWindowsKeyState::disable() +{ + if (m_fixTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); + EVENTQUEUE->deleteTimer(m_fixTimer); + m_fixTimer = NULL; + } + m_lastDown = 0; +} + +KeyButton +CMSWindowsKeyState::virtualKeyToButton(UINT virtualKey) const +{ + return m_virtualKeyToButton[virtualKey & 0xffu]; } void @@ -726,25 +616,39 @@ CMSWindowsKeyState::setKeyLayout(HKL keyLayout) m_keyLayout = keyLayout; } -void -CMSWindowsKeyState::fixKey(void* target, UINT virtualKey) +bool +CMSWindowsKeyState::testAutoRepeat(bool press, bool isRepeat, KeyButton button) { - // check if virtualKey is up but we think it's down. if so then - // synthesize a key release for it. - // - // we use GetAsyncKeyState() to check the state of the keys even - // though we might not be in sync with that yet. - KeyButton button = m_virtKeyToScanCode[virtualKey]; - if (isKeyDown(button) && (GetAsyncKeyState(virtualKey) & 0x8000) == 0) { - // compute appropriate parameters for fake event - LPARAM lParam = 0xc0000000 | ((LPARAM)button << 16); + if (!isRepeat) { + isRepeat = (press && m_lastDown != 0 && button == m_lastDown); + } + if (press) { + m_lastDown = button; + } + else { + m_lastDown = 0; + } + return isRepeat; +} - // process as if it were a key up - KeyModifierMask mask; - KeyID key = mapKeyFromEvent(virtualKey, lParam, &mask); - LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button); - setKeyDown(button, false); +void +CMSWindowsKeyState::saveModifiers() +{ + m_savedModifiers = getActiveModifiers(); + m_originalSavedModifiers = m_savedModifiers; +} + +void +CMSWindowsKeyState::useSavedModifiers(bool enable) +{ + if (enable != m_useSavedModifiers) { + m_useSavedModifiers = enable; + if (!m_useSavedModifiers) { + // transfer any modifier state changes to CKeyState's state + KeyModifierMask mask = m_originalSavedModifiers ^ m_savedModifiers; + getActiveModifiersRValue() = + (getActiveModifiers() & ~mask) | (m_savedModifiers & mask); + } } } @@ -752,19 +656,16 @@ KeyID CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, KeyModifierMask* maskOut) const { - // note: known microsoft bugs - // Q72583 -- MapVirtualKey() maps keypad keys incorrectly - // 95,98: num pad vk code -> invalid scan code - // 95,98,NT4: num pad scan code -> bad vk code except - // SEPARATOR, MULTIPLY, SUBTRACT, ADD + static const KeyModifierMask s_controlAlt = + KeyModifierControl | KeyModifierAlt; - // extract character and virtual key + // extract character, virtual key, and if we didn't use AltGr char c = (char)((charAndVirtKey & 0xff00u) >> 8); UINT vkCode = (charAndVirtKey & 0xffu); + bool noAltGr = ((charAndVirtKey & 0xff0000u) != 0); // handle some keys via table lookup - int extended = ((info >> 24) & 1); - KeyID id = s_virtualKey[vkCode][extended]; + KeyID id = getKeyID(vkCode, (KeyButton)((info >> 16) & 0x1ffu)); // check if not in table; map character to key id if (id == kKeyNone && c != 0) { @@ -789,63 +690,51 @@ CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey, } } - // set mask - KeyModifierMask activeMask = getActiveModifiers(); - bool needAltGr = false; - if (id != kKeyNone && c != 0) { - // note if key requires AltGr. VkKeyScan() can have a problem - // with some characters. there are two problems in particular. - // first, typing a dead key then pressing space will cause - // VkKeyScan() to return 0xffff. second, certain characters - // may map to multiple virtual keys and we might get the wrong - // one. if that happens then we might not get the right - // modifier mask. AltGr+9 on the french keyboard layout (^) - // has this problem. in the first case, we'll assume AltGr is - // required (only because it solves the problems we've seen - // so far). in the second, we'll use whatever the keyboard - // state says. - WORD virtualKeyAndModifierState = VkKeyScanEx(c, m_keyLayout); - if (virtualKeyAndModifierState == 0xffff) { - // there is no mapping. assume AltGr. - LOG((CLOG_DEBUG1 "no VkKeyScan() mapping")); - needAltGr = true; - } - else if (LOBYTE(virtualKeyAndModifierState) != vkCode) { - // we didn't get the key that was actually pressed - LOG((CLOG_DEBUG1 "VkKeyScan() mismatch")); - if ((activeMask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)) { - needAltGr = true; - } - } - else { - BYTE modifierState = HIBYTE(virtualKeyAndModifierState); - if ((modifierState & 6) == 6) { - // key requires ctrl and alt == AltGr - needAltGr = true; - } - } - } - - // map modifier key + // set modifier mask if (maskOut != NULL) { - if (needAltGr) { - activeMask |= KeyModifierModeSwitch; - activeMask &= ~(KeyModifierControl | KeyModifierAlt); + KeyModifierMask active = getActiveModifiers(); + if (!noAltGr && (active & s_controlAlt) == s_controlAlt) { + // if !noAltGr then we're only interested in matching the + // key, not the AltGr. AltGr is down (i.e. control and alt + // are down) but we don't want the client to have to match + // that so we clear it. + active &= ~s_controlAlt; } - else { - activeMask &= ~KeyModifierModeSwitch; - } - *maskOut = activeMask; + *maskOut = active; } return id; } -KeyButton -CMSWindowsKeyState::virtualKeyToButton(UINT virtualKey) const +bool +CMSWindowsKeyState::didGroupsChange() const { - return m_virtKeyToScanCode[virtualKey & 0xffu]; + GroupList groups; + return (getGroups(groups) && groups != m_groups); +} + +UINT +CMSWindowsKeyState::mapKeyToVirtualKey(KeyID key) const +{ + if (key == kKeyNone) { + return 0; + } + KeyToVKMap::const_iterator i = m_keyToVKMap.find(key); + if (i == m_keyToVKMap.end()) { + return 0; + } + else { + return i->second; + } +} + +void +CMSWindowsKeyState::onKey(KeyButton button, bool down, KeyModifierMask newState) +{ + // handle win32 brokenness and forward to superclass + fixKeys(); + CKeyState::onKey(button, down, newState); + fixKeys(); } void @@ -855,47 +744,8 @@ CMSWindowsKeyState::sendKeyEvent(void* target, SInt32 count, KeyButton button) { if (press || isAutoRepeat) { - // if AltGr is required for this key then make sure - // the ctrl and alt keys are *not* down on the - // client. windows simulates AltGr with ctrl and - // alt for some inexplicable reason and clients - // will get confused if they see mode switch and - // ctrl and alt. we'll also need to put ctrl and - // alt back the way they were after we simulate - // the key. - bool ctrlL = isKeyDown(m_virtKeyToScanCode[VK_LCONTROL]); - bool ctrlR = isKeyDown(m_virtKeyToScanCode[VK_RCONTROL]); - bool altL = isKeyDown(m_virtKeyToScanCode[VK_LMENU]); - bool altR = isKeyDown(m_virtKeyToScanCode[VK_RMENU]); - if ((mask & KeyModifierModeSwitch) != 0) { - KeyModifierMask mask2 = (mask & - ~(KeyModifierControl | - KeyModifierAlt | - KeyModifierModeSwitch)); - if (ctrlL) { - CKeyState::sendKeyEvent(target, false, false, - kKeyControl_L, mask2, 1, - m_virtKeyToScanCode[VK_LCONTROL]); - } - if (ctrlR) { - CKeyState::sendKeyEvent(target, false, false, - kKeyControl_R, mask2, 1, - m_virtKeyToScanCode[VK_RCONTROL]); - } - if (altL) { - CKeyState::sendKeyEvent(target, false, false, - kKeyAlt_L, mask2, 1, - m_virtKeyToScanCode[VK_LMENU]); - } - if (altR) { - CKeyState::sendKeyEvent(target, false, false, - kKeyAlt_R, mask2, 1, - m_virtKeyToScanCode[VK_RMENU]); - } - } - // send key - if (press) { + if (press && !isAutoRepeat) { CKeyState::sendKeyEvent(target, true, false, key, mask, 1, button); if (count > 0) { @@ -906,38 +756,6 @@ CMSWindowsKeyState::sendKeyEvent(void* target, CKeyState::sendKeyEvent(target, true, true, key, mask, count, button); } - - // restore ctrl and alt state - if ((mask & KeyModifierModeSwitch) != 0) { - KeyModifierMask mask2 = (mask & - ~(KeyModifierControl | - KeyModifierAlt | - KeyModifierModeSwitch)); - if (ctrlL) { - CKeyState::sendKeyEvent(target, true, false, - kKeyControl_L, mask2, 1, - m_virtKeyToScanCode[VK_LCONTROL]); - mask2 |= KeyModifierControl; - } - if (ctrlR) { - CKeyState::sendKeyEvent(target, true, false, - kKeyControl_R, mask2, 1, - m_virtKeyToScanCode[VK_RCONTROL]); - mask2 |= KeyModifierControl; - } - if (altL) { - CKeyState::sendKeyEvent(target, true, false, - kKeyAlt_L, mask2, 1, - m_virtKeyToScanCode[VK_LMENU]); - mask2 |= KeyModifierAlt; - } - if (altR) { - CKeyState::sendKeyEvent(target, true, false, - kKeyAlt_R, mask2, 1, - m_virtKeyToScanCode[VK_RMENU]); - mask2 |= KeyModifierAlt; - } - } } else { // do key up @@ -945,6 +763,20 @@ CMSWindowsKeyState::sendKeyEvent(void* target, } } +void +CMSWindowsKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button) +{ + CKeyState::fakeKeyDown(id, mask, button); +} + +void +CMSWindowsKeyState::fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button) +{ + CKeyState::fakeKeyRepeat(id, mask, count, button); +} + bool CMSWindowsKeyState::fakeCtrlAltDel() { @@ -960,7 +792,7 @@ CMSWindowsKeyState::fakeCtrlAltDel() else { // simulate ctrl+alt+del fakeKeyDown(kKeyDelete, KeyModifierControl | KeyModifierAlt, - m_virtKeyToScanCode[VK_DELETE]); + virtualKeyToButton(VK_DELETE)); } return true; } @@ -985,762 +817,604 @@ CMSWindowsKeyState::ctrlAltDelThread(void*) } } -const char* -CMSWindowsKeyState::getKeyName(KeyButton button) const +KeyModifierMask +CMSWindowsKeyState::pollActiveModifiers() const { - char keyName[100]; - char keyName2[100]; - CMSWindowsKeyState* self = const_cast(this); - if (GetKeyNameText(button << 16, keyName, sizeof(keyName)) != 0) { - // get the extended name of the key if button is not extended - // or vice versa. if the names are different then report both. - button ^= 0x100u; - if (GetKeyNameText(button << 16, keyName2, sizeof(keyName2)) != 0 && - strcmp(keyName, keyName2) != 0) { - self->m_keyName = CStringUtil::print("%s or %s", keyName, keyName2); - } - else { - self->m_keyName = keyName; + KeyModifierMask state = 0; + + // get non-toggle modifiers from our own shadow key state + for (size_t i = 0; i < sizeof(s_modifiers) / sizeof(s_modifiers[0]); ++i) { + KeyButton button = virtualKeyToButton(s_modifiers[i].m_vk); + if (button != 0 && isKeyDown(button)) { + state |= s_modifiers[i].m_mask; } } - else if (m_scanCodeToVirtKey[button] != 0) { - self->m_keyName = s_vkToName[m_scanCodeToVirtKey[button]]; + + // we can get toggle modifiers from the system + if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) { + state |= KeyModifierCapsLock; } - else { - self->m_keyName = CStringUtil::print("scan code 0x%03x", button); + if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) { + state |= KeyModifierNumLock; } - return m_keyName.c_str(); + if ((GetKeyState(VK_SCROLL) & 0x01) != 0) { + state |= KeyModifierScrollLock; + } + + return state; } -void -CMSWindowsKeyState::doUpdateKeys() +SInt32 +CMSWindowsKeyState::pollActiveGroup() const { - // clear scan code to/from virtual key mapping - memset(m_scanCodeToVirtKey, 0, sizeof(m_scanCodeToVirtKey)); - memset(m_scanCodeToVirtKeyNumLock, 0, sizeof(m_scanCodeToVirtKeyNumLock)); - memset(m_virtKeyToScanCode, 0, sizeof(m_virtKeyToScanCode)); - - // add modifiers. note that ModeSwitch is mapped to VK_RMENU and - // that it's mapped *before* the Alt modifier. we must map it so - // KeyModifierModeSwitch mask can be converted to keystrokes. it - // must be mapped before the Alt modifier so that the Alt modifier - // takes precedence when mapping keystrokes to modifier masks. - KeyButtons keys; - keys.push_back(mapVirtKeyToButton(VK_RMENU)); - addModifier(KeyModifierModeSwitch, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_LSHIFT)); - keys.push_back(mapVirtKeyToButton(VK_RSHIFT)); - addModifier(KeyModifierShift, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_LCONTROL)); - keys.push_back(mapVirtKeyToButton(VK_RCONTROL)); - addModifier(KeyModifierControl, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_LMENU)); - keys.push_back(mapVirtKeyToButton(VK_RMENU)); - addModifier(KeyModifierAlt, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_LWIN)); - keys.push_back(mapVirtKeyToButton(VK_RWIN)); - addModifier(KeyModifierSuper, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_CAPITAL)); - addModifier(KeyModifierCapsLock, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_NUMLOCK)); - addModifier(KeyModifierNumLock, keys); - keys.clear(); - keys.push_back(mapVirtKeyToButton(VK_SCROLL)); - addModifier(KeyModifierScrollLock, keys); - - BYTE keyState[256]; - GetKeyboardState(keyState); - for (UINT i = 1; i < 256; ++i) { - // skip mouse button virtual keys - switch (i) { - case VK_LBUTTON: - case VK_RBUTTON: - case VK_MBUTTON: - case VK_XBUTTON1: - case VK_XBUTTON2: - continue; - - default: - break; - } - - // map to a scancode and back to a virtual key - KeyButton button2; - KeyButton button = mapVirtKeyToButton(i, button2); - if (button == 0) { - continue; - } - - // okay, now we have the scan code for the virtual key. the - // numpad causes some confusion. buttons on the numpad are - // used for two virtual keys (one for num lock off and one - // for num lock on). keep a separate map for virtual keys - // used when num lock is on. - if ((i >= VK_NUMPAD0 && i <= VK_NUMPAD9) || - i == VK_SEPARATOR || i == VK_DECIMAL) { - m_scanCodeToVirtKeyNumLock[button] = i; - m_scanCodeToVirtKeyNumLock[button2] = i; - } - else { - m_scanCodeToVirtKey[button] = i; - m_scanCodeToVirtKey[button2] = i; - } - m_virtKeyToScanCode[i] = button; - - // if the virtual key is VK_DELETE then use the extended - // scan code. this is important for simulating ctrl+alt+del - // which only works with the extended key. - if (i == VK_DELETE) { - m_virtKeyToScanCode[i] |= 0x100u; - } - - // save the key state - if ((keyState[i] & 0x80) != 0) { - setKeyDown(button, true); - } - - // toggle state applies to all keys but we only want it for - // the modifier keys with corresponding lights. - if ((keyState[i] & 0x01) != 0) { - switch (i) { - case VK_CAPITAL: - setToggled(KeyModifierCapsLock); - break; - - case VK_NUMLOCK: - setToggled(KeyModifierNumLock); - break; - - case VK_SCROLL: - setToggled(KeyModifierScrollLock); - break; - } - } - } -} - -void -CMSWindowsKeyState::doFakeKeyEvent(KeyButton button, - bool press, bool isAutoRepeat) -{ - UINT vk = 0; - if ((getActiveModifiers() & KeyModifierNumLock) != 0) { - vk = m_scanCodeToVirtKeyNumLock[button]; - } - if (vk == 0) { - vk = m_scanCodeToVirtKey[button]; - } - m_desks->fakeKeyEvent(button, vk, press, isAutoRepeat); -} - -KeyButton -CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask mask, bool isAutoRepeat) const -{ - UINT extVirtualKey = 0; - - // check for special keys - if ((id & 0xfffff000u) == 0xe000u) { - if ((id & 0xff00u) == 0xe000u) { - extVirtualKey = s_mapE000[id & 0xffu]; - } - else if ((id & 0xff00) == 0xee00) { - extVirtualKey = s_mapEE00[id & 0xffu]; - } - else if ((id & 0xff00) == 0xef00) { - extVirtualKey = s_mapEF00[id & 0xffu]; - } - if (extVirtualKey == 0) { - LOG((CLOG_DEBUG2 "unknown special key")); - return 0; - } - } - - // special handling of VK_SNAPSHOT - if (extVirtualKey == VK_SNAPSHOT) { - // ignore key repeats on print screen - if (!isAutoRepeat) { - // active window (with alt) or fullscreen (without alt)? - BYTE scan = 0; - if ((mask & KeyModifierAlt) != 0) { - scan = 1; - } - - // send events - keybd_event(VK_SNAPSHOT, scan, 0, 0); - keybd_event(VK_SNAPSHOT, scan, KEYEVENTF_KEYUP, 0); - } - return 0; - } - - // handle other special keys - if (extVirtualKey != 0) { - // compute required modifiers - KeyModifierMask requiredMask = 0; - KeyModifierMask outMask = 0; - - // check numeric keypad. note that virtual keys do not distinguish - // between the keypad and non-keypad movement keys. however, the - // virtual keys do distinguish between keypad numbers and operators - // (e.g. add, multiply) and their main keyboard counterparts. - // therefore, we can ignore the num-lock state for movement virtual - // keys but not for numeric keys. - UINT virtualKey = (extVirtualKey & 0xffu); - if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) { - requiredMask |= KeyModifierNumLock; - if ((getActiveModifiers() & KeyModifierNumLock) != 0) { - LOG((CLOG_DEBUG2 "turn on num lock for keypad key")); - outMask |= KeyModifierNumLock; - } - } - - // check for left tab. that requires the shift key. - if (id == kKeyLeftTab) { - requiredMask |= KeyModifierShift; - outMask |= KeyModifierShift; - } - - // now generate the keystrokes and return the resulting modifier mask - KeyButton button = m_virtKeyToScanCode[virtualKey]; - if ((extVirtualKey & 0x100u) != 0) { - button |= 0x100u; - } - LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d scan code 0x%03x mask 0x%04x", id, virtualKey, button, outMask)); - return mapToKeystrokes(keys, button, - outMask, requiredMask, isAutoRepeat); - } - // determine the thread that'll receive this event - // FIXME -- we can't be sure we'll get the right thread here HWND targetWindow = GetForegroundWindow(); DWORD targetThread = GetWindowThreadProcessId(targetWindow, NULL); - // figure out the code page for the target thread. i'm just - // guessing here. get the target thread's keyboard layout, - // extract the language id from that, and choose the code page - // based on that language. - HKL hkl = GetKeyboardLayout(targetThread); - LANGID langID = static_cast(LOWORD(hkl)); - UINT codePage = getCodePageFromLangID(langID); - LOG((CLOG_DEBUG2 "using code page %d and language id 0x%04x for thread 0x%08x", codePage, langID, targetThread)); + // get keyboard layout for the thread + HKL hkl = GetKeyboardLayout(targetThread); - // regular characters are complicated by dead keys. it may not be - // possible to generate a desired character directly. we may need - // to generate a dead key first then some other character. the - // app receiving the events will compose these two characters into - // a single precomposed character. - // - // as best as i can tell this is the simplest way to convert a - // character into its uncomposed version. along the way we'll - // discover if the key cannot be handled at all. we convert - // from wide char to multibyte, then from multibyte to wide char - // forcing conversion to composite characters, then from wide - // char back to multibyte without making precomposed characters. - // - // after the first conversion to multibyte we see if we can map - // the key. if so then we don't bother trying to decompose dead - // keys. - BOOL error; - char multiByte[2 * MB_LEN_MAX]; - wchar_t unicode[2]; - unicode[0] = static_cast(id & 0x0000ffffu); - int nChars = WideCharToMultiByte(codePage, - WC_COMPOSITECHECK | WC_DEFAULTCHAR, - unicode, 1, - multiByte, sizeof(multiByte), - NULL, &error); - if (nChars == 0 || error) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x not in code page", id)); - return 0; - } - KeyButton button = mapCharacter(keys, multiByte[0], hkl, isAutoRepeat); - if (button != 0) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - if (isDeadChar(multiByte[0], hkl, false)) { - // character mapped to a dead key but we want the - // character for real so send a space key afterwards. - LOG((CLOG_DEBUG2 "character mapped to dead key")); - Keystroke keystroke; - keystroke.m_key = m_virtKeyToScanCode[VK_SPACE]; - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); - - // ignore the release of this key since we already - // handled it. - button = 0; - } - return button; - } - nChars = MultiByteToWideChar(codePage, - MB_COMPOSITE | MB_ERR_INVALID_CHARS, - multiByte, nChars, - unicode, 2); - if (nChars == 0) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x mb->wc mapping failed", id)); - return 0; - } - nChars = WideCharToMultiByte(codePage, - 0, - unicode, nChars, - multiByte, sizeof(multiByte), - NULL, &error); - if (nChars == 0 || error) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x wc->mb mapping failed", id)); + // get group + GroupMap::const_iterator i = m_groupMap.find(hkl); + if (i == m_groupMap.end()) { + LOG((CLOG_DEBUG1 "can't find keyboard layout %08x", hkl)); return 0; } - // we expect one or two characters in multiByte. if there are two - // then the *second* is a dead key. process the dead key if there. - // FIXME -- we assume each character is one byte here - if (nChars > 2) { - LOG((CLOG_DEBUG2 "multibyte characters not supported for character 0x%04x", id)); - return 0; - } - if (nChars == 2) { - LOG((CLOG_DEBUG2 "KeyID 0x%08x needs dead key %u", id, (unsigned char)multiByte[1])); - mapCharacter(keys, multiByte[1], hkl, isAutoRepeat); - } - - // process character - LOG((CLOG_DEBUG2 "KeyID 0x%08x maps to character %u", id, (unsigned char)multiByte[0])); - return mapCharacter(keys, multiByte[0], hkl, isAutoRepeat); + return i->second; } -UINT -CMSWindowsKeyState::getCodePageFromLangID(LANGID langid) const +void +CMSWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const { - // construct a locale id from the language id - LCID lcid = MAKELCID(langid, SORT_DEFAULT); - - // get the ANSI code page for this locale - char data[6]; - if (GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, data, 6) == 0) { - // can't get code page - LOG((CLOG_DEBUG1 "can't find code page for langid 0x%04x", langid)); - return CP_ACP; + BYTE keyState[256]; + GetKeyboardState(keyState); + for (KeyButton i = 1; i < 256; ++i) { + if ((keyState[i] & 0x80) != 0) { + pressedKeys.insert(i); + } } - - // convert stringified code page into a number - UINT codePage = static_cast(atoi(data)); - if (codePage == 0) { - // parse failed - LOG((CLOG_DEBUG1 "can't parse code page %s for langid 0x%04x", data, langid)); - return CP_ACP; - } - - return codePage; } -KeyButton -CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey, - KeyButton& extended) const +void +CMSWindowsKeyState::getKeyMap(CKeyMap& keyMap) { - // this method does what MapVirtualKey(virtualKey, 0) should do. - // we have to explicitly set the extended key flag for some - // modifiers because the win32 API is inadequate. we also find - // the unextended and the extended scancodes for those virtual - // keys that have both except for VK_SHIFT, VK_CONTROL, and VK_MENU. - // - // the windows 95 family doesn't map the side distinguishing virtual - // keys. but we know that VK_CONTROL maps to VK_LCONTROL and - // that VK_RCONTROL is the same scan code | 0x100. similarly for - // VK_MENU. but VK_RSHIFT cannot be determined that way so we - // search for it. - extended = 0; - KeyButton button; - if (m_is95Family) { - UINT scancode; - switch (virtualKey) { - case VK_LSHIFT: - button = (KeyButton)MapVirtualKey(VK_SHIFT, 0); - break; + // update keyboard groups + if (getGroups(m_groups)) { + m_groupMap.clear(); + SInt32 numGroups = (SInt32)m_groups.size(); + for (SInt32 g = 0; g < numGroups; ++g) { + m_groupMap[m_groups[g]] = g; + } + } + HKL activeLayout = GetKeyboardLayout(0); - case VK_RSHIFT: - // we have to search - scancode = MapVirtualKey(VK_SHIFT, 0); - for (UINT i = 1; i < 256; ++i) { - if (i != scancode && MapVirtualKey(i, 1) == VK_SHIFT) { - return (KeyButton)(i); + // clear table + memset(m_virtualKeyToButton, 0, sizeof(m_virtualKeyToButton)); + m_keyToVKMap.clear(); + + CKeyMap::KeyItem item; + SInt32 numGroups = (SInt32)m_groups.size(); + for (SInt32 g = 0; g < numGroups; ++g) { + item.m_group = g; + ActivateKeyboardLayout(m_groups[g], 0); + + // clear tables + memset(m_buttonToVK, 0, sizeof(m_buttonToVK)); + memset(m_buttonToNumpadVK, 0, sizeof(m_buttonToNumpadVK)); + + // map buttons (scancodes) to virtual keys + for (KeyButton i = 1; i < 256; ++i) { + UINT vk = MapVirtualKey(i, 1); + if (vk == 0) { + // unmapped + continue; + } + + // deal with certain virtual keys specially + switch (vk) { + case VK_SHIFT: + if (MapVirtualKey(VK_RSHIFT, 0) == i) { + vk = VK_RSHIFT; + } + else { + vk = VK_LSHIFT; + } + break; + + case VK_CONTROL: + vk = VK_LCONTROL; + break; + + case VK_MENU: + vk = VK_LMENU; + break; + + case VK_NUMLOCK: + vk = VK_PAUSE; + break; + + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: + case VK_DECIMAL: + // numpad keys are saved in their own table + m_buttonToNumpadVK[i] = vk; + continue; + + case VK_LWIN: + case VK_RWIN: + // add extended key only for these on 95 family + if (m_is95Family) { + m_buttonToVK[i | 0x100u] = vk; + continue; + } + break; + + case VK_RETURN: + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_INSERT: + case VK_DELETE: + // also add extended key for these + m_buttonToVK[i | 0x100u] = vk; + break; + } + + if (m_buttonToVK[i] == 0) { + m_buttonToVK[i] = vk; + } + } + + // now map virtual keys to buttons. multiple virtual keys may map + // to a single button. if the virtual key matches the one in + // m_buttonToVK then we use the button as is. if not then it's + // either a numpad key and we use the button as is or it's an + // extended button. + for (UINT i = 1; i < 255; ++i) { + // skip virtual keys we don't want + switch (i) { + case VK_LBUTTON: + case VK_RBUTTON: + case VK_MBUTTON: + case VK_XBUTTON1: + case VK_XBUTTON2: + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + continue; + } + + // get the button + KeyButton button = static_cast(MapVirtualKey(i, 0)); + if (button == 0) { + continue; + } + + // deal with certain virtual keys specially + switch (i) { + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: + case VK_DECIMAL: + m_buttonToNumpadVK[button] = i; + break; + + default: + // add extended key if virtual keys don't match + if (m_buttonToVK[button] != i) { + m_buttonToVK[button | 0x100u] = i; + } + break; + } + } + + // add alt+printscreen + if (m_buttonToVK[0x54u] == 0) { + m_buttonToVK[0x54u] = VK_SNAPSHOT; + } + + // set virtual key to button table + if (GetKeyboardLayout(0) == m_groups[g]) { + for (KeyButton i = 0; i < 512; ++i) { + if (m_buttonToVK[i] != 0) { + if (m_virtualKeyToButton[m_buttonToVK[i]] == 0) { + m_virtualKeyToButton[m_buttonToVK[i]] = i; + } + } + if (m_buttonToNumpadVK[i] != 0) { + if (m_virtualKeyToButton[m_buttonToNumpadVK[i]] == 0) { + m_virtualKeyToButton[m_buttonToNumpadVK[i]] = i; + } } } - return 0; + } - case VK_LCONTROL: - case VK_RCONTROL: - button = (KeyButton)MapVirtualKey(VK_CONTROL, 0); - break; + // add numpad keys + for (KeyButton i = 0; i < 512; ++i) { + if (m_buttonToNumpadVK[i] != 0) { + item.m_id = getKeyID(m_buttonToNumpadVK[i], i); + item.m_button = i; + item.m_required = KeyModifierNumLock; + item.m_sensitive = KeyModifierNumLock | KeyModifierShift; + item.m_generates = 0; + item.m_client = m_buttonToNumpadVK[i]; + addKeyEntry(keyMap, item); + } + } - case VK_LMENU: - case VK_RMENU: - button = (KeyButton)MapVirtualKey(VK_MENU, 0); - break; + // add other keys + BYTE keys[256]; + memset(keys, 0, sizeof(keys)); + for (KeyButton i = 0; i < 512; ++i) { + if (m_buttonToVK[i] != 0) { + // initialize item + item.m_id = getKeyID(m_buttonToVK[i], i); + item.m_button = i; + item.m_required = 0; + item.m_sensitive = 0; + item.m_client = m_buttonToVK[i]; - case VK_PAUSE: - // mapped to 0. i hope this works on all keyboards. - button = (KeyButton)0x45u; - break; + // get flags for modifier keys + CKeyMap::initModifierKey(item); - case VK_DIVIDE: - // mapped to 0. i hope this works on all keyboards. - button = (KeyButton)0x35u; - break; + if (item.m_id == 0) { + // translate virtual key to a character with and without + // shift, caps lock, and AltGr. + struct Modifier { + UINT m_vk1; + UINT m_vk2; + BYTE m_state; + KeyModifierMask m_mask; + }; + static const Modifier modifiers[] = { + { VK_SHIFT, VK_SHIFT, 0x80u, KeyModifierShift }, + { VK_CAPITAL, VK_CAPITAL, 0x01u, KeyModifierCapsLock }, + { VK_CONTROL, VK_MENU, 0x80u, KeyModifierControl | + KeyModifierAlt } + }; + static const size_t s_numModifiers = + sizeof(modifiers) / sizeof(modifiers[0]); + static const size_t s_numCombinations = 1 << s_numModifiers; + KeyID id[s_numCombinations]; - default: - button = (KeyButton)MapVirtualKey(virtualKey, 0); + bool anyFound = false; + KeyButton button = static_cast(i & 0xffu); + for (size_t j = 0; j < s_numCombinations; ++j) { + for (size_t k = 0; k < s_numModifiers; ++k) { + if ((j & (1 << k)) != 0) { + keys[modifiers[k].m_vk1] = modifiers[k].m_state; + keys[modifiers[k].m_vk2] = modifiers[k].m_state; + } + else { + keys[modifiers[k].m_vk1] = 0; + keys[modifiers[k].m_vk2] = 0; + } + } + id[j] = getIDForKey(item, button, + m_buttonToVK[i], keys, m_groups[g]); + if (id[j] != 0) { + anyFound = true; + } + } - // okay, now we have the scan code for the virtual key. windows - // may map different virtual keys to the same button. for example, - // windows 95/98/me maps virtual keys 220 and 226 to scan code 86 - // in the british english keyboard map. why? who knows. it - // doesn't make any sense since a button can't actually generate - // more than one virtual key. to avoid this stupidity, we map the - // button back to a virtual key to see if it matches the starting - // point. we don't do this for number pad keys since we expect - // each key to generate one of two virtual keys, depending on the - // state of NumLock, a state we can't pass to MapVirtualKey. - if ((virtualKey < VK_NUMPAD0 || virtualKey > VK_NUMPAD9) && - virtualKey != VK_SEPARATOR && virtualKey != VK_DECIMAL) { - if (button == 0 || MapVirtualKey(button, 1) != virtualKey) { - return 0; + if (anyFound) { + // determine what modifiers we're sensitive to. + // we're sensitive if the KeyID changes when the + // modifier does. + item.m_sensitive = 0; + for (size_t k = 0; k < s_numModifiers; ++k) { + for (size_t j = 0; j < s_numCombinations; ++j) { + if (id[j] != id[j ^ (1u << k)]) { + item.m_sensitive |= modifiers[k].m_mask; + break; + } + } + } + + // save each key. the map will automatically discard + // duplicates, like an unshift and shifted version of + // a key that's insensitive to shift. + for (size_t j = 0; j < s_numCombinations; ++j) { + item.m_id = id[j]; + item.m_required = 0; + for (size_t k = 0; k < s_numModifiers; ++k) { + if ((j & (1 << k)) != 0) { + item.m_required |= modifiers[k].m_mask; + } + } + addKeyEntry(keyMap, item); + } + } + } + else { + // found in table + switch (m_buttonToVK[i]) { + case VK_TAB: + // add kKeyLeftTab, too + item.m_id = kKeyLeftTab; + item.m_required |= KeyModifierShift; + item.m_sensitive |= KeyModifierShift; + addKeyEntry(keyMap, item); + item.m_id = kKeyTab; + item.m_required &= ~KeyModifierShift; + break; + + case VK_CANCEL: + item.m_required |= KeyModifierControl; + item.m_sensitive |= KeyModifierControl; + break; + + case VK_SNAPSHOT: + item.m_sensitive |= KeyModifierAlt; + if ((i & 0x100u) == 0) { + // non-extended snapshot key requires alt + item.m_required |= KeyModifierAlt; + } + break; + } + addKeyEntry(keyMap, item); } } + } + } + + // restore keyboard layout + ActivateKeyboardLayout(activeLayout, 0); +} + +void +CMSWindowsKeyState::fakeKey(const Keystroke& keystroke) +{ + switch (keystroke.m_type) { + case Keystroke::kButton: { + LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up")); + KeyButton button = keystroke.m_data.m_button.m_button; + + // windows doesn't send key ups for key repeats + if (keystroke.m_data.m_button.m_repeat && + !keystroke.m_data.m_button.m_press) { + LOG((CLOG_DEBUG1 " discard key repeat release")); break; } + + // get the virtual key for the button + UINT vk = keystroke.m_data.m_button.m_client; + + // special handling of VK_SNAPSHOT + if (vk == VK_SNAPSHOT) { + if ((getActiveModifiers() & KeyModifierAlt) != 0) { + // snapshot active window + button = 1; + } + else { + // snapshot full screen + button = 0; + } + } + + // synthesize event + m_desks->fakeKeyEvent(button, vk, + keystroke.m_data.m_button.m_press, + keystroke.m_data.m_button.m_repeat); + break; + } + + case Keystroke::kGroup: + // we don't restore the group. we'd like to but we can't be + // sure the restoring group change will be processed after the + // key events. + if (!keystroke.m_data.m_group.m_restore) { + if (keystroke.m_data.m_group.m_absolute) { + LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group)); + setWindowGroup(keystroke.m_data.m_group.m_group); + } + else { + LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group)); + setWindowGroup(getEffectiveGroup(pollActiveGroup(), + keystroke.m_data.m_group.m_group)); + } + } + break; + } +} + +KeyModifierMask& +CMSWindowsKeyState::getActiveModifiersRValue() +{ + if (m_useSavedModifiers) { + return m_savedModifiers; } else { - switch (virtualKey) { - case VK_PAUSE: - // mapped to 0. i hope this works on all keyboards. - button = (KeyButton)0x45u; - break; - - default: - button = (KeyButton)MapVirtualKey(virtualKey, 0); - break; - } + return CKeyState::getActiveModifiersRValue(); } - - // map extended keys - switch (virtualKey) { - case VK_RETURN: // Return/numpad Enter - case VK_PRIOR: // numpad PageUp/PageUp - case VK_NEXT: // numpad PageDown/PageDown - case VK_END: // numpad End/End - case VK_HOME: // numpad Home/Home - case VK_LEFT: // numpad Left/Left - case VK_UP: // numpad Up/Up - case VK_RIGHT: // numpad Right/Right - case VK_DOWN: // numpad Down/Down - case VK_INSERT: // numpad Insert/Insert - case VK_DELETE: // numpad Delete/Delete -// case VK_SELECT: -// case VK_EXECUTE: -// case VK_HELP: - extended = (KeyButton)(button | 0x100u); - break; - } - - // see if the win32 API can help us determine an extended key. - // if the remapped virtual key doesn't match the starting - // point then there's a really good chance that that virtual - // key is mapped to an extended key. however, this is not - // the case for modifiers that don't distinguish between left - // and right. - UINT virtualKey2 = MapVirtualKey(button, 3); - if (virtualKey2 != 0 && virtualKey2 != virtualKey) { - switch (virtualKey) { - case VK_SHIFT: - case VK_CONTROL: - case VK_MENU: - break; - - case VK_NUMPAD0: - case VK_NUMPAD1: - case VK_NUMPAD2: - case VK_NUMPAD3: - case VK_NUMPAD4: - case VK_NUMPAD5: - case VK_NUMPAD6: - case VK_NUMPAD7: - case VK_NUMPAD8: - case VK_NUMPAD9: - case VK_MULTIPLY: - case VK_ADD: - case VK_SEPARATOR: - case VK_SUBTRACT: - case VK_DECIMAL: - break; - - case VK_PAUSE: - break; - - default: - button |= 0x100u; - extended = 0; - break; - } - return button; - } - - // note other extended keys that the win32 API won't help us with. - // on the windows 95 family this is the only way to find extended - // keys since MapVirtualKey(N, 3) is unimplemented. - switch (virtualKey) { - case VK_CANCEL: - case VK_LWIN: - case VK_RWIN: - case VK_APPS: -// case VK_SEPARATOR: - case VK_DIVIDE: - case VK_F13: - case VK_F14: - case VK_F15: - case VK_F16: - case VK_F17: - case VK_F18: - case VK_F19: - case VK_F20: - case VK_F21: - case VK_F22: - case VK_F23: - case VK_F24: - case VK_NUMLOCK: - case VK_RSHIFT: - case VK_RCONTROL: - case VK_RMENU: - case VK_BROWSER_BACK: - case VK_BROWSER_FORWARD: - case VK_BROWSER_REFRESH: - case VK_BROWSER_STOP: - case VK_BROWSER_SEARCH: - case VK_BROWSER_FAVORITES: - case VK_BROWSER_HOME: - case VK_VOLUME_MUTE: - case VK_VOLUME_DOWN: - case VK_VOLUME_UP: - case VK_MEDIA_NEXT_TRACK: - case VK_MEDIA_PREV_TRACK: - case VK_MEDIA_STOP: - case VK_MEDIA_PLAY_PAUSE: - case VK_LAUNCH_MAIL: - case VK_LAUNCH_MEDIA_SELECT: - case VK_LAUNCH_APP1: - case VK_LAUNCH_APP2: - button |= 0x100u; - extended = 0; - break; - } - - return button; -} - -KeyButton -CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey) const -{ - KeyButton dummy; - return mapVirtKeyToButton(virtualKey, dummy); -} - -KeyButton -CMSWindowsKeyState::mapCharacter(Keystrokes& keys, - char c, HKL hkl, bool isAutoRepeat) const -{ - KeyModifierMask activeMask = getActiveModifiers(); - - // translate the character into its virtual key and its required - // modifier state. - SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); - - // get virtual key - UINT virtualKey = LOBYTE(virtualKeyAndModifierState); - if (virtualKey == 0xffu) { - LOG((CLOG_DEBUG2 "cannot map character %d", static_cast(c))); - return 0; - } - - // get the required modifier state - BYTE modifierState = HIBYTE(virtualKeyAndModifierState); - - // see what modifiers are needed. we only check for shift and - // AltGr. we must always match the desired shift state but only - // the desired AltGr state if AltGr is required. AltGr is actually - // ctrl + alt so we can't require that ctrl and alt not be pressed - // otherwise users couldn't do, say, ctrl+z. - // - // the space character (ascii 32) is special in that it's unaffected - // by shift and should match our stored shift state. - KeyModifierMask desiredMask = 0; - KeyModifierMask requiredMask = KeyModifierShift; - if (c == 32) { - desiredMask |= (activeMask & KeyModifierShift); - } - else if ((modifierState & 0x01u) == 1) { - desiredMask |= KeyModifierShift; - } - if ((modifierState & 0x06u) == 6) { - // add ctrl and alt, which must be matched. match alt via - // mode-switch, which uses the right alt key rather than - // the left. windows doesn't care which alt key so long - // as ctrl is also down but some apps do their own mapping - // and they do care. Emacs and PuTTY, for example. - desiredMask |= KeyModifierControl | KeyModifierModeSwitch; - requiredMask |= KeyModifierControl | KeyModifierModeSwitch; - } - - // handle combination of caps-lock and shift. if caps-lock is - // off locally then use shift as necessary. if caps-lock is on - // locally then it reverses the meaning of shift for keys that - // are subject to case conversion. - if ((activeMask & KeyModifierCapsLock) != 0) { - // there doesn't seem to be a simple way to test if a - // character respects the caps lock key. for normal - // characters it's easy enough but CharLower() and - // CharUpper() don't map dead keys even though they - // do respect caps lock for some unfathomable reason. - // first check the easy way. if that doesn't work - // then see if it's a dead key. - unsigned char uc = static_cast(c); - if (CharLower(reinterpret_cast(uc)) != - CharUpper(reinterpret_cast(uc)) || - (MapVirtualKeyEx(virtualKey, 2, hkl) & 0x80000000lu) != 0) { - LOG((CLOG_DEBUG2 "flip shift")); - desiredMask ^= KeyModifierShift; - } - } - - // now generate the keystrokes. ignore the resulting modifier - // mask since it can't have changed (because we don't call this - // method for modifier keys). - KeyButton scanCode = m_virtKeyToScanCode[virtualKey]; - LOG((CLOG_DEBUG2 "character %d to virtual key %d scan code 0x%04x mask 0x%08x", (unsigned char)c, virtualKey, scanCode, desiredMask)); - return mapToKeystrokes(keys, scanCode, - desiredMask, requiredMask, isAutoRepeat); -} - -KeyButton -CMSWindowsKeyState::mapToKeystrokes(Keystrokes& keys, KeyButton button, - KeyModifierMask desiredMask, KeyModifierMask requiredMask, - bool isAutoRepeat) const -{ - // adjust the modifiers to match the desired modifiers - Keystrokes undo; - if (!adjustModifiers(keys, undo, desiredMask, requiredMask)) { - LOG((CLOG_DEBUG2 "failed to adjust modifiers")); - keys.clear(); - return 0; - } - - // add the key event - Keystroke keystroke; - keystroke.m_key = button; - keystroke.m_press = true; - keystroke.m_repeat = isAutoRepeat; - keys.push_back(keystroke); - - // put undo keystrokes at end of keystrokes in reverse order - while (!undo.empty()) { - keys.push_back(undo.back()); - undo.pop_back(); - } - - return button; } bool -CMSWindowsKeyState::adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask) const +CMSWindowsKeyState::getGroups(GroupList& groups) const { - // for each modifier in requiredMask make sure the current state - // of that modifier matches the bit in desiredMask. - for (KeyModifierMask mask = 1u; requiredMask != 0; mask <<= 1) { - if ((mask & requiredMask) != 0) { - bool active = ((desiredMask & mask) != 0); - if (!mapModifier(keys, undo, mask, active)) { - return false; - } - requiredMask ^= mask; - } + // get keyboard layouts + UInt32 newNumLayouts = GetKeyboardLayoutList(0, NULL); + if (newNumLayouts == 0) { + LOG((CLOG_DEBUG1 "can't get keyboard layouts")); + return false; } + HKL* newLayouts = new HKL[newNumLayouts]; + newNumLayouts = GetKeyboardLayoutList(newNumLayouts, newLayouts); + if (newNumLayouts == 0) { + LOG((CLOG_DEBUG1 "can't get keyboard layouts")); + delete[] newLayouts; + return false; + } + + groups.clear(); + groups.insert(groups.end(), newLayouts, newLayouts + newNumLayouts); + delete[] newLayouts; return true; } -int -CMSWindowsKeyState::toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const +void +CMSWindowsKeyState::setWindowGroup(SInt32 group) { - // ignore bogus character - if (c == 0) { - return 0; - } + HWND targetWindow = GetForegroundWindow(); - // translate the character into its virtual key and its required - // modifier state. - SHORT virtualKeyAndModifierState = VkKeyScanEx(c, hkl); + bool sysCharSet = true; + // XXX -- determine if m_groups[group] can be used with the system + // character set. - // get virtual key - BYTE virtualKey = LOBYTE(virtualKeyAndModifierState); - if (virtualKey == 0xffu) { - return 0; - } + PostMessage(targetWindow, WM_INPUTLANGCHANGEREQUEST, + sysCharSet ? 1 : 0, (LPARAM)m_groups[group]); - // get the required modifier state - BYTE modifierState = HIBYTE(virtualKeyAndModifierState); - - // set shift state required to generate key - BYTE keys[256]; - memset(keys, 0, sizeof(keys)); - if (modifierState & 0x01u) { - keys[VK_SHIFT] = 0x80u; - } - if (modifierState & 0x02u) { - keys[VK_CONTROL] = 0x80u; - } - if (modifierState & 0x04u) { - keys[VK_MENU] = 0x80u; - } - - // get the scan code for the key - UINT scanCode = MapVirtualKeyEx(virtualKey, 0, hkl); - - // discard characters if chars is NULL - WORD dummy; - if (chars == NULL) { - chars = &dummy; - } - - // put it back - return ToAsciiEx(virtualKey, scanCode, keys, chars, menu ? 1 : 0, hkl); + // XXX -- use a short delay to let the target window process the message + // before it sees the keyboard events. i'm not sure why this is + // necessary since the messages should arrive in order. if we don't + // delay, though, some of our keyboard events may disappear. + Sleep(100); } -bool -CMSWindowsKeyState::isDeadChar(TCHAR c, HKL hkl, bool menu) const +void +CMSWindowsKeyState::fixKeys() { - // first clear out ToAsciiEx()'s internal buffer by sending it - // a space. - WORD ascii; - int old = toAscii(' ', hkl, 0, &ascii); - - // now pass the character of interest - WORD dummy; - bool isDead = (toAscii(c, hkl, menu, &dummy) < 0); - - // clear out internal buffer again - toAscii(' ', hkl, 0, &dummy); - - // put old dead key back if there was one - if (old == 1 && ascii != ' ') { - toAscii(static_cast(ascii & 0xffu), hkl, menu, &dummy); + // fake key releases for the windows keys if we think they're + // down but they're really up. we have to do this because if the + // user presses and releases a windows key without pressing any + // other key while it's down then the system will eat the key + // release. if we don't detect that and synthesize the release + // then the client won't take the usual windows key release action + // (which on windows is to show the start menu). + // + // only check on the windows 95 family since the NT family reports + // the key releases as usual. + if (!m_is95Family) { + return; } - return isDead; + KeyButton leftButton = virtualKeyToButton(VK_LWIN); + KeyButton rightButton = virtualKeyToButton(VK_RWIN); + bool leftDown = isKeyDown(leftButton); + bool rightDown = isKeyDown(rightButton); + bool fix = (leftDown || rightDown); + if (fix) { + // check if either button is not really down + bool leftAsyncDown = ((GetAsyncKeyState(VK_LWIN) & 0x8000) != 0); + bool rightAsyncDown = ((GetAsyncKeyState(VK_RWIN) & 0x8000) != 0); + + if (leftAsyncDown != leftDown || rightAsyncDown != rightDown) { + KeyModifierMask state = getActiveModifiers(); + if (!leftAsyncDown && !rightAsyncDown) { + // no win keys are down so remove super modifier + state &= ~KeyModifierSuper; + } + + // report up events + if (leftDown && !leftAsyncDown) { + LOG((CLOG_DEBUG1 "event: fake key release left windows key (0x%03x)", leftButton)); + CKeyState::onKey(leftButton, false, state); + CKeyState::sendKeyEvent(m_eventTarget, false, false, + kKeySuper_L, state, 1, leftButton); + } + if (rightDown && !rightAsyncDown) { + LOG((CLOG_DEBUG1 "event: fake key release right windows key (0x%03x)", rightButton)); + CKeyState::onKey(rightButton, false, state); + CKeyState::sendKeyEvent(m_eventTarget, false, false, + kKeySuper_R, state, 1, rightButton); + } + } + } + + if (fix && m_fixTimer == NULL) { + // schedule check + m_fixTimer = EVENTQUEUE->newTimer(0.1, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer, + new TMethodEventJob( + this, &CMSWindowsKeyState::handleFixKeys)); + } + else if (!fix && m_fixTimer != NULL) { + // remove scheduled check + EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); + EVENTQUEUE->deleteTimer(m_fixTimer); + m_fixTimer = NULL; + } +} + +void +CMSWindowsKeyState::handleFixKeys(const CEvent&, void*) +{ + fixKeys(); +} + +KeyID +CMSWindowsKeyState::getKeyID(UINT virtualKey, KeyButton button) +{ + if ((button & 0x100u) != 0) { + virtualKey += 0x100u; + } + return s_virtualKey[virtualKey]; +} + +KeyID +CMSWindowsKeyState::getIDForKey(CKeyMap::KeyItem& item, + KeyButton button, UINT virtualKey, + PBYTE keyState, HKL hkl) const +{ + int n; + KeyID id; + if (m_is95Family) { + // XXX -- how do we get characters not in Latin-1? + WORD ascii; + n = ToAsciiEx(virtualKey, button, keyState, &ascii, 0, hkl); + id = static_cast(ascii & 0xffu); + } + else { + WCHAR unicode[2]; + n = m_ToUnicodeEx(virtualKey, button, keyState, + unicode, sizeof(unicode) / sizeof(unicode[0]), + 0, hkl); + id = static_cast(unicode[0]); + } + switch (n) { + case -1: + return CKeyMap::getDeadKey(id); + + default: + case 0: + // unmapped + return kKeyNone; + + case 1: + return id; + + case 2: + // left over dead key in buffer; oops. + return getIDForKey(item, button, virtualKey, keyState, hkl); + } +} + +void +CMSWindowsKeyState::addKeyEntry(CKeyMap& keyMap, CKeyMap::KeyItem& item) +{ + keyMap.addKeyEntry(item); + if (item.m_group == 0) { + m_keyToVKMap[item.m_id] = static_cast(item.m_client); + } } diff --git a/lib/platform/CMSWindowsKeyState.h b/lib/platform/CMSWindowsKeyState.h index c2b7b149..d8320a58 100644 --- a/lib/platform/CMSWindowsKeyState.h +++ b/lib/platform/CMSWindowsKeyState.h @@ -17,9 +17,12 @@ #include "CKeyState.h" #include "CString.h" +#include "stdvector.h" #define WIN32_LEAN_AND_MEAN #include +class CEvent; +class CEventQueueTimer; class CMSWindowsDesks; //! Microsoft Windows key mapper @@ -28,24 +31,62 @@ This class maps KeyIDs to keystrokes. */ class CMSWindowsKeyState : public CKeyState { public: - CMSWindowsKeyState(CMSWindowsDesks* desks); + CMSWindowsKeyState(CMSWindowsDesks* desks, void* eventTarget); virtual ~CMSWindowsKeyState(); - //! @name accessors + //! @name manipulators //@{ + //! Handle screen disabling + /*! + Called when screen is disabled. This is needed to deal with platform + brokenness. + */ + void disable(); + //! Set the active keyboard layout /*! Uses \p keyLayout when querying the keyboard. */ void setKeyLayout(HKL keyLayout); - //! Check the named virtual key for release + //! Test and set autorepeat state /*! - If \p virtualKey isn't really pressed but we think it is then - update our state and post a key release event to \p eventTarget. + Returns true if the given button is autorepeating and updates internal + state. */ - void fixKey(void* eventTarget, UINT virtualKey); + bool testAutoRepeat(bool press, bool isRepeat, KeyButton); + + //! Remember modifier state + /*! + Records the current non-toggle modifier state. + */ + void saveModifiers(); + + //! Set effective modifier state + /*! + Temporarily sets the non-toggle modifier state to those saved by the + last call to \c saveModifiers if \p enable is \c true. Restores the + modifier state to the current modifier state if \p enable is \c false. + This is for synthesizing keystrokes on the primary screen when the + cursor is on a secondary screen. When on a secondary screen we capture + all non-toggle modifier state, track the state internally and do not + pass it on. So if Alt+F1 synthesizes Alt+X we need to synthesize + not just X but also Alt, despite the fact that our internal modifier + state indicates Alt is down, because local apps never saw the Alt down + event. + */ + void useSavedModifiers(bool enable); + + //@} + //! @name accessors + //@{ + + //! Map a virtual key to a button + /*! + Returns the button for the \p virtualKey. + */ + KeyButton virtualKeyToButton(UINT virtualKey) const; //! Map key event to a key /*! @@ -55,84 +96,121 @@ public: KeyID mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, KeyModifierMask* maskOut) const; - //! Map a virtual key to a button + //! Check if keyboard groups have changed /*! - Returns the button for the \p virtualKey. + Returns true iff the number or order of the keyboard groups have + changed since the last call to updateKeys(). */ - KeyButton virtualKeyToButton(UINT virtualKey) const; + bool didGroupsChange() const; + + //! Map key to virtual key + /*! + Returns the virtual key for key \p key or 0 if there's no such virtual + key. + */ + UINT mapKeyToVirtualKey(KeyID key) const; + + //! Map virtual key and button to KeyID + /*! + Returns the KeyID for virtual key \p virtualKey and button \p button + (button should include the extended key bit), or kKeyNone if there is + no such key. + */ + static KeyID getKeyID(UINT virtualKey, KeyButton button); //@} // IKeyState overrides + virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, + KeyButton button); + virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, + SInt32 count, KeyButton button); + virtual bool fakeCtrlAltDel(); + virtual KeyModifierMask + pollActiveModifiers() const; + virtual SInt32 pollActiveGroup() const; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const; + + // CKeyState overrides + virtual void onKey(KeyButton button, bool down, + KeyModifierMask newState); virtual void sendKeyEvent(void* target, bool press, bool isAutoRepeat, KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button); - virtual bool fakeCtrlAltDel(); - virtual const char* getKeyName(KeyButton) const; protected: - // IKeyState overrides - virtual void doUpdateKeys(); - virtual void doFakeKeyEvent(KeyButton button, - bool press, bool isAutoRepeat); - virtual KeyButton mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const; + // CKeyState overrides + virtual void getKeyMap(CKeyMap& keyMap); + virtual void fakeKey(const Keystroke& keystroke); + virtual KeyModifierMask& + getActiveModifiersRValue(); private: + typedef std::vector GroupList; + // send ctrl+alt+del hotkey event on NT family static void ctrlAltDelThread(void*); - // convert a language ID to a code page - UINT getCodePageFromLangID(LANGID langid) const; + bool getGroups(GroupList&) const; + void setWindowGroup(SInt32 group); - // map a virtual key to a button. this tries to deal with the - // broken win32 API as best it can. - KeyButton mapVirtKeyToButton(UINT virtualKey, - KeyButton& extended) const; + void fixKeys(); + void handleFixKeys(const CEvent&, void*); - // same as above and discard extended - KeyButton mapVirtKeyToButton(UINT virtualKey) const; + KeyID getIDForKey(CKeyMap::KeyItem& item, + KeyButton button, UINT virtualKey, + PBYTE keyState, HKL hkl) const; - // map character \c c given keyboard layout \c hkl to the keystrokes - // to generate it. - KeyButton mapCharacter(Keystrokes& keys, - char c, HKL hkl, bool isAutoRepeat) const; - - // map \c virtualKey to the keystrokes to generate it, along with - // keystrokes to update and restore the modifier state. - KeyButton mapToKeystrokes(Keystrokes& keys, KeyButton button, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask, - bool isAutoRepeat) const; - - // get keystrokes to get modifiers in a desired state - bool adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask) const; - - // pass character to ToAsciiEx(), returning what it returns - int toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const; - - // return true iff \c c is a dead character - bool isDeadChar(TCHAR c, HKL hkl, bool menu) const; + void addKeyEntry(CKeyMap& keyMap, CKeyMap::KeyItem& item); private: + // not implemented + CMSWindowsKeyState(const CMSWindowsKeyState&); + CMSWindowsKeyState& operator=(const CMSWindowsKeyState&); + +private: + typedef std::map GroupMap; + typedef std::map KeyToVKMap; + bool m_is95Family; + void* m_eventTarget; CMSWindowsDesks* m_desks; HKL m_keyLayout; - CString m_keyName; - UINT m_scanCodeToVirtKey[512]; - UINT m_scanCodeToVirtKeyNumLock[512]; - KeyButton m_virtKeyToScanCode[256]; + UINT m_buttonToVK[512]; + UINT m_buttonToNumpadVK[512]; + KeyButton m_virtualKeyToButton[256]; + KeyToVKMap m_keyToVKMap; - static const char* s_vkToName[]; - static const KeyID s_virtualKey[][2]; - static const UINT s_mapE000[]; - static const UINT s_mapEE00[]; - static const UINT s_mapEF00[]; + // the timer used to check for fixing key state + CEventQueueTimer* m_fixTimer; + + // the groups (keyboard layouts) + GroupList m_groups; + GroupMap m_groupMap; + + // the last button that we generated a key down event for. this + // is zero if the last key event was a key up. we use this to + // synthesize key repeats since the low level keyboard hook can't + // tell us if an event is a key repeat. + KeyButton m_lastDown; + + // modifier tracking + bool m_useSavedModifiers; + KeyModifierMask m_savedModifiers; + KeyModifierMask m_originalSavedModifiers; + + // pointer to ToUnicodeEx. on win95 family this will be NULL. + typedef int (WINAPI *ToUnicodeEx_t)(UINT wVirtKey, + UINT wScanCode, + PBYTE lpKeyState, + LPWSTR pwszBuff, + int cchBuff, + UINT wFlags, + HKL dwhkl); + ToUnicodeEx_t m_ToUnicodeEx; + + static const KeyID s_virtualKey[]; }; #endif diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 2ad2cd57..66349ff4 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -19,6 +19,7 @@ #include "CMSWindowsKeyState.h" #include "CMSWindowsScreenSaver.h" #include "CClipboard.h" +#include "CKeyMap.h" #include "XScreen.h" #include "CLock.h" #include "CThread.h" @@ -54,8 +55,8 @@ #define WM_NCXBUTTONDOWN 0x00AB #define WM_NCXBUTTONUP 0x00AC #define WM_NCXBUTTONDBLCLK 0x00AD -#define MOUSEEVENTF_XDOWN 0x0100 -#define MOUSEEVENTF_XUP 0x0200 +#define MOUSEEVENTF_XDOWN 0x0080 +#define MOUSEEVENTF_XUP 0x0100 #define XBUTTON1 0x0001 #define XBUTTON2 0x0002 #endif @@ -76,8 +77,7 @@ HINSTANCE CMSWindowsScreen::s_instance = NULL; CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; -CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, - IJob* suspend, IJob* resume) : +CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : m_isPrimary(isPrimary), m_is95Family(CArchMiscWindows::isWindows95Family()), m_isOnScreen(m_isPrimary), @@ -90,8 +90,8 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_sequenceNumber(0), m_mark(0), m_markReceived(0), - m_keyLayout(NULL), m_fixTimer(NULL), + m_keyLayout(NULL), m_screensaver(NULL), m_screensaverNotify(false), m_screensaverActive(false), @@ -106,8 +106,6 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_setZone(NULL), m_setMode(NULL), m_keyState(NULL), - m_suspend(suspend), - m_resume(resume), m_hasMouse(GetSystemMetrics(SM_MOUSEPRESENT) != 0), m_showingMouse(false) { @@ -124,7 +122,7 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, m_hookLibrary, m_screensaver, new TMethodJob(this, &CMSWindowsScreen::updateKeysCB)); - m_keyState = new CMSWindowsKeyState(m_desks); + m_keyState = new CMSWindowsKeyState(m_desks, getEventTarget()); updateScreenShape(); m_class = createWindowClass(); m_window = createWindow(m_class, "Synergy"); @@ -139,8 +137,6 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, destroyWindow(m_window); destroyClass(m_class); closeHookLibrary(m_hookLibrary); - delete m_suspend; - delete m_resume; s_screen = NULL; throw; } @@ -167,8 +163,6 @@ CMSWindowsScreen::~CMSWindowsScreen() destroyWindow(m_window); destroyClass(m_class); closeHookLibrary(m_hookLibrary); - delete m_suspend; - delete m_resume; s_screen = NULL; } @@ -192,6 +186,12 @@ CMSWindowsScreen::enable() { assert(m_isOnScreen == m_isPrimary); + // we need to poll some things to fix them + m_fixTimer = EVENTQUEUE->newTimer(1.0, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer, + new TMethodEventJob(this, + &CMSWindowsScreen::handleFixes)); + // install our clipboard snooper m_nextClipboardWindow = SetClipboardViewer(m_window); @@ -232,17 +232,20 @@ CMSWindowsScreen::disable() CArchMiscWindows::kDISPLAY); } - // uninstall fix key timer + // tell key state + m_keyState->disable(); + + // stop snooping the clipboard + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + // uninstall fix timer if (m_fixTimer != NULL) { EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); EVENTQUEUE->deleteTimer(m_fixTimer); m_fixTimer = NULL; } - // stop snooping the clipboard - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - m_isOnScreen = m_isPrimary; forceShowCursor(); } @@ -292,6 +295,10 @@ CMSWindowsScreen::leave() // all messages prior to now are invalid nextMark(); + // remember the modifier state. this is the modifier state + // reflected in the internal keyboard state. + m_keyState->saveModifiers(); + // capture events m_setMode(kHOOK_RELAY_EVENTS); } @@ -388,13 +395,13 @@ CMSWindowsScreen::screensaver(bool activate) void CMSWindowsScreen::resetOptions() { - // no options + m_desks->resetOptions(); } void -CMSWindowsScreen::setOptions(const COptionsList&) +CMSWindowsScreen::setOptions(const COptionsList& options) { - // no options + m_desks->setOptions(options); } void @@ -467,6 +474,130 @@ CMSWindowsScreen::warpCursor(SInt32 x, SInt32 y) m_yCursor = y; } +UInt32 +CMSWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask) +{ + // only allow certain modifiers + if ((mask & ~(KeyModifierShift | KeyModifierControl | + KeyModifierAlt | KeyModifierSuper)) != 0) { + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + + // fail if no keys + if (key == kKeyNone && mask == 0) { + return 0; + } + + // convert to win32 + UINT modifiers = 0; + if ((mask & KeyModifierShift) != 0) { + modifiers |= MOD_SHIFT; + } + if ((mask & KeyModifierControl) != 0) { + modifiers |= MOD_CONTROL; + } + if ((mask & KeyModifierAlt) != 0) { + modifiers |= MOD_ALT; + } + if ((mask & KeyModifierSuper) != 0) { + modifiers |= MOD_WIN; + } + UINT vk = m_keyState->mapKeyToVirtualKey(key); + if (key != kKeyNone && vk == 0) { + // can't map key + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + + // choose hotkey id + UInt32 id; + if (!m_oldHotKeyIDs.empty()) { + id = m_oldHotKeyIDs.back(); + m_oldHotKeyIDs.pop_back(); + } + else { + id = m_hotKeys.size() + 1; + } + + // if this hot key has modifiers only then we'll handle it specially + bool err; + if (key == kKeyNone) { + // check if already registered + err = (m_hotKeyToIDMap.count(CHotKeyItem(vk, modifiers)) > 0); + } + else { + // register with OS + err = (RegisterHotKey(NULL, id, modifiers, vk) == 0); + } + + if (!err) { + m_hotKeys.insert(std::make_pair(id, CHotKeyItem(vk, modifiers))); + m_hotKeyToIDMap[CHotKeyItem(vk, modifiers)] = id; + } + else { + m_oldHotKeyIDs.push_back(id); + m_hotKeys.erase(id); + LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", CKeyMap::formatKey(key, mask).c_str(), key, mask)); + return 0; + } + + LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", CKeyMap::formatKey(key, mask).c_str(), key, mask, id)); + return id; +} + +void +CMSWindowsScreen::unregisterHotKey(UInt32 id) +{ + // look up hotkey + HotKeyMap::iterator i = m_hotKeys.find(id); + if (i == m_hotKeys.end()) { + return; + } + + // unregister with OS + bool err; + if (i->second.getVirtualKey() != 0) { + err = !UnregisterHotKey(NULL, id); + } + else { + err = false; + } + if (err) { + LOG((CLOG_WARN "failed to unregister hotkey id=%d", id)); + } + else { + LOG((CLOG_DEBUG "unregistered hotkey id=%d", id)); + } + + // discard hot key from map and record old id for reuse + m_hotKeyToIDMap.erase(i->second); + m_hotKeys.erase(i); + m_oldHotKeyIDs.push_back(id); +} + +void +CMSWindowsScreen::fakeInputBegin() +{ + assert(m_isPrimary); + + if (!m_isOnScreen) { + m_keyState->useSavedModifiers(true); + } + m_desks->fakeInputBegin(); +} + +void +CMSWindowsScreen::fakeInputEnd() +{ + assert(m_isPrimary); + + m_desks->fakeInputEnd(); + if (!m_isOnScreen) { + m_keyState->useSavedModifiers(false); + } +} + SInt32 CMSWindowsScreen::getJumpZoneSize() const { @@ -521,9 +652,9 @@ CMSWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const } void -CMSWindowsScreen::fakeMouseWheel(SInt32 delta) const +CMSWindowsScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const { - m_desks->fakeMouseWheel(delta); + m_desks->fakeMouseWheel(xDelta, yDelta); } void @@ -556,9 +687,9 @@ CMSWindowsScreen::fakeKeyUp(KeyButton button) } void -CMSWindowsScreen::fakeToggle(KeyModifierMask modifier) +CMSWindowsScreen::fakeAllKeysUp() { - CPlatformScreen::fakeToggle(modifier); + CPlatformScreen::fakeAllKeysUp(); updateForceShowCursor(); } @@ -776,7 +907,8 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, static_cast(lParam)); case SYNERGY_MSG_MOUSE_WHEEL: - return onMouseWheel(static_cast(wParam)); + // XXX -- support x-axis scrolling + return onMouseWheel(0, static_cast(wParam)); case SYNERGY_MSG_PRE_WARP: { @@ -800,6 +932,13 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, case SYNERGY_MSG_POST_WARP: LOG((CLOG_WARN "unmatched post warp")); return true; + + case WM_HOTKEY: + // we discard these messages. we'll catch the hot key in the + // regular key event handling, where we can detect both key + // press and release. we only register the hot key so no other + // app will act on the key combination. + break; } return false; @@ -827,8 +966,6 @@ CMSWindowsScreen::onEvent(HWND, UINT msg, break; case WM_DRAWCLIPBOARD: - LOG((CLOG_DEBUG "clipboard was taken")); - // first pass on the message if (m_nextClipboardWindow != NULL) { SendMessage(m_nextClipboardWindow, msg, wParam, lParam); @@ -843,7 +980,6 @@ CMSWindowsScreen::onEvent(HWND, UINT msg, LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow)); } else if (m_nextClipboardWindow != NULL) { - LOG((CLOG_DEBUG "clipboard chain: forward: %d 0x%08x 0x%08x", msg, wParam, lParam)); SendMessage(m_nextClipboardWindow, msg, wParam, lParam); } return true; @@ -856,15 +992,15 @@ CMSWindowsScreen::onEvent(HWND, UINT msg, case PBT_APMRESUMEAUTOMATIC: case PBT_APMRESUMECRITICAL: case PBT_APMRESUMESUSPEND: - if (m_resume != NULL) { - m_resume->run(); - } + EVENTQUEUE->addEvent(CEvent(IScreen::getResumeEvent(), + getEventTarget(), NULL, + CEvent::kDeliverImmediately)); break; case PBT_APMSUSPEND: - if (m_suspend != NULL) { - m_suspend->run(); - } + EVENTQUEUE->addEvent(CEvent(IScreen::getSuspendEvent(), + getEventTarget(), NULL, + CEvent::kDeliverImmediately)); break; } *result = TRUE; @@ -894,62 +1030,70 @@ CMSWindowsScreen::onMark(UInt32 mark) bool CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) { - LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, lParam)); + static const KeyModifierMask s_ctrlAlt = + KeyModifierControl | KeyModifierAlt; - // fix up key state - fixKeys(); + LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, nagr=%d, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, (wParam & 0x10000u) ? 1 : 0, lParam)); - // get key info - KeyButton button = (KeyButton)((lParam & 0x01ff0000) >> 16); - bool down = ((lParam & 0xc0000000u) == 0x00000000u); - bool up = ((lParam & 0x80000000u) == 0x80000000u); - bool wasDown = isKeyDown(button); + // get event info + KeyButton button = (KeyButton)((lParam & 0x01ff0000) >> 16); + bool down = ((lParam & 0x80000000u) == 0x00000000u); + bool wasDown = isKeyDown(button); + KeyModifierMask oldState = pollActiveModifiers(); - // the windows keys are a royal pain on the windows 95 family. - // the system eats the key up events if and only if the windows - // key wasn't combined with another key, i.e. it was tapped. - // fixKeys() and scheduleFixKeys() are all about synthesizing - // the missing key up. but even windows itself gets a little - // confused and sets bit 30 in lParam if you tap the windows - // key twice. that bit means the key was previously down and - // that makes some sense since the up event was missing. - // anyway, on the windows 95 family we forget about windows - // key repeats and treat anything that's not a key down as a - // key up. - if (m_is95Family && - ((wParam & 0xffu) == VK_LWIN || (wParam & 0xffu) == VK_RWIN)) { - down = !up; + // check for autorepeat + if (m_keyState->testAutoRepeat(down, (lParam & 0x40000000u) == 1, button)) { + lParam |= 0x40000000u; } - // update key state. ignore key repeats. - if (down) { - m_keyState->setKeyDown(button, true); - } - else if (up) { - m_keyState->setKeyDown(button, false); + // if the button is zero then guess what the button should be. + // these are badly synthesized key events and logitech software + // that maps mouse buttons to keys is known to do this. + // alternatively, we could just throw these events out. + if (button == 0) { + button = m_keyState->virtualKeyToButton(wParam & 0xffu); + if (button == 0) { + return true; + } + wasDown = isKeyDown(button); } - // schedule a timer if we need to fix keys later - scheduleFixKeys(); + // record keyboard state + m_keyState->onKey(button, down, oldState); - // special case: we detect ctrl+alt+del being pressed on some - // systems but we don't detect the release of those keys. so - // if ctrl, alt, and del are down then mark them up. - KeyModifierMask mask = getActiveModifiers(); - bool ctrlAlt = ((mask & (KeyModifierControl | KeyModifierAlt)) == - (KeyModifierControl | KeyModifierAlt)); - if (down && ctrlAlt && - isKeyDown(m_keyState->virtualKeyToButton(VK_DELETE))) { - m_keyState->setKeyDown( - m_keyState->virtualKeyToButton(VK_LCONTROL), false); - m_keyState->setKeyDown( - m_keyState->virtualKeyToButton(VK_RCONTROL), false); - m_keyState->setKeyDown( - m_keyState->virtualKeyToButton(VK_LMENU), false); - m_keyState->setKeyDown( - m_keyState->virtualKeyToButton(VK_RMENU), false); - m_keyState->setKeyDown( - m_keyState->virtualKeyToButton(VK_DELETE), false); + // windows doesn't tell us the modifier key state on mouse or key + // events so we have to figure it out. most apps would use + // GetKeyState() or even GetAsyncKeyState() for that but we can't + // because our hook doesn't pass on key events for several modifiers. + // it can't otherwise the system would interpret them normally on + // the primary screen even when on a secondary screen. so tapping + // alt would activate menus and tapping the windows key would open + // the start menu. if you don't pass those events on in the hook + // then GetKeyState() understandably doesn't reflect the effect of + // the event. curiously, neither does GetAsyncKeyState(), which is + // surprising. + // + // so anyway, we have to track the modifier state ourselves for + // at least those modifiers we don't pass on. pollActiveModifiers() + // does that but we have to update the keyboard state before calling + // pollActiveModifiers() to get the right answer. but the only way + // to set the modifier state or to set the up/down state of a key + // is via onKey(). so we have to call onKey() twice. + KeyModifierMask state = pollActiveModifiers(); + m_keyState->onKey(button, down, state); + + // check for hot keys + if (oldState != state) { + // modifier key was pressed/released + if (onHotKey(0, lParam)) { + return true; + } + } + else { + // non-modifier was pressed/released + if (onHotKey(wParam, lParam)) { + return true; + } } // ignore message if posted prior to last mark change @@ -957,20 +1101,23 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) // check for ctrl+alt+del. we do not want to pass that to the // client. the user can use ctrl+alt+pause to emulate it. UINT virtKey = (wParam & 0xffu); - if (virtKey == VK_DELETE && ctrlAlt) { + if (virtKey == VK_DELETE && (state & s_ctrlAlt) == s_ctrlAlt) { LOG((CLOG_DEBUG "discard ctrl+alt+del")); return true; } // check for ctrl+alt+del emulation - if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) && ctrlAlt) { + if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) && + (state & s_ctrlAlt) == s_ctrlAlt) { LOG((CLOG_DEBUG "emulate ctrl+alt+del")); // switch wParam and lParam to be as if VK_DELETE was - // pressed or released - wParam = VK_DELETE; + // pressed or released. when mapping the key we require that + // we not use AltGr (the 0x10000 flag in wParam) and we not + // use the keypad delete key (the 0x01000000 flag in lParam). + wParam = VK_DELETE | 0x00010000u; lParam &= 0xfe000000; - lParam |= m_keyState->virtualKeyToButton(wParam) << 16; - lParam |= 0x00000001; + lParam |= m_keyState->virtualKeyToButton(wParam & 0xffu) << 16; + lParam |= 0x01000001; } // process key @@ -978,28 +1125,28 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) KeyID key = m_keyState->mapKeyFromEvent(wParam, lParam, &mask); button = static_cast((lParam & 0x01ff0000u) >> 16); if (key != kKeyNone) { - // fix up key. if the key isn't down according to + // fix key up. if the key isn't down according to // our table then we never got the key press event // for it. if it's not a modifier key then we'll // synthesize the press first. only do this on // the windows 95 family, which eats certain special // keys like alt+tab, ctrl+esc, etc. - if (m_is95Family && !wasDown && up) { + if (m_is95Family && !wasDown && !down) { switch (virtKey) { + case VK_SHIFT: case VK_LSHIFT: case VK_RSHIFT: - case VK_SHIFT: + case VK_CONTROL: case VK_LCONTROL: case VK_RCONTROL: - case VK_CONTROL: + case VK_MENU: case VK_LMENU: case VK_RMENU: - case VK_MENU: + case VK_LWIN: + case VK_RWIN: case VK_CAPITAL: case VK_NUMLOCK: case VK_SCROLL: - case VK_LWIN: - case VK_RWIN: break; default: @@ -1012,17 +1159,64 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) // do it m_keyState->sendKeyEvent(getEventTarget(), ((lParam & 0x80000000u) == 0), - ((lParam & 0x40000000u) == 1), + ((lParam & 0x40000000u) != 0), key, mask, (SInt32)(lParam & 0xffff), button); } else { - LOG((CLOG_DEBUG2 "event: cannot map key")); + LOG((CLOG_DEBUG1 "cannot map key")); } } return true; } +bool +CMSWindowsScreen::onHotKey(WPARAM wParam, LPARAM lParam) +{ + // get the key info + KeyModifierMask state = getActiveModifiers(); + UINT virtKey = (wParam & 0xffu); + UINT modifiers = 0; + if ((state & KeyModifierShift) != 0) { + modifiers |= MOD_SHIFT; + } + if ((state & KeyModifierControl) != 0) { + modifiers |= MOD_CONTROL; + } + if ((state & KeyModifierAlt) != 0) { + modifiers |= MOD_ALT; + } + if ((state & KeyModifierSuper) != 0) { + modifiers |= MOD_WIN; + } + + // find the hot key id + HotKeyToIDMap::const_iterator i = + m_hotKeyToIDMap.find(CHotKeyItem(virtKey, modifiers)); + if (i == m_hotKeyToIDMap.end()) { + return false; + } + + // find what kind of event + CEvent::Type type; + if ((lParam & 0x80000000u) == 0u) { + if ((lParam & 0x40000000u) != 0u) { + // ignore key repeats but it counts as a hot key + return true; + } + type = getHotKeyDownEvent(); + } + else { + type = getHotKeyUpEvent(); + } + + // generate event + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), + CHotKeyInfo::alloc(i->second))); + + return true; +} + bool CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) { @@ -1042,16 +1236,19 @@ CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) // ignore message if posted prior to last mark change if (!ignore()) { + KeyModifierMask mask = m_keyState->getActiveModifiers(); if (pressed) { LOG((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { - sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); + sendEvent(getButtonDownEvent(), + CButtonInfo::alloc(button, mask)); } } else { LOG((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { - sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); + sendEvent(getButtonUpEvent(), + CButtonInfo::alloc(button, mask)); } } } @@ -1109,12 +1306,12 @@ CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) } bool -CMSWindowsScreen::onMouseWheel(SInt32 delta) +CMSWindowsScreen::onMouseWheel(SInt32 xDelta, SInt32 yDelta) { // ignore message if posted prior to last mark change if (!ignore()) { - LOG((CLOG_DEBUG1 "event: button wheel delta=%d", delta)); - sendEvent(getWheelEvent(), CWheelInfo::alloc(delta)); + LOG((CLOG_DEBUG1 "event: button wheel delta=%+d,%+d", xDelta, yDelta)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(xDelta, yDelta)); } return true; } @@ -1197,7 +1394,6 @@ CMSWindowsScreen::onClipboardChange() // now notify client that somebody changed the clipboard (unless // we're the owner). if (!CMSWindowsClipboard::isOwnedBySynergy()) { - LOG((CLOG_DEBUG "clipboard changed: foreign owned")); if (m_ownClipboard) { LOG((CLOG_DEBUG "clipboard changed: lost ownership")); m_ownClipboard = false; @@ -1205,7 +1401,7 @@ CMSWindowsScreen::onClipboardChange() sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); } } - else { + else if (!m_ownClipboard) { LOG((CLOG_DEBUG "clipboard changed: synergy owned")); m_ownClipboard = true; } @@ -1282,6 +1478,34 @@ CMSWindowsScreen::updateScreenShape() m_desks->setShape(m_x, m_y, m_w, m_h, m_xCenter, m_yCenter, m_multimon); } +void +CMSWindowsScreen::handleFixes(const CEvent&, void*) +{ + // fix clipboard chain + fixClipboardViewer(); + + // update keys if keyboard layouts have changed + if (m_keyState->didGroupsChange()) { + updateKeys(); + } +} + +void +CMSWindowsScreen::fixClipboardViewer() +{ + // XXX -- disable this code for now. somehow it can cause an infinite + // recursion in the WM_DRAWCLIPBOARD handler. either we're sending + // the message to our own window or some window farther down the chain + // forwards the message to our window or a window farther up the chain. + // i'm not sure how that could happen. the m_nextClipboardWindow = NULL + // was not in the code that infinite loops and may fix the bug but i + // doubt it. + return; + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + m_nextClipboardWindow = SetClipboardViewer(m_window); +} + void CMSWindowsScreen::enableSpecialKeys(bool enable) const { @@ -1387,63 +1611,37 @@ CMSWindowsScreen::mapPressFromEvent(WPARAM msg, LPARAM) const } } -void -CMSWindowsScreen::fixKeys() -{ - // fake key releases for the windows keys if we think they're - // down but they're really up. we have to do this because if the - // user presses and releases a windows key without pressing any - // other key while it's down then the system will eat the key - // release. if we don't detect that and synthesize the release - // then the client won't take the usual windows key release action - // (which on windows is to show the start menu). - // - // only check on the windows 95 family since the NT family reports - // the key releases as usual. - if (m_is95Family) { - m_keyState->fixKey(getEventTarget(), VK_LWIN); - m_keyState->fixKey(getEventTarget(), VK_RWIN); - - // check if we need the fix timer anymore - scheduleFixKeys(); - } -} - -void -CMSWindowsScreen::scheduleFixKeys() -{ - if (m_is95Family) { - // see if any keys that need fixing are down - bool fix = - (m_keyState->isKeyDown(m_keyState->virtualKeyToButton(VK_LWIN)) || - m_keyState->isKeyDown(m_keyState->virtualKeyToButton(VK_RWIN))); - - // start or stop fix timer - if (fix && m_fixTimer == NULL) { - m_fixTimer = EVENTQUEUE->newTimer(0.1, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer, - new TMethodEventJob( - this, &CMSWindowsScreen::handleFixKeys)); - } - else if (!fix && m_fixTimer != NULL) { - EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); - EVENTQUEUE->deleteTimer(m_fixTimer); - m_fixTimer = NULL; - } - } -} - -void -CMSWindowsScreen::handleFixKeys(const CEvent&, void*) -{ - fixKeys(); -} - void CMSWindowsScreen::updateKeysCB(void*) { - m_keyState->updateKeys(); - updateButtons(); + // record which keys we think are down + bool down[IKeyState::kNumButtons]; + bool sendFixes = (isPrimary() && !m_isOnScreen); + if (sendFixes) { + for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) { + down[i] = m_keyState->isKeyDown(i); + } + } + + // update layouts if necessary + if (m_keyState->didGroupsChange()) { + CPlatformScreen::updateKeyMap(); + } + + // now update the keyboard state + CPlatformScreen::updateKeyState(); + + // now see which keys we thought were down but now think are up. + // send key releases for these keys to the active client. + if (sendFixes) { + KeyModifierMask mask = pollActiveModifiers(); + for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) { + if (down[i] && !m_keyState->isKeyDown(i)) { + m_keyState->sendKeyEvent(getEventTarget(), + false, false, kKeyNone, mask, 1, i); + } + } + } } void @@ -1512,3 +1710,28 @@ CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return result; } + + +// +// CMSWindowsScreen::CHotKeyItem +// + +CMSWindowsScreen::CHotKeyItem::CHotKeyItem(UINT keycode, UINT mask) : + m_keycode(keycode), + m_mask(mask) +{ + // do nothing +} + +UINT +CMSWindowsScreen::CHotKeyItem::getVirtualKey() const +{ + return m_keycode; +} + +bool +CMSWindowsScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const +{ + return (m_keycode < x.m_keycode || + (m_keycode == x.m_keycode && m_mask < x.m_mask)); +} diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 2b8b7366..eda3f554 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -28,12 +28,11 @@ class CMSWindowsDesks; class CMSWindowsKeyState; class CMSWindowsScreenSaver; class CThread; -class IJob; //! Implementation of IPlatformScreen for Microsoft Windows class CMSWindowsScreen : public CPlatformScreen { public: - CMSWindowsScreen(bool isPrimary, IJob* suspend, IJob* resume); + CMSWindowsScreen(bool isPrimary); virtual ~CMSWindowsScreen(); //! @name manipulators @@ -68,6 +67,11 @@ public: // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); + virtual UInt32 registerHotKey(KeyID key, + KeyModifierMask mask); + virtual void unregisterHotKey(UInt32 id); + virtual void fakeInputBegin(); + virtual void fakeInputEnd(); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; @@ -76,7 +80,7 @@ public: virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; - virtual void fakeMouseWheel(SInt32 delta) const; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; // IKeyState overrides virtual void updateKeys(); @@ -85,7 +89,7 @@ public: virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button); virtual void fakeKeyUp(KeyButton button); - virtual void fakeToggle(KeyModifierMask modifier); + virtual void fakeAllKeysUp(); // IPlatformScreen overrides virtual void enable(); @@ -139,9 +143,10 @@ private: // message handlers bool onMark(UInt32 mark); bool onKey(WPARAM, LPARAM); + bool onHotKey(WPARAM, LPARAM); bool onMouseButton(WPARAM, LPARAM); bool onMouseMove(SInt32 x, SInt32 y); - bool onMouseWheel(SInt32 delta); + bool onMouseWheel(SInt32 xDelta, SInt32 yDelta); bool onScreensaver(bool activated); bool onDisplayChange(); bool onClipboardChange(); @@ -158,6 +163,12 @@ private: // update screen size cache void updateScreenShape(); + // fix timer callback + void handleFixes(const CEvent&, void*); + + // fix the clipboard viewer chain + void fixClipboardViewer(); + // enable/disable special key combinations so we can catch/pass them void enableSpecialKeys(bool) const; @@ -167,16 +178,6 @@ private: // map a button event to a press (true) or release (false) bool mapPressFromEvent(WPARAM msg, LPARAM button) const; - // fix the key state, synthesizing fake key releases for keys - // that aren't down anymore. - void fixKeys(); - - // (un)schedule a later call to fixKeys - void scheduleFixKeys(); - - // event handler to fix the key state - void handleFixKeys(const CEvent&, void*); - // job to update the key state void updateKeysCB(void*); @@ -194,6 +195,22 @@ private: static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); private: + struct CHotKeyItem { + public: + CHotKeyItem(UINT vk, UINT modifiers); + + UINT getVirtualKey() const; + + bool operator<(const CHotKeyItem&) const; + + private: + UINT m_keycode; + UINT m_mask; + }; + typedef std::map HotKeyMap; + typedef std::vector HotKeyIDList; + typedef std::map HotKeyToIDMap; + static HINSTANCE s_instance; // true if screen is being used as a primary screen, false otherwise @@ -229,12 +246,12 @@ private: // the main loop's thread id DWORD m_threadID; + // timer for periodically checking stuff that requires polling + CEventQueueTimer* m_fixTimer; + // the keyboard layout to use when off primary screen HKL m_keyLayout; - // the timer used to check for fixing key state - CEventQueueTimer* m_fixTimer; - // screen saver stuff CMSWindowsScreenSaver* m_screensaver; bool m_screensaverNotify; @@ -260,13 +277,14 @@ private: // keyboard stuff CMSWindowsKeyState* m_keyState; + // hot key stuff + HotKeyMap m_hotKeys; + HotKeyIDList m_oldHotKeyIDs; + HotKeyToIDMap m_hotKeyToIDMap; + // map of button state bool m_buttons[1 + kButtonExtra0 + 1]; - // suspend/resume callbacks - IJob* m_suspend; - IJob* m_resume; - // the system shows the mouse cursor when an internal display count // is >= 0. this count is maintained per application but there's // apparently a system wide count added to the application's count. diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index b321fddb..529cd176 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -107,7 +107,7 @@ CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID); if (process == NULL) { // didn't start - LOG((CLOG_DEBUG "can't open screen saver process")); + LOG((CLOG_DEBUG2 "can't open screen saver process")); return false; } @@ -123,7 +123,7 @@ CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) // we first check that the screen saver is indeed active // before watching for it to stop. if (!isActive()) { - LOG((CLOG_DEBUG "can't open screen saver desktop")); + LOG((CLOG_DEBUG2 "can't open screen saver desktop")); return false; } @@ -441,7 +441,7 @@ void CMSWindowsScreenSaver::setSecure(bool secure, bool saveSecureAsInt) { HKEY hkey = - CArchMiscWindows::openKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure); + CArchMiscWindows::addKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure); if (hkey == NULL) { return; } diff --git a/lib/platform/COSXClipboard.cpp b/lib/platform/COSXClipboard.cpp index 6a244587..7d39eaad 100644 --- a/lib/platform/COSXClipboard.cpp +++ b/lib/platform/COSXClipboard.cpp @@ -42,15 +42,18 @@ COSXClipboard::empty() assert(m_scrap != NULL); OSStatus err = ClearScrap(&m_scrap); - // XXX -- check err? + if (err != noErr) { + LOG((CLOG_DEBUG "failed to grab clipboard")); + return false; + } + // we own the clipboard err = PutScrapFlavor( m_scrap, getOwnershipFlavor(), kScrapFlavorMaskNone, 0, - 0); - + 0); if (err != noErr) { LOG((CLOG_DEBUG "failed to grab clipboard")); return false; diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index 035a0468..5ae7f5d9 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -15,27 +15,7 @@ #include "COSXKeyState.h" #include "CLog.h" #include "CArch.h" -#include -struct CKCHRDeadKeyRecord { -public: - UInt8 m_tableIndex; - UInt8 m_virtualKey; - SInt16 m_numCompletions; - UInt8 m_completion[1][2]; -}; - -struct CKCHRDeadKeys { -public: - SInt16 m_numRecords; - CKCHRDeadKeyRecord m_records[1]; -}; - -struct CKeyEntry { -public: - KeyID m_keyID; - UInt32 m_virtualKey; -}; // Hardcoded virtual key table. Oddly, Apple doesn't document the // meaning of virtual key codes. The whole point of *virtual* key // codes is to make them hardware independent so these codes should @@ -47,48 +27,28 @@ public: // first instance of a virtual key code maps to the KeyID that we // want to generate for that code. The others are for mapping // different KeyIDs to a single key code. +static const UInt32 s_shiftVK = 56; +static const UInt32 s_controlVK = 59; +static const UInt32 s_altVK = 55; +static const UInt32 s_superVK = 58; +static const UInt32 s_capsLockVK = 57; +struct CKeyEntry { +public: + KeyID m_keyID; + UInt32 m_virtualKey; +}; static const CKeyEntry s_controlKeys[] = { - // TTY functions - { kKeyBackSpace, 51 }, - { kKeyTab, 48 }, - { kKeyLeftTab, 48 }, - { kKeyReturn, 36 }, - { kKeyLinefeed, 36 }, -// { kKeyClear, 0xFFFF }, /* no mapping on apple */ -// { kKeyPause, 0xFFFF }, /* no mapping on apple */ -// { kKeyScrollLock, 0xFFFF }, /* no mapping on apple */ -// { kKeySysReq, 0xFFFF }, /* no mapping on apple */ - { kKeyEscape, 53 }, - { kKeyDelete, 117 }, - - // cursor control - { kKeyHome, 115 }, - { kKeyBegin, 115 }, + // cursor keys. if we don't do this we'll may still get these from + // the keyboard resource but they may not correspond to the arrow + // keys. { kKeyLeft, 123 }, - { kKeyUp, 126 }, { kKeyRight, 124 }, + { kKeyUp, 126 }, { kKeyDown, 125 }, + { kKeyHome, 115 }, + { kKeyEnd, 119 }, { kKeyPageUp, 116 }, { kKeyPageDown, 121 }, - { kKeyEnd, 119 }, - - // numeric keypad - { kKeyKP_0, 82 }, - { kKeyKP_1, 83 }, - { kKeyKP_2, 84 }, - { kKeyKP_3, 85 }, - { kKeyKP_4, 86 }, - { kKeyKP_5, 87 }, - { kKeyKP_6, 88 }, - { kKeyKP_7, 89 }, - { kKeyKP_8, 91 }, - { kKeyKP_9, 92 }, - { kKeyKP_Enter, 76 }, - { kKeyKP_Decimal, 65 }, - { kKeyKP_Add, 69 }, - { kKeyKP_Subtract, 78 }, - { kKeyKP_Multiply, 67 }, - { kKeyKP_Divide, 75 }, // function keys { kKeyF1, 122 }, @@ -106,43 +66,44 @@ static const CKeyEntry s_controlKeys[] = { { kKeyF13, 105 }, { kKeyF14, 107 }, { kKeyF15, 113 }, + { kKeyF16, 106 }, - // misc keys - { kKeyHelp, 114 }, + // virtual key 110 is fn+enter and i have no idea what that's supposed + // to map to. also the enter key with numlock on is a modifier but i + // don't know which. - // modifier keys. i don't know how to make the mac properly - // interpret the right hand versions of modifier keys so they're - // currently mapped to the left hand version. + // modifier keys. OS X doesn't seem to support right handed versions + // of modifier keys so we map them to the left handed versions. + { kKeyShift_L, s_shiftVK }, + { kKeyShift_R, s_shiftVK }, // 60 + { kKeyControl_L, s_controlVK }, + { kKeyControl_R, s_controlVK }, // 62 + { kKeyAlt_L, s_altVK }, + { kKeyAlt_R, s_altVK }, + { kKeySuper_L, s_superVK }, + { kKeySuper_R, s_superVK }, // 61 + { kKeyMeta_L, s_superVK }, + { kKeyMeta_R, s_superVK }, // 61 + + // toggle modifiers // { kKeyNumLock, 71 }, - { kKeyShift_L, 56 }, - { kKeyShift_R, 56 /*60*/ }, - { kKeyControl_L, 59 }, - { kKeyControl_R, 59 /*62*/ }, - { kKeyAlt_L, 55 }, - { kKeyAlt_R, 55 }, - { kKeySuper_L, 58 }, - { kKeySuper_R, 58 /*61*/ }, - { kKeyMeta_L, 58 }, - { kKeyMeta_R, 58 /*61*/ }, - { kKeyCapsLock, 57 } + { kKeyCapsLock, s_capsLockVK } }; -// special key that synthesizes a delay. see doFakeKeyEvent() and -// mapKey(). -static const KeyButton kDelayKey = 510; - // // COSXKeyState // COSXKeyState::COSXKeyState() : - m_uchrFound(false) + m_deadKeyState(0) { - setHalfDuplexMask(0); - SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript); - SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys); - setKeyboardLayout(keyboardLayoutID); + // build virtual key map + for (size_t i = 0; i < sizeof(s_controlKeys) / + sizeof(s_controlKeys[0]); ++i) { + m_virtualKeyMap[s_controlKeys[i].m_virtualKey] = + s_controlKeys[i].m_keyID; + } } COSXKeyState::~COSXKeyState() @@ -150,252 +111,37 @@ COSXKeyState::~COSXKeyState() // do nothing } -void -COSXKeyState::sendKeyEvent(void* target, - bool press, bool isAutoRepeat, - KeyID key, KeyModifierMask mask, - SInt32 count, KeyButton button) +KeyModifierMask +COSXKeyState::mapModifiersFromOSX(UInt32 mask) const { - if (press || isAutoRepeat) { - // send key - if (press) { - CKeyState::sendKeyEvent(target, true, false, - key, mask, 1, button); - if (count > 0) { - --count; - } - } - - if (count >= 1) { - CKeyState::sendKeyEvent(target, true, true, - key, mask, count, button); - } - + // convert + KeyModifierMask outMask = 0; + if ((mask & shiftKey) != 0) { + outMask |= KeyModifierShift; } - else { - // do key up - CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button); + if ((mask & rightShiftKey) != 0) { + outMask |= KeyModifierShift; } -} - -void -COSXKeyState::setHalfDuplexMask(KeyModifierMask mask) -{ - CKeyState::setHalfDuplexMask(mask | KeyModifierCapsLock); -} - -bool -COSXKeyState::fakeCtrlAltDel() -{ - // pass keys through unchanged - return false; -} - -const char* -COSXKeyState::getKeyName(KeyButton button) const -{ - static char name[10]; - sprintf(name, "vk 0x%02x", button); - return name; -} - -void -COSXKeyState::doUpdateKeys() -{ - // save key mapping - m_keyMap.clear(); - m_uchrFound = false; - if (m_uchrResource != NULL) { - m_uchrFound = filluchrKeysMap(m_keyMap); + if ((mask & controlKey) != 0) { + outMask |= KeyModifierControl; } - if (!m_uchrFound && m_KCHRResource != NULL) { - fillKCHRKeysMap(m_keyMap); + if ((mask & rightControlKey) != 0) { + outMask |= KeyModifierControl; } - fillSpecialKeys(m_keyMap, m_virtualKeyMap); - - // add modifiers - KeyButtons keys; - addKeyButton(keys, kKeyShift_L); - addKeyButton(keys, kKeyShift_R); - addModifier(KeyModifierShift, keys); - keys.clear(); - addKeyButton(keys, kKeyControl_L); - addKeyButton(keys, kKeyControl_R); - addModifier(KeyModifierControl, keys); - keys.clear(); - addKeyButton(keys, kKeyAlt_L); - addKeyButton(keys, kKeyAlt_R); - addModifier(KeyModifierAlt, keys); - keys.clear(); - addKeyButton(keys, kKeySuper_L); - addKeyButton(keys, kKeySuper_R); - addModifier(KeyModifierSuper, keys); - keys.clear(); - addKeyButton(keys, kKeyCapsLock); - addModifier(KeyModifierCapsLock, keys); - keys.clear(); - addKeyButton(keys, kKeyNumLock); - addModifier(KeyModifierNumLock, keys); - keys.clear(); - - // FIXME -- get the current keyboard state. call setKeyDown() - // and setToggled() as appropriate. -} - -void -COSXKeyState::doFakeKeyEvent(KeyButton button, bool press, bool) -{ - LOG((CLOG_DEBUG2 "doFakeKeyEvent button:%d, press:%d", button, press)); - - // if it's the special delay key then just pause briefly - if (button == kDelayKey) { - ARCH->sleep(0.01); - return; + if ((mask & cmdKey) != 0) { + outMask |= KeyModifierAlt; + } + if ((mask & optionKey) != 0) { + outMask |= KeyModifierSuper; + } + if ((mask & rightOptionKey) != 0) { + outMask |= KeyModifierSuper; + } + if ((mask & alphaLock) != 0) { + outMask |= KeyModifierCapsLock; } - // let system figure out character for us - CGPostKeyboardEvent(0, mapKeyButtonToVirtualKey(button), press); -} - -KeyButton -COSXKeyState::mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const -{ - // see if the keyboard layout has changed since we last checked it. - // reload the keyboard info if so. - const_cast(this)->checkKeyboardLayout(); - - // look up virtual key - CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id); - if (keyIndex == m_keyMap.end()) { - return 0; - } - const CKeySequence& sequence = keyIndex->second; - if (sequence.empty()) { - return 0; - } - - // FIXME -- for both calls to addKeystrokes below we'd prefer to use - // a required mask that generates the same character but matches - // the desiredMask as closely as possible. - // FIXME -- would prefer to not restore the modifier keys after each - // dead key since it's unnecessary but we don't have a mechanism - // for tracking the modifier state without actually updating the - // internal keyboard state. we'd have to track the state to - // ensure we adjust the right modifiers for remaining dead keys - // and the final key. - - // add dead keys - for (size_t i = 0; i < sequence.size() - 1; ++i) { - // simulate press - KeyButton keyButton = - addKeystrokes(keys, sequence[i].m_button, - sequence[i].m_requiredState, - sequence[i].m_requiredMask, false); - - // simulate release - Keystroke keystroke; - keystroke.m_key = keyButton; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - - // pause briefly before sending the next key because some - // apps (e.g. TextEdit) seem to ignore dead keys that occur - // very shortly before the next key. why they'd do that i - // don't know. - keystroke.m_key = kDelayKey; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); - } - - // add final key. - // if the desired mask includes Alt or Control then match the - // desired mask. this ensures that combinations like - // Command+Shift+S use the Command and Shift modifiers and - // those like Command+S do not use the shift modifier. do not - // do this if the key to synthesize is a modifier key, otherwise - // we'd apply modifiers to modifiers which breaks things (by - // say, putting a Control press and release around a Control - // press). - if ((desiredMask & (KeyModifierControl | KeyModifierAlt)) != 0 && - id != kKeyShift_L && id != kKeyShift_R && - id != kKeyControl_L && id != kKeyControl_R && - id != kKeyAlt_L && id != kKeyAlt_R && - id != kKeySuper_L && id != kKeySuper_R && - id != kKeyMeta_L && id != kKeyMeta_R) { - return addKeystrokes(keys, sequence.back().m_button, - desiredMask, - KeyModifierShift | KeyModifierSuper | - KeyModifierAlt | KeyModifierControl | - KeyModifierCapsLock, - isAutoRepeat); - } - else { - return addKeystrokes(keys, sequence.back().m_button, - sequence.back().m_requiredState, - sequence.back().m_requiredMask, - isAutoRepeat); - } -} - -KeyButton -COSXKeyState::addKeystrokes(Keystrokes& keys, KeyButton keyButton, - KeyModifierMask desiredMask, KeyModifierMask requiredMask, - bool isAutoRepeat) const -{ - // adjust the modifiers - Keystrokes undo; - if (!adjustModifiers(keys, undo, desiredMask, requiredMask)) { - LOG((CLOG_DEBUG2 "failed to adjust modifiers")); - return 0; - } - - // add the key event - Keystroke keystroke; - keystroke.m_key = keyButton; - if (!isAutoRepeat) { - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - } - else { - keystroke.m_press = false; - keystroke.m_repeat = true; - keys.push_back(keystroke); - keystroke.m_press = true; - keys.push_back(keystroke); - } - - // put undo keystrokes at end of keystrokes in reverse order - while (!undo.empty()) { - keys.push_back(undo.back()); - undo.pop_back(); - } - - return keyButton; -} - -bool -COSXKeyState::adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask) const -{ - // for each modifier in requiredMask make sure the current state - // of that modifier matches the bit in desiredMask. - for (KeyModifierMask mask = 1u; requiredMask != 0; mask <<= 1) { - if ((mask & requiredMask) != 0) { - bool active = ((desiredMask & mask) != 0); - if (!mapModifier(keys, undo, mask, active)) { - return false; - } - requiredMask ^= mask; - } - } - return true; + return outMask; } KeyButton @@ -407,7 +153,7 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, // map modifier key if (maskOut != NULL) { KeyModifierMask activeMask = getActiveModifiers(); - activeMask &= ~KeyModifierModeSwitch; + activeMask &= ~KeyModifierAltGr; *maskOut = activeMask; } @@ -434,15 +180,33 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, return mapVirtualKeyToKeyButton(vkCode); } - // check for character keys - if (m_uchrFound) { - // get the event modifiers and remove the command and control - // keys. - UInt32 modifiers; - GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, - NULL, sizeof(modifiers), NULL, &modifiers); - modifiers = (modifiers & ~(cmdKey | controlKey | rightControlKey)) >> 8; + // get keyboard info + KeyboardLayoutRef keyboardLayout; + OSStatus status = KLGetCurrentKeyboardLayout(&keyboardLayout); + if (status != noErr) { + return kKeyNone; + } + // get the event modifiers and remove the command and control + // keys. note if we used them though. + UInt32 modifiers; + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, + NULL, sizeof(modifiers), NULL, &modifiers); + static const UInt32 s_commandModifiers = + cmdKey | controlKey | rightControlKey; + bool isCommand = ((modifiers & s_commandModifiers) != 0); + modifiers &= ~s_commandModifiers; + + // if we've used a command key then we want the glyph produced without + // the option key (i.e. the base glyph). + if (isCommand) { + modifiers &= ~optionKey; + } + + // translate via uchr resource + const void* resource; + if (KLGetKeyboardLayoutProperty(keyboardLayout, + kKLuchrData, &resource) == noErr) { // choose action UInt16 action; switch (eventKind) { @@ -461,10 +225,10 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, // translate key UniCharCount count; UniChar chars[2]; - OSStatus status = UCKeyTranslate(m_uchrResource, + OSStatus status = UCKeyTranslate((const UCKeyboardLayout*)resource, vkCode & 0xffu, action, - modifiers & 0xffu, - m_keyboardType, 0, &m_deadKeyState, + (modifiers >> 8) & 0xffu, + LMGetKbdType(), 0, &m_deadKeyState, sizeof(chars) / sizeof(chars[0]), &count, chars); // get the characters @@ -472,26 +236,24 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, if (count != 0 || m_deadKeyState == 0) { m_deadKeyState = 0; for (UniCharCount i = 0; i < count; ++i) { - ids.push_back(unicharToKeyID(chars[i])); + ids.push_back(CKeyResource::unicharToKeyID(chars[i])); } + adjustAltGrModifier(ids, maskOut, isCommand); return mapVirtualKeyToKeyButton(vkCode); } + return 0; } } - else if (m_KCHRResource != NULL) { - // get the event modifiers and remove the command and control - // keys. - UInt32 modifiers; - GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, - NULL, sizeof(modifiers), NULL, &modifiers); - modifiers &= ~(cmdKey | controlKey | rightControlKey); + // translate via KCHR resource + if (KLGetKeyboardLayoutProperty(keyboardLayout, + kKLKCHRData, &resource) == noErr) { // build keycode UInt16 keycode = static_cast((modifiers & 0xff00u) | (vkCode & 0x00ffu)); // translate key - UInt32 result = KeyTranslate(m_KCHRResource, keycode, &m_deadKeyState); + UInt32 result = KeyTranslate(resource, keycode, &m_deadKeyState); // get the characters UInt8 c1 = static_cast((result >> 16) & 0xffu); @@ -499,9 +261,10 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, if (c2 != 0) { m_deadKeyState = 0; if (c1 != 0) { - ids.push_back(charToKeyID(c1)); + ids.push_back(CKeyResource::getKeyID(c1)); } - ids.push_back(charToKeyID(c2)); + ids.push_back(CKeyResource::getKeyID(c2)); + adjustAltGrModifier(ids, maskOut, isCommand); return mapVirtualKeyToKeyButton(vkCode); } } @@ -509,16 +272,307 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, return 0; } -void -COSXKeyState::addKeyButton(KeyButtons& keys, KeyID id) const +bool +COSXKeyState::fakeCtrlAltDel() { - CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id); - if (keyIndex == m_keyMap.end()) { - return; - } - keys.push_back(keyIndex->second[0].m_button); + // pass keys through unchanged + return false; } +KeyModifierMask +COSXKeyState::pollActiveModifiers() const +{ + return mapModifiersFromOSX(GetCurrentKeyModifiers()); +} + +SInt32 +COSXKeyState::pollActiveGroup() const +{ + KeyboardLayoutRef keyboardLayout; + OSStatus status = KLGetCurrentKeyboardLayout(&keyboardLayout); + if (status == noErr) { + GroupMap::const_iterator i = m_groupMap.find(keyboardLayout); + if (i != m_groupMap.end()) { + return i->second; + } + } + return 0; +} + +void +COSXKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const +{ + KeyMap km; + GetKeys(km); + const UInt8* m = reinterpret_cast(km); + for (UInt32 i = 0; i < 16; ++i) { + for (UInt32 j = 0; j < 8; ++j) { + if ((m[i] & (1u << j)) != 0) { + pressedKeys.insert(mapVirtualKeyToKeyButton(8 * i + j)); + } + } + } +} + +void +COSXKeyState::getKeyMap(CKeyMap& keyMap) +{ + // update keyboard groups + if (getGroups(m_groups)) { + m_groupMap.clear(); + SInt32 numGroups = (SInt32)m_groups.size(); + for (SInt32 g = 0; g < numGroups; ++g) { + m_groupMap[m_groups[g]] = g; + } + } + + UInt32 keyboardType = LMGetKbdType(); + for (SInt32 g = 0, n = (SInt32)m_groups.size(); g < n; ++g) { + // add special keys + getKeyMapForSpecialKeys(keyMap, g); + + // add regular keys + + // try uchr resource first + const void* resource; + if (KLGetKeyboardLayoutProperty(m_groups[g], + kKLuchrData, &resource) == noErr) { + CUCHRKeyResource uchr(resource, keyboardType); + if (uchr.isValid()) { + LOG((CLOG_DEBUG1 "using uchr resource for group %d", g)); + getKeyMap(keyMap, g, uchr); + continue; + } + } + + // try KCHR resource + if (KLGetKeyboardLayoutProperty(m_groups[g], + kKLKCHRData, &resource) == noErr) { + CKCHRKeyResource kchr(resource); + if (kchr.isValid()) { + LOG((CLOG_DEBUG1 "using KCHR resource for group %d", g)); + getKeyMap(keyMap, g, kchr); + continue; + } + } + + LOG((CLOG_DEBUG1 "no keyboard resource for group %d", g)); + } +} + +void +COSXKeyState::fakeKey(const Keystroke& keystroke) +{ + switch (keystroke.m_type) { + case Keystroke::kButton: + LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up")); + + // let system figure out character for us + CGPostKeyboardEvent(0, mapKeyButtonToVirtualKey( + keystroke.m_data.m_button.m_button), + keystroke.m_data.m_button.m_press); + + // add a delay if client data isn't zero + if (keystroke.m_data.m_button.m_client) { + ARCH->sleep(0.01); + } + break; + + case Keystroke::kGroup: + if (keystroke.m_data.m_group.m_absolute) { + LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group)); + setGroup(keystroke.m_data.m_group.m_group); + } + else { + LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group)); + setGroup(getEffectiveGroup(pollActiveGroup(), + keystroke.m_data.m_group.m_group)); + } + break; + } +} + +void +COSXKeyState::getKeyMapForSpecialKeys(CKeyMap& keyMap, SInt32 group) const +{ + // special keys are insensitive to modifers and none are dead keys + CKeyMap::KeyItem item; + for (size_t i = 0; i < sizeof(s_controlKeys) / + sizeof(s_controlKeys[0]); ++i) { + const CKeyEntry& entry = s_controlKeys[i]; + item.m_id = entry.m_keyID; + item.m_group = group; + item.m_button = mapVirtualKeyToKeyButton(entry.m_virtualKey); + item.m_required = 0; + item.m_sensitive = 0; + item.m_dead = false; + item.m_client = 0; + CKeyMap::initModifierKey(item); + keyMap.addKeyEntry(item); + + if (item.m_lock) { + // all locking keys are half duplex on OS X + keyMap.addHalfDuplexButton(item.m_button); + } + } + + // note: we don't special case the number pad keys. querying the + // mac keyboard returns the non-keypad version of those keys but + // a CKeyState always provides a mapping from keypad keys to + // non-keypad keys so we'll be able to generate the characters + // anyway. +} + +bool +COSXKeyState::getKeyMap(CKeyMap& keyMap, + SInt32 group, const CKeyResource& r) const +{ + if (!r.isValid()) { + return false; + } + + // space for all possible modifier combinations + std::vector modifiers(r.getNumModifierCombinations()); + + // make space for the keys that any single button can synthesize + std::vector > buttonKeys(r.getNumTables()); + + // iterate over each button + CKeyMap::KeyItem item; + for (UInt32 i = 0; i < r.getNumButtons(); ++i) { + item.m_button = mapVirtualKeyToKeyButton(i); + + // the KeyIDs we've already handled + std::set keys; + + // convert the entry in each table for this button to a KeyID + for (UInt32 j = 0; j < r.getNumTables(); ++j) { + buttonKeys[j].first = r.getKey(j, i); + buttonKeys[j].second = CKeyMap::isDeadKey(buttonKeys[j].first); + } + + // iterate over each character table + for (UInt32 j = 0; j < r.getNumTables(); ++j) { + // get the KeyID for the button/table + KeyID id = buttonKeys[j].first; + if (id == kKeyNone) { + continue; + } + + // if we've already handled the KeyID in the table then + // move on to the next table + if (keys.count(id) > 0) { + continue; + } + keys.insert(id); + + // prepare item. the client state is 1 for dead keys. + item.m_id = id; + item.m_group = group; + item.m_dead = buttonKeys[j].second; + item.m_client = buttonKeys[j].second ? 1 : 0; + CKeyMap::initModifierKey(item); + if (item.m_lock) { + // all locking keys are half duplex on OS X + keyMap.addHalfDuplexButton(i); + } + + // collect the tables that map to the same KeyID. we know it + // can't be any earlier tables because of the check above. + std::set tables; + tables.insert(static_cast(j)); + for (UInt32 k = j + 1; k < r.getNumTables(); ++k) { + if (buttonKeys[k].first == id) { + tables.insert(static_cast(k)); + } + } + + // collect the modifier combinations that map to any of the + // tables we just collected + for (UInt32 k = 0; k < r.getNumModifierCombinations(); ++k) { + modifiers[k] = (tables.count(r.getTableForModifier(k)) > 0); + } + + // figure out which modifiers the key is sensitive to. the + // key is insensitive to a modifier if for every modifier mask + // with the modifier bit unset in the modifiers we also find + // the same mask with the bit set. + // + // we ignore a few modifiers that we know aren't important + // for generating characters. in fact, we want to ignore any + // characters generated by the control key. we don't map + // those and instead expect the control modifier plus a key. + UInt32 sensitive = 0; + for (UInt32 k = 0; (1u << k) < + r.getNumModifierCombinations(); ++k) { + UInt32 bit = (1u << k); + if ((bit << 8) == cmdKey || + (bit << 8) == controlKey || + (bit << 8) == rightControlKey) { + continue; + } + for (UInt32 m = 0; m < r.getNumModifierCombinations(); ++m) { + if (modifiers[m] != modifiers[m ^ bit]) { + sensitive |= bit; + break; + } + } + } + + // find each required modifier mask. the key can be synthesized + // using any of the masks. + std::set required; + for (UInt32 k = 0; k < r.getNumModifierCombinations(); ++k) { + if ((k & sensitive) == k && modifiers[k & sensitive]) { + required.insert(k); + } + } + + // now add a key entry for each key/required modifier pair. + item.m_sensitive = mapModifiersFromOSX(sensitive << 8); + for (std::set::iterator k = required.begin(); + k != required.end(); ++k) { + item.m_required = mapModifiersFromOSX(*k << 8); + keyMap.addKeyEntry(item); + } + } + } + + return true; +} + +bool +COSXKeyState::mapSynergyHotKeyToMac(KeyID key, KeyModifierMask mask, + UInt32 &macVirtualKey, UInt32 &macModifierMask) const +{ + // look up button for key + KeyButton button = getButton(key, pollActiveGroup()); + if (button == 0 && key != kKeyNone) { + return false; + } + macVirtualKey = mapKeyButtonToVirtualKey(button); + + // calculate modifier mask + macModifierMask = 0; + if ((mask & KeyModifierShift) != 0) { + macModifierMask |= shiftKey; + } + if ((mask & KeyModifierControl) != 0) { + macModifierMask |= controlKey; + } + if ((mask & KeyModifierAlt) != 0) { + macModifierMask |= cmdKey; + } + if ((mask & KeyModifierSuper) != 0) { + macModifierMask |= optionKey; + } + if ((mask & KeyModifierCapsLock) != 0) { + macModifierMask |= alphaLock; + } + + return true; +} + void COSXKeyState::handleModifierKeys(void* target, KeyModifierMask oldMask, KeyModifierMask newMask) @@ -528,399 +582,92 @@ COSXKeyState::handleModifierKeys(void* target, // synthesize changed modifier keys if ((changed & KeyModifierShift) != 0) { - handleModifierKey(target, kKeyShift_L, - (newMask & KeyModifierShift) != 0); + handleModifierKey(target, s_shiftVK, kKeyShift_L, + (newMask & KeyModifierShift) != 0, newMask); } if ((changed & KeyModifierControl) != 0) { - handleModifierKey(target, kKeyControl_L, - (newMask & KeyModifierControl) != 0); + handleModifierKey(target, s_controlVK, kKeyControl_L, + (newMask & KeyModifierControl) != 0, newMask); } if ((changed & KeyModifierAlt) != 0) { - handleModifierKey(target, kKeyAlt_L, - (newMask & KeyModifierAlt) != 0); + handleModifierKey(target, s_altVK, kKeyAlt_L, + (newMask & KeyModifierAlt) != 0, newMask); } if ((changed & KeyModifierSuper) != 0) { - handleModifierKey(target, kKeySuper_L, - (newMask & KeyModifierSuper) != 0); + handleModifierKey(target, s_superVK, kKeySuper_L, + (newMask & KeyModifierSuper) != 0, newMask); } if ((changed & KeyModifierCapsLock) != 0) { - handleModifierKey(target, kKeyCapsLock, - (newMask & KeyModifierCapsLock) != 0); + handleModifierKey(target, s_capsLockVK, kKeyCapsLock, + (newMask & KeyModifierCapsLock) != 0, newMask); } } void -COSXKeyState::handleModifierKey(void* target, KeyID id, bool down) +COSXKeyState::handleModifierKey(void* target, + UInt32 virtualKey, KeyID id, + bool down, KeyModifierMask newMask) { - CKeyIDMap::const_iterator keyIndex = m_keyMap.find(id); - if (keyIndex == m_keyMap.end()) { - return; + KeyButton button = mapVirtualKeyToKeyButton(virtualKey); + onKey(button, down, newMask); + sendKeyEvent(target, down, false, id, newMask, 0, button); +} + +bool +COSXKeyState::getGroups(GroupList& groups) const +{ + // get number of layouts + CFIndex n; + OSStatus status = KLGetKeyboardLayoutCount(&n); + if (status != noErr) { + LOG((CLOG_DEBUG1 "can't get keyboard layouts")); + return false; } - KeyButton button = keyIndex->second[0].m_button; - setKeyDown(button, down); - sendKeyEvent(target, down, false, id, getActiveModifiers(), 0, button); + + // get each layout + groups.clear(); + for (CFIndex i = 0; i < n; ++i) { + KeyboardLayoutRef keyboardLayout; + status = KLGetKeyboardLayoutAtIndex(i, &keyboardLayout); + if (status == noErr) { + groups.push_back(keyboardLayout); + } + } + return true; +} + +void +COSXKeyState::setGroup(SInt32 group) +{ + KLSetCurrentKeyboardLayout(m_groups[group]); } void COSXKeyState::checkKeyboardLayout() { - SInt16 currentKeyScript = GetScriptManagerVariable(smKeyScript); - SInt16 keyboardLayoutID = GetScriptVariable(currentKeyScript, smScriptKeys); - UInt32 keyboardType = LMGetKbdType(); - if (keyboardLayoutID != m_keyboardLayoutID || - keyboardType != m_keyboardType) { - // layout changed - m_keyboardType = keyboardType; - setKeyboardLayout(keyboardLayoutID); - updateKeys(); + // XXX -- should call this when notified that groups have changed. + // if no notification for that then we should poll. + GroupList groups; + if (getGroups(groups) && groups != m_groups) { + updateKeyMap(); + updateKeyState(); } } void -COSXKeyState::setKeyboardLayout(SInt16 keyboardLayoutID) +COSXKeyState::adjustAltGrModifier(const CKeyIDs& ids, + KeyModifierMask* mask, bool isCommand) const { - m_keyboardLayoutID = keyboardLayoutID; - m_deadKeyState = 0; - m_KCHRHandle = GetResource('KCHR', m_keyboardLayoutID); - m_uchrHandle = GetResource('uchr', m_keyboardLayoutID); - m_KCHRResource = NULL; - m_uchrResource = NULL; - if (m_uchrHandle != NULL) { - m_uchrResource = reinterpret_cast(*m_uchrHandle); - } - if (m_KCHRHandle != NULL) { - m_KCHRResource = reinterpret_cast(*m_KCHRHandle); - } -} - -void -COSXKeyState::fillSpecialKeys(CKeyIDMap& keyMap, - CVirtualKeyMap& virtualKeyMap) const -{ - // FIXME -- would like to avoid hard coded tables - CKeyEventInfo info; - for (UInt32 i = 0; i < sizeof(s_controlKeys) / - sizeof(s_controlKeys[0]); ++i) { - const CKeyEntry& entry = s_controlKeys[i]; - KeyID keyID = entry.m_keyID; - info.m_button = mapVirtualKeyToKeyButton(entry.m_virtualKey); - info.m_requiredMask = 0; - info.m_requiredState = 0; - if (keyMap.count(keyID) == 0) { - keyMap[keyID].push_back(info); - } - if (virtualKeyMap.count(entry.m_virtualKey) == 0) { - virtualKeyMap[entry.m_virtualKey] = entry.m_keyID; - } - } -} - -bool -COSXKeyState::fillKCHRKeysMap(CKeyIDMap& keyMap) const -{ - assert(m_KCHRResource != NULL); - - CKCHRResource* r = m_KCHRResource; - - // build non-composed keys to virtual keys mapping - std::map vkMap; - for (SInt32 i = 0; i < r->m_numTables; ++i) { - // determine the modifier keys for table i - KeyModifierMask mask = - maskForTable(static_cast(i), r->m_tableSelectionIndex, - 256, static_cast(i)); - - // build the KeyID to virtual key map - for (SInt32 j = 0; j < 128; ++j) { - // get character - UInt8 c = r->m_characterTables[i][j]; - - // save key info - // FIXME -- should set only those bits in m_requiredMask that - // correspond to modifiers that are truly necessary to - // generate the character. this mostly works as-is, though. - CKeyEventInfo info; - info.m_button = mapVirtualKeyToKeyButton(j); - info.m_requiredMask = - KeyModifierShift | KeyModifierSuper | - KeyModifierCapsLock; - info.m_requiredState = mask; - - // save character to virtual key mapping - if (vkMap.count(c) == 0) { - vkMap[c] = info; - } - - // skip non-glyph character - if (c < 32 || c == 127) { - continue; - } - - // map character to KeyID - KeyID keyID = charToKeyID(c); - - // if we've seen this character already then do nothing - if (keyMap.count(keyID) != 0) { - continue; - } - - // save entry for character - keyMap[keyID].push_back(info); - } - } - - // build composed keys to virtual keys mapping - CKCHRDeadKeys* dkp = - reinterpret_cast(r->m_characterTables[r->m_numTables]); - CKCHRDeadKeyRecord* dkr = dkp->m_records; - for (SInt32 i = 0; i < dkp->m_numRecords; ++i) { - // determine the modifier keys for table i - KeyModifierMask mask = - maskForTable(dkr->m_tableIndex, r->m_tableSelectionIndex, - 256, dkr->m_tableIndex); - - // map each completion - for (SInt32 j = 0; j < dkr->m_numCompletions; ++j) { - // get character - UInt8 c = dkr->m_completion[j][1]; - - // skip non-glyph character - if (c < 32 || c == 127) { - continue; - } - - // map character to KeyID - KeyID keyID = charToKeyID(c); - - // if we've seen this character already then do nothing - if (keyMap.count(keyID) != 0) { - continue; - } - - // map keyID, first to the dead key then to uncomposed - // character. we must find a virtual key that maps to - // to the uncomposed character. - if (vkMap.count(dkr->m_completion[j][0]) != 0) { - CKeySequence& sequence = keyMap[keyID]; - - // save key info - // FIXME -- should set only those bits in m_requiredMask that - // correspond to modifiers that are truly necessary to - // generate the character. this mostly works as-is, though. - CKeyEventInfo info; - info.m_button = mapVirtualKeyToKeyButton( - dkr->m_virtualKey); - info.m_requiredMask = - KeyModifierShift | KeyModifierSuper | - KeyModifierAlt | KeyModifierControl | - KeyModifierCapsLock; - info.m_requiredState = mask; - sequence.push_back(info); - sequence.push_back(vkMap[dkr->m_completion[j][0]]); + if (!isCommand) { + for (CKeyIDs::const_iterator i = ids.begin(); i != ids.end(); ++i) { + KeyID id = *i; + if (id != kKeyNone && + ((id < 0xe000u || id > 0xefffu) || + (id >= kKeyKP_Equal && id <= kKeyKP_9))) { + *mask |= KeyModifierAltGr; + return; } } - - // next table. skip all the completions and the no match - // pair to get the next table. - dkr = reinterpret_cast( - dkr->m_completion[dkr->m_numCompletions + 1]); - } - - return true; -} - -bool -COSXKeyState::filluchrKeysMap(CKeyIDMap& keyMap) const -{ - assert(m_uchrResource != NULL); - - UCKeyboardLayout* r = m_uchrResource; - UInt8* base = reinterpret_cast(r); - - UCKeyLayoutFeatureInfo* fi = NULL; - if (r->keyLayoutFeatureInfoOffset != 0) { - fi = reinterpret_cast( - base + r->keyLayoutFeatureInfoOffset); - } - - // find the keyboard info for the current keyboard type - UCKeyboardTypeHeader* th = NULL; - for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) { - if (m_keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst && - m_keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) { - th = r->keyboardTypeList + i; - break; - } - if (r->keyboardTypeList[i].keyboardTypeFirst == 0) { - // found the default. use it unless we find a match. - th = r->keyboardTypeList + i; - } - } - if (th == NULL) { - // cannot find a suitable keyboard type - return false; - } - - // get tables for keyboard type - UCKeyModifiersToTableNum* m = - reinterpret_cast( - base + th->keyModifiersToTableNumOffset); - UCKeyToCharTableIndex* cti = - reinterpret_cast( - base + th->keyToCharTableIndexOffset); - UCKeySequenceDataIndex* sdi = - reinterpret_cast( - base + th->keySequenceDataIndexOffset); - - // build non-composed keys to virtual keys mapping - CDeadKeyMap dkMap; - for (UInt32 i = 0; i < cti->keyToCharTableCount; ++i) { - // determine the modifier keys for table i - KeyModifierMask mask = - maskForTable(static_cast(i), m->tableNum, - m->modifiersCount, m->defaultTableNum); - - // get the character table - UCKeyOutput* ct = - reinterpret_cast( - base + cti->keyToCharTableOffsets[i]); - - // build the KeyID to virtual key map - for (SInt32 j = 0; j < cti->keyToCharTableSize; ++j) { - // get character - UInt16 c = ct[j]; - - // ignore key sequence mappings - if ((c & 0xc000) == 0x8000) { - UInt16 index = (c & 0x3fff); - if (index < sdi->charSequenceCount && - sdi->charSequenceOffsets[index] != - sdi->charSequenceOffsets[index + 1]) { - continue; - } - - // not a sequence mapping. use character as-is. - } - - // just record dead key mappings - else if ((c & 0xc000) == 0x4000) { - UInt16 index = (c & 0x3fff); - if (dkMap.count(index) == 0) { - dkMap[index] = std::make_pair(j, mask); - } - continue; - } - - // skip non-glyph character - if (c < 32 || c == 127 || c == 0xfffe || c == 0xffff) { - continue; - } - - // map character to KeyID - KeyID keyID = unicharToKeyID(c); - - // if we've seen this character already then do nothing - if (keyMap.count(keyID) != 0) { - continue; - } - - // save entry for character - // FIXME -- should set only those bits in m_requiredMask that - // correspond to modifiers that are truly necessary to - // generate the character. - CKeyEventInfo info; - info.m_button = mapVirtualKeyToKeyButton(j); - info.m_requiredMask = - KeyModifierShift | KeyModifierSuper | - KeyModifierCapsLock; - info.m_requiredState = mask; - keyMap[keyID].push_back(info); - } - } - - // build composed keys to virtual keys mapping - UCKeyStateRecordsIndex* sri = NULL; - if (th->keyStateRecordsIndexOffset != NULL) { - sri = reinterpret_cast( - base + th->keyStateRecordsIndexOffset); - } - UCKeyStateTerminators* st = NULL; - if (th->keyStateTerminatorsOffset != NULL) { - st = reinterpret_cast( - base + th->keyStateTerminatorsOffset); - } - CKeySequence sequence; - mapDeadKeySequence(keyMap, sequence, 0, base, sri, st, dkMap); - - return true; -} - -void -COSXKeyState::mapDeadKeySequence(CKeyIDMap& keyMap, - CKeySequence& sequence, - UInt16 state, const UInt8* base, - const UCKeyStateRecordsIndex* sri, - const UCKeyStateTerminators* st, - CDeadKeyMap& dkMap) -{ - for (CDeadKeyMap::const_iterator i = dkMap.begin(); i != dkMap.end(); ++i) { - UInt16 index = i->first; - const UCKeyStateRecord* sr = - reinterpret_cast( - base + sri->keyStateRecordOffsets[index]); - const UCKeyStateEntryTerminal* kset = - reinterpret_cast( - sr->stateEntryData); - - UInt16 c = 0; - UInt16 nextState = 0; - if (state == 0) { - c = sr->stateZeroCharData; - nextState = sr->stateZeroNextState; - } - else if (sr->stateEntryFormat == kUCKeyStateEntryTerminalFormat) { - for (UInt16 j = 0; j < sr->stateEntryCount; ++j) { - if (kset[j].curState == state) { - c = kset[j].charData; - break; - } - } - // XXX -- default state terminator not supported yet - } - else if (sr->stateEntryFormat == kUCKeyStateEntryRangeFormat) { - // XXX -- not supported yet - } - - // push character onto sequence. m_requiredMask should only - // have those modifiers that are truly necessary to generate - // the character. - CKeyEventInfo info; - info.m_button = mapVirtualKeyToKeyButton( - i->second.first); - info.m_requiredMask = - KeyModifierShift | KeyModifierSuper | - KeyModifierCapsLock; - info.m_requiredState = i->second.second; - sequence.push_back(info); - - if (nextState != 0) { - // next in dead key sequence - mapDeadKeySequence(keyMap, sequence, - nextState, base, sri, st, dkMap); - } - else if (c >= 32 && c != 127 && c != 0xfffe && c != 0xffff) { - // terminate sequence - KeyID keyID = unicharToKeyID(c); - - // if we've seen this character already then do nothing, - // otherwise save it in the key map. - if (keyMap.count(keyID) == 0) { - keyMap[keyID] = sequence; - } - } - - // pop character from sequence - sequence.pop_back(); } } @@ -937,123 +684,513 @@ COSXKeyState::mapKeyButtonToVirtualKey(KeyButton keyButton) return static_cast(keyButton - KeyButtonOffset); } + +// +// COSXKeyState::CKeyResource +// + KeyID -COSXKeyState::charToKeyID(UInt8 c) +COSXKeyState::CKeyResource::getKeyID(UInt8 c) { if (c == 0) { return kKeyNone; } - else if (c >= 32 && c < 127) { + else if (c >= 32 && c < 127) { // ASCII - return static_cast(c); - } - else { - // create string with character - char str[2]; - str[0] = static_cast(c); - str[1] = 0; + return static_cast(c); + } + else { + // handle special keys + switch (c) { + case 0x01: + return kKeyHome; - // convert to unicode - CFStringRef cfString = - CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, - str, GetScriptManagerVariable(smKeyScript), - kCFAllocatorNull); + case 0x02: + return kKeyKP_Enter; - // convert to precomposed - CFMutableStringRef mcfString = - CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString); - CFRelease(cfString); - CFStringNormalize(mcfString, kCFStringNormalizationFormC); + case 0x03: + return kKeyKP_Enter; - // check result - int unicodeLength = CFStringGetLength(mcfString); - if (unicodeLength == 0) { - return kKeyNone; - } - if (unicodeLength > 1) { + case 0x04: + return kKeyEnd; + + case 0x05: + return kKeyHelp; + + case 0x08: + return kKeyBackSpace; + + case 0x09: + return kKeyTab; + + case 0x0b: + return kKeyPageUp; + + case 0x0c: + return kKeyPageDown; + + case 0x0d: + return kKeyReturn; + + case 0x10: + // OS X maps all the function keys (F1, etc) to this one key. + // we can't determine the right key here so we have to do it + // some other way. + return kKeyNone; + + case 0x1b: + return kKeyEscape; + + case 0x1c: + return kKeyLeft; + + case 0x1d: + return kKeyRight; + + case 0x1e: + return kKeyUp; + + case 0x1f: + return kKeyDown; + + case 0x7f: + return kKeyDelete; + + case 0x06: + case 0x07: + case 0x0a: + case 0x0e: + case 0x0f: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + // discard other control characters + return kKeyNone; + + default: + // not special or unknown + break; + } + + // create string with character + char str[2]; + str[0] = static_cast(c); + str[1] = 0; + + // convert to unicode + CFStringRef cfString = + CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, + str, GetScriptManagerVariable(smKeyScript), + kCFAllocatorNull); + + // convert to precomposed + CFMutableStringRef mcfString = + CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString); + CFRelease(cfString); + CFStringNormalize(mcfString, kCFStringNormalizationFormC); + + // check result + int unicodeLength = CFStringGetLength(mcfString); + if (unicodeLength == 0) { + CFRelease(mcfString); + return kKeyNone; + } + if (unicodeLength > 1) { // FIXME -- more than one character, we should handle this - return kKeyNone; - } + CFRelease(mcfString); + return kKeyNone; + } - // get unicode character - UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0); - CFRelease(mcfString); + // get unicode character + UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0); + CFRelease(mcfString); - // convert to KeyID - return static_cast(uc); - } + // convert to KeyID + return static_cast(uc); + } } KeyID -COSXKeyState::unicharToKeyID(UniChar c) +COSXKeyState::CKeyResource::unicharToKeyID(UniChar c) { - return static_cast(c); + switch (c) { + case 3: + return kKeyKP_Enter; + + case 8: + return kKeyBackSpace; + + case 9: + return kKeyTab; + + case 13: + return kKeyReturn; + + case 27: + return kKeyEscape; + + case 127: + return kKeyDelete; + + default: + if (c < 32) { + return kKeyNone; + } + return static_cast(c); + } } -KeyModifierMask -COSXKeyState::maskForTable(UInt8 i, UInt8* tableSelectors, - UInt32 numEntries, UInt8 defaultIndex) + +// +// COSXKeyState::CKCHRKeyResource +// + +COSXKeyState::CKCHRKeyResource::CKCHRKeyResource(const void* resource) { - // this is a table of 0 to 255 sorted by the number of 1 bits then - // numerical order. - static const UInt8 s_indexTable[] = { -0, 1, 2, 4, 8, 16, 32, 64, 128, 3, 5, 6, 9, 10, 12, 17, -18, 20, 24, 33, 34, 36, 40, 48, 65, 66, 68, 72, 80, 96, 129, 130, -132, 136, 144, 160, 192, 7, 11, 13, 14, 19, 21, 22, 25, 26, 28, 35, -37, 38, 41, 42, 44, 49, 50, 52, 56, 67, 69, 70, 73, 74, 76, 81, -82, 84, 88, 97, 98, 100, 104, 112, 131, 133, 134, 137, 138, 140, 145, 146, -148, 152, 161, 162, 164, 168, 176, 193, 194, 196, 200, 208, 224, 15, 23, 27, -29, 30, 39, 43, 45, 46, 51, 53, 54, 57, 58, 60, 71, 75, 77, 78, -83, 85, 86, 89, 90, 92, 99, 101, 102, 105, 106, 108, 113, 114, 116, 120, -135, 139, 141, 142, 147, 149, 150, 153, 154, 156, 163, 165, 166, 169, 170, 172, -177, 178, 180, 184, 195, 197, 198, 201, 202, 204, 209, 210, 212, 216, 225, 226, -228, 232, 240, 31, 47, 55, 59, 61, 62, 79, 87, 91, 93, 94, 103, 107, -109, 110, 115, 117, 118, 121, 122, 124, 143, 151, 155, 157, 158, 167, 171, 173, -174, 179, 181, 182, 185, 186, 188, 199, 203, 205, 206, 211, 213, 214, 217, 218, -220, 227, 229, 230, 233, 234, 236, 241, 242, 244, 248, 63, 95, 111, 119, 123, -125, 126, 159, 175, 183, 187, 189, 190, 207, 215, 219, 221, 222, 231, 235, 237, -238, 243, 245, 246, 249, 250, 252, 127, 191, 223, 239, 247, 251, 253, 254, 255 - }; + m_resource = reinterpret_cast(resource); +} - while (true) { - // find first entry in tableSelectors that maps to i. this is the - // one that uses the fewest modifier keys. - UInt8 maxIndex = static_cast(numEntries - 1); - for (UInt32 j = 0; j < 256; ++j) { - if (s_indexTable[j] <= maxIndex && - tableSelectors[s_indexTable[j]] == i) { - // convert our mask to a traditional mac modifier mask - // (which just means shifting it left 8 bits). - UInt16 macMask = (static_cast(s_indexTable[j]) << 8); +bool +COSXKeyState::CKCHRKeyResource::isValid() const +{ + return (m_resource != NULL); +} - // convert the mac modifier mask to our mask. - KeyModifierMask mask = 0; - if ((macMask & (shiftKey | rightShiftKey)) != 0) { - mask |= KeyModifierShift; - } - if ((macMask & (controlKey | rightControlKey)) != 0) { - mask |= KeyModifierControl; - } - if ((macMask & cmdKey) != 0) { - mask |= KeyModifierAlt; - } - if ((macMask & (optionKey | rightOptionKey)) != 0) { - mask |= KeyModifierSuper; - } - if ((macMask & alphaLock) != 0) { - mask |= KeyModifierCapsLock; - } - return mask; - } - } +UInt32 +COSXKeyState::CKCHRKeyResource::getNumModifierCombinations() const +{ + // only 32 (not 256) because the righthanded modifier bits are ignored + return 32; +} - // no match. try defaultIndex. - if (i == defaultIndex) { - break; +UInt32 +COSXKeyState::CKCHRKeyResource::getNumTables() const +{ + return m_resource->m_numTables; +} + +UInt32 +COSXKeyState::CKCHRKeyResource::getNumButtons() const +{ + return 128; +} + +UInt32 +COSXKeyState::CKCHRKeyResource::getTableForModifier(UInt32 mask) const +{ + assert(mask < getNumModifierCombinations()); + + return m_resource->m_tableSelectionIndex[mask]; +} + +KeyID +COSXKeyState::CKCHRKeyResource::getKey(UInt32 table, UInt32 button) const +{ + assert(table < getNumTables()); + assert(button < getNumButtons()); + + UInt8 c = m_resource->m_characterTables[table][button]; + if (c == 0) { + // could be a dead key + const CKCHRDeadKeys* dkp = + reinterpret_cast( + m_resource->m_characterTables[getNumTables()]); + const CKCHRDeadKeyRecord* dkr = dkp->m_records; + for (SInt16 i = 0; i < dkp->m_numRecords; ++i) { + if (dkr->m_tableIndex == table && dkr->m_virtualKey == button) { + // get the no completion entry + c = dkr->m_completion[dkr->m_numCompletions][1]; + return CKeyMap::getDeadKey(getKeyID(c)); + } + + // next table. skip all the completions and the no match + // pair to get the next table. + dkr = reinterpret_cast( + dkr->m_completion[dkr->m_numCompletions + 1]); } - i = defaultIndex; } - // should never get here. - return 0; + return getKeyID(c); +} + + +// +// COSXKeyState::CUCHRKeyResource +// + +COSXKeyState::CUCHRKeyResource::CUCHRKeyResource(const void* resource, + UInt32 keyboardType) : + m_m(NULL), + m_cti(NULL), + m_sdi(NULL), + m_sri(NULL), + m_st(NULL) +{ + m_resource = reinterpret_cast(resource); + if (m_resource == NULL) { + return; + } + + // find the keyboard info for the current keyboard type + const UCKeyboardTypeHeader* th = NULL; + const UCKeyboardLayout* r = m_resource; + for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) { + if (keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst && + keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) { + th = r->keyboardTypeList + i; + break; + } + if (r->keyboardTypeList[i].keyboardTypeFirst == 0) { + // found the default. use it unless we find a match. + th = r->keyboardTypeList + i; + } + } + if (th == NULL) { + // cannot find a suitable keyboard type + return; + } + + // get tables for keyboard type + const UInt8* base = reinterpret_cast(m_resource); + m_m = reinterpret_cast(base + + th->keyModifiersToTableNumOffset); + m_cti = reinterpret_cast(base + + th->keyToCharTableIndexOffset); + m_sdi = reinterpret_cast(base + + th->keySequenceDataIndexOffset); + if (th->keyStateRecordsIndexOffset != 0) { + m_sri = reinterpret_cast(base + + th->keyStateRecordsIndexOffset); + } + if (th->keyStateTerminatorsOffset != 0) { + m_st = reinterpret_cast(base + + th->keyStateTerminatorsOffset); + } + + // find the space key, but only if it can combine with dead keys. + // a dead key followed by a space yields the non-dead version of + // the dead key. + m_spaceOutput = 0xffffu; + UInt32 table = getTableForModifier(0); + for (UInt32 button = 0, n = getNumButtons(); button < n; ++button) { + KeyID id = getKey(table, button); + if (id == 0x20) { + UCKeyOutput c = + reinterpret_cast(base + + m_cti->keyToCharTableOffsets[table])[button]; + if ((c & kUCKeyOutputTestForIndexMask) == + kUCKeyOutputStateIndexMask) { + m_spaceOutput = (c & kUCKeyOutputGetIndexMask); + break; + } + } + } +} + +bool +COSXKeyState::CUCHRKeyResource::isValid() const +{ + return (m_m != NULL); +} + +UInt32 +COSXKeyState::CUCHRKeyResource::getNumModifierCombinations() const +{ + // only 32 (not 256) because the righthanded modifier bits are ignored + return 32; +} + +UInt32 +COSXKeyState::CUCHRKeyResource::getNumTables() const +{ + return m_cti->keyToCharTableCount; +} + +UInt32 +COSXKeyState::CUCHRKeyResource::getNumButtons() const +{ + return m_cti->keyToCharTableSize; +} + +UInt32 +COSXKeyState::CUCHRKeyResource::getTableForModifier(UInt32 mask) const +{ + if (mask >= m_m->modifiersCount) { + return m_m->defaultTableNum; + } + else { + return m_m->tableNum[mask]; + } +} + +KeyID +COSXKeyState::CUCHRKeyResource::getKey(UInt32 table, UInt32 button) const +{ + assert(table < getNumTables()); + assert(button < getNumButtons()); + + const UInt8* base = reinterpret_cast(m_resource); + const UCKeyOutput c = reinterpret_cast(base + + m_cti->keyToCharTableOffsets[table])[button]; + + KeySequence keys; + switch (c & kUCKeyOutputTestForIndexMask) { + case kUCKeyOutputStateIndexMask: + if (!getDeadKey(keys, c & kUCKeyOutputGetIndexMask)) { + return kKeyNone; + } + break; + + case kUCKeyOutputSequenceIndexMask: + default: + if (!addSequence(keys, c)) { + return kKeyNone; + } + break; + } + + // XXX -- no support for multiple characters + if (keys.size() != 1) { + return kKeyNone; + } + + return keys.front(); +} + +bool +COSXKeyState::CUCHRKeyResource::getDeadKey( + KeySequence& keys, UInt16 index) const +{ + if (m_sri == NULL || index >= m_sri->keyStateRecordCount) { + // XXX -- should we be using some other fallback? + return false; + } + + UInt16 state = 0; + if (!getKeyRecord(keys, index, state)) { + return false; + } + if (state == 0) { + // not a dead key + return true; + } + + // no dead keys if we couldn't find the space key + if (m_spaceOutput == 0xffffu) { + return false; + } + + // the dead key should not have put anything in the key list + if (!keys.empty()) { + return false; + } + + // get the character generated by pressing the space key after the + // dead key. if we're still in a compose state afterwards then we're + // confused so we bail. + if (!getKeyRecord(keys, m_spaceOutput, state) || state != 0) { + return false; + } + + // convert keys to their dead counterparts + for (KeySequence::iterator i = keys.begin(); i != keys.end(); ++i) { + *i = CKeyMap::getDeadKey(*i); + } + + return true; +} + +bool +COSXKeyState::CUCHRKeyResource::getKeyRecord( + KeySequence& keys, UInt16 index, UInt16& state) const +{ + const UInt8* base = reinterpret_cast(m_resource); + const UCKeyStateRecord* sr = + reinterpret_cast(base + + m_sri->keyStateRecordOffsets[index]); + const UCKeyStateEntryTerminal* kset = + reinterpret_cast(sr->stateEntryData); + + UInt16 nextState = 0; + bool found = false; + if (state == 0) { + found = true; + nextState = sr->stateZeroNextState; + if (!addSequence(keys, sr->stateZeroCharData)) { + return false; + } + } + else { + // we have a next entry + switch (sr->stateEntryFormat) { + case kUCKeyStateEntryTerminalFormat: + for (UInt16 j = 0; j < sr->stateEntryCount; ++j) { + if (kset[j].curState == state) { + if (!addSequence(keys, kset[j].charData)) { + return false; + } + nextState = 0; + found = true; + break; + } + } + break; + + case kUCKeyStateEntryRangeFormat: + // XXX -- not supported yet + break; + + default: + // XXX -- unknown format + return false; + } + } + if (!found) { + // use a terminator + if (m_st != NULL && state < m_st->keyStateTerminatorCount) { + if (!addSequence(keys, m_st->keyStateTerminators[state - 1])) { + return false; + } + } + nextState = sr->stateZeroNextState; + if (!addSequence(keys, sr->stateZeroCharData)) { + return false; + } + } + + // next + state = nextState; + + return true; +} + +bool +COSXKeyState::CUCHRKeyResource::addSequence( + KeySequence& keys, UCKeyCharSeq c) const +{ + if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) { + UInt16 index = (c & kUCKeyOutputGetIndexMask); + if (index < m_sdi->charSequenceCount && + m_sdi->charSequenceOffsets[index] != + m_sdi->charSequenceOffsets[index + 1]) { + // XXX -- sequences not supported yet + return false; + } + } + + if (c != 0xfffe && c != 0xffff) { + KeyID id = unicharToKeyID(c); + if (id != kKeyNone) { + keys.push_back(id); + } + } + + return true; } diff --git a/lib/platform/COSXKeyState.h b/lib/platform/COSXKeyState.h index 685a56b1..baf69713 100644 --- a/lib/platform/COSXKeyState.h +++ b/lib/platform/COSXKeyState.h @@ -32,6 +32,28 @@ public: COSXKeyState(); virtual ~COSXKeyState(); + //! @name modifiers + //@{ + + //! Handle modifier key change + /*! + Determines which modifier keys have changed and updates the modifier + state and sends key events as appropriate. + */ + void handleModifierKeys(void* target, + KeyModifierMask oldMask, KeyModifierMask newMask); + + //@} + //! @name accessors + //@{ + + //! Convert OS X modifier mask to synergy mask + /*! + Returns the synergy modifier mask corresponding to the OS X modifier + mask in \p mask. + */ + KeyModifierMask mapModifiersFromOSX(UInt32 mask) const; + //! Map key event to keys /*! Converts a key event into a sequence of KeyIDs and the shadow modifier @@ -43,80 +65,64 @@ public: KeyButton mapKeyFromEvent(CKeyIDs& ids, KeyModifierMask* maskOut, EventRef event) const; - //! Handle modifier key change + //! Map key and mask to native values /*! - Determines which modifier keys have changed and updates the modifier - state and sends key events as appropriate. + Calculates mac virtual key and mask for a key \p key and modifiers + \p mask. Returns \c true if the key can be mapped, \c false otherwise. */ - void handleModifierKeys(void* target, - KeyModifierMask oldMask, KeyModifierMask newMask); + bool mapSynergyHotKeyToMac(KeyID key, KeyModifierMask mask, + UInt32& macVirtualKey, + UInt32& macModifierMask) const; + + //@} // IKeyState overrides - virtual void setHalfDuplexMask(KeyModifierMask); virtual bool fakeCtrlAltDel(); - virtual const char* getKeyName(KeyButton) const; - virtual void sendKeyEvent(void* target, - bool press, bool isAutoRepeat, - KeyID key, KeyModifierMask mask, - SInt32 count, KeyButton button); + virtual KeyModifierMask + pollActiveModifiers() const; + virtual SInt32 pollActiveGroup() const; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const; protected: - // IKeyState overrides - virtual void doUpdateKeys(); - virtual void doFakeKeyEvent(KeyButton button, - bool press, bool isAutoRepeat); - virtual KeyButton mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const; + // CKeyState overrides + virtual void getKeyMap(CKeyMap& keyMap); + virtual void fakeKey(const Keystroke& keystroke); private: - struct CKeyEventInfo { - public: - KeyButton m_button; - KeyModifierMask m_requiredMask; - KeyModifierMask m_requiredState; - }; - typedef std::vector CKeySequence; - typedef std::map CKeyIDMap; - typedef std::map CVirtualKeyMap; - typedef std::map > CDeadKeyMap; + class CKeyResource; + typedef std::vector GroupList; - KeyButton addKeystrokes(Keystrokes& keys, - KeyButton keyButton, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask, - bool isAutoRepeat) const; - bool adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask, - KeyModifierMask requiredMask) const; - void addKeyButton(KeyButtons& keys, KeyID id) const; - void handleModifierKey(void* target, KeyID id, bool down); + // Add hard coded special keys to a CKeyMap. + void getKeyMapForSpecialKeys( + CKeyMap& keyMap, SInt32 group) const; - // Check if the keyboard layout has changed and call doUpdateKeys + // Convert keyboard resource to a key map + bool getKeyMap(CKeyMap& keyMap, + SInt32 group, const CKeyResource& r) const; + + // Get the available keyboard groups + bool getGroups(GroupList&) const; + + // Change active keyboard group to group + void setGroup(SInt32 group); + + // Check if the keyboard layout has changed and update keyboard state // if so. void checkKeyboardLayout(); - // Switch to a new keyboard layout. - void setKeyboardLayout(SInt16 keyboardLayoutID); + // Send an event for the given modifier key + void handleModifierKey(void* target, + UInt32 virtualKey, KeyID id, + bool down, KeyModifierMask newMask); - // Insert KeyID to key sequences for non-printing characters, like - // delete, home, up arrow, etc. and the virtual key to KeyID mapping. - void fillSpecialKeys(CKeyIDMap& keyMap, - CVirtualKeyMap& virtualKeyMap) const; - - // Convert the KCHR resource to a KeyID to key sequence map. the - // map maps each KeyID to the sequence of keys (with modifiers) - // that would have to be synthesized to generate the KeyID character. - // Returns false iff no KCHR resource was found. - bool fillKCHRKeysMap(CKeyIDMap& keyMap) const; - - // Convert the uchr resource to a KeyID to key sequence map. the - // map maps each KeyID to the sequence of keys (with modifiers) - // that would have to be synthesized to generate the KeyID character. - // Returns false iff no uchr resource was found or it couldn't be - // mapped. - bool filluchrKeysMap(CKeyIDMap& keyMap) const; + // Checks if any in \p ids is a glyph key and if \p isCommand is false. + // If so it adds the AltGr modifier to \p mask. This allows OS X + // servers to use the option key both as AltGr and as a modifier. If + // option is acting as AltGr (i.e. it generates a glyph and there are + // no command modifiers active) then we don't send the super modifier + // to clients because they'd try to match it as a command modifier. + void adjustAltGrModifier(const CKeyIDs& ids, + KeyModifierMask* mask, bool isCommand) const; // Maps an OS X virtual key id to a KeyButton. This simply remaps // the ids so we don't use KeyButton 0. @@ -126,28 +132,89 @@ private: // mapVirtualKeyToKeyButton. static UInt32 mapKeyButtonToVirtualKey(KeyButton keyButton); - // Convert a character in the current script to the equivalent KeyID. - static KeyID charToKeyID(UInt8); - - // Convert a unicode character to the equivalent KeyID. - static KeyID unicharToKeyID(UniChar); - - // Choose the modifier mask with the fewest modifiers for character - // mapping table i. The tableSelectors table has numEntries. If - // no mapping is found for i, try mapping defaultIndex. - static KeyModifierMask - maskForTable(UInt8 i, UInt8* tableSelectors, - UInt32 numEntries, UInt8 defaultIndex); - - // Save characters built from dead key sequences. - static void mapDeadKeySequence(CKeyIDMap& keyMap, - CKeySequence& sequence, - UInt16 state, const UInt8* base, - const UCKeyStateRecordsIndex* sri, - const UCKeyStateTerminators* st, - CDeadKeyMap& dkMap); - private: + class CKeyResource : public IInterface { + public: + virtual bool isValid() const = 0; + virtual UInt32 getNumModifierCombinations() const = 0; + virtual UInt32 getNumTables() const = 0; + virtual UInt32 getNumButtons() const = 0; + virtual UInt32 getTableForModifier(UInt32 mask) const = 0; + virtual KeyID getKey(UInt32 table, UInt32 button) const = 0; + + // Convert a character in the current script to the equivalent KeyID + static KeyID getKeyID(UInt8); + + // Convert a unicode character to the equivalent KeyID. + static KeyID unicharToKeyID(UniChar); + }; + + class CKCHRKeyResource : public CKeyResource { + public: + CKCHRKeyResource(const void*); + + // CKeyResource overrides + virtual bool isValid() const; + virtual UInt32 getNumModifierCombinations() const; + virtual UInt32 getNumTables() const; + virtual UInt32 getNumButtons() const; + virtual UInt32 getTableForModifier(UInt32 mask) const; + virtual KeyID getKey(UInt32 table, UInt32 button) const; + + private: + struct KCHRResource { + public: + SInt16 m_version; + UInt8 m_tableSelectionIndex[256]; + SInt16 m_numTables; + UInt8 m_characterTables[1][128]; + }; + struct CKCHRDeadKeyRecord { + public: + UInt8 m_tableIndex; + UInt8 m_virtualKey; + SInt16 m_numCompletions; + UInt8 m_completion[1][2]; + }; + struct CKCHRDeadKeys { + public: + SInt16 m_numRecords; + CKCHRDeadKeyRecord m_records[1]; + }; + + const KCHRResource* m_resource; + }; + + class CUCHRKeyResource : public CKeyResource { + public: + CUCHRKeyResource(const void*, UInt32 keyboardType); + + // CKeyResource overrides + virtual bool isValid() const; + virtual UInt32 getNumModifierCombinations() const; + virtual UInt32 getNumTables() const; + virtual UInt32 getNumButtons() const; + virtual UInt32 getTableForModifier(UInt32 mask) const; + virtual KeyID getKey(UInt32 table, UInt32 button) const; + + private: + typedef std::vector KeySequence; + + bool getDeadKey(KeySequence& keys, UInt16 index) const; + bool getKeyRecord(KeySequence& keys, + UInt16 index, UInt16& state) const; + bool addSequence(KeySequence& keys, UCKeyCharSeq c) const; + + private: + const UCKeyboardLayout* m_resource; + const UCKeyModifiersToTableNum* m_m; + const UCKeyToCharTableIndex* m_cti; + const UCKeySequenceDataIndex* m_sdi; + const UCKeyStateRecordsIndex* m_sri; + const UCKeyStateTerminators* m_st; + UInt16 m_spaceOutput; + }; + // OS X uses a physical key if 0 for the 'A' key. synergy reserves // KeyButton 0 so we offset all OS X physical key ids by this much // when used as a KeyButton and by minus this much to map a KeyButton @@ -156,25 +223,13 @@ private: KeyButtonOffset = 1 }; - // KCHR resource header - struct CKCHRResource { - public: - SInt16 m_version; - UInt8 m_tableSelectionIndex[256]; - SInt16 m_numTables; - UInt8 m_characterTables[1][128]; - }; + typedef std::map GroupMap; + typedef std::map CVirtualKeyMap; - SInt16 m_keyboardLayoutID; - UInt32 m_keyboardType; - mutable UInt32 m_deadKeyState; - Handle m_KCHRHandle; - Handle m_uchrHandle; - CKCHRResource* m_KCHRResource; - UCKeyboardLayout* m_uchrResource; - CKeyIDMap m_keyMap; CVirtualKeyMap m_virtualKeyMap; - bool m_uchrFound; + mutable UInt32 m_deadKeyState; + GroupList m_groups; + GroupMap m_groupMap; }; #endif diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index 8ce872f1..82574cf4 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -18,33 +18,74 @@ #include "COSXKeyState.h" #include "COSXScreenSaver.h" #include "CClipboard.h" +#include "CKeyMap.h" +#include "CCondVar.h" +#include "CLock.h" +#include "CMutex.h" +#include "CThread.h" #include "CLog.h" #include "IEventQueue.h" #include "TMethodEventJob.h" +#include "TMethodJob.h" +#include "XArch.h" + +#include +#include + +// Set some enums for fast user switching if we're building with an SDK +// from before such support was added. +#if !defined(MAC_OS_X_VERSION_10_3) || \ + (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_3) +enum { + kEventClassSystem = 'macs', + kEventSystemUserSessionActivated = 10, + kEventSystemUserSessionDeactivated = 11 +}; +#endif + +// This isn't in any Apple SDK that I know of as of yet. +enum { + kSynergyEventMouseScroll = 11, + kSynergyMouseScrollAxisX = 'saxx', + kSynergyMouseScrollAxisY = 'saxy' +}; // // COSXScreen // +bool COSXScreen::s_testedForGHOM = false; +bool COSXScreen::s_hasGHOM = false; +CEvent::Type COSXScreen::s_confirmSleepEvent = CEvent::kUnknown; + COSXScreen::COSXScreen(bool isPrimary) : m_isPrimary(isPrimary), m_isOnScreen(m_isPrimary), m_cursorPosValid(false), m_cursorHidden(false), + m_dragNumButtonsDown(0), + m_dragTimer(NULL), m_keyState(NULL), m_sequenceNumber(0), m_screensaver(NULL), m_screensaverNotify(false), m_ownClipboard(false), + m_clipboardTimer(NULL), m_hiddenWindow(NULL), m_userInputWindow(NULL), - m_displayManagerNotificationUPP(NULL) + m_displayManagerNotificationUPP(NULL), + m_switchEventHandlerRef(0), + m_pmMutex(new CMutex), + m_pmWatchThread(NULL), + m_pmThreadReady(new CCondVar(m_pmMutex, false)), + m_activeModifierHotKey(0), + m_activeModifierHotKeyMask(0) { try { - m_displayID = CGMainDisplayID(); + m_displayID = CGMainDisplayID(); updateScreenShape(); - m_screensaver = new COSXScreenSaver(); - m_keyState = new COSXKeyState(); + m_screensaver = new COSXScreenSaver(getEventTarget()); + m_keyState = new COSXKeyState(); if (m_isPrimary) { // 1x1 window (to minimze the back buffer allocated for this @@ -81,17 +122,47 @@ COSXScreen::COSXScreen(bool isPrimary) : SetWindowAlpha(m_userInputWindow, 0); } + // install display manager notification handler m_displayManagerNotificationUPP = NewDMExtendedNotificationUPP(displayManagerCallback); - OSStatus err = GetCurrentProcess(&m_PSN); - err = DMRegisterExtendedNotifyProc(m_displayManagerNotificationUPP, this, 0, &m_PSN); + + // install fast user switching event handler + EventTypeSpec switchEventTypes[2]; + switchEventTypes[0].eventClass = kEventClassSystem; + switchEventTypes[0].eventKind = kEventSystemUserSessionDeactivated; + switchEventTypes[1].eventClass = kEventClassSystem; + switchEventTypes[1].eventKind = kEventSystemUserSessionActivated; + EventHandlerUPP switchEventHandler = + NewEventHandlerUPP(userSwitchCallback); + InstallApplicationEventHandler(switchEventHandler, 2, switchEventTypes, + this, &m_switchEventHandlerRef); + DisposeEventHandlerUPP(switchEventHandler); + + // watch for requests to sleep + EVENTQUEUE->adoptHandler(COSXScreen::getConfirmSleepEvent(), + getEventTarget(), + new TMethodEventJob(this, + &COSXScreen::handleConfirmSleep)); + + // create thread for monitoring system power state. + LOG((CLOG_DEBUG "starting watchSystemPowerThread")); + m_pmWatchThread = new CThread(new TMethodJob + (this, &COSXScreen::watchSystemPowerThread)); } catch (...) { - DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, + EVENTQUEUE->removeHandler(COSXScreen::getConfirmSleepEvent(), + getEventTarget()); + if (m_switchEventHandlerRef != 0) { + RemoveEventHandler(m_switchEventHandlerRef); + } + if (m_displayManagerNotificationUPP != NULL) { + DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, NULL, &m_PSN, 0); + } + if (m_hiddenWindow) { ReleaseWindow(m_hiddenWindow); m_hiddenWindow = NULL; @@ -121,6 +192,33 @@ COSXScreen::~COSXScreen() EVENTQUEUE->adoptBuffer(NULL); EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget()); + if (m_pmWatchThread) { + // make sure the thread has setup the runloop. + { + CLock lock(m_pmMutex); + while (!(bool)*m_pmThreadReady) { + m_pmThreadReady->wait(); + } + } + + // now exit the thread's runloop and wait for it to exit + LOG((CLOG_DEBUG "stopping watchSystemPowerThread")); + CFRunLoopStop(m_pmRunloop); + m_pmWatchThread->wait(); + delete m_pmWatchThread; + m_pmWatchThread = NULL; + } + delete m_pmThreadReady; + delete m_pmMutex; + + EVENTQUEUE->removeHandler(COSXScreen::getConfirmSleepEvent(), + getEventTarget()); + + RemoveEventHandler(m_switchEventHandlerRef); + + DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, + NULL, &m_PSN, 0); + if (m_hiddenWindow) { ReleaseWindow(m_hiddenWindow); m_hiddenWindow = NULL; @@ -131,9 +229,6 @@ COSXScreen::~COSXScreen() m_userInputWindow = NULL; } - DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, - NULL, &m_PSN, 0); - delete m_keyState; delete m_screensaver; } @@ -194,6 +289,18 @@ COSXScreen::warpCursor(SInt32 x, SInt32 y) m_cursorPosValid = true; } +void +COSXScreen::fakeInputBegin() +{ + // FIXME -- not implemented +} + +void +COSXScreen::fakeInputEnd() +{ + // FIXME -- not implemented +} + SInt32 COSXScreen::getJumpZoneSize() const { @@ -203,8 +310,7 @@ COSXScreen::getJumpZoneSize() const bool COSXScreen::isAnyMouseButtonDown() const { - // FIXME - return false; + return (GetCurrentButtonState() != 0); } void @@ -214,9 +320,137 @@ COSXScreen::getCursorCenter(SInt32& x, SInt32& y) const y = m_yCenter; } -void -COSXScreen::postMouseEvent(const CGPoint & pos) const +UInt32 +COSXScreen::registerHotKey(KeyID key, KeyModifierMask mask) { + // get mac virtual key and modifier mask matching synergy key and mask + UInt32 macKey, macMask; + if (!m_keyState->mapSynergyHotKeyToMac(key, mask, macKey, macMask)) { + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + + // choose hotkey id + UInt32 id; + if (!m_oldHotKeyIDs.empty()) { + id = m_oldHotKeyIDs.back(); + m_oldHotKeyIDs.pop_back(); + } + else { + id = m_hotKeys.size() + 1; + } + + // if this hot key has modifiers only then we'll handle it specially + EventHotKeyRef ref = NULL; + bool okay; + if (key == kKeyNone) { + if (m_modifierHotKeys.count(mask) > 0) { + // already registered + okay = false; + } + else { + m_modifierHotKeys[mask] = id; + okay = true; + } + } + else { + EventHotKeyID hkid = { 'SNRG', (UInt32)id }; + OSStatus status = RegisterEventHotKey(macKey, macMask, hkid, + GetApplicationEventTarget(), 0, + &ref); + okay = (status == noErr); + m_hotKeyToIDMap[CHotKeyItem(macKey, macMask)] = id; + } + + if (!okay) { + m_oldHotKeyIDs.push_back(id); + m_hotKeyToIDMap.erase(CHotKeyItem(macKey, macMask)); + LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", CKeyMap::formatKey(key, mask).c_str(), key, mask)); + return 0; + } + + m_hotKeys.insert(std::make_pair(id, CHotKeyItem(ref, macKey, macMask))); + + LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", CKeyMap::formatKey(key, mask).c_str(), key, mask, id)); + return id; +} + +void +COSXScreen::unregisterHotKey(UInt32 id) +{ + // look up hotkey + HotKeyMap::iterator i = m_hotKeys.find(id); + if (i == m_hotKeys.end()) { + return; + } + + // unregister with OS + bool okay; + if (i->second.getRef() != NULL) { + okay = (UnregisterEventHotKey(i->second.getRef()) == noErr); + } + else { + okay = false; + // XXX -- this is inefficient + for (ModifierHotKeyMap::iterator j = m_modifierHotKeys.begin(); + j != m_modifierHotKeys.end(); ++j) { + if (j->second == id) { + m_modifierHotKeys.erase(j); + okay = true; + break; + } + } + } + if (!okay) { + LOG((CLOG_WARN "failed to unregister hotkey id=%d", id)); + } + else { + LOG((CLOG_DEBUG "unregistered hotkey id=%d", id)); + } + + // discard hot key from map and record old id for reuse + m_hotKeyToIDMap.erase(i->second); + m_hotKeys.erase(i); + m_oldHotKeyIDs.push_back(id); + if (m_activeModifierHotKey == id) { + m_activeModifierHotKey = 0; + m_activeModifierHotKeyMask = 0; + } +} + +void +COSXScreen::postMouseEvent(CGPoint& pos) const +{ + // check if cursor position is valid on the client display configuration + // stkamp@users.sourceforge.net + CGDisplayCount displayCount = 0; + CGGetDisplaysWithPoint(pos, 0, NULL, &displayCount); + if (displayCount == 0) { + // cursor position invalid - clamp to bounds of last valid display. + // find the last valid display using the last cursor position. + displayCount = 0; + CGDirectDisplayID displayID; + CGGetDisplaysWithPoint(CGPointMake(m_xCursor, m_yCursor), 1, + &displayID, &displayCount); + if (displayCount != 0) { + CGRect displayRect = CGDisplayBounds(displayID); + if (pos.x < displayRect.origin.x) { + pos.x = displayRect.origin.x; + } + else if (pos.x > displayRect.origin.x + + displayRect.size.width - 1) { + pos.x = displayRect.origin.x + displayRect.size.width - 1; + } + if (pos.y < displayRect.origin.y) { + pos.y = displayRect.origin.y; + } + else if (pos.y > displayRect.origin.y + + displayRect.size.height - 1) { + pos.y = displayRect.origin.y + displayRect.size.height - 1; + } + } + } + // synthesize event. CGPostMouseEvent is a particularly good // example of a bad API. we have to shadow the mouse state to // use this API and if we want to support more buttons we have @@ -271,8 +505,8 @@ COSXScreen::fakeMouseMove(SInt32 x, SInt32 y) const postMouseEvent(pos); // save new cursor position - m_xCursor = x; - m_yCursor = y; + m_xCursor = static_cast(pos.x); + m_yCursor = static_cast(pos.y); m_cursorPosValid = true; } @@ -290,8 +524,10 @@ COSXScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const // synthesize event CGPoint pos; - pos.x = oldPos.h + dx; - pos.y = oldPos.v + dy; + m_xCursor = static_cast(oldPos.h); + m_yCursor = static_cast(oldPos.v); + pos.x = oldPos.h + dx; + pos.y = oldPos.v + dy; postMouseEvent(pos); // we now assume we don't know the current cursor position @@ -299,51 +535,22 @@ COSXScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const } void -COSXScreen::fakeMouseWheel(SInt32 delta) const +COSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const { - // synergy uses a wheel step size of 120. the mac uses a step size of 1. - delta /= 120; - if (delta == 0) { - return; + if (xDelta != 0 || yDelta != 0) { + CGPostScrollWheelEvent(2, mapScrollWheelFromSynergy(yDelta), + -mapScrollWheelFromSynergy(xDelta)); } - - CFPropertyListRef pref = ::CFPreferencesCopyValue( - CFSTR("com.apple.scrollwheel.scaling") , - kCFPreferencesAnyApplication, - kCFPreferencesCurrentUser, - kCFPreferencesAnyHost); - - int32_t wheelIncr = 1; - - if (pref != NULL) { - CFTypeID id = CFGetTypeID(pref); - if (id == CFNumberGetTypeID()) { - CFNumberRef value = static_cast(pref); - - double scaling; - if (CFNumberGetValue(value, kCFNumberDoubleType, &scaling)) { - wheelIncr = (int32_t)(8 * scaling); - if (wheelIncr == 0) { - wheelIncr = 1; - } - } - } - CFRelease(pref); - } - - // note that we ignore the magnitude of the delta. i think this is to - // avoid local wheel acceleration. - if (delta < 0) { - wheelIncr = -wheelIncr; - } - - CGPostScrollWheelEvent(1, wheelIncr); } void COSXScreen::enable() { - // FIXME -- install clipboard snooper (if we need one) + // watch the clipboard + m_clipboardTimer = EVENTQUEUE->newTimer(1.0, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_clipboardTimer, + new TMethodEventJob(this, + &COSXScreen::handleClipboardCheck)); if (m_isPrimary) { // FIXME -- start watching jump zones @@ -362,8 +569,6 @@ COSXScreen::enable() // FIXME -- prepare to show cursor if it moves } - - updateKeys(); } void @@ -382,7 +587,16 @@ COSXScreen::disable() // FIXME -- allow system to enter power saving mode } - // FIXME -- uninstall clipboard snooper (if we needed one) + // disable drag handling + m_dragNumButtonsDown = 0; + enableDragTimer(false); + + // uninstall clipboard timer + if (m_clipboardTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_clipboardTimer); + EVENTQUEUE->deleteTimer(m_clipboardTimer); + m_clipboardTimer = NULL; + } m_isOnScreen = m_isPrimary; } @@ -397,7 +611,10 @@ COSXScreen::enter() SetMouseCoalescingEnabled(true, NULL); - CGSetLocalEventsSuppressionInterval(HUGE_VAL); + CGSetLocalEventsSuppressionInterval(0.0); + + // enable global hotkeys + setGlobalHotKeysEnabled(true); } else { // show cursor @@ -410,6 +627,16 @@ COSXScreen::enter() for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { m_buttons[i] = false; } + + // avoid suppression of local hardware events + // stkamp@users.sourceforge.net + CGSetLocalEventsFilterDuringSupressionState( + kCGEventFilterMaskPermitAllEvents, + kCGEventSupressionStateSupressionInterval); + CGSetLocalEventsFilterDuringSupressionState( + (kCGEventFilterMaskPermitLocalKeyboardEvents | + kCGEventFilterMaskPermitSystemDefinedEvents), + kCGEventSupressionStateRemoteMouseDrag); } // now on screen @@ -419,12 +646,7 @@ COSXScreen::enter() bool COSXScreen::leave() { - // FIXME -- choose keyboard layout if per-process and activate it here - if (m_isPrimary) { - // update key and button state - updateKeys(); - // warp to center warpCursor(m_xCenter, m_yCenter); @@ -440,6 +662,9 @@ COSXScreen::leave() // to send it over to other machines. So disable it. SetMouseCoalescingEnabled(false, NULL); CGSetLocalEventsSuppressionInterval(0.0001); + + // disable global hotkeys + setGlobalHotKeysEnabled(false); } else { // hide cursor @@ -467,10 +692,11 @@ bool COSXScreen::setClipboard(ClipboardID, const IClipboard* src) { COSXClipboard dst; - m_ownClipboard = true; if (src != NULL) { // save clipboard data - return CClipboard::copy(&dst, src); + if (!CClipboard::copy(&dst, src)) { + return false; + } } else { // assert clipboard ownership @@ -479,25 +705,27 @@ COSXScreen::setClipboard(ClipboardID, const IClipboard* src) } dst.empty(); dst.close(); - return true; } + checkClipboards(); + return true; } void COSXScreen::checkClipboards() { - if (m_ownClipboard && !COSXClipboard::isOwnedBySynergy()) { - static ScrapRef sScrapbook = NULL; - ScrapRef currentScrap; - GetCurrentScrap(¤tScrap); - - if (sScrapbook != currentScrap) { + // check if clipboard ownership changed + if (!COSXClipboard::isOwnedBySynergy()) { + if (m_ownClipboard) { + LOG((CLOG_DEBUG "clipboard changed: lost ownership")); m_ownClipboard = false; sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard); sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); - sScrapbook = currentScrap; } } + else if (!m_ownClipboard) { + LOG((CLOG_DEBUG "clipboard changed: synergy owned")); + m_ownClipboard = true; + } } void @@ -632,7 +860,8 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*) sizeof(axis), NULL, &axis); - if (axis == kEventMouseWheelAxisY) { + if (axis == kEventMouseWheelAxisX || + axis == kEventMouseWheelAxisY) { GetEventParameter(*carbonEvent, kEventParamMouseWheelDelta, typeLongInteger, @@ -640,10 +869,49 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*) sizeof(delta), NULL, &delta); - onMouseWheel(120 * (SInt32)delta); + if (axis == kEventMouseWheelAxisX) { + onMouseWheel(-mapScrollWheelToSynergy((SInt32)delta), 0); + } + else { + onMouseWheel(0, mapScrollWheelToSynergy((SInt32)delta)); + } } break; } + + case kSynergyEventMouseScroll: + { + OSStatus r; + long xScroll; + long yScroll; + + // get scroll amount + r = GetEventParameter(*carbonEvent, + kSynergyMouseScrollAxisX, + typeLongInteger, + NULL, + sizeof(xScroll), + NULL, + &xScroll); + if (r != noErr) { + xScroll = 0; + } + r = GetEventParameter(*carbonEvent, + kSynergyMouseScrollAxisY, + typeLongInteger, + NULL, + sizeof(yScroll), + NULL, + &yScroll); + if (r != noErr) { + yScroll = 0; + } + + if (xScroll != 0 || yScroll != 0) { + onMouseWheel(-mapScrollWheelToSynergy(xScroll), + mapScrollWheelToSynergy(yScroll)); + } + } } break; @@ -653,11 +921,15 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*) case kEventRawKeyDown: case kEventRawKeyRepeat: case kEventRawKeyModifiersChanged: -// case kEventHotKeyPressed: -// case kEventHotKeyReleased: onKey(*carbonEvent); break; + + case kEventHotKeyPressed: + case kEventHotKeyReleased: + onHotKey(*carbonEvent); + break; } + break; case kEventClassWindow: @@ -681,7 +953,8 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*) } break; - default: + default: + SendEventToEventTarget(*carbonEvent, GetEventDispatcherTarget()); break; } } @@ -734,7 +1007,7 @@ COSXScreen::onMouseMove(SInt32 mx, SInt32 my) } bool -COSXScreen::onMouseButton(bool pressed, UInt16 macButton) const +COSXScreen::onMouseButton(bool pressed, UInt16 macButton) { // Buttons 2 and 3 are inverted on the mac ButtonID button = mapMacButtonToSynergy(macButton); @@ -742,27 +1015,51 @@ COSXScreen::onMouseButton(bool pressed, UInt16 macButton) const if (pressed) { LOG((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { - sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); + KeyModifierMask mask = m_keyState->getActiveModifiers(); + sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button, mask)); } } else { LOG((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { - sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); + KeyModifierMask mask = m_keyState->getActiveModifiers(); + sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button, mask)); } } - + + // handle drags with any button other than button 1 or 2 + if (macButton > 2) { + if (pressed) { + // one more button + if (m_dragNumButtonsDown++ == 0) { + enableDragTimer(true); + } + } + else { + // one less button + if (--m_dragNumButtonsDown == 0) { + enableDragTimer(false); + } + } + } + return true; } bool -COSXScreen::onMouseWheel(SInt32 delta) const +COSXScreen::onMouseWheel(SInt32 xDelta, SInt32 yDelta) const { - LOG((CLOG_DEBUG1 "event: button wheel delta=%d", delta)); - sendEvent(getWheelEvent(), CWheelInfo::alloc(delta)); + LOG((CLOG_DEBUG1 "event: button wheel delta=%+d,%+d", xDelta, yDelta)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(xDelta, yDelta)); return true; } +void +COSXScreen::handleClipboardCheck(const CEvent&, void*) +{ + checkClipboards(); +} + pascal void COSXScreen::displayManagerCallback(void* inUserData, SInt16 inMessage, void*) { @@ -798,16 +1095,17 @@ COSXScreen::onDisplayChange() return true; } - -bool -COSXScreen::onKey(EventRef event) const +bool +COSXScreen::onKey(EventRef event) { UInt32 eventKind = GetEventKind(event); - // get the key - UInt32 virtualKey; + // get the key and active modifiers + UInt32 virtualKey, macMask; GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(virtualKey), NULL, &virtualKey); + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, + NULL, sizeof(macMask), NULL, &macMask); LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey)); // sadly, OS X doesn't report the virtualKey for modifier keys. @@ -816,11 +1114,67 @@ COSXScreen::onKey(EventRef event) const if (virtualKey == 0 && eventKind == kEventRawKeyModifiersChanged) { // get old and new modifier state KeyModifierMask oldMask = getActiveModifiers(); - KeyModifierMask newMask = mapMacModifiersToSynergy(event); + KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask); m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask); + + // if the current set of modifiers exactly matches a modifiers-only + // hot key then generate a hot key down event. + if (m_activeModifierHotKey == 0) { + if (m_modifierHotKeys.count(newMask) > 0) { + m_activeModifierHotKey = m_modifierHotKeys[newMask]; + m_activeModifierHotKeyMask = newMask; + EVENTQUEUE->addEvent(CEvent(getHotKeyDownEvent(), + getEventTarget(), + CHotKeyInfo::alloc(m_activeModifierHotKey))); + } + } + + // if a modifiers-only hot key is active and should no longer be + // then generate a hot key up event. + else if (m_activeModifierHotKey != 0) { + KeyModifierMask mask = (newMask & m_activeModifierHotKeyMask); + if (mask != m_activeModifierHotKeyMask) { + EVENTQUEUE->addEvent(CEvent(getHotKeyUpEvent(), + getEventTarget(), + CHotKeyInfo::alloc(m_activeModifierHotKey))); + m_activeModifierHotKey = 0; + m_activeModifierHotKeyMask = 0; + } + } + return true; } + // check for hot key. when we're on a secondary screen we disable + // all hotkeys so we can capture the OS defined hot keys as regular + // keystrokes but that means we don't get our own hot keys either. + // so we check for a key/modifier match in our hot key map. + if (!m_isOnScreen) { + HotKeyToIDMap::const_iterator i = + m_hotKeyToIDMap.find(CHotKeyItem(virtualKey, macMask & 0xff00u)); + if (i != m_hotKeyToIDMap.end()) { + UInt32 id = i->second; + + // determine event type + CEvent::Type type; + UInt32 eventKind = GetEventKind(event); + if (eventKind == kEventRawKeyDown) { + type = getHotKeyDownEvent(); + } + else if (eventKind == kEventRawKeyUp) { + type = getHotKeyUpEvent(); + } + else { + return false; + } + + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), + CHotKeyInfo::alloc(id))); + + return true; + } + } + // decode event type bool down = (eventKind == kEventRawKeyDown); bool up = (eventKind == kEventRawKeyUp); @@ -834,28 +1188,65 @@ COSXScreen::onKey(EventRef event) const return false; } + // check for AltGr in mask. if set we send neither the AltGr nor + // the super modifiers to clients then remove AltGr before passing + // the modifiers to onKey. + KeyModifierMask sendMask = (mask & ~KeyModifierAltGr); + if ((mask & KeyModifierAltGr) != 0) { + sendMask &= ~KeyModifierSuper; + } + mask &= ~KeyModifierAltGr; + // update button state if (down) { - m_keyState->setKeyDown(button, true); + m_keyState->onKey(button, true, mask); } else if (up) { - if (!isKeyDown(button)) { + if (!m_keyState->isKeyDown(button)) { // up event for a dead key. throw it away. return false; } - m_keyState->setKeyDown(button, false); + m_keyState->onKey(button, false, mask); } // send key events for (COSXKeyState::CKeyIDs::const_iterator i = keys.begin(); i != keys.end(); ++i) { m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, - *i, mask, 1, button); + *i, sendMask, 1, button); } return true; } +bool +COSXScreen::onHotKey(EventRef event) const +{ + // get the hotkey id + EventHotKeyID hkid; + GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, + NULL, sizeof(EventHotKeyID), NULL, &hkid); + UInt32 id = hkid.id; + + // determine event type + CEvent::Type type; + UInt32 eventKind = GetEventKind(event); + if (eventKind == kEventHotKeyPressed) { + type = getHotKeyDownEvent(); + } + else if (eventKind == kEventHotKeyReleased) { + type = getHotKeyUpEvent(); + } + else { + return false; + } + + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), + CHotKeyInfo::alloc(id))); + + return true; +} + ButtonID COSXScreen::mapMacButtonToSynergy(UInt16 macButton) const { @@ -870,51 +1261,93 @@ COSXScreen::mapMacButtonToSynergy(UInt16 macButton) const return kButtonMiddle; } - return kButtonNone; + return static_cast(macButton); } -KeyModifierMask -COSXScreen::mapMacModifiersToSynergy(EventRef event) const +SInt32 +COSXScreen::mapScrollWheelToSynergy(SInt32 x) const { - // get native bit mask - UInt32 macMask; - GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, - NULL, sizeof(macMask), NULL, &macMask); + // return accelerated scrolling but not exponentially scaled as it is + // on the mac. + double d = (1.0 + getScrollSpeed()) * x / getScrollSpeedFactor(); + return static_cast(120.0 * d); +} - // convert - KeyModifierMask outMask = 0; - if ((macMask & shiftKey) != 0) { - outMask |= KeyModifierShift; - } - if ((macMask & rightShiftKey) != 0) { - outMask |= KeyModifierShift; - } - if ((macMask & controlKey) != 0) { - outMask |= KeyModifierControl; - } - if ((macMask & rightControlKey) != 0) { - outMask |= KeyModifierControl; - } - if ((macMask & cmdKey) != 0) { - outMask |= KeyModifierAlt; - } - if ((macMask & optionKey) != 0) { - outMask |= KeyModifierSuper; - } - if ((macMask & rightOptionKey) != 0) { - outMask |= KeyModifierSuper; - } - if ((macMask & alphaLock) != 0) { - outMask |= KeyModifierCapsLock; +SInt32 +COSXScreen::mapScrollWheelFromSynergy(SInt32 x) const +{ + // use server's acceleration with a little boost since other platforms + // take one wheel step as a larger step than the mac does. + return static_cast(3.0 * x / 120.0); +} + +double +COSXScreen::getScrollSpeed() const +{ + double scaling = 0.0; + + CFPropertyListRef pref = ::CFPreferencesCopyValue( + CFSTR("com.apple.scrollwheel.scaling") , + kCFPreferencesAnyApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + if (pref != NULL) { + CFTypeID id = CFGetTypeID(pref); + if (id == CFNumberGetTypeID()) { + CFNumberRef value = static_cast(pref); + if (CFNumberGetValue(value, kCFNumberDoubleType, &scaling)) { + if (scaling < 0.0) { + scaling = 0.0; + } + } + } + CFRelease(pref); } - return outMask; + return scaling; +} + +double +COSXScreen::getScrollSpeedFactor() const +{ + return pow(10.0, getScrollSpeed()); +} + +void +COSXScreen::enableDragTimer(bool enable) +{ + if (enable && m_dragTimer == NULL) { + m_dragTimer = EVENTQUEUE->newTimer(0.01, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_dragTimer, + new TMethodEventJob(this, + &COSXScreen::handleDrag)); + GetMouse(&m_dragLastPoint); + } + else if (!enable && m_dragTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_dragTimer); + EVENTQUEUE->deleteTimer(m_dragTimer); + m_dragTimer = NULL; + } +} + +void +COSXScreen::handleDrag(const CEvent&, void*) +{ + Point p; + GetMouse(&p); + if (p.h != m_dragLastPoint.h || p.v != m_dragLastPoint.v) { + m_dragLastPoint = p; + onMouseMove((SInt32)p.h, (SInt32)p.v); + } } void COSXScreen::updateButtons() { - // FIXME -- get current button state into m_buttons[] + UInt32 buttons = GetCurrentButtonState(); + for (size_t i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { + m_buttons[i] = ((buttons & (1u << i)) != 0); + } } IKeyState* @@ -977,3 +1410,290 @@ COSXScreen::updateScreenShape() LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d on %u %s", m_x, m_y, m_w, m_h, displayCount, (displayCount == 1) ? "display" : "displays")); } + +#pragma mark - + +// +// FAST USER SWITCH NOTIFICATION SUPPORT +// +// COSXScreen::userSwitchCallback(void*) +// +// gets called if a fast user switch occurs +// + +pascal OSStatus +COSXScreen::userSwitchCallback(EventHandlerCallRef nextHandler, + EventRef theEvent, + void* inUserData) +{ + COSXScreen* screen = (COSXScreen*)inUserData; + UInt32 kind = GetEventKind(theEvent); + + if (kind == kEventSystemUserSessionDeactivated) { + LOG((CLOG_DEBUG "user session deactivated")); + EVENTQUEUE->addEvent(CEvent(IScreen::getSuspendEvent(), + screen->getEventTarget())); + } + else if (kind == kEventSystemUserSessionActivated) { + LOG((CLOG_DEBUG "user session activated")); + EVENTQUEUE->addEvent(CEvent(IScreen::getResumeEvent(), + screen->getEventTarget())); + } + return (CallNextEventHandler(nextHandler, theEvent)); +} + +#pragma mark - + +// +// SLEEP/WAKEUP NOTIFICATION SUPPORT +// +// COSXScreen::watchSystemPowerThread(void*) +// +// main of thread monitoring system power (sleep/wakup) using a CFRunLoop +// + +void +COSXScreen::watchSystemPowerThread(void*) +{ + io_object_t notifier; + IONotificationPortRef notificationPortRef; + CFRunLoopSourceRef runloopSourceRef = 0; + + m_pmRunloop = CFRunLoopGetCurrent(); + + // install system power change callback + m_pmRootPort = IORegisterForSystemPower(this, ¬ificationPortRef, + powerChangeCallback, ¬ifier); + if (m_pmRootPort == 0) { + LOG((CLOG_WARN "IORegisterForSystemPower failed")); + } + else { + runloopSourceRef = + IONotificationPortGetRunLoopSource(notificationPortRef); + CFRunLoopAddSource(m_pmRunloop, runloopSourceRef, + kCFRunLoopCommonModes); + } + + // thread is ready + { + CLock lock(m_pmMutex); + *m_pmThreadReady = true; + m_pmThreadReady->signal(); + } + + // if we were unable to initialize then exit. we must do this after + // setting m_pmThreadReady to true otherwise the parent thread will + // block waiting for it. + if (m_pmRootPort == 0) { + return; + } + + // start the run loop + LOG((CLOG_DEBUG "started watchSystemPowerThread")); + CFRunLoopRun(); + + // cleanup + if (notificationPortRef) { + CFRunLoopRemoveSource(m_pmRunloop, + runloopSourceRef, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(runloopSourceRef); + CFRelease(runloopSourceRef); + } + + CLock lock(m_pmMutex); + IODeregisterForSystemPower(¬ifier); + m_pmRootPort = 0; + LOG((CLOG_DEBUG "stopped watchSystemPowerThread")); +} + +void +COSXScreen::powerChangeCallback(void* refcon, io_service_t service, + natural_t messageType, void* messageArg) +{ + ((COSXScreen*)refcon)->handlePowerChangeRequest(messageType, messageArg); +} + +void +COSXScreen::handlePowerChangeRequest(natural_t messageType, void* messageArg) +{ + // we've received a power change notification + switch (messageType) { + case kIOMessageSystemWillSleep: + // COSXScreen has to handle this in the main thread so we have to + // queue a confirm sleep event here. we actually don't allow the + // system to sleep until the event is handled. + EVENTQUEUE->addEvent(CEvent(COSXScreen::getConfirmSleepEvent(), + getEventTarget(), messageArg, + CEvent::kDontFreeData)); + return; + + case kIOMessageSystemHasPoweredOn: + LOG((CLOG_DEBUG "system wakeup")); + EVENTQUEUE->addEvent(CEvent(IScreen::getResumeEvent(), + getEventTarget())); + break; + + default: + break; + } + + CLock lock(m_pmMutex); + if (m_pmRootPort != 0) { + IOAllowPowerChange(m_pmRootPort, (long)messageArg); + } +} + +CEvent::Type +COSXScreen::getConfirmSleepEvent() +{ + return CEvent::registerTypeOnce(s_confirmSleepEvent, + "COSXScreen::confirmSleep"); +} + +void +COSXScreen::handleConfirmSleep(const CEvent& event, void*) +{ + long messageArg = (long)event.getData(); + if (messageArg != 0) { + CLock lock(m_pmMutex); + if (m_pmRootPort != 0) { + // deliver suspend event immediately. + EVENTQUEUE->addEvent(CEvent(IScreen::getSuspendEvent(), + getEventTarget(), NULL, + CEvent::kDeliverImmediately)); + + LOG((CLOG_DEBUG "system will sleep")); + IOAllowPowerChange(m_pmRootPort, messageArg); + } + } +} + +#pragma mark - + +// +// GLOBAL HOTKEY OPERATING MODE SUPPORT (10.3) +// +// CoreGraphics private API (OSX 10.3) +// Source: http://ichiro.nnip.org/osx/Cocoa/GlobalHotkey.html +// +// We load the functions dynamically because they're not available in +// older SDKs. We don't use weak linking because we want users of +// older SDKs to build an app that works on newer systems and older +// SDKs will not provide the symbols. +// + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int CGSConnection; +typedef enum { + CGSGlobalHotKeyEnable = 0, + CGSGlobalHotKeyDisable = 1, +} CGSGlobalHotKeyOperatingMode; + +extern CGSConnection _CGSDefaultConnection(void) WEAK_IMPORT_ATTRIBUTE; +extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode *mode) WEAK_IMPORT_ATTRIBUTE; +extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode) WEAK_IMPORT_ATTRIBUTE; + +typedef CGSConnection (*_CGSDefaultConnection_t)(void); +typedef CGError (*CGSGetGlobalHotKeyOperatingMode_t)(CGSConnection connection, CGSGlobalHotKeyOperatingMode *mode); +typedef CGError (*CGSSetGlobalHotKeyOperatingMode_t)(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode); + +static _CGSDefaultConnection_t s__CGSDefaultConnection; +static CGSGetGlobalHotKeyOperatingMode_t s_CGSGetGlobalHotKeyOperatingMode; +static CGSSetGlobalHotKeyOperatingMode_t s_CGSSetGlobalHotKeyOperatingMode; + +#ifdef __cplusplus +} +#endif + +#define LOOKUP(name_) \ + s_ ## name_ = NULL; \ + if (NSIsSymbolNameDefinedWithHint("_" #name_, "CoreGraphics")) { \ + s_ ## name_ = (name_ ## _t)NSAddressOfSymbol( \ + NSLookupAndBindSymbolWithHint( \ + "_" #name_, "CoreGraphics")); \ + } + +bool +COSXScreen::isGlobalHotKeyOperatingModeAvailable() +{ + if (!s_testedForGHOM) { + s_testedForGHOM = true; + LOOKUP(_CGSDefaultConnection); + LOOKUP(CGSGetGlobalHotKeyOperatingMode); + LOOKUP(CGSSetGlobalHotKeyOperatingMode); + s_hasGHOM = (s__CGSDefaultConnection != NULL && + s_CGSGetGlobalHotKeyOperatingMode != NULL && + s_CGSSetGlobalHotKeyOperatingMode != NULL); + } + return s_hasGHOM; +} + +void +COSXScreen::setGlobalHotKeysEnabled(bool enabled) +{ + if (isGlobalHotKeyOperatingModeAvailable()) { + CGSConnection conn = s__CGSDefaultConnection(); + + CGSGlobalHotKeyOperatingMode mode; + s_CGSGetGlobalHotKeyOperatingMode(conn, &mode); + + if (enabled && mode == CGSGlobalHotKeyDisable) { + s_CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyEnable); + } + else if (!enabled && mode == CGSGlobalHotKeyEnable) { + s_CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyDisable); + } + } +} + +bool +COSXScreen::getGlobalHotKeysEnabled() +{ + CGSGlobalHotKeyOperatingMode mode; + if (isGlobalHotKeyOperatingModeAvailable()) { + CGSConnection conn = s__CGSDefaultConnection(); + s_CGSGetGlobalHotKeyOperatingMode(conn, &mode); + } + else { + mode = CGSGlobalHotKeyEnable; + } + return (mode == CGSGlobalHotKeyEnable); +} + + +// +// COSXScreen::CHotKeyItem +// + +COSXScreen::CHotKeyItem::CHotKeyItem(UInt32 keycode, UInt32 mask) : + m_ref(NULL), + m_keycode(keycode), + m_mask(mask) +{ + // do nothing +} + +COSXScreen::CHotKeyItem::CHotKeyItem(EventHotKeyRef ref, + UInt32 keycode, UInt32 mask) : + m_ref(ref), + m_keycode(keycode), + m_mask(mask) +{ + // do nothing +} + +EventHotKeyRef +COSXScreen::CHotKeyItem::getRef() const +{ + return m_ref; +} + +bool +COSXScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const +{ + return (m_keycode < x.m_keycode || + (m_keycode == x.m_keycode && m_mask < x.m_mask)); +} diff --git a/lib/platform/COSXScreen.h b/lib/platform/COSXScreen.h index 292c1a40..f280d802 100644 --- a/lib/platform/COSXScreen.h +++ b/lib/platform/COSXScreen.h @@ -16,9 +16,21 @@ #define COSXSCREEN_H #include "CPlatformScreen.h" +#include "stdmap.h" #include "stdvector.h" #include +#include +#include +#include +#include +#include + +template +class CCondVar; +class CEventQueueTimer; +class CMutex; +class CThread; class COSXKeyState; class COSXScreenSaver; @@ -38,6 +50,10 @@ public: // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); + virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask); + virtual void unregisterHotKey(UInt32 id); + virtual void fakeInputBegin(); + virtual void fakeInputEnd(); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; @@ -46,7 +62,7 @@ public: virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; - virtual void fakeMouseWheel(SInt32 delta) const; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; // IPlatformScreen overrides virtual void enable(); @@ -71,7 +87,7 @@ protected: private: void updateScreenShape(); - void postMouseEvent(const CGPoint &) const; + void postMouseEvent(CGPoint&) const; // convenience function to send events void sendEvent(CEvent::Type type, void* = NULL) const; @@ -82,25 +98,83 @@ private: // mouse button handler. pressed is true if this is a mousedown // event, false if it is a mouseup event. macButton is the index // of the button pressed using the mac button mapping. - bool onMouseButton(bool pressed, UInt16 macButton) const; - bool onMouseWheel(SInt32 delta) const; + bool onMouseButton(bool pressed, UInt16 macButton); + bool onMouseWheel(SInt32 xDelta, SInt32 yDelta) const; bool onDisplayChange(); - bool onKey(EventRef event) const; + bool onKey(EventRef event); + bool onHotKey(EventRef event) const; // map mac mouse button to synergy buttons ButtonID mapMacButtonToSynergy(UInt16) const; - // map mac modifier mask to synergy modifier mask - KeyModifierMask mapMacModifiersToSynergy(EventRef event) const; - - /// Resolution switch callback + // map mac scroll wheel value to a synergy scroll wheel value + SInt32 mapScrollWheelToSynergy(SInt32) const; + + // map synergy scroll wheel value to a mac scroll wheel value + SInt32 mapScrollWheelFromSynergy(SInt32) const; + + // get the current scroll wheel speed + double getScrollSpeed() const; + + // get the current scroll wheel speed + double getScrollSpeedFactor() const; + + // enable/disable drag handling for buttons 3 and up + void enableDragTimer(bool enable); + + // drag timer handler + void handleDrag(const CEvent&, void*); + + // clipboard check timer handler + void handleClipboardCheck(const CEvent&, void*); + + // Resolution switch callback static pascal void displayManagerCallback(void* inUserData, SInt16 inMessage, void* inNotifyData); - + // fast user switch callback + static pascal OSStatus + userSwitchCallback(EventHandlerCallRef nextHandler, + EventRef theEvent, void* inUserData); + + // sleep / wakeup support + void watchSystemPowerThread(void*); + static void testCanceled(CFRunLoopTimerRef timer, void*info); + static void powerChangeCallback(void* refcon, io_service_t service, + natural_t messageType, void* messageArgument); + void handlePowerChangeRequest(natural_t messageType, + void* messageArgument); + + static CEvent::Type getConfirmSleepEvent(); + void handleConfirmSleep(const CEvent& event, void*); + + // global hotkey operating mode + static bool isGlobalHotKeyOperatingModeAvailable(); + static void setGlobalHotKeysEnabled(bool enabled); + static bool getGlobalHotKeysEnabled(); + private: + struct CHotKeyItem { + public: + CHotKeyItem(UInt32, UInt32); + CHotKeyItem(EventHotKeyRef, UInt32, UInt32); + + EventHotKeyRef getRef() const; + + bool operator<(const CHotKeyItem&) const; + + private: + EventHotKeyRef m_ref; + UInt32 m_keycode; + UInt32 m_mask; + }; + typedef std::map HotKeyMap; + typedef std::vector HotKeyIDList; + typedef std::map ModifierHotKeyMap; + typedef std::map HotKeyToIDMap; + // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; @@ -120,6 +194,9 @@ private: mutable bool m_cursorPosValid; mutable boolean_t m_buttons[5]; bool m_cursorHidden; + SInt32 m_dragNumButtonsDown; + Point m_dragLastPoint; + CEventQueueTimer* m_dragTimer; // keyboard stuff COSXKeyState* m_keyState; @@ -133,17 +210,43 @@ private: // clipboard stuff bool m_ownClipboard; - + CEventQueueTimer* m_clipboardTimer; + // window object that gets user input events when the server // has focus. WindowRef m_hiddenWindow; // window object that gets user input events when the server // does not have focus. WindowRef m_userInputWindow; - + // display manager stuff (to get screen resolution switches). DMExtendedNotificationUPP m_displayManagerNotificationUPP; ProcessSerialNumber m_PSN; + + // fast user switching + EventHandlerRef m_switchEventHandlerRef; + + // sleep / wakeup + CMutex* m_pmMutex; + CThread* m_pmWatchThread; + CCondVar* m_pmThreadReady; + CFRunLoopRef m_pmRunloop; + io_connect_t m_pmRootPort; + + // hot key stuff + HotKeyMap m_hotKeys; + HotKeyIDList m_oldHotKeyIDs; + ModifierHotKeyMap m_modifierHotKeys; + UInt32 m_activeModifierHotKey; + KeyModifierMask m_activeModifierHotKeyMask; + HotKeyToIDMap m_hotKeyToIDMap; + + // global hotkey operating mode + static bool s_testedForGHOM; + static bool s_hasGHOM; + + // events + static CEvent::Type s_confirmSleepEvent; }; #endif diff --git a/lib/platform/COSXScreenSaver.cpp b/lib/platform/COSXScreenSaver.cpp index f67dc016..9cc6a678 100644 --- a/lib/platform/COSXScreenSaver.cpp +++ b/lib/platform/COSXScreenSaver.cpp @@ -12,45 +12,164 @@ * GNU General Public License for more details. */ -#include "COSXScreenSaver.h" -#include - -// FIXME -- implement this +#import "COSXScreenSaver.h" +#import "COSXScreenSaverUtil.h" +#import "CLog.h" +#import "IEventQueue.h" +#import "IPrimaryScreen.h" +#import // // COSXScreenSaver // -COSXScreenSaver::COSXScreenSaver() +COSXScreenSaver::COSXScreenSaver(void* eventTarget) : + m_eventTarget(eventTarget), + m_enabled(true) { + m_autoReleasePool = screenSaverUtilCreatePool(); + m_screenSaverController = screenSaverUtilCreateController(); + + // install launch/termination event handlers + EventTypeSpec launchEventTypes[2]; + launchEventTypes[0].eventClass = kEventClassApplication; + launchEventTypes[0].eventKind = kEventAppLaunched; + launchEventTypes[1].eventClass = kEventClassApplication; + launchEventTypes[1].eventKind = kEventAppTerminated; + + EventHandlerUPP launchTerminationEventHandler = + NewEventHandlerUPP(launchTerminationCallback); + InstallApplicationEventHandler(launchTerminationEventHandler, 2, + launchEventTypes, this, + &m_launchTerminationEventHandlerRef); + DisposeEventHandlerUPP(launchTerminationEventHandler); + + m_screenSaverPSN.highLongOfPSN = 0; + m_screenSaverPSN.lowLongOfPSN = 0; + + // test if screensaver is running and find process number + if (isActive()) { + ProcessInfoRec procInfo; + Str31 procName; // pascal string. first byte holds length. + memset(&procInfo, 0, sizeof(procInfo)); + procInfo.processName = procName; + procInfo.processInfoLength = sizeof(ProcessInfoRec); + + ProcessSerialNumber psn; + OSErr err = GetNextProcess(&psn); + while (err == 0) { + memset(procName, 0, sizeof(procName)); + err = GetProcessInformation(&psn, &procInfo); + if (err != 0) { + break; + } + if (strcmp("ScreenSaverEngine", (const char*)&procName[1]) == 0) { + m_screenSaverPSN = psn; + break; + } + err = GetNextProcess(&psn); + } + } } COSXScreenSaver::~COSXScreenSaver() { + RemoveEventHandler(m_launchTerminationEventHandlerRef); +// screenSaverUtilReleaseController(m_screenSaverController); + screenSaverUtilReleasePool(m_autoReleasePool); } void COSXScreenSaver::enable() { + m_enabled = true; + screenSaverUtilEnable(m_screenSaverController); } void COSXScreenSaver::disable() { + m_enabled = false; + screenSaverUtilDisable(m_screenSaverController); } void COSXScreenSaver::activate() { + screenSaverUtilActivate(m_screenSaverController); } void COSXScreenSaver::deactivate() { + screenSaverUtilDeactivate(m_screenSaverController, m_enabled); } bool COSXScreenSaver::isActive() const { - return false; + return (screenSaverUtilIsActive(m_screenSaverController) != 0); +} + +void +COSXScreenSaver::processLaunched(ProcessSerialNumber psn) +{ + CFStringRef processName; + OSStatus err = CopyProcessName(&psn, &processName); + + if (err == 0 && CFEqual(CFSTR("ScreenSaverEngine"), processName)) { + m_screenSaverPSN = psn; + LOG((CLOG_DEBUG1 "ScreenSaverEngine launched. Enabled=%d", m_enabled)); + if (m_enabled) { + EVENTQUEUE->addEvent( + CEvent(IPrimaryScreen::getScreensaverActivatedEvent(), + m_eventTarget)); + } + } +} + +void +COSXScreenSaver::processTerminated(ProcessSerialNumber psn) +{ + if (m_screenSaverPSN.highLongOfPSN == psn.highLongOfPSN && + m_screenSaverPSN.lowLongOfPSN == psn.lowLongOfPSN) { + LOG((CLOG_DEBUG1 "ScreenSaverEngine terminated. Enabled=%d", m_enabled)); + if (m_enabled) { + EVENTQUEUE->addEvent( + CEvent(IPrimaryScreen::getScreensaverDeactivatedEvent(), + m_eventTarget)); + } + + m_screenSaverPSN.highLongOfPSN = 0; + m_screenSaverPSN.lowLongOfPSN = 0; + } +} + +pascal OSStatus +COSXScreenSaver::launchTerminationCallback( + EventHandlerCallRef nextHandler, + EventRef theEvent, void* userData) +{ + OSStatus result; + ProcessSerialNumber psn; + EventParamType actualType; + UInt32 actualSize; + + result = GetEventParameter(theEvent, kEventParamProcessID, + typeProcessSerialNumber, &actualType, + sizeof(psn), &actualSize, &psn); + + if ((result == noErr) && + (actualSize > 0) && + (actualType == typeProcessSerialNumber)) { + COSXScreenSaver* screenSaver = (COSXScreenSaver*)userData; + UInt32 eventKind = GetEventKind(theEvent); + if (eventKind == kEventAppLaunched) { + screenSaver->processLaunched(psn); + } + else if (eventKind == kEventAppTerminated) { + screenSaver->processTerminated(psn); + } + } + return (CallNextEventHandler(nextHandler, theEvent)); } diff --git a/lib/platform/COSXScreenSaver.h b/lib/platform/COSXScreenSaver.h index a40caefa..40c4e742 100644 --- a/lib/platform/COSXScreenSaver.h +++ b/lib/platform/COSXScreenSaver.h @@ -16,11 +16,12 @@ #define COSXSCREENSAVER_H #include "IScreenSaver.h" +#include //! OSX screen saver implementation class COSXScreenSaver : public IScreenSaver { public: - COSXScreenSaver(); + COSXScreenSaver(void* eventTarget); virtual ~COSXScreenSaver(); // IScreenSaver overrides @@ -29,6 +30,25 @@ public: virtual void activate(); virtual void deactivate(); virtual bool isActive() const; + +private: + void processLaunched(ProcessSerialNumber psn); + void processTerminated(ProcessSerialNumber psn); + + static pascal OSStatus + launchTerminationCallback( + EventHandlerCallRef nextHandler, + EventRef theEvent, void* userData); + +private: + // the target for the events we generate + void* m_eventTarget; + + bool m_enabled; + void* m_screenSaverController; + void* m_autoReleasePool; + EventHandlerRef m_launchTerminationEventHandlerRef; + ProcessSerialNumber m_screenSaverPSN; }; #endif diff --git a/lib/platform/COSXScreenSaverUtil.h b/lib/platform/COSXScreenSaverUtil.h new file mode 100644 index 00000000..d4124ab3 --- /dev/null +++ b/lib/platform/COSXScreenSaverUtil.h @@ -0,0 +1,39 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef COSXSCREENSAVERUTIL_H +#define COSXSCREENSAVERUTIL_H + +#include "common.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +void* screenSaverUtilCreatePool(); +void screenSaverUtilReleasePool(void*); + +void* screenSaverUtilCreateController(); +void screenSaverUtilReleaseController(void*); +void screenSaverUtilEnable(void*); +void screenSaverUtilDisable(void*); +void screenSaverUtilActivate(void*); +void screenSaverUtilDeactivate(void*, int isEnabled); +int screenSaverUtilIsActive(void*); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/lib/platform/COSXScreenSaverUtil.m b/lib/platform/COSXScreenSaverUtil.m new file mode 100644 index 00000000..1376d1f2 --- /dev/null +++ b/lib/platform/COSXScreenSaverUtil.m @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#import "COSXScreenSaverUtil.h" +#import "OSXScreenSaverControl.h" +#import + +// +// screenSaverUtil functions +// +// Note: these helper functions exist only so we can avoid using ObjC++. +// autoconf/automake don't know about ObjC++ and I don't know how to +// teach them about it. +// + +void* +screenSaverUtilCreatePool() +{ + return [[NSAutoreleasePool alloc] init]; +} + +void +screenSaverUtilReleasePool(void* pool) +{ + [(NSAutoreleasePool*)pool release]; +} + +void* +screenSaverUtilCreateController() +{ + return [[ScreenSaverController controller] retain]; +} + +void +screenSaverUtilReleaseController(void* controller) +{ + [(ScreenSaverController*)controller release]; +} + +void +screenSaverUtilEnable(void* controller) +{ + [(ScreenSaverController*)controller setScreenSaverCanRun:YES]; +} + +void +screenSaverUtilDisable(void* controller) +{ + [(ScreenSaverController*)controller setScreenSaverCanRun:NO]; +} + +void +screenSaverUtilActivate(void* controller) +{ + [(ScreenSaverController*)controller setScreenSaverCanRun:YES]; + [(ScreenSaverController*)controller screenSaverStartNow]; +} + +void +screenSaverUtilDeactivate(void* controller, int isEnabled) +{ + [(ScreenSaverController*)controller screenSaverStopNow]; + [(ScreenSaverController*)controller setScreenSaverCanRun:isEnabled]; +} + +int +screenSaverUtilIsActive(void* controller) +{ + return [(ScreenSaverController*)controller screenSaverIsRunning]; +} diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 279da06d..852e8bbc 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -59,8 +59,8 @@ typedef struct tagMOUSEHOOKSTRUCTWin2000 { #define WM_NCXBUTTONDOWN 0x00AB #define WM_NCXBUTTONUP 0x00AC #define WM_NCXBUTTONDBLCLK 0x00AD -#define MOUSEEVENTF_XDOWN 0x0100 -#define MOUSEEVENTF_XUP 0x0200 +#define MOUSEEVENTF_XDOWN 0x0080 +#define MOUSEEVENTF_XUP 0x0100 #define XBUTTON1 0x0001 #define XBUTTON2 0x0002 #endif @@ -96,10 +96,10 @@ static SInt32 g_wScreen = 0; static SInt32 g_hScreen = 0; static WPARAM g_deadVirtKey = 0; static LPARAM g_deadLParam = 0; -static WPARAM g_oldDeadVirtKey = 0; static BYTE g_deadKeyState[256] = { 0 }; static DWORD g_hookThread = 0; static DWORD g_attachedThread = 0; +static bool g_fakeInput = false; #if defined(_MSC_VER) #pragma data_seg() @@ -161,192 +161,213 @@ attachThreadToForeground() #if !NO_GRAB_KEYBOARD static WPARAM -makeKeyMsg(UINT virtKey, char c) +makeKeyMsg(UINT virtKey, char c, bool noAltGr) { - return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), 0); + return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), noAltGr ? 1 : 0); } static void keyboardGetState(BYTE keys[256]) { - if (g_hookThread != 0) { - GetKeyboardState(keys); - } - else { - SHORT key; - for (int i = 0; i < 256; ++i) { - key = GetAsyncKeyState(i); - keys[i] = (BYTE)((key < 0) ? 0x80u : 0); - } - key = GetKeyState(VK_CAPITAL); - keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1)); + // we have to use GetAsyncKeyState() rather than GetKeyState() because + // we don't pass through most keys so the event synchronous state + // doesn't get updated. we do that because certain modifier keys have + // side effects, like alt and the windows key. + SHORT key; + for (int i = 0; i < 256; ++i) { + key = GetAsyncKeyState(i); + keys[i] = (BYTE)((key < 0) ? 0x80u : 0); } + key = GetKeyState(VK_CAPITAL); + keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1)); } static bool doKeyboardHookHandler(WPARAM wParam, LPARAM lParam) { - // check for dead keys. we don't forward those to our window. - // instead we'll leave the key in the keyboard layout (a buffer - // internal to the system) for translation when the next key is - // pressed. note that some systems set bit 31 to indicate a - // dead key and others bit 15. nice. - UINT c = MapVirtualKey(wParam, 2); - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x00000000, c); - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | (c << 8) | 0x01000000, lParam); - if ((c & 0x80008000u) != 0) { - if ((lParam & 0x80000000u) == 0) { - if (g_deadVirtKey == 0) { - // dead key press, no dead key in the buffer - g_deadVirtKey = wParam; - g_deadLParam = lParam; - keyboardGetState(g_deadKeyState); - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x02000000, lParam); - return false; - } - // second dead key press in a row so let it pass - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x03000000, lParam); - } - else if (wParam == g_oldDeadVirtKey) { - // dead key release for second dead key in a row. discard - // because we've already handled it. also take it out of - // the keyboard buffer. - g_oldDeadVirtKey = 0; - WORD c; - UINT scanCode = ((lParam & 0x00ff0000u) >> 16); - ToAscii(wParam, scanCode, g_deadKeyState, &c, 0); - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x09000000, lParam); - return true; - } - else { - // dead key release - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x04000000, lParam); + // check for special events indicating if we should start or stop + // passing events through and not report them to the server. this + // is used to allow the server to synthesize events locally but + // not pick them up as user events. + if (wParam == SYNERGY_HOOK_FAKE_INPUT_VIRTUAL_KEY && + ((lParam >> 16) & 0xffu) == SYNERGY_HOOK_FAKE_INPUT_SCANCODE) { + // update flag + g_fakeInput = ((lParam & 0x80000000u) == 0); + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + 0xff000000u | wParam, lParam); + + // discard event + return true; + } + + // if we're expecting fake input then just pass the event through + // and do not forward to the server + if (g_fakeInput) { + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + 0xfe000000u | wParam, lParam); + return false; + } + + // VK_RSHIFT may be sent with an extended scan code but right shift + // is not an extended key so we reset that bit. + if (wParam == VK_RSHIFT) { + lParam &= ~0x01000000u; + } + + // tell server about event + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, wParam, lParam); + + // ignore dead key release + if (g_deadVirtKey == wParam && + (lParam & 0x80000000u) != 0) { + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | 0x04000000, lParam); + return false; + } + + // we need the keyboard state for ToAscii() + BYTE keys[256]; + keyboardGetState(keys); + + // ToAscii() maps ctrl+letter to the corresponding control code + // and ctrl+backspace to delete. we don't want those translations + // so clear the control modifier state. however, if we want to + // simulate AltGr (which is ctrl+alt) then we must not clear it. + UINT control = keys[VK_CONTROL] | keys[VK_LCONTROL] | keys[VK_RCONTROL]; + UINT menu = keys[VK_MENU] | keys[VK_LMENU] | keys[VK_RMENU]; + if ((control & 0x80) == 0 || (menu & 0x80) == 0) { + keys[VK_LCONTROL] = 0; + keys[VK_RCONTROL] = 0; + keys[VK_CONTROL] = 0; + } + else { + keys[VK_LCONTROL] = 0x80; + keys[VK_RCONTROL] = 0x80; + keys[VK_CONTROL] = 0x80; + keys[VK_LMENU] = 0x80; + keys[VK_RMENU] = 0x80; + keys[VK_MENU] = 0x80; + } + + // ToAscii() needs to know if a menu is active for some reason. + // we don't know and there doesn't appear to be any way to find + // out. so we'll just assume a menu is active if the menu key + // is down. + // FIXME -- figure out some way to check if a menu is active + UINT flags = 0; + if ((menu & 0x80) != 0) + flags |= 1; + + // if we're on the server screen then just pass numpad keys with alt + // key down as-is. we won't pick up the resulting character but the + // local app will. if on a client screen then grab keys as usual; + // if the client is a windows system it'll synthesize the expected + // character. if not then it'll probably just do nothing. + if (g_mode != kHOOK_RELAY_EVENTS) { + // we don't use virtual keys because we don't know what the + // state of the numlock key is. we'll hard code the scan codes + // instead. hopefully this works across all keyboards. + UINT sc = (lParam & 0x01ff0000u) >> 16; + if (menu && + (sc >= 0x47u && sc <= 0x52u && sc != 0x4au && sc != 0x4eu)) { return false; } } - // convert key to a character. this combines a saved dead key, - // if any, with this key. however, the dead key must remain in - // the keyboard layout for the application receiving this event - // so it can also convert the key to a character. we only do - // this on a key press. - WPARAM charAndVirtKey = (wParam & 0xffu); - if (c != 0) { - // we need the keyboard state for ToAscii() - BYTE keys[256]; - keyboardGetState(keys); - - // ToAscii() maps ctrl+letter to the corresponding control code - // and ctrl+backspace to delete. we don't want those translations - // so clear the control modifier state. however, if we want to - // simulate AltGr (which is ctrl+alt) then we must not clear it. - UINT control = keys[VK_CONTROL] | keys[VK_LCONTROL] | keys[VK_RCONTROL]; - UINT menu = keys[VK_MENU] | keys[VK_LMENU] | keys[VK_RMENU]; - if ((control & 0x80) == 0 || (menu & 0x80) == 0) { - keys[VK_LCONTROL] = 0; - keys[VK_RCONTROL] = 0; - keys[VK_CONTROL] = 0; - } - else { - keys[VK_LCONTROL] = 0x80; - keys[VK_RCONTROL] = 0x80; - keys[VK_CONTROL] = 0x80; - keys[VK_LMENU] = 0x80; - keys[VK_RMENU] = 0x80; - keys[VK_MENU] = 0x80; - } - - // ToAscii() needs to know if a menu is active for some reason. - // we don't know and there doesn't appear to be any way to find - // out. so we'll just assume a menu is active if the menu key - // is down. - // FIXME -- figure out some way to check if a menu is active - UINT flags = 0; - if ((menu & 0x80) != 0) - flags |= 1; - - // map the key event to a character. this has the side - // effect of removing the dead key from the system's keyboard - // layout buffer. - WORD c = 0; - UINT scanCode = ((lParam & 0x00ff0000u) >> 16); - int n = ToAscii(wParam, scanCode, keys, &c, flags); - - // if mapping failed and ctrl and alt are pressed then try again - // with both not pressed. this handles the case where ctrl and - // alt are being used as individual modifiers rather than AltGr. - // we have to put the dead key back first, if there was one. - if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) { - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x05000000, lParam); - if (g_deadVirtKey != 0) { - ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16, - g_deadKeyState, &c, flags); - } - keys[VK_LCONTROL] = 0; - keys[VK_RCONTROL] = 0; - keys[VK_CONTROL] = 0; - keys[VK_LMENU] = 0; - keys[VK_RMENU] = 0; - keys[VK_MENU] = 0; - n = ToAscii(wParam, scanCode, keys, &c, flags); - } + // map the key event to a character. we have to put the dead + // key back first and this has the side effect of removing it. + if (g_deadVirtKey != 0) { + WORD c = 0; + ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + g_deadKeyState, &c, flags); + } + WORD c = 0; + UINT scanCode = ((lParam & 0x10ff0000u) >> 16); + int n = ToAscii(wParam, scanCode, keys, &c, flags); + // if mapping failed and ctrl and alt are pressed then try again + // with both not pressed. this handles the case where ctrl and + // alt are being used as individual modifiers rather than AltGr. + // we note that's the case in the message sent back to synergy + // because there's no simple way to deduce it after the fact. + // we have to put the dead key back first, if there was one. + bool noAltGr = false; + if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) { + noAltGr = true; PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | (c << 8) | ((n & 0xff) << 16) | 0x06000000, + wParam | 0x05000000, lParam); + if (g_deadVirtKey != 0) { + ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + g_deadKeyState, &c, flags); + } + BYTE keys2[256]; + for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) { + keys2[i] = keys[i]; + } + keys2[VK_LCONTROL] = 0; + keys2[VK_RCONTROL] = 0; + keys2[VK_CONTROL] = 0; + keys2[VK_LMENU] = 0; + keys2[VK_RMENU] = 0; + keys2[VK_MENU] = 0; + n = ToAscii(wParam, scanCode, keys2, &c, flags); + } + + PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, + wParam | ((c & 0xff) << 8) | + ((n & 0xff) << 16) | 0x06000000, lParam); - switch (n) { - default: - // key is a dead key; we're not expecting this since we - // bailed out above for any dead key. - g_deadVirtKey = wParam; - g_deadLParam = lParam; - break; + WPARAM charAndVirtKey = 0; + bool clearDeadKey = false; + switch (n) { + default: + // key is a dead key + g_deadVirtKey = wParam; + g_deadLParam = lParam; + for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) { + g_deadKeyState[i] = keys[i]; + } + break; - case 0: - // key doesn't map to a character. this can happen if - // non-character keys are pressed after a dead key. - break; + case 0: + // key doesn't map to a character. this can happen if + // non-character keys are pressed after a dead key. + charAndVirtKey = makeKeyMsg(wParam, (char)0, noAltGr); + break; - case 1: - // key maps to a character composed with dead key - charAndVirtKey = makeKeyMsg(wParam, (char)LOBYTE(c)); - break; + case 1: + // key maps to a character composed with dead key + charAndVirtKey = makeKeyMsg(wParam, (char)LOBYTE(c), noAltGr); + clearDeadKey = true; + break; - case 2: { - // previous dead key not composed. send a fake key press - // and release for the dead key to our window. - WPARAM deadCharAndVirtKey = - makeKeyMsg(g_deadVirtKey, (char)LOBYTE(c)); - PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, + case 2: { + // previous dead key not composed. send a fake key press + // and release for the dead key to our window. + WPARAM deadCharAndVirtKey = + makeKeyMsg(g_deadVirtKey, (char)LOBYTE(c), noAltGr); + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, deadCharAndVirtKey, g_deadLParam & 0x7fffffffu); - PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, deadCharAndVirtKey, g_deadLParam | 0x80000000u); - // use uncomposed character - charAndVirtKey = makeKeyMsg(wParam, (char)HIBYTE(c)); - break; - } - } + // use uncomposed character + charAndVirtKey = makeKeyMsg(wParam, (char)HIBYTE(c), noAltGr); + clearDeadKey = true; + break; + } + } - // put back the dead key, if any, for the application to use - if (g_deadVirtKey != 0) { - ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16, + // put back the dead key, if any, for the application to use + if (g_deadVirtKey != 0) { + ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, g_deadKeyState, &c, flags); - for (int i = 0; i < 256; ++i) { - g_deadKeyState[i] = 0; - } - } + } - // clear out old dead key state + // clear out old dead key state + if (clearDeadKey) { g_deadVirtKey = 0; g_deadLParam = 0; } @@ -355,19 +376,12 @@ doKeyboardHookHandler(WPARAM wParam, LPARAM lParam) // forwarding events to clients because this'll keep our thread's // key state table up to date. that's important for querying // the scroll lock toggle state. - PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - charAndVirtKey | 0x07000000, lParam); - PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, charAndVirtKey, lParam); - - // send fake key release if the user just pressed two dead keys - // in a row, otherwise we'll lose the release because we always - // return from the top of this function for all dead key releases. - if ((c & 0x80008000u) != 0) { - g_oldDeadVirtKey = wParam; + // XXX -- with hot keys for actions we may only need to do this when + // forwarding. + if (charAndVirtKey != 0) { PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, - wParam | 0x08000000, lParam); - PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, - charAndVirtKey, lParam | 0x80000000u); + charAndVirtKey | 0x07000000, lParam); + PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, charAndVirtKey, lParam); } if (g_mode == kHOOK_RELAY_EVENTS) { @@ -381,32 +395,14 @@ doKeyboardHookHandler(WPARAM wParam, LPARAM lParam) // lights may not stay synchronized. break; - case VK_SHIFT: - case VK_LSHIFT: - case VK_RSHIFT: - // pass the shift modifiers. if we don't do this - // we may not get the right dead key when caps lock - // is on. for example, on the french layout (with - // english keycaps) on caps lock then press shift + [ - // and q. instead of an A with ^ above it you get an - // A with dots above it. - break; - - case VK_CONTROL: - case VK_LCONTROL: - case VK_RCONTROL: - case VK_MENU: - case VK_LMENU: - case VK_RMENU: case VK_HANGUL: - // pass the control and alt modifiers if using a low - // level hook, discard them if not. + // pass these modifiers if using a low level hook, discard + // them if not. if (g_hookThread == 0) { return true; } break; - default: // discard return true; @@ -531,7 +527,7 @@ static bool mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) { - attachThreadToForeground(); +// attachThreadToForeground(); return doMouseHookHandler(wParam, x, y, data); } @@ -804,7 +800,7 @@ init(DWORD threadID) // save thread id. we'll post messages to this thread's // message queue. - g_threadID = threadID; + g_threadID = threadID; // set defaults g_mode = kHOOK_DISABLE; @@ -847,6 +843,9 @@ install() g_deadVirtKey = 0; g_deadLParam = 0; + // reset fake input flag + g_fakeInput = false; + // check for mouse wheel support g_wheelSupport = getWheelSupport(); diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h index b0c8e5d3..e8661815 100644 --- a/lib/platform/CSynergyHook.h +++ b/lib/platform/CSynergyHook.h @@ -38,6 +38,9 @@ #define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP #define SYNERGY_HOOK_LAST_MSG SYNERGY_MSG_DEBUG +#define SYNERGY_HOOK_FAKE_INPUT_VIRTUAL_KEY VK_CANCEL +#define SYNERGY_HOOK_FAKE_INPUT_SCANCODE 0 + extern "C" { enum EHookResult { diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 5bbe98c2..c380c9cc 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -503,11 +503,10 @@ CXWindowsClipboard::icccmFillCache() (target != m_atomAtom && target != m_atomTargets)) { LOG((CLOG_DEBUG1 "selection doesn't support TARGETS")); data = ""; - - target = XA_STRING; - data.append(reinterpret_cast(&target), sizeof(target)); + CXWindowsUtil::appendAtomData(data, XA_STRING); } + CXWindowsUtil::convertAtomProperty(data); const Atom* targets = reinterpret_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); LOG((CLOG_DEBUG " available targets: %s", CXWindowsUtil::atomsToString(m_display, targets, numTargets).c_str())); @@ -525,12 +524,18 @@ CXWindowsClipboard::icccmFillCache() // see if atom is in target list Atom target = None; + // XXX -- just ask for the converter's target to see if it's + // available rather than checking TARGETS. i've seen clipboard + // owners that don't report all the targets they support. + target = converter->getAtom(); + /* for (UInt32 i = 0; i < numTargets; ++i) { if (converter->getAtom() == targets[i]) { target = targets[i]; break; } } + */ if (target == None) { continue; } @@ -661,7 +666,7 @@ CXWindowsClipboard::motifOwnsClipboard() const reinterpret_cast(data.data()); if (data.size() >= sizeof(CMotifClipHeader) && header->m_id == kMotifClipHeader) { - if (header->m_selectionOwner == owner) { + if (static_cast(header->m_selectionOwner) == owner) { return true; } } @@ -835,12 +840,12 @@ CXWindowsClipboard::insertMultipleReply(Window requestor, } // fail if the requested targets isn't of the correct form - if (format != 32 || - target != m_atomAtomPair) { + if (format != 32 || target != m_atomAtomPair) { return false; } // data is a list of atom pairs: target, property + CXWindowsUtil::convertAtomProperty(data); const Atom* targets = reinterpret_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); @@ -851,10 +856,7 @@ CXWindowsClipboard::insertMultipleReply(Window requestor, const Atom property = targets[i + 1]; if (!addSimpleRequest(requestor, target, time, property)) { // note that we can't perform the requested conversion - static const Atom none = None; - data.replace(i * sizeof(Atom), sizeof(Atom), - reinterpret_cast(&none), - sizeof(Atom)); + CXWindowsUtil::replaceAtomData(data, i, None); changed = true; } } @@ -900,16 +902,18 @@ CXWindowsClipboard::insertReply(CReply* reply) if (newWindow) { // note errors while we adjust event masks bool error = false; - CXWindowsUtil::CErrorLock lock(m_display, &error); + { + CXWindowsUtil::CErrorLock lock(m_display, &error); - // get and save the current event mask - XWindowAttributes attr; - XGetWindowAttributes(m_display, reply->m_requestor, &attr); - m_eventMasks[reply->m_requestor] = attr.your_event_mask; + // get and save the current event mask + XWindowAttributes attr; + XGetWindowAttributes(m_display, reply->m_requestor, &attr); + m_eventMasks[reply->m_requestor] = attr.your_event_mask; - // add the events we want - XSelectInput(m_display, reply->m_requestor, attr.your_event_mask | - StructureNotifyMask | PropertyChangeMask); + // add the events we want + XSelectInput(m_display, reply->m_requestor, attr.your_event_mask | + StructureNotifyMask | PropertyChangeMask); + } // if we failed then the window has already been destroyed if (error) { @@ -1193,13 +1197,9 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const assert(format != NULL); // add standard targets - Atom atom; - atom = m_atomTargets; - data.append(reinterpret_cast(&atom), sizeof(Atom)); - atom = m_atomMultiple; - data.append(reinterpret_cast(&atom), sizeof(Atom)); - atom = m_atomTimestamp; - data.append(reinterpret_cast(&atom), sizeof(Atom)); + CXWindowsUtil::appendAtomData(data, m_atomTargets); + CXWindowsUtil::appendAtomData(data, m_atomMultiple); + CXWindowsUtil::appendAtomData(data, m_atomTimestamp); // add targets we can convert to for (ConverterList::const_iterator index = m_converters.begin(); @@ -1208,8 +1208,7 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const // skip formats we don't have if (m_added[converter->getFormat()]) { - atom = converter->getAtom(); - data.append(reinterpret_cast(&atom), sizeof(Atom)); + CXWindowsUtil::appendAtomData(data, converter->getAtom()); } } @@ -1222,9 +1221,8 @@ CXWindowsClipboard::getTimestampData(CString& data, int* format) const { assert(format != NULL); - assert(sizeof(m_timeOwned) == 4); checkCache(); - data.append(reinterpret_cast(&m_timeOwned), 4); + CXWindowsUtil::appendTimeData(data, m_timeOwned); *format = 32; return m_atomInteger; } @@ -1264,6 +1262,9 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, LOG((CLOG_DEBUG1 "request selection=%s, target=%s, window=%x", CXWindowsUtil::atomToString(display, selection).c_str(), CXWindowsUtil::atomToString(display, target).c_str(), m_requestor)); + m_atomNone = XInternAtom(display, "NONE", False); + m_atomIncr = XInternAtom(display, "INCR", False); + // save output pointers m_actualTarget = actualTarget; m_data = data; @@ -1361,7 +1362,8 @@ CXWindowsClipboard::CICCCMGetClipboard::processEvent( case SelectionNotify: if (xevent->xselection.requestor == m_requestor) { // done if we can't convert - if (xevent->xselection.property == None) { + if (xevent->xselection.property == None || + xevent->xselection.property == m_atomNone) { m_done = true; return true; } @@ -1409,7 +1411,7 @@ CXWindowsClipboard::CICCCMGetClipboard::processEvent( // note if incremental. if we're already incremental then the // selection owner is busted. if the INCR property has no size // then the selection owner is busted. - if (target == XInternAtom(display, "INCR", False)) { + if (target == m_atomIncr) { if (m_incr) { m_failed = true; m_error = true; diff --git a/lib/platform/CXWindowsClipboard.h b/lib/platform/CXWindowsClipboard.h index 5fe126f8..ba3c7b62 100644 --- a/lib/platform/CXWindowsClipboard.h +++ b/lib/platform/CXWindowsClipboard.h @@ -155,6 +155,10 @@ private: bool m_failed; bool m_done; + // atoms needed for the protocol + Atom m_atomNone; // NONE, not None + Atom m_atomIncr; + // true iff we've received the selection notify bool m_reading; @@ -182,7 +186,7 @@ private: SInt32 m_pad2[4]; SInt32 m_numItems; SInt32 m_pad3[3]; - Window m_selectionOwner; + SInt32 m_selectionOwner; // a Window SInt32 m_pad4[2]; }; @@ -204,9 +208,9 @@ private: SInt32 m_pad1[6]; SInt32 m_length; SInt32 m_data; - Atom m_type; + SInt32 m_type; // an Atom SInt32 m_pad2[1]; - int m_deleted; + SInt32 m_deleted; SInt32 m_pad3[4]; }; diff --git a/lib/platform/CXWindowsKeyState.cpp b/lib/platform/CXWindowsKeyState.cpp index 8e303e2e..9f98ded4 100644 --- a/lib/platform/CXWindowsKeyState.cpp +++ b/lib/platform/CXWindowsKeyState.cpp @@ -16,6 +16,7 @@ #include "CXWindowsUtil.h" #include "CLog.h" #include "CStringUtil.h" +#include "stdmap.h" #if X_DISPLAY_MISSING # error X11 is required to build synergy #else @@ -24,156 +25,106 @@ # define XK_MISCELLANY # define XK_XKB_KEYS # include -// these should be in XF86keysym.h but there are several versions of -// that file floating around and not all have all symbols and none of -// them provide any form of versioning so we just define 'em here. -#define XF86XK_Standby 0x1008FF10 -#define XF86XK_AudioLowerVolume 0x1008FF11 -#define XF86XK_AudioMute 0x1008FF12 -#define XF86XK_AudioRaiseVolume 0x1008FF13 -#define XF86XK_AudioPlay 0x1008FF14 -#define XF86XK_AudioStop 0x1008FF15 -#define XF86XK_AudioPrev 0x1008FF16 -#define XF86XK_AudioNext 0x1008FF17 -#define XF86XK_HomePage 0x1008FF18 -#define XF86XK_Mail 0x1008FF19 -#define XF86XK_Start 0x1008FF1A -#define XF86XK_Search 0x1008FF1B -#define XF86XK_AudioRecord 0x1008FF1C -#define XF86XK_Calculator 0x1008FF1D -#define XF86XK_Memo 0x1008FF1E -#define XF86XK_ToDoList 0x1008FF1F -#define XF86XK_Calendar 0x1008FF20 -#define XF86XK_PowerDown 0x1008FF21 -#define XF86XK_ContrastAdjust 0x1008FF22 -#define XF86XK_RockerUp 0x1008FF23 -#define XF86XK_RockerDown 0x1008FF24 -#define XF86XK_RockerEnter 0x1008FF25 -#define XF86XK_Back 0x1008FF26 -#define XF86XK_Forward 0x1008FF27 -#define XF86XK_Stop 0x1008FF28 -#define XF86XK_Refresh 0x1008FF29 -#define XF86XK_PowerOff 0x1008FF2A -#define XF86XK_WakeUp 0x1008FF2B -#define XF86XK_Eject 0x1008FF2C -#define XF86XK_ScreenSaver 0x1008FF2D -#define XF86XK_WWW 0x1008FF2E -#define XF86XK_Sleep 0x1008FF2F -#define XF86XK_Favorites 0x1008FF30 -#define XF86XK_AudioPause 0x1008FF31 -#define XF86XK_AudioMedia 0x1008FF32 -#define XF86XK_MyComputer 0x1008FF33 -#define XF86XK_VendorHome 0x1008FF34 -#define XF86XK_LightBulb 0x1008FF35 -#define XF86XK_Shop 0x1008FF36 -#define XF86XK_History 0x1008FF37 -#define XF86XK_OpenURL 0x1008FF38 -#define XF86XK_AddFavorite 0x1008FF39 -#define XF86XK_HotLinks 0x1008FF3A -#define XF86XK_BrightnessAdjust 0x1008FF3B -#define XF86XK_Finance 0x1008FF3C -#define XF86XK_Community 0x1008FF3D -#define XF86XK_Launch0 0x1008FF40 -#define XF86XK_Launch1 0x1008FF41 -#define XF86XK_Launch2 0x1008FF42 -#define XF86XK_Launch3 0x1008FF43 -#define XF86XK_Launch4 0x1008FF44 -#define XF86XK_Launch5 0x1008FF45 -#define XF86XK_Launch6 0x1008FF46 -#define XF86XK_Launch7 0x1008FF47 -#define XF86XK_Launch8 0x1008FF48 -#define XF86XK_Launch9 0x1008FF49 -#define XF86XK_LaunchA 0x1008FF4A -#define XF86XK_LaunchB 0x1008FF4B -#define XF86XK_LaunchC 0x1008FF4C -#define XF86XK_LaunchD 0x1008FF4D -#define XF86XK_LaunchE 0x1008FF4E -#define XF86XK_LaunchF 0x1008FF4F +#if HAVE_XKB_EXTENSION +# include +#endif #endif -// map special KeyID keys to KeySyms -#if HAVE_X11_XF86KEYSYM_H -static const KeySym g_mapE000[] = -{ - /* 0x00 */ 0, XF86XK_Eject, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, - /* 0xa4 */ 0, 0, - /* 0xa6 */ XF86XK_Back, XF86XK_Forward, - /* 0xa8 */ XF86XK_Refresh, XF86XK_Stop, - /* 0xaa */ XF86XK_Search, XF86XK_Favorites, - /* 0xac */ XF86XK_HomePage, XF86XK_AudioMute, - /* 0xae */ XF86XK_AudioLowerVolume, XF86XK_AudioRaiseVolume, - /* 0xb0 */ XF86XK_AudioNext, XF86XK_AudioPrev, - /* 0xb2 */ XF86XK_AudioStop, XF86XK_AudioPlay, - /* 0xb4 */ XF86XK_Mail, XF86XK_AudioMedia, - /* 0xb6 */ XF86XK_Launch0, XF86XK_Launch1, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; -#endif - -CXWindowsKeyState::CXWindowsKeyState(Display* display) : +CXWindowsKeyState::CXWindowsKeyState(Display* display, bool useXKB) : m_display(display) { - // do nothing + XGetKeyboardControl(m_display, &m_keyboardState); +#if HAVE_XKB_EXTENSION + if (useXKB) { + m_xkb = XkbGetMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask | + XkbAllClientInfoMask, XkbUseCoreKbd); + } + else { + m_xkb = NULL; + } +#endif + setActiveGroup(kGroupPollAndSet); } CXWindowsKeyState::~CXWindowsKeyState() { - // do nothing +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + XkbFreeKeyboard(m_xkb, 0, True); + } +#endif +} + +void +CXWindowsKeyState::setActiveGroup(SInt32 group) +{ + if (group == kGroupPollAndSet) { + m_group = -1; + m_group = pollActiveGroup(); + } + else if (group == kGroupPoll) { + m_group = -1; + } + else { + assert(group >= 0); + m_group = group; + } +} + +void +CXWindowsKeyState::setAutoRepeat(const XKeyboardState& state) +{ + m_keyboardState = state; } KeyModifierMask CXWindowsKeyState::mapModifiersFromX(unsigned int state) const { + UInt32 offset = 8 * getGroupFromState(state); KeyModifierMask mask = 0; - if (state & ShiftMask) - mask |= KeyModifierShift; - if (state & LockMask) - mask |= KeyModifierCapsLock; - if (state & ControlMask) - mask |= KeyModifierControl; - if (state & m_altMask) - mask |= KeyModifierAlt; - if (state & m_metaMask) - mask |= KeyModifierMeta; - if (state & m_superMask) - mask |= KeyModifierSuper; - if (state & m_modeSwitchMask) - mask |= KeyModifierModeSwitch; - if (state & m_numLockMask) - mask |= KeyModifierNumLock; - if (state & m_scrollLockMask) - mask |= KeyModifierScrollLock; + for (int i = 0; i < 8; ++i) { + if ((state & (1u << i)) != 0) { + mask |= m_modifierFromX[offset + i]; + } + } return mask; } +bool +CXWindowsKeyState::mapModifiersToX(KeyModifierMask mask, + unsigned int& modifiers) const +{ + modifiers = 0; + + for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) { + KeyModifierMask bit = (1u << i); + if ((mask & bit) != 0) { + KeyModifierToXMask::const_iterator j = m_modifierToX.find(bit); + if (j == m_modifierToX.end()) { + return false; + } + else { + modifiers |= j->second; + } + } + } + + return true; +} + +void +CXWindowsKeyState::mapKeyToKeycodes(KeyID key, CKeycodeList& keycodes) const +{ + keycodes.clear(); + std::pair range = + m_keyCodeFromKey.equal_range(key); + for (KeyToKeyCodeMap::const_iterator i = range.first; + i != range.second; ++i) { + keycodes.push_back(i->second); + } +} + bool CXWindowsKeyState::fakeCtrlAltDel() { @@ -181,32 +132,9 @@ CXWindowsKeyState::fakeCtrlAltDel() return false; } -const char* -CXWindowsKeyState::getKeyName(KeyButton keycode) const +KeyModifierMask +CXWindowsKeyState::pollActiveModifiers() const { - KeySym keysym = XKeycodeToKeysym(m_display, keycode, 0); - char* name = XKeysymToString(keysym); - if (name != NULL) { - return name; - } - else { - static char buffer[20]; - return strcpy(buffer, - CStringUtil::print("keycode %d", keycode).c_str()); - } -} - -void -CXWindowsKeyState::doUpdateKeys() -{ - // query which keys are pressed - char keys[32]; - XQueryKeymap(m_display, keys); - - // save the auto-repeat mask - XGetKeyboardControl(m_display, &m_keyControl); - - // query the pointer to get the keyboard state Window root = DefaultRootWindow(m_display), window; int xRoot, yRoot, xWindow, yWindow; unsigned int state; @@ -214,901 +142,685 @@ CXWindowsKeyState::doUpdateKeys() &xRoot, &yRoot, &xWindow, &yWindow, &state)) { state = 0; } - - // update mappings - updateKeysymMap(); - updateModifiers(); - - // transfer to our state - for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) { - if ((keys[i] & 0x01) != 0) - setKeyDown(j + 0, true); - if ((keys[i] & 0x02) != 0) - setKeyDown(j + 1, true); - if ((keys[i] & 0x04) != 0) - setKeyDown(j + 2, true); - if ((keys[i] & 0x08) != 0) - setKeyDown(j + 3, true); - if ((keys[i] & 0x10) != 0) - setKeyDown(j + 4, true); - if ((keys[i] & 0x20) != 0) - setKeyDown(j + 5, true); - if ((keys[i] & 0x40) != 0) - setKeyDown(j + 6, true); - if ((keys[i] & 0x80) != 0) - setKeyDown(j + 7, true); - } - - // set toggle modifier states - if ((state & LockMask) != 0) - setToggled(KeyModifierCapsLock); - if ((state & m_numLockMask) != 0) - setToggled(KeyModifierNumLock); - if ((state & m_scrollLockMask) != 0) - setToggled(KeyModifierScrollLock); + return mapModifiersFromX(state); } -void -CXWindowsKeyState::doFakeKeyEvent(KeyButton keycode, bool press, bool) +SInt32 +CXWindowsKeyState::pollActiveGroup() const { - XTestFakeKeyEvent(m_display, keycode, press ? True : False, CurrentTime); - XFlush(m_display); -} - -KeyButton -CXWindowsKeyState::mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, bool isAutoRepeat) const -{ - // the system translates key events into characters depending - // on the modifier key state at the time of the event. to - // generate the right keysym we need to set the modifier key - // states appropriately. - // - // desiredMask is the mask desired by the caller. however, there - // may not be a keycode mapping to generate the desired keysym - // with that mask. we override the bits in the mask that cannot - // be accomodated. - - // convert KeyID to a KeySym - KeySym keysym = keyIDToKeySym(id, desiredMask); - if (keysym == NoSymbol) { - // unknown key - return 0; + if (m_group != -1) { + assert(m_group >= 0); + return m_group; } - // get the mapping for this keysym - KeySymIndex keyIndex = m_keysymMap.find(keysym); - if (keyIndex != m_keysymMap.end()) { - // the keysym is mapped to some keycode. create the keystrokes - // for this keysym. - return mapToKeystrokes(keys, keyIndex, isAutoRepeat, false); - } - - // we can't find the keysym mapped to any keycode. this doesn't - // necessarily mean we can't generate the keysym, though. if the - // keysym can be created by combining keysyms then we may still - // be okay. - if (!isAutoRepeat) { - KeyButton keycode = mapDecompositionToKeystrokes(keys, keysym, true); - if (keycode != 0) { - return keycode; - } - keycode = mapDecompositionToKeystrokes(keys, keysym, false); - if (keycode != 0) { - // no key is left synthetically down when using the compose key - // so return 0 even though we succeeded. - return 0; +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + XkbStateRec state; + if (XkbGetState(m_display, XkbUseCoreKbd, &state)) { + return state.group; } } - - // if the keysym is caps lock sensitive then convert the case of - // the keysym and try again. - KeySym lKey, uKey; - XConvertCase(keysym, &lKey, &uKey); - if (lKey != uKey) { - if (lKey == keysym) { - keyIndex = m_keysymMap.find(uKey); - } - else { - keyIndex = m_keysymMap.find(lKey); - } - if (keyIndex != m_keysymMap.end()) { - // the keysym is mapped to some keycode. create the keystrokes - // for this keysym. - return mapToKeystrokes(keys, keyIndex, isAutoRepeat, false); - } - } - +#endif return 0; } void -CXWindowsKeyState::updateKeysymMap() +CXWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const +{ + char keys[32]; + XQueryKeymap(m_display, keys); + for (UInt32 i = 0; i < 32; ++i) { + for (UInt32 j = 0; j < 8; ++j) { + if ((keys[i] & (1u << j)) != 0) { + pressedKeys.insert(8 * i + j); + } + } + } +} + +void +CXWindowsKeyState::getKeyMap(CKeyMap& keyMap) +{ + // get autorepeat info. we must use the global_auto_repeat told to + // us because it may have modified by synergy. + int oldGlobalAutoRepeat = m_keyboardState.global_auto_repeat; + XGetKeyboardControl(m_display, &m_keyboardState); + m_keyboardState.global_auto_repeat = oldGlobalAutoRepeat; + +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + XkbGetUpdatedMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask | + XkbAllClientInfoMask, m_xkb); + updateKeysymMapXKB(keyMap); + } + else +#endif + { + updateKeysymMap(keyMap); + } +} + +void +CXWindowsKeyState::fakeKey(const Keystroke& keystroke) +{ + switch (keystroke.m_type) { + case Keystroke::kButton: + LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up")); + if (keystroke.m_data.m_button.m_repeat) { + int c = keystroke.m_data.m_button.m_button; + int i = (c >> 3); + int b = 1 << (c & 7); + if (m_keyboardState.global_auto_repeat == AutoRepeatModeOff || + (m_keyboardState.auto_repeats[i] & b) == 0) { + LOG((CLOG_DEBUG1 " discard autorepeat")); + break; + } + } + XTestFakeKeyEvent(m_display, keystroke.m_data.m_button.m_button, + keystroke.m_data.m_button.m_press ? True : False, + CurrentTime); + break; + + case Keystroke::kGroup: + if (keystroke.m_data.m_group.m_absolute) { + LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group)); +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + XkbLockGroup(m_display, XkbUseCoreKbd, + keystroke.m_data.m_group.m_group); + } + else +#endif + { + LOG((CLOG_DEBUG1 " ignored")); + } + } + else { + LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group)); +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + XkbLockGroup(m_display, XkbUseCoreKbd, + getEffectiveGroup(pollActiveGroup(), + keystroke.m_data.m_group.m_group)); + } + else +#endif + { + LOG((CLOG_DEBUG1 " ignored")); + } + } + break; + } + XFlush(m_display); +} + +void +CXWindowsKeyState::updateKeysymMap(CKeyMap& keyMap) { // there are up to 4 keysyms per keycode - static const unsigned int maxKeysyms = 4; + static const int maxKeysyms = 4; + + LOG((CLOG_DEBUG1 "non-XKB mapping")); + + // prepare map from X modifier to KeyModifierMask. certain bits + // are predefined. + m_modifierFromX.clear(); + m_modifierFromX.resize(8); + m_modifierFromX[ShiftMapIndex] = KeyModifierShift; + m_modifierFromX[LockMapIndex] = KeyModifierCapsLock; + m_modifierFromX[ControlMapIndex] = KeyModifierControl; + m_modifierToX.clear(); + m_modifierToX[KeyModifierShift] = ShiftMask; + m_modifierToX[KeyModifierCapsLock] = LockMask; + m_modifierToX[KeyModifierControl] = ControlMask; + + // prepare map from KeyID to KeyCode + m_keyCodeFromKey.clear(); // get the number of keycodes int minKeycode, maxKeycode; XDisplayKeycodes(m_display, &minKeycode, &maxKeycode); - const int numKeycodes = maxKeycode - minKeycode + 1; + int numKeycodes = maxKeycode - minKeycode + 1; // get the keyboard mapping for all keys int keysymsPerKeycode; - KeySym* keysyms = XGetKeyboardMapping(m_display, + KeySym* allKeysyms = XGetKeyboardMapping(m_display, minKeycode, numKeycodes, &keysymsPerKeycode); - // we only understand up to maxKeysyms keysyms per keycodes - unsigned int numKeysyms = keysymsPerKeycode; - if (numKeysyms > maxKeysyms) { - numKeysyms = maxKeysyms; + // it's more convenient to always have maxKeysyms KeySyms per key + { + KeySym* tmpKeysyms = new KeySym[maxKeysyms * numKeycodes]; + for (int i = 0; i < numKeycodes; ++i) { + for (int j = 0; j < maxKeysyms; ++j) { + if (j < keysymsPerKeycode) { + tmpKeysyms[maxKeysyms * i + j] = + allKeysyms[keysymsPerKeycode * i + j]; + } + else { + tmpKeysyms[maxKeysyms * i + j] = NoSymbol; + } + } + } + XFree(allKeysyms); + allKeysyms = tmpKeysyms; } - // determine shift and mode switch sensitivity. a keysym is shift - // or mode switch sensitive if its keycode is. a keycode is mode - // mode switch sensitive if it has keysyms for indices 2 or 3. - // it's shift sensitive if the keysym for index 1 (if any) is - // different from the keysym for index 0 and, if the keysym for - // for index 3 (if any) is different from the keysym for index 2. - // that is, if shift changes the generated keysym for the keycode. - std::vector usesShift(numKeycodes); - std::vector usesModeSwitch(numKeycodes); - for (int i = 0; i < numKeycodes; ++i) { - // check mode switch first - if (numKeysyms > 2 && - keysyms[i * keysymsPerKeycode + 2] != NoSymbol || - keysyms[i * keysymsPerKeycode + 3] != NoSymbol) { - usesModeSwitch[i] = true; - } - - // check index 0 with index 1 keysyms - if (keysyms[i * keysymsPerKeycode + 0] != NoSymbol && - keysyms[i * keysymsPerKeycode + 1] != NoSymbol && - keysyms[i * keysymsPerKeycode + 1] != - keysyms[i * keysymsPerKeycode + 0]) { - usesShift[i] = true; - } - - else if (numKeysyms >= 4 && - keysyms[i * keysymsPerKeycode + 2] != NoSymbol && - keysyms[i * keysymsPerKeycode + 3] != NoSymbol && - keysyms[i * keysymsPerKeycode + 3] != - keysyms[i * keysymsPerKeycode + 2]) { - usesShift[i] = true; - } - } - - // get modifier map from server - XModifierKeymap* modifiers = XGetModifierMapping(m_display); - unsigned int keysPerModifier = modifiers->max_keypermod; - - // clear state - m_keysymMap.clear(); - m_modeSwitchKeysym = NoSymbol; - m_altMask = 0; - m_metaMask = 0; - m_superMask = 0; - m_modeSwitchMask = 0; - m_numLockMask = 0; - m_scrollLockMask = 0; - - // work around for my system, which reports this state bit when - // mode switch is down, instead of the appropriate modifier bit. - // should have no effect on other systems. -crs 9/02. - m_modeSwitchMask |= (1 << 13); - - // for each modifier keycode, get the index 0 keycode and add it to - // the keysym map. also collect all keycodes for each modifier. + // get the buttons assigned to modifiers. X11 does not predefine + // the meaning of any modifiers except shift, caps lock, and the + // control key. the meaning of a modifier bit (other than those) + // depends entirely on the KeySyms mapped to that bit. unfortunately + // you cannot map a bit back to the KeySym used to produce it. + // for example, let's say button 1 maps to Alt_L without shift and + // Meta_L with shift. now if mod1 is mapped to button 1 that could + // mean the user used Alt or Meta to turn on that modifier and there's + // no way to know which. it's also possible for one button to be + // mapped to multiple bits so both mod1 and mod2 could be generated + // by button 1. + // + // we're going to ignore any modifier for a button except the first. + // with the above example, that means we'll ignore the mod2 modifier + // bit unless it's also mapped to some other button. we're also + // going to ignore all KeySyms except the first modifier KeySym, + // which means button 1 above won't map to Meta, just Alt. + std::map modifierButtons; + XModifierKeymap* modifiers = XGetModifierMapping(m_display); for (unsigned int i = 0; i < 8; ++i) { - // no keycodes for this modifier yet - KeyModifierMask mask = 0; - KeyButtons modifierKeys; + const KeyCode* buttons = + modifiers->modifiermap + i * modifiers->max_keypermod; + for (int j = 0; j < modifiers->max_keypermod; ++j) { + modifierButtons.insert(std::make_pair(buttons[j], i)); + } + } + XFreeModifiermap(modifiers); + modifierButtons.erase(0); - // add each keycode for modifier - for (unsigned int j = 0; j < keysPerModifier; ++j) { - // get keycode and ignore unset keycodes - KeyCode keycode = modifiers->modifiermap[i * keysPerModifier + j]; - if (keycode == 0) { - continue; + // Hack to deal with VMware. When a VMware client grabs input the + // player clears out the X modifier map for whatever reason. We're + // notified of the change and arrive here to discover that there + // are no modifiers at all. Since this prevents the modifiers from + // working in the VMware client we'll use the last known good set + // of modifiers when there are no modifiers. If there are modifiers + // we update the last known good set. + if (!modifierButtons.empty()) { + m_lastGoodNonXKBModifiers = modifierButtons; + } + else { + modifierButtons = m_lastGoodNonXKBModifiers; + } + + // add entries for each keycode + CKeyMap::KeyItem item; + for (int i = 0; i < numKeycodes; ++i) { + KeySym* keysyms = allKeysyms + maxKeysyms * i; + KeyCode keycode = static_cast(i + minKeycode); + item.m_button = static_cast(keycode); + item.m_client = 0; + + // determine modifier sensitivity + item.m_sensitive = 0; + + // if the keysyms in levels 2 or 3 exist and differ from levels + // 0 and 1 then the key is sensitive AltGr (Mode_switch) + if ((keysyms[2] != NoSymbol && keysyms[2] != keysyms[0]) || + (keysyms[3] != NoSymbol && keysyms[2] != keysyms[1])) { + item.m_sensitive |= KeyModifierAltGr; + } + + // check if the key is caps-lock sensitive. some systems only + // provide one keysym for keys sensitive to caps-lock. if we + // find that then fill in the missing keysym. + if (keysyms[0] != NoSymbol && keysyms[1] == NoSymbol && + keysyms[2] == NoSymbol && keysyms[3] == NoSymbol) { + KeySym lKeysym, uKeysym; + XConvertCase(keysyms[0], &lKeysym, &uKeysym); + if (lKeysym != uKeysym) { + keysyms[0] = lKeysym; + keysyms[1] = uKeysym; + item.m_sensitive |= KeyModifierCapsLock; } - - // get keysym and get/create key mapping - int keycodeIndex = keycode - minKeycode; - KeySym keysym = keysyms[keycodeIndex * - keysymsPerKeycode + 0]; - - // prefer XK_ISO_Level3_Shift over XK_Mode_switch. newer - // versions of X use the former only. we assume here that - // if you have XK_ISO_Level3_Shift mapped then that's what - // you want to use even if XK_Mode_switch is also mapped. - if (j == 0 && keysym == XK_Mode_switch) { - // sort modifiers->modifiermap for this modifier so - // that a keycode mapped to XK_ISO_Level3_Shift appears - // before those mapped to anything else. this one keycode - // is enough so long as it's first because mapModifier in - // CKeyState uses the first keycode in modifierKeys to - // generate the key event for this modifier. - KeyCode* keycodes = modifiers->modifiermap + - i * keysPerModifier + j; - for (unsigned int k = j + 1; k < keysPerModifier; ++k) { - KeySym keysym2 = keysyms[(keycodes[k] - minKeycode) * - keysymsPerKeycode + 0]; - if (keysym2 == XK_ISO_Level3_Shift) { - // found an XK_ISO_Level3_Shift. swap it with - // the keycode in index j. - keycodes[j] = keycodes[k]; - keycodes[k] = keycode; - - // now use the new first keycode - keycode = keycodes[j]; - keycodeIndex = keycode - minKeycode; - keysym = keysym2; - break; - } + } + else if (keysyms[0] != NoSymbol && keysyms[1] != NoSymbol) { + KeySym lKeysym, uKeysym; + XConvertCase(keysyms[0], &lKeysym, &uKeysym); + if (lKeysym != uKeysym && + lKeysym == keysyms[0] && + uKeysym == keysyms[1]) { + item.m_sensitive |= KeyModifierCapsLock; + } + else if (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol) { + XConvertCase(keysyms[2], &lKeysym, &uKeysym); + if (lKeysym != uKeysym && + lKeysym == keysyms[2] && + uKeysym == keysyms[3]) { + item.m_sensitive |= KeyModifierCapsLock; } } + } - // get modifier mask if we haven't yet. this has the side - // effect of setting the m_*Mask members. - if (mask == 0) { - mask = mapToModifierMask(i, keysym); - if (mask == 0) { + // key is sensitive to shift if keysyms in levels 0 and 1 or + // levels 2 and 3 don't match. it's also sensitive to shift + // if it's sensitive to caps-lock. + if ((item.m_sensitive & KeyModifierCapsLock) != 0) { + item.m_sensitive |= KeyModifierShift; + } + else if ((keysyms[0] != NoSymbol && keysyms[1] != NoSymbol && + keysyms[0] != keysyms[1]) || + (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol && + keysyms[2] != keysyms[3])) { + item.m_sensitive |= KeyModifierShift; + } + + // key is sensitive to numlock if any keysym on it is + if (IsKeypadKey(keysyms[0]) || IsPrivateKeypadKey(keysyms[0]) || + IsKeypadKey(keysyms[1]) || IsPrivateKeypadKey(keysyms[1]) || + IsKeypadKey(keysyms[2]) || IsPrivateKeypadKey(keysyms[2]) || + IsKeypadKey(keysyms[3]) || IsPrivateKeypadKey(keysyms[3])) { + item.m_sensitive |= KeyModifierNumLock; + } + + // do each keysym (shift level) + for (int j = 0; j < maxKeysyms; ++j) { + item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[j]); + if (item.m_id == kKeyNone) { + if (j != 0 && modifierButtons.count(keycode) > 0) { + // pretend the modifier works in other shift levels + // because it probably does. + if (keysyms[1] == NoSymbol || j != 3) { + item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[0]); + } + else { + item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[1]); + } + } + if (item.m_id == kKeyNone) { continue; } } - // save keycode for modifier - modifierKeys.push_back(keycode); + // group is 0 for levels 0 and 1 and 1 for levels 2 and 3 + item.m_group = (j >= 2) ? 1 : 0; - // skip if we already have a keycode for this index - KeyMapping& mapping = m_keysymMap[keysym]; - if (mapping.m_keycode[0] != 0) { - continue; + // compute required modifiers + item.m_required = 0; + if ((j & 1) != 0) { + item.m_required |= KeyModifierShift; + } + if ((j & 2) != 0) { + item.m_required |= KeyModifierAltGr; } - // fill in keysym info - mapping.m_keycode[0] = keycode; - mapping.m_shiftSensitive[0] = usesShift[keycodeIndex]; - mapping.m_modeSwitchSensitive[0] = usesModeSwitch[keycodeIndex]; - mapping.m_modifierMask = mask; - mapping.m_capsLockSensitive = false; - mapping.m_numLockSensitive = false; - } + item.m_generates = 0; + item.m_lock = false; + if (modifierButtons.count(keycode) > 0) { + // get flags for modifier keys + CKeyMap::initModifierKey(item); - // note this modifier - if (mask != 0) { - addModifier(mask, modifierKeys); + // add mapping from X (unless we already have) + if (item.m_generates != 0) { + unsigned int bit = modifierButtons[keycode]; + if (m_modifierFromX[bit] == 0) { + m_modifierFromX[bit] = item.m_generates; + m_modifierToX[item.m_generates] = (1u << bit); + } + } + } + + // add key + keyMap.addKeyEntry(item); + m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode)); + + // add other ways to synthesize the key + if ((j & 1) != 0) { + // add capslock version of key is sensitive to capslock + KeySym lKeysym, uKeysym; + XConvertCase(keysyms[j], &lKeysym, &uKeysym); + if (lKeysym != uKeysym && + lKeysym == keysyms[j - 1] && + uKeysym == keysyms[j]) { + item.m_required &= ~KeyModifierShift; + item.m_required |= KeyModifierCapsLock; + keyMap.addKeyEntry(item); + item.m_required |= KeyModifierShift; + item.m_required &= ~KeyModifierCapsLock; + } + + // add numlock version of key if sensitive to numlock + if (IsKeypadKey(keysyms[j]) || IsPrivateKeypadKey(keysyms[j])) { + item.m_required &= ~KeyModifierShift; + item.m_required |= KeyModifierNumLock; + keyMap.addKeyEntry(item); + item.m_required |= KeyModifierShift; + item.m_required &= ~KeyModifierNumLock; + } + } } } - // create a convenient NoSymbol entry (if it doesn't exist yet). - // sometimes it's useful to handle NoSymbol like a normal keysym. - // remove any entry for NoSymbol. that keysym doesn't count. - { - KeyMapping& mapping = m_keysymMap[NoSymbol]; - for (unsigned int i = 0; i < numKeysyms; ++i) { - mapping.m_keycode[i] = 0; - mapping.m_shiftSensitive[i] = false; - mapping.m_modeSwitchSensitive[i] = false; - } - mapping.m_modifierMask = 0; - mapping.m_capsLockSensitive = false; - mapping.m_numLockSensitive = false; - } - - // add each keysym to the map, unless we've already inserted a key - // for that keysym index. - for (int i = 0; i < numKeycodes; ++i) { - for (unsigned int j = 0; j < numKeysyms; ++j) { - // lookup keysym - const KeySym keysym = keysyms[i * keysymsPerKeycode + j]; - if (keysym == NoSymbol) { - continue; - } - KeyMapping& mapping = m_keysymMap[keysym]; - - // skip if we already have a keycode for this index - if (mapping.m_keycode[j] != 0) { - continue; - } - - // fill in keysym info - if (mapping.m_keycode[0] == 0) { - mapping.m_modifierMask = 0; - } - mapping.m_keycode[j] = static_cast( - minKeycode + i); - mapping.m_shiftSensitive[j] = usesShift[i]; - mapping.m_modeSwitchSensitive[j] = usesModeSwitch[i]; - mapping.m_numLockSensitive = isNumLockSensitive(keysym); - mapping.m_capsLockSensitive = isCapsLockSensitive(keysym); - } - } - - // clean up - XFreeModifiermap(modifiers); - XFree(keysyms); -} - -KeyModifierMask -CXWindowsKeyState::mapToModifierMask(unsigned int i, KeySym keysym) -{ - // some modifier indices (0,1,2) are dedicated to particular uses, - // the rest depend on the keysyms bound. - switch (i) { - case 0: - return KeyModifierShift; - - case 1: - return KeyModifierCapsLock; - - case 2: - return KeyModifierControl; - - default: - switch (keysym) { - case XK_Shift_L: - case XK_Shift_R: - return KeyModifierShift; - - case XK_Control_L: - case XK_Control_R: - return KeyModifierControl; - - case XK_Alt_L: - case XK_Alt_R: - m_altMask = (1 << i); - return KeyModifierAlt; - - case XK_Meta_L: - case XK_Meta_R: - m_metaMask = (1 << i); - return KeyModifierMeta; - - case XK_Super_L: - case XK_Super_R: - m_superMask = (1 << i); - return KeyModifierSuper; - - case XK_Mode_switch: - case XK_ISO_Level3_Shift: - m_modeSwitchMask = (1 << i); - return KeyModifierModeSwitch; - - case XK_Caps_Lock: - return KeyModifierCapsLock; - - case XK_Num_Lock: - m_numLockMask = (1 << i); - return KeyModifierNumLock; - - case XK_Scroll_Lock: - m_scrollLockMask = (1 << i); - return KeyModifierScrollLock; - - default: - return 0; - } - } + delete[] allKeysyms; } +#if HAVE_XKB_EXTENSION void -CXWindowsKeyState::updateModifiers() +CXWindowsKeyState::updateKeysymMapXKB(CKeyMap& keyMap) { - struct CHandedModifiers { - KeySym m_left; - KeySym m_right; - }; - static const CHandedModifiers s_handedModifiers[] = { - { XK_Shift_L, XK_Shift_R }, - { XK_Control_L, XK_Control_R }, - { XK_Meta_L, XK_Meta_R }, - { XK_Alt_L, XK_Alt_R }, - { XK_Super_L, XK_Super_R }, - { XK_Hyper_L, XK_Hyper_R } - }; - struct CModifierBitInfo { - public: - KeySym CXWindowsKeyState::*m_keysym; - KeySym m_left; - KeySym m_right; - }; - static const CModifierBitInfo s_modifierBitTable[] = { - { &CXWindowsKeyState::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol }, - { &CXWindowsKeyState::m_modeSwitchKeysym, XK_ISO_Level3_Shift, NoSymbol }, + static const XkbKTMapEntryRec defMapEntry = { + True, // active + 0, // level + { + 0, // mods.mask + 0, // mods.real_mods + 0 // mods.vmods + } }; - // for any modifier with left/right versions that has one side but - // not the other mapped, map the missing side to the existing side. - // this will map, for example, Alt_R to Alt_L if Alt_L is mapped - // but Alt_R isn't. this is almost always what the user wants - // since the user almost never cares about the difference between - // Alt_L and Alt_R. - for (size_t i = 0; i < sizeof(s_handedModifiers) / - sizeof(s_handedModifiers[0]); ++i) { - KeySymIndex lIndex = m_keysymMap.find(s_handedModifiers[i].m_left); - KeySymIndex rIndex = m_keysymMap.find(s_handedModifiers[i].m_right); - if (lIndex == m_keysymMap.end() && rIndex != m_keysymMap.end()) { - m_keysymMap[s_handedModifiers[i].m_left] = rIndex->second; - } - else if (lIndex != m_keysymMap.end() && rIndex == m_keysymMap.end()) { - m_keysymMap[s_handedModifiers[i].m_right] = lIndex->second; + LOG((CLOG_DEBUG1 "XKB mapping")); + + // find the number of groups + int maxNumGroups = 0; + for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) { + int numGroups = XkbKeyNumGroups(m_xkb, static_cast(i)); + if (numGroups > maxNumGroups) { + maxNumGroups = numGroups; } } - // choose the keysym to use for some modifiers. if a modifier has - // both left and right versions then (arbitrarily) prefer the left. - for (size_t i = 0; i < sizeof(s_modifierBitTable) / - sizeof(s_modifierBitTable[0]); ++i) { - const CModifierBitInfo& info = s_modifierBitTable[i]; + // prepare map from X modifier to KeyModifierMask + std::vector modifierLevel(maxNumGroups * 8, 4); + m_modifierFromX.clear(); + m_modifierFromX.resize(maxNumGroups * 8); + m_modifierToX.clear(); - // find available keysym - KeySymIndex keyIndex = m_keysymMap.find(info.m_left); - if (keyIndex == m_keysymMap.end() && info.m_right != NoSymbol) { - keyIndex = m_keysymMap.find(info.m_right); - } + // prepare map from KeyID to KeyCode + m_keyCodeFromKey.clear(); - // save modifier info - if (keyIndex != m_keysymMap.end() && - keyIndex->second.m_modifierMask != 0) { - this->*(info.m_keysym) = keyIndex->first; - } + // Hack to deal with VMware. When a VMware client grabs input the + // player clears out the X modifier map for whatever reason. We're + // notified of the change and arrive here to discover that there + // are no modifiers at all. Since this prevents the modifiers from + // working in the VMware client we'll use the last known good set + // of modifiers when there are no modifiers. If there are modifiers + // we update the last known good set. + bool useLastGoodModifiers = !hasModifiersXKB(); + if (!useLastGoodModifiers) { + m_lastGoodXKBModifiers.clear(); } - // if there's no mode switch key mapped then remove all keycodes - // that depend on it and no keycode can be mode switch sensitive. - if (m_modeSwitchKeysym == NoSymbol) { - LOG((CLOG_DEBUG2 "no mode switch in keymap")); - for (KeySymMap::iterator i = m_keysymMap.begin(); - i != m_keysymMap.end(); ) { - i->second.m_keycode[2] = 0; - i->second.m_keycode[3] = 0; - i->second.m_modeSwitchSensitive[0] = false; - i->second.m_modeSwitchSensitive[1] = false; - i->second.m_modeSwitchSensitive[2] = false; - i->second.m_modeSwitchSensitive[3] = false; + // check every button. on this pass we save all modifiers as native + // X modifier masks. + CKeyMap::KeyItem item; + for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) { + KeyCode keycode = static_cast(i); + item.m_button = static_cast(keycode); + item.m_client = 0; - // if this keysym no has no keycodes then remove it - // except for the NoSymbol keysym mapping. - if (i->second.m_keycode[0] == 0 && i->second.m_keycode[1] == 0) { - m_keysymMap.erase(i++); - } - else { - ++i; + // skip keys with no groups (they generate no symbols) + if (XkbKeyNumGroups(m_xkb, keycode) == 0) { + continue; + } + + // note half-duplex keys + const XkbBehavior& b = m_xkb->server->behaviors[keycode]; + if ((b.type & XkbKB_OpMask) == XkbKB_Lock) { + keyMap.addHalfDuplexButton(item.m_button); + } + + // iterate over all groups + for (int group = 0; group < maxNumGroups; ++group) { + item.m_group = group; + int eGroup = getEffectiveGroup(keycode, group); + + // get key info + XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, eGroup); + + // set modifiers the item is sensitive to + item.m_sensitive = type->mods.mask; + + // iterate over all shift levels for the button (including none) + for (int j = -1; j < type->map_count; ++j) { + const XkbKTMapEntryRec* mapEntry = + ((j == -1) ? &defMapEntry : type->map + j); + if (!mapEntry->active) { + continue; + } + int level = mapEntry->level; + + // set required modifiers for this item + item.m_required = mapEntry->mods.mask; + if ((item.m_required & LockMask) != 0 && + j != -1 && type->preserve != NULL && + (type->preserve[j].mask & LockMask) != 0) { + // sensitive caps lock and we preserve caps-lock. + // preserving caps-lock means we Xlib functions would + // yield the capitialized KeySym so we'll adjust the + // level accordingly. + if ((level ^ 1) < type->num_levels) { + level ^= 1; + } + } + + // get the keysym for this item + KeySym keysym = XkbKeySymEntry(m_xkb, keycode, level, eGroup); + + // check for group change actions, locking modifiers, and + // modifier masks. + item.m_lock = false; + bool isModifier = false; + UInt32 modifierMask = m_xkb->map->modmap[keycode]; + if (XkbKeyHasActions(m_xkb, keycode)) { + XkbAction* action = + XkbKeyActionEntry(m_xkb, keycode, level, eGroup); + if (action->type == XkbSA_SetMods || + action->type == XkbSA_LockMods) { + isModifier = true; + + // note toggles + item.m_lock = (action->type == XkbSA_LockMods); + + // maybe use action's mask + if ((action->mods.flags & XkbSA_UseModMapMods) == 0) { + modifierMask = action->mods.mask; + } + } + else if (action->type == XkbSA_SetGroup || + action->type == XkbSA_LatchGroup || + action->type == XkbSA_LockGroup) { + // ignore group change key + continue; + } + } + level = mapEntry->level; + + // VMware modifier hack + if (useLastGoodModifiers) { + XKBModifierMap::const_iterator k = + m_lastGoodXKBModifiers.find(eGroup * 256 + keycode); + if (k != m_lastGoodXKBModifiers.end()) { + // Use last known good modifier + isModifier = true; + level = k->second.m_level; + modifierMask = k->second.m_mask; + item.m_lock = k->second.m_lock; + } + } + else if (isModifier) { + // Save known good modifier + XKBModifierInfo& info = + m_lastGoodXKBModifiers[eGroup * 256 + keycode]; + info.m_level = level; + info.m_mask = modifierMask; + info.m_lock = item.m_lock; + } + + // record the modifier mask for this key. don't bother + // for keys that change the group. + item.m_generates = 0; + UInt32 modifierBit = + CXWindowsUtil::getModifierBitForKeySym(keysym); + if (isModifier && modifierBit != kKeyModifierBitNone) { + item.m_generates = (1u << modifierBit); + for (SInt32 j = 0; j < 8; ++j) { + // skip modifiers this key doesn't generate + if ((modifierMask & (1u << j)) == 0) { + continue; + } + + // skip keys that map to a modifier that we've + // already seen using fewer modifiers. that is + // if this key must combine with other modifiers + // and we know of a key that combines with fewer + // modifiers (or no modifiers) then prefer the + // other key. + if (level >= modifierLevel[8 * group + j]) { + continue; + } + modifierLevel[8 * group + j] = level; + + // save modifier + m_modifierFromX[8 * group + j] |= (1u << modifierBit); + m_modifierToX.insert(std::make_pair( + 1u << modifierBit, 1u << j)); + } + } + + // handle special cases of just one keysym for the keycode + if (type->num_levels == 1) { + // if there are upper- and lowercase versions of the + // keysym then add both. + KeySym lKeysym, uKeysym; + XConvertCase(keysym, &lKeysym, &uKeysym); + if (lKeysym != uKeysym) { + if (j != -1) { + continue; + } + + item.m_sensitive |= ShiftMask | LockMask; + + KeyID lKeyID = CXWindowsUtil::mapKeySymToKeyID(lKeysym); + KeyID uKeyID = CXWindowsUtil::mapKeySymToKeyID(uKeysym); + if (lKeyID == kKeyNone || uKeyID == kKeyNone) { + continue; + } + + item.m_id = lKeyID; + item.m_required = 0; + keyMap.addKeyEntry(item); + + item.m_id = uKeyID; + item.m_required = ShiftMask; + keyMap.addKeyEntry(item); + item.m_required = LockMask; + keyMap.addKeyEntry(item); + + if (group == 0) { + m_keyCodeFromKey.insert( + std::make_pair(lKeyID, keycode)); + m_keyCodeFromKey.insert( + std::make_pair(uKeyID, keycode)); + } + continue; + } + } + + // add entry + item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysym); + keyMap.addKeyEntry(item); + if (group == 0) { + m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode)); + } } } } + + // change all modifier masks to synergy masks from X masks + keyMap.foreachKey(&CXWindowsKeyState::remapKeyModifiers, this); + + // allow composition across groups + keyMap.allowGroupSwitchDuringCompose(); } - -KeySym -CXWindowsKeyState::keyIDToKeySym(KeyID id, KeyModifierMask mask) const -{ - // convert id to keysym - KeySym keysym = NoSymbol; - if ((id & 0xfffff000) == 0xe000) { - // special character - switch (id & 0x0000ff00) { -#if HAVE_X11_XF86KEYSYM_H - case 0xe000: - return g_mapE000[id & 0xff]; #endif - case 0xee00: - // ISO 9995 Function and Modifier Keys - if (id == kKeyLeftTab) { - keysym = XK_ISO_Left_Tab; - } - break; - - case 0xef00: - // MISCELLANY - keysym = static_cast(id - 0xef00 + 0xff00); - break; - } - } - else if ((id >= 0x0020 && id <= 0x007e) || - (id >= 0x00a0 && id <= 0x00ff)) { - // Latin-1 maps directly - return static_cast(id); - } - else { - // lookup keysym in table - return CXWindowsUtil::mapUCS4ToKeySym(id); - } - - // fail if unknown key - if (keysym == NoSymbol) { - return keysym; - } - - // if kKeyTab is requested with shift active then try XK_ISO_Left_Tab - // instead. if that doesn't work, we'll fall back to XK_Tab with - // shift active. this is to handle primary screens that don't map - // XK_ISO_Left_Tab sending events to secondary screens that do. - if (keysym == XK_Tab && (mask & KeyModifierShift) != 0) { - keysym = XK_ISO_Left_Tab; - } - - // some keysyms have emergency backups (particularly the numpad - // keys since most laptops don't have a separate numpad and the - // numpad overlaying the main keyboard may not have movement - // key bindings). figure out the emergency backup. - KeySym backupKeysym; - switch (keysym) { - case XK_KP_Home: - backupKeysym = XK_Home; - break; - - case XK_KP_Left: - backupKeysym = XK_Left; - break; - - case XK_KP_Up: - backupKeysym = XK_Up; - break; - - case XK_KP_Right: - backupKeysym = XK_Right; - break; - - case XK_KP_Down: - backupKeysym = XK_Down; - break; - - case XK_KP_Prior: - backupKeysym = XK_Prior; - break; - - case XK_KP_Next: - backupKeysym = XK_Next; - break; - - case XK_KP_End: - backupKeysym = XK_End; - break; - - case XK_KP_Insert: - backupKeysym = XK_Insert; - break; - - case XK_KP_Delete: - backupKeysym = XK_Delete; - break; - - case XK_ISO_Left_Tab: - backupKeysym = XK_Tab; - break; - - default: - backupKeysym = keysym; - break; - } - - // see if the keysym is assigned to any keycode. if not and the - // backup keysym is then use the backup keysym. - if (backupKeysym != keysym && - m_keysymMap.find(keysym) == m_keysymMap.end() && - m_keysymMap.find(backupKeysym) != m_keysymMap.end()) { - keysym = backupKeysym; - } - - return keysym; -} - -KeyButton -CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys, - KeySymIndex keyIndex, bool isAutoRepeat, - bool pressAndRelease) const +void +CXWindowsKeyState::remapKeyModifiers(KeyID id, SInt32 group, + CKeyMap::KeyItem& item, void* vself) { - // keyIndex must be valid - assert(keyIndex != m_keysymMap.end()); - - KeyModifierMask currentMask = getActiveModifiers(); - - // get the keysym we're trying to generate and possible keycodes - const KeySym keysym = keyIndex->first; - const KeyMapping& mapping = keyIndex->second; - LOG((CLOG_DEBUG2 "keysym = 0x%08x", keysym)); - - // get the best keycode index for the keysym and modifiers. note - // that (bestIndex & 1) == 0 if the keycode is a shift modifier - // and (bestIndex & 2) == 0 if the keycode is a mode switch - // modifier. this is important later because we don't want - // adjustModifiers() to adjust a modifier if that's the key we're - // mapping. - unsigned int bestIndex = findBestKeyIndex(keyIndex, currentMask); - - // get the keycode - KeyButton keycode = mapping.m_keycode[bestIndex]; - - // flip low bit of bestIndex if shift is inverted. if there's a - // keycode for this new index then use it. otherwise use the old - // keycode. you'd think we should fail if there isn't a keycode - // for the new index but some keymaps only include the upper case - // keysyms (notably those on Sun Solaris) so to handle the missing - // lower case keysyms we just use the old keycode. note that - // isShiftInverted() will always return false for a shift modifier. - if (isShiftInverted(keyIndex, currentMask)) { - LOG((CLOG_DEBUG2 "shift is inverted")); - bestIndex ^= 1; - if (mapping.m_keycode[bestIndex] != 0) { - keycode = mapping.m_keycode[bestIndex]; - } - } - LOG((CLOG_DEBUG2 "bestIndex = %d, keycode = %d", bestIndex, keycode)); - - // if this for auto-repeat and this key does not auto-repeat - // then return 0. - if (isAutoRepeat && - (m_keyControl.auto_repeats[keycode >> 3] & - static_cast(1 << (keycode & 7))) == 0) { - LOG((CLOG_DEBUG2 "non-autorepeating")); - return 0; - } - - // compute desired mask. the desired mask is the one that matches - // bestIndex, except if the key being synthesized is a shift key - // where we desire what we already have or if it's the mode switch - // key where we only desire to adjust shift. also, if the keycode - // is not sensitive to shift then don't adjust it, otherwise - // something like shift+home would become just home. similiarly - // for mode switch. - KeyModifierMask desiredMask = currentMask; - if (keyIndex->second.m_modifierMask != KeyModifierShift) { - if (keyIndex->second.m_shiftSensitive[bestIndex]) { - if ((bestIndex & 1) != 0) { - desiredMask |= KeyModifierShift; - } - else { - desiredMask &= ~KeyModifierShift; - } - } - if (keyIndex->second.m_modifierMask != KeyModifierModeSwitch) { - if (keyIndex->second.m_modeSwitchSensitive[bestIndex]) { - if ((bestIndex & 2) != 0) { - desiredMask |= KeyModifierModeSwitch; - } - else { - desiredMask &= ~KeyModifierModeSwitch; - } - } - } - } - - // adjust the modifiers to match the desired modifiers - Keystrokes undo; - if (!adjustModifiers(keys, undo, desiredMask)) { - LOG((CLOG_DEBUG2 "failed to adjust modifiers")); - return 0; - } - - // add the key event - Keystroke keystroke; - keystroke.m_key = keycode; - if (pressAndRelease) { - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); - } - else if (!isAutoRepeat) { - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - } - else { - keystroke.m_press = false; - keystroke.m_repeat = true; - keys.push_back(keystroke); - keystroke.m_press = true; - keys.push_back(keystroke); - } - - // put undo keystrokes at end of keystrokes in reverse order - while (!undo.empty()) { - keys.push_back(undo.back()); - undo.pop_back(); - } - - return keycode; -} - -KeyButton -CXWindowsKeyState::mapDecompositionToKeystrokes( - Keystrokes& keys, KeySym keysym, bool usingDeadKeys) const -{ - // decompose the keysym - CXWindowsUtil::KeySyms decomposed; - if (usingDeadKeys) { - if (!CXWindowsUtil::decomposeKeySymWithDeadKeys(keysym, decomposed)) { - // no decomposition - return 0; - } - LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms using dead keys", keysym, decomposed.size())); - } - else { - if (!CXWindowsUtil::decomposeKeySymWithCompose(keysym, decomposed)) { - // no decomposition - return 0; - } - LOG((CLOG_DEBUG2 "decomposed keysym 0x%08x into %d keysyms using compose key", keysym, decomposed.size())); - } - size_t n = decomposed.size(); - if (n == 0) { - // nothing in the decomposition - return 0; - } - - // map to keystrokes - Keystrokes keystrokes; - KeyButton keycode = 0; - for (size_t i = 0; i < n; ++i) { - // lookup the key - keysym = decomposed[i]; - KeySymIndex keyIndex = m_keysymMap.find(keysym); - if (keyIndex == m_keysymMap.end()) { - // missing a required keysym - LOG((CLOG_DEBUG2 "can't map keysym %d: 0x%04x", i, keysym)); - return 0; - } - - // the keysym is mapped to some keycode. add press and - // release unless this is the last key and usingDeadKeys. - keycode = mapToKeystrokes(keystrokes, keyIndex, - false, (i + 1 < n || !usingDeadKeys)); - if (keycode == 0) { - return 0; - } - } - - // copy keystrokes - keys.insert(keys.end(), keystrokes.begin(), keystrokes.end()); - - return keycode; -} - -unsigned int -CXWindowsKeyState::findBestKeyIndex(KeySymIndex keyIndex, - KeyModifierMask /*currentMask*/) const -{ - // there are up to 4 keycodes per keysym to choose from. the - // best choice is the one that requires the fewest adjustments - // to the modifier state. for example, the letter A normally - // requires shift + a. if shift isn't already down we'd have - // to synthesize a shift press before the a press. however, - // if A could also be created with some other keycode without - // shift then we'd prefer that when shift wasn't down. - // - // if the action is an auto-repeat then we don't call this - // method since we just need to synthesize a key repeat on the - // same keycode that we pressed. - // FIXME -- do this right - for (unsigned int i = 0; i < 4; ++i) { - if (keyIndex->second.m_keycode[i] != 0) { - return i; - } - } - - assert(0 && "no keycode found for keysym"); - return 0; + CXWindowsKeyState* self = reinterpret_cast(vself); + item.m_required = + self->mapModifiersFromX(XkbBuildCoreState(item.m_required, group)); + item.m_sensitive = + self->mapModifiersFromX(XkbBuildCoreState(item.m_sensitive, group)); } bool -CXWindowsKeyState::isShiftInverted(KeySymIndex keyIndex, - KeyModifierMask currentMask) const +CXWindowsKeyState::hasModifiersXKB() const { - // each keycode has up to 4 keysym associated with it, one each for: - // no modifiers, shift, mode switch, and shift and mode switch. if - // a keysym is modified by num lock and num lock is active then you - // get the shifted keysym when shift is not down and the unshifted - // keysym when it is. that is, num lock inverts the sense of the - // shift modifier when active. similarly for caps lock. this - // method returns true iff the sense of shift should be inverted - // for this key given a modifier state. - if (keyIndex->second.m_numLockSensitive) { - if ((currentMask & KeyModifierNumLock) != 0) { - return true; +#if HAVE_XKB_EXTENSION + // iterate over all keycodes + for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) { + KeyCode keycode = static_cast(i); + if (XkbKeyHasActions(m_xkb, keycode)) { + // iterate over all groups + int numGroups = XkbKeyNumGroups(m_xkb, keycode); + for (int group = 0; group < numGroups; ++group) { + // iterate over all shift levels for the button (including none) + XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, group); + for (int j = -1; j < type->map_count; ++j) { + if (j != -1 && !type->map[j].active) { + continue; + } + int level = ((j == -1) ? 0 : type->map[j].level); + XkbAction* action = + XkbKeyActionEntry(m_xkb, keycode, level, group); + if (action->type == XkbSA_SetMods || + action->type == XkbSA_LockMods) { + return true; + } + } + } } } - - // if a keysym is num lock sensitive it is never caps lock - // sensitive, thus the else here. - else if (keyIndex->second.m_capsLockSensitive) { - if ((currentMask & KeyModifierCapsLock) != 0) { - return true; - } - } - +#endif return false; } -bool -CXWindowsKeyState::adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask) const +int +CXWindowsKeyState::getEffectiveGroup(KeyCode keycode, int group) const { - KeyModifierMask currentMask = getActiveModifiers(); + (void)keycode; +#if HAVE_XKB_EXTENSION + // get effective group for key + int numGroups = XkbKeyNumGroups(m_xkb, keycode); + if (group >= numGroups) { + unsigned char groupInfo = XkbKeyGroupInfo(m_xkb, keycode); + switch (XkbOutOfRangeGroupAction(groupInfo)) { + case XkbClampIntoRange: + group = numGroups - 1; + break; - // get mode switch set correctly. do this before shift because - // mode switch may be sensitive to the shift modifier and will - // set/reset it as necessary. - bool forceShift = false; - bool wantShift = ((desiredMask & KeyModifierShift) != 0); - bool wantModeSwitch = ((desiredMask & KeyModifierModeSwitch) != 0); - bool haveModeSwitch = ((currentMask & KeyModifierModeSwitch) != 0); - if (wantModeSwitch != haveModeSwitch) { - LOG((CLOG_DEBUG2 "fix mode switch")); - - // adjust shift if necessary (i.e. turn it off it's on and mode - // shift is sensitive to the shift key) - KeySymIndex modeSwitchIndex = m_keysymMap.find(m_modeSwitchKeysym); - assert(modeSwitchIndex != m_keysymMap.end()); - if (modeSwitchIndex->second.m_shiftSensitive[0]) { - bool haveShift = ((currentMask & KeyModifierShift) != 0); - if (haveShift) { - // add shift keystrokes - LOG((CLOG_DEBUG2 "fix shift for mode switch")); - if (!mapModifier(keys, undo, KeyModifierShift, false, true)) { - return false; - } - - // our local concept of shift has flipped - currentMask ^= KeyModifierShift; - - // force shift to get turned on below if we had to turn - // off here and shift is desired. if we didn't force it - // then mapModifier would think shift is already down - // and ignore the request. - forceShift = wantShift; + case XkbRedirectIntoRange: + group = XkbOutOfRangeGroupNumber(groupInfo); + if (group >= numGroups) { + group = 0; } - } + break; - // add mode switch keystrokes - if (!mapModifier(keys, undo, KeyModifierModeSwitch, wantModeSwitch)) { - return false; + default: + // wrap + group %= numGroups; + break; } - currentMask ^= KeyModifierModeSwitch; } +#endif + return group; +} - // get shift set correctly - bool haveShift = ((currentMask & KeyModifierShift) != 0); - if (wantShift != haveShift) { - // add shift keystrokes - LOG((CLOG_DEBUG2 "fix shift")); - if (!mapModifier(keys, undo, KeyModifierShift, wantShift, forceShift)) { - return false; - } - currentMask ^= KeyModifierShift; +UInt32 +CXWindowsKeyState::getGroupFromState(unsigned int state) const +{ +#if HAVE_XKB_EXTENSION + if (m_xkb != NULL) { + return XkbGroupForCoreState(state); } - - return true; -} - -bool -CXWindowsKeyState::isNumLockSensitive(KeySym keysym) const -{ - return (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym)); -} - -bool -CXWindowsKeyState::isCapsLockSensitive(KeySym keysym) const -{ - KeySym lKey, uKey; - XConvertCase(keysym, &lKey, &uKey); - return (lKey != uKey); -} - - -// -// CXWindowsKeyState::KeyMapping -// - -CXWindowsKeyState::KeyMapping::KeyMapping() -{ - m_keycode[0] = 0; - m_keycode[1] = 0; - m_keycode[2] = 0; - m_keycode[3] = 0; +#endif + return 0; } diff --git a/lib/platform/CXWindowsKeyState.h b/lib/platform/CXWindowsKeyState.h index bb660d48..02ed4520 100644 --- a/lib/platform/CXWindowsKeyState.h +++ b/lib/platform/CXWindowsKeyState.h @@ -17,6 +17,7 @@ #include "CKeyState.h" #include "stdmap.h" +#include "stdvector.h" #if X_DISPLAY_MISSING # error X11 is required to build synergy #else @@ -26,6 +27,9 @@ # else # error The XTest extension is required to build synergy # endif +# if HAVE_XKB_EXTENSION +# include +# endif #endif //! X Windows key state @@ -34,9 +38,35 @@ A key state for X Windows. */ class CXWindowsKeyState : public CKeyState { public: - CXWindowsKeyState(Display*); + typedef std::vector CKeycodeList; + enum { + kGroupPoll = -1, + kGroupPollAndSet = -2 + }; + + CXWindowsKeyState(Display*, bool useXKB); ~CXWindowsKeyState(); + //! @name modifiers + //@{ + + //! Set active group + /*! + Sets the active group to \p group. This is the group returned by + \c pollActiveGroup(). If \p group is \c kGroupPoll then + \c pollActiveGroup() will really poll, but that's a slow operation + on X11. If \p group is \c kGroupPollAndSet then this will poll the + active group now and use it for future calls to \c pollActiveGroup(). + */ + void setActiveGroup(SInt32 group); + + //! Set the auto-repeat state + /*! + Sets the auto-repeat state. + */ + void setAutoRepeat(const XKeyboardState&); + + //@} //! @name accessors //@{ @@ -47,106 +77,79 @@ public: */ KeyModifierMask mapModifiersFromX(unsigned int state) const; + //! Convert synergy modifier mask to X mask + /*! + Converts the synergy modifier mask to the corresponding X modifier + mask. Returns \c true if successful and \c false if any modifier + could not be converted. + */ + bool mapModifiersToX(KeyModifierMask, unsigned int&) const; + + //! Convert synergy key to all corresponding X keycodes + /*! + Converts the synergy key \p key to all of the keycodes that map to + that key. + */ + void mapKeyToKeycodes(KeyID key, + CKeycodeList& keycodes) const; + //@} // IKeyState overrides virtual bool fakeCtrlAltDel(); - virtual const char* getKeyName(KeyButton) const; + virtual KeyModifierMask + pollActiveModifiers() const; + virtual SInt32 pollActiveGroup() const; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const; protected: - // IKeyState overrides - virtual void doUpdateKeys(); - virtual void doFakeKeyEvent(KeyButton button, - bool press, bool isAutoRepeat); - virtual KeyButton mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const; + // CKeyState overrides + virtual void getKeyMap(CKeyMap& keyMap); + virtual void fakeKey(const Keystroke& keystroke); private: - class KeyMapping { + void updateKeysymMap(CKeyMap&); + void updateKeysymMapXKB(CKeyMap&); + bool hasModifiersXKB() const; + int getEffectiveGroup(KeyCode, int group) const; + UInt32 getGroupFromState(unsigned int state) const; + + static void remapKeyModifiers(KeyID, SInt32, + CKeyMap::KeyItem&, void*); + +private: + struct XKBModifierInfo { public: - KeyMapping(); - - public: - // KeyCode to generate keysym and whether keycode[i] is - // sensitive to shift and mode switch. - KeyCode m_keycode[4]; - bool m_shiftSensitive[4]; - bool m_modeSwitchSensitive[4]; - - // the modifier mask of keysym or 0 if not a modifier - KeyModifierMask m_modifierMask; - - // whether keysym is sensitive to caps and num lock - bool m_numLockSensitive; - bool m_capsLockSensitive; + unsigned char m_level; + UInt32 m_mask; + bool m_lock; }; - typedef std::map KeySymMap; - typedef KeySymMap::const_iterator KeySymIndex; - // save the current keyboard mapping and note the modifiers - void updateKeysymMap(); + typedef std::vector KeyModifierMaskList; + typedef std::map KeyModifierToXMask; + typedef std::multimap KeyToKeyCodeMap; + typedef std::map NonXKBModifierMap; + typedef std::map XKBModifierMap; - // note interesting modifier KeySyms - void updateModifiers(); - - // map a modifier index and its KeySym to a modifier mask. also - // save the modifier mask in one of m_*Mask. - KeyModifierMask mapToModifierMask(unsigned int, KeySym); - - // convert a KeyID to a KeySym - KeySym keyIDToKeySym(KeyID id, KeyModifierMask mask) const; - - // map a KeySym into the keystrokes to produce it - KeyButton mapToKeystrokes(Keystrokes& keys, - KeySymIndex keyIndex, - bool isAutoRepeat, - bool pressAndRelease) const; - - // map a decomposition into keystrokes to produce it. returns the - // last key added to keys iff successful and 0 otherwise. - KeyButton mapDecompositionToKeystrokes(Keystrokes& keys, - KeySym keysym, bool usingDeadKeys) const; - - // choose the best set of modifiers to generate the KeySym - unsigned int findBestKeyIndex(KeySymIndex keyIndex, - KeyModifierMask currentMask) const; - - // returns true if the sense of shift is inverted for KeySym - bool isShiftInverted(KeySymIndex keyIndex, - KeyModifierMask currentMask) const; - - // returns the keystrokes to adjust the modifiers into the desired - // state the keystrokes to get back to the current state. - bool adjustModifiers(Keystrokes& keys, - Keystrokes& undo, - KeyModifierMask desiredMask) const; - - // returns true if keysym is sensitive to the NumLock state - bool isNumLockSensitive(KeySym keysym) const; - - // returns true if keysym is sensitive to the CapsLock state - bool isCapsLockSensitive(KeySym keysym) const; - -private: Display* m_display; +#if HAVE_XKB_EXTENSION + XkbDescPtr m_xkb; +#endif + SInt32 m_group; + XKBModifierMap m_lastGoodXKBModifiers; + NonXKBModifierMap m_lastGoodNonXKBModifiers; - // keysym to keycode mapping - KeySymMap m_keysymMap; + // X modifier (bit number) to synergy modifier (mask) mapping + KeyModifierMaskList m_modifierFromX; - // the keyboard control state the last time this screen was entered - XKeyboardState m_keyControl; + // synergy modifier (mask) to X modifier (mask) + KeyModifierToXMask m_modifierToX; - // modifier keysyms - KeySym m_modeSwitchKeysym; + // map KeyID to all keycodes that can synthesize that KeyID + KeyToKeyCodeMap m_keyCodeFromKey; - // modifier masks - unsigned int m_altMask; - unsigned int m_metaMask; - unsigned int m_superMask; - unsigned int m_modeSwitchMask; - unsigned int m_numLockMask; - unsigned int m_scrollLockMask; + // autorepeat state + XKeyboardState m_keyboardState; }; #endif diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index ad62425b..9c4ab134 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -19,6 +19,7 @@ #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CClipboard.h" +#include "CKeyMap.h" #include "XScreen.h" #include "CLog.h" #include "CStopwatch.h" @@ -45,47 +46,12 @@ # include } # endif +# if HAVE_XKB_EXTENSION +# include +# endif #endif #include "CArch.h" -// map "Internet" keys to KeyIDs -static const KeySym g_map1008FF[] = -{ - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp, - /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, - /* 0x18 */ kKeyWWWHome, kKeyAppMail, 0, kKeyWWWSearch, 0, 0, 0, 0, - /* 0x20 */ 0, 0, 0, 0, 0, 0, kKeyWWWBack, kKeyWWWForward, - /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, kKeyEject, 0, 0, 0, - /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, - /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 -}; - // // CXWindowsScreen @@ -123,7 +89,8 @@ CXWindowsScreen::CXWindowsScreen(const char* displayName, bool isPrimary) : m_sequenceNumber(0), m_screensaver(NULL), m_screensaverNotify(false), - m_xtestIsXineramaUnaware(true) + m_xtestIsXineramaUnaware(true), + m_xkb(false) { assert(s_screen == NULL); @@ -139,7 +106,7 @@ CXWindowsScreen::CXWindowsScreen(const char* displayName, bool isPrimary) : m_window = openWindow(); m_screensaver = new CXWindowsScreenSaver(m_display, m_window, getEventTarget()); - m_keyState = new CXWindowsKeyState(m_display); + m_keyState = new CXWindowsKeyState(m_display, m_xkb); LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_xinerama ? "(xinerama)" : "")); LOG((CLOG_DEBUG "window is 0x%08x", m_window)); } @@ -215,6 +182,7 @@ CXWindowsScreen::enable() XKeyboardState keyControl; XGetKeyboardControl(m_display, &keyControl); m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn); + m_keyState->setAutoRepeat(keyControl); // move hider window under the cursor center XMoveWindow(m_display, m_window, m_xCenter, m_yCenter); @@ -226,8 +194,6 @@ CXWindowsScreen::enable() // warp the mouse to the cursor center fakeMouseMove(m_xCenter, m_yCenter); } - - updateKeys(); } void @@ -256,10 +222,6 @@ CXWindowsScreen::enter() XUnsetICFocus(m_ic); } - // unmap the hider/grab window. this also ungrabs the mouse and - // keyboard if they're grabbed. - XUnmapWindow(m_display, m_window); - // set the input focus to what it had been when we took it if (m_lastFocus != None) { // the window may not exist anymore so ignore errors @@ -267,6 +229,10 @@ CXWindowsScreen::enter() XSetInputFocus(m_display, m_lastFocus, m_lastFocusRevert, CurrentTime); } + // unmap the hider/grab window. this also ungrabs the mouse and + // keyboard if they're grabbed. + XUnmapWindow(m_display, m_window); + /* maybe call this if entering for the screensaver // set keyboard focus to root window. the screensaver should then // pick up key events for when the user enters a password to unlock. @@ -278,6 +244,7 @@ CXWindowsScreen::enter() XKeyboardState keyControl; XGetKeyboardControl(m_display, &keyControl); m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn); + m_keyState->setAutoRepeat(keyControl); // turn off auto-repeat. we do this so fake key press events don't // cause the local server to generate their own auto-repeats of @@ -511,6 +478,245 @@ CXWindowsScreen::warpCursor(SInt32 x, SInt32 y) m_yCursor = y; } +UInt32 +CXWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask) +{ + // only allow certain modifiers + if ((mask & ~(KeyModifierShift | KeyModifierControl | + KeyModifierAlt | KeyModifierSuper)) != 0) { + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + + // fail if no keys + if (key == kKeyNone && mask == 0) { + return 0; + } + + // convert to X + unsigned int modifiers; + if (!m_keyState->mapModifiersToX(mask, modifiers)) { + // can't map all modifiers + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + CXWindowsKeyState::CKeycodeList keycodes; + m_keyState->mapKeyToKeycodes(key, keycodes); + if (key != kKeyNone && keycodes.empty()) { + // can't map key + LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask)); + return 0; + } + + // choose hotkey id + UInt32 id; + if (!m_oldHotKeyIDs.empty()) { + id = m_oldHotKeyIDs.back(); + m_oldHotKeyIDs.pop_back(); + } + else { + id = m_hotKeys.size() + 1; + } + HotKeyList& hotKeys = m_hotKeys[id]; + + // all modifier hotkey must be treated specially. for each modifier + // we need to grab the modifier key in combination with all the other + // requested modifiers. + bool err = false; + { + CXWindowsUtil::CErrorLock lock(m_display, &err); + if (key == kKeyNone) { + static const KeyModifierMask s_hotKeyModifiers[] = { + KeyModifierShift, + KeyModifierControl, + KeyModifierAlt, + KeyModifierMeta, + KeyModifierSuper + }; + + XModifierKeymap* modKeymap = XGetModifierMapping(m_display); + for (size_t j = 0; j < sizeof(s_hotKeyModifiers) / + sizeof(s_hotKeyModifiers[0]) && !err; ++j) { + // skip modifier if not in mask + if ((mask & s_hotKeyModifiers[j]) == 0) { + continue; + } + + // skip with error if we can't map remaining modifiers + unsigned int modifiers2; + KeyModifierMask mask2 = (mask & ~s_hotKeyModifiers[j]); + if (!m_keyState->mapModifiersToX(mask2, modifiers2)) { + err = true; + continue; + } + + // compute modifier index for modifier. there should be + // exactly one X modifier missing + int index; + switch (modifiers ^ modifiers2) { + case ShiftMask: + index = ShiftMapIndex; + break; + + case LockMask: + index = LockMapIndex; + break; + + case ControlMask: + index = ControlMapIndex; + break; + + case Mod1Mask: + index = Mod1MapIndex; + break; + + case Mod2Mask: + index = Mod2MapIndex; + break; + + case Mod3Mask: + index = Mod3MapIndex; + break; + + case Mod4Mask: + index = Mod4MapIndex; + break; + + case Mod5Mask: + index = Mod5MapIndex; + break; + + default: + err = true; + continue; + } + + // grab each key for the modifier + const KeyCode* modifiermap = + modKeymap->modifiermap + index * modKeymap->max_keypermod; + for (int k = 0; k < modKeymap->max_keypermod && !err; ++k) { + KeyCode code = modifiermap[k]; + if (modifiermap[k] != 0) { + XGrabKey(m_display, code, modifiers2, m_root, + False, GrabModeAsync, GrabModeAsync); + if (!err) { + hotKeys.push_back(std::make_pair(code, modifiers2)); + m_hotKeyToIDMap[CHotKeyItem(code, modifiers2)] = id; + } + } + } + } + XFreeModifiermap(modKeymap); + } + + // a non-modifier key must be insensitive to CapsLock, NumLock and + // ScrollLock, so we have to grab the key with every combination of + // those. + else { + // collect available toggle modifiers + unsigned int modifier; + unsigned int toggleModifiers[3]; + size_t numToggleModifiers = 0; + if (m_keyState->mapModifiersToX(KeyModifierCapsLock, modifier)) { + toggleModifiers[numToggleModifiers++] = modifier; + } + if (m_keyState->mapModifiersToX(KeyModifierNumLock, modifier)) { + toggleModifiers[numToggleModifiers++] = modifier; + } + if (m_keyState->mapModifiersToX(KeyModifierScrollLock, modifier)) { + toggleModifiers[numToggleModifiers++] = modifier; + } + + + for (CXWindowsKeyState::CKeycodeList::iterator j = keycodes.begin(); + j != keycodes.end() && !err; ++j) { + for (size_t i = 0; i < (1u << numToggleModifiers); ++i) { + // add toggle modifiers for index i + unsigned int tmpModifiers = modifiers; + if ((i & 1) != 0) { + tmpModifiers |= toggleModifiers[0]; + } + if ((i & 2) != 0) { + tmpModifiers |= toggleModifiers[1]; + } + if ((i & 4) != 0) { + tmpModifiers |= toggleModifiers[2]; + } + + // add grab + XGrabKey(m_display, *j, tmpModifiers, m_root, + False, GrabModeAsync, GrabModeAsync); + if (!err) { + hotKeys.push_back(std::make_pair(*j, tmpModifiers)); + m_hotKeyToIDMap[CHotKeyItem(*j, tmpModifiers)] = id; + } + } + } + } + } + + if (err) { + // if any failed then unregister any we did get + for (HotKeyList::iterator j = hotKeys.begin(); + j != hotKeys.end(); ++j) { + XUngrabKey(m_display, j->first, j->second, m_root); + m_hotKeyToIDMap.erase(CHotKeyItem(j->first, j->second)); + } + + m_oldHotKeyIDs.push_back(id); + m_hotKeys.erase(id); + LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", CKeyMap::formatKey(key, mask).c_str(), key, mask)); + return 0; + } + + LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", CKeyMap::formatKey(key, mask).c_str(), key, mask, id)); + return id; +} + +void +CXWindowsScreen::unregisterHotKey(UInt32 id) +{ + // look up hotkey + HotKeyMap::iterator i = m_hotKeys.find(id); + if (i == m_hotKeys.end()) { + return; + } + + // unregister with OS + bool err = false; + { + CXWindowsUtil::CErrorLock lock(m_display, &err); + HotKeyList& hotKeys = i->second; + for (HotKeyList::iterator j = hotKeys.begin(); + j != hotKeys.end(); ++j) { + XUngrabKey(m_display, j->first, j->second, m_root); + m_hotKeyToIDMap.erase(CHotKeyItem(j->first, j->second)); + } + } + if (err) { + LOG((CLOG_WARN "failed to unregister hotkey id=%d", id)); + } + else { + LOG((CLOG_DEBUG "unregistered hotkey id=%d", id)); + } + + // discard hot key from map and record old id for reuse + m_hotKeys.erase(i); + m_oldHotKeyIDs.push_back(id); +} + +void +CXWindowsScreen::fakeInputBegin() +{ + // FIXME -- not implemented +} + +void +CXWindowsScreen::fakeInputEnd() +{ + // FIXME -- not implemented +} + SInt32 CXWindowsScreen::getJumpZoneSize() const { @@ -578,17 +784,22 @@ CXWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const } void -CXWindowsScreen::fakeMouseWheel(SInt32 delta) const +CXWindowsScreen::fakeMouseWheel(SInt32, SInt32 yDelta) const { + // XXX -- support x-axis scrolling + if (yDelta == 0) { + return; + } + // choose button depending on rotation direction const unsigned int xButton = mapButtonToX(static_cast( - (delta >= 0) ? -1 : -2)); + (yDelta >= 0) ? -1 : -2)); if (xButton == 0) { // If we get here, then the XServer does not support the scroll // wheel buttons, so send PageUp/PageDown keystrokes instead. // Patch by Tom Chadwick. KeyCode keycode = 0; - if (delta >= 0) { + if (yDelta >= 0) { keycode = XKeysymToKeycode(m_display, XK_Page_Up); } else { @@ -602,12 +813,12 @@ CXWindowsScreen::fakeMouseWheel(SInt32 delta) const } // now use absolute value of delta - if (delta < 0) { - delta = -delta; + if (yDelta < 0) { + yDelta = -yDelta; } // send as many clicks as necessary - for (; delta >= 120; delta -= 120) { + for (; yDelta >= 120; yDelta -= 120) { XTestFakeButtonEvent(m_display, xButton, True, CurrentTime); XTestFakeButtonEvent(m_display, xButton, False, CurrentTime); } @@ -615,7 +826,7 @@ CXWindowsScreen::fakeMouseWheel(SInt32 delta) const } Display* -CXWindowsScreen::openDisplay(const char* displayName) const +CXWindowsScreen::openDisplay(const char* displayName) { // get the DISPLAY if (displayName == NULL) { @@ -643,6 +854,25 @@ CXWindowsScreen::openDisplay(const char* displayName) const } } +#if HAVE_XKB_EXTENSION + { + m_xkb = false; + int major = XkbMajorVersion, minor = XkbMinorVersion; + if (XkbLibraryVersion(&major, &minor)) { + int opcode, firstError; + if (XkbQueryExtension(display, &opcode, &m_xkbEventBase, + &firstError, &major, &minor)) { + m_xkb = true; + XkbSelectEvents(display, XkbUseCoreKbd, + XkbMapNotifyMask, XkbMapNotifyMask); + XkbSelectEventDetails(display, XkbUseCoreKbd, + XkbStateNotifyMask, + XkbGroupStateMask, XkbGroupStateMask); + } + } + } +#endif + return display; } @@ -864,10 +1094,22 @@ CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) } if (xevent->type == KeyPress || xevent->type == KeyRelease) { - if (!isRepeat) { - m_keyState->setKeyDown(xevent->xkey.keycode, - xevent->type == KeyPress); + if (xevent->xkey.window == m_root) { + // this is a hot key + onHotKey(xevent->xkey, isRepeat); + return; } + else if (!m_isOnScreen) { + // this might be a hot key + if (onHotKey(xevent->xkey, isRepeat)) { + return; + } + } + + bool down = (isRepeat || xevent->type == KeyPress); + KeyModifierMask state = + m_keyState->mapModifiersFromX(xevent->xkey.state); + m_keyState->onKey(xevent->xkey.keycode, down, state); } } @@ -924,19 +1166,7 @@ CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) break; case MappingNotify: - if (XPending(m_display) > 0) { - XEvent tmpEvent; - XPeekEvent(m_display, &tmpEvent); - if (tmpEvent.type == MappingNotify) { - // discard this MappingNotify since another follows. - // we tend to get a bunch of these in a row. - return; - } - } - - // keyboard mapping changed - XRefreshKeyboardMapping(&xevent->xmapping); - m_keyState->updateKeys(); + refreshKeyboard(xevent); break; case LeaveNotify: @@ -1036,6 +1266,21 @@ CXWindowsScreen::handleSystemEvent(const CEvent& event, void*) return; default: +#if HAVE_XKB_EXTENSION + if (m_xkb && xevent->type == m_xkbEventBase) { + XkbEvent* xkbEvent = reinterpret_cast(xevent); + switch (xkbEvent->any.xkb_type) { + case XkbMapNotify: + refreshKeyboard(xevent); + return; + + case XkbStateNotify: + LOG((CLOG_INFO "group change: %d", xkbEvent->state.group)); + m_keyState->setActiveGroup((SInt32)xkbEvent->state.group); + return; + } + } +#endif break; } } @@ -1116,13 +1361,44 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat) } } +bool +CXWindowsScreen::onHotKey(XKeyEvent& xkey, bool isRepeat) +{ + // find the hot key id + HotKeyToIDMap::const_iterator i = + m_hotKeyToIDMap.find(CHotKeyItem(xkey.keycode, xkey.state)); + if (i == m_hotKeyToIDMap.end()) { + return false; + } + + // find what kind of event + CEvent::Type type; + if (xkey.type == KeyPress) { + type = getHotKeyDownEvent(); + } + else if (xkey.type == KeyRelease) { + type = getHotKeyUpEvent(); + } + else { + return false; + } + + // generate event (ignore key repeats) + if (!isRepeat) { + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), + CHotKeyInfo::alloc(i->second))); + } + return true; +} + void CXWindowsScreen::onMousePress(const XButtonEvent& xbutton) { LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button)); - const ButtonID button = mapButtonFromX(&xbutton); + ButtonID button = mapButtonFromX(&xbutton); + KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state); if (button != kButtonNone) { - sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); + sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button, mask)); } } @@ -1130,18 +1406,20 @@ void CXWindowsScreen::onMouseRelease(const XButtonEvent& xbutton) { LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button)); - const ButtonID button = mapButtonFromX(&xbutton); + ButtonID button = mapButtonFromX(&xbutton); + KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state); if (button != kButtonNone) { - sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); + sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button, mask)); } else if (xbutton.button == 4) { // wheel forward (away from user) - sendEvent(getWheelEvent(), CWheelInfo::alloc(120)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(0, 120)); } else if (xbutton.button == 5) { // wheel backward (toward user) - sendEvent(getWheelEvent(), CWheelInfo::alloc(-120)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(0, -120)); } + // XXX -- support x-axis scrolling } void @@ -1409,41 +1687,7 @@ CXWindowsScreen::mapKeyFromX(XKeyEvent* event) const } // convert key - switch (keysym & 0xffffff00) { - case 0x0000: - // Latin-1 - return static_cast(keysym); - - case 0xfe00: - // ISO 9995 Function and Modifier Keys - if (keysym == XK_ISO_Left_Tab) { - return kKeyLeftTab; - } - if (keysym == XK_ISO_Level3_Shift) { - // treat ISO_Level3_Shift as ModeSwitch - return kKeyModeSwitch; - } - return kKeyNone; - - case 0xff00: - // MISCELLANY - return static_cast(keysym - 0xff00 + 0xef00); - - case 0x1008ff00: - // "Internet" keys - return g_map1008FF[keysym & 0xff]; - - default: { - // lookup character in table - UInt32 key = CXWindowsUtil::mapKeySymToUCS4(keysym); - if (key != 0x0000ffff) { - return static_cast(key); - } - - // unknown character - return kKeyNone; - } - } + return CXWindowsUtil::mapKeySymToKeyID(keysym); } ButtonID @@ -1608,3 +1852,50 @@ CXWindowsScreen::grabMouseAndKeyboard() LOG((CLOG_DEBUG1 "grabbed pointer and keyboard")); return true; } + +void +CXWindowsScreen::refreshKeyboard(XEvent* event) +{ + if (XPending(m_display) > 0) { + XEvent tmpEvent; + XPeekEvent(m_display, &tmpEvent); + if (tmpEvent.type == MappingNotify) { + // discard this event since another follows. + // we tend to get a bunch of these in a row. + return; + } + } + + // keyboard mapping changed +#if HAVE_XKB_EXTENSION + if (m_xkb && event->type == m_xkbEventBase) { + XkbRefreshKeyboardMapping((XkbMapNotifyEvent*)event); + } + else +#else + { + XRefreshKeyboardMapping(&event->xmapping); + } +#endif + m_keyState->updateKeyMap(); + m_keyState->updateKeyState(); +} + + +// +// CXWindowsScreen::CHotKeyItem +// + +CXWindowsScreen::CHotKeyItem::CHotKeyItem(int keycode, unsigned int mask) : + m_keycode(keycode), + m_mask(mask) +{ + // do nothing +} + +bool +CXWindowsScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const +{ + return (m_keycode < x.m_keycode || + (m_keycode == x.m_keycode && m_mask < x.m_mask)); +} diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index cec6c6fa..99c45576 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -49,6 +49,10 @@ public: // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); + virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask); + virtual void unregisterHotKey(UInt32 id); + virtual void fakeInputBegin(); + virtual void fakeInputEnd(); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; virtual void getCursorCenter(SInt32& x, SInt32& y) const; @@ -57,7 +61,7 @@ public: virtual void fakeMouseButton(ButtonID id, bool press) const; virtual void fakeMouseMove(SInt32 x, SInt32 y) const; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const; - virtual void fakeMouseWheel(SInt32 delta) const; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const; // IPlatformScreen overrides virtual void enable(); @@ -112,7 +116,7 @@ private: KeyCode m_keycode; }; - Display* openDisplay(const char* displayName) const; + Display* openDisplay(const char* displayName); void saveShape(); Window openWindow() const; void openIM(); @@ -120,6 +124,7 @@ private: bool grabMouseAndKeyboard(); void onKeyPress(XKeyEvent&); void onKeyRelease(XKeyEvent&, bool isRepeat); + bool onHotKey(XKeyEvent&, bool isRepeat); void onMousePress(const XButtonEvent&); void onMouseRelease(const XButtonEvent&); void onMouseMove(const XMotionEvent&); @@ -133,10 +138,27 @@ private: void warpCursorNoFlush(SInt32 x, SInt32 y); + void refreshKeyboard(XEvent*); + static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: + struct CHotKeyItem { + public: + CHotKeyItem(int, unsigned int); + + bool operator<(const CHotKeyItem&) const; + + private: + int m_keycode; + unsigned int m_mask; + }; typedef std::set CFilteredKeycodes; + typedef std::vector > HotKeyList; + typedef std::map HotKeyMap; + typedef std::vector HotKeyIDList; + typedef std::map HotKeyToIDMap; + // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; @@ -158,6 +180,11 @@ private: // keyboard stuff CXWindowsKeyState* m_keyState; + // hot key stuff + HotKeyMap m_hotKeys; + HotKeyIDList m_oldHotKeyIDs; + HotKeyToIDMap m_hotKeyToIDMap; + // input focus stuff Window m_lastFocus; int m_lastFocusRevert; @@ -190,6 +217,10 @@ private: bool m_xtestIsXineramaUnaware; bool m_xinerama; + // XKB extension stuff + bool m_xkb; + int m_xkbEventBase; + // pointer to (singleton) screen. this is only needed by // ioErrorHandler(). static CXWindowsScreen* s_screen; diff --git a/lib/platform/CXWindowsScreenSaver.cpp b/lib/platform/CXWindowsScreenSaver.cpp index 6c8efbde..65af34e1 100644 --- a/lib/platform/CXWindowsScreenSaver.cpp +++ b/lib/platform/CXWindowsScreenSaver.cpp @@ -62,7 +62,8 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( m_dpms(false), m_disabled(false), m_suppressDisable(false), - m_disableTimer(NULL) + m_disableTimer(NULL), + m_disablePos(0) { // get atoms m_atomScreenSaver = XInternAtom(m_display, @@ -87,18 +88,18 @@ CXWindowsScreenSaver::CXWindowsScreenSaver( #endif // watch top-level windows for changes + bool error = false; { - bool error = false; CXWindowsUtil::CErrorLock lock(m_display, &error); Window root = DefaultRootWindow(m_display); XWindowAttributes attr; XGetWindowAttributes(m_display, root, &attr); m_rootEventMask = attr.your_event_mask; XSelectInput(m_display, root, m_rootEventMask | SubstructureNotifyMask); - if (error) { - LOG((CLOG_DEBUG "didn't set root event mask")); - m_rootEventMask = 0; - } + } + if (error) { + LOG((CLOG_DEBUG "didn't set root event mask")); + m_rootEventMask = 0; } // get the built-in settings @@ -346,9 +347,11 @@ CXWindowsScreenSaver::setXScreenSaver(Window window) // see if xscreensaver is active bool error = false; - CXWindowsUtil::CErrorLock lock(m_display, &error); XWindowAttributes attr; - XGetWindowAttributes(m_display, m_xscreensaver, &attr); + { + CXWindowsUtil::CErrorLock lock(m_display, &error); + XGetWindowAttributes(m_display, m_xscreensaver, &attr); + } setXScreenSaverActive(!error && attr.map_state != IsUnmapped); // save current DPMS state; xscreensaver may have changed it. @@ -418,8 +421,10 @@ CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2) LOG((CLOG_DEBUG "send xscreensaver command: %d %d %d", (long)cmd, arg1, arg2)); bool error = false; - CXWindowsUtil::CErrorLock lock(m_display, &error); - XSendEvent(m_display, m_xscreensaver, False, 0, &event); + { + CXWindowsUtil::CErrorLock lock(m_display, &error); + XSendEvent(m_display, m_xscreensaver, False, 0, &event); + } if (error) { findXScreenSaver(); } @@ -465,18 +470,23 @@ CXWindowsScreenSaver::clearWatchForXScreenSaver() void CXWindowsScreenSaver::addWatchXScreenSaver(Window window) { - bool error = false; - CXWindowsUtil::CErrorLock lock(m_display, &error); - // get window attributes + bool error = false; XWindowAttributes attr; - XGetWindowAttributes(m_display, window, &attr); + { + CXWindowsUtil::CErrorLock lock(m_display, &error); + XGetWindowAttributes(m_display, window, &attr); + } // if successful and window uses override_redirect (like xscreensaver // does) then watch it for property changes. if (!error && attr.override_redirect == True) { - XSelectInput(m_display, window, + error = false; + { + CXWindowsUtil::CErrorLock lock(m_display, &error); + XSelectInput(m_display, window, attr.your_event_mask | PropertyChangeMask); + } if (!error) { // if successful then add the window to our list m_watchWindows.insert(std::make_pair(window, attr.your_event_mask)); @@ -509,9 +519,9 @@ CXWindowsScreenSaver::handleDisableTimer(const CEvent&, void*) event.xmotion.root = DefaultRootWindow(m_display); event.xmotion.subwindow = None; event.xmotion.time = CurrentTime; - event.xmotion.x = 0; + event.xmotion.x = m_disablePos; event.xmotion.y = 0; - event.xmotion.x_root = 0; + event.xmotion.x_root = m_disablePos; event.xmotion.y_root = 0; event.xmotion.state = 0; event.xmotion.is_hint = NotifyNormal; @@ -519,6 +529,8 @@ CXWindowsScreenSaver::handleDisableTimer(const CEvent&, void*) CXWindowsUtil::CErrorLock lock(m_display); XSendEvent(m_display, m_xscreensaver, False, 0, &event); + + m_disablePos = 20 - m_disablePos; } } diff --git a/lib/platform/CXWindowsScreenSaver.h b/lib/platform/CXWindowsScreenSaver.h index 193e1e7b..991b20e1 100644 --- a/lib/platform/CXWindowsScreenSaver.h +++ b/lib/platform/CXWindowsScreenSaver.h @@ -154,6 +154,11 @@ private: // the disable timer (NULL if not installed) CEventQueueTimer* m_disableTimer; + + // fake mouse motion position for suppressing the screen saver. + // xscreensaver since 2.21 requires the mouse to move more than 10 + // pixels to be considered significant. + SInt32 m_disablePos; }; #endif diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 440be0ed..9478d655 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -13,19 +13,46 @@ */ #include "CXWindowsUtil.h" +#include "KeyTypes.h" #include "CThread.h" #include "CLog.h" #include "CStringUtil.h" #include -#define XK_MISCELLANY -#define XK_XKB_KEYS +#define XK_APL +#define XK_ARABIC +#define XK_ARMENIAN +#define XK_CAUCASUS +#define XK_CURRENCY +#define XK_CYRILLIC +#define XK_GEORGIAN +#define XK_GREEK +#define XK_HEBREW +#define XK_KATAKANA +#define XK_KOREAN #define XK_LATIN1 #define XK_LATIN2 #define XK_LATIN3 #define XK_LATIN4 #define XK_LATIN8 #define XK_LATIN9 -#include +#define XK_MISCELLANY +#define XK_PUBLISHING +#define XK_SPECIAL +#define XK_TECHNICAL +#define XK_THAI +#define XK_VIETNAMESE +#define XK_XKB_KEYS +#include + +#if !defined(XK_OE) +#define XK_OE 0x13bc +#endif +#if !defined(XK_oe) +#define XK_oe 0x13bd +#endif +#if !defined(XK_Ydiaeresis) +#define XK_Ydiaeresis 0x13be +#endif /* * This table maps keysym values into the corresponding ISO 10646 @@ -47,1094 +74,1189 @@ struct codepair { KeySym keysym; UInt32 ucs4; } s_keymap[] = { -{ 0x01a1, 0x0104 }, /* Aogonek LATIN CAPITAL LETTER A WITH OGONEK */ -{ 0x01a2, 0x02d8 }, /* breve BREVE */ -{ 0x01a3, 0x0141 }, /* Lstroke LATIN CAPITAL LETTER L WITH STROKE */ -{ 0x01a5, 0x013d }, /* Lcaron LATIN CAPITAL LETTER L WITH CARON */ -{ 0x01a6, 0x015a }, /* Sacute LATIN CAPITAL LETTER S WITH ACUTE */ -{ 0x01a9, 0x0160 }, /* Scaron LATIN CAPITAL LETTER S WITH CARON */ -{ 0x01aa, 0x015e }, /* Scedilla LATIN CAPITAL LETTER S WITH CEDILLA */ -{ 0x01ab, 0x0164 }, /* Tcaron LATIN CAPITAL LETTER T WITH CARON */ -{ 0x01ac, 0x0179 }, /* Zacute LATIN CAPITAL LETTER Z WITH ACUTE */ -{ 0x01ae, 0x017d }, /* Zcaron LATIN CAPITAL LETTER Z WITH CARON */ -{ 0x01af, 0x017b }, /* Zabovedot LATIN CAPITAL LETTER Z WITH DOT ABOVE */ -{ 0x01b1, 0x0105 }, /* aogonek LATIN SMALL LETTER A WITH OGONEK */ -{ 0x01b2, 0x02db }, /* ogonek OGONEK */ -{ 0x01b3, 0x0142 }, /* lstroke LATIN SMALL LETTER L WITH STROKE */ -{ 0x01b5, 0x013e }, /* lcaron LATIN SMALL LETTER L WITH CARON */ -{ 0x01b6, 0x015b }, /* sacute LATIN SMALL LETTER S WITH ACUTE */ -{ 0x01b7, 0x02c7 }, /* caron CARON */ -{ 0x01b9, 0x0161 }, /* scaron LATIN SMALL LETTER S WITH CARON */ -{ 0x01ba, 0x015f }, /* scedilla LATIN SMALL LETTER S WITH CEDILLA */ -{ 0x01bb, 0x0165 }, /* tcaron LATIN SMALL LETTER T WITH CARON */ -{ 0x01bc, 0x017a }, /* zacute LATIN SMALL LETTER Z WITH ACUTE */ -{ 0x01bd, 0x02dd }, /* doubleacute DOUBLE ACUTE ACCENT */ -{ 0x01be, 0x017e }, /* zcaron LATIN SMALL LETTER Z WITH CARON */ -{ 0x01bf, 0x017c }, /* zabovedot LATIN SMALL LETTER Z WITH DOT ABOVE */ -{ 0x01c0, 0x0154 }, /* Racute LATIN CAPITAL LETTER R WITH ACUTE */ -{ 0x01c3, 0x0102 }, /* Abreve LATIN CAPITAL LETTER A WITH BREVE */ -{ 0x01c5, 0x0139 }, /* Lacute LATIN CAPITAL LETTER L WITH ACUTE */ -{ 0x01c6, 0x0106 }, /* Cacute LATIN CAPITAL LETTER C WITH ACUTE */ -{ 0x01c8, 0x010c }, /* Ccaron LATIN CAPITAL LETTER C WITH CARON */ -{ 0x01ca, 0x0118 }, /* Eogonek LATIN CAPITAL LETTER E WITH OGONEK */ -{ 0x01cc, 0x011a }, /* Ecaron LATIN CAPITAL LETTER E WITH CARON */ -{ 0x01cf, 0x010e }, /* Dcaron LATIN CAPITAL LETTER D WITH CARON */ -{ 0x01d0, 0x0110 }, /* Dstroke LATIN CAPITAL LETTER D WITH STROKE */ -{ 0x01d1, 0x0143 }, /* Nacute LATIN CAPITAL LETTER N WITH ACUTE */ -{ 0x01d2, 0x0147 }, /* Ncaron LATIN CAPITAL LETTER N WITH CARON */ -{ 0x01d5, 0x0150 }, /* Odoubleacute LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ -{ 0x01d8, 0x0158 }, /* Rcaron LATIN CAPITAL LETTER R WITH CARON */ -{ 0x01d9, 0x016e }, /* Uring LATIN CAPITAL LETTER U WITH RING ABOVE */ -{ 0x01db, 0x0170 }, /* Udoubleacute LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ -{ 0x01de, 0x0162 }, /* Tcedilla LATIN CAPITAL LETTER T WITH CEDILLA */ -{ 0x01e0, 0x0155 }, /* racute LATIN SMALL LETTER R WITH ACUTE */ -{ 0x01e3, 0x0103 }, /* abreve LATIN SMALL LETTER A WITH BREVE */ -{ 0x01e5, 0x013a }, /* lacute LATIN SMALL LETTER L WITH ACUTE */ -{ 0x01e6, 0x0107 }, /* cacute LATIN SMALL LETTER C WITH ACUTE */ -{ 0x01e8, 0x010d }, /* ccaron LATIN SMALL LETTER C WITH CARON */ -{ 0x01ea, 0x0119 }, /* eogonek LATIN SMALL LETTER E WITH OGONEK */ -{ 0x01ec, 0x011b }, /* ecaron LATIN SMALL LETTER E WITH CARON */ -{ 0x01ef, 0x010f }, /* dcaron LATIN SMALL LETTER D WITH CARON */ -{ 0x01f0, 0x0111 }, /* dstroke LATIN SMALL LETTER D WITH STROKE */ -{ 0x01f1, 0x0144 }, /* nacute LATIN SMALL LETTER N WITH ACUTE */ -{ 0x01f2, 0x0148 }, /* ncaron LATIN SMALL LETTER N WITH CARON */ -{ 0x01f5, 0x0151 }, /* odoubleacute LATIN SMALL LETTER O WITH DOUBLE ACUTE */ -{ 0x01f8, 0x0159 }, /* rcaron LATIN SMALL LETTER R WITH CARON */ -{ 0x01f9, 0x016f }, /* uring LATIN SMALL LETTER U WITH RING ABOVE */ -{ 0x01fb, 0x0171 }, /* udoubleacute LATIN SMALL LETTER U WITH DOUBLE ACUTE */ -{ 0x01fe, 0x0163 }, /* tcedilla LATIN SMALL LETTER T WITH CEDILLA */ -{ 0x01ff, 0x02d9 }, /* abovedot DOT ABOVE */ -{ 0x02a1, 0x0126 }, /* Hstroke LATIN CAPITAL LETTER H WITH STROKE */ -{ 0x02a6, 0x0124 }, /* Hcircumflex LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ -{ 0x02a9, 0x0130 }, /* Iabovedot LATIN CAPITAL LETTER I WITH DOT ABOVE */ -{ 0x02ab, 0x011e }, /* Gbreve LATIN CAPITAL LETTER G WITH BREVE */ -{ 0x02ac, 0x0134 }, /* Jcircumflex LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ -{ 0x02b1, 0x0127 }, /* hstroke LATIN SMALL LETTER H WITH STROKE */ -{ 0x02b6, 0x0125 }, /* hcircumflex LATIN SMALL LETTER H WITH CIRCUMFLEX */ -{ 0x02b9, 0x0131 }, /* idotless LATIN SMALL LETTER DOTLESS I */ -{ 0x02bb, 0x011f }, /* gbreve LATIN SMALL LETTER G WITH BREVE */ -{ 0x02bc, 0x0135 }, /* jcircumflex LATIN SMALL LETTER J WITH CIRCUMFLEX */ -{ 0x02c5, 0x010a }, /* Cabovedot LATIN CAPITAL LETTER C WITH DOT ABOVE */ -{ 0x02c6, 0x0108 }, /* Ccircumflex LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ -{ 0x02d5, 0x0120 }, /* Gabovedot LATIN CAPITAL LETTER G WITH DOT ABOVE */ -{ 0x02d8, 0x011c }, /* Gcircumflex LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ -{ 0x02dd, 0x016c }, /* Ubreve LATIN CAPITAL LETTER U WITH BREVE */ -{ 0x02de, 0x015c }, /* Scircumflex LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ -{ 0x02e5, 0x010b }, /* cabovedot LATIN SMALL LETTER C WITH DOT ABOVE */ -{ 0x02e6, 0x0109 }, /* ccircumflex LATIN SMALL LETTER C WITH CIRCUMFLEX */ -{ 0x02f5, 0x0121 }, /* gabovedot LATIN SMALL LETTER G WITH DOT ABOVE */ -{ 0x02f8, 0x011d }, /* gcircumflex LATIN SMALL LETTER G WITH CIRCUMFLEX */ -{ 0x02fd, 0x016d }, /* ubreve LATIN SMALL LETTER U WITH BREVE */ -{ 0x02fe, 0x015d }, /* scircumflex LATIN SMALL LETTER S WITH CIRCUMFLEX */ -{ 0x03a2, 0x0138 }, /* kra LATIN SMALL LETTER KRA */ -{ 0x03a3, 0x0156 }, /* Rcedilla LATIN CAPITAL LETTER R WITH CEDILLA */ -{ 0x03a5, 0x0128 }, /* Itilde LATIN CAPITAL LETTER I WITH TILDE */ -{ 0x03a6, 0x013b }, /* Lcedilla LATIN CAPITAL LETTER L WITH CEDILLA */ -{ 0x03aa, 0x0112 }, /* Emacron LATIN CAPITAL LETTER E WITH MACRON */ -{ 0x03ab, 0x0122 }, /* Gcedilla LATIN CAPITAL LETTER G WITH CEDILLA */ -{ 0x03ac, 0x0166 }, /* Tslash LATIN CAPITAL LETTER T WITH STROKE */ -{ 0x03b3, 0x0157 }, /* rcedilla LATIN SMALL LETTER R WITH CEDILLA */ -{ 0x03b5, 0x0129 }, /* itilde LATIN SMALL LETTER I WITH TILDE */ -{ 0x03b6, 0x013c }, /* lcedilla LATIN SMALL LETTER L WITH CEDILLA */ -{ 0x03ba, 0x0113 }, /* emacron LATIN SMALL LETTER E WITH MACRON */ -{ 0x03bb, 0x0123 }, /* gcedilla LATIN SMALL LETTER G WITH CEDILLA */ -{ 0x03bc, 0x0167 }, /* tslash LATIN SMALL LETTER T WITH STROKE */ -{ 0x03bd, 0x014a }, /* ENG LATIN CAPITAL LETTER ENG */ -{ 0x03bf, 0x014b }, /* eng LATIN SMALL LETTER ENG */ -{ 0x03c0, 0x0100 }, /* Amacron LATIN CAPITAL LETTER A WITH MACRON */ -{ 0x03c7, 0x012e }, /* Iogonek LATIN CAPITAL LETTER I WITH OGONEK */ -{ 0x03cc, 0x0116 }, /* Eabovedot LATIN CAPITAL LETTER E WITH DOT ABOVE */ -{ 0x03cf, 0x012a }, /* Imacron LATIN CAPITAL LETTER I WITH MACRON */ -{ 0x03d1, 0x0145 }, /* Ncedilla LATIN CAPITAL LETTER N WITH CEDILLA */ -{ 0x03d2, 0x014c }, /* Omacron LATIN CAPITAL LETTER O WITH MACRON */ -{ 0x03d3, 0x0136 }, /* Kcedilla LATIN CAPITAL LETTER K WITH CEDILLA */ -{ 0x03d9, 0x0172 }, /* Uogonek LATIN CAPITAL LETTER U WITH OGONEK */ -{ 0x03dd, 0x0168 }, /* Utilde LATIN CAPITAL LETTER U WITH TILDE */ -{ 0x03de, 0x016a }, /* Umacron LATIN CAPITAL LETTER U WITH MACRON */ -{ 0x03e0, 0x0101 }, /* amacron LATIN SMALL LETTER A WITH MACRON */ -{ 0x03e7, 0x012f }, /* iogonek LATIN SMALL LETTER I WITH OGONEK */ -{ 0x03ec, 0x0117 }, /* eabovedot LATIN SMALL LETTER E WITH DOT ABOVE */ -{ 0x03ef, 0x012b }, /* imacron LATIN SMALL LETTER I WITH MACRON */ -{ 0x03f1, 0x0146 }, /* ncedilla LATIN SMALL LETTER N WITH CEDILLA */ -{ 0x03f2, 0x014d }, /* omacron LATIN SMALL LETTER O WITH MACRON */ -{ 0x03f3, 0x0137 }, /* kcedilla LATIN SMALL LETTER K WITH CEDILLA */ -{ 0x03f9, 0x0173 }, /* uogonek LATIN SMALL LETTER U WITH OGONEK */ -{ 0x03fd, 0x0169 }, /* utilde LATIN SMALL LETTER U WITH TILDE */ -{ 0x03fe, 0x016b }, /* umacron LATIN SMALL LETTER U WITH MACRON */ -{ 0x047e, 0x203e }, /* overline OVERLINE */ -{ 0x04a1, 0x3002 }, /* kana_fullstop IDEOGRAPHIC FULL STOP */ -{ 0x04a2, 0x300c }, /* kana_openingbracket LEFT CORNER BRACKET */ -{ 0x04a3, 0x300d }, /* kana_closingbracket RIGHT CORNER BRACKET */ -{ 0x04a4, 0x3001 }, /* kana_comma IDEOGRAPHIC COMMA */ -{ 0x04a5, 0x30fb }, /* kana_conjunctive KATAKANA MIDDLE DOT */ -{ 0x04a6, 0x30f2 }, /* kana_WO KATAKANA LETTER WO */ -{ 0x04a7, 0x30a1 }, /* kana_a KATAKANA LETTER SMALL A */ -{ 0x04a8, 0x30a3 }, /* kana_i KATAKANA LETTER SMALL I */ -{ 0x04a9, 0x30a5 }, /* kana_u KATAKANA LETTER SMALL U */ -{ 0x04aa, 0x30a7 }, /* kana_e KATAKANA LETTER SMALL E */ -{ 0x04ab, 0x30a9 }, /* kana_o KATAKANA LETTER SMALL O */ -{ 0x04ac, 0x30e3 }, /* kana_ya KATAKANA LETTER SMALL YA */ -{ 0x04ad, 0x30e5 }, /* kana_yu KATAKANA LETTER SMALL YU */ -{ 0x04ae, 0x30e7 }, /* kana_yo KATAKANA LETTER SMALL YO */ -{ 0x04af, 0x30c3 }, /* kana_tsu KATAKANA LETTER SMALL TU */ -{ 0x04b0, 0x30fc }, /* prolongedsound KATAKANA-HIRAGANA PROLONGED SOUND MARK */ -{ 0x04b1, 0x30a2 }, /* kana_A KATAKANA LETTER A */ -{ 0x04b2, 0x30a4 }, /* kana_I KATAKANA LETTER I */ -{ 0x04b3, 0x30a6 }, /* kana_U KATAKANA LETTER U */ -{ 0x04b4, 0x30a8 }, /* kana_E KATAKANA LETTER E */ -{ 0x04b5, 0x30aa }, /* kana_O KATAKANA LETTER O */ -{ 0x04b6, 0x30ab }, /* kana_KA KATAKANA LETTER KA */ -{ 0x04b7, 0x30ad }, /* kana_KI KATAKANA LETTER KI */ -{ 0x04b8, 0x30af }, /* kana_KU KATAKANA LETTER KU */ -{ 0x04b9, 0x30b1 }, /* kana_KE KATAKANA LETTER KE */ -{ 0x04ba, 0x30b3 }, /* kana_KO KATAKANA LETTER KO */ -{ 0x04bb, 0x30b5 }, /* kana_SA KATAKANA LETTER SA */ -{ 0x04bc, 0x30b7 }, /* kana_SHI KATAKANA LETTER SI */ -{ 0x04bd, 0x30b9 }, /* kana_SU KATAKANA LETTER SU */ -{ 0x04be, 0x30bb }, /* kana_SE KATAKANA LETTER SE */ -{ 0x04bf, 0x30bd }, /* kana_SO KATAKANA LETTER SO */ -{ 0x04c0, 0x30bf }, /* kana_TA KATAKANA LETTER TA */ -{ 0x04c1, 0x30c1 }, /* kana_CHI KATAKANA LETTER TI */ -{ 0x04c2, 0x30c4 }, /* kana_TSU KATAKANA LETTER TU */ -{ 0x04c3, 0x30c6 }, /* kana_TE KATAKANA LETTER TE */ -{ 0x04c4, 0x30c8 }, /* kana_TO KATAKANA LETTER TO */ -{ 0x04c5, 0x30ca }, /* kana_NA KATAKANA LETTER NA */ -{ 0x04c6, 0x30cb }, /* kana_NI KATAKANA LETTER NI */ -{ 0x04c7, 0x30cc }, /* kana_NU KATAKANA LETTER NU */ -{ 0x04c8, 0x30cd }, /* kana_NE KATAKANA LETTER NE */ -{ 0x04c9, 0x30ce }, /* kana_NO KATAKANA LETTER NO */ -{ 0x04ca, 0x30cf }, /* kana_HA KATAKANA LETTER HA */ -{ 0x04cb, 0x30d2 }, /* kana_HI KATAKANA LETTER HI */ -{ 0x04cc, 0x30d5 }, /* kana_FU KATAKANA LETTER HU */ -{ 0x04cd, 0x30d8 }, /* kana_HE KATAKANA LETTER HE */ -{ 0x04ce, 0x30db }, /* kana_HO KATAKANA LETTER HO */ -{ 0x04cf, 0x30de }, /* kana_MA KATAKANA LETTER MA */ -{ 0x04d0, 0x30df }, /* kana_MI KATAKANA LETTER MI */ -{ 0x04d1, 0x30e0 }, /* kana_MU KATAKANA LETTER MU */ -{ 0x04d2, 0x30e1 }, /* kana_ME KATAKANA LETTER ME */ -{ 0x04d3, 0x30e2 }, /* kana_MO KATAKANA LETTER MO */ -{ 0x04d4, 0x30e4 }, /* kana_YA KATAKANA LETTER YA */ -{ 0x04d5, 0x30e6 }, /* kana_YU KATAKANA LETTER YU */ -{ 0x04d6, 0x30e8 }, /* kana_YO KATAKANA LETTER YO */ -{ 0x04d7, 0x30e9 }, /* kana_RA KATAKANA LETTER RA */ -{ 0x04d8, 0x30ea }, /* kana_RI KATAKANA LETTER RI */ -{ 0x04d9, 0x30eb }, /* kana_RU KATAKANA LETTER RU */ -{ 0x04da, 0x30ec }, /* kana_RE KATAKANA LETTER RE */ -{ 0x04db, 0x30ed }, /* kana_RO KATAKANA LETTER RO */ -{ 0x04dc, 0x30ef }, /* kana_WA KATAKANA LETTER WA */ -{ 0x04dd, 0x30f3 }, /* kana_N KATAKANA LETTER N */ -{ 0x04de, 0x309b }, /* voicedsound KATAKANA-HIRAGANA VOICED SOUND MARK */ -{ 0x04df, 0x309c }, /* semivoicedsound KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ -{ 0x05ac, 0x060c }, /* Arabic_comma ARABIC COMMA */ -{ 0x05bb, 0x061b }, /* Arabic_semicolon ARABIC SEMICOLON */ -{ 0x05bf, 0x061f }, /* Arabic_question_mark ARABIC QUESTION MARK */ -{ 0x05c1, 0x0621 }, /* Arabic_hamza ARABIC LETTER HAMZA */ -{ 0x05c2, 0x0622 }, /* Arabic_maddaonalef ARABIC LETTER ALEF WITH MADDA ABOVE */ -{ 0x05c3, 0x0623 }, /* Arabic_hamzaonalef ARABIC LETTER ALEF WITH HAMZA ABOVE */ -{ 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ARABIC LETTER WAW WITH HAMZA ABOVE */ -{ 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef ARABIC LETTER ALEF WITH HAMZA BELOW */ -{ 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ARABIC LETTER YEH WITH HAMZA ABOVE */ -{ 0x05c7, 0x0627 }, /* Arabic_alef ARABIC LETTER ALEF */ -{ 0x05c8, 0x0628 }, /* Arabic_beh ARABIC LETTER BEH */ -{ 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ARABIC LETTER TEH MARBUTA */ -{ 0x05ca, 0x062a }, /* Arabic_teh ARABIC LETTER TEH */ -{ 0x05cb, 0x062b }, /* Arabic_theh ARABIC LETTER THEH */ -{ 0x05cc, 0x062c }, /* Arabic_jeem ARABIC LETTER JEEM */ -{ 0x05cd, 0x062d }, /* Arabic_hah ARABIC LETTER HAH */ -{ 0x05ce, 0x062e }, /* Arabic_khah ARABIC LETTER KHAH */ -{ 0x05cf, 0x062f }, /* Arabic_dal ARABIC LETTER DAL */ -{ 0x05d0, 0x0630 }, /* Arabic_thal ARABIC LETTER THAL */ -{ 0x05d1, 0x0631 }, /* Arabic_ra ARABIC LETTER REH */ -{ 0x05d2, 0x0632 }, /* Arabic_zain ARABIC LETTER ZAIN */ -{ 0x05d3, 0x0633 }, /* Arabic_seen ARABIC LETTER SEEN */ -{ 0x05d4, 0x0634 }, /* Arabic_sheen ARABIC LETTER SHEEN */ -{ 0x05d5, 0x0635 }, /* Arabic_sad ARABIC LETTER SAD */ -{ 0x05d6, 0x0636 }, /* Arabic_dad ARABIC LETTER DAD */ -{ 0x05d7, 0x0637 }, /* Arabic_tah ARABIC LETTER TAH */ -{ 0x05d8, 0x0638 }, /* Arabic_zah ARABIC LETTER ZAH */ -{ 0x05d9, 0x0639 }, /* Arabic_ain ARABIC LETTER AIN */ -{ 0x05da, 0x063a }, /* Arabic_ghain ARABIC LETTER GHAIN */ -{ 0x05e0, 0x0640 }, /* Arabic_tatweel ARABIC TATWEEL */ -{ 0x05e1, 0x0641 }, /* Arabic_feh ARABIC LETTER FEH */ -{ 0x05e2, 0x0642 }, /* Arabic_qaf ARABIC LETTER QAF */ -{ 0x05e3, 0x0643 }, /* Arabic_kaf ARABIC LETTER KAF */ -{ 0x05e4, 0x0644 }, /* Arabic_lam ARABIC LETTER LAM */ -{ 0x05e5, 0x0645 }, /* Arabic_meem ARABIC LETTER MEEM */ -{ 0x05e6, 0x0646 }, /* Arabic_noon ARABIC LETTER NOON */ -{ 0x05e7, 0x0647 }, /* Arabic_ha ARABIC LETTER HEH */ -{ 0x05e8, 0x0648 }, /* Arabic_waw ARABIC LETTER WAW */ -{ 0x05e9, 0x0649 }, /* Arabic_alefmaksura ARABIC LETTER ALEF MAKSURA */ -{ 0x05ea, 0x064a }, /* Arabic_yeh ARABIC LETTER YEH */ -{ 0x05eb, 0x064b }, /* Arabic_fathatan ARABIC FATHATAN */ -{ 0x05ec, 0x064c }, /* Arabic_dammatan ARABIC DAMMATAN */ -{ 0x05ed, 0x064d }, /* Arabic_kasratan ARABIC KASRATAN */ -{ 0x05ee, 0x064e }, /* Arabic_fatha ARABIC FATHA */ -{ 0x05ef, 0x064f }, /* Arabic_damma ARABIC DAMMA */ -{ 0x05f0, 0x0650 }, /* Arabic_kasra ARABIC KASRA */ -{ 0x05f1, 0x0651 }, /* Arabic_shadda ARABIC SHADDA */ -{ 0x05f2, 0x0652 }, /* Arabic_sukun ARABIC SUKUN */ -{ 0x06a1, 0x0452 }, /* Serbian_dje CYRILLIC SMALL LETTER DJE */ -{ 0x06a2, 0x0453 }, /* Macedonia_gje CYRILLIC SMALL LETTER GJE */ -{ 0x06a3, 0x0451 }, /* Cyrillic_io CYRILLIC SMALL LETTER IO */ -{ 0x06a4, 0x0454 }, /* Ukrainian_ie CYRILLIC SMALL LETTER UKRAINIAN IE */ -{ 0x06a5, 0x0455 }, /* Macedonia_dse CYRILLIC SMALL LETTER DZE */ -{ 0x06a6, 0x0456 }, /* Ukrainian_i CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ -{ 0x06a7, 0x0457 }, /* Ukrainian_yi CYRILLIC SMALL LETTER YI */ -{ 0x06a8, 0x0458 }, /* Cyrillic_je CYRILLIC SMALL LETTER JE */ -{ 0x06a9, 0x0459 }, /* Cyrillic_lje CYRILLIC SMALL LETTER LJE */ -{ 0x06aa, 0x045a }, /* Cyrillic_nje CYRILLIC SMALL LETTER NJE */ -{ 0x06ab, 0x045b }, /* Serbian_tshe CYRILLIC SMALL LETTER TSHE */ -{ 0x06ac, 0x045c }, /* Macedonia_kje CYRILLIC SMALL LETTER KJE */ -{ 0x06ae, 0x045e }, /* Byelorussian_shortu CYRILLIC SMALL LETTER SHORT U */ -{ 0x06af, 0x045f }, /* Cyrillic_dzhe CYRILLIC SMALL LETTER DZHE */ -{ 0x06b0, 0x2116 }, /* numerosign NUMERO SIGN */ -{ 0x06b1, 0x0402 }, /* Serbian_DJE CYRILLIC CAPITAL LETTER DJE */ -{ 0x06b2, 0x0403 }, /* Macedonia_GJE CYRILLIC CAPITAL LETTER GJE */ -{ 0x06b3, 0x0401 }, /* Cyrillic_IO CYRILLIC CAPITAL LETTER IO */ -{ 0x06b4, 0x0404 }, /* Ukrainian_IE CYRILLIC CAPITAL LETTER UKRAINIAN IE */ -{ 0x06b5, 0x0405 }, /* Macedonia_DSE CYRILLIC CAPITAL LETTER DZE */ -{ 0x06b6, 0x0406 }, /* Ukrainian_I CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ -{ 0x06b7, 0x0407 }, /* Ukrainian_YI CYRILLIC CAPITAL LETTER YI */ -{ 0x06b8, 0x0408 }, /* Cyrillic_JE CYRILLIC CAPITAL LETTER JE */ -{ 0x06b9, 0x0409 }, /* Cyrillic_LJE CYRILLIC CAPITAL LETTER LJE */ -{ 0x06ba, 0x040a }, /* Cyrillic_NJE CYRILLIC CAPITAL LETTER NJE */ -{ 0x06bb, 0x040b }, /* Serbian_TSHE CYRILLIC CAPITAL LETTER TSHE */ -{ 0x06bc, 0x040c }, /* Macedonia_KJE CYRILLIC CAPITAL LETTER KJE */ -{ 0x06be, 0x040e }, /* Byelorussian_SHORTU CYRILLIC CAPITAL LETTER SHORT U */ -{ 0x06bf, 0x040f }, /* Cyrillic_DZHE CYRILLIC CAPITAL LETTER DZHE */ -{ 0x06c0, 0x044e }, /* Cyrillic_yu CYRILLIC SMALL LETTER YU */ -{ 0x06c1, 0x0430 }, /* Cyrillic_a CYRILLIC SMALL LETTER A */ -{ 0x06c2, 0x0431 }, /* Cyrillic_be CYRILLIC SMALL LETTER BE */ -{ 0x06c3, 0x0446 }, /* Cyrillic_tse CYRILLIC SMALL LETTER TSE */ -{ 0x06c4, 0x0434 }, /* Cyrillic_de CYRILLIC SMALL LETTER DE */ -{ 0x06c5, 0x0435 }, /* Cyrillic_ie CYRILLIC SMALL LETTER IE */ -{ 0x06c6, 0x0444 }, /* Cyrillic_ef CYRILLIC SMALL LETTER EF */ -{ 0x06c7, 0x0433 }, /* Cyrillic_ghe CYRILLIC SMALL LETTER GHE */ -{ 0x06c8, 0x0445 }, /* Cyrillic_ha CYRILLIC SMALL LETTER HA */ -{ 0x06c9, 0x0438 }, /* Cyrillic_i CYRILLIC SMALL LETTER I */ -{ 0x06ca, 0x0439 }, /* Cyrillic_shorti CYRILLIC SMALL LETTER SHORT I */ -{ 0x06cb, 0x043a }, /* Cyrillic_ka CYRILLIC SMALL LETTER KA */ -{ 0x06cc, 0x043b }, /* Cyrillic_el CYRILLIC SMALL LETTER EL */ -{ 0x06cd, 0x043c }, /* Cyrillic_em CYRILLIC SMALL LETTER EM */ -{ 0x06ce, 0x043d }, /* Cyrillic_en CYRILLIC SMALL LETTER EN */ -{ 0x06cf, 0x043e }, /* Cyrillic_o CYRILLIC SMALL LETTER O */ -{ 0x06d0, 0x043f }, /* Cyrillic_pe CYRILLIC SMALL LETTER PE */ -{ 0x06d1, 0x044f }, /* Cyrillic_ya CYRILLIC SMALL LETTER YA */ -{ 0x06d2, 0x0440 }, /* Cyrillic_er CYRILLIC SMALL LETTER ER */ -{ 0x06d3, 0x0441 }, /* Cyrillic_es CYRILLIC SMALL LETTER ES */ -{ 0x06d4, 0x0442 }, /* Cyrillic_te CYRILLIC SMALL LETTER TE */ -{ 0x06d5, 0x0443 }, /* Cyrillic_u CYRILLIC SMALL LETTER U */ -{ 0x06d6, 0x0436 }, /* Cyrillic_zhe CYRILLIC SMALL LETTER ZHE */ -{ 0x06d7, 0x0432 }, /* Cyrillic_ve CYRILLIC SMALL LETTER VE */ -{ 0x06d8, 0x044c }, /* Cyrillic_softsign CYRILLIC SMALL LETTER SOFT SIGN */ -{ 0x06d9, 0x044b }, /* Cyrillic_yeru CYRILLIC SMALL LETTER YERU */ -{ 0x06da, 0x0437 }, /* Cyrillic_ze CYRILLIC SMALL LETTER ZE */ -{ 0x06db, 0x0448 }, /* Cyrillic_sha CYRILLIC SMALL LETTER SHA */ -{ 0x06dc, 0x044d }, /* Cyrillic_e CYRILLIC SMALL LETTER E */ -{ 0x06dd, 0x0449 }, /* Cyrillic_shcha CYRILLIC SMALL LETTER SHCHA */ -{ 0x06de, 0x0447 }, /* Cyrillic_che CYRILLIC SMALL LETTER CHE */ -{ 0x06df, 0x044a }, /* Cyrillic_hardsign CYRILLIC SMALL LETTER HARD SIGN */ -{ 0x06e0, 0x042e }, /* Cyrillic_YU CYRILLIC CAPITAL LETTER YU */ -{ 0x06e1, 0x0410 }, /* Cyrillic_A CYRILLIC CAPITAL LETTER A */ -{ 0x06e2, 0x0411 }, /* Cyrillic_BE CYRILLIC CAPITAL LETTER BE */ -{ 0x06e3, 0x0426 }, /* Cyrillic_TSE CYRILLIC CAPITAL LETTER TSE */ -{ 0x06e4, 0x0414 }, /* Cyrillic_DE CYRILLIC CAPITAL LETTER DE */ -{ 0x06e5, 0x0415 }, /* Cyrillic_IE CYRILLIC CAPITAL LETTER IE */ -{ 0x06e6, 0x0424 }, /* Cyrillic_EF CYRILLIC CAPITAL LETTER EF */ -{ 0x06e7, 0x0413 }, /* Cyrillic_GHE CYRILLIC CAPITAL LETTER GHE */ -{ 0x06e8, 0x0425 }, /* Cyrillic_HA CYRILLIC CAPITAL LETTER HA */ -{ 0x06e9, 0x0418 }, /* Cyrillic_I CYRILLIC CAPITAL LETTER I */ -{ 0x06ea, 0x0419 }, /* Cyrillic_SHORTI CYRILLIC CAPITAL LETTER SHORT I */ -{ 0x06eb, 0x041a }, /* Cyrillic_KA CYRILLIC CAPITAL LETTER KA */ -{ 0x06ec, 0x041b }, /* Cyrillic_EL CYRILLIC CAPITAL LETTER EL */ -{ 0x06ed, 0x041c }, /* Cyrillic_EM CYRILLIC CAPITAL LETTER EM */ -{ 0x06ee, 0x041d }, /* Cyrillic_EN CYRILLIC CAPITAL LETTER EN */ -{ 0x06ef, 0x041e }, /* Cyrillic_O CYRILLIC CAPITAL LETTER O */ -{ 0x06f0, 0x041f }, /* Cyrillic_PE CYRILLIC CAPITAL LETTER PE */ -{ 0x06f1, 0x042f }, /* Cyrillic_YA CYRILLIC CAPITAL LETTER YA */ -{ 0x06f2, 0x0420 }, /* Cyrillic_ER CYRILLIC CAPITAL LETTER ER */ -{ 0x06f3, 0x0421 }, /* Cyrillic_ES CYRILLIC CAPITAL LETTER ES */ -{ 0x06f4, 0x0422 }, /* Cyrillic_TE CYRILLIC CAPITAL LETTER TE */ -{ 0x06f5, 0x0423 }, /* Cyrillic_U CYRILLIC CAPITAL LETTER U */ -{ 0x06f6, 0x0416 }, /* Cyrillic_ZHE CYRILLIC CAPITAL LETTER ZHE */ -{ 0x06f7, 0x0412 }, /* Cyrillic_VE CYRILLIC CAPITAL LETTER VE */ -{ 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN CYRILLIC CAPITAL LETTER SOFT SIGN */ -{ 0x06f9, 0x042b }, /* Cyrillic_YERU CYRILLIC CAPITAL LETTER YERU */ -{ 0x06fa, 0x0417 }, /* Cyrillic_ZE CYRILLIC CAPITAL LETTER ZE */ -{ 0x06fb, 0x0428 }, /* Cyrillic_SHA CYRILLIC CAPITAL LETTER SHA */ -{ 0x06fc, 0x042d }, /* Cyrillic_E CYRILLIC CAPITAL LETTER E */ -{ 0x06fd, 0x0429 }, /* Cyrillic_SHCHA CYRILLIC CAPITAL LETTER SHCHA */ -{ 0x06fe, 0x0427 }, /* Cyrillic_CHE CYRILLIC CAPITAL LETTER CHE */ -{ 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN CYRILLIC CAPITAL LETTER HARD SIGN */ -{ 0x07a1, 0x0386 }, /* Greek_ALPHAaccent GREEK CAPITAL LETTER ALPHA WITH TONOS */ -{ 0x07a2, 0x0388 }, /* Greek_EPSILONaccent GREEK CAPITAL LETTER EPSILON WITH TONOS */ -{ 0x07a3, 0x0389 }, /* Greek_ETAaccent GREEK CAPITAL LETTER ETA WITH TONOS */ -{ 0x07a4, 0x038a }, /* Greek_IOTAaccent GREEK CAPITAL LETTER IOTA WITH TONOS */ -{ 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ -{ 0x07a7, 0x038c }, /* Greek_OMICRONaccent GREEK CAPITAL LETTER OMICRON WITH TONOS */ -{ 0x07a8, 0x038e }, /* Greek_UPSILONaccent GREEK CAPITAL LETTER UPSILON WITH TONOS */ -{ 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ -{ 0x07ab, 0x038f }, /* Greek_OMEGAaccent GREEK CAPITAL LETTER OMEGA WITH TONOS */ -{ 0x07ae, 0x0385 }, /* Greek_accentdieresis GREEK DIALYTIKA TONOS */ -{ 0x07af, 0x2015 }, /* Greek_horizbar HORIZONTAL BAR */ -{ 0x07b1, 0x03ac }, /* Greek_alphaaccent GREEK SMALL LETTER ALPHA WITH TONOS */ -{ 0x07b2, 0x03ad }, /* Greek_epsilonaccent GREEK SMALL LETTER EPSILON WITH TONOS */ -{ 0x07b3, 0x03ae }, /* Greek_etaaccent GREEK SMALL LETTER ETA WITH TONOS */ -{ 0x07b4, 0x03af }, /* Greek_iotaaccent GREEK SMALL LETTER IOTA WITH TONOS */ -{ 0x07b5, 0x03ca }, /* Greek_iotadieresis GREEK SMALL LETTER IOTA WITH DIALYTIKA */ -{ 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ -{ 0x07b7, 0x03cc }, /* Greek_omicronaccent GREEK SMALL LETTER OMICRON WITH TONOS */ -{ 0x07b8, 0x03cd }, /* Greek_upsilonaccent GREEK SMALL LETTER UPSILON WITH TONOS */ -{ 0x07b9, 0x03cb }, /* Greek_upsilondieresis GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ -{ 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ -{ 0x07bb, 0x03ce }, /* Greek_omegaaccent GREEK SMALL LETTER OMEGA WITH TONOS */ -{ 0x07c1, 0x0391 }, /* Greek_ALPHA GREEK CAPITAL LETTER ALPHA */ -{ 0x07c2, 0x0392 }, /* Greek_BETA GREEK CAPITAL LETTER BETA */ -{ 0x07c3, 0x0393 }, /* Greek_GAMMA GREEK CAPITAL LETTER GAMMA */ -{ 0x07c4, 0x0394 }, /* Greek_DELTA GREEK CAPITAL LETTER DELTA */ -{ 0x07c5, 0x0395 }, /* Greek_EPSILON GREEK CAPITAL LETTER EPSILON */ -{ 0x07c6, 0x0396 }, /* Greek_ZETA GREEK CAPITAL LETTER ZETA */ -{ 0x07c7, 0x0397 }, /* Greek_ETA GREEK CAPITAL LETTER ETA */ -{ 0x07c8, 0x0398 }, /* Greek_THETA GREEK CAPITAL LETTER THETA */ -{ 0x07c9, 0x0399 }, /* Greek_IOTA GREEK CAPITAL LETTER IOTA */ -{ 0x07ca, 0x039a }, /* Greek_KAPPA GREEK CAPITAL LETTER KAPPA */ -{ 0x07cb, 0x039b }, /* Greek_LAMBDA GREEK CAPITAL LETTER LAMDA */ -{ 0x07cc, 0x039c }, /* Greek_MU GREEK CAPITAL LETTER MU */ -{ 0x07cd, 0x039d }, /* Greek_NU GREEK CAPITAL LETTER NU */ -{ 0x07ce, 0x039e }, /* Greek_XI GREEK CAPITAL LETTER XI */ -{ 0x07cf, 0x039f }, /* Greek_OMICRON GREEK CAPITAL LETTER OMICRON */ -{ 0x07d0, 0x03a0 }, /* Greek_PI GREEK CAPITAL LETTER PI */ -{ 0x07d1, 0x03a1 }, /* Greek_RHO GREEK CAPITAL LETTER RHO */ -{ 0x07d2, 0x03a3 }, /* Greek_SIGMA GREEK CAPITAL LETTER SIGMA */ -{ 0x07d4, 0x03a4 }, /* Greek_TAU GREEK CAPITAL LETTER TAU */ -{ 0x07d5, 0x03a5 }, /* Greek_UPSILON GREEK CAPITAL LETTER UPSILON */ -{ 0x07d6, 0x03a6 }, /* Greek_PHI GREEK CAPITAL LETTER PHI */ -{ 0x07d7, 0x03a7 }, /* Greek_CHI GREEK CAPITAL LETTER CHI */ -{ 0x07d8, 0x03a8 }, /* Greek_PSI GREEK CAPITAL LETTER PSI */ -{ 0x07d9, 0x03a9 }, /* Greek_OMEGA GREEK CAPITAL LETTER OMEGA */ -{ 0x07e1, 0x03b1 }, /* Greek_alpha GREEK SMALL LETTER ALPHA */ -{ 0x07e2, 0x03b2 }, /* Greek_beta GREEK SMALL LETTER BETA */ -{ 0x07e3, 0x03b3 }, /* Greek_gamma GREEK SMALL LETTER GAMMA */ -{ 0x07e4, 0x03b4 }, /* Greek_delta GREEK SMALL LETTER DELTA */ -{ 0x07e5, 0x03b5 }, /* Greek_epsilon GREEK SMALL LETTER EPSILON */ -{ 0x07e6, 0x03b6 }, /* Greek_zeta GREEK SMALL LETTER ZETA */ -{ 0x07e7, 0x03b7 }, /* Greek_eta GREEK SMALL LETTER ETA */ -{ 0x07e8, 0x03b8 }, /* Greek_theta GREEK SMALL LETTER THETA */ -{ 0x07e9, 0x03b9 }, /* Greek_iota GREEK SMALL LETTER IOTA */ -{ 0x07ea, 0x03ba }, /* Greek_kappa GREEK SMALL LETTER KAPPA */ -{ 0x07eb, 0x03bb }, /* Greek_lambda GREEK SMALL LETTER LAMDA */ -{ 0x07ec, 0x03bc }, /* Greek_mu GREEK SMALL LETTER MU */ -{ 0x07ed, 0x03bd }, /* Greek_nu GREEK SMALL LETTER NU */ -{ 0x07ee, 0x03be }, /* Greek_xi GREEK SMALL LETTER XI */ -{ 0x07ef, 0x03bf }, /* Greek_omicron GREEK SMALL LETTER OMICRON */ -{ 0x07f0, 0x03c0 }, /* Greek_pi GREEK SMALL LETTER PI */ -{ 0x07f1, 0x03c1 }, /* Greek_rho GREEK SMALL LETTER RHO */ -{ 0x07f2, 0x03c3 }, /* Greek_sigma GREEK SMALL LETTER SIGMA */ -{ 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma GREEK SMALL LETTER FINAL SIGMA */ -{ 0x07f4, 0x03c4 }, /* Greek_tau GREEK SMALL LETTER TAU */ -{ 0x07f5, 0x03c5 }, /* Greek_upsilon GREEK SMALL LETTER UPSILON */ -{ 0x07f6, 0x03c6 }, /* Greek_phi GREEK SMALL LETTER PHI */ -{ 0x07f7, 0x03c7 }, /* Greek_chi GREEK SMALL LETTER CHI */ -{ 0x07f8, 0x03c8 }, /* Greek_psi GREEK SMALL LETTER PSI */ -{ 0x07f9, 0x03c9 }, /* Greek_omega GREEK SMALL LETTER OMEGA */ -{ 0x08a1, 0x23b7 }, /* leftradical ??? */ -{ 0x08a2, 0x250c }, /* topleftradical BOX DRAWINGS LIGHT DOWN AND RIGHT */ -{ 0x08a3, 0x2500 }, /* horizconnector BOX DRAWINGS LIGHT HORIZONTAL */ -{ 0x08a4, 0x2320 }, /* topintegral TOP HALF INTEGRAL */ -{ 0x08a5, 0x2321 }, /* botintegral BOTTOM HALF INTEGRAL */ -{ 0x08a6, 0x2502 }, /* vertconnector BOX DRAWINGS LIGHT VERTICAL */ -{ 0x08a7, 0x23a1 }, /* topleftsqbracket ??? */ -{ 0x08a8, 0x23a3 }, /* botleftsqbracket ??? */ -{ 0x08a9, 0x23a4 }, /* toprightsqbracket ??? */ -{ 0x08aa, 0x23a6 }, /* botrightsqbracket ??? */ -{ 0x08ab, 0x239b }, /* topleftparens ??? */ -{ 0x08ac, 0x239d }, /* botleftparens ??? */ -{ 0x08ad, 0x239e }, /* toprightparens ??? */ -{ 0x08ae, 0x23a0 }, /* botrightparens ??? */ -{ 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ??? */ -{ 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ??? */ -{ 0x08bc, 0x2264 }, /* lessthanequal LESS-THAN OR EQUAL TO */ -{ 0x08bd, 0x2260 }, /* notequal NOT EQUAL TO */ -{ 0x08be, 0x2265 }, /* greaterthanequal GREATER-THAN OR EQUAL TO */ -{ 0x08bf, 0x222b }, /* integral INTEGRAL */ -{ 0x08c0, 0x2234 }, /* therefore THEREFORE */ -{ 0x08c1, 0x221d }, /* variation PROPORTIONAL TO */ -{ 0x08c2, 0x221e }, /* infinity INFINITY */ -{ 0x08c5, 0x2207 }, /* nabla NABLA */ -{ 0x08c8, 0x223c }, /* approximate TILDE OPERATOR */ -{ 0x08c9, 0x2243 }, /* similarequal ASYMPTOTICALLY EQUAL TO */ -{ 0x08cd, 0x21d4 }, /* ifonlyif LEFT RIGHT DOUBLE ARROW */ -{ 0x08ce, 0x21d2 }, /* implies RIGHTWARDS DOUBLE ARROW */ -{ 0x08cf, 0x2261 }, /* identical IDENTICAL TO */ -{ 0x08d6, 0x221a }, /* radical SQUARE ROOT */ -{ 0x08da, 0x2282 }, /* includedin SUBSET OF */ -{ 0x08db, 0x2283 }, /* includes SUPERSET OF */ -{ 0x08dc, 0x2229 }, /* intersection INTERSECTION */ -{ 0x08dd, 0x222a }, /* union UNION */ -{ 0x08de, 0x2227 }, /* logicaland LOGICAL AND */ -{ 0x08df, 0x2228 }, /* logicalor LOGICAL OR */ -{ 0x08ef, 0x2202 }, /* partialderivative PARTIAL DIFFERENTIAL */ -{ 0x08f6, 0x0192 }, /* function LATIN SMALL LETTER F WITH HOOK */ -{ 0x08fb, 0x2190 }, /* leftarrow LEFTWARDS ARROW */ -{ 0x08fc, 0x2191 }, /* uparrow UPWARDS ARROW */ -{ 0x08fd, 0x2192 }, /* rightarrow RIGHTWARDS ARROW */ -{ 0x08fe, 0x2193 }, /* downarrow DOWNWARDS ARROW */ -/* 0x09df blank ??? */ -{ 0x09e0, 0x25c6 }, /* soliddiamond BLACK DIAMOND */ -{ 0x09e1, 0x2592 }, /* checkerboard MEDIUM SHADE */ -{ 0x09e2, 0x2409 }, /* ht SYMBOL FOR HORIZONTAL TABULATION */ -{ 0x09e3, 0x240c }, /* ff SYMBOL FOR FORM FEED */ -{ 0x09e4, 0x240d }, /* cr SYMBOL FOR CARRIAGE RETURN */ -{ 0x09e5, 0x240a }, /* lf SYMBOL FOR LINE FEED */ -{ 0x09e8, 0x2424 }, /* nl SYMBOL FOR NEWLINE */ -{ 0x09e9, 0x240b }, /* vt SYMBOL FOR VERTICAL TABULATION */ -{ 0x09ea, 0x2518 }, /* lowrightcorner BOX DRAWINGS LIGHT UP AND LEFT */ -{ 0x09eb, 0x2510 }, /* uprightcorner BOX DRAWINGS LIGHT DOWN AND LEFT */ -{ 0x09ec, 0x250c }, /* upleftcorner BOX DRAWINGS LIGHT DOWN AND RIGHT */ -{ 0x09ed, 0x2514 }, /* lowleftcorner BOX DRAWINGS LIGHT UP AND RIGHT */ -{ 0x09ee, 0x253c }, /* crossinglines BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ -{ 0x09ef, 0x23ba }, /* horizlinescan1 HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ -{ 0x09f0, 0x23bb }, /* horizlinescan3 HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ -{ 0x09f1, 0x2500 }, /* horizlinescan5 BOX DRAWINGS LIGHT HORIZONTAL */ -{ 0x09f2, 0x23bc }, /* horizlinescan7 HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ -{ 0x09f3, 0x23bd }, /* horizlinescan9 HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ -{ 0x09f4, 0x251c }, /* leftt BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ -{ 0x09f5, 0x2524 }, /* rightt BOX DRAWINGS LIGHT VERTICAL AND LEFT */ -{ 0x09f6, 0x2534 }, /* bott BOX DRAWINGS LIGHT UP AND HORIZONTAL */ -{ 0x09f7, 0x252c }, /* topt BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ -{ 0x09f8, 0x2502 }, /* vertbar BOX DRAWINGS LIGHT VERTICAL */ -{ 0x0aa1, 0x2003 }, /* emspace EM SPACE */ -{ 0x0aa2, 0x2002 }, /* enspace EN SPACE */ -{ 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */ -{ 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */ -{ 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */ -{ 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */ -{ 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */ -{ 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */ -{ 0x0aa9, 0x2014 }, /* emdash EM DASH */ -{ 0x0aaa, 0x2013 }, /* endash EN DASH */ -/* 0x0aac signifblank ??? */ -{ 0x0aae, 0x2026 }, /* ellipsis HORIZONTAL ELLIPSIS */ -{ 0x0aaf, 0x2025 }, /* doubbaselinedot TWO DOT LEADER */ -{ 0x0ab0, 0x2153 }, /* onethird VULGAR FRACTION ONE THIRD */ -{ 0x0ab1, 0x2154 }, /* twothirds VULGAR FRACTION TWO THIRDS */ -{ 0x0ab2, 0x2155 }, /* onefifth VULGAR FRACTION ONE FIFTH */ -{ 0x0ab3, 0x2156 }, /* twofifths VULGAR FRACTION TWO FIFTHS */ -{ 0x0ab4, 0x2157 }, /* threefifths VULGAR FRACTION THREE FIFTHS */ -{ 0x0ab5, 0x2158 }, /* fourfifths VULGAR FRACTION FOUR FIFTHS */ -{ 0x0ab6, 0x2159 }, /* onesixth VULGAR FRACTION ONE SIXTH */ -{ 0x0ab7, 0x215a }, /* fivesixths VULGAR FRACTION FIVE SIXTHS */ -{ 0x0ab8, 0x2105 }, /* careof CARE OF */ -{ 0x0abb, 0x2012 }, /* figdash FIGURE DASH */ -{ 0x0abc, 0x2329 }, /* leftanglebracket LEFT-POINTING ANGLE BRACKET */ -/* 0x0abd decimalpoint ??? */ -{ 0x0abe, 0x232a }, /* rightanglebracket RIGHT-POINTING ANGLE BRACKET */ -/* 0x0abf marker ??? */ -{ 0x0ac3, 0x215b }, /* oneeighth VULGAR FRACTION ONE EIGHTH */ -{ 0x0ac4, 0x215c }, /* threeeighths VULGAR FRACTION THREE EIGHTHS */ -{ 0x0ac5, 0x215d }, /* fiveeighths VULGAR FRACTION FIVE EIGHTHS */ -{ 0x0ac6, 0x215e }, /* seveneighths VULGAR FRACTION SEVEN EIGHTHS */ -{ 0x0ac9, 0x2122 }, /* trademark TRADE MARK SIGN */ -{ 0x0aca, 0x2613 }, /* signaturemark SALTIRE */ -/* 0x0acb trademarkincircle ??? */ -{ 0x0acc, 0x25c1 }, /* leftopentriangle WHITE LEFT-POINTING TRIANGLE */ -{ 0x0acd, 0x25b7 }, /* rightopentriangle WHITE RIGHT-POINTING TRIANGLE */ -{ 0x0ace, 0x25cb }, /* emopencircle WHITE CIRCLE */ -{ 0x0acf, 0x25af }, /* emopenrectangle WHITE VERTICAL RECTANGLE */ -{ 0x0ad0, 0x2018 }, /* leftsinglequotemark LEFT SINGLE QUOTATION MARK */ -{ 0x0ad1, 0x2019 }, /* rightsinglequotemark RIGHT SINGLE QUOTATION MARK */ -{ 0x0ad2, 0x201c }, /* leftdoublequotemark LEFT DOUBLE QUOTATION MARK */ -{ 0x0ad3, 0x201d }, /* rightdoublequotemark RIGHT DOUBLE QUOTATION MARK */ -{ 0x0ad4, 0x211e }, /* prescription PRESCRIPTION TAKE */ -{ 0x0ad6, 0x2032 }, /* minutes PRIME */ -{ 0x0ad7, 0x2033 }, /* seconds DOUBLE PRIME */ -{ 0x0ad9, 0x271d }, /* latincross LATIN CROSS */ -/* 0x0ada hexagram ??? */ -{ 0x0adb, 0x25ac }, /* filledrectbullet BLACK RECTANGLE */ -{ 0x0adc, 0x25c0 }, /* filledlefttribullet BLACK LEFT-POINTING TRIANGLE */ -{ 0x0add, 0x25b6 }, /* filledrighttribullet BLACK RIGHT-POINTING TRIANGLE */ -{ 0x0ade, 0x25cf }, /* emfilledcircle BLACK CIRCLE */ -{ 0x0adf, 0x25ae }, /* emfilledrect BLACK VERTICAL RECTANGLE */ -{ 0x0ae0, 0x25e6 }, /* enopencircbullet WHITE BULLET */ -{ 0x0ae1, 0x25ab }, /* enopensquarebullet WHITE SMALL SQUARE */ -{ 0x0ae2, 0x25ad }, /* openrectbullet WHITE RECTANGLE */ -{ 0x0ae3, 0x25b3 }, /* opentribulletup WHITE UP-POINTING TRIANGLE */ -{ 0x0ae4, 0x25bd }, /* opentribulletdown WHITE DOWN-POINTING TRIANGLE */ -{ 0x0ae5, 0x2606 }, /* openstar WHITE STAR */ -{ 0x0ae6, 0x2022 }, /* enfilledcircbullet BULLET */ -{ 0x0ae7, 0x25aa }, /* enfilledsqbullet BLACK SMALL SQUARE */ -{ 0x0ae8, 0x25b2 }, /* filledtribulletup BLACK UP-POINTING TRIANGLE */ -{ 0x0ae9, 0x25bc }, /* filledtribulletdown BLACK DOWN-POINTING TRIANGLE */ -{ 0x0aea, 0x261c }, /* leftpointer WHITE LEFT POINTING INDEX */ -{ 0x0aeb, 0x261e }, /* rightpointer WHITE RIGHT POINTING INDEX */ -{ 0x0aec, 0x2663 }, /* club BLACK CLUB SUIT */ -{ 0x0aed, 0x2666 }, /* diamond BLACK DIAMOND SUIT */ -{ 0x0aee, 0x2665 }, /* heart BLACK HEART SUIT */ -{ 0x0af0, 0x2720 }, /* maltesecross MALTESE CROSS */ -{ 0x0af1, 0x2020 }, /* dagger DAGGER */ -{ 0x0af2, 0x2021 }, /* doubledagger DOUBLE DAGGER */ -{ 0x0af3, 0x2713 }, /* checkmark CHECK MARK */ -{ 0x0af4, 0x2717 }, /* ballotcross BALLOT X */ -{ 0x0af5, 0x266f }, /* musicalsharp MUSIC SHARP SIGN */ -{ 0x0af6, 0x266d }, /* musicalflat MUSIC FLAT SIGN */ -{ 0x0af7, 0x2642 }, /* malesymbol MALE SIGN */ -{ 0x0af8, 0x2640 }, /* femalesymbol FEMALE SIGN */ -{ 0x0af9, 0x260e }, /* telephone BLACK TELEPHONE */ -{ 0x0afa, 0x2315 }, /* telephonerecorder TELEPHONE RECORDER */ -{ 0x0afb, 0x2117 }, /* phonographcopyright SOUND RECORDING COPYRIGHT */ -{ 0x0afc, 0x2038 }, /* caret CARET */ -{ 0x0afd, 0x201a }, /* singlelowquotemark SINGLE LOW-9 QUOTATION MARK */ -{ 0x0afe, 0x201e }, /* doublelowquotemark DOUBLE LOW-9 QUOTATION MARK */ -/* 0x0aff cursor ??? */ -{ 0x0ba3, 0x003c }, /* leftcaret LESS-THAN SIGN */ -{ 0x0ba6, 0x003e }, /* rightcaret GREATER-THAN SIGN */ -{ 0x0ba8, 0x2228 }, /* downcaret LOGICAL OR */ -{ 0x0ba9, 0x2227 }, /* upcaret LOGICAL AND */ -{ 0x0bc0, 0x00af }, /* overbar MACRON */ -{ 0x0bc2, 0x22a5 }, /* downtack UP TACK */ -{ 0x0bc3, 0x2229 }, /* upshoe INTERSECTION */ -{ 0x0bc4, 0x230a }, /* downstile LEFT FLOOR */ -{ 0x0bc6, 0x005f }, /* underbar LOW LINE */ -{ 0x0bca, 0x2218 }, /* jot RING OPERATOR */ -{ 0x0bcc, 0x2395 }, /* quad APL FUNCTIONAL SYMBOL QUAD */ -{ 0x0bce, 0x22a4 }, /* uptack DOWN TACK */ -{ 0x0bcf, 0x25cb }, /* circle WHITE CIRCLE */ -{ 0x0bd3, 0x2308 }, /* upstile LEFT CEILING */ -{ 0x0bd6, 0x222a }, /* downshoe UNION */ -{ 0x0bd8, 0x2283 }, /* rightshoe SUPERSET OF */ -{ 0x0bda, 0x2282 }, /* leftshoe SUBSET OF */ -{ 0x0bdc, 0x22a2 }, /* lefttack RIGHT TACK */ -{ 0x0bfc, 0x22a3 }, /* righttack LEFT TACK */ -{ 0x0cdf, 0x2017 }, /* hebrew_doublelowline DOUBLE LOW LINE */ -{ 0x0ce0, 0x05d0 }, /* hebrew_aleph HEBREW LETTER ALEF */ -{ 0x0ce1, 0x05d1 }, /* hebrew_bet HEBREW LETTER BET */ -{ 0x0ce2, 0x05d2 }, /* hebrew_gimel HEBREW LETTER GIMEL */ -{ 0x0ce3, 0x05d3 }, /* hebrew_dalet HEBREW LETTER DALET */ -{ 0x0ce4, 0x05d4 }, /* hebrew_he HEBREW LETTER HE */ -{ 0x0ce5, 0x05d5 }, /* hebrew_waw HEBREW LETTER VAV */ -{ 0x0ce6, 0x05d6 }, /* hebrew_zain HEBREW LETTER ZAYIN */ -{ 0x0ce7, 0x05d7 }, /* hebrew_chet HEBREW LETTER HET */ -{ 0x0ce8, 0x05d8 }, /* hebrew_tet HEBREW LETTER TET */ -{ 0x0ce9, 0x05d9 }, /* hebrew_yod HEBREW LETTER YOD */ -{ 0x0cea, 0x05da }, /* hebrew_finalkaph HEBREW LETTER FINAL KAF */ -{ 0x0ceb, 0x05db }, /* hebrew_kaph HEBREW LETTER KAF */ -{ 0x0cec, 0x05dc }, /* hebrew_lamed HEBREW LETTER LAMED */ -{ 0x0ced, 0x05dd }, /* hebrew_finalmem HEBREW LETTER FINAL MEM */ -{ 0x0cee, 0x05de }, /* hebrew_mem HEBREW LETTER MEM */ -{ 0x0cef, 0x05df }, /* hebrew_finalnun HEBREW LETTER FINAL NUN */ -{ 0x0cf0, 0x05e0 }, /* hebrew_nun HEBREW LETTER NUN */ -{ 0x0cf1, 0x05e1 }, /* hebrew_samech HEBREW LETTER SAMEKH */ -{ 0x0cf2, 0x05e2 }, /* hebrew_ayin HEBREW LETTER AYIN */ -{ 0x0cf3, 0x05e3 }, /* hebrew_finalpe HEBREW LETTER FINAL PE */ -{ 0x0cf4, 0x05e4 }, /* hebrew_pe HEBREW LETTER PE */ -{ 0x0cf5, 0x05e5 }, /* hebrew_finalzade HEBREW LETTER FINAL TSADI */ -{ 0x0cf6, 0x05e6 }, /* hebrew_zade HEBREW LETTER TSADI */ -{ 0x0cf7, 0x05e7 }, /* hebrew_qoph HEBREW LETTER QOF */ -{ 0x0cf8, 0x05e8 }, /* hebrew_resh HEBREW LETTER RESH */ -{ 0x0cf9, 0x05e9 }, /* hebrew_shin HEBREW LETTER SHIN */ -{ 0x0cfa, 0x05ea }, /* hebrew_taw HEBREW LETTER TAV */ -{ 0x0da1, 0x0e01 }, /* Thai_kokai THAI CHARACTER KO KAI */ -{ 0x0da2, 0x0e02 }, /* Thai_khokhai THAI CHARACTER KHO KHAI */ -{ 0x0da3, 0x0e03 }, /* Thai_khokhuat THAI CHARACTER KHO KHUAT */ -{ 0x0da4, 0x0e04 }, /* Thai_khokhwai THAI CHARACTER KHO KHWAI */ -{ 0x0da5, 0x0e05 }, /* Thai_khokhon THAI CHARACTER KHO KHON */ -{ 0x0da6, 0x0e06 }, /* Thai_khorakhang THAI CHARACTER KHO RAKHANG */ -{ 0x0da7, 0x0e07 }, /* Thai_ngongu THAI CHARACTER NGO NGU */ -{ 0x0da8, 0x0e08 }, /* Thai_chochan THAI CHARACTER CHO CHAN */ -{ 0x0da9, 0x0e09 }, /* Thai_choching THAI CHARACTER CHO CHING */ -{ 0x0daa, 0x0e0a }, /* Thai_chochang THAI CHARACTER CHO CHANG */ -{ 0x0dab, 0x0e0b }, /* Thai_soso THAI CHARACTER SO SO */ -{ 0x0dac, 0x0e0c }, /* Thai_chochoe THAI CHARACTER CHO CHOE */ -{ 0x0dad, 0x0e0d }, /* Thai_yoying THAI CHARACTER YO YING */ -{ 0x0dae, 0x0e0e }, /* Thai_dochada THAI CHARACTER DO CHADA */ -{ 0x0daf, 0x0e0f }, /* Thai_topatak THAI CHARACTER TO PATAK */ -{ 0x0db0, 0x0e10 }, /* Thai_thothan THAI CHARACTER THO THAN */ -{ 0x0db1, 0x0e11 }, /* Thai_thonangmontho THAI CHARACTER THO NANGMONTHO */ -{ 0x0db2, 0x0e12 }, /* Thai_thophuthao THAI CHARACTER THO PHUTHAO */ -{ 0x0db3, 0x0e13 }, /* Thai_nonen THAI CHARACTER NO NEN */ -{ 0x0db4, 0x0e14 }, /* Thai_dodek THAI CHARACTER DO DEK */ -{ 0x0db5, 0x0e15 }, /* Thai_totao THAI CHARACTER TO TAO */ -{ 0x0db6, 0x0e16 }, /* Thai_thothung THAI CHARACTER THO THUNG */ -{ 0x0db7, 0x0e17 }, /* Thai_thothahan THAI CHARACTER THO THAHAN */ -{ 0x0db8, 0x0e18 }, /* Thai_thothong THAI CHARACTER THO THONG */ -{ 0x0db9, 0x0e19 }, /* Thai_nonu THAI CHARACTER NO NU */ -{ 0x0dba, 0x0e1a }, /* Thai_bobaimai THAI CHARACTER BO BAIMAI */ -{ 0x0dbb, 0x0e1b }, /* Thai_popla THAI CHARACTER PO PLA */ -{ 0x0dbc, 0x0e1c }, /* Thai_phophung THAI CHARACTER PHO PHUNG */ -{ 0x0dbd, 0x0e1d }, /* Thai_fofa THAI CHARACTER FO FA */ -{ 0x0dbe, 0x0e1e }, /* Thai_phophan THAI CHARACTER PHO PHAN */ -{ 0x0dbf, 0x0e1f }, /* Thai_fofan THAI CHARACTER FO FAN */ -{ 0x0dc0, 0x0e20 }, /* Thai_phosamphao THAI CHARACTER PHO SAMPHAO */ -{ 0x0dc1, 0x0e21 }, /* Thai_moma THAI CHARACTER MO MA */ -{ 0x0dc2, 0x0e22 }, /* Thai_yoyak THAI CHARACTER YO YAK */ -{ 0x0dc3, 0x0e23 }, /* Thai_rorua THAI CHARACTER RO RUA */ -{ 0x0dc4, 0x0e24 }, /* Thai_ru THAI CHARACTER RU */ -{ 0x0dc5, 0x0e25 }, /* Thai_loling THAI CHARACTER LO LING */ -{ 0x0dc6, 0x0e26 }, /* Thai_lu THAI CHARACTER LU */ -{ 0x0dc7, 0x0e27 }, /* Thai_wowaen THAI CHARACTER WO WAEN */ -{ 0x0dc8, 0x0e28 }, /* Thai_sosala THAI CHARACTER SO SALA */ -{ 0x0dc9, 0x0e29 }, /* Thai_sorusi THAI CHARACTER SO RUSI */ -{ 0x0dca, 0x0e2a }, /* Thai_sosua THAI CHARACTER SO SUA */ -{ 0x0dcb, 0x0e2b }, /* Thai_hohip THAI CHARACTER HO HIP */ -{ 0x0dcc, 0x0e2c }, /* Thai_lochula THAI CHARACTER LO CHULA */ -{ 0x0dcd, 0x0e2d }, /* Thai_oang THAI CHARACTER O ANG */ -{ 0x0dce, 0x0e2e }, /* Thai_honokhuk THAI CHARACTER HO NOKHUK */ -{ 0x0dcf, 0x0e2f }, /* Thai_paiyannoi THAI CHARACTER PAIYANNOI */ -{ 0x0dd0, 0x0e30 }, /* Thai_saraa THAI CHARACTER SARA A */ -{ 0x0dd1, 0x0e31 }, /* Thai_maihanakat THAI CHARACTER MAI HAN-AKAT */ -{ 0x0dd2, 0x0e32 }, /* Thai_saraaa THAI CHARACTER SARA AA */ -{ 0x0dd3, 0x0e33 }, /* Thai_saraam THAI CHARACTER SARA AM */ -{ 0x0dd4, 0x0e34 }, /* Thai_sarai THAI CHARACTER SARA I */ -{ 0x0dd5, 0x0e35 }, /* Thai_saraii THAI CHARACTER SARA II */ -{ 0x0dd6, 0x0e36 }, /* Thai_saraue THAI CHARACTER SARA UE */ -{ 0x0dd7, 0x0e37 }, /* Thai_sarauee THAI CHARACTER SARA UEE */ -{ 0x0dd8, 0x0e38 }, /* Thai_sarau THAI CHARACTER SARA U */ -{ 0x0dd9, 0x0e39 }, /* Thai_sarauu THAI CHARACTER SARA UU */ -{ 0x0dda, 0x0e3a }, /* Thai_phinthu THAI CHARACTER PHINTHU */ -/* 0x0dde Thai_maihanakat_maitho ??? */ -{ 0x0ddf, 0x0e3f }, /* Thai_baht THAI CURRENCY SYMBOL BAHT */ -{ 0x0de0, 0x0e40 }, /* Thai_sarae THAI CHARACTER SARA E */ -{ 0x0de1, 0x0e41 }, /* Thai_saraae THAI CHARACTER SARA AE */ -{ 0x0de2, 0x0e42 }, /* Thai_sarao THAI CHARACTER SARA O */ -{ 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan THAI CHARACTER SARA AI MAIMUAN */ -{ 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai THAI CHARACTER SARA AI MAIMALAI */ -{ 0x0de5, 0x0e45 }, /* Thai_lakkhangyao THAI CHARACTER LAKKHANGYAO */ -{ 0x0de6, 0x0e46 }, /* Thai_maiyamok THAI CHARACTER MAIYAMOK */ -{ 0x0de7, 0x0e47 }, /* Thai_maitaikhu THAI CHARACTER MAITAIKHU */ -{ 0x0de8, 0x0e48 }, /* Thai_maiek THAI CHARACTER MAI EK */ -{ 0x0de9, 0x0e49 }, /* Thai_maitho THAI CHARACTER MAI THO */ -{ 0x0dea, 0x0e4a }, /* Thai_maitri THAI CHARACTER MAI TRI */ -{ 0x0deb, 0x0e4b }, /* Thai_maichattawa THAI CHARACTER MAI CHATTAWA */ -{ 0x0dec, 0x0e4c }, /* Thai_thanthakhat THAI CHARACTER THANTHAKHAT */ -{ 0x0ded, 0x0e4d }, /* Thai_nikhahit THAI CHARACTER NIKHAHIT */ -{ 0x0df0, 0x0e50 }, /* Thai_leksun THAI DIGIT ZERO */ -{ 0x0df1, 0x0e51 }, /* Thai_leknung THAI DIGIT ONE */ -{ 0x0df2, 0x0e52 }, /* Thai_leksong THAI DIGIT TWO */ -{ 0x0df3, 0x0e53 }, /* Thai_leksam THAI DIGIT THREE */ -{ 0x0df4, 0x0e54 }, /* Thai_leksi THAI DIGIT FOUR */ -{ 0x0df5, 0x0e55 }, /* Thai_lekha THAI DIGIT FIVE */ -{ 0x0df6, 0x0e56 }, /* Thai_lekhok THAI DIGIT SIX */ -{ 0x0df7, 0x0e57 }, /* Thai_lekchet THAI DIGIT SEVEN */ -{ 0x0df8, 0x0e58 }, /* Thai_lekpaet THAI DIGIT EIGHT */ -{ 0x0df9, 0x0e59 }, /* Thai_lekkao THAI DIGIT NINE */ -{ 0x0ea1, 0x3131 }, /* Hangul_Kiyeog HANGUL LETTER KIYEOK */ -{ 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog HANGUL LETTER SSANGKIYEOK */ -{ 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios HANGUL LETTER KIYEOK-SIOS */ -{ 0x0ea4, 0x3134 }, /* Hangul_Nieun HANGUL LETTER NIEUN */ -{ 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj HANGUL LETTER NIEUN-CIEUC */ -{ 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh HANGUL LETTER NIEUN-HIEUH */ -{ 0x0ea7, 0x3137 }, /* Hangul_Dikeud HANGUL LETTER TIKEUT */ -{ 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud HANGUL LETTER SSANGTIKEUT */ -{ 0x0ea9, 0x3139 }, /* Hangul_Rieul HANGUL LETTER RIEUL */ -{ 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog HANGUL LETTER RIEUL-KIYEOK */ -{ 0x0eab, 0x313b }, /* Hangul_RieulMieum HANGUL LETTER RIEUL-MIEUM */ -{ 0x0eac, 0x313c }, /* Hangul_RieulPieub HANGUL LETTER RIEUL-PIEUP */ -{ 0x0ead, 0x313d }, /* Hangul_RieulSios HANGUL LETTER RIEUL-SIOS */ -{ 0x0eae, 0x313e }, /* Hangul_RieulTieut HANGUL LETTER RIEUL-THIEUTH */ -{ 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf HANGUL LETTER RIEUL-PHIEUPH */ -{ 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh HANGUL LETTER RIEUL-HIEUH */ -{ 0x0eb1, 0x3141 }, /* Hangul_Mieum HANGUL LETTER MIEUM */ -{ 0x0eb2, 0x3142 }, /* Hangul_Pieub HANGUL LETTER PIEUP */ -{ 0x0eb3, 0x3143 }, /* Hangul_SsangPieub HANGUL LETTER SSANGPIEUP */ -{ 0x0eb4, 0x3144 }, /* Hangul_PieubSios HANGUL LETTER PIEUP-SIOS */ -{ 0x0eb5, 0x3145 }, /* Hangul_Sios HANGUL LETTER SIOS */ -{ 0x0eb6, 0x3146 }, /* Hangul_SsangSios HANGUL LETTER SSANGSIOS */ -{ 0x0eb7, 0x3147 }, /* Hangul_Ieung HANGUL LETTER IEUNG */ -{ 0x0eb8, 0x3148 }, /* Hangul_Jieuj HANGUL LETTER CIEUC */ -{ 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj HANGUL LETTER SSANGCIEUC */ -{ 0x0eba, 0x314a }, /* Hangul_Cieuc HANGUL LETTER CHIEUCH */ -{ 0x0ebb, 0x314b }, /* Hangul_Khieuq HANGUL LETTER KHIEUKH */ -{ 0x0ebc, 0x314c }, /* Hangul_Tieut HANGUL LETTER THIEUTH */ -{ 0x0ebd, 0x314d }, /* Hangul_Phieuf HANGUL LETTER PHIEUPH */ -{ 0x0ebe, 0x314e }, /* Hangul_Hieuh HANGUL LETTER HIEUH */ -{ 0x0ebf, 0x314f }, /* Hangul_A HANGUL LETTER A */ -{ 0x0ec0, 0x3150 }, /* Hangul_AE HANGUL LETTER AE */ -{ 0x0ec1, 0x3151 }, /* Hangul_YA HANGUL LETTER YA */ -{ 0x0ec2, 0x3152 }, /* Hangul_YAE HANGUL LETTER YAE */ -{ 0x0ec3, 0x3153 }, /* Hangul_EO HANGUL LETTER EO */ -{ 0x0ec4, 0x3154 }, /* Hangul_E HANGUL LETTER E */ -{ 0x0ec5, 0x3155 }, /* Hangul_YEO HANGUL LETTER YEO */ -{ 0x0ec6, 0x3156 }, /* Hangul_YE HANGUL LETTER YE */ -{ 0x0ec7, 0x3157 }, /* Hangul_O HANGUL LETTER O */ -{ 0x0ec8, 0x3158 }, /* Hangul_WA HANGUL LETTER WA */ -{ 0x0ec9, 0x3159 }, /* Hangul_WAE HANGUL LETTER WAE */ -{ 0x0eca, 0x315a }, /* Hangul_OE HANGUL LETTER OE */ -{ 0x0ecb, 0x315b }, /* Hangul_YO HANGUL LETTER YO */ -{ 0x0ecc, 0x315c }, /* Hangul_U HANGUL LETTER U */ -{ 0x0ecd, 0x315d }, /* Hangul_WEO HANGUL LETTER WEO */ -{ 0x0ece, 0x315e }, /* Hangul_WE HANGUL LETTER WE */ -{ 0x0ecf, 0x315f }, /* Hangul_WI HANGUL LETTER WI */ -{ 0x0ed0, 0x3160 }, /* Hangul_YU HANGUL LETTER YU */ -{ 0x0ed1, 0x3161 }, /* Hangul_EU HANGUL LETTER EU */ -{ 0x0ed2, 0x3162 }, /* Hangul_YI HANGUL LETTER YI */ -{ 0x0ed3, 0x3163 }, /* Hangul_I HANGUL LETTER I */ -{ 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog HANGUL JONGSEONG KIYEOK */ -{ 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog HANGUL JONGSEONG SSANGKIYEOK */ -{ 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios HANGUL JONGSEONG KIYEOK-SIOS */ -{ 0x0ed7, 0x11ab }, /* Hangul_J_Nieun HANGUL JONGSEONG NIEUN */ -{ 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj HANGUL JONGSEONG NIEUN-CIEUC */ -{ 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh HANGUL JONGSEONG NIEUN-HIEUH */ -{ 0x0eda, 0x11ae }, /* Hangul_J_Dikeud HANGUL JONGSEONG TIKEUT */ -{ 0x0edb, 0x11af }, /* Hangul_J_Rieul HANGUL JONGSEONG RIEUL */ -{ 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog HANGUL JONGSEONG RIEUL-KIYEOK */ -{ 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum HANGUL JONGSEONG RIEUL-MIEUM */ -{ 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub HANGUL JONGSEONG RIEUL-PIEUP */ -{ 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios HANGUL JONGSEONG RIEUL-SIOS */ -{ 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut HANGUL JONGSEONG RIEUL-THIEUTH */ -{ 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf HANGUL JONGSEONG RIEUL-PHIEUPH */ -{ 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh HANGUL JONGSEONG RIEUL-HIEUH */ -{ 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum HANGUL JONGSEONG MIEUM */ -{ 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub HANGUL JONGSEONG PIEUP */ -{ 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios HANGUL JONGSEONG PIEUP-SIOS */ -{ 0x0ee6, 0x11ba }, /* Hangul_J_Sios HANGUL JONGSEONG SIOS */ -{ 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios HANGUL JONGSEONG SSANGSIOS */ -{ 0x0ee8, 0x11bc }, /* Hangul_J_Ieung HANGUL JONGSEONG IEUNG */ -{ 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj HANGUL JONGSEONG CIEUC */ -{ 0x0eea, 0x11be }, /* Hangul_J_Cieuc HANGUL JONGSEONG CHIEUCH */ -{ 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq HANGUL JONGSEONG KHIEUKH */ -{ 0x0eec, 0x11c0 }, /* Hangul_J_Tieut HANGUL JONGSEONG THIEUTH */ -{ 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf HANGUL JONGSEONG PHIEUPH */ -{ 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh HANGUL JONGSEONG HIEUH */ -{ 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh HANGUL LETTER RIEUL-YEORINHIEUH */ -{ 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum HANGUL LETTER KAPYEOUNMIEUM */ -{ 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub HANGUL LETTER KAPYEOUNPIEUP */ -{ 0x0ef2, 0x317f }, /* Hangul_PanSios HANGUL LETTER PANSIOS */ -{ 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung HANGUL LETTER YESIEUNG */ -{ 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf HANGUL LETTER KAPYEOUNPHIEUPH */ -{ 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh HANGUL LETTER YEORINHIEUH */ -{ 0x0ef6, 0x318d }, /* Hangul_AraeA HANGUL LETTER ARAEA */ -{ 0x0ef7, 0x318e }, /* Hangul_AraeAE HANGUL LETTER ARAEAE */ -{ 0x0ef8, 0x11eb }, /* Hangul_J_PanSios HANGUL JONGSEONG PANSIOS */ -{ 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung HANGUL JONGSEONG YESIEUNG */ -{ 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh HANGUL JONGSEONG YEORINHIEUH */ -{ 0x0eff, 0x20a9 }, /* Korean_Won WON SIGN */ -{ 0x13a4, 0x20ac }, /* Euro EURO SIGN */ -{ 0x13bc, 0x0152 }, /* OE LATIN CAPITAL LIGATURE OE */ -{ 0x13bd, 0x0153 }, /* oe LATIN SMALL LIGATURE OE */ -{ 0x13be, 0x0178 }, /* Ydiaeresis LATIN CAPITAL LETTER Y WITH DIAERESIS */ -{ 0x20ac, 0x20ac } /* EuroSign EURO SIGN */ +{ XK_Aogonek, 0x0104 }, /* LATIN CAPITAL LETTER A WITH OGONEK */ +{ XK_breve, 0x02d8 }, /* BREVE */ +{ XK_Lstroke, 0x0141 }, /* LATIN CAPITAL LETTER L WITH STROKE */ +{ XK_Lcaron, 0x013d }, /* LATIN CAPITAL LETTER L WITH CARON */ +{ XK_Sacute, 0x015a }, /* LATIN CAPITAL LETTER S WITH ACUTE */ +{ XK_Scaron, 0x0160 }, /* LATIN CAPITAL LETTER S WITH CARON */ +{ XK_Scedilla, 0x015e }, /* LATIN CAPITAL LETTER S WITH CEDILLA */ +{ XK_Tcaron, 0x0164 }, /* LATIN CAPITAL LETTER T WITH CARON */ +{ XK_Zacute, 0x0179 }, /* LATIN CAPITAL LETTER Z WITH ACUTE */ +{ XK_Zcaron, 0x017d }, /* LATIN CAPITAL LETTER Z WITH CARON */ +{ XK_Zabovedot, 0x017b }, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */ +{ XK_aogonek, 0x0105 }, /* LATIN SMALL LETTER A WITH OGONEK */ +{ XK_ogonek, 0x02db }, /* OGONEK */ +{ XK_lstroke, 0x0142 }, /* LATIN SMALL LETTER L WITH STROKE */ +{ XK_lcaron, 0x013e }, /* LATIN SMALL LETTER L WITH CARON */ +{ XK_sacute, 0x015b }, /* LATIN SMALL LETTER S WITH ACUTE */ +{ XK_caron, 0x02c7 }, /* CARON */ +{ XK_scaron, 0x0161 }, /* LATIN SMALL LETTER S WITH CARON */ +{ XK_scedilla, 0x015f }, /* LATIN SMALL LETTER S WITH CEDILLA */ +{ XK_tcaron, 0x0165 }, /* LATIN SMALL LETTER T WITH CARON */ +{ XK_zacute, 0x017a }, /* LATIN SMALL LETTER Z WITH ACUTE */ +{ XK_doubleacute, 0x02dd }, /* DOUBLE ACUTE ACCENT */ +{ XK_zcaron, 0x017e }, /* LATIN SMALL LETTER Z WITH CARON */ +{ XK_zabovedot, 0x017c }, /* LATIN SMALL LETTER Z WITH DOT ABOVE */ +{ XK_Racute, 0x0154 }, /* LATIN CAPITAL LETTER R WITH ACUTE */ +{ XK_Abreve, 0x0102 }, /* LATIN CAPITAL LETTER A WITH BREVE */ +{ XK_Lacute, 0x0139 }, /* LATIN CAPITAL LETTER L WITH ACUTE */ +{ XK_Cacute, 0x0106 }, /* LATIN CAPITAL LETTER C WITH ACUTE */ +{ XK_Ccaron, 0x010c }, /* LATIN CAPITAL LETTER C WITH CARON */ +{ XK_Eogonek, 0x0118 }, /* LATIN CAPITAL LETTER E WITH OGONEK */ +{ XK_Ecaron, 0x011a }, /* LATIN CAPITAL LETTER E WITH CARON */ +{ XK_Dcaron, 0x010e }, /* LATIN CAPITAL LETTER D WITH CARON */ +{ XK_Dstroke, 0x0110 }, /* LATIN CAPITAL LETTER D WITH STROKE */ +{ XK_Nacute, 0x0143 }, /* LATIN CAPITAL LETTER N WITH ACUTE */ +{ XK_Ncaron, 0x0147 }, /* LATIN CAPITAL LETTER N WITH CARON */ +{ XK_Odoubleacute, 0x0150 }, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ +{ XK_Rcaron, 0x0158 }, /* LATIN CAPITAL LETTER R WITH CARON */ +{ XK_Uring, 0x016e }, /* LATIN CAPITAL LETTER U WITH RING ABOVE */ +{ XK_Udoubleacute, 0x0170 }, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ +{ XK_Tcedilla, 0x0162 }, /* LATIN CAPITAL LETTER T WITH CEDILLA */ +{ XK_racute, 0x0155 }, /* LATIN SMALL LETTER R WITH ACUTE */ +{ XK_abreve, 0x0103 }, /* LATIN SMALL LETTER A WITH BREVE */ +{ XK_lacute, 0x013a }, /* LATIN SMALL LETTER L WITH ACUTE */ +{ XK_cacute, 0x0107 }, /* LATIN SMALL LETTER C WITH ACUTE */ +{ XK_ccaron, 0x010d }, /* LATIN SMALL LETTER C WITH CARON */ +{ XK_eogonek, 0x0119 }, /* LATIN SMALL LETTER E WITH OGONEK */ +{ XK_ecaron, 0x011b }, /* LATIN SMALL LETTER E WITH CARON */ +{ XK_dcaron, 0x010f }, /* LATIN SMALL LETTER D WITH CARON */ +{ XK_dstroke, 0x0111 }, /* LATIN SMALL LETTER D WITH STROKE */ +{ XK_nacute, 0x0144 }, /* LATIN SMALL LETTER N WITH ACUTE */ +{ XK_ncaron, 0x0148 }, /* LATIN SMALL LETTER N WITH CARON */ +{ XK_odoubleacute, 0x0151 }, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE */ +{ XK_rcaron, 0x0159 }, /* LATIN SMALL LETTER R WITH CARON */ +{ XK_uring, 0x016f }, /* LATIN SMALL LETTER U WITH RING ABOVE */ +{ XK_udoubleacute, 0x0171 }, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE */ +{ XK_tcedilla, 0x0163 }, /* LATIN SMALL LETTER T WITH CEDILLA */ +{ XK_abovedot, 0x02d9 }, /* DOT ABOVE */ +{ XK_Hstroke, 0x0126 }, /* LATIN CAPITAL LETTER H WITH STROKE */ +{ XK_Hcircumflex, 0x0124 }, /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ +{ XK_Iabovedot, 0x0130 }, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */ +{ XK_Gbreve, 0x011e }, /* LATIN CAPITAL LETTER G WITH BREVE */ +{ XK_Jcircumflex, 0x0134 }, /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ +{ XK_hstroke, 0x0127 }, /* LATIN SMALL LETTER H WITH STROKE */ +{ XK_hcircumflex, 0x0125 }, /* LATIN SMALL LETTER H WITH CIRCUMFLEX */ +{ XK_idotless, 0x0131 }, /* LATIN SMALL LETTER DOTLESS I */ +{ XK_gbreve, 0x011f }, /* LATIN SMALL LETTER G WITH BREVE */ +{ XK_jcircumflex, 0x0135 }, /* LATIN SMALL LETTER J WITH CIRCUMFLEX */ +{ XK_Cabovedot, 0x010a }, /* LATIN CAPITAL LETTER C WITH DOT ABOVE */ +{ XK_Ccircumflex, 0x0108 }, /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ +{ XK_Gabovedot, 0x0120 }, /* LATIN CAPITAL LETTER G WITH DOT ABOVE */ +{ XK_Gcircumflex, 0x011c }, /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ +{ XK_Ubreve, 0x016c }, /* LATIN CAPITAL LETTER U WITH BREVE */ +{ XK_Scircumflex, 0x015c }, /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ +{ XK_cabovedot, 0x010b }, /* LATIN SMALL LETTER C WITH DOT ABOVE */ +{ XK_ccircumflex, 0x0109 }, /* LATIN SMALL LETTER C WITH CIRCUMFLEX */ +{ XK_gabovedot, 0x0121 }, /* LATIN SMALL LETTER G WITH DOT ABOVE */ +{ XK_gcircumflex, 0x011d }, /* LATIN SMALL LETTER G WITH CIRCUMFLEX */ +{ XK_ubreve, 0x016d }, /* LATIN SMALL LETTER U WITH BREVE */ +{ XK_scircumflex, 0x015d }, /* LATIN SMALL LETTER S WITH CIRCUMFLEX */ +{ XK_kra, 0x0138 }, /* LATIN SMALL LETTER KRA */ +{ XK_Rcedilla, 0x0156 }, /* LATIN CAPITAL LETTER R WITH CEDILLA */ +{ XK_Itilde, 0x0128 }, /* LATIN CAPITAL LETTER I WITH TILDE */ +{ XK_Lcedilla, 0x013b }, /* LATIN CAPITAL LETTER L WITH CEDILLA */ +{ XK_Emacron, 0x0112 }, /* LATIN CAPITAL LETTER E WITH MACRON */ +{ XK_Gcedilla, 0x0122 }, /* LATIN CAPITAL LETTER G WITH CEDILLA */ +{ XK_Tslash, 0x0166 }, /* LATIN CAPITAL LETTER T WITH STROKE */ +{ XK_rcedilla, 0x0157 }, /* LATIN SMALL LETTER R WITH CEDILLA */ +{ XK_itilde, 0x0129 }, /* LATIN SMALL LETTER I WITH TILDE */ +{ XK_lcedilla, 0x013c }, /* LATIN SMALL LETTER L WITH CEDILLA */ +{ XK_emacron, 0x0113 }, /* LATIN SMALL LETTER E WITH MACRON */ +{ XK_gcedilla, 0x0123 }, /* LATIN SMALL LETTER G WITH CEDILLA */ +{ XK_tslash, 0x0167 }, /* LATIN SMALL LETTER T WITH STROKE */ +{ XK_ENG, 0x014a }, /* LATIN CAPITAL LETTER ENG */ +{ XK_eng, 0x014b }, /* LATIN SMALL LETTER ENG */ +{ XK_Amacron, 0x0100 }, /* LATIN CAPITAL LETTER A WITH MACRON */ +{ XK_Iogonek, 0x012e }, /* LATIN CAPITAL LETTER I WITH OGONEK */ +{ XK_Eabovedot, 0x0116 }, /* LATIN CAPITAL LETTER E WITH DOT ABOVE */ +{ XK_Imacron, 0x012a }, /* LATIN CAPITAL LETTER I WITH MACRON */ +{ XK_Ncedilla, 0x0145 }, /* LATIN CAPITAL LETTER N WITH CEDILLA */ +{ XK_Omacron, 0x014c }, /* LATIN CAPITAL LETTER O WITH MACRON */ +{ XK_Kcedilla, 0x0136 }, /* LATIN CAPITAL LETTER K WITH CEDILLA */ +{ XK_Uogonek, 0x0172 }, /* LATIN CAPITAL LETTER U WITH OGONEK */ +{ XK_Utilde, 0x0168 }, /* LATIN CAPITAL LETTER U WITH TILDE */ +{ XK_Umacron, 0x016a }, /* LATIN CAPITAL LETTER U WITH MACRON */ +{ XK_amacron, 0x0101 }, /* LATIN SMALL LETTER A WITH MACRON */ +{ XK_iogonek, 0x012f }, /* LATIN SMALL LETTER I WITH OGONEK */ +{ XK_eabovedot, 0x0117 }, /* LATIN SMALL LETTER E WITH DOT ABOVE */ +{ XK_imacron, 0x012b }, /* LATIN SMALL LETTER I WITH MACRON */ +{ XK_ncedilla, 0x0146 }, /* LATIN SMALL LETTER N WITH CEDILLA */ +{ XK_omacron, 0x014d }, /* LATIN SMALL LETTER O WITH MACRON */ +{ XK_kcedilla, 0x0137 }, /* LATIN SMALL LETTER K WITH CEDILLA */ +{ XK_uogonek, 0x0173 }, /* LATIN SMALL LETTER U WITH OGONEK */ +{ XK_utilde, 0x0169 }, /* LATIN SMALL LETTER U WITH TILDE */ +{ XK_umacron, 0x016b }, /* LATIN SMALL LETTER U WITH MACRON */ +{ XK_Babovedot, 0x1e02 }, /* LATIN CAPITAL LETTER B WITH DOT ABOVE */ +{ XK_babovedot, 0x1e03 }, /* LATIN SMALL LETTER B WITH DOT ABOVE */ +{ XK_Dabovedot, 0x1e0a }, /* LATIN CAPITAL LETTER D WITH DOT ABOVE */ +{ XK_Wgrave, 0x1e80 }, /* LATIN CAPITAL LETTER W WITH GRAVE */ +{ XK_Wacute, 0x1e82 }, /* LATIN CAPITAL LETTER W WITH ACUTE */ +{ XK_dabovedot, 0x1e0b }, /* LATIN SMALL LETTER D WITH DOT ABOVE */ +{ XK_Ygrave, 0x1ef2 }, /* LATIN CAPITAL LETTER Y WITH GRAVE */ +{ XK_Fabovedot, 0x1e1e }, /* LATIN CAPITAL LETTER F WITH DOT ABOVE */ +{ XK_fabovedot, 0x1e1f }, /* LATIN SMALL LETTER F WITH DOT ABOVE */ +{ XK_Mabovedot, 0x1e40 }, /* LATIN CAPITAL LETTER M WITH DOT ABOVE */ +{ XK_mabovedot, 0x1e41 }, /* LATIN SMALL LETTER M WITH DOT ABOVE */ +{ XK_Pabovedot, 0x1e56 }, /* LATIN CAPITAL LETTER P WITH DOT ABOVE */ +{ XK_wgrave, 0x1e81 }, /* LATIN SMALL LETTER W WITH GRAVE */ +{ XK_pabovedot, 0x1e57 }, /* LATIN SMALL LETTER P WITH DOT ABOVE */ +{ XK_wacute, 0x1e83 }, /* LATIN SMALL LETTER W WITH ACUTE */ +{ XK_Sabovedot, 0x1e60 }, /* LATIN CAPITAL LETTER S WITH DOT ABOVE */ +{ XK_ygrave, 0x1ef3 }, /* LATIN SMALL LETTER Y WITH GRAVE */ +{ XK_Wdiaeresis, 0x1e84 }, /* LATIN CAPITAL LETTER W WITH DIAERESIS */ +{ XK_wdiaeresis, 0x1e85 }, /* LATIN SMALL LETTER W WITH DIAERESIS */ +{ XK_sabovedot, 0x1e61 }, /* LATIN SMALL LETTER S WITH DOT ABOVE */ +{ XK_Wcircumflex, 0x0174 }, /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ +{ XK_Tabovedot, 0x1e6a }, /* LATIN CAPITAL LETTER T WITH DOT ABOVE */ +{ XK_Ycircumflex, 0x0176 }, /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ +{ XK_wcircumflex, 0x0175 }, /* LATIN SMALL LETTER W WITH CIRCUMFLEX */ +{ XK_tabovedot, 0x1e6b }, /* LATIN SMALL LETTER T WITH DOT ABOVE */ +{ XK_ycircumflex, 0x0177 }, /* LATIN SMALL LETTER Y WITH CIRCUMFLEX */ +{ XK_overline, 0x203e }, /* OVERLINE */ +{ XK_kana_fullstop, 0x3002 }, /* IDEOGRAPHIC FULL STOP */ +{ XK_kana_openingbracket, 0x300c }, /* LEFT CORNER BRACKET */ +{ XK_kana_closingbracket, 0x300d }, /* RIGHT CORNER BRACKET */ +{ XK_kana_comma, 0x3001 }, /* IDEOGRAPHIC COMMA */ +{ XK_kana_conjunctive, 0x30fb }, /* KATAKANA MIDDLE DOT */ +{ XK_kana_WO, 0x30f2 }, /* KATAKANA LETTER WO */ +{ XK_kana_a, 0x30a1 }, /* KATAKANA LETTER SMALL A */ +{ XK_kana_i, 0x30a3 }, /* KATAKANA LETTER SMALL I */ +{ XK_kana_u, 0x30a5 }, /* KATAKANA LETTER SMALL U */ +{ XK_kana_e, 0x30a7 }, /* KATAKANA LETTER SMALL E */ +{ XK_kana_o, 0x30a9 }, /* KATAKANA LETTER SMALL O */ +{ XK_kana_ya, 0x30e3 }, /* KATAKANA LETTER SMALL YA */ +{ XK_kana_yu, 0x30e5 }, /* KATAKANA LETTER SMALL YU */ +{ XK_kana_yo, 0x30e7 }, /* KATAKANA LETTER SMALL YO */ +{ XK_kana_tsu, 0x30c3 }, /* KATAKANA LETTER SMALL TU */ +{ XK_prolongedsound, 0x30fc }, /* KATAKANA-HIRAGANA PROLONGED SOUND MARK */ +{ XK_kana_A, 0x30a2 }, /* KATAKANA LETTER A */ +{ XK_kana_I, 0x30a4 }, /* KATAKANA LETTER I */ +{ XK_kana_U, 0x30a6 }, /* KATAKANA LETTER U */ +{ XK_kana_E, 0x30a8 }, /* KATAKANA LETTER E */ +{ XK_kana_O, 0x30aa }, /* KATAKANA LETTER O */ +{ XK_kana_KA, 0x30ab }, /* KATAKANA LETTER KA */ +{ XK_kana_KI, 0x30ad }, /* KATAKANA LETTER KI */ +{ XK_kana_KU, 0x30af }, /* KATAKANA LETTER KU */ +{ XK_kana_KE, 0x30b1 }, /* KATAKANA LETTER KE */ +{ XK_kana_KO, 0x30b3 }, /* KATAKANA LETTER KO */ +{ XK_kana_SA, 0x30b5 }, /* KATAKANA LETTER SA */ +{ XK_kana_SHI, 0x30b7 }, /* KATAKANA LETTER SI */ +{ XK_kana_SU, 0x30b9 }, /* KATAKANA LETTER SU */ +{ XK_kana_SE, 0x30bb }, /* KATAKANA LETTER SE */ +{ XK_kana_SO, 0x30bd }, /* KATAKANA LETTER SO */ +{ XK_kana_TA, 0x30bf }, /* KATAKANA LETTER TA */ +{ XK_kana_CHI, 0x30c1 }, /* KATAKANA LETTER TI */ +{ XK_kana_TSU, 0x30c4 }, /* KATAKANA LETTER TU */ +{ XK_kana_TE, 0x30c6 }, /* KATAKANA LETTER TE */ +{ XK_kana_TO, 0x30c8 }, /* KATAKANA LETTER TO */ +{ XK_kana_NA, 0x30ca }, /* KATAKANA LETTER NA */ +{ XK_kana_NI, 0x30cb }, /* KATAKANA LETTER NI */ +{ XK_kana_NU, 0x30cc }, /* KATAKANA LETTER NU */ +{ XK_kana_NE, 0x30cd }, /* KATAKANA LETTER NE */ +{ XK_kana_NO, 0x30ce }, /* KATAKANA LETTER NO */ +{ XK_kana_HA, 0x30cf }, /* KATAKANA LETTER HA */ +{ XK_kana_HI, 0x30d2 }, /* KATAKANA LETTER HI */ +{ XK_kana_FU, 0x30d5 }, /* KATAKANA LETTER HU */ +{ XK_kana_HE, 0x30d8 }, /* KATAKANA LETTER HE */ +{ XK_kana_HO, 0x30db }, /* KATAKANA LETTER HO */ +{ XK_kana_MA, 0x30de }, /* KATAKANA LETTER MA */ +{ XK_kana_MI, 0x30df }, /* KATAKANA LETTER MI */ +{ XK_kana_MU, 0x30e0 }, /* KATAKANA LETTER MU */ +{ XK_kana_ME, 0x30e1 }, /* KATAKANA LETTER ME */ +{ XK_kana_MO, 0x30e2 }, /* KATAKANA LETTER MO */ +{ XK_kana_YA, 0x30e4 }, /* KATAKANA LETTER YA */ +{ XK_kana_YU, 0x30e6 }, /* KATAKANA LETTER YU */ +{ XK_kana_YO, 0x30e8 }, /* KATAKANA LETTER YO */ +{ XK_kana_RA, 0x30e9 }, /* KATAKANA LETTER RA */ +{ XK_kana_RI, 0x30ea }, /* KATAKANA LETTER RI */ +{ XK_kana_RU, 0x30eb }, /* KATAKANA LETTER RU */ +{ XK_kana_RE, 0x30ec }, /* KATAKANA LETTER RE */ +{ XK_kana_RO, 0x30ed }, /* KATAKANA LETTER RO */ +{ XK_kana_WA, 0x30ef }, /* KATAKANA LETTER WA */ +{ XK_kana_N, 0x30f3 }, /* KATAKANA LETTER N */ +{ XK_voicedsound, 0x309b }, /* KATAKANA-HIRAGANA VOICED SOUND MARK */ +{ XK_semivoicedsound, 0x309c }, /* KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ +{ XK_Farsi_0, 0x06f0 }, /* EXTENDED ARABIC-INDIC DIGIT 0 */ +{ XK_Farsi_1, 0x06f1 }, /* EXTENDED ARABIC-INDIC DIGIT 1 */ +{ XK_Farsi_2, 0x06f2 }, /* EXTENDED ARABIC-INDIC DIGIT 2 */ +{ XK_Farsi_3, 0x06f3 }, /* EXTENDED ARABIC-INDIC DIGIT 3 */ +{ XK_Farsi_4, 0x06f4 }, /* EXTENDED ARABIC-INDIC DIGIT 4 */ +{ XK_Farsi_5, 0x06f5 }, /* EXTENDED ARABIC-INDIC DIGIT 5 */ +{ XK_Farsi_6, 0x06f6 }, /* EXTENDED ARABIC-INDIC DIGIT 6 */ +{ XK_Farsi_7, 0x06f7 }, /* EXTENDED ARABIC-INDIC DIGIT 7 */ +{ XK_Farsi_8, 0x06f8 }, /* EXTENDED ARABIC-INDIC DIGIT 8 */ +{ XK_Farsi_9, 0x06f9 }, /* EXTENDED ARABIC-INDIC DIGIT 9 */ +{ XK_Arabic_percent, 0x066a }, /* ARABIC PERCENT */ +{ XK_Arabic_superscript_alef, 0x0670 }, /* ARABIC LETTER SUPERSCRIPT ALEF */ +{ XK_Arabic_tteh, 0x0679 }, /* ARABIC LETTER TTEH */ +{ XK_Arabic_peh, 0x067e }, /* ARABIC LETTER PEH */ +{ XK_Arabic_tcheh, 0x0686 }, /* ARABIC LETTER TCHEH */ +{ XK_Arabic_ddal, 0x0688 }, /* ARABIC LETTER DDAL */ +{ XK_Arabic_rreh, 0x0691 }, /* ARABIC LETTER RREH */ +{ XK_Arabic_comma, 0x060c }, /* ARABIC COMMA */ +{ XK_Arabic_fullstop, 0x06d4 }, /* ARABIC FULLSTOP */ +{ XK_Arabic_semicolon, 0x061b }, /* ARABIC SEMICOLON */ +{ XK_Arabic_0, 0x0660 }, /* ARABIC 0 */ +{ XK_Arabic_1, 0x0661 }, /* ARABIC 1 */ +{ XK_Arabic_2, 0x0662 }, /* ARABIC 2 */ +{ XK_Arabic_3, 0x0663 }, /* ARABIC 3 */ +{ XK_Arabic_4, 0x0664 }, /* ARABIC 4 */ +{ XK_Arabic_5, 0x0665 }, /* ARABIC 5 */ +{ XK_Arabic_6, 0x0666 }, /* ARABIC 6 */ +{ XK_Arabic_7, 0x0667 }, /* ARABIC 7 */ +{ XK_Arabic_8, 0x0668 }, /* ARABIC 8 */ +{ XK_Arabic_9, 0x0669 }, /* ARABIC 9 */ +{ XK_Arabic_question_mark, 0x061f }, /* ARABIC QUESTION MARK */ +{ XK_Arabic_hamza, 0x0621 }, /* ARABIC LETTER HAMZA */ +{ XK_Arabic_maddaonalef, 0x0622 }, /* ARABIC LETTER ALEF WITH MADDA ABOVE */ +{ XK_Arabic_hamzaonalef, 0x0623 }, /* ARABIC LETTER ALEF WITH HAMZA ABOVE */ +{ XK_Arabic_hamzaonwaw, 0x0624 }, /* ARABIC LETTER WAW WITH HAMZA ABOVE */ +{ XK_Arabic_hamzaunderalef, 0x0625 }, /* ARABIC LETTER ALEF WITH HAMZA BELOW */ +{ XK_Arabic_hamzaonyeh, 0x0626 }, /* ARABIC LETTER YEH WITH HAMZA ABOVE */ +{ XK_Arabic_alef, 0x0627 }, /* ARABIC LETTER ALEF */ +{ XK_Arabic_beh, 0x0628 }, /* ARABIC LETTER BEH */ +{ XK_Arabic_tehmarbuta, 0x0629 }, /* ARABIC LETTER TEH MARBUTA */ +{ XK_Arabic_teh, 0x062a }, /* ARABIC LETTER TEH */ +{ XK_Arabic_theh, 0x062b }, /* ARABIC LETTER THEH */ +{ XK_Arabic_jeem, 0x062c }, /* ARABIC LETTER JEEM */ +{ XK_Arabic_hah, 0x062d }, /* ARABIC LETTER HAH */ +{ XK_Arabic_khah, 0x062e }, /* ARABIC LETTER KHAH */ +{ XK_Arabic_dal, 0x062f }, /* ARABIC LETTER DAL */ +{ XK_Arabic_thal, 0x0630 }, /* ARABIC LETTER THAL */ +{ XK_Arabic_ra, 0x0631 }, /* ARABIC LETTER REH */ +{ XK_Arabic_zain, 0x0632 }, /* ARABIC LETTER ZAIN */ +{ XK_Arabic_seen, 0x0633 }, /* ARABIC LETTER SEEN */ +{ XK_Arabic_sheen, 0x0634 }, /* ARABIC LETTER SHEEN */ +{ XK_Arabic_sad, 0x0635 }, /* ARABIC LETTER SAD */ +{ XK_Arabic_dad, 0x0636 }, /* ARABIC LETTER DAD */ +{ XK_Arabic_tah, 0x0637 }, /* ARABIC LETTER TAH */ +{ XK_Arabic_zah, 0x0638 }, /* ARABIC LETTER ZAH */ +{ XK_Arabic_ain, 0x0639 }, /* ARABIC LETTER AIN */ +{ XK_Arabic_ghain, 0x063a }, /* ARABIC LETTER GHAIN */ +{ XK_Arabic_tatweel, 0x0640 }, /* ARABIC TATWEEL */ +{ XK_Arabic_feh, 0x0641 }, /* ARABIC LETTER FEH */ +{ XK_Arabic_qaf, 0x0642 }, /* ARABIC LETTER QAF */ +{ XK_Arabic_kaf, 0x0643 }, /* ARABIC LETTER KAF */ +{ XK_Arabic_lam, 0x0644 }, /* ARABIC LETTER LAM */ +{ XK_Arabic_meem, 0x0645 }, /* ARABIC LETTER MEEM */ +{ XK_Arabic_noon, 0x0646 }, /* ARABIC LETTER NOON */ +{ XK_Arabic_ha, 0x0647 }, /* ARABIC LETTER HEH */ +{ XK_Arabic_waw, 0x0648 }, /* ARABIC LETTER WAW */ +{ XK_Arabic_alefmaksura, 0x0649 }, /* ARABIC LETTER ALEF MAKSURA */ +{ XK_Arabic_yeh, 0x064a }, /* ARABIC LETTER YEH */ +{ XK_Arabic_fathatan, 0x064b }, /* ARABIC FATHATAN */ +{ XK_Arabic_dammatan, 0x064c }, /* ARABIC DAMMATAN */ +{ XK_Arabic_kasratan, 0x064d }, /* ARABIC KASRATAN */ +{ XK_Arabic_fatha, 0x064e }, /* ARABIC FATHA */ +{ XK_Arabic_damma, 0x064f }, /* ARABIC DAMMA */ +{ XK_Arabic_kasra, 0x0650 }, /* ARABIC KASRA */ +{ XK_Arabic_shadda, 0x0651 }, /* ARABIC SHADDA */ +{ XK_Arabic_sukun, 0x0652 }, /* ARABIC SUKUN */ +{ XK_Arabic_madda_above, 0x0653 }, /* ARABIC MADDA ABOVE */ +{ XK_Arabic_hamza_above, 0x0654 }, /* ARABIC HAMZA ABOVE */ +{ XK_Arabic_hamza_below, 0x0655 }, /* ARABIC HAMZA BELOW */ +{ XK_Arabic_jeh, 0x0698 }, /* ARABIC LETTER JEH */ +{ XK_Arabic_veh, 0x06a4 }, /* ARABIC LETTER VEH */ +{ XK_Arabic_keheh, 0x06a9 }, /* ARABIC LETTER KEHEH */ +{ XK_Arabic_gaf, 0x06af }, /* ARABIC LETTER GAF */ +{ XK_Arabic_noon_ghunna, 0x06ba }, /* ARABIC LETTER NOON GHUNNA */ +{ XK_Arabic_heh_doachashmee, 0x06be }, /* ARABIC LETTER HEH DOACHASHMEE */ +{ XK_Arabic_farsi_yeh, 0x06cc }, /* ARABIC LETTER FARSI YEH */ +{ XK_Arabic_yeh_baree, 0x06d2 }, /* ARABIC LETTER YEH BAREE */ +{ XK_Arabic_heh_goal, 0x06c1 }, /* ARABIC LETTER HEH GOAL */ +{ XK_Serbian_dje, 0x0452 }, /* CYRILLIC SMALL LETTER DJE */ +{ XK_Macedonia_gje, 0x0453 }, /* CYRILLIC SMALL LETTER GJE */ +{ XK_Cyrillic_io, 0x0451 }, /* CYRILLIC SMALL LETTER IO */ +{ XK_Ukrainian_ie, 0x0454 }, /* CYRILLIC SMALL LETTER UKRAINIAN IE */ +{ XK_Macedonia_dse, 0x0455 }, /* CYRILLIC SMALL LETTER DZE */ +{ XK_Ukrainian_i, 0x0456 }, /* CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ +{ XK_Ukrainian_yi, 0x0457 }, /* CYRILLIC SMALL LETTER YI */ +{ XK_Cyrillic_je, 0x0458 }, /* CYRILLIC SMALL LETTER JE */ +{ XK_Cyrillic_lje, 0x0459 }, /* CYRILLIC SMALL LETTER LJE */ +{ XK_Cyrillic_nje, 0x045a }, /* CYRILLIC SMALL LETTER NJE */ +{ XK_Serbian_tshe, 0x045b }, /* CYRILLIC SMALL LETTER TSHE */ +{ XK_Macedonia_kje, 0x045c }, /* CYRILLIC SMALL LETTER KJE */ +{ XK_Ukrainian_ghe_with_upturn, 0x0491 }, /* CYRILLIC SMALL LETTER GHE WITH UPTURN */ +{ XK_Byelorussian_shortu, 0x045e }, /* CYRILLIC SMALL LETTER SHORT U */ +{ XK_Cyrillic_dzhe, 0x045f }, /* CYRILLIC SMALL LETTER DZHE */ +{ XK_numerosign, 0x2116 }, /* NUMERO SIGN */ +{ XK_Serbian_DJE, 0x0402 }, /* CYRILLIC CAPITAL LETTER DJE */ +{ XK_Macedonia_GJE, 0x0403 }, /* CYRILLIC CAPITAL LETTER GJE */ +{ XK_Cyrillic_IO, 0x0401 }, /* CYRILLIC CAPITAL LETTER IO */ +{ XK_Ukrainian_IE, 0x0404 }, /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */ +{ XK_Macedonia_DSE, 0x0405 }, /* CYRILLIC CAPITAL LETTER DZE */ +{ XK_Ukrainian_I, 0x0406 }, /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ +{ XK_Ukrainian_YI, 0x0407 }, /* CYRILLIC CAPITAL LETTER YI */ +{ XK_Cyrillic_JE, 0x0408 }, /* CYRILLIC CAPITAL LETTER JE */ +{ XK_Cyrillic_LJE, 0x0409 }, /* CYRILLIC CAPITAL LETTER LJE */ +{ XK_Cyrillic_NJE, 0x040a }, /* CYRILLIC CAPITAL LETTER NJE */ +{ XK_Serbian_TSHE, 0x040b }, /* CYRILLIC CAPITAL LETTER TSHE */ +{ XK_Macedonia_KJE, 0x040c }, /* CYRILLIC CAPITAL LETTER KJE */ +{ XK_Ukrainian_GHE_WITH_UPTURN, 0x0490 }, /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ +{ XK_Byelorussian_SHORTU, 0x040e }, /* CYRILLIC CAPITAL LETTER SHORT U */ +{ XK_Cyrillic_DZHE, 0x040f }, /* CYRILLIC CAPITAL LETTER DZHE */ +{ XK_Cyrillic_yu, 0x044e }, /* CYRILLIC SMALL LETTER YU */ +{ XK_Cyrillic_a, 0x0430 }, /* CYRILLIC SMALL LETTER A */ +{ XK_Cyrillic_be, 0x0431 }, /* CYRILLIC SMALL LETTER BE */ +{ XK_Cyrillic_tse, 0x0446 }, /* CYRILLIC SMALL LETTER TSE */ +{ XK_Cyrillic_de, 0x0434 }, /* CYRILLIC SMALL LETTER DE */ +{ XK_Cyrillic_ie, 0x0435 }, /* CYRILLIC SMALL LETTER IE */ +{ XK_Cyrillic_ef, 0x0444 }, /* CYRILLIC SMALL LETTER EF */ +{ XK_Cyrillic_ghe, 0x0433 }, /* CYRILLIC SMALL LETTER GHE */ +{ XK_Cyrillic_ha, 0x0445 }, /* CYRILLIC SMALL LETTER HA */ +{ XK_Cyrillic_i, 0x0438 }, /* CYRILLIC SMALL LETTER I */ +{ XK_Cyrillic_shorti, 0x0439 }, /* CYRILLIC SMALL LETTER SHORT I */ +{ XK_Cyrillic_ka, 0x043a }, /* CYRILLIC SMALL LETTER KA */ +{ XK_Cyrillic_el, 0x043b }, /* CYRILLIC SMALL LETTER EL */ +{ XK_Cyrillic_em, 0x043c }, /* CYRILLIC SMALL LETTER EM */ +{ XK_Cyrillic_en, 0x043d }, /* CYRILLIC SMALL LETTER EN */ +{ XK_Cyrillic_o, 0x043e }, /* CYRILLIC SMALL LETTER O */ +{ XK_Cyrillic_pe, 0x043f }, /* CYRILLIC SMALL LETTER PE */ +{ XK_Cyrillic_ya, 0x044f }, /* CYRILLIC SMALL LETTER YA */ +{ XK_Cyrillic_er, 0x0440 }, /* CYRILLIC SMALL LETTER ER */ +{ XK_Cyrillic_es, 0x0441 }, /* CYRILLIC SMALL LETTER ES */ +{ XK_Cyrillic_te, 0x0442 }, /* CYRILLIC SMALL LETTER TE */ +{ XK_Cyrillic_u, 0x0443 }, /* CYRILLIC SMALL LETTER U */ +{ XK_Cyrillic_zhe, 0x0436 }, /* CYRILLIC SMALL LETTER ZHE */ +{ XK_Cyrillic_ve, 0x0432 }, /* CYRILLIC SMALL LETTER VE */ +{ XK_Cyrillic_softsign, 0x044c }, /* CYRILLIC SMALL LETTER SOFT SIGN */ +{ XK_Cyrillic_yeru, 0x044b }, /* CYRILLIC SMALL LETTER YERU */ +{ XK_Cyrillic_ze, 0x0437 }, /* CYRILLIC SMALL LETTER ZE */ +{ XK_Cyrillic_sha, 0x0448 }, /* CYRILLIC SMALL LETTER SHA */ +{ XK_Cyrillic_e, 0x044d }, /* CYRILLIC SMALL LETTER E */ +{ XK_Cyrillic_shcha, 0x0449 }, /* CYRILLIC SMALL LETTER SHCHA */ +{ XK_Cyrillic_che, 0x0447 }, /* CYRILLIC SMALL LETTER CHE */ +{ XK_Cyrillic_hardsign, 0x044a }, /* CYRILLIC SMALL LETTER HARD SIGN */ +{ XK_Cyrillic_YU, 0x042e }, /* CYRILLIC CAPITAL LETTER YU */ +{ XK_Cyrillic_A, 0x0410 }, /* CYRILLIC CAPITAL LETTER A */ +{ XK_Cyrillic_BE, 0x0411 }, /* CYRILLIC CAPITAL LETTER BE */ +{ XK_Cyrillic_TSE, 0x0426 }, /* CYRILLIC CAPITAL LETTER TSE */ +{ XK_Cyrillic_DE, 0x0414 }, /* CYRILLIC CAPITAL LETTER DE */ +{ XK_Cyrillic_IE, 0x0415 }, /* CYRILLIC CAPITAL LETTER IE */ +{ XK_Cyrillic_EF, 0x0424 }, /* CYRILLIC CAPITAL LETTER EF */ +{ XK_Cyrillic_GHE, 0x0413 }, /* CYRILLIC CAPITAL LETTER GHE */ +{ XK_Cyrillic_HA, 0x0425 }, /* CYRILLIC CAPITAL LETTER HA */ +{ XK_Cyrillic_I, 0x0418 }, /* CYRILLIC CAPITAL LETTER I */ +{ XK_Cyrillic_SHORTI, 0x0419 }, /* CYRILLIC CAPITAL LETTER SHORT I */ +{ XK_Cyrillic_KA, 0x041a }, /* CYRILLIC CAPITAL LETTER KA */ +{ XK_Cyrillic_EL, 0x041b }, /* CYRILLIC CAPITAL LETTER EL */ +{ XK_Cyrillic_EM, 0x041c }, /* CYRILLIC CAPITAL LETTER EM */ +{ XK_Cyrillic_EN, 0x041d }, /* CYRILLIC CAPITAL LETTER EN */ +{ XK_Cyrillic_O, 0x041e }, /* CYRILLIC CAPITAL LETTER O */ +{ XK_Cyrillic_PE, 0x041f }, /* CYRILLIC CAPITAL LETTER PE */ +{ XK_Cyrillic_YA, 0x042f }, /* CYRILLIC CAPITAL LETTER YA */ +{ XK_Cyrillic_ER, 0x0420 }, /* CYRILLIC CAPITAL LETTER ER */ +{ XK_Cyrillic_ES, 0x0421 }, /* CYRILLIC CAPITAL LETTER ES */ +{ XK_Cyrillic_TE, 0x0422 }, /* CYRILLIC CAPITAL LETTER TE */ +{ XK_Cyrillic_U, 0x0423 }, /* CYRILLIC CAPITAL LETTER U */ +{ XK_Cyrillic_ZHE, 0x0416 }, /* CYRILLIC CAPITAL LETTER ZHE */ +{ XK_Cyrillic_VE, 0x0412 }, /* CYRILLIC CAPITAL LETTER VE */ +{ XK_Cyrillic_SOFTSIGN, 0x042c }, /* CYRILLIC CAPITAL LETTER SOFT SIGN */ +{ XK_Cyrillic_YERU, 0x042b }, /* CYRILLIC CAPITAL LETTER YERU */ +{ XK_Cyrillic_ZE, 0x0417 }, /* CYRILLIC CAPITAL LETTER ZE */ +{ XK_Cyrillic_SHA, 0x0428 }, /* CYRILLIC CAPITAL LETTER SHA */ +{ XK_Cyrillic_E, 0x042d }, /* CYRILLIC CAPITAL LETTER E */ +{ XK_Cyrillic_SHCHA, 0x0429 }, /* CYRILLIC CAPITAL LETTER SHCHA */ +{ XK_Cyrillic_CHE, 0x0427 }, /* CYRILLIC CAPITAL LETTER CHE */ +{ XK_Cyrillic_HARDSIGN, 0x042a }, /* CYRILLIC CAPITAL LETTER HARD SIGN */ +{ XK_Greek_ALPHAaccent, 0x0386 }, /* GREEK CAPITAL LETTER ALPHA WITH TONOS */ +{ XK_Greek_EPSILONaccent, 0x0388 }, /* GREEK CAPITAL LETTER EPSILON WITH TONOS */ +{ XK_Greek_ETAaccent, 0x0389 }, /* GREEK CAPITAL LETTER ETA WITH TONOS */ +{ XK_Greek_IOTAaccent, 0x038a }, /* GREEK CAPITAL LETTER IOTA WITH TONOS */ +{ XK_Greek_IOTAdiaeresis, 0x03aa }, /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ +{ XK_Greek_OMICRONaccent, 0x038c }, /* GREEK CAPITAL LETTER OMICRON WITH TONOS */ +{ XK_Greek_UPSILONaccent, 0x038e }, /* GREEK CAPITAL LETTER UPSILON WITH TONOS */ +{ XK_Greek_UPSILONdieresis, 0x03ab }, /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ +{ XK_Greek_OMEGAaccent, 0x038f }, /* GREEK CAPITAL LETTER OMEGA WITH TONOS */ +{ XK_Greek_accentdieresis, 0x0385 }, /* GREEK DIALYTIKA TONOS */ +{ XK_Greek_horizbar, 0x2015 }, /* HORIZONTAL BAR */ +{ XK_Greek_alphaaccent, 0x03ac }, /* GREEK SMALL LETTER ALPHA WITH TONOS */ +{ XK_Greek_epsilonaccent, 0x03ad }, /* GREEK SMALL LETTER EPSILON WITH TONOS */ +{ XK_Greek_etaaccent, 0x03ae }, /* GREEK SMALL LETTER ETA WITH TONOS */ +{ XK_Greek_iotaaccent, 0x03af }, /* GREEK SMALL LETTER IOTA WITH TONOS */ +{ XK_Greek_iotadieresis, 0x03ca }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA */ +{ XK_Greek_iotaaccentdieresis, 0x0390 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ +{ XK_Greek_omicronaccent, 0x03cc }, /* GREEK SMALL LETTER OMICRON WITH TONOS */ +{ XK_Greek_upsilonaccent, 0x03cd }, /* GREEK SMALL LETTER UPSILON WITH TONOS */ +{ XK_Greek_upsilondieresis, 0x03cb }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ +{ XK_Greek_upsilonaccentdieresis, 0x03b0 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ +{ XK_Greek_omegaaccent, 0x03ce }, /* GREEK SMALL LETTER OMEGA WITH TONOS */ +{ XK_Greek_ALPHA, 0x0391 }, /* GREEK CAPITAL LETTER ALPHA */ +{ XK_Greek_BETA, 0x0392 }, /* GREEK CAPITAL LETTER BETA */ +{ XK_Greek_GAMMA, 0x0393 }, /* GREEK CAPITAL LETTER GAMMA */ +{ XK_Greek_DELTA, 0x0394 }, /* GREEK CAPITAL LETTER DELTA */ +{ XK_Greek_EPSILON, 0x0395 }, /* GREEK CAPITAL LETTER EPSILON */ +{ XK_Greek_ZETA, 0x0396 }, /* GREEK CAPITAL LETTER ZETA */ +{ XK_Greek_ETA, 0x0397 }, /* GREEK CAPITAL LETTER ETA */ +{ XK_Greek_THETA, 0x0398 }, /* GREEK CAPITAL LETTER THETA */ +{ XK_Greek_IOTA, 0x0399 }, /* GREEK CAPITAL LETTER IOTA */ +{ XK_Greek_KAPPA, 0x039a }, /* GREEK CAPITAL LETTER KAPPA */ +{ XK_Greek_LAMBDA, 0x039b }, /* GREEK CAPITAL LETTER LAMDA */ +{ XK_Greek_MU, 0x039c }, /* GREEK CAPITAL LETTER MU */ +{ XK_Greek_NU, 0x039d }, /* GREEK CAPITAL LETTER NU */ +{ XK_Greek_XI, 0x039e }, /* GREEK CAPITAL LETTER XI */ +{ XK_Greek_OMICRON, 0x039f }, /* GREEK CAPITAL LETTER OMICRON */ +{ XK_Greek_PI, 0x03a0 }, /* GREEK CAPITAL LETTER PI */ +{ XK_Greek_RHO, 0x03a1 }, /* GREEK CAPITAL LETTER RHO */ +{ XK_Greek_SIGMA, 0x03a3 }, /* GREEK CAPITAL LETTER SIGMA */ +{ XK_Greek_TAU, 0x03a4 }, /* GREEK CAPITAL LETTER TAU */ +{ XK_Greek_UPSILON, 0x03a5 }, /* GREEK CAPITAL LETTER UPSILON */ +{ XK_Greek_PHI, 0x03a6 }, /* GREEK CAPITAL LETTER PHI */ +{ XK_Greek_CHI, 0x03a7 }, /* GREEK CAPITAL LETTER CHI */ +{ XK_Greek_PSI, 0x03a8 }, /* GREEK CAPITAL LETTER PSI */ +{ XK_Greek_OMEGA, 0x03a9 }, /* GREEK CAPITAL LETTER OMEGA */ +{ XK_Greek_alpha, 0x03b1 }, /* GREEK SMALL LETTER ALPHA */ +{ XK_Greek_beta, 0x03b2 }, /* GREEK SMALL LETTER BETA */ +{ XK_Greek_gamma, 0x03b3 }, /* GREEK SMALL LETTER GAMMA */ +{ XK_Greek_delta, 0x03b4 }, /* GREEK SMALL LETTER DELTA */ +{ XK_Greek_epsilon, 0x03b5 }, /* GREEK SMALL LETTER EPSILON */ +{ XK_Greek_zeta, 0x03b6 }, /* GREEK SMALL LETTER ZETA */ +{ XK_Greek_eta, 0x03b7 }, /* GREEK SMALL LETTER ETA */ +{ XK_Greek_theta, 0x03b8 }, /* GREEK SMALL LETTER THETA */ +{ XK_Greek_iota, 0x03b9 }, /* GREEK SMALL LETTER IOTA */ +{ XK_Greek_kappa, 0x03ba }, /* GREEK SMALL LETTER KAPPA */ +{ XK_Greek_lambda, 0x03bb }, /* GREEK SMALL LETTER LAMDA */ +{ XK_Greek_mu, 0x03bc }, /* GREEK SMALL LETTER MU */ +{ XK_Greek_nu, 0x03bd }, /* GREEK SMALL LETTER NU */ +{ XK_Greek_xi, 0x03be }, /* GREEK SMALL LETTER XI */ +{ XK_Greek_omicron, 0x03bf }, /* GREEK SMALL LETTER OMICRON */ +{ XK_Greek_pi, 0x03c0 }, /* GREEK SMALL LETTER PI */ +{ XK_Greek_rho, 0x03c1 }, /* GREEK SMALL LETTER RHO */ +{ XK_Greek_sigma, 0x03c3 }, /* GREEK SMALL LETTER SIGMA */ +{ XK_Greek_finalsmallsigma, 0x03c2 }, /* GREEK SMALL LETTER FINAL SIGMA */ +{ XK_Greek_tau, 0x03c4 }, /* GREEK SMALL LETTER TAU */ +{ XK_Greek_upsilon, 0x03c5 }, /* GREEK SMALL LETTER UPSILON */ +{ XK_Greek_phi, 0x03c6 }, /* GREEK SMALL LETTER PHI */ +{ XK_Greek_chi, 0x03c7 }, /* GREEK SMALL LETTER CHI */ +{ XK_Greek_psi, 0x03c8 }, /* GREEK SMALL LETTER PSI */ +{ XK_Greek_omega, 0x03c9 }, /* GREEK SMALL LETTER OMEGA */ +{ XK_leftradical, 0x23b7 }, /* ??? */ +{ XK_topleftradical, 0x250c }, /* BOX DRAWINGS LIGHT DOWN AND RIGHT */ +{ XK_horizconnector, 0x2500 }, /* BOX DRAWINGS LIGHT HORIZONTAL */ +{ XK_topintegral, 0x2320 }, /* TOP HALF INTEGRAL */ +{ XK_botintegral, 0x2321 }, /* BOTTOM HALF INTEGRAL */ +{ XK_vertconnector, 0x2502 }, /* BOX DRAWINGS LIGHT VERTICAL */ +{ XK_topleftsqbracket, 0x23a1 }, /* ??? */ +{ XK_botleftsqbracket, 0x23a3 }, /* ??? */ +{ XK_toprightsqbracket, 0x23a4 }, /* ??? */ +{ XK_botrightsqbracket, 0x23a6 }, /* ??? */ +{ XK_topleftparens, 0x239b }, /* ??? */ +{ XK_botleftparens, 0x239d }, /* ??? */ +{ XK_toprightparens, 0x239e }, /* ??? */ +{ XK_botrightparens, 0x23a0 }, /* ??? */ +{ XK_leftmiddlecurlybrace, 0x23a8 }, /* ??? */ +{ XK_rightmiddlecurlybrace, 0x23ac }, /* ??? */ +{ XK_lessthanequal, 0x2264 }, /* LESS-THAN OR EQUAL TO */ +{ XK_notequal, 0x2260 }, /* NOT EQUAL TO */ +{ XK_greaterthanequal, 0x2265 }, /* GREATER-THAN OR EQUAL TO */ +{ XK_integral, 0x222b }, /* INTEGRAL */ +{ XK_therefore, 0x2234 }, /* THEREFORE */ +{ XK_variation, 0x221d }, /* PROPORTIONAL TO */ +{ XK_infinity, 0x221e }, /* INFINITY */ +{ XK_nabla, 0x2207 }, /* NABLA */ +{ XK_approximate, 0x223c }, /* TILDE OPERATOR */ +{ XK_similarequal, 0x2243 }, /* ASYMPTOTICALLY EQUAL TO */ +{ XK_ifonlyif, 0x21d4 }, /* LEFT RIGHT DOUBLE ARROW */ +{ XK_implies, 0x21d2 }, /* RIGHTWARDS DOUBLE ARROW */ +{ XK_identical, 0x2261 }, /* IDENTICAL TO */ +{ XK_radical, 0x221a }, /* SQUARE ROOT */ +{ XK_includedin, 0x2282 }, /* SUBSET OF */ +{ XK_includes, 0x2283 }, /* SUPERSET OF */ +{ XK_intersection, 0x2229 }, /* INTERSECTION */ +{ XK_union, 0x222a }, /* UNION */ +{ XK_logicaland, 0x2227 }, /* LOGICAL AND */ +{ XK_logicalor, 0x2228 }, /* LOGICAL OR */ +{ XK_partialderivative, 0x2202 }, /* PARTIAL DIFFERENTIAL */ +{ XK_function, 0x0192 }, /* LATIN SMALL LETTER F WITH HOOK */ +{ XK_leftarrow, 0x2190 }, /* LEFTWARDS ARROW */ +{ XK_uparrow, 0x2191 }, /* UPWARDS ARROW */ +{ XK_rightarrow, 0x2192 }, /* RIGHTWARDS ARROW */ +{ XK_downarrow, 0x2193 }, /* DOWNWARDS ARROW */ +/*{ XK_blank, ??? }, */ +{ XK_soliddiamond, 0x25c6 }, /* BLACK DIAMOND */ +{ XK_checkerboard, 0x2592 }, /* MEDIUM SHADE */ +{ XK_ht, 0x2409 }, /* SYMBOL FOR HORIZONTAL TABULATION */ +{ XK_ff, 0x240c }, /* SYMBOL FOR FORM FEED */ +{ XK_cr, 0x240d }, /* SYMBOL FOR CARRIAGE RETURN */ +{ XK_lf, 0x240a }, /* SYMBOL FOR LINE FEED */ +{ XK_nl, 0x2424 }, /* SYMBOL FOR NEWLINE */ +{ XK_vt, 0x240b }, /* SYMBOL FOR VERTICAL TABULATION */ +{ XK_lowrightcorner, 0x2518 }, /* BOX DRAWINGS LIGHT UP AND LEFT */ +{ XK_uprightcorner, 0x2510 }, /* BOX DRAWINGS LIGHT DOWN AND LEFT */ +{ XK_upleftcorner, 0x250c }, /* BOX DRAWINGS LIGHT DOWN AND RIGHT */ +{ XK_lowleftcorner, 0x2514 }, /* BOX DRAWINGS LIGHT UP AND RIGHT */ +{ XK_crossinglines, 0x253c }, /* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ +{ XK_horizlinescan1, 0x23ba }, /* HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ +{ XK_horizlinescan3, 0x23bb }, /* HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ +{ XK_horizlinescan5, 0x2500 }, /* BOX DRAWINGS LIGHT HORIZONTAL */ +{ XK_horizlinescan7, 0x23bc }, /* HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ +{ XK_horizlinescan9, 0x23bd }, /* HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ +{ XK_leftt, 0x251c }, /* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ +{ XK_rightt, 0x2524 }, /* BOX DRAWINGS LIGHT VERTICAL AND LEFT */ +{ XK_bott, 0x2534 }, /* BOX DRAWINGS LIGHT UP AND HORIZONTAL */ +{ XK_topt, 0x252c }, /* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ +{ XK_vertbar, 0x2502 }, /* BOX DRAWINGS LIGHT VERTICAL */ +{ XK_emspace, 0x2003 }, /* EM SPACE */ +{ XK_enspace, 0x2002 }, /* EN SPACE */ +{ XK_em3space, 0x2004 }, /* THREE-PER-EM SPACE */ +{ XK_em4space, 0x2005 }, /* FOUR-PER-EM SPACE */ +{ XK_digitspace, 0x2007 }, /* FIGURE SPACE */ +{ XK_punctspace, 0x2008 }, /* PUNCTUATION SPACE */ +{ XK_thinspace, 0x2009 }, /* THIN SPACE */ +{ XK_hairspace, 0x200a }, /* HAIR SPACE */ +{ XK_emdash, 0x2014 }, /* EM DASH */ +{ XK_endash, 0x2013 }, /* EN DASH */ +/*{ XK_signifblank, ??? }, */ +{ XK_ellipsis, 0x2026 }, /* HORIZONTAL ELLIPSIS */ +{ XK_doubbaselinedot, 0x2025 }, /* TWO DOT LEADER */ +{ XK_onethird, 0x2153 }, /* VULGAR FRACTION ONE THIRD */ +{ XK_twothirds, 0x2154 }, /* VULGAR FRACTION TWO THIRDS */ +{ XK_onefifth, 0x2155 }, /* VULGAR FRACTION ONE FIFTH */ +{ XK_twofifths, 0x2156 }, /* VULGAR FRACTION TWO FIFTHS */ +{ XK_threefifths, 0x2157 }, /* VULGAR FRACTION THREE FIFTHS */ +{ XK_fourfifths, 0x2158 }, /* VULGAR FRACTION FOUR FIFTHS */ +{ XK_onesixth, 0x2159 }, /* VULGAR FRACTION ONE SIXTH */ +{ XK_fivesixths, 0x215a }, /* VULGAR FRACTION FIVE SIXTHS */ +{ XK_careof, 0x2105 }, /* CARE OF */ +{ XK_figdash, 0x2012 }, /* FIGURE DASH */ +{ XK_leftanglebracket, 0x2329 }, /* LEFT-POINTING ANGLE BRACKET */ +/*{ XK_decimalpoint, ??? }, */ +{ XK_rightanglebracket, 0x232a }, /* RIGHT-POINTING ANGLE BRACKET */ +/*{ XK_marker, ??? }, */ +{ XK_oneeighth, 0x215b }, /* VULGAR FRACTION ONE EIGHTH */ +{ XK_threeeighths, 0x215c }, /* VULGAR FRACTION THREE EIGHTHS */ +{ XK_fiveeighths, 0x215d }, /* VULGAR FRACTION FIVE EIGHTHS */ +{ XK_seveneighths, 0x215e }, /* VULGAR FRACTION SEVEN EIGHTHS */ +{ XK_trademark, 0x2122 }, /* TRADE MARK SIGN */ +{ XK_signaturemark, 0x2613 }, /* SALTIRE */ +/*{ XK_trademarkincircle, ??? }, */ +{ XK_leftopentriangle, 0x25c1 }, /* WHITE LEFT-POINTING TRIANGLE */ +{ XK_rightopentriangle, 0x25b7 }, /* WHITE RIGHT-POINTING TRIANGLE */ +{ XK_emopencircle, 0x25cb }, /* WHITE CIRCLE */ +{ XK_emopenrectangle, 0x25af }, /* WHITE VERTICAL RECTANGLE */ +{ XK_leftsinglequotemark, 0x2018 }, /* LEFT SINGLE QUOTATION MARK */ +{ XK_rightsinglequotemark, 0x2019 }, /* RIGHT SINGLE QUOTATION MARK */ +{ XK_leftdoublequotemark, 0x201c }, /* LEFT DOUBLE QUOTATION MARK */ +{ XK_rightdoublequotemark, 0x201d }, /* RIGHT DOUBLE QUOTATION MARK */ +{ XK_prescription, 0x211e }, /* PRESCRIPTION TAKE */ +{ XK_minutes, 0x2032 }, /* PRIME */ +{ XK_seconds, 0x2033 }, /* DOUBLE PRIME */ +{ XK_latincross, 0x271d }, /* LATIN CROSS */ +/*{ XK_hexagram, ??? }, */ +{ XK_filledrectbullet, 0x25ac }, /* BLACK RECTANGLE */ +{ XK_filledlefttribullet, 0x25c0 }, /* BLACK LEFT-POINTING TRIANGLE */ +{ XK_filledrighttribullet, 0x25b6 }, /* BLACK RIGHT-POINTING TRIANGLE */ +{ XK_emfilledcircle, 0x25cf }, /* BLACK CIRCLE */ +{ XK_emfilledrect, 0x25ae }, /* BLACK VERTICAL RECTANGLE */ +{ XK_enopencircbullet, 0x25e6 }, /* WHITE BULLET */ +{ XK_enopensquarebullet, 0x25ab }, /* WHITE SMALL SQUARE */ +{ XK_openrectbullet, 0x25ad }, /* WHITE RECTANGLE */ +{ XK_opentribulletup, 0x25b3 }, /* WHITE UP-POINTING TRIANGLE */ +{ XK_opentribulletdown, 0x25bd }, /* WHITE DOWN-POINTING TRIANGLE */ +{ XK_openstar, 0x2606 }, /* WHITE STAR */ +{ XK_enfilledcircbullet, 0x2022 }, /* BULLET */ +{ XK_enfilledsqbullet, 0x25aa }, /* BLACK SMALL SQUARE */ +{ XK_filledtribulletup, 0x25b2 }, /* BLACK UP-POINTING TRIANGLE */ +{ XK_filledtribulletdown, 0x25bc }, /* BLACK DOWN-POINTING TRIANGLE */ +{ XK_leftpointer, 0x261c }, /* WHITE LEFT POINTING INDEX */ +{ XK_rightpointer, 0x261e }, /* WHITE RIGHT POINTING INDEX */ +{ XK_club, 0x2663 }, /* BLACK CLUB SUIT */ +{ XK_diamond, 0x2666 }, /* BLACK DIAMOND SUIT */ +{ XK_heart, 0x2665 }, /* BLACK HEART SUIT */ +{ XK_maltesecross, 0x2720 }, /* MALTESE CROSS */ +{ XK_dagger, 0x2020 }, /* DAGGER */ +{ XK_doubledagger, 0x2021 }, /* DOUBLE DAGGER */ +{ XK_checkmark, 0x2713 }, /* CHECK MARK */ +{ XK_ballotcross, 0x2717 }, /* BALLOT X */ +{ XK_musicalsharp, 0x266f }, /* MUSIC SHARP SIGN */ +{ XK_musicalflat, 0x266d }, /* MUSIC FLAT SIGN */ +{ XK_malesymbol, 0x2642 }, /* MALE SIGN */ +{ XK_femalesymbol, 0x2640 }, /* FEMALE SIGN */ +{ XK_telephone, 0x260e }, /* BLACK TELEPHONE */ +{ XK_telephonerecorder, 0x2315 }, /* TELEPHONE RECORDER */ +{ XK_phonographcopyright, 0x2117 }, /* SOUND RECORDING COPYRIGHT */ +{ XK_caret, 0x2038 }, /* CARET */ +{ XK_singlelowquotemark, 0x201a }, /* SINGLE LOW-9 QUOTATION MARK */ +{ XK_doublelowquotemark, 0x201e }, /* DOUBLE LOW-9 QUOTATION MARK */ +/*{ XK_cursor, ??? }, */ +{ XK_leftcaret, 0x003c }, /* LESS-THAN SIGN */ +{ XK_rightcaret, 0x003e }, /* GREATER-THAN SIGN */ +{ XK_downcaret, 0x2228 }, /* LOGICAL OR */ +{ XK_upcaret, 0x2227 }, /* LOGICAL AND */ +{ XK_overbar, 0x00af }, /* MACRON */ +{ XK_downtack, 0x22a5 }, /* UP TACK */ +{ XK_upshoe, 0x2229 }, /* INTERSECTION */ +{ XK_downstile, 0x230a }, /* LEFT FLOOR */ +{ XK_underbar, 0x005f }, /* LOW LINE */ +{ XK_jot, 0x2218 }, /* RING OPERATOR */ +{ XK_quad, 0x2395 }, /* APL FUNCTIONAL SYMBOL QUAD */ +{ XK_uptack, 0x22a4 }, /* DOWN TACK */ +{ XK_circle, 0x25cb }, /* WHITE CIRCLE */ +{ XK_upstile, 0x2308 }, /* LEFT CEILING */ +{ XK_downshoe, 0x222a }, /* UNION */ +{ XK_rightshoe, 0x2283 }, /* SUPERSET OF */ +{ XK_leftshoe, 0x2282 }, /* SUBSET OF */ +{ XK_lefttack, 0x22a2 }, /* RIGHT TACK */ +{ XK_righttack, 0x22a3 }, /* LEFT TACK */ +{ XK_hebrew_doublelowline, 0x2017 }, /* DOUBLE LOW LINE */ +{ XK_hebrew_aleph, 0x05d0 }, /* HEBREW LETTER ALEF */ +{ XK_hebrew_bet, 0x05d1 }, /* HEBREW LETTER BET */ +{ XK_hebrew_gimel, 0x05d2 }, /* HEBREW LETTER GIMEL */ +{ XK_hebrew_dalet, 0x05d3 }, /* HEBREW LETTER DALET */ +{ XK_hebrew_he, 0x05d4 }, /* HEBREW LETTER HE */ +{ XK_hebrew_waw, 0x05d5 }, /* HEBREW LETTER VAV */ +{ XK_hebrew_zain, 0x05d6 }, /* HEBREW LETTER ZAYIN */ +{ XK_hebrew_chet, 0x05d7 }, /* HEBREW LETTER HET */ +{ XK_hebrew_tet, 0x05d8 }, /* HEBREW LETTER TET */ +{ XK_hebrew_yod, 0x05d9 }, /* HEBREW LETTER YOD */ +{ XK_hebrew_finalkaph, 0x05da }, /* HEBREW LETTER FINAL KAF */ +{ XK_hebrew_kaph, 0x05db }, /* HEBREW LETTER KAF */ +{ XK_hebrew_lamed, 0x05dc }, /* HEBREW LETTER LAMED */ +{ XK_hebrew_finalmem, 0x05dd }, /* HEBREW LETTER FINAL MEM */ +{ XK_hebrew_mem, 0x05de }, /* HEBREW LETTER MEM */ +{ XK_hebrew_finalnun, 0x05df }, /* HEBREW LETTER FINAL NUN */ +{ XK_hebrew_nun, 0x05e0 }, /* HEBREW LETTER NUN */ +{ XK_hebrew_samech, 0x05e1 }, /* HEBREW LETTER SAMEKH */ +{ XK_hebrew_ayin, 0x05e2 }, /* HEBREW LETTER AYIN */ +{ XK_hebrew_finalpe, 0x05e3 }, /* HEBREW LETTER FINAL PE */ +{ XK_hebrew_pe, 0x05e4 }, /* HEBREW LETTER PE */ +{ XK_hebrew_finalzade, 0x05e5 }, /* HEBREW LETTER FINAL TSADI */ +{ XK_hebrew_zade, 0x05e6 }, /* HEBREW LETTER TSADI */ +{ XK_hebrew_qoph, 0x05e7 }, /* HEBREW LETTER QOF */ +{ XK_hebrew_resh, 0x05e8 }, /* HEBREW LETTER RESH */ +{ XK_hebrew_shin, 0x05e9 }, /* HEBREW LETTER SHIN */ +{ XK_hebrew_taw, 0x05ea }, /* HEBREW LETTER TAV */ +{ XK_Thai_kokai, 0x0e01 }, /* THAI CHARACTER KO KAI */ +{ XK_Thai_khokhai, 0x0e02 }, /* THAI CHARACTER KHO KHAI */ +{ XK_Thai_khokhuat, 0x0e03 }, /* THAI CHARACTER KHO KHUAT */ +{ XK_Thai_khokhwai, 0x0e04 }, /* THAI CHARACTER KHO KHWAI */ +{ XK_Thai_khokhon, 0x0e05 }, /* THAI CHARACTER KHO KHON */ +{ XK_Thai_khorakhang, 0x0e06 }, /* THAI CHARACTER KHO RAKHANG */ +{ XK_Thai_ngongu, 0x0e07 }, /* THAI CHARACTER NGO NGU */ +{ XK_Thai_chochan, 0x0e08 }, /* THAI CHARACTER CHO CHAN */ +{ XK_Thai_choching, 0x0e09 }, /* THAI CHARACTER CHO CHING */ +{ XK_Thai_chochang, 0x0e0a }, /* THAI CHARACTER CHO CHANG */ +{ XK_Thai_soso, 0x0e0b }, /* THAI CHARACTER SO SO */ +{ XK_Thai_chochoe, 0x0e0c }, /* THAI CHARACTER CHO CHOE */ +{ XK_Thai_yoying, 0x0e0d }, /* THAI CHARACTER YO YING */ +{ XK_Thai_dochada, 0x0e0e }, /* THAI CHARACTER DO CHADA */ +{ XK_Thai_topatak, 0x0e0f }, /* THAI CHARACTER TO PATAK */ +{ XK_Thai_thothan, 0x0e10 }, /* THAI CHARACTER THO THAN */ +{ XK_Thai_thonangmontho, 0x0e11 }, /* THAI CHARACTER THO NANGMONTHO */ +{ XK_Thai_thophuthao, 0x0e12 }, /* THAI CHARACTER THO PHUTHAO */ +{ XK_Thai_nonen, 0x0e13 }, /* THAI CHARACTER NO NEN */ +{ XK_Thai_dodek, 0x0e14 }, /* THAI CHARACTER DO DEK */ +{ XK_Thai_totao, 0x0e15 }, /* THAI CHARACTER TO TAO */ +{ XK_Thai_thothung, 0x0e16 }, /* THAI CHARACTER THO THUNG */ +{ XK_Thai_thothahan, 0x0e17 }, /* THAI CHARACTER THO THAHAN */ +{ XK_Thai_thothong, 0x0e18 }, /* THAI CHARACTER THO THONG */ +{ XK_Thai_nonu, 0x0e19 }, /* THAI CHARACTER NO NU */ +{ XK_Thai_bobaimai, 0x0e1a }, /* THAI CHARACTER BO BAIMAI */ +{ XK_Thai_popla, 0x0e1b }, /* THAI CHARACTER PO PLA */ +{ XK_Thai_phophung, 0x0e1c }, /* THAI CHARACTER PHO PHUNG */ +{ XK_Thai_fofa, 0x0e1d }, /* THAI CHARACTER FO FA */ +{ XK_Thai_phophan, 0x0e1e }, /* THAI CHARACTER PHO PHAN */ +{ XK_Thai_fofan, 0x0e1f }, /* THAI CHARACTER FO FAN */ +{ XK_Thai_phosamphao, 0x0e20 }, /* THAI CHARACTER PHO SAMPHAO */ +{ XK_Thai_moma, 0x0e21 }, /* THAI CHARACTER MO MA */ +{ XK_Thai_yoyak, 0x0e22 }, /* THAI CHARACTER YO YAK */ +{ XK_Thai_rorua, 0x0e23 }, /* THAI CHARACTER RO RUA */ +{ XK_Thai_ru, 0x0e24 }, /* THAI CHARACTER RU */ +{ XK_Thai_loling, 0x0e25 }, /* THAI CHARACTER LO LING */ +{ XK_Thai_lu, 0x0e26 }, /* THAI CHARACTER LU */ +{ XK_Thai_wowaen, 0x0e27 }, /* THAI CHARACTER WO WAEN */ +{ XK_Thai_sosala, 0x0e28 }, /* THAI CHARACTER SO SALA */ +{ XK_Thai_sorusi, 0x0e29 }, /* THAI CHARACTER SO RUSI */ +{ XK_Thai_sosua, 0x0e2a }, /* THAI CHARACTER SO SUA */ +{ XK_Thai_hohip, 0x0e2b }, /* THAI CHARACTER HO HIP */ +{ XK_Thai_lochula, 0x0e2c }, /* THAI CHARACTER LO CHULA */ +{ XK_Thai_oang, 0x0e2d }, /* THAI CHARACTER O ANG */ +{ XK_Thai_honokhuk, 0x0e2e }, /* THAI CHARACTER HO NOKHUK */ +{ XK_Thai_paiyannoi, 0x0e2f }, /* THAI CHARACTER PAIYANNOI */ +{ XK_Thai_saraa, 0x0e30 }, /* THAI CHARACTER SARA A */ +{ XK_Thai_maihanakat, 0x0e31 }, /* THAI CHARACTER MAI HAN-AKAT */ +{ XK_Thai_saraaa, 0x0e32 }, /* THAI CHARACTER SARA AA */ +{ XK_Thai_saraam, 0x0e33 }, /* THAI CHARACTER SARA AM */ +{ XK_Thai_sarai, 0x0e34 }, /* THAI CHARACTER SARA I */ +{ XK_Thai_saraii, 0x0e35 }, /* THAI CHARACTER SARA II */ +{ XK_Thai_saraue, 0x0e36 }, /* THAI CHARACTER SARA UE */ +{ XK_Thai_sarauee, 0x0e37 }, /* THAI CHARACTER SARA UEE */ +{ XK_Thai_sarau, 0x0e38 }, /* THAI CHARACTER SARA U */ +{ XK_Thai_sarauu, 0x0e39 }, /* THAI CHARACTER SARA UU */ +{ XK_Thai_phinthu, 0x0e3a }, /* THAI CHARACTER PHINTHU */ +/*{ XK_Thai_maihanakat_maitho, ??? }, */ +{ XK_Thai_baht, 0x0e3f }, /* THAI CURRENCY SYMBOL BAHT */ +{ XK_Thai_sarae, 0x0e40 }, /* THAI CHARACTER SARA E */ +{ XK_Thai_saraae, 0x0e41 }, /* THAI CHARACTER SARA AE */ +{ XK_Thai_sarao, 0x0e42 }, /* THAI CHARACTER SARA O */ +{ XK_Thai_saraaimaimuan, 0x0e43 }, /* THAI CHARACTER SARA AI MAIMUAN */ +{ XK_Thai_saraaimaimalai, 0x0e44 }, /* THAI CHARACTER SARA AI MAIMALAI */ +{ XK_Thai_lakkhangyao, 0x0e45 }, /* THAI CHARACTER LAKKHANGYAO */ +{ XK_Thai_maiyamok, 0x0e46 }, /* THAI CHARACTER MAIYAMOK */ +{ XK_Thai_maitaikhu, 0x0e47 }, /* THAI CHARACTER MAITAIKHU */ +{ XK_Thai_maiek, 0x0e48 }, /* THAI CHARACTER MAI EK */ +{ XK_Thai_maitho, 0x0e49 }, /* THAI CHARACTER MAI THO */ +{ XK_Thai_maitri, 0x0e4a }, /* THAI CHARACTER MAI TRI */ +{ XK_Thai_maichattawa, 0x0e4b }, /* THAI CHARACTER MAI CHATTAWA */ +{ XK_Thai_thanthakhat, 0x0e4c }, /* THAI CHARACTER THANTHAKHAT */ +{ XK_Thai_nikhahit, 0x0e4d }, /* THAI CHARACTER NIKHAHIT */ +{ XK_Thai_leksun, 0x0e50 }, /* THAI DIGIT ZERO */ +{ XK_Thai_leknung, 0x0e51 }, /* THAI DIGIT ONE */ +{ XK_Thai_leksong, 0x0e52 }, /* THAI DIGIT TWO */ +{ XK_Thai_leksam, 0x0e53 }, /* THAI DIGIT THREE */ +{ XK_Thai_leksi, 0x0e54 }, /* THAI DIGIT FOUR */ +{ XK_Thai_lekha, 0x0e55 }, /* THAI DIGIT FIVE */ +{ XK_Thai_lekhok, 0x0e56 }, /* THAI DIGIT SIX */ +{ XK_Thai_lekchet, 0x0e57 }, /* THAI DIGIT SEVEN */ +{ XK_Thai_lekpaet, 0x0e58 }, /* THAI DIGIT EIGHT */ +{ XK_Thai_lekkao, 0x0e59 }, /* THAI DIGIT NINE */ +{ XK_Hangul_Kiyeog, 0x3131 }, /* HANGUL LETTER KIYEOK */ +{ XK_Hangul_SsangKiyeog, 0x3132 }, /* HANGUL LETTER SSANGKIYEOK */ +{ XK_Hangul_KiyeogSios, 0x3133 }, /* HANGUL LETTER KIYEOK-SIOS */ +{ XK_Hangul_Nieun, 0x3134 }, /* HANGUL LETTER NIEUN */ +{ XK_Hangul_NieunJieuj, 0x3135 }, /* HANGUL LETTER NIEUN-CIEUC */ +{ XK_Hangul_NieunHieuh, 0x3136 }, /* HANGUL LETTER NIEUN-HIEUH */ +{ XK_Hangul_Dikeud, 0x3137 }, /* HANGUL LETTER TIKEUT */ +{ XK_Hangul_SsangDikeud, 0x3138 }, /* HANGUL LETTER SSANGTIKEUT */ +{ XK_Hangul_Rieul, 0x3139 }, /* HANGUL LETTER RIEUL */ +{ XK_Hangul_RieulKiyeog, 0x313a }, /* HANGUL LETTER RIEUL-KIYEOK */ +{ XK_Hangul_RieulMieum, 0x313b }, /* HANGUL LETTER RIEUL-MIEUM */ +{ XK_Hangul_RieulPieub, 0x313c }, /* HANGUL LETTER RIEUL-PIEUP */ +{ XK_Hangul_RieulSios, 0x313d }, /* HANGUL LETTER RIEUL-SIOS */ +{ XK_Hangul_RieulTieut, 0x313e }, /* HANGUL LETTER RIEUL-THIEUTH */ +{ XK_Hangul_RieulPhieuf, 0x313f }, /* HANGUL LETTER RIEUL-PHIEUPH */ +{ XK_Hangul_RieulHieuh, 0x3140 }, /* HANGUL LETTER RIEUL-HIEUH */ +{ XK_Hangul_Mieum, 0x3141 }, /* HANGUL LETTER MIEUM */ +{ XK_Hangul_Pieub, 0x3142 }, /* HANGUL LETTER PIEUP */ +{ XK_Hangul_SsangPieub, 0x3143 }, /* HANGUL LETTER SSANGPIEUP */ +{ XK_Hangul_PieubSios, 0x3144 }, /* HANGUL LETTER PIEUP-SIOS */ +{ XK_Hangul_Sios, 0x3145 }, /* HANGUL LETTER SIOS */ +{ XK_Hangul_SsangSios, 0x3146 }, /* HANGUL LETTER SSANGSIOS */ +{ XK_Hangul_Ieung, 0x3147 }, /* HANGUL LETTER IEUNG */ +{ XK_Hangul_Jieuj, 0x3148 }, /* HANGUL LETTER CIEUC */ +{ XK_Hangul_SsangJieuj, 0x3149 }, /* HANGUL LETTER SSANGCIEUC */ +{ XK_Hangul_Cieuc, 0x314a }, /* HANGUL LETTER CHIEUCH */ +{ XK_Hangul_Khieuq, 0x314b }, /* HANGUL LETTER KHIEUKH */ +{ XK_Hangul_Tieut, 0x314c }, /* HANGUL LETTER THIEUTH */ +{ XK_Hangul_Phieuf, 0x314d }, /* HANGUL LETTER PHIEUPH */ +{ XK_Hangul_Hieuh, 0x314e }, /* HANGUL LETTER HIEUH */ +{ XK_Hangul_A, 0x314f }, /* HANGUL LETTER A */ +{ XK_Hangul_AE, 0x3150 }, /* HANGUL LETTER AE */ +{ XK_Hangul_YA, 0x3151 }, /* HANGUL LETTER YA */ +{ XK_Hangul_YAE, 0x3152 }, /* HANGUL LETTER YAE */ +{ XK_Hangul_EO, 0x3153 }, /* HANGUL LETTER EO */ +{ XK_Hangul_E, 0x3154 }, /* HANGUL LETTER E */ +{ XK_Hangul_YEO, 0x3155 }, /* HANGUL LETTER YEO */ +{ XK_Hangul_YE, 0x3156 }, /* HANGUL LETTER YE */ +{ XK_Hangul_O, 0x3157 }, /* HANGUL LETTER O */ +{ XK_Hangul_WA, 0x3158 }, /* HANGUL LETTER WA */ +{ XK_Hangul_WAE, 0x3159 }, /* HANGUL LETTER WAE */ +{ XK_Hangul_OE, 0x315a }, /* HANGUL LETTER OE */ +{ XK_Hangul_YO, 0x315b }, /* HANGUL LETTER YO */ +{ XK_Hangul_U, 0x315c }, /* HANGUL LETTER U */ +{ XK_Hangul_WEO, 0x315d }, /* HANGUL LETTER WEO */ +{ XK_Hangul_WE, 0x315e }, /* HANGUL LETTER WE */ +{ XK_Hangul_WI, 0x315f }, /* HANGUL LETTER WI */ +{ XK_Hangul_YU, 0x3160 }, /* HANGUL LETTER YU */ +{ XK_Hangul_EU, 0x3161 }, /* HANGUL LETTER EU */ +{ XK_Hangul_YI, 0x3162 }, /* HANGUL LETTER YI */ +{ XK_Hangul_I, 0x3163 }, /* HANGUL LETTER I */ +{ XK_Hangul_J_Kiyeog, 0x11a8 }, /* HANGUL JONGSEONG KIYEOK */ +{ XK_Hangul_J_SsangKiyeog, 0x11a9 }, /* HANGUL JONGSEONG SSANGKIYEOK */ +{ XK_Hangul_J_KiyeogSios, 0x11aa }, /* HANGUL JONGSEONG KIYEOK-SIOS */ +{ XK_Hangul_J_Nieun, 0x11ab }, /* HANGUL JONGSEONG NIEUN */ +{ XK_Hangul_J_NieunJieuj, 0x11ac }, /* HANGUL JONGSEONG NIEUN-CIEUC */ +{ XK_Hangul_J_NieunHieuh, 0x11ad }, /* HANGUL JONGSEONG NIEUN-HIEUH */ +{ XK_Hangul_J_Dikeud, 0x11ae }, /* HANGUL JONGSEONG TIKEUT */ +{ XK_Hangul_J_Rieul, 0x11af }, /* HANGUL JONGSEONG RIEUL */ +{ XK_Hangul_J_RieulKiyeog, 0x11b0 }, /* HANGUL JONGSEONG RIEUL-KIYEOK */ +{ XK_Hangul_J_RieulMieum, 0x11b1 }, /* HANGUL JONGSEONG RIEUL-MIEUM */ +{ XK_Hangul_J_RieulPieub, 0x11b2 }, /* HANGUL JONGSEONG RIEUL-PIEUP */ +{ XK_Hangul_J_RieulSios, 0x11b3 }, /* HANGUL JONGSEONG RIEUL-SIOS */ +{ XK_Hangul_J_RieulTieut, 0x11b4 }, /* HANGUL JONGSEONG RIEUL-THIEUTH */ +{ XK_Hangul_J_RieulPhieuf, 0x11b5 }, /* HANGUL JONGSEONG RIEUL-PHIEUPH */ +{ XK_Hangul_J_RieulHieuh, 0x11b6 }, /* HANGUL JONGSEONG RIEUL-HIEUH */ +{ XK_Hangul_J_Mieum, 0x11b7 }, /* HANGUL JONGSEONG MIEUM */ +{ XK_Hangul_J_Pieub, 0x11b8 }, /* HANGUL JONGSEONG PIEUP */ +{ XK_Hangul_J_PieubSios, 0x11b9 }, /* HANGUL JONGSEONG PIEUP-SIOS */ +{ XK_Hangul_J_Sios, 0x11ba }, /* HANGUL JONGSEONG SIOS */ +{ XK_Hangul_J_SsangSios, 0x11bb }, /* HANGUL JONGSEONG SSANGSIOS */ +{ XK_Hangul_J_Ieung, 0x11bc }, /* HANGUL JONGSEONG IEUNG */ +{ XK_Hangul_J_Jieuj, 0x11bd }, /* HANGUL JONGSEONG CIEUC */ +{ XK_Hangul_J_Cieuc, 0x11be }, /* HANGUL JONGSEONG CHIEUCH */ +{ XK_Hangul_J_Khieuq, 0x11bf }, /* HANGUL JONGSEONG KHIEUKH */ +{ XK_Hangul_J_Tieut, 0x11c0 }, /* HANGUL JONGSEONG THIEUTH */ +{ XK_Hangul_J_Phieuf, 0x11c1 }, /* HANGUL JONGSEONG PHIEUPH */ +{ XK_Hangul_J_Hieuh, 0x11c2 }, /* HANGUL JONGSEONG HIEUH */ +{ XK_Hangul_RieulYeorinHieuh, 0x316d }, /* HANGUL LETTER RIEUL-YEORINHIEUH */ +{ XK_Hangul_SunkyeongeumMieum, 0x3171 }, /* HANGUL LETTER KAPYEOUNMIEUM */ +{ XK_Hangul_SunkyeongeumPieub, 0x3178 }, /* HANGUL LETTER KAPYEOUNPIEUP */ +{ XK_Hangul_PanSios, 0x317f }, /* HANGUL LETTER PANSIOS */ +{ XK_Hangul_KkogjiDalrinIeung, 0x3181 }, /* HANGUL LETTER YESIEUNG */ +{ XK_Hangul_SunkyeongeumPhieuf, 0x3184 }, /* HANGUL LETTER KAPYEOUNPHIEUPH */ +{ XK_Hangul_YeorinHieuh, 0x3186 }, /* HANGUL LETTER YEORINHIEUH */ +{ XK_Hangul_AraeA, 0x318d }, /* HANGUL LETTER ARAEA */ +{ XK_Hangul_AraeAE, 0x318e }, /* HANGUL LETTER ARAEAE */ +{ XK_Hangul_J_PanSios, 0x11eb }, /* HANGUL JONGSEONG PANSIOS */ +{ XK_Hangul_J_KkogjiDalrinIeung, 0x11f0 }, /* HANGUL JONGSEONG YESIEUNG */ +{ XK_Hangul_J_YeorinHieuh, 0x11f9 }, /* HANGUL JONGSEONG YEORINHIEUH */ +{ XK_Korean_Won, 0x20a9 }, /* WON SIGN */ +{ XK_OE, 0x0152 }, /* LATIN CAPITAL LIGATURE OE */ +{ XK_oe, 0x0153 }, /* LATIN SMALL LIGATURE OE */ +{ XK_Ydiaeresis, 0x0178 }, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ +{ XK_EuroSign, 0x20ac }, /* EURO SIGN */ + +/* combining dead keys */ +{ XK_dead_abovedot, 0x0307 }, /* COMBINING DOT ABOVE */ +{ XK_dead_abovering, 0x030a }, /* COMBINING RING ABOVE */ +{ XK_dead_acute, 0x0301 }, /* COMBINING ACUTE ACCENT */ +{ XK_dead_breve, 0x0306 }, /* COMBINING BREVE */ +{ XK_dead_caron, 0x030c }, /* COMBINING CARON */ +{ XK_dead_cedilla, 0x0327 }, /* COMBINING CEDILLA */ +{ XK_dead_circumflex, 0x0302 }, /* COMBINING CIRCUMFLEX ACCENT */ +{ XK_dead_diaeresis, 0x0308 }, /* COMBINING DIAERESIS */ +{ XK_dead_doubleacute, 0x030b }, /* COMBINING DOUBLE ACUTE ACCENT */ +{ XK_dead_grave, 0x0300 }, /* COMBINING GRAVE ACCENT */ +{ XK_dead_macron, 0x0304 }, /* COMBINING MACRON */ +{ XK_dead_ogonek, 0x0328 }, /* COMBINING OGONEK */ +{ XK_dead_tilde, 0x0303 } /* COMBINING TILDE */ }; +/* XXX -- map these too +XK_Cyrillic_GHE_bar +XK_Cyrillic_ZHE_descender +XK_Cyrillic_KA_descender +XK_Cyrillic_KA_vertstroke +XK_Cyrillic_EN_descender +XK_Cyrillic_U_straight +XK_Cyrillic_U_straight_bar +XK_Cyrillic_HA_descender +XK_Cyrillic_CHE_descender +XK_Cyrillic_CHE_vertstroke +XK_Cyrillic_SHHA +XK_Cyrillic_SCHWA +XK_Cyrillic_I_macron +XK_Cyrillic_O_bar +XK_Cyrillic_U_macron +XK_Cyrillic_ghe_bar +XK_Cyrillic_zhe_descender +XK_Cyrillic_ka_descender +XK_Cyrillic_ka_vertstroke +XK_Cyrillic_en_descender +XK_Cyrillic_u_straight +XK_Cyrillic_u_straight_bar +XK_Cyrillic_ha_descender +XK_Cyrillic_che_descender +XK_Cyrillic_che_vertstroke +XK_Cyrillic_shha +XK_Cyrillic_schwa +XK_Cyrillic_i_macron +XK_Cyrillic_o_bar +XK_Cyrillic_u_macron -static const KeySym s_rawDeadDecomposeTable[] = { - // non-dead version of dead keys - XK_grave, XK_dead_grave, XK_space, 0, - XK_acute, XK_dead_acute, XK_space, 0, - XK_asciicircum, XK_dead_circumflex, XK_space, 0, - XK_asciitilde, XK_dead_tilde, XK_space, 0, - XK_cedilla, XK_dead_cedilla, XK_space, 0, - XK_ogonek, XK_dead_ogonek, XK_space, 0, - XK_caron, XK_dead_caron, XK_space, 0, - XK_abovedot, XK_dead_abovedot, XK_space, 0, - XK_doubleacute, XK_dead_doubleacute, XK_space, 0, - XK_breve, XK_dead_breve, XK_space, 0, - XK_macron, XK_dead_macron, XK_space, 0, +XK_Armenian_eternity +XK_Armenian_ligature_ew +XK_Armenian_full_stop +XK_Armenian_verjaket +XK_Armenian_parenright +XK_Armenian_parenleft +XK_Armenian_guillemotright +XK_Armenian_guillemotleft +XK_Armenian_em_dash +XK_Armenian_dot +XK_Armenian_mijaket +XK_Armenian_but +XK_Armenian_separation_mark +XK_Armenian_comma +XK_Armenian_en_dash +XK_Armenian_hyphen +XK_Armenian_yentamna +XK_Armenian_ellipsis +XK_Armenian_amanak +XK_Armenian_exclam +XK_Armenian_accent +XK_Armenian_shesht +XK_Armenian_paruyk +XK_Armenian_question +XK_Armenian_AYB +XK_Armenian_ayb +XK_Armenian_BEN +XK_Armenian_ben +XK_Armenian_GIM +XK_Armenian_gim +XK_Armenian_DA +XK_Armenian_da +XK_Armenian_YECH +XK_Armenian_yech +XK_Armenian_ZA +XK_Armenian_za +XK_Armenian_E +XK_Armenian_e +XK_Armenian_AT +XK_Armenian_at +XK_Armenian_TO +XK_Armenian_to +XK_Armenian_ZHE +XK_Armenian_zhe +XK_Armenian_INI +XK_Armenian_ini +XK_Armenian_LYUN +XK_Armenian_lyun +XK_Armenian_KHE +XK_Armenian_khe +XK_Armenian_TSA +XK_Armenian_tsa +XK_Armenian_KEN +XK_Armenian_ken +XK_Armenian_HO +XK_Armenian_ho +XK_Armenian_DZA +XK_Armenian_dza +XK_Armenian_GHAT +XK_Armenian_ghat +XK_Armenian_TCHE +XK_Armenian_tche +XK_Armenian_MEN +XK_Armenian_men +XK_Armenian_HI +XK_Armenian_hi +XK_Armenian_NU +XK_Armenian_nu +XK_Armenian_SHA +XK_Armenian_sha +XK_Armenian_VO +XK_Armenian_vo +XK_Armenian_CHA +XK_Armenian_cha +XK_Armenian_PE +XK_Armenian_pe +XK_Armenian_JE +XK_Armenian_je +XK_Armenian_RA +XK_Armenian_ra +XK_Armenian_SE +XK_Armenian_se +XK_Armenian_VEV +XK_Armenian_vev +XK_Armenian_TYUN +XK_Armenian_tyun +XK_Armenian_RE +XK_Armenian_re +XK_Armenian_TSO +XK_Armenian_tso +XK_Armenian_VYUN +XK_Armenian_vyun +XK_Armenian_PYUR +XK_Armenian_pyur +XK_Armenian_KE +XK_Armenian_ke +XK_Armenian_O +XK_Armenian_o +XK_Armenian_FE +XK_Armenian_fe +XK_Armenian_apostrophe +XK_Armenian_section_sign - // Latin-1 (ISO 8859-1) - XK_Agrave, XK_dead_grave, XK_A, 0, - XK_Aacute, XK_dead_acute, XK_A, 0, - XK_Acircumflex, XK_dead_circumflex, XK_A, 0, - XK_Atilde, XK_dead_tilde, XK_A, 0, - XK_Adiaeresis, XK_dead_diaeresis, XK_A, 0, - XK_Aring, XK_dead_abovering, XK_A, 0, - XK_Ccedilla, XK_dead_cedilla, XK_C, 0, - XK_Egrave, XK_dead_grave, XK_E, 0, - XK_Eacute, XK_dead_acute, XK_E, 0, - XK_Ecircumflex, XK_dead_circumflex, XK_E, 0, - XK_Ediaeresis, XK_dead_diaeresis, XK_E, 0, - XK_Igrave, XK_dead_grave, XK_I, 0, - XK_Iacute, XK_dead_acute, XK_I, 0, - XK_Icircumflex, XK_dead_circumflex, XK_I, 0, - XK_Idiaeresis, XK_dead_diaeresis, XK_I, 0, - XK_Ntilde, XK_dead_tilde, XK_N, 0, - XK_Ograve, XK_dead_grave, XK_O, 0, - XK_Oacute, XK_dead_acute, XK_O, 0, - XK_Ocircumflex, XK_dead_circumflex, XK_O, 0, - XK_Otilde, XK_dead_tilde, XK_O, 0, - XK_Odiaeresis, XK_dead_diaeresis, XK_O, 0, - XK_Ugrave, XK_dead_grave, XK_U, 0, - XK_Uacute, XK_dead_acute, XK_U, 0, - XK_Ucircumflex, XK_dead_circumflex, XK_U, 0, - XK_Udiaeresis, XK_dead_diaeresis, XK_U, 0, - XK_Yacute, XK_dead_acute, XK_Y, 0, - XK_agrave, XK_dead_grave, XK_a, 0, - XK_aacute, XK_dead_acute, XK_a, 0, - XK_acircumflex, XK_dead_circumflex, XK_a, 0, - XK_atilde, XK_dead_tilde, XK_a, 0, - XK_adiaeresis, XK_dead_diaeresis, XK_a, 0, - XK_aring, XK_dead_abovering, XK_a, 0, - XK_ccedilla, XK_dead_cedilla, XK_c, 0, - XK_egrave, XK_dead_grave, XK_e, 0, - XK_eacute, XK_dead_acute, XK_e, 0, - XK_ecircumflex, XK_dead_circumflex, XK_e, 0, - XK_ediaeresis, XK_dead_diaeresis, XK_e, 0, - XK_igrave, XK_dead_grave, XK_i, 0, - XK_iacute, XK_dead_acute, XK_i, 0, - XK_icircumflex, XK_dead_circumflex, XK_i, 0, - XK_idiaeresis, XK_dead_diaeresis, XK_i, 0, - XK_ntilde, XK_dead_tilde, XK_n, 0, - XK_ograve, XK_dead_grave, XK_o, 0, - XK_oacute, XK_dead_acute, XK_o, 0, - XK_ocircumflex, XK_dead_circumflex, XK_o, 0, - XK_otilde, XK_dead_tilde, XK_o, 0, - XK_odiaeresis, XK_dead_diaeresis, XK_o, 0, - XK_ugrave, XK_dead_grave, XK_u, 0, - XK_uacute, XK_dead_acute, XK_u, 0, - XK_ucircumflex, XK_dead_circumflex, XK_u, 0, - XK_udiaeresis, XK_dead_diaeresis, XK_u, 0, - XK_yacute, XK_dead_acute, XK_y, 0, - XK_ydiaeresis, XK_dead_diaeresis, XK_y, 0, +XK_Georgian_an +XK_Georgian_ban +XK_Georgian_gan +XK_Georgian_don +XK_Georgian_en +XK_Georgian_vin +XK_Georgian_zen +XK_Georgian_tan +XK_Georgian_in +XK_Georgian_kan +XK_Georgian_las +XK_Georgian_man +XK_Georgian_nar +XK_Georgian_on +XK_Georgian_par +XK_Georgian_zhar +XK_Georgian_rae +XK_Georgian_san +XK_Georgian_tar +XK_Georgian_un +XK_Georgian_phar +XK_Georgian_khar +XK_Georgian_ghan +XK_Georgian_qar +XK_Georgian_shin +XK_Georgian_chin +XK_Georgian_can +XK_Georgian_jil +XK_Georgian_cil +XK_Georgian_char +XK_Georgian_xan +XK_Georgian_jhan +XK_Georgian_hae +XK_Georgian_he +XK_Georgian_hie +XK_Georgian_we +XK_Georgian_har +XK_Georgian_hoe +XK_Georgian_fi - // Latin-2 (ISO 8859-2) - XK_Aogonek, XK_dead_ogonek, XK_A, 0, - XK_Lcaron, XK_dead_caron, XK_L, 0, - XK_Sacute, XK_dead_acute, XK_S, 0, - XK_Scaron, XK_dead_caron, XK_S, 0, - XK_Scedilla, XK_dead_cedilla, XK_S, 0, - XK_Tcaron, XK_dead_caron, XK_T, 0, - XK_Zacute, XK_dead_acute, XK_Z, 0, - XK_Zcaron, XK_dead_caron, XK_Z, 0, - XK_Zabovedot, XK_dead_abovedot, XK_Z, 0, - XK_aogonek, XK_dead_ogonek, XK_a, 0, - XK_lcaron, XK_dead_caron, XK_l, 0, - XK_sacute, XK_dead_acute, XK_s, 0, - XK_scaron, XK_dead_caron, XK_s, 0, - XK_scedilla, XK_dead_cedilla, XK_s, 0, - XK_tcaron, XK_dead_caron, XK_t, 0, - XK_zacute, XK_dead_acute, XK_z, 0, - XK_zcaron, XK_dead_caron, XK_z, 0, - XK_zabovedot, XK_dead_abovedot, XK_z, 0, - XK_Racute, XK_dead_acute, XK_R, 0, - XK_Abreve, XK_dead_breve, XK_A, 0, - XK_Lacute, XK_dead_acute, XK_L, 0, - XK_Cacute, XK_dead_acute, XK_C, 0, - XK_Ccaron, XK_dead_caron, XK_C, 0, - XK_Eogonek, XK_dead_ogonek, XK_E, 0, - XK_Ecaron, XK_dead_caron, XK_E, 0, - XK_Dcaron, XK_dead_caron, XK_D, 0, - XK_Nacute, XK_dead_acute, XK_N, 0, - XK_Ncaron, XK_dead_caron, XK_N, 0, - XK_Odoubleacute, XK_dead_doubleacute, XK_O, 0, - XK_Rcaron, XK_dead_caron, XK_R, 0, - XK_Uring, XK_dead_abovering, XK_U, 0, - XK_Udoubleacute, XK_dead_doubleacute, XK_U, 0, - XK_Tcedilla, XK_dead_cedilla, XK_T, 0, - XK_racute, XK_dead_acute, XK_r, 0, - XK_abreve, XK_dead_breve, XK_a, 0, - XK_lacute, XK_dead_acute, XK_l, 0, - XK_cacute, XK_dead_acute, XK_c, 0, - XK_ccaron, XK_dead_caron, XK_c, 0, - XK_eogonek, XK_dead_ogonek, XK_e, 0, - XK_ecaron, XK_dead_caron, XK_e, 0, - XK_dcaron, XK_dead_caron, XK_d, 0, - XK_nacute, XK_dead_acute, XK_n, 0, - XK_ncaron, XK_dead_caron, XK_n, 0, - XK_odoubleacute, XK_dead_doubleacute, XK_o, 0, - XK_rcaron, XK_dead_caron, XK_r, 0, - XK_uring, XK_dead_abovering, XK_u, 0, - XK_udoubleacute, XK_dead_doubleacute, XK_u, 0, - XK_tcedilla, XK_dead_cedilla, XK_t, 0, +XK_Ccedillaabovedot +XK_Xabovedot +XK_Qabovedot +XK_Ibreve +XK_IE +XK_UO +XK_Zstroke +XK_Gcaron +XK_Obarred +XK_ccedillaabovedot +XK_xabovedot +XK_Ocaron +XK_qabovedot +XK_ibreve +XK_ie +XK_uo +XK_zstroke +XK_gcaron +XK_ocaron +XK_obarred +XK_SCHWA +XK_Lbelowdot +XK_Lstrokebelowdot +XK_Gtilde +XK_lbelowdot +XK_lstrokebelowdot +XK_gtilde +XK_schwa - // Latin-3 (ISO 8859-3) - XK_Hcircumflex, XK_dead_circumflex, XK_H, 0, - XK_Iabovedot, XK_dead_abovedot, XK_I, 0, - XK_Gbreve, XK_dead_breve, XK_G, 0, - XK_Jcircumflex, XK_dead_circumflex, XK_J, 0, - XK_hcircumflex, XK_dead_circumflex, XK_h, 0, - XK_gbreve, XK_dead_breve, XK_g, 0, - XK_jcircumflex, XK_dead_circumflex, XK_j, 0, - XK_Cabovedot, XK_dead_abovedot, XK_C, 0, - XK_Ccircumflex, XK_dead_circumflex, XK_C, 0, - XK_Gabovedot, XK_dead_abovedot, XK_G, 0, - XK_Gcircumflex, XK_dead_circumflex, XK_G, 0, - XK_Ubreve, XK_dead_breve, XK_U, 0, - XK_Scircumflex, XK_dead_circumflex, XK_S, 0, - XK_cabovedot, XK_dead_abovedot, XK_c, 0, - XK_ccircumflex, XK_dead_circumflex, XK_c, 0, - XK_gabovedot, XK_dead_abovedot, XK_g, 0, - XK_gcircumflex, XK_dead_circumflex, XK_g, 0, - XK_ubreve, XK_dead_breve, XK_u, 0, - XK_scircumflex, XK_dead_circumflex, XK_s, 0, +XK_Abelowdot +XK_abelowdot +XK_Ahook +XK_ahook +XK_Acircumflexacute +XK_acircumflexacute +XK_Acircumflexgrave +XK_acircumflexgrave +XK_Acircumflexhook +XK_acircumflexhook +XK_Acircumflextilde +XK_acircumflextilde +XK_Acircumflexbelowdot +XK_acircumflexbelowdot +XK_Abreveacute +XK_abreveacute +XK_Abrevegrave +XK_abrevegrave +XK_Abrevehook +XK_abrevehook +XK_Abrevetilde +XK_abrevetilde +XK_Abrevebelowdot +XK_abrevebelowdot +XK_Ebelowdot +XK_ebelowdot +XK_Ehook +XK_ehook +XK_Etilde +XK_etilde +XK_Ecircumflexacute +XK_ecircumflexacute +XK_Ecircumflexgrave +XK_ecircumflexgrave +XK_Ecircumflexhook +XK_ecircumflexhook +XK_Ecircumflextilde +XK_ecircumflextilde +XK_Ecircumflexbelowdot +XK_ecircumflexbelowdot +XK_Ihook +XK_ihook +XK_Ibelowdot +XK_ibelowdot +XK_Obelowdot +XK_obelowdot +XK_Ohook +XK_ohook +XK_Ocircumflexacute +XK_ocircumflexacute +XK_Ocircumflexgrave +XK_ocircumflexgrave +XK_Ocircumflexhook +XK_ocircumflexhook +XK_Ocircumflextilde +XK_ocircumflextilde +XK_Ocircumflexbelowdot +XK_ocircumflexbelowdot +XK_Ohornacute +XK_ohornacute +XK_Ohorngrave +XK_ohorngrave +XK_Ohornhook +XK_ohornhook +XK_Ohorntilde +XK_ohorntilde +XK_Ohornbelowdot +XK_ohornbelowdot +XK_Ubelowdot +XK_ubelowdot +XK_Uhook +XK_uhook +XK_Uhornacute +XK_uhornacute +XK_Uhorngrave +XK_uhorngrave +XK_Uhornhook +XK_uhornhook +XK_Uhorntilde +XK_uhorntilde +XK_Uhornbelowdot +XK_uhornbelowdot +XK_Ybelowdot +XK_ybelowdot +XK_Yhook +XK_yhook +XK_Ytilde +XK_ytilde +XK_Ohorn +XK_ohorn +XK_Uhorn +XK_uhorn +*/ - // Latin-4 (ISO 8859-4) - XK_scircumflex, XK_dead_circumflex, XK_s, 0, - XK_Rcedilla, XK_dead_cedilla, XK_R, 0, - XK_Itilde, XK_dead_tilde, XK_I, 0, - XK_Lcedilla, XK_dead_cedilla, XK_L, 0, - XK_Emacron, XK_dead_macron, XK_E, 0, - XK_Gcedilla, XK_dead_cedilla, XK_G, 0, - XK_rcedilla, XK_dead_cedilla, XK_r, 0, - XK_itilde, XK_dead_tilde, XK_i, 0, - XK_lcedilla, XK_dead_cedilla, XK_l, 0, - XK_emacron, XK_dead_macron, XK_e, 0, - XK_gcedilla, XK_dead_cedilla, XK_g, 0, - XK_Amacron, XK_dead_macron, XK_A, 0, - XK_Iogonek, XK_dead_ogonek, XK_I, 0, - XK_Eabovedot, XK_dead_abovedot, XK_E, 0, - XK_Imacron, XK_dead_macron, XK_I, 0, - XK_Ncedilla, XK_dead_cedilla, XK_N, 0, - XK_Omacron, XK_dead_macron, XK_O, 0, - XK_Kcedilla, XK_dead_cedilla, XK_K, 0, - XK_Uogonek, XK_dead_ogonek, XK_U, 0, - XK_Utilde, XK_dead_tilde, XK_U, 0, - XK_Umacron, XK_dead_macron, XK_U, 0, - XK_amacron, XK_dead_macron, XK_a, 0, - XK_iogonek, XK_dead_ogonek, XK_i, 0, - XK_eabovedot, XK_dead_abovedot, XK_e, 0, - XK_imacron, XK_dead_macron, XK_i, 0, - XK_ncedilla, XK_dead_cedilla, XK_n, 0, - XK_omacron, XK_dead_macron, XK_o, 0, - XK_kcedilla, XK_dead_cedilla, XK_k, 0, - XK_uogonek, XK_dead_ogonek, XK_u, 0, - XK_utilde, XK_dead_tilde, XK_u, 0, - XK_umacron, XK_dead_macron, XK_u, 0, - - // Latin-8 (ISO 8859-14) -#if defined(XK_Babovedot) - XK_Babovedot, XK_dead_abovedot, XK_B, 0, - XK_babovedot, XK_dead_abovedot, XK_b, 0, - XK_Dabovedot, XK_dead_abovedot, XK_D, 0, - XK_Wgrave, XK_dead_grave, XK_W, 0, - XK_Wacute, XK_dead_acute, XK_W, 0, - XK_dabovedot, XK_dead_abovedot, XK_d, 0, - XK_Ygrave, XK_dead_grave, XK_Y, 0, - XK_Fabovedot, XK_dead_abovedot, XK_F, 0, - XK_fabovedot, XK_dead_abovedot, XK_f, 0, - XK_Mabovedot, XK_dead_abovedot, XK_M, 0, - XK_mabovedot, XK_dead_abovedot, XK_m, 0, - XK_Pabovedot, XK_dead_abovedot, XK_P, 0, - XK_wgrave, XK_dead_grave, XK_w, 0, - XK_pabovedot, XK_dead_abovedot, XK_p, 0, - XK_wacute, XK_dead_acute, XK_w, 0, - XK_Sabovedot, XK_dead_abovedot, XK_S, 0, - XK_ygrave, XK_dead_grave, XK_y, 0, - XK_Wdiaeresis, XK_dead_diaeresis, XK_W, 0, - XK_wdiaeresis, XK_dead_diaeresis, XK_w, 0, - XK_sabovedot, XK_dead_abovedot, XK_s, 0, - XK_Wcircumflex, XK_dead_circumflex, XK_W, 0, - XK_Tabovedot, XK_dead_abovedot, XK_T, 0, - XK_Ycircumflex, XK_dead_circumflex, XK_Y, 0, - XK_wcircumflex, XK_dead_circumflex, XK_w, 0, - XK_tabovedot, XK_dead_abovedot, XK_t, 0, - XK_ycircumflex, XK_dead_circumflex, XK_y, 0, -#endif - - // Latin-9 (ISO 8859-15) -#if defined(XK_Ydiaeresis) - XK_Ydiaeresis, XK_dead_diaeresis, XK_Y, 0, -#endif - - // end of table - 0 -}; - -static const KeySym s_rawComposedDecomposeTable[] = { - XK_AE, XK_Multi_key, XK_A, XK_E, 0, - XK_Aacute, XK_Multi_key, XK_A, XK_apostrophe, 0, - XK_Acircumflex, XK_Multi_key, XK_A, XK_asciicircum, 0, - XK_Adiaeresis, XK_Multi_key, XK_A, XK_quotedbl, 0, - XK_Agrave, XK_Multi_key, XK_A, XK_grave, 0, - XK_Aring, XK_Multi_key, XK_A, XK_asterisk, 0, - XK_Atilde, XK_Multi_key, XK_A, XK_asciitilde, 0, - XK_Ccedilla, XK_Multi_key, XK_C, XK_comma, 0, - XK_ETH, XK_Multi_key, XK_D, XK_minus, 0, - XK_Eacute, XK_Multi_key, XK_E, XK_apostrophe, 0, - XK_Ecircumflex, XK_Multi_key, XK_E, XK_asciicircum, 0, - XK_Ediaeresis, XK_Multi_key, XK_E, XK_quotedbl, 0, - XK_Egrave, XK_Multi_key, XK_E, XK_grave, 0, - XK_Iacute, XK_Multi_key, XK_I, XK_apostrophe, 0, - XK_Icircumflex, XK_Multi_key, XK_I, XK_asciicircum, 0, - XK_Idiaeresis, XK_Multi_key, XK_I, XK_quotedbl, 0, - XK_Igrave, XK_Multi_key, XK_I, XK_grave, 0, - XK_Ntilde, XK_Multi_key, XK_N, XK_asciitilde, 0, - XK_Oacute, XK_Multi_key, XK_O, XK_apostrophe, 0, - XK_Ocircumflex, XK_Multi_key, XK_O, XK_asciicircum, 0, - XK_Odiaeresis, XK_Multi_key, XK_O, XK_quotedbl, 0, - XK_Ograve, XK_Multi_key, XK_O, XK_grave, 0, - XK_Ooblique, XK_Multi_key, XK_O, XK_slash, 0, - XK_Otilde, XK_Multi_key, XK_O, XK_asciitilde, 0, - XK_THORN, XK_Multi_key, XK_T, XK_H, 0, - XK_Uacute, XK_Multi_key, XK_U, XK_apostrophe, 0, - XK_Ucircumflex, XK_Multi_key, XK_U, XK_asciicircum, 0, - XK_Udiaeresis, XK_Multi_key, XK_U, XK_quotedbl, 0, - XK_Ugrave, XK_Multi_key, XK_U, XK_grave, 0, - XK_Yacute, XK_Multi_key, XK_Y, XK_apostrophe, 0, - XK_aacute, XK_Multi_key, XK_a, XK_apostrophe, 0, - XK_acircumflex, XK_Multi_key, XK_a, XK_asciicircum, 0, - XK_acute, XK_Multi_key, XK_apostrophe, XK_apostrophe, 0, - XK_adiaeresis, XK_Multi_key, XK_a, XK_quotedbl, 0, - XK_ae, XK_Multi_key, XK_a, XK_e, 0, - XK_agrave, XK_Multi_key, XK_a, XK_grave, 0, - XK_aring, XK_Multi_key, XK_a, XK_asterisk, 0, - XK_at, XK_Multi_key, XK_A, XK_T, 0, - XK_atilde, XK_Multi_key, XK_a, XK_asciitilde, 0, - XK_backslash, XK_Multi_key, XK_slash, XK_slash, 0, - XK_bar, XK_Multi_key, XK_L, XK_V, 0, - XK_braceleft, XK_Multi_key, XK_parenleft, XK_minus, 0, - XK_braceright, XK_Multi_key, XK_parenright, XK_minus, 0, - XK_bracketleft, XK_Multi_key, XK_parenleft, XK_parenleft, 0, - XK_bracketright, XK_Multi_key, XK_parenright, XK_parenright, 0, - XK_brokenbar, XK_Multi_key, XK_B, XK_V, 0, - XK_ccedilla, XK_Multi_key, XK_c, XK_comma, 0, - XK_cedilla, XK_Multi_key, XK_comma, XK_comma, 0, - XK_cent, XK_Multi_key, XK_c, XK_slash, 0, - XK_copyright, XK_Multi_key, XK_parenleft, XK_c, 0, - XK_currency, XK_Multi_key, XK_o, XK_x, 0, - XK_degree, XK_Multi_key, XK_0, XK_asciicircum, 0, - XK_diaeresis, XK_Multi_key, XK_quotedbl, XK_quotedbl, 0, - XK_division, XK_Multi_key, XK_colon, XK_minus, 0, - XK_eacute, XK_Multi_key, XK_e, XK_apostrophe, 0, - XK_ecircumflex, XK_Multi_key, XK_e, XK_asciicircum, 0, - XK_ediaeresis, XK_Multi_key, XK_e, XK_quotedbl, 0, - XK_egrave, XK_Multi_key, XK_e, XK_grave, 0, - XK_eth, XK_Multi_key, XK_d, XK_minus, 0, - XK_exclamdown, XK_Multi_key, XK_exclam, XK_exclam, 0, - XK_guillemotleft, XK_Multi_key, XK_less, XK_less, 0, - XK_guillemotright, XK_Multi_key, XK_greater, XK_greater, 0, - XK_numbersign, XK_Multi_key, XK_plus, XK_plus, 0, - XK_hyphen, XK_Multi_key, XK_minus, XK_minus, 0, - XK_iacute, XK_Multi_key, XK_i, XK_apostrophe, 0, - XK_icircumflex, XK_Multi_key, XK_i, XK_asciicircum, 0, - XK_idiaeresis, XK_Multi_key, XK_i, XK_quotedbl, 0, - XK_igrave, XK_Multi_key, XK_i, XK_grave, 0, - XK_macron, XK_Multi_key, XK_minus, XK_asciicircum, 0, - XK_masculine, XK_Multi_key, XK_o, XK_underscore, 0, - XK_mu, XK_Multi_key, XK_u, XK_slash, 0, - XK_multiply, XK_Multi_key, XK_x, XK_x, 0, - XK_nobreakspace, XK_Multi_key, XK_space, XK_space, 0, - XK_notsign, XK_Multi_key, XK_comma, XK_minus, 0, - XK_ntilde, XK_Multi_key, XK_n, XK_asciitilde, 0, - XK_oacute, XK_Multi_key, XK_o, XK_apostrophe, 0, - XK_ocircumflex, XK_Multi_key, XK_o, XK_asciicircum, 0, - XK_odiaeresis, XK_Multi_key, XK_o, XK_quotedbl, 0, - XK_ograve, XK_Multi_key, XK_o, XK_grave, 0, - XK_onehalf, XK_Multi_key, XK_1, XK_2, 0, - XK_onequarter, XK_Multi_key, XK_1, XK_4, 0, - XK_onesuperior, XK_Multi_key, XK_1, XK_asciicircum, 0, - XK_ordfeminine, XK_Multi_key, XK_a, XK_underscore, 0, - XK_oslash, XK_Multi_key, XK_o, XK_slash, 0, - XK_otilde, XK_Multi_key, XK_o, XK_asciitilde, 0, - XK_paragraph, XK_Multi_key, XK_p, XK_exclam, 0, - XK_periodcentered, XK_Multi_key, XK_period, XK_period, 0, - XK_plusminus, XK_Multi_key, XK_plus, XK_minus, 0, - XK_questiondown, XK_Multi_key, XK_question, XK_question, 0, - XK_registered, XK_Multi_key, XK_parenleft, XK_r, 0, - XK_section, XK_Multi_key, XK_s, XK_o, 0, - XK_ssharp, XK_Multi_key, XK_s, XK_s, 0, - XK_sterling, XK_Multi_key, XK_L, XK_minus, 0, - XK_thorn, XK_Multi_key, XK_t, XK_h, 0, - XK_threequarters, XK_Multi_key, XK_3, XK_4, 0, - XK_threesuperior, XK_Multi_key, XK_3, XK_asciicircum, 0, - XK_twosuperior, XK_Multi_key, XK_2, XK_asciicircum, 0, - XK_uacute, XK_Multi_key, XK_u, XK_apostrophe, 0, - XK_ucircumflex, XK_Multi_key, XK_u, XK_asciicircum, 0, - XK_udiaeresis, XK_Multi_key, XK_u, XK_quotedbl, 0, - XK_ugrave, XK_Multi_key, XK_u, XK_grave, 0, - XK_yacute, XK_Multi_key, XK_y, XK_apostrophe, 0, - XK_ydiaeresis, XK_Multi_key, XK_y, XK_quotedbl, 0, - XK_yen, XK_Multi_key, XK_y, XK_equal, 0, - - // end of table - 0 +// map "Internet" keys to KeyIDs +static const KeySym s_map1008FF[] = +{ + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp, + /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, + /* 0x18 */ kKeyWWWHome, kKeyAppMail, 0, kKeyWWWSearch, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, kKeyWWWBack, kKeyWWWForward, + /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, kKeyEject, 0, 0, 0, + /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -1143,9 +1265,6 @@ static const KeySym s_rawComposedDecomposeTable[] = { // CXWindowsUtil::CKeySymMap CXWindowsUtil::s_keySymToUCS4; -CXWindowsUtil::CUCS4Map CXWindowsUtil::s_UCS4ToKeySym; -CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_deadKeyDecomposedKeySyms; -CXWindowsUtil::CKeySymsMap CXWindowsUtil::s_composeDecomposedKeySyms; bool CXWindowsUtil::getWindowProperty(Display* display, Window window, @@ -1314,82 +1433,177 @@ CXWindowsUtil::getCurrentTime(Display* display, Window window) return xevent.xproperty.time; } +KeyID +CXWindowsUtil::mapKeySymToKeyID(KeySym k) +{ + initKeyMaps(); + + switch (k & 0xffffff00) { + case 0x0000: + // Latin-1 + return static_cast(k); + + case 0xfe00: + // ISO 9995 Function and Modifier Keys + switch (k) { + case XK_ISO_Left_Tab: + return kKeyLeftTab; + + case XK_ISO_Level3_Shift: + return kKeyAltGr; + + case XK_ISO_Next_Group: + return kKeyNextGroup; + + case XK_ISO_Prev_Group: + return kKeyPrevGroup; + + case XK_dead_grave: + return kKeyDeadGrave; + + case XK_dead_acute: + return kKeyDeadAcute; + + case XK_dead_circumflex: + return kKeyDeadCircumflex; + + case XK_dead_tilde: + return kKeyDeadTilde; + + case XK_dead_macron: + return kKeyDeadMacron; + + case XK_dead_breve: + return kKeyDeadBreve; + + case XK_dead_abovedot: + return kKeyDeadAbovedot; + + case XK_dead_diaeresis: + return kKeyDeadDiaeresis; + + case XK_dead_abovering: + return kKeyDeadAbovering; + + case XK_dead_doubleacute: + return kKeyDeadDoubleacute; + + case XK_dead_caron: + return kKeyDeadCaron; + + case XK_dead_cedilla: + return kKeyDeadCedilla; + + case XK_dead_ogonek: + return kKeyDeadOgonek; + + default: + return kKeyNone; + } + + case 0xff00: + // MISCELLANY + return static_cast(k - 0xff00 + 0xef00); + + case 0x1008ff00: + // "Internet" keys + return s_map1008FF[k & 0xff]; + + default: { + // lookup character in table + CKeySymMap::const_iterator index = s_keySymToUCS4.find(k); + if (index != s_keySymToUCS4.end()) { + return static_cast(index->second); + } + + // unknown character + return kKeyNone; + } + } +} + UInt32 -CXWindowsUtil::mapKeySymToUCS4(KeySym k) +CXWindowsUtil::getModifierBitForKeySym(KeySym keysym) { - initKeyMaps(); + switch (keysym) { + case XK_Shift_L: + case XK_Shift_R: + return kKeyModifierBitShift; - CKeySymMap::const_iterator index = s_keySymToUCS4.find(k); - if (index != s_keySymToUCS4.end()) { - return index->second; - } - else { - return 0x0000ffff; - } -} + case XK_Control_L: + case XK_Control_R: + return kKeyModifierBitControl; -KeySym -CXWindowsUtil::mapUCS4ToKeySym(UInt32 c) -{ - initKeyMaps(); + case XK_Alt_L: + case XK_Alt_R: + return kKeyModifierBitAlt; - CUCS4Map::const_iterator index = s_UCS4ToKeySym.find(c); - if (index != s_UCS4ToKeySym.end()) { - return index->second; - } - else { - return NoSymbol; - } -} + case XK_Meta_L: + case XK_Meta_R: + return kKeyModifierBitMeta; -bool -CXWindowsUtil::decomposeKeySymWithDeadKeys(KeySym keysym, KeySyms& decomposed) -{ - // unfortunately, X11 doesn't appear to have any way of - // decomposing a keysym into its component keysyms. we'll - // use a lookup table for certain character sets. - initKeyMaps(); - CKeySymsMap::const_iterator i = s_deadKeyDecomposedKeySyms.find(keysym); - if (i != s_deadKeyDecomposedKeySyms.end()) { - decomposed = i->second; - return true; - } - return false; -} + case XK_Super_L: + case XK_Super_R: + case XK_Hyper_L: + case XK_Hyper_R: + return kKeyModifierBitSuper; -bool -CXWindowsUtil::decomposeKeySymWithCompose(KeySym keysym, KeySyms& decomposed) -{ - // unfortunately, X11 doesn't appear to have any way of - // decomposing a keysym into its component keysyms. we'll - // use a lookup table for certain character sets. - initKeyMaps(); - CKeySymsMap::const_iterator i = s_composeDecomposedKeySyms.find(keysym); - if (i != s_composeDecomposedKeySyms.end()) { - decomposed = i->second; - return true; + case XK_Mode_switch: + case XK_ISO_Level3_Shift: + return kKeyModifierBitAltGr; + + case XK_Caps_Lock: + return kKeyModifierBitCapsLock; + + case XK_Num_Lock: + return kKeyModifierBitNumLock; + + case XK_Scroll_Lock: + return kKeyModifierBitScrollLock; + + default: + return kKeyModifierBitNone; } - return false; } CString CXWindowsUtil::atomToString(Display* display, Atom atom) { + if (atom == 0) { + return "None"; + } + + bool error = false; + CXWindowsUtil::CErrorLock lock(display, &error); char* name = XGetAtomName(display, atom); - CString msg = CStringUtil::print("%s (%d)", name, (int)atom); - XFree(name); - return msg; + if (error) { + return CStringUtil::print(" (%d)", (int)atom); + } + else { + CString msg = CStringUtil::print("%s (%d)", name, (int)atom); + XFree(name); + return msg; + } } CString CXWindowsUtil::atomsToString(Display* display, const Atom* atom, UInt32 num) { char** names = new char*[num]; + bool error = false; + CXWindowsUtil::CErrorLock lock(display, &error); XGetAtomNames(display, const_cast(atom), (int)num, names); CString msg; - for (UInt32 i = 0; i < num; ++i) { - msg += CStringUtil::print("%s (%d), ", names[i], (int)atom[i]); - XFree(names[i]); + if (error) { + for (UInt32 i = 0; i < num; ++i) { + msg += CStringUtil::print(" (%d), ", (int)atom[i]); + } + } + else { + for (UInt32 i = 0; i < num; ++i) { + msg += CStringUtil::print("%s (%d), ", names[i], (int)atom[i]); + XFree(names[i]); + } } delete[] names; if (msg.size() > 2) { @@ -1398,6 +1612,41 @@ CXWindowsUtil::atomsToString(Display* display, const Atom* atom, UInt32 num) return msg; } +void +CXWindowsUtil::convertAtomProperty(CString& data) +{ + // as best i can tell, 64-bit systems don't pack Atoms into properties + // as 32-bit numbers but rather as the 64-bit numbers they are. that + // seems wrong but we have to cope. sometimes we'll get a list of + // atoms that's 8*n+4 bytes long, missing the trailing 4 bytes which + // should all be 0. since we're going to reference the Atoms as + // 64-bit numbers we have to ensure the last number is a full 64 bits. + if (sizeof(Atom) != 4 && ((data.size() / 4) & 1) != 0) { + UInt32 zero = 0; + data.append(reinterpret_cast(&zero), sizeof(zero)); + } +} + +void +CXWindowsUtil::appendAtomData(CString& data, Atom atom) +{ + data.append(reinterpret_cast(&atom), sizeof(Atom)); +} + +void +CXWindowsUtil::replaceAtomData(CString& data, UInt32 index, Atom atom) +{ + data.replace(index * sizeof(Atom), sizeof(Atom), + reinterpret_cast(&atom), + sizeof(Atom)); +} + +void +CXWindowsUtil::appendTimeData(CString& data, Time time) +{ + data.append(reinterpret_cast(&time), sizeof(Time)); +} + Bool CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) { @@ -1412,38 +1661,9 @@ CXWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) void CXWindowsUtil::initKeyMaps() { - // note that keysyms 0x13a4 and 0x20ac both map to 0x20ac, which - // means ambiguity when converting unicode 0x20ac to a keysym. - // as written, the s_UCS4ToKeySym will map to XK_EuroSign. if (s_keySymToUCS4.empty()) { for (size_t i =0; i < sizeof(s_keymap) / sizeof(s_keymap[0]); ++i) { s_keySymToUCS4[s_keymap[i].keysym] = s_keymap[i].ucs4; - s_UCS4ToKeySym[s_keymap[i].ucs4] = s_keymap[i].keysym; - } - } - - // fill decomposed key table if not filled yet - if (s_deadKeyDecomposedKeySyms.empty()) { - for (const KeySym* scan = s_rawDeadDecomposeTable; *scan != 0; ++scan) { - // add an entry for this keysym - KeySyms& entry = s_deadKeyDecomposedKeySyms[*scan]; - - // add the decomposed keysyms for the keysym - while (*++scan != 0) { - entry.push_back(*scan); - } - } - } - if (s_composeDecomposedKeySyms.empty()) { - for (const KeySym* scan = - s_rawComposedDecomposeTable; *scan != 0; ++scan) { - // add an entry for this keysym - KeySyms& entry = s_composeDecomposedKeySyms[*scan]; - - // add the decomposed keysyms for the keysym - while (*++scan != 0) { - entry.push_back(*scan); - } } } } diff --git a/lib/platform/CXWindowsUtil.h b/lib/platform/CXWindowsUtil.h index 13e9313e..a9049ef6 100644 --- a/lib/platform/CXWindowsUtil.h +++ b/lib/platform/CXWindowsUtil.h @@ -59,37 +59,19 @@ public: */ static Time getCurrentTime(Display*, Window); - //! Convert KeySym to UCS-4 + //! Convert KeySym to KeyID /*! - Converts a KeySym to the equivalent UCS-4 character. Returns - 0x0000ffff if the KeySym cannot be mapped. + Converts a KeySym to the equivalent KeyID. Returns kKeyNone if the + KeySym cannot be mapped. */ - static UInt32 mapKeySymToUCS4(KeySym); + static UInt32 mapKeySymToKeyID(KeySym); - //! Convert UCS-4 to KeySym + //! Convert KeySym to corresponding KeyModifierMask /*! - Converts a UCS-4 character to the equivalent KeySym. Returns - NoSymbol (0) if the character cannot be mapped. + Converts a KeySym to the corresponding KeyModifierMask, or 0 if the + KeySym is not a modifier. */ - static KeySym mapUCS4ToKeySym(UInt32); - - //! Decompose a KeySym using dead keys - /*! - Decomposes \c keysym into its component keysyms. All but the last - decomposed KeySym are dead keys. Returns true iff the decomposition - was successful. - */ - static bool decomposeKeySymWithDeadKeys(KeySym keysym, - KeySyms& decomposed); - - //! Decompose a KeySym using the compose key - /*! - Decomposes \c keysym into its component keysyms. The first key is - Multi_key and the rest are normal (i.e. not dead) keys. Returns - true iff the decomposition was successful. - */ - static bool decomposeKeySymWithCompose(KeySym keysym, - KeySyms& decomposed); + static UInt32 getModifierBitForKeySym(KeySym keysym); //! Convert Atom to its string /*! @@ -105,6 +87,35 @@ public: static CString atomsToString(Display* display, const Atom* atom, UInt32 num); + //! Prepare a property of atoms for use + /*! + 64-bit systems may need to modify a property's data if it's a + list of Atoms before using it. + */ + static void convertAtomProperty(CString& data); + + //! Append an Atom to property data + /*! + Converts \p atom to a 32-bit on-the-wire format and appends it to + \p data. + */ + static void appendAtomData(CString& data, Atom atom); + + //! Replace an Atom in property data + /*! + Converts \p atom to a 32-bit on-the-wire format and replaces the atom + at index \p index in \p data. + */ + static void replaceAtomData(CString& data, + UInt32 index, Atom atom); + + //! Append an Time to property data + /*! + Converts \p time to a 32-bit on-the-wire format and appends it to + \p data. + */ + static void appendTimeData(CString& data, Time time); + //! X11 error handler /*! This class sets an X error handler in the c'tor and restores the @@ -167,13 +178,8 @@ private: private: typedef std::map CKeySymMap; - typedef std::map CUCS4Map; - typedef std::map CKeySymsMap; static CKeySymMap s_keySymToUCS4; - static CUCS4Map s_UCS4ToKeySym; - static CKeySymsMap s_deadKeyDecomposedKeySyms; - static CKeySymsMap s_composeDecomposedKeySyms; }; #endif diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 116d6566..8c9a10e0 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -78,6 +78,7 @@ CARBON_SOURCE_FILES = \ COSXKeyState.cpp \ COSXScreen.cpp \ COSXScreenSaver.cpp \ + COSXScreenSaverUtil.m \ COSXClipboard.h \ COSXClipboardAnyTextConverter.h \ COSXClipboardTextConverter.h \ @@ -86,6 +87,8 @@ CARBON_SOURCE_FILES = \ COSXKeyState.h \ COSXScreen.h \ COSXScreenSaver.h \ + COSXScreenSaverUtil.h \ + OSXScreenSaverControl.h \ $(NULL) EXTRA_DIST = \ diff --git a/lib/platform/OSXScreenSaverControl.h b/lib/platform/OSXScreenSaverControl.h new file mode 100644 index 00000000..75aecb17 --- /dev/null +++ b/lib/platform/OSXScreenSaverControl.h @@ -0,0 +1,36 @@ +// ScreenSaver.framework private API +// Class dumping by Alex Harper http://www.ragingmenace.com/ + +#import + +@protocol ScreenSaverControl +- (double)screenSaverTimeRemaining; +- (void)restartForUser:fp12; +- (void)screenSaverStopNow; +- (void)screenSaverStartNow; +- (void)setScreenSaverCanRun:(char)fp12; +- (BOOL)screenSaverCanRun; +- (BOOL)screenSaverIsRunning; +@end + + +@interface ScreenSaverController:NSObject + ++ controller; ++ monitor; ++ daemonConnectionName; ++ daemonPath; ++ enginePath; +- init; +- (void)dealloc; +- (void)_connectionClosed:fp12; +- (BOOL)screenSaverIsRunning; +- (BOOL)screenSaverCanRun; +- (void)setScreenSaverCanRun:(char)fp12; +- (void)screenSaverStartNow; +- (void)screenSaverStopNow; +- (void)restartForUser:fp12; +- (double)screenSaverTimeRemaining; + +@end + diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index caab922e..1333f52d 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\platform.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\platform.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\platform.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\platform.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp index 905c5aa6..df014508 100644 --- a/lib/platform/synrgyhk.dsp +++ b/lib/platform/synrgyhk.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GX /O1 /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /c /Fd..\..\gen\build\synrgyhk.pdb +# ADD CPP /nologo /MT /W4 /GR /GX /O1 /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /Fd"..\..\gen\build\synrgyhk.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -71,7 +71,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /FD /GZ /c /Fd..\..\gen\debug\synrgyhk.pdb +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /Fd"..\..\gen\debug\synrgyhk.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 diff --git a/lib/server/CBaseClientProxy.cpp b/lib/server/CBaseClientProxy.cpp new file mode 100644 index 00000000..0f049e35 --- /dev/null +++ b/lib/server/CBaseClientProxy.cpp @@ -0,0 +1,52 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CBaseClientProxy.h" + +// +// CBaseClientProxy +// + +CBaseClientProxy::CBaseClientProxy(const CString& name) : + m_name(name), + m_x(0), + m_y(0) +{ + // do nothing +} + +CBaseClientProxy::~CBaseClientProxy() +{ + // do nothing +} + +void +CBaseClientProxy::setJumpCursorPos(SInt32 x, SInt32 y) +{ + m_x = x; + m_y = y; +} + +void +CBaseClientProxy::getJumpCursorPos(SInt32& x, SInt32& y) const +{ + x = m_x; + y = m_y; +} + +CString +CBaseClientProxy::getName() const +{ + return m_name; +} diff --git a/lib/server/CBaseClientProxy.h b/lib/server/CBaseClientProxy.h new file mode 100644 index 00000000..e9cceca4 --- /dev/null +++ b/lib/server/CBaseClientProxy.h @@ -0,0 +1,85 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CBASECLIENTPROXY_H +#define CBASECLIENTPROXY_H + +#include "IClient.h" +#include "CString.h" + +//! Generic proxy for client or primary +class CBaseClientProxy : public IClient { +public: + /*! + \c name is the name of the client. + */ + CBaseClientProxy(const CString& name); + ~CBaseClientProxy(); + + //! @name manipulators + //@{ + + //! Save cursor position + /*! + Save the position of the cursor when jumping from client. + */ + void setJumpCursorPos(SInt32 x, SInt32 y); + + //@} + //! @name accessors + //@{ + + //! Get cursor position + /*! + Get the position of the cursor when last jumping from client. + */ + void getJumpCursorPos(SInt32& x, SInt32& y) const; + + //@} + + // IScreen + virtual void* getEventTarget() const = 0; + virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + // IClient overrides + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool forScreensaver) = 0; + virtual bool leave() = 0; + virtual void setClipboard(ClipboardID, const IClipboard*) = 0; + virtual void grabClipboard(ClipboardID) = 0; + virtual void setClipboardDirty(ClipboardID, bool) = 0; + virtual void keyDown(KeyID, KeyModifierMask, KeyButton) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, + SInt32 count, KeyButton) = 0; + virtual void keyUp(KeyID, KeyModifierMask, KeyButton) = 0; + virtual void mouseDown(ButtonID) = 0; + virtual void mouseUp(ButtonID) = 0; + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0; + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta) = 0; + virtual void screensaver(bool activate) = 0; + virtual void resetOptions() = 0; + virtual void setOptions(const COptionsList& options) = 0; + virtual CString getName() const; + +private: + CString m_name; + SInt32 m_x, m_y; +}; + +#endif diff --git a/lib/server/CClientProxy.cpp b/lib/server/CClientProxy.cpp index 99772cd9..715126d5 100644 --- a/lib/server/CClientProxy.cpp +++ b/lib/server/CClientProxy.cpp @@ -26,7 +26,7 @@ CEvent::Type CClientProxy::s_disconnectedEvent = CEvent::kUnknown; CEvent::Type CClientProxy::s_clipboardChangedEvent= CEvent::kUnknown; CClientProxy::CClientProxy(const CString& name, IStream* stream) : - m_name(name), + CBaseClientProxy(name), m_stream(stream) { // do nothing @@ -53,12 +53,6 @@ CClientProxy::getStream() const return m_stream; } -CString -CClientProxy::getName() const -{ - return m_name; -} - CEvent::Type CClientProxy::getReadyEvent() { diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index b8e38fa8..51ad014b 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -15,14 +15,14 @@ #ifndef CCLIENTPROXY_H #define CCLIENTPROXY_H -#include "IClient.h" +#include "CBaseClientProxy.h" #include "CEvent.h" #include "CString.h" class IStream; //! Generic proxy for client -class CClientProxy : public IClient { +class CClientProxy : public CBaseClientProxy { public: /*! \c name is the name of the client. @@ -97,14 +97,12 @@ public: virtual void mouseUp(ButtonID) = 0; virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0; - virtual void mouseWheel(SInt32 delta) = 0; + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta) = 0; virtual void screensaver(bool activate) = 0; virtual void resetOptions() = 0; virtual void setOptions(const COptionsList& options) = 0; - virtual CString getName() const; private: - CString m_name; IStream* m_stream; static CEvent::Type s_readyEvent; diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 3d2bd7e0..d4e06bd2 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -27,7 +27,6 @@ CClientProxy1_0::CClientProxy1_0(const CString& name, IStream* stream) : CClientProxy(name, stream), - m_heartbeatAlarm(kHeartRate * kHeartBeatsUntilDeath), m_heartbeatTimer(NULL), m_parser(&CClientProxy1_0::parseHandshakeMessage) { @@ -52,6 +51,8 @@ CClientProxy1_0::CClientProxy1_0(const CString& name, IStream* stream) : new TMethodEventJob(this, &CClientProxy1_0::handleFlatline, NULL)); + setHeartbeatRate(kHeartRate, kHeartRate * kHeartBeatsUntilDeath); + LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str())); CProtocolUtil::writef(getStream(), kMsgQInfo); } @@ -104,6 +105,26 @@ CClientProxy1_0::removeHeartbeatTimer() } } +void +CClientProxy1_0::resetHeartbeatTimer() +{ + // reset the alarm + removeHeartbeatTimer(); + addHeartbeatTimer(); +} + +void +CClientProxy1_0::resetHeartbeatRate() +{ + setHeartbeatRate(kHeartRate, kHeartRate * kHeartBeatsUntilDeath); +} + +void +CClientProxy1_0::setHeartbeatRate(double, double alarm) +{ + m_heartbeatAlarm = alarm; +} + void CClientProxy1_0::handleData(const CEvent&, void*) { @@ -121,7 +142,7 @@ CClientProxy1_0::handleData(const CEvent&, void*) // parse message LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); if (!(this->*m_parser)(code)) { - LOG((CLOG_ERR "invalid message from client \"%s\"", getName().c_str())); + LOG((CLOG_ERR "invalid message from client \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); disconnect(); return; } @@ -131,8 +152,7 @@ CClientProxy1_0::handleData(const CEvent&, void*) } // restart heartbeat timer - removeHeartbeatTimer(); - addHeartbeatTimer(); + resetHeartbeatTimer(); } bool @@ -148,6 +168,7 @@ CClientProxy1_0::parseHandshakeMessage(const UInt8* code) m_parser = &CClientProxy1_0::parseMessage; if (recvInfo()) { EVENTQUEUE->addEvent(CEvent(getReadyEvent(), getEventTarget())); + addHeartbeatTimer(); return true; } } @@ -325,10 +346,11 @@ CClientProxy1_0::mouseRelativeMove(SInt32, SInt32) } void -CClientProxy1_0::mouseWheel(SInt32 delta) +CClientProxy1_0::mouseWheel(SInt32, SInt32 yDelta) { - LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), delta)); - CProtocolUtil::writef(getStream(), kMsgDMouseWheel, delta); + // clients prior to 1.3 only support the y axis + LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), yDelta)); + CProtocolUtil::writef(getStream(), kMsgDMouseWheel1_0, yDelta); } void @@ -345,7 +367,7 @@ CClientProxy1_0::resetOptions() CProtocolUtil::writef(getStream(), kMsgCResetOptions); // reset heart rate and death - m_heartbeatAlarm = kHeartRate * kHeartBeatsUntilDeath; + resetHeartbeatRate(); removeHeartbeatTimer(); addHeartbeatTimer(); } @@ -363,7 +385,7 @@ CClientProxy1_0::setOptions(const COptionsList& options) if (rate <= 0.0) { rate = -1.0; } - m_heartbeatAlarm = rate * kHeartBeatsUntilDeath; + setHeartbeatRate(rate, rate * kHeartBeatsUntilDeath); removeHeartbeatTimer(); addHeartbeatTimer(); } diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 4f752085..9ebfc18d 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -50,7 +50,7 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); - virtual void mouseWheel(SInt32 delta); + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta); virtual void screensaver(bool activate); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); @@ -59,11 +59,15 @@ protected: virtual bool parseHandshakeMessage(const UInt8* code); virtual bool parseMessage(const UInt8* code); + virtual void resetHeartbeatRate(); + virtual void setHeartbeatRate(double rate, double alarm); + virtual void resetHeartbeatTimer(); + virtual void addHeartbeatTimer(); + virtual void removeHeartbeatTimer(); + private: void disconnect(); void removeHandlers(); - void addHeartbeatTimer(); - void removeHeartbeatTimer(); void handleData(const CEvent&, void*); void handleDisconnect(const CEvent&, void*); diff --git a/lib/server/CClientProxy1_3.cpp b/lib/server/CClientProxy1_3.cpp new file mode 100644 index 00000000..f68b550e --- /dev/null +++ b/lib/server/CClientProxy1_3.cpp @@ -0,0 +1,114 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CClientProxy1_3.h" +#include "CProtocolUtil.h" +#include "CLog.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" + +// +// CClientProxy1_3 +// + +CClientProxy1_3::CClientProxy1_3(const CString& name, IStream* stream) : + CClientProxy1_2(name, stream), + m_keepAliveRate(kKeepAliveRate), + m_keepAliveTimer(NULL) +{ + setHeartbeatRate(kKeepAliveRate, kKeepAliveRate * kKeepAlivesUntilDeath); +} + +CClientProxy1_3::~CClientProxy1_3() +{ + // cannot do this in superclass or our override wouldn't get called + removeHeartbeatTimer(); +} + +void +CClientProxy1_3::mouseWheel(SInt32 xDelta, SInt32 yDelta) +{ + LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d,%+d", getName().c_str(), xDelta, yDelta)); + CProtocolUtil::writef(getStream(), kMsgDMouseWheel, xDelta, yDelta); +} + +bool +CClientProxy1_3::parseMessage(const UInt8* code) +{ + // process message + if (memcmp(code, kMsgCKeepAlive, 4) == 0) { + // reset alarm + resetHeartbeatTimer(); + return true; + } + else { + return CClientProxy1_2::parseMessage(code); + } +} + +void +CClientProxy1_3::resetHeartbeatRate() +{ + setHeartbeatRate(kKeepAliveRate, kKeepAliveRate * kKeepAlivesUntilDeath); +} + +void +CClientProxy1_3::setHeartbeatRate(double rate, double) +{ + m_keepAliveRate = rate; + CClientProxy1_2::setHeartbeatRate(rate, rate * kKeepAlivesUntilDeath); +} + +void +CClientProxy1_3::resetHeartbeatTimer() +{ + // reset the alarm but not the keep alive timer + CClientProxy1_2::removeHeartbeatTimer(); + CClientProxy1_2::addHeartbeatTimer(); +} + +void +CClientProxy1_3::addHeartbeatTimer() +{ + // create and install a timer to periodically send keep alives + if (m_keepAliveRate > 0.0) { + m_keepAliveTimer = EVENTQUEUE->newTimer(m_keepAliveRate, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_keepAliveTimer, + new TMethodEventJob(this, + &CClientProxy1_3::handleKeepAlive, NULL)); + } + + // superclass does the alarm + CClientProxy1_2::addHeartbeatTimer(); +} + +void +CClientProxy1_3::removeHeartbeatTimer() +{ + // remove the timer that sends keep alives periodically + if (m_keepAliveTimer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_keepAliveTimer); + EVENTQUEUE->deleteTimer(m_keepAliveTimer); + m_keepAliveTimer = NULL; + } + + // superclass does the alarm + CClientProxy1_2::removeHeartbeatTimer(); +} + +void +CClientProxy1_3::handleKeepAlive(const CEvent&, void*) +{ + CProtocolUtil::writef(getStream(), kMsgCKeepAlive); +} diff --git a/lib/server/CClientProxy1_3.h b/lib/server/CClientProxy1_3.h new file mode 100644 index 00000000..681094b6 --- /dev/null +++ b/lib/server/CClientProxy1_3.h @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2006 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CCLIENTPROXY1_3_H +#define CCLIENTPROXY1_3_H + +#include "CClientProxy1_2.h" + +//! Proxy for client implementing protocol version 1.3 +class CClientProxy1_3 : public CClientProxy1_2 { +public: + CClientProxy1_3(const CString& name, IStream* adoptedStream); + ~CClientProxy1_3(); + + // IClient overrides + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta); + +protected: + // CClientProxy overrides + virtual bool parseMessage(const UInt8* code); + virtual void resetHeartbeatRate(); + virtual void setHeartbeatRate(double rate, double alarm); + virtual void resetHeartbeatTimer(); + virtual void addHeartbeatTimer(); + virtual void removeHeartbeatTimer(); + +private: + void handleKeepAlive(const CEvent&, void*); + + +private: + double m_keepAliveRate; + CEventQueueTimer* m_keepAliveTimer; +}; + +#endif diff --git a/lib/server/CClientProxyUnknown.cpp b/lib/server/CClientProxyUnknown.cpp index 806f23b8..51a25efa 100644 --- a/lib/server/CClientProxyUnknown.cpp +++ b/lib/server/CClientProxyUnknown.cpp @@ -16,6 +16,7 @@ #include "CClientProxy1_0.h" #include "CClientProxy1_1.h" #include "CClientProxy1_2.h" +#include "CClientProxy1_3.h" #include "ProtocolTypes.h" #include "CProtocolUtil.h" #include "XSynergy.h" @@ -219,6 +220,10 @@ CClientProxyUnknown::handleData(const CEvent&, void*) case 2: m_proxy = new CClientProxy1_2(name, m_stream); break; + + case 3: + m_proxy = new CClientProxy1_3(name, m_stream); + break; } } diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 360379fb..a502fe78 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -13,6 +13,8 @@ */ #include "CConfig.h" +#include "CServer.h" +#include "CKeyMap.h" #include "KeyTypes.h" #include "XSocket.h" #include "stdistream.h" @@ -23,7 +25,7 @@ // CConfig // -CConfig::CConfig() +CConfig::CConfig() : m_hasLockToScreenAction(false) { // do nothing } @@ -78,13 +80,9 @@ CConfig::renameScreen(const CString& oldName, m_nameToCanonicalName.insert(std::make_pair(newName, newName)); // update connections + CName oldNameObj(this, oldName); for (index = m_map.begin(); index != m_map.end(); ++index) { - for (UInt32 i = 0; i < kNumDirections; ++i) { - if (CStringUtil::CaselessCmp::equal(getCanonicalName( - index->second.m_neighbor[i]), oldCanonical)) { - index->second.m_neighbor[i] = newName; - } - } + index->second.rename(oldNameObj, newName); } // update alias targets @@ -115,13 +113,9 @@ CConfig::removeScreen(const CString& name) m_map.erase(index); // disconnect + CName nameObj(this, name); for (index = m_map.begin(); index != m_map.end(); ++index) { - CCell& cell = index->second; - for (UInt32 i = 0; i < kNumDirections; ++i) { - if (getCanonicalName(cell.m_neighbor[i]) == canonical) { - cell.m_neighbor[i].erase(); - } - } + index->second.remove(nameObj); } // remove aliases (and canonical name) @@ -182,6 +176,28 @@ CConfig::removeAlias(const CString& alias) return true; } +bool +CConfig::removeAliases(const CString& canonical) +{ + // must be a canonical name + if (m_map.find(canonical) == m_map.end()) { + return false; + } + + // find and removing matching aliases + for (CNameMap::iterator index = m_nameToCanonicalName.begin(); + index != m_nameToCanonicalName.end(); ) { + if (index->second == canonical && index->first != canonical) { + m_nameToCanonicalName.erase(index++); + } + else { + ++index; + } + } + + return true; +} + void CConfig::removeAllAliases() { @@ -198,7 +214,10 @@ CConfig::removeAllAliases() bool CConfig::connect(const CString& srcName, - EDirection srcSide, const CString& dstName) + EDirection srcSide, + float srcStart, float srcEnd, + const CString& dstName, + float dstStart, float dstEnd) { assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); @@ -208,12 +227,10 @@ CConfig::connect(const CString& srcName, return false; } - // connect side (overriding any previous connection). we - // canonicalize in getNeighbor() instead of here because the - // destination name doesn't have to exist yet. - index->second.m_neighbor[srcSide - kFirstDirection] = dstName; - - return true; + // add link + CCellEdge srcEdge(srcSide, CInterval(srcStart, srcEnd)); + CCellEdge dstEdge(dstName, srcSide, CInterval(dstStart, dstEnd)); + return index->second.add(srcEdge, dstEdge); } bool @@ -228,7 +245,24 @@ CConfig::disconnect(const CString& srcName, EDirection srcSide) } // disconnect side - index->second.m_neighbor[srcSide - kFirstDirection].erase(); + index->second.remove(srcSide); + + return true; +} + +bool +CConfig::disconnect(const CString& srcName, EDirection srcSide, float position) +{ + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // find source cell + CCellMap::iterator index = m_map.find(srcName); + if (index == m_map.end()) { + return false; + } + + // disconnect side + index->second.remove(srcSide, position); return true; } @@ -399,7 +433,8 @@ CConfig::isScreen(const CString& name) const bool CConfig::isCanonicalName(const CString& name) const { - return CStringUtil::CaselessCmp::equal(getCanonicalName(name), name); + return (!name.empty() && + CStringUtil::CaselessCmp::equal(getCanonicalName(name), name)); } CString @@ -415,7 +450,8 @@ CConfig::getCanonicalName(const CString& name) const } CString -CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const +CConfig::getNeighbor(const CString& srcName, EDirection srcSide, + float position, float* positionOut) const { assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); @@ -425,9 +461,59 @@ CConfig::getNeighbor(const CString& srcName, EDirection srcSide) const return CString(); } - // return connection - return getCanonicalName(index->second.m_neighbor[ - srcSide - kFirstDirection]); + // find edge + const CCellEdge* srcEdge, *dstEdge; + if (!index->second.getLink(srcSide, position, srcEdge, dstEdge)) { + // no neighbor + return ""; + } + else { + // compute position on neighbor + if (positionOut != NULL) { + *positionOut = + dstEdge->inverseTransform(srcEdge->transform(position)); + } + + // return neighbor's name + return getCanonicalName(dstEdge->getName()); + } +} + +bool +CConfig::hasNeighbor(const CString& srcName, EDirection srcSide) const +{ + return hasNeighbor(srcName, srcSide, 0.0f, 1.0f); +} + +bool +CConfig::hasNeighbor(const CString& srcName, EDirection srcSide, + float start, float end) const +{ + assert(srcSide >= kFirstDirection && srcSide <= kLastDirection); + + // find source cell + CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); + if (index == m_map.end()) { + return false; + } + + return index->second.overlaps(CCellEdge(srcSide, CInterval(start, end))); +} + +CConfig::link_const_iterator +CConfig::beginNeighbor(const CString& srcName) const +{ + CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); + assert(index != m_map.end()); + return index->second.begin(); +} + +CConfig::link_const_iterator +CConfig::endNeighbor(const CString& srcName) const +{ + CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName)); + assert(index != m_map.end()); + return index->second.end(); } const CNetworkAddress& @@ -455,6 +541,12 @@ CConfig::getOptions(const CString& name) const return options; } +bool +CConfig::hasLockToScreenAction() const +{ + return m_hasLockToScreenAction; +} + bool CConfig::operator==(const CConfig& x) const { @@ -481,18 +573,10 @@ CConfig::operator==(const CConfig& x) const return false; } - // compare options - if (index1->second.m_options != index2->second.m_options) { + // compare cells + if (index1->second != index2->second) { return false; } - - // compare neighbors - for (UInt32 i = 0; i < kNumDirections; ++i) { - if (!CStringUtil::CaselessCmp::equal(index1->second.m_neighbor[i], - index2->second.m_neighbor[i])) { - return false; - } - } } for (CNameMap::const_iterator index1 = m_nameToCanonicalName.begin(), @@ -505,6 +589,11 @@ CConfig::operator==(const CConfig& x) const } } + // compare input filters + if (m_inputFilter != x.m_inputFilter) { + return false; + } + return true; } @@ -514,92 +603,649 @@ CConfig::operator!=(const CConfig& x) const return !operator==(x); } +void +CConfig::read(CConfigReadContext& context) +{ + CConfig tmp; + while (context) { + tmp.readSection(context); + } + *this = tmp; +} + const char* CConfig::dirName(EDirection dir) { - static const char* s_name[] = { "left", "right", "top", "bottom" }; + static const char* s_name[] = { "left", "right", "up", "down" }; assert(dir >= kFirstDirection && dir <= kLastDirection); return s_name[dir - kFirstDirection]; } -bool -CConfig::readLine(std::istream& s, CString& line) +CInputFilter* +CConfig::getInputFilter() { - s >> std::ws; - while (std::getline(s, line)) { - // strip comments and then trailing whitespace - CString::size_type i = line.find('#'); - if (i != CString::npos) { - line.erase(i); - } - i = line.find_last_not_of(" \r\t"); - if (i != CString::npos) { - line.erase(i + 1); - } - - // return non empty line - if (!line.empty()) { - return true; - } - s >> std::ws; - } - return false; + return &m_inputFilter; } -OptionValue -CConfig::parseBoolean(const CString& arg) +CString +CConfig::formatInterval(const CInterval& x) { - if (CStringUtil::CaselessCmp::equal(arg, "true")) { - return static_cast(true); + if (x.first == 0.0f && x.second == 1.0f) { + return ""; } - if (CStringUtil::CaselessCmp::equal(arg, "false")) { - return static_cast(false); - } - throw XConfigRead("invalid argument"); + return CStringUtil::print("(%d,%d)", (int)(x.first * 100.0f + 0.5f), + (int)(x.second * 100.0f + 0.5f)); } -OptionValue -CConfig::parseInt(const CString& arg) +void +CConfig::readSection(CConfigReadContext& s) { - const char* s = arg.c_str(); - char* end; - long tmp = strtol(s, &end, 10); - if (*end != '\0') { - // invalid characters - throw XConfigRead("invalid argument"); + static const char s_section[] = "section:"; + static const char s_options[] = "options"; + static const char s_screens[] = "screens"; + static const char s_links[] = "links"; + static const char s_aliases[] = "aliases"; + + CString line; + if (!s.readLine(line)) { + // no more sections + return; } - OptionValue value = static_cast(tmp); - if (value != tmp) { - // out of range - throw XConfigRead("argument out of range"); + + // should be a section header + if (line.find(s_section) != 0) { + throw XConfigRead(s, "found data outside section"); + } + + // get section name + CString::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1); + if (i == CString::npos) { + throw XConfigRead(s, "section name is missing"); + } + CString name = line.substr(i); + i = name.find_first_of(" \t"); + if (i != CString::npos) { + throw XConfigRead(s, "unexpected data after section name"); + } + + // read section + if (name == s_options) { + readSectionOptions(s); + } + else if (name == s_screens) { + readSectionScreens(s); + } + else if (name == s_links) { + readSectionLinks(s); + } + else if (name == s_aliases) { + readSectionAliases(s); + } + else { + throw XConfigRead(s, "unknown section name \"%{1}\"", name); } - return value; } -OptionValue -CConfig::parseModifierKey(const CString& arg) +void +CConfig::readSectionOptions(CConfigReadContext& s) { - if (CStringUtil::CaselessCmp::equal(arg, "shift")) { - return static_cast(kKeyModifierIDShift); + CString line; + while (s.readLine(line)) { + // check for end of section + if (line == "end") { + return; + } + + // parse argument: `nameAndArgs = [values][;[values]]' + // nameAndArgs := [(arg[,...])] + // values := valueAndArgs[,valueAndArgs]... + // valueAndArgs := [(arg[,...])] + CString::size_type i = 0; + CString name, value; + CConfigReadContext::ArgList nameArgs, valueArgs; + s.parseNameWithArgs("name", line, "=", i, name, nameArgs); + ++i; + s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs); + + bool handled = true; + if (name == "address") { + try { + m_synergyAddress = CNetworkAddress(value, kDefaultPort); + m_synergyAddress.resolve(); + } + catch (XSocketAddress& e) { + throw XConfigRead(s, + CString("invalid address argument ") + e.what()); + } + } + else if (name == "heartbeat") { + addOption("", kOptionHeartbeat, s.parseInt(value)); + } + else if (name == "switchCorners") { + addOption("", kOptionScreenSwitchCorners, s.parseCorners(value)); + } + else if (name == "switchCornerSize") { + addOption("", kOptionScreenSwitchCornerSize, s.parseInt(value)); + } + else if (name == "switchDelay") { + addOption("", kOptionScreenSwitchDelay, s.parseInt(value)); + } + else if (name == "switchDoubleTap") { + addOption("", kOptionScreenSwitchTwoTap, s.parseInt(value)); + } + else if (name == "screenSaverSync") { + addOption("", kOptionScreenSaverSync, s.parseBoolean(value)); + } + else if (name == "relativeMouseMoves") { + addOption("", kOptionRelativeMouseMoves, s.parseBoolean(value)); + } + else if (name == "win32KeepForeground") { + addOption("", kOptionWin32KeepForeground, s.parseBoolean(value)); + } + else { + handled = false; + } + + if (handled) { + // make sure handled options aren't followed by more values + if (i < line.size() && (line[i] == ',' || line[i] == ';')) { + throw XConfigRead(s, "to many arguments to %s", name.c_str()); + } + } + else { + // make filter rule + CInputFilter::CRule rule(parseCondition(s, name, nameArgs)); + + // save first action (if any) + if (!value.empty() || line[i] != ';') { + parseAction(s, value, valueArgs, rule, true); + } + + // get remaining activate actions + while (i < line.length() && line[i] != ';') { + ++i; + s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs); + parseAction(s, value, valueArgs, rule, true); + } + + // get deactivate actions + if (i < line.length() && line[i] == ';') { + // allow trailing ';' + i = line.find_first_not_of(" \t", i + 1); + if (i == CString::npos) { + i = line.length(); + } + else { + --i; + } + + // get actions + while (i < line.length()) { + ++i; + s.parseNameWithArgs("value", line, ",\n", + i, value, valueArgs); + parseAction(s, value, valueArgs, rule, false); + } + } + + // add rule + m_inputFilter.addFilterRule(rule); + } } - if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) { - return static_cast(kKeyModifierIDControl); + throw XConfigRead(s, "unexpected end of options section"); +} + +void +CConfig::readSectionScreens(CConfigReadContext& s) +{ + CString line; + CString screen; + while (s.readLine(line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify validity of screen name + if (!isValidScreenName(screen)) { + throw XConfigRead(s, "invalid screen name \"%{1}\"", screen); + } + + // add the screen to the configuration + if (!addScreen(screen)) { + throw XConfigRead(s, "duplicate screen name \"%{1}\"", screen); + } + } + else if (screen.empty()) { + throw XConfigRead(s, "argument before first screen"); + } + else { + // parse argument: `=' + CString::size_type i = line.find_first_of(" \t="); + if (i == 0) { + throw XConfigRead(s, "missing argument name"); + } + if (i == CString::npos) { + throw XConfigRead(s, "missing ="); + } + CString name = line.substr(0, i); + i = line.find_first_not_of(" \t", i); + if (i == CString::npos || line[i] != '=') { + throw XConfigRead(s, "missing ="); + } + i = line.find_first_not_of(" \t", i + 1); + CString value; + if (i != CString::npos) { + value = line.substr(i); + } + + // handle argument + if (name == "halfDuplexCapsLock") { + addOption(screen, kOptionHalfDuplexCapsLock, + s.parseBoolean(value)); + } + else if (name == "halfDuplexNumLock") { + addOption(screen, kOptionHalfDuplexNumLock, + s.parseBoolean(value)); + } + else if (name == "halfDuplexScrollLock") { + addOption(screen, kOptionHalfDuplexScrollLock, + s.parseBoolean(value)); + } + else if (name == "shift") { + addOption(screen, kOptionModifierMapForShift, + s.parseModifierKey(value)); + } + else if (name == "ctrl") { + addOption(screen, kOptionModifierMapForControl, + s.parseModifierKey(value)); + } + else if (name == "alt") { + addOption(screen, kOptionModifierMapForAlt, + s.parseModifierKey(value)); + } + else if (name == "meta") { + addOption(screen, kOptionModifierMapForMeta, + s.parseModifierKey(value)); + } + else if (name == "super") { + addOption(screen, kOptionModifierMapForSuper, + s.parseModifierKey(value)); + } + else if (name == "xtestIsXineramaUnaware") { + addOption(screen, kOptionXTestXineramaUnaware, + s.parseBoolean(value)); + } + else if (name == "switchCorners") { + addOption(screen, kOptionScreenSwitchCorners, + s.parseCorners(value)); + } + else if (name == "switchCornerSize") { + addOption(screen, kOptionScreenSwitchCornerSize, + s.parseInt(value)); + } + else { + // unknown argument + throw XConfigRead(s, "unknown argument \"%{1}\"", name); + } + } } - if (CStringUtil::CaselessCmp::equal(arg, "alt")) { - return static_cast(kKeyModifierIDAlt); + throw XConfigRead(s, "unexpected end of screens section"); +} + +void +CConfig::readSectionLinks(CConfigReadContext& s) +{ + CString line; + CString screen; + while (s.readLine(line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify we know about the screen + if (!isScreen(screen)) { + throw XConfigRead(s, "unknown screen name \"%{1}\"", screen); + } + if (!isCanonicalName(screen)) { + throw XConfigRead(s, "cannot use screen name alias here"); + } + } + else if (screen.empty()) { + throw XConfigRead(s, "argument before first screen"); + } + else { + // parse argument: `[(,)]=[(,)]' + // the stuff in brackets is optional. interval values must be + // in the range [0,100] and start < end. if not given the + // interval is taken to be (0,100). + CString::size_type i = 0; + CString side, dstScreen, srcArgString, dstArgString; + CConfigReadContext::ArgList srcArgs, dstArgs; + s.parseNameWithArgs("link", line, "=", i, side, srcArgs); + ++i; + s.parseNameWithArgs("screen", line, "", i, dstScreen, dstArgs); + CInterval srcInterval(s.parseInterval(srcArgs)); + CInterval dstInterval(s.parseInterval(dstArgs)); + + // handle argument + EDirection dir; + if (side == "left") { + dir = kLeft; + } + else if (side == "right") { + dir = kRight; + } + else if (side == "up") { + dir = kTop; + } + else if (side == "down") { + dir = kBottom; + } + else { + // unknown argument + throw XConfigRead(s, "unknown side \"%{1}\" in link", side); + } + if (!isScreen(dstScreen)) { + throw XConfigRead(s, "unknown screen name \"%{1}\"", dstScreen); + } + if (!connect(screen, dir, + srcInterval.first, srcInterval.second, + dstScreen, + dstInterval.first, dstInterval.second)) { + throw XConfigRead(s, "overlapping range"); + } + } } - if (CStringUtil::CaselessCmp::equal(arg, "meta")) { - return static_cast(kKeyModifierIDMeta); + throw XConfigRead(s, "unexpected end of links section"); +} + +void +CConfig::readSectionAliases(CConfigReadContext& s) +{ + CString line; + CString screen; + while (s.readLine(line)) { + // check for end of section + if (line == "end") { + return; + } + + // see if it's the next screen + if (line[line.size() - 1] == ':') { + // strip : + screen = line.substr(0, line.size() - 1); + + // verify we know about the screen + if (!isScreen(screen)) { + throw XConfigRead(s, "unknown screen name \"%{1}\"", screen); + } + if (!isCanonicalName(screen)) { + throw XConfigRead(s, "cannot use screen name alias here"); + } + } + else if (screen.empty()) { + throw XConfigRead(s, "argument before first screen"); + } + else { + // verify validity of screen name + if (!isValidScreenName(line)) { + throw XConfigRead(s, "invalid screen alias \"%{1}\"", line); + } + + // add alias + if (!addAlias(screen, line)) { + throw XConfigRead(s, "alias \"%{1}\" is already used", line); + } + } } - if (CStringUtil::CaselessCmp::equal(arg, "super")) { - return static_cast(kKeyModifierIDSuper); + throw XConfigRead(s, "unexpected end of aliases section"); +} + + +CInputFilter::CCondition* +CConfig::parseCondition(CConfigReadContext& s, + const CString& name, const std::vector& args) +{ + if (name == "keystroke") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for condition: keystroke(modifiers+key)"); + } + + IPlatformScreen::CKeyInfo* keyInfo = s.parseKeystroke(args[0]); + + return new CInputFilter::CKeystrokeCondition(keyInfo); } - if (CStringUtil::CaselessCmp::equal(arg, "none")) { - return static_cast(kKeyModifierIDNull); + + if (name == "mousebutton") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for condition: mousebutton(modifiers+button)"); + } + + IPlatformScreen::CButtonInfo* mouseInfo = s.parseMouse(args[0]); + + return new CInputFilter::CMouseButtonCondition(mouseInfo); + } + + if (name == "connect") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for condition: connect([screen])"); + } + + CString screen = args[0]; + if (isScreen(screen)) { + screen = getCanonicalName(screen); + } + else if (!screen.empty()) { + throw XConfigRead(s, "unknown screen name \"%{1}\" in connect", screen); + } + + return new CInputFilter::CScreenConnectedCondition(screen); + } + + throw XConfigRead(s, "unknown argument \"%{1}\"", name); +} + +void +CConfig::parseAction(CConfigReadContext& s, + const CString& name, const std::vector& args, + CInputFilter::CRule& rule, bool activate) +{ + CInputFilter::CAction* action; + + if (name == "keystroke" || name == "keyDown" || name == "keyUp") { + if (args.size() < 1 || args.size() > 2) { + throw XConfigRead(s, "syntax for action: keystroke(modifiers+key[,screens])"); + } + + IPlatformScreen::CKeyInfo* keyInfo; + if (args.size() == 1) { + keyInfo = s.parseKeystroke(args[0]); + } + else { + std::set screens; + parseScreens(s, args[1], screens); + keyInfo = s.parseKeystroke(args[0], screens); + } + + if (name == "keystroke") { + IPlatformScreen::CKeyInfo* keyInfo2 = + IKeyState::CKeyInfo::alloc(*keyInfo); + action = new CInputFilter::CKeystrokeAction(keyInfo2, true); + rule.adoptAction(action, true); + action = new CInputFilter::CKeystrokeAction(keyInfo, false); + activate = false; + } + else if (name == "keyDown") { + action = new CInputFilter::CKeystrokeAction(keyInfo, true); + } + else { + action = new CInputFilter::CKeystrokeAction(keyInfo, false); + } + } + + else if (name == "mousebutton" || + name == "mouseDown" || name == "mouseUp") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for action: mousebutton(modifiers+button)"); + } + + IPlatformScreen::CButtonInfo* mouseInfo = s.parseMouse(args[0]); + + if (name == "mousebutton") { + IPlatformScreen::CButtonInfo* mouseInfo2 = + IPlatformScreen::CButtonInfo::alloc(*mouseInfo); + action = new CInputFilter::CMouseButtonAction(mouseInfo2, true); + rule.adoptAction(action, true); + action = new CInputFilter::CMouseButtonAction(mouseInfo, false); + activate = false; + } + else if (name == "mouseDown") { + action = new CInputFilter::CMouseButtonAction(mouseInfo, true); + } + else { + action = new CInputFilter::CMouseButtonAction(mouseInfo, false); + } + } + +/* XXX -- not supported + else if (name == "modifier") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for action: modifier(modifiers)"); + } + + KeyModifierMask mask = s.parseModifier(args[0]); + + action = new CInputFilter::CModifierAction(mask, ~mask); + } +*/ + + else if (name == "switchToScreen") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for action: switchToScreen(name)"); + } + + CString screen = args[0]; + if (isScreen(screen)) { + screen = getCanonicalName(screen); + } + else if (!screen.empty()) { + throw XConfigRead(s, "unknown screen name in switchToScreen"); + } + + action = new CInputFilter::CSwitchToScreenAction(screen); + } + + else if (name == "switchInDirection") { + if (args.size() != 1) { + throw XConfigRead(s, "syntax for action: switchInDirection()"); + } + + EDirection direction; + if (args[0] == "left") { + direction = kLeft; + } + else if (args[0] == "right") { + direction = kRight; + } + else if (args[0] == "up") { + direction = kTop; + } + else if (args[0] == "down") { + direction = kBottom; + } + else { + throw XConfigRead(s, "unknown direction \"%{1}\" in switchToScreen", args[0]); + } + + action = new CInputFilter::CSwitchInDirectionAction(direction); + } + + else if (name == "lockCursorToScreen") { + if (args.size() > 1) { + throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])"); + } + + CInputFilter::CLockCursorToScreenAction::Mode mode = + CInputFilter::CLockCursorToScreenAction::kToggle; + if (args.size() == 1) { + if (args[0] == "off") { + mode = CInputFilter::CLockCursorToScreenAction::kOff; + } + else if (args[0] == "on") { + mode = CInputFilter::CLockCursorToScreenAction::kOn; + } + else if (args[0] == "toggle") { + mode = CInputFilter::CLockCursorToScreenAction::kToggle; + } + else { + throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])"); + } + } + + if (mode != CInputFilter::CLockCursorToScreenAction::kOff) { + m_hasLockToScreenAction = true; + } + + action = new CInputFilter::CLockCursorToScreenAction(mode); + } + + else { + throw XConfigRead(s, "unknown action argument \"%{1}\"", name); + } + + rule.adoptAction(action, activate); +} + +void +CConfig::parseScreens(CConfigReadContext& c, + const CString& s, std::set& screens) const +{ + screens.clear(); + + CString::size_type i = 0; + while (i < s.size()) { + // find end of next screen name + CString::size_type j = s.find(':', i); + if (j == CString::npos) { + j = s.size(); + } + + // extract name + CString rawName; + i = s.find_first_not_of(" \t", i); + if (i < j) { + rawName = s.substr(i, s.find_last_not_of(" \t", j - 1) - i + 1); + } + + // add name + if (rawName == "*") { + screens.insert("*"); + } + else if (!rawName.empty()) { + CString name = getCanonicalName(rawName); + if (name.empty()) { + throw XConfigRead(c, "unknown screen name \"%{1}\"", rawName); + } + screens.insert(name); + } + + // next + i = j + 1; } - throw XConfigRead("invalid argument"); } const char* @@ -632,6 +1278,12 @@ CConfig::getOptionName(OptionID id) if (id == kOptionHeartbeat) { return "heartbeat"; } + if (id == kOptionScreenSwitchCorners) { + return "switchCorners"; + } + if (id == kOptionScreenSwitchCornerSize) { + return "switchCornerSize"; + } if (id == kOptionScreenSwitchDelay) { return "switchDelay"; } @@ -647,6 +1299,9 @@ CConfig::getOptionName(OptionID id) if (id == kOptionRelativeMouseMoves) { return "relativeMouseMoves"; } + if (id == kOptionWin32KeepForeground) { + return "win32KeepForeground"; + } return NULL; } @@ -658,7 +1313,8 @@ CConfig::getOptionValue(OptionID id, OptionValue value) id == kOptionHalfDuplexScrollLock || id == kOptionScreenSaverSync || id == kOptionXTestXineramaUnaware || - id == kOptionRelativeMouseMoves) { + id == kOptionRelativeMouseMoves || + id == kOptionWin32KeepForeground) { return (value != 0) ? "true" : "false"; } if (id == kOptionModifierMapForShift || @@ -687,344 +1343,333 @@ CConfig::getOptionValue(OptionID id, OptionValue value) } } if (id == kOptionHeartbeat || + id == kOptionScreenSwitchCornerSize || id == kOptionScreenSwitchDelay || id == kOptionScreenSwitchTwoTap) { return CStringUtil::print("%d", value); } + if (id == kOptionScreenSwitchCorners) { + std::string result("none"); + if ((value & kTopLeftMask) != 0) { + result += " +top-left"; + } + if ((value & kTopRightMask) != 0) { + result += " +top-right"; + } + if ((value & kBottomLeftMask) != 0) { + result += " +bottom-left"; + } + if ((value & kBottomRightMask) != 0) { + result += " +bottom-right"; + } + return result; + } return ""; } -void -CConfig::readSection(std::istream& s) + +// +// CConfig::CName +// + +CConfig::CName::CName(CConfig* config, const CString& name) : + m_config(config), + m_name(config->getCanonicalName(name)) { - static const char s_section[] = "section:"; - static const char s_options[] = "options"; - static const char s_screens[] = "screens"; - static const char s_links[] = "links"; - static const char s_aliases[] = "aliases"; + // do nothing +} - CString line; - if (!readLine(s, line)) { - // no more sections - return; +bool +CConfig::CName::operator==(const CString& name) const +{ + CString canonical = m_config->getCanonicalName(name); + return CStringUtil::CaselessCmp::equal(canonical, m_name); +} + + +// +// CConfig::CCellEdge +// + +CConfig::CCellEdge::CCellEdge(EDirection side, float position) +{ + init("", side, CInterval(position, position)); +} + +CConfig::CCellEdge::CCellEdge(EDirection side, const CInterval& interval) +{ + assert(interval.first >= 0.0f); + assert(interval.second <= 1.0f); + assert(interval.first < interval.second); + + init("", side, interval); +} + +CConfig::CCellEdge::CCellEdge(const CString& name, + EDirection side, const CInterval& interval) +{ + assert(interval.first >= 0.0f); + assert(interval.second <= 1.0f); + assert(interval.first < interval.second); + + init(name, side, interval); +} + +CConfig::CCellEdge::~CCellEdge() +{ + // do nothing +} + +void +CConfig::CCellEdge::init(const CString& name, EDirection side, + const CInterval& interval) +{ + assert(side != kNoDirection); + + m_name = name; + m_side = side; + m_interval = interval; +} + +CConfig::CInterval +CConfig::CCellEdge::getInterval() const +{ + return m_interval; +} + +void +CConfig::CCellEdge::setName(const CString& newName) +{ + m_name = newName; +} + +CString +CConfig::CCellEdge::getName() const +{ + return m_name; +} + +EDirection +CConfig::CCellEdge::getSide() const +{ + return m_side; +} + +bool +CConfig::CCellEdge::overlaps(const CCellEdge& edge) const +{ + const CInterval& x = m_interval; + const CInterval& y = edge.m_interval; + if (m_side != edge.m_side) { + return false; + } + return (x.first >= y.first && x.first < y.second) || + (x.second > y.first && x.second <= y.second) || + (y.first >= x.first && y.first < x.second) || + (y.second > x.first && y.second <= x.second); +} + +bool +CConfig::CCellEdge::isInside(float x) const +{ + return (x >= m_interval.first && x < m_interval.second); +} + +float +CConfig::CCellEdge::transform(float x) const +{ + return (x - m_interval.first) / (m_interval.second - m_interval.first); +} + + +float +CConfig::CCellEdge::inverseTransform(float x) const +{ + return x * (m_interval.second - m_interval.first) + m_interval.first; +} + +bool +CConfig::CCellEdge::operator<(const CCellEdge& o) const +{ + if (static_cast(m_side) < static_cast(o.m_side)) { + return true; + } + else if (static_cast(m_side) > static_cast(o.m_side)) { + return false; } - // should be a section header - if (line.find(s_section) != 0) { - throw XConfigRead("found data outside section"); + return (m_interval.first < o.m_interval.first); +} + +bool +CConfig::CCellEdge::operator==(const CCellEdge& x) const +{ + return (m_side == x.m_side && m_interval == x.m_interval); +} + +bool +CConfig::CCellEdge::operator!=(const CCellEdge& x) const +{ + return !operator==(x); +} + + +// +// CConfig::CCell +// + +bool +CConfig::CCell::add(const CCellEdge& src, const CCellEdge& dst) +{ + // cannot add an edge that overlaps other existing edges but we + // can exactly replace an edge. + if (!hasEdge(src) && overlaps(src)) { + return false; } - // get section name - CString::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1); - if (i == CString::npos) { - throw XConfigRead("section name is missing"); - } - CString name = line.substr(i); - i = name.find_first_of(" \t"); - if (i != CString::npos) { - throw XConfigRead("unexpected data after section name"); - } + m_neighbors.erase(src); + m_neighbors.insert(std::make_pair(src, dst)); + return true; +} - // read section - if (name == s_options) { - readSectionOptions(s); - } - else if (name == s_screens) { - readSectionScreens(s); - } - else if (name == s_links) { - readSectionLinks(s); - } - else if (name == s_aliases) { - readSectionAliases(s); - } - else { - throw XConfigRead("unknown section name"); +void +CConfig::CCell::remove(EDirection side) +{ + for (CEdgeLinks::iterator j = m_neighbors.begin(); + j != m_neighbors.end(); ) { + if (j->first.getSide() == side) { + m_neighbors.erase(j++); + } + else { + ++j; + } } } void -CConfig::readSectionOptions(std::istream& s) +CConfig::CCell::remove(EDirection side, float position) { - CString line; - CString name; - while (readLine(s, line)) { - // check for end of section - if (line == "end") { - return; - } - - // parse argument: `=' - CString::size_type i = line.find_first_of(" \t="); - if (i == 0) { - throw XConfigRead("missing argument name"); - } - if (i == CString::npos) { - throw XConfigRead("missing = in argument"); - } - CString name = line.substr(0, i); - i = line.find_first_not_of(" \t", i); - if (i == CString::npos || line[i] != '=') { - throw XConfigRead("missing = in argument"); - } - i = line.find_first_not_of(" \t", i + 1); - CString value; - if (i != CString::npos) { - value = line.substr(i); - } - if (value.empty()) { - throw XConfigRead("missing value after ="); - } - - if (name == "address") { - try { - m_synergyAddress = CNetworkAddress(value, kDefaultPort); - m_synergyAddress.resolve(); - } - catch (XSocketAddress& e) { - throw XConfigRead(CString("invalid address argument: ") + - e.what()); - } - } - else if (name == "heartbeat") { - addOption("", kOptionHeartbeat, parseInt(value)); - } - else if (name == "switchDelay") { - addOption("", kOptionScreenSwitchDelay, parseInt(value)); - } - else if (name == "switchDoubleTap") { - addOption("", kOptionScreenSwitchTwoTap, parseInt(value)); - } - else if (name == "screenSaverSync") { - addOption("", kOptionScreenSaverSync, parseBoolean(value)); - } - else if (name == "relativeMouseMoves") { - addOption("", kOptionRelativeMouseMoves, parseBoolean(value)); - } - else { - throw XConfigRead("unknown argument"); + for (CEdgeLinks::iterator j = m_neighbors.begin(); + j != m_neighbors.end(); ++j) { + if (j->first.getSide() == side && j->first.isInside(position)) { + m_neighbors.erase(j); + break; + } + } +} +void +CConfig::CCell::remove(const CName& name) +{ + for (CEdgeLinks::iterator j = m_neighbors.begin(); + j != m_neighbors.end(); ) { + if (name == j->second.getName()) { + m_neighbors.erase(j++); + } + else { + ++j; } } - throw XConfigRead("unexpected end of screens section"); } void -CConfig::readSectionScreens(std::istream& s) +CConfig::CCell::rename(const CName& oldName, const CString& newName) { - CString line; - CString screen; - while (readLine(s, line)) { - // check for end of section - if (line == "end") { - return; - } - - // see if it's the next screen - if (line[line.size() - 1] == ':') { - // strip : - screen = line.substr(0, line.size() - 1); - - // verify validity of screen name - if (!isValidScreenName(screen)) { - throw XConfigRead("invalid screen name"); - } - - // add the screen to the configuration - if (!addScreen(screen)) { - throw XConfigRead("duplicate screen name"); - } - } - else if (screen.empty()) { - throw XConfigRead("argument before first screen"); - } - else { - // parse argument: `=' - CString::size_type i = line.find_first_of(" \t="); - if (i == 0) { - throw XConfigRead("missing argument name"); - } - if (i == CString::npos) { - throw XConfigRead("missing = in argument"); - } - CString name = line.substr(0, i); - i = line.find_first_not_of(" \t", i); - if (i == CString::npos || line[i] != '=') { - throw XConfigRead("missing = in argument"); - } - i = line.find_first_not_of(" \t", i + 1); - CString value; - if (i != CString::npos) { - value = line.substr(i); - } - - // handle argument - if (name == "halfDuplexCapsLock") { - addOption(screen, kOptionHalfDuplexCapsLock, - parseBoolean(value)); - } - else if (name == "halfDuplexNumLock") { - addOption(screen, kOptionHalfDuplexNumLock, - parseBoolean(value)); - } - else if (name == "halfDuplexScrollLock") { - addOption(screen, kOptionHalfDuplexScrollLock, - parseBoolean(value)); - } - else if (name == "shift") { - addOption(screen, kOptionModifierMapForShift, - parseModifierKey(value)); - } - else if (name == "ctrl") { - addOption(screen, kOptionModifierMapForControl, - parseModifierKey(value)); - } - else if (name == "alt") { - addOption(screen, kOptionModifierMapForAlt, - parseModifierKey(value)); - } - else if (name == "meta") { - addOption(screen, kOptionModifierMapForMeta, - parseModifierKey(value)); - } - else if (name == "super") { - addOption(screen, kOptionModifierMapForSuper, - parseModifierKey(value)); - } - else if (name == "xtestIsXineramaUnaware") { - addOption(screen, kOptionXTestXineramaUnaware, - parseBoolean(value)); - } - else { - // unknown argument - throw XConfigRead("unknown argument"); - } + for (CEdgeLinks::iterator j = m_neighbors.begin(); + j != m_neighbors.end(); ++j) { + if (oldName == j->second.getName()) { + j->second.setName(newName); } } - throw XConfigRead("unexpected end of screens section"); } -void -CConfig::readSectionLinks(std::istream& s) +bool +CConfig::CCell::hasEdge(const CCellEdge& edge) const { - CString line; - CString screen; - while (readLine(s, line)) { - // check for end of section - if (line == "end") { - return; - } - - // see if it's the next screen - if (line[line.size() - 1] == ':') { - // strip : - screen = line.substr(0, line.size() - 1); - - // verify we know about the screen - if (!isScreen(screen)) { - throw XConfigRead("unknown screen name"); - } - if (!isCanonicalName(screen)) { - throw XConfigRead("cannot use screen name alias here"); - } - } - else if (screen.empty()) { - throw XConfigRead("argument before first screen"); - } - else { - // parse argument: `=' - CString::size_type i = line.find_first_of(" \t="); - if (i == 0) { - throw XConfigRead("missing argument name"); - } - if (i == CString::npos) { - throw XConfigRead("missing = in argument"); - } - CString name = line.substr(0, i); - i = line.find_first_not_of(" \t", i); - if (i == CString::npos || line[i] != '=') { - throw XConfigRead("missing = in argument"); - } - i = line.find_first_not_of(" \t", i + 1); - CString value; - if (i != CString::npos) { - value = line.substr(i); - } - - // handle argument - if (name == "left") { - if (!isScreen(value)) { - throw XConfigRead("unknown screen"); - } - connect(screen, kLeft, value); - } - else if (name == "right") { - if (!isScreen(value)) { - throw XConfigRead("unknown screen"); - } - connect(screen, kRight, value); - } - else if (name == "up") { - if (!isScreen(value)) { - throw XConfigRead("unknown screen"); - } - connect(screen, kTop, value); - } - else if (name == "down") { - if (!isScreen(value)) { - throw XConfigRead("unknown screen"); - } - connect(screen, kBottom, value); - } - else { - // unknown argument - throw XConfigRead("unknown argument"); - } - } - } - throw XConfigRead("unexpected end of links section"); + CEdgeLinks::const_iterator i = m_neighbors.find(edge); + return (i != m_neighbors.end() && i->first == edge); } -void -CConfig::readSectionAliases(std::istream& s) +bool +CConfig::CCell::overlaps(const CCellEdge& edge) const { - CString line; - CString screen; - while (readLine(s, line)) { - // check for end of section - if (line == "end") { - return; + CEdgeLinks::const_iterator i = m_neighbors.upper_bound(edge); + if (i != m_neighbors.end() && i->first.overlaps(edge)) { + return true; + } + if (i != m_neighbors.begin() && (--i)->first.overlaps(edge)) { + return true; + } + return false; +} + +bool +CConfig::CCell::getLink(EDirection side, float position, + const CCellEdge*& src, const CCellEdge*& dst) const +{ + CCellEdge edge(side, position); + CEdgeLinks::const_iterator i = m_neighbors.upper_bound(edge); + if (i == m_neighbors.begin()) { + return false; + } + --i; + if (i->first.getSide() == side && i->first.isInside(position)) { + src = &i->first; + dst = &i->second; + return true; + } + return false; +} + +bool +CConfig::CCell::operator==(const CCell& x) const +{ + // compare options + if (m_options != x.m_options) { + return false; + } + + // compare links + if (m_neighbors.size() != x.m_neighbors.size()) { + return false; + } + for (CEdgeLinks::const_iterator index1 = m_neighbors.begin(), + index2 = x.m_neighbors.begin(); + index1 != m_neighbors.end(); + ++index1, ++index2) { + if (index1->first != index2->first) { + return false; + } + if (index1->second != index2->second) { + return false; } - // see if it's the next screen - if (line[line.size() - 1] == ':') { - // strip : - screen = line.substr(0, line.size() - 1); - - // verify we know about the screen - if (!isScreen(screen)) { - throw XConfigRead("unknown screen name"); - } - if (!isCanonicalName(screen)) { - throw XConfigRead("cannot use screen name alias here"); - } - } - else if (screen.empty()) { - throw XConfigRead("argument before first screen"); - } - else { - // verify validity of screen name - if (!isValidScreenName(line)) { - throw XConfigRead("invalid screen alias"); - } - - // add alias - if (!addAlias(screen, line)) { - throw XConfigRead("alias is duplicate screen name"); - } + // operator== doesn't compare names. only compare destination + // names. + if (!CStringUtil::CaselessCmp::equal(index1->second.getName(), + index2->second.getName())) { + return false; } } - throw XConfigRead("unexpected end of aliases section"); + return true; +} + +bool +CConfig::CCell::operator!=(const CCell& x) const +{ + return !operator==(x); +} + +CConfig::CCell::const_iterator +CConfig::CCell::begin() const +{ + return m_neighbors.begin(); +} + +CConfig::CCell::const_iterator +CConfig::CCell::end() const +{ + return m_neighbors.end(); } @@ -1035,40 +1680,14 @@ CConfig::readSectionAliases(std::istream& s) std::istream& operator>>(std::istream& s, CConfig& config) { - // FIXME -- should track line and column to improve error reporting - - CConfig tmp; - while (s) { - tmp.readSection(s); - } - config = tmp; + CConfigReadContext context(s); + config.read(context); return s; } std::ostream& operator<<(std::ostream& s, const CConfig& config) { - // options section - s << "section: options" << std::endl; - const CConfig::CScreenOptions* options = config.getOptions(""); - if (options != NULL && options->size() > 0) { - for (CConfig::CScreenOptions::const_iterator - option = options->begin(); - option != options->end(); ++option) { - const char* name = CConfig::getOptionName(option->first); - CString value = CConfig::getOptionValue(option->first, - option->second); - if (name != NULL && !value.empty()) { - s << "\t" << name << " = " << value << std::endl; - } - } - } - if (config.m_synergyAddress.isValid()) { - s << "\taddress = " << - config.m_synergyAddress.getHostname().c_str() << std::endl; - } - s << "end" << std::endl; - // screens section s << "section: screens" << std::endl; for (CConfig::const_iterator screen = config.begin(); @@ -1097,24 +1716,14 @@ operator<<(std::ostream& s, const CConfig& config) screen != config.end(); ++screen) { s << "\t" << screen->c_str() << ":" << std::endl; - neighbor = config.getNeighbor(*screen, kLeft); - if (!neighbor.empty()) { - s << "\t\tleft=" << neighbor.c_str() << std::endl; - } - - neighbor = config.getNeighbor(*screen, kRight); - if (!neighbor.empty()) { - s << "\t\tright=" << neighbor.c_str() << std::endl; - } - - neighbor = config.getNeighbor(*screen, kTop); - if (!neighbor.empty()) { - s << "\t\tup=" << neighbor.c_str() << std::endl; - } - - neighbor = config.getNeighbor(*screen, kBottom); - if (!neighbor.empty()) { - s << "\t\tdown=" << neighbor.c_str() << std::endl; + for (CConfig::link_const_iterator + link = config.beginNeighbor(*screen), + nend = config.endNeighbor(*screen); link != nend; ++link) { + s << "\t\t" << CConfig::dirName(link->first.getSide()) << + CConfig::formatInterval(link->first.getInterval()) << + " = " << link->second.getName().c_str() << + CConfig::formatInterval(link->second.getInterval()) << + std::endl; } } s << "end" << std::endl; @@ -1148,6 +1757,460 @@ operator<<(std::ostream& s, const CConfig& config) s << "end" << std::endl; } + // options section + s << "section: options" << std::endl; + const CConfig::CScreenOptions* options = config.getOptions(""); + if (options != NULL && options->size() > 0) { + for (CConfig::CScreenOptions::const_iterator + option = options->begin(); + option != options->end(); ++option) { + const char* name = CConfig::getOptionName(option->first); + CString value = CConfig::getOptionValue(option->first, + option->second); + if (name != NULL && !value.empty()) { + s << "\t" << name << " = " << value << std::endl; + } + } + } + if (config.m_synergyAddress.isValid()) { + s << "\taddress = " << + config.m_synergyAddress.getHostname().c_str() << std::endl; + } + s << config.m_inputFilter.format("\t"); + s << "end" << std::endl; + + return s; +} + + +// +// CConfigReadContext +// + +CConfigReadContext::CConfigReadContext(std::istream& s, SInt32 firstLine) : + m_stream(s), + m_line(firstLine - 1) +{ + // do nothing +} + +CConfigReadContext::~CConfigReadContext() +{ + // do nothing +} + +bool +CConfigReadContext::readLine(CString& line) +{ + ++m_line; + while (std::getline(m_stream, line)) { + // strip leading whitespace + CString::size_type i = line.find_first_not_of(" \t"); + if (i != CString::npos) { + line.erase(0, i); + } + + // strip comments and then trailing whitespace + i = line.find('#'); + if (i != CString::npos) { + line.erase(i); + } + i = line.find_last_not_of(" \r\t"); + if (i != CString::npos) { + line.erase(i + 1); + } + + // return non empty line + if (!line.empty()) { + // make sure there are no invalid characters + for (i = 0; i < line.length(); ++i) { + if (!isgraph(line[i]) && line[i] != ' ' && line[i] != '\t') { + throw XConfigRead(*this, + "invalid character %{1}", + CStringUtil::print("%#2x", line[i])); + } + } + + return true; + } + + // next line + ++m_line; + } + return false; +} + +UInt32 +CConfigReadContext::getLineNumber() const +{ + return m_line; +} + +CConfigReadContext::operator void*() const +{ + return m_stream; +} + +bool +CConfigReadContext::operator!() const +{ + return !m_stream; +} + +OptionValue +CConfigReadContext::parseBoolean(const CString& arg) const +{ + if (CStringUtil::CaselessCmp::equal(arg, "true")) { + return static_cast(true); + } + if (CStringUtil::CaselessCmp::equal(arg, "false")) { + return static_cast(false); + } + throw XConfigRead(*this, "invalid boolean argument \"%{1}\"", arg); +} + +OptionValue +CConfigReadContext::parseInt(const CString& arg) const +{ + const char* s = arg.c_str(); + char* end; + long tmp = strtol(s, &end, 10); + if (*end != '\0') { + // invalid characters + throw XConfigRead(*this, "invalid integer argument \"%{1}\"", arg); + } + OptionValue value = static_cast(tmp); + if (value != tmp) { + // out of range + throw XConfigRead(*this, "integer argument \"%{1}\" out of range", arg); + } + return value; +} + +OptionValue +CConfigReadContext::parseModifierKey(const CString& arg) const +{ + if (CStringUtil::CaselessCmp::equal(arg, "shift")) { + return static_cast(kKeyModifierIDShift); + } + if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) { + return static_cast(kKeyModifierIDControl); + } + if (CStringUtil::CaselessCmp::equal(arg, "alt")) { + return static_cast(kKeyModifierIDAlt); + } + if (CStringUtil::CaselessCmp::equal(arg, "meta")) { + return static_cast(kKeyModifierIDMeta); + } + if (CStringUtil::CaselessCmp::equal(arg, "super")) { + return static_cast(kKeyModifierIDSuper); + } + if (CStringUtil::CaselessCmp::equal(arg, "none")) { + return static_cast(kKeyModifierIDNull); + } + throw XConfigRead(*this, "invalid argument \"%{1}\"", arg); +} + +OptionValue +CConfigReadContext::parseCorner(const CString& arg) const +{ + if (CStringUtil::CaselessCmp::equal(arg, "left")) { + return kTopLeftMask | kBottomLeftMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "right")) { + return kTopRightMask | kBottomRightMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "top")) { + return kTopLeftMask | kTopRightMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "bottom")) { + return kBottomLeftMask | kBottomRightMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "top-left")) { + return kTopLeftMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "top-right")) { + return kTopRightMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "bottom-left")) { + return kBottomLeftMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "bottom-right")) { + return kBottomRightMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "none")) { + return kNoCornerMask; + } + else if (CStringUtil::CaselessCmp::equal(arg, "all")) { + return kAllCornersMask; + } + throw XConfigRead(*this, "invalid argument \"%{1}\"", arg); +} + +OptionValue +CConfigReadContext::parseCorners(const CString& args) const +{ + // find first token + CString::size_type i = args.find_first_not_of(" \t", 0); + if (i == CString::npos) { + throw XConfigRead(*this, "missing corner argument"); + } + CString::size_type j = args.find_first_of(" \t", i); + + // parse first corner token + OptionValue corners = parseCorner(args.substr(i, j - i)); + + // get +/- + i = args.find_first_not_of(" \t", j); + while (i != CString::npos) { + // parse +/- + bool add; + if (args[i] == '-') { + add = false; + } + else if (args[i] == '+') { + add = true; + } + else { + throw XConfigRead(*this, + "invalid corner operator \"%{1}\"", + CString(args.c_str() + i, 1)); + } + + // get next corner token + i = args.find_first_not_of(" \t", i + 1); + j = args.find_first_of(" \t", i); + if (i == CString::npos) { + throw XConfigRead(*this, "missing corner argument"); + } + + // parse next corner token + if (add) { + corners |= parseCorner(args.substr(i, j - i)); + } + else { + corners &= ~parseCorner(args.substr(i, j - i)); + } + i = args.find_first_not_of(" \t", j); + } + + return corners; +} + +CConfig::CInterval +CConfigReadContext::parseInterval(const ArgList& args) const +{ + if (args.size() == 0) { + return CConfig::CInterval(0.0f, 1.0f); + } + if (args.size() != 2 || args[0].empty() || args[1].empty()) { + throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args)); + } + + char* end; + long startValue = strtol(args[0].c_str(), &end, 10); + if (end[0] != '\0') { + throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args)); + } + long endValue = strtol(args[1].c_str(), &end, 10); + if (end[0] != '\0') { + throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args)); + } + + if (startValue < 0 || startValue > 100 || + endValue < 0 || endValue > 100 || + startValue >= endValue) { + throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args)); + } + + return CConfig::CInterval(startValue / 100.0f, endValue / 100.0f); +} + +void +CConfigReadContext::parseNameWithArgs( + const CString& type, const CString& line, + const CString& delim, CString::size_type& index, + CString& name, ArgList& args) const +{ + // skip leading whitespace + CString::size_type i = line.find_first_not_of(" \t", index); + if (i == CString::npos) { + throw XConfigRead(*this, CString("missing ") + type); + } + + // find end of name + CString::size_type j = line.find_first_of(" \t(" + delim, i); + if (j == CString::npos) { + j = line.length(); + } + + // save name + name = line.substr(i, j - i); + args.clear(); + + // is it okay to not find a delimiter? + bool needDelim = (!delim.empty() && delim.find('\n') == CString::npos); + + // skip whitespace + i = line.find_first_not_of(" \t", j); + if (i == CString::npos && needDelim) { + // expected delimiter but didn't find it + throw XConfigRead(*this, CString("missing ") + delim[0]); + } + if (i == CString::npos) { + // no arguments + index = line.length(); + return; + } + if (line[i] != '(') { + // no arguments + index = i; + return; + } + + // eat '(' + ++i; + + // parse arguments + j = line.find_first_of(",)", i); + while (j != CString::npos) { + // extract arg + CString arg(line.substr(i, j - i)); + i = j; + + // trim whitespace + j = arg.find_first_not_of(" \t"); + if (j != CString::npos) { + arg.erase(0, j); + } + j = arg.find_last_not_of(" \t"); + if (j != CString::npos) { + arg.erase(j + 1); + } + + // save arg + args.push_back(arg); + + // exit loop at end of arguments + if (line[i] == ')') { + break; + } + + // eat ',' + ++i; + + // next + j = line.find_first_of(",)", i); + } + + // verify ')' + if (j == CString::npos) { + // expected ) + throw XConfigRead(*this, "missing )"); + } + + // eat ')' + ++i; + + // skip whitespace + j = line.find_first_not_of(" \t", i); + if (j == CString::npos && needDelim) { + // expected delimiter but didn't find it + throw XConfigRead(*this, CString("missing ") + delim[0]); + } + + // verify delimiter + if (needDelim && delim.find(line[j]) == CString::npos) { + throw XConfigRead(*this, CString("expected ") + delim[0]); + } + + if (j == CString::npos) { + j = line.length(); + } + + index = j; + return; +} + +IPlatformScreen::CKeyInfo* +CConfigReadContext::parseKeystroke(const CString& keystroke) const +{ + return parseKeystroke(keystroke, std::set()); +} + +IPlatformScreen::CKeyInfo* +CConfigReadContext::parseKeystroke(const CString& keystroke, + const std::set& screens) const +{ + CString s = keystroke; + + KeyModifierMask mask; + if (!CKeyMap::parseModifiers(s, mask)) { + throw XConfigRead(*this, "unable to parse key modifiers"); + } + + KeyID key; + if (!CKeyMap::parseKey(s, key)) { + throw XConfigRead(*this, "unable to parse key"); + } + + if (key == kKeyNone && mask == 0) { + throw XConfigRead(*this, "missing key and/or modifiers in keystroke"); + } + + return IPlatformScreen::CKeyInfo::alloc(key, mask, 0, 0, screens); +} + +IPlatformScreen::CButtonInfo* +CConfigReadContext::parseMouse(const CString& mouse) const +{ + CString s = mouse; + + KeyModifierMask mask; + if (!CKeyMap::parseModifiers(s, mask)) { + throw XConfigRead(*this, "unable to parse button modifiers"); + } + + char* end; + ButtonID button = (ButtonID)strtol(s.c_str(), &end, 10); + if (*end != '\0') { + throw XConfigRead(*this, "unable to parse button"); + } + if (s.empty() || button <= 0) { + throw XConfigRead(*this, "invalid button"); + } + + return IPlatformScreen::CButtonInfo::alloc(button, mask); +} + +KeyModifierMask +CConfigReadContext::parseModifier(const CString& modifiers) const +{ + CString s = modifiers; + + KeyModifierMask mask; + if (!CKeyMap::parseModifiers(s, mask)) { + throw XConfigRead(*this, "unable to parse modifiers"); + } + + if (mask == 0) { + throw XConfigRead(*this, "no modifiers specified"); + } + + return mask; +} + +CString +CConfigReadContext::concatArgs(const ArgList& args) +{ + CString s("("); + for (size_t i = 0; i < args.size(); ++i) { + if (i != 0) { + s += ","; + } + s += args[i]; + } + s += ")"; return s; } @@ -1156,8 +2219,18 @@ operator<<(std::ostream& s, const CConfig& config) // CConfig I/O exceptions // -XConfigRead::XConfigRead(const CString& error) : - m_error(error) +XConfigRead::XConfigRead(const CConfigReadContext& context, + const CString& error) : + m_error(CStringUtil::print("line %d: %s", + context.getLineNumber(), error.c_str())) +{ + // do nothing +} + +XConfigRead::XConfigRead(const CConfigReadContext& context, + const char* errorFmt, const CString& arg) : + m_error(CStringUtil::print("line %d: ", context.getLineNumber()) + + CStringUtil::format(errorFmt, arg.c_str())) { // do nothing } diff --git a/lib/server/CConfig.h b/lib/server/CConfig.h index a2a9eed5..c0d2faa8 100644 --- a/lib/server/CConfig.h +++ b/lib/server/CConfig.h @@ -19,12 +19,15 @@ #include "ProtocolTypes.h" #include "CNetworkAddress.h" #include "CStringUtil.h" +#include "CInputFilter.h" #include "XBase.h" #include "stdmap.h" #include "stdset.h" +#include "IPlatformScreen.h" #include class CConfig; +class CConfigReadContext; namespace std { template <> @@ -50,17 +53,93 @@ namespace and must be unique. class CConfig { public: typedef std::map CScreenOptions; + typedef std::pair CInterval; + + class CCellEdge { + public: + CCellEdge(EDirection side, float position); + CCellEdge(EDirection side, const CInterval&); + CCellEdge(const CString& name, EDirection side, const CInterval&); + ~CCellEdge(); + + CInterval getInterval() const; + void setName(const CString& newName); + CString getName() const; + EDirection getSide() const; + bool overlaps(const CCellEdge&) const; + bool isInside(float x) const; + + // transform position to [0,1] + float transform(float x) const; + + // transform [0,1] to position + float inverseTransform(float x) const; + + // compares side and start of interval + bool operator<(const CCellEdge&) const; + + // compares side and interval + bool operator==(const CCellEdge&) const; + bool operator!=(const CCellEdge&) const; + + private: + void init(const CString& name, EDirection side, + const CInterval&); + + private: + CString m_name; + EDirection m_side; + CInterval m_interval; + }; private: - class CCell { + class CName { + public: + CName(CConfig*, const CString& name); + + bool operator==(const CString& name) const; + + private: + CConfig* m_config; + CString m_name; + }; + + class CCell { + private: + typedef std::map CEdgeLinks; + + public: + typedef CEdgeLinks::const_iterator const_iterator; + + bool add(const CCellEdge& src, const CCellEdge& dst); + void remove(EDirection side); + void remove(EDirection side, float position); + void remove(const CName& destinationName); + void rename(const CName& oldName, const CString& newName); + + bool hasEdge(const CCellEdge&) const; + bool overlaps(const CCellEdge&) const; + + bool getLink(EDirection side, float position, + const CCellEdge*& src, const CCellEdge*& dst) const; + + bool operator==(const CCell&) const; + bool operator!=(const CCell&) const; + + const_iterator begin() const; + const_iterator end() const; + + private: + CEdgeLinks m_neighbors; + public: - CString m_neighbor[kLastDirection - kFirstDirection + 1]; CScreenOptions m_options; }; typedef std::map CCellMap; typedef std::map CNameMap; public: + typedef CCell::const_iterator link_const_iterator; typedef CCellMap::const_iterator internal_const_iterator; typedef CNameMap::const_iterator all_const_iterator; class const_iterator : std::iterator_traits { @@ -141,6 +220,13 @@ public: */ bool removeAlias(const CString& alias); + //! Remove aliases + /*! + Removes all aliases for a canonical screen name. It returns false + if the canonical name is unknown, otherwise returns true. + */ + bool removeAliases(const CString& canonical); + //! Remove all aliases /*! This removes all aliases but not the screens. @@ -149,25 +235,43 @@ public: //! Connect screens /*! - Establishes a one-way connection between opposite edges of two - screens. The user will be able to jump from the \c srcSide of - screen \c srcName to the opposite side of screen \c dstName - when both screens are connected to the server and the user - isn't locked to a screen. Returns false if \c srcName is - unknown. + Establishes a one-way connection between portions of opposite edges + of two screens. Each portion is described by an interval defined + by two numbers, the start and end of the interval half-open on the + end. The numbers range from 0 to 1, inclusive, for the left/top + to the right/bottom. The user will be able to jump from the + \c srcStart to \c srcSend interval of \c srcSide of screen + \c srcName to the opposite side of screen \c dstName in the interval + \c dstStart and \c dstEnd when both screens are connected to the + server and the user isn't locked to a screen. Returns false if + \c srcName is unknown. \c srcStart must be less than or equal to + \c srcEnd and \c dstStart must be less then or equal to \c dstEnd + and all of \c srcStart, \c srcEnd, \c dstStart, or \c dstEnd must + be inside the range [0,1]. */ bool connect(const CString& srcName, EDirection srcSide, - const CString& dstName); + float srcStart, float srcEnd, + const CString& dstName, + float dstStart, float dstEnd); //! Disconnect screens /*! - Removes a connection created by connect(). Returns false if - \c srcName is unknown. + Removes all connections created by connect() on side \c srcSide. + Returns false if \c srcName is unknown. */ bool disconnect(const CString& srcName, EDirection srcSide); + //! Disconnect screens + /*! + Removes the connections created by connect() on side \c srcSide + covering position \c position. Returns false if \c srcName is + unknown. + */ + bool disconnect(const CString& srcName, + EDirection srcSide, float position); + //! Set server address /*! Set the synergy listen addresses. There is no default address so @@ -199,6 +303,13 @@ public: */ bool removeOptions(const CString& name); + //! Get the hot key input filter + /*! + Returns the hot key input filter. Clients can modify hotkeys using + that object. + */ + CInputFilter* getInputFilter(); + //@} //! @name accessors //@{ @@ -241,10 +352,33 @@ public: //! Get neighbor /*! Returns the canonical screen name of the neighbor in the given - direction (set through connect()). Returns the empty string - if there is no neighbor in that direction. + direction (set through connect()) at position \c position. Returns + the empty string if there is no neighbor in that direction, otherwise + saves the position on the neighbor in \c positionOut if it's not + \c NULL. */ - CString getNeighbor(const CString&, EDirection) const; + CString getNeighbor(const CString&, EDirection, + float position, float* positionOut) const; + + //! Check for neighbor + /*! + Returns \c true if the screen has a neighbor anywhere along the edge + given by the direction. + */ + bool hasNeighbor(const CString&, EDirection) const; + + //! Check for neighbor + /*! + Returns \c true if the screen has a neighbor in the given range along + the edge given by the direction. + */ + bool hasNeighbor(const CString&, EDirection, + float start, float end) const; + + //! Get beginning neighbor iterator + link_const_iterator beginNeighbor(const CString&) const; + //! Get ending neighbor iterator + link_const_iterator endNeighbor(const CString&) const; //! Get the server address const CNetworkAddress& getSynergyAddress() const; @@ -257,11 +391,25 @@ public: */ const CScreenOptions* getOptions(const CString& name) const; + //! Check for lock to screen action + /*! + Returns \c true if this configuration has a lock to screen action. + This is for backwards compatible support of ScrollLock locking. + */ + bool hasLockToScreenAction() const; + //! Compare configurations bool operator==(const CConfig&) const; //! Compare configurations bool operator!=(const CConfig&) const; + //! Read configuration + /*! + Reads a configuration from a context. Throws XConfigRead on error + and context is unchanged. + */ + void read(CConfigReadContext& context); + //! Read configuration /*! Reads a configuration from a stream. Throws XConfigRead on error. @@ -280,26 +428,90 @@ public: */ static const char* dirName(EDirection); + //! Get interval as string + /*! + Returns an interval as a parseable string. + */ + static CString formatInterval(const CInterval&); + //@} private: - static bool readLine(std::istream&, CString&); - static OptionValue parseBoolean(const CString&); - static OptionValue parseInt(const CString&); - static OptionValue parseModifierKey(const CString&); + void readSection(CConfigReadContext&); + void readSectionOptions(CConfigReadContext&); + void readSectionScreens(CConfigReadContext&); + void readSectionLinks(CConfigReadContext&); + void readSectionAliases(CConfigReadContext&); + + CInputFilter::CCondition* + parseCondition(CConfigReadContext&, + const CString& condition, + const std::vector& args); + void parseAction(CConfigReadContext&, + const CString& action, + const std::vector& args, + CInputFilter::CRule&, bool activate); + + void parseScreens(CConfigReadContext&, const CString&, + std::set& screens) const; static const char* getOptionName(OptionID); static CString getOptionValue(OptionID, OptionValue); - void readSection(std::istream&); - void readSectionOptions(std::istream&); - void readSectionScreens(std::istream&); - void readSectionLinks(std::istream&); - void readSectionAliases(std::istream&); private: CCellMap m_map; CNameMap m_nameToCanonicalName; CNetworkAddress m_synergyAddress; CScreenOptions m_globalOptions; + CInputFilter m_inputFilter; + bool m_hasLockToScreenAction; +}; + +//! Configuration read context +/*! +Maintains a context when reading a configuration from a stream. +*/ +class CConfigReadContext { +public: + typedef std::vector ArgList; + + CConfigReadContext(std::istream&, SInt32 firstLine = 1); + ~CConfigReadContext(); + + bool readLine(CString&); + UInt32 getLineNumber() const; + + operator void*() const; + bool operator!() const; + + OptionValue parseBoolean(const CString&) const; + OptionValue parseInt(const CString&) const; + OptionValue parseModifierKey(const CString&) const; + OptionValue parseCorner(const CString&) const; + OptionValue parseCorners(const CString&) const; + CConfig::CInterval + parseInterval(const ArgList& args) const; + void parseNameWithArgs( + const CString& type, const CString& line, + const CString& delim, CString::size_type& index, + CString& name, ArgList& args) const; + IPlatformScreen::CKeyInfo* + parseKeystroke(const CString& keystroke) const; + IPlatformScreen::CKeyInfo* + parseKeystroke(const CString& keystroke, + const std::set& screens) const; + IPlatformScreen::CButtonInfo* + parseMouse(const CString& mouse) const; + KeyModifierMask parseModifier(const CString& modifiers) const; + +private: + // not implemented + CConfigReadContext& operator=(const CConfigReadContext&); + + static CString concatArgs(const ArgList& args); + +private: + std::istream& m_stream; + SInt32 m_line; }; //! Configuration stream read exception @@ -308,7 +520,9 @@ Thrown when a configuration stream cannot be parsed. */ class XConfigRead : public XBase { public: - XConfigRead(const CString&); + XConfigRead(const CConfigReadContext& context, const CString&); + XConfigRead(const CConfigReadContext& context, + const char* errorFmt, const CString& arg); ~XConfigRead(); protected: diff --git a/lib/server/CInputFilter.cpp b/lib/server/CInputFilter.cpp new file mode 100644 index 00000000..2a6c0e3a --- /dev/null +++ b/lib/server/CInputFilter.cpp @@ -0,0 +1,999 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2005 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CInputFilter.h" +#include "CServer.h" +#include "CPrimaryClient.h" +#include "CKeyMap.h" +#include "CEventQueue.h" +#include "CLog.h" +#include "TMethodEventJob.h" +#include +#include + +// ----------------------------------------------------------------------------- +// Input Filter Condition Classes +// ----------------------------------------------------------------------------- +CInputFilter::CCondition::CCondition() +{ + // do nothing +} + +CInputFilter::CCondition::~CCondition() +{ + // do nothing +} + +void +CInputFilter::CCondition::enablePrimary(CPrimaryClient*) +{ + // do nothing +} + +void +CInputFilter::CCondition::disablePrimary(CPrimaryClient*) +{ + // do nothing +} + +CInputFilter::CKeystrokeCondition::CKeystrokeCondition( + IPlatformScreen::CKeyInfo* info) : + m_id(0), + m_key(info->m_key), + m_mask(info->m_mask) +{ + free(info); +} + +CInputFilter::CKeystrokeCondition::CKeystrokeCondition( + KeyID key, KeyModifierMask mask) : + m_id(0), + m_key(key), + m_mask(mask) +{ + // do nothing +} + +CInputFilter::CKeystrokeCondition::~CKeystrokeCondition() +{ + // do nothing +} + +KeyID +CInputFilter::CKeystrokeCondition::getKey() const +{ + return m_key; +} + +KeyModifierMask +CInputFilter::CKeystrokeCondition::getMask() const +{ + return m_mask; +} + +CInputFilter::CCondition* +CInputFilter::CKeystrokeCondition::clone() const +{ + return new CKeystrokeCondition(m_key, m_mask); +} + +CString +CInputFilter::CKeystrokeCondition::format() const +{ + return CStringUtil::print("keystroke(%s)", + CKeyMap::formatKey(m_key, m_mask).c_str()); +} + +CInputFilter::EFilterStatus +CInputFilter::CKeystrokeCondition::match(const CEvent& event) +{ + EFilterStatus status; + + // check for hotkey events + CEvent::Type type = event.getType(); + if (type == IPrimaryScreen::getHotKeyDownEvent()) { + status = kActivate; + } + else if (type == IPrimaryScreen::getHotKeyUpEvent()) { + status = kDeactivate; + } + else { + return kNoMatch; + } + + // check if it's our hotkey + IPrimaryScreen::CHotKeyInfo* kinfo = + reinterpret_cast(event.getData()); + if (kinfo->m_id != m_id) { + return kNoMatch; + } + + return status; +} + +void +CInputFilter::CKeystrokeCondition::enablePrimary(CPrimaryClient* primary) +{ + m_id = primary->registerHotKey(m_key, m_mask); +} + +void +CInputFilter::CKeystrokeCondition::disablePrimary(CPrimaryClient* primary) +{ + primary->unregisterHotKey(m_id); + m_id = 0; +} + +CInputFilter::CMouseButtonCondition::CMouseButtonCondition( + IPlatformScreen::CButtonInfo* info) : + m_button(info->m_button), + m_mask(info->m_mask) +{ + free(info); +} + +CInputFilter::CMouseButtonCondition::CMouseButtonCondition( + ButtonID button, KeyModifierMask mask) : + m_button(button), + m_mask(mask) +{ + // do nothing +} + +CInputFilter::CMouseButtonCondition::~CMouseButtonCondition() +{ + // do nothing +} + +ButtonID +CInputFilter::CMouseButtonCondition::getButton() const +{ + return m_button; +} + +KeyModifierMask +CInputFilter::CMouseButtonCondition::getMask() const +{ + return m_mask; +} + +CInputFilter::CCondition* +CInputFilter::CMouseButtonCondition::clone() const +{ + return new CMouseButtonCondition(m_button, m_mask); +} + +CString +CInputFilter::CMouseButtonCondition::format() const +{ + CString key = CKeyMap::formatKey(kKeyNone, m_mask); + if (!key.empty()) { + key += "+"; + } + return CStringUtil::print("mousebutton(%s%d)", key.c_str(), m_button); +} + +CInputFilter::EFilterStatus +CInputFilter::CMouseButtonCondition::match(const CEvent& event) +{ + static const KeyModifierMask s_ignoreMask = + KeyModifierAltGr | KeyModifierCapsLock | + KeyModifierNumLock | KeyModifierScrollLock; + + EFilterStatus status; + + // check for hotkey events + CEvent::Type type = event.getType(); + if (type == IPrimaryScreen::getButtonDownEvent()) { + status = kActivate; + } + else if (type == IPrimaryScreen::getButtonUpEvent()) { + status = kDeactivate; + } + else { + return kNoMatch; + } + + // check if it's the right button and modifiers. ignore modifiers + // that cannot be combined with a mouse button. + IPlatformScreen::CButtonInfo* minfo = + reinterpret_cast(event.getData()); + if (minfo->m_button != m_button || + (minfo->m_mask & ~s_ignoreMask) != m_mask) { + return kNoMatch; + } + + return status; +} + +CInputFilter::CScreenConnectedCondition::CScreenConnectedCondition( + const CString& screen) : + m_screen(screen) +{ + // do nothing +} + +CInputFilter::CScreenConnectedCondition::~CScreenConnectedCondition() +{ + // do nothing +} + +CInputFilter::CCondition* +CInputFilter::CScreenConnectedCondition::clone() const +{ + return new CScreenConnectedCondition(m_screen); +} + +CString +CInputFilter::CScreenConnectedCondition::format() const +{ + return CStringUtil::print("connect(%s)", m_screen.c_str()); +} + +CInputFilter::EFilterStatus +CInputFilter::CScreenConnectedCondition::match(const CEvent& event) +{ + if (event.getType() == CServer::getConnectedEvent()) { + CServer::CScreenConnectedInfo* info = + reinterpret_cast(event.getData()); + if (m_screen == info->m_screen || m_screen.empty()) { + return kActivate; + } + } + + return kNoMatch; +} + +// ----------------------------------------------------------------------------- +// Input Filter Action Classes +// ----------------------------------------------------------------------------- +CInputFilter::CAction::CAction() +{ + // do nothing +} + +CInputFilter::CAction::~CAction() +{ + // do nothing +} + +CInputFilter::CLockCursorToScreenAction::CLockCursorToScreenAction(Mode mode): + m_mode(mode) +{ + // do nothing +} + + +CInputFilter::CLockCursorToScreenAction::Mode +CInputFilter::CLockCursorToScreenAction::getMode() const +{ + return m_mode; +} + +CInputFilter::CAction* +CInputFilter::CLockCursorToScreenAction::clone() const +{ + return new CLockCursorToScreenAction(*this); +} + +CString +CInputFilter::CLockCursorToScreenAction::format() const +{ + static const char* s_mode[] = { "off", "on", "toggle" }; + + return CStringUtil::print("lockCursorToScreen(%s)", s_mode[m_mode]); +} + +void +CInputFilter::CLockCursorToScreenAction::perform(const CEvent& event) +{ + static const CServer::CLockCursorToScreenInfo::State s_state[] = { + CServer::CLockCursorToScreenInfo::kOff, + CServer::CLockCursorToScreenInfo::kOn, + CServer::CLockCursorToScreenInfo::kToggle + }; + + // send event + CServer::CLockCursorToScreenInfo* info = + CServer::CLockCursorToScreenInfo::alloc(s_state[m_mode]); + EVENTQUEUE->addEvent(CEvent(CServer::getLockCursorToScreenEvent(), + event.getTarget(), info, + CEvent::kDeliverImmediately)); +} + +CInputFilter::CSwitchToScreenAction::CSwitchToScreenAction( + const CString& screen) : + m_screen(screen) +{ + // do nothing +} + +CString +CInputFilter::CSwitchToScreenAction::getScreen() const +{ + return m_screen; +} + +CInputFilter::CAction* +CInputFilter::CSwitchToScreenAction::clone() const +{ + return new CSwitchToScreenAction(*this); +} + +CString +CInputFilter::CSwitchToScreenAction::format() const +{ + return CStringUtil::print("switchToScreen(%s)", m_screen.c_str()); +} + +void +CInputFilter::CSwitchToScreenAction::perform(const CEvent& event) +{ + // pick screen name. if m_screen is empty then use the screen from + // event if it has one. + CString screen = m_screen; + if (screen.empty() && event.getType() == CServer::getConnectedEvent()) { + CServer::CScreenConnectedInfo* info = + reinterpret_cast(event.getData()); + screen = info->m_screen; + } + + // send event + CServer::CSwitchToScreenInfo* info = + CServer::CSwitchToScreenInfo::alloc(screen); + EVENTQUEUE->addEvent(CEvent(CServer::getSwitchToScreenEvent(), + event.getTarget(), info, + CEvent::kDeliverImmediately)); +} + +CInputFilter::CSwitchInDirectionAction::CSwitchInDirectionAction( + EDirection direction) : + m_direction(direction) +{ + // do nothing +} + +EDirection +CInputFilter::CSwitchInDirectionAction::getDirection() const +{ + return m_direction; +} + +CInputFilter::CAction* +CInputFilter::CSwitchInDirectionAction::clone() const +{ + return new CSwitchInDirectionAction(*this); +} + +CString +CInputFilter::CSwitchInDirectionAction::format() const +{ + static const char* s_names[] = { + "", + "left", + "right", + "up", + "down" + }; + + return CStringUtil::print("switchInDirection(%s)", s_names[m_direction]); +} + +void +CInputFilter::CSwitchInDirectionAction::perform(const CEvent& event) +{ + CServer::CSwitchInDirectionInfo* info = + CServer::CSwitchInDirectionInfo::alloc(m_direction); + EVENTQUEUE->addEvent(CEvent(CServer::getSwitchInDirectionEvent(), + event.getTarget(), info, + CEvent::kDeliverImmediately)); +} + +CInputFilter::CKeystrokeAction::CKeystrokeAction( + IPlatformScreen::CKeyInfo* info, bool press) : + m_keyInfo(info), + m_press(press) +{ + // do nothing +} + +CInputFilter::CKeystrokeAction::~CKeystrokeAction() +{ + free(m_keyInfo); +} + +void +CInputFilter::CKeystrokeAction::adoptInfo(IPlatformScreen::CKeyInfo* info) +{ + free(m_keyInfo); + m_keyInfo = info; +} + +const IPlatformScreen::CKeyInfo* +CInputFilter::CKeystrokeAction::getInfo() const +{ + return m_keyInfo; +} + +bool +CInputFilter::CKeystrokeAction::isOnPress() const +{ + return m_press; +} + +CInputFilter::CAction* +CInputFilter::CKeystrokeAction::clone() const +{ + IKeyState::CKeyInfo* info = IKeyState::CKeyInfo::alloc(*m_keyInfo); + return new CKeystrokeAction(info, m_press); +} + +CString +CInputFilter::CKeystrokeAction::format() const +{ + const char* type = formatName(); + + if (m_keyInfo->m_screens[0] == '\0') { + return CStringUtil::print("%s(%s)", type, + CKeyMap::formatKey(m_keyInfo->m_key, + m_keyInfo->m_mask).c_str()); + } + else if (m_keyInfo->m_screens[0] == '*') { + return CStringUtil::print("%s(%s,*)", type, + CKeyMap::formatKey(m_keyInfo->m_key, + m_keyInfo->m_mask).c_str()); + } + else { + return CStringUtil::print("%s(%s,%.*s)", type, + CKeyMap::formatKey(m_keyInfo->m_key, + m_keyInfo->m_mask).c_str(), + strlen(m_keyInfo->m_screens + 1) - 1, + m_keyInfo->m_screens + 1); + } +} + +void +CInputFilter::CKeystrokeAction::perform(const CEvent& event) +{ + CEvent::Type type = m_press ? IPlatformScreen::getKeyDownEvent() : + IPlatformScreen::getKeyUpEvent(); + EVENTQUEUE->addEvent(CEvent(IPlatformScreen::getFakeInputBeginEvent(), + event.getTarget(), NULL, + CEvent::kDeliverImmediately)); + EVENTQUEUE->addEvent(CEvent(type, event.getTarget(), m_keyInfo, + CEvent::kDeliverImmediately | + CEvent::kDontFreeData)); + EVENTQUEUE->addEvent(CEvent(IPlatformScreen::getFakeInputEndEvent(), + event.getTarget(), NULL, + CEvent::kDeliverImmediately)); +} + +const char* +CInputFilter::CKeystrokeAction::formatName() const +{ + return (m_press ? "keyDown" : "keyUp"); +} + +CInputFilter::CMouseButtonAction::CMouseButtonAction( + IPlatformScreen::CButtonInfo* info, bool press) : + m_buttonInfo(info), + m_press(press) +{ + // do nothing +} + +CInputFilter::CMouseButtonAction::~CMouseButtonAction() +{ + free(m_buttonInfo); +} + +const IPlatformScreen::CButtonInfo* +CInputFilter::CMouseButtonAction::getInfo() const +{ + return m_buttonInfo; +} + +bool +CInputFilter::CMouseButtonAction::isOnPress() const +{ + return m_press; +} + +CInputFilter::CAction* +CInputFilter::CMouseButtonAction::clone() const +{ + IPlatformScreen::CButtonInfo* info = + IPrimaryScreen::CButtonInfo::alloc(*m_buttonInfo); + return new CMouseButtonAction(info, m_press); +} + +CString +CInputFilter::CMouseButtonAction::format() const +{ + const char* type = formatName(); + + CString key = CKeyMap::formatKey(kKeyNone, m_buttonInfo->m_mask); + return CStringUtil::print("%s(%s%s%d)", type, + key.c_str(), key.empty() ? "" : "+", + m_buttonInfo->m_button); +} + +void +CInputFilter::CMouseButtonAction::perform(const CEvent& event) + +{ + // send modifiers + IPlatformScreen::CKeyInfo* modifierInfo = NULL; + if (m_buttonInfo->m_mask != 0) { + KeyID key = m_press ? kKeySetModifiers : kKeyClearModifiers; + modifierInfo = + IKeyState::CKeyInfo::alloc(key, m_buttonInfo->m_mask, 0, 1); + EVENTQUEUE->addEvent(CEvent(IPlatformScreen::getKeyDownEvent(), + event.getTarget(), modifierInfo, + CEvent::kDeliverImmediately)); + } + + // send button + CEvent::Type type = m_press ? IPlatformScreen::getButtonDownEvent() : + IPlatformScreen::getButtonUpEvent(); + EVENTQUEUE->addEvent(CEvent(type, event.getTarget(), m_buttonInfo, + CEvent::kDeliverImmediately | + CEvent::kDontFreeData)); +} + +const char* +CInputFilter::CMouseButtonAction::formatName() const +{ + return (m_press ? "mouseDown" : "mouseUp"); +} + +// +// CInputFilter::CRule +// + +CInputFilter::CRule::CRule() : + m_condition(NULL) +{ + // do nothing +} + +CInputFilter::CRule::CRule(CCondition* adoptedCondition) : + m_condition(adoptedCondition) +{ + // do nothing +} + +CInputFilter::CRule::CRule(const CRule& rule) : + m_condition(NULL) +{ + copy(rule); +} + +CInputFilter::CRule::~CRule() +{ + clear(); +} + +CInputFilter::CRule& +CInputFilter::CRule::operator=(const CRule& rule) +{ + if (&rule != this) { + copy(rule); + } + return *this; +} + +void +CInputFilter::CRule::clear() +{ + delete m_condition; + for (CActionList::iterator i = m_activateActions.begin(); + i != m_activateActions.end(); ++i) { + delete *i; + } + for (CActionList::iterator i = m_deactivateActions.begin(); + i != m_deactivateActions.end(); ++i) { + delete *i; + } + + m_condition = NULL; + m_activateActions.clear(); + m_deactivateActions.clear(); +} + +void +CInputFilter::CRule::copy(const CRule& rule) +{ + clear(); + if (rule.m_condition != NULL) { + m_condition = rule.m_condition->clone(); + } + for (CActionList::const_iterator i = rule.m_activateActions.begin(); + i != rule.m_activateActions.end(); ++i) { + m_activateActions.push_back((*i)->clone()); + } + for (CActionList::const_iterator i = rule.m_deactivateActions.begin(); + i != rule.m_deactivateActions.end(); ++i) { + m_deactivateActions.push_back((*i)->clone()); + } +} + +void +CInputFilter::CRule::setCondition(CCondition* adopted) +{ + delete m_condition; + m_condition = adopted; +} + +void +CInputFilter::CRule::adoptAction(CAction* action, bool onActivation) +{ + if (action != NULL) { + if (onActivation) { + m_activateActions.push_back(action); + } + else { + m_deactivateActions.push_back(action); + } + } +} + +void +CInputFilter::CRule::removeAction(bool onActivation, UInt32 index) +{ + if (onActivation) { + delete m_activateActions[index]; + m_activateActions.erase(m_activateActions.begin() + index); + } + else { + delete m_deactivateActions[index]; + m_deactivateActions.erase(m_deactivateActions.begin() + index); + } +} + +void +CInputFilter::CRule::replaceAction(CAction* adopted, + bool onActivation, UInt32 index) +{ + if (adopted == NULL) { + removeAction(onActivation, index); + } + else if (onActivation) { + delete m_activateActions[index]; + m_activateActions[index] = adopted; + } + else { + delete m_deactivateActions[index]; + m_deactivateActions[index] = adopted; + } +} + +void +CInputFilter::CRule::enable(CPrimaryClient* primaryClient) +{ + if (m_condition != NULL) { + m_condition->enablePrimary(primaryClient); + } +} + +void +CInputFilter::CRule::disable(CPrimaryClient* primaryClient) +{ + if (m_condition != NULL) { + m_condition->disablePrimary(primaryClient); + } +} + +bool +CInputFilter::CRule::handleEvent(const CEvent& event) +{ + // NULL condition never matches + if (m_condition == NULL) { + return false; + } + + // match + const CActionList* actions; + switch (m_condition->match(event)) { + default: + // not handled + return false; + + case kActivate: + actions = &m_activateActions; + LOG((CLOG_DEBUG1 "activate actions")); + break; + + case kDeactivate: + actions = &m_deactivateActions; + LOG((CLOG_DEBUG1 "deactivate actions")); + break; + } + + // perform actions + for (CActionList::const_iterator i = actions->begin(); + i != actions->end(); ++i) { + LOG((CLOG_DEBUG1 "hotkey: %s", (*i)->format().c_str())); + (*i)->perform(event); + } + + return true; +} + +CString +CInputFilter::CRule::format() const +{ + CString s; + if (m_condition != NULL) { + // condition + s += m_condition->format(); + s += " = "; + + // activate actions + CActionList::const_iterator i = m_activateActions.begin(); + if (i != m_activateActions.end()) { + s += (*i)->format(); + while (++i != m_activateActions.end()) { + s += ", "; + s += (*i)->format(); + } + } + + // deactivate actions + if (!m_deactivateActions.empty()) { + s += "; "; + i = m_deactivateActions.begin(); + if (i != m_deactivateActions.end()) { + s += (*i)->format(); + while (++i != m_deactivateActions.end()) { + s += ", "; + s += (*i)->format(); + } + } + } + } + return s; +} + +const CInputFilter::CCondition* +CInputFilter::CRule::getCondition() const +{ + return m_condition; +} + +UInt32 +CInputFilter::CRule::getNumActions(bool onActivation) const +{ + if (onActivation) { + return static_cast(m_activateActions.size()); + } + else { + return static_cast(m_deactivateActions.size()); + } +} + +const CInputFilter::CAction& +CInputFilter::CRule::getAction(bool onActivation, UInt32 index) const +{ + if (onActivation) { + return *m_activateActions[index]; + } + else { + return *m_deactivateActions[index]; + } +} + + +// ----------------------------------------------------------------------------- +// Input Filter Class +// ----------------------------------------------------------------------------- +CInputFilter::CInputFilter() : + m_primaryClient(NULL) +{ + // do nothing +} + +CInputFilter::CInputFilter(const CInputFilter& x) : + m_ruleList(x.m_ruleList), + m_primaryClient(NULL) +{ + setPrimaryClient(x.m_primaryClient); +} + +CInputFilter::~CInputFilter() +{ + setPrimaryClient(NULL); +} + +CInputFilter& +CInputFilter::operator=(const CInputFilter& x) +{ + if (&x != this) { + CPrimaryClient* oldClient = m_primaryClient; + setPrimaryClient(NULL); + + m_ruleList = x.m_ruleList; + + setPrimaryClient(oldClient); + } + return *this; +} + +void +CInputFilter::addFilterRule(const CRule& rule) +{ + m_ruleList.push_back(rule); + if (m_primaryClient != NULL) { + m_ruleList.back().enable(m_primaryClient); + } +} + +void +CInputFilter::removeFilterRule(UInt32 index) +{ + if (m_primaryClient != NULL) { + m_ruleList[index].disable(m_primaryClient); + } + m_ruleList.erase(m_ruleList.begin() + index); +} + +CInputFilter::CRule& +CInputFilter::getRule(UInt32 index) +{ + return m_ruleList[index]; +} + +void +CInputFilter::setPrimaryClient(CPrimaryClient* client) +{ + if (m_primaryClient == client) { + return; + } + + if (m_primaryClient != NULL) { + for (CRuleList::iterator rule = m_ruleList.begin(); + rule != m_ruleList.end(); ++rule) { + rule->disable(m_primaryClient); + } + + EVENTQUEUE->removeHandler(IPlatformScreen::getKeyDownEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getKeyUpEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getKeyRepeatEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getButtonDownEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getButtonUpEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getHotKeyDownEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getHotKeyUpEvent(), + m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(CServer::getConnectedEvent(), + m_primaryClient->getEventTarget()); + } + + m_primaryClient = client; + + if (m_primaryClient != NULL) { + EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyDownEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyUpEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyRepeatEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonDownEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonUpEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getHotKeyDownEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getHotKeyUpEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + EVENTQUEUE->adoptHandler(CServer::getConnectedEvent(), + m_primaryClient->getEventTarget(), + new TMethodEventJob(this, + &CInputFilter::handleEvent)); + + for (CRuleList::iterator rule = m_ruleList.begin(); + rule != m_ruleList.end(); ++rule) { + rule->enable(m_primaryClient); + } + } +} + +CString +CInputFilter::format(const CString& linePrefix) const +{ + CString s; + for (CRuleList::const_iterator i = m_ruleList.begin(); + i != m_ruleList.end(); ++i) { + s += linePrefix; + s += i->format(); + s += "\n"; + } + return s; +} + +UInt32 +CInputFilter::getNumRules() const +{ + return static_cast(m_ruleList.size()); +} + +bool +CInputFilter::operator==(const CInputFilter& x) const +{ + // if there are different numbers of rules then we can't be equal + if (m_ruleList.size() != x.m_ruleList.size()) { + return false; + } + + // compare rule lists. the easiest way to do that is to format each + // rule into a string, sort the strings, then compare the results. + std::vector aList, bList; + for (CRuleList::const_iterator i = m_ruleList.begin(); + i != m_ruleList.end(); ++i) { + aList.push_back(i->format()); + } + for (CRuleList::const_iterator i = x.m_ruleList.begin(); + i != x.m_ruleList.end(); ++i) { + bList.push_back(i->format()); + } + std::partial_sort(aList.begin(), aList.end(), aList.end()); + std::partial_sort(bList.begin(), bList.end(), bList.end()); + return (aList == bList); +} + +bool +CInputFilter::operator!=(const CInputFilter& x) const +{ + return !operator==(x); +} + +void +CInputFilter::handleEvent(const CEvent& event, void*) +{ + // copy event and adjust target + CEvent myEvent(event.getType(), this, event.getData(), + event.getFlags() | CEvent::kDontFreeData | + CEvent::kDeliverImmediately); + + // let each rule try to match the event until one does + for (CRuleList::iterator rule = m_ruleList.begin(); + rule != m_ruleList.end(); ++rule) { + if (rule->handleEvent(myEvent)) { + // handled + return; + } + } + + // not handled so pass through + EVENTQUEUE->addEvent(myEvent); +} diff --git a/lib/server/CInputFilter.h b/lib/server/CInputFilter.h new file mode 100644 index 00000000..1c64636c --- /dev/null +++ b/lib/server/CInputFilter.h @@ -0,0 +1,322 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2005 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CINPUTFILTER_H +#define CINPUTFILTER_H + +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "ProtocolTypes.h" +#include "IPlatformScreen.h" +#include "CString.h" +#include "stdmap.h" + +class CPrimaryClient; +class CEvent; + +class CInputFilter { +public: + // ------------------------------------------------------------------------- + // Input Filter Condition Classes + // ------------------------------------------------------------------------- + enum EFilterStatus { + kNoMatch, + kActivate, + kDeactivate + }; + + class CCondition { + public: + CCondition(); + virtual ~CCondition(); + + virtual CCondition* clone() const = 0; + virtual CString format() const = 0; + + virtual EFilterStatus match(const CEvent&) = 0; + + virtual void enablePrimary(CPrimaryClient*); + virtual void disablePrimary(CPrimaryClient*); + }; + + // CKeystrokeCondition + class CKeystrokeCondition : public CCondition { + public: + CKeystrokeCondition(IPlatformScreen::CKeyInfo*); + CKeystrokeCondition(KeyID key, KeyModifierMask mask); + virtual ~CKeystrokeCondition(); + + KeyID getKey() const; + KeyModifierMask getMask() const; + + // CCondition overrides + virtual CCondition* clone() const; + virtual CString format() const; + virtual EFilterStatus match(const CEvent&); + virtual void enablePrimary(CPrimaryClient*); + virtual void disablePrimary(CPrimaryClient*); + + private: + UInt32 m_id; + KeyID m_key; + KeyModifierMask m_mask; + }; + + // CMouseButtonCondition + class CMouseButtonCondition : public CCondition { + public: + CMouseButtonCondition(IPlatformScreen::CButtonInfo*); + CMouseButtonCondition(ButtonID, KeyModifierMask mask); + virtual ~CMouseButtonCondition(); + + ButtonID getButton() const; + KeyModifierMask getMask() const; + + // CCondition overrides + virtual CCondition* clone() const; + virtual CString format() const; + virtual EFilterStatus match(const CEvent&); + + private: + ButtonID m_button; + KeyModifierMask m_mask; + }; + + // CScreenConnectedCondition + class CScreenConnectedCondition : public CCondition { + public: + CScreenConnectedCondition(const CString& screen); + virtual ~CScreenConnectedCondition(); + + // CCondition overrides + virtual CCondition* clone() const; + virtual CString format() const; + virtual EFilterStatus match(const CEvent&); + + private: + CString m_screen; + }; + + // ------------------------------------------------------------------------- + // Input Filter Action Classes + // ------------------------------------------------------------------------- + + class CAction { + public: + CAction(); + virtual ~CAction(); + + virtual CAction* clone() const = 0; + virtual CString format() const = 0; + + virtual void perform(const CEvent&) = 0; + }; + + // CLockCursorToScreenAction + class CLockCursorToScreenAction : public CAction { + public: + enum Mode { kOff, kOn, kToggle }; + + CLockCursorToScreenAction(Mode = kToggle); + + Mode getMode() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + private: + Mode m_mode; + }; + + // CSwitchToScreenAction + class CSwitchToScreenAction : public CAction { + public: + CSwitchToScreenAction(const CString& screen); + + CString getScreen() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + private: + CString m_screen; + }; + + // CSwitchInDirectionAction + class CSwitchInDirectionAction : public CAction { + public: + CSwitchInDirectionAction(EDirection); + + EDirection getDirection() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + private: + EDirection m_direction; + }; + + // CKeystrokeAction + class CKeystrokeAction : public CAction { + public: + CKeystrokeAction(IPlatformScreen::CKeyInfo* adoptedInfo, bool press); + ~CKeystrokeAction(); + + void adoptInfo(IPlatformScreen::CKeyInfo*); + const IPlatformScreen::CKeyInfo* + getInfo() const; + bool isOnPress() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + protected: + virtual const char* formatName() const; + + private: + IPlatformScreen::CKeyInfo* m_keyInfo; + bool m_press; + }; + + // CMouseButtonAction -- modifier combinations not implemented yet + class CMouseButtonAction : public CAction { + public: + CMouseButtonAction(IPlatformScreen::CButtonInfo* adoptedInfo, + bool press); + ~CMouseButtonAction(); + + const IPlatformScreen::CButtonInfo* + getInfo() const; + bool isOnPress() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + protected: + virtual const char* formatName() const; + + private: + IPlatformScreen::CButtonInfo* m_buttonInfo; + bool m_press; + }; + + class CRule { + public: + CRule(); + CRule(CCondition* adopted); + CRule(const CRule&); + ~CRule(); + + CRule& operator=(const CRule&); + + // replace the condition + void setCondition(CCondition* adopted); + + // add an action to the rule + void adoptAction(CAction*, bool onActivation); + + // remove an action from the rule + void removeAction(bool onActivation, UInt32 index); + + // replace an action in the rule + void replaceAction(CAction* adopted, + bool onActivation, UInt32 index); + + // enable/disable + void enable(CPrimaryClient*); + void disable(CPrimaryClient*); + + // event handling + bool handleEvent(const CEvent&); + + // convert rule to a string + CString format() const; + + // get the rule's condition + const CCondition* + getCondition() const; + + // get number of actions + UInt32 getNumActions(bool onActivation) const; + + // get action by index + const CAction& getAction(bool onActivation, UInt32 index) const; + + private: + void clear(); + void copy(const CRule&); + + private: + typedef std::vector CActionList; + + CCondition* m_condition; + CActionList m_activateActions; + CActionList m_deactivateActions; + }; + + // ------------------------------------------------------------------------- + // Input Filter Class + // ------------------------------------------------------------------------- + typedef std::vector CRuleList; + + CInputFilter(); + CInputFilter(const CInputFilter&); + virtual ~CInputFilter(); + + CInputFilter& operator=(const CInputFilter&); + + // add rule, adopting the condition and the actions + void addFilterRule(const CRule& rule); + + // remove a rule + void removeFilterRule(UInt32 index); + + // get rule by index + CRule& getRule(UInt32 index); + + // enable event filtering using the given primary client. disable + // if client is NULL. + void setPrimaryClient(CPrimaryClient* client); + + // convert rules to a string + CString format(const CString& linePrefix) const; + + // get number of rules + UInt32 getNumRules() const; + + //! Compare filters + bool operator==(const CInputFilter&) const; + //! Compare filters + bool operator!=(const CInputFilter&) const; + +private: + // event handling + void handleEvent(const CEvent&, void*); + +private: + CRuleList m_ruleList; + CPrimaryClient* m_primaryClient; +}; + +#endif diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 7ec681bc..03146655 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -22,8 +22,9 @@ // CPrimaryClient::CPrimaryClient(const CString& name, CScreen* screen) : - m_name(name), - m_screen(screen) + CBaseClientProxy(name), + m_screen(screen), + m_fakeInputCount(0) { // all clipboards are clean for (UInt32 i = 0; i < kClipboardEnd; ++i) { @@ -42,6 +43,34 @@ CPrimaryClient::reconfigure(UInt32 activeSides) m_screen->reconfigure(activeSides); } +UInt32 +CPrimaryClient::registerHotKey(KeyID key, KeyModifierMask mask) +{ + return m_screen->registerHotKey(key, mask); +} + +void +CPrimaryClient::unregisterHotKey(UInt32 id) +{ + m_screen->unregisterHotKey(id); +} + +void +CPrimaryClient::fakeInputBegin() +{ + if (++m_fakeInputCount == 1) { + m_screen->fakeInputBegin(); + } +} + +void +CPrimaryClient::fakeInputEnd() +{ + if (--m_fakeInputCount == 0) { + m_screen->fakeInputEnd(); + } +} + SInt32 CPrimaryClient::getJumpZoneSize() const { @@ -57,7 +86,7 @@ CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const KeyModifierMask CPrimaryClient::getToggleMask() const { - return m_screen->getActiveModifiers(); + return m_screen->pollActiveModifiers(); } bool @@ -150,9 +179,15 @@ CPrimaryClient::setClipboardDirty(ClipboardID id, bool dirty) } void -CPrimaryClient::keyDown(KeyID, KeyModifierMask, KeyButton) +CPrimaryClient::keyDown(KeyID key, KeyModifierMask mask, KeyButton button) { - // ignore + if (m_fakeInputCount > 0) { +// XXX -- don't forward keystrokes to primary screen for now + (void)key; + (void)mask; + (void)button; +// m_screen->keyDown(key, mask, button); + } } void @@ -162,9 +197,15 @@ CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton) } void -CPrimaryClient::keyUp(KeyID, KeyModifierMask, KeyButton) +CPrimaryClient::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) { - // ignore + if (m_fakeInputCount > 0) { +// XXX -- don't forward keystrokes to primary screen for now + (void)key; + (void)mask; + (void)button; +// m_screen->keyUp(key, mask, button); + } } void @@ -192,7 +233,7 @@ CPrimaryClient::mouseRelativeMove(SInt32, SInt32) } void -CPrimaryClient::mouseWheel(SInt32) +CPrimaryClient::mouseWheel(SInt32, SInt32) { // ignore } @@ -214,9 +255,3 @@ CPrimaryClient::setOptions(const COptionsList& options) { m_screen->setOptions(options); } - -CString -CPrimaryClient::getName() const -{ - return m_name; -} diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 2006508c..e768a21d 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -15,7 +15,7 @@ #ifndef CPRIMARYCLIENT_H #define CPRIMARYCLIENT_H -#include "IClient.h" +#include "CBaseClientProxy.h" #include "ProtocolTypes.h" class CScreen; @@ -26,7 +26,7 @@ The primary screen does not have a client associated with it. This class provides a pseudo-client to allow the primary screen to be treated as if it was a client. */ -class CPrimaryClient : public IClient { +class CPrimaryClient : public CBaseClientProxy { public: /*! \c name is the name of the server and \p screen is primary screen. @@ -43,6 +43,34 @@ public: */ void reconfigure(UInt32 activeSides); + //! Register a system hotkey + /*! + Registers a system-wide hotkey for key \p key with modifiers \p mask. + Returns an id used to unregister the hotkey. + */ + UInt32 registerHotKey(KeyID key, KeyModifierMask mask); + + //! Unregister a system hotkey + /*! + Unregisters a previously registered hot key. + */ + void unregisterHotKey(UInt32 id); + + //! Prepare to synthesize input on primary screen + /*! + Prepares the primary screen to receive synthesized input. We do not + want to receive this synthesized input as user input so this method + ensures that we ignore it. Calls to \c fakeInputBegin() and + \c fakeInputEnd() may be nested; only the outermost have an effect. + */ + void fakeInputBegin(); + + //! Done synthesizing input on primary screen + /*! + Undoes whatever \c fakeInputBegin() did. + */ + void fakeInputEnd(); + //@} //! @name accessors //@{ @@ -61,7 +89,7 @@ public: the edges of the screen, typically the center. */ void getCursorCenter(SInt32& x, SInt32& y) const; - + //! Get toggle key state /*! Returns the primary screen's current toggle modifier key state. @@ -103,16 +131,15 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel); - virtual void mouseWheel(SInt32 delta); + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta); virtual void screensaver(bool activate); virtual void resetOptions(); virtual void setOptions(const COptionsList& options); - virtual CString getName() const; private: - CString m_name; CScreen* m_screen; bool m_clipboardDirty[kClipboardEnd]; + SInt32 m_fakeInputCount; }; #endif diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index fce2584a..ab7a7ade 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -28,13 +28,18 @@ #include "CLog.h" #include "TMethodEventJob.h" #include "CArch.h" +#include // // CServer // -CEvent::Type CServer::s_errorEvent = CEvent::kUnknown; -CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; +CEvent::Type CServer::s_errorEvent = CEvent::kUnknown; +CEvent::Type CServer::s_connectedEvent = CEvent::kUnknown; +CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; +CEvent::Type CServer::s_switchToScreen = CEvent::kUnknown; +CEvent::Type CServer::s_switchInDirection = CEvent::kUnknown; +CEvent::Type CServer::s_lockCursorToScreen = CEvent::kUnknown; CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_primaryClient(primaryClient), @@ -44,7 +49,8 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_yDelta(0), m_xDelta2(0), m_yDelta2(0), - m_config(config), + m_config(), + m_inputFilter(m_config.getInputFilter()), m_activeSaver(NULL), m_switchDir(kNoDirection), m_switchScreen(NULL), @@ -54,11 +60,12 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_switchTwoTapEngaged(false), m_switchTwoTapArmed(false), m_switchTwoTapZone(3), - m_relativeMoves(false) + m_relativeMoves(false), + m_lockedToScreen(false) { // must have a primary client and it must have a canonical name assert(m_primaryClient != NULL); - assert(m_config.isScreen(primaryClient->getName())); + assert(config.isScreen(primaryClient->getName())); CString primaryName = getName(primaryClient); @@ -79,23 +86,23 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : new TMethodEventJob(this, &CServer::handleSwitchWaitTimeout)); EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyDownEvent(), - m_primaryClient->getEventTarget(), + m_inputFilter, new TMethodEventJob(this, &CServer::handleKeyDownEvent)); EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyUpEvent(), - m_primaryClient->getEventTarget(), + m_inputFilter, new TMethodEventJob(this, &CServer::handleKeyUpEvent)); EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyRepeatEvent(), - m_primaryClient->getEventTarget(), + m_inputFilter, new TMethodEventJob(this, &CServer::handleKeyRepeatEvent)); EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonDownEvent(), - m_primaryClient->getEventTarget(), + m_inputFilter, new TMethodEventJob(this, &CServer::handleButtonDownEvent)); EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonUpEvent(), - m_primaryClient->getEventTarget(), + m_inputFilter, new TMethodEventJob(this, &CServer::handleButtonUpEvent)); EVENTQUEUE->adoptHandler(IPlatformScreen::getMotionOnPrimaryEvent(), @@ -118,32 +125,51 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_primaryClient->getEventTarget(), new TMethodEventJob(this, &CServer::handleScreensaverDeactivatedEvent)); + EVENTQUEUE->adoptHandler(getSwitchToScreenEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleSwitchToScreenEvent)); + EVENTQUEUE->adoptHandler(getSwitchInDirectionEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleSwitchInDirectionEvent)); + EVENTQUEUE->adoptHandler(getLockCursorToScreenEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleLockCursorToScreenEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getFakeInputBeginEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleFakeInputBeginEvent)); + EVENTQUEUE->adoptHandler(IPlatformScreen::getFakeInputEndEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleFakeInputEndEvent)); // add connection addClient(m_primaryClient); - // process options locally - processOptions(); - - // tell primary client about its options - sendOptions(m_primaryClient); + // set initial configuration + setConfig(config); + // enable primary client m_primaryClient->enable(); + m_inputFilter->setPrimaryClient(m_primaryClient); } CServer::~CServer() { // remove event handlers and timers EVENTQUEUE->removeHandler(IPlatformScreen::getKeyDownEvent(), - m_primaryClient->getEventTarget()); + m_inputFilter); EVENTQUEUE->removeHandler(IPlatformScreen::getKeyUpEvent(), - m_primaryClient->getEventTarget()); + m_inputFilter); EVENTQUEUE->removeHandler(IPlatformScreen::getKeyRepeatEvent(), - m_primaryClient->getEventTarget()); + m_inputFilter); EVENTQUEUE->removeHandler(IPlatformScreen::getButtonDownEvent(), - m_primaryClient->getEventTarget()); + m_inputFilter); EVENTQUEUE->removeHandler(IPlatformScreen::getButtonUpEvent(), - m_primaryClient->getEventTarget()); + m_inputFilter); EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnPrimaryEvent(), m_primaryClient->getEventTarget()); EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnSecondaryEvent(), @@ -154,6 +180,10 @@ CServer::~CServer() m_primaryClient->getEventTarget()); EVENTQUEUE->removeHandler(IPlatformScreen::getScreensaverDeactivatedEvent(), m_primaryClient->getEventTarget()); + EVENTQUEUE->removeHandler(IPlatformScreen::getFakeInputBeginEvent(), + m_inputFilter); + EVENTQUEUE->removeHandler(IPlatformScreen::getFakeInputEndEvent(), + m_inputFilter); EVENTQUEUE->removeHandler(CEvent::kTimer, this); stopSwitch(); @@ -161,14 +191,18 @@ CServer::~CServer() disconnect(); for (COldClients::iterator index = m_oldClients.begin(); index != m_oldClients.begin(); ++index) { - IClient* client = index->first; + CBaseClientProxy* client = index->first; EVENTQUEUE->deleteTimer(index->second); EVENTQUEUE->removeHandler(CEvent::kTimer, client); EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); delete client; } - // disconnect primary client + // remove input filter + m_inputFilter->setPrimaryClient(NULL); + + // disable and disconnect primary client + m_primaryClient->disable(); removeClient(m_primaryClient); } @@ -188,13 +222,28 @@ CServer::setConfig(const CConfig& config) m_config = config; processOptions(); + // add ScrollLock as a hotkey to lock to the screen. this was a + // built-in feature in earlier releases and is now supported via + // the user configurable hotkey mechanism. if the user has already + // registered ScrollLock for something else then that will win but + // we will unfortunately generate a warning. if the user has + // configured a CLockCursorToScreenAction then we don't add + // ScrollLock as a hotkey. + if (!m_config.hasLockToScreenAction()) { + IPlatformScreen::CKeyInfo* key = + IPlatformScreen::CKeyInfo::alloc(kKeyScrollLock, 0, 0, 0); + CInputFilter::CRule rule(new CInputFilter::CKeystrokeCondition(key)); + rule.adoptAction(new CInputFilter::CLockCursorToScreenAction, true); + m_inputFilter->addFilterRule(rule); + } + // tell primary screen about reconfiguration m_primaryClient->reconfigure(getActivePrimarySides()); // tell all (connected) clients about current options for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { - IClient* client = index->second; + CBaseClientProxy* client = index->second; sendOptions(client); } @@ -202,7 +251,7 @@ CServer::setConfig(const CConfig& config) } void -CServer::adoptClient(IClient* client) +CServer::adoptClient(CBaseClientProxy* client) { assert(client != NULL); @@ -234,6 +283,12 @@ CServer::adoptClient(IClient* client) if (m_activeSaver != NULL) { client->screensaver(true); } + + // send notification + CServer::CScreenConnectedInfo* info = + CServer::CScreenConnectedInfo::alloc(getName(client)); + EVENTQUEUE->addEvent(CEvent(CServer::getConnectedEvent(), + m_primaryClient->getEventTarget(), info)); } void @@ -272,6 +327,13 @@ CServer::getErrorEvent() "CServer::error"); } +CEvent::Type +CServer::getConnectedEvent() +{ + return CEvent::registerTypeOnce(s_connectedEvent, + "CServer::connected"); +} + CEvent::Type CServer::getDisconnectedEvent() { @@ -279,20 +341,29 @@ CServer::getDisconnectedEvent() "CServer::disconnected"); } -bool -CServer::onCommandKey(KeyID id, KeyModifierMask /*mask*/, bool /*down*/) +CEvent::Type +CServer::getSwitchToScreenEvent() { - if (id == kKeyScrollLock) { - m_primaryClient->reconfigure(getActivePrimarySides()); - if (!isLockedToScreenServer()) { - stopRelativeMoves(); - } - } - return false; + return CEvent::registerTypeOnce(s_switchToScreen, + "CServer::switchToScreen"); +} + +CEvent::Type +CServer::getSwitchInDirectionEvent() +{ + return CEvent::registerTypeOnce(s_switchInDirection, + "CServer::switchInDirection"); +} + +CEvent::Type +CServer::getLockCursorToScreenEvent() +{ + return CEvent::registerTypeOnce(s_lockCursorToScreen, + "CServer::lockCursorToScreen"); } CString -CServer::getName(const IClient* client) const +CServer::getName(const CBaseClientProxy* client) const { CString name = m_config.getCanonicalName(client->getName()); if (name.empty()) { @@ -306,16 +377,16 @@ CServer::getActivePrimarySides() const { UInt32 sides = 0; if (!isLockedToScreenServer()) { - if (getNeighbor(m_primaryClient, kLeft) != NULL) { + if (hasAnyNeighbor(m_primaryClient, kLeft)) { sides |= kLeftMask; } - if (getNeighbor(m_primaryClient, kRight) != NULL) { + if (hasAnyNeighbor(m_primaryClient, kRight)) { sides |= kRightMask; } - if (getNeighbor(m_primaryClient, kTop) != NULL) { + if (hasAnyNeighbor(m_primaryClient, kTop)) { sides |= kTopMask; } - if (getNeighbor(m_primaryClient, kBottom) != NULL) { + if (hasAnyNeighbor(m_primaryClient, kBottom)) { sides |= kBottomMask; } } @@ -326,7 +397,7 @@ bool CServer::isLockedToScreenServer() const { // locked if scroll-lock is toggled on - return ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0); + return m_lockedToScreen; } bool @@ -334,7 +405,7 @@ CServer::isLockedToScreen() const { // locked if we say we're locked if (isLockedToScreenServer()) { - LOG((CLOG_DEBUG "locked by ScrollLock")); + LOG((CLOG_DEBUG "locked to screen")); return true; } @@ -348,7 +419,7 @@ CServer::isLockedToScreen() const } SInt32 -CServer::getJumpZoneSize(IClient* client) const +CServer::getJumpZoneSize(CBaseClientProxy* client) const { if (client == m_primaryClient) { return m_primaryClient->getJumpZoneSize(); @@ -359,7 +430,8 @@ CServer::getJumpZoneSize(IClient* client) const } void -CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) +CServer::switchScreen(CBaseClientProxy* dst, + SInt32 x, SInt32 y, bool forScreensaver) { assert(dst != NULL); #ifndef NDEBUG @@ -428,8 +500,77 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) } } -IClient* -CServer::getNeighbor(IClient* src, EDirection dir) const +void +CServer::jumpToScreen(CBaseClientProxy* newScreen) +{ + assert(newScreen != NULL); + + // record the current cursor position on the active screen + m_active->setJumpCursorPos(m_x, m_y); + + // get the last cursor position on the target screen + SInt32 x, y; + newScreen->getJumpCursorPos(x, y); + + switchScreen(newScreen, x, y, false); +} + +float +CServer::mapToFraction(CBaseClientProxy* client, + EDirection dir, SInt32 x, SInt32 y) const +{ + SInt32 sx, sy, sw, sh; + client->getShape(sx, sy, sw, sh); + switch (dir) { + case kLeft: + case kRight: + return static_cast(y - sy + 0.5f) / static_cast(sh); + + case kTop: + case kBottom: + return static_cast(x - sx + 0.5f) / static_cast(sw); + + case kNoDirection: + assert(0 && "bad direction"); + break; + } + return 0.0f; +} + +void +CServer::mapToPixel(CBaseClientProxy* client, + EDirection dir, float f, SInt32& x, SInt32& y) const +{ + SInt32 sx, sy, sw, sh; + client->getShape(sx, sy, sw, sh); + switch (dir) { + case kLeft: + case kRight: + y = static_cast(f * sh) + sy; + break; + + case kTop: + case kBottom: + x = static_cast(f * sw) + sx; + break; + + case kNoDirection: + assert(0 && "bad direction"); + break; + } +} + +bool +CServer::hasAnyNeighbor(CBaseClientProxy* client, EDirection dir) const +{ + assert(client != NULL); + + return m_config.hasNeighbor(getName(client), dir); +} + +CBaseClientProxy* +CServer::getNeighbor(CBaseClientProxy* src, + EDirection dir, SInt32& x, SInt32& y) const { // note -- must be locked on entry @@ -440,21 +581,19 @@ CServer::getNeighbor(IClient* src, EDirection dir) const assert(!srcName.empty()); LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); - // get first neighbor. if it's the source then the source jumps - // to itself and we return the source. - CString dstName(m_config.getNeighbor(srcName, dir)); - if (dstName == srcName) { - LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); - return src; - } + // convert position to fraction + float t = mapToFraction(src, dir, x, y); - // keep checking + // search for the closest neighbor that exists in direction dir + float tTmp; for (;;) { + CString dstName(m_config.getNeighbor(srcName, dir, t, &tTmp)); + // if nothing in that direction then return NULL. if the // destination is the source then we can make no more // progress in this direction. since we haven't found a // connected neighbor we return NULL. - if (dstName.empty() || dstName == srcName) { + if (dstName.empty()) { LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); return NULL; } @@ -463,7 +602,8 @@ CServer::getNeighbor(IClient* src, EDirection dir) const // ready then we can stop. CClientList::const_iterator index = m_clients.find(dstName); if (index != m_clients.end()) { - LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); + LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\" at %f", dstName.c_str(), CConfig::dirName(dir), srcName.c_str(), t)); + mapToPixel(index->second, dir, tTmp, x, y); return index->second; } @@ -471,13 +611,13 @@ CServer::getNeighbor(IClient* src, EDirection dir) const LOG((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); srcName = dstName; - // look up name of neighbor of skipped screen - dstName = m_config.getNeighbor(srcName, dir); + // use position on skipped screen + t = tTmp; } } -IClient* -CServer::getNeighbor(IClient* src, +CBaseClientProxy* +CServer::mapToNeighbor(CBaseClientProxy* src, EDirection srcSide, SInt32& x, SInt32& y) const { // note -- must be locked on entry @@ -485,16 +625,14 @@ CServer::getNeighbor(IClient* src, assert(src != NULL); // get the first neighbor - IClient* dst = getNeighbor(src, srcSide); + CBaseClientProxy* dst = getNeighbor(src, srcSide, x, y); if (dst == NULL) { return NULL; } - // get the source screen's size (needed for kRight and kBottom) - SInt32 sx, sy, sw, sh; + // get the source screen's size SInt32 dx, dy, dw, dh; - IClient* lastGoodScreen = src; - lastGoodScreen->getShape(sx, sy, sw, sh); + CBaseClientProxy* lastGoodScreen = src; lastGoodScreen->getShape(dx, dy, dw, dh); // find destination screen, adjusting x or y (but not both). the @@ -513,7 +651,7 @@ CServer::getNeighbor(IClient* src, break; } LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); + dst = getNeighbor(lastGoodScreen, srcSide, x, y); } assert(lastGoodScreen != NULL); x += dx; @@ -529,7 +667,7 @@ CServer::getNeighbor(IClient* src, break; } LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); + dst = getNeighbor(lastGoodScreen, srcSide, x, y); } assert(lastGoodScreen != NULL); x += dx; @@ -545,7 +683,7 @@ CServer::getNeighbor(IClient* src, break; } LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); + dst = getNeighbor(lastGoodScreen, srcSide, x, y); } assert(lastGoodScreen != NULL); y += dy; @@ -557,11 +695,11 @@ CServer::getNeighbor(IClient* src, y -= dh; lastGoodScreen = dst; lastGoodScreen->getShape(dx, dy, dw, dh); - if (y < sh) { + if (y < dh) { break; } LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); - dst = getNeighbor(lastGoodScreen, srcSide); + dst = getNeighbor(lastGoodScreen, srcSide, x, y); } assert(lastGoodScreen != NULL); y += dy; @@ -580,88 +718,63 @@ CServer::getNeighbor(IClient* src, // to avoid the jump zone. if entering a side that doesn't have // a neighbor (i.e. an asymmetrical side) then we don't need to // move inwards because that side can't provoke a jump. - if (dst == m_primaryClient) { - const CString dstName(getName(dst)); - switch (srcSide) { - case kLeft: - if (!m_config.getNeighbor(dstName, kRight).empty() && - x > dx + dw - 1 - getJumpZoneSize(dst)) - x = dx + dw - 1 - getJumpZoneSize(dst); - break; - - case kRight: - if (!m_config.getNeighbor(dstName, kLeft).empty() && - x < dx + getJumpZoneSize(dst)) - x = dx + getJumpZoneSize(dst); - break; - - case kTop: - if (!m_config.getNeighbor(dstName, kBottom).empty() && - y > dy + dh - 1 - getJumpZoneSize(dst)) - y = dy + dh - 1 - getJumpZoneSize(dst); - break; - - case kBottom: - if (!m_config.getNeighbor(dstName, kTop).empty() && - y < dy + getJumpZoneSize(dst)) - y = dy + getJumpZoneSize(dst); - break; - - case kNoDirection: - assert(0 && "bad direction"); - return NULL; - } - } - - // adjust the coordinate orthogonal to srcSide to account for - // resolution differences. for example, if y is 200 pixels from - // the top on a screen 1000 pixels high (20% from the top) when - // we cross the left edge onto a screen 600 pixels high then y - // should be set 120 pixels from the top (again 20% from the - // top). - switch (srcSide) { - case kLeft: - case kRight: - y -= sy; - if (y < 0) { - y = 0; - } - else if (y >= sh) { - y = dh - 1; - } - else { - y = static_cast(0.5 + y * - static_cast(dh - 1) / (sh - 1)); - } - y += dy; - break; - - case kTop: - case kBottom: - x -= sx; - if (x < 0) { - x = 0; - } - else if (x >= sw) { - x = dw - 1; - } - else { - x = static_cast(0.5 + x * - static_cast(dw - 1) / (sw - 1)); - } - x += dx; - break; - - case kNoDirection: - assert(0 && "bad direction"); - return NULL; - } + avoidJumpZone(dst, srcSide, x, y); return dst; } +void +CServer::avoidJumpZone(CBaseClientProxy* dst, + EDirection dir, SInt32& x, SInt32& y) const +{ + // we only need to avoid jump zones on the primary screen + if (dst != m_primaryClient) { + return; + } + + const CString dstName(getName(dst)); + SInt32 dx, dy, dw, dh; + dst->getShape(dx, dy, dw, dh); + float t = mapToFraction(dst, dir, x, y); + SInt32 z = getJumpZoneSize(dst); + + // move in far enough to avoid the jump zone. if entering a side + // that doesn't have a neighbor (i.e. an asymmetrical side) then we + // don't need to move inwards because that side can't provoke a jump. + switch (dir) { + case kLeft: + if (!m_config.getNeighbor(dstName, kRight, t, NULL).empty() && + x > dx + dw - 1 - z) + x = dx + dw - 1 - z; + break; + + case kRight: + if (!m_config.getNeighbor(dstName, kLeft, t, NULL).empty() && + x < dx + z) + x = dx + z; + break; + + case kTop: + if (!m_config.getNeighbor(dstName, kBottom, t, NULL).empty() && + y > dy + dh - 1 - z) + y = dy + dh - 1 - z; + break; + + case kBottom: + if (!m_config.getNeighbor(dstName, kTop, t, NULL).empty() && + y < dy + z) + y = dy + z; + break; + + case kNoDirection: + assert(0 && "bad direction"); + } +} + bool -CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) +CServer::isSwitchOkay(CBaseClientProxy* newScreen, + EDirection dir, SInt32 x, SInt32 y, + SInt32 xActive, SInt32 yActive) { LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", getName(m_active).c_str(), CConfig::dirName(dir))); @@ -709,6 +822,33 @@ CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y) preventSwitch = true; } + // are we in a locked corner? first check if screen has the option set + // and, if not, check the global options. + const CConfig::CScreenOptions* options = + m_config.getOptions(getName(m_active)); + if (options == NULL || options->count(kOptionScreenSwitchCorners) == 0) { + options = m_config.getOptions(""); + } + if (options != NULL && options->count(kOptionScreenSwitchCorners) > 0) { + // get corner mask and size + CConfig::CScreenOptions::const_iterator i = + options->find(kOptionScreenSwitchCorners); + UInt32 corners = static_cast(i->second); + i = options->find(kOptionScreenSwitchCornerSize); + SInt32 size = 0; + if (i != options->end()) { + size = i->second; + } + + // see if we're in a locked corner + if ((getCorner(m_active, xActive, yActive, size) & corners) != 0) { + // yep, no switching + LOG((CLOG_DEBUG1 "locked in corner")); + preventSwitch = true; + stopSwitch(); + } + } + // ignore if mouse is locked to screen and don't try to switch later if (!preventSwitch && isLockedToScreen()) { LOG((CLOG_DEBUG1 "locked to screen")); @@ -840,6 +980,63 @@ CServer::isSwitchWaitStarted() const return (m_switchWaitTimer != NULL); } +UInt32 +CServer::getCorner(CBaseClientProxy* client, + SInt32 x, SInt32 y, SInt32 size) const +{ + assert(client != NULL); + + // get client screen shape + SInt32 ax, ay, aw, ah; + client->getShape(ax, ay, aw, ah); + + // check for x,y on the left or right + SInt32 xSide; + if (x <= ax) { + xSide = -1; + } + else if (x >= ax + aw - 1) { + xSide = 1; + } + else { + xSide = 0; + } + + // check for x,y on the top or bottom + SInt32 ySide; + if (y <= ay) { + ySide = -1; + } + else if (y >= ay + ah - 1) { + ySide = 1; + } + else { + ySide = 0; + } + + // if against the left or right then check if y is within size + if (xSide != 0) { + if (y < ay + size) { + return (xSide < 0) ? kTopLeftMask : kTopRightMask; + } + else if (y >= ay + ah - size) { + return (xSide < 0) ? kBottomLeftMask : kBottomRightMask; + } + } + + // if against the left or right then check if y is within size + if (ySide != 0) { + if (x < ax + size) { + return (ySide < 0) ? kTopLeftMask : kBottomLeftMask; + } + else if (x >= ax + aw - size) { + return (ySide < 0) ? kTopRightMask : kBottomRightMask; + } + } + + return kNoCornerMask; +} + void CServer::stopRelativeMoves() { @@ -859,7 +1056,7 @@ CServer::stopRelativeMoves() } void -CServer::sendOptions(IClient* client) const +CServer::sendOptions(CBaseClientProxy* client) const { COptionsList optionsList; @@ -889,6 +1086,7 @@ CServer::sendOptions(IClient* client) const } // send the options + client->resetOptions(); client->setOptions(optionsList); } @@ -934,16 +1132,23 @@ void CServer::handleShapeChanged(const CEvent&, void* vclient) { // ignore events from unknown clients - IClient* client = reinterpret_cast(vclient); + CBaseClientProxy* client = reinterpret_cast(vclient); if (m_clientSet.count(client) == 0) { return; } + LOG((CLOG_INFO "screen \"%s\" shape changed", getName(client).c_str())); + + // update jump coordinate + SInt32 x, y; + client->getCursorPos(x, y); + client->setJumpCursorPos(x, y); + // update the mouse coordinates if (client == m_active) { - client->getCursorPos(m_x, m_y); + m_x = x; + m_y = y; } - LOG((CLOG_INFO "screen \"%s\" shape changed", getName(client).c_str())); // handle resolution change to primary screen if (client == m_primaryClient) { @@ -960,7 +1165,7 @@ void CServer::handleClipboardGrabbed(const CEvent& event, void* vclient) { // ignore events from unknown clients - IClient* grabber = reinterpret_cast(vclient); + CBaseClientProxy* grabber = reinterpret_cast(vclient); if (m_clientSet.count(grabber) == 0) { return; } @@ -992,7 +1197,7 @@ CServer::handleClipboardGrabbed(const CEvent& event, void* vclient) // grabber that it's clipboard isn't dirty. for (CClientList::iterator index = m_clients.begin(); index != m_clients.end(); ++index) { - IClient* client = index->second; + CBaseClientProxy* client = index->second; if (client == grabber) { client->setClipboardDirty(info->m_id, false); } @@ -1006,7 +1211,7 @@ void CServer::handleClipboardChanged(const CEvent& event, void* vclient) { // ignore events from unknown clients - IClient* sender = reinterpret_cast(vclient); + CBaseClientProxy* sender = reinterpret_cast(vclient); if (m_clientSet.count(sender) == 0) { return; } @@ -1020,7 +1225,7 @@ CServer::handleKeyDownEvent(const CEvent& event, void*) { IPlatformScreen::CKeyInfo* info = reinterpret_cast(event.getData()); - onKeyDown(info->m_key, info->m_mask, info->m_button); + onKeyDown(info->m_key, info->m_mask, info->m_button, info->m_screens); } void @@ -1028,7 +1233,7 @@ CServer::handleKeyUpEvent(const CEvent& event, void*) { IPlatformScreen::CKeyInfo* info = reinterpret_cast(event.getData()); - onKeyUp(info->m_key, info->m_mask, info->m_button); + onKeyUp(info->m_key, info->m_mask, info->m_button, info->m_screens); } void @@ -1076,7 +1281,7 @@ CServer::handleWheelEvent(const CEvent& event, void*) { IPlatformScreen::CWheelInfo* info = reinterpret_cast(event.getData()); - onMouseWheel(info->m_wheel); + onMouseWheel(info->m_xDelta, info->m_yDelta); } void @@ -1110,7 +1315,7 @@ CServer::handleClientDisconnected(const CEvent&, void* vclient) { // client has disconnected. it might be an old client or an // active client. we don't care so just handle it both ways. - IClient* client = reinterpret_cast(vclient); + CBaseClientProxy* client = reinterpret_cast(vclient); removeActiveClient(client); removeOldClient(client); delete client; @@ -1120,20 +1325,100 @@ void CServer::handleClientCloseTimeout(const CEvent&, void* vclient) { // client took too long to disconnect. just dump it. - IClient* client = reinterpret_cast(vclient); + CBaseClientProxy* client = reinterpret_cast(vclient); LOG((CLOG_NOTE "forced disconnection of client \"%s\"", getName(client).c_str())); removeOldClient(client); delete client; } void -CServer::onClipboardChanged(IClient* sender, ClipboardID id, UInt32 seqNum) +CServer::handleSwitchToScreenEvent(const CEvent& event, void*) +{ + CSwitchToScreenInfo* info = + reinterpret_cast(event.getData()); + + CClientList::const_iterator index = m_clients.find(info->m_screen); + if (index == m_clients.end()) { + LOG((CLOG_DEBUG1 "screen \"%s\" not active", info->m_screen)); + } + else { + jumpToScreen(index->second); + } +} + +void +CServer::handleSwitchInDirectionEvent(const CEvent& event, void*) +{ + CSwitchInDirectionInfo* info = + reinterpret_cast(event.getData()); + + // jump to screen in chosen direction from center of this screen + SInt32 x = m_x, y = m_y; + CBaseClientProxy* newScreen = + getNeighbor(m_active, info->m_direction, x, y); + if (newScreen == NULL) { + LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(info->m_direction))); + } + else { + jumpToScreen(newScreen); + } +} + +void +CServer::handleLockCursorToScreenEvent(const CEvent& event, void*) +{ + CLockCursorToScreenInfo* info = (CLockCursorToScreenInfo*)event.getData(); + + // choose new state + bool newState; + switch (info->m_state) { + case CLockCursorToScreenInfo::kOff: + newState = false; + break; + + default: + case CLockCursorToScreenInfo::kOn: + newState = true; + break; + + case CLockCursorToScreenInfo::kToggle: + newState = !m_lockedToScreen; + break; + } + + // enter new state + if (newState != m_lockedToScreen) { + m_lockedToScreen = newState; + LOG((CLOG_DEBUG "cursor %s current screen", m_lockedToScreen ? "locked to" : "unlocked from")); + + m_primaryClient->reconfigure(getActivePrimarySides()); + if (!isLockedToScreenServer()) { + stopRelativeMoves(); + } + } +} + +void +CServer::handleFakeInputBeginEvent(const CEvent&, void*) +{ + m_primaryClient->fakeInputBegin(); +} + +void +CServer::handleFakeInputEndEvent(const CEvent&, void*) +{ + m_primaryClient->fakeInputEnd(); +} + +void +CServer::onClipboardChanged(CBaseClientProxy* sender, + ClipboardID id, UInt32 seqNum) { CClipboardInfo& clipboard = m_clipboards[id]; // ignore update if sequence number is old if (seqNum < clipboard.m_clipboardSeqNum) { - LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", clipboard.m_clipboardOwner.c_str(), id)); + LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", getName(sender).c_str(), id)); return; } @@ -1157,7 +1442,7 @@ CServer::onClipboardChanged(IClient* sender, ClipboardID id, UInt32 seqNum) // tell all clients except the sender that the clipboard is dirty for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { - IClient* client = index->second; + CBaseClientProxy* client = index->second; client->setClipboardDirty(id, client != sender); } @@ -1187,7 +1472,7 @@ CServer::onScreensaver(bool activated) // changed resolutions while the screen saver was running. if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { // check position - IClient* screen = m_activeSaver; + CBaseClientProxy* screen = m_activeSaver; SInt32 x, y, w, h; screen->getShape(x, y, w, h); SInt32 zoneSize = getJumpZoneSize(screen); @@ -1215,39 +1500,51 @@ CServer::onScreensaver(bool activated) // send message to all clients for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { - IClient* client = index->second; + CBaseClientProxy* client = index->second; client->screensaver(activated); } } void -CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) +CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button, + const char* screens) { LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); assert(m_active != NULL); - // handle command keys - if (onCommandKey(id, mask, true)) { - return; - } - // relay - m_active->keyDown(id, mask, button); + if (IKeyState::CKeyInfo::isDefault(screens)) { + m_active->keyDown(id, mask, button); + } + else { + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + if (IKeyState::CKeyInfo::contains(screens, index->first)) { + index->second->keyDown(id, mask, button); + } + } + } } void -CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button) +CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button, + const char* screens) { LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); assert(m_active != NULL); - // handle command keys - if (onCommandKey(id, mask, false)) { - return; - } - // relay - m_active->keyUp(id, mask, button); + if (IKeyState::CKeyInfo::isDefault(screens)) { + m_active->keyUp(id, mask, button); + } + else { + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + if (IKeyState::CKeyInfo::contains(screens, index->first)) { + index->second->keyUp(id, mask, button); + } + } + } } void @@ -1257,12 +1554,6 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); assert(m_active != NULL); - // handle command keys - if (onCommandKey(id, mask, false)) { - onCommandKey(id, mask, true); - return; - } - // relay m_active->keyRepeat(id, mask, count, button); } @@ -1315,6 +1606,21 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y) m_active->getShape(ax, ay, aw, ah); SInt32 zoneSize = getJumpZoneSize(m_active); + // clamp position to screen + SInt32 xc = x, yc = y; + if (xc < ax + zoneSize) { + xc = ax; + } + else if (xc >= ax + aw - zoneSize) { + xc = ax + aw - 1; + } + if (yc < ay + zoneSize) { + yc = ay; + } + else if (yc >= ay + ah - zoneSize) { + yc = ay + ah - 1; + } + // see if we should change screens EDirection dir; if (x < ax + zoneSize) { @@ -1340,10 +1646,10 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y) } // get jump destination - IClient* newScreen = getNeighbor(m_active, dir, x, y); + CBaseClientProxy* newScreen = mapToNeighbor(m_active, dir, x, y); // should we switch or not? - if (isSwitchOkay(newScreen, dir, x, y)) { + if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) { // switch screen switchScreen(newScreen, x, y, false); return true; @@ -1399,8 +1705,23 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) // find direction of neighbor and get the neighbor bool jump = true; - IClient* newScreen; + CBaseClientProxy* newScreen; do { + // clamp position to screen + SInt32 xc = m_x, yc = m_y; + if (xc < ax) { + xc = ax; + } + else if (xc >= ax + aw) { + xc = ax + aw - 1; + } + if (yc < ay) { + yc = ay; + } + else if (yc >= ay + ah) { + yc = ay + ah - 1; + } + EDirection dir; if (m_x < ax) { dir = kLeft; @@ -1457,10 +1778,10 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) } // try to switch screen. get the neighbor. - newScreen = getNeighbor(m_active, dir, m_x, m_y); + newScreen = mapToNeighbor(m_active, dir, m_x, m_y); // see if we should switch - if (!isSwitchOkay(newScreen, dir, m_x, m_y)) { + if (!isSwitchOkay(newScreen, dir, m_x, m_y, xc, yc)) { newScreen = m_active; jump = false; } @@ -1500,17 +1821,17 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) } void -CServer::onMouseWheel(SInt32 delta) +CServer::onMouseWheel(SInt32 xDelta, SInt32 yDelta) { - LOG((CLOG_DEBUG1 "onMouseWheel %+d", delta)); + LOG((CLOG_DEBUG1 "onMouseWheel %+d,%+d", xDelta, yDelta)); assert(m_active != NULL); // relay - m_active->mouseWheel(delta); + m_active->mouseWheel(xDelta, yDelta); } bool -CServer::addClient(IClient* client) +CServer::addClient(CBaseClientProxy* client) { CString name = getName(client); if (m_clients.count(name) != 0) { @@ -1535,6 +1856,11 @@ CServer::addClient(IClient* client) m_clientSet.insert(client); m_clients.insert(std::make_pair(name, client)); + // initialize client data + SInt32 x, y; + client->getCursorPos(x, y); + client->setJumpCursorPos(x, y); + // tell primary client about the active sides m_primaryClient->reconfigure(getActivePrimarySides()); @@ -1542,7 +1868,7 @@ CServer::addClient(IClient* client) } bool -CServer::removeClient(IClient* client) +CServer::removeClient(CBaseClientProxy* client) { // return false if not in list CClientSet::iterator i = m_clientSet.find(client); @@ -1566,7 +1892,7 @@ CServer::removeClient(IClient* client) } void -CServer::closeClient(IClient* client, const char* msg) +CServer::closeClient(CBaseClientProxy* client, const char* msg) { assert(client != m_primaryClient); assert(msg != NULL); @@ -1606,7 +1932,7 @@ CServer::closeClients(const CConfig& config) { // collect the clients that are connected but are being dropped // from the configuration (or who's canonical name is changing). - typedef std::set CRemovedClients; + typedef std::set CRemovedClients; CRemovedClients removed; for (CClientList::iterator index = m_clients.begin(); index != m_clients.end(); ++index) { @@ -1627,7 +1953,7 @@ CServer::closeClients(const CConfig& config) } void -CServer::removeActiveClient(IClient* client) +CServer::removeActiveClient(CBaseClientProxy* client) { if (removeClient(client)) { forceLeaveClient(client); @@ -1639,7 +1965,7 @@ CServer::removeActiveClient(IClient* client) } void -CServer::removeOldClient(IClient* client) +CServer::removeOldClient(CBaseClientProxy* client) { COldClients::iterator i = m_oldClients.find(client); if (i != m_oldClients.end()) { @@ -1654,9 +1980,10 @@ CServer::removeOldClient(IClient* client) } void -CServer::forceLeaveClient(IClient* client) +CServer::forceLeaveClient(CBaseClientProxy* client) { - IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; + CBaseClientProxy* active = + (m_activeSaver != NULL) ? m_activeSaver : m_active; if (active == client) { // record new position (center of primary screen) m_primaryClient->getCursorCenter(m_x, m_y); @@ -1705,3 +2032,61 @@ CServer::CClipboardInfo::CClipboardInfo() : { // do nothing } + + +// +// CServer::CLockCursorToScreenInfo +// + +CServer::CLockCursorToScreenInfo* +CServer::CLockCursorToScreenInfo::alloc(State state) +{ + CLockCursorToScreenInfo* info = + (CLockCursorToScreenInfo*)malloc(sizeof(CLockCursorToScreenInfo)); + info->m_state = state; + return info; +} + + +// +// CServer::CSwitchToScreenInfo +// + +CServer::CSwitchToScreenInfo* +CServer::CSwitchToScreenInfo::alloc(const CString& screen) +{ + CSwitchToScreenInfo* info = + (CSwitchToScreenInfo*)malloc(sizeof(CSwitchToScreenInfo) + + screen.size()); + strcpy(info->m_screen, screen.c_str()); + return info; +} + + +// +// CServer::CSwitchInDirectionInfo +// + +CServer::CSwitchInDirectionInfo* +CServer::CSwitchInDirectionInfo::alloc(EDirection direction) +{ + CSwitchInDirectionInfo* info = + (CSwitchInDirectionInfo*)malloc(sizeof(CSwitchInDirectionInfo)); + info->m_direction = direction; + return info; +} + + +// +// CServer::CScreenConnectedInfo +// + +CServer::CScreenConnectedInfo* +CServer::CScreenConnectedInfo::alloc(const CString& screen) +{ + CScreenConnectedInfo* info = + (CScreenConnectedInfo*)malloc(sizeof(CScreenConnectedInfo) + + screen.size()); + strcpy(info->m_screen, screen.c_str()); + return info; +} diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 3272d837..c05d138d 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -26,9 +26,10 @@ #include "stdset.h" #include "stdvector.h" +class CBaseClientProxy; class CEventQueueTimer; class CPrimaryClient; -class IClient; +class CInputFilter; //! Synergy server /*! @@ -36,6 +37,46 @@ This class implements the top-level server algorithms for synergy. */ class CServer { public: + //! Lock cursor to screen data + class CLockCursorToScreenInfo { + public: + enum State { kOff, kOn, kToggle }; + + static CLockCursorToScreenInfo* alloc(State state = kToggle); + + public: + State m_state; + }; + + //! Switch to screen data + class CSwitchToScreenInfo { + public: + static CSwitchToScreenInfo* alloc(const CString& screen); + + public: + // this is a C-string; this type is a variable size structure + char m_screen[1]; + }; + + //! Switch in direction data + class CSwitchInDirectionInfo { + public: + static CSwitchInDirectionInfo* alloc(EDirection direction); + + public: + EDirection m_direction; + }; + + //! Screen connected data + class CScreenConnectedInfo { + public: + static CScreenConnectedInfo* alloc(const CString& screen); + + public: + // this is a C-string; this type is a variable size structure + char m_screen[1]; + }; + /*! Start the server with the configuration \p config and the primary client (local screen) \p primaryClient. The client retains @@ -60,7 +101,7 @@ public: Adds \p client to the server. The client is adopted and will be destroyed when the client disconnects or is disconnected. */ - void adoptClient(IClient* client); + void adoptClient(CBaseClientProxy* client); //! Disconnect clients /*! @@ -94,6 +135,14 @@ public: */ static CEvent::Type getErrorEvent(); + //! Get connected event type + /*! + Returns the connected event type. This is sent when a client screen + has connected. The event data is a \c CScreenConnectedInfo* that + indicates the connected screen. + */ + static CEvent::Type getConnectedEvent(); + //! Get disconnected event type /*! Returns the disconnected event type. This is sent when all the @@ -101,18 +150,35 @@ public: */ static CEvent::Type getDisconnectedEvent(); - //@} - -protected: - //! Handle special keys + //! Get switch to screen event type /*! - Handles keys with special meaning. + Returns the switch to screen event type. The server responds to this + by switching screens. The event data is a \c CSwitchToScreenInfo* + that indicates the target screen. */ - bool onCommandKey(KeyID, KeyModifierMask, bool down); + static CEvent::Type getSwitchToScreenEvent(); + + //! Get switch in direction event type + /*! + Returns the switch in direction event type. The server responds to this + by switching screens. The event data is a \c CSwitchInDirectionInfo* + that indicates the target direction. + */ + static CEvent::Type getSwitchInDirectionEvent(); + + //! Get lock cursor event type + /*! + Returns the lock cursor event type. The server responds to this + by locking the cursor to the active screen or unlocking it. The + event data is a \c CLockCursorToScreenInfo*. + */ + static CEvent::Type getLockCursorToScreenEvent(); + + //@} private: // get canonical name of client - CString getName(const IClient*) const; + CString getName(const CBaseClientProxy*) const; // get the sides of the primary screen that have neighbors UInt32 getActivePrimarySides() const; @@ -127,28 +193,52 @@ private: bool isLockedToScreen() const; // returns the jump zone of the client - SInt32 getJumpZoneSize(IClient*) const; + SInt32 getJumpZoneSize(CBaseClientProxy*) const; // change the active screen - void switchScreen(IClient*, + void switchScreen(CBaseClientProxy*, SInt32 x, SInt32 y, bool forScreenSaver); - // lookup neighboring screen - IClient* getNeighbor(IClient*, EDirection) const; + // jump to screen + void jumpToScreen(CBaseClientProxy*); + + // convert pixel position to fraction, using x or y depending on the + // direction. + float mapToFraction(CBaseClientProxy*, EDirection, + SInt32 x, SInt32 y) const; + + // convert fraction to pixel position, writing only x or y depending + // on the direction. + void mapToPixel(CBaseClientProxy*, EDirection, float f, + SInt32& x, SInt32& y) const; + + // returns true if the client has a neighbor anywhere along the edge + // indicated by the direction. + bool hasAnyNeighbor(CBaseClientProxy*, EDirection) const; + + // lookup neighboring screen, mapping the coordinate independent of + // the direction to the neighbor's coordinate space. + CBaseClientProxy* getNeighbor(CBaseClientProxy*, EDirection, + SInt32& x, SInt32& y) const; // lookup neighboring screen. given a position relative to the // source screen, find the screen we should move onto and where. // if the position is sufficiently far from the source then we // cross multiple screens. if there is no suitable screen then // return NULL and x,y are not modified. - IClient* getNeighbor(IClient*, EDirection, + CBaseClientProxy* mapToNeighbor(CBaseClientProxy*, EDirection, + SInt32& x, SInt32& y) const; + + // adjusts x and y or neither to avoid ending up in a jump zone + // after entering the client in the given direction. + void avoidJumpZone(CBaseClientProxy*, EDirection, SInt32& x, SInt32& y) const; // test if a switch is permitted. this includes testing user // options like switch delay and tracking any state required to // implement them. returns true iff a switch is permitted. - bool isSwitchOkay(IClient* dst, EDirection, - SInt32 x, SInt32 y); + bool isSwitchOkay(CBaseClientProxy* dst, EDirection, + SInt32 x, SInt32 y, SInt32 xActive, SInt32 yActive); // update switch state due to a mouse move at \p x, \p y that // doesn't switch screens. @@ -181,11 +271,16 @@ private: // returns true iff the delay switch timer is started bool isSwitchWaitStarted() const; + // returns the corner (EScreenSwitchCornerMasks) where x,y is on the + // given client. corners have the given size. + UInt32 getCorner(CBaseClientProxy*, + SInt32 x, SInt32 y, SInt32 size) const; + // stop relative mouse moves void stopRelativeMoves(); // send screen options to \c client - void sendOptions(IClient* client) const; + void sendOptions(CBaseClientProxy* client) const; // process options from configuration void processOptions(); @@ -207,28 +302,35 @@ private: void handleSwitchWaitTimeout(const CEvent&, void*); void handleClientDisconnected(const CEvent&, void*); void handleClientCloseTimeout(const CEvent&, void*); + void handleSwitchToScreenEvent(const CEvent&, void*); + void handleSwitchInDirectionEvent(const CEvent&, void*); + void handleLockCursorToScreenEvent(const CEvent&, void*); + void handleFakeInputBeginEvent(const CEvent&, void*); + void handleFakeInputEndEvent(const CEvent&, void*); // event processing - void onClipboardChanged(IClient* sender, + void onClipboardChanged(CBaseClientProxy* sender, ClipboardID id, UInt32 seqNum); void onScreensaver(bool activated); - void onKeyDown(KeyID, KeyModifierMask, KeyButton); - void onKeyUp(KeyID, KeyModifierMask, KeyButton); + void onKeyDown(KeyID, KeyModifierMask, KeyButton, + const char* screens); + void onKeyUp(KeyID, KeyModifierMask, KeyButton, + const char* screens); void onKeyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton); void onMouseDown(ButtonID); void onMouseUp(ButtonID); bool onMouseMovePrimary(SInt32 x, SInt32 y); void onMouseMoveSecondary(SInt32 dx, SInt32 dy); - void onMouseWheel(SInt32 delta); + void onMouseWheel(SInt32 xDelta, SInt32 yDelta); // add client to list and attach event handlers for client - bool addClient(IClient*); + bool addClient(CBaseClientProxy*); // remove client from list and detach event handlers for client - bool removeClient(IClient*); + bool removeClient(CBaseClientProxy*); // close a client - void closeClient(IClient*, const char* msg); + void closeClient(CBaseClientProxy*, const char* msg); // close clients not in \p config void closeClients(const CConfig& config); @@ -238,11 +340,11 @@ private: void closeAllClients(); // remove clients from internal state - void removeActiveClient(IClient*); - void removeOldClient(IClient*); + void removeActiveClient(CBaseClientProxy*); + void removeOldClient(CBaseClientProxy*); // force the cursor off of \p client - void forceLeaveClient(IClient* client); + void forceLeaveClient(CBaseClientProxy* client); private: class CClipboardInfo { @@ -260,17 +362,17 @@ private: CPrimaryClient* m_primaryClient; // all clients (including the primary client) indexed by name - typedef std::map CClientList; - typedef std::set CClientSet; + typedef std::map CClientList; + typedef std::set CClientSet; CClientList m_clients; CClientSet m_clientSet; // all old connections that we're waiting to hangup - typedef std::map COldClients; + typedef std::map COldClients; COldClients m_oldClients; // the client with focus - IClient* m_active; + CBaseClientProxy* m_active; // the sequence number of enter messages UInt32 m_seqNum; @@ -289,17 +391,20 @@ private: // current configuration CConfig m_config; + // input filter (from m_config); + CInputFilter* m_inputFilter; + // clipboard cache CClipboardInfo m_clipboards[kClipboardEnd]; // state saved when screen saver activates - IClient* m_activeSaver; + CBaseClientProxy* m_activeSaver; SInt32 m_xSaver, m_ySaver; // common state for screen switch tests. all tests are always // trying to reach the same screen in the same direction. EDirection m_switchDir; - IClient* m_switchScreen; + CBaseClientProxy* m_switchScreen; // state for delayed screen switching double m_switchWaitDelay; @@ -316,8 +421,15 @@ private: // relative mouse move option bool m_relativeMoves; + // screen locking (former scroll lock) + bool m_lockedToScreen; + static CEvent::Type s_errorEvent; + static CEvent::Type s_connectedEvent; static CEvent::Type s_disconnectedEvent; + static CEvent::Type s_switchToScreen; + static CEvent::Type s_switchInDirection; + static CEvent::Type s_lockCursorToScreen; }; #endif diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index 90f3d6e1..ec56c582 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -23,22 +23,28 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libserver.a libserver_a_SOURCES = \ + CBaseClientProxy.cpp \ CClientListener.cpp \ CClientProxy.cpp \ CClientProxy1_0.cpp \ CClientProxy1_1.cpp \ CClientProxy1_2.cpp \ + CClientProxy1_3.cpp \ CClientProxyUnknown.cpp \ CConfig.cpp \ + CInputFilter.cpp \ CPrimaryClient.cpp \ CServer.cpp \ + CBaseClientProxy.h \ CClientListener.h \ CClientProxy.h \ CClientProxy1_0.h \ CClientProxy1_1.h \ CClientProxy1_2.h \ + CClientProxy1_3.h \ CClientProxyUnknown.h \ CConfig.h \ + CInputFilter.h \ CPrimaryClient.h \ CServer.h \ $(NULL) diff --git a/lib/server/server.dsp b/lib/server/server.dsp index 992fd7c3..72268967 100644 --- a/lib/server/server.dsp +++ b/lib/server/server.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\server.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\server.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\server.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\server.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -87,6 +87,10 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\CBaseClientProxy.cpp +# End Source File +# Begin Source File + SOURCE=.\CClientListener.cpp # End Source File # Begin Source File @@ -107,6 +111,10 @@ SOURCE=.\CClientProxy1_2.cpp # End Source File # Begin Source File +SOURCE=.\CClientProxy1_3.cpp +# End Source File +# Begin Source File + SOURCE=.\CClientProxyUnknown.cpp # End Source File # Begin Source File @@ -115,6 +123,10 @@ SOURCE=.\CConfig.cpp # End Source File # Begin Source File +SOURCE=.\CInputFilter.cpp +# End Source File +# Begin Source File + SOURCE=.\CPrimaryClient.cpp # End Source File # Begin Source File @@ -127,6 +139,10 @@ SOURCE=.\CServer.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE=.\CBaseClientProxy.h +# End Source File +# Begin Source File + SOURCE=.\CClientListener.h # End Source File # Begin Source File @@ -147,6 +163,10 @@ SOURCE=.\CClientProxy1_2.h # End Source File # Begin Source File +SOURCE=.\CClientProxy1_3.h +# End Source File +# Begin Source File + SOURCE=.\CClientProxyUnknown.h # End Source File # Begin Source File @@ -155,6 +175,10 @@ SOURCE=.\CConfig.h # End Source File # Begin Source File +SOURCE=.\CInputFilter.h +# End Source File +# Begin Source File + SOURCE=.\CPrimaryClient.h # End Source File # Begin Source File diff --git a/lib/synergy/CKeyMap.cpp b/lib/synergy/CKeyMap.cpp new file mode 100644 index 00000000..4141c8b1 --- /dev/null +++ b/lib/synergy/CKeyMap.cpp @@ -0,0 +1,1330 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2005 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CKeyMap.h" +#include "KeyTypes.h" +#include "CLog.h" +#include +#include +#include + +CKeyMap::CNameToKeyMap* CKeyMap::s_nameToKeyMap = NULL; +CKeyMap::CNameToModifierMap* CKeyMap::s_nameToModifierMap = NULL; +CKeyMap::CKeyToNameMap* CKeyMap::s_keyToNameMap = NULL; +CKeyMap::CModifierToNameMap* CKeyMap::s_modifierToNameMap = NULL; + +CKeyMap::CKeyMap() : + m_numGroups(0), + m_composeAcrossGroups(false) +{ + m_modifierKeyItem.m_id = kKeyNone; + m_modifierKeyItem.m_group = 0; + m_modifierKeyItem.m_button = 0; + m_modifierKeyItem.m_required = 0; + m_modifierKeyItem.m_sensitive = 0; + m_modifierKeyItem.m_generates = 0; + m_modifierKeyItem.m_dead = false; + m_modifierKeyItem.m_lock = false; + m_modifierKeyItem.m_client = 0; +} + +CKeyMap::~CKeyMap() +{ + // do nothing +} + +void +CKeyMap::swap(CKeyMap& x) +{ + m_keyIDMap.swap(x.m_keyIDMap); + m_modifierKeys.swap(x.m_modifierKeys); + m_halfDuplex.swap(x.m_halfDuplex); + m_halfDuplexMods.swap(x.m_halfDuplexMods); + SInt32 tmp1 = m_numGroups; + m_numGroups = x.m_numGroups; + x.m_numGroups = tmp1; + bool tmp2 = m_composeAcrossGroups; + m_composeAcrossGroups = x.m_composeAcrossGroups; + x.m_composeAcrossGroups = tmp2; +} + +void +CKeyMap::addKeyEntry(const KeyItem& item) +{ + // ignore kKeyNone + if (item.m_id == kKeyNone) { + return; + } + + // resize number of groups for key + SInt32 numGroups = item.m_group + 1; + if (getNumGroups() > numGroups) { + numGroups = getNumGroups(); + } + KeyGroupTable& groupTable = m_keyIDMap[item.m_id]; + if (groupTable.size() < static_cast(numGroups)) { + groupTable.resize(numGroups); + } + + // make a list from the item + KeyItemList items; + items.push_back(item); + + // set group and dead key flag on the item + KeyItem& newItem = items.back(); + newItem.m_dead = isDeadKey(item.m_id); + + // mask the required bits with the sensitive bits + newItem.m_required &= newItem.m_sensitive; + + // see if we already have this item; just return if so + KeyEntryList& entries = groupTable[item.m_group]; + for (size_t i = 0, n = entries.size(); i < n; ++i) { + if (entries[i].size() == 1 && newItem == entries[i][0]) { + return; + } + } + + // add item list + entries.push_back(items); + LOG((CLOG_DEBUG1 "add key: %04x %d %03x %04x (%04x %04x %04x)%s", newItem.m_id, newItem.m_group, newItem.m_button, newItem.m_client, newItem.m_required, newItem.m_sensitive, newItem.m_generates, newItem.m_dead ? " dead" : "")); +} + +void +CKeyMap::addKeyAliasEntry(KeyID targetID, SInt32 group, + KeyModifierMask targetRequired, + KeyModifierMask targetSensitive, + KeyID sourceID, + KeyModifierMask sourceRequired, + KeyModifierMask sourceSensitive) +{ + // if we can already generate the target as desired then we're done. + if (findCompatibleKey(targetID, group, targetRequired, + targetSensitive) != NULL) { + return; + } + + // find a compatible source, preferably in the same group + for (SInt32 gd = 0, n = getNumGroups(); gd < n; ++gd) { + SInt32 eg = getEffectiveGroup(group, gd); + const KeyItemList* sourceEntry = + findCompatibleKey(sourceID, eg, + sourceRequired, sourceSensitive); + if (sourceEntry != NULL && sourceEntry->size() == 1) { + CKeyMap::KeyItem targetItem = sourceEntry->back(); + targetItem.m_id = targetID; + targetItem.m_group = eg; + addKeyEntry(targetItem); + break; + } + } +} + +bool +CKeyMap::addKeyCombinationEntry(KeyID id, SInt32 group, + const KeyID* keys, UInt32 numKeys) +{ + // disallow kKeyNone + if (id == kKeyNone) { + return false; + } + + SInt32 numGroups = group + 1; + if (getNumGroups() > numGroups) { + numGroups = getNumGroups(); + } + KeyGroupTable& groupTable = m_keyIDMap[id]; + if (groupTable.size() < static_cast(numGroups)) { + groupTable.resize(numGroups); + } + if (!groupTable[group].empty()) { + // key is already in the table + return false; + } + + // convert to buttons + KeyItemList items; + for (UInt32 i = 0; i < numKeys; ++i) { + KeyIDMap::const_iterator gtIndex = m_keyIDMap.find(keys[i]); + if (gtIndex == m_keyIDMap.end()) { + return false; + } + const KeyGroupTable& groupTable = gtIndex->second; + + // if we allow group switching during composition then search all + // groups for keys, otherwise search just the given group. + SInt32 n = 1; + if (m_composeAcrossGroups) { + n = (SInt32)groupTable.size(); + } + + bool found = false; + for (SInt32 gd = 0; gd < n && !found; ++gd) { + SInt32 eg = (group + gd) % getNumGroups(); + const KeyEntryList& entries = groupTable[eg]; + for (size_t j = 0; j < entries.size(); ++j) { + if (entries[j].size() == 1) { + found = true; + items.push_back(entries[j][0]); + break; + } + } + } + if (!found) { + // required key is not in keyboard group + return false; + } + } + + // add key + groupTable[group].push_back(items); + return true; +} + +void +CKeyMap::allowGroupSwitchDuringCompose() +{ + m_composeAcrossGroups = true; +} + +void +CKeyMap::addHalfDuplexButton(KeyButton button) +{ + m_halfDuplex.insert(button); +} + +void +CKeyMap::clearHalfDuplexModifiers() +{ + m_halfDuplexMods.clear(); +} + +void +CKeyMap::addHalfDuplexModifier(KeyID key) +{ + m_halfDuplexMods.insert(key); +} + +void +CKeyMap::finish() +{ + m_numGroups = findNumGroups(); + + // make sure every key has the same number of groups + for (KeyIDMap::iterator i = m_keyIDMap.begin(); + i != m_keyIDMap.end(); ++i) { + i->second.resize(m_numGroups); + } + + // compute keys that generate each modifier + setModifierKeys(); +} + +void +CKeyMap::foreachKey(ForeachKeyCallback cb, void* userData) +{ + for (KeyIDMap::iterator i = m_keyIDMap.begin(); + i != m_keyIDMap.end(); ++i) { + KeyGroupTable& groupTable = i->second; + for (size_t group = 0; group < groupTable.size(); ++group) { + KeyEntryList& entryList = groupTable[group]; + for (size_t j = 0; j < entryList.size(); ++j) { + KeyItemList& itemList = entryList[j]; + for (size_t k = 0; k < itemList.size(); ++k) { + (*cb)(i->first, static_cast(group), + itemList[k], userData); + } + } + } + } +} + +const CKeyMap::KeyItem* +CKeyMap::mapKey(Keystrokes& keys, KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + LOG((CLOG_DEBUG1 "mapKey %04x (%d) with mask %04x, start state: %04x", id, id, desiredMask, currentState)); + + // handle group change + if (id == kKeyNextGroup) { + keys.push_back(Keystroke(1, false, false)); + return NULL; + } + else if (id == kKeyPrevGroup) { + keys.push_back(Keystroke(-1, false, false)); + return NULL; + } + + const KeyItem* item; + switch (id) { + case kKeyShift_L: + case kKeyShift_R: + case kKeyControl_L: + case kKeyControl_R: + case kKeyAlt_L: + case kKeyAlt_R: + case kKeyMeta_L: + case kKeyMeta_R: + case kKeySuper_L: + case kKeySuper_R: + case kKeyAltGr: + case kKeyCapsLock: + case kKeyNumLock: + case kKeyScrollLock: + item = mapModifierKey(keys, id, group, activeModifiers, + currentState, desiredMask, isAutoRepeat); + break; + + case kKeySetModifiers: + if (!keysForModifierState(0, group, activeModifiers, currentState, + desiredMask, desiredMask, 0, keys)) { + LOG((CLOG_DEBUG1 "unable to set modifiers %04x", desiredMask)); + return NULL; + } + return &m_modifierKeyItem; + + case kKeyClearModifiers: + if (!keysForModifierState(0, group, activeModifiers, currentState, + currentState & ~desiredMask, + desiredMask, 0, keys)) { + LOG((CLOG_DEBUG1 "unable to clear modifiers %04x", desiredMask)); + return NULL; + } + return &m_modifierKeyItem; + + default: + if (isCommand(desiredMask)) { + item = mapCommandKey(keys, id, group, activeModifiers, + currentState, desiredMask, isAutoRepeat); + } + else { + item = mapCharacterKey(keys, id, group, activeModifiers, + currentState, desiredMask, isAutoRepeat); + } + break; + } + + if (item != NULL) { + LOG((CLOG_DEBUG1 "mapped to %03x, new state %04x", item->m_button, currentState)); + } + return item; +} + +SInt32 +CKeyMap::getNumGroups() const +{ + return m_numGroups; +} + +SInt32 +CKeyMap::getEffectiveGroup(SInt32 group, SInt32 offset) const +{ + return (group + offset + getNumGroups()) % getNumGroups(); +} + +const CKeyMap::KeyItemList* +CKeyMap::findCompatibleKey(KeyID id, SInt32 group, + KeyModifierMask required, KeyModifierMask sensitive) const +{ + assert(group >= 0 && group < getNumGroups()); + + KeyIDMap::const_iterator i = m_keyIDMap.find(id); + if (i == m_keyIDMap.end()) { + return NULL; + } + + const KeyEntryList& entries = i->second[group]; + for (size_t j = 0; j < entries.size(); ++j) { + if ((entries[j].back().m_sensitive & sensitive) == 0 || + (entries[j].back().m_required & sensitive) == + (required & sensitive)) { + return &entries[j]; + } + } + + return NULL; +} + +bool +CKeyMap::isHalfDuplex(KeyID key, KeyButton button) const +{ + return (m_halfDuplex.count(button) + m_halfDuplexMods.count(key) > 0); +} + +bool +CKeyMap::isCommand(KeyModifierMask mask) const +{ + return ((mask & getCommandModifiers()) != 0); +} + +KeyModifierMask +CKeyMap::getCommandModifiers() const +{ + // we currently treat ctrl, alt, meta and super as command modifiers. + // some platforms may have a more limited set (OS X only needs Alt) + // but this works anyway. + return KeyModifierControl | + KeyModifierAlt | + KeyModifierMeta | + KeyModifierSuper; +} + +void +CKeyMap::collectButtons(const ModifierToKeys& mods, ButtonToKeyMap& keys) +{ + keys.clear(); + for (ModifierToKeys::const_iterator i = mods.begin(); + i != mods.end(); ++i) { + keys.insert(std::make_pair(i->second.m_button, &i->second)); + } +} + +void +CKeyMap::initModifierKey(KeyItem& item) +{ + item.m_generates = 0; + item.m_lock = false; + switch (item.m_id) { + case kKeyShift_L: + case kKeyShift_R: + item.m_generates = KeyModifierShift; + break; + + case kKeyControl_L: + case kKeyControl_R: + item.m_generates = KeyModifierControl; + break; + + case kKeyAlt_L: + case kKeyAlt_R: + item.m_generates = KeyModifierAlt; + break; + + case kKeyMeta_L: + case kKeyMeta_R: + item.m_generates = KeyModifierMeta; + break; + + case kKeySuper_L: + case kKeySuper_R: + item.m_generates = KeyModifierSuper; + break; + + case kKeyAltGr: + item.m_generates = KeyModifierAltGr; + break; + + case kKeyCapsLock: + item.m_generates = KeyModifierCapsLock; + item.m_lock = true; + break; + + case kKeyNumLock: + item.m_generates = KeyModifierNumLock; + item.m_lock = true; + break; + + case kKeyScrollLock: + item.m_generates = KeyModifierScrollLock; + item.m_lock = true; + break; + + default: + // not a modifier + break; + } +} + +SInt32 +CKeyMap::findNumGroups() const +{ + size_t max = 0; + for (KeyIDMap::const_iterator i = m_keyIDMap.begin(); + i != m_keyIDMap.end(); ++i) { + if (i->second.size() > max) { + max = i->second.size(); + } + } + return static_cast(max); +} + +void +CKeyMap::setModifierKeys() +{ + m_modifierKeys.clear(); + m_modifierKeys.resize(kKeyModifierNumBits * getNumGroups()); + for (KeyIDMap::const_iterator i = m_keyIDMap.begin(); + i != m_keyIDMap.end(); ++i) { + const KeyGroupTable& groupTable = i->second; + for (size_t g = 0; g < groupTable.size(); ++g) { + const KeyEntryList& entries = groupTable[g]; + for (size_t j = 0; j < entries.size(); ++j) { + // skip multi-key sequences + if (entries[j].size() != 1) { + continue; + } + + // skip keys that don't generate a modifier + const KeyItem& item = entries[j].back(); + if (item.m_generates == 0) { + continue; + } + + // add key to each indicated modifier in this group + for (SInt32 b = 0; b < kKeyModifierNumBits; ++b) { + // skip if item doesn't generate bit b + if (((1u << b) & item.m_generates) != 0) { + SInt32 mIndex = g * kKeyModifierNumBits + b; + m_modifierKeys[mIndex].push_back(&item); + } + } + } + } + } +} + +const CKeyMap::KeyItem* +CKeyMap::mapCommandKey(Keystrokes& keys, KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + static const KeyModifierMask s_overrideModifiers = 0xffffu; + + // find KeySym in table + KeyIDMap::const_iterator i = m_keyIDMap.find(id); + if (i == m_keyIDMap.end()) { + // unknown key + LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id)); + return NULL; + } + const KeyGroupTable& keyGroupTable = i->second; + + // find the first key that generates this KeyID + const KeyItem* keyItem = NULL; + SInt32 numGroups = getNumGroups(); + for (SInt32 groupOffset = 0; groupOffset < numGroups; ++groupOffset) { + SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset); + const KeyEntryList& entryList = keyGroupTable[effectiveGroup]; + for (size_t i = 0; i < entryList.size(); ++i) { + if (entryList[i].size() != 1) { + // ignore multikey entries + continue; + } + + // only match based on shift; we're after the right button + // not the right character. we'll use desiredMask as-is, + // overriding the key's required modifiers, when synthesizing + // this button. + const KeyItem& item = entryList[i].back(); + if ((item.m_required & KeyModifierShift & desiredMask) == + (item.m_sensitive & KeyModifierShift & desiredMask)) { + LOG((CLOG_DEBUG1 "found key in group %d", effectiveGroup)); + keyItem = &item; + break; + } + } + if (keyItem != NULL) { + break; + } + } + if (keyItem == NULL) { + // no mapping for this keysym + LOG((CLOG_DEBUG1 "no mapping for key %04x", id)); + return NULL; + } + + // make working copy of modifiers + ModifierToKeys newModifiers = activeModifiers; + KeyModifierMask newState = currentState; + SInt32 newGroup = group; + + // don't try to change CapsLock + desiredMask = (desiredMask & ~KeyModifierCapsLock) | + (currentState & KeyModifierCapsLock); + + // add the key + if (!keysForKeyItem(*keyItem, newGroup, newModifiers, + newState, desiredMask, + s_overrideModifiers, isAutoRepeat, keys)) { + LOG((CLOG_DEBUG1 "can't map key")); + keys.clear(); + return NULL; + } + + // add keystrokes to restore modifier keys + if (!keysToRestoreModifiers(*keyItem, group, newModifiers, newState, + activeModifiers, keys)) { + LOG((CLOG_DEBUG1 "failed to restore modifiers")); + keys.clear(); + return NULL; + } + + // add keystrokes to restore group + if (newGroup != group) { + keys.push_back(Keystroke(group, true, true)); + } + + // save new modifiers + activeModifiers = newModifiers; + currentState = newState; + + return keyItem; +} + +const CKeyMap::KeyItem* +CKeyMap::mapCharacterKey(Keystrokes& keys, KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + // find KeySym in table + KeyIDMap::const_iterator i = m_keyIDMap.find(id); + if (i == m_keyIDMap.end()) { + // unknown key + LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id)); + return NULL; + } + const KeyGroupTable& keyGroupTable = i->second; + + // find best key in any group, starting with the active group + SInt32 keyIndex = -1; + SInt32 numGroups = getNumGroups(); + SInt32 groupOffset; + LOG((CLOG_DEBUG1 "find best: %04x %04x", currentState, desiredMask)); + for (groupOffset = 0; groupOffset < numGroups; ++groupOffset) { + SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset); + keyIndex = findBestKey(keyGroupTable[effectiveGroup], + currentState, desiredMask); + if (keyIndex != -1) { + LOG((CLOG_DEBUG1 "found key in group %d", effectiveGroup)); + break; + } + } + if (keyIndex == -1) { + // no mapping for this keysym + LOG((CLOG_DEBUG1 "no mapping for key %04x", id)); + return NULL; + } + + // get keys to press for key + SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset); + const KeyItemList& itemList = keyGroupTable[effectiveGroup][keyIndex]; + if (itemList.empty()) { + return NULL; + } + const KeyItem& keyItem = itemList.back(); + + // make working copy of modifiers + ModifierToKeys newModifiers = activeModifiers; + KeyModifierMask newState = currentState; + SInt32 newGroup = group; + + // add each key + for (size_t j = 0; j < itemList.size(); ++j) { + if (!keysForKeyItem(itemList[j], newGroup, newModifiers, + newState, desiredMask, + 0, isAutoRepeat, keys)) { + LOG((CLOG_DEBUG1 "can't map key")); + keys.clear(); + return NULL; + } + } + + // add keystrokes to restore modifier keys + if (!keysToRestoreModifiers(keyItem, group, newModifiers, newState, + activeModifiers, keys)) { + LOG((CLOG_DEBUG1 "failed to restore modifiers")); + keys.clear(); + return NULL; + } + + // add keystrokes to restore group + if (newGroup != group) { + keys.push_back(Keystroke(group, true, true)); + } + + // save new modifiers + activeModifiers = newModifiers; + currentState = newState; + + return &keyItem; +} + +const CKeyMap::KeyItem* +CKeyMap::mapModifierKey(Keystrokes& keys, KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const +{ + return mapCharacterKey(keys, id, group, activeModifiers, + currentState, desiredMask, isAutoRepeat); +} + +SInt32 +CKeyMap::findBestKey(const KeyEntryList& entryList, + KeyModifierMask /*currentState*/, + KeyModifierMask desiredState) const +{ + // check for an item that can accommodate the desiredState exactly + for (size_t i = 0; i < entryList.size(); ++i) { + const KeyItem& item = entryList[i].back(); + if ((item.m_required & desiredState) == + (item.m_sensitive & desiredState)) { + LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i, entryList.size())); + return i; + } + } + + // choose the item that requires the fewest modifier changes + SInt32 bestCount = 32; + SInt32 bestIndex = -1; + for (size_t i = 0; i < entryList.size(); ++i) { + const KeyItem& item = entryList[i].back(); + KeyModifierMask change = + ((item.m_required ^ desiredState) & item.m_sensitive); + SInt32 n = getNumModifiers(change); + if (n < bestCount) { + bestCount = n; + bestIndex = i; + } + } + if (bestIndex != -1) { + LOG((CLOG_DEBUG1 "best key index %d of %d (%d modifiers)", bestIndex, entryList.size(), bestCount)); + } + + return bestIndex; +} + + +const CKeyMap::KeyItem* +CKeyMap::keyForModifier(KeyButton button, SInt32 group, + SInt32 modifierBit) const +{ + assert(modifierBit >= 0 && modifierBit < kKeyModifierNumBits); + assert(group >= 0 && group < getNumGroups()); + + // find a key that generates the given modifier in the given group + // but doesn't use the given button, presumably because we're trying + // to generate a KeyID that's only bound the the given button. + // this is important when a shift button is modified by shift; we + // must use the other shift button to do the shifting. + const ModifierKeyItemList& items = + m_modifierKeys[group * kKeyModifierNumBits + modifierBit]; + for (ModifierKeyItemList::const_iterator i = items.begin(); + i != items.end(); ++i) { + if ((*i)->m_button != button) { + return (*i); + } + } + return NULL; +} + +bool +CKeyMap::keysForKeyItem(const KeyItem& keyItem, SInt32& group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, KeyModifierMask desiredState, + KeyModifierMask overrideModifiers, + bool isAutoRepeat, + Keystrokes& keystrokes) const +{ + static const KeyModifierMask s_notRequiredMask = + KeyModifierAltGr | KeyModifierNumLock | KeyModifierScrollLock; + + // add keystrokes to adjust the group + if (group != keyItem.m_group) { + group = keyItem.m_group; + keystrokes.push_back(Keystroke(group, true, false)); + } + + EKeystroke type; + if (keyItem.m_dead) { + // adjust modifiers for dead key + if (!keysForModifierState(keyItem.m_button, group, + activeModifiers, currentState, + keyItem.m_required, keyItem.m_sensitive, + 0, keystrokes)) { + LOG((CLOG_DEBUG1 "unable to match modifier state for dead key %d", keyItem.m_button)); + return false; + } + + // press and release the dead key + type = kKeystrokeClick; + } + else { + // if this a command key then we don't have to match some of the + // key's required modifiers. + KeyModifierMask sensitive = keyItem.m_sensitive & ~overrideModifiers; + + // XXX -- must handle pressing a modifier. in particular, if we want + // to synthesize a KeyID on level 1 of a KeyButton that has Shift_L + // mapped to level 0 then we must release that button if it's down + // (in order to satisfy a shift modifier) then press a different + // button (any other button) mapped to the shift modifier and then + // the Shift_L button. + // match key's required state + LOG((CLOG_DEBUG1 "state: %04x,%04x,%04x", currentState, keyItem.m_required, sensitive)); + if (!keysForModifierState(keyItem.m_button, group, + activeModifiers, currentState, + keyItem.m_required, sensitive, + 0, keystrokes)) { + LOG((CLOG_DEBUG1 "unable to match modifier state (%04x,%04x) for key %d", keyItem.m_required, keyItem.m_sensitive, keyItem.m_button)); + return false; + } + + // match desiredState as closely as possible. we must not + // change any modifiers in keyItem.m_sensitive. and if the key + // is a modifier, we don't want to change that modifier. + LOG((CLOG_DEBUG1 "desired state: %04x %04x,%04x,%04x", desiredState, currentState, keyItem.m_required, keyItem.m_sensitive)); + if (!keysForModifierState(keyItem.m_button, group, + activeModifiers, currentState, + desiredState, + ~(sensitive | keyItem.m_generates), + s_notRequiredMask, keystrokes)) { + LOG((CLOG_DEBUG1 "unable to match desired modifier state (%04x,%04x) for key %d", desiredState, ~keyItem.m_sensitive & 0xffffu, keyItem.m_button)); + return false; + } + + // repeat or press of key + type = isAutoRepeat ? kKeystrokeRepeat : kKeystrokePress; + } + addKeystrokes(type, keyItem, activeModifiers, currentState, keystrokes); + + return true; +} + +bool +CKeyMap::keysToRestoreModifiers(const KeyItem& keyItem, SInt32, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + const ModifierToKeys& desiredModifiers, + Keystrokes& keystrokes) const +{ + // XXX -- we're not considering modified modifiers here + + ModifierToKeys oldModifiers = activeModifiers; + + // get the pressed modifier buttons before and after + ButtonToKeyMap oldKeys, newKeys; + collectButtons(oldModifiers, oldKeys); + collectButtons(desiredModifiers, newKeys); + + // release unwanted keys + for (ModifierToKeys::const_iterator i = oldModifiers.begin(); + i != oldModifiers.end(); ++i) { + KeyButton button = i->second.m_button; + if (button != keyItem.m_button && newKeys.count(button) == 0) { + EKeystroke type = kKeystrokeRelease; + if (i->second.m_lock) { + type = kKeystrokeUnmodify; + } + addKeystrokes(type, i->second, + activeModifiers, currentState, keystrokes); + } + } + + // press wanted keys + for (ModifierToKeys::const_iterator i = desiredModifiers.begin(); + i != desiredModifiers.end(); ++i) { + KeyButton button = i->second.m_button; + if (button != keyItem.m_button && oldKeys.count(button) == 0) { + EKeystroke type = kKeystrokePress; + if (i->second.m_lock) { + type = kKeystrokeModify; + } + addKeystrokes(type, i->second, + activeModifiers, currentState, keystrokes); + } + } + + return true; +} + +bool +CKeyMap::keysForModifierState(KeyButton button, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask requiredState, KeyModifierMask sensitiveMask, + KeyModifierMask notRequiredMask, + Keystrokes& keystrokes) const +{ + // compute which modifiers need changing + KeyModifierMask flipMask = ((currentState ^ requiredState) & sensitiveMask); + // if a modifier is not required then don't even try to match it. if + // we don't mask out notRequiredMask then we'll try to match those + // modifiers but succeed if we can't. however, this is known not + // to work if the key itself is a modifier (the numlock toggle can + // interfere) so we don't try to match at all. + flipMask &= ~notRequiredMask; + LOG((CLOG_DEBUG1 "flip: %04x (%04x vs %04x in %04x - %04x)", flipMask, currentState, requiredState, sensitiveMask & 0xffffu, notRequiredMask & 0xffffu)); + if (flipMask == 0) { + return true; + } + + // fix modifiers. this is complicated by the fact that a modifier may + // be sensitive to other modifiers! (who thought that up?) + // + // we'll assume that modifiers with higher bits are affected by modifiers + // with lower bits. there's not much basis for that assumption except + // that we're pretty sure shift isn't changed by other modifiers. + for (SInt32 bit = kKeyModifierNumBits; bit-- > 0; ) { + KeyModifierMask mask = (1u << bit); + if ((flipMask & mask) == 0) { + // modifier is already correct + continue; + } + + // do we want the modifier active or inactive? + bool active = ((requiredState & mask) != 0); + + // get the KeyItem for the modifier in the group + const KeyItem* keyItem = keyForModifier(button, group, bit); + if (keyItem == NULL) { + if ((mask & notRequiredMask) == 0) { + LOG((CLOG_DEBUG1 "no key for modifier %04x", mask)); + return false; + } + else { + continue; + } + } + + // if this modifier is sensitive to modifiers then adjust those + // modifiers. also check if our assumption was correct. note + // that we only need to adjust the modifiers on key down. + KeyModifierMask sensitive = keyItem->m_sensitive; + if ((sensitive & mask) != 0) { + // modifier is sensitive to itself. that makes no sense + // so ignore it. + LOG((CLOG_DEBUG1 "modifier %04x modified by itself", mask)); + sensitive &= ~mask; + } + if (sensitive != 0) { + if (sensitive > mask) { + // our assumption is incorrect + LOG((CLOG_DEBUG1 "modifier %04x modified by %04x", mask, sensitive)); + return false; + } + if (active && !keysForModifierState(button, group, + activeModifiers, currentState, + keyItem->m_required, sensitive, + notRequiredMask, keystrokes)) { + return false; + } + else if (!active) { + // release the modifier + // XXX -- this doesn't work! if Alt and Meta are mapped + // to one key and we want to release Meta we can't do + // that without also releasing Alt. + // need to think about support for modified modifiers. + } + } + + // current state should match required state + if ((currentState & sensitive) != (keyItem->m_required & sensitive)) { + LOG((CLOG_DEBUG1 "unable to match modifier state for modifier %04x (%04x vs %04x in %04x)", mask, currentState, keyItem->m_required, sensitive)); + return false; + } + + // add keystrokes + EKeystroke type = active ? kKeystrokeModify : kKeystrokeUnmodify; + addKeystrokes(type, *keyItem, activeModifiers, currentState, + keystrokes); + } + + return true; +} + +void +CKeyMap::addKeystrokes(EKeystroke type, const KeyItem& keyItem, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + Keystrokes& keystrokes) const +{ + KeyButton button = keyItem.m_button; + UInt32 data = keyItem.m_client; + switch (type) { + case kKeystrokePress: + keystrokes.push_back(Keystroke(button, true, false, data)); + if (keyItem.m_generates != 0) { + if (!keyItem.m_lock || (currentState & keyItem.m_generates) == 0) { + // add modifier key and activate modifier + activeModifiers.insert(std::make_pair( + keyItem.m_generates, keyItem)); + currentState |= keyItem.m_generates; + } + else { + // deactivate locking modifier + activeModifiers.erase(keyItem.m_generates); + currentState &= ~keyItem.m_generates; + } + } + break; + + case kKeystrokeRelease: + keystrokes.push_back(Keystroke(button, false, false, data)); + if (keyItem.m_generates != 0 && !keyItem.m_lock) { + // remove key from active modifiers + std::pair range = + activeModifiers.equal_range(keyItem.m_generates); + for (ModifierToKeys::iterator i = range.first; + i != range.second; ++i) { + if (i->second.m_button == button) { + activeModifiers.erase(i); + break; + } + } + + // if no more keys for this modifier then deactivate modifier + if (activeModifiers.count(keyItem.m_generates) == 0) { + currentState &= ~keyItem.m_generates; + } + } + break; + + case kKeystrokeRepeat: + keystrokes.push_back(Keystroke(button, false, true, data)); + keystrokes.push_back(Keystroke(button, true, true, data)); + // no modifier changes on key repeat + break; + + case kKeystrokeClick: + keystrokes.push_back(Keystroke(button, true, false, data)); + keystrokes.push_back(Keystroke(button, false, false, data)); + // no modifier changes on key click + break; + + case kKeystrokeModify: + case kKeystrokeUnmodify: + if (keyItem.m_lock) { + // we assume there's just one button for this modifier + if (m_halfDuplex.count(button) > 0) { + if (type == kKeystrokeModify) { + // turn half-duplex toggle on (press) + keystrokes.push_back(Keystroke(button, true, false, data)); + } + else { + // turn half-duplex toggle off (release) + keystrokes.push_back(Keystroke(button, false, false, data)); + } + } + else { + // toggle (click) + keystrokes.push_back(Keystroke(button, true, false, data)); + keystrokes.push_back(Keystroke(button, false, false, data)); + } + } + else if (type == kKeystrokeModify) { + // press modifier + keystrokes.push_back(Keystroke(button, true, false, data)); + } + else { + // release all the keys that generate the modifier that are + // currently down + std::pair range = + activeModifiers.equal_range(keyItem.m_generates); + for (ModifierToKeys::const_iterator i = range.first; + i != range.second; ++i) { + keystrokes.push_back(Keystroke(i->second.m_button, + false, false, i->second.m_client)); + } + } + + if (type == kKeystrokeModify) { + activeModifiers.insert(std::make_pair( + keyItem.m_generates, keyItem)); + currentState |= keyItem.m_generates; + } + else { + activeModifiers.erase(keyItem.m_generates); + currentState &= ~keyItem.m_generates; + } + break; + } +} + +SInt32 +CKeyMap::getNumModifiers(KeyModifierMask state) +{ + SInt32 n = 0; + for (; state != 0; state >>= 1) { + if ((state & 1) != 0) { + ++n; + } + } + return n; +} + +bool +CKeyMap::isDeadKey(KeyID key) +{ + return (key == kKeyCompose || (key >= 0x0300 && key <= 0x036f)); +} + +KeyID +CKeyMap::getDeadKey(KeyID key) +{ + if (isDeadKey(key)) { + // already dead + return key; + } + + switch (key) { + case '`': + return kKeyDeadGrave; + + case 0xb4u: + return kKeyDeadAcute; + + case '^': + case 0x2c6: + return kKeyDeadCircumflex; + + case '~': + case 0x2dcu: + return kKeyDeadTilde; + + case 0xafu: + return kKeyDeadMacron; + + case 0x2d8u: + return kKeyDeadBreve; + + case 0x2d9u: + return kKeyDeadAbovedot; + + case 0xa8u: + return kKeyDeadDiaeresis; + + case 0xb0u: + case 0x2dau: + return kKeyDeadAbovering; + + case '\"': + case 0x2ddu: + return kKeyDeadDoubleacute; + + case 0x2c7u: + return kKeyDeadCaron; + + case 0xb8u: + return kKeyDeadCedilla; + + case 0x2dbu: + return kKeyDeadOgonek; + + default: + // unknown + return kKeyNone; + } +} + +CString +CKeyMap::formatKey(KeyID key, KeyModifierMask mask) +{ + // initialize tables + initKeyNameMaps(); + + CString x; + for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) { + KeyModifierMask mod = (1u << i); + if ((mask & mod) != 0 && s_modifierToNameMap->count(mod) > 0) { + x += s_modifierToNameMap->find(mod)->second; + x += "+"; + } + } + if (key != kKeyNone) { + if (s_keyToNameMap->count(key) > 0) { + x += s_keyToNameMap->find(key)->second; + } + // XXX -- we're assuming ASCII here + else if (key >= 33 && key < 127) { + x += (char)key; + } + else { + x += CStringUtil::print("\\u%04x", key); + } + } + else if (!x.empty()) { + // remove trailing '+' + x.erase(x.size() - 1); + } + return x; +} + +bool +CKeyMap::parseKey(const CString& x, KeyID& key) +{ + // initialize tables + initKeyNameMaps(); + + // parse the key + key = kKeyNone; + if (s_nameToKeyMap->count(x) > 0) { + key = s_nameToKeyMap->find(x)->second; + } + // XXX -- we're assuming ASCII encoding here + else if (x.size() == 1) { + if (!isgraph(x[0])) { + // unknown key + return false; + } + key = (KeyID)x[0]; + } + else if (x.size() == 6 && x[0] == '\\' && x[1] == 'u') { + // escaped unicode (\uXXXX where XXXX is a hex number) + char* end; + key = (KeyID)strtol(x.c_str() + 2, &end, 16); + if (*end != '\0') { + return false; + } + } + else if (!x.empty()) { + // unknown key + return false; + } + + return true; +} + +bool +CKeyMap::parseModifiers(CString& x, KeyModifierMask& mask) +{ + // initialize tables + initKeyNameMaps(); + + mask = 0; + CString::size_type tb = x.find_first_not_of(" \t", 0); + while (tb != CString::npos) { + // get next component + CString::size_type te = x.find_first_of(" \t+)", tb); + if (te == CString::npos) { + te = x.size(); + } + CString c = x.substr(tb, te - tb); + if (c.empty()) { + // missing component + return false; + } + + if (s_nameToModifierMap->count(c) > 0) { + KeyModifierMask mod = s_nameToModifierMap->find(c)->second; + if ((mask & mod) != 0) { + // modifier appears twice + return false; + } + mask |= mod; + } + else { + // unknown string + x.erase(0, tb); + CString::size_type tb = x.find_first_not_of(" \t"); + CString::size_type te = x.find_last_not_of(" \t"); + if (tb == CString::npos) { + x = ""; + } + else { + x = x.substr(tb, te - tb + 1); + } + return true; + } + + // check for '+' or end of string + tb = x.find_first_not_of(" \t", te); + if (tb != CString::npos) { + if (x[tb] != '+') { + // expected '+' + return false; + } + tb = x.find_first_not_of(" \t", tb + 1); + } + } + + // parsed the whole thing + x = ""; + return true; +} + +void +CKeyMap::initKeyNameMaps() +{ + // initialize tables + if (s_nameToKeyMap == NULL) { + s_nameToKeyMap = new CNameToKeyMap; + s_keyToNameMap = new CKeyToNameMap; + for (const KeyNameMapEntry* i = kKeyNameMap; i->m_name != NULL; ++i) { + (*s_nameToKeyMap)[i->m_name] = i->m_id; + (*s_keyToNameMap)[i->m_id] = i->m_name; + } + } + if (s_nameToModifierMap == NULL) { + s_nameToModifierMap = new CNameToModifierMap; + s_modifierToNameMap = new CModifierToNameMap; + for (const KeyModifierNameMapEntry* i = kModifierNameMap; + i->m_name != NULL; ++i) { + (*s_nameToModifierMap)[i->m_name] = i->m_mask; + (*s_modifierToNameMap)[i->m_mask] = i->m_name; + } + } +} + + +// +// CKeyMap::KeyItem +// + +bool +CKeyMap::KeyItem::operator==(const KeyItem& x) const +{ + return (m_id == x.m_id && + m_group == x.m_group && + m_button == x.m_button && + m_required == x.m_required && + m_sensitive == x.m_sensitive && + m_generates == x.m_generates && + m_dead == x.m_dead && + m_lock == x.m_lock && + m_client == x.m_client); +} + + +// +// CKeyMap::Keystroke +// + +CKeyMap::Keystroke::Keystroke(KeyButton button, + bool press, bool repeat, UInt32 data) : + m_type(kButton) +{ + m_data.m_button.m_button = button; + m_data.m_button.m_press = press; + m_data.m_button.m_repeat = repeat; + m_data.m_button.m_client = data; +} + +CKeyMap::Keystroke::Keystroke(SInt32 group, bool absolute, bool restore) : + m_type(kGroup) +{ + m_data.m_group.m_group = group; + m_data.m_group.m_absolute = absolute; + m_data.m_group.m_restore = restore; +} diff --git a/lib/synergy/CKeyMap.h b/lib/synergy/CKeyMap.h new file mode 100644 index 00000000..7f34113b --- /dev/null +++ b/lib/synergy/CKeyMap.h @@ -0,0 +1,491 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2005 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef CKEYMAP_H +#define CKEYMAP_H + +#include "KeyTypes.h" +#include "CString.h" +#include "CStringUtil.h" +#include "stdmap.h" +#include "stdset.h" +#include "stdvector.h" + +//! Key map +/*! +This class provides a keyboard mapping. +*/ +class CKeyMap { +public: + CKeyMap(); + ~CKeyMap(); + + //! KeyID synthesis info + /*! + This structure contains the information necessary to synthesize a + keystroke that generates a KeyID (stored elsewhere). \c m_sensitive + lists the modifiers that the key is affected by and must therefore + be in the correct state, which is listed in \c m_required. If the + key is mapped to a modifier, that modifier is in \c m_generates and + is not in \c m_sensitive. + */ + struct KeyItem { + public: + KeyID m_id; //!< KeyID + SInt32 m_group; //!< Group for key + KeyButton m_button; //!< Button to generate KeyID + KeyModifierMask m_required; //!< Modifiers required for KeyID + KeyModifierMask m_sensitive; //!< Modifiers key is sensitive to + KeyModifierMask m_generates; //!< Modifiers key is mapped to + bool m_dead; //!< \c true if this is a dead KeyID + bool m_lock; //!< \c true if this locks a modifier + UInt32 m_client; //!< Client data + + public: + bool operator==(const KeyItem&) const; + }; + + //! The KeyButtons needed to synthesize a KeyID + /*! + An ordered list of \c KeyItems produces a particular KeyID. If + the KeyID can be synthesized directly then there is one entry in + the list. If dead keys are required then they're listed first. + A list is the minimal set of keystrokes necessary to synthesize + the KeyID, so it doesn't include no-ops. A list does not include + any modifier keys unless the KeyID is a modifier, in which case + it has exactly one KeyItem for the modifier itself. + */ + typedef std::vector KeyItemList; + + //! A keystroke + class Keystroke { + public: + enum EType { + kButton, //!< Synthesize button + kGroup //!< Set new group + }; + + Keystroke(KeyButton, bool press, bool repeat, UInt32 clientData); + Keystroke(SInt32 group, bool absolute, bool restore); + + public: + struct CButton { + public: + KeyButton m_button; //!< Button to synthesize + bool m_press; //!< \c true iff press + bool m_repeat; //!< \c true iff for an autorepeat + UInt32 m_client; //!< Client data + }; + struct CGroup { + public: + SInt32 m_group; //!< Group/offset to change to/by + bool m_absolute; //!< \c true iff change to, else by + bool m_restore; //!< \c true iff for restoring state + }; + union CData { + public: + CButton m_button; + CGroup m_group; + }; + + EType m_type; + CData m_data; + }; + + //! A sequence of keystrokes + typedef std::vector Keystrokes; + + //! A mapping of a modifier to keys for that modifier + typedef std::multimap ModifierToKeys; + + //! A set of buttons + typedef std::map ButtonToKeyMap; + + //! Callback type for \c foreachKey + typedef void (*ForeachKeyCallback)(KeyID, SInt32 group, + KeyItem&, void* userData); + + //! @name manipulators + //@{ + + //! Swap with another \c CKeyMap + void swap(CKeyMap&); + + //! Add a key entry + /*! + Adds \p item to the entries for the item's id and group. The + \c m_dead member is set automatically. + */ + void addKeyEntry(const KeyItem& item); + + //! Add an alias key entry + /*! + If \p targetID with the modifiers given by \p targetRequired and + \p targetSensitive is not available in group \p group then find an + entry for \p sourceID with modifiers given by \p sourceRequired and + \p sourceSensitive in any group with exactly one item and, if found, + add a new item just like it except using id \p targetID. This + effectively makes the \p sourceID an alias for \p targetID (i.e. we + can generate \p targetID using \p sourceID). + */ + void addKeyAliasEntry(KeyID targetID, SInt32 group, + KeyModifierMask targetRequired, + KeyModifierMask targetSensitive, + KeyID sourceID, + KeyModifierMask sourceRequired, + KeyModifierMask sourceSensitive); + + //! Add a key sequence entry + /*! + Adds the sequence of keys \p keys (\p numKeys elements long) to + synthesize key \p id in group \p group. This looks up in the + map each key in \p keys. If all are found then each key is + converted to the button for that key and the buttons are added + as the entry for \p id. If \p id is already in the map or at + least one key in \p keys is not in the map then nothing is added + and this returns \c false, otherwise it returns \c true. + */ + bool addKeyCombinationEntry(KeyID id, SInt32 group, + const KeyID* keys, UInt32 numKeys); + + //! Enable composition across groups + /*! + If called then the keyboard map will allow switching between groups + during key composition. Not all systems allow that. + */ + void allowGroupSwitchDuringCompose(); + + //! Add a half-duplex button + /*! + Records that button \p button is a half-duplex key. This is called + when translating the system's keyboard map. It's independent of the + half-duplex modifier calls. + */ + void addHalfDuplexButton(KeyButton button); + + //! Remove all half-duplex modifiers + /*! + Removes all half-duplex modifiers. This is called to set user + configurable half-duplex settings. + */ + void clearHalfDuplexModifiers(); + + //! Add a half-duplex modifier + /*! + Records that modifier key \p key is half-duplex. This is called to + set user configurable half-duplex settings. + */ + void addHalfDuplexModifier(KeyID key); + + //! Finish adding entries + /*! + Called after adding entries, this does some internal housekeeping. + */ + void finish(); + + //! Iterate over all added keys items + /*! + Calls \p cb for every key item. + */ + void foreachKey(ForeachKeyCallback cb, void* userData); + + //@} + //! @name accessors + //@{ + + //! Map key press/repeat to keystrokes. + /*! + Converts press/repeat of key \p id in group \p group with current + modifiers as given in \p currentState and the desired modifiers in + \p desiredMask into the keystrokes necessary to synthesize that key + event in \p keys. It returns the \c KeyItem of the key being + pressed/repeated, or NULL if the key cannot be mapped. + */ + const KeyItem* mapKey(Keystrokes& keys, KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + + //! Get number of groups + /*! + Returns the number of keyboard groups (independent layouts) in the map. + */ + SInt32 getNumGroups() const; + + //! Compute a group number + /*! + Returns the number of the group \p offset groups after group \p group. + */ + SInt32 getEffectiveGroup(SInt32 group, SInt32 offset) const; + + //! Find key entry compatible with modifiers + /*! + Returns the \c KeyItemList for the first entry for \p id in group + \p group that is compatible with the given modifiers, or NULL + if there isn't one. A button list is compatible with a modifiers + if it is either insensitive to all modifiers in \p sensitive or + it requires the modifiers to be in the state indicated by \p required + for every modifier indicated by \p sensitive. + */ + const KeyItemList* findCompatibleKey(KeyID id, SInt32 group, + KeyModifierMask required, + KeyModifierMask sensitive) const; + + //! Test if modifier is half-duplex + /*! + Returns \c true iff modifier key \p key or button \p button is + half-duplex. + */ + bool isHalfDuplex(KeyID key, KeyButton button) const; + + //! Test if modifiers indicate a command + /*! + Returns \c true iff the modifiers in \p mask contain any command + modifiers. A command modifier is used for keyboard shortcuts and + hotkeys, Rather than trying to synthesize a character, a command + is trying to synthesize a particular set of buttons. So it's not + important to match the shift or AltGr state to achieve a character + but it is important to match the modifier state exactly. + */ + bool isCommand(KeyModifierMask mask) const; + + // Get the modifiers that indicate a command + /*! + Returns the modifiers that when combined with other keys indicate + a command (e.g. shortcut or hotkey). + */ + KeyModifierMask getCommandModifiers() const; + + //! Get buttons from modifier map + /*! + Put all the keys in \p modifiers into \p keys. + */ + static void collectButtons(const ModifierToKeys& modifiers, + ButtonToKeyMap& keys); + + //! Set modifier key state + /*! + Sets the modifier key state (\c m_generates and \c m_lock) in \p item + based on the \c m_id in \p item. + */ + static void initModifierKey(KeyItem& item); + + //! Test for a dead key + /*! + Returns \c true if \p key is a dead key. + */ + static bool isDeadKey(KeyID key); + + //! Get corresponding dead key + /*! + Returns the dead key corresponding to \p key if one exists, otherwise + return \c kKeyNone. This returns \p key if it's already a dead key. + */ + static KeyID getDeadKey(KeyID key); + + //! Get string for a key and modifier mask + /*! + Converts a key and modifier mask into a string representing the + combination. + */ + static CString formatKey(KeyID key, KeyModifierMask); + + //! Parse a string into a key + /*! + Converts a string into a key. Returns \c true on success and \c false + if the string cannot be parsed. + */ + static bool parseKey(const CString&, KeyID&); + + //! Parse a string into a modifier mask + /*! + Converts a string into a modifier mask. Returns \c true on success + and \c false if the string cannot be parsed. The modifiers plus any + remaining leading and trailing whitespace is stripped from the input + string. + */ + static bool parseModifiers(CString&, KeyModifierMask&); + + //@} + +private: + //! Ways to synthesize a key + enum EKeystroke { + kKeystrokePress, //!< Synthesize a press + kKeystrokeRelease, //!< Synthesize a release + kKeystrokeRepeat, //!< Synthesize an autorepeat + kKeystrokeClick, //!< Synthesize a press and release + kKeystrokeModify, //!< Synthesize pressing a modifier + kKeystrokeUnmodify //!< Synthesize releasing a modifier + }; + + // A list of ways to synthesize a KeyID + typedef std::vector KeyEntryList; + + // computes the number of groups + SInt32 findNumGroups() const; + + // computes the map of modifiers to the keys that generate the modifiers + void setModifierKeys(); + + // maps a command key. a command key is a keyboard shortcut and we're + // trying to synthesize a button press with an exact sets of modifiers, + // not trying to synthesize a character. so we just need to find the + // right button and synthesize the requested modifiers without regard + // to what character they would synthesize. we disallow multikey + // entries since they don't make sense as hotkeys. + const KeyItem* mapCommandKey(Keystrokes& keys, + KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + + // maps a character key. a character key is trying to synthesize a + // particular KeyID and isn't entirely concerned with the modifiers + // used to do it. + const KeyItem* mapCharacterKey(Keystrokes& keys, + KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + + // maps a modifier key + const KeyItem* mapModifierKey(Keystrokes& keys, + KeyID id, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat) const; + + // returns the index into \p entryList of the KeyItemList requiring + // the fewest modifier changes between \p currentState and + // \p desiredState. + SInt32 findBestKey(const KeyEntryList& entryList, + KeyModifierMask currentState, + KeyModifierMask desiredState) const; + + // gets the \c KeyItem used to synthesize the modifier who's bit is + // given by \p modifierBit in group \p group and does not synthesize + // the key \p button. + const KeyItem* keyForModifier(KeyButton button, SInt32 group, + SInt32 modifierBit) const; + + // fills \p keystrokes with the keys to synthesize the key in + // \p keyItem taking the modifiers into account. returns \c true + // iff successful and sets \p currentState to the + // resulting modifier state. + bool keysForKeyItem(const KeyItem& keyItem, + SInt32& group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredState, + KeyModifierMask overrideModifiers, + bool isAutoRepeat, + Keystrokes& keystrokes) const; + + // fills \p keystrokes with the keys to synthesize the modifiers + // in \p desiredModifiers from the active modifiers listed in + // \p activeModifiers not including the key in \p keyItem. + // returns \c true iff successful. + bool keysToRestoreModifiers(const KeyItem& keyItem, + SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + const ModifierToKeys& desiredModifiers, + Keystrokes& keystrokes) const; + + // fills \p keystrokes and \p undo with the keys to change the + // current modifier state in \p currentState to match the state in + // \p requiredState for each modifier indicated in \p sensitiveMask. + // returns \c true iff successful and sets \p currentState to the + // resulting modifier state. + bool keysForModifierState(KeyButton button, SInt32 group, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask requiredState, + KeyModifierMask sensitiveMask, + KeyModifierMask notRequiredMask, + Keystrokes& keystrokes) const; + + // Adds keystrokes to synthesize key \p keyItem in mode \p type to + // \p keystrokes and to undo the synthesis to \p undo. + void addKeystrokes(EKeystroke type, + const KeyItem& keyItem, + ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + Keystrokes& keystrokes) const; + + // Returns the number of modifiers indicated in \p state. + static SInt32 getNumModifiers(KeyModifierMask state); + + // Initialize key name/id maps + static void initKeyNameMaps(); + + // not implemented + CKeyMap(const CKeyMap&); + CKeyMap& operator=(const CKeyMap&); + +private: + // Ways to synthesize a KeyID over multiple keyboard groups + typedef std::vector KeyGroupTable; + + // Table of KeyID to ways to synthesize that KeyID + typedef std::map KeyIDMap; + + // List of KeyItems that generate a particular modifier + typedef std::vector ModifierKeyItemList; + + // Map a modifier to the KeyItems that synthesize that modifier + typedef std::vector ModifierToKeyTable; + + // A set of keys + typedef std::set KeySet; + + // A set of buttons + typedef std::set KeyButtonSet; + + // Key maps for parsing/formatting + typedef std::map CNameToKeyMap; + typedef std::map CNameToModifierMap; + typedef std::map CKeyToNameMap; + typedef std::map CModifierToNameMap; + + // KeyID info + KeyIDMap m_keyIDMap; + SInt32 m_numGroups; + ModifierToKeyTable m_modifierKeys; + + // composition info + bool m_composeAcrossGroups; + + // half-duplex info + KeyButtonSet m_halfDuplex; // half-duplex set by synergy + KeySet m_halfDuplexMods; // half-duplex set by user + + // dummy KeyItem for changing modifiers + KeyItem m_modifierKeyItem; + + // parsing/formatting tables + static CNameToKeyMap* s_nameToKeyMap; + static CNameToModifierMap* s_nameToModifierMap; + static CKeyToNameMap* s_keyToNameMap; + static CModifierToNameMap* s_modifierToNameMap; +}; + +#endif diff --git a/lib/synergy/CKeyState.cpp b/lib/synergy/CKeyState.cpp index 481354fe..ff374828 100644 --- a/lib/synergy/CKeyState.cpp +++ b/lib/synergy/CKeyState.cpp @@ -16,18 +16,374 @@ #include "IEventQueue.h" #include "CLog.h" #include +#include + +static const KeyButton kButtonMask = (KeyButton)(IKeyState::kNumButtons - 1); + +static const KeyID s_decomposeTable[] = { + // spacing version of dead keys + 0x0060, 0x0300, 0x0020, 0, // grave, dead_grave, space + 0x00b4, 0x0301, 0x0020, 0, // acute, dead_acute, space + 0x005e, 0x0302, 0x0020, 0, // asciicircum, dead_circumflex, space + 0x007e, 0x0303, 0x0020, 0, // asciitilde, dead_tilde, space + 0x00a8, 0x0308, 0x0020, 0, // diaeresis, dead_diaeresis, space + 0x00b0, 0x030a, 0x0020, 0, // degree, dead_abovering, space + 0x00b8, 0x0327, 0x0020, 0, // cedilla, dead_cedilla, space + 0x02db, 0x0328, 0x0020, 0, // ogonek, dead_ogonek, space + 0x02c7, 0x030c, 0x0020, 0, // caron, dead_caron, space + 0x02d9, 0x0307, 0x0020, 0, // abovedot, dead_abovedot, space + 0x02dd, 0x030b, 0x0020, 0, // doubleacute, dead_doubleacute, space + 0x02d8, 0x0306, 0x0020, 0, // breve, dead_breve, space + 0x00af, 0x0304, 0x0020, 0, // macron, dead_macron, space + + // Latin-1 (ISO 8859-1) + 0x00c0, 0x0300, 0x0041, 0, // Agrave, dead_grave, A + 0x00c1, 0x0301, 0x0041, 0, // Aacute, dead_acute, A + 0x00c2, 0x0302, 0x0041, 0, // Acircumflex, dead_circumflex, A + 0x00c3, 0x0303, 0x0041, 0, // Atilde, dead_tilde, A + 0x00c4, 0x0308, 0x0041, 0, // Adiaeresis, dead_diaeresis, A + 0x00c5, 0x030a, 0x0041, 0, // Aring, dead_abovering, A + 0x00c7, 0x0327, 0x0043, 0, // Ccedilla, dead_cedilla, C + 0x00c8, 0x0300, 0x0045, 0, // Egrave, dead_grave, E + 0x00c9, 0x0301, 0x0045, 0, // Eacute, dead_acute, E + 0x00ca, 0x0302, 0x0045, 0, // Ecircumflex, dead_circumflex, E + 0x00cb, 0x0308, 0x0045, 0, // Ediaeresis, dead_diaeresis, E + 0x00cc, 0x0300, 0x0049, 0, // Igrave, dead_grave, I + 0x00cd, 0x0301, 0x0049, 0, // Iacute, dead_acute, I + 0x00ce, 0x0302, 0x0049, 0, // Icircumflex, dead_circumflex, I + 0x00cf, 0x0308, 0x0049, 0, // Idiaeresis, dead_diaeresis, I + 0x00d1, 0x0303, 0x004e, 0, // Ntilde, dead_tilde, N + 0x00d2, 0x0300, 0x004f, 0, // Ograve, dead_grave, O + 0x00d3, 0x0301, 0x004f, 0, // Oacute, dead_acute, O + 0x00d4, 0x0302, 0x004f, 0, // Ocircumflex, dead_circumflex, O + 0x00d5, 0x0303, 0x004f, 0, // Otilde, dead_tilde, O + 0x00d6, 0x0308, 0x004f, 0, // Odiaeresis, dead_diaeresis, O + 0x00d9, 0x0300, 0x0055, 0, // Ugrave, dead_grave, U + 0x00da, 0x0301, 0x0055, 0, // Uacute, dead_acute, U + 0x00db, 0x0302, 0x0055, 0, // Ucircumflex, dead_circumflex, U + 0x00dc, 0x0308, 0x0055, 0, // Udiaeresis, dead_diaeresis, U + 0x00dd, 0x0301, 0x0059, 0, // Yacute, dead_acute, Y + 0x00e0, 0x0300, 0x0061, 0, // agrave, dead_grave, a + 0x00e1, 0x0301, 0x0061, 0, // aacute, dead_acute, a + 0x00e2, 0x0302, 0x0061, 0, // acircumflex, dead_circumflex, a + 0x00e3, 0x0303, 0x0061, 0, // atilde, dead_tilde, a + 0x00e4, 0x0308, 0x0061, 0, // adiaeresis, dead_diaeresis, a + 0x00e5, 0x030a, 0x0061, 0, // aring, dead_abovering, a + 0x00e7, 0x0327, 0x0063, 0, // ccedilla, dead_cedilla, c + 0x00e8, 0x0300, 0x0065, 0, // egrave, dead_grave, e + 0x00e9, 0x0301, 0x0065, 0, // eacute, dead_acute, e + 0x00ea, 0x0302, 0x0065, 0, // ecircumflex, dead_circumflex, e + 0x00eb, 0x0308, 0x0065, 0, // ediaeresis, dead_diaeresis, e + 0x00ec, 0x0300, 0x0069, 0, // igrave, dead_grave, i + 0x00ed, 0x0301, 0x0069, 0, // iacute, dead_acute, i + 0x00ee, 0x0302, 0x0069, 0, // icircumflex, dead_circumflex, i + 0x00ef, 0x0308, 0x0069, 0, // idiaeresis, dead_diaeresis, i + 0x00f1, 0x0303, 0x006e, 0, // ntilde, dead_tilde, n + 0x00f2, 0x0300, 0x006f, 0, // ograve, dead_grave, o + 0x00f3, 0x0301, 0x006f, 0, // oacute, dead_acute, o + 0x00f4, 0x0302, 0x006f, 0, // ocircumflex, dead_circumflex, o + 0x00f5, 0x0303, 0x006f, 0, // otilde, dead_tilde, o + 0x00f6, 0x0308, 0x006f, 0, // odiaeresis, dead_diaeresis, o + 0x00f9, 0x0300, 0x0075, 0, // ugrave, dead_grave, u + 0x00fa, 0x0301, 0x0075, 0, // uacute, dead_acute, u + 0x00fb, 0x0302, 0x0075, 0, // ucircumflex, dead_circumflex, u + 0x00fc, 0x0308, 0x0075, 0, // udiaeresis, dead_diaeresis, u + 0x00fd, 0x0301, 0x0079, 0, // yacute, dead_acute, y + 0x00ff, 0x0308, 0x0079, 0, // ydiaeresis, dead_diaeresis, y + + // Latin-2 (ISO 8859-2) + 0x0104, 0x0328, 0x0041, 0, // Aogonek, dead_ogonek, A + 0x013d, 0x030c, 0x004c, 0, // Lcaron, dead_caron, L + 0x015a, 0x0301, 0x0053, 0, // Sacute, dead_acute, S + 0x0160, 0x030c, 0x0053, 0, // Scaron, dead_caron, S + 0x015e, 0x0327, 0x0053, 0, // Scedilla, dead_cedilla, S + 0x0164, 0x030c, 0x0054, 0, // Tcaron, dead_caron, T + 0x0179, 0x0301, 0x005a, 0, // Zacute, dead_acute, Z + 0x017d, 0x030c, 0x005a, 0, // Zcaron, dead_caron, Z + 0x017b, 0x0307, 0x005a, 0, // Zabovedot, dead_abovedot, Z + 0x0105, 0x0328, 0x0061, 0, // aogonek, dead_ogonek, a + 0x013e, 0x030c, 0x006c, 0, // lcaron, dead_caron, l + 0x015b, 0x0301, 0x0073, 0, // sacute, dead_acute, s + 0x0161, 0x030c, 0x0073, 0, // scaron, dead_caron, s + 0x015f, 0x0327, 0x0073, 0, // scedilla, dead_cedilla, s + 0x0165, 0x030c, 0x0074, 0, // tcaron, dead_caron, t + 0x017a, 0x0301, 0x007a, 0, // zacute, dead_acute, z + 0x017e, 0x030c, 0x007a, 0, // zcaron, dead_caron, z + 0x017c, 0x0307, 0x007a, 0, // zabovedot, dead_abovedot, z + 0x0154, 0x0301, 0x0052, 0, // Racute, dead_acute, R + 0x0102, 0x0306, 0x0041, 0, // Abreve, dead_breve, A + 0x0139, 0x0301, 0x004c, 0, // Lacute, dead_acute, L + 0x0106, 0x0301, 0x0043, 0, // Cacute, dead_acute, C + 0x010c, 0x030c, 0x0043, 0, // Ccaron, dead_caron, C + 0x0118, 0x0328, 0x0045, 0, // Eogonek, dead_ogonek, E + 0x011a, 0x030c, 0x0045, 0, // Ecaron, dead_caron, E + 0x010e, 0x030c, 0x0044, 0, // Dcaron, dead_caron, D + 0x0143, 0x0301, 0x004e, 0, // Nacute, dead_acute, N + 0x0147, 0x030c, 0x004e, 0, // Ncaron, dead_caron, N + 0x0150, 0x030b, 0x004f, 0, // Odoubleacute, dead_doubleacute, O + 0x0158, 0x030c, 0x0052, 0, // Rcaron, dead_caron, R + 0x016e, 0x030a, 0x0055, 0, // Uring, dead_abovering, U + 0x0170, 0x030b, 0x0055, 0, // Udoubleacute, dead_doubleacute, U + 0x0162, 0x0327, 0x0054, 0, // Tcedilla, dead_cedilla, T + 0x0155, 0x0301, 0x0072, 0, // racute, dead_acute, r + 0x0103, 0x0306, 0x0061, 0, // abreve, dead_breve, a + 0x013a, 0x0301, 0x006c, 0, // lacute, dead_acute, l + 0x0107, 0x0301, 0x0063, 0, // cacute, dead_acute, c + 0x010d, 0x030c, 0x0063, 0, // ccaron, dead_caron, c + 0x0119, 0x0328, 0x0065, 0, // eogonek, dead_ogonek, e + 0x011b, 0x030c, 0x0065, 0, // ecaron, dead_caron, e + 0x010f, 0x030c, 0x0064, 0, // dcaron, dead_caron, d + 0x0144, 0x0301, 0x006e, 0, // nacute, dead_acute, n + 0x0148, 0x030c, 0x006e, 0, // ncaron, dead_caron, n + 0x0151, 0x030b, 0x006f, 0, // odoubleacute, dead_doubleacute, o + 0x0159, 0x030c, 0x0072, 0, // rcaron, dead_caron, r + 0x016f, 0x030a, 0x0075, 0, // uring, dead_abovering, u + 0x0171, 0x030b, 0x0075, 0, // udoubleacute, dead_doubleacute, u + 0x0163, 0x0327, 0x0074, 0, // tcedilla, dead_cedilla, t + + // Latin-3 (ISO 8859-3) + 0x0124, 0x0302, 0x0048, 0, // Hcircumflex, dead_circumflex, H + 0x0130, 0x0307, 0x0049, 0, // Iabovedot, dead_abovedot, I + 0x011e, 0x0306, 0x0047, 0, // Gbreve, dead_breve, G + 0x0134, 0x0302, 0x004a, 0, // Jcircumflex, dead_circumflex, J + 0x0125, 0x0302, 0x0068, 0, // hcircumflex, dead_circumflex, h + 0x011f, 0x0306, 0x0067, 0, // gbreve, dead_breve, g + 0x0135, 0x0302, 0x006a, 0, // jcircumflex, dead_circumflex, j + 0x010a, 0x0307, 0x0043, 0, // Cabovedot, dead_abovedot, C + 0x0108, 0x0302, 0x0043, 0, // Ccircumflex, dead_circumflex, C + 0x0120, 0x0307, 0x0047, 0, // Gabovedot, dead_abovedot, G + 0x011c, 0x0302, 0x0047, 0, // Gcircumflex, dead_circumflex, G + 0x016c, 0x0306, 0x0055, 0, // Ubreve, dead_breve, U + 0x015c, 0x0302, 0x0053, 0, // Scircumflex, dead_circumflex, S + 0x010b, 0x0307, 0x0063, 0, // cabovedot, dead_abovedot, c + 0x0109, 0x0302, 0x0063, 0, // ccircumflex, dead_circumflex, c + 0x0121, 0x0307, 0x0067, 0, // gabovedot, dead_abovedot, g + 0x011d, 0x0302, 0x0067, 0, // gcircumflex, dead_circumflex, g + 0x016d, 0x0306, 0x0075, 0, // ubreve, dead_breve, u + 0x015d, 0x0302, 0x0073, 0, // scircumflex, dead_circumflex, s + + // Latin-4 (ISO 8859-4) + 0x0156, 0x0327, 0x0052, 0, // Rcedilla, dead_cedilla, R + 0x0128, 0x0303, 0x0049, 0, // Itilde, dead_tilde, I + 0x013b, 0x0327, 0x004c, 0, // Lcedilla, dead_cedilla, L + 0x0112, 0x0304, 0x0045, 0, // Emacron, dead_macron, E + 0x0122, 0x0327, 0x0047, 0, // Gcedilla, dead_cedilla, G + 0x0157, 0x0327, 0x0072, 0, // rcedilla, dead_cedilla, r + 0x0129, 0x0303, 0x0069, 0, // itilde, dead_tilde, i + 0x013c, 0x0327, 0x006c, 0, // lcedilla, dead_cedilla, l + 0x0113, 0x0304, 0x0065, 0, // emacron, dead_macron, e + 0x0123, 0x0327, 0x0067, 0, // gcedilla, dead_cedilla, g + 0x0100, 0x0304, 0x0041, 0, // Amacron, dead_macron, A + 0x012e, 0x0328, 0x0049, 0, // Iogonek, dead_ogonek, I + 0x0116, 0x0307, 0x0045, 0, // Eabovedot, dead_abovedot, E + 0x012a, 0x0304, 0x0049, 0, // Imacron, dead_macron, I + 0x0145, 0x0327, 0x004e, 0, // Ncedilla, dead_cedilla, N + 0x014c, 0x0304, 0x004f, 0, // Omacron, dead_macron, O + 0x0136, 0x0327, 0x004b, 0, // Kcedilla, dead_cedilla, K + 0x0172, 0x0328, 0x0055, 0, // Uogonek, dead_ogonek, U + 0x0168, 0x0303, 0x0055, 0, // Utilde, dead_tilde, U + 0x016a, 0x0304, 0x0055, 0, // Umacron, dead_macron, U + 0x0101, 0x0304, 0x0061, 0, // amacron, dead_macron, a + 0x012f, 0x0328, 0x0069, 0, // iogonek, dead_ogonek, i + 0x0117, 0x0307, 0x0065, 0, // eabovedot, dead_abovedot, e + 0x012b, 0x0304, 0x0069, 0, // imacron, dead_macron, i + 0x0146, 0x0327, 0x006e, 0, // ncedilla, dead_cedilla, n + 0x014d, 0x0304, 0x006f, 0, // omacron, dead_macron, o + 0x0137, 0x0327, 0x006b, 0, // kcedilla, dead_cedilla, k + 0x0173, 0x0328, 0x0075, 0, // uogonek, dead_ogonek, u + 0x0169, 0x0303, 0x0075, 0, // utilde, dead_tilde, u + 0x016b, 0x0304, 0x0075, 0, // umacron, dead_macron, u + + // Latin-8 (ISO 8859-14) + 0x1e02, 0x0307, 0x0042, 0, // Babovedot, dead_abovedot, B + 0x1e03, 0x0307, 0x0062, 0, // babovedot, dead_abovedot, b + 0x1e0a, 0x0307, 0x0044, 0, // Dabovedot, dead_abovedot, D + 0x1e80, 0x0300, 0x0057, 0, // Wgrave, dead_grave, W + 0x1e82, 0x0301, 0x0057, 0, // Wacute, dead_acute, W + 0x1e0b, 0x0307, 0x0064, 0, // dabovedot, dead_abovedot, d + 0x1ef2, 0x0300, 0x0059, 0, // Ygrave, dead_grave, Y + 0x1e1e, 0x0307, 0x0046, 0, // Fabovedot, dead_abovedot, F + 0x1e1f, 0x0307, 0x0066, 0, // fabovedot, dead_abovedot, f + 0x1e40, 0x0307, 0x004d, 0, // Mabovedot, dead_abovedot, M + 0x1e41, 0x0307, 0x006d, 0, // mabovedot, dead_abovedot, m + 0x1e56, 0x0307, 0x0050, 0, // Pabovedot, dead_abovedot, P + 0x1e81, 0x0300, 0x0077, 0, // wgrave, dead_grave, w + 0x1e57, 0x0307, 0x0070, 0, // pabovedot, dead_abovedot, p + 0x1e83, 0x0301, 0x0077, 0, // wacute, dead_acute, w + 0x1e60, 0x0307, 0x0053, 0, // Sabovedot, dead_abovedot, S + 0x1ef3, 0x0300, 0x0079, 0, // ygrave, dead_grave, y + 0x1e84, 0x0308, 0x0057, 0, // Wdiaeresis, dead_diaeresis, W + 0x1e85, 0x0308, 0x0077, 0, // wdiaeresis, dead_diaeresis, w + 0x1e61, 0x0307, 0x0073, 0, // sabovedot, dead_abovedot, s + 0x0174, 0x0302, 0x0057, 0, // Wcircumflex, dead_circumflex, W + 0x1e6a, 0x0307, 0x0054, 0, // Tabovedot, dead_abovedot, T + 0x0176, 0x0302, 0x0059, 0, // Ycircumflex, dead_circumflex, Y + 0x0175, 0x0302, 0x0077, 0, // wcircumflex, dead_circumflex, w + 0x1e6b, 0x0307, 0x0074, 0, // tabovedot, dead_abovedot, t + 0x0177, 0x0302, 0x0079, 0, // ycircumflex, dead_circumflex, y + + // Latin-9 (ISO 8859-15) + 0x0178, 0x0308, 0x0059, 0, // Ydiaeresis, dead_diaeresis, Y + + // Compose key sequences + 0x00c6, kKeyCompose, 0x0041, 0x0045, 0, // AE, A, E + 0x00c1, kKeyCompose, 0x0041, 0x0027, 0, // Aacute, A, apostrophe + 0x00c2, kKeyCompose, 0x0041, 0x0053, 0, // Acircumflex, A, asciicircum + 0x00c3, kKeyCompose, 0x0041, 0x0022, 0, // Adiaeresis, A, quotedbl + 0x00c0, kKeyCompose, 0x0041, 0x0060, 0, // Agrave, A, grave + 0x00c5, kKeyCompose, 0x0041, 0x002a, 0, // Aring, A, asterisk + 0x00c3, kKeyCompose, 0x0041, 0x007e, 0, // Atilde, A, asciitilde + 0x00c7, kKeyCompose, 0x0043, 0x002c, 0, // Ccedilla, C, comma + 0x00d0, kKeyCompose, 0x0044, 0x002d, 0, // ETH, D, minus + 0x00c9, kKeyCompose, 0x0045, 0x0027, 0, // Eacute, E, apostrophe + 0x00ca, kKeyCompose, 0x0045, 0x0053, 0, // Ecircumflex, E, asciicircum + 0x00cb, kKeyCompose, 0x0045, 0x0022, 0, // Ediaeresis, E, quotedbl + 0x00c8, kKeyCompose, 0x0045, 0x0060, 0, // Egrave, E, grave + 0x00cd, kKeyCompose, 0x0049, 0x0027, 0, // Iacute, I, apostrophe + 0x00ce, kKeyCompose, 0x0049, 0x0053, 0, // Icircumflex, I, asciicircum + 0x00cf, kKeyCompose, 0x0049, 0x0022, 0, // Idiaeresis, I, quotedbl + 0x00cc, kKeyCompose, 0x0049, 0x0060, 0, // Igrave, I, grave + 0x00d1, kKeyCompose, 0x004e, 0x007e, 0, // Ntilde, N, asciitilde + 0x00d3, kKeyCompose, 0x004f, 0x0027, 0, // Oacute, O, apostrophe + 0x00d4, kKeyCompose, 0x004f, 0x0053, 0, // Ocircumflex, O, asciicircum + 0x00d6, kKeyCompose, 0x004f, 0x0022, 0, // Odiaeresis, O, quotedbl + 0x00d2, kKeyCompose, 0x004f, 0x0060, 0, // Ograve, O, grave + 0x00d8, kKeyCompose, 0x004f, 0x002f, 0, // Ooblique, O, slash + 0x00d5, kKeyCompose, 0x004f, 0x007e, 0, // Otilde, O, asciitilde + 0x00de, kKeyCompose, 0x0054, 0x0048, 0, // THORN, T, H + 0x00da, kKeyCompose, 0x0055, 0x0027, 0, // Uacute, U, apostrophe + 0x00db, kKeyCompose, 0x0055, 0x0053, 0, // Ucircumflex, U, asciicircum + 0x00dc, kKeyCompose, 0x0055, 0x0022, 0, // Udiaeresis, U, quotedbl + 0x00d9, kKeyCompose, 0x0055, 0x0060, 0, // Ugrave, U, grave + 0x00dd, kKeyCompose, 0x0059, 0x0027, 0, // Yacute, Y, apostrophe + 0x00e1, kKeyCompose, 0x0061, 0x0027, 0, // aacute, a, apostrophe + 0x00e2, kKeyCompose, 0x0061, 0x0053, 0, // acircumflex, a, asciicircum + 0x00b4, kKeyCompose, 0x0027, 0x0027, 0, // acute, apostrophe, apostrophe + 0x00e4, kKeyCompose, 0x0061, 0x0022, 0, // adiaeresis, a, quotedbl + 0x00e6, kKeyCompose, 0x0061, 0x0065, 0, // ae, a, e + 0x00e0, kKeyCompose, 0x0061, 0x0060, 0, // agrave, a, grave + 0x00e5, kKeyCompose, 0x0061, 0x002a, 0, // aring, a, asterisk + 0x0040, kKeyCompose, 0x0041, 0x0054, 0, // at, A, T + 0x00e3, kKeyCompose, 0x0061, 0x007e, 0, // atilde, a, asciitilde + 0x005c, kKeyCompose, 0x002f, 0x002f, 0, // backslash, slash, slash + 0x007c, kKeyCompose, 0x004c, 0x0056, 0, // bar, L, V + 0x007b, kKeyCompose, 0x0028, 0x002d, 0, // braceleft, parenleft, minus + 0x007d, kKeyCompose, 0x0029, 0x002d, 0, // braceright, parenright, minus + 0x005b, kKeyCompose, 0x0028, 0x0028, 0, // bracketleft, parenleft, parenleft + 0x005d, kKeyCompose, 0x0029, 0x0029, 0, // bracketright, parenright, parenright + 0x00a6, kKeyCompose, 0x0042, 0x0056, 0, // brokenbar, B, V + 0x00e7, kKeyCompose, 0x0063, 0x002c, 0, // ccedilla, c, comma + 0x00b8, kKeyCompose, 0x002c, 0x002c, 0, // cedilla, comma, comma + 0x00a2, kKeyCompose, 0x0063, 0x002f, 0, // cent, c, slash + 0x00a9, kKeyCompose, 0x0028, 0x0063, 0, // copyright, parenleft, c + 0x00a4, kKeyCompose, 0x006f, 0x0078, 0, // currency, o, x + 0x00b0, kKeyCompose, 0x0030, 0x0053, 0, // degree, 0, asciicircum + 0x00a8, kKeyCompose, 0x0022, 0x0022, 0, // diaeresis, quotedbl, quotedbl + 0x00f7, kKeyCompose, 0x003a, 0x002d, 0, // division, colon, minus + 0x00e9, kKeyCompose, 0x0065, 0x0027, 0, // eacute, e, apostrophe + 0x00ea, kKeyCompose, 0x0065, 0x0053, 0, // ecircumflex, e, asciicircum + 0x00eb, kKeyCompose, 0x0065, 0x0022, 0, // ediaeresis, e, quotedbl + 0x00e8, kKeyCompose, 0x0065, 0x0060, 0, // egrave, e, grave + 0x00f0, kKeyCompose, 0x0064, 0x002d, 0, // eth, d, minus + 0x00a1, kKeyCompose, 0x0021, 0x0021, 0, // exclamdown, exclam, exclam + 0x00ab, kKeyCompose, 0x003c, 0x003c, 0, // guillemotleft, less, less + 0x00bb, kKeyCompose, 0x003e, 0x003e, 0, // guillemotright, greater, greater + 0x0023, kKeyCompose, 0x002b, 0x002b, 0, // numbersign, plus, plus + 0x00ad, kKeyCompose, 0x002d, 0x002d, 0, // hyphen, minus, minus + 0x00ed, kKeyCompose, 0x0069, 0x0027, 0, // iacute, i, apostrophe + 0x00ee, kKeyCompose, 0x0069, 0x0053, 0, // icircumflex, i, asciicircum + 0x00ef, kKeyCompose, 0x0069, 0x0022, 0, // idiaeresis, i, quotedbl + 0x00ec, kKeyCompose, 0x0069, 0x0060, 0, // igrave, i, grave + 0x00af, kKeyCompose, 0x002d, 0x0053, 0, // macron, minus, asciicircum + 0x00ba, kKeyCompose, 0x006f, 0x005f, 0, // masculine, o, underscore + 0x00b5, kKeyCompose, 0x0075, 0x002f, 0, // mu, u, slash + 0x00d7, kKeyCompose, 0x0078, 0x0078, 0, // multiply, x, x + 0x00a0, kKeyCompose, 0x0020, 0x0020, 0, // nobreakspace, space, space + 0x00ac, kKeyCompose, 0x002c, 0x002d, 0, // notsign, comma, minus + 0x00f1, kKeyCompose, 0x006e, 0x007e, 0, // ntilde, n, asciitilde + 0x00f3, kKeyCompose, 0x006f, 0x0027, 0, // oacute, o, apostrophe + 0x00f4, kKeyCompose, 0x006f, 0x0053, 0, // ocircumflex, o, asciicircum + 0x00f6, kKeyCompose, 0x006f, 0x0022, 0, // odiaeresis, o, quotedbl + 0x00f2, kKeyCompose, 0x006f, 0x0060, 0, // ograve, o, grave + 0x00bd, kKeyCompose, 0x0031, 0x0032, 0, // onehalf, 1, 2 + 0x00bc, kKeyCompose, 0x0031, 0x0034, 0, // onequarter, 1, 4 + 0x00b9, kKeyCompose, 0x0031, 0x0053, 0, // onesuperior, 1, asciicircum + 0x00aa, kKeyCompose, 0x0061, 0x005f, 0, // ordfeminine, a, underscore + 0x00f8, kKeyCompose, 0x006f, 0x002f, 0, // oslash, o, slash + 0x00f5, kKeyCompose, 0x006f, 0x007e, 0, // otilde, o, asciitilde + 0x00b6, kKeyCompose, 0x0070, 0x0021, 0, // paragraph, p, exclam + 0x00b7, kKeyCompose, 0x002e, 0x002e, 0, // periodcentered, period, period + 0x00b1, kKeyCompose, 0x002b, 0x002d, 0, // plusminus, plus, minus + 0x00bf, kKeyCompose, 0x003f, 0x003f, 0, // questiondown, question, question + 0x00ae, kKeyCompose, 0x0028, 0x0072, 0, // registered, parenleft, r + 0x00a7, kKeyCompose, 0x0073, 0x006f, 0, // section, s, o + 0x00df, kKeyCompose, 0x0073, 0x0073, 0, // ssharp, s, s + 0x00a3, kKeyCompose, 0x004c, 0x002d, 0, // sterling, L, minus + 0x00fe, kKeyCompose, 0x0074, 0x0068, 0, // thorn, t, h + 0x00be, kKeyCompose, 0x0033, 0x0034, 0, // threequarters, 3, 4 + 0x00b3, kKeyCompose, 0x0033, 0x0053, 0, // threesuperior, 3, asciicircum + 0x00b2, kKeyCompose, 0x0032, 0x0053, 0, // twosuperior, 2, asciicircum + 0x00fa, kKeyCompose, 0x0075, 0x0027, 0, // uacute, u, apostrophe + 0x00fb, kKeyCompose, 0x0075, 0x0053, 0, // ucircumflex, u, asciicircum + 0x00fc, kKeyCompose, 0x0075, 0x0022, 0, // udiaeresis, u, quotedbl + 0x00f9, kKeyCompose, 0x0075, 0x0060, 0, // ugrave, u, grave + 0x00fd, kKeyCompose, 0x0079, 0x0027, 0, // yacute, y, apostrophe + 0x00ff, kKeyCompose, 0x0079, 0x0022, 0, // ydiaeresis, y, quotedbl + 0x00a5, kKeyCompose, 0x0079, 0x003d, 0, // yen, y, equal + + // end of table + 0 +}; + +static const KeyID s_numpadTable[] = { + kKeyKP_Space, 0x0020, + kKeyKP_Tab, kKeyTab, + kKeyKP_Enter, kKeyReturn, + kKeyKP_F1, kKeyF1, + kKeyKP_F2, kKeyF2, + kKeyKP_F3, kKeyF3, + kKeyKP_F4, kKeyF4, + kKeyKP_Home, kKeyHome, + kKeyKP_Left, kKeyLeft, + kKeyKP_Up, kKeyUp, + kKeyKP_Right, kKeyRight, + kKeyKP_Down, kKeyDown, + kKeyKP_PageUp, kKeyPageUp, + kKeyKP_PageDown, kKeyPageDown, + kKeyKP_End, kKeyEnd, + kKeyKP_Begin, kKeyBegin, + kKeyKP_Insert, kKeyInsert, + kKeyKP_Delete, kKeyDelete, + kKeyKP_Equal, 0x003d, + kKeyKP_Multiply, 0x002a, + kKeyKP_Add, 0x002b, + kKeyKP_Separator, 0x002c, + kKeyKP_Subtract, 0x002d, + kKeyKP_Decimal, 0x002e, + kKeyKP_Divide, 0x002f, + kKeyKP_0, 0x0030, + kKeyKP_1, 0x0031, + kKeyKP_2, 0x0032, + kKeyKP_3, 0x0033, + kKeyKP_4, 0x0034, + kKeyKP_5, 0x0035, + kKeyKP_6, 0x0036, + kKeyKP_7, 0x0037, + kKeyKP_8, 0x0038, + kKeyKP_9, 0x0039 +}; // // CKeyState // CKeyState::CKeyState() : - m_halfDuplex(0), m_mask(0) { memset(&m_keys, 0, sizeof(m_keys)); - memset(&m_serverKeyMap, 0, sizeof(m_serverKeyMap)); - memset(&m_keyToMask, 0, sizeof(m_keyToMask)); + memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys)); + memset(&m_keyClientData, 0, sizeof(m_keyClientData)); + memset(&m_serverKeys, 0, sizeof(m_serverKeys)); } CKeyState::~CKeyState() @@ -36,21 +392,26 @@ CKeyState::~CKeyState() } void -CKeyState::setKeyDown(KeyButton button, bool down) +CKeyState::onKey(KeyButton button, bool down, KeyModifierMask newState) { - button &= kButtonMask; - updateKeyState(button, button, down, false); -} + // update modifier state + m_mask = newState; + LOG((CLOG_DEBUG1 "new mask: 0x%04x", m_mask)); -void -CKeyState::setToggled(KeyModifierMask modifier) -{ - if (isToggle(modifier)) { - const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(modifier)]; - for (KeyButtons::const_iterator j = buttons.begin(); - j != buttons.end(); ++j) { - m_keys[(*j) & kButtonMask] |= kToggled; - } + // ignore bogus buttons + button &= kButtonMask; + if (button == 0) { + return; + } + + // update key state + if (down) { + m_keys[button] = 1; + m_syntheticKeys[button] = 1; + } + else { + m_keys[button] = 0; + m_syntheticKeys[button] = 0; } } @@ -60,7 +421,7 @@ CKeyState::sendKeyEvent( KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button) { - if (isHalfDuplex(m_keyToMask[button])) { + if (m_keyMap.isHalfDuplex(key, button)) { if (isAutoRepeat) { // ignore auto-repeat on half-duplex keys } @@ -88,182 +449,239 @@ CKeyState::sendKeyEvent( } void -CKeyState::updateKeys() +CKeyState::updateKeyMap() { - static const KeyModifierMask s_masks[] = { - KeyModifierShift, - KeyModifierControl, - KeyModifierAlt, - KeyModifierMeta, - KeyModifierSuper, - KeyModifierModeSwitch, - KeyModifierCapsLock, - KeyModifierNumLock, - KeyModifierScrollLock - }; + // get the current keyboard map + CKeyMap keyMap; + getKeyMap(keyMap); + m_keyMap.swap(keyMap); + m_keyMap.finish(); + // add special keys + addCombinationEntries(); + addKeypadEntries(); + addAliasEntries(); +} + +void +CKeyState::updateKeyState() +{ // reset our state memset(&m_keys, 0, sizeof(m_keys)); - memset(&m_serverKeyMap, 0, sizeof(m_serverKeyMap)); - memset(&m_keyToMask, 0, sizeof(m_keyToMask)); - for (UInt32 i = 0; i < sizeof(m_maskToKeys)/sizeof(m_maskToKeys[0]); ++i) { - m_maskToKeys[i].clear(); + memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys)); + memset(&m_keyClientData, 0, sizeof(m_keyClientData)); + memset(&m_serverKeys, 0, sizeof(m_serverKeys)); + m_activeModifiers.clear(); + + // get the current keyboard state + KeyButtonSet keysDown; + pollPressedKeys(keysDown); + for (KeyButtonSet::const_iterator i = keysDown.begin(); + i != keysDown.end(); ++i) { + m_keys[*i] = 1; } - // let subclass set the state - doUpdateKeys(); + // get the current modifier state + m_mask = pollActiveModifiers(); - // figure out the active modifiers - m_mask = 0; - for (UInt32 i = 0; i < sizeof(s_masks) / sizeof(s_masks[0]); ++i) { - if (isModifierActive(s_masks[i])) { - m_mask |= s_masks[i]; - } - } - LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); + // set active modifiers + CAddActiveModifierContext addModifierContext(pollActiveGroup(), m_mask, + m_activeModifiers); + m_keyMap.foreachKey(&CKeyState::addActiveModifierCB, &addModifierContext); + + LOG((CLOG_DEBUG1 "modifiers on update: 0x%04x", m_mask)); +} + +void +CKeyState::addActiveModifierCB(KeyID, SInt32 group, + CKeyMap::KeyItem& keyItem, void* vcontext) +{ + CAddActiveModifierContext* context = + reinterpret_cast(vcontext); + if (group == context->m_activeGroup && + (keyItem.m_generates & context->m_mask) != 0) { + context->m_activeModifiers.insert(std::make_pair( + keyItem.m_generates, keyItem)); + } } void CKeyState::setHalfDuplexMask(KeyModifierMask mask) { - m_halfDuplex = mask & (KeyModifierCapsLock | - KeyModifierNumLock | - KeyModifierScrollLock); + m_keyMap.clearHalfDuplexModifiers(); + if ((mask & KeyModifierCapsLock) != 0) { + m_keyMap.addHalfDuplexModifier(kKeyCapsLock); + } + if ((mask & KeyModifierNumLock) != 0) { + m_keyMap.addHalfDuplexModifier(kKeyNumLock); + } + if ((mask & KeyModifierScrollLock) != 0) { + m_keyMap.addHalfDuplexModifier(kKeyScrollLock); + } } void -CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) +CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID) { - // get the sequence of keys to simulate key press and the final - // modifier state. - Keystrokes keys; - KeyButton localID = - (KeyButton)(mapKey(keys, id, mask, false) & kButtonMask); - if (keys.empty()) { - // do nothing if there are no associated keys - LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); + // if this server key is already down then this is probably a + // mis-reported autorepeat. + serverID &= kButtonMask; + if (m_serverKeys[serverID] != 0) { + fakeKeyRepeat(id, mask, 1, serverID); return; } - // generate key events - fakeKeyEvents(keys, 1); + // ignore certain keys + if (isIgnoredKey(id, mask)) { + LOG((CLOG_DEBUG1 "ignored key %04x %04x", id, mask)); + return; + } - // note that key is down - updateKeyState((KeyButton)(button & kButtonMask), localID, true, true); + // get keys for key press + Keystrokes keys; + ModifierToKeys oldActiveModifiers = m_activeModifiers; + const CKeyMap::KeyItem* keyItem = + m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers, + getActiveModifiersRValue(), mask, false); + if (keyItem == NULL) { + return; + } + KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask); + updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers); + if (localID != 0) { + // note keys down + ++m_keys[localID]; + ++m_syntheticKeys[localID]; + m_keyClientData[localID] = keyItem->m_client; + m_serverKeys[serverID] = localID; + } + + // generate key events + fakeKeys(keys, 1); } void CKeyState::fakeKeyRepeat( KeyID id, KeyModifierMask mask, - SInt32 count, KeyButton button) + SInt32 count, KeyButton serverID) { - button &= kButtonMask; + serverID &= kButtonMask; // if we haven't seen this button go down then ignore it - KeyButton oldLocalID = m_serverKeyMap[button]; + KeyButton oldLocalID = m_serverKeys[serverID]; if (oldLocalID == 0) { return; } - // get the sequence of keys to simulate key repeat and the final - // modifier state. + // get keys for key repeat Keystrokes keys; - KeyButton localID = (KeyButton)(mapKey(keys, id, mask, true) & kButtonMask); - if (localID == 0) { - LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); + ModifierToKeys oldActiveModifiers = m_activeModifiers; + const CKeyMap::KeyItem* keyItem = + m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers, + getActiveModifiersRValue(), mask, true); + if (keyItem == NULL) { return; } - if (keys.empty()) { - // do nothing if there are no associated keys + KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask); + if (localID == 0) { return; } - // if the keycode for the auto-repeat is not the same as for the + // if the KeyButton for the auto-repeat is not the same as for the // initial press then mark the initial key as released and the new // key as pressed. this can happen when we auto-repeat after a // dead key. for example, a dead accent followed by 'a' will // generate an 'a with accent' followed by a repeating 'a'. the - // keycodes for the two keysyms might be different. + // KeyButtons for the two KeyIDs might be different. if (localID != oldLocalID) { - // replace key up with previous key id but leave key down - // alone so it uses the new keycode. + // replace key up with previous KeyButton but leave key down + // alone so it uses the new KeyButton. for (Keystrokes::iterator index = keys.begin(); index != keys.end(); ++index) { - if (index->m_key == localID) { - index->m_key = oldLocalID; + if (index->m_type == Keystroke::kButton && + index->m_data.m_button.m_button == localID) { + index->m_data.m_button.m_button = oldLocalID; break; } } // note that old key is now up - m_keys[oldLocalID] &= ~kDown; + --m_keys[oldLocalID]; + --m_syntheticKeys[oldLocalID]; - // map server key to new key - m_serverKeyMap[button] = localID; - - // note that new key is now down - m_keys[localID] |= kDown; + // note keys down + updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers); + ++m_keys[localID]; + ++m_syntheticKeys[localID]; + m_keyClientData[localID] = keyItem->m_client; + m_serverKeys[serverID] = localID; } // generate key events - fakeKeyEvents(keys, count); + fakeKeys(keys, count); } void -CKeyState::fakeKeyUp(KeyButton button) +CKeyState::fakeKeyUp(KeyButton serverID) { // if we haven't seen this button go down then ignore it - KeyButton localID = m_serverKeyMap[button & kButtonMask]; + KeyButton localID = m_serverKeys[serverID & kButtonMask]; if (localID == 0) { return; } // get the sequence of keys to simulate key release Keystrokes keys; - Keystroke keystroke; - keystroke.m_key = localID; - keystroke.m_press = false; - keystroke.m_repeat = false; - keys.push_back(keystroke); + keys.push_back(Keystroke(localID, false, false, m_keyClientData[localID])); + + // note keys down + --m_keys[localID]; + --m_syntheticKeys[localID]; + m_serverKeys[serverID] = 0; + + // check if this is a modifier + for (ModifierToKeys::iterator i = m_activeModifiers.begin(); + i != m_activeModifiers.end(); ++i) { + if (i->second.m_button == localID && !i->second.m_lock) { + // modifier is no longer down + KeyModifierMask mask = i->first; + m_activeModifiers.erase(i); + + if (m_activeModifiers.count(mask) == 0) { + // no key for modifier is down so deactivate modifier + m_mask &= ~mask; + LOG((CLOG_DEBUG1 "new state %04x", m_mask)); + } + + break; + } + } // generate key events - fakeKeyEvents(keys, 1); - - // note that key is now up - updateKeyState(button, localID, false, true); + fakeKeys(keys, 1); } void -CKeyState::fakeToggle(KeyModifierMask modifier) +CKeyState::fakeAllKeysUp() { - const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(modifier)]; - if (buttons.empty() || !isToggle(modifier)) { - return; - } - KeyButton button = buttons[0]; - - // get the sequence of keys to simulate key toggle Keystrokes keys; - Keystroke keystroke; - keystroke.m_key = button; - keystroke.m_press = true; - keystroke.m_repeat = false; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); - - // generate key events - fakeKeyEvents(keys, 1); - - // note the toggle - m_keys[button] ^= kToggled; - m_mask ^= modifier; + for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) { + if (m_syntheticKeys[i] > 0) { + keys.push_back(Keystroke(i, false, false, m_keyClientData[i])); + m_keys[i] = 0; + m_syntheticKeys[i] = 0; + } + } + fakeKeys(keys, 1); + memset(&m_serverKeys, 0, sizeof(m_serverKeys)); + m_activeModifiers.clear(); + m_mask = pollActiveModifiers(); } bool CKeyState::isKeyDown(KeyButton button) const { - return ((m_keys[button & kButtonMask] & kDown) != 0); + return (m_keys[button & kButtonMask] > 0); } KeyModifierMask @@ -272,166 +690,106 @@ CKeyState::getActiveModifiers() const return m_mask; } -void -CKeyState::addModifier(KeyModifierMask modifier, const KeyButtons& buttons) +KeyModifierMask& +CKeyState::getActiveModifiersRValue() { - // the mask must not be zero - assert(modifier != 0); + return m_mask; +} - // the mask must have exactly one high bit - assert((modifier & (modifier - 1)) == 0); - - for (KeyButtons::const_iterator j = buttons.begin(); - j != buttons.end(); ++j) { - KeyButton button = static_cast(((*j) & kButtonMask)); - if (button != 0) { - m_keyToMask[button] = modifier; - } - } - - // index keys by mask - m_maskToKeys[getIndexForModifier(modifier)] = buttons; +SInt32 +CKeyState::getEffectiveGroup(SInt32 group, SInt32 offset) const +{ + return m_keyMap.getEffectiveGroup(group, offset); } bool -CKeyState::mapModifier(Keystrokes& keys, Keystrokes& undo, - KeyModifierMask mask, bool desireActive, bool force) const +CKeyState::isIgnoredKey(KeyID key, KeyModifierMask) const { - // look up modifier - const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)]; - if (buttons.empty()) { - return false; - } - - // ignore if already in desired state - if (!force && isModifierActive(mask) == desireActive) { + switch (key) { + case kKeyCapsLock: + case kKeyNumLock: + case kKeyScrollLock: return true; - } - - // initialize keystroke - Keystroke keystroke; - keystroke.m_repeat = false; - - // handle toggles - if (isToggle(mask)) { - keystroke.m_key = buttons[0]; - keystroke.m_press = true; - keys.push_back(keystroke); - keystroke.m_press = false; - keys.push_back(keystroke); - keystroke.m_press = false; - undo.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - - else if (desireActive) { - // press - keystroke.m_key = buttons[0]; - keystroke.m_press = true; - keys.push_back(keystroke); - keystroke.m_press = false; - undo.push_back(keystroke); - } - - else { - // releasing a modifier is quite different from pressing one. - // when we release a modifier we have to release every keycode that - // is assigned to the modifier since the modifier is active if any - // one of them is down. when we press a modifier we just have to - // press one of those keycodes. - for (KeyButtons::const_iterator j = buttons.begin(); - j != buttons.end(); ++j) { - if (isKeyDown(*j)) { - keystroke.m_key = *j; - keystroke.m_press = false; - keys.push_back(keystroke); - keystroke.m_press = true; - undo.push_back(keystroke); - } - } - } - - return true; -} - -bool -CKeyState::isToggle(KeyModifierMask mask) const -{ - return (mask == KeyModifierCapsLock || - mask == KeyModifierNumLock || - mask == KeyModifierScrollLock); -} - -bool -CKeyState::isHalfDuplex(KeyModifierMask mask) const -{ - return ((mask & m_halfDuplex) != 0); -} - -bool -CKeyState::isModifierActive(KeyModifierMask mask) const -{ - const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)]; - KeyButtons::const_iterator j = buttons.begin(); - if (j != buttons.end()) { - if (isToggle(mask)) { - // modifier is a toggle - if ((m_keys[*j] & kToggled) != 0) { - return true; - } - } - else { - // modifier is not a toggle - for (; j != buttons.end(); ++j) { - if ((m_keys[*j] & kDown) != 0) { - return true; - } - } - } - } - return false; -} - -UInt32 -CKeyState::getIndexForModifier(KeyModifierMask mask) const -{ - switch (mask) { - case KeyModifierShift: - return 0; - - case KeyModifierControl: - return 1; - - case KeyModifierAlt: - return 2; - - case KeyModifierMeta: - return 3; - - case KeyModifierSuper: - return 4; - - case KeyModifierModeSwitch: - return 5; - - case KeyModifierCapsLock: - return 6; - - case KeyModifierNumLock: - return 7; - - case KeyModifierScrollLock: - return 8; default: - assert(0 && "invalid modifier mask"); + return false; + } +} + +KeyButton +CKeyState::getButton(KeyID id, SInt32 group) const +{ + const CKeyMap::KeyItemList* items = + m_keyMap.findCompatibleKey(id, group, 0, 0); + if (items == NULL) { return 0; } + else { + return items->back().m_button; + } } void -CKeyState::fakeKeyEvents(const Keystrokes& keys, UInt32 count) +CKeyState::addAliasEntries() +{ + for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) { + // if we can't shift any kKeyTab key in a particular group but we can + // shift kKeyLeftTab then add a shifted kKeyTab entry that matches a + // shifted kKeyLeftTab entry. + m_keyMap.addKeyAliasEntry(kKeyTab, g, + KeyModifierShift, KeyModifierShift, + kKeyLeftTab, + KeyModifierShift, KeyModifierShift); + + // if we have no kKeyLeftTab but we do have a kKeyTab that can be + // shifted then add kKeyLeftTab that matches a kKeyTab. + m_keyMap.addKeyAliasEntry(kKeyLeftTab, g, + KeyModifierShift, KeyModifierShift, + kKeyTab, + 0, KeyModifierShift); + + // map non-breaking space to space + m_keyMap.addKeyAliasEntry(0x20, g, 0, 0, 0xa0, 0, 0); + } +} + +void +CKeyState::addKeypadEntries() +{ + // map every numpad key to its equivalent non-numpad key if it's not + // on the keyboard. + for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) { + for (size_t i = 0; i < sizeof(s_numpadTable) / + sizeof(s_numpadTable[0]); i += 2) { + m_keyMap.addKeyCombinationEntry(s_numpadTable[i], g, + s_numpadTable + i + 1, 1); + } + } +} + +void +CKeyState::addCombinationEntries() +{ + for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) { + // add dead and compose key composition sequences + for (const KeyID* i = s_decomposeTable; *i != 0; ++i) { + // count the decomposed keys for this key + UInt32 numKeys = 0; + for (const KeyID* j = i; *++j != 0; ) { + ++numKeys; + } + + // add an entry for this key + m_keyMap.addKeyCombinationEntry(*i, g, i + 1, numKeys); + + // next key + i += numKeys + 1; + } + } +} + +void +CKeyState::fakeKeys(const Keystrokes& keys, UInt32 count) { // do nothing if no keys or no repeats if (count == 0 || keys.empty()) { @@ -439,16 +797,18 @@ CKeyState::fakeKeyEvents(const Keystrokes& keys, UInt32 count) } // generate key events - LOG((CLOG_DEBUG2 "keystrokes:")); + LOG((CLOG_DEBUG1 "keystrokes:")); for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { - if (k->m_repeat) { + if (k->m_type == Keystroke::kButton && k->m_data.m_button.m_repeat) { // repeat from here up to but not including the next key // with m_repeat == false count times. Keystrokes::const_iterator start = k; while (count-- > 0) { // send repeating events - for (k = start; k != keys.end() && k->m_repeat; ++k) { - fakeKeyEvent(k->m_key, k->m_press, true); + for (k = start; k != keys.end() && + k->m_type == Keystroke::kButton && + k->m_data.m_button.m_repeat; ++k) { + fakeKey(*k); } } @@ -457,7 +817,7 @@ CKeyState::fakeKeyEvents(const Keystrokes& keys, UInt32 count) } else { // send event - fakeKeyEvent(k->m_key, k->m_press, false); + fakeKey(*k); // next key ++k; @@ -466,77 +826,61 @@ CKeyState::fakeKeyEvents(const Keystrokes& keys, UInt32 count) } void -CKeyState::fakeKeyEvent(KeyButton button, bool press, bool isAutoRepeat) +CKeyState::updateModifierKeyState(KeyButton button, + const ModifierToKeys& oldModifiers, + const ModifierToKeys& newModifiers) { - // half-duplex keys are special. we ignore releases and convert - // a press when the toggle is active to a release. - KeyModifierMask mask = m_keyToMask[button]; - if (isHalfDuplex(mask)) { - if (isAutoRepeat || !press) { - return; - } - if (isModifierActive(mask)) { - press = false; - } + // get the pressed modifier buttons before and after + CKeyMap::ButtonToKeyMap oldKeys, newKeys; + for (ModifierToKeys::const_iterator i = oldModifiers.begin(); + i != oldModifiers.end(); ++i) { + oldKeys.insert(std::make_pair(i->second.m_button, &i->second)); + } + for (ModifierToKeys::const_iterator i = newModifiers.begin(); + i != newModifiers.end(); ++i) { + newKeys.insert(std::make_pair(i->second.m_button, &i->second)); } - // send key event - LOG((CLOG_DEBUG2 " %d %s%s", button, press ? "down" : "up", isAutoRepeat ? " repeat" : "")); - doFakeKeyEvent(button, press, isAutoRepeat); + // get the modifier buttons that were pressed or released + CKeyMap::ButtonToKeyMap pressed, released; + std::set_difference(oldKeys.begin(), oldKeys.end(), + newKeys.begin(), newKeys.end(), + std::inserter(released, released.end()), + ButtonToKeyLess()); + std::set_difference(newKeys.begin(), newKeys.end(), + oldKeys.begin(), oldKeys.end(), + std::inserter(pressed, pressed.end()), + ButtonToKeyLess()); + + // update state + for (CKeyMap::ButtonToKeyMap::const_iterator i = released.begin(); + i != released.end(); ++i) { + if (i->first != button) { + m_keys[i->first] = 0; + m_syntheticKeys[i->first] = 0; + } + } + for (CKeyMap::ButtonToKeyMap::const_iterator i = pressed.begin(); + i != pressed.end(); ++i) { + if (i->first != button) { + m_keys[i->first] = 1; + m_syntheticKeys[i->first] = 1; + m_keyClientData[i->first] = i->second->m_client; + } + } } -void -CKeyState::updateKeyState(KeyButton serverID, KeyButton localID, - bool press, bool fake) + +// +// CKeyState::CAddActiveModifierContext +// + +CKeyState::CAddActiveModifierContext::CAddActiveModifierContext( + SInt32 group, KeyModifierMask mask, + ModifierToKeys& activeModifiers) : + m_activeGroup(group), + m_mask(mask), + m_activeModifiers(activeModifiers) { - // ignore bogus keys - if (serverID == 0 || localID == 0) { - return; - } - - // update key state. state doesn't change when auto-repeating. - if (press) { - m_serverKeyMap[serverID] = localID; - m_keys[localID] |= kDown; - } - else { - m_serverKeyMap[serverID] = 0; - m_keys[localID] &= ~kDown; - } - - // update modifier state - KeyModifierMask mask = m_keyToMask[localID]; - if (mask != 0) { - if (isToggle(mask)) { - // never report half-duplex keys as down - if (isHalfDuplex(mask)) { - m_keys[localID] &= ~kDown; - // half-duplex keys on the primary screen don't send the - // usual press/release pairs but instead send the press - // when toggling on and the release when toggleing off. - // since we normally toggle our shadow state on the press - // we need to treat the release as a press on the primary - // screen. we know we're on the primary screen if fake is - // false. secondary screens always get press/release pairs. - if (!fake) { - press = true; - } - } - - // toggle on the press - if (press) { - m_keys[localID] ^= kToggled; - m_mask ^= mask; - } - } - else { - if (press) { - m_mask |= mask; - } - else if (!isModifierActive(mask)) { - m_mask &= ~mask; - } - } - LOG((CLOG_DEBUG2 "new mask: 0x%04x", m_mask)); - } + // do nothing } diff --git a/lib/synergy/CKeyState.h b/lib/synergy/CKeyState.h index 2e8c44b4..6bfd9d8b 100644 --- a/lib/synergy/CKeyState.h +++ b/lib/synergy/CKeyState.h @@ -16,7 +16,7 @@ #define CKEYSTATE_H #include "IKeyState.h" -#include "stdvector.h" +#include "CKeyMap.h" //! Core key state /*! @@ -31,18 +31,15 @@ public: //! @name manipulators //@{ - //! Mark key as being down + //! Handle key event /*! - Sets the state of \p button to down or up. + Sets the state of \p button to down or up and updates the current + modifier state to \p newState. This method should be called by + primary screens only in response to local events. For auto-repeat + set \p down to \c true. Overrides must forward to the superclass. */ - void setKeyDown(KeyButton button, bool down); - - //! Mark modifier as being toggled on - /*! - Sets the state of the keys for the given (single) \p modifier to be - toggled on. - */ - void setToggled(KeyModifierMask modifier); + virtual void onKey(KeyButton button, bool down, + KeyModifierMask newState); //! Post a key event /*! @@ -62,129 +59,158 @@ public: //@} // IKeyState overrides - virtual void updateKeys(); + virtual void updateKeyMap(); + virtual void updateKeyState(); virtual void setHalfDuplexMask(KeyModifierMask); virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button); virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button); virtual void fakeKeyUp(KeyButton button); - virtual void fakeToggle(KeyModifierMask modifier); + virtual void fakeAllKeysUp(); virtual bool fakeCtrlAltDel() = 0; virtual bool isKeyDown(KeyButton) const; virtual KeyModifierMask getActiveModifiers() const; - virtual const char* getKeyName(KeyButton) const = 0; + virtual KeyModifierMask + pollActiveModifiers() const = 0; + virtual SInt32 pollActiveGroup() const = 0; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0; protected: - class Keystroke { - public: - KeyButton m_key; - bool m_press; - bool m_repeat; - }; - typedef std::vector Keystrokes; - typedef std::vector KeyButtons; + typedef CKeyMap::Keystroke Keystroke; - //! @name protocted manipulators + //! @name protected manipulators //@{ - //! Add keys for modifier + //! Get the keyboard map /*! - Sets the buttons that are mapped to the given (single) \p modifier. For - example, if buttons 5 and 23 were mapped to KeyModifierShift (perhaps - as left and right shift keys) then the mask would be KeyModifierShift - and \c buttons would contain 5 and 23. A modifier with no keys is - ignored. Buttons that are zero are ignored. + Fills \p keyMap with the current keyboard map. */ - void addModifier(KeyModifierMask modifier, - const KeyButtons& buttons); - - //! Get key events to change modifier state - /*! - Retrieves the key events necessary to activate (\p desireActive is true) - or deactivate (\p desireActive is false) the modifier given by \p mask - by pushing them onto the back of \p keys. \p mask must specify exactly - one modifier. \p undo receives the key events necessary to restore the - modifier's previous state. They're pushed onto \p undo in the reverse - order they should be executed. If \p force is false then \p keys and - \p undo are only changed if the modifier is not currently in the - desired state. If \p force is true then \p keys and \p undo are always - changed. Returns true if the modifier can be adjusted, false otherwise. - */ - bool mapModifier(Keystrokes& keys, Keystrokes& undo, - KeyModifierMask mask, bool desireActive, - bool force = false) const; - - //! Update the key state - /*! - Update the key state to reflect the physical keyboard state and - current keyboard mapping. This must call \c setKeyDown, \c setToggled, - and \c addModifier to set the current state. - */ - virtual void doUpdateKeys() = 0; + virtual void getKeyMap(CKeyMap& keyMap) = 0; //! Fake a key event /*! - Synthesize a key event for \p button. If \p press is true then - synthesize a key press and, if false, a key release. If - \p isAutoRepeat is true then the event is an auto-repeat. + Synthesize an event for \p keystroke. */ - virtual void doFakeKeyEvent(KeyButton button, - bool press, bool isAutoRepeat) = 0; + virtual void fakeKey(const Keystroke& keystroke) = 0; - //! Map key press/repeat to keystrokes + //! Get the active modifiers /*! - Converts a press/repeat of key \p id with the modifiers as given - in \p desiredMask into the keystrokes necessary to synthesize - that key event. Returns the platform specific code of the key - being pressed, or 0 if the key cannot be mapped or \p isAutoRepeat - is true and the key does not auto-repeat. + Returns the modifiers that are currently active according to our + shadowed state. The state may be modified. */ - virtual KeyButton mapKey(Keystrokes& keys, KeyID id, - KeyModifierMask desiredMask, - bool isAutoRepeat) const = 0; + virtual KeyModifierMask& + getActiveModifiersRValue(); + + //@} + //! @name protected accessors + //@{ + + //! Compute a group number + /*! + Returns the number of the group \p offset groups after group \p group. + */ + SInt32 getEffectiveGroup(SInt32 group, SInt32 offset) const; + + //! Check if key is ignored + /*! + Returns \c true if and only if the key should always be ignored. + The default returns \c true only for the toggle keys. + */ + virtual bool isIgnoredKey(KeyID key, KeyModifierMask mask) const; + + //! Get button for a KeyID + /*! + Return the button mapped to key \p id in group \p group if any, + otherwise returns 0. + */ + KeyButton getButton(KeyID id, SInt32 group) const; //@} private: - bool isHalfDuplex(KeyModifierMask) const; - bool isToggle(KeyModifierMask) const; - bool isModifierActive(KeyModifierMask) const; - UInt32 getIndexForModifier(KeyModifierMask) const; - void fakeKeyEvents(const Keystrokes&, UInt32 count); - void fakeKeyEvent(KeyButton, bool press, bool isAutoRepeat); - void updateKeyState(KeyButton serverID, - KeyButton localID, bool press, bool fake); + typedef CKeyMap::Keystrokes Keystrokes; + typedef CKeyMap::ModifierToKeys ModifierToKeys; + struct CAddActiveModifierContext { + public: + CAddActiveModifierContext(SInt32 group, KeyModifierMask mask, + ModifierToKeys& activeModifiers); + + public: + SInt32 m_activeGroup; + KeyModifierMask m_mask; + ModifierToKeys& m_activeModifiers; + + private: + // not implemented + CAddActiveModifierContext(const CAddActiveModifierContext&); + CAddActiveModifierContext& operator=(const CAddActiveModifierContext&); + }; + + class ButtonToKeyLess { + public: + bool operator()(const CKeyMap::ButtonToKeyMap::value_type& a, + const CKeyMap::ButtonToKeyMap::value_type b) const + { + return (a.first < b.first); + } + }; + + // not implemented + CKeyState(const CKeyState&); + CKeyState& operator=(const CKeyState&); + + // adds alias key sequences. these are sequences that are equivalent + // to other sequences. + void addAliasEntries(); + + // adds non-keypad key sequences for keypad KeyIDs + void addKeypadEntries(); + + // adds key sequences for combination KeyIDs (those built using + // dead keys) + void addCombinationEntries(); + + // synthesize key events. synthesize auto-repeat events count times. + void fakeKeys(const Keystrokes&, UInt32 count); + + // update key state to match changes to modifiers + void updateModifierKeyState(KeyButton button, + const ModifierToKeys& oldModifiers, + const ModifierToKeys& newModifiers); + + // active modifiers collection callback + static void addActiveModifierCB(KeyID id, SInt32 group, + CKeyMap::KeyItem& keyItem, void* vcontext); private: - enum { - kNumModifiers = 9, - kButtonMask = kNumButtons - 1 - }; - typedef UInt8 KeyState; - enum EKeyState { - kDown = 0x01, //!< Key is down - kToggled = 0x02 //!< Key is toggled on - }; - - // modifiers that are half-duplex - KeyModifierMask m_halfDuplex; + // the keyboard map + CKeyMap m_keyMap; // current modifier state KeyModifierMask m_mask; - // current keyboard state - KeyState m_keys[kNumButtons]; + // the active modifiers and the buttons activating them + ModifierToKeys m_activeModifiers; - // map from server button ID to local button ID for pressed keys - KeyButton m_serverKeyMap[kNumButtons]; + // current keyboard state (> 0 if pressed, 0 otherwise). this is + // initialized to the keyboard state according to the system then + // it tracks synthesized events. + SInt32 m_keys[kNumButtons]; - // map button to the modifier mask it represents - KeyModifierMask m_keyToMask[kNumButtons]; + // synthetic keyboard state (> 0 if pressed, 0 otherwise). this + // tracks the synthesized keyboard state. if m_keys[n] > 0 but + // m_syntheticKeys[n] == 0 then the key was pressed locally and + // not synthesized yet. + SInt32 m_syntheticKeys[kNumButtons]; - // map modifier to buttons with that modifier - KeyButtons m_maskToKeys[kNumModifiers]; + // client data for each pressed key + UInt32 m_keyClientData[kNumButtons]; + + // server keyboard state. an entry is 0 if not the key isn't pressed + // otherwise it's the local KeyButton synthesized for the server key. + KeyButton m_serverKeys[kNumButtons]; }; #endif diff --git a/lib/synergy/CPlatformScreen.cpp b/lib/synergy/CPlatformScreen.cpp index ea0760a5..ca33d5a9 100644 --- a/lib/synergy/CPlatformScreen.cpp +++ b/lib/synergy/CPlatformScreen.cpp @@ -25,9 +25,15 @@ CPlatformScreen::~CPlatformScreen() } void -CPlatformScreen::updateKeys() +CPlatformScreen::updateKeyMap() { - getKeyState()->updateKeys(); + getKeyState()->updateKeyMap(); +} + +void +CPlatformScreen::updateKeyState() +{ + getKeyState()->updateKeyState(); updateButtons(); } @@ -58,9 +64,9 @@ CPlatformScreen::fakeKeyUp(KeyButton button) } void -CPlatformScreen::fakeToggle(KeyModifierMask modifier) +CPlatformScreen::fakeAllKeysUp() { - getKeyState()->fakeToggle(modifier); + getKeyState()->fakeAllKeysUp(); } bool @@ -81,8 +87,20 @@ CPlatformScreen::getActiveModifiers() const return getKeyState()->getActiveModifiers(); } -const char* -CPlatformScreen::getKeyName(KeyButton button) const +KeyModifierMask +CPlatformScreen::pollActiveModifiers() const { - return getKeyState()->getKeyName(button); + return getKeyState()->pollActiveModifiers(); +} + +SInt32 +CPlatformScreen::pollActiveGroup() const +{ + return getKeyState()->pollActiveGroup(); +} + +void +CPlatformScreen::pollPressedKeys(KeyButtonSet& pressedKeys) const +{ + getKeyState()->pollPressedKeys(pressedKeys); } diff --git a/lib/synergy/CPlatformScreen.h b/lib/synergy/CPlatformScreen.h index d5bb7fbb..2e5c87f1 100644 --- a/lib/synergy/CPlatformScreen.h +++ b/lib/synergy/CPlatformScreen.h @@ -38,6 +38,11 @@ public: // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides) = 0; virtual void warpCursor(SInt32 x, SInt32 y) = 0; + virtual UInt32 registerHotKey(KeyID key, + KeyModifierMask mask) = 0; + virtual void unregisterHotKey(UInt32 id) = 0; + virtual void fakeInputBegin() = 0; + virtual void fakeInputEnd() = 0; virtual SInt32 getJumpZoneSize() const = 0; virtual bool isAnyMouseButtonDown() const = 0; virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; @@ -46,22 +51,26 @@ public: virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; - virtual void fakeMouseWheel(SInt32 delta) const = 0; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0; // IKeyState overrides - virtual void updateKeys(); + virtual void updateKeyMap(); + virtual void updateKeyState(); virtual void setHalfDuplexMask(KeyModifierMask); virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button); virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button); virtual void fakeKeyUp(KeyButton button); - virtual void fakeToggle(KeyModifierMask modifier); + virtual void fakeAllKeysUp(); virtual bool fakeCtrlAltDel(); virtual bool isKeyDown(KeyButton) const; virtual KeyModifierMask getActiveModifiers() const; - virtual const char* getKeyName(KeyButton) const; + virtual KeyModifierMask + pollActiveModifiers() const; + virtual SInt32 pollActiveGroup() const; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const; // IPlatformScreen overrides virtual void enable() = 0; diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index c7543645..bff8daca 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -28,7 +28,7 @@ CScreen::CScreen(IPlatformScreen* platformScreen) : m_enabled(false), m_entered(m_isPrimary), m_screenSaverSync(true), - m_toggleKeys(0) + m_fakeInput(false) { assert(m_screen != NULL); @@ -54,6 +54,8 @@ CScreen::enable() { assert(!m_enabled); + m_screen->updateKeyMap(); + m_screen->updateKeyState(); m_screen->enable(); if (m_isPrimary) { enablePrimary(); @@ -172,7 +174,7 @@ CScreen::screensaver(bool activate) void CScreen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button) { - assert(!m_isPrimary); + assert(!m_isPrimary || m_fakeInput); // check for ctrl+alt+del emulation if (id == kKeyDelete && @@ -197,7 +199,7 @@ CScreen::keyRepeat(KeyID id, void CScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) { - assert(!m_isPrimary); + assert(!m_isPrimary || m_fakeInput); m_screen->fakeKeyUp(button); } @@ -230,10 +232,10 @@ CScreen::mouseRelativeMove(SInt32 dx, SInt32 dy) } void -CScreen::mouseWheel(SInt32 delta) +CScreen::mouseWheel(SInt32 xDelta, SInt32 yDelta) { assert(!m_isPrimary); - m_screen->fakeMouseWheel(delta); + m_screen->fakeMouseWheel(xDelta, yDelta); } void @@ -272,7 +274,6 @@ CScreen::setOptions(const COptionsList& options) else { m_halfDuplex &= ~KeyModifierCapsLock; } - m_screen->setHalfDuplexMask(m_halfDuplex); LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", ((m_halfDuplex & KeyModifierCapsLock) != 0) ? "on" : "off")); } else if (options[i] == kOptionHalfDuplexNumLock) { @@ -282,7 +283,6 @@ CScreen::setOptions(const COptionsList& options) else { m_halfDuplex &= ~KeyModifierNumLock; } - m_screen->setHalfDuplexMask(m_halfDuplex); LOG((CLOG_DEBUG1 "half-duplex num-lock %s", ((m_halfDuplex & KeyModifierNumLock) != 0) ? "on" : "off")); } else if (options[i] == kOptionHalfDuplexScrollLock) { @@ -292,11 +292,13 @@ CScreen::setOptions(const COptionsList& options) else { m_halfDuplex &= ~KeyModifierScrollLock; } - m_screen->setHalfDuplexMask(m_halfDuplex); LOG((CLOG_DEBUG1 "half-duplex scroll-lock %s", ((m_halfDuplex & KeyModifierScrollLock) != 0) ? "on" : "off")); } } + // update half-duplex options + m_screen->setHalfDuplexMask(m_halfDuplex); + // update screen saver synchronization if (!m_isPrimary && oldScreenSaverSync != m_screenSaverSync) { if (m_screenSaverSync) { @@ -317,6 +319,36 @@ CScreen::setSequenceNumber(UInt32 seqNum) m_screen->setSequenceNumber(seqNum); } +UInt32 +CScreen::registerHotKey(KeyID key, KeyModifierMask mask) +{ + return m_screen->registerHotKey(key, mask); +} + +void +CScreen::unregisterHotKey(UInt32 id) +{ + m_screen->unregisterHotKey(id); +} + +void +CScreen::fakeInputBegin() +{ + assert(!m_fakeInput); + + m_fakeInput = true; + m_screen->fakeInputBegin(); +} + +void +CScreen::fakeInputEnd() +{ + assert(m_fakeInput); + + m_fakeInput = false; + m_screen->fakeInputEnd(); +} + bool CScreen::isOnScreen() const { @@ -332,35 +364,6 @@ CScreen::isLockedToScreen() const return true; } -// note -- we don't lock to the screen if a key is down. key -// reporting is simply not reliable enough to trust. the effect -// of switching screens with a key down is that the client will -// receive key repeats and key releases for keys that it hasn't -// see go down. that's okay because CKeyState will ignore those -// events. the user might be surprised that any modifier keys -// held while crossing to another screen don't apply on the -// target screen. if that ends up being a problem we can try -// to synthesize a key press for those modifiers on entry. -/* - // check for any pressed key - KeyButton key = isAnyKeyDown(); - if (key != 0) { - // double check current state of the keys. this shouldn't - // be necessary but we don't seem to get some key release - // events sometimes. this is an emergency backup so the - // client doesn't get stuck on the screen. - m_screen->updateKeys(); - KeyButton key2 = isAnyKeyDown(); - if (key2 != 0) { - LOG((CLOG_DEBUG "locked by %s", m_screen->getKeyName(key2))); - return true; - } - else { - LOG((CLOG_DEBUG "spuriously locked by %s", m_screen->getKeyName(key))); - } - } -*/ - // not locked return false; } @@ -388,6 +391,12 @@ CScreen::getActiveModifiers() const return m_screen->getActiveModifiers(); } +KeyModifierMask +CScreen::pollActiveModifiers() const +{ + return m_screen->pollActiveModifiers(); +} + void* CScreen::getEventTarget() const { @@ -457,13 +466,9 @@ CScreen::enterPrimary() } void -CScreen::enterSecondary(KeyModifierMask toggleMask) +CScreen::enterSecondary(KeyModifierMask) { - // remember toggle key state. we'll restore this when we leave. - m_toggleKeys = getActiveModifiers(); - - // restore toggle key state - setToggleState(toggleMask); + // do nothing } void @@ -472,55 +477,12 @@ CScreen::leavePrimary() // we don't track keys while on the primary screen so update our // idea of them now. this is particularly to update the state of // the toggle modifiers. - m_screen->updateKeys(); + m_screen->updateKeyState(); } void CScreen::leaveSecondary() { // release any keys we think are still down - releaseKeys(); - - // restore toggle key state - setToggleState(m_toggleKeys); -} - -void -CScreen::releaseKeys() -{ - // release keys that we've synthesized a press for and only those - // keys. we don't want to synthesize a release on a key the user - // is still physically pressing. - for (KeyButton i = 1; i < IKeyState::kNumButtons; ++i) { - if (m_screen->isKeyDown(i)) { - m_screen->fakeKeyUp(i); - } - } -} - -void -CScreen::setToggleState(KeyModifierMask mask) -{ - // toggle modifiers that don't match the desired state - KeyModifierMask different = (m_screen->getActiveModifiers() ^ mask); - if ((different & KeyModifierCapsLock) != 0) { - m_screen->fakeToggle(KeyModifierCapsLock); - } - if ((different & KeyModifierNumLock) != 0) { - m_screen->fakeToggle(KeyModifierNumLock); - } - if ((different & KeyModifierScrollLock) != 0) { - m_screen->fakeToggle(KeyModifierScrollLock); - } -} - -KeyButton -CScreen::isAnyKeyDown() const -{ - for (KeyButton i = 1; i < IKeyState::kNumButtons; ++i) { - if (m_screen->isKeyDown(i)) { - return i; - } - } - return 0; + m_screen->fakeAllKeysUp(); } diff --git a/lib/synergy/CScreen.h b/lib/synergy/CScreen.h index 01b10417..4d216f8b 100644 --- a/lib/synergy/CScreen.h +++ b/lib/synergy/CScreen.h @@ -155,12 +155,12 @@ public: //! Notify of mouse wheel motion /*! - Synthesize mouse events to generate mouse wheel motion of \c delta. - \c delta is positive for motion away from the user and negative for - motion towards the user. Each wheel click should generate a delta - of +/-120. + Synthesize mouse events to generate mouse wheel motion of \c xDelta + and \c yDelta. Deltas are positive for motion away from the user or + to the right and negative for motion towards the user or to the left. + Each wheel click should generate a delta of +/-120. */ - void mouseWheel(SInt32 delta); + void mouseWheel(SInt32 xDelta, SInt32 yDelta); //! Notify of options changes /*! @@ -181,6 +181,34 @@ public: */ void setSequenceNumber(UInt32); + //! Register a system hotkey + /*! + Registers a system-wide hotkey for key \p key with modifiers \p mask. + Returns an id used to unregister the hotkey. + */ + UInt32 registerHotKey(KeyID key, KeyModifierMask mask); + + //! Unregister a system hotkey + /*! + Unregisters a previously registered hot key. + */ + void unregisterHotKey(UInt32 id); + + //! Prepare to synthesize input on primary screen + /*! + Prepares the primary screen to receive synthesized input. We do not + want to receive this synthesized input as user input so this method + ensures that we ignore it. Calls to \c fakeInputBegin() may not be + nested. + */ + void fakeInputBegin(); + + //! Done synthesizing input on primary screen + /*! + Undoes whatever \c fakeInputBegin() did. + */ + void fakeInputEnd(); + //@} //! @name accessors //@{ @@ -217,10 +245,18 @@ public: //! Get the active modifiers /*! - Returns the modifiers that are currently active. + Returns the modifiers that are currently active according to our + shadowed state. */ KeyModifierMask getActiveModifiers() const; + //! Get the active modifiers from OS + /*! + Returns the modifiers that are currently active according to the + operating system. + */ + KeyModifierMask pollActiveModifiers() const; + //@} // IScreen overrides @@ -241,11 +277,6 @@ protected: void leavePrimary(); void leaveSecondary(); -private: - void releaseKeys(); - void setToggleState(KeyModifierMask); - KeyButton isAnyKeyDown() const; - private: // our platform dependent screen IPlatformScreen* m_screen; @@ -266,8 +297,8 @@ private: // transition (true) KeyModifierMask m_halfDuplex; - // the toggle key state when this screen was last entered - KeyModifierMask m_toggleKeys; + // true if we're faking input on a primary screen + bool m_fakeInput; }; #endif diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index 7679d104..81cbf06a 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -129,12 +129,12 @@ public: //! Notify of mouse wheel motion /*! - Synthesize mouse events to generate mouse wheel motion of \c delta. - \c delta is positive for motion away from the user and negative for - motion towards the user. Each wheel click should generate a delta - of +/-120. + Synthesize mouse events to generate mouse wheel motion of \c xDelta + and \c yDelta. Deltas are positive for motion away from the user or + to the right and negative for motion towards the user or to the left. + Each wheel click should generate a delta of +/-120. */ - virtual void mouseWheel(SInt32 delta) = 0; + virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta) = 0; //! Notify of screen saver change virtual void screensaver(bool activate) = 0; diff --git a/lib/synergy/IKeyState.cpp b/lib/synergy/IKeyState.cpp index bcc1ea2a..28cd3313 100644 --- a/lib/synergy/IKeyState.cpp +++ b/lib/synergy/IKeyState.cpp @@ -13,6 +13,7 @@ */ #include "IKeyState.h" +#include // // IKeyState @@ -52,10 +53,113 @@ IKeyState::CKeyInfo* IKeyState::CKeyInfo::alloc(KeyID id, KeyModifierMask mask, KeyButton button, SInt32 count) { - CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); + info->m_key = id; + info->m_mask = mask; + info->m_button = button; + info->m_count = count; + info->m_screens[0] = '\0'; + return info; +} + +IKeyState::CKeyInfo* +IKeyState::CKeyInfo::alloc(KeyID id, + KeyModifierMask mask, KeyButton button, SInt32 count, + const std::set& destinations) +{ + // collect destinations into a string. names are surrounded by ':' + // which makes searching easy later. the string is empty if there + // are no destinations and "*" means all destinations. + CString screens; + for (std::set::const_iterator i = destinations.begin(); + i != destinations.end(); ++i) { + if (*i == "*") { + screens = "*"; + break; + } + else { + if (screens.empty()) { + screens = ":"; + } + screens += *i; + screens += ":"; + } + } + + // build structure + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + screens.size()); info->m_key = id; info->m_mask = mask; info->m_button = button; info->m_count = count; + strcpy(info->m_screens, screens.c_str()); return info; } + +IKeyState::CKeyInfo* +IKeyState::CKeyInfo::alloc(const CKeyInfo& x) +{ + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + strlen(x.m_screens)); + info->m_key = x.m_key; + info->m_mask = x.m_mask; + info->m_button = x.m_button; + info->m_count = x.m_count; + strcpy(info->m_screens, x.m_screens); + return info; +} + +bool +IKeyState::CKeyInfo::isDefault(const char* screens) +{ + return (screens == NULL || screens[0] == '\0'); +} + +bool +IKeyState::CKeyInfo::contains(const char* screens, const CString& name) +{ + // special cases + if (isDefault(screens)) { + return false; + } + if (screens[0] == '*') { + return true; + } + + // search + CString match; + match.reserve(name.size() + 2); + match += ":"; + match += name; + match += ":"; + return (strstr(screens, match.c_str()) != NULL); +} + +bool +IKeyState::CKeyInfo::equal(const CKeyInfo* a, const CKeyInfo* b) +{ + return (a->m_key == b->m_key && + a->m_mask == b->m_mask && + a->m_button == b->m_button && + a->m_count == b->m_count && + strcmp(a->m_screens, b->m_screens) == 0); +} + +void +IKeyState::CKeyInfo::split(const char* screens, std::set& dst) +{ + dst.clear(); + if (isDefault(screens)) { + return; + } + if (screens[0] == '*') { + dst.insert("*"); + return; + } + + const char* i = screens + 1; + while (*i != '\0') { + const char* j = strchr(i, ':'); + dst.insert(CString(i, j - i)); + i = j + 1; + } +} diff --git a/lib/synergy/IKeyState.h b/lib/synergy/IKeyState.h index 04e7be8e..1985a630 100644 --- a/lib/synergy/IKeyState.h +++ b/lib/synergy/IKeyState.h @@ -18,6 +18,8 @@ #include "IInterface.h" #include "KeyTypes.h" #include "CEvent.h" +#include "CString.h" +#include "stdset.h" //! Key state interface /*! @@ -34,23 +36,41 @@ public: class CKeyInfo { public: static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count); + static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count, + const std::set& destinations); + static CKeyInfo* alloc(const CKeyInfo&); + + static bool isDefault(const char* screens); + static bool contains(const char* screens, const CString& name); + static bool equal(const CKeyInfo*, const CKeyInfo*); + static void split(const char* screens, std::set&); public: KeyID m_key; KeyModifierMask m_mask; KeyButton m_button; SInt32 m_count; + char m_screens[1]; }; + typedef std::set KeyButtonSet; + //! @name manipulators //@{ + //! Update the keyboard map + /*! + Causes the key state to get updated to reflect the current keyboard + mapping. + */ + virtual void updateKeyMap() = 0; + //! Update the key state /*! Causes the key state to get updated to reflect the physical keyboard - state and current keyboard mapping. + state. */ - virtual void updateKeys() = 0; + virtual void updateKeyState() = 0; //! Set half-duplex mask /*! @@ -80,12 +100,12 @@ public: */ virtual void fakeKeyUp(KeyButton button) = 0; - //! Fake a modifier toggle + //! Fake key releases for all fake pressed keys /*! - Synthesizes key press/release events to toggle the given \p modifier - and updates the key state. + Synthesizes a key release event for every key that is synthetically + pressed and updates the key state. */ - virtual void fakeToggle(KeyModifierMask modifier) = 0; + virtual void fakeAllKeysUp() = 0; //! Fake ctrl+alt+del /*! @@ -107,16 +127,32 @@ public: //! Get the active modifiers /*! - Returns the modifiers that are currently active. + Returns the modifiers that are currently active according to our + shadowed state. */ virtual KeyModifierMask getActiveModifiers() const = 0; - //! Get name of key + //! Get the active modifiers from OS /*! - Return a string describing the given key. + Returns the modifiers that are currently active according to the + operating system. */ - virtual const char* getKeyName(KeyButton) const = 0; + virtual KeyModifierMask + pollActiveModifiers() const = 0; + + //! Get the active keyboard layout from OS + /*! + Returns the active keyboard layout according to the operating system. + */ + virtual SInt32 pollActiveGroup() const = 0; + + //! Get the keys currently pressed from OS + /*! + Adds any keys that are currently pressed according to the operating + system to \p pressedKeys. + */ + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0; //! Get key down event type. Event data is CKeyInfo*, count == 1. static CEvent::Type getKeyDownEvent(); diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index 47f5d36a..b4d1b785 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -146,6 +146,10 @@ public: // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides) = 0; virtual void warpCursor(SInt32 x, SInt32 y) = 0; + virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask) = 0; + virtual void unregisterHotKey(UInt32 id) = 0; + virtual void fakeInputBegin() = 0; + virtual void fakeInputEnd() = 0; virtual SInt32 getJumpZoneSize() const = 0; virtual bool isAnyMouseButtonDown() const = 0; virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; @@ -154,22 +158,26 @@ public: virtual void fakeMouseButton(ButtonID id, bool press) const = 0; virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0; virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0; - virtual void fakeMouseWheel(SInt32 delta) const = 0; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0; // IKeyState overrides - virtual void updateKeys() = 0; + virtual void updateKeyMap() = 0; + virtual void updateKeyState() = 0; virtual void setHalfDuplexMask(KeyModifierMask) = 0; virtual void fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) = 0; virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button) = 0; virtual void fakeKeyUp(KeyButton button) = 0; - virtual void fakeToggle(KeyModifierMask modifier) = 0; + virtual void fakeAllKeysUp() = 0; virtual bool fakeCtrlAltDel() = 0; virtual bool isKeyDown(KeyButton) const = 0; virtual KeyModifierMask getActiveModifiers() const = 0; - virtual const char* getKeyName(KeyButton) const = 0; + virtual KeyModifierMask + pollActiveModifiers() const = 0; + virtual SInt32 pollActiveGroup() const = 0; + virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0; protected: //! Handle system event @@ -188,9 +196,9 @@ protected: A primary screen has further responsibilities. It should post the events in \c IPrimaryScreen as appropriate. It should also - call \c setKeyDown() on its \c CKeyState whenever a key is pressed + call \c onKey() on its \c CKeyState whenever a key is pressed or released (but not for key repeats). And it should call - \c updateKeys() on its \c CKeyState if necessary when the keyboard + \c updateKeyMap() on its \c CKeyState if necessary when the keyboard mapping changes. The target of all events should be the value returned by diff --git a/lib/synergy/IPrimaryScreen.cpp b/lib/synergy/IPrimaryScreen.cpp index 62d3c4b9..71b103f3 100644 --- a/lib/synergy/IPrimaryScreen.cpp +++ b/lib/synergy/IPrimaryScreen.cpp @@ -25,6 +25,10 @@ CEvent::Type IPrimaryScreen::s_motionSecondaryEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_wheelEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_ssActivatedEvent = CEvent::kUnknown; CEvent::Type IPrimaryScreen::s_ssDeactivatedEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_hotKeyDownEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_hotKeyUpEvent = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_fakeInputBegin = CEvent::kUnknown; +CEvent::Type IPrimaryScreen::s_fakeInputEnd = CEvent::kUnknown; CEvent::Type IPrimaryScreen::getButtonDownEvent() @@ -75,19 +79,63 @@ IPrimaryScreen::getScreensaverDeactivatedEvent() "IPrimaryScreen::screensaverDeactivated"); } +CEvent::Type +IPrimaryScreen::getHotKeyDownEvent() +{ + return CEvent::registerTypeOnce(s_hotKeyDownEvent, + "IPrimaryScreen::hotKeyDown"); +} + +CEvent::Type +IPrimaryScreen::getHotKeyUpEvent() +{ + return CEvent::registerTypeOnce(s_hotKeyUpEvent, + "IPrimaryScreen::hotKeyUp"); +} + +CEvent::Type +IPrimaryScreen::getFakeInputBeginEvent() +{ + return CEvent::registerTypeOnce(s_fakeInputBegin, + "IPrimaryScreen::fakeInputBegin"); +} + +CEvent::Type +IPrimaryScreen::getFakeInputEndEvent() +{ + return CEvent::registerTypeOnce(s_fakeInputEnd, + "IPrimaryScreen::fakeInputEnd"); +} + // // IPrimaryScreen::CButtonInfo // IPrimaryScreen::CButtonInfo* -IPrimaryScreen::CButtonInfo::alloc(ButtonID id) +IPrimaryScreen::CButtonInfo::alloc(ButtonID id, KeyModifierMask mask) { CButtonInfo* info = (CButtonInfo*)malloc(sizeof(CButtonInfo)); info->m_button = id; + info->m_mask = mask; return info; } +IPrimaryScreen::CButtonInfo* +IPrimaryScreen::CButtonInfo::alloc(const CButtonInfo& x) +{ + CButtonInfo* info = (CButtonInfo*)malloc(sizeof(CButtonInfo)); + info->m_button = x.m_button; + info->m_mask = x.m_mask; + return info; +} + +bool +IPrimaryScreen::CButtonInfo::equal(const CButtonInfo* a, const CButtonInfo* b) +{ + return (a->m_button == b->m_button && a->m_mask == b->m_mask); +} + // // IPrimaryScreen::CMotionInfo @@ -108,9 +156,23 @@ IPrimaryScreen::CMotionInfo::alloc(SInt32 x, SInt32 y) // IPrimaryScreen::CWheelInfo* -IPrimaryScreen::CWheelInfo::alloc(SInt32 wheel) +IPrimaryScreen::CWheelInfo::alloc(SInt32 xDelta, SInt32 yDelta) { CWheelInfo* info = (CWheelInfo*)malloc(sizeof(CWheelInfo)); - info->m_wheel = wheel; + info->m_xDelta = xDelta; + info->m_yDelta = yDelta; + return info; +} + + +// +// IPrimaryScreen::CHotKeyInfo +// + +IPrimaryScreen::CHotKeyInfo* +IPrimaryScreen::CHotKeyInfo::alloc(UInt32 id) +{ + CHotKeyInfo* info = (CHotKeyInfo*)malloc(sizeof(CHotKeyInfo)); + info->m_id = id; return info; } diff --git a/lib/synergy/IPrimaryScreen.h b/lib/synergy/IPrimaryScreen.h index 6718a17b..93166d23 100644 --- a/lib/synergy/IPrimaryScreen.h +++ b/lib/synergy/IPrimaryScreen.h @@ -16,6 +16,7 @@ #define IPRIMARYSCREEN_H #include "IInterface.h" +#include "KeyTypes.h" #include "MouseTypes.h" #include "CEvent.h" @@ -29,10 +30,14 @@ public: //! Button event data class CButtonInfo { public: - static CButtonInfo* alloc(ButtonID); + static CButtonInfo* alloc(ButtonID, KeyModifierMask); + static CButtonInfo* alloc(const CButtonInfo&); + + static bool equal(const CButtonInfo*, const CButtonInfo*); public: ButtonID m_button; + KeyModifierMask m_mask; }; //! Motion event data class CMotionInfo { @@ -46,10 +51,19 @@ public: //! Wheel motion event data class CWheelInfo { public: - static CWheelInfo* alloc(SInt32); + static CWheelInfo* alloc(SInt32 xDelta, SInt32 yDelta); public: - SInt32 m_wheel; + SInt32 m_xDelta; + SInt32 m_yDelta; + }; + //! Hot key event data + class CHotKeyInfo { + public: + static CHotKeyInfo* alloc(UInt32 id); + + public: + UInt32 m_id; }; //! @name manipulators @@ -72,6 +86,50 @@ public: */ virtual void warpCursor(SInt32 x, SInt32 y) = 0; + //! Register a system hotkey + /*! + Registers a system-wide hotkey. The screen should arrange for an event + to be delivered to itself when the hot key is pressed or released. When + that happens the screen should post a \c getHotKeyDownEvent() or + \c getHotKeyUpEvent(), respectively. The hot key is key \p key with + exactly the modifiers \p mask. Returns 0 on failure otherwise an id + that can be used to unregister the hotkey. + + A hot key is a set of modifiers and a key, which may itself be a modifier. + The hot key is pressed when the hot key's modifiers and only those + modifiers are logically down (active) and the key is pressed. The hot + key is released when the key is released, regardless of the modifiers. + + The hot key event should be generated no matter what window or application + has the focus. No other window or application should receive the key + press or release events (they can and should see the modifier key events). + When the key is a modifier, it's acceptable to allow the user to press + the modifiers in any order or to require the user to press the given key + last. + */ + virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask) = 0; + + //! Unregister a system hotkey + /*! + Unregisters a previously registered hot key. + */ + virtual void unregisterHotKey(UInt32 id) = 0; + + //! Prepare to synthesize input on primary screen + /*! + Prepares the primary screen to receive synthesized input. We do not + want to receive this synthesized input as user input so this method + ensures that we ignore it. Calls to \c fakeInputBegin() may not be + nested. + */ + virtual void fakeInputBegin() = 0; + + //! Done synthesizing input on primary screen + /*! + Undoes whatever \c fakeInputBegin() did. + */ + virtual void fakeInputEnd() = 0; + //@} //! @name accessors //@{ @@ -98,7 +156,7 @@ public: the edges of the screen, typically the center. */ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; - + //! Get button down event type. Event data is CButtonInfo*. static CEvent::Type getButtonDownEvent(); //! Get button up event type. Event data is CButtonInfo*. @@ -120,6 +178,14 @@ public: static CEvent::Type getScreensaverActivatedEvent(); //! Get screensaver deactivated event type static CEvent::Type getScreensaverDeactivatedEvent(); + //! Get hot key down event type. Event data is CHotKeyInfo*. + static CEvent::Type getHotKeyDownEvent(); + //! Get hot key up event type. Event data is CHotKeyInfo*. + static CEvent::Type getHotKeyUpEvent(); + //! Get start of fake input event type + static CEvent::Type getFakeInputBeginEvent(); + //! Get end of fake input event type + static CEvent::Type getFakeInputEndEvent(); //@} @@ -131,6 +197,10 @@ private: static CEvent::Type s_wheelEvent; static CEvent::Type s_ssActivatedEvent; static CEvent::Type s_ssDeactivatedEvent; + static CEvent::Type s_hotKeyDownEvent; + static CEvent::Type s_hotKeyUpEvent; + static CEvent::Type s_fakeInputBegin; + static CEvent::Type s_fakeInputEnd; }; #endif diff --git a/lib/synergy/IScreen.cpp b/lib/synergy/IScreen.cpp index fadbe8b3..3095b9d4 100644 --- a/lib/synergy/IScreen.cpp +++ b/lib/synergy/IScreen.cpp @@ -21,6 +21,8 @@ CEvent::Type IScreen::s_errorEvent = CEvent::kUnknown; CEvent::Type IScreen::s_shapeChangedEvent = CEvent::kUnknown; CEvent::Type IScreen::s_clipboardGrabbedEvent = CEvent::kUnknown; +CEvent::Type IScreen::s_suspendEvent = CEvent::kUnknown; +CEvent::Type IScreen::s_resumeEvent = CEvent::kUnknown; CEvent::Type IScreen::getErrorEvent() @@ -42,3 +44,17 @@ IScreen::getClipboardGrabbedEvent() return CEvent::registerTypeOnce(s_clipboardGrabbedEvent, "IScreen::clipboardGrabbed"); } + +CEvent::Type +IScreen::getSuspendEvent() +{ + return CEvent::registerTypeOnce(s_suspendEvent, + "IScreen::suspend"); +} + +CEvent::Type +IScreen::getResumeEvent() +{ + return CEvent::registerTypeOnce(s_resumeEvent, + "IScreen::resume"); +} diff --git a/lib/synergy/IScreen.h b/lib/synergy/IScreen.h index a36f8fb1..e1f94d59 100644 --- a/lib/synergy/IScreen.h +++ b/lib/synergy/IScreen.h @@ -85,12 +85,28 @@ public: */ static CEvent::Type getClipboardGrabbedEvent(); + //! Get suspend event type + /*! + Returns the suspend event type. This is sent whenever the system goes + to sleep or a user session is deactivated (fast user switching). + */ + static CEvent::Type getSuspendEvent(); + + //! Get resume event type + /*! + Returns the suspend event type. This is sent whenever the system wakes + up or a user session is activated (fast user switching). + */ + static CEvent::Type getResumeEvent(); + //@} private: static CEvent::Type s_errorEvent; static CEvent::Type s_shapeChangedEvent; static CEvent::Type s_clipboardGrabbedEvent; + static CEvent::Type s_suspendEvent; + static CEvent::Type s_resumeEvent; }; #endif diff --git a/lib/synergy/ISecondaryScreen.h b/lib/synergy/ISecondaryScreen.h index 092cd805..5b3a150d 100644 --- a/lib/synergy/ISecondaryScreen.h +++ b/lib/synergy/ISecondaryScreen.h @@ -48,9 +48,9 @@ public: //! Fake mouse wheel /*! - Synthesize a mouse wheel event of amount \c delta. + Synthesize a mouse wheel event of amount \c xDelta and \c yDelta. */ - virtual void fakeMouseWheel(SInt32 delta) const = 0; + virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0; //@} }; diff --git a/lib/synergy/KeyTypes.cpp b/lib/synergy/KeyTypes.cpp new file mode 100644 index 00000000..b483745d --- /dev/null +++ b/lib/synergy/KeyTypes.cpp @@ -0,0 +1,204 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "KeyTypes.h" + +const KeyNameMapEntry kKeyNameMap[] = { + { "AltGr", kKeyAltGr }, + { "Alt_L", kKeyAlt_L }, + { "Alt_R", kKeyAlt_R }, + { "AppMail", kKeyAppMail }, + { "AppMedia", kKeyAppMedia }, + { "AppUser1", kKeyAppUser1 }, + { "AppUser2", kKeyAppUser2 }, + { "AudioDown", kKeyAudioDown }, + { "AudioMute", kKeyAudioMute }, + { "AudioNext", kKeyAudioNext }, + { "AudioPlay", kKeyAudioPlay }, + { "AudioPrev", kKeyAudioPrev }, + { "AudioStop", kKeyAudioStop }, + { "AudioUp", kKeyAudioUp }, + { "BackSpace", kKeyBackSpace }, + { "Begin", kKeyBegin }, + { "Break", kKeyBreak }, + { "Cancel", kKeyCancel }, + { "CapsLock", kKeyCapsLock }, + { "Clear", kKeyClear }, + { "Control_L", kKeyControl_L }, + { "Control_R", kKeyControl_R }, + { "Delete", kKeyDelete }, + { "Down", kKeyDown }, + { "Eject", kKeyEject }, + { "End", kKeyEnd }, + { "Escape", kKeyEscape }, + { "Execute", kKeyExecute }, + { "F1", kKeyF1 }, + { "F2", kKeyF2 }, + { "F3", kKeyF3 }, + { "F4", kKeyF4 }, + { "F5", kKeyF5 }, + { "F6", kKeyF6 }, + { "F7", kKeyF7 }, + { "F8", kKeyF8 }, + { "F9", kKeyF9 }, + { "F10", kKeyF10 }, + { "F11", kKeyF11 }, + { "F12", kKeyF12 }, + { "F13", kKeyF13 }, + { "F14", kKeyF14 }, + { "F15", kKeyF15 }, + { "F16", kKeyF16 }, + { "F17", kKeyF17 }, + { "F18", kKeyF18 }, + { "F19", kKeyF19 }, + { "F20", kKeyF20 }, + { "F21", kKeyF21 }, + { "F22", kKeyF22 }, + { "F23", kKeyF23 }, + { "F24", kKeyF24 }, + { "F25", kKeyF25 }, + { "F26", kKeyF26 }, + { "F27", kKeyF27 }, + { "F28", kKeyF28 }, + { "F29", kKeyF29 }, + { "F30", kKeyF30 }, + { "F31", kKeyF31 }, + { "F32", kKeyF32 }, + { "F33", kKeyF33 }, + { "F34", kKeyF34 }, + { "F35", kKeyF35 }, + { "Find", kKeyFind }, + { "Help", kKeyHelp }, + { "Henkan", kKeyHenkan }, + { "Home", kKeyHome }, + { "Hyper_L", kKeyHyper_L }, + { "Hyper_R", kKeyHyper_R }, + { "Insert", kKeyInsert }, + { "KP_0", kKeyKP_0 }, + { "KP_1", kKeyKP_1 }, + { "KP_2", kKeyKP_2 }, + { "KP_3", kKeyKP_3 }, + { "KP_4", kKeyKP_4 }, + { "KP_5", kKeyKP_5 }, + { "KP_6", kKeyKP_6 }, + { "KP_7", kKeyKP_7 }, + { "KP_8", kKeyKP_8 }, + { "KP_9", kKeyKP_9 }, + { "KP_Add", kKeyKP_Add }, + { "KP_Begin", kKeyKP_Begin }, + { "KP_Decimal", kKeyKP_Decimal }, + { "KP_Delete", kKeyKP_Delete }, + { "KP_Divide", kKeyKP_Divide }, + { "KP_Down", kKeyKP_Down }, + { "KP_End", kKeyKP_End }, + { "KP_Enter", kKeyKP_Enter }, + { "KP_Equal", kKeyKP_Equal }, + { "KP_F1", kKeyKP_F1 }, + { "KP_F2", kKeyKP_F2 }, + { "KP_F3", kKeyKP_F3 }, + { "KP_F4", kKeyKP_F4 }, + { "KP_Home", kKeyKP_Home }, + { "KP_Insert", kKeyKP_Insert }, + { "KP_Left", kKeyKP_Left }, + { "KP_Multiply", kKeyKP_Multiply }, + { "KP_PageDown", kKeyKP_PageDown }, + { "KP_PageUp", kKeyKP_PageUp }, + { "KP_Right", kKeyKP_Right }, + { "KP_Separator", kKeyKP_Separator }, + { "KP_Space", kKeyKP_Space }, + { "KP_Subtract", kKeyKP_Subtract }, + { "KP_Tab", kKeyKP_Tab }, + { "KP_Up", kKeyKP_Up }, + { "Left", kKeyLeft }, + { "LeftTab", kKeyLeftTab }, + { "Linefeed", kKeyLinefeed }, + { "Menu", kKeyMenu }, + { "Meta_L", kKeyMeta_L }, + { "Meta_R", kKeyMeta_R }, + { "NumLock", kKeyNumLock }, + { "PageDown", kKeyPageDown }, + { "PageUp", kKeyPageUp }, + { "Pause", kKeyPause }, + { "Print", kKeyPrint }, + { "Redo", kKeyRedo }, + { "Return", kKeyReturn }, + { "Right", kKeyRight }, + { "ScrollLock", kKeyScrollLock }, + { "Select", kKeySelect }, + { "ShiftLock", kKeyShiftLock }, + { "Shift_L", kKeyShift_L }, + { "Shift_R", kKeyShift_R }, + { "Sleep", kKeySleep }, + { "Super_L", kKeySuper_L }, + { "Super_R", kKeySuper_R }, + { "SysReq", kKeySysReq }, + { "Tab", kKeyTab }, + { "Undo", kKeyUndo }, + { "Up", kKeyUp }, + { "WWWBack", kKeyWWWBack }, + { "WWWFavorites", kKeyWWWFavorites }, + { "WWWForward", kKeyWWWForward }, + { "WWWHome", kKeyWWWHome }, + { "WWWRefresh", kKeyWWWRefresh }, + { "WWWSearch", kKeyWWWSearch }, + { "WWWStop", kKeyWWWStop }, + { "Zenkaku", kKeyZenkaku }, + { "Space", 0x0020 }, + { "Exclaim", 0x0021 }, + { "DoubleQuote", 0x0022 }, + { "Number", 0x0023 }, + { "Dollar", 0x0024 }, + { "Percent", 0x0025 }, + { "Ampersand", 0x0026 }, + { "Apostrophe", 0x0027 }, + { "ParenthesisL", 0x0028 }, + { "ParenthesisR", 0x0029 }, + { "Asterisk", 0x002a }, + { "Plus", 0x002b }, + { "Comma", 0x002c }, + { "Minus", 0x002d }, + { "Period", 0x002e }, + { "Slash", 0x002f }, + { "Colon", 0x003a }, + { "Semicolon", 0x003b }, + { "Less", 0x003c }, + { "Equal", 0x003d }, + { "Greater", 0x003e }, + { "Question", 0x003f }, + { "At", 0x0040 }, + { "BracketL", 0x005b }, + { "Backslash", 0x005c }, + { "BracketR", 0x005d }, + { "Circumflex", 0x005e }, + { "Underscore", 0x005f }, + { "Grave", 0x0060 }, + { "BraceL", 0x007b }, + { "Bar", 0x007c }, + { "BraceR", 0x007d }, + { "Tilde", 0x007e }, + { NULL, 0 }, +}; + +const KeyModifierNameMapEntry kModifierNameMap[] = { + { "Alt", KeyModifierAlt }, + { "AltGr", KeyModifierAltGr }, +// { "CapsLock", KeyModifierCapsLock }, + { "Control", KeyModifierControl }, + { "Meta", KeyModifierMeta }, +// { "NumLock", KeyModifierNumLock }, +// { "ScrollLock", KeyModifierScrollLock }, + { "Shift", KeyModifierShift }, + { "Super", KeyModifierSuper }, + { NULL, 0 }, +}; diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index c6ff7148..039f9030 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -53,12 +53,27 @@ static const KeyModifierMask KeyModifierControl = 0x0002; static const KeyModifierMask KeyModifierAlt = 0x0004; static const KeyModifierMask KeyModifierMeta = 0x0008; static const KeyModifierMask KeyModifierSuper = 0x0010; -static const KeyModifierMask KeyModifierModeSwitch = 0x0020; +static const KeyModifierMask KeyModifierAltGr = 0x0020; static const KeyModifierMask KeyModifierCapsLock = 0x1000; static const KeyModifierMask KeyModifierNumLock = 0x2000; static const KeyModifierMask KeyModifierScrollLock = 0x4000; //@} +//! @name Modifier key bits +//@{ +static const UInt32 kKeyModifierBitNone = 16; +static const UInt32 kKeyModifierBitShift = 0; +static const UInt32 kKeyModifierBitControl = 1; +static const UInt32 kKeyModifierBitAlt = 2; +static const UInt32 kKeyModifierBitMeta = 3; +static const UInt32 kKeyModifierBitSuper = 4; +static const UInt32 kKeyModifierBitAltGr = 5; +static const UInt32 kKeyModifierBitCapsLock = 12; +static const UInt32 kKeyModifierBitNumLock = 13; +static const UInt32 kKeyModifierBitScrollLock = 14; +static const SInt32 kKeyModifierNumBits = 16; +//@} + //! @name Modifier key identifiers //@{ static const KeyModifierID kKeyModifierIDNull = 0; @@ -92,9 +107,6 @@ static const KeyID kKeyHenkan = 0xEF23; /* Start/Stop Conversion */ static const KeyID kKeyZenkaku = 0xEF2A; /* Zenkaku/Hankaku */ static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ -// multi-key character composition -static const KeyID kKeyMultiKey = 0xEF20; /* Multi-key character compose */ - // cursor control static const KeyID kKeyHome = 0xEF50; static const KeyID kKeyLeft = 0xEF51; /* Move left, left arrow */ @@ -118,7 +130,7 @@ static const KeyID kKeyFind = 0xEF68; /* Find, search */ static const KeyID kKeyCancel = 0xEF69; /* Cancel, stop, abort, exit */ static const KeyID kKeyHelp = 0xEF6A; /* Help */ static const KeyID kKeyBreak = 0xEF6B; -static const KeyID kKeyModeSwitch = 0xEF7E; /* Character set switch */ +static const KeyID kKeyAltGr = 0xEF7E; /* Character set switch */ static const KeyID kKeyNumLock = 0xEF7F; // keypad @@ -134,9 +146,7 @@ static const KeyID kKeyKP_Left = 0xEF96; static const KeyID kKeyKP_Up = 0xEF97; static const KeyID kKeyKP_Right = 0xEF98; static const KeyID kKeyKP_Down = 0xEF99; -static const KeyID kKeyKP_Prior = 0xEF9A; static const KeyID kKeyKP_PageUp = 0xEF9A; -static const KeyID kKeyKP_Next = 0xEF9B; static const KeyID kKeyKP_PageDown = 0xEF9B; static const KeyID kKeyKP_End = 0xEF9C; static const KeyID kKeyKP_Begin = 0xEF9D; @@ -213,8 +223,32 @@ static const KeyID kKeySuper_R = 0xEFEC; /* Right super */ static const KeyID kKeyHyper_L = 0xEFED; /* Left hyper */ static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */ +// multi-key character composition +static const KeyID kKeyCompose = 0xEF20; +static const KeyID kKeyDeadGrave = 0x0300; +static const KeyID kKeyDeadAcute = 0x0301; +static const KeyID kKeyDeadCircumflex = 0x0302; +static const KeyID kKeyDeadTilde = 0x0303; +static const KeyID kKeyDeadMacron = 0x0304; +static const KeyID kKeyDeadBreve = 0x0306; +static const KeyID kKeyDeadAbovedot = 0x0307; +static const KeyID kKeyDeadDiaeresis = 0x0308; +static const KeyID kKeyDeadAbovering = 0x030a; +static const KeyID kKeyDeadDoubleacute = 0x030b; +static const KeyID kKeyDeadCaron = 0x030c; +static const KeyID kKeyDeadCedilla = 0x0327; +static const KeyID kKeyDeadOgonek = 0x0328; + // more function and modifier keys -static const KeyID kKeyLeftTab = 0xEE20; +static const KeyID kKeyLeftTab = 0xEE20; + +// update modifiers +static const KeyID kKeySetModifiers = 0xEE06; +static const KeyID kKeyClearModifiers = 0xEE07; + +// group change +static const KeyID kKeyNextGroup = 0xEE08; +static const KeyID kKeyPrevGroup = 0xEE0A; // extended keys static const KeyID kKeyEject = 0xE001; @@ -240,4 +274,30 @@ static const KeyID kKeyAppUser2 = 0xE0B7; //@} +struct KeyNameMapEntry { +public: + const char* m_name; + KeyID m_id; +}; +struct KeyModifierNameMapEntry { +public: + const char* m_name; + KeyModifierMask m_mask; +}; + +//! Key name to KeyID table +/*! +A table of key names to the corresponding KeyID. Only the keys listed +above plus non-alphanumeric ASCII characters are in the table. The end +of the table is the first pair with a NULL m_name. +*/ +extern const KeyNameMapEntry kKeyNameMap[]; + +//! Modifier key name to KeyModifierMask table +/*! +A table of modifier key names to the corresponding KeyModifierMask. +The end of the table is the first pair with a NULL m_name. +*/ +extern const KeyModifierNameMapEntry kModifierNameMap[]; + #endif diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 0265490c..0b481eef 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -24,6 +24,7 @@ MAINTAINERCLEANFILES = \ noinst_LIBRARIES = libsynergy.a libsynergy_a_SOURCES = \ CClipboard.cpp \ + CKeyMap.cpp \ CKeyState.cpp \ CPacketStreamFilter.cpp \ CPlatformScreen.cpp \ @@ -33,9 +34,12 @@ libsynergy_a_SOURCES = \ IKeyState.cpp \ IPrimaryScreen.cpp \ IScreen.cpp \ + KeyTypes.cpp \ + ProtocolTypes.cpp \ XScreen.cpp \ XSynergy.cpp \ CClipboard.h \ + CKeyMap.h \ CKeyState.h \ CPacketStreamFilter.h \ CPlatformScreen.h \ diff --git a/lib/synergy/OptionTypes.h b/lib/synergy/OptionTypes.h index 31504993..74beda00 100644 --- a/lib/synergy/OptionTypes.h +++ b/lib/synergy/OptionTypes.h @@ -42,20 +42,49 @@ typedef std::vector COptionsList; //! @name Option identifiers //@{ -static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); -static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); -static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); -static const OptionID kOptionModifierMapForShift = OPTION_CODE("MMFS"); -static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC"); -static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); -static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); -static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); -static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); -static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); -static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); -static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); -static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); -static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT"); +static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); +static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); +static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); +static const OptionID kOptionModifierMapForShift = OPTION_CODE("MMFS"); +static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC"); +static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); +static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); +static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); +static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); +static const OptionID kOptionScreenSwitchCorners = OPTION_CODE("SSCM"); +static const OptionID kOptionScreenSwitchCornerSize = OPTION_CODE("SSCS"); +static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); +static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); +static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); +static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); +static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT"); +static const OptionID kOptionWin32KeepForeground = OPTION_CODE("_KFW"); +//@} + +//! @name Screen switch corner enumeration +//@{ +enum EScreenSwitchCorners { + kNoCorner, + kTopLeft, + kTopRight, + kBottomLeft, + kBottomRight, + kFirstCorner = kTopLeft, + kLastCorner = kBottomRight +}; +//@} + +//! @name Screen switch corner masks +//@{ +enum EScreenSwitchCornerMasks { + kNoCornerMask = 0, + kTopLeftMask = 1 << (kTopLeft - kFirstCorner), + kTopRightMask = 1 << (kTopRight - kFirstCorner), + kBottomLeftMask = 1 << (kBottomLeft - kFirstCorner), + kBottomRightMask = 1 << (kBottomRight - kFirstCorner), + kAllCornersMask = kTopLeftMask | kTopRightMask | + kBottomLeftMask | kBottomRightMask +}; //@} #undef OPTION_CODE diff --git a/lib/synergy/ProtocolTypes.cpp b/lib/synergy/ProtocolTypes.cpp new file mode 100644 index 00000000..441b2f33 --- /dev/null +++ b/lib/synergy/ProtocolTypes.cpp @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ProtocolTypes.h" + +const char* kMsgHello = "Synergy%2i%2i"; +const char* kMsgHelloBack = "Synergy%2i%2i%s"; +const char* kMsgCNoop = "CNOP"; +const char* kMsgCClose = "CBYE"; +const char* kMsgCEnter = "CINN%2i%2i%4i%2i"; +const char* kMsgCLeave = "COUT"; +const char* kMsgCClipboard = "CCLP%1i%4i"; +const char* kMsgCScreenSaver = "CSEC%1i"; +const char* kMsgCResetOptions = "CROP"; +const char* kMsgCInfoAck = "CIAK"; +const char* kMsgCKeepAlive = "CALV"; +const char* kMsgDKeyDown = "DKDN%2i%2i%2i"; +const char* kMsgDKeyDown1_0 = "DKDN%2i%2i"; +const char* kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i"; +const char* kMsgDKeyRepeat1_0 = "DKRP%2i%2i%2i"; +const char* kMsgDKeyUp = "DKUP%2i%2i%2i"; +const char* kMsgDKeyUp1_0 = "DKUP%2i%2i"; +const char* kMsgDMouseDown = "DMDN%1i"; +const char* kMsgDMouseUp = "DMUP%1i"; +const char* kMsgDMouseMove = "DMMV%2i%2i"; +const char* kMsgDMouseRelMove = "DMRM%2i%2i"; +const char* kMsgDMouseWheel = "DMWM%2i%2i"; +const char* kMsgDMouseWheel1_0 = "DMWM%2i"; +const char* kMsgDClipboard = "DCLP%1i%4i%s"; +const char* kMsgDInfo = "DINF%2i%2i%2i%2i%2i%2i%2i"; +const char* kMsgDSetOptions = "DSOP%4I"; +const char* kMsgQInfo = "QINF"; +const char* kMsgEIncompatible = "EICV%2i%2i"; +const char* kMsgEBusy = "EBSY"; +const char* kMsgEUnknown = "EUNK"; +const char* kMsgEBad = "EBAD"; diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index b4b8ae22..c4d3c99d 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -20,8 +20,11 @@ // protocol version number // 1.0: initial protocol // 1.1: adds KeyCode to key press, release, and repeat +// 1.2: adds mouse relative motion +// 1.3: adds keep alive and deprecates heartbeats, +// adds horizontal mouse scrolling static const SInt16 kProtocolMajorVersion = 1; -static const SInt16 kProtocolMinorVersion = 2; +static const SInt16 kProtocolMinorVersion = 3; // default contact port number static const UInt16 kDefaultPort = 24800; @@ -29,11 +32,16 @@ static const UInt16 kDefaultPort = 24800; // maximum total length for greeting returned by client static const UInt32 kMaxHelloLength = 1024; -// time between heartbeats (in seconds). negative value disables -// heartbeat. -static const double kHeartRate = -1.0; +// time between kMsgCKeepAlive (in seconds). a non-positive value disables +// keep alives. this is the default rate that can be overridden using an +// option. +static const double kKeepAliveRate = 3.0; -// number of skipped heartbeats that constitutes death +// number of skipped kMsgCKeepAlive messages that indicates a problem +static const double kKeepAlivesUntilDeath = 3.0; + +// obsolete heartbeat stuff +static const double kHeartRate = -1.0; static const double kHeartBeatsUntilDeath = 3.0; // direction constants @@ -74,13 +82,13 @@ enum EDirectionMask { // say hello to client; primary -> secondary // $1 = protocol major version number supported by server. $2 = // protocol minor version number supported by server. -static const char kMsgHello[] = "Synergy%2i%2i"; +extern const char* kMsgHello; // respond to hello from server; secondary -> primary // $1 = protocol major version number supported by client. $2 = // protocol minor version number supported by client. $3 = client // name. -static const char kMsgHelloBack[] = "Synergy%2i%2i%s"; +extern const char* kMsgHelloBack; // @@ -88,10 +96,10 @@ static const char kMsgHelloBack[] = "Synergy%2i%2i%s"; // // no operation; secondary -> primary -static const char kMsgCNoop[] = "CNOP"; +extern const char* kMsgCNoop; // close connection; primary -> secondary -static const char kMsgCClose[] = "CBYE"; +extern const char* kMsgCClose; // enter screen: primary -> secondary // entering screen at screen position $1 = x, $2 = y. x,y are @@ -101,7 +109,7 @@ static const char kMsgCClose[] = "CBYE"; // mask. this will have bits set for each toggle modifier key // that is activated on entry to the screen. the secondary screen // should adjust its toggle modifiers to reflect that state. -static const char kMsgCEnter[] = "CINN%2i%2i%4i%2i"; +extern const char* kMsgCEnter; // leave screen: primary -> secondary // leaving screen. the secondary screen should send clipboard @@ -110,28 +118,38 @@ static const char kMsgCEnter[] = "CINN%2i%2i%4i%2i"; // not received a kMsgCClipboard for with a greater sequence // number) and that were grabbed or have changed since the // last leave. -static const char kMsgCLeave[] = "COUT"; +extern const char* kMsgCLeave; // grab clipboard: primary <-> secondary // sent by screen when some other app on that screen grabs a // clipboard. $1 = the clipboard identifier, $2 = sequence number. // secondary screens must use the sequence number passed in the // most recent kMsgCEnter. the primary always sends 0. -static const char kMsgCClipboard[] = "CCLP%1i%4i"; +extern const char* kMsgCClipboard; // screensaver change: primary -> secondary // screensaver on primary has started ($1 == 1) or closed ($1 == 0) -static const char kMsgCScreenSaver[] = "CSEC%1i"; +extern const char* kMsgCScreenSaver; // reset options: primary -> secondary // client should reset all of its options to their defaults. -static const char kMsgCResetOptions[] = "CROP"; +extern const char* kMsgCResetOptions; // resolution change acknowledgment: primary -> secondary // sent by primary in response to a secondary screen's kMsgDInfo. // this is sent for every kMsgDInfo, whether or not the primary // had sent a kMsgQInfo. -static const char kMsgCInfoAck[] = "CIAK"; +extern const char* kMsgCInfoAck; + +// keep connection alive: primary <-> secondary +// sent by the server periodically to verify that connections are still +// up and running. clients must reply in kind on receipt. if the server +// gets an error sending the message or does not receive a reply within +// a reasonable time then the server disconnects the client. if the +// client doesn't receive these (or any message) periodically then it +// should disconnect from the server. the appropriate interval is +// defined by an option. +extern const char* kMsgCKeepAlive; // @@ -149,52 +167,57 @@ static const char kMsgCInfoAck[] = "CIAK"; // the press. this can happen with combining (dead) keys or if // the keyboard layouts are not identical and the user releases // a modifier key before releasing the modified key. -static const char kMsgDKeyDown[] = "DKDN%2i%2i%2i"; +extern const char* kMsgDKeyDown; // key pressed 1.0: same as above but without KeyButton -static const char kMsgDKeyDown1_0[] = "DKDN%2i%2i"; +extern const char* kMsgDKeyDown1_0; // key auto-repeat: primary -> secondary // $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton -static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i%2i"; +extern const char* kMsgDKeyRepeat; // key auto-repeat 1.0: same as above but without KeyButton -static const char kMsgDKeyRepeat1_0[] = "DKRP%2i%2i%2i"; +extern const char* kMsgDKeyRepeat1_0; // key released: primary -> secondary // $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton -static const char kMsgDKeyUp[] = "DKUP%2i%2i%2i"; +extern const char* kMsgDKeyUp; // key released 1.0: same as above but without KeyButton -static const char kMsgDKeyUp1_0[] = "DKUP%2i%2i"; +extern const char* kMsgDKeyUp1_0; // mouse button pressed: primary -> secondary // $1 = ButtonID -static const char kMsgDMouseDown[] = "DMDN%1i"; +extern const char* kMsgDMouseDown; // mouse button released: primary -> secondary // $1 = ButtonID -static const char kMsgDMouseUp[] = "DMUP%1i"; +extern const char* kMsgDMouseUp; // mouse moved: primary -> secondary // $1 = x, $2 = y. x,y are absolute screen coordinates. -static const char kMsgDMouseMove[] = "DMMV%2i%2i"; +extern const char* kMsgDMouseMove; // relative mouse move: primary -> secondary // $1 = dx, $2 = dy. dx,dy are motion deltas. -static const char kMsgDMouseRelMove[] = "DMRM%2i%2i"; +extern const char* kMsgDMouseRelMove; -// mouse button pressed: primary -> secondary -// $1 = delta. the delta should be +120 for one tick forward (away -// from the user) and -120 for one tick backward (toward the user). -static const char kMsgDMouseWheel[] = "DMWM%2i"; +// mouse scroll: primary -> secondary +// $1 = xDelta, $2 = yDelta. the delta should be +120 for one tick forward +// (away from the user) or right and -120 for one tick backward (toward +// the user) or left. +extern const char* kMsgDMouseWheel; + +// mouse vertical scroll: primary -> secondary +// like as kMsgDMouseWheel except only sends $1 = yDelta. +extern const char* kMsgDMouseWheel1_0; // clipboard data: primary <-> secondary // $2 = sequence number, $3 = clipboard data. the sequence number // is 0 when sent by the primary. secondary screens should use the // sequence number from the most recent kMsgCEnter. $1 = clipboard // identifier. -static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; +extern const char* kMsgDClipboard; // client data: secondary -> primary // $1 = coordinate of leftmost pixel on secondary screen, @@ -210,12 +233,12 @@ static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; // should ignore any kMsgDMouseMove messages until it receives a // kMsgCInfoAck in order to prevent attempts to move the mouse off // the new screen area. -static const char kMsgDInfo[] = "DINF%2i%2i%2i%2i%2i%2i%2i"; +extern const char* kMsgDInfo; // set options: primary -> secondary // client should set the given option/value pairs. $1 = option/value // pairs. -static const char kMsgDSetOptions[] = "DSOP%4I"; +extern const char* kMsgDSetOptions; // @@ -224,7 +247,7 @@ static const char kMsgDSetOptions[] = "DSOP%4I"; // query screen info: primary -> secondary // client should reply with a kMsgDInfo. -static const char kMsgQInfo[] = "QINF"; +extern const char* kMsgQInfo; // @@ -233,19 +256,19 @@ static const char kMsgQInfo[] = "QINF"; // incompatible versions: primary -> secondary // $1 = major version of primary, $2 = minor version of primary. -static const char kMsgEIncompatible[] = "EICV%2i%2i"; +extern const char* kMsgEIncompatible; // name provided when connecting is already in use: primary -> secondary -static const char kMsgEBusy[] = "EBSY"; +extern const char* kMsgEBusy; // unknown client: primary -> secondary // name provided when connecting is not in primary's screen // configuration map. -static const char kMsgEUnknown[] = "EUNK"; +extern const char* kMsgEUnknown; // protocol violation: primary -> secondary // primary should disconnect after sending this message. -static const char kMsgEBad[] = "EBAD"; +extern const char* kMsgEBad; // diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp index 9cf43e79..33300e0c 100644 --- a/lib/synergy/libsynergy.dsp +++ b/lib/synergy/libsynergy.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir "..\..\gen\build" # 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 /W4 /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\libsynergy.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\libsynergy.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir "..\..\gen\debug" # 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 /W4 /Gm /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\libsynergy.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\libsynergy.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -91,6 +91,10 @@ SOURCE=.\CClipboard.cpp # End Source File # Begin Source File +SOURCE=.\CKeyMap.cpp +# End Source File +# Begin Source File + SOURCE=.\CKeyState.cpp # End Source File # Begin Source File @@ -127,6 +131,14 @@ SOURCE=.\IScreen.cpp # End Source File # Begin Source File +SOURCE=.\KeyTypes.cpp +# End Source File +# Begin Source File + +SOURCE=.\ProtocolTypes.cpp +# End Source File +# Begin Source File + SOURCE=.\XScreen.cpp # End Source File # Begin Source File @@ -143,6 +155,10 @@ SOURCE=.\CClipboard.h # End Source File # Begin Source File +SOURCE=.\CKeyMap.h +# End Source File +# Begin Source File + SOURCE=.\CKeyState.h # End Source File # Begin Source File diff --git a/synergy.dsw b/synergy.dsw index 225c09ed..455df6a7 100644 --- a/synergy.dsw +++ b/synergy.dsw @@ -126,9 +126,15 @@ Package=<4> Project_Dep_Name base End Project Dependency Begin Project Dependency + Project_Dep_Name exec + End Project Dependency + Begin Project Dependency Project_Dep_Name io End Project Dependency Begin Project Dependency + Project_Dep_Name libsynergy + End Project Dependency + Begin Project Dependency Project_Dep_Name mt End Project Dependency Begin Project Dependency @@ -140,9 +146,6 @@ Package=<4> Begin Project Dependency Project_Dep_Name server End Project Dependency - Begin Project Dependency - Project_Dep_Name exec - End Project Dependency }}} ############################################################################### diff --git a/synergy.xcode/project.pbxproj b/synergy.xcode/project.pbxproj index ae2d35d4..75cd3385 100644 --- a/synergy.xcode/project.pbxproj +++ b/synergy.xcode/project.pbxproj @@ -3,11 +3,394 @@ archiveVersion = 1; classes = { }; - objectVersion = 39; + objectVersion = 42; objects = { - 014CEA460018CE2711CA2923 = { - buildRules = ( + +/* Begin PBXAggregateTarget section */ + 4CD033E80650965F00525ED1 /* ALL */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 68FBBB18089F06C20016DF44 /* Build configuration list for PBXAggregateTarget "ALL" */; + buildPhases = ( ); + buildSettings = { + PRODUCT_NAME = ALL; + }; + dependencies = ( + 4CD033EA0650968500525ED1 /* PBXTargetDependency */, + 4CD033EC0650968500525ED1 /* PBXTargetDependency */, + ); + name = ALL; + productName = ALL; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 4C537F14064E293000D3815C /* COSXServerTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F0F064E293000D3815C /* COSXServerTaskBarReceiver.cpp */; }; + 4C537F15064E293000D3815C /* COSXServerTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F10064E293000D3815C /* COSXServerTaskBarReceiver.h */; }; + 4C537F16064E293000D3815C /* CServerTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F11064E293000D3815C /* CServerTaskBarReceiver.cpp */; }; + 4C537F17064E293000D3815C /* CServerTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F12064E293000D3815C /* CServerTaskBarReceiver.h */; }; + 4C537F18064E293000D3815C /* synergys.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F13064E293000D3815C /* synergys.cpp */; }; + 4C537F44064E2A0F00D3815C /* CClientListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F32064E2A0F00D3815C /* CClientListener.cpp */; }; + 4C537F45064E2A0F00D3815C /* CClientListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F33064E2A0F00D3815C /* CClientListener.h */; }; + 4C537F46064E2A0F00D3815C /* CClientProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F34064E2A0F00D3815C /* CClientProxy.cpp */; }; + 4C537F47064E2A0F00D3815C /* CClientProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F35064E2A0F00D3815C /* CClientProxy.h */; }; + 4C537F48064E2A0F00D3815C /* CClientProxy1_0.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F36064E2A0F00D3815C /* CClientProxy1_0.cpp */; }; + 4C537F49064E2A0F00D3815C /* CClientProxy1_0.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F37064E2A0F00D3815C /* CClientProxy1_0.h */; }; + 4C537F4A064E2A0F00D3815C /* CClientProxy1_1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F38064E2A0F00D3815C /* CClientProxy1_1.cpp */; }; + 4C537F4B064E2A0F00D3815C /* CClientProxy1_1.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F39064E2A0F00D3815C /* CClientProxy1_1.h */; }; + 4C537F4C064E2A0F00D3815C /* CClientProxy1_2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F3A064E2A0F00D3815C /* CClientProxy1_2.cpp */; }; + 4C537F4D064E2A0F00D3815C /* CClientProxy1_2.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F3B064E2A0F00D3815C /* CClientProxy1_2.h */; }; + 4C537F4E064E2A0F00D3815C /* CClientProxyUnknown.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F3C064E2A0F00D3815C /* CClientProxyUnknown.cpp */; }; + 4C537F4F064E2A0F00D3815C /* CClientProxyUnknown.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F3D064E2A0F00D3815C /* CClientProxyUnknown.h */; }; + 4C537F50064E2A0F00D3815C /* CConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F3E064E2A0F00D3815C /* CConfig.cpp */; }; + 4C537F51064E2A0F00D3815C /* CConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F3F064E2A0F00D3815C /* CConfig.h */; }; + 4C537F52064E2A0F00D3815C /* CPrimaryClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F40064E2A0F00D3815C /* CPrimaryClient.cpp */; }; + 4C537F53064E2A0F00D3815C /* CPrimaryClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F41064E2A0F00D3815C /* CPrimaryClient.h */; }; + 4C537F54064E2A0F00D3815C /* CServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F42064E2A0F00D3815C /* CServer.cpp */; }; + 4C537F55064E2A0F00D3815C /* CServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F43064E2A0F00D3815C /* CServer.h */; }; + 4C537F5B064E2B4200D3815C /* libcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C5E868D0648C2ED003C637B /* libcommon.a */; }; + 4C537F5C064E2B4200D3815C /* libarch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437B5063E443800969041 /* libarch.a */; }; + 4C537F5D064E2B4200D3815C /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437D1063E44C200969041 /* libbase.a */; }; + 4C537F5E064E2B4200D3815C /* libmt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43801063E45F200969041 /* libmt.a */; }; + 4C537F5F064E2B4200D3815C /* libio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43824063E46AB00969041 /* libio.a */; }; + 4C537F60064E2B4200D3815C /* libnet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43848063E475800969041 /* libnet.a */; }; + 4C537F61064E2B4200D3815C /* libserver.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C537F31064E29F800D3815C /* libserver.a */; }; + 4C537F62064E2B4200D3815C /* libsynergy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB4386E063E47F800969041 /* libsynergy.a */; }; + 4C537F63064E2B4200D3815C /* libplatform.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB438B1063E488600969041 /* libplatform.a */; }; + 4C537F64064E2B4B00D3815C /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43923063E4B1300969041 /* Carbon.framework */; }; + 4C5E86A90648C412003C637B /* CArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86920648C412003C637B /* CArch.cpp */; }; + 4C5E86AA0648C412003C637B /* CArch.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86930648C412003C637B /* CArch.h */; }; + 4C5E86AB0648C412003C637B /* CArchConsoleUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86940648C412003C637B /* CArchConsoleUnix.cpp */; }; + 4C5E86AC0648C412003C637B /* CArchConsoleUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86950648C412003C637B /* CArchConsoleUnix.h */; }; + 4C5E86AD0648C412003C637B /* CArchDaemonUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86960648C412003C637B /* CArchDaemonUnix.cpp */; }; + 4C5E86AE0648C412003C637B /* CArchDaemonUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86970648C412003C637B /* CArchDaemonUnix.h */; }; + 4C5E86AF0648C412003C637B /* CArchFileUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86980648C412003C637B /* CArchFileUnix.cpp */; }; + 4C5E86B00648C412003C637B /* CArchFileUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86990648C412003C637B /* CArchFileUnix.h */; }; + 4C5E86B10648C412003C637B /* CArchLogUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E869A0648C412003C637B /* CArchLogUnix.cpp */; }; + 4C5E86B20648C412003C637B /* CArchLogUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E869B0648C412003C637B /* CArchLogUnix.h */; }; + 4C5E86B30648C412003C637B /* CArchMultithreadPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E869C0648C412003C637B /* CArchMultithreadPosix.cpp */; }; + 4C5E86B40648C412003C637B /* CArchMultithreadPosix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E869D0648C412003C637B /* CArchMultithreadPosix.h */; }; + 4C5E86B50648C412003C637B /* CArchNetworkBSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E869E0648C412003C637B /* CArchNetworkBSD.cpp */; }; + 4C5E86B60648C412003C637B /* CArchNetworkBSD.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E869F0648C412003C637B /* CArchNetworkBSD.h */; }; + 4C5E86B70648C412003C637B /* CArchSleepUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A00648C412003C637B /* CArchSleepUnix.cpp */; }; + 4C5E86B80648C412003C637B /* CArchSleepUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A10648C412003C637B /* CArchSleepUnix.h */; }; + 4C5E86B90648C412003C637B /* CArchStringUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A20648C412003C637B /* CArchStringUnix.cpp */; }; + 4C5E86BA0648C412003C637B /* CArchStringUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A30648C412003C637B /* CArchStringUnix.h */; }; + 4C5E86BB0648C412003C637B /* CArchTimeUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A40648C412003C637B /* CArchTimeUnix.cpp */; }; + 4C5E86BC0648C412003C637B /* CArchTimeUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A50648C412003C637B /* CArchTimeUnix.h */; }; + 4C5E86BE0648C412003C637B /* XArchUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A70648C412003C637B /* XArchUnix.cpp */; }; + 4C5E86BF0648C412003C637B /* XArchUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A80648C412003C637B /* XArchUnix.h */; }; + 4C5E86C30648C653003C637B /* CArchDaemonNone.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86C10648C653003C637B /* CArchDaemonNone.h */; }; + 4C5E86CC0648C726003C637B /* Version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86CB0648C725003C637B /* Version.cpp */; }; + 4C5E86ED0648C7B9003C637B /* CEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86CD0648C7B9003C637B /* CEvent.cpp */; }; + 4C5E86EE0648C7B9003C637B /* CEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86CE0648C7B9003C637B /* CEvent.h */; }; + 4C5E86EF0648C7B9003C637B /* CEventQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86CF0648C7B9003C637B /* CEventQueue.cpp */; }; + 4C5E86F00648C7B9003C637B /* CEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D00648C7B9003C637B /* CEventQueue.h */; }; + 4C5E86F10648C7B9003C637B /* CFunctionEventJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D10648C7B9003C637B /* CFunctionEventJob.cpp */; }; + 4C5E86F20648C7B9003C637B /* CFunctionEventJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D20648C7B9003C637B /* CFunctionEventJob.h */; }; + 4C5E86F30648C7B9003C637B /* CFunctionJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D30648C7B9003C637B /* CFunctionJob.cpp */; }; + 4C5E86F40648C7B9003C637B /* CFunctionJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D40648C7B9003C637B /* CFunctionJob.h */; }; + 4C5E86F50648C7B9003C637B /* CLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D50648C7B9003C637B /* CLog.cpp */; }; + 4C5E86F60648C7B9003C637B /* CLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D60648C7B9003C637B /* CLog.h */; }; + 4C5E86F70648C7B9003C637B /* CPriorityQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D70648C7B9003C637B /* CPriorityQueue.h */; }; + 4C5E86F80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp */; }; + 4C5E86F90648C7B9003C637B /* CSimpleEventQueueBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D90648C7B9003C637B /* CSimpleEventQueueBuffer.h */; }; + 4C5E86FA0648C7B9003C637B /* CStopwatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86DA0648C7B9003C637B /* CStopwatch.cpp */; }; + 4C5E86FB0648C7B9003C637B /* CStopwatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86DB0648C7B9003C637B /* CStopwatch.h */; }; + 4C5E86FC0648C7B9003C637B /* CString.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86DC0648C7B9003C637B /* CString.h */; }; + 4C5E86FD0648C7B9003C637B /* CStringUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86DD0648C7B9003C637B /* CStringUtil.cpp */; }; + 4C5E86FE0648C7B9003C637B /* CStringUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86DE0648C7B9003C637B /* CStringUtil.h */; }; + 4C5E86FF0648C7B9003C637B /* CUnicode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86DF0648C7B9003C637B /* CUnicode.cpp */; }; + 4C5E87000648C7B9003C637B /* CUnicode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E00648C7B9003C637B /* CUnicode.h */; }; + 4C5E87010648C7B9003C637B /* IEventJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E10648C7B9003C637B /* IEventJob.h */; }; + 4C5E87020648C7B9003C637B /* IEventQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86E20648C7B9003C637B /* IEventQueue.cpp */; }; + 4C5E87030648C7B9003C637B /* IEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E30648C7B9003C637B /* IEventQueue.h */; }; + 4C5E87040648C7B9003C637B /* IEventQueueBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E40648C7B9003C637B /* IEventQueueBuffer.h */; }; + 4C5E87050648C7B9003C637B /* IJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E50648C7B9003C637B /* IJob.h */; }; + 4C5E87060648C7B9003C637B /* ILogOutputter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E60648C7B9003C637B /* ILogOutputter.h */; }; + 4C5E87070648C7B9003C637B /* LogOutputters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86E70648C7B9003C637B /* LogOutputters.cpp */; }; + 4C5E87080648C7B9003C637B /* LogOutputters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E80648C7B9003C637B /* LogOutputters.h */; }; + 4C5E87090648C7B9003C637B /* TMethodEventJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E90648C7B9003C637B /* TMethodEventJob.h */; }; + 4C5E870A0648C7B9003C637B /* TMethodJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86EA0648C7B9003C637B /* TMethodJob.h */; }; + 4C5E870B0648C7B9003C637B /* XBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86EB0648C7B9003C637B /* XBase.cpp */; }; + 4C5E870C0648C7B9003C637B /* XBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86EC0648C7B9003C637B /* XBase.h */; }; + 4C5E87180648C809003C637B /* CCondVar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E870D0648C809003C637B /* CCondVar.cpp */; }; + 4C5E87190648C809003C637B /* CCondVar.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E870E0648C809003C637B /* CCondVar.h */; }; + 4C5E871A0648C809003C637B /* CLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E870F0648C809003C637B /* CLock.cpp */; }; + 4C5E871B0648C809003C637B /* CLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87100648C809003C637B /* CLock.h */; }; + 4C5E871C0648C809003C637B /* CMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87110648C809003C637B /* CMutex.cpp */; }; + 4C5E871D0648C809003C637B /* CMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87120648C809003C637B /* CMutex.h */; }; + 4C5E871E0648C809003C637B /* CThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87130648C809003C637B /* CThread.cpp */; }; + 4C5E871F0648C809003C637B /* CThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87140648C809003C637B /* CThread.h */; }; + 4C5E87200648C809003C637B /* XMT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87150648C809003C637B /* XMT.cpp */; }; + 4C5E87210648C809003C637B /* XMT.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87160648C809003C637B /* XMT.h */; }; + 4C5E87220648C809003C637B /* XThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87170648C809003C637B /* XThread.h */; }; + 4C5E872C0648C83C003C637B /* CStreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87230648C83C003C637B /* CStreamBuffer.cpp */; }; + 4C5E872D0648C83C003C637B /* CStreamBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87240648C83C003C637B /* CStreamBuffer.h */; }; + 4C5E872E0648C83C003C637B /* CStreamFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87250648C83C003C637B /* CStreamFilter.cpp */; }; + 4C5E872F0648C83C003C637B /* CStreamFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87260648C83C003C637B /* CStreamFilter.h */; }; + 4C5E87300648C83C003C637B /* IStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87270648C83C003C637B /* IStream.cpp */; }; + 4C5E87310648C83C003C637B /* IStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87280648C83C003C637B /* IStream.h */; }; + 4C5E87320648C83C003C637B /* IStreamFilterFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87290648C83C003C637B /* IStreamFilterFactory.h */; }; + 4C5E87330648C83C003C637B /* XIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E872A0648C83C003C637B /* XIO.cpp */; }; + 4C5E87340648C83C003C637B /* XIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E872B0648C83C003C637B /* XIO.h */; }; + 4C5E874A0648C870003C637B /* CNetworkAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87350648C870003C637B /* CNetworkAddress.cpp */; }; + 4C5E874B0648C870003C637B /* CNetworkAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87360648C870003C637B /* CNetworkAddress.h */; }; + 4C5E874C0648C870003C637B /* CSocketMultiplexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87370648C870003C637B /* CSocketMultiplexer.cpp */; }; + 4C5E874D0648C870003C637B /* CSocketMultiplexer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87380648C870003C637B /* CSocketMultiplexer.h */; }; + 4C5E874E0648C870003C637B /* CTCPListenSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87390648C870003C637B /* CTCPListenSocket.cpp */; }; + 4C5E874F0648C870003C637B /* CTCPListenSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E873A0648C870003C637B /* CTCPListenSocket.h */; }; + 4C5E87500648C870003C637B /* CTCPSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E873B0648C870003C637B /* CTCPSocket.cpp */; }; + 4C5E87510648C870003C637B /* CTCPSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E873C0648C870003C637B /* CTCPSocket.h */; }; + 4C5E87520648C870003C637B /* CTCPSocketFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E873D0648C870003C637B /* CTCPSocketFactory.cpp */; }; + 4C5E87530648C870003C637B /* CTCPSocketFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E873E0648C870003C637B /* CTCPSocketFactory.h */; }; + 4C5E87540648C870003C637B /* IDataSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E873F0648C870003C637B /* IDataSocket.cpp */; }; + 4C5E87550648C870003C637B /* IDataSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87400648C870003C637B /* IDataSocket.h */; }; + 4C5E87560648C870003C637B /* IListenSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87410648C870003C637B /* IListenSocket.cpp */; }; + 4C5E87570648C870003C637B /* IListenSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87420648C870003C637B /* IListenSocket.h */; }; + 4C5E87580648C870003C637B /* ISocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87430648C870003C637B /* ISocket.cpp */; }; + 4C5E87590648C870003C637B /* ISocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87440648C870003C637B /* ISocket.h */; }; + 4C5E875A0648C870003C637B /* ISocketFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87450648C870003C637B /* ISocketFactory.h */; }; + 4C5E875B0648C870003C637B /* ISocketMultiplexerJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87460648C870003C637B /* ISocketMultiplexerJob.h */; }; + 4C5E875C0648C870003C637B /* TSocketMultiplexerMethodJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87470648C870003C637B /* TSocketMultiplexerMethodJob.h */; }; + 4C5E875D0648C870003C637B /* XSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87480648C870003C637B /* XSocket.cpp */; }; + 4C5E875E0648C870003C637B /* XSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87490648C870003C637B /* XSocket.h */; }; + 4C5E87800648C8BD003C637B /* CClipboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E875F0648C8BD003C637B /* CClipboard.cpp */; }; + 4C5E87810648C8BD003C637B /* CClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87600648C8BD003C637B /* CClipboard.h */; }; + 4C5E87820648C8BD003C637B /* CKeyState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87610648C8BD003C637B /* CKeyState.cpp */; }; + 4C5E87830648C8BD003C637B /* CKeyState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87620648C8BD003C637B /* CKeyState.h */; }; + 4C5E87840648C8BD003C637B /* ClipboardTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87630648C8BD003C637B /* ClipboardTypes.h */; }; + 4C5E87850648C8BD003C637B /* CPacketStreamFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87640648C8BD003C637B /* CPacketStreamFilter.cpp */; }; + 4C5E87860648C8BD003C637B /* CPacketStreamFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87650648C8BD003C637B /* CPacketStreamFilter.h */; }; + 4C5E87870648C8BD003C637B /* CPlatformScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87660648C8BD003C637B /* CPlatformScreen.cpp */; }; + 4C5E87880648C8BD003C637B /* CPlatformScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87670648C8BD003C637B /* CPlatformScreen.h */; }; + 4C5E87890648C8BD003C637B /* CProtocolUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87680648C8BD003C637B /* CProtocolUtil.cpp */; }; + 4C5E878A0648C8BD003C637B /* CProtocolUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87690648C8BD003C637B /* CProtocolUtil.h */; }; + 4C5E878B0648C8BD003C637B /* CScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E876A0648C8BD003C637B /* CScreen.cpp */; }; + 4C5E878C0648C8BD003C637B /* CScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E876B0648C8BD003C637B /* CScreen.h */; }; + 4C5E878D0648C8BD003C637B /* IClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E876C0648C8BD003C637B /* IClient.h */; }; + 4C5E878E0648C8BD003C637B /* IClipboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E876D0648C8BD003C637B /* IClipboard.cpp */; }; + 4C5E878F0648C8BD003C637B /* IClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E876E0648C8BD003C637B /* IClipboard.h */; }; + 4C5E87900648C8BD003C637B /* IKeyState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E876F0648C8BD003C637B /* IKeyState.cpp */; }; + 4C5E87910648C8BD003C637B /* IKeyState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87700648C8BD003C637B /* IKeyState.h */; }; + 4C5E87920648C8BD003C637B /* IPlatformScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87710648C8BD003C637B /* IPlatformScreen.h */; }; + 4C5E87930648C8BD003C637B /* IPrimaryScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87720648C8BD003C637B /* IPrimaryScreen.cpp */; }; + 4C5E87940648C8BD003C637B /* IPrimaryScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87730648C8BD003C637B /* IPrimaryScreen.h */; }; + 4C5E87950648C8BD003C637B /* IScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87740648C8BD003C637B /* IScreen.cpp */; }; + 4C5E87960648C8BD003C637B /* IScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87750648C8BD003C637B /* IScreen.h */; }; + 4C5E87970648C8BD003C637B /* IScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87760648C8BD003C637B /* IScreenSaver.h */; }; + 4C5E87980648C8BD003C637B /* ISecondaryScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87770648C8BD003C637B /* ISecondaryScreen.h */; }; + 4C5E87990648C8BD003C637B /* KeyTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87780648C8BD003C637B /* KeyTypes.h */; }; + 4C5E879A0648C8BD003C637B /* MouseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87790648C8BD003C637B /* MouseTypes.h */; }; + 4C5E879B0648C8BD003C637B /* OptionTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877A0648C8BD003C637B /* OptionTypes.h */; }; + 4C5E879C0648C8BD003C637B /* ProtocolTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877B0648C8BD003C637B /* ProtocolTypes.h */; }; + 4C5E879D0648C8BD003C637B /* XScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E877C0648C8BD003C637B /* XScreen.cpp */; }; + 4C5E879E0648C8BD003C637B /* XScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877D0648C8BD003C637B /* XScreen.h */; }; + 4C5E879F0648C8BD003C637B /* XSynergy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E877E0648C8BD003C637B /* XSynergy.cpp */; }; + 4C5E87A00648C8BD003C637B /* XSynergy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877F0648C8BD003C637B /* XSynergy.h */; }; + 4C5E87AD0648C913003C637B /* COSXClipboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A10648C913003C637B /* COSXClipboard.cpp */; }; + 4C5E87AE0648C913003C637B /* COSXClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A20648C913003C637B /* COSXClipboard.h */; }; + 4C5E87AF0648C913003C637B /* COSXEventQueueBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A30648C913003C637B /* COSXEventQueueBuffer.cpp */; }; + 4C5E87B00648C913003C637B /* COSXEventQueueBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A40648C913003C637B /* COSXEventQueueBuffer.h */; }; + 4C5E87B10648C913003C637B /* COSXKeyState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A50648C913003C637B /* COSXKeyState.cpp */; }; + 4C5E87B20648C913003C637B /* COSXKeyState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A60648C913003C637B /* COSXKeyState.h */; }; + 4C5E87B30648C913003C637B /* COSXScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A70648C913003C637B /* COSXScreen.cpp */; }; + 4C5E87B40648C913003C637B /* COSXScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A80648C913003C637B /* COSXScreen.h */; }; + 4C5E87BD0648C969003C637B /* CClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87B90648C969003C637B /* CClient.cpp */; }; + 4C5E87BE0648C969003C637B /* CClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87BA0648C969003C637B /* CClient.h */; }; + 4C5E87BF0648C969003C637B /* CServerProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87BB0648C969003C637B /* CServerProxy.cpp */; }; + 4C5E87C00648C969003C637B /* CServerProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87BC0648C969003C637B /* CServerProxy.h */; }; + 4C5E87C60648C9D2003C637B /* CClientTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87C10648C9D2003C637B /* CClientTaskBarReceiver.cpp */; }; + 4C5E87C70648C9D2003C637B /* CClientTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87C20648C9D2003C637B /* CClientTaskBarReceiver.h */; }; + 4C5E87C80648C9D2003C637B /* COSXClientTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87C30648C9D2003C637B /* COSXClientTaskBarReceiver.cpp */; }; + 4C5E87C90648C9D2003C637B /* COSXClientTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87C40648C9D2003C637B /* COSXClientTaskBarReceiver.h */; }; + 4C5E87CA0648C9D2003C637B /* synergyc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87C50648C9D2003C637B /* synergyc.cpp */; }; + 4C5E87CF0648CA4B003C637B /* libcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C5E868D0648C2ED003C637B /* libcommon.a */; }; + 4C5E87D10648CA75003C637B /* XArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87D00648CA75003C637B /* XArch.cpp */; }; + 4C7D7CDA066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CDB066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CDC066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CDD066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CDE066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CDF066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE0066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE1066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE2066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE3066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE4066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4C7D7CE5066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; + 4CB43934063E4B4A00969041 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43923063E4B1300969041 /* Carbon.framework */; }; + 4CD034950650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD0348F0650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp */; }; + 4CD034960650B6F100525ED1 /* COSXClipboardAnyTextConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CD034900650B6F100525ED1 /* COSXClipboardAnyTextConverter.h */; }; + 4CD034970650B6F100525ED1 /* COSXClipboardTextConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD034910650B6F100525ED1 /* COSXClipboardTextConverter.cpp */; }; + 4CD034980650B6F100525ED1 /* COSXClipboardTextConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CD034920650B6F100525ED1 /* COSXClipboardTextConverter.h */; }; + 4CD034990650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD034930650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp */; }; + 4CD0349A0650B6F100525ED1 /* COSXClipboardUTF16Converter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CD034940650B6F100525ED1 /* COSXClipboardUTF16Converter.h */; }; + 6828A1AA081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */; }; + 6828A1AB081DF7AB003D9989 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A8081DF7AB003D9989 /* Foundation.framework */; }; + 6828A1AC081DF7AB003D9989 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A9081DF7AB003D9989 /* IOKit.framework */; }; + 6828A1AD081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */; }; + 6828A1AE081DF7AB003D9989 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A8081DF7AB003D9989 /* Foundation.framework */; }; + 6828A1AF081DF7AB003D9989 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A9081DF7AB003D9989 /* IOKit.framework */; }; + 6828A1C2081DF9EB003D9989 /* OSXScreenSaverControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 6828A1C1081DF9EB003D9989 /* OSXScreenSaverControl.h */; }; + 6828A238081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A237081DFAF9003D9989 /* ScreenSaver.framework */; }; + 6828A239081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A237081DFAF9003D9989 /* ScreenSaver.framework */; }; + 684B63620996FB2800FE7CC7 /* CClientProxy1_3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 684B63600996FB2800FE7CC7 /* CClientProxy1_3.cpp */; }; + 684B63630996FB2800FE7CC7 /* CClientProxy1_3.h in Headers */ = {isa = PBXBuildFile; fileRef = 684B63610996FB2800FE7CC7 /* CClientProxy1_3.h */; }; + 68871685073EBF7000C5ABE7 /* CArchSystemUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68871676073EBF6F00C5ABE7 /* CArchSystemUnix.cpp */; }; + 68871686073EBF7000C5ABE7 /* CArchSystemUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871677073EBF6F00C5ABE7 /* CArchSystemUnix.h */; }; + 68871687073EBF7000C5ABE7 /* IArchConsole.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871678073EBF6F00C5ABE7 /* IArchConsole.h */; }; + 68871688073EBF7000C5ABE7 /* IArchDaemon.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871679073EBF6F00C5ABE7 /* IArchDaemon.h */; }; + 68871689073EBF7000C5ABE7 /* IArchFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167A073EBF6F00C5ABE7 /* IArchFile.h */; }; + 6887168A073EBF7000C5ABE7 /* IArchLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167B073EBF6F00C5ABE7 /* IArchLog.h */; }; + 6887168B073EBF7000C5ABE7 /* IArchMultithread.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167C073EBF6F00C5ABE7 /* IArchMultithread.h */; }; + 6887168C073EBF7000C5ABE7 /* IArchNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167D073EBF6F00C5ABE7 /* IArchNetwork.h */; }; + 6887168D073EBF7000C5ABE7 /* IArchSleep.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167E073EBF6F00C5ABE7 /* IArchSleep.h */; }; + 6887168E073EBF7000C5ABE7 /* IArchString.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167F073EBF6F00C5ABE7 /* IArchString.h */; }; + 6887168F073EBF7000C5ABE7 /* IArchSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871680073EBF6F00C5ABE7 /* IArchSystem.h */; }; + 68871690073EBF7000C5ABE7 /* IArchTaskBar.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871681073EBF7000C5ABE7 /* IArchTaskBar.h */; }; + 68871691073EBF7000C5ABE7 /* IArchTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871682073EBF7000C5ABE7 /* IArchTaskBarReceiver.h */; }; + 68871692073EBF7000C5ABE7 /* IArchTime.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871683073EBF7000C5ABE7 /* IArchTime.h */; }; + 68871693073EBF7000C5ABE7 /* XArch.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871684073EBF7000C5ABE7 /* XArch.h */; }; + 6887169B073EC03800C5ABE7 /* MacOSXPrecomp.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871699073EC02100C5ABE7 /* MacOSXPrecomp.h */; }; + 688925A909DF64B6002EB18C /* CBaseClientProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 688925A709DF64B6002EB18C /* CBaseClientProxy.cpp */; }; + 688925AA09DF64B6002EB18C /* CBaseClientProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 688925A809DF64B6002EB18C /* CBaseClientProxy.h */; }; + 68968F5D073EC484004B2F9B /* CArchDaemonNone.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68968F5A073EC484004B2F9B /* CArchDaemonNone.cpp */; }; + 68968F5E073EC484004B2F9B /* CArchTaskBarXWindows.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68968F5B073EC484004B2F9B /* CArchTaskBarXWindows.cpp */; }; + 68968F5F073EC484004B2F9B /* CArchTaskBarXWindows.h in Headers */ = {isa = PBXBuildFile; fileRef = 68968F5C073EC484004B2F9B /* CArchTaskBarXWindows.h */; }; + 689D73340956490500FFAB1D /* CInputFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 689D73320956490500FFAB1D /* CInputFilter.cpp */; }; + 689D73350956490500FFAB1D /* CInputFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 689D73330956490500FFAB1D /* CInputFilter.h */; }; + 689D7339095649AF00FFAB1D /* CKeyMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 689D7337095649AF00FFAB1D /* CKeyMap.cpp */; }; + 689D733A095649AF00FFAB1D /* CKeyMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 689D7338095649AF00FFAB1D /* CKeyMap.h */; }; + 68D87A6509A00D8800856835 /* KeyTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D87A6409A00D8800856835 /* KeyTypes.cpp */; }; + 68FBBB21089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */; }; + 68FBBB22089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */; }; + 68FBBB24089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */; }; + 68FBBB25089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */; }; + 68FBBB28089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */; }; + 68FBBB29089F072D0016DF44 /* COSXScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */; }; + 68FBBB2A089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */; }; + 68FBBB2B089F072D0016DF44 /* COSXScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */; }; + 68FBBB2C089F07810016DF44 /* COSXScreenSaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */; }; + 68FBBB2D089F07920016DF44 /* COSXScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */; }; + 68FBBB2E089F07970016DF44 /* COSXScreenSaverUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */; }; + 68FBBB2F089F079B0016DF44 /* COSXScreenSaverUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */; }; + 68FBBB5B089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */; }; + 68FBBB5C089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */; }; + 68FBBB5D089F1BCE0016DF44 /* ProtocolTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */; }; + CAB4475F081E367700724B8D /* libclient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43778063E406A00969041 /* libclient.a */; }; + CAB44760081E368100724B8D /* libarch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437B5063E443800969041 /* libarch.a */; }; + CAB44761081E368200724B8D /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437D1063E44C200969041 /* libbase.a */; }; + CAB44762081E368300724B8D /* libclient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43778063E406A00969041 /* libclient.a */; }; + CAB44764081E368700724B8D /* libio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43824063E46AB00969041 /* libio.a */; }; + CAB44765081E368800724B8D /* libmt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43801063E45F200969041 /* libmt.a */; }; + CAB44766081E368A00724B8D /* libnet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43848063E475800969041 /* libnet.a */; }; + CAB44767081E368B00724B8D /* libplatform.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB438B1063E488600969041 /* libplatform.a */; }; + CAB44768081E368D00724B8D /* libsynergy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB4386E063E47F800969041 /* libsynergy.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + 68FBBB32089F07E40016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB37089F08160016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB3A089F08200016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB3D089F082E0016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB3E089F084C0016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB41089F08590016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB44089F08620016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB47089F086B0016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB4A089F08750016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB4D089F087F0016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB50089F08890016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + 68FBBB53089F08940016DF44 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.3_3; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; +/* End PBXBuildRule section */ + +/* Begin PBXBuildStyle section */ + 014CEA460018CE2711CA2923 /* Development */ = { + isa = PBXBuildStyle; buildSettings = { COPY_PHASE_STRIP = NO; DEBUGGING_SYMBOLS = YES; @@ -15,4862 +398,2485 @@ GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; + MACOSX_DEPLOYMENT_TARGET = 10.2; OPTIMIZATION_CFLAGS = "-O0"; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; ZERO_LINK = NO; }; - isa = PBXBuildStyle; name = Development; }; - 014CEA470018CE2711CA2923 = { - buildRules = ( - ); + 014CEA470018CE2711CA2923 /* Deployment */ = { + isa = PBXBuildStyle; buildSettings = { COPY_PHASE_STRIP = YES; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + MACOSX_DEPLOYMENT_TARGET = 10.2; + SYMROOT = gen/build; ZERO_LINK = NO; }; - isa = PBXBuildStyle; name = Deployment; }; -//010 -//011 -//012 -//013 -//014 -//020 -//021 -//022 -//023 -//024 - 0249A662FF388D9811CA2CEA = { - children = ( - 4CDF3892063E561B007B20A1, - 4CDF3893063E561B007B20A1, - 4CDF3894063E561B007B20A1, - 4CDF3895063E561B007B20A1, - 4CDF3896063E561B007B20A1, - 4CDF3897063E561B007B20A1, - 4CDF3898063E561B007B20A1, - 4CDF3899063E561B007B20A1, - 4CB43923063E4B1300969041, - ); - isa = PBXGroup; - name = "External Frameworks and Libraries"; - refType = 4; - sourceTree = ""; - }; -//020 -//021 -//022 -//023 -//024 -//080 -//081 -//082 -//083 -//084 - 08FB7793FE84155DC02AAC07 = { - buildSettings = { - }; - buildStyles = ( - 014CEA460018CE2711CA2923, - 014CEA470018CE2711CA2923, - ); - hasScannedForEncodings = 1; - isa = PBXProject; - mainGroup = 08FB7794FE84155DC02AAC07; - projectDirPath = ""; - targets = ( - 4CD033E80650965F00525ED1, - 4CB437B4063E443800969041, - 4C5E868C0648C2ED003C637B, - 4CB437D0063E44C200969041, - 4CB43800063E45F200969041, - 4CB43823063E46AB00969041, - 4CB43847063E475800969041, - 4CB4386D063E47F800969041, - 4CB438B0063E488600969041, - 4CB43777063E406A00969041, - 4C537F30064E29F800D3815C, - 4CB43913063E497700969041, - 4C537F0C064E286700D3815C, - ); - }; - 08FB7794FE84155DC02AAC07 = { - children = ( - 08FB7795FE84155DC02AAC07, - 0249A662FF388D9811CA2CEA, - 1AB674ADFE9D54B511CA2CBB, - ); - isa = PBXGroup; - name = synergy; - refType = 4; - sourceTree = ""; - }; - 08FB7795FE84155DC02AAC07 = { - children = ( - 4CB4378B063E432C00969041, - 4CB437D2063E44E400969041, - 4CB43779063E40B600969041, - 4C5E86CA0648C6FB003C637B, - 4CB4381E063E469300969041, - 4CB437FB063E45D700969041, - 4CB43849063E478900969041, - 4CB438AD063E487200969041, - 4C537F2B064E29C900D3815C, - 4CB43866063E47C800969041, - 4CB43916063E4A1A00969041, - 4C537F07064E283300D3815C, - ); - isa = PBXGroup; - name = Source; - refType = 4; - sourceTree = ""; - }; -//080 -//081 -//082 -//083 -//084 -//1A0 -//1A1 -//1A2 -//1A3 -//1A4 - 1AB674ADFE9D54B511CA2CBB = { - children = ( - 4CB43778063E406A00969041, - 4C5E868D0648C2ED003C637B, - 4CB437B5063E443800969041, - 4CB437D1063E44C200969041, - 4CB43801063E45F200969041, - 4CB43824063E46AB00969041, - 4CB43848063E475800969041, - 4C537F31064E29F800D3815C, - 4CB4386E063E47F800969041, - 4CB438B1063E488600969041, - 4CB43914063E497700969041, - 4C537F0D064E286700D3815C, - ); - isa = PBXGroup; - name = Products; - refType = 4; - sourceTree = ""; - }; -//1A0 -//1A1 -//1A2 -//1A3 -//1A4 -//4C0 -//4C1 -//4C2 -//4C3 -//4C4 - 4C537F07064E283300D3815C = { - children = ( - 4C537F0F064E293000D3815C, - 4C537F10064E293000D3815C, - 4C537F11064E293000D3815C, - 4C537F12064E293000D3815C, - 4C537F13064E293000D3815C, - ); - isa = PBXGroup; - name = SynergyS; - path = ""; - refType = 4; - sourceTree = ""; - }; - 4C537F0A064E286700D3815C = { - buildActionMask = 2147483647; - files = ( - 4C537F15064E293000D3815C, - 4C537F17064E293000D3815C, - 4C7D7CDA066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F0B064E286700D3815C = { - buildActionMask = 2147483647; - files = ( - 4C537F14064E293000D3815C, - 4C537F16064E293000D3815C, - 4C537F18064E293000D3815C, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F0C064E286700D3815C = { - buildPhases = ( - 4C537F0A064E286700D3815C, - 4C537F0B064E286700D3815C, - 4C537F5A064E2B0700D3815C, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform ./lib/client"; - INSTALL_PATH = /usr/local/bin; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = synergys; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - 4C537F1A064E298800D3815C, - 4C537F1C064E298800D3815C, - 4C537F1E064E298800D3815C, - 4C537F20064E298800D3815C, - 4C537F22064E298800D3815C, - 4C537F24064E298800D3815C, - 4C537F26064E298800D3815C, - 4C537F28064E298800D3815C, - 4C537F57064E2AA300D3815C, - ); - isa = PBXNativeTarget; - name = synergys; - productName = synergys; - productReference = 4C537F0D064E286700D3815C; - productType = "com.apple.product-type.tool"; - }; - 4C537F0D064E286700D3815C = { - explicitFileType = "compiled.mach-o.executable"; - includeInIndex = 0; - isa = PBXFileReference; - path = synergys; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4C537F0F064E293000D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXServerTaskBarReceiver.cpp; - path = cmd/synergys/COSXServerTaskBarReceiver.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F10064E293000D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXServerTaskBarReceiver.h; - path = cmd/synergys/COSXServerTaskBarReceiver.h; - refType = 4; - sourceTree = ""; - }; - 4C537F11064E293000D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CServerTaskBarReceiver.cpp; - path = cmd/synergys/CServerTaskBarReceiver.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F12064E293000D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CServerTaskBarReceiver.h; - path = cmd/synergys/CServerTaskBarReceiver.h; - refType = 4; - sourceTree = ""; - }; - 4C537F13064E293000D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = synergys.cpp; - path = cmd/synergys/synergys.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F14064E293000D3815C = { - fileRef = 4C537F0F064E293000D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F15064E293000D3815C = { - fileRef = 4C537F10064E293000D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F16064E293000D3815C = { - fileRef = 4C537F11064E293000D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F17064E293000D3815C = { - fileRef = 4C537F12064E293000D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F18064E293000D3815C = { - fileRef = 4C537F13064E293000D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F19064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; +/* End PBXBuildStyle section */ + +/* Begin PBXContainerItemProxy section */ + 4C537F19064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB437B4063E443800969041; remoteInfo = arch; }; - 4C537F1A064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB437B4063E443800969041; - targetProxy = 4C537F19064E298800D3815C; - }; - 4C537F1B064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F1B064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4C5E868C0648C2ED003C637B; remoteInfo = common; }; - 4C537F1C064E298800D3815C = { - isa = PBXTargetDependency; - target = 4C5E868C0648C2ED003C637B; - targetProxy = 4C537F1B064E298800D3815C; - }; - 4C537F1D064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F1D064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB437D0063E44C200969041; remoteInfo = base; }; - 4C537F1E064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB437D0063E44C200969041; - targetProxy = 4C537F1D064E298800D3815C; - }; - 4C537F1F064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F1F064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43800063E45F200969041; remoteInfo = mt; }; - 4C537F20064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB43800063E45F200969041; - targetProxy = 4C537F1F064E298800D3815C; - }; - 4C537F21064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F21064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43823063E46AB00969041; remoteInfo = io; }; - 4C537F22064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB43823063E46AB00969041; - targetProxy = 4C537F21064E298800D3815C; - }; - 4C537F23064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F23064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43847063E475800969041; remoteInfo = net; }; - 4C537F24064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB43847063E475800969041; - targetProxy = 4C537F23064E298800D3815C; - }; - 4C537F25064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F25064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB4386D063E47F800969041; remoteInfo = synergy; }; - 4C537F26064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB4386D063E47F800969041; - targetProxy = 4C537F25064E298800D3815C; - }; - 4C537F27064E298800D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F27064E298800D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB438B0063E488600969041; remoteInfo = platform; }; - 4C537F28064E298800D3815C = { - isa = PBXTargetDependency; - target = 4CB438B0063E488600969041; - targetProxy = 4C537F27064E298800D3815C; - }; - 4C537F2B064E29C900D3815C = { - children = ( - 4C537F32064E2A0F00D3815C, - 4C537F33064E2A0F00D3815C, - 4C537F34064E2A0F00D3815C, - 4C537F35064E2A0F00D3815C, - 4C537F36064E2A0F00D3815C, - 4C537F37064E2A0F00D3815C, - 4C537F38064E2A0F00D3815C, - 4C537F39064E2A0F00D3815C, - 4C537F3A064E2A0F00D3815C, - 4C537F3B064E2A0F00D3815C, - 4C537F3C064E2A0F00D3815C, - 4C537F3D064E2A0F00D3815C, - 4C537F3E064E2A0F00D3815C, - 4C537F3F064E2A0F00D3815C, - 4C537F40064E2A0F00D3815C, - 4C537F41064E2A0F00D3815C, - 4C537F42064E2A0F00D3815C, - 4C537F43064E2A0F00D3815C, - ); - isa = PBXGroup; - name = Server; - refType = 4; - sourceTree = ""; - }; - 4C537F2E064E29F800D3815C = { - buildActionMask = 2147483647; - files = ( - 4C537F45064E2A0F00D3815C, - 4C537F47064E2A0F00D3815C, - 4C537F49064E2A0F00D3815C, - 4C537F4B064E2A0F00D3815C, - 4C537F4D064E2A0F00D3815C, - 4C537F4F064E2A0F00D3815C, - 4C537F51064E2A0F00D3815C, - 4C537F53064E2A0F00D3815C, - 4C537F55064E2A0F00D3815C, - 4C7D7CE4066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F2F064E29F800D3815C = { - buildActionMask = 2147483647; - files = ( - 4C537F44064E2A0F00D3815C, - 4C537F46064E2A0F00D3815C, - 4C537F48064E2A0F00D3815C, - 4C537F4A064E2A0F00D3815C, - 4C537F4C064E2A0F00D3815C, - 4C537F4E064E2A0F00D3815C, - 4C537F50064E2A0F00D3815C, - 4C537F52064E2A0F00D3815C, - 4C537F54064E2A0F00D3815C, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F30064E29F800D3815C = { - buildPhases = ( - 4C537F2E064E29F800D3815C, - 4C537F2F064E29F800D3815C, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = server; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = server; - productName = server; - productReference = 4C537F31064E29F800D3815C; - productType = "com.apple.product-type.library.static"; - }; - 4C537F31064E29F800D3815C = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libserver.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4C537F32064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientListener.cpp; - path = lib/server/CClientListener.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F33064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientListener.h; - path = lib/server/CClientListener.h; - refType = 4; - sourceTree = ""; - }; - 4C537F34064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientProxy.cpp; - path = lib/server/CClientProxy.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F35064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientProxy.h; - path = lib/server/CClientProxy.h; - refType = 4; - sourceTree = ""; - }; - 4C537F36064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientProxy1_0.cpp; - path = lib/server/CClientProxy1_0.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F37064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientProxy1_0.h; - path = lib/server/CClientProxy1_0.h; - refType = 4; - sourceTree = ""; - }; - 4C537F38064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientProxy1_1.cpp; - path = lib/server/CClientProxy1_1.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F39064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientProxy1_1.h; - path = lib/server/CClientProxy1_1.h; - refType = 4; - sourceTree = ""; - }; - 4C537F3A064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientProxy1_2.cpp; - path = lib/server/CClientProxy1_2.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F3B064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientProxy1_2.h; - path = lib/server/CClientProxy1_2.h; - refType = 4; - sourceTree = ""; - }; - 4C537F3C064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientProxyUnknown.cpp; - path = lib/server/CClientProxyUnknown.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F3D064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientProxyUnknown.h; - path = lib/server/CClientProxyUnknown.h; - refType = 4; - sourceTree = ""; - }; - 4C537F3E064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CConfig.cpp; - path = lib/server/CConfig.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F3F064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CConfig.h; - path = lib/server/CConfig.h; - refType = 4; - sourceTree = ""; - }; - 4C537F40064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CPrimaryClient.cpp; - path = lib/server/CPrimaryClient.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F41064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CPrimaryClient.h; - path = lib/server/CPrimaryClient.h; - refType = 4; - sourceTree = ""; - }; - 4C537F42064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CServer.cpp; - path = lib/server/CServer.cpp; - refType = 4; - sourceTree = ""; - }; - 4C537F43064E2A0F00D3815C = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CServer.h; - path = lib/server/CServer.h; - refType = 4; - sourceTree = ""; - }; - 4C537F44064E2A0F00D3815C = { - fileRef = 4C537F32064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F45064E2A0F00D3815C = { - fileRef = 4C537F33064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F46064E2A0F00D3815C = { - fileRef = 4C537F34064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F47064E2A0F00D3815C = { - fileRef = 4C537F35064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F48064E2A0F00D3815C = { - fileRef = 4C537F36064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F49064E2A0F00D3815C = { - fileRef = 4C537F37064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4A064E2A0F00D3815C = { - fileRef = 4C537F38064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4B064E2A0F00D3815C = { - fileRef = 4C537F39064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4C064E2A0F00D3815C = { - fileRef = 4C537F3A064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4D064E2A0F00D3815C = { - fileRef = 4C537F3B064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4E064E2A0F00D3815C = { - fileRef = 4C537F3C064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F4F064E2A0F00D3815C = { - fileRef = 4C537F3D064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F50064E2A0F00D3815C = { - fileRef = 4C537F3E064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F51064E2A0F00D3815C = { - fileRef = 4C537F3F064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F52064E2A0F00D3815C = { - fileRef = 4C537F40064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F53064E2A0F00D3815C = { - fileRef = 4C537F41064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F54064E2A0F00D3815C = { - fileRef = 4C537F42064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F55064E2A0F00D3815C = { - fileRef = 4C537F43064E2A0F00D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F56064E2AA300D3815C = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C537F56064E2AA300D3815C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4C537F30064E29F800D3815C; remoteInfo = server; }; - 4C537F57064E2AA300D3815C = { - isa = PBXTargetDependency; - target = 4C537F30064E29F800D3815C; - targetProxy = 4C537F56064E2AA300D3815C; - }; - 4C537F5A064E2B0700D3815C = { - buildActionMask = 2147483647; - files = ( - 4C537F64064E2B4B00D3815C, - 4C537F5B064E2B4200D3815C, - 4C537F5C064E2B4200D3815C, - 4C537F5D064E2B4200D3815C, - 4C537F5E064E2B4200D3815C, - 4C537F5F064E2B4200D3815C, - 4C537F60064E2B4200D3815C, - 4C537F61064E2B4200D3815C, - 4C537F62064E2B4200D3815C, - 4C537F63064E2B4200D3815C, - ); - isa = PBXFrameworksBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F5B064E2B4200D3815C = { - fileRef = 4C5E868D0648C2ED003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F5C064E2B4200D3815C = { - fileRef = 4CB437B5063E443800969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F5D064E2B4200D3815C = { - fileRef = 4CB437D1063E44C200969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F5E064E2B4200D3815C = { - fileRef = 4CB43801063E45F200969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F5F064E2B4200D3815C = { - fileRef = 4CB43824063E46AB00969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F60064E2B4200D3815C = { - fileRef = 4CB43848063E475800969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F61064E2B4200D3815C = { - fileRef = 4C537F31064E29F800D3815C; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F62064E2B4200D3815C = { - fileRef = 4CB4386E063E47F800969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F63064E2B4200D3815C = { - fileRef = 4CB438B1063E488600969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C537F64064E2B4B00D3815C = { - fileRef = 4CB43923063E4B1300969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E868A0648C2ED003C637B = { - buildActionMask = 2147483647; - files = ( - 4C7D7CDD066319560097CA11, - 6887169B073EC03800C5ABE7, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C5E868B0648C2ED003C637B = { - buildActionMask = 2147483647; - files = ( - 4C5E86CC0648C726003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4C5E868C0648C2ED003C637B = { - buildPhases = ( - 4C5E868A0648C2ED003C637B, - 4C5E868B0648C2ED003C637B, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRECOMP_DESTINATION_DIR = ""; - PRODUCT_NAME = common; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = common; - productName = common; - productReference = 4C5E868D0648C2ED003C637B; - productType = "com.apple.product-type.library.static"; - }; - 4C5E868D0648C2ED003C637B = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libcommon.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4C5E868F0648C32E003C637B = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4C5E868F0648C32E003C637B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4C5E868C0648C2ED003C637B; remoteInfo = common; }; - 4C5E86900648C32E003C637B = { - isa = PBXTargetDependency; - target = 4C5E868C0648C2ED003C637B; - targetProxy = 4C5E868F0648C32E003C637B; - }; - 4C5E86920648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArch.cpp; - path = lib/arch/CArch.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86930648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArch.h; - path = lib/arch/CArch.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86940648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchConsoleUnix.cpp; - path = lib/arch/CArchConsoleUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86950648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchConsoleUnix.h; - path = lib/arch/CArchConsoleUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86960648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchDaemonUnix.cpp; - path = lib/arch/CArchDaemonUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86970648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchDaemonUnix.h; - path = lib/arch/CArchDaemonUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86980648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchFileUnix.cpp; - path = lib/arch/CArchFileUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86990648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchFileUnix.h; - path = lib/arch/CArchFileUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E869A0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchLogUnix.cpp; - path = lib/arch/CArchLogUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E869B0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchLogUnix.h; - path = lib/arch/CArchLogUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E869C0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchMultithreadPosix.cpp; - path = lib/arch/CArchMultithreadPosix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E869D0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchMultithreadPosix.h; - path = lib/arch/CArchMultithreadPosix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E869E0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchNetworkBSD.cpp; - path = lib/arch/CArchNetworkBSD.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E869F0648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchNetworkBSD.h; - path = lib/arch/CArchNetworkBSD.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86A00648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchSleepUnix.cpp; - path = lib/arch/CArchSleepUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86A10648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchSleepUnix.h; - path = lib/arch/CArchSleepUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86A20648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchStringUnix.cpp; - path = lib/arch/CArchStringUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86A30648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchStringUnix.h; - path = lib/arch/CArchStringUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86A40648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchTimeUnix.cpp; - path = lib/arch/CArchTimeUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86A50648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchTimeUnix.h; - path = lib/arch/CArchTimeUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86A70648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XArchUnix.cpp; - path = lib/arch/XArchUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86A80648C412003C637B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XArchUnix.h; - path = lib/arch/XArchUnix.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86A90648C412003C637B = { - fileRef = 4C5E86920648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AA0648C412003C637B = { - fileRef = 4C5E86930648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AB0648C412003C637B = { - fileRef = 4C5E86940648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AC0648C412003C637B = { - fileRef = 4C5E86950648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AD0648C412003C637B = { - fileRef = 4C5E86960648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AE0648C412003C637B = { - fileRef = 4C5E86970648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86AF0648C412003C637B = { - fileRef = 4C5E86980648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B00648C412003C637B = { - fileRef = 4C5E86990648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B10648C412003C637B = { - fileRef = 4C5E869A0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B20648C412003C637B = { - fileRef = 4C5E869B0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B30648C412003C637B = { - fileRef = 4C5E869C0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B40648C412003C637B = { - fileRef = 4C5E869D0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B50648C412003C637B = { - fileRef = 4C5E869E0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B60648C412003C637B = { - fileRef = 4C5E869F0648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B70648C412003C637B = { - fileRef = 4C5E86A00648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B80648C412003C637B = { - fileRef = 4C5E86A10648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86B90648C412003C637B = { - fileRef = 4C5E86A20648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86BA0648C412003C637B = { - fileRef = 4C5E86A30648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86BB0648C412003C637B = { - fileRef = 4C5E86A40648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86BC0648C412003C637B = { - fileRef = 4C5E86A50648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86BE0648C412003C637B = { - fileRef = 4C5E86A70648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86BF0648C412003C637B = { - fileRef = 4C5E86A80648C412003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86C10648C653003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchDaemonNone.h; - path = lib/arch/CArchDaemonNone.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86C30648C653003C637B = { - fileRef = 4C5E86C10648C653003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86CA0648C6FB003C637B = { - children = ( - 4C7D7CD9066319560097CA11, - 68871699073EC02100C5ABE7, - 4C5E86CB0648C725003C637B, - ); - isa = PBXGroup; - name = Common; - refType = 4; - sourceTree = ""; - }; - 4C5E86CB0648C725003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = Version.cpp; - path = lib/common/Version.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86CC0648C726003C637B = { - fileRef = 4C5E86CB0648C725003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86CD0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CEvent.cpp; - path = lib/base/CEvent.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86CE0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CEvent.h; - path = lib/base/CEvent.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86CF0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CEventQueue.cpp; - path = lib/base/CEventQueue.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86D00648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CEventQueue.h; - path = lib/base/CEventQueue.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86D10648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CFunctionEventJob.cpp; - path = lib/base/CFunctionEventJob.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86D20648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CFunctionEventJob.h; - path = lib/base/CFunctionEventJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86D30648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CFunctionJob.cpp; - path = lib/base/CFunctionJob.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86D40648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CFunctionJob.h; - path = lib/base/CFunctionJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86D50648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CLog.cpp; - path = lib/base/CLog.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86D60648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CLog.h; - path = lib/base/CLog.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86D70648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CPriorityQueue.h; - path = lib/base/CPriorityQueue.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86D80648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CSimpleEventQueueBuffer.cpp; - path = lib/base/CSimpleEventQueueBuffer.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86D90648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CSimpleEventQueueBuffer.h; - path = lib/base/CSimpleEventQueueBuffer.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86DA0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CStopwatch.cpp; - path = lib/base/CStopwatch.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86DB0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CStopwatch.h; - path = lib/base/CStopwatch.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86DC0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CString.h; - path = lib/base/CString.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86DD0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CStringUtil.cpp; - path = lib/base/CStringUtil.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86DE0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CStringUtil.h; - path = lib/base/CStringUtil.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86DF0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CUnicode.cpp; - path = lib/base/CUnicode.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86E00648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CUnicode.h; - path = lib/base/CUnicode.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E10648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IEventJob.h; - path = lib/base/IEventJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E20648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IEventQueue.cpp; - path = lib/base/IEventQueue.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86E30648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IEventQueue.h; - path = lib/base/IEventQueue.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E40648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IEventQueueBuffer.h; - path = lib/base/IEventQueueBuffer.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E50648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IJob.h; - path = lib/base/IJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E60648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ILogOutputter.h; - path = lib/base/ILogOutputter.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E70648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = LogOutputters.cpp; - path = lib/base/LogOutputters.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86E80648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = LogOutputters.h; - path = lib/base/LogOutputters.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86E90648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = TMethodEventJob.h; - path = lib/base/TMethodEventJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86EA0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = TMethodJob.h; - path = lib/base/TMethodJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86EB0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XBase.cpp; - path = lib/base/XBase.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E86EC0648C7B9003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XBase.h; - path = lib/base/XBase.h; - refType = 4; - sourceTree = ""; - }; - 4C5E86ED0648C7B9003C637B = { - fileRef = 4C5E86CD0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86EE0648C7B9003C637B = { - fileRef = 4C5E86CE0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86EF0648C7B9003C637B = { - fileRef = 4C5E86CF0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F00648C7B9003C637B = { - fileRef = 4C5E86D00648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F10648C7B9003C637B = { - fileRef = 4C5E86D10648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F20648C7B9003C637B = { - fileRef = 4C5E86D20648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F30648C7B9003C637B = { - fileRef = 4C5E86D30648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F40648C7B9003C637B = { - fileRef = 4C5E86D40648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F50648C7B9003C637B = { - fileRef = 4C5E86D50648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F60648C7B9003C637B = { - fileRef = 4C5E86D60648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F70648C7B9003C637B = { - fileRef = 4C5E86D70648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F80648C7B9003C637B = { - fileRef = 4C5E86D80648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86F90648C7B9003C637B = { - fileRef = 4C5E86D90648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FA0648C7B9003C637B = { - fileRef = 4C5E86DA0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FB0648C7B9003C637B = { - fileRef = 4C5E86DB0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FC0648C7B9003C637B = { - fileRef = 4C5E86DC0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FD0648C7B9003C637B = { - fileRef = 4C5E86DD0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FE0648C7B9003C637B = { - fileRef = 4C5E86DE0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E86FF0648C7B9003C637B = { - fileRef = 4C5E86DF0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87000648C7B9003C637B = { - fileRef = 4C5E86E00648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87010648C7B9003C637B = { - fileRef = 4C5E86E10648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87020648C7B9003C637B = { - fileRef = 4C5E86E20648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87030648C7B9003C637B = { - fileRef = 4C5E86E30648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87040648C7B9003C637B = { - fileRef = 4C5E86E40648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87050648C7B9003C637B = { - fileRef = 4C5E86E50648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87060648C7B9003C637B = { - fileRef = 4C5E86E60648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87070648C7B9003C637B = { - fileRef = 4C5E86E70648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87080648C7B9003C637B = { - fileRef = 4C5E86E80648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87090648C7B9003C637B = { - fileRef = 4C5E86E90648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E870A0648C7B9003C637B = { - fileRef = 4C5E86EA0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E870B0648C7B9003C637B = { - fileRef = 4C5E86EB0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E870C0648C7B9003C637B = { - fileRef = 4C5E86EC0648C7B9003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E870D0648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CCondVar.cpp; - path = lib/mt/CCondVar.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E870E0648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CCondVar.h; - path = lib/mt/CCondVar.h; - refType = 4; - sourceTree = ""; - }; - 4C5E870F0648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CLock.cpp; - path = lib/mt/CLock.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87100648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CLock.h; - path = lib/mt/CLock.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87110648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CMutex.cpp; - path = lib/mt/CMutex.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87120648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CMutex.h; - path = lib/mt/CMutex.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87130648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CThread.cpp; - path = lib/mt/CThread.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87140648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CThread.h; - path = lib/mt/CThread.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87150648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XMT.cpp; - path = lib/mt/XMT.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87160648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XMT.h; - path = lib/mt/XMT.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87170648C809003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XThread.h; - path = lib/mt/XThread.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87180648C809003C637B = { - fileRef = 4C5E870D0648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87190648C809003C637B = { - fileRef = 4C5E870E0648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871A0648C809003C637B = { - fileRef = 4C5E870F0648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871B0648C809003C637B = { - fileRef = 4C5E87100648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871C0648C809003C637B = { - fileRef = 4C5E87110648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871D0648C809003C637B = { - fileRef = 4C5E87120648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871E0648C809003C637B = { - fileRef = 4C5E87130648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E871F0648C809003C637B = { - fileRef = 4C5E87140648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87200648C809003C637B = { - fileRef = 4C5E87150648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87210648C809003C637B = { - fileRef = 4C5E87160648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87220648C809003C637B = { - fileRef = 4C5E87170648C809003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87230648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CStreamBuffer.cpp; - path = lib/io/CStreamBuffer.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87240648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CStreamBuffer.h; - path = lib/io/CStreamBuffer.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87250648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CStreamFilter.cpp; - path = lib/io/CStreamFilter.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87260648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CStreamFilter.h; - path = lib/io/CStreamFilter.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87270648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IStream.cpp; - path = lib/io/IStream.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87280648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IStream.h; - path = lib/io/IStream.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87290648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IStreamFilterFactory.h; - path = lib/io/IStreamFilterFactory.h; - refType = 4; - sourceTree = ""; - }; - 4C5E872A0648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XIO.cpp; - path = lib/io/XIO.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E872B0648C83C003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XIO.h; - path = lib/io/XIO.h; - refType = 4; - sourceTree = ""; - }; - 4C5E872C0648C83C003C637B = { - fileRef = 4C5E87230648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E872D0648C83C003C637B = { - fileRef = 4C5E87240648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E872E0648C83C003C637B = { - fileRef = 4C5E87250648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E872F0648C83C003C637B = { - fileRef = 4C5E87260648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87300648C83C003C637B = { - fileRef = 4C5E87270648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87310648C83C003C637B = { - fileRef = 4C5E87280648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87320648C83C003C637B = { - fileRef = 4C5E87290648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87330648C83C003C637B = { - fileRef = 4C5E872A0648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87340648C83C003C637B = { - fileRef = 4C5E872B0648C83C003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87350648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CNetworkAddress.cpp; - path = lib/net/CNetworkAddress.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87360648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CNetworkAddress.h; - path = lib/net/CNetworkAddress.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87370648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CSocketMultiplexer.cpp; - path = lib/net/CSocketMultiplexer.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87380648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CSocketMultiplexer.h; - path = lib/net/CSocketMultiplexer.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87390648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CTCPListenSocket.cpp; - path = lib/net/CTCPListenSocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E873A0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CTCPListenSocket.h; - path = lib/net/CTCPListenSocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E873B0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CTCPSocket.cpp; - path = lib/net/CTCPSocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E873C0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CTCPSocket.h; - path = lib/net/CTCPSocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E873D0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CTCPSocketFactory.cpp; - path = lib/net/CTCPSocketFactory.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E873E0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CTCPSocketFactory.h; - path = lib/net/CTCPSocketFactory.h; - refType = 4; - sourceTree = ""; - }; - 4C5E873F0648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IDataSocket.cpp; - path = lib/net/IDataSocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87400648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IDataSocket.h; - path = lib/net/IDataSocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87410648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IListenSocket.cpp; - path = lib/net/IListenSocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87420648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IListenSocket.h; - path = lib/net/IListenSocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87430648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = ISocket.cpp; - path = lib/net/ISocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87440648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ISocket.h; - path = lib/net/ISocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87450648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ISocketFactory.h; - path = lib/net/ISocketFactory.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87460648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ISocketMultiplexerJob.h; - path = lib/net/ISocketMultiplexerJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87470648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = TSocketMultiplexerMethodJob.h; - path = lib/net/TSocketMultiplexerMethodJob.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87480648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XSocket.cpp; - path = lib/net/XSocket.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87490648C870003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XSocket.h; - path = lib/net/XSocket.h; - refType = 4; - sourceTree = ""; - }; - 4C5E874A0648C870003C637B = { - fileRef = 4C5E87350648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E874B0648C870003C637B = { - fileRef = 4C5E87360648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E874C0648C870003C637B = { - fileRef = 4C5E87370648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E874D0648C870003C637B = { - fileRef = 4C5E87380648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E874E0648C870003C637B = { - fileRef = 4C5E87390648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E874F0648C870003C637B = { - fileRef = 4C5E873A0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87500648C870003C637B = { - fileRef = 4C5E873B0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87510648C870003C637B = { - fileRef = 4C5E873C0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87520648C870003C637B = { - fileRef = 4C5E873D0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87530648C870003C637B = { - fileRef = 4C5E873E0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87540648C870003C637B = { - fileRef = 4C5E873F0648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87550648C870003C637B = { - fileRef = 4C5E87400648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87560648C870003C637B = { - fileRef = 4C5E87410648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87570648C870003C637B = { - fileRef = 4C5E87420648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87580648C870003C637B = { - fileRef = 4C5E87430648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87590648C870003C637B = { - fileRef = 4C5E87440648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875A0648C870003C637B = { - fileRef = 4C5E87450648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875B0648C870003C637B = { - fileRef = 4C5E87460648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875C0648C870003C637B = { - fileRef = 4C5E87470648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875D0648C870003C637B = { - fileRef = 4C5E87480648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875E0648C870003C637B = { - fileRef = 4C5E87490648C870003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E875F0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClipboard.cpp; - path = lib/synergy/CClipboard.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87600648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClipboard.h; - path = lib/synergy/CClipboard.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87610648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CKeyState.cpp; - path = lib/synergy/CKeyState.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87620648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CKeyState.h; - path = lib/synergy/CKeyState.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87630648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ClipboardTypes.h; - path = lib/synergy/ClipboardTypes.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87640648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CPacketStreamFilter.cpp; - path = lib/synergy/CPacketStreamFilter.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87650648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CPacketStreamFilter.h; - path = lib/synergy/CPacketStreamFilter.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87660648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CPlatformScreen.cpp; - path = lib/synergy/CPlatformScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87670648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CPlatformScreen.h; - path = lib/synergy/CPlatformScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87680648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CProtocolUtil.cpp; - path = lib/synergy/CProtocolUtil.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87690648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CProtocolUtil.h; - path = lib/synergy/CProtocolUtil.h; - refType = 4; - sourceTree = ""; - }; - 4C5E876A0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CScreen.cpp; - path = lib/synergy/CScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E876B0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CScreen.h; - path = lib/synergy/CScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E876C0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IClient.h; - path = lib/synergy/IClient.h; - refType = 4; - sourceTree = ""; - }; - 4C5E876D0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IClipboard.cpp; - path = lib/synergy/IClipboard.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E876E0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IClipboard.h; - path = lib/synergy/IClipboard.h; - refType = 4; - sourceTree = ""; - }; - 4C5E876F0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IKeyState.cpp; - path = lib/synergy/IKeyState.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87700648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IKeyState.h; - path = lib/synergy/IKeyState.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87710648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IPlatformScreen.h; - path = lib/synergy/IPlatformScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87720648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IPrimaryScreen.cpp; - path = lib/synergy/IPrimaryScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87730648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IPrimaryScreen.h; - path = lib/synergy/IPrimaryScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87740648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = IScreen.cpp; - path = lib/synergy/IScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87750648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IScreen.h; - path = lib/synergy/IScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87760648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IScreenSaver.h; - path = lib/synergy/IScreenSaver.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87770648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ISecondaryScreen.h; - path = lib/synergy/ISecondaryScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87780648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = KeyTypes.h; - path = lib/synergy/KeyTypes.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87790648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = MouseTypes.h; - path = lib/synergy/MouseTypes.h; - refType = 4; - sourceTree = ""; - }; - 4C5E877A0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = OptionTypes.h; - path = lib/synergy/OptionTypes.h; - refType = 4; - sourceTree = ""; - }; - 4C5E877B0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = ProtocolTypes.h; - path = lib/synergy/ProtocolTypes.h; - refType = 4; - sourceTree = ""; - }; - 4C5E877C0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XScreen.cpp; - path = lib/synergy/XScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E877D0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XScreen.h; - path = lib/synergy/XScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E877E0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XSynergy.cpp; - path = lib/synergy/XSynergy.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E877F0648C8BD003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XSynergy.h; - path = lib/synergy/XSynergy.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87800648C8BD003C637B = { - fileRef = 4C5E875F0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87810648C8BD003C637B = { - fileRef = 4C5E87600648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87820648C8BD003C637B = { - fileRef = 4C5E87610648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87830648C8BD003C637B = { - fileRef = 4C5E87620648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87840648C8BD003C637B = { - fileRef = 4C5E87630648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87850648C8BD003C637B = { - fileRef = 4C5E87640648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87860648C8BD003C637B = { - fileRef = 4C5E87650648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87870648C8BD003C637B = { - fileRef = 4C5E87660648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87880648C8BD003C637B = { - fileRef = 4C5E87670648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87890648C8BD003C637B = { - fileRef = 4C5E87680648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878A0648C8BD003C637B = { - fileRef = 4C5E87690648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878B0648C8BD003C637B = { - fileRef = 4C5E876A0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878C0648C8BD003C637B = { - fileRef = 4C5E876B0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878D0648C8BD003C637B = { - fileRef = 4C5E876C0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878E0648C8BD003C637B = { - fileRef = 4C5E876D0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E878F0648C8BD003C637B = { - fileRef = 4C5E876E0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87900648C8BD003C637B = { - fileRef = 4C5E876F0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87910648C8BD003C637B = { - fileRef = 4C5E87700648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87920648C8BD003C637B = { - fileRef = 4C5E87710648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87930648C8BD003C637B = { - fileRef = 4C5E87720648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87940648C8BD003C637B = { - fileRef = 4C5E87730648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87950648C8BD003C637B = { - fileRef = 4C5E87740648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87960648C8BD003C637B = { - fileRef = 4C5E87750648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87970648C8BD003C637B = { - fileRef = 4C5E87760648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87980648C8BD003C637B = { - fileRef = 4C5E87770648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87990648C8BD003C637B = { - fileRef = 4C5E87780648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879A0648C8BD003C637B = { - fileRef = 4C5E87790648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879B0648C8BD003C637B = { - fileRef = 4C5E877A0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879C0648C8BD003C637B = { - fileRef = 4C5E877B0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879D0648C8BD003C637B = { - fileRef = 4C5E877C0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879E0648C8BD003C637B = { - fileRef = 4C5E877D0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E879F0648C8BD003C637B = { - fileRef = 4C5E877E0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87A00648C8BD003C637B = { - fileRef = 4C5E877F0648C8BD003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87A10648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXClipboard.cpp; - path = lib/platform/COSXClipboard.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87A20648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXClipboard.h; - path = lib/platform/COSXClipboard.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87A30648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXEventQueueBuffer.cpp; - path = lib/platform/COSXEventQueueBuffer.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87A40648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXEventQueueBuffer.h; - path = lib/platform/COSXEventQueueBuffer.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87A50648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXKeyState.cpp; - path = lib/platform/COSXKeyState.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87A60648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXKeyState.h; - path = lib/platform/COSXKeyState.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87A70648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXScreen.cpp; - path = lib/platform/COSXScreen.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87A80648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXScreen.h; - path = lib/platform/COSXScreen.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87A90648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXScreenSaver.cpp; - path = lib/platform/COSXScreenSaver.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87AA0648C913003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXScreenSaver.h; - path = lib/platform/COSXScreenSaver.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87AD0648C913003C637B = { - fileRef = 4C5E87A10648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87AE0648C913003C637B = { - fileRef = 4C5E87A20648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87AF0648C913003C637B = { - fileRef = 4C5E87A30648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B00648C913003C637B = { - fileRef = 4C5E87A40648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B10648C913003C637B = { - fileRef = 4C5E87A50648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B20648C913003C637B = { - fileRef = 4C5E87A60648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B30648C913003C637B = { - fileRef = 4C5E87A70648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B40648C913003C637B = { - fileRef = 4C5E87A80648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B50648C913003C637B = { - fileRef = 4C5E87A90648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B60648C913003C637B = { - fileRef = 4C5E87AA0648C913003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87B90648C969003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClient.cpp; - path = lib/client/CClient.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87BA0648C969003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClient.h; - path = lib/client/CClient.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87BB0648C969003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CServerProxy.cpp; - path = lib/client/CServerProxy.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87BC0648C969003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CServerProxy.h; - path = lib/client/CServerProxy.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87BD0648C969003C637B = { - fileRef = 4C5E87B90648C969003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87BE0648C969003C637B = { - fileRef = 4C5E87BA0648C969003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87BF0648C969003C637B = { - fileRef = 4C5E87BB0648C969003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87C00648C969003C637B = { - fileRef = 4C5E87BC0648C969003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87C10648C9D2003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CClientTaskBarReceiver.cpp; - path = cmd/synergyc/CClientTaskBarReceiver.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87C20648C9D2003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CClientTaskBarReceiver.h; - path = cmd/synergyc/CClientTaskBarReceiver.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87C30648C9D2003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXClientTaskBarReceiver.cpp; - path = cmd/synergyc/COSXClientTaskBarReceiver.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87C40648C9D2003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXClientTaskBarReceiver.h; - path = cmd/synergyc/COSXClientTaskBarReceiver.h; - refType = 4; - sourceTree = ""; - }; - 4C5E87C50648C9D2003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = synergyc.cpp; - path = cmd/synergyc/synergyc.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87C60648C9D2003C637B = { - fileRef = 4C5E87C10648C9D2003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87C70648C9D2003C637B = { - fileRef = 4C5E87C20648C9D2003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87C80648C9D2003C637B = { - fileRef = 4C5E87C30648C9D2003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87C90648C9D2003C637B = { - fileRef = 4C5E87C40648C9D2003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87CA0648C9D2003C637B = { - fileRef = 4C5E87C50648C9D2003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87CF0648CA4B003C637B = { - fileRef = 4C5E868D0648C2ED003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C5E87D00648CA75003C637B = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = XArch.cpp; - path = lib/arch/XArch.cpp; - refType = 4; - sourceTree = ""; - }; - 4C5E87D10648CA75003C637B = { - fileRef = 4C5E87D00648CA75003C637B; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CD9066319560097CA11 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = common.h; - path = lib/common/common.h; - refType = 4; - sourceTree = ""; - }; - 4C7D7CDA066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CDB066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CDC066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CDD066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CDE066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CDF066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE0066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE1066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE2066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE3066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE4066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4C7D7CE5066319560097CA11 = { - fileRef = 4C7D7CD9066319560097CA11; - isa = PBXBuildFile; - settings = { - }; - }; - 4CB43775063E406A00969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87BE0648C969003C637B, - 4C5E87C00648C969003C637B, - 4C7D7CE5066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43776063E406A00969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87BD0648C969003C637B, - 4C5E87BF0648C969003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43777063E406A00969041 = { - buildPhases = ( - 4CB43775063E406A00969041, - 4CB43776063E406A00969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = client; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = client; - productName = client; - productReference = 4CB43778063E406A00969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB43778063E406A00969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libclient.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB43779063E40B600969041 = { - children = ( - 4C5E87B90648C969003C637B, - 4C5E87BA0648C969003C637B, - 4C5E87BB0648C969003C637B, - 4C5E87BC0648C969003C637B, - ); - isa = PBXGroup; - name = Client; - refType = 4; - sourceTree = ""; - }; - 4CB4378B063E432C00969041 = { - children = ( - 4C5E86920648C412003C637B, - 4C5E86930648C412003C637B, - 4C5E86940648C412003C637B, - 4C5E86950648C412003C637B, - 68968F5A073EC484004B2F9B, - 4C5E86C10648C653003C637B, - 4C5E86960648C412003C637B, - 4C5E86970648C412003C637B, - 4C5E86980648C412003C637B, - 4C5E86990648C412003C637B, - 4C5E869A0648C412003C637B, - 4C5E869B0648C412003C637B, - 4C5E869C0648C412003C637B, - 4C5E869D0648C412003C637B, - 4C5E869E0648C412003C637B, - 4C5E869F0648C412003C637B, - 4C5E86A00648C412003C637B, - 4C5E86A10648C412003C637B, - 4C5E86A20648C412003C637B, - 4C5E86A30648C412003C637B, - 68871676073EBF6F00C5ABE7, - 68871677073EBF6F00C5ABE7, - 68968F5B073EC484004B2F9B, - 68968F5C073EC484004B2F9B, - 4C5E86A40648C412003C637B, - 4C5E86A50648C412003C637B, - 68871678073EBF6F00C5ABE7, - 68871679073EBF6F00C5ABE7, - 6887167A073EBF6F00C5ABE7, - 6887167B073EBF6F00C5ABE7, - 6887167C073EBF6F00C5ABE7, - 6887167D073EBF6F00C5ABE7, - 6887167E073EBF6F00C5ABE7, - 6887167F073EBF6F00C5ABE7, - 68871680073EBF6F00C5ABE7, - 68871681073EBF7000C5ABE7, - 68871682073EBF7000C5ABE7, - 68871683073EBF7000C5ABE7, - 68871684073EBF7000C5ABE7, - 4C5E87D00648CA75003C637B, - 4C5E86A70648C412003C637B, - 4C5E86A80648C412003C637B, - ); - isa = PBXGroup; - name = Arch; - refType = 4; - sourceTree = ""; - }; - 4CB437B2063E443800969041 = { - buildActionMask = 2147483647; - files = ( - 4C7D7CDC066319560097CA11, - 4C5E86AA0648C412003C637B, - 4C5E86AC0648C412003C637B, - 4C5E86C30648C653003C637B, - 4C5E86AE0648C412003C637B, - 4C5E86B00648C412003C637B, - 4C5E86B20648C412003C637B, - 4C5E86B40648C412003C637B, - 4C5E86B60648C412003C637B, - 4C5E86B80648C412003C637B, - 4C5E86BA0648C412003C637B, - 68871686073EBF7000C5ABE7, - 68968F5F073EC484004B2F9B, - 4C5E86BC0648C412003C637B, - 68871687073EBF7000C5ABE7, - 68871688073EBF7000C5ABE7, - 68871689073EBF7000C5ABE7, - 6887168A073EBF7000C5ABE7, - 6887168B073EBF7000C5ABE7, - 6887168C073EBF7000C5ABE7, - 6887168D073EBF7000C5ABE7, - 6887168E073EBF7000C5ABE7, - 6887168F073EBF7000C5ABE7, - 68871690073EBF7000C5ABE7, - 68871691073EBF7000C5ABE7, - 68871692073EBF7000C5ABE7, - 68871693073EBF7000C5ABE7, - 4C5E86BF0648C412003C637B, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437B3063E443800969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E86A90648C412003C637B, - 4C5E86AB0648C412003C637B, - 68968F5D073EC484004B2F9B, - 4C5E86AD0648C412003C637B, - 4C5E86AF0648C412003C637B, - 4C5E86B10648C412003C637B, - 4C5E86B30648C412003C637B, - 4C5E86B50648C412003C637B, - 4C5E86B70648C412003C637B, - 4C5E86B90648C412003C637B, - 68871685073EBF7000C5ABE7, - 68968F5E073EC484004B2F9B, - 4C5E86BB0648C412003C637B, - 4C5E87D10648CA75003C637B, - 4C5E86BE0648C412003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437B4063E443800969041 = { - buildPhases = ( - 4CB437B2063E443800969041, - 4CB437B3063E443800969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = ./lib/common; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRECOMP_DESTINATION_DIR = lib/common/; - PRODUCT_NAME = arch; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = arch; - productName = arch; - productReference = 4CB437B5063E443800969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB437B5063E443800969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libarch.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB437CE063E44C200969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E86EE0648C7B9003C637B, - 4C5E86F00648C7B9003C637B, - 4C5E86F20648C7B9003C637B, - 4C5E86F40648C7B9003C637B, - 4C5E86F60648C7B9003C637B, - 4C5E86F70648C7B9003C637B, - 4C5E86F90648C7B9003C637B, - 4C5E86FB0648C7B9003C637B, - 4C5E86FC0648C7B9003C637B, - 4C5E86FE0648C7B9003C637B, - 4C5E87000648C7B9003C637B, - 4C5E87010648C7B9003C637B, - 4C5E87030648C7B9003C637B, - 4C5E87040648C7B9003C637B, - 4C5E87050648C7B9003C637B, - 4C5E87060648C7B9003C637B, - 4C5E87080648C7B9003C637B, - 4C5E87090648C7B9003C637B, - 4C5E870A0648C7B9003C637B, - 4C5E870C0648C7B9003C637B, - 4C7D7CDE066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437CF063E44C200969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E86ED0648C7B9003C637B, - 4C5E86EF0648C7B9003C637B, - 4C5E86F10648C7B9003C637B, - 4C5E86F30648C7B9003C637B, - 4C5E86F50648C7B9003C637B, - 4C5E86F80648C7B9003C637B, - 4C5E86FA0648C7B9003C637B, - 4C5E86FD0648C7B9003C637B, - 4C5E86FF0648C7B9003C637B, - 4C5E87020648C7B9003C637B, - 4C5E87070648C7B9003C637B, - 4C5E870B0648C7B9003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437D0063E44C200969041 = { - buildPhases = ( - 4CB437CE063E44C200969041, - 4CB437CF063E44C200969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = base; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = base; - productName = base; - productReference = 4CB437D1063E44C200969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB437D1063E44C200969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libbase.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB437D2063E44E400969041 = { - children = ( - 4C5E86CD0648C7B9003C637B, - 4C5E86CE0648C7B9003C637B, - 4C5E86CF0648C7B9003C637B, - 4C5E86D00648C7B9003C637B, - 4C5E86D10648C7B9003C637B, - 4C5E86D20648C7B9003C637B, - 4C5E86D30648C7B9003C637B, - 4C5E86D40648C7B9003C637B, - 4C5E86D50648C7B9003C637B, - 4C5E86D60648C7B9003C637B, - 4C5E86D70648C7B9003C637B, - 4C5E86D80648C7B9003C637B, - 4C5E86D90648C7B9003C637B, - 4C5E86DA0648C7B9003C637B, - 4C5E86DB0648C7B9003C637B, - 4C5E86DC0648C7B9003C637B, - 4C5E86DD0648C7B9003C637B, - 4C5E86DE0648C7B9003C637B, - 4C5E86DF0648C7B9003C637B, - 4C5E86E00648C7B9003C637B, - 4C5E86E10648C7B9003C637B, - 4C5E86E20648C7B9003C637B, - 4C5E86E30648C7B9003C637B, - 4C5E86E40648C7B9003C637B, - 4C5E86E50648C7B9003C637B, - 4C5E86E60648C7B9003C637B, - 4C5E86E70648C7B9003C637B, - 4C5E86E80648C7B9003C637B, - 4C5E86E90648C7B9003C637B, - 4C5E86EA0648C7B9003C637B, - 4C5E86EB0648C7B9003C637B, - 4C5E86EC0648C7B9003C637B, - ); - isa = PBXGroup; - name = Base; - refType = 4; - sourceTree = ""; - }; - 4CB437FB063E45D700969041 = { - children = ( - 4C5E870D0648C809003C637B, - 4C5E870E0648C809003C637B, - 4C5E870F0648C809003C637B, - 4C5E87100648C809003C637B, - 4C5E87110648C809003C637B, - 4C5E87120648C809003C637B, - 4C5E87130648C809003C637B, - 4C5E87140648C809003C637B, - 4C5E87150648C809003C637B, - 4C5E87160648C809003C637B, - 4C5E87170648C809003C637B, - ); - isa = PBXGroup; - name = MT; - refType = 4; - sourceTree = ""; - }; - 4CB437FE063E45F200969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87190648C809003C637B, - 4C5E871B0648C809003C637B, - 4C5E871D0648C809003C637B, - 4C5E871F0648C809003C637B, - 4C5E87210648C809003C637B, - 4C5E87220648C809003C637B, - 4C7D7CDF066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437FF063E45F200969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87180648C809003C637B, - 4C5E871A0648C809003C637B, - 4C5E871C0648C809003C637B, - 4C5E871E0648C809003C637B, - 4C5E87200648C809003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43800063E45F200969041 = { - buildPhases = ( - 4CB437FE063E45F200969041, - 4CB437FF063E45F200969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = mt; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = mt; - productName = mt; - productReference = 4CB43801063E45F200969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB43801063E45F200969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libmt.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB4381E063E469300969041 = { - children = ( - 4C5E87230648C83C003C637B, - 4C5E87240648C83C003C637B, - 4C5E87250648C83C003C637B, - 4C5E87260648C83C003C637B, - 4C5E87270648C83C003C637B, - 4C5E87280648C83C003C637B, - 4C5E87290648C83C003C637B, - 4C5E872A0648C83C003C637B, - 4C5E872B0648C83C003C637B, - ); - isa = PBXGroup; - name = IO; - refType = 4; - sourceTree = ""; - }; - 4CB43821063E46AB00969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E872D0648C83C003C637B, - 4C5E872F0648C83C003C637B, - 4C5E87310648C83C003C637B, - 4C5E87320648C83C003C637B, - 4C5E87340648C83C003C637B, - 4C7D7CE0066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43822063E46AB00969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E872C0648C83C003C637B, - 4C5E872E0648C83C003C637B, - 4C5E87300648C83C003C637B, - 4C5E87330648C83C003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43823063E46AB00969041 = { - buildPhases = ( - 4CB43821063E46AB00969041, - 4CB43822063E46AB00969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = io; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = io; - productName = io; - productReference = 4CB43824063E46AB00969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB43824063E46AB00969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libio.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB43845063E475800969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E874B0648C870003C637B, - 4C5E874D0648C870003C637B, - 4C5E874F0648C870003C637B, - 4C5E87510648C870003C637B, - 4C5E87530648C870003C637B, - 4C5E87550648C870003C637B, - 4C5E87570648C870003C637B, - 4C5E87590648C870003C637B, - 4C5E875A0648C870003C637B, - 4C5E875B0648C870003C637B, - 4C5E875C0648C870003C637B, - 4C5E875E0648C870003C637B, - 4C7D7CE1066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43846063E475800969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E874A0648C870003C637B, - 4C5E874C0648C870003C637B, - 4C5E874E0648C870003C637B, - 4C5E87500648C870003C637B, - 4C5E87520648C870003C637B, - 4C5E87540648C870003C637B, - 4C5E87560648C870003C637B, - 4C5E87580648C870003C637B, - 4C5E875D0648C870003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43847063E475800969041 = { - buildPhases = ( - 4CB43845063E475800969041, - 4CB43846063E475800969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = net; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = net; - productName = net; - productReference = 4CB43848063E475800969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB43848063E475800969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libnet.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB43849063E478900969041 = { - children = ( - 4C5E87350648C870003C637B, - 4C5E87360648C870003C637B, - 4C5E87370648C870003C637B, - 4C5E87380648C870003C637B, - 4C5E87390648C870003C637B, - 4C5E873A0648C870003C637B, - 4C5E873B0648C870003C637B, - 4C5E873C0648C870003C637B, - 4C5E873D0648C870003C637B, - 4C5E873E0648C870003C637B, - 4C5E873F0648C870003C637B, - 4C5E87400648C870003C637B, - 4C5E87410648C870003C637B, - 4C5E87420648C870003C637B, - 4C5E87430648C870003C637B, - 4C5E87440648C870003C637B, - 4C5E87450648C870003C637B, - 4C5E87460648C870003C637B, - 4C5E87470648C870003C637B, - 4C5E87480648C870003C637B, - 4C5E87490648C870003C637B, - ); - isa = PBXGroup; - name = Net; - refType = 4; - sourceTree = ""; - }; - 4CB43866063E47C800969041 = { - children = ( - 4C5E875F0648C8BD003C637B, - 4C5E87600648C8BD003C637B, - 4C5E87610648C8BD003C637B, - 4C5E87620648C8BD003C637B, - 4C5E87630648C8BD003C637B, - 4C5E87640648C8BD003C637B, - 4C5E87650648C8BD003C637B, - 4C5E87660648C8BD003C637B, - 4C5E87670648C8BD003C637B, - 4C5E87680648C8BD003C637B, - 4C5E87690648C8BD003C637B, - 4C5E876A0648C8BD003C637B, - 4C5E876B0648C8BD003C637B, - 4C5E876C0648C8BD003C637B, - 4C5E876D0648C8BD003C637B, - 4C5E876E0648C8BD003C637B, - 4C5E876F0648C8BD003C637B, - 4C5E87700648C8BD003C637B, - 4C5E87710648C8BD003C637B, - 4C5E87720648C8BD003C637B, - 4C5E87730648C8BD003C637B, - 4C5E87740648C8BD003C637B, - 4C5E87750648C8BD003C637B, - 4C5E87760648C8BD003C637B, - 4C5E87770648C8BD003C637B, - 4C5E87780648C8BD003C637B, - 4C5E87790648C8BD003C637B, - 4C5E877A0648C8BD003C637B, - 4C5E877B0648C8BD003C637B, - 4C5E877C0648C8BD003C637B, - 4C5E877D0648C8BD003C637B, - 4C5E877E0648C8BD003C637B, - 4C5E877F0648C8BD003C637B, - ); - isa = PBXGroup; - name = Synergy; - refType = 4; - sourceTree = ""; - }; - 4CB4386B063E47F800969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87810648C8BD003C637B, - 4C5E87830648C8BD003C637B, - 4C5E87840648C8BD003C637B, - 4C5E87860648C8BD003C637B, - 4C5E87880648C8BD003C637B, - 4C5E878A0648C8BD003C637B, - 4C5E878C0648C8BD003C637B, - 4C5E878D0648C8BD003C637B, - 4C5E878F0648C8BD003C637B, - 4C5E87910648C8BD003C637B, - 4C5E87920648C8BD003C637B, - 4C5E87940648C8BD003C637B, - 4C5E87960648C8BD003C637B, - 4C5E87970648C8BD003C637B, - 4C5E87980648C8BD003C637B, - 4C5E87990648C8BD003C637B, - 4C5E879A0648C8BD003C637B, - 4C5E879B0648C8BD003C637B, - 4C5E879C0648C8BD003C637B, - 4C5E879E0648C8BD003C637B, - 4C5E87A00648C8BD003C637B, - 4C7D7CE2066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB4386C063E47F800969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87800648C8BD003C637B, - 4C5E87820648C8BD003C637B, - 4C5E87850648C8BD003C637B, - 4C5E87870648C8BD003C637B, - 4C5E87890648C8BD003C637B, - 4C5E878B0648C8BD003C637B, - 4C5E878E0648C8BD003C637B, - 4C5E87900648C8BD003C637B, - 4C5E87930648C8BD003C637B, - 4C5E87950648C8BD003C637B, - 4C5E879D0648C8BD003C637B, - 4C5E879F0648C8BD003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB4386D063E47F800969041 = { - buildPhases = ( - 4CB4386B063E47F800969041, - 4CB4386C063E47F800969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = synergy; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = synergy; - productName = synergy; - productReference = 4CB4386E063E47F800969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB4386E063E47F800969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libsynergy.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB438AD063E487200969041 = { - children = ( - 4C5E87A10648C913003C637B, - 4C5E87A20648C913003C637B, - 4CD0348F0650B6F100525ED1, - 4CD034900650B6F100525ED1, - 4CD034910650B6F100525ED1, - 4CD034920650B6F100525ED1, - 4CD034930650B6F100525ED1, - 4CD034940650B6F100525ED1, - 4C5E87A30648C913003C637B, - 4C5E87A40648C913003C637B, - 4C5E87A50648C913003C637B, - 4C5E87A60648C913003C637B, - 4C5E87A70648C913003C637B, - 4C5E87A80648C913003C637B, - 4C5E87A90648C913003C637B, - 4C5E87AA0648C913003C637B, - ); - isa = PBXGroup; - name = Platform; - refType = 4; - sourceTree = ""; - }; - 4CB438AE063E488600969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87AE0648C913003C637B, - 4C5E87B00648C913003C637B, - 4C5E87B20648C913003C637B, - 4C5E87B40648C913003C637B, - 4C5E87B60648C913003C637B, - 4CD034960650B6F100525ED1, - 4CD034980650B6F100525ED1, - 4CD0349A0650B6F100525ED1, - 4C7D7CE3066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB438AF063E488600969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87AD0648C913003C637B, - 4C5E87AF0648C913003C637B, - 4C5E87B10648C913003C637B, - 4C5E87B30648C913003C637B, - 4C5E87B50648C913003C637B, - 4CD034950650B6F100525ED1, - 4CD034970650B6F100525ED1, - 4CD034990650B6F100525ED1, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB438B0063E488600969041 = { - buildPhases = ( - 4CB438AE063E488600969041, - 4CB438AF063E488600969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io /lib/synergy"; - INSTALL_PATH = /usr/local/lib; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = platform; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - ); - isa = PBXNativeTarget; - name = platform; - productName = platform; - productReference = 4CB438B1063E488600969041; - productType = "com.apple.product-type.library.static"; - }; - 4CB438B1063E488600969041 = { - explicitFileType = archive.ar; - includeInIndex = 0; - isa = PBXFileReference; - path = libplatform.a; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB43911063E497700969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87C70648C9D2003C637B, - 4C5E87C90648C9D2003C637B, - 4C7D7CDB066319560097CA11, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43912063E497700969041 = { - buildActionMask = 2147483647; - files = ( - 4C5E87C60648C9D2003C637B, - 4C5E87C80648C9D2003C637B, - 4C5E87CA0648C9D2003C637B, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43913063E497700969041 = { - buildPhases = ( - 4CB43911063E497700969041, - 4CB43912063E497700969041, - 4CB43930063E4B4300969041, - ); - buildRules = ( - ); - buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = NO; - GCC_PREFIX_HEADER = lib/common/MacOSXPrecomp.h; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = "./lib/common ./lib/arch ./lib/base ./lib/mt ./lib/io ./lib/net ./lib/synergy ./lib/platform ./lib/client"; - INSTALL_PATH = /usr/local/bin; - LIBRARY_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = synergyc; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost"; - }; - dependencies = ( - 4CB43948063E4E1600969041, - 4C5E86900648C32E003C637B, - 4CB4394A063E4E1600969041, - 4CB4394C063E4E1600969041, - 4CB4394E063E4E1600969041, - 4CB43950063E4E1600969041, - 4CB43952063E4E1600969041, - 4CB43954063E4E1600969041, - 4CB43946063E4E1600969041, - ); - isa = PBXNativeTarget; - name = synergyc; - productName = synergyc; - productReference = 4CB43914063E497700969041; - productType = "com.apple.product-type.tool"; - }; - 4CB43914063E497700969041 = { - explicitFileType = "compiled.mach-o.executable"; - includeInIndex = 0; - isa = PBXFileReference; - path = synergyc; - refType = 3; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4CB43916063E4A1A00969041 = { - children = ( - 4C5E87C10648C9D2003C637B, - 4C5E87C20648C9D2003C637B, - 4C5E87C30648C9D2003C637B, - 4C5E87C40648C9D2003C637B, - 4C5E87C50648C9D2003C637B, - ); - isa = PBXGroup; - name = SynergyC; - refType = 4; - sourceTree = ""; - }; - 4CB43923063E4B1300969041 = { - isa = PBXFileReference; - lastKnownFileType = wrapper.framework; - name = Carbon.framework; - path = ../../../../../System/Library/Frameworks/Carbon.framework; - refType = 4; - sourceTree = ""; - }; - 4CB43930063E4B4300969041 = { - buildActionMask = 2147483647; - files = ( - 4CB43934063E4B4A00969041, - 4CDF389A063E561B007B20A1, - 4C5E87CF0648CA4B003C637B, - 4CDF389B063E561B007B20A1, - 4CDF389C063E561B007B20A1, - 4CDF389D063E561B007B20A1, - 4CDF389E063E561B007B20A1, - 4CDF389F063E561B007B20A1, - 4CDF38A0063E561B007B20A1, - 4CDF38A1063E561B007B20A1, - ); - isa = PBXFrameworksBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43934063E4B4A00969041 = { - fileRef = 4CB43923063E4B1300969041; - isa = PBXBuildFile; - settings = { - }; - }; - 4CB43945063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB43945063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43777063E406A00969041; remoteInfo = client; }; - 4CB43946063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB43777063E406A00969041; - targetProxy = 4CB43945063E4E1600969041; - }; - 4CB43947063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB43947063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB437B4063E443800969041; remoteInfo = arch; }; - 4CB43948063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB437B4063E443800969041; - targetProxy = 4CB43947063E4E1600969041; - }; - 4CB43949063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB43949063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB437D0063E44C200969041; remoteInfo = base; }; - 4CB4394A063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB437D0063E44C200969041; - targetProxy = 4CB43949063E4E1600969041; - }; - 4CB4394B063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB4394B063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43800063E45F200969041; remoteInfo = mt; }; - 4CB4394C063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB43800063E45F200969041; - targetProxy = 4CB4394B063E4E1600969041; - }; - 4CB4394D063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB4394D063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43823063E46AB00969041; remoteInfo = io; }; - 4CB4394E063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB43823063E46AB00969041; - targetProxy = 4CB4394D063E4E1600969041; - }; - 4CB4394F063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB4394F063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43847063E475800969041; remoteInfo = net; }; - 4CB43950063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB43847063E475800969041; - targetProxy = 4CB4394F063E4E1600969041; - }; - 4CB43951063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB43951063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB4386D063E47F800969041; remoteInfo = synergy; }; - 4CB43952063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB4386D063E47F800969041; - targetProxy = 4CB43951063E4E1600969041; - }; - 4CB43953063E4E1600969041 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CB43953063E4E1600969041 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB438B0063E488600969041; remoteInfo = platform; }; - 4CB43954063E4E1600969041 = { - isa = PBXTargetDependency; - target = 4CB438B0063E488600969041; - targetProxy = 4CB43953063E4E1600969041; - }; - 4CD033E80650965F00525ED1 = { - buildPhases = ( - ); - buildSettings = { - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = ALL; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; - }; - dependencies = ( - 4CD033EA0650968500525ED1, - 4CD033EC0650968500525ED1, - ); - isa = PBXAggregateTarget; - name = ALL; - productName = ALL; - }; - 4CD033E90650968500525ED1 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CD033E90650968500525ED1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4CB43913063E497700969041; remoteInfo = synergyc; }; - 4CD033EA0650968500525ED1 = { - isa = PBXTargetDependency; - target = 4CB43913063E497700969041; - targetProxy = 4CD033E90650968500525ED1; - }; - 4CD033EB0650968500525ED1 = { - containerPortal = 08FB7793FE84155DC02AAC07; + 4CD033EB0650968500525ED1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; proxyType = 1; remoteGlobalIDString = 4C537F0C064E286700D3815C; remoteInfo = synergys; }; - 4CD033EC0650968500525ED1 = { +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 4C537F0D064E286700D3815C /* synergys */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = synergys; sourceTree = BUILT_PRODUCTS_DIR; }; + 4C537F0F064E293000D3815C /* COSXServerTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXServerTaskBarReceiver.cpp; path = cmd/synergys/COSXServerTaskBarReceiver.cpp; sourceTree = ""; }; + 4C537F10064E293000D3815C /* COSXServerTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXServerTaskBarReceiver.h; path = cmd/synergys/COSXServerTaskBarReceiver.h; sourceTree = ""; }; + 4C537F11064E293000D3815C /* CServerTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CServerTaskBarReceiver.cpp; path = cmd/synergys/CServerTaskBarReceiver.cpp; sourceTree = ""; }; + 4C537F12064E293000D3815C /* CServerTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CServerTaskBarReceiver.h; path = cmd/synergys/CServerTaskBarReceiver.h; sourceTree = ""; }; + 4C537F13064E293000D3815C /* synergys.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = synergys.cpp; path = cmd/synergys/synergys.cpp; sourceTree = ""; }; + 4C537F31064E29F800D3815C /* libserver.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libserver.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4C537F32064E2A0F00D3815C /* CClientListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientListener.cpp; path = lib/server/CClientListener.cpp; sourceTree = ""; }; + 4C537F33064E2A0F00D3815C /* CClientListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientListener.h; path = lib/server/CClientListener.h; sourceTree = ""; }; + 4C537F34064E2A0F00D3815C /* CClientProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy.cpp; path = lib/server/CClientProxy.cpp; sourceTree = ""; }; + 4C537F35064E2A0F00D3815C /* CClientProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy.h; path = lib/server/CClientProxy.h; sourceTree = ""; }; + 4C537F36064E2A0F00D3815C /* CClientProxy1_0.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_0.cpp; path = lib/server/CClientProxy1_0.cpp; sourceTree = ""; }; + 4C537F37064E2A0F00D3815C /* CClientProxy1_0.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_0.h; path = lib/server/CClientProxy1_0.h; sourceTree = ""; }; + 4C537F38064E2A0F00D3815C /* CClientProxy1_1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_1.cpp; path = lib/server/CClientProxy1_1.cpp; sourceTree = ""; }; + 4C537F39064E2A0F00D3815C /* CClientProxy1_1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_1.h; path = lib/server/CClientProxy1_1.h; sourceTree = ""; }; + 4C537F3A064E2A0F00D3815C /* CClientProxy1_2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_2.cpp; path = lib/server/CClientProxy1_2.cpp; sourceTree = ""; }; + 4C537F3B064E2A0F00D3815C /* CClientProxy1_2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_2.h; path = lib/server/CClientProxy1_2.h; sourceTree = ""; }; + 4C537F3C064E2A0F00D3815C /* CClientProxyUnknown.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxyUnknown.cpp; path = lib/server/CClientProxyUnknown.cpp; sourceTree = ""; }; + 4C537F3D064E2A0F00D3815C /* CClientProxyUnknown.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxyUnknown.h; path = lib/server/CClientProxyUnknown.h; sourceTree = ""; }; + 4C537F3E064E2A0F00D3815C /* CConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CConfig.cpp; path = lib/server/CConfig.cpp; sourceTree = ""; }; + 4C537F3F064E2A0F00D3815C /* CConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CConfig.h; path = lib/server/CConfig.h; sourceTree = ""; }; + 4C537F40064E2A0F00D3815C /* CPrimaryClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPrimaryClient.cpp; path = lib/server/CPrimaryClient.cpp; sourceTree = ""; }; + 4C537F41064E2A0F00D3815C /* CPrimaryClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPrimaryClient.h; path = lib/server/CPrimaryClient.h; sourceTree = ""; }; + 4C537F42064E2A0F00D3815C /* CServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CServer.cpp; path = lib/server/CServer.cpp; sourceTree = ""; }; + 4C537F43064E2A0F00D3815C /* CServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CServer.h; path = lib/server/CServer.h; sourceTree = ""; }; + 4C5E868D0648C2ED003C637B /* libcommon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcommon.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4C5E86920648C412003C637B /* CArch.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArch.cpp; path = lib/arch/CArch.cpp; sourceTree = ""; }; + 4C5E86930648C412003C637B /* CArch.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArch.h; path = lib/arch/CArch.h; sourceTree = ""; }; + 4C5E86940648C412003C637B /* CArchConsoleUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchConsoleUnix.cpp; path = lib/arch/CArchConsoleUnix.cpp; sourceTree = ""; }; + 4C5E86950648C412003C637B /* CArchConsoleUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchConsoleUnix.h; path = lib/arch/CArchConsoleUnix.h; sourceTree = ""; }; + 4C5E86960648C412003C637B /* CArchDaemonUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchDaemonUnix.cpp; path = lib/arch/CArchDaemonUnix.cpp; sourceTree = ""; }; + 4C5E86970648C412003C637B /* CArchDaemonUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchDaemonUnix.h; path = lib/arch/CArchDaemonUnix.h; sourceTree = ""; }; + 4C5E86980648C412003C637B /* CArchFileUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchFileUnix.cpp; path = lib/arch/CArchFileUnix.cpp; sourceTree = ""; }; + 4C5E86990648C412003C637B /* CArchFileUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchFileUnix.h; path = lib/arch/CArchFileUnix.h; sourceTree = ""; }; + 4C5E869A0648C412003C637B /* CArchLogUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchLogUnix.cpp; path = lib/arch/CArchLogUnix.cpp; sourceTree = ""; }; + 4C5E869B0648C412003C637B /* CArchLogUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchLogUnix.h; path = lib/arch/CArchLogUnix.h; sourceTree = ""; }; + 4C5E869C0648C412003C637B /* CArchMultithreadPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchMultithreadPosix.cpp; path = lib/arch/CArchMultithreadPosix.cpp; sourceTree = ""; }; + 4C5E869D0648C412003C637B /* CArchMultithreadPosix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchMultithreadPosix.h; path = lib/arch/CArchMultithreadPosix.h; sourceTree = ""; }; + 4C5E869E0648C412003C637B /* CArchNetworkBSD.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchNetworkBSD.cpp; path = lib/arch/CArchNetworkBSD.cpp; sourceTree = ""; }; + 4C5E869F0648C412003C637B /* CArchNetworkBSD.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchNetworkBSD.h; path = lib/arch/CArchNetworkBSD.h; sourceTree = ""; }; + 4C5E86A00648C412003C637B /* CArchSleepUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchSleepUnix.cpp; path = lib/arch/CArchSleepUnix.cpp; sourceTree = ""; }; + 4C5E86A10648C412003C637B /* CArchSleepUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchSleepUnix.h; path = lib/arch/CArchSleepUnix.h; sourceTree = ""; }; + 4C5E86A20648C412003C637B /* CArchStringUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchStringUnix.cpp; path = lib/arch/CArchStringUnix.cpp; sourceTree = ""; }; + 4C5E86A30648C412003C637B /* CArchStringUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchStringUnix.h; path = lib/arch/CArchStringUnix.h; sourceTree = ""; }; + 4C5E86A40648C412003C637B /* CArchTimeUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchTimeUnix.cpp; path = lib/arch/CArchTimeUnix.cpp; sourceTree = ""; }; + 4C5E86A50648C412003C637B /* CArchTimeUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchTimeUnix.h; path = lib/arch/CArchTimeUnix.h; sourceTree = ""; }; + 4C5E86A70648C412003C637B /* XArchUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = XArchUnix.cpp; path = lib/arch/XArchUnix.cpp; sourceTree = ""; }; + 4C5E86A80648C412003C637B /* XArchUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = XArchUnix.h; path = lib/arch/XArchUnix.h; sourceTree = ""; }; + 4C5E86C10648C653003C637B /* CArchDaemonNone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CArchDaemonNone.h; path = lib/arch/CArchDaemonNone.h; sourceTree = ""; }; + 4C5E86CB0648C725003C637B /* Version.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Version.cpp; path = lib/common/Version.cpp; sourceTree = ""; }; + 4C5E86CD0648C7B9003C637B /* CEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CEvent.cpp; path = lib/base/CEvent.cpp; sourceTree = ""; }; + 4C5E86CE0648C7B9003C637B /* CEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CEvent.h; path = lib/base/CEvent.h; sourceTree = ""; }; + 4C5E86CF0648C7B9003C637B /* CEventQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CEventQueue.cpp; path = lib/base/CEventQueue.cpp; sourceTree = ""; }; + 4C5E86D00648C7B9003C637B /* CEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CEventQueue.h; path = lib/base/CEventQueue.h; sourceTree = ""; }; + 4C5E86D10648C7B9003C637B /* CFunctionEventJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFunctionEventJob.cpp; path = lib/base/CFunctionEventJob.cpp; sourceTree = ""; }; + 4C5E86D20648C7B9003C637B /* CFunctionEventJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFunctionEventJob.h; path = lib/base/CFunctionEventJob.h; sourceTree = ""; }; + 4C5E86D30648C7B9003C637B /* CFunctionJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFunctionJob.cpp; path = lib/base/CFunctionJob.cpp; sourceTree = ""; }; + 4C5E86D40648C7B9003C637B /* CFunctionJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFunctionJob.h; path = lib/base/CFunctionJob.h; sourceTree = ""; }; + 4C5E86D50648C7B9003C637B /* CLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CLog.cpp; path = lib/base/CLog.cpp; sourceTree = ""; }; + 4C5E86D60648C7B9003C637B /* CLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CLog.h; path = lib/base/CLog.h; sourceTree = ""; }; + 4C5E86D70648C7B9003C637B /* CPriorityQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPriorityQueue.h; path = lib/base/CPriorityQueue.h; sourceTree = ""; }; + 4C5E86D80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSimpleEventQueueBuffer.cpp; path = lib/base/CSimpleEventQueueBuffer.cpp; sourceTree = ""; }; + 4C5E86D90648C7B9003C637B /* CSimpleEventQueueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSimpleEventQueueBuffer.h; path = lib/base/CSimpleEventQueueBuffer.h; sourceTree = ""; }; + 4C5E86DA0648C7B9003C637B /* CStopwatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStopwatch.cpp; path = lib/base/CStopwatch.cpp; sourceTree = ""; }; + 4C5E86DB0648C7B9003C637B /* CStopwatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStopwatch.h; path = lib/base/CStopwatch.h; sourceTree = ""; }; + 4C5E86DC0648C7B9003C637B /* CString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CString.h; path = lib/base/CString.h; sourceTree = ""; }; + 4C5E86DD0648C7B9003C637B /* CStringUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStringUtil.cpp; path = lib/base/CStringUtil.cpp; sourceTree = ""; }; + 4C5E86DE0648C7B9003C637B /* CStringUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStringUtil.h; path = lib/base/CStringUtil.h; sourceTree = ""; }; + 4C5E86DF0648C7B9003C637B /* CUnicode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CUnicode.cpp; path = lib/base/CUnicode.cpp; sourceTree = ""; }; + 4C5E86E00648C7B9003C637B /* CUnicode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CUnicode.h; path = lib/base/CUnicode.h; sourceTree = ""; }; + 4C5E86E10648C7B9003C637B /* IEventJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IEventJob.h; path = lib/base/IEventJob.h; sourceTree = ""; }; + 4C5E86E20648C7B9003C637B /* IEventQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IEventQueue.cpp; path = lib/base/IEventQueue.cpp; sourceTree = ""; }; + 4C5E86E30648C7B9003C637B /* IEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IEventQueue.h; path = lib/base/IEventQueue.h; sourceTree = ""; }; + 4C5E86E40648C7B9003C637B /* IEventQueueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IEventQueueBuffer.h; path = lib/base/IEventQueueBuffer.h; sourceTree = ""; }; + 4C5E86E50648C7B9003C637B /* IJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJob.h; path = lib/base/IJob.h; sourceTree = ""; }; + 4C5E86E60648C7B9003C637B /* ILogOutputter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ILogOutputter.h; path = lib/base/ILogOutputter.h; sourceTree = ""; }; + 4C5E86E70648C7B9003C637B /* LogOutputters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LogOutputters.cpp; path = lib/base/LogOutputters.cpp; sourceTree = ""; }; + 4C5E86E80648C7B9003C637B /* LogOutputters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LogOutputters.h; path = lib/base/LogOutputters.h; sourceTree = ""; }; + 4C5E86E90648C7B9003C637B /* TMethodEventJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TMethodEventJob.h; path = lib/base/TMethodEventJob.h; sourceTree = ""; }; + 4C5E86EA0648C7B9003C637B /* TMethodJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TMethodJob.h; path = lib/base/TMethodJob.h; sourceTree = ""; }; + 4C5E86EB0648C7B9003C637B /* XBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XBase.cpp; path = lib/base/XBase.cpp; sourceTree = ""; }; + 4C5E86EC0648C7B9003C637B /* XBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XBase.h; path = lib/base/XBase.h; sourceTree = ""; }; + 4C5E870D0648C809003C637B /* CCondVar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CCondVar.cpp; path = lib/mt/CCondVar.cpp; sourceTree = ""; }; + 4C5E870E0648C809003C637B /* CCondVar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCondVar.h; path = lib/mt/CCondVar.h; sourceTree = ""; }; + 4C5E870F0648C809003C637B /* CLock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CLock.cpp; path = lib/mt/CLock.cpp; sourceTree = ""; }; + 4C5E87100648C809003C637B /* CLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CLock.h; path = lib/mt/CLock.h; sourceTree = ""; }; + 4C5E87110648C809003C637B /* CMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CMutex.cpp; path = lib/mt/CMutex.cpp; sourceTree = ""; }; + 4C5E87120648C809003C637B /* CMutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CMutex.h; path = lib/mt/CMutex.h; sourceTree = ""; }; + 4C5E87130648C809003C637B /* CThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CThread.cpp; path = lib/mt/CThread.cpp; sourceTree = ""; }; + 4C5E87140648C809003C637B /* CThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CThread.h; path = lib/mt/CThread.h; sourceTree = ""; }; + 4C5E87150648C809003C637B /* XMT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XMT.cpp; path = lib/mt/XMT.cpp; sourceTree = ""; }; + 4C5E87160648C809003C637B /* XMT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMT.h; path = lib/mt/XMT.h; sourceTree = ""; }; + 4C5E87170648C809003C637B /* XThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XThread.h; path = lib/mt/XThread.h; sourceTree = ""; }; + 4C5E87230648C83C003C637B /* CStreamBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStreamBuffer.cpp; path = lib/io/CStreamBuffer.cpp; sourceTree = ""; }; + 4C5E87240648C83C003C637B /* CStreamBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStreamBuffer.h; path = lib/io/CStreamBuffer.h; sourceTree = ""; }; + 4C5E87250648C83C003C637B /* CStreamFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStreamFilter.cpp; path = lib/io/CStreamFilter.cpp; sourceTree = ""; }; + 4C5E87260648C83C003C637B /* CStreamFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStreamFilter.h; path = lib/io/CStreamFilter.h; sourceTree = ""; }; + 4C5E87270648C83C003C637B /* IStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IStream.cpp; path = lib/io/IStream.cpp; sourceTree = ""; }; + 4C5E87280648C83C003C637B /* IStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IStream.h; path = lib/io/IStream.h; sourceTree = ""; }; + 4C5E87290648C83C003C637B /* IStreamFilterFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IStreamFilterFactory.h; path = lib/io/IStreamFilterFactory.h; sourceTree = ""; }; + 4C5E872A0648C83C003C637B /* XIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XIO.cpp; path = lib/io/XIO.cpp; sourceTree = ""; }; + 4C5E872B0648C83C003C637B /* XIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XIO.h; path = lib/io/XIO.h; sourceTree = ""; }; + 4C5E87350648C870003C637B /* CNetworkAddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CNetworkAddress.cpp; path = lib/net/CNetworkAddress.cpp; sourceTree = ""; }; + 4C5E87360648C870003C637B /* CNetworkAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CNetworkAddress.h; path = lib/net/CNetworkAddress.h; sourceTree = ""; }; + 4C5E87370648C870003C637B /* CSocketMultiplexer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSocketMultiplexer.cpp; path = lib/net/CSocketMultiplexer.cpp; sourceTree = ""; }; + 4C5E87380648C870003C637B /* CSocketMultiplexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSocketMultiplexer.h; path = lib/net/CSocketMultiplexer.h; sourceTree = ""; }; + 4C5E87390648C870003C637B /* CTCPListenSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTCPListenSocket.cpp; path = lib/net/CTCPListenSocket.cpp; sourceTree = ""; }; + 4C5E873A0648C870003C637B /* CTCPListenSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTCPListenSocket.h; path = lib/net/CTCPListenSocket.h; sourceTree = ""; }; + 4C5E873B0648C870003C637B /* CTCPSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTCPSocket.cpp; path = lib/net/CTCPSocket.cpp; sourceTree = ""; }; + 4C5E873C0648C870003C637B /* CTCPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTCPSocket.h; path = lib/net/CTCPSocket.h; sourceTree = ""; }; + 4C5E873D0648C870003C637B /* CTCPSocketFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTCPSocketFactory.cpp; path = lib/net/CTCPSocketFactory.cpp; sourceTree = ""; }; + 4C5E873E0648C870003C637B /* CTCPSocketFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTCPSocketFactory.h; path = lib/net/CTCPSocketFactory.h; sourceTree = ""; }; + 4C5E873F0648C870003C637B /* IDataSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IDataSocket.cpp; path = lib/net/IDataSocket.cpp; sourceTree = ""; }; + 4C5E87400648C870003C637B /* IDataSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDataSocket.h; path = lib/net/IDataSocket.h; sourceTree = ""; }; + 4C5E87410648C870003C637B /* IListenSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IListenSocket.cpp; path = lib/net/IListenSocket.cpp; sourceTree = ""; }; + 4C5E87420648C870003C637B /* IListenSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IListenSocket.h; path = lib/net/IListenSocket.h; sourceTree = ""; }; + 4C5E87430648C870003C637B /* ISocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ISocket.cpp; path = lib/net/ISocket.cpp; sourceTree = ""; }; + 4C5E87440648C870003C637B /* ISocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISocket.h; path = lib/net/ISocket.h; sourceTree = ""; }; + 4C5E87450648C870003C637B /* ISocketFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISocketFactory.h; path = lib/net/ISocketFactory.h; sourceTree = ""; }; + 4C5E87460648C870003C637B /* ISocketMultiplexerJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISocketMultiplexerJob.h; path = lib/net/ISocketMultiplexerJob.h; sourceTree = ""; }; + 4C5E87470648C870003C637B /* TSocketMultiplexerMethodJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSocketMultiplexerMethodJob.h; path = lib/net/TSocketMultiplexerMethodJob.h; sourceTree = ""; }; + 4C5E87480648C870003C637B /* XSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XSocket.cpp; path = lib/net/XSocket.cpp; sourceTree = ""; }; + 4C5E87490648C870003C637B /* XSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XSocket.h; path = lib/net/XSocket.h; sourceTree = ""; }; + 4C5E875F0648C8BD003C637B /* CClipboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClipboard.cpp; path = lib/synergy/CClipboard.cpp; sourceTree = ""; }; + 4C5E87600648C8BD003C637B /* CClipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClipboard.h; path = lib/synergy/CClipboard.h; sourceTree = ""; }; + 4C5E87610648C8BD003C637B /* CKeyState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CKeyState.cpp; path = lib/synergy/CKeyState.cpp; sourceTree = ""; }; + 4C5E87620648C8BD003C637B /* CKeyState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CKeyState.h; path = lib/synergy/CKeyState.h; sourceTree = ""; }; + 4C5E87630648C8BD003C637B /* ClipboardTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClipboardTypes.h; path = lib/synergy/ClipboardTypes.h; sourceTree = ""; }; + 4C5E87640648C8BD003C637B /* CPacketStreamFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPacketStreamFilter.cpp; path = lib/synergy/CPacketStreamFilter.cpp; sourceTree = ""; }; + 4C5E87650648C8BD003C637B /* CPacketStreamFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPacketStreamFilter.h; path = lib/synergy/CPacketStreamFilter.h; sourceTree = ""; }; + 4C5E87660648C8BD003C637B /* CPlatformScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPlatformScreen.cpp; path = lib/synergy/CPlatformScreen.cpp; sourceTree = ""; }; + 4C5E87670648C8BD003C637B /* CPlatformScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPlatformScreen.h; path = lib/synergy/CPlatformScreen.h; sourceTree = ""; }; + 4C5E87680648C8BD003C637B /* CProtocolUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CProtocolUtil.cpp; path = lib/synergy/CProtocolUtil.cpp; sourceTree = ""; }; + 4C5E87690648C8BD003C637B /* CProtocolUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CProtocolUtil.h; path = lib/synergy/CProtocolUtil.h; sourceTree = ""; }; + 4C5E876A0648C8BD003C637B /* CScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CScreen.cpp; path = lib/synergy/CScreen.cpp; sourceTree = ""; }; + 4C5E876B0648C8BD003C637B /* CScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CScreen.h; path = lib/synergy/CScreen.h; sourceTree = ""; }; + 4C5E876C0648C8BD003C637B /* IClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IClient.h; path = lib/synergy/IClient.h; sourceTree = ""; }; + 4C5E876D0648C8BD003C637B /* IClipboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IClipboard.cpp; path = lib/synergy/IClipboard.cpp; sourceTree = ""; }; + 4C5E876E0648C8BD003C637B /* IClipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IClipboard.h; path = lib/synergy/IClipboard.h; sourceTree = ""; }; + 4C5E876F0648C8BD003C637B /* IKeyState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IKeyState.cpp; path = lib/synergy/IKeyState.cpp; sourceTree = ""; }; + 4C5E87700648C8BD003C637B /* IKeyState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IKeyState.h; path = lib/synergy/IKeyState.h; sourceTree = ""; }; + 4C5E87710648C8BD003C637B /* IPlatformScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IPlatformScreen.h; path = lib/synergy/IPlatformScreen.h; sourceTree = ""; }; + 4C5E87720648C8BD003C637B /* IPrimaryScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IPrimaryScreen.cpp; path = lib/synergy/IPrimaryScreen.cpp; sourceTree = ""; }; + 4C5E87730648C8BD003C637B /* IPrimaryScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IPrimaryScreen.h; path = lib/synergy/IPrimaryScreen.h; sourceTree = ""; }; + 4C5E87740648C8BD003C637B /* IScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IScreen.cpp; path = lib/synergy/IScreen.cpp; sourceTree = ""; }; + 4C5E87750648C8BD003C637B /* IScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IScreen.h; path = lib/synergy/IScreen.h; sourceTree = ""; }; + 4C5E87760648C8BD003C637B /* IScreenSaver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IScreenSaver.h; path = lib/synergy/IScreenSaver.h; sourceTree = ""; }; + 4C5E87770648C8BD003C637B /* ISecondaryScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISecondaryScreen.h; path = lib/synergy/ISecondaryScreen.h; sourceTree = ""; }; + 4C5E87780648C8BD003C637B /* KeyTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KeyTypes.h; path = lib/synergy/KeyTypes.h; sourceTree = ""; }; + 4C5E87790648C8BD003C637B /* MouseTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MouseTypes.h; path = lib/synergy/MouseTypes.h; sourceTree = ""; }; + 4C5E877A0648C8BD003C637B /* OptionTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionTypes.h; path = lib/synergy/OptionTypes.h; sourceTree = ""; }; + 4C5E877B0648C8BD003C637B /* ProtocolTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProtocolTypes.h; path = lib/synergy/ProtocolTypes.h; sourceTree = ""; }; + 4C5E877C0648C8BD003C637B /* XScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XScreen.cpp; path = lib/synergy/XScreen.cpp; sourceTree = ""; }; + 4C5E877D0648C8BD003C637B /* XScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XScreen.h; path = lib/synergy/XScreen.h; sourceTree = ""; }; + 4C5E877E0648C8BD003C637B /* XSynergy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XSynergy.cpp; path = lib/synergy/XSynergy.cpp; sourceTree = ""; }; + 4C5E877F0648C8BD003C637B /* XSynergy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XSynergy.h; path = lib/synergy/XSynergy.h; sourceTree = ""; }; + 4C5E87A10648C913003C637B /* COSXClipboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboard.cpp; path = lib/platform/COSXClipboard.cpp; sourceTree = ""; }; + 4C5E87A20648C913003C637B /* COSXClipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboard.h; path = lib/platform/COSXClipboard.h; sourceTree = ""; }; + 4C5E87A30648C913003C637B /* COSXEventQueueBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXEventQueueBuffer.cpp; path = lib/platform/COSXEventQueueBuffer.cpp; sourceTree = ""; }; + 4C5E87A40648C913003C637B /* COSXEventQueueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXEventQueueBuffer.h; path = lib/platform/COSXEventQueueBuffer.h; sourceTree = ""; }; + 4C5E87A50648C913003C637B /* COSXKeyState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXKeyState.cpp; path = lib/platform/COSXKeyState.cpp; sourceTree = ""; }; + 4C5E87A60648C913003C637B /* COSXKeyState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXKeyState.h; path = lib/platform/COSXKeyState.h; sourceTree = ""; }; + 4C5E87A70648C913003C637B /* COSXScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXScreen.cpp; path = lib/platform/COSXScreen.cpp; sourceTree = ""; }; + 4C5E87A80648C913003C637B /* COSXScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXScreen.h; path = lib/platform/COSXScreen.h; sourceTree = ""; }; + 4C5E87B90648C969003C637B /* CClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClient.cpp; path = lib/client/CClient.cpp; sourceTree = ""; }; + 4C5E87BA0648C969003C637B /* CClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClient.h; path = lib/client/CClient.h; sourceTree = ""; }; + 4C5E87BB0648C969003C637B /* CServerProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CServerProxy.cpp; path = lib/client/CServerProxy.cpp; sourceTree = ""; }; + 4C5E87BC0648C969003C637B /* CServerProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CServerProxy.h; path = lib/client/CServerProxy.h; sourceTree = ""; }; + 4C5E87C10648C9D2003C637B /* CClientTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientTaskBarReceiver.cpp; path = cmd/synergyc/CClientTaskBarReceiver.cpp; sourceTree = ""; }; + 4C5E87C20648C9D2003C637B /* CClientTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientTaskBarReceiver.h; path = cmd/synergyc/CClientTaskBarReceiver.h; sourceTree = ""; }; + 4C5E87C30648C9D2003C637B /* COSXClientTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClientTaskBarReceiver.cpp; path = cmd/synergyc/COSXClientTaskBarReceiver.cpp; sourceTree = ""; }; + 4C5E87C40648C9D2003C637B /* COSXClientTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClientTaskBarReceiver.h; path = cmd/synergyc/COSXClientTaskBarReceiver.h; sourceTree = ""; }; + 4C5E87C50648C9D2003C637B /* synergyc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = synergyc.cpp; path = cmd/synergyc/synergyc.cpp; sourceTree = ""; }; + 4C5E87D00648CA75003C637B /* XArch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XArch.cpp; path = lib/arch/XArch.cpp; sourceTree = ""; }; + 4C7D7CD9066319560097CA11 /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = common.h; path = lib/common/common.h; sourceTree = ""; }; + 4CB43778063E406A00969041 /* libclient.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libclient.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB437B5063E443800969041 /* libarch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libarch.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB437D1063E44C200969041 /* libbase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libbase.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB43801063E45F200969041 /* libmt.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libmt.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB43824063E46AB00969041 /* libio.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libio.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB43848063E475800969041 /* libnet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libnet.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB4386E063E47F800969041 /* libsynergy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libsynergy.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB438B1063E488600969041 /* libplatform.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libplatform.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB43914063E497700969041 /* synergyc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = synergyc; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CB43923063E4B1300969041 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; + 4CD0348F0650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboardAnyTextConverter.cpp; path = lib/platform/COSXClipboardAnyTextConverter.cpp; sourceTree = ""; }; + 4CD034900650B6F100525ED1 /* COSXClipboardAnyTextConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboardAnyTextConverter.h; path = lib/platform/COSXClipboardAnyTextConverter.h; sourceTree = ""; }; + 4CD034910650B6F100525ED1 /* COSXClipboardTextConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboardTextConverter.cpp; path = lib/platform/COSXClipboardTextConverter.cpp; sourceTree = ""; }; + 4CD034920650B6F100525ED1 /* COSXClipboardTextConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboardTextConverter.h; path = lib/platform/COSXClipboardTextConverter.h; sourceTree = ""; }; + 4CD034930650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboardUTF16Converter.cpp; path = lib/platform/COSXClipboardUTF16Converter.cpp; sourceTree = ""; }; + 4CD034940650B6F100525ED1 /* COSXClipboardUTF16Converter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboardUTF16Converter.h; path = lib/platform/COSXClipboardUTF16Converter.h; sourceTree = ""; }; + 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = ""; }; + 6828A1A8081DF7AB003D9989 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 6828A1A9081DF7AB003D9989 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; + 6828A1C1081DF9EB003D9989 /* OSXScreenSaverControl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = OSXScreenSaverControl.h; path = lib/platform/OSXScreenSaverControl.h; sourceTree = ""; }; + 6828A237081DFAF9003D9989 /* ScreenSaver.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScreenSaver.framework; path = /System/Library/Frameworks/ScreenSaver.framework; sourceTree = ""; }; + 684B63600996FB2800FE7CC7 /* CClientProxy1_3.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_3.cpp; path = lib/server/CClientProxy1_3.cpp; sourceTree = ""; }; + 684B63610996FB2800FE7CC7 /* CClientProxy1_3.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_3.h; path = lib/server/CClientProxy1_3.h; sourceTree = ""; }; + 68871676073EBF6F00C5ABE7 /* CArchSystemUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchSystemUnix.cpp; path = lib/arch/CArchSystemUnix.cpp; sourceTree = ""; }; + 68871677073EBF6F00C5ABE7 /* CArchSystemUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchSystemUnix.h; path = lib/arch/CArchSystemUnix.h; sourceTree = ""; }; + 68871678073EBF6F00C5ABE7 /* IArchConsole.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchConsole.h; path = lib/arch/IArchConsole.h; sourceTree = ""; }; + 68871679073EBF6F00C5ABE7 /* IArchDaemon.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchDaemon.h; path = lib/arch/IArchDaemon.h; sourceTree = ""; }; + 6887167A073EBF6F00C5ABE7 /* IArchFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchFile.h; path = lib/arch/IArchFile.h; sourceTree = ""; }; + 6887167B073EBF6F00C5ABE7 /* IArchLog.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchLog.h; path = lib/arch/IArchLog.h; sourceTree = ""; }; + 6887167C073EBF6F00C5ABE7 /* IArchMultithread.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchMultithread.h; path = lib/arch/IArchMultithread.h; sourceTree = ""; }; + 6887167D073EBF6F00C5ABE7 /* IArchNetwork.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchNetwork.h; path = lib/arch/IArchNetwork.h; sourceTree = ""; }; + 6887167E073EBF6F00C5ABE7 /* IArchSleep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchSleep.h; path = lib/arch/IArchSleep.h; sourceTree = ""; }; + 6887167F073EBF6F00C5ABE7 /* IArchString.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchString.h; path = lib/arch/IArchString.h; sourceTree = ""; }; + 68871680073EBF6F00C5ABE7 /* IArchSystem.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchSystem.h; path = lib/arch/IArchSystem.h; sourceTree = ""; }; + 68871681073EBF7000C5ABE7 /* IArchTaskBar.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchTaskBar.h; path = lib/arch/IArchTaskBar.h; sourceTree = ""; }; + 68871682073EBF7000C5ABE7 /* IArchTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchTaskBarReceiver.h; path = lib/arch/IArchTaskBarReceiver.h; sourceTree = ""; }; + 68871683073EBF7000C5ABE7 /* IArchTime.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchTime.h; path = lib/arch/IArchTime.h; sourceTree = ""; }; + 68871684073EBF7000C5ABE7 /* XArch.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = XArch.h; path = lib/arch/XArch.h; sourceTree = ""; }; + 68871699073EC02100C5ABE7 /* MacOSXPrecomp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = MacOSXPrecomp.h; path = lib/common/MacOSXPrecomp.h; sourceTree = ""; }; + 688925A709DF64B6002EB18C /* CBaseClientProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CBaseClientProxy.cpp; path = lib/server/CBaseClientProxy.cpp; sourceTree = ""; }; + 688925A809DF64B6002EB18C /* CBaseClientProxy.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CBaseClientProxy.h; path = lib/server/CBaseClientProxy.h; sourceTree = ""; }; + 68968F5A073EC484004B2F9B /* CArchDaemonNone.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchDaemonNone.cpp; path = lib/arch/CArchDaemonNone.cpp; sourceTree = ""; }; + 68968F5B073EC484004B2F9B /* CArchTaskBarXWindows.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchTaskBarXWindows.cpp; path = lib/arch/CArchTaskBarXWindows.cpp; sourceTree = ""; }; + 68968F5C073EC484004B2F9B /* CArchTaskBarXWindows.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchTaskBarXWindows.h; path = lib/arch/CArchTaskBarXWindows.h; sourceTree = ""; }; + 689D73320956490500FFAB1D /* CInputFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CInputFilter.cpp; path = lib/server/CInputFilter.cpp; sourceTree = ""; }; + 689D73330956490500FFAB1D /* CInputFilter.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CInputFilter.h; path = lib/server/CInputFilter.h; sourceTree = ""; }; + 689D7337095649AF00FFAB1D /* CKeyMap.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CKeyMap.cpp; path = lib/synergy/CKeyMap.cpp; sourceTree = ""; }; + 689D7338095649AF00FFAB1D /* CKeyMap.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CKeyMap.h; path = lib/synergy/CKeyMap.h; sourceTree = ""; }; + 68D87A6409A00D8800856835 /* KeyTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = KeyTypes.cpp; path = lib/synergy/KeyTypes.cpp; sourceTree = ""; }; + 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = COSXScreenSaverUtil.m; path = lib/platform/COSXScreenSaverUtil.m; sourceTree = ""; }; + 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = COSXScreenSaverUtil.h; path = lib/platform/COSXScreenSaverUtil.h; sourceTree = ""; }; + 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = COSXScreenSaver.cpp; path = lib/platform/COSXScreenSaver.cpp; sourceTree = ""; }; + 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = COSXScreenSaver.h; path = lib/platform/COSXScreenSaver.h; sourceTree = ""; }; + 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ProtocolTypes.cpp; path = lib/synergy/ProtocolTypes.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4C537F5A064E2B0700D3815C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C537F64064E2B4B00D3815C /* Carbon.framework in Frameworks */, + 4C537F5B064E2B4200D3815C /* libcommon.a in Frameworks */, + 4C537F5C064E2B4200D3815C /* libarch.a in Frameworks */, + 4C537F5D064E2B4200D3815C /* libbase.a in Frameworks */, + 4C537F5E064E2B4200D3815C /* libmt.a in Frameworks */, + 4C537F5F064E2B4200D3815C /* libio.a in Frameworks */, + 4C537F60064E2B4200D3815C /* libnet.a in Frameworks */, + 4C537F61064E2B4200D3815C /* libserver.a in Frameworks */, + 4C537F62064E2B4200D3815C /* libsynergy.a in Frameworks */, + 4C537F63064E2B4200D3815C /* libplatform.a in Frameworks */, + 6828A1AD081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */, + 6828A1AE081DF7AB003D9989 /* Foundation.framework in Frameworks */, + 6828A1AF081DF7AB003D9989 /* IOKit.framework in Frameworks */, + 6828A239081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */, + CAB4475F081E367700724B8D /* libclient.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43930063E4B4300969041 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CB43934063E4B4A00969041 /* Carbon.framework in Frameworks */, + 4C5E87CF0648CA4B003C637B /* libcommon.a in Frameworks */, + 6828A1AA081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */, + 6828A1AB081DF7AB003D9989 /* Foundation.framework in Frameworks */, + 6828A1AC081DF7AB003D9989 /* IOKit.framework in Frameworks */, + 6828A238081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */, + CAB44760081E368100724B8D /* libarch.a in Frameworks */, + CAB44761081E368200724B8D /* libbase.a in Frameworks */, + CAB44762081E368300724B8D /* libclient.a in Frameworks */, + CAB44764081E368700724B8D /* libio.a in Frameworks */, + CAB44765081E368800724B8D /* libmt.a in Frameworks */, + CAB44766081E368A00724B8D /* libnet.a in Frameworks */, + CAB44767081E368B00724B8D /* libplatform.a in Frameworks */, + CAB44768081E368D00724B8D /* libsynergy.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0249A662FF388D9811CA2CEA /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 6828A237081DFAF9003D9989 /* ScreenSaver.framework */, + 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */, + 6828A1A8081DF7AB003D9989 /* Foundation.framework */, + 6828A1A9081DF7AB003D9989 /* IOKit.framework */, + 4CB43923063E4B1300969041 /* Carbon.framework */, + ); + name = "External Frameworks and Libraries"; + sourceTree = ""; + }; + 08FB7794FE84155DC02AAC07 /* synergy */ = { + isa = PBXGroup; + children = ( + 08FB7795FE84155DC02AAC07 /* Source */, + 0249A662FF388D9811CA2CEA /* External Frameworks and Libraries */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = synergy; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 4CB4378B063E432C00969041 /* Arch */, + 4CB437D2063E44E400969041 /* Base */, + 4CB43779063E40B600969041 /* Client */, + 4C5E86CA0648C6FB003C637B /* Common */, + 4CB4381E063E469300969041 /* IO */, + 4CB437FB063E45D700969041 /* MT */, + 4CB43849063E478900969041 /* Net */, + 4CB438AD063E487200969041 /* Platform */, + 4C537F2B064E29C900D3815C /* Server */, + 4CB43866063E47C800969041 /* Synergy */, + 4CB43916063E4A1A00969041 /* SynergyC */, + 4C537F07064E283300D3815C /* SynergyS */, + ); + name = Source; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 4CB43778063E406A00969041 /* libclient.a */, + 4C5E868D0648C2ED003C637B /* libcommon.a */, + 4CB437B5063E443800969041 /* libarch.a */, + 4CB437D1063E44C200969041 /* libbase.a */, + 4CB43801063E45F200969041 /* libmt.a */, + 4CB43824063E46AB00969041 /* libio.a */, + 4CB43848063E475800969041 /* libnet.a */, + 4C537F31064E29F800D3815C /* libserver.a */, + 4CB4386E063E47F800969041 /* libsynergy.a */, + 4CB438B1063E488600969041 /* libplatform.a */, + 4CB43914063E497700969041 /* synergyc */, + 4C537F0D064E286700D3815C /* synergys */, + ); + name = Products; + sourceTree = ""; + }; + 4C537F07064E283300D3815C /* SynergyS */ = { + isa = PBXGroup; + children = ( + 4C537F0F064E293000D3815C /* COSXServerTaskBarReceiver.cpp */, + 4C537F10064E293000D3815C /* COSXServerTaskBarReceiver.h */, + 4C537F11064E293000D3815C /* CServerTaskBarReceiver.cpp */, + 4C537F12064E293000D3815C /* CServerTaskBarReceiver.h */, + 4C537F13064E293000D3815C /* synergys.cpp */, + ); + name = SynergyS; + sourceTree = ""; + }; + 4C537F2B064E29C900D3815C /* Server */ = { + isa = PBXGroup; + children = ( + 688925A709DF64B6002EB18C /* CBaseClientProxy.cpp */, + 688925A809DF64B6002EB18C /* CBaseClientProxy.h */, + 4C537F32064E2A0F00D3815C /* CClientListener.cpp */, + 4C537F33064E2A0F00D3815C /* CClientListener.h */, + 4C537F34064E2A0F00D3815C /* CClientProxy.cpp */, + 4C537F35064E2A0F00D3815C /* CClientProxy.h */, + 4C537F36064E2A0F00D3815C /* CClientProxy1_0.cpp */, + 4C537F37064E2A0F00D3815C /* CClientProxy1_0.h */, + 4C537F38064E2A0F00D3815C /* CClientProxy1_1.cpp */, + 4C537F39064E2A0F00D3815C /* CClientProxy1_1.h */, + 4C537F3A064E2A0F00D3815C /* CClientProxy1_2.cpp */, + 4C537F3B064E2A0F00D3815C /* CClientProxy1_2.h */, + 684B63600996FB2800FE7CC7 /* CClientProxy1_3.cpp */, + 684B63610996FB2800FE7CC7 /* CClientProxy1_3.h */, + 4C537F3C064E2A0F00D3815C /* CClientProxyUnknown.cpp */, + 4C537F3D064E2A0F00D3815C /* CClientProxyUnknown.h */, + 4C537F3E064E2A0F00D3815C /* CConfig.cpp */, + 4C537F3F064E2A0F00D3815C /* CConfig.h */, + 689D73320956490500FFAB1D /* CInputFilter.cpp */, + 689D73330956490500FFAB1D /* CInputFilter.h */, + 4C537F40064E2A0F00D3815C /* CPrimaryClient.cpp */, + 4C537F41064E2A0F00D3815C /* CPrimaryClient.h */, + 4C537F42064E2A0F00D3815C /* CServer.cpp */, + 4C537F43064E2A0F00D3815C /* CServer.h */, + ); + name = Server; + sourceTree = ""; + }; + 4C5E86CA0648C6FB003C637B /* Common */ = { + isa = PBXGroup; + children = ( + 4C7D7CD9066319560097CA11 /* common.h */, + 68871699073EC02100C5ABE7 /* MacOSXPrecomp.h */, + 4C5E86CB0648C725003C637B /* Version.cpp */, + ); + name = Common; + sourceTree = ""; + }; + 4CB43779063E40B600969041 /* Client */ = { + isa = PBXGroup; + children = ( + 4C5E87B90648C969003C637B /* CClient.cpp */, + 4C5E87BA0648C969003C637B /* CClient.h */, + 4C5E87BB0648C969003C637B /* CServerProxy.cpp */, + 4C5E87BC0648C969003C637B /* CServerProxy.h */, + ); + name = Client; + sourceTree = ""; + }; + 4CB4378B063E432C00969041 /* Arch */ = { + isa = PBXGroup; + children = ( + 4C5E86920648C412003C637B /* CArch.cpp */, + 4C5E86930648C412003C637B /* CArch.h */, + 4C5E86940648C412003C637B /* CArchConsoleUnix.cpp */, + 4C5E86950648C412003C637B /* CArchConsoleUnix.h */, + 68968F5A073EC484004B2F9B /* CArchDaemonNone.cpp */, + 4C5E86C10648C653003C637B /* CArchDaemonNone.h */, + 4C5E86960648C412003C637B /* CArchDaemonUnix.cpp */, + 4C5E86970648C412003C637B /* CArchDaemonUnix.h */, + 4C5E86980648C412003C637B /* CArchFileUnix.cpp */, + 4C5E86990648C412003C637B /* CArchFileUnix.h */, + 4C5E869A0648C412003C637B /* CArchLogUnix.cpp */, + 4C5E869B0648C412003C637B /* CArchLogUnix.h */, + 4C5E869C0648C412003C637B /* CArchMultithreadPosix.cpp */, + 4C5E869D0648C412003C637B /* CArchMultithreadPosix.h */, + 4C5E869E0648C412003C637B /* CArchNetworkBSD.cpp */, + 4C5E869F0648C412003C637B /* CArchNetworkBSD.h */, + 4C5E86A00648C412003C637B /* CArchSleepUnix.cpp */, + 4C5E86A10648C412003C637B /* CArchSleepUnix.h */, + 4C5E86A20648C412003C637B /* CArchStringUnix.cpp */, + 4C5E86A30648C412003C637B /* CArchStringUnix.h */, + 68871676073EBF6F00C5ABE7 /* CArchSystemUnix.cpp */, + 68871677073EBF6F00C5ABE7 /* CArchSystemUnix.h */, + 68968F5B073EC484004B2F9B /* CArchTaskBarXWindows.cpp */, + 68968F5C073EC484004B2F9B /* CArchTaskBarXWindows.h */, + 4C5E86A40648C412003C637B /* CArchTimeUnix.cpp */, + 4C5E86A50648C412003C637B /* CArchTimeUnix.h */, + 68871678073EBF6F00C5ABE7 /* IArchConsole.h */, + 68871679073EBF6F00C5ABE7 /* IArchDaemon.h */, + 6887167A073EBF6F00C5ABE7 /* IArchFile.h */, + 6887167B073EBF6F00C5ABE7 /* IArchLog.h */, + 6887167C073EBF6F00C5ABE7 /* IArchMultithread.h */, + 6887167D073EBF6F00C5ABE7 /* IArchNetwork.h */, + 6887167E073EBF6F00C5ABE7 /* IArchSleep.h */, + 6887167F073EBF6F00C5ABE7 /* IArchString.h */, + 68871680073EBF6F00C5ABE7 /* IArchSystem.h */, + 68871681073EBF7000C5ABE7 /* IArchTaskBar.h */, + 68871682073EBF7000C5ABE7 /* IArchTaskBarReceiver.h */, + 68871683073EBF7000C5ABE7 /* IArchTime.h */, + 68871684073EBF7000C5ABE7 /* XArch.h */, + 4C5E87D00648CA75003C637B /* XArch.cpp */, + 4C5E86A70648C412003C637B /* XArchUnix.cpp */, + 4C5E86A80648C412003C637B /* XArchUnix.h */, + ); + name = Arch; + sourceTree = ""; + }; + 4CB437D2063E44E400969041 /* Base */ = { + isa = PBXGroup; + children = ( + 4C5E86CD0648C7B9003C637B /* CEvent.cpp */, + 4C5E86CE0648C7B9003C637B /* CEvent.h */, + 4C5E86CF0648C7B9003C637B /* CEventQueue.cpp */, + 4C5E86D00648C7B9003C637B /* CEventQueue.h */, + 4C5E86D10648C7B9003C637B /* CFunctionEventJob.cpp */, + 4C5E86D20648C7B9003C637B /* CFunctionEventJob.h */, + 4C5E86D30648C7B9003C637B /* CFunctionJob.cpp */, + 4C5E86D40648C7B9003C637B /* CFunctionJob.h */, + 4C5E86D50648C7B9003C637B /* CLog.cpp */, + 4C5E86D60648C7B9003C637B /* CLog.h */, + 4C5E86D70648C7B9003C637B /* CPriorityQueue.h */, + 4C5E86D80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp */, + 4C5E86D90648C7B9003C637B /* CSimpleEventQueueBuffer.h */, + 4C5E86DA0648C7B9003C637B /* CStopwatch.cpp */, + 4C5E86DB0648C7B9003C637B /* CStopwatch.h */, + 4C5E86DC0648C7B9003C637B /* CString.h */, + 4C5E86DD0648C7B9003C637B /* CStringUtil.cpp */, + 4C5E86DE0648C7B9003C637B /* CStringUtil.h */, + 4C5E86DF0648C7B9003C637B /* CUnicode.cpp */, + 4C5E86E00648C7B9003C637B /* CUnicode.h */, + 4C5E86E10648C7B9003C637B /* IEventJob.h */, + 4C5E86E20648C7B9003C637B /* IEventQueue.cpp */, + 4C5E86E30648C7B9003C637B /* IEventQueue.h */, + 4C5E86E40648C7B9003C637B /* IEventQueueBuffer.h */, + 4C5E86E50648C7B9003C637B /* IJob.h */, + 4C5E86E60648C7B9003C637B /* ILogOutputter.h */, + 4C5E86E70648C7B9003C637B /* LogOutputters.cpp */, + 4C5E86E80648C7B9003C637B /* LogOutputters.h */, + 4C5E86E90648C7B9003C637B /* TMethodEventJob.h */, + 4C5E86EA0648C7B9003C637B /* TMethodJob.h */, + 4C5E86EB0648C7B9003C637B /* XBase.cpp */, + 4C5E86EC0648C7B9003C637B /* XBase.h */, + ); + name = Base; + sourceTree = ""; + }; + 4CB437FB063E45D700969041 /* MT */ = { + isa = PBXGroup; + children = ( + 4C5E870D0648C809003C637B /* CCondVar.cpp */, + 4C5E870E0648C809003C637B /* CCondVar.h */, + 4C5E870F0648C809003C637B /* CLock.cpp */, + 4C5E87100648C809003C637B /* CLock.h */, + 4C5E87110648C809003C637B /* CMutex.cpp */, + 4C5E87120648C809003C637B /* CMutex.h */, + 4C5E87130648C809003C637B /* CThread.cpp */, + 4C5E87140648C809003C637B /* CThread.h */, + 4C5E87150648C809003C637B /* XMT.cpp */, + 4C5E87160648C809003C637B /* XMT.h */, + 4C5E87170648C809003C637B /* XThread.h */, + ); + name = MT; + sourceTree = ""; + }; + 4CB4381E063E469300969041 /* IO */ = { + isa = PBXGroup; + children = ( + 4C5E87230648C83C003C637B /* CStreamBuffer.cpp */, + 4C5E87240648C83C003C637B /* CStreamBuffer.h */, + 4C5E87250648C83C003C637B /* CStreamFilter.cpp */, + 4C5E87260648C83C003C637B /* CStreamFilter.h */, + 4C5E87270648C83C003C637B /* IStream.cpp */, + 4C5E87280648C83C003C637B /* IStream.h */, + 4C5E87290648C83C003C637B /* IStreamFilterFactory.h */, + 4C5E872A0648C83C003C637B /* XIO.cpp */, + 4C5E872B0648C83C003C637B /* XIO.h */, + ); + name = IO; + sourceTree = ""; + }; + 4CB43849063E478900969041 /* Net */ = { + isa = PBXGroup; + children = ( + 4C5E87350648C870003C637B /* CNetworkAddress.cpp */, + 4C5E87360648C870003C637B /* CNetworkAddress.h */, + 4C5E87370648C870003C637B /* CSocketMultiplexer.cpp */, + 4C5E87380648C870003C637B /* CSocketMultiplexer.h */, + 4C5E87390648C870003C637B /* CTCPListenSocket.cpp */, + 4C5E873A0648C870003C637B /* CTCPListenSocket.h */, + 4C5E873B0648C870003C637B /* CTCPSocket.cpp */, + 4C5E873C0648C870003C637B /* CTCPSocket.h */, + 4C5E873D0648C870003C637B /* CTCPSocketFactory.cpp */, + 4C5E873E0648C870003C637B /* CTCPSocketFactory.h */, + 4C5E873F0648C870003C637B /* IDataSocket.cpp */, + 4C5E87400648C870003C637B /* IDataSocket.h */, + 4C5E87410648C870003C637B /* IListenSocket.cpp */, + 4C5E87420648C870003C637B /* IListenSocket.h */, + 4C5E87430648C870003C637B /* ISocket.cpp */, + 4C5E87440648C870003C637B /* ISocket.h */, + 4C5E87450648C870003C637B /* ISocketFactory.h */, + 4C5E87460648C870003C637B /* ISocketMultiplexerJob.h */, + 4C5E87470648C870003C637B /* TSocketMultiplexerMethodJob.h */, + 4C5E87480648C870003C637B /* XSocket.cpp */, + 4C5E87490648C870003C637B /* XSocket.h */, + ); + name = Net; + sourceTree = ""; + }; + 4CB43866063E47C800969041 /* Synergy */ = { + isa = PBXGroup; + children = ( + 4C5E875F0648C8BD003C637B /* CClipboard.cpp */, + 4C5E87600648C8BD003C637B /* CClipboard.h */, + 689D7337095649AF00FFAB1D /* CKeyMap.cpp */, + 689D7338095649AF00FFAB1D /* CKeyMap.h */, + 4C5E87610648C8BD003C637B /* CKeyState.cpp */, + 4C5E87620648C8BD003C637B /* CKeyState.h */, + 4C5E87630648C8BD003C637B /* ClipboardTypes.h */, + 4C5E87640648C8BD003C637B /* CPacketStreamFilter.cpp */, + 4C5E87650648C8BD003C637B /* CPacketStreamFilter.h */, + 4C5E87660648C8BD003C637B /* CPlatformScreen.cpp */, + 4C5E87670648C8BD003C637B /* CPlatformScreen.h */, + 4C5E87680648C8BD003C637B /* CProtocolUtil.cpp */, + 4C5E87690648C8BD003C637B /* CProtocolUtil.h */, + 4C5E876A0648C8BD003C637B /* CScreen.cpp */, + 4C5E876B0648C8BD003C637B /* CScreen.h */, + 4C5E876C0648C8BD003C637B /* IClient.h */, + 4C5E876D0648C8BD003C637B /* IClipboard.cpp */, + 4C5E876E0648C8BD003C637B /* IClipboard.h */, + 4C5E876F0648C8BD003C637B /* IKeyState.cpp */, + 4C5E87700648C8BD003C637B /* IKeyState.h */, + 4C5E87710648C8BD003C637B /* IPlatformScreen.h */, + 4C5E87720648C8BD003C637B /* IPrimaryScreen.cpp */, + 4C5E87730648C8BD003C637B /* IPrimaryScreen.h */, + 4C5E87740648C8BD003C637B /* IScreen.cpp */, + 4C5E87750648C8BD003C637B /* IScreen.h */, + 4C5E87760648C8BD003C637B /* IScreenSaver.h */, + 4C5E87770648C8BD003C637B /* ISecondaryScreen.h */, + 68D87A6409A00D8800856835 /* KeyTypes.cpp */, + 4C5E87780648C8BD003C637B /* KeyTypes.h */, + 4C5E87790648C8BD003C637B /* MouseTypes.h */, + 4C5E877A0648C8BD003C637B /* OptionTypes.h */, + 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */, + 4C5E877B0648C8BD003C637B /* ProtocolTypes.h */, + 4C5E877C0648C8BD003C637B /* XScreen.cpp */, + 4C5E877D0648C8BD003C637B /* XScreen.h */, + 4C5E877E0648C8BD003C637B /* XSynergy.cpp */, + 4C5E877F0648C8BD003C637B /* XSynergy.h */, + ); + name = Synergy; + sourceTree = ""; + }; + 4CB438AD063E487200969041 /* Platform */ = { + isa = PBXGroup; + children = ( + 4C5E87A10648C913003C637B /* COSXClipboard.cpp */, + 4C5E87A20648C913003C637B /* COSXClipboard.h */, + 4CD0348F0650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp */, + 4CD034900650B6F100525ED1 /* COSXClipboardAnyTextConverter.h */, + 4CD034910650B6F100525ED1 /* COSXClipboardTextConverter.cpp */, + 4CD034920650B6F100525ED1 /* COSXClipboardTextConverter.h */, + 4CD034930650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp */, + 4CD034940650B6F100525ED1 /* COSXClipboardUTF16Converter.h */, + 4C5E87A30648C913003C637B /* COSXEventQueueBuffer.cpp */, + 4C5E87A40648C913003C637B /* COSXEventQueueBuffer.h */, + 4C5E87A50648C913003C637B /* COSXKeyState.cpp */, + 4C5E87A60648C913003C637B /* COSXKeyState.h */, + 4C5E87A70648C913003C637B /* COSXScreen.cpp */, + 4C5E87A80648C913003C637B /* COSXScreen.h */, + 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */, + 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */, + 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */, + 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */, + 6828A1C1081DF9EB003D9989 /* OSXScreenSaverControl.h */, + ); + name = Platform; + sourceTree = ""; + }; + 4CB43916063E4A1A00969041 /* SynergyC */ = { + isa = PBXGroup; + children = ( + 4C5E87C10648C9D2003C637B /* CClientTaskBarReceiver.cpp */, + 4C5E87C20648C9D2003C637B /* CClientTaskBarReceiver.h */, + 4C5E87C30648C9D2003C637B /* COSXClientTaskBarReceiver.cpp */, + 4C5E87C40648C9D2003C637B /* COSXClientTaskBarReceiver.h */, + 4C5E87C50648C9D2003C637B /* synergyc.cpp */, + ); + name = SynergyC; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 4C537F0A064E286700D3815C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C537F15064E293000D3815C /* COSXServerTaskBarReceiver.h in Headers */, + 4C537F17064E293000D3815C /* CServerTaskBarReceiver.h in Headers */, + 4C7D7CDA066319560097CA11 /* common.h in Headers */, + 68FBBB25089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */, + 68FBBB2B089F072D0016DF44 /* COSXScreenSaver.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C537F2E064E29F800D3815C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C537F45064E2A0F00D3815C /* CClientListener.h in Headers */, + 4C537F47064E2A0F00D3815C /* CClientProxy.h in Headers */, + 4C537F49064E2A0F00D3815C /* CClientProxy1_0.h in Headers */, + 4C537F4B064E2A0F00D3815C /* CClientProxy1_1.h in Headers */, + 4C537F4D064E2A0F00D3815C /* CClientProxy1_2.h in Headers */, + 684B63630996FB2800FE7CC7 /* CClientProxy1_3.h in Headers */, + 4C537F4F064E2A0F00D3815C /* CClientProxyUnknown.h in Headers */, + 4C537F51064E2A0F00D3815C /* CConfig.h in Headers */, + 689D73350956490500FFAB1D /* CInputFilter.h in Headers */, + 4C537F53064E2A0F00D3815C /* CPrimaryClient.h in Headers */, + 4C537F55064E2A0F00D3815C /* CServer.h in Headers */, + 4C7D7CE4066319560097CA11 /* common.h in Headers */, + 688925AA09DF64B6002EB18C /* CBaseClientProxy.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C5E868A0648C2ED003C637B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C7D7CDD066319560097CA11 /* common.h in Headers */, + 6887169B073EC03800C5ABE7 /* MacOSXPrecomp.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43775063E406A00969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87BE0648C969003C637B /* CClient.h in Headers */, + 4C5E87C00648C969003C637B /* CServerProxy.h in Headers */, + 4C7D7CE5066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437B2063E443800969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C7D7CDC066319560097CA11 /* common.h in Headers */, + 4C5E86AA0648C412003C637B /* CArch.h in Headers */, + 4C5E86AC0648C412003C637B /* CArchConsoleUnix.h in Headers */, + 4C5E86C30648C653003C637B /* CArchDaemonNone.h in Headers */, + 4C5E86AE0648C412003C637B /* CArchDaemonUnix.h in Headers */, + 4C5E86B00648C412003C637B /* CArchFileUnix.h in Headers */, + 4C5E86B20648C412003C637B /* CArchLogUnix.h in Headers */, + 4C5E86B40648C412003C637B /* CArchMultithreadPosix.h in Headers */, + 4C5E86B60648C412003C637B /* CArchNetworkBSD.h in Headers */, + 4C5E86B80648C412003C637B /* CArchSleepUnix.h in Headers */, + 4C5E86BA0648C412003C637B /* CArchStringUnix.h in Headers */, + 68871686073EBF7000C5ABE7 /* CArchSystemUnix.h in Headers */, + 68968F5F073EC484004B2F9B /* CArchTaskBarXWindows.h in Headers */, + 4C5E86BC0648C412003C637B /* CArchTimeUnix.h in Headers */, + 68871687073EBF7000C5ABE7 /* IArchConsole.h in Headers */, + 68871688073EBF7000C5ABE7 /* IArchDaemon.h in Headers */, + 68871689073EBF7000C5ABE7 /* IArchFile.h in Headers */, + 6887168A073EBF7000C5ABE7 /* IArchLog.h in Headers */, + 6887168B073EBF7000C5ABE7 /* IArchMultithread.h in Headers */, + 6887168C073EBF7000C5ABE7 /* IArchNetwork.h in Headers */, + 6887168D073EBF7000C5ABE7 /* IArchSleep.h in Headers */, + 6887168E073EBF7000C5ABE7 /* IArchString.h in Headers */, + 6887168F073EBF7000C5ABE7 /* IArchSystem.h in Headers */, + 68871690073EBF7000C5ABE7 /* IArchTaskBar.h in Headers */, + 68871691073EBF7000C5ABE7 /* IArchTaskBarReceiver.h in Headers */, + 68871692073EBF7000C5ABE7 /* IArchTime.h in Headers */, + 68871693073EBF7000C5ABE7 /* XArch.h in Headers */, + 4C5E86BF0648C412003C637B /* XArchUnix.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437CE063E44C200969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E86EE0648C7B9003C637B /* CEvent.h in Headers */, + 4C5E86F00648C7B9003C637B /* CEventQueue.h in Headers */, + 4C5E86F20648C7B9003C637B /* CFunctionEventJob.h in Headers */, + 4C5E86F40648C7B9003C637B /* CFunctionJob.h in Headers */, + 4C5E86F60648C7B9003C637B /* CLog.h in Headers */, + 4C5E86F70648C7B9003C637B /* CPriorityQueue.h in Headers */, + 4C5E86F90648C7B9003C637B /* CSimpleEventQueueBuffer.h in Headers */, + 4C5E86FB0648C7B9003C637B /* CStopwatch.h in Headers */, + 4C5E86FC0648C7B9003C637B /* CString.h in Headers */, + 4C5E86FE0648C7B9003C637B /* CStringUtil.h in Headers */, + 4C5E87000648C7B9003C637B /* CUnicode.h in Headers */, + 4C5E87010648C7B9003C637B /* IEventJob.h in Headers */, + 4C5E87030648C7B9003C637B /* IEventQueue.h in Headers */, + 4C5E87040648C7B9003C637B /* IEventQueueBuffer.h in Headers */, + 4C5E87050648C7B9003C637B /* IJob.h in Headers */, + 4C5E87060648C7B9003C637B /* ILogOutputter.h in Headers */, + 4C5E87080648C7B9003C637B /* LogOutputters.h in Headers */, + 4C5E87090648C7B9003C637B /* TMethodEventJob.h in Headers */, + 4C5E870A0648C7B9003C637B /* TMethodJob.h in Headers */, + 4C5E870C0648C7B9003C637B /* XBase.h in Headers */, + 4C7D7CDE066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437FE063E45F200969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87190648C809003C637B /* CCondVar.h in Headers */, + 4C5E871B0648C809003C637B /* CLock.h in Headers */, + 4C5E871D0648C809003C637B /* CMutex.h in Headers */, + 4C5E871F0648C809003C637B /* CThread.h in Headers */, + 4C5E87210648C809003C637B /* XMT.h in Headers */, + 4C5E87220648C809003C637B /* XThread.h in Headers */, + 4C7D7CDF066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43821063E46AB00969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E872D0648C83C003C637B /* CStreamBuffer.h in Headers */, + 4C5E872F0648C83C003C637B /* CStreamFilter.h in Headers */, + 4C5E87310648C83C003C637B /* IStream.h in Headers */, + 4C5E87320648C83C003C637B /* IStreamFilterFactory.h in Headers */, + 4C5E87340648C83C003C637B /* XIO.h in Headers */, + 4C7D7CE0066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43845063E475800969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E874B0648C870003C637B /* CNetworkAddress.h in Headers */, + 4C5E874D0648C870003C637B /* CSocketMultiplexer.h in Headers */, + 4C5E874F0648C870003C637B /* CTCPListenSocket.h in Headers */, + 4C5E87510648C870003C637B /* CTCPSocket.h in Headers */, + 4C5E87530648C870003C637B /* CTCPSocketFactory.h in Headers */, + 4C5E87550648C870003C637B /* IDataSocket.h in Headers */, + 4C5E87570648C870003C637B /* IListenSocket.h in Headers */, + 4C5E87590648C870003C637B /* ISocket.h in Headers */, + 4C5E875A0648C870003C637B /* ISocketFactory.h in Headers */, + 4C5E875B0648C870003C637B /* ISocketMultiplexerJob.h in Headers */, + 4C5E875C0648C870003C637B /* TSocketMultiplexerMethodJob.h in Headers */, + 4C5E875E0648C870003C637B /* XSocket.h in Headers */, + 4C7D7CE1066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB4386B063E47F800969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87810648C8BD003C637B /* CClipboard.h in Headers */, + 689D733A095649AF00FFAB1D /* CKeyMap.h in Headers */, + 4C5E87830648C8BD003C637B /* CKeyState.h in Headers */, + 4C5E87840648C8BD003C637B /* ClipboardTypes.h in Headers */, + 4C5E87860648C8BD003C637B /* CPacketStreamFilter.h in Headers */, + 4C5E87880648C8BD003C637B /* CPlatformScreen.h in Headers */, + 4C5E878A0648C8BD003C637B /* CProtocolUtil.h in Headers */, + 4C5E878C0648C8BD003C637B /* CScreen.h in Headers */, + 4C5E878D0648C8BD003C637B /* IClient.h in Headers */, + 4C5E878F0648C8BD003C637B /* IClipboard.h in Headers */, + 4C5E87910648C8BD003C637B /* IKeyState.h in Headers */, + 4C5E87920648C8BD003C637B /* IPlatformScreen.h in Headers */, + 4C5E87940648C8BD003C637B /* IPrimaryScreen.h in Headers */, + 4C5E87960648C8BD003C637B /* IScreen.h in Headers */, + 4C5E87970648C8BD003C637B /* IScreenSaver.h in Headers */, + 4C5E87980648C8BD003C637B /* ISecondaryScreen.h in Headers */, + 4C5E87990648C8BD003C637B /* KeyTypes.h in Headers */, + 4C5E879A0648C8BD003C637B /* MouseTypes.h in Headers */, + 4C5E879B0648C8BD003C637B /* OptionTypes.h in Headers */, + 4C5E879C0648C8BD003C637B /* ProtocolTypes.h in Headers */, + 4C5E879E0648C8BD003C637B /* XScreen.h in Headers */, + 4C5E87A00648C8BD003C637B /* XSynergy.h in Headers */, + 4C7D7CE2066319560097CA11 /* common.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB438AE063E488600969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C7D7CE3066319560097CA11 /* common.h in Headers */, + 4C5E87AE0648C913003C637B /* COSXClipboard.h in Headers */, + 4CD034960650B6F100525ED1 /* COSXClipboardAnyTextConverter.h in Headers */, + 4CD034980650B6F100525ED1 /* COSXClipboardTextConverter.h in Headers */, + 4CD0349A0650B6F100525ED1 /* COSXClipboardUTF16Converter.h in Headers */, + 4C5E87B00648C913003C637B /* COSXEventQueueBuffer.h in Headers */, + 4C5E87B20648C913003C637B /* COSXKeyState.h in Headers */, + 4C5E87B40648C913003C637B /* COSXScreen.h in Headers */, + 68FBBB2D089F07920016DF44 /* COSXScreenSaver.h in Headers */, + 68FBBB2F089F079B0016DF44 /* COSXScreenSaverUtil.h in Headers */, + 6828A1C2081DF9EB003D9989 /* OSXScreenSaverControl.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43911063E497700969041 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87C70648C9D2003C637B /* CClientTaskBarReceiver.h in Headers */, + 4C5E87C90648C9D2003C637B /* COSXClientTaskBarReceiver.h in Headers */, + 4C7D7CDB066319560097CA11 /* common.h in Headers */, + 68FBBB24089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */, + 68FBBB29089F072D0016DF44 /* COSXScreenSaver.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 4C537F0C064E286700D3815C /* synergys */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB14089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergys" */; + buildPhases = ( + 4C537F0A064E286700D3815C /* Headers */, + 4C537F0B064E286700D3815C /* Sources */, + 4C537F5A064E2B0700D3815C /* Frameworks */, + ); + buildRules = ( + 68FBBB53089F08940016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + PRODUCT_NAME = synergys; + }; + dependencies = ( + 4C537F1A064E298800D3815C /* PBXTargetDependency */, + 4C537F1C064E298800D3815C /* PBXTargetDependency */, + 4C537F1E064E298800D3815C /* PBXTargetDependency */, + 4C537F20064E298800D3815C /* PBXTargetDependency */, + 4C537F22064E298800D3815C /* PBXTargetDependency */, + 4C537F24064E298800D3815C /* PBXTargetDependency */, + 4C537F26064E298800D3815C /* PBXTargetDependency */, + 4C537F28064E298800D3815C /* PBXTargetDependency */, + 4C537F57064E2AA300D3815C /* PBXTargetDependency */, + ); + name = synergys; + productName = synergys; + productReference = 4C537F0D064E286700D3815C /* synergys */; + productType = "com.apple.product-type.tool"; + }; + 4C537F30064E29F800D3815C /* server */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB10089F06C20016DF44 /* Build configuration list for PBXNativeTarget "server" */; + buildPhases = ( + 4C537F2E064E29F800D3815C /* Headers */, + 4C537F2F064E29F800D3815C /* Sources */, + ); + buildRules = ( + 68FBBB4D089F087F0016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = server; + }; + dependencies = ( + ); + name = server; + productName = server; + productReference = 4C537F31064E29F800D3815C /* libserver.a */; + productType = "com.apple.product-type.library.static"; + }; + 4C5E868C0648C2ED003C637B /* common */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAEC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "common" */; + buildPhases = ( + 4C5E868A0648C2ED003C637B /* Headers */, + 4C5E868B0648C2ED003C637B /* Sources */, + ); + buildRules = ( + 68FBBB37089F08160016DF44 /* PBXBuildRule */, + ); + buildSettings = { + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = common; + }; + dependencies = ( + ); + name = common; + productName = common; + productReference = 4C5E868D0648C2ED003C637B /* libcommon.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB43777063E406A00969041 /* client */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB08089F06C20016DF44 /* Build configuration list for PBXNativeTarget "client" */; + buildPhases = ( + 4CB43775063E406A00969041 /* Headers */, + 4CB43776063E406A00969041 /* Sources */, + ); + buildRules = ( + 68FBBB4A089F08750016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = client; + }; + dependencies = ( + ); + name = client; + productName = client; + productReference = 4CB43778063E406A00969041 /* libclient.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB437B4063E443800969041 /* arch */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAE8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "arch" */; + buildPhases = ( + 4CB437B2063E443800969041 /* Headers */, + 4CB437B3063E443800969041 /* Sources */, + ); + buildRules = ( + 68FBBB32089F07E40016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = lib/common; + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = arch; + }; + dependencies = ( + ); + name = arch; + productName = arch; + productReference = 4CB437B5063E443800969041 /* libarch.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB437D0063E44C200969041 /* base */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAF0089F06C20016DF44 /* Build configuration list for PBXNativeTarget "base" */; + buildPhases = ( + 4CB437CE063E44C200969041 /* Headers */, + 4CB437CF063E44C200969041 /* Sources */, + ); + buildRules = ( + 68FBBB3A089F08200016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = base; + }; + dependencies = ( + ); + name = base; + productName = base; + productReference = 4CB437D1063E44C200969041 /* libbase.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB43800063E45F200969041 /* mt */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAF4089F06C20016DF44 /* Build configuration list for PBXNativeTarget "mt" */; + buildPhases = ( + 4CB437FE063E45F200969041 /* Headers */, + 4CB437FF063E45F200969041 /* Sources */, + ); + buildRules = ( + 68FBBB3D089F082E0016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = mt; + }; + dependencies = ( + ); + name = mt; + productName = mt; + productReference = 4CB43801063E45F200969041 /* libmt.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB43823063E46AB00969041 /* io */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAF8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "io" */; + buildPhases = ( + 4CB43821063E46AB00969041 /* Headers */, + 4CB43822063E46AB00969041 /* Sources */, + ); + buildRules = ( + 68FBBB3E089F084C0016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = io; + }; + dependencies = ( + ); + name = io; + productName = io; + productReference = 4CB43824063E46AB00969041 /* libio.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB43847063E475800969041 /* net */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBAFC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "net" */; + buildPhases = ( + 4CB43845063E475800969041 /* Headers */, + 4CB43846063E475800969041 /* Sources */, + ); + buildRules = ( + 68FBBB41089F08590016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = net; + }; + dependencies = ( + ); + name = net; + productName = net; + productReference = 4CB43848063E475800969041 /* libnet.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB4386D063E47F800969041 /* synergy */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB00089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergy" */; + buildPhases = ( + 4CB4386B063E47F800969041 /* Headers */, + 4CB4386C063E47F800969041 /* Sources */, + ); + buildRules = ( + 68FBBB44089F08620016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = synergy; + }; + dependencies = ( + ); + name = synergy; + productName = synergy; + productReference = 4CB4386E063E47F800969041 /* libsynergy.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB438B0063E488600969041 /* platform */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB04089F06C20016DF44 /* Build configuration list for PBXNativeTarget "platform" */; + buildPhases = ( + 4CB438AE063E488600969041 /* Headers */, + 4CB438AF063E488600969041 /* Sources */, + ); + buildRules = ( + 68FBBB47089F086B0016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/synergy, + ); + LIBRARY_STYLE = STATIC; + PRODUCT_NAME = platform; + }; + dependencies = ( + ); + name = platform; + productName = platform; + productReference = 4CB438B1063E488600969041 /* libplatform.a */; + productType = "com.apple.product-type.library.static"; + }; + 4CB43913063E497700969041 /* synergyc */ = { + isa = PBXNativeTarget; + buildConfigurationList = 68FBBB0C089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergyc" */; + buildPhases = ( + 4CB43911063E497700969041 /* Headers */, + 4CB43912063E497700969041 /* Sources */, + 4CB43930063E4B4300969041 /* Frameworks */, + ); + buildRules = ( + 68FBBB50089F08890016DF44 /* PBXBuildRule */, + ); + buildSettings = { + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + PRODUCT_NAME = synergyc; + }; + dependencies = ( + 4CB43948063E4E1600969041 /* PBXTargetDependency */, + 4C5E86900648C32E003C637B /* PBXTargetDependency */, + 4CB4394A063E4E1600969041 /* PBXTargetDependency */, + 4CB4394C063E4E1600969041 /* PBXTargetDependency */, + 4CB4394E063E4E1600969041 /* PBXTargetDependency */, + 4CB43950063E4E1600969041 /* PBXTargetDependency */, + 4CB43952063E4E1600969041 /* PBXTargetDependency */, + 4CB43954063E4E1600969041 /* PBXTargetDependency */, + 4CB43946063E4E1600969041 /* PBXTargetDependency */, + ); + name = synergyc; + productName = synergyc; + productReference = 4CB43914063E497700969041 /* synergyc */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 68FBBB1C089F06C20016DF44 /* Build configuration list for PBXProject "synergy" */; + buildSettings = { + MACOSX_DEPLOYMENT_TARGET = 10.2; + SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; + }; + buildStyles = ( + 014CEA460018CE2711CA2923 /* Development */, + 014CEA470018CE2711CA2923 /* Deployment */, + ); + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* synergy */; + projectDirPath = ""; + targets = ( + 4CD033E80650965F00525ED1 /* ALL */, + 4CB437B4063E443800969041 /* arch */, + 4C5E868C0648C2ED003C637B /* common */, + 4CB437D0063E44C200969041 /* base */, + 4CB43800063E45F200969041 /* mt */, + 4CB43823063E46AB00969041 /* io */, + 4CB43847063E475800969041 /* net */, + 4CB4386D063E47F800969041 /* synergy */, + 4CB438B0063E488600969041 /* platform */, + 4CB43777063E406A00969041 /* client */, + 4C537F30064E29F800D3815C /* server */, + 4CB43913063E497700969041 /* synergyc */, + 4C537F0C064E286700D3815C /* synergys */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 4C537F0B064E286700D3815C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C537F14064E293000D3815C /* COSXServerTaskBarReceiver.cpp in Sources */, + 4C537F16064E293000D3815C /* CServerTaskBarReceiver.cpp in Sources */, + 4C537F18064E293000D3815C /* synergys.cpp in Sources */, + 68FBBB22089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */, + 68FBBB2A089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */, + 68FBBB5C089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C537F2F064E29F800D3815C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C537F44064E2A0F00D3815C /* CClientListener.cpp in Sources */, + 4C537F46064E2A0F00D3815C /* CClientProxy.cpp in Sources */, + 4C537F48064E2A0F00D3815C /* CClientProxy1_0.cpp in Sources */, + 4C537F4A064E2A0F00D3815C /* CClientProxy1_1.cpp in Sources */, + 4C537F4C064E2A0F00D3815C /* CClientProxy1_2.cpp in Sources */, + 684B63620996FB2800FE7CC7 /* CClientProxy1_3.cpp in Sources */, + 4C537F4E064E2A0F00D3815C /* CClientProxyUnknown.cpp in Sources */, + 4C537F50064E2A0F00D3815C /* CConfig.cpp in Sources */, + 689D73340956490500FFAB1D /* CInputFilter.cpp in Sources */, + 4C537F52064E2A0F00D3815C /* CPrimaryClient.cpp in Sources */, + 4C537F54064E2A0F00D3815C /* CServer.cpp in Sources */, + 688925A909DF64B6002EB18C /* CBaseClientProxy.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4C5E868B0648C2ED003C637B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E86CC0648C726003C637B /* Version.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43776063E406A00969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87BD0648C969003C637B /* CClient.cpp in Sources */, + 4C5E87BF0648C969003C637B /* CServerProxy.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437B3063E443800969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E86A90648C412003C637B /* CArch.cpp in Sources */, + 4C5E86AB0648C412003C637B /* CArchConsoleUnix.cpp in Sources */, + 68968F5D073EC484004B2F9B /* CArchDaemonNone.cpp in Sources */, + 4C5E86AD0648C412003C637B /* CArchDaemonUnix.cpp in Sources */, + 4C5E86AF0648C412003C637B /* CArchFileUnix.cpp in Sources */, + 4C5E86B10648C412003C637B /* CArchLogUnix.cpp in Sources */, + 4C5E86B30648C412003C637B /* CArchMultithreadPosix.cpp in Sources */, + 4C5E86B50648C412003C637B /* CArchNetworkBSD.cpp in Sources */, + 4C5E86B70648C412003C637B /* CArchSleepUnix.cpp in Sources */, + 4C5E86B90648C412003C637B /* CArchStringUnix.cpp in Sources */, + 68871685073EBF7000C5ABE7 /* CArchSystemUnix.cpp in Sources */, + 68968F5E073EC484004B2F9B /* CArchTaskBarXWindows.cpp in Sources */, + 4C5E86BB0648C412003C637B /* CArchTimeUnix.cpp in Sources */, + 4C5E87D10648CA75003C637B /* XArch.cpp in Sources */, + 4C5E86BE0648C412003C637B /* XArchUnix.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437CF063E44C200969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E86ED0648C7B9003C637B /* CEvent.cpp in Sources */, + 4C5E86EF0648C7B9003C637B /* CEventQueue.cpp in Sources */, + 4C5E86F10648C7B9003C637B /* CFunctionEventJob.cpp in Sources */, + 4C5E86F30648C7B9003C637B /* CFunctionJob.cpp in Sources */, + 4C5E86F50648C7B9003C637B /* CLog.cpp in Sources */, + 4C5E86F80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp in Sources */, + 4C5E86FA0648C7B9003C637B /* CStopwatch.cpp in Sources */, + 4C5E86FD0648C7B9003C637B /* CStringUtil.cpp in Sources */, + 4C5E86FF0648C7B9003C637B /* CUnicode.cpp in Sources */, + 4C5E87020648C7B9003C637B /* IEventQueue.cpp in Sources */, + 4C5E87070648C7B9003C637B /* LogOutputters.cpp in Sources */, + 4C5E870B0648C7B9003C637B /* XBase.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB437FF063E45F200969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87180648C809003C637B /* CCondVar.cpp in Sources */, + 4C5E871A0648C809003C637B /* CLock.cpp in Sources */, + 4C5E871C0648C809003C637B /* CMutex.cpp in Sources */, + 4C5E871E0648C809003C637B /* CThread.cpp in Sources */, + 4C5E87200648C809003C637B /* XMT.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43822063E46AB00969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E872C0648C83C003C637B /* CStreamBuffer.cpp in Sources */, + 4C5E872E0648C83C003C637B /* CStreamFilter.cpp in Sources */, + 4C5E87300648C83C003C637B /* IStream.cpp in Sources */, + 4C5E87330648C83C003C637B /* XIO.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43846063E475800969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E874A0648C870003C637B /* CNetworkAddress.cpp in Sources */, + 4C5E874C0648C870003C637B /* CSocketMultiplexer.cpp in Sources */, + 4C5E874E0648C870003C637B /* CTCPListenSocket.cpp in Sources */, + 4C5E87500648C870003C637B /* CTCPSocket.cpp in Sources */, + 4C5E87520648C870003C637B /* CTCPSocketFactory.cpp in Sources */, + 4C5E87540648C870003C637B /* IDataSocket.cpp in Sources */, + 4C5E87560648C870003C637B /* IListenSocket.cpp in Sources */, + 4C5E87580648C870003C637B /* ISocket.cpp in Sources */, + 4C5E875D0648C870003C637B /* XSocket.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB4386C063E47F800969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87800648C8BD003C637B /* CClipboard.cpp in Sources */, + 689D7339095649AF00FFAB1D /* CKeyMap.cpp in Sources */, + 4C5E87820648C8BD003C637B /* CKeyState.cpp in Sources */, + 4C5E87850648C8BD003C637B /* CPacketStreamFilter.cpp in Sources */, + 4C5E87870648C8BD003C637B /* CPlatformScreen.cpp in Sources */, + 4C5E87890648C8BD003C637B /* CProtocolUtil.cpp in Sources */, + 4C5E878B0648C8BD003C637B /* CScreen.cpp in Sources */, + 4C5E878E0648C8BD003C637B /* IClipboard.cpp in Sources */, + 4C5E87900648C8BD003C637B /* IKeyState.cpp in Sources */, + 4C5E87930648C8BD003C637B /* IPrimaryScreen.cpp in Sources */, + 4C5E87950648C8BD003C637B /* IScreen.cpp in Sources */, + 68D87A6509A00D8800856835 /* KeyTypes.cpp in Sources */, + 68FBBB5D089F1BCE0016DF44 /* ProtocolTypes.cpp in Sources */, + 4C5E879D0648C8BD003C637B /* XScreen.cpp in Sources */, + 4C5E879F0648C8BD003C637B /* XSynergy.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB438AF063E488600969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87AD0648C913003C637B /* COSXClipboard.cpp in Sources */, + 4CD034950650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp in Sources */, + 4CD034970650B6F100525ED1 /* COSXClipboardTextConverter.cpp in Sources */, + 4CD034990650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp in Sources */, + 4C5E87AF0648C913003C637B /* COSXEventQueueBuffer.cpp in Sources */, + 4C5E87B10648C913003C637B /* COSXKeyState.cpp in Sources */, + 4C5E87B30648C913003C637B /* COSXScreen.cpp in Sources */, + 68FBBB2C089F07810016DF44 /* COSXScreenSaver.cpp in Sources */, + 68FBBB2E089F07970016DF44 /* COSXScreenSaverUtil.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CB43912063E497700969041 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C5E87C60648C9D2003C637B /* CClientTaskBarReceiver.cpp in Sources */, + 4C5E87C80648C9D2003C637B /* COSXClientTaskBarReceiver.cpp in Sources */, + 4C5E87CA0648C9D2003C637B /* synergyc.cpp in Sources */, + 68FBBB21089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */, + 68FBBB28089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */, + 68FBBB5B089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 4C537F1A064E298800D3815C /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 4C537F0C064E286700D3815C; - targetProxy = 4CD033EB0650968500525ED1; + target = 4CB437B4063E443800969041 /* arch */; + targetProxy = 4C537F19064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD0348F0650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXClipboardAnyTextConverter.cpp; - path = lib/platform/COSXClipboardAnyTextConverter.cpp; - refType = 4; - sourceTree = ""; + 4C537F1C064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C5E868C0648C2ED003C637B /* common */; + targetProxy = 4C537F1B064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034900650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXClipboardAnyTextConverter.h; - path = lib/platform/COSXClipboardAnyTextConverter.h; - refType = 4; - sourceTree = ""; + 4C537F1E064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB437D0063E44C200969041 /* base */; + targetProxy = 4C537F1D064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034910650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXClipboardTextConverter.cpp; - path = lib/platform/COSXClipboardTextConverter.cpp; - refType = 4; - sourceTree = ""; + 4C537F20064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43800063E45F200969041 /* mt */; + targetProxy = 4C537F1F064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034920650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXClipboardTextConverter.h; - path = lib/platform/COSXClipboardTextConverter.h; - refType = 4; - sourceTree = ""; + 4C537F22064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43823063E46AB00969041 /* io */; + targetProxy = 4C537F21064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034930650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = COSXClipboardUTF16Converter.cpp; - path = lib/platform/COSXClipboardUTF16Converter.cpp; - refType = 4; - sourceTree = ""; + 4C537F24064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43847063E475800969041 /* net */; + targetProxy = 4C537F23064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034940650B6F100525ED1 = { - fileEncoding = 4; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = COSXClipboardUTF16Converter.h; - path = lib/platform/COSXClipboardUTF16Converter.h; - refType = 4; - sourceTree = ""; + 4C537F26064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB4386D063E47F800969041 /* synergy */; + targetProxy = 4C537F25064E298800D3815C /* PBXContainerItemProxy */; }; - 4CD034950650B6F100525ED1 = { - fileRef = 4CD0348F0650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 4C537F28064E298800D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB438B0063E488600969041 /* platform */; + targetProxy = 4C537F27064E298800D3815C /* PBXContainerItemProxy */; + }; + 4C537F57064E2AA300D3815C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C537F30064E29F800D3815C /* server */; + targetProxy = 4C537F56064E2AA300D3815C /* PBXContainerItemProxy */; + }; + 4C5E86900648C32E003C637B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C5E868C0648C2ED003C637B /* common */; + targetProxy = 4C5E868F0648C32E003C637B /* PBXContainerItemProxy */; + }; + 4CB43946063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43777063E406A00969041 /* client */; + targetProxy = 4CB43945063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB43948063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB437B4063E443800969041 /* arch */; + targetProxy = 4CB43947063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB4394A063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB437D0063E44C200969041 /* base */; + targetProxy = 4CB43949063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB4394C063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43800063E45F200969041 /* mt */; + targetProxy = 4CB4394B063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB4394E063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43823063E46AB00969041 /* io */; + targetProxy = 4CB4394D063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB43950063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43847063E475800969041 /* net */; + targetProxy = 4CB4394F063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB43952063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB4386D063E47F800969041 /* synergy */; + targetProxy = 4CB43951063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CB43954063E4E1600969041 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB438B0063E488600969041 /* platform */; + targetProxy = 4CB43953063E4E1600969041 /* PBXContainerItemProxy */; + }; + 4CD033EA0650968500525ED1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CB43913063E497700969041 /* synergyc */; + targetProxy = 4CD033E90650968500525ED1 /* PBXContainerItemProxy */; + }; + 4CD033EC0650968500525ED1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4C537F0C064E286700D3815C /* synergys */; + targetProxy = 4CD033EB0650968500525ED1 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 68FBBAE9089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = lib/common; + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = arch; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CD034960650B6F100525ED1 = { - fileRef = 4CD034900650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 68FBBAEA089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = lib/common; + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = arch; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CD034970650B6F100525ED1 = { - fileRef = 4CD034910650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 68FBBAED089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = common; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CD034980650B6F100525ED1 = { - fileRef = 4CD034920650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 68FBBAEE089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = common; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CD034990650B6F100525ED1 = { - fileRef = 4CD034930650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 68FBBAF1089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = base; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CD0349A0650B6F100525ED1 = { - fileRef = 4CD034940650B6F100525ED1; - isa = PBXBuildFile; - settings = { + 68FBBAF2089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = base; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CDF3892063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libarch.a; - path = build/libarch.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3893063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libbase.a; - path = build/libbase.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3894063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libclient.a; - path = build/libclient.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3895063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libio.a; - path = build/libio.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3896063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libmt.a; - path = build/libmt.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3897063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libnet.a; - path = build/libnet.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3898063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libplatform.a; - path = build/libplatform.a; - refType = 4; - sourceTree = ""; - }; - 4CDF3899063E561B007B20A1 = { - isa = PBXFileReference; - lastKnownFileType = archive.ar; - name = libsynergy.a; - path = build/libsynergy.a; - refType = 4; - sourceTree = ""; - }; - 4CDF389A063E561B007B20A1 = { - fileRef = 4CDF3892063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAF5089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = mt; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CDF389B063E561B007B20A1 = { - fileRef = 4CDF3893063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAF6089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = mt; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CDF389C063E561B007B20A1 = { - fileRef = 4CDF3894063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAF9089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = io; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CDF389D063E561B007B20A1 = { - fileRef = 4CDF3895063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAFA089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = io; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CDF389E063E561B007B20A1 = { - fileRef = 4CDF3896063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAFD089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = net; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CDF389F063E561B007B20A1 = { - fileRef = 4CDF3897063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBAFE089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = net; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 4CDF38A0063E561B007B20A1 = { - fileRef = 4CDF3898063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBB01089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = synergy; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 4CDF38A1063E561B007B20A1 = { - fileRef = 4CDF3899063E561B007B20A1; - isa = PBXBuildFile; - settings = { + 68FBBB02089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = synergy; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; -//4C0 -//4C1 -//4C2 -//4C3 -//4C4 -//680 -//681 -//682 -//683 -//684 - 68871676073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchSystemUnix.cpp; - path = lib/arch/CArchSystemUnix.cpp; - refType = 4; - sourceTree = ""; - }; - 68871677073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchSystemUnix.h; - path = lib/arch/CArchSystemUnix.h; - refType = 4; - sourceTree = ""; - }; - 68871678073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchConsole.h; - path = lib/arch/IArchConsole.h; - refType = 4; - sourceTree = ""; - }; - 68871679073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchDaemon.h; - path = lib/arch/IArchDaemon.h; - refType = 4; - sourceTree = ""; - }; - 6887167A073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchFile.h; - path = lib/arch/IArchFile.h; - refType = 4; - sourceTree = ""; - }; - 6887167B073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchLog.h; - path = lib/arch/IArchLog.h; - refType = 4; - sourceTree = ""; - }; - 6887167C073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchMultithread.h; - path = lib/arch/IArchMultithread.h; - refType = 4; - sourceTree = ""; - }; - 6887167D073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchNetwork.h; - path = lib/arch/IArchNetwork.h; - refType = 4; - sourceTree = ""; - }; - 6887167E073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchSleep.h; - path = lib/arch/IArchSleep.h; - refType = 4; - sourceTree = ""; - }; - 6887167F073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchString.h; - path = lib/arch/IArchString.h; - refType = 4; - sourceTree = ""; - }; - 68871680073EBF6F00C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchSystem.h; - path = lib/arch/IArchSystem.h; - refType = 4; - sourceTree = ""; - }; - 68871681073EBF7000C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchTaskBar.h; - path = lib/arch/IArchTaskBar.h; - refType = 4; - sourceTree = ""; - }; - 68871682073EBF7000C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchTaskBarReceiver.h; - path = lib/arch/IArchTaskBarReceiver.h; - refType = 4; - sourceTree = ""; - }; - 68871683073EBF7000C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = IArchTime.h; - path = lib/arch/IArchTime.h; - refType = 4; - sourceTree = ""; - }; - 68871684073EBF7000C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = XArch.h; - path = lib/arch/XArch.h; - refType = 4; - sourceTree = ""; - }; - 68871685073EBF7000C5ABE7 = { - fileRef = 68871676073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB05089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/synergy, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = platform; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 68871686073EBF7000C5ABE7 = { - fileRef = 68871677073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB06089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/synergy, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = platform; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 68871687073EBF7000C5ABE7 = { - fileRef = 68871678073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB09089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = client; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 68871688073EBF7000C5ABE7 = { - fileRef = 68871679073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB0A089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = client; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 68871689073EBF7000C5ABE7 = { - fileRef = 6887167A073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB0D089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = synergyc; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 6887168A073EBF7000C5ABE7 = { - fileRef = 6887167B073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB0E089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = synergyc; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 6887168B073EBF7000C5ABE7 = { - fileRef = 6887167C073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB11089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = server; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 6887168C073EBF7000C5ABE7 = { - fileRef = 6887167D073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB12089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + ); + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = server; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 6887168D073EBF7000C5ABE7 = { - fileRef = 6887167E073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB15089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = synergys; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 6887168E073EBF7000C5ABE7 = { - fileRef = 6887167F073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB16089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + lib/common, + lib/arch, + lib/base, + lib/mt, + lib/io, + lib/net, + lib/synergy, + lib/platform, + lib/client, + ); + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = synergys; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 6887168F073EBF7000C5ABE7 = { - fileRef = 68871680073EBF6F00C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB19089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + PRODUCT_NAME = ALL; + SYMROOT = gen/debug; + WARNING_CFLAGS = "-Wall"; + ZERO_LINK = NO; }; + name = Development; }; - 68871690073EBF7000C5ABE7 = { - fileRef = 68871681073EBF7000C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB1A089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + MACOSX_DEPLOYMENT_TARGET = 10.2; + PRODUCT_NAME = ALL; + SYMROOT = gen/build; + ZERO_LINK = NO; }; + name = Deployment; }; - 68871691073EBF7000C5ABE7 = { - fileRef = 68871682073EBF7000C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB1D089F06C20016DF44 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + GCC_VERSION_i386 = 4.0; + GCC_VERSION_ppc = 3.3; + MACOSX_DEPLOYMENT_TARGET = 10.2; + MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; + MACOSX_DEPLOYMENT_TARGET_ppc = 10.2; + SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; + SDKROOT_i386 = /Developer/SDKs/MacOSX10.4u.sdk; + SDKROOT_ppc = /Developer/SDKs/MacOSX10.2.8.sdk; }; + name = Development; }; - 68871692073EBF7000C5ABE7 = { - fileRef = 68871683073EBF7000C5ABE7; - isa = PBXBuildFile; - settings = { + 68FBBB1E089F06C20016DF44 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + GCC_VERSION_i386 = 4.0; + GCC_VERSION_ppc = 3.3; + MACOSX_DEPLOYMENT_TARGET = 10.2; + MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; + MACOSX_DEPLOYMENT_TARGET_ppc = 10.2; + SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; + SDKROOT_i386 = /Developer/SDKs/MacOSX10.4u.sdk; + SDKROOT_ppc = /Developer/SDKs/MacOSX10.2.8.sdk; }; + name = Deployment; }; - 68871693073EBF7000C5ABE7 = { - fileRef = 68871684073EBF7000C5ABE7; - isa = PBXBuildFile; - settings = { - }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 68FBBAE8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "arch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAE9089F06C20016DF44 /* Development */, + 68FBBAEA089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68871699073EC02100C5ABE7 = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = MacOSXPrecomp.h; - path = lib/common/MacOSXPrecomp.h; - refType = 4; - sourceTree = ""; + 68FBBAEC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "common" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAED089F06C20016DF44 /* Development */, + 68FBBAEE089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 6887169B073EC03800C5ABE7 = { - fileRef = 68871699073EC02100C5ABE7; - isa = PBXBuildFile; - settings = { - }; + 68FBBAF0089F06C20016DF44 /* Build configuration list for PBXNativeTarget "base" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAF1089F06C20016DF44 /* Development */, + 68FBBAF2089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5A073EC484004B2F9B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchDaemonNone.cpp; - path = lib/arch/CArchDaemonNone.cpp; - refType = 4; - sourceTree = ""; + 68FBBAF4089F06C20016DF44 /* Build configuration list for PBXNativeTarget "mt" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAF5089F06C20016DF44 /* Development */, + 68FBBAF6089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5B073EC484004B2F9B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.cpp.cpp; - name = CArchTaskBarXWindows.cpp; - path = lib/arch/CArchTaskBarXWindows.cpp; - refType = 4; - sourceTree = ""; + 68FBBAF8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "io" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAF9089F06C20016DF44 /* Development */, + 68FBBAFA089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5C073EC484004B2F9B = { - fileEncoding = 30; - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.h; - name = CArchTaskBarXWindows.h; - path = lib/arch/CArchTaskBarXWindows.h; - refType = 4; - sourceTree = ""; + 68FBBAFC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "net" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBAFD089F06C20016DF44 /* Development */, + 68FBBAFE089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5D073EC484004B2F9B = { - fileRef = 68968F5A073EC484004B2F9B; - isa = PBXBuildFile; - settings = { - }; + 68FBBB00089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB01089F06C20016DF44 /* Development */, + 68FBBB02089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5E073EC484004B2F9B = { - fileRef = 68968F5B073EC484004B2F9B; - isa = PBXBuildFile; - settings = { - }; + 68FBBB04089F06C20016DF44 /* Build configuration list for PBXNativeTarget "platform" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB05089F06C20016DF44 /* Development */, + 68FBBB06089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; - 68968F5F073EC484004B2F9B = { - fileRef = 68968F5C073EC484004B2F9B; - isa = PBXBuildFile; - settings = { - }; + 68FBBB08089F06C20016DF44 /* Build configuration list for PBXNativeTarget "client" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB09089F06C20016DF44 /* Development */, + 68FBBB0A089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; }; + 68FBBB0C089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergyc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB0D089F06C20016DF44 /* Development */, + 68FBBB0E089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 68FBBB10089F06C20016DF44 /* Build configuration list for PBXNativeTarget "server" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB11089F06C20016DF44 /* Development */, + 68FBBB12089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 68FBBB14089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergys" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB15089F06C20016DF44 /* Development */, + 68FBBB16089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 68FBBB18089F06C20016DF44 /* Build configuration list for PBXAggregateTarget "ALL" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB19089F06C20016DF44 /* Development */, + 68FBBB1A089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 68FBBB1C089F06C20016DF44 /* Build configuration list for PBXProject "synergy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 68FBBB1D089F06C20016DF44 /* Development */, + 68FBBB1E089F06C20016DF44 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; +/* End XCConfigurationList section */ }; - rootObject = 08FB7793FE84155DC02AAC07; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; } From 34cf5bfd928a2cf4e5630e6ed53eb8f279d47a7b Mon Sep 17 00:00:00 2001 From: crs23 Date: Fri, 22 Jun 2007 03:38:08 +0000 Subject: [PATCH 786/807] Added missing docs images. --- doc/images/logo.gif | Bin 0 -> 1827 bytes doc/images/warp.gif | Bin 0 -> 68841 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/images/logo.gif create mode 100644 doc/images/warp.gif diff --git a/doc/images/logo.gif b/doc/images/logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..d9750ba3fe8ecb33b94feaa5233c4c3e3030913a GIT binary patch literal 1827 zcmbu7X*-*V0)}6a(}u=QJC?Rdlo?CW6l3d|P?u#|U9K~OlB&=srkz?^T2(@j#8CTA z&>~_h5gNu4sU;bNs-mIR+CnW6rMCCXoWF3M5BJCW%l+6p+L@W-#sDW^0RWgxCWS&7 z9v=3|0qv3+;+j!fK>=d1SSS=mL`0;grw0cI`}z4%snquN_Nl2U9*;+-(+>_0@p-vv zZ@|MN@aX8Eg6|z(+k1F))HF)i-515T=q9!q{#F21^ng({)p2#Tv`$dGyZ^8d9A2#R zDq^>MjCD$5rMA9Dhds&TjGhmDoYQ67HabtKXWYzeS`vNOKR6uu{JBj)dQJ!J$Z@>M z<(!TYMt^@~1vs+E6z_shs(GTFopmu-#RV5Px6|8(1p4tPDC)jslJV|zP;1^O1`?;q~$?jP{RLDtCg$hyAw zbF=&@a6-5_y9zc8@OuPc^U%=Z=KG-;@ZfN7Zy)TP0H;^LoxME|4-XIof&XvFZ_xh< z^#9>MO@QMd!9SW96B|b&Q{w+fNPLl$oRXTBo{^cAos*lFUqCG^Dt`H@q_m7yPOqq} zs;+rmTgRwpHZ;CrH8ua)(%RO}?%=%bjK<${_wDNKQgKy2CnO$ldK5g!*es#TPQE2}d~>zn(u5h8e5b57ZWzzw%v4N? z=)IdskXKSWxv<+|+$*he2^Nez2_XOlq#?dC09yi5*65}Do#j`krUrDV?8~f_HPJW# zocqg32?LDXy3T#k%7zgIOlu4C>?^J{(UPIwj|Y2k#Fq*`9N8^Hi)!&KDf#b19$P<@ zP>Xjy&>}>o5qirt?}s$J4USSIV-vIqF=>$D^7a}Jd~xiG(u?DX@|Og*$F5~cApU)$ z$%yc&DuHFdMmlCHyP*c!aLoY(oBl;zgcd+}3d=o(Wci;V5X33+W=mmQ|7U+z8ectY z>HZ-88cm^_Eg7SMU`N5EG1C&UbZ1Z89fhw2YJd^O6}q7UV?#1^2!+Vl)=+hLxAMf1^f`E!u3 zStgjzd4VR54mi&tJSsiwflk)#;O<`+3j*hPT!psfU$F|s%DLr9q#x9DkW@~odXc=W z2%rakGhfQnvWeG?C@w?N)#^r?$p6y9*1Aggbr7OXSVjlwR`}^}6eDWXbh)*7SsQ&S z5sGnzgq)tnRP{hCkdT2!a#c*(wJQ`AFL+>=P9j7IASL*vx3U-(r`l_^FGJ?q zEYYkfvc>NlrpP^WChH+~UC~s}P#*EKFo|deKh+s-Ay@~oQ0X8e%2fP4hw4*zOpD9> z*!CilBF(xkfn$bY;Ju_3C2$elWB0ilN~xllA^rzU^l@kSXtRTMR$ymLqCE_XlttGM z_>XD$UT`)^M&r%~S&k+z^MzRCrCt-#L3qdqnuj3 zN_s`oqX-x;$nk&T!|VS& vY%6aXPBZIqZZfae3Am*+2Bx!oh~$h+L} zs2L8xojGG|XJ-fC9R=4P66gPA>E(xBJlEP90D!mkRp7YXr7z#|`vQ`7dqqVns_AfC|J2l!wzjsWrly-V4z3L!A0HPI2K85#DDTmP_j8AahHmFBb8!G= zWo0h+y%M@eNl8goN%ryU$HVVgr5zJoE>2ondSiW^bwD+z;&H923>?SRk(P!)Ahast z=MQr@oSU#G5$6V&dq3-t*67CNmggtI0sYqII%K%qU`65<`Q82fT)%D7q{6!Xe!v|} zd4bdU`w1CbF4xViKRrDihr{*b0D8KIhKBmrzbutJ+&z)*3DnHl0 z`CN#1c6ROu03011dtVJtyjxiK_5;`7U(h(je)C>Tir)=~1N8F_!qxW4=W+oTuAQ;A zw#K=^Q?OWC%AIfDzUAbnZBKb&Y;64L)2E^SXWL=hc!ynHUM7*!bd7bFmX-iG?)&BU z`}gn1^>bgpf8EH&$8~1a$%;02-SY*?dd@KrY}ANYRo`eG(0nR&(j3 zXSc^mDDSvqR+;lOQTBlMcxPp``jo4>)Mi6rREgxiSF9cmldx1SGU{Lm9c4_ zFG}#Tb>(Bl*WYz#e!o>d(HtPuGVvWxd(#?WGPB**QT=NM-jeelbgq`x#wq$L_$qq8z0_!!(gZjV7R5V&LbGu$Tz1SL4U;B`Kq~b7MTR z4q{buMd9~svxV;}y*;!GWkDt}cAvgrtfr6ku<>Oc#jMuRM@R_Q|)xJ9My`*mZ-gnK070oXL0v_p)EUOzRmY6*D4A4E|l;6l)m4|cACnk`SY>uR7i)~DgeXv+#kDRJ~@}!Z}(-9 z@YwL7Wke|~&ZW2WT*yaMm+C(BtBRgUi-%Ijx-*KyB)MFZc3J5ug!z!`zTNjO93L1= zWIsQ(aI?&O3|#;G*5&*y+8-T`o={nvn?$B_<|-9;czfm=O~&pNJBNCKoA59F6`NFE zSJSivJ>q}<68;?7C(Jk8I%8OIxXV^DAfjk*{>Hq+*XJ9kotBV=(vj;n3xoCVY2O{3 zA0ueX?FlEcRX5AN>!wn*&Fe9TdiDhkLCaIuCWU z%p6^o+DVTpbI)f8UlJF6h*A9Y*<%Y$!cF(c1=7J^Mi9P&2N4c%Nl?a-I2)=`BtTiN zYlo~6@cO=R8D{Uf9JeD^&3eVKfJl+!z&5677f@Ma@(|Z=I%${uYP_X8|B)F$yDV5p zQ(OOB%G+9#>t9%V&qe|yY)B$XE^Ue94W_ak0U-bQ$|Ti!GwDp(w3;bE^tD-q$r8_} z>ZkT3n#Vs;Jj3iT;4Gwo(uH({yHJO!Dz*GHPRi$)TU3lx?se#H*MQM;t({YKaRyVr zTL`EKP^{*dZcE|_+HHN1#{YS0d*WL`<@j4$ z&2<=RN`T(OHIXNuHN_;jVIQ+%yh|-=!;qCAUAHeGKk^NmiW_&i&cDXw4utOEWOge)JN}KF6WVq<>jty$ z(A=CvaFB{MqBB=zU>&V>Mn-{yG&V;|2qnw&5q1jAfT1W>8lna%wx!3R2?kbHVL&<# zwkZmFdhgRn zW$`_?j1?phB72#qi!_zy`;(d^OUtV8w}E>RKH&Z>FD+9qW)@2(ub-vUOv}u6mwMCo z+ek;Y%i1qJ&>q(tslWcEu-emvf8P5%j&t@>(1iMBGk@Ex(IU~ujI!$o^b^VbNqK1k@5uWW9Xv48&Zt_s&!SxPV=>7(>#E1I)vz=a%r?!sD ze{1Npqxs+Tc|QItT1HSwXv=Q?an3IHhOOB>>ZVl_m^;G?6F;?XUrW6Jxf2aLNoOc3 zMF~cp8Ez7OqIKh0X=eHl0gyK=US75G(vM#zqD!G}x1Z;t=B1KloM)4T5w64s1oIYT z_Dz4!w^8q{Enqis9}HJ00(Mibt%6S&4^3AWsoc9Ep_z2zKYpe0>kXJ zjbQT22}BD;MNWWYWW0;gP^F1{v*`^rnqigxS8Y*Se2f*O7?rpbs7{n@08EzdAMOaf zviqb?{fDO!@hLxC)FF9bal}WAUD*R|iD<5ryqI6=>9=pOFXeW#zuY><(0C^&O+sq1 zvBEV`y6?y;_YYg;DPNSaK63b3I)fooV2Dz_J<|TY?}^Ol1&O`q zw=wfbfZV6^vuTXoZM!QEo^=;JXX}%rFECeeYX|ERQG0J|{NdF&adQv^@V&B}@Hw;a z)O#!SW;X1~*AU{8sF#UC_KK)OmD|N7za#o8{joKcE;S2T4MS_JjdSd zsc(7_>gQkBJpQreTVuAJ!o3dG#C&9Z<{WxCa%^n;)1_}Ma~E$08Vd^nJmMJ(7e9EP z9%X}1uC-|%yBX@H|CT}4X_wPlFu3rD^O~EruVb}uF*V9y>f+Xf)+_sOgIW!a?CQAP zce(1*>TAU#y2H1xg^5w9kfX+?N6>>QJ4kuzM`vs=qhIWVekqE(KA-2*^lC2Z%gC9~ zh1R*IyDeh(p2deQe$>?W>~GmoRr+|&cbmiLn`56FwwW*7xtTNpivITKg5PqRfAgDm zp@6-huoWToADLn)0eE=W>g}5bxjR}3EjGUhxC(=J`?%kFKOsJ5oP6|tJ7|NYANDz~ zgEluE6;$IG_66Y;Iv)r5UfucYDy8Bv^kzlN^M$bckCq?Lhaa!!eA4*FJXy3_iTW`m zes}$ZSIeh%aOkvE_{RP6u+O0>Klgg?+*`36#m z^?`lEgL5MftQ%t9dEA%H4!oioySDBAH66oCqzCQ+lGk-YJc}ZH&r9|?NYaL5o9gf1 zYfuc(2`)Somzy0c;~yu3jq^~A4{?mw{}|WCwFr(Xl1iKf~i1D{|Q22oOPf;k( zA{5abHMTbn+Zp3A97j+|n0}AC3QQ>OMD=zg*vuvnKZ}>lhF8r-*2pH%f#E@p!Od$4 zG}q8ZQp6L-#Cl+4D==7dk3ww&s&8ale!P?k6=K2=_N79GiqNkD(631731D&)LwG_b zdCW3-2$(#(h934ro2;N`8qi}S$;*!Dam!>jDft61W!*EyL<0H^m^@8No^(VPttEw7 zN-n9Ud<{rh)=4(O2+$<3jzz&nNUSUwE8mD!WMg+UVu5n0`fsti_NjXUQ&q{Sdr$&< z0x`S15@q~TWvQvV=fIONtS=Xa?P&j!pW|RZzl_gvT!HX zaYie+Q@VIjPU0t_La zIpLWIf~TCAyPRO4oS?2fAS@8l${|LSk~Ws{F~DPEfc-(w5TFoYBtLJblk4GbZFRyv6#GGj>0MG?1w(zkWtoEk+> zt|y5ip<^7;KMm%{ApQnRXnIw0EQu`bzIVb{>Bdo{~_Co;tFzF;ldbym)3s9z= zAw{{1SkJW=#Ojj5=ZLkA05cAe5r|LfRE+W>2^nU{G^Q2QXROU(uY)p+8VLnnk|cU& zC`Z_H9r~t`D2Jk)^b$A|XaxiChtv6tSXn7nlGCf=3Uofnc^tEt{8cV!T`oty@u8=Rh6Fn@_+$n}{8=K79ATs@v8Ic6b4XFHS*svYFCV|c3ibE~b#XmwA3RCknjZ!u zMX|D0W~1>kt5n|&oNW`CzBfP9D=kD`?6h9G-yDJD%0DJ2lEmPvC?-@F<3hRKX(3+Z zFga4B6-kJdYh_KsT4g`6l8n|Z{)8gyq6!U)3$L(?!mM*VymHE{giTSnGBAZ?NIlqH zG|4IqA>&Vb6<;64rH?A^n8bN>my+hPF9(L_IhWL?mxU(~o*?);8A2pO;oYwMd+hm> z5P~$8NU#LuN)Tbpu(WQJh?*C_SK7Ae~;8TtXF<5QL5b#20YAk$FyCG*FkM*aA*GxNv88 z;J1VdaB#_)Ac7w!ohvU=!4}!QQZXYzL9)uFQ|Xgx#VZ@MR_+Rfm`6(W#vAAts$i-K z^pm{P7w?Ll-?8fnB~A8K{;py)L*YV1ZJ|W%43)nUBwVdlji%EZM(K9N#M;hm zq#BO3MjeQrsUWhKQFu|;x?9#Lu3;gKQgGK9k5(`F25;PM)hHxSnc?IIM}Pz`&}T5< zTV7%lLDbM@`b;2XUX2`OpCuHLC*Q5u2jH*gMkHWR)p5ZzSVSJdn?!_Tzp(3;_C9JK ztF0XcZj1`L!h|Z}#9LOGKWsAZcGc&7ujMZ*eQP6W=hfWO9WNfl=rO8nQ>$P=a#otL z%wU2@FkygNd(R7;+gMA3wL&PQQ3&aQ967GB0q@;187#>~kbvVfuTf~MS8-xj-N;=b zos4$9N9_jV?Z2*Boz73-Cd?)lOh)!VJ) zvLn~qw)wOKE#?~cv?SY{v&`sB;6Cb18t+UtVEScXks2L9s}9>TYKvT1bC8t+L%?+r zcg(pEl+>K-VkO_5-3qyq@~FFPyqor;6PtwfU)ahiDU*%pn6au(b54}hdzc|lGKk0v zf_5^Y{C)JSh0gofKc|LiT0u>07xf|HMq}0F;J@3crw%tqVJ<)P4#kNDe8q7W;)-C|_$i*RFG&6=Su+5%%`{=N)?XZ#W zFh5dYCyaTO0d1KOmf?)G9UTt~6*?x`2D(9vy)jZ&0=+K*_B9laTVbJ)!Whlg`X*6_ zK;F|tfpX;qownE6c?o1m)Qxc{?lb zF{HnE*smJkb%+y#5xlt~ZY#sQr3rv)0c>H=I!(ZFTaoiH!1G;z<`uppjImyfPz(%` z00ZAN1UlKj-JK=)RCJ`Sq|d`}M3Mu}=G`s{J`6{IMjt`BifkJfROn-7MdEHol9z(p z^U>{Gdi(tb#r+ILy^v9K#%Srq(O>254;g@mbbvYs(t-rF7y_>%Ip@Qsj%SGK83Kx6 z!{WaFnJ6rKm)vg zXei>if9Xkbd(a0k+3akfhntjp_1~U+Sl!lwCQLB^ieX0Nat5~ z1*wD!v01`X42~XRRr;amEf%=9zrBBlsPDkn7stTc5Fd|*EZIG98KVKKA^>q8KE6Aa z?!jRl5d+mCfpHv`c_S1B11TCbQC_F{*b8x21bvWL1FEn$L%_XBcu;BGhy(g@NSG9k z)uFCG+`sN)59GS?2?KD(VV&l8-{f!ayS6a(t!3Uae7>C4&iCfcQ3Da@Fi!AL?+R-;@cO@WMhG0u1b`;4k1du3=5`?=_Pivg4oEMyQ=#Val z3!Ic?PJ-gv9rFxUWe>34Szsn_H9SA`{FN0BewOCWem-qO(!O}HaevyLJ{oO;y^=s^&i zbsZbhWUC`iT*|*d{4i2{m2}M?-_d!QK{*URrIN76l8M@~@7$tVhFgcxuSV}vja8YZ z;1aCT3uPJn4qE59)4h)5>F&v2LXkgo6x~w1hXhyiZg99H{=FJd)1n(+<;UuW0`yj+ z>i3FP`yBW&A^Fu1t76xkcWGkdE>7nrfzp)r{Ge&lYhFosvONwz<-Knpl5QcjNgm>+Zhu_2X zD$4}uMjh#r=V0q#Lx#I7#d6g_`Wn?QKdLNwzWva%+`X=4kU!Mi_UPh0hl)q5q@a`f z>jgQ>YTp~4iuglk}mp;&&%TzvDe1QFAz_2;%~XH2&$Gudr;+ar(LPRA-#YoF=Xh<20f zmD1LhOuBrhQr?OXcxWh2w@g6kL6oPVf{T!`YW#^<4JW6QP}bdRDpx^oukA3Xl^XR5 z&Z-*~Ns-#PD3G|vXMC;u!GiRi?K0^XZN?wi;C$~naNVVY_;%N@_^-%1@70s_JSgv{ zG+rvC3{)T4ij$hDxzga{YgZNTaKSwFz#`p5%IfnB18q{uZ0wR1%KRrBJ}ab&scHA>~{jf{OD&4poxS~tOTj@S5W=7F&$J<4?(s&S%GAiYc%m$Y}~I(XX+Ts)}>rA z%)7efV!SNe7@mNLc{uCoX@aN{j5AZojSTRVmdvY6@^N=lfaJB^$U`LWO?s#3;Mt*< zMVEw+D{prgu{hQ*iSb{|h5eKjGu*KsiyV*LK3UXuOzRf*R>;(Lb(E!T4lD7>RGtko z)!aVz6fRfg8r;swdSrJWy3{>i;r5EPbz^3Hv(;3W=;{SnNPGr*LyBDy~Ss$ z6j*zvCb|Fw$UU|*`|+q=kHcx?!I**rTs&`!j6I$44w`$UFYv^j zg0zeUcMtuE_0&7hPG^16xN7)EkHCNR=&v6A)uX?9^jDAm>d{|4`m0BO_2~bu9=#8g zRSK!u8~c(Ncr760N_^}Gi`Z>fVhg8ZH=*c>;+7{NH!S0F&r1eaN(SkK+*ymfHwzA* z1xE(NM#o=x;Aj=d1%~s`3F*nBr1&rOaegCl_Z?9YKoluHHr*0Ma7T%##wS`{LI+so z$x8BsCOg22OHYuHMbUIn^nm#MqWD6c*y4c1Dpmg)$KZ;9#L5v=^&0A`P7UTofG%MF^R@Of7eNQuOq0-0dHj|)NT{p z)T4DQOfGeoZYpdYBd(jOi%C^$Ox0kgs-Xn5P<(20m^~n@CJ&IfNUSdsJJ1lMvYuur zhuZt3{Xin zs<`Wc1UojiazxOVO$g=)yCC>oB#15ufD3@==PGuCO_;+5?4%W1rQ{42?YV@UH*ie^|an>0*y|za}{%8@VP`l z{39SP5+YS7P^ha|ASyky3!C4CJsF4>rHh^lOx$E7R{;mF3?o+of>YWh5IzG=kOu|l z>Spd{D>enD-vIHHi_ecEEWi<_p+J>fIK`il$z}^SHYz^~gqRtU1phO{i!Du+} zgB7`ljhCq;2{FiW?6d-lOn$voA97|nh*-+QB*QEoCf!(v&Voog*pyRV8R!0mNzUtd ziep-wEPh~)+yqQl(x$3zV1-5r$^c6F9Jnw-NYPL-R6-;)LdXvw5=^Ejkp&Xqz~w+v z;yPqgk}%m8>De*5dHD5g*r?)GY+9yV<}q^4xuzUnUE;mphE%DW%QK{!Ibn%zqSPo> z#~#1k8P9`-a6^7S9zL-ELmbM5xxACzNn4ciSs-e*M}96UElOT29F=)Pmv|cm+GCjI z&t(7=4JkKK6g%A_9XV3EE-4mHCOHGl!2Izb@^Y6V1yvX*pRZ??bK5gVnXf3vsyHSg z-;R}2S&XB(;;fx0Bo0+yuXqws6zNsy+oUL0Qn0N_@xEF@&zfXNLJ2m%q_n%_`drC_ zCgT4tNv@P@-GnhlX^R>uxwse=NXpzGrGWBQ5{hr6KP;J*?6J=01D6}a%jxsN!@ms) z`p1wkrFz~4MzC_3Q9u_6zf~UJt5n$HB`}7BI?ok+)k|2DC-jz_@0U;iA&>jzUF16_ zT&+v@H>8Bx^9%Th)qzB&^^`=pW2HJi#hV6=U)xDVcWok$PBpK&TQPb>>c zD7y>BAAwYDY!o_tPdiP?^rKTr^x6xho0z1dR_sQpj?yK6=%RfHvijfz_}WPOX;pG_ zOfd~kRt~DqAl3&ZR^t=$pKye)!zforMQZ12i!tDQ36Z95y4V;%nu2eaYqs~wQvmbd zu;!P%fX^~a+k(x8**99K<*d&aQhFMd8|!`7p>Xy}V!S8uKqTuOl9HU8s9J3Vbn2;2Ba~An`ss z6EL0`%8i!7dHE>r%+*Hzx<_fpZt77t9om()h~2xebvC`42S`iuH5oi`O6Z8~ z5>R1Pl59FZH34? zs0UI6g8{yx17Bf)7!C`C0e0E<2YXfD%^)SIlT*ogbA)JcLYvGk`KiZk-i~vB9n$`Y`70? zQHGYJ1b4*9HKwWqwd^`$Q0>=3hm6J*`84?%t}pg44O!X(^^trQw!?QGx8FU=ysrs3 zvchMoA>7LvyJ5)pc>8$U&*AeDBVWHa8itOW&t(_}9R%q&KCa)2A;@pAQ4^9pexf00@2lXoe_- z0UkpPvjH>ZFcGL26Tt!d{sP^J;QhmXLC^O+4FjqX5~5InDxCL`m)^#Ny;X&X+(Qh~ z?0>rvouwWoa_9u3!>)*&oYNKJY?M5oVL4y^dY&q_bUbPKn<(o9?Tx;{LJI;YSTQYd z$(TT2F#54@6$ZHd7}AUpwMA-ryl(G0A;P}`8ssbzIefWD(1zBg8wK^h=eg1NrM{?8 zVez9M68q8d_M+Rl9Ps?>m&6aH%g3mzJDiM8jFu#q$iu!%z%J6Oa&JWy3_uk&DMN76*}EJ(d@9k$f<2`}5al zIFB4Uh{40*YdYM2p%LRZ8qvXuP5@yRXhmG&KhVf1Dg(g+#qCuTcEf&T zx+NJW$sLOAnY2D{abC$RS;gu3)Nehyb9dU>hNxxnE7vJ`k48H8>W93!s=|Q`z3^Xm z;JeP-N+K>KjL6+iuCO5ruWyKad6Qss`yBK+`?Gbz7Nra)DQf$oQ_ZCc22k$R(uMkq zl!PmUr)PI42Y&Gz&9lU+66J-&_qdSgrO0?mk@fc_UGF;XNx_TEV{KFH&jdB>y6wT< zu8-Ln_kEkG|663Nc8QZTGwtvLH*f}>iWuT94Eeaeaeg<)gp6e!tO>J2d^39m)(sNu zdcJ;pcF?Rl$%*D=N8!3o;(DqVSmDizUj$oE3nKEYD-d7wI+o5{KDV#jQ9G`5R7OeA zUAfd`iCa4wl_%tzaz7*DU%b?`}(L?9uD_3@&lu^?J3V7}}R5}$!$}xY2FpeZD`?{6!DO`kH6;-)c zgKSL6OS@&QW|Gstd!eeAma$l;T=GbrF?Ma+$CicuqR&Nc74UC4Q3GllHBAHRvZKiB z(l&P_Tnl}{GOm2JBT+q?dQ+Hp48e=eTozt<=c0|I@<-P_C=E5{%Bl8)wrG>VRI9vn z38TDSPX~-wjm!qqkq)OJJ9SPdK~AW0B4J@{fBd+sezy5ZsjI3$7ow@wRz>G#r0(JL z(pyf~Pc~KQY1Km$^q1qoFM2a*Xq4)~#8Wr>GLHo=mzUP0x2Kv8-`W%Hxu)Kef9$6q zuHOiBNXDUdYu}WSnY*@ehNcU-=D9lJ&>BMa$X)1u9RgNEQbHQ5V*L`-ayKI22 zd|>t;aJ2YL;bzhSEjtsrwBK-q*o31Z-I?8+aP)Q)j<(C~e6;fVQm0k4n|{J64;Fb^ zx7Vk7;;?V_=_AXkH(wc~N9I)~R3EdEMUIxrS0@|e0#beYBMsCGMRDXMl+`Ss@D(Yidlh=?OcHx~;MAfQ^Ldmr7APh5Kg)~*! zM?|=#pZTfB4m_X(`h4qnV1{)43lbm-jNY}F)omljq-gipkbI;8BwNqpA zR6w;OX0|z|d#+8n?>)M1wW&wh?iUAN23Vgd(H1Fnzove1{Xg|+-CA+{6tVeHzWO<` zm1`<W&vArpxvHShnqTwbSW?A$znx5v_fdub;mAgr`T3*ZZ`*d3vPc zq5EW0k4|U8;hTEY4-*#rT^;>(qrcVB-|FaZb@aD7`dc0St&aXyM}MoM|I5`;h^#~S z@9HSznxiz2br$abQyo3}T^$`5jII3^OG9p!Mw?BNrDWJFI3ghSwoZ8TNSuCDoD4TU z{&r^^FFBG0qC8bm=(V^M)i_lA1#ioEqA99vOT1r0JTEZH8HpmO#*#d(iUTg@HN+JJ zB=oE%2xiC8hZ71t6O9@YY>E=Ii%|60#2vt-8qdUrh9J#*IyEFzkE&BbMFp||&y7@^QupYl z>L62Pk*O-hn4M0EGOHNh6-*^CSTQh7hf7Y|ib|0C9UJki=om_pOU3f6$dZh+mJ_sQ z<4&+~hZ(q&D4f=M>XubZlVKW@6rsn)Uvk22K_%EB2=4#tjXZz%MjpK0$dKSJC*~?A zc=va26vXR|sKl^2BCj`!;)Oo;fXGH-sMBV36a^y%7n9DeW6#flo#5E)_jpoZ8q5lN z6;AxO*a-PMHUbcDu?6pRk$W7l7P|N`Dz0V(&_NeHjKMX_2{xl}{cEXdvT1~_KyqNl zcD7>19Qf9MQKDQFs30)iACTz|5}`Ta^o_B)NQfDiP6}Tq2_X}BP!z-_g*B4UPNY6b za+@ysP-9SM;6nvA*$pI;6G)))peSOqI&xo!PI&QZEQ;N~l!(`4SvkeG%_TW^p#~kL z)*7?76+d*-<3$!9|Z>r1Nc8W zWg9p>lv&Ku3{c#P$er*e^Wmm;vRbV!*tOO7~ZXk&wCWNIG^;PxA9cz2;yj{Nqk?X|+d8z=OU#kSMVtz*w^%WUFymS-GG8?v2$?D&EzS`= z^a^?hp)mX6I4+8GWE9$D6aQ$T;K3S367=X9{E_?|_(ExIpc;|KMWu5DKe@~eg-rjs zdT&TWbyERHo^hm!Z+a~5ePZ3U?+yNHfgirN5cFHe;KTuB?JX~GIjUAup|#SPgzqLs zHj<*dh(RD)G@I1BN_@W|BI^%%Z{0H6%2od2Y|bc9N`v^vie9<=%Ze~h&i2;YujBe0 zb_f~Nl`nK{8Qh-LTkWCHYksJ=$A!7ezV}c_?`p_E(Rgo~E&arQgCfJ>*6&yZwM||qzWs=}>_iHM15sX&@lCIC$Dbb^SNx{I&tN=X zaTzlCZAHe9hJ0?c`!2Qn{kEdxJS(!_w4&O~v8Eft=Ocy{LmJEHhQl=f(~9_cRunO^ z(o;Lez?CLan%Dp%iC30zJP(Q3OLPbB1Z7P0_4wn0cQ@#=PZ$z2uk=E!dK8DwyAEjm ze69D#ithf)il*AVH35L%R#dJDF!--lg!p$W!c59#2|hv(OD^VlFoyrFF+yxw5#N}g z*!Cw`+PGltPRfmTZp1%<(UC=V0(3NI`rg}p4HKJm4Ep)&_c36Y%DynHG1 z9pOWq!RvS4EsBSF=gxEHjzQ;_?FN>Q4t>mQ|MGasE)2B(_{}C1QD=1jKoNC_?K;ha zBEgn+9u!^u4Mj|?g~Pow{|QBg@9)2!4Ei-m;z1FHMPm$a21XJr8N!t%=ULRbzH%?ya zV=pJyr6<2z`S5Q3!`!j{Ps`g!uO1!M3t3(}1}*P@^CvLc`X?{~aP;g~gnkD`kF(la zcZgnE{=(!1M)s?ENsGL|sP}hZ6e#xbJq?y2@d};P8;$E_-1?S2}#Mzi`RBe(vV+0-z+Au zzLxyx9W)?#*b88|3}Lny=qO@D?Tr9v;$zrJx{i}bB?pujAt+NUy4N01YKRqb1!U5H zRoaUfF$C@cB(gm1KvGjNktcKvCX3#|x57^vJd??D9K70~NV{|G&Z|LQb@VScI)9#) zcc%E`yW^y3*S4tjifKEN+U@x=uiNiW;CJ1sm3gv1Kc1rbey!<_7p5ZImRsI%(BmnL zVpWmnDA`8d&8Npq-d*|yoRn<$iguQAs95&8lgqUBso@U=b_p`5Q(lA1ml=_K&0q@> z5))2K*SO@n7aQg6-uW45mLI3S|2|=&vOU1RA>0VMO>8eIC;gC-ftR{Y0RsdPaw|Xa zIQ9#-{7}}3N0=omllfOh0WY&Wn_%W>MF%a9*W-;AM9rSvX!xg{8tH!i*MF|xB<5yJ z+)MzNzntBY=ls4Xblzq@e~bEz>mkanmNphBoRKfP>rTdaiKUkVdoiz}!JNVqi%e^h!0IpgCgGO{k zL|KBag5@N0fkKy(F`|{i*UxCiAHp#Fji(L=eXvr^T~yV>7gRrQx>eRLrn*X-^&?(v zdv`)VyEjqmao&(m$uVseQL8eB%&coLnk{zBx+5l z+pMa=5ow7wo*J<~VeOu_9c4XN#jGk^tP|+&(e}xy+fD6;&Kn%O zxe@cYwxG&e%m#Y2hYZc>+NXj4JYGGZ!zX%&W!z_cP+E8k_9@W64k>iXs-ox)=ZQ1( zjP^5*`((l%x0sn=}HUU5JRBu#=IuQj5aKM%TE2h$tGzQb^7PGd{4^Ruh*}U57GB#^(`%Xl-HQ0T&O;_UT2uqQJd#dZQ6Bxo7rKfZ~aHtr?}#= zSyytR`HXYPUl|R~2;+TWH&%fhE6_R0(?JQ7k?LzIy6s~6D%cpm<|W$5Tb5SHV8fv- z(p%J(KzkE3`Lg*(RCp5~4HN#1;jqdO8CZIdeh_w8d9VLh1E);hopAPT72f!~8aw*6 zH%Cq=1RY#~k#e)S78f|R zrQg&~4+O2)f(LB1r^z0+NAc!S!Ha`IpQDS`Z-32oC+vM*`%&&Lwd=s`-Jg3mLL*NK z2f9me`H)^VpPUYF1r$JC-K5snxBonI3Z($#2<&7?vpA`O(ChZma!|*O=R__E8Dk%_ z=|(n|$HWUTuAOpGeWTyn(jE;&9>~aMCA8_SU$cw**<~*7Z@F_uIJn5x{f zfAC&`B$l~mDzotbW#OCye$}D7t<64h7j{zo@yJevDpNtXA6Mo20?dD5kP6B!ZouIY zbIG<;o|%bgYmu_h^zih1(&d=`gNSCSuSs3=Iwby$E;rS4+B z-KuFNMND!?qx|DWVW;kzn7G01P|pv^o2-1g`VygbnECG6)Z${5d$>f`NxS4W?(>uy#@1oz+7QMjzc zUo`qVRrhzQ?(bCH->JI4Q+0o*>i$mE{hg}&zdluG5iT1Uxrs*K!mf2jw#UVSEyH|N z{r~t7Z>ny0K&+om_}y9XeM`x}k=S4!26@KyM8>rpifbZ6Q&Bi8q#`mqp zAC*OU>Y&0JlnIs+jt%jlysn6adgvIOu9}cn6ki~lK*S}GRAUQO6XJmJXNnT&+Y+nd zlW>fs1BWYHEqErgrF}&xM?Nn#cUF5HtD%1nq36( zb>+>_p(oao*s{qZK=go);K&;Kr6qbMKDpl!J)w$z8<0Gw%A27Rd;&;e&!XQJp7*>`q@YPjOwYv9ZGuoXMr<7;A%~UH#Y&(sTi3;<$pZf|Rj0Fpm0iKAbYVpT zvAJsrHkMdkY@{`Zfukhd?Iq)2X$Me}vRt`MA~N7j)yWASlfxOy;STebvIB7jfvMYy zF$LdJc~f<|U6|Yv!81jfX?Gx(GZR z@^2*Ea01+2Cq%)BKE=e~b%GETx3vqG{S6o2mD)RqIju|hcN`*bF40L4+}MKG;P{?F zjD8?)jFVRN2ZvZ`rE-F$U1^1EfHo>MBfzTfM z&Vod;P+0vo%%%_Vq^6OW!&{`I5<=$)!EEAPc)}#tilk~4TpuXaM9z|Clg@xdQc-ve z&xfcPJRdr{4jq7FbLY}3Kh`*HgZ98_BX7hZf@XWtP3qh1!cU+?ufJ_*GrD~(AY z9A8iW=|$xN3M_utMdn@k3Wx#|Hm$|mYDK=RO|L@Jsp6hB-Ya6Z(g}jp<2ERWH!0&tjmBJM>zwc>6zJsl znm%u_D=XPiLA62@LIjAYI$A_@&*ZF^--0Iqf(oVr8yF>(-Zog@3;4~V2uXGr6-kHx zQxsJ==kFT>{B*(FVHu2N3Gf;M6UHIikE@a@L9y^|5Fh z`~aOiBe6M8#|3ZB(_Quw8%ck-FF13$gg7xraP7|DR3g_*Yzgt;cX1dCO0#J)?^$7(m^0|eodHvG6UYWjf#9&>bZ!sZRj~Lpec(W@voI?x@ zglGq*0yZE*icaTO+7OEE`wiL;y8JsD*|r0r*d4#E$TWn>i;XNE30f}l7Waxanp>}o zrTw&d+H)nHt5DF8kmlsle0{8VMgE!C7N&St`Nf`MJQLH?Q?II4?bn#ohlKPc7AUX? z>fV6ig(og$j};ItmowT5zqCggbjG-J^3X`n_L&&h>zSvom3Kz#cXGPZMp~8EW?f|B zRd97ovA`Ic)=2Dhb0ak?@` zx+i`1QP?0bDj>+-L`1Y?Cm|S!T;<>Tc_{9kEHJYq*kE|CI4-n4)lAT?zI6y zv-#P!*NU;#>u}0JXWA8qqnq2tx?}tu8XaY(T5l{gEu~QWgl-@)s zg2ql0W2Y$61QJ>TffS0NL+DiqMUa{R(veUE6_DO+fN1`Ag6_Sa{hW7pCg)wwn1z*< zK~_HZ_xiCoZt%z@mJ|##N9wxbk{#n!9&=DeIyJ*PTj-L#fFK=8>!o^GGfv7Ew6Ws}h+vkuibjzU*ATN=)F zYD6>~$?H7!ghTB|0{}>Y%&9s}0FY7|{+g;g(B2E#Ec!b(vW(e^jreYHneb8`iQ%?) zBO`9&E%8~gD}J}0*MYF61-o_1p^?` zhaZKuqlC%4w^L5Ug(rm{cV?;!G~aM)z&5rscIp;}4sIEe$nTxHpax*44#G5~7s{Yx z|1_ko|1>1k_p(Xs&Afvw%7g!7Zv@rqUY`>?zP|mP>e#Zw7+w08Asq}K3eFwMI8MGG+KF;{&VKO?@)Hq*!TH~P2L8&6QeX@yT~k!P_u`&07?2+ zX~cJt0Z3b=QG5Q}fq#`ozVCyi24A{x@hw1})o_Q@adM8ne+2(vk~o6pOFp>>A=k|e zo*T=!7@y)hn{}e$YyRxP_Sx^elQsY#2`oEYWGvKGdNkDhbD{3;)S<_}7V6r!0BL;Q z7W8*%G?Y)(1^|iJHJ&uxy#+{g_@c4X%zD-0ENyY=?UgULhCbCIPJ1k^pIEw>KVcX- zu~9wbIy!V@a|v`3TITUoKx#VC&?3cqNc^~KrDq@~T*pAY}l8(EIiRhhjJ{8w+p^Lm_D1^W8Hit6Ke zVf0GJ+piLfklXy<`t!LT&fLLA)Nh)%8(hBJ7~=HoLbY1!Nn0~H zlkylt-J^%Dc?YHRG>G6e+ZG4$O6nsor6a^bOscIq2+~QVMd+hK1G=*Y>MgaqkXN zojTM9m+hAf%q@kz&e|hrxNRj|t|QK9B2r(@D!sJV`a(zS=|h_>F~OERul(Yn&Y>9T zjJlPg2+Q%w&=Dy&>6XC&J2vpH!}rb8W=W;1#?kp<3YsdF`$IIBBK4M#D-UUtS}S!X z$eOvgfr=+cQ*E=g`JZ`sf{!K88n^9aUsjM{=ddH@y_iCV@u^Q((oyv|$@?67V3xCu zp;|a286N2Iz9U6^_TLLjuWic?ZWPRhS(REob}lO2du9Ge0~f{(SR03UN{3#0?D*^j z&AhH#C|C+|#daSyjDu_N$o)v(>B9S8?PE(D7flWzHdLxpTTNs3-Ez@zHA@@1*PCfE z5o3~*KUElH^&){HO`vxn!Cg9BHN9P@gq%v1&m2Zu3fhMY+~l!UYWj>c6FTpH+CnxS z8hKsOP;bDGjV|wC@aWq>XR{n!kyDFuZL~ypu4EF8B5Xn~05$XizPJ zk5f2e?=a_zYkg=PdwIHC;Nf#?x5Uo*w7=_#Lr*|8=TZ96)!*LwT#dAvD?B|l;CThP zFM3j!_b5DrM%njsW~xH(r&vQv3-1}piXEOkI`*ind9TkmpYA<-!J}qRmGeOFS;diK zu{Zo?@}?U*w{fQM?dKpps^^TSuj#{CeC%JoV>>gw=Erg}RjjLCK;M5i`wh~I+FL>I z25Ji}Oy-i0C>S4Z!rV-{Ty}1!h}7NIg<*cZn>`<1S`Q^#^SezCgq@Ev!;iRyt#LfQ zA`m6o2;$*Bn#HRu!J~SXv+y8-E^y%K!R$s}_GN$mwQT1{MtWS@z*HT-RQr>~KvOkS zPv%seqUjj7QW)+*`ewEMhAF&a0sxD5g>-vEw*(SCX(J>8LMy7Ys zJTS?#_*R=6hoWXoZ>R_nI^Ay^%~t5Rx^Fcq7C@u9&~+vn!REKnDDrF~W2(+Rz}yp< zs?&#Z|FNS#cJ#-N{@BqUJNjcsf9&Xw9sRMR|Lb=2$Y0bdkYPv1{jU!O-V6<9$zlxE zHGB+KEb?blEp4X}o2QGw{aF421^XfOgzZAHC>p;g6V4uR1x zLYaa@hs7L62#TvS1PK_b(-ULYv&Wpa#~jnaoTr z@VhX&$SjPUfWn9v>mbH=-X)eXMYkLWOwrv2me7DHIym+YFhvK$!WFO_MC^S9?nhwk zQ!wuSIu@>ud%28(;H`m<$wmynog&GPh;?@Pk2N|XbB*o>A`WP7Y%3-C*JDg7dG*$j zTWfT>M)A5j@jBpmULrD_5>5Mzixx}RL6Zd9CFg%QQUU_}989=FOmF~mWoDx_-!mNv zh6@4XNPr{Jm*WD|v3?3T?`-6Z3$hxArmPv(DI}~Kq2ute@It&39>3im+(YB?AjWpW z6NM=RJw&|0@6-q=vZ`E+c!ney2adYpE!X2!)Srusqrc!|WykUH|JtO3CAv`J1n_JR z6}UrOlI~C>r3+cW$W$-^cgF~O*NgQLKuJUfC8@clf`_0$OthVVP7;1DK~CNY9Gp>) zV`$Pceh;8Y(gdJpQ8otl=`g(B#I~X|Ilm0%I33ZzD;1z5L9%2AiuiCSt=8p?rFi;T zV3Tk>(^s7REF=yFXp%HDK9VLl(p?By;H;X#Ob$lO876r*O^fV2HI(sym(ufbz9)pLM~i;jqk63_NGCh{TEuDN2`>oNK+ zX$E9Tup%aFBZ;ppGgmw&Uom^<|A!`}fir+pRpMfKw-7k21NL5acAR3Wk3IG@gmuCt zdjXky$2jxzEVfNC{Mux0rz?6|Bi#d*#{i|SU!YV$O|h@cK@G*7Ld0_t;@fmt-Mlf3 z5Q&H}A+y}_#u&P$N`NpgXaz^bV%tOu533h8BjP*+2;34}DRjV<{0gUZ2ymTzz?G&U zsVf?#I5IZX)#$WIDb71pPPLTVm=$XSvQRH;s358E#HfbFkyT56-98d1&s=8QK1u^~ zlD+ev4sq8EVT={BNS+vd^_gWvW|^P;(l~Wd~Bx< zVXBMaOT2RoU%KYPH$hh<>V8jL-oQ?(W5uh|+O0C*Qm{{4QaeG21Bd687_!Y81ln1%J zzWTMB1!(RipL&fMs0bh?=did87{P_7lw~BxdnWOY3)T;T)ph`7h^M`%lf3Ve_7qn6 z)FrJ0j-_4@eAiViRsB-o0f#9wII?7(IkItVI{&!IE}_Yxy2(oEl@Pduv0*3Be9f`> zhH0~jWpk2r5n8d{!}x`+ef^zk$%5>9u}yv+a=qxC#4TEZqODOAO+6Cz@VTS{ zwO4|4Xr+N>>YR7>vJcPX{!9r}cB?CB(Wii3n5tG?e0rpM{d+yFv1uzf%1WTxnzjle z@W8e-iG_6}a=ODT>nTGQ%Deh-TKyXyu-kwX%g`!pG zwnq*(ixNu>+zJw*$r3uHcPVV*!`avgEMX4vp%V8vp>cb6Wm01!;7(?MJ9V|MYIRy} zw)4-BB2?>?s<+M}b^iSAPD<^~x56-TZIVLX_4j?MYL$}d0^NS5f(?&fH|I8z8()8J zZ;}~*Z3^w&som4z-dQ}-eD+_-5~vqJ2gdDSUC!$zA1+|$G)jH5Q!A^8v0AAi3fNVj zREfg0mxX<%Ceo~=Jw6uF0NSgK7ZFVn7cG~I)%GC8=Go$Y1tryZv z2lR=~)Tg6>KB@K|R0i`aRW%y70qxQQhiapmv}?13)08(FWTFND5@?`~?3 ziLdCs>@;wGzJE9Gz>3y@BQSMmN!3`UY4|k(Q+KvXoEcAsvJ!{v>l$yGz4}|9P`~u) z=q;{CHLZ_baN1@`2dn!Q+p_g<)q08*q(HDA_gBJ=`llbaJ809+jY!G@gLljQl6f=- z0HEk_7JKLrakMensVS8yQ0EzocW(iKTG%hks%scD zQa-GMDU*mV0RT#RP@abYQ1Wv{0k`6XBHszW#YkK%65$)Q-D!V8xe2S^8TW)kMw>RW z(CG2u7!T^asyVmOGAF%G|J){x74?C&j{TM8TUOPk`*03cX=To)&Zez1Nl%#-Q=;!o z((7r9_P75&lVmxWd#i^_rEcdxD<)7x1yC_rz9DtJ%6~j?a0a}c#vR4`vF{1T-JkPM z?LIzKnQg0Y>|dNuF>iV!)gvA89vAW6S86)&;xtzJ!x83{B-+o$tt&~-6L}+nD@k1+ ztbr>@#2Lnwq@Ja@*Nth5e_u(;{W*JHWzNpL4i4iwx&SF+GL&-1$^(#{W$2m@JQvGe9|bkX4GnPRrMrv>f(p3g2{Xfpimz)x;|+$0qQGX)0z}7#$x>k z1E4-zNiG8b#ZkaGkyI4?R(Er9CkQD*|we#xpEx6!`K@iPI6yaiC8 zO+JN90HCUV1JvP}sonXuvQnKxQT?&>ppOruRtKeizVBRk{C3q+iz{er@lNLhU0{uI zCdqPb>r9eH?FTIt=qA5_Mm|)|lOuS45LQ^_n&-?V-GE;ghYZ3`6Wy)ToM8gLf}?9j zw(qRTuEG<~bM_v*nv~6gh{(}LdFvbY)t`&fN61ma#TGB2Vig+_h3)sn?Xx!2an-bf ziL|nEYG_CN<|!60z5RR^YOk7QCJ`;788Qu%@boYhy=t#7i<84|7{QSs-+hjQOD7vA z1se1=1>TD^vdNlwY{FM3{AKP63R|riUlxoiXkST(15H!sv>S~CyP^w|9 z`J?LvIVvPu(Iig)Y{03h}-TTgv${?(LCJUfedX%Por$IPi*F zkt?N9_&eA3<}3TB548vkH=$c~lnmKnYM-D-x~J@>k6f~;y7o+D%FnD+SWiGYewcmR z!kIC@)KegtRKdlA?;Ururbb|dfPje*=wbXZ?ZWJy6vpOV!ZsHE-HU)g@g>{rR@9zx zxVyN^LPtZ9n@!*_NRBagXNJ{d%-t>D%Vs(Ljxl#P<%m+L+G|6XDoSS> zjCRbSq=k0#tUPymsBouO=y;oZ(|$dlc7xt4HW8=acsz#wCMbD%2^oN(UW^5#0(X+a zKdYu7Fn#isk-yrdXk%w9hb8LlHDjP%y8AvFa)`py#FhN|)V-mPQicL@JS1-bpQaqI zpIkS0?iDI-hrA!v6$$4E`9{2o;^mSM@f@#JRC4Yyrgy^+wja4A+Sq)g>BzATo7^s& z&=X$h-;&v(G zbycf?4{`A}6xH1M#a*8UBvO!PYV1lajy#<9h*D;;KVnr(GeDb}=O49o@~^Hss3FRI zj1Y|INa2u;7HQdD?d^NanV7woko8x#^x%zCl!NP+^VWy+nc0#rBU>u(mHG^1O9u;q zYzcREZR8Ps=~?rU6i} zI_-Hdjx_&X={nx{Leky1inQx$?zFZ`gu4>CM<#dN6l;g26yx$7GY%xu42&JD2|l~q z_W{9?`@I`yf#7KBSkoILYv9aLYP&8oI0A<1w$2=xdu9l6t)$)5{xE~QYku~*Fj-IK zE(`Q2=jZ1v99buMG%Wq4`iSh;r}czpEyNjP{0AM6gYo z?4In4C%mmO8TX@DSeOpQ^53J;2E%9NBL~#jv7TW`?BTkFsJ|PgbyTArnl6o2N1?YG zCSVy&3c(_!9xba7Eejw79M$N22BMAz*hiIus`X?dl%L==x@C;$9W-DgyvvM(NxA|U zeR}{BfXDbT#+8`Uh-gJxm=ZqhAmC7x7@l>+|Dr=ZuEgG%!BpF!t&GBK)bT15$wL4k z(b)f^YEorR-ofJV*$j$;&cqm3^6FOpq9`4gcwJchNriacx_F%#9LX8oRH3D8gm0#y zH1EdZvSZ=noRZ_59;zUZWjs!rkSfO2Nl7px5-2nvv0@MQ!uio>5WwW!Z3WytFfJe) zo79?w#Uy!y6JEK5vfCuIcs&=cOt1$^C|Ya+5H`UX!*>qr&|yj(?+igVJ6``cNEuNu zH7-ViTOd`)BDfK+u#S5L*87ThE+@`zhlrCEASC^Zr9fQH*#vDDYycd$ymcpOnW(!# zM2G=mhusIq2Eaf9WR`Zpw38dej=E%E-R_xS}cWO<5X>`Cd-o@!3B=?CaD z7zHuDy`<;%U?xz(nLuSpaKsaK%w);a;vUXm@6KRvD`2g>)84Jewbx@a6tMy0sckID zR%B9!cUGuk7*a7d+&Gp9$%YYP6%+~TGY}1YOa`!$=bid|BP*Fi6c$gv>wsx5!kk)9 zGuOy@2Ep7z=5V`&N7Hi7uIHeUasR%h1VaHsd6_f@QFY8_0YsGn5EY)3PG%65g=;#8 zKn`0QX^1KrKqySxhEwBcIM-U3e;^+sZL+MYa;Tj08%R;5x*Wk^N%p zs8XA2vHUOyio}%Qt51T{1gRC`#;n>Bm?UESNr&_x2B8p{IPS!t{vem5@vNNO{Qb z#V3dB8oUJG?@!sLRS)&5{f>z1?0Vts0>RI`5YZy56S42aun)mFPnR@~!nzmIxOQEv z#ReDYJJw{{LIKGJ;Ey+L5ToIzBo zEe{{Jc)GWkz*~}t1i9)=1aSFEskKcoUa9&Oy(?LSzQNDGQKMPk^h%VH#@fPfl>aO~ zC-z#c?Oa@j5AKI6iG8GzPL>ozl1f}btht5Z?u8m*<-FBR0#H2j#EKJC1Yv^QOXqFl8ya??&KzHgLaqp_vB8yi2=lvz=KF0keShur%nY>Th4?-D- zYT9dwWQpZIEk!IRG)-cI>t$E}Ws|I5M1_d%Yv)uP72Fs(ta7W@RkD{Z*~K8L@qvfh zZECk(o{7f;M8)sKi6Vl|07PZkcS;#7o6tP~!2p5OC#@{G!sKoxOrbsIlNRuaWs?}H z7Idv6j6$W1JIJU3$)W+#-hoHAn&H~LcXxI@SZD?^snT4w(mXa-Ad`x2zEU_`Jvv;Q z$Z;{@jaowNUpsrpw|4g6eGgHCuiCM}F8y34jZQVV9BL}OGRd0`&he!+mK$SVzy|VF zhx*%l-e|pX9+5l>V3oidl^;Xy%v#FLak%V9Q~8tT3Z_*#GZy#80juIj8i(+W*h-K1 z=kywejkG4l{AE=-fK^G4^1qD(PWhe6t}d~U=M`X&?I_tl2iDqO5*rbJ%(7;m)CXbe z`@5ugd5^c#yqB}Io3Z*`IZrr_biDn{+q}h9#=p7hrs*)N>Li1!y2e;7oBzpGY_I== ztK45t8P$!ydQxTR!`b|wT-5<^m1VI#rO$RwOan-!=sF#zbcQmY4bcUfA24q;!RFt- zfR3%2Ot^nQ z*iwjdrL}X}QqAm19Qkz+OCmQC#(t@bRk$8{TWUmYXQN+O{rBv@?=!X8EdbS2NFGFI zq|NI`bdoAMLV74%%0AMUBT5AkePZTit>i_erTL$8H%ylfEBETX9n!0vN#dJL@dcYM zPB51DU`v;`!YQ3$TH!3Pyk~3P41`lJn9FbL(RAIee6{Eh;`#SvjAyk ziq%Q_t&fjD)3Tf6$r_7tgz1loi{AYozAP@%rP-E$Hm!MlmR$PqRchkY;!yn08Q@Hl z^`%*J;7k*I`Fiy-aHgsB_nD?*sb&!i=9wnq%GQ}CqoozVt(tu20k@LB-PHiZQ>uL` zmloQ#;wj5dp*t5hNT1dypEOl2WhuWg+?8N(a!Jx+M)&zFFsrL4HI}OlyuV+pPD0t} z6I*m;v_)5{lN#e+E;8v#=s>f`F5dFn-^EWtc8q_$I==$Yl?;=vUPpdYjEcLt-0QnV zSIfP}nRIo+z54x$-*iPU{AE{gGG}-F58ZL_=26b~FM+QRKcDyfaI@s5!&o>4VzgdP zD-W#A^QOe{3E1Xu%C1?A>jZLfY}2n$P1X^tr7Zoaonj2re{w3t2!=J_w|14G*RIHb^6FCR@}eX2rc8Un`WNw&_8=F z*VgIT@GQxSv1J#&(&&OPy8K3LiH{;l+$KQ@t-A>H+ zi{zh;&$3E0iEzJ5GPLRIUYWcwS|yTeSM}GKrmX3Dkwc4iQ+%59uB-=Y)`}0DI6A=n zO6XRac&eX}5=v3^mYieis|y3m6G1zUeb#3KD}=Y7>YmuYP}=7lbLX_Un@z_te<~%E zc=tYlR&RsuIvWN(yc3lCE;x`I_rbk^J5&AfMrbP0(BYlj?gGQz>@3CIi+jgjL2qkm zz>pbQ39s)x<4z~IS_tcL``59 ztymxpm2L%PGsgDL)Ds}U*xsN(E~iwYDloQ3>@(=KEV*;;{DnN_%E0@W{uZf6JgY=G zA?gFH6RPj%o)6M`=IZk46WnBx{4^Dl-}jkxlHFkrAgz^F235Uow>CRA`k2b>q{7ta zY;5;)tu#jE0-e;2yc5~-pX)3i6HV&%hhvvmwx<`57Xk}=&w9>j9_f+&gl2gTv;@%xE z(41szk7)^Tu2XFc;8qGXSGrYo#R4kt=^=LoEm3UeY5C3Ioc6KJW$cb2^tI-O`<+QkeqkDE4pheLx+xw+X1D0(<5XLoPP%fb@Vq*c?Co_{TZj zW7AKfMP_;LZZu#vC-)r&fU3>J_}D#8cfNfJ%Ofg<{=5ea=JxQY?)>4g%yOpd9UByP z{)ptGB!N&?&fT!nT-u&=P12-!(D`>e?Y&_;6b74wGd!CQ3zte8o>M-H7r)FKsH66! z$RgwrEB@3}NQ_c{l&=gdx==nuh*M=;DA2P}8aJ)q;i3{svjY_z@{2I?MEL6o8}9Lt z_TzsHb3n-&Z)*n*CMDf*Jyv?z?$yd^wPvfxdkI&eG|v`NjWf6U$mLy^drzrw9zuQ7 zeE;6^QLFUD-4bEKX}3KDA`bN*;E74Qwnw@0v{nz?DHIsKTW}UJi&Tv9tiHUl>1j*@ z6{BA|MdC&3Z>tCV{7>TZGX`Y1yO*#5(KmaJB-ENCF&6K#bu~HtF;GT}+%dta14^8Hdwd%Q5&1CzJYrdyV^YJ4?vT ztY_ElLJA*-+>^iLJ$UZ`F4)!1=poL}bMQ(~MM&^q$Y5-!A6+dd02hL_3Ed}(2vtKM z?GPUFh&X+Oe+2^TY1H-(_>B&EUmlr?L!5L*WQHQs)`D`UL$U@X3$lD}dxkX}4J*w; zHXi@&QOjWs(_wYfVXvISDU2z-@OH!Sj1 zk8_=0{Pz*2LkK|uIv({uPV5<~V|LS`$d=IwYp613Zxw*2@GI}XVL)C0 znS~XeiH)m_Znr`Kmh&G%W%)~}0J(DYifyy{wY!(~dv~u=jJ=W)Q$_>5q{T#n@p~2U z^6F2p_ypenN~V$#;52)}9qB|MnaTyDHT6*HbYOdr*-SCE_x?(z6p$-kMvK|l$n~fu zmjtWqXe#YFq!JG}6_FO3#2DcFd`=$`mdz(O#Vu2&4Wr#`hK<=k9`1hcT$?%NAAXBpVq6w_g6HDiMIH&}nCk zlRE@bwy3JXB_+=}fpy(S$vZ=LJjD$3ORLgcQUR^XoZ*Ddra};Gx`^a$WJyPAI-{dP zCF>&6fR0KM=%{p(!|CE#C&V()Mj6*hKF+TA-Co)6P#LcgaWHTk4#qkp#uZ-2l1Yip zp%HN6(SN5@_;d@`Bt}ZLP6AS@(_~2zLVD>2Dbgq!t(YBykG^f2ZG$J_Ng1lfgkv+1 z<1R5-2rvX0mnF`J8;T2>jjx`~(xzmNZ15X`F}AMhqvXserLb}r$+ z%}SL$zl+NUMR-Jr9R{ISXXCurNe|Sq#j~VSWb6a4^otH4vSLx$P+ogcmgq3)HaL5o zmak}#y*w1XO3Ltn7ck-~KjPL}U*Axwy;mNMzCi?RQ%fxQ-B9|kxjwNJ3DvAq`1CVm z7Om_O&9)fnw)}jx;xgOfy?jCnW%Kz+4=fyLC~8`H&n7GPcUW z$UUs+0RrgOiiC_=KT}A4Gq`%`jN*#0{P9pWqq6;8#qoq#GO-K@t-8D+;XaTqz^_mQ z7%Y!FEH8pcQrnL?jn7C?!|1q_zH>DaMk0wm<+IcmK+7q@Qn9eFBG9djG@H`pMZ|Sg zMC23zN1rskNwGd92wI`O;&Vo8rGN}aG8D_gCqP6`IcOrmvuKMhpeC& z+m(arsUl{(@ZAR>OQ7V?7Gzl#@)8O^iWNPjCEcT??rCwpLij^(}nYPKVw{G#&NJ2-2JBr-i z7ORhX*B0{voIgfr)GTJvQpE&|lRNyWal$FG;%&FHS_d3+N2@zGM>>DHw~jfYg66i@ z_>_r)Uhu}3yFWJC4tW6-S58Ciu@l%VZq|;%q?d&)<&Om;*S1%Vw*cp%=vqB;d95sF zuM3B{kL1Q~orgN`qsQ$o=R>+QxDC|&JiJnmQj*p#DMigO$cfkE?L48JKU*!!nEJEq z+~eJeNaz%K*D15y{Y0{7&y#*TUdlf0I;AI^k3e9AC2yD|F9HO<%AEQ;9uaqAfBgeW z;m+@ICdk_DWES|&b`F78hLDt}%RIiyFCvDRQ-9LfdX0|L{Vg&-`jmhK&aB@S*3aN9 zRDWDY=e~uuqOc}26!ZkB_c{m+@f!dRM%^$Qh+b}(e2ftV4o1Pc!-0uW-Z zCa%d^pp=hp$-AqEJL>x8kWR>m&WN3@KeYO#YX)VUXoEm>MeNW}9o4971){4kCC;Im z(UH+ndwN$3zN6pXo)bX?K|o-t&adjq`4$)HR#T2cbDa-v!K*pUC`TWHU9K$LK8N+# z%(?HyBc5m5)G=roKEjy&D;#NB7>Q&;R)pzj-$F}&V(S|wWFY~_5_k_l*6xe%_x>EU z6?l8~aZ`@h+mE;YhOEns*}t)axxK*bA2427fiEeqDb<_ZmBwe-jL1?tYTgmV3<9sJ*m&{xL zom}}%m0X++*($D7N8x-Q#V)pP6<0;f;;J)gzH4zV5c>Y##T9CLyr%zZZclN}-*{Dz zTLkbbD-Uuxw+-JBom7X4fOQI-p0%}{tqPqLURkQGm3-p9j0|5s$4iSI9f6&gEAySh z!ls;=tyQ0S>sD*ko5))Sv{nKigMrqnZl2Lv6|agdwG3>vR`fzJQit`e)FQIBKRmJt zh^?F#Ad^hGg7MBgBF>O%-hX?YVm7jruhNPD{7#w;Qn2jn{w2iyi=!0nBHz0J=nBAA zR|@8w0k-m6329#e*sAw8TeX~M-MhtBg{xa^r99Shz=FLc9r(b29-}_7D8Ds>b6(^9 zJR=nzYRS8E9CGIQ&=-|4lcnco79AF2>s=bm?{=+$Qcto%4qJ{;sXksUensGu8<=nbL0K5|sK;;Z^;3|7Q7d zb{lQDezYpib%MCkQgfUkrL#J=p@sDQ z1)K)Eg9NzuVQdN^W@qP1faSku#rnYQsfO#@DpL=iJ8`v0uMzjXCB|Q$=M@Mm4G927 z|6oUpLV?jg?wvwz#C~ZzCpNG>{@x+7Ld?1CX^I6Et2E`Z`PU|@ZpCq#Kyh{0^pl^4 z5qt8W(p<+^RKV?L8Z%YBxxYsLDiq@Lw}UKoSZ7eDBK6)F*jSzZc1VW*Ms%x8kC9MP zRu9iPQy{jIAI2c9ETByxuWhvY4SI1tFp9sD--5riufKDwl#7=CISXGN6>iLqD~I9& z_5w?P*RcIwv>SPSha7NO9A~wXTxALrS6+WVHgfFt2K(!>_wl#+Oa`9OnW5Famod|M zJ9Jokd$5;Ih3OrMsTSTk5|ut4SYYYtfe@`NJ*XDft%^2N7m6*>B%!Z~MHpPgRrYDs zD~EDk3{&Rr510-bXma`52#S_HBeT8rUYLQjQPil{{iHVuU=y4*{oaFtFl&NYM%%{3 zOu6;*p=eUu*2>?tInSzXbr(Trp8_L)YZ1ZDcs}=fZvUpD>#`Ms8Jd@?4sN=O8NDuS z;4_k2Ke4$CK2UAYV6ELjR7kQkJbtuKAoSC-q_s>5F$r-tnGM>bv6vmz`fS^_?VN#% zrr)6H`yV;zFeh0-e&fGiyZ9<-AtfMx_@MNo^F}O75B=%9?#tkdj`pAxNzXujwE(_d zFu&@QA~M@E9br{&mc#Cz4YQO5Z%s_==!xFIPQ!PHGgO-nCncwdR2pA?K^EcnvICyM zxuc;i!T$-qs-r|!F*AuLo-?lSvw*%A3>1(RyAx9Nz-hMxPwRPL=MQ`$RyE8vYisAP zHRl1`NN?AVwKFH1I}qkJrfN_xL;bzhXr#@1>7CiCA7T2)7$vaCM^6gZY1N144_@5& z@>8aJ(6*TKKwZW8rA~=?mnmI(7p*bK3*agDv`a9t+%xZ2(Uxh~4rpJYSaJ3$5;o(w z342Yi?LCV}jBv`tyR_ao@d?!vp0QiPztt^6?-PpFICZ#^*6!|oHvZlClv*35{gz=2 z9??Ff-Z|6$!2EF1MB2yQ%Bymhreb6j58n92Q$#kAtQW9M)1bXJcy*Zbsht~^p{H$9 zgwmZ2fJ`d%Jt$RTQvS44z2%eOBYP@aIaCe3d!m#2(&U_6jID;O<486c2cIYy^`}`T zOnvs3nRKyuveDZ!W0Q4i&#a}tw@upKbJ+&hUC$;WDSn_o3x9v;>JMH0p{qZ1^@pzh z(A6Kh`a@U$_vxx(hg)M=kVB|n)}CM&yI|DAV5!02(r@nV$#=IK2H$fI&bI{gDnMTz zE>a(&jeX|n>5zX~F!ox=;kA%}9if`^l+fd?p{}8!QG+2T81QIJWoEn!7e=dZ_@hTyAEZD`lc4K92z;oBrPa7G`}L8BGBC>Xm+UJIA7trb9XN`h`(f*B%#S31#LjF9RTa~U6#lSvqvL3aoySO*yDrM@v$^qfjklW`FVsGfgh1b^9sv& zlgLL!yDB7=uE!Mvq}7!Kq*w0i(3N%U4xDGll)h&OdIh21?&T6iB*A518l2m zhEtMG#!9)O)udw;G*a4DGHx?dEJelG3qXq1odTp-do)P64a|LGf)^oFs4~XaHSW+l z>D_W>fdcj}FcsL9#5g=v81JzjZw$^k1jZr6@Xy5X0rog|#f)#j(Ww<&vjZ*wmELwa z#hsknEKWFzAZ07Y<&Ylc&63`Ma*9dVQ^@!;GY~^BOf>>5E}s5UJbxL2ix*Gqbs(9m zXMf({w-<|bC1iY2%+7E~eFKhDTerl1%exNFEfP;N6M)M4K^^_T*DP80!dUmzai1E8 z0995EEz7NvcS$kdpN4Bj;)3zHO|I#Y2%M{UCWk=Y{1K z)T*qzvu(t}H?>rW3LYMEXIK0bF$#fl|h}U|0M@7wi;3P~0ru81cBc?M_OoMjq#K!=btM zx$5+ooJJ4R^O;a;vQmpTq-`ysa|58OTQf zorD7U)FY4%fn%M*Mbxj3fsM1b-r$?zWW?eS!O>SFPtw8;jzw0eO$#xFy z*z@u2K)rQ=RK*VMZtvcC&1nFrw?uBGDu1PXN$4h3cf)zxWCZ#WwEE5@zVUXV8fiDk zZW8`=+bV_K{gIKRP&68!w2b0^pU>QIWOh{q!8b^R*7TSe_lS6CJ;dRG1dS-oT;lpbT>*~}{9n95&h-FPkXj3nH z<5Yfb4t%|;?9D>Lv97wa!&8dLsc1+dpj~@ZI?EHO*v|vo>pN$kyqPx}&e%G0^lIB55LvHJE+oNW)lh}=FCs_H&0J(5T z{m0B8GzkE&7Bc~w=e(HV`u63K0k78-x&U>n*;4cs^j&=K$M+%dHRyREX#Y4*EgW2{ z!+oW2npQiX^~=1*VouCxcu=>@3(zq}vjtmP@?DycqU!!vvz5y?F4Hk_uDuXW{F}YH zMOOg!qU1sR!&{y#01}UmfOW54f7|A}`gnT2vv$dw+TB)9J#e6}gzhxFpbfnI!l*A8 zQ9I4ZwF;Ngw{k7jF&b(Xd>PWulkKC92sj-o>S#)&P20Ze+NB0np2V ziCz$X0bO=JIlHcL-5_@08dHTT^BR+}7MlV(ha2U(Q{&nSD(5;v4o?`A1FkXgiUpe( z!p&lzS`2VcB%kJu@>d(sLa6r&r9Zt2hxDyqqlV$#AU{fA#69(Jbw^(GEEOoL?8`hR zhx+Gi3-j|G1vSsl+uPFn@~>+?bRT{}A0eOfNzZqx^e~v{xo|!=w9@0RoU3mu=c@KE zNV5}c#e>>eajJp?_BcL@EzGgTX6W;b3Hsx&oU7=%1S997rxaN{SUQo_b1pSR(B$7E zgfb=s{h9dmMW%SkFvW|VAznVe!3$X@G|2$36HM@0j+`~N?Jr~%hU>5v9NMp%JD_+* z*=_)`kYBZYS3s}->+GCPot#8&X~R#2`RC13b}YqR@;vooChT384Ypr^-a2Ay8FR3# z%kCa;IW2s!@tA==CE~dBqY{;hng2<=GBg#^VoC~=xBZF>m{+ZDDG1&quMX;vJ|XiL*1N{j_Z;D({#SfL*9vfqf#$yLT%p;10VQ zsi61eIUGvU)VEGg+y8{k{KZo(LGE;EsAXHWG$Yq?*(J^mdqh6 zWEuJ7<*=SEl}S_~zwPw>MhgSC2E3V4 zx2|7hF~!|~$JGQhaNNrI=oP!`>0ge?uEO>rll!?xE#p97r7tCj=0R6@;5 zBc8ZfKrJ^O%3UeauCWWAbs%XrotJv+#eaQnJ=7xhXJ3UvqGqYZy!nDxjq*FqUweY= zFVzmSsQC(dmrcWTCY%Qzwl!}L*RhwpkWU_&?0B&7n0(#vJ@jEAFqx;T!zOO|Ag96l zRMHnrqN(oQBSm2Ippc_U_Z#+^hy|Wi5vA+mJ>PS|3(1BDx$@;(Ofmxl?k z-neEJ_Px!KUagjgH%sH%gVr-Ly^Or6bycIU+wleYOViIXlV|tj1Za+;HPaN!U{rM9 zCB4ALU+1i(d1kbi3=T-}tl2V{%X+s}l*g}gRxqXDTqC`G+UrIZSNI|?1{tcEdKuef zSjUpm2IQS8bADPri!6bL?c4`khq6Y(21=5z4Ss9Wx^W6u3GLubDN@wFp^I<3cH^+Y z4E6XT1lP-c!z9Hk-?rysZ`j?m>&GXLDqIOZ8+Ak}jJsXI{64VomuWnC%yP#3ELkWi zGa*yUjK*#A{%}%e-^~;6XcmuVKRy2rJ#!N5+V#-mYSMyf`Uyc~*P}z2q;1O11{Pj= zw{INTf8*dy#=;+pvG6A{qvBR!4wO}i>aeY{s(Gudy5)Tq_a2m~IjLaoRBs)@S*FBb zu2Ap36si6!eJ59I^pH)4;yIGl6Gf{%>DIY7&PC}>y4u!k^cT6D%e}g1zkR*;`O=JY zdDpMIT^KcP|HE8=3azBjKg{)qx&AQMALjbQTz{DB4|DzhfVtjm%lZb0_h%w(?D_5OVC2Q|pY+J!kNCOl*z6T2%D$-{{u4WiK!kZ?_i z8(*Ra9|WNQ!C!^oNeKi}B1Qs=3r>u(gQ7?7ha7_zk3fKh!7>sgUcx?6GpR;1u`n^1 zoS0NXN^0hVK1D;@6_c7hq2-xUCzg@;*3q(2!>Z zo%)%S_7k1_buQ)Ij&}hEi~EAXZ#EuYAgBL%$Vw7^cr%@12iG|a*Ghvc?-W|f5Y7`2 z_7hqe>Pi{vfOoOf(tXv^dlg|vDM>WNAbG9r0YTZnWn4y(y(|$pP;pt?Bd$wwUB@6C zFbIuE#Engab0b1iE4`^bBSq8yfEKLP?%|bDq`xFW29jy2h6?(Nyn=Vg>k&X+`lujD z;Q&dlxE=C>0^~(Rr!cq43r^W4uk=QAvOODO6CEvwNh-qNHqqEdG$90oq@Z1yAa}2H zz0C~C#s~hAxJwk7Kqe^qU*si%-zG0MbUhd?4aP}pA)Fe8t~dTkUN+#Y3;J2s{8>W8 zEE^2AWHjTv7r25I5>#bRP#Be=c;SNncK@ms!rv~54`dJ)M!C8|;E5pumqoBSjhGrB zdR}9XtuNT$Tl{^Eut*T%hGj zQQ~{cp9e^-mn31P$j#7!6kn8gzX%l$kQYmYw_Pb8Fs}|K&k=3(F>1PI8saoHhZa>B zyhC24BJ@G2pu`pnb}Dpk3C3#p>{vR61l!*_Zf1N*jx_ zjkj=tjLh=YOfxVZt(M~uS~?wBlBiWY+E`ZRfWS3Xu-K4xT7~zh71;S=y{)V}{QgnJ zFn62sS%=KI;*ucA|1){Lv@g2fDD)Lt0kOyQfG{bNW!+o(NPf)y;zDj#TA8|W*$=8r zF}9jsjIPkmKDuckB3*e&t8!i&kTJLGk% zF;`ly_=bI=;Qo|KSyt%5T_uabuLf9NtWtw*ycwM$N1RKESz3 zEV?!XSL!%l?cThvr=Yw2ndnWf2iU?%C{|B7`(7AC(WDLD`TP*2xqgiA zJMbpE1wMj_S_yfzB8i@EZr{Sz@#y6GEWBbda-4LY^N+Ux=LH`(_&T&azxgBm1pW^N zY;&|1sJ^KAmhMvoi=POS#Nf0r-VSV5j|H+QGO1dHPl!*kT7}(mmZwvsZp2MhaX?K1S|f^=gMIw2D8t6Ti?2I2v= zh@D2iyGYkH%DwN~Nne14vA2%kr5YF0#iUziyus3#+Ejr8)rC?M6UxbE88uMZxHp?o zY4zaoUbnGE8(62?16q+UwdCgB`X9Y)vVFjSAbFx&cC&l>F?go5O-VV6HD58XvsZY- zzWCL+OfNV|XDoME&}GYHZ}mj4oN1qeXSwZv=k6{H}xA&AV*F2eZ12KFl+lv)O9WvycBqLpj&3La>%!vlc)Xl`LWvurnFzjX@^?}$j3Ph&ryw* z1|nrgxP=CzOi=&wuH+r>a#BS@jC;~Xt5R;T-BEs~*2IqW>)~d+a4X*SE|Onk>8^M6 zp&8KLD>lO>S;JNIVQ}jJfL+m(<7PsGdM!=$7(uk5Ef&hL$$zUPYI=6MVJPHRlt8G_51|Jf)^0@5z$X$wNdcqBqtCab@(c4D&5ae?1Y zr*6V8OC#;s4e5{S)LATa8@rSVy(AeL%5C_7C2d7@>XYM4K7d`UYBsOhx!7IoQlM?9 zvSsdIm);3B^53yb|DV{^H(y5JGWBOP!$<&jf&PhIdjafnuDu^Jy0su_zPbOK$#A$H zmF4^=cI7X=%WLVaKF)DkcfH3NMe;6rMNXk2n0m{9lGoHu=JhjTg$w8)Kq4vTI9V;xdz8ew0sE)oJ19 z;)&jbH+oAPtTvgRp5@ZV!-Xbm;K||Gw@ZcixtUwbsJ^+>oz#ot33WI1+Q<@G22!s` zX2tedt(BeBYaMJ(Jw<7^nx#eKu`G++IgVK3y9m_1)6T@jTTDD81rVL8(CL^;IdHQM)`6koswOcc3ufTd(Qo4eB?E`ELg&7wJxG zdP-{vtgV4t8~x@>{U@i!->xOEP~Wl4mI3oJ*u&iIyeiI9x5-PNsm<6V?2woB6YBNf zgudQo-PrEDw#o%X{ybZCA=W}e2_DCwBIJNVE6?uryUR)C&%FjDw$D}_x9k%|qU_ow_Q2H2n03 zxW3=xe%Y(F-@n};Qyw4tz-DOCz6L0?+{cHOZrJXbp|*FN!14PV*2i`ZeklWVKz2^Tt++kCq<$OuR5`DId)Fzip%zSa?&^+riCS(KUWd26 z>+xUSW%NR93Gl89=K=2umWa_L-+Uc+yg}4D-~vx~jtaT@voh~VyU(FzL>ZuQGa0;|LC5zdsaPc@F#cy1A>oLHEezRKh+HfsgUYpG&W5$8HwumG*=#O zdFtqhS`wvBcg-CVR}Kw`gXtg}J5rW>pHmvLVy$wO{0{Go6eb$#`jM5_cIvLU-_HRD z4mZhC0Sz2`d{@o&IaL>)sZc`%EAAcr!Qw@N0>u{9gdYXEsR?t6v7`4aG#!4?IZG@% zP4Vydjq8yKSBw+k>NS7n)+s!|6ptfli!#Vf?79ZvnSo}-YFyKP;N6MI!? zaM1#t+t=cbo@~8j*6>6ZGFKoc*j`eyV_iPy;M>+^*PqK%F7zC*F2?l+y}jZ=w?c;x z4_G|jXPWu^%5OgxoH`zk#$7Yiv0gm1S7Q9j@8;ml#TWYw9ba3Ij0Jdz83&2Tk$uf> zFkZ<#$^)heJ@YrN%ZqPy?@klmdV;XeaYp4SVGN2PJ$B02!PDZu)x^~4s3nBy_a+|K zUjdpfruV+HF`CaD%$I4blYFEYJJ()2_~|NrjrV1b7DM^N6<>$5EHj@A{{AAlL+CuM z?^5i(*)wEwy)vQ8`#zFmZ%g1Qa#$Pp$0XO(yAy$R>Bi;jr+!nnOgtq2V)XewluNv) zM^76aY?sckE1(#_x=*BgSXKbXvzipP?ytU0s)$;9BXF@rPW#jTr{mw$BfTcl6F+?2 zxSc+~K>nc+781(;!20Bz(!&R#w#>H!%fot)x*n8&EtBT-H#!In&RzvGHo| zQ%+12Jl6|%a?(NV;@->3^)E~5uFDg{Sfzxcw6qtJi_W}Y!*`MVCe8~tKQ<D8AYF%HXiIW9VI$m)B2 zBP(ui&>1GXX`a3q06CJqBf^g~50nQ$K0W#5?IJ7bRwvP96#9GVbpGi<^ziXO_CF+E z$n#isU5Z!8iuGM9T~()}JB?WzG>-YGuU%Bt6j>^rRaJnDk35@*PJA0^SZJJzr zembR1)Z_O!N`}`!xe9VrP+rz=(=^PBik^8tBlPhwM-Y7bkX2#H^+q9^=VFM}%sa{p zU~_AYi-;}!9p%kN^N?R+gdc*Q=Q}=`#~l8WHBS1dV@olQQ$7P+?mc#SPv~pwViCOb z<)oW^o4d?)#0v}_GnT2d%2#o}BYYDW5d2@}0tN&NH2Z);tJuo6Av5<o~!Hsp)%mAJW7V5Rq;(qRXz=+W@>~7R~K~S zG^HaZDAk3%NvC2!vPLx|OQcqYvLp4($KFDV+-O~++Ty{ID>&~-YHi6tg|p6WS>w9W z(Q?oJVuVCp*?5u13^nWfy|T&XVD6`r-x1|gZIPE|WliiMerX~awNlO|1H^m|F_&|BT1dCXy)6|H=vsm^nx;kRCbUvvG3iFV!< zt|%v3?mXo+As33A{QJ3*Ht z=9=}(FC!CEf810N$OG+B`<(Vq=mNn9Fg@j6)u^*z$0$#iS6oN&0zt43lZUhE9}ww*T+iw?WgjdmaDh#hKtEe%xP>i9k}IxpC)!%kx%mykH7z;{-)`b<+E+-zwSP~ z^J7|+SNP?r{<3|4#OTg5Q2)k=Yw6rw46%l?vi)BkDJ0PXmxh? zJUN(6Z1b2i*aLf=s<)Om<-y$J-_(DBbDczcG;dzfcFmUP5log2$5Yoy>88j`fDK9?L-q&66LZzqL4puNbW~2Ye9@M)2#Md~j0NMmRP2 zxk`jTtSw{aH>YP^<3~PaqOw2mYHrfkLe^4#G=J>n2t5dm_+p}R&Ot3T6itd)&$|0l z%*m;9nyR;6^T#mrZd>!j!olX2gqD?%{ofhQZX5ICt)K2feher**&OiI(@5GAjxc<( z)mss9u_9G$$fr$V;t`0o-j=$yI{f1M`vd%DG82!#6i!Lcwn4u4|HkTqv3|~1oHv~{ zkNCO5`nAFPgywKI@D%Rg5+7iZ$yBv#^V+e-wAkGtRmp&VD5BN^mTL zH@-c=>(|iz)WSHIF3~%Q@zBC}cSCW%t~jt~^w29Am4wg$&oGZUP;hV{raqz2UHnml zOo(P^w1j;u`VJx}(Timm|5z~r)*zp<5uc`+7(S91X%`G3*(S{a`-G5(!w|Ri?S?By z;!&Qbbpaq=Bq$1?uV85W2Gp@0sx%0#K||{$k_egrZY9|zBnjsy0q3*)gYWw_!002e zo())^MDiQWOvYPw;HB?RUjh-gW43RCu0yH0^CGQrWb~rfk7+r;lP+&Fcz(a%?!a3H!%n%vYZWN zSd=+2@*o(4Go#2Hqo813!Z+L5Z$=6PMhe^%;q8L(hdT>HDQHs$I;>IV7!%>$DC7eE z6TO@%Sr^)~oFG|3EIER+60SxG1@^)jYdvThC7c|U31fm_e?zYq5dKaGKG6A(2!7sP zr3{_ha4lfKkcce-V+#TIQo|I4VDh}Mgb?VyP_XDcrfM^Nu88oYE0g|#aCi*y_x&7sc$@~~a3@28JuLP65TYG7N3bn?t2nYrl!hr*7 zFJdlGdj-rFXpIS80cx)R0K*8zQVEx)^A56_2qO4tl9Or&=qa7&BMjvevFME4cYvU4NH%0`rN zSBgrS=SdHlC6AQKo@*DG2IO~Q5amRKeIp*dngf$8n-(ceA1$7?FRuq7^Bu@6GSZb< z817BZgO-3p2`0s&FezBiNXaS)@maeR_J46NI)8C^qtNNmN;nfU!op%ujb+2d1%F^L z5e_77weoWdWjr*Q8ts}PZA{}>wiu+w(Q{+=Dh9EKpFZ(KO>x@-|zlH21T#i@s2D(Cy7FH>!Z*>o%zDg-Tr+ z1i-iKd?vOm1X^p9l&gddEg_w$ZyGm-DDXGDrNLvqSNc2ibd45;lksghP>}^FkWrCs zR4wG3>xs$Q{{wNiDVH2ttPoJ|!sKT#3u3)XO|tV_BYN>h>uqDQH$SP&sQ#Wn z<@v2WUby-pTKTEPHg%I%?s)iFmTwMhQ}8(rUBJM1=rnphZXpyA5k;6h3g&?Wo5N#) z0*VZpS@=Z1Etgr?O~yomu&Mxkt^NsJ(Cw>ZBH*%@*gP&f!Lx(I0!X#yVL6c~2&~qkkZWrR|4vB}mn3meDCL`{2!HX;L#GSQB)k>E_Lz&?EFF zUs@H6CVcL-xZ~^nOMR;_xU6yS`thu9kAaWN;!h$!*q66=>f3iQ5N$ zQioWxG|_YYnpyqY-89K?c+5f%Y~%HFC_6r>8nHk@)#T!RFuM9A9!GEvc)R=pY`sjz z{*2JTYjn>LQUKtq#+Lru=Z4xR`j09BLz=H5cH=L`kpExt7yNj)TsZQr$w8gZ1--au zDu9IT9f<}cjIG~Dx!+b~NZhf%^>M$g9LKGnRAwK`7lDcL>Vg)Ax;G`)T%d9>EIE9C27 zhU}DGT=%qRC}p)7yBABT7%lTL!ialUUr@mkVddx$ z0o%#Gq$OInwN*BqO*- z9Vu%@4W=UR4iOOlVXz#4!N~9K2)-FtLmt3qjf3mf{OUax_#e{h)|Rp~b1jQ*G{lC|DC}iKm4Ql z5?Opk;Bt97&0$<|XLWin1C-6izgL7kjlo&&{0`#G>om?)-aYLw33F&%5-1yi{oGSU zmx(0HoNkw?)P0{C4*aG#uB0#x&1nB-Ftz#h6U)uJ4A#dec{+d1jJ8{Tt?e+_-nLcI zJ*!pt5C34Wv^#YDMJfNG2ZUGe{te`~Zb9W@n>?Cbh!h{Gy98;is z?ek55gfUjPB~0|26N@VGHwlwF|M{i=XPFaEAEu3!uTF2rUm|0)2;RYzw{CG2WjgOD=`6EKlJ8-`0G|1?KTj9^)dd8zu>D? z^sl?|SKw~^rS{F%;u{cu$*g<=;;$!D{bRtXE{n0aw$IbM64t))F?>yiCA=xGy!n~? zOQhN|%i_yy&nBVmi%G@uT*`9h&tl#Pz%geibqYl1+qFE~ucXc0>9e z2iM%lV1TWLR#_}l(NHA5#8XoJ*1N92fjDChm0No|jwhAqUa>BZ1+uB*T%0lVJp2Ax zQ@mDS6R0!e&h`~7LD^8AYlm(3$_;7<&CMOMrHO)1U#^tc(G4jc^D}B$N zpvxkC2wDQmZ+4=Q_%RB16}mq>{|j)`iw{bAoL`lgZacek)QbgN_0n+KzUn2HF28ft zEBhj#VXA*fe9uv_mCZNabBp|*lBr_fWUU~!`e^lReUmqrHEB@&ce%Em^8%oa?DQYFTIwL`a({4`ShzZcd$3B|c` zGj2!;$ zW#!i=w}R4WHRtNKXt^VaO0(S1qvF9@&n<{j#IuPZ!}^D(R@780trF!Q!d^yQtZlHq z=4xOwXs6>*d&tgS)${5=D1N=u(M$dww{yDqSI}Q)x`;&IlpB#$m6O?kg%QI;9ONC| z_3T=hZ~cSNg41oa%7BFhvhLng9w<{6|83dFw$1-p81W6}xZ)~+VVgb(gBVShTjq*2 zYJ7Dub{%i*hD5ClG!7y+R@#(!5Y{lAakQ)bef;ZJ3`4@8+f9`%H`{p!LI)hRlYFH( zb`fW|MnV)MLcdCSW>=q~AWR%WDSUx-0hhfBDuTBjk~)x~ikip^>gjM4$nV!3l)h{IHGD(m z9=`U5>vy-$Y5uyO@6lbwj^G>>Z;KJC?dPssZTjjLFD%ICaK`O&+Coh`AJl?t(U(1I zRJF{jLE37d$CZWn-}vjI7*-^N(w||@;FhQ}aXId;W$e&?r@k&|M)k2|hm*fVMBonY`W$aujPl-#o?pitgL*@8nNDS;8%16F43Y&lUdSCh#b=fCMzln5#;~d~GTx0% zIztmFr4_P2}iL+6ragCQLAWb9@_7I@Dx$UM$lb!%ZkWj156QmTS6ROcK&W% zWbEfDjNh^*U9l;)%J;S@3$+1yu42tTpyw*KvDHKry0+4HPITdI$~ Date: Fri, 22 Jun 2007 19:17:24 +0000 Subject: [PATCH 787/807] Applied patch 1731039. Fixes a bug in testing if X clipboard was owned at a given time. --- lib/platform/CXWindowsClipboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index c380c9cc..91399013 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -1188,7 +1188,7 @@ CXWindowsClipboard::wasOwnedAtTime(::Time time) const // compare time to range Time duration = lost - m_timeOwned; Time when = time - m_timeOwned; - return (/*when >= 0 &&*/ when < duration); + return (/*when >= 0 &&*/ when <= duration); } Atom From da0521bcd20d62fa1df82420d91b2c2b1b855d86 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:00:52 +0000 Subject: [PATCH 788/807] Allow input scripts other than roman on OS X. --- lib/platform/COSXKeyState.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index 5ae7f5d9..ec6c68c9 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -98,6 +98,9 @@ static const CKeyEntry s_controlKeys[] = { COSXKeyState::COSXKeyState() : m_deadKeyState(0) { + // enable input in scripts other that roman + KeyScript(smKeyEnableKybds); + // build virtual key map for (size_t i = 0; i < sizeof(s_controlKeys) / sizeof(s_controlKeys[0]); ++i) { From dacca8f96bab9e1d744598483b2ea52042cfb4c2 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:00:58 +0000 Subject: [PATCH 789/807] Updated change log. --- ChangeLog | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ChangeLog b/ChangeLog index 10ccab7b..ea4c7950 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2007/08/22 21:42:09 crs +lib/platform/COSXKeyState.cpp + +Allow input scripts other than roman on OS X. + +---------- +2007/06/22 19:17:24 crs +lib/platform/CXWindowsClipboard.cpp + +Applied patch 1731039. Fixes a bug in testing if X clipboard +was owned at a given time. + +---------- 2006/04/02 12:16:23 crs lib/platform/CXWindowsKeyState.cpp From 90b1d4d8ab8392699128c4fb46b62777b9e17540 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:01:04 +0000 Subject: [PATCH 790/807] Fixes to p4tolog. --- nodist/p4tolog | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/nodist/p4tolog b/nodist/p4tolog index 561a25a3..43d0fe71 100755 --- a/nodist/p4tolog +++ b/nodist/p4tolog @@ -6,14 +6,15 @@ # changes are written to stdout # location of perforce client -P4=/home/perforce/bin/p4 +#P4=/home/perforce/bin/p4 +P4=/usr/local/bin/p4 -prefix="" +prefix="//depot/project/synergy/" while [ -n "$1" ]; do case "$1" in -p) - # get depot filespec prefix to strip and escape slashes - prefix=`echo $2 | sed -e 's#/#\\\\/#g'` + # get depot filespec prefix to strip + prefix="$2" shift ;; *) @@ -23,6 +24,9 @@ while [ -n "$1" ]; do shift done +# escape slashes in prefix +prefix=`echo $prefix | sed -e 's#/#\\\\/#g'` + # get relevant changes changes=`$P4 changes $* | sed -e 's/Change \([0-9]*\).*/\1/'` if test -z "$changes"; then @@ -39,7 +43,7 @@ for change in $changes; do sed -e 's/^[^ ]* \([^#]*\)#.*$/\1/' | sed -e "s/^$prefix//" echo $P4 describe -s $change | \ - awk 'p==1 && !/$^/;/^$/{if (p==1) exit; else p=1}' | \ + awk 'p==1 && !/^$/;/^$/{if (p==1) exit; else p=1}' | \ sed -e 's/^.//' echo echo "----------" From 0ed17ccca29569cd858002ac144cd8014c87b0ff Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:01:11 +0000 Subject: [PATCH 791/807] Updated compiling documentation for upcoming change. --- doc/compiling.html | 68 ++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/doc/compiling.html b/doc/compiling.html index 3fe5c393..47772be6 100644 --- a/doc/compiling.html +++ b/doc/compiling.html @@ -15,7 +15,8 @@ To build synergy from the sources you'll need the following:
          • Windows
              -
            • VC++ 6.0 or up +
            • Microsoft Windows SDK for Vista; or +
            • VC++ 6.0 or up should work

          • Unix @@ -26,14 +27,14 @@ To build synergy from the sources you'll need the following:

          • Mac OS X
              -
            • XCode; or gcc 2.95 or up +
            • gcc 2.95 or up
            • Carbon development headers and libraries

          Configuring the build

          -This step is not necessary when using VC++ or XCode. +This step is not necessary on Windows.

          To configure the build for your platform use the configure script:

          @@ -52,14 +53,17 @@ so synergy can find the X11 includes and libraries.
           

          Building

          • Windows -

            - Start VC++ and open synergy.dsw. Set the - active configuration (Build > Set Active Configuration) to - All - Debug or All - - Release then build. Binaries are built into - ./debug or ./build. -

            -

          • Unix or Mac OS X without XCode +

            + Open a command prompt window (cmd.exe or command.exe). If necessary + run vcvars.bat, created when VC++ or Visual Studio was installed. (Use + search to find it.) It's necessary to run the file if you didn't have + the installer set up environment variables for you. Then enter: +

            +  nmake /nologo /f nmake.mak
            +  
            + This will build the programs into build\Release. +

            +
          • Unix or Mac OS X

            Simply enter:

            @@ -67,27 +71,24 @@ so synergy can find the X11 includes and libraries.
               
            This will build the client and server and leave them in their respective source directories. -

            -

          • Mac OS X with XCode -

            - Start XCode and open the synergy.xcode - project. Build the all project using - the Deployment flavor. -

            +

          -

          +

          Installing

          • Windows -

            - You'll need NSIS, the - Nullsoft Scriptable Install System. Build All - - Release then build Installer - Release. - This creates SynergyInstaller.exe in the - build directory. Run this to install synergy. +

            + You'll need NSIS, + the Nullsoft Scriptable Install System. As in the building on Windows + description above, enter: +

            +  nmake /nologo /f nmake.mak installer
            +  
            + to build build\Release\SynergyInstaller.exe. Run + this to install synergy.

            Alternatively, you can simply copy the following files from the - debug or build + build\Release directory to a directory you choose (perhaps under the Program Files directory):

              @@ -96,24 +97,15 @@ so synergy can find the X11 includes and libraries.
            • synergys.exe
            • synrgyhk.dll
            -

            -

          • Unix or Mac OS X without XCode -

            +

            +
          • Unix or Mac OS X +

               make install
               
            will install the client and server into /usr/local/bin unless you specified a different directory when you ran configure. -

            -

          • Mac OS X with XCode -

            - Copy the following files from ./build to a convenient location: -

            -

              -
            • synergyc -
            • synergys -

            From 5c29ae99678349a8cf4e2afd09a0e37657306971 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:01:44 +0000 Subject: [PATCH 792/807] Applied patch by maruel: - Fixed taking the address of begin() on an empty std::vector. - Fixed nsis makefile to use %ProgramFiles% environment variable. - Fixed nsis makefile to pass the output directory and file to makensis. - Fixed synergy.nsi to get the files from the output directory. That enables a debug build of the installer. - Fixes to compile under VS2005. I did not apply VS2005 project files, instead adding nmake files. nmake is pretty weak but the makefiles can be modified without having visual studio. Also modified the .rc files to not use winres.h. This plus nmake means synergy can now be built using the freely downloadable Microsoft Windows SDK for Vista, available from microsoft's web site. This change removes all of the old VC++6 project files in favor of the nmake files. It also removes the XCode project in favor of ./configure and make. All of the nmake files are named nmake.mak. Only the top level makefile is directly useful (the rest are included by it) so all builds are from the top level directory. nmake knows the following targets: all: build synergy.exe, synergyc.exe and synergys.exe clean: remove all intermediate files, keep programs clobber: clean and remove programs installer: build programs and an installer debug: build a debug version of 'all' release: build a release version of 'all' debug-installer: build an installer of the debug build release-installer: build an installer of the release build The default build version is release so 'all' and 'installer' will build a release version. The installer itself never has debug symbols, just the stuff it installs. The default target is 'all'. To build use: nmake /nologo /f nmake.mak VC++ and VisualStudio users may need to manually run vcvars.bat in a command.exe or cmd.exe window before invoking nmake. The Window 98/Me command.exe may not handle potentially long command lines; I haven't tried to verify if that works. --- COPYING | 2 +- ChangeLog | 51 + Makefile.am | 5 +- all.dsp | 63 - cmd/exec.dsp | 63 - cmd/launcher/Makefile.am | 2 +- cmd/launcher/launcher.dsp | 186 -- cmd/launcher/launcher.rc | 5 +- cmd/launcher/nmake.mak | 101 + cmd/synergyc/Makefile.am | 2 +- cmd/synergyc/nmake.mak | 89 + cmd/synergyc/synergyc.cpp | 6 +- cmd/synergyc/synergyc.dsp | 153 -- cmd/synergyc/synergyc.rc | 2 +- cmd/synergys/Makefile.am | 2 +- cmd/synergys/nmake.mak | 89 + cmd/synergys/synergys.cpp | 6 +- cmd/synergys/synergys.dsp | 153 -- cmd/synergys/synergys.rc | 2 +- dist/nullsoft/Makefile.am | 3 +- dist/nullsoft/installer.dsp | 114 -- dist/nullsoft/installer.mak | 20 - dist/nullsoft/nmake.mak | 63 + dist/nullsoft/synergy.nsi | 69 +- doc/authors.html | 7 + lib/arch/CArchDaemonNone.cpp | 2 +- lib/arch/Makefile.am | 2 +- lib/arch/arch.dsp | 272 --- lib/arch/nmake.mak | 84 + lib/base/Makefile.am | 2 +- lib/base/base.dsp | 222 --- lib/base/nmake.mak | 77 + lib/client/Makefile.am | 2 +- lib/client/client.dsp | 114 -- lib/client/nmake.mak | 63 + lib/common/Makefile.am | 2 +- lib/common/common.dsp | 166 -- lib/common/nmake.mak | 53 + lib/io/Makefile.am | 2 +- lib/io/io.dsp | 130 -- lib/io/nmake.mak | 63 + lib/mt/Makefile.am | 2 +- lib/mt/mt.dsp | 138 -- lib/mt/nmake.mak | 64 + lib/net/CSocketMultiplexer.cpp | 7 +- lib/net/CTCPSocket.cpp | 2 +- lib/net/Makefile.am | 2 +- lib/net/net.dsp | 178 -- lib/net/nmake.mak | 74 + lib/platform/CMSWindowsScreen.cpp | 3 +- lib/platform/CSynergyHook.cpp | 89 +- lib/platform/Makefile.am | 4 +- lib/platform/makehook.dsp | 63 - lib/platform/nmake.mak | 119 ++ lib/platform/platform.dsp | 190 -- lib/platform/synrgyhk.dsp | 116 -- lib/server/Makefile.am | 2 +- lib/server/nmake.mak | 83 + lib/server/server.dsp | 194 -- lib/synergy/Makefile.am | 2 +- lib/synergy/libsynergy.dsp | 246 --- lib/synergy/nmake.mak | 87 + nmake.mak | 145 ++ synergy.dsw | 332 ---- synergy.xcode/project.pbxproj | 2882 ----------------------------- win32util/autodep.cpp | 149 ++ 66 files changed, 1623 insertions(+), 6064 deletions(-) delete mode 100644 all.dsp delete mode 100644 cmd/exec.dsp delete mode 100644 cmd/launcher/launcher.dsp create mode 100644 cmd/launcher/nmake.mak create mode 100644 cmd/synergyc/nmake.mak delete mode 100644 cmd/synergyc/synergyc.dsp create mode 100644 cmd/synergys/nmake.mak delete mode 100644 cmd/synergys/synergys.dsp delete mode 100644 dist/nullsoft/installer.dsp delete mode 100755 dist/nullsoft/installer.mak create mode 100644 dist/nullsoft/nmake.mak delete mode 100644 lib/arch/arch.dsp create mode 100644 lib/arch/nmake.mak delete mode 100644 lib/base/base.dsp create mode 100644 lib/base/nmake.mak delete mode 100644 lib/client/client.dsp create mode 100644 lib/client/nmake.mak delete mode 100644 lib/common/common.dsp create mode 100644 lib/common/nmake.mak delete mode 100644 lib/io/io.dsp create mode 100644 lib/io/nmake.mak delete mode 100644 lib/mt/mt.dsp create mode 100644 lib/mt/nmake.mak delete mode 100644 lib/net/net.dsp create mode 100644 lib/net/nmake.mak delete mode 100644 lib/platform/makehook.dsp create mode 100644 lib/platform/nmake.mak delete mode 100644 lib/platform/platform.dsp delete mode 100644 lib/platform/synrgyhk.dsp create mode 100644 lib/server/nmake.mak delete mode 100644 lib/server/server.dsp delete mode 100644 lib/synergy/libsynergy.dsp create mode 100644 lib/synergy/nmake.mak create mode 100644 nmake.mak delete mode 100644 synergy.dsw delete mode 100644 synergy.xcode/project.pbxproj create mode 100644 win32util/autodep.cpp diff --git a/COPYING b/COPYING index 43bb4f3e..ce0fb161 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Synergy is copyright (C) 2002 Chris Schoeneman. +Synergy is copyright (C) 2002-2007 Chris Schoeneman. Synergy is distributed under the following license. GNU GENERAL PUBLIC LICENSE diff --git a/ChangeLog b/ChangeLog index ea4c7950..9099fd9f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,54 @@ +2007/07/01 crs +*.vcproj +*.vsprops +synergy.sln +dist/nullsoft/installer.mak +dist/nullsoft/synergy.nsi +lib/net/CSocketMultiplexer.cpp +lib/net/CTCPSocket.cpp +lib/platform/CSynergyHook.cpp + +Applied patch by maruel: +- Fixed taking the address of begin() on an empty std::vector. +- Fixed nsis makefile to use %ProgramFiles% environment variable. +- Fixed nsis makefile to pass the output directory and file to makensis. +- Fixed synergy.nsi to get the files from the output directory. That + enables a debug build of the installer. +- Fixes to compile under VS2005. + +I did not apply VS2005 project files, instead adding nmake files. nmake is +pretty weak but the makefiles can be modified without having visual studio. +Also modified the .rc files to not use winres.h. This plus nmake means +synergy can now be built using the freely downloadable Microsoft Windows +SDK for Vista, available from microsoft's web site. This change removes +all of the old VC++6 project files in favor of the nmake files. It also +removes the XCode project in favor of ./configure and make. + +All of the nmake files are named nmake.mak. Only the top level makefile +is directly useful (the rest are included by it) so all builds are from +the top level directory. nmake knows the following targets: + + all: build synergy.exe, synergyc.exe and synergys.exe + clean: remove all intermediate files, keep programs + clobber: clean and remove programs + installer: build programs and an installer + debug: build a debug version of 'all' + release: build a release version of 'all' + debug-installer: build an installer of the debug build + release-installer: build an installer of the release build + +The default build version is release so 'all' and 'installer' will build +a release version. The installer itself never has debug symbols, just +the stuff it installs. The default target is 'all'. To build use: + + nmake /nologo /f nmake.mak + +VC++ and VisualStudio users may need to manually run vcvars.bat in a +command.exe or cmd.exe window before invoking nmake. The Window 98/Me +command.exe may not handle potentially long command lines; I haven't +tried to verify if that works. + +---------- 2007/08/22 21:42:09 crs lib/platform/COSXKeyState.cpp diff --git a/Makefile.am b/Makefile.am index 6ef89d99..566aacf7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,10 +21,9 @@ SUBDIRS = \ $(NULL) EXTRA_DIST = \ - all.dsp \ - synergy.dsw \ - synergy.xcode/project.pbxproj \ + nmake.mak \ examples/synergy.conf \ + win32util/autodep.cpp \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/all.dsp b/all.dsp deleted file mode 100644 index 741b7b6e..00000000 --- a/all.dsp +++ /dev/null @@ -1,63 +0,0 @@ -# Microsoft Developer Studio Project File - Name="all" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Generic Project" 0x010a - -CFG=all - Win32 Debug -!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 "all.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 "all.mak" CFG="all - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "all - Win32 Release" (based on "Win32 (x86) Generic Project") -!MESSAGE "all - Win32 Debug" (based on "Win32 (x86) Generic Project") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -MTL=midl.exe - -!IF "$(CFG)" == "all - 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 "..\..\build" -# PROP Intermediate_Dir "..\..\gen\build" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "all - 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 "..\..\gen\debug" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "all - Win32 Release" -# Name "all - Win32 Debug" -# End Target -# End Project diff --git a/cmd/exec.dsp b/cmd/exec.dsp deleted file mode 100644 index e6e5fafc..00000000 --- a/cmd/exec.dsp +++ /dev/null @@ -1,63 +0,0 @@ -# Microsoft Developer Studio Project File - Name="exec" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Generic Project" 0x010a - -CFG=exec - Win32 Debug -!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 "exec.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 "exec.mak" CFG="exec - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "exec - Win32 Release" (based on "Win32 (x86) Generic Project") -!MESSAGE "exec - Win32 Debug" (based on "Win32 (x86) Generic Project") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -MTL=midl.exe - -!IF "$(CFG)" == "exec - 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 "..\..\build" -# PROP Intermediate_Dir "..\..\gen\build" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "exec - 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 "..\..\gen\debug" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "exec - Win32 Release" -# Name "exec - Win32 Debug" -# End Target -# End Project diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am index 729aeb79..27c3cce6 100644 --- a/cmd/launcher/Makefile.am +++ b/cmd/launcher/Makefile.am @@ -36,7 +36,7 @@ MSWINDOWS_SOURCE_FILES = \ $(NULL) EXTRA_DIST = \ - launcher.dsp \ + nmake.mak \ synergy.ico \ $(MSWINDOWS_SOURCE_FILES) \ $(NULL) diff --git a/cmd/launcher/launcher.dsp b/cmd/launcher/launcher.dsp deleted file mode 100644 index f799afc9..00000000 --- a/cmd/launcher/launcher.dsp +++ /dev/null @@ -1,186 +0,0 @@ -# Microsoft Developer Studio Project File - Name="launcher" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Application" 0x0101 - -CFG=launcher - Win32 Debug -!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 "launcher.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 "launcher.mak" CFG="launcher - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "launcher - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "launcher - Win32 Debug" (based on "Win32 (x86) Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "launcher - 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 "..\..\build" -# PROP Intermediate_Dir "..\..\gen\build" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\launcher.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"..\..\build\synergy.exe" -# SUBTRACT LINK32 /map - -!ELSEIF "$(CFG)" == "launcher - 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 "..\..\gen\debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\launcher.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"..\..\debug\synergy.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "launcher - Win32 Release" -# Name "launcher - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CAddScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CAdvancedOptions.cpp -# End Source File -# Begin Source File - -SOURCE=.\CAutoStart.cpp -# End Source File -# Begin Source File - -SOURCE=.\CGlobalOptions.cpp -# End Source File -# Begin Source File - -SOURCE=.\CHotkeyOptions.cpp -# End Source File -# Begin Source File - -SOURCE=.\CInfo.cpp -# End Source File -# Begin Source File - -SOURCE=.\CScreensLinks.cpp -# End Source File -# Begin Source File - -SOURCE=.\launcher.cpp -# End Source File -# Begin Source File - -SOURCE=.\launcher.rc -# End Source File -# Begin Source File - -SOURCE=.\LaunchUtil.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CAddScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CAdvancedOptions.h -# End Source File -# Begin Source File - -SOURCE=.\CAutoStart.h -# End Source File -# Begin Source File - -SOURCE=.\CGlobalOptions.h -# End Source File -# Begin Source File - -SOURCE=.\CHotkeyOptions.h -# End Source File -# Begin Source File - -SOURCE=.\CInfo.h -# End Source File -# Begin Source File - -SOURCE=.\CScreensLinks.h -# End Source File -# Begin Source File - -SOURCE=.\LaunchUtil.h -# End Source File -# Begin Source File - -SOURCE=.\resource.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# Begin Source File - -SOURCE=.\synergy.ico -# End Source File -# End Group -# End Target -# End Project diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 802f3d3e..3f5ff0d7 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -7,7 +7,10 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" +#include +#if !defined(IDC_STATIC) +#define IDC_STATIC (-1) +#endif ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS diff --git a/cmd/launcher/nmake.mak b/cmd/launcher/nmake.mak new file mode 100644 index 00000000..3d4f277a --- /dev/null +++ b/cmd/launcher/nmake.mak @@ -0,0 +1,101 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +BIN_LAUNCHER_SRC = cmd\launcher +BIN_LAUNCHER_DST = $(BUILD_DST)\$(BIN_LAUNCHER_SRC) +BIN_LAUNCHER_EXE = "$(BUILD_DST)\synergy.exe" +BIN_LAUNCHER_CPP = \ + "CAddScreen.cpp" \ + "CAdvancedOptions.cpp" \ + "CAutoStart.cpp" \ + "CGlobalOptions.cpp" \ + "CHotkeyOptions.cpp" \ + "CInfo.cpp" \ + "CScreensLinks.cpp" \ + "LaunchUtil.cpp" \ + "launcher.cpp" \ + $(NULL) +BIN_LAUNCHER_OBJ = \ + "$(BIN_LAUNCHER_DST)\CAddScreen.obj" \ + "$(BIN_LAUNCHER_DST)\CAdvancedOptions.obj" \ + "$(BIN_LAUNCHER_DST)\CAutoStart.obj" \ + "$(BIN_LAUNCHER_DST)\CGlobalOptions.obj" \ + "$(BIN_LAUNCHER_DST)\CHotkeyOptions.obj" \ + "$(BIN_LAUNCHER_DST)\CInfo.obj" \ + "$(BIN_LAUNCHER_DST)\CScreensLinks.obj" \ + "$(BIN_LAUNCHER_DST)\LaunchUtil.obj" \ + "$(BIN_LAUNCHER_DST)\launcher.obj" \ + $(NULL) +BIN_LAUNCHER_RC = "$(BIN_LAUNCHER_SRC)\launcher.rc" +BIN_LAUNCHER_RES = "$(BIN_LAUNCHER_DST)\launcher.res" +BIN_LAUNCHER_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + /I"lib\base" \ + /I"lib\mt" \ + /I"lib\io" \ + /I"lib\net" \ + /I"lib\synergy" \ + /I"lib\platform" \ + /I"lib\server" \ + $(NULL) +BIN_LAUNCHER_LIB = \ + $(LIB_SERVER_LIB) \ + $(LIB_PLATFORM_LIB) \ + $(LIB_SYNERGY_LIB) \ + $(LIB_NET_LIB) \ + $(LIB_IO_LIB) \ + $(LIB_MT_LIB) \ + $(LIB_BASE_LIB) \ + $(LIB_ARCH_LIB) \ + $(LIB_COMMON_LIB) \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(BIN_LAUNCHER_CPP) +OBJ_FILES = $(OBJ_FILES) $(BIN_LAUNCHER_OBJ) +PROGRAMS = $(PROGRAMS) $(BIN_LAUNCHER_EXE) + +# Need shell functions. +guilibs = $(guilibs) shell32.lib + +# Dependency rules +$(BIN_LAUNCHER_OBJ): $(AUTODEP) +!if EXIST($(BIN_LAUNCHER_DST)\deps.mak) +!include $(BIN_LAUNCHER_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(BIN_LAUNCHER_SRC)\}.cpp{$(BIN_LAUNCHER_DST)\}.obj:: +!else +{$(BIN_LAUNCHER_SRC)\}.cpp{$(BIN_LAUNCHER_DST)\}.obj: +!endif + @$(ECHO) Compile in $(BIN_LAUNCHER_SRC) + -@$(MKDIR) $(BIN_LAUNCHER_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(BIN_LAUNCHER_INC) \ + /Fo$(BIN_LAUNCHER_DST)\ \ + /Fd$(BIN_LAUNCHER_DST)\src.pdb \ + $< | $(AUTODEP) $(BIN_LAUNCHER_SRC) $(BIN_LAUNCHER_DST) +$(BIN_LAUNCHER_RES): $(BIN_LAUNCHER_RC) + @$(ECHO) Compile $(**F) + -@$(MKDIR) $(BIN_LAUNCHER_DST) 2>NUL: + $(rc) $(rcflags) $(rcvars) \ + /fo$@ \ + $** +$(BIN_LAUNCHER_EXE): $(BIN_LAUNCHER_OBJ) $(BIN_LAUNCHER_RES) $(BIN_LAUNCHER_LIB) + @$(ECHO) Link $(@F) + $(link) $(ldebug) $(guilflags) $(guilibsmt) \ + /out:$@ \ + $** + $(AUTODEP) $(BIN_LAUNCHER_SRC) $(BIN_LAUNCHER_DST) \ + $(BIN_LAUNCHER_OBJ:.obj=.d) diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 89942666..70360e77 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -34,7 +34,7 @@ CARBON_SOURCE_FILES = \ $(NULL) EXTRA_DIST = \ - synergyc.dsp \ + nmake.mak \ synergyc.ico \ tb_error.ico \ tb_idle.ico \ diff --git a/cmd/synergyc/nmake.mak b/cmd/synergyc/nmake.mak new file mode 100644 index 00000000..29f2e516 --- /dev/null +++ b/cmd/synergyc/nmake.mak @@ -0,0 +1,89 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +BIN_SYNERGYC_SRC = cmd\synergyc +BIN_SYNERGYC_DST = $(BUILD_DST)\$(BIN_SYNERGYC_SRC) +BIN_SYNERGYC_EXE = "$(BUILD_DST)\synergyc.exe" +BIN_SYNERGYC_CPP = \ + "CClientTaskBarReceiver.cpp" \ + "CMSWindowsClientTaskBarReceiver.cpp" \ + "synergyc.cpp" \ + $(NULL) +BIN_SYNERGYC_OBJ = \ + "$(BIN_SYNERGYC_DST)\CClientTaskBarReceiver.obj" \ + "$(BIN_SYNERGYC_DST)\CMSWindowsClientTaskBarReceiver.obj" \ + "$(BIN_SYNERGYC_DST)\synergyc.obj" \ + $(NULL) +BIN_SYNERGYC_RC = "$(BIN_SYNERGYC_SRC)\synergyc.rc" +BIN_SYNERGYC_RES = "$(BIN_SYNERGYC_DST)\synergyc.res" +BIN_SYNERGYC_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + /I"lib\base" \ + /I"lib\mt" \ + /I"lib\io" \ + /I"lib\net" \ + /I"lib\synergy" \ + /I"lib\platform" \ + /I"lib\client" \ + $(NULL) +BIN_SYNERGYC_LIB = \ + $(LIB_CLIENT_LIB) \ + $(LIB_PLATFORM_LIB) \ + $(LIB_SYNERGY_LIB) \ + $(LIB_NET_LIB) \ + $(LIB_IO_LIB) \ + $(LIB_MT_LIB) \ + $(LIB_BASE_LIB) \ + $(LIB_ARCH_LIB) \ + $(LIB_COMMON_LIB) \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(BIN_SYNERGYC_CPP) +OBJ_FILES = $(OBJ_FILES) $(BIN_SYNERGYC_OBJ) +PROGRAMS = $(PROGRAMS) $(BIN_SYNERGYC_EXE) + +# Need shell functions. +guilibs = $(guilibs) shell32.lib + +# Dependency rules +$(BIN_SYNERGYC_OBJ): $(AUTODEP) +!if EXIST($(BIN_SYNERGYC_DST)\deps.mak) +!include $(BIN_SYNERGYC_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(BIN_SYNERGYC_SRC)\}.cpp{$(BIN_SYNERGYC_DST)\}.obj:: +!else +{$(BIN_SYNERGYC_SRC)\}.cpp{$(BIN_SYNERGYC_DST)\}.obj: +!endif + @$(ECHO) Compile in $(BIN_SYNERGYC_SRC) + -@$(MKDIR) $(BIN_SYNERGYC_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(BIN_SYNERGYC_INC) \ + /Fo$(BIN_SYNERGYC_DST)\ \ + /Fd$(BIN_SYNERGYC_DST)\src.pdb \ + $< | $(AUTODEP) $(BIN_SYNERGYC_SRC) $(BIN_SYNERGYC_DST) +$(BIN_SYNERGYC_RES): $(BIN_SYNERGYC_RC) + @$(ECHO) Compile $(**F) + -@$(MKDIR) $(BIN_SYNERGYC_DST) 2>NUL: + $(rc) $(rcflags) $(rcvars) \ + /fo$@ \ + $** +$(BIN_SYNERGYC_EXE): $(BIN_SYNERGYC_OBJ) $(BIN_SYNERGYC_RES) $(BIN_SYNERGYC_LIB) + @$(ECHO) Link $(@F) + $(link) $(ldebug) $(guilflags) $(guilibsmt) \ + /out:$@ \ + $** + $(AUTODEP) $(BIN_SYNERGYC_SRC) $(BIN_SYNERGYC_DST) \ + $(BIN_SYNERGYC_OBJ:.obj=.d) diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index eabddb49..1a230f0d 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -862,16 +862,16 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) } catch (XBase& e) { showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what()); - throw; + //throw; } catch (XArch& e) { showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str()); - return kExitFailed; } catch (...) { showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, ""); - throw; + //throw; } + return kExitFailed; } #elif SYSAPI_UNIX diff --git a/cmd/synergyc/synergyc.dsp b/cmd/synergyc/synergyc.dsp deleted file mode 100644 index 4238ea03..00000000 --- a/cmd/synergyc/synergyc.dsp +++ /dev/null @@ -1,153 +0,0 @@ -# Microsoft Developer Studio Project File - Name="synergyc" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Application" 0x0101 - -CFG=synergyc - Win32 Debug -!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 "synergyc.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 "synergyc.mak" CFG="synergyc - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "synergyc - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "synergyc - Win32 Debug" (based on "Win32 (x86) Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "synergyc - 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 "..\..\build" -# PROP Intermediate_Dir "..\..\gen\build" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\synergyc.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 - -!ELSEIF "$(CFG)" == "synergyc - 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 "..\..\gen\debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\io" /I "..\..\lib\mt" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\client" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\synergyc.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "synergyc - Win32 Release" -# Name "synergyc - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CClientTaskBarReceiver.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClientTaskBarReceiver.cpp -# End Source File -# Begin Source File - -SOURCE=.\synergyc.cpp -# End Source File -# Begin Source File - -SOURCE=.\synergyc.rc -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CClientTaskBarReceiver.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClientTaskBarReceiver.h -# End Source File -# Begin Source File - -SOURCE=.\resource.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# Begin Source File - -SOURCE=.\synergyc.ico -# End Source File -# Begin Source File - -SOURCE=.\tb_error.ico -# End Source File -# Begin Source File - -SOURCE=.\tb_idle.ico -# End Source File -# Begin Source File - -SOURCE=.\tb_run.ico -# End Source File -# Begin Source File - -SOURCE=.\tb_wait.ico -# End Source File -# End Group -# End Target -# End Project diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index 519e8e48..72d032ac 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -7,7 +7,7 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "winres.h" +#include #if !defined(IDC_STATIC) #define IDC_STATIC (-1) #endif diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index 3e02739d..c1a0dee1 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -34,7 +34,7 @@ CARBON_SOURCE_FILES = \ $(NULL) EXTRA_DIST = \ - synergys.dsp \ + nmake.mak \ synergys.ico \ tb_error.ico \ tb_idle.ico \ diff --git a/cmd/synergys/nmake.mak b/cmd/synergys/nmake.mak new file mode 100644 index 00000000..09d39958 --- /dev/null +++ b/cmd/synergys/nmake.mak @@ -0,0 +1,89 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +BIN_SYNERGYS_SRC = cmd\synergys +BIN_SYNERGYS_DST = $(BUILD_DST)\$(BIN_SYNERGYS_SRC) +BIN_SYNERGYS_EXE = "$(BUILD_DST)\synergys.exe" +BIN_SYNERGYS_CPP = \ + "CServerTaskBarReceiver.cpp" \ + "CMSWindowsServerTaskBarReceiver.cpp" \ + "synergys.cpp" \ + $(NULL) +BIN_SYNERGYS_OBJ = \ + "$(BIN_SYNERGYS_DST)\CServerTaskBarReceiver.obj" \ + "$(BIN_SYNERGYS_DST)\CMSWindowsServerTaskBarReceiver.obj" \ + "$(BIN_SYNERGYS_DST)\synergys.obj" \ + $(NULL) +BIN_SYNERGYS_RC = "$(BIN_SYNERGYS_SRC)\synergys.rc" +BIN_SYNERGYS_RES = "$(BIN_SYNERGYS_DST)\synergys.res" +BIN_SYNERGYS_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + /I"lib\base" \ + /I"lib\mt" \ + /I"lib\io" \ + /I"lib\net" \ + /I"lib\synergy" \ + /I"lib\platform" \ + /I"lib\server" \ + $(NULL) +BIN_SYNERGYS_LIB = \ + $(LIB_SERVER_LIB) \ + $(LIB_PLATFORM_LIB) \ + $(LIB_SYNERGY_LIB) \ + $(LIB_NET_LIB) \ + $(LIB_IO_LIB) \ + $(LIB_MT_LIB) \ + $(LIB_BASE_LIB) \ + $(LIB_ARCH_LIB) \ + $(LIB_COMMON_LIB) \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(BIN_SYNERGYS_CPP) +OBJ_FILES = $(OBJ_FILES) $(BIN_SYNERGYS_OBJ) +PROGRAMS = $(PROGRAMS) $(BIN_SYNERGYS_EXE) + +# Need shell functions. +guilibs = $(guilibs) shell32.lib + +# Dependency rules +$(BIN_SYNERGYS_OBJ): $(AUTODEP) +!if EXIST($(BIN_SYNERGYS_DST)\deps.mak) +!include $(BIN_SYNERGYS_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(BIN_SYNERGYS_SRC)\}.cpp{$(BIN_SYNERGYS_DST)\}.obj:: +!else +{$(BIN_SYNERGYS_SRC)\}.cpp{$(BIN_SYNERGYS_DST)\}.obj: +!endif + @$(ECHO) Compile in $(BIN_SYNERGYS_SRC) + -@$(MKDIR) $(BIN_SYNERGYS_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(BIN_SYNERGYS_INC) \ + /Fo$(BIN_SYNERGYS_DST)\ \ + /Fd$(BIN_SYNERGYS_DST)\src.pdb \ + $< | $(AUTODEP) $(BIN_SYNERGYS_SRC) $(BIN_SYNERGYS_DST) +$(BIN_SYNERGYS_RES): $(BIN_SYNERGYS_RC) + @$(ECHO) Compile $(**F) + -@$(MKDIR) $(BIN_SYNERGYS_DST) 2>NUL: + $(rc) $(rcflags) $(rcvars) \ + /fo$@ \ + $** +$(BIN_SYNERGYS_EXE): $(BIN_SYNERGYS_OBJ) $(BIN_SYNERGYS_RES) $(BIN_SYNERGYS_LIB) + @$(ECHO) Link $(@F) + $(link) $(ldebug) $(guilflags) $(guilibsmt) \ + /out:$@ \ + $** + $(AUTODEP) $(BIN_SYNERGYS_SRC) $(BIN_SYNERGYS_DST) \ + $(BIN_SYNERGYS_OBJ:.obj=.d) diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 6821a4e2..4319af1e 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -1264,16 +1264,16 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) } catch (XBase& e) { showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what()); - throw; + //throw; } catch (XArch& e) { showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str()); - return kExitFailed; } catch (...) { showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, ""); - throw; + //throw; } + return kExitFailed; } #elif SYSAPI_UNIX diff --git a/cmd/synergys/synergys.dsp b/cmd/synergys/synergys.dsp deleted file mode 100644 index a8d791de..00000000 --- a/cmd/synergys/synergys.dsp +++ /dev/null @@ -1,153 +0,0 @@ -# Microsoft Developer Studio Project File - Name="synergys" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Application" 0x0101 - -CFG=synergys - Win32 Debug -!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 "synergys.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 "synergys.mak" CFG="synergys - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "synergys - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "synergys - Win32 Debug" (based on "Win32 (x86) Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "synergys - 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 "..\..\build" -# PROP Intermediate_Dir "..\..\gen\build" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\build\synergys.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 - -!ELSEIF "$(CFG)" == "synergys - 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 "..\..\gen\debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\..\lib\common" /I "..\..\lib\arch" /I "..\..\lib\base" /I "..\..\lib\mt" /I "..\..\lib\io" /I "..\..\lib\net" /I "..\..\lib\synergy" /I "..\..\lib\platform" /I "..\..\lib\server" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fd"..\..\gen\debug\synergys.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "synergys - Win32 Release" -# Name "synergys - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CMSWindowsServerTaskBarReceiver.cpp -# End Source File -# Begin Source File - -SOURCE=.\CServerTaskBarReceiver.cpp -# End Source File -# Begin Source File - -SOURCE=.\synergys.cpp -# End Source File -# Begin Source File - -SOURCE=.\synergys.rc -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CMSWindowsServerTaskBarReceiver.h -# End Source File -# Begin Source File - -SOURCE=.\CServerTaskBarReceiver.h -# End Source File -# Begin Source File - -SOURCE=.\resource.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# Begin Source File - -SOURCE=.\synergys.ico -# End Source File -# Begin Source File - -SOURCE=.\tb_error.ico -# End Source File -# Begin Source File - -SOURCE=.\tb_idle.ico -# End Source File -# Begin Source File - -SOURCE=.\tb_run.ico -# End Source File -# Begin Source File - -SOURCE=.\tb_wait.ico -# End Source File -# End Group -# End Target -# End Project diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index 117b1852..97272ff2 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -7,7 +7,7 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "winres.h" +#include #if !defined(IDC_STATIC) #define IDC_STATIC (-1) #endif diff --git a/dist/nullsoft/Makefile.am b/dist/nullsoft/Makefile.am index 5630da82..968f23cf 100644 --- a/dist/nullsoft/Makefile.am +++ b/dist/nullsoft/Makefile.am @@ -14,8 +14,7 @@ NULL = EXTRA_DIST = \ - installer.dsp \ - installer.mak \ + nmake.mak \ synergy.nsi \ dosify.c \ $(NULL) diff --git a/dist/nullsoft/installer.dsp b/dist/nullsoft/installer.dsp deleted file mode 100644 index 2fe0bd5d..00000000 --- a/dist/nullsoft/installer.dsp +++ /dev/null @@ -1,114 +0,0 @@ -# Microsoft Developer Studio Project File - Name="installer" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) External Target" 0x0106 - -CFG=installer - Win32 Debug -!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 "installer.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 "installer.mak" CFG="installer - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "installer - Win32 Release" (based on "Win32 (x86) External Target") -!MESSAGE "installer - Win32 Debug" (based on "Win32 (x86) External Target") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" - -!IF "$(CFG)" == "installer - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "installer___Win32_Release" -# PROP BASE Intermediate_Dir "installer___Win32_Release" -# PROP BASE Cmd_Line "NMAKE /f installer.mak" -# PROP BASE Rebuild_Opt "/a" -# PROP BASE Target_File "installer.exe" -# PROP BASE Bsc_Name "installer.bsc" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "." -# PROP Intermediate_Dir "." -# PROP Cmd_Line "nmake /nologo /f "installer.mak"" -# PROP Rebuild_Opt "/a" -# PROP Target_File "..\..\build\SynergyInstaller.exe" -# PROP Bsc_Name "" -# PROP Target_Dir "" -NSIS=makensis - -!ELSEIF "$(CFG)" == "installer - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "installer___Win32_Debug" -# PROP BASE Intermediate_Dir "installer___Win32_Debug" -# PROP BASE Cmd_Line "NMAKE /f installer.mak" -# PROP BASE Rebuild_Opt "/a" -# PROP BASE Target_File "installer.exe" -# PROP BASE Bsc_Name "installer.bsc" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "." -# PROP Intermediate_Dir "." -# PROP Cmd_Line "echo "Can only build installer for Release configuration"" -# PROP Rebuild_Opt "" -# PROP Target_File "dummy.exe" -# PROP Bsc_Name "" -# PROP Target_Dir "" -NSIS=makensis - -!ENDIF - -# Begin Target - -# Name "installer - Win32 Release" -# Name "installer - Win32 Debug" - -!IF "$(CFG)" == "installer - Win32 Release" - -!ELSEIF "$(CFG)" == "installer - Win32 Debug" - -!ENDIF - -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\dosify.c -# PROP Intermediate_Dir "." -# End Source File -# Begin Source File - -SOURCE=.\installer.mak -# PROP Intermediate_Dir "." -# End Source File -# Begin Source File - -SOURCE=.\synergy.nsi -# PROP Intermediate_Dir "." -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/dist/nullsoft/installer.mak b/dist/nullsoft/installer.mak deleted file mode 100755 index 4f4b69b5..00000000 --- a/dist/nullsoft/installer.mak +++ /dev/null @@ -1,20 +0,0 @@ -NULL= -DEPTH=..\.. - -NSIS="D:\Program Files\NSIS\makensis" - -DOCS = \ - COPYING \ - ChangeLog \ - $(NULL) - -default: dosifydocs installer - -installer: - $(NSIS) synergy.nsi - -dosifydocs: dosify.exe - .\dosify.exe $(DEPTH) . $(DOCS) - -dosify.exe: dosify.c - $(CC) /nologo /Yd /Zi /MLd /Fe$@ $** diff --git a/dist/nullsoft/nmake.mak b/dist/nullsoft/nmake.mak new file mode 100644 index 00000000..91aa68bb --- /dev/null +++ b/dist/nullsoft/nmake.mak @@ -0,0 +1,63 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +NSIS = "$(PROGRAMFILES)\NSIS\makensis.exe" +NSIS_FLAGS = /NOCD /V1 + +BIN_INSTALLER_SRC = dist\nullsoft +BIN_INSTALLER_DST = $(BUILD_DST)\$(BIN_INSTALLER_SRC) +BIN_DOSIFY_EXE = "$(BIN_INSTALLER_DST)\dosify.exe" +BIN_DOSIFY_C = \ + "$(BIN_INSTALLER_SRC)\dosify.c" \ + $(NULL) +BIN_DOSIFY_OBJ = \ + "$(BIN_INSTALLER_DST)\dosify.obj" \ + $(NULL) + +BIN_INSTALLER_NSI = "$(BIN_INSTALLER_SRC)\synergy.nsi" +BIN_INSTALLER_EXE = "$(BUILD_DST)\SynergyInstaller.exe" +BIN_INSTALLER_DOCS = \ + COPYING \ + ChangeLog \ + $(NULL) +BIN_INSTALLER_DOS_DOCS = \ + $(BUILD_DST)\COPYING.txt \ + $(BUILD_DST)\ChangeLog.txt \ + $(NULL) + +C_FILES = $(C_FILES) $(BIN_DOSIFY_C) +OBJ_FILES = $(OBJ_FILES) $(BIN_DOSIFY_OBJ) +OPTPROGRAMS = $(OPTPROGRAMS) $(BIN_DOSIFY_EXE) + +# Build rules. +$(BIN_DOSIFY_OBJ): $(BIN_DOSIFY_C) + @$(ECHO) Compile $(BIN_DOSIFY_C) + -@$(MKDIR) $(BIN_INSTALLER_DST) 2>NUL: + $(cc) $(cdebug) $(cflags) $(cvars) /Fo$@ /Fd$(@:.obj=.pdb) $** +$(BIN_DOSIFY_EXE): $(BIN_DOSIFY_OBJ) + @$(ECHO) Link $(@F) + $(link) $(ldebug) $(conlflags) $(conlibsmt) /out:$@ $** + +# Convert text files from Unix to DOS format. +$(BIN_INSTALLER_DOS_DOCS): $(BIN_DOSIFY_EXE) $(BIN_INSTALLER_DOCS) + @$(ECHO) Convert text files to DOS format + $(BIN_DOSIFY_EXE) "." "$(BUILD_DST)" $(BIN_INSTALLER_DOCS) + +# Allow installers for both debug and release. +$(BIN_INSTALLER_EXE): $(BIN_INSTALLER_NSI) all $(BIN_INSTALLER_DOS_DOCS) + @$(ECHO) Build $(@F) + $(NSIS) $(NSIS_FLAGS) /DOUTPUTDIR=$(@D) /DOUTPUTFILE=$@ $(BIN_INSTALLER_NSI) +installer: $(BIN_INSTALLER_EXE) +debug-installer: + @$(MAKE) /nologo /f $(MAKEFILE) DEBUG=1 installer +release-installer: + @$(MAKE) /nologo /f $(MAKEFILE) NODEBUG=1 installer diff --git a/dist/nullsoft/synergy.nsi b/dist/nullsoft/synergy.nsi index bbc26cfc..3370d03a 100644 --- a/dist/nullsoft/synergy.nsi +++ b/dist/nullsoft/synergy.nsi @@ -7,14 +7,15 @@ ;-------------------------------- -; Path to root of tree -!define DEPTH "..\.." +!ifndef OUTPUTDIR +!define OUTPUTDIR "build\Release" +!endif ; The name of the installer Name "Synergy" ; The file to write -OutFile "${DEPTH}\build\SynergyInstaller.exe" +OutFile "${OUTPUTFILE}" ; The default installation directory InstallDir $PROGRAMFILES\Synergy @@ -42,7 +43,7 @@ ComponentText "This will install Synergy on your computer. Select the optional DirText "Choose a directory to install Synergy to:" UninstallText "This will uninstall Synergy from your computer." LicenseText "Synergy is distributed under the GNU GPL:" -LicenseData "COPYING.txt" +LicenseData ${OUTPUTDIR}\COPYING.txt ;-------------------------------- @@ -55,38 +56,38 @@ Section "Synergy (required)" SetOutPath $INSTDIR ; Put files there - File "${DEPTH}\build\synergy.exe" - File "${DEPTH}\build\synergyc.exe" - File "${DEPTH}\build\synergys.exe" - File "${DEPTH}\build\*.dll" - File COPYING.txt - File ChangeLog.txt - File ${DEPTH}\doc\PORTING - File ${DEPTH}\doc\about.html - File ${DEPTH}\doc\authors.html - File ${DEPTH}\doc\autostart.html - File ${DEPTH}\doc\banner.html - File ${DEPTH}\doc\compiling.html - File ${DEPTH}\doc\configuration.html - File ${DEPTH}\doc\contact.html - File ${DEPTH}\doc\developer.html - File ${DEPTH}\doc\faq.html - File ${DEPTH}\doc\history.html - File ${DEPTH}\doc\home.html - File ${DEPTH}\doc\index.html - File ${DEPTH}\doc\license.html - File ${DEPTH}\doc\news.html - File ${DEPTH}\doc\roadmap.html - File ${DEPTH}\doc\running.html - File ${DEPTH}\doc\security.html - File ${DEPTH}\doc\synergy.css - File ${DEPTH}\doc\tips.html - File ${DEPTH}\doc\toc.html - File ${DEPTH}\doc\trouble.html + File "${OUTPUTDIR}\synergy.exe" + File "${OUTPUTDIR}\synergyc.exe" + File "${OUTPUTDIR}\synergys.exe" + File "${OUTPUTDIR}\*.dll" + File "${OUTPUTDIR}\COPYING.txt" + File "${OUTPUTDIR}\ChangeLog.txt" + File doc\PORTING + File doc\about.html + File doc\authors.html + File doc\autostart.html + File doc\banner.html + File doc\compiling.html + File doc\configuration.html + File doc\contact.html + File doc\developer.html + File doc\faq.html + File doc\history.html + File doc\home.html + File doc\index.html + File doc\license.html + File doc\news.html + File doc\roadmap.html + File doc\running.html + File doc\security.html + File doc\synergy.css + File doc\tips.html + File doc\toc.html + File doc\trouble.html SetOutPath $INSTDIR\images - File ${DEPTH}\doc\images\logo.gif - File ${DEPTH}\doc\images\warp.gif + File doc\images\logo.gif + File doc\images\warp.gif ; Write the installation path into the registry WriteRegStr HKLM SOFTWARE\Synergy "Install_Dir" "$INSTDIR" diff --git a/doc/authors.html b/doc/authors.html index bbf3fd10..3fe7b4f1 100644 --- a/doc/authors.html +++ b/doc/authors.html @@ -54,6 +54,13 @@   Re-resolving server hostname on each connection + + Marc-Antoine Ruel +   + maruel@users.sourceforge.no_spam.net +   + Visual Studio 2005 port +

            To avoid spam bots, the above email addresses have ".no_spam" diff --git a/lib/arch/CArchDaemonNone.cpp b/lib/arch/CArchDaemonNone.cpp index f4d25ae1..0281f365 100644 --- a/lib/arch/CArchDaemonNone.cpp +++ b/lib/arch/CArchDaemonNone.cpp @@ -60,7 +60,7 @@ CArchDaemonNone::canInstallDaemon(const char*, bool) } bool -CArchDaemonNone::isDaemonInstalled(const char* name, bool allUsers) +CArchDaemonNone::isDaemonInstalled(const char*, bool) { return false; } diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am index 0e9aa158..049c3146 100644 --- a/lib/arch/Makefile.am +++ b/lib/arch/Makefile.am @@ -89,8 +89,8 @@ WIN32_SOURCE_FILES = \ $(NULL) EXTRA_DIST = \ - arch.dsp \ CMultibyte.cpp \ + nmake.mak \ vsnprintf.cpp \ $(UNIX_SOURCE_FILES) \ $(WIN32_SOURCE_FILES) \ diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp deleted file mode 100644 index 37c9bcd8..00000000 --- a/lib/arch/arch.dsp +++ /dev/null @@ -1,272 +0,0 @@ -# Microsoft Developer Studio Project File - Name="arch" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=arch - Win32 Debug -!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 "arch.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 "arch.mak" CFG="arch - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "arch - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "arch - Win32 Debug" (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)" == "arch - 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 "..\..\gen\build" -# PROP Intermediate_Dir "..\..\gen\build" -# 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 /W4 /GR /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\arch.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "arch - 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 "..\..\gen\debug" -# PROP Intermediate_Dir "..\..\gen\debug" -# 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 /W4 /Gm /GR /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\arch.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "arch - Win32 Release" -# Name "arch - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CArch.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchConsoleWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchDaemonWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchFileWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchLogWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchMiscWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchMultithreadWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchNetworkWinsock.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchSleepWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchStringWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchSystemWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchTaskBarWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CArchTimeWindows.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMultibyte.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\vsnprintf.cpp -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\XArch.cpp -# End Source File -# Begin Source File - -SOURCE=.\XArchWindows.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CArch.h -# End Source File -# Begin Source File - -SOURCE=.\CArchConsoleWindows.h -# End Source File -# Begin Source File - -SOURCE=.\CArchDaemonWindows.h -# End Source File -# Begin Source File - -SOURCE=.\CArchFileWindows.h -# End Source File -# Begin Source File - -SOURCE=.\CArchLogWindows.h -# End Source File -# Begin Source File - -SOURCE=.\CArchMiscWindows.h -# End Source File -# Begin Source File - -SOURCE=.\CArchMultithreadWindows.h -# End Source File -# Begin Source File - -SOURCE=.\CArchNetworkWinsock.h -# End Source File -# Begin Source File - -SOURCE=.\CArchSleepWindows.h -# End Source File -# Begin Source File - -SOURCE=.\CArchStringWindows.h -# End Source File -# Begin Source File - -SOURCE=.\CArchSystemWindows.h -# End Source File -# Begin Source File - -SOURCE=.\CArchTaskBarWindows.h -# End Source File -# Begin Source File - -SOURCE=.\CArchTimeWindows.h -# End Source File -# Begin Source File - -SOURCE=.\IArchConsole.h -# End Source File -# Begin Source File - -SOURCE=.\IArchDaemon.h -# End Source File -# Begin Source File - -SOURCE=.\IArchFile.h -# End Source File -# Begin Source File - -SOURCE=.\IArchLog.h -# End Source File -# Begin Source File - -SOURCE=.\IArchMultithread.h -# End Source File -# Begin Source File - -SOURCE=.\IArchNetwork.h -# End Source File -# Begin Source File - -SOURCE=.\IArchSleep.h -# End Source File -# Begin Source File - -SOURCE=.\IArchString.h -# End Source File -# Begin Source File - -SOURCE=.\IArchSystem.h -# End Source File -# Begin Source File - -SOURCE=.\IArchTaskBar.h -# End Source File -# Begin Source File - -SOURCE=.\IArchTaskBarReceiver.h -# End Source File -# Begin Source File - -SOURCE=.\IArchTime.h -# End Source File -# Begin Source File - -SOURCE=.\XArch.h -# End Source File -# Begin Source File - -SOURCE=.\XArchWindows.h -# End Source File -# End Group -# End Target -# End Project diff --git a/lib/arch/nmake.mak b/lib/arch/nmake.mak new file mode 100644 index 00000000..4e151976 --- /dev/null +++ b/lib/arch/nmake.mak @@ -0,0 +1,84 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +LIB_ARCH_SRC = lib\arch +LIB_ARCH_DST = $(BUILD_DST)\$(LIB_ARCH_SRC) +LIB_ARCH_LIB = "$(LIB_ARCH_DST)\arch.lib" +LIB_ARCH_CPP = \ + "CArch.cpp" \ + "CArchDaemonNone.cpp" \ + "XArch.cpp" \ + "CArchConsoleWindows.cpp" \ + "CArchDaemonWindows.cpp" \ + "CArchFileWindows.cpp" \ + "CArchLogWindows.cpp" \ + "CArchMiscWindows.cpp" \ + "CArchMultithreadWindows.cpp" \ + "CArchNetworkWinsock.cpp" \ + "CArchSleepWindows.cpp" \ + "CArchStringWindows.cpp" \ + "CArchSystemWindows.cpp" \ + "CArchTaskBarWindows.cpp" \ + "CArchTimeWindows.cpp" \ + "XArchWindows.cpp" \ + $(NULL) +LIB_ARCH_OBJ = \ + "$(LIB_ARCH_DST)\CArch.obj" \ + "$(LIB_ARCH_DST)\CArchDaemonNone.obj" \ + "$(LIB_ARCH_DST)\XArch.obj" \ + "$(LIB_ARCH_DST)\CArchConsoleWindows.obj" \ + "$(LIB_ARCH_DST)\CArchDaemonWindows.obj" \ + "$(LIB_ARCH_DST)\CArchFileWindows.obj" \ + "$(LIB_ARCH_DST)\CArchLogWindows.obj" \ + "$(LIB_ARCH_DST)\CArchMiscWindows.obj" \ + "$(LIB_ARCH_DST)\CArchMultithreadWindows.obj" \ + "$(LIB_ARCH_DST)\CArchNetworkWinsock.obj" \ + "$(LIB_ARCH_DST)\CArchSleepWindows.obj" \ + "$(LIB_ARCH_DST)\CArchStringWindows.obj" \ + "$(LIB_ARCH_DST)\CArchSystemWindows.obj" \ + "$(LIB_ARCH_DST)\CArchTaskBarWindows.obj" \ + "$(LIB_ARCH_DST)\CArchTimeWindows.obj" \ + "$(LIB_ARCH_DST)\XArchWindows.obj" \ + $(NULL) +LIB_ARCH_INC = \ + /I"lib\common" \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(LIB_ARCH_CPP) +OBJ_FILES = $(OBJ_FILES) $(LIB_ARCH_OBJ) +LIB_FILES = $(LIB_FILES) $(LIB_ARCH_LIB) + +# Dependency rules +$(LIB_ARCH_OBJ): $(AUTODEP) +!if EXIST($(LIB_ARCH_DST)\deps.mak) +!include $(LIB_ARCH_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(LIB_ARCH_SRC)\}.cpp{$(LIB_ARCH_DST)\}.obj:: +!else +{$(LIB_ARCH_SRC)\}.cpp{$(LIB_ARCH_DST)\}.obj: +!endif + @$(ECHO) Compile in $(LIB_ARCH_SRC) + -@$(MKDIR) $(LIB_ARCH_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(LIB_ARCH_INC) \ + /Fo$(LIB_ARCH_DST)\ \ + /Fd$(LIB_ARCH_LIB:.lib=.pdb) \ + $< | $(AUTODEP) $(LIB_ARCH_SRC) $(LIB_ARCH_DST) +$(LIB_ARCH_LIB): $(LIB_ARCH_OBJ) + @$(ECHO) Link $(@F) + $(implib) $(ildebug) $(ilflags) \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_ARCH_SRC) $(LIB_ARCH_DST) $(**:.obj=.d) diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index 87f48162..4fec44d8 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - base.dsp \ + nmake.mak \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/base/base.dsp b/lib/base/base.dsp deleted file mode 100644 index 542cfe8d..00000000 --- a/lib/base/base.dsp +++ /dev/null @@ -1,222 +0,0 @@ -# Microsoft Developer Studio Project File - Name="base" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=base - Win32 Debug -!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 "base.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 "base.mak" CFG="base - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "base - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "base - Win32 Debug" (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)" == "base - 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 "..\..\gen\build" -# PROP Intermediate_Dir "..\..\gen\build" -# 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 /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\base.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "base - 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 "..\..\gen\debug" -# PROP Intermediate_Dir "..\..\gen\debug" -# 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 /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\base.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "base - Win32 Release" -# Name "base - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CEvent.cpp -# End Source File -# Begin Source File - -SOURCE=.\CEventQueue.cpp -# End Source File -# Begin Source File - -SOURCE=.\CFunctionEventJob.cpp -# End Source File -# Begin Source File - -SOURCE=.\CFunctionJob.cpp -# End Source File -# Begin Source File - -SOURCE=.\CLog.cpp -# End Source File -# Begin Source File - -SOURCE=.\CSimpleEventQueueBuffer.cpp -# End Source File -# Begin Source File - -SOURCE=.\CStopwatch.cpp -# End Source File -# Begin Source File - -SOURCE=.\CStringUtil.cpp -# End Source File -# Begin Source File - -SOURCE=.\CUnicode.cpp -# End Source File -# Begin Source File - -SOURCE=.\IEventQueue.cpp -# End Source File -# Begin Source File - -SOURCE=.\LogOutputters.cpp -# End Source File -# Begin Source File - -SOURCE=.\XBase.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CEvent.h -# End Source File -# Begin Source File - -SOURCE=.\CEventQueue.h -# End Source File -# Begin Source File - -SOURCE=.\CFunctionEventJob.h -# End Source File -# Begin Source File - -SOURCE=.\CFunctionJob.h -# End Source File -# Begin Source File - -SOURCE=.\CLog.h -# End Source File -# Begin Source File - -SOURCE=.\CPriorityQueue.h -# End Source File -# Begin Source File - -SOURCE=.\CSimpleEventQueueBuffer.h -# End Source File -# Begin Source File - -SOURCE=.\CStopwatch.h -# End Source File -# Begin Source File - -SOURCE=.\CString.h -# End Source File -# Begin Source File - -SOURCE=.\CStringUtil.h -# End Source File -# Begin Source File - -SOURCE=.\CUnicode.h -# End Source File -# Begin Source File - -SOURCE=.\IEventJob.h -# End Source File -# Begin Source File - -SOURCE=.\IEventQueue.h -# End Source File -# Begin Source File - -SOURCE=.\IEventQueueBuffer.h -# End Source File -# Begin Source File - -SOURCE=.\IJob.h -# End Source File -# Begin Source File - -SOURCE=.\ILogOutputter.h -# End Source File -# Begin Source File - -SOURCE=.\LogOutputters.h -# End Source File -# Begin Source File - -SOURCE=.\TMethodEventJob.h -# End Source File -# Begin Source File - -SOURCE=.\TMethodJob.h -# End Source File -# Begin Source File - -SOURCE=.\XBase.h -# End Source File -# End Group -# End Target -# End Project diff --git a/lib/base/nmake.mak b/lib/base/nmake.mak new file mode 100644 index 00000000..a386da17 --- /dev/null +++ b/lib/base/nmake.mak @@ -0,0 +1,77 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +LIB_BASE_SRC = lib\base +LIB_BASE_DST = $(BUILD_DST)\$(LIB_BASE_SRC) +LIB_BASE_LIB = "$(LIB_BASE_DST)\base.lib" +LIB_BASE_CPP = \ + "CEvent.cpp" \ + "CEventQueue.cpp" \ + "CFunctionEventJob.cpp" \ + "CFunctionJob.cpp" \ + "CLog.cpp" \ + "CSimpleEventQueueBuffer.cpp" \ + "CStopwatch.cpp" \ + "CStringUtil.cpp" \ + "CUnicode.cpp" \ + "IEventQueue.cpp" \ + "LogOutputters.cpp" \ + "XBase.cpp" \ + $(NULL) +LIB_BASE_OBJ = \ + "$(LIB_BASE_DST)\CEvent.obj" \ + "$(LIB_BASE_DST)\CEventQueue.obj" \ + "$(LIB_BASE_DST)\CFunctionEventJob.obj" \ + "$(LIB_BASE_DST)\CFunctionJob.obj" \ + "$(LIB_BASE_DST)\CLog.obj" \ + "$(LIB_BASE_DST)\CSimpleEventQueueBuffer.obj" \ + "$(LIB_BASE_DST)\CStopwatch.obj" \ + "$(LIB_BASE_DST)\CStringUtil.obj" \ + "$(LIB_BASE_DST)\CUnicode.obj" \ + "$(LIB_BASE_DST)\IEventQueue.obj" \ + "$(LIB_BASE_DST)\LogOutputters.obj" \ + "$(LIB_BASE_DST)\XBase.obj" \ + $(NULL) +LIB_BASE_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(LIB_BASE_CPP) +OBJ_FILES = $(OBJ_FILES) $(LIB_BASE_OBJ) +LIB_FILES = $(LIB_FILES) $(LIB_BASE_LIB) + +# Dependency rules +$(LIB_BASE_OBJ): $(AUTODEP) +!if EXIST($(LIB_BASE_DST)\deps.mak) +!include $(LIB_BASE_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(LIB_BASE_SRC)\}.cpp{$(LIB_BASE_DST)\}.obj:: +!else +{$(LIB_BASE_SRC)\}.cpp{$(LIB_BASE_DST)\}.obj: +!endif + @$(ECHO) Compile in $(LIB_BASE_SRC) + -@$(MKDIR) $(LIB_BASE_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(LIB_BASE_INC) \ + /Fo$(LIB_BASE_DST)\ \ + /Fd$(LIB_BASE_LIB:.lib=.pdb) \ + $< | $(AUTODEP) $(LIB_BASE_SRC) $(LIB_BASE_DST) +$(LIB_BASE_LIB): $(LIB_BASE_OBJ) + @$(ECHO) Link $(@F) + $(implib) $(ildebug) $(ilflags) \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_BASE_SRC) $(LIB_BASE_DST) $(**:.obj=.d) diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index 67b2b80e..40caacc2 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - client.dsp \ + nmake.mak \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/client/client.dsp b/lib/client/client.dsp deleted file mode 100644 index 6fe46b93..00000000 --- a/lib/client/client.dsp +++ /dev/null @@ -1,114 +0,0 @@ -# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=client - Win32 Debug -!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 "client.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 "client.mak" CFG="client - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "client - Win32 Debug" (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)" == "client - 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 "..\..\gen\build" -# PROP Intermediate_Dir "..\..\gen\build" -# 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 /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\client.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "client - 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 "..\..\gen\debug" -# PROP Intermediate_Dir "..\..\gen\debug" -# 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 /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\client.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "client - Win32 Release" -# Name "client - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CClient.cpp -# End Source File -# Begin Source File - -SOURCE=.\CServerProxy.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CClient.h -# End Source File -# Begin Source File - -SOURCE=.\CServerProxy.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/lib/client/nmake.mak b/lib/client/nmake.mak new file mode 100644 index 00000000..3da2c73c --- /dev/null +++ b/lib/client/nmake.mak @@ -0,0 +1,63 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +LIB_CLIENT_SRC = lib\client +LIB_CLIENT_DST = $(BUILD_DST)\$(LIB_CLIENT_SRC) +LIB_CLIENT_LIB = "$(LIB_CLIENT_DST)\client.lib" +LIB_CLIENT_CPP = \ + "CClient.cpp" \ + "CServerProxy.cpp" \ + $(NULL) +LIB_CLIENT_OBJ = \ + "$(LIB_CLIENT_DST)\CClient.obj" \ + "$(LIB_CLIENT_DST)\CServerProxy.obj" \ + $(NULL) +LIB_CLIENT_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + /I"lib\base" \ + /I"lib\mt" \ + /I"lib\io" \ + /I"lib\net" \ + /I"lib\synergy" \ + /I"lib\platform" \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(LIB_CLIENT_CPP) +OBJ_FILES = $(OBJ_FILES) $(LIB_CLIENT_OBJ) +LIB_FILES = $(LIB_FILES) $(LIB_CLIENT_LIB) + +# Dependency rules +$(LIB_CLIENT_OBJ): $(AUTODEP) +!if EXIST($(LIB_CLIENT_DST)\deps.mak) +!include $(LIB_CLIENT_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(LIB_CLIENT_SRC)\}.cpp{$(LIB_CLIENT_DST)\}.obj:: +!else +{$(LIB_CLIENT_SRC)\}.cpp{$(LIB_CLIENT_DST)\}.obj: +!endif + @$(ECHO) Compile in $(LIB_CLIENT_SRC) + -@$(MKDIR) $(LIB_CLIENT_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(LIB_CLIENT_INC) \ + /Fo$(LIB_CLIENT_DST)\ \ + /Fd$(LIB_CLIENT_LIB:.lib=.pdb) \ + $< | $(AUTODEP) $(LIB_CLIENT_SRC) $(LIB_CLIENT_DST) +$(LIB_CLIENT_LIB): $(LIB_CLIENT_OBJ) + @$(ECHO) Link $(@F) + $(implib) $(ildebug) $(ilflags) \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_CLIENT_SRC) $(LIB_CLIENT_DST) $(**:.obj=.d) diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index 4b9e634f..b81d4e42 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - common.dsp \ + nmake.mak \ BasicTypes.h \ IInterface.h \ MacOSXPrecomp.h \ diff --git a/lib/common/common.dsp b/lib/common/common.dsp deleted file mode 100644 index f4f4b8a2..00000000 --- a/lib/common/common.dsp +++ /dev/null @@ -1,166 +0,0 @@ -# Microsoft Developer Studio Project File - Name="common" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=common - Win32 Debug -!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 "common.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 "common.mak" CFG="common - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "common - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "common - Win32 Debug" (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)" == "common - 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 "..\..\gen\build" -# PROP Intermediate_Dir "..\..\gen\build" -# 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 /W4 /GR /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\common.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "common - 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 "..\..\gen\debug" -# PROP Intermediate_Dir "..\..\gen\debug" -# 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 /W4 /Gm /GR /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\common.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "common - Win32 Release" -# Name "common - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\Version.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\BasicTypes.h -# End Source File -# Begin Source File - -SOURCE=.\common.h -# End Source File -# Begin Source File - -SOURCE=.\IInterface.h -# End Source File -# Begin Source File - -SOURCE=.\stdbitset.h -# End Source File -# Begin Source File - -SOURCE=.\stddeque.h -# End Source File -# Begin Source File - -SOURCE=.\stdfstream.h -# End Source File -# Begin Source File - -SOURCE=.\stdistream.h -# End Source File -# Begin Source File - -SOURCE=.\stdlist.h -# End Source File -# Begin Source File - -SOURCE=.\stdmap.h -# End Source File -# Begin Source File - -SOURCE=.\stdostream.h -# End Source File -# Begin Source File - -SOURCE=.\stdpost.h -# End Source File -# Begin Source File - -SOURCE=.\stdpre.h -# End Source File -# Begin Source File - -SOURCE=.\stdset.h -# End Source File -# Begin Source File - -SOURCE=.\stdsstream.h -# End Source File -# Begin Source File - -SOURCE=.\stdstring.h -# End Source File -# Begin Source File - -SOURCE=.\stdvector.h -# End Source File -# Begin Source File - -SOURCE=.\Version.h -# End Source File -# End Group -# End Target -# End Project diff --git a/lib/common/nmake.mak b/lib/common/nmake.mak new file mode 100644 index 00000000..9b63a046 --- /dev/null +++ b/lib/common/nmake.mak @@ -0,0 +1,53 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +LIB_COMMON_SRC = lib\common +LIB_COMMON_DST = $(BUILD_DST)\$(LIB_COMMON_SRC) +LIB_COMMON_LIB = "$(LIB_COMMON_DST)\common.lib" +LIB_COMMON_CPP = \ + Version.cpp \ + $(NULL) +LIB_COMMON_OBJ = \ + "$(LIB_COMMON_DST)\Version.obj" \ + $(NULL) +LIB_COMMON_INC = \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(LIB_COMMON_CPP) +OBJ_FILES = $(OBJ_FILES) $(LIB_COMMON_OBJ) +LIB_FILES = $(LIB_FILES) $(LIB_COMMON_LIB) + +# Dependency rules +$(LIB_COMMON_OBJ): $(AUTODEP) +!if EXIST($(LIB_COMMON_DST)\deps.mak) +!include $(LIB_COMMON_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(LIB_COMMON_SRC)\}.cpp{$(LIB_COMMON_DST)\}.obj:: +!else +{$(LIB_COMMON_SRC)\}.cpp{$(LIB_COMMON_DST)\}.obj: +!endif + @$(ECHO) Compile in $(LIB_COMMON_SRC) + -@$(MKDIR) $(LIB_COMMON_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(LIB_COMMON_INC) \ + /Fo$(LIB_COMMON_DST)\ \ + /Fd$(LIB_COMMON_LIB:.lib=.pdb) \ + $< | $(AUTODEP) $(LIB_COMMON_SRC) $(LIB_COMMON_DST) +$(LIB_COMMON_LIB): $(LIB_COMMON_OBJ) + @$(ECHO) Link $(@F) + $(implib) $(ildebug) $(ilflags) \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_COMMON_SRC) $(LIB_COMMON_DST) $(**:.obj=.d) diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index d7388187..9c849b73 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - io.dsp \ + nmake.mak \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/io/io.dsp b/lib/io/io.dsp deleted file mode 100644 index 5d117b62..00000000 --- a/lib/io/io.dsp +++ /dev/null @@ -1,130 +0,0 @@ -# Microsoft Developer Studio Project File - Name="io" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=io - Win32 Debug -!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 "io.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 "io.mak" CFG="io - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "io - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "io - Win32 Debug" (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)" == "io - 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 "..\..\gen\build" -# PROP Intermediate_Dir "..\..\gen\build" -# 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 /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\io.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "io - 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 "..\..\gen\debug" -# PROP Intermediate_Dir "..\..\gen\debug" -# 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 /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\io.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "io - Win32 Release" -# Name "io - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CStreamBuffer.cpp -# End Source File -# Begin Source File - -SOURCE=.\CStreamFilter.cpp -# End Source File -# Begin Source File - -SOURCE=.\IStream.cpp -# End Source File -# Begin Source File - -SOURCE=.\XIO.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CStreamBuffer.h -# End Source File -# Begin Source File - -SOURCE=.\CStreamFilter.h -# End Source File -# Begin Source File - -SOURCE=.\IStream.h -# End Source File -# Begin Source File - -SOURCE=.\IStreamFilterFactory.h -# End Source File -# Begin Source File - -SOURCE=.\XIO.h -# End Source File -# End Group -# End Target -# End Project diff --git a/lib/io/nmake.mak b/lib/io/nmake.mak new file mode 100644 index 00000000..765c6909 --- /dev/null +++ b/lib/io/nmake.mak @@ -0,0 +1,63 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +LIB_IO_SRC = lib\io +LIB_IO_DST = $(BUILD_DST)\$(LIB_IO_SRC) +LIB_IO_LIB = "$(LIB_IO_DST)\io.lib" +LIB_IO_CPP = \ + "CStreamBuffer.cpp" \ + "CStreamFilter.cpp" \ + "IStream.cpp" \ + "XIO.cpp" \ + $(NULL) +LIB_IO_OBJ = \ + "$(LIB_IO_DST)\CStreamBuffer.obj" \ + "$(LIB_IO_DST)\CStreamFilter.obj" \ + "$(LIB_IO_DST)\IStream.obj" \ + "$(LIB_IO_DST)\XIO.obj" \ + $(NULL) +LIB_IO_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + /I"lib\base" \ + /I"lib\mt" \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(LIB_IO_CPP) +OBJ_FILES = $(OBJ_FILES) $(LIB_IO_OBJ) +LIB_FILES = $(LIB_FILES) $(LIB_IO_LIB) + +# Dependency rules +$(LIB_IO_OBJ): $(AUTODEP) +!if EXIST($(LIB_IO_DST)\deps.mak) +!include $(LIB_IO_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(LIB_IO_SRC)\}.cpp{$(LIB_IO_DST)\}.obj:: +!else +{$(LIB_IO_SRC)\}.cpp{$(LIB_IO_DST)\}.obj: +!endif + @$(ECHO) Compile in $(LIB_IO_SRC) + -@$(MKDIR) $(LIB_IO_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(LIB_IO_INC) \ + /Fo$(LIB_IO_DST)\ \ + /Fd$(LIB_IO_LIB:.lib=.pdb) \ + $< | $(AUTODEP) $(LIB_IO_SRC) $(LIB_IO_DST) +$(LIB_IO_LIB): $(LIB_IO_OBJ) + @$(ECHO) Link $(@F) + $(implib) $(ildebug) $(ilflags) \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_IO_SRC) $(LIB_IO_DST) $(**:.obj=.d) diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index 553cf6a3..beae3df9 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - mt.dsp \ + nmake.mak \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/mt/mt.dsp b/lib/mt/mt.dsp deleted file mode 100644 index 6d7400ed..00000000 --- a/lib/mt/mt.dsp +++ /dev/null @@ -1,138 +0,0 @@ -# Microsoft Developer Studio Project File - Name="mt" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=mt - Win32 Debug -!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 "mt.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 "mt.mak" CFG="mt - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "mt - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "mt - Win32 Debug" (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)" == "mt - 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 "..\..\gen\build" -# PROP Intermediate_Dir "..\..\gen\build" -# 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 /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\mt.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "mt - 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 "..\..\gen\debug" -# PROP Intermediate_Dir "..\..\gen\debug" -# 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 /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\mt.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "mt - Win32 Release" -# Name "mt - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CCondVar.cpp -# End Source File -# Begin Source File - -SOURCE=.\CLock.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMutex.cpp -# End Source File -# Begin Source File - -SOURCE=.\CThread.cpp -# End Source File -# Begin Source File - -SOURCE=.\XMT.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CCondVar.h -# End Source File -# Begin Source File - -SOURCE=.\CLock.h -# End Source File -# Begin Source File - -SOURCE=.\CMutex.h -# End Source File -# Begin Source File - -SOURCE=.\CThread.h -# End Source File -# Begin Source File - -SOURCE=.\XMT.h -# End Source File -# Begin Source File - -SOURCE=.\XThread.h -# End Source File -# End Group -# End Target -# End Project diff --git a/lib/mt/nmake.mak b/lib/mt/nmake.mak new file mode 100644 index 00000000..4ae79130 --- /dev/null +++ b/lib/mt/nmake.mak @@ -0,0 +1,64 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +LIB_MT_SRC = lib\mt +LIB_MT_DST = $(BUILD_DST)\$(LIB_MT_SRC) +LIB_MT_LIB = "$(LIB_MT_DST)\mt.lib" +LIB_MT_CPP = \ + "CCondVar.cpp" \ + "CLock.cpp" \ + "CMutex.cpp" \ + "CThread.cpp" \ + "XMT.cpp" \ + $(NULL) +LIB_MT_OBJ = \ + "$(LIB_MT_DST)\CCondVar.obj" \ + "$(LIB_MT_DST)\CLock.obj" \ + "$(LIB_MT_DST)\CMutex.obj" \ + "$(LIB_MT_DST)\CThread.obj" \ + "$(LIB_MT_DST)\XMT.obj" \ + $(NULL) +LIB_MT_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + /I"lib\base" \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(LIB_MT_CPP) +OBJ_FILES = $(OBJ_FILES) $(LIB_MT_OBJ) +LIB_FILES = $(LIB_FILES) $(LIB_MT_LIB) + +# Dependency rules +$(LIB_MT_OBJ): $(AUTODEP) +!if EXIST($(LIB_MT_DST)\deps.mak) +!include $(LIB_MT_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(LIB_MT_SRC)\}.cpp{$(LIB_MT_DST)\}.obj:: +!else +{$(LIB_MT_SRC)\}.cpp{$(LIB_MT_DST)\}.obj: +!endif + @$(ECHO) Compile in $(LIB_MT_SRC) + -@$(MKDIR) $(LIB_MT_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(LIB_MT_INC) \ + /Fo$(LIB_MT_DST)\ \ + /Fd$(LIB_MT_LIB:.lib=.pdb) \ + $< | $(AUTODEP) $(LIB_MT_SRC) $(LIB_MT_DST) +$(LIB_MT_LIB): $(LIB_MT_OBJ) + @$(ECHO) Link $(@F) + $(implib) $(ildebug) $(ilflags) \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_MT_SRC) $(LIB_MT_DST) $(**:.obj=.d) diff --git a/lib/net/CSocketMultiplexer.cpp b/lib/net/CSocketMultiplexer.cpp index 742198f9..2082730b 100644 --- a/lib/net/CSocketMultiplexer.cpp +++ b/lib/net/CSocketMultiplexer.cpp @@ -201,7 +201,12 @@ CSocketMultiplexer::serviceThread(void*) int status; try { // check for status - status = ARCH->pollSocket(&pfds[0], pfds.size(), -1); + if (!pfds.empty()) { + status = ARCH->pollSocket(&pfds[0], pfds.size(), -1); + } + else { + status = 0; + } } catch (XArchNetwork& e) { LOG((CLOG_WARN "error in socket multiplexer: %s", e.what().c_str())); diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 8e313176..c44b41ea 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -123,7 +123,7 @@ CTCPSocket::read(void* buffer, UInt32 n) if (n > size) { n = size; } - if (buffer != NULL) { + if (buffer != NULL && n != 0) { memcpy(buffer, m_inputBuffer.peek(n), n); } m_inputBuffer.pop(n); diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index aab44c0d..c3ff4c34 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - net.dsp \ + nmake.mak \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/net/net.dsp b/lib/net/net.dsp deleted file mode 100644 index 9080dbdb..00000000 --- a/lib/net/net.dsp +++ /dev/null @@ -1,178 +0,0 @@ -# Microsoft Developer Studio Project File - Name="net" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=net - Win32 Debug -!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 "net.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 "net.mak" CFG="net - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "net - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "net - Win32 Debug" (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)" == "net - 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 "..\..\gen\build" -# PROP Intermediate_Dir "..\..\gen\build" -# 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 /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\net.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "net - 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 "..\..\gen\debug" -# PROP Intermediate_Dir "..\..\gen\debug" -# 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 /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\net.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "net - Win32 Release" -# Name "net - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CNetworkAddress.cpp -# End Source File -# Begin Source File - -SOURCE=.\CSocketMultiplexer.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTCPListenSocket.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocket.cpp -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocketFactory.cpp -# End Source File -# Begin Source File - -SOURCE=.\IDataSocket.cpp -# End Source File -# Begin Source File - -SOURCE=.\IListenSocket.cpp -# End Source File -# Begin Source File - -SOURCE=.\ISocket.cpp -# End Source File -# Begin Source File - -SOURCE=.\XSocket.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CNetworkAddress.h -# End Source File -# Begin Source File - -SOURCE=.\CSocketMultiplexer.h -# End Source File -# Begin Source File - -SOURCE=.\CTCPListenSocket.h -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocket.h -# End Source File -# Begin Source File - -SOURCE=.\CTCPSocketFactory.h -# End Source File -# Begin Source File - -SOURCE=.\IDataSocket.h -# End Source File -# Begin Source File - -SOURCE=.\IListenSocket.h -# End Source File -# Begin Source File - -SOURCE=.\ISocket.h -# End Source File -# Begin Source File - -SOURCE=.\ISocketFactory.h -# End Source File -# Begin Source File - -SOURCE=.\ISocketMultiplexerJob.h -# End Source File -# Begin Source File - -SOURCE=.\TSocketMultiplexerMethodJob.h -# End Source File -# Begin Source File - -SOURCE=.\XSocket.h -# End Source File -# End Group -# End Target -# End Project diff --git a/lib/net/nmake.mak b/lib/net/nmake.mak new file mode 100644 index 00000000..d4cd357c --- /dev/null +++ b/lib/net/nmake.mak @@ -0,0 +1,74 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +LIB_NET_SRC = lib\net +LIB_NET_DST = $(BUILD_DST)\$(LIB_NET_SRC) +LIB_NET_LIB = "$(LIB_NET_DST)\net.lib" +LIB_NET_CPP = \ + "CNetworkAddress.cpp" \ + "CSocketMultiplexer.cpp" \ + "CTCPListenSocket.cpp" \ + "CTCPSocket.cpp" \ + "CTCPSocketFactory.cpp" \ + "IDataSocket.cpp" \ + "IListenSocket.cpp" \ + "ISocket.cpp" \ + "XSocket.cpp" \ + $(NULL) +LIB_NET_OBJ = \ + "$(LIB_NET_DST)\CNetworkAddress.obj" \ + "$(LIB_NET_DST)\CSocketMultiplexer.obj" \ + "$(LIB_NET_DST)\CTCPListenSocket.obj" \ + "$(LIB_NET_DST)\CTCPSocket.obj" \ + "$(LIB_NET_DST)\CTCPSocketFactory.obj" \ + "$(LIB_NET_DST)\IDataSocket.obj" \ + "$(LIB_NET_DST)\IListenSocket.obj" \ + "$(LIB_NET_DST)\ISocket.obj" \ + "$(LIB_NET_DST)\XSocket.obj" \ + $(NULL) +LIB_NET_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + /I"lib\base" \ + /I"lib\mt" \ + /I"lib\io" \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(LIB_NET_CPP) +OBJ_FILES = $(OBJ_FILES) $(LIB_NET_OBJ) +LIB_FILES = $(LIB_FILES) $(LIB_NET_LIB) + +# Dependency rules +$(LIB_NET_OBJ): $(AUTODEP) +!if EXIST($(LIB_NET_DST)\deps.mak) +!include $(LIB_NET_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(LIB_NET_SRC)\}.cpp{$(LIB_NET_DST)\}.obj:: +!else +{$(LIB_NET_SRC)\}.cpp{$(LIB_NET_DST)\}.obj: +!endif + @$(ECHO) Compile in $(LIB_NET_SRC) + -@$(MKDIR) $(LIB_NET_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(LIB_NET_INC) \ + /Fo$(LIB_NET_DST)\ \ + /Fd$(LIB_NET_LIB:.lib=.pdb) \ + $< | $(AUTODEP) $(LIB_NET_SRC) $(LIB_NET_DST) +$(LIB_NET_LIB): $(LIB_NET_OBJ) + @$(ECHO) Link $(@F) + $(implib) $(ildebug) $(ilflags) \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_NET_SRC) $(LIB_NET_DST) $(**:.obj=.d) diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index 66349ff4..b000b477 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -1500,10 +1500,11 @@ CMSWindowsScreen::fixClipboardViewer() // i'm not sure how that could happen. the m_nextClipboardWindow = NULL // was not in the code that infinite loops and may fix the bug but i // doubt it. - return; +/* ChangeClipboardChain(m_window, m_nextClipboardWindow); m_nextClipboardWindow = NULL; m_nextClipboardWindow = SetClipboardViewer(m_window); +*/ } void diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 852e8bbc..f4deb476 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -15,6 +15,20 @@ #include "CSynergyHook.h" #include "ProtocolTypes.h" #include +#include + +#if _MSC_VER >= 1400 +// VS2005 hack - we don't use assert here because we don't want to link with the CRT. +#undef assert +#if _DEBUG +#define assert(_X_) if (!(_X_)) __debugbreak() +#else +#define assert(_X_) __noop() +#endif +// VS2005 is a bit more smart than VC6 and optimize simple copy loop to +// intrinsic memcpy. +#pragma function(memcpy) +#endif // // debugging compile flag. when not zero the server doesn't grab @@ -766,6 +780,79 @@ DllMain(HINSTANCE instance, DWORD reason, LPVOID) extern "C" { +// VS2005 hack to not link with the CRT +#if _MSC_VER >= 1400 +BOOL WINAPI _DllMainCRTStartup( + HINSTANCE instance, DWORD reason, LPVOID lpreserved) +{ + return DllMain(instance, reason, lpreserved); +} + +// VS2005 is a bit more bright than VC6 and optimize simple copy loop to +// intrinsic memcpy. +void * __cdecl memcpy(void * _Dst, const void * _Src, size_t _MaxCount) +{ + void * _DstBackup = _Dst; + switch (_MaxCount & 3) { + case 3: + ((char*)_Dst)[0] = ((char*)_Src)[0]; + ++(char*&)_Dst; + ++(char*&)_Src; + --_MaxCount; + case 2: + ((char*)_Dst)[0] = ((char*)_Src)[0]; + ++(char*&)_Dst; + ++(char*&)_Src; + --_MaxCount; + case 1: + ((char*)_Dst)[0] = ((char*)_Src)[0]; + ++(char*&)_Dst; + ++(char*&)_Src; + --_MaxCount; + break; + case 0: + break; + + default: + __assume(0); + break; + } + + // I think it's faster on intel to deference than modify the pointer. + const size_t max = _MaxCount / sizeof(UINT_PTR); + for (size_t i = 0; i < max; ++i) { + ((UINT_PTR*)_Dst)[i] = ((UINT_PTR*)_Src)[i]; + } + + (UINT_PTR*&)_Dst += max; + (UINT_PTR*&)_Src += max; + + switch (_MaxCount & 3) { + case 3: + ((char*)_Dst)[0] = ((char*)_Src)[0]; + ++(char*&)_Dst; + ++(char*&)_Src; + case 2: + ((char*)_Dst)[0] = ((char*)_Src)[0]; + ++(char*&)_Dst; + ++(char*&)_Src; + case 1: + ((char*)_Dst)[0] = ((char*)_Src)[0]; + ++(char*&)_Dst; + ++(char*&)_Src; + break; + case 0: + break; + + default: + __assume(0); + break; + } + + return _DstBackup; +} +#endif + int init(DWORD threadID) { @@ -786,7 +873,7 @@ init(DWORD threadID) // clean up after old process. the system should've already // removed the hooks so we just need to reset our state. - g_hinstance = GetModuleHandle("synrgyhk"); + g_hinstance = GetModuleHandle(_T("synrgyhk")); g_processID = GetCurrentProcessId(); g_wheelSupport = kWheelNone; g_threadID = 0; diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 8c9a10e0..3edf1be9 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -92,9 +92,7 @@ CARBON_SOURCE_FILES = \ $(NULL) EXTRA_DIST = \ - makehook.dsp \ - platform.dsp \ - synrgyhk.dsp \ + nmake.mak \ $(XWINDOWS_SOURCE_FILES) \ $(MSWINDOWS_SOURCE_FILES) \ $(MSWINDOWS_HOOK_SOURCE_FILES) \ diff --git a/lib/platform/makehook.dsp b/lib/platform/makehook.dsp deleted file mode 100644 index c6528080..00000000 --- a/lib/platform/makehook.dsp +++ /dev/null @@ -1,63 +0,0 @@ -# Microsoft Developer Studio Project File - Name="makehook" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Generic Project" 0x010a - -CFG=makehook - Win32 Debug -!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 "makehook.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 "makehook.mak" CFG="makehook - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "makehook - Win32 Release" (based on "Win32 (x86) Generic Project") -!MESSAGE "makehook - Win32 Debug" (based on "Win32 (x86) Generic Project") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -MTL=midl.exe - -!IF "$(CFG)" == "makehook - 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 "..\..\build" -# PROP Intermediate_Dir "..\..\gen\build" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "makehook - 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 "..\..\gen\debug" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "makehook - Win32 Release" -# Name "makehook - Win32 Debug" -# End Target -# End Project diff --git a/lib/platform/nmake.mak b/lib/platform/nmake.mak new file mode 100644 index 00000000..001200d1 --- /dev/null +++ b/lib/platform/nmake.mak @@ -0,0 +1,119 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +LIB_PLATFORM_SRC = lib\platform +LIB_PLATFORM_DST = $(BUILD_DST)\$(LIB_PLATFORM_SRC) +LIB_PLATFORM_LIB = "$(LIB_PLATFORM_DST)\platform.lib" +LIB_PLATFORM_CPP = \ + "CMSWindowsClipboard.cpp" \ + "CMSWindowsClipboardAnyTextConverter.cpp" \ + "CMSWindowsClipboardBitmapConverter.cpp" \ + "CMSWindowsClipboardHTMLConverter.cpp" \ + "CMSWindowsClipboardTextConverter.cpp" \ + "CMSWindowsClipboardUTF16Converter.cpp" \ + "CMSWindowsDesks.cpp" \ + "CMSWindowsEventQueueBuffer.cpp" \ + "CMSWindowsKeyState.cpp" \ + "CMSWindowsScreen.cpp" \ + "CMSWindowsScreenSaver.cpp" \ + "CMSWindowsUtil.cpp" \ + $(NULL) +LIB_PLATFORM_OBJ = \ + "$(LIB_PLATFORM_DST)\CMSWindowsClipboard.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsClipboardAnyTextConverter.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsClipboardBitmapConverter.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsClipboardHTMLConverter.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsClipboardTextConverter.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsClipboardUTF16Converter.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsDesks.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsEventQueueBuffer.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsKeyState.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsScreen.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsScreenSaver.obj" \ + "$(LIB_PLATFORM_DST)\CMSWindowsUtil.obj" \ + $(NULL) +LIB_PLATFORM_HOOK_CPP = \ + "$(LIB_PLATFORM_SRC)\CSynergyHook.cpp" \ + $(NULL) +LIB_PLATFORM_HOOK_OBJ = \ + "$(LIB_PLATFORM_DST)\CSynergyHook.obj" \ + $(NULL) +LIB_PLATFORM_HOOK_DLL = "$(BUILD_DST)\synrgyhk.dll" +LIB_PLATFORM_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + /I"lib\base" \ + /I"lib\mt" \ + /I"lib\io" \ + /I"lib\net" \ + /I"lib\synergy" \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(LIB_PLATFORM_CPP) +OBJ_FILES = $(OBJ_FILES) $(LIB_PLATFORM_OBJ) +LIB_FILES = $(LIB_FILES) $(LIB_PLATFORM_LIB) $(LIB_PLATFORM_HOOK_DLL) + +# Hook should be as small as possible. +cpphookdebug = $(cppdebug:-Ox=-O1) + +# Don't do security checks or run time error checking on hook. +cpphookflags = $(cppflags:-GS=) +cpphookdebug = $(cpphookdebug:/GZ=) +cpphookdebug = $(cpphookdebug:/RTC1=) + +# Dependency rules +$(LIB_PLATFORM_OBJ): $(AUTODEP) +!if EXIST($(LIB_PLATFORM_DST)\deps.mak) +!include $(LIB_PLATFORM_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(LIB_PLATFORM_SRC)\}.cpp{$(LIB_PLATFORM_DST)\}.obj:: +!else +{$(LIB_PLATFORM_SRC)\}.cpp{$(LIB_PLATFORM_DST)\}.obj: +!endif + @$(ECHO) Compile in $(LIB_PLATFORM_SRC) + -@$(MKDIR) $(LIB_PLATFORM_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(LIB_PLATFORM_INC) \ + /Fo$(LIB_PLATFORM_DST)\ \ + /Fd$(LIB_PLATFORM_LIB:.lib=.pdb) \ + $< | $(AUTODEP) $(LIB_PLATFORM_SRC) $(LIB_PLATFORM_DST) +$(LIB_PLATFORM_LIB): $(LIB_PLATFORM_OBJ) + @$(ECHO) Link $(@F) + $(implib) $(ildebug) $(ilflags) \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_PLATFORM_SRC) $(LIB_PLATFORM_DST) \ + $(LIB_PLATFORM_OBJ:.obj=.d) $(LIB_PLATFORM_HOOK_OBJ:.obj=.d) + +# Hook build rules +$(LIB_PLATFORM_HOOK_OBJ): \ + $(LIB_PLATFORM_HOOK_CPP) $(LIB_PLATFORM_HOOK_CPP:.cpp=.h) + @$(ECHO) Compile $(LIB_PLATFORM_HOOK_CPP) + -@$(MKDIR) $(LIB_PLATFORM_DST) 2>NUL: + $(cpp) $(cpphookdebug) $(cpphookflags) $(cppvarsmt) /showIncludes \ + -D_DLL -D_USRDLL -DSYNRGYHK_EXPORTS \ + $(LIB_PLATFORM_INC) \ + /Fo$(LIB_PLATFORM_DST)\ \ + /Fd$(@:.obj=.pdb) \ + $(LIB_PLATFORM_HOOK_CPP) | \ + $(AUTODEP) $(LIB_PLATFORM_SRC) $(LIB_PLATFORM_DST) +$(LIB_PLATFORM_HOOK_DLL): $(LIB_PLATFORM_HOOK_OBJ) + @$(ECHO) Link $(@F) + $(link) $(ldebug) $(lflags) $(guilibsmt) \ + /entry:"DllMain$(DLLENTRY)" /dll \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_PLATFORM_SRC) $(LIB_PLATFORM_DST) \ + $(LIB_PLATFORM_OBJ:.obj=.d) $(LIB_PLATFORM_HOOK_OBJ:.obj=.d) diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp deleted file mode 100644 index 1333f52d..00000000 --- a/lib/platform/platform.dsp +++ /dev/null @@ -1,190 +0,0 @@ -# Microsoft Developer Studio Project File - Name="platform" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=platform - Win32 Debug -!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 "platform.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 "platform.mak" CFG="platform - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "platform - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "platform - Win32 Debug" (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)" == "platform - 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 "..\..\gen\build" -# PROP Intermediate_Dir "..\..\gen\build" -# 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 /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\platform.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "platform - 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 "..\..\gen\debug" -# PROP Intermediate_Dir "..\..\gen\debug" -# 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 /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\platform.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "platform - Win32 Release" -# Name "platform - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CMSWindowsClipboard.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardAnyTextConverter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardBitmapConverter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardHTMLConverter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardTextConverter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsDesks.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsEventQueueBuffer.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsKeyState.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreenSaver.cpp -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsUtil.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CMSWindowsClipboard.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardAnyTextConverter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardBitmapConverter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardHTMLConverter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardTextConverter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsClipboardUTF16Converter.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsDesks.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsEventQueueBuffer.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsKeyState.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsScreenSaver.h -# End Source File -# Begin Source File - -SOURCE=.\CMSWindowsUtil.h -# End Source File -# End Group -# End Target -# End Project diff --git a/lib/platform/synrgyhk.dsp b/lib/platform/synrgyhk.dsp deleted file mode 100644 index df014508..00000000 --- a/lib/platform/synrgyhk.dsp +++ /dev/null @@ -1,116 +0,0 @@ -# Microsoft Developer Studio Project File - Name="synrgyhk" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=synrgyhk - Win32 Debug -!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 "synrgyhk.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 "synrgyhk.mak" CFG="synrgyhk - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "synrgyhk - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "synrgyhk - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "synrgyhk - 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 "..\..\build" -# PROP Intermediate_Dir "..\..\gen\build" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GR /GX /O1 /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /Fd"..\..\gen\build\synrgyhk.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"DllMain" /dll /machine:I386 /nodefaultlib -# SUBTRACT LINK32 /verbose - -!ELSEIF "$(CFG)" == "synrgyhk - 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 "..\..\gen\debug" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\net" /I "..\synergy" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SYNRGYHK_EXPORTS" /Fd"..\..\gen\debug\synrgyhk.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# SUBTRACT LINK32 /nodefaultlib - -!ENDIF - -# Begin Target - -# Name "synrgyhk - Win32 Release" -# Name "synrgyhk - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CSynergyHook.cpp -# ADD CPP /D _WIN32_WINNT=0x0400 -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CSynergyHook.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index ec56c582..d435aca8 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - server.dsp \ + nmake.mak \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/server/nmake.mak b/lib/server/nmake.mak new file mode 100644 index 00000000..ed059e51 --- /dev/null +++ b/lib/server/nmake.mak @@ -0,0 +1,83 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +LIB_SERVER_SRC = lib\server +LIB_SERVER_DST = $(BUILD_DST)\$(LIB_SERVER_SRC) +LIB_SERVER_LIB = "$(LIB_SERVER_DST)\server.lib" +LIB_SERVER_CPP = \ + "CBaseClientProxy.cpp" \ + "CClientListener.cpp" \ + "CClientProxy.cpp" \ + "CClientProxy1_0.cpp" \ + "CClientProxy1_1.cpp" \ + "CClientProxy1_2.cpp" \ + "CClientProxy1_3.cpp" \ + "CClientProxyUnknown.cpp" \ + "CConfig.cpp" \ + "CInputFilter.cpp" \ + "CPrimaryClient.cpp" \ + "CServer.cpp" \ + $(NULL) +LIB_SERVER_OBJ = \ + "$(LIB_SERVER_DST)\CBaseClientProxy.obj" \ + "$(LIB_SERVER_DST)\CClientListener.obj" \ + "$(LIB_SERVER_DST)\CClientProxy.obj" \ + "$(LIB_SERVER_DST)\CClientProxy1_0.obj" \ + "$(LIB_SERVER_DST)\CClientProxy1_1.obj" \ + "$(LIB_SERVER_DST)\CClientProxy1_2.obj" \ + "$(LIB_SERVER_DST)\CClientProxy1_3.obj" \ + "$(LIB_SERVER_DST)\CClientProxyUnknown.obj" \ + "$(LIB_SERVER_DST)\CConfig.obj" \ + "$(LIB_SERVER_DST)\CInputFilter.obj" \ + "$(LIB_SERVER_DST)\CPrimaryClient.obj" \ + "$(LIB_SERVER_DST)\CServer.obj" \ + $(NULL) +LIB_SERVER_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + /I"lib\base" \ + /I"lib\mt" \ + /I"lib\io" \ + /I"lib\net" \ + /I"lib\synergy" \ + /I"lib\platform" \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(LIB_SERVER_CPP) +OBJ_FILES = $(OBJ_FILES) $(LIB_SERVER_OBJ) +LIB_FILES = $(LIB_FILES) $(LIB_SERVER_LIB) + +# Dependency rules +$(LIB_SERVER_OBJ): $(AUTODEP) +!if EXIST($(LIB_SERVER_DST)\deps.mak) +!include $(LIB_SERVER_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(LIB_SERVER_SRC)\}.cpp{$(LIB_SERVER_DST)\}.obj:: +!else +{$(LIB_SERVER_SRC)\}.cpp{$(LIB_SERVER_DST)\}.obj: +!endif + @$(ECHO) Compile in $(LIB_SERVER_SRC) + -@$(MKDIR) $(LIB_SERVER_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(LIB_SERVER_INC) \ + /Fo$(LIB_SERVER_DST)\ \ + /Fd$(LIB_SERVER_LIB:.lib=.pdb) \ + $< | $(AUTODEP) $(LIB_SERVER_SRC) $(LIB_SERVER_DST) +$(LIB_SERVER_LIB): $(LIB_SERVER_OBJ) + @$(ECHO) Link $(@F) + $(implib) $(ildebug) $(ilflags) \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_SERVER_SRC) $(LIB_SERVER_DST) $(**:.obj=.d) diff --git a/lib/server/server.dsp b/lib/server/server.dsp deleted file mode 100644 index 72268967..00000000 --- a/lib/server/server.dsp +++ /dev/null @@ -1,194 +0,0 @@ -# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=server - Win32 Debug -!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 "server.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 "server.mak" CFG="server - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "server - Win32 Debug" (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)" == "server - 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 "..\..\gen\build" -# PROP Intermediate_Dir "..\..\gen\build" -# 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 /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\server.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "server - 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 "..\..\gen\debug" -# PROP Intermediate_Dir "..\..\gen\debug" -# 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 /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\mt" /I "..\io" /I "..\net" /I "..\synergy" /I "..\platform" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\server.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "server - Win32 Release" -# Name "server - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CBaseClientProxy.cpp -# End Source File -# Begin Source File - -SOURCE=.\CClientListener.cpp -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy.cpp -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_0.cpp -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_1.cpp -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_2.cpp -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_3.cpp -# End Source File -# Begin Source File - -SOURCE=.\CClientProxyUnknown.cpp -# End Source File -# Begin Source File - -SOURCE=.\CConfig.cpp -# End Source File -# Begin Source File - -SOURCE=.\CInputFilter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryClient.cpp -# End Source File -# Begin Source File - -SOURCE=.\CServer.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CBaseClientProxy.h -# End Source File -# Begin Source File - -SOURCE=.\CClientListener.h -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy.h -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_0.h -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_1.h -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_2.h -# End Source File -# Begin Source File - -SOURCE=.\CClientProxy1_3.h -# End Source File -# Begin Source File - -SOURCE=.\CClientProxyUnknown.h -# End Source File -# Begin Source File - -SOURCE=.\CConfig.h -# End Source File -# Begin Source File - -SOURCE=.\CInputFilter.h -# End Source File -# Begin Source File - -SOURCE=.\CPrimaryClient.h -# End Source File -# Begin Source File - -SOURCE=.\CServer.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 0b481eef..03979e64 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - libsynergy.dsp \ + nmake.mak \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/synergy/libsynergy.dsp b/lib/synergy/libsynergy.dsp deleted file mode 100644 index 33300e0c..00000000 --- a/lib/synergy/libsynergy.dsp +++ /dev/null @@ -1,246 +0,0 @@ -# Microsoft Developer Studio Project File - Name="libsynergy" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=libsynergy - Win32 Release -!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 "libsynergy.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 "libsynergy.mak" CFG="libsynergy - Win32 Release" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "libsynergy - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "libsynergy - Win32 Debug" (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)" == "libsynergy - 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 "..\..\gen\build" -# PROP Intermediate_Dir "..\..\gen\build" -# 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 /W4 /GR /GX /O2 /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\libsynergy.pdb" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /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)" == "libsynergy - 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 "..\..\gen\debug" -# PROP Intermediate_Dir "..\..\gen\debug" -# 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 /W4 /Gm /GR /GX /ZI /Od /I "..\common" /I "..\arch" /I "..\base" /I "..\io" /I "..\mt" /I "..\net" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\libsynergy.pdb" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /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 "libsynergy - Win32 Release" -# Name "libsynergy - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\CClipboard.cpp -# End Source File -# Begin Source File - -SOURCE=.\CKeyMap.cpp -# End Source File -# Begin Source File - -SOURCE=.\CKeyState.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPacketStreamFilter.cpp -# End Source File -# Begin Source File - -SOURCE=.\CPlatformScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\CProtocolUtil.cpp -# End Source File -# Begin Source File - -SOURCE=.\CScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\IClipboard.cpp -# End Source File -# Begin Source File - -SOURCE=.\IKeyState.cpp -# End Source File -# Begin Source File - -SOURCE=.\IPrimaryScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\IScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\KeyTypes.cpp -# End Source File -# Begin Source File - -SOURCE=.\ProtocolTypes.cpp -# End Source File -# Begin Source File - -SOURCE=.\XScreen.cpp -# End Source File -# Begin Source File - -SOURCE=.\XSynergy.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\CClipboard.h -# End Source File -# Begin Source File - -SOURCE=.\CKeyMap.h -# End Source File -# Begin Source File - -SOURCE=.\CKeyState.h -# End Source File -# Begin Source File - -SOURCE=.\ClipboardTypes.h -# End Source File -# Begin Source File - -SOURCE=.\CPacketStreamFilter.h -# End Source File -# Begin Source File - -SOURCE=.\CPlatformScreen.h -# End Source File -# Begin Source File - -SOURCE=.\CProtocolUtil.h -# End Source File -# Begin Source File - -SOURCE=.\CScreen.h -# End Source File -# Begin Source File - -SOURCE=.\IClient.h -# End Source File -# Begin Source File - -SOURCE=.\IClipboard.h -# End Source File -# Begin Source File - -SOURCE=.\IKeyState.h -# End Source File -# Begin Source File - -SOURCE=.\IPlatformScreen.h -# End Source File -# Begin Source File - -SOURCE=.\IPrimaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\IScreen.h -# End Source File -# Begin Source File - -SOURCE=.\IScreenSaver.h -# End Source File -# Begin Source File - -SOURCE=.\ISecondaryScreen.h -# End Source File -# Begin Source File - -SOURCE=.\KeyTypes.h -# End Source File -# Begin Source File - -SOURCE=.\MouseTypes.h -# End Source File -# Begin Source File - -SOURCE=.\OptionTypes.h -# End Source File -# Begin Source File - -SOURCE=.\ProtocolTypes.h -# End Source File -# Begin Source File - -SOURCE=.\XScreen.h -# End Source File -# Begin Source File - -SOURCE=.\XSynergy.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/lib/synergy/nmake.mak b/lib/synergy/nmake.mak new file mode 100644 index 00000000..e6d56d42 --- /dev/null +++ b/lib/synergy/nmake.mak @@ -0,0 +1,87 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +LIB_SYNERGY_SRC = lib\synergy +LIB_SYNERGY_DST = $(BUILD_DST)\$(LIB_SYNERGY_SRC) +LIB_SYNERGY_LIB = "$(LIB_SYNERGY_DST)\libsynergy.lib" +LIB_SYNERGY_CPP = \ + "CClipboard.cpp" \ + "CKeyMap.cpp" \ + "CKeyState.cpp" \ + "CPacketStreamFilter.cpp" \ + "CPlatformScreen.cpp" \ + "CProtocolUtil.cpp" \ + "CScreen.cpp" \ + "IClipboard.cpp" \ + "IKeyState.cpp" \ + "IPrimaryScreen.cpp" \ + "IScreen.cpp" \ + "KeyTypes.cpp" \ + "ProtocolTypes.cpp" \ + "XScreen.cpp" \ + "XSynergy.cpp" \ + $(NULL) +LIB_SYNERGY_OBJ = \ + "$(LIB_SYNERGY_DST)\CClipboard.obj" \ + "$(LIB_SYNERGY_DST)\CKeyMap.obj" \ + "$(LIB_SYNERGY_DST)\CKeyState.obj" \ + "$(LIB_SYNERGY_DST)\CPacketStreamFilter.obj" \ + "$(LIB_SYNERGY_DST)\CPlatformScreen.obj" \ + "$(LIB_SYNERGY_DST)\CProtocolUtil.obj" \ + "$(LIB_SYNERGY_DST)\CScreen.obj" \ + "$(LIB_SYNERGY_DST)\IClipboard.obj" \ + "$(LIB_SYNERGY_DST)\IKeyState.obj" \ + "$(LIB_SYNERGY_DST)\IPrimaryScreen.obj" \ + "$(LIB_SYNERGY_DST)\IScreen.obj" \ + "$(LIB_SYNERGY_DST)\KeyTypes.obj" \ + "$(LIB_SYNERGY_DST)\ProtocolTypes.obj" \ + "$(LIB_SYNERGY_DST)\XScreen.obj" \ + "$(LIB_SYNERGY_DST)\XSynergy.obj" \ + $(NULL) +LIB_SYNERGY_INC = \ + /I"lib\common" \ + /I"lib\arch" \ + /I"lib\base" \ + /I"lib\mt" \ + /I"lib\io" \ + /I"lib\net" \ + $(NULL) + +CPP_FILES = $(CPP_FILES) $(LIB_SYNERGY_CPP) +OBJ_FILES = $(OBJ_FILES) $(LIB_SYNERGY_OBJ) +LIB_FILES = $(LIB_FILES) $(LIB_SYNERGY_LIB) + +# Dependency rules +$(LIB_SYNERGY_OBJ): $(AUTODEP) +!if EXIST($(LIB_SYNERGY_DST)\deps.mak) +!include $(LIB_SYNERGY_DST)\deps.mak +!endif + +# Build rules. Use batch-mode rules if possible. +!if DEFINED(_NMAKE_VER) +{$(LIB_SYNERGY_SRC)\}.cpp{$(LIB_SYNERGY_DST)\}.obj:: +!else +{$(LIB_SYNERGY_SRC)\}.cpp{$(LIB_SYNERGY_DST)\}.obj: +!endif + @$(ECHO) Compile in $(LIB_SYNERGY_SRC) + -@$(MKDIR) $(LIB_SYNERGY_DST) 2>NUL: + $(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \ + $(LIB_SYNERGY_INC) \ + /Fo$(LIB_SYNERGY_DST)\ \ + /Fd$(LIB_SYNERGY_LIB:.lib=.pdb) \ + $< | $(AUTODEP) $(LIB_SYNERGY_SRC) $(LIB_SYNERGY_DST) +$(LIB_SYNERGY_LIB): $(LIB_SYNERGY_OBJ) + @$(ECHO) Link $(@F) + $(implib) $(ildebug) $(ilflags) \ + /out:$@ \ + $** + $(AUTODEP) $(LIB_SYNERGY_SRC) $(LIB_SYNERGY_DST) $(**:.obj=.d) diff --git a/nmake.mak b/nmake.mak new file mode 100644 index 00000000..27a6e1cb --- /dev/null +++ b/nmake.mak @@ -0,0 +1,145 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2007 Chris Schoeneman +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file COPYING that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# Name of this file for recursive make +MAKEFILE = nmake.mak + +# Default build is release is NODEBUG is defined, debug otherwise. +!if !DEFINED(DEBUG) +NODEBUG = 1 +!else +!undef NODEBUG +!endif + +# Build all by default +default: all + +# Redefine implicit rule suffixes +.SUFFIXES: +.SUFFIXES: .cpp .rc .obj + +# Shut up +.SILENT: + +# Include system macros +#APPVER = 5.0 +#TARGETOS = WINNT +!include + +# Be explicit about C++ compiler +cpp = $(cc) +cppdebug = $(cdebug) +cppflags = $(cflags) +cppvarsmt = $(cvarsmt) + +# Library tool options +ildebug = +ilflags = /nologo + +# Handy macro for defining list macros +NULL = + +# System commands +ECHO = echo +MKDIR = mkdir +RM = del /f +RMR = rmdir /q /s + +# Local build utilities +UTIL_DIR = win32util +AUTODEP = "$(UTIL_DIR)\autodep.exe" + +# Destination for intermediate build targets +BUILD_DIR = build +BUILD_DEBUG_DIR = $(BUILD_DIR)\Debug +BUILD_RELEASE_DIR = $(BUILD_DIR)\Release +!if DEFINED(NODEBUG) +BUILD_DST = $(BUILD_RELEASE_DIR) +!else +BUILD_DST = $(BUILD_DEBUG_DIR) +!endif + +# Compiler argument changes +cflags = $(cflags:-W3=-W4) /WX +cflags = $(cflags) -D_CRT_SECURE_NO_DEPRECATE +cflags = $(cflags) /GR +!if !DEFINED(OLDCOMPILER) +cflags = $(cflags) /EHsc +!else +cflags = $(cflags) /GX +!endif +!if !DEFINED(NODEBUG) +!if !DEFINED(OLDCOMPILER) +cdebug = $(cdebug) /RTC1 +!else +cdebug = $(cdebug) /GZ +!endif +!endif + +# Initialize variables for library and program makefiles +C_FILES = +CPP_FILES = +OBJ_FILES = +LIB_FILES = +PROGRAMS = +OPTPROGRAMS = $(AUTODEP) + +# Include subdirectory makefiles +!include lib\common\$(MAKEFILE) +!include lib\arch\$(MAKEFILE) +!include lib\base\$(MAKEFILE) +!include lib\mt\$(MAKEFILE) +!include lib\io\$(MAKEFILE) +!include lib\net\$(MAKEFILE) +!include lib\synergy\$(MAKEFILE) +!include lib\platform\$(MAKEFILE) +!include lib\client\$(MAKEFILE) +!include lib\server\$(MAKEFILE) +!include cmd\synergyc\$(MAKEFILE) +!include cmd\synergys\$(MAKEFILE) +!include cmd\launcher\$(MAKEFILE) +!include dist\nullsoft\$(MAKEFILE) + +# Collect library and program variables +INTERMEDIATES = $(OBJ_FILES) $(AUTODEP:.exe=.obj) +TARGETS = $(LIB_FILES) $(PROGRAMS) +OPTTARGETS = $(OPTPROGRAMS) + +# Build release by reinvoking make with NODEBUG defined +release: + @$(MAKE) /nologo /f $(MAKEFILE) NODEBUG=1 + +# Build debug by reinvoking make with DEBUG defined +debug: + @$(MAKE) /nologo /f $(MAKEFILE) DEBUG=1 + +# Build all targets +all: $(TARGETS) + +# Clean intermediate targets +clean: + -$(RMR) $(BUILD_DEBUG_DIR) + -$(RMR) $(BUILD_RELEASE_DIR) + +# Clean all targets +clobber: clean + -$(RMR) $(BUILD_DIR) + +# Utility command build rules +$(AUTODEP): $(AUTODEP:.exe=.cpp) +!if DEFINED(NODEBUG) + @$(ECHO) Build $(@F) + $(cpp) $(cppdebug) $(cppflags) $(cppvars) /Fo"$(**:.cpp=.obj)" $** + $(link) $(ldebug) $(conflags) -out:$@ $(**:.cpp=.obj) $(conlibs) +!else + @$(MAKE) /nologo /f $(MAKEFILE) NODEBUG=1 $@ +!endif diff --git a/synergy.dsw b/synergy.dsw deleted file mode 100644 index 455df6a7..00000000 --- a/synergy.dsw +++ /dev/null @@ -1,332 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "all"=".\all.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name launcher - End Project Dependency -}}} - -############################################################################### - -Project: "arch"=".\lib\arch\arch.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "base"=".\lib\base\base.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "client"=".\lib\client\client.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "common"=".\lib\common\common.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "exec"=".\cmd\exec.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name synergyc - End Project Dependency - Begin Project Dependency - Project_Dep_Name synergys - End Project Dependency -}}} - -############################################################################### - -Project: "installer"=".\DIST\NULLSOFT\installer.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "io"=".\lib\io\io.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "launcher"=".\cmd\launcher\launcher.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name common - End Project Dependency - Begin Project Dependency - Project_Dep_Name arch - End Project Dependency - Begin Project Dependency - Project_Dep_Name base - End Project Dependency - Begin Project Dependency - Project_Dep_Name exec - End Project Dependency - Begin Project Dependency - Project_Dep_Name io - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsynergy - End Project Dependency - Begin Project Dependency - Project_Dep_Name mt - End Project Dependency - Begin Project Dependency - Project_Dep_Name net - End Project Dependency - Begin Project Dependency - Project_Dep_Name platform - End Project Dependency - Begin Project Dependency - Project_Dep_Name server - End Project Dependency -}}} - -############################################################################### - -Project: "libsynergy"=".\lib\synergy\libsynergy.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "makehook"=".\lib\platform\makehook.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name synrgyhk - End Project Dependency -}}} - -############################################################################### - -Project: "mt"=".\lib\mt\mt.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "net"=".\lib\net\net.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "platform"=".\lib\platform\platform.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "server"=".\lib\server\server.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "synergyc"=".\cmd\synergyc\synergyc.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name common - End Project Dependency - Begin Project Dependency - Project_Dep_Name arch - End Project Dependency - Begin Project Dependency - Project_Dep_Name base - End Project Dependency - Begin Project Dependency - Project_Dep_Name io - End Project Dependency - Begin Project Dependency - Project_Dep_Name mt - End Project Dependency - Begin Project Dependency - Project_Dep_Name net - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsynergy - End Project Dependency - Begin Project Dependency - Project_Dep_Name platform - End Project Dependency - Begin Project Dependency - Project_Dep_Name client - End Project Dependency -}}} - -############################################################################### - -Project: "synergys"=".\cmd\synergys\synergys.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name common - End Project Dependency - Begin Project Dependency - Project_Dep_Name arch - End Project Dependency - Begin Project Dependency - Project_Dep_Name base - End Project Dependency - Begin Project Dependency - Project_Dep_Name io - End Project Dependency - Begin Project Dependency - Project_Dep_Name mt - End Project Dependency - Begin Project Dependency - Project_Dep_Name net - End Project Dependency - Begin Project Dependency - Project_Dep_Name libsynergy - End Project Dependency - Begin Project Dependency - Project_Dep_Name platform - End Project Dependency - Begin Project Dependency - Project_Dep_Name makehook - End Project Dependency - Begin Project Dependency - Project_Dep_Name server - End Project Dependency -}}} - -############################################################################### - -Project: "synrgyhk"=".\lib\platform\synrgyhk.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/synergy.xcode/project.pbxproj b/synergy.xcode/project.pbxproj deleted file mode 100644 index 75cd3385..00000000 --- a/synergy.xcode/project.pbxproj +++ /dev/null @@ -1,2882 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 42; - objects = { - -/* Begin PBXAggregateTarget section */ - 4CD033E80650965F00525ED1 /* ALL */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 68FBBB18089F06C20016DF44 /* Build configuration list for PBXAggregateTarget "ALL" */; - buildPhases = ( - ); - buildSettings = { - PRODUCT_NAME = ALL; - }; - dependencies = ( - 4CD033EA0650968500525ED1 /* PBXTargetDependency */, - 4CD033EC0650968500525ED1 /* PBXTargetDependency */, - ); - name = ALL; - productName = ALL; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 4C537F14064E293000D3815C /* COSXServerTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F0F064E293000D3815C /* COSXServerTaskBarReceiver.cpp */; }; - 4C537F15064E293000D3815C /* COSXServerTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F10064E293000D3815C /* COSXServerTaskBarReceiver.h */; }; - 4C537F16064E293000D3815C /* CServerTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F11064E293000D3815C /* CServerTaskBarReceiver.cpp */; }; - 4C537F17064E293000D3815C /* CServerTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F12064E293000D3815C /* CServerTaskBarReceiver.h */; }; - 4C537F18064E293000D3815C /* synergys.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F13064E293000D3815C /* synergys.cpp */; }; - 4C537F44064E2A0F00D3815C /* CClientListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F32064E2A0F00D3815C /* CClientListener.cpp */; }; - 4C537F45064E2A0F00D3815C /* CClientListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F33064E2A0F00D3815C /* CClientListener.h */; }; - 4C537F46064E2A0F00D3815C /* CClientProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F34064E2A0F00D3815C /* CClientProxy.cpp */; }; - 4C537F47064E2A0F00D3815C /* CClientProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F35064E2A0F00D3815C /* CClientProxy.h */; }; - 4C537F48064E2A0F00D3815C /* CClientProxy1_0.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F36064E2A0F00D3815C /* CClientProxy1_0.cpp */; }; - 4C537F49064E2A0F00D3815C /* CClientProxy1_0.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F37064E2A0F00D3815C /* CClientProxy1_0.h */; }; - 4C537F4A064E2A0F00D3815C /* CClientProxy1_1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F38064E2A0F00D3815C /* CClientProxy1_1.cpp */; }; - 4C537F4B064E2A0F00D3815C /* CClientProxy1_1.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F39064E2A0F00D3815C /* CClientProxy1_1.h */; }; - 4C537F4C064E2A0F00D3815C /* CClientProxy1_2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F3A064E2A0F00D3815C /* CClientProxy1_2.cpp */; }; - 4C537F4D064E2A0F00D3815C /* CClientProxy1_2.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F3B064E2A0F00D3815C /* CClientProxy1_2.h */; }; - 4C537F4E064E2A0F00D3815C /* CClientProxyUnknown.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F3C064E2A0F00D3815C /* CClientProxyUnknown.cpp */; }; - 4C537F4F064E2A0F00D3815C /* CClientProxyUnknown.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F3D064E2A0F00D3815C /* CClientProxyUnknown.h */; }; - 4C537F50064E2A0F00D3815C /* CConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F3E064E2A0F00D3815C /* CConfig.cpp */; }; - 4C537F51064E2A0F00D3815C /* CConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F3F064E2A0F00D3815C /* CConfig.h */; }; - 4C537F52064E2A0F00D3815C /* CPrimaryClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F40064E2A0F00D3815C /* CPrimaryClient.cpp */; }; - 4C537F53064E2A0F00D3815C /* CPrimaryClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F41064E2A0F00D3815C /* CPrimaryClient.h */; }; - 4C537F54064E2A0F00D3815C /* CServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C537F42064E2A0F00D3815C /* CServer.cpp */; }; - 4C537F55064E2A0F00D3815C /* CServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C537F43064E2A0F00D3815C /* CServer.h */; }; - 4C537F5B064E2B4200D3815C /* libcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C5E868D0648C2ED003C637B /* libcommon.a */; }; - 4C537F5C064E2B4200D3815C /* libarch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437B5063E443800969041 /* libarch.a */; }; - 4C537F5D064E2B4200D3815C /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437D1063E44C200969041 /* libbase.a */; }; - 4C537F5E064E2B4200D3815C /* libmt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43801063E45F200969041 /* libmt.a */; }; - 4C537F5F064E2B4200D3815C /* libio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43824063E46AB00969041 /* libio.a */; }; - 4C537F60064E2B4200D3815C /* libnet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43848063E475800969041 /* libnet.a */; }; - 4C537F61064E2B4200D3815C /* libserver.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C537F31064E29F800D3815C /* libserver.a */; }; - 4C537F62064E2B4200D3815C /* libsynergy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB4386E063E47F800969041 /* libsynergy.a */; }; - 4C537F63064E2B4200D3815C /* libplatform.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB438B1063E488600969041 /* libplatform.a */; }; - 4C537F64064E2B4B00D3815C /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43923063E4B1300969041 /* Carbon.framework */; }; - 4C5E86A90648C412003C637B /* CArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86920648C412003C637B /* CArch.cpp */; }; - 4C5E86AA0648C412003C637B /* CArch.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86930648C412003C637B /* CArch.h */; }; - 4C5E86AB0648C412003C637B /* CArchConsoleUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86940648C412003C637B /* CArchConsoleUnix.cpp */; }; - 4C5E86AC0648C412003C637B /* CArchConsoleUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86950648C412003C637B /* CArchConsoleUnix.h */; }; - 4C5E86AD0648C412003C637B /* CArchDaemonUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86960648C412003C637B /* CArchDaemonUnix.cpp */; }; - 4C5E86AE0648C412003C637B /* CArchDaemonUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86970648C412003C637B /* CArchDaemonUnix.h */; }; - 4C5E86AF0648C412003C637B /* CArchFileUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86980648C412003C637B /* CArchFileUnix.cpp */; }; - 4C5E86B00648C412003C637B /* CArchFileUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86990648C412003C637B /* CArchFileUnix.h */; }; - 4C5E86B10648C412003C637B /* CArchLogUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E869A0648C412003C637B /* CArchLogUnix.cpp */; }; - 4C5E86B20648C412003C637B /* CArchLogUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E869B0648C412003C637B /* CArchLogUnix.h */; }; - 4C5E86B30648C412003C637B /* CArchMultithreadPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E869C0648C412003C637B /* CArchMultithreadPosix.cpp */; }; - 4C5E86B40648C412003C637B /* CArchMultithreadPosix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E869D0648C412003C637B /* CArchMultithreadPosix.h */; }; - 4C5E86B50648C412003C637B /* CArchNetworkBSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E869E0648C412003C637B /* CArchNetworkBSD.cpp */; }; - 4C5E86B60648C412003C637B /* CArchNetworkBSD.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E869F0648C412003C637B /* CArchNetworkBSD.h */; }; - 4C5E86B70648C412003C637B /* CArchSleepUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A00648C412003C637B /* CArchSleepUnix.cpp */; }; - 4C5E86B80648C412003C637B /* CArchSleepUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A10648C412003C637B /* CArchSleepUnix.h */; }; - 4C5E86B90648C412003C637B /* CArchStringUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A20648C412003C637B /* CArchStringUnix.cpp */; }; - 4C5E86BA0648C412003C637B /* CArchStringUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A30648C412003C637B /* CArchStringUnix.h */; }; - 4C5E86BB0648C412003C637B /* CArchTimeUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A40648C412003C637B /* CArchTimeUnix.cpp */; }; - 4C5E86BC0648C412003C637B /* CArchTimeUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A50648C412003C637B /* CArchTimeUnix.h */; }; - 4C5E86BE0648C412003C637B /* XArchUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86A70648C412003C637B /* XArchUnix.cpp */; }; - 4C5E86BF0648C412003C637B /* XArchUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86A80648C412003C637B /* XArchUnix.h */; }; - 4C5E86C30648C653003C637B /* CArchDaemonNone.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86C10648C653003C637B /* CArchDaemonNone.h */; }; - 4C5E86CC0648C726003C637B /* Version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86CB0648C725003C637B /* Version.cpp */; }; - 4C5E86ED0648C7B9003C637B /* CEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86CD0648C7B9003C637B /* CEvent.cpp */; }; - 4C5E86EE0648C7B9003C637B /* CEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86CE0648C7B9003C637B /* CEvent.h */; }; - 4C5E86EF0648C7B9003C637B /* CEventQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86CF0648C7B9003C637B /* CEventQueue.cpp */; }; - 4C5E86F00648C7B9003C637B /* CEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D00648C7B9003C637B /* CEventQueue.h */; }; - 4C5E86F10648C7B9003C637B /* CFunctionEventJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D10648C7B9003C637B /* CFunctionEventJob.cpp */; }; - 4C5E86F20648C7B9003C637B /* CFunctionEventJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D20648C7B9003C637B /* CFunctionEventJob.h */; }; - 4C5E86F30648C7B9003C637B /* CFunctionJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D30648C7B9003C637B /* CFunctionJob.cpp */; }; - 4C5E86F40648C7B9003C637B /* CFunctionJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D40648C7B9003C637B /* CFunctionJob.h */; }; - 4C5E86F50648C7B9003C637B /* CLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D50648C7B9003C637B /* CLog.cpp */; }; - 4C5E86F60648C7B9003C637B /* CLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D60648C7B9003C637B /* CLog.h */; }; - 4C5E86F70648C7B9003C637B /* CPriorityQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D70648C7B9003C637B /* CPriorityQueue.h */; }; - 4C5E86F80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86D80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp */; }; - 4C5E86F90648C7B9003C637B /* CSimpleEventQueueBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86D90648C7B9003C637B /* CSimpleEventQueueBuffer.h */; }; - 4C5E86FA0648C7B9003C637B /* CStopwatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86DA0648C7B9003C637B /* CStopwatch.cpp */; }; - 4C5E86FB0648C7B9003C637B /* CStopwatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86DB0648C7B9003C637B /* CStopwatch.h */; }; - 4C5E86FC0648C7B9003C637B /* CString.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86DC0648C7B9003C637B /* CString.h */; }; - 4C5E86FD0648C7B9003C637B /* CStringUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86DD0648C7B9003C637B /* CStringUtil.cpp */; }; - 4C5E86FE0648C7B9003C637B /* CStringUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86DE0648C7B9003C637B /* CStringUtil.h */; }; - 4C5E86FF0648C7B9003C637B /* CUnicode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86DF0648C7B9003C637B /* CUnicode.cpp */; }; - 4C5E87000648C7B9003C637B /* CUnicode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E00648C7B9003C637B /* CUnicode.h */; }; - 4C5E87010648C7B9003C637B /* IEventJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E10648C7B9003C637B /* IEventJob.h */; }; - 4C5E87020648C7B9003C637B /* IEventQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86E20648C7B9003C637B /* IEventQueue.cpp */; }; - 4C5E87030648C7B9003C637B /* IEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E30648C7B9003C637B /* IEventQueue.h */; }; - 4C5E87040648C7B9003C637B /* IEventQueueBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E40648C7B9003C637B /* IEventQueueBuffer.h */; }; - 4C5E87050648C7B9003C637B /* IJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E50648C7B9003C637B /* IJob.h */; }; - 4C5E87060648C7B9003C637B /* ILogOutputter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E60648C7B9003C637B /* ILogOutputter.h */; }; - 4C5E87070648C7B9003C637B /* LogOutputters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86E70648C7B9003C637B /* LogOutputters.cpp */; }; - 4C5E87080648C7B9003C637B /* LogOutputters.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E80648C7B9003C637B /* LogOutputters.h */; }; - 4C5E87090648C7B9003C637B /* TMethodEventJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86E90648C7B9003C637B /* TMethodEventJob.h */; }; - 4C5E870A0648C7B9003C637B /* TMethodJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86EA0648C7B9003C637B /* TMethodJob.h */; }; - 4C5E870B0648C7B9003C637B /* XBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E86EB0648C7B9003C637B /* XBase.cpp */; }; - 4C5E870C0648C7B9003C637B /* XBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E86EC0648C7B9003C637B /* XBase.h */; }; - 4C5E87180648C809003C637B /* CCondVar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E870D0648C809003C637B /* CCondVar.cpp */; }; - 4C5E87190648C809003C637B /* CCondVar.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E870E0648C809003C637B /* CCondVar.h */; }; - 4C5E871A0648C809003C637B /* CLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E870F0648C809003C637B /* CLock.cpp */; }; - 4C5E871B0648C809003C637B /* CLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87100648C809003C637B /* CLock.h */; }; - 4C5E871C0648C809003C637B /* CMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87110648C809003C637B /* CMutex.cpp */; }; - 4C5E871D0648C809003C637B /* CMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87120648C809003C637B /* CMutex.h */; }; - 4C5E871E0648C809003C637B /* CThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87130648C809003C637B /* CThread.cpp */; }; - 4C5E871F0648C809003C637B /* CThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87140648C809003C637B /* CThread.h */; }; - 4C5E87200648C809003C637B /* XMT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87150648C809003C637B /* XMT.cpp */; }; - 4C5E87210648C809003C637B /* XMT.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87160648C809003C637B /* XMT.h */; }; - 4C5E87220648C809003C637B /* XThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87170648C809003C637B /* XThread.h */; }; - 4C5E872C0648C83C003C637B /* CStreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87230648C83C003C637B /* CStreamBuffer.cpp */; }; - 4C5E872D0648C83C003C637B /* CStreamBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87240648C83C003C637B /* CStreamBuffer.h */; }; - 4C5E872E0648C83C003C637B /* CStreamFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87250648C83C003C637B /* CStreamFilter.cpp */; }; - 4C5E872F0648C83C003C637B /* CStreamFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87260648C83C003C637B /* CStreamFilter.h */; }; - 4C5E87300648C83C003C637B /* IStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87270648C83C003C637B /* IStream.cpp */; }; - 4C5E87310648C83C003C637B /* IStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87280648C83C003C637B /* IStream.h */; }; - 4C5E87320648C83C003C637B /* IStreamFilterFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87290648C83C003C637B /* IStreamFilterFactory.h */; }; - 4C5E87330648C83C003C637B /* XIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E872A0648C83C003C637B /* XIO.cpp */; }; - 4C5E87340648C83C003C637B /* XIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E872B0648C83C003C637B /* XIO.h */; }; - 4C5E874A0648C870003C637B /* CNetworkAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87350648C870003C637B /* CNetworkAddress.cpp */; }; - 4C5E874B0648C870003C637B /* CNetworkAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87360648C870003C637B /* CNetworkAddress.h */; }; - 4C5E874C0648C870003C637B /* CSocketMultiplexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87370648C870003C637B /* CSocketMultiplexer.cpp */; }; - 4C5E874D0648C870003C637B /* CSocketMultiplexer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87380648C870003C637B /* CSocketMultiplexer.h */; }; - 4C5E874E0648C870003C637B /* CTCPListenSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87390648C870003C637B /* CTCPListenSocket.cpp */; }; - 4C5E874F0648C870003C637B /* CTCPListenSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E873A0648C870003C637B /* CTCPListenSocket.h */; }; - 4C5E87500648C870003C637B /* CTCPSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E873B0648C870003C637B /* CTCPSocket.cpp */; }; - 4C5E87510648C870003C637B /* CTCPSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E873C0648C870003C637B /* CTCPSocket.h */; }; - 4C5E87520648C870003C637B /* CTCPSocketFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E873D0648C870003C637B /* CTCPSocketFactory.cpp */; }; - 4C5E87530648C870003C637B /* CTCPSocketFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E873E0648C870003C637B /* CTCPSocketFactory.h */; }; - 4C5E87540648C870003C637B /* IDataSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E873F0648C870003C637B /* IDataSocket.cpp */; }; - 4C5E87550648C870003C637B /* IDataSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87400648C870003C637B /* IDataSocket.h */; }; - 4C5E87560648C870003C637B /* IListenSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87410648C870003C637B /* IListenSocket.cpp */; }; - 4C5E87570648C870003C637B /* IListenSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87420648C870003C637B /* IListenSocket.h */; }; - 4C5E87580648C870003C637B /* ISocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87430648C870003C637B /* ISocket.cpp */; }; - 4C5E87590648C870003C637B /* ISocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87440648C870003C637B /* ISocket.h */; }; - 4C5E875A0648C870003C637B /* ISocketFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87450648C870003C637B /* ISocketFactory.h */; }; - 4C5E875B0648C870003C637B /* ISocketMultiplexerJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87460648C870003C637B /* ISocketMultiplexerJob.h */; }; - 4C5E875C0648C870003C637B /* TSocketMultiplexerMethodJob.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87470648C870003C637B /* TSocketMultiplexerMethodJob.h */; }; - 4C5E875D0648C870003C637B /* XSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87480648C870003C637B /* XSocket.cpp */; }; - 4C5E875E0648C870003C637B /* XSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87490648C870003C637B /* XSocket.h */; }; - 4C5E87800648C8BD003C637B /* CClipboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E875F0648C8BD003C637B /* CClipboard.cpp */; }; - 4C5E87810648C8BD003C637B /* CClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87600648C8BD003C637B /* CClipboard.h */; }; - 4C5E87820648C8BD003C637B /* CKeyState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87610648C8BD003C637B /* CKeyState.cpp */; }; - 4C5E87830648C8BD003C637B /* CKeyState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87620648C8BD003C637B /* CKeyState.h */; }; - 4C5E87840648C8BD003C637B /* ClipboardTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87630648C8BD003C637B /* ClipboardTypes.h */; }; - 4C5E87850648C8BD003C637B /* CPacketStreamFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87640648C8BD003C637B /* CPacketStreamFilter.cpp */; }; - 4C5E87860648C8BD003C637B /* CPacketStreamFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87650648C8BD003C637B /* CPacketStreamFilter.h */; }; - 4C5E87870648C8BD003C637B /* CPlatformScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87660648C8BD003C637B /* CPlatformScreen.cpp */; }; - 4C5E87880648C8BD003C637B /* CPlatformScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87670648C8BD003C637B /* CPlatformScreen.h */; }; - 4C5E87890648C8BD003C637B /* CProtocolUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87680648C8BD003C637B /* CProtocolUtil.cpp */; }; - 4C5E878A0648C8BD003C637B /* CProtocolUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87690648C8BD003C637B /* CProtocolUtil.h */; }; - 4C5E878B0648C8BD003C637B /* CScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E876A0648C8BD003C637B /* CScreen.cpp */; }; - 4C5E878C0648C8BD003C637B /* CScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E876B0648C8BD003C637B /* CScreen.h */; }; - 4C5E878D0648C8BD003C637B /* IClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E876C0648C8BD003C637B /* IClient.h */; }; - 4C5E878E0648C8BD003C637B /* IClipboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E876D0648C8BD003C637B /* IClipboard.cpp */; }; - 4C5E878F0648C8BD003C637B /* IClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E876E0648C8BD003C637B /* IClipboard.h */; }; - 4C5E87900648C8BD003C637B /* IKeyState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E876F0648C8BD003C637B /* IKeyState.cpp */; }; - 4C5E87910648C8BD003C637B /* IKeyState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87700648C8BD003C637B /* IKeyState.h */; }; - 4C5E87920648C8BD003C637B /* IPlatformScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87710648C8BD003C637B /* IPlatformScreen.h */; }; - 4C5E87930648C8BD003C637B /* IPrimaryScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87720648C8BD003C637B /* IPrimaryScreen.cpp */; }; - 4C5E87940648C8BD003C637B /* IPrimaryScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87730648C8BD003C637B /* IPrimaryScreen.h */; }; - 4C5E87950648C8BD003C637B /* IScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87740648C8BD003C637B /* IScreen.cpp */; }; - 4C5E87960648C8BD003C637B /* IScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87750648C8BD003C637B /* IScreen.h */; }; - 4C5E87970648C8BD003C637B /* IScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87760648C8BD003C637B /* IScreenSaver.h */; }; - 4C5E87980648C8BD003C637B /* ISecondaryScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87770648C8BD003C637B /* ISecondaryScreen.h */; }; - 4C5E87990648C8BD003C637B /* KeyTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87780648C8BD003C637B /* KeyTypes.h */; }; - 4C5E879A0648C8BD003C637B /* MouseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87790648C8BD003C637B /* MouseTypes.h */; }; - 4C5E879B0648C8BD003C637B /* OptionTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877A0648C8BD003C637B /* OptionTypes.h */; }; - 4C5E879C0648C8BD003C637B /* ProtocolTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877B0648C8BD003C637B /* ProtocolTypes.h */; }; - 4C5E879D0648C8BD003C637B /* XScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E877C0648C8BD003C637B /* XScreen.cpp */; }; - 4C5E879E0648C8BD003C637B /* XScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877D0648C8BD003C637B /* XScreen.h */; }; - 4C5E879F0648C8BD003C637B /* XSynergy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E877E0648C8BD003C637B /* XSynergy.cpp */; }; - 4C5E87A00648C8BD003C637B /* XSynergy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E877F0648C8BD003C637B /* XSynergy.h */; }; - 4C5E87AD0648C913003C637B /* COSXClipboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A10648C913003C637B /* COSXClipboard.cpp */; }; - 4C5E87AE0648C913003C637B /* COSXClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A20648C913003C637B /* COSXClipboard.h */; }; - 4C5E87AF0648C913003C637B /* COSXEventQueueBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A30648C913003C637B /* COSXEventQueueBuffer.cpp */; }; - 4C5E87B00648C913003C637B /* COSXEventQueueBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A40648C913003C637B /* COSXEventQueueBuffer.h */; }; - 4C5E87B10648C913003C637B /* COSXKeyState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A50648C913003C637B /* COSXKeyState.cpp */; }; - 4C5E87B20648C913003C637B /* COSXKeyState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A60648C913003C637B /* COSXKeyState.h */; }; - 4C5E87B30648C913003C637B /* COSXScreen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87A70648C913003C637B /* COSXScreen.cpp */; }; - 4C5E87B40648C913003C637B /* COSXScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87A80648C913003C637B /* COSXScreen.h */; }; - 4C5E87BD0648C969003C637B /* CClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87B90648C969003C637B /* CClient.cpp */; }; - 4C5E87BE0648C969003C637B /* CClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87BA0648C969003C637B /* CClient.h */; }; - 4C5E87BF0648C969003C637B /* CServerProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87BB0648C969003C637B /* CServerProxy.cpp */; }; - 4C5E87C00648C969003C637B /* CServerProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87BC0648C969003C637B /* CServerProxy.h */; }; - 4C5E87C60648C9D2003C637B /* CClientTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87C10648C9D2003C637B /* CClientTaskBarReceiver.cpp */; }; - 4C5E87C70648C9D2003C637B /* CClientTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87C20648C9D2003C637B /* CClientTaskBarReceiver.h */; }; - 4C5E87C80648C9D2003C637B /* COSXClientTaskBarReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87C30648C9D2003C637B /* COSXClientTaskBarReceiver.cpp */; }; - 4C5E87C90648C9D2003C637B /* COSXClientTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5E87C40648C9D2003C637B /* COSXClientTaskBarReceiver.h */; }; - 4C5E87CA0648C9D2003C637B /* synergyc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87C50648C9D2003C637B /* synergyc.cpp */; }; - 4C5E87CF0648CA4B003C637B /* libcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C5E868D0648C2ED003C637B /* libcommon.a */; }; - 4C5E87D10648CA75003C637B /* XArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C5E87D00648CA75003C637B /* XArch.cpp */; }; - 4C7D7CDA066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CDB066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CDC066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CDD066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CDE066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CDF066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CE0066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CE1066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CE2066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CE3066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CE4066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4C7D7CE5066319560097CA11 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7D7CD9066319560097CA11 /* common.h */; }; - 4CB43934063E4B4A00969041 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43923063E4B1300969041 /* Carbon.framework */; }; - 4CD034950650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD0348F0650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp */; }; - 4CD034960650B6F100525ED1 /* COSXClipboardAnyTextConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CD034900650B6F100525ED1 /* COSXClipboardAnyTextConverter.h */; }; - 4CD034970650B6F100525ED1 /* COSXClipboardTextConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD034910650B6F100525ED1 /* COSXClipboardTextConverter.cpp */; }; - 4CD034980650B6F100525ED1 /* COSXClipboardTextConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CD034920650B6F100525ED1 /* COSXClipboardTextConverter.h */; }; - 4CD034990650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD034930650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp */; }; - 4CD0349A0650B6F100525ED1 /* COSXClipboardUTF16Converter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CD034940650B6F100525ED1 /* COSXClipboardUTF16Converter.h */; }; - 6828A1AA081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */; }; - 6828A1AB081DF7AB003D9989 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A8081DF7AB003D9989 /* Foundation.framework */; }; - 6828A1AC081DF7AB003D9989 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A9081DF7AB003D9989 /* IOKit.framework */; }; - 6828A1AD081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */; }; - 6828A1AE081DF7AB003D9989 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A8081DF7AB003D9989 /* Foundation.framework */; }; - 6828A1AF081DF7AB003D9989 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A1A9081DF7AB003D9989 /* IOKit.framework */; }; - 6828A1C2081DF9EB003D9989 /* OSXScreenSaverControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 6828A1C1081DF9EB003D9989 /* OSXScreenSaverControl.h */; }; - 6828A238081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A237081DFAF9003D9989 /* ScreenSaver.framework */; }; - 6828A239081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6828A237081DFAF9003D9989 /* ScreenSaver.framework */; }; - 684B63620996FB2800FE7CC7 /* CClientProxy1_3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 684B63600996FB2800FE7CC7 /* CClientProxy1_3.cpp */; }; - 684B63630996FB2800FE7CC7 /* CClientProxy1_3.h in Headers */ = {isa = PBXBuildFile; fileRef = 684B63610996FB2800FE7CC7 /* CClientProxy1_3.h */; }; - 68871685073EBF7000C5ABE7 /* CArchSystemUnix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68871676073EBF6F00C5ABE7 /* CArchSystemUnix.cpp */; }; - 68871686073EBF7000C5ABE7 /* CArchSystemUnix.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871677073EBF6F00C5ABE7 /* CArchSystemUnix.h */; }; - 68871687073EBF7000C5ABE7 /* IArchConsole.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871678073EBF6F00C5ABE7 /* IArchConsole.h */; }; - 68871688073EBF7000C5ABE7 /* IArchDaemon.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871679073EBF6F00C5ABE7 /* IArchDaemon.h */; }; - 68871689073EBF7000C5ABE7 /* IArchFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167A073EBF6F00C5ABE7 /* IArchFile.h */; }; - 6887168A073EBF7000C5ABE7 /* IArchLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167B073EBF6F00C5ABE7 /* IArchLog.h */; }; - 6887168B073EBF7000C5ABE7 /* IArchMultithread.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167C073EBF6F00C5ABE7 /* IArchMultithread.h */; }; - 6887168C073EBF7000C5ABE7 /* IArchNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167D073EBF6F00C5ABE7 /* IArchNetwork.h */; }; - 6887168D073EBF7000C5ABE7 /* IArchSleep.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167E073EBF6F00C5ABE7 /* IArchSleep.h */; }; - 6887168E073EBF7000C5ABE7 /* IArchString.h in Headers */ = {isa = PBXBuildFile; fileRef = 6887167F073EBF6F00C5ABE7 /* IArchString.h */; }; - 6887168F073EBF7000C5ABE7 /* IArchSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871680073EBF6F00C5ABE7 /* IArchSystem.h */; }; - 68871690073EBF7000C5ABE7 /* IArchTaskBar.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871681073EBF7000C5ABE7 /* IArchTaskBar.h */; }; - 68871691073EBF7000C5ABE7 /* IArchTaskBarReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871682073EBF7000C5ABE7 /* IArchTaskBarReceiver.h */; }; - 68871692073EBF7000C5ABE7 /* IArchTime.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871683073EBF7000C5ABE7 /* IArchTime.h */; }; - 68871693073EBF7000C5ABE7 /* XArch.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871684073EBF7000C5ABE7 /* XArch.h */; }; - 6887169B073EC03800C5ABE7 /* MacOSXPrecomp.h in Headers */ = {isa = PBXBuildFile; fileRef = 68871699073EC02100C5ABE7 /* MacOSXPrecomp.h */; }; - 688925A909DF64B6002EB18C /* CBaseClientProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 688925A709DF64B6002EB18C /* CBaseClientProxy.cpp */; }; - 688925AA09DF64B6002EB18C /* CBaseClientProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 688925A809DF64B6002EB18C /* CBaseClientProxy.h */; }; - 68968F5D073EC484004B2F9B /* CArchDaemonNone.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68968F5A073EC484004B2F9B /* CArchDaemonNone.cpp */; }; - 68968F5E073EC484004B2F9B /* CArchTaskBarXWindows.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68968F5B073EC484004B2F9B /* CArchTaskBarXWindows.cpp */; }; - 68968F5F073EC484004B2F9B /* CArchTaskBarXWindows.h in Headers */ = {isa = PBXBuildFile; fileRef = 68968F5C073EC484004B2F9B /* CArchTaskBarXWindows.h */; }; - 689D73340956490500FFAB1D /* CInputFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 689D73320956490500FFAB1D /* CInputFilter.cpp */; }; - 689D73350956490500FFAB1D /* CInputFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 689D73330956490500FFAB1D /* CInputFilter.h */; }; - 689D7339095649AF00FFAB1D /* CKeyMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 689D7337095649AF00FFAB1D /* CKeyMap.cpp */; }; - 689D733A095649AF00FFAB1D /* CKeyMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 689D7338095649AF00FFAB1D /* CKeyMap.h */; }; - 68D87A6509A00D8800856835 /* KeyTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68D87A6409A00D8800856835 /* KeyTypes.cpp */; }; - 68FBBB21089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */; }; - 68FBBB22089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */; }; - 68FBBB24089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */; }; - 68FBBB25089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */; }; - 68FBBB28089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */; }; - 68FBBB29089F072D0016DF44 /* COSXScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */; }; - 68FBBB2A089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */; }; - 68FBBB2B089F072D0016DF44 /* COSXScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */; }; - 68FBBB2C089F07810016DF44 /* COSXScreenSaver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */; }; - 68FBBB2D089F07920016DF44 /* COSXScreenSaver.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */; }; - 68FBBB2E089F07970016DF44 /* COSXScreenSaverUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */; }; - 68FBBB2F089F079B0016DF44 /* COSXScreenSaverUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */; }; - 68FBBB5B089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */; }; - 68FBBB5C089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */; }; - 68FBBB5D089F1BCE0016DF44 /* ProtocolTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */; }; - CAB4475F081E367700724B8D /* libclient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43778063E406A00969041 /* libclient.a */; }; - CAB44760081E368100724B8D /* libarch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437B5063E443800969041 /* libarch.a */; }; - CAB44761081E368200724B8D /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB437D1063E44C200969041 /* libbase.a */; }; - CAB44762081E368300724B8D /* libclient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43778063E406A00969041 /* libclient.a */; }; - CAB44764081E368700724B8D /* libio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43824063E46AB00969041 /* libio.a */; }; - CAB44765081E368800724B8D /* libmt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43801063E45F200969041 /* libmt.a */; }; - CAB44766081E368A00724B8D /* libnet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB43848063E475800969041 /* libnet.a */; }; - CAB44767081E368B00724B8D /* libplatform.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB438B1063E488600969041 /* libplatform.a */; }; - CAB44768081E368D00724B8D /* libsynergy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CB4386E063E47F800969041 /* libsynergy.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXBuildRule section */ - 68FBBB32089F07E40016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB37089F08160016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB3A089F08200016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB3D089F082E0016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB3E089F084C0016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB41089F08590016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB44089F08620016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB47089F086B0016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB4A089F08750016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB4D089F087F0016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB50089F08890016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - 68FBBB53089F08940016DF44 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.3_3; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; -/* End PBXBuildRule section */ - -/* Begin PBXBuildStyle section */ - 014CEA460018CE2711CA2923 /* Development */ = { - isa = PBXBuildStyle; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 014CEA470018CE2711CA2923 /* Deployment */ = { - isa = PBXBuildStyle; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - MACOSX_DEPLOYMENT_TARGET = 10.2; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; -/* End PBXBuildStyle section */ - -/* Begin PBXContainerItemProxy section */ - 4C537F19064E298800D3815C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB437B4063E443800969041; - remoteInfo = arch; - }; - 4C537F1B064E298800D3815C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4C5E868C0648C2ED003C637B; - remoteInfo = common; - }; - 4C537F1D064E298800D3815C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB437D0063E44C200969041; - remoteInfo = base; - }; - 4C537F1F064E298800D3815C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB43800063E45F200969041; - remoteInfo = mt; - }; - 4C537F21064E298800D3815C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB43823063E46AB00969041; - remoteInfo = io; - }; - 4C537F23064E298800D3815C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB43847063E475800969041; - remoteInfo = net; - }; - 4C537F25064E298800D3815C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB4386D063E47F800969041; - remoteInfo = synergy; - }; - 4C537F27064E298800D3815C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB438B0063E488600969041; - remoteInfo = platform; - }; - 4C537F56064E2AA300D3815C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4C537F30064E29F800D3815C; - remoteInfo = server; - }; - 4C5E868F0648C32E003C637B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4C5E868C0648C2ED003C637B; - remoteInfo = common; - }; - 4CB43945063E4E1600969041 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB43777063E406A00969041; - remoteInfo = client; - }; - 4CB43947063E4E1600969041 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB437B4063E443800969041; - remoteInfo = arch; - }; - 4CB43949063E4E1600969041 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB437D0063E44C200969041; - remoteInfo = base; - }; - 4CB4394B063E4E1600969041 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB43800063E45F200969041; - remoteInfo = mt; - }; - 4CB4394D063E4E1600969041 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB43823063E46AB00969041; - remoteInfo = io; - }; - 4CB4394F063E4E1600969041 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB43847063E475800969041; - remoteInfo = net; - }; - 4CB43951063E4E1600969041 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB4386D063E47F800969041; - remoteInfo = synergy; - }; - 4CB43953063E4E1600969041 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB438B0063E488600969041; - remoteInfo = platform; - }; - 4CD033E90650968500525ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4CB43913063E497700969041; - remoteInfo = synergyc; - }; - 4CD033EB0650968500525ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4C537F0C064E286700D3815C; - remoteInfo = synergys; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 4C537F0D064E286700D3815C /* synergys */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = synergys; sourceTree = BUILT_PRODUCTS_DIR; }; - 4C537F0F064E293000D3815C /* COSXServerTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXServerTaskBarReceiver.cpp; path = cmd/synergys/COSXServerTaskBarReceiver.cpp; sourceTree = ""; }; - 4C537F10064E293000D3815C /* COSXServerTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXServerTaskBarReceiver.h; path = cmd/synergys/COSXServerTaskBarReceiver.h; sourceTree = ""; }; - 4C537F11064E293000D3815C /* CServerTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CServerTaskBarReceiver.cpp; path = cmd/synergys/CServerTaskBarReceiver.cpp; sourceTree = ""; }; - 4C537F12064E293000D3815C /* CServerTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CServerTaskBarReceiver.h; path = cmd/synergys/CServerTaskBarReceiver.h; sourceTree = ""; }; - 4C537F13064E293000D3815C /* synergys.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = synergys.cpp; path = cmd/synergys/synergys.cpp; sourceTree = ""; }; - 4C537F31064E29F800D3815C /* libserver.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libserver.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4C537F32064E2A0F00D3815C /* CClientListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientListener.cpp; path = lib/server/CClientListener.cpp; sourceTree = ""; }; - 4C537F33064E2A0F00D3815C /* CClientListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientListener.h; path = lib/server/CClientListener.h; sourceTree = ""; }; - 4C537F34064E2A0F00D3815C /* CClientProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy.cpp; path = lib/server/CClientProxy.cpp; sourceTree = ""; }; - 4C537F35064E2A0F00D3815C /* CClientProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy.h; path = lib/server/CClientProxy.h; sourceTree = ""; }; - 4C537F36064E2A0F00D3815C /* CClientProxy1_0.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_0.cpp; path = lib/server/CClientProxy1_0.cpp; sourceTree = ""; }; - 4C537F37064E2A0F00D3815C /* CClientProxy1_0.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_0.h; path = lib/server/CClientProxy1_0.h; sourceTree = ""; }; - 4C537F38064E2A0F00D3815C /* CClientProxy1_1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_1.cpp; path = lib/server/CClientProxy1_1.cpp; sourceTree = ""; }; - 4C537F39064E2A0F00D3815C /* CClientProxy1_1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_1.h; path = lib/server/CClientProxy1_1.h; sourceTree = ""; }; - 4C537F3A064E2A0F00D3815C /* CClientProxy1_2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_2.cpp; path = lib/server/CClientProxy1_2.cpp; sourceTree = ""; }; - 4C537F3B064E2A0F00D3815C /* CClientProxy1_2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_2.h; path = lib/server/CClientProxy1_2.h; sourceTree = ""; }; - 4C537F3C064E2A0F00D3815C /* CClientProxyUnknown.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxyUnknown.cpp; path = lib/server/CClientProxyUnknown.cpp; sourceTree = ""; }; - 4C537F3D064E2A0F00D3815C /* CClientProxyUnknown.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientProxyUnknown.h; path = lib/server/CClientProxyUnknown.h; sourceTree = ""; }; - 4C537F3E064E2A0F00D3815C /* CConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CConfig.cpp; path = lib/server/CConfig.cpp; sourceTree = ""; }; - 4C537F3F064E2A0F00D3815C /* CConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CConfig.h; path = lib/server/CConfig.h; sourceTree = ""; }; - 4C537F40064E2A0F00D3815C /* CPrimaryClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPrimaryClient.cpp; path = lib/server/CPrimaryClient.cpp; sourceTree = ""; }; - 4C537F41064E2A0F00D3815C /* CPrimaryClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPrimaryClient.h; path = lib/server/CPrimaryClient.h; sourceTree = ""; }; - 4C537F42064E2A0F00D3815C /* CServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CServer.cpp; path = lib/server/CServer.cpp; sourceTree = ""; }; - 4C537F43064E2A0F00D3815C /* CServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CServer.h; path = lib/server/CServer.h; sourceTree = ""; }; - 4C5E868D0648C2ED003C637B /* libcommon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcommon.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4C5E86920648C412003C637B /* CArch.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArch.cpp; path = lib/arch/CArch.cpp; sourceTree = ""; }; - 4C5E86930648C412003C637B /* CArch.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArch.h; path = lib/arch/CArch.h; sourceTree = ""; }; - 4C5E86940648C412003C637B /* CArchConsoleUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchConsoleUnix.cpp; path = lib/arch/CArchConsoleUnix.cpp; sourceTree = ""; }; - 4C5E86950648C412003C637B /* CArchConsoleUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchConsoleUnix.h; path = lib/arch/CArchConsoleUnix.h; sourceTree = ""; }; - 4C5E86960648C412003C637B /* CArchDaemonUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchDaemonUnix.cpp; path = lib/arch/CArchDaemonUnix.cpp; sourceTree = ""; }; - 4C5E86970648C412003C637B /* CArchDaemonUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchDaemonUnix.h; path = lib/arch/CArchDaemonUnix.h; sourceTree = ""; }; - 4C5E86980648C412003C637B /* CArchFileUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchFileUnix.cpp; path = lib/arch/CArchFileUnix.cpp; sourceTree = ""; }; - 4C5E86990648C412003C637B /* CArchFileUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchFileUnix.h; path = lib/arch/CArchFileUnix.h; sourceTree = ""; }; - 4C5E869A0648C412003C637B /* CArchLogUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchLogUnix.cpp; path = lib/arch/CArchLogUnix.cpp; sourceTree = ""; }; - 4C5E869B0648C412003C637B /* CArchLogUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchLogUnix.h; path = lib/arch/CArchLogUnix.h; sourceTree = ""; }; - 4C5E869C0648C412003C637B /* CArchMultithreadPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchMultithreadPosix.cpp; path = lib/arch/CArchMultithreadPosix.cpp; sourceTree = ""; }; - 4C5E869D0648C412003C637B /* CArchMultithreadPosix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchMultithreadPosix.h; path = lib/arch/CArchMultithreadPosix.h; sourceTree = ""; }; - 4C5E869E0648C412003C637B /* CArchNetworkBSD.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchNetworkBSD.cpp; path = lib/arch/CArchNetworkBSD.cpp; sourceTree = ""; }; - 4C5E869F0648C412003C637B /* CArchNetworkBSD.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchNetworkBSD.h; path = lib/arch/CArchNetworkBSD.h; sourceTree = ""; }; - 4C5E86A00648C412003C637B /* CArchSleepUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchSleepUnix.cpp; path = lib/arch/CArchSleepUnix.cpp; sourceTree = ""; }; - 4C5E86A10648C412003C637B /* CArchSleepUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchSleepUnix.h; path = lib/arch/CArchSleepUnix.h; sourceTree = ""; }; - 4C5E86A20648C412003C637B /* CArchStringUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchStringUnix.cpp; path = lib/arch/CArchStringUnix.cpp; sourceTree = ""; }; - 4C5E86A30648C412003C637B /* CArchStringUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchStringUnix.h; path = lib/arch/CArchStringUnix.h; sourceTree = ""; }; - 4C5E86A40648C412003C637B /* CArchTimeUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchTimeUnix.cpp; path = lib/arch/CArchTimeUnix.cpp; sourceTree = ""; }; - 4C5E86A50648C412003C637B /* CArchTimeUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchTimeUnix.h; path = lib/arch/CArchTimeUnix.h; sourceTree = ""; }; - 4C5E86A70648C412003C637B /* XArchUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = XArchUnix.cpp; path = lib/arch/XArchUnix.cpp; sourceTree = ""; }; - 4C5E86A80648C412003C637B /* XArchUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = XArchUnix.h; path = lib/arch/XArchUnix.h; sourceTree = ""; }; - 4C5E86C10648C653003C637B /* CArchDaemonNone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CArchDaemonNone.h; path = lib/arch/CArchDaemonNone.h; sourceTree = ""; }; - 4C5E86CB0648C725003C637B /* Version.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Version.cpp; path = lib/common/Version.cpp; sourceTree = ""; }; - 4C5E86CD0648C7B9003C637B /* CEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CEvent.cpp; path = lib/base/CEvent.cpp; sourceTree = ""; }; - 4C5E86CE0648C7B9003C637B /* CEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CEvent.h; path = lib/base/CEvent.h; sourceTree = ""; }; - 4C5E86CF0648C7B9003C637B /* CEventQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CEventQueue.cpp; path = lib/base/CEventQueue.cpp; sourceTree = ""; }; - 4C5E86D00648C7B9003C637B /* CEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CEventQueue.h; path = lib/base/CEventQueue.h; sourceTree = ""; }; - 4C5E86D10648C7B9003C637B /* CFunctionEventJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFunctionEventJob.cpp; path = lib/base/CFunctionEventJob.cpp; sourceTree = ""; }; - 4C5E86D20648C7B9003C637B /* CFunctionEventJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFunctionEventJob.h; path = lib/base/CFunctionEventJob.h; sourceTree = ""; }; - 4C5E86D30648C7B9003C637B /* CFunctionJob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CFunctionJob.cpp; path = lib/base/CFunctionJob.cpp; sourceTree = ""; }; - 4C5E86D40648C7B9003C637B /* CFunctionJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFunctionJob.h; path = lib/base/CFunctionJob.h; sourceTree = ""; }; - 4C5E86D50648C7B9003C637B /* CLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CLog.cpp; path = lib/base/CLog.cpp; sourceTree = ""; }; - 4C5E86D60648C7B9003C637B /* CLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CLog.h; path = lib/base/CLog.h; sourceTree = ""; }; - 4C5E86D70648C7B9003C637B /* CPriorityQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPriorityQueue.h; path = lib/base/CPriorityQueue.h; sourceTree = ""; }; - 4C5E86D80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSimpleEventQueueBuffer.cpp; path = lib/base/CSimpleEventQueueBuffer.cpp; sourceTree = ""; }; - 4C5E86D90648C7B9003C637B /* CSimpleEventQueueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSimpleEventQueueBuffer.h; path = lib/base/CSimpleEventQueueBuffer.h; sourceTree = ""; }; - 4C5E86DA0648C7B9003C637B /* CStopwatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStopwatch.cpp; path = lib/base/CStopwatch.cpp; sourceTree = ""; }; - 4C5E86DB0648C7B9003C637B /* CStopwatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStopwatch.h; path = lib/base/CStopwatch.h; sourceTree = ""; }; - 4C5E86DC0648C7B9003C637B /* CString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CString.h; path = lib/base/CString.h; sourceTree = ""; }; - 4C5E86DD0648C7B9003C637B /* CStringUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStringUtil.cpp; path = lib/base/CStringUtil.cpp; sourceTree = ""; }; - 4C5E86DE0648C7B9003C637B /* CStringUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStringUtil.h; path = lib/base/CStringUtil.h; sourceTree = ""; }; - 4C5E86DF0648C7B9003C637B /* CUnicode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CUnicode.cpp; path = lib/base/CUnicode.cpp; sourceTree = ""; }; - 4C5E86E00648C7B9003C637B /* CUnicode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CUnicode.h; path = lib/base/CUnicode.h; sourceTree = ""; }; - 4C5E86E10648C7B9003C637B /* IEventJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IEventJob.h; path = lib/base/IEventJob.h; sourceTree = ""; }; - 4C5E86E20648C7B9003C637B /* IEventQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IEventQueue.cpp; path = lib/base/IEventQueue.cpp; sourceTree = ""; }; - 4C5E86E30648C7B9003C637B /* IEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IEventQueue.h; path = lib/base/IEventQueue.h; sourceTree = ""; }; - 4C5E86E40648C7B9003C637B /* IEventQueueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IEventQueueBuffer.h; path = lib/base/IEventQueueBuffer.h; sourceTree = ""; }; - 4C5E86E50648C7B9003C637B /* IJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJob.h; path = lib/base/IJob.h; sourceTree = ""; }; - 4C5E86E60648C7B9003C637B /* ILogOutputter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ILogOutputter.h; path = lib/base/ILogOutputter.h; sourceTree = ""; }; - 4C5E86E70648C7B9003C637B /* LogOutputters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LogOutputters.cpp; path = lib/base/LogOutputters.cpp; sourceTree = ""; }; - 4C5E86E80648C7B9003C637B /* LogOutputters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LogOutputters.h; path = lib/base/LogOutputters.h; sourceTree = ""; }; - 4C5E86E90648C7B9003C637B /* TMethodEventJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TMethodEventJob.h; path = lib/base/TMethodEventJob.h; sourceTree = ""; }; - 4C5E86EA0648C7B9003C637B /* TMethodJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TMethodJob.h; path = lib/base/TMethodJob.h; sourceTree = ""; }; - 4C5E86EB0648C7B9003C637B /* XBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XBase.cpp; path = lib/base/XBase.cpp; sourceTree = ""; }; - 4C5E86EC0648C7B9003C637B /* XBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XBase.h; path = lib/base/XBase.h; sourceTree = ""; }; - 4C5E870D0648C809003C637B /* CCondVar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CCondVar.cpp; path = lib/mt/CCondVar.cpp; sourceTree = ""; }; - 4C5E870E0648C809003C637B /* CCondVar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCondVar.h; path = lib/mt/CCondVar.h; sourceTree = ""; }; - 4C5E870F0648C809003C637B /* CLock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CLock.cpp; path = lib/mt/CLock.cpp; sourceTree = ""; }; - 4C5E87100648C809003C637B /* CLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CLock.h; path = lib/mt/CLock.h; sourceTree = ""; }; - 4C5E87110648C809003C637B /* CMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CMutex.cpp; path = lib/mt/CMutex.cpp; sourceTree = ""; }; - 4C5E87120648C809003C637B /* CMutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CMutex.h; path = lib/mt/CMutex.h; sourceTree = ""; }; - 4C5E87130648C809003C637B /* CThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CThread.cpp; path = lib/mt/CThread.cpp; sourceTree = ""; }; - 4C5E87140648C809003C637B /* CThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CThread.h; path = lib/mt/CThread.h; sourceTree = ""; }; - 4C5E87150648C809003C637B /* XMT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XMT.cpp; path = lib/mt/XMT.cpp; sourceTree = ""; }; - 4C5E87160648C809003C637B /* XMT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMT.h; path = lib/mt/XMT.h; sourceTree = ""; }; - 4C5E87170648C809003C637B /* XThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XThread.h; path = lib/mt/XThread.h; sourceTree = ""; }; - 4C5E87230648C83C003C637B /* CStreamBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStreamBuffer.cpp; path = lib/io/CStreamBuffer.cpp; sourceTree = ""; }; - 4C5E87240648C83C003C637B /* CStreamBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStreamBuffer.h; path = lib/io/CStreamBuffer.h; sourceTree = ""; }; - 4C5E87250648C83C003C637B /* CStreamFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CStreamFilter.cpp; path = lib/io/CStreamFilter.cpp; sourceTree = ""; }; - 4C5E87260648C83C003C637B /* CStreamFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CStreamFilter.h; path = lib/io/CStreamFilter.h; sourceTree = ""; }; - 4C5E87270648C83C003C637B /* IStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IStream.cpp; path = lib/io/IStream.cpp; sourceTree = ""; }; - 4C5E87280648C83C003C637B /* IStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IStream.h; path = lib/io/IStream.h; sourceTree = ""; }; - 4C5E87290648C83C003C637B /* IStreamFilterFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IStreamFilterFactory.h; path = lib/io/IStreamFilterFactory.h; sourceTree = ""; }; - 4C5E872A0648C83C003C637B /* XIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XIO.cpp; path = lib/io/XIO.cpp; sourceTree = ""; }; - 4C5E872B0648C83C003C637B /* XIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XIO.h; path = lib/io/XIO.h; sourceTree = ""; }; - 4C5E87350648C870003C637B /* CNetworkAddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CNetworkAddress.cpp; path = lib/net/CNetworkAddress.cpp; sourceTree = ""; }; - 4C5E87360648C870003C637B /* CNetworkAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CNetworkAddress.h; path = lib/net/CNetworkAddress.h; sourceTree = ""; }; - 4C5E87370648C870003C637B /* CSocketMultiplexer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CSocketMultiplexer.cpp; path = lib/net/CSocketMultiplexer.cpp; sourceTree = ""; }; - 4C5E87380648C870003C637B /* CSocketMultiplexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSocketMultiplexer.h; path = lib/net/CSocketMultiplexer.h; sourceTree = ""; }; - 4C5E87390648C870003C637B /* CTCPListenSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTCPListenSocket.cpp; path = lib/net/CTCPListenSocket.cpp; sourceTree = ""; }; - 4C5E873A0648C870003C637B /* CTCPListenSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTCPListenSocket.h; path = lib/net/CTCPListenSocket.h; sourceTree = ""; }; - 4C5E873B0648C870003C637B /* CTCPSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTCPSocket.cpp; path = lib/net/CTCPSocket.cpp; sourceTree = ""; }; - 4C5E873C0648C870003C637B /* CTCPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTCPSocket.h; path = lib/net/CTCPSocket.h; sourceTree = ""; }; - 4C5E873D0648C870003C637B /* CTCPSocketFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CTCPSocketFactory.cpp; path = lib/net/CTCPSocketFactory.cpp; sourceTree = ""; }; - 4C5E873E0648C870003C637B /* CTCPSocketFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTCPSocketFactory.h; path = lib/net/CTCPSocketFactory.h; sourceTree = ""; }; - 4C5E873F0648C870003C637B /* IDataSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IDataSocket.cpp; path = lib/net/IDataSocket.cpp; sourceTree = ""; }; - 4C5E87400648C870003C637B /* IDataSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDataSocket.h; path = lib/net/IDataSocket.h; sourceTree = ""; }; - 4C5E87410648C870003C637B /* IListenSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IListenSocket.cpp; path = lib/net/IListenSocket.cpp; sourceTree = ""; }; - 4C5E87420648C870003C637B /* IListenSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IListenSocket.h; path = lib/net/IListenSocket.h; sourceTree = ""; }; - 4C5E87430648C870003C637B /* ISocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ISocket.cpp; path = lib/net/ISocket.cpp; sourceTree = ""; }; - 4C5E87440648C870003C637B /* ISocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISocket.h; path = lib/net/ISocket.h; sourceTree = ""; }; - 4C5E87450648C870003C637B /* ISocketFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISocketFactory.h; path = lib/net/ISocketFactory.h; sourceTree = ""; }; - 4C5E87460648C870003C637B /* ISocketMultiplexerJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISocketMultiplexerJob.h; path = lib/net/ISocketMultiplexerJob.h; sourceTree = ""; }; - 4C5E87470648C870003C637B /* TSocketMultiplexerMethodJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSocketMultiplexerMethodJob.h; path = lib/net/TSocketMultiplexerMethodJob.h; sourceTree = ""; }; - 4C5E87480648C870003C637B /* XSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XSocket.cpp; path = lib/net/XSocket.cpp; sourceTree = ""; }; - 4C5E87490648C870003C637B /* XSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XSocket.h; path = lib/net/XSocket.h; sourceTree = ""; }; - 4C5E875F0648C8BD003C637B /* CClipboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClipboard.cpp; path = lib/synergy/CClipboard.cpp; sourceTree = ""; }; - 4C5E87600648C8BD003C637B /* CClipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClipboard.h; path = lib/synergy/CClipboard.h; sourceTree = ""; }; - 4C5E87610648C8BD003C637B /* CKeyState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CKeyState.cpp; path = lib/synergy/CKeyState.cpp; sourceTree = ""; }; - 4C5E87620648C8BD003C637B /* CKeyState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CKeyState.h; path = lib/synergy/CKeyState.h; sourceTree = ""; }; - 4C5E87630648C8BD003C637B /* ClipboardTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClipboardTypes.h; path = lib/synergy/ClipboardTypes.h; sourceTree = ""; }; - 4C5E87640648C8BD003C637B /* CPacketStreamFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPacketStreamFilter.cpp; path = lib/synergy/CPacketStreamFilter.cpp; sourceTree = ""; }; - 4C5E87650648C8BD003C637B /* CPacketStreamFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPacketStreamFilter.h; path = lib/synergy/CPacketStreamFilter.h; sourceTree = ""; }; - 4C5E87660648C8BD003C637B /* CPlatformScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPlatformScreen.cpp; path = lib/synergy/CPlatformScreen.cpp; sourceTree = ""; }; - 4C5E87670648C8BD003C637B /* CPlatformScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CPlatformScreen.h; path = lib/synergy/CPlatformScreen.h; sourceTree = ""; }; - 4C5E87680648C8BD003C637B /* CProtocolUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CProtocolUtil.cpp; path = lib/synergy/CProtocolUtil.cpp; sourceTree = ""; }; - 4C5E87690648C8BD003C637B /* CProtocolUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CProtocolUtil.h; path = lib/synergy/CProtocolUtil.h; sourceTree = ""; }; - 4C5E876A0648C8BD003C637B /* CScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CScreen.cpp; path = lib/synergy/CScreen.cpp; sourceTree = ""; }; - 4C5E876B0648C8BD003C637B /* CScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CScreen.h; path = lib/synergy/CScreen.h; sourceTree = ""; }; - 4C5E876C0648C8BD003C637B /* IClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IClient.h; path = lib/synergy/IClient.h; sourceTree = ""; }; - 4C5E876D0648C8BD003C637B /* IClipboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IClipboard.cpp; path = lib/synergy/IClipboard.cpp; sourceTree = ""; }; - 4C5E876E0648C8BD003C637B /* IClipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IClipboard.h; path = lib/synergy/IClipboard.h; sourceTree = ""; }; - 4C5E876F0648C8BD003C637B /* IKeyState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IKeyState.cpp; path = lib/synergy/IKeyState.cpp; sourceTree = ""; }; - 4C5E87700648C8BD003C637B /* IKeyState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IKeyState.h; path = lib/synergy/IKeyState.h; sourceTree = ""; }; - 4C5E87710648C8BD003C637B /* IPlatformScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IPlatformScreen.h; path = lib/synergy/IPlatformScreen.h; sourceTree = ""; }; - 4C5E87720648C8BD003C637B /* IPrimaryScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IPrimaryScreen.cpp; path = lib/synergy/IPrimaryScreen.cpp; sourceTree = ""; }; - 4C5E87730648C8BD003C637B /* IPrimaryScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IPrimaryScreen.h; path = lib/synergy/IPrimaryScreen.h; sourceTree = ""; }; - 4C5E87740648C8BD003C637B /* IScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IScreen.cpp; path = lib/synergy/IScreen.cpp; sourceTree = ""; }; - 4C5E87750648C8BD003C637B /* IScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IScreen.h; path = lib/synergy/IScreen.h; sourceTree = ""; }; - 4C5E87760648C8BD003C637B /* IScreenSaver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IScreenSaver.h; path = lib/synergy/IScreenSaver.h; sourceTree = ""; }; - 4C5E87770648C8BD003C637B /* ISecondaryScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISecondaryScreen.h; path = lib/synergy/ISecondaryScreen.h; sourceTree = ""; }; - 4C5E87780648C8BD003C637B /* KeyTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KeyTypes.h; path = lib/synergy/KeyTypes.h; sourceTree = ""; }; - 4C5E87790648C8BD003C637B /* MouseTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MouseTypes.h; path = lib/synergy/MouseTypes.h; sourceTree = ""; }; - 4C5E877A0648C8BD003C637B /* OptionTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionTypes.h; path = lib/synergy/OptionTypes.h; sourceTree = ""; }; - 4C5E877B0648C8BD003C637B /* ProtocolTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProtocolTypes.h; path = lib/synergy/ProtocolTypes.h; sourceTree = ""; }; - 4C5E877C0648C8BD003C637B /* XScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XScreen.cpp; path = lib/synergy/XScreen.cpp; sourceTree = ""; }; - 4C5E877D0648C8BD003C637B /* XScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XScreen.h; path = lib/synergy/XScreen.h; sourceTree = ""; }; - 4C5E877E0648C8BD003C637B /* XSynergy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XSynergy.cpp; path = lib/synergy/XSynergy.cpp; sourceTree = ""; }; - 4C5E877F0648C8BD003C637B /* XSynergy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XSynergy.h; path = lib/synergy/XSynergy.h; sourceTree = ""; }; - 4C5E87A10648C913003C637B /* COSXClipboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboard.cpp; path = lib/platform/COSXClipboard.cpp; sourceTree = ""; }; - 4C5E87A20648C913003C637B /* COSXClipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboard.h; path = lib/platform/COSXClipboard.h; sourceTree = ""; }; - 4C5E87A30648C913003C637B /* COSXEventQueueBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXEventQueueBuffer.cpp; path = lib/platform/COSXEventQueueBuffer.cpp; sourceTree = ""; }; - 4C5E87A40648C913003C637B /* COSXEventQueueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXEventQueueBuffer.h; path = lib/platform/COSXEventQueueBuffer.h; sourceTree = ""; }; - 4C5E87A50648C913003C637B /* COSXKeyState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXKeyState.cpp; path = lib/platform/COSXKeyState.cpp; sourceTree = ""; }; - 4C5E87A60648C913003C637B /* COSXKeyState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXKeyState.h; path = lib/platform/COSXKeyState.h; sourceTree = ""; }; - 4C5E87A70648C913003C637B /* COSXScreen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXScreen.cpp; path = lib/platform/COSXScreen.cpp; sourceTree = ""; }; - 4C5E87A80648C913003C637B /* COSXScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXScreen.h; path = lib/platform/COSXScreen.h; sourceTree = ""; }; - 4C5E87B90648C969003C637B /* CClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClient.cpp; path = lib/client/CClient.cpp; sourceTree = ""; }; - 4C5E87BA0648C969003C637B /* CClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClient.h; path = lib/client/CClient.h; sourceTree = ""; }; - 4C5E87BB0648C969003C637B /* CServerProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CServerProxy.cpp; path = lib/client/CServerProxy.cpp; sourceTree = ""; }; - 4C5E87BC0648C969003C637B /* CServerProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CServerProxy.h; path = lib/client/CServerProxy.h; sourceTree = ""; }; - 4C5E87C10648C9D2003C637B /* CClientTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CClientTaskBarReceiver.cpp; path = cmd/synergyc/CClientTaskBarReceiver.cpp; sourceTree = ""; }; - 4C5E87C20648C9D2003C637B /* CClientTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CClientTaskBarReceiver.h; path = cmd/synergyc/CClientTaskBarReceiver.h; sourceTree = ""; }; - 4C5E87C30648C9D2003C637B /* COSXClientTaskBarReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClientTaskBarReceiver.cpp; path = cmd/synergyc/COSXClientTaskBarReceiver.cpp; sourceTree = ""; }; - 4C5E87C40648C9D2003C637B /* COSXClientTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClientTaskBarReceiver.h; path = cmd/synergyc/COSXClientTaskBarReceiver.h; sourceTree = ""; }; - 4C5E87C50648C9D2003C637B /* synergyc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = synergyc.cpp; path = cmd/synergyc/synergyc.cpp; sourceTree = ""; }; - 4C5E87D00648CA75003C637B /* XArch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XArch.cpp; path = lib/arch/XArch.cpp; sourceTree = ""; }; - 4C7D7CD9066319560097CA11 /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = common.h; path = lib/common/common.h; sourceTree = ""; }; - 4CB43778063E406A00969041 /* libclient.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libclient.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CB437B5063E443800969041 /* libarch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libarch.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CB437D1063E44C200969041 /* libbase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libbase.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CB43801063E45F200969041 /* libmt.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libmt.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CB43824063E46AB00969041 /* libio.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libio.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CB43848063E475800969041 /* libnet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libnet.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CB4386E063E47F800969041 /* libsynergy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libsynergy.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CB438B1063E488600969041 /* libplatform.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libplatform.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CB43914063E497700969041 /* synergyc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = synergyc; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CB43923063E4B1300969041 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; - 4CD0348F0650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboardAnyTextConverter.cpp; path = lib/platform/COSXClipboardAnyTextConverter.cpp; sourceTree = ""; }; - 4CD034900650B6F100525ED1 /* COSXClipboardAnyTextConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboardAnyTextConverter.h; path = lib/platform/COSXClipboardAnyTextConverter.h; sourceTree = ""; }; - 4CD034910650B6F100525ED1 /* COSXClipboardTextConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboardTextConverter.cpp; path = lib/platform/COSXClipboardTextConverter.cpp; sourceTree = ""; }; - 4CD034920650B6F100525ED1 /* COSXClipboardTextConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboardTextConverter.h; path = lib/platform/COSXClipboardTextConverter.h; sourceTree = ""; }; - 4CD034930650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = COSXClipboardUTF16Converter.cpp; path = lib/platform/COSXClipboardUTF16Converter.cpp; sourceTree = ""; }; - 4CD034940650B6F100525ED1 /* COSXClipboardUTF16Converter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSXClipboardUTF16Converter.h; path = lib/platform/COSXClipboardUTF16Converter.h; sourceTree = ""; }; - 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = ""; }; - 6828A1A8081DF7AB003D9989 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; - 6828A1A9081DF7AB003D9989 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; - 6828A1C1081DF9EB003D9989 /* OSXScreenSaverControl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = OSXScreenSaverControl.h; path = lib/platform/OSXScreenSaverControl.h; sourceTree = ""; }; - 6828A237081DFAF9003D9989 /* ScreenSaver.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScreenSaver.framework; path = /System/Library/Frameworks/ScreenSaver.framework; sourceTree = ""; }; - 684B63600996FB2800FE7CC7 /* CClientProxy1_3.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CClientProxy1_3.cpp; path = lib/server/CClientProxy1_3.cpp; sourceTree = ""; }; - 684B63610996FB2800FE7CC7 /* CClientProxy1_3.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CClientProxy1_3.h; path = lib/server/CClientProxy1_3.h; sourceTree = ""; }; - 68871676073EBF6F00C5ABE7 /* CArchSystemUnix.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchSystemUnix.cpp; path = lib/arch/CArchSystemUnix.cpp; sourceTree = ""; }; - 68871677073EBF6F00C5ABE7 /* CArchSystemUnix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchSystemUnix.h; path = lib/arch/CArchSystemUnix.h; sourceTree = ""; }; - 68871678073EBF6F00C5ABE7 /* IArchConsole.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchConsole.h; path = lib/arch/IArchConsole.h; sourceTree = ""; }; - 68871679073EBF6F00C5ABE7 /* IArchDaemon.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchDaemon.h; path = lib/arch/IArchDaemon.h; sourceTree = ""; }; - 6887167A073EBF6F00C5ABE7 /* IArchFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchFile.h; path = lib/arch/IArchFile.h; sourceTree = ""; }; - 6887167B073EBF6F00C5ABE7 /* IArchLog.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchLog.h; path = lib/arch/IArchLog.h; sourceTree = ""; }; - 6887167C073EBF6F00C5ABE7 /* IArchMultithread.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchMultithread.h; path = lib/arch/IArchMultithread.h; sourceTree = ""; }; - 6887167D073EBF6F00C5ABE7 /* IArchNetwork.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchNetwork.h; path = lib/arch/IArchNetwork.h; sourceTree = ""; }; - 6887167E073EBF6F00C5ABE7 /* IArchSleep.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchSleep.h; path = lib/arch/IArchSleep.h; sourceTree = ""; }; - 6887167F073EBF6F00C5ABE7 /* IArchString.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchString.h; path = lib/arch/IArchString.h; sourceTree = ""; }; - 68871680073EBF6F00C5ABE7 /* IArchSystem.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchSystem.h; path = lib/arch/IArchSystem.h; sourceTree = ""; }; - 68871681073EBF7000C5ABE7 /* IArchTaskBar.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchTaskBar.h; path = lib/arch/IArchTaskBar.h; sourceTree = ""; }; - 68871682073EBF7000C5ABE7 /* IArchTaskBarReceiver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchTaskBarReceiver.h; path = lib/arch/IArchTaskBarReceiver.h; sourceTree = ""; }; - 68871683073EBF7000C5ABE7 /* IArchTime.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = IArchTime.h; path = lib/arch/IArchTime.h; sourceTree = ""; }; - 68871684073EBF7000C5ABE7 /* XArch.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = XArch.h; path = lib/arch/XArch.h; sourceTree = ""; }; - 68871699073EC02100C5ABE7 /* MacOSXPrecomp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = MacOSXPrecomp.h; path = lib/common/MacOSXPrecomp.h; sourceTree = ""; }; - 688925A709DF64B6002EB18C /* CBaseClientProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CBaseClientProxy.cpp; path = lib/server/CBaseClientProxy.cpp; sourceTree = ""; }; - 688925A809DF64B6002EB18C /* CBaseClientProxy.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CBaseClientProxy.h; path = lib/server/CBaseClientProxy.h; sourceTree = ""; }; - 68968F5A073EC484004B2F9B /* CArchDaemonNone.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchDaemonNone.cpp; path = lib/arch/CArchDaemonNone.cpp; sourceTree = ""; }; - 68968F5B073EC484004B2F9B /* CArchTaskBarXWindows.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CArchTaskBarXWindows.cpp; path = lib/arch/CArchTaskBarXWindows.cpp; sourceTree = ""; }; - 68968F5C073EC484004B2F9B /* CArchTaskBarXWindows.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CArchTaskBarXWindows.h; path = lib/arch/CArchTaskBarXWindows.h; sourceTree = ""; }; - 689D73320956490500FFAB1D /* CInputFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CInputFilter.cpp; path = lib/server/CInputFilter.cpp; sourceTree = ""; }; - 689D73330956490500FFAB1D /* CInputFilter.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CInputFilter.h; path = lib/server/CInputFilter.h; sourceTree = ""; }; - 689D7337095649AF00FFAB1D /* CKeyMap.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CKeyMap.cpp; path = lib/synergy/CKeyMap.cpp; sourceTree = ""; }; - 689D7338095649AF00FFAB1D /* CKeyMap.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CKeyMap.h; path = lib/synergy/CKeyMap.h; sourceTree = ""; }; - 68D87A6409A00D8800856835 /* KeyTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = KeyTypes.cpp; path = lib/synergy/KeyTypes.cpp; sourceTree = ""; }; - 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = COSXScreenSaverUtil.m; path = lib/platform/COSXScreenSaverUtil.m; sourceTree = ""; }; - 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = COSXScreenSaverUtil.h; path = lib/platform/COSXScreenSaverUtil.h; sourceTree = ""; }; - 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = COSXScreenSaver.cpp; path = lib/platform/COSXScreenSaver.cpp; sourceTree = ""; }; - 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = COSXScreenSaver.h; path = lib/platform/COSXScreenSaver.h; sourceTree = ""; }; - 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ProtocolTypes.cpp; path = lib/synergy/ProtocolTypes.cpp; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 4C537F5A064E2B0700D3815C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C537F64064E2B4B00D3815C /* Carbon.framework in Frameworks */, - 4C537F5B064E2B4200D3815C /* libcommon.a in Frameworks */, - 4C537F5C064E2B4200D3815C /* libarch.a in Frameworks */, - 4C537F5D064E2B4200D3815C /* libbase.a in Frameworks */, - 4C537F5E064E2B4200D3815C /* libmt.a in Frameworks */, - 4C537F5F064E2B4200D3815C /* libio.a in Frameworks */, - 4C537F60064E2B4200D3815C /* libnet.a in Frameworks */, - 4C537F61064E2B4200D3815C /* libserver.a in Frameworks */, - 4C537F62064E2B4200D3815C /* libsynergy.a in Frameworks */, - 4C537F63064E2B4200D3815C /* libplatform.a in Frameworks */, - 6828A1AD081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */, - 6828A1AE081DF7AB003D9989 /* Foundation.framework in Frameworks */, - 6828A1AF081DF7AB003D9989 /* IOKit.framework in Frameworks */, - 6828A239081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */, - CAB4475F081E367700724B8D /* libclient.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43930063E4B4300969041 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CB43934063E4B4A00969041 /* Carbon.framework in Frameworks */, - 4C5E87CF0648CA4B003C637B /* libcommon.a in Frameworks */, - 6828A1AA081DF7AB003D9989 /* ApplicationServices.framework in Frameworks */, - 6828A1AB081DF7AB003D9989 /* Foundation.framework in Frameworks */, - 6828A1AC081DF7AB003D9989 /* IOKit.framework in Frameworks */, - 6828A238081DFAF9003D9989 /* ScreenSaver.framework in Frameworks */, - CAB44760081E368100724B8D /* libarch.a in Frameworks */, - CAB44761081E368200724B8D /* libbase.a in Frameworks */, - CAB44762081E368300724B8D /* libclient.a in Frameworks */, - CAB44764081E368700724B8D /* libio.a in Frameworks */, - CAB44765081E368800724B8D /* libmt.a in Frameworks */, - CAB44766081E368A00724B8D /* libnet.a in Frameworks */, - CAB44767081E368B00724B8D /* libplatform.a in Frameworks */, - CAB44768081E368D00724B8D /* libsynergy.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0249A662FF388D9811CA2CEA /* External Frameworks and Libraries */ = { - isa = PBXGroup; - children = ( - 6828A237081DFAF9003D9989 /* ScreenSaver.framework */, - 6828A1A7081DF7AB003D9989 /* ApplicationServices.framework */, - 6828A1A8081DF7AB003D9989 /* Foundation.framework */, - 6828A1A9081DF7AB003D9989 /* IOKit.framework */, - 4CB43923063E4B1300969041 /* Carbon.framework */, - ); - name = "External Frameworks and Libraries"; - sourceTree = ""; - }; - 08FB7794FE84155DC02AAC07 /* synergy */ = { - isa = PBXGroup; - children = ( - 08FB7795FE84155DC02AAC07 /* Source */, - 0249A662FF388D9811CA2CEA /* External Frameworks and Libraries */, - 1AB674ADFE9D54B511CA2CBB /* Products */, - ); - name = synergy; - sourceTree = ""; - }; - 08FB7795FE84155DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - 4CB4378B063E432C00969041 /* Arch */, - 4CB437D2063E44E400969041 /* Base */, - 4CB43779063E40B600969041 /* Client */, - 4C5E86CA0648C6FB003C637B /* Common */, - 4CB4381E063E469300969041 /* IO */, - 4CB437FB063E45D700969041 /* MT */, - 4CB43849063E478900969041 /* Net */, - 4CB438AD063E487200969041 /* Platform */, - 4C537F2B064E29C900D3815C /* Server */, - 4CB43866063E47C800969041 /* Synergy */, - 4CB43916063E4A1A00969041 /* SynergyC */, - 4C537F07064E283300D3815C /* SynergyS */, - ); - name = Source; - sourceTree = ""; - }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 4CB43778063E406A00969041 /* libclient.a */, - 4C5E868D0648C2ED003C637B /* libcommon.a */, - 4CB437B5063E443800969041 /* libarch.a */, - 4CB437D1063E44C200969041 /* libbase.a */, - 4CB43801063E45F200969041 /* libmt.a */, - 4CB43824063E46AB00969041 /* libio.a */, - 4CB43848063E475800969041 /* libnet.a */, - 4C537F31064E29F800D3815C /* libserver.a */, - 4CB4386E063E47F800969041 /* libsynergy.a */, - 4CB438B1063E488600969041 /* libplatform.a */, - 4CB43914063E497700969041 /* synergyc */, - 4C537F0D064E286700D3815C /* synergys */, - ); - name = Products; - sourceTree = ""; - }; - 4C537F07064E283300D3815C /* SynergyS */ = { - isa = PBXGroup; - children = ( - 4C537F0F064E293000D3815C /* COSXServerTaskBarReceiver.cpp */, - 4C537F10064E293000D3815C /* COSXServerTaskBarReceiver.h */, - 4C537F11064E293000D3815C /* CServerTaskBarReceiver.cpp */, - 4C537F12064E293000D3815C /* CServerTaskBarReceiver.h */, - 4C537F13064E293000D3815C /* synergys.cpp */, - ); - name = SynergyS; - sourceTree = ""; - }; - 4C537F2B064E29C900D3815C /* Server */ = { - isa = PBXGroup; - children = ( - 688925A709DF64B6002EB18C /* CBaseClientProxy.cpp */, - 688925A809DF64B6002EB18C /* CBaseClientProxy.h */, - 4C537F32064E2A0F00D3815C /* CClientListener.cpp */, - 4C537F33064E2A0F00D3815C /* CClientListener.h */, - 4C537F34064E2A0F00D3815C /* CClientProxy.cpp */, - 4C537F35064E2A0F00D3815C /* CClientProxy.h */, - 4C537F36064E2A0F00D3815C /* CClientProxy1_0.cpp */, - 4C537F37064E2A0F00D3815C /* CClientProxy1_0.h */, - 4C537F38064E2A0F00D3815C /* CClientProxy1_1.cpp */, - 4C537F39064E2A0F00D3815C /* CClientProxy1_1.h */, - 4C537F3A064E2A0F00D3815C /* CClientProxy1_2.cpp */, - 4C537F3B064E2A0F00D3815C /* CClientProxy1_2.h */, - 684B63600996FB2800FE7CC7 /* CClientProxy1_3.cpp */, - 684B63610996FB2800FE7CC7 /* CClientProxy1_3.h */, - 4C537F3C064E2A0F00D3815C /* CClientProxyUnknown.cpp */, - 4C537F3D064E2A0F00D3815C /* CClientProxyUnknown.h */, - 4C537F3E064E2A0F00D3815C /* CConfig.cpp */, - 4C537F3F064E2A0F00D3815C /* CConfig.h */, - 689D73320956490500FFAB1D /* CInputFilter.cpp */, - 689D73330956490500FFAB1D /* CInputFilter.h */, - 4C537F40064E2A0F00D3815C /* CPrimaryClient.cpp */, - 4C537F41064E2A0F00D3815C /* CPrimaryClient.h */, - 4C537F42064E2A0F00D3815C /* CServer.cpp */, - 4C537F43064E2A0F00D3815C /* CServer.h */, - ); - name = Server; - sourceTree = ""; - }; - 4C5E86CA0648C6FB003C637B /* Common */ = { - isa = PBXGroup; - children = ( - 4C7D7CD9066319560097CA11 /* common.h */, - 68871699073EC02100C5ABE7 /* MacOSXPrecomp.h */, - 4C5E86CB0648C725003C637B /* Version.cpp */, - ); - name = Common; - sourceTree = ""; - }; - 4CB43779063E40B600969041 /* Client */ = { - isa = PBXGroup; - children = ( - 4C5E87B90648C969003C637B /* CClient.cpp */, - 4C5E87BA0648C969003C637B /* CClient.h */, - 4C5E87BB0648C969003C637B /* CServerProxy.cpp */, - 4C5E87BC0648C969003C637B /* CServerProxy.h */, - ); - name = Client; - sourceTree = ""; - }; - 4CB4378B063E432C00969041 /* Arch */ = { - isa = PBXGroup; - children = ( - 4C5E86920648C412003C637B /* CArch.cpp */, - 4C5E86930648C412003C637B /* CArch.h */, - 4C5E86940648C412003C637B /* CArchConsoleUnix.cpp */, - 4C5E86950648C412003C637B /* CArchConsoleUnix.h */, - 68968F5A073EC484004B2F9B /* CArchDaemonNone.cpp */, - 4C5E86C10648C653003C637B /* CArchDaemonNone.h */, - 4C5E86960648C412003C637B /* CArchDaemonUnix.cpp */, - 4C5E86970648C412003C637B /* CArchDaemonUnix.h */, - 4C5E86980648C412003C637B /* CArchFileUnix.cpp */, - 4C5E86990648C412003C637B /* CArchFileUnix.h */, - 4C5E869A0648C412003C637B /* CArchLogUnix.cpp */, - 4C5E869B0648C412003C637B /* CArchLogUnix.h */, - 4C5E869C0648C412003C637B /* CArchMultithreadPosix.cpp */, - 4C5E869D0648C412003C637B /* CArchMultithreadPosix.h */, - 4C5E869E0648C412003C637B /* CArchNetworkBSD.cpp */, - 4C5E869F0648C412003C637B /* CArchNetworkBSD.h */, - 4C5E86A00648C412003C637B /* CArchSleepUnix.cpp */, - 4C5E86A10648C412003C637B /* CArchSleepUnix.h */, - 4C5E86A20648C412003C637B /* CArchStringUnix.cpp */, - 4C5E86A30648C412003C637B /* CArchStringUnix.h */, - 68871676073EBF6F00C5ABE7 /* CArchSystemUnix.cpp */, - 68871677073EBF6F00C5ABE7 /* CArchSystemUnix.h */, - 68968F5B073EC484004B2F9B /* CArchTaskBarXWindows.cpp */, - 68968F5C073EC484004B2F9B /* CArchTaskBarXWindows.h */, - 4C5E86A40648C412003C637B /* CArchTimeUnix.cpp */, - 4C5E86A50648C412003C637B /* CArchTimeUnix.h */, - 68871678073EBF6F00C5ABE7 /* IArchConsole.h */, - 68871679073EBF6F00C5ABE7 /* IArchDaemon.h */, - 6887167A073EBF6F00C5ABE7 /* IArchFile.h */, - 6887167B073EBF6F00C5ABE7 /* IArchLog.h */, - 6887167C073EBF6F00C5ABE7 /* IArchMultithread.h */, - 6887167D073EBF6F00C5ABE7 /* IArchNetwork.h */, - 6887167E073EBF6F00C5ABE7 /* IArchSleep.h */, - 6887167F073EBF6F00C5ABE7 /* IArchString.h */, - 68871680073EBF6F00C5ABE7 /* IArchSystem.h */, - 68871681073EBF7000C5ABE7 /* IArchTaskBar.h */, - 68871682073EBF7000C5ABE7 /* IArchTaskBarReceiver.h */, - 68871683073EBF7000C5ABE7 /* IArchTime.h */, - 68871684073EBF7000C5ABE7 /* XArch.h */, - 4C5E87D00648CA75003C637B /* XArch.cpp */, - 4C5E86A70648C412003C637B /* XArchUnix.cpp */, - 4C5E86A80648C412003C637B /* XArchUnix.h */, - ); - name = Arch; - sourceTree = ""; - }; - 4CB437D2063E44E400969041 /* Base */ = { - isa = PBXGroup; - children = ( - 4C5E86CD0648C7B9003C637B /* CEvent.cpp */, - 4C5E86CE0648C7B9003C637B /* CEvent.h */, - 4C5E86CF0648C7B9003C637B /* CEventQueue.cpp */, - 4C5E86D00648C7B9003C637B /* CEventQueue.h */, - 4C5E86D10648C7B9003C637B /* CFunctionEventJob.cpp */, - 4C5E86D20648C7B9003C637B /* CFunctionEventJob.h */, - 4C5E86D30648C7B9003C637B /* CFunctionJob.cpp */, - 4C5E86D40648C7B9003C637B /* CFunctionJob.h */, - 4C5E86D50648C7B9003C637B /* CLog.cpp */, - 4C5E86D60648C7B9003C637B /* CLog.h */, - 4C5E86D70648C7B9003C637B /* CPriorityQueue.h */, - 4C5E86D80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp */, - 4C5E86D90648C7B9003C637B /* CSimpleEventQueueBuffer.h */, - 4C5E86DA0648C7B9003C637B /* CStopwatch.cpp */, - 4C5E86DB0648C7B9003C637B /* CStopwatch.h */, - 4C5E86DC0648C7B9003C637B /* CString.h */, - 4C5E86DD0648C7B9003C637B /* CStringUtil.cpp */, - 4C5E86DE0648C7B9003C637B /* CStringUtil.h */, - 4C5E86DF0648C7B9003C637B /* CUnicode.cpp */, - 4C5E86E00648C7B9003C637B /* CUnicode.h */, - 4C5E86E10648C7B9003C637B /* IEventJob.h */, - 4C5E86E20648C7B9003C637B /* IEventQueue.cpp */, - 4C5E86E30648C7B9003C637B /* IEventQueue.h */, - 4C5E86E40648C7B9003C637B /* IEventQueueBuffer.h */, - 4C5E86E50648C7B9003C637B /* IJob.h */, - 4C5E86E60648C7B9003C637B /* ILogOutputter.h */, - 4C5E86E70648C7B9003C637B /* LogOutputters.cpp */, - 4C5E86E80648C7B9003C637B /* LogOutputters.h */, - 4C5E86E90648C7B9003C637B /* TMethodEventJob.h */, - 4C5E86EA0648C7B9003C637B /* TMethodJob.h */, - 4C5E86EB0648C7B9003C637B /* XBase.cpp */, - 4C5E86EC0648C7B9003C637B /* XBase.h */, - ); - name = Base; - sourceTree = ""; - }; - 4CB437FB063E45D700969041 /* MT */ = { - isa = PBXGroup; - children = ( - 4C5E870D0648C809003C637B /* CCondVar.cpp */, - 4C5E870E0648C809003C637B /* CCondVar.h */, - 4C5E870F0648C809003C637B /* CLock.cpp */, - 4C5E87100648C809003C637B /* CLock.h */, - 4C5E87110648C809003C637B /* CMutex.cpp */, - 4C5E87120648C809003C637B /* CMutex.h */, - 4C5E87130648C809003C637B /* CThread.cpp */, - 4C5E87140648C809003C637B /* CThread.h */, - 4C5E87150648C809003C637B /* XMT.cpp */, - 4C5E87160648C809003C637B /* XMT.h */, - 4C5E87170648C809003C637B /* XThread.h */, - ); - name = MT; - sourceTree = ""; - }; - 4CB4381E063E469300969041 /* IO */ = { - isa = PBXGroup; - children = ( - 4C5E87230648C83C003C637B /* CStreamBuffer.cpp */, - 4C5E87240648C83C003C637B /* CStreamBuffer.h */, - 4C5E87250648C83C003C637B /* CStreamFilter.cpp */, - 4C5E87260648C83C003C637B /* CStreamFilter.h */, - 4C5E87270648C83C003C637B /* IStream.cpp */, - 4C5E87280648C83C003C637B /* IStream.h */, - 4C5E87290648C83C003C637B /* IStreamFilterFactory.h */, - 4C5E872A0648C83C003C637B /* XIO.cpp */, - 4C5E872B0648C83C003C637B /* XIO.h */, - ); - name = IO; - sourceTree = ""; - }; - 4CB43849063E478900969041 /* Net */ = { - isa = PBXGroup; - children = ( - 4C5E87350648C870003C637B /* CNetworkAddress.cpp */, - 4C5E87360648C870003C637B /* CNetworkAddress.h */, - 4C5E87370648C870003C637B /* CSocketMultiplexer.cpp */, - 4C5E87380648C870003C637B /* CSocketMultiplexer.h */, - 4C5E87390648C870003C637B /* CTCPListenSocket.cpp */, - 4C5E873A0648C870003C637B /* CTCPListenSocket.h */, - 4C5E873B0648C870003C637B /* CTCPSocket.cpp */, - 4C5E873C0648C870003C637B /* CTCPSocket.h */, - 4C5E873D0648C870003C637B /* CTCPSocketFactory.cpp */, - 4C5E873E0648C870003C637B /* CTCPSocketFactory.h */, - 4C5E873F0648C870003C637B /* IDataSocket.cpp */, - 4C5E87400648C870003C637B /* IDataSocket.h */, - 4C5E87410648C870003C637B /* IListenSocket.cpp */, - 4C5E87420648C870003C637B /* IListenSocket.h */, - 4C5E87430648C870003C637B /* ISocket.cpp */, - 4C5E87440648C870003C637B /* ISocket.h */, - 4C5E87450648C870003C637B /* ISocketFactory.h */, - 4C5E87460648C870003C637B /* ISocketMultiplexerJob.h */, - 4C5E87470648C870003C637B /* TSocketMultiplexerMethodJob.h */, - 4C5E87480648C870003C637B /* XSocket.cpp */, - 4C5E87490648C870003C637B /* XSocket.h */, - ); - name = Net; - sourceTree = ""; - }; - 4CB43866063E47C800969041 /* Synergy */ = { - isa = PBXGroup; - children = ( - 4C5E875F0648C8BD003C637B /* CClipboard.cpp */, - 4C5E87600648C8BD003C637B /* CClipboard.h */, - 689D7337095649AF00FFAB1D /* CKeyMap.cpp */, - 689D7338095649AF00FFAB1D /* CKeyMap.h */, - 4C5E87610648C8BD003C637B /* CKeyState.cpp */, - 4C5E87620648C8BD003C637B /* CKeyState.h */, - 4C5E87630648C8BD003C637B /* ClipboardTypes.h */, - 4C5E87640648C8BD003C637B /* CPacketStreamFilter.cpp */, - 4C5E87650648C8BD003C637B /* CPacketStreamFilter.h */, - 4C5E87660648C8BD003C637B /* CPlatformScreen.cpp */, - 4C5E87670648C8BD003C637B /* CPlatformScreen.h */, - 4C5E87680648C8BD003C637B /* CProtocolUtil.cpp */, - 4C5E87690648C8BD003C637B /* CProtocolUtil.h */, - 4C5E876A0648C8BD003C637B /* CScreen.cpp */, - 4C5E876B0648C8BD003C637B /* CScreen.h */, - 4C5E876C0648C8BD003C637B /* IClient.h */, - 4C5E876D0648C8BD003C637B /* IClipboard.cpp */, - 4C5E876E0648C8BD003C637B /* IClipboard.h */, - 4C5E876F0648C8BD003C637B /* IKeyState.cpp */, - 4C5E87700648C8BD003C637B /* IKeyState.h */, - 4C5E87710648C8BD003C637B /* IPlatformScreen.h */, - 4C5E87720648C8BD003C637B /* IPrimaryScreen.cpp */, - 4C5E87730648C8BD003C637B /* IPrimaryScreen.h */, - 4C5E87740648C8BD003C637B /* IScreen.cpp */, - 4C5E87750648C8BD003C637B /* IScreen.h */, - 4C5E87760648C8BD003C637B /* IScreenSaver.h */, - 4C5E87770648C8BD003C637B /* ISecondaryScreen.h */, - 68D87A6409A00D8800856835 /* KeyTypes.cpp */, - 4C5E87780648C8BD003C637B /* KeyTypes.h */, - 4C5E87790648C8BD003C637B /* MouseTypes.h */, - 4C5E877A0648C8BD003C637B /* OptionTypes.h */, - 68FBBB5A089F1BA00016DF44 /* ProtocolTypes.cpp */, - 4C5E877B0648C8BD003C637B /* ProtocolTypes.h */, - 4C5E877C0648C8BD003C637B /* XScreen.cpp */, - 4C5E877D0648C8BD003C637B /* XScreen.h */, - 4C5E877E0648C8BD003C637B /* XSynergy.cpp */, - 4C5E877F0648C8BD003C637B /* XSynergy.h */, - ); - name = Synergy; - sourceTree = ""; - }; - 4CB438AD063E487200969041 /* Platform */ = { - isa = PBXGroup; - children = ( - 4C5E87A10648C913003C637B /* COSXClipboard.cpp */, - 4C5E87A20648C913003C637B /* COSXClipboard.h */, - 4CD0348F0650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp */, - 4CD034900650B6F100525ED1 /* COSXClipboardAnyTextConverter.h */, - 4CD034910650B6F100525ED1 /* COSXClipboardTextConverter.cpp */, - 4CD034920650B6F100525ED1 /* COSXClipboardTextConverter.h */, - 4CD034930650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp */, - 4CD034940650B6F100525ED1 /* COSXClipboardUTF16Converter.h */, - 4C5E87A30648C913003C637B /* COSXEventQueueBuffer.cpp */, - 4C5E87A40648C913003C637B /* COSXEventQueueBuffer.h */, - 4C5E87A50648C913003C637B /* COSXKeyState.cpp */, - 4C5E87A60648C913003C637B /* COSXKeyState.h */, - 4C5E87A70648C913003C637B /* COSXScreen.cpp */, - 4C5E87A80648C913003C637B /* COSXScreen.h */, - 68FBBB26089F072D0016DF44 /* COSXScreenSaver.cpp */, - 68FBBB27089F072D0016DF44 /* COSXScreenSaver.h */, - 68FBBB20089F06DC0016DF44 /* COSXScreenSaverUtil.m */, - 68FBBB23089F06F20016DF44 /* COSXScreenSaverUtil.h */, - 6828A1C1081DF9EB003D9989 /* OSXScreenSaverControl.h */, - ); - name = Platform; - sourceTree = ""; - }; - 4CB43916063E4A1A00969041 /* SynergyC */ = { - isa = PBXGroup; - children = ( - 4C5E87C10648C9D2003C637B /* CClientTaskBarReceiver.cpp */, - 4C5E87C20648C9D2003C637B /* CClientTaskBarReceiver.h */, - 4C5E87C30648C9D2003C637B /* COSXClientTaskBarReceiver.cpp */, - 4C5E87C40648C9D2003C637B /* COSXClientTaskBarReceiver.h */, - 4C5E87C50648C9D2003C637B /* synergyc.cpp */, - ); - name = SynergyC; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 4C537F0A064E286700D3815C /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C537F15064E293000D3815C /* COSXServerTaskBarReceiver.h in Headers */, - 4C537F17064E293000D3815C /* CServerTaskBarReceiver.h in Headers */, - 4C7D7CDA066319560097CA11 /* common.h in Headers */, - 68FBBB25089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */, - 68FBBB2B089F072D0016DF44 /* COSXScreenSaver.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F2E064E29F800D3815C /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C537F45064E2A0F00D3815C /* CClientListener.h in Headers */, - 4C537F47064E2A0F00D3815C /* CClientProxy.h in Headers */, - 4C537F49064E2A0F00D3815C /* CClientProxy1_0.h in Headers */, - 4C537F4B064E2A0F00D3815C /* CClientProxy1_1.h in Headers */, - 4C537F4D064E2A0F00D3815C /* CClientProxy1_2.h in Headers */, - 684B63630996FB2800FE7CC7 /* CClientProxy1_3.h in Headers */, - 4C537F4F064E2A0F00D3815C /* CClientProxyUnknown.h in Headers */, - 4C537F51064E2A0F00D3815C /* CConfig.h in Headers */, - 689D73350956490500FFAB1D /* CInputFilter.h in Headers */, - 4C537F53064E2A0F00D3815C /* CPrimaryClient.h in Headers */, - 4C537F55064E2A0F00D3815C /* CServer.h in Headers */, - 4C7D7CE4066319560097CA11 /* common.h in Headers */, - 688925AA09DF64B6002EB18C /* CBaseClientProxy.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4C5E868A0648C2ED003C637B /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C7D7CDD066319560097CA11 /* common.h in Headers */, - 6887169B073EC03800C5ABE7 /* MacOSXPrecomp.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43775063E406A00969041 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E87BE0648C969003C637B /* CClient.h in Headers */, - 4C5E87C00648C969003C637B /* CServerProxy.h in Headers */, - 4C7D7CE5066319560097CA11 /* common.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437B2063E443800969041 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C7D7CDC066319560097CA11 /* common.h in Headers */, - 4C5E86AA0648C412003C637B /* CArch.h in Headers */, - 4C5E86AC0648C412003C637B /* CArchConsoleUnix.h in Headers */, - 4C5E86C30648C653003C637B /* CArchDaemonNone.h in Headers */, - 4C5E86AE0648C412003C637B /* CArchDaemonUnix.h in Headers */, - 4C5E86B00648C412003C637B /* CArchFileUnix.h in Headers */, - 4C5E86B20648C412003C637B /* CArchLogUnix.h in Headers */, - 4C5E86B40648C412003C637B /* CArchMultithreadPosix.h in Headers */, - 4C5E86B60648C412003C637B /* CArchNetworkBSD.h in Headers */, - 4C5E86B80648C412003C637B /* CArchSleepUnix.h in Headers */, - 4C5E86BA0648C412003C637B /* CArchStringUnix.h in Headers */, - 68871686073EBF7000C5ABE7 /* CArchSystemUnix.h in Headers */, - 68968F5F073EC484004B2F9B /* CArchTaskBarXWindows.h in Headers */, - 4C5E86BC0648C412003C637B /* CArchTimeUnix.h in Headers */, - 68871687073EBF7000C5ABE7 /* IArchConsole.h in Headers */, - 68871688073EBF7000C5ABE7 /* IArchDaemon.h in Headers */, - 68871689073EBF7000C5ABE7 /* IArchFile.h in Headers */, - 6887168A073EBF7000C5ABE7 /* IArchLog.h in Headers */, - 6887168B073EBF7000C5ABE7 /* IArchMultithread.h in Headers */, - 6887168C073EBF7000C5ABE7 /* IArchNetwork.h in Headers */, - 6887168D073EBF7000C5ABE7 /* IArchSleep.h in Headers */, - 6887168E073EBF7000C5ABE7 /* IArchString.h in Headers */, - 6887168F073EBF7000C5ABE7 /* IArchSystem.h in Headers */, - 68871690073EBF7000C5ABE7 /* IArchTaskBar.h in Headers */, - 68871691073EBF7000C5ABE7 /* IArchTaskBarReceiver.h in Headers */, - 68871692073EBF7000C5ABE7 /* IArchTime.h in Headers */, - 68871693073EBF7000C5ABE7 /* XArch.h in Headers */, - 4C5E86BF0648C412003C637B /* XArchUnix.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437CE063E44C200969041 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E86EE0648C7B9003C637B /* CEvent.h in Headers */, - 4C5E86F00648C7B9003C637B /* CEventQueue.h in Headers */, - 4C5E86F20648C7B9003C637B /* CFunctionEventJob.h in Headers */, - 4C5E86F40648C7B9003C637B /* CFunctionJob.h in Headers */, - 4C5E86F60648C7B9003C637B /* CLog.h in Headers */, - 4C5E86F70648C7B9003C637B /* CPriorityQueue.h in Headers */, - 4C5E86F90648C7B9003C637B /* CSimpleEventQueueBuffer.h in Headers */, - 4C5E86FB0648C7B9003C637B /* CStopwatch.h in Headers */, - 4C5E86FC0648C7B9003C637B /* CString.h in Headers */, - 4C5E86FE0648C7B9003C637B /* CStringUtil.h in Headers */, - 4C5E87000648C7B9003C637B /* CUnicode.h in Headers */, - 4C5E87010648C7B9003C637B /* IEventJob.h in Headers */, - 4C5E87030648C7B9003C637B /* IEventQueue.h in Headers */, - 4C5E87040648C7B9003C637B /* IEventQueueBuffer.h in Headers */, - 4C5E87050648C7B9003C637B /* IJob.h in Headers */, - 4C5E87060648C7B9003C637B /* ILogOutputter.h in Headers */, - 4C5E87080648C7B9003C637B /* LogOutputters.h in Headers */, - 4C5E87090648C7B9003C637B /* TMethodEventJob.h in Headers */, - 4C5E870A0648C7B9003C637B /* TMethodJob.h in Headers */, - 4C5E870C0648C7B9003C637B /* XBase.h in Headers */, - 4C7D7CDE066319560097CA11 /* common.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437FE063E45F200969041 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E87190648C809003C637B /* CCondVar.h in Headers */, - 4C5E871B0648C809003C637B /* CLock.h in Headers */, - 4C5E871D0648C809003C637B /* CMutex.h in Headers */, - 4C5E871F0648C809003C637B /* CThread.h in Headers */, - 4C5E87210648C809003C637B /* XMT.h in Headers */, - 4C5E87220648C809003C637B /* XThread.h in Headers */, - 4C7D7CDF066319560097CA11 /* common.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43821063E46AB00969041 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E872D0648C83C003C637B /* CStreamBuffer.h in Headers */, - 4C5E872F0648C83C003C637B /* CStreamFilter.h in Headers */, - 4C5E87310648C83C003C637B /* IStream.h in Headers */, - 4C5E87320648C83C003C637B /* IStreamFilterFactory.h in Headers */, - 4C5E87340648C83C003C637B /* XIO.h in Headers */, - 4C7D7CE0066319560097CA11 /* common.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43845063E475800969041 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E874B0648C870003C637B /* CNetworkAddress.h in Headers */, - 4C5E874D0648C870003C637B /* CSocketMultiplexer.h in Headers */, - 4C5E874F0648C870003C637B /* CTCPListenSocket.h in Headers */, - 4C5E87510648C870003C637B /* CTCPSocket.h in Headers */, - 4C5E87530648C870003C637B /* CTCPSocketFactory.h in Headers */, - 4C5E87550648C870003C637B /* IDataSocket.h in Headers */, - 4C5E87570648C870003C637B /* IListenSocket.h in Headers */, - 4C5E87590648C870003C637B /* ISocket.h in Headers */, - 4C5E875A0648C870003C637B /* ISocketFactory.h in Headers */, - 4C5E875B0648C870003C637B /* ISocketMultiplexerJob.h in Headers */, - 4C5E875C0648C870003C637B /* TSocketMultiplexerMethodJob.h in Headers */, - 4C5E875E0648C870003C637B /* XSocket.h in Headers */, - 4C7D7CE1066319560097CA11 /* common.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB4386B063E47F800969041 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E87810648C8BD003C637B /* CClipboard.h in Headers */, - 689D733A095649AF00FFAB1D /* CKeyMap.h in Headers */, - 4C5E87830648C8BD003C637B /* CKeyState.h in Headers */, - 4C5E87840648C8BD003C637B /* ClipboardTypes.h in Headers */, - 4C5E87860648C8BD003C637B /* CPacketStreamFilter.h in Headers */, - 4C5E87880648C8BD003C637B /* CPlatformScreen.h in Headers */, - 4C5E878A0648C8BD003C637B /* CProtocolUtil.h in Headers */, - 4C5E878C0648C8BD003C637B /* CScreen.h in Headers */, - 4C5E878D0648C8BD003C637B /* IClient.h in Headers */, - 4C5E878F0648C8BD003C637B /* IClipboard.h in Headers */, - 4C5E87910648C8BD003C637B /* IKeyState.h in Headers */, - 4C5E87920648C8BD003C637B /* IPlatformScreen.h in Headers */, - 4C5E87940648C8BD003C637B /* IPrimaryScreen.h in Headers */, - 4C5E87960648C8BD003C637B /* IScreen.h in Headers */, - 4C5E87970648C8BD003C637B /* IScreenSaver.h in Headers */, - 4C5E87980648C8BD003C637B /* ISecondaryScreen.h in Headers */, - 4C5E87990648C8BD003C637B /* KeyTypes.h in Headers */, - 4C5E879A0648C8BD003C637B /* MouseTypes.h in Headers */, - 4C5E879B0648C8BD003C637B /* OptionTypes.h in Headers */, - 4C5E879C0648C8BD003C637B /* ProtocolTypes.h in Headers */, - 4C5E879E0648C8BD003C637B /* XScreen.h in Headers */, - 4C5E87A00648C8BD003C637B /* XSynergy.h in Headers */, - 4C7D7CE2066319560097CA11 /* common.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB438AE063E488600969041 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C7D7CE3066319560097CA11 /* common.h in Headers */, - 4C5E87AE0648C913003C637B /* COSXClipboard.h in Headers */, - 4CD034960650B6F100525ED1 /* COSXClipboardAnyTextConverter.h in Headers */, - 4CD034980650B6F100525ED1 /* COSXClipboardTextConverter.h in Headers */, - 4CD0349A0650B6F100525ED1 /* COSXClipboardUTF16Converter.h in Headers */, - 4C5E87B00648C913003C637B /* COSXEventQueueBuffer.h in Headers */, - 4C5E87B20648C913003C637B /* COSXKeyState.h in Headers */, - 4C5E87B40648C913003C637B /* COSXScreen.h in Headers */, - 68FBBB2D089F07920016DF44 /* COSXScreenSaver.h in Headers */, - 68FBBB2F089F079B0016DF44 /* COSXScreenSaverUtil.h in Headers */, - 6828A1C2081DF9EB003D9989 /* OSXScreenSaverControl.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43911063E497700969041 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E87C70648C9D2003C637B /* CClientTaskBarReceiver.h in Headers */, - 4C5E87C90648C9D2003C637B /* COSXClientTaskBarReceiver.h in Headers */, - 4C7D7CDB066319560097CA11 /* common.h in Headers */, - 68FBBB24089F06F20016DF44 /* COSXScreenSaverUtil.h in Headers */, - 68FBBB29089F072D0016DF44 /* COSXScreenSaver.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 4C537F0C064E286700D3815C /* synergys */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBB14089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergys" */; - buildPhases = ( - 4C537F0A064E286700D3815C /* Headers */, - 4C537F0B064E286700D3815C /* Sources */, - 4C537F5A064E2B0700D3815C /* Frameworks */, - ); - buildRules = ( - 68FBBB53089F08940016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - lib/client, - ); - PRODUCT_NAME = synergys; - }; - dependencies = ( - 4C537F1A064E298800D3815C /* PBXTargetDependency */, - 4C537F1C064E298800D3815C /* PBXTargetDependency */, - 4C537F1E064E298800D3815C /* PBXTargetDependency */, - 4C537F20064E298800D3815C /* PBXTargetDependency */, - 4C537F22064E298800D3815C /* PBXTargetDependency */, - 4C537F24064E298800D3815C /* PBXTargetDependency */, - 4C537F26064E298800D3815C /* PBXTargetDependency */, - 4C537F28064E298800D3815C /* PBXTargetDependency */, - 4C537F57064E2AA300D3815C /* PBXTargetDependency */, - ); - name = synergys; - productName = synergys; - productReference = 4C537F0D064E286700D3815C /* synergys */; - productType = "com.apple.product-type.tool"; - }; - 4C537F30064E29F800D3815C /* server */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBB10089F06C20016DF44 /* Build configuration list for PBXNativeTarget "server" */; - buildPhases = ( - 4C537F2E064E29F800D3815C /* Headers */, - 4C537F2F064E29F800D3815C /* Sources */, - ); - buildRules = ( - 68FBBB4D089F087F0016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - ); - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = server; - }; - dependencies = ( - ); - name = server; - productName = server; - productReference = 4C537F31064E29F800D3815C /* libserver.a */; - productType = "com.apple.product-type.library.static"; - }; - 4C5E868C0648C2ED003C637B /* common */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBAEC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "common" */; - buildPhases = ( - 4C5E868A0648C2ED003C637B /* Headers */, - 4C5E868B0648C2ED003C637B /* Sources */, - ); - buildRules = ( - 68FBBB37089F08160016DF44 /* PBXBuildRule */, - ); - buildSettings = { - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = common; - }; - dependencies = ( - ); - name = common; - productName = common; - productReference = 4C5E868D0648C2ED003C637B /* libcommon.a */; - productType = "com.apple.product-type.library.static"; - }; - 4CB43777063E406A00969041 /* client */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBB08089F06C20016DF44 /* Build configuration list for PBXNativeTarget "client" */; - buildPhases = ( - 4CB43775063E406A00969041 /* Headers */, - 4CB43776063E406A00969041 /* Sources */, - ); - buildRules = ( - 68FBBB4A089F08750016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - ); - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = client; - }; - dependencies = ( - ); - name = client; - productName = client; - productReference = 4CB43778063E406A00969041 /* libclient.a */; - productType = "com.apple.product-type.library.static"; - }; - 4CB437B4063E443800969041 /* arch */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBAE8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "arch" */; - buildPhases = ( - 4CB437B2063E443800969041 /* Headers */, - 4CB437B3063E443800969041 /* Sources */, - ); - buildRules = ( - 68FBBB32089F07E40016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = lib/common; - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = arch; - }; - dependencies = ( - ); - name = arch; - productName = arch; - productReference = 4CB437B5063E443800969041 /* libarch.a */; - productType = "com.apple.product-type.library.static"; - }; - 4CB437D0063E44C200969041 /* base */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBAF0089F06C20016DF44 /* Build configuration list for PBXNativeTarget "base" */; - buildPhases = ( - 4CB437CE063E44C200969041 /* Headers */, - 4CB437CF063E44C200969041 /* Sources */, - ); - buildRules = ( - 68FBBB3A089F08200016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - ); - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = base; - }; - dependencies = ( - ); - name = base; - productName = base; - productReference = 4CB437D1063E44C200969041 /* libbase.a */; - productType = "com.apple.product-type.library.static"; - }; - 4CB43800063E45F200969041 /* mt */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBAF4089F06C20016DF44 /* Build configuration list for PBXNativeTarget "mt" */; - buildPhases = ( - 4CB437FE063E45F200969041 /* Headers */, - 4CB437FF063E45F200969041 /* Sources */, - ); - buildRules = ( - 68FBBB3D089F082E0016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - ); - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = mt; - }; - dependencies = ( - ); - name = mt; - productName = mt; - productReference = 4CB43801063E45F200969041 /* libmt.a */; - productType = "com.apple.product-type.library.static"; - }; - 4CB43823063E46AB00969041 /* io */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBAF8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "io" */; - buildPhases = ( - 4CB43821063E46AB00969041 /* Headers */, - 4CB43822063E46AB00969041 /* Sources */, - ); - buildRules = ( - 68FBBB3E089F084C0016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - ); - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = io; - }; - dependencies = ( - ); - name = io; - productName = io; - productReference = 4CB43824063E46AB00969041 /* libio.a */; - productType = "com.apple.product-type.library.static"; - }; - 4CB43847063E475800969041 /* net */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBAFC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "net" */; - buildPhases = ( - 4CB43845063E475800969041 /* Headers */, - 4CB43846063E475800969041 /* Sources */, - ); - buildRules = ( - 68FBBB41089F08590016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - ); - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = net; - }; - dependencies = ( - ); - name = net; - productName = net; - productReference = 4CB43848063E475800969041 /* libnet.a */; - productType = "com.apple.product-type.library.static"; - }; - 4CB4386D063E47F800969041 /* synergy */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBB00089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergy" */; - buildPhases = ( - 4CB4386B063E47F800969041 /* Headers */, - 4CB4386C063E47F800969041 /* Sources */, - ); - buildRules = ( - 68FBBB44089F08620016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - ); - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = synergy; - }; - dependencies = ( - ); - name = synergy; - productName = synergy; - productReference = 4CB4386E063E47F800969041 /* libsynergy.a */; - productType = "com.apple.product-type.library.static"; - }; - 4CB438B0063E488600969041 /* platform */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBB04089F06C20016DF44 /* Build configuration list for PBXNativeTarget "platform" */; - buildPhases = ( - 4CB438AE063E488600969041 /* Headers */, - 4CB438AF063E488600969041 /* Sources */, - ); - buildRules = ( - 68FBBB47089F086B0016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/synergy, - ); - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = platform; - }; - dependencies = ( - ); - name = platform; - productName = platform; - productReference = 4CB438B1063E488600969041 /* libplatform.a */; - productType = "com.apple.product-type.library.static"; - }; - 4CB43913063E497700969041 /* synergyc */ = { - isa = PBXNativeTarget; - buildConfigurationList = 68FBBB0C089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergyc" */; - buildPhases = ( - 4CB43911063E497700969041 /* Headers */, - 4CB43912063E497700969041 /* Sources */, - 4CB43930063E4B4300969041 /* Frameworks */, - ); - buildRules = ( - 68FBBB50089F08890016DF44 /* PBXBuildRule */, - ); - buildSettings = { - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - lib/client, - ); - PRODUCT_NAME = synergyc; - }; - dependencies = ( - 4CB43948063E4E1600969041 /* PBXTargetDependency */, - 4C5E86900648C32E003C637B /* PBXTargetDependency */, - 4CB4394A063E4E1600969041 /* PBXTargetDependency */, - 4CB4394C063E4E1600969041 /* PBXTargetDependency */, - 4CB4394E063E4E1600969041 /* PBXTargetDependency */, - 4CB43950063E4E1600969041 /* PBXTargetDependency */, - 4CB43952063E4E1600969041 /* PBXTargetDependency */, - 4CB43954063E4E1600969041 /* PBXTargetDependency */, - 4CB43946063E4E1600969041 /* PBXTargetDependency */, - ); - name = synergyc; - productName = synergyc; - productReference = 4CB43914063E497700969041 /* synergyc */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = 68FBBB1C089F06C20016DF44 /* Build configuration list for PBXProject "synergy" */; - buildSettings = { - MACOSX_DEPLOYMENT_TARGET = 10.2; - SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; - }; - buildStyles = ( - 014CEA460018CE2711CA2923 /* Development */, - 014CEA470018CE2711CA2923 /* Deployment */, - ); - hasScannedForEncodings = 1; - mainGroup = 08FB7794FE84155DC02AAC07 /* synergy */; - projectDirPath = ""; - targets = ( - 4CD033E80650965F00525ED1 /* ALL */, - 4CB437B4063E443800969041 /* arch */, - 4C5E868C0648C2ED003C637B /* common */, - 4CB437D0063E44C200969041 /* base */, - 4CB43800063E45F200969041 /* mt */, - 4CB43823063E46AB00969041 /* io */, - 4CB43847063E475800969041 /* net */, - 4CB4386D063E47F800969041 /* synergy */, - 4CB438B0063E488600969041 /* platform */, - 4CB43777063E406A00969041 /* client */, - 4C537F30064E29F800D3815C /* server */, - 4CB43913063E497700969041 /* synergyc */, - 4C537F0C064E286700D3815C /* synergys */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 4C537F0B064E286700D3815C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C537F14064E293000D3815C /* COSXServerTaskBarReceiver.cpp in Sources */, - 4C537F16064E293000D3815C /* CServerTaskBarReceiver.cpp in Sources */, - 4C537F18064E293000D3815C /* synergys.cpp in Sources */, - 68FBBB22089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */, - 68FBBB2A089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */, - 68FBBB5C089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4C537F2F064E29F800D3815C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C537F44064E2A0F00D3815C /* CClientListener.cpp in Sources */, - 4C537F46064E2A0F00D3815C /* CClientProxy.cpp in Sources */, - 4C537F48064E2A0F00D3815C /* CClientProxy1_0.cpp in Sources */, - 4C537F4A064E2A0F00D3815C /* CClientProxy1_1.cpp in Sources */, - 4C537F4C064E2A0F00D3815C /* CClientProxy1_2.cpp in Sources */, - 684B63620996FB2800FE7CC7 /* CClientProxy1_3.cpp in Sources */, - 4C537F4E064E2A0F00D3815C /* CClientProxyUnknown.cpp in Sources */, - 4C537F50064E2A0F00D3815C /* CConfig.cpp in Sources */, - 689D73340956490500FFAB1D /* CInputFilter.cpp in Sources */, - 4C537F52064E2A0F00D3815C /* CPrimaryClient.cpp in Sources */, - 4C537F54064E2A0F00D3815C /* CServer.cpp in Sources */, - 688925A909DF64B6002EB18C /* CBaseClientProxy.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4C5E868B0648C2ED003C637B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E86CC0648C726003C637B /* Version.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43776063E406A00969041 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E87BD0648C969003C637B /* CClient.cpp in Sources */, - 4C5E87BF0648C969003C637B /* CServerProxy.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437B3063E443800969041 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E86A90648C412003C637B /* CArch.cpp in Sources */, - 4C5E86AB0648C412003C637B /* CArchConsoleUnix.cpp in Sources */, - 68968F5D073EC484004B2F9B /* CArchDaemonNone.cpp in Sources */, - 4C5E86AD0648C412003C637B /* CArchDaemonUnix.cpp in Sources */, - 4C5E86AF0648C412003C637B /* CArchFileUnix.cpp in Sources */, - 4C5E86B10648C412003C637B /* CArchLogUnix.cpp in Sources */, - 4C5E86B30648C412003C637B /* CArchMultithreadPosix.cpp in Sources */, - 4C5E86B50648C412003C637B /* CArchNetworkBSD.cpp in Sources */, - 4C5E86B70648C412003C637B /* CArchSleepUnix.cpp in Sources */, - 4C5E86B90648C412003C637B /* CArchStringUnix.cpp in Sources */, - 68871685073EBF7000C5ABE7 /* CArchSystemUnix.cpp in Sources */, - 68968F5E073EC484004B2F9B /* CArchTaskBarXWindows.cpp in Sources */, - 4C5E86BB0648C412003C637B /* CArchTimeUnix.cpp in Sources */, - 4C5E87D10648CA75003C637B /* XArch.cpp in Sources */, - 4C5E86BE0648C412003C637B /* XArchUnix.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437CF063E44C200969041 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E86ED0648C7B9003C637B /* CEvent.cpp in Sources */, - 4C5E86EF0648C7B9003C637B /* CEventQueue.cpp in Sources */, - 4C5E86F10648C7B9003C637B /* CFunctionEventJob.cpp in Sources */, - 4C5E86F30648C7B9003C637B /* CFunctionJob.cpp in Sources */, - 4C5E86F50648C7B9003C637B /* CLog.cpp in Sources */, - 4C5E86F80648C7B9003C637B /* CSimpleEventQueueBuffer.cpp in Sources */, - 4C5E86FA0648C7B9003C637B /* CStopwatch.cpp in Sources */, - 4C5E86FD0648C7B9003C637B /* CStringUtil.cpp in Sources */, - 4C5E86FF0648C7B9003C637B /* CUnicode.cpp in Sources */, - 4C5E87020648C7B9003C637B /* IEventQueue.cpp in Sources */, - 4C5E87070648C7B9003C637B /* LogOutputters.cpp in Sources */, - 4C5E870B0648C7B9003C637B /* XBase.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB437FF063E45F200969041 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E87180648C809003C637B /* CCondVar.cpp in Sources */, - 4C5E871A0648C809003C637B /* CLock.cpp in Sources */, - 4C5E871C0648C809003C637B /* CMutex.cpp in Sources */, - 4C5E871E0648C809003C637B /* CThread.cpp in Sources */, - 4C5E87200648C809003C637B /* XMT.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43822063E46AB00969041 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E872C0648C83C003C637B /* CStreamBuffer.cpp in Sources */, - 4C5E872E0648C83C003C637B /* CStreamFilter.cpp in Sources */, - 4C5E87300648C83C003C637B /* IStream.cpp in Sources */, - 4C5E87330648C83C003C637B /* XIO.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43846063E475800969041 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E874A0648C870003C637B /* CNetworkAddress.cpp in Sources */, - 4C5E874C0648C870003C637B /* CSocketMultiplexer.cpp in Sources */, - 4C5E874E0648C870003C637B /* CTCPListenSocket.cpp in Sources */, - 4C5E87500648C870003C637B /* CTCPSocket.cpp in Sources */, - 4C5E87520648C870003C637B /* CTCPSocketFactory.cpp in Sources */, - 4C5E87540648C870003C637B /* IDataSocket.cpp in Sources */, - 4C5E87560648C870003C637B /* IListenSocket.cpp in Sources */, - 4C5E87580648C870003C637B /* ISocket.cpp in Sources */, - 4C5E875D0648C870003C637B /* XSocket.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB4386C063E47F800969041 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E87800648C8BD003C637B /* CClipboard.cpp in Sources */, - 689D7339095649AF00FFAB1D /* CKeyMap.cpp in Sources */, - 4C5E87820648C8BD003C637B /* CKeyState.cpp in Sources */, - 4C5E87850648C8BD003C637B /* CPacketStreamFilter.cpp in Sources */, - 4C5E87870648C8BD003C637B /* CPlatformScreen.cpp in Sources */, - 4C5E87890648C8BD003C637B /* CProtocolUtil.cpp in Sources */, - 4C5E878B0648C8BD003C637B /* CScreen.cpp in Sources */, - 4C5E878E0648C8BD003C637B /* IClipboard.cpp in Sources */, - 4C5E87900648C8BD003C637B /* IKeyState.cpp in Sources */, - 4C5E87930648C8BD003C637B /* IPrimaryScreen.cpp in Sources */, - 4C5E87950648C8BD003C637B /* IScreen.cpp in Sources */, - 68D87A6509A00D8800856835 /* KeyTypes.cpp in Sources */, - 68FBBB5D089F1BCE0016DF44 /* ProtocolTypes.cpp in Sources */, - 4C5E879D0648C8BD003C637B /* XScreen.cpp in Sources */, - 4C5E879F0648C8BD003C637B /* XSynergy.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB438AF063E488600969041 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E87AD0648C913003C637B /* COSXClipboard.cpp in Sources */, - 4CD034950650B6F100525ED1 /* COSXClipboardAnyTextConverter.cpp in Sources */, - 4CD034970650B6F100525ED1 /* COSXClipboardTextConverter.cpp in Sources */, - 4CD034990650B6F100525ED1 /* COSXClipboardUTF16Converter.cpp in Sources */, - 4C5E87AF0648C913003C637B /* COSXEventQueueBuffer.cpp in Sources */, - 4C5E87B10648C913003C637B /* COSXKeyState.cpp in Sources */, - 4C5E87B30648C913003C637B /* COSXScreen.cpp in Sources */, - 68FBBB2C089F07810016DF44 /* COSXScreenSaver.cpp in Sources */, - 68FBBB2E089F07970016DF44 /* COSXScreenSaverUtil.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CB43912063E497700969041 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C5E87C60648C9D2003C637B /* CClientTaskBarReceiver.cpp in Sources */, - 4C5E87C80648C9D2003C637B /* COSXClientTaskBarReceiver.cpp in Sources */, - 4C5E87CA0648C9D2003C637B /* synergyc.cpp in Sources */, - 68FBBB21089F06DC0016DF44 /* COSXScreenSaverUtil.m in Sources */, - 68FBBB28089F072D0016DF44 /* COSXScreenSaver.cpp in Sources */, - 68FBBB5B089F1BA00016DF44 /* ProtocolTypes.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 4C537F1A064E298800D3815C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB437B4063E443800969041 /* arch */; - targetProxy = 4C537F19064E298800D3815C /* PBXContainerItemProxy */; - }; - 4C537F1C064E298800D3815C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4C5E868C0648C2ED003C637B /* common */; - targetProxy = 4C537F1B064E298800D3815C /* PBXContainerItemProxy */; - }; - 4C537F1E064E298800D3815C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB437D0063E44C200969041 /* base */; - targetProxy = 4C537F1D064E298800D3815C /* PBXContainerItemProxy */; - }; - 4C537F20064E298800D3815C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB43800063E45F200969041 /* mt */; - targetProxy = 4C537F1F064E298800D3815C /* PBXContainerItemProxy */; - }; - 4C537F22064E298800D3815C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB43823063E46AB00969041 /* io */; - targetProxy = 4C537F21064E298800D3815C /* PBXContainerItemProxy */; - }; - 4C537F24064E298800D3815C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB43847063E475800969041 /* net */; - targetProxy = 4C537F23064E298800D3815C /* PBXContainerItemProxy */; - }; - 4C537F26064E298800D3815C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB4386D063E47F800969041 /* synergy */; - targetProxy = 4C537F25064E298800D3815C /* PBXContainerItemProxy */; - }; - 4C537F28064E298800D3815C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB438B0063E488600969041 /* platform */; - targetProxy = 4C537F27064E298800D3815C /* PBXContainerItemProxy */; - }; - 4C537F57064E2AA300D3815C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4C537F30064E29F800D3815C /* server */; - targetProxy = 4C537F56064E2AA300D3815C /* PBXContainerItemProxy */; - }; - 4C5E86900648C32E003C637B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4C5E868C0648C2ED003C637B /* common */; - targetProxy = 4C5E868F0648C32E003C637B /* PBXContainerItemProxy */; - }; - 4CB43946063E4E1600969041 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB43777063E406A00969041 /* client */; - targetProxy = 4CB43945063E4E1600969041 /* PBXContainerItemProxy */; - }; - 4CB43948063E4E1600969041 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB437B4063E443800969041 /* arch */; - targetProxy = 4CB43947063E4E1600969041 /* PBXContainerItemProxy */; - }; - 4CB4394A063E4E1600969041 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB437D0063E44C200969041 /* base */; - targetProxy = 4CB43949063E4E1600969041 /* PBXContainerItemProxy */; - }; - 4CB4394C063E4E1600969041 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB43800063E45F200969041 /* mt */; - targetProxy = 4CB4394B063E4E1600969041 /* PBXContainerItemProxy */; - }; - 4CB4394E063E4E1600969041 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB43823063E46AB00969041 /* io */; - targetProxy = 4CB4394D063E4E1600969041 /* PBXContainerItemProxy */; - }; - 4CB43950063E4E1600969041 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB43847063E475800969041 /* net */; - targetProxy = 4CB4394F063E4E1600969041 /* PBXContainerItemProxy */; - }; - 4CB43952063E4E1600969041 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB4386D063E47F800969041 /* synergy */; - targetProxy = 4CB43951063E4E1600969041 /* PBXContainerItemProxy */; - }; - 4CB43954063E4E1600969041 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB438B0063E488600969041 /* platform */; - targetProxy = 4CB43953063E4E1600969041 /* PBXContainerItemProxy */; - }; - 4CD033EA0650968500525ED1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4CB43913063E497700969041 /* synergyc */; - targetProxy = 4CD033E90650968500525ED1 /* PBXContainerItemProxy */; - }; - 4CD033EC0650968500525ED1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4C537F0C064E286700D3815C /* synergys */; - targetProxy = 4CD033EB0650968500525ED1 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 68FBBAE9089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = lib/common; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = arch; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBAEA089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = lib/common; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = arch; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBAED089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = common; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBAEE089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = common; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBAF1089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = base; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBAF2089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = base; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBAF5089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = mt; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBAF6089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = mt; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBAF9089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = io; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBAFA089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = io; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBAFD089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = net; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBAFE089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = net; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBB01089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = synergy; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBB02089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = synergy; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBB05089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/synergy, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = platform; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBB06089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/synergy, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = platform; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBB09089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = client; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBB0A089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = client; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBB0D089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - lib/client, - ); - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = synergyc; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBB0E089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - lib/client, - ); - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = synergyc; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBB11089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = server; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBB12089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - ); - LIBRARY_STYLE = STATIC; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = server; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBB15089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - lib/client, - ); - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = synergys; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBB16089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - HEADER_SEARCH_PATHS = ( - lib/common, - lib/arch, - lib/base, - lib/mt, - lib/io, - lib/net, - lib/synergy, - lib/platform, - lib/client, - ); - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = synergys; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBB19089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUGGING_SYMBOLS = YES; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - MACOSX_DEPLOYMENT_TARGET = 10.2; - OPTIMIZATION_CFLAGS = "-O0"; - PRODUCT_NAME = ALL; - SYMROOT = gen/debug; - WARNING_CFLAGS = "-Wall"; - ZERO_LINK = NO; - }; - name = Development; - }; - 68FBBB1A089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; - MACOSX_DEPLOYMENT_TARGET = 10.2; - PRODUCT_NAME = ALL; - SYMROOT = gen/build; - ZERO_LINK = NO; - }; - name = Deployment; - }; - 68FBBB1D089F06C20016DF44 /* Development */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = ( - ppc, - i386, - ); - GCC_VERSION_i386 = 4.0; - GCC_VERSION_ppc = 3.3; - MACOSX_DEPLOYMENT_TARGET = 10.2; - MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; - MACOSX_DEPLOYMENT_TARGET_ppc = 10.2; - SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; - SDKROOT_i386 = /Developer/SDKs/MacOSX10.4u.sdk; - SDKROOT_ppc = /Developer/SDKs/MacOSX10.2.8.sdk; - }; - name = Development; - }; - 68FBBB1E089F06C20016DF44 /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = ( - ppc, - i386, - ); - GCC_VERSION_i386 = 4.0; - GCC_VERSION_ppc = 3.3; - MACOSX_DEPLOYMENT_TARGET = 10.2; - MACOSX_DEPLOYMENT_TARGET_i386 = 10.4; - MACOSX_DEPLOYMENT_TARGET_ppc = 10.2; - SDKROOT = /Developer/SDKs/MacOSX10.2.8.sdk; - SDKROOT_i386 = /Developer/SDKs/MacOSX10.4u.sdk; - SDKROOT_ppc = /Developer/SDKs/MacOSX10.2.8.sdk; - }; - name = Deployment; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 68FBBAE8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "arch" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBAE9089F06C20016DF44 /* Development */, - 68FBBAEA089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBAEC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "common" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBAED089F06C20016DF44 /* Development */, - 68FBBAEE089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBAF0089F06C20016DF44 /* Build configuration list for PBXNativeTarget "base" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBAF1089F06C20016DF44 /* Development */, - 68FBBAF2089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBAF4089F06C20016DF44 /* Build configuration list for PBXNativeTarget "mt" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBAF5089F06C20016DF44 /* Development */, - 68FBBAF6089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBAF8089F06C20016DF44 /* Build configuration list for PBXNativeTarget "io" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBAF9089F06C20016DF44 /* Development */, - 68FBBAFA089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBAFC089F06C20016DF44 /* Build configuration list for PBXNativeTarget "net" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBAFD089F06C20016DF44 /* Development */, - 68FBBAFE089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBB00089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergy" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBB01089F06C20016DF44 /* Development */, - 68FBBB02089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBB04089F06C20016DF44 /* Build configuration list for PBXNativeTarget "platform" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBB05089F06C20016DF44 /* Development */, - 68FBBB06089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBB08089F06C20016DF44 /* Build configuration list for PBXNativeTarget "client" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBB09089F06C20016DF44 /* Development */, - 68FBBB0A089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBB0C089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergyc" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBB0D089F06C20016DF44 /* Development */, - 68FBBB0E089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBB10089F06C20016DF44 /* Build configuration list for PBXNativeTarget "server" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBB11089F06C20016DF44 /* Development */, - 68FBBB12089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBB14089F06C20016DF44 /* Build configuration list for PBXNativeTarget "synergys" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBB15089F06C20016DF44 /* Development */, - 68FBBB16089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBB18089F06C20016DF44 /* Build configuration list for PBXAggregateTarget "ALL" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBB19089F06C20016DF44 /* Development */, - 68FBBB1A089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; - 68FBBB1C089F06C20016DF44 /* Build configuration list for PBXProject "synergy" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 68FBBB1D089F06C20016DF44 /* Development */, - 68FBBB1E089F06C20016DF44 /* Deployment */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; - }; -/* End XCConfigurationList section */ - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -} diff --git a/win32util/autodep.cpp b/win32util/autodep.cpp new file mode 100644 index 00000000..2a2e9d50 --- /dev/null +++ b/win32util/autodep.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include + +using namespace std; + +static +string +baseName(const string& filename) +{ + return filename.substr(0, filename.rfind('.')); +} + +static +int +writeMakefile(const string& dstdir, const set& depFilenames) +{ + string makeFilename = dstdir + "\\deps.mak"; + ofstream makeFile(makeFilename.c_str()); + if (!makeFile) { + cerr << "Can't open '" << makeFilename << "' for writing" << endl; + return 1; + } + + for (set::const_iterator i = depFilenames.begin(); + i != depFilenames.end(); ++i) { + makeFile << "!if EXIST(\"" << *i << "\")" << endl; + makeFile << "!include \"" << *i << "\"" << endl; + makeFile << "!endif" << endl; + } + + return 0; +} + +static +void +writeDependencies( + const string& filename, + const string& srcdir, + const string& dstdir, + const set& paths) +{ + string basename = baseName(filename); + string depFilename = dstdir + "\\" + basename + ".d"; + ofstream depFile(depFilename.c_str()); + if (!depFile) { + cerr << "Can't open '" << depFilename << "' for writing" << endl; + return; + } + + // Write dependencies rule for filename + depFile << "\"" << dstdir << "\\" << basename << ".obj\": \"" << + srcdir << "\\" << filename << "\" \\" << endl; + for (set::const_iterator i = paths.begin(); i != paths.end(); ++i) { + depFile << "\t\"" << *i << "\" \\" << endl; + } + depFile << "\t$(NULL)" << endl; +} + +static +int +writeDepfiles(const string& srcdir, const string& dstdir) +{ + const string includeLine = "Note: including file:"; + + // Parse stdin + string line; + string filename; + set paths; + locale loc = locale::classic(); + const ctype& ct = use_facet >(loc); + while (getline(cin, line)) { + bool echo = true; + + // Check for include line + if (line.compare(0, includeLine.length(), includeLine) == 0) { + // Strip includeLine and leading spaces + line.erase(0, line.find_first_not_of(" ", includeLine.length())); + if (line.length() == 0) { + continue; + } + + // Uppercase all drive letters + if (line.length() > 2 && line[1] == ':') { + line[0] = ct.toupper(line[0]); + } + + // Record path + paths.insert(line); + echo = false; + } + + // Maybe a source filename + else if (line.rfind(".cpp") == line.length() - 4) { + // Write dependencies for previous source file + if (filename.length() != 0) { + writeDependencies(filename, srcdir, dstdir, paths); + paths.clear(); + } + filename = line; + } + + // Otherwise other output + else { + // do nothing + } + + if (echo) { + cout << line << endl; + } + } + + // Write dependencies for last source file + if (filename.length() != 0) { + writeDependencies(filename, srcdir, dstdir, paths); + paths.clear(); + } + + return 0; +} + +int +main(int argc, char** argv) +{ + if (argc < 3) { + cerr << "usage: " << argv[0] << + " []" << endl; + return 1; + } + string srcdir = argv[1]; + string dstdir = argv[2]; + + // If depfiles were supplied then create a makefile in outdir to load + // all of them. + int result; + if (argc > 3) { + set depFilenames(argv + 3, argv + argc); + result = writeMakefile(dstdir, depFilenames); + } + + // Otherwise parse stdin and create a depfile for each listed file + else { + result = writeDepfiles(srcdir, dstdir); + } + + return result; +} From c169961986d6d4c01cd8b0615eb823979608807e Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:01:51 +0000 Subject: [PATCH 793/807] Fixed minor errors in .rc files. --- cmd/launcher/launcher.rc | 2 +- cmd/synergyc/synergyc.rc | 2 +- cmd/synergys/synergys.rc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 3f5ff0d7..6d2952e8 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -37,7 +37,7 @@ END 2 TEXTINCLUDE DISCARDABLE BEGIN - "#include ""afxres.h""\r\n" + "#include \r\n" "\0" END diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index 72d032ac..7f2a5dc1 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -37,7 +37,7 @@ END 2 TEXTINCLUDE DISCARDABLE BEGIN - "#include ""afxres.h""\r\n" + "#include \r\n" "\0" END diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index 97272ff2..d56a4313 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -37,7 +37,7 @@ END 2 TEXTINCLUDE DISCARDABLE BEGIN - "#include ""afxres.h""\r\n" + "#include \r\n" "\0" END From 5c7c2dbaa44966f818a1726fd8eaa23d08a49af7 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:01:55 +0000 Subject: [PATCH 794/807] Fixed makefile. Forgot to remove exec.dsp from EXTRA_DIST. --- cmd/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 2472175e..1e43b156 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -20,7 +20,6 @@ SUBDIRS = \ $(NULL) EXTRA_DIST = \ - exec.dsp \ $(NULL) MAINTAINERCLEANFILES = \ From 709261e2bf784d8fbe9d6218af9f811f761af5f0 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:02:01 +0000 Subject: [PATCH 795/807] Changed version to 1.3.2. --- lib/common/Version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/Version.h b/lib/common/Version.h index 1f98a6a2..43783c8c 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -19,7 +19,7 @@ // set version macro if not set yet #if !defined(VERSION) -# define VERSION "1.3.1" +# define VERSION "1.3.2" #endif // important strings From 617b93b984578c80cf6d21af3862c92e39c64c14 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:02:04 +0000 Subject: [PATCH 796/807] Fixed change log update for maruel's change. --- ChangeLog | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9099fd9f..5b101ca1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,12 +1,70 @@ -2007/07/01 crs -*.vcproj -*.vsprops -synergy.sln +2007/08/22 22:33:43 crs +all.dsp +ChangeLog +cmd/exec.dsp +cmd/launcher/launcher.dsp +cmd/launcher/launcher.rc +cmd/launcher/Makefile.am +cmd/launcher/nmake.mak +cmd/synergyc/Makefile.am +cmd/synergyc/nmake.mak +cmd/synergyc/synergyc.cpp +cmd/synergyc/synergyc.dsp +cmd/synergyc/synergyc.rc +cmd/synergys/Makefile.am +cmd/synergys/nmake.mak +cmd/synergys/synergys.cpp +cmd/synergys/synergys.dsp +cmd/synergys/synergys.rc +COPYING +dist/nullsoft/installer.dsp dist/nullsoft/installer.mak +dist/nullsoft/Makefile.am +dist/nullsoft/nmake.mak dist/nullsoft/synergy.nsi +doc/authors.html +lib/arch/arch.dsp +lib/arch/CArchDaemonNone.cpp +lib/arch/Makefile.am +lib/arch/nmake.mak +lib/base/base.dsp +lib/base/Makefile.am +lib/base/nmake.mak +lib/client/client.dsp +lib/client/Makefile.am +lib/client/nmake.mak +lib/common/common.dsp +lib/common/Makefile.am +lib/common/nmake.mak +lib/io/io.dsp +lib/io/Makefile.am +lib/io/nmake.mak +lib/mt/Makefile.am +lib/mt/mt.dsp +lib/mt/nmake.mak lib/net/CSocketMultiplexer.cpp lib/net/CTCPSocket.cpp +lib/net/Makefile.am +lib/net/net.dsp +lib/net/nmake.mak +lib/platform/CMSWindowsScreen.cpp lib/platform/CSynergyHook.cpp +lib/platform/Makefile.am +lib/platform/makehook.dsp +lib/platform/nmake.mak +lib/platform/platform.dsp +lib/platform/synrgyhk.dsp +lib/server/Makefile.am +lib/server/nmake.mak +lib/server/server.dsp +lib/synergy/libsynergy.dsp +lib/synergy/Makefile.am +lib/synergy/nmake.mak +Makefile.am +nmake.mak +synergy.dsw +synergy.xcode/project.pbxproj +win32util/autodep.cpp Applied patch by maruel: - Fixed taking the address of begin() on an empty std::vector. From 899beb69199ba6363794975058b1d965c5989c71 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:02:08 +0000 Subject: [PATCH 797/807] Applied patch from tailblues. It checks for a NULL result from CFStringCreate...() to avoid a crash in a subsequent use of that NULL pointer. --- lib/platform/COSXKeyState.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index ec6c68c9..2f94f283 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -793,6 +793,13 @@ COSXKeyState::CKeyResource::getKeyID(UInt8 c) str, GetScriptManagerVariable(smKeyScript), kCFAllocatorNull); + // sometimes CFStringCreate...() returns NULL (e.g. Apple Korean + // encoding with char value 214). if it did then make no key, + // otherwise CFStringCreateMutableCopy() will crash. + if (cfString == NULL) { + return kKeyNone; + } + // convert to precomposed CFMutableStringRef mcfString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString); From 460b751aba90ca78da0f54ad293bc6930fa262f1 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:02:13 +0000 Subject: [PATCH 798/807] Applied a number of patches. * HP-UX fixes (Bernd Noll) * Now handling accept() taking an int 3rd arg * Now casting sizeof() to socklen_t where necessary * BSD ports fix (Kristen Glynn) * Now defining NULL via stddef.h * Crash on X11 using clipboard (Daniel Gollub) * CXWindowsClipboard::pushReplies() increments iterator better * Solaris/HP-UX compile errors * Now #ifdef out sets of key syms if first key sym undefined * Fix assertion on bad mouse position (ubiquitous_q) * Validate mouse position from client and fix if necessary --- acinclude.m4 | 17 +++++++++++++++++ configure.in | 1 + lib/arch/CArchNetworkBSD.cpp | 22 ++++++++++++---------- lib/common/common.h | 4 +--- lib/platform/CXWindowsClipboard.cpp | 12 +++++++++--- lib/platform/CXWindowsClipboard.h | 2 +- lib/platform/CXWindowsUtil.cpp | 20 ++++++++++++++++++++ lib/server/CClientProxy1_0.cpp | 6 +++++- 8 files changed, 66 insertions(+), 18 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index e7666464..29163d5a 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -27,6 +27,23 @@ AC_DEFUN([ACX_CHECK_SOCKLEN_T], [ fi ])dnl ACX_CHECK_SOCKLEN_T +# HP-UX defines socklen_t but doesn't use it in arg 3 for accept(). +AC_DEFUN([ACX_FUNC_ACCEPT], [ + AC_MSG_CHECKING([for type of arg 3 for accept]) + acx_accept_socklen_t_arg3=int + if test x"$acx_socklen_t_ok" = xyes; then + AC_TRY_COMPILE([ + #include + #include + ], + [struct sockaddr addr; socklen_t len; accept(0, &addr, &len);], + [acx_accept_socklen_t_arg3=socklen_t], + [acx_accept_socklen_t_arg3=int]) + fi + AC_MSG_RESULT($acx_accept_socklen_t_arg3) + AC_DEFINE_UNQUOTED(ACCEPT_TYPE_ARG3,$acx_accept_socklen_t_arg3,[Define to the base type of arg 3 for `accept'.]) +])dnl ACX_FUNC_ACCEPT + AC_DEFUN([ACX_CHECK_CXX], [ AC_MSG_CHECKING([if g++ defines correct C++ macro]) AC_TRY_COMPILE(, [ diff --git a/configure.in b/configure.in index b33bf5f1..14367546 100644 --- a/configure.in +++ b/configure.in @@ -233,6 +233,7 @@ ACX_CHECK_GETPWUID_R AC_CHECK_FUNCS(vsnprintf) AC_FUNC_SELECT_ARGTYPES ACX_CHECK_POLL +ACX_FUNC_ACCEPT dnl use AC_REPLACE_FUNCS() for stuff in string.h dnl checks for system services diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 6b36cff5..af474e86 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -212,7 +212,9 @@ CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr) *addr = new CArchNetAddressImpl; // accept on socket - int fd = accept(s->m_fd, &(*addr)->m_addr, &(*addr)->m_len); + ACCEPT_TYPE_ARG3 len = (ACCEPT_TYPE_ARG3)((*addr)->m_len); + int fd = accept(s->m_fd, &(*addr)->m_addr, &len); + (*addr)->m_len = (socklen_t)len; if (fd == -1) { int err = errno; delete newSocket; @@ -528,7 +530,7 @@ CArchNetworkBSD::throwErrorOnSocket(CArchSocket s) // get the error from the socket layer int err = 0; - socklen_t size = sizeof(err); + socklen_t size = (socklen_t)sizeof(err); if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR, (optval_t*)&err, &size) == -1) { err = errno; @@ -567,14 +569,14 @@ CArchNetworkBSD::setNoDelayOnSocket(CArchSocket s, bool noDelay) // get old state int oflag; - socklen_t size = sizeof(oflag); + socklen_t size = (socklen_t)sizeof(oflag); if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, (optval_t*)&oflag, &size) == -1) { throwError(errno); } int flag = noDelay ? 1 : 0; - size = sizeof(flag); + size = (socklen_t)sizeof(flag); if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY, (optval_t*)&flag, size) == -1) { throwError(errno); @@ -590,14 +592,14 @@ CArchNetworkBSD::setReuseAddrOnSocket(CArchSocket s, bool reuse) // get old state int oflag; - socklen_t size = sizeof(oflag); + socklen_t size = (socklen_t)sizeof(oflag); if (getsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR, (optval_t*)&oflag, &size) == -1) { throwError(errno); } int flag = reuse ? 1 : 0; - size = sizeof(flag); + size = (socklen_t)sizeof(flag); if (setsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR, (optval_t*)&flag, size) == -1) { throwError(errno); @@ -633,7 +635,7 @@ CArchNetworkBSD::newAnyAddr(EAddressFamily family) ipAddr->sin_family = AF_INET; ipAddr->sin_port = 0; ipAddr->sin_addr.s_addr = INADDR_ANY; - addr->m_len = sizeof(struct sockaddr_in); + addr->m_len = (socklen_t)sizeof(struct sockaddr_in); break; } @@ -665,7 +667,7 @@ CArchNetworkBSD::nameToAddr(const std::string& name) memset(&inaddr, 0, sizeof(inaddr)); if (inet_aton(name.c_str(), &inaddr.sin_addr) != 0) { // it's a dot notation address - addr->m_len = sizeof(struct sockaddr_in); + addr->m_len = (socklen_t)sizeof(struct sockaddr_in); inaddr.sin_family = AF_INET; inaddr.sin_port = 0; memcpy(&addr->m_addr, &inaddr, addr->m_len); @@ -683,7 +685,7 @@ CArchNetworkBSD::nameToAddr(const std::string& name) // copy over address (only IPv4 currently supported) if (info->h_addrtype == AF_INET) { - addr->m_len = sizeof(struct sockaddr_in); + addr->m_len = (socklen_t)sizeof(struct sockaddr_in); inaddr.sin_family = info->h_addrtype; inaddr.sin_port = 0; memcpy(&inaddr.sin_addr, info->h_addr_list[0], @@ -819,7 +821,7 @@ CArchNetworkBSD::isAnyAddr(CArchNetAddress addr) struct sockaddr_in* ipAddr = reinterpret_cast(&addr->m_addr); return (ipAddr->sin_addr.s_addr == INADDR_ANY && - addr->m_len == sizeof(struct sockaddr_in)); + addr->m_len == (socklen_t)sizeof(struct sockaddr_in)); } default: diff --git a/lib/common/common.h b/lib/common/common.h index 9f51d4e3..e539a7d6 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -127,9 +127,7 @@ #endif // define NULL -#ifndef NULL -#define NULL 0 -#endif +#include // make assert available since we use it a lot #include diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 91399013..33252af9 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -929,16 +929,19 @@ CXWindowsClipboard::pushReplies() // send the first reply for each window if that reply hasn't // been sent yet. for (CReplyMap::iterator index = m_replies.begin(); - index != m_replies.end(); ++index) { + index != m_replies.end(); ) { assert(!index->second.empty()); if (!index->second.front()->m_replied) { pushReplies(index, index->second, index->second.begin()); } + else { + ++index; + } } } void -CXWindowsClipboard::pushReplies(CReplyMap::iterator mapIndex, +CXWindowsClipboard::pushReplies(CReplyMap::iterator& mapIndex, CReplyList& replies, CReplyList::iterator index) { CReply* reply = *index; @@ -959,9 +962,12 @@ CXWindowsClipboard::pushReplies(CReplyMap::iterator mapIndex, CXWindowsUtil::CErrorLock lock(m_display); Window requestor = mapIndex->first; XSelectInput(m_display, requestor, m_eventMasks[requestor]); - m_replies.erase(mapIndex); + m_replies.erase(mapIndex++); m_eventMasks.erase(requestor); } + else { + ++mapIndex; + } } bool diff --git a/lib/platform/CXWindowsClipboard.h b/lib/platform/CXWindowsClipboard.h index ba3c7b62..b36a6c8d 100644 --- a/lib/platform/CXWindowsClipboard.h +++ b/lib/platform/CXWindowsClipboard.h @@ -265,7 +265,7 @@ private: bool insertMultipleReply(Window, ::Time, Atom); void insertReply(CReply*); void pushReplies(); - void pushReplies(CReplyMap::iterator, + void pushReplies(CReplyMap::iterator&, CReplyList&, CReplyList::iterator); bool sendReply(CReply*); void clearReplies(); diff --git a/lib/platform/CXWindowsUtil.cpp b/lib/platform/CXWindowsUtil.cpp index 9478d655..a33fc4ee 100644 --- a/lib/platform/CXWindowsUtil.cpp +++ b/lib/platform/CXWindowsUtil.cpp @@ -188,6 +188,7 @@ struct codepair { { XK_uogonek, 0x0173 }, /* LATIN SMALL LETTER U WITH OGONEK */ { XK_utilde, 0x0169 }, /* LATIN SMALL LETTER U WITH TILDE */ { XK_umacron, 0x016b }, /* LATIN SMALL LETTER U WITH MACRON */ +#if defined(XK_Babovedot) { XK_Babovedot, 0x1e02 }, /* LATIN CAPITAL LETTER B WITH DOT ABOVE */ { XK_babovedot, 0x1e03 }, /* LATIN SMALL LETTER B WITH DOT ABOVE */ { XK_Dabovedot, 0x1e0a }, /* LATIN CAPITAL LETTER D WITH DOT ABOVE */ @@ -214,6 +215,8 @@ struct codepair { { XK_wcircumflex, 0x0175 }, /* LATIN SMALL LETTER W WITH CIRCUMFLEX */ { XK_tabovedot, 0x1e6b }, /* LATIN SMALL LETTER T WITH DOT ABOVE */ { XK_ycircumflex, 0x0177 }, /* LATIN SMALL LETTER Y WITH CIRCUMFLEX */ +#endif // defined(XK_Babovedot) +#if defined(XK_overline) { XK_overline, 0x203e }, /* OVERLINE */ { XK_kana_fullstop, 0x3002 }, /* IDEOGRAPHIC FULL STOP */ { XK_kana_openingbracket, 0x300c }, /* LEFT CORNER BRACKET */ @@ -278,6 +281,8 @@ struct codepair { { XK_kana_N, 0x30f3 }, /* KATAKANA LETTER N */ { XK_voicedsound, 0x309b }, /* KATAKANA-HIRAGANA VOICED SOUND MARK */ { XK_semivoicedsound, 0x309c }, /* KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ +#endif // defined(XK_overline) +#if defined(XK_Farsi_0) { XK_Farsi_0, 0x06f0 }, /* EXTENDED ARABIC-INDIC DIGIT 0 */ { XK_Farsi_1, 0x06f1 }, /* EXTENDED ARABIC-INDIC DIGIT 1 */ { XK_Farsi_2, 0x06f2 }, /* EXTENDED ARABIC-INDIC DIGIT 2 */ @@ -366,6 +371,8 @@ struct codepair { { XK_Arabic_farsi_yeh, 0x06cc }, /* ARABIC LETTER FARSI YEH */ { XK_Arabic_yeh_baree, 0x06d2 }, /* ARABIC LETTER YEH BAREE */ { XK_Arabic_heh_goal, 0x06c1 }, /* ARABIC LETTER HEH GOAL */ +#endif // defined(XK_Farsi_0) +#if defined(XK_Serbian_dje) { XK_Serbian_dje, 0x0452 }, /* CYRILLIC SMALL LETTER DJE */ { XK_Macedonia_gje, 0x0453 }, /* CYRILLIC SMALL LETTER GJE */ { XK_Cyrillic_io, 0x0451 }, /* CYRILLIC SMALL LETTER IO */ @@ -378,7 +385,9 @@ struct codepair { { XK_Cyrillic_nje, 0x045a }, /* CYRILLIC SMALL LETTER NJE */ { XK_Serbian_tshe, 0x045b }, /* CYRILLIC SMALL LETTER TSHE */ { XK_Macedonia_kje, 0x045c }, /* CYRILLIC SMALL LETTER KJE */ +#if defined(XK_Ukrainian_ghe_with_upturn) { XK_Ukrainian_ghe_with_upturn, 0x0491 }, /* CYRILLIC SMALL LETTER GHE WITH UPTURN */ +#endif { XK_Byelorussian_shortu, 0x045e }, /* CYRILLIC SMALL LETTER SHORT U */ { XK_Cyrillic_dzhe, 0x045f }, /* CYRILLIC SMALL LETTER DZHE */ { XK_numerosign, 0x2116 }, /* NUMERO SIGN */ @@ -394,7 +403,9 @@ struct codepair { { XK_Cyrillic_NJE, 0x040a }, /* CYRILLIC CAPITAL LETTER NJE */ { XK_Serbian_TSHE, 0x040b }, /* CYRILLIC CAPITAL LETTER TSHE */ { XK_Macedonia_KJE, 0x040c }, /* CYRILLIC CAPITAL LETTER KJE */ +#if defined(XK_Ukrainian_GHE_WITH_UPTURN) { XK_Ukrainian_GHE_WITH_UPTURN, 0x0490 }, /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ +#endif { XK_Byelorussian_SHORTU, 0x040e }, /* CYRILLIC CAPITAL LETTER SHORT U */ { XK_Cyrillic_DZHE, 0x040f }, /* CYRILLIC CAPITAL LETTER DZHE */ { XK_Cyrillic_yu, 0x044e }, /* CYRILLIC SMALL LETTER YU */ @@ -461,6 +472,8 @@ struct codepair { { XK_Cyrillic_SHCHA, 0x0429 }, /* CYRILLIC CAPITAL LETTER SHCHA */ { XK_Cyrillic_CHE, 0x0427 }, /* CYRILLIC CAPITAL LETTER CHE */ { XK_Cyrillic_HARDSIGN, 0x042a }, /* CYRILLIC CAPITAL LETTER HARD SIGN */ +#endif // defined(XK_Serbian_dje) +#if defined(XK_Greek_ALPHAaccent) { XK_Greek_ALPHAaccent, 0x0386 }, /* GREEK CAPITAL LETTER ALPHA WITH TONOS */ { XK_Greek_EPSILONaccent, 0x0388 }, /* GREEK CAPITAL LETTER EPSILON WITH TONOS */ { XK_Greek_ETAaccent, 0x0389 }, /* GREEK CAPITAL LETTER ETA WITH TONOS */ @@ -532,6 +545,7 @@ struct codepair { { XK_Greek_chi, 0x03c7 }, /* GREEK SMALL LETTER CHI */ { XK_Greek_psi, 0x03c8 }, /* GREEK SMALL LETTER PSI */ { XK_Greek_omega, 0x03c9 }, /* GREEK SMALL LETTER OMEGA */ +#endif // defined(XK_Greek_ALPHAaccent) { XK_leftradical, 0x23b7 }, /* ??? */ { XK_topleftradical, 0x250c }, /* BOX DRAWINGS LIGHT DOWN AND RIGHT */ { XK_horizconnector, 0x2500 }, /* BOX DRAWINGS LIGHT HORIZONTAL */ @@ -700,6 +714,7 @@ struct codepair { { XK_leftshoe, 0x2282 }, /* SUBSET OF */ { XK_lefttack, 0x22a2 }, /* RIGHT TACK */ { XK_righttack, 0x22a3 }, /* LEFT TACK */ +#if defined(XK_hebrew_doublelowline) { XK_hebrew_doublelowline, 0x2017 }, /* DOUBLE LOW LINE */ { XK_hebrew_aleph, 0x05d0 }, /* HEBREW LETTER ALEF */ { XK_hebrew_bet, 0x05d1 }, /* HEBREW LETTER BET */ @@ -728,6 +743,8 @@ struct codepair { { XK_hebrew_resh, 0x05e8 }, /* HEBREW LETTER RESH */ { XK_hebrew_shin, 0x05e9 }, /* HEBREW LETTER SHIN */ { XK_hebrew_taw, 0x05ea }, /* HEBREW LETTER TAV */ +#endif // defined(XK_hebrew_doublelowline) +#if defined(XK_Thai_kokai) { XK_Thai_kokai, 0x0e01 }, /* THAI CHARACTER KO KAI */ { XK_Thai_khokhai, 0x0e02 }, /* THAI CHARACTER KHO KHAI */ { XK_Thai_khokhuat, 0x0e03 }, /* THAI CHARACTER KHO KHUAT */ @@ -812,6 +829,8 @@ struct codepair { { XK_Thai_lekchet, 0x0e57 }, /* THAI DIGIT SEVEN */ { XK_Thai_lekpaet, 0x0e58 }, /* THAI DIGIT EIGHT */ { XK_Thai_lekkao, 0x0e59 }, /* THAI DIGIT NINE */ +#endif // defined(XK_Thai_kokai) +#if defined(XK_Hangul_Kiyeog) { XK_Hangul_Kiyeog, 0x3131 }, /* HANGUL LETTER KIYEOK */ { XK_Hangul_SsangKiyeog, 0x3132 }, /* HANGUL LETTER SSANGKIYEOK */ { XK_Hangul_KiyeogSios, 0x3133 }, /* HANGUL LETTER KIYEOK-SIOS */ @@ -903,6 +922,7 @@ struct codepair { { XK_Hangul_J_KkogjiDalrinIeung, 0x11f0 }, /* HANGUL JONGSEONG YESIEUNG */ { XK_Hangul_J_YeorinHieuh, 0x11f9 }, /* HANGUL JONGSEONG YEORINHIEUH */ { XK_Korean_Won, 0x20a9 }, /* WON SIGN */ +#endif // defined(XK_Hangul_Kiyeog) { XK_OE, 0x0152 }, /* LATIN CAPITAL LIGATURE OE */ { XK_oe, 0x0153 }, /* LATIN SMALL LIGATURE OE */ { XK_Ydiaeresis, 0x0178 }, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index d4e06bd2..b40372e5 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -401,12 +401,16 @@ CClientProxy1_0::recvInfo() &x, &y, &w, &h, &dummy1, &mx, &my)) { return false; } - LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d", getName().c_str(), x, y, w, h)); + LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d at %d,%d", getName().c_str(), x, y, w, h, mx, my)); // validate if (w <= 0 || h <= 0) { return false; } + if (mx < x || mx >= x + w || my < y || my >= y + h) { + mx = x + w / 2; + my = y + h / 2; + } // save m_info.m_x = x; From 1becc0f29ab9fb4b23e420f7175cc907e5f249a8 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:05:25 +0000 Subject: [PATCH 799/807] Removed files that should not be distributed. --- nodist/addheader | 96 ---------- nodist/copyright | 11 -- nodist/notes | 488 ----------------------------------------------- nodist/p4tolog | 50 ----- 4 files changed, 645 deletions(-) delete mode 100755 nodist/addheader delete mode 100644 nodist/copyright delete mode 100644 nodist/notes delete mode 100755 nodist/p4tolog diff --git a/nodist/addheader b/nodist/addheader deleted file mode 100755 index 3411aab0..00000000 --- a/nodist/addheader +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/sh -# add header to file. intended to add copyright info to files. -# -# usage: addheader - -# choose comment character -ext=`echo $2 | sed -e 's/.*\.\([^.]*\)$/\1/'` -case $ext in - c|cpp|h) - openComment='/*' - closeComment=' */' - innerComment=' * ' - ;; - am) - openComment='' - closeComment='' - innerComment='# ' - ;; - m4) - openComment='' - closeComment='' - innerComment='dnl ' - ;; - in) - if test $2 = "configure.in"; then - openComment='' - closeComment='' - innerComment='dnl ' - fi - ;; - *) - ;; -esac -if test -z "$innerComment"; then - echo Unrecognized extension $ext. Exiting. - exit 1 -fi - -# create awk program to strip old header. we have to use a heuristic -# to determine what's a header. we'll strip any continuous comment -# at the start of a file that includes a line with `Copyright'. -body=".$$.ahb" -ocMatch=`echo "$openComment" | sed -e 's/\*/\\\\*/g' -e 's#/#\\\\/#g'` -ccMatch=`echo "$closeComment" | sed -e 's/\*/\\\\*/g' -e 's/^ +/ */' -e 's#/#\\\\/#g'` -icMatch=`echo "$innerComment" | sed -e 's/\*/\\\\*/g' -e 's#/#\\\\/#g'` -awkGetHeader="m==4" -if test -n "$ocMatch"; then - awkGetHeader="$awkGetHeader;m==0 && /${ocMatch}/{m=1}" -else - awkGetHeader="BEGIN{m=1};$awkGetHeader" -fi -if test -n "$ccMatch"; then - awkGetHeader="$awkGetHeader;m==1 && /^${icMatch}/{m=2}" - awkGetHeader="$awkGetHeader;/${ccMatch}/{m=4}" -else - awkGetHeader="m==3 && !/^${icMatch}/{m=4};$awkGetHeader" - awkGetHeader="$awkGetHeader;m==1 && /^${icMatch}/{m=3}" -fi - -# strip old header -awk "$awkGetHeader" $2 > $body -if test $? -ne 0; then - rm -f $body - echo "can't parse file" -fi - -# if body is empty then there was no header -if test ! -s $body; then - cp $2 $body -else - fLength=`cat $2 | wc -l` - bLength=`cat $body | wc -l` - hLength=`expr $fLength - $bLength` - crLine=`grep -n -i "copyright" $2 | sed -e 's/:.*//'` - test -z "$crLine" && crLine=`expr $hLength + 1` - if test $crLine -gt $hLength; then - # no copyright in header - cp $2 $body - fi -fi - -# add new header -echo -n "" > $2 -if test -n "$openComment"; then - echo "$openComment" >> $2 -fi -cat $1 | sed -e "s/^/$innerComment/" >> $2 -if test -n "$closeComment"; then - echo "$closeComment" >> $2 -fi -head -1 $body | tr '\t' ' ' | grep "^ *$" > /dev/null -if test $? -eq 1; then - echo "" >> $2 -fi -cat $body >> $2 -rm -f $body diff --git a/nodist/copyright b/nodist/copyright deleted file mode 100644 index 9e64584b..00000000 --- a/nodist/copyright +++ /dev/null @@ -1,11 +0,0 @@ -synergy -- mouse and keyboard sharing utility -Copyright (C) 2002 Chris Schoeneman - -This package is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -found in the file COPYING that should have accompanied this file. - -This package is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. diff --git a/nodist/notes b/nodist/notes deleted file mode 100644 index ec9f5296..00000000 --- a/nodist/notes +++ /dev/null @@ -1,488 +0,0 @@ -edit FIXME's in: - PORTING - -make man pages - docbook? - should have makefile target to build these - remove line from BUGS about missing man pages - ---- -not removing all doxygen generated files in dist-clean or maintainer-clean - should remove them all in dist-clean - ---- -check for cancellation points: - following functions may be interrupted (returning -1, errno=EINTR): - read(), write(), ioctl(), select(), poll(), pause(), connect(), - accept(), syslog(), TCP operations - creat(), close(), getmsg(), putmsg(), msgrcv(), msgsnd(), - recv(), send(), wait(), waitpid(), wait3(), sigpause() - should ensure that we handle EINTR anywhere these are called. in - particular we must call testCancel() (should do that anyway) and - sometimes restart the call. - make sure unbounded time win32 calls do testCancel(). - ---- -not handling large motif selections - don't know protocol for transfer - ---- -not handling international characters - ---- -CServer::setConfig() should update network ports - if the server address or http address change then the old ports - should be closed and new ones opened. - ---- -try to determine X11 keyboard quirks automatically - in particular, set the half-duplex flags - maybe KBD extension will help determine this - -bug with half-duplex keys - win32 server sent num-lock to grace - grace converts to half-duplex so only synthesizes key down - now grace starts server and is locked to screen - grace should ignore half duplex keys when seeing if locked to screen - ---- -HTTP stuff - no way to save config using HTTP - should have a button or a page to force save of config - or just save config every time it's changed - should use authentication (at least basic) - provide way to kill/restart server via HTTP - provide way to query why locked to screen? - handy for debugging at least - - bug in updating screens - saw a goofy set of screens after update - i think the config was similar to: - A B - C - D E - ---- -win32: - need icon - provide taskbar icon - provide control panel? - -win32 service: - need message table for proper event reporting - -desktop switcher program failure when running synergy as service - returns access denied from CreateDesktop() - don't know why - ---- -hot keys - should have keyboard shortcuts to jump to screens - ---- -should switch to user nobody (or something similar) if running as root - for security reasons - will make it impossible to overwrite /etc/synergy.conf - if that file is only root writable (as it should be) - ---- -xscreensaver won't start if pointer on secondary screen - unless using MIT or SGI screen saver extensions - otherwise it cannot know when to stop the screen saver - cos it can't grab mouse and keyboard - ---- -all screensavers hidden when showing win32 screensaver lock window - win32 kills the screen saver and shows the lock window - synergy thinks the screen saver is dead (cos the process is) - deactivates secondary screen screen savers - user is constrained to locked display, though - also, we don't detect screen saver restarting if user doesn't unlock screen - ---- -not distinguishing between caps lock and shift lock - In Caps Lock mode, the alphabetic keys send their uppercase (shifted) char- - acter when pressed alone. For Caps Lock mode, pressing and releasing the - lock key turns on the mode and pressing and releasing the lock key again - turns off the mode. In Shift Lock mode, all keys on the main keypad send - their shifted character when pressed alone. For Shift Lock mode, pressing - and releasing the Lock key turns on the mode, and pressing and releasing - either the Lock or the Shift key turns off the mode. - ---- -need timeout on CXWindowsClipboard::CReply objects - should flush replies that are too old - assumption is that requestor is broken - ---- -avoid fullscreen transparent window on win32 - using CBT hook to discard activation/focus messages - but it's not being called when we lose keyboard input - also, we sometimes don't get the keyboard input to start with - ---- -keyboard hook problems (win32) - now passing keyboard events through in most cases - this fixes problem on isabel where toggle lights don't toggle - and num-lock behaved incorrectly (generated wrong keys). - seems the DefWindowProc() needed to process the keys - unfortunately, keys sometimes leak into wrong app. seems related - to times when we don't get the keyboard input. key events are - not delivered to app on primary until cursor returns to primary. - no idea how that's possible. - current have some code to check active window and reset it in - keyboard hook. that code doesn't work. - seem to be having problems with windows key now, too. looks like - a down is sent but not the corresponding up so secondary system - thinks the key is always down. - ---- -key events sent to console on win me (95/98?) are very slow - 1/4 to 1/2 second delay before key up is processed - key up log message is also delayed - console causing a system-wide delay? - ---- -adjust thread priorities on win32 - maybe remove changes altogether - currently isabel starts single-stepping mouse when dragging window - sometimes it goes normal speed but it's mostly very slow - even a very high priority doesn't seem to help - haven't tried real-time priority - a very high priority on client fixes delay when typing into console - is it the output console causing the slowness? - ---- -results: - X primary - ctrl+alt+keypad_add, ctrl+alt_keypad_minus -> eaten by primary X server - ctrl+alt+backspace probably eaten by primary X server if so configured - ctrl+alt+delete probably eaten locally - probably underneath primary X server too - Win32 desktop -> X laptop - eaten global hot keys: - ctrl+alt+delete -> task manager (or logon or reboot) - alt+[shift+]tab -> cycle through tasks - alt+[shift+]esc -> cycle windows - ctrl+esc -> start menu - windows+R -> run dialog - windows+M -> minimize all - windows+shift+M -> undo minimize all - windows+F1 -> help - windows+E -> explorer - windows+F -> find files - windows+ctrl+F -> find computer - windows+tab -> cycle through taskbar buttons - windows+break -> system properties - not eaten: - alt+space -> system menu - shift+F10 -> context menu - app key -> context menu - misc: - accessibility shortcuts: not eaten but dialogs appear anyway - X laptop -> Win32 desktop - check: - shift+tab -> okay - alt+[shift+]tab -> okay - alt+[shift+]esc -> okay - ctrl+esc -> start menu -> okay - ctrl+esc, esc, shift+F10 -> toolbar context menu -> okay - F10 -> activate menu in explorer -> okay - shift+F10 -> context menu -> okay - alt+space -> context menu -> okay - alt+hyphen -> MDI window menu -> okay - alt+letter -> open corresponding menu -> okay - alt+F6 -> switch between windows in single program -> okay - shift+insert CD-ROM -> bypass auto-run - check accessibility shortcuts -> does not work - check accessibility features - sticky keys -> no - filter keys -> no - toggle keys -> no - mouse keys -> no - high contrast -> no - X desktop -> win32 - check: - shift+tab - windows+R -> run dialog - windows+M -> minimize all - windows+shift+M -> minimize all - windows+F1 -> help - windows+E -> explorer - windows+F -> find files - windows+ctrl+F -> find computer - windows+tab -> cycle through taskbar buttons - windows+break -> system properties - app key -> context menu - Win32 -> Win32 - alt+F6 -> eaten (isabel to audrey, but not audrey to isabel) - check accessibility shortcuts work on secondary - check accessibility features work on secondary - ---- -Accessibility Shortcuts - - Key Result - ------------------------------------------------------------------- - Tap SHIFT 5 times Toggles StickyKeys on and off. - - Press down and hold the right Toggles FilterKeys on and off. - SHIFT key for 8 seconds - - Press down and hold the NUM LOCK Toggles ToggleKeys on and off. - key for 5 seconds - - Left ALT+left SHIFT+NUM LOCK Toggles MouseKeys on and off. - - Left ALT+left SHIFT+PRINT SCREEN Toggles High Contrast on and off. - ---- -disable ctrl+alt+del info: - SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0) - fools system into thinking screen saver is running - that disables ctrl+alt+del, alt+tab, ctrl+esc - dunno if keystrokes are queued - may be limited to win 95/98/me - win nt sp 3 and above: - low-level keyboard hook is called before ctrl+esc, alt+tab, alt+esc - use that to capture and disable - seems that low-level hook is notified of ctrl+alt+del but can't stop it - win nt sp 2 and below - ctrl+esc can be disabled by replacing task manager (dunno how) - RegisterHotKey() can be used to catch alt+tab and alt+esc - only while app is running, of course - win nt - keyboard filter driver can capture keys (see win NT DDK) - http://216.239.51.100/search?q=cache:q-f03UHhFMMC:www.thescarms.com/VBasic/StopReBoot.asp+alt%2Btab+disable&hl=en - some info on changing keyboard scan code mapping - can be used to disable keys but looks like reboot is required - ---- -Q179905 - suggestion for bypassing hook code when called by debugger. - may prevent system from getting locked up when debugging. - ---- -win32 rel notes: - HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows\NoInteractiveServices - can prevent an interactive service from running interactively. - how can we tell if we're not running interactively? - even if not interactive we can use MessageBox() to alert user - use MB_SERVICE_NOTIFICATION flag - must use a separate thread in initialization if called from handler - must close dialog if CTRL_LOGOFF_EVENT is received - see SetConsoleCtrlHandler() and CLog (which uses it) - maybe possible to get around NoInteractiveServices - modify DACLs on windows station/desktops to impersonate logged on user - see "Interacting with the User by a Win32 Service" - ---- -unix release notes: - restarting fails to connect until a user logs in: - Xlib: no protocol specified - this is caused by login manager - all clients are rejected, except those started by manager itself - workaround is to have synergy started by login manager - should then use --no-restart, though that's not necessary - affects client and server - cannot switch screens on login screen: - xdm,kdm grab keyboard for duration of login screen - synergy cannot switch unless it can grab keyboard - gdm doesn't appear to grab keyboard or mouse for duration - affects server, only - -=== feedback === -send all toggle states instead of (individual) toggle ups/downs (gspencer) - Actually, Teleffect already does that too, but what I meant was that - each time the state of a "toggle" key (caps lock, scroll lock, num lock) - changes on the master, I think it should force the client to take on - that state, instead of just toggling on the client. That way when Word - (on the client) helpfullly turns off the caps lock, it doesn't get - toggled back on when I turn off the caps lock on the master. Ideally, - it would just notice that Word turned off the caps lock on the client - and turn it off on the master (and all other clients) too, but I think - that's probably too complicated to implement. - -double clicking NT binaries fails (liz) - add note to README or use separate binaries for services. client - would also need a dialog to query options. server would probably - need one too. use dialogs if there are no command line args. - -how to exit (liz) - no good way to exit on windows - -win32 console disappears too quickly (liz) - maybe show a message box if exiting to due an error - -better support for reloading configuration (liz) - use SIGHUP on unix - use an app message on win32 - provide access through a control panel or taskbar icon - -non-functional on ctrl+alt+del screen in win2k (kurt) - i suppose it must be when win2k is the client. mouse input wasn't - working. keyboard probably didn't work either. - ---- -automake stuff - rebuild: - * 'touch NEWS README AUTHORS ChangeLog' - * 'touch stamp-h' - * 'aclocal' - creates aclocal.m4 - * 'autoheader' - creates config.h.in - * 'autoconf' - creates configure from configure.in - * 'automake' - Creates Makefile.in from Makefile.am - * './configure' - creates Makefile from Makefile.in - * 'make' - - funky functions: - snprintf -- ick! - some return negative value -- no problem - some return truncated length -- uh oh - some return buffer length without truncation -- huh? - some actually overrun the buffer -- must not build on those! - - use AC_LIBOBJ(function) for missing/broken functions - adds function.c to build - must add @LIBOBJS@ to *_LIBADD - or AC_REPLACE_FUNCS(function ...) - automatically calls AC_LIBOBJ for missing functions - use AC_CHECK_FUNC or AC_CHECK_FUNCS to detect missing functions - use AC_CHECK_LIB first if function not in std C lib - - AC_FUNC_MEMCMP -- adds memcmp.o to LIBOBJS if memcmp is broken - AC_FUNC_STRFTIME -- check for strftime(), define HAVE_STRFTIME - AC_FUNC_VPRINTF -- check for vprintf(), define HAVE_VPRINTF - also checks for _doprnt(), define HAVE_DOPRNT - - #if STDC_HEADERS - # include - #else - # if !HAVE_STRCHR - # define strchr index - # define strrchr rindex - # endif - char *strchr (), *strrchr (); - # if !HAVE_MEMCPY - # define memcpy(d, s, n) bcopy ((s), (d), (n)) - # define memmove(d, s, n) bcopy ((s), (d), (n)) - # endif - #endif - -must provide fallbacks for stuff: - non-reentrant functions: - put (shared) lock around non-reentrant function - provide reentrant API - gethostbyaddr - gethostbyname - getprotobyname - getprotobynumber - getservbyname - getservbyport - gmtime - getpwuid - strerror - string/memory functions: - memcpy - memcmp - memmove - memset - strcat - strcmp - strcpy - strrchr - -used library functions: - accept - bind - chdir - close - connect - dup - exit - fcntl -* fork - fprintf -* free - getenv - gethostbyaddr - gethostbyname - gethostname - getpeername - getprotobyname - getprotobynumber - getpwuid_r - getservbyname - getservbyport - getsockname - getsockopt - gettimeofday - getuid - inet_addr - inet_ntoa - ioctl - listen -* malloc - mbrtowc - mbsinit - memcpy -! memcmp - memmove - memset - nanosleep - open - openlog - poll - read - recv - recvfrom - send - sendto - setsid - setsockopt - shutdown - sigaction - sigaddset - sigemptyset - sigwait - socket - sprintf - strcat - strcmp - strcpy - strerror - strrchr - strtol - sysconf - syslog - umask - vsnprintf - wcrtomb - write - -included files - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -these put stuff in std::. replace with .h versions or use std::? - 3 #include - 3 #include - 1 #include - 3 #include - 4 #include - 12 #include - 1 #include - -can we use AC_REPLACE_FUNCS and only build the missing function files -in a single directory. do we have to have a separate configure.in -script to do that? diff --git a/nodist/p4tolog b/nodist/p4tolog deleted file mode 100755 index 43d0fe71..00000000 --- a/nodist/p4tolog +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh -# convert p4 logs to ChangeLog -# -# usage: p4tolog [file[revRange] ...] -# -# changes are written to stdout - -# location of perforce client -#P4=/home/perforce/bin/p4 -P4=/usr/local/bin/p4 - -prefix="//depot/project/synergy/" -while [ -n "$1" ]; do - case "$1" in - -p) - # get depot filespec prefix to strip - prefix="$2" - shift - ;; - *) - break - ;; - esac - shift -done - -# escape slashes in prefix -prefix=`echo $prefix | sed -e 's#/#\\\\/#g'` - -# get relevant changes -changes=`$P4 changes $* | sed -e 's/Change \([0-9]*\).*/\1/'` -if test -z "$changes"; then - echo "No changes" - exit 0 -fi - -# convert each change -for change in $changes; do - $P4 describe -s $change | head -1 | \ - sed -e 's/.*by \([^ @]*\)[^ ]* on \([^ ]*\) \([^ ]*\)/\2 \3 \1/' - $P4 describe -s $change | \ - awk 'p==1 && !/^$/;/^Affected/{p=1}' | \ - sed -e 's/^[^ ]* \([^#]*\)#.*$/\1/' | sed -e "s/^$prefix//" - echo - $P4 describe -s $change | \ - awk 'p==1 && !/^$/;/^$/{if (p==1) exit; else p=1}' | \ - sed -e 's/^.//' - echo - echo "----------" -done From 8f9896038dfb22627a639d549ef590862c73c2b0 Mon Sep 17 00:00:00 2001 From: crs23 Date: Thu, 6 Sep 2007 05:07:29 +0000 Subject: [PATCH 800/807] Renamed nmake.mak to Makefile.win everywhere. --- Makefile.am | 2 +- nmake.mak => Makefile.win | 2 +- cmd/launcher/Makefile.am | 2 +- cmd/launcher/{nmake.mak => Makefile.win} | 0 cmd/synergyc/Makefile.am | 2 +- cmd/synergyc/{nmake.mak => Makefile.win} | 0 cmd/synergys/Makefile.am | 2 +- cmd/synergys/{nmake.mak => Makefile.win} | 0 dist/nullsoft/Makefile.am | 2 +- dist/nullsoft/{nmake.mak => Makefile.win} | 0 doc/compiling.html | 4 ++-- lib/arch/Makefile.am | 2 +- lib/arch/{nmake.mak => Makefile.win} | 0 lib/base/Makefile.am | 2 +- lib/base/{nmake.mak => Makefile.win} | 0 lib/client/Makefile.am | 2 +- lib/client/{nmake.mak => Makefile.win} | 0 lib/common/Makefile.am | 2 +- lib/common/{nmake.mak => Makefile.win} | 0 lib/io/Makefile.am | 2 +- lib/io/{nmake.mak => Makefile.win} | 0 lib/mt/Makefile.am | 2 +- lib/mt/{nmake.mak => Makefile.win} | 0 lib/net/Makefile.am | 2 +- lib/net/{nmake.mak => Makefile.win} | 0 lib/platform/Makefile.am | 2 +- lib/platform/{nmake.mak => Makefile.win} | 0 lib/server/Makefile.am | 2 +- lib/server/{nmake.mak => Makefile.win} | 0 lib/synergy/Makefile.am | 2 +- lib/synergy/{nmake.mak => Makefile.win} | 0 31 files changed, 18 insertions(+), 18 deletions(-) rename nmake.mak => Makefile.win (99%) rename cmd/launcher/{nmake.mak => Makefile.win} (100%) rename cmd/synergyc/{nmake.mak => Makefile.win} (100%) rename cmd/synergys/{nmake.mak => Makefile.win} (100%) rename dist/nullsoft/{nmake.mak => Makefile.win} (100%) rename lib/arch/{nmake.mak => Makefile.win} (100%) rename lib/base/{nmake.mak => Makefile.win} (100%) rename lib/client/{nmake.mak => Makefile.win} (100%) rename lib/common/{nmake.mak => Makefile.win} (100%) rename lib/io/{nmake.mak => Makefile.win} (100%) rename lib/mt/{nmake.mak => Makefile.win} (100%) rename lib/net/{nmake.mak => Makefile.win} (100%) rename lib/platform/{nmake.mak => Makefile.win} (100%) rename lib/server/{nmake.mak => Makefile.win} (100%) rename lib/synergy/{nmake.mak => Makefile.win} (100%) diff --git a/Makefile.am b/Makefile.am index 566aacf7..26a5086f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,7 +21,7 @@ SUBDIRS = \ $(NULL) EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ examples/synergy.conf \ win32util/autodep.cpp \ $(NULL) diff --git a/nmake.mak b/Makefile.win similarity index 99% rename from nmake.mak rename to Makefile.win index 27a6e1cb..d232b58a 100644 --- a/nmake.mak +++ b/Makefile.win @@ -11,7 +11,7 @@ # GNU General Public License for more details. # Name of this file for recursive make -MAKEFILE = nmake.mak +MAKEFILE = Makefile.win # Default build is release is NODEBUG is defined, debug otherwise. !if !DEFINED(DEBUG) diff --git a/cmd/launcher/Makefile.am b/cmd/launcher/Makefile.am index 27c3cce6..6fc879e3 100644 --- a/cmd/launcher/Makefile.am +++ b/cmd/launcher/Makefile.am @@ -36,7 +36,7 @@ MSWINDOWS_SOURCE_FILES = \ $(NULL) EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ synergy.ico \ $(MSWINDOWS_SOURCE_FILES) \ $(NULL) diff --git a/cmd/launcher/nmake.mak b/cmd/launcher/Makefile.win similarity index 100% rename from cmd/launcher/nmake.mak rename to cmd/launcher/Makefile.win diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 70360e77..b6e8c83d 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -34,7 +34,7 @@ CARBON_SOURCE_FILES = \ $(NULL) EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ synergyc.ico \ tb_error.ico \ tb_idle.ico \ diff --git a/cmd/synergyc/nmake.mak b/cmd/synergyc/Makefile.win similarity index 100% rename from cmd/synergyc/nmake.mak rename to cmd/synergyc/Makefile.win diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index c1a0dee1..4ad48ce5 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -34,7 +34,7 @@ CARBON_SOURCE_FILES = \ $(NULL) EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ synergys.ico \ tb_error.ico \ tb_idle.ico \ diff --git a/cmd/synergys/nmake.mak b/cmd/synergys/Makefile.win similarity index 100% rename from cmd/synergys/nmake.mak rename to cmd/synergys/Makefile.win diff --git a/dist/nullsoft/Makefile.am b/dist/nullsoft/Makefile.am index 968f23cf..120cd016 100644 --- a/dist/nullsoft/Makefile.am +++ b/dist/nullsoft/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ synergy.nsi \ dosify.c \ $(NULL) diff --git a/dist/nullsoft/nmake.mak b/dist/nullsoft/Makefile.win similarity index 100% rename from dist/nullsoft/nmake.mak rename to dist/nullsoft/Makefile.win diff --git a/doc/compiling.html b/doc/compiling.html index 47772be6..69321913 100644 --- a/doc/compiling.html +++ b/doc/compiling.html @@ -59,7 +59,7 @@ so synergy can find the X11 includes and libraries. search to find it.) It's necessary to run the file if you didn't have the installer set up environment variables for you. Then enter:

            -  nmake /nologo /f nmake.mak
            +  nmake /nologo /f Makefile.win
               
            This will build the programs into build\Release.

            @@ -82,7 +82,7 @@ so synergy can find the X11 includes and libraries. the Nullsoft Scriptable Install System. As in the building on Windows description above, enter:
            -  nmake /nologo /f nmake.mak installer
            +  nmake /nologo /f Makefile.win installer
               
            to build build\Release\SynergyInstaller.exe. Run this to install synergy. diff --git a/lib/arch/Makefile.am b/lib/arch/Makefile.am index 049c3146..8a1749ab 100644 --- a/lib/arch/Makefile.am +++ b/lib/arch/Makefile.am @@ -90,7 +90,7 @@ WIN32_SOURCE_FILES = \ EXTRA_DIST = \ CMultibyte.cpp \ - nmake.mak \ + Makefile.win \ vsnprintf.cpp \ $(UNIX_SOURCE_FILES) \ $(WIN32_SOURCE_FILES) \ diff --git a/lib/arch/nmake.mak b/lib/arch/Makefile.win similarity index 100% rename from lib/arch/nmake.mak rename to lib/arch/Makefile.win diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index 4fec44d8..a53c8b33 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/base/nmake.mak b/lib/base/Makefile.win similarity index 100% rename from lib/base/nmake.mak rename to lib/base/Makefile.win diff --git a/lib/client/Makefile.am b/lib/client/Makefile.am index 40caacc2..4ea5a230 100644 --- a/lib/client/Makefile.am +++ b/lib/client/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/client/nmake.mak b/lib/client/Makefile.win similarity index 100% rename from lib/client/nmake.mak rename to lib/client/Makefile.win diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index b81d4e42..74804748 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ BasicTypes.h \ IInterface.h \ MacOSXPrecomp.h \ diff --git a/lib/common/nmake.mak b/lib/common/Makefile.win similarity index 100% rename from lib/common/nmake.mak rename to lib/common/Makefile.win diff --git a/lib/io/Makefile.am b/lib/io/Makefile.am index 9c849b73..fec1dd31 100644 --- a/lib/io/Makefile.am +++ b/lib/io/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/io/nmake.mak b/lib/io/Makefile.win similarity index 100% rename from lib/io/nmake.mak rename to lib/io/Makefile.win diff --git a/lib/mt/Makefile.am b/lib/mt/Makefile.am index beae3df9..fbf01c03 100644 --- a/lib/mt/Makefile.am +++ b/lib/mt/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/mt/nmake.mak b/lib/mt/Makefile.win similarity index 100% rename from lib/mt/nmake.mak rename to lib/mt/Makefile.win diff --git a/lib/net/Makefile.am b/lib/net/Makefile.am index c3ff4c34..b49fe363 100644 --- a/lib/net/Makefile.am +++ b/lib/net/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/net/nmake.mak b/lib/net/Makefile.win similarity index 100% rename from lib/net/nmake.mak rename to lib/net/Makefile.win diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 3edf1be9..6ff0523c 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -92,7 +92,7 @@ CARBON_SOURCE_FILES = \ $(NULL) EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ $(XWINDOWS_SOURCE_FILES) \ $(MSWINDOWS_SOURCE_FILES) \ $(MSWINDOWS_HOOK_SOURCE_FILES) \ diff --git a/lib/platform/nmake.mak b/lib/platform/Makefile.win similarity index 100% rename from lib/platform/nmake.mak rename to lib/platform/Makefile.win diff --git a/lib/server/Makefile.am b/lib/server/Makefile.am index d435aca8..e03d9b7c 100644 --- a/lib/server/Makefile.am +++ b/lib/server/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/server/nmake.mak b/lib/server/Makefile.win similarity index 100% rename from lib/server/nmake.mak rename to lib/server/Makefile.win diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index 03979e64..480102a4 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -14,7 +14,7 @@ NULL = EXTRA_DIST = \ - nmake.mak \ + Makefile.win \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/lib/synergy/nmake.mak b/lib/synergy/Makefile.win similarity index 100% rename from lib/synergy/nmake.mak rename to lib/synergy/Makefile.win From 766aaf7b9b765ad455afa91f7c83825e41aeb423 Mon Sep 17 00:00:00 2001 From: crs23 Date: Sat, 8 Sep 2007 21:57:37 +0000 Subject: [PATCH 801/807] Applied Hangul/Hanja patch. The patch was pretty old so this is a best guess application and it may not work. --- lib/platform/CMSWindowsKeyState.cpp | 10 +++++----- lib/synergy/KeyTypes.h | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index 2baba7d0..dbf7396d 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -56,11 +56,11 @@ const KeyID CMSWindowsKeyState::s_virtualKey[] = /* 0x012 */ { kKeyAlt_L }, // VK_MENU /* 0x013 */ { kKeyPause }, // VK_PAUSE /* 0x014 */ { kKeyCapsLock }, // VK_CAPITAL - /* 0x015 */ { kKeyNone }, // VK_KANA - /* 0x016 */ { kKeyNone }, // VK_HANGUL - /* 0x017 */ { kKeyNone }, // VK_JUNJA - /* 0x018 */ { kKeyNone }, // VK_FINAL - /* 0x019 */ { kKeyZenkaku }, // VK_KANJI + /* 0x015 */ { kKeyHangulKana }, // VK_HANGUL, VK_KANA + /* 0x016 */ { kKeyNone }, // undefined + /* 0x017 */ { kKeyNone }, // VK_JUNJA + /* 0x018 */ { kKeyNone }, // VK_FINAL + /* 0x019 */ { kKeyHanjaKanzi }, // VK_KANJI /* 0x01a */ { kKeyNone }, // undefined /* 0x01b */ { kKeyEscape }, // VK_ESCAPE /* 0x01c */ { kKeyHenkan }, // VK_CONVERT diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 039f9030..1483e0d7 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -104,7 +104,9 @@ static const KeyID kKeyScrollLock = 0xEF14; static const KeyID kKeySysReq = 0xEF15; static const KeyID kKeyEscape = 0xEF1B; static const KeyID kKeyHenkan = 0xEF23; /* Start/Stop Conversion */ +static const KeyID kKeyHangulKana = 0xEF26; /* Hangul, Kana */ static const KeyID kKeyZenkaku = 0xEF2A; /* Zenkaku/Hankaku */ +static const KeyID kKeyHanjaKanzi = 0xEF2A; /* Hanja, Kanzi */ static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ // cursor control From 4baf785907f4db591d6dcde7cb02824ddf4a1096 Mon Sep 17 00:00:00 2001 From: crs23 Date: Sun, 9 Sep 2007 16:12:59 +0000 Subject: [PATCH 802/807] Applied patch 1468436, Japanese keyboard support in Windows. --- lib/platform/CMSWindowsKeyState.cpp | 12 ++++++------ lib/synergy/KeyTypes.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/platform/CMSWindowsKeyState.cpp b/lib/platform/CMSWindowsKeyState.cpp index dbf7396d..ba105530 100644 --- a/lib/platform/CMSWindowsKeyState.cpp +++ b/lib/platform/CMSWindowsKeyState.cpp @@ -277,9 +277,9 @@ const KeyID CMSWindowsKeyState::s_virtualKey[] = /* 0x0ef */ { kKeyNone }, // OEM specific /* 0x0f0 */ { kKeyNone }, // OEM specific /* 0x0f1 */ { kKeyNone }, // OEM specific - /* 0x0f2 */ { kKeyNone }, // OEM specific - /* 0x0f3 */ { kKeyNone }, // OEM specific - /* 0x0f4 */ { kKeyNone }, // OEM specific + /* 0x0f2 */ { kKeyHiraganaKatakana }, // VK_OEM_COPY + /* 0x0f3 */ { kKeyZenkaku }, // VK_OEM_AUTO + /* 0x0f4 */ { kKeyZenkaku }, // VK_OEM_ENLW /* 0x0f5 */ { kKeyNone }, // OEM specific /* 0x0f6 */ { kKeyNone }, // VK_ATTN /* 0x0f7 */ { kKeyNone }, // VK_CRSEL @@ -534,9 +534,9 @@ const KeyID CMSWindowsKeyState::s_virtualKey[] = /* 0x1ef */ { kKeyNone }, // OEM specific /* 0x1f0 */ { kKeyNone }, // OEM specific /* 0x1f1 */ { kKeyNone }, // OEM specific - /* 0x1f2 */ { kKeyNone }, // OEM specific - /* 0x1f3 */ { kKeyNone }, // OEM specific - /* 0x1f4 */ { kKeyNone }, // OEM specific + /* 0x1f2 */ { kKeyNone }, // VK_OEM_COPY + /* 0x1f3 */ { kKeyNone }, // VK_OEM_AUTO + /* 0x1f4 */ { kKeyNone }, // VK_OEM_ENLW /* 0x1f5 */ { kKeyNone }, // OEM specific /* 0x1f6 */ { kKeyNone }, // VK_ATTN /* 0x1f7 */ { kKeyNone }, // VK_CRSEL diff --git a/lib/synergy/KeyTypes.h b/lib/synergy/KeyTypes.h index 1483e0d7..c8b4f6b8 100644 --- a/lib/synergy/KeyTypes.h +++ b/lib/synergy/KeyTypes.h @@ -105,6 +105,7 @@ static const KeyID kKeySysReq = 0xEF15; static const KeyID kKeyEscape = 0xEF1B; static const KeyID kKeyHenkan = 0xEF23; /* Start/Stop Conversion */ static const KeyID kKeyHangulKana = 0xEF26; /* Hangul, Kana */ +static const KeyID kKeyHiraganaKatakana = 0xEF27; /* Hiragana/Katakana toggle */ static const KeyID kKeyZenkaku = 0xEF2A; /* Zenkaku/Hankaku */ static const KeyID kKeyHanjaKanzi = 0xEF2A; /* Hanja, Kanzi */ static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ From 8896add372803d3515d280707583c7d450e87e5a Mon Sep 17 00:00:00 2001 From: crs23 Date: Sun, 9 Sep 2007 19:24:57 +0000 Subject: [PATCH 803/807] Applied patch 1528302, number pad keys in OS X. Also added support for NumLock key. --- lib/platform/COSXKeyState.cpp | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/platform/COSXKeyState.cpp b/lib/platform/COSXKeyState.cpp index 2f94f283..e904d5e4 100644 --- a/lib/platform/COSXKeyState.cpp +++ b/lib/platform/COSXKeyState.cpp @@ -32,6 +32,8 @@ static const UInt32 s_controlVK = 59; static const UInt32 s_altVK = 55; static const UInt32 s_superVK = 58; static const UInt32 s_capsLockVK = 57; +static const UInt32 s_numLockVK = 71; +static const UInt32 s_osxNumLock = 1 << 16; struct CKeyEntry { public: KeyID m_keyID; @@ -68,6 +70,24 @@ static const CKeyEntry s_controlKeys[] = { { kKeyF15, 113 }, { kKeyF16, 106 }, + { kKeyKP_0, 82 }, + { kKeyKP_1, 83 }, + { kKeyKP_2, 84 }, + { kKeyKP_3, 85 }, + { kKeyKP_4, 86 }, + { kKeyKP_5, 87 }, + { kKeyKP_6, 88 }, + { kKeyKP_7, 89 }, + { kKeyKP_8, 91 }, + { kKeyKP_9, 92 }, + { kKeyKP_Decimal, 65 }, + { kKeyKP_Equal, 81 }, + { kKeyKP_Multiply, 67 }, + { kKeyKP_Add, 69 }, + { kKeyKP_Divide, 75 }, + { kKeyKP_Subtract, 79 }, + { kKeyKP_Enter, 76 }, + // virtual key 110 is fn+enter and i have no idea what that's supposed // to map to. also the enter key with numlock on is a modifier but i // don't know which. @@ -86,7 +106,7 @@ static const CKeyEntry s_controlKeys[] = { { kKeyMeta_R, s_superVK }, // 61 // toggle modifiers -// { kKeyNumLock, 71 }, + { kKeyNumLock, s_numLockVK }, { kKeyCapsLock, s_capsLockVK } }; @@ -117,6 +137,7 @@ COSXKeyState::~COSXKeyState() KeyModifierMask COSXKeyState::mapModifiersFromOSX(UInt32 mask) const { +LOG((CLOG_DEBUG1 "mask: %04x", mask)); // convert KeyModifierMask outMask = 0; if ((mask & shiftKey) != 0) { @@ -143,6 +164,9 @@ COSXKeyState::mapModifiersFromOSX(UInt32 mask) const if ((mask & alphaLock) != 0) { outMask |= KeyModifierCapsLock; } + if ((mask & s_osxNumLock) != 0) { + outMask |= KeyModifierNumLock; + } return outMask; } @@ -572,6 +596,9 @@ COSXKeyState::mapSynergyHotKeyToMac(KeyID key, KeyModifierMask mask, if ((mask & KeyModifierCapsLock) != 0) { macModifierMask |= alphaLock; } + if ((mask & KeyModifierNumLock) != 0) { + macModifierMask |= s_osxNumLock; + } return true; } @@ -604,6 +631,10 @@ COSXKeyState::handleModifierKeys(void* target, handleModifierKey(target, s_capsLockVK, kKeyCapsLock, (newMask & KeyModifierCapsLock) != 0, newMask); } + if ((changed & KeyModifierNumLock) != 0) { + handleModifierKey(target, s_numLockVK, kKeyNumLock, + (newMask & KeyModifierNumLock) != 0, newMask); + } } void From d001ca488a3715083c5234a5a40f0f522aaf6899 Mon Sep 17 00:00:00 2001 From: crs23 Date: Sun, 9 Sep 2007 20:48:03 +0000 Subject: [PATCH 804/807] Applied patch 1677756, win64 patch. This replaces calls to the obsoleted {GS}etWindowsLong() with {GS}etWindowsLongPtr(). These latter functions are supported from Win95 and WinNT 3.1 so they shouldn't introduce any compatibility problems. --- cmd/launcher/CAddScreen.cpp | 2 +- cmd/launcher/CAdvancedOptions.cpp | 2 +- cmd/launcher/CAutoStart.cpp | 2 +- cmd/launcher/CGlobalOptions.cpp | 2 +- cmd/launcher/CHotkeyOptions.cpp | 2 +- cmd/launcher/CInfo.cpp | 2 +- cmd/launcher/CScreensLinks.cpp | 2 +- cmd/launcher/launcher.cpp | 2 +- cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp | 6 +++--- cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp | 10 +++++----- lib/platform/CMSWindowsScreenSaver.cpp | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cmd/launcher/CAddScreen.cpp b/cmd/launcher/CAddScreen.cpp index bc65f283..5a876b77 100644 --- a/cmd/launcher/CAddScreen.cpp +++ b/cmd/launcher/CAddScreen.cpp @@ -70,7 +70,7 @@ CAddScreen::doModal() { // do dialog return (DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADD), - m_parent, dlgProc, (LPARAM)this) != 0); + m_parent, (DLGPROC)dlgProc, (LPARAM)this) != 0); } CString diff --git a/cmd/launcher/CAdvancedOptions.cpp b/cmd/launcher/CAdvancedOptions.cpp index ee5b45ea..c1ea83ef 100644 --- a/cmd/launcher/CAdvancedOptions.cpp +++ b/cmd/launcher/CAdvancedOptions.cpp @@ -54,7 +54,7 @@ CAdvancedOptions::doModal(bool isClient) // do dialog DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_ADVANCED_OPTIONS), - m_parent, dlgProc, (LPARAM)this); + m_parent, (DLGPROC)dlgProc, (LPARAM)this); } CString diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp index 7b390a58..eb18a6b0 100644 --- a/cmd/launcher/CAutoStart.cpp +++ b/cmd/launcher/CAutoStart.cpp @@ -87,7 +87,7 @@ CAutoStart::doModal() // do dialog DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_AUTOSTART), - m_parent, dlgProc, (LPARAM)this); + m_parent, (DLGPROC)dlgProc, (LPARAM)this); // remove log outputter CLOG->pop_front(); diff --git a/cmd/launcher/CGlobalOptions.cpp b/cmd/launcher/CGlobalOptions.cpp index b7c15801..8237a07f 100644 --- a/cmd/launcher/CGlobalOptions.cpp +++ b/cmd/launcher/CGlobalOptions.cpp @@ -50,7 +50,7 @@ CGlobalOptions::doModal() { // do dialog DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_GLOBAL_OPTIONS), - m_parent, dlgProc, (LPARAM)this); + m_parent, (DLGPROC)dlgProc, (LPARAM)this); } void diff --git a/cmd/launcher/CHotkeyOptions.cpp b/cmd/launcher/CHotkeyOptions.cpp index 7e7a1b56..5b71d4ae 100644 --- a/cmd/launcher/CHotkeyOptions.cpp +++ b/cmd/launcher/CHotkeyOptions.cpp @@ -53,7 +53,7 @@ CHotkeyOptions::doModal() // do dialog m_inputFilter = m_config->getInputFilter(); DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_OPTIONS), - m_parent, dlgProc, (LPARAM)this); + m_parent, (DLGPROC)dlgProc, (LPARAM)this); } void diff --git a/cmd/launcher/CInfo.cpp b/cmd/launcher/CInfo.cpp index f2292db8..669da0e2 100644 --- a/cmd/launcher/CInfo.cpp +++ b/cmd/launcher/CInfo.cpp @@ -43,7 +43,7 @@ CInfo::doModal() { // do dialog DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_INFO), - m_parent, dlgProc, (LPARAM)this); + m_parent, (DLGPROC)dlgProc, (LPARAM)this); } void diff --git a/cmd/launcher/CScreensLinks.cpp b/cmd/launcher/CScreensLinks.cpp index dbbca67b..c7e58a04 100644 --- a/cmd/launcher/CScreensLinks.cpp +++ b/cmd/launcher/CScreensLinks.cpp @@ -59,7 +59,7 @@ CScreensLinks::doModal() { // do dialog DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_SCREENS_LINKS), - m_parent, dlgProc, (LPARAM)this); + m_parent, (DLGPROC)dlgProc, (LPARAM)this); } void diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 07eab676..938822da 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -379,7 +379,7 @@ waitForChild(HWND hwnd, HANDLE thread, DWORD threadID) // do dialog that let's the user terminate the test DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_WAIT), hwnd, - waitDlgProc, (LPARAM)&info); + (DLGPROC)waitDlgProc, (LPARAM)&info); // force the waiter thread to finish and wait for it SetEvent(info.m_ready); diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp index 5c31f4a5..c002b9f1 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -272,15 +272,15 @@ CMSWindowsClientTaskBarReceiver::createWindow() m_window = CreateDialogParam(m_appInstance, MAKEINTRESOURCE(IDD_TASKBAR_STATUS), NULL, - &CMSWindowsClientTaskBarReceiver::staticDlgProc, + (DLGPROC)&CMSWindowsClientTaskBarReceiver::staticDlgProc, reinterpret_cast( reinterpret_cast(this))); // window should appear on top of everything, including (especially) // the task bar. - DWORD style = GetWindowLong(m_window, GWL_EXSTYLE); + LONG_PTR style = GetWindowLongPtr(m_window, GWL_EXSTYLE); style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; - SetWindowLong(m_window, GWL_EXSTYLE, style); + SetWindowLongPtr(m_window, GWL_EXSTYLE, style); // tell the task bar about this dialog CArchTaskBarWindows::addDialog(m_window); diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp index 21a61954..e332ccea 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -300,15 +300,15 @@ CMSWindowsServerTaskBarReceiver::createWindow() m_window = CreateDialogParam(m_appInstance, MAKEINTRESOURCE(IDD_TASKBAR_STATUS), NULL, - &CMSWindowsServerTaskBarReceiver::staticDlgProc, + (DLGPROC)&CMSWindowsServerTaskBarReceiver::staticDlgProc, reinterpret_cast( reinterpret_cast(this))); // window should appear on top of everything, including (especially) // the task bar. - DWORD style = GetWindowLong(m_window, GWL_EXSTYLE); + LONG_PTR style = GetWindowLongPtr(m_window, GWL_EXSTYLE); style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; - SetWindowLong(m_window, GWL_EXSTYLE, style); + SetWindowLongPtr(m_window, GWL_EXSTYLE, style); // tell the task bar about this dialog CArchTaskBarWindows::addDialog(m_window); @@ -353,11 +353,11 @@ CMSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd, if (msg == WM_INITDIALOG) { self = reinterpret_cast( reinterpret_cast(lParam)); - SetWindowLong(hwnd, GWL_USERDATA, lParam); + SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam); } else { // get the extra window data and forward the call - LONG data = GetWindowLong(hwnd, GWL_USERDATA); + LONG data = GetWindowLongPtr(hwnd, GWLP_USERDATA); if (data != 0) { self = reinterpret_cast( reinterpret_cast(data)); diff --git a/lib/platform/CMSWindowsScreenSaver.cpp b/lib/platform/CMSWindowsScreenSaver.cpp index 529cd176..7217338f 100644 --- a/lib/platform/CMSWindowsScreenSaver.cpp +++ b/lib/platform/CMSWindowsScreenSaver.cpp @@ -294,7 +294,7 @@ BOOL CALLBACK CMSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg) { if (IsWindowVisible(hwnd)) { - HINSTANCE instance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE); + HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE); if (instance != CMSWindowsScreen::getInstance()) { PostMessage(hwnd, WM_CLOSE, 0, 0); *reinterpret_cast(arg) = true; From b728885e25b2af6f69c7aa87716c6753c254aab4 Mon Sep 17 00:00:00 2001 From: crs23 Date: Mon, 10 Sep 2007 00:59:51 +0000 Subject: [PATCH 805/807] Applied patch 1547642, key broadcasting. Modified the patch to conform to style guidelines. Also enhanced it to allow binding hot keys to not only toggling broadcasting but also turning it on or off, resized the hot key dialog to accommodate the keyboard broadcasting option, changed the configuration string for the keyboard broadcasting option, and added documentation. This change does not include the VS8 fix included in the patch. --- cmd/launcher/CHotkeyOptions.cpp | 80 ++++++++++++++++++++++++++++-- cmd/launcher/CHotkeyOptions.h | 2 + cmd/launcher/launcher.rc | 25 ++++++---- cmd/launcher/resource.h | 11 +++-- doc/configuration.html | 26 ++++++++++ lib/server/CConfig.cpp | 30 +++++++++++ lib/server/CInputFilter.cpp | 71 +++++++++++++++++++++++++- lib/server/CInputFilter.h | 22 +++++++++ lib/server/CServer.cpp | 88 ++++++++++++++++++++++++++++++++- lib/server/CServer.h | 29 +++++++++++ lib/synergy/IKeyState.cpp | 85 +++++++++++++++++-------------- lib/synergy/IKeyState.h | 4 +- 12 files changed, 415 insertions(+), 58 deletions(-) diff --git a/cmd/launcher/CHotkeyOptions.cpp b/cmd/launcher/CHotkeyOptions.cpp index 5b71d4ae..5aa981e0 100644 --- a/cmd/launcher/CHotkeyOptions.cpp +++ b/cmd/launcher/CHotkeyOptions.cpp @@ -925,6 +925,8 @@ CInputFilter::CAction* CHotkeyOptions::CActionDialog::s_action = NULL; CInputFilter::CAction* CHotkeyOptions::CActionDialog::s_lastGoodAction = NULL; +std::set + CHotkeyOptions::CActionDialog::s_screens; WNDPROC CHotkeyOptions::CActionDialog::s_editWndProc = NULL; bool @@ -995,11 +997,21 @@ CHotkeyOptions::CActionDialog::doInit(HWND hwnd) // fill lock modes child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST); SendMessage(child, CB_ADDSTRING, 0, - (LPARAM)getString(IDS_LOCK_MODE_OFF).c_str()); + (LPARAM)getString(IDS_MODE_OFF).c_str()); SendMessage(child, CB_ADDSTRING, 0, - (LPARAM)getString(IDS_LOCK_MODE_ON).c_str()); + (LPARAM)getString(IDS_MODE_ON).c_str()); SendMessage(child, CB_ADDSTRING, 0, - (LPARAM)getString(IDS_LOCK_MODE_TOGGLE).c_str()); + (LPARAM)getString(IDS_MODE_TOGGLE).c_str()); + SendMessage(child, CB_SETCURSEL, 0, 0); + + // fill keyboard broadcast modes + child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_MODE_OFF).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_MODE_ON).c_str()); + SendMessage(child, CB_ADDSTRING, 0, + (LPARAM)getString(IDS_MODE_TOGGLE).c_str()); SendMessage(child, CB_SETCURSEL, 0, 0); // select when @@ -1011,6 +1023,9 @@ CHotkeyOptions::CActionDialog::doInit(HWND hwnd) } setItemChecked(child, true); + // no screens by default + s_screens.clear(); + // select mode child = NULL; CInputFilter::CKeystrokeAction* keyAction = @@ -1023,6 +1038,8 @@ CHotkeyOptions::CActionDialog::doInit(HWND hwnd) dynamic_cast(s_action); CInputFilter::CSwitchInDirectionAction* switchInAction = dynamic_cast(s_action); + CInputFilter::CKeyboardBroadcastAction* keyboardBroadcastAction= + dynamic_cast(s_action); if (keyAction != NULL) { if (dynamic_cast(s_action) != NULL) { child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP); @@ -1066,6 +1083,14 @@ CHotkeyOptions::CActionDialog::doInit(HWND hwnd) switchInAction->getDirection() - kLeft, 0); child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN); } + else if (keyboardBroadcastAction != NULL) { + // Save the screens we're broadcasting to + s_screens = keyboardBroadcastAction->getScreens(); + + child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST); + SendMessage(child, CB_SETCURSEL, keyboardBroadcastAction->getMode(), 0); + child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST); + } if (child != NULL) { setItemChecked(child, true); } @@ -1111,12 +1136,18 @@ CHotkeyOptions::CActionDialog::updateControls(HWND hwnd) else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_LOCK))) { mode = 4; } + else if (isItemChecked(getItem(hwnd, + IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST))) { + mode = 5; + } // enable/disable all mode specific controls enableItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY, mode == 1); enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST, mode == 2); enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST, mode == 3); enableItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST, mode == 4); + enableItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST, mode == 5); + enableItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS, mode == 5); // can only set screens in key actions CInputFilter::CKeystrokeAction* keyAction = @@ -1306,6 +1337,18 @@ CHotkeyOptions::CActionDialog::onSwitchInAction(HWND hwnd) } } +void +CHotkeyOptions::CActionDialog::onKeyboardBroadcastAction(HWND hwnd) +{ + HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST); + LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0); + if (index != CB_ERR) { + delete s_action; + s_action = new CInputFilter::CKeyboardBroadcastAction( + (CInputFilter::CKeyboardBroadcastAction::Mode)index, s_screens); + } +} + KeyID CHotkeyOptions::CActionDialog::getChar(WPARAM wParam, LPARAM lParam) { @@ -1509,6 +1552,11 @@ CHotkeyOptions::CActionDialog::dlgProc(HWND hwnd, updateControls(hwnd); return TRUE; + case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST: + onKeyboardBroadcastAction(hwnd); + updateControls(hwnd); + return TRUE; + case IDC_HOTKEY_ACTION_LOCK_LIST: switch (HIWORD(wParam)) { case LBN_SELCHANGE: @@ -1533,11 +1581,37 @@ CHotkeyOptions::CActionDialog::dlgProc(HWND hwnd, } break; + case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST: + switch (HIWORD(wParam)) { + case LBN_SELCHANGE: + onKeyboardBroadcastAction(hwnd); + return TRUE; + } + break; + case IDC_HOTKEY_ACTION_SCREENS: CScreensDialog::doModal(hwnd, s_config, dynamic_cast(s_action)); fillHotkey(hwnd); return TRUE; + + case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS: { + // convert screens to form that CScreenDialog::doModal() wants + IPlatformScreen::CKeyInfo* tmpInfo = + IPlatformScreen::CKeyInfo::alloc(0, 0, 0, 1, s_screens); + CInputFilter::CKeystrokeAction tmpAction(tmpInfo, true); + + // get the screens + CScreensDialog::doModal(hwnd, s_config, &tmpAction); + + // convert screens back + IPlatformScreen::CKeyInfo::split( + tmpAction.getInfo()->m_screens, s_screens); + + // update + onKeyboardBroadcastAction(hwnd); + return TRUE; + } } break; diff --git a/cmd/launcher/CHotkeyOptions.h b/cmd/launcher/CHotkeyOptions.h index b63fcf2b..dec5367a 100644 --- a/cmd/launcher/CHotkeyOptions.h +++ b/cmd/launcher/CHotkeyOptions.h @@ -156,6 +156,7 @@ private: static void onLockAction(HWND hwnd); static void onSwitchToAction(HWND hwnd); static void onSwitchInAction(HWND hwnd); + static void onKeyboardBroadcastAction(HWND hwnd); static KeyID getChar(WPARAM wParam, LPARAM lParam); static KeyModifierMask @@ -176,6 +177,7 @@ private: s_action; static CInputFilter::CAction* s_lastGoodAction; + static std::set s_screens; static WNDPROC s_editWndProc; }; diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index 6d2952e8..ff72d069 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -338,7 +338,7 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,126,37,50,14 END -IDD_HOTKEY_ACTION DIALOG DISCARDABLE 0, 0, 183, 202 +IDD_HOTKEY_ACTION DIALOG DISCARDABLE 0, 0, 183, 218 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Action" FONT 8, "MS Sans Serif" @@ -356,6 +356,9 @@ BEGIN "Button",BS_AUTORADIOBUTTON,7,101,77,10 CONTROL "Lock Cursor to Screen:",IDC_HOTKEY_ACTION_LOCK,"Button", BS_AUTORADIOBUTTON,7,117,89,10 + CONTROL "Keyboard broadcasting:", + IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST,"Button", + BS_AUTORADIOBUTTON,7,133,89,10 LTEXT "&Hot key or mouse button:",IDC_STATIC,7,55,80,8 EDITTEXT IDC_HOTKEY_ACTION_HOTKEY,7,67,152,12,ES_WANTRETURN PUSHBUTTON "...",IDC_HOTKEY_ACTION_SCREENS,162,67,14,12 @@ -365,13 +368,17 @@ BEGIN CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP COMBOBOX IDC_HOTKEY_ACTION_LOCK_LIST,106,115,70,58, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Action takes place &when:",IDC_STATIC,7,137,81,8 + COMBOBOX IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST,106,131,53,58, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "...",IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS, + 162,131,14,12 + LTEXT "Action takes place &when:",IDC_STATIC,7,153,81,8 CONTROL "Hot key is pressed",IDC_HOTKEY_ACTION_ON_ACTIVATE, - "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,149,74,10 + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,165,74,10 CONTROL "Hot key is released",IDC_HOTKEY_ACTION_ON_DEACTIVATE, - "Button",BS_AUTORADIOBUTTON,7,161,76,10 - DEFPUSHBUTTON "OK",IDOK,70,181,50,14 - PUSHBUTTON "Cancel",IDCANCEL,126,181,50,14 + "Button",BS_AUTORADIOBUTTON,7,177,76,10 + DEFPUSHBUTTON "OK",IDOK,70,197,50,14 + PUSHBUTTON "Cancel",IDCANCEL,126,197,50,14 END IDD_HOTKEY_SCREENS DIALOG DISCARDABLE 0, 0, 237, 79 @@ -586,9 +593,9 @@ BEGIN IDS_AUTOSTART_SAVE_FAILED "Failed to save autostart configuration: %{1}" IDS_LOAD_FAILED "Failed to load configuration." IDS_CONFIG_CHANGED "Configuration changed on disk. Reload?" - IDS_LOCK_MODE_OFF "off" - IDS_LOCK_MODE_ON "on" - IDS_LOCK_MODE_TOGGLE "toggle" + IDS_MODE_OFF "off" + IDS_MODE_ON "on" + IDS_MODE_TOGGLE "toggle" IDS_ALL_SCREENS "All Screens" IDS_ACTIVE_SCREEN "Active Screen" END diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index e4e7b487..f284fd62 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -59,9 +59,9 @@ #define IDS_AUTOSTART_SAVE_FAILED 54 #define IDS_LOAD_FAILED 55 #define IDS_CONFIG_CHANGED 56 -#define IDS_LOCK_MODE_OFF 57 -#define IDS_LOCK_MODE_ON 58 -#define IDS_LOCK_MODE_TOGGLE 59 +#define IDS_MODE_OFF 57 +#define IDS_MODE_ON 58 +#define IDS_MODE_TOGGLE 59 #define IDS_ALL_SCREENS 60 #define IDS_ACTIVE_SCREEN 61 #define IDD_MAIN 101 @@ -169,6 +169,9 @@ #define IDC_HOTKEY_SCREENS_DST 1096 #define IDC_HOTKEY_SCREENS_ADD 1097 #define IDC_HOTKEY_SCREENS_REMOVE 1098 +#define IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST 1099 +#define IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST 1100 +#define IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS 1101 // Next default values for new objects // @@ -177,7 +180,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 116 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1098 +#define _APS_NEXT_CONTROL_VALUE 1102 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/doc/configuration.html b/doc/configuration.html index 48b2cc2d..6c1c8baa 100644 --- a/doc/configuration.html +++ b/doc/configuration.html @@ -406,6 +406,8 @@ Allowed individual actions are: screens lists the screen or screens to direct the event to, regardless of the active screen. If not given then the event is directed to the active screen only. + (Due to a bug, keys cannot be directed to the server while on a + client screen.)

            keyDown synthesizes a key press and keyUp synthesizes a key release. @@ -458,6 +460,30 @@ Allowed individual actions are: right, up or down.

            +

          • keyboardBroadcast(mode[,screens]) +

            + Turns broadcasting of keystrokes to multiple screens on and off. When + turned on all key presses and releases are sent to all of the screens + listed in screens. If not given, empty or + * then keystrokes are broadcast to all screens. + (However, due to a bug, keys cannot be sent to the server while on a + client screen.) +

            + mode can be off + to turn broadcasting off, on to turn it + on, or toggle to toggle the current + state. The default is toggle. +

            + screens is either * + to indicate all screens or a colon (:) separated list of screen + names. (Note that the screen name must have already been encountered + in the configuration file so you'll probably want to put actions at + the bottom of the file.) +

            + Multiple keyboardBroadcast actions may be + configured with different screens. The most + recently performed action defines the screens to broadcast to. +

          Examples: diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index a502fe78..92054f42 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -1203,6 +1203,36 @@ CConfig::parseAction(CConfigReadContext& s, action = new CInputFilter::CLockCursorToScreenAction(mode); } + else if (name == "keyboardBroadcast") { + if (args.size() > 2) { + throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])"); + } + + CInputFilter::CKeyboardBroadcastAction::Mode mode = + CInputFilter::CKeyboardBroadcastAction::kToggle; + if (args.size() >= 1) { + if (args[0] == "off") { + mode = CInputFilter::CKeyboardBroadcastAction::kOff; + } + else if (args[0] == "on") { + mode = CInputFilter::CKeyboardBroadcastAction::kOn; + } + else if (args[0] == "toggle") { + mode = CInputFilter::CKeyboardBroadcastAction::kToggle; + } + else { + throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])"); + } + } + + std::set screens; + if (args.size() >= 2) { + parseScreens(s, args[1], screens); + } + + action = new CInputFilter::CKeyboardBroadcastAction(mode, screens); + } + else { throw XConfigRead(s, "unknown action argument \"%{1}\"", name); } diff --git a/lib/server/CInputFilter.cpp b/lib/server/CInputFilter.cpp index 2a6c0e3a..d5d7fc20 100644 --- a/lib/server/CInputFilter.cpp +++ b/lib/server/CInputFilter.cpp @@ -268,13 +268,12 @@ CInputFilter::CAction::~CAction() // do nothing } -CInputFilter::CLockCursorToScreenAction::CLockCursorToScreenAction(Mode mode): +CInputFilter::CLockCursorToScreenAction::CLockCursorToScreenAction(Mode mode) : m_mode(mode) { // do nothing } - CInputFilter::CLockCursorToScreenAction::Mode CInputFilter::CLockCursorToScreenAction::getMode() const { @@ -400,6 +399,74 @@ CInputFilter::CSwitchInDirectionAction::perform(const CEvent& event) CEvent::kDeliverImmediately)); } +CInputFilter::CKeyboardBroadcastAction::CKeyboardBroadcastAction(Mode mode) : + m_mode(mode) +{ + // do nothing +} + +CInputFilter::CKeyboardBroadcastAction::CKeyboardBroadcastAction( + Mode mode, + const std::set& screens) : + m_mode(mode), + m_screens(IKeyState::CKeyInfo::join(screens)) +{ + // do nothing +} + +CInputFilter::CKeyboardBroadcastAction::Mode +CInputFilter::CKeyboardBroadcastAction::getMode() const +{ + return m_mode; +} + +std::set +CInputFilter::CKeyboardBroadcastAction::getScreens() const +{ + std::set screens; + IKeyState::CKeyInfo::split(m_screens.c_str(), screens); + return screens; +} + +CInputFilter::CAction* +CInputFilter::CKeyboardBroadcastAction::clone() const +{ + return new CKeyboardBroadcastAction(*this); +} + +CString +CInputFilter::CKeyboardBroadcastAction::format() const +{ + static const char* s_mode[] = { "off", "on", "toggle" }; + static const char* s_name = "keyboardBroadcast"; + + if (m_screens.empty() || m_screens[0] == '*') { + return CStringUtil::print("%s(%s)", s_name, s_mode[m_mode]); + } + else { + return CStringUtil::print("%s(%s,%.*s)", s_name, s_mode[m_mode], + m_screens.size() - 2, + m_screens.c_str() + 1); + } +} + +void +CInputFilter::CKeyboardBroadcastAction::perform(const CEvent& event) +{ + static const CServer::CKeyboardBroadcastInfo::State s_state[] = { + CServer::CKeyboardBroadcastInfo::kOff, + CServer::CKeyboardBroadcastInfo::kOn, + CServer::CKeyboardBroadcastInfo::kToggle + }; + + // send event + CServer::CKeyboardBroadcastInfo* info = + CServer::CKeyboardBroadcastInfo::alloc(s_state[m_mode], m_screens); + EVENTQUEUE->addEvent(CEvent(CServer::getKeyboardBroadcastEvent(), + event.getTarget(), info, + CEvent::kDeliverImmediately)); +} + CInputFilter::CKeystrokeAction::CKeystrokeAction( IPlatformScreen::CKeyInfo* info, bool press) : m_keyInfo(info), diff --git a/lib/server/CInputFilter.h b/lib/server/CInputFilter.h index 1c64636c..571ec82b 100644 --- a/lib/server/CInputFilter.h +++ b/lib/server/CInputFilter.h @@ -21,6 +21,7 @@ #include "IPlatformScreen.h" #include "CString.h" #include "stdmap.h" +#include "stdset.h" class CPrimaryClient; class CEvent; @@ -173,6 +174,27 @@ public: EDirection m_direction; }; + // CKeyboardBroadcastAction + class CKeyboardBroadcastAction : public CAction { + public: + enum Mode { kOff, kOn, kToggle }; + + CKeyboardBroadcastAction(Mode = kToggle); + CKeyboardBroadcastAction(Mode, const std::set& screens); + + Mode getMode() const; + std::set getScreens() const; + + // CAction overrides + virtual CAction* clone() const; + virtual CString format() const; + virtual void perform(const CEvent&); + + private: + Mode m_mode; + CString m_screens; + }; + // CKeystrokeAction class CKeystrokeAction : public CAction { public: diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index ab7a7ade..3d4d32c2 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -39,6 +39,7 @@ CEvent::Type CServer::s_connectedEvent = CEvent::kUnknown; CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; CEvent::Type CServer::s_switchToScreen = CEvent::kUnknown; CEvent::Type CServer::s_switchInDirection = CEvent::kUnknown; +CEvent::Type CServer::s_keyboardBroadcast = CEvent::kUnknown; CEvent::Type CServer::s_lockCursorToScreen = CEvent::kUnknown; CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : @@ -61,6 +62,7 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_switchTwoTapArmed(false), m_switchTwoTapZone(3), m_relativeMoves(false), + m_keyboardBroadcasting(false), m_lockedToScreen(false) { // must have a primary client and it must have a canonical name @@ -133,6 +135,10 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : m_inputFilter, new TMethodEventJob(this, &CServer::handleSwitchInDirectionEvent)); + EVENTQUEUE->adoptHandler(getKeyboardBroadcastEvent(), + m_inputFilter, + new TMethodEventJob(this, + &CServer::handleKeyboardBroadcastEvent)); EVENTQUEUE->adoptHandler(getLockCursorToScreenEvent(), m_inputFilter, new TMethodEventJob(this, @@ -355,6 +361,13 @@ CServer::getSwitchInDirectionEvent() "CServer::switchInDirection"); } +CEvent::Type +CServer::getKeyboardBroadcastEvent() +{ + return CEvent::registerTypeOnce(s_keyboardBroadcast, + "CServer:keyboardBroadcast"); +} + CEvent::Type CServer::getLockCursorToScreenEvent() { @@ -1364,6 +1377,37 @@ CServer::handleSwitchInDirectionEvent(const CEvent& event, void*) } } +void +CServer::handleKeyboardBroadcastEvent(const CEvent& event, void*) +{ + CKeyboardBroadcastInfo* info = (CKeyboardBroadcastInfo*)event.getData(); + + // choose new state + bool newState; + switch (info->m_state) { + case CKeyboardBroadcastInfo::kOff: + newState = false; + break; + + default: + case CKeyboardBroadcastInfo::kOn: + newState = true; + break; + + case CKeyboardBroadcastInfo::kToggle: + newState = !m_keyboardBroadcasting; + break; + } + + // enter new state + if (newState != m_keyboardBroadcasting || + info->m_screens != m_keyboardBroadcastingScreens) { + m_keyboardBroadcasting = newState; + m_keyboardBroadcastingScreens = info->m_screens; + LOG((CLOG_DEBUG "keyboard broadcasting %s: %s", m_keyboardBroadcasting ? "on" : "off", m_keyboardBroadcastingScreens.c_str())); + } +} + void CServer::handleLockCursorToScreenEvent(const CEvent& event, void*) { @@ -1513,10 +1557,17 @@ CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button, assert(m_active != NULL); // relay - if (IKeyState::CKeyInfo::isDefault(screens)) { + if (!m_keyboardBroadcasting || + (screens && IKeyState::CKeyInfo::isDefault(screens))) { m_active->keyDown(id, mask, button); } else { + if (!screens && m_keyboardBroadcasting) { + screens = m_keyboardBroadcastingScreens.c_str(); + if (IKeyState::CKeyInfo::isDefault(screens)) { + screens = "*"; + } + } for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { if (IKeyState::CKeyInfo::contains(screens, index->first)) { @@ -1534,10 +1585,17 @@ CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button, assert(m_active != NULL); // relay - if (IKeyState::CKeyInfo::isDefault(screens)) { + if (!m_keyboardBroadcasting || + (screens && IKeyState::CKeyInfo::isDefault(screens))) { m_active->keyUp(id, mask, button); } else { + if (!screens && m_keyboardBroadcasting) { + screens = m_keyboardBroadcastingScreens.c_str(); + if (IKeyState::CKeyInfo::isDefault(screens)) { + screens = "*"; + } + } for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { if (IKeyState::CKeyInfo::contains(screens, index->first)) { @@ -2090,3 +2148,29 @@ CServer::CScreenConnectedInfo::alloc(const CString& screen) strcpy(info->m_screen, screen.c_str()); return info; } + + +// +// CServer::CKeyboardBroadcastInfo +// + +CServer::CKeyboardBroadcastInfo* +CServer::CKeyboardBroadcastInfo::alloc(State state) +{ + CKeyboardBroadcastInfo* info = + (CKeyboardBroadcastInfo*)malloc(sizeof(CKeyboardBroadcastInfo)); + info->m_state = state; + info->m_screens[0] = '\0'; + return info; +} + +CServer::CKeyboardBroadcastInfo* +CServer::CKeyboardBroadcastInfo::alloc(State state, const CString& screens) +{ + CKeyboardBroadcastInfo* info = + (CKeyboardBroadcastInfo*)malloc(sizeof(CKeyboardBroadcastInfo) + + screens.size()); + info->m_state = state; + strcpy(info->m_screens, screens.c_str()); + return info; +} diff --git a/lib/server/CServer.h b/lib/server/CServer.h index c05d138d..36cf5e83 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -77,6 +77,20 @@ public: char m_screen[1]; }; + //! Keyboard broadcast data + class CKeyboardBroadcastInfo { + public: + enum State { kOff, kOn, kToggle }; + + static CKeyboardBroadcastInfo* alloc(State state = kToggle); + static CKeyboardBroadcastInfo* alloc(State state, + const CString& screens); + + public: + State m_state; + char m_screens[1]; + }; + /*! Start the server with the configuration \p config and the primary client (local screen) \p primaryClient. The client retains @@ -166,6 +180,14 @@ public: */ static CEvent::Type getSwitchInDirectionEvent(); + //! Get keyboard broadcast event type + /*! + Returns the keyboard broadcast event type. The server responds + to this by turning on keyboard broadcasting or turning it off. The + event data is a \c CKeyboardBroadcastInfo*. + */ + static CEvent::Type getKeyboardBroadcastEvent(); + //! Get lock cursor event type /*! Returns the lock cursor event type. The server responds to this @@ -304,6 +326,7 @@ private: void handleClientCloseTimeout(const CEvent&, void*); void handleSwitchToScreenEvent(const CEvent&, void*); void handleSwitchInDirectionEvent(const CEvent&, void*); + void handleKeyboardBroadcastEvent(const CEvent&,void*); void handleLockCursorToScreenEvent(const CEvent&, void*); void handleFakeInputBeginEvent(const CEvent&, void*); void handleFakeInputEndEvent(const CEvent&, void*); @@ -421,6 +444,11 @@ private: // relative mouse move option bool m_relativeMoves; + // flag whether or not we have broadcasting enabled and the screens to + // which we should send broadcasted keys. + bool m_keyboardBroadcasting; + CString m_keyboardBroadcastingScreens; + // screen locking (former scroll lock) bool m_lockedToScreen; @@ -429,6 +457,7 @@ private: static CEvent::Type s_disconnectedEvent; static CEvent::Type s_switchToScreen; static CEvent::Type s_switchInDirection; + static CEvent::Type s_keyboardBroadcast; static CEvent::Type s_lockCursorToScreen; }; diff --git a/lib/synergy/IKeyState.cpp b/lib/synergy/IKeyState.cpp index 28cd3313..d44e17d3 100644 --- a/lib/synergy/IKeyState.cpp +++ b/lib/synergy/IKeyState.cpp @@ -53,12 +53,13 @@ IKeyState::CKeyInfo* IKeyState::CKeyInfo::alloc(KeyID id, KeyModifierMask mask, KeyButton button, SInt32 count) { - CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); - info->m_key = id; - info->m_mask = mask; - info->m_button = button; - info->m_count = count; - info->m_screens[0] = '\0'; + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); + info->m_key = id; + info->m_mask = mask; + info->m_button = button; + info->m_count = count; + info->m_screens = NULL; + info->m_screensBuffer[0] = '\0'; return info; } @@ -67,44 +68,30 @@ IKeyState::CKeyInfo::alloc(KeyID id, KeyModifierMask mask, KeyButton button, SInt32 count, const std::set& destinations) { - // collect destinations into a string. names are surrounded by ':' - // which makes searching easy later. the string is empty if there - // are no destinations and "*" means all destinations. - CString screens; - for (std::set::const_iterator i = destinations.begin(); - i != destinations.end(); ++i) { - if (*i == "*") { - screens = "*"; - break; - } - else { - if (screens.empty()) { - screens = ":"; - } - screens += *i; - screens += ":"; - } - } + CString screens = join(destinations); // build structure - CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + screens.size()); - info->m_key = id; - info->m_mask = mask; - info->m_button = button; - info->m_count = count; - strcpy(info->m_screens, screens.c_str()); + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + screens.size()); + info->m_key = id; + info->m_mask = mask; + info->m_button = button; + info->m_count = count; + info->m_screens = info->m_screensBuffer; + strcpy(info->m_screensBuffer, screens.c_str()); return info; } IKeyState::CKeyInfo* IKeyState::CKeyInfo::alloc(const CKeyInfo& x) { - CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + strlen(x.m_screens)); - info->m_key = x.m_key; - info->m_mask = x.m_mask; - info->m_button = x.m_button; - info->m_count = x.m_count; - strcpy(info->m_screens, x.m_screens); + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo) + + strlen(x.m_screensBuffer)); + info->m_key = x.m_key; + info->m_mask = x.m_mask; + info->m_button = x.m_button; + info->m_count = x.m_count; + info->m_screens = x.m_screens ? info->m_screensBuffer : NULL; + strcpy(info->m_screensBuffer, x.m_screensBuffer); return info; } @@ -141,7 +128,31 @@ IKeyState::CKeyInfo::equal(const CKeyInfo* a, const CKeyInfo* b) a->m_mask == b->m_mask && a->m_button == b->m_button && a->m_count == b->m_count && - strcmp(a->m_screens, b->m_screens) == 0); + strcmp(a->m_screensBuffer, b->m_screensBuffer) == 0); +} + +CString +IKeyState::CKeyInfo::join(const std::set& destinations) +{ + // collect destinations into a string. names are surrounded by ':' + // which makes searching easy. the string is empty if there are no + // destinations and "*" means all destinations. + CString screens; + for (std::set::const_iterator i = destinations.begin(); + i != destinations.end(); ++i) { + if (*i == "*") { + screens = "*"; + break; + } + else { + if (screens.empty()) { + screens = ":"; + } + screens += *i; + screens += ":"; + } + } + return screens; } void diff --git a/lib/synergy/IKeyState.h b/lib/synergy/IKeyState.h index 1985a630..2b282487 100644 --- a/lib/synergy/IKeyState.h +++ b/lib/synergy/IKeyState.h @@ -43,6 +43,7 @@ public: static bool isDefault(const char* screens); static bool contains(const char* screens, const CString& name); static bool equal(const CKeyInfo*, const CKeyInfo*); + static CString join(const std::set& destinations); static void split(const char* screens, std::set&); public: @@ -50,7 +51,8 @@ public: KeyModifierMask m_mask; KeyButton m_button; SInt32 m_count; - char m_screens[1]; + char* m_screens; + char m_screensBuffer[1]; }; typedef std::set KeyButtonSet; From de7d35026f95a99fa36264c4291d420a7bcc5e9d Mon Sep 17 00:00:00 2001 From: crs23 Date: Mon, 10 Sep 2007 01:23:40 +0000 Subject: [PATCH 806/807] Applied patch 1547642, fixing a bug where we try to access elements in an empty std::list. --- lib/io/CStreamBuffer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/io/CStreamBuffer.cpp b/lib/io/CStreamBuffer.cpp index 2f10cac2..d11494b4 100644 --- a/lib/io/CStreamBuffer.cpp +++ b/lib/io/CStreamBuffer.cpp @@ -37,6 +37,12 @@ CStreamBuffer::peek(UInt32 n) { assert(n <= m_size); + // if requesting no data then return NULL so we don't try to access + // an empty list. + if (n == 0) { + return NULL; + } + // reserve space in first chunk ChunkList::iterator head = m_chunks.begin(); head->reserve(n + m_headUsed); From 3c737395e44e29c9dcc0552660dd75ce2cdd93b0 Mon Sep 17 00:00:00 2001 From: crs23 Date: Mon, 10 Sep 2007 01:25:57 +0000 Subject: [PATCH 807/807] Changed CSS to use 'Courier New' instead of 'Courier'. --- doc/synergy.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/synergy.css b/doc/synergy.css index 8d831aa0..f020aa59 100644 --- a/doc/synergy.css +++ b/doc/synergy.css @@ -44,7 +44,7 @@ b { pre { display: block; white-space: pre; - font-family: courier; + font-family: courier new; font-size: 87.5%; } @@ -124,19 +124,19 @@ pre { .arg { font-style: italic; - font-family: courier; + font-family: courier new; } .userinput { display: block; white-space: pre; - font-family: courier; + font-family: courier new; font-size: 87.5%; font-weight: bold; } .code { - font-family: courier; + font-family: courier new; } .code table { @@ -147,7 +147,7 @@ pre { .codeblock { display: block; white-space: pre; - font-family: courier; + font-family: courier new; font-size: 87.5%; border: 1px solid #000000; padding: 1em;